qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH V4 1/5] SSI: Built in multiple device support


From: Peter A. G. Crosthwaite
Subject: [Qemu-devel] [PATCH V4 1/5] SSI: Built in multiple device support
Date: Mon, 4 Jun 2012 18:08:20 +1000

Added support for multiple devices attached to a single SSI bus (Previously
SSI masters with multiple slaves were emulated as multiple point to point SSI
busses)

Signed-off-by: Peter A. G. Crosthwaite <address@hidden>
---
changed from v3:
added ssi_create_slave_noinit
changed from v2:
This patch is new (totally rewitten replacement of (1/4) from v2)
 hw/spitz.c     |    8 ++--
 hw/ssi.c       |  107 ++++++++++++++++++++++++++++++++++++++++++++++++--------
 hw/ssi.h       |   28 ++++++++++++--
 hw/stellaris.c |    6 ++--
 hw/tosa.c      |    2 +-
 hw/z2.c        |    2 +-
 6 files changed, 125 insertions(+), 28 deletions(-)

diff --git a/hw/spitz.c b/hw/spitz.c
index 1d6d2b0..f63a9bf 100644
--- a/hw/spitz.c
+++ b/hw/spitz.c
@@ -669,18 +669,18 @@ static void spitz_ssp_attach(PXA2xxState *cpu)
     DeviceState *dev;
     void *bus;
 
-    mux = ssi_create_slave(cpu->ssp[CORGI_SSP_PORT - 1], "corgi-ssp");
+    mux = ssi_create_slave(cpu->ssp[CORGI_SSP_PORT - 1], "corgi-ssp", 0);
 
     bus = qdev_get_child_bus(mux, "ssi0");
-    ssi_create_slave(bus, "spitz-lcdtg");
+    ssi_create_slave(bus, "spitz-lcdtg", 0);
 
     bus = qdev_get_child_bus(mux, "ssi1");
-    dev = ssi_create_slave(bus, "ads7846");
+    dev = ssi_create_slave(bus, "ads7846", 0);
     qdev_connect_gpio_out(dev, 0,
                           qdev_get_gpio_in(cpu->gpio, SPITZ_GPIO_TP_INT));
 
     bus = qdev_get_child_bus(mux, "ssi2");
-    max1111 = ssi_create_slave(bus, "max1111");
+    max1111 = ssi_create_slave(bus, "max1111", 0);
     max111x_set_input(max1111, MAX1111_BATT_VOLT, SPITZ_BATTERY_VOLT);
     max111x_set_input(max1111, MAX1111_BATT_TEMP, 0);
     max111x_set_input(max1111, MAX1111_ACIN_VOLT, SPITZ_CHARGEON_ACIN);
diff --git a/hw/ssi.c b/hw/ssi.c
index 8f2d9bc..af7e887 100644
--- a/hw/ssi.c
+++ b/hw/ssi.c
@@ -2,6 +2,8 @@
  * QEMU Synchronous Serial Interface support
  *
  * Copyright (c) 2009 CodeSourcery.
+ * Copyright (c) 2012 Peter A.G. Crosthwaite (address@hidden)
+ * Copyright (c) 2012 PetaLogix Pty Ltd.
  * Written by Paul Brook
  *
  * This code is licensed under the GNU GPL v2.
@@ -14,24 +16,33 @@
 
 struct SSIBus {
     BusState qbus;
+    int32_t selected_slave;
 };
 
 static struct BusInfo ssi_bus_info = {
     .name = "SSI",
     .size = sizeof(SSIBus),
+    .props = (Property[]) {
+        DEFINE_PROP_INT32("slave_select", struct SSISlave, slave_select, 0),
+        DEFINE_PROP_END_OF_LIST(),
+    }
+};
+
+static const VMStateDescription vmstate_ssi_bus = {
+    .name = "ssi_bus",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_INT32(selected_slave, SSIBus),
+        VMSTATE_END_OF_LIST()
+    }
 };
 
 static int ssi_slave_init(DeviceState *dev)
 {
     SSISlave *s = SSI_SLAVE(dev);
     SSISlaveClass *ssc = SSI_SLAVE_GET_CLASS(s);
-    SSIBus *bus;
-
-    bus = FROM_QBUS(SSIBus, qdev_get_parent_bus(dev));
-    if (QTAILQ_FIRST(&bus->qbus.children) != dev
-        || QTAILQ_NEXT(dev, sibling) != NULL) {
-        hw_error("Too many devices on SSI bus");
-    }
 
     return ssc->init(s);
 }
