summaryrefslogtreecommitdiffstats
path: root/qemu/hw/misc
diff options
context:
space:
mode:
authorJosé Pekkarinen <jose.pekkarinen@nokia.com>2016-05-18 13:18:31 +0300
committerJosé Pekkarinen <jose.pekkarinen@nokia.com>2016-05-18 13:42:15 +0300
commit437fd90c0250dee670290f9b714253671a990160 (patch)
treeb871786c360704244a07411c69fb58da9ead4a06 /qemu/hw/misc
parent5bbd6fe9b8bab2a93e548c5a53b032d1939eec05 (diff)
These changes are the raw update to qemu-2.6.
Collission happened in the following patches: migration: do cleanup operation after completion(738df5b9) Bug fix.(1750c932f86) kvmclock: add a new function to update env->tsc.(b52baab2) The code provided by the patches was already in the upstreamed version. Change-Id: I3cc11841a6a76ae20887b2e245710199e1ea7f9a Signed-off-by: José Pekkarinen <jose.pekkarinen@nokia.com>
Diffstat (limited to 'qemu/hw/misc')
-rw-r--r--qemu/hw/misc/Makefile.objs10
-rw-r--r--qemu/hw/misc/a9scu.c1
-rw-r--r--qemu/hw/misc/applesmc.c1
-rw-r--r--qemu/hw/misc/arm11scu.c1
-rw-r--r--qemu/hw/misc/arm_integrator_debug.c3
-rw-r--r--qemu/hw/misc/arm_l2x0.c1
-rw-r--r--qemu/hw/misc/arm_sysctl.c4
-rw-r--r--qemu/hw/misc/bcm2835_mbox.c335
-rw-r--r--qemu/hw/misc/bcm2835_property.c424
-rw-r--r--qemu/hw/misc/cbus.c3
-rw-r--r--qemu/hw/misc/debugexit.c1
-rw-r--r--qemu/hw/misc/eccmemctl.c1
-rw-r--r--qemu/hw/misc/edu.c13
-rw-r--r--qemu/hw/misc/exynos4210_pmu.c1
-rw-r--r--qemu/hw/misc/hyperv_testdev.c168
-rw-r--r--qemu/hw/misc/imx25_ccm.c317
-rw-r--r--qemu/hw/misc/imx31_ccm.c344
-rw-r--r--qemu/hw/misc/imx6_ccm.c774
-rw-r--r--qemu/hw/misc/imx_ccm.c309
-rw-r--r--qemu/hw/misc/ivshmem.c1334
-rw-r--r--qemu/hw/misc/macio/cuda.c480
-rw-r--r--qemu/hw/misc/macio/mac_dbdma.c78
-rw-r--r--qemu/hw/misc/macio/macio.c19
-rw-r--r--qemu/hw/misc/max111x.c3
-rw-r--r--qemu/hw/misc/milkymist-hpdmc.c1
-rw-r--r--qemu/hw/misc/milkymist-pfpu.c1
-rw-r--r--qemu/hw/misc/mips_cmgcr.c160
-rw-r--r--qemu/hw/misc/mips_cpc.c177
-rw-r--r--qemu/hw/misc/mips_itu.c521
-rw-r--r--qemu/hw/misc/mst_fpga.c1
-rw-r--r--qemu/hw/misc/omap_clk.c3
-rw-r--r--qemu/hw/misc/omap_gpmc.c4
-rw-r--r--qemu/hw/misc/omap_l4.c1
-rw-r--r--qemu/hw/misc/omap_sdrc.c4
-rw-r--r--qemu/hw/misc/omap_tap.c1
-rw-r--r--qemu/hw/misc/pc-testdev.c2
-rw-r--r--qemu/hw/misc/pci-testdev.c9
-rw-r--r--qemu/hw/misc/puv3_pm.c1
-rw-r--r--qemu/hw/misc/pvpanic.c4
-rw-r--r--qemu/hw/misc/sga.c1
-rw-r--r--qemu/hw/misc/slavio_misc.c1
-rw-r--r--qemu/hw/misc/stm32f2xx_syscfg.c1
-rw-r--r--qemu/hw/misc/tmp105.c14
-rw-r--r--qemu/hw/misc/vmport.c1
-rw-r--r--qemu/hw/misc/zynq-xadc.c303
-rw-r--r--qemu/hw/misc/zynq_slcr.c9
46 files changed, 4931 insertions, 914 deletions
diff --git a/qemu/hw/misc/Makefile.objs b/qemu/hw/misc/Makefile.objs
index 4aa76ffec..93f952880 100644
--- a/qemu/hw/misc/Makefile.objs
+++ b/qemu/hw/misc/Makefile.objs
@@ -26,6 +26,9 @@ obj-$(CONFIG_NSERIES) += cbus.o
obj-$(CONFIG_ECCMEMCTL) += eccmemctl.o
obj-$(CONFIG_EXYNOS4) += exynos4210_pmu.o
obj-$(CONFIG_IMX) += imx_ccm.o
+obj-$(CONFIG_IMX) += imx31_ccm.o
+obj-$(CONFIG_IMX) += imx25_ccm.o
+obj-$(CONFIG_IMX) += imx6_ccm.o
obj-$(CONFIG_MILKYMIST) += milkymist-hpdmc.o
obj-$(CONFIG_MILKYMIST) += milkymist-pfpu.o
obj-$(CONFIG_MAINSTONE) += mst_fpga.o
@@ -34,9 +37,16 @@ obj-$(CONFIG_OMAP) += omap_gpmc.o
obj-$(CONFIG_OMAP) += omap_l4.o
obj-$(CONFIG_OMAP) += omap_sdrc.o
obj-$(CONFIG_OMAP) += omap_tap.o
+obj-$(CONFIG_RASPI) += bcm2835_mbox.o
+obj-$(CONFIG_RASPI) += bcm2835_property.o
obj-$(CONFIG_SLAVIO) += slavio_misc.o
obj-$(CONFIG_ZYNQ) += zynq_slcr.o
+obj-$(CONFIG_ZYNQ) += zynq-xadc.o
obj-$(CONFIG_STM32F2XX_SYSCFG) += stm32f2xx_syscfg.o
+obj-$(CONFIG_MIPS_CPS) += mips_cmgcr.o
+obj-$(CONFIG_MIPS_CPS) += mips_cpc.o
+obj-$(CONFIG_MIPS_ITU) += mips_itu.o
obj-$(CONFIG_PVPANIC) += pvpanic.o
obj-$(CONFIG_EDU) += edu.o
+obj-$(CONFIG_HYPERV_TESTDEV) += hyperv_testdev.o
diff --git a/qemu/hw/misc/a9scu.c b/qemu/hw/misc/a9scu.c
index 443494590..3e8ad8cd7 100644
--- a/qemu/hw/misc/a9scu.c
+++ b/qemu/hw/misc/a9scu.c
@@ -8,6 +8,7 @@
* This code is licensed under the GPL.
*/
+#include "qemu/osdep.h"
#include "hw/misc/a9scu.h"
static uint64_t a9_scu_read(void *opaque, hwaddr offset,
diff --git a/qemu/hw/misc/applesmc.c b/qemu/hw/misc/applesmc.c
index 6bd61e782..77fab5b9d 100644
--- a/qemu/hw/misc/applesmc.c
+++ b/qemu/hw/misc/applesmc.c
@@ -30,6 +30,7 @@
*
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/isa/isa.h"
#include "ui/console.h"
diff --git a/qemu/hw/misc/arm11scu.c b/qemu/hw/misc/arm11scu.c
index a79167544..5e54b494b 100644
--- a/qemu/hw/misc/arm11scu.c
+++ b/qemu/hw/misc/arm11scu.c
@@ -8,6 +8,7 @@
* This code is licensed under the GPL.
*/
+#include "qemu/osdep.h"
#include "hw/misc/arm11scu.h"
static uint64_t mpcore_scu_read(void *opaque, hwaddr offset,
diff --git a/qemu/hw/misc/arm_integrator_debug.c b/qemu/hw/misc/arm_integrator_debug.c
index 99b720fbb..902605fef 100644
--- a/qemu/hw/misc/arm_integrator_debug.c
+++ b/qemu/hw/misc/arm_integrator_debug.c
@@ -14,6 +14,7 @@
* See the COPYING file in the top-level directory.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/sysbus.h"
#include "exec/address-spaces.h"
@@ -79,7 +80,7 @@ static void intdbg_control_init(Object *obj)
SysBusDevice *sd = SYS_BUS_DEVICE(obj);
IntegratorDebugState *s = INTEGRATOR_DEBUG(obj);
- memory_region_init_io(&s->iomem, NULL, &intdbg_control_ops,
+ memory_region_init_io(&s->iomem, obj, &intdbg_control_ops,
NULL, "dbg-leds", 0x1000000);
sysbus_init_mmio(sd, &s->iomem);
}
diff --git a/qemu/hw/misc/arm_l2x0.c b/qemu/hw/misc/arm_l2x0.c
index 9e220c9a5..7e179f1a4 100644
--- a/qemu/hw/misc/arm_l2x0.c
+++ b/qemu/hw/misc/arm_l2x0.c
@@ -18,6 +18,7 @@
*
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
/* L2C-310 r3p2 */
diff --git a/qemu/hw/misc/arm_sysctl.c b/qemu/hw/misc/arm_sysctl.c
index 3fad6f86d..34d90d523 100644
--- a/qemu/hw/misc/arm_sysctl.c
+++ b/qemu/hw/misc/arm_sysctl.c
@@ -7,6 +7,7 @@
* This code is licensed under the GPL.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "qemu/timer.h"
#include "qemu/bitops.h"
@@ -170,7 +171,8 @@ static uint64_t arm_sysctl_read(void *opaque, hwaddr offset,
case 0x58: /* BOOTCS */
return 0;
case 0x5c: /* 24MHz */
- return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), 24000000, get_ticks_per_sec());
+ return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), 24000000,
+ NANOSECONDS_PER_SECOND);
case 0x60: /* MISC */
return 0;
case 0x84: /* PROCID0 */
diff --git a/qemu/hw/misc/bcm2835_mbox.c b/qemu/hw/misc/bcm2835_mbox.c
new file mode 100644
index 000000000..263280fd4
--- /dev/null
+++ b/qemu/hw/misc/bcm2835_mbox.c
@@ -0,0 +1,335 @@
+/*
+ * Raspberry Pi emulation (c) 2012 Gregory Estrade
+ * This code is licensed under the GNU GPLv2 and later.
+ *
+ * This file models the system mailboxes, which are used for
+ * communication with low-bandwidth GPU peripherals. Refs:
+ * https://github.com/raspberrypi/firmware/wiki/Mailboxes
+ * https://github.com/raspberrypi/firmware/wiki/Accessing-mailboxes
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/misc/bcm2835_mbox.h"
+
+#define MAIL0_PEEK 0x90
+#define MAIL0_SENDER 0x94
+#define MAIL1_STATUS 0xb8
+
+/* Mailbox status register */
+#define MAIL0_STATUS 0x98
+#define ARM_MS_FULL 0x80000000
+#define ARM_MS_EMPTY 0x40000000
+#define ARM_MS_LEVEL 0x400000FF /* Max. value depends on mailbox depth */
+
+/* MAILBOX config/status register */
+#define MAIL0_CONFIG 0x9c
+/* ANY write to this register clears the error bits! */
+#define ARM_MC_IHAVEDATAIRQEN 0x00000001 /* mbox irq enable: has data */
+#define ARM_MC_IHAVESPACEIRQEN 0x00000002 /* mbox irq enable: has space */
+#define ARM_MC_OPPISEMPTYIRQEN 0x00000004 /* mbox irq enable: Opp is empty */
+#define ARM_MC_MAIL_CLEAR 0x00000008 /* mbox clear write 1, then 0 */
+#define ARM_MC_IHAVEDATAIRQPEND 0x00000010 /* mbox irq pending: has space */
+#define ARM_MC_IHAVESPACEIRQPEND 0x00000020 /* mbox irq pending: Opp is empty */
+#define ARM_MC_OPPISEMPTYIRQPEND 0x00000040 /* mbox irq pending */
+/* Bit 7 is unused */
+#define ARM_MC_ERRNOOWN 0x00000100 /* error : none owner read from mailbox */
+#define ARM_MC_ERROVERFLW 0x00000200 /* error : write to fill mailbox */
+#define ARM_MC_ERRUNDRFLW 0x00000400 /* error : read from empty mailbox */
+
+static void mbox_update_status(BCM2835Mbox *mb)
+{
+ mb->status &= ~(ARM_MS_EMPTY | ARM_MS_FULL);
+ if (mb->count == 0) {
+ mb->status |= ARM_MS_EMPTY;
+ } else if (mb->count == MBOX_SIZE) {
+ mb->status |= ARM_MS_FULL;
+ }
+}
+
+static void mbox_reset(BCM2835Mbox *mb)
+{
+ int n;
+
+ mb->count = 0;
+ mb->config = 0;
+ for (n = 0; n < MBOX_SIZE; n++) {
+ mb->reg[n] = MBOX_INVALID_DATA;
+ }
+ mbox_update_status(mb);
+}
+
+static uint32_t mbox_pull(BCM2835Mbox *mb, int index)
+{
+ int n;
+ uint32_t val;
+
+ assert(mb->count > 0);
+ assert(index < mb->count);
+
+ val = mb->reg[index];
+ for (n = index + 1; n < mb->count; n++) {
+ mb->reg[n - 1] = mb->reg[n];
+ }
+ mb->count--;
+ mb->reg[mb->count] = MBOX_INVALID_DATA;
+
+ mbox_update_status(mb);
+
+ return val;
+}
+
+static void mbox_push(BCM2835Mbox *mb, uint32_t val)
+{
+ assert(mb->count < MBOX_SIZE);
+ mb->reg[mb->count++] = val;
+ mbox_update_status(mb);
+}
+
+static void bcm2835_mbox_update(BCM2835MboxState *s)
+{
+ uint32_t value;
+ bool set;
+ int n;
+
+ s->mbox_irq_disabled = true;
+
+ /* Get pending responses and put them in the vc->arm mbox,
+ * as long as it's not full
+ */
+ for (n = 0; n < MBOX_CHAN_COUNT; n++) {
+ while (s->available[n] && !(s->mbox[0].status & ARM_MS_FULL)) {
+ value = ldl_le_phys(&s->mbox_as, n << MBOX_AS_CHAN_SHIFT);
+ assert(value != MBOX_INVALID_DATA); /* Pending interrupt but no data */
+ mbox_push(&s->mbox[0], value);
+ }
+ }
+
+ /* TODO (?): Try to push pending requests from the arm->vc mbox */
+
+ /* Re-enable calls from the IRQ routine */
+ s->mbox_irq_disabled = false;
+
+ /* Update ARM IRQ status */
+ set = false;
+ s->mbox[0].config &= ~ARM_MC_IHAVEDATAIRQPEND;
+ if (!(s->mbox[0].status & ARM_MS_EMPTY)) {
+ s->mbox[0].config |= ARM_MC_IHAVEDATAIRQPEND;
+ if (s->mbox[0].config & ARM_MC_IHAVEDATAIRQEN) {
+ set = true;
+ }
+ }
+ qemu_set_irq(s->arm_irq, set);
+}
+
+static void bcm2835_mbox_set_irq(void *opaque, int irq, int level)
+{
+ BCM2835MboxState *s = opaque;
+
+ s->available[irq] = level;
+
+ /* avoid recursively calling bcm2835_mbox_update when the interrupt
+ * status changes due to the ldl_phys call within that function
+ */
+ if (!s->mbox_irq_disabled) {
+ bcm2835_mbox_update(s);
+ }
+}
+
+static uint64_t bcm2835_mbox_read(void *opaque, hwaddr offset, unsigned size)
+{
+ BCM2835MboxState *s = opaque;
+ uint32_t res = 0;
+
+ offset &= 0xff;
+
+ switch (offset) {
+ case 0x80 ... 0x8c: /* MAIL0_READ */
+ if (s->mbox[0].status & ARM_MS_EMPTY) {
+ res = MBOX_INVALID_DATA;
+ } else {
+ res = mbox_pull(&s->mbox[0], 0);
+ }
+ break;
+
+ case MAIL0_PEEK:
+ res = s->mbox[0].reg[0];
+ break;
+
+ case MAIL0_SENDER:
+ break;
+
+ case MAIL0_STATUS:
+ res = s->mbox[0].status;
+ break;
+
+ case MAIL0_CONFIG:
+ res = s->mbox[0].config;
+ break;
+
+ case MAIL1_STATUS:
+ res = s->mbox[1].status;
+ break;
+
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
+ __func__, offset);
+ return 0;
+ }
+
+ bcm2835_mbox_update(s);
+
+ return res;
+}
+
+static void bcm2835_mbox_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ BCM2835MboxState *s = opaque;
+ hwaddr childaddr;
+ uint8_t ch;
+
+ offset &= 0xff;
+
+ switch (offset) {
+ case MAIL0_SENDER:
+ break;
+
+ case MAIL0_CONFIG:
+ s->mbox[0].config &= ~ARM_MC_IHAVEDATAIRQEN;
+ s->mbox[0].config |= value & ARM_MC_IHAVEDATAIRQEN;
+ break;
+
+ case 0xa0 ... 0xac: /* MAIL1_WRITE */
+ if (s->mbox[1].status & ARM_MS_FULL) {
+ /* Mailbox full */
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: mailbox full\n", __func__);
+ } else {
+ ch = value & 0xf;
+ if (ch < MBOX_CHAN_COUNT) {
+ childaddr = ch << MBOX_AS_CHAN_SHIFT;
+ if (ldl_le_phys(&s->mbox_as, childaddr + MBOX_AS_PENDING)) {
+ /* Child busy, push delayed. Push it in the arm->vc mbox */
+ mbox_push(&s->mbox[1], value);
+ } else {
+ /* Push it directly to the child device */
+ stl_le_phys(&s->mbox_as, childaddr, value);
+ }
+ } else {
+ /* Invalid channel number */
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid channel %u\n",
+ __func__, ch);
+ }
+ }
+ break;
+
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
+ __func__, offset);
+ return;
+ }
+
+ bcm2835_mbox_update(s);
+}
+
+static const MemoryRegionOps bcm2835_mbox_ops = {
+ .read = bcm2835_mbox_read,
+ .write = bcm2835_mbox_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+};
+
+/* vmstate of a single mailbox */
+static const VMStateDescription vmstate_bcm2835_mbox_box = {
+ .name = TYPE_BCM2835_MBOX "_box",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(reg, BCM2835Mbox, MBOX_SIZE),
+ VMSTATE_UINT32(count, BCM2835Mbox),
+ VMSTATE_UINT32(status, BCM2835Mbox),
+ VMSTATE_UINT32(config, BCM2835Mbox),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+/* vmstate of the entire device */
+static const VMStateDescription vmstate_bcm2835_mbox = {
+ .name = TYPE_BCM2835_MBOX,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_BOOL_ARRAY(available, BCM2835MboxState, MBOX_CHAN_COUNT),
+ VMSTATE_STRUCT_ARRAY(mbox, BCM2835MboxState, 2, 1,
+ vmstate_bcm2835_mbox_box, BCM2835Mbox),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void bcm2835_mbox_init(Object *obj)
+{
+ BCM2835MboxState *s = BCM2835_MBOX(obj);
+
+ memory_region_init_io(&s->iomem, obj, &bcm2835_mbox_ops, s,
+ TYPE_BCM2835_MBOX, 0x400);
+ sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem);
+ sysbus_init_irq(SYS_BUS_DEVICE(s), &s->arm_irq);
+ qdev_init_gpio_in(DEVICE(s), bcm2835_mbox_set_irq, MBOX_CHAN_COUNT);
+}
+
+static void bcm2835_mbox_reset(DeviceState *dev)
+{
+ BCM2835MboxState *s = BCM2835_MBOX(dev);
+ int n;
+
+ mbox_reset(&s->mbox[0]);
+ mbox_reset(&s->mbox[1]);
+ s->mbox_irq_disabled = false;
+ for (n = 0; n < MBOX_CHAN_COUNT; n++) {
+ s->available[n] = false;
+ }
+}
+
+static void bcm2835_mbox_realize(DeviceState *dev, Error **errp)
+{
+ BCM2835MboxState *s = BCM2835_MBOX(dev);
+ Object *obj;
+ Error *err = NULL;
+
+ obj = object_property_get_link(OBJECT(dev), "mbox-mr", &err);
+ if (obj == NULL) {
+ error_setg(errp, "%s: required mbox-mr link not found: %s",
+ __func__, error_get_pretty(err));
+ return;
+ }
+
+ s->mbox_mr = MEMORY_REGION(obj);
+ address_space_init(&s->mbox_as, s->mbox_mr, NULL);
+ bcm2835_mbox_reset(dev);
+}
+
+static void bcm2835_mbox_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = bcm2835_mbox_realize;
+ dc->reset = bcm2835_mbox_reset;
+ dc->vmsd = &vmstate_bcm2835_mbox;
+}
+
+static TypeInfo bcm2835_mbox_info = {
+ .name = TYPE_BCM2835_MBOX,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(BCM2835MboxState),
+ .class_init = bcm2835_mbox_class_init,
+ .instance_init = bcm2835_mbox_init,
+};
+
+static void bcm2835_mbox_register_types(void)
+{
+ type_register_static(&bcm2835_mbox_info);
+}
+
+type_init(bcm2835_mbox_register_types)
diff --git a/qemu/hw/misc/bcm2835_property.c b/qemu/hw/misc/bcm2835_property.c
new file mode 100644
index 000000000..530411f84
--- /dev/null
+++ b/qemu/hw/misc/bcm2835_property.c
@@ -0,0 +1,424 @@
+/*
+ * Raspberry Pi emulation (c) 2012 Gregory Estrade
+ * This code is licensed under the GNU GPLv2 and later.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/misc/bcm2835_property.h"
+#include "hw/misc/bcm2835_mbox_defs.h"
+#include "sysemu/dma.h"
+
+/* https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface */
+
+static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value)
+{
+ uint32_t tag;
+ uint32_t bufsize;
+ uint32_t tot_len;
+ size_t resplen;
+ uint32_t tmp;
+ int n;
+ uint32_t offset, length, color;
+ uint32_t xres, yres, xoffset, yoffset, bpp, pixo, alpha;
+ uint32_t *newxres = NULL, *newyres = NULL, *newxoffset = NULL,
+ *newyoffset = NULL, *newbpp = NULL, *newpixo = NULL, *newalpha = NULL;
+
+ value &= ~0xf;
+
+ s->addr = value;
+
+ tot_len = ldl_le_phys(&s->dma_as, value);
+
+ /* @(addr + 4) : Buffer response code */
+ value = s->addr + 8;
+ while (value + 8 <= s->addr + tot_len) {
+ tag = ldl_le_phys(&s->dma_as, value);
+ bufsize = ldl_le_phys(&s->dma_as, value + 4);
+ /* @(value + 8) : Request/response indicator */
+ resplen = 0;
+ switch (tag) {
+ case 0x00000000: /* End tag */
+ break;
+ case 0x00000001: /* Get firmware revision */
+ stl_le_phys(&s->dma_as, value + 12, 346337);
+ resplen = 4;
+ break;
+ case 0x00010001: /* Get board model */
+ qemu_log_mask(LOG_UNIMP,
+ "bcm2835_property: %x get board model NYI\n", tag);
+ resplen = 4;
+ break;
+ case 0x00010002: /* Get board revision */
+ stl_le_phys(&s->dma_as, value + 12, s->board_rev);
+ resplen = 4;
+ break;
+ case 0x00010003: /* Get board MAC address */
+ resplen = sizeof(s->macaddr.a);
+ dma_memory_write(&s->dma_as, value + 12, s->macaddr.a, resplen);
+ break;
+ case 0x00010004: /* Get board serial */
+ qemu_log_mask(LOG_UNIMP,
+ "bcm2835_property: %x get board serial NYI\n", tag);
+ resplen = 8;
+ break;
+ case 0x00010005: /* Get ARM memory */
+ /* base */
+ stl_le_phys(&s->dma_as, value + 12, 0);
+ /* size */
+ stl_le_phys(&s->dma_as, value + 16, s->fbdev->vcram_base);
+ resplen = 8;
+ break;
+ case 0x00010006: /* Get VC memory */
+ /* base */
+ stl_le_phys(&s->dma_as, value + 12, s->fbdev->vcram_base);
+ /* size */
+ stl_le_phys(&s->dma_as, value + 16, s->fbdev->vcram_size);
+ resplen = 8;
+ break;
+ case 0x00028001: /* Set power state */
+ /* Assume that whatever device they asked for exists,
+ * and we'll just claim we set it to the desired state
+ */
+ tmp = ldl_le_phys(&s->dma_as, value + 16);
+ stl_le_phys(&s->dma_as, value + 16, (tmp & 1));
+ resplen = 8;
+ break;
+
+ /* Clocks */
+
+ case 0x00030001: /* Get clock state */
+ stl_le_phys(&s->dma_as, value + 16, 0x1);
+ resplen = 8;
+ break;
+
+ case 0x00038001: /* Set clock state */
+ qemu_log_mask(LOG_UNIMP,
+ "bcm2835_property: %x set clock state NYI\n", tag);
+ resplen = 8;
+ break;
+
+ case 0x00030002: /* Get clock rate */
+ case 0x00030004: /* Get max clock rate */
+ case 0x00030007: /* Get min clock rate */
+ switch (ldl_le_phys(&s->dma_as, value + 12)) {
+ case 1: /* EMMC */
+ stl_le_phys(&s->dma_as, value + 16, 50000000);
+ break;
+ case 2: /* UART */
+ stl_le_phys(&s->dma_as, value + 16, 3000000);
+ break;
+ default:
+ stl_le_phys(&s->dma_as, value + 16, 700000000);
+ break;
+ }
+ resplen = 8;
+ break;
+
+ case 0x00038002: /* Set clock rate */
+ case 0x00038004: /* Set max clock rate */
+ case 0x00038007: /* Set min clock rate */
+ qemu_log_mask(LOG_UNIMP,
+ "bcm2835_property: %x set clock rates NYI\n", tag);
+ resplen = 8;
+ break;
+
+ /* Temperature */
+
+ case 0x00030006: /* Get temperature */
+ stl_le_phys(&s->dma_as, value + 16, 25000);
+ resplen = 8;
+ break;
+
+ case 0x0003000A: /* Get max temperature */
+ stl_le_phys(&s->dma_as, value + 16, 99000);
+ resplen = 8;
+ break;
+
+ /* Frame buffer */
+
+ case 0x00040001: /* Allocate buffer */
+ stl_le_phys(&s->dma_as, value + 12, s->fbdev->base);
+ stl_le_phys(&s->dma_as, value + 16, s->fbdev->size);
+ resplen = 8;
+ break;
+ case 0x00048001: /* Release buffer */
+ resplen = 0;
+ break;
+ case 0x00040002: /* Blank screen */
+ resplen = 4;
+ break;
+ case 0x00040003: /* Get display width/height */
+ case 0x00040004:
+ stl_le_phys(&s->dma_as, value + 12, s->fbdev->xres);
+ stl_le_phys(&s->dma_as, value + 16, s->fbdev->yres);
+ resplen = 8;
+ break;
+ case 0x00044003: /* Test display width/height */
+ case 0x00044004:
+ resplen = 8;
+ break;
+ case 0x00048003: /* Set display width/height */
+ case 0x00048004:
+ xres = ldl_le_phys(&s->dma_as, value + 12);
+ newxres = &xres;
+ yres = ldl_le_phys(&s->dma_as, value + 16);
+ newyres = &yres;
+ resplen = 8;
+ break;
+ case 0x00040005: /* Get depth */
+ stl_le_phys(&s->dma_as, value + 12, s->fbdev->bpp);
+ resplen = 4;
+ break;
+ case 0x00044005: /* Test depth */
+ resplen = 4;
+ break;
+ case 0x00048005: /* Set depth */
+ bpp = ldl_le_phys(&s->dma_as, value + 12);
+ newbpp = &bpp;
+ resplen = 4;
+ break;
+ case 0x00040006: /* Get pixel order */
+ stl_le_phys(&s->dma_as, value + 12, s->fbdev->pixo);
+ resplen = 4;
+ break;
+ case 0x00044006: /* Test pixel order */
+ resplen = 4;
+ break;
+ case 0x00048006: /* Set pixel order */
+ pixo = ldl_le_phys(&s->dma_as, value + 12);
+ newpixo = &pixo;
+ resplen = 4;
+ break;
+ case 0x00040007: /* Get alpha */
+ stl_le_phys(&s->dma_as, value + 12, s->fbdev->alpha);
+ resplen = 4;
+ break;
+ case 0x00044007: /* Test pixel alpha */
+ resplen = 4;
+ break;
+ case 0x00048007: /* Set alpha */
+ alpha = ldl_le_phys(&s->dma_as, value + 12);
+ newalpha = &alpha;
+ resplen = 4;
+ break;
+ case 0x00040008: /* Get pitch */
+ stl_le_phys(&s->dma_as, value + 12, s->fbdev->pitch);
+ resplen = 4;
+ break;
+ case 0x00040009: /* Get virtual offset */
+ stl_le_phys(&s->dma_as, value + 12, s->fbdev->xoffset);
+ stl_le_phys(&s->dma_as, value + 16, s->fbdev->yoffset);
+ resplen = 8;
+ break;
+ case 0x00044009: /* Test virtual offset */
+ resplen = 8;
+ break;
+ case 0x00048009: /* Set virtual offset */
+ xoffset = ldl_le_phys(&s->dma_as, value + 12);
+ newxoffset = &xoffset;
+ yoffset = ldl_le_phys(&s->dma_as, value + 16);
+ newyoffset = &yoffset;
+ resplen = 8;
+ break;
+ case 0x0004000a: /* Get/Test/Set overscan */
+ case 0x0004400a:
+ case 0x0004800a:
+ stl_le_phys(&s->dma_as, value + 12, 0);
+ stl_le_phys(&s->dma_as, value + 16, 0);
+ stl_le_phys(&s->dma_as, value + 20, 0);
+ stl_le_phys(&s->dma_as, value + 24, 0);
+ resplen = 16;
+ break;
+ case 0x0004800b: /* Set palette */
+ offset = ldl_le_phys(&s->dma_as, value + 12);
+ length = ldl_le_phys(&s->dma_as, value + 16);
+ n = 0;
+ while (n < length - offset) {
+ color = ldl_le_phys(&s->dma_as, value + 20 + (n << 2));
+ stl_le_phys(&s->dma_as,
+ s->fbdev->vcram_base + ((offset + n) << 2), color);
+ n++;
+ }
+ stl_le_phys(&s->dma_as, value + 12, 0);
+ resplen = 4;
+ break;
+
+ case 0x00060001: /* Get DMA channels */
+ /* channels 2-5 */
+ stl_le_phys(&s->dma_as, value + 12, 0x003C);
+ resplen = 4;
+ break;
+
+ case 0x00050001: /* Get command line */
+ resplen = 0;
+ break;
+
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "bcm2835_property: unhandled tag %08x\n", tag);
+ break;
+ }
+
+ if (tag == 0) {
+ break;
+ }
+
+ stl_le_phys(&s->dma_as, value + 8, (1 << 31) | resplen);
+ value += bufsize + 12;
+ }
+
+ /* Reconfigure framebuffer if required */
+ if (newxres || newyres || newxoffset || newyoffset || newbpp || newpixo
+ || newalpha) {
+ bcm2835_fb_reconfigure(s->fbdev, newxres, newyres, newxoffset,
+ newyoffset, newbpp, newpixo, newalpha);
+ }
+
+ /* Buffer response code */
+ stl_le_phys(&s->dma_as, s->addr + 4, (1 << 31));
+}
+
+static uint64_t bcm2835_property_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ BCM2835PropertyState *s = opaque;
+ uint32_t res = 0;
+
+ switch (offset) {
+ case MBOX_AS_DATA:
+ res = MBOX_CHAN_PROPERTY | s->addr;
+ s->pending = false;
+ qemu_set_irq(s->mbox_irq, 0);
+ break;
+
+ case MBOX_AS_PENDING:
+ res = s->pending;
+ break;
+
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
+ __func__, offset);
+ return 0;
+ }
+
+ return res;
+}
+
+static void bcm2835_property_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ BCM2835PropertyState *s = opaque;
+
+ switch (offset) {
+ case MBOX_AS_DATA:
+ /* bcm2835_mbox should check our pending status before pushing */
+ assert(!s->pending);
+ s->pending = true;
+ bcm2835_property_mbox_push(s, value);
+ qemu_set_irq(s->mbox_irq, 1);
+ break;
+
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
+ __func__, offset);
+ return;
+ }
+}
+
+static const MemoryRegionOps bcm2835_property_ops = {
+ .read = bcm2835_property_read,
+ .write = bcm2835_property_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+};
+
+static const VMStateDescription vmstate_bcm2835_property = {
+ .name = TYPE_BCM2835_PROPERTY,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_MACADDR(macaddr, BCM2835PropertyState),
+ VMSTATE_UINT32(addr, BCM2835PropertyState),
+ VMSTATE_BOOL(pending, BCM2835PropertyState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void bcm2835_property_init(Object *obj)
+{
+ BCM2835PropertyState *s = BCM2835_PROPERTY(obj);
+
+ memory_region_init_io(&s->iomem, OBJECT(s), &bcm2835_property_ops, s,
+ TYPE_BCM2835_PROPERTY, 0x10);
+ sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem);
+ sysbus_init_irq(SYS_BUS_DEVICE(s), &s->mbox_irq);
+}
+
+static void bcm2835_property_reset(DeviceState *dev)
+{
+ BCM2835PropertyState *s = BCM2835_PROPERTY(dev);
+
+ s->pending = false;
+}
+
+static void bcm2835_property_realize(DeviceState *dev, Error **errp)
+{
+ BCM2835PropertyState *s = BCM2835_PROPERTY(dev);
+ Object *obj;
+ Error *err = NULL;
+
+ obj = object_property_get_link(OBJECT(dev), "fb", &err);
+ if (obj == NULL) {
+ error_setg(errp, "%s: required fb link not found: %s",
+ __func__, error_get_pretty(err));
+ return;
+ }
+
+ s->fbdev = BCM2835_FB(obj);
+
+ obj = object_property_get_link(OBJECT(dev), "dma-mr", &err);
+ if (obj == NULL) {
+ error_setg(errp, "%s: required dma-mr link not found: %s",
+ __func__, error_get_pretty(err));
+ return;
+ }
+
+ s->dma_mr = MEMORY_REGION(obj);
+ address_space_init(&s->dma_as, s->dma_mr, NULL);
+
+ /* TODO: connect to MAC address of USB NIC device, once we emulate it */
+ qemu_macaddr_default_if_unset(&s->macaddr);
+
+ bcm2835_property_reset(dev);
+}
+
+static Property bcm2835_property_props[] = {
+ DEFINE_PROP_UINT32("board-rev", BCM2835PropertyState, board_rev, 0),
+ DEFINE_PROP_END_OF_LIST()
+};
+
+static void bcm2835_property_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->props = bcm2835_property_props;
+ dc->realize = bcm2835_property_realize;
+ dc->vmsd = &vmstate_bcm2835_property;
+}
+
+static TypeInfo bcm2835_property_info = {
+ .name = TYPE_BCM2835_PROPERTY,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(BCM2835PropertyState),
+ .class_init = bcm2835_property_class_init,
+ .instance_init = bcm2835_property_init,
+};
+
+static void bcm2835_property_register_types(void)
+{
+ type_register_static(&bcm2835_property_info);
+}
+
+type_init(bcm2835_property_register_types)
diff --git a/qemu/hw/misc/cbus.c b/qemu/hw/misc/cbus.c
index 495d5078f..0c207e310 100644
--- a/qemu/hw/misc/cbus.c
+++ b/qemu/hw/misc/cbus.c
@@ -20,7 +20,8 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
-#include "qemu-common.h"
+#include "qemu/osdep.h"
+#include "hw/hw.h"
#include "hw/irq.h"
#include "hw/devices.h"
#include "sysemu/sysemu.h"
diff --git a/qemu/hw/misc/debugexit.c b/qemu/hw/misc/debugexit.c
index 69a1b004c..84fa1a5b9 100644
--- a/qemu/hw/misc/debugexit.c
+++ b/qemu/hw/misc/debugexit.c
@@ -7,6 +7,7 @@
* (at your option) any later version.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/isa/isa.h"
diff --git a/qemu/hw/misc/eccmemctl.c b/qemu/hw/misc/eccmemctl.c
index 8bad6f682..a0071f3ea 100644
--- a/qemu/hw/misc/eccmemctl.c
+++ b/qemu/hw/misc/eccmemctl.c
@@ -22,6 +22,7 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "trace.h"
diff --git a/qemu/hw/misc/edu.c b/qemu/hw/misc/edu.c
index fe50b42af..888ba49a0 100644
--- a/qemu/hw/misc/edu.c
+++ b/qemu/hw/misc/edu.c
@@ -22,6 +22,7 @@
* DEALINGS IN THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/pci/pci.h"
#include "qemu/timer.h"
#include "qemu/main-loop.h" /* iothread mutex */
@@ -327,7 +328,7 @@ static void *edu_fact_thread(void *opaque)
return NULL;
}
-static int pci_edu_init(PCIDevice *pdev)
+static void pci_edu_realize(PCIDevice *pdev, Error **errp)
{
EduState *edu = DO_UPCAST(EduState, pdev, pdev);
uint8_t *pci_conf = pdev->config;
@@ -344,8 +345,6 @@ static int pci_edu_init(PCIDevice *pdev)
memory_region_init_io(&edu->mmio, OBJECT(edu), &edu_mmio_ops, edu,
"edu-mmio", 1 << 20);
pci_register_bar(pdev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &edu->mmio);
-
- return 0;
}
static void pci_edu_uninit(PCIDevice *pdev)
@@ -364,12 +363,12 @@ static void pci_edu_uninit(PCIDevice *pdev)
timer_del(&edu->dma_timer);
}
-static void edu_obj_uint64(Object *obj, struct Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void edu_obj_uint64(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
{
uint64_t *val = opaque;
- visit_type_uint64(v, val, name, errp);
+ visit_type_uint64(v, name, val, errp);
}
static void edu_instance_init(Object *obj)
@@ -385,7 +384,7 @@ static void edu_class_init(ObjectClass *class, void *data)
{
PCIDeviceClass *k = PCI_DEVICE_CLASS(class);
- k->init = pci_edu_init;
+ k->realize = pci_edu_realize;
k->exit = pci_edu_uninit;
k->vendor_id = PCI_VENDOR_ID_QEMU;
k->device_id = 0x11e8;
diff --git a/qemu/hw/misc/exynos4210_pmu.c b/qemu/hw/misc/exynos4210_pmu.c
index 2b118c725..889abadfe 100644
--- a/qemu/hw/misc/exynos4210_pmu.c
+++ b/qemu/hw/misc/exynos4210_pmu.c
@@ -24,6 +24,7 @@
* uses PMU INFORM5 register as a holding pen.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#ifndef DEBUG_PMU
diff --git a/qemu/hw/misc/hyperv_testdev.c b/qemu/hw/misc/hyperv_testdev.c
new file mode 100644
index 000000000..1883fd7f2
--- /dev/null
+++ b/qemu/hw/misc/hyperv_testdev.c
@@ -0,0 +1,168 @@
+/*
+ * QEMU KVM Hyper-V test device to support Hyper-V kvm-unit-tests
+ *
+ * Copyright (C) 2015 Andrey Smetanin <asmetanin@virtuozzo.com>
+ *
+ * Authors:
+ * Andrey Smetanin <asmetanin@virtuozzo.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "hw/hw.h"
+#include "hw/qdev.h"
+#include "hw/isa/isa.h"
+#include "sysemu/kvm.h"
+#include "linux/kvm.h"
+#include "target-i386/hyperv.h"
+#include "kvm_i386.h"
+
+#define HV_TEST_DEV_MAX_SINT_ROUTES 64
+
+struct HypervTestDev {
+ ISADevice parent_obj;
+ MemoryRegion sint_control;
+ HvSintRoute *sint_route[HV_TEST_DEV_MAX_SINT_ROUTES];
+};
+typedef struct HypervTestDev HypervTestDev;
+
+#define TYPE_HYPERV_TEST_DEV "hyperv-testdev"
+#define HYPERV_TEST_DEV(obj) \
+ OBJECT_CHECK(HypervTestDev, (obj), TYPE_HYPERV_TEST_DEV)
+
+enum {
+ HV_TEST_DEV_SINT_ROUTE_CREATE = 1,
+ HV_TEST_DEV_SINT_ROUTE_DESTROY,
+ HV_TEST_DEV_SINT_ROUTE_SET_SINT
+};
+
+static int alloc_sint_route_index(HypervTestDev *dev)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(dev->sint_route); i++) {
+ if (dev->sint_route[i] == NULL) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+static void free_sint_route_index(HypervTestDev *dev, int i)
+{
+ assert(i >= 0 && i < ARRAY_SIZE(dev->sint_route));
+ dev->sint_route[i] = NULL;
+}
+
+static int find_sint_route_index(HypervTestDev *dev, uint32_t vcpu_id,
+ uint32_t sint)
+{
+ HvSintRoute *sint_route;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(dev->sint_route); i++) {
+ sint_route = dev->sint_route[i];
+ if (sint_route && sint_route->vcpu_id == vcpu_id &&
+ sint_route->sint == sint) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+static void hv_synic_test_dev_control(HypervTestDev *dev, uint32_t ctl,
+ uint32_t vcpu_id, uint32_t sint)
+{
+ int i;
+ HvSintRoute *sint_route;
+
+ switch (ctl) {
+ case HV_TEST_DEV_SINT_ROUTE_CREATE:
+ i = alloc_sint_route_index(dev);
+ assert(i >= 0);
+ sint_route = kvm_hv_sint_route_create(vcpu_id, sint, NULL);
+ assert(sint_route);
+ dev->sint_route[i] = sint_route;
+ break;
+ case HV_TEST_DEV_SINT_ROUTE_DESTROY:
+ i = find_sint_route_index(dev, vcpu_id, sint);
+ assert(i >= 0);
+ sint_route = dev->sint_route[i];
+ kvm_hv_sint_route_destroy(sint_route);
+ free_sint_route_index(dev, i);
+ break;
+ case HV_TEST_DEV_SINT_ROUTE_SET_SINT:
+ i = find_sint_route_index(dev, vcpu_id, sint);
+ assert(i >= 0);
+ sint_route = dev->sint_route[i];
+ kvm_hv_sint_route_set_sint(sint_route);
+ break;
+ default:
+ break;
+ }
+}
+
+static void hv_test_dev_control(void *opaque, hwaddr addr, uint64_t data,
+ uint32_t len)
+{
+ HypervTestDev *dev = HYPERV_TEST_DEV(opaque);
+ uint8_t ctl;
+
+ ctl = (data >> 16ULL) & 0xFF;
+ switch (ctl) {
+ case HV_TEST_DEV_SINT_ROUTE_CREATE:
+ case HV_TEST_DEV_SINT_ROUTE_DESTROY:
+ case HV_TEST_DEV_SINT_ROUTE_SET_SINT: {
+ uint8_t sint = data & 0xFF;
+ uint8_t vcpu_id = (data >> 8ULL) & 0xFF;
+ hv_synic_test_dev_control(dev, ctl, vcpu_id, sint);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+static const MemoryRegionOps synic_test_sint_ops = {
+ .write = hv_test_dev_control,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void hv_test_dev_realizefn(DeviceState *d, Error **errp)
+{
+ ISADevice *isa = ISA_DEVICE(d);
+ HypervTestDev *dev = HYPERV_TEST_DEV(d);
+ MemoryRegion *io = isa_address_space_io(isa);
+
+ memset(dev->sint_route, 0, sizeof(dev->sint_route));
+ memory_region_init_io(&dev->sint_control, OBJECT(dev),
+ &synic_test_sint_ops, dev,
+ "hyperv-testdev-ctl", 4);
+ memory_region_add_subregion(io, 0x3000, &dev->sint_control);
+}
+
+static void hv_test_dev_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+ dc->realize = hv_test_dev_realizefn;
+}
+
+static const TypeInfo hv_test_dev_info = {
+ .name = TYPE_HYPERV_TEST_DEV,
+ .parent = TYPE_ISA_DEVICE,
+ .instance_size = sizeof(HypervTestDev),
+ .class_init = hv_test_dev_class_init,
+};
+
+static void hv_test_dev_register_types(void)
+{
+ type_register_static(&hv_test_dev_info);
+}
+type_init(hv_test_dev_register_types);
diff --git a/qemu/hw/misc/imx25_ccm.c b/qemu/hw/misc/imx25_ccm.c
new file mode 100644
index 000000000..225604d82
--- /dev/null
+++ b/qemu/hw/misc/imx25_ccm.c
@@ -0,0 +1,317 @@
+/*
+ * IMX25 Clock Control Module
+ *
+ * Copyright (C) 2012 NICTA
+ * Updated by Jean-Christophe Dubois <jcd@tribudubois.net>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ * To get the timer frequencies right, we need to emulate at least part of
+ * the CCM.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/misc/imx25_ccm.h"
+
+#ifndef DEBUG_IMX25_CCM
+#define DEBUG_IMX25_CCM 0
+#endif
+
+#define DPRINTF(fmt, args...) \
+ do { \
+ if (DEBUG_IMX25_CCM) { \
+ fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX25_CCM, \
+ __func__, ##args); \
+ } \
+ } while (0)
+
+static char const *imx25_ccm_reg_name(uint32_t reg)
+{
+ static char unknown[20];
+
+ switch (reg) {
+ case IMX25_CCM_MPCTL_REG:
+ return "mpctl";
+ case IMX25_CCM_UPCTL_REG:
+ return "upctl";
+ case IMX25_CCM_CCTL_REG:
+ return "cctl";
+ case IMX25_CCM_CGCR0_REG:
+ return "cgcr0";
+ case IMX25_CCM_CGCR1_REG:
+ return "cgcr1";
+ case IMX25_CCM_CGCR2_REG:
+ return "cgcr2";
+ case IMX25_CCM_PCDR0_REG:
+ return "pcdr0";
+ case IMX25_CCM_PCDR1_REG:
+ return "pcdr1";
+ case IMX25_CCM_PCDR2_REG:
+ return "pcdr2";
+ case IMX25_CCM_PCDR3_REG:
+ return "pcdr3";
+ case IMX25_CCM_RCSR_REG:
+ return "rcsr";
+ case IMX25_CCM_CRDR_REG:
+ return "crdr";
+ case IMX25_CCM_DCVR0_REG:
+ return "dcvr0";
+ case IMX25_CCM_DCVR1_REG:
+ return "dcvr1";
+ case IMX25_CCM_DCVR2_REG:
+ return "dcvr2";
+ case IMX25_CCM_DCVR3_REG:
+ return "dcvr3";
+ case IMX25_CCM_LTR0_REG:
+ return "ltr0";
+ case IMX25_CCM_LTR1_REG:
+ return "ltr1";
+ case IMX25_CCM_LTR2_REG:
+ return "ltr2";
+ case IMX25_CCM_LTR3_REG:
+ return "ltr3";
+ case IMX25_CCM_LTBR0_REG:
+ return "ltbr0";
+ case IMX25_CCM_LTBR1_REG:
+ return "ltbr1";
+ case IMX25_CCM_PMCR0_REG:
+ return "pmcr0";
+ case IMX25_CCM_PMCR1_REG:
+ return "pmcr1";
+ case IMX25_CCM_PMCR2_REG:
+ return "pmcr2";
+ case IMX25_CCM_MCR_REG:
+ return "mcr";
+ case IMX25_CCM_LPIMR0_REG:
+ return "lpimr0";
+ case IMX25_CCM_LPIMR1_REG:
+ return "lpimr1";
+ default:
+ sprintf(unknown, "[%d ?]", reg);
+ return unknown;
+ }
+}
+#define CKIH_FREQ 24000000 /* 24MHz crystal input */
+
+static const VMStateDescription vmstate_imx25_ccm = {
+ .name = TYPE_IMX25_CCM,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(reg, IMX25CCMState, IMX25_CCM_MAX_REG),
+ VMSTATE_END_OF_LIST()
+ },
+};
+
+static uint32_t imx25_ccm_get_mpll_clk(IMXCCMState *dev)
+{
+ uint32_t freq;
+ IMX25CCMState *s = IMX25_CCM(dev);
+
+ if (EXTRACT(s->reg[IMX25_CCM_CCTL_REG], MPLL_BYPASS)) {
+ freq = CKIH_FREQ;
+ } else {
+ freq = imx_ccm_calc_pll(s->reg[IMX25_CCM_MPCTL_REG], CKIH_FREQ);
+ }
+
+ DPRINTF("freq = %d\n", freq);
+
+ return freq;
+}
+
+static uint32_t imx25_ccm_get_mcu_clk(IMXCCMState *dev)
+{
+ uint32_t freq;
+ IMX25CCMState *s = IMX25_CCM(dev);
+
+ freq = imx25_ccm_get_mpll_clk(dev);
+
+ if (EXTRACT(s->reg[IMX25_CCM_CCTL_REG], ARM_SRC)) {
+ freq = (freq * 3 / 4);
+ }
+
+ freq = freq / (1 + EXTRACT(s->reg[IMX25_CCM_CCTL_REG], ARM_CLK_DIV));
+
+ DPRINTF("freq = %d\n", freq);
+
+ return freq;
+}
+
+static uint32_t imx25_ccm_get_ahb_clk(IMXCCMState *dev)
+{
+ uint32_t freq;
+ IMX25CCMState *s = IMX25_CCM(dev);
+
+ freq = imx25_ccm_get_mcu_clk(dev)
+ / (1 + EXTRACT(s->reg[IMX25_CCM_CCTL_REG], AHB_CLK_DIV));
+
+ DPRINTF("freq = %d\n", freq);
+
+ return freq;
+}
+
+static uint32_t imx25_ccm_get_ipg_clk(IMXCCMState *dev)
+{
+ uint32_t freq;
+
+ freq = imx25_ccm_get_ahb_clk(dev) / 2;
+
+ DPRINTF("freq = %d\n", freq);
+
+ return freq;
+}
+
+static uint32_t imx25_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock)
+{
+ uint32_t freq = 0;
+ DPRINTF("Clock = %d)\n", clock);
+
+ switch (clock) {
+ case CLK_NONE:
+ break;
+ case CLK_IPG:
+ case CLK_IPG_HIGH:
+ freq = imx25_ccm_get_ipg_clk(dev);
+ break;
+ case CLK_32k:
+ freq = CKIL_FREQ;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: unsupported clock %d\n",
+ TYPE_IMX25_CCM, __func__, clock);
+ break;
+ }
+
+ DPRINTF("Clock = %d) = %d\n", clock, freq);
+
+ return freq;
+}
+
+static void imx25_ccm_reset(DeviceState *dev)
+{
+ IMX25CCMState *s = IMX25_CCM(dev);
+
+ DPRINTF("\n");
+
+ memset(s->reg, 0, IMX25_CCM_MAX_REG * sizeof(uint32_t));
+ s->reg[IMX25_CCM_MPCTL_REG] = 0x800b2c01;
+ s->reg[IMX25_CCM_UPCTL_REG] = 0x84042800;
+ /*
+ * The value below gives:
+ * CPU = 133 MHz, AHB = 66,5 MHz, IPG = 33 MHz.
+ */
+ s->reg[IMX25_CCM_CCTL_REG] = 0xd0030000;
+ s->reg[IMX25_CCM_CGCR0_REG] = 0x028A0100;
+ s->reg[IMX25_CCM_CGCR1_REG] = 0x04008100;
+ s->reg[IMX25_CCM_CGCR2_REG] = 0x00000438;
+ s->reg[IMX25_CCM_PCDR0_REG] = 0x01010101;
+ s->reg[IMX25_CCM_PCDR1_REG] = 0x01010101;
+ s->reg[IMX25_CCM_PCDR2_REG] = 0x01010101;
+ s->reg[IMX25_CCM_PCDR3_REG] = 0x01010101;
+ s->reg[IMX25_CCM_PMCR0_REG] = 0x00A00000;
+ s->reg[IMX25_CCM_PMCR1_REG] = 0x0000A030;
+ s->reg[IMX25_CCM_PMCR2_REG] = 0x0000A030;
+ s->reg[IMX25_CCM_MCR_REG] = 0x43000000;
+
+ /*
+ * default boot will change the reset values to allow:
+ * CPU = 399 MHz, AHB = 133 MHz, IPG = 66,5 MHz.
+ * For some reason, this doesn't work. With the value below, linux
+ * detects a 88 MHz IPG CLK instead of 66,5 MHz.
+ s->reg[IMX25_CCM_CCTL_REG] = 0x20032000;
+ */
+}
+
+static uint64_t imx25_ccm_read(void *opaque, hwaddr offset, unsigned size)
+{
+ uint32_t value = 0;
+ IMX25CCMState *s = (IMX25CCMState *)opaque;
+
+ if (offset < 0x70) {
+ value = s->reg[offset >> 2];
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
+ HWADDR_PRIx "\n", TYPE_IMX25_CCM, __func__, offset);
+ }
+
+ DPRINTF("reg[%s] => 0x%" PRIx32 "\n", imx25_ccm_reg_name(offset >> 2),
+ value);
+
+ return value;
+}
+
+static void imx25_ccm_write(void *opaque, hwaddr offset, uint64_t value,
+ unsigned size)
+{
+ IMX25CCMState *s = (IMX25CCMState *)opaque;
+
+ DPRINTF("reg[%s] <= 0x%" PRIx32 "\n", imx25_ccm_reg_name(offset >> 2),
+ (uint32_t)value);
+
+ if (offset < 0x70) {
+ /*
+ * We will do a better implementation later. In particular some bits
+ * cannot be written to.
+ */
+ s->reg[offset >> 2] = value;
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
+ HWADDR_PRIx "\n", TYPE_IMX25_CCM, __func__, offset);
+ }
+}
+
+static const struct MemoryRegionOps imx25_ccm_ops = {
+ .read = imx25_ccm_read,
+ .write = imx25_ccm_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ /*
+ * Our device would not work correctly if the guest was doing
+ * unaligned access. This might not be a limitation on the real
+ * device but in practice there is no reason for a guest to access
+ * this device unaligned.
+ */
+ .min_access_size = 4,
+ .max_access_size = 4,
+ .unaligned = false,
+ },
+};
+
+static void imx25_ccm_init(Object *obj)
+{
+ DeviceState *dev = DEVICE(obj);
+ SysBusDevice *sd = SYS_BUS_DEVICE(obj);
+ IMX25CCMState *s = IMX25_CCM(obj);
+
+ memory_region_init_io(&s->iomem, OBJECT(dev), &imx25_ccm_ops, s,
+ TYPE_IMX25_CCM, 0x1000);
+ sysbus_init_mmio(sd, &s->iomem);
+}
+
+static void imx25_ccm_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ IMXCCMClass *ccm = IMX_CCM_CLASS(klass);
+
+ dc->reset = imx25_ccm_reset;
+ dc->vmsd = &vmstate_imx25_ccm;
+ dc->desc = "i.MX25 Clock Control Module";
+
+ ccm->get_clock_frequency = imx25_ccm_get_clock_frequency;
+}
+
+static const TypeInfo imx25_ccm_info = {
+ .name = TYPE_IMX25_CCM,
+ .parent = TYPE_IMX_CCM,
+ .instance_size = sizeof(IMX25CCMState),
+ .instance_init = imx25_ccm_init,
+ .class_init = imx25_ccm_class_init,
+};
+
+static void imx25_ccm_register_types(void)
+{
+ type_register_static(&imx25_ccm_info);
+}
+
+type_init(imx25_ccm_register_types)
diff --git a/qemu/hw/misc/imx31_ccm.c b/qemu/hw/misc/imx31_ccm.c
new file mode 100644
index 000000000..80c164716
--- /dev/null
+++ b/qemu/hw/misc/imx31_ccm.c
@@ -0,0 +1,344 @@
+/*
+ * IMX31 Clock Control Module
+ *
+ * Copyright (C) 2012 NICTA
+ * Updated by Jean-Christophe Dubois <jcd@tribudubois.net>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ * To get the timer frequencies right, we need to emulate at least part of
+ * the i.MX31 CCM.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/misc/imx31_ccm.h"
+
+#define CKIH_FREQ 26000000 /* 26MHz crystal input */
+
+#ifndef DEBUG_IMX31_CCM
+#define DEBUG_IMX31_CCM 0
+#endif
+
+#define DPRINTF(fmt, args...) \
+ do { \
+ if (DEBUG_IMX31_CCM) { \
+ fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX31_CCM, \
+ __func__, ##args); \
+ } \
+ } while (0)
+
+static char const *imx31_ccm_reg_name(uint32_t reg)
+{
+ static char unknown[20];
+
+ switch (reg) {
+ case IMX31_CCM_CCMR_REG:
+ return "CCMR";
+ case IMX31_CCM_PDR0_REG:
+ return "PDR0";
+ case IMX31_CCM_PDR1_REG:
+ return "PDR1";
+ case IMX31_CCM_RCSR_REG:
+ return "RCSR";
+ case IMX31_CCM_MPCTL_REG:
+ return "MPCTL";
+ case IMX31_CCM_UPCTL_REG:
+ return "UPCTL";
+ case IMX31_CCM_SPCTL_REG:
+ return "SPCTL";
+ case IMX31_CCM_COSR_REG:
+ return "COSR";
+ case IMX31_CCM_CGR0_REG:
+ return "CGR0";
+ case IMX31_CCM_CGR1_REG:
+ return "CGR1";
+ case IMX31_CCM_CGR2_REG:
+ return "CGR2";
+ case IMX31_CCM_WIMR_REG:
+ return "WIMR";
+ case IMX31_CCM_LDC_REG:
+ return "LDC";
+ case IMX31_CCM_DCVR0_REG:
+ return "DCVR0";
+ case IMX31_CCM_DCVR1_REG:
+ return "DCVR1";
+ case IMX31_CCM_DCVR2_REG:
+ return "DCVR2";
+ case IMX31_CCM_DCVR3_REG:
+ return "DCVR3";
+ case IMX31_CCM_LTR0_REG:
+ return "LTR0";
+ case IMX31_CCM_LTR1_REG:
+ return "LTR1";
+ case IMX31_CCM_LTR2_REG:
+ return "LTR2";
+ case IMX31_CCM_LTR3_REG:
+ return "LTR3";
+ case IMX31_CCM_LTBR0_REG:
+ return "LTBR0";
+ case IMX31_CCM_LTBR1_REG:
+ return "LTBR1";
+ case IMX31_CCM_PMCR0_REG:
+ return "PMCR0";
+ case IMX31_CCM_PMCR1_REG:
+ return "PMCR1";
+ case IMX31_CCM_PDR2_REG:
+ return "PDR2";
+ default:
+ sprintf(unknown, "[%d ?]", reg);
+ return unknown;
+ }
+}
+
+static const VMStateDescription vmstate_imx31_ccm = {
+ .name = TYPE_IMX31_CCM,
+ .version_id = 2,
+ .minimum_version_id = 2,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(reg, IMX31CCMState, IMX31_CCM_MAX_REG),
+ VMSTATE_END_OF_LIST()
+ },
+};
+
+static uint32_t imx31_ccm_get_pll_ref_clk(IMXCCMState *dev)
+{
+ uint32_t freq = 0;
+ IMX31CCMState *s = IMX31_CCM(dev);
+
+ if ((s->reg[IMX31_CCM_CCMR_REG] & CCMR_PRCS) == 2) {
+ if (s->reg[IMX31_CCM_CCMR_REG] & CCMR_FPME) {
+ freq = CKIL_FREQ;
+ if (s->reg[IMX31_CCM_CCMR_REG] & CCMR_FPMF) {
+ freq *= 1024;
+ }
+ }
+ } else {
+ freq = CKIH_FREQ;
+ }
+
+ DPRINTF("freq = %d\n", freq);
+
+ return freq;
+}
+
+static uint32_t imx31_ccm_get_mpll_clk(IMXCCMState *dev)
+{
+ uint32_t freq;
+ IMX31CCMState *s = IMX31_CCM(dev);
+
+ freq = imx_ccm_calc_pll(s->reg[IMX31_CCM_MPCTL_REG],
+ imx31_ccm_get_pll_ref_clk(dev));
+
+ DPRINTF("freq = %d\n", freq);
+
+ return freq;
+}
+
+static uint32_t imx31_ccm_get_mcu_main_clk(IMXCCMState *dev)
+{
+ uint32_t freq;
+ IMX31CCMState *s = IMX31_CCM(dev);
+
+ if ((s->reg[IMX31_CCM_CCMR_REG] & CCMR_MDS) ||
+ !(s->reg[IMX31_CCM_CCMR_REG] & CCMR_MPE)) {
+ freq = imx31_ccm_get_pll_ref_clk(dev);
+ } else {
+ freq = imx31_ccm_get_mpll_clk(dev);
+ }
+
+ DPRINTF("freq = %d\n", freq);
+
+ return freq;
+}
+
+static uint32_t imx31_ccm_get_hclk_clk(IMXCCMState *dev)
+{
+ uint32_t freq;
+ IMX31CCMState *s = IMX31_CCM(dev);
+
+ freq = imx31_ccm_get_mcu_main_clk(dev)
+ / (1 + EXTRACT(s->reg[IMX31_CCM_PDR0_REG], MAX));
+
+ DPRINTF("freq = %d\n", freq);
+
+ return freq;
+}
+
+static uint32_t imx31_ccm_get_ipg_clk(IMXCCMState *dev)
+{
+ uint32_t freq;
+ IMX31CCMState *s = IMX31_CCM(dev);
+
+ freq = imx31_ccm_get_hclk_clk(dev)
+ / (1 + EXTRACT(s->reg[IMX31_CCM_PDR0_REG], IPG));
+
+ DPRINTF("freq = %d\n", freq);
+
+ return freq;
+}
+
+static uint32_t imx31_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock)
+{
+ uint32_t freq = 0;
+
+ switch (clock) {
+ case CLK_NONE:
+ break;
+ case CLK_IPG:
+ case CLK_IPG_HIGH:
+ freq = imx31_ccm_get_ipg_clk(dev);
+ break;
+ case CLK_32k:
+ freq = CKIL_FREQ;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: unsupported clock %d\n",
+ TYPE_IMX31_CCM, __func__, clock);
+ break;
+ }
+
+ DPRINTF("Clock = %d) = %d\n", clock, freq);
+
+ return freq;
+}
+
+static void imx31_ccm_reset(DeviceState *dev)
+{
+ IMX31CCMState *s = IMX31_CCM(dev);
+
+ DPRINTF("()\n");
+
+ memset(s->reg, 0, sizeof(uint32_t) * IMX31_CCM_MAX_REG);
+
+ s->reg[IMX31_CCM_CCMR_REG] = 0x074b0b7d;
+ s->reg[IMX31_CCM_PDR0_REG] = 0xff870b48;
+ s->reg[IMX31_CCM_PDR1_REG] = 0x49fcfe7f;
+ s->reg[IMX31_CCM_RCSR_REG] = 0x007f0000;
+ s->reg[IMX31_CCM_MPCTL_REG] = 0x04001800;
+ s->reg[IMX31_CCM_UPCTL_REG] = 0x04051c03;
+ s->reg[IMX31_CCM_SPCTL_REG] = 0x04043001;
+ s->reg[IMX31_CCM_COSR_REG] = 0x00000280;
+ s->reg[IMX31_CCM_CGR0_REG] = 0xffffffff;
+ s->reg[IMX31_CCM_CGR1_REG] = 0xffffffff;
+ s->reg[IMX31_CCM_CGR2_REG] = 0xffffffff;
+ s->reg[IMX31_CCM_WIMR_REG] = 0xffffffff;
+ s->reg[IMX31_CCM_LTR1_REG] = 0x00004040;
+ s->reg[IMX31_CCM_PMCR0_REG] = 0x80209828;
+ s->reg[IMX31_CCM_PMCR1_REG] = 0x00aa0000;
+ s->reg[IMX31_CCM_PDR2_REG] = 0x00000285;
+}
+
+static uint64_t imx31_ccm_read(void *opaque, hwaddr offset, unsigned size)
+{
+ uint32_t value = 0;
+ IMX31CCMState *s = (IMX31CCMState *)opaque;
+
+ if ((offset >> 2) < IMX31_CCM_MAX_REG) {
+ value = s->reg[offset >> 2];
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
+ HWADDR_PRIx "\n", TYPE_IMX31_CCM, __func__, offset);
+ }
+
+ DPRINTF("reg[%s] => 0x%" PRIx32 "\n", imx31_ccm_reg_name(offset >> 2),
+ value);
+
+ return (uint64_t)value;
+}
+
+static void imx31_ccm_write(void *opaque, hwaddr offset, uint64_t value,
+ unsigned size)
+{
+ IMX31CCMState *s = (IMX31CCMState *)opaque;
+
+ DPRINTF("reg[%s] <= 0x%" PRIx32 "\n", imx31_ccm_reg_name(offset >> 2),
+ (uint32_t)value);
+
+ switch (offset >> 2) {
+ case IMX31_CCM_CCMR_REG:
+ s->reg[IMX31_CCM_CCMR_REG] = CCMR_FPMF | (value & 0x3b6fdfff);
+ break;
+ case IMX31_CCM_PDR0_REG:
+ s->reg[IMX31_CCM_PDR0_REG] = value & 0xff9f3fff;
+ break;
+ case IMX31_CCM_PDR1_REG:
+ s->reg[IMX31_CCM_PDR1_REG] = value;
+ break;
+ case IMX31_CCM_MPCTL_REG:
+ s->reg[IMX31_CCM_MPCTL_REG] = value & 0xbfff3fff;
+ break;
+ case IMX31_CCM_SPCTL_REG:
+ s->reg[IMX31_CCM_SPCTL_REG] = value & 0xbfff3fff;
+ break;
+ case IMX31_CCM_CGR0_REG:
+ s->reg[IMX31_CCM_CGR0_REG] = value;
+ break;
+ case IMX31_CCM_CGR1_REG:
+ s->reg[IMX31_CCM_CGR1_REG] = value;
+ break;
+ case IMX31_CCM_CGR2_REG:
+ s->reg[IMX31_CCM_CGR2_REG] = value;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
+ HWADDR_PRIx "\n", TYPE_IMX31_CCM, __func__, offset);
+ break;
+ }
+}
+
+static const struct MemoryRegionOps imx31_ccm_ops = {
+ .read = imx31_ccm_read,
+ .write = imx31_ccm_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ /*
+ * Our device would not work correctly if the guest was doing
+ * unaligned access. This might not be a limitation on the real
+ * device but in practice there is no reason for a guest to access
+ * this device unaligned.
+ */
+ .min_access_size = 4,
+ .max_access_size = 4,
+ .unaligned = false,
+ },
+
+};
+
+static void imx31_ccm_init(Object *obj)
+{
+ DeviceState *dev = DEVICE(obj);
+ SysBusDevice *sd = SYS_BUS_DEVICE(obj);
+ IMX31CCMState *s = IMX31_CCM(obj);
+
+ memory_region_init_io(&s->iomem, OBJECT(dev), &imx31_ccm_ops, s,
+ TYPE_IMX31_CCM, 0x1000);
+ sysbus_init_mmio(sd, &s->iomem);
+}
+
+static void imx31_ccm_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ IMXCCMClass *ccm = IMX_CCM_CLASS(klass);
+
+ dc->reset = imx31_ccm_reset;
+ dc->vmsd = &vmstate_imx31_ccm;
+ dc->desc = "i.MX31 Clock Control Module";
+
+ ccm->get_clock_frequency = imx31_ccm_get_clock_frequency;
+}
+
+static const TypeInfo imx31_ccm_info = {
+ .name = TYPE_IMX31_CCM,
+ .parent = TYPE_IMX_CCM,
+ .instance_size = sizeof(IMX31CCMState),
+ .instance_init = imx31_ccm_init,
+ .class_init = imx31_ccm_class_init,
+};
+
+static void imx31_ccm_register_types(void)
+{
+ type_register_static(&imx31_ccm_info);
+}
+
+type_init(imx31_ccm_register_types)
diff --git a/qemu/hw/misc/imx6_ccm.c b/qemu/hw/misc/imx6_ccm.c
new file mode 100644
index 000000000..4e1d49da6
--- /dev/null
+++ b/qemu/hw/misc/imx6_ccm.c
@@ -0,0 +1,774 @@
+/*
+ * IMX6 Clock Control Module
+ *
+ * Copyright (c) 2015 Jean-Christophe Dubois <jcd@tribudubois.net>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ * To get the timer frequencies right, we need to emulate at least part of
+ * the CCM.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/misc/imx6_ccm.h"
+
+#ifndef DEBUG_IMX6_CCM
+#define DEBUG_IMX6_CCM 0
+#endif
+
+#define DPRINTF(fmt, args...) \
+ do { \
+ if (DEBUG_IMX6_CCM) { \
+ fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX6_CCM, \
+ __func__, ##args); \
+ } \
+ } while (0)
+
+static char const *imx6_ccm_reg_name(uint32_t reg)
+{
+ static char unknown[20];
+
+ switch (reg) {
+ case CCM_CCR:
+ return "CCR";
+ case CCM_CCDR:
+ return "CCDR";
+ case CCM_CSR:
+ return "CSR";
+ case CCM_CCSR:
+ return "CCSR";
+ case CCM_CACRR:
+ return "CACRR";
+ case CCM_CBCDR:
+ return "CBCDR";
+ case CCM_CBCMR:
+ return "CBCMR";
+ case CCM_CSCMR1:
+ return "CSCMR1";
+ case CCM_CSCMR2:
+ return "CSCMR2";
+ case CCM_CSCDR1:
+ return "CSCDR1";
+ case CCM_CS1CDR:
+ return "CS1CDR";
+ case CCM_CS2CDR:
+ return "CS2CDR";
+ case CCM_CDCDR:
+ return "CDCDR";
+ case CCM_CHSCCDR:
+ return "CHSCCDR";
+ case CCM_CSCDR2:
+ return "CSCDR2";
+ case CCM_CSCDR3:
+ return "CSCDR3";
+ case CCM_CDHIPR:
+ return "CDHIPR";
+ case CCM_CTOR:
+ return "CTOR";
+ case CCM_CLPCR:
+ return "CLPCR";
+ case CCM_CISR:
+ return "CISR";
+ case CCM_CIMR:
+ return "CIMR";
+ case CCM_CCOSR:
+ return "CCOSR";
+ case CCM_CGPR:
+ return "CGPR";
+ case CCM_CCGR0:
+ return "CCGR0";
+ case CCM_CCGR1:
+ return "CCGR1";
+ case CCM_CCGR2:
+ return "CCGR2";
+ case CCM_CCGR3:
+ return "CCGR3";
+ case CCM_CCGR4:
+ return "CCGR4";
+ case CCM_CCGR5:
+ return "CCGR5";
+ case CCM_CCGR6:
+ return "CCGR6";
+ case CCM_CMEOR:
+ return "CMEOR";
+ default:
+ sprintf(unknown, "%d ?", reg);
+ return unknown;
+ }
+}
+
+static char const *imx6_analog_reg_name(uint32_t reg)
+{
+ static char unknown[20];
+
+ switch (reg) {
+ case CCM_ANALOG_PLL_ARM:
+ return "PLL_ARM";
+ case CCM_ANALOG_PLL_ARM_SET:
+ return "PLL_ARM_SET";
+ case CCM_ANALOG_PLL_ARM_CLR:
+ return "PLL_ARM_CLR";
+ case CCM_ANALOG_PLL_ARM_TOG:
+ return "PLL_ARM_TOG";
+ case CCM_ANALOG_PLL_USB1:
+ return "PLL_USB1";
+ case CCM_ANALOG_PLL_USB1_SET:
+ return "PLL_USB1_SET";
+ case CCM_ANALOG_PLL_USB1_CLR:
+ return "PLL_USB1_CLR";
+ case CCM_ANALOG_PLL_USB1_TOG:
+ return "PLL_USB1_TOG";
+ case CCM_ANALOG_PLL_USB2:
+ return "PLL_USB2";
+ case CCM_ANALOG_PLL_USB2_SET:
+ return "PLL_USB2_SET";
+ case CCM_ANALOG_PLL_USB2_CLR:
+ return "PLL_USB2_CLR";
+ case CCM_ANALOG_PLL_USB2_TOG:
+ return "PLL_USB2_TOG";
+ case CCM_ANALOG_PLL_SYS:
+ return "PLL_SYS";
+ case CCM_ANALOG_PLL_SYS_SET:
+ return "PLL_SYS_SET";
+ case CCM_ANALOG_PLL_SYS_CLR:
+ return "PLL_SYS_CLR";
+ case CCM_ANALOG_PLL_SYS_TOG:
+ return "PLL_SYS_TOG";
+ case CCM_ANALOG_PLL_SYS_SS:
+ return "PLL_SYS_SS";
+ case CCM_ANALOG_PLL_SYS_NUM:
+ return "PLL_SYS_NUM";
+ case CCM_ANALOG_PLL_SYS_DENOM:
+ return "PLL_SYS_DENOM";
+ case CCM_ANALOG_PLL_AUDIO:
+ return "PLL_AUDIO";
+ case CCM_ANALOG_PLL_AUDIO_SET:
+ return "PLL_AUDIO_SET";
+ case CCM_ANALOG_PLL_AUDIO_CLR:
+ return "PLL_AUDIO_CLR";
+ case CCM_ANALOG_PLL_AUDIO_TOG:
+ return "PLL_AUDIO_TOG";
+ case CCM_ANALOG_PLL_AUDIO_NUM:
+ return "PLL_AUDIO_NUM";
+ case CCM_ANALOG_PLL_AUDIO_DENOM:
+ return "PLL_AUDIO_DENOM";
+ case CCM_ANALOG_PLL_VIDEO:
+ return "PLL_VIDEO";
+ case CCM_ANALOG_PLL_VIDEO_SET:
+ return "PLL_VIDEO_SET";
+ case CCM_ANALOG_PLL_VIDEO_CLR:
+ return "PLL_VIDEO_CLR";
+ case CCM_ANALOG_PLL_VIDEO_TOG:
+ return "PLL_VIDEO_TOG";
+ case CCM_ANALOG_PLL_VIDEO_NUM:
+ return "PLL_VIDEO_NUM";
+ case CCM_ANALOG_PLL_VIDEO_DENOM:
+ return "PLL_VIDEO_DENOM";
+ case CCM_ANALOG_PLL_MLB:
+ return "PLL_MLB";
+ case CCM_ANALOG_PLL_MLB_SET:
+ return "PLL_MLB_SET";
+ case CCM_ANALOG_PLL_MLB_CLR:
+ return "PLL_MLB_CLR";
+ case CCM_ANALOG_PLL_MLB_TOG:
+ return "PLL_MLB_TOG";
+ case CCM_ANALOG_PLL_ENET:
+ return "PLL_ENET";
+ case CCM_ANALOG_PLL_ENET_SET:
+ return "PLL_ENET_SET";
+ case CCM_ANALOG_PLL_ENET_CLR:
+ return "PLL_ENET_CLR";
+ case CCM_ANALOG_PLL_ENET_TOG:
+ return "PLL_ENET_TOG";
+ case CCM_ANALOG_PFD_480:
+ return "PFD_480";
+ case CCM_ANALOG_PFD_480_SET:
+ return "PFD_480_SET";
+ case CCM_ANALOG_PFD_480_CLR:
+ return "PFD_480_CLR";
+ case CCM_ANALOG_PFD_480_TOG:
+ return "PFD_480_TOG";
+ case CCM_ANALOG_PFD_528:
+ return "PFD_528";
+ case CCM_ANALOG_PFD_528_SET:
+ return "PFD_528_SET";
+ case CCM_ANALOG_PFD_528_CLR:
+ return "PFD_528_CLR";
+ case CCM_ANALOG_PFD_528_TOG:
+ return "PFD_528_TOG";
+ case CCM_ANALOG_MISC0:
+ return "MISC0";
+ case CCM_ANALOG_MISC0_SET:
+ return "MISC0_SET";
+ case CCM_ANALOG_MISC0_CLR:
+ return "MISC0_CLR";
+ case CCM_ANALOG_MISC0_TOG:
+ return "MISC0_TOG";
+ case CCM_ANALOG_MISC2:
+ return "MISC2";
+ case CCM_ANALOG_MISC2_SET:
+ return "MISC2_SET";
+ case CCM_ANALOG_MISC2_CLR:
+ return "MISC2_CLR";
+ case CCM_ANALOG_MISC2_TOG:
+ return "MISC2_TOG";
+ case PMU_REG_1P1:
+ return "PMU_REG_1P1";
+ case PMU_REG_3P0:
+ return "PMU_REG_3P0";
+ case PMU_REG_2P5:
+ return "PMU_REG_2P5";
+ case PMU_REG_CORE:
+ return "PMU_REG_CORE";
+ case PMU_MISC1:
+ return "PMU_MISC1";
+ case PMU_MISC1_SET:
+ return "PMU_MISC1_SET";
+ case PMU_MISC1_CLR:
+ return "PMU_MISC1_CLR";
+ case PMU_MISC1_TOG:
+ return "PMU_MISC1_TOG";
+ case USB_ANALOG_DIGPROG:
+ return "USB_ANALOG_DIGPROG";
+ default:
+ sprintf(unknown, "%d ?", reg);
+ return unknown;
+ }
+}
+
+#define CKIH_FREQ 24000000 /* 24MHz crystal input */
+
+static const VMStateDescription vmstate_imx6_ccm = {
+ .name = TYPE_IMX6_CCM,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(ccm, IMX6CCMState, CCM_MAX),
+ VMSTATE_UINT32_ARRAY(analog, IMX6CCMState, CCM_ANALOG_MAX),
+ VMSTATE_END_OF_LIST()
+ },
+};
+
+static uint64_t imx6_analog_get_pll2_clk(IMX6CCMState *dev)
+{
+ uint64_t freq = 24000000;
+
+ if (EXTRACT(dev->analog[CCM_ANALOG_PLL_SYS], DIV_SELECT)) {
+ freq *= 22;
+ } else {
+ freq *= 20;
+ }
+
+ DPRINTF("freq = %d\n", (uint32_t)freq);
+
+ return freq;
+}
+
+static uint64_t imx6_analog_get_pll2_pfd0_clk(IMX6CCMState *dev)
+{
+ uint64_t freq = 0;
+
+ freq = imx6_analog_get_pll2_clk(dev) * 18
+ / EXTRACT(dev->analog[CCM_ANALOG_PFD_528], PFD0_FRAC);
+
+ DPRINTF("freq = %d\n", (uint32_t)freq);
+
+ return freq;
+}
+
+static uint64_t imx6_analog_get_pll2_pfd2_clk(IMX6CCMState *dev)
+{
+ uint64_t freq = 0;
+
+ freq = imx6_analog_get_pll2_clk(dev) * 18
+ / EXTRACT(dev->analog[CCM_ANALOG_PFD_528], PFD2_FRAC);
+
+ DPRINTF("freq = %d\n", (uint32_t)freq);
+
+ return freq;
+}
+
+static uint64_t imx6_analog_get_periph_clk(IMX6CCMState *dev)
+{
+ uint64_t freq = 0;
+
+ switch (EXTRACT(dev->ccm[CCM_CBCMR], PRE_PERIPH_CLK_SEL)) {
+ case 0:
+ freq = imx6_analog_get_pll2_clk(dev);
+ break;
+ case 1:
+ freq = imx6_analog_get_pll2_pfd2_clk(dev);
+ break;
+ case 2:
+ freq = imx6_analog_get_pll2_pfd0_clk(dev);
+ break;
+ case 3:
+ freq = imx6_analog_get_pll2_pfd2_clk(dev) / 2;
+ break;
+ default:
+ /* We should never get there */
+ g_assert_not_reached();
+ break;
+ }
+
+ DPRINTF("freq = %d\n", (uint32_t)freq);
+
+ return freq;
+}
+
+static uint64_t imx6_ccm_get_ahb_clk(IMX6CCMState *dev)
+{
+ uint64_t freq = 0;
+
+ freq = imx6_analog_get_periph_clk(dev)
+ / (1 + EXTRACT(dev->ccm[CCM_CBCDR], AHB_PODF));
+
+ DPRINTF("freq = %d\n", (uint32_t)freq);
+
+ return freq;
+}
+
+static uint64_t imx6_ccm_get_ipg_clk(IMX6CCMState *dev)
+{
+ uint64_t freq = 0;
+
+ freq = imx6_ccm_get_ahb_clk(dev)
+ / (1 + EXTRACT(dev->ccm[CCM_CBCDR], IPG_PODF));;
+
+ DPRINTF("freq = %d\n", (uint32_t)freq);
+
+ return freq;
+}
+
+static uint64_t imx6_ccm_get_per_clk(IMX6CCMState *dev)
+{
+ uint64_t freq = 0;
+
+ freq = imx6_ccm_get_ipg_clk(dev)
+ / (1 + EXTRACT(dev->ccm[CCM_CSCMR1], PERCLK_PODF));
+
+ DPRINTF("freq = %d\n", (uint32_t)freq);
+
+ return freq;
+}
+
+static uint32_t imx6_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock)
+{
+ uint32_t freq = 0;
+ IMX6CCMState *s = IMX6_CCM(dev);
+
+ switch (clock) {
+ case CLK_NONE:
+ break;
+ case CLK_IPG:
+ freq = imx6_ccm_get_ipg_clk(s);
+ break;
+ case CLK_IPG_HIGH:
+ freq = imx6_ccm_get_per_clk(s);
+ break;
+ case CLK_32k:
+ freq = CKIL_FREQ;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: unsupported clock %d\n",
+ TYPE_IMX6_CCM, __func__, clock);
+ break;
+ }
+
+ DPRINTF("Clock = %d) = %d\n", clock, freq);
+
+ return freq;
+}
+
+static void imx6_ccm_reset(DeviceState *dev)
+{
+ IMX6CCMState *s = IMX6_CCM(dev);
+
+ DPRINTF("\n");
+
+ s->ccm[CCM_CCR] = 0x040116FF;
+ s->ccm[CCM_CCDR] = 0x00000000;
+ s->ccm[CCM_CSR] = 0x00000010;
+ s->ccm[CCM_CCSR] = 0x00000100;
+ s->ccm[CCM_CACRR] = 0x00000000;
+ s->ccm[CCM_CBCDR] = 0x00018D40;
+ s->ccm[CCM_CBCMR] = 0x00022324;
+ s->ccm[CCM_CSCMR1] = 0x00F00000;
+ s->ccm[CCM_CSCMR2] = 0x02B92F06;
+ s->ccm[CCM_CSCDR1] = 0x00490B00;
+ s->ccm[CCM_CS1CDR] = 0x0EC102C1;
+ s->ccm[CCM_CS2CDR] = 0x000736C1;
+ s->ccm[CCM_CDCDR] = 0x33F71F92;
+ s->ccm[CCM_CHSCCDR] = 0x0002A150;
+ s->ccm[CCM_CSCDR2] = 0x0002A150;
+ s->ccm[CCM_CSCDR3] = 0x00014841;
+ s->ccm[CCM_CDHIPR] = 0x00000000;
+ s->ccm[CCM_CTOR] = 0x00000000;
+ s->ccm[CCM_CLPCR] = 0x00000079;
+ s->ccm[CCM_CISR] = 0x00000000;
+ s->ccm[CCM_CIMR] = 0xFFFFFFFF;
+ s->ccm[CCM_CCOSR] = 0x000A0001;
+ s->ccm[CCM_CGPR] = 0x0000FE62;
+ s->ccm[CCM_CCGR0] = 0xFFFFFFFF;
+ s->ccm[CCM_CCGR1] = 0xFFFFFFFF;
+ s->ccm[CCM_CCGR2] = 0xFC3FFFFF;
+ s->ccm[CCM_CCGR3] = 0xFFFFFFFF;
+ s->ccm[CCM_CCGR4] = 0xFFFFFFFF;
+ s->ccm[CCM_CCGR5] = 0xFFFFFFFF;
+ s->ccm[CCM_CCGR6] = 0xFFFFFFFF;
+ s->ccm[CCM_CMEOR] = 0xFFFFFFFF;
+
+ s->analog[CCM_ANALOG_PLL_ARM] = 0x00013042;
+ s->analog[CCM_ANALOG_PLL_USB1] = 0x00012000;
+ s->analog[CCM_ANALOG_PLL_USB2] = 0x00012000;
+ s->analog[CCM_ANALOG_PLL_SYS] = 0x00013001;
+ s->analog[CCM_ANALOG_PLL_SYS_SS] = 0x00000000;
+ s->analog[CCM_ANALOG_PLL_SYS_NUM] = 0x00000000;
+ s->analog[CCM_ANALOG_PLL_SYS_DENOM] = 0x00000012;
+ s->analog[CCM_ANALOG_PLL_AUDIO] = 0x00011006;
+ s->analog[CCM_ANALOG_PLL_AUDIO_NUM] = 0x05F5E100;
+ s->analog[CCM_ANALOG_PLL_AUDIO_DENOM] = 0x2964619C;
+ s->analog[CCM_ANALOG_PLL_VIDEO] = 0x0001100C;
+ s->analog[CCM_ANALOG_PLL_VIDEO_NUM] = 0x05F5E100;
+ s->analog[CCM_ANALOG_PLL_VIDEO_DENOM] = 0x10A24447;
+ s->analog[CCM_ANALOG_PLL_MLB] = 0x00010000;
+ s->analog[CCM_ANALOG_PLL_ENET] = 0x00011001;
+ s->analog[CCM_ANALOG_PFD_480] = 0x1311100C;
+ s->analog[CCM_ANALOG_PFD_528] = 0x1018101B;
+
+ s->analog[PMU_REG_1P1] = 0x00001073;
+ s->analog[PMU_REG_3P0] = 0x00000F74;
+ s->analog[PMU_REG_2P5] = 0x00005071;
+ s->analog[PMU_REG_CORE] = 0x00402010;
+ s->analog[PMU_MISC0] = 0x04000000;
+ s->analog[PMU_MISC1] = 0x00000000;
+ s->analog[PMU_MISC2] = 0x00272727;
+
+ s->analog[USB_ANALOG_USB1_VBUS_DETECT] = 0x00000004;
+ s->analog[USB_ANALOG_USB1_CHRG_DETECT] = 0x00000000;
+ s->analog[USB_ANALOG_USB1_VBUS_DETECT_STAT] = 0x00000000;
+ s->analog[USB_ANALOG_USB1_CHRG_DETECT_STAT] = 0x00000000;
+ s->analog[USB_ANALOG_USB1_MISC] = 0x00000002;
+ s->analog[USB_ANALOG_USB2_VBUS_DETECT] = 0x00000004;
+ s->analog[USB_ANALOG_USB2_CHRG_DETECT] = 0x00000000;
+ s->analog[USB_ANALOG_USB2_MISC] = 0x00000002;
+ s->analog[USB_ANALOG_DIGPROG] = 0x00000000;
+
+ /* all PLLs need to be locked */
+ s->analog[CCM_ANALOG_PLL_ARM] |= CCM_ANALOG_PLL_LOCK;
+ s->analog[CCM_ANALOG_PLL_USB1] |= CCM_ANALOG_PLL_LOCK;
+ s->analog[CCM_ANALOG_PLL_USB2] |= CCM_ANALOG_PLL_LOCK;
+ s->analog[CCM_ANALOG_PLL_SYS] |= CCM_ANALOG_PLL_LOCK;
+ s->analog[CCM_ANALOG_PLL_AUDIO] |= CCM_ANALOG_PLL_LOCK;
+ s->analog[CCM_ANALOG_PLL_VIDEO] |= CCM_ANALOG_PLL_LOCK;
+ s->analog[CCM_ANALOG_PLL_MLB] |= CCM_ANALOG_PLL_LOCK;
+ s->analog[CCM_ANALOG_PLL_ENET] |= CCM_ANALOG_PLL_LOCK;
+}
+
+static uint64_t imx6_ccm_read(void *opaque, hwaddr offset, unsigned size)
+{
+ uint32_t value = 0;
+ uint32_t index = offset >> 2;
+ IMX6CCMState *s = (IMX6CCMState *)opaque;
+
+ value = s->ccm[index];
+
+ DPRINTF("reg[%s] => 0x%" PRIx32 "\n", imx6_ccm_reg_name(index), value);
+
+ return (uint64_t)value;
+}
+
+static void imx6_ccm_write(void *opaque, hwaddr offset, uint64_t value,
+ unsigned size)
+{
+ uint32_t index = offset >> 2;
+ IMX6CCMState *s = (IMX6CCMState *)opaque;
+
+ DPRINTF("reg[%s] <= 0x%" PRIx32 "\n", imx6_ccm_reg_name(index),
+ (uint32_t)value);
+
+ /*
+ * We will do a better implementation later. In particular some bits
+ * cannot be written to.
+ */
+ s->ccm[index] = (uint32_t)value;
+}
+
+static uint64_t imx6_analog_read(void *opaque, hwaddr offset, unsigned size)
+{
+ uint32_t value;
+ uint32_t index = offset >> 2;
+ IMX6CCMState *s = (IMX6CCMState *)opaque;
+
+ switch (index) {
+ case CCM_ANALOG_PLL_ARM_SET:
+ case CCM_ANALOG_PLL_USB1_SET:
+ case CCM_ANALOG_PLL_USB2_SET:
+ case CCM_ANALOG_PLL_SYS_SET:
+ case CCM_ANALOG_PLL_AUDIO_SET:
+ case CCM_ANALOG_PLL_VIDEO_SET:
+ case CCM_ANALOG_PLL_MLB_SET:
+ case CCM_ANALOG_PLL_ENET_SET:
+ case CCM_ANALOG_PFD_480_SET:
+ case CCM_ANALOG_PFD_528_SET:
+ case CCM_ANALOG_MISC0_SET:
+ case PMU_MISC1_SET:
+ case CCM_ANALOG_MISC2_SET:
+ case USB_ANALOG_USB1_VBUS_DETECT_SET:
+ case USB_ANALOG_USB1_CHRG_DETECT_SET:
+ case USB_ANALOG_USB1_MISC_SET:
+ case USB_ANALOG_USB2_VBUS_DETECT_SET:
+ case USB_ANALOG_USB2_CHRG_DETECT_SET:
+ case USB_ANALOG_USB2_MISC_SET:
+ /*
+ * All REG_NAME_SET register access are in fact targeting the
+ * the REG_NAME register.
+ */
+ value = s->analog[index - 1];
+ break;
+ case CCM_ANALOG_PLL_ARM_CLR:
+ case CCM_ANALOG_PLL_USB1_CLR:
+ case CCM_ANALOG_PLL_USB2_CLR:
+ case CCM_ANALOG_PLL_SYS_CLR:
+ case CCM_ANALOG_PLL_AUDIO_CLR:
+ case CCM_ANALOG_PLL_VIDEO_CLR:
+ case CCM_ANALOG_PLL_MLB_CLR:
+ case CCM_ANALOG_PLL_ENET_CLR:
+ case CCM_ANALOG_PFD_480_CLR:
+ case CCM_ANALOG_PFD_528_CLR:
+ case CCM_ANALOG_MISC0_CLR:
+ case PMU_MISC1_CLR:
+ case CCM_ANALOG_MISC2_CLR:
+ case USB_ANALOG_USB1_VBUS_DETECT_CLR:
+ case USB_ANALOG_USB1_CHRG_DETECT_CLR:
+ case USB_ANALOG_USB1_MISC_CLR:
+ case USB_ANALOG_USB2_VBUS_DETECT_CLR:
+ case USB_ANALOG_USB2_CHRG_DETECT_CLR:
+ case USB_ANALOG_USB2_MISC_CLR:
+ /*
+ * All REG_NAME_CLR register access are in fact targeting the
+ * the REG_NAME register.
+ */
+ value = s->analog[index - 2];
+ break;
+ case CCM_ANALOG_PLL_ARM_TOG:
+ case CCM_ANALOG_PLL_USB1_TOG:
+ case CCM_ANALOG_PLL_USB2_TOG:
+ case CCM_ANALOG_PLL_SYS_TOG:
+ case CCM_ANALOG_PLL_AUDIO_TOG:
+ case CCM_ANALOG_PLL_VIDEO_TOG:
+ case CCM_ANALOG_PLL_MLB_TOG:
+ case CCM_ANALOG_PLL_ENET_TOG:
+ case CCM_ANALOG_PFD_480_TOG:
+ case CCM_ANALOG_PFD_528_TOG:
+ case CCM_ANALOG_MISC0_TOG:
+ case PMU_MISC1_TOG:
+ case CCM_ANALOG_MISC2_TOG:
+ case USB_ANALOG_USB1_VBUS_DETECT_TOG:
+ case USB_ANALOG_USB1_CHRG_DETECT_TOG:
+ case USB_ANALOG_USB1_MISC_TOG:
+ case USB_ANALOG_USB2_VBUS_DETECT_TOG:
+ case USB_ANALOG_USB2_CHRG_DETECT_TOG:
+ case USB_ANALOG_USB2_MISC_TOG:
+ /*
+ * All REG_NAME_TOG register access are in fact targeting the
+ * the REG_NAME register.
+ */
+ value = s->analog[index - 3];
+ break;
+ default:
+ value = s->analog[index];
+ break;
+ }
+
+ DPRINTF("reg[%s] => 0x%" PRIx32 "\n", imx6_analog_reg_name(index), value);
+
+ return (uint64_t)value;
+}
+
+static void imx6_analog_write(void *opaque, hwaddr offset, uint64_t value,
+ unsigned size)
+{
+ uint32_t index = offset >> 2;
+ IMX6CCMState *s = (IMX6CCMState *)opaque;
+
+ DPRINTF("reg[%s] <= 0x%" PRIx32 "\n", imx6_analog_reg_name(index),
+ (uint32_t)value);
+
+ switch (index) {
+ case CCM_ANALOG_PLL_ARM_SET:
+ case CCM_ANALOG_PLL_USB1_SET:
+ case CCM_ANALOG_PLL_USB2_SET:
+ case CCM_ANALOG_PLL_SYS_SET:
+ case CCM_ANALOG_PLL_AUDIO_SET:
+ case CCM_ANALOG_PLL_VIDEO_SET:
+ case CCM_ANALOG_PLL_MLB_SET:
+ case CCM_ANALOG_PLL_ENET_SET:
+ case CCM_ANALOG_PFD_480_SET:
+ case CCM_ANALOG_PFD_528_SET:
+ case CCM_ANALOG_MISC0_SET:
+ case PMU_MISC1_SET:
+ case CCM_ANALOG_MISC2_SET:
+ case USB_ANALOG_USB1_VBUS_DETECT_SET:
+ case USB_ANALOG_USB1_CHRG_DETECT_SET:
+ case USB_ANALOG_USB1_MISC_SET:
+ case USB_ANALOG_USB2_VBUS_DETECT_SET:
+ case USB_ANALOG_USB2_CHRG_DETECT_SET:
+ case USB_ANALOG_USB2_MISC_SET:
+ /*
+ * All REG_NAME_SET register access are in fact targeting the
+ * the REG_NAME register. So we change the value of the
+ * REG_NAME register, setting bits passed in the value.
+ */
+ s->analog[index - 1] |= value;
+ break;
+ case CCM_ANALOG_PLL_ARM_CLR:
+ case CCM_ANALOG_PLL_USB1_CLR:
+ case CCM_ANALOG_PLL_USB2_CLR:
+ case CCM_ANALOG_PLL_SYS_CLR:
+ case CCM_ANALOG_PLL_AUDIO_CLR:
+ case CCM_ANALOG_PLL_VIDEO_CLR:
+ case CCM_ANALOG_PLL_MLB_CLR:
+ case CCM_ANALOG_PLL_ENET_CLR:
+ case CCM_ANALOG_PFD_480_CLR:
+ case CCM_ANALOG_PFD_528_CLR:
+ case CCM_ANALOG_MISC0_CLR:
+ case PMU_MISC1_CLR:
+ case CCM_ANALOG_MISC2_CLR:
+ case USB_ANALOG_USB1_VBUS_DETECT_CLR:
+ case USB_ANALOG_USB1_CHRG_DETECT_CLR:
+ case USB_ANALOG_USB1_MISC_CLR:
+ case USB_ANALOG_USB2_VBUS_DETECT_CLR:
+ case USB_ANALOG_USB2_CHRG_DETECT_CLR:
+ case USB_ANALOG_USB2_MISC_CLR:
+ /*
+ * All REG_NAME_CLR register access are in fact targeting the
+ * the REG_NAME register. So we change the value of the
+ * REG_NAME register, unsetting bits passed in the value.
+ */
+ s->analog[index - 2] &= ~value;
+ break;
+ case CCM_ANALOG_PLL_ARM_TOG:
+ case CCM_ANALOG_PLL_USB1_TOG:
+ case CCM_ANALOG_PLL_USB2_TOG:
+ case CCM_ANALOG_PLL_SYS_TOG:
+ case CCM_ANALOG_PLL_AUDIO_TOG:
+ case CCM_ANALOG_PLL_VIDEO_TOG:
+ case CCM_ANALOG_PLL_MLB_TOG:
+ case CCM_ANALOG_PLL_ENET_TOG:
+ case CCM_ANALOG_PFD_480_TOG:
+ case CCM_ANALOG_PFD_528_TOG:
+ case CCM_ANALOG_MISC0_TOG:
+ case PMU_MISC1_TOG:
+ case CCM_ANALOG_MISC2_TOG:
+ case USB_ANALOG_USB1_VBUS_DETECT_TOG:
+ case USB_ANALOG_USB1_CHRG_DETECT_TOG:
+ case USB_ANALOG_USB1_MISC_TOG:
+ case USB_ANALOG_USB2_VBUS_DETECT_TOG:
+ case USB_ANALOG_USB2_CHRG_DETECT_TOG:
+ case USB_ANALOG_USB2_MISC_TOG:
+ /*
+ * All REG_NAME_TOG register access are in fact targeting the
+ * the REG_NAME register. So we change the value of the
+ * REG_NAME register, toggling bits passed in the value.
+ */
+ s->analog[index - 3] ^= value;
+ break;
+ default:
+ /*
+ * We will do a better implementation later. In particular some bits
+ * cannot be written to.
+ */
+ s->analog[index] = value;
+ break;
+ }
+}
+
+static const struct MemoryRegionOps imx6_ccm_ops = {
+ .read = imx6_ccm_read,
+ .write = imx6_ccm_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ /*
+ * Our device would not work correctly if the guest was doing
+ * unaligned access. This might not be a limitation on the real
+ * device but in practice there is no reason for a guest to access
+ * this device unaligned.
+ */
+ .min_access_size = 4,
+ .max_access_size = 4,
+ .unaligned = false,
+ },
+};
+
+static const struct MemoryRegionOps imx6_analog_ops = {
+ .read = imx6_analog_read,
+ .write = imx6_analog_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ /*
+ * Our device would not work correctly if the guest was doing
+ * unaligned access. This might not be a limitation on the real
+ * device but in practice there is no reason for a guest to access
+ * this device unaligned.
+ */
+ .min_access_size = 4,
+ .max_access_size = 4,
+ .unaligned = false,
+ },
+};
+
+static void imx6_ccm_init(Object *obj)
+{
+ DeviceState *dev = DEVICE(obj);
+ SysBusDevice *sd = SYS_BUS_DEVICE(obj);
+ IMX6CCMState *s = IMX6_CCM(obj);
+
+ /* initialize a container for the all memory range */
+ memory_region_init(&s->container, OBJECT(dev), TYPE_IMX6_CCM, 0x5000);
+
+ /* We initialize an IO memory region for the CCM part */
+ memory_region_init_io(&s->ioccm, OBJECT(dev), &imx6_ccm_ops, s,
+ TYPE_IMX6_CCM ".ccm", CCM_MAX * sizeof(uint32_t));
+
+ /* Add the CCM as a subregion at offset 0 */
+ memory_region_add_subregion(&s->container, 0, &s->ioccm);
+
+ /* We initialize an IO memory region for the ANALOG part */
+ memory_region_init_io(&s->ioanalog, OBJECT(dev), &imx6_analog_ops, s,
+ TYPE_IMX6_CCM ".analog",
+ CCM_ANALOG_MAX * sizeof(uint32_t));
+
+ /* Add the ANALOG as a subregion at offset 0x4000 */
+ memory_region_add_subregion(&s->container, 0x4000, &s->ioanalog);
+
+ sysbus_init_mmio(sd, &s->container);
+}
+
+static void imx6_ccm_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ IMXCCMClass *ccm = IMX_CCM_CLASS(klass);
+
+ dc->reset = imx6_ccm_reset;
+ dc->vmsd = &vmstate_imx6_ccm;
+ dc->desc = "i.MX6 Clock Control Module";
+
+ ccm->get_clock_frequency = imx6_ccm_get_clock_frequency;
+}
+
+static const TypeInfo imx6_ccm_info = {
+ .name = TYPE_IMX6_CCM,
+ .parent = TYPE_IMX_CCM,
+ .instance_size = sizeof(IMX6CCMState),
+ .instance_init = imx6_ccm_init,
+ .class_init = imx6_ccm_class_init,
+};
+
+static void imx6_ccm_register_types(void)
+{
+ type_register_static(&imx6_ccm_info);
+}
+
+type_init(imx6_ccm_register_types)
diff --git a/qemu/hw/misc/imx_ccm.c b/qemu/hw/misc/imx_ccm.c
index 092028863..986d890ca 100644
--- a/qemu/hw/misc/imx_ccm.c
+++ b/qemu/hw/misc/imx_ccm.c
@@ -2,138 +2,51 @@
* IMX31 Clock Control Module
*
* Copyright (C) 2012 NICTA
+ * Updated by Jean-Christophe Dubois <jcd@tribudubois.net>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*
- * To get the timer frequencies right, we need to emulate at least part of
- * the CCM.
+ * This is an abstract base class used to get a common interface to
+ * retrieve the CCM frequencies from the various i.MX SOC.
*/
-#include "hw/hw.h"
-#include "hw/sysbus.h"
-#include "sysemu/sysemu.h"
-#include "hw/arm/imx.h"
+#include "qemu/osdep.h"
+#include "hw/misc/imx_ccm.h"
-#define CKIH_FREQ 26000000 /* 26MHz crystal input */
-#define CKIL_FREQ 32768 /* nominal 32khz clock */
-
-
-//#define DEBUG_CCM 1
-#ifdef DEBUG_CCM
-#define DPRINTF(fmt, args...) \
-do { printf("imx_ccm: " fmt , ##args); } while (0)
-#else
-#define DPRINTF(fmt, args...) do {} while (0)
+#ifndef DEBUG_IMX_CCM
+#define DEBUG_IMX_CCM 0
#endif
-static int imx_ccm_post_load(void *opaque, int version_id);
-
-#define TYPE_IMX_CCM "imx_ccm"
-#define IMX_CCM(obj) OBJECT_CHECK(IMXCCMState, (obj), TYPE_IMX_CCM)
-
-typedef struct IMXCCMState {
- SysBusDevice parent_obj;
-
- MemoryRegion iomem;
-
- uint32_t ccmr;
- uint32_t pdr0;
- uint32_t pdr1;
- uint32_t mpctl;
- uint32_t spctl;
- uint32_t cgr[3];
- uint32_t pmcr0;
- uint32_t pmcr1;
-
- /* Frequencies precalculated on register changes */
- uint32_t pll_refclk_freq;
- uint32_t mcu_clk_freq;
- uint32_t hsp_clk_freq;
- uint32_t ipg_clk_freq;
-} IMXCCMState;
-
-static const VMStateDescription vmstate_imx_ccm = {
- .name = "imx-ccm",
- .version_id = 1,
- .minimum_version_id = 1,
- .fields = (VMStateField[]) {
- VMSTATE_UINT32(ccmr, IMXCCMState),
- VMSTATE_UINT32(pdr0, IMXCCMState),
- VMSTATE_UINT32(pdr1, IMXCCMState),
- VMSTATE_UINT32(mpctl, IMXCCMState),
- VMSTATE_UINT32(spctl, IMXCCMState),
- VMSTATE_UINT32_ARRAY(cgr, IMXCCMState, 3),
- VMSTATE_UINT32(pmcr0, IMXCCMState),
- VMSTATE_UINT32(pmcr1, IMXCCMState),
- VMSTATE_UINT32(pll_refclk_freq, IMXCCMState),
- VMSTATE_END_OF_LIST()
- },
- .post_load = imx_ccm_post_load,
-};
-
-/* CCMR */
-#define CCMR_FPME (1<<0)
-#define CCMR_MPE (1<<3)
-#define CCMR_MDS (1<<7)
-#define CCMR_FPMF (1<<26)
-#define CCMR_PRCS (3<<1)
-
-/* PDR0 */
-#define PDR0_MCU_PODF_SHIFT (0)
-#define PDR0_MCU_PODF_MASK (0x7)
-#define PDR0_MAX_PODF_SHIFT (3)
-#define PDR0_MAX_PODF_MASK (0x7)
-#define PDR0_IPG_PODF_SHIFT (6)
-#define PDR0_IPG_PODF_MASK (0x3)
-#define PDR0_NFC_PODF_SHIFT (8)
-#define PDR0_NFC_PODF_MASK (0x7)
-#define PDR0_HSP_PODF_SHIFT (11)
-#define PDR0_HSP_PODF_MASK (0x7)
-#define PDR0_PER_PODF_SHIFT (16)
-#define PDR0_PER_PODF_MASK (0x1f)
-#define PDR0_CSI_PODF_SHIFT (23)
-#define PDR0_CSI_PODF_MASK (0x1ff)
-
-#define EXTRACT(value, name) (((value) >> PDR0_##name##_PODF_SHIFT) \
- & PDR0_##name##_PODF_MASK)
-#define INSERT(value, name) (((value) & PDR0_##name##_PODF_MASK) << \
- PDR0_##name##_PODF_SHIFT)
-/* PLL control registers */
-#define PD(v) (((v) >> 26) & 0xf)
-#define MFD(v) (((v) >> 16) & 0x3ff)
-#define MFI(v) (((v) >> 10) & 0xf);
-#define MFN(v) ((v) & 0x3ff)
+#define DPRINTF(fmt, args...) \
+ do { \
+ if (DEBUG_IMX_CCM) { \
+ fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX_CCM, \
+ __func__, ##args); \
+ } \
+ } while (0)
-#define PLL_PD(x) (((x) & 0xf) << 26)
-#define PLL_MFD(x) (((x) & 0x3ff) << 16)
-#define PLL_MFI(x) (((x) & 0xf) << 10)
-#define PLL_MFN(x) (((x) & 0x3ff) << 0)
-uint32_t imx_clock_frequency(DeviceState *dev, IMXClk clock)
+uint32_t imx_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock)
{
- IMXCCMState *s = IMX_CCM(dev);
+ uint32_t freq = 0;
+ IMXCCMClass *klass = IMX_GET_CLASS(dev);
- switch (clock) {
- case NOCLK:
- return 0;
- case MCU:
- return s->mcu_clk_freq;
- case HSP:
- return s->hsp_clk_freq;
- case IPG:
- return s->ipg_clk_freq;
- case CLK_32k:
- return CKIL_FREQ;
+ if (klass->get_clock_frequency) {
+ freq = klass->get_clock_frequency(dev, clock);
}
- return 0;
+
+ DPRINTF("(clock = %d) = %d\n", clock, freq);
+
+ return freq;
}
/*
* Calculate PLL output frequency
*/
-static uint32_t calc_pll(uint32_t pllreg, uint32_t base_freq)
+uint32_t imx_ccm_calc_pll(uint32_t pllreg, uint32_t base_freq)
{
+ int32_t freq;
int32_t mfn = MFN(pllreg); /* Numerator */
uint32_t mfi = MFI(pllreg); /* Integer part */
uint32_t mfd = 1 + MFD(pllreg); /* Denominator */
@@ -142,180 +55,26 @@ static uint32_t calc_pll(uint32_t pllreg, uint32_t base_freq)
if (mfi < 5) {
mfi = 5;
}
+
/* mfn is 10-bit signed twos-complement */
mfn <<= 32 - 10;
mfn >>= 32 - 10;
- return ((2 * (base_freq >> 10) * (mfi * mfd + mfn)) /
+ freq = ((2 * (base_freq >> 10) * (mfi * mfd + mfn)) /
(mfd * pd)) << 10;
-}
-
-static void update_clocks(IMXCCMState *s)
-{
- /*
- * If we ever emulate more clocks, this should switch to a data-driven
- * approach
- */
-
- if ((s->ccmr & CCMR_PRCS) == 2) {
- s->pll_refclk_freq = CKIL_FREQ * 1024;
- } else {
- s->pll_refclk_freq = CKIH_FREQ;
- }
-
- /* ipg_clk_arm aka MCU clock */
- if ((s->ccmr & CCMR_MDS) || !(s->ccmr & CCMR_MPE)) {
- s->mcu_clk_freq = s->pll_refclk_freq;
- } else {
- s->mcu_clk_freq = calc_pll(s->mpctl, s->pll_refclk_freq);
- }
-
- /* High-speed clock */
- s->hsp_clk_freq = s->mcu_clk_freq / (1 + EXTRACT(s->pdr0, HSP));
- s->ipg_clk_freq = s->hsp_clk_freq / (1 + EXTRACT(s->pdr0, IPG));
- DPRINTF("Clocks: mcu %uMHz, HSP %uMHz, IPG %uHz\n",
- s->mcu_clk_freq / 1000000,
- s->hsp_clk_freq / 1000000,
- s->ipg_clk_freq);
-}
-
-static void imx_ccm_reset(DeviceState *dev)
-{
- IMXCCMState *s = IMX_CCM(dev);
-
- s->ccmr = 0x074b0b7b;
- s->pdr0 = 0xff870b48;
- s->pdr1 = 0x49fcfe7f;
- s->mpctl = PLL_PD(1) | PLL_MFD(0) | PLL_MFI(6) | PLL_MFN(0);
- s->cgr[0] = s->cgr[1] = s->cgr[2] = 0xffffffff;
- s->spctl = PLL_PD(1) | PLL_MFD(4) | PLL_MFI(0xc) | PLL_MFN(1);
- s->pmcr0 = 0x80209828;
-
- update_clocks(s);
-}
-
-static uint64_t imx_ccm_read(void *opaque, hwaddr offset,
- unsigned size)
-{
- IMXCCMState *s = (IMXCCMState *)opaque;
-
- DPRINTF("read(offset=%x)", offset >> 2);
- switch (offset >> 2) {
- case 0: /* CCMR */
- DPRINTF(" ccmr = 0x%x\n", s->ccmr);
- return s->ccmr;
- case 1:
- DPRINTF(" pdr0 = 0x%x\n", s->pdr0);
- return s->pdr0;
- case 2:
- DPRINTF(" pdr1 = 0x%x\n", s->pdr1);
- return s->pdr1;
- case 4:
- DPRINTF(" mpctl = 0x%x\n", s->mpctl);
- return s->mpctl;
- case 6:
- DPRINTF(" spctl = 0x%x\n", s->spctl);
- return s->spctl;
- case 8:
- DPRINTF(" cgr0 = 0x%x\n", s->cgr[0]);
- return s->cgr[0];
- case 9:
- DPRINTF(" cgr1 = 0x%x\n", s->cgr[1]);
- return s->cgr[1];
- case 10:
- DPRINTF(" cgr2 = 0x%x\n", s->cgr[2]);
- return s->cgr[2];
- case 18: /* LTR1 */
- return 0x00004040;
- case 23:
- DPRINTF(" pcmr0 = 0x%x\n", s->pmcr0);
- return s->pmcr0;
- }
- DPRINTF(" return 0\n");
- return 0;
-}
-
-static void imx_ccm_write(void *opaque, hwaddr offset,
- uint64_t value, unsigned size)
-{
- IMXCCMState *s = (IMXCCMState *)opaque;
-
- DPRINTF("write(offset=%x, value = %x)\n",
- offset >> 2, (unsigned int)value);
- switch (offset >> 2) {
- case 0:
- s->ccmr = CCMR_FPMF | (value & 0x3b6fdfff);
- break;
- case 1:
- s->pdr0 = value & 0xff9f3fff;
- break;
- case 2:
- s->pdr1 = value;
- break;
- case 4:
- s->mpctl = value & 0xbfff3fff;
- break;
- case 6:
- s->spctl = value & 0xbfff3fff;
- break;
- case 8:
- s->cgr[0] = value;
- return;
- case 9:
- s->cgr[1] = value;
- return;
- case 10:
- s->cgr[2] = value;
- return;
-
- default:
- return;
- }
- update_clocks(s);
-}
-
-static const struct MemoryRegionOps imx_ccm_ops = {
- .read = imx_ccm_read,
- .write = imx_ccm_write,
- .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static int imx_ccm_init(SysBusDevice *dev)
-{
- IMXCCMState *s = IMX_CCM(dev);
-
- memory_region_init_io(&s->iomem, OBJECT(dev), &imx_ccm_ops, s,
- "imx_ccm", 0x1000);
- sysbus_init_mmio(dev, &s->iomem);
-
- return 0;
-}
-
-static int imx_ccm_post_load(void *opaque, int version_id)
-{
- IMXCCMState *s = (IMXCCMState *)opaque;
-
- update_clocks(s);
- return 0;
-}
-
-static void imx_ccm_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass);
+ DPRINTF("(pllreg = 0x%08x, base_freq = %d) = %d\n", pllreg, base_freq,
+ freq);
- sbc->init = imx_ccm_init;
- dc->reset = imx_ccm_reset;
- dc->vmsd = &vmstate_imx_ccm;
- dc->desc = "i.MX Clock Control Module";
+ return freq;
}
static const TypeInfo imx_ccm_info = {
- .name = TYPE_IMX_CCM,
- .parent = TYPE_SYS_BUS_DEVICE,
+ .name = TYPE_IMX_CCM,
+ .parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(IMXCCMState),
- .class_init = imx_ccm_class_init,
+ .class_size = sizeof(IMXCCMClass),
+ .abstract = true,
};
static void imx_ccm_register_types(void)
diff --git a/qemu/hw/misc/ivshmem.c b/qemu/hw/misc/ivshmem.c
index cc76989a3..e40f23bfc 100644
--- a/qemu/hw/misc/ivshmem.c
+++ b/qemu/hw/misc/ivshmem.c
@@ -16,39 +16,57 @@
* Contributions after 2012-01-13 are licensed under the terms of the
* GNU GPL, version 2 or (at your option) any later version.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/cutils.h"
#include "hw/hw.h"
#include "hw/i386/pc.h"
#include "hw/pci/pci.h"
+#include "hw/pci/msi.h"
#include "hw/pci/msix.h"
#include "sysemu/kvm.h"
#include "migration/migration.h"
#include "qemu/error-report.h"
#include "qemu/event_notifier.h"
-#include "qemu/fifo8.h"
+#include "qom/object_interfaces.h"
#include "sysemu/char.h"
+#include "sysemu/hostmem.h"
+#include "sysemu/qtest.h"
+#include "qapi/visitor.h"
+#include "exec/ram_addr.h"
+
+#include "hw/misc/ivshmem.h"
#include <sys/mman.h>
-#include <sys/types.h>
-#include <limits.h>
#define PCI_VENDOR_ID_IVSHMEM PCI_VENDOR_ID_REDHAT_QUMRANET
#define PCI_DEVICE_ID_IVSHMEM 0x1110
+#define IVSHMEM_MAX_PEERS UINT16_MAX
#define IVSHMEM_IOEVENTFD 0
#define IVSHMEM_MSI 1
-#define IVSHMEM_PEER 0
-#define IVSHMEM_MASTER 1
-
#define IVSHMEM_REG_BAR_SIZE 0x100
-//#define DEBUG_IVSHMEM
-#ifdef DEBUG_IVSHMEM
-#define IVSHMEM_DPRINTF(fmt, ...) \
- do {printf("IVSHMEM: " fmt, ## __VA_ARGS__); } while (0)
-#else
-#define IVSHMEM_DPRINTF(fmt, ...)
-#endif
+#define IVSHMEM_DEBUG 0
+#define IVSHMEM_DPRINTF(fmt, ...) \
+ do { \
+ if (IVSHMEM_DEBUG) { \
+ printf("IVSHMEM: " fmt, ## __VA_ARGS__); \
+ } \
+ } while (0)
+
+#define TYPE_IVSHMEM_COMMON "ivshmem-common"
+#define IVSHMEM_COMMON(obj) \
+ OBJECT_CHECK(IVShmemState, (obj), TYPE_IVSHMEM_COMMON)
+
+#define TYPE_IVSHMEM_PLAIN "ivshmem-plain"
+#define IVSHMEM_PLAIN(obj) \
+ OBJECT_CHECK(IVShmemState, (obj), TYPE_IVSHMEM_PLAIN)
+
+#define TYPE_IVSHMEM_DOORBELL "ivshmem-doorbell"
+#define IVSHMEM_DOORBELL(obj) \
+ OBJECT_CHECK(IVShmemState, (obj), TYPE_IVSHMEM_DOORBELL)
#define TYPE_IVSHMEM "ivshmem"
#define IVSHMEM(obj) \
@@ -59,51 +77,50 @@ typedef struct Peer {
EventNotifier *eventfds;
} Peer;
-typedef struct EventfdEntry {
+typedef struct MSIVector {
PCIDevice *pdev;
- int vector;
-} EventfdEntry;
+ int virq;
+} MSIVector;
typedef struct IVShmemState {
/*< private >*/
PCIDevice parent_obj;
/*< public >*/
+ uint32_t features;
+
+ /* exactly one of these two may be set */
+ HostMemoryBackend *hostmem; /* with interrupts */
+ CharDriverState *server_chr; /* without interrupts */
+
+ /* registers */
uint32_t intrmask;
uint32_t intrstatus;
- uint32_t doorbell;
-
- CharDriverState **eventfd_chr;
- CharDriverState *server_chr;
- Fifo8 incoming_fifo;
- MemoryRegion ivshmem_mmio;
+ int vm_id;
- /* We might need to register the BAR before we actually have the memory.
- * So prepare a container MemoryRegion for the BAR immediately and
- * add a subregion when we have the memory.
- */
- MemoryRegion bar;
- MemoryRegion ivshmem;
- uint64_t ivshmem_size; /* size of shared memory region */
- uint32_t ivshmem_attr;
- uint32_t ivshmem_64bit;
- int shm_fd; /* shared memory file descriptor */
+ /* BARs */
+ MemoryRegion ivshmem_mmio; /* BAR 0 (registers) */
+ MemoryRegion *ivshmem_bar2; /* BAR 2 (shared memory) */
+ MemoryRegion server_bar2; /* used with server_chr */
+ /* interrupt support */
Peer *peers;
- int nb_peers; /* how many guests we have space for */
- int max_peer; /* maximum numbered peer */
-
- int vm_id;
+ int nb_peers; /* space in @peers[] */
uint32_t vectors;
- uint32_t features;
- EventfdEntry *eventfd_table;
+ MSIVector *msi_vectors;
+ uint64_t msg_buf; /* buffer for receiving server messages */
+ int msg_buffered_bytes; /* #bytes in @msg_buf */
+ /* migration stuff */
+ OnOffAuto master;
Error *migration_blocker;
- char * shmobj;
- char * sizearg;
- char * role;
- int role_val; /* scalar to avoid multiple string comparisons */
+ /* legacy cruft */
+ char *role;
+ char *shmobj;
+ char *sizearg;
+ size_t legacy_size;
+ uint32_t not_legacy_32bit;
} IVShmemState;
/* registers for the Inter-VM shared memory device */
@@ -119,16 +136,34 @@ static inline uint32_t ivshmem_has_feature(IVShmemState *ivs,
return (ivs->features & (1 << feature));
}
-static inline bool is_power_of_two(uint64_t x) {
- return (x & (x - 1)) == 0;
+static inline bool ivshmem_is_master(IVShmemState *s)
+{
+ assert(s->master != ON_OFF_AUTO_AUTO);
+ return s->master == ON_OFF_AUTO_ON;
}
-/* accessing registers - based on rtl8139 */
-static void ivshmem_update_irq(IVShmemState *s, int val)
+static void ivshmem_update_irq(IVShmemState *s)
{
PCIDevice *d = PCI_DEVICE(s);
- int isr;
- isr = (s->intrstatus & s->intrmask) & 0xffffffff;
+ uint32_t isr = s->intrstatus & s->intrmask;
+
+ /*
+ * Do nothing unless the device actually uses INTx. Here's how
+ * the device variants signal interrupts, what they put in PCI
+ * config space:
+ * Device variant Interrupt Interrupt Pin MSI-X cap.
+ * ivshmem-plain none 0 no
+ * ivshmem-doorbell MSI-X 1 yes(1)
+ * ivshmem,msi=off INTx 1 no
+ * ivshmem,msi=on MSI-X 1(2) yes(1)
+ * (1) if guest enabled MSI-X
+ * (2) the device lies
+ * Leads to the condition for doing nothing:
+ */
+ if (ivshmem_has_feature(s, IVSHMEM_MSI)
+ || !d->config[PCI_INTERRUPT_PIN]) {
+ return;
+ }
/* don't print ISR resets */
if (isr) {
@@ -136,7 +171,7 @@ static void ivshmem_update_irq(IVShmemState *s, int val)
isr ? 1 : 0, s->intrstatus, s->intrmask);
}
- pci_set_irq(d, (isr != 0));
+ pci_set_irq(d, isr != 0);
}
static void ivshmem_IntrMask_write(IVShmemState *s, uint32_t val)
@@ -144,8 +179,7 @@ static void ivshmem_IntrMask_write(IVShmemState *s, uint32_t val)
IVSHMEM_DPRINTF("IntrMask write(w) val = 0x%04x\n", val);
s->intrmask = val;
-
- ivshmem_update_irq(s, val);
+ ivshmem_update_irq(s);
}
static uint32_t ivshmem_IntrMask_read(IVShmemState *s)
@@ -153,7 +187,6 @@ static uint32_t ivshmem_IntrMask_read(IVShmemState *s)
uint32_t ret = s->intrmask;
IVSHMEM_DPRINTF("intrmask read(w) val = 0x%04x\n", ret);
-
return ret;
}
@@ -162,8 +195,7 @@ static void ivshmem_IntrStatus_write(IVShmemState *s, uint32_t val)
IVSHMEM_DPRINTF("IntrStatus write(w) val = 0x%04x\n", val);
s->intrstatus = val;
-
- ivshmem_update_irq(s, val);
+ ivshmem_update_irq(s);
}
static uint32_t ivshmem_IntrStatus_read(IVShmemState *s)
@@ -172,9 +204,7 @@ static uint32_t ivshmem_IntrStatus_read(IVShmemState *s)
/* reading ISR clears all interrupts */
s->intrstatus = 0;
-
- ivshmem_update_irq(s, 0);
-
+ ivshmem_update_irq(s);
return ret;
}
@@ -201,7 +231,7 @@ static void ivshmem_io_write(void *opaque, hwaddr addr,
case DOORBELL:
/* check that dest VM ID is reasonable */
- if (dest > s->max_peer) {
+ if (dest >= s->nb_peers) {
IVSHMEM_DPRINTF("Invalid destination VM ID (%d)\n", dest);
break;
}
@@ -210,10 +240,13 @@ static void ivshmem_io_write(void *opaque, hwaddr addr,
if (vector < s->peers[dest].nb_eventfds) {
IVSHMEM_DPRINTF("Notifying VM %d on vector %d\n", dest, vector);
event_notifier_set(&s->peers[dest].eventfds[vector]);
+ } else {
+ IVSHMEM_DPRINTF("Invalid destination vector %d on VM %d\n",
+ vector, dest);
}
break;
default:
- IVSHMEM_DPRINTF("Invalid VM Doorbell VM %d\n", dest);
+ IVSHMEM_DPRINTF("Unhandled write " TARGET_FMT_plx "\n", addr);
}
}
@@ -235,12 +268,7 @@ static uint64_t ivshmem_io_read(void *opaque, hwaddr addr,
break;
case IVPOSITION:
- /* return my VM ID if the memory is mapped */
- if (s->shm_fd > 0) {
- ret = s->vm_id;
- } else {
- ret = -1;
- }
+ ret = s->vm_id;
break;
default:
@@ -261,105 +289,95 @@ static const MemoryRegionOps ivshmem_mmio_ops = {
},
};
-static void ivshmem_receive(void *opaque, const uint8_t *buf, int size)
+static void ivshmem_vector_notify(void *opaque)
{
- IVShmemState *s = opaque;
-
- ivshmem_IntrStatus_write(s, *buf);
+ MSIVector *entry = opaque;
+ PCIDevice *pdev = entry->pdev;
+ IVShmemState *s = IVSHMEM_COMMON(pdev);
+ int vector = entry - s->msi_vectors;
+ EventNotifier *n = &s->peers[s->vm_id].eventfds[vector];
- IVSHMEM_DPRINTF("ivshmem_receive 0x%02x\n", *buf);
-}
+ if (!event_notifier_test_and_clear(n)) {
+ return;
+ }
-static int ivshmem_can_receive(void * opaque)
-{
- return 8;
+ IVSHMEM_DPRINTF("interrupt on vector %p %d\n", pdev, vector);
+ if (ivshmem_has_feature(s, IVSHMEM_MSI)) {
+ if (msix_enabled(pdev)) {
+ msix_notify(pdev, vector);
+ }
+ } else {
+ ivshmem_IntrStatus_write(s, 1);
+ }
}
-static void ivshmem_event(void *opaque, int event)
+static int ivshmem_vector_unmask(PCIDevice *dev, unsigned vector,
+ MSIMessage msg)
{
- IVSHMEM_DPRINTF("ivshmem_event %d\n", event);
-}
+ IVShmemState *s = IVSHMEM_COMMON(dev);
+ EventNotifier *n = &s->peers[s->vm_id].eventfds[vector];
+ MSIVector *v = &s->msi_vectors[vector];
+ int ret;
-static void fake_irqfd(void *opaque, const uint8_t *buf, int size) {
+ IVSHMEM_DPRINTF("vector unmask %p %d\n", dev, vector);
- EventfdEntry *entry = opaque;
- PCIDevice *pdev = entry->pdev;
+ ret = kvm_irqchip_update_msi_route(kvm_state, v->virq, msg, dev);
+ if (ret < 0) {
+ return ret;
+ }
- IVSHMEM_DPRINTF("interrupt on vector %p %d\n", pdev, entry->vector);
- msix_notify(pdev, entry->vector);
+ return kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, n, NULL, v->virq);
}
-static CharDriverState* create_eventfd_chr_device(void * opaque, EventNotifier *n,
- int vector)
+static void ivshmem_vector_mask(PCIDevice *dev, unsigned vector)
{
- /* create a event character device based on the passed eventfd */
- IVShmemState *s = opaque;
- CharDriverState * chr;
- int eventfd = event_notifier_get_fd(n);
-
- chr = qemu_chr_open_eventfd(eventfd);
-
- if (chr == NULL) {
- error_report("creating eventfd for eventfd %d failed", eventfd);
- exit(1);
- }
- qemu_chr_fe_claim_no_fail(chr);
+ IVShmemState *s = IVSHMEM_COMMON(dev);
+ EventNotifier *n = &s->peers[s->vm_id].eventfds[vector];
+ int ret;
- /* if MSI is supported we need multiple interrupts */
- if (ivshmem_has_feature(s, IVSHMEM_MSI)) {
- s->eventfd_table[vector].pdev = PCI_DEVICE(s);
- s->eventfd_table[vector].vector = vector;
+ IVSHMEM_DPRINTF("vector mask %p %d\n", dev, vector);
- qemu_chr_add_handlers(chr, ivshmem_can_receive, fake_irqfd,
- ivshmem_event, &s->eventfd_table[vector]);
- } else {
- qemu_chr_add_handlers(chr, ivshmem_can_receive, ivshmem_receive,
- ivshmem_event, s);
+ ret = kvm_irqchip_remove_irqfd_notifier_gsi(kvm_state, n,
+ s->msi_vectors[vector].virq);
+ if (ret != 0) {
+ error_report("remove_irqfd_notifier_gsi failed");
}
+}
- return chr;
+static void ivshmem_vector_poll(PCIDevice *dev,
+ unsigned int vector_start,
+ unsigned int vector_end)
+{
+ IVShmemState *s = IVSHMEM_COMMON(dev);
+ unsigned int vector;
-}
+ IVSHMEM_DPRINTF("vector poll %p %d-%d\n", dev, vector_start, vector_end);
-static int check_shm_size(IVShmemState *s, int fd) {
- /* check that the guest isn't going to try and map more memory than the
- * the object has allocated return -1 to indicate error */
+ vector_end = MIN(vector_end, s->vectors);
- struct stat buf;
+ for (vector = vector_start; vector < vector_end; vector++) {
+ EventNotifier *notifier = &s->peers[s->vm_id].eventfds[vector];
- if (fstat(fd, &buf) < 0) {
- error_report("exiting: fstat on fd %d failed: %s",
- fd, strerror(errno));
- return -1;
- }
+ if (!msix_is_masked(dev, vector)) {
+ continue;
+ }
- if (s->ivshmem_size > buf.st_size) {
- error_report("Requested memory size greater"
- " than shared object size (%" PRIu64 " > %" PRIu64")",
- s->ivshmem_size, (uint64_t)buf.st_size);
- return -1;
- } else {
- return 0;
+ if (event_notifier_test_and_clear(notifier)) {
+ msix_set_pending(dev, vector);
+ }
}
}
-/* create the shared memory BAR when we are not using the server, so we can
- * create the BAR and map the memory immediately */
-static void create_shared_memory_BAR(IVShmemState *s, int fd) {
-
- void * ptr;
-
- s->shm_fd = fd;
-
- ptr = mmap(0, s->ivshmem_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
+static void watch_vector_notifier(IVShmemState *s, EventNotifier *n,
+ int vector)
+{
+ int eventfd = event_notifier_get_fd(n);
- memory_region_init_ram_ptr(&s->ivshmem, OBJECT(s), "ivshmem.bar2",
- s->ivshmem_size, ptr);
- vmstate_register_ram(&s->ivshmem, DEVICE(s));
- memory_region_add_subregion(&s->bar, 0, &s->ivshmem);
+ assert(!s->msi_vectors[vector].pdev);
+ s->msi_vectors[vector].pdev = PCI_DEVICE(s);
- /* region for shared memory */
- pci_register_bar(PCI_DEVICE(s), 2, s->ivshmem_attr, &s->bar);
+ qemu_set_fd_handler(eventfd, ivshmem_vector_notify,
+ NULL, &s->msi_vectors[vector]);
}
static void ivshmem_add_eventfd(IVShmemState *s, int posn, int i)
@@ -382,25 +400,22 @@ static void ivshmem_del_eventfd(IVShmemState *s, int posn, int i)
&s->peers[posn].eventfds[i]);
}
-static void close_guest_eventfds(IVShmemState *s, int posn)
+static void close_peer_eventfds(IVShmemState *s, int posn)
{
- int i, guest_curr_max;
+ int i, n;
- if (!ivshmem_has_feature(s, IVSHMEM_IOEVENTFD)) {
- return;
- }
- if (posn < 0 || posn >= s->nb_peers) {
- return;
- }
+ assert(posn >= 0 && posn < s->nb_peers);
+ n = s->peers[posn].nb_eventfds;
- guest_curr_max = s->peers[posn].nb_eventfds;
-
- memory_region_transaction_begin();
- for (i = 0; i < guest_curr_max; i++) {
- ivshmem_del_eventfd(s, posn, i);
+ if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD)) {
+ memory_region_transaction_begin();
+ for (i = 0; i < n; i++) {
+ ivshmem_del_eventfd(s, posn, i);
+ }
+ memory_region_transaction_commit();
}
- memory_region_transaction_commit();
- for (i = 0; i < guest_curr_max; i++) {
+
+ for (i = 0; i < n; i++) {
event_notifier_cleanup(&s->peers[posn].eventfds[i]);
}
@@ -408,187 +423,320 @@ static void close_guest_eventfds(IVShmemState *s, int posn)
s->peers[posn].nb_eventfds = 0;
}
-/* this function increase the dynamic storage need to store data about other
- * guests */
-static int increase_dynamic_storage(IVShmemState *s, int new_min_size)
+static void resize_peers(IVShmemState *s, int nb_peers)
{
+ int old_nb_peers = s->nb_peers;
+ int i;
- int j, old_nb_alloc;
-
- /* check for integer overflow */
- if (new_min_size >= INT_MAX / sizeof(Peer) - 1 || new_min_size <= 0) {
- return -1;
- }
+ assert(nb_peers > old_nb_peers);
+ IVSHMEM_DPRINTF("bumping storage to %d peers\n", nb_peers);
- old_nb_alloc = s->nb_peers;
+ s->peers = g_realloc(s->peers, nb_peers * sizeof(Peer));
+ s->nb_peers = nb_peers;
- if (new_min_size >= s->nb_peers) {
- /* +1 because #new_min_size is used as last array index */
- s->nb_peers = new_min_size + 1;
- } else {
- return 0;
+ for (i = old_nb_peers; i < nb_peers; i++) {
+ s->peers[i].eventfds = g_new0(EventNotifier, s->vectors);
+ s->peers[i].nb_eventfds = 0;
}
+}
- IVSHMEM_DPRINTF("bumping storage to %d guests\n", s->nb_peers);
- s->peers = g_realloc(s->peers, s->nb_peers * sizeof(Peer));
+static void ivshmem_add_kvm_msi_virq(IVShmemState *s, int vector,
+ Error **errp)
+{
+ PCIDevice *pdev = PCI_DEVICE(s);
+ MSIMessage msg = msix_get_message(pdev, vector);
+ int ret;
- /* zero out new pointers */
- for (j = old_nb_alloc; j < s->nb_peers; j++) {
- s->peers[j].eventfds = NULL;
- s->peers[j].nb_eventfds = 0;
+ IVSHMEM_DPRINTF("ivshmem_add_kvm_msi_virq vector:%d\n", vector);
+ assert(!s->msi_vectors[vector].pdev);
+
+ ret = kvm_irqchip_add_msi_route(kvm_state, msg, pdev);
+ if (ret < 0) {
+ error_setg(errp, "kvm_irqchip_add_msi_route failed");
+ return;
}
- return 0;
+ s->msi_vectors[vector].virq = ret;
+ s->msi_vectors[vector].pdev = pdev;
}
-static void ivshmem_read(void *opaque, const uint8_t *buf, int size)
+static void setup_interrupt(IVShmemState *s, int vector, Error **errp)
{
- IVShmemState *s = opaque;
- int incoming_fd, tmp_fd;
- int guest_max_eventfd;
- long incoming_posn;
-
- if (fifo8_is_empty(&s->incoming_fifo) && size == sizeof(incoming_posn)) {
- memcpy(&incoming_posn, buf, size);
- } else {
- const uint8_t *p;
- uint32_t num;
-
- IVSHMEM_DPRINTF("short read of %d bytes\n", size);
- num = MAX(size, sizeof(long) - fifo8_num_used(&s->incoming_fifo));
- fifo8_push_all(&s->incoming_fifo, buf, num);
- if (fifo8_num_used(&s->incoming_fifo) < sizeof(incoming_posn)) {
+ EventNotifier *n = &s->peers[s->vm_id].eventfds[vector];
+ bool with_irqfd = kvm_msi_via_irqfd_enabled() &&
+ ivshmem_has_feature(s, IVSHMEM_MSI);
+ PCIDevice *pdev = PCI_DEVICE(s);
+ Error *err = NULL;
+
+ IVSHMEM_DPRINTF("setting up interrupt for vector: %d\n", vector);
+
+ if (!with_irqfd) {
+ IVSHMEM_DPRINTF("with eventfd\n");
+ watch_vector_notifier(s, n, vector);
+ } else if (msix_enabled(pdev)) {
+ IVSHMEM_DPRINTF("with irqfd\n");
+ ivshmem_add_kvm_msi_virq(s, vector, &err);
+ if (err) {
+ error_propagate(errp, err);
return;
}
- size -= num;
- buf += num;
- p = fifo8_pop_buf(&s->incoming_fifo, sizeof(incoming_posn), &num);
- g_assert(num == sizeof(incoming_posn));
- memcpy(&incoming_posn, p, sizeof(incoming_posn));
- if (size > 0) {
- fifo8_push_all(&s->incoming_fifo, buf, size);
+
+ if (!msix_is_masked(pdev, vector)) {
+ kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, n, NULL,
+ s->msi_vectors[vector].virq);
+ /* TODO handle error */
}
+ } else {
+ /* it will be delayed until msix is enabled, in write_config */
+ IVSHMEM_DPRINTF("with irqfd, delayed until msix enabled\n");
}
+}
- if (incoming_posn < -1) {
- IVSHMEM_DPRINTF("invalid incoming_posn %ld\n", incoming_posn);
+static void process_msg_shmem(IVShmemState *s, int fd, Error **errp)
+{
+ struct stat buf;
+ size_t size;
+ void *ptr;
+
+ if (s->ivshmem_bar2) {
+ error_setg(errp, "server sent unexpected shared memory message");
+ close(fd);
+ return;
+ }
+
+ if (fstat(fd, &buf) < 0) {
+ error_setg_errno(errp, errno,
+ "can't determine size of shared memory sent by server");
+ close(fd);
return;
}
- /* pick off s->server_chr->msgfd and store it, posn should accompany msg */
- tmp_fd = qemu_chr_fe_get_msgfd(s->server_chr);
- IVSHMEM_DPRINTF("posn is %ld, fd is %d\n", incoming_posn, tmp_fd);
+ size = buf.st_size;
- /* make sure we have enough space for this guest */
- if (incoming_posn >= s->nb_peers) {
- if (increase_dynamic_storage(s, incoming_posn) < 0) {
- error_report("increase_dynamic_storage() failed");
- if (tmp_fd != -1) {
- close(tmp_fd);
- }
+ /* Legacy cruft */
+ if (s->legacy_size != SIZE_MAX) {
+ if (size < s->legacy_size) {
+ error_setg(errp, "server sent only %zd bytes of shared memory",
+ (size_t)buf.st_size);
+ close(fd);
return;
}
+ size = s->legacy_size;
}
- if (tmp_fd == -1) {
- /* if posn is positive and unseen before then this is our posn*/
- if ((incoming_posn >= 0) &&
- (s->peers[incoming_posn].eventfds == NULL)) {
- /* receive our posn */
- s->vm_id = incoming_posn;
- return;
- } else {
- /* otherwise an fd == -1 means an existing guest has gone away */
- IVSHMEM_DPRINTF("posn %ld has gone away\n", incoming_posn);
- close_guest_eventfds(s, incoming_posn);
- return;
- }
+ /* mmap the region and map into the BAR2 */
+ ptr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (ptr == MAP_FAILED) {
+ error_setg_errno(errp, errno, "Failed to mmap shared memory");
+ close(fd);
+ return;
}
+ memory_region_init_ram_ptr(&s->server_bar2, OBJECT(s),
+ "ivshmem.bar2", size, ptr);
+ qemu_set_ram_fd(memory_region_get_ram_addr(&s->server_bar2), fd);
+ s->ivshmem_bar2 = &s->server_bar2;
+}
- /* because of the implementation of get_msgfd, we need a dup */
- incoming_fd = dup(tmp_fd);
+static void process_msg_disconnect(IVShmemState *s, uint16_t posn,
+ Error **errp)
+{
+ IVSHMEM_DPRINTF("posn %d has gone away\n", posn);
+ if (posn >= s->nb_peers || posn == s->vm_id) {
+ error_setg(errp, "invalid peer %d", posn);
+ return;
+ }
+ close_peer_eventfds(s, posn);
+}
- if (incoming_fd == -1) {
- error_report("could not allocate file descriptor %s", strerror(errno));
- close(tmp_fd);
+static void process_msg_connect(IVShmemState *s, uint16_t posn, int fd,
+ Error **errp)
+{
+ Peer *peer = &s->peers[posn];
+ int vector;
+
+ /*
+ * The N-th connect message for this peer comes with the file
+ * descriptor for vector N-1. Count messages to find the vector.
+ */
+ if (peer->nb_eventfds >= s->vectors) {
+ error_setg(errp, "Too many eventfd received, device has %d vectors",
+ s->vectors);
+ close(fd);
return;
}
+ vector = peer->nb_eventfds++;
+
+ IVSHMEM_DPRINTF("eventfds[%d][%d] = %d\n", posn, vector, fd);
+ event_notifier_init_fd(&peer->eventfds[vector], fd);
+ fcntl_setfl(fd, O_NONBLOCK); /* msix/irqfd poll non block */
+
+ if (posn == s->vm_id) {
+ setup_interrupt(s, vector, errp);
+ /* TODO do we need to handle the error? */
+ }
- /* if the position is -1, then it's shared memory region fd */
- if (incoming_posn == -1) {
+ if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD)) {
+ ivshmem_add_eventfd(s, posn, vector);
+ }
+}
- void * map_ptr;
+static void process_msg(IVShmemState *s, int64_t msg, int fd, Error **errp)
+{
+ IVSHMEM_DPRINTF("posn is %" PRId64 ", fd is %d\n", msg, fd);
- s->max_peer = 0;
+ if (msg < -1 || msg > IVSHMEM_MAX_PEERS) {
+ error_setg(errp, "server sent invalid message %" PRId64, msg);
+ close(fd);
+ return;
+ }
- if (check_shm_size(s, incoming_fd) == -1) {
- exit(1);
- }
+ if (msg == -1) {
+ process_msg_shmem(s, fd, errp);
+ return;
+ }
- /* mmap the region and map into the BAR2 */
- map_ptr = mmap(0, s->ivshmem_size, PROT_READ|PROT_WRITE, MAP_SHARED,
- incoming_fd, 0);
- memory_region_init_ram_ptr(&s->ivshmem, OBJECT(s),
- "ivshmem.bar2", s->ivshmem_size, map_ptr);
- vmstate_register_ram(&s->ivshmem, DEVICE(s));
+ if (msg >= s->nb_peers) {
+ resize_peers(s, msg + 1);
+ }
- IVSHMEM_DPRINTF("guest h/w addr = %p, size = %" PRIu64 "\n",
- map_ptr, s->ivshmem_size);
+ if (fd >= 0) {
+ process_msg_connect(s, msg, fd, errp);
+ } else {
+ process_msg_disconnect(s, msg, errp);
+ }
+}
- memory_region_add_subregion(&s->bar, 0, &s->ivshmem);
+static int ivshmem_can_receive(void *opaque)
+{
+ IVShmemState *s = opaque;
- /* only store the fd if it is successfully mapped */
- s->shm_fd = incoming_fd;
+ assert(s->msg_buffered_bytes < sizeof(s->msg_buf));
+ return sizeof(s->msg_buf) - s->msg_buffered_bytes;
+}
+static void ivshmem_read(void *opaque, const uint8_t *buf, int size)
+{
+ IVShmemState *s = opaque;
+ Error *err = NULL;
+ int fd;
+ int64_t msg;
+
+ assert(size >= 0 && s->msg_buffered_bytes + size <= sizeof(s->msg_buf));
+ memcpy((unsigned char *)&s->msg_buf + s->msg_buffered_bytes, buf, size);
+ s->msg_buffered_bytes += size;
+ if (s->msg_buffered_bytes < sizeof(s->msg_buf)) {
return;
}
+ msg = le64_to_cpu(s->msg_buf);
+ s->msg_buffered_bytes = 0;
- /* each guest has an array of eventfds, and we keep track of how many
- * guests for each VM */
- guest_max_eventfd = s->peers[incoming_posn].nb_eventfds;
+ fd = qemu_chr_fe_get_msgfd(s->server_chr);
+ IVSHMEM_DPRINTF("posn is %" PRId64 ", fd is %d\n", msg, fd);
- if (guest_max_eventfd == 0) {
- /* one eventfd per MSI vector */
- s->peers[incoming_posn].eventfds = g_new(EventNotifier, s->vectors);
+ process_msg(s, msg, fd, &err);
+ if (err) {
+ error_report_err(err);
}
+}
- /* this is an eventfd for a particular guest VM */
- IVSHMEM_DPRINTF("eventfds[%ld][%d] = %d\n", incoming_posn,
- guest_max_eventfd, incoming_fd);
- event_notifier_init_fd(&s->peers[incoming_posn].eventfds[guest_max_eventfd],
- incoming_fd);
+static int64_t ivshmem_recv_msg(IVShmemState *s, int *pfd, Error **errp)
+{
+ int64_t msg;
+ int n, ret;
+
+ n = 0;
+ do {
+ ret = qemu_chr_fe_read_all(s->server_chr, (uint8_t *)&msg + n,
+ sizeof(msg) - n);
+ if (ret < 0 && ret != -EINTR) {
+ error_setg_errno(errp, -ret, "read from server failed");
+ return INT64_MIN;
+ }
+ n += ret;
+ } while (n < sizeof(msg));
- /* increment count for particular guest */
- s->peers[incoming_posn].nb_eventfds++;
+ *pfd = qemu_chr_fe_get_msgfd(s->server_chr);
+ return msg;
+}
- /* keep track of the maximum VM ID */
- if (incoming_posn > s->max_peer) {
- s->max_peer = incoming_posn;
- }
+static void ivshmem_recv_setup(IVShmemState *s, Error **errp)
+{
+ Error *err = NULL;
+ int64_t msg;
+ int fd;
- if (incoming_posn == s->vm_id) {
- s->eventfd_chr[guest_max_eventfd] = create_eventfd_chr_device(s,
- &s->peers[s->vm_id].eventfds[guest_max_eventfd],
- guest_max_eventfd);
+ msg = ivshmem_recv_msg(s, &fd, &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ if (msg != IVSHMEM_PROTOCOL_VERSION) {
+ error_setg(errp, "server sent version %" PRId64 ", expecting %d",
+ msg, IVSHMEM_PROTOCOL_VERSION);
+ return;
+ }
+ if (fd != -1) {
+ error_setg(errp, "server sent invalid version message");
+ return;
}
- if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD)) {
- ivshmem_add_eventfd(s, incoming_posn, guest_max_eventfd);
+ /*
+ * ivshmem-server sends the remaining initial messages in a fixed
+ * order, but the device has always accepted them in any order.
+ * Stay as compatible as practical, just in case people use
+ * servers that behave differently.
+ */
+
+ /*
+ * ivshmem_device_spec.txt has always required the ID message
+ * right here, and ivshmem-server has always complied. However,
+ * older versions of the device accepted it out of order, but
+ * broke when an interrupt setup message arrived before it.
+ */
+ msg = ivshmem_recv_msg(s, &fd, &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ if (fd != -1 || msg < 0 || msg > IVSHMEM_MAX_PEERS) {
+ error_setg(errp, "server sent invalid ID message");
+ return;
}
+ s->vm_id = msg;
+
+ /*
+ * Receive more messages until we got shared memory.
+ */
+ do {
+ msg = ivshmem_recv_msg(s, &fd, &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ process_msg(s, msg, fd, &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ } while (msg != -1);
+
+ /*
+ * This function must either map the shared memory or fail. The
+ * loop above ensures that: it terminates normally only after it
+ * successfully processed the server's shared memory message.
+ * Assert that actually mapped the shared memory:
+ */
+ assert(s->ivshmem_bar2);
}
/* Select the MSI-X vectors used by device.
* ivshmem maps events to vectors statically, so
* we just enable all vectors on init and after reset. */
-static void ivshmem_use_msix(IVShmemState * s)
+static void ivshmem_msix_vector_use(IVShmemState *s)
{
PCIDevice *d = PCI_DEVICE(s);
int i;
- if (!msix_present(d)) {
- return;
- }
-
for (i = 0; i < s->vectors; i++) {
msix_vector_use(d, i);
}
@@ -596,295 +744,579 @@ static void ivshmem_use_msix(IVShmemState * s)
static void ivshmem_reset(DeviceState *d)
{
- IVShmemState *s = IVSHMEM(d);
+ IVShmemState *s = IVSHMEM_COMMON(d);
s->intrstatus = 0;
- ivshmem_use_msix(s);
+ s->intrmask = 0;
+ if (ivshmem_has_feature(s, IVSHMEM_MSI)) {
+ ivshmem_msix_vector_use(s);
+ }
}
-static uint64_t ivshmem_get_size(IVShmemState * s) {
+static int ivshmem_setup_interrupts(IVShmemState *s)
+{
+ /* allocate QEMU callback data for receiving interrupts */
+ s->msi_vectors = g_malloc0(s->vectors * sizeof(MSIVector));
- uint64_t value;
- char *ptr;
+ if (ivshmem_has_feature(s, IVSHMEM_MSI)) {
+ if (msix_init_exclusive_bar(PCI_DEVICE(s), s->vectors, 1)) {
+ return -1;
+ }
- value = strtoull(s->sizearg, &ptr, 10);
- switch (*ptr) {
- case 0: case 'M': case 'm':
- value <<= 20;
- break;
- case 'G': case 'g':
- value <<= 30;
- break;
- default:
- error_report("invalid ram size: %s", s->sizearg);
- exit(1);
+ IVSHMEM_DPRINTF("msix initialized (%d vectors)\n", s->vectors);
+ ivshmem_msix_vector_use(s);
}
- /* BARs must be a power of 2 */
- if (!is_power_of_two(value)) {
- error_report("size must be power of 2");
- exit(1);
+ return 0;
+}
+
+static void ivshmem_enable_irqfd(IVShmemState *s)
+{
+ PCIDevice *pdev = PCI_DEVICE(s);
+ int i;
+
+ for (i = 0; i < s->peers[s->vm_id].nb_eventfds; i++) {
+ Error *err = NULL;
+
+ ivshmem_add_kvm_msi_virq(s, i, &err);
+ if (err) {
+ error_report_err(err);
+ /* TODO do we need to handle the error? */
+ }
}
- return value;
+ if (msix_set_vector_notifiers(pdev,
+ ivshmem_vector_unmask,
+ ivshmem_vector_mask,
+ ivshmem_vector_poll)) {
+ error_report("ivshmem: msix_set_vector_notifiers failed");
+ }
}
-static void ivshmem_setup_msi(IVShmemState * s)
+static void ivshmem_remove_kvm_msi_virq(IVShmemState *s, int vector)
{
- if (msix_init_exclusive_bar(PCI_DEVICE(s), s->vectors, 1)) {
- IVSHMEM_DPRINTF("msix initialization failed\n");
- exit(1);
- }
+ IVSHMEM_DPRINTF("ivshmem_remove_kvm_msi_virq vector:%d\n", vector);
- IVSHMEM_DPRINTF("msix initialized (%d vectors)\n", s->vectors);
+ if (s->msi_vectors[vector].pdev == NULL) {
+ return;
+ }
- /* allocate QEMU char devices for receiving interrupts */
- s->eventfd_table = g_malloc0(s->vectors * sizeof(EventfdEntry));
+ /* it was cleaned when masked in the frontend. */
+ kvm_irqchip_release_virq(kvm_state, s->msi_vectors[vector].virq);
- ivshmem_use_msix(s);
+ s->msi_vectors[vector].pdev = NULL;
}
-static void ivshmem_save(QEMUFile* f, void *opaque)
+static void ivshmem_disable_irqfd(IVShmemState *s)
{
- IVShmemState *proxy = opaque;
- PCIDevice *pci_dev = PCI_DEVICE(proxy);
-
- IVSHMEM_DPRINTF("ivshmem_save\n");
- pci_device_save(pci_dev, f);
+ PCIDevice *pdev = PCI_DEVICE(s);
+ int i;
- if (ivshmem_has_feature(proxy, IVSHMEM_MSI)) {
- msix_save(pci_dev, f);
- } else {
- qemu_put_be32(f, proxy->intrstatus);
- qemu_put_be32(f, proxy->intrmask);
+ for (i = 0; i < s->peers[s->vm_id].nb_eventfds; i++) {
+ ivshmem_remove_kvm_msi_virq(s, i);
}
+ msix_unset_vector_notifiers(pdev);
}
-static int ivshmem_load(QEMUFile* f, void *opaque, int version_id)
+static void ivshmem_write_config(PCIDevice *pdev, uint32_t address,
+ uint32_t val, int len)
{
- IVSHMEM_DPRINTF("ivshmem_load\n");
+ IVShmemState *s = IVSHMEM_COMMON(pdev);
+ int is_enabled, was_enabled = msix_enabled(pdev);
- IVShmemState *proxy = opaque;
- PCIDevice *pci_dev = PCI_DEVICE(proxy);
- int ret;
+ pci_default_write_config(pdev, address, val, len);
+ is_enabled = msix_enabled(pdev);
- if (version_id > 0) {
- return -EINVAL;
+ if (kvm_msi_via_irqfd_enabled()) {
+ if (!was_enabled && is_enabled) {
+ ivshmem_enable_irqfd(s);
+ } else if (was_enabled && !is_enabled) {
+ ivshmem_disable_irqfd(s);
+ }
}
+}
- if (proxy->role_val == IVSHMEM_PEER) {
- error_report("'peer' devices are not migratable");
- return -EINVAL;
+static void ivshmem_common_realize(PCIDevice *dev, Error **errp)
+{
+ IVShmemState *s = IVSHMEM_COMMON(dev);
+ Error *err = NULL;
+ uint8_t *pci_conf;
+ uint8_t attr = PCI_BASE_ADDRESS_SPACE_MEMORY |
+ PCI_BASE_ADDRESS_MEM_PREFETCH;
+
+ /* IRQFD requires MSI */
+ if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD) &&
+ !ivshmem_has_feature(s, IVSHMEM_MSI)) {
+ error_setg(errp, "ioeventfd/irqfd requires MSI");
+ return;
}
- ret = pci_device_load(pci_dev, f);
- if (ret) {
- return ret;
+ pci_conf = dev->config;
+ pci_conf[PCI_COMMAND] = PCI_COMMAND_IO | PCI_COMMAND_MEMORY;
+
+ memory_region_init_io(&s->ivshmem_mmio, OBJECT(s), &ivshmem_mmio_ops, s,
+ "ivshmem-mmio", IVSHMEM_REG_BAR_SIZE);
+
+ /* region for registers*/
+ pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY,
+ &s->ivshmem_mmio);
+
+ if (!s->not_legacy_32bit) {
+ attr |= PCI_BASE_ADDRESS_MEM_TYPE_64;
}
- if (ivshmem_has_feature(proxy, IVSHMEM_MSI)) {
- msix_load(pci_dev, f);
- ivshmem_use_msix(proxy);
+ if (s->hostmem != NULL) {
+ IVSHMEM_DPRINTF("using hostmem\n");
+
+ s->ivshmem_bar2 = host_memory_backend_get_memory(s->hostmem,
+ &error_abort);
} else {
- proxy->intrstatus = qemu_get_be32(f);
- proxy->intrmask = qemu_get_be32(f);
+ assert(s->server_chr);
+
+ IVSHMEM_DPRINTF("using shared memory server (socket = %s)\n",
+ s->server_chr->filename);
+
+ /* we allocate enough space for 16 peers and grow as needed */
+ resize_peers(s, 16);
+
+ /*
+ * Receive setup messages from server synchronously.
+ * Older versions did it asynchronously, but that creates a
+ * number of entertaining race conditions.
+ */
+ ivshmem_recv_setup(s, &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ if (s->master == ON_OFF_AUTO_ON && s->vm_id != 0) {
+ error_setg(errp,
+ "master must connect to the server before any peers");
+ return;
+ }
+
+ qemu_chr_add_handlers(s->server_chr, ivshmem_can_receive,
+ ivshmem_read, NULL, s);
+
+ if (ivshmem_setup_interrupts(s) < 0) {
+ error_setg(errp, "failed to initialize interrupts");
+ return;
+ }
}
- return 0;
-}
+ vmstate_register_ram(s->ivshmem_bar2, DEVICE(s));
+ pci_register_bar(PCI_DEVICE(s), 2, attr, s->ivshmem_bar2);
-static void ivshmem_write_config(PCIDevice *pci_dev, uint32_t address,
- uint32_t val, int len)
-{
- pci_default_write_config(pci_dev, address, val, len);
+ if (s->master == ON_OFF_AUTO_AUTO) {
+ s->master = s->vm_id == 0 ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF;
+ }
+
+ if (!ivshmem_is_master(s)) {
+ error_setg(&s->migration_blocker,
+ "Migration is disabled when using feature 'peer mode' in device 'ivshmem'");
+ migrate_add_blocker(s->migration_blocker);
+ }
}
-static int pci_ivshmem_init(PCIDevice *dev)
+static void ivshmem_exit(PCIDevice *dev)
{
- IVShmemState *s = IVSHMEM(dev);
- uint8_t *pci_conf;
+ IVShmemState *s = IVSHMEM_COMMON(dev);
+ int i;
- if (s->sizearg == NULL)
- s->ivshmem_size = 4 << 20; /* 4 MB default */
- else {
- s->ivshmem_size = ivshmem_get_size(s);
+ if (s->migration_blocker) {
+ migrate_del_blocker(s->migration_blocker);
+ error_free(s->migration_blocker);
}
- fifo8_create(&s->incoming_fifo, sizeof(long));
+ if (memory_region_is_mapped(s->ivshmem_bar2)) {
+ if (!s->hostmem) {
+ void *addr = memory_region_get_ram_ptr(s->ivshmem_bar2);
+ int fd;
- register_savevm(DEVICE(dev), "ivshmem", 0, 0, ivshmem_save, ivshmem_load,
- dev);
+ if (munmap(addr, memory_region_size(s->ivshmem_bar2) == -1)) {
+ error_report("Failed to munmap shared memory %s",
+ strerror(errno));
+ }
- /* IRQFD requires MSI */
- if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD) &&
- !ivshmem_has_feature(s, IVSHMEM_MSI)) {
- error_report("ioeventfd/irqfd requires MSI");
- exit(1);
+ fd = qemu_get_ram_fd(memory_region_get_ram_addr(s->ivshmem_bar2));
+ close(fd);
+ }
+
+ vmstate_unregister_ram(s->ivshmem_bar2, DEVICE(dev));
}
- /* check that role is reasonable */
- if (s->role) {
- if (strncmp(s->role, "peer", 5) == 0) {
- s->role_val = IVSHMEM_PEER;
- } else if (strncmp(s->role, "master", 7) == 0) {
- s->role_val = IVSHMEM_MASTER;
- } else {
- error_report("'role' must be 'peer' or 'master'");
- exit(1);
+ if (s->peers) {
+ for (i = 0; i < s->nb_peers; i++) {
+ close_peer_eventfds(s, i);
}
- } else {
- s->role_val = IVSHMEM_MASTER; /* default */
+ g_free(s->peers);
}
- if (s->role_val == IVSHMEM_PEER) {
- error_setg(&s->migration_blocker,
- "Migration is disabled when using feature 'peer mode' in device 'ivshmem'");
- migrate_add_blocker(s->migration_blocker);
+ if (ivshmem_has_feature(s, IVSHMEM_MSI)) {
+ msix_uninit_exclusive_bar(dev);
}
- pci_conf = dev->config;
- pci_conf[PCI_COMMAND] = PCI_COMMAND_IO | PCI_COMMAND_MEMORY;
+ g_free(s->msi_vectors);
+}
- pci_config_set_interrupt_pin(pci_conf, 1);
+static int ivshmem_pre_load(void *opaque)
+{
+ IVShmemState *s = opaque;
- s->shm_fd = 0;
+ if (!ivshmem_is_master(s)) {
+ error_report("'peer' devices are not migratable");
+ return -EINVAL;
+ }
- memory_region_init_io(&s->ivshmem_mmio, OBJECT(s), &ivshmem_mmio_ops, s,
- "ivshmem-mmio", IVSHMEM_REG_BAR_SIZE);
+ return 0;
+}
- /* region for registers*/
- pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY,
- &s->ivshmem_mmio);
+static int ivshmem_post_load(void *opaque, int version_id)
+{
+ IVShmemState *s = opaque;
- memory_region_init(&s->bar, OBJECT(s), "ivshmem-bar2-container", s->ivshmem_size);
- s->ivshmem_attr = PCI_BASE_ADDRESS_SPACE_MEMORY |
- PCI_BASE_ADDRESS_MEM_PREFETCH;
- if (s->ivshmem_64bit) {
- s->ivshmem_attr |= PCI_BASE_ADDRESS_MEM_TYPE_64;
+ if (ivshmem_has_feature(s, IVSHMEM_MSI)) {
+ ivshmem_msix_vector_use(s);
}
+ return 0;
+}
- if ((s->server_chr != NULL) &&
- (strncmp(s->server_chr->filename, "unix:", 5) == 0)) {
- /* if we get a UNIX socket as the parameter we will talk
- * to the ivshmem server to receive the memory region */
+static void ivshmem_common_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
- if (s->shmobj != NULL) {
- error_report("WARNING: do not specify both 'chardev' "
- "and 'shm' with ivshmem");
- }
+ k->realize = ivshmem_common_realize;
+ k->exit = ivshmem_exit;
+ k->config_write = ivshmem_write_config;
+ k->vendor_id = PCI_VENDOR_ID_IVSHMEM;
+ k->device_id = PCI_DEVICE_ID_IVSHMEM;
+ k->class_id = PCI_CLASS_MEMORY_RAM;
+ k->revision = 1;
+ dc->reset = ivshmem_reset;
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+ dc->desc = "Inter-VM shared memory";
+}
- IVSHMEM_DPRINTF("using shared memory server (socket = %s)\n",
- s->server_chr->filename);
+static const TypeInfo ivshmem_common_info = {
+ .name = TYPE_IVSHMEM_COMMON,
+ .parent = TYPE_PCI_DEVICE,
+ .instance_size = sizeof(IVShmemState),
+ .abstract = true,
+ .class_init = ivshmem_common_class_init,
+};
- if (ivshmem_has_feature(s, IVSHMEM_MSI)) {
- ivshmem_setup_msi(s);
- }
+static void ivshmem_check_memdev_is_busy(Object *obj, const char *name,
+ Object *val, Error **errp)
+{
+ MemoryRegion *mr;
- /* we allocate enough space for 16 guests and grow as needed */
- s->nb_peers = 16;
- s->vm_id = -1;
+ mr = host_memory_backend_get_memory(MEMORY_BACKEND(val), &error_abort);
+ if (memory_region_is_mapped(mr)) {
+ char *path = object_get_canonical_path_component(val);
+ error_setg(errp, "can't use already busy memdev: %s", path);
+ g_free(path);
+ } else {
+ qdev_prop_allow_set_link_before_realize(obj, name, val, errp);
+ }
+}
+
+static const VMStateDescription ivshmem_plain_vmsd = {
+ .name = TYPE_IVSHMEM_PLAIN,
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .pre_load = ivshmem_pre_load,
+ .post_load = ivshmem_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_PCI_DEVICE(parent_obj, IVShmemState),
+ VMSTATE_UINT32(intrstatus, IVShmemState),
+ VMSTATE_UINT32(intrmask, IVShmemState),
+ VMSTATE_END_OF_LIST()
+ },
+};
- /* allocate/initialize space for interrupt handling */
- s->peers = g_malloc0(s->nb_peers * sizeof(Peer));
+static Property ivshmem_plain_properties[] = {
+ DEFINE_PROP_ON_OFF_AUTO("master", IVShmemState, master, ON_OFF_AUTO_OFF),
+ DEFINE_PROP_END_OF_LIST(),
+};
- pci_register_bar(dev, 2, s->ivshmem_attr, &s->bar);
+static void ivshmem_plain_init(Object *obj)
+{
+ IVShmemState *s = IVSHMEM_PLAIN(obj);
- s->eventfd_chr = g_malloc0(s->vectors * sizeof(CharDriverState *));
+ object_property_add_link(obj, "memdev", TYPE_MEMORY_BACKEND,
+ (Object **)&s->hostmem,
+ ivshmem_check_memdev_is_busy,
+ OBJ_PROP_LINK_UNREF_ON_RELEASE,
+ &error_abort);
+}
- qemu_chr_add_handlers(s->server_chr, ivshmem_can_receive, ivshmem_read,
- ivshmem_event, s);
- } else {
- /* just map the file immediately, we're not using a server */
- int fd;
+static void ivshmem_plain_realize(PCIDevice *dev, Error **errp)
+{
+ IVShmemState *s = IVSHMEM_COMMON(dev);
- if (s->shmobj == NULL) {
- error_report("Must specify 'chardev' or 'shm' to ivshmem");
- exit(1);
- }
+ if (!s->hostmem) {
+ error_setg(errp, "You must specify a 'memdev'");
+ return;
+ }
- IVSHMEM_DPRINTF("using shm_open (shm object = %s)\n", s->shmobj);
+ ivshmem_common_realize(dev, errp);
+}
- /* try opening with O_EXCL and if it succeeds zero the memory
- * by truncating to 0 */
- if ((fd = shm_open(s->shmobj, O_CREAT|O_RDWR|O_EXCL,
- S_IRWXU|S_IRWXG|S_IRWXO)) > 0) {
- /* truncate file to length PCI device's memory */
- if (ftruncate(fd, s->ivshmem_size) != 0) {
- error_report("could not truncate shared file");
- }
+static void ivshmem_plain_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
- } else if ((fd = shm_open(s->shmobj, O_CREAT|O_RDWR,
- S_IRWXU|S_IRWXG|S_IRWXO)) < 0) {
- error_report("could not open shared file");
- exit(1);
+ k->realize = ivshmem_plain_realize;
+ dc->props = ivshmem_plain_properties;
+ dc->vmsd = &ivshmem_plain_vmsd;
+}
- }
+static const TypeInfo ivshmem_plain_info = {
+ .name = TYPE_IVSHMEM_PLAIN,
+ .parent = TYPE_IVSHMEM_COMMON,
+ .instance_size = sizeof(IVShmemState),
+ .instance_init = ivshmem_plain_init,
+ .class_init = ivshmem_plain_class_init,
+};
- if (check_shm_size(s, fd) == -1) {
- exit(1);
- }
+static const VMStateDescription ivshmem_doorbell_vmsd = {
+ .name = TYPE_IVSHMEM_DOORBELL,
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .pre_load = ivshmem_pre_load,
+ .post_load = ivshmem_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_PCI_DEVICE(parent_obj, IVShmemState),
+ VMSTATE_MSIX(parent_obj, IVShmemState),
+ VMSTATE_UINT32(intrstatus, IVShmemState),
+ VMSTATE_UINT32(intrmask, IVShmemState),
+ VMSTATE_END_OF_LIST()
+ },
+};
- create_shared_memory_BAR(s, fd);
+static Property ivshmem_doorbell_properties[] = {
+ DEFINE_PROP_CHR("chardev", IVShmemState, server_chr),
+ DEFINE_PROP_UINT32("vectors", IVShmemState, vectors, 1),
+ DEFINE_PROP_BIT("ioeventfd", IVShmemState, features, IVSHMEM_IOEVENTFD,
+ true),
+ DEFINE_PROP_ON_OFF_AUTO("master", IVShmemState, master, ON_OFF_AUTO_OFF),
+ DEFINE_PROP_END_OF_LIST(),
+};
+static void ivshmem_doorbell_init(Object *obj)
+{
+ IVShmemState *s = IVSHMEM_DOORBELL(obj);
+
+ s->features |= (1 << IVSHMEM_MSI);
+ s->legacy_size = SIZE_MAX; /* whatever the server sends */
+}
+
+static void ivshmem_doorbell_realize(PCIDevice *dev, Error **errp)
+{
+ IVShmemState *s = IVSHMEM_COMMON(dev);
+
+ if (!s->server_chr) {
+ error_setg(errp, "You must specify a 'chardev'");
+ return;
}
- dev->config_write = ivshmem_write_config;
+ ivshmem_common_realize(dev, errp);
+}
- return 0;
+static void ivshmem_doorbell_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+ k->realize = ivshmem_doorbell_realize;
+ dc->props = ivshmem_doorbell_properties;
+ dc->vmsd = &ivshmem_doorbell_vmsd;
}
-static void pci_ivshmem_uninit(PCIDevice *dev)
+static const TypeInfo ivshmem_doorbell_info = {
+ .name = TYPE_IVSHMEM_DOORBELL,
+ .parent = TYPE_IVSHMEM_COMMON,
+ .instance_size = sizeof(IVShmemState),
+ .instance_init = ivshmem_doorbell_init,
+ .class_init = ivshmem_doorbell_class_init,
+};
+
+static int ivshmem_load_old(QEMUFile *f, void *opaque, int version_id)
{
- IVShmemState *s = IVSHMEM(dev);
+ IVShmemState *s = opaque;
+ PCIDevice *pdev = PCI_DEVICE(s);
+ int ret;
- if (s->migration_blocker) {
- migrate_del_blocker(s->migration_blocker);
- error_free(s->migration_blocker);
+ IVSHMEM_DPRINTF("ivshmem_load_old\n");
+
+ if (version_id != 0) {
+ return -EINVAL;
+ }
+
+ ret = ivshmem_pre_load(s);
+ if (ret) {
+ return ret;
+ }
+
+ ret = pci_device_load(pdev, f);
+ if (ret) {
+ return ret;
+ }
+
+ if (ivshmem_has_feature(s, IVSHMEM_MSI)) {
+ msix_load(pdev, f);
+ ivshmem_msix_vector_use(s);
+ } else {
+ s->intrstatus = qemu_get_be32(f);
+ s->intrmask = qemu_get_be32(f);
}
- memory_region_del_subregion(&s->bar, &s->ivshmem);
- vmstate_unregister_ram(&s->ivshmem, DEVICE(dev));
- unregister_savevm(DEVICE(dev), "ivshmem", s);
- fifo8_destroy(&s->incoming_fifo);
+ return 0;
}
+static bool test_msix(void *opaque, int version_id)
+{
+ IVShmemState *s = opaque;
+
+ return ivshmem_has_feature(s, IVSHMEM_MSI);
+}
+
+static bool test_no_msix(void *opaque, int version_id)
+{
+ return !test_msix(opaque, version_id);
+}
+
+static const VMStateDescription ivshmem_vmsd = {
+ .name = "ivshmem",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .pre_load = ivshmem_pre_load,
+ .post_load = ivshmem_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_PCI_DEVICE(parent_obj, IVShmemState),
+
+ VMSTATE_MSIX_TEST(parent_obj, IVShmemState, test_msix),
+ VMSTATE_UINT32_TEST(intrstatus, IVShmemState, test_no_msix),
+ VMSTATE_UINT32_TEST(intrmask, IVShmemState, test_no_msix),
+
+ VMSTATE_END_OF_LIST()
+ },
+ .load_state_old = ivshmem_load_old,
+ .minimum_version_id_old = 0
+};
+
static Property ivshmem_properties[] = {
DEFINE_PROP_CHR("chardev", IVShmemState, server_chr),
DEFINE_PROP_STRING("size", IVShmemState, sizearg),
DEFINE_PROP_UINT32("vectors", IVShmemState, vectors, 1),
- DEFINE_PROP_BIT("ioeventfd", IVShmemState, features, IVSHMEM_IOEVENTFD, false),
+ DEFINE_PROP_BIT("ioeventfd", IVShmemState, features, IVSHMEM_IOEVENTFD,
+ false),
DEFINE_PROP_BIT("msi", IVShmemState, features, IVSHMEM_MSI, true),
DEFINE_PROP_STRING("shm", IVShmemState, shmobj),
DEFINE_PROP_STRING("role", IVShmemState, role),
- DEFINE_PROP_UINT32("use64", IVShmemState, ivshmem_64bit, 1),
+ DEFINE_PROP_UINT32("use64", IVShmemState, not_legacy_32bit, 1),
DEFINE_PROP_END_OF_LIST(),
};
+static void desugar_shm(IVShmemState *s)
+{
+ Object *obj;
+ char *path;
+
+ obj = object_new("memory-backend-file");
+ path = g_strdup_printf("/dev/shm/%s", s->shmobj);
+ object_property_set_str(obj, path, "mem-path", &error_abort);
+ g_free(path);
+ object_property_set_int(obj, s->legacy_size, "size", &error_abort);
+ object_property_set_bool(obj, true, "share", &error_abort);
+ object_property_add_child(OBJECT(s), "internal-shm-backend", obj,
+ &error_abort);
+ user_creatable_complete(obj, &error_abort);
+ s->hostmem = MEMORY_BACKEND(obj);
+}
+
+static void ivshmem_realize(PCIDevice *dev, Error **errp)
+{
+ IVShmemState *s = IVSHMEM_COMMON(dev);
+
+ if (!qtest_enabled()) {
+ error_report("ivshmem is deprecated, please use ivshmem-plain"
+ " or ivshmem-doorbell instead");
+ }
+
+ if (!!s->server_chr + !!s->shmobj != 1) {
+ error_setg(errp, "You must specify either 'shm' or 'chardev'");
+ return;
+ }
+
+ if (s->sizearg == NULL) {
+ s->legacy_size = 4 << 20; /* 4 MB default */
+ } else {
+ char *end;
+ int64_t size = qemu_strtosz(s->sizearg, &end);
+ if (size < 0 || (size_t)size != size || *end != '\0'
+ || !is_power_of_2(size)) {
+ error_setg(errp, "Invalid size %s", s->sizearg);
+ return;
+ }
+ s->legacy_size = size;
+ }
+
+ /* check that role is reasonable */
+ if (s->role) {
+ if (strncmp(s->role, "peer", 5) == 0) {
+ s->master = ON_OFF_AUTO_OFF;
+ } else if (strncmp(s->role, "master", 7) == 0) {
+ s->master = ON_OFF_AUTO_ON;
+ } else {
+ error_setg(errp, "'role' must be 'peer' or 'master'");
+ return;
+ }
+ } else {
+ s->master = ON_OFF_AUTO_AUTO;
+ }
+
+ if (s->shmobj) {
+ desugar_shm(s);
+ }
+
+ /*
+ * Note: we don't use INTx with IVSHMEM_MSI at all, so this is a
+ * bald-faced lie then. But it's a backwards compatible lie.
+ */
+ pci_config_set_interrupt_pin(dev->config, 1);
+
+ ivshmem_common_realize(dev, errp);
+}
+
static void ivshmem_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
- k->init = pci_ivshmem_init;
- k->exit = pci_ivshmem_uninit;
- k->vendor_id = PCI_VENDOR_ID_IVSHMEM;
- k->device_id = PCI_DEVICE_ID_IVSHMEM;
- k->class_id = PCI_CLASS_MEMORY_RAM;
- dc->reset = ivshmem_reset;
+ k->realize = ivshmem_realize;
+ k->revision = 0;
+ dc->desc = "Inter-VM shared memory (legacy)";
dc->props = ivshmem_properties;
- set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+ dc->vmsd = &ivshmem_vmsd;
}
static const TypeInfo ivshmem_info = {
.name = TYPE_IVSHMEM,
- .parent = TYPE_PCI_DEVICE,
+ .parent = TYPE_IVSHMEM_COMMON,
.instance_size = sizeof(IVShmemState),
.class_init = ivshmem_class_init,
};
static void ivshmem_register_types(void)
{
+ type_register_static(&ivshmem_common_info);
+ type_register_static(&ivshmem_plain_info);
+ type_register_static(&ivshmem_doorbell_info);
type_register_static(&ivshmem_info);
}
diff --git a/qemu/hw/misc/macio/cuda.c b/qemu/hw/misc/macio/cuda.c
index f3984e3a2..f15f30110 100644
--- a/qemu/hw/misc/macio/cuda.c
+++ b/qemu/hw/misc/macio/cuda.c
@@ -22,11 +22,13 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/ppc/mac.h"
#include "hw/input/adb.h"
#include "qemu/timer.h"
#include "sysemu/sysemu.h"
+#include "qemu/cutils.h"
/* XXX: implement all timer modes */
@@ -57,6 +59,8 @@
#define IER_SET 0x80 /* set bits in IER */
#define IER_CLR 0 /* clear bits in IER */
#define SR_INT 0x04 /* Shift register full/empty */
+#define SR_DATA_INT 0x08
+#define SR_CLOCK_INT 0x10
#define T1_INT 0x40 /* Timer 1 interrupt */
#define T2_INT 0x20 /* Timer 2 interrupt */
@@ -103,11 +107,28 @@
#define CUDA_COMBINED_FORMAT_IIC 0x25
#define CUDA_TIMER_FREQ (4700000 / 6)
-#define CUDA_ADB_POLL_FREQ 50
/* CUDA returns time_t's offset from Jan 1, 1904, not 1970 */
#define RTC_OFFSET 2082844800
+/* CUDA registers */
+#define CUDA_REG_B 0x00
+#define CUDA_REG_A 0x01
+#define CUDA_REG_DIRB 0x02
+#define CUDA_REG_DIRA 0x03
+#define CUDA_REG_T1CL 0x04
+#define CUDA_REG_T1CH 0x05
+#define CUDA_REG_T1LL 0x06
+#define CUDA_REG_T1LH 0x07
+#define CUDA_REG_T2CL 0x08
+#define CUDA_REG_T2CH 0x09
+#define CUDA_REG_SR 0x0a
+#define CUDA_REG_ACR 0x0b
+#define CUDA_REG_PCR 0x0c
+#define CUDA_REG_IFR 0x0d
+#define CUDA_REG_IER 0x0e
+#define CUDA_REG_ANH 0x0f
+
static void cuda_update(CUDAState *s);
static void cuda_receive_packet_from_host(CUDAState *s,
const uint8_t *data, int len);
@@ -116,47 +137,48 @@ static void cuda_timer_update(CUDAState *s, CUDATimer *ti,
static void cuda_update_irq(CUDAState *s)
{
- if (s->ifr & s->ier & (SR_INT | T1_INT)) {
+ if (s->ifr & s->ier & (SR_INT | T1_INT | T2_INT)) {
qemu_irq_raise(s->irq);
} else {
qemu_irq_lower(s->irq);
}
}
-static uint64_t get_tb(uint64_t freq)
+static uint64_t get_tb(uint64_t time, uint64_t freq)
{
- return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL),
- freq, get_ticks_per_sec());
+ return muldiv64(time, freq, NANOSECONDS_PER_SECOND);
}
-static unsigned int get_counter(CUDATimer *s)
+static unsigned int get_counter(CUDATimer *ti)
{
int64_t d;
unsigned int counter;
uint64_t tb_diff;
+ uint64_t current_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
/* Reverse of the tb calculation algorithm that Mac OS X uses on bootup. */
- tb_diff = get_tb(s->frequency) - s->load_time;
- d = (tb_diff * 0xBF401675E5DULL) / (s->frequency << 24);
+ tb_diff = get_tb(current_time, ti->frequency) - ti->load_time;
+ d = (tb_diff * 0xBF401675E5DULL) / (ti->frequency << 24);
- if (s->index == 0) {
+ if (ti->index == 0) {
/* the timer goes down from latch to -1 (period of latch + 2) */
- if (d <= (s->counter_value + 1)) {
- counter = (s->counter_value - d) & 0xffff;
+ if (d <= (ti->counter_value + 1)) {
+ counter = (ti->counter_value - d) & 0xffff;
} else {
- counter = (d - (s->counter_value + 1)) % (s->latch + 2);
- counter = (s->latch - counter) & 0xffff;
+ counter = (d - (ti->counter_value + 1)) % (ti->latch + 2);
+ counter = (ti->latch - counter) & 0xffff;
}
} else {
- counter = (s->counter_value - d) & 0xffff;
+ counter = (ti->counter_value - d) & 0xffff;
}
return counter;
}
static void set_counter(CUDAState *s, CUDATimer *ti, unsigned int val)
{
- CUDA_DPRINTF("T%d.counter=%d\n", 1 + (ti->timer == NULL), val);
- ti->load_time = get_tb(s->frequency);
+ CUDA_DPRINTF("T%d.counter=%d\n", 1 + ti->index, val);
+ ti->load_time = get_tb(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL),
+ s->frequency);
ti->counter_value = val;
cuda_timer_update(s, ti, ti->load_time);
}
@@ -168,7 +190,7 @@ static int64_t get_next_irq_time(CUDATimer *s, int64_t current_time)
/* current counter value */
d = muldiv64(current_time - s->load_time,
- CUDA_TIMER_FREQ, get_ticks_per_sec());
+ CUDA_TIMER_FREQ, NANOSECONDS_PER_SECOND);
/* the timer goes down from latch to -1 (period of latch + 2) */
if (d <= (s->counter_value + 1)) {
counter = (s->counter_value - d) & 0xffff;
@@ -187,7 +209,7 @@ static int64_t get_next_irq_time(CUDATimer *s, int64_t current_time)
}
CUDA_DPRINTF("latch=%d counter=%" PRId64 " delta_next=%" PRId64 "\n",
s->latch, d, next_time - d);
- next_time = muldiv64(next_time, get_ticks_per_sec(), CUDA_TIMER_FREQ) +
+ next_time = muldiv64(next_time, NANOSECONDS_PER_SECOND, CUDA_TIMER_FREQ) +
s->load_time;
if (next_time <= current_time)
next_time = current_time + 1;
@@ -199,7 +221,7 @@ static void cuda_timer_update(CUDAState *s, CUDATimer *ti,
{
if (!ti->timer)
return;
- if ((s->acr & T1MODE) != T1MODE_CONT) {
+ if (ti->index == 0 && (s->acr & T1MODE) != T1MODE_CONT) {
timer_del(ti->timer);
} else {
ti->next_irq_time = get_next_irq_time(ti, current_time);
@@ -217,6 +239,41 @@ static void cuda_timer1(void *opaque)
cuda_update_irq(s);
}
+static void cuda_timer2(void *opaque)
+{
+ CUDAState *s = opaque;
+ CUDATimer *ti = &s->timers[1];
+
+ cuda_timer_update(s, ti, ti->next_irq_time);
+ s->ifr |= T2_INT;
+ cuda_update_irq(s);
+}
+
+static void cuda_set_sr_int(void *opaque)
+{
+ CUDAState *s = opaque;
+
+ CUDA_DPRINTF("CUDA: %s:%d\n", __func__, __LINE__);
+ s->ifr |= SR_INT;
+ cuda_update_irq(s);
+}
+
+static void cuda_delay_set_sr_int(CUDAState *s)
+{
+ int64_t expire;
+
+ if (s->dirb == 0xff) {
+ /* Not in Mac OS, fire the IRQ directly */
+ cuda_set_sr_int(s);
+ return;
+ }
+
+ CUDA_DPRINTF("CUDA: %s:%d\n", __func__, __LINE__);
+
+ expire = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 300 * SCALE_US;
+ timer_mod(s->sr_delay_timer, expire);
+}
+
static uint32_t cuda_readb(void *opaque, hwaddr addr)
{
CUDAState *s = opaque;
@@ -224,66 +281,68 @@ static uint32_t cuda_readb(void *opaque, hwaddr addr)
addr = (addr >> 9) & 0xf;
switch(addr) {
- case 0:
+ case CUDA_REG_B:
val = s->b;
break;
- case 1:
+ case CUDA_REG_A:
val = s->a;
break;
- case 2:
+ case CUDA_REG_DIRB:
val = s->dirb;
break;
- case 3:
+ case CUDA_REG_DIRA:
val = s->dira;
break;
- case 4:
+ case CUDA_REG_T1CL:
val = get_counter(&s->timers[0]) & 0xff;
s->ifr &= ~T1_INT;
cuda_update_irq(s);
break;
- case 5:
+ case CUDA_REG_T1CH:
val = get_counter(&s->timers[0]) >> 8;
cuda_update_irq(s);
break;
- case 6:
+ case CUDA_REG_T1LL:
val = s->timers[0].latch & 0xff;
break;
- case 7:
+ case CUDA_REG_T1LH:
/* XXX: check this */
val = (s->timers[0].latch >> 8) & 0xff;
break;
- case 8:
+ case CUDA_REG_T2CL:
val = get_counter(&s->timers[1]) & 0xff;
s->ifr &= ~T2_INT;
+ cuda_update_irq(s);
break;
- case 9:
+ case CUDA_REG_T2CH:
val = get_counter(&s->timers[1]) >> 8;
break;
- case 10:
+ case CUDA_REG_SR:
val = s->sr;
- s->ifr &= ~SR_INT;
+ s->ifr &= ~(SR_INT | SR_CLOCK_INT | SR_DATA_INT);
cuda_update_irq(s);
break;
- case 11:
+ case CUDA_REG_ACR:
val = s->acr;
break;
- case 12:
+ case CUDA_REG_PCR:
val = s->pcr;
break;
- case 13:
+ case CUDA_REG_IFR:
val = s->ifr;
- if (s->ifr & s->ier)
+ if (s->ifr & s->ier) {
val |= 0x80;
+ }
break;
- case 14:
+ case CUDA_REG_IER:
val = s->ier | 0x80;
break;
default:
- case 15:
+ case CUDA_REG_ANH:
val = s->anh;
break;
}
- if (addr != 13 || val != 0) {
+ if (addr != CUDA_REG_IFR || val != 0) {
CUDA_DPRINTF("read: reg=0x%x val=%02x\n", (int)addr, val);
}
@@ -298,61 +357,65 @@ static void cuda_writeb(void *opaque, hwaddr addr, uint32_t val)
CUDA_DPRINTF("write: reg=0x%x val=%02x\n", (int)addr, val);
switch(addr) {
- case 0:
+ case CUDA_REG_B:
s->b = val;
cuda_update(s);
break;
- case 1:
+ case CUDA_REG_A:
s->a = val;
break;
- case 2:
+ case CUDA_REG_DIRB:
s->dirb = val;
break;
- case 3:
+ case CUDA_REG_DIRA:
s->dira = val;
break;
- case 4:
+ case CUDA_REG_T1CL:
s->timers[0].latch = (s->timers[0].latch & 0xff00) | val;
cuda_timer_update(s, &s->timers[0], qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
break;
- case 5:
+ case CUDA_REG_T1CH:
s->timers[0].latch = (s->timers[0].latch & 0xff) | (val << 8);
s->ifr &= ~T1_INT;
set_counter(s, &s->timers[0], s->timers[0].latch);
break;
- case 6:
+ case CUDA_REG_T1LL:
s->timers[0].latch = (s->timers[0].latch & 0xff00) | val;
cuda_timer_update(s, &s->timers[0], qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
break;
- case 7:
+ case CUDA_REG_T1LH:
s->timers[0].latch = (s->timers[0].latch & 0xff) | (val << 8);
s->ifr &= ~T1_INT;
cuda_timer_update(s, &s->timers[0], qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
break;
- case 8:
- s->timers[1].latch = val;
- set_counter(s, &s->timers[1], val);
+ case CUDA_REG_T2CL:
+ s->timers[1].latch = (s->timers[1].latch & 0xff00) | val;
break;
- case 9:
- set_counter(s, &s->timers[1], (val << 8) | s->timers[1].latch);
+ case CUDA_REG_T2CH:
+ /* To ensure T2 generates an interrupt on zero crossing with the
+ common timer code, write the value directly from the latch to
+ the counter */
+ s->timers[1].latch = (s->timers[1].latch & 0xff) | (val << 8);
+ s->ifr &= ~T2_INT;
+ set_counter(s, &s->timers[1], s->timers[1].latch);
break;
- case 10:
+ case CUDA_REG_SR:
s->sr = val;
break;
- case 11:
+ case CUDA_REG_ACR:
s->acr = val;
cuda_timer_update(s, &s->timers[0], qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
cuda_update(s);
break;
- case 12:
+ case CUDA_REG_PCR:
s->pcr = val;
break;
- case 13:
+ case CUDA_REG_IFR:
/* reset bits */
s->ifr &= ~val;
cuda_update_irq(s);
break;
- case 14:
+ case CUDA_REG_IER:
if (val & IER_SET) {
/* set bits */
s->ier |= val & 0x7f;
@@ -363,7 +426,7 @@ static void cuda_writeb(void *opaque, hwaddr addr, uint32_t val)
cuda_update_irq(s);
break;
default:
- case 15:
+ case CUDA_REG_ANH:
s->anh = val;
break;
}
@@ -384,8 +447,7 @@ static void cuda_update(CUDAState *s)
if (s->data_out_index < sizeof(s->data_out)) {
CUDA_DPRINTF("send: %02x\n", s->sr);
s->data_out[s->data_out_index++] = s->sr;
- s->ifr |= SR_INT;
- cuda_update_irq(s);
+ cuda_delay_set_sr_int(s);
}
}
} else {
@@ -398,8 +460,7 @@ static void cuda_update(CUDAState *s)
if (s->data_in_index >= s->data_in_size) {
s->b = (s->b | TREQ);
}
- s->ifr |= SR_INT;
- cuda_update_irq(s);
+ cuda_delay_set_sr_int(s);
}
}
}
@@ -411,15 +472,13 @@ static void cuda_update(CUDAState *s)
s->b = (s->b | TREQ);
else
s->b = (s->b & ~TREQ);
- s->ifr |= SR_INT;
- cuda_update_irq(s);
+ cuda_delay_set_sr_int(s);
} else {
if (!(s->last_b & TIP)) {
/* handle end of host to cuda transfer */
packet_received = (s->data_out_index > 0);
/* always an IRQ at the end of transfer */
- s->ifr |= SR_INT;
- cuda_update_irq(s);
+ cuda_delay_set_sr_int(s);
}
/* signal if there is data to read */
if (s->data_in_index < s->data_in_size) {
@@ -456,8 +515,7 @@ static void cuda_send_packet_to_host(CUDAState *s,
s->data_in_size = len;
s->data_in_index = 0;
cuda_update(s);
- s->ifr |= SR_INT;
- cuda_update_irq(s);
+ cuda_delay_set_sr_int(s);
}
static void cuda_adb_poll(void *opaque)
@@ -466,7 +524,7 @@ static void cuda_adb_poll(void *opaque)
uint8_t obuf[ADB_MAX_OUT_LEN + 2];
int olen;
- olen = adb_poll(&s->adb_bus, obuf + 2);
+ olen = adb_poll(&s->adb_bus, obuf + 2, s->adb_poll_mask);
if (olen > 0) {
obuf[0] = ADB_PACKET;
obuf[1] = 0x40; /* polled data */
@@ -474,75 +532,213 @@ static void cuda_adb_poll(void *opaque)
}
timer_mod(s->adb_poll_timer,
qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
- (get_ticks_per_sec() / CUDA_ADB_POLL_FREQ));
+ (NANOSECONDS_PER_SECOND / (1000 / s->autopoll_rate_ms)));
}
-static void cuda_receive_packet(CUDAState *s,
- const uint8_t *data, int len)
+/* description of commands */
+typedef struct CudaCommand {
+ uint8_t command;
+ const char *name;
+ bool (*handler)(CUDAState *s,
+ const uint8_t *in_args, int in_len,
+ uint8_t *out_args, int *out_len);
+} CudaCommand;
+
+static bool cuda_cmd_autopoll(CUDAState *s,
+ const uint8_t *in_data, int in_len,
+ uint8_t *out_data, int *out_len)
{
- uint8_t obuf[16];
int autopoll;
+
+ if (in_len != 1) {
+ return false;
+ }
+
+ autopoll = (in_data[0] != 0);
+ if (autopoll != s->autopoll) {
+ s->autopoll = autopoll;
+ if (autopoll) {
+ timer_mod(s->adb_poll_timer,
+ qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
+ (NANOSECONDS_PER_SECOND / (1000 / s->autopoll_rate_ms)));
+ } else {
+ timer_del(s->adb_poll_timer);
+ }
+ }
+ return true;
+}
+
+static bool cuda_cmd_set_autorate(CUDAState *s,
+ const uint8_t *in_data, int in_len,
+ uint8_t *out_data, int *out_len)
+{
+ if (in_len != 1) {
+ return false;
+ }
+
+ /* we don't want a period of 0 ms */
+ /* FIXME: check what real hardware does */
+ if (in_data[0] == 0) {
+ return false;
+ }
+
+ s->autopoll_rate_ms = in_data[0];
+ if (s->autopoll) {
+ timer_mod(s->adb_poll_timer,
+ qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
+ (NANOSECONDS_PER_SECOND / (1000 / s->autopoll_rate_ms)));
+ }
+ return true;
+}
+
+static bool cuda_cmd_set_device_list(CUDAState *s,
+ const uint8_t *in_data, int in_len,
+ uint8_t *out_data, int *out_len)
+{
+ if (in_len != 2) {
+ return false;
+ }
+
+ s->adb_poll_mask = (((uint16_t)in_data[0]) << 8) | in_data[1];
+ return true;
+}
+
+static bool cuda_cmd_powerdown(CUDAState *s,
+ const uint8_t *in_data, int in_len,
+ uint8_t *out_data, int *out_len)
+{
+ if (in_len != 0) {
+ return false;
+ }
+
+ qemu_system_shutdown_request();
+ return true;
+}
+
+static bool cuda_cmd_reset_system(CUDAState *s,
+ const uint8_t *in_data, int in_len,
+ uint8_t *out_data, int *out_len)
+{
+ if (in_len != 0) {
+ return false;
+ }
+
+ qemu_system_reset_request();
+ return true;
+}
+
+static bool cuda_cmd_set_file_server_flag(CUDAState *s,
+ const uint8_t *in_data, int in_len,
+ uint8_t *out_data, int *out_len)
+{
+ if (in_len != 1) {
+ return false;
+ }
+
+ qemu_log_mask(LOG_UNIMP,
+ "CUDA: unimplemented command FILE_SERVER_FLAG %d\n",
+ in_data[0]);
+ return true;
+}
+
+static bool cuda_cmd_set_power_message(CUDAState *s,
+ const uint8_t *in_data, int in_len,
+ uint8_t *out_data, int *out_len)
+{
+ if (in_len != 1) {
+ return false;
+ }
+
+ qemu_log_mask(LOG_UNIMP,
+ "CUDA: unimplemented command SET_POWER_MESSAGE %d\n",
+ in_data[0]);
+ return true;
+}
+
+static bool cuda_cmd_get_time(CUDAState *s,
+ const uint8_t *in_data, int in_len,
+ uint8_t *out_data, int *out_len)
+{
uint32_t ti;
- switch(data[0]) {
- case CUDA_AUTOPOLL:
- autopoll = (data[1] != 0);
- if (autopoll != s->autopoll) {
- s->autopoll = autopoll;
- if (autopoll) {
- timer_mod(s->adb_poll_timer,
- qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
- (get_ticks_per_sec() / CUDA_ADB_POLL_FREQ));
+ if (in_len != 0) {
+ return false;
+ }
+
+ ti = s->tick_offset + (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)
+ / NANOSECONDS_PER_SECOND);
+ out_data[0] = ti >> 24;
+ out_data[1] = ti >> 16;
+ out_data[2] = ti >> 8;
+ out_data[3] = ti;
+ *out_len = 4;
+ return true;
+}
+
+static bool cuda_cmd_set_time(CUDAState *s,
+ const uint8_t *in_data, int in_len,
+ uint8_t *out_data, int *out_len)
+{
+ uint32_t ti;
+
+ if (in_len != 4) {
+ return false;
+ }
+
+ ti = (((uint32_t)in_data[0]) << 24) + (((uint32_t)in_data[1]) << 16)
+ + (((uint32_t)in_data[2]) << 8) + in_data[3];
+ s->tick_offset = ti - (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)
+ / NANOSECONDS_PER_SECOND);
+ return true;
+}
+
+static const CudaCommand handlers[] = {
+ { CUDA_AUTOPOLL, "AUTOPOLL", cuda_cmd_autopoll },
+ { CUDA_SET_AUTO_RATE, "SET_AUTO_RATE", cuda_cmd_set_autorate },
+ { CUDA_SET_DEVICE_LIST, "SET_DEVICE_LIST", cuda_cmd_set_device_list },
+ { CUDA_POWERDOWN, "POWERDOWN", cuda_cmd_powerdown },
+ { CUDA_RESET_SYSTEM, "RESET_SYSTEM", cuda_cmd_reset_system },
+ { CUDA_FILE_SERVER_FLAG, "FILE_SERVER_FLAG",
+ cuda_cmd_set_file_server_flag },
+ { CUDA_SET_POWER_MESSAGES, "SET_POWER_MESSAGES",
+ cuda_cmd_set_power_message },
+ { CUDA_GET_TIME, "GET_TIME", cuda_cmd_get_time },
+ { CUDA_SET_TIME, "SET_TIME", cuda_cmd_set_time },
+};
+
+static void cuda_receive_packet(CUDAState *s,
+ const uint8_t *data, int len)
+{
+ uint8_t obuf[16] = { CUDA_PACKET, 0, data[0] };
+ int i, out_len = 0;
+
+ for (i = 0; i < ARRAY_SIZE(handlers); i++) {
+ const CudaCommand *desc = &handlers[i];
+ if (desc->command == data[0]) {
+ CUDA_DPRINTF("handling command %s\n", desc->name);
+ out_len = 0;
+ if (desc->handler(s, data + 1, len - 1, obuf + 3, &out_len)) {
+ cuda_send_packet_to_host(s, obuf, 3 + out_len);
} else {
- timer_del(s->adb_poll_timer);
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "CUDA: %s: wrong parameters %d\n",
+ desc->name, len);
+ obuf[0] = ERROR_PACKET;
+ obuf[1] = 0x5; /* bad parameters */
+ obuf[2] = CUDA_PACKET;
+ obuf[3] = data[0];
+ cuda_send_packet_to_host(s, obuf, 4);
}
+ return;
}
- obuf[0] = CUDA_PACKET;
- obuf[1] = data[1];
- cuda_send_packet_to_host(s, obuf, 2);
- break;
- case CUDA_SET_TIME:
- ti = (((uint32_t)data[1]) << 24) + (((uint32_t)data[2]) << 16) + (((uint32_t)data[3]) << 8) + data[4];
- s->tick_offset = ti - (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / get_ticks_per_sec());
- obuf[0] = CUDA_PACKET;
- obuf[1] = 0;
- obuf[2] = 0;
- cuda_send_packet_to_host(s, obuf, 3);
- break;
- case CUDA_GET_TIME:
- ti = s->tick_offset + (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / get_ticks_per_sec());
- obuf[0] = CUDA_PACKET;
- obuf[1] = 0;
- obuf[2] = 0;
- obuf[3] = ti >> 24;
- obuf[4] = ti >> 16;
- obuf[5] = ti >> 8;
- obuf[6] = ti;
- cuda_send_packet_to_host(s, obuf, 7);
- break;
- case CUDA_FILE_SERVER_FLAG:
- case CUDA_SET_DEVICE_LIST:
- case CUDA_SET_AUTO_RATE:
- case CUDA_SET_POWER_MESSAGES:
- obuf[0] = CUDA_PACKET;
- obuf[1] = 0;
- cuda_send_packet_to_host(s, obuf, 2);
- break;
- case CUDA_POWERDOWN:
- obuf[0] = CUDA_PACKET;
- obuf[1] = 0;
- cuda_send_packet_to_host(s, obuf, 2);
- qemu_system_shutdown_request();
- break;
- case CUDA_RESET_SYSTEM:
- obuf[0] = CUDA_PACKET;
- obuf[1] = 0;
- cuda_send_packet_to_host(s, obuf, 2);
- qemu_system_reset_request();
- break;
- default:
- break;
}
+
+ qemu_log_mask(LOG_GUEST_ERROR, "CUDA: unknown command 0x%02x\n", data[0]);
+ obuf[0] = ERROR_PACKET;
+ obuf[1] = 0x2; /* unknown command */
+ obuf[2] = CUDA_PACKET;
+ obuf[3] = data[0];
+ cuda_send_packet_to_host(s, obuf, 4);
}
static void cuda_receive_packet_from_host(CUDAState *s,
@@ -560,19 +756,21 @@ static void cuda_receive_packet_from_host(CUDAState *s,
switch(data[0]) {
case ADB_PACKET:
{
- uint8_t obuf[ADB_MAX_OUT_LEN + 2];
+ uint8_t obuf[ADB_MAX_OUT_LEN + 3];
int olen;
olen = adb_request(&s->adb_bus, obuf + 2, data + 1, len - 1);
if (olen > 0) {
obuf[0] = ADB_PACKET;
obuf[1] = 0x00;
+ cuda_send_packet_to_host(s, obuf, olen + 2);
} else {
/* error */
obuf[0] = ADB_PACKET;
obuf[1] = -olen;
+ obuf[2] = data[1];
olen = 0;
+ cuda_send_packet_to_host(s, obuf, olen + 3);
}
- cuda_send_packet_to_host(s, obuf, olen + 2);
}
break;
case CUDA_PACKET:
@@ -638,15 +836,17 @@ static const VMStateDescription vmstate_cuda_timer = {
static const VMStateDescription vmstate_cuda = {
.name = "cuda",
- .version_id = 2,
- .minimum_version_id = 2,
+ .version_id = 4,
+ .minimum_version_id = 4,
.fields = (VMStateField[]) {
VMSTATE_UINT8(a, CUDAState),
VMSTATE_UINT8(b, CUDAState),
+ VMSTATE_UINT8(last_b, CUDAState),
VMSTATE_UINT8(dira, CUDAState),
VMSTATE_UINT8(dirb, CUDAState),
VMSTATE_UINT8(sr, CUDAState),
VMSTATE_UINT8(acr, CUDAState),
+ VMSTATE_UINT8(last_acr, CUDAState),
VMSTATE_UINT8(pcr, CUDAState),
VMSTATE_UINT8(ifr, CUDAState),
VMSTATE_UINT8(ier, CUDAState),
@@ -655,12 +855,15 @@ static const VMStateDescription vmstate_cuda = {
VMSTATE_INT32(data_in_index, CUDAState),
VMSTATE_INT32(data_out_index, CUDAState),
VMSTATE_UINT8(autopoll, CUDAState),
+ VMSTATE_UINT8(autopoll_rate_ms, CUDAState),
+ VMSTATE_UINT16(adb_poll_mask, CUDAState),
VMSTATE_BUFFER(data_in, CUDAState),
VMSTATE_BUFFER(data_out, CUDAState),
VMSTATE_UINT32(tick_offset, CUDAState),
VMSTATE_STRUCT_ARRAY(timers, CUDAState, 2, 1,
vmstate_cuda_timer, CUDATimer),
VMSTATE_TIMER_PTR(adb_poll_timer, CUDAState),
+ VMSTATE_TIMER_PTR(sr_delay_timer, CUDAState),
VMSTATE_END_OF_LIST()
}
};
@@ -671,7 +874,7 @@ static void cuda_reset(DeviceState *dev)
s->b = 0;
s->a = 0;
- s->dirb = 0;
+ s->dirb = 0xff;
s->dira = 0;
s->sr = 0;
s->acr = 0;
@@ -688,8 +891,9 @@ static void cuda_reset(DeviceState *dev)
s->timers[0].latch = 0xffff;
set_counter(s, &s->timers[0], 0xffff);
- s->timers[1].latch = 0;
- set_counter(s, &s->timers[1], 0xffff);
+ s->timers[1].latch = 0xffff;
+
+ s->sr_delay_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, cuda_set_sr_int, s);
}
static void cuda_realizefn(DeviceState *dev, Error **errp)
@@ -699,12 +903,15 @@ static void cuda_realizefn(DeviceState *dev, Error **errp)
s->timers[0].timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, cuda_timer1, s);
s->timers[0].frequency = s->frequency;
- s->timers[1].frequency = s->frequency;
+ s->timers[1].timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, cuda_timer2, s);
+ s->timers[1].frequency = (SCALE_US * 6000) / 4700;
qemu_get_timedate(&tm, 0);
s->tick_offset = (uint32_t)mktimegm(&tm) + RTC_OFFSET;
s->adb_poll_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, cuda_adb_poll, s);
+ s->autopoll_rate_ms = 20;
+ s->adb_poll_mask = 0xffff;
}
static void cuda_initfn(Object *obj)
@@ -713,7 +920,7 @@ static void cuda_initfn(Object *obj)
CUDAState *s = CUDA(obj);
int i;
- memory_region_init_io(&s->mem, NULL, &cuda_ops, s, "cuda", 0x2000);
+ memory_region_init_io(&s->mem, obj, &cuda_ops, s, "cuda", 0x2000);
sysbus_init_mmio(d, &s->mem);
sysbus_init_irq(d, &s->irq);
@@ -738,6 +945,7 @@ static void cuda_class_init(ObjectClass *oc, void *data)
dc->reset = cuda_reset;
dc->vmsd = &vmstate_cuda;
dc->props = cuda_properties;
+ set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
}
static const TypeInfo cuda_type_info = {
diff --git a/qemu/hw/misc/macio/mac_dbdma.c b/qemu/hw/misc/macio/mac_dbdma.c
index b25e8511b..6051f17db 100644
--- a/qemu/hw/misc/macio/mac_dbdma.c
+++ b/qemu/hw/misc/macio/mac_dbdma.c
@@ -36,6 +36,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/isa/isa.h"
#include "hw/ppc/mac_dbdma.h"
@@ -556,12 +557,13 @@ void DBDMA_register_channel(void *dbdma, int nchan, qemu_irq irq,
DBDMA_DPRINTF("DBDMA_register_channel 0x%x\n", nchan);
+ assert(rw);
+ assert(flush);
+
ch->irq = irq;
- ch->channel = nchan;
ch->rw = rw;
ch->flush = flush;
ch->io.opaque = opaque;
- ch->io.channel = ch;
}
static void
@@ -590,10 +592,11 @@ dbdma_control_write(DBDMA_channel *ch)
if ((ch->regs[DBDMA_STATUS] & RUN) && !(status & RUN)) {
/* RUN is cleared */
status &= ~(ACTIVE|DEAD);
- if ((status & FLUSH) && ch->flush) {
- ch->flush(&ch->io);
- status &= ~FLUSH;
- }
+ }
+
+ if ((status & FLUSH) && ch->flush) {
+ ch->flush(&ch->io);
+ status &= ~FLUSH;
}
DBDMA_DPRINTF(" status 0x%08x\n", status);
@@ -603,9 +606,6 @@ dbdma_control_write(DBDMA_channel *ch)
if (status & ACTIVE) {
DBDMA_kick(dbdma_from_ch(ch));
}
- if ((status & FLUSH) && ch->flush) {
- ch->flush(&ch->io);
- }
}
static void dbdma_write(void *opaque, hwaddr addr,
@@ -715,20 +715,52 @@ static const MemoryRegionOps dbdma_ops = {
},
};
-static const VMStateDescription vmstate_dbdma_channel = {
- .name = "dbdma_channel",
+static const VMStateDescription vmstate_dbdma_io = {
+ .name = "dbdma_io",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT64(addr, struct DBDMA_io),
+ VMSTATE_INT32(len, struct DBDMA_io),
+ VMSTATE_INT32(is_last, struct DBDMA_io),
+ VMSTATE_INT32(is_dma_out, struct DBDMA_io),
+ VMSTATE_BOOL(processing, struct DBDMA_io),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_dbdma_cmd = {
+ .name = "dbdma_cmd",
.version_id = 0,
.minimum_version_id = 0,
.fields = (VMStateField[]) {
+ VMSTATE_UINT16(req_count, dbdma_cmd),
+ VMSTATE_UINT16(command, dbdma_cmd),
+ VMSTATE_UINT32(phy_addr, dbdma_cmd),
+ VMSTATE_UINT32(cmd_dep, dbdma_cmd),
+ VMSTATE_UINT16(res_count, dbdma_cmd),
+ VMSTATE_UINT16(xfer_status, dbdma_cmd),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_dbdma_channel = {
+ .name = "dbdma_channel",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
VMSTATE_UINT32_ARRAY(regs, struct DBDMA_channel, DBDMA_REGS),
+ VMSTATE_STRUCT(io, struct DBDMA_channel, 0, vmstate_dbdma_io, DBDMA_io),
+ VMSTATE_STRUCT(current, struct DBDMA_channel, 0, vmstate_dbdma_cmd,
+ dbdma_cmd),
VMSTATE_END_OF_LIST()
}
};
static const VMStateDescription vmstate_dbdma = {
.name = "dbdma",
- .version_id = 2,
- .minimum_version_id = 2,
+ .version_id = 3,
+ .minimum_version_id = 3,
.fields = (VMStateField[]) {
VMSTATE_STRUCT_ARRAY(channels, DBDMAState, DBDMA_CHANNELS, 1,
vmstate_dbdma_channel, DBDMA_channel),
@@ -745,6 +777,20 @@ static void dbdma_reset(void *opaque)
memset(s->channels[i].regs, 0, DBDMA_SIZE);
}
+static void dbdma_unassigned_rw(DBDMA_io *io)
+{
+ DBDMA_channel *ch = io->channel;
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: use of unassigned channel %d\n",
+ __func__, ch->channel);
+}
+
+static void dbdma_unassigned_flush(DBDMA_io *io)
+{
+ DBDMA_channel *ch = io->channel;
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: use of unassigned channel %d\n",
+ __func__, ch->channel);
+}
+
void* DBDMA_init (MemoryRegion **dbdma_mem)
{
DBDMAState *s;
@@ -754,7 +800,13 @@ void* DBDMA_init (MemoryRegion **dbdma_mem)
for (i = 0; i < DBDMA_CHANNELS; i++) {
DBDMA_io *io = &s->channels[i].io;
+ DBDMA_channel *ch = &s->channels[i];
qemu_iovec_init(&io->iov, 1);
+
+ ch->rw = dbdma_unassigned_rw;
+ ch->flush = dbdma_unassigned_flush;
+ ch->channel = i;
+ ch->io.channel = ch;
}
memory_region_init_io(&s->mem, NULL, &dbdma_ops, s, "dbdma", 0x1000);
diff --git a/qemu/hw/misc/macio/macio.c b/qemu/hw/misc/macio/macio.c
index e3c0242d4..be03926b9 100644
--- a/qemu/hw/misc/macio/macio.c
+++ b/qemu/hw/misc/macio/macio.c
@@ -22,6 +22,8 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "hw/hw.h"
#include "hw/ppc/mac.h"
#include "hw/pci/pci.h"
@@ -105,10 +107,10 @@ static void macio_escc_legacy_setup(MacIOState *macio_state)
0xF0, 0xE0,
};
- memory_region_init(escc_legacy, NULL, "escc-legacy", 256);
+ memory_region_init(escc_legacy, OBJECT(macio_state), "escc-legacy", 256);
for (i = 0; i < ARRAY_SIZE(maps); i += 2) {
MemoryRegion *port = g_new(MemoryRegion, 1);
- memory_region_init_alias(port, NULL, "escc-legacy-port",
+ memory_region_init_alias(port, OBJECT(macio_state), "escc-legacy-port",
macio_state->escc_mem, maps[i+1], 0x2);
memory_region_add_subregion(escc_legacy, maps[i], port);
}
@@ -131,6 +133,10 @@ static void macio_common_realize(PCIDevice *d, Error **errp)
MacIOState *s = MACIO(d);
SysBusDevice *sysbus_dev;
Error *err = NULL;
+ MemoryRegion *dbdma_mem;
+
+ s->dbdma = DBDMA_init(&dbdma_mem);
+ memory_region_add_subregion(&s->bar, 0x08000, dbdma_mem);
object_property_set_bool(OBJECT(&s->cuda), true, "realized", &err);
if (err) {
@@ -248,7 +254,7 @@ static uint64_t timer_read(void *opaque, hwaddr addr, unsigned size)
uint64_t systime = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
uint64_t kltime;
- kltime = muldiv64(systime, 4194300, get_ticks_per_sec() * 4);
+ kltime = muldiv64(systime, 4194300, NANOSECONDS_PER_SECOND * 4);
kltime = muldiv64(kltime, 18432000, 1048575);
switch (addr) {
@@ -328,16 +334,12 @@ static void macio_newworld_init(Object *obj)
static void macio_instance_init(Object *obj)
{
MacIOState *s = MACIO(obj);
- MemoryRegion *dbdma_mem;
- memory_region_init(&s->bar, NULL, "macio", 0x80000);
+ memory_region_init(&s->bar, obj, "macio", 0x80000);
object_initialize(&s->cuda, sizeof(s->cuda), TYPE_CUDA);
qdev_set_parent_bus(DEVICE(&s->cuda), sysbus_get_default());
object_property_add_child(obj, "cuda", OBJECT(&s->cuda), NULL);
-
- s->dbdma = DBDMA_init(&dbdma_mem);
- memory_region_add_subregion(&s->bar, 0x08000, dbdma_mem);
}
static const VMStateDescription vmstate_macio_oldworld = {
@@ -393,6 +395,7 @@ static void macio_class_init(ObjectClass *klass, void *data)
k->vendor_id = PCI_VENDOR_ID_APPLE;
k->class_id = PCI_CLASS_OTHERS << 8;
dc->props = macio_properties;
+ set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
}
static const TypeInfo macio_oldworld_type_info = {
diff --git a/qemu/hw/misc/max111x.c b/qemu/hw/misc/max111x.c
index bef3651d6..9014f0f70 100644
--- a/qemu/hw/misc/max111x.c
+++ b/qemu/hw/misc/max111x.c
@@ -10,7 +10,8 @@
* GNU GPL, version 2 or (at your option) any later version.
*/
-#include "hw/ssi.h"
+#include "qemu/osdep.h"
+#include "hw/ssi/ssi.h"
typedef struct {
SSISlave parent_obj;
diff --git a/qemu/hw/misc/milkymist-hpdmc.c b/qemu/hw/misc/milkymist-hpdmc.c
index f5f4c1b34..b97000fc4 100644
--- a/qemu/hw/misc/milkymist-hpdmc.c
+++ b/qemu/hw/misc/milkymist-hpdmc.c
@@ -21,6 +21,7 @@
* http://www.milkymist.org/socdoc/hpdmc.pdf
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/sysbus.h"
#include "trace.h"
diff --git a/qemu/hw/misc/milkymist-pfpu.c b/qemu/hw/misc/milkymist-pfpu.c
index 08b604f13..57acd7b36 100644
--- a/qemu/hw/misc/milkymist-pfpu.c
+++ b/qemu/hw/misc/milkymist-pfpu.c
@@ -22,6 +22,7 @@
*
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/sysbus.h"
#include "trace.h"
diff --git a/qemu/hw/misc/mips_cmgcr.c b/qemu/hw/misc/mips_cmgcr.c
new file mode 100644
index 000000000..37be23995
--- /dev/null
+++ b/qemu/hw/misc/mips_cmgcr.c
@@ -0,0 +1,160 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2012 MIPS Technologies, Inc. All rights reserved.
+ * Authors: Sanjay Lal <sanjayl@kymasys.com>
+ *
+ * Copyright (C) 2015 Imagination Technologies
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/hw.h"
+#include "hw/sysbus.h"
+#include "sysemu/sysemu.h"
+#include "hw/misc/mips_cmgcr.h"
+#include "hw/misc/mips_cpc.h"
+
+static inline bool is_cpc_connected(MIPSGCRState *s)
+{
+ return s->cpc_mr != NULL;
+}
+
+static inline void update_cpc_base(MIPSGCRState *gcr, uint64_t val)
+{
+ if (is_cpc_connected(gcr)) {
+ gcr->cpc_base = val & GCR_CPC_BASE_MSK;
+ memory_region_transaction_begin();
+ memory_region_set_address(gcr->cpc_mr,
+ gcr->cpc_base & GCR_CPC_BASE_CPCBASE_MSK);
+ memory_region_set_enabled(gcr->cpc_mr,
+ gcr->cpc_base & GCR_CPC_BASE_CPCEN_MSK);
+ memory_region_transaction_commit();
+ }
+}
+
+/* Read GCR registers */
+static uint64_t gcr_read(void *opaque, hwaddr addr, unsigned size)
+{
+ MIPSGCRState *gcr = (MIPSGCRState *) opaque;
+
+ switch (addr) {
+ /* Global Control Block Register */
+ case GCR_CONFIG_OFS:
+ /* Set PCORES to 0 */
+ return 0;
+ case GCR_BASE_OFS:
+ return gcr->gcr_base;
+ case GCR_REV_OFS:
+ return gcr->gcr_rev;
+ case GCR_CPC_BASE_OFS:
+ return gcr->cpc_base;
+ case GCR_CPC_STATUS_OFS:
+ return is_cpc_connected(gcr);
+ case GCR_L2_CONFIG_OFS:
+ /* L2 BYPASS */
+ return GCR_L2_CONFIG_BYPASS_MSK;
+ /* Core-Local and Core-Other Control Blocks */
+ case MIPS_CLCB_OFS + GCR_CL_CONFIG_OFS:
+ case MIPS_COCB_OFS + GCR_CL_CONFIG_OFS:
+ /* Set PVP to # of VPs - 1 */
+ return gcr->num_vps - 1;
+ case MIPS_CLCB_OFS + GCR_CL_OTHER_OFS:
+ return 0;
+ default:
+ qemu_log_mask(LOG_UNIMP, "Read %d bytes at GCR offset 0x%" HWADDR_PRIx
+ "\n", size, addr);
+ return 0;
+ }
+ return 0;
+}
+
+/* Write GCR registers */
+static void gcr_write(void *opaque, hwaddr addr, uint64_t data, unsigned size)
+{
+ MIPSGCRState *gcr = (MIPSGCRState *)opaque;
+
+ switch (addr) {
+ case GCR_CPC_BASE_OFS:
+ update_cpc_base(gcr, data);
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP, "Write %d bytes at GCR offset 0x%" HWADDR_PRIx
+ " 0x%" PRIx64 "\n", size, addr, data);
+ break;
+ }
+}
+
+static const MemoryRegionOps gcr_ops = {
+ .read = gcr_read,
+ .write = gcr_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .impl = {
+ .max_access_size = 8,
+ },
+};
+
+static void mips_gcr_init(Object *obj)
+{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+ MIPSGCRState *s = MIPS_GCR(obj);
+
+ object_property_add_link(obj, "cpc", TYPE_MEMORY_REGION,
+ (Object **)&s->cpc_mr,
+ qdev_prop_allow_set_link_before_realize,
+ OBJ_PROP_LINK_UNREF_ON_RELEASE,
+ &error_abort);
+
+ memory_region_init_io(&s->iomem, OBJECT(s), &gcr_ops, s,
+ "mips-gcr", GCR_ADDRSPACE_SZ);
+ sysbus_init_mmio(sbd, &s->iomem);
+}
+
+static void mips_gcr_reset(DeviceState *dev)
+{
+ MIPSGCRState *s = MIPS_GCR(dev);
+
+ update_cpc_base(s, 0);
+}
+
+static const VMStateDescription vmstate_mips_gcr = {
+ .name = "mips-gcr",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT64(cpc_base, MIPSGCRState),
+ VMSTATE_END_OF_LIST()
+ },
+};
+
+static Property mips_gcr_properties[] = {
+ DEFINE_PROP_INT32("num-vp", MIPSGCRState, num_vps, 1),
+ DEFINE_PROP_INT32("gcr-rev", MIPSGCRState, gcr_rev, 0x800),
+ DEFINE_PROP_UINT64("gcr-base", MIPSGCRState, gcr_base, GCR_BASE_ADDR),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void mips_gcr_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ dc->props = mips_gcr_properties;
+ dc->vmsd = &vmstate_mips_gcr;
+ dc->reset = mips_gcr_reset;
+}
+
+static const TypeInfo mips_gcr_info = {
+ .name = TYPE_MIPS_GCR,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(MIPSGCRState),
+ .instance_init = mips_gcr_init,
+ .class_init = mips_gcr_class_init,
+};
+
+static void mips_gcr_register_types(void)
+{
+ type_register_static(&mips_gcr_info);
+}
+
+type_init(mips_gcr_register_types)
diff --git a/qemu/hw/misc/mips_cpc.c b/qemu/hw/misc/mips_cpc.c
new file mode 100644
index 000000000..d2b8e42da
--- /dev/null
+++ b/qemu/hw/misc/mips_cpc.c
@@ -0,0 +1,177 @@
+/*
+ * Cluster Power Controller emulation
+ *
+ * Copyright (c) 2016 Imagination Technologies
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/sysbus.h"
+
+#include "hw/misc/mips_cpc.h"
+
+static inline uint64_t cpc_vp_run_mask(MIPSCPCState *cpc)
+{
+ return (1ULL << cpc->num_vp) - 1;
+}
+
+static void cpc_run_vp(MIPSCPCState *cpc, uint64_t vp_run)
+{
+ CPUState *cs = first_cpu;
+
+ CPU_FOREACH(cs) {
+ uint64_t i = 1ULL << cs->cpu_index;
+ if (i & vp_run & ~cpc->vp_running) {
+ cpu_interrupt(cs, CPU_INTERRUPT_WAKE);
+ cpc->vp_running |= i;
+ }
+ }
+}
+
+static void cpc_stop_vp(MIPSCPCState *cpc, uint64_t vp_stop)
+{
+ CPUState *cs = first_cpu;
+
+ CPU_FOREACH(cs) {
+ uint64_t i = 1ULL << cs->cpu_index;
+ if (i & vp_stop & cpc->vp_running) {
+ cs->halted = 1;
+ cpu_reset_interrupt(cs, CPU_INTERRUPT_WAKE);
+ cpc->vp_running &= ~i;
+ }
+ }
+}
+
+static void cpc_write(void *opaque, hwaddr offset, uint64_t data,
+ unsigned size)
+{
+ MIPSCPCState *s = opaque;
+
+ switch (offset) {
+ case CPC_CL_BASE_OFS + CPC_VP_RUN_OFS:
+ case CPC_CO_BASE_OFS + CPC_VP_RUN_OFS:
+ cpc_run_vp(s, data & cpc_vp_run_mask(s));
+ break;
+ case CPC_CL_BASE_OFS + CPC_VP_STOP_OFS:
+ case CPC_CO_BASE_OFS + CPC_VP_STOP_OFS:
+ cpc_stop_vp(s, data & cpc_vp_run_mask(s));
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP,
+ "%s: Bad offset 0x%x\n", __func__, (int)offset);
+ break;
+ }
+
+ return;
+}
+
+static uint64_t cpc_read(void *opaque, hwaddr offset, unsigned size)
+{
+ MIPSCPCState *s = opaque;
+
+ switch (offset) {
+ case CPC_CL_BASE_OFS + CPC_VP_RUNNING_OFS:
+ case CPC_CO_BASE_OFS + CPC_VP_RUNNING_OFS:
+ return s->vp_running;
+ default:
+ qemu_log_mask(LOG_UNIMP,
+ "%s: Bad offset 0x%x\n", __func__, (int)offset);
+ return 0;
+ }
+}
+
+static const MemoryRegionOps cpc_ops = {
+ .read = cpc_read,
+ .write = cpc_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .impl = {
+ .max_access_size = 8,
+ },
+};
+
+static void mips_cpc_init(Object *obj)
+{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+ MIPSCPCState *s = MIPS_CPC(obj);
+
+ memory_region_init_io(&s->mr, OBJECT(s), &cpc_ops, s, "mips-cpc",
+ CPC_ADDRSPACE_SZ);
+ sysbus_init_mmio(sbd, &s->mr);
+}
+
+static void mips_cpc_realize(DeviceState *dev, Error **errp)
+{
+ MIPSCPCState *s = MIPS_CPC(dev);
+
+ if (s->vp_start_running > cpc_vp_run_mask(s)) {
+ error_setg(errp,
+ "incorrect vp_start_running 0x%" PRIx64 " for num_vp = %d",
+ s->vp_running, s->num_vp);
+ return;
+ }
+}
+
+static void mips_cpc_reset(DeviceState *dev)
+{
+ MIPSCPCState *s = MIPS_CPC(dev);
+
+ /* Reflect the fact that all VPs are halted on reset */
+ s->vp_running = 0;
+
+ /* Put selected VPs into run state */
+ cpc_run_vp(s, s->vp_start_running);
+}
+
+static const VMStateDescription vmstate_mips_cpc = {
+ .name = "mips-cpc",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT64(vp_running, MIPSCPCState),
+ VMSTATE_END_OF_LIST()
+ },
+};
+
+static Property mips_cpc_properties[] = {
+ DEFINE_PROP_UINT32("num-vp", MIPSCPCState, num_vp, 0x1),
+ DEFINE_PROP_UINT64("vp-start-running", MIPSCPCState, vp_start_running, 0x1),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void mips_cpc_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = mips_cpc_realize;
+ dc->reset = mips_cpc_reset;
+ dc->vmsd = &vmstate_mips_cpc;
+ dc->props = mips_cpc_properties;
+}
+
+static const TypeInfo mips_cpc_info = {
+ .name = TYPE_MIPS_CPC,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(MIPSCPCState),
+ .instance_init = mips_cpc_init,
+ .class_init = mips_cpc_class_init,
+};
+
+static void mips_cpc_register_types(void)
+{
+ type_register_static(&mips_cpc_info);
+}
+
+type_init(mips_cpc_register_types)
diff --git a/qemu/hw/misc/mips_itu.c b/qemu/hw/misc/mips_itu.c
new file mode 100644
index 000000000..da5455062
--- /dev/null
+++ b/qemu/hw/misc/mips_itu.c
@@ -0,0 +1,521 @@
+/*
+ * Inter-Thread Communication Unit emulation.
+ *
+ * Copyright (c) 2016 Imagination Technologies
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/hw.h"
+#include "hw/sysbus.h"
+#include "sysemu/sysemu.h"
+#include "hw/misc/mips_itu.h"
+
+#define ITC_TAG_ADDRSPACE_SZ (ITC_ADDRESSMAP_NUM * 8)
+/* Initialize as 4kB area to fit all 32 cells with default 128B grain.
+ Storage may be resized by the software. */
+#define ITC_STORAGE_ADDRSPACE_SZ 0x1000
+
+#define ITC_FIFO_NUM_MAX 16
+#define ITC_SEMAPH_NUM_MAX 16
+#define ITC_AM1_NUMENTRIES_OFS 20
+
+#define ITC_CELL_PV_MAX_VAL 0xFFFF
+
+#define ITC_CELL_TAG_FIFO_DEPTH 28
+#define ITC_CELL_TAG_FIFO_PTR 18
+#define ITC_CELL_TAG_FIFO 17
+#define ITC_CELL_TAG_T 16
+#define ITC_CELL_TAG_F 1
+#define ITC_CELL_TAG_E 0
+
+#define ITC_AM0_BASE_ADDRESS_MASK 0xFFFFFC00ULL
+#define ITC_AM0_EN_MASK 0x1
+
+#define ITC_AM1_ADDR_MASK_MASK 0x1FC00
+#define ITC_AM1_ENTRY_GRAIN_MASK 0x7
+
+typedef enum ITCView {
+ ITCVIEW_BYPASS = 0,
+ ITCVIEW_CONTROL = 1,
+ ITCVIEW_EF_SYNC = 2,
+ ITCVIEW_EF_TRY = 3,
+ ITCVIEW_PV_SYNC = 4,
+ ITCVIEW_PV_TRY = 5
+} ITCView;
+
+MemoryRegion *mips_itu_get_tag_region(MIPSITUState *itu)
+{
+ return &itu->tag_io;
+}
+
+static uint64_t itc_tag_read(void *opaque, hwaddr addr, unsigned size)
+{
+ MIPSITUState *tag = (MIPSITUState *)opaque;
+ uint64_t index = addr >> 3;
+
+ if (index >= ITC_ADDRESSMAP_NUM) {
+ qemu_log_mask(LOG_GUEST_ERROR, "Read 0x%" PRIx64 "\n", addr);
+ return 0;
+ }
+
+ return tag->ITCAddressMap[index];
+}
+
+static void itc_reconfigure(MIPSITUState *tag)
+{
+ uint64_t *am = &tag->ITCAddressMap[0];
+ MemoryRegion *mr = &tag->storage_io;
+ hwaddr address = am[0] & ITC_AM0_BASE_ADDRESS_MASK;
+ uint64_t size = (1 << 10) + (am[1] & ITC_AM1_ADDR_MASK_MASK);
+ bool is_enabled = (am[0] & ITC_AM0_EN_MASK) != 0;
+
+ memory_region_transaction_begin();
+ if (!(size & (size - 1))) {
+ memory_region_set_size(mr, size);
+ }
+ memory_region_set_address(mr, address);
+ memory_region_set_enabled(mr, is_enabled);
+ memory_region_transaction_commit();
+}
+
+static void itc_tag_write(void *opaque, hwaddr addr,
+ uint64_t data, unsigned size)
+{
+ MIPSITUState *tag = (MIPSITUState *)opaque;
+ uint64_t *am = &tag->ITCAddressMap[0];
+ uint64_t am_old, mask;
+ uint64_t index = addr >> 3;
+
+ switch (index) {
+ case 0:
+ mask = ITC_AM0_BASE_ADDRESS_MASK | ITC_AM0_EN_MASK;
+ break;
+ case 1:
+ mask = ITC_AM1_ADDR_MASK_MASK | ITC_AM1_ENTRY_GRAIN_MASK;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "Bad write 0x%" PRIx64 "\n", addr);
+ return;
+ }
+
+ am_old = am[index];
+ am[index] = (data & mask) | (am_old & ~mask);
+ if (am_old != am[index]) {
+ itc_reconfigure(tag);
+ }
+}
+
+static const MemoryRegionOps itc_tag_ops = {
+ .read = itc_tag_read,
+ .write = itc_tag_write,
+ .impl = {
+ .max_access_size = 8,
+ },
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static inline uint32_t get_num_cells(MIPSITUState *s)
+{
+ return s->num_fifo + s->num_semaphores;
+}
+
+static inline ITCView get_itc_view(hwaddr addr)
+{
+ return (addr >> 3) & 0xf;
+}
+
+static inline int get_cell_stride_shift(const MIPSITUState *s)
+{
+ /* Minimum interval (for EntryGain = 0) is 128 B */
+ return 7 + (s->ITCAddressMap[1] & ITC_AM1_ENTRY_GRAIN_MASK);
+}
+
+static inline ITCStorageCell *get_cell(MIPSITUState *s,
+ hwaddr addr)
+{
+ uint32_t cell_idx = addr >> get_cell_stride_shift(s);
+ uint32_t num_cells = get_num_cells(s);
+
+ if (cell_idx >= num_cells) {
+ cell_idx = num_cells - 1;
+ }
+
+ return &s->cell[cell_idx];
+}
+
+static void wake_blocked_threads(ITCStorageCell *c)
+{
+ CPUState *cs;
+ CPU_FOREACH(cs) {
+ if (cs->halted && (c->blocked_threads & (1ULL << cs->cpu_index))) {
+ cpu_interrupt(cs, CPU_INTERRUPT_WAKE);
+ }
+ }
+ c->blocked_threads = 0;
+}
+
+static void QEMU_NORETURN block_thread_and_exit(ITCStorageCell *c)
+{
+ c->blocked_threads |= 1ULL << current_cpu->cpu_index;
+ cpu_restore_state(current_cpu, current_cpu->mem_io_pc);
+ current_cpu->halted = 1;
+ current_cpu->exception_index = EXCP_HLT;
+ cpu_loop_exit(current_cpu);
+}
+
+/* ITC Bypass View */
+
+static inline uint64_t view_bypass_read(ITCStorageCell *c)
+{
+ if (c->tag.FIFO) {
+ return c->data[c->fifo_out];
+ } else {
+ return c->data[0];
+ }
+}
+
+static inline void view_bypass_write(ITCStorageCell *c, uint64_t val)
+{
+ if (c->tag.FIFO && (c->tag.FIFOPtr > 0)) {
+ int idx = (c->fifo_out + c->tag.FIFOPtr - 1) % ITC_CELL_DEPTH;
+ c->data[idx] = val;
+ }
+
+ /* ignore a write to the semaphore cell */
+}
+
+/* ITC Control View */
+
+static inline uint64_t view_control_read(ITCStorageCell *c)
+{
+ return ((uint64_t)c->tag.FIFODepth << ITC_CELL_TAG_FIFO_DEPTH) |
+ (c->tag.FIFOPtr << ITC_CELL_TAG_FIFO_PTR) |
+ (c->tag.FIFO << ITC_CELL_TAG_FIFO) |
+ (c->tag.T << ITC_CELL_TAG_T) |
+ (c->tag.E << ITC_CELL_TAG_E) |
+ (c->tag.F << ITC_CELL_TAG_F);
+}
+
+static inline void view_control_write(ITCStorageCell *c, uint64_t val)
+{
+ c->tag.T = (val >> ITC_CELL_TAG_T) & 1;
+ c->tag.E = (val >> ITC_CELL_TAG_E) & 1;
+ c->tag.F = (val >> ITC_CELL_TAG_F) & 1;
+
+ if (c->tag.E) {
+ c->tag.FIFOPtr = 0;
+ }
+}
+
+/* ITC Empty/Full View */
+
+static uint64_t view_ef_common_read(ITCStorageCell *c, bool blocking)
+{
+ uint64_t ret = 0;
+
+ if (!c->tag.FIFO) {
+ return 0;
+ }
+
+ c->tag.F = 0;
+
+ if (blocking && c->tag.E) {
+ block_thread_and_exit(c);
+ }
+
+ if (c->blocked_threads) {
+ wake_blocked_threads(c);
+ }
+
+ if (c->tag.FIFOPtr > 0) {
+ ret = c->data[c->fifo_out];
+ c->fifo_out = (c->fifo_out + 1) % ITC_CELL_DEPTH;
+ c->tag.FIFOPtr--;
+ }
+
+ if (c->tag.FIFOPtr == 0) {
+ c->tag.E = 1;
+ }
+
+ return ret;
+}
+
+static uint64_t view_ef_sync_read(ITCStorageCell *c)
+{
+ return view_ef_common_read(c, true);
+}
+
+static uint64_t view_ef_try_read(ITCStorageCell *c)
+{
+ return view_ef_common_read(c, false);
+}
+
+static inline void view_ef_common_write(ITCStorageCell *c, uint64_t val,
+ bool blocking)
+{
+ if (!c->tag.FIFO) {
+ return;
+ }
+
+ c->tag.E = 0;
+
+ if (blocking && c->tag.F) {
+ block_thread_and_exit(c);
+ }
+
+ if (c->blocked_threads) {
+ wake_blocked_threads(c);
+ }
+
+ if (c->tag.FIFOPtr < ITC_CELL_DEPTH) {
+ int idx = (c->fifo_out + c->tag.FIFOPtr) % ITC_CELL_DEPTH;
+ c->data[idx] = val;
+ c->tag.FIFOPtr++;
+ }
+
+ if (c->tag.FIFOPtr == ITC_CELL_DEPTH) {
+ c->tag.F = 1;
+ }
+}
+
+static void view_ef_sync_write(ITCStorageCell *c, uint64_t val)
+{
+ view_ef_common_write(c, val, true);
+}
+
+static void view_ef_try_write(ITCStorageCell *c, uint64_t val)
+{
+ view_ef_common_write(c, val, false);
+}
+
+/* ITC P/V View */
+
+static uint64_t view_pv_common_read(ITCStorageCell *c, bool blocking)
+{
+ uint64_t ret = c->data[0];
+
+ if (c->tag.FIFO) {
+ return 0;
+ }
+
+ if (c->data[0] > 0) {
+ c->data[0]--;
+ } else if (blocking) {
+ block_thread_and_exit(c);
+ }
+
+ return ret;
+}
+
+static uint64_t view_pv_sync_read(ITCStorageCell *c)
+{
+ return view_pv_common_read(c, true);
+}
+
+static uint64_t view_pv_try_read(ITCStorageCell *c)
+{
+ return view_pv_common_read(c, false);
+}
+
+static inline void view_pv_common_write(ITCStorageCell *c)
+{
+ if (c->tag.FIFO) {
+ return;
+ }
+
+ if (c->data[0] < ITC_CELL_PV_MAX_VAL) {
+ c->data[0]++;
+ }
+
+ if (c->blocked_threads) {
+ wake_blocked_threads(c);
+ }
+}
+
+static void view_pv_sync_write(ITCStorageCell *c)
+{
+ view_pv_common_write(c);
+}
+
+static void view_pv_try_write(ITCStorageCell *c)
+{
+ view_pv_common_write(c);
+}
+
+static uint64_t itc_storage_read(void *opaque, hwaddr addr, unsigned size)
+{
+ MIPSITUState *s = (MIPSITUState *)opaque;
+ ITCStorageCell *cell = get_cell(s, addr);
+ ITCView view = get_itc_view(addr);
+ uint64_t ret = -1;
+
+ switch (view) {
+ case ITCVIEW_BYPASS:
+ ret = view_bypass_read(cell);
+ break;
+ case ITCVIEW_CONTROL:
+ ret = view_control_read(cell);
+ break;
+ case ITCVIEW_EF_SYNC:
+ ret = view_ef_sync_read(cell);
+ break;
+ case ITCVIEW_EF_TRY:
+ ret = view_ef_try_read(cell);
+ break;
+ case ITCVIEW_PV_SYNC:
+ ret = view_pv_sync_read(cell);
+ break;
+ case ITCVIEW_PV_TRY:
+ ret = view_pv_try_read(cell);
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "itc_storage_read: Bad ITC View %d\n", (int)view);
+ break;
+ }
+
+ return ret;
+}
+
+static void itc_storage_write(void *opaque, hwaddr addr, uint64_t data,
+ unsigned size)
+{
+ MIPSITUState *s = (MIPSITUState *)opaque;
+ ITCStorageCell *cell = get_cell(s, addr);
+ ITCView view = get_itc_view(addr);
+
+ switch (view) {
+ case ITCVIEW_BYPASS:
+ view_bypass_write(cell, data);
+ break;
+ case ITCVIEW_CONTROL:
+ view_control_write(cell, data);
+ break;
+ case ITCVIEW_EF_SYNC:
+ view_ef_sync_write(cell, data);
+ break;
+ case ITCVIEW_EF_TRY:
+ view_ef_try_write(cell, data);
+ break;
+ case ITCVIEW_PV_SYNC:
+ view_pv_sync_write(cell);
+ break;
+ case ITCVIEW_PV_TRY:
+ view_pv_try_write(cell);
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "itc_storage_write: Bad ITC View %d\n", (int)view);
+ break;
+ }
+
+}
+
+static const MemoryRegionOps itc_storage_ops = {
+ .read = itc_storage_read,
+ .write = itc_storage_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void itc_reset_cells(MIPSITUState *s)
+{
+ int i;
+
+ memset(s->cell, 0, get_num_cells(s) * sizeof(s->cell[0]));
+
+ for (i = 0; i < s->num_fifo; i++) {
+ s->cell[i].tag.E = 1;
+ s->cell[i].tag.FIFO = 1;
+ s->cell[i].tag.FIFODepth = ITC_CELL_DEPTH_SHIFT;
+ }
+}
+
+static void mips_itu_init(Object *obj)
+{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+ MIPSITUState *s = MIPS_ITU(obj);
+
+ memory_region_init_io(&s->storage_io, OBJECT(s), &itc_storage_ops, s,
+ "mips-itc-storage", ITC_STORAGE_ADDRSPACE_SZ);
+ sysbus_init_mmio(sbd, &s->storage_io);
+
+ memory_region_init_io(&s->tag_io, OBJECT(s), &itc_tag_ops, s,
+ "mips-itc-tag", ITC_TAG_ADDRSPACE_SZ);
+}
+
+static void mips_itu_realize(DeviceState *dev, Error **errp)
+{
+ MIPSITUState *s = MIPS_ITU(dev);
+
+ if (s->num_fifo > ITC_FIFO_NUM_MAX) {
+ error_setg(errp, "Exceed maximum number of FIFO cells: %d",
+ s->num_fifo);
+ return;
+ }
+ if (s->num_semaphores > ITC_SEMAPH_NUM_MAX) {
+ error_setg(errp, "Exceed maximum number of Semaphore cells: %d",
+ s->num_semaphores);
+ return;
+ }
+
+ s->cell = g_new(ITCStorageCell, get_num_cells(s));
+}
+
+static void mips_itu_reset(DeviceState *dev)
+{
+ MIPSITUState *s = MIPS_ITU(dev);
+
+ s->ITCAddressMap[0] = 0;
+ s->ITCAddressMap[1] =
+ ((ITC_STORAGE_ADDRSPACE_SZ - 1) & ITC_AM1_ADDR_MASK_MASK) |
+ (get_num_cells(s) << ITC_AM1_NUMENTRIES_OFS);
+ itc_reconfigure(s);
+
+ itc_reset_cells(s);
+}
+
+static Property mips_itu_properties[] = {
+ DEFINE_PROP_INT32("num-fifo", MIPSITUState, num_fifo,
+ ITC_FIFO_NUM_MAX),
+ DEFINE_PROP_INT32("num-semaphores", MIPSITUState, num_semaphores,
+ ITC_SEMAPH_NUM_MAX),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void mips_itu_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->props = mips_itu_properties;
+ dc->realize = mips_itu_realize;
+ dc->reset = mips_itu_reset;
+}
+
+static const TypeInfo mips_itu_info = {
+ .name = TYPE_MIPS_ITU,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(MIPSITUState),
+ .instance_init = mips_itu_init,
+ .class_init = mips_itu_class_init,
+};
+
+static void mips_itu_register_types(void)
+{
+ type_register_static(&mips_itu_info);
+}
+
+type_init(mips_itu_register_types)
diff --git a/qemu/hw/misc/mst_fpga.c b/qemu/hw/misc/mst_fpga.c
index d5090799f..48d7dfb2d 100644
--- a/qemu/hw/misc/mst_fpga.c
+++ b/qemu/hw/misc/mst_fpga.c
@@ -10,6 +10,7 @@
* Contributions after 2012-01-13 are licensed under the terms of the
* GNU GPL, version 2 or (at your option) any later version.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/sysbus.h"
diff --git a/qemu/hw/misc/omap_clk.c b/qemu/hw/misc/omap_clk.c
index 80a3c50e1..19151d07d 100644
--- a/qemu/hw/misc/omap_clk.c
+++ b/qemu/hw/misc/omap_clk.c
@@ -18,6 +18,7 @@
* 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 "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/arm/omap.h"
@@ -1239,7 +1240,7 @@ void omap_clk_init(struct omap_mpu_state_s *mpu)
for (i = onchip_clks, count = 0; *i; i ++)
if ((*i)->flags & flag)
count ++;
- mpu->clks = (struct clk *) g_malloc0(sizeof(struct clk) * (count + 1));
+ mpu->clks = g_new0(struct clk, count + 1);
for (i = onchip_clks, j = mpu->clks; *i; i ++)
if ((*i)->flags & flag) {
memcpy(j, *i, sizeof(struct clk));
diff --git a/qemu/hw/misc/omap_gpmc.c b/qemu/hw/misc/omap_gpmc.c
index 74fc91c8e..67d8e2f02 100644
--- a/qemu/hw/misc/omap_gpmc.c
+++ b/qemu/hw/misc/omap_gpmc.c
@@ -18,6 +18,7 @@
* 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 "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/block/flash.h"
#include "hw/arm/omap.h"
@@ -826,8 +827,7 @@ struct omap_gpmc_s *omap_gpmc_init(struct omap_mpu_state_s *mpu,
qemu_irq irq, qemu_irq drq)
{
int cs;
- struct omap_gpmc_s *s = (struct omap_gpmc_s *)
- g_malloc0(sizeof(struct omap_gpmc_s));
+ struct omap_gpmc_s *s = g_new0(struct omap_gpmc_s, 1);
memory_region_init_io(&s->iomem, NULL, &omap_gpmc_ops, s, "omap-gpmc", 0x1000);
memory_region_add_subregion(get_system_memory(), base, &s->iomem);
diff --git a/qemu/hw/misc/omap_l4.c b/qemu/hw/misc/omap_l4.c
index 245ceac84..88c533a0f 100644
--- a/qemu/hw/misc/omap_l4.c
+++ b/qemu/hw/misc/omap_l4.c
@@ -17,6 +17,7 @@
* 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 "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/arm/omap.h"
diff --git a/qemu/hw/misc/omap_sdrc.c b/qemu/hw/misc/omap_sdrc.c
index 3de0c0e9d..dff37ecaf 100644
--- a/qemu/hw/misc/omap_sdrc.c
+++ b/qemu/hw/misc/omap_sdrc.c
@@ -17,6 +17,7 @@
* 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 "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/arm/omap.h"
@@ -157,8 +158,7 @@ static const MemoryRegionOps omap_sdrc_ops = {
struct omap_sdrc_s *omap_sdrc_init(MemoryRegion *sysmem,
hwaddr base)
{
- struct omap_sdrc_s *s = (struct omap_sdrc_s *)
- g_malloc0(sizeof(struct omap_sdrc_s));
+ struct omap_sdrc_s *s = g_new0(struct omap_sdrc_s, 1);
omap_sdrc_reset(s);
diff --git a/qemu/hw/misc/omap_tap.c b/qemu/hw/misc/omap_tap.c
index 6f02bb9e4..e6ea8ee23 100644
--- a/qemu/hw/misc/omap_tap.c
+++ b/qemu/hw/misc/omap_tap.c
@@ -18,6 +18,7 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/arm/omap.h"
diff --git a/qemu/hw/misc/pc-testdev.c b/qemu/hw/misc/pc-testdev.c
index 18e94e07b..086893dcc 100644
--- a/qemu/hw/misc/pc-testdev.c
+++ b/qemu/hw/misc/pc-testdev.c
@@ -35,7 +35,7 @@
* git://git.kernel.org/pub/scm/virt/kvm/kvm-unit-tests.git
*/
-#include "config-host.h"
+#include "qemu/osdep.h"
#if defined(CONFIG_POSIX)
#include <sys/mman.h>
#endif
diff --git a/qemu/hw/misc/pci-testdev.c b/qemu/hw/misc/pci-testdev.c
index 26b9b8617..2f2e98977 100644
--- a/qemu/hw/misc/pci-testdev.c
+++ b/qemu/hw/misc/pci-testdev.c
@@ -17,10 +17,10 @@
* 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 "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/pci/pci.h"
#include "qemu/event_notifier.h"
-#include "qemu/osdep.h"
typedef struct PCITestDevHdr {
uint8_t test;
@@ -239,6 +239,7 @@ static void pci_testdev_realize(PCIDevice *pci_dev, Error **errp)
uint8_t *pci_conf;
char *name;
int r, i;
+ bool fastmmio = kvm_ioeventfd_any_length_enabled();
pci_conf = pci_dev->config;
@@ -261,8 +262,12 @@ static void pci_testdev_realize(PCIDevice *pci_dev, Error **errp)
memcpy(test->hdr->name, name, strlen(name) + 1);
g_free(name);
test->hdr->offset = cpu_to_le32(IOTEST_SIZE(i) + i * IOTEST_ACCESS_WIDTH);
- test->size = IOTEST_ACCESS_WIDTH;
test->match_data = strcmp(IOTEST_TEST(i), "wildcard-eventfd");
+ if (fastmmio && IOTEST_IS_MEM(i) && !test->match_data) {
+ test->size = 0;
+ } else {
+ test->size = IOTEST_ACCESS_WIDTH;
+ }
test->hdr->test = i;
test->hdr->data = test->match_data ? IOTEST_DATAMATCH : IOTEST_NOMATCH;
test->hdr->width = IOTEST_ACCESS_WIDTH;
diff --git a/qemu/hw/misc/puv3_pm.c b/qemu/hw/misc/puv3_pm.c
index 37f23695d..577cebaac 100644
--- a/qemu/hw/misc/puv3_pm.c
+++ b/qemu/hw/misc/puv3_pm.c
@@ -8,6 +8,7 @@
* published by the Free Software Foundation, or any later version.
* See the COPYING file in the top-level directory.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/sysbus.h"
diff --git a/qemu/hw/misc/pvpanic.c b/qemu/hw/misc/pvpanic.c
index 994f8af8e..0ac1e6ac9 100644
--- a/qemu/hw/misc/pvpanic.c
+++ b/qemu/hw/misc/pvpanic.c
@@ -12,6 +12,7 @@
*
*/
+#include "qemu/osdep.h"
#include "qapi/qmp/qobject.h"
#include "qapi/qmp/qjson.h"
#include "sysemu/sysemu.h"
@@ -41,8 +42,7 @@ static void handle_event(int event)
}
if (event & PVPANIC_PANICKED) {
- qapi_event_send_guest_panicked(GUEST_PANIC_ACTION_PAUSE, &error_abort);
- vm_stop(RUN_STATE_GUEST_PANICKED);
+ qemu_system_guest_panicked();
return;
}
}
diff --git a/qemu/hw/misc/sga.c b/qemu/hw/misc/sga.c
index 83d2fd9d3..03b006d6f 100644
--- a/qemu/hw/misc/sga.c
+++ b/qemu/hw/misc/sga.c
@@ -24,6 +24,7 @@
* sgabios code originally available at code.google.com/p/sgabios
*
*/
+#include "qemu/osdep.h"
#include "hw/pci/pci.h"
#include "hw/i386/pc.h"
#include "hw/loader.h"
diff --git a/qemu/hw/misc/slavio_misc.c b/qemu/hw/misc/slavio_misc.c
index ec50f1075..edd5de070 100644
--- a/qemu/hw/misc/slavio_misc.c
+++ b/qemu/hw/misc/slavio_misc.c
@@ -22,6 +22,7 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "sysemu/sysemu.h"
#include "hw/sysbus.h"
#include "trace.h"
diff --git a/qemu/hw/misc/stm32f2xx_syscfg.c b/qemu/hw/misc/stm32f2xx_syscfg.c
index 4ae4042bf..d0d7076ef 100644
--- a/qemu/hw/misc/stm32f2xx_syscfg.c
+++ b/qemu/hw/misc/stm32f2xx_syscfg.c
@@ -22,6 +22,7 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/misc/stm32f2xx_syscfg.h"
#ifndef STM_SYSCFG_ERR_DEBUG
diff --git a/qemu/hw/misc/tmp105.c b/qemu/hw/misc/tmp105.c
index f3fe8b81f..f5c2472b5 100644
--- a/qemu/hw/misc/tmp105.c
+++ b/qemu/hw/misc/tmp105.c
@@ -18,9 +18,11 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/i2c/i2c.h"
#include "tmp105.h"
+#include "qapi/error.h"
#include "qapi/visitor.h"
static void tmp105_interrupt_update(TMP105State *s)
@@ -52,26 +54,26 @@ static void tmp105_alarm_update(TMP105State *s)
tmp105_interrupt_update(s);
}
-static void tmp105_get_temperature(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void tmp105_get_temperature(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
{
TMP105State *s = TMP105(obj);
int64_t value = s->temperature * 1000 / 256;
- visit_type_int(v, &value, name, errp);
+ visit_type_int(v, name, &value, errp);
}
/* Units are 0.001 centigrades relative to 0 C. s->temperature is 8.8
* fixed point, so units are 1/256 centigrades. A simple ratio will do.
*/
-static void tmp105_set_temperature(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void tmp105_set_temperature(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
{
TMP105State *s = TMP105(obj);
Error *local_err = NULL;
int64_t temp;
- visit_type_int(v, &temp, name, &local_err);
+ visit_type_int(v, name, &temp, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
diff --git a/qemu/hw/misc/vmport.c b/qemu/hw/misc/vmport.c
index cd5716a46..689678980 100644
--- a/qemu/hw/misc/vmport.c
+++ b/qemu/hw/misc/vmport.c
@@ -21,6 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/isa/isa.h"
#include "hw/i386/pc.h"
diff --git a/qemu/hw/misc/zynq-xadc.c b/qemu/hw/misc/zynq-xadc.c
new file mode 100644
index 000000000..71fbccd79
--- /dev/null
+++ b/qemu/hw/misc/zynq-xadc.c
@@ -0,0 +1,303 @@
+/*
+ * ADC registers for Xilinx Zynq Platform
+ *
+ * Copyright (c) 2015 Guenter Roeck
+ * Based on hw/misc/zynq_slcr.c, written by Michal Simek
+ *
+ * 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.
+ *
+ * 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 "qemu/osdep.h"
+#include "hw/hw.h"
+#include "hw/misc/zynq-xadc.h"
+#include "qemu/timer.h"
+#include "sysemu/sysemu.h"
+
+enum {
+ CFG = 0x000 / 4,
+ INT_STS,
+ INT_MASK,
+ MSTS,
+ CMDFIFO,
+ RDFIFO,
+ MCTL,
+};
+
+#define CFG_ENABLE BIT(31)
+#define CFG_CFIFOTH_SHIFT 20
+#define CFG_CFIFOTH_LENGTH 4
+#define CFG_DFIFOTH_SHIFT 16
+#define CFG_DFIFOTH_LENGTH 4
+#define CFG_WEDGE BIT(13)
+#define CFG_REDGE BIT(12)
+#define CFG_TCKRATE_SHIFT 8
+#define CFG_TCKRATE_LENGTH 2
+
+#define CFG_TCKRATE_DIV(x) (0x1 << (x - 1))
+
+#define CFG_IGAP_SHIFT 0
+#define CFG_IGAP_LENGTH 5
+
+#define INT_CFIFO_LTH BIT(9)
+#define INT_DFIFO_GTH BIT(8)
+#define INT_OT BIT(7)
+#define INT_ALM_SHIFT 0
+#define INT_ALM_LENGTH 7
+#define INT_ALM_MASK (((1 << INT_ALM_LENGTH) - 1) << INT_ALM_SHIFT)
+
+#define INT_ALL (INT_CFIFO_LTH | INT_DFIFO_GTH | INT_OT | INT_ALM_MASK)
+
+#define MSTS_CFIFO_LVL_SHIFT 16
+#define MSTS_CFIFO_LVL_LENGTH 4
+#define MSTS_DFIFO_LVL_SHIFT 12
+#define MSTS_DFIFO_LVL_LENGTH 4
+#define MSTS_CFIFOF BIT(11)
+#define MSTS_CFIFOE BIT(10)
+#define MSTS_DFIFOF BIT(9)
+#define MSTS_DFIFOE BIT(8)
+#define MSTS_OT BIT(7)
+#define MSTS_ALM_SHIFT 0
+#define MSTS_ALM_LENGTH 7
+
+#define MCTL_RESET BIT(4)
+
+#define CMD_NOP 0x00
+#define CMD_READ 0x01
+#define CMD_WRITE 0x02
+
+static void zynq_xadc_update_ints(ZynqXADCState *s)
+{
+
+ /* We are fast, commands are actioned instantly so the CFIFO is always
+ * empty (and below threshold).
+ */
+ s->regs[INT_STS] |= INT_CFIFO_LTH;
+
+ if (s->xadc_dfifo_entries >
+ extract32(s->regs[CFG], CFG_DFIFOTH_SHIFT, CFG_DFIFOTH_LENGTH)) {
+ s->regs[INT_STS] |= INT_DFIFO_GTH;
+ }
+
+ qemu_set_irq(s->qemu_irq, !!(s->regs[INT_STS] & ~s->regs[INT_MASK]));
+}
+
+static void zynq_xadc_reset(DeviceState *d)
+{
+ ZynqXADCState *s = ZYNQ_XADC(d);
+
+ s->regs[CFG] = 0x14 << CFG_IGAP_SHIFT |
+ CFG_TCKRATE_DIV(4) << CFG_TCKRATE_SHIFT | CFG_REDGE;
+ s->regs[INT_STS] = INT_CFIFO_LTH;
+ s->regs[INT_MASK] = 0xffffffff;
+ s->regs[CMDFIFO] = 0;
+ s->regs[RDFIFO] = 0;
+ s->regs[MCTL] = MCTL_RESET;
+
+ memset(s->xadc_regs, 0, sizeof(s->xadc_regs));
+ memset(s->xadc_dfifo, 0, sizeof(s->xadc_dfifo));
+ s->xadc_dfifo_entries = 0;
+
+ zynq_xadc_update_ints(s);
+}
+
+static uint16_t xadc_pop_dfifo(ZynqXADCState *s)
+{
+ uint16_t rv = s->xadc_dfifo[0];
+ int i;
+
+ if (s->xadc_dfifo_entries > 0) {
+ s->xadc_dfifo_entries--;
+ }
+ for (i = 0; i < s->xadc_dfifo_entries; i++) {
+ s->xadc_dfifo[i] = s->xadc_dfifo[i + 1];
+ }
+ s->xadc_dfifo[s->xadc_dfifo_entries] = 0;
+ zynq_xadc_update_ints(s);
+ return rv;
+}
+
+static void xadc_push_dfifo(ZynqXADCState *s, uint16_t regval)
+{
+ if (s->xadc_dfifo_entries < ZYNQ_XADC_FIFO_DEPTH) {
+ s->xadc_dfifo[s->xadc_dfifo_entries++] = s->xadc_read_reg_previous;
+ }
+ s->xadc_read_reg_previous = regval;
+ zynq_xadc_update_ints(s);
+}
+
+static bool zynq_xadc_check_offset(hwaddr offset, bool rnw)
+{
+ switch (offset) {
+ case CFG:
+ case INT_MASK:
+ case INT_STS:
+ case MCTL:
+ return true;
+ case RDFIFO:
+ case MSTS:
+ return rnw; /* read only */
+ case CMDFIFO:
+ return !rnw; /* write only */
+ default:
+ return false;
+ }
+}
+
+static uint64_t zynq_xadc_read(void *opaque, hwaddr offset, unsigned size)
+{
+ ZynqXADCState *s = opaque;
+ int reg = offset / 4;
+ uint32_t rv = 0;
+
+ if (!zynq_xadc_check_offset(reg, true)) {
+ qemu_log_mask(LOG_GUEST_ERROR, "zynq_xadc: Invalid read access to "
+ "addr %" HWADDR_PRIx "\n", offset);
+ return 0;
+ }
+
+ switch (reg) {
+ case CFG:
+ case INT_MASK:
+ case INT_STS:
+ case MCTL:
+ rv = s->regs[reg];
+ break;
+ case MSTS:
+ rv = MSTS_CFIFOE;
+ rv |= s->xadc_dfifo_entries << MSTS_DFIFO_LVL_SHIFT;
+ if (!s->xadc_dfifo_entries) {
+ rv |= MSTS_DFIFOE;
+ } else if (s->xadc_dfifo_entries == ZYNQ_XADC_FIFO_DEPTH) {
+ rv |= MSTS_DFIFOF;
+ }
+ break;
+ case RDFIFO:
+ rv = xadc_pop_dfifo(s);
+ break;
+ }
+ return rv;
+}
+
+static void zynq_xadc_write(void *opaque, hwaddr offset, uint64_t val,
+ unsigned size)
+{
+ ZynqXADCState *s = (ZynqXADCState *)opaque;
+ int reg = offset / 4;
+ int xadc_reg;
+ int xadc_cmd;
+ int xadc_data;
+
+ if (!zynq_xadc_check_offset(reg, false)) {
+ qemu_log_mask(LOG_GUEST_ERROR, "zynq_xadc: Invalid write access "
+ "to addr %" HWADDR_PRIx "\n", offset);
+ return;
+ }
+
+ switch (reg) {
+ case CFG:
+ s->regs[CFG] = val;
+ break;
+ case INT_STS:
+ s->regs[INT_STS] &= ~val;
+ break;
+ case INT_MASK:
+ s->regs[INT_MASK] = val & INT_ALL;
+ break;
+ case CMDFIFO:
+ xadc_cmd = extract32(val, 26, 4);
+ xadc_reg = extract32(val, 16, 10);
+ xadc_data = extract32(val, 0, 16);
+
+ if (s->regs[MCTL] & MCTL_RESET) {
+ qemu_log_mask(LOG_GUEST_ERROR, "zynq_xadc: Sending command "
+ "while comm channel held in reset: %" PRIx32 "\n",
+ (uint32_t) val);
+ break;
+ }
+
+ if (xadc_reg >= ZYNQ_XADC_NUM_ADC_REGS && xadc_cmd != CMD_NOP) {
+ qemu_log_mask(LOG_GUEST_ERROR, "read/write op to invalid xadc "
+ "reg 0x%x\n", xadc_reg);
+ break;
+ }
+
+ switch (xadc_cmd) {
+ case CMD_READ:
+ xadc_push_dfifo(s, s->xadc_regs[xadc_reg]);
+ break;
+ case CMD_WRITE:
+ s->xadc_regs[xadc_reg] = xadc_data;
+ /* fallthrough */
+ case CMD_NOP:
+ xadc_push_dfifo(s, 0);
+ break;
+ }
+ break;
+ case MCTL:
+ s->regs[MCTL] = val & 0x00fffeff;
+ break;
+ }
+ zynq_xadc_update_ints(s);
+}
+
+static const MemoryRegionOps xadc_ops = {
+ .read = zynq_xadc_read,
+ .write = zynq_xadc_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void zynq_xadc_init(Object *obj)
+{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+ ZynqXADCState *s = ZYNQ_XADC(obj);
+
+ memory_region_init_io(&s->iomem, obj, &xadc_ops, s, "zynq-xadc",
+ ZYNQ_XADC_MMIO_SIZE);
+ sysbus_init_mmio(sbd, &s->iomem);
+ sysbus_init_irq(sbd, &s->qemu_irq);
+}
+
+static const VMStateDescription vmstate_zynq_xadc = {
+ .name = "zynq-xadc",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(regs, ZynqXADCState, ZYNQ_XADC_NUM_IO_REGS),
+ VMSTATE_UINT16_ARRAY(xadc_regs, ZynqXADCState,
+ ZYNQ_XADC_NUM_ADC_REGS),
+ VMSTATE_UINT16_ARRAY(xadc_dfifo, ZynqXADCState,
+ ZYNQ_XADC_FIFO_DEPTH),
+ VMSTATE_UINT16(xadc_read_reg_previous, ZynqXADCState),
+ VMSTATE_UINT16(xadc_dfifo_entries, ZynqXADCState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void zynq_xadc_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->vmsd = &vmstate_zynq_xadc;
+ dc->reset = zynq_xadc_reset;
+}
+
+static const TypeInfo zynq_xadc_info = {
+ .class_init = zynq_xadc_class_init,
+ .name = TYPE_ZYNQ_XADC,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(ZynqXADCState),
+ .instance_init = zynq_xadc_init,
+};
+
+static void zynq_xadc_register_types(void)
+{
+ type_register_static(&zynq_xadc_info);
+}
+
+type_init(zynq_xadc_register_types)
diff --git a/qemu/hw/misc/zynq_slcr.c b/qemu/hw/misc/zynq_slcr.c
index 964f2532f..b1b7591ef 100644
--- a/qemu/hw/misc/zynq_slcr.c
+++ b/qemu/hw/misc/zynq_slcr.c
@@ -14,6 +14,7 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "qemu/timer.h"
#include "hw/sysbus.h"
@@ -393,12 +394,12 @@ static void zynq_slcr_write(void *opaque, hwaddr offset,
return;
}
- if (!s->regs[LOCKSTA]) {
- s->regs[offset / 4] = val;
- } else {
- DB_PRINT("SCLR registers are locked. Unlock them first\n");
+ if (s->regs[LOCKSTA]) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "SCLR registers are locked. Unlock them first\n");
return;
}
+ s->regs[offset] = val;
switch (offset) {
case PSS_RST_CTRL: