]> git.proxmox.com Git - qemu.git/blobdiff - hw/arm_sysctl.c
Merge remote-tracking branch 'spice/spice.v39' into staging
[qemu.git] / hw / arm_sysctl.c
index ab22e840d70b7080b024e4c907699d7f0a0b2b83..9225b588b8d737ca7e314cf266f8a94ff8203140 100644 (file)
@@ -8,12 +8,15 @@
  */
 
 #include "hw.h"
+#include "qemu-timer.h"
+#include "sysbus.h"
 #include "primecell.h"
 #include "sysemu.h"
 
 #define LOCK_VALUE 0xa05f
 
 typedef struct {
+    SysBusDevice busdev;
     uint32_t sys_id;
     uint32_t leds;
     uint16_t lockval;
@@ -22,8 +25,61 @@ typedef struct {
     uint32_t flags;
     uint32_t nvflags;
     uint32_t resetlevel;
+    uint32_t proc_id;
+    uint32_t sys_mci;
+    uint32_t sys_cfgdata;
+    uint32_t sys_cfgctrl;
+    uint32_t sys_cfgstat;
 } arm_sysctl_state;
 
+static const VMStateDescription vmstate_arm_sysctl = {
+    .name = "realview_sysctl",
+    .version_id = 2,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(leds, arm_sysctl_state),
+        VMSTATE_UINT16(lockval, arm_sysctl_state),
+        VMSTATE_UINT32(cfgdata1, arm_sysctl_state),
+        VMSTATE_UINT32(cfgdata2, arm_sysctl_state),
+        VMSTATE_UINT32(flags, arm_sysctl_state),
+        VMSTATE_UINT32(nvflags, arm_sysctl_state),
+        VMSTATE_UINT32(resetlevel, arm_sysctl_state),
+        VMSTATE_UINT32_V(sys_mci, arm_sysctl_state, 2),
+        VMSTATE_UINT32_V(sys_cfgdata, arm_sysctl_state, 2),
+        VMSTATE_UINT32_V(sys_cfgctrl, arm_sysctl_state, 2),
+        VMSTATE_UINT32_V(sys_cfgstat, arm_sysctl_state, 2),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+/* The PB926 actually uses a different format for
+ * its SYS_ID register. Fortunately the bits which are
+ * board type on later boards are distinct.
+ */
+#define BOARD_ID_PB926 0x100
+#define BOARD_ID_EB 0x140
+#define BOARD_ID_PBA8 0x178
+#define BOARD_ID_PBX 0x182
+#define BOARD_ID_VEXPRESS 0x190
+
+static int board_id(arm_sysctl_state *s)
+{
+    /* Extract the board ID field from the SYS_ID register value */
+    return (s->sys_id >> 16) & 0xfff;
+}
+
+static void arm_sysctl_reset(DeviceState *d)
+{
+    arm_sysctl_state *s = FROM_SYSBUS(arm_sysctl_state, sysbus_from_qdev(d));
+
+    s->leds = 0;
+    s->lockval = 0;
+    s->cfgdata1 = 0;
+    s->cfgdata2 = 0;
+    s->flags = 0;
+    s->resetlevel = 0;
+}
+
 static uint32_t arm_sysctl_read(void *opaque, target_phys_addr_t offset)
 {
     arm_sysctl_state *s = (arm_sysctl_state *)opaque;
@@ -56,11 +112,15 @@ static uint32_t arm_sysctl_read(void *opaque, target_phys_addr_t offset)
     case 0x38: /* NVFLAGS */
         return s->nvflags;
     case 0x40: /* RESETCTL */
+        if (board_id(s) == BOARD_ID_VEXPRESS) {
+            /* reserved: RAZ/WI */
+            return 0;
+        }
         return s->resetlevel;
     case 0x44: /* PCICTL */
         return 1;
     case 0x48: /* MCI */
-        return 0;
+        return s->sys_mci;
     case 0x4c: /* FLASH */
         return 0;
     case 0x50: /* CLCD */
@@ -70,13 +130,11 @@ static uint32_t arm_sysctl_read(void *opaque, target_phys_addr_t offset)
     case 0x58: /* BOOTCS */
         return 0;
     case 0x5c: /* 24MHz */
-        /* ??? not implemented.  */
-        return 0;
+        return muldiv64(qemu_get_clock_ns(vm_clock), 24000000, get_ticks_per_sec());
     case 0x60: /* MISC */
         return 0;
     case 0x84: /* PROCID0 */
-        /* ??? Don't know what the proper value for the core tile ID is.  */
-        return 0x02000000;
+        return s->proc_id;
     case 0x88: /* PROCID1 */
         return 0xff000000;
     case 0x64: /* DMAPSR0 */
@@ -96,7 +154,23 @@ static uint32_t arm_sysctl_read(void *opaque, target_phys_addr_t offset)
     case 0xcc: /* SYS_TEST_OSC3 */
     case 0xd0: /* SYS_TEST_OSC4 */
         return 0;
+    case 0xa0: /* SYS_CFGDATA */
+        if (board_id(s) != BOARD_ID_VEXPRESS) {
+            goto bad_reg;
+        }
+        return s->sys_cfgdata;
+    case 0xa4: /* SYS_CFGCTRL */
+        if (board_id(s) != BOARD_ID_VEXPRESS) {
+            goto bad_reg;
+        }
+        return s->sys_cfgctrl;
+    case 0xa8: /* SYS_CFGSTAT */
+        if (board_id(s) != BOARD_ID_VEXPRESS) {
+            goto bad_reg;
+        }
+        return s->sys_cfgstat;
     default:
+    bad_reg:
         printf ("arm_sysctl_read: Bad register offset 0x%x\n", (int)offset);
         return 0;
     }
@@ -144,6 +218,10 @@ static void arm_sysctl_write(void *opaque, target_phys_addr_t offset,
         s->nvflags &= ~val;
         break;
     case 0x40: /* RESETCTL */
+        if (board_id(s) == BOARD_ID_VEXPRESS) {
+            /* reserved: RAZ/WI */
+            break;
+        }
         if (s->lockval == LOCK_VALUE) {
             s->resetlevel = val;
             if (val & 0x100)
@@ -170,39 +248,124 @@ static void arm_sysctl_write(void *opaque, target_phys_addr_t offset,
     case 0x98: /* OSCRESET3 */
     case 0x9c: /* OSCRESET4 */
         break;
+    case 0xa0: /* SYS_CFGDATA */
+        if (board_id(s) != BOARD_ID_VEXPRESS) {
+            goto bad_reg;
+        }
+        s->sys_cfgdata = val;
+        return;
+    case 0xa4: /* SYS_CFGCTRL */
+        if (board_id(s) != BOARD_ID_VEXPRESS) {
+            goto bad_reg;
+        }
+        s->sys_cfgctrl = val & ~(3 << 18);
+        s->sys_cfgstat = 1;            /* complete */
+        switch (s->sys_cfgctrl) {
+        case 0xc0800000:            /* SYS_CFG_SHUTDOWN to motherboard */
+            qemu_system_shutdown_request();
+            break;
+        case 0xc0900000:            /* SYS_CFG_REBOOT to motherboard */
+            qemu_system_reset_request();
+            break;
+        default:
+            s->sys_cfgstat |= 2;        /* error */
+        }
+        return;
+    case 0xa8: /* SYS_CFGSTAT */
+        if (board_id(s) != BOARD_ID_VEXPRESS) {
+            goto bad_reg;
+        }
+        s->sys_cfgstat = val & 3;
+        return;
     default:
+    bad_reg:
         printf ("arm_sysctl_write: Bad register offset 0x%x\n", (int)offset);
         return;
     }
 }
 
-static CPUReadMemoryFunc *arm_sysctl_readfn[] = {
+static CPUReadMemoryFunc * const arm_sysctl_readfn[] = {
    arm_sysctl_read,
    arm_sysctl_read,
    arm_sysctl_read
 };
 
-static CPUWriteMemoryFunc *arm_sysctl_writefn[] = {
+static CPUWriteMemoryFunc * const arm_sysctl_writefn[] = {
    arm_sysctl_write,
    arm_sysctl_write,
    arm_sysctl_write
 };
 
-void arm_sysctl_init(uint32_t base, uint32_t sys_id)
+static void arm_sysctl_gpio_set(void *opaque, int line, int level)
+{
+    arm_sysctl_state *s = (arm_sysctl_state *)opaque;
+    switch (line) {
+    case ARM_SYSCTL_GPIO_MMC_WPROT:
+    {
+        /* For PB926 and EB write-protect is bit 2 of SYS_MCI;
+         * for all later boards it is bit 1.
+         */
+        int bit = 2;
+        if ((board_id(s) == BOARD_ID_PB926) || (board_id(s) == BOARD_ID_EB)) {
+            bit = 4;
+        }
+        s->sys_mci &= ~bit;
+        if (level) {
+            s->sys_mci |= bit;
+        }
+        break;
+    }
+    case ARM_SYSCTL_GPIO_MMC_CARDIN:
+        s->sys_mci &= ~1;
+        if (level) {
+            s->sys_mci |= 1;
+        }
+        break;
+    }
+}
+
+static int arm_sysctl_init1(SysBusDevice *dev)
 {
-    arm_sysctl_state *s;
+    arm_sysctl_state *s = FROM_SYSBUS(arm_sysctl_state, dev);
     int iomemtype;
 
-    s = (arm_sysctl_state *)qemu_mallocz(sizeof(arm_sysctl_state));
-    if (!s)
-        return;
-    s->sys_id = sys_id;
-    /* The MPcore bootloader uses these flags to start secondary CPUs.
-       We don't use a bootloader, so do this here.  */
-    s->flags = 3;
-    iomemtype = cpu_register_io_memory(0, arm_sysctl_readfn,
-                                       arm_sysctl_writefn, s);
-    cpu_register_physical_memory(base, 0x00001000, iomemtype);
+    iomemtype = cpu_register_io_memory(arm_sysctl_readfn,
+                                       arm_sysctl_writefn, s,
+                                       DEVICE_NATIVE_ENDIAN);
+    sysbus_init_mmio(dev, 0x1000, iomemtype);
+    qdev_init_gpio_in(&s->busdev.qdev, arm_sysctl_gpio_set, 2);
     /* ??? Save/restore.  */
+    return 0;
+}
+
+/* Legacy helper function.  */
+void arm_sysctl_init(uint32_t base, uint32_t sys_id, uint32_t proc_id)
+{
+    DeviceState *dev;
+
+    dev = qdev_create(NULL, "realview_sysctl");
+    qdev_prop_set_uint32(dev, "sys_id", sys_id);
+    qdev_init_nofail(dev);
+    qdev_prop_set_uint32(dev, "proc_id", proc_id);
+    sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
+}
+
+static SysBusDeviceInfo arm_sysctl_info = {
+    .init = arm_sysctl_init1,
+    .qdev.name  = "realview_sysctl",
+    .qdev.size  = sizeof(arm_sysctl_state),
+    .qdev.vmsd = &vmstate_arm_sysctl,
+    .qdev.reset = arm_sysctl_reset,
+    .qdev.props = (Property[]) {
+        DEFINE_PROP_UINT32("sys_id", arm_sysctl_state, sys_id, 0),
+        DEFINE_PROP_UINT32("proc_id", arm_sysctl_state, proc_id, 0),
+        DEFINE_PROP_END_OF_LIST(),
+    }
+};
+
+static void arm_sysctl_register_devices(void)
+{
+    sysbus_register_withprop(&arm_sysctl_info);
 }
 
+device_init(arm_sysctl_register_devices)