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>
This commit is contained in:
parent
a1659d6d12
commit
4914d7b458
|
@ -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 */
|
||||
|
|
|
@ -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",
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
||||
/* Start up the clock event device. */
|
||||
init_clockevent();
|
||||
init_rtc_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
|
||||
|
|
Loading…
Reference in a new issue