diff options
Diffstat (limited to 'drivers/clocksource/tcb_clksrc.c')
-rw-r--r-- | drivers/clocksource/tcb_clksrc.c | 90 |
1 files changed, 62 insertions, 28 deletions
diff --git a/drivers/clocksource/tcb_clksrc.c b/drivers/clocksource/tcb_clksrc.c index 55d0f95f82f9..32cb929b8eb6 100644 --- a/drivers/clocksource/tcb_clksrc.c +++ b/drivers/clocksource/tcb_clksrc.c | |||
@@ -19,6 +19,8 @@ | |||
19 | * - Two channels combine to create a free-running 32 bit counter | 19 | * - Two channels combine to create a free-running 32 bit counter |
20 | * with a base rate of 5+ MHz, packaged as a clocksource (with | 20 | * with a base rate of 5+ MHz, packaged as a clocksource (with |
21 | * resolution better than 200 nsec). | 21 | * resolution better than 200 nsec). |
22 | * - Some chips support 32 bit counter. A single channel is used for | ||
23 | * this 32 bit free-running counter. the second channel is not used. | ||
22 | * | 24 | * |
23 | * - The third channel may be used to provide a 16-bit clockevent | 25 | * - The third channel may be used to provide a 16-bit clockevent |
24 | * source, used in either periodic or oneshot mode. This runs | 26 | * source, used in either periodic or oneshot mode. This runs |
@@ -54,6 +56,11 @@ static cycle_t tc_get_cycles(struct clocksource *cs) | |||
54 | return (upper << 16) | lower; | 56 | return (upper << 16) | lower; |
55 | } | 57 | } |
56 | 58 | ||
59 | static cycle_t tc_get_cycles32(struct clocksource *cs) | ||
60 | { | ||
61 | return __raw_readl(tcaddr + ATMEL_TC_REG(0, CV)); | ||
62 | } | ||
63 | |||
57 | static struct clocksource clksrc = { | 64 | static struct clocksource clksrc = { |
58 | .name = "tcb_clksrc", | 65 | .name = "tcb_clksrc", |
59 | .rating = 200, | 66 | .rating = 200, |
@@ -209,6 +216,48 @@ static void __init setup_clkevents(struct atmel_tc *tc, int clk32k_divisor_idx) | |||
209 | 216 | ||
210 | #endif | 217 | #endif |
211 | 218 | ||
219 | static void __init tcb_setup_dual_chan(struct atmel_tc *tc, int mck_divisor_idx) | ||
220 | { | ||
221 | /* channel 0: waveform mode, input mclk/8, clock TIOA0 on overflow */ | ||
222 | __raw_writel(mck_divisor_idx /* likely divide-by-8 */ | ||
223 | | ATMEL_TC_WAVE | ||
224 | | ATMEL_TC_WAVESEL_UP /* free-run */ | ||
225 | | ATMEL_TC_ACPA_SET /* TIOA0 rises at 0 */ | ||
226 | | ATMEL_TC_ACPC_CLEAR, /* (duty cycle 50%) */ | ||
227 | tcaddr + ATMEL_TC_REG(0, CMR)); | ||
228 | __raw_writel(0x0000, tcaddr + ATMEL_TC_REG(0, RA)); | ||
229 | __raw_writel(0x8000, tcaddr + ATMEL_TC_REG(0, RC)); | ||
230 | __raw_writel(0xff, tcaddr + ATMEL_TC_REG(0, IDR)); /* no irqs */ | ||
231 | __raw_writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(0, CCR)); | ||
232 | |||
233 | /* channel 1: waveform mode, input TIOA0 */ | ||
234 | __raw_writel(ATMEL_TC_XC1 /* input: TIOA0 */ | ||
235 | | ATMEL_TC_WAVE | ||
236 | | ATMEL_TC_WAVESEL_UP, /* free-run */ | ||
237 | tcaddr + ATMEL_TC_REG(1, CMR)); | ||
238 | __raw_writel(0xff, tcaddr + ATMEL_TC_REG(1, IDR)); /* no irqs */ | ||
239 | __raw_writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(1, CCR)); | ||
240 | |||
241 | /* chain channel 0 to channel 1*/ | ||
242 | __raw_writel(ATMEL_TC_TC1XC1S_TIOA0, tcaddr + ATMEL_TC_BMR); | ||
243 | /* then reset all the timers */ | ||
244 | __raw_writel(ATMEL_TC_SYNC, tcaddr + ATMEL_TC_BCR); | ||
245 | } | ||
246 | |||
247 | static void __init tcb_setup_single_chan(struct atmel_tc *tc, int mck_divisor_idx) | ||
248 | { | ||
249 | /* channel 0: waveform mode, input mclk/8 */ | ||
250 | __raw_writel(mck_divisor_idx /* likely divide-by-8 */ | ||
251 | | ATMEL_TC_WAVE | ||
252 | | ATMEL_TC_WAVESEL_UP, /* free-run */ | ||
253 | tcaddr + ATMEL_TC_REG(0, CMR)); | ||
254 | __raw_writel(0xff, tcaddr + ATMEL_TC_REG(0, IDR)); /* no irqs */ | ||
255 | __raw_writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(0, CCR)); | ||
256 | |||
257 | /* then reset all the timers */ | ||
258 | __raw_writel(ATMEL_TC_SYNC, tcaddr + ATMEL_TC_BCR); | ||
259 | } | ||
260 | |||
212 | static int __init tcb_clksrc_init(void) | 261 | static int __init tcb_clksrc_init(void) |
213 | { | 262 | { |
214 | static char bootinfo[] __initdata | 263 | static char bootinfo[] __initdata |
@@ -260,34 +309,19 @@ static int __init tcb_clksrc_init(void) | |||
260 | divided_rate / 1000000, | 309 | divided_rate / 1000000, |
261 | ((divided_rate + 500000) % 1000000) / 1000); | 310 | ((divided_rate + 500000) % 1000000) / 1000); |
262 | 311 | ||
263 | /* tclib will give us three clocks no matter what the | 312 | if (tc->tcb_config && tc->tcb_config->counter_width == 32) { |
264 | * underlying platform supports. | 313 | /* use apropriate function to read 32 bit counter */ |
265 | */ | 314 | clksrc.read = tc_get_cycles32; |
266 | clk_enable(tc->clk[1]); | 315 | /* setup ony channel 0 */ |
267 | 316 | tcb_setup_single_chan(tc, best_divisor_idx); | |
268 | /* channel 0: waveform mode, input mclk/8, clock TIOA0 on overflow */ | 317 | } else { |
269 | __raw_writel(best_divisor_idx /* likely divide-by-8 */ | 318 | /* tclib will give us three clocks no matter what the |
270 | | ATMEL_TC_WAVE | 319 | * underlying platform supports. |
271 | | ATMEL_TC_WAVESEL_UP /* free-run */ | 320 | */ |
272 | | ATMEL_TC_ACPA_SET /* TIOA0 rises at 0 */ | 321 | clk_enable(tc->clk[1]); |
273 | | ATMEL_TC_ACPC_CLEAR, /* (duty cycle 50%) */ | 322 | /* setup both channel 0 & 1 */ |
274 | tcaddr + ATMEL_TC_REG(0, CMR)); | 323 | tcb_setup_dual_chan(tc, best_divisor_idx); |
275 | __raw_writel(0x0000, tcaddr + ATMEL_TC_REG(0, RA)); | 324 | } |
276 | __raw_writel(0x8000, tcaddr + ATMEL_TC_REG(0, RC)); | ||
277 | __raw_writel(0xff, tcaddr + ATMEL_TC_REG(0, IDR)); /* no irqs */ | ||
278 | __raw_writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(0, CCR)); | ||
279 | |||
280 | /* channel 1: waveform mode, input TIOA0 */ | ||
281 | __raw_writel(ATMEL_TC_XC1 /* input: TIOA0 */ | ||
282 | | ATMEL_TC_WAVE | ||
283 | | ATMEL_TC_WAVESEL_UP, /* free-run */ | ||
284 | tcaddr + ATMEL_TC_REG(1, CMR)); | ||
285 | __raw_writel(0xff, tcaddr + ATMEL_TC_REG(1, IDR)); /* no irqs */ | ||
286 | __raw_writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(1, CCR)); | ||
287 | |||
288 | /* chain channel 0 to channel 1, then reset all the timers */ | ||
289 | __raw_writel(ATMEL_TC_TC1XC1S_TIOA0, tcaddr + ATMEL_TC_BMR); | ||
290 | __raw_writel(ATMEL_TC_SYNC, tcaddr + ATMEL_TC_BCR); | ||
291 | 325 | ||
292 | /* and away we go! */ | 326 | /* and away we go! */ |
293 | clocksource_register_hz(&clksrc, divided_rate); | 327 | clocksource_register_hz(&clksrc, divided_rate); |