Message ID | 20111130034234.561208677@nicta.com.au |
---|---|
State | New |
Headers | show |
On 30 November 2011 03:36, Peter Chubb <peter.chubb@nicta.com.au> wrote: > > Signed-off-by: Hans Jang <hsjang@ok-labs.com> > Signed-off-by: Adam Clench <adamc@ok-labs.com> > Signed-off-by: Peter Chubb <peter.chubb@nicta.com.au> > --- > Makefile.target | 2 > hw/imx_timer.c | 460 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > 2 files changed, 461 insertions(+), 1 deletion(-) > create mode 100644 hw/imx_timer.c > > Index: qemu-working/hw/imx_timer.c > =================================================================== > --- /dev/null 1970-01-01 00:00:00.000000000 +0000 > +++ qemu-working/hw/imx_timer.c 2011-11-30 13:38:25.818785258 +1100 > @@ -0,0 +1,460 @@ > +/* > + * IMX31 Timer > + * > + * Copyright (c) 2008 OKL > + * Copyright (c) 2011 NICTA Pty Ltd > + * Originally Written by Hans Jiang > + * Updated by Peter Chubb > + * > + * This code is licenced under GPL version 2 or later. See > + * the COPYING file in the top-level directory. > + */ > + > +#include "hw.h" > +#include "qemu-timer.h" > +#include "sysbus.h" > + > +//#define DEBUG_TIMER 1 > + > +#ifdef DEBUG_TIMER > +# define DPRINTF(fmt, args...) \ > + do { printf("imx_timer: " fmt , ##args); } while (0) > +#else > +# define DPRINTF(fmt, args...) do {} while (0) > +#endif > + > +/* > + * Define to 1 for messages about attempts to > + * access unimplemented registers or similar. > + */ > +#define DEBUG_IMPLEMENTATION 1 > +#if DEBUG_IMPLEMENTATION > +# define IPRINTF(fmt, args...) \ > + do { fprintf(stderr, "imx_timer: " fmt, ##args); } while (0) > +#else > +# define IPRINTF(fmt, args...) do {} while (0) > +#endif > + > +/* > + * GPT : General purpose timer > + */ > + > +#define TIMER_MAX 0xFFFFFFFFUL > +#define GPT_FREQ 50000000 /* Hz == 50 MHz */ > + > +/* Control register. Not all of these bits have any effect (yet) */ > +#define GPT_CR_EN (1 << 0) /* GPT Enable */ > +#define GPT_CR_ENMODE (1 << 1) /* GPT Enable Mode */ > +#define GPT_CR_DBGEN (1 << 2) /* GPT Debug mode enable */ > +#define GPT_CR_WAITEN (1 << 3) /* GPT Wait Mode Enable */ > +#define GPT_CR_DOZEN (1 << 4) /* GPT Doze mode enable */ > +#define GPT_CR_STOPEN (1 << 5) /* GPT Stop Mode Enable */ > +#define GPT_CR_CLKSRC (7 << 6) /* Clock source select (3 bits) */ > +#define GPT_CR_FRR (1 << 9) /* Freerun or Restart */ > +#define GPT_CR_SWR (1 << 15) > +#define GPT_CR_IM1 (3 << 16) /* Input capture channel 1 mode (2 bits) */ > +#define GPT_CR_IM2 (3 << 18) /* Input capture channel 2 mode (2 bits) */ > +#define GPT_CR_OM1 (7 << 20) /* Output Compare Channel 1 Mode (3 bits) */ > +#define GPT_CR_OM2 (7 << 23) /* Output Compare Channel 2 Mode (3 bits) */ > +#define GPT_CR_OM3 (7 << 26) /* Output Compare Channel 3 Mode (3 bits) */ > +#define GPT_CR_FO1 (1 << 29) /* Force Output Compare Channel 1 */ > +#define GPT_CR_FO2 (1 << 30) /* Force Output Compare Channel 2 */ > +#define GPT_CR_FO3 (1 << 31) /* Force Output Compare Channel 3 */ > + > +#define GPT_SR_OF1 (1 << 0) > +#define GPT_SR_ROV (1 << 5) > + > +#define GPT_IR_OF1IE (1 << 0) > +#define GPT_IR_ROVIE (1 << 5) > + > +typedef struct { > + SysBusDevice busdev; > + QEMUTimer *timer; > + MemoryRegion iomem; > + uint32_t cr; > + uint32_t sr; > + uint32_t pr; > + uint32_t ir; > + uint32_t ocr1; > + uint32_t cnt; > + > + int waiting_rov; > + qemu_irq irq; > +} imxg_timer_state; > + > +static const VMStateDescription vmstate_imxg_timer = { > + .name = "imxg-timer", > + .version_id = 1, > + .minimum_version_id = 1, > + .minimum_version_id_old = 1, > + .fields = (VMStateField[]) { > + VMSTATE_UINT32(cr, imxg_timer_state), > + VMSTATE_UINT32(sr, imxg_timer_state), > + VMSTATE_UINT32(ir, imxg_timer_state), > + VMSTATE_UINT32(cnt, imxg_timer_state), > + VMSTATE_UINT32(ocr1, imxg_timer_state), > + VMSTATE_TIMER(timer, imxg_timer_state), Missing 'pr' and 'waiting_rov' ? > + VMSTATE_END_OF_LIST() > + } > +}; > + > + > +/* Check all active timers, and schedule the next timer interrupt. */ > +static void imxg_timer_update(imxg_timer_state *s) > +{ > + uint32_t flags = s->sr & s->ir & (GPT_SR_OF1 | GPT_SR_ROV); > + > + if ((s->cr & GPT_CR_EN) && flags) { > + qemu_irq_raise(s->irq); > + } else { > + qemu_irq_lower(s->irq); > + } > +} > + > +static uint64_t imxg_timer_update_count(imxg_timer_state *s) > +{ > + uint64_t clk = qemu_get_clock_ns(vm_clock); > + > + s->cnt = ((uint32_t)muldiv64(clk, GPT_FREQ/1000000, > + 1000)) % TIMER_MAX; > + return clk; > +} > + > +static void imxg_timer_run(imxg_timer_state *s, uint32_t timeout) > +{ > + uint64_t clk = imxg_timer_update_count(s); > + uint32_t diff_cnt; > + if (s->cnt < timeout) { > + diff_cnt = (timeout - s->cnt); > + s->waiting_rov = 0; > + } else { > + diff_cnt = (TIMER_MAX - s->cnt); > + s->waiting_rov = 1; > + } > + qemu_mod_timer(s->timer, clk + diff_cnt * 1000 / (GPT_FREQ/1000000)); You haven't explained why this uses raw qemu timers but the enhanced timer later in the file uses ptimers, why doing a % 0xffffffff makes sense, etc. Stopped review here. > +} > + > +static uint64_t imxg_timer_read(void *opaque, target_phys_addr_t offset, > + unsigned size) > +{ > + imxg_timer_state *s = (imxg_timer_state *)opaque; > + > + DPRINTF("g-read(offset=%x)\n", offset >> 2); > + switch (offset >> 2) { > + case 0: /* CR */ > + return s->cr; > + > + case 1: /* prescaler */ > + return s->pr; > + > + case 2: > + return s->sr; > + > + case 3: > + return s->ir; > + > + case 4: > + return s->ocr1; > + > + case 9: /* cnt */ > + imxg_timer_update_count(s); > + return s->cnt; > + } > + > + IPRINTF("imxg_timer_read: Bad offset %x\n", > + (int)offset >> 2); > + return 0; > +} > + > +static void imxg_timer_reset(DeviceState *dev) > +{ > + imxg_timer_state *s = container_of(dev, imxg_timer_state, busdev.qdev); > + > + s->cr &= ~(GPT_CR_EN|GPT_CR_DOZEN|GPT_CR_WAITEN|GPT_CR_DBGEN); > + s->sr = 0; > + s->pr = 0; > + s->ir = 0; > + s->cnt = 0; > + s->ocr1 = 0; > + imxg_timer_update_count(s); > + imxg_timer_update(s); > +} > + > +static void imxg_timer_write(void *opaque, target_phys_addr_t offset, > + uint64_t value, unsigned size) > +{ > + imxg_timer_state *s = (imxg_timer_state *)opaque; > + DPRINTF("g-write(offset=%x, value = %x)\n", (unsigned int)offset >> 2, > + (unsigned int)value); > + > + switch (offset >> 2) { > + case 0: /* CR */ > + if (value & GPT_CR_SWR) { /* force reset */ > + value &= ~GPT_CR_SWR; > + imxg_timer_reset(&s->busdev.qdev); > + } > + if (!(s->cr & GPT_CR_EN) && (value & GPT_CR_EN)) { > + if (value & GPT_CR_ENMODE) { > + s->cnt = 0; > + } > + imxg_timer_run(s, s->ocr1); > + } else if ((s->cr & GPT_CR_EN) && !(value & GPT_CR_EN)) { > + qemu_del_timer(s->timer); > + }; > + s->cr = value; > + return; > + > + case 1: > + s->pr = value; > + return; > + > + case 2: > + if (value & GPT_SR_OF1) { > + s->sr &= ~GPT_SR_OF1; > + } > + if (value & GPT_SR_ROV) { > + s->sr &= ~GPT_SR_ROV; > + } > + imxg_timer_update(s); > + return; > + > + case 3: > + s->ir = value; > + imxg_timer_update(s); > + return; > + > + case 4: > + s->ocr1 = value; > + if (s->cr & GPT_CR_EN) { > + imxg_timer_run(s, s->ocr1); > + } > + return; > + > + default: > + IPRINTF("imxg_timer_write: Bad offset %x\n", > + (int)offset >> 2); > + } > +} > + > +static void imxg_timer_timeout(void *opaque) > +{ > + imxg_timer_state *s = (imxg_timer_state *)opaque; > + > + DPRINTF("imxg_timer_timeout\n"); > + if (s->waiting_rov) { > + s->sr |= GPT_SR_ROV; > + imxg_timer_run(s, s->ocr1); > + } else { > + s->sr |= GPT_SR_OF1; > + imxg_timer_run(s, 0); > + } > + imxg_timer_update(s); > +} > + > +static const MemoryRegionOps imxg_timer_ops = { > + .read = imxg_timer_read, > + .write = imxg_timer_write, > + .endianness = DEVICE_NATIVE_ENDIAN, > +}; > + > + > +static int imxg_timer_init(SysBusDevice *dev) > +{ > + imxg_timer_state *s = FROM_SYSBUS(imxg_timer_state, dev); > + > + sysbus_init_irq(dev, &s->irq); > + memory_region_init_io(&s->iomem, &imxg_timer_ops, > + s, "imxg-timer", > + 0x00001000); > + sysbus_init_mmio_region(dev, &s->iomem); > + > + s->timer = qemu_new_timer_ns(vm_clock, imxg_timer_timeout, s); > + imxg_timer_reset(&dev->qdev); > + return 0; > +} > + > + > + > +/* > + * EPIT :Enhanced periodic interrupt timer > + */ > + > +#define EPIT_FREQ 1000000 > +#define TIMER_TICK_LENGTH 5000 > +#define IMX31_TICKS_PER_TIMESLICE (72 * TIMER_TICK_LENGTH) > +#define CR_EN (1 << 0) > +#define CR_SWR (1 << 16) > + > +typedef struct { > + SysBusDevice busdev; > + ptimer_state *timer; > + MemoryRegion iomem; > + uint32_t cr; > + uint32_t lr; > + uint32_t cmp; > + int int_level; > + qemu_irq irq; > +} imxp_timer_state; > + > +/* Check all active timers, and schedule the next timer interrupt. */ > +static void imxp_timer_update(imxp_timer_state *s) > +{ > + /* Update interrupts. */ > + if (s->int_level && (s->cr & CR_EN)) { > + qemu_irq_raise(s->irq); > + } else { > + qemu_irq_lower(s->irq); > + } > +} > + > +static void imxp_timer_reset(DeviceState *dev) > +{ > + imxp_timer_state *s = container_of(dev, imxp_timer_state, busdev.qdev); > + > + s->cr = 0; > + s->lr = 0; > + ptimer_stop(s->timer); > +} > + > +static uint64_t imxp_timer_read(void *opaque, target_phys_addr_t offset, > + unsigned size) > +{ > + imxp_timer_state *s = (imxp_timer_state *)opaque; > + > + DPRINTF("p-read(offset=%x)\n", offset); > + switch (offset >> 2) { > + case 0: /* CR */ > + return s->cr; > + > + case 1: /* SR */ > + return s->int_level; > + > + case 2: /* LR - set ticks*/ > + return s->lr; > + > + case 3: /* CMP */ > + return s->cmp; > + > + case 4: /* CNT */ > + return ptimer_get_count(s->timer); > + } > + IPRINTF("imxp_timer_read: Bad offset %x\n", > + (int)offset >> 2); > + return 0; > +} > + > +static void imxp_timer_write(void *opaque, target_phys_addr_t offset, > + uint64_t value, unsigned size) > +{ > + imxp_timer_state *s = (imxp_timer_state *)opaque; > + DPRINTF("p-write(offset=%x, value = %x)\n", (unsigned int)offset >> 2, > + (unsigned int)value); > + > + switch (offset >> 2) { > + case 0: /* CR */ > + s->cr = value; > + if (s->cr & CR_EN) { > + ptimer_run(s->timer, 0); > + } else { > + ptimer_stop(s->timer); > + } > + if (s->cr & CR_SWR) { > + imxp_timer_reset(&s->busdev.qdev); > + } > + break; > + > + case 1: /* SR - ACK*/ > + s->int_level = 0; > + imxp_timer_update(s); > + break; > + > + case 2: /* LR - set ticks*/ > + s->lr = value; > + ptimer_set_freq(s->timer, EPIT_FREQ); > + ptimer_set_limit(s->timer, value, 1); > + break; > + > + case 3: /* CMP */ > + s->cmp = value; > + break; > + > + default: > + IPRINTF("imxp_timer_write: Bad offset %x\n", > + (int)offset >> 2); > + } > +} > + > +static void imxp_timer_tick(void *opaque) > +{ > + imxp_timer_state *s = (imxp_timer_state *)opaque; > + s->int_level = 1; > + imxp_timer_update(s); > +} > + > +static const MemoryRegionOps imxp_timer_ops = { > + .read = imxp_timer_read, > + .write = imxp_timer_write, > + .endianness = DEVICE_NATIVE_ENDIAN, > +}; > + > +static const VMStateDescription vmstate_imxp_timer = { > + .name = "imxp-timer", > + .version_id = 1, > + .minimum_version_id = 1, > + .minimum_version_id_old = 1, > + .fields = (VMStateField[]) { > + VMSTATE_UINT32(cr, imxp_timer_state), > + VMSTATE_UINT32(lr, imxp_timer_state), > + VMSTATE_UINT32(cmp, imxp_timer_state), > + VMSTATE_INT32(int_level, imxp_timer_state), > + VMSTATE_PTIMER(timer, imxp_timer_state), > + VMSTATE_END_OF_LIST() > + } > +}; > + > +static int imxp_timer_init(SysBusDevice *dev) > +{ > + imxp_timer_state *s = FROM_SYSBUS(imxp_timer_state, dev); > + QEMUBH *bh; > + > + DPRINTF("imxp_timer_init\n"); > + > + sysbus_init_irq(dev, &s->irq); > + memory_region_init_io(&s->iomem, &imxp_timer_ops, > + s, "imxp-timer", > + 0x00001000); > + sysbus_init_mmio_region(dev, &s->iomem); > + > + bh = qemu_bh_new(imxp_timer_tick, s); > + s->timer = ptimer_init(bh); > + imxp_timer_reset(&s->busdev.qdev); > + > + return 0; > +} > + > +static SysBusDeviceInfo imx_timerp_info = { > + .qdev.name = "imx_timerp", > + .qdev.desc = "i.MX Periodic Timer", > + .qdev.size = sizeof (imxp_timer_state), > + .qdev.vmsd = &vmstate_imxp_timer, > + .qdev.reset = imxp_timer_reset, > + .init = imxp_timer_init, > + > +}; > + > +static SysBusDeviceInfo imx_timerg_info = { > + .qdev.name = "imx_timerg", > + .qdev.desc = "i.MX General Timer", > + .qdev.size = sizeof (imxg_timer_state), > + .qdev.vmsd = &vmstate_imxg_timer, > + .qdev.reset = imxg_timer_reset, > + .init = imxg_timer_init, > +}; > + > +static void imx_timer_register_devices(void) > +{ > + sysbus_register_withprop(&imx_timerp_info); > + sysbus_register_withprop(&imx_timerg_info); > +} > + > +device_init(imx_timer_register_devices) > Index: qemu-working/Makefile.target > =================================================================== > --- qemu-working.orig/Makefile.target 2011-11-30 13:38:24.786779925 +1100 > +++ qemu-working/Makefile.target 2011-11-30 13:38:25.818785258 +1100 > @@ -361,21 +361,21 @@ obj-arm-y += mst_fpga.o mainstone.o > obj-arm-y += z2.o > obj-arm-y += musicpal.o bitbang_i2c.o marvell_88w8618_audio.o > obj-arm-y += framebuffer.o > obj-arm-y += syborg.o syborg_fb.o syborg_interrupt.o syborg_keyboard.o > obj-arm-y += syborg_serial.o syborg_timer.o syborg_pointer.o syborg_rtc.o > obj-arm-y += syborg_virtio.o > obj-arm-y += vexpress.o > obj-arm-y += strongarm.o > obj-arm-y += collie.o > obj-arm-y += pl041.o lm4549.o > -obj-arm-y += imx_serial.o > +obj-arm-y += imx_serial.o imx_timer.o > > obj-sh4-y = shix.o r2d.o sh7750.o sh7750_regnames.o tc58128.o > obj-sh4-y += sh_timer.o sh_serial.o sh_intc.o sh_pci.o sm501.o > obj-sh4-y += ide/mmio.o > > obj-m68k-y = an5206.o mcf5206.o mcf_uart.o mcf_intc.o mcf5208.o mcf_fec.o > obj-m68k-y += m68k-semi.o dummy_m68k.o > > obj-s390x-y = s390-virtio-bus.o s390-virtio.o > >
Index: qemu-working/hw/imx_timer.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ qemu-working/hw/imx_timer.c 2011-11-30 13:38:25.818785258 +1100 @@ -0,0 +1,460 @@ +/* + * IMX31 Timer + * + * Copyright (c) 2008 OKL + * Copyright (c) 2011 NICTA Pty Ltd + * Originally Written by Hans Jiang + * Updated by Peter Chubb + * + * This code is licenced under GPL version 2 or later. See + * the COPYING file in the top-level directory. + */ + +#include "hw.h" +#include "qemu-timer.h" +#include "sysbus.h" + +//#define DEBUG_TIMER 1 + +#ifdef DEBUG_TIMER +# define DPRINTF(fmt, args...) \ + do { printf("imx_timer: " fmt , ##args); } while (0) +#else +# define DPRINTF(fmt, args...) do {} while (0) +#endif + +/* + * Define to 1 for messages about attempts to + * access unimplemented registers or similar. + */ +#define DEBUG_IMPLEMENTATION 1 +#if DEBUG_IMPLEMENTATION +# define IPRINTF(fmt, args...) \ + do { fprintf(stderr, "imx_timer: " fmt, ##args); } while (0) +#else +# define IPRINTF(fmt, args...) do {} while (0) +#endif + +/* + * GPT : General purpose timer + */ + +#define TIMER_MAX 0xFFFFFFFFUL +#define GPT_FREQ 50000000 /* Hz == 50 MHz */ + +/* Control register. Not all of these bits have any effect (yet) */ +#define GPT_CR_EN (1 << 0) /* GPT Enable */ +#define GPT_CR_ENMODE (1 << 1) /* GPT Enable Mode */ +#define GPT_CR_DBGEN (1 << 2) /* GPT Debug mode enable */ +#define GPT_CR_WAITEN (1 << 3) /* GPT Wait Mode Enable */ +#define GPT_CR_DOZEN (1 << 4) /* GPT Doze mode enable */ +#define GPT_CR_STOPEN (1 << 5) /* GPT Stop Mode Enable */ +#define GPT_CR_CLKSRC (7 << 6) /* Clock source select (3 bits) */ +#define GPT_CR_FRR (1 << 9) /* Freerun or Restart */ +#define GPT_CR_SWR (1 << 15) +#define GPT_CR_IM1 (3 << 16) /* Input capture channel 1 mode (2 bits) */ +#define GPT_CR_IM2 (3 << 18) /* Input capture channel 2 mode (2 bits) */ +#define GPT_CR_OM1 (7 << 20) /* Output Compare Channel 1 Mode (3 bits) */ +#define GPT_CR_OM2 (7 << 23) /* Output Compare Channel 2 Mode (3 bits) */ +#define GPT_CR_OM3 (7 << 26) /* Output Compare Channel 3 Mode (3 bits) */ +#define GPT_CR_FO1 (1 << 29) /* Force Output Compare Channel 1 */ +#define GPT_CR_FO2 (1 << 30) /* Force Output Compare Channel 2 */ +#define GPT_CR_FO3 (1 << 31) /* Force Output Compare Channel 3 */ + +#define GPT_SR_OF1 (1 << 0) +#define GPT_SR_ROV (1 << 5) + +#define GPT_IR_OF1IE (1 << 0) +#define GPT_IR_ROVIE (1 << 5) + +typedef struct { + SysBusDevice busdev; + QEMUTimer *timer; + MemoryRegion iomem; + uint32_t cr; + uint32_t sr; + uint32_t pr; + uint32_t ir; + uint32_t ocr1; + uint32_t cnt; + + int waiting_rov; + qemu_irq irq; +} imxg_timer_state; + +static const VMStateDescription vmstate_imxg_timer = { + .name = "imxg-timer", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(cr, imxg_timer_state), + VMSTATE_UINT32(sr, imxg_timer_state), + VMSTATE_UINT32(ir, imxg_timer_state), + VMSTATE_UINT32(cnt, imxg_timer_state), + VMSTATE_UINT32(ocr1, imxg_timer_state), + VMSTATE_TIMER(timer, imxg_timer_state), + VMSTATE_END_OF_LIST() + } +}; + + +/* Check all active timers, and schedule the next timer interrupt. */ +static void imxg_timer_update(imxg_timer_state *s) +{ + uint32_t flags = s->sr & s->ir & (GPT_SR_OF1 | GPT_SR_ROV); + + if ((s->cr & GPT_CR_EN) && flags) { + qemu_irq_raise(s->irq); + } else { + qemu_irq_lower(s->irq); + } +} + +static uint64_t imxg_timer_update_count(imxg_timer_state *s) +{ + uint64_t clk = qemu_get_clock_ns(vm_clock); + + s->cnt = ((uint32_t)muldiv64(clk, GPT_FREQ/1000000, + 1000)) % TIMER_MAX; + return clk; +} + +static void imxg_timer_run(imxg_timer_state *s, uint32_t timeout) +{ + uint64_t clk = imxg_timer_update_count(s); + uint32_t diff_cnt; + if (s->cnt < timeout) { + diff_cnt = (timeout - s->cnt); + s->waiting_rov = 0; + } else { + diff_cnt = (TIMER_MAX - s->cnt); + s->waiting_rov = 1; + } + qemu_mod_timer(s->timer, clk + diff_cnt * 1000 / (GPT_FREQ/1000000)); +} + +static uint64_t imxg_timer_read(void *opaque, target_phys_addr_t offset, + unsigned size) +{ + imxg_timer_state *s = (imxg_timer_state *)opaque; + + DPRINTF("g-read(offset=%x)\n", offset >> 2); + switch (offset >> 2) { + case 0: /* CR */ + return s->cr; + + case 1: /* prescaler */ + return s->pr; + + case 2: + return s->sr; + + case 3: + return s->ir; + + case 4: + return s->ocr1; + + case 9: /* cnt */ + imxg_timer_update_count(s); + return s->cnt; + } + + IPRINTF("imxg_timer_read: Bad offset %x\n", + (int)offset >> 2); + return 0; +} + +static void imxg_timer_reset(DeviceState *dev) +{ + imxg_timer_state *s = container_of(dev, imxg_timer_state, busdev.qdev); + + s->cr &= ~(GPT_CR_EN|GPT_CR_DOZEN|GPT_CR_WAITEN|GPT_CR_DBGEN); + s->sr = 0; + s->pr = 0; + s->ir = 0; + s->cnt = 0; + s->ocr1 = 0; + imxg_timer_update_count(s); + imxg_timer_update(s); +} + +static void imxg_timer_write(void *opaque, target_phys_addr_t offset, + uint64_t value, unsigned size) +{ + imxg_timer_state *s = (imxg_timer_state *)opaque; + DPRINTF("g-write(offset=%x, value = %x)\n", (unsigned int)offset >> 2, + (unsigned int)value); + + switch (offset >> 2) { + case 0: /* CR */ + if (value & GPT_CR_SWR) { /* force reset */ + value &= ~GPT_CR_SWR; + imxg_timer_reset(&s->busdev.qdev); + } + if (!(s->cr & GPT_CR_EN) && (value & GPT_CR_EN)) { + if (value & GPT_CR_ENMODE) { + s->cnt = 0; + } + imxg_timer_run(s, s->ocr1); + } else if ((s->cr & GPT_CR_EN) && !(value & GPT_CR_EN)) { + qemu_del_timer(s->timer); + }; + s->cr = value; + return; + + case 1: + s->pr = value; + return; + + case 2: + if (value & GPT_SR_OF1) { + s->sr &= ~GPT_SR_OF1; + } + if (value & GPT_SR_ROV) { + s->sr &= ~GPT_SR_ROV; + } + imxg_timer_update(s); + return; + + case 3: + s->ir = value; + imxg_timer_update(s); + return; + + case 4: + s->ocr1 = value; + if (s->cr & GPT_CR_EN) { + imxg_timer_run(s, s->ocr1); + } + return; + + default: + IPRINTF("imxg_timer_write: Bad offset %x\n", + (int)offset >> 2); + } +} + +static void imxg_timer_timeout(void *opaque) +{ + imxg_timer_state *s = (imxg_timer_state *)opaque; + + DPRINTF("imxg_timer_timeout\n"); + if (s->waiting_rov) { + s->sr |= GPT_SR_ROV; + imxg_timer_run(s, s->ocr1); + } else { + s->sr |= GPT_SR_OF1; + imxg_timer_run(s, 0); + } + imxg_timer_update(s); +} + +static const MemoryRegionOps imxg_timer_ops = { + .read = imxg_timer_read, + .write = imxg_timer_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + + +static int imxg_timer_init(SysBusDevice *dev) +{ + imxg_timer_state *s = FROM_SYSBUS(imxg_timer_state, dev); + + sysbus_init_irq(dev, &s->irq); + memory_region_init_io(&s->iomem, &imxg_timer_ops, + s, "imxg-timer", + 0x00001000); + sysbus_init_mmio_region(dev, &s->iomem); + + s->timer = qemu_new_timer_ns(vm_clock, imxg_timer_timeout, s); + imxg_timer_reset(&dev->qdev); + return 0; +} + + + +/* + * EPIT :Enhanced periodic interrupt timer + */ + +#define EPIT_FREQ 1000000 +#define TIMER_TICK_LENGTH 5000 +#define IMX31_TICKS_PER_TIMESLICE (72 * TIMER_TICK_LENGTH) +#define CR_EN (1 << 0) +#define CR_SWR (1 << 16) + +typedef struct { + SysBusDevice busdev; + ptimer_state *timer; + MemoryRegion iomem; + uint32_t cr; + uint32_t lr; + uint32_t cmp; + int int_level; + qemu_irq irq; +} imxp_timer_state; + +/* Check all active timers, and schedule the next timer interrupt. */ +static void imxp_timer_update(imxp_timer_state *s) +{ + /* Update interrupts. */ + if (s->int_level && (s->cr & CR_EN)) { + qemu_irq_raise(s->irq); + } else { + qemu_irq_lower(s->irq); + } +} + +static void imxp_timer_reset(DeviceState *dev) +{ + imxp_timer_state *s = container_of(dev, imxp_timer_state, busdev.qdev); + + s->cr = 0; + s->lr = 0; + ptimer_stop(s->timer); +} + +static uint64_t imxp_timer_read(void *opaque, target_phys_addr_t offset, + unsigned size) +{ + imxp_timer_state *s = (imxp_timer_state *)opaque; + + DPRINTF("p-read(offset=%x)\n", offset); + switch (offset >> 2) { + case 0: /* CR */ + return s->cr; + + case 1: /* SR */ + return s->int_level; + + case 2: /* LR - set ticks*/ + return s->lr; + + case 3: /* CMP */ + return s->cmp; + + case 4: /* CNT */ + return ptimer_get_count(s->timer); + } + IPRINTF("imxp_timer_read: Bad offset %x\n", + (int)offset >> 2); + return 0; +} + +static void imxp_timer_write(void *opaque, target_phys_addr_t offset, + uint64_t value, unsigned size) +{ + imxp_timer_state *s = (imxp_timer_state *)opaque; + DPRINTF("p-write(offset=%x, value = %x)\n", (unsigned int)offset >> 2, + (unsigned int)value); + + switch (offset >> 2) { + case 0: /* CR */ + s->cr = value; + if (s->cr & CR_EN) { + ptimer_run(s->timer, 0); + } else { + ptimer_stop(s->timer); + } + if (s->cr & CR_SWR) { + imxp_timer_reset(&s->busdev.qdev); + } + break; + + case 1: /* SR - ACK*/ + s->int_level = 0; + imxp_timer_update(s); + break; + + case 2: /* LR - set ticks*/ + s->lr = value; + ptimer_set_freq(s->timer, EPIT_FREQ); + ptimer_set_limit(s->timer, value, 1); + break; + + case 3: /* CMP */ + s->cmp = value; + break; + + default: + IPRINTF("imxp_timer_write: Bad offset %x\n", + (int)offset >> 2); + } +} + +static void imxp_timer_tick(void *opaque) +{ + imxp_timer_state *s = (imxp_timer_state *)opaque; + s->int_level = 1; + imxp_timer_update(s); +} + +static const MemoryRegionOps imxp_timer_ops = { + .read = imxp_timer_read, + .write = imxp_timer_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static const VMStateDescription vmstate_imxp_timer = { + .name = "imxp-timer", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(cr, imxp_timer_state), + VMSTATE_UINT32(lr, imxp_timer_state), + VMSTATE_UINT32(cmp, imxp_timer_state), + VMSTATE_INT32(int_level, imxp_timer_state), + VMSTATE_PTIMER(timer, imxp_timer_state), + VMSTATE_END_OF_LIST() + } +}; + +static int imxp_timer_init(SysBusDevice *dev) +{ + imxp_timer_state *s = FROM_SYSBUS(imxp_timer_state, dev); + QEMUBH *bh; + + DPRINTF("imxp_timer_init\n"); + + sysbus_init_irq(dev, &s->irq); + memory_region_init_io(&s->iomem, &imxp_timer_ops, + s, "imxp-timer", + 0x00001000); + sysbus_init_mmio_region(dev, &s->iomem); + + bh = qemu_bh_new(imxp_timer_tick, s); + s->timer = ptimer_init(bh); + imxp_timer_reset(&s->busdev.qdev); + + return 0; +} + +static SysBusDeviceInfo imx_timerp_info = { + .qdev.name = "imx_timerp", + .qdev.desc = "i.MX Periodic Timer", + .qdev.size = sizeof (imxp_timer_state), + .qdev.vmsd = &vmstate_imxp_timer, + .qdev.reset = imxp_timer_reset, + .init = imxp_timer_init, + +}; + +static SysBusDeviceInfo imx_timerg_info = { + .qdev.name = "imx_timerg", + .qdev.desc = "i.MX General Timer", + .qdev.size = sizeof (imxg_timer_state), + .qdev.vmsd = &vmstate_imxg_timer, + .qdev.reset = imxg_timer_reset, + .init = imxg_timer_init, +}; + +static void imx_timer_register_devices(void) +{ + sysbus_register_withprop(&imx_timerp_info); + sysbus_register_withprop(&imx_timerg_info); +} + +device_init(imx_timer_register_devices) Index: qemu-working/Makefile.target =================================================================== --- qemu-working.orig/Makefile.target 2011-11-30 13:38:24.786779925 +1100 +++ qemu-working/Makefile.target 2011-11-30 13:38:25.818785258 +1100 @@ -361,21 +361,21 @@ obj-arm-y += mst_fpga.o mainstone.o obj-arm-y += z2.o obj-arm-y += musicpal.o bitbang_i2c.o marvell_88w8618_audio.o obj-arm-y += framebuffer.o obj-arm-y += syborg.o syborg_fb.o syborg_interrupt.o syborg_keyboard.o obj-arm-y += syborg_serial.o syborg_timer.o syborg_pointer.o syborg_rtc.o obj-arm-y += syborg_virtio.o obj-arm-y += vexpress.o obj-arm-y += strongarm.o obj-arm-y += collie.o obj-arm-y += pl041.o lm4549.o -obj-arm-y += imx_serial.o +obj-arm-y += imx_serial.o imx_timer.o obj-sh4-y = shix.o r2d.o sh7750.o sh7750_regnames.o tc58128.o obj-sh4-y += sh_timer.o sh_serial.o sh_intc.o sh_pci.o sm501.o obj-sh4-y += ide/mmio.o obj-m68k-y = an5206.o mcf5206.o mcf_uart.o mcf_intc.o mcf5208.o mcf_fec.o obj-m68k-y += m68k-semi.o dummy_m68k.o obj-s390x-y = s390-virtio-bus.o s390-virtio.o