[ARM] S3C: Update time initialisation to fix S3C64XX time problems
authorBen Dooks <ben-linux@fluff.org>
Fri, 21 Nov 2008 10:36:05 +0000 (10:36 +0000)
committerBen Dooks <ben-linux@fluff.org>
Tue, 16 Dec 2008 10:13:02 +0000 (10:13 +0000)
The S3C64XX timer is running at the wrong rate due to the
assumptions made in the timer initialisation about the way
the pwm dividers work. This means that time on the S3C64XX
runs twice as fast as it should.

Fix the problem by moving to using the clk framework to setup
the pwm timer clock muxes, as the pwm-clock code has all the
necessary knowledge of how the timer clock inputs are routed.

Signed-off-by: Ben Dooks <ben-linux@fluff.org>
arch/arm/mach-s3c2412/clock.c
arch/arm/mach-s3c2443/clock.c
arch/arm/plat-s3c/include/plat/clock.h
arch/arm/plat-s3c/pwm-clock.c
arch/arm/plat-s3c/time.c
arch/arm/plat-s3c24xx/s3c2410-clock.c
arch/arm/plat-s3c64xx/clock.c

index 3ce15e082e7795bf1709e69a0d114aa41ec519f2..a037df5e1c2da52103b3b635bc18653f7b541999 100644 (file)
@@ -767,5 +767,6 @@ int __init s3c2412_baseclk_add(void)
                s3c2412_clkcon_enable(clkp, 0);
        }
 
+       s3c_pwmclk_init();
        return 0;
 }
index 363f39608783e972c3e34e1c531a661af9433c78..fdd4ec335a77f3896d8f9a8d9688150de277b810 100644 (file)
@@ -1107,4 +1107,6 @@ void __init s3c2443_init_clocks(int xtal)
 
                (clkp->enable)(clkp, 0);
        }
+
+       s3c_pwmclk_init();
 }
index ea1f3ffa97175ad9aba7ec23427356e0dbb72c82..a10622eed43ab10ab2b5fd13474d6f7a27af71fe 100644 (file)
@@ -81,3 +81,8 @@ extern void s3c2443_setup_clocks(void);
 /* S3C64XX specific functions and clocks */
 
 extern int s3c64xx_sclk_ctrl(struct clk *clk, int enable);
+
+/* Init for pwm clock code */
+
+extern void s3c_pwmclk_init(void);
+
index 988c0cd7ade2ddf2c3290d6866de7c5dbdc89367..a318215ab5352e3b137d85e869e01a6dd3d7c8f0 100644 (file)
@@ -407,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;
@@ -416,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++) {
@@ -424,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;
                }
        }
 
@@ -432,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;
                }
        }
 
@@ -440,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;
                }
        }
 
@@ -448,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);
index a581ff7ba664b835da5eeee73b330cd06cd23162..3b27b29da4787e452f76c2c2f5573e3605e06df6 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/err.h>
 #include <linux/clk.h>
 #include <linux/io.h>
+#include <linux/platform_device.h>
 
 #include <asm/system.h>
 #include <asm/leds.h>
@@ -147,6 +148,10 @@ static struct irqaction s3c2410_timer_irq = {
        machine_is_anubis()     || \
        machine_is_osiris())
 
+static struct clk *tin;
+static struct clk *tdiv;
+static struct clk *timerclk;
+
 /*
  * Set up timer interrupt, and return the current time in seconds.
  *
@@ -162,12 +167,6 @@ static void s3c2410_timer_setup (void)
 
        tcnt = TICK_MAX;  /* default value for tcnt */
 
