Commit 4914d7b4 authored by Richard Henderson's avatar Richard Henderson Committed by Matt Turner

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: default avatarRichard Henderson <rth@twiddle.net>
parent a1659d6d
...@@ -112,5 +112,75 @@ __CALL_PAL_RW1(wtint, unsigned long, unsigned long); ...@@ -112,5 +112,75 @@ __CALL_PAL_RW1(wtint, unsigned long, unsigned long);
#define tbiap() __tbi(-1, /* no second argument */) #define tbiap() __tbi(-1, /* no second argument */)
#define tbia() __tbi(-2, /* 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 /* !__ASSEMBLY__ */
#endif /* __ALPHA_PAL_H */ #endif /* __ALPHA_PAL_H */
...@@ -214,7 +214,7 @@ process_mcheck_info(unsigned long vector, unsigned long la_ptr, ...@@ -214,7 +214,7 @@ process_mcheck_info(unsigned long vector, unsigned long la_ptr,
*/ */
struct irqaction timer_irqaction = { struct irqaction timer_irqaction = {
.handler = timer_interrupt, .handler = rtc_timer_interrupt,
.name = "timer", .name = "timer",
}; };
......
...@@ -140,7 +140,7 @@ extern void handle_ipi(struct pt_regs *); ...@@ -140,7 +140,7 @@ extern void handle_ipi(struct pt_regs *);
/* extern void reset_for_srm(void); */ /* extern void reset_for_srm(void); */
/* time.c */ /* 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 init_clockevent(void);
extern void common_init_rtc(void); extern void common_init_rtc(void);
extern unsigned long est_cycle_freq; extern unsigned long est_cycle_freq;
......
...@@ -87,7 +87,7 @@ static inline __u32 rpcc(void) ...@@ -87,7 +87,7 @@ static inline __u32 rpcc(void)
static DEFINE_PER_CPU(struct clock_event_device, cpu_ce); static DEFINE_PER_CPU(struct clock_event_device, cpu_ce);
irqreturn_t irqreturn_t
timer_interrupt(int irq, void *dev) rtc_timer_interrupt(int irq, void *dev)
{ {
int cpu = smp_processor_id(); int cpu = smp_processor_id();
struct clock_event_device *ce = &per_cpu(cpu_ce, cpu); 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,8 +118,8 @@ rtc_ce_set_next_event(unsigned long evt, struct clock_event_device *ce)
return -EINVAL; return -EINVAL;
} }
void __init static void __init
init_clockevent(void) init_rtc_clockevent(void)
{ {
int cpu = smp_processor_id(); int cpu = smp_processor_id();
struct clock_event_device *ce = &per_cpu(cpu_ce, cpu); struct clock_event_device *ce = &per_cpu(cpu_ce, cpu);
...@@ -136,6 +136,75 @@ init_clockevent(void) ...@@ -136,6 +136,75 @@ init_clockevent(void)
clockevents_config_and_register(ce, CONFIG_HZ, 0, 0); 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 void __init
common_init_rtc(void) common_init_rtc(void)
{ {
...@@ -329,6 +398,15 @@ time_init(void) ...@@ -329,6 +398,15 @@ time_init(void)
unsigned long cycle_freq, tolerance; unsigned long cycle_freq, tolerance;
long diff; 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. */ /* Calibrate CPU clock -- attempt #1. */
if (!est_cycle_freq) if (!est_cycle_freq)
est_cycle_freq = validate_cc_value(calibrate_cc_with_pit()); est_cycle_freq = validate_cc_value(calibrate_cc_with_pit());
...@@ -371,7 +449,17 @@ time_init(void) ...@@ -371,7 +449,17 @@ time_init(void)
/* Startup the timer source. */ /* Startup the timer source. */
alpha_mv.init_rtc(); alpha_mv.init_rtc();
init_rtc_clockevent();
}
/* Start up the clock event device. */ /* Initialize the clock_event_device for secondary cpus. */
init_clockevent(); #ifdef CONFIG_SMP
void __init
init_clockevent(void)
{
if (alpha_using_qemu)
init_qemu_clockevent();
else
init_rtc_clockevent();
} }
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment