From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([140.186.70.92]:37015) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1RTecZ-00053U-EP for qemu-devel@nongnu.org; Thu, 24 Nov 2011 14:06:09 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1RTecW-0005Dr-ER for qemu-devel@nongnu.org; Thu, 24 Nov 2011 14:06:07 -0500 Received: from mail-qw0-f45.google.com ([209.85.216.45]:50162) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1RTecW-0005Dh-8z for qemu-devel@nongnu.org; Thu, 24 Nov 2011 14:06:04 -0500 Received: by qabg14 with SMTP id g14so80683qab.4 for ; Thu, 24 Nov 2011 11:06:03 -0800 (PST) MIME-Version: 1.0 In-Reply-To: References: Date: Thu, 24 Nov 2011 19:06:03 +0000 Message-ID: From: Peter Maydell Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Subject: Re: [Qemu-devel] [PATCH V2 2/4] imx.31 and KZM board support: Timer support List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: Peter Chubb Cc: =?UTF-8?Q?Andreas_F=C3=A4rber?= , qemu-devel@nongnu.org On 22 November 2011 04:33, Peter Chubb wrote: > Implement the timers on the FreeScale i.MX31 SoC. > This is not a complete implementation, but gives enough for > Linux to boot and run. > > > Signed-off-by: Hans Jang > Signed-off-by: Adam Clench > Signed-off-by: Peter Chubb > --- > =C2=A0hw/imx_timer.c | =C2=A0441 ++++++++++++++++++++++++++++++++++++++++= +++++++++++++++++ > =C2=A01 file changed, 441 insertions(+) > =C2=A0create mode 100644 hw/imx_timer.c > > Index: qemu-working/hw/imx_timer.c > =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D > --- /dev/null =C2=A0 1970-01-01 00:00:00.000000000 +0000 > +++ qemu-working/hw/imx_timer.c 2011-11-22 14:47:10.058038639 +1100 > @@ -0,0 +1,441 @@ > +/* > + * IMX31 Timer > + * > + * Copyright (c) 2008 OKL > + * Written by Hans > + * Updated by Peter Chubb > + * > + * This code is licenced under GPL version 2 or later. > + */ > + > +#include "hw.h" > +#include "qemu-timer.h" > +#include "sysbus.h" > + > +//#define DEBUG_TIMER 1 > + > +#ifdef DEBUG_TIMER > +# =C2=A0 define DPRINTF(fmt, args...) \ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0do { printf("imx_timer: " fmt , ##args); } w= hile (0) > +#else > +# =C2=A0 define DPRINTF(fmt, args...) do {} while (0) > +#endif > + > +/* > + * Print a message at most ten times. > + */ > +#define scream(fmt, args...) \ > + =C2=A0 =C2=A0do { \ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0static int printable =3D 10;\ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if (printable--) { \ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0fprintf(stderr, fmt, ##args);\ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} \ > + =C2=A0 =C2=A0} while (0) > + > + > +/* > + * GPT : General purpose timer > + */ > + > +#define TIMER_MAX =C2=A00xFFFFFFFFUL > +#define GPT_FREQ =C2=A0 50000000 =C2=A0 =C2=A0/* Hz =3D=3D 50 MHz */ > + > +/* Control register. =C2=A0Not all of these bits have any effect (yet) *= / > +#define GPT_CR_EN =C2=A0 (1 << 0) =C2=A0 =C2=A0/* GPT Enable */ > +#define GPT_CR_ENMODE (1 << 1) =C2=A0/* GPT Enable Mode */ > +#define GPT_CR_DBGEN (1 << 2) =C2=A0 /* GPT Debug mode enable */ > +#define GPT_CR_WAITEN (1 << 3) =C2=A0/* GPT Wait Mode Enable =C2=A0*/ > +#define GPT_CR_DOZEN (1 << 4) =C2=A0 /* GPT Doze mode enable */ > +#define GPT_CR_STOPEN (1 << 5) =C2=A0/* GPT Stop Mode Enable */ > +#define GPT_CR_CLKSRC (7 << 6) /* Clock source select (3 bits) */ > +#define GPT_CR_FRR =C2=A0(1 << 9) =C2=A0 =C2=A0/* Freerun or Restart */ > +#define GPT_CR_SWR =C2=A0(1 << 15) > +#define GPT_CR_IM1 =C2=A0(3 << 16) =C2=A0 /* Input capture channel 1 mod= e (2 bits) */ > +#define GPT_CR_IM2 =C2=A0(3 << 18) =C2=A0 /* Input capture channel 2 mod= e (2 bits) */ > +#define GPT_CR_OM1 =C2=A0(7 << 20) =C2=A0 /* Output Compare Channel 1 Mo= de (3 bits) */ > +#define GPT_CR_OM2 =C2=A0(7 << 23) =C2=A0 /* Output Compare Channel 2 Mo= de (3 bits) */ > +#define GPT_CR_OM3 =C2=A0(7 << 26) =C2=A0 /* Output Compare Channel 3 Mo= de (3 bits) */ > +#define GPT_CR_FO1 =C2=A0(1 << 29) =C2=A0 /* Force Output Compare Channe= l 1 */ > +#define GPT_CR_FO2 =C2=A0(1 << 30) =C2=A0 /* Force Output Compare Channe= l 2 */ > +#define GPT_CR_FO3 =C2=A0(1 << 31) =C2=A0 /* Force Output Compare Channe= l 3 */ > + > + > + > + > +#define GPT_SR_OF1 =C2=A0(1 << 0) > +#define GPT_SR_ROV =C2=A0(1 << 5) > +#define GPT_IR_OF1IE =C2=A0(1 << 0) > +#define GPT_IR_ROVIE =C2=A0(1 << 5) > + > +typedef struct { > + =C2=A0 =C2=A0SysBusDevice busdev; > + =C2=A0 =C2=A0QEMUTimer *timer; > + =C2=A0 =C2=A0MemoryRegion iomem; > + =C2=A0 =C2=A0uint32_t cr; > + =C2=A0 =C2=A0uint32_t sr; > + =C2=A0 =C2=A0uint32_t pr; > + =C2=A0 =C2=A0uint32_t ir; > + =C2=A0 =C2=A0uint32_t ocr1; > + =C2=A0 =C2=A0uint32_t cnt; > + > + =C2=A0 =C2=A0int waiting_rov; > + =C2=A0 =C2=A0qemu_irq irq; > +} imxg_timer_state; > + > +static const VMStateDescription vmstate_imxg_timer =3D { We don't seem to actually use this struct anywhere... > + =C2=A0 =C2=A0.name =3D "imxg-timer", > + =C2=A0 =C2=A0.version_id =3D 1, > + =C2=A0 =C2=A0.minimum_version_id =3D 1, > + =C2=A0 =C2=A0.minimum_version_id_old =3D 1, > + =C2=A0 =C2=A0.fields =C2=A0 =C2=A0 =C2=A0=3D (VMStateField[]) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0VMSTATE_UINT32(cr, imxg_timer_state), > + =C2=A0 =C2=A0 =C2=A0 =C2=A0VMSTATE_UINT32(sr, imxg_timer_state), > + =C2=A0 =C2=A0 =C2=A0 =C2=A0VMSTATE_UINT32(ir, imxg_timer_state), > + =C2=A0 =C2=A0 =C2=A0 =C2=A0VMSTATE_UINT32(cnt, imxg_timer_state), > + =C2=A0 =C2=A0 =C2=A0 =C2=A0VMSTATE_UINT32(ocr1, imxg_timer_state), > + =C2=A0 =C2=A0 =C2=A0 =C2=A0VMSTATE_TIMER(timer, imxg_timer_state), Looks like there are missing fields here. > + =C2=A0 =C2=A0 =C2=A0 =C2=A0VMSTATE_END_OF_LIST() > + =C2=A0 =C2=A0} > +}; > + > + > +/* Check all active timers, and schedule the next timer interrupt. =C2= =A0*/ > +static void imxg_timer_update(imxg_timer_state *s) > +{ > + =C2=A0 =C2=A0/* Update interrupts. =C2=A0*/ > + =C2=A0 =C2=A0if ((s->cr & GPT_CR_EN) > + =C2=A0 =C2=A0 =C2=A0 =C2=A0&& (((s->sr & GPT_SR_OF1) && (s->ir & GPT_IR= _OF1IE)) || > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0((s->sr & GPT_SR_ROV) && (s->i= r & GPT_IR_ROVIE)))) { The registers have obviously been laid out to let you AND them together and look for set bits so I think that would be clearer. > + =C2=A0 =C2=A0 =C2=A0 =C2=A0qemu_irq_raise(s->irq); > + =C2=A0 =C2=A0} else { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0qemu_irq_lower(s->irq); > + =C2=A0 =C2=A0} > +} > + > +static uint64_t imxg_timer_update_count(imxg_timer_state *s) > +{ > + =C2=A0 =C2=A0uint64_t clk =3D qemu_get_clock_ns(vm_clock); > + > + =C2=A0 =C2=A0s->cnt =3D ((uint32_t)muldiv64(clk, GPT_FREQ/1000000, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 1000)) % TIMER_MAX; Does the hardware really do the equivalent of "% 0xffffffff" ? That looks kind of odd. > + =C2=A0 =C2=A0return clk; > +} > + > +static void imxg_timer_run(imxg_timer_state *s, uint32_t timeout) > +{ > + =C2=A0 =C2=A0uint64_t clk =3D imxg_timer_update_count(s); > + =C2=A0 =C2=A0uint32_t diff_cnt; > + =C2=A0 =C2=A0if (s->cnt < timeout) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0diff_cnt =3D (timeout - s->cnt); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0s->waiting_rov =3D 0; > + =C2=A0 =C2=A0} else { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0diff_cnt =3D (TIMER_MAX - s->cnt); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0s->waiting_rov =3D 1; > + =C2=A0 =C2=A0} > + =C2=A0 =C2=A0qemu_mod_timer(s->timer, clk + diff_cnt * 1000 / (GPT_FREQ= /1000000)); > +/* > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 clk + mu= ldiv64(get_ticks_per_sec(), > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0diff_cnt, GPT_FREQ) > +*/ What's this commented out code doing here? I haven't looked much at the qemu timer infrastructure, but have you checked whether the ptimer.c functions would be useful here? > +} > + > +static uint64_t imxg_timer_read(void *opaque, target_phys_addr_t offset, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0unsigned size) > +{ > + =C2=A0 =C2=A0imxg_timer_state *s =3D (imxg_timer_state *)opaque; > + > + =C2=A0 =C2=A0DPRINTF("g-read(offset=3D%x)\n", offset >> 2); > + =C2=A0 =C2=A0switch (offset >> 2) { > + =C2=A0 =C2=A0case 0: /* CR */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0return s->cr; > + > + =C2=A0 =C2=A0case 1: /* prescaler */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0return s->pr; > + > + =C2=A0 =C2=A0case 2: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0return s->sr; > + > + =C2=A0 =C2=A0case 3: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0return s->ir; > + > + =C2=A0 =C2=A0case 4: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0return s->ocr1; > + > + =C2=A0 =C2=A0case 9: /* cnt */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0imxg_timer_update_count(s); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0return s->cnt; > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0scream("imxg_timer_read: Bad offset %x\n", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (int)offset >> 2); > + =C2=A0 =C2=A0return 0; > +} > + > +static void imxg_timer_write(void *opaque, target_phys_addr_t offset, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 uint64_t value, unsigned size) > +{ > + =C2=A0 =C2=A0imxg_timer_state *s =3D (imxg_timer_state *)opaque; > + =C2=A0 =C2=A0DPRINTF("g-write(offset=3D%x, value =3D %x)\n", (unsigned = int)offset >> 2, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(unsigned int)value); > + > + =C2=A0 =C2=A0switch (offset >> 2) { > + =C2=A0 =C2=A0case 0: /* CR */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if (value & GPT_CR_SWR) { /* force reset */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0value &=3D ~GPT_CR_SWR; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0s->cr &=3D ~(GPT_CR_EN|GPT_CR_= DOZEN|GPT_CR_WAITEN|GPT_CR_DBGEN); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0s->sr =3D 0; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0s->pr =3D 0; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0s->ir =3D 0; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0s->cnt =3D 0; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0s->ocr1 =3D 0; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} This should probably be calling a reset function rather than being open-coded. > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if (!(s->cr & GPT_CR_EN) && (value & GPT_CR_= EN)) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0if (value & GPT_CR_ENMODE) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0s->cnt =3D 0; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0imxg_timer_run(s, s->ocr1); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} else if ((s->cr & GPT_CR_EN) && !(value & = GPT_CR_EN)) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0qemu_del_timer(s->timer); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0}; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0s->cr =3D value; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0return; > + > + =C2=A0 =C2=A0case 1: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0s->pr =3D value; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0return; > + > + =C2=A0 =C2=A0case 2: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if (value & GPT_SR_OF1) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0s->sr &=3D ~GPT_SR_OF1; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if (value & GPT_SR_ROV) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0s->sr &=3D ~GPT_SR_ROV; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0 =C2=A0 =C2=A0imxg_timer_update(s); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0return; > + > + =C2=A0 =C2=A0case 3: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0s->ir =3D value; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0imxg_timer_update(s); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0return; > + > + =C2=A0 =C2=A0case 4: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0s->ocr1 =3D value; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if (s->cr & GPT_CR_EN) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0imxg_timer_run(s, s->ocr1); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0 =C2=A0 =C2=A0return; > + > + =C2=A0 =C2=A0default: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0scream("imxg_timer_write: Bad offset %x\n", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (int)off= set >> 2); > + =C2=A0 =C2=A0} > +} > + > +static void imxg_timer_timeout(void *opaque) > +{ > + =C2=A0 =C2=A0imxg_timer_state *s =3D (imxg_timer_state *)opaque; > + > + =C2=A0 =C2=A0DPRINTF("imxg_timer_timeout\n"); > + =C2=A0 =C2=A0if (s->waiting_rov) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0s->sr |=3D GPT_SR_ROV; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0imxg_timer_run(s, s->ocr1); > + =C2=A0 =C2=A0} else { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0s->sr |=3D GPT_SR_OF1; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0imxg_timer_run(s, 0); > + =C2=A0 =C2=A0} > + =C2=A0 =C2=A0imxg_timer_update(s); > +} > + > +static const MemoryRegionOps imxg_timer_ops =3D { > + =C2=A0.read =3D imxg_timer_read, > + =C2=A0.write =3D imxg_timer_write, > + =C2=A0.endianness =3D DEVICE_NATIVE_ENDIAN, > +}; > + > + > +static int imxg_timer_init(SysBusDevice *dev) > +{ > + =C2=A0 =C2=A0imxg_timer_state *s =3D FROM_SYSBUS(imxg_timer_state, dev)= ; > + > + =C2=A0 =C2=A0sysbus_init_irq(dev, &s->irq); > + =C2=A0 =C2=A0memory_region_init_io(&s->iomem, &imxg_timer_ops, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0s, "imxg-timer", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A00x00001000); > + =C2=A0 =C2=A0sysbus_init_mmio_region(dev, &s->iomem); > + > + =C2=A0 =C2=A0s->timer =3D qemu_new_timer_ns(vm_clock, imxg_timer_timeou= t, s); > + =C2=A0 =C2=A0s->cr =3D 0; > + =C2=A0 =C2=A0s->ir =3D 0; > + =C2=A0 =C2=A0s->pr =3D 0; > + =C2=A0 =C2=A0s->ocr1 =3D 0; Reset function. > + =C2=A0 =C2=A0imxg_timer_update_count(s); > + > + =C2=A0 =C2=A0return 0; > +} > + > + > + > +/* > + * EPIT :Enhanced periodic interrupt timer > + */ > + > +#define EPIT_FREQ =C2=A0 1000000 > +#define TIMER_TICK_LENGTH 5000 > +#define IMX31_TICKS_PER_TIMESLICE (72 * TIMER_TICK_LENGTH) > +#define CR_EN =C2=A0 =C2=A0 =C2=A0 (1 << 0) > +#define CR_SWR =C2=A0 =C2=A0 =C2=A0(1 << 16) > + > +typedef struct { > + =C2=A0 =C2=A0SysBusDevice busdev; > + =C2=A0 =C2=A0ptimer_state *timer; hey, this one uses ptimers... > + =C2=A0 =C2=A0MemoryRegion iomem; > + =C2=A0 =C2=A0uint32_t cr; > + =C2=A0 =C2=A0uint32_t lr; > + =C2=A0 =C2=A0uint32_t cmp; > + =C2=A0 =C2=A0int int_level; > + =C2=A0 =C2=A0qemu_irq irq; > +} imxp_timer_state; > + > +/* Check all active timers, and schedule the next timer interrupt. =C2= =A0*/ > +static void imxp_timer_update(imxp_timer_state *s) > +{ > + =C2=A0 =C2=A0/* Update interrupts. =C2=A0*/ > + =C2=A0 =C2=A0if (s->int_level && (s->cr & CR_EN)) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0qemu_irq_raise(s->irq); > + =C2=A0 =C2=A0} else { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0qemu_irq_lower(s->irq); > + =C2=A0 =C2=A0} > +} > + > +static uint64_t imxp_timer_read(void *opaque, target_phys_addr_t offset, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0unsigned size) > +{ > + =C2=A0 =C2=A0imxp_timer_state *s =3D (imxp_timer_state *)opaque; > + > + =C2=A0 =C2=A0DPRINTF("p-read(offset=3D%x)\n", offset); > + =C2=A0 =C2=A0switch (offset >> 2) { > + =C2=A0 =C2=A0case 0: /* CR */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0return s->cr; > + > + =C2=A0 =C2=A0case 1: /* SR */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0return s->int_level; > + > + =C2=A0 =C2=A0case 2: /* LR - set ticks*/ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0return s->lr; > + > + =C2=A0 =C2=A0case 3: /* CMP */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0return s->cmp; > + > + =C2=A0 =C2=A0case 4: /* CNT */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0return ptimer_get_count(s->timer); > + =C2=A0 =C2=A0} > + =C2=A0 =C2=A0scream("imxp_timer_read: Bad offset %x\n", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (int)offset >> 2); > + =C2=A0 =C2=A0return 0; > +} > + > +static void imxp_timer_write(void *opaque, target_phys_addr_t offset, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 uint64_t value, unsigned size) > +{ > + =C2=A0 =C2=A0imxp_timer_state *s =3D (imxp_timer_state *)opaque; > + =C2=A0 =C2=A0DPRINTF("p-write(offset=3D%x, value =3D %x)\n", (unsigned = int)offset >> 2, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(unsigned int)value); > + > + =C2=A0 =C2=A0switch (offset >> 2) { > + =C2=A0 =C2=A0case 0: /* CR */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if (s->cr & CR_EN) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ptimer_run(s->timer, 0); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} else { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ptimer_stop(s->timer); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if (s->cr & CR_SWR) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0s->cr =3D 0; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0s->lr =3D 0; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ptimer_stop(s->timer); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + > + =C2=A0 =C2=A0case 1: /* SR - ACK*/ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0s->int_level =3D 0; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0imxp_timer_update(s); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + > + =C2=A0 =C2=A0case 2: /* LR - set ticks*/ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0s->lr =3D value; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0ptimer_set_freq(s->timer, EPIT_FREQ); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0ptimer_set_limit(s->timer, value, 1); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + > + =C2=A0 =C2=A0case 3: /* CMP */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0s->cmp =3D value; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + > + =C2=A0 =C2=A0default: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0scream("imxp_timer_write: Bad offset %x\n", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (int)off= set >> 2); > + =C2=A0 =C2=A0} > +} > + > +static void imxp_timer_tick(void *opaque) > +{ > + =C2=A0 =C2=A0imxp_timer_state *s =3D (imxp_timer_state *)opaque; > + =C2=A0 =C2=A0s->int_level =3D 1; > + =C2=A0 =C2=A0imxp_timer_update(s); > +} > + > +static const MemoryRegionOps imxp_timer_ops =3D { > + =C2=A0.read =3D imxp_timer_read, > + =C2=A0.write =3D imxp_timer_write, > + =C2=A0.endianness =3D DEVICE_NATIVE_ENDIAN, > +}; > + > +static const VMStateDescription vmstate_imxp_timer =3D { > + =C2=A0 =C2=A0.name =3D "imxp-timer", > + =C2=A0 =C2=A0.version_id =3D 1, > + =C2=A0 =C2=A0.minimum_version_id =3D 1, > + =C2=A0 =C2=A0.minimum_version_id_old =3D 1, > + =C2=A0 =C2=A0.fields =C2=A0 =C2=A0 =C2=A0=3D (VMStateField[]) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0VMSTATE_UINT32(cr, imxp_timer_state), > + =C2=A0 =C2=A0 =C2=A0 =C2=A0VMSTATE_UINT32(lr, imxp_timer_state), > + =C2=A0 =C2=A0 =C2=A0 =C2=A0VMSTATE_UINT32(cmp, imxp_timer_state), > + =C2=A0 =C2=A0 =C2=A0 =C2=A0VMSTATE_INT32(int_level, imxp_timer_state), > + =C2=A0 =C2=A0 =C2=A0 =C2=A0VMSTATE_PTIMER(timer, imxp_timer_state), > + =C2=A0 =C2=A0 =C2=A0 =C2=A0VMSTATE_END_OF_LIST() > + =C2=A0 =C2=A0} > +}; > + > +static int imxp_timer_init(SysBusDevice *dev) > +{ > + =C2=A0 =C2=A0imxp_timer_state *s =3D FROM_SYSBUS(imxp_timer_state, dev)= ; > + =C2=A0 =C2=A0QEMUBH *bh; > + > + =C2=A0 =C2=A0DPRINTF("imxp_timer_init\n"); > + > + =C2=A0 =C2=A0sysbus_init_irq(dev, &s->irq); > + =C2=A0 =C2=A0memory_region_init_io(&s->iomem, &imxp_timer_ops, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0s, "imxp-timer", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A00x00001000); > + =C2=A0 =C2=A0sysbus_init_mmio_region(dev, &s->iomem); > + > + =C2=A0 =C2=A0s->cr =3D 0; > + =C2=A0 =C2=A0s->lr =3D 0; Reset function. > + > + =C2=A0 =C2=A0bh =3D qemu_bh_new(imxp_timer_tick, s); > + =C2=A0 =C2=A0s->timer =3D ptimer_init(bh); > + =C2=A0 =C2=A0vmstate_register(&dev->qdev, -1, &vmstate_imxp_timer, s); SysBusDeviceInfo struct. > + > + =C2=A0 =C2=A0return 0; > +} > + > +static void imx_timer_register_devices(void) > +{ > + =C2=A0 =C2=A0DPRINTF("Registering Timers\n"); > + =C2=A0 =C2=A0sysbus_register_dev("imx_timerp", sizeof(imxp_timer_state)= , > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0imxp_timer_init); > + =C2=A0 =C2=A0sysbus_register_dev("imx_timerg", sizeof(imxg_timer_state)= , > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0imxg_timer_init); > +} > + > + > +device_init(imx_timer_register_devices); Unnecessary semicolon. -- PMM