mirror of
https://github.com/qemu/qemu.git
synced 2025-08-14 20:31:47 +00:00

ssi_auto_connect_slaves(parent, cs_line, bus) iterates over @parent's
QOM children @dev of type TYPE_SSI_SLAVE. It puts these on @bus, and
sets cs_line[] to qdev_get_gpio_in_named(dev, SSI_GPIO_CS, 0).
Suspicious: there is no protection against overrunning cs_line[].
Turns out it's safe because ssi_auto_connect_slaves() never finds any
such children. Its called by realize methods of some (but not all)
devices providing an SSI bus, and gets passed the device.
SSI slave devices are always created with ssi_create_slave_no_init(),
optionally via ssi_create_slave(). This adds them to their SSI bus.
It doesn't set their QOM parent.
ssi_create_slave_no_init() is always immediately followed by
qdev_init_nofail(), with no QOM parent assigned, so
device_set_realized() puts the device into the /machine/unattached/
orphanage. None become QOM children of a device providing an SSI bus.
ssi_auto_connect_slaves() was added in commit b4ae3cfa57
"ssi: Add
slave autoconnect helper". I can't see which slaves it was supposed
to connect back then.
Cc: Alistair Francis <alistair@alistair23.me>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Acked-by: Alistair Francis <alistair.francis@wdc.com>
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
Message-Id: <20200610053247.1583243-23-armbru@redhat.com>
93 lines
3.0 KiB
C
93 lines
3.0 KiB
C
/* 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".
|
|
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.
|
|
It is assumed that master and slave are both using the same transfer width.
|
|
*/
|
|
|
|
#ifndef QEMU_SSI_H
|
|
#define QEMU_SSI_H
|
|
|
|
#include "hw/qdev-core.h"
|
|
|
|
typedef struct SSISlave SSISlave;
|
|
typedef struct SSISlaveClass SSISlaveClass;
|
|
typedef enum SSICSMode SSICSMode;
|
|
|
|
#define TYPE_SSI_SLAVE "ssi-slave"
|
|
#define SSI_SLAVE(obj) \
|
|
OBJECT_CHECK(SSISlave, (obj), TYPE_SSI_SLAVE)
|
|
#define SSI_SLAVE_CLASS(klass) \
|
|
OBJECT_CLASS_CHECK(SSISlaveClass, (klass), TYPE_SSI_SLAVE)
|
|
#define SSI_SLAVE_GET_CLASS(obj) \
|
|
OBJECT_GET_CLASS(SSISlaveClass, (obj), TYPE_SSI_SLAVE)
|
|
|
|
#define SSI_GPIO_CS "ssi-gpio-cs"
|
|
|
|
enum SSICSMode {
|
|
SSI_CS_NONE = 0,
|
|
SSI_CS_LOW,
|
|
SSI_CS_HIGH,
|
|
};
|
|
|
|
/* Slave devices. */
|
|
struct SSISlaveClass {
|
|
DeviceClass parent_class;
|
|
|
|
void (*realize)(SSISlave *dev, Error **errp);
|
|
|
|
/* if you have standard or no CS behaviour, just override transfer.
|
|
* This is called when the device cs is active (true by default).
|
|
*/
|
|
uint32_t (*transfer)(SSISlave *dev, uint32_t val);
|
|
/* called when the CS line changes. Optional, devices only need to implement
|
|
* this if they have side effects associated with the cs line (beyond
|
|
* tristating the txrx lines).
|
|
*/
|
|
int (*set_cs)(SSISlave *dev, bool select);
|
|
/* define whether or not CS exists and is active low/high */
|
|
SSICSMode cs_polarity;
|
|
|
|
/* if you have non-standard CS behaviour override this to take control
|
|
* of the CS behaviour at the device level. transfer, set_cs, and
|
|
* cs_polarity are unused if this is overwritten. Transfer_raw will
|
|
* always be called for the device for every txrx access to the parent bus
|
|
*/
|
|
uint32_t (*transfer_raw)(SSISlave *dev, uint32_t val);
|
|
};
|
|
|
|
struct SSISlave {
|
|
DeviceState parent_obj;
|
|
|
|
/* Chip select state */
|
|
bool cs;
|
|
};
|
|
|
|
#define FROM_SSI_SLAVE(type, dev) DO_UPCAST(type, ssidev, dev)
|
|
|
|
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), \
|
|
}
|
|
|
|
DeviceState *ssi_create_slave(SSIBus *bus, const char *name);
|
|
DeviceState *ssi_create_slave_no_init(SSIBus *bus, const char *name);
|
|
|
|
/* Master interface. */
|
|
SSIBus *ssi_create_bus(DeviceState *parent, const char *name);
|
|
|
|
uint32_t ssi_transfer(SSIBus *bus, uint32_t val);
|
|
|
|
/* max111x.c */
|
|
void max111x_set_input(DeviceState *dev, int line, uint8_t value);
|
|
|
|
#endif
|