diff options
author | Ben Dooks <ben-linux@fluff.org> | 2008-11-21 05:36:05 -0500 |
---|---|---|
committer | Ben Dooks <ben-linux@fluff.org> | 2008-12-16 05:13:02 -0500 |
commit | 9d325f23416d1525401d43442bafca2bb254ab74 (patch) | |
tree | 4b8e0ef7c121c263d2cbbea136c97d81d6be0463 /arch/arm/plat-s3c/time.c | |
parent | b09bcdd4c2f52b54115895c4d62ad82918f71431 (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/plat-s3c/time.c')
-rw-r--r-- | arch/arm/plat-s3c/time.c | 66 |
1 files changed, 45 insertions, 21 deletions
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 | ||
151 | static struct clk *tin; | ||
152 | static struct clk *tdiv; | ||
153 | static 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 | ||
248 | static 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 | |||
251 | static void __init s3c2410_timer_init(void) | 274 | static 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 | } |