summaryrefslogtreecommitdiffstats
path: root/hw/i2c
diff options
context:
space:
mode:
authorKlaus Jensen2022-06-30 09:21:14 +0200
committerCédric Le Goater2022-06-30 09:21:14 +0200
commita78e9839ae5eb0b95d9db8dd672e2977d2831605 (patch)
tree1c0d7b678c12f55dd03ff41dc850578d29409be8 /hw/i2c
parenthw/i2c: support multiple masters (diff)
downloadqemu-a78e9839ae5eb0b95d9db8dd672e2977d2831605.tar.gz
qemu-a78e9839ae5eb0b95d9db8dd672e2977d2831605.tar.xz
qemu-a78e9839ae5eb0b95d9db8dd672e2977d2831605.zip
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>
Diffstat (limited to 'hw/i2c')
-rw-r--r--hw/i2c/core.c36
-rw-r--r--hw/i2c/smbus_slave.c4
-rw-r--r--hw/i2c/trace-events2
3 files changed, 41 insertions, 1 deletions
diff --git a/hw/i2c/core.c b/hw/i2c/core.c
index 145dce6078..d4ba8146bf 100644
--- a/hw/i2c/core.c
+++ b/hw/i2c/core.c
@@ -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;
diff --git a/hw/i2c/smbus_slave.c b/hw/i2c/smbus_slave.c
index 5d10e27664..feb3ec6333 100644
--- a/hw/i2c/smbus_slave.c
+++ b/hw/i2c/smbus_slave.c
@@ -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;
diff --git a/hw/i2c/trace-events b/hw/i2c/trace-events
index 209275ed2d..af181d43ee 100644
--- a/hw/i2c/trace-events
+++ b/hw/i2c/trace-events
@@ -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