aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/clocksource/tcb_clksrc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/clocksource/tcb_clksrc.c')
-rw-r--r--drivers/clocksource/tcb_clksrc.c90
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
59static cycle_t tc_get_cycles32(struct clocksource *cs)
60{
61 return __raw_readl(tcaddr + ATMEL_TC_REG(0, CV));
62}
63
57static struct clocksource clksrc = { 64static 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
219static 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
247static 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
212static int __init tcb_clksrc_init(void) 261static 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);