target-arm: Implement the generic timer

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>
Tested-by: Laurent Desnogues <laurent.desnogues@gmail.com>
Message-id: 1376065080-26661-4-git-send-email-peter.maydell@linaro.org
This commit is contained in:
Peter Maydell 2013-08-20 14:54:31 +01:00
parent 2452731c88
commit 55d284af8e
5 changed files with 290 additions and 8 deletions

View file

@ -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 arm_gt_ptimer_cb(void *opaque);
void arm_gt_vtimer_cb(void *opaque);
#endif

View file

@ -203,6 +203,13 @@ static void arm_cpu_initfn(Object *obj)
} else {
qdev_init_gpio_in(DEVICE(cpu), arm_cpu_set_irq, 2);
}
cpu->gt_timer[GTIMER_PHYS] = qemu_new_timer(vm_clock, GTIMER_SCALE,
arm_gt_ptimer_cb, cpu);
cpu->gt_timer[GTIMER_VIRT] = qemu_new_timer(vm_clock, GTIMER_SCALE,
arm_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) {

View file

@ -79,6 +79,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];
@ -146,6 +161,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. */

View file

@ -695,15 +695,261 @@ 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 arm_gt_ptimer_cb(void *opaque)
{
ARMCPU *cpu = opaque;
gt_recalc_timer(cpu, GTIMER_PHYS);
}
void arm_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)) {

View file

@ -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[]) {