Skip to content

Commit ef1d6a2

Browse files
alexandrebellonidlezcano
authored andcommitted
clocksource/drivers/timer-atmel-tcb: Stop using the 32kHz for clockevents
Stop using the slow clock as the clock source for 32 bit counters because even at 10MHz, they are able to handle delays up to two minutes. This provides a way better resolution. Signed-off-by: Alexandre Belloni <[email protected]> Signed-off-by: Daniel Lezcano <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent d2c60dc commit ef1d6a2

File tree

1 file changed

+33
-30
lines changed

1 file changed

+33
-30
lines changed

drivers/clocksource/timer-atmel-tcb.c

Lines changed: 33 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,10 @@
2727
* - Some chips support 32 bit counter. A single channel is used for
2828
* this 32 bit free-running counter. the second channel is not used.
2929
*
30-
* - The third channel may be used to provide a 16-bit clockevent
31-
* source, used in either periodic or oneshot mode. This runs
32-
* at 32 KiHZ, and can handle delays of up to two seconds.
30+
* - The third channel may be used to provide a clockevent source, used in
31+
* either periodic or oneshot mode. For 16-bit counter its runs at 32 KiHZ,
32+
* and can handle delays of up to two seconds. For 32-bit counters, it runs at
33+
* the same rate as the clocksource
3334
*
3435
* REVISIT behavior during system suspend states... we should disable
3536
* all clocks and save the power. Easily done for clockevent devices,
@@ -47,6 +48,8 @@ static struct
4748
} tcb_cache[3];
4849
static u32 bmr_cache;
4950

51+
static const u8 atmel_tcb_divisors[] = { 2, 8, 32, 128 };
52+
5053
static u64 tc_get_cycles(struct clocksource *cs)
5154
{
5255
unsigned long flags;
@@ -143,6 +146,7 @@ static unsigned long notrace tc_delay_timer_read32(void)
143146
struct tc_clkevt_device {
144147
struct clock_event_device clkevt;
145148
struct clk *clk;
149+
u32 rate;
146150
void __iomem *regs;
147151
};
148152

@@ -151,13 +155,6 @@ static struct tc_clkevt_device *to_tc_clkevt(struct clock_event_device *clkevt)
151155
return container_of(clkevt, struct tc_clkevt_device, clkevt);
152156
}
153157

154-
/* For now, we always use the 32K clock ... this optimizes for NO_HZ,
155-
* because using one of the divided clocks would usually mean the
156-
* tick rate can never be less than several dozen Hz (vs 0.5 Hz).
157-
*
158-
* A divided clock could be good for high resolution timers, since
159-
* 30.5 usec resolution can seem "low".
160-
*/
161158
static u32 timer_clock;
162159

163160
static int tc_shutdown(struct clock_event_device *d)
@@ -183,7 +180,7 @@ static int tc_set_oneshot(struct clock_event_device *d)
183180

184181
clk_enable(tcd->clk);
185182

186-
/* slow clock, count up to RC, then irq and stop */
183+
/* count up to RC, then irq and stop */
187184
writel(timer_clock | ATMEL_TC_CPCSTOP | ATMEL_TC_WAVE |
188185
ATMEL_TC_WAVESEL_UP_AUTO, regs + ATMEL_TC_REG(2, CMR));
189186
writel(ATMEL_TC_CPCS, regs + ATMEL_TC_REG(2, IER));
@@ -205,10 +202,10 @@ static int tc_set_periodic(struct clock_event_device *d)
205202
*/
206203
clk_enable(tcd->clk);
207204

208-
/* slow clock, count up to RC, then irq and restart */
205+
/* count up to RC, then irq and restart */
209206
writel(timer_clock | ATMEL_TC_WAVE | ATMEL_TC_WAVESEL_UP_AUTO,
210207
regs + ATMEL_TC_REG(2, CMR));
211-
writel((32768 + HZ / 2) / HZ, tcaddr + ATMEL_TC_REG(2, RC));
208+
writel((tcd->rate + HZ / 2) / HZ, tcaddr + ATMEL_TC_REG(2, RC));
212209

