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: 37fa5ca426 ("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>
This commit is contained in:
Klaus Jensen 2023-03-02 13:57:50 +01:00 committed by Cédric Le Goater
parent 9b29a468bd
commit 791cb95f23
3 changed files with 27 additions and 16 deletions

View File

@ -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)) {

View File

@ -185,22 +185,39 @@ int i2c_start_transfer(I2CBus *bus, uint8_t address, bool is_recv)
void i2c_bus_master(I2CBus *bus, QEMUBH *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;
if (i2c_bus_busy(bus)) {
I2CPendingMaster *node = g_new(struct I2CPendingMaster, 1);
node->bh = bh;
QSIMPLEQ_INSERT_TAIL(&bus->pending_masters, node, entry);
/* someone is already controlling the bus; wait for it to release it */
return;
}
bus->bh = bh;
if (QSIMPLEQ_EMPTY(&bus->pending_masters)) {
return;
}
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)

View File

@ -141,6 +141,8 @@ int i2c_start_send(I2CBus *bus, uint8_t address);
*/
int i2c_start_send_async(I2CBus *bus, uint8_t address);
void i2c_schedule_pending_master(I2CBus *bus);
void i2c_end_transfer(I2CBus *bus);
void i2c_nack(I2CBus *bus);
void i2c_ack(I2CBus *bus);