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

@ -1305,6 +1305,8 @@ static int pxa2xx_i2c_event(I2CSlave *i2c, enum i2c_event event)
case I2C_NACK: case I2C_NACK:
s->status |= 1 << 1; /* set ACKNAK */ s->status |= 1 << 1; /* set ACKNAK */
break; break;
default:
return -1;
} }
pxa2xx_i2c_update(s); pxa2xx_i2c_update(s);

View File

@ -76,6 +76,8 @@ static int sii9022_event(I2CSlave *i2c, enum i2c_event event)
break; break;
case I2C_NACK: case I2C_NACK:
break; break;
default:
return -1;
} }
return 0; return 0;

View File

@ -196,6 +196,8 @@ static int ssd0303_event(I2CSlave *i2c, enum i2c_event event)
case I2C_NACK: case I2C_NACK:
/* Nothing to do. */ /* Nothing to do. */
break; break;
default:
return -1;
} }
return 0; return 0;

View File

@ -161,7 +161,8 @@ static int i2c_do_start_transfer(I2CBus *bus, uint8_t address,
start condition. */ start condition. */
if (sc->event) { 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); rv = sc->event(s, event);
if (rv && !bus->broadcast) { if (rv && !bus->broadcast) {
if (bus_scanned) { 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); 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) void i2c_end_transfer(I2CBus *bus)
{ {
I2CSlaveClass *sc; I2CSlaveClass *sc;
@ -261,6 +267,23 @@ int i2c_send(I2CBus *bus, uint8_t data)
return ret ? -1 : 0; 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 i2c_recv(I2CBus *bus)
{ {
uint8_t data = 0xff; 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) static int i2c_slave_post_load(void *opaque, int version_id)
{ {
I2CSlave *dev = opaque; I2CSlave *dev = opaque;

View File

@ -143,6 +143,10 @@ static int smbus_i2c_event(I2CSlave *s, enum i2c_event event)
dev->mode = SMBUS_CONFUSED; dev->mode = SMBUS_CONFUSED;
break; break;
} }
break;
default:
return -1;
} }
return 0; return 0;

View File

@ -4,7 +4,9 @@
i2c_event(const char *event, uint8_t address) "%s(addr:0x%02x)" i2c_event(const char *event, uint8_t address) "%s(addr:0x%02x)"
i2c_send(uint8_t address, uint8_t data) "send(addr:0x%02x) data:0x%02x" i2c_send(uint8_t address, uint8_t data) "send(addr:0x%02x) data:0x%02x"
i2c_send_async(uint8_t address, uint8_t data) "send_async(addr:0x%02x) data:0x%02x"
i2c_recv(uint8_t address, uint8_t data) "recv(addr:0x%02x) data:0x%02x" i2c_recv(uint8_t address, uint8_t data) "recv(addr:0x%02x) data:0x%02x"
i2c_ack(void) ""
# aspeed_i2c.c # aspeed_i2c.c

View File

@ -75,6 +75,8 @@ int at24c_eeprom_event(I2CSlave *s, enum i2c_event event)
break; break;
case I2C_NACK: case I2C_NACK:
break; break;
default:
return -1;
} }
return 0; return 0;
} }

View File

@ -427,6 +427,8 @@ static int lsm303dlhc_mag_event(I2CSlave *i2c, enum i2c_event event)
break; break;
case I2C_NACK: case I2C_NACK:
break; break;
default:
return -1;
} }
s->len = 0; s->len = 0;

View File

@ -12,6 +12,7 @@
enum i2c_event { enum i2c_event {
I2C_START_RECV, I2C_START_RECV,
I2C_START_SEND, I2C_START_SEND,
I2C_START_SEND_ASYNC,
I2C_FINISH, I2C_FINISH,
I2C_NACK /* Masker NACKed a receive byte. */ I2C_NACK /* Masker NACKed a receive byte. */
}; };
@ -28,6 +29,9 @@ struct I2CSlaveClass {
/* Master to slave. Returns non-zero for a NAK, 0 for success. */ /* Master to slave. Returns non-zero for a NAK, 0 for success. */
int (*send)(I2CSlave *s, uint8_t data); int (*send)(I2CSlave *s, uint8_t data);
/* Master to slave (asynchronous). Receiving slave must call i2c_ack(). */
void (*send_async)(I2CSlave *s, uint8_t data);
/* /*
* Slave to master. This cannot fail, the device should always * Slave to master. This cannot fail, the device should always
* return something here. * return something here.
@ -127,11 +131,23 @@ int i2c_start_recv(I2CBus *bus, uint8_t address);
*/ */
int i2c_start_send(I2CBus *bus, uint8_t address); int i2c_start_send(I2CBus *bus, uint8_t address);
/**
* i2c_start_send_async: start an asynchronous 'send' transfer on an I2C bus.
*
* @bus: #I2CBus to be used
* @address: address of the slave
*
* Return: 0 on success, -1 on error
*/
int i2c_start_send_async(I2CBus *bus, uint8_t address);
void i2c_end_transfer(I2CBus *bus); void i2c_end_transfer(I2CBus *bus);
void i2c_nack(I2CBus *bus); void i2c_nack(I2CBus *bus);
void i2c_ack(I2CBus *bus);
void i2c_bus_master(I2CBus *bus, QEMUBH *bh); void i2c_bus_master(I2CBus *bus, QEMUBH *bh);
void i2c_bus_release(I2CBus *bus); void i2c_bus_release(I2CBus *bus);
int i2c_send(I2CBus *bus, uint8_t data); int i2c_send(I2CBus *bus, uint8_t data);
int i2c_send_async(I2CBus *bus, uint8_t data);
uint8_t i2c_recv(I2CBus *bus); uint8_t i2c_recv(I2CBus *bus);
bool i2c_scan_bus(I2CBus *bus, uint8_t address, bool broadcast, bool i2c_scan_bus(I2CBus *bus, uint8_t address, bool broadcast,
I2CNodeList *current_devs); I2CNodeList *current_devs);