configure: change "found" to "find"
[qemu] / hw / armv7m_nvic.c
index d304082..3360808 100644 (file)
  * NVIC.  Much of that is also implemented here.
  */
 
-#include "vl.h"
-#include "arm_pic.h"
+#include "sysbus.h"
+#include "qemu-timer.h"
+#include "arm-misc.h"
 
-#define GIC_NIRQ 64
+/* 32 internal lines (16 used for system exceptions) plus 64 external
+   interrupt lines.  */
+#define GIC_NIRQ 96
 #define NCPU 1
 #define NVIC 1
 
@@ -30,13 +33,13 @@ static void nvic_writel(void *opaque, uint32_t offset, uint32_t value);
 #include "arm_gic.c"
 
 typedef struct {
+    gic_state gic;
     struct {
         uint32_t control;
         uint32_t reload;
         int64_t tick;
         QEMUTimer *timer;
     } systick;
-    gic_state *gic;
 } nvic_state;
 
 /* qemu timers run at 1GHz.   We want something closer to 1MHz.  */
@@ -47,14 +50,13 @@ typedef struct {
 #define SYSTICK_CLKSOURCE (1 << 2)
 #define SYSTICK_COUNTFLAG (1 << 16)
 
-/* Conversion factor from qemu timer to SysTick frequencies.
-   QEMU uses a base of 1GHz, so these give 20MHz and 1MHz for core and
-   reference frequencies.  */
+int system_clock_scale;
 
+/* Conversion factor from qemu timer to SysTick frequencies.  */
 static inline int64_t systick_scale(nvic_state *s)
 {
     if (s->systick.control & SYSTICK_CLKSOURCE)
-        return 50;
+        return system_clock_scale;
     else
         return 1000;
 }
@@ -89,7 +91,7 @@ void armv7m_nvic_set_pending(void *opaque, int irq)
     nvic_state *s = (nvic_state *)opaque;
     if (irq >= 16)
         irq += 16;
-    gic_set_pending_private(s->gic, 0, irq);
+    gic_set_pending_private(&s->gic, 0, irq);
 }
 
 /* Make pending IRQ active.  */
@@ -98,9 +100,9 @@ int armv7m_nvic_acknowledge_irq(void *opaque)
     nvic_state *s = (nvic_state *)opaque;
     uint32_t irq;
 
-    irq = gic_acknowledge_irq(s->gic, 0);
+    irq = gic_acknowledge_irq(&s->gic, 0);
     if (irq == 1023)
-        cpu_abort(cpu_single_env, "Interrupt but no vector\n");
+        hw_error("Interrupt but no vector\n");
     if (irq >= 32)
         irq -= 16;
     return irq;
@@ -111,7 +113,7 @@ void armv7m_nvic_complete_irq(void *opaque, int irq)
     nvic_state *s = (nvic_state *)opaque;
     if (irq >= 16)
         irq += 16;
-    gic_complete_irq(s->gic, 0, irq);
+    gic_complete_irq(&s->gic, 0, irq);
 }
 
 static uint32_t nvic_readl(void *opaque, uint32_t offset)
@@ -151,35 +153,35 @@ static uint32_t nvic_readl(void *opaque, uint32_t offset)
         return cpu_single_env->cp15.c0_cpuid;
     case 0xd04: /* Interrypt Control State.  */
         /* VECTACTIVE */
-        val = s->gic->running_irq[0];
+        val = s->gic.running_irq[0];
         if (val == 1023) {
             val = 0;
         } else if (val >= 32) {
             val -= 16;
         }
         /* RETTOBASE */
