xref: /openbmc/qemu/hw/i3c/dw-i3c.c (revision 5a2a5453bac7daafcdf0601a91009e01345b1dd2)
1c52aaabdSJoe Komlodi /*
2c52aaabdSJoe Komlodi  * DesignWare I3C Controller
3c52aaabdSJoe Komlodi  *
4c52aaabdSJoe Komlodi  * Copyright (C) 2021 ASPEED Technology Inc.
5c52aaabdSJoe Komlodi  * Copyright (C) 2025 Google, LLC
6c52aaabdSJoe Komlodi  *
7c52aaabdSJoe Komlodi  * SPDX-License-Identifier: GPL-2.0-or-later
8c52aaabdSJoe Komlodi  */
9c52aaabdSJoe Komlodi 
10c52aaabdSJoe Komlodi #include "qemu/osdep.h"
11c52aaabdSJoe Komlodi #include "qemu/log.h"
12c52aaabdSJoe Komlodi #include "qemu/error-report.h"
13c52aaabdSJoe Komlodi #include "hw/i3c/i3c.h"
14c52aaabdSJoe Komlodi #include "hw/i3c/dw-i3c.h"
15c52aaabdSJoe Komlodi #include "hw/registerfields.h"
16c52aaabdSJoe Komlodi #include "hw/qdev-properties.h"
17c52aaabdSJoe Komlodi #include "qapi/error.h"
18c52aaabdSJoe Komlodi #include "migration/vmstate.h"
19c52aaabdSJoe Komlodi #include "trace.h"
20a825bbb7SJoe Komlodi #include "hw/irq.h"
21c52aaabdSJoe Komlodi 
22*5a2a5453SJoe Komlodi /*
23*5a2a5453SJoe Komlodi  * Disable event command values. sent along with a DISEC CCC to disable certain
24*5a2a5453SJoe Komlodi  * events on targets.
25*5a2a5453SJoe Komlodi  */
26*5a2a5453SJoe Komlodi #define DISEC_HJ 0x08
27*5a2a5453SJoe Komlodi #define DISEC_CR 0x02
28*5a2a5453SJoe Komlodi #define DISEC_INT 0x01
29*5a2a5453SJoe Komlodi 
30c52aaabdSJoe Komlodi REG32(DEVICE_CTRL,                  0x00)
3159b8d0c9SJoe Komlodi     FIELD(DEVICE_CTRL, I3C_BROADCAST_ADDR_INC,    0, 1)
3259b8d0c9SJoe Komlodi     FIELD(DEVICE_CTRL, I2C_SLAVE_PRESENT,         7, 1)
3359b8d0c9SJoe Komlodi     FIELD(DEVICE_CTRL, HOT_JOIN_ACK_NACK_CTRL,    8, 1)
3459b8d0c9SJoe Komlodi     FIELD(DEVICE_CTRL, IDLE_CNT_MULTIPLIER,       24, 2)
3559b8d0c9SJoe Komlodi     FIELD(DEVICE_CTRL, SLV_ADAPT_TO_I2C_I3C_MODE, 27, 1)
3659b8d0c9SJoe Komlodi     FIELD(DEVICE_CTRL, DMA_HANDSHAKE_EN,          28, 1)
3759b8d0c9SJoe Komlodi     FIELD(DEVICE_CTRL, I3C_ABORT,                 29, 1)
3859b8d0c9SJoe Komlodi     FIELD(DEVICE_CTRL, I3C_RESUME,                30, 1)
3959b8d0c9SJoe Komlodi     FIELD(DEVICE_CTRL, I3C_EN,                    31, 1)
40c52aaabdSJoe Komlodi REG32(DEVICE_ADDR,                  0x04)
4159b8d0c9SJoe Komlodi     FIELD(DEVICE_ADDR, STATIC_ADDR,         0, 7)
4259b8d0c9SJoe Komlodi     FIELD(DEVICE_ADDR, STATIC_ADDR_VALID,   15, 1)
4359b8d0c9SJoe Komlodi     FIELD(DEVICE_ADDR, DYNAMIC_ADDR,        16, 7)
4459b8d0c9SJoe Komlodi     FIELD(DEVICE_ADDR, DYNAMIC_ADDR_VALID,  15, 1)
45c52aaabdSJoe Komlodi REG32(HW_CAPABILITY,                0x08)
4659b8d0c9SJoe Komlodi     FIELD(HW_CAPABILITY, DEVICE_ROLE_CONFIG,  0, 2)
4759b8d0c9SJoe Komlodi     FIELD(HW_CAPABILITY, HDR_DDR, 3, 1)
4859b8d0c9SJoe Komlodi     FIELD(HW_CAPABILITY, HDR_TS,  4, 1)
49c52aaabdSJoe Komlodi REG32(COMMAND_QUEUE_PORT,           0x0c)
5059b8d0c9SJoe Komlodi     FIELD(COMMAND_QUEUE_PORT, CMD_ATTR, 0, 3)
5159b8d0c9SJoe Komlodi     /* Transfer command structure */
5259b8d0c9SJoe Komlodi     FIELD(COMMAND_QUEUE_PORT, TID, 3, 4)
5359b8d0c9SJoe Komlodi     FIELD(COMMAND_QUEUE_PORT, CMD, 7, 8)
5459b8d0c9SJoe Komlodi     FIELD(COMMAND_QUEUE_PORT, CP, 15, 1)
5559b8d0c9SJoe Komlodi     FIELD(COMMAND_QUEUE_PORT, DEV_INDEX, 16, 5)
5659b8d0c9SJoe Komlodi     FIELD(COMMAND_QUEUE_PORT, SPEED, 21, 3)
5759b8d0c9SJoe Komlodi     FIELD(COMMAND_QUEUE_PORT, ROC, 26, 1)
5859b8d0c9SJoe Komlodi     FIELD(COMMAND_QUEUE_PORT, SDAP, 27, 1)
5959b8d0c9SJoe Komlodi     FIELD(COMMAND_QUEUE_PORT, RNW, 28, 1)
6059b8d0c9SJoe Komlodi     FIELD(COMMAND_QUEUE_PORT, TOC, 30, 1)
6159b8d0c9SJoe Komlodi     FIELD(COMMAND_QUEUE_PORT, PEC, 31, 1)
6259b8d0c9SJoe Komlodi     /* Transfer argument data structure */
6359b8d0c9SJoe Komlodi     FIELD(COMMAND_QUEUE_PORT, DB, 8, 8)
6459b8d0c9SJoe Komlodi     FIELD(COMMAND_QUEUE_PORT, DL, 16, 16)
6559b8d0c9SJoe Komlodi     /* Short data argument data structure */
6659b8d0c9SJoe Komlodi     FIELD(COMMAND_QUEUE_PORT, BYTE_STRB, 3, 3)
6759b8d0c9SJoe Komlodi     FIELD(COMMAND_QUEUE_PORT, BYTE0, 8, 8)
6859b8d0c9SJoe Komlodi     FIELD(COMMAND_QUEUE_PORT, BYTE1, 16, 8)
6959b8d0c9SJoe Komlodi     FIELD(COMMAND_QUEUE_PORT, BYTE2, 24, 8)
7059b8d0c9SJoe Komlodi     /* Address assignment command structure */
7159b8d0c9SJoe Komlodi     /*
7259b8d0c9SJoe Komlodi      * bits 3..21 and 26..31 are the same as the transfer command structure, or
7359b8d0c9SJoe Komlodi      * marked as reserved.
7459b8d0c9SJoe Komlodi      */
7559b8d0c9SJoe Komlodi     FIELD(COMMAND_QUEUE_PORT, DEV_COUNT, 21, 3)
76c52aaabdSJoe Komlodi REG32(RESPONSE_QUEUE_PORT,          0x10)
7759b8d0c9SJoe Komlodi     FIELD(RESPONSE_QUEUE_PORT, DL, 0, 16)
7859b8d0c9SJoe Komlodi     FIELD(RESPONSE_QUEUE_PORT, CCCT, 16, 8)
7959b8d0c9SJoe Komlodi     FIELD(RESPONSE_QUEUE_PORT, TID, 24, 4)
8059b8d0c9SJoe Komlodi     FIELD(RESPONSE_QUEUE_PORT, ERR_STATUS, 28, 4)
81c52aaabdSJoe Komlodi REG32(RX_TX_DATA_PORT,              0x14)
82c52aaabdSJoe Komlodi REG32(IBI_QUEUE_STATUS,             0x18)
8359b8d0c9SJoe Komlodi     FIELD(IBI_QUEUE_STATUS, IBI_DATA_LEN,   0, 8)
8459b8d0c9SJoe Komlodi     FIELD(IBI_QUEUE_STATUS, IBI_ID,         8, 8)
8559b8d0c9SJoe Komlodi     FIELD(IBI_QUEUE_STATUS, LAST_STATUS,    24, 1)
8659b8d0c9SJoe Komlodi     FIELD(IBI_QUEUE_STATUS, ERROR,          30, 1)
8759b8d0c9SJoe Komlodi     FIELD(IBI_QUEUE_STATUS, IBI_STATUS,     31, 1)
88c52aaabdSJoe Komlodi REG32(IBI_QUEUE_DATA,               0x18)
89c52aaabdSJoe Komlodi REG32(QUEUE_THLD_CTRL,              0x1c)
9059b8d0c9SJoe Komlodi     FIELD(QUEUE_THLD_CTRL, CMD_BUF_EMPTY_THLD,  0, 8);
9159b8d0c9SJoe Komlodi     FIELD(QUEUE_THLD_CTRL, RESP_BUF_THLD, 8, 8);
9259b8d0c9SJoe Komlodi     FIELD(QUEUE_THLD_CTRL, IBI_DATA_THLD, 16, 8);
9359b8d0c9SJoe Komlodi     FIELD(QUEUE_THLD_CTRL, IBI_STATUS_THLD,     24, 8);
94c52aaabdSJoe Komlodi REG32(DATA_BUFFER_THLD_CTRL,        0x20)
9559b8d0c9SJoe Komlodi     FIELD(DATA_BUFFER_THLD_CTRL, TX_BUF_THLD,   0, 3)
9659b8d0c9SJoe Komlodi     FIELD(DATA_BUFFER_THLD_CTRL, RX_BUF_THLD,   10, 3)
9759b8d0c9SJoe Komlodi     FIELD(DATA_BUFFER_THLD_CTRL, TX_START_THLD, 16, 3)
9859b8d0c9SJoe Komlodi     FIELD(DATA_BUFFER_THLD_CTRL, RX_START_THLD, 24, 3)
99c52aaabdSJoe Komlodi REG32(IBI_QUEUE_CTRL,               0x24)
10059b8d0c9SJoe Komlodi     FIELD(IBI_QUEUE_CTRL, NOTIFY_REJECTED_HOT_JOIN,   0, 1)
10159b8d0c9SJoe Komlodi     FIELD(IBI_QUEUE_CTRL, NOTIFY_REJECTED_MASTER_REQ, 1, 1)
10259b8d0c9SJoe Komlodi     FIELD(IBI_QUEUE_CTRL, NOTIFY_REJECTED_SLAVE_IRQ,  3, 1)
103c52aaabdSJoe Komlodi REG32(IBI_MR_REQ_REJECT,            0x2c)
104c52aaabdSJoe Komlodi REG32(IBI_SIR_REQ_REJECT,           0x30)
105c52aaabdSJoe Komlodi REG32(RESET_CTRL,                   0x34)
10659b8d0c9SJoe Komlodi     FIELD(RESET_CTRL, CORE_RESET,       0, 1)
10759b8d0c9SJoe Komlodi     FIELD(RESET_CTRL, CMD_QUEUE_RESET,  1, 1)
10859b8d0c9SJoe Komlodi     FIELD(RESET_CTRL, RESP_QUEUE_RESET, 2, 1)
10959b8d0c9SJoe Komlodi     FIELD(RESET_CTRL, TX_BUF_RESET,     3, 1)
11059b8d0c9SJoe Komlodi     FIELD(RESET_CTRL, RX_BUF_RESET,     4, 1)
11159b8d0c9SJoe Komlodi     FIELD(RESET_CTRL, IBI_QUEUE_RESET,  5, 1)
112c52aaabdSJoe Komlodi REG32(SLV_EVENT_CTRL,               0x38)
11359b8d0c9SJoe Komlodi     FIELD(SLV_EVENT_CTRL, SLV_INTERRUPT,      0, 1)
11459b8d0c9SJoe Komlodi     FIELD(SLV_EVENT_CTRL, MASTER_INTERRUPT,   1, 1)
11559b8d0c9SJoe Komlodi     FIELD(SLV_EVENT_CTRL, HOT_JOIN_INTERRUPT, 3, 1)
11659b8d0c9SJoe Komlodi     FIELD(SLV_EVENT_CTRL, ACTIVITY_STATE,     4, 2)
11759b8d0c9SJoe Komlodi     FIELD(SLV_EVENT_CTRL, MRL_UPDATED,        6, 1)
11859b8d0c9SJoe Komlodi     FIELD(SLV_EVENT_CTRL, MWL_UPDATED,        7, 1)
119c52aaabdSJoe Komlodi REG32(INTR_STATUS,                  0x3c)
12059b8d0c9SJoe Komlodi     FIELD(INTR_STATUS, TX_THLD,           0, 1)
12159b8d0c9SJoe Komlodi     FIELD(INTR_STATUS, RX_THLD,           1, 1)
12259b8d0c9SJoe Komlodi     FIELD(INTR_STATUS, IBI_THLD,          2, 1)
12359b8d0c9SJoe Komlodi     FIELD(INTR_STATUS, CMD_QUEUE_RDY,     3, 1)
12459b8d0c9SJoe Komlodi     FIELD(INTR_STATUS, RESP_RDY,          4, 1)
12559b8d0c9SJoe Komlodi     FIELD(INTR_STATUS, TRANSFER_ABORT,    5, 1)
12659b8d0c9SJoe Komlodi     FIELD(INTR_STATUS, CCC_UPDATED,       6, 1)
12759b8d0c9SJoe Komlodi     FIELD(INTR_STATUS, DYN_ADDR_ASSGN,    8, 1)
12859b8d0c9SJoe Komlodi     FIELD(INTR_STATUS, TRANSFER_ERR,      9, 1)
12959b8d0c9SJoe Komlodi     FIELD(INTR_STATUS, DEFSLV,            10, 1)
13059b8d0c9SJoe Komlodi     FIELD(INTR_STATUS, READ_REQ_RECV,     11, 1)
13159b8d0c9SJoe Komlodi     FIELD(INTR_STATUS, IBI_UPDATED,       12, 1)
13259b8d0c9SJoe Komlodi     FIELD(INTR_STATUS, BUSOWNER_UPDATED,  13, 1)
133c52aaabdSJoe Komlodi REG32(INTR_STATUS_EN,               0x40)
13459b8d0c9SJoe Komlodi     FIELD(INTR_STATUS_EN, TX_THLD,          0, 1)
13559b8d0c9SJoe Komlodi     FIELD(INTR_STATUS_EN, RX_THLD,          1, 1)
13659b8d0c9SJoe Komlodi     FIELD(INTR_STATUS_EN, IBI_THLD,         2, 1)
13759b8d0c9SJoe Komlodi     FIELD(INTR_STATUS_EN, CMD_QUEUE_RDY,    3, 1)
13859b8d0c9SJoe Komlodi     FIELD(INTR_STATUS_EN, RESP_RDY,         4, 1)
13959b8d0c9SJoe Komlodi     FIELD(INTR_STATUS_EN, TRANSFER_ABORT,   5, 1)
14059b8d0c9SJoe Komlodi     FIELD(INTR_STATUS_EN, CCC_UPDATED,      6, 1)
14159b8d0c9SJoe Komlodi     FIELD(INTR_STATUS_EN, DYN_ADDR_ASSGN,   8, 1)
14259b8d0c9SJoe Komlodi     FIELD(INTR_STATUS_EN, TRANSFER_ERR,     9, 1)
14359b8d0c9SJoe Komlodi     FIELD(INTR_STATUS_EN, DEFSLV,           10, 1)
14459b8d0c9SJoe Komlodi     FIELD(INTR_STATUS_EN, READ_REQ_RECV,    11, 1)
14559b8d0c9SJoe Komlodi     FIELD(INTR_STATUS_EN, IBI_UPDATED,      12, 1)
14659b8d0c9SJoe Komlodi     FIELD(INTR_STATUS_EN, BUSOWNER_UPDATED, 13, 1)
147c52aaabdSJoe Komlodi REG32(INTR_SIGNAL_EN,               0x44)
14859b8d0c9SJoe Komlodi     FIELD(INTR_SIGNAL_EN, TX_THLD,          0, 1)
14959b8d0c9SJoe Komlodi     FIELD(INTR_SIGNAL_EN, RX_THLD,          1, 1)
15059b8d0c9SJoe Komlodi     FIELD(INTR_SIGNAL_EN, IBI_THLD,         2, 1)
15159b8d0c9SJoe Komlodi     FIELD(INTR_SIGNAL_EN, CMD_QUEUE_RDY,    3, 1)
15259b8d0c9SJoe Komlodi     FIELD(INTR_SIGNAL_EN, RESP_RDY,         4, 1)
15359b8d0c9SJoe Komlodi     FIELD(INTR_SIGNAL_EN, TRANSFER_ABORT,   5, 1)
15459b8d0c9SJoe Komlodi     FIELD(INTR_SIGNAL_EN, CCC_UPDATED,      6, 1)
15559b8d0c9SJoe Komlodi     FIELD(INTR_SIGNAL_EN, DYN_ADDR_ASSGN,   8, 1)
15659b8d0c9SJoe Komlodi     FIELD(INTR_SIGNAL_EN, TRANSFER_ERR,     9, 1)
15759b8d0c9SJoe Komlodi     FIELD(INTR_SIGNAL_EN, DEFSLV,           10, 1)
15859b8d0c9SJoe Komlodi     FIELD(INTR_SIGNAL_EN, READ_REQ_RECV,    11, 1)
15959b8d0c9SJoe Komlodi     FIELD(INTR_SIGNAL_EN, IBI_UPDATED,      12, 1)
16059b8d0c9SJoe Komlodi     FIELD(INTR_SIGNAL_EN, BUSOWNER_UPDATED, 13, 1)
161c52aaabdSJoe Komlodi REG32(INTR_FORCE,                   0x48)
16259b8d0c9SJoe Komlodi     FIELD(INTR_FORCE, TX_THLD,          0, 1)
16359b8d0c9SJoe Komlodi     FIELD(INTR_FORCE, RX_THLD,          1, 1)
16459b8d0c9SJoe Komlodi     FIELD(INTR_FORCE, IBI_THLD,         2, 1)
16559b8d0c9SJoe Komlodi     FIELD(INTR_FORCE, CMD_QUEUE_RDY,    3, 1)
16659b8d0c9SJoe Komlodi     FIELD(INTR_FORCE, RESP_RDY,         4, 1)
16759b8d0c9SJoe Komlodi     FIELD(INTR_FORCE, TRANSFER_ABORT,   5, 1)
16859b8d0c9SJoe Komlodi     FIELD(INTR_FORCE, CCC_UPDATED,      6, 1)
16959b8d0c9SJoe Komlodi     FIELD(INTR_FORCE, DYN_ADDR_ASSGN,   8, 1)
17059b8d0c9SJoe Komlodi     FIELD(INTR_FORCE, TRANSFER_ERR,     9, 1)
17159b8d0c9SJoe Komlodi     FIELD(INTR_FORCE, DEFSLV,           10, 1)
17259b8d0c9SJoe Komlodi     FIELD(INTR_FORCE, READ_REQ_RECV,    11, 1)
17359b8d0c9SJoe Komlodi     FIELD(INTR_FORCE, IBI_UPDATED,      12, 1)
17459b8d0c9SJoe Komlodi     FIELD(INTR_FORCE, BUSOWNER_UPDATED, 13, 1)
175c52aaabdSJoe Komlodi REG32(QUEUE_STATUS_LEVEL,           0x4c)
17659b8d0c9SJoe Komlodi     FIELD(QUEUE_STATUS_LEVEL, CMD_QUEUE_EMPTY_LOC,  0, 8)
17759b8d0c9SJoe Komlodi     FIELD(QUEUE_STATUS_LEVEL, RESP_BUF_BLR,         8, 8)
17859b8d0c9SJoe Komlodi     FIELD(QUEUE_STATUS_LEVEL, IBI_BUF_BLR,          16, 8)
17959b8d0c9SJoe Komlodi     FIELD(QUEUE_STATUS_LEVEL, IBI_STATUS_CNT,       24, 5)
180c52aaabdSJoe Komlodi REG32(DATA_BUFFER_STATUS_LEVEL,     0x50)
18159b8d0c9SJoe Komlodi     FIELD(DATA_BUFFER_STATUS_LEVEL, TX_BUF_EMPTY_LOC, 0, 8)
18259b8d0c9SJoe Komlodi     FIELD(DATA_BUFFER_STATUS_LEVEL, RX_BUF_BLR,       16, 8)
183c52aaabdSJoe Komlodi REG32(PRESENT_STATE,                0x54)
18459b8d0c9SJoe Komlodi     FIELD(PRESENT_STATE, SCL_LINE_SIGNAL_LEVEL, 0, 1)
18559b8d0c9SJoe Komlodi     FIELD(PRESENT_STATE, SDA_LINE_SIGNAL_LEVEL, 1, 1)
18659b8d0c9SJoe Komlodi     FIELD(PRESENT_STATE, CURRENT_MASTER,        2, 1)
18759b8d0c9SJoe Komlodi     FIELD(PRESENT_STATE, CM_TFR_STATUS,         8, 6)
18859b8d0c9SJoe Komlodi     FIELD(PRESENT_STATE, CM_TFR_ST_STATUS,      16, 6)
18959b8d0c9SJoe Komlodi     FIELD(PRESENT_STATE, CMD_TID,               24, 4)
190c52aaabdSJoe Komlodi REG32(CCC_DEVICE_STATUS,            0x58)
19159b8d0c9SJoe Komlodi     FIELD(CCC_DEVICE_STATUS, PENDING_INTR,      0, 4)
19259b8d0c9SJoe Komlodi     FIELD(CCC_DEVICE_STATUS, PROTOCOL_ERR,      4, 2)
19359b8d0c9SJoe Komlodi     FIELD(CCC_DEVICE_STATUS, ACTIVITY_MODE,     6, 2)
19459b8d0c9SJoe Komlodi     FIELD(CCC_DEVICE_STATUS, UNDER_ERR,         8, 1)
19559b8d0c9SJoe Komlodi     FIELD(CCC_DEVICE_STATUS, SLV_BUSY,          9, 1)
19659b8d0c9SJoe Komlodi     FIELD(CCC_DEVICE_STATUS, OVERFLOW_ERR,      10, 1)
19759b8d0c9SJoe Komlodi     FIELD(CCC_DEVICE_STATUS, DATA_NOT_READY,    11, 1)
19859b8d0c9SJoe Komlodi     FIELD(CCC_DEVICE_STATUS, BUFFER_NOT_AVAIL,  12, 1)
199c52aaabdSJoe Komlodi REG32(DEVICE_ADDR_TABLE_POINTER,    0x5c)
200c52aaabdSJoe Komlodi     FIELD(DEVICE_ADDR_TABLE_POINTER, DEPTH, 16, 16)
201c52aaabdSJoe Komlodi     FIELD(DEVICE_ADDR_TABLE_POINTER, ADDR,  0,  16)
202c52aaabdSJoe Komlodi REG32(DEV_CHAR_TABLE_POINTER,       0x60)
20359b8d0c9SJoe Komlodi     FIELD(DEV_CHAR_TABLE_POINTER, P_DEV_CHAR_TABLE_START_ADDR,  0, 12)
20459b8d0c9SJoe Komlodi     FIELD(DEV_CHAR_TABLE_POINTER, DEV_CHAR_TABLE_DEPTH,         12, 7)
20559b8d0c9SJoe Komlodi     FIELD(DEV_CHAR_TABLE_POINTER, PRESENT_DEV_CHAR_TABLE_INDEX, 19, 3)
206c52aaabdSJoe Komlodi REG32(VENDOR_SPECIFIC_REG_POINTER,  0x6c)
20759b8d0c9SJoe Komlodi     FIELD(VENDOR_SPECIFIC_REG_POINTER, P_VENDOR_REG_START_ADDR, 0, 16)
208c52aaabdSJoe Komlodi REG32(SLV_MIPI_PID_VALUE,           0x70)
209c52aaabdSJoe Komlodi REG32(SLV_PID_VALUE,                0x74)
21059b8d0c9SJoe Komlodi     FIELD(SLV_PID_VALUE, SLV_PID_DCR, 0, 12)
21159b8d0c9SJoe Komlodi     FIELD(SLV_PID_VALUE, SLV_INST_ID, 12, 4)
21259b8d0c9SJoe Komlodi     FIELD(SLV_PID_VALUE, SLV_PART_ID, 16, 16)
213c52aaabdSJoe Komlodi REG32(SLV_CHAR_CTRL,                0x78)
21459b8d0c9SJoe Komlodi     FIELD(SLV_CHAR_CTRL, BCR,     0, 8)
21559b8d0c9SJoe Komlodi     FIELD(SLV_CHAR_CTRL, DCR,     8, 8)
21659b8d0c9SJoe Komlodi     FIELD(SLV_CHAR_CTRL, HDR_CAP, 16, 8)
217c52aaabdSJoe Komlodi REG32(SLV_MAX_LEN,                  0x7c)
21859b8d0c9SJoe Komlodi     FIELD(SLV_MAX_LEN, MWL, 0, 16)
21959b8d0c9SJoe Komlodi     FIELD(SLV_MAX_LEN, MRL, 16, 16)
220c52aaabdSJoe Komlodi REG32(MAX_READ_TURNAROUND,          0x80)
221c52aaabdSJoe Komlodi REG32(MAX_DATA_SPEED,               0x84)
222c52aaabdSJoe Komlodi REG32(SLV_DEBUG_STATUS,             0x88)
223c52aaabdSJoe Komlodi REG32(SLV_INTR_REQ,                 0x8c)
22459b8d0c9SJoe Komlodi     FIELD(SLV_INTR_REQ, SIR,          0, 1)
22559b8d0c9SJoe Komlodi     FIELD(SLV_INTR_REQ, SIR_CTRL,     1, 2)
22659b8d0c9SJoe Komlodi     FIELD(SLV_INTR_REQ, MIR,          3, 1)
22759b8d0c9SJoe Komlodi     FIELD(SLV_INTR_REQ, TS,           4, 1)
22859b8d0c9SJoe Komlodi     FIELD(SLV_INTR_REQ, IBI_STS,      8, 2)
22959b8d0c9SJoe Komlodi     FIELD(SLV_INTR_REQ, MDB,          8, 8)
23059b8d0c9SJoe Komlodi     FIELD(SLV_INTR_REQ, SIR_DATA_LEN, 16, 8)
23159b8d0c9SJoe Komlodi REG32(SLV_TSX_SYMBL_TIMING,         0x90)
23259b8d0c9SJoe Komlodi     FIELD(SLV_TSX_SYMBL_TIMING, SLV_TSX_SYMBL_CNT, 0, 6)
23359b8d0c9SJoe Komlodi REG32(SLV_SIR_DATA,                 0x94)
23459b8d0c9SJoe Komlodi     FIELD(SLV_SIR_DATA, SIR_DATA_BYTE0, 0, 8)
23559b8d0c9SJoe Komlodi     FIELD(SLV_SIR_DATA, SIR_DATA_BYTE1, 8, 8)
23659b8d0c9SJoe Komlodi     FIELD(SLV_SIR_DATA, SIR_DATA_BYTE2, 16, 8)
23759b8d0c9SJoe Komlodi     FIELD(SLV_SIR_DATA, SIR_DATA_BYTE3, 24, 8)
23859b8d0c9SJoe Komlodi REG32(SLV_IBI_RESP,                 0x98)
23959b8d0c9SJoe Komlodi     FIELD(SLV_IBI_RESP, IBI_STS,           0, 2)
24059b8d0c9SJoe Komlodi     FIELD(SLV_IBI_RESP, SIR_RESP_DATA_LEN, 8, 16)
241c52aaabdSJoe Komlodi REG32(DEVICE_CTRL_EXTENDED,         0xb0)
24259b8d0c9SJoe Komlodi     FIELD(DEVICE_CTRL_EXTENDED, MODE, 0, 2)
24359b8d0c9SJoe Komlodi     FIELD(DEVICE_CTRL_EXTENDED, REQMST_ACK_CTRL, 3, 1)
244c52aaabdSJoe Komlodi REG32(SCL_I3C_OD_TIMING,            0xb4)
24559b8d0c9SJoe Komlodi     FIELD(SCL_I3C_OD_TIMING, I3C_OD_LCNT, 0, 8)
24659b8d0c9SJoe Komlodi     FIELD(SCL_I3C_OD_TIMING, I3C_OD_HCNT, 16, 8)
247c52aaabdSJoe Komlodi REG32(SCL_I3C_PP_TIMING,            0xb8)
24859b8d0c9SJoe Komlodi     FIELD(SCL_I3C_PP_TIMING, I3C_PP_LCNT, 0, 8)
24959b8d0c9SJoe Komlodi     FIELD(SCL_I3C_PP_TIMING, I3C_PP_HCNT, 16, 8)
250c52aaabdSJoe Komlodi REG32(SCL_I2C_FM_TIMING,            0xbc)
251c52aaabdSJoe Komlodi REG32(SCL_I2C_FMP_TIMING,           0xc0)
25259b8d0c9SJoe Komlodi     FIELD(SCL_I2C_FMP_TIMING, I2C_FMP_LCNT, 0, 16)
25359b8d0c9SJoe Komlodi     FIELD(SCL_I2C_FMP_TIMING, I2C_FMP_HCNT, 16, 8)
254c52aaabdSJoe Komlodi REG32(SCL_EXT_LCNT_TIMING,          0xc8)
255c52aaabdSJoe Komlodi REG32(SCL_EXT_TERMN_LCNT_TIMING,    0xcc)
256c52aaabdSJoe Komlodi REG32(BUS_FREE_TIMING,              0xd4)
257c52aaabdSJoe Komlodi REG32(BUS_IDLE_TIMING,              0xd8)
25859b8d0c9SJoe Komlodi     FIELD(BUS_IDLE_TIMING, BUS_IDLE_TIME, 0, 20)
259c52aaabdSJoe Komlodi REG32(I3C_VER_ID,                   0xe0)
260c52aaabdSJoe Komlodi REG32(I3C_VER_TYPE,                 0xe4)
261c52aaabdSJoe Komlodi REG32(EXTENDED_CAPABILITY,          0xe8)
26259b8d0c9SJoe Komlodi     FIELD(EXTENDED_CAPABILITY, APP_IF_MODE,       0, 2)
26359b8d0c9SJoe Komlodi     FIELD(EXTENDED_CAPABILITY, APP_IF_DATA_WIDTH, 2, 2)
26459b8d0c9SJoe Komlodi     FIELD(EXTENDED_CAPABILITY, OPERATION_MODE,    4, 2)
26559b8d0c9SJoe Komlodi     FIELD(EXTENDED_CAPABILITY, CLK_PERIOD,        8, 6)
266c52aaabdSJoe Komlodi REG32(SLAVE_CONFIG,                 0xec)
26759b8d0c9SJoe Komlodi     FIELD(SLAVE_CONFIG, DMA_EN,     0, 1)
26859b8d0c9SJoe Komlodi     FIELD(SLAVE_CONFIG, HJ_CAP,     0, 1)
26959b8d0c9SJoe Komlodi     FIELD(SLAVE_CONFIG, CLK_PERIOD, 2, 14)
27059b8d0c9SJoe Komlodi /* Device characteristic table fields */
27159b8d0c9SJoe Komlodi REG32(DEVICE_CHARACTERISTIC_TABLE_LOC1, 0x200)
27259b8d0c9SJoe Komlodi REG32(DEVICE_CHARACTERISTIC_TABLE_LOC_SECONDARY, 0x200)
27359b8d0c9SJoe Komlodi     FIELD(DEVICE_CHARACTERISTIC_TABLE_LOC_SECONDARY, DYNAMIC_ADDR, 0, 8)
27459b8d0c9SJoe Komlodi     FIELD(DEVICE_CHARACTERISTIC_TABLE_LOC_SECONDARY, DCR, 8, 8)
27559b8d0c9SJoe Komlodi     FIELD(DEVICE_CHARACTERISTIC_TABLE_LOC_SECONDARY, BCR, 16, 8)
27659b8d0c9SJoe Komlodi     FIELD(DEVICE_CHARACTERISTIC_TABLE_LOC_SECONDARY, STATIC_ADDR, 24, 8)
27759b8d0c9SJoe Komlodi REG32(DEVICE_CHARACTERISTIC_TABLE_LOC2, 0x204)
27859b8d0c9SJoe Komlodi     FIELD(DEVICE_CHARACTERISTIC_TABLE_LOC2, MSB_PID, 0, 16)
27959b8d0c9SJoe Komlodi REG32(DEVICE_CHARACTERISTIC_TABLE_LOC3, 0x208)
28059b8d0c9SJoe Komlodi     FIELD(DEVICE_CHARACTERISTIC_TABLE_LOC3, DCR, 0, 8)
28159b8d0c9SJoe Komlodi     FIELD(DEVICE_CHARACTERISTIC_TABLE_LOC3, BCR, 8, 8)
28259b8d0c9SJoe Komlodi REG32(DEVICE_CHARACTERISTIC_TABLE_LOC4, 0x20c)
28359b8d0c9SJoe Komlodi     FIELD(DEVICE_CHARACTERISTIC_TABLE_LOC4, DEV_DYNAMIC_ADDR, 0, 8)
28459b8d0c9SJoe Komlodi /* Dev addr table fields */
28559b8d0c9SJoe Komlodi REG32(DEVICE_ADDR_TABLE_LOC1, 0x280)
28659b8d0c9SJoe Komlodi     FIELD(DEVICE_ADDR_TABLE_LOC1, DEV_STATIC_ADDR, 0, 7)
28759b8d0c9SJoe Komlodi     FIELD(DEVICE_ADDR_TABLE_LOC1, IBI_PEC_EN, 11, 1)
28859b8d0c9SJoe Komlodi     FIELD(DEVICE_ADDR_TABLE_LOC1, IBI_WITH_DATA, 12, 1)
28959b8d0c9SJoe Komlodi     FIELD(DEVICE_ADDR_TABLE_LOC1, SIR_REJECT, 13, 1)
29059b8d0c9SJoe Komlodi     FIELD(DEVICE_ADDR_TABLE_LOC1, MR_REJECT, 14, 1)
29159b8d0c9SJoe Komlodi     FIELD(DEVICE_ADDR_TABLE_LOC1, DEV_DYNAMIC_ADDR, 16, 8)
29259b8d0c9SJoe Komlodi     FIELD(DEVICE_ADDR_TABLE_LOC1, IBI_ADDR_MASK, 24, 2)
29359b8d0c9SJoe Komlodi     FIELD(DEVICE_ADDR_TABLE_LOC1, DEV_NACK_RETRY_CNT, 29, 2)
29459b8d0c9SJoe Komlodi     FIELD(DEVICE_ADDR_TABLE_LOC1, LEGACY_I2C_DEVICE, 31, 1)
295c52aaabdSJoe Komlodi 
296c52aaabdSJoe Komlodi static const uint32_t dw_i3c_resets[DW_I3C_NR_REGS] = {
29773a38a9bSJoe Komlodi     /* Target mode is not supported, don't advertise it for now. */
29873a38a9bSJoe Komlodi     [R_HW_CAPABILITY]               = 0x000e00b9,
299c52aaabdSJoe Komlodi     [R_QUEUE_THLD_CTRL]             = 0x01000101,
30073a38a9bSJoe Komlodi     [R_DATA_BUFFER_THLD_CTRL]       = 0x01010100,
30173a38a9bSJoe Komlodi     [R_SLV_EVENT_CTRL]              = 0x0000000b,
30273a38a9bSJoe Komlodi     [R_QUEUE_STATUS_LEVEL]          = 0x00000002,
30373a38a9bSJoe Komlodi     [R_DATA_BUFFER_STATUS_LEVEL]    = 0x00000010,
30473a38a9bSJoe Komlodi     [R_PRESENT_STATE]               = 0x00000003,
305c52aaabdSJoe Komlodi     [R_I3C_VER_ID]                  = 0x3130302a,
306c52aaabdSJoe Komlodi     [R_I3C_VER_TYPE]                = 0x6c633033,
307c52aaabdSJoe Komlodi     [R_DEVICE_ADDR_TABLE_POINTER]   = 0x00080280,
308c52aaabdSJoe Komlodi     [R_DEV_CHAR_TABLE_POINTER]      = 0x00020200,
30973a38a9bSJoe Komlodi     [R_SLV_CHAR_CTRL]               = 0x00010000,
310c52aaabdSJoe Komlodi     [A_VENDOR_SPECIFIC_REG_POINTER] = 0x000000b0,
311c52aaabdSJoe Komlodi     [R_SLV_MAX_LEN]                 = 0x00ff00ff,
31273a38a9bSJoe Komlodi     [R_SLV_TSX_SYMBL_TIMING]        = 0x0000003f,
31373a38a9bSJoe Komlodi     [R_SCL_I3C_OD_TIMING]           = 0x000a0010,
31473a38a9bSJoe Komlodi     [R_SCL_I3C_PP_TIMING]           = 0x000a000a,
31573a38a9bSJoe Komlodi     [R_SCL_I2C_FM_TIMING]           = 0x00100010,
31673a38a9bSJoe Komlodi     [R_SCL_I2C_FMP_TIMING]          = 0x00100010,
31773a38a9bSJoe Komlodi     [R_SCL_EXT_LCNT_TIMING]         = 0x20202020,
31873a38a9bSJoe Komlodi     [R_SCL_EXT_TERMN_LCNT_TIMING]   = 0x00300000,
31973a38a9bSJoe Komlodi     [R_BUS_FREE_TIMING]             = 0x00200020,
32073a38a9bSJoe Komlodi     [R_BUS_IDLE_TIMING]             = 0x00000020,
32173a38a9bSJoe Komlodi     [R_EXTENDED_CAPABILITY]         = 0x00000239,
32273a38a9bSJoe Komlodi     [R_SLAVE_CONFIG]                = 0x00000023,
323c52aaabdSJoe Komlodi };
324c52aaabdSJoe Komlodi 
325ef77491fSJoe Komlodi static const uint32_t dw_i3c_ro[DW_I3C_NR_REGS] = {
326ef77491fSJoe Komlodi     [R_DEVICE_CTRL]                 = 0x04fffe00,
327ef77491fSJoe Komlodi     [R_DEVICE_ADDR]                 = 0x7f807f80,
328ef77491fSJoe Komlodi     [R_HW_CAPABILITY]               = 0xffffffff,
329ef77491fSJoe Komlodi     [R_IBI_QUEUE_STATUS]            = 0xffffffff,
330ef77491fSJoe Komlodi     [R_DATA_BUFFER_THLD_CTRL]       = 0xf8f8f8f8,
331ef77491fSJoe Komlodi     [R_IBI_QUEUE_CTRL]              = 0xfffffff0,
332ef77491fSJoe Komlodi     [R_RESET_CTRL]                  = 0xffffffc0,
333ef77491fSJoe Komlodi     [R_SLV_EVENT_CTRL]              = 0xffffff3f,
334ef77491fSJoe Komlodi     [R_INTR_STATUS]                 = 0xffff809f,
335ef77491fSJoe Komlodi     [R_INTR_STATUS_EN]              = 0xffff8080,
336ef77491fSJoe Komlodi     [R_INTR_SIGNAL_EN]              = 0xffff8080,
337ef77491fSJoe Komlodi     [R_INTR_FORCE]                  = 0xffff8000,
338ef77491fSJoe Komlodi     [R_QUEUE_STATUS_LEVEL]          = 0xffffffff,
339ef77491fSJoe Komlodi     [R_DATA_BUFFER_STATUS_LEVEL]    = 0xffffffff,
340ef77491fSJoe Komlodi     [R_PRESENT_STATE]               = 0xffffffff,
341ef77491fSJoe Komlodi     [R_CCC_DEVICE_STATUS]           = 0xffffffff,
342ef77491fSJoe Komlodi     [R_I3C_VER_ID]                  = 0xffffffff,
343ef77491fSJoe Komlodi     [R_I3C_VER_TYPE]                = 0xffffffff,
344ef77491fSJoe Komlodi     [R_DEVICE_ADDR_TABLE_POINTER]   = 0xffffffff,
345ef77491fSJoe Komlodi     [R_DEV_CHAR_TABLE_POINTER]      = 0xffcbffff,
346ef77491fSJoe Komlodi     [R_SLV_PID_VALUE]               = 0xffff0fff,
347ef77491fSJoe Komlodi     [R_SLV_CHAR_CTRL]               = 0xffffffff,
348ef77491fSJoe Komlodi     [A_VENDOR_SPECIFIC_REG_POINTER] = 0xffffffff,
349ef77491fSJoe Komlodi     [R_SLV_MAX_LEN]                 = 0xffffffff,
350ef77491fSJoe Komlodi     [R_MAX_READ_TURNAROUND]         = 0xffffffff,
351ef77491fSJoe Komlodi     [R_MAX_DATA_SPEED]              = 0xffffffff,
352ef77491fSJoe Komlodi     [R_SLV_INTR_REQ]                = 0xfffffff0,
353ef77491fSJoe Komlodi     [R_SLV_TSX_SYMBL_TIMING]        = 0xffffffc0,
354ef77491fSJoe Komlodi     [R_DEVICE_CTRL_EXTENDED]        = 0xfffffff8,
355ef77491fSJoe Komlodi     [R_SCL_I3C_OD_TIMING]           = 0xff00ff00,
356ef77491fSJoe Komlodi     [R_SCL_I3C_PP_TIMING]           = 0xff00ff00,
357ef77491fSJoe Komlodi     [R_SCL_I2C_FMP_TIMING]          = 0xff000000,
358ef77491fSJoe Komlodi     [R_SCL_EXT_TERMN_LCNT_TIMING]   = 0x0000fff0,
359ef77491fSJoe Komlodi     [R_BUS_IDLE_TIMING]             = 0xfff00000,
360ef77491fSJoe Komlodi     [R_EXTENDED_CAPABILITY]         = 0xffffffff,
361ef77491fSJoe Komlodi     [R_SLAVE_CONFIG]                = 0xffffffff,
362ef77491fSJoe Komlodi };
363ef77491fSJoe Komlodi 
3649c0476a2SJoe Komlodi static inline bool dw_i3c_has_hdr_ts(DWI3C *s)
3659c0476a2SJoe Komlodi {
3669c0476a2SJoe Komlodi     return ARRAY_FIELD_EX32(s->regs, HW_CAPABILITY, HDR_TS);
3679c0476a2SJoe Komlodi }
3689c0476a2SJoe Komlodi 
3699c0476a2SJoe Komlodi static inline bool dw_i3c_has_hdr_ddr(DWI3C *s)
3709c0476a2SJoe Komlodi {
3719c0476a2SJoe Komlodi     return ARRAY_FIELD_EX32(s->regs, HW_CAPABILITY, HDR_DDR);
3729c0476a2SJoe Komlodi }
3739c0476a2SJoe Komlodi 
3749c0476a2SJoe Komlodi static inline bool dw_i3c_can_transmit(DWI3C *s)
3759c0476a2SJoe Komlodi {
3769c0476a2SJoe Komlodi     /*
3779c0476a2SJoe Komlodi      * We can only transmit if we're enabled and the resume bit is cleared.
3789c0476a2SJoe Komlodi      * The resume bit is set on a transaction error, and software must clear it.
3799c0476a2SJoe Komlodi      */
3809c0476a2SJoe Komlodi     return ARRAY_FIELD_EX32(s->regs, DEVICE_CTRL, I3C_EN) &&
3819c0476a2SJoe Komlodi            !ARRAY_FIELD_EX32(s->regs, DEVICE_CTRL, I3C_RESUME);
3829c0476a2SJoe Komlodi }
3839c0476a2SJoe Komlodi 
384*5a2a5453SJoe Komlodi static inline uint8_t dw_i3c_ibi_slice_size(DWI3C *s)
385*5a2a5453SJoe Komlodi {
386*5a2a5453SJoe Komlodi     uint8_t ibi_slice_size = ARRAY_FIELD_EX32(s->regs, QUEUE_THLD_CTRL,
387*5a2a5453SJoe Komlodi                                               IBI_DATA_THLD);
388*5a2a5453SJoe Komlodi     /* The minimum supported slice size is 4 bytes. */
389*5a2a5453SJoe Komlodi     if (ibi_slice_size == 0) {
390*5a2a5453SJoe Komlodi         ibi_slice_size = 1;
391*5a2a5453SJoe Komlodi     }
392*5a2a5453SJoe Komlodi     ibi_slice_size *= sizeof(uint32_t);
393*5a2a5453SJoe Komlodi     /* maximum supported size is 63 bytes. */
394*5a2a5453SJoe Komlodi     if (ibi_slice_size >= 64) {
395*5a2a5453SJoe Komlodi         ibi_slice_size = 63;
396*5a2a5453SJoe Komlodi     }
397*5a2a5453SJoe Komlodi 
398*5a2a5453SJoe Komlodi     return ibi_slice_size;
399*5a2a5453SJoe Komlodi }
400*5a2a5453SJoe Komlodi 
4019c0476a2SJoe Komlodi static inline uint8_t dw_i3c_fifo_threshold_from_reg(uint8_t regval)
4029c0476a2SJoe Komlodi {
4039c0476a2SJoe Komlodi     return regval = regval ? (2 << regval) : 1;
4049c0476a2SJoe Komlodi }
4059c0476a2SJoe Komlodi 
406a825bbb7SJoe Komlodi static void dw_i3c_update_irq(DWI3C *s)
407a825bbb7SJoe Komlodi {
408a825bbb7SJoe Komlodi     bool level = !!(s->regs[R_INTR_SIGNAL_EN] & s->regs[R_INTR_STATUS]);
409a825bbb7SJoe Komlodi     qemu_set_irq(s->irq, level);
410a825bbb7SJoe Komlodi }
411a825bbb7SJoe Komlodi 
4129c0476a2SJoe Komlodi static void dw_i3c_end_transfer(DWI3C *s, bool is_i2c)
4139c0476a2SJoe Komlodi {
4149c0476a2SJoe Komlodi     if (is_i2c) {
4159c0476a2SJoe Komlodi         legacy_i2c_end_transfer(s->bus);
4169c0476a2SJoe Komlodi     } else {
4179c0476a2SJoe Komlodi         i3c_end_transfer(s->bus);
4189c0476a2SJoe Komlodi     }
4199c0476a2SJoe Komlodi }
4209c0476a2SJoe Komlodi 
4219c0476a2SJoe Komlodi static int dw_i3c_send_start(DWI3C *s, uint8_t addr, bool is_recv, bool is_i2c)
4229c0476a2SJoe Komlodi {
4239c0476a2SJoe Komlodi     int ret;
4249c0476a2SJoe Komlodi 
4259c0476a2SJoe Komlodi     if (is_i2c) {
4269c0476a2SJoe Komlodi         ret = legacy_i2c_start_transfer(s->bus, addr, is_recv);
4279c0476a2SJoe Komlodi     } else {
4289c0476a2SJoe Komlodi         ret = i3c_start_transfer(s->bus, addr, is_recv);
4299c0476a2SJoe Komlodi     }
4309c0476a2SJoe Komlodi     if (ret) {
4319c0476a2SJoe Komlodi         g_autofree char *path = object_get_canonical_path(OBJECT(s));
4329c0476a2SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: NACKed on TX with addr 0x%.2x\n",
4339c0476a2SJoe Komlodi                       path, addr);
4349c0476a2SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS,
4359c0476a2SJoe Komlodi                          DW_I3C_TRANSFER_STATE_HALT);
4369c0476a2SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_STATUS,
4379c0476a2SJoe Komlodi                          DW_I3C_TRANSFER_STATUS_HALT);
4389c0476a2SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, INTR_STATUS, TRANSFER_ERR, 1);
4399c0476a2SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, DEVICE_CTRL, I3C_RESUME, 1);
4409c0476a2SJoe Komlodi     }
4419c0476a2SJoe Komlodi 
4429c0476a2SJoe Komlodi     return ret;
4439c0476a2SJoe Komlodi }
4449c0476a2SJoe Komlodi 
4459c0476a2SJoe Komlodi static int dw_i3c_send(DWI3C *s, const uint8_t *data, uint32_t num_to_send,
4469c0476a2SJoe Komlodi                        uint32_t *num_sent, bool is_i2c)
4479c0476a2SJoe Komlodi {
4489c0476a2SJoe Komlodi     int ret;
4499c0476a2SJoe Komlodi     uint32_t i;
4509c0476a2SJoe Komlodi 
4519c0476a2SJoe Komlodi     *num_sent = 0;
4529c0476a2SJoe Komlodi     if (is_i2c) {
4539c0476a2SJoe Komlodi         /* Legacy I2C must be byte-by-byte. */
4549c0476a2SJoe Komlodi         for (i = 0; i < num_to_send; i++) {
4559c0476a2SJoe Komlodi             ret = legacy_i2c_send(s->bus, data[i]);
4569c0476a2SJoe Komlodi             if (ret) {
4579c0476a2SJoe Komlodi                 break;
4589c0476a2SJoe Komlodi             }
4599c0476a2SJoe Komlodi             (*num_sent)++;
4609c0476a2SJoe Komlodi         }
4619c0476a2SJoe Komlodi     } else {
4629c0476a2SJoe Komlodi         ret = i3c_send(s->bus, data, num_to_send, num_sent);
4639c0476a2SJoe Komlodi     }
4649c0476a2SJoe Komlodi     if (ret) {
4659c0476a2SJoe Komlodi         g_autofree char *path = object_get_canonical_path(OBJECT(s));
4669c0476a2SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: NACKed sending byte 0x%.2x\n",
4679c0476a2SJoe Komlodi                       path, data[*num_sent]);
4689c0476a2SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS,
4699c0476a2SJoe Komlodi                          DW_I3C_TRANSFER_STATE_HALT);
4709c0476a2SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_STATUS,
4719c0476a2SJoe Komlodi                          DW_I3C_TRANSFER_STATUS_HALT);
4729c0476a2SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, INTR_STATUS, TRANSFER_ERR, 1);
4739c0476a2SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, DEVICE_CTRL, I3C_RESUME, 1);
4749c0476a2SJoe Komlodi     }
4759c0476a2SJoe Komlodi 
4769c0476a2SJoe Komlodi     trace_dw_i3c_send(s->cfg.id, *num_sent);
4779c0476a2SJoe Komlodi 
4789c0476a2SJoe Komlodi     return ret;
4799c0476a2SJoe Komlodi }
4809c0476a2SJoe Komlodi 
4819c0476a2SJoe Komlodi static int dw_i3c_send_byte(DWI3C *s, uint8_t byte, bool is_i2c)
4829c0476a2SJoe Komlodi {
4839c0476a2SJoe Komlodi     /*
4849c0476a2SJoe Komlodi      * Ignored, the caller will know if we sent 0 or 1 bytes depending on if
4859c0476a2SJoe Komlodi      * we were ACKed/NACKed.
4869c0476a2SJoe Komlodi      */
4879c0476a2SJoe Komlodi     uint32_t num_sent;
4889c0476a2SJoe Komlodi     return dw_i3c_send(s, &byte, 1, &num_sent, is_i2c);
4899c0476a2SJoe Komlodi }
4909c0476a2SJoe Komlodi 
4919c0476a2SJoe Komlodi static int dw_i3c_recv_data(DWI3C *s, bool is_i2c, uint8_t *data,
4929c0476a2SJoe Komlodi                             uint16_t num_to_read, uint32_t *num_read)
4939c0476a2SJoe Komlodi {
4949c0476a2SJoe Komlodi     int ret;
4959c0476a2SJoe Komlodi 
4969c0476a2SJoe Komlodi     if (is_i2c) {
4979c0476a2SJoe Komlodi         for (uint16_t i = 0; i < num_to_read; i++) {
4989c0476a2SJoe Komlodi             data[i] = legacy_i2c_recv(s->bus);
4999c0476a2SJoe Komlodi         }
5009c0476a2SJoe Komlodi         /* I2C devices can neither NACK a read, nor end transfers early. */
5019c0476a2SJoe Komlodi         *num_read = num_to_read;
5029c0476a2SJoe Komlodi         trace_dw_i3c_recv_data(s->cfg.id, *num_read);
5039c0476a2SJoe Komlodi         return 0;
5049c0476a2SJoe Komlodi     }
5059c0476a2SJoe Komlodi     /* I3C devices can NACK if the controller sends an unsupported CCC. */
5069c0476a2SJoe Komlodi     ret = i3c_recv(s->bus, data, num_to_read, num_read);
5079c0476a2SJoe Komlodi     if (ret) {
5089c0476a2SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: NACKed receiving byte\n",
5099c0476a2SJoe Komlodi                       object_get_canonical_path(OBJECT(s)));
5109c0476a2SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS,
5119c0476a2SJoe Komlodi                          DW_I3C_TRANSFER_STATE_HALT);
5129c0476a2SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_STATUS,
5139c0476a2SJoe Komlodi                          DW_I3C_TRANSFER_STATUS_HALT);
5149c0476a2SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, INTR_STATUS, TRANSFER_ERR, 1);
5159c0476a2SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, DEVICE_CTRL, I3C_RESUME, 1);
5169c0476a2SJoe Komlodi     }
5179c0476a2SJoe Komlodi 
5189c0476a2SJoe Komlodi     trace_dw_i3c_recv_data(s->cfg.id, *num_read);
5199c0476a2SJoe Komlodi 
5209c0476a2SJoe Komlodi     return ret;
5219c0476a2SJoe Komlodi }
5229c0476a2SJoe Komlodi 
5239c0476a2SJoe Komlodi static inline bool dw_i3c_target_is_i2c(DWI3C *s, uint16_t offset)
5249c0476a2SJoe Komlodi {
5259c0476a2SJoe Komlodi     /* / sizeof(uint32_t) because we're indexing into our 32-bit reg array. */
5269c0476a2SJoe Komlodi     uint16_t dev_index = (ARRAY_FIELD_EX32(s->regs, DEVICE_ADDR_TABLE_POINTER,
5279c0476a2SJoe Komlodi                                           ADDR) / sizeof(uint32_t)) + offset;
5289c0476a2SJoe Komlodi     return FIELD_EX32(s->regs[dev_index], DEVICE_ADDR_TABLE_LOC1,
5299c0476a2SJoe Komlodi                    LEGACY_I2C_DEVICE);
5309c0476a2SJoe Komlodi }
5319c0476a2SJoe Komlodi 
5329c0476a2SJoe Komlodi static uint8_t dw_i3c_target_addr(DWI3C *s, uint16_t offset)
5339c0476a2SJoe Komlodi {
5349c0476a2SJoe Komlodi     if (offset > s->cfg.num_addressable_devices) {
5359c0476a2SJoe Komlodi         g_autofree char *path = object_get_canonical_path(OBJECT(s));
5369c0476a2SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: Device addr table offset %d out of "
5379c0476a2SJoe Komlodi                       "bounds\n", path, offset);
5389c0476a2SJoe Komlodi         /* If we're out of bounds, return an address of 0. */
5399c0476a2SJoe Komlodi         return 0;
5409c0476a2SJoe Komlodi     }
5419c0476a2SJoe Komlodi 
5429c0476a2SJoe Komlodi     /* / sizeof(uint32_t) because we're indexing into our 32-bit reg array. */
5439c0476a2SJoe Komlodi     uint16_t dev_index = (ARRAY_FIELD_EX32(s->regs, DEVICE_ADDR_TABLE_POINTER,
5449c0476a2SJoe Komlodi                                           ADDR) / sizeof(uint32_t)) + offset;
5459c0476a2SJoe Komlodi     /* I2C devices use a static address. */
5469c0476a2SJoe Komlodi     if (dw_i3c_target_is_i2c(s, offset)) {
5479c0476a2SJoe Komlodi         return FIELD_EX32(s->regs[dev_index], DEVICE_ADDR_TABLE_LOC1,
5489c0476a2SJoe Komlodi                           DEV_STATIC_ADDR);
5499c0476a2SJoe Komlodi     }
5509c0476a2SJoe Komlodi     return FIELD_EX32(s->regs[dev_index], DEVICE_ADDR_TABLE_LOC1,
5519c0476a2SJoe Komlodi                       DEV_DYNAMIC_ADDR);
5529c0476a2SJoe Komlodi }
5539c0476a2SJoe Komlodi 
554*5a2a5453SJoe Komlodi static int dw_i3c_addr_table_index_from_addr(DWI3C *s, uint8_t addr)
555*5a2a5453SJoe Komlodi {
556*5a2a5453SJoe Komlodi     uint8_t table_size = ARRAY_FIELD_EX32(s->regs, DEVICE_ADDR_TABLE_POINTER,
557*5a2a5453SJoe Komlodi                                           DEPTH);
558*5a2a5453SJoe Komlodi     for (uint8_t i = 0; i < table_size; i++) {
559*5a2a5453SJoe Komlodi         if (dw_i3c_target_addr(s, i) == addr) {
560*5a2a5453SJoe Komlodi             return i;
561*5a2a5453SJoe Komlodi         }
562*5a2a5453SJoe Komlodi     }
563*5a2a5453SJoe Komlodi     return -1;
564*5a2a5453SJoe Komlodi }
565*5a2a5453SJoe Komlodi 
566*5a2a5453SJoe Komlodi static void dw_i3c_send_disec(DWI3C *s)
567*5a2a5453SJoe Komlodi {
568*5a2a5453SJoe Komlodi     uint8_t ccc = I3C_CCC_DISEC;
569*5a2a5453SJoe Komlodi     if (s->ibi_data.send_direct_disec) {
570*5a2a5453SJoe Komlodi         ccc = I3C_CCCD_DISEC;
571*5a2a5453SJoe Komlodi     }
572*5a2a5453SJoe Komlodi 
573*5a2a5453SJoe Komlodi     dw_i3c_send_start(s, I3C_BROADCAST, /*is_recv=*/false,
574*5a2a5453SJoe Komlodi                                  /*is_i2c=*/false);
575*5a2a5453SJoe Komlodi     dw_i3c_send_byte(s, ccc, /*is_i2c=*/false);
576*5a2a5453SJoe Komlodi     if (s->ibi_data.send_direct_disec) {
577*5a2a5453SJoe Komlodi         dw_i3c_send_start(s, s->ibi_data.disec_addr,
578*5a2a5453SJoe Komlodi                                      /*is_recv=*/false, /*is_i2c=*/false);
579*5a2a5453SJoe Komlodi     }
580*5a2a5453SJoe Komlodi     dw_i3c_send_byte(s, s->ibi_data.disec_byte, /*is_i2c=*/false);
581*5a2a5453SJoe Komlodi }
582*5a2a5453SJoe Komlodi 
583*5a2a5453SJoe Komlodi static int dw_i3c_handle_hj(DWI3C *s)
584*5a2a5453SJoe Komlodi {
585*5a2a5453SJoe Komlodi     if (ARRAY_FIELD_EX32(s->regs, IBI_QUEUE_CTRL, NOTIFY_REJECTED_HOT_JOIN)) {
586*5a2a5453SJoe Komlodi         s->ibi_data.notify_ibi_nack = true;
587*5a2a5453SJoe Komlodi     }
588*5a2a5453SJoe Komlodi 
589*5a2a5453SJoe Komlodi     bool nack_and_disable = ARRAY_FIELD_EX32(s->regs, DEVICE_CTRL,
590*5a2a5453SJoe Komlodi                                              HOT_JOIN_ACK_NACK_CTRL);
591*5a2a5453SJoe Komlodi     if (nack_and_disable) {
592*5a2a5453SJoe Komlodi         s->ibi_data.ibi_queue_status = FIELD_DP32(s->ibi_data.ibi_queue_status,
593*5a2a5453SJoe Komlodi                                                   IBI_QUEUE_STATUS,
594*5a2a5453SJoe Komlodi                                                   IBI_STATUS, 1);
595*5a2a5453SJoe Komlodi         s->ibi_data.ibi_nacked = true;
596*5a2a5453SJoe Komlodi         s->ibi_data.disec_byte = DISEC_HJ;
597*5a2a5453SJoe Komlodi         return -1;
598*5a2a5453SJoe Komlodi     }
599*5a2a5453SJoe Komlodi     return 0;
600*5a2a5453SJoe Komlodi }
601*5a2a5453SJoe Komlodi 
602*5a2a5453SJoe Komlodi static int dw_i3c_handle_ctlr_req(DWI3C *s, uint8_t addr)
603*5a2a5453SJoe Komlodi {
604*5a2a5453SJoe Komlodi     if (ARRAY_FIELD_EX32(s->regs, IBI_QUEUE_CTRL, NOTIFY_REJECTED_MASTER_REQ)) {
605*5a2a5453SJoe Komlodi         s->ibi_data.notify_ibi_nack = true;
606*5a2a5453SJoe Komlodi     }
607*5a2a5453SJoe Komlodi 
608*5a2a5453SJoe Komlodi     int table_offset = dw_i3c_addr_table_index_from_addr(s, addr);
609*5a2a5453SJoe Komlodi     /* Doesn't exist in the table, NACK it, don't DISEC. */
610*5a2a5453SJoe Komlodi     if (table_offset < 0) {
611*5a2a5453SJoe Komlodi         return -1;
612*5a2a5453SJoe Komlodi     }
613*5a2a5453SJoe Komlodi 
614*5a2a5453SJoe Komlodi     /* / sizeof(uint32_t) because we're indexing into our 32-bit reg array. */
615*5a2a5453SJoe Komlodi     table_offset += (ARRAY_FIELD_EX32(s->regs, DEVICE_ADDR_TABLE_POINTER,
616*5a2a5453SJoe Komlodi                                       ADDR) / sizeof(uint32_t));
617*5a2a5453SJoe Komlodi     if (FIELD_EX32(s->regs[table_offset], DEVICE_ADDR_TABLE_LOC1, MR_REJECT)) {
618*5a2a5453SJoe Komlodi         s->ibi_data.ibi_queue_status = FIELD_DP32(s->ibi_data.ibi_queue_status,
619*5a2a5453SJoe Komlodi                                                   IBI_QUEUE_STATUS,
620*5a2a5453SJoe Komlodi                                                   IBI_STATUS, 1);
621*5a2a5453SJoe Komlodi         s->ibi_data.ibi_nacked = true;
622*5a2a5453SJoe Komlodi         s->ibi_data.disec_addr = addr;
623*5a2a5453SJoe Komlodi         /* Tell the requester to disable controller role requests. */
624*5a2a5453SJoe Komlodi         s->ibi_data.disec_byte = DISEC_CR;
625*5a2a5453SJoe Komlodi         s->ibi_data.send_direct_disec = true;
626*5a2a5453SJoe Komlodi         return -1;
627*5a2a5453SJoe Komlodi     }
628*5a2a5453SJoe Komlodi     return 0;
629*5a2a5453SJoe Komlodi }
630*5a2a5453SJoe Komlodi 
631*5a2a5453SJoe Komlodi static int dw_i3c_handle_targ_irq(DWI3C *s, uint8_t addr)
632*5a2a5453SJoe Komlodi {
633*5a2a5453SJoe Komlodi     if (ARRAY_FIELD_EX32(s->regs, IBI_QUEUE_CTRL, NOTIFY_REJECTED_SLAVE_IRQ)) {
634*5a2a5453SJoe Komlodi         s->ibi_data.notify_ibi_nack = true;
635*5a2a5453SJoe Komlodi     }
636*5a2a5453SJoe Komlodi 
637*5a2a5453SJoe Komlodi     int table_offset = dw_i3c_addr_table_index_from_addr(s, addr);
638*5a2a5453SJoe Komlodi     /* Doesn't exist in the table, NACK it, don't DISEC. */
639*5a2a5453SJoe Komlodi     if (table_offset < 0) {
640*5a2a5453SJoe Komlodi         return -1;
641*5a2a5453SJoe Komlodi     }
642*5a2a5453SJoe Komlodi 
643*5a2a5453SJoe Komlodi     /* / sizeof(uint32_t) because we're indexing into our 32-bit reg array. */
644*5a2a5453SJoe Komlodi     table_offset += (ARRAY_FIELD_EX32(s->regs, DEVICE_ADDR_TABLE_POINTER,
645*5a2a5453SJoe Komlodi                                       ADDR) / sizeof(uint32_t));
646*5a2a5453SJoe Komlodi     if (FIELD_EX32(s->regs[table_offset], DEVICE_ADDR_TABLE_LOC1, SIR_REJECT)) {
647*5a2a5453SJoe Komlodi         s->ibi_data.ibi_queue_status = FIELD_DP32(s->ibi_data.ibi_queue_status,
648*5a2a5453SJoe Komlodi                                                   IBI_QUEUE_STATUS,
649*5a2a5453SJoe Komlodi                                                   IBI_STATUS, 1);
650*5a2a5453SJoe Komlodi         s->ibi_data.ibi_nacked = true;
651*5a2a5453SJoe Komlodi         s->ibi_data.disec_addr = addr;
652*5a2a5453SJoe Komlodi         /* Tell the requester to disable interrupts. */
653*5a2a5453SJoe Komlodi         s->ibi_data.disec_byte = DISEC_INT;
654*5a2a5453SJoe Komlodi         s->ibi_data.send_direct_disec = true;
655*5a2a5453SJoe Komlodi         return -1;
656*5a2a5453SJoe Komlodi     }
657*5a2a5453SJoe Komlodi     return 0;
658*5a2a5453SJoe Komlodi }
659*5a2a5453SJoe Komlodi 
660*5a2a5453SJoe Komlodi static int dw_i3c_ibi_handle(I3CBus *bus, uint8_t addr, bool is_recv)
661*5a2a5453SJoe Komlodi {
662*5a2a5453SJoe Komlodi     DWI3C *s = DW_I3C(bus->qbus.parent);
663*5a2a5453SJoe Komlodi 
664*5a2a5453SJoe Komlodi     trace_dw_i3c_ibi_handle(s->cfg.id, addr, is_recv);
665*5a2a5453SJoe Komlodi     s->ibi_data.ibi_queue_status = FIELD_DP32(s->ibi_data.ibi_queue_status,
666*5a2a5453SJoe Komlodi                                               IBI_QUEUE_STATUS, IBI_ID,
667*5a2a5453SJoe Komlodi                                               (addr << 1) | is_recv);
668*5a2a5453SJoe Komlodi     /* Is this a hot join request? */
669*5a2a5453SJoe Komlodi     if (addr == I3C_HJ_ADDR) {
670*5a2a5453SJoe Komlodi         return dw_i3c_handle_hj(s);
671*5a2a5453SJoe Komlodi     }
672*5a2a5453SJoe Komlodi     /* Is secondary controller requesting access? */
673*5a2a5453SJoe Komlodi     if (!is_recv) {
674*5a2a5453SJoe Komlodi         return dw_i3c_handle_ctlr_req(s, addr);
675*5a2a5453SJoe Komlodi     }
676*5a2a5453SJoe Komlodi     /* Is this a target IRQ? */
677*5a2a5453SJoe Komlodi     if (is_recv) {
678*5a2a5453SJoe Komlodi         return dw_i3c_handle_targ_irq(s, addr);
679*5a2a5453SJoe Komlodi     }
680*5a2a5453SJoe Komlodi 
681*5a2a5453SJoe Komlodi     /* At this point the IBI should have been ACKed or NACKed. */
682*5a2a5453SJoe Komlodi     g_assert_not_reached();
683*5a2a5453SJoe Komlodi     return -1;
684*5a2a5453SJoe Komlodi }
685*5a2a5453SJoe Komlodi 
686*5a2a5453SJoe Komlodi static int dw_i3c_ibi_recv(I3CBus *bus, uint8_t data)
687*5a2a5453SJoe Komlodi {
688*5a2a5453SJoe Komlodi     DWI3C *s = DW_I3C(bus->qbus.parent);
689*5a2a5453SJoe Komlodi     if (fifo8_is_full(&s->ibi_data.ibi_intermediate_queue)) {
690*5a2a5453SJoe Komlodi         return -1;
691*5a2a5453SJoe Komlodi     }
692*5a2a5453SJoe Komlodi 
693*5a2a5453SJoe Komlodi     fifo8_push(&s->ibi_data.ibi_intermediate_queue, data);
694*5a2a5453SJoe Komlodi     trace_dw_i3c_ibi_recv(s->cfg.id, data);
695*5a2a5453SJoe Komlodi     return 0;
696*5a2a5453SJoe Komlodi }
697*5a2a5453SJoe Komlodi 
698*5a2a5453SJoe Komlodi static void dw_i3c_ibi_queue_push(DWI3C *s)
699*5a2a5453SJoe Komlodi {
700*5a2a5453SJoe Komlodi     /* Stored value is in 32-bit chunks, convert it to byte chunks. */
701*5a2a5453SJoe Komlodi     uint8_t ibi_slice_size = dw_i3c_ibi_slice_size(s);
702*5a2a5453SJoe Komlodi     uint8_t num_slices = (fifo8_num_used(&s->ibi_data.ibi_intermediate_queue) /
703*5a2a5453SJoe Komlodi                          ibi_slice_size) +
704*5a2a5453SJoe Komlodi                          ((fifo8_num_used(&s->ibi_data.ibi_intermediate_queue) %
705*5a2a5453SJoe Komlodi                          ibi_slice_size) ? 1 : 0);
706*5a2a5453SJoe Komlodi     uint8_t ibi_status_count = num_slices;
707*5a2a5453SJoe Komlodi     union {
708*5a2a5453SJoe Komlodi         uint8_t b[sizeof(uint32_t)];
709*5a2a5453SJoe Komlodi         uint32_t val32;
710*5a2a5453SJoe Komlodi     } ibi_data = {
711*5a2a5453SJoe Komlodi         .val32 = 0
712*5a2a5453SJoe Komlodi     };
713*5a2a5453SJoe Komlodi 
714*5a2a5453SJoe Komlodi     /* The report was suppressed, do nothing. */
715*5a2a5453SJoe Komlodi     if (s->ibi_data.ibi_nacked && !s->ibi_data.notify_ibi_nack) {
716*5a2a5453SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS,
717*5a2a5453SJoe Komlodi                          DW_I3C_TRANSFER_STATE_IDLE);
718*5a2a5453SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_STATUS,
719*5a2a5453SJoe Komlodi                          DW_I3C_TRANSFER_STATUS_IDLE);
720*5a2a5453SJoe Komlodi         return;
721*5a2a5453SJoe Komlodi     }
722*5a2a5453SJoe Komlodi 
723*5a2a5453SJoe Komlodi     /* If we don't have any slices to push, just push the status. */
724*5a2a5453SJoe Komlodi     if (num_slices == 0) {
725*5a2a5453SJoe Komlodi         s->ibi_data.ibi_queue_status =
726*5a2a5453SJoe Komlodi              FIELD_DP32(s->ibi_data.ibi_queue_status, IBI_QUEUE_STATUS,
727*5a2a5453SJoe Komlodi                         LAST_STATUS, 1);
728*5a2a5453SJoe Komlodi         fifo32_push(&s->ibi_queue, s->ibi_data.ibi_queue_status);
729*5a2a5453SJoe Komlodi         ibi_status_count = 1;
730*5a2a5453SJoe Komlodi     }
731*5a2a5453SJoe Komlodi 
732*5a2a5453SJoe Komlodi     for (uint8_t i = 0; i < num_slices; i++) {
733*5a2a5453SJoe Komlodi         /* If this is the last slice, set LAST_STATUS. */
734*5a2a5453SJoe Komlodi         if (fifo8_num_used(&s->ibi_data.ibi_intermediate_queue) <
735*5a2a5453SJoe Komlodi             ibi_slice_size) {
736*5a2a5453SJoe Komlodi             s->ibi_data.ibi_queue_status =
737*5a2a5453SJoe Komlodi                 FIELD_DP32(s->ibi_data.ibi_queue_status, IBI_QUEUE_STATUS,
738*5a2a5453SJoe Komlodi                            IBI_DATA_LEN,
739*5a2a5453SJoe Komlodi                            fifo8_num_used(&s->ibi_data.ibi_intermediate_queue));
740*5a2a5453SJoe Komlodi             s->ibi_data.ibi_queue_status =
741*5a2a5453SJoe Komlodi                 FIELD_DP32(s->ibi_data.ibi_queue_status, IBI_QUEUE_STATUS,
742*5a2a5453SJoe Komlodi                            LAST_STATUS, 1);
743*5a2a5453SJoe Komlodi         } else {
744*5a2a5453SJoe Komlodi             s->ibi_data.ibi_queue_status =
745*5a2a5453SJoe Komlodi                 FIELD_DP32(s->ibi_data.ibi_queue_status, IBI_QUEUE_STATUS,
746*5a2a5453SJoe Komlodi                            IBI_DATA_LEN, ibi_slice_size);
747*5a2a5453SJoe Komlodi         }
748*5a2a5453SJoe Komlodi 
749*5a2a5453SJoe Komlodi         /* Push the IBI status header. */
750*5a2a5453SJoe Komlodi         fifo32_push(&s->ibi_queue, s->ibi_data.ibi_queue_status);
751*5a2a5453SJoe Komlodi         /* Move each IBI byte into a 32-bit word and push it into the queue. */
752*5a2a5453SJoe Komlodi         for (uint8_t j = 0; j < ibi_slice_size; ++j) {
753*5a2a5453SJoe Komlodi             if (fifo8_is_empty(&s->ibi_data.ibi_intermediate_queue)) {
754*5a2a5453SJoe Komlodi                 break;
755*5a2a5453SJoe Komlodi             }
756*5a2a5453SJoe Komlodi 
757*5a2a5453SJoe Komlodi             ibi_data.b[j & 3] = fifo8_pop(&s->ibi_data.ibi_intermediate_queue);
758*5a2a5453SJoe Komlodi             /* We have 32-bits, push it to the IBI FIFO. */
759*5a2a5453SJoe Komlodi             if ((j & 0x03) == 0x03) {
760*5a2a5453SJoe Komlodi                 fifo32_push(&s->ibi_queue, ibi_data.val32);
761*5a2a5453SJoe Komlodi                 ibi_data.val32 = 0;
762*5a2a5453SJoe Komlodi             }
763*5a2a5453SJoe Komlodi         }
764*5a2a5453SJoe Komlodi         /* If the data isn't 32-bit aligned, push the leftover bytes. */
765*5a2a5453SJoe Komlodi         if (ibi_slice_size & 0x03) {
766*5a2a5453SJoe Komlodi             fifo32_push(&s->ibi_queue, ibi_data.val32);
767*5a2a5453SJoe Komlodi         }
768*5a2a5453SJoe Komlodi 
769*5a2a5453SJoe Komlodi         /* Clear out the data length for the next iteration. */
770*5a2a5453SJoe Komlodi         s->ibi_data.ibi_queue_status = FIELD_DP32(s->ibi_data.ibi_queue_status,
771*5a2a5453SJoe Komlodi                                          IBI_QUEUE_STATUS, IBI_DATA_LEN, 0);
772*5a2a5453SJoe Komlodi     }
773*5a2a5453SJoe Komlodi 
774*5a2a5453SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, QUEUE_STATUS_LEVEL, IBI_BUF_BLR,
775*5a2a5453SJoe Komlodi                      fifo32_num_used(&s->ibi_queue));
776*5a2a5453SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, QUEUE_STATUS_LEVEL, IBI_STATUS_CNT,
777*5a2a5453SJoe Komlodi                      ibi_status_count);
778*5a2a5453SJoe Komlodi     /* Threshold is the register value + 1. */
779*5a2a5453SJoe Komlodi     uint8_t threshold = ARRAY_FIELD_EX32(s->regs, QUEUE_THLD_CTRL,
780*5a2a5453SJoe Komlodi                                          IBI_STATUS_THLD) + 1;
781*5a2a5453SJoe Komlodi     if (fifo32_num_used(&s->ibi_queue) >= threshold) {
782*5a2a5453SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, INTR_STATUS, IBI_THLD, 1);
783*5a2a5453SJoe Komlodi         dw_i3c_update_irq(s);
784*5a2a5453SJoe Komlodi     }
785*5a2a5453SJoe Komlodi 
786*5a2a5453SJoe Komlodi     /* State update. */
787*5a2a5453SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS,
788*5a2a5453SJoe Komlodi                      DW_I3C_TRANSFER_STATE_IDLE);
789*5a2a5453SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_STATUS,
790*5a2a5453SJoe Komlodi                      DW_I3C_TRANSFER_STATUS_IDLE);
791*5a2a5453SJoe Komlodi }
792*5a2a5453SJoe Komlodi 
793*5a2a5453SJoe Komlodi static int dw_i3c_ibi_finish(I3CBus *bus)
794*5a2a5453SJoe Komlodi {
795*5a2a5453SJoe Komlodi     DWI3C *s = DW_I3C(bus->qbus.parent);
796*5a2a5453SJoe Komlodi     bool nack_and_disable_hj = ARRAY_FIELD_EX32(s->regs, DEVICE_CTRL,
797*5a2a5453SJoe Komlodi                                                 HOT_JOIN_ACK_NACK_CTRL);
798*5a2a5453SJoe Komlodi     if (nack_and_disable_hj || s->ibi_data.send_direct_disec) {
799*5a2a5453SJoe Komlodi         dw_i3c_send_disec(s);
800*5a2a5453SJoe Komlodi     }
801*5a2a5453SJoe Komlodi     dw_i3c_ibi_queue_push(s);
802*5a2a5453SJoe Komlodi 
803*5a2a5453SJoe Komlodi     /* Clear out the intermediate values. */
804*5a2a5453SJoe Komlodi     s->ibi_data.ibi_queue_status = 0;
805*5a2a5453SJoe Komlodi     s->ibi_data.disec_addr = 0;
806*5a2a5453SJoe Komlodi     s->ibi_data.disec_byte = 0;
807*5a2a5453SJoe Komlodi     s->ibi_data.send_direct_disec = false;
808*5a2a5453SJoe Komlodi     s->ibi_data.notify_ibi_nack = false;
809*5a2a5453SJoe Komlodi     s->ibi_data.ibi_nacked = false;
810*5a2a5453SJoe Komlodi 
811*5a2a5453SJoe Komlodi     return 0;
812*5a2a5453SJoe Komlodi }
813*5a2a5453SJoe Komlodi 
814a825bbb7SJoe Komlodi static uint32_t dw_i3c_intr_status_r(DWI3C *s)
815a825bbb7SJoe Komlodi {
816a825bbb7SJoe Komlodi     /* Only return the status whose corresponding EN bits are set. */
817a825bbb7SJoe Komlodi     return s->regs[R_INTR_STATUS] & s->regs[R_INTR_STATUS_EN];
818a825bbb7SJoe Komlodi }
819a825bbb7SJoe Komlodi 
820a825bbb7SJoe Komlodi static void dw_i3c_intr_status_w(DWI3C *s, uint32_t val)
821a825bbb7SJoe Komlodi {
822a825bbb7SJoe Komlodi     /* INTR_STATUS[13:5] is w1c, other bits are RO. */
823a825bbb7SJoe Komlodi     val &= 0x3fe0;
824a825bbb7SJoe Komlodi     s->regs[R_INTR_STATUS] &= ~val;
825a825bbb7SJoe Komlodi 
826a825bbb7SJoe Komlodi     dw_i3c_update_irq(s);
827a825bbb7SJoe Komlodi }
828a825bbb7SJoe Komlodi 
829a825bbb7SJoe Komlodi static void dw_i3c_intr_status_en_w(DWI3C *s, uint32_t val)
830a825bbb7SJoe Komlodi {
831a825bbb7SJoe Komlodi     s->regs[R_INTR_STATUS_EN] = val;
832a825bbb7SJoe Komlodi     dw_i3c_update_irq(s);
833a825bbb7SJoe Komlodi }
834a825bbb7SJoe Komlodi 
835a825bbb7SJoe Komlodi static void dw_i3c_intr_signal_en_w(DWI3C *s, uint32_t val)
836a825bbb7SJoe Komlodi {
837a825bbb7SJoe Komlodi     s->regs[R_INTR_SIGNAL_EN] = val;
838a825bbb7SJoe Komlodi     dw_i3c_update_irq(s);
839a825bbb7SJoe Komlodi }
840a825bbb7SJoe Komlodi 
841a825bbb7SJoe Komlodi static void dw_i3c_intr_force_w(DWI3C *s, uint32_t val)
842a825bbb7SJoe Komlodi {
843a825bbb7SJoe Komlodi     /* INTR_FORCE is WO, just set the corresponding INTR_STATUS bits. */
844a825bbb7SJoe Komlodi     s->regs[R_INTR_STATUS] = val;
845a825bbb7SJoe Komlodi     dw_i3c_update_irq(s);
846a825bbb7SJoe Komlodi }
847a825bbb7SJoe Komlodi 
8489c0476a2SJoe Komlodi static uint32_t dw_i3c_pop_rx(DWI3C *s)
8499c0476a2SJoe Komlodi {
8509c0476a2SJoe Komlodi     if (fifo32_is_empty(&s->rx_queue)) {
8519c0476a2SJoe Komlodi         g_autofree char *path = object_get_canonical_path(OBJECT(s));
8529c0476a2SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: Tried to read RX FIFO when empty\n",
8539c0476a2SJoe Komlodi                       path);
8549c0476a2SJoe Komlodi         return 0;
8559c0476a2SJoe Komlodi     }
8569c0476a2SJoe Komlodi 
8579c0476a2SJoe Komlodi     uint32_t val = fifo32_pop(&s->rx_queue);
8589c0476a2SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, DATA_BUFFER_STATUS_LEVEL, RX_BUF_BLR,
8599c0476a2SJoe Komlodi                      fifo32_num_used(&s->rx_queue));
8609c0476a2SJoe Komlodi 
8619c0476a2SJoe Komlodi     /* Threshold is 2^RX_BUF_THLD. */
8629c0476a2SJoe Komlodi     uint8_t threshold = ARRAY_FIELD_EX32(s->regs, DATA_BUFFER_THLD_CTRL,
8639c0476a2SJoe Komlodi                                          RX_BUF_THLD);
8649c0476a2SJoe Komlodi     threshold = dw_i3c_fifo_threshold_from_reg(threshold);
8659c0476a2SJoe Komlodi     if (fifo32_num_used(&s->rx_queue) < threshold) {
8669c0476a2SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, INTR_STATUS, RX_THLD, 0);
8679c0476a2SJoe Komlodi         dw_i3c_update_irq(s);
8689c0476a2SJoe Komlodi     }
8699c0476a2SJoe Komlodi 
8709c0476a2SJoe Komlodi     trace_dw_i3c_pop_rx(s->cfg.id, val);
8719c0476a2SJoe Komlodi     return val;
8729c0476a2SJoe Komlodi }
8739c0476a2SJoe Komlodi 
874*5a2a5453SJoe Komlodi static uint32_t dw_i3c_ibi_queue_r(DWI3C *s)
875*5a2a5453SJoe Komlodi {
876*5a2a5453SJoe Komlodi     if (fifo32_is_empty(&s->ibi_queue)) {
877*5a2a5453SJoe Komlodi         return 0;
878*5a2a5453SJoe Komlodi     }
879*5a2a5453SJoe Komlodi 
880*5a2a5453SJoe Komlodi     uint32_t val = fifo32_pop(&s->ibi_queue);
881*5a2a5453SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, QUEUE_STATUS_LEVEL, IBI_BUF_BLR,
882*5a2a5453SJoe Komlodi                      fifo32_num_used(&s->ibi_queue));
883*5a2a5453SJoe Komlodi     /* Threshold is the register value + 1. */
884*5a2a5453SJoe Komlodi     uint8_t threshold = ARRAY_FIELD_EX32(s->regs, QUEUE_THLD_CTRL,
885*5a2a5453SJoe Komlodi                                          IBI_STATUS_THLD) + 1;
886*5a2a5453SJoe Komlodi     if (fifo32_num_used(&s->ibi_queue) < threshold) {
887*5a2a5453SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, INTR_STATUS, IBI_THLD, 0);
888*5a2a5453SJoe Komlodi         dw_i3c_update_irq(s);
889*5a2a5453SJoe Komlodi     }
890*5a2a5453SJoe Komlodi     return val;
891*5a2a5453SJoe Komlodi }
892*5a2a5453SJoe Komlodi 
8939c0476a2SJoe Komlodi static uint32_t dw_i3c_resp_queue_port_r(DWI3C *s)
8949c0476a2SJoe Komlodi {
8959c0476a2SJoe Komlodi     if (fifo32_is_empty(&s->resp_queue)) {
8969c0476a2SJoe Komlodi         g_autofree char *path = object_get_canonical_path(OBJECT(s));
8979c0476a2SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: Tried to read response FIFO when "
8989c0476a2SJoe Komlodi                       "empty\n", path);
8999c0476a2SJoe Komlodi         return 0;
9009c0476a2SJoe Komlodi     }
9019c0476a2SJoe Komlodi 
9029c0476a2SJoe Komlodi     uint32_t val = fifo32_pop(&s->resp_queue);
9039c0476a2SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, QUEUE_STATUS_LEVEL, RESP_BUF_BLR,
9049c0476a2SJoe Komlodi                      fifo32_num_used(&s->resp_queue));
9059c0476a2SJoe Komlodi 
9069c0476a2SJoe Komlodi     /* Threshold is the register value + 1. */
9079c0476a2SJoe Komlodi     uint8_t threshold = ARRAY_FIELD_EX32(s->regs, QUEUE_THLD_CTRL,
9089c0476a2SJoe Komlodi                                          RESP_BUF_THLD) + 1;
9099c0476a2SJoe Komlodi     if (fifo32_num_used(&s->resp_queue) < threshold) {
9109c0476a2SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, INTR_STATUS, RESP_RDY, 0);
9119c0476a2SJoe Komlodi         dw_i3c_update_irq(s);
9129c0476a2SJoe Komlodi     }
9139c0476a2SJoe Komlodi 
9149c0476a2SJoe Komlodi     return val;
9159c0476a2SJoe Komlodi }
9169c0476a2SJoe Komlodi 
917c52aaabdSJoe Komlodi static uint64_t dw_i3c_read(void *opaque, hwaddr offset, unsigned size)
918c52aaabdSJoe Komlodi {
919c52aaabdSJoe Komlodi     DWI3C *s = DW_I3C(opaque);
920c52aaabdSJoe Komlodi     uint32_t addr = offset >> 2;
921c52aaabdSJoe Komlodi     uint64_t value;
922c52aaabdSJoe Komlodi 
923c52aaabdSJoe Komlodi     switch (addr) {
9245f31322eSJoe Komlodi     /* RAZ */
925c52aaabdSJoe Komlodi     case R_COMMAND_QUEUE_PORT:
9265f31322eSJoe Komlodi     case R_RESET_CTRL:
9275f31322eSJoe Komlodi     case R_INTR_FORCE:
928c52aaabdSJoe Komlodi         value = 0;
929c52aaabdSJoe Komlodi         break;
930*5a2a5453SJoe Komlodi     case R_IBI_QUEUE_DATA:
931*5a2a5453SJoe Komlodi         value = dw_i3c_ibi_queue_r(s);
932*5a2a5453SJoe Komlodi         break;
933a825bbb7SJoe Komlodi     case R_INTR_STATUS:
934a825bbb7SJoe Komlodi         value = dw_i3c_intr_status_r(s);
935a825bbb7SJoe Komlodi         break;
9369c0476a2SJoe Komlodi     case R_RX_TX_DATA_PORT:
9379c0476a2SJoe Komlodi         value = dw_i3c_pop_rx(s);
9389c0476a2SJoe Komlodi         break;
9399c0476a2SJoe Komlodi     case R_RESPONSE_QUEUE_PORT:
9409c0476a2SJoe Komlodi         value = dw_i3c_resp_queue_port_r(s);
9419c0476a2SJoe Komlodi         break;
942c52aaabdSJoe Komlodi     default:
943c52aaabdSJoe Komlodi         value = s->regs[addr];
944c52aaabdSJoe Komlodi         break;
945c52aaabdSJoe Komlodi     }
946c52aaabdSJoe Komlodi 
9479c0476a2SJoe Komlodi     trace_dw_i3c_read(s->cfg.id, offset, value);
948c52aaabdSJoe Komlodi 
949c52aaabdSJoe Komlodi     return value;
950c52aaabdSJoe Komlodi }
951c52aaabdSJoe Komlodi 
9529c0476a2SJoe Komlodi static void dw_i3c_resp_queue_push(DWI3C *s, uint8_t err, uint8_t tid,
9539c0476a2SJoe Komlodi                                    uint8_t ccc_type, uint16_t data_len)
9549c0476a2SJoe Komlodi {
9559c0476a2SJoe Komlodi     uint32_t val = 0;
9569c0476a2SJoe Komlodi     val = FIELD_DP32(val, RESPONSE_QUEUE_PORT, ERR_STATUS, err);
9579c0476a2SJoe Komlodi     val = FIELD_DP32(val, RESPONSE_QUEUE_PORT, TID, tid);
9589c0476a2SJoe Komlodi     val = FIELD_DP32(val, RESPONSE_QUEUE_PORT, CCCT, ccc_type);
9599c0476a2SJoe Komlodi     val = FIELD_DP32(val, RESPONSE_QUEUE_PORT, DL, data_len);
9609c0476a2SJoe Komlodi     if (!fifo32_is_full(&s->resp_queue)) {
9619c0476a2SJoe Komlodi         trace_dw_i3c_resp_queue_push(s->cfg.id, val);
9629c0476a2SJoe Komlodi         fifo32_push(&s->resp_queue, val);
9639c0476a2SJoe Komlodi     }
9649c0476a2SJoe Komlodi 
9659c0476a2SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, QUEUE_STATUS_LEVEL, RESP_BUF_BLR,
9669c0476a2SJoe Komlodi                      fifo32_num_used(&s->resp_queue));
9679c0476a2SJoe Komlodi     /* Threshold is the register value + 1. */
9689c0476a2SJoe Komlodi     uint8_t threshold = ARRAY_FIELD_EX32(s->regs, QUEUE_THLD_CTRL,
9699c0476a2SJoe Komlodi                                          RESP_BUF_THLD) + 1;
9709c0476a2SJoe Komlodi     if (fifo32_num_used(&s->resp_queue) >= threshold) {
9719c0476a2SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, INTR_STATUS, RESP_RDY, 1);
9729c0476a2SJoe Komlodi         dw_i3c_update_irq(s);
9739c0476a2SJoe Komlodi     }
9749c0476a2SJoe Komlodi }
9759c0476a2SJoe Komlodi 
9769c0476a2SJoe Komlodi static void dw_i3c_push_tx(DWI3C *s, uint32_t val)
9779c0476a2SJoe Komlodi {
9789c0476a2SJoe Komlodi     if (fifo32_is_full(&s->tx_queue)) {
9799c0476a2SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: Tried to push to TX FIFO when "
9809c0476a2SJoe Komlodi                       "full\n", object_get_canonical_path(OBJECT(s)));
9819c0476a2SJoe Komlodi         return;
9829c0476a2SJoe Komlodi     }
9839c0476a2SJoe Komlodi 
9849c0476a2SJoe Komlodi     trace_dw_i3c_push_tx(s->cfg.id, val);
9859c0476a2SJoe Komlodi     fifo32_push(&s->tx_queue, val);
9869c0476a2SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, DATA_BUFFER_STATUS_LEVEL, TX_BUF_EMPTY_LOC,
9879c0476a2SJoe Komlodi                      fifo32_num_free(&s->tx_queue));
9889c0476a2SJoe Komlodi 
9899c0476a2SJoe Komlodi     /* Threshold is 2^TX_BUF_THLD. */
9909c0476a2SJoe Komlodi     uint8_t empty_threshold = ARRAY_FIELD_EX32(s->regs, DATA_BUFFER_THLD_CTRL,
9919c0476a2SJoe Komlodi                                                TX_BUF_THLD);
9929c0476a2SJoe Komlodi     empty_threshold =
9939c0476a2SJoe Komlodi         dw_i3c_fifo_threshold_from_reg(empty_threshold);
9949c0476a2SJoe Komlodi     if (fifo32_num_free(&s->tx_queue) < empty_threshold) {
9959c0476a2SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, INTR_STATUS, TX_THLD, 0);
9969c0476a2SJoe Komlodi         dw_i3c_update_irq(s);
9979c0476a2SJoe Komlodi     }
9989c0476a2SJoe Komlodi }
9999c0476a2SJoe Komlodi 
10009c0476a2SJoe Komlodi static uint32_t dw_i3c_pop_tx(DWI3C *s)
10019c0476a2SJoe Komlodi {
10029c0476a2SJoe Komlodi     if (fifo32_is_empty(&s->tx_queue)) {
10039c0476a2SJoe Komlodi         g_autofree char *path = object_get_canonical_path(OBJECT(s));
10049c0476a2SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: Tried to pop from TX FIFO when "
10059c0476a2SJoe Komlodi                       "empty\n", path);
10069c0476a2SJoe Komlodi         return 0;
10079c0476a2SJoe Komlodi     }
10089c0476a2SJoe Komlodi 
10099c0476a2SJoe Komlodi     uint32_t val = fifo32_pop(&s->tx_queue);
10109c0476a2SJoe Komlodi     trace_dw_i3c_pop_tx(s->cfg.id, val);
10119c0476a2SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, DATA_BUFFER_STATUS_LEVEL, TX_BUF_EMPTY_LOC,
10129c0476a2SJoe Komlodi                      fifo32_num_free(&s->tx_queue));
10139c0476a2SJoe Komlodi 
10149c0476a2SJoe Komlodi     /* Threshold is 2^TX_BUF_THLD. */
10159c0476a2SJoe Komlodi     uint8_t empty_threshold = ARRAY_FIELD_EX32(s->regs, DATA_BUFFER_THLD_CTRL,
10169c0476a2SJoe Komlodi                                                TX_BUF_THLD);
10179c0476a2SJoe Komlodi     empty_threshold =
10189c0476a2SJoe Komlodi         dw_i3c_fifo_threshold_from_reg(empty_threshold);
10199c0476a2SJoe Komlodi     if (fifo32_num_free(&s->tx_queue) >= empty_threshold) {
10209c0476a2SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, INTR_STATUS, TX_THLD, 1);
10219c0476a2SJoe Komlodi         dw_i3c_update_irq(s);
10229c0476a2SJoe Komlodi     }
10239c0476a2SJoe Komlodi     return val;
10249c0476a2SJoe Komlodi }
10259c0476a2SJoe Komlodi 
10269c0476a2SJoe Komlodi static void dw_i3c_push_rx(DWI3C *s, uint32_t val)
10279c0476a2SJoe Komlodi {
10289c0476a2SJoe Komlodi     if (fifo32_is_full(&s->rx_queue)) {
10299c0476a2SJoe Komlodi         g_autofree char *path = object_get_canonical_path(OBJECT(s));
10309c0476a2SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: Tried to push to RX FIFO when "
10319c0476a2SJoe Komlodi                       "full\n", path);
10329c0476a2SJoe Komlodi         return;
10339c0476a2SJoe Komlodi     }
10349c0476a2SJoe Komlodi     trace_dw_i3c_push_rx(s->cfg.id, val);
10359c0476a2SJoe Komlodi     fifo32_push(&s->rx_queue, val);
10369c0476a2SJoe Komlodi 
10379c0476a2SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, DATA_BUFFER_STATUS_LEVEL, RX_BUF_BLR,
10389c0476a2SJoe Komlodi                      fifo32_num_used(&s->rx_queue));
10399c0476a2SJoe Komlodi     /* Threshold is 2^RX_BUF_THLD. */
10409c0476a2SJoe Komlodi     uint8_t threshold = ARRAY_FIELD_EX32(s->regs, DATA_BUFFER_THLD_CTRL,
10419c0476a2SJoe Komlodi                                          RX_BUF_THLD);
10429c0476a2SJoe Komlodi     threshold = dw_i3c_fifo_threshold_from_reg(threshold);
10439c0476a2SJoe Komlodi     if (fifo32_num_used(&s->rx_queue) >= threshold) {
10449c0476a2SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, INTR_STATUS, RX_THLD, 1);
10459c0476a2SJoe Komlodi         dw_i3c_update_irq(s);
10469c0476a2SJoe Komlodi     }
10479c0476a2SJoe Komlodi }
10489c0476a2SJoe Komlodi 
10499c0476a2SJoe Komlodi static void dw_i3c_short_transfer(DWI3C *s, DWI3CTransferCmd cmd,
10509c0476a2SJoe Komlodi                                   DWI3CShortArg arg)
10519c0476a2SJoe Komlodi {
10529c0476a2SJoe Komlodi     uint8_t err = DW_I3C_RESP_QUEUE_ERR_NONE;
10539c0476a2SJoe Komlodi     uint8_t addr = dw_i3c_target_addr(s, cmd.dev_index);
10549c0476a2SJoe Komlodi     bool is_i2c = dw_i3c_target_is_i2c(s, cmd.dev_index);
10559c0476a2SJoe Komlodi     uint8_t data[4]; /* Max we can send on a short transfer is 4 bytes. */
10569c0476a2SJoe Komlodi     uint8_t len = 0;
10579c0476a2SJoe Komlodi     uint32_t bytes_sent; /* Ignored on short transfers. */
10589c0476a2SJoe Komlodi 
10599c0476a2SJoe Komlodi     /* Can't do reads on a short transfer. */
10609c0476a2SJoe Komlodi     if (cmd.rnw) {
10619c0476a2SJoe Komlodi         g_autofree char *path = object_get_canonical_path(OBJECT(s));
10629c0476a2SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: Cannot do a read on a short "
10639c0476a2SJoe Komlodi                       "transfer\n", path);
10649c0476a2SJoe Komlodi         return;
10659c0476a2SJoe Komlodi     }
10669c0476a2SJoe Komlodi 
10679c0476a2SJoe Komlodi     if (dw_i3c_send_start(s, addr, /*is_recv=*/false, is_i2c)) {
10689c0476a2SJoe Komlodi         err = DW_I3C_RESP_QUEUE_ERR_I2C_NACK;
10699c0476a2SJoe Komlodi         goto transfer_done;
10709c0476a2SJoe Komlodi     }
10719c0476a2SJoe Komlodi 
10729c0476a2SJoe Komlodi     /* Are we sending a command? */
10739c0476a2SJoe Komlodi     if (cmd.cp) {
10749c0476a2SJoe Komlodi         data[len] = cmd.cmd;
10759c0476a2SJoe Komlodi         len++;
10769c0476a2SJoe Komlodi         /*
10779c0476a2SJoe Komlodi          * byte0 is the defining byte for a command, and is only sent if a
10789c0476a2SJoe Komlodi          * command is present and if the command has a defining byte present.
10799c0476a2SJoe Komlodi          * (byte_strb & 0x01) is always treated as set by the controller, and is
10809c0476a2SJoe Komlodi          * ignored.
10819c0476a2SJoe Komlodi          */
10829c0476a2SJoe Komlodi         if (cmd.dbp) {
10839c0476a2SJoe Komlodi             data[len] += arg.byte0;
10849c0476a2SJoe Komlodi             len++;
10859c0476a2SJoe Komlodi         }
10869c0476a2SJoe Komlodi     }
10879c0476a2SJoe Komlodi 
10889c0476a2SJoe Komlodi     /* Send the bytes passed in the argument. */
10899c0476a2SJoe Komlodi     if (arg.byte_strb & 0x02) {
10909c0476a2SJoe Komlodi         data[len] = arg.byte1;
10919c0476a2SJoe Komlodi         len++;
10929c0476a2SJoe Komlodi     }
10939c0476a2SJoe Komlodi     if (arg.byte_strb & 0x04) {
10949c0476a2SJoe Komlodi         data[len] = arg.byte2;
10959c0476a2SJoe Komlodi         len++;
10969c0476a2SJoe Komlodi     }
10979c0476a2SJoe Komlodi 
10989c0476a2SJoe Komlodi     if (dw_i3c_send(s, data, len, &bytes_sent, is_i2c)) {
10999c0476a2SJoe Komlodi         err = DW_I3C_RESP_QUEUE_ERR_I2C_NACK;
11009c0476a2SJoe Komlodi     } else {
11019c0476a2SJoe Komlodi         /* Only go to an idle state on a successful transfer. */
11029c0476a2SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS,
11039c0476a2SJoe Komlodi                          DW_I3C_TRANSFER_STATE_IDLE);
11049c0476a2SJoe Komlodi     }
11059c0476a2SJoe Komlodi 
11069c0476a2SJoe Komlodi transfer_done:
11079c0476a2SJoe Komlodi     if (cmd.toc) {
11089c0476a2SJoe Komlodi         dw_i3c_end_transfer(s, is_i2c);
11099c0476a2SJoe Komlodi     }
11109c0476a2SJoe Komlodi     if (cmd.roc) {
11119c0476a2SJoe Komlodi         /*
11129c0476a2SJoe Komlodi          * ccc_type is always 0 in controller mode, data_len is 0 in short
11139c0476a2SJoe Komlodi          * transfers.
11149c0476a2SJoe Komlodi          */
11159c0476a2SJoe Komlodi         dw_i3c_resp_queue_push(s, err, cmd.tid, /*ccc_type=*/0,
11169c0476a2SJoe Komlodi                                           /*data_len=*/0);
11179c0476a2SJoe Komlodi     }
11189c0476a2SJoe Komlodi }
11199c0476a2SJoe Komlodi 
11209c0476a2SJoe Komlodi /* Returns number of bytes transmitted. */
11219c0476a2SJoe Komlodi static uint16_t dw_i3c_tx(DWI3C *s, uint16_t num, bool is_i2c)
11229c0476a2SJoe Komlodi {
11239c0476a2SJoe Komlodi     uint16_t bytes_sent = 0;
11249c0476a2SJoe Komlodi     union {
11259c0476a2SJoe Komlodi         uint8_t b[sizeof(uint32_t)];
11269c0476a2SJoe Komlodi         uint32_t val;
11279c0476a2SJoe Komlodi     } val32;
11289c0476a2SJoe Komlodi 
11299c0476a2SJoe Komlodi     while (bytes_sent < num) {
11309c0476a2SJoe Komlodi         val32.val = dw_i3c_pop_tx(s);
11319c0476a2SJoe Komlodi         for (uint8_t i = 0; i < sizeof(val32.val); i++) {
11329c0476a2SJoe Komlodi             if (dw_i3c_send_byte(s, val32.b[i], is_i2c)) {
11339c0476a2SJoe Komlodi                 return bytes_sent;
11349c0476a2SJoe Komlodi             }
11359c0476a2SJoe Komlodi             bytes_sent++;
11369c0476a2SJoe Komlodi 
11379c0476a2SJoe Komlodi             /* We're not sending the full 32-bits, break early. */
11389c0476a2SJoe Komlodi             if (bytes_sent >= num) {
11399c0476a2SJoe Komlodi                 break;
11409c0476a2SJoe Komlodi             }
11419c0476a2SJoe Komlodi         }
11429c0476a2SJoe Komlodi     }
11439c0476a2SJoe Komlodi 
11449c0476a2SJoe Komlodi     return bytes_sent;
11459c0476a2SJoe Komlodi }
11469c0476a2SJoe Komlodi 
11479c0476a2SJoe Komlodi /* Returns number of bytes received. */
11489c0476a2SJoe Komlodi static uint16_t dw_i3c_rx(DWI3C *s, uint16_t num, bool is_i2c)
11499c0476a2SJoe Komlodi {
11509c0476a2SJoe Komlodi     /*
11519c0476a2SJoe Komlodi      * Allocate a temporary buffer to read data from the target.
11529c0476a2SJoe Komlodi      * Zero it and word-align it as well in case we're reading unaligned data.
11539c0476a2SJoe Komlodi      */
11549c0476a2SJoe Komlodi     g_autofree uint8_t *data = g_new0(uint8_t, num + (4 - (num & 0x03)));
11559c0476a2SJoe Komlodi     uint32_t *data32 = (uint32_t *)data;
11569c0476a2SJoe Komlodi     /*
11579c0476a2SJoe Komlodi      * 32-bits since the I3C API wants a 32-bit number, even though the
11589c0476a2SJoe Komlodi      * controller can only do 16-bit transfers.
11599c0476a2SJoe Komlodi      */
11609c0476a2SJoe Komlodi     uint32_t num_read = 0;
11619c0476a2SJoe Komlodi 
11629c0476a2SJoe Komlodi     /* Can NACK if the target receives an unsupported CCC. */
11639c0476a2SJoe Komlodi     if (dw_i3c_recv_data(s, is_i2c, data, num, &num_read)) {
11649c0476a2SJoe Komlodi         return 0;
11659c0476a2SJoe Komlodi     }
11669c0476a2SJoe Komlodi 
11679c0476a2SJoe Komlodi     for (uint16_t i = 0; i < num_read / 4; i++) {
11689c0476a2SJoe Komlodi         dw_i3c_push_rx(s, *data32);
11699c0476a2SJoe Komlodi         data32++;
11709c0476a2SJoe Komlodi     }
11719c0476a2SJoe Komlodi     /*
11729c0476a2SJoe Komlodi      * If we're pushing data that isn't 32-bit aligned, push what's left.
11739c0476a2SJoe Komlodi      * It's software's responsibility to know what bits are valid in the partial
11749c0476a2SJoe Komlodi      * data.
11759c0476a2SJoe Komlodi      */
11769c0476a2SJoe Komlodi     if (num_read & 0x03) {
11779c0476a2SJoe Komlodi         dw_i3c_push_rx(s, *data32);
11789c0476a2SJoe Komlodi     }
11799c0476a2SJoe Komlodi 
11809c0476a2SJoe Komlodi     return num_read;
11819c0476a2SJoe Komlodi }
11829c0476a2SJoe Komlodi 
11839c0476a2SJoe Komlodi static int dw_i3c_transfer_ccc(DWI3C *s, DWI3CTransferCmd cmd,
11849c0476a2SJoe Komlodi                                DWI3CTransferArg arg)
11859c0476a2SJoe Komlodi {
11869c0476a2SJoe Komlodi     /* CCC start is always a write. CCCs cannot be done on I2C devices. */
11879c0476a2SJoe Komlodi     if (dw_i3c_send_start(s, I3C_BROADCAST, /*is_recv=*/false,
11889c0476a2SJoe Komlodi                                      /*is_i2c=*/false)) {
11899c0476a2SJoe Komlodi         return DW_I3C_RESP_QUEUE_ERR_BROADCAST_NACK;
11909c0476a2SJoe Komlodi     }
11919c0476a2SJoe Komlodi     trace_dw_i3c_transfer_ccc(s->cfg.id, cmd.cmd);
11929c0476a2SJoe Komlodi     if (dw_i3c_send_byte(s, cmd.cmd, /*is_i2c=*/false)) {
11939c0476a2SJoe Komlodi         return DW_I3C_RESP_QUEUE_ERR_I2C_NACK;
11949c0476a2SJoe Komlodi     }
11959c0476a2SJoe Komlodi 
11969c0476a2SJoe Komlodi     /* On a direct CCC, we do a restart and then send the target's address. */
11979c0476a2SJoe Komlodi     if (CCC_IS_DIRECT(cmd.cmd)) {
11989c0476a2SJoe Komlodi         bool is_recv = cmd.rnw;
11999c0476a2SJoe Komlodi         uint8_t addr = dw_i3c_target_addr(s, cmd.dev_index);
12009c0476a2SJoe Komlodi         if (dw_i3c_send_start(s, addr, is_recv, /*is_i2c=*/false)) {
12019c0476a2SJoe Komlodi             return DW_I3C_RESP_QUEUE_ERR_BROADCAST_NACK;
12029c0476a2SJoe Komlodi         }
12039c0476a2SJoe Komlodi     }
12049c0476a2SJoe Komlodi 
12059c0476a2SJoe Komlodi     return DW_I3C_RESP_QUEUE_ERR_NONE;
12069c0476a2SJoe Komlodi }
12079c0476a2SJoe Komlodi 
12089c0476a2SJoe Komlodi static void dw_i3c_transfer(DWI3C *s, DWI3CTransferCmd cmd,
12099c0476a2SJoe Komlodi                             DWI3CTransferArg arg)
12109c0476a2SJoe Komlodi {
12119c0476a2SJoe Komlodi     bool is_recv = cmd.rnw;
12129c0476a2SJoe Komlodi     uint8_t err = DW_I3C_RESP_QUEUE_ERR_NONE;
12139c0476a2SJoe Komlodi     uint8_t addr = dw_i3c_target_addr(s, cmd.dev_index);
12149c0476a2SJoe Komlodi     bool is_i2c = dw_i3c_target_is_i2c(s, cmd.dev_index);
12159c0476a2SJoe Komlodi     uint16_t bytes_transferred = 0;
12169c0476a2SJoe Komlodi 
12179c0476a2SJoe Komlodi     if (cmd.cp) {
12189c0476a2SJoe Komlodi         /* We're sending a CCC. */
12199c0476a2SJoe Komlodi         err = dw_i3c_transfer_ccc(s, cmd, arg);
12209c0476a2SJoe Komlodi         if (err != DW_I3C_RESP_QUEUE_ERR_NONE) {
12219c0476a2SJoe Komlodi             goto transfer_done;
12229c0476a2SJoe Komlodi         }
12239c0476a2SJoe Komlodi     } else {
12249c0476a2SJoe Komlodi         if (ARRAY_FIELD_EX32(s->regs, DEVICE_CTRL, I3C_BROADCAST_ADDR_INC) &&
12259c0476a2SJoe Komlodi             is_i2c == false) {
12269c0476a2SJoe Komlodi             if (dw_i3c_send_start(s, I3C_BROADCAST,
12279c0476a2SJoe Komlodi                                              /*is_recv=*/false, is_i2c)) {
12289c0476a2SJoe Komlodi                 err = DW_I3C_RESP_QUEUE_ERR_I2C_NACK;
12299c0476a2SJoe Komlodi                 goto transfer_done;
12309c0476a2SJoe Komlodi             }
12319c0476a2SJoe Komlodi         }
12329c0476a2SJoe Komlodi         /* Otherwise we're doing a private transfer. */
12339c0476a2SJoe Komlodi         if (dw_i3c_send_start(s, addr, is_recv, is_i2c)) {
12349c0476a2SJoe Komlodi             err = DW_I3C_RESP_QUEUE_ERR_I2C_NACK;
12359c0476a2SJoe Komlodi             goto transfer_done;
12369c0476a2SJoe Komlodi         }
12379c0476a2SJoe Komlodi     }
12389c0476a2SJoe Komlodi 
12399c0476a2SJoe Komlodi     if (is_recv) {
12409c0476a2SJoe Komlodi         bytes_transferred = dw_i3c_rx(s, arg.data_len, is_i2c);
12419c0476a2SJoe Komlodi     } else {
12429c0476a2SJoe Komlodi         bytes_transferred = dw_i3c_tx(s, arg.data_len, is_i2c);
12439c0476a2SJoe Komlodi     }
12449c0476a2SJoe Komlodi 
12459c0476a2SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS,
12469c0476a2SJoe Komlodi                      DW_I3C_TRANSFER_STATE_IDLE);
12479c0476a2SJoe Komlodi 
12489c0476a2SJoe Komlodi transfer_done:
12499c0476a2SJoe Komlodi     if (cmd.toc) {
12509c0476a2SJoe Komlodi         dw_i3c_end_transfer(s, is_i2c);
12519c0476a2SJoe Komlodi     }
12529c0476a2SJoe Komlodi     if (cmd.roc) {
12539c0476a2SJoe Komlodi         /*
12549c0476a2SJoe Komlodi          * data_len is the number of bytes that still need to be TX'd, or the
12559c0476a2SJoe Komlodi          * number of bytes RX'd.
12569c0476a2SJoe Komlodi          */
12579c0476a2SJoe Komlodi         uint16_t data_len = is_recv ? bytes_transferred : arg.data_len -
12589c0476a2SJoe Komlodi                                                           bytes_transferred;
12599c0476a2SJoe Komlodi         /* CCCT is always 0 in controller mode. */
12609c0476a2SJoe Komlodi         dw_i3c_resp_queue_push(s, err, cmd.tid, /*ccc_type=*/0,
12619c0476a2SJoe Komlodi                                           data_len);
12629c0476a2SJoe Komlodi     }
12639c0476a2SJoe Komlodi 
12649c0476a2SJoe Komlodi     dw_i3c_update_irq(s);
12659c0476a2SJoe Komlodi }
12669c0476a2SJoe Komlodi 
12679c0476a2SJoe Komlodi static void dw_i3c_transfer_cmd(DWI3C *s, DWI3CTransferCmd cmd,
12689c0476a2SJoe Komlodi                                 DWI3CCmdQueueData arg)
12699c0476a2SJoe Komlodi {
12709c0476a2SJoe Komlodi     uint8_t arg_attr = FIELD_EX32(arg.word, COMMAND_QUEUE_PORT, CMD_ATTR);
12719c0476a2SJoe Komlodi 
12729c0476a2SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CMD_TID, cmd.tid);
12739c0476a2SJoe Komlodi 
12749c0476a2SJoe Komlodi     /* User is trying to do HDR transfers, see if we can do them. */
12759c0476a2SJoe Komlodi     if (cmd.speed == 0x06 && !dw_i3c_has_hdr_ddr(s)) {
12769c0476a2SJoe Komlodi         g_autofree char *path = object_get_canonical_path(OBJECT(s));
12779c0476a2SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: HDR DDR is not supported\n", path);
12789c0476a2SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS,
12799c0476a2SJoe Komlodi                          DW_I3C_TRANSFER_STATE_HALT);
12809c0476a2SJoe Komlodi         return;
12819c0476a2SJoe Komlodi     }
12829c0476a2SJoe Komlodi     if (cmd.speed == 0x05 && !dw_i3c_has_hdr_ts(s)) {
12839c0476a2SJoe Komlodi         g_autofree char *path = object_get_canonical_path(OBJECT(s));
12849c0476a2SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: HDR TS is not supported\n", path);
12859c0476a2SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS,
12869c0476a2SJoe Komlodi                          DW_I3C_TRANSFER_STATE_HALT);
12879c0476a2SJoe Komlodi         return;
12889c0476a2SJoe Komlodi     }
12899c0476a2SJoe Komlodi 
12909c0476a2SJoe Komlodi     if (arg_attr == DW_I3C_CMD_ATTR_TRANSFER_ARG) {
12919c0476a2SJoe Komlodi         dw_i3c_transfer(s, cmd, arg.transfer_arg);
12929c0476a2SJoe Komlodi     } else if (arg_attr == DW_I3C_CMD_ATTR_SHORT_DATA_ARG) {
12939c0476a2SJoe Komlodi         dw_i3c_short_transfer(s, cmd, arg.short_arg);
12949c0476a2SJoe Komlodi     } else {
12959c0476a2SJoe Komlodi         g_autofree char *path = object_get_canonical_path(OBJECT(s));
12969c0476a2SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: Unknown command queue cmd_attr 0x%x"
12979c0476a2SJoe Komlodi                       "\n", path, arg_attr);
12989c0476a2SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS,
12999c0476a2SJoe Komlodi                          DW_I3C_TRANSFER_STATE_HALT);
13009c0476a2SJoe Komlodi     }
13019c0476a2SJoe Komlodi }
13029c0476a2SJoe Komlodi 
13039c0476a2SJoe Komlodi static void dw_i3c_update_char_table(DWI3C *s, uint8_t offset, uint64_t pid,
13049c0476a2SJoe Komlodi                                      uint8_t bcr, uint8_t dcr, uint8_t addr)
13059c0476a2SJoe Komlodi {
13069c0476a2SJoe Komlodi     if (offset > s->cfg.num_addressable_devices) {
13079c0476a2SJoe Komlodi         g_autofree char *path = object_get_canonical_path(OBJECT(s));
13089c0476a2SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: Device char table offset %d out of "
13099c0476a2SJoe Komlodi                       "bounds\n", path, offset);
13109c0476a2SJoe Komlodi         /* If we're out of bounds, do nothing. */
13119c0476a2SJoe Komlodi         return;
13129c0476a2SJoe Komlodi     }
13139c0476a2SJoe Komlodi 
13149c0476a2SJoe Komlodi     /*
13159c0476a2SJoe Komlodi      * Each device offset is 128 bits apart in the table, since each device gets
13169c0476a2SJoe Komlodi      * 4 * 32-bits of entries in the table.
13179c0476a2SJoe Komlodi      * / sizeof(uint32_t) because we're indexing into our 32-bit reg array.
13189c0476a2SJoe Komlodi      */
13199c0476a2SJoe Komlodi     uint16_t dev_index = (ARRAY_FIELD_EX32(s->regs, DEV_CHAR_TABLE_POINTER,
13209c0476a2SJoe Komlodi                                           P_DEV_CHAR_TABLE_START_ADDR) /
13219c0476a2SJoe Komlodi                                           sizeof(uint32_t)) +
13229c0476a2SJoe Komlodi                                           (offset * sizeof(uint32_t));
13239c0476a2SJoe Komlodi     s->regs[dev_index] = pid & 0xffffffff;
13249c0476a2SJoe Komlodi     pid >>= 32;
13259c0476a2SJoe Komlodi     s->regs[dev_index + 1] = FIELD_DP32(s->regs[dev_index + 1],
13269c0476a2SJoe Komlodi                                         DEVICE_CHARACTERISTIC_TABLE_LOC2,
13279c0476a2SJoe Komlodi                                         MSB_PID, pid);
13289c0476a2SJoe Komlodi     s->regs[dev_index + 2] = FIELD_DP32(s->regs[dev_index + 2],
13299c0476a2SJoe Komlodi                                         DEVICE_CHARACTERISTIC_TABLE_LOC3, DCR,
13309c0476a2SJoe Komlodi                                         dcr);
13319c0476a2SJoe Komlodi     s->regs[dev_index + 2] = FIELD_DP32(s->regs[dev_index + 2],
13329c0476a2SJoe Komlodi                                         DEVICE_CHARACTERISTIC_TABLE_LOC3, BCR,
13339c0476a2SJoe Komlodi                                         bcr);
13349c0476a2SJoe Komlodi     s->regs[dev_index + 3] = FIELD_DP32(s->regs[dev_index + 3],
13359c0476a2SJoe Komlodi                                         DEVICE_CHARACTERISTIC_TABLE_LOC4,
13369c0476a2SJoe Komlodi                                         DEV_DYNAMIC_ADDR, addr);
13379c0476a2SJoe Komlodi 
13389c0476a2SJoe Komlodi     /* Increment PRESENT_DEV_CHAR_TABLE_INDEX. */
13399c0476a2SJoe Komlodi     uint8_t idx = ARRAY_FIELD_EX32(s->regs, DEV_CHAR_TABLE_POINTER,
13409c0476a2SJoe Komlodi                      PRESENT_DEV_CHAR_TABLE_INDEX);
13419c0476a2SJoe Komlodi     /* Increment and rollover. */
13429c0476a2SJoe Komlodi     idx++;
13439c0476a2SJoe Komlodi     if (idx >= ARRAY_FIELD_EX32(s->regs, DEV_CHAR_TABLE_POINTER,
13449c0476a2SJoe Komlodi                                DEV_CHAR_TABLE_DEPTH) / 4) {
13459c0476a2SJoe Komlodi         idx = 0;
13469c0476a2SJoe Komlodi     }
13479c0476a2SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, DEV_CHAR_TABLE_POINTER,
13489c0476a2SJoe Komlodi                      PRESENT_DEV_CHAR_TABLE_INDEX, idx);
13499c0476a2SJoe Komlodi }
13509c0476a2SJoe Komlodi 
13519c0476a2SJoe Komlodi static void dw_i3c_addr_assign_cmd(DWI3C *s, DWI3CAddrAssignCmd cmd)
13529c0476a2SJoe Komlodi {
13539c0476a2SJoe Komlodi     uint8_t i = 0;
13549c0476a2SJoe Komlodi     uint8_t err = DW_I3C_RESP_QUEUE_ERR_NONE;
13559c0476a2SJoe Komlodi 
13569c0476a2SJoe Komlodi     /* Tell everyone to ENTDAA. If these error, no one is on the bus. */
13579c0476a2SJoe Komlodi     if (dw_i3c_send_start(s, I3C_BROADCAST, /*is_recv=*/false,
13589c0476a2SJoe Komlodi                                      /*is_i2c=*/false)) {
13599c0476a2SJoe Komlodi         err = DW_I3C_RESP_QUEUE_ERR_BROADCAST_NACK;
13609c0476a2SJoe Komlodi         goto transfer_done;
13619c0476a2SJoe Komlodi     }
13629c0476a2SJoe Komlodi     if (dw_i3c_send_byte(s, cmd.cmd, /*is_i2c=*/false)) {
13639c0476a2SJoe Komlodi         err = DW_I3C_RESP_QUEUE_ERR_BROADCAST_NACK;
13649c0476a2SJoe Komlodi         goto transfer_done;
13659c0476a2SJoe Komlodi     }
13669c0476a2SJoe Komlodi 
13679c0476a2SJoe Komlodi     /* Go through each device in the table and assign it an address. */
13689c0476a2SJoe Komlodi     for (i = 0; i < cmd.dev_count; i++) {
13699c0476a2SJoe Komlodi         uint8_t addr = dw_i3c_target_addr(s, cmd.dev_index + i);
13709c0476a2SJoe Komlodi         union {
13719c0476a2SJoe Komlodi             uint64_t pid:48;
13729c0476a2SJoe Komlodi             uint8_t bcr;
13739c0476a2SJoe Komlodi             uint8_t dcr;
13749c0476a2SJoe Komlodi             uint32_t w[2];
13759c0476a2SJoe Komlodi             uint8_t b[8];
13769c0476a2SJoe Komlodi         } target_info;
13779c0476a2SJoe Komlodi 
13789c0476a2SJoe Komlodi         /* If this fails, there was no one left to ENTDAA. */
13799c0476a2SJoe Komlodi         if (dw_i3c_send_start(s, I3C_BROADCAST, /*is_recv=*/false,
13809c0476a2SJoe Komlodi                                          /*is_i2c=*/false)) {
13819c0476a2SJoe Komlodi             err = DW_I3C_RESP_QUEUE_ERR_BROADCAST_NACK;
13829c0476a2SJoe Komlodi             break;
13839c0476a2SJoe Komlodi         }
13849c0476a2SJoe Komlodi 
13859c0476a2SJoe Komlodi         /*
13869c0476a2SJoe Komlodi          * In ENTDAA, we read 8 bytes from the target, which will be the
13879c0476a2SJoe Komlodi          * target's PID, BCR, and DCR. After that, we send it the dynamic
13889c0476a2SJoe Komlodi          * address.
13899c0476a2SJoe Komlodi          * Don't bother checking the number of bytes received, it must send 8
13909c0476a2SJoe Komlodi          * bytes during ENTDAA.
13919c0476a2SJoe Komlodi          */
13929c0476a2SJoe Komlodi         uint32_t num_read;
13939c0476a2SJoe Komlodi         if (dw_i3c_recv_data(s, /*is_i2c=*/false, target_info.b,
13949c0476a2SJoe Komlodi                                         I3C_ENTDAA_SIZE, &num_read)) {
13959c0476a2SJoe Komlodi             g_autofree char *path = object_get_canonical_path(OBJECT(s));
13969c0476a2SJoe Komlodi             qemu_log_mask(LOG_GUEST_ERROR, "%s: Target NACKed ENTDAA CCC\n",
13979c0476a2SJoe Komlodi                           path);
13989c0476a2SJoe Komlodi             err = DW_I3C_RESP_QUEUE_ERR_DAA_NACK;
13999c0476a2SJoe Komlodi             goto transfer_done;
14009c0476a2SJoe Komlodi         }
14019c0476a2SJoe Komlodi         if (dw_i3c_send_byte(s, addr, /*is_i2c=*/false)) {
14029c0476a2SJoe Komlodi             g_autofree char *path = object_get_canonical_path(OBJECT(s));
14039c0476a2SJoe Komlodi             qemu_log_mask(LOG_GUEST_ERROR, "%s: Target NACKed addr 0x%.2x "
14049c0476a2SJoe Komlodi                           "during ENTDAA\n", path, addr);
14059c0476a2SJoe Komlodi             err = DW_I3C_RESP_QUEUE_ERR_DAA_NACK;
14069c0476a2SJoe Komlodi             break;
14079c0476a2SJoe Komlodi         }
14089c0476a2SJoe Komlodi         dw_i3c_update_char_table(s, cmd.dev_index + i,
14099c0476a2SJoe Komlodi                                             target_info.pid, target_info.bcr,
14109c0476a2SJoe Komlodi                                             target_info.dcr, addr);
14119c0476a2SJoe Komlodi 
14129c0476a2SJoe Komlodi         /* Push the PID, BCR, and DCR to the RX queue. */
14139c0476a2SJoe Komlodi         dw_i3c_push_rx(s, target_info.w[0]);
14149c0476a2SJoe Komlodi         dw_i3c_push_rx(s, target_info.w[1]);
14159c0476a2SJoe Komlodi     }
14169c0476a2SJoe Komlodi 
14179c0476a2SJoe Komlodi transfer_done:
14189c0476a2SJoe Komlodi     /* Do we send a STOP? */
14199c0476a2SJoe Komlodi     if (cmd.toc) {
14209c0476a2SJoe Komlodi         dw_i3c_end_transfer(s, /*is_i2c=*/false);
14219c0476a2SJoe Komlodi     }
14229c0476a2SJoe Komlodi     /*
14239c0476a2SJoe Komlodi      * For addr assign commands, the length field is the number of devices
14249c0476a2SJoe Komlodi      * left to assign. CCCT is always 0 in controller mode.
14259c0476a2SJoe Komlodi      */
14269c0476a2SJoe Komlodi     if (cmd.roc) {
14279c0476a2SJoe Komlodi         dw_i3c_resp_queue_push(s, err, cmd.tid, /*ccc_type=*/0,
14289c0476a2SJoe Komlodi                                          cmd.dev_count - i);
14299c0476a2SJoe Komlodi     }
14309c0476a2SJoe Komlodi }
14319c0476a2SJoe Komlodi 
14329c0476a2SJoe Komlodi static uint32_t dw_i3c_cmd_queue_pop(DWI3C *s)
14339c0476a2SJoe Komlodi {
14349c0476a2SJoe Komlodi     if (fifo32_is_empty(&s->cmd_queue)) {
14359c0476a2SJoe Komlodi         g_autofree char *path = object_get_canonical_path(OBJECT(s));
14369c0476a2SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: Tried to dequeue command queue "
14379c0476a2SJoe Komlodi                       "when it was empty\n", path);
14389c0476a2SJoe Komlodi         return 0;
14399c0476a2SJoe Komlodi     }
14409c0476a2SJoe Komlodi     uint32_t val = fifo32_pop(&s->cmd_queue);
14419c0476a2SJoe Komlodi 
14429c0476a2SJoe Komlodi     uint8_t empty_threshold = ARRAY_FIELD_EX32(s->regs, QUEUE_THLD_CTRL,
14439c0476a2SJoe Komlodi                                                CMD_BUF_EMPTY_THLD);
14449c0476a2SJoe Komlodi     uint8_t cmd_queue_empty_loc = ARRAY_FIELD_EX32(s->regs,
14459c0476a2SJoe Komlodi                                                    QUEUE_STATUS_LEVEL,
14469c0476a2SJoe Komlodi                                                    CMD_QUEUE_EMPTY_LOC);
14479c0476a2SJoe Komlodi     cmd_queue_empty_loc++;
14489c0476a2SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, QUEUE_STATUS_LEVEL, CMD_QUEUE_EMPTY_LOC,
14499c0476a2SJoe Komlodi                      cmd_queue_empty_loc);
14509c0476a2SJoe Komlodi     if (cmd_queue_empty_loc >= empty_threshold) {
14519c0476a2SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, INTR_STATUS, CMD_QUEUE_RDY, 1);
14529c0476a2SJoe Komlodi         dw_i3c_update_irq(s);
14539c0476a2SJoe Komlodi     }
14549c0476a2SJoe Komlodi 
14559c0476a2SJoe Komlodi     return val;
14569c0476a2SJoe Komlodi }
14579c0476a2SJoe Komlodi 
14589c0476a2SJoe Komlodi static void dw_i3c_cmd_queue_execute(DWI3C *s)
14599c0476a2SJoe Komlodi {
14609c0476a2SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS,
14619c0476a2SJoe Komlodi                      DW_I3C_TRANSFER_STATE_IDLE);
14629c0476a2SJoe Komlodi     if (!dw_i3c_can_transmit(s)) {
14639c0476a2SJoe Komlodi         return;
14649c0476a2SJoe Komlodi     }
14659c0476a2SJoe Komlodi 
14669c0476a2SJoe Komlodi     /*
14679c0476a2SJoe Komlodi      * We only start executing when a command is passed into the FIFO.
14689c0476a2SJoe Komlodi      * We expect there to be a multiple of 2 items in the queue. The first item
14699c0476a2SJoe Komlodi      * should be an argument to a command, and the command should be the second
14709c0476a2SJoe Komlodi      * item.
14719c0476a2SJoe Komlodi      */
14729c0476a2SJoe Komlodi     if (fifo32_num_used(&s->cmd_queue) & 1) {
14739c0476a2SJoe Komlodi         return;
14749c0476a2SJoe Komlodi     }
14759c0476a2SJoe Komlodi 
14769c0476a2SJoe Komlodi     while (!fifo32_is_empty(&s->cmd_queue)) {
14779c0476a2SJoe Komlodi         DWI3CCmdQueueData arg;
14789c0476a2SJoe Komlodi         arg.word = dw_i3c_cmd_queue_pop(s);
14799c0476a2SJoe Komlodi         DWI3CCmdQueueData cmd;
14809c0476a2SJoe Komlodi         cmd.word = dw_i3c_cmd_queue_pop(s);
14819c0476a2SJoe Komlodi         trace_dw_i3c_cmd_queue_execute(s->cfg.id, cmd.word, arg.word);
14829c0476a2SJoe Komlodi 
14839c0476a2SJoe Komlodi         uint8_t cmd_attr = FIELD_EX32(cmd.word, COMMAND_QUEUE_PORT, CMD_ATTR);
14849c0476a2SJoe Komlodi         switch (cmd_attr) {
14859c0476a2SJoe Komlodi         case DW_I3C_CMD_ATTR_TRANSFER_CMD:
14869c0476a2SJoe Komlodi             dw_i3c_transfer_cmd(s, cmd.transfer_cmd, arg);
14879c0476a2SJoe Komlodi             break;
14889c0476a2SJoe Komlodi         case DW_I3C_CMD_ATTR_ADDR_ASSIGN_CMD:
14899c0476a2SJoe Komlodi             /* Arg is discarded for addr assign commands. */
14909c0476a2SJoe Komlodi             dw_i3c_addr_assign_cmd(s, cmd.addr_assign_cmd);
14919c0476a2SJoe Komlodi             break;
14929c0476a2SJoe Komlodi         case DW_I3C_CMD_ATTR_TRANSFER_ARG:
14939c0476a2SJoe Komlodi         case DW_I3C_CMD_ATTR_SHORT_DATA_ARG:
14949c0476a2SJoe Komlodi             {
14959c0476a2SJoe Komlodi                 g_autofree char *path = object_get_canonical_path(OBJECT(s));
14969c0476a2SJoe Komlodi                 qemu_log_mask(LOG_GUEST_ERROR, "%s: Command queue received "
14979c0476a2SJoe Komlodi                               "argument packet when it expected a command "
14989c0476a2SJoe Komlodi                               "packet\n", path);
14999c0476a2SJoe Komlodi             }
15009c0476a2SJoe Komlodi             break;
15019c0476a2SJoe Komlodi         default:
15029c0476a2SJoe Komlodi             /*
15039c0476a2SJoe Komlodi              * The caller's check before queueing an item should prevent this
15049c0476a2SJoe Komlodi              * from happening.
15059c0476a2SJoe Komlodi              */
15069c0476a2SJoe Komlodi             g_assert_not_reached();
15079c0476a2SJoe Komlodi             break;
15089c0476a2SJoe Komlodi         }
15099c0476a2SJoe Komlodi     }
15109c0476a2SJoe Komlodi }
15119c0476a2SJoe Komlodi 
15129c0476a2SJoe Komlodi static void dw_i3c_cmd_queue_push(DWI3C *s, uint32_t val)
15139c0476a2SJoe Komlodi {
15149c0476a2SJoe Komlodi     if (fifo32_is_full(&s->cmd_queue)) {
15159c0476a2SJoe Komlodi         g_autofree char *path = object_get_canonical_path(OBJECT(s));
15169c0476a2SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: Command queue received packet when "
15179c0476a2SJoe Komlodi                       "already full\n", path);
15189c0476a2SJoe Komlodi         return;
15199c0476a2SJoe Komlodi     }
15209c0476a2SJoe Komlodi     trace_dw_i3c_cmd_queue_push(s->cfg.id, val);
15219c0476a2SJoe Komlodi     fifo32_push(&s->cmd_queue, val);
15229c0476a2SJoe Komlodi 
15239c0476a2SJoe Komlodi     uint8_t empty_threshold = ARRAY_FIELD_EX32(s->regs, QUEUE_THLD_CTRL,
15249c0476a2SJoe Komlodi                                                CMD_BUF_EMPTY_THLD);
15259c0476a2SJoe Komlodi     uint8_t cmd_queue_empty_loc = ARRAY_FIELD_EX32(s->regs,
15269c0476a2SJoe Komlodi                                                    QUEUE_STATUS_LEVEL,
15279c0476a2SJoe Komlodi                                                    CMD_QUEUE_EMPTY_LOC);
15289c0476a2SJoe Komlodi     if (cmd_queue_empty_loc) {
15299c0476a2SJoe Komlodi         cmd_queue_empty_loc--;
15309c0476a2SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, QUEUE_STATUS_LEVEL, CMD_QUEUE_EMPTY_LOC,
15319c0476a2SJoe Komlodi                          cmd_queue_empty_loc);
15329c0476a2SJoe Komlodi     }
15339c0476a2SJoe Komlodi     if (cmd_queue_empty_loc < empty_threshold) {
15349c0476a2SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, INTR_STATUS, CMD_QUEUE_RDY, 0);
15359c0476a2SJoe Komlodi         dw_i3c_update_irq(s);
15369c0476a2SJoe Komlodi     }
15379c0476a2SJoe Komlodi }
15389c0476a2SJoe Komlodi 
15399c0476a2SJoe Komlodi static void dw_i3c_cmd_queue_port_w(DWI3C *s, uint32_t val)
15409c0476a2SJoe Komlodi {
15419c0476a2SJoe Komlodi     uint8_t cmd_attr = FIELD_EX32(val, COMMAND_QUEUE_PORT, CMD_ATTR);
15429c0476a2SJoe Komlodi 
15439c0476a2SJoe Komlodi     switch (cmd_attr) {
15449c0476a2SJoe Komlodi     /* If a command is received we can start executing it. */
15459c0476a2SJoe Komlodi     case DW_I3C_CMD_ATTR_TRANSFER_CMD:
15469c0476a2SJoe Komlodi     case DW_I3C_CMD_ATTR_ADDR_ASSIGN_CMD:
15479c0476a2SJoe Komlodi         dw_i3c_cmd_queue_push(s, val);
15489c0476a2SJoe Komlodi         dw_i3c_cmd_queue_execute(s);
15499c0476a2SJoe Komlodi         break;
15509c0476a2SJoe Komlodi     /* If we get an argument just push it. */
15519c0476a2SJoe Komlodi     case DW_I3C_CMD_ATTR_TRANSFER_ARG:
15529c0476a2SJoe Komlodi     case DW_I3C_CMD_ATTR_SHORT_DATA_ARG:
15539c0476a2SJoe Komlodi         dw_i3c_cmd_queue_push(s, val);
15549c0476a2SJoe Komlodi         break;
15559c0476a2SJoe Komlodi     default:
15569c0476a2SJoe Komlodi         {
15579c0476a2SJoe Komlodi             g_autofree char *path = object_get_canonical_path(OBJECT(s));
15589c0476a2SJoe Komlodi             qemu_log_mask(LOG_GUEST_ERROR, "%s: Command queue received packet "
15599c0476a2SJoe Komlodi                           "with unknown cmd attr 0x%x\n", path, cmd_attr);
15609c0476a2SJoe Komlodi         }
15619c0476a2SJoe Komlodi         break;
15629c0476a2SJoe Komlodi     }
15639c0476a2SJoe Komlodi }
15649c0476a2SJoe Komlodi 
1565c52aaabdSJoe Komlodi static void dw_i3c_write(void *opaque, hwaddr offset, uint64_t value,
1566c52aaabdSJoe Komlodi                          unsigned size)
1567c52aaabdSJoe Komlodi {
1568c52aaabdSJoe Komlodi     DWI3C *s = DW_I3C(opaque);
1569c52aaabdSJoe Komlodi     uint32_t addr = offset >> 2;
15707e5e02dbSJoe Komlodi     uint32_t val32 = (uint32_t)value;
1571c52aaabdSJoe Komlodi 
15729c0476a2SJoe Komlodi     trace_dw_i3c_write(s->cfg.id, offset, value);
1573c52aaabdSJoe Komlodi 
15747e5e02dbSJoe Komlodi     val32 &= ~dw_i3c_ro[addr];
1575c52aaabdSJoe Komlodi     switch (addr) {
1576c52aaabdSJoe Komlodi     case R_HW_CAPABILITY:
1577c52aaabdSJoe Komlodi     case R_RESPONSE_QUEUE_PORT:
1578c52aaabdSJoe Komlodi     case R_IBI_QUEUE_DATA:
1579c52aaabdSJoe Komlodi     case R_QUEUE_STATUS_LEVEL:
1580c52aaabdSJoe Komlodi     case R_PRESENT_STATE:
1581c52aaabdSJoe Komlodi     case R_CCC_DEVICE_STATUS:
1582c52aaabdSJoe Komlodi     case R_DEVICE_ADDR_TABLE_POINTER:
1583c52aaabdSJoe Komlodi     case R_VENDOR_SPECIFIC_REG_POINTER:
1584c52aaabdSJoe Komlodi     case R_SLV_CHAR_CTRL:
1585c52aaabdSJoe Komlodi     case R_SLV_MAX_LEN:
1586c52aaabdSJoe Komlodi     case R_MAX_READ_TURNAROUND:
1587c52aaabdSJoe Komlodi     case R_I3C_VER_ID:
1588c52aaabdSJoe Komlodi     case R_I3C_VER_TYPE:
1589c52aaabdSJoe Komlodi     case R_EXTENDED_CAPABILITY:
1590c52aaabdSJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR,
1591c52aaabdSJoe Komlodi                       "%s: write to readonly register[0x%02" HWADDR_PRIx
1592c52aaabdSJoe Komlodi                       "] = 0x%08" PRIx64 "\n",
1593c52aaabdSJoe Komlodi                       __func__, offset, value);
1594c52aaabdSJoe Komlodi         break;
1595c52aaabdSJoe Komlodi     case R_RX_TX_DATA_PORT:
15969c0476a2SJoe Komlodi         dw_i3c_push_tx(s, val32);
15979c0476a2SJoe Komlodi         break;
15989c0476a2SJoe Komlodi     case R_COMMAND_QUEUE_PORT:
15999c0476a2SJoe Komlodi         dw_i3c_cmd_queue_port_w(s, val32);
1600c52aaabdSJoe Komlodi         break;
1601c52aaabdSJoe Komlodi     case R_RESET_CTRL:
1602c52aaabdSJoe Komlodi         break;
1603a825bbb7SJoe Komlodi     case R_INTR_STATUS:
1604a825bbb7SJoe Komlodi         dw_i3c_intr_status_w(s, val32);
1605a825bbb7SJoe Komlodi         break;
1606a825bbb7SJoe Komlodi     case R_INTR_STATUS_EN:
1607a825bbb7SJoe Komlodi         dw_i3c_intr_status_en_w(s, val32);
1608a825bbb7SJoe Komlodi         break;
1609a825bbb7SJoe Komlodi     case R_INTR_SIGNAL_EN:
1610a825bbb7SJoe Komlodi         dw_i3c_intr_signal_en_w(s, val32);
1611a825bbb7SJoe Komlodi         break;
1612a825bbb7SJoe Komlodi     case R_INTR_FORCE:
1613a825bbb7SJoe Komlodi         dw_i3c_intr_force_w(s, val32);
1614a825bbb7SJoe Komlodi         break;
1615c52aaabdSJoe Komlodi     default:
16167e5e02dbSJoe Komlodi         s->regs[addr] = val32;
1617c52aaabdSJoe Komlodi         break;
1618c52aaabdSJoe Komlodi     }
1619c52aaabdSJoe Komlodi }
1620c52aaabdSJoe Komlodi 
1621c52aaabdSJoe Komlodi const VMStateDescription vmstate_dw_i3c = {
1622c52aaabdSJoe Komlodi     .name = TYPE_DW_I3C,
1623c52aaabdSJoe Komlodi     .version_id = 1,
1624c52aaabdSJoe Komlodi     .minimum_version_id = 1,
1625c52aaabdSJoe Komlodi     .fields = (VMStateField[]){
1626c52aaabdSJoe Komlodi         VMSTATE_UINT32_ARRAY(regs, DWI3C, DW_I3C_NR_REGS),
1627c52aaabdSJoe Komlodi         VMSTATE_END_OF_LIST(),
1628c52aaabdSJoe Komlodi     }
1629c52aaabdSJoe Komlodi };
1630c52aaabdSJoe Komlodi 
1631c52aaabdSJoe Komlodi static const MemoryRegionOps dw_i3c_ops = {
1632c52aaabdSJoe Komlodi     .read = dw_i3c_read,
1633c52aaabdSJoe Komlodi     .write = dw_i3c_write,
1634c52aaabdSJoe Komlodi     .endianness = DEVICE_LITTLE_ENDIAN,
1635c52aaabdSJoe Komlodi };
1636c52aaabdSJoe Komlodi 
1637c52aaabdSJoe Komlodi static void dw_i3c_reset_enter(Object *obj, ResetType type)
1638c52aaabdSJoe Komlodi {
1639c52aaabdSJoe Komlodi     DWI3C *s = DW_I3C(obj);
1640c52aaabdSJoe Komlodi 
1641c52aaabdSJoe Komlodi     memcpy(s->regs, dw_i3c_resets, sizeof(s->regs));
16429c0476a2SJoe Komlodi     /*
16439c0476a2SJoe Komlodi      * The user config for these may differ from our resets array, set them
16449c0476a2SJoe Komlodi      * manually.
16459c0476a2SJoe Komlodi      */
16469c0476a2SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, DEVICE_ADDR_TABLE_POINTER, ADDR,
16479c0476a2SJoe Komlodi                      s->cfg.dev_addr_table_pointer);
16489c0476a2SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, DEVICE_ADDR_TABLE_POINTER, DEPTH,
16499c0476a2SJoe Komlodi                      s->cfg.dev_addr_table_depth);
16509c0476a2SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, DEV_CHAR_TABLE_POINTER,
16519c0476a2SJoe Komlodi                      P_DEV_CHAR_TABLE_START_ADDR,
16529c0476a2SJoe Komlodi                      s->cfg.dev_char_table_pointer);
16539c0476a2SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, DEV_CHAR_TABLE_POINTER, DEV_CHAR_TABLE_DEPTH,
16549c0476a2SJoe Komlodi                      s->cfg.dev_char_table_depth);
1655c52aaabdSJoe Komlodi }
1656c52aaabdSJoe Komlodi 
1657c52aaabdSJoe Komlodi static void dw_i3c_realize(DeviceState *dev, Error **errp)
1658c52aaabdSJoe Komlodi {
1659c52aaabdSJoe Komlodi     DWI3C *s = DW_I3C(dev);
16609c0476a2SJoe Komlodi     g_autofree char *name = g_strdup_printf(TYPE_DW_I3C ".%d", s->cfg.id);
1661c52aaabdSJoe Komlodi 
1662c52aaabdSJoe Komlodi     sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq);
1663c52aaabdSJoe Komlodi 
1664c52aaabdSJoe Komlodi     memory_region_init_io(&s->mr, OBJECT(s), &dw_i3c_ops, s, name,
1665c52aaabdSJoe Komlodi                           DW_I3C_NR_REGS << 2);
1666c52aaabdSJoe Komlodi     sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mr);
16679c0476a2SJoe Komlodi 
16689c0476a2SJoe Komlodi     fifo32_create(&s->cmd_queue, s->cfg.cmd_resp_queue_capacity_bytes);
16699c0476a2SJoe Komlodi     fifo32_create(&s->resp_queue, s->cfg.cmd_resp_queue_capacity_bytes);
16709c0476a2SJoe Komlodi     fifo32_create(&s->tx_queue, s->cfg.tx_rx_queue_capacity_bytes);
16719c0476a2SJoe Komlodi     fifo32_create(&s->rx_queue, s->cfg.tx_rx_queue_capacity_bytes);
1672*5a2a5453SJoe Komlodi     fifo32_create(&s->ibi_queue, s->cfg.ibi_queue_capacity_bytes);
1673*5a2a5453SJoe Komlodi     /* Arbitrarily large enough to not be an issue. */
1674*5a2a5453SJoe Komlodi     fifo8_create(&s->ibi_data.ibi_intermediate_queue,
1675*5a2a5453SJoe Komlodi                  s->cfg.ibi_queue_capacity_bytes * 8);
16769c0476a2SJoe Komlodi 
16779c0476a2SJoe Komlodi     s->bus = i3c_init_bus(DEVICE(s), name);
1678*5a2a5453SJoe Komlodi     I3CBusClass *bc = I3C_BUS_GET_CLASS(s->bus);
1679*5a2a5453SJoe Komlodi     bc->ibi_handle = dw_i3c_ibi_handle;
1680*5a2a5453SJoe Komlodi     bc->ibi_recv = dw_i3c_ibi_recv;
1681*5a2a5453SJoe Komlodi     bc->ibi_finish = dw_i3c_ibi_finish;
1682c52aaabdSJoe Komlodi }
1683c52aaabdSJoe Komlodi 
1684c52aaabdSJoe Komlodi static const Property dw_i3c_properties[] = {
16859c0476a2SJoe Komlodi     DEFINE_PROP_UINT8("device-id", DWI3C, cfg.id, 0),
16869c0476a2SJoe Komlodi     DEFINE_PROP_UINT8("command-response-queue-capacity-bytes", DWI3C,
16879c0476a2SJoe Komlodi                       cfg.cmd_resp_queue_capacity_bytes, 0x10),
16889c0476a2SJoe Komlodi     DEFINE_PROP_UINT16("tx-rx-queue-capacity-bytes", DWI3C,
16899c0476a2SJoe Komlodi                       cfg.tx_rx_queue_capacity_bytes, 0x40),
1690*5a2a5453SJoe Komlodi     DEFINE_PROP_UINT8("ibi-queue-capacity-bytes", DWI3C,
1691*5a2a5453SJoe Komlodi                       cfg.ibi_queue_capacity_bytes, 0x10),
16929c0476a2SJoe Komlodi     DEFINE_PROP_UINT8("num-addressable-devices", DWI3C,
16939c0476a2SJoe Komlodi                       cfg.num_addressable_devices, 8),
16949c0476a2SJoe Komlodi     DEFINE_PROP_UINT16("dev-addr-table-pointer", DWI3C,
16959c0476a2SJoe Komlodi                        cfg.dev_addr_table_pointer, 0x280),
16969c0476a2SJoe Komlodi     DEFINE_PROP_UINT16("dev-addr-table-depth", DWI3C,
16979c0476a2SJoe Komlodi                        cfg.dev_addr_table_depth, 0x08),
16989c0476a2SJoe Komlodi     DEFINE_PROP_UINT16("dev-char-table-pointer", DWI3C,
16999c0476a2SJoe Komlodi                        cfg.dev_char_table_pointer, 0x200),
17009c0476a2SJoe Komlodi     DEFINE_PROP_UINT16("dev-char-table-depth", DWI3C,
17019c0476a2SJoe Komlodi                        cfg.dev_char_table_depth, 0x20),
1702c52aaabdSJoe Komlodi };
1703c52aaabdSJoe Komlodi 
1704c52aaabdSJoe Komlodi static void dw_i3c_class_init(ObjectClass *klass, const void *data)
1705c52aaabdSJoe Komlodi {
1706c52aaabdSJoe Komlodi     DeviceClass *dc = DEVICE_CLASS(klass);
1707c52aaabdSJoe Komlodi     ResettableClass *rc = RESETTABLE_CLASS(klass);
1708c52aaabdSJoe Komlodi 
1709c52aaabdSJoe Komlodi     rc->phases.enter = dw_i3c_reset_enter;
1710c52aaabdSJoe Komlodi 
1711c52aaabdSJoe Komlodi     dc->desc = "DesignWare I3C Controller";
1712c52aaabdSJoe Komlodi     dc->realize = dw_i3c_realize;
1713c52aaabdSJoe Komlodi     dc->vmsd = &vmstate_dw_i3c;
1714c52aaabdSJoe Komlodi     device_class_set_props(dc, dw_i3c_properties);
1715c52aaabdSJoe Komlodi }
1716c52aaabdSJoe Komlodi 
1717c52aaabdSJoe Komlodi static const TypeInfo dw_i3c_info = {
1718c52aaabdSJoe Komlodi     .name = TYPE_DW_I3C,
1719c52aaabdSJoe Komlodi     .parent = TYPE_SYS_BUS_DEVICE,
1720c52aaabdSJoe Komlodi     .instance_size = sizeof(DWI3C),
1721c52aaabdSJoe Komlodi     .class_init = dw_i3c_class_init,
1722c52aaabdSJoe Komlodi };
1723c52aaabdSJoe Komlodi 
1724c52aaabdSJoe Komlodi static void dw_i3c_register_types(void)
1725c52aaabdSJoe Komlodi {
1726c52aaabdSJoe Komlodi     type_register_static(&dw_i3c_info);
1727c52aaabdSJoe Komlodi }
1728c52aaabdSJoe Komlodi 
1729c52aaabdSJoe Komlodi type_init(dw_i3c_register_types);
1730