aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/clocksource
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
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')
-rw-r--r--drivers/clocksource/Makefile1
-rw-r--r--drivers/clocksource/tcb_clksrc.c305
2 files changed, 306 insertions, 0 deletions
<
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index a52225470225..1525882190fd 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -1,3 +1,4 @@
1obj-$(CONFIG_ATMEL_TCB_CLKSRC) += tcb_clksrc.o
1obj-$(CONFIG_X86_CYCLONE_TIMER) += cyclone.o 2obj-$(CONFIG_X86_CYCLONE_TIMER) += cyclone.o
2obj-$(CONFIG_X86_PM_TIMER) += acpi_pm.o 3obj-$(CONFIG_X86_PM_TIMER) += acpi_pm.o
3obj-$(CONFIG_SCx200HR_TIMER) += scx200_hrt.o 4obj-$(CONFIG_SCx200HR_TIMER) += scx200_hrt.o
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 = {