@@ -116,7 +116,7 @@ common-obj-$(CONFIG_SSD0323) += ssd0323.o
common-obj-$(CONFIG_ADS7846) += ads7846.o
common-obj-$(CONFIG_MAX111X) += max111x.o
common-obj-$(CONFIG_DS1338) += ds1338.o
-common-obj-y += i2c.o smbus.o smbus_eeprom.o
+common-obj-y += i2c.o smbus.o smbus_eeprom.o spi.o
common-obj-y += eeprom93xx.o
common-obj-y += scsi-disk.o cdrom.o
common-obj-y += scsi-generic.o scsi-bus.o
new file mode 100644
@@ -0,0 +1,148 @@
+/*
+ * QEMU SPI bus interface.
+ *
+ * Copyright (C) 2012 Peter A. G. Crosthwaite <peter.crosthwaite@petalogix.com>
+ * Copyright (C) 2012 PetaLogix
+ *
+ * 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 "spi.h"
+
+static struct BusInfo SPIBus_info = {
+ .name = "SPI",
+ .size = sizeof(SPIBus)
+};
+
+static const VMStateDescription vmstate_SPIBus = {
+ .name = "SPIBus",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT8(cur_slave, SPIBus),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+SPIBus *spi_init_bus(DeviceState *parent, int num_slaves, const char *name)
+{
+ SPIBus *bus;
+
+ bus = FROM_QBUS(SPIBus, qbus_create(&SPIBus_info, parent, name));
+ if (num_slaves >= SPIBus_NO_CS) {
+ hw_error("too many slaves on spi bus: %d\n", num_slaves);
+ }
+ bus->num_slaves = num_slaves;
+ bus->slaves = g_malloc0(sizeof(*bus->slaves) * num_slaves);
+ vmstate_register(NULL, -1, &vmstate_SPIBus, bus);
+ return bus;
+}
+
+int spi_attach_slave(SPIBus *bus, SPISlave *slave, int cs)
+{
+ if (bus->slaves[cs]) {
+ return 1;
+ }
+ bus->slaves[cs] = slave;
+ return 0;
+}
+
+int spi_set_cs(SPIBus *bus, int cs)
+{
+ SPISlave *dev;
+ SPISlaveClass *klass;
+
+ if (bus->cur_slave == cs) {
+ return 0;
+ }
+
+ if (bus->cur_slave != SPIBus_NO_CS) {
+ dev = bus->slaves[bus->cur_slave];
+ dev->cs = 0;
+ klass = SPI_SLAVE_GET_CLASS(dev);
+ klass->cs(dev, 0);
+ }
+
+ if (cs >= bus->num_slaves && cs != SPIBus_NO_CS) {
+ hw_error("attempted to assert non existent spi CS line: %d\n", cs);
+ }
+
+ bus->cur_slave = (uint8_t)cs;
+
+ if (cs != SPIBus_NO_CS) {
+ dev = bus->slaves[cs];
+ dev->cs = 1;
+ klass = SPI_SLAVE_GET_CLASS(dev);
+ klass->cs(dev, 1);
+ }
+ return 0;
+};
+
+int spi_get_cs(SPIBus *bus)
+{
+ return bus->cur_slave;
+}
+
+int spi_txrx(SPIBus *bus, uint8_t *tx, uint8_t *txz, uint8_t *rx,
+ uint8_t *rxz, int len)
+{
+ SPISlave *dev;
+ SPISlaveClass *klass;
+
+ if (bus->cur_slave == SPIBus_NO_CS) {
+ return 1;
+ }
+ dev = bus->slaves[bus->cur_slave];
+ klass = SPI_SLAVE_GET_CLASS(dev);
+
+ return klass->txrx(dev, tx, txz, rx, rxz, len);
+}
+
+const VMStateDescription vmstate_spi_slave = {
+ .name = "SPISlave",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT8(cs, SPISlave),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static int spi_slave_qdev_init(DeviceState *dev)
+{
+ SPISlave *s = SPI_SLAVE_FROM_QDEV(dev);
+ SPISlaveClass *sc = SPI_SLAVE_GET_CLASS(s);
+
+ return sc->init(s);
+}
+
+static void spi_slave_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *k = DEVICE_CLASS(klass);
+ k->init = spi_slave_qdev_init;
+ k->bus_info = &SPIBus_info;
+}
+
+static TypeInfo spi_slave_type_info = {
+ .name = TYPE_SPI_SLAVE,
+ .parent = TYPE_DEVICE,
+ .instance_size = sizeof(SPISlave),
+ .abstract = true,
+ .class_size = sizeof(SPISlaveClass),
+ .class_init = spi_slave_class_init,
+};
+
+static void spi_slave_register_types(void)
+{
+ type_register_static(&spi_slave_type_info);
+}
+
+type_init(spi_slave_register_types)
new file mode 100644
@@ -0,0 +1,69 @@
+#ifndef QEMU_SPI_H
+#define QEMU_SPI_H
+
+#include "qdev.h"
+
+/* pass to spi_set_cs to deslect all devices on bus */
+
+#define SPIBus_NO_CS 0xFF
+
+typedef struct SPISlave {
+ DeviceState qdev;
+ uint8_t cs;
+} SPISlave;
+
+#define TYPE_SPI_SLAVE "spi-slave"
+#define SPI_SLAVE(obj) \
+ OBJECT_CHECK(SPISlave, (obj), TYPE_SPI_SLAVE)
+#define SPI_SLAVE_CLASS(klass) \
+ OBJECT_CLASS_CHECK(SPISlaveClass, (klass), TYPE_SPI_SLAVE)
+#define SPI_SLAVE_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(SPISlaveClass, (obj), TYPE_SPI_SLAVE)
+
+typedef struct SPISlaveClass {
+ DeviceClass parent_class;
+
+ /* Callbacks provided by the device. */
+ int (*init)(SPISlave *s);
+
+ /* change the cs pin state */
+ void (*cs)(SPISlave *s, uint8_t select);
+
+ /* transaction */
+ int (*txrx)(SPISlave *s, uint8_t *tx, uint8_t *txz, uint8_t *rx,
+ uint8_t *rxz, int len);
+
+} SPISlaveClass;
+
+#define SPI_SLAVE_FROM_QDEV(dev) DO_UPCAST(SPISlave, qdev, dev)
+#define FROM_SPI_SLAVE(type, dev) DO_UPCAST(type, spi, dev)
+
+extern const VMStateDescription vmstate_spi_slave;
+
+#define VMSTATE_SPI_SLAVE(_field, _state) { \
+ .name = (stringify(_field)), \
+ .size = sizeof(SPISlave), \
+ .vmsd = &vmstate_spi_slave, \
+ .flags = VMS_STRUCT, \
+ .offset = vmstate_offset_value(_state, _field, SPISlave), \
+}
+
+typedef struct SPIBus {
+ BusState qbus;
+ SPISlave **slaves;
+ uint8_t num_slaves;
+ uint8_t cur_slave;
+} SPIBus;
+
+/* create a new spi bus */
+SPIBus *spi_init_bus(DeviceState *parent, int num_slaves, const char *name);
+int spi_attach_slave(SPIBus *bus, SPISlave *s, int cs);
+
+/* change the chip select. Return 1 on failure. */
+int spi_set_cs(SPIBus *bus, int cs);
+int spi_get_cs(SPIBus *bus);
+
+int spi_txrx(SPIBus *s, uint8_t *tx, uint8_t *txz, uint8_t *rx,
+ uint8_t *rxz, int len);
+
+#endif
Defined SPI bus and SPI slave QOM interfaces. Inspired by and loosely based on existing I2C framework. Signed-off-by: Peter A. G. Crosthwaite <peter.crosthwaite@petalogix.com> --- changed from v1: minor sylistic changes converted spi api to modified txrx style Makefile.objs | 2 +- hw/spi.c | 148 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/spi.h | 69 ++++++++++++++++++++++++++ 3 files changed, 218 insertions(+), 1 deletions(-) create mode 100644 hw/spi.c create mode 100644 hw/spi.h