-        if (s->gic->running_irq[0] == 1023
-                || s->gic->last_active[s->gic->running_irq[0]][0] == 1023) {
+        if (s->gic.running_irq[0] == 1023
+                || s->gic.last_active[s->gic.running_irq[0]][0] == 1023) {
             val |= (1 << 11);
         }
         /* VECTPENDING */
-        if (s->gic->current_pending[0] != 1023)
-            val |= (s->gic->current_pending[0] << 12);
+        if (s->gic.current_pending[0] != 1023)
+            val |= (s->gic.current_pending[0] << 12);
         /* ISRPENDING */
         for (irq = 32; irq < GIC_NIRQ; irq++) {
-            if (s->gic->irq_state[irq].pending) {
+            if (s->gic.irq_state[irq].pending) {
                 val |= (1 << 22);
                 break;
             }
         }
         /* PENDSTSET */
-        if (s->gic->irq_state[ARMV7M_EXCP_SYSTICK].pending)
+        if (s->gic.irq_state[ARMV7M_EXCP_SYSTICK].pending)
             val |= (1 << 26);
         /* PENDSVSET */
-        if (s->gic->irq_state[ARMV7M_EXCP_PENDSV].pending)
+        if (s->gic.irq_state[ARMV7M_EXCP_PENDSV].pending)
             val |= (1 << 28);
         /* NMIPENDSET */
-        if (s->gic->irq_state[ARMV7M_EXCP_NMI].pending)
+        if (s->gic.irq_state[ARMV7M_EXCP_NMI].pending)
             val |= (1 << 31);
         return val;
     case 0xd08: /* Vector Table Offset.  */
@@ -195,32 +197,31 @@ static uint32_t nvic_readl(void *opaque, uint32_t offset)
     case 0xd18: case 0xd1c: case 0xd20: /* System Handler Priority.  */
         irq = offset - 0xd14;
         val = 0;
-        val = s->gic->priority1[irq++][0];
-        val = s->gic->priority1[irq++][0] << 8;
-        val = s->gic->priority1[irq++][0] << 16;
-        val = s->gic->priority1[irq][0] << 24;
+        val = s->gic.priority1[irq++][0];
+        val = s->gic.priority1[irq++][0] << 8;
+        val = s->gic.priority1[irq++][0] << 16;
+        val = s->gic.priority1[irq][0] << 24;
         return val;
     case 0xd24: /* System Handler Status.  */
         val = 0;
-        if (s->gic->irq_state[ARMV7M_EXCP_MEM].active) val |= (1 << 0);
-        if (s->gic->irq_state[ARMV7M_EXCP_BUS].active) val |= (1 << 1);
-        if (s->gic->irq_state[ARMV7M_EXCP_USAGE].active) val |= (1 << 3);
-        if (s->gic->irq_state[ARMV7M_EXCP_SVC].active) val |= (1 << 7);
-        if (s->gic->irq_state[ARMV7M_EXCP_DEBUG].active) val |= (1 << 8);
-        if (s->gic->irq_state[ARMV7M_EXCP_PENDSV].active) val |= (1 << 10);
-        if (s->gic->irq_state[ARMV7M_EXCP_SYSTICK].active) val |= (1 << 11);
-        if (s->gic->irq_state[ARMV7M_EXCP_USAGE].pending) val |= (1 << 12);
-        if (s->gic->irq_state[ARMV7M_EXCP_MEM].pending) val |= (1 << 13);
-        if (s->gic->irq_state[ARMV7M_EXCP_BUS].pending) val |= (1 << 14);
-        if (s->gic->irq_state[ARMV7M_EXCP_SVC].pending) val |= (1 << 15);
-        if (s->gic->irq_state[ARMV7M_EXCP_MEM].enabled) val |= (1 << 16);
-        if (s->gic->irq_state[ARMV7M_EXCP_BUS].enabled) val |= (1 << 17);
-        if (s->gic->irq_state[ARMV7M_EXCP_USAGE].enabled) val |= (1 << 18);
+        if (s->gic.irq_state[ARMV7M_EXCP_MEM].active) val |= (1 << 0);
+        if (s->gic.irq_state[ARMV7M_EXCP_BUS].active) val |= (1 << 1);
+        if (s->gic.irq_state[ARMV7M_EXCP_USAGE].active) val |= (1 << 3);
+        if (s->gic.irq_state[ARMV7M_EXCP_SVC].active) val |= (1 << 7);
+        if (s->gic.irq_state[ARMV7M_EXCP_DEBUG].active) val |= (1 << 8);
+        if (s->gic.irq_state[ARMV7M_EXCP_PENDSV].active) val |= (1 << 10);
+        if (s->gic.irq_state[ARMV7M_EXCP_SYSTICK].active) val |= (1 << 11);
+        if (s->gic.irq_state[ARMV7M_EXCP_USAGE].pending) val |= (1 << 12);
+        if (s->gic.irq_state[ARMV7M_EXCP_MEM].pending) val |= (1 << 13);
+        if (s->gic.irq_state[ARMV7M_EXCP_BUS].pending) val |= (1 << 14);
+        if (s->gic.irq_state[ARMV7M_EXCP_SVC].pending) val |= (1 << 15);
+        if (s->gic.irq_state[ARMV7M_EXCP_MEM].enabled) val |= (1 << 16);
+        if (s->gic.irq_state[ARMV7M_EXCP_BUS].enabled) val |= (1 << 17);
+        if (s->gic.irq_state[ARMV7M_EXCP_USAGE].enabled) val |= (1 << 18);
         return val;
     case 0xd28: /* Configurable Fault Status.  */
         /* TODO: Implement Fault Status.  */
-        cpu_abort(cpu_single_env,
-                  "Not implemented: Configurable Fault Status.");
+        hw_error("Not implemented: Configurable Fault Status.");
         return 0;
     case 0xd2c: /* Hard Fault Status.  */
     case 0xd30: /* Debug Fault Status.  */
@@ -258,7 +259,7 @@ static uint32_t nvic_readl(void *opaque, uint32_t offset)
     /* TODO: Implement debug registers.  */
     default:
     bad_reg:
-        cpu_abort(cpu_single_env, "NVIC: Bad read offset 0x%x\n", offset);
+        hw_error("NVIC: Bad read offset 0x%x\n", offset);
     }
 }
 
