Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+70 −0
Original line number Diff line number Diff line
@@ -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 */
+1 −1
Original line number Diff line number Diff line
@@ -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",
};

+1 −1
Original line number Diff line number Diff line
@@ -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;
+93 −5
Original line number Diff line number Diff line
@@ -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