-       /* read the current timer configuration bits */
-
-       tcon = __raw_readl(S3C2410_TCON);
-       tcfg1 = __raw_readl(S3C2410_TCFG1);
-       tcfg0 = __raw_readl(S3C2410_TCFG0);
-
        /* configure the system for whichever machine is in use */
 
        if (use_tclk1_12()) {
@@ -175,11 +174,13 @@ static void s3c2410_timer_setup (void)
                timer_usec_ticks = timer_mask_usec_ticks(1, 12000000);
                tcnt = 12000000 / HZ;
 
+               tcfg1 = __raw_readl(S3C2410_TCFG1);
                tcfg1 &= ~S3C2410_TCFG1_MUX4_MASK;
                tcfg1 |= S3C2410_TCFG1_MUX4_TCLK1;
+               __raw_writel(tcfg1, S3C2410_TCFG1);
        } else {
                unsigned long pclk;
-               struct clk *clk;
+               struct clk *tscaler;
 
                /* for the h1940 (and others), we use the pclk from the core
                 * to generate the timer values. since values around 50 to
@@ -190,29 +191,25 @@ static void s3c2410_timer_setup (void)
                 * (8.45 ticks per usec)
                 */
 
-               /* this is used as default if no other timer can be found */
-
-               clk = clk_get(NULL, "timers");
-               if (IS_ERR(clk))
-                       panic("failed to get clock for system timer");
-
-               clk_enable(clk);
-
-               pclk = clk_get_rate(clk);
+               pclk = clk_get_rate(timerclk);
 
                /* configure clock tick */
 
                timer_usec_ticks = timer_mask_usec_ticks(6, pclk);
 
-               tcfg1 &= ~S3C2410_TCFG1_MUX4_MASK;
-               tcfg1 |= S3C2410_TCFG1_MUX4_DIV2;
+               tscaler = clk_get_parent(tdiv);
 
-               tcfg0 &= ~S3C2410_TCFG_PRESCALER1_MASK;
-               tcfg0 |= ((6 - 1) / 2) << S3C2410_TCFG_PRESCALER1_SHIFT;
+               clk_set_rate(tscaler, pclk / 3);
+               clk_set_rate(tdiv, pclk / 6);
+               clk_set_parent(tin, tdiv);
 
-               tcnt = (pclk / 6) / HZ;
+               tcnt = clk_get_rate(tin) / HZ;
        }
 
+       tcon = __raw_readl(S3C2410_TCON);
+       tcfg0 = __raw_readl(S3C2410_TCFG0);
+       tcfg1 = __raw_readl(S3C2410_TCFG1);
+
        /* timers reload after counting zero, so reduce the count by 1 */
 
        tcnt--;
@@ -248,8 +245,35 @@ static void s3c2410_timer_setup (void)
        __raw_writel(tcon, S3C2410_TCON);
 }
 
+static void __init s3c2410_timer_resources(void)
+{
+       struct platform_device tmpdev;
+
+       tmpdev.dev.bus = &platform_bus_type;
+       tmpdev.id = 4;
+
+       timerclk = clk_get(NULL, "timers");
+       if (IS_ERR(timerclk))
+               panic("failed to get clock for system timer");
+
+       clk_enable(timerclk);
+
+       if (!use_tclk1_12()) {
+               tin = clk_get(&tmpdev.dev, "pwm-tin");
+               if (IS_ERR(tin))
+                       panic("failed to get pwm-tin clock for system timer");
+
+               tdiv = clk_get(&tmpdev.dev, "pwm-tdiv");
+               if (IS_ERR(tdiv))
+                       panic("failed to get pwm-tdiv clock for system timer");
+       }
+
+       clk_enable(tin);
+}
+
 static void __init s3c2410_timer_init(void)
 {
+       s3c2410_timer_resources();
        s3c2410_timer_setup();
        setup_irq(IRQ_TIMER4, &s3c2410_timer_irq);
 }
index 4e07943c1e29116603aef7427ec41410084949ed..b61bdb7937341931373036d55bd80e6f1919110a 100644 (file)
@@ -272,5 +272,6 @@ int __init s3c2410_baseclk_add(void)
               (clkslow & S3C2410_CLKSLOW_MPLL_OFF) ? "off" : "on",
               (clkslow & S3C2410_CLKSLOW_UCLK_OFF) ? "off" : "on");
 
+       s3c_pwmclk_init();
        return 0;
 }
index 523da0cb55c563f371a48522b233bb3c1d22959c..5a1e97e1f8f64bb889a55eb2ff1ead4886350de4 100644 (file)
@@ -277,4 +277,6 @@ void s3c64xx_register_clocks(void)
 
                (clkp->enable)(clkp, 0);
        }
+
+       s3c_pwmclk_init();
 }