diff options
author | Richard Henderson <rth@twiddle.net> | 2013-07-14 14:50:21 -0700 |
---|---|---|
committer | Matt Turner <mattst88@gmail.com> | 2013-11-16 16:33:21 -0800 |
commit | 4914d7b458e35a7db2f9c7dc6eb014620254bbbf (patch) | |
tree | 07bc9c2eedb174f0710dddca3950dcb12789236c | |
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>
-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 e78ec9bcc76..5422a47646f 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); #define tbiap() __tbi(-1, /* no second argument */) #define tbia() __tbi(-2, /* no second argument */) +/* + * QEMU Cserv routines.. + */ + +static inline unsigned long +qemu_get_walltime(void) +{ + register unsigned long v0 __asm__("$0"); + register unsigned long a0 __asm__("$16") = 3; + + asm("call_pal %2 # cserve get_time" + : "=r"(v0), "+r"(a0) + : "i"(PAL_cserve) + : "$17", "$18", "$19", "$20", "$21"); + + return v0; +} + +static inline unsigned long +qemu_get_alarm(void) +{ + register unsigned long v0 __asm__("$0"); + register unsigned long a0 __asm__("$16") = 4; + + asm("call_pal %2 # cserve get_alarm" + : "=r"(v0), "+r"(a0) + : "i"(PAL_cserve) + : "$17", "$18", "$19", "$20", "$21"); + + return v0; +} + +static inline void +qemu_set_alarm_rel(unsigned long expire) +{ + register unsigned long a0 __asm__("$16") = 5; + register unsigned long a1 __asm__("$17") = expire; + + asm volatile("call_pal %2 # cserve set_alarm_rel" + : "+r"(a0), "+r"(a1) + : "i"(PAL_cserve) + : "$0", "$18", "$19", "$20", "$21"); +} + +static inline void +qemu_set_alarm_abs(unsigned long expire) +{ + register unsigned long a0 __asm__("$16") = 6; + register unsigned long a1 __asm__("$17") = expire; + + asm volatile("call_pal %2 # cserve set_alarm_abs" + : "+r"(a0), "+r"(a1) + : "i"(PAL_cserve) + : "$0", "$18", "$19", "$20", "$21"); +} + +static inline unsigned long +qemu_get_vmtime(void) +{ + register unsigned long v0 __asm__("$0"); + register unsigned long a0 __asm__("$16") = 7; + + asm("call_pal %2 # cserve get_time" + : "=r"(v0), "+r"(a0) + : "i"(PAL_cserve) + : "$17", "$18", "$19", "$20", "$21"); + + return v0; +} + #endif /* !__ASSEMBLY__ */ #endif /* __ALPHA_PAL_H */ diff --git a/arch/alpha/kernel/irq_alpha.c b/arch/alpha/kernel/irq_alpha.c index 6990ddc0fba..1c8625cb0e2 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, */ struct irqaction timer_irqaction = { - .handler = timer_interrupt, + .handler = rtc_timer_interrupt, .name = "timer", }; diff --git a/arch/alpha/kernel/proto.h b/arch/alpha/kernel/proto.h index bc806893afe..da2d6ec9c37 100644 --- a/arch/alpha/kernel/proto.h +++ b/arch/alpha/kernel/proto.h @@ -140,7 +140,7 @@ extern void handle_ipi(struct pt_regs *); /* extern void reset_for_srm(void); */ /* time.c */ -extern irqreturn_t timer_interrupt(int irq, void *dev); +extern irqreturn_t rtc_timer_interrupt(int irq, void *dev); extern void init_clockevent(void); extern void common_init_rtc(void); extern unsigned long est_cycle_freq; diff --git a/arch/alpha/kernel/time.c b/arch/alpha/kernel/time.c index 08ff3f502a7..ee39cee8064 100644 --- a/arch/alpha/kernel/time.c +++ b/arch/alpha/kernel/time.c @@ -87,7 +87,7 @@ static inline __u32 rpcc(void) static DEFINE_PER_CPU(struct clock_event_device, cpu_ce); irqreturn_t -timer_interrupt(int irq, void *dev) +rtc_timer_interrupt(int irq, void *dev) { int cpu = smp_processor_id(); 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) return -EINVAL; } -void __init -init_clockevent(void) +static void __init +init_rtc_clockevent(void) { int cpu = smp_processor_id(); struct clock_event_device *ce = &per_cpu(cpu_ce, cpu); @@ -136,6 +136,75 @@ init_clockevent(void) clockevents_config_and_register(ce, CONFIG_HZ, 0, 0); } + +/* + * The QEMU clock as a clocksource primitive. + */ + +static cycle_t +qemu_cs_read(struct clocksource *cs) +{ + return qemu_get_vmtime(); +} + +static struct clocksource qemu_cs = { + .name = "qemu", + .rating = 400, + .read = qemu_cs_read, + .mask = CLOCKSOURCE_MASK(64), + .flags = CLOCK_SOURCE_IS_CONTINUOUS, + .max_idle_ns = LONG_MAX +}; + + +/* + * The QEMU alarm as a clock_event_device primitive. + */ + +static void +qemu_ce_set_mode(enum clock_event_mode mode, struct clock_event_device *ce) +{ + /* The mode member of CE is updated for us in generic code. + Just make sure that the event is disabled. */ + qemu_set_alarm_abs(0); +} + +static int +qemu_ce_set_next_event(unsigned long evt, struct clock_event_device *ce) +{ + qemu_set_alarm_rel(evt); + return 0; +} + +static irqreturn_t +qemu_timer_interrupt(int irq, void *dev) +{ + int cpu = smp_processor_id(); + struct clock_event_device *ce = &per_cpu(cpu_ce, cpu); + + ce->event_handler(ce); + return IRQ_HANDLED; +} + +static void __init +init_qemu_clockevent(void) +{ + int cpu = smp_processor_id(); + struct clock_event_device *ce = &per_cpu(cpu_ce, cpu); + + *ce = (struct clock_event_device){ + .name = "qemu", + .features = CLOCK_EVT_FEAT_ONESHOT, + .rating = 400, + .cpumask = cpumask_of(cpu), + .set_mode = qemu_ce_set_mode, + .set_next_event = qemu_ce_set_next_event, + }; + + clockevents_config_and_register(ce, NSEC_PER_SEC, 1000, LONG_MAX); +} + + void __init common_init_rtc(void) { @@ -329,6 +398,15 @@ time_init(void) unsigned long cycle_freq, tolerance; long diff; + if (alpha_using_qemu) { + clocksource_register_hz(&qemu_cs, NSEC_PER_SEC); + init_qemu_clockevent(); + + timer_irqaction.handler = qemu_timer_interrupt; + init_rtc_irq(); + return; + } + /* Calibrate CPU clock -- attempt #1. */ if (!est_cycle_freq) est_cycle_freq = validate_cc_value(calibrate_cc_with_pit()); @@ -371,7 +449,17 @@ time_init(void) /* Startup the timer source. */ alpha_mv.init_rtc(); + init_rtc_clockevent(); +} - /* Start up the clock event device. */ - init_clockevent(); +/* Initialize the clock_event_device for secondary cpus. */ +#ifdef CONFIG_SMP +void __init +init_clockevent(void) +{ + if (alpha_using_qemu) + init_qemu_clockevent(); + else + init_rtc_clockevent(); } +#endif |