From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([208.118.235.92]:48027) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1URlXW-00057k-16 for qemu-devel@nongnu.org; Mon, 15 Apr 2013 11:41:59 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1URlXN-0008Gv-Co for qemu-devel@nongnu.org; Mon, 15 Apr 2013 11:41:53 -0400 Received: from smtp1-g21.free.fr ([2a01:e0c:1:1599::10]:59483) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1URlXM-0008G7-Co for qemu-devel@nongnu.org; Mon, 15 Apr 2013 11:41:45 -0400 Received: from mail.thom.fr.eu.org (unknown [IPv6:2a01:e35:2f1f:e7b0:215:17ff:fec0:7e1f]) by smtp1-g21.free.fr (Postfix) with ESMTP id E34C69401AE for ; Mon, 15 Apr 2013 17:41:37 +0200 (CEST) Received: from mail.thom.fr.eu.org (localhost [127.0.0.1]) by mail.thom.fr.eu.org (8.14.3/8.14.3/Debian-9.4) with ESMTP id r3FFfYSG014223 for ; Mon, 15 Apr 2013 17:41:34 +0200 MIME-Version: 1.0 Content-Type: multipart/alternative; boundary="=_de7def6e29a62ae4b1c1a03617d3d96d" Date: Mon, 15 Apr 2013 17:41:34 +0200 From: =?UTF-8?Q?Fran=C3=A7ois_Legal?= Message-ID: <3e0e268a897603d5ae067f6e01fad68e@thom.fr.eu.org> Subject: [Qemu-devel] [PATCH] ARM Cortex A9 Global Timer List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org --=_de7def6e29a62ae4b1c1a03617d3d96d Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable =20 Hello,=20 I made up this patch to implement the Cortex A9 global timer in Qemu.=20 My patch is based on the Qemu branch maintained by Xilinx for the Zynq. diff -urN qemu-master/hw/cpu/a9mpcore.c qemu-master.new/hw/cpu/a9mpcore.c --- qemu-master/hw/cpu/a9mpcore.c 2013-04-08 20:12:33.000000000 +0200 +++ qemu-master.new/hw/cpu/a9mpcore.c 2013-04-15 12:54:06.000000000 +0200 @@ -15,6 +15,7 @@ uint32_t num_cpu; MemoryRegion container; DeviceState *mptimer; + DeviceState *mpgtimer; DeviceState *wdt; DeviceState *gic; DeviceState *scu; @@ -31,6 +32,7 @@ { A9MPPrivState *s =3D FROM_SYSBUS(A9MPPrivState, dev); SysBusDevice *timerbusdev, *wdtbusdev, *gicbusdev, *scubusdev; + SysBusDevice *gtimerbusdev; int i; s->gic =3D qdev_create(NULL, "arm_gic"); @@ -50,6 +52,11 @@ qdev_init_nofail(s->scu); scubusdev =3D SYS_BUS_DEVICE(s->scu); + s->mpgtimer =3D qdev_create(NULL, "arm_mp_globaltimer"); + qdev_prop_set_uint32(s->mpgtimer, "num-cpu", s->num_cpu); + qdev_init_nofail(s->mpgtimer); + gtimerbusdev =3D SYS_BUS_DEVICE(s->mpgtimer); + s->mptimer =3D qdev_create(NULL, "arm_mptimer"); qdev_prop_set_uint32(s->mptimer, "num-cpu", s->num_cpu); qdev_init_nofail(s->mptimer); @@ -68,8 +75,6 @@ * 0x0600-0x06ff -- private timers and watchdogs * 0x0700-0x0fff -- nothing * 0x1000-0x1fff -- GIC Distributor - * - * We should implement the global timer but don't currently do so. */ memory_region_init(&s->container, "a9mp-priv-container", 0x2000); memory_region_add_subregion(&s->container, 0, @@ -80,6 +85,8 @@ /* Note that the A9 exposes only the "timer/watchdog for this core" * memory region, not the "timer/watchdog for core X" ones 11MPcore has. */ + memory_region_add_subregion(&s->container, 0x200, + sysbus_mmio_get_region(gtimerbusdev, 0)); memory_region_add_subregion(&s->container, 0x600, sysbus_mmio_get_region(timerbusdev, 0)); memory_region_add_subregion(&s->container, 0x620, @@ -90,10 +97,13 @@ sysbus_init_mmio(dev, &s->container); /* Wire up the interrupt from each watchdog and timer. - * For each core the timer is PPI 29 and the watchdog PPI 30. + * For each core the global timer is PPI 27, the private + * timer is PPI 29 and the watchdog PPI 30. */ for (i =3D 0; i < s->num_cpu; i++) { int ppibase =3D (s->num_irq - 32) + i * 32; + sysbus_connect_irq(gtimerbusdev, i, + qdev_get_gpio_in(s->gic, ppibase + 27)); sysbus_connect_irq(timerbusdev, i, qdev_get_gpio_in(s->gic, ppibase + 29)); sysbus_connect_irq(wdtbusdev, i, diff -urN qemu-master/hw/timer/arm_mpgtimer.c qemu-master.new/hw/timer/arm_mpgtimer.c --- qemu-master/hw/timer/arm_mpgtimer.c 1970-01-01 01:00:00.000000000 +0100 +++ qemu-master.new/hw/timer/arm_mpgtimer.c 2013-04-15 13:56:23.000000000 +0200 @@ -0,0 +1,359 @@ +/* + * Global peripheral timer block for ARM 11MPCore and A9MP + * + * Written by Fran=C3=A7ois LEGAL + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#include "hw/sysbus.h" +#include "qemu/timer.h" + +/* This device implements the per-cpu private timer and watchdog block + * which is used in both the ARM11MPCore and Cortex-A9MP. + */ + +#define MAX_CPUS 4 + +/* State of a single gtimer or block */ +typedef struct { + uint32_t control; + uint64_t compare; + uint32_t inc; + uint32_t status; + int64_t tick; + + int64_t delta; + uint64_t *gtimer_counter; + uint32_t *gtimer_control; + + QEMUTimer *timer; + MemoryRegion iomem; + qemu_irq irq; +} gTimerBlock; + +typedef struct { + SysBusDevice busdev; + uint32_t num_cpu; + uint64_t gtimer_counter; + uint32_t gtimer_control; + gTimerBlock gtimer[MAX_CPUS]; + MemoryRegion iomem; +} ARMMPGTimerState; + +static inline int get_current_cpu(ARMMPGTimerState *s) +{ + CPUState *cpu_single_cpu; + + if (cpu_single_env !=3D NULL) { + cpu_single_cpu =3D ENV_GET_CPU(cpu_single_env); + + if (cpu_single_cpu->cpu_index >=3D s->num_cpu) { + hw_error("arm_mptimer: num-cpu %d but this cpu is %d!n", + s->num_cpu, cpu_single_cpu->cpu_index); + } + return cpu_single_cpu->cpu_index; + } else { + return 0; + } +} + +static inline void gtimerblock_update_irq(gTimerBlock *gtb) +{ + qemu_set_irq(gtb->irq, gtb->status); +} + +/* Return conversion factor from mpcore timer ticks to qemu timer ticks. */ +static inline uint32_t gtimerblock_scale(gTimerBlock *gtb) +{ + return ((((*gtb->gtimer_control) >> 8) & 0xff) + 1) * 10; +} + +static void gtimerblock_reload(gTimerBlock *gtb, int restart) +{ + if (restart) { + gtb->tick =3D qemu_get_clock_ns(vm_clock); + gtb->tick +=3D (int64_t)(((gtb->compare - *gtb->gtimer_counter) + + gtb->delta) * gtimerblock_scale(gtb)); + } else { + gtb->tick +=3D (int64_t)(gtb->inc * gtimerblock_scale(gtb)); + } + qemu_mod_timer(gtb->timer, gtb->tick); +} + +static void gtimerblock_tick(void *opaque) +{ + gTimerBlock *gtb =3D (gTimerBlock *)opaque; + *gtb->gtimer_counter =3D gtb->compare; + if ((gtb->control | *gtb->gtimer_control) & 0x9) { + gtb->compare +=3D gtb->inc; + gtimerblock_reload(gtb, 0); + } + if (((gtb->control | *gtb->gtimer_control) & 0x7) && + ((gtb->status & 1) =3D=3D 0)) { + gtb->status =3D 1; + gtimerblock_update_irq(gtb); + } +} + +static uint64_t gtimer_read(void *opaque, hwaddr addr, + unsigned size) +{ + gTimerBlock *gtb =3D (gTimerBlock *)opaque; + int64_t val =3D 0; + switch (addr) { + case 0: /* Counter LSB */ + if (*gtb->gtimer_control & 1) { + val =3D gtb->tick - qemu_get_clock_ns(vm_clock); + val /=3D gtimerblock_scale(gtb); + } + return val < 0 ? val : 0 & 0xFFFFFFFF; + case 4: /* Counter MSB. */ + if (*gtb->gtimer_control & 1) { + val =3D gtb->tick - qemu_get_clock_ns(vm_clock); + val /=3D gtimerblock_scale(gtb); + } + return val < 0 ? (val >> 32) : 0; + case 8: /* Control. */ + return (gtb->control & 0x0000000E) | + ((*gtb->gtimer_control) & 0x0000FF01); + case 12: /* Interrupt status. */ + return gtb->status; + case 16: /* Compare LSB */ + return gtb->compare & 0xFFFFFFFF; + case 20: /* Counter MSB */ + return gtb->compare >> 32; + case 24: /* Autoincrement */ + return gtb->inc; + default: + return 0; + } +} + +static void gtimer_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + gTimerBlock *gtb =3D (gTimerBlock *)opaque; + int64_t old; + switch (addr) { + case 0: /* Counter LSB */ + old =3D (*gtb->gtimer_counter); + old &=3D 0xFFFFFFFF; + gtb->delta =3D old - (value & 0xFFFFFFFF); + old |=3D value & 0xFFFFFFFF; + *gtb->gtimer_counter =3D old; + /* Cancel the previous timer. */ + if (((gtb->control | *gtb->gtimer_control) & 7) =3D=3D 7) { + gtimerblock_reload(gtb, 1); + } + break; + case 4: /* Counter MSB. */ + old =3D (*gtb->gtimer_counter); + old &=3D 0xFFFFFFFF00000000; + gtb->delta =3D old - (value << 32); + old |=3D value << 32; + *gtb->gtimer_counter =3D old; + if (((gtb->control | *gtb->gtimer_control) & 7) =3D=3D 7) { + gtimerblock_reload(gtb, 1); + } + break; + case 8: /* Control. */ + old =3D *gtb->gtimer_control; + gtb->control =3D value & 0x0000000E; + *gtb->gtimer_control =3D value & 0x0000FF01; + if (((old & 1) =3D=3D 0) && ((value & 7) =3D=3D 7)) { + gtb->delta =3D 0; + gtimerblock_reload(gtb, 1); + } + break; + case 12: /* Interrupt status. */ + gtb->status ^=3D value & 1; + gtimerblock_update_irq(gtb); + break; + case 16: /* Compare LSB */ + old =3D gtb->compare; + old &=3D 0xFFFFFFFF; + gtb->delta =3D (value & 0xFFFFFFFF) - old; + old |=3D value & 0xFFFFFFFF; + gtb->compare =3D old; + if (((gtb->control | *gtb->gtimer_control) & 7) =3D=3D 7) { + gtimerblock_reload(gtb, 1); + } + break; + case 20: /* Compare MSB. */ + old =3D gtb->compare; + old &=3D 0xFFFFFFFF00000000; + gtb->delta =3D (value << 32) - old; + old |=3D value << 32; + gtb->compare =3D old; + if (((gtb->control | *gtb->gtimer_control) & 7) =3D=3D 7) { + gtimerblock_reload(gtb, 1); + } + break; + case 24: /* Autoincrement */ + gtb->inc =3D value; + break; + default: + break; + } +} + +/* Wrapper functions to implement the "read timer/watchdog for + * the current CPU" memory regions. + */ +static uint64_t arm_thisgtimer_read(void *opaque, hwaddr addr, + unsigned size) +{ + ARMMPGTimerState *s =3D (ARMMPGTimerState *)opaque; + int id =3D get_current_cpu(s); + return gtimer_read(&s->gtimer[id], addr, size); +} + +static void arm_thisgtimer_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + ARMMPGTimerState *s =3D (ARMMPGTimerState *)opaque; + int id =3D get_current_cpu(s); + gtimer_write(&s->gtimer[id], addr, value, size); +} + +static const MemoryRegionOps arm_thisgtimer_ops =3D { + .read =3D arm_thisgtimer_read, + .write =3D arm_thisgtimer_write, + .valid =3D { + .min_access_size =3D 4, + .max_access_size =3D 4, + }, + .endianness =3D DEVICE_NATIVE_ENDIAN, +}; + +static const MemoryRegionOps gtimerblock_ops =3D { + .read =3D gtimer_read, + .write =3D gtimer_write, + .valid =3D { + .min_access_size =3D 4, + .max_access_size =3D 4, + }, + .endianness =3D DEVICE_NATIVE_ENDIAN, +}; + +static void gtimer_reset(gTimerBlock *gtb) +{ + gtb->control =3D 0; + gtb->status =3D 0; + gtb->compare =3D 0; + gtb->inc =3D 0; + gtb->tick =3D 0; +} + +static void arm_mpgtimer_reset(DeviceState *dev) +{ + ARMMPGTimerState *s =3D + FROM_SYSBUS(ARMMPGTimerState, SYS_BUS_DEVICE(dev)); + int i; + for (i =3D 0; i < ARRAY_SIZE(s->gtimer); i++) { + gtimer_reset(&s->gtimer[i]); + } +} + +static int arm_mpgtimer_init(SysBusDevice *dev) +{ + ARMMPGTimerState *s =3D FROM_SYSBUS(ARMMPGTimerState, dev); + int i; + if (s->num_cpu < 1 || s->num_cpu > MAX_CPUS) { + hw_error("%s: num-cpu must be between 1 and %dn", __func__, MAX_CPUS); + } + + /* We implement one timer block per CPU, and expose multiple MMIO regions: + * * region 0 is "timer for this core" + * * region 1 is "timer for core 0" + * * region 2 is "timer for core 1" + * and so on. + * The outgoing interrupt lines are + * * timer for core 0 + * * timer for core 1 + * and so on. + */ + memory_region_init_io(&s->iomem, &arm_thisgtimer_ops, s, + "arm_mptimer_gtimer", 0x20); + sysbus_init_mmio(dev, &s->iomem); + for (i =3D 0; i < s->num_cpu; i++) { + gTimerBlock *gtb =3D &s->gtimer[i]; + gtb->gtimer_counter =3D &s->gtimer_counter; + gtb->gtimer_control =3D &s->gtimer_control; + gtb->timer =3D qemu_new_timer_ns(vm_clock, gtimerblock_tick, gtb); + sysbus_init_irq(dev, >b->irq); + memory_region_init_io(>b->iomem, >imerblock_ops, gtb, + "arm_mptimer_gtimerblock", 0x20); + sysbus_init_mmio(dev, >b->iomem); + } + + return 0; +} + +static const VMStateDescription vmstate_gtimerblock =3D { + .name =3D "arm_mptimer_gtimerblock", + .version_id =3D 2, + .minimum_version_id =3D 2, + .fields =3D (VMStateField[]) { + VMSTATE_UINT32(control, gTimerBlock), + VMSTATE_UINT32(status, gTimerBlock), + VMSTATE_UINT64(compare, gTimerBlock), + VMSTATE_INT64(tick, gTimerBlock), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_arm_mpgtimer =3D { + .name =3D "arm_mp_globaltimer", + .version_id =3D 2, + .minimum_version_id =3D 2, + .fields =3D (VMStateField[]) { + VMSTATE_STRUCT_VARRAY_UINT32(gtimer, ARMMPGTimerState, num_cpu, + 1, vmstate_gtimerblock, gTimerBlock), + VMSTATE_END_OF_LIST() + } +}; + +static Property arm_mpgtimer_properties[] =3D { + DEFINE_PROP_UINT32("num-cpu", ARMMPGTimerState, num_cpu, 0), + DEFINE_PROP_END_OF_LIST() +}; + +static void arm_mpgtimer_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc =3D DEVICE_CLASS(klass); + SysBusDeviceClass *sbc =3D SYS_BUS_DEVICE_CLASS(klass); + + sbc->init =3D arm_mpgtimer_init; + dc->vmsd =3D &vmstate_arm_mpgtimer; + dc->reset =3D arm_mpgtimer_reset; + dc->no_user =3D 1; + dc->props =3D arm_mpgtimer_properties; +} + +static const TypeInfo arm_mpgtimer_info =3D { + .name =3D "arm_mp_globaltimer", + .parent =3D TYPE_SYS_BUS_DEVICE, + .instance_size =3D sizeof(ARMMPGTimerState), + .class_init =3D arm_mpgtimer_class_init, +}; + +static void arm_mpgtimer_register_types(void) +{ + type_register_static(&arm_mpgtimer_info); +} + +type_init(arm_mpgtimer_register_types) + diff -urN qemu-master/hw/timer/arm_mptimer.c qemu-master.new/hw/timer/arm_mptimer.c --- qemu-master/hw/timer/arm_mptimer.c 2013-04-08 20:12:33.000000000 +0200 +++ qemu-master.new/hw/timer/arm_mptimer.c 2013-04-15 13:44:33.000000000 +0200 @@ -49,13 +49,19 @@ static inline int get_current_cpu(ARMMPTimerState *s) { - CPUState *cpu_single_cpu =3D ENV_GET_CPU(cpu_single_env); + CPUState *cpu_single_cpu; - if (cpu_single_cpu->cpu_index >=3D s->num_cpu) { - hw_error("arm_mptimer: num-cpu %d but this cpu is %d!n", - s->num_cpu, cpu_single_cpu->cpu_index); + if (cpu_single_env !=3D NULL) { + cpu_single_cpu =3D ENV_GET_CPU(cpu_single_env); + + if (cpu_single_cpu->cpu_index >=3D s->num_cpu) { + hw_error("arm_mptimer: num-cpu %d but this cpu is %d!n", + s->num_cpu, cpu_single_cpu->cpu_index); + } + return cpu_single_cpu->cpu_index; + } else { + return 0; } - return cpu_single_cpu->cpu_index; } static inline void timerblock_update_irq(TimerBlock *tb) diff -urN qemu-master/hw/timer/Makefile.objs qemu-master.new/hw/timer/Makefile.objs --- qemu-master/hw/timer/Makefile.objs 2013-04-08 20:12:33.000000000 +0200 +++ qemu-master.new/hw/timer/Makefile.objs 2013-04-15 12:54:06.000000000 +020= 0 @@ -24,5 +24,5 @@ obj-$(CONFIG_SH4) +=3D sh_timer.o obj-$(CONFIG_TUSB6010) +=3D tusb6010.o -obj-$(CONFIG_ARM_MPTIMER) +=3D arm_mptimer.o +obj-$(CONFIG_ARM_MPTIMER) +=3D arm_mptimer.o arm_mpgtimer.o obj-$(CONFIG_MC146818RTC) +=3D mc146818rtc.o=20 Signed-off-by: Fran=C3=A7ois LEGAL =20 =20 --=_de7def6e29a62ae4b1c1a03617d3d96d Content-Transfer-Encoding: quoted-printable Content-Type: text/html; charset=UTF-8

Hello,

 

I made up this patch to implement the Cortex A9 global timer in Qemu.

My patch is based on the Qemu branch maintained by Xilinx for the Zynq= =2E

 

diff -urN qemu-master/hw/cpu/a9mpcore.c qemu-master.new/hw/cpu/a9mpcore= =2Ec
--- qemu-master/hw/cpu/a9mpcore.c    2013-04-08 20= :12:33.000000000 +0200
+++ qemu-master.new/hw/cpu/a9mpcore.c &nbs= p;  2013-04-15 12:54:06.000000000 +0200
@@ -15,6 +15,7 @@
&n= bsp;    uint32_t num_cpu;
     Memo= ryRegion container;
     DeviceState *mptimer;
+    DeviceState *mpgtimer;
     = DeviceState *wdt;
     DeviceState *gic;
&nbs= p;    DeviceState *scu;
@@ -31,6 +32,7 @@
 {<= br />     A9MPPrivState *s =3D FROM_SYSBUS(A9MPPrivStat= e, dev);
     SysBusDevice *timerbusdev, *wdtbusde= v, *gicbusdev, *scubusdev;
+    SysBusDevice *gtimerbus= dev;
     int i;
 
  &nbs= p;  s->gic =3D qdev_create(NULL, "arm_gic");
@@ -50,6 +52,11 @= @
     qdev_init_nofail(s->scu);
 &nb= sp;   scubusdev =3D SYS_BUS_DEVICE(s->scu);
 
+=     s->mpgtimer =3D qdev_create(NULL, "arm_mp_globaltimer= ");
+    qdev_prop_set_uint32(s->mpgtimer, "num-cpu"= , s->num_cpu);
+    qdev_init_nofail(s->mpgtimer)= ;
+    gtimerbusdev =3D SYS_BUS_DEVICE(s->mpgtimer);=
+
     s->mptimer =3D qdev_create(NULL, "= arm_mptimer");
     qdev_prop_set_uint32(s->mpt= imer, "num-cpu", s->num_cpu);
     qdev_init_no= fail(s->mptimer);
@@ -68,8 +75,6 @@
    &n= bsp; *  0x0600-0x06ff -- private timers and watchdogs
  = ;    *  0x0700-0x0fff -- nothing
   = ;   *  0x1000-0x1fff -- GIC Distributor
-  &n= bsp;  *
-     * We should implement the globa= l timer but don't currently do so.
      */     memory_region_init(&s->container, "a9mp= -priv-container", 0x2000);
     memory_region_add_= subregion(&s->container, 0,
@@ -80,6 +85,8 @@
  =    /* Note that the A9 exposes only the "timer/watchdog for this = core"
      * memory region, not the "timer/w= atchdog for core X" ones 11MPcore has.
      = */
+    memory_region_add_subregion(&s->containe= r, 0x200,
+          = ;            &n= bsp;         sysbus_mmio_get_region= (gtimerbusdev, 0));
     memory_region_add_subregi= on(&s->container, 0x600,
      &n= bsp;            = ;            &n= bsp; sysbus_mmio_get_region(timerbusdev, 0));
    = memory_region_add_subregion(&s->container, 0x620,
@@ -90,10 +9= 7,13 @@
     sysbus_init_mmio(dev, &s->cont= ainer);
 
     /* Wire up the interrupt = from each watchdog and timer.
-     * For each cor= e the timer is PPI 29 and the watchdog PPI 30.
+   &nbs= p; * For each core the global timer is PPI 27, the private
+ &nbs= p;   * timer is PPI 29 and the watchdog PPI 30.
  =     */
     for (i =3D 0; i < s-= >num_cpu; i++) {
         i= nt ppibase =3D (s->num_irq - 32) + i * 32;
+    = ;    sysbus_connect_irq(gtimerbusdev, i,
+  &= nbsp;           &nbs= p;            qdev_g= et_gpio_in(s->gic, ppibase + 27));
     &n= bsp;   sysbus_connect_irq(timerbusdev, i,
   =             &nb= sp;            qdev_= get_gpio_in(s->gic, ppibase + 29));
     &= nbsp;   sysbus_connect_irq(wdtbusdev, i,
diff -urN qemu-mast= er/hw/timer/arm_mpgtimer.c qemu-master.new/hw/timer/arm_mpgtimer.c
---= qemu-master/hw/timer/arm_mpgtimer.c    1970-01-01 01:00:00= =2E000000000 +0100
+++ qemu-master.new/hw/timer/arm_mpgtimer.c &n= bsp;  2013-04-15 13:56:23.000000000 +0200
@@ -0,0 +1,359 @@
= +/*
+ * Global peripheral timer block for ARM 11MPCore and A9MP
+= *
+ * Written by François LEGAL
+ *
+ * This program= is free software; you can redistribute it and/or
+ * modify it under = the terms of the GNU General Public License
+ * as published by the Fr= ee Software Foundation; either version
+ * 2 of the License, or (at yo= ur option) any later version.
+ *
+ * This program is distributed= in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; wit= hout even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A= PARTICULAR PURPOSE.  See the
+ * GNU General Public License for = more details.
+ *
+ * You should have received a copy of the GNU = General Public License along
+ * with this program; if not, see <ht= tp://www.gnu.org/licenses/>.
+ */
+
+#include "hw/sysbus= =2Eh"
+#include "qemu/timer.h"
+
+/* This device implements = the per-cpu private timer and watchdog block
+ * which is used in both= the ARM11MPCore and Cortex-A9MP.
+ */
+
+#define MAX_CPUS 4=
+
+/* State of a single gtimer or block */
+typedef struct = {
+    uint32_t control;
+    uint6= 4_t compare;
+    uint32_t inc;
+   = ; uint32_t status;
+    int64_t  tick;
+
+    int64_t    delta;
+  &nb= sp; uint64_t *gtimer_counter;
+    uint32_t *gtimer_con= trol;
+
+    QEMUTimer *timer;
+  &= nbsp; MemoryRegion iomem;
+    qemu_irq irq;
+} gT= imerBlock;
+
+typedef struct {
+    SysBusDev= ice busdev;
+    uint32_t num_cpu;
+  &n= bsp; uint64_t gtimer_counter;
+    uint32_t gtimer_cont= rol;
+    gTimerBlock gtimer[MAX_CPUS];
+ &nb= sp;  MemoryRegion iomem;
+} ARMMPGTimerState;
+
+static= inline int get_current_cpu(ARMMPGTimerState *s)
+{
+  =   CPUState *cpu_single_cpu;
+
+    if (cpu_si= ngle_env !=3D NULL) {
+        cpu_= single_cpu =3D ENV_GET_CPU(cpu_single_env);
+
+   =      if (cpu_single_cpu->cpu_index >=3D s->num= _cpu) {
+          &= nbsp; hw_error("arm_mptimer: num-cpu %d but this cpu is %d!\n",
+ = ;            &n= bsp;       s->num_cpu, cpu_single_cpu->= cpu_index);
+        }
+ =        return cpu_single_cpu->cpu_index;+    } else {
+      =   return 0;
+    }
+}
+
+static inl= ine void gtimerblock_update_irq(gTimerBlock *gtb)
+{
+  = ;  qemu_set_irq(gtb->irq, gtb->status);
+}
+
+/* = Return conversion factor from mpcore timer ticks to qemu timer ticks. = */
+static inline uint32_t gtimerblock_scale(gTimerBlock *gtb)
+= {
+    return ((((*gtb->gtimer_control) >> 8) = & 0xff) + 1) * 10;
+}
+
+static void gtimerblock_reload(= gTimerBlock *gtb, int restart)
+{
+    if (restart= ) {
+        gtb->tick =3D qemu_= get_clock_ns(vm_clock);
+        gt= b->tick +=3D (int64_t)(((gtb->compare - *gtb->gtimer_counter) ++            = ;            &n= bsp;        gtb->delta) * gtimerblock= _scale(gtb));
+    } else {
+   &nb= sp;    gtb->tick +=3D (int64_t)(gtb->inc * gtimerblock= _scale(gtb));
+    }
+    qemu_mod_= timer(gtb->timer, gtb->tick);
+}
+
+static void gtimer= block_tick(void *opaque)
+{
+    gTimerBlock *gtb = =3D (gTimerBlock *)opaque;
+    *gtb->gtimer_counter= =3D gtb->compare;
+    if ((gtb->control | *gtb-= >gtimer_control) & 0x9) {
+      =   gtb->compare +=3D gtb->inc;
+    &nbs= p;   gtimerblock_reload(gtb, 0);
+    }
= +    if (((gtb->control | *gtb->gtimer_control) & = 0x7) &&
+        ((gtb->= status & 1) =3D=3D 0)) {
+      &nbs= p; gtb->status =3D 1;
+        g= timerblock_update_irq(gtb);
+    }
+}
+
= +static uint64_t gtimer_read(void *opaque, hwaddr addr,
+  &= nbsp;           &nbs= p;            &= nbsp;    unsigned size)
+{
+    gTi= merBlock *gtb =3D (gTimerBlock *)opaque;
+    int64_t v= al =3D 0;
+    switch (addr) {
+   = case 0: /* Counter LSB */
+       = if (*gtb->gtimer_control & 1)  {
+    = ;        val =3D gtb->tick - qemu_get= _clock_ns(vm_clock);
+        =     val /=3D gtimerblock_scale(gtb);
+   = ;     }
+       = ; return val < 0 ? val : 0 & 0xFFFFFFFF;
+    ca= se 4: /* Counter MSB.  */
+      &n= bsp; if (*gtb->gtimer_control & 1)  {
+   &= nbsp;        val =3D gtb->tick - qemu= _get_clock_ns(vm_clock);
+       &n= bsp;    val /=3D gtimerblock_scale(gtb);
+  &= nbsp;     }
+      &= nbsp; return val < 0 ? (val >> 32) : 0;
+    c= ase 8: /* Control.  */
+       = ; return (gtb->control & 0x0000000E) |
+    = ;           ((*gtb->gt= imer_control) & 0x0000FF01);
+    case 12: /* Inter= rupt status.  */
+        retu= rn gtb->status;
+    case 16: /* Compare LSB */
+        return gtb->compare & 0= xFFFFFFFF;
+    case 20: /* Counter MSB  */
+=         return gtb->compare >> = 32;
+    case 24: /* Autoincrement */
+  = ;      return gtb->inc;
+   = ; default:
+        return 0;
= +    }
+}
+
+static void gtimer_write(void *o= paque, hwaddr addr,
+        &= nbsp;           &nbs= p;        uint64_t value, unsigned size)=
+{
+    gTimerBlock *gtb =3D (gTimerBlock *)opaqu= e;
+    int64_t old;
+    switch (a= ddr) {
+    case 0: /* Counter LSB */
+  = ;      old =3D (*gtb->gtimer_counter);
+&n= bsp;       old &=3D 0xFFFFFFFF;
+&nb= sp;       gtb->delta =3D old - (value &= ; 0xFFFFFFFF);
+        old |=3D va= lue & 0xFFFFFFFF;
+        *gtb= ->gtimer_counter =3D old;
+      &nbs= p; /* Cancel the previous timer.  */
+    &nb= sp;   if (((gtb->control | *gtb->gtimer_control) & 7) = =3D=3D 7) {
+         &nb= sp;  gtimerblock_reload(gtb, 1);
+     &= nbsp;  }
+        break;
= +    case 4: /* Counter MSB.  */
+  &nbs= p;     old =3D (*gtb->gtimer_counter);
+ &= nbsp;      old &=3D 0xFFFFFFFF00000000;
+=         gtb->delta =3D old - (value &= lt;< 32);
+        old |=3D valu= e << 32;
+        *gtb->gt= imer_counter =3D old;
+        if (= ((gtb->control | *gtb->gtimer_control) & 7) =3D=3D 7) {
+&nb= sp;           gtimerblock= _reload(gtb, 1);
+        }
+&= nbsp;       break;
+    c= ase 8: /* Control.  */
+       = ; old =3D *gtb->gtimer_control;
+     &nbs= p;  gtb->control =3D value & 0x0000000E;
+  &nbs= p;     *gtb->gtimer_control =3D value & 0x0000FF= 01;
+        if (((old & 1) =3D= =3D 0) && ((value & 7) =3D=3D 7)) {
+   &nb= sp;        gtb->delta =3D 0;
+&n= bsp;           gtimerbloc= k_reload(gtb, 1);
+        }
+=         break;
+    = case 12: /* Interrupt status.  */
+     =    gtb->status ^=3D value & 1;
+   &nb= sp;    gtimerblock_update_irq(gtb);
+   =      break;
+    case 16: /* Compar= e LSB */
+        old =3D gtb->c= ompare;
+        old &=3D 0xFFF= FFFFF;
+        gtb->delta =3D (= value & 0xFFFFFFFF) - old;
+      &n= bsp; old |=3D value & 0xFFFFFFFF;
+     &= nbsp;  gtb->compare =3D old;
+     &n= bsp;  if (((gtb->control | *gtb->gtimer_control) & 7) =3D=3D= 7) {
+          &nb= sp; gtimerblock_reload(gtb, 1);
+      &= nbsp; }
+        break;
+ = ;   case 20: /* Compare MSB.  */
+   &nb= sp;    old =3D gtb->compare;
+   &nbs= p;    old &=3D 0xFFFFFFFF00000000;
+  &nb= sp;     gtb->delta =3D (value << 32) - old;+        old |=3D value << 32;<= br />+        gtb->compare =3D old;+        if (((gtb->control | *gt= b->gtimer_control) & 7) =3D=3D 7) {
+    &n= bsp;       gtimerblock_reload(gtb, 1);
+=         }
+    =     break;
+    case 24: /* Autoincremen= t */
+        gtb->inc =3D value= ;
+        break;
+  = ;  default:
+        break;+    }
+}
+
+/* Wrapper functions to imple= ment the "read timer/watchdog for
+ * the current CPU" memory regions= =2E
+ */
+static uint64_t arm_thisgtimer_read(void *opaque, hwadd= r addr,
+          &= nbsp;           &nbs= p;            unsign= ed size)
+{
+    ARMMPGTimerState *s =3D (ARMMPGTi= merState *)opaque;
+    int id =3D get_current_cpu(s);<= br />+    return gtimer_read(&s->gtimer[id], addr, si= ze);
+}
+
+static void arm_thisgtimer_write(void *opaque, hw= addr addr,
+         &nbs= p;            &= nbsp;         uint64_t value, unsig= ned size)
+{
+    ARMMPGTimerState *s =3D (ARMMPGT= imerState *)opaque;
+    int id =3D get_current_cpu(s);=
+    gtimer_write(&s->gtimer[id], addr, value, = size);
+}
+
+static const MemoryRegionOps arm_thisgtimer_ops= =3D {
+    .read =3D arm_thisgtimer_read,
+ =    .write =3D arm_thisgtimer_write,
+    .val= id =3D {
+        .min_access_size = =3D 4,
+        .max_access_size = =3D 4,
+    },
+    .endianness =3D= DEVICE_NATIVE_ENDIAN,
+};
+
+static const MemoryRegionOps g= timerblock_ops =3D {
+    .read =3D gtimer_read,
+=     .write =3D gtimer_write,
+    .valid= =3D {
+        .min_access_size = =3D 4,
+        .max_access_size = =3D 4,
+    },
+    .endianness =3D= DEVICE_NATIVE_ENDIAN,
+};
+
+static void gtimer_reset(gTime= rBlock *gtb)
+{
+    gtb->control =3D 0;
+=     gtb->status =3D 0;
+    gtb->c= ompare =3D 0;
+    gtb->inc =3D 0;
+  = ;  gtb->tick =3D 0;
+}
+
+static void arm_mpgtimer_r= eset(DeviceState *dev)
+{
+    ARMMPGTimerState *s= =3D
+        FROM_SYSBUS(ARMMPGTim= erState, SYS_BUS_DEVICE(dev));
+    int i;
+ =    for (i =3D 0; i < ARRAY_SIZE(s->gtimer); i++) {
+&n= bsp;       gtimer_reset(&s->gtimer[i])= ;
+    }
+}
+
+static int arm_mpgtimer_i= nit(SysBusDevice *dev)
+{
+    ARMMPGTimerState *s= =3D FROM_SYSBUS(ARMMPGTimerState, dev);
+    int i;+    if (s->num_cpu < 1 || s->num_cpu > MAX_C= PUS) {
+        hw_error("%s: num-c= pu must be between 1 and %d\n", __func__, MAX_CPUS);
+  &nbs= p; }
+
+    /* We implement one timer block per CP= U, and expose multiple MMIO regions:
+     * = * region 0 is "timer for this core"
+     * = * region 1 is "timer for core 0"
+     *  * = region 2 is "timer for core 1"
+     * and so on= =2E
+     * The outgoing interrupt lines are
= +     *  * timer for core 0
+  &nbs= p;  *  * timer for core 1
+     * and so= on.
+     */
+    memory_regi= on_init_io(&s->iomem, &arm_thisgtimer_ops, s,
+  =             &nb= sp;           "arm_mptime= r_gtimer", 0x20);
+    sysbus_init_mmio(dev, &s->= ;iomem);
+    for (i =3D 0; i < s->num_cpu; i++) = {
+        gTimerBlock *gtb =3D &am= p;s->gtimer[i];
+        gtb->= ;gtimer_counter =3D &s->gtimer_counter;
+   &nbs= p;    gtb->gtimer_control =3D &s->gtimer_control;<= br />+        gtb->timer =3D qemu_new= _timer_ns(vm_clock, gtimerblock_tick, gtb);
+    &= nbsp;   sysbus_init_irq(dev, &gtb->irq);
+  = ;      memory_region_init_io(&gtb->iomem, &= amp;gtimerblock_ops, gtb,
+       &= nbsp;           &nbs= p;          "arm_mptimer_gtime= rblock", 0x20);
+        sysbus_ini= t_mmio(dev, &gtb->iomem);
+    }
+
+&n= bsp;   return 0;
+}
+
+static const VMStateDescrip= tion vmstate_gtimerblock =3D {
+    .name =3D "arm_mpti= mer_gtimerblock",
+    .version_id =3D 2,
+ &= nbsp;  .minimum_version_id =3D 2,
+    .fields =3D= (VMStateField[]) {
+        VMSTAT= E_UINT32(control, gTimerBlock),
+      &= nbsp; VMSTATE_UINT32(status, gTimerBlock),
+    &n= bsp;   VMSTATE_UINT64(compare, gTimerBlock),
+  &n= bsp;     VMSTATE_INT64(tick, gTimerBlock),
+ =        VMSTATE_END_OF_LIST()
+ &nbs= p;  }
+};
+
+static const VMStateDescription vmstate_ar= m_mpgtimer =3D {
+    .name =3D "arm_mp_globaltimer",+    .version_id =3D 2,
+    .minimu= m_version_id =3D 2,
+    .fields =3D (VMStateField[]) {=
+    VMSTATE_STRUCT_VARRAY_UINT32(gtimer, ARMMPGTimerS= tate, num_cpu,
+         =             &nb= sp;           1, vmstate_= gtimerblock, gTimerBlock),
+       = VMSTATE_END_OF_LIST()
+    }
+};
+
+sta= tic Property arm_mpgtimer_properties[] =3D {
+    DEFIN= E_PROP_UINT32("num-cpu", ARMMPGTimerState, num_cpu, 0),
+  &= nbsp; DEFINE_PROP_END_OF_LIST()
+};
+
+static void arm_mpgti= mer_class_init(ObjectClass *klass, void *data)
+{
+  &n= bsp; DeviceClass *dc =3D DEVICE_CLASS(klass);
+    SysB= usDeviceClass *sbc =3D SYS_BUS_DEVICE_CLASS(klass);
+
+ &nbs= p;  sbc->init =3D arm_mpgtimer_init;
+    dc-&g= t;vmsd =3D &vmstate_arm_mpgtimer;
+    dc->reset= =3D arm_mpgtimer_reset;
+    dc->no_user =3D 1;
+    dc->props =3D arm_mpgtimer_properties;
+}
+
+static const TypeInfo arm_mpgtimer_info =3D {
+  &= nbsp; .name          =3D "arm_= mp_globaltimer",
+    .parent    &n= bsp;   =3D TYPE_SYS_BUS_DEVICE,
+    .instanc= e_size =3D sizeof(ARMMPGTimerState),
+    .class_init&n= bsp;   =3D arm_mpgtimer_class_init,
+};
+
+static = void arm_mpgtimer_register_types(void)
+{
+    typ= e_register_static(&arm_mpgtimer_info);
+}
+
+type_init(a= rm_mpgtimer_register_types)
+
diff -urN qemu-master/hw/timer/arm_= mptimer.c qemu-master.new/hw/timer/arm_mptimer.c
--- qemu-master/hw/ti= mer/arm_mptimer.c    2013-04-08 20:12:33.000000000 +0200
+++ qemu-master.new/hw/timer/arm_mptimer.c    2013-04-15 1= 3:44:33.000000000 +0200
@@ -49,13 +49,19 @@
 
 sta= tic inline int get_current_cpu(ARMMPTimerState *s)
 {
- = ;   CPUState *cpu_single_cpu =3D ENV_GET_CPU(cpu_single_env);
+    CPUState *cpu_single_cpu;
 
- &nb= sp;  if (cpu_single_cpu->cpu_index >=3D s->num_cpu) {
-&= nbsp;       hw_error("arm_mptimer: num-cpu %d= but this cpu is %d!\n",
-       &n= bsp;         s->num_cpu, cpu_sin= gle_cpu->cpu_index);
+    if (cpu_single_env !=3D NU= LL) {
+        cpu_single_cpu =3D E= NV_GET_CPU(cpu_single_env);
+
+     &nbs= p;  if (cpu_single_cpu->cpu_index >=3D s->num_cpu) {
+&n= bsp;           hw_error("= arm_mptimer: num-cpu %d but this cpu is %d!\n",
+   &nb= sp;            =      s->num_cpu, cpu_single_cpu->cpu_index);
+        }
+   &nbs= p;    return cpu_single_cpu->cpu_index;
+  = ;  } else {
+        return 0;=
     }
-    return cpu_single= _cpu->cpu_index;
 }
 
 static inline void = timerblock_update_irq(TimerBlock *tb)
diff -urN qemu-master/hw/timer/M= akefile.objs qemu-master.new/hw/timer/Makefile.objs
--- qemu-master/hw= /timer/Makefile.objs    2013-04-08 20:12:33.000000000 +0200<= br />+++ qemu-master.new/hw/timer/Makefile.objs    2013-04-1= 5 12:54:06.000000000 +0200
@@ -24,5 +24,5 @@
 obj-$(CONFIG_S= H4) +=3D sh_timer.o
 obj-$(CONFIG_TUSB6010) +=3D tusb6010.o
=  
-obj-$(CONFIG_ARM_MPTIMER) +=3D arm_mptimer.o
+obj-$(CONFI= G_ARM_MPTIMER) +=3D arm_mptimer.o arm_mpgtimer.o
 obj-$(CONFIG_MC= 146818RTC) +=3D mc146818rtc.o


Signed-off-by: François LEGAL <devel@thom.fr.eu.org><= /p>

 
 
 
--=_de7def6e29a62ae4b1c1a03617d3d96d--