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:
|
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);
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
Loading…
Reference in New Issue
Block a user