* [Qemu-devel] [PATCH] bcm2835_armtimer: add bcm2835 ARM timer
@ 2017-10-11 9:09 Thomas Venriès
2017-10-11 10:43 ` no-reply
0 siblings, 1 reply; 4+ messages in thread
From: Thomas Venriès @ 2017-10-11 9:09 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-arm
The ARM Timer is based on a ARM AP804, but it has
a number of differences with the standard SP804.
There is only one timer which runs in continuous
mode with an extra clock pre-divider register
and a 32-bit free running counter.
The extra stop-in-debug-mode control bit is not
implemented.
Signed-off-by: Thomas Venriès <thomas.venries@gmail.com>
---
hw/arm/bcm2835_peripherals.c | 18 +++
hw/timer/Makefile.objs | 1 +
hw/timer/bcm2835_armtimer.c | 271 +++++++++++++++++++++++++++++++++++
hw/timer/trace-events | 4 +
include/hw/arm/bcm2835_peripherals.h | 2 +
include/hw/timer/bcm2835_armtimer.h | 35 +++++
6 files changed, 331 insertions(+)
create mode 100644 hw/timer/bcm2835_armtimer.c
create mode 100644 include/hw/timer/bcm2835_armtimer.h
diff --git a/hw/arm/bcm2835_peripherals.c b/hw/arm/bcm2835_peripherals.c
index 12e0dd1..73deb2e 100644
--- a/hw/arm/bcm2835_peripherals.c
+++ b/hw/arm/bcm2835_peripherals.c
@@ -90,6 +90,11 @@ static void bcm2835_peripherals_init(Object *obj)
object_property_add_child(obj, "rng", OBJECT(&s->rng), NULL);
qdev_set_parent_bus(DEVICE(&s->rng), sysbus_get_default());
+ /* ARM Timer */
+ object_initialize(&s->armtimer, sizeof(s->armtimer), TYPE_BCM2835_ARMTIMER);
+ object_property_add_child(obj, "armtimer", OBJECT(&s->armtimer), NULL);
+ qdev_set_parent_bus(DEVICE(&s->armtimer), sysbus_get_default());
+
/* Extended Mass Media Controller */
object_initialize(&s->sdhci, sizeof(s->sdhci), TYPE_SYSBUS_SDHCI);
object_property_add_child(obj, "sdhci", OBJECT(&s->sdhci), NULL);
@@ -254,6 +259,19 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
memory_region_add_subregion(&s->peri_mr, RNG_OFFSET,
sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->rng), 0));
+ /* ARM Timer */
+ object_property_set_bool(OBJECT(&s->armtimer), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ memory_region_add_subregion(&s->peri_mr, ARMCTRL_TIMER0_1_OFFSET,
+ sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->armtimer), 0));
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->armtimer), 0,
+ qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_ARM_IRQ,
+ INTERRUPT_ARM_TIMER));
+
/* Extended Mass Media Controller */
object_property_set_int(OBJECT(&s->sdhci), BCM2835_SDHC_CAPAREG, "capareg",
&err);
diff --git a/hw/timer/Makefile.objs b/hw/timer/Makefile.objs
index 8c19eac..268d485 100644
--- a/hw/timer/Makefile.objs
+++ b/hw/timer/Makefile.objs
@@ -29,6 +29,7 @@ obj-$(CONFIG_EXYNOS4) += exynos4210_rtc.o
obj-$(CONFIG_OMAP) += omap_gptimer.o
obj-$(CONFIG_OMAP) += omap_synctimer.o
obj-$(CONFIG_PXA2XX) += pxa2xx_timer.o
+obj-$(CONFIG_RASPI) += bcm2835_armtimer.o
obj-$(CONFIG_SH4) += sh_timer.o
obj-$(CONFIG_DIGIC) += digic-timer.o
obj-$(CONFIG_MIPS_CPS) += mips_gictimer.o
diff --git a/hw/timer/bcm2835_armtimer.c b/hw/timer/bcm2835_armtimer.c
new file mode 100644
index 0000000..7ea3162
--- /dev/null
+++ b/hw/timer/bcm2835_armtimer.c
@@ -0,0 +1,271 @@
+/*
+ * BCM2835 ARM Timer
+ *
+ * Copyright (C) 2017 Thomas Venriès <thomas.venries@gmail.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qapi/error.h"
+#include "qemu/timer.h"
+#include "qemu/main-loop.h"
+#include "hw/ptimer.h"
+#include "hw/timer/bcm2835_armtimer.h"
+#include "trace.h"
+
+#define ARM_TIMER_REG_SIZE 0x24
+
+/* Register offsets */
+#define ARM_TIMER_LOAD 0x00
+#define ARM_TIMER_VALUE 0x04
+#define ARM_TIMER_CTRL 0x08
+#define ARM_TIMER_INTCLR 0x0C
+#define ARM_TIMER_RAW_IRQ 0x10
+#define ARM_TIMER_MASK_IRQ 0x14
+#define ARM_TIMER_RELOAD 0x18
+#define ARM_TIMER_PREDIVIDER 0x1C
+#define ARM_TIMER_COUNTER 0x20
+
+/* Control register masks */
+#define CTRL_CNT_PRESCALE (0xFF << 16)
+#define CTRL_CNT_ENABLE (1 << 9)
+#define CTRL_TIMER_ENABLE (1 << 7)
+#define CTRL_INT_ENABLE (1 << 5)
+#define CTRL_TIMER_PRESCALE (3 << 2)
+#define CTRL_TIMER_SIZE_32BIT (1 << 1)
+
+#define CTRL_TIMER_WRAP_MODE 0
+
+/* Register reset values */
+#define CTRL_CNT_PRESCALE_RESET (0x3E << 16)
+#define ARM_TIMER_CTRL_RESET (CTRL_CNT_PRESCALE_RESET | CTRL_INT_ENABLE)
+#define ARM_TIMER_IE_READ_VALUE 0x544D5241 /* ASCII "ARMT" */
+/*
+ The system clock refers to a 250 MHz frequency by default.
+ This frequency can be changed by setting `core_freq` the `config.txt` file.
+ APB clock runs at half the speed of the system clock also called ARM clock.
+
+ The ARM timer's predivider register is 10 bits wide and can be written
+ or read from. This register has been added as the SP804 expects a 1MHz clock
+ which they do not have. Instead the predivider takes the APB clock
+ and divides it down according to:
+
+ timer_clock = apb_clock / (prediv + 1)
+
+ The need is a 1MHz timer clock frequency and BCM2835 ARM Peripherals
+ documentation mentions the predivider reset value is 0x7D (or 125), so
+ the APB clock refers to a 126MHz frequency.
+
+ Also the additional free-running counter runs from the APB clock and has
+ its own clock predivider controlled by buts 16-23 of the timer control reg:
+
+ frc_clock = apb_clock / (prediv + 1)
+
+ The predivider reset value is 0x3E (or 62), knowing APB clock frequency,
+ the FRN clock refers to a 2MHz frequency by default.
+*/
+#define ARM_APB_FREQ 126000000UL /* Hz */
+#define ARM_TIMER_PREDIVIDER_RESET 125 /* MHz */
+
+static const uint16_t ctrl_prescale [] = { 1, 16, 256, 1 };
+
+static void bcm2835_armtimer_recalibrate(BCM2835ARMTimerState *s, int reload)
+{
+ uint32_t limit;
+
+ /* ARM Dual-Timer Module SP804, section 3.2.1:
+ If the Load Register is set to 0 then an interrupt is generated
+ immediately. */
+ if (reload == 2) {
+ limit = s->reload;
+ } else {
+ limit = (s->ctrl & CTRL_TIMER_SIZE_32BIT) ? 0xFFFFFFFF : 0XFFFF;
+ }
+
+ ptimer_set_limit(s->timer, limit, reload);
+}
+
+static void bcm2835_armtimer_cb(void *opaque)
+{
+ BCM2835ARMTimerState *s = (BCM2835ARMTimerState *)opaque;
+
+ s->raw_irq = 1;
+
+ if (s->ctrl & CTRL_TIMER_ENABLE) {
+ qemu_irq_raise(s->irq);
+ trace_bcm2835_armtimer_interrupt();
+ }
+}
+
+static uint64_t bcm2835_armtimer_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ BCM2835ARMTimerState *s = (BCM2835ARMTimerState *)opaque;
+
+ switch (offset) {
+ case ARM_TIMER_LOAD:
+ case ARM_TIMER_RELOAD:
+ return s->reload;
+ case ARM_TIMER_VALUE:
+ return ptimer_get_count(s->timer);
+ case ARM_TIMER_CTRL:
+ return s->ctrl;
+ case ARM_TIMER_INTCLR:
+ return ARM_TIMER_IE_READ_VALUE;
+ case ARM_TIMER_RAW_IRQ:
+ return s->raw_irq;
+ case ARM_TIMER_MASK_IRQ:
+ return (s->raw_irq && (s->ctrl & CTRL_INT_ENABLE));
+ case ARM_TIMER_PREDIVIDER:
+ return s->prediv;
+ case ARM_TIMER_COUNTER:
+ return qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / s->prescaler;
+
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "bcm2835_armtimer_read: Bad offset - [%x]\n",
+ (int)offset);
+ return 0;
+ }
+}
+
+static void bcm2835_armtimer_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ BCM2835ARMTimerState *s = (BCM2835ARMTimerState *)opaque;
+ uint32_t div;
+
+ switch (offset) {
+ case ARM_TIMER_LOAD:
+ bcm2835_armtimer_recalibrate(s, 2);
+ break;
+ case ARM_TIMER_CTRL:
+ if (s->ctrl & CTRL_TIMER_ENABLE)
+ ptimer_stop(s->timer);
+
+ s->ctrl = value;
+
+ s->prescaler = (ARM_APB_FREQ /
+ (((s->ctrl & CTRL_CNT_PRESCALE) >> 16) + 1));
+
+ bcm2835_armtimer_recalibrate(s, s->ctrl & CTRL_TIMER_ENABLE);
+
+ div = ctrl_prescale[((s->ctrl & CTRL_TIMER_PRESCALE) >> 2)]
+ * s->prediv;
+ ptimer_set_freq(s->timer, ARM_APB_FREQ / div);
+
+ if (s->ctrl & CTRL_TIMER_ENABLE)
+ ptimer_run(s->timer, CTRL_TIMER_WRAP_MODE);
+ break;
+ case ARM_TIMER_INTCLR:
+ qemu_irq_lower(s->irq);
+ s->raw_irq = 0;
+ trace_bcm2835_armtimer_ack();
+ break;
+ case ARM_TIMER_RELOAD:
+ /* In Free-running mode the timer counter wraps around to 32 or 16-bit
+ limit (respectively 0xFFFFFFFF or 0xFFFFF) regardless the Reload
+ and Load Register values, except that when the Load Register is
+ written to directly, the current count immediately resets to the 32
+ or 16-bits limit according to the Control Register bit [1]. */
+ s->reload = value;
+ break;
+ case ARM_TIMER_PREDIVIDER:
+ s->prediv = value + 1;
+ if (s->ctrl & CTRL_TIMER_ENABLE) {
+ ptimer_stop(s->timer);
+ div = ctrl_prescale[((s->ctrl & CTRL_TIMER_PRESCALE) >> 2)]
+ * s->prediv;
+ ptimer_set_freq(s->timer, ARM_APB_FREQ / div);
+ ptimer_run(s->timer, CTRL_TIMER_WRAP_MODE);
+ }
+ break;
+
+ case ARM_TIMER_VALUE:
+ case ARM_TIMER_RAW_IRQ:
+ case ARM_TIMER_MASK_IRQ:
+ case ARM_TIMER_COUNTER:
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "bcm2835_armtimer_write: Bad offset - [%x]\n",
+ (int)offset);
+ }
+}
+
+static const MemoryRegionOps bcm2835_armtimer_ops = {
+ .read = bcm2835_armtimer_read,
+ .write = bcm2835_armtimer_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+};
+
+static const VMStateDescription vmstate_bcm2835_armtimer = {
+ .name = TYPE_BCM2835_ARMTIMER,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(ctrl, BCM2835ARMTimerState),
+ VMSTATE_UINT32(reload, BCM2835ARMTimerState),
+ VMSTATE_UINT32(raw_irq, BCM2835ARMTimerState),
+ VMSTATE_UINT32(msk_irq, BCM2835ARMTimerState),
+ VMSTATE_UINT32(prediv, BCM2835ARMTimerState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void bcm2835_armtimer_init(Object *obj)
+{
+ BCM2835ARMTimerState *s = BCM2835_ARMTIMER(obj);
+ QEMUBH *bh = qemu_bh_new(bcm2835_armtimer_cb, s);
+
+ s->reload = s->raw_irq = s->msk_irq = 0;
+ s->prediv = ARM_TIMER_PREDIVIDER_RESET;
+
+ /* ARM Dual-Timer Module SP804, section 2.2.6:
+ Timer Control Register Initialization :
+ - the timer counter is disabled, Bit[7]=0
+ - 16-bit counter mode is selected, Bit[1]=0
+ - prescalers are set to divide by 1, Bit[2:3]=0x0
+ - interrupts are cleared but enabled, Bit[5]=1
+ - the Load Register is set to zero
+ - the counter Value is set to 0xFFFFFFFF (useless)
+ BCM2835 ARM Peripherals, section 14.2:
+ - free-running mode is always selected, Bit[6]=0 and Bit[0]=0
+ because periodic and one-shot modes are not supported. */
+ s->ctrl = ARM_TIMER_CTRL_RESET;
+
+ s->timer = ptimer_init(bh, PTIMER_POLICY_DEFAULT);
+
+ memory_region_init_io(&s->iomem, obj, &bcm2835_armtimer_ops, s,
+ TYPE_BCM2835_ARMTIMER, ARM_TIMER_REG_SIZE);
+
+ sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem);
+ sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq);
+}
+
+static void bcm2835_armtimer_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->desc = "BCM2835 ARM Timer";
+ dc->vmsd = &vmstate_bcm2835_armtimer;
+}
+
+static TypeInfo bcm2835_armtimer_info = {
+ .name = TYPE_BCM2835_ARMTIMER,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(BCM2835ARMTimerState),
+ .class_init = bcm2835_armtimer_class_init,
+ .instance_init = bcm2835_armtimer_init,
+};
+
+static void bcm2835_armtimer_register_types(void)
+{
+ type_register_static(&bcm2835_armtimer_info);
+}
+
+type_init(bcm2835_armtimer_register_types)
diff --git a/hw/timer/trace-events b/hw/timer/trace-events
index 640722b..d81dd38 100644
--- a/hw/timer/trace-events
+++ b/hw/timer/trace-events
@@ -60,3 +60,7 @@ systick_write(uint64_t addr, uint32_t value, unsigned size) "systick write addr
cmsdk_apb_timer_read(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB timer read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
cmsdk_apb_timer_write(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB timer write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
cmsdk_apb_timer_reset(void) "CMSDK APB timer: reset"
+
+# hw/timer/bcm2535_armtimer.c
+bcm2835_armtimer_interrupt(void) "irq"
+bcm2835_armtimer_ack(void) "ack"
diff --git a/include/hw/arm/bcm2835_peripherals.h b/include/hw/arm/bcm2835_peripherals.h
index 122b286..236433b 100644
--- a/include/hw/arm/bcm2835_peripherals.h
+++ b/include/hw/arm/bcm2835_peripherals.h
@@ -20,6 +20,7 @@
#include "hw/intc/bcm2835_ic.h"
#include "hw/misc/bcm2835_property.h"
#include "hw/misc/bcm2835_rng.h"
+#include "hw/timer/bcm2835_armtimer.h"
#include "hw/misc/bcm2835_mbox.h"
#include "hw/sd/sdhci.h"
#include "hw/sd/bcm2835_sdhost.h"
@@ -43,6 +44,7 @@ typedef struct BCM2835PeripheralState {
BCM2835FBState fb;
BCM2835DMAState dma;
BCM2835ICState ic;
+ BCM2835ARMTimerState armtimer;
BCM2835PropertyState property;
BCM2835RngState rng;
BCM2835MboxState mboxes;
diff --git a/include/hw/timer/bcm2835_armtimer.h b/include/hw/timer/bcm2835_armtimer.h
new file mode 100644
index 0000000..cb69f75
--- /dev/null
+++ b/include/hw/timer/bcm2835_armtimer.h
@@ -0,0 +1,35 @@
+/*
+ * BCM2835 ARM Timer
+ *
+ * Copyright (C) 2017 Thomas Venriès <thomas.venries@gmail.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef BCM2835_ARMTIMER_H
+#define BCM2835_ARMTIMER_H
+
+#include "hw/ptimer.h"
+#include "hw/sysbus.h"
+
+#define TYPE_BCM2835_ARMTIMER "bcm2835-armtimer"
+#define BCM2835_ARMTIMER(obj) \
+ OBJECT_CHECK(BCM2835ARMTimerState, (obj), TYPE_BCM2835_ARMTIMER)
+
+typedef struct {
+ SysBusDevice parent_obj;
+ MemoryRegion iomem;
+
+ ptimer_state *timer;
+ qemu_irq irq;
+
+ uint32_t ctrl;
+ uint32_t raw_irq;
+ uint32_t msk_irq;
+ uint32_t reload;
+ uint32_t prediv;
+ uint32_t prescaler;
+} BCM2835ARMTimerState;
+
+#endif
--
2.7.4
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [Qemu-devel] [PATCH] bcm2835_armtimer: add bcm2835 ARM timer
2017-10-11 9:09 [Qemu-devel] [PATCH] bcm2835_armtimer: add bcm2835 ARM timer Thomas Venriès
@ 2017-10-11 10:43 ` no-reply
2017-10-11 13:24 ` [Qemu-devel] [PATCH v2] " Thomas Venriès
0 siblings, 1 reply; 4+ messages in thread
From: no-reply @ 2017-10-11 10:43 UTC (permalink / raw)
To: thomas.venries; +Cc: famz, qemu-devel, qemu-arm
Hi,
This series seems to have some coding style problems. See output below for
more information:
Type: series
Message-id: 1507712980-12135-1-git-send-email-thomas.venries@gmail.com
Subject: [Qemu-devel] [PATCH] bcm2835_armtimer: add bcm2835 ARM timer
=== TEST SCRIPT BEGIN ===
#!/bin/bash
BASE=base
n=1
total=$(git log --oneline $BASE.. | wc -l)
failed=0
git config --local diff.renamelimit 0
git config --local diff.renames True
commits="$(git log --format=%H --reverse $BASE..)"
for c in $commits; do
echo "Checking PATCH $n/$total: $(git log -n 1 --format=%s $c)..."
if ! git show $c --format=email | ./scripts/checkpatch.pl --mailback -; then
failed=1
echo
fi
n=$((n+1))
done
exit $failed
=== TEST SCRIPT END ===
Updating 3c8cf5a9c21ff8782164d1def7f44bd888713384
Switched to a new branch 'test'
fd596b541c bcm2835_armtimer: add bcm2835 ARM timer
=== OUTPUT BEGIN ===
Checking PATCH 1/1: bcm2835_armtimer: add bcm2835 ARM timer...
ERROR: space prohibited before open square bracket '['
#147: FILE: hw/timer/bcm2835_armtimer.c:73:
+static const uint16_t ctrl_prescale [] = { 1, 16, 256, 1 };
ERROR: return is not a function, parentheses are not required
#195: FILE: hw/timer/bcm2835_armtimer.c:121:
+ return (s->raw_irq && (s->ctrl & CTRL_INT_ENABLE));
ERROR: braces {} are necessary for all arms of this statement
#220: FILE: hw/timer/bcm2835_armtimer.c:146:
+ if (s->ctrl & CTRL_TIMER_ENABLE)
[...]
ERROR: braces {} are necessary for all arms of this statement
#234: FILE: hw/timer/bcm2835_armtimer.c:160:
+ if (s->ctrl & CTRL_TIMER_ENABLE)
[...]
total: 4 errors, 0 warnings, 364 lines checked
Your patch has style problems, please review. If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
=== OUTPUT END ===
Test command exited with code: 1
---
Email generated automatically by Patchew [http://patchew.org/].
Please send your feedback to patchew-devel@freelists.org
^ permalink raw reply [flat|nested] 4+ messages in thread
* [Qemu-devel] [PATCH v2] bcm2835_armtimer: add bcm2835 ARM timer
2017-10-11 10:43 ` no-reply
@ 2017-10-11 13:24 ` Thomas Venriès
2017-10-11 14:52 ` Peter Maydell
0 siblings, 1 reply; 4+ messages in thread
From: Thomas Venriès @ 2017-10-11 13:24 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-arm
The ARM Timer is based on a ARM AP804, but it has
a number of differences with the standard SP804.
There is only one timer which runs in continuous
mode with an extra clock pre-divider register
and a 32-bit free running counter.
The extra stop-in-debug-mode control bit is not
implemented.
Signed-off-by: Thomas Venriès <thomas.venries@gmail.com>
---
hw/arm/bcm2835_peripherals.c | 18 +++
hw/timer/Makefile.objs | 1 +
hw/timer/bcm2835_armtimer.c | 273 +++++++++++++++++++++++++++++++++++
hw/timer/trace-events | 4 +
include/hw/arm/bcm2835_peripherals.h | 2 +
include/hw/timer/bcm2835_armtimer.h | 35 +++++
6 files changed, 333 insertions(+)
create mode 100644 hw/timer/bcm2835_armtimer.c
create mode 100644 include/hw/timer/bcm2835_armtimer.h
diff --git a/hw/arm/bcm2835_peripherals.c b/hw/arm/bcm2835_peripherals.c
index 12e0dd1..73deb2e 100644
--- a/hw/arm/bcm2835_peripherals.c
+++ b/hw/arm/bcm2835_peripherals.c
@@ -90,6 +90,11 @@ static void bcm2835_peripherals_init(Object *obj)
object_property_add_child(obj, "rng", OBJECT(&s->rng), NULL);
qdev_set_parent_bus(DEVICE(&s->rng), sysbus_get_default());
+ /* ARM Timer */
+ object_initialize(&s->armtimer, sizeof(s->armtimer), TYPE_BCM2835_ARMTIMER);
+ object_property_add_child(obj, "armtimer", OBJECT(&s->armtimer), NULL);
+ qdev_set_parent_bus(DEVICE(&s->armtimer), sysbus_get_default());
+
/* Extended Mass Media Controller */
object_initialize(&s->sdhci, sizeof(s->sdhci), TYPE_SYSBUS_SDHCI);
object_property_add_child(obj, "sdhci", OBJECT(&s->sdhci), NULL);
@@ -254,6 +259,19 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
memory_region_add_subregion(&s->peri_mr, RNG_OFFSET,
sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->rng), 0));
+ /* ARM Timer */
+ object_property_set_bool(OBJECT(&s->armtimer), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ memory_region_add_subregion(&s->peri_mr, ARMCTRL_TIMER0_1_OFFSET,
+ sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->armtimer), 0));
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->armtimer), 0,
+ qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_ARM_IRQ,
+ INTERRUPT_ARM_TIMER));
+
/* Extended Mass Media Controller */
object_property_set_int(OBJECT(&s->sdhci), BCM2835_SDHC_CAPAREG, "capareg",
&err);
diff --git a/hw/timer/Makefile.objs b/hw/timer/Makefile.objs
index 8c19eac..268d485 100644
--- a/hw/timer/Makefile.objs
+++ b/hw/timer/Makefile.objs
@@ -29,6 +29,7 @@ obj-$(CONFIG_EXYNOS4) += exynos4210_rtc.o
obj-$(CONFIG_OMAP) += omap_gptimer.o
obj-$(CONFIG_OMAP) += omap_synctimer.o
obj-$(CONFIG_PXA2XX) += pxa2xx_timer.o
+obj-$(CONFIG_RASPI) += bcm2835_armtimer.o
obj-$(CONFIG_SH4) += sh_timer.o
obj-$(CONFIG_DIGIC) += digic-timer.o
obj-$(CONFIG_MIPS_CPS) += mips_gictimer.o
diff --git a/hw/timer/bcm2835_armtimer.c b/hw/timer/bcm2835_armtimer.c
new file mode 100644
index 0000000..39ff213
--- /dev/null
+++ b/hw/timer/bcm2835_armtimer.c
@@ -0,0 +1,273 @@
+/*
+ * BCM2835 ARM Timer
+ *
+ * Copyright (C) 2017 Thomas Venriès <thomas.venries@gmail.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qapi/error.h"
+#include "qemu/timer.h"
+#include "qemu/main-loop.h"
+#include "hw/ptimer.h"
+#include "hw/timer/bcm2835_armtimer.h"
+#include "trace.h"
+
+#define ARM_TIMER_REG_SIZE 0x24
+
+/* Register offsets */
+#define ARM_TIMER_LOAD 0x00
+#define ARM_TIMER_VALUE 0x04
+#define ARM_TIMER_CTRL 0x08
+#define ARM_TIMER_INTCLR 0x0C
+#define ARM_TIMER_RAW_IRQ 0x10
+#define ARM_TIMER_MASK_IRQ 0x14
+#define ARM_TIMER_RELOAD 0x18
+#define ARM_TIMER_PREDIVIDER 0x1C
+#define ARM_TIMER_COUNTER 0x20
+
+/* Control register masks */
+#define CTRL_CNT_PRESCALE (0xFF << 16)
+#define CTRL_CNT_ENABLE (1 << 9)
+#define CTRL_TIMER_ENABLE (1 << 7)
+#define CTRL_INT_ENABLE (1 << 5)
+#define CTRL_TIMER_PRESCALE (3 << 2)
+#define CTRL_TIMER_SIZE_32BIT (1 << 1)
+
+#define CTRL_TIMER_WRAP_MODE 0
+
+/* Register reset values */
+#define CTRL_CNT_PRESCALE_RESET (0x3E << 16)
+#define ARM_TIMER_CTRL_RESET (CTRL_CNT_PRESCALE_RESET | CTRL_INT_ENABLE)
+#define ARM_TIMER_IE_READ_VALUE 0x544D5241 /* ASCII "ARMT" */
+/*
+ The system clock refers to a 250 MHz frequency by default.
+ This frequency can be changed by setting `core_freq` the `config.txt` file.
+ APB clock runs at half the speed of the system clock also called ARM clock.
+
+ The ARM timer's predivider register is 10 bits wide and can be written
+ or read from. This register has been added as the SP804 expects a 1MHz clock
+ which they do not have. Instead the predivider takes the APB clock
+ and divides it down according to:
+
+ timer_clock = apb_clock / (prediv + 1)
+
+ The need is a 1MHz timer clock frequency and BCM2835 ARM Peripherals
+ documentation mentions the predivider reset value is 0x7D (or 125), so
+ the APB clock refers to a 126MHz frequency.
+
+ Also the additional free-running counter runs from the APB clock and has
+ its own clock predivider controlled by buts 16-23 of the timer control reg:
+
+ frc_clock = apb_clock / (prediv + 1)
+
+ The predivider reset value is 0x3E (or 62), knowing APB clock frequency,
+ the FRN clock refers to a 2MHz frequency by default.
+*/
+#define ARM_APB_FREQ 126000000UL /* Hz */
+#define ARM_TIMER_PREDIVIDER_RESET 125 /* MHz */
+
+static const uint16_t ctrl_prescale[] = { 1, 16, 256, 1 };
+
+static void bcm2835_armtimer_recalibrate(BCM2835ARMTimerState *s, int reload)
+{
+ uint32_t limit;
+
+ /* ARM Dual-Timer Module SP804, section 3.2.1:
+ If the Load Register is set to 0 then an interrupt is generated
+ immediately. */
+ if (reload == 2) {
+ limit = s->reload;
+ } else {
+ limit = (s->ctrl & CTRL_TIMER_SIZE_32BIT) ? 0xFFFFFFFF : 0XFFFF;
+ }
+
+ ptimer_set_limit(s->timer, limit, reload);
+}
+
+static void bcm2835_armtimer_cb(void *opaque)
+{
+ BCM2835ARMTimerState *s = (BCM2835ARMTimerState *)opaque;
+
+ s->raw_irq = 1;
+
+ if (s->ctrl & CTRL_TIMER_ENABLE) {
+ qemu_irq_raise(s->irq);
+ trace_bcm2835_armtimer_interrupt();
+ }
+}
+
+static uint64_t bcm2835_armtimer_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ BCM2835ARMTimerState *s = (BCM2835ARMTimerState *)opaque;
+
+ switch (offset) {
+ case ARM_TIMER_LOAD:
+ case ARM_TIMER_RELOAD:
+ return s->reload;
+ case ARM_TIMER_VALUE:
+ return ptimer_get_count(s->timer);
+ case ARM_TIMER_CTRL:
+ return s->ctrl;
+ case ARM_TIMER_INTCLR:
+ return ARM_TIMER_IE_READ_VALUE;
+ case ARM_TIMER_RAW_IRQ:
+ return s->raw_irq;
+ case ARM_TIMER_MASK_IRQ:
+ return s->raw_irq && (s->ctrl & CTRL_INT_ENABLE);
+ case ARM_TIMER_PREDIVIDER:
+ return s->prediv;
+ case ARM_TIMER_COUNTER:
+ return qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / s->prescaler;
+
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "bcm2835_armtimer_read: Bad offset - [%x]\n",
+ (int)offset);
+ return 0;
+ }
+}
+
+static void bcm2835_armtimer_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ BCM2835ARMTimerState *s = (BCM2835ARMTimerState *)opaque;
+ uint32_t div;
+
+ switch (offset) {
+ case ARM_TIMER_LOAD:
+ bcm2835_armtimer_recalibrate(s, 2);
+ break;
+ case ARM_TIMER_CTRL:
+ if (s->ctrl & CTRL_TIMER_ENABLE) {
+ ptimer_stop(s->timer);
+ }
+
+ s->ctrl = value;
+
+ s->prescaler = (ARM_APB_FREQ /
+ (((s->ctrl & CTRL_CNT_PRESCALE) >> 16) + 1));
+
+ bcm2835_armtimer_recalibrate(s, s->ctrl & CTRL_TIMER_ENABLE);
+
+ div = ctrl_prescale[((s->ctrl & CTRL_TIMER_PRESCALE) >> 2)]
+ * s->prediv;
+ ptimer_set_freq(s->timer, ARM_APB_FREQ / div);
+
+ if (s->ctrl & CTRL_TIMER_ENABLE) {
+ ptimer_run(s->timer, CTRL_TIMER_WRAP_MODE);
+ }
+ break;
+ case ARM_TIMER_INTCLR:
+ qemu_irq_lower(s->irq);
+ s->raw_irq = 0;
+ trace_bcm2835_armtimer_ack();
+ break;
+ case ARM_TIMER_RELOAD:
+ /* In Free-running mode the timer counter wraps around to 32 or 16-bit
+ limit (respectively 0xFFFFFFFF or 0xFFFFF) regardless the Reload
+ and Load Register values, except that when the Load Register is
+ written to directly, the current count immediately resets to the 32
+ or 16-bits limit according to the Control Register bit [1]. */
+ s->reload = value;
+ break;
+ case ARM_TIMER_PREDIVIDER:
+ s->prediv = value + 1;
+ if (s->ctrl & CTRL_TIMER_ENABLE) {
+ ptimer_stop(s->timer);
+ div = ctrl_prescale[((s->ctrl & CTRL_TIMER_PRESCALE) >> 2)]
+ * s->prediv;
+ ptimer_set_freq(s->timer, ARM_APB_FREQ / div);
+ ptimer_run(s->timer, CTRL_TIMER_WRAP_MODE);
+ }
+ break;
+
+ case ARM_TIMER_VALUE:
+ case ARM_TIMER_RAW_IRQ:
+ case ARM_TIMER_MASK_IRQ:
+ case ARM_TIMER_COUNTER:
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "bcm2835_armtimer_write: Bad offset - [%x]\n",
+ (int)offset);
+ }
+}
+
+static const MemoryRegionOps bcm2835_armtimer_ops = {
+ .read = bcm2835_armtimer_read,
+ .write = bcm2835_armtimer_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+};
+
+static const VMStateDescription vmstate_bcm2835_armtimer = {
+ .name = TYPE_BCM2835_ARMTIMER,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(ctrl, BCM2835ARMTimerState),
+ VMSTATE_UINT32(reload, BCM2835ARMTimerState),
+ VMSTATE_UINT32(raw_irq, BCM2835ARMTimerState),
+ VMSTATE_UINT32(msk_irq, BCM2835ARMTimerState),
+ VMSTATE_UINT32(prediv, BCM2835ARMTimerState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void bcm2835_armtimer_init(Object *obj)
+{
+ BCM2835ARMTimerState *s = BCM2835_ARMTIMER(obj);
+ QEMUBH *bh = qemu_bh_new(bcm2835_armtimer_cb, s);
+
+ s->reload = s->raw_irq = s->msk_irq = 0;
+ s->prediv = ARM_TIMER_PREDIVIDER_RESET;
+
+ /* ARM Dual-Timer Module SP804, section 2.2.6:
+ Timer Control Register Initialization :
+ - the timer counter is disabled, Bit[7]=0
+ - 16-bit counter mode is selected, Bit[1]=0
+ - prescalers are set to divide by 1, Bit[2:3]=0x0
+ - interrupts are cleared but enabled, Bit[5]=1
+ - the Load Register is set to zero
+ - the counter Value is set to 0xFFFFFFFF (useless)
+ BCM2835 ARM Peripherals, section 14.2:
+ - free-running mode is always selected, Bit[6]=0 and Bit[0]=0
+ because periodic and one-shot modes are not supported. */
+ s->ctrl = ARM_TIMER_CTRL_RESET;
+
+ s->timer = ptimer_init(bh, PTIMER_POLICY_DEFAULT);
+
+ memory_region_init_io(&s->iomem, obj, &bcm2835_armtimer_ops, s,
+ TYPE_BCM2835_ARMTIMER, ARM_TIMER_REG_SIZE);
+
+ sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem);
+ sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq);
+}
+
+static void bcm2835_armtimer_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->desc = "BCM2835 ARM Timer";
+ dc->vmsd = &vmstate_bcm2835_armtimer;
+}
+
+static TypeInfo bcm2835_armtimer_info = {
+ .name = TYPE_BCM2835_ARMTIMER,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(BCM2835ARMTimerState),
+ .class_init = bcm2835_armtimer_class_init,
+ .instance_init = bcm2835_armtimer_init,
+};
+
+static void bcm2835_armtimer_register_types(void)
+{
+ type_register_static(&bcm2835_armtimer_info);
+}
+
+type_init(bcm2835_armtimer_register_types)
diff --git a/hw/timer/trace-events b/hw/timer/trace-events
index 640722b..d81dd38 100644
--- a/hw/timer/trace-events
+++ b/hw/timer/trace-events
@@ -60,3 +60,7 @@ systick_write(uint64_t addr, uint32_t value, unsigned size) "systick write addr
cmsdk_apb_timer_read(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB timer read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
cmsdk_apb_timer_write(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB timer write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
cmsdk_apb_timer_reset(void) "CMSDK APB timer: reset"
+
+# hw/timer/bcm2535_armtimer.c
+bcm2835_armtimer_interrupt(void) "irq"
+bcm2835_armtimer_ack(void) "ack"
diff --git a/include/hw/arm/bcm2835_peripherals.h b/include/hw/arm/bcm2835_peripherals.h
index 122b286..236433b 100644
--- a/include/hw/arm/bcm2835_peripherals.h
+++ b/include/hw/arm/bcm2835_peripherals.h
@@ -20,6 +20,7 @@
#include "hw/intc/bcm2835_ic.h"
#include "hw/misc/bcm2835_property.h"
#include "hw/misc/bcm2835_rng.h"
+#include "hw/timer/bcm2835_armtimer.h"
#include "hw/misc/bcm2835_mbox.h"
#include "hw/sd/sdhci.h"
#include "hw/sd/bcm2835_sdhost.h"
@@ -43,6 +44,7 @@ typedef struct BCM2835PeripheralState {
BCM2835FBState fb;
BCM2835DMAState dma;
BCM2835ICState ic;
+ BCM2835ARMTimerState armtimer;
BCM2835PropertyState property;
BCM2835RngState rng;
BCM2835MboxState mboxes;
diff --git a/include/hw/timer/bcm2835_armtimer.h b/include/hw/timer/bcm2835_armtimer.h
new file mode 100644
index 0000000..cb69f75
--- /dev/null
+++ b/include/hw/timer/bcm2835_armtimer.h
@@ -0,0 +1,35 @@
+/*
+ * BCM2835 ARM Timer
+ *
+ * Copyright (C) 2017 Thomas Venriès <thomas.venries@gmail.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef BCM2835_ARMTIMER_H
+#define BCM2835_ARMTIMER_H
+
+#include "hw/ptimer.h"
+#include "hw/sysbus.h"
+
+#define TYPE_BCM2835_ARMTIMER "bcm2835-armtimer"
+#define BCM2835_ARMTIMER(obj) \
+ OBJECT_CHECK(BCM2835ARMTimerState, (obj), TYPE_BCM2835_ARMTIMER)
+
+typedef struct {
+ SysBusDevice parent_obj;
+ MemoryRegion iomem;
+
+ ptimer_state *timer;
+ qemu_irq irq;
+
+ uint32_t ctrl;
+ uint32_t raw_irq;
+ uint32_t msk_irq;
+ uint32_t reload;
+ uint32_t prediv;
+ uint32_t prescaler;
+} BCM2835ARMTimerState;
+
+#endif
--
2.7.4
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [Qemu-devel] [PATCH v2] bcm2835_armtimer: add bcm2835 ARM timer
2017-10-11 13:24 ` [Qemu-devel] [PATCH v2] " Thomas Venriès
@ 2017-10-11 14:52 ` Peter Maydell
0 siblings, 0 replies; 4+ messages in thread
From: Peter Maydell @ 2017-10-11 14:52 UTC (permalink / raw)
To: Thomas Venriès; +Cc: QEMU Developers, qemu-arm
On 11 October 2017 at 14:24, Thomas Venriès <thomas.venries@gmail.com> wrote:
> The ARM Timer is based on a ARM AP804, but it has
> a number of differences with the standard SP804.
> There is only one timer which runs in continuous
> mode with an extra clock pre-divider register
> and a 32-bit free running counter.
>
> The extra stop-in-debug-mode control bit is not
> implemented.
>
> Signed-off-by: Thomas Venriès <thomas.venries@gmail.com>
> ---
> hw/arm/bcm2835_peripherals.c | 18 +++
> hw/timer/Makefile.objs | 1 +
> hw/timer/bcm2835_armtimer.c | 273 +++++++++++++++++++++++++++++++++++
> hw/timer/trace-events | 4 +
> include/hw/arm/bcm2835_peripherals.h | 2 +
> include/hw/timer/bcm2835_armtimer.h | 35 +++++
> 6 files changed, 333 insertions(+)
> create mode 100644 hw/timer/bcm2835_armtimer.c
> create mode 100644 include/hw/timer/bcm2835_armtimer.h
>
> diff --git a/hw/arm/bcm2835_peripherals.c b/hw/arm/bcm2835_peripherals.c
> index 12e0dd1..73deb2e 100644
> --- a/hw/arm/bcm2835_peripherals.c
> +++ b/hw/arm/bcm2835_peripherals.c
> @@ -90,6 +90,11 @@ static void bcm2835_peripherals_init(Object *obj)
> object_property_add_child(obj, "rng", OBJECT(&s->rng), NULL);
> qdev_set_parent_bus(DEVICE(&s->rng), sysbus_get_default());
>
> + /* ARM Timer */
> + object_initialize(&s->armtimer, sizeof(s->armtimer), TYPE_BCM2835_ARMTIMER);
> + object_property_add_child(obj, "armtimer", OBJECT(&s->armtimer), NULL);
> + qdev_set_parent_bus(DEVICE(&s->armtimer), sysbus_get_default());
> +
> /* Extended Mass Media Controller */
> object_initialize(&s->sdhci, sizeof(s->sdhci), TYPE_SYSBUS_SDHCI);
> object_property_add_child(obj, "sdhci", OBJECT(&s->sdhci), NULL);
> @@ -254,6 +259,19 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
> memory_region_add_subregion(&s->peri_mr, RNG_OFFSET,
> sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->rng), 0));
>
> + /* ARM Timer */
> + object_property_set_bool(OBJECT(&s->armtimer), true, "realized", &err);
> + if (err) {
> + error_propagate(errp, err);
> + return;
> + }
> +
> + memory_region_add_subregion(&s->peri_mr, ARMCTRL_TIMER0_1_OFFSET,
> + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->armtimer), 0));
> + sysbus_connect_irq(SYS_BUS_DEVICE(&s->armtimer), 0,
> + qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_ARM_IRQ,
> + INTERRUPT_ARM_TIMER));
> +
> /* Extended Mass Media Controller */
> object_property_set_int(OBJECT(&s->sdhci), BCM2835_SDHC_CAPAREG, "capareg",
> &err);
> diff --git a/hw/timer/Makefile.objs b/hw/timer/Makefile.objs
> index 8c19eac..268d485 100644
> --- a/hw/timer/Makefile.objs
> +++ b/hw/timer/Makefile.objs
> @@ -29,6 +29,7 @@ obj-$(CONFIG_EXYNOS4) += exynos4210_rtc.o
> obj-$(CONFIG_OMAP) += omap_gptimer.o
> obj-$(CONFIG_OMAP) += omap_synctimer.o
> obj-$(CONFIG_PXA2XX) += pxa2xx_timer.o
> +obj-$(CONFIG_RASPI) += bcm2835_armtimer.o
> obj-$(CONFIG_SH4) += sh_timer.o
> obj-$(CONFIG_DIGIC) += digic-timer.o
> obj-$(CONFIG_MIPS_CPS) += mips_gictimer.o
> diff --git a/hw/timer/bcm2835_armtimer.c b/hw/timer/bcm2835_armtimer.c
> new file mode 100644
> index 0000000..39ff213
> --- /dev/null
> +++ b/hw/timer/bcm2835_armtimer.c
> @@ -0,0 +1,273 @@
> +/*
> + * BCM2835 ARM Timer
> + *
> + * Copyright (C) 2017 Thomas Venriès <thomas.venries@gmail.com>
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2 or later.
> + * See the COPYING file in the top-level directory.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qemu/log.h"
> +#include "qapi/error.h"
> +#include "qemu/timer.h"
> +#include "qemu/main-loop.h"
> +#include "hw/ptimer.h"
> +#include "hw/timer/bcm2835_armtimer.h"
> +#include "trace.h"
> +
> +#define ARM_TIMER_REG_SIZE 0x24
> +
> +/* Register offsets */
> +#define ARM_TIMER_LOAD 0x00
> +#define ARM_TIMER_VALUE 0x04
> +#define ARM_TIMER_CTRL 0x08
> +#define ARM_TIMER_INTCLR 0x0C
> +#define ARM_TIMER_RAW_IRQ 0x10
> +#define ARM_TIMER_MASK_IRQ 0x14
> +#define ARM_TIMER_RELOAD 0x18
> +#define ARM_TIMER_PREDIVIDER 0x1C
> +#define ARM_TIMER_COUNTER 0x20
> +
> +/* Control register masks */
> +#define CTRL_CNT_PRESCALE (0xFF << 16)
> +#define CTRL_CNT_ENABLE (1 << 9)
> +#define CTRL_TIMER_ENABLE (1 << 7)
> +#define CTRL_INT_ENABLE (1 << 5)
> +#define CTRL_TIMER_PRESCALE (3 << 2)
> +#define CTRL_TIMER_SIZE_32BIT (1 << 1)
> +
> +#define CTRL_TIMER_WRAP_MODE 0
> +
> +/* Register reset values */
> +#define CTRL_CNT_PRESCALE_RESET (0x3E << 16)
> +#define ARM_TIMER_CTRL_RESET (CTRL_CNT_PRESCALE_RESET | CTRL_INT_ENABLE)
> +#define ARM_TIMER_IE_READ_VALUE 0x544D5241 /* ASCII "ARMT" */
> +/*
> + The system clock refers to a 250 MHz frequency by default.
> + This frequency can be changed by setting `core_freq` the `config.txt` file.
What config.txt is this referring to?
> + APB clock runs at half the speed of the system clock also called ARM clock.
> +
> + The ARM timer's predivider register is 10 bits wide and can be written
> + or read from. This register has been added as the SP804 expects a 1MHz clock
> + which they do not have. Instead the predivider takes the APB clock
> + and divides it down according to:
> +
> + timer_clock = apb_clock / (prediv + 1)
> +
> + The need is a 1MHz timer clock frequency and BCM2835 ARM Peripherals
> + documentation mentions the predivider reset value is 0x7D (or 125), so
> + the APB clock refers to a 126MHz frequency.
> +
> + Also the additional free-running counter runs from the APB clock and has
> + its own clock predivider controlled by buts 16-23 of the timer control reg:
> +
> + frc_clock = apb_clock / (prediv + 1)
> +
> + The predivider reset value is 0x3E (or 62), knowing APB clock frequency,
> + the FRN clock refers to a 2MHz frequency by default.
> +*/
> +#define ARM_APB_FREQ 126000000UL /* Hz */
> +#define ARM_TIMER_PREDIVIDER_RESET 125 /* MHz */
> +
> +static const uint16_t ctrl_prescale[] = { 1, 16, 256, 1 };
> +
> +static void bcm2835_armtimer_recalibrate(BCM2835ARMTimerState *s, int reload)
> +{
> + uint32_t limit;
> +
> + /* ARM Dual-Timer Module SP804, section 3.2.1:
> + If the Load Register is set to 0 then an interrupt is generated
> + immediately. */
> + if (reload == 2) {
> + limit = s->reload;
> + } else {
> + limit = (s->ctrl & CTRL_TIMER_SIZE_32BIT) ? 0xFFFFFFFF : 0XFFFF;
> + }
> +
> + ptimer_set_limit(s->timer, limit, reload);
> +}
This whole file is very duplicative of code with arm-timer.c. Can
we implement it by adding a property to the arm_timer device that
says "behave like the bcm2835 variant" ?
thanks
-- PMM
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2017-10-11 14:53 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-10-11 9:09 [Qemu-devel] [PATCH] bcm2835_armtimer: add bcm2835 ARM timer Thomas Venriès
2017-10-11 10:43 ` no-reply
2017-10-11 13:24 ` [Qemu-devel] [PATCH v2] " Thomas Venriès
2017-10-11 14:52 ` Peter Maydell
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.