diff options
-rw-r--r-- | hw/i2c/core.c | 34 | ||||
-rw-r--r-- | include/hw/i2c/i2c.h | 14 |
2 files changed, 47 insertions, 1 deletions
diff --git a/hw/i2c/core.c b/hw/i2c/core.c index d0cb2d3..145dce6 100644 --- a/hw/i2c/core.c +++ b/hw/i2c/core.c @@ -13,6 +13,7 @@ #include "migration/vmstate.h" #include "qapi/error.h" #include "qemu/module.h" +#include "qemu/main-loop.h" #include "trace.h" #define I2C_BROADCAST 0x00 @@ -62,6 +63,7 @@ I2CBus *i2c_init_bus(DeviceState *parent, const char *name) bus = I2C_BUS(qbus_new(TYPE_I2C_BUS, parent, name)); QLIST_INIT(&bus->current_devs); + QSIMPLEQ_INIT(&bus->pending_masters); vmstate_register(NULL, VMSTATE_INSTANCE_ID_ANY, &vmstate_i2c_bus, bus); return bus; } @@ -74,7 +76,7 @@ void i2c_slave_set_address(I2CSlave *dev, uint8_t address) /* Return nonzero if bus is busy. */ int i2c_bus_busy(I2CBus *bus) { - return !QLIST_EMPTY(&bus->current_devs); + return !QLIST_EMPTY(&bus->current_devs) || bus->bh; } bool i2c_scan_bus(I2CBus *bus, uint8_t address, bool broadcast, @@ -180,6 +182,26 @@ int i2c_start_transfer(I2CBus *bus, uint8_t address, bool is_recv) : I2C_START_SEND); } +void i2c_bus_master(I2CBus *bus, QEMUBH *bh) +{ + if (i2c_bus_busy(bus)) { + I2CPendingMaster *node = g_new(struct I2CPendingMaster, 1); + node->bh = bh; + + QSIMPLEQ_INSERT_TAIL(&bus->pending_masters, node, entry); + + return; + } + + bus->bh = bh; + qemu_bh_schedule(bus->bh); +} + +void i2c_bus_release(I2CBus *bus) +{ + bus->bh = NULL; +} + int i2c_start_recv(I2CBus *bus, uint8_t address) { return i2c_do_start_transfer(bus, address, I2C_START_RECV); @@ -206,6 +228,16 @@ void i2c_end_transfer(I2CBus *bus) g_free(node); } bus->broadcast = false; + + if (!QSIMPLEQ_EMPTY(&bus->pending_masters)) { + I2CPendingMaster *node = QSIMPLEQ_FIRST(&bus->pending_masters); + bus->bh = node->bh; + + QSIMPLEQ_REMOVE_HEAD(&bus->pending_masters, entry); + g_free(node); + + qemu_bh_schedule(bus->bh); + } } int i2c_send(I2CBus *bus, uint8_t data) diff --git a/include/hw/i2c/i2c.h b/include/hw/i2c/i2c.h index 5ca3b70..be8bb8b 100644 --- a/include/hw/i2c/i2c.h +++ b/include/hw/i2c/i2c.h @@ -69,13 +69,25 @@ struct I2CNode { QLIST_ENTRY(I2CNode) next; }; +typedef struct I2CPendingMaster I2CPendingMaster; + +struct I2CPendingMaster { + QEMUBH *bh; + QSIMPLEQ_ENTRY(I2CPendingMaster) entry; +}; + typedef QLIST_HEAD(I2CNodeList, I2CNode) I2CNodeList; +typedef QSIMPLEQ_HEAD(I2CPendingMasters, I2CPendingMaster) I2CPendingMasters; struct I2CBus { BusState qbus; I2CNodeList current_devs; + I2CPendingMasters pending_masters; uint8_t saved_address; bool broadcast; + + /* Set from slave currently mastering the bus. */ + QEMUBH *bh; }; I2CBus *i2c_init_bus(DeviceState *parent, const char *name); @@ -117,6 +129,8 @@ int i2c_start_send(I2CBus *bus, uint8_t address); void i2c_end_transfer(I2CBus *bus); void i2c_nack(I2CBus *bus); +void i2c_bus_master(I2CBus *bus, QEMUBH *bh); +void i2c_bus_release(I2CBus *bus); int i2c_send(I2CBus *bus, uint8_t data); uint8_t i2c_recv(I2CBus *bus); bool i2c_scan_bus(I2CBus *bus, uint8_t address, bool broadcast, |