Signed-off-by: liguang <lig.f...@cn.fujitsu.com> --- hw/arm/sunxi-soc.c | 209 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 209 insertions(+), 0 deletions(-)
diff --git a/hw/arm/sunxi-soc.c b/hw/arm/sunxi-soc.c index 1b369ba..960539a 100644 --- a/hw/arm/sunxi-soc.c +++ b/hw/arm/sunxi-soc.c @@ -2,6 +2,7 @@ #include "hw/devices.h" #include "hw/boards.h" #include "hw/arm/arm.h" +#include "hw/ptimer.h" #include "sysemu/sysemu.h" @@ -296,6 +297,214 @@ static const TypeInfo sunxi_pic_info = { .class_init = sunxi_pic_class_init, }; + +#define TYPE_SUNXI_PIT "sunix-timer" +#define SUNXI_PIT(obj) OBJECT_CHECK(SunxiPITState, (obj), TYPE_SUNXI_PIT) + +#define SUNXI_TIMER_NR 6 +#define SUNXI_TIMER_IRQ 0x1 +#define SUNXI_WDOG_IRQ 0x100 + +#define SUNXI_TIMER_IRQ_EN 0 +#define SUNXI_TIMER_IRQ_ST 0x4 +#define SUNXI_TIMER_CONTROL 0x0 +#define SUNXI_TIMER_INTERVAL 0x4 +#define SUNXI_TIMER_COUNT 0x8 +#define SUNXI_WDOG_CONTROL 0x90 +#define SUNXI_WDOG_MODE 0x94 +#define SUNXI_WDOG_COUNT_LO 0xa4 +#define SUNXI_WDOG_COUNT_HI 0xa8 +#define SUNXI_TIMER_BASE 0x10 + +typedef struct SunxiPITState { + SysBusDevice busdev; + qemu_irq irq[SUNXI_TIMER_NR]; + ptimer_state *timer[6]; + MemoryRegion iomem; + uint32_t irq_enable; + uint32_t irq_status; + uint32_t control[6]; + uint32_t interval[6]; + uint32_t count[6]; + uint32_t watch_dog_mode; + uint32_t watch_dog_control; + uint32_t watch_dog_count_lo; + uint32_t watch_dog_count_hi; +} SunxiPITState; + +static uint64_t sunxi_pit_read(void *opaque, hwaddr offset, unsigned size) +{ + SunxiPITState *s = SUNXI_PIT(opaque); + uint8_t index = 0; + + switch (offset) { + case SUNXI_TIMER_IRQ_EN: + return s->irq_enable; + break; + case SUNXI_TIMER_IRQ_ST: + return s->irq_status; + break; + case SUNXI_TIMER_BASE ... SUNXI_TIMER_BASE * 6 + SUNXI_TIMER_COUNT: + index = offset & 0xf0; + switch (offset & 0x0f) { + case SUNXI_TIMER_CONTROL: + return s->control[index]; + break; + case SUNXI_TIMER_INTERVAL: + return s->interval[index]; + break; + case SUNXI_TIMER_COUNT: + s->count[index] = ptimer_get_count(s->timer[index]); + return s->count[index]; + default: + break; + } + break; + case SUNXI_WDOG_CONTROL: + break; + case SUNXI_WDOG_MODE: + break; + case SUNXI_WDOG_COUNT_LO: + break; + case SUNXI_WDOG_COUNT_HI: + default: + break; + } + + return 0; +} + +static void sunxi_pit_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + SunxiPITState *s = SUNXI_PIT(opaque); + uint8_t index = 0; + + switch (offset) { + case SUNXI_TIMER_IRQ_EN: + s->irq_enable = value; + break; + case SUNXI_TIMER_IRQ_ST: + for (index = 0; index < sizeof(uint32_t); index++) { + if (test_bit(index, (void *)&value)) { + clear_bit(index, (void *)&s->irq_status); + } + } + break; + case SUNXI_TIMER_BASE ... SUNXI_TIMER_BASE * 6 + SUNXI_TIMER_COUNT: + index = (offset & 0xf0) - 1; + index >>= 4; + switch (offset & 0x0f) { + case SUNXI_TIMER_CONTROL: + s->control[index] = value; + if (s->control[index] & 0x1) { + ptimer_run(s->timer[index], 0); + } else { + ptimer_stop(s->timer[index]); + } + break; + case SUNXI_TIMER_INTERVAL: + s->interval[index] = value; + ptimer_set_limit(s->timer[index], s->interval[index], + s->control[index] & 0x2); + break; + case SUNXI_TIMER_COUNT: + s->count[index] = value; + default: + break; + } + break; + case SUNXI_WDOG_CONTROL: + s->watch_dog_control = value; + break; + case SUNXI_WDOG_MODE: + s->watch_dog_mode = value; + break; + case SUNXI_WDOG_COUNT_LO: + s->watch_dog_count_lo = value; + break; + case SUNXI_WDOG_COUNT_HI: + s->watch_dog_count_hi = value; + default: + break; + } +} + +static const MemoryRegionOps sunxi_pit_ops = { + .read = sunxi_pit_read, + .write = sunxi_pit_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void sunxi_pit_reset(DeviceState *dev) +{ + SunxiPITState *s = SUNXI_PIT(dev); + uint8_t i = 0; + + s->irq_enable = 0; + s->irq_status = 0; + for (i = 0; i < 6; i++) { + s->control[i] = 0x4; + s->interval[i] = 0; + s->count[i] = 0; + ptimer_stop(s->timer[i]); + ptimer_set_limit(s->timer[i], 0XFFFFFFFFUL, 1); + } + s->watch_dog_mode = 0; + s->watch_dog_control = 0; + s->watch_dog_count_lo = 0; + s->watch_dog_count_hi = 0; +} + +static void sunxi_pit_timer_cb(void *opaque) +{ + SunxiPITState *s = SUNXI_PIT(opaque); + uint8_t i = 0; + + for (i = 0; i < SUNXI_TIMER_NR; i++) { + if (s->irq_status & s->irq_enable & (1 << i)) { + qemu_irq_raise(s->irq[i]); + } else { + qemu_irq_raise(s->irq[i]); + } + } +} + +static void sunxi_pit_realize(DeviceState *dev, Error **errp) +{ + SunxiPITState *s = SUNXI_PIT(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + QEMUBH *bh; + uint8_t i = 0; + + for (i = 0; i < SUNXI_TIMER_NR; i++) { + sysbus_init_irq(sbd, &s->irq[i]); + } + memory_region_init_io(&s->iomem, OBJECT(s), &sunxi_pit_ops, s, + TYPE_SUNXI_PIT, 0x400); + sysbus_init_mmio(sbd, &s->iomem); + bh = qemu_bh_new(sunxi_pit_timer_cb, s); + for (i = 0; i < SUNXI_TIMER_NR; i++) { + s->timer[i] = ptimer_init(bh); + } +} + +static void sunxi_pit_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = sunxi_pit_realize; + dc->reset = sunxi_pit_reset; + dc->desc = "sunxi timer"; +} + +static const TypeInfo sunxi_pit_info = { + .name = TYPE_SUNXI_PIT, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(SunxiPITState), + .class_init = sunxi_pit_class_init, +}; + static void sunxi_init(QEMUMachineInitArgs *args) { } -- 1.7.2.5