diff options
author | Richard Henderson <rth@twiddle.net> | 2013-07-14 17:50:21 -0400 |
---|---|---|
committer | Matt Turner <mattst88@gmail.com> | 2013-11-16 19:33:21 -0500 |
commit | 4914d7b458e35a7db2f9c7dc6eb014620254bbbf (patch) | |
tree | 07bc9c2eedb174f0710dddca3950dcb12789236c /arch | |
parent | a1659d6d128a7e0c2985bce7c957b66af1f71181 (diff) |
alpha: Use qemu+cserve provided high-res clock and alarm.
QEMU provides a high-resolution timer and alarm; use this for
a clock source and clock event source when available.
Signed-off-by: Richard Henderson <rth@twiddle.net>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/alpha/include/asm/pal.h | 70 | ||||
-rw-r--r-- | arch/alpha/kernel/irq_alpha.c | 2 | ||||
-rw-r--r-- | arch/alpha/kernel/proto.h | 2 | ||||
-rw-r--r-- | arch/alpha/kernel/time.c | 98 |
4 files changed, 165 insertions, 7 deletions
diff --git a/arch/alpha/include/asm/pal.h b/arch/alpha/include/asm/pal.h index e78ec9bcc768..5422a47646fc 100644 --- a/arch/alpha/include/asm/pal.h +++ b/arch/alpha/include/asm/pal.h | |||
@@ -112,5 +112,75 @@ __CALL_PAL_RW1(wtint, unsigned long, unsigned long); | |||
112 | #define tbiap() __tbi(-1, /* no second argument */) | 112 | #define tbiap() __tbi(-1, /* no second argument */) |
113 | #define tbia() __tbi(-2, /* no second argument */) | 113 | #define tbia() __tbi(-2, /* no second argument */) |
114 | 114 | ||
115 | /* | ||
116 | * QEMU Cserv routines.. | ||
117 | */ | ||
118 | |||
119 | static inline unsigned long | ||
120 | qemu_get_walltime(void) | ||
121 | { | ||
122 | register unsigned long v0 __asm__("$0"); | ||
123 | register unsigned long a0 __asm__("$16") = 3; | ||
124 | |||
125 | asm("call_pal %2 # cserve get_time" | ||
126 | : "=r"(v0), "+r"(a0) | ||
127 | : "i"(PAL_cserve) | ||
128 | : "$17", "$18", "$19", "$20", "$21"); | ||
129 | |||
130 | return v0; | ||
131 | } | ||
132 | |||
133 | static inline unsigned long | ||
134 | qemu_get_alarm(void) | ||
135 | { | ||
136 | register unsigned long v0 __asm__("$0"); | ||
137 | register unsigned long a0 __asm__("$16") = 4; | ||
138 | |||
139 | asm("call_pal %2 # cserve get_alarm" | ||
140 | : "=r"(v0), "+r"(a0) | ||
141 | : "i"(PAL_cserve) | ||
142 | : "$17", "$18", "$19", "$20", "$21"); | ||
143 | |||
144 | return v0; | ||
145 | } | ||
146 | |||
147 | static inline void | ||
148 | qemu_set_alarm_rel(unsigned long expire) | ||
149 | { | ||
150 | register unsigned long a0 __asm__("$16") = 5; | ||
151 | register unsigned long a1 __asm__("$17") = expire; | ||
152 | |||
153 | asm volatile("call_pal %2 # cserve set_alarm_rel" | ||
154 | : "+r"(a0), "+r"(a1) | ||
155 | : "i"(PAL_cserve) | ||
156 | : "$0", "$18", "$19", "$20", "$21"); | ||
157 | } | ||
158 | |||
159 | static inline void | ||
160 | qemu_set_alarm_abs(unsigned long expire) | ||
161 | { | ||
162 | register unsigned long a0 __asm__("$16") = 6; | ||
163 | register unsigned long a1 __asm__("$17") = expire; | ||
164 | |||
165 | asm volatile("call_pal %2 # cserve set_alarm_abs" | ||
166 | : "+r"(a0), "+r"(a1) | ||
167 | : "i"(PAL_cserve) | ||
168 | : "$0", "$18", "$19", "$20", "$21"); | ||
169 | } | ||
170 | |||
171 | static inline unsigned long | ||
172 | qemu_get_vmtime(void) | ||
173 | { | ||
174 | register unsigned long v0 __asm__("$0"); | ||
175 | register unsigned long a0 __asm__("$16") = 7; | ||
176 | |||
177 | asm("call_pal %2 # cserve get_time" | ||
178 | : "=r"(v0), "+r"(a0) | ||
179 | : "i"(PAL_cserve) | ||
180 | : "$17", "$18", "$19", "$20", "$21"); | ||
181 | |||
182 | return v0; | ||
183 | } | ||
184 | |||
115 | #endif /* !__ASSEMBLY__ */ | 185 | #endif /* !__ASSEMBLY__ */ |
116 | #endif /* __ALPHA_PAL_H */ | 186 | #endif /* __ALPHA_PAL_H */ |
diff --git a/arch/alpha/kernel/irq_alpha.c b/arch/alpha/kernel/irq_alpha.c index 6990ddc0fbaf..1c8625cb0e25 100644 --- a/arch/alpha/kernel/irq_alpha.c +++ b/arch/alpha/kernel/irq_alpha.c | |||
@@ -214,7 +214,7 @@ process_mcheck_info(unsigned long vector, unsigned long la_ptr, | |||
214 | */ | 214 | */ |
215 | 215 | ||
216 | struct irqaction timer_irqaction = { | 216 | struct irqaction timer_irqaction = { |
217 | .handler = timer_interrupt, | 217 | .handler = rtc_timer_interrupt, |
218 | .name = "timer", | 218 | .name = "timer", |
219 | }; | 219 | }; |
220 | 220 | ||
diff --git a/arch/alpha/kernel/proto.h b/arch/alpha/kernel/proto.h index bc806893afe0..da2d6ec9c370 100644 --- a/arch/alpha/kernel/proto.h +++ b/arch/alpha/kernel/proto.h | |||
@@ -140,7 +140,7 @@ extern void handle_ipi(struct pt_regs *); | |||
140 | /* extern void reset_for_srm(void); */ | 140 | /* extern void reset_for_srm(void); */ |
141 | 141 | ||
142 | /* time.c */ | 142 | /* time.c */ |
143 | extern irqreturn_t timer_interrupt(int irq, void *dev); | 143 | extern irqreturn_t rtc_timer_interrupt(int irq, void *dev); |
144 | extern void init_clockevent(void); | 144 | extern void init_clockevent(void); |
145 | extern void common_init_rtc(void); | 145 | extern void common_init_rtc(void); |
146 | extern unsigned long est_cycle_freq; | 146 | extern unsigned long est_cycle_freq; |
diff --git a/arch/alpha/kernel/time.c b/arch/alpha/kernel/time.c index 08ff3f502a76..ee39cee8064c 100644 --- a/arch/alpha/kernel/time.c +++ b/arch/alpha/kernel/time.c | |||
@@ -87,7 +87,7 @@ static inline __u32 rpcc(void) | |||
87 | static DEFINE_PER_CPU(struct clock_event_device, cpu_ce); | 87 | static DEFINE_PER_CPU(struct clock_event_device, cpu_ce); |
88 | 88 | ||
89 | irqreturn_t | 89 | irqreturn_t |
90 | timer_interrupt(int irq, void *dev) | 90 | rtc_timer_interrupt(int irq, void *dev) |
91 | { | 91 | { |
92 | int cpu = smp_processor_id(); | 92 | int cpu = smp_processor_id(); |
93 | struct clock_event_device *ce = &per_cpu(cpu_ce, cpu); | 93 | struct clock_event_device *ce = &per_cpu(cpu_ce, cpu); |
@@ -118,8 +118,8 @@ rtc_ce_set_next_event(unsigned long evt, struct clock_event_device *ce) | |||
118 | return -EINVAL; | 118 | return -EINVAL; |
119 | } | 119 | } |
120 | 120 | ||
121 | void __init | 121 | static void __init |
122 | init_clockevent(void) | 122 | init_rtc_clockevent(void) |
123 | { | 123 | { |
124 | int cpu = smp_processor_id(); | 124 | int cpu = smp_processor_id(); |
125 | struct clock_event_device *ce = &per_cpu(cpu_ce, cpu); | 125 | struct clock_event_device *ce = &per_cpu(cpu_ce, cpu); |
@@ -136,6 +136,75 @@ init_clockevent(void) | |||
136 | clockevents_config_and_register(ce, CONFIG_HZ, 0, 0); | 136 | clockevents_config_and_register(ce, CONFIG_HZ, 0, 0); |
137 | } | 137 | } |
138 | 138 | ||
139 | |||
140 | /* | ||
141 | * The QEMU clock as a clocksource primitive. | ||
142 | */ | ||
143 | |||
144 | static cycle_t | ||
145 | qemu_cs_read(struct clocksource *cs) | ||
146 | { | ||
147 | return qemu_get_vmtime(); | ||
148 | } | ||
149 | |||
150 | static struct clocksource qemu_cs = { | ||
151 | .name = "qemu", | ||
152 | .rating = 400, | ||
153 | .read = qemu_cs_read, | ||
154 | .mask = CLOCKSOURCE_MASK(64), | ||
155 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | ||
156 | .max_idle_ns = LONG_MAX | ||
157 | }; | ||
158 | |||
159 | |||
160 | /* | ||
161 | * The QEMU alarm as a clock_event_device primitive. | ||
162 | */ | ||
163 | |||
164 | static void | ||
165 | qemu_ce_set_mode(enum clock_event_mode mode, struct clock_event_device *ce) | ||
166 | { | ||
167 | /* The mode member of CE is updated for us in generic code. | ||
168 | Just make sure that the event is disabled. */ | ||
169 | qemu_set_alarm_abs(0); | ||
170 | } | ||
171 | |||
172 | static int | ||
173 | qemu_ce_set_next_event(unsigned long evt, struct clock_event_device *ce) | ||
174 | { | ||
175 | qemu_set_alarm_rel(evt); | ||
176 | return 0; | ||
177 | } | ||
178 | |||
179 | static irqreturn_t | ||
180 | qemu_timer_interrupt(int irq, void *dev) | ||
181 | { | ||
182 | int cpu = smp_processor_id(); | ||
183 | struct clock_event_device *ce = &per_cpu(cpu_ce, cpu); | ||
184 | |||
185 | ce->event_handler(ce); | ||
186 | return IRQ_HANDLED; | ||
187 | } | ||
188 | |||
189 | static void __init | ||
190 | init_qemu_clockevent(void) | ||
191 | { | ||
192 | int cpu = smp_processor_id(); | ||
193 | struct clock_event_device *ce = &per_cpu(cpu_ce, cpu); | ||
194 | |||
195 | *ce = (struct clock_event_device){ | ||
196 | .name = "qemu", | ||
197 | .features = CLOCK_EVT_FEAT_ONESHOT, | ||
198 | .rating = 400, | ||
199 | .cpumask = cpumask_of(cpu), | ||
200 | .set_mode = qemu_ce_set_mode, | ||
201 | .set_next_event = qemu_ce_set_next_event, | ||
202 | }; | ||
203 | |||
204 | clockevents_config_and_register(ce, NSEC_PER_SEC, 1000, LONG_MAX); | ||
205 | } | ||
206 | |||
207 | |||
139 | void __init | 208 | void __init |
140 | common_init_rtc(void) | 209 | common_init_rtc(void) |
141 | { | 210 | { |
@@ -329,6 +398,15 @@ time_init(void) | |||
329 | unsigned long cycle_freq, tolerance; | 398 | unsigned long cycle_freq, tolerance; |
330 | long diff; | 399 | long diff; |
331 | 400 | ||
401 | if (alpha_using_qemu) { | ||
402 | clocksource_register_hz(&qemu_cs, NSEC_PER_SEC); | ||
403 | init_qemu_clockevent(); | ||
404 | |||
405 | timer_irqaction.handler = qemu_timer_interrupt; | ||
406 | init_rtc_irq(); | ||
407 | return; | ||
408 | } | ||
409 | |||
332 | /* Calibrate CPU clock -- attempt #1. */ | 410 | /* Calibrate CPU clock -- attempt #1. */ |
333 | if (!est_cycle_freq) | 411 | if (!est_cycle_freq) |
334 | est_cycle_freq = validate_cc_value(calibrate_cc_with_pit()); | 412 | est_cycle_freq = validate_cc_value(calibrate_cc_with_pit()); |
@@ -371,7 +449,17 @@ time_init(void) | |||
371 | 449 | ||
372 | /* Startup the timer source. */ | 450 | /* Startup the timer source. */ |
373 | alpha_mv.init_rtc(); | 451 | alpha_mv.init_rtc(); |
452 | init_rtc_clockevent(); | ||
453 | } | ||
374 | 454 | ||
375 | /* Start up the clock event device. */ | 455 | /* Initialize the clock_event_device for secondary cpus. */ |
376 | init_clockevent(); | 456 | #ifdef CONFIG_SMP |
457 | void __init | ||
458 | init_clockevent(void) | ||
459 | { | ||
460 | if (alpha_using_qemu) | ||
461 | init_qemu_clockevent(); | ||
462 | else | ||
463 | init_rtc_clockevent(); | ||
377 | } | 464 | } |
465 | #endif | ||