On 11/16/22 09:43, Klaus Jensen wrote:
From: Klaus Jensen <k.jen...@samsung.com>

It is not given that the current master will release the bus after a
transfer ends. Only schedule a pending master if the bus is idle.

Fixes: 37fa5ca42623 ("hw/i2c: support multiple masters")
Signed-off-by: Klaus Jensen <k.jen...@samsung.com>
---
  hw/i2c/aspeed_i2c.c  |  2 ++
  hw/i2c/core.c        | 37 ++++++++++++++++++++++---------------
  include/hw/i2c/i2c.h |  2 ++
  3 files changed, 26 insertions(+), 15 deletions(-)

diff --git a/hw/i2c/aspeed_i2c.c b/hw/i2c/aspeed_i2c.c
index c166fd20fa11..1f071a3811f7 100644
--- a/hw/i2c/aspeed_i2c.c
+++ b/hw/i2c/aspeed_i2c.c
@@ -550,6 +550,8 @@ static void aspeed_i2c_bus_handle_cmd(AspeedI2CBus *bus, 
uint64_t value)
          }
          SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, M_STOP_CMD, 0);
          aspeed_i2c_set_state(bus, I2CD_IDLE);
+
+        i2c_schedule_pending_master(bus->bus);

Shouldn't it be i2c_bus_release() ?

Thanks,

C.


      }
if (aspeed_i2c_bus_pkt_mode_en(bus)) {
diff --git a/hw/i2c/core.c b/hw/i2c/core.c
index d4ba8146bffb..bed594fe599b 100644
--- a/hw/i2c/core.c
+++ b/hw/i2c/core.c
@@ -185,22 +185,39 @@ int i2c_start_transfer(I2CBus *bus, uint8_t address, bool 
is_recv)
void i2c_bus_master(I2CBus *bus, QEMUBH *bh)
  {
+    I2CPendingMaster *node = g_new(struct I2CPendingMaster, 1);
+    node->bh = bh;
+
+    QSIMPLEQ_INSERT_TAIL(&bus->pending_masters, node, entry);
+}
+
+void i2c_schedule_pending_master(I2CBus *bus)
+{
+    I2CPendingMaster *node;
+
      if (i2c_bus_busy(bus)) {
-        I2CPendingMaster *node = g_new(struct I2CPendingMaster, 1);
-        node->bh = bh;
-
-        QSIMPLEQ_INSERT_TAIL(&bus->pending_masters, node, entry);
+        /* someone is already controlling the bus; wait for it to release it */
+        return;
+    }
+ if (QSIMPLEQ_EMPTY(&bus->pending_masters)) {
          return;
      }
- bus->bh = bh;
+    node = QSIMPLEQ_FIRST(&bus->pending_masters);
+    bus->bh = node->bh;
+
+    QSIMPLEQ_REMOVE_HEAD(&bus->pending_masters, entry);
+    g_free(node);
+
      qemu_bh_schedule(bus->bh);
  }
void i2c_bus_release(I2CBus *bus)
  {
      bus->bh = NULL;
+
+    i2c_schedule_pending_master(bus);
  }
int i2c_start_recv(I2CBus *bus, uint8_t address)
@@ -234,16 +251,6 @@ void i2c_end_transfer(I2CBus *bus)
          g_free(node);
      }
      bus->broadcast = false;
-
-    if (!QSIMPLEQ_EMPTY(&bus->pending_masters)) {
-        I2CPendingMaster *node = QSIMPLEQ_FIRST(&bus->pending_masters);
-        bus->bh = node->bh;
-
-        QSIMPLEQ_REMOVE_HEAD(&bus->pending_masters, entry);
-        g_free(node);
-
-        qemu_bh_schedule(bus->bh);
-    }
  }
int i2c_send(I2CBus *bus, uint8_t data)
diff --git a/include/hw/i2c/i2c.h b/include/hw/i2c/i2c.h
index 9b9581d23097..2a3abacd1ba6 100644
--- a/include/hw/i2c/i2c.h
+++ b/include/hw/i2c/i2c.h
@@ -141,6 +141,8 @@ int i2c_start_send(I2CBus *bus, uint8_t address);
   */
  int i2c_start_send_async(I2CBus *bus, uint8_t address);
+void i2c_schedule_pending_master(I2CBus *bus);
+
  void i2c_end_transfer(I2CBus *bus);
  void i2c_nack(I2CBus *bus);
  void i2c_ack(I2CBus *bus);


Reply via email to