qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH] Basic support for ARM A15 "architectured" (cp15) ti


From: Daniel Forsgren
Subject: [Qemu-devel] [PATCH] Basic support for ARM A15 "architectured" (cp15) timers
Date: Wed, 12 Sep 2012 11:49:18 +0000

This patch adds basic support for the "architected" timers (i.e. cp15)
found in A15. It's enough to allow Linux to boot, using arch_timer for
the tick. However - it is not a complete model of the timer block at
large, it is not that well structured, and it is currently tested with
qemu-linaro-1.1.50-2012.07 (not latest and greatest). It's simply a
prototype.

However, if anyone wants to play with the architectured (cp15) timers
instead of sp804, then please feel free to try it out. It has been
tested with linux-linaro-3.6-rc2-2012.08, and you can easily verify
the existence of these timers under /proc/interrupts:

    address@hidden:~# cat /proc/interrupts 
    cat /proc/interrupts 
               CPU0       
     29:       7424       GIC  arch_timer
     30:          0       GIC  arch_timer

Please note that this also requires some minor fixes that are not part
of qemu-linaro-1.1.50-2012.07:

    http://patches.linaro.org/9833/

Signed-off-by: Daniel Forsgren <address@hidden>

---

diff -Nupr qemu-linaro-1.1.50-2012.07/hw/a15mpcore.c 
qemu-linaro-1.1.50-2012.07-modified/hw/a15mpcore.c
--- qemu-linaro-1.1.50-2012.07/hw/a15mpcore.c   2012-07-05 16:48:28.000000000 
+0200
+++ qemu-linaro-1.1.50-2012.07-modified/hw/a15mpcore.c  2012-09-12 
11:24:25.844237405 +0200
@@ -28,6 +28,7 @@ typedef struct A15MPPrivState {
     uint32_t num_cpu;
     uint32_t num_irq;
     MemoryRegion container;
+    DeviceState *archtimer;
     DeviceState *gic;
 } A15MPPrivState;
 
