Merge branch for-rmk-devel of git://aeryn.fluff.org.uk/bjdooks/linux into devel
[pandora-kernel.git] / arch / arm / plat-s3c / pwm-clock.c
index 3fad68a..a318215 100644 (file)
 #include <linux/kernel.h>
 #include <linux/list.h>
 #include <linux/errno.h>
+#include <linux/log2.h>
 #include <linux/clk.h>
 #include <linux/err.h>
 #include <linux/io.h>
 
 #include <mach/hardware.h>
+#include <mach/map.h>
 #include <asm/irq.h>
 
-#include <mach/regs-clock.h>
-#include <mach/regs-gpio.h>
-
 #include <plat/clock.h>
 #include <plat/cpu.h>
 
 #include <plat/regs-timer.h>
+#include <mach/pwm-clock.h>
 
 /* Each of the timers 0 through 5 go through the following
  * clock tree, with the inputs depending on the timers.
  * tclk -------------------------/
 */
 
-static unsigned long clk_pwm_scaler_getrate(struct clk *clk)
+static struct clk clk_timer_scaler[];
+
+static unsigned long clk_pwm_scaler_get_rate(struct clk *clk)
 {
        unsigned long tcfg0 = __raw_readl(S3C2410_TCFG0);
 
-       if (clk->id == 1) {
+       if (clk == &clk_timer_scaler[1]) {
                tcfg0 &= S3C2410_TCFG_PRESCALER1_MASK;
                tcfg0 >>= S3C2410_TCFG_PRESCALER1_SHIFT;
        } else {
@@ -87,18 +89,61 @@ static unsigned long clk_pwm_scaler_getrate(struct clk *clk)
        return clk_get_rate(clk->parent) / (tcfg0 + 1);
 }
 
-/* TODO - add set rate calls. */
+static unsigned long clk_pwm_scaler_round_rate(struct clk *clk,
+                                              unsigned long rate)
+{
+       unsigned long parent_rate = clk_get_rate(clk->parent);
+       unsigned long divisor = parent_rate / rate;
+
+       if (divisor > 256)
+               divisor = 256;
+       else if (divisor < 2)
+               divisor = 2;
+
+       return parent_rate / divisor;
+}
+
+static int clk_pwm_scaler_set_rate(struct clk *clk, unsigned long rate)
+{
+       unsigned long round = clk_pwm_scaler_round_rate(clk, rate);
+       unsigned long tcfg0;
+       unsigned long divisor;
+       unsigned long flags;
+
+       divisor = clk_get_rate(clk->parent) / round;
+       divisor--;
+
+       local_irq_save(flags);
+       tcfg0 = __raw_readl(S3C2410_TCFG0);
+
+       if (clk == &clk_timer_scaler[1]) {
+               tcfg0 &= ~S3C2410_TCFG_PRESCALER1_MASK;
+               tcfg0 |= divisor << S3C2410_TCFG_PRESCALER1_SHIFT;
+       } else {
+               tcfg0 &= ~S3C2410_TCFG_PRESCALER0_MASK;
+               tcfg0 |= divisor;
+       }
+
+       __raw_writel(tcfg0, S3C2410_TCFG0);
+       local_irq_restore(flags);
+
+       return 0;
+}
 
 static struct clk clk_timer_scaler[] = {
        [0]     = {
                .name           = "pwm-scaler0",
                .id             = -1,
-               .get_rate       = clk_pwm_scaler_getrate,
+               .get_rate       = clk_pwm_scaler_get_rate,
+               .set_rate       = clk_pwm_scaler_set_rate,
+               .round_rate     = clk_pwm_scaler_round_rate,
        },
        [1]     = {
                .name           = "pwm-scaler1",
                .id             = -1,
-               .get_rate       = clk_pwm_scaler_getrate,
+               .get_rate       = clk_pwm_scaler_get_rate,
+               .set_rate       = clk_pwm_scaler_set_rate,
+               .round_rate     = clk_pwm_scaler_round_rate,
        },
 };
 
@@ -123,11 +168,6 @@ static inline struct pwm_tdiv_clk *to_tdiv(struct clk *clk)
        return container_of(clk, struct pwm_tdiv_clk, clk);
 }
 
-static inline unsigned long tcfg_to_divisor(unsigned long tcfg1)
-{
-       return 1 << (1 + tcfg1);
-}
-
 static unsigned long clk_pwm_tdiv_get_rate(struct clk *clk)
 {
        unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1);
@@ -136,7 +176,7 @@ static unsigned long clk_pwm_tdiv_get_rate(struct clk *clk)
        tcfg1 >>= S3C2410_TCFG1_SHIFT(clk->id);
        tcfg1 &= S3C2410_TCFG1_MUX_MASK;
 
-       if (tcfg1 == S3C2410_TCFG1_MUX_TCLK)
+       if (pwm_cfg_src_is_tclk(tcfg1))
                divisor = to_tdiv(clk)->divisor;
        else
                divisor = tcfg_to_divisor(tcfg1);
@@ -153,7 +193,9 @@ static unsigned long clk_pwm_tdiv_round_rate(struct clk *clk,
        parent_rate = clk_get_rate(clk->parent);
        divisor = parent_rate / rate;
 
-       if (divisor <= 2)
+       if (divisor <= 1 && pwm_tdiv_has_div1())
+               divisor = 1;
+       else if (divisor <= 2)
                divisor = 2;
        else if (divisor <= 4)
                divisor = 4;
@@ -167,25 +209,7 @@ static unsigned long clk_pwm_tdiv_round_rate(struct clk *clk,
 
 static unsigned long clk_pwm_tdiv_bits(struct pwm_tdiv_clk *divclk)
 {
-       unsigned long bits;
-
-       switch (divclk->divisor) {
-       case 2:
-               bits = S3C2410_TCFG1_MUX_DIV2;
-               break;
-       case 4:
-               bits = S3C2410_TCFG1_MUX_DIV4;
-               break;
-       case 8:
-               bits = S3C2410_TCFG1_MUX_DIV8;
-               break;
-       case 16:
-       default:
-               bits = S3C2410_TCFG1_MUX_DIV16;
-               break;
-       }
-
-       return bits;
+       return pwm_tdiv_div_bits(divclk->divisor);
 }
 
 static void clk_pwm_tdiv_update(struct pwm_tdiv_clk *divclk)
@@ -226,7 +250,7 @@ static int clk_pwm_tdiv_set_rate(struct clk *clk, unsigned long rate)
        /* Update the current MUX settings if we are currently
         * selected as the clock source for this clock. */
 
-       if (tcfg1 != S3C2410_TCFG1_MUX_TCLK)
+       if (!pwm_cfg_src_is_tclk(tcfg1))
                clk_pwm_tdiv_update(divclk);
 
        return 0;
@@ -313,7 +337,7 @@ static int clk_pwm_tin_set_parent(struct clk *clk, struct clk *parent)
        unsigned long shift = S3C2410_TCFG1_SHIFT(id);
 
        if (parent == s3c24xx_pwmclk_tclk(id))
-               bits = S3C2410_TCFG1_MUX_TCLK << shift;
+               bits = S3C_TCFG1_MUX_TCLK << shift;
        else if (parent == s3c24xx_pwmclk_tdiv(id))
                bits = clk_pwm_tdiv_bits(to_tdiv(parent)) << shift;
        else
@@ -375,7 +399,7 @@ static __init int clk_pwm_tin_register(struct clk *pwm)
        tcfg1 >>= S3C2410_TCFG1_SHIFT(id);
        tcfg1 &= S3C2410_TCFG1_MUX_MASK;
 
-       if (tcfg1 == S3C2410_TCFG1_MUX_TCLK)
+       if (pwm_cfg_src_is_tclk(tcfg1))
                parent = s3c24xx_pwmclk_tclk(id);
        else
                parent = s3c24xx_pwmclk_tdiv(id);
@@ -383,7 +407,16 @@ static __init int clk_pwm_tin_register(struct clk *pwm)
        return clk_set_parent(pwm, parent);
 }
 
-static __init int s3c24xx_pwmclk_init(void)
+/**
+ * s3c_pwmclk_init() - initialise pwm clocks
+ *
+ * Initialise and register the clocks which provide the inputs for the
+ * pwm timer blocks.
+ *
+ * Note, this call is required by the time core, so must be called after
+ * the base clocks are added and before any of the initcalls are run.
+ */
+__init void s3c_pwmclk_init(void)
 {
        struct clk *clk_timers;
        unsigned int clk;
@@ -392,7 +425,7 @@ static __init int s3c24xx_pwmclk_init(void)
        clk_timers = clk_get(NULL, "timers");
        if (IS_ERR(clk_timers)) {
                printk(KERN_ERR "%s: no parent clock\n", __func__);
-               return -EINVAL;
+               return;
        }
 
        for (clk = 0; clk < ARRAY_SIZE(clk_timer_scaler); clk++) {
@@ -400,7 +433,7 @@ static __init int s3c24xx_pwmclk_init(void)
                ret = s3c24xx_register_clock(&clk_timer_scaler[clk]);
                if (ret < 0) {
                        printk(KERN_ERR "error adding pwm scaler%d clock\n", clk);
-                       goto err;
+                       return;
                }
        }
 
@@ -408,7 +441,7 @@ static __init int s3c24xx_pwmclk_init(void)
                ret = s3c24xx_register_clock(&clk_timer_tclk[clk]);
                if (ret < 0) {
                        printk(KERN_ERR "error adding pww tclk%d\n", clk);
-                       goto err;
+                       return;
                }
        }
 
@@ -416,7 +449,7 @@ static __init int s3c24xx_pwmclk_init(void)
                ret = clk_pwm_tdiv_register(clk);
                if (ret < 0) {
                        printk(KERN_ERR "error adding pwm%d tdiv clock\n", clk);
-                       goto err;
+                       return;
                }
        }
 
@@ -424,14 +457,7 @@ static __init int s3c24xx_pwmclk_init(void)
                ret = clk_pwm_tin_register(&clk_tin[clk]);
                if (ret < 0) {
                        printk(KERN_ERR "error adding pwm%d tin clock\n", clk);
-                       goto err;
+                       return;
                }
        }
-
-       return 0;
-
- err:
-       return ret;
 }
-
-arch_initcall(s3c24xx_pwmclk_init);