Blame view
drivers/clocksource/timer-atmel-tcb.c
13.4 KB
b24413180 License cleanup: ... |
1 |
// SPDX-License-Identifier: GPL-2.0 |
4d243f92e atmel_tc clocksou... |
2 3 4 5 6 7 8 |
#include <linux/init.h> #include <linux/clocksource.h> #include <linux/clockchips.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/clk.h> |
1ce861cec clocksource/drive... |
9 |
#include <linux/delay.h> |
4d243f92e atmel_tc clocksou... |
10 11 12 |
#include <linux/err.h> #include <linux/ioport.h> #include <linux/io.h> |
86232bfd2 clocksource/drive... |
13 14 |
#include <linux/of_address.h> #include <linux/of_irq.h> |
f712a1e8e clocksource/drive... |
15 |
#include <linux/sched_clock.h> |
2a515e5d7 clocksource/drive... |
16 |
#include <linux/syscore_ops.h> |
c2c9136b7 ARM: at91: move S... |
17 |
#include <soc/at91/atmel_tcb.h> |
4d243f92e atmel_tc clocksou... |
18 19 20 21 22 23 24 25 26 |
/* * We're configured to use a specific TC block, one that's not hooked * up to external hardware, to provide a time solution: * * - Two channels combine to create a free-running 32 bit counter * with a base rate of 5+ MHz, packaged as a clocksource (with * resolution better than 200 nsec). |
8e315a7b0 ARM: at91/tc/cloc... |
27 28 |
* - Some chips support 32 bit counter. A single channel is used for * this 32 bit free-running counter. the second channel is not used. |
4d243f92e atmel_tc clocksou... |
29 |
* |
ef1d6a20e clocksource/drive... |
30 31 32 33 |
* - The third channel may be used to provide a clockevent source, used in * either periodic or oneshot mode. For 16-bit counter its runs at 32 KiHZ, * and can handle delays of up to two seconds. For 32-bit counters, it runs at * the same rate as the clocksource |
4d243f92e atmel_tc clocksou... |
34 |
* |
4d243f92e atmel_tc clocksou... |
35 36 37 38 39 40 41 |
* REVISIT behavior during system suspend states... we should disable * all clocks and save the power. Easily done for clockevent devices, * but clocksources won't necessarily get the needed notifications. * For deeper system sleep states, this will be mandatory... */ static void __iomem *tcaddr; |
2a515e5d7 clocksource/drive... |
42 43 44 45 46 47 48 49 |
static struct { u32 cmr; u32 imr; u32 rc; bool clken; } tcb_cache[3]; static u32 bmr_cache; |
4d243f92e atmel_tc clocksou... |
50 |
|
ef1d6a20e clocksource/drive... |
51 |
static const u8 atmel_tcb_divisors[] = { 2, 8, 32, 128 }; |
a5a1d1c29 clocksource: Use ... |
52 |
static u64 tc_get_cycles(struct clocksource *cs) |
4d243f92e atmel_tc clocksou... |
53 54 55 56 57 58 |
{ unsigned long flags; u32 lower, upper; raw_local_irq_save(flags); do { |
6ec8be251 clocksource/drive... |
59 60 61 |
upper = readl_relaxed(tcaddr + ATMEL_TC_REG(1, CV)); lower = readl_relaxed(tcaddr + ATMEL_TC_REG(0, CV)); } while (upper != readl_relaxed(tcaddr + ATMEL_TC_REG(1, CV))); |
4d243f92e atmel_tc clocksou... |
62 63 64 65 |
raw_local_irq_restore(flags); return (upper << 16) | lower; } |
7b9f1d16e clocksource/drive... |
66 67 |
static u64 tc_get_cycles32(struct clocksource *cs) { |
6ec8be251 clocksource/drive... |
68 |
return readl_relaxed(tcaddr + ATMEL_TC_REG(0, CV)); |
7b9f1d16e clocksource/drive... |
69 |
} |
7ebe68100 clocksource/drive... |
70 |
static void tc_clksrc_suspend(struct clocksource *cs) |
2a515e5d7 clocksource/drive... |
71 72 73 74 75 76 77 78 79 80 81 82 83 |
{ int i; for (i = 0; i < ARRAY_SIZE(tcb_cache); i++) { tcb_cache[i].cmr = readl(tcaddr + ATMEL_TC_REG(i, CMR)); tcb_cache[i].imr = readl(tcaddr + ATMEL_TC_REG(i, IMR)); tcb_cache[i].rc = readl(tcaddr + ATMEL_TC_REG(i, RC)); tcb_cache[i].clken = !!(readl(tcaddr + ATMEL_TC_REG(i, SR)) & ATMEL_TC_CLKSTA); } bmr_cache = readl(tcaddr + ATMEL_TC_BMR); } |
7ebe68100 clocksource/drive... |
84 |
static void tc_clksrc_resume(struct clocksource *cs) |
2a515e5d7 clocksource/drive... |
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
{ int i; for (i = 0; i < ARRAY_SIZE(tcb_cache); i++) { /* Restore registers for the channel, RA and RB are not used */ writel(tcb_cache[i].cmr, tcaddr + ATMEL_TC_REG(i, CMR)); writel(tcb_cache[i].rc, tcaddr + ATMEL_TC_REG(i, RC)); writel(0, tcaddr + ATMEL_TC_REG(i, RA)); writel(0, tcaddr + ATMEL_TC_REG(i, RB)); /* Disable all the interrupts */ writel(0xff, tcaddr + ATMEL_TC_REG(i, IDR)); /* Reenable interrupts that were enabled before suspending */ writel(tcb_cache[i].imr, tcaddr + ATMEL_TC_REG(i, IER)); /* Start the clock if it was used */ if (tcb_cache[i].clken) writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(i, CCR)); } /* Dual channel, chain channels */ writel(bmr_cache, tcaddr + ATMEL_TC_BMR); /* Finally, trigger all the channels*/ writel(ATMEL_TC_SYNC, tcaddr + ATMEL_TC_BCR); } |
4d243f92e atmel_tc clocksou... |
108 |
static struct clocksource clksrc = { |
4d243f92e atmel_tc clocksou... |
109 110 111 |
.rating = 200, .read = tc_get_cycles, .mask = CLOCKSOURCE_MASK(32), |
4d243f92e atmel_tc clocksou... |
112 |
.flags = CLOCK_SOURCE_IS_CONTINUOUS, |
2a515e5d7 clocksource/drive... |
113 114 |
.suspend = tc_clksrc_suspend, .resume = tc_clksrc_resume, |
4d243f92e atmel_tc clocksou... |
115 |
}; |
f712a1e8e clocksource/drive... |
116 117 118 119 120 121 122 123 124 |
static u64 notrace tc_sched_clock_read(void) { return tc_get_cycles(&clksrc); } static u64 notrace tc_sched_clock_read32(void) { return tc_get_cycles32(&clksrc); } |
1ce861cec clocksource/drive... |
125 126 127 128 129 130 131 132 133 134 135 |
static struct delay_timer tc_delay_timer; static unsigned long tc_delay_timer_read(void) { return tc_get_cycles(&clksrc); } static unsigned long notrace tc_delay_timer_read32(void) { return tc_get_cycles32(&clksrc); } |
4d243f92e atmel_tc clocksou... |
136 137 138 139 140 |
#ifdef CONFIG_GENERIC_CLOCKEVENTS struct tc_clkevt_device { struct clock_event_device clkevt; struct clk *clk; |
ef1d6a20e clocksource/drive... |
141 |
u32 rate; |
4d243f92e atmel_tc clocksou... |
142 143 144 145 146 147 148 |
void __iomem *regs; }; static struct tc_clkevt_device *to_tc_clkevt(struct clock_event_device *clkevt) { return container_of(clkevt, struct tc_clkevt_device, clkevt); } |
4d243f92e atmel_tc clocksou... |
149 |
static u32 timer_clock; |
cf4541c10 clockevents/drive... |
150 |
static int tc_shutdown(struct clock_event_device *d) |
4d243f92e atmel_tc clocksou... |
151 152 153 |
{ struct tc_clkevt_device *tcd = to_tc_clkevt(d); void __iomem *regs = tcd->regs; |
6ec8be251 clocksource/drive... |
154 155 |
writel(0xff, regs + ATMEL_TC_REG(2, IDR)); writel(ATMEL_TC_CLKDIS, regs + ATMEL_TC_REG(2, CCR)); |
f02b4b72d clockevents/tcb_c... |
156 157 |
if (!clockevent_state_detached(d)) clk_disable(tcd->clk); |
4d243f92e atmel_tc clocksou... |
158 |
|
cf4541c10 clockevents/drive... |
159 160 |
return 0; } |
4d243f92e atmel_tc clocksou... |
161 |
|
cf4541c10 clockevents/drive... |
162 163 164 165 |
static int tc_set_oneshot(struct clock_event_device *d) { struct tc_clkevt_device *tcd = to_tc_clkevt(d); void __iomem *regs = tcd->regs; |
4d243f92e atmel_tc clocksou... |
166 |
|
cf4541c10 clockevents/drive... |
167 168 |
if (clockevent_state_oneshot(d) || clockevent_state_periodic(d)) tc_shutdown(d); |
4d243f92e atmel_tc clocksou... |
169 |
|
cf4541c10 clockevents/drive... |
170 |
clk_enable(tcd->clk); |
4d243f92e atmel_tc clocksou... |
171 |
|
ef1d6a20e clocksource/drive... |
172 |
/* count up to RC, then irq and stop */ |
6ec8be251 clocksource/drive... |
173 |
writel(timer_clock | ATMEL_TC_CPCSTOP | ATMEL_TC_WAVE | |
cf4541c10 clockevents/drive... |
174 |
ATMEL_TC_WAVESEL_UP_AUTO, regs + ATMEL_TC_REG(2, CMR)); |
6ec8be251 clocksource/drive... |
175 |
writel(ATMEL_TC_CPCS, regs + ATMEL_TC_REG(2, IER)); |
4d243f92e atmel_tc clocksou... |
176 |
|
cf4541c10 clockevents/drive... |
177 178 179 |
/* set_next_event() configures and starts the timer */ return 0; } |
4d243f92e atmel_tc clocksou... |
180 |
|
cf4541c10 clockevents/drive... |
181 182 183 184 |
static int tc_set_periodic(struct clock_event_device *d) { struct tc_clkevt_device *tcd = to_tc_clkevt(d); void __iomem *regs = tcd->regs; |
4d243f92e atmel_tc clocksou... |
185 |
|
cf4541c10 clockevents/drive... |
186 187 |
if (clockevent_state_oneshot(d) || clockevent_state_periodic(d)) tc_shutdown(d); |
4d243f92e atmel_tc clocksou... |
188 |
|
cf4541c10 clockevents/drive... |
189 190 191 192 |
/* By not making the gentime core emulate periodic mode on top * of oneshot, we get lower overhead and improved accuracy. */ clk_enable(tcd->clk); |
ef1d6a20e clocksource/drive... |
193 |
/* count up to RC, then irq and restart */ |
6ec8be251 clocksource/drive... |
194 |
writel(timer_clock | ATMEL_TC_WAVE | ATMEL_TC_WAVESEL_UP_AUTO, |
cf4541c10 clockevents/drive... |
195 |
regs + ATMEL_TC_REG(2, CMR)); |
ef1d6a20e clocksource/drive... |
196 |
writel((tcd->rate + HZ / 2) / HZ, tcaddr + ATMEL_TC_REG(2, RC)); |
cf4541c10 clockevents/drive... |
197 198 |
/* Enable clock and interrupts on RC compare */ |
6ec8be251 clocksource/drive... |
199 |
writel(ATMEL_TC_CPCS, regs + ATMEL_TC_REG(2, IER)); |
cf4541c10 clockevents/drive... |
200 201 |
/* go go gadget! */ |
6ec8be251 clocksource/drive... |
202 |
writel(ATMEL_TC_CLKEN | ATMEL_TC_SWTRG, regs + |
cf4541c10 clockevents/drive... |
203 204 |
ATMEL_TC_REG(2, CCR)); return 0; |
4d243f92e atmel_tc clocksou... |
205 206 207 208 |
} static int tc_next_event(unsigned long delta, struct clock_event_device *d) { |
6ec8be251 clocksource/drive... |
209 |
writel_relaxed(delta, tcaddr + ATMEL_TC_REG(2, RC)); |
4d243f92e atmel_tc clocksou... |
210 211 |
/* go go gadget! */ |
6ec8be251 clocksource/drive... |
212 |
writel_relaxed(ATMEL_TC_CLKEN | ATMEL_TC_SWTRG, |
4d243f92e atmel_tc clocksou... |
213 214 215 216 217 218 |
tcaddr + ATMEL_TC_REG(2, CCR)); return 0; } static struct tc_clkevt_device clkevt = { .clkevt = { |
cf4541c10 clockevents/drive... |
219 220 |
.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, |
4d243f92e atmel_tc clocksou... |
221 |
/* Should be lower than at91rm9200's system timer */ |
cf4541c10 clockevents/drive... |
222 223 224 225 226 |
.rating = 125, .set_next_event = tc_next_event, .set_state_shutdown = tc_shutdown, .set_state_periodic = tc_set_periodic, .set_state_oneshot = tc_set_oneshot, |
4d243f92e atmel_tc clocksou... |
227 228 229 230 231 232 233 |
}, }; static irqreturn_t ch2_irq(int irq, void *handle) { struct tc_clkevt_device *dev = handle; unsigned int sr; |
6ec8be251 clocksource/drive... |
234 |
sr = readl_relaxed(dev->regs + ATMEL_TC_REG(2, SR)); |
4d243f92e atmel_tc clocksou... |
235 236 237 238 239 240 241 |
if (sr & ATMEL_TC_CPCS) { dev->clkevt.event_handler(&dev->clkevt); return IRQ_HANDLED; } return IRQ_NONE; } |
ef1d6a20e clocksource/drive... |
242 |
static int __init setup_clkevents(struct atmel_tc *tc, int divisor_idx) |
4d243f92e atmel_tc clocksou... |
243 |
{ |
5b3c11da1 clocksource: tcb_... |
244 |
int ret; |
4d243f92e atmel_tc clocksou... |
245 246 |
struct clk *t2_clk = tc->clk[2]; int irq = tc->irq[2]; |
ef1d6a20e clocksource/drive... |
247 |
int bits = tc->tcb_config->counter_width; |
7d8d05d11 misc: atmel_tclib... |
248 |
|
5b3c11da1 clocksource: tcb_... |
249 250 |
/* try to enable t2 clk to avoid future errors in mode change */ ret = clk_prepare_enable(t2_clk); |
ef1d6a20e clocksource/drive... |
251 |
if (ret) |
5b3c11da1 clocksource: tcb_... |
252 |
return ret; |
5b3c11da1 clocksource: tcb_... |
253 |
|
4d243f92e atmel_tc clocksou... |
254 255 |
clkevt.regs = tc->regs; clkevt.clk = t2_clk; |
4d243f92e atmel_tc clocksou... |
256 |
|
ef1d6a20e clocksource/drive... |
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 |
if (bits == 32) { timer_clock = divisor_idx; clkevt.rate = clk_get_rate(t2_clk) / atmel_tcb_divisors[divisor_idx]; } else { ret = clk_prepare_enable(tc->slow_clk); if (ret) { clk_disable_unprepare(t2_clk); return ret; } clkevt.rate = clk_get_rate(tc->slow_clk); timer_clock = ATMEL_TC_TIMER_CLOCK5; } clk_disable(t2_clk); |
4d243f92e atmel_tc clocksou... |
272 |
|
320ab2b0b cpumask: convert ... |
273 |
clkevt.clkevt.cpumask = cpumask_of(0); |
4d243f92e atmel_tc clocksou... |
274 |
|
d07a1ecdf clocksource: tcb_... |
275 276 |
ret = request_irq(irq, ch2_irq, IRQF_TIMER, "tc_clkevt", &clkevt); if (ret) { |
eed9fb9df clocksource: tcb_... |
277 |
clk_unprepare(t2_clk); |
ef1d6a20e clocksource/drive... |
278 279 |
if (bits != 32) clk_disable_unprepare(tc->slow_clk); |
5b3c11da1 clocksource: tcb_... |
280 |
return ret; |
d07a1ecdf clocksource: tcb_... |
281 |
} |
5b3c11da1 clocksource: tcb_... |
282 |
|
ef1d6a20e clocksource/drive... |
283 |
clockevents_config_and_register(&clkevt.clkevt, clkevt.rate, 1, BIT(bits) - 1); |
1817dc037 drivers/clocksour... |
284 |
|
5b3c11da1 clocksource: tcb_... |
285 |
return ret; |
4d243f92e atmel_tc clocksou... |
286 287 288 |
} #else /* !CONFIG_GENERIC_CLOCKEVENTS */ |
ef1d6a20e clocksource/drive... |
289 |
static int __init setup_clkevents(struct atmel_tc *tc, int divisor_idx) |
4d243f92e atmel_tc clocksou... |
290 291 |
{ /* NOTHING */ |
5b3c11da1 clocksource: tcb_... |
292 |
return 0; |
4d243f92e atmel_tc clocksou... |
293 294 295 |
} #endif |
8e315a7b0 ARM: at91/tc/cloc... |
296 297 298 |
static void __init tcb_setup_dual_chan(struct atmel_tc *tc, int mck_divisor_idx) { /* channel 0: waveform mode, input mclk/8, clock TIOA0 on overflow */ |
6ec8be251 clocksource/drive... |
299 |
writel(mck_divisor_idx /* likely divide-by-8 */ |
8e315a7b0 ARM: at91/tc/cloc... |
300 301 302 303 304 |
| ATMEL_TC_WAVE | ATMEL_TC_WAVESEL_UP /* free-run */ | ATMEL_TC_ACPA_SET /* TIOA0 rises at 0 */ | ATMEL_TC_ACPC_CLEAR, /* (duty cycle 50%) */ tcaddr + ATMEL_TC_REG(0, CMR)); |
6ec8be251 clocksource/drive... |
305 306 307 308 |
writel(0x0000, tcaddr + ATMEL_TC_REG(0, RA)); writel(0x8000, tcaddr + ATMEL_TC_REG(0, RC)); writel(0xff, tcaddr + ATMEL_TC_REG(0, IDR)); /* no irqs */ writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(0, CCR)); |
8e315a7b0 ARM: at91/tc/cloc... |
309 310 |
/* channel 1: waveform mode, input TIOA0 */ |
6ec8be251 clocksource/drive... |
311 |
writel(ATMEL_TC_XC1 /* input: TIOA0 */ |
8e315a7b0 ARM: at91/tc/cloc... |
312 313 314 |
| ATMEL_TC_WAVE | ATMEL_TC_WAVESEL_UP, /* free-run */ tcaddr + ATMEL_TC_REG(1, CMR)); |
6ec8be251 clocksource/drive... |
315 316 |
writel(0xff, tcaddr + ATMEL_TC_REG(1, IDR)); /* no irqs */ writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(1, CCR)); |
8e315a7b0 ARM: at91/tc/cloc... |
317 318 |
/* chain channel 0 to channel 1*/ |
6ec8be251 clocksource/drive... |
319 |
writel(ATMEL_TC_TC1XC1S_TIOA0, tcaddr + ATMEL_TC_BMR); |
8e315a7b0 ARM: at91/tc/cloc... |
320 |
/* then reset all the timers */ |
6ec8be251 clocksource/drive... |
321 |
writel(ATMEL_TC_SYNC, tcaddr + ATMEL_TC_BCR); |
8e315a7b0 ARM: at91/tc/cloc... |
322 323 324 325 326 |
} static void __init tcb_setup_single_chan(struct atmel_tc *tc, int mck_divisor_idx) { /* channel 0: waveform mode, input mclk/8 */ |
6ec8be251 clocksource/drive... |
327 |
writel(mck_divisor_idx /* likely divide-by-8 */ |
8e315a7b0 ARM: at91/tc/cloc... |
328 329 330 |
| ATMEL_TC_WAVE | ATMEL_TC_WAVESEL_UP, /* free-run */ tcaddr + ATMEL_TC_REG(0, CMR)); |
6ec8be251 clocksource/drive... |
331 332 |
writel(0xff, tcaddr + ATMEL_TC_REG(0, IDR)); /* no irqs */ writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(0, CCR)); |
8e315a7b0 ARM: at91/tc/cloc... |
333 334 |
/* then reset all the timers */ |
6ec8be251 clocksource/drive... |
335 |
writel(ATMEL_TC_SYNC, tcaddr + ATMEL_TC_BCR); |
8e315a7b0 ARM: at91/tc/cloc... |
336 |
} |
d2c60dcf8 clocksource/drive... |
337 338 339 340 341 342 343 |
static struct atmel_tcb_config tcb_rm9200_config = { .counter_width = 16, }; static struct atmel_tcb_config tcb_sam9x5_config = { .counter_width = 32, }; |
467ae18aa clocksource/drive... |
344 345 346 347 |
static struct atmel_tcb_config tcb_sama5d2_config = { .counter_width = 32, .has_gclk = 1, }; |
86232bfd2 clocksource/drive... |
348 |
static const struct of_device_id atmel_tcb_of_match[] = { |
d2c60dcf8 clocksource/drive... |
349 350 |
{ .compatible = "atmel,at91rm9200-tcb", .data = &tcb_rm9200_config, }, { .compatible = "atmel,at91sam9x5-tcb", .data = &tcb_sam9x5_config, }, |
467ae18aa clocksource/drive... |
351 |
{ .compatible = "atmel,sama5d2-tcb", .data = &tcb_sama5d2_config, }, |
86232bfd2 clocksource/drive... |
352 353 |
{ /* sentinel */ } }; |
4d243f92e atmel_tc clocksou... |
354 |
|
86232bfd2 clocksource/drive... |
355 356 357 |
static int __init tcb_clksrc_init(struct device_node *node) { struct atmel_tc tc; |
3ee08aea7 tclib: Fix compil... |
358 |
struct clk *t0_clk; |
86232bfd2 clocksource/drive... |
359 |
const struct of_device_id *match; |
f712a1e8e clocksource/drive... |
360 |
u64 (*tc_sched_clock)(void); |
4d243f92e atmel_tc clocksou... |
361 362 |
u32 rate, divided_rate = 0; int best_divisor_idx = -1; |
86232bfd2 clocksource/drive... |
363 |
int bits; |
4d243f92e atmel_tc clocksou... |
364 |
int i; |
0e746ec55 clocksource: tcb_... |
365 |
int ret; |
4d243f92e atmel_tc clocksou... |
366 |
|
86232bfd2 clocksource/drive... |
367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 |
/* Protect against multiple calls */ if (tcaddr) return 0; tc.regs = of_iomap(node->parent, 0); if (!tc.regs) return -ENXIO; t0_clk = of_clk_get_by_name(node->parent, "t0_clk"); if (IS_ERR(t0_clk)) return PTR_ERR(t0_clk); tc.slow_clk = of_clk_get_by_name(node->parent, "slow_clk"); if (IS_ERR(tc.slow_clk)) return PTR_ERR(tc.slow_clk); tc.clk[0] = t0_clk; tc.clk[1] = of_clk_get_by_name(node->parent, "t1_clk"); if (IS_ERR(tc.clk[1])) tc.clk[1] = t0_clk; tc.clk[2] = of_clk_get_by_name(node->parent, "t2_clk"); if (IS_ERR(tc.clk[2])) tc.clk[2] = t0_clk; tc.irq[2] = of_irq_get(node->parent, 2); if (tc.irq[2] <= 0) { tc.irq[2] = of_irq_get(node->parent, 0); if (tc.irq[2] <= 0) return -EINVAL; |
4d243f92e atmel_tc clocksou... |
396 |
} |
4d243f92e atmel_tc clocksou... |
397 |
|
86232bfd2 clocksource/drive... |
398 |
match = of_match_node(atmel_tcb_of_match, node->parent); |
d2c60dcf8 clocksource/drive... |
399 400 401 402 403 |
if (!match) return -ENODEV; tc.tcb_config = match->data; bits = tc.tcb_config->counter_width; |
86232bfd2 clocksource/drive... |
404 405 406 |
for (i = 0; i < ARRAY_SIZE(tc.irq); i++) writel(ATMEL_TC_ALL_IRQ, tc.regs + ATMEL_TC_REG(i, IDR)); |
0e746ec55 clocksource: tcb_... |
407 408 409 410 |
ret = clk_prepare_enable(t0_clk); if (ret) { pr_debug("can't enable T0 clk "); |
86232bfd2 clocksource/drive... |
411 |
return ret; |
0e746ec55 clocksource: tcb_... |
412 |
} |
4d243f92e atmel_tc clocksou... |
413 414 415 |
/* How fast will we be counting? Pick something over 5 MHz. */ rate = (u32) clk_get_rate(t0_clk); |
467ae18aa clocksource/drive... |
416 417 418 419 |
i = 0; if (tc.tcb_config->has_gclk) i = 1; for (; i < ARRAY_SIZE(atmel_tcb_divisors); i++) { |
86232bfd2 clocksource/drive... |
420 |
unsigned divisor = atmel_tcb_divisors[i]; |
4d243f92e atmel_tc clocksou... |
421 |
unsigned tmp; |
4d243f92e atmel_tc clocksou... |
422 423 424 |
tmp = rate / divisor; pr_debug("TC: %u / %-3u [%d] --> %u ", rate, divisor, i, tmp); |
501465d5d clocksource/drive... |
425 426 |
if ((best_divisor_idx >= 0) && (tmp < 5 * 1000 * 1000)) break; |
4d243f92e atmel_tc clocksou... |
427 428 429 |
divided_rate = tmp; best_divisor_idx = i; } |
86232bfd2 clocksource/drive... |
430 431 432 433 |
clksrc.name = kbasename(node->parent->full_name); clkevt.clkevt.name = kbasename(node->parent->full_name); pr_debug("%s at %d.%03d MHz ", clksrc.name, divided_rate / 1000000, |
542f82460 clocksource/drive... |
434 |
((divided_rate % 1000000) + 500) / 1000); |
4d243f92e atmel_tc clocksou... |
435 |
|
86232bfd2 clocksource/drive... |
436 437 438 |
tcaddr = tc.regs; if (bits == 32) { |
8e315a7b0 ARM: at91/tc/cloc... |
439 440 441 |
/* use apropriate function to read 32 bit counter */ clksrc.read = tc_get_cycles32; /* setup ony channel 0 */ |
86232bfd2 clocksource/drive... |
442 |
tcb_setup_single_chan(&tc, best_divisor_idx); |
f712a1e8e clocksource/drive... |
443 |
tc_sched_clock = tc_sched_clock_read32; |
1ce861cec clocksource/drive... |
444 |
tc_delay_timer.read_current_timer = tc_delay_timer_read32; |
8e315a7b0 ARM: at91/tc/cloc... |
445 |
} else { |
86232bfd2 clocksource/drive... |
446 |
/* we have three clocks no matter what the |
8e315a7b0 ARM: at91/tc/cloc... |
447 448 |
* underlying platform supports. */ |
86232bfd2 clocksource/drive... |
449 |
ret = clk_prepare_enable(tc.clk[1]); |
0e746ec55 clocksource: tcb_... |
450 451 452 453 454 |
if (ret) { pr_debug("can't enable T1 clk "); goto err_disable_t0; } |
8e315a7b0 ARM: at91/tc/cloc... |
455 |
/* setup both channel 0 & 1 */ |
86232bfd2 clocksource/drive... |
456 |
tcb_setup_dual_chan(&tc, best_divisor_idx); |
f712a1e8e clocksource/drive... |
457 |
tc_sched_clock = tc_sched_clock_read; |
1ce861cec clocksource/drive... |
458 |
tc_delay_timer.read_current_timer = tc_delay_timer_read; |
8e315a7b0 ARM: at91/tc/cloc... |
459 |
} |
4d243f92e atmel_tc clocksou... |
460 461 |
/* and away we go! */ |
5b3c11da1 clocksource: tcb_... |
462 463 464 |
ret = clocksource_register_hz(&clksrc, divided_rate); if (ret) goto err_disable_t1; |
4d243f92e atmel_tc clocksou... |
465 466 |
/* channel 2: periodic and oneshot timer support */ |
ef1d6a20e clocksource/drive... |
467 |
ret = setup_clkevents(&tc, best_divisor_idx); |
5b3c11da1 clocksource: tcb_... |
468 469 |
if (ret) goto err_unregister_clksrc; |
4d243f92e atmel_tc clocksou... |
470 |
|
f712a1e8e clocksource/drive... |
471 |
sched_clock_register(tc_sched_clock, 32, divided_rate); |
1ce861cec clocksource/drive... |
472 473 |
tc_delay_timer.freq = divided_rate; register_current_timer_delay(&tc_delay_timer); |
4d243f92e atmel_tc clocksou... |
474 |
return 0; |
0e746ec55 clocksource: tcb_... |
475 |
|
5b3c11da1 clocksource: tcb_... |
476 477 478 479 |
err_unregister_clksrc: clocksource_unregister(&clksrc); err_disable_t1: |
86232bfd2 clocksource/drive... |
480 481 |
if (bits != 32) clk_disable_unprepare(tc.clk[1]); |
5b3c11da1 clocksource: tcb_... |
482 |
|
0e746ec55 clocksource: tcb_... |
483 484 |
err_disable_t0: clk_disable_unprepare(t0_clk); |
86232bfd2 clocksource/drive... |
485 |
tcaddr = NULL; |
0e746ec55 clocksource: tcb_... |
486 |
return ret; |
4d243f92e atmel_tc clocksou... |
487 |
} |
86232bfd2 clocksource/drive... |
488 |
TIMER_OF_DECLARE(atmel_tcb_clksrc, "atmel,tcb-timer", tcb_clksrc_init); |