* [Qemu-devel] [PATCH 0/4] target-arm: Implement support for generic timers @ 2013-07-30 17:55 Peter Maydell 2013-07-30 17:55 ` [Qemu-devel] [PATCH 1/4] target-arm: Allow raw_read() and raw_write() to handle 64 bit regs Peter Maydell ` (4 more replies) 0 siblings, 5 replies; 7+ messages in thread From: Peter Maydell @ 2013-07-30 17:55 UTC (permalink / raw) To: qemu-devel; +Cc: patches This patch series implements support for the 'generic timers', which are a set of timers defined in the ARM Architecture Reference Manual and implemented by the Cortex-A15. We've got away without these up til now because Linux will generally fall back on whatever random timer is present on the devboard if it can't find the on-CPU timers, but this is less than ideal. (Among other things, the proposed mach-virt should just use the generic timers so it doesn't have to provide an sp804 timer.) Peter Maydell (4): target-arm: Allow raw_read() and raw_write() to handle 64 bit regs target-arm: Support coprocessor registers which do I/O target-arm: Implement the generic timer hw/cpu/a15mpcore: Wire generic timer outputs to GIC inputs hw/cpu/a15mpcore.c | 18 ++++ target-arm/cpu-qom.h | 9 ++ target-arm/cpu.c | 9 ++ target-arm/cpu.h | 24 ++++- target-arm/helper.c | 267 ++++++++++++++++++++++++++++++++++++++++++++++-- target-arm/machine.c | 8 +- target-arm/translate.c | 16 ++- 7 files changed, 337 insertions(+), 14 deletions(-) -- 1.7.9.5 ^ permalink raw reply [flat|nested] 7+ messages in thread
* [Qemu-devel] [PATCH 1/4] target-arm: Allow raw_read() and raw_write() to handle 64 bit regs 2013-07-30 17:55 [Qemu-devel] [PATCH 0/4] target-arm: Implement support for generic timers Peter Maydell @ 2013-07-30 17:55 ` Peter Maydell 2013-07-30 17:55 ` [Qemu-devel] [PATCH 2/4] target-arm: Support coprocessor registers which do I/O Peter Maydell ` (3 subsequent siblings) 4 siblings, 0 replies; 7+ messages in thread From: Peter Maydell @ 2013-07-30 17:55 UTC (permalink / raw) To: qemu-devel; +Cc: patches Extend the raw_read() and raw_write() helper accessors so that they can be used for 64 bit registers as well as 32 bit registers. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target-arm/helper.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/target-arm/helper.c b/target-arm/helper.c index 4968391..fc5f757 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -67,14 +67,22 @@ static int vfp_gdb_set_reg(CPUARMState *env, uint8_t *buf, int reg) static int raw_read(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t *value) { - *value = CPREG_FIELD32(env, ri); + if (ri->type & ARM_CP_64BIT) { + *value = CPREG_FIELD64(env, ri); + } else { + *value = CPREG_FIELD32(env, ri); + } return 0; } static int raw_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { - CPREG_FIELD32(env, ri) = value; + if (ri->type & ARM_CP_64BIT) { + CPREG_FIELD64(env, ri) = value; + } else { + CPREG_FIELD32(env, ri) = value; + } return 0; } -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 7+ messages in thread
* [Qemu-devel] [PATCH 2/4] target-arm: Support coprocessor registers which do I/O 2013-07-30 17:55 [Qemu-devel] [PATCH 0/4] target-arm: Implement support for generic timers Peter Maydell 2013-07-30 17:55 ` [Qemu-devel] [PATCH 1/4] target-arm: Allow raw_read() and raw_write() to handle 64 bit regs Peter Maydell @ 2013-07-30 17:55 ` Peter Maydell 2013-07-30 17:55 ` [Qemu-devel] [PATCH 3/4] target-arm: Implement the generic timer Peter Maydell ` (2 subsequent siblings) 4 siblings, 0 replies; 7+ messages in thread From: Peter Maydell @ 2013-07-30 17:55 UTC (permalink / raw) To: qemu-devel; +Cc: patches Add an ARM_CP_IO flag which an ARMCPRegInfo definition can use to indicate that the register's implementation does I/O and thus its accesses need to be surrounded by gen_io_start()/gen_io_end() in order for icount to work. Most notably, cp registers which implement clocks or timers need this. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target-arm/cpu.h | 6 +++++- target-arm/translate.c | 16 +++++++++++++--- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/target-arm/cpu.h b/target-arm/cpu.h index b2dc494..770a240 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -469,6 +469,9 @@ static inline uint64_t cpreg_to_kvm_id(uint32_t cpregid) * old must have the OVERRIDE bit set. * NO_MIGRATE indicates that this register should be ignored for migration; * (eg because any state is accessed via some other coprocessor register). + * IO indicates that this register does I/O and therefore its accesses + * need to be surrounded by gen_io_start()/gen_io_end(). In particular, + * registers which implement clocks or timers require this. */ #define ARM_CP_SPECIAL 1 #define ARM_CP_CONST 2 @@ -476,13 +479,14 @@ static inline uint64_t cpreg_to_kvm_id(uint32_t cpregid) #define ARM_CP_SUPPRESS_TB_END 8 #define ARM_CP_OVERRIDE 16 #define ARM_CP_NO_MIGRATE 32 +#define ARM_CP_IO 64 #define ARM_CP_NOP (ARM_CP_SPECIAL | (1 << 8)) #define ARM_CP_WFI (ARM_CP_SPECIAL | (2 << 8)) #define ARM_LAST_SPECIAL ARM_CP_WFI /* Used only as a terminator for ARMCPRegInfo lists */ #define ARM_CP_SENTINEL 0xffff /* Mask of only the flag bits in a type field */ -#define ARM_CP_FLAG_MASK 0x3f +#define ARM_CP_FLAG_MASK 0x7f /* Return true if cptype is a valid type field. This is used to try to * catch errors where the sentinel has been accidentally left off the end diff --git a/target-arm/translate.c b/target-arm/translate.c index 6db4c50..d1e8538 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -6280,6 +6280,10 @@ static int disas_coproc_insn(CPUARMState * env, DisasContext *s, uint32_t insn) break; } + if (use_icount && (ri->type & ARM_CP_IO)) { + gen_io_start(); + } + if (isread) { /* Read */ if (is64) { @@ -6369,14 +6373,20 @@ static int disas_coproc_insn(CPUARMState * env, DisasContext *s, uint32_t insn) store_cpu_offset(tmp, ri->fieldoffset); } } + } + + if (use_icount && (ri->type & ARM_CP_IO)) { + /* I/O operations must end the TB here (whether read or write) */ + gen_io_end(); + gen_lookup_tb(s); + } else if (!isread && !(ri->type & ARM_CP_SUPPRESS_TB_END)) { /* We default to ending the TB on a coprocessor register write, * but allow this to be suppressed by the register definition * (usually only necessary to work around guest bugs). */ - if (!(ri->type & ARM_CP_SUPPRESS_TB_END)) { - gen_lookup_tb(s); - } + gen_lookup_tb(s); } + return 0; } -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 7+ messages in thread
* [Qemu-devel] [PATCH 3/4] target-arm: Implement the generic timer 2013-07-30 17:55 [Qemu-devel] [PATCH 0/4] target-arm: Implement support for generic timers Peter Maydell 2013-07-30 17:55 ` [Qemu-devel] [PATCH 1/4] target-arm: Allow raw_read() and raw_write() to handle 64 bit regs Peter Maydell 2013-07-30 17:55 ` [Qemu-devel] [PATCH 2/4] target-arm: Support coprocessor registers which do I/O Peter Maydell @ 2013-07-30 17:55 ` Peter Maydell 2013-08-01 15:50 ` Andreas Färber 2013-07-30 17:55 ` [Qemu-devel] [PATCH 4/4] hw/cpu/a15mpcore: Wire generic timer outputs to GIC inputs Peter Maydell 2013-08-01 9:39 ` [Qemu-devel] [PATCH 0/4] target-arm: Implement support for generic timers Laurent Desnogues 4 siblings, 1 reply; 7+ messages in thread From: Peter Maydell @ 2013-07-30 17:55 UTC (permalink / raw) To: qemu-devel; +Cc: patches The ARMv7 architecture specifies a 'generic timer' which is implemented via cp15 registers. Newer kernels will prefer to use this rather than a devboard-level timer. Implement the generic timer for TCG; for KVM we will already use the hardware's virtualized timer for this. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target-arm/cpu-qom.h | 9 ++ target-arm/cpu.c | 9 ++ target-arm/cpu.h | 18 ++++ target-arm/helper.c | 255 +++++++++++++++++++++++++++++++++++++++++++++++++- target-arm/machine.c | 8 +- 5 files changed, 291 insertions(+), 8 deletions(-) diff --git a/target-arm/cpu-qom.h b/target-arm/cpu-qom.h index cf36587..0b0b790 100644 --- a/target-arm/cpu-qom.h +++ b/target-arm/cpu-qom.h @@ -86,6 +86,11 @@ typedef struct ARMCPU { uint64_t *cpreg_vmstate_values; int32_t cpreg_vmstate_array_len; + /* Timers used by the generic (architected) timer */ + QEMUTimer *gt_timer[NUM_GTIMERS]; + /* GPIO outputs for generic timer */ + qemu_irq gt_timer_outputs[NUM_GTIMERS]; + /* The instance init functions for implementation-specific subclasses * set these fields to specify the implementation-dependent values of * various constant registers and reset values of non-constant @@ -152,4 +157,8 @@ hwaddr arm_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); int arm_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); int arm_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); +/* Callback functions for the generic timer's timers. */ +void gt_ptimer_cb(void *opaque); +void gt_vtimer_cb(void *opaque); + #endif diff --git a/target-arm/cpu.c b/target-arm/cpu.c index 87d35c6..3a10cf7 100644 --- a/target-arm/cpu.c +++ b/target-arm/cpu.c @@ -145,6 +145,15 @@ static void arm_cpu_initfn(Object *obj) cpu->cp_regs = g_hash_table_new_full(g_int_hash, g_int_equal, g_free, g_free); +#ifndef CONFIG_USER_ONLY + cpu->gt_timer[GTIMER_PHYS] = qemu_new_timer(vm_clock, GTIMER_SCALE, + gt_ptimer_cb, cpu); + cpu->gt_timer[GTIMER_VIRT] = qemu_new_timer(vm_clock, GTIMER_SCALE, + gt_vtimer_cb, cpu); + qdev_init_gpio_out(DEVICE(cpu), cpu->gt_timer_outputs, + ARRAY_SIZE(cpu->gt_timer_outputs)); +#endif + if (tcg_enabled() && !inited) { inited = true; arm_translate_init(); diff --git a/target-arm/cpu.h b/target-arm/cpu.h index 770a240..27715b5 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -76,6 +76,21 @@ struct arm_boot_info; s<2n+1> maps to the most significant half of d<n> */ +/* CPU state for each instance of a generic timer (in cp15 c14) */ +typedef struct ARMGenericTimer { + uint64_t cval; /* Timer CompareValue register */ + uint32_t ctl; /* Timer Control register */ +} ARMGenericTimer; + +#define GTIMER_PHYS 0 +#define GTIMER_VIRT 1 +#define NUM_GTIMERS 2 + +/* Scale factor for generic timers, ie number of ns per tick. + * This gives a 62.5MHz timer. + */ +#define GTIMER_SCALE 16 + typedef struct CPUARMState { /* Regs for current mode. */ uint32_t regs[16]; @@ -143,6 +158,9 @@ typedef struct CPUARMState { uint32_t c13_tls1; /* User RW Thread register. */ uint32_t c13_tls2; /* User RO Thread register. */ uint32_t c13_tls3; /* Privileged Thread register. */ + uint32_t c14_cntfrq; /* Counter Frequency register */ + uint32_t c14_cntkctl; /* Timer Control register */ + ARMGenericTimer c14_timer[NUM_GTIMERS]; uint32_t c15_cpar; /* XScale Coprocessor Access Register */ uint32_t c15_ticonfig; /* TI925T configuration byte. */ uint32_t c15_i_max; /* Maximum D-cache dirty line index. */ diff --git a/target-arm/helper.c b/target-arm/helper.c index fc5f757..9f1336a 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -695,15 +695,260 @@ static const ARMCPRegInfo v6k_cp_reginfo[] = { REGINFO_SENTINEL }; +#ifndef CONFIG_USER_ONLY + +static uint64_t gt_get_countervalue(CPUARMState *env) +{ + return qemu_get_clock_ns(vm_clock) / GTIMER_SCALE; +} + +static void gt_recalc_timer(ARMCPU *cpu, int timeridx) +{ + ARMGenericTimer *gt = &cpu->env.cp15.c14_timer[timeridx]; + + if (gt->ctl & 1) { + /* Timer enabled: calculate and set current ISTATUS, irq, and + * reset timer to when ISTATUS next has to change + */ + uint64_t count = gt_get_countervalue(&cpu->env); + /* Note that this must be unsigned 64 bit arithmetic: */ + int istatus = count >= gt->cval; + uint64_t nexttick; + + gt->ctl = deposit32(gt->ctl, 2, 1, istatus); + qemu_set_irq(cpu->gt_timer_outputs[timeridx], + (istatus && !(gt->ctl & 2))); + if (istatus) { + /* Next transition is when count rolls back over to zero */ + nexttick = UINT64_MAX; + } else { + /* Next transition is when we hit cval */ + nexttick = gt->cval; + } + /* Note that the desired next expiry time might be beyond the + * signed-64-bit range of a QEMUTimer -- in this case we just + * set the timer for as far in the future as possible. When the + * timer expires we will reset the timer for any remaining period. + */ + if (nexttick > INT64_MAX / GTIMER_SCALE) { + nexttick = INT64_MAX / GTIMER_SCALE; + } + qemu_mod_timer(cpu->gt_timer[timeridx], nexttick); + } else { + /* Timer disabled: ISTATUS and timer output always clear */ + gt->ctl &= ~4; + qemu_set_irq(cpu->gt_timer_outputs[timeridx], 0); + qemu_del_timer(cpu->gt_timer[timeridx]); + } +} + +static int gt_cntfrq_read(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t *value) +{ + /* Not visible from PL0 if both PL0PCTEN and PL0VCTEN are zero */ + if (arm_current_pl(env) == 0 && !extract32(env->cp15.c14_cntkctl, 0, 2)) { + return EXCP_UDEF; + } + *value = env->cp15.c14_cntfrq; + return 0; +} + +static void gt_cnt_reset(CPUARMState *env, const ARMCPRegInfo *ri) +{ + ARMCPU *cpu = arm_env_get_cpu(env); + int timeridx = ri->opc1 & 1; + + qemu_del_timer(cpu->gt_timer[timeridx]); +} + +static int gt_cnt_read(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t *value) +{ + int timeridx = ri->opc1 & 1; + + if (arm_current_pl(env) == 0 && + !extract32(env->cp15.c14_cntkctl, timeridx, 1)) { + return EXCP_UDEF; + } + *value = gt_get_countervalue(env); + return 0; +} + +static int gt_cval_read(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t *value) +{ + int timeridx = ri->opc1 & 1; + + if (arm_current_pl(env) == 0 && + !extract32(env->cp15.c14_cntkctl, 9 - timeridx, 1)) { + return EXCP_UDEF; + } + *value = env->cp15.c14_timer[timeridx].cval; + return 0; +} + +static int gt_cval_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + int timeridx = ri->opc1 & 1; + + env->cp15.c14_timer[timeridx].cval = value; + gt_recalc_timer(arm_env_get_cpu(env), timeridx); + return 0; +} +static int gt_tval_read(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t *value) +{ + int timeridx = ri->crm & 1; + + if (arm_current_pl(env) == 0 && + !extract32(env->cp15.c14_cntkctl, 9 - timeridx, 1)) { + return EXCP_UDEF; + } + *value = (uint32_t)(env->cp15.c14_timer[timeridx].cval - + gt_get_countervalue(env)); + return 0; +} + +static int gt_tval_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + int timeridx = ri->crm & 1; + + env->cp15.c14_timer[timeridx].cval = gt_get_countervalue(env) + + + sextract64(value, 0, 32); + gt_recalc_timer(arm_env_get_cpu(env), timeridx); + return 0; +} + +static int gt_ctl_read(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t *value) +{ + int timeridx = ri->crm & 1; + + if (arm_current_pl(env) == 0 && + !extract32(env->cp15.c14_cntkctl, 9 - timeridx, 1)) { + return EXCP_UDEF; + } + *value = env->cp15.c14_timer[timeridx].ctl; + return 0; +} + +static int gt_ctl_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + ARMCPU *cpu = arm_env_get_cpu(env); + int timeridx = ri->crm & 1; + uint32_t oldval = env->cp15.c14_timer[timeridx].ctl; + + env->cp15.c14_timer[timeridx].ctl = value & 3; + if ((oldval ^ value) & 1) { + /* Enable toggled */ + gt_recalc_timer(cpu, timeridx); + } else if ((oldval & value) & 2) { + /* IMASK toggled: don't need to recalculate, + * just set the interrupt line based on ISTATUS + */ + qemu_set_irq(cpu->gt_timer_outputs[timeridx], + (oldval & 4) && (value & 2)); + } + return 0; +} + +void gt_ptimer_cb(void *opaque) +{ + ARMCPU *cpu = opaque; + + gt_recalc_timer(cpu, GTIMER_PHYS); +} +void gt_vtimer_cb(void *opaque) +{ + ARMCPU *cpu = opaque; + + gt_recalc_timer(cpu, GTIMER_VIRT); +} + static const ARMCPRegInfo generic_timer_cp_reginfo[] = { - /* Dummy implementation: RAZ/WI the whole crn=14 space */ - { .name = "GENERIC_TIMER", .cp = 15, .crn = 14, - .crm = CP_ANY, .opc1 = CP_ANY, .opc2 = CP_ANY, - .access = PL1_RW, .type = ARM_CP_CONST | ARM_CP_NO_MIGRATE, - .resetvalue = 0 }, + /* Note that CNTFRQ is purely reads-as-written for the benefit + * of software; writing it doesn't actually change the timer frequency. + * Our reset value matches the fixed frequency we implement the timer at. + */ + { .name = "CNTFRQ", .cp = 15, .crn = 14, .crm = 0, .opc1 = 0, .opc2 = 0, + .access = PL1_RW | PL0_R, + .fieldoffset = offsetof(CPUARMState, cp15.c14_cntfrq), + .resetvalue = (1000 * 1000 * 1000) / GTIMER_SCALE, + .readfn = gt_cntfrq_read, .raw_readfn = raw_read, + }, + /* overall control: mostly access permissions */ + { .name = "CNTKCTL", .cp = 15, .crn = 14, .crm = 1, .opc1 = 0, .opc2 = 0, + .access = PL1_RW, + .fieldoffset = offsetof(CPUARMState, cp15.c14_cntkctl), + .resetvalue = 0, + }, + /* per-timer control */ + { .name = "CNTP_CTL", .cp = 15, .crn = 14, .crm = 2, .opc1 = 0, .opc2 = 1, + .type = ARM_CP_IO, .access = PL1_RW | PL0_R, + .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHYS].ctl), + .resetvalue = 0, + .readfn = gt_ctl_read, .writefn = gt_ctl_write, + .raw_readfn = raw_read, .raw_writefn = raw_write, + }, + { .name = "CNTV_CTL", .cp = 15, .crn = 14, .crm = 3, .opc1 = 0, .opc2 = 1, + .type = ARM_CP_IO, .access = PL1_RW | PL0_R, + .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIRT].ctl), + .resetvalue = 0, + .readfn = gt_ctl_read, .writefn = gt_ctl_write, + .raw_readfn = raw_read, .raw_writefn = raw_write, + }, + /* TimerValue views: a 32 bit downcounting view of the underlying state */ + { .name = "CNTP_TVAL", .cp = 15, .crn = 14, .crm = 2, .opc1 = 0, .opc2 = 0, + .type = ARM_CP_NO_MIGRATE | ARM_CP_IO, .access = PL1_RW | PL0_R, + .readfn = gt_tval_read, .writefn = gt_tval_write, + }, + { .name = "CNTV_TVAL", .cp = 15, .crn = 14, .crm = 3, .opc1 = 0, .opc2 = 0, + .type = ARM_CP_NO_MIGRATE | ARM_CP_IO, .access = PL1_RW | PL0_R, + .readfn = gt_tval_read, .writefn = gt_tval_write, + }, + /* The counter itself */ + { .name = "CNTPCT", .cp = 15, .crm = 14, .opc1 = 0, + .access = PL0_R, .type = ARM_CP_64BIT | ARM_CP_NO_MIGRATE | ARM_CP_IO, + .readfn = gt_cnt_read, .resetfn = gt_cnt_reset, + }, + { .name = "CNTVCT", .cp = 15, .crm = 14, .opc1 = 1, + .access = PL0_R, .type = ARM_CP_64BIT | ARM_CP_NO_MIGRATE | ARM_CP_IO, + .readfn = gt_cnt_read, .resetfn = gt_cnt_reset, + }, + /* Comparison value, indicating when the timer goes off */ + { .name = "CNTP_CVAL", .cp = 15, .crm = 14, .opc1 = 2, + .access = PL1_RW | PL0_R, + .type = ARM_CP_64BIT | ARM_CP_IO, + .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHYS].cval), + .resetvalue = 0, + .readfn = gt_cval_read, .writefn = gt_cval_write, + .raw_readfn = raw_read, .raw_writefn = raw_write, + }, + { .name = "CNTV_CVAL", .cp = 15, .crm = 14, .opc1 = 3, + .access = PL1_RW | PL0_R, + .type = ARM_CP_64BIT | ARM_CP_IO, + .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIRT].cval), + .resetvalue = 0, + .readfn = gt_cval_read, .writefn = gt_cval_write, + .raw_readfn = raw_read, .raw_writefn = raw_write, + }, REGINFO_SENTINEL }; +#else +/* In user-mode none of the generic timer registers are accessible, + * and their implementation depends on vm_clock and qdev gpio outputs, + * so instead just don't register any of them. + */ +static const ARMCPRegInfo generic_timer_cp_reginfo[] = { + REGINFO_SENTINEL +}; + +#endif + static int par_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { if (arm_feature(env, ARM_FEATURE_LPAE)) { diff --git a/target-arm/machine.c b/target-arm/machine.c index 6d4c2d4..5b6f375 100644 --- a/target-arm/machine.c +++ b/target-arm/machine.c @@ -222,9 +222,9 @@ static int cpu_post_load(void *opaque, int version_id) const VMStateDescription vmstate_arm_cpu = { .name = "cpu", - .version_id = 12, - .minimum_version_id = 12, - .minimum_version_id_old = 12, + .version_id = 13, + .minimum_version_id = 13, + .minimum_version_id_old = 13, .pre_save = cpu_pre_save, .post_load = cpu_post_load, .fields = (VMStateField[]) { @@ -257,6 +257,8 @@ const VMStateDescription vmstate_arm_cpu = { VMSTATE_UINT32(env.exclusive_val, ARMCPU), VMSTATE_UINT32(env.exclusive_high, ARMCPU), VMSTATE_UINT64(env.features, ARMCPU), + VMSTATE_TIMER(gt_timer[GTIMER_PHYS], ARMCPU), + VMSTATE_TIMER(gt_timer[GTIMER_VIRT], ARMCPU), VMSTATE_END_OF_LIST() }, .subsections = (VMStateSubsection[]) { -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [Qemu-devel] [PATCH 3/4] target-arm: Implement the generic timer 2013-07-30 17:55 ` [Qemu-devel] [PATCH 3/4] target-arm: Implement the generic timer Peter Maydell @ 2013-08-01 15:50 ` Andreas Färber 0 siblings, 0 replies; 7+ messages in thread From: Andreas Färber @ 2013-08-01 15:50 UTC (permalink / raw) To: Peter Maydell; +Cc: qemu-devel, patches Am 30.07.2013 19:55, schrieb Peter Maydell: > The ARMv7 architecture specifies a 'generic timer' which is implemented > via cp15 registers. Newer kernels will prefer to use this rather than > a devboard-level timer. Implement the generic timer for TCG; for KVM > we will already use the hardware's virtualized timer for this. > > Signed-off-by: Peter Maydell <peter.maydell@linaro.org> > --- > target-arm/cpu-qom.h | 9 ++ > target-arm/cpu.c | 9 ++ > target-arm/cpu.h | 18 ++++ > target-arm/helper.c | 255 +++++++++++++++++++++++++++++++++++++++++++++++++- > target-arm/machine.c | 8 +- > 5 files changed, 291 insertions(+), 8 deletions(-) > > diff --git a/target-arm/cpu-qom.h b/target-arm/cpu-qom.h > index cf36587..0b0b790 100644 > --- a/target-arm/cpu-qom.h > +++ b/target-arm/cpu-qom.h > @@ -86,6 +86,11 @@ typedef struct ARMCPU { > uint64_t *cpreg_vmstate_values; > int32_t cpreg_vmstate_array_len; > > + /* Timers used by the generic (architected) timer */ > + QEMUTimer *gt_timer[NUM_GTIMERS]; > + /* GPIO outputs for generic timer */ > + qemu_irq gt_timer_outputs[NUM_GTIMERS]; > + > /* The instance init functions for implementation-specific subclasses > * set these fields to specify the implementation-dependent values of > * various constant registers and reset values of non-constant > @@ -152,4 +157,8 @@ hwaddr arm_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); > int arm_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); > int arm_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); > > +/* Callback functions for the generic timer's timers. */ > +void gt_ptimer_cb(void *opaque); > +void gt_vtimer_cb(void *opaque); Please use an arm_ prefix for globally visible symbols. One minor nit below although I didn't review in detail. Andreas > + > #endif > diff --git a/target-arm/cpu.c b/target-arm/cpu.c > index 87d35c6..3a10cf7 100644 > --- a/target-arm/cpu.c > +++ b/target-arm/cpu.c > @@ -145,6 +145,15 @@ static void arm_cpu_initfn(Object *obj) > cpu->cp_regs = g_hash_table_new_full(g_int_hash, g_int_equal, > g_free, g_free); > > +#ifndef CONFIG_USER_ONLY > + cpu->gt_timer[GTIMER_PHYS] = qemu_new_timer(vm_clock, GTIMER_SCALE, > + gt_ptimer_cb, cpu); > + cpu->gt_timer[GTIMER_VIRT] = qemu_new_timer(vm_clock, GTIMER_SCALE, > + gt_vtimer_cb, cpu); > + qdev_init_gpio_out(DEVICE(cpu), cpu->gt_timer_outputs, > + ARRAY_SIZE(cpu->gt_timer_outputs)); > +#endif > + > if (tcg_enabled() && !inited) { > inited = true; > arm_translate_init(); > diff --git a/target-arm/cpu.h b/target-arm/cpu.h > index 770a240..27715b5 100644 > --- a/target-arm/cpu.h > +++ b/target-arm/cpu.h > @@ -76,6 +76,21 @@ struct arm_boot_info; > s<2n+1> maps to the most significant half of d<n> > */ > > +/* CPU state for each instance of a generic timer (in cp15 c14) */ > +typedef struct ARMGenericTimer { > + uint64_t cval; /* Timer CompareValue register */ > + uint32_t ctl; /* Timer Control register */ > +} ARMGenericTimer; > + > +#define GTIMER_PHYS 0 > +#define GTIMER_VIRT 1 > +#define NUM_GTIMERS 2 > + > +/* Scale factor for generic timers, ie number of ns per tick. > + * This gives a 62.5MHz timer. > + */ > +#define GTIMER_SCALE 16 > + > typedef struct CPUARMState { > /* Regs for current mode. */ > uint32_t regs[16]; > @@ -143,6 +158,9 @@ typedef struct CPUARMState { > uint32_t c13_tls1; /* User RW Thread register. */ > uint32_t c13_tls2; /* User RO Thread register. */ > uint32_t c13_tls3; /* Privileged Thread register. */ > + uint32_t c14_cntfrq; /* Counter Frequency register */ > + uint32_t c14_cntkctl; /* Timer Control register */ > + ARMGenericTimer c14_timer[NUM_GTIMERS]; > uint32_t c15_cpar; /* XScale Coprocessor Access Register */ > uint32_t c15_ticonfig; /* TI925T configuration byte. */ > uint32_t c15_i_max; /* Maximum D-cache dirty line index. */ > diff --git a/target-arm/helper.c b/target-arm/helper.c > index fc5f757..9f1336a 100644 > --- a/target-arm/helper.c > +++ b/target-arm/helper.c > @@ -695,15 +695,260 @@ static const ARMCPRegInfo v6k_cp_reginfo[] = { > REGINFO_SENTINEL > }; > > +#ifndef CONFIG_USER_ONLY > + > +static uint64_t gt_get_countervalue(CPUARMState *env) > +{ > + return qemu_get_clock_ns(vm_clock) / GTIMER_SCALE; > +} > + > +static void gt_recalc_timer(ARMCPU *cpu, int timeridx) > +{ > + ARMGenericTimer *gt = &cpu->env.cp15.c14_timer[timeridx]; > + > + if (gt->ctl & 1) { > + /* Timer enabled: calculate and set current ISTATUS, irq, and > + * reset timer to when ISTATUS next has to change > + */ > + uint64_t count = gt_get_countervalue(&cpu->env); > + /* Note that this must be unsigned 64 bit arithmetic: */ > + int istatus = count >= gt->cval; > + uint64_t nexttick; > + > + gt->ctl = deposit32(gt->ctl, 2, 1, istatus); > + qemu_set_irq(cpu->gt_timer_outputs[timeridx], > + (istatus && !(gt->ctl & 2))); > + if (istatus) { > + /* Next transition is when count rolls back over to zero */ > + nexttick = UINT64_MAX; > + } else { > + /* Next transition is when we hit cval */ > + nexttick = gt->cval; > + } > + /* Note that the desired next expiry time might be beyond the > + * signed-64-bit range of a QEMUTimer -- in this case we just > + * set the timer for as far in the future as possible. When the > + * timer expires we will reset the timer for any remaining period. > + */ > + if (nexttick > INT64_MAX / GTIMER_SCALE) { > + nexttick = INT64_MAX / GTIMER_SCALE; > + } > + qemu_mod_timer(cpu->gt_timer[timeridx], nexttick); > + } else { > + /* Timer disabled: ISTATUS and timer output always clear */ > + gt->ctl &= ~4; > + qemu_set_irq(cpu->gt_timer_outputs[timeridx], 0); > + qemu_del_timer(cpu->gt_timer[timeridx]); > + } > +} > + > +static int gt_cntfrq_read(CPUARMState *env, const ARMCPRegInfo *ri, > + uint64_t *value) > +{ > + /* Not visible from PL0 if both PL0PCTEN and PL0VCTEN are zero */ > + if (arm_current_pl(env) == 0 && !extract32(env->cp15.c14_cntkctl, 0, 2)) { > + return EXCP_UDEF; > + } > + *value = env->cp15.c14_cntfrq; > + return 0; > +} > + > +static void gt_cnt_reset(CPUARMState *env, const ARMCPRegInfo *ri) > +{ > + ARMCPU *cpu = arm_env_get_cpu(env); > + int timeridx = ri->opc1 & 1; > + > + qemu_del_timer(cpu->gt_timer[timeridx]); > +} > + > +static int gt_cnt_read(CPUARMState *env, const ARMCPRegInfo *ri, > + uint64_t *value) > +{ > + int timeridx = ri->opc1 & 1; > + > + if (arm_current_pl(env) == 0 && > + !extract32(env->cp15.c14_cntkctl, timeridx, 1)) { > + return EXCP_UDEF; > + } > + *value = gt_get_countervalue(env); > + return 0; > +} > + > +static int gt_cval_read(CPUARMState *env, const ARMCPRegInfo *ri, > + uint64_t *value) > +{ > + int timeridx = ri->opc1 & 1; > + > + if (arm_current_pl(env) == 0 && > + !extract32(env->cp15.c14_cntkctl, 9 - timeridx, 1)) { > + return EXCP_UDEF; > + } > + *value = env->cp15.c14_timer[timeridx].cval; > + return 0; > +} > + > +static int gt_cval_write(CPUARMState *env, const ARMCPRegInfo *ri, > + uint64_t value) > +{ > + int timeridx = ri->opc1 & 1; > + > + env->cp15.c14_timer[timeridx].cval = value; > + gt_recalc_timer(arm_env_get_cpu(env), timeridx); > + return 0; > +} > +static int gt_tval_read(CPUARMState *env, const ARMCPRegInfo *ri, > + uint64_t *value) > +{ > + int timeridx = ri->crm & 1; > + > + if (arm_current_pl(env) == 0 && > + !extract32(env->cp15.c14_cntkctl, 9 - timeridx, 1)) { > + return EXCP_UDEF; > + } > + *value = (uint32_t)(env->cp15.c14_timer[timeridx].cval - > + gt_get_countervalue(env)); > + return 0; > +} > + > +static int gt_tval_write(CPUARMState *env, const ARMCPRegInfo *ri, > + uint64_t value) > +{ > + int timeridx = ri->crm & 1; > + > + env->cp15.c14_timer[timeridx].cval = gt_get_countervalue(env) + > + + sextract64(value, 0, 32); > + gt_recalc_timer(arm_env_get_cpu(env), timeridx); > + return 0; > +} > + > +static int gt_ctl_read(CPUARMState *env, const ARMCPRegInfo *ri, > + uint64_t *value) > +{ > + int timeridx = ri->crm & 1; > + > + if (arm_current_pl(env) == 0 && > + !extract32(env->cp15.c14_cntkctl, 9 - timeridx, 1)) { > + return EXCP_UDEF; > + } > + *value = env->cp15.c14_timer[timeridx].ctl; > + return 0; > +} > + > +static int gt_ctl_write(CPUARMState *env, const ARMCPRegInfo *ri, > + uint64_t value) > +{ > + ARMCPU *cpu = arm_env_get_cpu(env); > + int timeridx = ri->crm & 1; > + uint32_t oldval = env->cp15.c14_timer[timeridx].ctl; > + > + env->cp15.c14_timer[timeridx].ctl = value & 3; > + if ((oldval ^ value) & 1) { > + /* Enable toggled */ > + gt_recalc_timer(cpu, timeridx); > + } else if ((oldval & value) & 2) { > + /* IMASK toggled: don't need to recalculate, > + * just set the interrupt line based on ISTATUS > + */ > + qemu_set_irq(cpu->gt_timer_outputs[timeridx], > + (oldval & 4) && (value & 2)); > + } > + return 0; > +} > + > +void gt_ptimer_cb(void *opaque) > +{ > + ARMCPU *cpu = opaque; > + > + gt_recalc_timer(cpu, GTIMER_PHYS); > +} White line missing. > +void gt_vtimer_cb(void *opaque) > +{ > + ARMCPU *cpu = opaque; > + > + gt_recalc_timer(cpu, GTIMER_VIRT); > +} > + > static const ARMCPRegInfo generic_timer_cp_reginfo[] = { > - /* Dummy implementation: RAZ/WI the whole crn=14 space */ > - { .name = "GENERIC_TIMER", .cp = 15, .crn = 14, > - .crm = CP_ANY, .opc1 = CP_ANY, .opc2 = CP_ANY, > - .access = PL1_RW, .type = ARM_CP_CONST | ARM_CP_NO_MIGRATE, > - .resetvalue = 0 }, > + /* Note that CNTFRQ is purely reads-as-written for the benefit > + * of software; writing it doesn't actually change the timer frequency. > + * Our reset value matches the fixed frequency we implement the timer at. > + */ > + { .name = "CNTFRQ", .cp = 15, .crn = 14, .crm = 0, .opc1 = 0, .opc2 = 0, > + .access = PL1_RW | PL0_R, > + .fieldoffset = offsetof(CPUARMState, cp15.c14_cntfrq), > + .resetvalue = (1000 * 1000 * 1000) / GTIMER_SCALE, > + .readfn = gt_cntfrq_read, .raw_readfn = raw_read, > + }, > + /* overall control: mostly access permissions */ > + { .name = "CNTKCTL", .cp = 15, .crn = 14, .crm = 1, .opc1 = 0, .opc2 = 0, > + .access = PL1_RW, > + .fieldoffset = offsetof(CPUARMState, cp15.c14_cntkctl), > + .resetvalue = 0, > + }, > + /* per-timer control */ > + { .name = "CNTP_CTL", .cp = 15, .crn = 14, .crm = 2, .opc1 = 0, .opc2 = 1, > + .type = ARM_CP_IO, .access = PL1_RW | PL0_R, > + .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHYS].ctl), > + .resetvalue = 0, > + .readfn = gt_ctl_read, .writefn = gt_ctl_write, > + .raw_readfn = raw_read, .raw_writefn = raw_write, > + }, > + { .name = "CNTV_CTL", .cp = 15, .crn = 14, .crm = 3, .opc1 = 0, .opc2 = 1, > + .type = ARM_CP_IO, .access = PL1_RW | PL0_R, > + .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIRT].ctl), > + .resetvalue = 0, > + .readfn = gt_ctl_read, .writefn = gt_ctl_write, > + .raw_readfn = raw_read, .raw_writefn = raw_write, > + }, > + /* TimerValue views: a 32 bit downcounting view of the underlying state */ > + { .name = "CNTP_TVAL", .cp = 15, .crn = 14, .crm = 2, .opc1 = 0, .opc2 = 0, > + .type = ARM_CP_NO_MIGRATE | ARM_CP_IO, .access = PL1_RW | PL0_R, > + .readfn = gt_tval_read, .writefn = gt_tval_write, > + }, > + { .name = "CNTV_TVAL", .cp = 15, .crn = 14, .crm = 3, .opc1 = 0, .opc2 = 0, > + .type = ARM_CP_NO_MIGRATE | ARM_CP_IO, .access = PL1_RW | PL0_R, > + .readfn = gt_tval_read, .writefn = gt_tval_write, > + }, > + /* The counter itself */ > + { .name = "CNTPCT", .cp = 15, .crm = 14, .opc1 = 0, > + .access = PL0_R, .type = ARM_CP_64BIT | ARM_CP_NO_MIGRATE | ARM_CP_IO, > + .readfn = gt_cnt_read, .resetfn = gt_cnt_reset, > + }, > + { .name = "CNTVCT", .cp = 15, .crm = 14, .opc1 = 1, > + .access = PL0_R, .type = ARM_CP_64BIT | ARM_CP_NO_MIGRATE | ARM_CP_IO, > + .readfn = gt_cnt_read, .resetfn = gt_cnt_reset, > + }, > + /* Comparison value, indicating when the timer goes off */ > + { .name = "CNTP_CVAL", .cp = 15, .crm = 14, .opc1 = 2, > + .access = PL1_RW | PL0_R, > + .type = ARM_CP_64BIT | ARM_CP_IO, > + .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHYS].cval), > + .resetvalue = 0, > + .readfn = gt_cval_read, .writefn = gt_cval_write, > + .raw_readfn = raw_read, .raw_writefn = raw_write, > + }, > + { .name = "CNTV_CVAL", .cp = 15, .crm = 14, .opc1 = 3, > + .access = PL1_RW | PL0_R, > + .type = ARM_CP_64BIT | ARM_CP_IO, > + .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIRT].cval), > + .resetvalue = 0, > + .readfn = gt_cval_read, .writefn = gt_cval_write, > + .raw_readfn = raw_read, .raw_writefn = raw_write, > + }, > REGINFO_SENTINEL > }; > > +#else > +/* In user-mode none of the generic timer registers are accessible, > + * and their implementation depends on vm_clock and qdev gpio outputs, > + * so instead just don't register any of them. > + */ > +static const ARMCPRegInfo generic_timer_cp_reginfo[] = { > + REGINFO_SENTINEL > +}; > + > +#endif > + > static int par_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) > { > if (arm_feature(env, ARM_FEATURE_LPAE)) { > diff --git a/target-arm/machine.c b/target-arm/machine.c > index 6d4c2d4..5b6f375 100644 > --- a/target-arm/machine.c > +++ b/target-arm/machine.c > @@ -222,9 +222,9 @@ static int cpu_post_load(void *opaque, int version_id) > > const VMStateDescription vmstate_arm_cpu = { > .name = "cpu", > - .version_id = 12, > - .minimum_version_id = 12, > - .minimum_version_id_old = 12, > + .version_id = 13, > + .minimum_version_id = 13, > + .minimum_version_id_old = 13, > .pre_save = cpu_pre_save, > .post_load = cpu_post_load, > .fields = (VMStateField[]) { > @@ -257,6 +257,8 @@ const VMStateDescription vmstate_arm_cpu = { > VMSTATE_UINT32(env.exclusive_val, ARMCPU), > VMSTATE_UINT32(env.exclusive_high, ARMCPU), > VMSTATE_UINT64(env.features, ARMCPU), > + VMSTATE_TIMER(gt_timer[GTIMER_PHYS], ARMCPU), > + VMSTATE_TIMER(gt_timer[GTIMER_VIRT], ARMCPU), > VMSTATE_END_OF_LIST() > }, > .subsections = (VMStateSubsection[]) { > -- SUSE LINUX Products GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany GF: Jeff Hawn, Jennifer Guild, Felix Imendörffer; HRB 16746 AG Nürnberg ^ permalink raw reply [flat|nested] 7+ messages in thread
* [Qemu-devel] [PATCH 4/4] hw/cpu/a15mpcore: Wire generic timer outputs to GIC inputs 2013-07-30 17:55 [Qemu-devel] [PATCH 0/4] target-arm: Implement support for generic timers Peter Maydell ` (2 preceding siblings ...) 2013-07-30 17:55 ` [Qemu-devel] [PATCH 3/4] target-arm: Implement the generic timer Peter Maydell @ 2013-07-30 17:55 ` Peter Maydell 2013-08-01 9:39 ` [Qemu-devel] [PATCH 0/4] target-arm: Implement support for generic timers Laurent Desnogues 4 siblings, 0 replies; 7+ messages in thread From: Peter Maydell @ 2013-07-30 17:55 UTC (permalink / raw) To: qemu-devel; +Cc: patches Now our A15 CPU implements the generic timers, we can wire them up to the appropriate inputs on the GIC. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/cpu/a15mpcore.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/hw/cpu/a15mpcore.c b/hw/cpu/a15mpcore.c index c736257..8c23020 100644 --- a/hw/cpu/a15mpcore.c +++ b/hw/cpu/a15mpcore.c @@ -42,6 +42,8 @@ static int a15mp_priv_init(SysBusDevice *dev) A15MPPrivState *s = FROM_SYSBUS(A15MPPrivState, dev); SysBusDevice *busdev; const char *gictype = "arm_gic"; + int i; + CPUState *cpu; if (kvm_irqchip_in_kernel()) { gictype = "kvm-arm-gic"; @@ -60,6 +62,22 @@ static int a15mp_priv_init(SysBusDevice *dev) /* Pass through inbound GPIO lines to the GIC */ qdev_init_gpio_in(&s->busdev.qdev, a15mp_priv_set_irq, s->num_irq - 32); + /* Wire the outputs from each CPU's generic timer to the + * appropriate GIC PPI inputs + */ + for (i = 0, cpu = first_cpu; i < s->num_cpu; i++, cpu = cpu->next_cpu) { + DeviceState *cpudev = DEVICE(cpu); + int ppibase = s->num_irq - 32 + i * 32; + /* physical timer; we wire it up to the non-secure timer's ID, + * since a real A15 always has TrustZone but QEMU doesn't. + */ + qdev_connect_gpio_out(cpudev, 0, + qdev_get_gpio_in(s->gic, ppibase + 30)); + /* virtual timer */ + qdev_connect_gpio_out(cpudev, 1, + qdev_get_gpio_in(s->gic, ppibase + 27)); + } + /* Memory map (addresses are offsets from PERIPHBASE): * 0x0000-0x0fff -- reserved * 0x1000-0x1fff -- GIC Distributor -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [Qemu-devel] [PATCH 0/4] target-arm: Implement support for generic timers 2013-07-30 17:55 [Qemu-devel] [PATCH 0/4] target-arm: Implement support for generic timers Peter Maydell ` (3 preceding siblings ...) 2013-07-30 17:55 ` [Qemu-devel] [PATCH 4/4] hw/cpu/a15mpcore: Wire generic timer outputs to GIC inputs Peter Maydell @ 2013-08-01 9:39 ` Laurent Desnogues 4 siblings, 0 replies; 7+ messages in thread From: Laurent Desnogues @ 2013-08-01 9:39 UTC (permalink / raw) To: Peter Maydell; +Cc: qemu-devel@nongnu.org, patches Hi, On Tue, Jul 30, 2013 at 7:55 PM, Peter Maydell <peter.maydell@linaro.org> wrote: > This patch series implements support for the 'generic timers', > which are a set of timers defined in the ARM Architecture Reference > Manual and implemented by the Cortex-A15. We've got away without > these up til now because Linux will generally fall back on whatever > random timer is present on the devboard if it can't find the > on-CPU timers, but this is less than ideal. (Among other things, > the proposed mach-virt should just use the generic timers so it > doesn't have to provide an sp804 timer.) > > Peter Maydell (4): > target-arm: Allow raw_read() and raw_write() to handle 64 bit regs > target-arm: Support coprocessor registers which do I/O > target-arm: Implement the generic timer > hw/cpu/a15mpcore: Wire generic timer outputs to GIC inputs > > hw/cpu/a15mpcore.c | 18 ++++ > target-arm/cpu-qom.h | 9 ++ > target-arm/cpu.c | 9 ++ > target-arm/cpu.h | 24 ++++- > target-arm/helper.c | 267 ++++++++++++++++++++++++++++++++++++++++++++++-- > target-arm/machine.c | 8 +- > target-arm/translate.c | 16 ++- > 7 files changed, 337 insertions(+), 14 deletions(-) I tested this patch series with a 3.9 kernel for Vexpress with Cortex-A15. It works fine with the generic timer correctly detected and used. Note the 4th patch doesn't apply as is any more. Tested-by: Laurent Desnogues <laurent.desnogues@gmail.com> Thanks, Laurent ^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2013-08-02 4:57 UTC | newest] Thread overview: 7+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2013-07-30 17:55 [Qemu-devel] [PATCH 0/4] target-arm: Implement support for generic timers Peter Maydell 2013-07-30 17:55 ` [Qemu-devel] [PATCH 1/4] target-arm: Allow raw_read() and raw_write() to handle 64 bit regs Peter Maydell 2013-07-30 17:55 ` [Qemu-devel] [PATCH 2/4] target-arm: Support coprocessor registers which do I/O Peter Maydell 2013-07-30 17:55 ` [Qemu-devel] [PATCH 3/4] target-arm: Implement the generic timer Peter Maydell 2013-08-01 15:50 ` Andreas Färber 2013-07-30 17:55 ` [Qemu-devel] [PATCH 4/4] hw/cpu/a15mpcore: Wire generic timer outputs to GIC inputs Peter Maydell 2013-08-01 9:39 ` [Qemu-devel] [PATCH 0/4] target-arm: Implement support for generic timers Laurent Desnogues
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).