aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/clocksource/tcb_clksrc.c
diff options
context:
space:
mode:
authorDavid Brownell <david-b@pacbell.net>2008-02-22 20:28:37 -0500
committerHaavard Skinnemoen <hskinnemoen@atmel.com>2008-03-04 07:42:24 -0500
commit4d243f92e48a7913938f48fa9ebea5239168bb11 (patch)
tree8068f70eda76ff21839f9672771d3bd0ab9deb58 /drivers/clocksource/tcb_clksrc.c
parent2a341f5cf57dce9d89b41484a69e88adc6422f6c (diff)
atmel_tc clocksource/clockevent code
Clocksource and clockevent device based on the Atmel TC blocks. The clockevent device handles both periodic and oneshot modes, so this enables NO_HZ and high res timers on some platforms that previously couldn't use those mechanisms. This works on both AVR32 and AT91 chips, given relevant patches for tclib support (always) and clockevents (or else this will only look like a higher precision clocksource). It's an updated and modularized version of an AT91-only patch that has circulated for some time now. Changes relative to the original patch: * Update to use new tclib API * Replace open-coded do-while loop using goto with a real do-while loop * Minor irq handler optimization: Load register base address from dev_id instead of a global variable. * Aggressively turn off clocks when the clockevent isn't being used * Include the clockevent code on AT91RM9200 as well. The rating is lower than the System Timer, so the clock will usually stay off. * Don't assume that the number of clocks is always equal to the number of irqs. Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> Signed-off-by: Haavard Skinnemoen <hskinnemoen@atmel.com>
Diffstat (limited to 'drivers/clocksource/tcb_clksrc.c')
-rw-r--r--drivers/clocksource/tcb_clksrc.c305
1 files changed, 305 insertions, 0 deletions
diff --git a/drivers/clocksource/tcb_clksrc.c b/drivers/clocksource/tcb_clksrc.c
new file mode 100644
index 000000000000..17facda52605
--- /dev/null
+++ b/drivers/clocksource/tcb_clksrc.c
@@ -0,0 +1,305 @@
1#include <linux/init.h>
2#include <linux/clocksource.h>
3#include <linux/clockchips.h>
4#include <linux/interrupt.h>
5#include <linux/irq.h>
6
7#include <linux/clk.h>
8#include <linux/err.h>
9#include <linux/ioport.h>
10#include <linux/io.h>
11#include <linux/platform_device.h>
12#include <linux/atmel_tc.h>
13
14
15/*
16 * We're configured to use a specific TC block, one that's not hooked
17 * up to external hardware, to provide a time solution:
18 *
19 * - Two channels combine to create a free-running 32 bit counter
20 * with a base rate of 5+ MHz, packaged as a clocksource (with
21 * resolution better than 200 nsec).
22 *
23 * - The third channel may be used to provide a 16-bit clockevent
24 * source, used in either periodic or oneshot mode. This runs
25 * at 32 KiHZ, and can handle delays of up to two seconds.
26 *
27 * A boot clocksource and clockevent source are also currently needed,
28 * unless the relevant platforms (ARM/AT91, AVR32/AT32) are changed so
29 * this code can be used when init_timers() is called, well before most
30 * devices are set up. (Some low end AT91 parts, which can run uClinux,
31 * have only the timers in one TC block... they currently don't support
32 * the tclib code, because of that initialization issue.)
33 *
34 * REVISIT behavior during system suspend states... we should disable
35 * all clocks and save the power. Easily done for clockevent devices,
36 * but clocksources won't necessarily get the needed notifications.
37 * For deeper system sleep states, this will be mandatory...
38 */
39
40static void __iomem *tcaddr;
41
42static cycle_t tc_get_cycles(void)
43{
44 unsigned long flags;
45 u32 lower, upper;
46
47 raw_local_irq_save(flags);
48 do {
49 upper = __raw_readl(tcaddr + ATMEL_TC_REG(1, CV));
50 lower = __raw_readl(tcaddr + ATMEL_TC_REG(0, CV));
51 } while (upper != __raw_readl(tcaddr + ATMEL_TC_REG(1, CV)));
52
53 raw_local_irq_restore(flags);
54 return (upper << 16) | lower;
55}
56
57static struct clocksource clksrc = {
58 .name = "tcb_clksrc",
59 .rating = 200,
60 .read = tc_get_cycles,
61 .mask = CLOCKSOURCE_MASK(32),
62 .shift = 18,
63 .flags = CLOCK_SOURCE_IS_CONTINUOUS,
64};
65
66#ifdef CONFIG_GENERIC_CLOCKEVENTS
67
68struct tc_clkevt_device {
69 struct clock_event_device clkevt;
70 struct clk *clk;
71 void __iomem *regs;
72};
73
74static struct tc_clkevt_device *to_tc_clkevt(struct clock_event_device *clkevt)
75{
76 return container_of(clkevt, struct tc_clkevt_device, clkevt);
77}
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;
87
88static void tc_mode(enum clock_event_mode m, struct clock_event_device *d)
89{
90 struct tc_clkevt_device *tcd = to_tc_clkevt(d);
91 void __iomem *regs = tcd->regs;
92
93 if (tcd->clkevt.mode == CLOCK_EVT_MODE_PERIODIC
94 || tcd->clkevt.mode == CLOCK_EVT_MODE_ONESHOT) {
95 __raw_writel(0xff, regs + ATMEL_TC_REG(2, IDR));
96 __raw_writel(ATMEL_TC_CLKDIS, regs + ATMEL_TC_REG(2, CCR));
97 clk_disable(tcd->clk);
98 }
99
100 switch (m) {
101
102 /* By not making the gentime core emulate periodic mode on top
103 * of oneshot, we get lower overhead and improved accuracy.
104 */
105 case CLOCK_EVT_MODE_PERIODIC:
106 clk_enable(tcd->clk);
107
108 /* slow clock, count up to RC, then irq and restart */
109 __raw_writel(timer_clock
110 | ATMEL_TC_WAVE | ATMEL_TC_WAVESEL_UP_AUTO,
111 regs + ATMEL_TC_REG(2, CMR));
112 __raw_writel((32768 + HZ/2) / HZ, tcaddr + ATMEL_TC_REG(2, RC));
113
114 /* Enable clock and interrupts on RC compare */
115 __raw_writel(ATMEL_TC_CPCS, regs + ATMEL_TC_REG(2, IER));
116
117 /* go go gadget! */
118 __raw_writel(ATMEL_TC_CLKEN | ATMEL_TC_SWTRG,
119 regs + ATMEL_TC_REG(2, CCR));
120 break;
121
122 case CLOCK_EVT_MODE_ONESHOT:
123 clk_enable(tcd->clk);
124
125 /* slow clock, count up to RC, then irq and stop */
126 __raw_writel(timer_clock | ATMEL_TC_CPCSTOP
127 | ATMEL_TC_WAVE | ATMEL_TC_WAVESEL_UP_AUTO,
128 regs + ATMEL_TC_REG(2, CMR));
129 __raw_writel(ATMEL_TC_CPCS, regs + ATMEL_TC_REG(2, IER));
130
131 /* set_next_event() configures and starts the timer */
132 break;
133
134 default:
135 break;
136 }
137}
138
139static int tc_next_event(unsigned long delta, struct clock_event_device *d)
140{
141 __raw_writel(delta, tcaddr + ATMEL_TC_REG(2, RC));
142
143 /* go go gadget! */
144 __raw_writel(ATMEL_TC_CLKEN | ATMEL_TC_SWTRG,
145 tcaddr + ATMEL_TC_REG(2, CCR));
146 return 0;
147}
148
149static struct tc_clkevt_device clkevt = {
150 .clkevt = {
151 .name = "tc_clkevt",
152 .features = CLOCK_EVT_FEAT_PERIODIC
153 | CLOCK_EVT_FEAT_ONESHOT,
154 .shift = 32,
155 /* Should be lower than at91rm9200's system timer */
156 .rating = 125,
157 .cpumask = CPU_MASK_CPU0,
158 .set_next_event = tc_next_event,
159 .set_mode = tc_mode,
160 },
161};
162
163static irqreturn_t ch2_irq(int irq, void *handle)
164{
165 struct tc_clkevt_device *dev = handle;
166 unsigned int sr;
167
168 sr = __raw_readl(dev->regs + ATMEL_TC_REG(2, SR));
169 if (sr & ATMEL_TC_CPCS) {
170 dev->clkevt.event_handler(&dev->clkevt);
171 return IRQ_HANDLED;
172 }
173
174 return IRQ_NONE;
175}
176
177static struct irqaction tc_irqaction = {
178 .name = "tc_clkevt",
179 .flags = IRQF_TIMER | IRQF_DISABLED,
180 .handler = ch2_irq,
181};
182
183static void __init setup_clkevents(struct atmel_tc *tc,
184 struct clk *t0_clk, int clk32k_divisor_idx)
185{
186 struct platform_device *pdev = tc->pdev;
187 struct clk *t2_clk = tc->clk[2];
188 int irq = tc->irq[2];
189
190 clkevt.regs = tc->regs;
191 clkevt.clk = t2_clk;
192 tc_irqaction.dev_id = &clkevt;
193
194 timer_clock = clk32k_divisor_idx;
195
196 clkevt.clkevt.mult = div_sc(32768, NSEC_PER_SEC, clkevt.clkevt.shift);
197 clkevt.clkevt.max_delta_ns
198 = clockevent_delta2ns(0xffff, &clkevt.clkevt);
199 clkevt.clkevt.min_delta_ns = clockevent_delta2ns(1, &clkevt.clkevt) + 1;
200
201 setup_irq(irq, &tc_irqaction);
202
203 clockevents_register_device(&clkevt.clkevt);
204}
205
206#else /* !CONFIG_GENERIC_CLOCKEVENTS */
207
208static void __init setup_clkevents(struct atmel_tc *tc,
209 struct clk *t0_clk, int clk32k_divisor_idx)
210{
211 /* NOTHING */
212}
213
214#endif
215
216static int __init tcb_clksrc_init(void)
217{
218 static char bootinfo[] __initdata
219 = KERN_DEBUG "%s: tc%d at %d.%03d MHz\n";
220
221 struct platform_device *pdev;
222 struct atmel_tc *tc;
223 struct clk *t0_clk, *t1_clk;
224 u32 rate, divided_rate = 0;
225 int best_divisor_idx = -1;
226 int clk32k_divisor_idx = -1;
227 int i;
228
229 tc = atmel_tc_alloc(CONFIG_ATMEL_TCB_CLKSRC_BLOCK, clksrc.name);
230 if (!tc) {
231 pr_debug("can't alloc TC for clocksource\n");
232 return -ENODEV;
233 }
234 tcaddr = tc->regs;
235 pdev = tc->pdev;
236
237 t0_clk = tc->clk[0];
238 clk_enable(t0_clk);
239
240 /* How fast will we be counting? Pick something over 5 MHz. */
241 rate = (u32) clk_get_rate(t0_clk);
242 for (i = 0; i < 5; i++) {
243 unsigned divisor = atmel_tc_divisors[i];
244 unsigned tmp;
245
246 /* remember 32 KiHz clock for later */
247 if (!divisor) {
248 clk32k_divisor_idx = i;
249 continue;
250 }
251
252 tmp = rate / divisor;
253 pr_debug("TC: %u / %-3u [%d] --> %u\n", rate, divisor, i, tmp);
254 if (best_divisor_idx > 0) {
255 if (tmp < 5 * 1000 * 1000)
256 continue;
257 }
258 divided_rate = tmp;
259 best_divisor_idx = i;
260 }
261
262 clksrc.mult = clocksource_hz2mult(divided_rate, clksrc.shift);
263
264 printk(bootinfo, clksrc.name, CONFIG_ATMEL_TCB_CLKSRC_BLOCK,
265 divided_rate / 1000000,
266 ((divided_rate + 500000) % 1000000) / 1000);
267
268 /* tclib will give us three clocks no matter what the
269 * underlying platform supports.
270 */
271 clk_enable(tc->clk[1]);
272
273 /* channel 0: waveform mode, input mclk/8, clock TIOA0 on overflow */
274 __raw_writel(best_divisor_idx /* likely divide-by-8 */
275 | ATMEL_TC_WAVE
276 | ATMEL_TC_WAVESEL_UP /* free-run */
277 | ATMEL_TC_ACPA_SET /* TIOA0 rises at 0 */
278 | ATMEL_TC_ACPC_CLEAR, /* (duty cycle 50%) */
279 tcaddr + ATMEL_TC_REG(0, CMR));
280 __raw_writel(0x0000, tcaddr + ATMEL_TC_REG(0, RA));
281 __raw_writel(0x8000, tcaddr + ATMEL_TC_REG(0, RC));
282 __raw_writel(0xff, tcaddr + ATMEL_TC_REG(0, IDR)); /* no irqs */
283 __raw_writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(0, CCR));
284
285 /* channel 1: waveform mode, input TIOA0 */
286 __raw_writel(ATMEL_TC_XC1 /* input: TIOA0 */
287 | ATMEL_TC_WAVE
288 | ATMEL_TC_WAVESEL_UP, /* free-run */
289 tcaddr + ATMEL_TC_REG(1, CMR));
290 __raw_writel(0xff, tcaddr + ATMEL_TC_REG(1, IDR)); /* no irqs */
291 __raw_writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(1, CCR));
292
293 /* chain channel 0 to channel 1, then reset all the timers */
294 __raw_writel(ATMEL_TC_TC1XC1S_TIOA0, tcaddr + ATMEL_TC_BMR);
295 __raw_writel(ATMEL_TC_SYNC, tcaddr + ATMEL_TC_BCR);
296
297 /* and away we go! */
298 clocksource_register(&clksrc);
299
300 /* channel 2: periodic and oneshot timer support */
301 setup_clkevents(tc, t0_clk, clk32k_divisor_idx);
302
303 return 0;
304}
305arch_initcall(tcb_clksrc_init);