@@ -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
new file mode 100644
@@ -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);
new file mode 100644
@@ -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
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