@@ -306,14 +307,14 @@ static void nvic_writel(void *opaque, uint32_t offset, uint32_t value)
         if (value & (1 << 28)) {
             armv7m_nvic_set_pending(s, ARMV7M_EXCP_PENDSV);
         } else if (value & (1 << 27)) {
-            s->gic->irq_state[ARMV7M_EXCP_PENDSV].pending = 0;
-            gic_update(s->gic);
+            s->gic.irq_state[ARMV7M_EXCP_PENDSV].pending = 0;
+            gic_update(&s->gic);
         }
         if (value & (1 << 26)) {
             armv7m_nvic_set_pending(s, ARMV7M_EXCP_SYSTICK);
         } else if (value & (1 << 25)) {
-            s->gic->irq_state[ARMV7M_EXCP_SYSTICK].pending = 0;
-            gic_update(s->gic);
+            s->gic.irq_state[ARMV7M_EXCP_SYSTICK].pending = 0;
+            gic_update(&s->gic);
         }
         break;
     case 0xd08: /* Vector Table Offset.  */
@@ -322,10 +323,10 @@ static void nvic_writel(void *opaque, uint32_t offset, uint32_t value)
     case 0xd0c: /* Application Interrupt/Reset Control.  */
         if ((value >> 16) == 0x05fa) {
             if (value & 2) {
-                cpu_abort(cpu_single_env, "VECTCLRACTIVE not implemented");
+                hw_error("VECTCLRACTIVE not implemented");
             }
             if (value & 5) {
-                cpu_abort(cpu_single_env, "System reset");
+                hw_error("System reset");
             }
         }
         break;
