xref: /openbmc/qemu/hw/i3c/aspeed_i3c.c (revision eee51e8f03858655900f9fdea5975b61f8fa65bf)
17d87775fSJoe Komlodi /*
27d87775fSJoe Komlodi  * ASPEED I3C Controller
37d87775fSJoe Komlodi  *
47d87775fSJoe Komlodi  * Copyright (C) 2021 ASPEED Technology Inc.
5e7b1406cSJoe Komlodi  * Copyright (C) 2023 Google LLC
67d87775fSJoe Komlodi  *
77d87775fSJoe Komlodi  * This code is licensed under the GPL version 2 or later.  See
87d87775fSJoe Komlodi  * the COPYING file in the top-level directory.
97d87775fSJoe Komlodi  */
107d87775fSJoe Komlodi 
117d87775fSJoe Komlodi #include "qemu/osdep.h"
127d87775fSJoe Komlodi #include "qemu/log.h"
137d87775fSJoe Komlodi #include "qemu/error-report.h"
147d87775fSJoe Komlodi #include "hw/i3c/aspeed_i3c.h"
157d87775fSJoe Komlodi #include "hw/registerfields.h"
167d87775fSJoe Komlodi #include "hw/qdev-properties.h"
177d87775fSJoe Komlodi #include "qapi/error.h"
187d87775fSJoe Komlodi #include "migration/vmstate.h"
197d87775fSJoe Komlodi #include "trace.h"
203816bedaSJoe Komlodi #include "hw/i3c/i3c.h"
213816bedaSJoe Komlodi #include "hw/irq.h"
227d87775fSJoe Komlodi 
2393ec6949SJoe Komlodi /*
2493ec6949SJoe Komlodi  * Disable event command values. sent along with a DISEC CCC to disable certain
2593ec6949SJoe Komlodi  * events on targets.
2693ec6949SJoe Komlodi  */
2793ec6949SJoe Komlodi #define DISEC_HJ 0x08
2893ec6949SJoe Komlodi #define DISEC_CR 0x02
2993ec6949SJoe Komlodi #define DISEC_INT 0x01
3093ec6949SJoe Komlodi 
317d87775fSJoe Komlodi /* I3C Controller Registers */
327d87775fSJoe Komlodi REG32(I3C1_REG0, 0x10)
337d87775fSJoe Komlodi REG32(I3C1_REG1, 0x14)
347d87775fSJoe Komlodi     FIELD(I3C1_REG1, I2C_MODE,      0,  1)
35e7b1406cSJoe Komlodi     FIELD(I3C1_REG1, SLV_TEST_MODE, 1,  1)
36e7b1406cSJoe Komlodi     FIELD(I3C1_REG1, ACT_MODE,      2,  2)
37e7b1406cSJoe Komlodi     FIELD(I3C1_REG1, PENDING_INT,   4,  4)
38e7b1406cSJoe Komlodi     FIELD(I3C1_REG1, SA,            8,  7)
397d87775fSJoe Komlodi     FIELD(I3C1_REG1, SA_EN,         15, 1)
40e7b1406cSJoe Komlodi     FIELD(I3C1_REG1, INST_ID,       16, 4)
417d87775fSJoe Komlodi REG32(I3C2_REG0, 0x20)
427d87775fSJoe Komlodi REG32(I3C2_REG1, 0x24)
437d87775fSJoe Komlodi     FIELD(I3C2_REG1, I2C_MODE,      0,  1)
44e7b1406cSJoe Komlodi     FIELD(I3C2_REG1, SLV_TEST_MODE, 1,  1)
45e7b1406cSJoe Komlodi     FIELD(I3C2_REG1, ACT_MODE,      2,  2)
46e7b1406cSJoe Komlodi     FIELD(I3C2_REG1, PENDING_INT,   4,  4)
47e7b1406cSJoe Komlodi     FIELD(I3C2_REG1, SA,            8,  7)
487d87775fSJoe Komlodi     FIELD(I3C2_REG1, SA_EN,         15, 1)
49e7b1406cSJoe Komlodi     FIELD(I3C2_REG1, INST_ID,       16, 4)
507d87775fSJoe Komlodi REG32(I3C3_REG0, 0x30)
517d87775fSJoe Komlodi REG32(I3C3_REG1, 0x34)
527d87775fSJoe Komlodi     FIELD(I3C3_REG1, I2C_MODE,      0,  1)
53e7b1406cSJoe Komlodi     FIELD(I3C3_REG1, SLV_TEST_MODE, 1,  1)
54e7b1406cSJoe Komlodi     FIELD(I3C3_REG1, ACT_MODE,      2,  2)
55e7b1406cSJoe Komlodi     FIELD(I3C3_REG1, PENDING_INT,   4,  4)
56e7b1406cSJoe Komlodi     FIELD(I3C3_REG1, SA,            8,  7)
577d87775fSJoe Komlodi     FIELD(I3C3_REG1, SA_EN,         15, 1)
58e7b1406cSJoe Komlodi     FIELD(I3C3_REG1, INST_ID,       16, 4)
597d87775fSJoe Komlodi REG32(I3C4_REG0, 0x40)
607d87775fSJoe Komlodi REG32(I3C4_REG1, 0x44)
617d87775fSJoe Komlodi     FIELD(I3C4_REG1, I2C_MODE,      0,  1)
62e7b1406cSJoe Komlodi     FIELD(I3C4_REG1, SLV_TEST_MODE, 1,  1)
63e7b1406cSJoe Komlodi     FIELD(I3C4_REG1, ACT_MODE,      2,  2)
64e7b1406cSJoe Komlodi     FIELD(I3C4_REG1, PENDING_INT,   4,  4)
65e7b1406cSJoe Komlodi     FIELD(I3C4_REG1, SA,            8,  7)
667d87775fSJoe Komlodi     FIELD(I3C4_REG1, SA_EN,         15, 1)
67e7b1406cSJoe Komlodi     FIELD(I3C4_REG1, INST_ID,       16, 4)
687d87775fSJoe Komlodi REG32(I3C5_REG0, 0x50)
697d87775fSJoe Komlodi REG32(I3C5_REG1, 0x54)
707d87775fSJoe Komlodi     FIELD(I3C5_REG1, I2C_MODE,      0,  1)
71e7b1406cSJoe Komlodi     FIELD(I3C5_REG1, SLV_TEST_MODE, 1,  1)
72e7b1406cSJoe Komlodi     FIELD(I3C5_REG1, ACT_MODE,      2,  2)
73e7b1406cSJoe Komlodi     FIELD(I3C5_REG1, PENDING_INT,   4,  4)
74e7b1406cSJoe Komlodi     FIELD(I3C5_REG1, SA,            8,  7)
757d87775fSJoe Komlodi     FIELD(I3C5_REG1, SA_EN,         15, 1)
76e7b1406cSJoe Komlodi     FIELD(I3C5_REG1, INST_ID,       16, 4)
777d87775fSJoe Komlodi REG32(I3C6_REG0, 0x60)
787d87775fSJoe Komlodi REG32(I3C6_REG1, 0x64)
797d87775fSJoe Komlodi     FIELD(I3C6_REG1, I2C_MODE,      0,  1)
80e7b1406cSJoe Komlodi     FIELD(I3C6_REG1, SLV_TEST_MODE, 1,  1)
81e7b1406cSJoe Komlodi     FIELD(I3C6_REG1, ACT_MODE,      2,  2)
82e7b1406cSJoe Komlodi     FIELD(I3C6_REG1, PENDING_INT,   4,  4)
83e7b1406cSJoe Komlodi     FIELD(I3C6_REG1, SA,            8,  7)
847d87775fSJoe Komlodi     FIELD(I3C6_REG1, SA_EN,         15, 1)
85e7b1406cSJoe Komlodi     FIELD(I3C6_REG1, INST_ID,       16, 4)
867d87775fSJoe Komlodi 
877d87775fSJoe Komlodi /* I3C Device Registers */
887d87775fSJoe Komlodi REG32(DEVICE_CTRL,                  0x00)
89e7b1406cSJoe Komlodi     FIELD(DEVICE_CTRL, I3C_BROADCAST_ADDR_INC,    0, 1)
90e7b1406cSJoe Komlodi     FIELD(DEVICE_CTRL, I2C_SLAVE_PRESENT,         7, 1)
91e7b1406cSJoe Komlodi     FIELD(DEVICE_CTRL, HOT_JOIN_ACK_NACK_CTRL,    8, 1)
92e7b1406cSJoe Komlodi     FIELD(DEVICE_CTRL, IDLE_CNT_MULTIPLIER,       24, 2)
93e7b1406cSJoe Komlodi     FIELD(DEVICE_CTRL, SLV_ADAPT_TO_I2C_I3C_MODE, 27, 1)
94e7b1406cSJoe Komlodi     FIELD(DEVICE_CTRL, DMA_HANDSHAKE_EN,          28, 1)
95e7b1406cSJoe Komlodi     FIELD(DEVICE_CTRL, I3C_ABORT,                 29, 1)
96e7b1406cSJoe Komlodi     FIELD(DEVICE_CTRL, I3C_RESUME,                30, 1)
97e7b1406cSJoe Komlodi     FIELD(DEVICE_CTRL, I3C_EN,                    31, 1)
987d87775fSJoe Komlodi REG32(DEVICE_ADDR,                  0x04)
99e7b1406cSJoe Komlodi     FIELD(DEVICE_ADDR, STATIC_ADDR,         0, 7)
100e7b1406cSJoe Komlodi     FIELD(DEVICE_ADDR, STATIC_ADDR_VALID,   15, 1)
101e7b1406cSJoe Komlodi     FIELD(DEVICE_ADDR, DYNAMIC_ADDR,        16, 7)
102e7b1406cSJoe Komlodi     FIELD(DEVICE_ADDR, DYNAMIC_ADDR_VALID,  15, 1)
1037d87775fSJoe Komlodi REG32(HW_CAPABILITY,                0x08)
104e7b1406cSJoe Komlodi     FIELD(HW_CAPABILITY, ENTDAA,  0, 1)
105e7b1406cSJoe Komlodi     FIELD(HW_CAPABILITY, HDR_DDR, 3, 1)
106e7b1406cSJoe Komlodi     FIELD(HW_CAPABILITY, HDR_TS,  4, 1)
1077d87775fSJoe Komlodi REG32(COMMAND_QUEUE_PORT,           0x0c)
108e7b1406cSJoe Komlodi     FIELD(COMMAND_QUEUE_PORT, CMD_ATTR, 0, 3)
109e7b1406cSJoe Komlodi     /* Transfer command structure */
110e7b1406cSJoe Komlodi     FIELD(COMMAND_QUEUE_PORT, TID, 3, 4)
111e7b1406cSJoe Komlodi     FIELD(COMMAND_QUEUE_PORT, CMD, 7, 8)
112e7b1406cSJoe Komlodi     FIELD(COMMAND_QUEUE_PORT, CP, 15, 1)
113e7b1406cSJoe Komlodi     FIELD(COMMAND_QUEUE_PORT, DEV_INDEX, 16, 5)
114e7b1406cSJoe Komlodi     FIELD(COMMAND_QUEUE_PORT, SPEED, 21, 3)
115e7b1406cSJoe Komlodi     FIELD(COMMAND_QUEUE_PORT, ROC, 26, 1)
116e7b1406cSJoe Komlodi     FIELD(COMMAND_QUEUE_PORT, SDAP, 27, 1)
117e7b1406cSJoe Komlodi     FIELD(COMMAND_QUEUE_PORT, RNW, 28, 1)
118e7b1406cSJoe Komlodi     FIELD(COMMAND_QUEUE_PORT, TOC, 30, 1)
119e7b1406cSJoe Komlodi     FIELD(COMMAND_QUEUE_PORT, PEC, 31, 1)
120e7b1406cSJoe Komlodi     /* Transfer argument data structure */
121e7b1406cSJoe Komlodi     FIELD(COMMAND_QUEUE_PORT, DB, 8, 8)
122e7b1406cSJoe Komlodi     FIELD(COMMAND_QUEUE_PORT, DL, 16, 16)
123e7b1406cSJoe Komlodi     /* Short data argument data structure */
124e7b1406cSJoe Komlodi     FIELD(COMMAND_QUEUE_PORT, BYTE_STRB, 3, 3)
125e7b1406cSJoe Komlodi     FIELD(COMMAND_QUEUE_PORT, BYTE0, 8, 8)
126e7b1406cSJoe Komlodi     FIELD(COMMAND_QUEUE_PORT, BYTE1, 16, 8)
127e7b1406cSJoe Komlodi     FIELD(COMMAND_QUEUE_PORT, BYTE2, 24, 8)
128e7b1406cSJoe Komlodi     /* Address assignment command structure */
129e7b1406cSJoe Komlodi     /*
130e7b1406cSJoe Komlodi      * bits 3..21 and 26..31 are the same as the transfer command structure, or
131e7b1406cSJoe Komlodi      * marked as reserved.
132e7b1406cSJoe Komlodi      */
133e7b1406cSJoe Komlodi     FIELD(COMMAND_QUEUE_PORT, DEV_COUNT, 21, 3)
1347d87775fSJoe Komlodi REG32(RESPONSE_QUEUE_PORT,          0x10)
135e7b1406cSJoe Komlodi     FIELD(RESPONSE_QUEUE_PORT, DL, 0, 16)
136e7b1406cSJoe Komlodi     FIELD(RESPONSE_QUEUE_PORT, CCCT, 16, 8)
137e7b1406cSJoe Komlodi     FIELD(RESPONSE_QUEUE_PORT, TID, 24, 4)
138e7b1406cSJoe Komlodi     FIELD(RESPONSE_QUEUE_PORT, ERR_STATUS, 28, 4)
1397d87775fSJoe Komlodi REG32(RX_TX_DATA_PORT,              0x14)
1407d87775fSJoe Komlodi REG32(IBI_QUEUE_STATUS,             0x18)
141e7b1406cSJoe Komlodi     FIELD(IBI_QUEUE_STATUS, IBI_DATA_LEN,   0, 8)
142e7b1406cSJoe Komlodi     FIELD(IBI_QUEUE_STATUS, IBI_ID,         8, 8)
143e7b1406cSJoe Komlodi     FIELD(IBI_QUEUE_STATUS, LAST_STATUS,  24, 1)
144e7b1406cSJoe Komlodi     FIELD(IBI_QUEUE_STATUS, ERROR,  30, 1)
145e7b1406cSJoe Komlodi     FIELD(IBI_QUEUE_STATUS, IBI_STATUS,  31, 1)
1467d87775fSJoe Komlodi REG32(IBI_QUEUE_DATA,               0x18)
1477d87775fSJoe Komlodi REG32(QUEUE_THLD_CTRL,              0x1c)
148e7b1406cSJoe Komlodi     FIELD(QUEUE_THLD_CTRL, CMD_BUF_EMPTY_THLD,  0, 8);
149e7b1406cSJoe Komlodi     FIELD(QUEUE_THLD_CTRL, RESP_BUF_THLD, 8, 8);
150e7b1406cSJoe Komlodi     FIELD(QUEUE_THLD_CTRL, IBI_DATA_THLD, 16, 8);
151e7b1406cSJoe Komlodi     FIELD(QUEUE_THLD_CTRL, IBI_STATUS_THLD,     24, 8);
1527d87775fSJoe Komlodi REG32(DATA_BUFFER_THLD_CTRL,        0x20)
153e7b1406cSJoe Komlodi     FIELD(DATA_BUFFER_THLD_CTRL, TX_BUF_THLD,   0, 3)
154e7b1406cSJoe Komlodi     FIELD(DATA_BUFFER_THLD_CTRL, RX_BUF_THLD,   10, 3)
155e7b1406cSJoe Komlodi     FIELD(DATA_BUFFER_THLD_CTRL, TX_START_THLD, 16, 3)
156e7b1406cSJoe Komlodi     FIELD(DATA_BUFFER_THLD_CTRL, RX_START_THLD, 24, 3)
1577d87775fSJoe Komlodi REG32(IBI_QUEUE_CTRL,               0x24)
158e7b1406cSJoe Komlodi     FIELD(IBI_QUEUE_CTRL, NOTIFY_REJECTED_HOT_JOIN,   0, 1)
159e7b1406cSJoe Komlodi     FIELD(IBI_QUEUE_CTRL, NOTIFY_REJECTED_MASTER_REQ, 1, 1)
160e7b1406cSJoe Komlodi     FIELD(IBI_QUEUE_CTRL, NOTIFY_REJECTED_SLAVE_IRQ,  3, 1)
1617d87775fSJoe Komlodi REG32(IBI_MR_REQ_REJECT,            0x2c)
1627d87775fSJoe Komlodi REG32(IBI_SIR_REQ_REJECT,           0x30)
1637d87775fSJoe Komlodi REG32(RESET_CTRL,                   0x34)
164e7b1406cSJoe Komlodi     FIELD(RESET_CTRL, CORE_RESET,       0, 1)
165e7b1406cSJoe Komlodi     FIELD(RESET_CTRL, CMD_QUEUE_RESET,  1, 1)
166e7b1406cSJoe Komlodi     FIELD(RESET_CTRL, RESP_QUEUE_RESET, 2, 1)
167e7b1406cSJoe Komlodi     FIELD(RESET_CTRL, TX_BUF_RESET,     3, 1)
168e7b1406cSJoe Komlodi     FIELD(RESET_CTRL, RX_BUF_RESET,     4, 1)
169e7b1406cSJoe Komlodi     FIELD(RESET_CTRL, IBI_QUEUE_RESET,  5, 1)
1707d87775fSJoe Komlodi REG32(SLV_EVENT_CTRL,               0x38)
171e7b1406cSJoe Komlodi     FIELD(SLV_EVENT_CTRL, SLV_INTERRUPT,      0, 1)
172e7b1406cSJoe Komlodi     FIELD(SLV_EVENT_CTRL, MASTER_INTERRUPT,   1, 1)
173e7b1406cSJoe Komlodi     FIELD(SLV_EVENT_CTRL, HOT_JOIN_INTERRUPT, 3, 1)
174e7b1406cSJoe Komlodi     FIELD(SLV_EVENT_CTRL, ACTIVITY_STATE,     4, 2)
175e7b1406cSJoe Komlodi     FIELD(SLV_EVENT_CTRL, MRL_UPDATED,        6, 1)
176e7b1406cSJoe Komlodi     FIELD(SLV_EVENT_CTRL, MWL_UPDATED,        7, 1)
1777d87775fSJoe Komlodi REG32(INTR_STATUS,                  0x3c)
178e7b1406cSJoe Komlodi     FIELD(INTR_STATUS, TX_THLD,           0, 1)
179e7b1406cSJoe Komlodi     FIELD(INTR_STATUS, RX_THLD,           1, 1)
180e7b1406cSJoe Komlodi     FIELD(INTR_STATUS, IBI_THLD,          2, 1)
181e7b1406cSJoe Komlodi     FIELD(INTR_STATUS, CMD_QUEUE_RDY,     3, 1)
182e7b1406cSJoe Komlodi     FIELD(INTR_STATUS, RESP_RDY,          4, 1)
183e7b1406cSJoe Komlodi     FIELD(INTR_STATUS, TRANSFER_ABORT,    5, 1)
184e7b1406cSJoe Komlodi     FIELD(INTR_STATUS, CCC_UPDATED,       6, 1)
185e7b1406cSJoe Komlodi     FIELD(INTR_STATUS, DYN_ADDR_ASSGN,    8, 1)
186e7b1406cSJoe Komlodi     FIELD(INTR_STATUS, TRANSFER_ERR,      9, 1)
187e7b1406cSJoe Komlodi     FIELD(INTR_STATUS, DEFSLV,            10, 1)
188e7b1406cSJoe Komlodi     FIELD(INTR_STATUS, READ_REQ_RECV,     11, 1)
189e7b1406cSJoe Komlodi     FIELD(INTR_STATUS, IBI_UPDATED,       12, 1)
190e7b1406cSJoe Komlodi     FIELD(INTR_STATUS, BUSOWNER_UPDATED,  13, 1)
1917d87775fSJoe Komlodi REG32(INTR_STATUS_EN,               0x40)
192e7b1406cSJoe Komlodi     FIELD(INTR_STATUS_EN, TX_THLD,          0, 1)
193e7b1406cSJoe Komlodi     FIELD(INTR_STATUS_EN, RX_THLD,          1, 1)
194e7b1406cSJoe Komlodi     FIELD(INTR_STATUS_EN, IBI_THLD,         2, 1)
195e7b1406cSJoe Komlodi     FIELD(INTR_STATUS_EN, CMD_QUEUE_RDY,    3, 1)
196e7b1406cSJoe Komlodi     FIELD(INTR_STATUS_EN, RESP_RDY,         4, 1)
197e7b1406cSJoe Komlodi     FIELD(INTR_STATUS_EN, TRANSFER_ABORT,   5, 1)
198e7b1406cSJoe Komlodi     FIELD(INTR_STATUS_EN, CCC_UPDATED,      6, 1)
199e7b1406cSJoe Komlodi     FIELD(INTR_STATUS_EN, DYN_ADDR_ASSGN,   8, 1)
200e7b1406cSJoe Komlodi     FIELD(INTR_STATUS_EN, TRANSFER_ERR,     9, 1)
201e7b1406cSJoe Komlodi     FIELD(INTR_STATUS_EN, DEFSLV,           10, 1)
202e7b1406cSJoe Komlodi     FIELD(INTR_STATUS_EN, READ_REQ_RECV,    11, 1)
203e7b1406cSJoe Komlodi     FIELD(INTR_STATUS_EN, IBI_UPDATED,      12, 1)
204e7b1406cSJoe Komlodi     FIELD(INTR_STATUS_EN, BUSOWNER_UPDATED, 13, 1)
2057d87775fSJoe Komlodi REG32(INTR_SIGNAL_EN,               0x44)
206e7b1406cSJoe Komlodi     FIELD(INTR_SIGNAL_EN, TX_THLD,          0, 1)
207e7b1406cSJoe Komlodi     FIELD(INTR_SIGNAL_EN, RX_THLD,          1, 1)
208e7b1406cSJoe Komlodi     FIELD(INTR_SIGNAL_EN, IBI_THLD,         2, 1)
209e7b1406cSJoe Komlodi     FIELD(INTR_SIGNAL_EN, CMD_QUEUE_RDY,    3, 1)
210e7b1406cSJoe Komlodi     FIELD(INTR_SIGNAL_EN, RESP_RDY,         4, 1)
211e7b1406cSJoe Komlodi     FIELD(INTR_SIGNAL_EN, TRANSFER_ABORT,   5, 1)
212e7b1406cSJoe Komlodi     FIELD(INTR_SIGNAL_EN, CCC_UPDATED,      6, 1)
213e7b1406cSJoe Komlodi     FIELD(INTR_SIGNAL_EN, DYN_ADDR_ASSGN,   8, 1)
214e7b1406cSJoe Komlodi     FIELD(INTR_SIGNAL_EN, TRANSFER_ERR,     9, 1)
215e7b1406cSJoe Komlodi     FIELD(INTR_SIGNAL_EN, DEFSLV,           10, 1)
216e7b1406cSJoe Komlodi     FIELD(INTR_SIGNAL_EN, READ_REQ_RECV,    11, 1)
217e7b1406cSJoe Komlodi     FIELD(INTR_SIGNAL_EN, IBI_UPDATED,      12, 1)
218e7b1406cSJoe Komlodi     FIELD(INTR_SIGNAL_EN, BUSOWNER_UPDATED, 13, 1)
2197d87775fSJoe Komlodi REG32(INTR_FORCE,                   0x48)
220e7b1406cSJoe Komlodi     FIELD(INTR_FORCE, TX_THLD,          0, 1)
221e7b1406cSJoe Komlodi     FIELD(INTR_FORCE, RX_THLD,          1, 1)
222e7b1406cSJoe Komlodi     FIELD(INTR_FORCE, IBI_THLD,         2, 1)
223e7b1406cSJoe Komlodi     FIELD(INTR_FORCE, CMD_QUEUE_RDY,    3, 1)
224e7b1406cSJoe Komlodi     FIELD(INTR_FORCE, RESP_RDY,         4, 1)
225e7b1406cSJoe Komlodi     FIELD(INTR_FORCE, TRANSFER_ABORT,   5, 1)
226e7b1406cSJoe Komlodi     FIELD(INTR_FORCE, CCC_UPDATED,      6, 1)
227e7b1406cSJoe Komlodi     FIELD(INTR_FORCE, DYN_ADDR_ASSGN,   8, 1)
228e7b1406cSJoe Komlodi     FIELD(INTR_FORCE, TRANSFER_ERR,     9, 1)
229e7b1406cSJoe Komlodi     FIELD(INTR_FORCE, DEFSLV,           10, 1)
230e7b1406cSJoe Komlodi     FIELD(INTR_FORCE, READ_REQ_RECV,    11, 1)
231e7b1406cSJoe Komlodi     FIELD(INTR_FORCE, IBI_UPDATED,      12, 1)
232e7b1406cSJoe Komlodi     FIELD(INTR_FORCE, BUSOWNER_UPDATED, 13, 1)
2337d87775fSJoe Komlodi REG32(QUEUE_STATUS_LEVEL,           0x4c)
234e7b1406cSJoe Komlodi     FIELD(QUEUE_STATUS_LEVEL, CMD_QUEUE_EMPTY_LOC,  0, 8)
235e7b1406cSJoe Komlodi     FIELD(QUEUE_STATUS_LEVEL, RESP_BUF_BLR,         8, 8)
236e7b1406cSJoe Komlodi     FIELD(QUEUE_STATUS_LEVEL, IBI_BUF_BLR,          16, 8)
237e7b1406cSJoe Komlodi     FIELD(QUEUE_STATUS_LEVEL, IBI_STATUS_CNT,       24, 5)
2387d87775fSJoe Komlodi REG32(DATA_BUFFER_STATUS_LEVEL,     0x50)
239e7b1406cSJoe Komlodi     FIELD(DATA_BUFFER_STATUS_LEVEL, TX_BUF_EMPTY_LOC, 0, 8)
240e7b1406cSJoe Komlodi     FIELD(DATA_BUFFER_STATUS_LEVEL, RX_BUF_BLR,       16, 8)
2417d87775fSJoe Komlodi REG32(PRESENT_STATE,                0x54)
242e7b1406cSJoe Komlodi     FIELD(PRESENT_STATE, SCL_LINE_SIGNAL_LEVEL, 0, 1)
243e7b1406cSJoe Komlodi     FIELD(PRESENT_STATE, SDA_LINE_SIGNAL_LEVEL, 1, 1)
244e7b1406cSJoe Komlodi     FIELD(PRESENT_STATE, CURRENT_MASTER,        2, 1)
245e7b1406cSJoe Komlodi     FIELD(PRESENT_STATE, CM_TFR_STATUS,         8, 6)
246e7b1406cSJoe Komlodi     FIELD(PRESENT_STATE, CM_TFR_ST_STATUS,      16, 6)
247e7b1406cSJoe Komlodi     FIELD(PRESENT_STATE, CMD_TID,               24, 4)
2487d87775fSJoe Komlodi REG32(CCC_DEVICE_STATUS,            0x58)
249e7b1406cSJoe Komlodi     FIELD(CCC_DEVICE_STATUS, PENDING_INTR,      0, 4)
250e7b1406cSJoe Komlodi     FIELD(CCC_DEVICE_STATUS, PROTOCOL_ERR,      4, 2)
251e7b1406cSJoe Komlodi     FIELD(CCC_DEVICE_STATUS, ACTIVITY_MODE,     6, 2)
252e7b1406cSJoe Komlodi     FIELD(CCC_DEVICE_STATUS, UNDER_ERR,         8, 1)
253e7b1406cSJoe Komlodi     FIELD(CCC_DEVICE_STATUS, SLV_BUSY,          9, 1)
254e7b1406cSJoe Komlodi     FIELD(CCC_DEVICE_STATUS, OVERFLOW_ERR,      10, 1)
255e7b1406cSJoe Komlodi     FIELD(CCC_DEVICE_STATUS, DATA_NOT_READY,    11, 1)
256e7b1406cSJoe Komlodi     FIELD(CCC_DEVICE_STATUS, BUFFER_NOT_AVAIL,  12, 1)
2577d87775fSJoe Komlodi REG32(DEVICE_ADDR_TABLE_POINTER,    0x5c)
2587d87775fSJoe Komlodi     FIELD(DEVICE_ADDR_TABLE_POINTER, DEPTH, 16, 16)
2597d87775fSJoe Komlodi     FIELD(DEVICE_ADDR_TABLE_POINTER, ADDR,  0,  16)
2607d87775fSJoe Komlodi REG32(DEV_CHAR_TABLE_POINTER,       0x60)
261e7b1406cSJoe Komlodi     FIELD(DEV_CHAR_TABLE_POINTER, P_DEV_CHAR_TABLE_START_ADDR,  0, 12)
262e7b1406cSJoe Komlodi     FIELD(DEV_CHAR_TABLE_POINTER, DEV_CHAR_TABLE_DEPTH,         12, 7)
263e7b1406cSJoe Komlodi     FIELD(DEV_CHAR_TABLE_POINTER, PRESENT_DEV_CHAR_TABLE_INDEX, 19, 3)
2647d87775fSJoe Komlodi REG32(VENDOR_SPECIFIC_REG_POINTER,  0x6c)
265e7b1406cSJoe Komlodi     FIELD(VENDOR_SPECIFIC_REG_POINTER, P_VENDOR_REG_START_ADDR, 0, 16)
2667d87775fSJoe Komlodi REG32(SLV_MIPI_PID_VALUE,           0x70)
2677d87775fSJoe Komlodi REG32(SLV_PID_VALUE,                0x74)
268e7b1406cSJoe Komlodi     FIELD(SLV_PID_VALUE, SLV_PID_DCR, 0, 12)
269e7b1406cSJoe Komlodi     FIELD(SLV_PID_VALUE, SLV_INST_ID, 12, 4)
270e7b1406cSJoe Komlodi     FIELD(SLV_PID_VALUE, SLV_PART_ID, 16, 16)
2717d87775fSJoe Komlodi REG32(SLV_CHAR_CTRL,                0x78)
272e7b1406cSJoe Komlodi     FIELD(SLV_CHAR_CTRL, BCR,     0, 8)
273e7b1406cSJoe Komlodi     FIELD(SLV_CHAR_CTRL, DCR,     8, 8)
274e7b1406cSJoe Komlodi     FIELD(SLV_CHAR_CTRL, HDR_CAP, 16, 8)
2757d87775fSJoe Komlodi REG32(SLV_MAX_LEN,                  0x7c)
276e7b1406cSJoe Komlodi     FIELD(SLV_MAX_LEN, MWL, 0, 16)
277e7b1406cSJoe Komlodi     FIELD(SLV_MAX_LEN, MRL, 16, 16)
2787d87775fSJoe Komlodi REG32(MAX_READ_TURNAROUND,          0x80)
2797d87775fSJoe Komlodi REG32(MAX_DATA_SPEED,               0x84)
2807d87775fSJoe Komlodi REG32(SLV_DEBUG_STATUS,             0x88)
2817d87775fSJoe Komlodi REG32(SLV_INTR_REQ,                 0x8c)
282e7b1406cSJoe Komlodi     FIELD(SLV_INTR_REQ, SIR,      0, 1)
283e7b1406cSJoe Komlodi     FIELD(SLV_INTR_REQ, SIR_CTRL, 1, 2)
284e7b1406cSJoe Komlodi     FIELD(SLV_INTR_REQ, MIR,      3, 1)
285e7b1406cSJoe Komlodi     FIELD(SLV_INTR_REQ, IBI_STS,  8, 2)
286e7b1406cSJoe Komlodi REG32(SLV_TSX_SYMBL_TIMING,         0x90)
287e7b1406cSJoe Komlodi     FIELD(SLV_TSX_SYMBL_TIMING, SLV_TSX_SYMBL_CNT, 0, 6)
2887d87775fSJoe Komlodi REG32(DEVICE_CTRL_EXTENDED,         0xb0)
289e7b1406cSJoe Komlodi     FIELD(DEVICE_CTRL_EXTENDED, MODE, 0, 2)
290e7b1406cSJoe Komlodi     FIELD(DEVICE_CTRL_EXTENDED, REQMST_ACK_CTRL, 3, 1)
2917d87775fSJoe Komlodi REG32(SCL_I3C_OD_TIMING,            0xb4)
292e7b1406cSJoe Komlodi     FIELD(SCL_I3C_OD_TIMING, I3C_OD_LCNT, 0, 8)
293e7b1406cSJoe Komlodi     FIELD(SCL_I3C_OD_TIMING, I3C_OD_HCNT, 16, 8)
2947d87775fSJoe Komlodi REG32(SCL_I3C_PP_TIMING,            0xb8)
295e7b1406cSJoe Komlodi     FIELD(SCL_I3C_PP_TIMING, I3C_PP_LCNT, 0, 8)
296e7b1406cSJoe Komlodi     FIELD(SCL_I3C_PP_TIMING, I3C_PP_HCNT, 16, 8)
2977d87775fSJoe Komlodi REG32(SCL_I2C_FM_TIMING,            0xbc)
2987d87775fSJoe Komlodi REG32(SCL_I2C_FMP_TIMING,           0xc0)
299e7b1406cSJoe Komlodi     FIELD(SCL_I2C_FMP_TIMING, I2C_FMP_LCNT, 0, 16)
300e7b1406cSJoe Komlodi     FIELD(SCL_I2C_FMP_TIMING, I2C_FMP_HCNT, 16, 8)
3017d87775fSJoe Komlodi REG32(SCL_EXT_LCNT_TIMING,          0xc8)
3027d87775fSJoe Komlodi REG32(SCL_EXT_TERMN_LCNT_TIMING,    0xcc)
3037d87775fSJoe Komlodi REG32(BUS_FREE_TIMING,              0xd4)
3047d87775fSJoe Komlodi REG32(BUS_IDLE_TIMING,              0xd8)
305e7b1406cSJoe Komlodi     FIELD(BUS_IDLE_TIMING, BUS_IDLE_TIME, 0, 20)
3067d87775fSJoe Komlodi REG32(I3C_VER_ID,                   0xe0)
3077d87775fSJoe Komlodi REG32(I3C_VER_TYPE,                 0xe4)
3087d87775fSJoe Komlodi REG32(EXTENDED_CAPABILITY,          0xe8)
309e7b1406cSJoe Komlodi     FIELD(EXTENDED_CAPABILITY, APP_IF_MODE,       0, 2)
310e7b1406cSJoe Komlodi     FIELD(EXTENDED_CAPABILITY, APP_IF_DATA_WIDTH, 2, 2)
311e7b1406cSJoe Komlodi     FIELD(EXTENDED_CAPABILITY, OPERATION_MODE,    4, 2)
312e7b1406cSJoe Komlodi     FIELD(EXTENDED_CAPABILITY, CLK_PERIOD,        8, 6)
3137d87775fSJoe Komlodi REG32(SLAVE_CONFIG,                 0xec)
314e7b1406cSJoe Komlodi     FIELD(SLAVE_CONFIG, DMA_EN,     0, 1)
315e7b1406cSJoe Komlodi     FIELD(SLAVE_CONFIG, HJ_CAP,     0, 1)
316e7b1406cSJoe Komlodi     FIELD(SLAVE_CONFIG, CLK_PERIOD, 2, 14)
317e7b1406cSJoe Komlodi /* Device characteristic table fields */
318e7b1406cSJoe Komlodi REG32(DEVICE_CHARACTERISTIC_TABLE_LOC1, 0x200)
319e7b1406cSJoe Komlodi REG32(DEVICE_CHARACTERISTIC_TABLE_LOC_SECONDARY, 0x200)
320e7b1406cSJoe Komlodi     FIELD(DEVICE_CHARACTERISTIC_TABLE_LOC_SECONDARY, DYNAMIC_ADDR, 0, 8)
321e7b1406cSJoe Komlodi     FIELD(DEVICE_CHARACTERISTIC_TABLE_LOC_SECONDARY, DCR, 8, 8)
322e7b1406cSJoe Komlodi     FIELD(DEVICE_CHARACTERISTIC_TABLE_LOC_SECONDARY, BCR, 16, 8)
323e7b1406cSJoe Komlodi     FIELD(DEVICE_CHARACTERISTIC_TABLE_LOC_SECONDARY, STATIC_ADDR, 24, 8)
324e7b1406cSJoe Komlodi REG32(DEVICE_CHARACTERISTIC_TABLE_LOC2, 0x204)
325e7b1406cSJoe Komlodi     FIELD(DEVICE_CHARACTERISTIC_TABLE_LOC2, MSB_PID, 0, 16)
326e7b1406cSJoe Komlodi REG32(DEVICE_CHARACTERISTIC_TABLE_LOC3, 0x208)
327e7b1406cSJoe Komlodi     FIELD(DEVICE_CHARACTERISTIC_TABLE_LOC3, DCR, 0, 8)
328e7b1406cSJoe Komlodi     FIELD(DEVICE_CHARACTERISTIC_TABLE_LOC3, BCR, 8, 8)
329e7b1406cSJoe Komlodi REG32(DEVICE_CHARACTERISTIC_TABLE_LOC4, 0x20c)
330e7b1406cSJoe Komlodi     FIELD(DEVICE_CHARACTERISTIC_TABLE_LOC4, DEV_DYNAMIC_ADDR, 0, 8)
331e7b1406cSJoe Komlodi /* Dev addr table fields */
332e7b1406cSJoe Komlodi REG32(DEVICE_ADDR_TABLE_LOC1, 0x280)
333e7b1406cSJoe Komlodi     FIELD(DEVICE_ADDR_TABLE_LOC1, DEV_STATIC_ADDR, 0, 7)
334e7b1406cSJoe Komlodi     FIELD(DEVICE_ADDR_TABLE_LOC1, IBI_PEC_EN, 11, 1)
335e7b1406cSJoe Komlodi     FIELD(DEVICE_ADDR_TABLE_LOC1, IBI_WITH_DATA, 12, 1)
336e7b1406cSJoe Komlodi     FIELD(DEVICE_ADDR_TABLE_LOC1, SIR_REJECT, 13, 1)
337e7b1406cSJoe Komlodi     FIELD(DEVICE_ADDR_TABLE_LOC1, MR_REJECT, 14, 1)
338e7b1406cSJoe Komlodi     FIELD(DEVICE_ADDR_TABLE_LOC1, DEV_DYNAMIC_ADDR, 16, 8)
339e7b1406cSJoe Komlodi     FIELD(DEVICE_ADDR_TABLE_LOC1, IBI_ADDR_MASK, 24, 2)
340e7b1406cSJoe Komlodi     FIELD(DEVICE_ADDR_TABLE_LOC1, DEV_NACK_RETRY_CNT, 29, 2)
341e7b1406cSJoe Komlodi     FIELD(DEVICE_ADDR_TABLE_LOC1, LEGACY_I2C_DEVICE, 31, 1)
3427d87775fSJoe Komlodi 
343*eee51e8fSJoe Komlodi static void aspeed_i3c_device_cmd_queue_execute(AspeedI3CDevice *s);
344*eee51e8fSJoe Komlodi 
345a9d3f922SJoe Komlodi static const uint32_t ast2600_i3c_controller_ro[ASPEED_I3C_DEVICE_NR_REGS] = {
346a9d3f922SJoe Komlodi     [R_I3C1_REG0]                   = 0xfc000000,
347a9d3f922SJoe Komlodi     [R_I3C1_REG1]                   = 0xfff00000,
348a9d3f922SJoe Komlodi     [R_I3C2_REG0]                   = 0xfc000000,
349a9d3f922SJoe Komlodi     [R_I3C2_REG1]                   = 0xfff00000,
350a9d3f922SJoe Komlodi     [R_I3C3_REG0]                   = 0xfc000000,
351a9d3f922SJoe Komlodi     [R_I3C3_REG1]                   = 0xfff00000,
352a9d3f922SJoe Komlodi     [R_I3C4_REG0]                   = 0xfc000000,
353a9d3f922SJoe Komlodi     [R_I3C4_REG1]                   = 0xfff00000,
354a9d3f922SJoe Komlodi     [R_I3C5_REG0]                   = 0xfc000000,
355a9d3f922SJoe Komlodi     [R_I3C5_REG1]                   = 0xfff00000,
356a9d3f922SJoe Komlodi     [R_I3C6_REG0]                   = 0xfc000000,
357a9d3f922SJoe Komlodi     [R_I3C6_REG1]                   = 0xfff00000,
358a9d3f922SJoe Komlodi };
359a9d3f922SJoe Komlodi 
3607d87775fSJoe Komlodi static const uint32_t ast2600_i3c_device_resets[ASPEED_I3C_DEVICE_NR_REGS] = {
3617d87775fSJoe Komlodi     [R_HW_CAPABILITY]               = 0x000e00bf,
3627d87775fSJoe Komlodi     [R_QUEUE_THLD_CTRL]             = 0x01000101,
3631032d6d6SJoe Komlodi     [R_DATA_BUFFER_THLD_CTRL]       = 0x01010100,
3641032d6d6SJoe Komlodi     [R_SLV_EVENT_CTRL]              = 0x0000000b,
3651032d6d6SJoe Komlodi     [R_QUEUE_STATUS_LEVEL]          = 0x00000002,
3661032d6d6SJoe Komlodi     [R_DATA_BUFFER_STATUS_LEVEL]    = 0x00000010,
3671032d6d6SJoe Komlodi     [R_PRESENT_STATE]               = 0x00000003,
3687d87775fSJoe Komlodi     [R_I3C_VER_ID]                  = 0x3130302a,
3697d87775fSJoe Komlodi     [R_I3C_VER_TYPE]                = 0x6c633033,
3707d87775fSJoe Komlodi     [R_DEVICE_ADDR_TABLE_POINTER]   = 0x00080280,
3717d87775fSJoe Komlodi     [R_DEV_CHAR_TABLE_POINTER]      = 0x00020200,
3721032d6d6SJoe Komlodi     [R_SLV_CHAR_CTRL]               = 0x00010000,
3737d87775fSJoe Komlodi     [A_VENDOR_SPECIFIC_REG_POINTER] = 0x000000b0,
3747d87775fSJoe Komlodi     [R_SLV_MAX_LEN]                 = 0x00ff00ff,
3751032d6d6SJoe Komlodi     [R_SLV_TSX_SYMBL_TIMING]        = 0x0000003f,
3761032d6d6SJoe Komlodi     [R_SCL_I3C_OD_TIMING]           = 0x000a0010,
3771032d6d6SJoe Komlodi     [R_SCL_I3C_PP_TIMING]           = 0x000a000a,
3781032d6d6SJoe Komlodi     [R_SCL_I2C_FM_TIMING]           = 0x00100010,
3791032d6d6SJoe Komlodi     [R_SCL_I2C_FMP_TIMING]          = 0x00100010,
3801032d6d6SJoe Komlodi     [R_SCL_EXT_LCNT_TIMING]         = 0x20202020,
3811032d6d6SJoe Komlodi     [R_SCL_EXT_TERMN_LCNT_TIMING]   = 0x00300000,
3821032d6d6SJoe Komlodi     [R_BUS_FREE_TIMING]             = 0x00200020,
3831032d6d6SJoe Komlodi     [R_BUS_IDLE_TIMING]             = 0x00000020,
3841032d6d6SJoe Komlodi     [R_EXTENDED_CAPABILITY]         = 0x00000239,
3851032d6d6SJoe Komlodi     [R_SLAVE_CONFIG]                = 0x00000023,
3867d87775fSJoe Komlodi };
3877d87775fSJoe Komlodi 
388a9d3f922SJoe Komlodi static const uint32_t ast2600_i3c_device_ro[ASPEED_I3C_DEVICE_NR_REGS] = {
389a9d3f922SJoe Komlodi     [R_DEVICE_CTRL]                 = 0x04fffe00,
390a9d3f922SJoe Komlodi     [R_DEVICE_ADDR]                 = 0x7f807f80,
391a9d3f922SJoe Komlodi     [R_HW_CAPABILITY]               = 0xffffffff,
392a9d3f922SJoe Komlodi     [R_IBI_QUEUE_STATUS]            = 0xffffffff,
393a9d3f922SJoe Komlodi     [R_DATA_BUFFER_THLD_CTRL]       = 0xf8f8f8f8,
394a9d3f922SJoe Komlodi     [R_IBI_QUEUE_CTRL]              = 0xfffffff0,
395a9d3f922SJoe Komlodi     [R_RESET_CTRL]                  = 0xffffffc0,
396a9d3f922SJoe Komlodi     [R_SLV_EVENT_CTRL]              = 0xffffff3f,
397a9d3f922SJoe Komlodi     [R_INTR_STATUS]                 = 0xffff809f,
398a9d3f922SJoe Komlodi     [R_INTR_STATUS_EN]              = 0xffff8080,
399a9d3f922SJoe Komlodi     [R_INTR_SIGNAL_EN]              = 0xffff8080,
400a9d3f922SJoe Komlodi     [R_INTR_FORCE]                  = 0xffff8000,
401a9d3f922SJoe Komlodi     [R_QUEUE_STATUS_LEVEL]          = 0xffffffff,
402a9d3f922SJoe Komlodi     [R_DATA_BUFFER_STATUS_LEVEL]    = 0xffffffff,
403a9d3f922SJoe Komlodi     [R_PRESENT_STATE]               = 0xffffffff,
404a9d3f922SJoe Komlodi     [R_CCC_DEVICE_STATUS]           = 0xffffffff,
405a9d3f922SJoe Komlodi     [R_I3C_VER_ID]                  = 0xffffffff,
406a9d3f922SJoe Komlodi     [R_I3C_VER_TYPE]                = 0xffffffff,
407a9d3f922SJoe Komlodi     [R_DEVICE_ADDR_TABLE_POINTER]   = 0xffffffff,
408a9d3f922SJoe Komlodi     [R_DEV_CHAR_TABLE_POINTER]      = 0xffcbffff,
409a9d3f922SJoe Komlodi     [R_SLV_PID_VALUE]               = 0xffff0fff,
410a9d3f922SJoe Komlodi     [R_SLV_CHAR_CTRL]               = 0xffffffff,
411a9d3f922SJoe Komlodi     [A_VENDOR_SPECIFIC_REG_POINTER] = 0xffffffff,
412a9d3f922SJoe Komlodi     [R_SLV_MAX_LEN]                 = 0xffffffff,
413a9d3f922SJoe Komlodi     [R_MAX_READ_TURNAROUND]         = 0xffffffff,
414a9d3f922SJoe Komlodi     [R_MAX_DATA_SPEED]              = 0xffffffff,
415a9d3f922SJoe Komlodi     [R_SLV_INTR_REQ]                = 0xfffffff0,
416a9d3f922SJoe Komlodi     [R_SLV_TSX_SYMBL_TIMING]        = 0xffffffc0,
417a9d3f922SJoe Komlodi     [R_DEVICE_CTRL_EXTENDED]        = 0xfffffff8,
418a9d3f922SJoe Komlodi     [R_SCL_I3C_OD_TIMING]           = 0xff00ff00,
419a9d3f922SJoe Komlodi     [R_SCL_I3C_PP_TIMING]           = 0xff00ff00,
420a9d3f922SJoe Komlodi     [R_SCL_I2C_FMP_TIMING]          = 0xff000000,
421a9d3f922SJoe Komlodi     [R_SCL_EXT_TERMN_LCNT_TIMING]   = 0x0000fff0,
422a9d3f922SJoe Komlodi     [R_BUS_IDLE_TIMING]             = 0xfff00000,
423a9d3f922SJoe Komlodi     [R_EXTENDED_CAPABILITY]         = 0xffffffff,
424a9d3f922SJoe Komlodi     [R_SLAVE_CONFIG]                = 0xffffffff,
425a9d3f922SJoe Komlodi };
426a9d3f922SJoe Komlodi 
4274ba25376SJoe Komlodi static inline bool aspeed_i3c_device_has_entdaa(AspeedI3CDevice *s)
4284ba25376SJoe Komlodi {
4294ba25376SJoe Komlodi     return ARRAY_FIELD_EX32(s->regs, HW_CAPABILITY, ENTDAA);
4304ba25376SJoe Komlodi }
4314ba25376SJoe Komlodi 
4324ba25376SJoe Komlodi static inline bool aspeed_i3c_device_has_hdr_ts(AspeedI3CDevice *s)
4334ba25376SJoe Komlodi {
4344ba25376SJoe Komlodi     return ARRAY_FIELD_EX32(s->regs, HW_CAPABILITY, HDR_TS);
4354ba25376SJoe Komlodi }
4364ba25376SJoe Komlodi 
4374ba25376SJoe Komlodi static inline bool aspeed_i3c_device_has_hdr_ddr(AspeedI3CDevice *s)
4384ba25376SJoe Komlodi {
4394ba25376SJoe Komlodi     return ARRAY_FIELD_EX32(s->regs, HW_CAPABILITY, HDR_DDR);
4404ba25376SJoe Komlodi }
4414ba25376SJoe Komlodi 
4424ba25376SJoe Komlodi static inline bool aspeed_i3c_device_can_transmit(AspeedI3CDevice *s)
4434ba25376SJoe Komlodi {
4444ba25376SJoe Komlodi     /*
4454ba25376SJoe Komlodi      * We can only transmit if we're enabled and the resume bit is cleared.
4464ba25376SJoe Komlodi      * The resume bit is set on a transaction error, and software must clear it.
4474ba25376SJoe Komlodi      */
4484ba25376SJoe Komlodi     return ARRAY_FIELD_EX32(s->regs, DEVICE_CTRL, I3C_EN) &&
4494ba25376SJoe Komlodi            !ARRAY_FIELD_EX32(s->regs, DEVICE_CTRL, I3C_RESUME);
4504ba25376SJoe Komlodi }
4514ba25376SJoe Komlodi 
4524ba25376SJoe Komlodi static inline uint8_t aspeed_i3c_device_fifo_threshold_from_reg(uint8_t regval)
4534ba25376SJoe Komlodi {
4544ba25376SJoe Komlodi     return regval = regval ? (2 << regval) : 1;
4554ba25376SJoe Komlodi }
4564ba25376SJoe Komlodi 
45793ec6949SJoe Komlodi static inline uint8_t aspeed_i3c_device_ibi_slice_size(AspeedI3CDevice *s)
45893ec6949SJoe Komlodi {
45993ec6949SJoe Komlodi     uint8_t ibi_slice_size = ARRAY_FIELD_EX32(s->regs, QUEUE_THLD_CTRL,
46093ec6949SJoe Komlodi                                               IBI_DATA_THLD);
46193ec6949SJoe Komlodi     /* The minimum supported slice size is 4 bytes. */
46293ec6949SJoe Komlodi     if (ibi_slice_size == 0) {
46393ec6949SJoe Komlodi         ibi_slice_size = 1;
46493ec6949SJoe Komlodi     }
46593ec6949SJoe Komlodi     ibi_slice_size *= sizeof(uint32_t);
46693ec6949SJoe Komlodi     /* maximum supported size is 63 bytes. */
46793ec6949SJoe Komlodi     if (ibi_slice_size >= 64) {
46893ec6949SJoe Komlodi         ibi_slice_size = 63;
46993ec6949SJoe Komlodi     }
47093ec6949SJoe Komlodi 
47193ec6949SJoe Komlodi     return ibi_slice_size;
47293ec6949SJoe Komlodi }
47393ec6949SJoe Komlodi 
4743816bedaSJoe Komlodi static void aspeed_i3c_device_update_irq(AspeedI3CDevice *s)
4753816bedaSJoe Komlodi {
4763816bedaSJoe Komlodi     bool level = !!(s->regs[R_INTR_SIGNAL_EN] & s->regs[R_INTR_STATUS]);
4773816bedaSJoe Komlodi     qemu_set_irq(s->irq, level);
4783816bedaSJoe Komlodi }
4793816bedaSJoe Komlodi 
4804ba25376SJoe Komlodi static void aspeed_i3c_device_end_transfer(AspeedI3CDevice *s, bool is_i2c)
4814ba25376SJoe Komlodi {
4824ba25376SJoe Komlodi     if (is_i2c) {
4834ba25376SJoe Komlodi         legacy_i2c_end_transfer(s->bus);
4844ba25376SJoe Komlodi     } else {
4854ba25376SJoe Komlodi         i3c_end_transfer(s->bus);
4864ba25376SJoe Komlodi     }
4874ba25376SJoe Komlodi }
4884ba25376SJoe Komlodi 
4894ba25376SJoe Komlodi static int aspeed_i3c_device_send_start(AspeedI3CDevice *s, uint8_t addr,
4904ba25376SJoe Komlodi                                         bool is_recv, bool is_i2c)
4914ba25376SJoe Komlodi {
4924ba25376SJoe Komlodi     int ret;
4934ba25376SJoe Komlodi 
4944ba25376SJoe Komlodi     if (is_i2c) {
4954ba25376SJoe Komlodi         ret = legacy_i2c_start_transfer(s->bus, addr, is_recv);
4964ba25376SJoe Komlodi     } else {
4974ba25376SJoe Komlodi         ret = i3c_start_transfer(s->bus, addr, is_recv);
4984ba25376SJoe Komlodi     }
4994ba25376SJoe Komlodi     if (ret) {
5004ba25376SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: NACKed on TX with addr 0x%.2x\n",
5014ba25376SJoe Komlodi                       object_get_canonical_path(OBJECT(s)), addr);
5024ba25376SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS,
5034ba25376SJoe Komlodi                          ASPEED_I3C_TRANSFER_STATE_HALT);
5044ba25376SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_STATUS,
5054ba25376SJoe Komlodi                          ASPEED_I3C_TRANSFER_STATUS_HALT);
5064ba25376SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, INTR_STATUS, TRANSFER_ERR, 1);
5074ba25376SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, DEVICE_CTRL, I3C_RESUME, 1);
5084ba25376SJoe Komlodi     }
5094ba25376SJoe Komlodi 
5104ba25376SJoe Komlodi     return ret;
5114ba25376SJoe Komlodi }
5124ba25376SJoe Komlodi 
5134ba25376SJoe Komlodi static int aspeed_i3c_device_send(AspeedI3CDevice *s, const uint8_t *data,
5144ba25376SJoe Komlodi                                   uint32_t num_to_send, uint32_t *num_sent,
5154ba25376SJoe Komlodi                                   bool is_i2c)
5164ba25376SJoe Komlodi {
5174ba25376SJoe Komlodi     int ret;
5184ba25376SJoe Komlodi     uint32_t i;
5194ba25376SJoe Komlodi 
5204ba25376SJoe Komlodi     *num_sent = 0;
5214ba25376SJoe Komlodi     if (is_i2c) {
5224ba25376SJoe Komlodi         /* Legacy I2C must be byte-by-byte. */
5234ba25376SJoe Komlodi         for (i = 0; i < num_to_send; i++) {
5244ba25376SJoe Komlodi             ret = legacy_i2c_send(s->bus, data[i]);
5254ba25376SJoe Komlodi             if (ret) {
5264ba25376SJoe Komlodi                 break;
5274ba25376SJoe Komlodi             }
5284ba25376SJoe Komlodi             (*num_sent)++;
5294ba25376SJoe Komlodi         }
5304ba25376SJoe Komlodi     } else {
5314ba25376SJoe Komlodi         ret = i3c_send(s->bus, data, num_to_send, num_sent);
5324ba25376SJoe Komlodi     }
5334ba25376SJoe Komlodi     if (ret) {
5344ba25376SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: NACKed sending byte 0x%.2x\n",
5354ba25376SJoe Komlodi                       object_get_canonical_path(OBJECT(s)), data[*num_sent]);
5364ba25376SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS,
5374ba25376SJoe Komlodi                          ASPEED_I3C_TRANSFER_STATE_HALT);
5384ba25376SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_STATUS,
5394ba25376SJoe Komlodi                          ASPEED_I3C_TRANSFER_STATUS_HALT);
5404ba25376SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, INTR_STATUS, TRANSFER_ERR, 1);
5414ba25376SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, DEVICE_CTRL, I3C_RESUME, 1);
5424ba25376SJoe Komlodi     }
5434ba25376SJoe Komlodi 
5444ba25376SJoe Komlodi     trace_aspeed_i3c_device_send(s->id, *num_sent);
5454ba25376SJoe Komlodi 
5464ba25376SJoe Komlodi     return ret;
5474ba25376SJoe Komlodi }
5484ba25376SJoe Komlodi 
5494ba25376SJoe Komlodi static int aspeed_i3c_device_send_byte(AspeedI3CDevice *s, uint8_t byte,
5504ba25376SJoe Komlodi                                        bool is_i2c)
5514ba25376SJoe Komlodi {
5524ba25376SJoe Komlodi     /*
5534ba25376SJoe Komlodi      * Ignored, the caller will know if we sent 0 or 1 bytes depending on if
5544ba25376SJoe Komlodi      * we were ACKed/NACKed.
5554ba25376SJoe Komlodi      */
5564ba25376SJoe Komlodi     uint32_t num_sent;
5574ba25376SJoe Komlodi     return aspeed_i3c_device_send(s, &byte, 1, &num_sent, is_i2c);
5584ba25376SJoe Komlodi }
5594ba25376SJoe Komlodi 
5604ba25376SJoe Komlodi static int aspeed_i3c_device_recv_data(AspeedI3CDevice *s, bool is_i2c,
5614ba25376SJoe Komlodi                                        uint8_t *data, uint16_t num_to_read,
5624ba25376SJoe Komlodi                                        uint32_t *num_read)
5634ba25376SJoe Komlodi {
5644ba25376SJoe Komlodi     int ret;
5654ba25376SJoe Komlodi 
5664ba25376SJoe Komlodi     if (is_i2c) {
5674ba25376SJoe Komlodi         for (uint16_t i = 0; i < num_to_read; i++) {
5684ba25376SJoe Komlodi             data[i] = legacy_i2c_recv(s->bus);
5694ba25376SJoe Komlodi         }
5704ba25376SJoe Komlodi         /* I2C devices can neither NACK a read, nor end transfers early. */
5714ba25376SJoe Komlodi         *num_read = num_to_read;
5724ba25376SJoe Komlodi         trace_aspeed_i3c_device_recv_data(s->id, *num_read);
5734ba25376SJoe Komlodi         return 0;
5744ba25376SJoe Komlodi     }
5754ba25376SJoe Komlodi     /* I3C devices can NACK if the controller sends an unsupported CCC. */
5764ba25376SJoe Komlodi     ret = i3c_recv(s->bus, data, num_to_read, num_read);
5774ba25376SJoe Komlodi     if (ret) {
5784ba25376SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: NACKed receiving byte\n",
5794ba25376SJoe Komlodi                       object_get_canonical_path(OBJECT(s)));
5804ba25376SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS,
5814ba25376SJoe Komlodi                          ASPEED_I3C_TRANSFER_STATE_HALT);
5824ba25376SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_STATUS,
5834ba25376SJoe Komlodi                          ASPEED_I3C_TRANSFER_STATUS_HALT);
5844ba25376SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, INTR_STATUS, TRANSFER_ERR, 1);
5854ba25376SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, DEVICE_CTRL, I3C_RESUME, 1);
5864ba25376SJoe Komlodi     }
5874ba25376SJoe Komlodi 
5884ba25376SJoe Komlodi     trace_aspeed_i3c_device_recv_data(s->id, *num_read);
5894ba25376SJoe Komlodi 
5904ba25376SJoe Komlodi     return ret;
5914ba25376SJoe Komlodi }
5924ba25376SJoe Komlodi 
593*eee51e8fSJoe Komlodi static inline void aspeed_i3c_device_ctrl_w(AspeedI3CDevice *s,
594*eee51e8fSJoe Komlodi                                                    uint32_t val)
595*eee51e8fSJoe Komlodi {
596*eee51e8fSJoe Komlodi     /*
597*eee51e8fSJoe Komlodi      * If the user is setting I3C_RESUME, the controller was halted.
598*eee51e8fSJoe Komlodi      * Try and resume execution and leave the bit cleared.
599*eee51e8fSJoe Komlodi      */
600*eee51e8fSJoe Komlodi     if (FIELD_EX32(val, DEVICE_CTRL, I3C_RESUME)) {
601*eee51e8fSJoe Komlodi         aspeed_i3c_device_cmd_queue_execute(s);
602*eee51e8fSJoe Komlodi         val = FIELD_DP32(val, DEVICE_CTRL, I3C_RESUME, 0);
603*eee51e8fSJoe Komlodi     }
604*eee51e8fSJoe Komlodi     /*
605*eee51e8fSJoe Komlodi      * I3C_ABORT being set sends an I3C STOP. It's cleared when the STOP is
606*eee51e8fSJoe Komlodi      * sent.
607*eee51e8fSJoe Komlodi      */
608*eee51e8fSJoe Komlodi     if (FIELD_EX32(val, DEVICE_CTRL, I3C_ABORT)) {
609*eee51e8fSJoe Komlodi         aspeed_i3c_device_end_transfer(s, /*is_i2c=*/true);
610*eee51e8fSJoe Komlodi         aspeed_i3c_device_end_transfer(s, /*is_i2c=*/false);
611*eee51e8fSJoe Komlodi         val = FIELD_DP32(val, DEVICE_CTRL, I3C_ABORT, 0);
612*eee51e8fSJoe Komlodi         ARRAY_FIELD_DP32(s->regs, INTR_STATUS, TRANSFER_ABORT, 1);
613*eee51e8fSJoe Komlodi         aspeed_i3c_device_update_irq(s);
614*eee51e8fSJoe Komlodi     }
615*eee51e8fSJoe Komlodi     /* Update present state. */
616*eee51e8fSJoe Komlodi     ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS,
617*eee51e8fSJoe Komlodi                      ASPEED_I3C_TRANSFER_STATE_IDLE);
618*eee51e8fSJoe Komlodi     ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_STATUS,
619*eee51e8fSJoe Komlodi                      ASPEED_I3C_TRANSFER_STATUS_IDLE);
620*eee51e8fSJoe Komlodi 
621*eee51e8fSJoe Komlodi     s->regs[R_DEVICE_CTRL] = val;
622*eee51e8fSJoe Komlodi }
623*eee51e8fSJoe Komlodi 
6244ba25376SJoe Komlodi static inline bool aspeed_i3c_device_target_is_i2c(AspeedI3CDevice *s,
6254ba25376SJoe Komlodi                                                    uint16_t offset)
6264ba25376SJoe Komlodi {
6274ba25376SJoe Komlodi     uint16_t dev_index = R_DEVICE_ADDR_TABLE_LOC1 + offset;
6284ba25376SJoe Komlodi     return FIELD_EX32(s->regs[dev_index], DEVICE_ADDR_TABLE_LOC1,
6294ba25376SJoe Komlodi                    LEGACY_I2C_DEVICE);
6304ba25376SJoe Komlodi }
6314ba25376SJoe Komlodi 
6324ba25376SJoe Komlodi static uint8_t aspeed_i3c_device_target_addr(AspeedI3CDevice *s,
6334ba25376SJoe Komlodi                                              uint16_t offset)
6344ba25376SJoe Komlodi {
6354ba25376SJoe Komlodi     if (offset > ASPEED_I3C_NR_DEVICES) {
6364ba25376SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: Device addr table offset %d out of "
6374ba25376SJoe Komlodi                       "bounds\n", object_get_canonical_path(OBJECT(s)), offset);
6384ba25376SJoe Komlodi         /* If we're out of bounds, return an address of 0. */
6394ba25376SJoe Komlodi         return 0;
6404ba25376SJoe Komlodi     }
6414ba25376SJoe Komlodi 
6424ba25376SJoe Komlodi     uint16_t dev_index = R_DEVICE_ADDR_TABLE_LOC1 + offset;
6434ba25376SJoe Komlodi     /* I2C devices use a static address. */
6444ba25376SJoe Komlodi     if (aspeed_i3c_device_target_is_i2c(s, offset)) {
6454ba25376SJoe Komlodi         return FIELD_EX32(s->regs[dev_index], DEVICE_ADDR_TABLE_LOC1,
6464ba25376SJoe Komlodi                           DEV_STATIC_ADDR);
6474ba25376SJoe Komlodi     }
6484ba25376SJoe Komlodi     return FIELD_EX32(s->regs[dev_index], DEVICE_ADDR_TABLE_LOC1,
6494ba25376SJoe Komlodi                       DEV_DYNAMIC_ADDR);
6504ba25376SJoe Komlodi }
6514ba25376SJoe Komlodi 
65293ec6949SJoe Komlodi static int aspeed_i3c_device_addr_table_index_from_addr(AspeedI3CDevice *s,
65393ec6949SJoe Komlodi                                                         uint8_t addr)
65493ec6949SJoe Komlodi {
65593ec6949SJoe Komlodi     uint8_t table_size = ARRAY_FIELD_EX32(s->regs, DEVICE_ADDR_TABLE_POINTER,
65693ec6949SJoe Komlodi                                           DEPTH);
65793ec6949SJoe Komlodi     for (uint8_t i = 0; i < table_size; i++) {
65893ec6949SJoe Komlodi         if (aspeed_i3c_device_target_addr(s, i) == addr) {
65993ec6949SJoe Komlodi             return i;
66093ec6949SJoe Komlodi         }
66193ec6949SJoe Komlodi     }
66293ec6949SJoe Komlodi     return -1;
66393ec6949SJoe Komlodi }
66493ec6949SJoe Komlodi 
66593ec6949SJoe Komlodi static void aspeed_i3c_device_send_disec(AspeedI3CDevice *s)
66693ec6949SJoe Komlodi {
66793ec6949SJoe Komlodi     uint8_t ccc = I3C_CCC_DISEC;
66893ec6949SJoe Komlodi     if (s->ibi_data.send_direct_disec) {
66993ec6949SJoe Komlodi         ccc = I3C_CCCD_DISEC;
67093ec6949SJoe Komlodi     }
67193ec6949SJoe Komlodi 
67293ec6949SJoe Komlodi     aspeed_i3c_device_send_start(s, I3C_BROADCAST, /*is_recv=*/false,
67393ec6949SJoe Komlodi                                  /*is_i2c=*/false);
67493ec6949SJoe Komlodi     aspeed_i3c_device_send_byte(s, ccc, /*is_i2c=*/false);
67593ec6949SJoe Komlodi     if (s->ibi_data.send_direct_disec) {
67693ec6949SJoe Komlodi         aspeed_i3c_device_send_start(s, s->ibi_data.disec_addr,
67793ec6949SJoe Komlodi                                      /*is_recv=*/false, /*is_i2c=*/false);
67893ec6949SJoe Komlodi     }
67993ec6949SJoe Komlodi     aspeed_i3c_device_send_byte(s, s->ibi_data.disec_byte, /*is_i2c=*/false);
68093ec6949SJoe Komlodi }
68193ec6949SJoe Komlodi 
68293ec6949SJoe Komlodi static int aspeed_i3c_device_handle_hj(AspeedI3CDevice *s)
68393ec6949SJoe Komlodi {
68493ec6949SJoe Komlodi     if (ARRAY_FIELD_EX32(s->regs, IBI_QUEUE_CTRL, NOTIFY_REJECTED_HOT_JOIN)) {
68593ec6949SJoe Komlodi         s->ibi_data.notify_ibi_nack = true;
68693ec6949SJoe Komlodi     }
68793ec6949SJoe Komlodi 
68893ec6949SJoe Komlodi     bool nack_and_disable = ARRAY_FIELD_EX32(s->regs, DEVICE_CTRL,
68993ec6949SJoe Komlodi                                              HOT_JOIN_ACK_NACK_CTRL);
69093ec6949SJoe Komlodi     if (nack_and_disable) {
69193ec6949SJoe Komlodi         s->ibi_data.ibi_queue_status = FIELD_DP32(s->ibi_data.ibi_queue_status,
69293ec6949SJoe Komlodi                                                   IBI_QUEUE_STATUS,
69393ec6949SJoe Komlodi                                                   IBI_STATUS, 1);
69493ec6949SJoe Komlodi         s->ibi_data.ibi_nacked = true;
69593ec6949SJoe Komlodi         s->ibi_data.disec_byte = DISEC_HJ;
69693ec6949SJoe Komlodi         return -1;
69793ec6949SJoe Komlodi     }
69893ec6949SJoe Komlodi     return 0;
69993ec6949SJoe Komlodi }
70093ec6949SJoe Komlodi 
70193ec6949SJoe Komlodi static int aspeed_i3c_device_handle_ctlr_req(AspeedI3CDevice *s, uint8_t addr)
70293ec6949SJoe Komlodi {
70393ec6949SJoe Komlodi     if (ARRAY_FIELD_EX32(s->regs, IBI_QUEUE_CTRL, NOTIFY_REJECTED_MASTER_REQ)) {
70493ec6949SJoe Komlodi         s->ibi_data.notify_ibi_nack = true;
70593ec6949SJoe Komlodi     }
70693ec6949SJoe Komlodi 
70793ec6949SJoe Komlodi     int table_offset = aspeed_i3c_device_addr_table_index_from_addr(s, addr);
70893ec6949SJoe Komlodi     /* Doesn't exist in the table, NACK it, don't DISEC. */
70993ec6949SJoe Komlodi     if (table_offset < 0) {
71093ec6949SJoe Komlodi         return -1;
71193ec6949SJoe Komlodi     }
71293ec6949SJoe Komlodi 
71393ec6949SJoe Komlodi     table_offset += R_DEVICE_ADDR_TABLE_LOC1;
71493ec6949SJoe Komlodi     if (FIELD_EX32(s->regs[table_offset], DEVICE_ADDR_TABLE_LOC1, MR_REJECT)) {
71593ec6949SJoe Komlodi         s->ibi_data.ibi_queue_status = FIELD_DP32(s->ibi_data.ibi_queue_status,
71693ec6949SJoe Komlodi                                                   IBI_QUEUE_STATUS,
71793ec6949SJoe Komlodi                                                   IBI_STATUS, 1);
71893ec6949SJoe Komlodi         s->ibi_data.ibi_nacked = true;
71993ec6949SJoe Komlodi         s->ibi_data.disec_addr = addr;
72093ec6949SJoe Komlodi         /* Tell the requester to disable controller role requests. */
72193ec6949SJoe Komlodi         s->ibi_data.disec_byte = DISEC_CR;
72293ec6949SJoe Komlodi         s->ibi_data.send_direct_disec = true;
72393ec6949SJoe Komlodi         return -1;
72493ec6949SJoe Komlodi     }
72593ec6949SJoe Komlodi     return 0;
72693ec6949SJoe Komlodi }
72793ec6949SJoe Komlodi 
72893ec6949SJoe Komlodi static int aspeed_i3c_device_handle_targ_irq(AspeedI3CDevice *s, uint8_t addr)
72993ec6949SJoe Komlodi {
73093ec6949SJoe Komlodi     if (ARRAY_FIELD_EX32(s->regs, IBI_QUEUE_CTRL, NOTIFY_REJECTED_SLAVE_IRQ)) {
73193ec6949SJoe Komlodi         s->ibi_data.notify_ibi_nack = true;
73293ec6949SJoe Komlodi     }
73393ec6949SJoe Komlodi 
73493ec6949SJoe Komlodi     int table_offset = aspeed_i3c_device_addr_table_index_from_addr(s, addr);
73593ec6949SJoe Komlodi     /* Doesn't exist in the table, NACK it, don't DISEC. */
73693ec6949SJoe Komlodi     if (table_offset < 0) {
73793ec6949SJoe Komlodi         return -1;
73893ec6949SJoe Komlodi     }
73993ec6949SJoe Komlodi 
74093ec6949SJoe Komlodi     table_offset += R_DEVICE_ADDR_TABLE_LOC1;
74193ec6949SJoe Komlodi     if (FIELD_EX32(s->regs[table_offset], DEVICE_ADDR_TABLE_LOC1, SIR_REJECT)) {
74293ec6949SJoe Komlodi         s->ibi_data.ibi_queue_status = FIELD_DP32(s->ibi_data.ibi_queue_status,
74393ec6949SJoe Komlodi                                                   IBI_QUEUE_STATUS,
74493ec6949SJoe Komlodi                                                   IBI_STATUS, 1);
74593ec6949SJoe Komlodi         s->ibi_data.ibi_nacked = true;
74693ec6949SJoe Komlodi         s->ibi_data.disec_addr = addr;
74793ec6949SJoe Komlodi         /* Tell the requester to disable interrupts. */
74893ec6949SJoe Komlodi         s->ibi_data.disec_byte = DISEC_INT;
74993ec6949SJoe Komlodi         s->ibi_data.send_direct_disec = true;
75093ec6949SJoe Komlodi         return -1;
75193ec6949SJoe Komlodi     }
75293ec6949SJoe Komlodi     return 0;
75393ec6949SJoe Komlodi }
75493ec6949SJoe Komlodi 
75593ec6949SJoe Komlodi static int aspeed_i3c_device_ibi_handle(I3CBus *bus, I3CTarget *target,
75693ec6949SJoe Komlodi                                         uint8_t addr, bool is_recv)
75793ec6949SJoe Komlodi {
75893ec6949SJoe Komlodi     AspeedI3CDevice *s = ASPEED_I3C_DEVICE(bus->qbus.parent);
75993ec6949SJoe Komlodi 
76093ec6949SJoe Komlodi     trace_aspeed_i3c_device_ibi_handle(s->id, addr, is_recv);
76193ec6949SJoe Komlodi     s->ibi_data.ibi_queue_status = FIELD_DP32(s->ibi_data.ibi_queue_status,
76293ec6949SJoe Komlodi                                               IBI_QUEUE_STATUS, IBI_ID,
76393ec6949SJoe Komlodi                                               (addr << 1) | is_recv);
76493ec6949SJoe Komlodi     /* Is this a hot join request? */
76593ec6949SJoe Komlodi     if (addr == I3C_HJ_ADDR) {
76693ec6949SJoe Komlodi         return aspeed_i3c_device_handle_hj(s);
76793ec6949SJoe Komlodi     }
76893ec6949SJoe Komlodi     /* Is secondary controller requesting access? */
76993ec6949SJoe Komlodi     if (addr == target->address && !is_recv) {
77093ec6949SJoe Komlodi         return aspeed_i3c_device_handle_ctlr_req(s, addr);
77193ec6949SJoe Komlodi     }
77293ec6949SJoe Komlodi     /* Is this a target IRQ? */
77393ec6949SJoe Komlodi     if (addr == target->address && is_recv) {
77493ec6949SJoe Komlodi         return aspeed_i3c_device_handle_targ_irq(s, addr);
77593ec6949SJoe Komlodi     }
77693ec6949SJoe Komlodi 
77793ec6949SJoe Komlodi     /* Not sure what this is, NACK it. */
77893ec6949SJoe Komlodi     return -1;
77993ec6949SJoe Komlodi }
78093ec6949SJoe Komlodi 
78193ec6949SJoe Komlodi static int aspeed_i3c_device_ibi_recv(I3CBus *bus, uint8_t data)
78293ec6949SJoe Komlodi {
78393ec6949SJoe Komlodi     AspeedI3CDevice *s = ASPEED_I3C_DEVICE(bus->qbus.parent);
78493ec6949SJoe Komlodi     if (fifo8_is_full(&s->ibi_data.ibi_intermediate_queue)) {
78593ec6949SJoe Komlodi         return -1;
78693ec6949SJoe Komlodi     }
78793ec6949SJoe Komlodi 
78893ec6949SJoe Komlodi     fifo8_push(&s->ibi_data.ibi_intermediate_queue, data);
78993ec6949SJoe Komlodi     trace_aspeed_i3c_device_ibi_recv(s->id, data);
79093ec6949SJoe Komlodi     return 0;
79193ec6949SJoe Komlodi }
79293ec6949SJoe Komlodi 
79393ec6949SJoe Komlodi static void aspeed_i3c_device_ibi_queue_push(AspeedI3CDevice *s)
79493ec6949SJoe Komlodi {
79593ec6949SJoe Komlodi     /* Stored value is in 32-bit chunks, convert it to byte chunks. */
79693ec6949SJoe Komlodi     uint8_t ibi_slice_size = aspeed_i3c_device_ibi_slice_size(s);
79793ec6949SJoe Komlodi     uint8_t num_slices = fifo8_num_used(&s->ibi_data.ibi_intermediate_queue) /
79893ec6949SJoe Komlodi                          ibi_slice_size;
79993ec6949SJoe Komlodi     uint8_t ibi_status_count = num_slices;
80093ec6949SJoe Komlodi     union {
80193ec6949SJoe Komlodi         uint8_t b[sizeof(uint32_t)];
80293ec6949SJoe Komlodi         uint32_t val32;
80393ec6949SJoe Komlodi     } ibi_data = {
80493ec6949SJoe Komlodi         .val32 = 0
80593ec6949SJoe Komlodi     };
80693ec6949SJoe Komlodi 
80793ec6949SJoe Komlodi     /* The report was suppressed, do nothing. */
80893ec6949SJoe Komlodi     if (s->ibi_data.ibi_nacked && !s->ibi_data.notify_ibi_nack) {
80993ec6949SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS,
81093ec6949SJoe Komlodi                          ASPEED_I3C_TRANSFER_STATE_IDLE);
81193ec6949SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_STATUS,
81293ec6949SJoe Komlodi                          ASPEED_I3C_TRANSFER_STATUS_IDLE);
81393ec6949SJoe Komlodi         return;
81493ec6949SJoe Komlodi     }
81593ec6949SJoe Komlodi 
81693ec6949SJoe Komlodi     /* If we don't have any slices to push, just push the status. */
81793ec6949SJoe Komlodi     if (num_slices == 0) {
81893ec6949SJoe Komlodi         s->ibi_data.ibi_queue_status =
81993ec6949SJoe Komlodi              FIELD_DP32(s->ibi_data.ibi_queue_status, IBI_QUEUE_STATUS,
82093ec6949SJoe Komlodi                         LAST_STATUS, 1);
82193ec6949SJoe Komlodi         fifo32_push(&s->ibi_queue, s->ibi_data.ibi_queue_status);
82293ec6949SJoe Komlodi         ibi_status_count = 1;
82393ec6949SJoe Komlodi     }
82493ec6949SJoe Komlodi 
82593ec6949SJoe Komlodi     for (uint8_t i = 0; i < num_slices; i++) {
82693ec6949SJoe Komlodi         /* If this is the last slice, set LAST_STATUS. */
82793ec6949SJoe Komlodi         if (fifo8_num_used(&s->ibi_data.ibi_intermediate_queue) <
82893ec6949SJoe Komlodi             ibi_slice_size) {
82993ec6949SJoe Komlodi             s->ibi_data.ibi_queue_status =
83093ec6949SJoe Komlodi                 FIELD_DP32(s->ibi_data.ibi_queue_status, IBI_QUEUE_STATUS,
83193ec6949SJoe Komlodi                            IBI_DATA_LEN,
83293ec6949SJoe Komlodi                            fifo8_num_used(&s->ibi_data.ibi_intermediate_queue));
83393ec6949SJoe Komlodi             s->ibi_data.ibi_queue_status =
83493ec6949SJoe Komlodi                 FIELD_DP32(s->ibi_data.ibi_queue_status, IBI_QUEUE_STATUS,
83593ec6949SJoe Komlodi                            LAST_STATUS, 1);
83693ec6949SJoe Komlodi         } else {
83793ec6949SJoe Komlodi             s->ibi_data.ibi_queue_status =
83893ec6949SJoe Komlodi                 FIELD_DP32(s->ibi_data.ibi_queue_status, IBI_QUEUE_STATUS,
83993ec6949SJoe Komlodi                            IBI_DATA_LEN, ibi_slice_size);
84093ec6949SJoe Komlodi         }
84193ec6949SJoe Komlodi 
84293ec6949SJoe Komlodi         /* Push the IBI status header. */
84393ec6949SJoe Komlodi         fifo32_push(&s->ibi_queue, s->ibi_data.ibi_queue_status);
84493ec6949SJoe Komlodi         /* Move each IBI byte into a 32-bit word and push it into the queue. */
84593ec6949SJoe Komlodi         for (uint8_t j = 0; j < ibi_slice_size; ++j) {
84693ec6949SJoe Komlodi             if (fifo8_is_empty(&s->ibi_data.ibi_intermediate_queue)) {
84793ec6949SJoe Komlodi                 break;
84893ec6949SJoe Komlodi             }
84993ec6949SJoe Komlodi 
85093ec6949SJoe Komlodi             ibi_data.b[j & 3] = fifo8_pop(&s->ibi_data.ibi_intermediate_queue);
85193ec6949SJoe Komlodi             /* We have 32-bits, push it to the IBI FIFO. */
85293ec6949SJoe Komlodi             if ((j & 0x03) == 0x03) {
85393ec6949SJoe Komlodi                 fifo32_push(&s->ibi_queue, ibi_data.val32);
85493ec6949SJoe Komlodi                 ibi_data.val32 = 0;
85593ec6949SJoe Komlodi             }
85693ec6949SJoe Komlodi         }
85793ec6949SJoe Komlodi         /* If the data isn't 32-bit aligned, push the leftover bytes. */
85893ec6949SJoe Komlodi         if (ibi_slice_size & 0x03) {
85993ec6949SJoe Komlodi             fifo32_push(&s->ibi_queue, ibi_data.val32);
86093ec6949SJoe Komlodi         }
86193ec6949SJoe Komlodi 
86293ec6949SJoe Komlodi         /* Clear out the data length for the next iteration. */
86393ec6949SJoe Komlodi         s->ibi_data.ibi_queue_status = FIELD_DP32(s->ibi_data.ibi_queue_status,
86493ec6949SJoe Komlodi                                          IBI_QUEUE_STATUS, IBI_DATA_LEN, 0);
86593ec6949SJoe Komlodi     }
86693ec6949SJoe Komlodi 
86793ec6949SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, QUEUE_STATUS_LEVEL, IBI_BUF_BLR,
86893ec6949SJoe Komlodi                      fifo32_num_used(&s->ibi_queue));
86993ec6949SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, QUEUE_STATUS_LEVEL, IBI_STATUS_CNT,
87093ec6949SJoe Komlodi                      ibi_status_count);
87193ec6949SJoe Komlodi     /* Threshold is the register value + 1. */
87293ec6949SJoe Komlodi     uint8_t threshold = ARRAY_FIELD_EX32(s->regs, QUEUE_THLD_CTRL,
87393ec6949SJoe Komlodi                                          IBI_STATUS_THLD) + 1;
87493ec6949SJoe Komlodi     if (fifo32_num_used(&s->ibi_queue) >= threshold) {
87593ec6949SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, INTR_STATUS, IBI_THLD, 1);
87693ec6949SJoe Komlodi         aspeed_i3c_device_update_irq(s);
87793ec6949SJoe Komlodi     }
87893ec6949SJoe Komlodi 
87993ec6949SJoe Komlodi     /* State update. */
88093ec6949SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS,
88193ec6949SJoe Komlodi                      ASPEED_I3C_TRANSFER_STATE_IDLE);
88293ec6949SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_STATUS,
88393ec6949SJoe Komlodi                      ASPEED_I3C_TRANSFER_STATUS_IDLE);
88493ec6949SJoe Komlodi }
88593ec6949SJoe Komlodi 
88693ec6949SJoe Komlodi static int aspeed_i3c_device_ibi_finish(I3CBus *bus)
88793ec6949SJoe Komlodi {
88893ec6949SJoe Komlodi     AspeedI3CDevice *s = ASPEED_I3C_DEVICE(bus->qbus.parent);
88993ec6949SJoe Komlodi     bool nack_and_disable_hj = ARRAY_FIELD_EX32(s->regs, DEVICE_CTRL,
89093ec6949SJoe Komlodi                                                 HOT_JOIN_ACK_NACK_CTRL);
89193ec6949SJoe Komlodi     if (nack_and_disable_hj || s->ibi_data.send_direct_disec) {
89293ec6949SJoe Komlodi         aspeed_i3c_device_send_disec(s);
89393ec6949SJoe Komlodi     }
89493ec6949SJoe Komlodi     aspeed_i3c_device_ibi_queue_push(s);
89593ec6949SJoe Komlodi 
89693ec6949SJoe Komlodi     /* Clear out the intermediate values. */
89793ec6949SJoe Komlodi     s->ibi_data.ibi_queue_status = 0;
89893ec6949SJoe Komlodi     s->ibi_data.disec_addr = 0;
89993ec6949SJoe Komlodi     s->ibi_data.disec_byte = 0;
90093ec6949SJoe Komlodi     s->ibi_data.send_direct_disec = false;
90193ec6949SJoe Komlodi     s->ibi_data.notify_ibi_nack = false;
90293ec6949SJoe Komlodi     s->ibi_data.ibi_nacked = false;
90393ec6949SJoe Komlodi 
90493ec6949SJoe Komlodi     return 0;
90593ec6949SJoe Komlodi }
90693ec6949SJoe Komlodi 
9073816bedaSJoe Komlodi static uint32_t aspeed_i3c_device_intr_status_r(AspeedI3CDevice *s)
9083816bedaSJoe Komlodi {
9093816bedaSJoe Komlodi     /* Only return the status whose corresponding EN bits are set. */
9103816bedaSJoe Komlodi     return s->regs[R_INTR_STATUS] & s->regs[R_INTR_STATUS_EN];
9113816bedaSJoe Komlodi }
9123816bedaSJoe Komlodi 
9133816bedaSJoe Komlodi static void aspeed_i3c_device_intr_status_w(AspeedI3CDevice *s, uint32_t val)
9143816bedaSJoe Komlodi {
9153816bedaSJoe Komlodi     /* INTR_STATUS[13:5] is w1c, other bits are RO. */
9163816bedaSJoe Komlodi     val &= 0x3fe0;
9173816bedaSJoe Komlodi     s->regs[R_INTR_STATUS] &= ~val;
9183816bedaSJoe Komlodi 
9193816bedaSJoe Komlodi     aspeed_i3c_device_update_irq(s);
9203816bedaSJoe Komlodi }
9213816bedaSJoe Komlodi 
9223816bedaSJoe Komlodi static void aspeed_i3c_device_intr_status_en_w(AspeedI3CDevice *s, uint32_t val)
9233816bedaSJoe Komlodi {
9243816bedaSJoe Komlodi     s->regs[R_INTR_STATUS_EN] = val;
9253816bedaSJoe Komlodi     aspeed_i3c_device_update_irq(s);
9263816bedaSJoe Komlodi }
9273816bedaSJoe Komlodi 
9283816bedaSJoe Komlodi static void aspeed_i3c_device_intr_signal_en_w(AspeedI3CDevice *s, uint32_t val)
9293816bedaSJoe Komlodi {
9303816bedaSJoe Komlodi     s->regs[R_INTR_SIGNAL_EN] = val;
9313816bedaSJoe Komlodi     aspeed_i3c_device_update_irq(s);
9323816bedaSJoe Komlodi }
9333816bedaSJoe Komlodi 
9343816bedaSJoe Komlodi static void aspeed_i3c_device_intr_force_w(AspeedI3CDevice *s, uint32_t val)
9353816bedaSJoe Komlodi {
9363816bedaSJoe Komlodi     /* INTR_FORCE is WO, just set the corresponding INTR_STATUS bits. */
9373816bedaSJoe Komlodi     s->regs[R_INTR_STATUS] = val;
9383816bedaSJoe Komlodi     aspeed_i3c_device_update_irq(s);
9393816bedaSJoe Komlodi }
9403816bedaSJoe Komlodi 
9414ba25376SJoe Komlodi static uint32_t aspeed_i3c_device_pop_rx(AspeedI3CDevice *s)
9424ba25376SJoe Komlodi {
9434ba25376SJoe Komlodi     if (fifo32_is_empty(&s->rx_queue)) {
9444ba25376SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: Tried to read RX FIFO when empty\n",
9454ba25376SJoe Komlodi                       object_get_canonical_path(OBJECT(s)));
9464ba25376SJoe Komlodi         return 0;
9474ba25376SJoe Komlodi     }
9484ba25376SJoe Komlodi 
9494ba25376SJoe Komlodi     uint32_t val = fifo32_pop(&s->rx_queue);
9504ba25376SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, DATA_BUFFER_STATUS_LEVEL, RX_BUF_BLR,
9514ba25376SJoe Komlodi                      fifo32_num_used(&s->rx_queue));
9524ba25376SJoe Komlodi 
9534ba25376SJoe Komlodi     /* Threshold is 2^RX_BUF_THLD. */
9544ba25376SJoe Komlodi     uint8_t threshold = ARRAY_FIELD_EX32(s->regs, DATA_BUFFER_THLD_CTRL,
9554ba25376SJoe Komlodi                                          RX_BUF_THLD);
9564ba25376SJoe Komlodi     threshold = aspeed_i3c_device_fifo_threshold_from_reg(threshold);
9574ba25376SJoe Komlodi     if (fifo32_num_used(&s->rx_queue) < threshold) {
9584ba25376SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, INTR_STATUS, RX_THLD, 0);
9594ba25376SJoe Komlodi         aspeed_i3c_device_update_irq(s);
9604ba25376SJoe Komlodi     }
9614ba25376SJoe Komlodi 
9624ba25376SJoe Komlodi     trace_aspeed_i3c_device_pop_rx(s->id, val);
9634ba25376SJoe Komlodi     return val;
9644ba25376SJoe Komlodi }
9654ba25376SJoe Komlodi 
96693ec6949SJoe Komlodi static uint32_t aspeed_i3c_device_ibi_queue_r(AspeedI3CDevice *s)
96793ec6949SJoe Komlodi {
96893ec6949SJoe Komlodi     if (fifo32_is_empty(&s->ibi_queue)) {
96993ec6949SJoe Komlodi         return 0;
97093ec6949SJoe Komlodi     }
97193ec6949SJoe Komlodi 
97293ec6949SJoe Komlodi     uint32_t val = fifo32_pop(&s->ibi_queue);
97393ec6949SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, QUEUE_STATUS_LEVEL, IBI_BUF_BLR,
97493ec6949SJoe Komlodi                      fifo32_num_used(&s->ibi_queue));
97593ec6949SJoe Komlodi     /* Threshold is the register value + 1. */
97693ec6949SJoe Komlodi     uint8_t threshold = ARRAY_FIELD_EX32(s->regs, QUEUE_THLD_CTRL,
97793ec6949SJoe Komlodi                                          IBI_STATUS_THLD) + 1;
97893ec6949SJoe Komlodi     if (fifo32_num_used(&s->ibi_queue) < threshold) {
97993ec6949SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, INTR_STATUS, IBI_THLD, 0);
98093ec6949SJoe Komlodi         aspeed_i3c_device_update_irq(s);
98193ec6949SJoe Komlodi     }
98293ec6949SJoe Komlodi     return val;
98393ec6949SJoe Komlodi }
98493ec6949SJoe Komlodi 
9854ba25376SJoe Komlodi static uint32_t aspeed_i3c_device_resp_queue_port_r(AspeedI3CDevice *s)
9864ba25376SJoe Komlodi {
9874ba25376SJoe Komlodi     if (fifo32_is_empty(&s->resp_queue)) {
9884ba25376SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: Tried to read response FIFO when "
9894ba25376SJoe Komlodi                       "empty\n", object_get_canonical_path(OBJECT(s)));
9904ba25376SJoe Komlodi         return 0;
9914ba25376SJoe Komlodi     }
9924ba25376SJoe Komlodi 
9934ba25376SJoe Komlodi     uint32_t val = fifo32_pop(&s->resp_queue);
9944ba25376SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, QUEUE_STATUS_LEVEL, RESP_BUF_BLR,
9954ba25376SJoe Komlodi                      fifo32_num_used(&s->resp_queue));
9964ba25376SJoe Komlodi 
9974ba25376SJoe Komlodi     /* Threshold is the register value + 1. */
9984ba25376SJoe Komlodi     uint8_t threshold = ARRAY_FIELD_EX32(s->regs, QUEUE_THLD_CTRL,
9994ba25376SJoe Komlodi                                          RESP_BUF_THLD) + 1;
10004ba25376SJoe Komlodi     if (fifo32_num_used(&s->resp_queue) < threshold) {
10014ba25376SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, INTR_STATUS, RESP_RDY, 0);
10024ba25376SJoe Komlodi         aspeed_i3c_device_update_irq(s);
10034ba25376SJoe Komlodi     }
10044ba25376SJoe Komlodi 
10054ba25376SJoe Komlodi     return val;
10064ba25376SJoe Komlodi }
10074ba25376SJoe Komlodi 
10087d87775fSJoe Komlodi static uint64_t aspeed_i3c_device_read(void *opaque, hwaddr offset,
10097d87775fSJoe Komlodi                                        unsigned size)
10107d87775fSJoe Komlodi {
10117d87775fSJoe Komlodi     AspeedI3CDevice *s = ASPEED_I3C_DEVICE(opaque);
10127d87775fSJoe Komlodi     uint32_t addr = offset >> 2;
10137d87775fSJoe Komlodi     uint64_t value;
10147d87775fSJoe Komlodi 
10157d87775fSJoe Komlodi     switch (addr) {
101609467a8aSJoe Komlodi     /* RAZ */
10177d87775fSJoe Komlodi     case R_COMMAND_QUEUE_PORT:
101809467a8aSJoe Komlodi     case R_RESET_CTRL:
101909467a8aSJoe Komlodi     case R_INTR_FORCE:
10207d87775fSJoe Komlodi         value = 0;
10217d87775fSJoe Komlodi         break;
102293ec6949SJoe Komlodi     case R_IBI_QUEUE_DATA:
102393ec6949SJoe Komlodi         value = aspeed_i3c_device_ibi_queue_r(s);
102493ec6949SJoe Komlodi         break;
10253816bedaSJoe Komlodi     case R_INTR_STATUS:
10263816bedaSJoe Komlodi         value = aspeed_i3c_device_intr_status_r(s);
10273816bedaSJoe Komlodi         break;
10284ba25376SJoe Komlodi     case R_RX_TX_DATA_PORT:
10294ba25376SJoe Komlodi         value = aspeed_i3c_device_pop_rx(s);
10304ba25376SJoe Komlodi         break;
10314ba25376SJoe Komlodi     case R_RESPONSE_QUEUE_PORT:
10324ba25376SJoe Komlodi         value = aspeed_i3c_device_resp_queue_port_r(s);
10334ba25376SJoe Komlodi         break;
10347d87775fSJoe Komlodi     default:
10357d87775fSJoe Komlodi         value = s->regs[addr];
10367d87775fSJoe Komlodi         break;
10377d87775fSJoe Komlodi     }
10387d87775fSJoe Komlodi 
10397d87775fSJoe Komlodi     trace_aspeed_i3c_device_read(s->id, offset, value);
10407d87775fSJoe Komlodi 
10417d87775fSJoe Komlodi     return value;
10427d87775fSJoe Komlodi }
10437d87775fSJoe Komlodi 
10444ba25376SJoe Komlodi static void aspeed_i3c_device_resp_queue_push(AspeedI3CDevice *s,
10454ba25376SJoe Komlodi                                               uint8_t err, uint8_t tid,
10464ba25376SJoe Komlodi                                               uint8_t ccc_type,
10474ba25376SJoe Komlodi                                               uint16_t data_len)
10484ba25376SJoe Komlodi {
10494ba25376SJoe Komlodi     uint32_t val = 0;
10504ba25376SJoe Komlodi     val = FIELD_DP32(val, RESPONSE_QUEUE_PORT, ERR_STATUS, err);
10514ba25376SJoe Komlodi     val = FIELD_DP32(val, RESPONSE_QUEUE_PORT, TID, tid);
10524ba25376SJoe Komlodi     val = FIELD_DP32(val, RESPONSE_QUEUE_PORT, CCCT, ccc_type);
10534ba25376SJoe Komlodi     val = FIELD_DP32(val, RESPONSE_QUEUE_PORT, DL, data_len);
10544ba25376SJoe Komlodi     if (!fifo32_is_full(&s->resp_queue)) {
10554ba25376SJoe Komlodi         trace_aspeed_i3c_device_resp_queue_push(s->id, val);
10564ba25376SJoe Komlodi         fifo32_push(&s->resp_queue, val);
10574ba25376SJoe Komlodi     }
10584ba25376SJoe Komlodi 
10594ba25376SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, QUEUE_STATUS_LEVEL, RESP_BUF_BLR,
10604ba25376SJoe Komlodi                      fifo32_num_used(&s->resp_queue));
10614ba25376SJoe Komlodi     /* Threshold is the register value + 1. */
10624ba25376SJoe Komlodi     uint8_t threshold = ARRAY_FIELD_EX32(s->regs, QUEUE_THLD_CTRL,
10634ba25376SJoe Komlodi                                          RESP_BUF_THLD) + 1;
10644ba25376SJoe Komlodi     if (fifo32_num_used(&s->resp_queue) >= threshold) {
10654ba25376SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, INTR_STATUS, RESP_RDY, 1);
10664ba25376SJoe Komlodi         aspeed_i3c_device_update_irq(s);
10674ba25376SJoe Komlodi     }
10684ba25376SJoe Komlodi }
10694ba25376SJoe Komlodi 
10704ba25376SJoe Komlodi static void aspeed_i3c_device_push_tx(AspeedI3CDevice *s, uint32_t val)
10714ba25376SJoe Komlodi {
10724ba25376SJoe Komlodi     if (fifo32_is_full(&s->tx_queue)) {
10734ba25376SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: Tried to push to TX FIFO when "
10744ba25376SJoe Komlodi                       "full\n", object_get_canonical_path(OBJECT(s)));
10754ba25376SJoe Komlodi         return;
10764ba25376SJoe Komlodi     }
10774ba25376SJoe Komlodi 
10784ba25376SJoe Komlodi     trace_aspeed_i3c_device_push_tx(s->id, val);
10794ba25376SJoe Komlodi     fifo32_push(&s->tx_queue, val);
10804ba25376SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, DATA_BUFFER_STATUS_LEVEL, TX_BUF_EMPTY_LOC,
10814ba25376SJoe Komlodi                      fifo32_num_free(&s->tx_queue));
10824ba25376SJoe Komlodi 
10834ba25376SJoe Komlodi     /* Threshold is 2^TX_BUF_THLD. */
10844ba25376SJoe Komlodi     uint8_t empty_threshold = ARRAY_FIELD_EX32(s->regs, DATA_BUFFER_THLD_CTRL,
10854ba25376SJoe Komlodi                                                TX_BUF_THLD);
10864ba25376SJoe Komlodi     empty_threshold =
10874ba25376SJoe Komlodi         aspeed_i3c_device_fifo_threshold_from_reg(empty_threshold);
10884ba25376SJoe Komlodi     if (fifo32_num_free(&s->tx_queue) < empty_threshold) {
10894ba25376SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, INTR_STATUS, TX_THLD, 0);
10904ba25376SJoe Komlodi         aspeed_i3c_device_update_irq(s);
10914ba25376SJoe Komlodi     }
10924ba25376SJoe Komlodi }
10934ba25376SJoe Komlodi 
10944ba25376SJoe Komlodi static uint32_t aspeed_i3c_device_pop_tx(AspeedI3CDevice *s)
10954ba25376SJoe Komlodi {
10964ba25376SJoe Komlodi     if (fifo32_is_empty(&s->tx_queue)) {
10974ba25376SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: Tried to pop from TX FIFO when "
10984ba25376SJoe Komlodi                       "empty\n", object_get_canonical_path(OBJECT(s)));
10994ba25376SJoe Komlodi         return 0;
11004ba25376SJoe Komlodi     }
11014ba25376SJoe Komlodi 
11024ba25376SJoe Komlodi     uint32_t val = fifo32_pop(&s->tx_queue);
11034ba25376SJoe Komlodi     trace_aspeed_i3c_device_pop_tx(s->id, val);
11044ba25376SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, DATA_BUFFER_STATUS_LEVEL, TX_BUF_EMPTY_LOC,
11054ba25376SJoe Komlodi                      fifo32_num_free(&s->tx_queue));
11064ba25376SJoe Komlodi 
11074ba25376SJoe Komlodi     /* Threshold is 2^TX_BUF_THLD. */
11084ba25376SJoe Komlodi     uint8_t empty_threshold = ARRAY_FIELD_EX32(s->regs, DATA_BUFFER_THLD_CTRL,
11094ba25376SJoe Komlodi                                                TX_BUF_THLD);
11104ba25376SJoe Komlodi     empty_threshold =
11114ba25376SJoe Komlodi         aspeed_i3c_device_fifo_threshold_from_reg(empty_threshold);
11124ba25376SJoe Komlodi     if (fifo32_num_free(&s->tx_queue) >= empty_threshold) {
11134ba25376SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, INTR_STATUS, TX_THLD, 1);
11144ba25376SJoe Komlodi         aspeed_i3c_device_update_irq(s);
11154ba25376SJoe Komlodi     }
11164ba25376SJoe Komlodi     return val;
11174ba25376SJoe Komlodi }
11184ba25376SJoe Komlodi 
11194ba25376SJoe Komlodi static void aspeed_i3c_device_push_rx(AspeedI3CDevice *s, uint32_t val)
11204ba25376SJoe Komlodi {
11214ba25376SJoe Komlodi     if (fifo32_is_full(&s->rx_queue)) {
11224ba25376SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: Tried to push to RX FIFO when "
11234ba25376SJoe Komlodi                       "full\n", object_get_canonical_path(OBJECT(s)));
11244ba25376SJoe Komlodi         return;
11254ba25376SJoe Komlodi     }
11264ba25376SJoe Komlodi     trace_aspeed_i3c_device_push_rx(s->id, val);
11274ba25376SJoe Komlodi     fifo32_push(&s->rx_queue, val);
11284ba25376SJoe Komlodi 
11294ba25376SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, DATA_BUFFER_STATUS_LEVEL, RX_BUF_BLR,
11304ba25376SJoe Komlodi                      fifo32_num_used(&s->rx_queue));
11314ba25376SJoe Komlodi     /* Threshold is 2^RX_BUF_THLD. */
11324ba25376SJoe Komlodi     uint8_t threshold = ARRAY_FIELD_EX32(s->regs, DATA_BUFFER_THLD_CTRL,
11334ba25376SJoe Komlodi                                          RX_BUF_THLD);
11344ba25376SJoe Komlodi     threshold = aspeed_i3c_device_fifo_threshold_from_reg(threshold);
11354ba25376SJoe Komlodi     if (fifo32_num_used(&s->rx_queue) >= threshold) {
11364ba25376SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, INTR_STATUS, RX_THLD, 1);
11374ba25376SJoe Komlodi         aspeed_i3c_device_update_irq(s);
11384ba25376SJoe Komlodi     }
11394ba25376SJoe Komlodi }
11404ba25376SJoe Komlodi 
11414ba25376SJoe Komlodi static void aspeed_i3c_device_short_transfer(AspeedI3CDevice *s,
11424ba25376SJoe Komlodi                                              AspeedI3CTransferCmd cmd,
11434ba25376SJoe Komlodi                                              AspeedI3CShortArg arg)
11444ba25376SJoe Komlodi {
11454ba25376SJoe Komlodi     uint8_t err = ASPEED_I3C_RESP_QUEUE_ERR_NONE;
11464ba25376SJoe Komlodi     uint8_t addr = aspeed_i3c_device_target_addr(s, cmd.dev_index);
11474ba25376SJoe Komlodi     bool is_i2c = aspeed_i3c_device_target_is_i2c(s, cmd.dev_index);
11484ba25376SJoe Komlodi     uint8_t data[4]; /* Max we can send on a short transfer is 4 bytes. */
11494ba25376SJoe Komlodi     uint8_t len = 0;
11504ba25376SJoe Komlodi     uint32_t bytes_sent; /* Ignored on short transfers. */
11514ba25376SJoe Komlodi 
11524ba25376SJoe Komlodi     /* Can't do reads on a short transfer. */
11534ba25376SJoe Komlodi     if (cmd.rnw) {
11544ba25376SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: Cannot do a read on a short "
11554ba25376SJoe Komlodi                       "transfer\n", object_get_canonical_path(OBJECT(s)));
11564ba25376SJoe Komlodi         return;
11574ba25376SJoe Komlodi     }
11584ba25376SJoe Komlodi 
11594ba25376SJoe Komlodi     if (aspeed_i3c_device_send_start(s, addr, /*is_recv=*/false, is_i2c)) {
11604ba25376SJoe Komlodi         err = ASPEED_I3C_RESP_QUEUE_ERR_I2C_NACK;
11614ba25376SJoe Komlodi         goto transfer_done;
11624ba25376SJoe Komlodi     }
11634ba25376SJoe Komlodi 
11644ba25376SJoe Komlodi     /* Are we sending a command? */
11654ba25376SJoe Komlodi     if (cmd.cp) {
11664ba25376SJoe Komlodi         data[len] = cmd.cmd;
11674ba25376SJoe Komlodi         len++;
11684ba25376SJoe Komlodi         /*
11694ba25376SJoe Komlodi          * byte0 is the defining byte for a command, and is only sent if a
11704ba25376SJoe Komlodi          * command is present and if the command has a defining byte present.
11714ba25376SJoe Komlodi          * (byte_strb & 0x01) is always treated as set by the controller, and is
11724ba25376SJoe Komlodi          * ignored.
11734ba25376SJoe Komlodi          */
11744ba25376SJoe Komlodi         if (cmd.dbp) {
11754ba25376SJoe Komlodi             data[len] += arg.byte0;
11764ba25376SJoe Komlodi             len++;
11774ba25376SJoe Komlodi         }
11784ba25376SJoe Komlodi     }
11794ba25376SJoe Komlodi 
11804ba25376SJoe Komlodi     /* Send the bytes passed in the argument. */
11814ba25376SJoe Komlodi     if (arg.byte_strb & 0x02) {
11824ba25376SJoe Komlodi         data[len] = arg.byte1;
11834ba25376SJoe Komlodi         len++;
11844ba25376SJoe Komlodi     }
11854ba25376SJoe Komlodi     if (arg.byte_strb & 0x04) {
11864ba25376SJoe Komlodi         data[len] = arg.byte2;
11874ba25376SJoe Komlodi         len++;
11884ba25376SJoe Komlodi     }
11894ba25376SJoe Komlodi 
11904ba25376SJoe Komlodi     if (aspeed_i3c_device_send(s, data, len, &bytes_sent, is_i2c)) {
11914ba25376SJoe Komlodi         err = ASPEED_I3C_RESP_QUEUE_ERR_I2C_NACK;
11924ba25376SJoe Komlodi     } else {
11934ba25376SJoe Komlodi         /* Only go to an idle state on a successful transfer. */
11944ba25376SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS,
11954ba25376SJoe Komlodi                          ASPEED_I3C_TRANSFER_STATE_IDLE);
11964ba25376SJoe Komlodi     }
11974ba25376SJoe Komlodi 
11984ba25376SJoe Komlodi transfer_done:
11994ba25376SJoe Komlodi     if (cmd.toc) {
12004ba25376SJoe Komlodi         aspeed_i3c_device_end_transfer(s, is_i2c);
12014ba25376SJoe Komlodi     }
12024ba25376SJoe Komlodi     if (cmd.roc) {
12034ba25376SJoe Komlodi         /*
12044ba25376SJoe Komlodi          * ccc_type is always 0 in controller mode, data_len is 0 in short
12054ba25376SJoe Komlodi          * transfers.
12064ba25376SJoe Komlodi          */
12074ba25376SJoe Komlodi         aspeed_i3c_device_resp_queue_push(s, err, cmd.tid, /*ccc_type=*/0,
12084ba25376SJoe Komlodi                                           /*data_len=*/0);
12094ba25376SJoe Komlodi     }
12104ba25376SJoe Komlodi }
12114ba25376SJoe Komlodi 
12124ba25376SJoe Komlodi /* Returns number of bytes transmitted. */
12134ba25376SJoe Komlodi static uint16_t aspeed_i3c_device_tx(AspeedI3CDevice *s, uint16_t num,
12144ba25376SJoe Komlodi                                      bool is_i2c)
12154ba25376SJoe Komlodi {
12164ba25376SJoe Komlodi     uint16_t bytes_sent = 0;
12174ba25376SJoe Komlodi     union {
12184ba25376SJoe Komlodi         uint8_t b[sizeof(uint32_t)];
12194ba25376SJoe Komlodi         uint32_t val;
12204ba25376SJoe Komlodi     } val32;
12214ba25376SJoe Komlodi 
12224ba25376SJoe Komlodi     while (bytes_sent < num) {
12234ba25376SJoe Komlodi         val32.val = aspeed_i3c_device_pop_tx(s);
12244ba25376SJoe Komlodi         for (uint8_t i = 0; i < sizeof(val32.val); i++) {
12254ba25376SJoe Komlodi             if (aspeed_i3c_device_send_byte(s, val32.b[i], is_i2c)) {
12264ba25376SJoe Komlodi                 return bytes_sent;
12274ba25376SJoe Komlodi             }
12284ba25376SJoe Komlodi             bytes_sent++;
12294ba25376SJoe Komlodi 
12304ba25376SJoe Komlodi             /* We're not sending the full 32-bits, break early. */
12314ba25376SJoe Komlodi             if (bytes_sent >= num) {
12324ba25376SJoe Komlodi                 break;
12334ba25376SJoe Komlodi             }
12344ba25376SJoe Komlodi         }
12354ba25376SJoe Komlodi     }
12364ba25376SJoe Komlodi 
12374ba25376SJoe Komlodi     return bytes_sent;
12384ba25376SJoe Komlodi }
12394ba25376SJoe Komlodi 
12404ba25376SJoe Komlodi /* Returns number of bytes received. */
12414ba25376SJoe Komlodi static uint16_t aspeed_i3c_device_rx(AspeedI3CDevice *s, uint16_t num,
12424ba25376SJoe Komlodi                                      bool is_i2c)
12434ba25376SJoe Komlodi {
12444ba25376SJoe Komlodi     /*
12454ba25376SJoe Komlodi      * Allocate a temporary buffer to read data from the target.
12464ba25376SJoe Komlodi      * Zero it and word-align it as well in case we're reading unaligned data.
12474ba25376SJoe Komlodi      */
12484ba25376SJoe Komlodi     g_autofree uint8_t *data = g_new0(uint8_t, num + (num & 0x03));
12494ba25376SJoe Komlodi     uint32_t *data32 = (uint32_t *)data;
12504ba25376SJoe Komlodi     /*
12514ba25376SJoe Komlodi      * 32-bits since the I3C API wants a 32-bit number, even though the
12524ba25376SJoe Komlodi      * controller can only do 16-bit transfers.
12534ba25376SJoe Komlodi      */
12544ba25376SJoe Komlodi     uint32_t num_read = 0;
12554ba25376SJoe Komlodi 
12564ba25376SJoe Komlodi     /* Can NACK if the target receives an unsupported CCC. */
12574ba25376SJoe Komlodi     if (aspeed_i3c_device_recv_data(s, is_i2c, data, num, &num_read)) {
12584ba25376SJoe Komlodi         return 0;
12594ba25376SJoe Komlodi     }
12604ba25376SJoe Komlodi 
12614ba25376SJoe Komlodi     for (uint16_t i = 0; i < num_read / 4; i++) {
12624ba25376SJoe Komlodi         aspeed_i3c_device_push_rx(s, *data32);
12634ba25376SJoe Komlodi         data32++;
12644ba25376SJoe Komlodi     }
12654ba25376SJoe Komlodi     /*
12664ba25376SJoe Komlodi      * If we're pushing data that isn't 32-bit aligned, push what's left.
12674ba25376SJoe Komlodi      * It's software's responsibility to know what bits are valid in the partial
12684ba25376SJoe Komlodi      * data.
12694ba25376SJoe Komlodi      */
12704ba25376SJoe Komlodi     if (num_read & 0x03) {
12714ba25376SJoe Komlodi         aspeed_i3c_device_push_rx(s, *data32);
12724ba25376SJoe Komlodi     }
12734ba25376SJoe Komlodi 
12744ba25376SJoe Komlodi     return num_read;
12754ba25376SJoe Komlodi }
12764ba25376SJoe Komlodi 
12774ba25376SJoe Komlodi static int aspeed_i3c_device_transfer_ccc(AspeedI3CDevice *s,
12784ba25376SJoe Komlodi                                            AspeedI3CTransferCmd cmd,
12794ba25376SJoe Komlodi                                            AspeedI3CTransferArg arg)
12804ba25376SJoe Komlodi {
12814ba25376SJoe Komlodi     /* CCC start is always a write. CCCs cannot be done on I2C devices. */
12824ba25376SJoe Komlodi     if (aspeed_i3c_device_send_start(s, I3C_BROADCAST, /*is_recv=*/false,
12834ba25376SJoe Komlodi                                      /*is_i2c=*/false)) {
12844ba25376SJoe Komlodi         return ASPEED_I3C_RESP_QUEUE_ERR_BROADCAST_NACK;
12854ba25376SJoe Komlodi     }
12864ba25376SJoe Komlodi     trace_aspeed_i3c_device_transfer_ccc(s->id, cmd.cmd);
12874ba25376SJoe Komlodi     if (aspeed_i3c_device_send_byte(s, cmd.cmd, /*is_i2c=*/false)) {
12884ba25376SJoe Komlodi         return ASPEED_I3C_RESP_QUEUE_ERR_I2C_NACK;
12894ba25376SJoe Komlodi     }
12904ba25376SJoe Komlodi 
12914ba25376SJoe Komlodi     /* On a direct CCC, we do a restart and then send the target's address. */
12924ba25376SJoe Komlodi     if (CCC_IS_DIRECT(cmd.cmd)) {
12934ba25376SJoe Komlodi         bool is_recv = cmd.rnw;
12944ba25376SJoe Komlodi         uint8_t addr = aspeed_i3c_device_target_addr(s, cmd.dev_index);
12954ba25376SJoe Komlodi         if (aspeed_i3c_device_send_start(s, addr, is_recv, /*is_i2c=*/false)) {
12964ba25376SJoe Komlodi             return ASPEED_I3C_RESP_QUEUE_ERR_BROADCAST_NACK;
12974ba25376SJoe Komlodi         }
12984ba25376SJoe Komlodi     }
12994ba25376SJoe Komlodi 
13004ba25376SJoe Komlodi     return ASPEED_I3C_RESP_QUEUE_ERR_NONE;
13014ba25376SJoe Komlodi }
13024ba25376SJoe Komlodi 
13034ba25376SJoe Komlodi static void aspeed_i3c_device_transfer(AspeedI3CDevice *s,
13044ba25376SJoe Komlodi                                        AspeedI3CTransferCmd cmd,
13054ba25376SJoe Komlodi                                        AspeedI3CTransferArg arg)
13064ba25376SJoe Komlodi {
13074ba25376SJoe Komlodi     bool is_recv = cmd.rnw;
13084ba25376SJoe Komlodi     uint8_t err = ASPEED_I3C_RESP_QUEUE_ERR_NONE;
13094ba25376SJoe Komlodi     uint8_t addr = aspeed_i3c_device_target_addr(s, cmd.dev_index);
13104ba25376SJoe Komlodi     bool is_i2c = aspeed_i3c_device_target_is_i2c(s, cmd.dev_index);
13114ba25376SJoe Komlodi     uint16_t bytes_transferred = 0;
13124ba25376SJoe Komlodi 
13134ba25376SJoe Komlodi     if (cmd.cp) {
13144ba25376SJoe Komlodi         /* We're sending a CCC. */
13154ba25376SJoe Komlodi         err = aspeed_i3c_device_transfer_ccc(s, cmd, arg);
13164ba25376SJoe Komlodi         if (err != ASPEED_I3C_RESP_QUEUE_ERR_NONE) {
13174ba25376SJoe Komlodi             goto transfer_done;
13184ba25376SJoe Komlodi         }
13194ba25376SJoe Komlodi     } else {
13204ba25376SJoe Komlodi         if (ARRAY_FIELD_EX32(s->regs, DEVICE_CTRL, I3C_BROADCAST_ADDR_INC) &&
13214ba25376SJoe Komlodi             is_i2c == false) {
13224ba25376SJoe Komlodi             if (aspeed_i3c_device_send_start(s, I3C_BROADCAST,
13234ba25376SJoe Komlodi                                              /*is_recv=*/false, is_i2c)) {
13244ba25376SJoe Komlodi                 err = ASPEED_I3C_RESP_QUEUE_ERR_I2C_NACK;
13254ba25376SJoe Komlodi                 goto transfer_done;
13264ba25376SJoe Komlodi             }
13274ba25376SJoe Komlodi         }
13284ba25376SJoe Komlodi         /* Otherwise we're doing a private transfer. */
13294ba25376SJoe Komlodi         if (aspeed_i3c_device_send_start(s, addr, is_recv, is_i2c)) {
13304ba25376SJoe Komlodi             err = ASPEED_I3C_RESP_QUEUE_ERR_I2C_NACK;
13314ba25376SJoe Komlodi             goto transfer_done;
13324ba25376SJoe Komlodi         }
13334ba25376SJoe Komlodi     }
13344ba25376SJoe Komlodi 
13354ba25376SJoe Komlodi     if (is_recv) {
13364ba25376SJoe Komlodi         bytes_transferred = aspeed_i3c_device_rx(s, arg.data_len, is_i2c);
13374ba25376SJoe Komlodi     } else {
13384ba25376SJoe Komlodi         bytes_transferred = aspeed_i3c_device_tx(s, arg.data_len, is_i2c);
13394ba25376SJoe Komlodi     }
13404ba25376SJoe Komlodi 
13414ba25376SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS,
13424ba25376SJoe Komlodi                      ASPEED_I3C_TRANSFER_STATE_IDLE);
13434ba25376SJoe Komlodi 
13444ba25376SJoe Komlodi transfer_done:
13454ba25376SJoe Komlodi     if (cmd.toc) {
13464ba25376SJoe Komlodi         aspeed_i3c_device_end_transfer(s, is_i2c);
13474ba25376SJoe Komlodi     }
13484ba25376SJoe Komlodi     if (cmd.roc) {
13494ba25376SJoe Komlodi         /*
13504ba25376SJoe Komlodi          * data_len is the number of bytes that still need to be TX'd, or the
13514ba25376SJoe Komlodi          * number of bytes RX'd.
13524ba25376SJoe Komlodi          */
13534ba25376SJoe Komlodi         uint16_t data_len = is_recv ? bytes_transferred : arg.data_len -
13544ba25376SJoe Komlodi                                                           bytes_transferred;
13554ba25376SJoe Komlodi         /* CCCT is always 0 in controller mode. */
13564ba25376SJoe Komlodi         aspeed_i3c_device_resp_queue_push(s, err, cmd.tid, /*ccc_type=*/0,
13574ba25376SJoe Komlodi                                           data_len);
13584ba25376SJoe Komlodi     }
13594ba25376SJoe Komlodi 
13604ba25376SJoe Komlodi     aspeed_i3c_device_update_irq(s);
13614ba25376SJoe Komlodi }
13624ba25376SJoe Komlodi 
13634ba25376SJoe Komlodi static void aspeed_i3c_device_transfer_cmd(AspeedI3CDevice *s,
13644ba25376SJoe Komlodi                                            AspeedI3CTransferCmd cmd,
13654ba25376SJoe Komlodi                                            AspeedI3CCmdQueueData arg)
13664ba25376SJoe Komlodi {
13674ba25376SJoe Komlodi     uint8_t arg_attr = FIELD_EX32(arg.word, COMMAND_QUEUE_PORT, CMD_ATTR);
13684ba25376SJoe Komlodi 
13694ba25376SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CMD_TID, cmd.tid);
13704ba25376SJoe Komlodi 
13714ba25376SJoe Komlodi     /* User is trying to do HDR transfers, see if we can do them. */
13724ba25376SJoe Komlodi     if (cmd.speed == 0x06 && !aspeed_i3c_device_has_hdr_ddr(s)) {
13734ba25376SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: HDR DDR is not supported\n",
13744ba25376SJoe Komlodi                       object_get_canonical_path(OBJECT(s)));
13754ba25376SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS,
13764ba25376SJoe Komlodi                          ASPEED_I3C_TRANSFER_STATE_HALT);
13774ba25376SJoe Komlodi         return;
13784ba25376SJoe Komlodi     }
13794ba25376SJoe Komlodi     if (cmd.speed == 0x05 && !aspeed_i3c_device_has_hdr_ts(s)) {
13804ba25376SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: HDR TS is not supported\n",
13814ba25376SJoe Komlodi                       object_get_canonical_path(OBJECT(s)));
13824ba25376SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS,
13834ba25376SJoe Komlodi                          ASPEED_I3C_TRANSFER_STATE_HALT);
13844ba25376SJoe Komlodi         return;
13854ba25376SJoe Komlodi     }
13864ba25376SJoe Komlodi 
13874ba25376SJoe Komlodi     if (arg_attr == ASPEED_I3C_CMD_ATTR_TRANSFER_ARG) {
13884ba25376SJoe Komlodi         aspeed_i3c_device_transfer(s, cmd, arg.transfer_arg);
13894ba25376SJoe Komlodi     } else if (arg_attr == ASPEED_I3C_CMD_ATTR_SHORT_DATA_ARG) {
13904ba25376SJoe Komlodi         aspeed_i3c_device_short_transfer(s, cmd, arg.short_arg);
13914ba25376SJoe Komlodi     } else {
13924ba25376SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: Unknown command queue cmd_attr 0x%x"
13934ba25376SJoe Komlodi                       "\n", object_get_canonical_path(OBJECT(s)), arg_attr);
13944ba25376SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS,
13954ba25376SJoe Komlodi                          ASPEED_I3C_TRANSFER_STATE_HALT);
13964ba25376SJoe Komlodi     }
13974ba25376SJoe Komlodi }
13984ba25376SJoe Komlodi 
13994ba25376SJoe Komlodi static void aspeed_i3c_device_update_char_table(AspeedI3CDevice *s,
14004ba25376SJoe Komlodi                                                 uint8_t offset, uint64_t pid,
14014ba25376SJoe Komlodi                                                 uint8_t bcr, uint8_t dcr,
14024ba25376SJoe Komlodi                                                 uint8_t addr)
14034ba25376SJoe Komlodi {
14044ba25376SJoe Komlodi     if (offset > ASPEED_I3C_NR_DEVICES) {
14054ba25376SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: Device char table offset %d out of "
14064ba25376SJoe Komlodi                       "bounds\n", object_get_canonical_path(OBJECT(s)), offset);
14074ba25376SJoe Komlodi         /* If we're out of bounds, do nothing. */
14084ba25376SJoe Komlodi         return;
14094ba25376SJoe Komlodi     }
14104ba25376SJoe Komlodi 
14114ba25376SJoe Komlodi     /* Each char table index is 128 bits apart. */
14124ba25376SJoe Komlodi     uint16_t dev_index = R_DEVICE_CHARACTERISTIC_TABLE_LOC1 + offset *
14134ba25376SJoe Komlodi                                                             sizeof(uint32_t);
14144ba25376SJoe Komlodi     s->regs[dev_index] = pid & 0xffffffff;
14154ba25376SJoe Komlodi     pid >>= 32;
14164ba25376SJoe Komlodi     s->regs[dev_index + 1] = FIELD_DP32(s->regs[dev_index + 1],
14174ba25376SJoe Komlodi                                         DEVICE_CHARACTERISTIC_TABLE_LOC2,
14184ba25376SJoe Komlodi                                         MSB_PID, pid);
14194ba25376SJoe Komlodi     s->regs[dev_index + 2] = FIELD_DP32(s->regs[dev_index + 2],
14204ba25376SJoe Komlodi                                         DEVICE_CHARACTERISTIC_TABLE_LOC3, DCR,
14214ba25376SJoe Komlodi                                         dcr);
14224ba25376SJoe Komlodi     s->regs[dev_index + 2] = FIELD_DP32(s->regs[dev_index + 2],
14234ba25376SJoe Komlodi                                         DEVICE_CHARACTERISTIC_TABLE_LOC3, BCR,
14244ba25376SJoe Komlodi                                         bcr);
14254ba25376SJoe Komlodi     s->regs[dev_index + 3] = FIELD_DP32(s->regs[dev_index + 3],
14264ba25376SJoe Komlodi                                         DEVICE_CHARACTERISTIC_TABLE_LOC4,
14274ba25376SJoe Komlodi                                         DEV_DYNAMIC_ADDR, addr);
14284ba25376SJoe Komlodi 
14294ba25376SJoe Komlodi     /* Increment PRESENT_DEV_CHAR_TABLE_INDEX. */
14304ba25376SJoe Komlodi     uint8_t idx = ARRAY_FIELD_EX32(s->regs, DEV_CHAR_TABLE_POINTER,
14314ba25376SJoe Komlodi                      PRESENT_DEV_CHAR_TABLE_INDEX);
14324ba25376SJoe Komlodi     /* Increment and rollover. */
14334ba25376SJoe Komlodi     idx++;
14344ba25376SJoe Komlodi     if (idx >= ARRAY_FIELD_EX32(s->regs, DEV_CHAR_TABLE_POINTER,
14354ba25376SJoe Komlodi                                DEV_CHAR_TABLE_DEPTH) / 4) {
14364ba25376SJoe Komlodi         idx = 0;
14374ba25376SJoe Komlodi     }
14384ba25376SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, DEV_CHAR_TABLE_POINTER,
14394ba25376SJoe Komlodi                      PRESENT_DEV_CHAR_TABLE_INDEX, idx);
14404ba25376SJoe Komlodi }
14414ba25376SJoe Komlodi 
14424ba25376SJoe Komlodi static void aspeed_i3c_device_addr_assign_cmd(AspeedI3CDevice *s,
14434ba25376SJoe Komlodi                                               AspeedI3CAddrAssignCmd cmd)
14444ba25376SJoe Komlodi {
14454ba25376SJoe Komlodi     uint8_t i = 0;
14464ba25376SJoe Komlodi     uint8_t err = ASPEED_I3C_RESP_QUEUE_ERR_NONE;
14474ba25376SJoe Komlodi 
14484ba25376SJoe Komlodi     if (!aspeed_i3c_device_has_entdaa(s)) {
14494ba25376SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: ENTDAA is not supported\n",
14504ba25376SJoe Komlodi                       object_get_canonical_path(OBJECT(s)));
14514ba25376SJoe Komlodi         return;
14524ba25376SJoe Komlodi     }
14534ba25376SJoe Komlodi 
14544ba25376SJoe Komlodi     /* Tell everyone to ENTDAA. If these error, no one is on the bus. */
14554ba25376SJoe Komlodi     if (aspeed_i3c_device_send_start(s, I3C_BROADCAST, /*is_recv=*/false,
14564ba25376SJoe Komlodi                                      /*is_i2c=*/false)) {
14574ba25376SJoe Komlodi         err = ASPEED_I3C_RESP_QUEUE_ERR_BROADCAST_NACK;
14584ba25376SJoe Komlodi         goto transfer_done;
14594ba25376SJoe Komlodi     }
14604ba25376SJoe Komlodi     if (aspeed_i3c_device_send_byte(s, cmd.cmd, /*is_i2c=*/false)) {
14614ba25376SJoe Komlodi         err = ASPEED_I3C_RESP_QUEUE_ERR_BROADCAST_NACK;
14624ba25376SJoe Komlodi         goto transfer_done;
14634ba25376SJoe Komlodi     }
14644ba25376SJoe Komlodi 
14654ba25376SJoe Komlodi     /* Go through each device in the table and assign it an address. */
14664ba25376SJoe Komlodi     for (i = 0; i < cmd.dev_count; i++) {
14674ba25376SJoe Komlodi         uint8_t addr = aspeed_i3c_device_target_addr(s, cmd.dev_index + i);
14684ba25376SJoe Komlodi         union {
14694ba25376SJoe Komlodi             uint64_t pid:48;
14704ba25376SJoe Komlodi             uint8_t bcr;
14714ba25376SJoe Komlodi             uint8_t dcr;
14724ba25376SJoe Komlodi             uint32_t w[2];
14734ba25376SJoe Komlodi             uint8_t b[8];
14744ba25376SJoe Komlodi         } target_info;
14754ba25376SJoe Komlodi 
14764ba25376SJoe Komlodi         /* If this fails, there was no one left to ENTDAA. */
14774ba25376SJoe Komlodi         if (aspeed_i3c_device_send_start(s, I3C_BROADCAST, /*is_recv=*/false,
14784ba25376SJoe Komlodi                                          /*is_i2c=*/false)) {
14794ba25376SJoe Komlodi             err = ASPEED_I3C_RESP_QUEUE_ERR_BROADCAST_NACK;
14804ba25376SJoe Komlodi             break;
14814ba25376SJoe Komlodi         }
14824ba25376SJoe Komlodi 
14834ba25376SJoe Komlodi         /*
14844ba25376SJoe Komlodi          * In ENTDAA, we read 8 bytes from the target, which will be the
14854ba25376SJoe Komlodi          * target's PID, BCR, and DCR. After that, we send it the dynamic
14864ba25376SJoe Komlodi          * address.
14874ba25376SJoe Komlodi          * Don't bother checking the number of bytes received, it must send 8
14884ba25376SJoe Komlodi          * bytes during ENTDAA.
14894ba25376SJoe Komlodi          */
14904ba25376SJoe Komlodi         uint32_t num_read;
14914ba25376SJoe Komlodi         if (aspeed_i3c_device_recv_data(s, /*is_i2c=*/false, target_info.b,
14924ba25376SJoe Komlodi                                         I3C_ENTDAA_SIZE, &num_read)) {
14934ba25376SJoe Komlodi             qemu_log_mask(LOG_GUEST_ERROR, "%s: Target NACKed ENTDAA CCC\n",
14944ba25376SJoe Komlodi                           object_get_canonical_path(OBJECT(s)));
14954ba25376SJoe Komlodi             err = ASPEED_I3C_RESP_QUEUE_ERR_DAA_NACK;
14964ba25376SJoe Komlodi             goto transfer_done;
14974ba25376SJoe Komlodi         }
14984ba25376SJoe Komlodi         if (aspeed_i3c_device_send_byte(s, addr, /*is_i2c=*/false)) {
14994ba25376SJoe Komlodi             qemu_log_mask(LOG_GUEST_ERROR, "%s: Target NACKed addr 0x%.2x "
15004ba25376SJoe Komlodi                           "during ENTDAA\n",
15014ba25376SJoe Komlodi                           object_get_canonical_path(OBJECT(s)), addr);
15024ba25376SJoe Komlodi             err = ASPEED_I3C_RESP_QUEUE_ERR_DAA_NACK;
15034ba25376SJoe Komlodi             break;
15044ba25376SJoe Komlodi         }
15054ba25376SJoe Komlodi         aspeed_i3c_device_update_char_table(s, cmd.dev_index + i,
15064ba25376SJoe Komlodi                                             target_info.pid, target_info.bcr,
15074ba25376SJoe Komlodi                                             target_info.dcr, addr);
15084ba25376SJoe Komlodi 
15094ba25376SJoe Komlodi         /* Push the PID, BCR, and DCR to the RX queue. */
15104ba25376SJoe Komlodi         aspeed_i3c_device_push_rx(s, target_info.w[0]);
15114ba25376SJoe Komlodi         aspeed_i3c_device_push_rx(s, target_info.w[1]);
15124ba25376SJoe Komlodi     }
15134ba25376SJoe Komlodi 
15144ba25376SJoe Komlodi transfer_done:
15154ba25376SJoe Komlodi     /* Do we send a STOP? */
15164ba25376SJoe Komlodi     if (cmd.toc) {
15174ba25376SJoe Komlodi         aspeed_i3c_device_end_transfer(s, /*is_i2c=*/false);
15184ba25376SJoe Komlodi     }
15194ba25376SJoe Komlodi     /*
15204ba25376SJoe Komlodi      * For addr assign commands, the length field is the number of devices
15214ba25376SJoe Komlodi      * left to assign. CCCT is always 0 in controller mode.
15224ba25376SJoe Komlodi      */
15234ba25376SJoe Komlodi     if (cmd.roc) {
15244ba25376SJoe Komlodi         aspeed_i3c_device_resp_queue_push(s, err, cmd.tid, /*ccc_type=*/0,
15254ba25376SJoe Komlodi                                          cmd.dev_count - i);
15264ba25376SJoe Komlodi     }
15274ba25376SJoe Komlodi }
15284ba25376SJoe Komlodi 
15294ba25376SJoe Komlodi static uint32_t aspeed_i3c_device_cmd_queue_pop(AspeedI3CDevice *s)
15304ba25376SJoe Komlodi {
15314ba25376SJoe Komlodi     if (fifo32_is_empty(&s->cmd_queue)) {
15324ba25376SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: Tried to dequeue command queue "
15334ba25376SJoe Komlodi                       "when it was empty\n",
15344ba25376SJoe Komlodi                       object_get_canonical_path(OBJECT(s)));
15354ba25376SJoe Komlodi         return 0;
15364ba25376SJoe Komlodi     }
15374ba25376SJoe Komlodi     uint32_t val = fifo32_pop(&s->cmd_queue);
15384ba25376SJoe Komlodi 
15394ba25376SJoe Komlodi     uint8_t empty_threshold = ARRAY_FIELD_EX32(s->regs, QUEUE_THLD_CTRL,
15404ba25376SJoe Komlodi                                                CMD_BUF_EMPTY_THLD);
15414ba25376SJoe Komlodi     uint8_t cmd_queue_empty_loc = ARRAY_FIELD_EX32(s->regs,
15424ba25376SJoe Komlodi                                                    QUEUE_STATUS_LEVEL,
15434ba25376SJoe Komlodi                                                    CMD_QUEUE_EMPTY_LOC);
15444ba25376SJoe Komlodi     cmd_queue_empty_loc++;
15454ba25376SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, QUEUE_STATUS_LEVEL, CMD_QUEUE_EMPTY_LOC,
15464ba25376SJoe Komlodi                      cmd_queue_empty_loc);
15474ba25376SJoe Komlodi     if (cmd_queue_empty_loc >= empty_threshold) {
15484ba25376SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, INTR_STATUS, CMD_QUEUE_RDY, 1);
15494ba25376SJoe Komlodi         aspeed_i3c_device_update_irq(s);
15504ba25376SJoe Komlodi     }
15514ba25376SJoe Komlodi 
15524ba25376SJoe Komlodi     return val;
15534ba25376SJoe Komlodi }
15544ba25376SJoe Komlodi 
15554ba25376SJoe Komlodi static void aspeed_i3c_device_cmd_queue_execute(AspeedI3CDevice *s)
15564ba25376SJoe Komlodi {
15574ba25376SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS,
15584ba25376SJoe Komlodi                      ASPEED_I3C_TRANSFER_STATE_IDLE);
15594ba25376SJoe Komlodi     if (!aspeed_i3c_device_can_transmit(s)) {
15604ba25376SJoe Komlodi         return;
15614ba25376SJoe Komlodi     }
15624ba25376SJoe Komlodi 
15634ba25376SJoe Komlodi     /*
15644ba25376SJoe Komlodi      * We only start executing when a command is passed into the FIFO.
15654ba25376SJoe Komlodi      * We expect there to be a multiple of 2 items in the queue. The first item
15664ba25376SJoe Komlodi      * should be an argument to a command, and the command should be the second
15674ba25376SJoe Komlodi      * item.
15684ba25376SJoe Komlodi      */
15694ba25376SJoe Komlodi     if (fifo32_num_used(&s->cmd_queue) & 1) {
15704ba25376SJoe Komlodi         return;
15714ba25376SJoe Komlodi     }
15724ba25376SJoe Komlodi 
15734ba25376SJoe Komlodi     while (!fifo32_is_empty(&s->cmd_queue)) {
15744ba25376SJoe Komlodi         AspeedI3CCmdQueueData arg;
15754ba25376SJoe Komlodi         arg.word = aspeed_i3c_device_cmd_queue_pop(s);
15764ba25376SJoe Komlodi         AspeedI3CCmdQueueData cmd;
15774ba25376SJoe Komlodi         cmd.word = aspeed_i3c_device_cmd_queue_pop(s);
15784ba25376SJoe Komlodi         trace_aspeed_i3c_device_cmd_queue_execute(s->id, cmd.word, arg.word);
15794ba25376SJoe Komlodi 
15804ba25376SJoe Komlodi         uint8_t cmd_attr = FIELD_EX32(cmd.word, COMMAND_QUEUE_PORT, CMD_ATTR);
15814ba25376SJoe Komlodi         switch (cmd_attr) {
15824ba25376SJoe Komlodi         case ASPEED_I3C_CMD_ATTR_TRANSFER_CMD:
15834ba25376SJoe Komlodi             aspeed_i3c_device_transfer_cmd(s, cmd.transfer_cmd, arg);
15844ba25376SJoe Komlodi             break;
15854ba25376SJoe Komlodi         case ASPEED_I3C_CMD_ATTR_ADDR_ASSIGN_CMD:
15864ba25376SJoe Komlodi             /* Arg is discarded for addr assign commands. */
15874ba25376SJoe Komlodi             aspeed_i3c_device_addr_assign_cmd(s, cmd.addr_assign_cmd);
15884ba25376SJoe Komlodi             break;
15894ba25376SJoe Komlodi         case ASPEED_I3C_CMD_ATTR_TRANSFER_ARG:
15904ba25376SJoe Komlodi         case ASPEED_I3C_CMD_ATTR_SHORT_DATA_ARG:
15914ba25376SJoe Komlodi             qemu_log_mask(LOG_GUEST_ERROR, "%s: Command queue received argument"
15924ba25376SJoe Komlodi                           " packet when it expected a command packet\n",
15934ba25376SJoe Komlodi                           object_get_canonical_path(OBJECT(s)));
15944ba25376SJoe Komlodi             break;
15954ba25376SJoe Komlodi         default:
15964ba25376SJoe Komlodi             /*
15974ba25376SJoe Komlodi              * The caller's check before queueing an item should prevent this
15984ba25376SJoe Komlodi              * from happening.
15994ba25376SJoe Komlodi              */
16004ba25376SJoe Komlodi             g_assert_not_reached();
16014ba25376SJoe Komlodi             break;
16024ba25376SJoe Komlodi         }
16034ba25376SJoe Komlodi     }
16044ba25376SJoe Komlodi }
16054ba25376SJoe Komlodi 
16064ba25376SJoe Komlodi static void aspeed_i3c_device_cmd_queue_push(AspeedI3CDevice *s, uint32_t val)
16074ba25376SJoe Komlodi {
16084ba25376SJoe Komlodi     if (fifo32_is_full(&s->cmd_queue)) {
16094ba25376SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: Command queue received packet when "
16104ba25376SJoe Komlodi                       "already full\n", object_get_canonical_path(OBJECT(s)));
16114ba25376SJoe Komlodi         return;
16124ba25376SJoe Komlodi     }
16134ba25376SJoe Komlodi     trace_aspeed_i3c_device_cmd_queue_push(s->id, val);
16144ba25376SJoe Komlodi     fifo32_push(&s->cmd_queue, val);
16154ba25376SJoe Komlodi 
16164ba25376SJoe Komlodi     uint8_t empty_threshold = ARRAY_FIELD_EX32(s->regs, QUEUE_THLD_CTRL,
16174ba25376SJoe Komlodi                                                CMD_BUF_EMPTY_THLD);
16184ba25376SJoe Komlodi     uint8_t cmd_queue_empty_loc = ARRAY_FIELD_EX32(s->regs,
16194ba25376SJoe Komlodi                                                    QUEUE_STATUS_LEVEL,
16204ba25376SJoe Komlodi                                                    CMD_QUEUE_EMPTY_LOC);
16214ba25376SJoe Komlodi     if (cmd_queue_empty_loc) {
16224ba25376SJoe Komlodi         cmd_queue_empty_loc--;
16234ba25376SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, QUEUE_STATUS_LEVEL, CMD_QUEUE_EMPTY_LOC,
16244ba25376SJoe Komlodi                          cmd_queue_empty_loc);
16254ba25376SJoe Komlodi     }
16264ba25376SJoe Komlodi     if (cmd_queue_empty_loc < empty_threshold) {
16274ba25376SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, INTR_STATUS, CMD_QUEUE_RDY, 0);
16284ba25376SJoe Komlodi         aspeed_i3c_device_update_irq(s);
16294ba25376SJoe Komlodi     }
16304ba25376SJoe Komlodi }
16314ba25376SJoe Komlodi 
16324ba25376SJoe Komlodi static void aspeed_i3c_device_cmd_queue_port_w(AspeedI3CDevice *s, uint32_t val)
16334ba25376SJoe Komlodi {
16344ba25376SJoe Komlodi     uint8_t cmd_attr = FIELD_EX32(val, COMMAND_QUEUE_PORT, CMD_ATTR);
16354ba25376SJoe Komlodi 
16364ba25376SJoe Komlodi     switch (cmd_attr) {
16374ba25376SJoe Komlodi     /* If a command is received we can start executing it. */
16384ba25376SJoe Komlodi     case ASPEED_I3C_CMD_ATTR_TRANSFER_CMD:
16394ba25376SJoe Komlodi     case ASPEED_I3C_CMD_ATTR_ADDR_ASSIGN_CMD:
16404ba25376SJoe Komlodi         aspeed_i3c_device_cmd_queue_push(s, val);
16414ba25376SJoe Komlodi         aspeed_i3c_device_cmd_queue_execute(s);
16424ba25376SJoe Komlodi         break;
16434ba25376SJoe Komlodi     /* If we get an argument just push it. */
16444ba25376SJoe Komlodi     case ASPEED_I3C_CMD_ATTR_TRANSFER_ARG:
16454ba25376SJoe Komlodi     case ASPEED_I3C_CMD_ATTR_SHORT_DATA_ARG:
16464ba25376SJoe Komlodi         aspeed_i3c_device_cmd_queue_push(s, val);
16474ba25376SJoe Komlodi         break;
16484ba25376SJoe Komlodi     default:
16494ba25376SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: Command queue received packet with "
16504ba25376SJoe Komlodi                       "unknown cmd attr 0x%x\n",
16514ba25376SJoe Komlodi                       object_get_canonical_path(OBJECT(s)), cmd_attr);
16524ba25376SJoe Komlodi         break;
16534ba25376SJoe Komlodi     }
16544ba25376SJoe Komlodi }
16554ba25376SJoe Komlodi 
16567d87775fSJoe Komlodi static void aspeed_i3c_device_write(void *opaque, hwaddr offset,
16577d87775fSJoe Komlodi                                     uint64_t value, unsigned size)
16587d87775fSJoe Komlodi {
16597d87775fSJoe Komlodi     AspeedI3CDevice *s = ASPEED_I3C_DEVICE(opaque);
16607d87775fSJoe Komlodi     uint32_t addr = offset >> 2;
166197fb7498SJoe Komlodi     uint32_t val32 = (uint32_t)value;
16627d87775fSJoe Komlodi 
16637d87775fSJoe Komlodi     trace_aspeed_i3c_device_write(s->id, offset, value);
16647d87775fSJoe Komlodi 
166597fb7498SJoe Komlodi     val32 &= ~ast2600_i3c_device_ro[addr];
16667d87775fSJoe Komlodi     switch (addr) {
16677d87775fSJoe Komlodi     case R_HW_CAPABILITY:
16687d87775fSJoe Komlodi     case R_RESPONSE_QUEUE_PORT:
16697d87775fSJoe Komlodi     case R_IBI_QUEUE_DATA:
16707d87775fSJoe Komlodi     case R_QUEUE_STATUS_LEVEL:
16717d87775fSJoe Komlodi     case R_PRESENT_STATE:
16727d87775fSJoe Komlodi     case R_CCC_DEVICE_STATUS:
16737d87775fSJoe Komlodi     case R_DEVICE_ADDR_TABLE_POINTER:
16747d87775fSJoe Komlodi     case R_VENDOR_SPECIFIC_REG_POINTER:
16757d87775fSJoe Komlodi     case R_SLV_CHAR_CTRL:
16767d87775fSJoe Komlodi     case R_SLV_MAX_LEN:
16777d87775fSJoe Komlodi     case R_MAX_READ_TURNAROUND:
16787d87775fSJoe Komlodi     case R_I3C_VER_ID:
16797d87775fSJoe Komlodi     case R_I3C_VER_TYPE:
16807d87775fSJoe Komlodi     case R_EXTENDED_CAPABILITY:
16817d87775fSJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR,
16827d87775fSJoe Komlodi                       "%s: write to readonly register[0x%02" HWADDR_PRIx
16837d87775fSJoe Komlodi                       "] = 0x%08" PRIx64 "\n",
16847d87775fSJoe Komlodi                       __func__, offset, value);
16857d87775fSJoe Komlodi         break;
1686*eee51e8fSJoe Komlodi     case R_DEVICE_CTRL:
1687*eee51e8fSJoe Komlodi         aspeed_i3c_device_ctrl_w(s, val32);
1688*eee51e8fSJoe Komlodi         break;
16897d87775fSJoe Komlodi     case R_RX_TX_DATA_PORT:
16904ba25376SJoe Komlodi         aspeed_i3c_device_push_tx(s, val32);
16914ba25376SJoe Komlodi         break;
16924ba25376SJoe Komlodi     case R_COMMAND_QUEUE_PORT:
16934ba25376SJoe Komlodi         aspeed_i3c_device_cmd_queue_port_w(s, val32);
16947d87775fSJoe Komlodi         break;
16957d87775fSJoe Komlodi     case R_RESET_CTRL:
16967d87775fSJoe Komlodi         break;
16973816bedaSJoe Komlodi     case R_INTR_STATUS:
16983816bedaSJoe Komlodi         aspeed_i3c_device_intr_status_w(s, val32);
16993816bedaSJoe Komlodi         break;
17003816bedaSJoe Komlodi     case R_INTR_STATUS_EN:
17013816bedaSJoe Komlodi         aspeed_i3c_device_intr_status_en_w(s, val32);
17023816bedaSJoe Komlodi         break;
17033816bedaSJoe Komlodi     case R_INTR_SIGNAL_EN:
17043816bedaSJoe Komlodi         aspeed_i3c_device_intr_signal_en_w(s, val32);
17053816bedaSJoe Komlodi         break;
17063816bedaSJoe Komlodi     case R_INTR_FORCE:
17073816bedaSJoe Komlodi         aspeed_i3c_device_intr_force_w(s, val32);
17083816bedaSJoe Komlodi         break;
17097d87775fSJoe Komlodi     default:
171097fb7498SJoe Komlodi         s->regs[addr] = val32;
17117d87775fSJoe Komlodi         break;
17127d87775fSJoe Komlodi     }
17137d87775fSJoe Komlodi }
17147d87775fSJoe Komlodi 
17157d87775fSJoe Komlodi static const VMStateDescription aspeed_i3c_device_vmstate = {
17167d87775fSJoe Komlodi     .name = TYPE_ASPEED_I3C,
17177d87775fSJoe Komlodi     .version_id = 1,
17187d87775fSJoe Komlodi     .minimum_version_id = 1,
17197d87775fSJoe Komlodi     .fields = (const VMStateField[]){
17207d87775fSJoe Komlodi         VMSTATE_UINT32_ARRAY(regs, AspeedI3CDevice, ASPEED_I3C_DEVICE_NR_REGS),
17217d87775fSJoe Komlodi         VMSTATE_END_OF_LIST(),
17227d87775fSJoe Komlodi     }
17237d87775fSJoe Komlodi };
17247d87775fSJoe Komlodi 
17257d87775fSJoe Komlodi static const MemoryRegionOps aspeed_i3c_device_ops = {
17267d87775fSJoe Komlodi     .read = aspeed_i3c_device_read,
17277d87775fSJoe Komlodi     .write = aspeed_i3c_device_write,
17287d87775fSJoe Komlodi     .endianness = DEVICE_LITTLE_ENDIAN,
17297d87775fSJoe Komlodi };
17307d87775fSJoe Komlodi 
17317d87775fSJoe Komlodi static void aspeed_i3c_device_reset(DeviceState *dev)
17327d87775fSJoe Komlodi {
17337d87775fSJoe Komlodi     AspeedI3CDevice *s = ASPEED_I3C_DEVICE(dev);
17347d87775fSJoe Komlodi 
17357d87775fSJoe Komlodi     memcpy(s->regs, ast2600_i3c_device_resets, sizeof(s->regs));
17367d87775fSJoe Komlodi }
17377d87775fSJoe Komlodi 
17387d87775fSJoe Komlodi static void aspeed_i3c_device_realize(DeviceState *dev, Error **errp)
17397d87775fSJoe Komlodi {
17407d87775fSJoe Komlodi     AspeedI3CDevice *s = ASPEED_I3C_DEVICE(dev);
17417d87775fSJoe Komlodi     g_autofree char *name = g_strdup_printf(TYPE_ASPEED_I3C_DEVICE ".%d",
17427d87775fSJoe Komlodi                                             s->id);
17437d87775fSJoe Komlodi 
17447d87775fSJoe Komlodi     sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq);
17457d87775fSJoe Komlodi 
17467d87775fSJoe Komlodi     memory_region_init_io(&s->mr, OBJECT(s), &aspeed_i3c_device_ops,
17477d87775fSJoe Komlodi                           s, name, ASPEED_I3C_DEVICE_NR_REGS << 2);
17484ba25376SJoe Komlodi 
17494ba25376SJoe Komlodi     fifo32_create(&s->cmd_queue, ASPEED_I3C_CMD_QUEUE_CAPACITY);
17504ba25376SJoe Komlodi     fifo32_create(&s->resp_queue, ASPEED_I3C_RESP_QUEUE_CAPACITY);
17514ba25376SJoe Komlodi     fifo32_create(&s->tx_queue, ASPEED_I3C_TX_QUEUE_CAPACITY);
17524ba25376SJoe Komlodi     fifo32_create(&s->rx_queue, ASPEED_I3C_RX_QUEUE_CAPACITY);
175393ec6949SJoe Komlodi     fifo32_create(&s->ibi_queue, ASPEED_I3C_IBI_QUEUE_CAPACITY);
175493ec6949SJoe Komlodi     /* Arbitrarily large enough to not be an issue. */
175593ec6949SJoe Komlodi     fifo8_create(&s->ibi_data.ibi_intermediate_queue,
175693ec6949SJoe Komlodi                   ASPEED_I3C_IBI_QUEUE_CAPACITY * 8);
17574ba25376SJoe Komlodi 
17584ba25376SJoe Komlodi     s->bus = i3c_init_bus(DEVICE(s), name);
175993ec6949SJoe Komlodi     I3CBusClass *bc = I3C_BUS_GET_CLASS(s->bus);
176093ec6949SJoe Komlodi     bc->ibi_handle = aspeed_i3c_device_ibi_handle;
176193ec6949SJoe Komlodi     bc->ibi_recv = aspeed_i3c_device_ibi_recv;
176293ec6949SJoe Komlodi     bc->ibi_finish = aspeed_i3c_device_ibi_finish;
17637d87775fSJoe Komlodi }
17647d87775fSJoe Komlodi 
17657d87775fSJoe Komlodi static uint64_t aspeed_i3c_read(void *opaque, hwaddr addr, unsigned int size)
17667d87775fSJoe Komlodi {
17677d87775fSJoe Komlodi     AspeedI3CState *s = ASPEED_I3C(opaque);
17687d87775fSJoe Komlodi     uint64_t val = 0;
17697d87775fSJoe Komlodi 
17707d87775fSJoe Komlodi     val = s->regs[addr >> 2];
17717d87775fSJoe Komlodi 
17727d87775fSJoe Komlodi     trace_aspeed_i3c_read(addr, val);
17737d87775fSJoe Komlodi 
17747d87775fSJoe Komlodi     return val;
17757d87775fSJoe Komlodi }
17767d87775fSJoe Komlodi 
17777d87775fSJoe Komlodi static void aspeed_i3c_write(void *opaque,
17787d87775fSJoe Komlodi                              hwaddr addr,
17797d87775fSJoe Komlodi                              uint64_t data,
17807d87775fSJoe Komlodi                              unsigned int size)
17817d87775fSJoe Komlodi {
17827d87775fSJoe Komlodi     AspeedI3CState *s = ASPEED_I3C(opaque);
17837d87775fSJoe Komlodi 
17847d87775fSJoe Komlodi     trace_aspeed_i3c_write(addr, data);
17857d87775fSJoe Komlodi 
17867d87775fSJoe Komlodi     addr >>= 2;
17877d87775fSJoe Komlodi 
1788a9d3f922SJoe Komlodi     data &= ~ast2600_i3c_controller_ro[addr];
17897d87775fSJoe Komlodi     /* I3C controller register */
17907d87775fSJoe Komlodi     switch (addr) {
17917d87775fSJoe Komlodi     case R_I3C1_REG1:
17927d87775fSJoe Komlodi     case R_I3C2_REG1:
17937d87775fSJoe Komlodi     case R_I3C3_REG1:
17947d87775fSJoe Komlodi     case R_I3C4_REG1:
17957d87775fSJoe Komlodi     case R_I3C5_REG1:
17967d87775fSJoe Komlodi     case R_I3C6_REG1:
17977d87775fSJoe Komlodi         if (data & R_I3C1_REG1_I2C_MODE_MASK) {
17987d87775fSJoe Komlodi             qemu_log_mask(LOG_UNIMP,
17997d87775fSJoe Komlodi                           "%s: Unsupported I2C mode [0x%08" HWADDR_PRIx
18007d87775fSJoe Komlodi                           "]=%08" PRIx64 "\n",
18017d87775fSJoe Komlodi                           __func__, addr << 2, data);
18027d87775fSJoe Komlodi             break;
18037d87775fSJoe Komlodi         }
18047d87775fSJoe Komlodi         if (data & R_I3C1_REG1_SA_EN_MASK) {
18057d87775fSJoe Komlodi             qemu_log_mask(LOG_UNIMP,
18067d87775fSJoe Komlodi                           "%s: Unsupported slave mode [%08" HWADDR_PRIx
18077d87775fSJoe Komlodi                           "]=0x%08" PRIx64 "\n",
18087d87775fSJoe Komlodi                           __func__, addr << 2, data);
18097d87775fSJoe Komlodi             break;
18107d87775fSJoe Komlodi         }
18117d87775fSJoe Komlodi         s->regs[addr] = data;
18127d87775fSJoe Komlodi         break;
18137d87775fSJoe Komlodi     default:
18147d87775fSJoe Komlodi         s->regs[addr] = data;
18157d87775fSJoe Komlodi         break;
18167d87775fSJoe Komlodi     }
18177d87775fSJoe Komlodi }
18187d87775fSJoe Komlodi 
18197d87775fSJoe Komlodi static const MemoryRegionOps aspeed_i3c_ops = {
18207d87775fSJoe Komlodi     .read = aspeed_i3c_read,
18217d87775fSJoe Komlodi     .write = aspeed_i3c_write,
18227d87775fSJoe Komlodi     .endianness = DEVICE_LITTLE_ENDIAN,
18237d87775fSJoe Komlodi     .valid = {
18247d87775fSJoe Komlodi         .min_access_size = 1,
18257d87775fSJoe Komlodi         .max_access_size = 4,
18267d87775fSJoe Komlodi     }
18277d87775fSJoe Komlodi };
18287d87775fSJoe Komlodi 
18297d87775fSJoe Komlodi static void aspeed_i3c_reset(DeviceState *dev)
18307d87775fSJoe Komlodi {
18317d87775fSJoe Komlodi     AspeedI3CState *s = ASPEED_I3C(dev);
18327d87775fSJoe Komlodi     memset(s->regs, 0, sizeof(s->regs));
18337d87775fSJoe Komlodi }
18347d87775fSJoe Komlodi 
18357d87775fSJoe Komlodi static void aspeed_i3c_instance_init(Object *obj)
18367d87775fSJoe Komlodi {
18377d87775fSJoe Komlodi     AspeedI3CState *s = ASPEED_I3C(obj);
18387d87775fSJoe Komlodi     int i;
18397d87775fSJoe Komlodi 
18407d87775fSJoe Komlodi     for (i = 0; i < ASPEED_I3C_NR_DEVICES; ++i) {
18417d87775fSJoe Komlodi         object_initialize_child(obj, "device[*]", &s->devices[i],
18427d87775fSJoe Komlodi                 TYPE_ASPEED_I3C_DEVICE);
18437d87775fSJoe Komlodi     }
18447d87775fSJoe Komlodi }
18457d87775fSJoe Komlodi 
18467d87775fSJoe Komlodi static void aspeed_i3c_realize(DeviceState *dev, Error **errp)
18477d87775fSJoe Komlodi {
18487d87775fSJoe Komlodi     int i;
18497d87775fSJoe Komlodi     AspeedI3CState *s = ASPEED_I3C(dev);
18507d87775fSJoe Komlodi     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
18517d87775fSJoe Komlodi 
18527d87775fSJoe Komlodi     memory_region_init(&s->iomem_container, OBJECT(s),
18537d87775fSJoe Komlodi             TYPE_ASPEED_I3C ".container", 0x8000);
18547d87775fSJoe Komlodi 
18557d87775fSJoe Komlodi     sysbus_init_mmio(sbd, &s->iomem_container);
18567d87775fSJoe Komlodi 
18577d87775fSJoe Komlodi     memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_i3c_ops, s,
18587d87775fSJoe Komlodi             TYPE_ASPEED_I3C ".regs", ASPEED_I3C_NR_REGS << 2);
18597d87775fSJoe Komlodi 
18607d87775fSJoe Komlodi     memory_region_add_subregion(&s->iomem_container, 0x0, &s->iomem);
18617d87775fSJoe Komlodi 
18627d87775fSJoe Komlodi     for (i = 0; i < ASPEED_I3C_NR_DEVICES; ++i) {
18637d87775fSJoe Komlodi         Object *i3c_dev = OBJECT(&s->devices[i]);
18647d87775fSJoe Komlodi 
18657d87775fSJoe Komlodi         if (!object_property_set_uint(i3c_dev, "device-id", i, errp)) {
18667d87775fSJoe Komlodi             return;
18677d87775fSJoe Komlodi         }
18687d87775fSJoe Komlodi 
18697d87775fSJoe Komlodi         if (!sysbus_realize(SYS_BUS_DEVICE(i3c_dev), errp)) {
18707d87775fSJoe Komlodi             return;
18717d87775fSJoe Komlodi         }
18727d87775fSJoe Komlodi 
18737d87775fSJoe Komlodi         /*
18747d87775fSJoe Komlodi          * Register Address of I3CX Device =
18757d87775fSJoe Komlodi          *     (Base Address of Global Register) + (Offset of I3CX) + Offset
18767d87775fSJoe Komlodi          * X = 0, 1, 2, 3, 4, 5
18777d87775fSJoe Komlodi          * Offset of I3C0 = 0x2000
18787d87775fSJoe Komlodi          * Offset of I3C1 = 0x3000
18797d87775fSJoe Komlodi          * Offset of I3C2 = 0x4000
18807d87775fSJoe Komlodi          * Offset of I3C3 = 0x5000
18817d87775fSJoe Komlodi          * Offset of I3C4 = 0x6000
18827d87775fSJoe Komlodi          * Offset of I3C5 = 0x7000
18837d87775fSJoe Komlodi          */
18847d87775fSJoe Komlodi         memory_region_add_subregion(&s->iomem_container,
18857d87775fSJoe Komlodi                 0x2000 + i * 0x1000, &s->devices[i].mr);
18867d87775fSJoe Komlodi     }
18877d87775fSJoe Komlodi 
18887d87775fSJoe Komlodi }
18897d87775fSJoe Komlodi 
18907d87775fSJoe Komlodi static Property aspeed_i3c_device_properties[] = {
18917d87775fSJoe Komlodi     DEFINE_PROP_UINT8("device-id", AspeedI3CDevice, id, 0),
18927d87775fSJoe Komlodi     DEFINE_PROP_END_OF_LIST(),
18937d87775fSJoe Komlodi };
18947d87775fSJoe Komlodi 
18957d87775fSJoe Komlodi static void aspeed_i3c_device_class_init(ObjectClass *klass, void *data)
18967d87775fSJoe Komlodi {
18977d87775fSJoe Komlodi     DeviceClass *dc = DEVICE_CLASS(klass);
18987d87775fSJoe Komlodi 
18997d87775fSJoe Komlodi     dc->desc = "Aspeed I3C Device";
19007d87775fSJoe Komlodi     dc->realize = aspeed_i3c_device_realize;
19017d87775fSJoe Komlodi     device_class_set_legacy_reset(dc, aspeed_i3c_device_reset);
19027d87775fSJoe Komlodi     device_class_set_props(dc, aspeed_i3c_device_properties);
19037d87775fSJoe Komlodi }
19047d87775fSJoe Komlodi 
19057d87775fSJoe Komlodi static const TypeInfo aspeed_i3c_device_info = {
19067d87775fSJoe Komlodi     .name = TYPE_ASPEED_I3C_DEVICE,
19077d87775fSJoe Komlodi     .parent = TYPE_SYS_BUS_DEVICE,
19087d87775fSJoe Komlodi     .instance_size = sizeof(AspeedI3CDevice),
19097d87775fSJoe Komlodi     .class_init = aspeed_i3c_device_class_init,
19107d87775fSJoe Komlodi };
19117d87775fSJoe Komlodi 
19127d87775fSJoe Komlodi static const VMStateDescription vmstate_aspeed_i3c = {
19137d87775fSJoe Komlodi     .name = TYPE_ASPEED_I3C,
19147d87775fSJoe Komlodi     .version_id = 1,
19157d87775fSJoe Komlodi     .minimum_version_id = 1,
19167d87775fSJoe Komlodi     .fields = (const VMStateField[]) {
19177d87775fSJoe Komlodi         VMSTATE_UINT32_ARRAY(regs, AspeedI3CState, ASPEED_I3C_NR_REGS),
19187d87775fSJoe Komlodi         VMSTATE_STRUCT_ARRAY(devices, AspeedI3CState, ASPEED_I3C_NR_DEVICES, 1,
19197d87775fSJoe Komlodi                              aspeed_i3c_device_vmstate, AspeedI3CDevice),
19207d87775fSJoe Komlodi         VMSTATE_END_OF_LIST(),
19217d87775fSJoe Komlodi     }
19227d87775fSJoe Komlodi };
19237d87775fSJoe Komlodi 
19247d87775fSJoe Komlodi static void aspeed_i3c_class_init(ObjectClass *klass, void *data)
19257d87775fSJoe Komlodi {
19267d87775fSJoe Komlodi     DeviceClass *dc = DEVICE_CLASS(klass);
19277d87775fSJoe Komlodi 
19287d87775fSJoe Komlodi     dc->realize = aspeed_i3c_realize;
19297d87775fSJoe Komlodi     device_class_set_legacy_reset(dc, aspeed_i3c_reset);
19307d87775fSJoe Komlodi     dc->desc = "Aspeed I3C Controller";
19317d87775fSJoe Komlodi     dc->vmsd = &vmstate_aspeed_i3c;
19327d87775fSJoe Komlodi }
19337d87775fSJoe Komlodi 
19347d87775fSJoe Komlodi static const TypeInfo aspeed_i3c_info = {
19357d87775fSJoe Komlodi     .name = TYPE_ASPEED_I3C,
19367d87775fSJoe Komlodi     .parent = TYPE_SYS_BUS_DEVICE,
19377d87775fSJoe Komlodi     .instance_init = aspeed_i3c_instance_init,
19387d87775fSJoe Komlodi     .instance_size = sizeof(AspeedI3CState),
19397d87775fSJoe Komlodi     .class_init = aspeed_i3c_class_init,
19407d87775fSJoe Komlodi };
19417d87775fSJoe Komlodi 
19427d87775fSJoe Komlodi static void aspeed_i3c_register_types(void)
19437d87775fSJoe Komlodi {
19447d87775fSJoe Komlodi     type_register_static(&aspeed_i3c_device_info);
19457d87775fSJoe Komlodi     type_register_static(&aspeed_i3c_info);
19467d87775fSJoe Komlodi }
19477d87775fSJoe Komlodi 
19487d87775fSJoe Komlodi type_init(aspeed_i3c_register_types);
1949