.. | .. |
---|
25 | 25 | * this 32 bit free-running counter. the second channel is not used. |
---|
26 | 26 | * |
---|
27 | 27 | * - The third channel may be used to provide a 16-bit clockevent |
---|
28 | | - * source, used in either periodic or oneshot mode. This runs |
---|
29 | | - * at 32 KiHZ, and can handle delays of up to two seconds. |
---|
| 28 | + * source, used in either periodic or oneshot mode. |
---|
30 | 29 | * |
---|
31 | 30 | * A boot clocksource and clockevent source are also currently needed, |
---|
32 | 31 | * unless the relevant platforms (ARM/AT91, AVR32/AT32) are changed so |
---|
.. | .. |
---|
126 | 125 | struct tc_clkevt_device { |
---|
127 | 126 | struct clock_event_device clkevt; |
---|
128 | 127 | struct clk *clk; |
---|
| 128 | + bool clk_enabled; |
---|
| 129 | + u32 freq; |
---|
129 | 130 | void __iomem *regs; |
---|
130 | 131 | }; |
---|
131 | 132 | |
---|
.. | .. |
---|
134 | 135 | return container_of(clkevt, struct tc_clkevt_device, clkevt); |
---|
135 | 136 | } |
---|
136 | 137 | |
---|
137 | | -/* For now, we always use the 32K clock ... this optimizes for NO_HZ, |
---|
138 | | - * because using one of the divided clocks would usually mean the |
---|
139 | | - * tick rate can never be less than several dozen Hz (vs 0.5 Hz). |
---|
140 | | - * |
---|
141 | | - * A divided clock could be good for high resolution timers, since |
---|
142 | | - * 30.5 usec resolution can seem "low". |
---|
143 | | - */ |
---|
144 | 138 | static u32 timer_clock; |
---|
| 139 | + |
---|
| 140 | +static void tc_clk_disable(struct clock_event_device *d) |
---|
| 141 | +{ |
---|
| 142 | + struct tc_clkevt_device *tcd = to_tc_clkevt(d); |
---|
| 143 | + |
---|
| 144 | + clk_disable(tcd->clk); |
---|
| 145 | + tcd->clk_enabled = false; |
---|
| 146 | +} |
---|
| 147 | + |
---|
| 148 | +static void tc_clk_enable(struct clock_event_device *d) |
---|
| 149 | +{ |
---|
| 150 | + struct tc_clkevt_device *tcd = to_tc_clkevt(d); |
---|
| 151 | + |
---|
| 152 | + if (tcd->clk_enabled) |
---|
| 153 | + return; |
---|
| 154 | + clk_enable(tcd->clk); |
---|
| 155 | + tcd->clk_enabled = true; |
---|
| 156 | +} |
---|
145 | 157 | |
---|
146 | 158 | static int tc_shutdown(struct clock_event_device *d) |
---|
147 | 159 | { |
---|
.. | .. |
---|
150 | 162 | |
---|
151 | 163 | writel(0xff, regs + ATMEL_TC_REG(2, IDR)); |
---|
152 | 164 | writel(ATMEL_TC_CLKDIS, regs + ATMEL_TC_REG(2, CCR)); |
---|
| 165 | + return 0; |
---|
| 166 | +} |
---|
| 167 | + |
---|
| 168 | +static int tc_shutdown_clk_off(struct clock_event_device *d) |
---|
| 169 | +{ |
---|
| 170 | + tc_shutdown(d); |
---|
153 | 171 | if (!clockevent_state_detached(d)) |
---|
154 | | - clk_disable(tcd->clk); |
---|
| 172 | + tc_clk_disable(d); |
---|
155 | 173 | |
---|
156 | 174 | return 0; |
---|
157 | 175 | } |
---|
.. | .. |
---|
164 | 182 | if (clockevent_state_oneshot(d) || clockevent_state_periodic(d)) |
---|
165 | 183 | tc_shutdown(d); |
---|
166 | 184 | |
---|
167 | | - clk_enable(tcd->clk); |
---|
| 185 | + tc_clk_enable(d); |
---|
168 | 186 | |
---|
169 | | - /* slow clock, count up to RC, then irq and stop */ |
---|
| 187 | + /* count up to RC, then irq and stop */ |
---|
170 | 188 | writel(timer_clock | ATMEL_TC_CPCSTOP | ATMEL_TC_WAVE | |
---|
171 | 189 | ATMEL_TC_WAVESEL_UP_AUTO, regs + ATMEL_TC_REG(2, CMR)); |
---|
172 | 190 | writel(ATMEL_TC_CPCS, regs + ATMEL_TC_REG(2, IER)); |
---|
.. | .. |
---|
186 | 204 | /* By not making the gentime core emulate periodic mode on top |
---|
187 | 205 | * of oneshot, we get lower overhead and improved accuracy. |
---|
188 | 206 | */ |
---|
189 | | - clk_enable(tcd->clk); |
---|
| 207 | + tc_clk_enable(d); |
---|
190 | 208 | |
---|
191 | | - /* slow clock, count up to RC, then irq and restart */ |
---|
| 209 | + /* count up to RC, then irq and restart */ |
---|
192 | 210 | writel(timer_clock | ATMEL_TC_WAVE | ATMEL_TC_WAVESEL_UP_AUTO, |
---|
193 | 211 | regs + ATMEL_TC_REG(2, CMR)); |
---|
194 | | - writel((32768 + HZ / 2) / HZ, tcaddr + ATMEL_TC_REG(2, RC)); |
---|
| 212 | + writel((tcd->freq + HZ / 2) / HZ, tcaddr + ATMEL_TC_REG(2, RC)); |
---|
195 | 213 | |
---|
196 | 214 | /* Enable clock and interrupts on RC compare */ |
---|
197 | 215 | writel(ATMEL_TC_CPCS, regs + ATMEL_TC_REG(2, IER)); |
---|
.. | .. |
---|
218 | 236 | .features = CLOCK_EVT_FEAT_PERIODIC | |
---|
219 | 237 | CLOCK_EVT_FEAT_ONESHOT, |
---|
220 | 238 | /* Should be lower than at91rm9200's system timer */ |
---|
| 239 | +#ifdef CONFIG_ATMEL_TCB_CLKSRC_USE_SLOW_CLOCK |
---|
221 | 240 | .rating = 125, |
---|
| 241 | +#else |
---|
| 242 | + .rating = 200, |
---|
| 243 | +#endif |
---|
222 | 244 | .set_next_event = tc_next_event, |
---|
223 | | - .set_state_shutdown = tc_shutdown, |
---|
| 245 | + .set_state_shutdown = tc_shutdown_clk_off, |
---|
224 | 246 | .set_state_periodic = tc_set_periodic, |
---|
225 | 247 | .set_state_oneshot = tc_set_oneshot, |
---|
226 | 248 | }, |
---|
.. | .. |
---|
240 | 262 | return IRQ_NONE; |
---|
241 | 263 | } |
---|
242 | 264 | |
---|
243 | | -static int __init setup_clkevents(struct atmel_tc *tc, int clk32k_divisor_idx) |
---|
| 265 | +static int __init setup_clkevents(struct atmel_tc *tc, int divisor_idx) |
---|
244 | 266 | { |
---|
| 267 | + unsigned divisor = atmel_tc_divisors[divisor_idx]; |
---|
245 | 268 | int ret; |
---|
246 | 269 | struct clk *t2_clk = tc->clk[2]; |
---|
247 | 270 | int irq = tc->irq[2]; |
---|
.. | .. |
---|
262 | 285 | clkevt.regs = tc->regs; |
---|
263 | 286 | clkevt.clk = t2_clk; |
---|
264 | 287 | |
---|
265 | | - timer_clock = clk32k_divisor_idx; |
---|
| 288 | + timer_clock = divisor_idx; |
---|
| 289 | + if (!divisor) |
---|
| 290 | + clkevt.freq = 32768; |
---|
| 291 | + else |
---|
| 292 | + clkevt.freq = clk_get_rate(t2_clk) / divisor; |
---|
266 | 293 | |
---|
267 | 294 | clkevt.clkevt.cpumask = cpumask_of(0); |
---|
268 | 295 | |
---|
.. | .. |
---|
273 | 300 | return ret; |
---|
274 | 301 | } |
---|
275 | 302 | |
---|
276 | | - clockevents_config_and_register(&clkevt.clkevt, 32768, 1, 0xffff); |
---|
| 303 | + clockevents_config_and_register(&clkevt.clkevt, clkevt.freq, 1, 0xffff); |
---|
277 | 304 | |
---|
278 | 305 | return ret; |
---|
279 | 306 | } |
---|
.. | .. |
---|
410 | 437 | goto err_disable_t1; |
---|
411 | 438 | |
---|
412 | 439 | /* channel 2: periodic and oneshot timer support */ |
---|
| 440 | +#ifdef CONFIG_ATMEL_TCB_CLKSRC_USE_SLOW_CLOCK |
---|
413 | 441 | ret = setup_clkevents(tc, clk32k_divisor_idx); |
---|
| 442 | +#else |
---|
| 443 | + ret = setup_clkevents(tc, best_divisor_idx); |
---|
| 444 | +#endif |
---|
414 | 445 | if (ret) |
---|
415 | 446 | goto err_unregister_clksrc; |
---|
416 | 447 | |
---|