aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--hw/arm/Kconfig1
-rw-r--r--hw/arm/stellaris.c321
-rw-r--r--hw/timer/Kconfig3
-rw-r--r--hw/timer/meson.build1
-rw-r--r--hw/timer/stellaris-gptm.c314
-rw-r--r--include/hw/timer/stellaris-gptm.h48
6 files changed, 368 insertions, 320 deletions
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index dc050b5..78fdd1b 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -235,6 +235,7 @@ config STELLARIS
select SSI_SD
select STELLARIS_INPUT
select STELLARIS_ENET # ethernet
+ select STELLARIS_GPTM # general purpose timer module
select UNIMP
config STM32VLDISCOVERY
diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c
index a32c567..8c547f1 100644
--- a/hw/arm/stellaris.c
+++ b/hw/arm/stellaris.c
@@ -26,6 +26,7 @@
#include "hw/watchdog/cmsdk-apb-watchdog.h"
#include "migration/vmstate.h"
#include "hw/misc/unimp.h"
+#include "hw/timer/stellaris-gptm.h"
#include "hw/qdev-clock.h"
#include "qom/object.h"
@@ -55,309 +56,6 @@ typedef const struct {
uint32_t peripherals;
} stellaris_board_info;
-/* General purpose timer module. */
-
-#define TYPE_STELLARIS_GPTM "stellaris-gptm"
-OBJECT_DECLARE_SIMPLE_TYPE(gptm_state, STELLARIS_GPTM)
-
-struct gptm_state {
- SysBusDevice parent_obj;
-
- MemoryRegion iomem;
- uint32_t config;
- uint32_t mode[2];
- uint32_t control;
- uint32_t state;
- uint32_t mask;
- uint32_t load[2];
- uint32_t match[2];
- uint32_t prescale[2];
- uint32_t match_prescale[2];
- uint32_t rtc;
- int64_t tick[2];
- struct gptm_state *opaque[2];
- QEMUTimer *timer[2];
- /* The timers have an alternate output used to trigger the ADC. */
- qemu_irq trigger;
- qemu_irq irq;
-};
-
-static void gptm_update_irq(gptm_state *s)
-{
- int level;
- level = (s->state & s->mask) != 0;
- qemu_set_irq(s->irq, level);
-}
-
-static void gptm_stop(gptm_state *s, int n)
-{
- timer_del(s->timer[n]);
-}
-
-static void gptm_reload(gptm_state *s, int n, int reset)
-{
- int64_t tick;
- if (reset) {
- tick = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
- } else {
- tick = s->tick[n];
- }
-
- if (s->config == 0) {
- /* 32-bit CountDown. */
- uint32_t count;
- count = s->load[0] | (s->load[1] << 16);
- tick += (int64_t)count * system_clock_scale;
- } else if (s->config == 1) {
- /* 32-bit RTC. 1Hz tick. */
- tick += NANOSECONDS_PER_SECOND;
- } else if (s->mode[n] == 0xa) {
- /* PWM mode. Not implemented. */
- } else {
- qemu_log_mask(LOG_UNIMP,
- "GPTM: 16-bit timer mode unimplemented: 0x%x\n",
- s->mode[n]);
- return;
- }
- s->tick[n] = tick;
- timer_mod(s->timer[n], tick);
-}
-
-static void gptm_tick(void *opaque)
-{
- gptm_state **p = (gptm_state **)opaque;
- gptm_state *s;
- int n;
-
- s = *p;
- n = p - s->opaque;
- if (s->config == 0) {
- s->state |= 1;
- if ((s->control & 0x20)) {
- /* Output trigger. */
- qemu_irq_pulse(s->trigger);
- }
- if (s->mode[0] & 1) {
- /* One-shot. */
- s->control &= ~1;
- } else {
- /* Periodic. */
- gptm_reload(s, 0, 0);
- }
- } else if (s->config == 1) {
- /* RTC. */
- uint32_t match;
- s->rtc++;
- match = s->match[0] | (s->match[1] << 16);
- if (s->rtc > match)
- s->rtc = 0;
- if (s->rtc == 0) {
- s->state |= 8;
- }
- gptm_reload(s, 0, 0);
- } else if (s->mode[n] == 0xa) {
- /* PWM mode. Not implemented. */
- } else {
- qemu_log_mask(LOG_UNIMP,
- "GPTM: 16-bit timer mode unimplemented: 0x%x\n",
- s->mode[n]);
- }
- gptm_update_irq(s);
-}
-
-static uint64_t gptm_read(void *opaque, hwaddr offset,
- unsigned size)
-{
- gptm_state *s = (gptm_state *)opaque;
-
- switch (offset) {
- case 0x00: /* CFG */
- return s->config;
- case 0x04: /* TAMR */
- return s->mode[0];
- case 0x08: /* TBMR */
- return s->mode[1];
- case 0x0c: /* CTL */
- return s->control;
- case 0x18: /* IMR */
- return s->mask;
- case 0x1c: /* RIS */
- return s->state;
- case 0x20: /* MIS */
- return s->state & s->mask;
- case 0x24: /* CR */
- return 0;
- case 0x28: /* TAILR */
- return s->load[0] | ((s->config < 4) ? (s->load[1] << 16) : 0);
- case 0x2c: /* TBILR */
- return s->load[1];
- case 0x30: /* TAMARCHR */
- return s->match[0] | ((s->config < 4) ? (s->match[1] << 16) : 0);
- case 0x34: /* TBMATCHR */
- return s->match[1];
- case 0x38: /* TAPR */
- return s->prescale[0];
- case 0x3c: /* TBPR */
- return s->prescale[1];
- case 0x40: /* TAPMR */
- return s->match_prescale[0];
- case 0x44: /* TBPMR */
- return s->match_prescale[1];
- case 0x48: /* TAR */
- if (s->config == 1) {
- return s->rtc;
- }
- qemu_log_mask(LOG_UNIMP,
- "GPTM: read of TAR but timer read not supported\n");
- return 0;
- case 0x4c: /* TBR */
- qemu_log_mask(LOG_UNIMP,
- "GPTM: read of TBR but timer read not supported\n");
- return 0;
- default:
- qemu_log_mask(LOG_GUEST_ERROR,
- "GPTM: read at bad offset 0x02%" HWADDR_PRIx "\n",
- offset);
- return 0;
- }
-}
-
-static void gptm_write(void *opaque, hwaddr offset,
- uint64_t value, unsigned size)
-{
- gptm_state *s = (gptm_state *)opaque;
- uint32_t oldval;
-
- /*
- * The timers should be disabled before changing the configuration.
- * We take advantage of this and defer everything until the timer
- * is enabled.
- */
- switch (offset) {
- case 0x00: /* CFG */
- s->config = value;
- break;
- case 0x04: /* TAMR */
- s->mode[0] = value;
- break;
- case 0x08: /* TBMR */
- s->mode[1] = value;
- break;
- case 0x0c: /* CTL */
- oldval = s->control;
- s->control = value;
- /* TODO: Implement pause. */
- if ((oldval ^ value) & 1) {
- if (value & 1) {
- gptm_reload(s, 0, 1);
- } else {
- gptm_stop(s, 0);
- }
- }
- if (((oldval ^ value) & 0x100) && s->config >= 4) {
- if (value & 0x100) {
- gptm_reload(s, 1, 1);
- } else {
- gptm_stop(s, 1);
- }
- }
- break;
- case 0x18: /* IMR */
- s->mask = value & 0x77;
- gptm_update_irq(s);
- break;
- case 0x24: /* CR */
- s->state &= ~value;
- break;
- case 0x28: /* TAILR */
- s->load[0] = value & 0xffff;
- if (s->config < 4) {
- s->load[1] = value >> 16;
- }
- break;
- case 0x2c: /* TBILR */
- s->load[1] = value & 0xffff;
- break;
- case 0x30: /* TAMARCHR */
- s->match[0] = value & 0xffff;
- if (s->config < 4) {
- s->match[1] = value >> 16;
- }
- break;
- case 0x34: /* TBMATCHR */
- s->match[1] = value >> 16;
- break;
- case 0x38: /* TAPR */
- s->prescale[0] = value;
- break;
- case 0x3c: /* TBPR */
- s->prescale[1] = value;
- break;
- case 0x40: /* TAPMR */
- s->match_prescale[0] = value;
- break;
- case 0x44: /* TBPMR */
- s->match_prescale[0] = value;
- break;
- default:
- qemu_log_mask(LOG_GUEST_ERROR,
- "GPTM: write at bad offset 0x02%" HWADDR_PRIx "\n",
- offset);
- }
- gptm_update_irq(s);
-}
-
-static const MemoryRegionOps gptm_ops = {
- .read = gptm_read,
- .write = gptm_write,
- .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static const VMStateDescription vmstate_stellaris_gptm = {
- .name = "stellaris_gptm",
- .version_id = 1,
- .minimum_version_id = 1,
- .fields = (VMStateField[]) {
- VMSTATE_UINT32(config, gptm_state),
- VMSTATE_UINT32_ARRAY(mode, gptm_state, 2),
- VMSTATE_UINT32(control, gptm_state),
- VMSTATE_UINT32(state, gptm_state),
- VMSTATE_UINT32(mask, gptm_state),
- VMSTATE_UNUSED(8),
- VMSTATE_UINT32_ARRAY(load, gptm_state, 2),
- VMSTATE_UINT32_ARRAY(match, gptm_state, 2),
- VMSTATE_UINT32_ARRAY(prescale, gptm_state, 2),
- VMSTATE_UINT32_ARRAY(match_prescale, gptm_state, 2),
- VMSTATE_UINT32(rtc, gptm_state),
- VMSTATE_INT64_ARRAY(tick, gptm_state, 2),
- VMSTATE_TIMER_PTR_ARRAY(timer, gptm_state, 2),
- VMSTATE_END_OF_LIST()
- }
-};
-
-static void stellaris_gptm_init(Object *obj)
-{
- DeviceState *dev = DEVICE(obj);
- gptm_state *s = STELLARIS_GPTM(obj);
- SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
-
- sysbus_init_irq(sbd, &s->irq);
- qdev_init_gpio_out(dev, &s->trigger, 1);
-
- memory_region_init_io(&s->iomem, obj, &gptm_ops, s,
- "gptm", 0x1000);
- sysbus_init_mmio(sbd, &s->iomem);
-
- s->opaque[0] = s->opaque[1] = s;
-}
-
-static void stellaris_gptm_realize(DeviceState *dev, Error **errp)
-{
- gptm_state *s = STELLARIS_GPTM(dev);
- s->timer[0] = timer_new_ns(QEMU_CLOCK_VIRTUAL, gptm_tick, &s->opaque[0]);
- s->timer[1] = timer_new_ns(QEMU_CLOCK_VIRTUAL, gptm_tick, &s->opaque[1]);
-}
-
/* System controller. */
#define TYPE_STELLARIS_SYS "stellaris-sys"
@@ -1642,22 +1340,6 @@ static const TypeInfo stellaris_i2c_info = {
.class_init = stellaris_i2c_class_init,
};
-static void stellaris_gptm_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
-
- dc->vmsd = &vmstate_stellaris_gptm;
- dc->realize = stellaris_gptm_realize;
-}
-
-static const TypeInfo stellaris_gptm_info = {
- .name = TYPE_STELLARIS_GPTM,
- .parent = TYPE_SYS_BUS_DEVICE,
- .instance_size = sizeof(gptm_state),
- .instance_init = stellaris_gptm_init,
- .class_init = stellaris_gptm_class_init,
-};
-
static void stellaris_adc_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
@@ -1696,7 +1378,6 @@ static const TypeInfo stellaris_sys_info = {
static void stellaris_register_types(void)
{
type_register_static(&stellaris_i2c_info);
- type_register_static(&stellaris_gptm_info);
type_register_static(&stellaris_adc_info);
type_register_static(&stellaris_sys_info);
}
diff --git a/hw/timer/Kconfig b/hw/timer/Kconfig
index bac2511..1e73da7 100644
--- a/hw/timer/Kconfig
+++ b/hw/timer/Kconfig
@@ -52,5 +52,8 @@ config SSE_COUNTER
config SSE_TIMER
bool
+config STELLARIS_GPTM
+ bool
+
config AVR_TIMER16
bool
diff --git a/hw/timer/meson.build b/hw/timer/meson.build
index 1aa3cd2..e67478a 100644
--- a/hw/timer/meson.build
+++ b/hw/timer/meson.build
@@ -31,6 +31,7 @@ softmmu_ss.add(when: 'CONFIG_SH_TIMER', if_true: files('sh_timer.c'))
softmmu_ss.add(when: 'CONFIG_SLAVIO', if_true: files('slavio_timer.c'))
softmmu_ss.add(when: 'CONFIG_SSE_COUNTER', if_true: files('sse-counter.c'))
softmmu_ss.add(when: 'CONFIG_SSE_TIMER', if_true: files('sse-timer.c'))
+softmmu_ss.add(when: 'CONFIG_STELLARIS_GPTM', if_true: files('stellaris-gptm.c'))
softmmu_ss.add(when: 'CONFIG_STM32F2XX_TIMER', if_true: files('stm32f2xx_timer.c'))
softmmu_ss.add(when: 'CONFIG_XILINX', if_true: files('xilinx_timer.c'))
specific_ss.add(when: 'CONFIG_IBEX', if_true: files('ibex_timer.c'))
diff --git a/hw/timer/stellaris-gptm.c b/hw/timer/stellaris-gptm.c
new file mode 100644
index 0000000..7846fe5
--- /dev/null
+++ b/hw/timer/stellaris-gptm.c
@@ -0,0 +1,314 @@
+/*
+ * Luminary Micro Stellaris General Purpose Timer Module
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licensed under the GPL.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qemu/timer.h"
+#include "migration/vmstate.h"
+#include "hw/timer/stellaris-gptm.h"
+#include "hw/timer/armv7m_systick.h" /* Needed only for system_clock_scale */
+
+static void gptm_update_irq(gptm_state *s)
+{
+ int level;
+ level = (s->state & s->mask) != 0;
+ qemu_set_irq(s->irq, level);
+}
+
+static void gptm_stop(gptm_state *s, int n)
+{
+ timer_del(s->timer[n]);
+}
+
+static void gptm_reload(gptm_state *s, int n, int reset)
+{
+ int64_t tick;
+ if (reset) {
+ tick = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ } else {
+ tick = s->tick[n];
+ }
+
+ if (s->config == 0) {
+ /* 32-bit CountDown. */
+ uint32_t count;
+ count = s->load[0] | (s->load[1] << 16);
+ tick += (int64_t)count * system_clock_scale;
+ } else if (s->config == 1) {
+ /* 32-bit RTC. 1Hz tick. */
+ tick += NANOSECONDS_PER_SECOND;
+ } else if (s->mode[n] == 0xa) {
+ /* PWM mode. Not implemented. */
+ } else {
+ qemu_log_mask(LOG_UNIMP,
+ "GPTM: 16-bit timer mode unimplemented: 0x%x\n",
+ s->mode[n]);
+ return;
+ }
+ s->tick[n] = tick;
+ timer_mod(s->timer[n], tick);
+}
+
+static void gptm_tick(void *opaque)
+{
+ gptm_state **p = (gptm_state **)opaque;
+ gptm_state *s;
+ int n;
+
+ s = *p;
+ n = p - s->opaque;
+ if (s->config == 0) {
+ s->state |= 1;
+ if ((s->control & 0x20)) {
+ /* Output trigger. */
+ qemu_irq_pulse(s->trigger);
+ }
+ if (s->mode[0] & 1) {
+ /* One-shot. */
+ s->control &= ~1;
+ } else {
+ /* Periodic. */
+ gptm_reload(s, 0, 0);
+ }
+ } else if (s->config == 1) {
+ /* RTC. */
+ uint32_t match;
+ s->rtc++;
+ match = s->match[0] | (s->match[1] << 16);
+ if (s->rtc > match)
+ s->rtc = 0;
+ if (s->rtc == 0) {
+ s->state |= 8;
+ }
+ gptm_reload(s, 0, 0);
+ } else if (s->mode[n] == 0xa) {
+ /* PWM mode. Not implemented. */
+ } else {
+ qemu_log_mask(LOG_UNIMP,
+ "GPTM: 16-bit timer mode unimplemented: 0x%x\n",
+ s->mode[n]);
+ }
+ gptm_update_irq(s);
+}
+
+static uint64_t gptm_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ gptm_state *s = (gptm_state *)opaque;
+
+ switch (offset) {
+ case 0x00: /* CFG */
+ return s->config;
+ case 0x04: /* TAMR */
+ return s->mode[0];
+ case 0x08: /* TBMR */
+ return s->mode[1];
+ case 0x0c: /* CTL */
+ return s->control;
+ case 0x18: /* IMR */
+ return s->mask;
+ case 0x1c: /* RIS */
+ return s->state;
+ case 0x20: /* MIS */
+ return s->state & s->mask;
+ case 0x24: /* CR */
+ return 0;
+ case 0x28: /* TAILR */
+ return s->load[0] | ((s->config < 4) ? (s->load[1] << 16) : 0);
+ case 0x2c: /* TBILR */
+ return s->load[1];
+ case 0x30: /* TAMARCHR */
+ return s->match[0] | ((s->config < 4) ? (s->match[1] << 16) : 0);
+ case 0x34: /* TBMATCHR */
+ return s->match[1];
+ case 0x38: /* TAPR */
+ return s->prescale[0];
+ case 0x3c: /* TBPR */
+ return s->prescale[1];
+ case 0x40: /* TAPMR */
+ return s->match_prescale[0];
+ case 0x44: /* TBPMR */
+ return s->match_prescale[1];
+ case 0x48: /* TAR */
+ if (s->config == 1) {
+ return s->rtc;
+ }
+ qemu_log_mask(LOG_UNIMP,
+ "GPTM: read of TAR but timer read not supported\n");
+ return 0;
+ case 0x4c: /* TBR */
+ qemu_log_mask(LOG_UNIMP,
+ "GPTM: read of TBR but timer read not supported\n");
+ return 0;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "GPTM: read at bad offset 0x02%" HWADDR_PRIx "\n",
+ offset);
+ return 0;
+ }
+}
+
+static void gptm_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ gptm_state *s = (gptm_state *)opaque;
+ uint32_t oldval;
+
+ /*
+ * The timers should be disabled before changing the configuration.
+ * We take advantage of this and defer everything until the timer
+ * is enabled.
+ */
+ switch (offset) {
+ case 0x00: /* CFG */
+ s->config = value;
+ break;
+ case 0x04: /* TAMR */
+ s->mode[0] = value;
+ break;
+ case 0x08: /* TBMR */
+ s->mode[1] = value;
+ break;
+ case 0x0c: /* CTL */
+ oldval = s->control;
+ s->control = value;
+ /* TODO: Implement pause. */
+ if ((oldval ^ value) & 1) {
+ if (value & 1) {
+ gptm_reload(s, 0, 1);
+ } else {
+ gptm_stop(s, 0);
+ }
+ }
+ if (((oldval ^ value) & 0x100) && s->config >= 4) {
+ if (value & 0x100) {
+ gptm_reload(s, 1, 1);
+ } else {
+ gptm_stop(s, 1);
+ }
+ }
+ break;
+ case 0x18: /* IMR */
+ s->mask = value & 0x77;
+ gptm_update_irq(s);
+ break;
+ case 0x24: /* CR */
+ s->state &= ~value;
+ break;
+ case 0x28: /* TAILR */
+ s->load[0] = value & 0xffff;
+ if (s->config < 4) {
+ s->load[1] = value >> 16;
+ }
+ break;
+ case 0x2c: /* TBILR */
+ s->load[1] = value & 0xffff;
+ break;
+ case 0x30: /* TAMARCHR */
+ s->match[0] = value & 0xffff;
+ if (s->config < 4) {
+ s->match[1] = value >> 16;
+ }
+ break;
+ case 0x34: /* TBMATCHR */
+ s->match[1] = value >> 16;
+ break;
+ case 0x38: /* TAPR */
+ s->prescale[0] = value;
+ break;
+ case 0x3c: /* TBPR */
+ s->prescale[1] = value;
+ break;
+ case 0x40: /* TAPMR */
+ s->match_prescale[0] = value;
+ break;
+ case 0x44: /* TBPMR */
+ s->match_prescale[0] = value;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "GPTM: write at bad offset 0x02%" HWADDR_PRIx "\n",
+ offset);
+ }
+ gptm_update_irq(s);
+}
+
+static const MemoryRegionOps gptm_ops = {
+ .read = gptm_read,
+ .write = gptm_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static const VMStateDescription vmstate_stellaris_gptm = {
+ .name = "stellaris_gptm",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(config, gptm_state),
+ VMSTATE_UINT32_ARRAY(mode, gptm_state, 2),
+ VMSTATE_UINT32(control, gptm_state),
+ VMSTATE_UINT32(state, gptm_state),
+ VMSTATE_UINT32(mask, gptm_state),
+ VMSTATE_UNUSED(8),
+ VMSTATE_UINT32_ARRAY(load, gptm_state, 2),
+ VMSTATE_UINT32_ARRAY(match, gptm_state, 2),
+ VMSTATE_UINT32_ARRAY(prescale, gptm_state, 2),
+ VMSTATE_UINT32_ARRAY(match_prescale, gptm_state, 2),
+ VMSTATE_UINT32(rtc, gptm_state),
+ VMSTATE_INT64_ARRAY(tick, gptm_state, 2),
+ VMSTATE_TIMER_PTR_ARRAY(timer, gptm_state, 2),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void stellaris_gptm_init(Object *obj)
+{
+ DeviceState *dev = DEVICE(obj);
+ gptm_state *s = STELLARIS_GPTM(obj);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+
+ sysbus_init_irq(sbd, &s->irq);
+ qdev_init_gpio_out(dev, &s->trigger, 1);
+
+ memory_region_init_io(&s->iomem, obj, &gptm_ops, s,
+ "gptm", 0x1000);
+ sysbus_init_mmio(sbd, &s->iomem);
+
+ s->opaque[0] = s->opaque[1] = s;
+}
+
+static void stellaris_gptm_realize(DeviceState *dev, Error **errp)
+{
+ gptm_state *s = STELLARIS_GPTM(dev);
+ s->timer[0] = timer_new_ns(QEMU_CLOCK_VIRTUAL, gptm_tick, &s->opaque[0]);
+ s->timer[1] = timer_new_ns(QEMU_CLOCK_VIRTUAL, gptm_tick, &s->opaque[1]);
+}
+
+static void stellaris_gptm_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->vmsd = &vmstate_stellaris_gptm;
+ dc->realize = stellaris_gptm_realize;
+}
+
+static const TypeInfo stellaris_gptm_info = {
+ .name = TYPE_STELLARIS_GPTM,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(gptm_state),
+ .instance_init = stellaris_gptm_init,
+ .class_init = stellaris_gptm_class_init,
+};
+
+static void stellaris_gptm_register_types(void)
+{
+ type_register_static(&stellaris_gptm_info);
+}
+
+type_init(stellaris_gptm_register_types)
diff --git a/include/hw/timer/stellaris-gptm.h b/include/hw/timer/stellaris-gptm.h
new file mode 100644
index 0000000..b8fa43c
--- /dev/null
+++ b/include/hw/timer/stellaris-gptm.h
@@ -0,0 +1,48 @@
+/*
+ * Luminary Micro Stellaris General Purpose Timer Module
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licensed under the GPL.
+ */
+
+#ifndef HW_TIMER_STELLARIS_GPTM_H
+#define HW_TIMER_STELLARIS_GPTM_H
+
+#include "qom/object.h"
+#include "hw/sysbus.h"
+#include "hw/irq.h"
+
+#define TYPE_STELLARIS_GPTM "stellaris-gptm"
+OBJECT_DECLARE_SIMPLE_TYPE(gptm_state, STELLARIS_GPTM)
+
+/*
+ * QEMU interface:
+ * + sysbus MMIO region 0: register bank
+ * + sysbus IRQ 0: timer interrupt
+ * + unnamed GPIO output 0: trigger output for the ADC
+ */
+struct gptm_state {
+ SysBusDevice parent_obj;
+
+ MemoryRegion iomem;
+ uint32_t config;
+ uint32_t mode[2];
+ uint32_t control;
+ uint32_t state;
+ uint32_t mask;
+ uint32_t load[2];
+ uint32_t match[2];
+ uint32_t prescale[2];
+ uint32_t match_prescale[2];
+ uint32_t rtc;
+ int64_t tick[2];
+ struct gptm_state *opaque[2];
+ QEMUTimer *timer[2];
+ /* The timers have an alternate output used to trigger the ADC. */
+ qemu_irq trigger;
+ qemu_irq irq;
+};
+
+#endif