mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-05 16:54:27 +00:00
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
4 changed files with 166 additions and 8 deletions
|
@ -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,
|
||||||
*/
|
*/
|
||||||
|
|
||||||
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 *);
|
||||||
/* 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)
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
|
|
||||||
/* 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. */
|
|
||||||
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
|
||||||
|
|
Loading…
Add table
Reference in a new issue