diff options
author | David Brownell <david-b@pacbell.net> | 2007-07-30 20:41:26 -0400 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2007-10-12 18:43:15 -0400 |
commit | 5e802dfab7bbbee1e63607a3e6d1ceb78ec4ceeb (patch) | |
tree | eb85a0c5746afae2c6ca0959bb9c58caf83351eb /arch/arm/mach-at91/at91rm9200_time.c | |
parent | f2c10d6c669e5b792c48e86da37ec7fde0a2e302 (diff) |
[ARM] 4539/1: clocksource and clockevents for at91rm9200
GENERIC_TIME and GENERIC_CLOCKEVENTS support for the at91rm9200.
- Oneshot mode (used for NO_HZ and high res timers) uses the
alarm to emulate a real oneshot timer; the trickiest bit is
how to avoid some lowlevel races. Thanks to Remy Bohmer for
various fixes to this code.
- Tighten up periodic mode support using the PIT.
- Streamline reads of the 32KHz counter. Thanks to Marc Pignat
for some testing results: the CRTR register has *very* odd
behavior. The reread appears to work around stranger glitches
than just getting an old clock value (which would quickly
self-correct).
- Remove the rounding-up of tick_usec to 10.009 msec (32KiHz/100),
since that no longer acts correct (time increases too fast).
Note that the at91sam9 and at91x40 chips need other solutions,
since they don't have the same system timer module.
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Acked-by: Bill Gatliff <bgat@billgatliff.com>
Acked-by:Remy Bohmer <linux@bohmer.net>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch/arm/mach-at91/at91rm9200_time.c')
-rw-r--r-- | arch/arm/mach-at91/at91rm9200_time.c | 195 |
1 files changed, 130 insertions, 65 deletions
diff --git a/arch/arm/mach-at91/at91rm9200_time.c b/arch/arm/mach-at91/at91rm9200_time.c index a6340357585d..50392ff71513 100644 --- a/arch/arm/mach-at91/at91rm9200_time.c +++ b/arch/arm/mach-at91/at91rm9200_time.c | |||
@@ -19,70 +19,64 @@ | |||
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
20 | */ | 20 | */ |
21 | 21 | ||
22 | #include <linux/init.h> | 22 | #include <linux/kernel.h> |
23 | #include <linux/interrupt.h> | 23 | #include <linux/interrupt.h> |
24 | #include <linux/irq.h> | 24 | #include <linux/irq.h> |
25 | #include <linux/kernel.h> | 25 | #include <linux/clockchips.h> |
26 | #include <linux/sched.h> | ||
27 | #include <linux/time.h> | ||
28 | 26 | ||
29 | #include <asm/hardware.h> | ||
30 | #include <asm/io.h> | ||
31 | #include <asm/mach/time.h> | 27 | #include <asm/mach/time.h> |
32 | 28 | ||
33 | #include <asm/arch/at91_st.h> | 29 | #include <asm/arch/at91_st.h> |
34 | 30 | ||
35 | static unsigned long last_crtr; | 31 | static unsigned long last_crtr; |
32 | static u32 irqmask; | ||
33 | static struct clock_event_device clkevt; | ||
36 | 34 | ||
37 | /* | 35 | /* |
38 | * The ST_CRTR is updated asynchronously to the master clock. It is therefore | 36 | * The ST_CRTR is updated asynchronously to the master clock ... but |
39 | * necessary to read it twice (with the same value) to ensure accuracy. | 37 | * the updates as seen by the CPU don't seem to be strictly monotonic. |
38 | * Waiting until we read the same value twice avoids glitching. | ||
40 | */ | 39 | */ |
41 | static inline unsigned long read_CRTR(void) { | 40 | static inline unsigned long read_CRTR(void) |
41 | { | ||
42 | unsigned long x1, x2; | 42 | unsigned long x1, x2; |
43 | 43 | ||
44 | x1 = at91_sys_read(AT91_ST_CRTR); | ||
44 | do { | 45 | do { |
45 | x1 = at91_sys_read(AT91_ST_CRTR); | ||
46 | x2 = at91_sys_read(AT91_ST_CRTR); | 46 | x2 = at91_sys_read(AT91_ST_CRTR); |
47 | } while (x1 != x2); | 47 | if (x1 == x2) |
48 | 48 | break; | |
49 | x1 = x2; | ||
50 | } while (1); | ||
49 | return x1; | 51 | return x1; |
50 | } | 52 | } |
51 | 53 | ||
52 | /* | 54 | /* |
53 | * Returns number of microseconds since last timer interrupt. Note that interrupts | ||
54 | * will have been disabled by do_gettimeofday() | ||
55 | * 'LATCH' is hwclock ticks (see CLOCK_TICK_RATE in timex.h) per jiffy. | ||
56 | * 'tick' is usecs per jiffy (linux/timex.h). | ||
57 | */ | ||
58 | static unsigned long at91rm9200_gettimeoffset(void) | ||
59 | { | ||
60 | unsigned long elapsed; | ||
61 | |||
62 | elapsed = (read_CRTR() - last_crtr) & AT91_ST_ALMV; | ||
63 | |||
64 | return (unsigned long)(elapsed * (tick_nsec / 1000)) / LATCH; | ||
65 | } | ||
66 | |||
67 | /* | ||
68 | * IRQ handler for the timer. | 55 | * IRQ handler for the timer. |
69 | */ | 56 | */ |
70 | static irqreturn_t at91rm9200_timer_interrupt(int irq, void *dev_id) | 57 | static irqreturn_t at91rm9200_timer_interrupt(int irq, void *dev_id) |
71 | { | 58 | { |
72 | if (at91_sys_read(AT91_ST_SR) & AT91_ST_PITS) { /* This is a shared interrupt */ | 59 | u32 sr = at91_sys_read(AT91_ST_SR) & irqmask; |
73 | write_seqlock(&xtime_lock); | ||
74 | 60 | ||
75 | while (((read_CRTR() - last_crtr) & AT91_ST_ALMV) >= LATCH) { | 61 | /* simulate "oneshot" timer with alarm */ |
76 | timer_tick(); | 62 | if (sr & AT91_ST_ALMS) { |
77 | last_crtr = (last_crtr + LATCH) & AT91_ST_ALMV; | 63 | clkevt.event_handler(&clkevt); |
78 | } | 64 | return IRQ_HANDLED; |
65 | } | ||
79 | 66 | ||
80 | write_sequnlock(&xtime_lock); | 67 | /* periodic mode should handle delayed ticks */ |
68 | if (sr & AT91_ST_PITS) { | ||
69 | u32 crtr = read_CRTR(); | ||
81 | 70 | ||
71 | while (((crtr - last_crtr) & AT91_ST_CRTV) >= LATCH) { | ||
72 | last_crtr += LATCH; | ||
73 | clkevt.event_handler(&clkevt); | ||
74 | } | ||
82 | return IRQ_HANDLED; | 75 | return IRQ_HANDLED; |
83 | } | 76 | } |
84 | else | 77 | |
85 | return IRQ_NONE; /* not handled */ | 78 | /* this irq is shared ... */ |
79 | return IRQ_NONE; | ||
86 | } | 80 | } |
87 | 81 | ||
88 | static struct irqaction at91rm9200_timer_irq = { | 82 | static struct irqaction at91rm9200_timer_irq = { |
@@ -91,56 +85,127 @@ static struct irqaction at91rm9200_timer_irq = { | |||
91 | .handler = at91rm9200_timer_interrupt | 85 | .handler = at91rm9200_timer_interrupt |
92 | }; | 86 | }; |
93 | 87 | ||
94 | void at91rm9200_timer_reset(void) | 88 | static cycle_t read_clk32k(void) |
95 | { | 89 | { |
96 | last_crtr = 0; | 90 | return read_CRTR(); |
91 | } | ||
97 | 92 | ||
98 | /* Real time counter incremented every 30.51758 microseconds */ | 93 | static struct clocksource clk32k = { |
99 | at91_sys_write(AT91_ST_RTMR, 1); | 94 | .name = "32k_counter", |
95 | .rating = 150, | ||
96 | .read = read_clk32k, | ||
97 | .mask = CLOCKSOURCE_MASK(20), | ||
98 | .shift = 10, | ||
99 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | ||
100 | }; | ||
101 | |||
102 | static void | ||
103 | clkevt32k_mode(enum clock_event_mode mode, struct clock_event_device *dev) | ||
104 | { | ||
105 | /* Disable and flush pending timer interrupts */ | ||
106 | at91_sys_write(AT91_ST_IDR, AT91_ST_PITS | AT91_ST_ALMS); | ||
107 | (void) at91_sys_read(AT91_ST_SR); | ||
100 | 108 | ||
101 | /* Set Period Interval timer */ | 109 | last_crtr = read_CRTR(); |
102 | at91_sys_write(AT91_ST_PIMR, LATCH); | 110 | switch (mode) { |
111 | case CLOCK_EVT_MODE_PERIODIC: | ||
112 | /* PIT for periodic irqs; fixed rate of 1/HZ */ | ||
113 | irqmask = AT91_ST_PITS; | ||
114 | at91_sys_write(AT91_ST_PIMR, LATCH); | ||
115 | break; | ||
116 | case CLOCK_EVT_MODE_ONESHOT: | ||
117 | /* ALM for oneshot irqs, set by next_event() | ||
118 | * before 32 seconds have passed | ||
119 | */ | ||
120 | irqmask = AT91_ST_ALMS; | ||
121 | at91_sys_write(AT91_ST_RTAR, last_crtr); | ||
122 | break; | ||
123 | case CLOCK_EVT_MODE_SHUTDOWN: | ||
124 | case CLOCK_EVT_MODE_UNUSED: | ||
125 | case CLOCK_EVT_MODE_RESUME: | ||
126 | irqmask = 0; | ||
127 | break; | ||
128 | } | ||
129 | at91_sys_write(AT91_ST_IER, irqmask); | ||
130 | } | ||
103 | 131 | ||
104 | /* Clear any pending interrupts */ | 132 | static int |
133 | clkevt32k_next_event(unsigned long delta, struct clock_event_device *dev) | ||
134 | { | ||
135 | unsigned long flags; | ||
136 | u32 alm; | ||
137 | int status = 0; | ||
138 | |||
139 | BUG_ON(delta < 2); | ||
140 | |||
141 | /* Use "raw" primitives so we behave correctly on RT kernels. */ | ||
142 | raw_local_irq_save(flags); | ||
143 | |||
144 | /* The alarm IRQ uses absolute time (now+delta), not the relative | ||
145 | * time (delta) in our calling convention. Like all clockevents | ||
146 | * using such "match" hardware, we have a race to defend against. | ||
147 | * | ||
148 | * Our defense here is to have set up the clockevent device so the | ||
149 | * delta is at least two. That way we never end up writing RTAR | ||
150 | * with the value then held in CRTR ... which would mean the match | ||
151 | * wouldn't trigger until 32 seconds later, after CRTR wraps. | ||
152 | */ | ||
153 | alm = read_CRTR(); | ||
154 | |||
155 | /* Cancel any pending alarm; flush any pending IRQ */ | ||
156 | at91_sys_write(AT91_ST_RTAR, alm); | ||
105 | (void) at91_sys_read(AT91_ST_SR); | 157 | (void) at91_sys_read(AT91_ST_SR); |
106 | 158 | ||
107 | /* Enable Period Interval Timer interrupt */ | 159 | /* Schedule alarm by writing RTAR. */ |
108 | at91_sys_write(AT91_ST_IER, AT91_ST_PITS); | 160 | alm += delta; |
161 | at91_sys_write(AT91_ST_RTAR, alm); | ||
162 | |||
163 | raw_local_irq_restore(flags); | ||
164 | return status; | ||
109 | } | 165 | } |
110 | 166 | ||
167 | static struct clock_event_device clkevt = { | ||
168 | .name = "at91_tick", | ||
169 | .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, | ||
170 | .shift = 32, | ||
171 | .rating = 150, | ||
172 | .cpumask = CPU_MASK_CPU0, | ||
173 | .set_next_event = clkevt32k_next_event, | ||
174 | .set_mode = clkevt32k_mode, | ||
175 | }; | ||
176 | |||
111 | /* | 177 | /* |
112 | * Set up timer interrupt. | 178 | * ST (system timer) module supports both clockevents and clocksource. |
113 | */ | 179 | */ |
114 | void __init at91rm9200_timer_init(void) | 180 | void __init at91rm9200_timer_init(void) |
115 | { | 181 | { |
116 | /* Disable all timer interrupts */ | 182 | /* Disable all timer interrupts, and clear any pending ones */ |
117 | at91_sys_write(AT91_ST_IDR, AT91_ST_PITS | AT91_ST_WDOVF | AT91_ST_RTTINC | AT91_ST_ALMS); | 183 | at91_sys_write(AT91_ST_IDR, |
118 | (void) at91_sys_read(AT91_ST_SR); /* Clear any pending interrupts */ | 184 | AT91_ST_PITS | AT91_ST_WDOVF | AT91_ST_RTTINC | AT91_ST_ALMS); |
185 | (void) at91_sys_read(AT91_ST_SR); | ||
119 | 186 | ||
120 | /* Make IRQs happen for the system timer */ | 187 | /* Make IRQs happen for the system timer */ |
121 | setup_irq(AT91_ID_SYS, &at91rm9200_timer_irq); | 188 | setup_irq(AT91_ID_SYS, &at91rm9200_timer_irq); |
122 | 189 | ||
123 | /* Change the kernel's 'tick' value to 10009 usec. (the default is 10000) */ | 190 | /* The 32KiHz "Slow Clock" (tick every 30517.58 nanoseconds) is used |
124 | tick_usec = (LATCH * 1000000) / CLOCK_TICK_RATE; | 191 | * directly for the clocksource and all clockevents, after adjusting |
192 | * its prescaler from the 1 Hz default. | ||
193 | */ | ||
194 | at91_sys_write(AT91_ST_RTMR, 1); | ||
125 | 195 | ||
126 | /* Initialize and enable the timer interrupt */ | 196 | /* Setup timer clockevent, with minimum of two ticks (important!!) */ |
127 | at91rm9200_timer_reset(); | 197 | clkevt.mult = div_sc(AT91_SLOW_CLOCK, NSEC_PER_SEC, clkevt.shift); |
128 | } | 198 | clkevt.max_delta_ns = clockevent_delta2ns(AT91_ST_ALMV, &clkevt); |
199 | clkevt.min_delta_ns = clockevent_delta2ns(2, &clkevt) + 1; | ||
200 | clkevt.cpumask = cpumask_of_cpu(0); | ||
201 | clockevents_register_device(&clkevt); | ||
129 | 202 | ||
130 | #ifdef CONFIG_PM | 203 | /* register clocksource */ |
131 | static void at91rm9200_timer_suspend(void) | 204 | clk32k.mult = clocksource_hz2mult(AT91_SLOW_CLOCK, clk32k.shift); |
132 | { | 205 | clocksource_register(&clk32k); |
133 | /* disable Period Interval Timer interrupt */ | ||
134 | at91_sys_write(AT91_ST_IDR, AT91_ST_PITS); | ||
135 | } | 206 | } |
136 | #else | ||
137 | #define at91rm9200_timer_suspend NULL | ||
138 | #endif | ||
139 | 207 | ||
140 | struct sys_timer at91rm9200_timer = { | 208 | struct sys_timer at91rm9200_timer = { |
141 | .init = at91rm9200_timer_init, | 209 | .init = at91rm9200_timer_init, |
142 | .offset = at91rm9200_gettimeoffset, | ||
143 | .suspend = at91rm9200_timer_suspend, | ||
144 | .resume = at91rm9200_timer_reset, | ||
145 | }; | 210 | }; |
146 | 211 | ||