aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-at91/at91rm9200_time.c
diff options
context:
space:
mode:
authorDavid Brownell <david-b@pacbell.net>2007-07-30 20:41:26 -0400
committerRussell King <rmk+kernel@arm.linux.org.uk>2007-10-12 18:43:15 -0400
commit5e802dfab7bbbee1e63607a3e6d1ceb78ec4ceeb (patch)
treeeb85a0c5746afae2c6ca0959bb9c58caf83351eb /arch/arm/mach-at91/at91rm9200_time.c
parentf2c10d6c669e5b792c48e86da37ec7fde0a2e302 (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.c195
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
35static unsigned long last_crtr; 31static unsigned long last_crtr;
32static u32 irqmask;
33static 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 */
41static inline unsigned long read_CRTR(void) { 40static 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 */
58static 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 */
70static irqreturn_t at91rm9200_timer_interrupt(int irq, void *dev_id) 57static 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
88static struct irqaction at91rm9200_timer_irq = { 82static 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
94void at91rm9200_timer_reset(void) 88static 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 */ 93static 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
102static void
103clkevt32k_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 */ 132static int
133clkevt32k_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
167static 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 */
114void __init at91rm9200_timer_init(void) 180void __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 */
131static 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
140struct sys_timer at91rm9200_timer = { 208struct 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