diff options
author | Russell King <rmk@dyn-67.arm.linux.org.uk> | 2008-04-14 18:03:10 -0400 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2008-04-14 18:03:10 -0400 |
commit | 3e238be2f75f2a6d5d5064eda46ac96268c6411e (patch) | |
tree | 829a241e737c25efc81f2f1bc47cb2ef037baa39 /arch/arm/mach-sa1100/time.c | |
parent | b43a9e60873ebe16502061961ca3f3b7bc6f69d2 (diff) |
[ARM] sa1100: add clock event support
d142b6e77d394a4fcc0a42381b03852bd9c4e263 added clock source support,
now it's time for the clock event support.
Tested-by: Thomas Kunze <thommycheck@gmx.de>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch/arm/mach-sa1100/time.c')
-rw-r--r-- | arch/arm/mach-sa1100/time.c | 159 |
1 files changed, 65 insertions, 94 deletions
diff --git a/arch/arm/mach-sa1100/time.c b/arch/arm/mach-sa1100/time.c index c2677368d6af..a9799cb35b74 100644 --- a/arch/arm/mach-sa1100/time.c +++ b/arch/arm/mach-sa1100/time.c | |||
@@ -13,67 +13,69 @@ | |||
13 | #include <linux/interrupt.h> | 13 | #include <linux/interrupt.h> |
14 | #include <linux/irq.h> | 14 | #include <linux/irq.h> |
15 | #include <linux/timex.h> | 15 | #include <linux/timex.h> |
16 | #include <linux/signal.h> | 16 | #include <linux/clockchips.h> |
17 | #include <linux/clocksource.h> | ||
18 | 17 | ||
19 | #include <asm/mach/time.h> | 18 | #include <asm/mach/time.h> |
20 | #include <asm/hardware.h> | 19 | #include <asm/hardware.h> |
21 | 20 | ||
22 | #define RTC_DEF_DIVIDER (32768 - 1) | 21 | #define MIN_OSCR_DELTA 2 |
23 | #define RTC_DEF_TRIM 0 | ||
24 | 22 | ||
25 | static int sa1100_set_rtc(void) | 23 | static irqreturn_t sa1100_ost0_interrupt(int irq, void *dev_id) |
26 | { | 24 | { |
27 | unsigned long current_time = xtime.tv_sec; | 25 | struct clock_event_device *c = dev_id; |
28 | 26 | ||
29 | if (RTSR & RTSR_ALE) { | 27 | /* Disarm the compare/match, signal the event. */ |
30 | /* make sure not to forward the clock over an alarm */ | 28 | OIER &= ~OIER_E0; |
31 | unsigned long alarm = RTAR; | 29 | OSSR = OSSR_M0; |
32 | if (current_time >= alarm && alarm >= RCNR) | 30 | c->event_handler(c); |
33 | return -ERESTARTSYS; | ||
34 | } | ||
35 | RCNR = current_time; | ||
36 | return 0; | ||
37 | } | ||
38 | 31 | ||
39 | #ifdef CONFIG_NO_IDLE_HZ | 32 | return IRQ_HANDLED; |
40 | static unsigned long initial_match; | 33 | } |
41 | static int match_posponed; | ||
42 | #endif | ||
43 | 34 | ||
44 | static irqreturn_t | 35 | static int |
45 | sa1100_timer_interrupt(int irq, void *dev_id) | 36 | sa1100_osmr0_set_next_event(unsigned long delta, struct clock_event_device *c) |
46 | { | 37 | { |
47 | unsigned int next_match; | 38 | unsigned long flags, next, oscr; |
48 | 39 | ||
49 | #ifdef CONFIG_NO_IDLE_HZ | 40 | raw_local_irq_save(flags); |
50 | if (match_posponed) { | 41 | OIER |= OIER_E0; |
51 | match_posponed = 0; | 42 | next = OSCR + delta; |
52 | OSMR0 = initial_match; | 43 | OSMR0 = next; |
53 | } | 44 | oscr = OSCR; |
54 | #endif | 45 | raw_local_irq_restore(flags); |
55 | 46 | ||
56 | /* | 47 | return (signed)(next - oscr) <= MIN_OSCR_DELTA ? -ETIME : 0; |
57 | * Loop until we get ahead of the free running timer. | 48 | } |
58 | * This ensures an exact clock tick count and time accuracy. | ||
59 | * Since IRQs are disabled at this point, coherence between | ||
60 | * lost_ticks(updated in do_timer()) and the match reg value is | ||
61 | * ensured, hence we can use do_gettimeofday() from interrupt | ||
62 | * handlers. | ||
63 | */ | ||
64 | do { | ||
65 | timer_tick(); | ||
66 | OSSR = OSSR_M0; /* Clear match on timer 0 */ | ||
67 | next_match = (OSMR0 += LATCH); | ||
68 | } while ((signed long)(next_match - OSCR) <= 0); | ||
69 | 49 | ||
70 | return IRQ_HANDLED; | 50 | static void |
51 | sa1100_osmr0_set_mode(enum clock_event_mode mode, struct clock_event_device *c) | ||
52 | { | ||
53 | unsigned long flags; | ||
54 | |||
55 | switch (mode) { | ||
56 | case CLOCK_EVT_MODE_ONESHOT: | ||
57 | case CLOCK_EVT_MODE_UNUSED: | ||
58 | case CLOCK_EVT_MODE_SHUTDOWN: | ||
59 | raw_local_irq_save(flags); | ||
60 | OIER &= ~OIER_E0; | ||
61 | OSSR = OSSR_M0; | ||
62 | raw_local_irq_restore(flags); | ||
63 | break; | ||
64 | |||
65 | case CLOCK_EVT_MODE_RESUME: | ||
66 | case CLOCK_EVT_MODE_PERIODIC: | ||
67 | break; | ||
68 | } | ||
71 | } | 69 | } |
72 | 70 | ||
73 | static struct irqaction sa1100_timer_irq = { | 71 | static struct clock_event_device ckevt_sa1100_osmr0 = { |
74 | .name = "SA11xx Timer Tick", | 72 | .name = "osmr0", |
75 | .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, | 73 | .features = CLOCK_EVT_FEAT_ONESHOT, |
76 | .handler = sa1100_timer_interrupt, | 74 | .shift = 32, |
75 | .rating = 200, | ||
76 | .cpumask = CPU_MASK_CPU0, | ||
77 | .set_next_event = sa1100_osmr0_set_next_event, | ||
78 | .set_mode = sa1100_osmr0_set_mode, | ||
77 | }; | 79 | }; |
78 | 80 | ||
79 | static cycle_t sa1100_read_oscr(void) | 81 | static cycle_t sa1100_read_oscr(void) |
@@ -90,62 +92,34 @@ static struct clocksource cksrc_sa1100_oscr = { | |||
90 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | 92 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, |
91 | }; | 93 | }; |
92 | 94 | ||
95 | static struct irqaction sa1100_timer_irq = { | ||
96 | .name = "ost0", | ||
97 | .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, | ||
98 | .handler = sa1100_ost0_interrupt, | ||
99 | .dev_id = &ckevt_sa1100_osmr0, | ||
100 | }; | ||
101 | |||
93 | static void __init sa1100_timer_init(void) | 102 | static void __init sa1100_timer_init(void) |
94 | { | 103 | { |
95 | unsigned long flags; | ||
96 | |||
97 | set_rtc = sa1100_set_rtc; | ||
98 | |||
99 | OIER = 0; /* disable any timer interrupts */ | 104 | OIER = 0; /* disable any timer interrupts */ |
100 | OSSR = 0xf; /* clear status on all timers */ | 105 | OSSR = 0xf; /* clear status on all timers */ |
101 | setup_irq(IRQ_OST0, &sa1100_timer_irq); | 106 | |
102 | local_irq_save(flags); | 107 | ckevt_sa1100_osmr0.mult = |
103 | OIER = OIER_E0; /* enable match on timer 0 to cause interrupts */ | 108 | div_sc(3686400, NSEC_PER_SEC, ckevt_sa1100_osmr0.shift); |
104 | OSMR0 = OSCR + LATCH; /* set initial match */ | 109 | ckevt_sa1100_osmr0.max_delta_ns = |
105 | local_irq_restore(flags); | 110 | clockevent_delta2ns(0x7fffffff, &ckevt_sa1100_osmr0); |
111 | ckevt_sa1100_osmr0.min_delta_ns = | ||
112 | clockevent_delta2ns(MIN_OSCR_DELTA * 2, &ckevt_sa1100_osmr0) + 1; | ||
106 | 113 | ||
107 | cksrc_sa1100_oscr.mult = | 114 | cksrc_sa1100_oscr.mult = |
108 | clocksource_hz2mult(CLOCK_TICK_RATE, cksrc_sa1100_oscr.shift); | 115 | clocksource_hz2mult(CLOCK_TICK_RATE, cksrc_sa1100_oscr.shift); |
109 | 116 | ||
110 | clocksource_register(&cksrc_sa1100_oscr); | 117 | setup_irq(IRQ_OST0, &sa1100_timer_irq); |
111 | } | ||
112 | |||
113 | #ifdef CONFIG_NO_IDLE_HZ | ||
114 | static int sa1100_dyn_tick_enable_disable(void) | ||
115 | { | ||
116 | /* nothing to do */ | ||
117 | return 0; | ||
118 | } | ||
119 | |||
120 | static void sa1100_dyn_tick_reprogram(unsigned long ticks) | ||
121 | { | ||
122 | if (ticks > 1) { | ||
123 | initial_match = OSMR0; | ||
124 | OSMR0 = initial_match + ticks * LATCH; | ||
125 | match_posponed = 1; | ||
126 | } | ||
127 | } | ||
128 | 118 | ||
129 | static irqreturn_t | 119 | clocksource_register(&cksrc_sa1100_oscr); |
130 | sa1100_dyn_tick_handler(int irq, void *dev_id) | 120 | clockevents_register_device(&ckevt_sa1100_osmr0); |
131 | { | ||
132 | if (match_posponed) { | ||
133 | match_posponed = 0; | ||
134 | OSMR0 = initial_match; | ||
135 | if ((signed long)(initial_match - OSCR) <= 0) | ||
136 | return sa1100_timer_interrupt(irq, dev_id); | ||
137 | } | ||
138 | return IRQ_NONE; | ||
139 | } | 121 | } |
140 | 122 | ||
141 | static struct dyn_tick_timer sa1100_dyn_tick = { | ||
142 | .enable = sa1100_dyn_tick_enable_disable, | ||
143 | .disable = sa1100_dyn_tick_enable_disable, | ||
144 | .reprogram = sa1100_dyn_tick_reprogram, | ||
145 | .handler = sa1100_dyn_tick_handler, | ||
146 | }; | ||
147 | #endif | ||
148 | |||
149 | #ifdef CONFIG_PM | 123 | #ifdef CONFIG_PM |
150 | unsigned long osmr[4], oier; | 124 | unsigned long osmr[4], oier; |
151 | 125 | ||
@@ -181,7 +155,4 @@ struct sys_timer sa1100_timer = { | |||
181 | .init = sa1100_timer_init, | 155 | .init = sa1100_timer_init, |
182 | .suspend = sa1100_timer_suspend, | 156 | .suspend = sa1100_timer_suspend, |
183 | .resume = sa1100_timer_resume, | 157 | .resume = sa1100_timer_resume, |
184 | #ifdef CONFIG_NO_IDLE_HZ | ||
185 | .dyn_tick = &sa1100_dyn_tick, | ||
186 | #endif | ||
187 | }; | 158 | }; |