@@ -337,19 +338,19 @@ static void nvic_writel(void *opaque, uint32_t offset, uint32_t value)
         {
             int irq;
             irq = offset - 0xd14;
-            s->gic->priority1[irq++][0] = value & 0xff;
-            s->gic->priority1[irq++][0] = (value >> 8) & 0xff;
-            s->gic->priority1[irq++][0] = (value >> 16) & 0xff;
-            s->gic->priority1[irq][0] = (value >> 24) & 0xff;
-            gic_update(s->gic);
+            s->gic.priority1[irq++][0] = value & 0xff;
+            s->gic.priority1[irq++][0] = (value >> 8) & 0xff;
+            s->gic.priority1[irq++][0] = (value >> 16) & 0xff;
+            s->gic.priority1[irq][0] = (value >> 24) & 0xff;
+            gic_update(&s->gic);
         }
         break;
     case 0xd24: /* System Handler Control.  */
         /* TODO: Real hardware allows you to set/clear the active bits
            under some circumstances.  We don't implement this.  */
-        s->gic->irq_state[ARMV7M_EXCP_MEM].enabled = (value & (1 << 16)) != 0;
-        s->gic->irq_state[ARMV7M_EXCP_BUS].enabled = (value & (1 << 17)) != 0;
-        s->gic->irq_state[ARMV7M_EXCP_USAGE].enabled = (value & (1 << 18)) != 0;
+        s->gic.irq_state[ARMV7M_EXCP_MEM].enabled = (value & (1 << 16)) != 0;
+        s->gic.irq_state[ARMV7M_EXCP_BUS].enabled = (value & (1 << 17)) != 0;
+        s->gic.irq_state[ARMV7M_EXCP_USAGE].enabled = (value & (1 << 18)) != 0;
         break;
     case 0xd28: /* Configurable Fault Status.  */
     case 0xd2c: /* Hard Fault Status.  */
@@ -360,22 +361,49 @@ static void nvic_writel(void *opaque, uint32_t offset, uint32_t value)
         goto bad_reg;
     default:
     bad_reg:
-        cpu_abort(cpu_single_env, "NVIC: Bad write offset 0x%x\n", offset);
+        hw_error("NVIC: Bad write offset 0x%x\n", offset);
     }
 }
 
-qemu_irq *armv7m_nvic_init(CPUState *env)
+static void nvic_save(QEMUFile *f, void *opaque)
 {
-    nvic_state *s;
-    qemu_irq *parent;
+    nvic_state *s = (nvic_state *)opaque;
+
+    qemu_put_be32(f, s->systick.control);
+    qemu_put_be32(f, s->systick.reload);
+    qemu_put_be64(f, s->systick.tick);
+    qemu_put_timer(f, s->systick.timer);
+}
+
+static int nvic_load(QEMUFile *f, void *opaque, int version_id)
+{
+    nvic_state *s = (nvic_state *)opaque;
+
+    if (version_id != 1)
+        return -EINVAL;
+
+    s->systick.control = qemu_get_be32(f);
+    s->systick.reload = qemu_get_be32(f);
+    s->systick.tick = qemu_get_be64(f);
+    qemu_get_timer(f, s->systick.timer);
+
+    return 0;
+}
 
-    parent = arm_pic_init_cpu(env);
-    s = (nvic_state *)qemu_mallocz(sizeof(nvic_state));
-    s->gic = gic_init(0xe000e000, &parent[ARM_PIC_CPU_IRQ]);
-    s->gic->nvic = s;
+static int armv7m_nvic_init(SysBusDevice *dev)
+{
+    nvic_state *s= FROM_SYSBUSGIC(nvic_state, dev);
+
+    gic_init(&s->gic);
+    cpu_register_physical_memory(0xe000e000, 0x1000, s->gic.iomemtype);
     s->systick.timer = qemu_new_timer(vm_clock, systick_timer_tick, s);
-    if (env->v7m.nvic)
-        cpu_abort(env, "CPU can only have one NVIC\n");
-    env->v7m.nvic = s;
-    return s->gic->in;
+    register_savevm("armv7m_nvic", -1, 1, nvic_save, nvic_load, s);
+    return 0;
 }
+
+static void armv7m_nvic_register_devices(void)
+{
+    sysbus_register_dev("armv7m_nvic", sizeof(nvic_state), armv7m_nvic_init);
+}
+
+device_init(armv7m_nvic_register_devices)