@@ -40,7 +41,8 @@ static void a15mp_priv_set_irq(void *opa
 static int a15mp_priv_init(SysBusDevice *dev)
 {
     A15MPPrivState *s = FROM_SYSBUS(A15MPPrivState, dev);
-    SysBusDevice *busdev;
+    SysBusDevice *busdev, *timerbusdev;
+    int i;
 
     if (kvm_irqchip_in_kernel()) {
         s->gic = qdev_create(NULL, "kvm-arm_gic");
@@ -60,6 +62,11 @@ static int a15mp_priv_init(SysBusDevice
     /* Pass through inbound GPIO lines to the GIC */
     qdev_init_gpio_in(&s->busdev.qdev, a15mp_priv_set_irq, s->num_irq - 32);
 
+    s->archtimer = qdev_create(NULL, "arm_archtimer");
+    //    qdev_prop_set_uint32(s->archtimer, "num-cpu", s->num_cpu);
+    qdev_init_nofail(s->archtimer);
+    timerbusdev = sysbus_from_qdev(s->archtimer);
+
     /* Memory map (addresses are offsets from PERIPHBASE):
      *  0x0000-0x0fff -- reserved
      *  0x1000-0x1fff -- GIC Distributor
@@ -75,6 +82,16 @@ static int a15mp_priv_init(SysBusDevice
                                 sysbus_mmio_get_region(busdev, 1));
 
     sysbus_init_mmio(dev, &s->container);
+
+
+    for (i = 0; i < s->num_cpu; i++) {
+        int ppibase = (s->num_irq - 32) + i * 32;
+        sysbus_connect_irq(timerbusdev, i * 2,
+                           qdev_get_gpio_in(s->gic, ppibase + 29));
+        sysbus_connect_irq(timerbusdev, i * 2 + 1,
+                           qdev_get_gpio_in(s->gic, ppibase + 30));
+    }
+
     return 0;
 }
 
diff -Nupr qemu-linaro-1.1.50-2012.07/hw/arm/Makefile.objs 
qemu-linaro-1.1.50-2012.07-modified/hw/arm/Makefile.objs
--- qemu-linaro-1.1.50-2012.07/hw/arm/Makefile.objs     2012-07-05 
16:48:28.000000000 +0200
+++ qemu-linaro-1.1.50-2012.07-modified/hw/arm/Makefile.objs    2012-09-12 
11:28:39.121053287 +0200
@@ -1,4 +1,4 @@
-obj-y = integratorcp.o versatilepb.o arm_pic.o arm_timer.o
+obj-y = integratorcp.o versatilepb.o arm_pic.o arm_timer.o arm_archtimer.o
 obj-y += arm_boot.o pl011.o pl031.o pl050.o pl080.o pl110.o pl181.o pl190.o
 obj-y += versatile_pci.o
 obj-y += versatile_i2c.o
diff -Nupr qemu-linaro-1.1.50-2012.07/hw/arm_archtimer.c 
qemu-linaro-1.1.50-2012.07-modified/hw/arm_archtimer.c
--- qemu-linaro-1.1.50-2012.07/hw/arm_archtimer.c       1970-01-01 
01:00:00.000000000 +0100
+++ qemu-linaro-1.1.50-2012.07-modified/hw/arm_archtimer.c      2012-09-12 
13:21:44.676267111 +0200
@@ -0,0 +1,232 @@
+/*
+ * "Architectured" timer for A15
+ *
+ * Copyright (c) 2012 Enea Software AB
+ * Written by Daniel Forsgren
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "sysbus.h"
+#include "qemu-timer.h"
+
+/* Primitive (and imcomplete) support for A15 "architectured" timers 
+
+   - Only PL1 timer supported.
+
+   - Only minimal subset of fuctionality requred by Linux.
+   
+   - Only tested with single-core.
+
+*/
+
+/* control register bit assignment */
+#define CTL_ENABLE  0x01
+#define CTL_MASK    0x02
+#define CTL_INT     0x04
+
+/* state of per-core timers */
+typedef struct {
+    ARMCPU *cpu; /* who are we serving */
+    QEMUTimer *pl1_timer;
+    QEMUTimer *pl2_timer;
+    qemu_irq pl1_irq;
+    qemu_irq pl2_irq;
+    uint32_t cntkctl; /* timer pl1 control register */
+    uint32_t cntp_ctl; /* pl1 physical timer control register */
+    uint64_t cntvoff; /* virtual offset register */
+} archtimers;
+
+#define MAX_CPUS 4
+
+typedef struct {
+    SysBusDevice busdev;
+    archtimers archtimers[MAX_CPUS];
+} arm_archtimer_state;
+
+
+static int a15_cntfrq_read(CPUARMState *env, const ARMCPRegInfo *ri,  uint64_t 
*value)
+{  
+    /* Let's assume that we're running at 1GHz for now. It's not
+       correct, but it simplifies translation between cycles <-> ns */
+    *value = 1000000000UL; 
+    return 0;
+}
+
+static int a15_cntkctl_read(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t 
*value)
+{
+    archtimers *at = (archtimers *)(ri->opaque);
+    *value = at->cntkctl;
+    return 0;
+}
+
+static int a15_cntpct_read(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t 
*value)
+{
+    /* Let's assume that the physical count register is driven by
+       vm_clock for now. As we have specified that that we're running
+       at 1GHz, no translation from ns should be needed. */
+    *value = qemu_get_clock_ns(vm_clock);
+    return 0;
+}
+
+static int a15_cntvct_read(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t 
*value)
+{
+    archtimers *at = (archtimers *)(ri->opaque);
+
+    /* Virtual count should subtract the virtual offfset from physical
+       count? */
+    *value = qemu_get_clock_ns(vm_clock) - at->cntvoff;
+    return 0;
+}
+
+static int a15_cntp_tval_read(CPUARMState *env, const ARMCPRegInfo *ri, 
uint64_t *value)
+{
+    archtimers *at = (archtimers *)(ri->opaque);    
+    *value = qemu_timer_expire_time_ns(at->pl1_timer);
+    return 0;
+}
+
+static int a15_cntp_tval_write(CPUARMState *env, const ARMCPRegInfo *ri, 
uint64_t value)
+{
+    archtimers *at = (archtimers *)(ri->opaque);
+
+    /* I assume that setting a new value means that we should clear
+       any previous? */
+    qemu_set_irq(at->pl1_irq, 0);
+    at->cntp_ctl &= ~CTL_INT;
+
+    qemu_mod_timer_ns(at->pl1_timer, qemu_get_clock_ns(vm_clock) + value);
+
+    return 0;
+}
+
+static int a15_cntp_ctl_read(CPUARMState *env, const ARMCPRegInfo *ri, 
uint64_t *value)
+{
+    archtimers *at = (archtimers *)(ri->opaque);
+
+    *value = at->cntp_ctl;
+
+    return 0;
+}
+
+static int a15_cntp_ctl_write(CPUARMState *env, const ARMCPRegInfo *ri,
+                              uint64_t value)
+{
+    archtimers *at = (archtimers *)(ri->opaque);
+
+    /* Copy "enable" and "mask" bits from the new value. Preserve the rest. */
+    at->cntp_ctl = (at->cntp_ctl & ~(CTL_ENABLE | CTL_MASK)) | (value & 
(CTL_ENABLE | CTL_MASK));
+
+    /* If no interrupt is asserted, or interrupt is masked, then lower the 
irq. */
+    if (!(at->cntp_ctl & CTL_INT) || (at->cntp_ctl & CTL_MASK))
+        qemu_set_irq(at->pl1_irq, 0);
+        
+    /* If interrupt is asserted and not masked, then raise the irq. */
+    if ((at->cntp_ctl & CTL_INT) && !(at->cntp_ctl & CTL_MASK))
+        qemu_set_irq(at->pl1_irq, 1);
+
+    return 0;
+}
+
+static const ARMCPRegInfo archtimer_cp_reginfo[] = {
+   
+    { .name = "CNTFRQ", .cp = 15, .crn = 14, .crm = 0, .opc1 = 0, .opc2 = 0,
+      .access = PL1_R, .resetvalue = 0, .readfn = a15_cntfrq_read, },
+
+    { .name = "CNTKCTL", .cp = 15, .crn = 14, .crm = 1, .opc1 = 0, .opc2 = 0,
+      .access = PL1_R, .resetvalue = 0, .readfn = a15_cntkctl_read, },
+
+    { .name = "CNTP_TVAL", .cp = 15, .crn = 14, .crm = 2, .opc1 = 0, .opc2 = 0,
+      .access = PL1_RW, .resetvalue = 0, .readfn = a15_cntp_tval_read,
+      .writefn = a15_cntp_tval_write, },
+    
+    { .name = "CNTP_CTL", .cp = 15, .crn = 14, .crm = 2, .opc1 = 0, .opc2 = 1,
+      .access = PL1_RW, .resetvalue = 0, .readfn = a15_cntp_ctl_read,
+      .writefn = a15_cntp_ctl_write, },
+
+    { .name = "CNTPCT", .type = ARM_CP_64BIT, .cp = 15, .crn = 0, .crm = 14, 
.opc1 = 0, .opc2 = 0,
+      .access = PL1_R, .resetvalue = 0, .readfn = a15_cntpct_read, },
+
+    { .name = "CNTVCT", .type = ARM_CP_64BIT, .cp = 15, .crn = 0, .crm = 14, 
.opc1 = 1, .opc2 = 0,
+      .access = PL1_R, .resetvalue = 0, .readfn = a15_cntvct_read, },
+
+    REGINFO_SENTINEL
+};
+
+static void pl1_timer_cb(void *opaque)
+{
+    archtimers *at = (archtimers *)opaque;
+
+    /* Set the interrupt bit in control register */
+    at->cntp_ctl |= CTL_INT;
+
+    /* If not masked, then raise the irq */
+    if (!(at->cntp_ctl & CTL_MASK))
+        qemu_set_irq(at->pl1_irq, 1);
+}
+
+static int arm_archtimer_init(SysBusDevice *dev)
+{
+    arm_archtimer_state *s = FROM_SYSBUS(arm_archtimer_state, dev);
+    CPUArchState *cpu;
+    int i;
+
+    for (cpu = first_cpu, i = 0; cpu; cpu = cpu->next_cpu, i++) {
+        archtimers *at = &s->archtimers[i];
+        at->pl1_timer = qemu_new_timer_ns(vm_clock, &pl1_timer_cb, at);
+        sysbus_init_irq(dev, &(at->pl1_irq));
+        sysbus_init_irq(dev, &(at->pl2_irq));
+        s->archtimers[i].cpu = arm_env_get_cpu(cpu);
+        define_arm_cp_regs_with_opaque(s->archtimers[i].cpu, 
archtimer_cp_reginfo, (void *)at);
+    }
+
+    return 0;
+}
+
+static void arm_archtimer_reset(DeviceState *dev)
+{
+    arm_archtimer_state *s =
+        FROM_SYSBUS(arm_archtimer_state, sysbus_from_qdev(dev));
+    int i;
+
+    for (i = 0; i < MAX_CPUS; i++) {
+        if (s->archtimers[i].pl1_timer)
+            qemu_del_timer(s->archtimers[i].pl1_timer);
+        if (s->archtimers[i].pl2_timer)
+            qemu_del_timer(s->archtimers[i].pl2_timer);
+    }
+}
+
+static void arm_archtimer_class_init(ObjectClass *class, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(class);
+    SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(class);
+
+    sbc->init = arm_archtimer_init;
+    dc->reset = arm_archtimer_reset;
+}
+
+static TypeInfo arm_archtimer_info = {
+    .name          = "arm_archtimer",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(arm_archtimer_state),
+    .class_init    = arm_archtimer_class_init,
+};
+
+static void arm_archtimer_register_types(void)
+{
+    type_register_static(&arm_archtimer_info);
+}
+
+type_init(arm_archtimer_register_types)
diff -Nupr qemu-linaro-1.1.50-2012.07/target-arm/helper.c 
qemu-linaro-1.1.50-2012.07-modified/target-arm/helper.c
--- qemu-linaro-1.1.50-2012.07/target-arm/helper.c      2012-07-05 
16:48:28.000000000 +0200
+++ qemu-linaro-1.1.50-2012.07-modified/target-arm/helper.c     2012-09-12 
13:15:45.544424842 +0200
@@ -1012,9 +1012,11 @@ void register_cp_regs_for_features(ARMCP
     if (arm_feature(env, ARM_FEATURE_THUMB2EE)) {
         define_arm_cp_regs(cpu, t2ee_cp_reginfo);
     }
+    /*
     if (arm_feature(env, ARM_FEATURE_GENERIC_TIMER)) {
         define_arm_cp_regs(cpu, generic_timer_cp_reginfo);
     }
+    */
     if (arm_feature(env, ARM_FEATURE_VAPA)) {
         define_arm_cp_regs(cpu, vapa_cp_reginfo);
     }




reply via email to

[Prev in Thread] Current Thread [Next in Thread]