From: Baptiste Reynal <b.rey...@virtualopensystems.com> 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.rey...@virtualopensystems.com> Signed-off-by: Christian Pinto <c.pi...@virtualopensystems.com> --- hw/misc/Makefile.objs | 1 + hw/misc/sdm-platform.c | 231 +++++++++++++++++++++++++++++++++++++++++ hw/misc/sdm-signal.c | 1 + include/hw/misc/sdm-platform.h | 65 ++++++++++++ include/hw/misc/sdm-signal.h | 1 + 5 files changed, 299 insertions(+) create mode 100644 hw/misc/sdm-platform.c create mode 100644 include/hw/misc/sdm-platform.h diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs index ce2aa89..85cfda9 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..26c202f --- /dev/null +++ b/hw/misc/sdm-platform.c @@ -0,0 +1,231 @@ +/* + * SDM Platform Device + * + * Copyright (C) 2015 - Virtual Open Systems + * + * Author: Christian Pinto <c.pi...@virtualopensystems.com> + * Baptiste Reynal <b.rey...@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/hw/misc/sdm-signal.c b/hw/misc/sdm-signal.c index c4af0ac..a34fbea 100644 --- a/hw/misc/sdm-signal.c +++ b/hw/misc/sdm-signal.c @@ -4,6 +4,7 @@ * Copyright (C) 2016 - Virtual Open Systems * * Author: Baptiste Reynal <b.rey...@virtualopensystems.com> + * Christian Pinto <c.pi...@virtualopensystems.com> * * This work is licensed under the terms of the GNU GPL, version 2. See * the COPYING file in the top-level directory. diff --git a/include/hw/misc/sdm-platform.h b/include/hw/misc/sdm-platform.h new file mode 100644 index 0000000..5b504fe --- /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.pi...@virtualopensystems.com> + * Baptiste Reynal <b.rey...@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 diff --git a/include/hw/misc/sdm-signal.h b/include/hw/misc/sdm-signal.h index f9b82eb..4b60724 100644 --- a/include/hw/misc/sdm-signal.h +++ b/include/hw/misc/sdm-signal.h @@ -4,6 +4,7 @@ * Copyright (C) 2016 - Virtual Open Systems * * Author: Baptiste Reynal <b.rey...@virtualopensystems.com> + * Christian Pinto <c.pi...@virtualopensystems.com> * * This work is licensed under the terms of the GNU GPL, version 2. See * the COPYING file in the top-level directory. -- 1.9.1