| .. | .. |
|---|
| 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 | |
|---|