diff mbox series

[11/16] hw/i3c/aspeed_i3c: Add ctrl MMIO handling

Message ID 20230331010131.1412571-12-komlodi@google.com (mailing list archive)
State New, archived
Headers show
Series i3c: aspeed: Add I3C support | expand

Commit Message

Joe Komlodi March 31, 2023, 1:01 a.m. UTC
Adds functionality to the CTRL register.

Signed-off-by: Joe Komlodi <komlodi@google.com>

Reviewed-by: Titus Rwantare <titusr@google.com>
Reviewed-by: Patrick Venture <venture@google.com>
---
 hw/i3c/aspeed_i3c.c | 36 ++++++++++++++++++++++++++++++++++++
 1 file changed, 36 insertions(+)
diff mbox series

Patch

diff --git a/hw/i3c/aspeed_i3c.c b/hw/i3c/aspeed_i3c.c
index 055ad3f7fd..4c0c074012 100644
--- a/hw/i3c/aspeed_i3c.c
+++ b/hw/i3c/aspeed_i3c.c
@@ -340,6 +340,8 @@  REG32(DEVICE_ADDR_TABLE_LOC1, 0x280)
     FIELD(DEVICE_ADDR_TABLE_LOC1, DEV_NACK_RETRY_CNT, 29, 2)
     FIELD(DEVICE_ADDR_TABLE_LOC1, LEGACY_I2C_DEVICE, 31, 1)
 
+static void aspeed_i3c_device_cmd_queue_execute(AspeedI3CDevice *s);
+
 static const uint32_t ast2600_i3c_controller_ro[ASPEED_I3C_DEVICE_NR_REGS] = {
     [R_I3C1_REG0]                   = 0xfc000000,
     [R_I3C1_REG1]                   = 0xfff00000,
@@ -588,6 +590,37 @@  static int aspeed_i3c_device_recv_data(AspeedI3CDevice *s, bool is_i2c,
     return ret;
 }
 
+static inline void aspeed_i3c_device_ctrl_w(AspeedI3CDevice *s,
+                                                   uint32_t val)
+{
+    /*
+     * If the user is setting I3C_RESUME, the controller was halted.
+     * Try and resume execution and leave the bit cleared.
+     */
+    if (FIELD_EX32(val, DEVICE_CTRL, I3C_RESUME)) {
+        aspeed_i3c_device_cmd_queue_execute(s);
+        val = FIELD_DP32(val, DEVICE_CTRL, I3C_RESUME, 0);
+    }
+    /*
+     * I3C_ABORT being set sends an I3C STOP. It's cleared when the STOP is
+     * sent.
+     */
+    if (FIELD_EX32(val, DEVICE_CTRL, I3C_ABORT)) {
+        aspeed_i3c_device_end_transfer(s, /*is_i2c=*/true);
+        aspeed_i3c_device_end_transfer(s, /*is_i2c=*/false);
+        val = FIELD_DP32(val, DEVICE_CTRL, I3C_ABORT, 0);
+        ARRAY_FIELD_DP32(s->regs, INTR_STATUS, TRANSFER_ABORT, 1);
+        aspeed_i3c_device_update_irq(s);
+    }
+    /* Update present state. */
+    ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS,
+                     ASPEED_I3C_TRANSFER_STATE_IDLE);
+    ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_STATUS,
+                     ASPEED_I3C_TRANSFER_STATUS_IDLE);
+
+    s->regs[R_DEVICE_CTRL] = val;
+}
+
 static inline bool aspeed_i3c_device_target_is_i2c(AspeedI3CDevice *s,
                                                    uint16_t offset)
 {
@@ -1650,6 +1683,9 @@  static void aspeed_i3c_device_write(void *opaque, hwaddr offset,
                       "] = 0x%08" PRIx64 "\n",
                       __func__, offset, value);
         break;
+    case R_DEVICE_CTRL:
+        aspeed_i3c_device_ctrl_w(s, val32);
+        break;
     case R_RX_TX_DATA_PORT:
         aspeed_i3c_device_push_tx(s, val32);
         break;