[ARM] S3C: Update time initialisation to fix S3C64XX time problems
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>
diff --git a/arch/arm/mach-s3c2412/clock.c b/arch/arm/mach-s3c2412/clock.c
index 3ce15e0..a037df5 100644
--- a/arch/arm/mach-s3c2412/clock.c
+++ b/arch/arm/mach-s3c2412/clock.c
@@ -767,5 +767,6 @@
s3c2412_clkcon_enable(clkp, 0);
}
+ s3c_pwmclk_init();
return 0;
}
diff --git a/arch/arm/mach-s3c2443/clock.c b/arch/arm/mach-s3c2443/clock.c
index 363f396..fdd4ec3 100644
--- a/arch/arm/mach-s3c2443/clock.c
+++ b/arch/arm/mach-s3c2443/clock.c
@@ -1107,4 +1107,6 @@
(clkp->enable)(clkp, 0);
}
+
+ s3c_pwmclk_init();
}
diff --git a/arch/arm/plat-s3c/include/plat/clock.h b/arch/arm/plat-s3c/include/plat/clock.h
index ea1f3ff..a10622e 100644
--- a/arch/arm/plat-s3c/include/plat/clock.h
+++ b/arch/arm/plat-s3c/include/plat/clock.h
@@ -81,3 +81,8 @@
/* 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);
+
diff --git a/arch/arm/plat-s3c/pwm-clock.c b/arch/arm/plat-s3c/pwm-clock.c
index 988c0cd..a318215 100644
--- a/arch/arm/plat-s3c/pwm-clock.c
+++ b/arch/arm/plat-s3c/pwm-clock.c
@@ -407,7 +407,16 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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);
diff --git a/arch/arm/plat-s3c/time.c b/arch/arm/plat-s3c/time.c
index a581ff7..3b27b29 100644
--- a/arch/arm/plat-s3c/time.c
+++ b/arch/arm/plat-s3c/time.c
@@ -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 @@
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 @@
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 @@
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 @@
* (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 @@
__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);
}
diff --git a/arch/arm/plat-s3c24xx/s3c2410-clock.c b/arch/arm/plat-s3c24xx/s3c2410-clock.c
index 4e07943..b61bdb7 100644
--- a/arch/arm/plat-s3c24xx/s3c2410-clock.c
+++ b/arch/arm/plat-s3c24xx/s3c2410-clock.c
@@ -272,5 +272,6 @@
(clkslow & S3C2410_CLKSLOW_MPLL_OFF) ? "off" : "on",
(clkslow & S3C2410_CLKSLOW_UCLK_OFF) ? "off" : "on");
+ s3c_pwmclk_init();
return 0;
}
diff --git a/arch/arm/plat-s3c64xx/clock.c b/arch/arm/plat-s3c64xx/clock.c
index 523da0c..5a1e97e 100644
--- a/arch/arm/plat-s3c64xx/clock.c
+++ b/arch/arm/plat-s3c64xx/clock.c
@@ -277,4 +277,6 @@
(clkp->enable)(clkp, 0);
}
+
+ s3c_pwmclk_init();
}