diff options
author | Klaus Jensen <k.jensen@samsung.com> | 2023-03-02 13:57:50 +0100 |
---|---|---|
committer | Cédric Le Goater <clg@kaod.org> | 2023-03-02 13:57:50 +0100 |
commit | 791cb95f23045f2d69364786edf61d289889199d (patch) | |
tree | 5182af8a274a8fde5e5c18e5e593a88339fc72aa /hw | |
parent | 9b29a468bd0ca6994dc3d347a01af743c8fe76a7 (diff) | |
download | qemu-791cb95f23045f2d69364786edf61d289889199d.zip qemu-791cb95f23045f2d69364786edf61d289889199d.tar.gz qemu-791cb95f23045f2d69364786edf61d289889199d.tar.bz2 |
hw/i2c: only schedule pending master when bus is idle
It is not given that the current master will release the bus after a
transfer ends. Only schedule a pending master if the bus is idle.
Fixes: 37fa5ca42623 ("hw/i2c: support multiple masters")
Signed-off-by: Klaus Jensen <k.jensen@samsung.com>
Acked-by: Corey Minyard <cminyard@mvista.com>
Message-Id: <20221116084312.35808-2-its@irrelevant.dk>
Signed-off-by: Cédric Le Goater <clg@kaod.org>
Diffstat (limited to 'hw')
-rw-r--r-- | hw/i2c/aspeed_i2c.c | 2 | ||||
-rw-r--r-- | hw/i2c/core.c | 37 |
2 files changed, 24 insertions, 15 deletions
diff --git a/hw/i2c/aspeed_i2c.c b/hw/i2c/aspeed_i2c.c index c166fd2..1f071a3 100644 --- a/hw/i2c/aspeed_i2c.c +++ b/hw/i2c/aspeed_i2c.c @@ -550,6 +550,8 @@ static void aspeed_i2c_bus_handle_cmd(AspeedI2CBus *bus, uint64_t value) } SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, M_STOP_CMD, 0); aspeed_i2c_set_state(bus, I2CD_IDLE); + + i2c_schedule_pending_master(bus->bus); } if (aspeed_i2c_bus_pkt_mode_en(bus)) { diff --git a/hw/i2c/core.c b/hw/i2c/core.c index d4ba814..bed594f 100644 --- a/hw/i2c/core.c +++ b/hw/i2c/core.c @@ -185,22 +185,39 @@ int i2c_start_transfer(I2CBus *bus, uint8_t address, bool is_recv) void i2c_bus_master(I2CBus *bus, QEMUBH *bh) { - if (i2c_bus_busy(bus)) { - I2CPendingMaster *node = g_new(struct I2CPendingMaster, 1); - node->bh = bh; + I2CPendingMaster *node = g_new(struct I2CPendingMaster, 1); + node->bh = bh; + + QSIMPLEQ_INSERT_TAIL(&bus->pending_masters, node, entry); +} + +void i2c_schedule_pending_master(I2CBus *bus) +{ + I2CPendingMaster *node; - QSIMPLEQ_INSERT_TAIL(&bus->pending_masters, node, entry); + if (i2c_bus_busy(bus)) { + /* someone is already controlling the bus; wait for it to release it */ + return; + } + if (QSIMPLEQ_EMPTY(&bus->pending_masters)) { return; } - bus->bh = bh; + 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); } void i2c_bus_release(I2CBus *bus) { bus->bh = NULL; + + i2c_schedule_pending_master(bus); } int i2c_start_recv(I2CBus *bus, uint8_t address) @@ -234,16 +251,6 @@ 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) |