qemu-devel
[Top][All Lists]
Advanced

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

Re: [PATCH v3 03/16] hw/timer/bcm2835: Add the BCM2835 SYS_timer


From: Alistair Francis
Subject: Re: [PATCH v3 03/16] hw/timer/bcm2835: Add the BCM2835 SYS_timer
Date: Mon, 21 Oct 2019 13:31:48 -0700

On Sat, Oct 19, 2019 at 4:47 PM Philippe Mathieu-Daudé <address@hidden> wrote:
>
> Add the 64-bit free running timer. Do not model the COMPARE register
> (no IRQ generated).
> This timer is used by Linux kernel and recently U-Boot:
> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/clocksource/bcm2835_timer.c?h=v3.7
> https://github.com/u-boot/u-boot/blob/v2019.07/include/configs/rpi.h#L19
>
> Datasheet used:
> https://www.raspberrypi.org/app/uploads/2012/02/BCM2835-ARM-Peripherals.pdf
>
> Signed-off-by: Philippe Mathieu-Daudé <address@hidden>
> ---
> v2:
> - Add status/compare* registers
> - Add vmstate and reset handler
>
> checkpatch warning:
> WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
> This is OK because the regex are:
>
>   F: hw/*/bcm283*
>   F: include/hw/*/bcm283*
> ---
>  hw/timer/Makefile.objs            |   1 +
>  hw/timer/bcm2835_systmr.c         | 166 ++++++++++++++++++++++++++++++
>  hw/timer/trace-events             |   5 +
>  include/hw/timer/bcm2835_systmr.h |  33 ++++++
>  4 files changed, 205 insertions(+)
>  create mode 100644 hw/timer/bcm2835_systmr.c
>  create mode 100644 include/hw/timer/bcm2835_systmr.h
>
> diff --git a/hw/timer/Makefile.objs b/hw/timer/Makefile.objs
> index 123d92c969..696cda5905 100644
> --- a/hw/timer/Makefile.objs
> +++ b/hw/timer/Makefile.objs
> @@ -47,3 +47,4 @@ common-obj-$(CONFIG_SUN4V_RTC) += sun4v-rtc.o
>  common-obj-$(CONFIG_CMSDK_APB_TIMER) += cmsdk-apb-timer.o
>  common-obj-$(CONFIG_CMSDK_APB_DUALTIMER) += cmsdk-apb-dualtimer.o
>  common-obj-$(CONFIG_MSF2) += mss-timer.o
> +common-obj-$(CONFIG_RASPI) += bcm2835_systmr.o
> diff --git a/hw/timer/bcm2835_systmr.c b/hw/timer/bcm2835_systmr.c
> new file mode 100644
> index 0000000000..49b40b55f9
> --- /dev/null
> +++ b/hw/timer/bcm2835_systmr.c
> @@ -0,0 +1,166 @@
> +/*
> + * BCM2835 SYS timer emulation
> + *
> + * Copyright (C) 2019 Philippe Mathieu-Daudé <address@hidden>
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + *
> + * Datasheet: BCM2835 ARM Peripherals (C6357-M-1398)
> + * 
> https://www.raspberrypi.org/app/uploads/2012/02/BCM2835-ARM-Peripherals.pdf
> + *
> + * Only the free running 64-bit counter is implemented.
> + * The 4 COMPARE registers and the interruption are not implemented.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qemu/log.h"
> +#include "qemu/timer.h"
> +#include "hw/timer/bcm2835_systmr.h"
> +#include "hw/registerfields.h"
> +#include "migration/vmstate.h"
> +#include "trace.h"
> +
> +REG32(CTRL_STATUS,  0x00)
> +REG32(COUNTER_LOW,  0x04)
> +REG32(COUNTER_HIGH, 0x08)
> +REG32(COMPARE0,     0x0c)
> +REG32(COMPARE1,     0x10)
> +REG32(COMPARE2,     0x14)
> +REG32(COMPARE3,     0x18)
> +
> +static void bcm2835_systmr_update_irq(BCM2835SystemTimerState *s)
> +{
> +    bool enable = !!s->reg.status;
> +
> +    trace_bcm2835_systmr_irq(enable);
> +    qemu_set_irq(s->irq, enable);
> +}
> +
> +static void bcm2835_systmr_update_compare(BCM2835SystemTimerState *s,
> +                                          unsigned timer_index)
> +{
> +    /* TODO fow now, since neither Linux nor U-boot use these timers. */
> +    qemu_log_mask(LOG_UNIMP, "COMPARE register %u not implemented\n",
> +                  timer_index);
> +}
> +
> +static uint64_t bcm2835_systmr_read(void *opaque, hwaddr offset,
> +                                    unsigned size)
> +{
> +    BCM2835SystemTimerState *s = BCM2835_SYSTIMER(opaque);
> +    uint64_t r = 0;
> +
> +    switch (offset) {
> +    case A_CTRL_STATUS:
> +        r = s->reg.status;
> +        break;
> +    case A_COMPARE0 ... A_COMPARE3:
> +        r = s->reg.compare[(offset - A_COMPARE0) >> 2];
> +        break;
> +    case A_COUNTER_LOW:
> +    case A_COUNTER_HIGH:
> +        /* Free running counter at 1MHz */
> +        r = qemu_clock_get_us(QEMU_CLOCK_VIRTUAL);
> +        r >>= 8 * (offset - A_COUNTER_LOW);
> +        r &= UINT32_MAX;
> +        break;
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR, "%s: bad offset 0x%" HWADDR_PRIx "\n",
> +                      __func__, offset);
> +        break;
> +    }
> +    trace_bcm2835_systmr_read(offset, r);
> +
> +    return r;
> +}
> +
> +static void bcm2835_systmr_write(void *opaque, hwaddr offset,
> +                                 uint64_t value, unsigned size)
> +{
> +    BCM2835SystemTimerState *s = BCM2835_SYSTIMER(opaque);
> +
> +    trace_bcm2835_systmr_write(offset, value);
> +    switch (offset) {
> +    case A_CTRL_STATUS:
> +        s->reg.status &= ~value; /* Ack */
> +        bcm2835_systmr_update_irq(s);
> +        break;
> +    case A_COMPARE0 ... A_COMPARE3:
> +        s->reg.compare[(offset - A_COMPARE0) >> 2] = value;
> +        bcm2835_systmr_update_compare(s, (offset - A_COMPARE0) >> 2);
> +        break;
> +    case A_COUNTER_LOW:
> +    case A_COUNTER_HIGH:
> +        qemu_log_mask(LOG_GUEST_ERROR, "%s: read-only ofs 0x%" HWADDR_PRIx 
> "\n",
> +                      __func__, offset);
> +        break;
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR, "%s: bad offset 0x%" HWADDR_PRIx "\n",
> +                      __func__, offset);
> +        break;
> +    }
> +}
> +
> +static const MemoryRegionOps bcm2835_systmr_ops = {
> +    .read = bcm2835_systmr_read,
> +    .write = bcm2835_systmr_write,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +    .impl = {
> +        .min_access_size = 4,
> +        .max_access_size = 4,
> +    },
> +};
> +
> +static void bcm2835_systmr_reset(DeviceState *dev)
> +{
> +    BCM2835SystemTimerState *s = BCM2835_SYSTIMER(dev);
> +
> +    s->reg.status = 0;
> +    for (size_t i = 0; i < ARRAY_SIZE(s->reg.compare); i++) {
> +        s->reg.compare[i] = 0;
> +    }

Can we use meset (or the glib version) here instead?

Otherwise:

Reviewed-by: Alistair Francis <address@hidden>

Alistair

> +}
> +
> +static void bcm2835_systmr_realize(DeviceState *dev, Error **errp)
> +{
> +    BCM2835SystemTimerState *s = BCM2835_SYSTIMER(dev);
> +
> +    memory_region_init_io(&s->iomem, OBJECT(dev), &bcm2835_systmr_ops,
> +                          s, "bcm2835-sys-timer", 0x20);
> +    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem);
> +    sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq);
> +}
> +
> +static const VMStateDescription bcm2835_systmr_vmstate = {
> +    .name = "bcm2835_sys_timer",
> +    .version_id = 1,
> +    .minimum_version_id = 1,
> +    .fields = (VMStateField[]) {
> +        VMSTATE_UINT32(reg.status, BCM2835SystemTimerState),
> +        VMSTATE_UINT32_ARRAY(reg.compare, BCM2835SystemTimerState, 4),
> +        VMSTATE_END_OF_LIST()
> +    }
> +};
> +
> +static void bcm2835_systmr_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +
> +    dc->realize = bcm2835_systmr_realize;
> +    dc->reset = bcm2835_systmr_reset;
> +    dc->vmsd = &bcm2835_systmr_vmstate;
> +}
> +
> +static const TypeInfo bcm2835_systmr_info = {
> +    .name = TYPE_BCM2835_SYSTIMER,
> +    .parent = TYPE_SYS_BUS_DEVICE,
> +    .instance_size = sizeof(BCM2835SystemTimerState),
> +    .class_init = bcm2835_systmr_class_init,
> +};
> +
> +static void bcm2835_systmr_register_types(void)
> +{
> +    type_register_static(&bcm2835_systmr_info);
> +}
> +
> +type_init(bcm2835_systmr_register_types);
> diff --git a/hw/timer/trace-events b/hw/timer/trace-events
> index db02a9142c..0aa399ac69 100644
> --- a/hw/timer/trace-events
> +++ b/hw/timer/trace-events
> @@ -87,3 +87,8 @@ pl031_read(uint32_t addr, uint32_t value) "addr 0x%08x 
> value 0x%08x"
>  pl031_write(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x"
>  pl031_alarm_raised(void) "alarm raised"
>  pl031_set_alarm(uint32_t ticks) "alarm set for %u ticks"
> +
> +# bcm2835_systmr.c
> +bcm2835_systmr_irq(bool enable) "timer irq state %u"
> +bcm2835_systmr_read(uint64_t offset, uint64_t data) "timer read: offset 0x%" 
> PRIx64 " data 0x%" PRIx64
> +bcm2835_systmr_write(uint64_t offset, uint64_t data) "timer write: offset 
> 0x%" PRIx64 " data 0x%" PRIx64
> diff --git a/include/hw/timer/bcm2835_systmr.h 
> b/include/hw/timer/bcm2835_systmr.h
> new file mode 100644
> index 0000000000..c0bc5c8127
> --- /dev/null
> +++ b/include/hw/timer/bcm2835_systmr.h
> @@ -0,0 +1,33 @@
> +/*
> + * BCM2835 SYS timer emulation
> + *
> + * Copyright (c) 2019 Philippe Mathieu-Daudé <address@hidden>
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#ifndef BCM2835_SYSTIMER_H
> +#define BCM2835_SYSTIMER_H
> +
> +#include "hw/sysbus.h"
> +#include "hw/irq.h"
> +
> +#define TYPE_BCM2835_SYSTIMER "bcm2835-sys-timer"
> +#define BCM2835_SYSTIMER(obj) \
> +    OBJECT_CHECK(BCM2835SystemTimerState, (obj), TYPE_BCM2835_SYSTIMER)
> +
> +typedef struct {
> +    /*< private >*/
> +    SysBusDevice parent_obj;
> +
> +    /*< public >*/
> +    MemoryRegion iomem;
> +    qemu_irq irq;
> +
> +    struct {
> +        uint32_t status;
> +        uint32_t compare[4];
> +    } reg;
> +} BCM2835SystemTimerState;
> +
> +#endif
> --
> 2.21.0
>
>



reply via email to

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