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:
parent
37fa5ca426
commit
a78e9839ae
@ -1305,6 +1305,8 @@ static int pxa2xx_i2c_event(I2CSlave *i2c, enum i2c_event event)
|
||||
case I2C_NACK:
|
||||
s->status |= 1 << 1; /* set ACKNAK */
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
pxa2xx_i2c_update(s);
|
||||
|
||||
|
@ -76,6 +76,8 @@ static int sii9022_event(I2CSlave *i2c, enum i2c_event event)
|
||||
break;
|
||||
case I2C_NACK:
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -196,6 +196,8 @@ static int ssd0303_event(I2CSlave *i2c, enum i2c_event event)
|
||||
case I2C_NACK:
|
||||
/* Nothing to do. */
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -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;
|
||||
|
@ -143,6 +143,10 @@ static int smbus_i2c_event(I2CSlave *s, enum i2c_event event)
|
||||
dev->mode = SMBUS_CONFUSED;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -4,7 +4,9 @@
|
||||
|
||||
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_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_ack(void) ""
|
||||
|
||||
# aspeed_i2c.c
|
||||
|
||||
|
@ -75,6 +75,8 @@ int at24c_eeprom_event(I2CSlave *s, enum i2c_event event)
|
||||
break;
|
||||
case I2C_NACK:
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -427,6 +427,8 @@ static int lsm303dlhc_mag_event(I2CSlave *i2c, enum i2c_event event)
|
||||
break;
|
||||
case I2C_NACK:
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
s->len = 0;
|
||||
|
@ -12,6 +12,7 @@
|
||||
enum i2c_event {
|
||||
I2C_START_RECV,
|
||||
I2C_START_SEND,
|
||||
I2C_START_SEND_ASYNC,
|
||||
I2C_FINISH,
|
||||
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. */
|
||||
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
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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_nack(I2CBus *bus);
|
||||
void i2c_ack(I2CBus *bus);
|
||||
void i2c_bus_master(I2CBus *bus, QEMUBH *bh);
|
||||
void i2c_bus_release(I2CBus *bus);
|
||||
int i2c_send(I2CBus *bus, uint8_t data);
|
||||
int i2c_send_async(I2CBus *bus, uint8_t data);
|
||||
uint8_t i2c_recv(I2CBus *bus);
|
||||
bool i2c_scan_bus(I2CBus *bus, uint8_t address, bool broadcast,
|
||||
I2CNodeList *current_devs);
|
||||
|
Loading…
Reference in New Issue
Block a user