From: "Andreas Färber" <afaerber@suse.de>
To: Peter Maydell <peter.maydell@linaro.org>
Cc: qemu-devel@nongnu.org, patches@linaro.org
Subject: Re: [Qemu-devel] [PATCH 3/4] target-arm: Implement the generic timer
Date: Thu, 01 Aug 2013 17:50:41 +0200 [thread overview]
Message-ID: <51FA83D1.1030308@suse.de> (raw)
In-Reply-To: <1375206933-15053-4-git-send-email-peter.maydell@linaro.org>
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
next prev parent reply other threads:[~2013-08-02 4:57 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
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 [this message]
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
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=51FA83D1.1030308@suse.de \
--to=afaerber@suse.de \
--cc=patches@linaro.org \
--cc=peter.maydell@linaro.org \
--cc=qemu-devel@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.