hw/i2c: add asynchronous send

Add an asynchronous version of i2c_send() that requires the slave to
explicitly acknowledge on the bus with i2c_ack().

The current master must use the new i2c_start_send_async() to indicate
that it wants to do an asynchronous transfer. This allows the i2c core
to check if the target slave supports this or not. This approach relies
on adding a new enum i2c_event member, which is why a bunch of other
devices needs changes in their event handling switches.

Signed-off-by: Klaus Jensen <k.jensen@samsung.com>
Message-Id: <20220601210831.67259-5-its@irrelevant.dk>
Signed-off-by: Cédric Le Goater <clg@kaod.org>
Message-Id: <20220630045133.32251-6-me@pjd.dev>
Signed-off-by: Cédric Le Goater <clg@kaod.org>
This commit is contained in:
Klaus Jensen 2022-06-30 09:21:14 +02:00 committed by Cédric Le Goater
parent 37fa5ca426
commit a78e9839ae
9 changed files with 67 additions and 1 deletions

View file

@ -161,7 +161,8 @@ static int i2c_do_start_transfer(I2CBus *bus, uint8_t address,
start condition. */
if (sc->event) {
trace_i2c_event("start", s->address);
trace_i2c_event(event == I2C_START_SEND ? "start" : "start_async",
s->address);
rv = sc->event(s, event);
if (rv && !bus->broadcast) {
if (bus_scanned) {
@ -212,6 +213,11 @@ int i2c_start_send(I2CBus *bus, uint8_t address)
return i2c_do_start_transfer(bus, address, I2C_START_SEND);
}
int i2c_start_send_async(I2CBus *bus, uint8_t address)
{
return i2c_do_start_transfer(bus, address, I2C_START_SEND_ASYNC);
}
void i2c_end_transfer(I2CBus *bus)
{
I2CSlaveClass *sc;
@ -261,6 +267,23 @@ int i2c_send(I2CBus *bus, uint8_t data)
return ret ? -1 : 0;
}
int i2c_send_async(I2CBus *bus, uint8_t data)
{
I2CNode *node = QLIST_FIRST(&bus->current_devs);
I2CSlave *slave = node->elt;
I2CSlaveClass *sc = I2C_SLAVE_GET_CLASS(slave);
if (!sc->send_async) {
return -1;
}
trace_i2c_send_async(slave->address, data);
sc->send_async(slave, data);
return 0;
}
uint8_t i2c_recv(I2CBus *bus)
{
uint8_t data = 0xff;
@ -297,6 +320,17 @@ void i2c_nack(I2CBus *bus)
}
}
void i2c_ack(I2CBus *bus)
{
if (!bus->bh) {
return;
}
trace_i2c_ack();
qemu_bh_schedule(bus->bh);
}
static int i2c_slave_post_load(void *opaque, int version_id)
{
I2CSlave *dev = opaque;