xref: /openbmc/qemu/hw/i3c/aspeed_i3c.c (revision 93ec69491130ed0a9daa6a9107bb3bbdfc7e9e23)
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 
23*93ec6949SJoe Komlodi /*
24*93ec6949SJoe Komlodi  * Disable event command values. sent along with a DISEC CCC to disable certain
25*93ec6949SJoe Komlodi  * events on targets.
26*93ec6949SJoe Komlodi  */
27*93ec6949SJoe Komlodi #define DISEC_HJ 0x08
28*93ec6949SJoe Komlodi #define DISEC_CR 0x02
29*93ec6949SJoe Komlodi #define DISEC_INT 0x01
30*93ec6949SJoe 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 
343a9d3f922SJoe Komlodi static const uint32_t ast2600_i3c_controller_ro[ASPEED_I3C_DEVICE_NR_REGS] = {
344a9d3f922SJoe Komlodi     [R_I3C1_REG0]                   = 0xfc000000,
345a9d3f922SJoe Komlodi     [R_I3C1_REG1]                   = 0xfff00000,
346a9d3f922SJoe Komlodi     [R_I3C2_REG0]                   = 0xfc000000,
347a9d3f922SJoe Komlodi     [R_I3C2_REG1]                   = 0xfff00000,
348a9d3f922SJoe Komlodi     [R_I3C3_REG0]                   = 0xfc000000,
349a9d3f922SJoe Komlodi     [R_I3C3_REG1]                   = 0xfff00000,
350a9d3f922SJoe Komlodi     [R_I3C4_REG0]                   = 0xfc000000,
351a9d3f922SJoe Komlodi     [R_I3C4_REG1]                   = 0xfff00000,
352a9d3f922SJoe Komlodi     [R_I3C5_REG0]                   = 0xfc000000,
353a9d3f922SJoe Komlodi     [R_I3C5_REG1]                   = 0xfff00000,
354a9d3f922SJoe Komlodi     [R_I3C6_REG0]                   = 0xfc000000,
355a9d3f922SJoe Komlodi     [R_I3C6_REG1]                   = 0xfff00000,
356a9d3f922SJoe Komlodi };
357a9d3f922SJoe Komlodi 
3587d87775fSJoe Komlodi static const uint32_t ast2600_i3c_device_resets[ASPEED_I3C_DEVICE_NR_REGS] = {
3597d87775fSJoe Komlodi     [R_HW_CAPABILITY]               = 0x000e00bf,
3607d87775fSJoe Komlodi     [R_QUEUE_THLD_CTRL]             = 0x01000101,
3611032d6d6SJoe Komlodi     [R_DATA_BUFFER_THLD_CTRL]       = 0x01010100,
3621032d6d6SJoe Komlodi     [R_SLV_EVENT_CTRL]              = 0x0000000b,
3631032d6d6SJoe Komlodi     [R_QUEUE_STATUS_LEVEL]          = 0x00000002,
3641032d6d6SJoe Komlodi     [R_DATA_BUFFER_STATUS_LEVEL]    = 0x00000010,
3651032d6d6SJoe Komlodi     [R_PRESENT_STATE]               = 0x00000003,
3667d87775fSJoe Komlodi     [R_I3C_VER_ID]                  = 0x3130302a,
3677d87775fSJoe Komlodi     [R_I3C_VER_TYPE]                = 0x6c633033,
3687d87775fSJoe Komlodi     [R_DEVICE_ADDR_TABLE_POINTER]   = 0x00080280,
3697d87775fSJoe Komlodi     [R_DEV_CHAR_TABLE_POINTER]      = 0x00020200,
3701032d6d6SJoe Komlodi     [R_SLV_CHAR_CTRL]               = 0x00010000,
3717d87775fSJoe Komlodi     [A_VENDOR_SPECIFIC_REG_POINTER] = 0x000000b0,
3727d87775fSJoe Komlodi     [R_SLV_MAX_LEN]                 = 0x00ff00ff,
3731032d6d6SJoe Komlodi     [R_SLV_TSX_SYMBL_TIMING]        = 0x0000003f,
3741032d6d6SJoe Komlodi     [R_SCL_I3C_OD_TIMING]           = 0x000a0010,
3751032d6d6SJoe Komlodi     [R_SCL_I3C_PP_TIMING]           = 0x000a000a,
3761032d6d6SJoe Komlodi     [R_SCL_I2C_FM_TIMING]           = 0x00100010,
3771032d6d6SJoe Komlodi     [R_SCL_I2C_FMP_TIMING]          = 0x00100010,
3781032d6d6SJoe Komlodi     [R_SCL_EXT_LCNT_TIMING]         = 0x20202020,
3791032d6d6SJoe Komlodi     [R_SCL_EXT_TERMN_LCNT_TIMING]   = 0x00300000,
3801032d6d6SJoe Komlodi     [R_BUS_FREE_TIMING]             = 0x00200020,
3811032d6d6SJoe Komlodi     [R_BUS_IDLE_TIMING]             = 0x00000020,
3821032d6d6SJoe Komlodi     [R_EXTENDED_CAPABILITY]         = 0x00000239,
3831032d6d6SJoe Komlodi     [R_SLAVE_CONFIG]                = 0x00000023,
3847d87775fSJoe Komlodi };
3857d87775fSJoe Komlodi 
386a9d3f922SJoe Komlodi static const uint32_t ast2600_i3c_device_ro[ASPEED_I3C_DEVICE_NR_REGS] = {
387a9d3f922SJoe Komlodi     [R_DEVICE_CTRL]                 = 0x04fffe00,
388a9d3f922SJoe Komlodi     [R_DEVICE_ADDR]                 = 0x7f807f80,
389a9d3f922SJoe Komlodi     [R_HW_CAPABILITY]               = 0xffffffff,
390a9d3f922SJoe Komlodi     [R_IBI_QUEUE_STATUS]            = 0xffffffff,
391a9d3f922SJoe Komlodi     [R_DATA_BUFFER_THLD_CTRL]       = 0xf8f8f8f8,
392a9d3f922SJoe Komlodi     [R_IBI_QUEUE_CTRL]              = 0xfffffff0,
393a9d3f922SJoe Komlodi     [R_RESET_CTRL]                  = 0xffffffc0,
394a9d3f922SJoe Komlodi     [R_SLV_EVENT_CTRL]              = 0xffffff3f,
395a9d3f922SJoe Komlodi     [R_INTR_STATUS]                 = 0xffff809f,
396a9d3f922SJoe Komlodi     [R_INTR_STATUS_EN]              = 0xffff8080,
397a9d3f922SJoe Komlodi     [R_INTR_SIGNAL_EN]              = 0xffff8080,
398a9d3f922SJoe Komlodi     [R_INTR_FORCE]                  = 0xffff8000,
399a9d3f922SJoe Komlodi     [R_QUEUE_STATUS_LEVEL]          = 0xffffffff,
400a9d3f922SJoe Komlodi     [R_DATA_BUFFER_STATUS_LEVEL]    = 0xffffffff,
401a9d3f922SJoe Komlodi     [R_PRESENT_STATE]               = 0xffffffff,
402a9d3f922SJoe Komlodi     [R_CCC_DEVICE_STATUS]           = 0xffffffff,
403a9d3f922SJoe Komlodi     [R_I3C_VER_ID]                  = 0xffffffff,
404a9d3f922SJoe Komlodi     [R_I3C_VER_TYPE]                = 0xffffffff,
405a9d3f922SJoe Komlodi     [R_DEVICE_ADDR_TABLE_POINTER]   = 0xffffffff,
406a9d3f922SJoe Komlodi     [R_DEV_CHAR_TABLE_POINTER]      = 0xffcbffff,
407a9d3f922SJoe Komlodi     [R_SLV_PID_VALUE]               = 0xffff0fff,
408a9d3f922SJoe Komlodi     [R_SLV_CHAR_CTRL]               = 0xffffffff,
409a9d3f922SJoe Komlodi     [A_VENDOR_SPECIFIC_REG_POINTER] = 0xffffffff,
410a9d3f922SJoe Komlodi     [R_SLV_MAX_LEN]                 = 0xffffffff,
411a9d3f922SJoe Komlodi     [R_MAX_READ_TURNAROUND]         = 0xffffffff,
412a9d3f922SJoe Komlodi     [R_MAX_DATA_SPEED]              = 0xffffffff,
413a9d3f922SJoe Komlodi     [R_SLV_INTR_REQ]                = 0xfffffff0,
414a9d3f922SJoe Komlodi     [R_SLV_TSX_SYMBL_TIMING]        = 0xffffffc0,
415a9d3f922SJoe Komlodi     [R_DEVICE_CTRL_EXTENDED]        = 0xfffffff8,
416a9d3f922SJoe Komlodi     [R_SCL_I3C_OD_TIMING]           = 0xff00ff00,
417a9d3f922SJoe Komlodi     [R_SCL_I3C_PP_TIMING]           = 0xff00ff00,
418a9d3f922SJoe Komlodi     [R_SCL_I2C_FMP_TIMING]          = 0xff000000,
419a9d3f922SJoe Komlodi     [R_SCL_EXT_TERMN_LCNT_TIMING]   = 0x0000fff0,
420a9d3f922SJoe Komlodi     [R_BUS_IDLE_TIMING]             = 0xfff00000,
421a9d3f922SJoe Komlodi     [R_EXTENDED_CAPABILITY]         = 0xffffffff,
422a9d3f922SJoe Komlodi     [R_SLAVE_CONFIG]                = 0xffffffff,
423a9d3f922SJoe Komlodi };
424a9d3f922SJoe Komlodi 
4254ba25376SJoe Komlodi static inline bool aspeed_i3c_device_has_entdaa(AspeedI3CDevice *s)
4264ba25376SJoe Komlodi {
4274ba25376SJoe Komlodi     return ARRAY_FIELD_EX32(s->regs, HW_CAPABILITY, ENTDAA);
4284ba25376SJoe Komlodi }
4294ba25376SJoe Komlodi 
4304ba25376SJoe Komlodi static inline bool aspeed_i3c_device_has_hdr_ts(AspeedI3CDevice *s)
4314ba25376SJoe Komlodi {
4324ba25376SJoe Komlodi     return ARRAY_FIELD_EX32(s->regs, HW_CAPABILITY, HDR_TS);
4334ba25376SJoe Komlodi }
4344ba25376SJoe Komlodi 
4354ba25376SJoe Komlodi static inline bool aspeed_i3c_device_has_hdr_ddr(AspeedI3CDevice *s)
4364ba25376SJoe Komlodi {
4374ba25376SJoe Komlodi     return ARRAY_FIELD_EX32(s->regs, HW_CAPABILITY, HDR_DDR);
4384ba25376SJoe Komlodi }
4394ba25376SJoe Komlodi 
4404ba25376SJoe Komlodi static inline bool aspeed_i3c_device_can_transmit(AspeedI3CDevice *s)
4414ba25376SJoe Komlodi {
4424ba25376SJoe Komlodi     /*
4434ba25376SJoe Komlodi      * We can only transmit if we're enabled and the resume bit is cleared.
4444ba25376SJoe Komlodi      * The resume bit is set on a transaction error, and software must clear it.
4454ba25376SJoe Komlodi      */
4464ba25376SJoe Komlodi     return ARRAY_FIELD_EX32(s->regs, DEVICE_CTRL, I3C_EN) &&
4474ba25376SJoe Komlodi            !ARRAY_FIELD_EX32(s->regs, DEVICE_CTRL, I3C_RESUME);
4484ba25376SJoe Komlodi }
4494ba25376SJoe Komlodi 
4504ba25376SJoe Komlodi static inline uint8_t aspeed_i3c_device_fifo_threshold_from_reg(uint8_t regval)
4514ba25376SJoe Komlodi {
4524ba25376SJoe Komlodi     return regval = regval ? (2 << regval) : 1;
4534ba25376SJoe Komlodi }
4544ba25376SJoe Komlodi 
455*93ec6949SJoe Komlodi static inline uint8_t aspeed_i3c_device_ibi_slice_size(AspeedI3CDevice *s)
456*93ec6949SJoe Komlodi {
457*93ec6949SJoe Komlodi     uint8_t ibi_slice_size = ARRAY_FIELD_EX32(s->regs, QUEUE_THLD_CTRL,
458*93ec6949SJoe Komlodi                                               IBI_DATA_THLD);
459*93ec6949SJoe Komlodi     /* The minimum supported slice size is 4 bytes. */
460*93ec6949SJoe Komlodi     if (ibi_slice_size == 0) {
461*93ec6949SJoe Komlodi         ibi_slice_size = 1;
462*93ec6949SJoe Komlodi     }
463*93ec6949SJoe Komlodi     ibi_slice_size *= sizeof(uint32_t);
464*93ec6949SJoe Komlodi     /* maximum supported size is 63 bytes. */
465*93ec6949SJoe Komlodi     if (ibi_slice_size >= 64) {
466*93ec6949SJoe Komlodi         ibi_slice_size = 63;
467*93ec6949SJoe Komlodi     }
468*93ec6949SJoe Komlodi 
469*93ec6949SJoe Komlodi     return ibi_slice_size;
470*93ec6949SJoe Komlodi }
471*93ec6949SJoe Komlodi 
4723816bedaSJoe Komlodi static void aspeed_i3c_device_update_irq(AspeedI3CDevice *s)
4733816bedaSJoe Komlodi {
4743816bedaSJoe Komlodi     bool level = !!(s->regs[R_INTR_SIGNAL_EN] & s->regs[R_INTR_STATUS]);
4753816bedaSJoe Komlodi     qemu_set_irq(s->irq, level);
4763816bedaSJoe Komlodi }
4773816bedaSJoe Komlodi 
4784ba25376SJoe Komlodi static void aspeed_i3c_device_end_transfer(AspeedI3CDevice *s, bool is_i2c)
4794ba25376SJoe Komlodi {
4804ba25376SJoe Komlodi     if (is_i2c) {
4814ba25376SJoe Komlodi         legacy_i2c_end_transfer(s->bus);
4824ba25376SJoe Komlodi     } else {
4834ba25376SJoe Komlodi         i3c_end_transfer(s->bus);
4844ba25376SJoe Komlodi     }
4854ba25376SJoe Komlodi }
4864ba25376SJoe Komlodi 
4874ba25376SJoe Komlodi static int aspeed_i3c_device_send_start(AspeedI3CDevice *s, uint8_t addr,
4884ba25376SJoe Komlodi                                         bool is_recv, bool is_i2c)
4894ba25376SJoe Komlodi {
4904ba25376SJoe Komlodi     int ret;
4914ba25376SJoe Komlodi 
4924ba25376SJoe Komlodi     if (is_i2c) {
4934ba25376SJoe Komlodi         ret = legacy_i2c_start_transfer(s->bus, addr, is_recv);
4944ba25376SJoe Komlodi     } else {
4954ba25376SJoe Komlodi         ret = i3c_start_transfer(s->bus, addr, is_recv);
4964ba25376SJoe Komlodi     }
4974ba25376SJoe Komlodi     if (ret) {
4984ba25376SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: NACKed on TX with addr 0x%.2x\n",
4994ba25376SJoe Komlodi                       object_get_canonical_path(OBJECT(s)), addr);
5004ba25376SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS,
5014ba25376SJoe Komlodi                          ASPEED_I3C_TRANSFER_STATE_HALT);
5024ba25376SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_STATUS,
5034ba25376SJoe Komlodi                          ASPEED_I3C_TRANSFER_STATUS_HALT);
5044ba25376SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, INTR_STATUS, TRANSFER_ERR, 1);
5054ba25376SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, DEVICE_CTRL, I3C_RESUME, 1);
5064ba25376SJoe Komlodi     }
5074ba25376SJoe Komlodi 
5084ba25376SJoe Komlodi     return ret;
5094ba25376SJoe Komlodi }
5104ba25376SJoe Komlodi 
5114ba25376SJoe Komlodi static int aspeed_i3c_device_send(AspeedI3CDevice *s, const uint8_t *data,
5124ba25376SJoe Komlodi                                   uint32_t num_to_send, uint32_t *num_sent,
5134ba25376SJoe Komlodi                                   bool is_i2c)
5144ba25376SJoe Komlodi {
5154ba25376SJoe Komlodi     int ret;
5164ba25376SJoe Komlodi     uint32_t i;
5174ba25376SJoe Komlodi 
5184ba25376SJoe Komlodi     *num_sent = 0;
5194ba25376SJoe Komlodi     if (is_i2c) {
5204ba25376SJoe Komlodi         /* Legacy I2C must be byte-by-byte. */
5214ba25376SJoe Komlodi         for (i = 0; i < num_to_send; i++) {
5224ba25376SJoe Komlodi             ret = legacy_i2c_send(s->bus, data[i]);
5234ba25376SJoe Komlodi             if (ret) {
5244ba25376SJoe Komlodi                 break;
5254ba25376SJoe Komlodi             }
5264ba25376SJoe Komlodi             (*num_sent)++;
5274ba25376SJoe Komlodi         }
5284ba25376SJoe Komlodi     } else {
5294ba25376SJoe Komlodi         ret = i3c_send(s->bus, data, num_to_send, num_sent);
5304ba25376SJoe Komlodi     }
5314ba25376SJoe Komlodi     if (ret) {
5324ba25376SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: NACKed sending byte 0x%.2x\n",
5334ba25376SJoe Komlodi                       object_get_canonical_path(OBJECT(s)), data[*num_sent]);
5344ba25376SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS,
5354ba25376SJoe Komlodi                          ASPEED_I3C_TRANSFER_STATE_HALT);
5364ba25376SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_STATUS,
5374ba25376SJoe Komlodi                          ASPEED_I3C_TRANSFER_STATUS_HALT);
5384ba25376SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, INTR_STATUS, TRANSFER_ERR, 1);
5394ba25376SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, DEVICE_CTRL, I3C_RESUME, 1);
5404ba25376SJoe Komlodi     }
5414ba25376SJoe Komlodi 
5424ba25376SJoe Komlodi     trace_aspeed_i3c_device_send(s->id, *num_sent);
5434ba25376SJoe Komlodi 
5444ba25376SJoe Komlodi     return ret;
5454ba25376SJoe Komlodi }
5464ba25376SJoe Komlodi 
5474ba25376SJoe Komlodi static int aspeed_i3c_device_send_byte(AspeedI3CDevice *s, uint8_t byte,
5484ba25376SJoe Komlodi                                        bool is_i2c)
5494ba25376SJoe Komlodi {
5504ba25376SJoe Komlodi     /*
5514ba25376SJoe Komlodi      * Ignored, the caller will know if we sent 0 or 1 bytes depending on if
5524ba25376SJoe Komlodi      * we were ACKed/NACKed.
5534ba25376SJoe Komlodi      */
5544ba25376SJoe Komlodi     uint32_t num_sent;
5554ba25376SJoe Komlodi     return aspeed_i3c_device_send(s, &byte, 1, &num_sent, is_i2c);
5564ba25376SJoe Komlodi }
5574ba25376SJoe Komlodi 
5584ba25376SJoe Komlodi static int aspeed_i3c_device_recv_data(AspeedI3CDevice *s, bool is_i2c,
5594ba25376SJoe Komlodi                                        uint8_t *data, uint16_t num_to_read,
5604ba25376SJoe Komlodi                                        uint32_t *num_read)
5614ba25376SJoe Komlodi {
5624ba25376SJoe Komlodi     int ret;
5634ba25376SJoe Komlodi 
5644ba25376SJoe Komlodi     if (is_i2c) {
5654ba25376SJoe Komlodi         for (uint16_t i = 0; i < num_to_read; i++) {
5664ba25376SJoe Komlodi             data[i] = legacy_i2c_recv(s->bus);
5674ba25376SJoe Komlodi         }
5684ba25376SJoe Komlodi         /* I2C devices can neither NACK a read, nor end transfers early. */
5694ba25376SJoe Komlodi         *num_read = num_to_read;
5704ba25376SJoe Komlodi         trace_aspeed_i3c_device_recv_data(s->id, *num_read);
5714ba25376SJoe Komlodi         return 0;
5724ba25376SJoe Komlodi     }
5734ba25376SJoe Komlodi     /* I3C devices can NACK if the controller sends an unsupported CCC. */
5744ba25376SJoe Komlodi     ret = i3c_recv(s->bus, data, num_to_read, num_read);
5754ba25376SJoe Komlodi     if (ret) {
5764ba25376SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: NACKed receiving byte\n",
5774ba25376SJoe Komlodi                       object_get_canonical_path(OBJECT(s)));
5784ba25376SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS,
5794ba25376SJoe Komlodi                          ASPEED_I3C_TRANSFER_STATE_HALT);
5804ba25376SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_STATUS,
5814ba25376SJoe Komlodi                          ASPEED_I3C_TRANSFER_STATUS_HALT);
5824ba25376SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, INTR_STATUS, TRANSFER_ERR, 1);
5834ba25376SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, DEVICE_CTRL, I3C_RESUME, 1);
5844ba25376SJoe Komlodi     }
5854ba25376SJoe Komlodi 
5864ba25376SJoe Komlodi     trace_aspeed_i3c_device_recv_data(s->id, *num_read);
5874ba25376SJoe Komlodi 
5884ba25376SJoe Komlodi     return ret;
5894ba25376SJoe Komlodi }
5904ba25376SJoe Komlodi 
5914ba25376SJoe Komlodi static inline bool aspeed_i3c_device_target_is_i2c(AspeedI3CDevice *s,
5924ba25376SJoe Komlodi                                                    uint16_t offset)
5934ba25376SJoe Komlodi {
5944ba25376SJoe Komlodi     uint16_t dev_index = R_DEVICE_ADDR_TABLE_LOC1 + offset;
5954ba25376SJoe Komlodi     return FIELD_EX32(s->regs[dev_index], DEVICE_ADDR_TABLE_LOC1,
5964ba25376SJoe Komlodi                    LEGACY_I2C_DEVICE);
5974ba25376SJoe Komlodi }
5984ba25376SJoe Komlodi 
5994ba25376SJoe Komlodi static uint8_t aspeed_i3c_device_target_addr(AspeedI3CDevice *s,
6004ba25376SJoe Komlodi                                              uint16_t offset)
6014ba25376SJoe Komlodi {
6024ba25376SJoe Komlodi     if (offset > ASPEED_I3C_NR_DEVICES) {
6034ba25376SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: Device addr table offset %d out of "
6044ba25376SJoe Komlodi                       "bounds\n", object_get_canonical_path(OBJECT(s)), offset);
6054ba25376SJoe Komlodi         /* If we're out of bounds, return an address of 0. */
6064ba25376SJoe Komlodi         return 0;
6074ba25376SJoe Komlodi     }
6084ba25376SJoe Komlodi 
6094ba25376SJoe Komlodi     uint16_t dev_index = R_DEVICE_ADDR_TABLE_LOC1 + offset;
6104ba25376SJoe Komlodi     /* I2C devices use a static address. */
6114ba25376SJoe Komlodi     if (aspeed_i3c_device_target_is_i2c(s, offset)) {
6124ba25376SJoe Komlodi         return FIELD_EX32(s->regs[dev_index], DEVICE_ADDR_TABLE_LOC1,
6134ba25376SJoe Komlodi                           DEV_STATIC_ADDR);
6144ba25376SJoe Komlodi     }
6154ba25376SJoe Komlodi     return FIELD_EX32(s->regs[dev_index], DEVICE_ADDR_TABLE_LOC1,
6164ba25376SJoe Komlodi                       DEV_DYNAMIC_ADDR);
6174ba25376SJoe Komlodi }
6184ba25376SJoe Komlodi 
619*93ec6949SJoe Komlodi static int aspeed_i3c_device_addr_table_index_from_addr(AspeedI3CDevice *s,
620*93ec6949SJoe Komlodi                                                         uint8_t addr)
621*93ec6949SJoe Komlodi {
622*93ec6949SJoe Komlodi     uint8_t table_size = ARRAY_FIELD_EX32(s->regs, DEVICE_ADDR_TABLE_POINTER,
623*93ec6949SJoe Komlodi                                           DEPTH);
624*93ec6949SJoe Komlodi     for (uint8_t i = 0; i < table_size; i++) {
625*93ec6949SJoe Komlodi         if (aspeed_i3c_device_target_addr(s, i) == addr) {
626*93ec6949SJoe Komlodi             return i;
627*93ec6949SJoe Komlodi         }
628*93ec6949SJoe Komlodi     }
629*93ec6949SJoe Komlodi     return -1;
630*93ec6949SJoe Komlodi }
631*93ec6949SJoe Komlodi 
632*93ec6949SJoe Komlodi static void aspeed_i3c_device_send_disec(AspeedI3CDevice *s)
633*93ec6949SJoe Komlodi {
634*93ec6949SJoe Komlodi     uint8_t ccc = I3C_CCC_DISEC;
635*93ec6949SJoe Komlodi     if (s->ibi_data.send_direct_disec) {
636*93ec6949SJoe Komlodi         ccc = I3C_CCCD_DISEC;
637*93ec6949SJoe Komlodi     }
638*93ec6949SJoe Komlodi 
639*93ec6949SJoe Komlodi     aspeed_i3c_device_send_start(s, I3C_BROADCAST, /*is_recv=*/false,
640*93ec6949SJoe Komlodi                                  /*is_i2c=*/false);
641*93ec6949SJoe Komlodi     aspeed_i3c_device_send_byte(s, ccc, /*is_i2c=*/false);
642*93ec6949SJoe Komlodi     if (s->ibi_data.send_direct_disec) {
643*93ec6949SJoe Komlodi         aspeed_i3c_device_send_start(s, s->ibi_data.disec_addr,
644*93ec6949SJoe Komlodi                                      /*is_recv=*/false, /*is_i2c=*/false);
645*93ec6949SJoe Komlodi     }
646*93ec6949SJoe Komlodi     aspeed_i3c_device_send_byte(s, s->ibi_data.disec_byte, /*is_i2c=*/false);
647*93ec6949SJoe Komlodi }
648*93ec6949SJoe Komlodi 
649*93ec6949SJoe Komlodi static int aspeed_i3c_device_handle_hj(AspeedI3CDevice *s)
650*93ec6949SJoe Komlodi {
651*93ec6949SJoe Komlodi     if (ARRAY_FIELD_EX32(s->regs, IBI_QUEUE_CTRL, NOTIFY_REJECTED_HOT_JOIN)) {
652*93ec6949SJoe Komlodi         s->ibi_data.notify_ibi_nack = true;
653*93ec6949SJoe Komlodi     }
654*93ec6949SJoe Komlodi 
655*93ec6949SJoe Komlodi     bool nack_and_disable = ARRAY_FIELD_EX32(s->regs, DEVICE_CTRL,
656*93ec6949SJoe Komlodi                                              HOT_JOIN_ACK_NACK_CTRL);
657*93ec6949SJoe Komlodi     if (nack_and_disable) {
658*93ec6949SJoe Komlodi         s->ibi_data.ibi_queue_status = FIELD_DP32(s->ibi_data.ibi_queue_status,
659*93ec6949SJoe Komlodi                                                   IBI_QUEUE_STATUS,
660*93ec6949SJoe Komlodi                                                   IBI_STATUS, 1);
661*93ec6949SJoe Komlodi         s->ibi_data.ibi_nacked = true;
662*93ec6949SJoe Komlodi         s->ibi_data.disec_byte = DISEC_HJ;
663*93ec6949SJoe Komlodi         return -1;
664*93ec6949SJoe Komlodi     }
665*93ec6949SJoe Komlodi     return 0;
666*93ec6949SJoe Komlodi }
667*93ec6949SJoe Komlodi 
668*93ec6949SJoe Komlodi static int aspeed_i3c_device_handle_ctlr_req(AspeedI3CDevice *s, uint8_t addr)
669*93ec6949SJoe Komlodi {
670*93ec6949SJoe Komlodi     if (ARRAY_FIELD_EX32(s->regs, IBI_QUEUE_CTRL, NOTIFY_REJECTED_MASTER_REQ)) {
671*93ec6949SJoe Komlodi         s->ibi_data.notify_ibi_nack = true;
672*93ec6949SJoe Komlodi     }
673*93ec6949SJoe Komlodi 
674*93ec6949SJoe Komlodi     int table_offset = aspeed_i3c_device_addr_table_index_from_addr(s, addr);
675*93ec6949SJoe Komlodi     /* Doesn't exist in the table, NACK it, don't DISEC. */
676*93ec6949SJoe Komlodi     if (table_offset < 0) {
677*93ec6949SJoe Komlodi         return -1;
678*93ec6949SJoe Komlodi     }
679*93ec6949SJoe Komlodi 
680*93ec6949SJoe Komlodi     table_offset += R_DEVICE_ADDR_TABLE_LOC1;
681*93ec6949SJoe Komlodi     if (FIELD_EX32(s->regs[table_offset], DEVICE_ADDR_TABLE_LOC1, MR_REJECT)) {
682*93ec6949SJoe Komlodi         s->ibi_data.ibi_queue_status = FIELD_DP32(s->ibi_data.ibi_queue_status,
683*93ec6949SJoe Komlodi                                                   IBI_QUEUE_STATUS,
684*93ec6949SJoe Komlodi                                                   IBI_STATUS, 1);
685*93ec6949SJoe Komlodi         s->ibi_data.ibi_nacked = true;
686*93ec6949SJoe Komlodi         s->ibi_data.disec_addr = addr;
687*93ec6949SJoe Komlodi         /* Tell the requester to disable controller role requests. */
688*93ec6949SJoe Komlodi         s->ibi_data.disec_byte = DISEC_CR;
689*93ec6949SJoe Komlodi         s->ibi_data.send_direct_disec = true;
690*93ec6949SJoe Komlodi         return -1;
691*93ec6949SJoe Komlodi     }
692*93ec6949SJoe Komlodi     return 0;
693*93ec6949SJoe Komlodi }
694*93ec6949SJoe Komlodi 
695*93ec6949SJoe Komlodi static int aspeed_i3c_device_handle_targ_irq(AspeedI3CDevice *s, uint8_t addr)
696*93ec6949SJoe Komlodi {
697*93ec6949SJoe Komlodi     if (ARRAY_FIELD_EX32(s->regs, IBI_QUEUE_CTRL, NOTIFY_REJECTED_SLAVE_IRQ)) {
698*93ec6949SJoe Komlodi         s->ibi_data.notify_ibi_nack = true;
699*93ec6949SJoe Komlodi     }
700*93ec6949SJoe Komlodi 
701*93ec6949SJoe Komlodi     int table_offset = aspeed_i3c_device_addr_table_index_from_addr(s, addr);
702*93ec6949SJoe Komlodi     /* Doesn't exist in the table, NACK it, don't DISEC. */
703*93ec6949SJoe Komlodi     if (table_offset < 0) {
704*93ec6949SJoe Komlodi         return -1;
705*93ec6949SJoe Komlodi     }
706*93ec6949SJoe Komlodi 
707*93ec6949SJoe Komlodi     table_offset += R_DEVICE_ADDR_TABLE_LOC1;
708*93ec6949SJoe Komlodi     if (FIELD_EX32(s->regs[table_offset], DEVICE_ADDR_TABLE_LOC1, SIR_REJECT)) {
709*93ec6949SJoe Komlodi         s->ibi_data.ibi_queue_status = FIELD_DP32(s->ibi_data.ibi_queue_status,
710*93ec6949SJoe Komlodi                                                   IBI_QUEUE_STATUS,
711*93ec6949SJoe Komlodi                                                   IBI_STATUS, 1);
712*93ec6949SJoe Komlodi         s->ibi_data.ibi_nacked = true;
713*93ec6949SJoe Komlodi         s->ibi_data.disec_addr = addr;
714*93ec6949SJoe Komlodi         /* Tell the requester to disable interrupts. */
715*93ec6949SJoe Komlodi         s->ibi_data.disec_byte = DISEC_INT;
716*93ec6949SJoe Komlodi         s->ibi_data.send_direct_disec = true;
717*93ec6949SJoe Komlodi         return -1;
718*93ec6949SJoe Komlodi     }
719*93ec6949SJoe Komlodi     return 0;
720*93ec6949SJoe Komlodi }
721*93ec6949SJoe Komlodi 
722*93ec6949SJoe Komlodi static int aspeed_i3c_device_ibi_handle(I3CBus *bus, I3CTarget *target,
723*93ec6949SJoe Komlodi                                         uint8_t addr, bool is_recv)
724*93ec6949SJoe Komlodi {
725*93ec6949SJoe Komlodi     AspeedI3CDevice *s = ASPEED_I3C_DEVICE(bus->qbus.parent);
726*93ec6949SJoe Komlodi 
727*93ec6949SJoe Komlodi     trace_aspeed_i3c_device_ibi_handle(s->id, addr, is_recv);
728*93ec6949SJoe Komlodi     s->ibi_data.ibi_queue_status = FIELD_DP32(s->ibi_data.ibi_queue_status,
729*93ec6949SJoe Komlodi                                               IBI_QUEUE_STATUS, IBI_ID,
730*93ec6949SJoe Komlodi                                               (addr << 1) | is_recv);
731*93ec6949SJoe Komlodi     /* Is this a hot join request? */
732*93ec6949SJoe Komlodi     if (addr == I3C_HJ_ADDR) {
733*93ec6949SJoe Komlodi         return aspeed_i3c_device_handle_hj(s);
734*93ec6949SJoe Komlodi     }
735*93ec6949SJoe Komlodi     /* Is secondary controller requesting access? */
736*93ec6949SJoe Komlodi     if (addr == target->address && !is_recv) {
737*93ec6949SJoe Komlodi         return aspeed_i3c_device_handle_ctlr_req(s, addr);
738*93ec6949SJoe Komlodi     }
739*93ec6949SJoe Komlodi     /* Is this a target IRQ? */
740*93ec6949SJoe Komlodi     if (addr == target->address && is_recv) {
741*93ec6949SJoe Komlodi         return aspeed_i3c_device_handle_targ_irq(s, addr);
742*93ec6949SJoe Komlodi     }
743*93ec6949SJoe Komlodi 
744*93ec6949SJoe Komlodi     /* Not sure what this is, NACK it. */
745*93ec6949SJoe Komlodi     return -1;
746*93ec6949SJoe Komlodi }
747*93ec6949SJoe Komlodi 
748*93ec6949SJoe Komlodi static int aspeed_i3c_device_ibi_recv(I3CBus *bus, uint8_t data)
749*93ec6949SJoe Komlodi {
750*93ec6949SJoe Komlodi     AspeedI3CDevice *s = ASPEED_I3C_DEVICE(bus->qbus.parent);
751*93ec6949SJoe Komlodi     if (fifo8_is_full(&s->ibi_data.ibi_intermediate_queue)) {
752*93ec6949SJoe Komlodi         return -1;
753*93ec6949SJoe Komlodi     }
754*93ec6949SJoe Komlodi 
755*93ec6949SJoe Komlodi     fifo8_push(&s->ibi_data.ibi_intermediate_queue, data);
756*93ec6949SJoe Komlodi     trace_aspeed_i3c_device_ibi_recv(s->id, data);
757*93ec6949SJoe Komlodi     return 0;
758*93ec6949SJoe Komlodi }
759*93ec6949SJoe Komlodi 
760*93ec6949SJoe Komlodi static void aspeed_i3c_device_ibi_queue_push(AspeedI3CDevice *s)
761*93ec6949SJoe Komlodi {
762*93ec6949SJoe Komlodi     /* Stored value is in 32-bit chunks, convert it to byte chunks. */
763*93ec6949SJoe Komlodi     uint8_t ibi_slice_size = aspeed_i3c_device_ibi_slice_size(s);
764*93ec6949SJoe Komlodi     uint8_t num_slices = fifo8_num_used(&s->ibi_data.ibi_intermediate_queue) /
765*93ec6949SJoe Komlodi                          ibi_slice_size;
766*93ec6949SJoe Komlodi     uint8_t ibi_status_count = num_slices;
767*93ec6949SJoe Komlodi     union {
768*93ec6949SJoe Komlodi         uint8_t b[sizeof(uint32_t)];
769*93ec6949SJoe Komlodi         uint32_t val32;
770*93ec6949SJoe Komlodi     } ibi_data = {
771*93ec6949SJoe Komlodi         .val32 = 0
772*93ec6949SJoe Komlodi     };
773*93ec6949SJoe Komlodi 
774*93ec6949SJoe Komlodi     /* The report was suppressed, do nothing. */
775*93ec6949SJoe Komlodi     if (s->ibi_data.ibi_nacked && !s->ibi_data.notify_ibi_nack) {
776*93ec6949SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS,
777*93ec6949SJoe Komlodi                          ASPEED_I3C_TRANSFER_STATE_IDLE);
778*93ec6949SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_STATUS,
779*93ec6949SJoe Komlodi                          ASPEED_I3C_TRANSFER_STATUS_IDLE);
780*93ec6949SJoe Komlodi         return;
781*93ec6949SJoe Komlodi     }
782*93ec6949SJoe Komlodi 
783*93ec6949SJoe Komlodi     /* If we don't have any slices to push, just push the status. */
784*93ec6949SJoe Komlodi     if (num_slices == 0) {
785*93ec6949SJoe Komlodi         s->ibi_data.ibi_queue_status =
786*93ec6949SJoe Komlodi              FIELD_DP32(s->ibi_data.ibi_queue_status, IBI_QUEUE_STATUS,
787*93ec6949SJoe Komlodi                         LAST_STATUS, 1);
788*93ec6949SJoe Komlodi         fifo32_push(&s->ibi_queue, s->ibi_data.ibi_queue_status);
789*93ec6949SJoe Komlodi         ibi_status_count = 1;
790*93ec6949SJoe Komlodi     }
791*93ec6949SJoe Komlodi 
792*93ec6949SJoe Komlodi     for (uint8_t i = 0; i < num_slices; i++) {
793*93ec6949SJoe Komlodi         /* If this is the last slice, set LAST_STATUS. */
794*93ec6949SJoe Komlodi         if (fifo8_num_used(&s->ibi_data.ibi_intermediate_queue) <
795*93ec6949SJoe Komlodi             ibi_slice_size) {
796*93ec6949SJoe Komlodi             s->ibi_data.ibi_queue_status =
797*93ec6949SJoe Komlodi                 FIELD_DP32(s->ibi_data.ibi_queue_status, IBI_QUEUE_STATUS,
798*93ec6949SJoe Komlodi                            IBI_DATA_LEN,
799*93ec6949SJoe Komlodi                            fifo8_num_used(&s->ibi_data.ibi_intermediate_queue));
800*93ec6949SJoe Komlodi             s->ibi_data.ibi_queue_status =
801*93ec6949SJoe Komlodi                 FIELD_DP32(s->ibi_data.ibi_queue_status, IBI_QUEUE_STATUS,
802*93ec6949SJoe Komlodi                            LAST_STATUS, 1);
803*93ec6949SJoe Komlodi         } else {
804*93ec6949SJoe Komlodi             s->ibi_data.ibi_queue_status =
805*93ec6949SJoe Komlodi                 FIELD_DP32(s->ibi_data.ibi_queue_status, IBI_QUEUE_STATUS,
806*93ec6949SJoe Komlodi                            IBI_DATA_LEN, ibi_slice_size);
807*93ec6949SJoe Komlodi         }
808*93ec6949SJoe Komlodi 
809*93ec6949SJoe Komlodi         /* Push the IBI status header. */
810*93ec6949SJoe Komlodi         fifo32_push(&s->ibi_queue, s->ibi_data.ibi_queue_status);
811*93ec6949SJoe Komlodi         /* Move each IBI byte into a 32-bit word and push it into the queue. */
812*93ec6949SJoe Komlodi         for (uint8_t j = 0; j < ibi_slice_size; ++j) {
813*93ec6949SJoe Komlodi             if (fifo8_is_empty(&s->ibi_data.ibi_intermediate_queue)) {
814*93ec6949SJoe Komlodi                 break;
815*93ec6949SJoe Komlodi             }
816*93ec6949SJoe Komlodi 
817*93ec6949SJoe Komlodi             ibi_data.b[j & 3] = fifo8_pop(&s->ibi_data.ibi_intermediate_queue);
818*93ec6949SJoe Komlodi             /* We have 32-bits, push it to the IBI FIFO. */
819*93ec6949SJoe Komlodi             if ((j & 0x03) == 0x03) {
820*93ec6949SJoe Komlodi                 fifo32_push(&s->ibi_queue, ibi_data.val32);
821*93ec6949SJoe Komlodi                 ibi_data.val32 = 0;
822*93ec6949SJoe Komlodi             }
823*93ec6949SJoe Komlodi         }
824*93ec6949SJoe Komlodi         /* If the data isn't 32-bit aligned, push the leftover bytes. */
825*93ec6949SJoe Komlodi         if (ibi_slice_size & 0x03) {
826*93ec6949SJoe Komlodi             fifo32_push(&s->ibi_queue, ibi_data.val32);
827*93ec6949SJoe Komlodi         }
828*93ec6949SJoe Komlodi 
829*93ec6949SJoe Komlodi         /* Clear out the data length for the next iteration. */
830*93ec6949SJoe Komlodi         s->ibi_data.ibi_queue_status = FIELD_DP32(s->ibi_data.ibi_queue_status,
831*93ec6949SJoe Komlodi                                          IBI_QUEUE_STATUS, IBI_DATA_LEN, 0);
832*93ec6949SJoe Komlodi     }
833*93ec6949SJoe Komlodi 
834*93ec6949SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, QUEUE_STATUS_LEVEL, IBI_BUF_BLR,
835*93ec6949SJoe Komlodi                      fifo32_num_used(&s->ibi_queue));
836*93ec6949SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, QUEUE_STATUS_LEVEL, IBI_STATUS_CNT,
837*93ec6949SJoe Komlodi                      ibi_status_count);
838*93ec6949SJoe Komlodi     /* Threshold is the register value + 1. */
839*93ec6949SJoe Komlodi     uint8_t threshold = ARRAY_FIELD_EX32(s->regs, QUEUE_THLD_CTRL,
840*93ec6949SJoe Komlodi                                          IBI_STATUS_THLD) + 1;
841*93ec6949SJoe Komlodi     if (fifo32_num_used(&s->ibi_queue) >= threshold) {
842*93ec6949SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, INTR_STATUS, IBI_THLD, 1);
843*93ec6949SJoe Komlodi         aspeed_i3c_device_update_irq(s);
844*93ec6949SJoe Komlodi     }
845*93ec6949SJoe Komlodi 
846*93ec6949SJoe Komlodi     /* State update. */
847*93ec6949SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS,
848*93ec6949SJoe Komlodi                      ASPEED_I3C_TRANSFER_STATE_IDLE);
849*93ec6949SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_STATUS,
850*93ec6949SJoe Komlodi                      ASPEED_I3C_TRANSFER_STATUS_IDLE);
851*93ec6949SJoe Komlodi }
852*93ec6949SJoe Komlodi 
853*93ec6949SJoe Komlodi static int aspeed_i3c_device_ibi_finish(I3CBus *bus)
854*93ec6949SJoe Komlodi {
855*93ec6949SJoe Komlodi     AspeedI3CDevice *s = ASPEED_I3C_DEVICE(bus->qbus.parent);
856*93ec6949SJoe Komlodi     bool nack_and_disable_hj = ARRAY_FIELD_EX32(s->regs, DEVICE_CTRL,
857*93ec6949SJoe Komlodi                                                 HOT_JOIN_ACK_NACK_CTRL);
858*93ec6949SJoe Komlodi     if (nack_and_disable_hj || s->ibi_data.send_direct_disec) {
859*93ec6949SJoe Komlodi         aspeed_i3c_device_send_disec(s);
860*93ec6949SJoe Komlodi     }
861*93ec6949SJoe Komlodi     aspeed_i3c_device_ibi_queue_push(s);
862*93ec6949SJoe Komlodi 
863*93ec6949SJoe Komlodi     /* Clear out the intermediate values. */
864*93ec6949SJoe Komlodi     s->ibi_data.ibi_queue_status = 0;
865*93ec6949SJoe Komlodi     s->ibi_data.disec_addr = 0;
866*93ec6949SJoe Komlodi     s->ibi_data.disec_byte = 0;
867*93ec6949SJoe Komlodi     s->ibi_data.send_direct_disec = false;
868*93ec6949SJoe Komlodi     s->ibi_data.notify_ibi_nack = false;
869*93ec6949SJoe Komlodi     s->ibi_data.ibi_nacked = false;
870*93ec6949SJoe Komlodi 
871*93ec6949SJoe Komlodi     return 0;
872*93ec6949SJoe Komlodi }
873*93ec6949SJoe Komlodi 
8743816bedaSJoe Komlodi static uint32_t aspeed_i3c_device_intr_status_r(AspeedI3CDevice *s)
8753816bedaSJoe Komlodi {
8763816bedaSJoe Komlodi     /* Only return the status whose corresponding EN bits are set. */
8773816bedaSJoe Komlodi     return s->regs[R_INTR_STATUS] & s->regs[R_INTR_STATUS_EN];
8783816bedaSJoe Komlodi }
8793816bedaSJoe Komlodi 
8803816bedaSJoe Komlodi static void aspeed_i3c_device_intr_status_w(AspeedI3CDevice *s, uint32_t val)
8813816bedaSJoe Komlodi {
8823816bedaSJoe Komlodi     /* INTR_STATUS[13:5] is w1c, other bits are RO. */
8833816bedaSJoe Komlodi     val &= 0x3fe0;
8843816bedaSJoe Komlodi     s->regs[R_INTR_STATUS] &= ~val;
8853816bedaSJoe Komlodi 
8863816bedaSJoe Komlodi     aspeed_i3c_device_update_irq(s);
8873816bedaSJoe Komlodi }
8883816bedaSJoe Komlodi 
8893816bedaSJoe Komlodi static void aspeed_i3c_device_intr_status_en_w(AspeedI3CDevice *s, uint32_t val)
8903816bedaSJoe Komlodi {
8913816bedaSJoe Komlodi     s->regs[R_INTR_STATUS_EN] = val;
8923816bedaSJoe Komlodi     aspeed_i3c_device_update_irq(s);
8933816bedaSJoe Komlodi }
8943816bedaSJoe Komlodi 
8953816bedaSJoe Komlodi static void aspeed_i3c_device_intr_signal_en_w(AspeedI3CDevice *s, uint32_t val)
8963816bedaSJoe Komlodi {
8973816bedaSJoe Komlodi     s->regs[R_INTR_SIGNAL_EN] = val;
8983816bedaSJoe Komlodi     aspeed_i3c_device_update_irq(s);
8993816bedaSJoe Komlodi }
9003816bedaSJoe Komlodi 
9013816bedaSJoe Komlodi static void aspeed_i3c_device_intr_force_w(AspeedI3CDevice *s, uint32_t val)
9023816bedaSJoe Komlodi {
9033816bedaSJoe Komlodi     /* INTR_FORCE is WO, just set the corresponding INTR_STATUS bits. */
9043816bedaSJoe Komlodi     s->regs[R_INTR_STATUS] = val;
9053816bedaSJoe Komlodi     aspeed_i3c_device_update_irq(s);
9063816bedaSJoe Komlodi }
9073816bedaSJoe Komlodi 
9084ba25376SJoe Komlodi static uint32_t aspeed_i3c_device_pop_rx(AspeedI3CDevice *s)
9094ba25376SJoe Komlodi {
9104ba25376SJoe Komlodi     if (fifo32_is_empty(&s->rx_queue)) {
9114ba25376SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: Tried to read RX FIFO when empty\n",
9124ba25376SJoe Komlodi                       object_get_canonical_path(OBJECT(s)));
9134ba25376SJoe Komlodi         return 0;
9144ba25376SJoe Komlodi     }
9154ba25376SJoe Komlodi 
9164ba25376SJoe Komlodi     uint32_t val = fifo32_pop(&s->rx_queue);
9174ba25376SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, DATA_BUFFER_STATUS_LEVEL, RX_BUF_BLR,
9184ba25376SJoe Komlodi                      fifo32_num_used(&s->rx_queue));
9194ba25376SJoe Komlodi 
9204ba25376SJoe Komlodi     /* Threshold is 2^RX_BUF_THLD. */
9214ba25376SJoe Komlodi     uint8_t threshold = ARRAY_FIELD_EX32(s->regs, DATA_BUFFER_THLD_CTRL,
9224ba25376SJoe Komlodi                                          RX_BUF_THLD);
9234ba25376SJoe Komlodi     threshold = aspeed_i3c_device_fifo_threshold_from_reg(threshold);
9244ba25376SJoe Komlodi     if (fifo32_num_used(&s->rx_queue) < threshold) {
9254ba25376SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, INTR_STATUS, RX_THLD, 0);
9264ba25376SJoe Komlodi         aspeed_i3c_device_update_irq(s);
9274ba25376SJoe Komlodi     }
9284ba25376SJoe Komlodi 
9294ba25376SJoe Komlodi     trace_aspeed_i3c_device_pop_rx(s->id, val);
9304ba25376SJoe Komlodi     return val;
9314ba25376SJoe Komlodi }
9324ba25376SJoe Komlodi 
933*93ec6949SJoe Komlodi static uint32_t aspeed_i3c_device_ibi_queue_r(AspeedI3CDevice *s)
934*93ec6949SJoe Komlodi {
935*93ec6949SJoe Komlodi     if (fifo32_is_empty(&s->ibi_queue)) {
936*93ec6949SJoe Komlodi         return 0;
937*93ec6949SJoe Komlodi     }
938*93ec6949SJoe Komlodi 
939*93ec6949SJoe Komlodi     uint32_t val = fifo32_pop(&s->ibi_queue);
940*93ec6949SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, QUEUE_STATUS_LEVEL, IBI_BUF_BLR,
941*93ec6949SJoe Komlodi                      fifo32_num_used(&s->ibi_queue));
942*93ec6949SJoe Komlodi     /* Threshold is the register value + 1. */
943*93ec6949SJoe Komlodi     uint8_t threshold = ARRAY_FIELD_EX32(s->regs, QUEUE_THLD_CTRL,
944*93ec6949SJoe Komlodi                                          IBI_STATUS_THLD) + 1;
945*93ec6949SJoe Komlodi     if (fifo32_num_used(&s->ibi_queue) < threshold) {
946*93ec6949SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, INTR_STATUS, IBI_THLD, 0);
947*93ec6949SJoe Komlodi         aspeed_i3c_device_update_irq(s);
948*93ec6949SJoe Komlodi     }
949*93ec6949SJoe Komlodi     return val;
950*93ec6949SJoe Komlodi }
951*93ec6949SJoe Komlodi 
9524ba25376SJoe Komlodi static uint32_t aspeed_i3c_device_resp_queue_port_r(AspeedI3CDevice *s)
9534ba25376SJoe Komlodi {
9544ba25376SJoe Komlodi     if (fifo32_is_empty(&s->resp_queue)) {
9554ba25376SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: Tried to read response FIFO when "
9564ba25376SJoe Komlodi                       "empty\n", object_get_canonical_path(OBJECT(s)));
9574ba25376SJoe Komlodi         return 0;
9584ba25376SJoe Komlodi     }
9594ba25376SJoe Komlodi 
9604ba25376SJoe Komlodi     uint32_t val = fifo32_pop(&s->resp_queue);
9614ba25376SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, QUEUE_STATUS_LEVEL, RESP_BUF_BLR,
9624ba25376SJoe Komlodi                      fifo32_num_used(&s->resp_queue));
9634ba25376SJoe Komlodi 
9644ba25376SJoe Komlodi     /* Threshold is the register value + 1. */
9654ba25376SJoe Komlodi     uint8_t threshold = ARRAY_FIELD_EX32(s->regs, QUEUE_THLD_CTRL,
9664ba25376SJoe Komlodi                                          RESP_BUF_THLD) + 1;
9674ba25376SJoe Komlodi     if (fifo32_num_used(&s->resp_queue) < threshold) {
9684ba25376SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, INTR_STATUS, RESP_RDY, 0);
9694ba25376SJoe Komlodi         aspeed_i3c_device_update_irq(s);
9704ba25376SJoe Komlodi     }
9714ba25376SJoe Komlodi 
9724ba25376SJoe Komlodi     return val;
9734ba25376SJoe Komlodi }
9744ba25376SJoe Komlodi 
9757d87775fSJoe Komlodi static uint64_t aspeed_i3c_device_read(void *opaque, hwaddr offset,
9767d87775fSJoe Komlodi                                        unsigned size)
9777d87775fSJoe Komlodi {
9787d87775fSJoe Komlodi     AspeedI3CDevice *s = ASPEED_I3C_DEVICE(opaque);
9797d87775fSJoe Komlodi     uint32_t addr = offset >> 2;
9807d87775fSJoe Komlodi     uint64_t value;
9817d87775fSJoe Komlodi 
9827d87775fSJoe Komlodi     switch (addr) {
98309467a8aSJoe Komlodi     /* RAZ */
9847d87775fSJoe Komlodi     case R_COMMAND_QUEUE_PORT:
98509467a8aSJoe Komlodi     case R_RESET_CTRL:
98609467a8aSJoe Komlodi     case R_INTR_FORCE:
9877d87775fSJoe Komlodi         value = 0;
9887d87775fSJoe Komlodi         break;
989*93ec6949SJoe Komlodi     case R_IBI_QUEUE_DATA:
990*93ec6949SJoe Komlodi         value = aspeed_i3c_device_ibi_queue_r(s);
991*93ec6949SJoe Komlodi         break;
9923816bedaSJoe Komlodi     case R_INTR_STATUS:
9933816bedaSJoe Komlodi         value = aspeed_i3c_device_intr_status_r(s);
9943816bedaSJoe Komlodi         break;
9954ba25376SJoe Komlodi     case R_RX_TX_DATA_PORT:
9964ba25376SJoe Komlodi         value = aspeed_i3c_device_pop_rx(s);
9974ba25376SJoe Komlodi         break;
9984ba25376SJoe Komlodi     case R_RESPONSE_QUEUE_PORT:
9994ba25376SJoe Komlodi         value = aspeed_i3c_device_resp_queue_port_r(s);
10004ba25376SJoe Komlodi         break;
10017d87775fSJoe Komlodi     default:
10027d87775fSJoe Komlodi         value = s->regs[addr];
10037d87775fSJoe Komlodi         break;
10047d87775fSJoe Komlodi     }
10057d87775fSJoe Komlodi 
10067d87775fSJoe Komlodi     trace_aspeed_i3c_device_read(s->id, offset, value);
10077d87775fSJoe Komlodi 
10087d87775fSJoe Komlodi     return value;
10097d87775fSJoe Komlodi }
10107d87775fSJoe Komlodi 
10114ba25376SJoe Komlodi static void aspeed_i3c_device_resp_queue_push(AspeedI3CDevice *s,
10124ba25376SJoe Komlodi                                               uint8_t err, uint8_t tid,
10134ba25376SJoe Komlodi                                               uint8_t ccc_type,
10144ba25376SJoe Komlodi                                               uint16_t data_len)
10154ba25376SJoe Komlodi {
10164ba25376SJoe Komlodi     uint32_t val = 0;
10174ba25376SJoe Komlodi     val = FIELD_DP32(val, RESPONSE_QUEUE_PORT, ERR_STATUS, err);
10184ba25376SJoe Komlodi     val = FIELD_DP32(val, RESPONSE_QUEUE_PORT, TID, tid);
10194ba25376SJoe Komlodi     val = FIELD_DP32(val, RESPONSE_QUEUE_PORT, CCCT, ccc_type);
10204ba25376SJoe Komlodi     val = FIELD_DP32(val, RESPONSE_QUEUE_PORT, DL, data_len);
10214ba25376SJoe Komlodi     if (!fifo32_is_full(&s->resp_queue)) {
10224ba25376SJoe Komlodi         trace_aspeed_i3c_device_resp_queue_push(s->id, val);
10234ba25376SJoe Komlodi         fifo32_push(&s->resp_queue, val);
10244ba25376SJoe Komlodi     }
10254ba25376SJoe Komlodi 
10264ba25376SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, QUEUE_STATUS_LEVEL, RESP_BUF_BLR,
10274ba25376SJoe Komlodi                      fifo32_num_used(&s->resp_queue));
10284ba25376SJoe Komlodi     /* Threshold is the register value + 1. */
10294ba25376SJoe Komlodi     uint8_t threshold = ARRAY_FIELD_EX32(s->regs, QUEUE_THLD_CTRL,
10304ba25376SJoe Komlodi                                          RESP_BUF_THLD) + 1;
10314ba25376SJoe Komlodi     if (fifo32_num_used(&s->resp_queue) >= threshold) {
10324ba25376SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, INTR_STATUS, RESP_RDY, 1);
10334ba25376SJoe Komlodi         aspeed_i3c_device_update_irq(s);
10344ba25376SJoe Komlodi     }
10354ba25376SJoe Komlodi }
10364ba25376SJoe Komlodi 
10374ba25376SJoe Komlodi static void aspeed_i3c_device_push_tx(AspeedI3CDevice *s, uint32_t val)
10384ba25376SJoe Komlodi {
10394ba25376SJoe Komlodi     if (fifo32_is_full(&s->tx_queue)) {
10404ba25376SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: Tried to push to TX FIFO when "
10414ba25376SJoe Komlodi                       "full\n", object_get_canonical_path(OBJECT(s)));
10424ba25376SJoe Komlodi         return;
10434ba25376SJoe Komlodi     }
10444ba25376SJoe Komlodi 
10454ba25376SJoe Komlodi     trace_aspeed_i3c_device_push_tx(s->id, val);
10464ba25376SJoe Komlodi     fifo32_push(&s->tx_queue, val);
10474ba25376SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, DATA_BUFFER_STATUS_LEVEL, TX_BUF_EMPTY_LOC,
10484ba25376SJoe Komlodi                      fifo32_num_free(&s->tx_queue));
10494ba25376SJoe Komlodi 
10504ba25376SJoe Komlodi     /* Threshold is 2^TX_BUF_THLD. */
10514ba25376SJoe Komlodi     uint8_t empty_threshold = ARRAY_FIELD_EX32(s->regs, DATA_BUFFER_THLD_CTRL,
10524ba25376SJoe Komlodi                                                TX_BUF_THLD);
10534ba25376SJoe Komlodi     empty_threshold =
10544ba25376SJoe Komlodi         aspeed_i3c_device_fifo_threshold_from_reg(empty_threshold);
10554ba25376SJoe Komlodi     if (fifo32_num_free(&s->tx_queue) < empty_threshold) {
10564ba25376SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, INTR_STATUS, TX_THLD, 0);
10574ba25376SJoe Komlodi         aspeed_i3c_device_update_irq(s);
10584ba25376SJoe Komlodi     }
10594ba25376SJoe Komlodi }
10604ba25376SJoe Komlodi 
10614ba25376SJoe Komlodi static uint32_t aspeed_i3c_device_pop_tx(AspeedI3CDevice *s)
10624ba25376SJoe Komlodi {
10634ba25376SJoe Komlodi     if (fifo32_is_empty(&s->tx_queue)) {
10644ba25376SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: Tried to pop from TX FIFO when "
10654ba25376SJoe Komlodi                       "empty\n", object_get_canonical_path(OBJECT(s)));
10664ba25376SJoe Komlodi         return 0;
10674ba25376SJoe Komlodi     }
10684ba25376SJoe Komlodi 
10694ba25376SJoe Komlodi     uint32_t val = fifo32_pop(&s->tx_queue);
10704ba25376SJoe Komlodi     trace_aspeed_i3c_device_pop_tx(s->id, val);
10714ba25376SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, DATA_BUFFER_STATUS_LEVEL, TX_BUF_EMPTY_LOC,
10724ba25376SJoe Komlodi                      fifo32_num_free(&s->tx_queue));
10734ba25376SJoe Komlodi 
10744ba25376SJoe Komlodi     /* Threshold is 2^TX_BUF_THLD. */
10754ba25376SJoe Komlodi     uint8_t empty_threshold = ARRAY_FIELD_EX32(s->regs, DATA_BUFFER_THLD_CTRL,
10764ba25376SJoe Komlodi                                                TX_BUF_THLD);
10774ba25376SJoe Komlodi     empty_threshold =
10784ba25376SJoe Komlodi         aspeed_i3c_device_fifo_threshold_from_reg(empty_threshold);
10794ba25376SJoe Komlodi     if (fifo32_num_free(&s->tx_queue) >= empty_threshold) {
10804ba25376SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, INTR_STATUS, TX_THLD, 1);
10814ba25376SJoe Komlodi         aspeed_i3c_device_update_irq(s);
10824ba25376SJoe Komlodi     }
10834ba25376SJoe Komlodi     return val;
10844ba25376SJoe Komlodi }
10854ba25376SJoe Komlodi 
10864ba25376SJoe Komlodi static void aspeed_i3c_device_push_rx(AspeedI3CDevice *s, uint32_t val)
10874ba25376SJoe Komlodi {
10884ba25376SJoe Komlodi     if (fifo32_is_full(&s->rx_queue)) {
10894ba25376SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: Tried to push to RX FIFO when "
10904ba25376SJoe Komlodi                       "full\n", object_get_canonical_path(OBJECT(s)));
10914ba25376SJoe Komlodi         return;
10924ba25376SJoe Komlodi     }
10934ba25376SJoe Komlodi     trace_aspeed_i3c_device_push_rx(s->id, val);
10944ba25376SJoe Komlodi     fifo32_push(&s->rx_queue, val);
10954ba25376SJoe Komlodi 
10964ba25376SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, DATA_BUFFER_STATUS_LEVEL, RX_BUF_BLR,
10974ba25376SJoe Komlodi                      fifo32_num_used(&s->rx_queue));
10984ba25376SJoe Komlodi     /* Threshold is 2^RX_BUF_THLD. */
10994ba25376SJoe Komlodi     uint8_t threshold = ARRAY_FIELD_EX32(s->regs, DATA_BUFFER_THLD_CTRL,
11004ba25376SJoe Komlodi                                          RX_BUF_THLD);
11014ba25376SJoe Komlodi     threshold = aspeed_i3c_device_fifo_threshold_from_reg(threshold);
11024ba25376SJoe Komlodi     if (fifo32_num_used(&s->rx_queue) >= threshold) {
11034ba25376SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, INTR_STATUS, RX_THLD, 1);
11044ba25376SJoe Komlodi         aspeed_i3c_device_update_irq(s);
11054ba25376SJoe Komlodi     }
11064ba25376SJoe Komlodi }
11074ba25376SJoe Komlodi 
11084ba25376SJoe Komlodi static void aspeed_i3c_device_short_transfer(AspeedI3CDevice *s,
11094ba25376SJoe Komlodi                                              AspeedI3CTransferCmd cmd,
11104ba25376SJoe Komlodi                                              AspeedI3CShortArg arg)
11114ba25376SJoe Komlodi {
11124ba25376SJoe Komlodi     uint8_t err = ASPEED_I3C_RESP_QUEUE_ERR_NONE;
11134ba25376SJoe Komlodi     uint8_t addr = aspeed_i3c_device_target_addr(s, cmd.dev_index);
11144ba25376SJoe Komlodi     bool is_i2c = aspeed_i3c_device_target_is_i2c(s, cmd.dev_index);
11154ba25376SJoe Komlodi     uint8_t data[4]; /* Max we can send on a short transfer is 4 bytes. */
11164ba25376SJoe Komlodi     uint8_t len = 0;
11174ba25376SJoe Komlodi     uint32_t bytes_sent; /* Ignored on short transfers. */
11184ba25376SJoe Komlodi 
11194ba25376SJoe Komlodi     /* Can't do reads on a short transfer. */
11204ba25376SJoe Komlodi     if (cmd.rnw) {
11214ba25376SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: Cannot do a read on a short "
11224ba25376SJoe Komlodi                       "transfer\n", object_get_canonical_path(OBJECT(s)));
11234ba25376SJoe Komlodi         return;
11244ba25376SJoe Komlodi     }
11254ba25376SJoe Komlodi 
11264ba25376SJoe Komlodi     if (aspeed_i3c_device_send_start(s, addr, /*is_recv=*/false, is_i2c)) {
11274ba25376SJoe Komlodi         err = ASPEED_I3C_RESP_QUEUE_ERR_I2C_NACK;
11284ba25376SJoe Komlodi         goto transfer_done;
11294ba25376SJoe Komlodi     }
11304ba25376SJoe Komlodi 
11314ba25376SJoe Komlodi     /* Are we sending a command? */
11324ba25376SJoe Komlodi     if (cmd.cp) {
11334ba25376SJoe Komlodi         data[len] = cmd.cmd;
11344ba25376SJoe Komlodi         len++;
11354ba25376SJoe Komlodi         /*
11364ba25376SJoe Komlodi          * byte0 is the defining byte for a command, and is only sent if a
11374ba25376SJoe Komlodi          * command is present and if the command has a defining byte present.
11384ba25376SJoe Komlodi          * (byte_strb & 0x01) is always treated as set by the controller, and is
11394ba25376SJoe Komlodi          * ignored.
11404ba25376SJoe Komlodi          */
11414ba25376SJoe Komlodi         if (cmd.dbp) {
11424ba25376SJoe Komlodi             data[len] += arg.byte0;
11434ba25376SJoe Komlodi             len++;
11444ba25376SJoe Komlodi         }
11454ba25376SJoe Komlodi     }
11464ba25376SJoe Komlodi 
11474ba25376SJoe Komlodi     /* Send the bytes passed in the argument. */
11484ba25376SJoe Komlodi     if (arg.byte_strb & 0x02) {
11494ba25376SJoe Komlodi         data[len] = arg.byte1;
11504ba25376SJoe Komlodi         len++;
11514ba25376SJoe Komlodi     }
11524ba25376SJoe Komlodi     if (arg.byte_strb & 0x04) {
11534ba25376SJoe Komlodi         data[len] = arg.byte2;
11544ba25376SJoe Komlodi         len++;
11554ba25376SJoe Komlodi     }
11564ba25376SJoe Komlodi 
11574ba25376SJoe Komlodi     if (aspeed_i3c_device_send(s, data, len, &bytes_sent, is_i2c)) {
11584ba25376SJoe Komlodi         err = ASPEED_I3C_RESP_QUEUE_ERR_I2C_NACK;
11594ba25376SJoe Komlodi     } else {
11604ba25376SJoe Komlodi         /* Only go to an idle state on a successful transfer. */
11614ba25376SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS,
11624ba25376SJoe Komlodi                          ASPEED_I3C_TRANSFER_STATE_IDLE);
11634ba25376SJoe Komlodi     }
11644ba25376SJoe Komlodi 
11654ba25376SJoe Komlodi transfer_done:
11664ba25376SJoe Komlodi     if (cmd.toc) {
11674ba25376SJoe Komlodi         aspeed_i3c_device_end_transfer(s, is_i2c);
11684ba25376SJoe Komlodi     }
11694ba25376SJoe Komlodi     if (cmd.roc) {
11704ba25376SJoe Komlodi         /*
11714ba25376SJoe Komlodi          * ccc_type is always 0 in controller mode, data_len is 0 in short
11724ba25376SJoe Komlodi          * transfers.
11734ba25376SJoe Komlodi          */
11744ba25376SJoe Komlodi         aspeed_i3c_device_resp_queue_push(s, err, cmd.tid, /*ccc_type=*/0,
11754ba25376SJoe Komlodi                                           /*data_len=*/0);
11764ba25376SJoe Komlodi     }
11774ba25376SJoe Komlodi }
11784ba25376SJoe Komlodi 
11794ba25376SJoe Komlodi /* Returns number of bytes transmitted. */
11804ba25376SJoe Komlodi static uint16_t aspeed_i3c_device_tx(AspeedI3CDevice *s, uint16_t num,
11814ba25376SJoe Komlodi                                      bool is_i2c)
11824ba25376SJoe Komlodi {
11834ba25376SJoe Komlodi     uint16_t bytes_sent = 0;
11844ba25376SJoe Komlodi     union {
11854ba25376SJoe Komlodi         uint8_t b[sizeof(uint32_t)];
11864ba25376SJoe Komlodi         uint32_t val;
11874ba25376SJoe Komlodi     } val32;
11884ba25376SJoe Komlodi 
11894ba25376SJoe Komlodi     while (bytes_sent < num) {
11904ba25376SJoe Komlodi         val32.val = aspeed_i3c_device_pop_tx(s);
11914ba25376SJoe Komlodi         for (uint8_t i = 0; i < sizeof(val32.val); i++) {
11924ba25376SJoe Komlodi             if (aspeed_i3c_device_send_byte(s, val32.b[i], is_i2c)) {
11934ba25376SJoe Komlodi                 return bytes_sent;
11944ba25376SJoe Komlodi             }
11954ba25376SJoe Komlodi             bytes_sent++;
11964ba25376SJoe Komlodi 
11974ba25376SJoe Komlodi             /* We're not sending the full 32-bits, break early. */
11984ba25376SJoe Komlodi             if (bytes_sent >= num) {
11994ba25376SJoe Komlodi                 break;
12004ba25376SJoe Komlodi             }
12014ba25376SJoe Komlodi         }
12024ba25376SJoe Komlodi     }
12034ba25376SJoe Komlodi 
12044ba25376SJoe Komlodi     return bytes_sent;
12054ba25376SJoe Komlodi }
12064ba25376SJoe Komlodi 
12074ba25376SJoe Komlodi /* Returns number of bytes received. */
12084ba25376SJoe Komlodi static uint16_t aspeed_i3c_device_rx(AspeedI3CDevice *s, uint16_t num,
12094ba25376SJoe Komlodi                                      bool is_i2c)
12104ba25376SJoe Komlodi {
12114ba25376SJoe Komlodi     /*
12124ba25376SJoe Komlodi      * Allocate a temporary buffer to read data from the target.
12134ba25376SJoe Komlodi      * Zero it and word-align it as well in case we're reading unaligned data.
12144ba25376SJoe Komlodi      */
12154ba25376SJoe Komlodi     g_autofree uint8_t *data = g_new0(uint8_t, num + (num & 0x03));
12164ba25376SJoe Komlodi     uint32_t *data32 = (uint32_t *)data;
12174ba25376SJoe Komlodi     /*
12184ba25376SJoe Komlodi      * 32-bits since the I3C API wants a 32-bit number, even though the
12194ba25376SJoe Komlodi      * controller can only do 16-bit transfers.
12204ba25376SJoe Komlodi      */
12214ba25376SJoe Komlodi     uint32_t num_read = 0;
12224ba25376SJoe Komlodi 
12234ba25376SJoe Komlodi     /* Can NACK if the target receives an unsupported CCC. */
12244ba25376SJoe Komlodi     if (aspeed_i3c_device_recv_data(s, is_i2c, data, num, &num_read)) {
12254ba25376SJoe Komlodi         return 0;
12264ba25376SJoe Komlodi     }
12274ba25376SJoe Komlodi 
12284ba25376SJoe Komlodi     for (uint16_t i = 0; i < num_read / 4; i++) {
12294ba25376SJoe Komlodi         aspeed_i3c_device_push_rx(s, *data32);
12304ba25376SJoe Komlodi         data32++;
12314ba25376SJoe Komlodi     }
12324ba25376SJoe Komlodi     /*
12334ba25376SJoe Komlodi      * If we're pushing data that isn't 32-bit aligned, push what's left.
12344ba25376SJoe Komlodi      * It's software's responsibility to know what bits are valid in the partial
12354ba25376SJoe Komlodi      * data.
12364ba25376SJoe Komlodi      */
12374ba25376SJoe Komlodi     if (num_read & 0x03) {
12384ba25376SJoe Komlodi         aspeed_i3c_device_push_rx(s, *data32);
12394ba25376SJoe Komlodi     }
12404ba25376SJoe Komlodi 
12414ba25376SJoe Komlodi     return num_read;
12424ba25376SJoe Komlodi }
12434ba25376SJoe Komlodi 
12444ba25376SJoe Komlodi static int aspeed_i3c_device_transfer_ccc(AspeedI3CDevice *s,
12454ba25376SJoe Komlodi                                            AspeedI3CTransferCmd cmd,
12464ba25376SJoe Komlodi                                            AspeedI3CTransferArg arg)
12474ba25376SJoe Komlodi {
12484ba25376SJoe Komlodi     /* CCC start is always a write. CCCs cannot be done on I2C devices. */
12494ba25376SJoe Komlodi     if (aspeed_i3c_device_send_start(s, I3C_BROADCAST, /*is_recv=*/false,
12504ba25376SJoe Komlodi                                      /*is_i2c=*/false)) {
12514ba25376SJoe Komlodi         return ASPEED_I3C_RESP_QUEUE_ERR_BROADCAST_NACK;
12524ba25376SJoe Komlodi     }
12534ba25376SJoe Komlodi     trace_aspeed_i3c_device_transfer_ccc(s->id, cmd.cmd);
12544ba25376SJoe Komlodi     if (aspeed_i3c_device_send_byte(s, cmd.cmd, /*is_i2c=*/false)) {
12554ba25376SJoe Komlodi         return ASPEED_I3C_RESP_QUEUE_ERR_I2C_NACK;
12564ba25376SJoe Komlodi     }
12574ba25376SJoe Komlodi 
12584ba25376SJoe Komlodi     /* On a direct CCC, we do a restart and then send the target's address. */
12594ba25376SJoe Komlodi     if (CCC_IS_DIRECT(cmd.cmd)) {
12604ba25376SJoe Komlodi         bool is_recv = cmd.rnw;
12614ba25376SJoe Komlodi         uint8_t addr = aspeed_i3c_device_target_addr(s, cmd.dev_index);
12624ba25376SJoe Komlodi         if (aspeed_i3c_device_send_start(s, addr, is_recv, /*is_i2c=*/false)) {
12634ba25376SJoe Komlodi             return ASPEED_I3C_RESP_QUEUE_ERR_BROADCAST_NACK;
12644ba25376SJoe Komlodi         }
12654ba25376SJoe Komlodi     }
12664ba25376SJoe Komlodi 
12674ba25376SJoe Komlodi     return ASPEED_I3C_RESP_QUEUE_ERR_NONE;
12684ba25376SJoe Komlodi }
12694ba25376SJoe Komlodi 
12704ba25376SJoe Komlodi static void aspeed_i3c_device_transfer(AspeedI3CDevice *s,
12714ba25376SJoe Komlodi                                        AspeedI3CTransferCmd cmd,
12724ba25376SJoe Komlodi                                        AspeedI3CTransferArg arg)
12734ba25376SJoe Komlodi {
12744ba25376SJoe Komlodi     bool is_recv = cmd.rnw;
12754ba25376SJoe Komlodi     uint8_t err = ASPEED_I3C_RESP_QUEUE_ERR_NONE;
12764ba25376SJoe Komlodi     uint8_t addr = aspeed_i3c_device_target_addr(s, cmd.dev_index);
12774ba25376SJoe Komlodi     bool is_i2c = aspeed_i3c_device_target_is_i2c(s, cmd.dev_index);
12784ba25376SJoe Komlodi     uint16_t bytes_transferred = 0;
12794ba25376SJoe Komlodi 
12804ba25376SJoe Komlodi     if (cmd.cp) {
12814ba25376SJoe Komlodi         /* We're sending a CCC. */
12824ba25376SJoe Komlodi         err = aspeed_i3c_device_transfer_ccc(s, cmd, arg);
12834ba25376SJoe Komlodi         if (err != ASPEED_I3C_RESP_QUEUE_ERR_NONE) {
12844ba25376SJoe Komlodi             goto transfer_done;
12854ba25376SJoe Komlodi         }
12864ba25376SJoe Komlodi     } else {
12874ba25376SJoe Komlodi         if (ARRAY_FIELD_EX32(s->regs, DEVICE_CTRL, I3C_BROADCAST_ADDR_INC) &&
12884ba25376SJoe Komlodi             is_i2c == false) {
12894ba25376SJoe Komlodi             if (aspeed_i3c_device_send_start(s, I3C_BROADCAST,
12904ba25376SJoe Komlodi                                              /*is_recv=*/false, is_i2c)) {
12914ba25376SJoe Komlodi                 err = ASPEED_I3C_RESP_QUEUE_ERR_I2C_NACK;
12924ba25376SJoe Komlodi                 goto transfer_done;
12934ba25376SJoe Komlodi             }
12944ba25376SJoe Komlodi         }
12954ba25376SJoe Komlodi         /* Otherwise we're doing a private transfer. */
12964ba25376SJoe Komlodi         if (aspeed_i3c_device_send_start(s, addr, is_recv, is_i2c)) {
12974ba25376SJoe Komlodi             err = ASPEED_I3C_RESP_QUEUE_ERR_I2C_NACK;
12984ba25376SJoe Komlodi             goto transfer_done;
12994ba25376SJoe Komlodi         }
13004ba25376SJoe Komlodi     }
13014ba25376SJoe Komlodi 
13024ba25376SJoe Komlodi     if (is_recv) {
13034ba25376SJoe Komlodi         bytes_transferred = aspeed_i3c_device_rx(s, arg.data_len, is_i2c);
13044ba25376SJoe Komlodi     } else {
13054ba25376SJoe Komlodi         bytes_transferred = aspeed_i3c_device_tx(s, arg.data_len, is_i2c);
13064ba25376SJoe Komlodi     }
13074ba25376SJoe Komlodi 
13084ba25376SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS,
13094ba25376SJoe Komlodi                      ASPEED_I3C_TRANSFER_STATE_IDLE);
13104ba25376SJoe Komlodi 
13114ba25376SJoe Komlodi transfer_done:
13124ba25376SJoe Komlodi     if (cmd.toc) {
13134ba25376SJoe Komlodi         aspeed_i3c_device_end_transfer(s, is_i2c);
13144ba25376SJoe Komlodi     }
13154ba25376SJoe Komlodi     if (cmd.roc) {
13164ba25376SJoe Komlodi         /*
13174ba25376SJoe Komlodi          * data_len is the number of bytes that still need to be TX'd, or the
13184ba25376SJoe Komlodi          * number of bytes RX'd.
13194ba25376SJoe Komlodi          */
13204ba25376SJoe Komlodi         uint16_t data_len = is_recv ? bytes_transferred : arg.data_len -
13214ba25376SJoe Komlodi                                                           bytes_transferred;
13224ba25376SJoe Komlodi         /* CCCT is always 0 in controller mode. */
13234ba25376SJoe Komlodi         aspeed_i3c_device_resp_queue_push(s, err, cmd.tid, /*ccc_type=*/0,
13244ba25376SJoe Komlodi                                           data_len);
13254ba25376SJoe Komlodi     }
13264ba25376SJoe Komlodi 
13274ba25376SJoe Komlodi     aspeed_i3c_device_update_irq(s);
13284ba25376SJoe Komlodi }
13294ba25376SJoe Komlodi 
13304ba25376SJoe Komlodi static void aspeed_i3c_device_transfer_cmd(AspeedI3CDevice *s,
13314ba25376SJoe Komlodi                                            AspeedI3CTransferCmd cmd,
13324ba25376SJoe Komlodi                                            AspeedI3CCmdQueueData arg)
13334ba25376SJoe Komlodi {
13344ba25376SJoe Komlodi     uint8_t arg_attr = FIELD_EX32(arg.word, COMMAND_QUEUE_PORT, CMD_ATTR);
13354ba25376SJoe Komlodi 
13364ba25376SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CMD_TID, cmd.tid);
13374ba25376SJoe Komlodi 
13384ba25376SJoe Komlodi     /* User is trying to do HDR transfers, see if we can do them. */
13394ba25376SJoe Komlodi     if (cmd.speed == 0x06 && !aspeed_i3c_device_has_hdr_ddr(s)) {
13404ba25376SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: HDR DDR is not supported\n",
13414ba25376SJoe Komlodi                       object_get_canonical_path(OBJECT(s)));
13424ba25376SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS,
13434ba25376SJoe Komlodi                          ASPEED_I3C_TRANSFER_STATE_HALT);
13444ba25376SJoe Komlodi         return;
13454ba25376SJoe Komlodi     }
13464ba25376SJoe Komlodi     if (cmd.speed == 0x05 && !aspeed_i3c_device_has_hdr_ts(s)) {
13474ba25376SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: HDR TS is not supported\n",
13484ba25376SJoe Komlodi                       object_get_canonical_path(OBJECT(s)));
13494ba25376SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS,
13504ba25376SJoe Komlodi                          ASPEED_I3C_TRANSFER_STATE_HALT);
13514ba25376SJoe Komlodi         return;
13524ba25376SJoe Komlodi     }
13534ba25376SJoe Komlodi 
13544ba25376SJoe Komlodi     if (arg_attr == ASPEED_I3C_CMD_ATTR_TRANSFER_ARG) {
13554ba25376SJoe Komlodi         aspeed_i3c_device_transfer(s, cmd, arg.transfer_arg);
13564ba25376SJoe Komlodi     } else if (arg_attr == ASPEED_I3C_CMD_ATTR_SHORT_DATA_ARG) {
13574ba25376SJoe Komlodi         aspeed_i3c_device_short_transfer(s, cmd, arg.short_arg);
13584ba25376SJoe Komlodi     } else {
13594ba25376SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: Unknown command queue cmd_attr 0x%x"
13604ba25376SJoe Komlodi                       "\n", object_get_canonical_path(OBJECT(s)), arg_attr);
13614ba25376SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS,
13624ba25376SJoe Komlodi                          ASPEED_I3C_TRANSFER_STATE_HALT);
13634ba25376SJoe Komlodi     }
13644ba25376SJoe Komlodi }
13654ba25376SJoe Komlodi 
13664ba25376SJoe Komlodi static void aspeed_i3c_device_update_char_table(AspeedI3CDevice *s,
13674ba25376SJoe Komlodi                                                 uint8_t offset, uint64_t pid,
13684ba25376SJoe Komlodi                                                 uint8_t bcr, uint8_t dcr,
13694ba25376SJoe Komlodi                                                 uint8_t addr)
13704ba25376SJoe Komlodi {
13714ba25376SJoe Komlodi     if (offset > ASPEED_I3C_NR_DEVICES) {
13724ba25376SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: Device char table offset %d out of "
13734ba25376SJoe Komlodi                       "bounds\n", object_get_canonical_path(OBJECT(s)), offset);
13744ba25376SJoe Komlodi         /* If we're out of bounds, do nothing. */
13754ba25376SJoe Komlodi         return;
13764ba25376SJoe Komlodi     }
13774ba25376SJoe Komlodi 
13784ba25376SJoe Komlodi     /* Each char table index is 128 bits apart. */
13794ba25376SJoe Komlodi     uint16_t dev_index = R_DEVICE_CHARACTERISTIC_TABLE_LOC1 + offset *
13804ba25376SJoe Komlodi                                                             sizeof(uint32_t);
13814ba25376SJoe Komlodi     s->regs[dev_index] = pid & 0xffffffff;
13824ba25376SJoe Komlodi     pid >>= 32;
13834ba25376SJoe Komlodi     s->regs[dev_index + 1] = FIELD_DP32(s->regs[dev_index + 1],
13844ba25376SJoe Komlodi                                         DEVICE_CHARACTERISTIC_TABLE_LOC2,
13854ba25376SJoe Komlodi                                         MSB_PID, pid);
13864ba25376SJoe Komlodi     s->regs[dev_index + 2] = FIELD_DP32(s->regs[dev_index + 2],
13874ba25376SJoe Komlodi                                         DEVICE_CHARACTERISTIC_TABLE_LOC3, DCR,
13884ba25376SJoe Komlodi                                         dcr);
13894ba25376SJoe Komlodi     s->regs[dev_index + 2] = FIELD_DP32(s->regs[dev_index + 2],
13904ba25376SJoe Komlodi                                         DEVICE_CHARACTERISTIC_TABLE_LOC3, BCR,
13914ba25376SJoe Komlodi                                         bcr);
13924ba25376SJoe Komlodi     s->regs[dev_index + 3] = FIELD_DP32(s->regs[dev_index + 3],
13934ba25376SJoe Komlodi                                         DEVICE_CHARACTERISTIC_TABLE_LOC4,
13944ba25376SJoe Komlodi                                         DEV_DYNAMIC_ADDR, addr);
13954ba25376SJoe Komlodi 
13964ba25376SJoe Komlodi     /* Increment PRESENT_DEV_CHAR_TABLE_INDEX. */
13974ba25376SJoe Komlodi     uint8_t idx = ARRAY_FIELD_EX32(s->regs, DEV_CHAR_TABLE_POINTER,
13984ba25376SJoe Komlodi                      PRESENT_DEV_CHAR_TABLE_INDEX);
13994ba25376SJoe Komlodi     /* Increment and rollover. */
14004ba25376SJoe Komlodi     idx++;
14014ba25376SJoe Komlodi     if (idx >= ARRAY_FIELD_EX32(s->regs, DEV_CHAR_TABLE_POINTER,
14024ba25376SJoe Komlodi                                DEV_CHAR_TABLE_DEPTH) / 4) {
14034ba25376SJoe Komlodi         idx = 0;
14044ba25376SJoe Komlodi     }
14054ba25376SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, DEV_CHAR_TABLE_POINTER,
14064ba25376SJoe Komlodi                      PRESENT_DEV_CHAR_TABLE_INDEX, idx);
14074ba25376SJoe Komlodi }
14084ba25376SJoe Komlodi 
14094ba25376SJoe Komlodi static void aspeed_i3c_device_addr_assign_cmd(AspeedI3CDevice *s,
14104ba25376SJoe Komlodi                                               AspeedI3CAddrAssignCmd cmd)
14114ba25376SJoe Komlodi {
14124ba25376SJoe Komlodi     uint8_t i = 0;
14134ba25376SJoe Komlodi     uint8_t err = ASPEED_I3C_RESP_QUEUE_ERR_NONE;
14144ba25376SJoe Komlodi 
14154ba25376SJoe Komlodi     if (!aspeed_i3c_device_has_entdaa(s)) {
14164ba25376SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: ENTDAA is not supported\n",
14174ba25376SJoe Komlodi                       object_get_canonical_path(OBJECT(s)));
14184ba25376SJoe Komlodi         return;
14194ba25376SJoe Komlodi     }
14204ba25376SJoe Komlodi 
14214ba25376SJoe Komlodi     /* Tell everyone to ENTDAA. If these error, no one is on the bus. */
14224ba25376SJoe Komlodi     if (aspeed_i3c_device_send_start(s, I3C_BROADCAST, /*is_recv=*/false,
14234ba25376SJoe Komlodi                                      /*is_i2c=*/false)) {
14244ba25376SJoe Komlodi         err = ASPEED_I3C_RESP_QUEUE_ERR_BROADCAST_NACK;
14254ba25376SJoe Komlodi         goto transfer_done;
14264ba25376SJoe Komlodi     }
14274ba25376SJoe Komlodi     if (aspeed_i3c_device_send_byte(s, cmd.cmd, /*is_i2c=*/false)) {
14284ba25376SJoe Komlodi         err = ASPEED_I3C_RESP_QUEUE_ERR_BROADCAST_NACK;
14294ba25376SJoe Komlodi         goto transfer_done;
14304ba25376SJoe Komlodi     }
14314ba25376SJoe Komlodi 
14324ba25376SJoe Komlodi     /* Go through each device in the table and assign it an address. */
14334ba25376SJoe Komlodi     for (i = 0; i < cmd.dev_count; i++) {
14344ba25376SJoe Komlodi         uint8_t addr = aspeed_i3c_device_target_addr(s, cmd.dev_index + i);
14354ba25376SJoe Komlodi         union {
14364ba25376SJoe Komlodi             uint64_t pid:48;
14374ba25376SJoe Komlodi             uint8_t bcr;
14384ba25376SJoe Komlodi             uint8_t dcr;
14394ba25376SJoe Komlodi             uint32_t w[2];
14404ba25376SJoe Komlodi             uint8_t b[8];
14414ba25376SJoe Komlodi         } target_info;
14424ba25376SJoe Komlodi 
14434ba25376SJoe Komlodi         /* If this fails, there was no one left to ENTDAA. */
14444ba25376SJoe Komlodi         if (aspeed_i3c_device_send_start(s, I3C_BROADCAST, /*is_recv=*/false,
14454ba25376SJoe Komlodi                                          /*is_i2c=*/false)) {
14464ba25376SJoe Komlodi             err = ASPEED_I3C_RESP_QUEUE_ERR_BROADCAST_NACK;
14474ba25376SJoe Komlodi             break;
14484ba25376SJoe Komlodi         }
14494ba25376SJoe Komlodi 
14504ba25376SJoe Komlodi         /*
14514ba25376SJoe Komlodi          * In ENTDAA, we read 8 bytes from the target, which will be the
14524ba25376SJoe Komlodi          * target's PID, BCR, and DCR. After that, we send it the dynamic
14534ba25376SJoe Komlodi          * address.
14544ba25376SJoe Komlodi          * Don't bother checking the number of bytes received, it must send 8
14554ba25376SJoe Komlodi          * bytes during ENTDAA.
14564ba25376SJoe Komlodi          */
14574ba25376SJoe Komlodi         uint32_t num_read;
14584ba25376SJoe Komlodi         if (aspeed_i3c_device_recv_data(s, /*is_i2c=*/false, target_info.b,
14594ba25376SJoe Komlodi                                         I3C_ENTDAA_SIZE, &num_read)) {
14604ba25376SJoe Komlodi             qemu_log_mask(LOG_GUEST_ERROR, "%s: Target NACKed ENTDAA CCC\n",
14614ba25376SJoe Komlodi                           object_get_canonical_path(OBJECT(s)));
14624ba25376SJoe Komlodi             err = ASPEED_I3C_RESP_QUEUE_ERR_DAA_NACK;
14634ba25376SJoe Komlodi             goto transfer_done;
14644ba25376SJoe Komlodi         }
14654ba25376SJoe Komlodi         if (aspeed_i3c_device_send_byte(s, addr, /*is_i2c=*/false)) {
14664ba25376SJoe Komlodi             qemu_log_mask(LOG_GUEST_ERROR, "%s: Target NACKed addr 0x%.2x "
14674ba25376SJoe Komlodi                           "during ENTDAA\n",
14684ba25376SJoe Komlodi                           object_get_canonical_path(OBJECT(s)), addr);
14694ba25376SJoe Komlodi             err = ASPEED_I3C_RESP_QUEUE_ERR_DAA_NACK;
14704ba25376SJoe Komlodi             break;
14714ba25376SJoe Komlodi         }
14724ba25376SJoe Komlodi         aspeed_i3c_device_update_char_table(s, cmd.dev_index + i,
14734ba25376SJoe Komlodi                                             target_info.pid, target_info.bcr,
14744ba25376SJoe Komlodi                                             target_info.dcr, addr);
14754ba25376SJoe Komlodi 
14764ba25376SJoe Komlodi         /* Push the PID, BCR, and DCR to the RX queue. */
14774ba25376SJoe Komlodi         aspeed_i3c_device_push_rx(s, target_info.w[0]);
14784ba25376SJoe Komlodi         aspeed_i3c_device_push_rx(s, target_info.w[1]);
14794ba25376SJoe Komlodi     }
14804ba25376SJoe Komlodi 
14814ba25376SJoe Komlodi transfer_done:
14824ba25376SJoe Komlodi     /* Do we send a STOP? */
14834ba25376SJoe Komlodi     if (cmd.toc) {
14844ba25376SJoe Komlodi         aspeed_i3c_device_end_transfer(s, /*is_i2c=*/false);
14854ba25376SJoe Komlodi     }
14864ba25376SJoe Komlodi     /*
14874ba25376SJoe Komlodi      * For addr assign commands, the length field is the number of devices
14884ba25376SJoe Komlodi      * left to assign. CCCT is always 0 in controller mode.
14894ba25376SJoe Komlodi      */
14904ba25376SJoe Komlodi     if (cmd.roc) {
14914ba25376SJoe Komlodi         aspeed_i3c_device_resp_queue_push(s, err, cmd.tid, /*ccc_type=*/0,
14924ba25376SJoe Komlodi                                          cmd.dev_count - i);
14934ba25376SJoe Komlodi     }
14944ba25376SJoe Komlodi }
14954ba25376SJoe Komlodi 
14964ba25376SJoe Komlodi static uint32_t aspeed_i3c_device_cmd_queue_pop(AspeedI3CDevice *s)
14974ba25376SJoe Komlodi {
14984ba25376SJoe Komlodi     if (fifo32_is_empty(&s->cmd_queue)) {
14994ba25376SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: Tried to dequeue command queue "
15004ba25376SJoe Komlodi                       "when it was empty\n",
15014ba25376SJoe Komlodi                       object_get_canonical_path(OBJECT(s)));
15024ba25376SJoe Komlodi         return 0;
15034ba25376SJoe Komlodi     }
15044ba25376SJoe Komlodi     uint32_t val = fifo32_pop(&s->cmd_queue);
15054ba25376SJoe Komlodi 
15064ba25376SJoe Komlodi     uint8_t empty_threshold = ARRAY_FIELD_EX32(s->regs, QUEUE_THLD_CTRL,
15074ba25376SJoe Komlodi                                                CMD_BUF_EMPTY_THLD);
15084ba25376SJoe Komlodi     uint8_t cmd_queue_empty_loc = ARRAY_FIELD_EX32(s->regs,
15094ba25376SJoe Komlodi                                                    QUEUE_STATUS_LEVEL,
15104ba25376SJoe Komlodi                                                    CMD_QUEUE_EMPTY_LOC);
15114ba25376SJoe Komlodi     cmd_queue_empty_loc++;
15124ba25376SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, QUEUE_STATUS_LEVEL, CMD_QUEUE_EMPTY_LOC,
15134ba25376SJoe Komlodi                      cmd_queue_empty_loc);
15144ba25376SJoe Komlodi     if (cmd_queue_empty_loc >= empty_threshold) {
15154ba25376SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, INTR_STATUS, CMD_QUEUE_RDY, 1);
15164ba25376SJoe Komlodi         aspeed_i3c_device_update_irq(s);
15174ba25376SJoe Komlodi     }
15184ba25376SJoe Komlodi 
15194ba25376SJoe Komlodi     return val;
15204ba25376SJoe Komlodi }
15214ba25376SJoe Komlodi 
15224ba25376SJoe Komlodi static void aspeed_i3c_device_cmd_queue_execute(AspeedI3CDevice *s)
15234ba25376SJoe Komlodi {
15244ba25376SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS,
15254ba25376SJoe Komlodi                      ASPEED_I3C_TRANSFER_STATE_IDLE);
15264ba25376SJoe Komlodi     if (!aspeed_i3c_device_can_transmit(s)) {
15274ba25376SJoe Komlodi         return;
15284ba25376SJoe Komlodi     }
15294ba25376SJoe Komlodi 
15304ba25376SJoe Komlodi     /*
15314ba25376SJoe Komlodi      * We only start executing when a command is passed into the FIFO.
15324ba25376SJoe Komlodi      * We expect there to be a multiple of 2 items in the queue. The first item
15334ba25376SJoe Komlodi      * should be an argument to a command, and the command should be the second
15344ba25376SJoe Komlodi      * item.
15354ba25376SJoe Komlodi      */
15364ba25376SJoe Komlodi     if (fifo32_num_used(&s->cmd_queue) & 1) {
15374ba25376SJoe Komlodi         return;
15384ba25376SJoe Komlodi     }
15394ba25376SJoe Komlodi 
15404ba25376SJoe Komlodi     while (!fifo32_is_empty(&s->cmd_queue)) {
15414ba25376SJoe Komlodi         AspeedI3CCmdQueueData arg;
15424ba25376SJoe Komlodi         arg.word = aspeed_i3c_device_cmd_queue_pop(s);
15434ba25376SJoe Komlodi         AspeedI3CCmdQueueData cmd;
15444ba25376SJoe Komlodi         cmd.word = aspeed_i3c_device_cmd_queue_pop(s);
15454ba25376SJoe Komlodi         trace_aspeed_i3c_device_cmd_queue_execute(s->id, cmd.word, arg.word);
15464ba25376SJoe Komlodi 
15474ba25376SJoe Komlodi         uint8_t cmd_attr = FIELD_EX32(cmd.word, COMMAND_QUEUE_PORT, CMD_ATTR);
15484ba25376SJoe Komlodi         switch (cmd_attr) {
15494ba25376SJoe Komlodi         case ASPEED_I3C_CMD_ATTR_TRANSFER_CMD:
15504ba25376SJoe Komlodi             aspeed_i3c_device_transfer_cmd(s, cmd.transfer_cmd, arg);
15514ba25376SJoe Komlodi             break;
15524ba25376SJoe Komlodi         case ASPEED_I3C_CMD_ATTR_ADDR_ASSIGN_CMD:
15534ba25376SJoe Komlodi             /* Arg is discarded for addr assign commands. */
15544ba25376SJoe Komlodi             aspeed_i3c_device_addr_assign_cmd(s, cmd.addr_assign_cmd);
15554ba25376SJoe Komlodi             break;
15564ba25376SJoe Komlodi         case ASPEED_I3C_CMD_ATTR_TRANSFER_ARG:
15574ba25376SJoe Komlodi         case ASPEED_I3C_CMD_ATTR_SHORT_DATA_ARG:
15584ba25376SJoe Komlodi             qemu_log_mask(LOG_GUEST_ERROR, "%s: Command queue received argument"
15594ba25376SJoe Komlodi                           " packet when it expected a command packet\n",
15604ba25376SJoe Komlodi                           object_get_canonical_path(OBJECT(s)));
15614ba25376SJoe Komlodi             break;
15624ba25376SJoe Komlodi         default:
15634ba25376SJoe Komlodi             /*
15644ba25376SJoe Komlodi              * The caller's check before queueing an item should prevent this
15654ba25376SJoe Komlodi              * from happening.
15664ba25376SJoe Komlodi              */
15674ba25376SJoe Komlodi             g_assert_not_reached();
15684ba25376SJoe Komlodi             break;
15694ba25376SJoe Komlodi         }
15704ba25376SJoe Komlodi     }
15714ba25376SJoe Komlodi }
15724ba25376SJoe Komlodi 
15734ba25376SJoe Komlodi static void aspeed_i3c_device_cmd_queue_push(AspeedI3CDevice *s, uint32_t val)
15744ba25376SJoe Komlodi {
15754ba25376SJoe Komlodi     if (fifo32_is_full(&s->cmd_queue)) {
15764ba25376SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: Command queue received packet when "
15774ba25376SJoe Komlodi                       "already full\n", object_get_canonical_path(OBJECT(s)));
15784ba25376SJoe Komlodi         return;
15794ba25376SJoe Komlodi     }
15804ba25376SJoe Komlodi     trace_aspeed_i3c_device_cmd_queue_push(s->id, val);
15814ba25376SJoe Komlodi     fifo32_push(&s->cmd_queue, val);
15824ba25376SJoe Komlodi 
15834ba25376SJoe Komlodi     uint8_t empty_threshold = ARRAY_FIELD_EX32(s->regs, QUEUE_THLD_CTRL,
15844ba25376SJoe Komlodi                                                CMD_BUF_EMPTY_THLD);
15854ba25376SJoe Komlodi     uint8_t cmd_queue_empty_loc = ARRAY_FIELD_EX32(s->regs,
15864ba25376SJoe Komlodi                                                    QUEUE_STATUS_LEVEL,
15874ba25376SJoe Komlodi                                                    CMD_QUEUE_EMPTY_LOC);
15884ba25376SJoe Komlodi     if (cmd_queue_empty_loc) {
15894ba25376SJoe Komlodi         cmd_queue_empty_loc--;
15904ba25376SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, QUEUE_STATUS_LEVEL, CMD_QUEUE_EMPTY_LOC,
15914ba25376SJoe Komlodi                          cmd_queue_empty_loc);
15924ba25376SJoe Komlodi     }
15934ba25376SJoe Komlodi     if (cmd_queue_empty_loc < empty_threshold) {
15944ba25376SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, INTR_STATUS, CMD_QUEUE_RDY, 0);
15954ba25376SJoe Komlodi         aspeed_i3c_device_update_irq(s);
15964ba25376SJoe Komlodi     }
15974ba25376SJoe Komlodi }
15984ba25376SJoe Komlodi 
15994ba25376SJoe Komlodi static void aspeed_i3c_device_cmd_queue_port_w(AspeedI3CDevice *s, uint32_t val)
16004ba25376SJoe Komlodi {
16014ba25376SJoe Komlodi     uint8_t cmd_attr = FIELD_EX32(val, COMMAND_QUEUE_PORT, CMD_ATTR);
16024ba25376SJoe Komlodi 
16034ba25376SJoe Komlodi     switch (cmd_attr) {
16044ba25376SJoe Komlodi     /* If a command is received we can start executing it. */
16054ba25376SJoe Komlodi     case ASPEED_I3C_CMD_ATTR_TRANSFER_CMD:
16064ba25376SJoe Komlodi     case ASPEED_I3C_CMD_ATTR_ADDR_ASSIGN_CMD:
16074ba25376SJoe Komlodi         aspeed_i3c_device_cmd_queue_push(s, val);
16084ba25376SJoe Komlodi         aspeed_i3c_device_cmd_queue_execute(s);
16094ba25376SJoe Komlodi         break;
16104ba25376SJoe Komlodi     /* If we get an argument just push it. */
16114ba25376SJoe Komlodi     case ASPEED_I3C_CMD_ATTR_TRANSFER_ARG:
16124ba25376SJoe Komlodi     case ASPEED_I3C_CMD_ATTR_SHORT_DATA_ARG:
16134ba25376SJoe Komlodi         aspeed_i3c_device_cmd_queue_push(s, val);
16144ba25376SJoe Komlodi         break;
16154ba25376SJoe Komlodi     default:
16164ba25376SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: Command queue received packet with "
16174ba25376SJoe Komlodi                       "unknown cmd attr 0x%x\n",
16184ba25376SJoe Komlodi                       object_get_canonical_path(OBJECT(s)), cmd_attr);
16194ba25376SJoe Komlodi         break;
16204ba25376SJoe Komlodi     }
16214ba25376SJoe Komlodi }
16224ba25376SJoe Komlodi 
16237d87775fSJoe Komlodi static void aspeed_i3c_device_write(void *opaque, hwaddr offset,
16247d87775fSJoe Komlodi                                     uint64_t value, unsigned size)
16257d87775fSJoe Komlodi {
16267d87775fSJoe Komlodi     AspeedI3CDevice *s = ASPEED_I3C_DEVICE(opaque);
16277d87775fSJoe Komlodi     uint32_t addr = offset >> 2;
162897fb7498SJoe Komlodi     uint32_t val32 = (uint32_t)value;
16297d87775fSJoe Komlodi 
16307d87775fSJoe Komlodi     trace_aspeed_i3c_device_write(s->id, offset, value);
16317d87775fSJoe Komlodi 
163297fb7498SJoe Komlodi     val32 &= ~ast2600_i3c_device_ro[addr];
16337d87775fSJoe Komlodi     switch (addr) {
16347d87775fSJoe Komlodi     case R_HW_CAPABILITY:
16357d87775fSJoe Komlodi     case R_RESPONSE_QUEUE_PORT:
16367d87775fSJoe Komlodi     case R_IBI_QUEUE_DATA:
16377d87775fSJoe Komlodi     case R_QUEUE_STATUS_LEVEL:
16387d87775fSJoe Komlodi     case R_PRESENT_STATE:
16397d87775fSJoe Komlodi     case R_CCC_DEVICE_STATUS:
16407d87775fSJoe Komlodi     case R_DEVICE_ADDR_TABLE_POINTER:
16417d87775fSJoe Komlodi     case R_VENDOR_SPECIFIC_REG_POINTER:
16427d87775fSJoe Komlodi     case R_SLV_CHAR_CTRL:
16437d87775fSJoe Komlodi     case R_SLV_MAX_LEN:
16447d87775fSJoe Komlodi     case R_MAX_READ_TURNAROUND:
16457d87775fSJoe Komlodi     case R_I3C_VER_ID:
16467d87775fSJoe Komlodi     case R_I3C_VER_TYPE:
16477d87775fSJoe Komlodi     case R_EXTENDED_CAPABILITY:
16487d87775fSJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR,
16497d87775fSJoe Komlodi                       "%s: write to readonly register[0x%02" HWADDR_PRIx
16507d87775fSJoe Komlodi                       "] = 0x%08" PRIx64 "\n",
16517d87775fSJoe Komlodi                       __func__, offset, value);
16527d87775fSJoe Komlodi         break;
16537d87775fSJoe Komlodi     case R_RX_TX_DATA_PORT:
16544ba25376SJoe Komlodi         aspeed_i3c_device_push_tx(s, val32);
16554ba25376SJoe Komlodi         break;
16564ba25376SJoe Komlodi     case R_COMMAND_QUEUE_PORT:
16574ba25376SJoe Komlodi         aspeed_i3c_device_cmd_queue_port_w(s, val32);
16587d87775fSJoe Komlodi         break;
16597d87775fSJoe Komlodi     case R_RESET_CTRL:
16607d87775fSJoe Komlodi         break;
16613816bedaSJoe Komlodi     case R_INTR_STATUS:
16623816bedaSJoe Komlodi         aspeed_i3c_device_intr_status_w(s, val32);
16633816bedaSJoe Komlodi         break;
16643816bedaSJoe Komlodi     case R_INTR_STATUS_EN:
16653816bedaSJoe Komlodi         aspeed_i3c_device_intr_status_en_w(s, val32);
16663816bedaSJoe Komlodi         break;
16673816bedaSJoe Komlodi     case R_INTR_SIGNAL_EN:
16683816bedaSJoe Komlodi         aspeed_i3c_device_intr_signal_en_w(s, val32);
16693816bedaSJoe Komlodi         break;
16703816bedaSJoe Komlodi     case R_INTR_FORCE:
16713816bedaSJoe Komlodi         aspeed_i3c_device_intr_force_w(s, val32);
16723816bedaSJoe Komlodi         break;
16737d87775fSJoe Komlodi     default:
167497fb7498SJoe Komlodi         s->regs[addr] = val32;
16757d87775fSJoe Komlodi         break;
16767d87775fSJoe Komlodi     }
16777d87775fSJoe Komlodi }
16787d87775fSJoe Komlodi 
16797d87775fSJoe Komlodi static const VMStateDescription aspeed_i3c_device_vmstate = {
16807d87775fSJoe Komlodi     .name = TYPE_ASPEED_I3C,
16817d87775fSJoe Komlodi     .version_id = 1,
16827d87775fSJoe Komlodi     .minimum_version_id = 1,
16837d87775fSJoe Komlodi     .fields = (const VMStateField[]){
16847d87775fSJoe Komlodi         VMSTATE_UINT32_ARRAY(regs, AspeedI3CDevice, ASPEED_I3C_DEVICE_NR_REGS),
16857d87775fSJoe Komlodi         VMSTATE_END_OF_LIST(),
16867d87775fSJoe Komlodi     }
16877d87775fSJoe Komlodi };
16887d87775fSJoe Komlodi 
16897d87775fSJoe Komlodi static const MemoryRegionOps aspeed_i3c_device_ops = {
16907d87775fSJoe Komlodi     .read = aspeed_i3c_device_read,
16917d87775fSJoe Komlodi     .write = aspeed_i3c_device_write,
16927d87775fSJoe Komlodi     .endianness = DEVICE_LITTLE_ENDIAN,
16937d87775fSJoe Komlodi };
16947d87775fSJoe Komlodi 
16957d87775fSJoe Komlodi static void aspeed_i3c_device_reset(DeviceState *dev)
16967d87775fSJoe Komlodi {
16977d87775fSJoe Komlodi     AspeedI3CDevice *s = ASPEED_I3C_DEVICE(dev);
16987d87775fSJoe Komlodi 
16997d87775fSJoe Komlodi     memcpy(s->regs, ast2600_i3c_device_resets, sizeof(s->regs));
17007d87775fSJoe Komlodi }
17017d87775fSJoe Komlodi 
17027d87775fSJoe Komlodi static void aspeed_i3c_device_realize(DeviceState *dev, Error **errp)
17037d87775fSJoe Komlodi {
17047d87775fSJoe Komlodi     AspeedI3CDevice *s = ASPEED_I3C_DEVICE(dev);
17057d87775fSJoe Komlodi     g_autofree char *name = g_strdup_printf(TYPE_ASPEED_I3C_DEVICE ".%d",
17067d87775fSJoe Komlodi                                             s->id);
17077d87775fSJoe Komlodi 
17087d87775fSJoe Komlodi     sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq);
17097d87775fSJoe Komlodi 
17107d87775fSJoe Komlodi     memory_region_init_io(&s->mr, OBJECT(s), &aspeed_i3c_device_ops,
17117d87775fSJoe Komlodi                           s, name, ASPEED_I3C_DEVICE_NR_REGS << 2);
17124ba25376SJoe Komlodi 
17134ba25376SJoe Komlodi     fifo32_create(&s->cmd_queue, ASPEED_I3C_CMD_QUEUE_CAPACITY);
17144ba25376SJoe Komlodi     fifo32_create(&s->resp_queue, ASPEED_I3C_RESP_QUEUE_CAPACITY);
17154ba25376SJoe Komlodi     fifo32_create(&s->tx_queue, ASPEED_I3C_TX_QUEUE_CAPACITY);
17164ba25376SJoe Komlodi     fifo32_create(&s->rx_queue, ASPEED_I3C_RX_QUEUE_CAPACITY);
1717*93ec6949SJoe Komlodi     fifo32_create(&s->ibi_queue, ASPEED_I3C_IBI_QUEUE_CAPACITY);
1718*93ec6949SJoe Komlodi     /* Arbitrarily large enough to not be an issue. */
1719*93ec6949SJoe Komlodi     fifo8_create(&s->ibi_data.ibi_intermediate_queue,
1720*93ec6949SJoe Komlodi                   ASPEED_I3C_IBI_QUEUE_CAPACITY * 8);
17214ba25376SJoe Komlodi 
17224ba25376SJoe Komlodi     s->bus = i3c_init_bus(DEVICE(s), name);
1723*93ec6949SJoe Komlodi     I3CBusClass *bc = I3C_BUS_GET_CLASS(s->bus);
1724*93ec6949SJoe Komlodi     bc->ibi_handle = aspeed_i3c_device_ibi_handle;
1725*93ec6949SJoe Komlodi     bc->ibi_recv = aspeed_i3c_device_ibi_recv;
1726*93ec6949SJoe Komlodi     bc->ibi_finish = aspeed_i3c_device_ibi_finish;
17277d87775fSJoe Komlodi }
17287d87775fSJoe Komlodi 
17297d87775fSJoe Komlodi static uint64_t aspeed_i3c_read(void *opaque, hwaddr addr, unsigned int size)
17307d87775fSJoe Komlodi {
17317d87775fSJoe Komlodi     AspeedI3CState *s = ASPEED_I3C(opaque);
17327d87775fSJoe Komlodi     uint64_t val = 0;
17337d87775fSJoe Komlodi 
17347d87775fSJoe Komlodi     val = s->regs[addr >> 2];
17357d87775fSJoe Komlodi 
17367d87775fSJoe Komlodi     trace_aspeed_i3c_read(addr, val);
17377d87775fSJoe Komlodi 
17387d87775fSJoe Komlodi     return val;
17397d87775fSJoe Komlodi }
17407d87775fSJoe Komlodi 
17417d87775fSJoe Komlodi static void aspeed_i3c_write(void *opaque,
17427d87775fSJoe Komlodi                              hwaddr addr,
17437d87775fSJoe Komlodi                              uint64_t data,
17447d87775fSJoe Komlodi                              unsigned int size)
17457d87775fSJoe Komlodi {
17467d87775fSJoe Komlodi     AspeedI3CState *s = ASPEED_I3C(opaque);
17477d87775fSJoe Komlodi 
17487d87775fSJoe Komlodi     trace_aspeed_i3c_write(addr, data);
17497d87775fSJoe Komlodi 
17507d87775fSJoe Komlodi     addr >>= 2;
17517d87775fSJoe Komlodi 
1752a9d3f922SJoe Komlodi     data &= ~ast2600_i3c_controller_ro[addr];
17537d87775fSJoe Komlodi     /* I3C controller register */
17547d87775fSJoe Komlodi     switch (addr) {
17557d87775fSJoe Komlodi     case R_I3C1_REG1:
17567d87775fSJoe Komlodi     case R_I3C2_REG1:
17577d87775fSJoe Komlodi     case R_I3C3_REG1:
17587d87775fSJoe Komlodi     case R_I3C4_REG1:
17597d87775fSJoe Komlodi     case R_I3C5_REG1:
17607d87775fSJoe Komlodi     case R_I3C6_REG1:
17617d87775fSJoe Komlodi         if (data & R_I3C1_REG1_I2C_MODE_MASK) {
17627d87775fSJoe Komlodi             qemu_log_mask(LOG_UNIMP,
17637d87775fSJoe Komlodi                           "%s: Unsupported I2C mode [0x%08" HWADDR_PRIx
17647d87775fSJoe Komlodi                           "]=%08" PRIx64 "\n",
17657d87775fSJoe Komlodi                           __func__, addr << 2, data);
17667d87775fSJoe Komlodi             break;
17677d87775fSJoe Komlodi         }
17687d87775fSJoe Komlodi         if (data & R_I3C1_REG1_SA_EN_MASK) {
17697d87775fSJoe Komlodi             qemu_log_mask(LOG_UNIMP,
17707d87775fSJoe Komlodi                           "%s: Unsupported slave mode [%08" HWADDR_PRIx
17717d87775fSJoe Komlodi                           "]=0x%08" PRIx64 "\n",
17727d87775fSJoe Komlodi                           __func__, addr << 2, data);
17737d87775fSJoe Komlodi             break;
17747d87775fSJoe Komlodi         }
17757d87775fSJoe Komlodi         s->regs[addr] = data;
17767d87775fSJoe Komlodi         break;
17777d87775fSJoe Komlodi     default:
17787d87775fSJoe Komlodi         s->regs[addr] = data;
17797d87775fSJoe Komlodi         break;
17807d87775fSJoe Komlodi     }
17817d87775fSJoe Komlodi }
17827d87775fSJoe Komlodi 
17837d87775fSJoe Komlodi static const MemoryRegionOps aspeed_i3c_ops = {
17847d87775fSJoe Komlodi     .read = aspeed_i3c_read,
17857d87775fSJoe Komlodi     .write = aspeed_i3c_write,
17867d87775fSJoe Komlodi     .endianness = DEVICE_LITTLE_ENDIAN,
17877d87775fSJoe Komlodi     .valid = {
17887d87775fSJoe Komlodi         .min_access_size = 1,
17897d87775fSJoe Komlodi         .max_access_size = 4,
17907d87775fSJoe Komlodi     }
17917d87775fSJoe Komlodi };
17927d87775fSJoe Komlodi 
17937d87775fSJoe Komlodi static void aspeed_i3c_reset(DeviceState *dev)
17947d87775fSJoe Komlodi {
17957d87775fSJoe Komlodi     AspeedI3CState *s = ASPEED_I3C(dev);
17967d87775fSJoe Komlodi     memset(s->regs, 0, sizeof(s->regs));
17977d87775fSJoe Komlodi }
17987d87775fSJoe Komlodi 
17997d87775fSJoe Komlodi static void aspeed_i3c_instance_init(Object *obj)
18007d87775fSJoe Komlodi {
18017d87775fSJoe Komlodi     AspeedI3CState *s = ASPEED_I3C(obj);
18027d87775fSJoe Komlodi     int i;
18037d87775fSJoe Komlodi 
18047d87775fSJoe Komlodi     for (i = 0; i < ASPEED_I3C_NR_DEVICES; ++i) {
18057d87775fSJoe Komlodi         object_initialize_child(obj, "device[*]", &s->devices[i],
18067d87775fSJoe Komlodi                 TYPE_ASPEED_I3C_DEVICE);
18077d87775fSJoe Komlodi     }
18087d87775fSJoe Komlodi }
18097d87775fSJoe Komlodi 
18107d87775fSJoe Komlodi static void aspeed_i3c_realize(DeviceState *dev, Error **errp)
18117d87775fSJoe Komlodi {
18127d87775fSJoe Komlodi     int i;
18137d87775fSJoe Komlodi     AspeedI3CState *s = ASPEED_I3C(dev);
18147d87775fSJoe Komlodi     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
18157d87775fSJoe Komlodi 
18167d87775fSJoe Komlodi     memory_region_init(&s->iomem_container, OBJECT(s),
18177d87775fSJoe Komlodi             TYPE_ASPEED_I3C ".container", 0x8000);
18187d87775fSJoe Komlodi 
18197d87775fSJoe Komlodi     sysbus_init_mmio(sbd, &s->iomem_container);
18207d87775fSJoe Komlodi 
18217d87775fSJoe Komlodi     memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_i3c_ops, s,
18227d87775fSJoe Komlodi             TYPE_ASPEED_I3C ".regs", ASPEED_I3C_NR_REGS << 2);
18237d87775fSJoe Komlodi 
18247d87775fSJoe Komlodi     memory_region_add_subregion(&s->iomem_container, 0x0, &s->iomem);
18257d87775fSJoe Komlodi 
18267d87775fSJoe Komlodi     for (i = 0; i < ASPEED_I3C_NR_DEVICES; ++i) {
18277d87775fSJoe Komlodi         Object *i3c_dev = OBJECT(&s->devices[i]);
18287d87775fSJoe Komlodi 
18297d87775fSJoe Komlodi         if (!object_property_set_uint(i3c_dev, "device-id", i, errp)) {
18307d87775fSJoe Komlodi             return;
18317d87775fSJoe Komlodi         }
18327d87775fSJoe Komlodi 
18337d87775fSJoe Komlodi         if (!sysbus_realize(SYS_BUS_DEVICE(i3c_dev), errp)) {
18347d87775fSJoe Komlodi             return;
18357d87775fSJoe Komlodi         }
18367d87775fSJoe Komlodi 
18377d87775fSJoe Komlodi         /*
18387d87775fSJoe Komlodi          * Register Address of I3CX Device =
18397d87775fSJoe Komlodi          *     (Base Address of Global Register) + (Offset of I3CX) + Offset
18407d87775fSJoe Komlodi          * X = 0, 1, 2, 3, 4, 5
18417d87775fSJoe Komlodi          * Offset of I3C0 = 0x2000
18427d87775fSJoe Komlodi          * Offset of I3C1 = 0x3000
18437d87775fSJoe Komlodi          * Offset of I3C2 = 0x4000
18447d87775fSJoe Komlodi          * Offset of I3C3 = 0x5000
18457d87775fSJoe Komlodi          * Offset of I3C4 = 0x6000
18467d87775fSJoe Komlodi          * Offset of I3C5 = 0x7000
18477d87775fSJoe Komlodi          */
18487d87775fSJoe Komlodi         memory_region_add_subregion(&s->iomem_container,
18497d87775fSJoe Komlodi                 0x2000 + i * 0x1000, &s->devices[i].mr);
18507d87775fSJoe Komlodi     }
18517d87775fSJoe Komlodi 
18527d87775fSJoe Komlodi }
18537d87775fSJoe Komlodi 
18547d87775fSJoe Komlodi static Property aspeed_i3c_device_properties[] = {
18557d87775fSJoe Komlodi     DEFINE_PROP_UINT8("device-id", AspeedI3CDevice, id, 0),
18567d87775fSJoe Komlodi     DEFINE_PROP_END_OF_LIST(),
18577d87775fSJoe Komlodi };
18587d87775fSJoe Komlodi 
18597d87775fSJoe Komlodi static void aspeed_i3c_device_class_init(ObjectClass *klass, void *data)
18607d87775fSJoe Komlodi {
18617d87775fSJoe Komlodi     DeviceClass *dc = DEVICE_CLASS(klass);
18627d87775fSJoe Komlodi 
18637d87775fSJoe Komlodi     dc->desc = "Aspeed I3C Device";
18647d87775fSJoe Komlodi     dc->realize = aspeed_i3c_device_realize;
18657d87775fSJoe Komlodi     device_class_set_legacy_reset(dc, aspeed_i3c_device_reset);
18667d87775fSJoe Komlodi     device_class_set_props(dc, aspeed_i3c_device_properties);
18677d87775fSJoe Komlodi }
18687d87775fSJoe Komlodi 
18697d87775fSJoe Komlodi static const TypeInfo aspeed_i3c_device_info = {
18707d87775fSJoe Komlodi     .name = TYPE_ASPEED_I3C_DEVICE,
18717d87775fSJoe Komlodi     .parent = TYPE_SYS_BUS_DEVICE,
18727d87775fSJoe Komlodi     .instance_size = sizeof(AspeedI3CDevice),
18737d87775fSJoe Komlodi     .class_init = aspeed_i3c_device_class_init,
18747d87775fSJoe Komlodi };
18757d87775fSJoe Komlodi 
18767d87775fSJoe Komlodi static const VMStateDescription vmstate_aspeed_i3c = {
18777d87775fSJoe Komlodi     .name = TYPE_ASPEED_I3C,
18787d87775fSJoe Komlodi     .version_id = 1,
18797d87775fSJoe Komlodi     .minimum_version_id = 1,
18807d87775fSJoe Komlodi     .fields = (const VMStateField[]) {
18817d87775fSJoe Komlodi         VMSTATE_UINT32_ARRAY(regs, AspeedI3CState, ASPEED_I3C_NR_REGS),
18827d87775fSJoe Komlodi         VMSTATE_STRUCT_ARRAY(devices, AspeedI3CState, ASPEED_I3C_NR_DEVICES, 1,
18837d87775fSJoe Komlodi                              aspeed_i3c_device_vmstate, AspeedI3CDevice),
18847d87775fSJoe Komlodi         VMSTATE_END_OF_LIST(),
18857d87775fSJoe Komlodi     }
18867d87775fSJoe Komlodi };
18877d87775fSJoe Komlodi 
18887d87775fSJoe Komlodi static void aspeed_i3c_class_init(ObjectClass *klass, void *data)
18897d87775fSJoe Komlodi {
18907d87775fSJoe Komlodi     DeviceClass *dc = DEVICE_CLASS(klass);
18917d87775fSJoe Komlodi 
18927d87775fSJoe Komlodi     dc->realize = aspeed_i3c_realize;
18937d87775fSJoe Komlodi     device_class_set_legacy_reset(dc, aspeed_i3c_reset);
18947d87775fSJoe Komlodi     dc->desc = "Aspeed I3C Controller";
18957d87775fSJoe Komlodi     dc->vmsd = &vmstate_aspeed_i3c;
18967d87775fSJoe Komlodi }
18977d87775fSJoe Komlodi 
18987d87775fSJoe Komlodi static const TypeInfo aspeed_i3c_info = {
18997d87775fSJoe Komlodi     .name = TYPE_ASPEED_I3C,
19007d87775fSJoe Komlodi     .parent = TYPE_SYS_BUS_DEVICE,
19017d87775fSJoe Komlodi     .instance_init = aspeed_i3c_instance_init,
19027d87775fSJoe Komlodi     .instance_size = sizeof(AspeedI3CState),
19037d87775fSJoe Komlodi     .class_init = aspeed_i3c_class_init,
19047d87775fSJoe Komlodi };
19057d87775fSJoe Komlodi 
19067d87775fSJoe Komlodi static void aspeed_i3c_register_types(void)
19077d87775fSJoe Komlodi {
19087d87775fSJoe Komlodi     type_register_static(&aspeed_i3c_device_info);
19097d87775fSJoe Komlodi     type_register_static(&aspeed_i3c_info);
19107d87775fSJoe Komlodi }
19117d87775fSJoe Komlodi 
19127d87775fSJoe Komlodi type_init(aspeed_i3c_register_types);
1913