[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH 1/3] timer: add EFM32 timer
From: |
Rabin Vincent |
Subject: |
[Qemu-devel] [PATCH 1/3] timer: add EFM32 timer |
Date: |
Sun, 4 May 2014 17:52:25 +0200 |
Add support for the TIMER block on the EFM32GG.
Signed-off-by: Rabin Vincent <address@hidden>
---
hw/timer/Makefile.objs | 1 +
hw/timer/efm32-timer.c | 204 +++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 205 insertions(+)
create mode 100644 hw/timer/efm32-timer.c
diff --git a/hw/timer/Makefile.objs b/hw/timer/Makefile.objs
index 2c86c3d..440d4ec 100644
--- a/hw/timer/Makefile.objs
+++ b/hw/timer/Makefile.objs
@@ -18,6 +18,7 @@ common-obj-$(CONFIG_IMX) += imx_gpt.o
common-obj-$(CONFIG_LM32) += lm32_timer.o
common-obj-$(CONFIG_MILKYMIST) += milkymist-sysctl.o
+obj-$(CONFIG_EFM32) += efm32-timer.o
obj-$(CONFIG_EXYNOS4) += exynos4210_mct.o
obj-$(CONFIG_EXYNOS4) += exynos4210_pwm.o
obj-$(CONFIG_EXYNOS4) += exynos4210_rtc.o
diff --git a/hw/timer/efm32-timer.c b/hw/timer/efm32-timer.c
new file mode 100644
index 0000000..bbceb43
--- /dev/null
+++ b/hw/timer/efm32-timer.c
@@ -0,0 +1,204 @@
+/*
+ * EFM32GG Timer
+ *
+ * Copyright (C) 2014 Rabin Vincent <address@hidden>
+ *
+ * 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 "hw/sysbus.h"
+#include "sysemu/sysemu.h"
+#include "qemu/timer.h"
+
+enum {
+ TIMER_CTRL = 0x00,
+ TIMER_CMD = 0x04,
+ TIMER_IEN = 0x0C,
+ TIMER_IF = 0x10,
+ TIMER_IFS = 0x14,
+ TIMER_IFC = 0x18,
+ TIMER_CNT = 0x24,
+ TIMER_REG_SIZE = TIMER_CNT + 4,
+
+ TIMER_CTRL_MODE_UP = 0x0,
+ TIMER_CTRL_MODE_DOWN = 0x1,
+
+ TIMER_CMD_START = 1 << 0,
+ TIMER_CMD_STOP = 1 << 1,
+
+ TIMER_IF_OF = 1 << 0,
+ TIMER_IF_UF = 1 << 1,
+};
+
+#define REG(s, x) (s->regs[(x) / 4])
+
+#define TYPE_EFM32_TIMER "efm32-timer"
+#define EFM32_TIMER(obj) \
+ OBJECT_CHECK(Efm32TimerState, (obj), TYPE_EFM32_TIMER)
+
+typedef struct Efm32TimerState {
+ SysBusDevice parent_obj;
+
+ MemoryRegion iomem;
+ uint32_t rate;
+ QEMUTimer *timer;
+ uint32_t regs[TIMER_REG_SIZE / 4];
+ qemu_irq irq;
+} Efm32TimerState;
+
+#define HFPERCLK_RATE 48000000
+
+static uint64_t timer_to_ns(Efm32TimerState *s, uint64_t value)
+{
+ return muldiv64(value, get_ticks_per_sec(), s->rate);
+}
+
+static uint64_t ns_to_timer(Efm32TimerState *s, uint64_t value)
+{
+ return muldiv64(value, s->rate, get_ticks_per_sec());
+}
+
+static uint32_t timer_mode(Efm32TimerState *s)
+{
+ return REG(s, TIMER_CTRL) & 0x3;
+}
+
+static void timer_update_irq(Efm32TimerState *s)
+{
+ qemu_set_irq(s->irq, REG(s, TIMER_IF) & REG(s, TIMER_IEN));
+}
+
+static void timer_tick(void *opaque)
+{
+ Efm32TimerState *s = opaque;
+ uint32_t flag;
+
+ if (timer_mode(s) == TIMER_CTRL_MODE_UP) {
+ flag = TIMER_IF_OF;
+ } else {
+ flag = TIMER_IF_UF;
+ }
+
+ REG(s, TIMER_IF) |= flag;
+
+ timer_update_irq(s);
+}
+
+static uint64_t timer_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ Efm32TimerState *s = opaque;
+
+ switch (offset) {
+ case TIMER_CTRL:
+ return REG(s, TIMER_CTRL);
+ case TIMER_IEN:
+ return REG(s, TIMER_IEN);
+ case TIMER_IF:
+ return REG(s, TIMER_IF);
+ case TIMER_CNT:
+ return ns_to_timer(s, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
+ }
+
+ return 0;
+}
+
+static void timer_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ Efm32TimerState *s = opaque;
+
+ switch (offset) {
+ case TIMER_CTRL:
+ REG(s, TIMER_CTRL) = value;
+ s->rate = HFPERCLK_RATE / (1 << ((value >> 24) & 0xff));
+ break;
+ case TIMER_IEN:
+ REG(s, TIMER_IEN) = value;
+ break;
+ case TIMER_IFS:
+ REG(s, TIMER_IF) |= value;
+ break;
+ case TIMER_IFC:
+ REG(s, TIMER_IF) &= ~value;
+ break;
+ case TIMER_CMD:
+ if (value & TIMER_CMD_START) {
+ uint32_t expiry;
+
+ if (timer_mode(s) == TIMER_CTRL_MODE_UP) {
+ expiry = 0xffffffff - REG(s, TIMER_CNT);
+ } else if (timer_mode(s) == TIMER_CTRL_MODE_DOWN) {
+ expiry = REG(s, TIMER_CNT);
+ }
+
+ timer_mod(s->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) \
+ + timer_to_ns(s, expiry));
+ } else if (value & TIMER_CMD_STOP) {
+ timer_del(s->timer);
+ }
+ break;
+ case TIMER_CNT:
+ REG(s, TIMER_CNT) = value;
+ break;
+ }
+
+ timer_update_irq(s);
+}
+
+static const MemoryRegionOps timer_ops = {
+ .read = timer_read,
+ .write = timer_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static const VMStateDescription vmstate_efm32_timer = {
+ .name = "efm32-timer",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(rate, Efm32TimerState),
+ VMSTATE_TIMER(timer, Efm32TimerState),
+ VMSTATE_UINT32_ARRAY(regs, Efm32TimerState, TIMER_REG_SIZE / 4),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static int efm32_timer_init(SysBusDevice *sbd)
+{
+ DeviceState *dev = DEVICE(sbd);
+ Efm32TimerState *s = EFM32_TIMER(dev);
+
+ sysbus_init_irq(sbd, &s->irq);
+
+ memory_region_init_io(&s->iomem, OBJECT(s), &timer_ops, s,
+ "timer", 0x400);
+ sysbus_init_mmio(sbd, &s->iomem);
+
+ s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, timer_tick, s);
+ vmstate_register(dev, -1, &vmstate_efm32_timer, s);
+ return 0;
+}
+
+static void efm32_timer_class_init(ObjectClass *klass, void *data)
+{
+ SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+
+ sdc->init = efm32_timer_init;
+}
+
+static const TypeInfo efm32_timer_info = {
+ .name = TYPE_EFM32_TIMER,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(Efm32TimerState),
+ .class_init = efm32_timer_class_init,
+};
+
+static void efm32_register_types(void)
+{
+ type_register_static(&efm32_timer_info);
+}
+
+type_init(efm32_register_types)
--
2.0.0.rc0
- [Qemu-devel] [PATCH 1/3] timer: add EFM32 timer,
Rabin Vincent <=