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.c44
1 files changed, 26 insertions, 18 deletions
diff --git a/drivers/clocksource/tcb_clksrc.c b/drivers/clocksource/tcb_clksrc.c
index 01b886e68822..90350b6612bb 100644
--- a/drivers/clocksource/tcb_clksrc.c
+++ b/drivers/clocksource/tcb_clksrc.c
@@ -21,8 +21,7 @@
21 * resolution better than 200 nsec). 21 * resolution better than 200 nsec).
22 * 22 *
23 * - The third channel may be used to provide a 16-bit clockevent 23 * - The third channel may be used to provide a 16-bit clockevent
24 * source, used in either periodic or oneshot mode. This runs 24 * source, used in either periodic or oneshot mode.
25 * at 32 KiHZ, and can handle delays of up to two seconds.
26 * 25 *
27 * A boot clocksource and clockevent source are also currently needed, 26 * A boot clocksource and clockevent source are also currently needed,
28 * unless the relevant platforms (ARM/AT91, AVR32/AT32) are changed so 27 * unless the relevant platforms (ARM/AT91, AVR32/AT32) are changed so
@@ -68,6 +67,7 @@ static struct clocksource clksrc = {
68struct tc_clkevt_device { 67struct tc_clkevt_device {
69 struct clock_event_device clkevt; 68 struct clock_event_device clkevt;
70 struct clk *clk; 69 struct clk *clk;
70 u32 freq;
71 void __iomem *regs; 71 void __iomem *regs;
72}; 72};
73 73
@@ -76,13 +76,6 @@ static struct tc_clkevt_device *to_tc_clkevt(struct clock_event_device *clkevt)
76 return container_of(clkevt, struct tc_clkevt_device, clkevt); 76 return container_of(clkevt, struct tc_clkevt_device, clkevt);
77} 77}
78 78
79/* For now, we always use the 32K clock ... this optimizes for NO_HZ,
80 * because using one of the divided clocks would usually mean the
81 * tick rate can never be less than several dozen Hz (vs 0.5 Hz).
82 *
83 * A divided clock could be good for high resolution timers, since
84 * 30.5 usec resolution can seem "low".
85 */
86static u32 timer_clock; 79static u32 timer_clock;
87 80
88static void tc_mode(enum clock_event_mode m, struct clock_event_device *d) 81static void tc_mode(enum clock_event_mode m, struct clock_event_device *d)
@@ -105,11 +98,12 @@ static void tc_mode(enum clock_event_mode m, struct clock_event_device *d)
105 case CLOCK_EVT_MODE_PERIODIC: 98 case CLOCK_EVT_MODE_PERIODIC:
106 clk_enable(tcd->clk); 99 clk_enable(tcd->clk);
107 100
108 /* slow clock, count up to RC, then irq and restart */ 101 /* count up to RC, then irq and restart */
109 __raw_writel(timer_clock 102 __raw_writel(timer_clock
110 | ATMEL_TC_WAVE | ATMEL_TC_WAVESEL_UP_AUTO, 103 | ATMEL_TC_WAVE | ATMEL_TC_WAVESEL_UP_AUTO,
111 regs + ATMEL_TC_REG(2, CMR)); 104 regs + ATMEL_TC_REG(2, CMR));
112 __raw_writel((32768 + HZ/2) / HZ, tcaddr + ATMEL_TC_REG(2, RC)); 105 __raw_writel((tcd->freq + HZ/2)/HZ,
106 tcaddr + ATMEL_TC_REG(2, RC));
113 107
114 /* Enable clock and interrupts on RC compare */ 108 /* Enable clock and interrupts on RC compare */
115 __raw_writel(ATMEL_TC_CPCS, regs + ATMEL_TC_REG(2, IER)); 109 __raw_writel(ATMEL_TC_CPCS, regs + ATMEL_TC_REG(2, IER));
@@ -122,7 +116,7 @@ static void tc_mode(enum clock_event_mode m, struct clock_event_device *d)
122 case CLOCK_EVT_MODE_ONESHOT: 116 case CLOCK_EVT_MODE_ONESHOT:
123 clk_enable(tcd->clk); 117 clk_enable(tcd->clk);
124 118
125 /* slow clock, count up to RC, then irq and stop */ 119 /* count up to RC, then irq and stop */
126 __raw_writel(timer_clock | ATMEL_TC_CPCSTOP 120 __raw_writel(timer_clock | ATMEL_TC_CPCSTOP
127 | ATMEL_TC_WAVE | ATMEL_TC_WAVESEL_UP_AUTO, 121 | ATMEL_TC_WAVE | ATMEL_TC_WAVESEL_UP_AUTO,
128 regs + ATMEL_TC_REG(2, CMR)); 122 regs + ATMEL_TC_REG(2, CMR));
@@ -152,8 +146,12 @@ static struct tc_clkevt_device clkevt = {
152 .features = CLOCK_EVT_FEAT_PERIODIC 146 .features = CLOCK_EVT_FEAT_PERIODIC
153 | CLOCK_EVT_FEAT_ONESHOT, 147 | CLOCK_EVT_FEAT_ONESHOT,
154 .shift = 32, 148 .shift = 32,
149#ifdef CONFIG_ATMEL_TCB_CLKSRC_USE_SLOW_CLOCK
155 /* Should be lower than at91rm9200's system timer */ 150 /* Should be lower than at91rm9200's system timer */
156 .rating = 125, 151 .rating = 125,
152#else
153 .rating = 200,
154#endif
157 .set_next_event = tc_next_event, 155 .set_next_event = tc_next_event,
158 .set_mode = tc_mode, 156 .set_mode = tc_mode,
159 }, 157 },
@@ -179,8 +177,9 @@ static struct irqaction tc_irqaction = {
179 .handler = ch2_irq, 177 .handler = ch2_irq,
180}; 178};
181 179
182static void __init setup_clkevents(struct atmel_tc *tc, int clk32k_divisor_idx) 180static void __init setup_clkevents(struct atmel_tc *tc, int divisor_idx)
183{ 181{
182 unsigned divisor = atmel_tc_divisors[divisor_idx];
184 struct clk *t2_clk = tc->clk[2]; 183 struct clk *t2_clk = tc->clk[2];
185 int irq = tc->irq[2]; 184 int irq = tc->irq[2];
186 185
@@ -188,11 +187,17 @@ static void __init setup_clkevents(struct atmel_tc *tc, int clk32k_divisor_idx)
188 clkevt.clk = t2_clk; 187 clkevt.clk = t2_clk;
189 tc_irqaction.dev_id = &clkevt; 188 tc_irqaction.dev_id = &clkevt;
190 189
191 timer_clock = clk32k_divisor_idx; 190 timer_clock = divisor_idx;
192 191
193 clkevt.clkevt.mult = div_sc(32768, NSEC_PER_SEC, clkevt.clkevt.shift); 192 if (!divisor)
194 clkevt.clkevt.max_delta_ns 193 clkevt.freq = 32768;
195 = clockevent_delta2ns(0xffff, &clkevt.clkevt); 194 else
195 clkevt.freq = clk_get_rate(t2_clk)/divisor;
196
197 clkevt.clkevt.mult = div_sc(clkevt.freq, NSEC_PER_SEC,
198 clkevt.clkevt.shift);
199 clkevt.clkevt.max_delta_ns =
200 clockevent_delta2ns(0xffff, &clkevt.clkevt);
196 clkevt.clkevt.min_delta_ns = clockevent_delta2ns(1, &clkevt.clkevt) + 1; 201 clkevt.clkevt.min_delta_ns = clockevent_delta2ns(1, &clkevt.clkevt) + 1;
197 clkevt.clkevt.cpumask = cpumask_of(0); 202 clkevt.clkevt.cpumask = cpumask_of(0);
198 203
@@ -295,8 +300,11 @@ static int __init tcb_clksrc_init(void)
295 clocksource_register(&clksrc); 300 clocksource_register(&clksrc);
296 301
297 /* channel 2: periodic and oneshot timer support */ 302 /* channel 2: periodic and oneshot timer support */
303#ifdef CONFIG_ATMEL_TCB_CLKSRC_USE_SLOW_CLOCK
298 setup_clkevents(tc, clk32k_divisor_idx); 304 setup_clkevents(tc, clk32k_divisor_idx);
299 305#else
306 setup_clkevents(tc, best_divisor_idx);
307#endif
300 return 0; 308 return 0;
301} 309}
302arch_initcall(tcb_clksrc_init); 310arch_initcall(tcb_clksrc_init);