@@ -46,40 +57,106 @@ static void ssi_slave_class_init(ObjectClass *klass, void 
*data)
 static TypeInfo ssi_slave_info = {
     .name = TYPE_SSI_SLAVE,
     .parent = TYPE_DEVICE,
+    .instance_size = sizeof(struct SSISlave),
     .class_init = ssi_slave_class_init,
     .class_size = sizeof(SSISlaveClass),
     .abstract = true,
 };
 
-DeviceState *ssi_create_slave(SSIBus *bus, const char *name)
+DeviceState *ssi_create_slave_no_init(SSIBus *bus, const char *name,
+                                                    int32_t slave_select)
 {
     DeviceState *dev;
     dev = qdev_create(&bus->qbus, name);
+    qdev_prop_set_int32(dev, "slave_select", slave_select);
+    return dev;
+}
+
+DeviceState *ssi_create_slave(SSIBus *bus, const char *name,
+                                                    int32_t slave_select)
+{
+    DeviceState *dev;
+    dev = ssi_create_slave_no_init(bus, name, slave_select);
     qdev_init_nofail(dev);
     return dev;
+
 }
 
 SSIBus *ssi_create_bus(DeviceState *parent, const char *name)
 {
-    BusState *bus;
-    bus = qbus_create(&ssi_bus_info, parent, name);
-    return FROM_QBUS(SSIBus, bus);
+    SSIBus *bus;
+
+    bus = FROM_QBUS(SSIBus, qbus_create(&ssi_bus_info, parent, name));
+    vmstate_register(NULL, -1, &vmstate_ssi_bus, bus);
+    return bus;
+}
+
+static SSISlave *get_current_slave(SSIBus *bus)
+{
+    DeviceState *qdev;
+
+    QTAILQ_FOREACH(qdev, &bus->qbus.children, sibling) {
+        SSISlave *candidate = SSI_SLAVE_FROM_QDEV(qdev);
+        if (candidate->slave_select == bus->selected_slave) {
+            return candidate;
+        }
+    }
+
+    return NULL;
+}
+
+void ssi_select_slave(SSIBus *bus, int32_t selected_slave)
+{
+    SSISlave *slave;
+    SSISlaveClass *ssc;
+
+    if (bus->selected_slave == selected_slave) {
+        return;
+    }
+
+    slave = get_current_slave(bus);
+    if (slave) {
+        ssc = SSI_SLAVE_GET_CLASS(slave);
+        if (ssc->set_cs) {
+            ssc->set_cs(slave, 0);
+        }
+    }
+    bus->selected_slave = selected_slave;
+
+    slave = get_current_slave(bus);
+    if (slave) {
+        ssc = SSI_SLAVE_GET_CLASS(slave);
+        if (ssc->set_cs) {
+            ssc->set_cs(slave, 1);
+        }
+    }
+
 }
 
 uint32_t ssi_transfer(SSIBus *bus, uint32_t val)
 {
-    DeviceState *dev;
     SSISlave *slave;
     SSISlaveClass *ssc;
-    dev = QTAILQ_FIRST(&bus->qbus.children);
-    if (!dev) {
+
+    slave = get_current_slave(bus);
+    if (!slave) {
         return 0;
     }
-    slave = SSI_SLAVE(dev);
     ssc = SSI_SLAVE_GET_CLASS(slave);
     return ssc->transfer(slave, val);
 }
 
+const VMStateDescription vmstate_ssi_slave = {
+    .name = "SSISlave",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_INT32(slave_select, SSISlave),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
 static void ssi_slave_register_types(void)
 {
     type_register_static(&ssi_slave_info);
diff --git a/hw/ssi.h b/hw/ssi.h
index 06657d7..7f0ea36 100644
--- a/hw/ssi.h
+++ b/hw/ssi.h
@@ -1,10 +1,10 @@
 /* QEMU Synchronous Serial Interface support.  */
 
-/* In principle SSI is a point-point interface.  As such the qemu
-   implementation has a single slave device on a "bus".
+/* In principle SSI is a point-point interface.
    However it is fairly common for boards to have multiple slaves
    connected to a single master, and select devices with an external
-   chip select.  This is implemented in qemu by having an explicit mux device.
+   chip select. SSI busses can therefore have any number of slaves,
+   of which a master can select using ssi_select_slave().
    It is assumed that master and slave are both using the same transfer width.
    */
 
@@ -29,22 +29,42 @@ typedef struct SSISlaveClass {
 
     int (*init)(SSISlave *dev);
     uint32_t (*transfer)(SSISlave *dev, uint32_t val);
+    int (*set_cs)(SSISlave *dev, int state);
 } SSISlaveClass;
 
 struct SSISlave {
     DeviceState qdev;
+
+    int32_t slave_select;
 };
 
 #define SSI_SLAVE_FROM_QDEV(dev) DO_UPCAST(SSISlave, qdev, dev)
 #define FROM_SSI_SLAVE(type, dev) DO_UPCAST(type, ssidev, dev)
 
-DeviceState *ssi_create_slave(SSIBus *bus, const char *name);
+DeviceState *ssi_create_slave(SSIBus *bus, const char *name,
+                                                    int32_t slave_select);
+DeviceState *ssi_create_slave_no_init(SSIBus *bus, const char *name,
+                                                    int32_t slave_select);
 
 /* Master interface.  */
 SSIBus *ssi_create_bus(DeviceState *parent, const char *name);
 
+#define SSI_SLAVE_SELECT_NONE (-1)
+
+void ssi_select_slave(SSIBus *bus, int32_t selected_slave);
+
 uint32_t ssi_transfer(SSIBus *bus, uint32_t val);
 
+extern const VMStateDescription vmstate_ssi_slave;
+
+#define VMSTATE_SSI_SLAVE(_field, _state) {                          \
+    .name       = (stringify(_field)),                               \
+    .size       = sizeof(SSISlave),                                  \
+    .vmsd       = &vmstate_ssi_slave,                                \
+    .flags      = VMS_STRUCT,                                        \
+    .offset     = vmstate_offset_value(_state, _field, SSISlave),    \
+}
+
 /* max111x.c */
 void max111x_set_input(DeviceState *dev, int line, uint8_t value);
 
diff --git a/hw/stellaris.c b/hw/stellaris.c
index 562fbbf..e0600a1 100644
--- a/hw/stellaris.c
+++ b/hw/stellaris.c
@@ -1309,14 +1309,14 @@ static void stellaris_init(const char *kernel_filename, 
const char *cpu_model,
             void *bus;
 
             bus = qdev_get_child_bus(dev, "ssi");
-            mux = ssi_create_slave(bus, "evb6965-ssi");
+            mux = ssi_create_slave(bus, "evb6965-ssi", 0);
             gpio_out[GPIO_D][0] = qdev_get_gpio_in(mux, 0);
 
             bus = qdev_get_child_bus(mux, "ssi0");
-            ssi_create_slave(bus, "ssi-sd");
+            ssi_create_slave(bus, "ssi-sd", 0);
 
             bus = qdev_get_child_bus(mux, "ssi1");
-            dev = ssi_create_slave(bus, "ssd0323");
+            dev = ssi_create_slave(bus, "ssd0323", 0);
             gpio_out[GPIO_C][7] = qdev_get_gpio_in(dev, 0);
 
             /* Make sure the select pin is high.  */
diff --git a/hw/tosa.c b/hw/tosa.c
index 6baa17d..3986810 100644
--- a/hw/tosa.c
+++ b/hw/tosa.c
@@ -196,7 +196,7 @@ static void tosa_tg_init(PXA2xxState *cpu)
 {
     i2c_bus *bus = pxa2xx_i2c_bus(cpu->i2c[0]);
     i2c_create_slave(bus, "tosa_dac", DAC_BASE);
-    ssi_create_slave(cpu->ssp[1], "tosa-ssp");
+    ssi_create_slave(cpu->ssp[1], "tosa-ssp", 0);
 }
 
 
diff --git a/hw/z2.c b/hw/z2.c
index 654ac55..2a0ecf5 100644
--- a/hw/z2.c
+++ b/hw/z2.c
@@ -346,7 +346,7 @@ static void z2_init(ram_addr_t ram_size,
 
     type_register_static(&zipit_lcd_info);
     type_register_static(&aer915_info);
-    z2_lcd = ssi_create_slave(cpu->ssp[1], "zipit-lcd");
+    z2_lcd = ssi_create_slave(cpu->ssp[1], "zipit-lcd", 0);
     bus = pxa2xx_i2c_bus(cpu->i2c[0]);
     i2c_create_slave(bus, "aer915", 0x55);
     wm = i2c_create_slave(bus, "wm8750", 0x1b);
-- 
1.7.3.2




reply via email to

[Prev in Thread] Current Thread [Next in Thread]