diff --git a/xen/arch/arm/time.c b/xen/arch/arm/time.c new file mode 100644 index 0000000000..13c1254cd4 --- /dev/null +++ b/xen/arch/arm/time.c @@ -0,0 +1,181 @@ +/* + * xen/arch/arm/time.c + * + * Time and timer support, using the ARM Generic Timer interfaces + * + * Tim Deegan + * Copyright (c) 2011 Citrix Systems. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Unfortunately the hypervisor timer interrupt appears to be buggy */ +#define USE_HYP_TIMER 0 + +/* For fine-grained timekeeping, we use the ARM "Generic Timer", a + * register-mapped time source in the SoC. */ +static uint32_t __read_mostly cntfrq; /* Ticks per second */ +static uint64_t __read_mostly boot_count; /* Counter value at boot time */ + +/*static inline*/ s_time_t ticks_to_ns(uint64_t ticks) +{ + return muldiv64(ticks, SECONDS(1), cntfrq); +} + +/*static inline*/ uint64_t ns_to_ticks(s_time_t ns) +{ + return muldiv64(ns, cntfrq, SECONDS(1)); +} + +/* TODO: On a real system the firmware would have set the frequency in + the CNTFRQ register. Also we'd need to use devicetree to find + the RTC. When we've seen some real systems, we can delete this. +static uint32_t calibrate_timer(void) +{ + uint32_t sec; + uint64_t start, end; + paddr_t rtc_base = 0x1C170000ull; + volatile uint32_t *rtc; + + ASSERT(!local_irq_is_enabled()); + set_fixmap(FIXMAP_MISC, rtc_base >> PAGE_SHIFT, DEV_SHARED); + rtc = (uint32_t *) FIXMAP_ADDR(FIXMAP_MISC); + + printk("Calibrating timer against RTC..."); + // Turn on the RTC + rtc[3] = 1; + // Wait for an edge + sec = rtc[0] + 1; + do {} while ( rtc[0] != sec ); + // Now time a few seconds + start = READ_CP64(CNTPCT); + do {} while ( rtc[0] < sec + 32 ); + end = READ_CP64(CNTPCT); + printk("done.\n"); + + clear_fixmap(FIXMAP_MISC); + return (end - start) / 32; +} +*/ + +/* Set up the timer on the boot CPU */ +int __init init_xen_time(void) +{ + /* Check that this CPU supports the Generic Timer interface */ + if ( (READ_CP32(ID_PFR1) & ID_PFR1_GT_MASK) != ID_PFR1_GT_v1 ) + panic("CPU does not support the Generic Timer v1 interface.\n"); + + cntfrq = READ_CP32(CNTFRQ); + boot_count = READ_CP64(CNTPCT); + printk("Using generic timer at %"PRIu32" Hz\n", cntfrq); + + return 0; +} + +/* Return number of nanoseconds since boot */ +s_time_t get_s_time(void) +{ + uint64_t ticks = READ_CP64(CNTPCT) - boot_count; + return ticks_to_ns(ticks); +} + +/* Set the timer to wake us up at a particular time. + * Timeout is a Xen system time (nanoseconds since boot); 0 disables the timer. + * Returns 1 on success; 0 if the timeout is too soon or is in the past. */ +int reprogram_timer(s_time_t timeout) +{ + uint64_t deadline; + + if ( timeout == 0 ) + { +#if USE_HYP_TIMER + WRITE_CP32(0, CNTHP_CTL); +#else + WRITE_CP32(0, CNTP_CTL); +#endif + return 1; + } + + deadline = ns_to_ticks(timeout) + boot_count; +#if USE_HYP_TIMER + WRITE_CP64(deadline, CNTHP_CVAL); + WRITE_CP32(CNTx_CTL_ENABLE, CNTHP_CTL); +#else + WRITE_CP64(deadline, CNTP_CVAL); + WRITE_CP32(CNTx_CTL_ENABLE, CNTP_CTL); +#endif + isb(); + + /* No need to check for timers in the past; the Generic Timer fires + * on a signed 63-bit comparison. */ + return 1; +} + +/* Handle the firing timer */ +static void timer_interrupt(int irq, void *dev_id, struct cpu_user_regs *regs) +{ + if ( irq == 26 && READ_CP32(CNTHP_CTL) & CNTx_CTL_PENDING ) + { + /* Signal the generic timer code to do its work */ + raise_softirq(TIMER_SOFTIRQ); + /* Disable the timer to avoid more interrupts */ + WRITE_CP32(0, CNTHP_CTL); + } + + if (irq == 30 && READ_CP32(CNTP_CTL) & CNTx_CTL_PENDING ) + { + /* Signal the generic timer code to do its work */ + raise_softirq(TIMER_SOFTIRQ); + /* Disable the timer to avoid more interrupts */ + WRITE_CP32(0, CNTP_CTL); + } +} + +/* Set up the timer interrupt on this CPU */ +void __cpuinit init_timer_interrupt(void) +{ + /* Sensible defaults */ + WRITE_CP64(0, CNTVOFF); /* No VM-specific offset */ + WRITE_CP32(0, CNTKCTL); /* No user-mode access */ +#if USE_HYP_TIMER + /* Let the VMs read the physical counter and timer so they can tell time */ + WRITE_CP32(CNTHCTL_PA|CNTHCTL_TA, CNTHCTL); +#else + /* Cannot let VMs access physical counter if we are using it */ + WRITE_CP32(0, CNTHCTL); +#endif + WRITE_CP32(0, CNTP_CTL); /* Physical timer disabled */ + WRITE_CP32(0, CNTHP_CTL); /* Hypervisor's timer disabled */ + isb(); + + /* XXX Need to find this IRQ number from devicetree? */ + request_irq(26, timer_interrupt, 0, "hyptimer", NULL); + request_irq(30, timer_interrupt, 0, "phytimer", NULL); +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/xen/include/asm-arm/time.h b/xen/include/asm-arm/time.h new file mode 100644 index 0000000000..8cc9e78888 --- /dev/null +++ b/xen/include/asm-arm/time.h @@ -0,0 +1,26 @@ +#ifndef __ARM_TIME_H__ +#define __ARM_TIME_H__ + +typedef unsigned long cycles_t; + +static inline cycles_t get_cycles (void) +{ + return 0; +} + +struct tm; +struct tm wallclock_time(void); + + +/* Set up the timer interrupt on this CPU */ +extern void __cpuinit init_timer_interrupt(void); + +#endif /* __ARM_TIME_H__ */ +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */