diff mbox

[RFC,v2,2/6] hw/misc: sdm platform device

Message ID 1458292385-13802-3-git-send-email-b.reynal@virtualopensystems.com (mailing list archive)
State New, archived
Headers show

Commit Message

Baptiste Reynal March 18, 2016, 9:13 a.m. UTC
This is the platform implementation for an SDM device.

Parameters are:
    comm=[sdm_communication_id] specifies the communication channel
    master=[true/false] - configure the SDM device as master or slave
    num_slaves=[slave_number] - if master is true, specifies the number
of slaves
    len-signals=[signals_number] - specifies the number of signals
    signals[x]=[signal_id] - add a signal to the device, with the ID x

Signed-off-by: Baptiste Reynal <b.reynal@virtualopensystems.com>
---
 hw/misc/Makefile.objs          |   1 +
 hw/misc/sdm-platform.c         | 236 +++++++++++++++++++++++++++++++++++++++++
 include/hw/misc/sdm-platform.h |  65 ++++++++++++
 3 files changed, 302 insertions(+)
 create mode 100644 hw/misc/sdm-platform.c
 create mode 100644 include/hw/misc/sdm-platform.h
diff mbox

Patch

diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
index 6e73901..fc32161 100644
--- a/hw/misc/Makefile.objs
+++ b/hw/misc/Makefile.objs
@@ -24,6 +24,7 @@  obj-$(CONFIG_IVSHMEM) += ivshmem.o
 obj-$(CONFIG_SDM) += sdm-device.o
 obj-$(CONFIG_SDM) += sdm-communication.o
 obj-$(CONFIG_SDM) += sdm-signal.o
+obj-$(CONFIG_SDM) += sdm-platform.o
 
 obj-$(CONFIG_REALVIEW) += arm_sysctl.o
 obj-$(CONFIG_NSERIES) += cbus.o
