aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm
diff options
context:
space:
mode:
authorBen Dooks <ben-linux@fluff.org>2008-11-21 05:36:05 -0500
committerBen Dooks <ben-linux@fluff.org>2008-12-16 05:13:02 -0500
commit9d325f23416d1525401d43442bafca2bb254ab74 (patch)
tree4b8e0ef7c121c263d2cbbea136c97d81d6be0463 /arch/arm
parentb09bcdd4c2f52b54115895c4d62ad82918f71431 (diff)
[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>
Diffstat (limited to 'arch/arm')
-rw-r--r--arch/arm/mach-s3c2412/clock.c1
-rw-r--r--arch/arm/mach-s3c2443/clock.c2
-rw-r--r--arch/arm/plat-s3c/include/plat/clock.h5
-rw-r--r--arch/arm/plat-s3c/pwm-clock.c28
-rw-r--r--arch/arm/plat-s3c/time.c66
-rw-r--r--arch/arm/plat-s3c24xx/s3c2410-clock.c1
-rw-r--r--arch/arm/plat-s3c64xx/clock.c2
7 files changed, 71 insertions, 34 deletions
diff --git a/arch/arm/mach-s3c2412/clock.c b/arch/arm/mach-s3c2412/clock.c
index 3ce15e082e77..a037df5e1c2d 100644
--- a/arch/arm/mach-s3c2412/clock.c
+++ b/arch/arm/mach-s3c2412/clock.c
@@ -767,5 +767,6 @@ int __init s3c2412_baseclk_add(void)
767 s3c2412_clkcon_enable(clkp, 0); 767 s3c2412_clkcon_enable(clkp, 0);
768 } 768 }
769 769
770 s3c_pwmclk_init();
770 return 0; 771 return 0;
771} 772}
diff --git a/arch/arm/mach-s3c2443/clock.c b/arch/arm/mach-s3c2443/clock.c
index 363f39608783..fdd4ec335a77 100644
--- a/arch/arm/mach-s3c2443/clock.c
+++ b/arch/arm/mach-s3c2443/clock.c
@@ -1107,4 +1107,6 @@ void __init s3c2443_init_clocks(int xtal)
1107 1107
1108 (clkp->enable)(clkp, 0); 1108 (clkp->enable)(clkp, 0);
1109 } 1109 }
1110
1111 s3c_pwmclk_init();
1110} 1112}
diff --git a/arch/arm/plat-s3c/include/plat/clock.h b/arch/arm/plat-s3c/include/plat/clock.h
index ea1f3ffa9717..a10622eed43a 100644
--- a/arch/arm/plat-s3c/include/plat/clock.h
+++ b/arch/arm/plat-s3c/include/plat/clock.h
@@ -81,3 +81,8 @@ extern void s3c2443_setup_clocks(void);
81/* S3C64XX specific functions and clocks */ 81/* S3C64XX specific functions and clocks */
82 82
83extern int s3c64xx_sclk_ctrl(struct clk *clk, int enable); 83extern int s3c64xx_sclk_ctrl(struct clk *clk, int enable);
84
85/* Init for pwm clock code */
86
87extern void s3c_pwmclk_init(void);
88
diff --git a/arch/arm/plat-s3c/pwm-clock.c b/arch/arm/plat-s3c/pwm-clock.c
index 988c0cd7ade2..a318215ab535 100644
--- a/arch/arm/plat-s3c/pwm-clock.c
+++ b/arch/arm/plat-s3c/pwm-clock.c
@@ -407,7 +407,16 @@ static __init int clk_pwm_tin_register(struct clk *pwm)
407 return clk_set_parent(pwm, parent); 407 return clk_set_parent(pwm, parent);
408} 408}
409 409
410static __init int s3c24xx_pwmclk_init(void) 410/**
411 * s3c_pwmclk_init() - initialise pwm clocks
412 *
413 * Initialise and register the clocks which provide the inputs for the
414 * pwm timer blocks.
415 *
416 * Note, this call is required by the time core, so must be called after
417 * the base clocks are added and before any of the initcalls are run.
418 */
419__init void s3c_pwmclk_init(void)
411{ 420{
412 struct clk *clk_timers; 421 struct clk *clk_timers;
413 unsigned int clk; 422 unsigned int clk;
@@ -416,7 +425,7 @@ static __init int s3c24xx_pwmclk_init(void)
416 clk_timers = clk_get(NULL, "timers"); 425 clk_timers = clk_get(NULL, "timers");
417 if (IS_ERR(clk_timers)) { 426 if (IS_ERR(clk_timers)) {
418 printk(KERN_ERR "%s: no parent clock\n", __func__); 427 printk(KERN_ERR "%s: no parent clock\n", __func__);
419 return -EINVAL; 428 return;
420 } 429 }
421 430
422 for (clk = 0; clk < ARRAY_SIZE(clk_timer_scaler); clk++) { 431 for (clk = 0; clk < ARRAY_SIZE(clk_timer_scaler); clk++) {
@@ -424,7 +433,7 @@ static __init int s3c24xx_pwmclk_init(void)
424 ret = s3c24xx_register_clock(&clk_timer_scaler[clk]); 433 ret = s3c24xx_register_clock(&clk_timer_scaler[clk]);
425 if (ret < 0) { 434 if (ret < 0) {
426 printk(KERN_ERR "error adding pwm scaler%d clock\n", clk); 435 printk(KERN_ERR "error adding pwm scaler%d clock\n", clk);
427 goto err; 436 return;
428 } 437 }
429 } 438 }
430 439
@@ -432,7 +441,7 @@ static __init int s3c24xx_pwmclk_init(void)
432 ret = s3c24xx_register_clock(&clk_timer_tclk[clk]); 441 ret = s3c24xx_register_clock(&clk_timer_tclk[clk]);
433 if (ret < 0) { 442 if (ret < 0) {
434 printk(KERN_ERR "error adding pww tclk%d\n", clk); 443 printk(KERN_ERR "error adding pww tclk%d\n", clk);
435 goto err; 444 return;
436 } 445 }
437 } 446 }
438 447
@@ -440,7 +449,7 @@ static __init int s3c24xx_pwmclk_init(void)
440 ret = clk_pwm_tdiv_register(clk); 449 ret = clk_pwm_tdiv_register(clk);
441 if (ret < 0) { 450 if (ret < 0) {
442 printk(KERN_ERR "error adding pwm%d tdiv clock\n", clk); 451 printk(KERN_ERR "error adding pwm%d tdiv clock\n", clk);
443 goto err; 452 return;
444 } 453 }
445 } 454 }
446 455
@@ -448,14 +457,7 @@ static __init int s3c24xx_pwmclk_init(void)
448 ret = clk_pwm_tin_register(&clk_tin[clk]); 457 ret = clk_pwm_tin_register(&clk_tin[clk]);
449 if (ret < 0) { 458 if (ret < 0) {
450 printk(KERN_ERR "error adding pwm%d tin clock\n", clk); 459 printk(KERN_ERR "error adding pwm%d tin clock\n", clk);
451 goto err; 460 return;
452 } 461 }
453 } 462 }
454
455 return 0;
456
457 err:
458 return ret;
459} 463}
460
461arch_initcall(s3c24xx_pwmclk_init);
diff --git a/arch/arm/plat-s3c/time.c b/arch/arm/plat-s3c/time.c
index a581ff7ba664..3b27b29da478 100644
--- a/arch/arm/plat-s3c/time.c
+++ b/arch/arm/plat-s3c/time.c
@@ -26,6 +26,7 @@
26#include <linux/err.h> 26#include <linux/err.h>
27#include <linux/clk.h> 27#include <linux/clk.h>
28#include <linux/io.h> 28#include <linux/io.h>
29#include <linux/platform_device.h>
29 30
30#include <asm/system.h> 31#include <asm/system.h>
31#include <asm/leds.h> 32#include <asm/leds.h>
@@ -147,6 +148,10 @@ static struct irqaction s3c2410_timer_irq = {
147 machine_is_anubis() || \ 148 machine_is_anubis() || \
148 machine_is_osiris()) 149 machine_is_osiris())
149 150
151static struct clk *tin;
152static struct clk *tdiv;
153static struct clk *timerclk;
154
150/* 155/*
151 * Set up timer interrupt, and return the current time in seconds. 156 * Set up timer interrupt, and return the current time in seconds.
152 * 157 *
@@ -162,12 +167,6 @@ static void s3c2410_timer_setup (void)
162 167
163 tcnt = TICK_MAX; /* default value for tcnt */ 168 tcnt = TICK_MAX; /* default value for tcnt */
164 169
165 /* read the current timer configuration bits */
166
167 tcon = __raw_readl(S3C2410_TCON);
168 tcfg1 = __raw_readl(S3C2410_TCFG1);
169 tcfg0 = __raw_readl(S3C2410_TCFG0);
170
171 /* configure the system for whichever machine is in use */ 170 /* configure the system for whichever machine is in use */
172 171
173 if (use_tclk1_12()) { 172 if (use_tclk1_12()) {
@@ -175,11 +174,13 @@ static void s3c2410_timer_setup (void)
175 timer_usec_ticks = timer_mask_usec_ticks(1, 12000000); 174 timer_usec_ticks = timer_mask_usec_ticks(1, 12000000);
176 tcnt = 12000000 / HZ; 175 tcnt = 12000000 / HZ;
177 176
177 tcfg1 = __raw_readl(S3C2410_TCFG1);
178 tcfg1 &= ~S3C2410_TCFG1_MUX4_MASK; 178 tcfg1 &= ~S3C2410_TCFG1_MUX4_MASK;
179 tcfg1 |= S3C2410_TCFG1_MUX4_TCLK1; 179 tcfg1 |= S3C2410_TCFG1_MUX4_TCLK1;
180 __raw_writel(tcfg1, S3C2410_TCFG1);
180 } else { 181 } else {
181 unsigned long pclk; 182 unsigned long pclk;
182 struct clk *clk; 183 struct clk *tscaler;
183 184
184 /* for the h1940 (and others), we use the pclk from the core 185 /* for the h1940 (and others), we use the pclk from the core
185 * to generate the timer values. since values around 50 to 186 * to generate the timer values. since values around 50 to
@@ -190,29 +191,25 @@ static void s3c2410_timer_setup (void)
190 * (8.45 ticks per usec) 191 * (8.45 ticks per usec)
191 */ 192 */
192 193
193 /* this is used as default if no other timer can be found */ 194 pclk = clk_get_rate(timerclk);
194
195 clk = clk_get(NULL, "timers");
196 if (IS_ERR(clk))
197 panic("failed to get clock for system timer");
198
199 clk_enable(clk);
200
201 pclk = clk_get_rate(clk);
202 195
203 /* configure clock tick */ 196 /* configure clock tick */
204 197
205 timer_usec_ticks = timer_mask_usec_ticks(6, pclk); 198 timer_usec_ticks = timer_mask_usec_ticks(6, pclk);
206 199
207 tcfg1 &= ~S3C2410_TCFG1_MUX4_MASK; 200 tscaler = clk_get_parent(tdiv);
208 tcfg1 |= S3C2410_TCFG1_MUX4_DIV2;
209 201
210 tcfg0 &= ~S3C2410_TCFG_PRESCALER1_MASK; 202 clk_set_rate(tscaler, pclk / 3);
211 tcfg0 |= ((6 - 1) / 2) << S3C2410_TCFG_PRESCALER1_SHIFT; 203 clk_set_rate(tdiv, pclk / 6);
204 clk_set_parent(tin, tdiv);
212 205
213 tcnt = (pclk / 6) / HZ; 206 tcnt = clk_get_rate(tin) / HZ;
214 } 207 }
215 208
209 tcon = __raw_readl(S3C2410_TCON);
210 tcfg0 = __raw_readl(S3C2410_TCFG0);
211 tcfg1 = __raw_readl(S3C2410_TCFG1);
212
216 /* timers reload after counting zero, so reduce the count by 1 */ 213 /* timers reload after counting zero, so reduce the count by 1 */
217 214
218 tcnt--; 215 tcnt--;
@@ -248,8 +245,35 @@ static void s3c2410_timer_setup (void)
248 __raw_writel(tcon, S3C2410_TCON); 245 __raw_writel(tcon, S3C2410_TCON);
249} 246}
250 247
248static void __init s3c2410_timer_resources(void)
249{
250 struct platform_device tmpdev;
251
252 tmpdev.dev.bus = &platform_bus_type;
253 tmpdev.id = 4;
254
255 timerclk = clk_get(NULL, "timers");
256 if (IS_ERR(timerclk))
257 panic("failed to get clock for system timer");
258
259 clk_enable(timerclk);
260
261 if (!use_tclk1_12()) {
262 tin = clk_get(&tmpdev.dev, "pwm-tin");
263 if (IS_ERR(tin))
264 panic("failed to get pwm-tin clock for system timer");
265
266 tdiv = clk_get(&tmpdev.dev, "pwm-tdiv");
267 if (IS_ERR(tdiv))
268 panic("failed to get pwm-tdiv clock for system timer");
269 }
270
271 clk_enable(tin);
272}
273
251static void __init s3c2410_timer_init(void) 274static void __init s3c2410_timer_init(void)
252{ 275{
276 s3c2410_timer_resources();
253 s3c2410_timer_setup(); 277 s3c2410_timer_setup();
254 setup_irq(IRQ_TIMER4, &s3c2410_timer_irq); 278 setup_irq(IRQ_TIMER4, &s3c2410_timer_irq);
255} 279}
diff --git a/arch/arm/plat-s3c24xx/s3c2410-clock.c b/arch/arm/plat-s3c24xx/s3c2410-clock.c
index 4e07943c1e29..b61bdb793734 100644
--- a/arch/arm/plat-s3c24xx/s3c2410-clock.c
+++ b/arch/arm/plat-s3c24xx/s3c2410-clock.c
@@ -272,5 +272,6 @@ int __init s3c2410_baseclk_add(void)
272 (clkslow & S3C2410_CLKSLOW_MPLL_OFF) ? "off" : "on", 272 (clkslow & S3C2410_CLKSLOW_MPLL_OFF) ? "off" : "on",
273 (clkslow & S3C2410_CLKSLOW_UCLK_OFF) ? "off" : "on"); 273 (clkslow & S3C2410_CLKSLOW_UCLK_OFF) ? "off" : "on");
274 274
275 s3c_pwmclk_init();
275 return 0; 276 return 0;
276} 277}
diff --git a/arch/arm/plat-s3c64xx/clock.c b/arch/arm/plat-s3c64xx/clock.c
index 523da0cb55c5..5a1e97e1f8f6 100644
--- a/arch/arm/plat-s3c64xx/clock.c
+++ b/arch/arm/plat-s3c64xx/clock.c
@@ -277,4 +277,6 @@ void s3c64xx_register_clocks(void)
277 277
278 (clkp->enable)(clkp, 0); 278 (clkp->enable)(clkp, 0);
279 } 279 }
280
281 s3c_pwmclk_init();
280} 282}