qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [PATCH] Basic support for ARM A15 "architectured" (cp15


From: Blue Swirl
Subject: Re: [Qemu-devel] [PATCH] Basic support for ARM A15 "architectured" (cp15) timers
Date: Fri, 14 Sep 2012 17:26:03 +0000

On Wed, Sep 12, 2012 at 11:49 AM, Daniel Forsgren
<address@hidden> wrote:
> This patch adds basic support for the "architected" timers (i.e. cp15)
> found in A15. It's enough to allow Linux to boot, using arch_timer for
> the tick. However - it is not a complete model of the timer block at
> large, it is not that well structured, and it is currently tested with
> qemu-linaro-1.1.50-2012.07 (not latest and greatest). It's simply a
> prototype.
>
> However, if anyone wants to play with the architectured (cp15) timers
> instead of sp804, then please feel free to try it out. It has been
> tested with linux-linaro-3.6-rc2-2012.08, and you can easily verify
> the existence of these timers under /proc/interrupts:
>
>     address@hidden:~# cat /proc/interrupts
>     cat /proc/interrupts
>                CPU0
>      29:       7424       GIC  arch_timer
>      30:          0       GIC  arch_timer
>
> Please note that this also requires some minor fixes that are not part
> of qemu-linaro-1.1.50-2012.07:
>
>     http://patches.linaro.org/9833/
>
> Signed-off-by: Daniel Forsgren <address@hidden>
>
> ---
>
> diff -Nupr qemu-linaro-1.1.50-2012.07/hw/a15mpcore.c 
> qemu-linaro-1.1.50-2012.07-modified/hw/a15mpcore.c
> --- qemu-linaro-1.1.50-2012.07/hw/a15mpcore.c   2012-07-05 16:48:28.000000000 
> +0200
> +++ qemu-linaro-1.1.50-2012.07-modified/hw/a15mpcore.c  2012-09-12 
> 11:24:25.844237405 +0200
> @@ -28,6 +28,7 @@ typedef struct A15MPPrivState {
>      uint32_t num_cpu;
>      uint32_t num_irq;
>      MemoryRegion container;
> +    DeviceState *archtimer;
>      DeviceState *gic;
>  } A15MPPrivState;
>
> @@ -40,7 +41,8 @@ static void a15mp_priv_set_irq(void *opa
>  static int a15mp_priv_init(SysBusDevice *dev)
>  {
>      A15MPPrivState *s = FROM_SYSBUS(A15MPPrivState, dev);
> -    SysBusDevice *busdev;
> +    SysBusDevice *busdev, *timerbusdev;
> +    int i;
>
>      if (kvm_irqchip_in_kernel()) {
>          s->gic = qdev_create(NULL, "kvm-arm_gic");
> @@ -60,6 +62,11 @@ static int a15mp_priv_init(SysBusDevice
>      /* Pass through inbound GPIO lines to the GIC */
>      qdev_init_gpio_in(&s->busdev.qdev, a15mp_priv_set_irq, s->num_irq - 32);
>
> +    s->archtimer = qdev_create(NULL, "arm_archtimer");
> +    //    qdev_prop_set_uint32(s->archtimer, "num-cpu", s->num_cpu);

Please don't introduce dead code.

> +    qdev_init_nofail(s->archtimer);
> +    timerbusdev = sysbus_from_qdev(s->archtimer);
> +
>      /* Memory map (addresses are offsets from PERIPHBASE):
>       *  0x0000-0x0fff -- reserved
>       *  0x1000-0x1fff -- GIC Distributor
> @@ -75,6 +82,16 @@ static int a15mp_priv_init(SysBusDevice
>                                  sysbus_mmio_get_region(busdev, 1));
>
>      sysbus_init_mmio(dev, &s->container);
> +
> +
> +    for (i = 0; i < s->num_cpu; i++) {
> +        int ppibase = (s->num_irq - 32) + i * 32;
> +        sysbus_connect_irq(timerbusdev, i * 2,
> +                           qdev_get_gpio_in(s->gic, ppibase + 29));
> +        sysbus_connect_irq(timerbusdev, i * 2 + 1,
> +                           qdev_get_gpio_in(s->gic, ppibase + 30));
> +    }
> +
>      return 0;
>  }
>
> diff -Nupr qemu-linaro-1.1.50-2012.07/hw/arm/Makefile.objs 
> qemu-linaro-1.1.50-2012.07-modified/hw/arm/Makefile.objs
> --- qemu-linaro-1.1.50-2012.07/hw/arm/Makefile.objs     2012-07-05 
> 16:48:28.000000000 +0200
> +++ qemu-linaro-1.1.50-2012.07-modified/hw/arm/Makefile.objs    2012-09-12 
> 11:28:39.121053287 +0200
> @@ -1,4 +1,4 @@
> -obj-y = integratorcp.o versatilepb.o arm_pic.o arm_timer.o
> +obj-y = integratorcp.o versatilepb.o arm_pic.o arm_timer.o arm_archtimer.o
>  obj-y += arm_boot.o pl011.o pl031.o pl050.o pl080.o pl110.o pl181.o pl190.o
>  obj-y += versatile_pci.o
>  obj-y += versatile_i2c.o
> diff -Nupr qemu-linaro-1.1.50-2012.07/hw/arm_archtimer.c 
> qemu-linaro-1.1.50-2012.07-modified/hw/arm_archtimer.c
> --- qemu-linaro-1.1.50-2012.07/hw/arm_archtimer.c       1970-01-01 
> 01:00:00.000000000 +0100
> +++ qemu-linaro-1.1.50-2012.07-modified/hw/arm_archtimer.c      2012-09-12 
> 13:21:44.676267111 +0200
> @@ -0,0 +1,232 @@
> +/*
> + * "Architectured" timer for A15
> + *
> + * Copyright (c) 2012 Enea Software AB
> + * Written by Daniel Forsgren
> + *
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "sysbus.h"
> +#include "qemu-timer.h"
> +
> +/* Primitive (and imcomplete) support for A15 "architectured" timers

incomplete

> +
> +   - Only PL1 timer supported.
> +
> +   - Only minimal subset of fuctionality requred by Linux.

functionality required

> +
> +   - Only tested with single-core.
> +
> +*/
> +
> +/* control register bit assignment */
> +#define CTL_ENABLE  0x01
> +#define CTL_MASK    0x02
> +#define CTL_INT     0x04
> +
> +/* state of per-core timers */
> +typedef struct {
> +    ARMCPU *cpu; /* who are we serving */
> +    QEMUTimer *pl1_timer;
> +    QEMUTimer *pl2_timer;
> +    qemu_irq pl1_irq;
> +    qemu_irq pl2_irq;
> +    uint32_t cntkctl; /* timer pl1 control register */
> +    uint32_t cntp_ctl; /* pl1 physical timer control register */
> +    uint64_t cntvoff; /* virtual offset register */
> +} archtimers;

ArchTimers, please see CODING_STYLE.

> +
> +#define MAX_CPUS 4
> +
> +typedef struct {
> +    SysBusDevice busdev;
> +    archtimers archtimers[MAX_CPUS];
> +} arm_archtimer_state;

ARMArchTimerState

> +
> +
> +static int a15_cntfrq_read(CPUARMState *env, const ARMCPRegInfo *ri,  
> uint64_t *value)
> +{
> +    /* Let's assume that we're running at 1GHz for now. It's not
> +       correct, but it simplifies translation between cycles <-> ns */
> +    *value = 1000000000UL;
> +    return 0;
> +}
> +
> +static int a15_cntkctl_read(CPUARMState *env, const ARMCPRegInfo *ri, 
> uint64_t *value)
> +{
> +    archtimers *at = (archtimers *)(ri->opaque);

If ri->opaque is void pointer, the cast is useless in C. Also other
similar cases.

> +    *value = at->cntkctl;
> +    return 0;
> +}
> +
> +static int a15_cntpct_read(CPUARMState *env, const ARMCPRegInfo *ri, 
> uint64_t *value)
> +{
> +    /* Let's assume that the physical count register is driven by
> +       vm_clock for now. As we have specified that that we're running
> +       at 1GHz, no translation from ns should be needed. */
> +    *value = qemu_get_clock_ns(vm_clock);
> +    return 0;
> +}
> +
> +static int a15_cntvct_read(CPUARMState *env, const ARMCPRegInfo *ri, 
> uint64_t *value)
> +{
> +    archtimers *at = (archtimers *)(ri->opaque);
> +
> +    /* Virtual count should subtract the virtual offfset from physical
> +       count? */
> +    *value = qemu_get_clock_ns(vm_clock) - at->cntvoff;
> +    return 0;
> +}
> +
> +static int a15_cntp_tval_read(CPUARMState *env, const ARMCPRegInfo *ri, 
> uint64_t *value)
> +{
> +    archtimers *at = (archtimers *)(ri->opaque);
> +    *value = qemu_timer_expire_time_ns(at->pl1_timer);
> +    return 0;
> +}
> +
> +static int a15_cntp_tval_write(CPUARMState *env, const ARMCPRegInfo *ri, 
> uint64_t value)
> +{
> +    archtimers *at = (archtimers *)(ri->opaque);
> +
> +    /* I assume that setting a new value means that we should clear
> +       any previous? */
> +    qemu_set_irq(at->pl1_irq, 0);
> +    at->cntp_ctl &= ~CTL_INT;
> +
> +    qemu_mod_timer_ns(at->pl1_timer, qemu_get_clock_ns(vm_clock) + value);
> +
> +    return 0;
> +}
> +
> +static int a15_cntp_ctl_read(CPUARMState *env, const ARMCPRegInfo *ri, 
> uint64_t *value)
> +{
> +    archtimers *at = (archtimers *)(ri->opaque);
> +
> +    *value = at->cntp_ctl;
> +
> +    return 0;
> +}
> +
> +static int a15_cntp_ctl_write(CPUARMState *env, const ARMCPRegInfo *ri,
> +                              uint64_t value)
> +{
> +    archtimers *at = (archtimers *)(ri->opaque);
> +
> +    /* Copy "enable" and "mask" bits from the new value. Preserve the rest. 
> */
> +    at->cntp_ctl = (at->cntp_ctl & ~(CTL_ENABLE | CTL_MASK)) | (value & 
> (CTL_ENABLE | CTL_MASK));
> +
> +    /* If no interrupt is asserted, or interrupt is masked, then lower the 
> irq. */
> +    if (!(at->cntp_ctl & CTL_INT) || (at->cntp_ctl & CTL_MASK))
> +        qemu_set_irq(at->pl1_irq, 0);

Missing braces, please read CODING_STYLE. Please fix also other cases below.

> +
> +    /* If interrupt is asserted and not masked, then raise the irq. */
> +    if ((at->cntp_ctl & CTL_INT) && !(at->cntp_ctl & CTL_MASK))
> +        qemu_set_irq(at->pl1_irq, 1);
> +
> +    return 0;
> +}
> +
> +static const ARMCPRegInfo archtimer_cp_reginfo[] = {
> +
> +    { .name = "CNTFRQ", .cp = 15, .crn = 14, .crm = 0, .opc1 = 0, .opc2 = 0,
> +      .access = PL1_R, .resetvalue = 0, .readfn = a15_cntfrq_read, },
> +
> +    { .name = "CNTKCTL", .cp = 15, .crn = 14, .crm = 1, .opc1 = 0, .opc2 = 0,
> +      .access = PL1_R, .resetvalue = 0, .readfn = a15_cntkctl_read, },
> +
> +    { .name = "CNTP_TVAL", .cp = 15, .crn = 14, .crm = 2, .opc1 = 0, .opc2 = 
> 0,
> +      .access = PL1_RW, .resetvalue = 0, .readfn = a15_cntp_tval_read,
> +      .writefn = a15_cntp_tval_write, },
> +
> +    { .name = "CNTP_CTL", .cp = 15, .crn = 14, .crm = 2, .opc1 = 0, .opc2 = 
> 1,
> +      .access = PL1_RW, .resetvalue = 0, .readfn = a15_cntp_ctl_read,
> +      .writefn = a15_cntp_ctl_write, },
> +
> +    { .name = "CNTPCT", .type = ARM_CP_64BIT, .cp = 15, .crn = 0, .crm = 14, 
> .opc1 = 0, .opc2 = 0,
> +      .access = PL1_R, .resetvalue = 0, .readfn = a15_cntpct_read, },
> +
> +    { .name = "CNTVCT", .type = ARM_CP_64BIT, .cp = 15, .crn = 0, .crm = 14, 
> .opc1 = 1, .opc2 = 0,
> +      .access = PL1_R, .resetvalue = 0, .readfn = a15_cntvct_read, },
> +
> +    REGINFO_SENTINEL
> +};
> +
> +static void pl1_timer_cb(void *opaque)
> +{
> +    archtimers *at = (archtimers *)opaque;

Here the cast is definitely useless, please remove.

> +
> +    /* Set the interrupt bit in control register */
> +    at->cntp_ctl |= CTL_INT;
> +
> +    /* If not masked, then raise the irq */
> +    if (!(at->cntp_ctl & CTL_MASK))
> +        qemu_set_irq(at->pl1_irq, 1);
> +}
> +
> +static int arm_archtimer_init(SysBusDevice *dev)
> +{
> +    arm_archtimer_state *s = FROM_SYSBUS(arm_archtimer_state, dev);
> +    CPUArchState *cpu;
> +    int i;
> +
> +    for (cpu = first_cpu, i = 0; cpu; cpu = cpu->next_cpu, i++) {
> +        archtimers *at = &s->archtimers[i];
> +        at->pl1_timer = qemu_new_timer_ns(vm_clock, &pl1_timer_cb, at);
> +        sysbus_init_irq(dev, &(at->pl1_irq));
> +        sysbus_init_irq(dev, &(at->pl2_irq));
> +        s->archtimers[i].cpu = arm_env_get_cpu(cpu);
> +        define_arm_cp_regs_with_opaque(s->archtimers[i].cpu, 
> archtimer_cp_reginfo, (void *)at);
> +    }
> +
> +    return 0;
> +}
> +
> +static void arm_archtimer_reset(DeviceState *dev)
> +{
> +    arm_archtimer_state *s =
> +        FROM_SYSBUS(arm_archtimer_state, sysbus_from_qdev(dev));
> +    int i;
> +
> +    for (i = 0; i < MAX_CPUS; i++) {
> +        if (s->archtimers[i].pl1_timer)
> +            qemu_del_timer(s->archtimers[i].pl1_timer);
> +        if (s->archtimers[i].pl2_timer)
> +            qemu_del_timer(s->archtimers[i].pl2_timer);
> +    }
> +}
> +
> +static void arm_archtimer_class_init(ObjectClass *class, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(class);
> +    SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(class);
> +
> +    sbc->init = arm_archtimer_init;
> +    dc->reset = arm_archtimer_reset;
> +}
> +
> +static TypeInfo arm_archtimer_info = {
> +    .name          = "arm_archtimer",
> +    .parent        = TYPE_SYS_BUS_DEVICE,
> +    .instance_size = sizeof(arm_archtimer_state),
> +    .class_init    = arm_archtimer_class_init,
> +};
> +
> +static void arm_archtimer_register_types(void)
> +{
> +    type_register_static(&arm_archtimer_info);
> +}
> +
> +type_init(arm_archtimer_register_types)
> diff -Nupr qemu-linaro-1.1.50-2012.07/target-arm/helper.c 
> qemu-linaro-1.1.50-2012.07-modified/target-arm/helper.c
> --- qemu-linaro-1.1.50-2012.07/target-arm/helper.c      2012-07-05 
> 16:48:28.000000000 +0200
> +++ qemu-linaro-1.1.50-2012.07-modified/target-arm/helper.c     2012-09-12 
> 13:15:45.544424842 +0200
> @@ -1012,9 +1012,11 @@ void register_cp_regs_for_features(ARMCP
>      if (arm_feature(env, ARM_FEATURE_THUMB2EE)) {
>          define_arm_cp_regs(cpu, t2ee_cp_reginfo);
>      }
> +    /*

Why would this be needed?

>      if (arm_feature(env, ARM_FEATURE_GENERIC_TIMER)) {
>          define_arm_cp_regs(cpu, generic_timer_cp_reginfo);
>      }
> +    */
>      if (arm_feature(env, ARM_FEATURE_VAPA)) {
>          define_arm_cp_regs(cpu, vapa_cp_reginfo);
>      }
>
>



reply via email to

[Prev in Thread] Current Thread [Next in Thread]