diff --git a/hw/misc/sdm-platform.c b/hw/misc/sdm-platform.c
new file mode 100644
index 0000000..ff3a4c7
--- /dev/null
+++ b/hw/misc/sdm-platform.c
@@ -0,0 +1,236 @@ 
+/*
+ * SDM Platform Device
+ *
+ * Copyright (C) 2015 - Virtual Open Systems
+ *
+ * Author: Christian Pinto <c.pinto@virtualopensystems.com>
+ *         Baptiste Reynal <b.reynal@virtualopensystems.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See the
+ * COPYING file in the top-level directory.
+ */
+#include "hw/misc/sdm-platform.h"
+#include "qemu/error-report.h"
+
+static void reset_irq(SDMPlatform *s)
+{
+    qemu_irq_lower(s->irq);
+}
+
+static uint64_t sdm_platform_read(void *opaque, hwaddr offset, unsigned size)
+{
+    SDMPlatform *sdmp = opaque;
+    uint64_t ret = 0;
+
+    switch (offset) {
+    case SIGNAL_REG:
+        /**
+         * Reading the REGISTER returns informations about the signal (16 high
+         * bytes) and the source (16 low bytes).
+         * The signal register is cleared, and the interrupt
+         * is lowered. Be sure to read the payload before.
+         */
+        ret = sdmp->signal_reg;
+        sdmp->signal_reg = 0;
+        reset_irq(sdmp);
+        sdmp->busy = false;
+    case PAYLOAD_REG0:
+        /**
+         * Reading the payload registers returns the value of the last
+         * payload received (0 initialized)
+         */
+        ret = sdmp->payload_reg[0];
+        break;
+    case PAYLOAD_REG1:
+        ret = sdmp->payload_reg[1];
+        break;
+    default:
+        error_report("SDM platform: wrong register in sdm_read\n");
+        break;
+    }
+
+    return ret;
+}
+
+static void sdm_platform_write(void *opaque, hwaddr offset, uint64_t value,
+        unsigned size)
+{
+    SDMPlatform *sdmp = opaque;
+    SDMSignalData signal;
+
+    switch (offset) {
+    case SIGNAL_REG:
+        /* To signal another slave, the ID should be written to the 16
+         * low bytes and the signal ID to the 16 high bytes. Value 0 is
+         * reserved to kick the master, from 1 upwards for the slaves.
+         */
+        signal.slave = value & 0xffff;
+        signal.type = (value >> 16) & 0xffff;
+        memcpy(&signal.payload, sdmp->payload_reg,
+                2 * sizeof(uint32_t));
+
+        sdm_communication_signal(sdmp->sdmc, SDM_DEVICE(sdmp), &signal);
+
+        break;
+    case PAYLOAD_REG0:
+        /* Set the payload for the next signal */
+        sdmp->payload_reg[0] = value;
+        break;
+    case PAYLOAD_REG1:
+        sdmp->payload_reg[1] = value;
+        break;
+    default:
+        error_report("SDM Platform: wrong register in sdm_write\n");
+        break;
+    }
+}
+
+
+
+static const MemoryRegionOps sdm_platform_mem_ops = {
+    .read = sdm_platform_read,
+    .write = sdm_platform_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void sdm_platform_realize(DeviceState *dev, Error **errp)
+{
+    SDMPlatform *s = SDM_PLATFORM(dev);
+    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+    int i;
+
+    s->last_slave = 0;
+
+    /* Initialize MMIO regions with read/write functions */
+    memory_region_init_io(&s->iomem, OBJECT(s), &sdm_platform_mem_ops, s,
+            TYPE_SDM_PLATFORM, SDM_PLATFORM_SIZE);
+    sysbus_init_mmio(sbd, &s->iomem);
+
+    /* Initialize IRQ */
+    sysbus_init_irq(sbd, &s->irq);
+
+    sdm_communication_connect(s->sdmc, SDM_DEVICE(s));
+
+    s->signals = calloc(s->num_signals, sizeof(SDMSignal));
+    for (i = 0; i < s->num_signals; i++) {
+        s->signals[i] = (SDMSignal *) object_resolve_path_type(
+                s->signals_name[i],
+                TYPE_SDM_SIGNAL, false);
+        if (!s->signals[i]) {
+            error_report("SDM Platform: Cannot find signal %s",
+                    s->signals_name[i]);;
+        }
+    }
+
+}
+
+static int sdm_platform_accept(SDMDevice *sdm)
+{
+    SDMPlatform *sdmp = SDM_PLATFORM(sdm);
+
+    if (sdmp->last_slave >= sdmp->num_slaves) {
+        return -1;
+    }
+
+    return ++sdmp->last_slave;
+}
+
+static int sdm_platform_notify(SDMDevice *sdm, SDMSignalData *signal)
+{
+    SDMPlatform *sdmp = SDM_PLATFORM(sdm);
+    SDMSignal *hw_signal;
+
+    if (signal->type > sdmp->num_signals) {
+        return 0;
+    }
+
+    hw_signal = sdmp->signals[signal->type];
+
+    if (!hw_signal) {
+        return 0;
+    }
+
+    sdm_signal_hw_ops(hw_signal, signal);
+
+    if (!sdm_signal_hw_only(hw_signal)) {
+        if (sdmp->busy) {
+            return -1;
+        }
+        sdmp->busy = true;
+
+        sdmp->signal_reg = (signal->type << 16) & (signal->slave & 0xffff);
+
+        qemu_irq_raise(sdmp->irq);
+    }
+
+    return 0;
+}
+
+static int sdm_platform_get_num_slaves(SDMDevice *sdm)
+{
+    SDMPlatform *sdmp = SDM_PLATFORM(sdm);
+
+    return sdmp->num_slaves;
+}
+
+static bool sdm_platform_is_master(SDMDevice *sdm)
+{
+    SDMPlatform *sdmp = SDM_PLATFORM(sdm);
+
+    return sdmp->master;
+}
+
+static Property sdm_platform_properties[] = {
+    DEFINE_PROP_UINT32("num-slaves", SDMPlatform, num_slaves, 1),
+    DEFINE_PROP_BOOL("master", SDMPlatform, master, false),
+    DEFINE_PROP_ARRAY("signals", SDMPlatform, num_signals, signals_name,
+            qdev_prop_string, char *),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void sdm_platform_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SDMDeviceClass *sdmc = SDM_DEVICE_CLASS(klass);
+
+    dc->props = sdm_platform_properties;
+    dc->realize = sdm_platform_realize;
+
+    set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+
+    sdmc->accept = sdm_platform_accept;
+    sdmc->notify = sdm_platform_notify;
+    sdmc->get_num_slaves = sdm_platform_get_num_slaves;
+    sdmc->is_master = sdm_platform_is_master;
+}
+
+static void sdm_platform_init(Object *obj)
+{
+    SDMPlatform *sdmp = SDM_PLATFORM(obj);
+
+    object_property_add_link(obj, "comm", TYPE_SDM_COMMUNICATION,
+            (Object **)&sdmp->sdmc,
+            qdev_prop_allow_set_link_before_realize,
+            OBJ_PROP_LINK_UNREF_ON_RELEASE, &error_abort);
+
+    sdmp->busy = false;
+}
+
+static const TypeInfo sdm_platform_info = {
+    .name               = TYPE_SDM_PLATFORM,
+    .parent             = TYPE_SYS_BUS_DEVICE,
+    .instance_init      = sdm_platform_init,
+    .instance_size      = sizeof(struct SDMPlatform),
+    .class_init         = sdm_platform_class_init,
+    .interfaces         = (InterfaceInfo[]) {
+        { TYPE_SDM_DEVICE },
+        { }
+    }
+};
+
+static void sdm_register_types(void)
+{
+    type_register_static(&sdm_platform_info);
+}
+
+type_init(sdm_register_types);
diff --git a/include/hw/misc/sdm-platform.h b/include/hw/misc/sdm-platform.h
new file mode 100644
index 0000000..42a0070
--- /dev/null
+++ b/include/hw/misc/sdm-platform.h
@@ -0,0 +1,65 @@ 
+/*
+ * SDM Device Platform
+ *
+ * Copyright (C) 2016 - Virtual Open Systems
+ *
+ * Author: Christian Pinto <c.pinto@virtualopensystems.com>
+ *         Baptiste Reynal <b.reynal@virtualopensystems.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+#ifndef HW_SDM_PLATFORM_H
+#define HW_SDM_PLATFORM_H
+
+#include "qemu-common.h"
+#include "hw/misc/sdm-communication.h"
+#include "hw/sysbus.h"
+
+/*
+ * Size of the IO memory mapped region
+ * associated with IDM device registers
+ */
+#define SDM_PLATFORM_SIZE 0x100
+
+/*
+ * Registers
+ */
+#define SIGNAL_REG 0x00
+#define PAYLOAD_REG0 0x04
+#define PAYLOAD_REG1 0x08
+
+#define TYPE_SDM_PLATFORM "sdm-platform"
+#define SDM_PLATFORM(obj) \
+    OBJECT_CHECK(SDMPlatform, (obj), TYPE_SDM_PLATFORM)
+
+typedef struct SDMPlatform SDMPlatform;
+
+/**
+ * @SDMPlatform
+ *
+ * @parent: opaque parent object container
+ */
+struct SDMPlatform {
+    /* private */
+    SysBusDevice parent;
+
+    MemoryRegion iomem;
+    SDMCommunication *sdmc;
+
+    bool master;
+    bool busy;
+
+    uint32_t num_slaves;
+    uint32_t last_slave;
+
+    uint32_t num_signals;
+    char **signals_name;
+    SDMSignal **signals;
+
+    uint32_t signal_reg;
+    uint32_t payload_reg[4];
+
+    qemu_irq irq;
+};
+#endif