213210
/* Enable clock and interrupts on RC compare */
214211
writel(ATMEL_TC_CPCS, regs + ATMEL_TC_REG(2, IER));
@@ -256,47 +253,55 @@ static irqreturn_t ch2_irq(int irq, void *handle)
256253
return IRQ_NONE;
257254
}
258255

259-
static int __init setup_clkevents(struct atmel_tc *tc, int clk32k_divisor_idx)
256+
static int __init setup_clkevents(struct atmel_tc *tc, int divisor_idx)
260257
{
261258
int ret;
262259
struct clk *t2_clk = tc->clk[2];
263260
int irq = tc->irq[2];
264-
265-
ret = clk_prepare_enable(tc->slow_clk);
266-
if (ret)
267-
return ret;
261+
int bits = tc->tcb_config->counter_width;
268262

269263
/* try to enable t2 clk to avoid future errors in mode change */
270264
ret = clk_prepare_enable(t2_clk);
271-
if (ret) {
272-
clk_disable_unprepare(tc->slow_clk);
265+
if (ret)
273266
return ret;
274-
}
275-
276-
clk_disable(t2_clk);
277267

278268
clkevt.regs = tc->regs;
279269
clkevt.clk = t2_clk;
280270

281-
timer_clock = clk32k_divisor_idx;
271+
if (bits == 32) {
272+
timer_clock = divisor_idx;
273+
clkevt.rate = clk_get_rate(t2_clk) / atmel_tcb_divisors[divisor_idx];
274+
} else {
275+
ret = clk_prepare_enable(tc->slow_clk);
276+
if (ret) {
277+
clk_disable_unprepare(t2_clk);
278+
return ret;
279+
}
280+
281+
clkevt.rate = clk_get_rate(tc->slow_clk);
282+
timer_clock = ATMEL_TC_TIMER_CLOCK5;
283+
}
284+
285+
clk_disable(t2_clk);
282286

283287
clkevt.clkevt.cpumask = cpumask_of(0);
284288

285289
ret = request_irq(irq, ch2_irq, IRQF_TIMER, "tc_clkevt", &clkevt);
286290
if (ret) {
287291
clk_unprepare(t2_clk);
288-
clk_disable_unprepare(tc->slow_clk);
292+
if (bits != 32)
293+
clk_disable_unprepare(tc->slow_clk);
289294
return ret;
290295
}
291296

292-
clockevents_config_and_register(&clkevt.clkevt, 32768, 1, 0xffff);
297+
clockevents_config_and_register(&clkevt.clkevt, clkevt.rate, 1, BIT(bits) - 1);
293298

294299
return ret;
295300
}
296301

297302
#else /* !CONFIG_GENERIC_CLOCKEVENTS */
298303

299-
static int __init setup_clkevents(struct atmel_tc *tc, int clk32k_divisor_idx)
304+
static int __init setup_clkevents(struct atmel_tc *tc, int divisor_idx)
300305
{
301306
/* NOTHING */
302307
return 0;
@@ -346,8 +351,6 @@ static void __init tcb_setup_single_chan(struct atmel_tc *tc, int mck_divisor_id
346351
writel(ATMEL_TC_SYNC, tcaddr + ATMEL_TC_BCR);
347352
}
348353

349-
static const u8 atmel_tcb_divisors[] = { 2, 8, 32, 128 };
350-
351354
static struct atmel_tcb_config tcb_rm9200_config = {
352355
.counter_width = 16,
353356
};
@@ -472,7 +475,7 @@ static int __init tcb_clksrc_init(struct device_node *node)
472475
goto err_disable_t1;
473476

474477
/* channel 2: periodic and oneshot timer support */
475-
ret = setup_clkevents(&tc, ATMEL_TC_TIMER_CLOCK5);
478+
ret = setup_clkevents(&tc, best_divisor_idx);
476479
if (ret)
477480
goto err_unregister_clksrc;
478481

0 commit comments

Comments
 (0)