xref: /openbmc/qemu/hw/i3c/dw-i3c.c (revision 9c0476a21da3b8b027399f93be86e8c06c92590c)
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 
22c52aaabdSJoe Komlodi REG32(DEVICE_CTRL,                  0x00)
2359b8d0c9SJoe Komlodi     FIELD(DEVICE_CTRL, I3C_BROADCAST_ADDR_INC,    0, 1)
2459b8d0c9SJoe Komlodi     FIELD(DEVICE_CTRL, I2C_SLAVE_PRESENT,         7, 1)
2559b8d0c9SJoe Komlodi     FIELD(DEVICE_CTRL, HOT_JOIN_ACK_NACK_CTRL,    8, 1)
2659b8d0c9SJoe Komlodi     FIELD(DEVICE_CTRL, IDLE_CNT_MULTIPLIER,       24, 2)
2759b8d0c9SJoe Komlodi     FIELD(DEVICE_CTRL, SLV_ADAPT_TO_I2C_I3C_MODE, 27, 1)
2859b8d0c9SJoe Komlodi     FIELD(DEVICE_CTRL, DMA_HANDSHAKE_EN,          28, 1)
2959b8d0c9SJoe Komlodi     FIELD(DEVICE_CTRL, I3C_ABORT,                 29, 1)
3059b8d0c9SJoe Komlodi     FIELD(DEVICE_CTRL, I3C_RESUME,                30, 1)
3159b8d0c9SJoe Komlodi     FIELD(DEVICE_CTRL, I3C_EN,                    31, 1)
32c52aaabdSJoe Komlodi REG32(DEVICE_ADDR,                  0x04)
3359b8d0c9SJoe Komlodi     FIELD(DEVICE_ADDR, STATIC_ADDR,         0, 7)
3459b8d0c9SJoe Komlodi     FIELD(DEVICE_ADDR, STATIC_ADDR_VALID,   15, 1)
3559b8d0c9SJoe Komlodi     FIELD(DEVICE_ADDR, DYNAMIC_ADDR,        16, 7)
3659b8d0c9SJoe Komlodi     FIELD(DEVICE_ADDR, DYNAMIC_ADDR_VALID,  15, 1)
37c52aaabdSJoe Komlodi REG32(HW_CAPABILITY,                0x08)
3859b8d0c9SJoe Komlodi     FIELD(HW_CAPABILITY, DEVICE_ROLE_CONFIG,  0, 2)
3959b8d0c9SJoe Komlodi     FIELD(HW_CAPABILITY, HDR_DDR, 3, 1)
4059b8d0c9SJoe Komlodi     FIELD(HW_CAPABILITY, HDR_TS,  4, 1)
41c52aaabdSJoe Komlodi REG32(COMMAND_QUEUE_PORT,           0x0c)
4259b8d0c9SJoe Komlodi     FIELD(COMMAND_QUEUE_PORT, CMD_ATTR, 0, 3)
4359b8d0c9SJoe Komlodi     /* Transfer command structure */
4459b8d0c9SJoe Komlodi     FIELD(COMMAND_QUEUE_PORT, TID, 3, 4)
4559b8d0c9SJoe Komlodi     FIELD(COMMAND_QUEUE_PORT, CMD, 7, 8)
4659b8d0c9SJoe Komlodi     FIELD(COMMAND_QUEUE_PORT, CP, 15, 1)
4759b8d0c9SJoe Komlodi     FIELD(COMMAND_QUEUE_PORT, DEV_INDEX, 16, 5)
4859b8d0c9SJoe Komlodi     FIELD(COMMAND_QUEUE_PORT, SPEED, 21, 3)
4959b8d0c9SJoe Komlodi     FIELD(COMMAND_QUEUE_PORT, ROC, 26, 1)
5059b8d0c9SJoe Komlodi     FIELD(COMMAND_QUEUE_PORT, SDAP, 27, 1)
5159b8d0c9SJoe Komlodi     FIELD(COMMAND_QUEUE_PORT, RNW, 28, 1)
5259b8d0c9SJoe Komlodi     FIELD(COMMAND_QUEUE_PORT, TOC, 30, 1)
5359b8d0c9SJoe Komlodi     FIELD(COMMAND_QUEUE_PORT, PEC, 31, 1)
5459b8d0c9SJoe Komlodi     /* Transfer argument data structure */
5559b8d0c9SJoe Komlodi     FIELD(COMMAND_QUEUE_PORT, DB, 8, 8)
5659b8d0c9SJoe Komlodi     FIELD(COMMAND_QUEUE_PORT, DL, 16, 16)
5759b8d0c9SJoe Komlodi     /* Short data argument data structure */
5859b8d0c9SJoe Komlodi     FIELD(COMMAND_QUEUE_PORT, BYTE_STRB, 3, 3)
5959b8d0c9SJoe Komlodi     FIELD(COMMAND_QUEUE_PORT, BYTE0, 8, 8)
6059b8d0c9SJoe Komlodi     FIELD(COMMAND_QUEUE_PORT, BYTE1, 16, 8)
6159b8d0c9SJoe Komlodi     FIELD(COMMAND_QUEUE_PORT, BYTE2, 24, 8)
6259b8d0c9SJoe Komlodi     /* Address assignment command structure */
6359b8d0c9SJoe Komlodi     /*
6459b8d0c9SJoe Komlodi      * bits 3..21 and 26..31 are the same as the transfer command structure, or
6559b8d0c9SJoe Komlodi      * marked as reserved.
6659b8d0c9SJoe Komlodi      */
6759b8d0c9SJoe Komlodi     FIELD(COMMAND_QUEUE_PORT, DEV_COUNT, 21, 3)
68c52aaabdSJoe Komlodi REG32(RESPONSE_QUEUE_PORT,          0x10)
6959b8d0c9SJoe Komlodi     FIELD(RESPONSE_QUEUE_PORT, DL, 0, 16)
7059b8d0c9SJoe Komlodi     FIELD(RESPONSE_QUEUE_PORT, CCCT, 16, 8)
7159b8d0c9SJoe Komlodi     FIELD(RESPONSE_QUEUE_PORT, TID, 24, 4)
7259b8d0c9SJoe Komlodi     FIELD(RESPONSE_QUEUE_PORT, ERR_STATUS, 28, 4)
73c52aaabdSJoe Komlodi REG32(RX_TX_DATA_PORT,              0x14)
74c52aaabdSJoe Komlodi REG32(IBI_QUEUE_STATUS,             0x18)
7559b8d0c9SJoe Komlodi     FIELD(IBI_QUEUE_STATUS, IBI_DATA_LEN,   0, 8)
7659b8d0c9SJoe Komlodi     FIELD(IBI_QUEUE_STATUS, IBI_ID,         8, 8)
7759b8d0c9SJoe Komlodi     FIELD(IBI_QUEUE_STATUS, LAST_STATUS,    24, 1)
7859b8d0c9SJoe Komlodi     FIELD(IBI_QUEUE_STATUS, ERROR,          30, 1)
7959b8d0c9SJoe Komlodi     FIELD(IBI_QUEUE_STATUS, IBI_STATUS,     31, 1)
80c52aaabdSJoe Komlodi REG32(IBI_QUEUE_DATA,               0x18)
81c52aaabdSJoe Komlodi REG32(QUEUE_THLD_CTRL,              0x1c)
8259b8d0c9SJoe Komlodi     FIELD(QUEUE_THLD_CTRL, CMD_BUF_EMPTY_THLD,  0, 8);
8359b8d0c9SJoe Komlodi     FIELD(QUEUE_THLD_CTRL, RESP_BUF_THLD, 8, 8);
8459b8d0c9SJoe Komlodi     FIELD(QUEUE_THLD_CTRL, IBI_DATA_THLD, 16, 8);
8559b8d0c9SJoe Komlodi     FIELD(QUEUE_THLD_CTRL, IBI_STATUS_THLD,     24, 8);
86c52aaabdSJoe Komlodi REG32(DATA_BUFFER_THLD_CTRL,        0x20)
8759b8d0c9SJoe Komlodi     FIELD(DATA_BUFFER_THLD_CTRL, TX_BUF_THLD,   0, 3)
8859b8d0c9SJoe Komlodi     FIELD(DATA_BUFFER_THLD_CTRL, RX_BUF_THLD,   10, 3)
8959b8d0c9SJoe Komlodi     FIELD(DATA_BUFFER_THLD_CTRL, TX_START_THLD, 16, 3)
9059b8d0c9SJoe Komlodi     FIELD(DATA_BUFFER_THLD_CTRL, RX_START_THLD, 24, 3)
91c52aaabdSJoe Komlodi REG32(IBI_QUEUE_CTRL,               0x24)
9259b8d0c9SJoe Komlodi     FIELD(IBI_QUEUE_CTRL, NOTIFY_REJECTED_HOT_JOIN,   0, 1)
9359b8d0c9SJoe Komlodi     FIELD(IBI_QUEUE_CTRL, NOTIFY_REJECTED_MASTER_REQ, 1, 1)
9459b8d0c9SJoe Komlodi     FIELD(IBI_QUEUE_CTRL, NOTIFY_REJECTED_SLAVE_IRQ,  3, 1)
95c52aaabdSJoe Komlodi REG32(IBI_MR_REQ_REJECT,            0x2c)
96c52aaabdSJoe Komlodi REG32(IBI_SIR_REQ_REJECT,           0x30)
97c52aaabdSJoe Komlodi REG32(RESET_CTRL,                   0x34)
9859b8d0c9SJoe Komlodi     FIELD(RESET_CTRL, CORE_RESET,       0, 1)
9959b8d0c9SJoe Komlodi     FIELD(RESET_CTRL, CMD_QUEUE_RESET,  1, 1)
10059b8d0c9SJoe Komlodi     FIELD(RESET_CTRL, RESP_QUEUE_RESET, 2, 1)
10159b8d0c9SJoe Komlodi     FIELD(RESET_CTRL, TX_BUF_RESET,     3, 1)
10259b8d0c9SJoe Komlodi     FIELD(RESET_CTRL, RX_BUF_RESET,     4, 1)
10359b8d0c9SJoe Komlodi     FIELD(RESET_CTRL, IBI_QUEUE_RESET,  5, 1)
104c52aaabdSJoe Komlodi REG32(SLV_EVENT_CTRL,               0x38)
10559b8d0c9SJoe Komlodi     FIELD(SLV_EVENT_CTRL, SLV_INTERRUPT,      0, 1)
10659b8d0c9SJoe Komlodi     FIELD(SLV_EVENT_CTRL, MASTER_INTERRUPT,   1, 1)
10759b8d0c9SJoe Komlodi     FIELD(SLV_EVENT_CTRL, HOT_JOIN_INTERRUPT, 3, 1)
10859b8d0c9SJoe Komlodi     FIELD(SLV_EVENT_CTRL, ACTIVITY_STATE,     4, 2)
10959b8d0c9SJoe Komlodi     FIELD(SLV_EVENT_CTRL, MRL_UPDATED,        6, 1)
11059b8d0c9SJoe Komlodi     FIELD(SLV_EVENT_CTRL, MWL_UPDATED,        7, 1)
111c52aaabdSJoe Komlodi REG32(INTR_STATUS,                  0x3c)
11259b8d0c9SJoe Komlodi     FIELD(INTR_STATUS, TX_THLD,           0, 1)
11359b8d0c9SJoe Komlodi     FIELD(INTR_STATUS, RX_THLD,           1, 1)
11459b8d0c9SJoe Komlodi     FIELD(INTR_STATUS, IBI_THLD,          2, 1)
11559b8d0c9SJoe Komlodi     FIELD(INTR_STATUS, CMD_QUEUE_RDY,     3, 1)
11659b8d0c9SJoe Komlodi     FIELD(INTR_STATUS, RESP_RDY,          4, 1)
11759b8d0c9SJoe Komlodi     FIELD(INTR_STATUS, TRANSFER_ABORT,    5, 1)
11859b8d0c9SJoe Komlodi     FIELD(INTR_STATUS, CCC_UPDATED,       6, 1)
11959b8d0c9SJoe Komlodi     FIELD(INTR_STATUS, DYN_ADDR_ASSGN,    8, 1)
12059b8d0c9SJoe Komlodi     FIELD(INTR_STATUS, TRANSFER_ERR,      9, 1)
12159b8d0c9SJoe Komlodi     FIELD(INTR_STATUS, DEFSLV,            10, 1)
12259b8d0c9SJoe Komlodi     FIELD(INTR_STATUS, READ_REQ_RECV,     11, 1)
12359b8d0c9SJoe Komlodi     FIELD(INTR_STATUS, IBI_UPDATED,       12, 1)
12459b8d0c9SJoe Komlodi     FIELD(INTR_STATUS, BUSOWNER_UPDATED,  13, 1)
125c52aaabdSJoe Komlodi REG32(INTR_STATUS_EN,               0x40)
12659b8d0c9SJoe Komlodi     FIELD(INTR_STATUS_EN, TX_THLD,          0, 1)
12759b8d0c9SJoe Komlodi     FIELD(INTR_STATUS_EN, RX_THLD,          1, 1)
12859b8d0c9SJoe Komlodi     FIELD(INTR_STATUS_EN, IBI_THLD,         2, 1)
12959b8d0c9SJoe Komlodi     FIELD(INTR_STATUS_EN, CMD_QUEUE_RDY,    3, 1)
13059b8d0c9SJoe Komlodi     FIELD(INTR_STATUS_EN, RESP_RDY,         4, 1)
13159b8d0c9SJoe Komlodi     FIELD(INTR_STATUS_EN, TRANSFER_ABORT,   5, 1)
13259b8d0c9SJoe Komlodi     FIELD(INTR_STATUS_EN, CCC_UPDATED,      6, 1)
13359b8d0c9SJoe Komlodi     FIELD(INTR_STATUS_EN, DYN_ADDR_ASSGN,   8, 1)
13459b8d0c9SJoe Komlodi     FIELD(INTR_STATUS_EN, TRANSFER_ERR,     9, 1)
13559b8d0c9SJoe Komlodi     FIELD(INTR_STATUS_EN, DEFSLV,           10, 1)
13659b8d0c9SJoe Komlodi     FIELD(INTR_STATUS_EN, READ_REQ_RECV,    11, 1)
13759b8d0c9SJoe Komlodi     FIELD(INTR_STATUS_EN, IBI_UPDATED,      12, 1)
13859b8d0c9SJoe Komlodi     FIELD(INTR_STATUS_EN, BUSOWNER_UPDATED, 13, 1)
139c52aaabdSJoe Komlodi REG32(INTR_SIGNAL_EN,               0x44)
14059b8d0c9SJoe Komlodi     FIELD(INTR_SIGNAL_EN, TX_THLD,          0, 1)
14159b8d0c9SJoe Komlodi     FIELD(INTR_SIGNAL_EN, RX_THLD,          1, 1)
14259b8d0c9SJoe Komlodi     FIELD(INTR_SIGNAL_EN, IBI_THLD,         2, 1)
14359b8d0c9SJoe Komlodi     FIELD(INTR_SIGNAL_EN, CMD_QUEUE_RDY,    3, 1)
14459b8d0c9SJoe Komlodi     FIELD(INTR_SIGNAL_EN, RESP_RDY,         4, 1)
14559b8d0c9SJoe Komlodi     FIELD(INTR_SIGNAL_EN, TRANSFER_ABORT,   5, 1)
14659b8d0c9SJoe Komlodi     FIELD(INTR_SIGNAL_EN, CCC_UPDATED,      6, 1)
14759b8d0c9SJoe Komlodi     FIELD(INTR_SIGNAL_EN, DYN_ADDR_ASSGN,   8, 1)
14859b8d0c9SJoe Komlodi     FIELD(INTR_SIGNAL_EN, TRANSFER_ERR,     9, 1)
14959b8d0c9SJoe Komlodi     FIELD(INTR_SIGNAL_EN, DEFSLV,           10, 1)
15059b8d0c9SJoe Komlodi     FIELD(INTR_SIGNAL_EN, READ_REQ_RECV,    11, 1)
15159b8d0c9SJoe Komlodi     FIELD(INTR_SIGNAL_EN, IBI_UPDATED,      12, 1)
15259b8d0c9SJoe Komlodi     FIELD(INTR_SIGNAL_EN, BUSOWNER_UPDATED, 13, 1)
153c52aaabdSJoe Komlodi REG32(INTR_FORCE,                   0x48)
15459b8d0c9SJoe Komlodi     FIELD(INTR_FORCE, TX_THLD,          0, 1)
15559b8d0c9SJoe Komlodi     FIELD(INTR_FORCE, RX_THLD,          1, 1)
15659b8d0c9SJoe Komlodi     FIELD(INTR_FORCE, IBI_THLD,         2, 1)
15759b8d0c9SJoe Komlodi     FIELD(INTR_FORCE, CMD_QUEUE_RDY,    3, 1)
15859b8d0c9SJoe Komlodi     FIELD(INTR_FORCE, RESP_RDY,         4, 1)
15959b8d0c9SJoe Komlodi     FIELD(INTR_FORCE, TRANSFER_ABORT,   5, 1)
16059b8d0c9SJoe Komlodi     FIELD(INTR_FORCE, CCC_UPDATED,      6, 1)
16159b8d0c9SJoe Komlodi     FIELD(INTR_FORCE, DYN_ADDR_ASSGN,   8, 1)
16259b8d0c9SJoe Komlodi     FIELD(INTR_FORCE, TRANSFER_ERR,     9, 1)
16359b8d0c9SJoe Komlodi     FIELD(INTR_FORCE, DEFSLV,           10, 1)
16459b8d0c9SJoe Komlodi     FIELD(INTR_FORCE, READ_REQ_RECV,    11, 1)
16559b8d0c9SJoe Komlodi     FIELD(INTR_FORCE, IBI_UPDATED,      12, 1)
16659b8d0c9SJoe Komlodi     FIELD(INTR_FORCE, BUSOWNER_UPDATED, 13, 1)
167c52aaabdSJoe Komlodi REG32(QUEUE_STATUS_LEVEL,           0x4c)
16859b8d0c9SJoe Komlodi     FIELD(QUEUE_STATUS_LEVEL, CMD_QUEUE_EMPTY_LOC,  0, 8)
16959b8d0c9SJoe Komlodi     FIELD(QUEUE_STATUS_LEVEL, RESP_BUF_BLR,         8, 8)
17059b8d0c9SJoe Komlodi     FIELD(QUEUE_STATUS_LEVEL, IBI_BUF_BLR,          16, 8)
17159b8d0c9SJoe Komlodi     FIELD(QUEUE_STATUS_LEVEL, IBI_STATUS_CNT,       24, 5)
172c52aaabdSJoe Komlodi REG32(DATA_BUFFER_STATUS_LEVEL,     0x50)
17359b8d0c9SJoe Komlodi     FIELD(DATA_BUFFER_STATUS_LEVEL, TX_BUF_EMPTY_LOC, 0, 8)
17459b8d0c9SJoe Komlodi     FIELD(DATA_BUFFER_STATUS_LEVEL, RX_BUF_BLR,       16, 8)
175c52aaabdSJoe Komlodi REG32(PRESENT_STATE,                0x54)
17659b8d0c9SJoe Komlodi     FIELD(PRESENT_STATE, SCL_LINE_SIGNAL_LEVEL, 0, 1)
17759b8d0c9SJoe Komlodi     FIELD(PRESENT_STATE, SDA_LINE_SIGNAL_LEVEL, 1, 1)
17859b8d0c9SJoe Komlodi     FIELD(PRESENT_STATE, CURRENT_MASTER,        2, 1)
17959b8d0c9SJoe Komlodi     FIELD(PRESENT_STATE, CM_TFR_STATUS,         8, 6)
18059b8d0c9SJoe Komlodi     FIELD(PRESENT_STATE, CM_TFR_ST_STATUS,      16, 6)
18159b8d0c9SJoe Komlodi     FIELD(PRESENT_STATE, CMD_TID,               24, 4)
182c52aaabdSJoe Komlodi REG32(CCC_DEVICE_STATUS,            0x58)
18359b8d0c9SJoe Komlodi     FIELD(CCC_DEVICE_STATUS, PENDING_INTR,      0, 4)
18459b8d0c9SJoe Komlodi     FIELD(CCC_DEVICE_STATUS, PROTOCOL_ERR,      4, 2)
18559b8d0c9SJoe Komlodi     FIELD(CCC_DEVICE_STATUS, ACTIVITY_MODE,     6, 2)
18659b8d0c9SJoe Komlodi     FIELD(CCC_DEVICE_STATUS, UNDER_ERR,         8, 1)
18759b8d0c9SJoe Komlodi     FIELD(CCC_DEVICE_STATUS, SLV_BUSY,          9, 1)
18859b8d0c9SJoe Komlodi     FIELD(CCC_DEVICE_STATUS, OVERFLOW_ERR,      10, 1)
18959b8d0c9SJoe Komlodi     FIELD(CCC_DEVICE_STATUS, DATA_NOT_READY,    11, 1)
19059b8d0c9SJoe Komlodi     FIELD(CCC_DEVICE_STATUS, BUFFER_NOT_AVAIL,  12, 1)
191c52aaabdSJoe Komlodi REG32(DEVICE_ADDR_TABLE_POINTER,    0x5c)
192c52aaabdSJoe Komlodi     FIELD(DEVICE_ADDR_TABLE_POINTER, DEPTH, 16, 16)
193c52aaabdSJoe Komlodi     FIELD(DEVICE_ADDR_TABLE_POINTER, ADDR,  0,  16)
194c52aaabdSJoe Komlodi REG32(DEV_CHAR_TABLE_POINTER,       0x60)
19559b8d0c9SJoe Komlodi     FIELD(DEV_CHAR_TABLE_POINTER, P_DEV_CHAR_TABLE_START_ADDR,  0, 12)
19659b8d0c9SJoe Komlodi     FIELD(DEV_CHAR_TABLE_POINTER, DEV_CHAR_TABLE_DEPTH,         12, 7)
19759b8d0c9SJoe Komlodi     FIELD(DEV_CHAR_TABLE_POINTER, PRESENT_DEV_CHAR_TABLE_INDEX, 19, 3)
198c52aaabdSJoe Komlodi REG32(VENDOR_SPECIFIC_REG_POINTER,  0x6c)
19959b8d0c9SJoe Komlodi     FIELD(VENDOR_SPECIFIC_REG_POINTER, P_VENDOR_REG_START_ADDR, 0, 16)
200c52aaabdSJoe Komlodi REG32(SLV_MIPI_PID_VALUE,           0x70)
201c52aaabdSJoe Komlodi REG32(SLV_PID_VALUE,                0x74)
20259b8d0c9SJoe Komlodi     FIELD(SLV_PID_VALUE, SLV_PID_DCR, 0, 12)
20359b8d0c9SJoe Komlodi     FIELD(SLV_PID_VALUE, SLV_INST_ID, 12, 4)
20459b8d0c9SJoe Komlodi     FIELD(SLV_PID_VALUE, SLV_PART_ID, 16, 16)
205c52aaabdSJoe Komlodi REG32(SLV_CHAR_CTRL,                0x78)
20659b8d0c9SJoe Komlodi     FIELD(SLV_CHAR_CTRL, BCR,     0, 8)
20759b8d0c9SJoe Komlodi     FIELD(SLV_CHAR_CTRL, DCR,     8, 8)
20859b8d0c9SJoe Komlodi     FIELD(SLV_CHAR_CTRL, HDR_CAP, 16, 8)
209c52aaabdSJoe Komlodi REG32(SLV_MAX_LEN,                  0x7c)
21059b8d0c9SJoe Komlodi     FIELD(SLV_MAX_LEN, MWL, 0, 16)
21159b8d0c9SJoe Komlodi     FIELD(SLV_MAX_LEN, MRL, 16, 16)
212c52aaabdSJoe Komlodi REG32(MAX_READ_TURNAROUND,          0x80)
213c52aaabdSJoe Komlodi REG32(MAX_DATA_SPEED,               0x84)
214c52aaabdSJoe Komlodi REG32(SLV_DEBUG_STATUS,             0x88)
215c52aaabdSJoe Komlodi REG32(SLV_INTR_REQ,                 0x8c)
21659b8d0c9SJoe Komlodi     FIELD(SLV_INTR_REQ, SIR,          0, 1)
21759b8d0c9SJoe Komlodi     FIELD(SLV_INTR_REQ, SIR_CTRL,     1, 2)
21859b8d0c9SJoe Komlodi     FIELD(SLV_INTR_REQ, MIR,          3, 1)
21959b8d0c9SJoe Komlodi     FIELD(SLV_INTR_REQ, TS,           4, 1)
22059b8d0c9SJoe Komlodi     FIELD(SLV_INTR_REQ, IBI_STS,      8, 2)
22159b8d0c9SJoe Komlodi     FIELD(SLV_INTR_REQ, MDB,          8, 8)
22259b8d0c9SJoe Komlodi     FIELD(SLV_INTR_REQ, SIR_DATA_LEN, 16, 8)
22359b8d0c9SJoe Komlodi REG32(SLV_TSX_SYMBL_TIMING,         0x90)
22459b8d0c9SJoe Komlodi     FIELD(SLV_TSX_SYMBL_TIMING, SLV_TSX_SYMBL_CNT, 0, 6)
22559b8d0c9SJoe Komlodi REG32(SLV_SIR_DATA,                 0x94)
22659b8d0c9SJoe Komlodi     FIELD(SLV_SIR_DATA, SIR_DATA_BYTE0, 0, 8)
22759b8d0c9SJoe Komlodi     FIELD(SLV_SIR_DATA, SIR_DATA_BYTE1, 8, 8)
22859b8d0c9SJoe Komlodi     FIELD(SLV_SIR_DATA, SIR_DATA_BYTE2, 16, 8)
22959b8d0c9SJoe Komlodi     FIELD(SLV_SIR_DATA, SIR_DATA_BYTE3, 24, 8)
23059b8d0c9SJoe Komlodi REG32(SLV_IBI_RESP,                 0x98)
23159b8d0c9SJoe Komlodi     FIELD(SLV_IBI_RESP, IBI_STS,           0, 2)
23259b8d0c9SJoe Komlodi     FIELD(SLV_IBI_RESP, SIR_RESP_DATA_LEN, 8, 16)
233c52aaabdSJoe Komlodi REG32(DEVICE_CTRL_EXTENDED,         0xb0)
23459b8d0c9SJoe Komlodi     FIELD(DEVICE_CTRL_EXTENDED, MODE, 0, 2)
23559b8d0c9SJoe Komlodi     FIELD(DEVICE_CTRL_EXTENDED, REQMST_ACK_CTRL, 3, 1)
236c52aaabdSJoe Komlodi REG32(SCL_I3C_OD_TIMING,            0xb4)
23759b8d0c9SJoe Komlodi     FIELD(SCL_I3C_OD_TIMING, I3C_OD_LCNT, 0, 8)
23859b8d0c9SJoe Komlodi     FIELD(SCL_I3C_OD_TIMING, I3C_OD_HCNT, 16, 8)
239c52aaabdSJoe Komlodi REG32(SCL_I3C_PP_TIMING,            0xb8)
24059b8d0c9SJoe Komlodi     FIELD(SCL_I3C_PP_TIMING, I3C_PP_LCNT, 0, 8)
24159b8d0c9SJoe Komlodi     FIELD(SCL_I3C_PP_TIMING, I3C_PP_HCNT, 16, 8)
242c52aaabdSJoe Komlodi REG32(SCL_I2C_FM_TIMING,            0xbc)
243c52aaabdSJoe Komlodi REG32(SCL_I2C_FMP_TIMING,           0xc0)
24459b8d0c9SJoe Komlodi     FIELD(SCL_I2C_FMP_TIMING, I2C_FMP_LCNT, 0, 16)
24559b8d0c9SJoe Komlodi     FIELD(SCL_I2C_FMP_TIMING, I2C_FMP_HCNT, 16, 8)
246c52aaabdSJoe Komlodi REG32(SCL_EXT_LCNT_TIMING,          0xc8)
247c52aaabdSJoe Komlodi REG32(SCL_EXT_TERMN_LCNT_TIMING,    0xcc)
248c52aaabdSJoe Komlodi REG32(BUS_FREE_TIMING,              0xd4)
249c52aaabdSJoe Komlodi REG32(BUS_IDLE_TIMING,              0xd8)
25059b8d0c9SJoe Komlodi     FIELD(BUS_IDLE_TIMING, BUS_IDLE_TIME, 0, 20)
251c52aaabdSJoe Komlodi REG32(I3C_VER_ID,                   0xe0)
252c52aaabdSJoe Komlodi REG32(I3C_VER_TYPE,                 0xe4)
253c52aaabdSJoe Komlodi REG32(EXTENDED_CAPABILITY,          0xe8)
25459b8d0c9SJoe Komlodi     FIELD(EXTENDED_CAPABILITY, APP_IF_MODE,       0, 2)
25559b8d0c9SJoe Komlodi     FIELD(EXTENDED_CAPABILITY, APP_IF_DATA_WIDTH, 2, 2)
25659b8d0c9SJoe Komlodi     FIELD(EXTENDED_CAPABILITY, OPERATION_MODE,    4, 2)
25759b8d0c9SJoe Komlodi     FIELD(EXTENDED_CAPABILITY, CLK_PERIOD,        8, 6)
258c52aaabdSJoe Komlodi REG32(SLAVE_CONFIG,                 0xec)
25959b8d0c9SJoe Komlodi     FIELD(SLAVE_CONFIG, DMA_EN,     0, 1)
26059b8d0c9SJoe Komlodi     FIELD(SLAVE_CONFIG, HJ_CAP,     0, 1)
26159b8d0c9SJoe Komlodi     FIELD(SLAVE_CONFIG, CLK_PERIOD, 2, 14)
26259b8d0c9SJoe Komlodi /* Device characteristic table fields */
26359b8d0c9SJoe Komlodi REG32(DEVICE_CHARACTERISTIC_TABLE_LOC1, 0x200)
26459b8d0c9SJoe Komlodi REG32(DEVICE_CHARACTERISTIC_TABLE_LOC_SECONDARY, 0x200)
26559b8d0c9SJoe Komlodi     FIELD(DEVICE_CHARACTERISTIC_TABLE_LOC_SECONDARY, DYNAMIC_ADDR, 0, 8)
26659b8d0c9SJoe Komlodi     FIELD(DEVICE_CHARACTERISTIC_TABLE_LOC_SECONDARY, DCR, 8, 8)
26759b8d0c9SJoe Komlodi     FIELD(DEVICE_CHARACTERISTIC_TABLE_LOC_SECONDARY, BCR, 16, 8)
26859b8d0c9SJoe Komlodi     FIELD(DEVICE_CHARACTERISTIC_TABLE_LOC_SECONDARY, STATIC_ADDR, 24, 8)
26959b8d0c9SJoe Komlodi REG32(DEVICE_CHARACTERISTIC_TABLE_LOC2, 0x204)
27059b8d0c9SJoe Komlodi     FIELD(DEVICE_CHARACTERISTIC_TABLE_LOC2, MSB_PID, 0, 16)
27159b8d0c9SJoe Komlodi REG32(DEVICE_CHARACTERISTIC_TABLE_LOC3, 0x208)
27259b8d0c9SJoe Komlodi     FIELD(DEVICE_CHARACTERISTIC_TABLE_LOC3, DCR, 0, 8)
27359b8d0c9SJoe Komlodi     FIELD(DEVICE_CHARACTERISTIC_TABLE_LOC3, BCR, 8, 8)
27459b8d0c9SJoe Komlodi REG32(DEVICE_CHARACTERISTIC_TABLE_LOC4, 0x20c)
27559b8d0c9SJoe Komlodi     FIELD(DEVICE_CHARACTERISTIC_TABLE_LOC4, DEV_DYNAMIC_ADDR, 0, 8)
27659b8d0c9SJoe Komlodi /* Dev addr table fields */
27759b8d0c9SJoe Komlodi REG32(DEVICE_ADDR_TABLE_LOC1, 0x280)
27859b8d0c9SJoe Komlodi     FIELD(DEVICE_ADDR_TABLE_LOC1, DEV_STATIC_ADDR, 0, 7)
27959b8d0c9SJoe Komlodi     FIELD(DEVICE_ADDR_TABLE_LOC1, IBI_PEC_EN, 11, 1)
28059b8d0c9SJoe Komlodi     FIELD(DEVICE_ADDR_TABLE_LOC1, IBI_WITH_DATA, 12, 1)
28159b8d0c9SJoe Komlodi     FIELD(DEVICE_ADDR_TABLE_LOC1, SIR_REJECT, 13, 1)
28259b8d0c9SJoe Komlodi     FIELD(DEVICE_ADDR_TABLE_LOC1, MR_REJECT, 14, 1)
28359b8d0c9SJoe Komlodi     FIELD(DEVICE_ADDR_TABLE_LOC1, DEV_DYNAMIC_ADDR, 16, 8)
28459b8d0c9SJoe Komlodi     FIELD(DEVICE_ADDR_TABLE_LOC1, IBI_ADDR_MASK, 24, 2)
28559b8d0c9SJoe Komlodi     FIELD(DEVICE_ADDR_TABLE_LOC1, DEV_NACK_RETRY_CNT, 29, 2)
28659b8d0c9SJoe Komlodi     FIELD(DEVICE_ADDR_TABLE_LOC1, LEGACY_I2C_DEVICE, 31, 1)
287c52aaabdSJoe Komlodi 
288c52aaabdSJoe Komlodi static const uint32_t dw_i3c_resets[DW_I3C_NR_REGS] = {
28973a38a9bSJoe Komlodi     /* Target mode is not supported, don't advertise it for now. */
29073a38a9bSJoe Komlodi     [R_HW_CAPABILITY]               = 0x000e00b9,
291c52aaabdSJoe Komlodi     [R_QUEUE_THLD_CTRL]             = 0x01000101,
29273a38a9bSJoe Komlodi     [R_DATA_BUFFER_THLD_CTRL]       = 0x01010100,
29373a38a9bSJoe Komlodi     [R_SLV_EVENT_CTRL]              = 0x0000000b,
29473a38a9bSJoe Komlodi     [R_QUEUE_STATUS_LEVEL]          = 0x00000002,
29573a38a9bSJoe Komlodi     [R_DATA_BUFFER_STATUS_LEVEL]    = 0x00000010,
29673a38a9bSJoe Komlodi     [R_PRESENT_STATE]               = 0x00000003,
297c52aaabdSJoe Komlodi     [R_I3C_VER_ID]                  = 0x3130302a,
298c52aaabdSJoe Komlodi     [R_I3C_VER_TYPE]                = 0x6c633033,
299c52aaabdSJoe Komlodi     [R_DEVICE_ADDR_TABLE_POINTER]   = 0x00080280,
300c52aaabdSJoe Komlodi     [R_DEV_CHAR_TABLE_POINTER]      = 0x00020200,
30173a38a9bSJoe Komlodi     [R_SLV_CHAR_CTRL]               = 0x00010000,
302c52aaabdSJoe Komlodi     [A_VENDOR_SPECIFIC_REG_POINTER] = 0x000000b0,
303c52aaabdSJoe Komlodi     [R_SLV_MAX_LEN]                 = 0x00ff00ff,
30473a38a9bSJoe Komlodi     [R_SLV_TSX_SYMBL_TIMING]        = 0x0000003f,
30573a38a9bSJoe Komlodi     [R_SCL_I3C_OD_TIMING]           = 0x000a0010,
30673a38a9bSJoe Komlodi     [R_SCL_I3C_PP_TIMING]           = 0x000a000a,
30773a38a9bSJoe Komlodi     [R_SCL_I2C_FM_TIMING]           = 0x00100010,
30873a38a9bSJoe Komlodi     [R_SCL_I2C_FMP_TIMING]          = 0x00100010,
30973a38a9bSJoe Komlodi     [R_SCL_EXT_LCNT_TIMING]         = 0x20202020,
31073a38a9bSJoe Komlodi     [R_SCL_EXT_TERMN_LCNT_TIMING]   = 0x00300000,
31173a38a9bSJoe Komlodi     [R_BUS_FREE_TIMING]             = 0x00200020,
31273a38a9bSJoe Komlodi     [R_BUS_IDLE_TIMING]             = 0x00000020,
31373a38a9bSJoe Komlodi     [R_EXTENDED_CAPABILITY]         = 0x00000239,
31473a38a9bSJoe Komlodi     [R_SLAVE_CONFIG]                = 0x00000023,
315c52aaabdSJoe Komlodi };
316c52aaabdSJoe Komlodi 
317ef77491fSJoe Komlodi static const uint32_t dw_i3c_ro[DW_I3C_NR_REGS] = {
318ef77491fSJoe Komlodi     [R_DEVICE_CTRL]                 = 0x04fffe00,
319ef77491fSJoe Komlodi     [R_DEVICE_ADDR]                 = 0x7f807f80,
320ef77491fSJoe Komlodi     [R_HW_CAPABILITY]               = 0xffffffff,
321ef77491fSJoe Komlodi     [R_IBI_QUEUE_STATUS]            = 0xffffffff,
322ef77491fSJoe Komlodi     [R_DATA_BUFFER_THLD_CTRL]       = 0xf8f8f8f8,
323ef77491fSJoe Komlodi     [R_IBI_QUEUE_CTRL]              = 0xfffffff0,
324ef77491fSJoe Komlodi     [R_RESET_CTRL]                  = 0xffffffc0,
325ef77491fSJoe Komlodi     [R_SLV_EVENT_CTRL]              = 0xffffff3f,
326ef77491fSJoe Komlodi     [R_INTR_STATUS]                 = 0xffff809f,
327ef77491fSJoe Komlodi     [R_INTR_STATUS_EN]              = 0xffff8080,
328ef77491fSJoe Komlodi     [R_INTR_SIGNAL_EN]              = 0xffff8080,
329ef77491fSJoe Komlodi     [R_INTR_FORCE]                  = 0xffff8000,
330ef77491fSJoe Komlodi     [R_QUEUE_STATUS_LEVEL]          = 0xffffffff,
331ef77491fSJoe Komlodi     [R_DATA_BUFFER_STATUS_LEVEL]    = 0xffffffff,
332ef77491fSJoe Komlodi     [R_PRESENT_STATE]               = 0xffffffff,
333ef77491fSJoe Komlodi     [R_CCC_DEVICE_STATUS]           = 0xffffffff,
334ef77491fSJoe Komlodi     [R_I3C_VER_ID]                  = 0xffffffff,
335ef77491fSJoe Komlodi     [R_I3C_VER_TYPE]                = 0xffffffff,
336ef77491fSJoe Komlodi     [R_DEVICE_ADDR_TABLE_POINTER]   = 0xffffffff,
337ef77491fSJoe Komlodi     [R_DEV_CHAR_TABLE_POINTER]      = 0xffcbffff,
338ef77491fSJoe Komlodi     [R_SLV_PID_VALUE]               = 0xffff0fff,
339ef77491fSJoe Komlodi     [R_SLV_CHAR_CTRL]               = 0xffffffff,
340ef77491fSJoe Komlodi     [A_VENDOR_SPECIFIC_REG_POINTER] = 0xffffffff,
341ef77491fSJoe Komlodi     [R_SLV_MAX_LEN]                 = 0xffffffff,
342ef77491fSJoe Komlodi     [R_MAX_READ_TURNAROUND]         = 0xffffffff,
343ef77491fSJoe Komlodi     [R_MAX_DATA_SPEED]              = 0xffffffff,
344ef77491fSJoe Komlodi     [R_SLV_INTR_REQ]                = 0xfffffff0,
345ef77491fSJoe Komlodi     [R_SLV_TSX_SYMBL_TIMING]        = 0xffffffc0,
346ef77491fSJoe Komlodi     [R_DEVICE_CTRL_EXTENDED]        = 0xfffffff8,
347ef77491fSJoe Komlodi     [R_SCL_I3C_OD_TIMING]           = 0xff00ff00,
348ef77491fSJoe Komlodi     [R_SCL_I3C_PP_TIMING]           = 0xff00ff00,
349ef77491fSJoe Komlodi     [R_SCL_I2C_FMP_TIMING]          = 0xff000000,
350ef77491fSJoe Komlodi     [R_SCL_EXT_TERMN_LCNT_TIMING]   = 0x0000fff0,
351ef77491fSJoe Komlodi     [R_BUS_IDLE_TIMING]             = 0xfff00000,
352ef77491fSJoe Komlodi     [R_EXTENDED_CAPABILITY]         = 0xffffffff,
353ef77491fSJoe Komlodi     [R_SLAVE_CONFIG]                = 0xffffffff,
354ef77491fSJoe Komlodi };
355ef77491fSJoe Komlodi 
356*9c0476a2SJoe Komlodi static inline bool dw_i3c_has_hdr_ts(DWI3C *s)
357*9c0476a2SJoe Komlodi {
358*9c0476a2SJoe Komlodi     return ARRAY_FIELD_EX32(s->regs, HW_CAPABILITY, HDR_TS);
359*9c0476a2SJoe Komlodi }
360*9c0476a2SJoe Komlodi 
361*9c0476a2SJoe Komlodi static inline bool dw_i3c_has_hdr_ddr(DWI3C *s)
362*9c0476a2SJoe Komlodi {
363*9c0476a2SJoe Komlodi     return ARRAY_FIELD_EX32(s->regs, HW_CAPABILITY, HDR_DDR);
364*9c0476a2SJoe Komlodi }
365*9c0476a2SJoe Komlodi 
366*9c0476a2SJoe Komlodi static inline bool dw_i3c_can_transmit(DWI3C *s)
367*9c0476a2SJoe Komlodi {
368*9c0476a2SJoe Komlodi     /*
369*9c0476a2SJoe Komlodi      * We can only transmit if we're enabled and the resume bit is cleared.
370*9c0476a2SJoe Komlodi      * The resume bit is set on a transaction error, and software must clear it.
371*9c0476a2SJoe Komlodi      */
372*9c0476a2SJoe Komlodi     return ARRAY_FIELD_EX32(s->regs, DEVICE_CTRL, I3C_EN) &&
373*9c0476a2SJoe Komlodi            !ARRAY_FIELD_EX32(s->regs, DEVICE_CTRL, I3C_RESUME);
374*9c0476a2SJoe Komlodi }
375*9c0476a2SJoe Komlodi 
376*9c0476a2SJoe Komlodi static inline uint8_t dw_i3c_fifo_threshold_from_reg(uint8_t regval)
377*9c0476a2SJoe Komlodi {
378*9c0476a2SJoe Komlodi     return regval = regval ? (2 << regval) : 1;
379*9c0476a2SJoe Komlodi }
380*9c0476a2SJoe Komlodi 
381a825bbb7SJoe Komlodi static void dw_i3c_update_irq(DWI3C *s)
382a825bbb7SJoe Komlodi {
383a825bbb7SJoe Komlodi     bool level = !!(s->regs[R_INTR_SIGNAL_EN] & s->regs[R_INTR_STATUS]);
384a825bbb7SJoe Komlodi     qemu_set_irq(s->irq, level);
385a825bbb7SJoe Komlodi }
386a825bbb7SJoe Komlodi 
387*9c0476a2SJoe Komlodi static void dw_i3c_end_transfer(DWI3C *s, bool is_i2c)
388*9c0476a2SJoe Komlodi {
389*9c0476a2SJoe Komlodi     if (is_i2c) {
390*9c0476a2SJoe Komlodi         legacy_i2c_end_transfer(s->bus);
391*9c0476a2SJoe Komlodi     } else {
392*9c0476a2SJoe Komlodi         i3c_end_transfer(s->bus);
393*9c0476a2SJoe Komlodi     }
394*9c0476a2SJoe Komlodi }
395*9c0476a2SJoe Komlodi 
396*9c0476a2SJoe Komlodi static int dw_i3c_send_start(DWI3C *s, uint8_t addr, bool is_recv, bool is_i2c)
397*9c0476a2SJoe Komlodi {
398*9c0476a2SJoe Komlodi     int ret;
399*9c0476a2SJoe Komlodi 
400*9c0476a2SJoe Komlodi     if (is_i2c) {
401*9c0476a2SJoe Komlodi         ret = legacy_i2c_start_transfer(s->bus, addr, is_recv);
402*9c0476a2SJoe Komlodi     } else {
403*9c0476a2SJoe Komlodi         ret = i3c_start_transfer(s->bus, addr, is_recv);
404*9c0476a2SJoe Komlodi     }
405*9c0476a2SJoe Komlodi     if (ret) {
406*9c0476a2SJoe Komlodi         g_autofree char *path = object_get_canonical_path(OBJECT(s));
407*9c0476a2SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: NACKed on TX with addr 0x%.2x\n",
408*9c0476a2SJoe Komlodi                       path, addr);
409*9c0476a2SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS,
410*9c0476a2SJoe Komlodi                          DW_I3C_TRANSFER_STATE_HALT);
411*9c0476a2SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_STATUS,
412*9c0476a2SJoe Komlodi                          DW_I3C_TRANSFER_STATUS_HALT);
413*9c0476a2SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, INTR_STATUS, TRANSFER_ERR, 1);
414*9c0476a2SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, DEVICE_CTRL, I3C_RESUME, 1);
415*9c0476a2SJoe Komlodi     }
416*9c0476a2SJoe Komlodi 
417*9c0476a2SJoe Komlodi     return ret;
418*9c0476a2SJoe Komlodi }
419*9c0476a2SJoe Komlodi 
420*9c0476a2SJoe Komlodi static int dw_i3c_send(DWI3C *s, const uint8_t *data, uint32_t num_to_send,
421*9c0476a2SJoe Komlodi                        uint32_t *num_sent, bool is_i2c)
422*9c0476a2SJoe Komlodi {
423*9c0476a2SJoe Komlodi     int ret;
424*9c0476a2SJoe Komlodi     uint32_t i;
425*9c0476a2SJoe Komlodi 
426*9c0476a2SJoe Komlodi     *num_sent = 0;
427*9c0476a2SJoe Komlodi     if (is_i2c) {
428*9c0476a2SJoe Komlodi         /* Legacy I2C must be byte-by-byte. */
429*9c0476a2SJoe Komlodi         for (i = 0; i < num_to_send; i++) {
430*9c0476a2SJoe Komlodi             ret = legacy_i2c_send(s->bus, data[i]);
431*9c0476a2SJoe Komlodi             if (ret) {
432*9c0476a2SJoe Komlodi                 break;
433*9c0476a2SJoe Komlodi             }
434*9c0476a2SJoe Komlodi             (*num_sent)++;
435*9c0476a2SJoe Komlodi         }
436*9c0476a2SJoe Komlodi     } else {
437*9c0476a2SJoe Komlodi         ret = i3c_send(s->bus, data, num_to_send, num_sent);
438*9c0476a2SJoe Komlodi     }
439*9c0476a2SJoe Komlodi     if (ret) {
440*9c0476a2SJoe Komlodi         g_autofree char *path = object_get_canonical_path(OBJECT(s));
441*9c0476a2SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: NACKed sending byte 0x%.2x\n",
442*9c0476a2SJoe Komlodi                       path, data[*num_sent]);
443*9c0476a2SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS,
444*9c0476a2SJoe Komlodi                          DW_I3C_TRANSFER_STATE_HALT);
445*9c0476a2SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_STATUS,
446*9c0476a2SJoe Komlodi                          DW_I3C_TRANSFER_STATUS_HALT);
447*9c0476a2SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, INTR_STATUS, TRANSFER_ERR, 1);
448*9c0476a2SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, DEVICE_CTRL, I3C_RESUME, 1);
449*9c0476a2SJoe Komlodi     }
450*9c0476a2SJoe Komlodi 
451*9c0476a2SJoe Komlodi     trace_dw_i3c_send(s->cfg.id, *num_sent);
452*9c0476a2SJoe Komlodi 
453*9c0476a2SJoe Komlodi     return ret;
454*9c0476a2SJoe Komlodi }
455*9c0476a2SJoe Komlodi 
456*9c0476a2SJoe Komlodi static int dw_i3c_send_byte(DWI3C *s, uint8_t byte, bool is_i2c)
457*9c0476a2SJoe Komlodi {
458*9c0476a2SJoe Komlodi     /*
459*9c0476a2SJoe Komlodi      * Ignored, the caller will know if we sent 0 or 1 bytes depending on if
460*9c0476a2SJoe Komlodi      * we were ACKed/NACKed.
461*9c0476a2SJoe Komlodi      */
462*9c0476a2SJoe Komlodi     uint32_t num_sent;
463*9c0476a2SJoe Komlodi     return dw_i3c_send(s, &byte, 1, &num_sent, is_i2c);
464*9c0476a2SJoe Komlodi }
465*9c0476a2SJoe Komlodi 
466*9c0476a2SJoe Komlodi static int dw_i3c_recv_data(DWI3C *s, bool is_i2c, uint8_t *data,
467*9c0476a2SJoe Komlodi                             uint16_t num_to_read, uint32_t *num_read)
468*9c0476a2SJoe Komlodi {
469*9c0476a2SJoe Komlodi     int ret;
470*9c0476a2SJoe Komlodi 
471*9c0476a2SJoe Komlodi     if (is_i2c) {
472*9c0476a2SJoe Komlodi         for (uint16_t i = 0; i < num_to_read; i++) {
473*9c0476a2SJoe Komlodi             data[i] = legacy_i2c_recv(s->bus);
474*9c0476a2SJoe Komlodi         }
475*9c0476a2SJoe Komlodi         /* I2C devices can neither NACK a read, nor end transfers early. */
476*9c0476a2SJoe Komlodi         *num_read = num_to_read;
477*9c0476a2SJoe Komlodi         trace_dw_i3c_recv_data(s->cfg.id, *num_read);
478*9c0476a2SJoe Komlodi         return 0;
479*9c0476a2SJoe Komlodi     }
480*9c0476a2SJoe Komlodi     /* I3C devices can NACK if the controller sends an unsupported CCC. */
481*9c0476a2SJoe Komlodi     ret = i3c_recv(s->bus, data, num_to_read, num_read);
482*9c0476a2SJoe Komlodi     if (ret) {
483*9c0476a2SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: NACKed receiving byte\n",
484*9c0476a2SJoe Komlodi                       object_get_canonical_path(OBJECT(s)));
485*9c0476a2SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS,
486*9c0476a2SJoe Komlodi                          DW_I3C_TRANSFER_STATE_HALT);
487*9c0476a2SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_STATUS,
488*9c0476a2SJoe Komlodi                          DW_I3C_TRANSFER_STATUS_HALT);
489*9c0476a2SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, INTR_STATUS, TRANSFER_ERR, 1);
490*9c0476a2SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, DEVICE_CTRL, I3C_RESUME, 1);
491*9c0476a2SJoe Komlodi     }
492*9c0476a2SJoe Komlodi 
493*9c0476a2SJoe Komlodi     trace_dw_i3c_recv_data(s->cfg.id, *num_read);
494*9c0476a2SJoe Komlodi 
495*9c0476a2SJoe Komlodi     return ret;
496*9c0476a2SJoe Komlodi }
497*9c0476a2SJoe Komlodi 
498*9c0476a2SJoe Komlodi static inline bool dw_i3c_target_is_i2c(DWI3C *s, uint16_t offset)
499*9c0476a2SJoe Komlodi {
500*9c0476a2SJoe Komlodi     /* / sizeof(uint32_t) because we're indexing into our 32-bit reg array. */
501*9c0476a2SJoe Komlodi     uint16_t dev_index = (ARRAY_FIELD_EX32(s->regs, DEVICE_ADDR_TABLE_POINTER,
502*9c0476a2SJoe Komlodi                                           ADDR) / sizeof(uint32_t)) + offset;
503*9c0476a2SJoe Komlodi     return FIELD_EX32(s->regs[dev_index], DEVICE_ADDR_TABLE_LOC1,
504*9c0476a2SJoe Komlodi                    LEGACY_I2C_DEVICE);
505*9c0476a2SJoe Komlodi }
506*9c0476a2SJoe Komlodi 
507*9c0476a2SJoe Komlodi static uint8_t dw_i3c_target_addr(DWI3C *s, uint16_t offset)
508*9c0476a2SJoe Komlodi {
509*9c0476a2SJoe Komlodi     if (offset > s->cfg.num_addressable_devices) {
510*9c0476a2SJoe Komlodi         g_autofree char *path = object_get_canonical_path(OBJECT(s));
511*9c0476a2SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: Device addr table offset %d out of "
512*9c0476a2SJoe Komlodi                       "bounds\n", path, offset);
513*9c0476a2SJoe Komlodi         /* If we're out of bounds, return an address of 0. */
514*9c0476a2SJoe Komlodi         return 0;
515*9c0476a2SJoe Komlodi     }
516*9c0476a2SJoe Komlodi 
517*9c0476a2SJoe Komlodi     /* / sizeof(uint32_t) because we're indexing into our 32-bit reg array. */
518*9c0476a2SJoe Komlodi     uint16_t dev_index = (ARRAY_FIELD_EX32(s->regs, DEVICE_ADDR_TABLE_POINTER,
519*9c0476a2SJoe Komlodi                                           ADDR) / sizeof(uint32_t)) + offset;
520*9c0476a2SJoe Komlodi     /* I2C devices use a static address. */
521*9c0476a2SJoe Komlodi     if (dw_i3c_target_is_i2c(s, offset)) {
522*9c0476a2SJoe Komlodi         return FIELD_EX32(s->regs[dev_index], DEVICE_ADDR_TABLE_LOC1,
523*9c0476a2SJoe Komlodi                           DEV_STATIC_ADDR);
524*9c0476a2SJoe Komlodi     }
525*9c0476a2SJoe Komlodi     return FIELD_EX32(s->regs[dev_index], DEVICE_ADDR_TABLE_LOC1,
526*9c0476a2SJoe Komlodi                       DEV_DYNAMIC_ADDR);
527*9c0476a2SJoe Komlodi }
528*9c0476a2SJoe Komlodi 
529a825bbb7SJoe Komlodi static uint32_t dw_i3c_intr_status_r(DWI3C *s)
530a825bbb7SJoe Komlodi {
531a825bbb7SJoe Komlodi     /* Only return the status whose corresponding EN bits are set. */
532a825bbb7SJoe Komlodi     return s->regs[R_INTR_STATUS] & s->regs[R_INTR_STATUS_EN];
533a825bbb7SJoe Komlodi }
534a825bbb7SJoe Komlodi 
535a825bbb7SJoe Komlodi static void dw_i3c_intr_status_w(DWI3C *s, uint32_t val)
536a825bbb7SJoe Komlodi {
537a825bbb7SJoe Komlodi     /* INTR_STATUS[13:5] is w1c, other bits are RO. */
538a825bbb7SJoe Komlodi     val &= 0x3fe0;
539a825bbb7SJoe Komlodi     s->regs[R_INTR_STATUS] &= ~val;
540a825bbb7SJoe Komlodi 
541a825bbb7SJoe Komlodi     dw_i3c_update_irq(s);
542a825bbb7SJoe Komlodi }
543a825bbb7SJoe Komlodi 
544a825bbb7SJoe Komlodi static void dw_i3c_intr_status_en_w(DWI3C *s, uint32_t val)
545a825bbb7SJoe Komlodi {
546a825bbb7SJoe Komlodi     s->regs[R_INTR_STATUS_EN] = val;
547a825bbb7SJoe Komlodi     dw_i3c_update_irq(s);
548a825bbb7SJoe Komlodi }
549a825bbb7SJoe Komlodi 
550a825bbb7SJoe Komlodi static void dw_i3c_intr_signal_en_w(DWI3C *s, uint32_t val)
551a825bbb7SJoe Komlodi {
552a825bbb7SJoe Komlodi     s->regs[R_INTR_SIGNAL_EN] = val;
553a825bbb7SJoe Komlodi     dw_i3c_update_irq(s);
554a825bbb7SJoe Komlodi }
555a825bbb7SJoe Komlodi 
556a825bbb7SJoe Komlodi static void dw_i3c_intr_force_w(DWI3C *s, uint32_t val)
557a825bbb7SJoe Komlodi {
558a825bbb7SJoe Komlodi     /* INTR_FORCE is WO, just set the corresponding INTR_STATUS bits. */
559a825bbb7SJoe Komlodi     s->regs[R_INTR_STATUS] = val;
560a825bbb7SJoe Komlodi     dw_i3c_update_irq(s);
561a825bbb7SJoe Komlodi }
562a825bbb7SJoe Komlodi 
563*9c0476a2SJoe Komlodi static uint32_t dw_i3c_pop_rx(DWI3C *s)
564*9c0476a2SJoe Komlodi {
565*9c0476a2SJoe Komlodi     if (fifo32_is_empty(&s->rx_queue)) {
566*9c0476a2SJoe Komlodi         g_autofree char *path = object_get_canonical_path(OBJECT(s));
567*9c0476a2SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: Tried to read RX FIFO when empty\n",
568*9c0476a2SJoe Komlodi                       path);
569*9c0476a2SJoe Komlodi         return 0;
570*9c0476a2SJoe Komlodi     }
571*9c0476a2SJoe Komlodi 
572*9c0476a2SJoe Komlodi     uint32_t val = fifo32_pop(&s->rx_queue);
573*9c0476a2SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, DATA_BUFFER_STATUS_LEVEL, RX_BUF_BLR,
574*9c0476a2SJoe Komlodi                      fifo32_num_used(&s->rx_queue));
575*9c0476a2SJoe Komlodi 
576*9c0476a2SJoe Komlodi     /* Threshold is 2^RX_BUF_THLD. */
577*9c0476a2SJoe Komlodi     uint8_t threshold = ARRAY_FIELD_EX32(s->regs, DATA_BUFFER_THLD_CTRL,
578*9c0476a2SJoe Komlodi                                          RX_BUF_THLD);
579*9c0476a2SJoe Komlodi     threshold = dw_i3c_fifo_threshold_from_reg(threshold);
580*9c0476a2SJoe Komlodi     if (fifo32_num_used(&s->rx_queue) < threshold) {
581*9c0476a2SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, INTR_STATUS, RX_THLD, 0);
582*9c0476a2SJoe Komlodi         dw_i3c_update_irq(s);
583*9c0476a2SJoe Komlodi     }
584*9c0476a2SJoe Komlodi 
585*9c0476a2SJoe Komlodi     trace_dw_i3c_pop_rx(s->cfg.id, val);
586*9c0476a2SJoe Komlodi     return val;
587*9c0476a2SJoe Komlodi }
588*9c0476a2SJoe Komlodi 
589*9c0476a2SJoe Komlodi static uint32_t dw_i3c_resp_queue_port_r(DWI3C *s)
590*9c0476a2SJoe Komlodi {
591*9c0476a2SJoe Komlodi     if (fifo32_is_empty(&s->resp_queue)) {
592*9c0476a2SJoe Komlodi         g_autofree char *path = object_get_canonical_path(OBJECT(s));
593*9c0476a2SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: Tried to read response FIFO when "
594*9c0476a2SJoe Komlodi                       "empty\n", path);
595*9c0476a2SJoe Komlodi         return 0;
596*9c0476a2SJoe Komlodi     }
597*9c0476a2SJoe Komlodi 
598*9c0476a2SJoe Komlodi     uint32_t val = fifo32_pop(&s->resp_queue);
599*9c0476a2SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, QUEUE_STATUS_LEVEL, RESP_BUF_BLR,
600*9c0476a2SJoe Komlodi                      fifo32_num_used(&s->resp_queue));
601*9c0476a2SJoe Komlodi 
602*9c0476a2SJoe Komlodi     /* Threshold is the register value + 1. */
603*9c0476a2SJoe Komlodi     uint8_t threshold = ARRAY_FIELD_EX32(s->regs, QUEUE_THLD_CTRL,
604*9c0476a2SJoe Komlodi                                          RESP_BUF_THLD) + 1;
605*9c0476a2SJoe Komlodi     if (fifo32_num_used(&s->resp_queue) < threshold) {
606*9c0476a2SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, INTR_STATUS, RESP_RDY, 0);
607*9c0476a2SJoe Komlodi         dw_i3c_update_irq(s);
608*9c0476a2SJoe Komlodi     }
609*9c0476a2SJoe Komlodi 
610*9c0476a2SJoe Komlodi     return val;
611*9c0476a2SJoe Komlodi }
612*9c0476a2SJoe Komlodi 
613c52aaabdSJoe Komlodi static uint64_t dw_i3c_read(void *opaque, hwaddr offset, unsigned size)
614c52aaabdSJoe Komlodi {
615c52aaabdSJoe Komlodi     DWI3C *s = DW_I3C(opaque);
616c52aaabdSJoe Komlodi     uint32_t addr = offset >> 2;
617c52aaabdSJoe Komlodi     uint64_t value;
618c52aaabdSJoe Komlodi 
619c52aaabdSJoe Komlodi     switch (addr) {
6205f31322eSJoe Komlodi     /* RAZ */
621c52aaabdSJoe Komlodi     case R_COMMAND_QUEUE_PORT:
6225f31322eSJoe Komlodi     case R_RESET_CTRL:
6235f31322eSJoe Komlodi     case R_INTR_FORCE:
624c52aaabdSJoe Komlodi         value = 0;
625c52aaabdSJoe Komlodi         break;
626a825bbb7SJoe Komlodi     case R_INTR_STATUS:
627a825bbb7SJoe Komlodi         value = dw_i3c_intr_status_r(s);
628a825bbb7SJoe Komlodi         break;
629*9c0476a2SJoe Komlodi     case R_RX_TX_DATA_PORT:
630*9c0476a2SJoe Komlodi         value = dw_i3c_pop_rx(s);
631*9c0476a2SJoe Komlodi         break;
632*9c0476a2SJoe Komlodi     case R_RESPONSE_QUEUE_PORT:
633*9c0476a2SJoe Komlodi         value = dw_i3c_resp_queue_port_r(s);
634*9c0476a2SJoe Komlodi         break;
635c52aaabdSJoe Komlodi     default:
636c52aaabdSJoe Komlodi         value = s->regs[addr];
637c52aaabdSJoe Komlodi         break;
638c52aaabdSJoe Komlodi     }
639c52aaabdSJoe Komlodi 
640*9c0476a2SJoe Komlodi     trace_dw_i3c_read(s->cfg.id, offset, value);
641c52aaabdSJoe Komlodi 
642c52aaabdSJoe Komlodi     return value;
643c52aaabdSJoe Komlodi }
644c52aaabdSJoe Komlodi 
645*9c0476a2SJoe Komlodi static void dw_i3c_resp_queue_push(DWI3C *s, uint8_t err, uint8_t tid,
646*9c0476a2SJoe Komlodi                                    uint8_t ccc_type, uint16_t data_len)
647*9c0476a2SJoe Komlodi {
648*9c0476a2SJoe Komlodi     uint32_t val = 0;
649*9c0476a2SJoe Komlodi     val = FIELD_DP32(val, RESPONSE_QUEUE_PORT, ERR_STATUS, err);
650*9c0476a2SJoe Komlodi     val = FIELD_DP32(val, RESPONSE_QUEUE_PORT, TID, tid);
651*9c0476a2SJoe Komlodi     val = FIELD_DP32(val, RESPONSE_QUEUE_PORT, CCCT, ccc_type);
652*9c0476a2SJoe Komlodi     val = FIELD_DP32(val, RESPONSE_QUEUE_PORT, DL, data_len);
653*9c0476a2SJoe Komlodi     if (!fifo32_is_full(&s->resp_queue)) {
654*9c0476a2SJoe Komlodi         trace_dw_i3c_resp_queue_push(s->cfg.id, val);
655*9c0476a2SJoe Komlodi         fifo32_push(&s->resp_queue, val);
656*9c0476a2SJoe Komlodi     }
657*9c0476a2SJoe Komlodi 
658*9c0476a2SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, QUEUE_STATUS_LEVEL, RESP_BUF_BLR,
659*9c0476a2SJoe Komlodi                      fifo32_num_used(&s->resp_queue));
660*9c0476a2SJoe Komlodi     /* Threshold is the register value + 1. */
661*9c0476a2SJoe Komlodi     uint8_t threshold = ARRAY_FIELD_EX32(s->regs, QUEUE_THLD_CTRL,
662*9c0476a2SJoe Komlodi                                          RESP_BUF_THLD) + 1;
663*9c0476a2SJoe Komlodi     if (fifo32_num_used(&s->resp_queue) >= threshold) {
664*9c0476a2SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, INTR_STATUS, RESP_RDY, 1);
665*9c0476a2SJoe Komlodi         dw_i3c_update_irq(s);
666*9c0476a2SJoe Komlodi     }
667*9c0476a2SJoe Komlodi }
668*9c0476a2SJoe Komlodi 
669*9c0476a2SJoe Komlodi static void dw_i3c_push_tx(DWI3C *s, uint32_t val)
670*9c0476a2SJoe Komlodi {
671*9c0476a2SJoe Komlodi     if (fifo32_is_full(&s->tx_queue)) {
672*9c0476a2SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: Tried to push to TX FIFO when "
673*9c0476a2SJoe Komlodi                       "full\n", object_get_canonical_path(OBJECT(s)));
674*9c0476a2SJoe Komlodi         return;
675*9c0476a2SJoe Komlodi     }
676*9c0476a2SJoe Komlodi 
677*9c0476a2SJoe Komlodi     trace_dw_i3c_push_tx(s->cfg.id, val);
678*9c0476a2SJoe Komlodi     fifo32_push(&s->tx_queue, val);
679*9c0476a2SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, DATA_BUFFER_STATUS_LEVEL, TX_BUF_EMPTY_LOC,
680*9c0476a2SJoe Komlodi                      fifo32_num_free(&s->tx_queue));
681*9c0476a2SJoe Komlodi 
682*9c0476a2SJoe Komlodi     /* Threshold is 2^TX_BUF_THLD. */
683*9c0476a2SJoe Komlodi     uint8_t empty_threshold = ARRAY_FIELD_EX32(s->regs, DATA_BUFFER_THLD_CTRL,
684*9c0476a2SJoe Komlodi                                                TX_BUF_THLD);
685*9c0476a2SJoe Komlodi     empty_threshold =
686*9c0476a2SJoe Komlodi         dw_i3c_fifo_threshold_from_reg(empty_threshold);
687*9c0476a2SJoe Komlodi     if (fifo32_num_free(&s->tx_queue) < empty_threshold) {
688*9c0476a2SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, INTR_STATUS, TX_THLD, 0);
689*9c0476a2SJoe Komlodi         dw_i3c_update_irq(s);
690*9c0476a2SJoe Komlodi     }
691*9c0476a2SJoe Komlodi }
692*9c0476a2SJoe Komlodi 
693*9c0476a2SJoe Komlodi static uint32_t dw_i3c_pop_tx(DWI3C *s)
694*9c0476a2SJoe Komlodi {
695*9c0476a2SJoe Komlodi     if (fifo32_is_empty(&s->tx_queue)) {
696*9c0476a2SJoe Komlodi         g_autofree char *path = object_get_canonical_path(OBJECT(s));
697*9c0476a2SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: Tried to pop from TX FIFO when "
698*9c0476a2SJoe Komlodi                       "empty\n", path);
699*9c0476a2SJoe Komlodi         return 0;
700*9c0476a2SJoe Komlodi     }
701*9c0476a2SJoe Komlodi 
702*9c0476a2SJoe Komlodi     uint32_t val = fifo32_pop(&s->tx_queue);
703*9c0476a2SJoe Komlodi     trace_dw_i3c_pop_tx(s->cfg.id, val);
704*9c0476a2SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, DATA_BUFFER_STATUS_LEVEL, TX_BUF_EMPTY_LOC,
705*9c0476a2SJoe Komlodi                      fifo32_num_free(&s->tx_queue));
706*9c0476a2SJoe Komlodi 
707*9c0476a2SJoe Komlodi     /* Threshold is 2^TX_BUF_THLD. */
708*9c0476a2SJoe Komlodi     uint8_t empty_threshold = ARRAY_FIELD_EX32(s->regs, DATA_BUFFER_THLD_CTRL,
709*9c0476a2SJoe Komlodi                                                TX_BUF_THLD);
710*9c0476a2SJoe Komlodi     empty_threshold =
711*9c0476a2SJoe Komlodi         dw_i3c_fifo_threshold_from_reg(empty_threshold);
712*9c0476a2SJoe Komlodi     if (fifo32_num_free(&s->tx_queue) >= empty_threshold) {
713*9c0476a2SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, INTR_STATUS, TX_THLD, 1);
714*9c0476a2SJoe Komlodi         dw_i3c_update_irq(s);
715*9c0476a2SJoe Komlodi     }
716*9c0476a2SJoe Komlodi     return val;
717*9c0476a2SJoe Komlodi }
718*9c0476a2SJoe Komlodi 
719*9c0476a2SJoe Komlodi static void dw_i3c_push_rx(DWI3C *s, uint32_t val)
720*9c0476a2SJoe Komlodi {
721*9c0476a2SJoe Komlodi     if (fifo32_is_full(&s->rx_queue)) {
722*9c0476a2SJoe Komlodi         g_autofree char *path = object_get_canonical_path(OBJECT(s));
723*9c0476a2SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: Tried to push to RX FIFO when "
724*9c0476a2SJoe Komlodi                       "full\n", path);
725*9c0476a2SJoe Komlodi         return;
726*9c0476a2SJoe Komlodi     }
727*9c0476a2SJoe Komlodi     trace_dw_i3c_push_rx(s->cfg.id, val);
728*9c0476a2SJoe Komlodi     fifo32_push(&s->rx_queue, val);
729*9c0476a2SJoe Komlodi 
730*9c0476a2SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, DATA_BUFFER_STATUS_LEVEL, RX_BUF_BLR,
731*9c0476a2SJoe Komlodi                      fifo32_num_used(&s->rx_queue));
732*9c0476a2SJoe Komlodi     /* Threshold is 2^RX_BUF_THLD. */
733*9c0476a2SJoe Komlodi     uint8_t threshold = ARRAY_FIELD_EX32(s->regs, DATA_BUFFER_THLD_CTRL,
734*9c0476a2SJoe Komlodi                                          RX_BUF_THLD);
735*9c0476a2SJoe Komlodi     threshold = dw_i3c_fifo_threshold_from_reg(threshold);
736*9c0476a2SJoe Komlodi     if (fifo32_num_used(&s->rx_queue) >= threshold) {
737*9c0476a2SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, INTR_STATUS, RX_THLD, 1);
738*9c0476a2SJoe Komlodi         dw_i3c_update_irq(s);
739*9c0476a2SJoe Komlodi     }
740*9c0476a2SJoe Komlodi }
741*9c0476a2SJoe Komlodi 
742*9c0476a2SJoe Komlodi static void dw_i3c_short_transfer(DWI3C *s, DWI3CTransferCmd cmd,
743*9c0476a2SJoe Komlodi                                   DWI3CShortArg arg)
744*9c0476a2SJoe Komlodi {
745*9c0476a2SJoe Komlodi     uint8_t err = DW_I3C_RESP_QUEUE_ERR_NONE;
746*9c0476a2SJoe Komlodi     uint8_t addr = dw_i3c_target_addr(s, cmd.dev_index);
747*9c0476a2SJoe Komlodi     bool is_i2c = dw_i3c_target_is_i2c(s, cmd.dev_index);
748*9c0476a2SJoe Komlodi     uint8_t data[4]; /* Max we can send on a short transfer is 4 bytes. */
749*9c0476a2SJoe Komlodi     uint8_t len = 0;
750*9c0476a2SJoe Komlodi     uint32_t bytes_sent; /* Ignored on short transfers. */
751*9c0476a2SJoe Komlodi 
752*9c0476a2SJoe Komlodi     /* Can't do reads on a short transfer. */
753*9c0476a2SJoe Komlodi     if (cmd.rnw) {
754*9c0476a2SJoe Komlodi         g_autofree char *path = object_get_canonical_path(OBJECT(s));
755*9c0476a2SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: Cannot do a read on a short "
756*9c0476a2SJoe Komlodi                       "transfer\n", path);
757*9c0476a2SJoe Komlodi         return;
758*9c0476a2SJoe Komlodi     }
759*9c0476a2SJoe Komlodi 
760*9c0476a2SJoe Komlodi     if (dw_i3c_send_start(s, addr, /*is_recv=*/false, is_i2c)) {
761*9c0476a2SJoe Komlodi         err = DW_I3C_RESP_QUEUE_ERR_I2C_NACK;
762*9c0476a2SJoe Komlodi         goto transfer_done;
763*9c0476a2SJoe Komlodi     }
764*9c0476a2SJoe Komlodi 
765*9c0476a2SJoe Komlodi     /* Are we sending a command? */
766*9c0476a2SJoe Komlodi     if (cmd.cp) {
767*9c0476a2SJoe Komlodi         data[len] = cmd.cmd;
768*9c0476a2SJoe Komlodi         len++;
769*9c0476a2SJoe Komlodi         /*
770*9c0476a2SJoe Komlodi          * byte0 is the defining byte for a command, and is only sent if a
771*9c0476a2SJoe Komlodi          * command is present and if the command has a defining byte present.
772*9c0476a2SJoe Komlodi          * (byte_strb & 0x01) is always treated as set by the controller, and is
773*9c0476a2SJoe Komlodi          * ignored.
774*9c0476a2SJoe Komlodi          */
775*9c0476a2SJoe Komlodi         if (cmd.dbp) {
776*9c0476a2SJoe Komlodi             data[len] += arg.byte0;
777*9c0476a2SJoe Komlodi             len++;
778*9c0476a2SJoe Komlodi         }
779*9c0476a2SJoe Komlodi     }
780*9c0476a2SJoe Komlodi 
781*9c0476a2SJoe Komlodi     /* Send the bytes passed in the argument. */
782*9c0476a2SJoe Komlodi     if (arg.byte_strb & 0x02) {
783*9c0476a2SJoe Komlodi         data[len] = arg.byte1;
784*9c0476a2SJoe Komlodi         len++;
785*9c0476a2SJoe Komlodi     }
786*9c0476a2SJoe Komlodi     if (arg.byte_strb & 0x04) {
787*9c0476a2SJoe Komlodi         data[len] = arg.byte2;
788*9c0476a2SJoe Komlodi         len++;
789*9c0476a2SJoe Komlodi     }
790*9c0476a2SJoe Komlodi 
791*9c0476a2SJoe Komlodi     if (dw_i3c_send(s, data, len, &bytes_sent, is_i2c)) {
792*9c0476a2SJoe Komlodi         err = DW_I3C_RESP_QUEUE_ERR_I2C_NACK;
793*9c0476a2SJoe Komlodi     } else {
794*9c0476a2SJoe Komlodi         /* Only go to an idle state on a successful transfer. */
795*9c0476a2SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS,
796*9c0476a2SJoe Komlodi                          DW_I3C_TRANSFER_STATE_IDLE);
797*9c0476a2SJoe Komlodi     }
798*9c0476a2SJoe Komlodi 
799*9c0476a2SJoe Komlodi transfer_done:
800*9c0476a2SJoe Komlodi     if (cmd.toc) {
801*9c0476a2SJoe Komlodi         dw_i3c_end_transfer(s, is_i2c);
802*9c0476a2SJoe Komlodi     }
803*9c0476a2SJoe Komlodi     if (cmd.roc) {
804*9c0476a2SJoe Komlodi         /*
805*9c0476a2SJoe Komlodi          * ccc_type is always 0 in controller mode, data_len is 0 in short
806*9c0476a2SJoe Komlodi          * transfers.
807*9c0476a2SJoe Komlodi          */
808*9c0476a2SJoe Komlodi         dw_i3c_resp_queue_push(s, err, cmd.tid, /*ccc_type=*/0,
809*9c0476a2SJoe Komlodi                                           /*data_len=*/0);
810*9c0476a2SJoe Komlodi     }
811*9c0476a2SJoe Komlodi }
812*9c0476a2SJoe Komlodi 
813*9c0476a2SJoe Komlodi /* Returns number of bytes transmitted. */
814*9c0476a2SJoe Komlodi static uint16_t dw_i3c_tx(DWI3C *s, uint16_t num, bool is_i2c)
815*9c0476a2SJoe Komlodi {
816*9c0476a2SJoe Komlodi     uint16_t bytes_sent = 0;
817*9c0476a2SJoe Komlodi     union {
818*9c0476a2SJoe Komlodi         uint8_t b[sizeof(uint32_t)];
819*9c0476a2SJoe Komlodi         uint32_t val;
820*9c0476a2SJoe Komlodi     } val32;
821*9c0476a2SJoe Komlodi 
822*9c0476a2SJoe Komlodi     while (bytes_sent < num) {
823*9c0476a2SJoe Komlodi         val32.val = dw_i3c_pop_tx(s);
824*9c0476a2SJoe Komlodi         for (uint8_t i = 0; i < sizeof(val32.val); i++) {
825*9c0476a2SJoe Komlodi             if (dw_i3c_send_byte(s, val32.b[i], is_i2c)) {
826*9c0476a2SJoe Komlodi                 return bytes_sent;
827*9c0476a2SJoe Komlodi             }
828*9c0476a2SJoe Komlodi             bytes_sent++;
829*9c0476a2SJoe Komlodi 
830*9c0476a2SJoe Komlodi             /* We're not sending the full 32-bits, break early. */
831*9c0476a2SJoe Komlodi             if (bytes_sent >= num) {
832*9c0476a2SJoe Komlodi                 break;
833*9c0476a2SJoe Komlodi             }
834*9c0476a2SJoe Komlodi         }
835*9c0476a2SJoe Komlodi     }
836*9c0476a2SJoe Komlodi 
837*9c0476a2SJoe Komlodi     return bytes_sent;
838*9c0476a2SJoe Komlodi }
839*9c0476a2SJoe Komlodi 
840*9c0476a2SJoe Komlodi /* Returns number of bytes received. */
841*9c0476a2SJoe Komlodi static uint16_t dw_i3c_rx(DWI3C *s, uint16_t num, bool is_i2c)
842*9c0476a2SJoe Komlodi {
843*9c0476a2SJoe Komlodi     /*
844*9c0476a2SJoe Komlodi      * Allocate a temporary buffer to read data from the target.
845*9c0476a2SJoe Komlodi      * Zero it and word-align it as well in case we're reading unaligned data.
846*9c0476a2SJoe Komlodi      */
847*9c0476a2SJoe Komlodi     g_autofree uint8_t *data = g_new0(uint8_t, num + (4 - (num & 0x03)));
848*9c0476a2SJoe Komlodi     uint32_t *data32 = (uint32_t *)data;
849*9c0476a2SJoe Komlodi     /*
850*9c0476a2SJoe Komlodi      * 32-bits since the I3C API wants a 32-bit number, even though the
851*9c0476a2SJoe Komlodi      * controller can only do 16-bit transfers.
852*9c0476a2SJoe Komlodi      */
853*9c0476a2SJoe Komlodi     uint32_t num_read = 0;
854*9c0476a2SJoe Komlodi 
855*9c0476a2SJoe Komlodi     /* Can NACK if the target receives an unsupported CCC. */
856*9c0476a2SJoe Komlodi     if (dw_i3c_recv_data(s, is_i2c, data, num, &num_read)) {
857*9c0476a2SJoe Komlodi         return 0;
858*9c0476a2SJoe Komlodi     }
859*9c0476a2SJoe Komlodi 
860*9c0476a2SJoe Komlodi     for (uint16_t i = 0; i < num_read / 4; i++) {
861*9c0476a2SJoe Komlodi         dw_i3c_push_rx(s, *data32);
862*9c0476a2SJoe Komlodi         data32++;
863*9c0476a2SJoe Komlodi     }
864*9c0476a2SJoe Komlodi     /*
865*9c0476a2SJoe Komlodi      * If we're pushing data that isn't 32-bit aligned, push what's left.
866*9c0476a2SJoe Komlodi      * It's software's responsibility to know what bits are valid in the partial
867*9c0476a2SJoe Komlodi      * data.
868*9c0476a2SJoe Komlodi      */
869*9c0476a2SJoe Komlodi     if (num_read & 0x03) {
870*9c0476a2SJoe Komlodi         dw_i3c_push_rx(s, *data32);
871*9c0476a2SJoe Komlodi     }
872*9c0476a2SJoe Komlodi 
873*9c0476a2SJoe Komlodi     return num_read;
874*9c0476a2SJoe Komlodi }
875*9c0476a2SJoe Komlodi 
876*9c0476a2SJoe Komlodi static int dw_i3c_transfer_ccc(DWI3C *s, DWI3CTransferCmd cmd,
877*9c0476a2SJoe Komlodi                                DWI3CTransferArg arg)
878*9c0476a2SJoe Komlodi {
879*9c0476a2SJoe Komlodi     /* CCC start is always a write. CCCs cannot be done on I2C devices. */
880*9c0476a2SJoe Komlodi     if (dw_i3c_send_start(s, I3C_BROADCAST, /*is_recv=*/false,
881*9c0476a2SJoe Komlodi                                      /*is_i2c=*/false)) {
882*9c0476a2SJoe Komlodi         return DW_I3C_RESP_QUEUE_ERR_BROADCAST_NACK;
883*9c0476a2SJoe Komlodi     }
884*9c0476a2SJoe Komlodi     trace_dw_i3c_transfer_ccc(s->cfg.id, cmd.cmd);
885*9c0476a2SJoe Komlodi     if (dw_i3c_send_byte(s, cmd.cmd, /*is_i2c=*/false)) {
886*9c0476a2SJoe Komlodi         return DW_I3C_RESP_QUEUE_ERR_I2C_NACK;
887*9c0476a2SJoe Komlodi     }
888*9c0476a2SJoe Komlodi 
889*9c0476a2SJoe Komlodi     /* On a direct CCC, we do a restart and then send the target's address. */
890*9c0476a2SJoe Komlodi     if (CCC_IS_DIRECT(cmd.cmd)) {
891*9c0476a2SJoe Komlodi         bool is_recv = cmd.rnw;
892*9c0476a2SJoe Komlodi         uint8_t addr = dw_i3c_target_addr(s, cmd.dev_index);
893*9c0476a2SJoe Komlodi         if (dw_i3c_send_start(s, addr, is_recv, /*is_i2c=*/false)) {
894*9c0476a2SJoe Komlodi             return DW_I3C_RESP_QUEUE_ERR_BROADCAST_NACK;
895*9c0476a2SJoe Komlodi         }
896*9c0476a2SJoe Komlodi     }
897*9c0476a2SJoe Komlodi 
898*9c0476a2SJoe Komlodi     return DW_I3C_RESP_QUEUE_ERR_NONE;
899*9c0476a2SJoe Komlodi }
900*9c0476a2SJoe Komlodi 
901*9c0476a2SJoe Komlodi static void dw_i3c_transfer(DWI3C *s, DWI3CTransferCmd cmd,
902*9c0476a2SJoe Komlodi                             DWI3CTransferArg arg)
903*9c0476a2SJoe Komlodi {
904*9c0476a2SJoe Komlodi     bool is_recv = cmd.rnw;
905*9c0476a2SJoe Komlodi     uint8_t err = DW_I3C_RESP_QUEUE_ERR_NONE;
906*9c0476a2SJoe Komlodi     uint8_t addr = dw_i3c_target_addr(s, cmd.dev_index);
907*9c0476a2SJoe Komlodi     bool is_i2c = dw_i3c_target_is_i2c(s, cmd.dev_index);
908*9c0476a2SJoe Komlodi     uint16_t bytes_transferred = 0;
909*9c0476a2SJoe Komlodi 
910*9c0476a2SJoe Komlodi     if (cmd.cp) {
911*9c0476a2SJoe Komlodi         /* We're sending a CCC. */
912*9c0476a2SJoe Komlodi         err = dw_i3c_transfer_ccc(s, cmd, arg);
913*9c0476a2SJoe Komlodi         if (err != DW_I3C_RESP_QUEUE_ERR_NONE) {
914*9c0476a2SJoe Komlodi             goto transfer_done;
915*9c0476a2SJoe Komlodi         }
916*9c0476a2SJoe Komlodi     } else {
917*9c0476a2SJoe Komlodi         if (ARRAY_FIELD_EX32(s->regs, DEVICE_CTRL, I3C_BROADCAST_ADDR_INC) &&
918*9c0476a2SJoe Komlodi             is_i2c == false) {
919*9c0476a2SJoe Komlodi             if (dw_i3c_send_start(s, I3C_BROADCAST,
920*9c0476a2SJoe Komlodi                                              /*is_recv=*/false, is_i2c)) {
921*9c0476a2SJoe Komlodi                 err = DW_I3C_RESP_QUEUE_ERR_I2C_NACK;
922*9c0476a2SJoe Komlodi                 goto transfer_done;
923*9c0476a2SJoe Komlodi             }
924*9c0476a2SJoe Komlodi         }
925*9c0476a2SJoe Komlodi         /* Otherwise we're doing a private transfer. */
926*9c0476a2SJoe Komlodi         if (dw_i3c_send_start(s, addr, is_recv, is_i2c)) {
927*9c0476a2SJoe Komlodi             err = DW_I3C_RESP_QUEUE_ERR_I2C_NACK;
928*9c0476a2SJoe Komlodi             goto transfer_done;
929*9c0476a2SJoe Komlodi         }
930*9c0476a2SJoe Komlodi     }
931*9c0476a2SJoe Komlodi 
932*9c0476a2SJoe Komlodi     if (is_recv) {
933*9c0476a2SJoe Komlodi         bytes_transferred = dw_i3c_rx(s, arg.data_len, is_i2c);
934*9c0476a2SJoe Komlodi     } else {
935*9c0476a2SJoe Komlodi         bytes_transferred = dw_i3c_tx(s, arg.data_len, is_i2c);
936*9c0476a2SJoe Komlodi     }
937*9c0476a2SJoe Komlodi 
938*9c0476a2SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS,
939*9c0476a2SJoe Komlodi                      DW_I3C_TRANSFER_STATE_IDLE);
940*9c0476a2SJoe Komlodi 
941*9c0476a2SJoe Komlodi transfer_done:
942*9c0476a2SJoe Komlodi     if (cmd.toc) {
943*9c0476a2SJoe Komlodi         dw_i3c_end_transfer(s, is_i2c);
944*9c0476a2SJoe Komlodi     }
945*9c0476a2SJoe Komlodi     if (cmd.roc) {
946*9c0476a2SJoe Komlodi         /*
947*9c0476a2SJoe Komlodi          * data_len is the number of bytes that still need to be TX'd, or the
948*9c0476a2SJoe Komlodi          * number of bytes RX'd.
949*9c0476a2SJoe Komlodi          */
950*9c0476a2SJoe Komlodi         uint16_t data_len = is_recv ? bytes_transferred : arg.data_len -
951*9c0476a2SJoe Komlodi                                                           bytes_transferred;
952*9c0476a2SJoe Komlodi         /* CCCT is always 0 in controller mode. */
953*9c0476a2SJoe Komlodi         dw_i3c_resp_queue_push(s, err, cmd.tid, /*ccc_type=*/0,
954*9c0476a2SJoe Komlodi                                           data_len);
955*9c0476a2SJoe Komlodi     }
956*9c0476a2SJoe Komlodi 
957*9c0476a2SJoe Komlodi     dw_i3c_update_irq(s);
958*9c0476a2SJoe Komlodi }
959*9c0476a2SJoe Komlodi 
960*9c0476a2SJoe Komlodi static void dw_i3c_transfer_cmd(DWI3C *s, DWI3CTransferCmd cmd,
961*9c0476a2SJoe Komlodi                                 DWI3CCmdQueueData arg)
962*9c0476a2SJoe Komlodi {
963*9c0476a2SJoe Komlodi     uint8_t arg_attr = FIELD_EX32(arg.word, COMMAND_QUEUE_PORT, CMD_ATTR);
964*9c0476a2SJoe Komlodi 
965*9c0476a2SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CMD_TID, cmd.tid);
966*9c0476a2SJoe Komlodi 
967*9c0476a2SJoe Komlodi     /* User is trying to do HDR transfers, see if we can do them. */
968*9c0476a2SJoe Komlodi     if (cmd.speed == 0x06 && !dw_i3c_has_hdr_ddr(s)) {
969*9c0476a2SJoe Komlodi         g_autofree char *path = object_get_canonical_path(OBJECT(s));
970*9c0476a2SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: HDR DDR is not supported\n", path);
971*9c0476a2SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS,
972*9c0476a2SJoe Komlodi                          DW_I3C_TRANSFER_STATE_HALT);
973*9c0476a2SJoe Komlodi         return;
974*9c0476a2SJoe Komlodi     }
975*9c0476a2SJoe Komlodi     if (cmd.speed == 0x05 && !dw_i3c_has_hdr_ts(s)) {
976*9c0476a2SJoe Komlodi         g_autofree char *path = object_get_canonical_path(OBJECT(s));
977*9c0476a2SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: HDR TS is not supported\n", path);
978*9c0476a2SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS,
979*9c0476a2SJoe Komlodi                          DW_I3C_TRANSFER_STATE_HALT);
980*9c0476a2SJoe Komlodi         return;
981*9c0476a2SJoe Komlodi     }
982*9c0476a2SJoe Komlodi 
983*9c0476a2SJoe Komlodi     if (arg_attr == DW_I3C_CMD_ATTR_TRANSFER_ARG) {
984*9c0476a2SJoe Komlodi         dw_i3c_transfer(s, cmd, arg.transfer_arg);
985*9c0476a2SJoe Komlodi     } else if (arg_attr == DW_I3C_CMD_ATTR_SHORT_DATA_ARG) {
986*9c0476a2SJoe Komlodi         dw_i3c_short_transfer(s, cmd, arg.short_arg);
987*9c0476a2SJoe Komlodi     } else {
988*9c0476a2SJoe Komlodi         g_autofree char *path = object_get_canonical_path(OBJECT(s));
989*9c0476a2SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: Unknown command queue cmd_attr 0x%x"
990*9c0476a2SJoe Komlodi                       "\n", path, arg_attr);
991*9c0476a2SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS,
992*9c0476a2SJoe Komlodi                          DW_I3C_TRANSFER_STATE_HALT);
993*9c0476a2SJoe Komlodi     }
994*9c0476a2SJoe Komlodi }
995*9c0476a2SJoe Komlodi 
996*9c0476a2SJoe Komlodi static void dw_i3c_update_char_table(DWI3C *s, uint8_t offset, uint64_t pid,
997*9c0476a2SJoe Komlodi                                      uint8_t bcr, uint8_t dcr, uint8_t addr)
998*9c0476a2SJoe Komlodi {
999*9c0476a2SJoe Komlodi     if (offset > s->cfg.num_addressable_devices) {
1000*9c0476a2SJoe Komlodi         g_autofree char *path = object_get_canonical_path(OBJECT(s));
1001*9c0476a2SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: Device char table offset %d out of "
1002*9c0476a2SJoe Komlodi                       "bounds\n", path, offset);
1003*9c0476a2SJoe Komlodi         /* If we're out of bounds, do nothing. */
1004*9c0476a2SJoe Komlodi         return;
1005*9c0476a2SJoe Komlodi     }
1006*9c0476a2SJoe Komlodi 
1007*9c0476a2SJoe Komlodi     /*
1008*9c0476a2SJoe Komlodi      * Each device offset is 128 bits apart in the table, since each device gets
1009*9c0476a2SJoe Komlodi      * 4 * 32-bits of entries in the table.
1010*9c0476a2SJoe Komlodi      * / sizeof(uint32_t) because we're indexing into our 32-bit reg array.
1011*9c0476a2SJoe Komlodi      */
1012*9c0476a2SJoe Komlodi     uint16_t dev_index = (ARRAY_FIELD_EX32(s->regs, DEV_CHAR_TABLE_POINTER,
1013*9c0476a2SJoe Komlodi                                           P_DEV_CHAR_TABLE_START_ADDR) /
1014*9c0476a2SJoe Komlodi                                           sizeof(uint32_t)) +
1015*9c0476a2SJoe Komlodi                                           (offset * sizeof(uint32_t));
1016*9c0476a2SJoe Komlodi     s->regs[dev_index] = pid & 0xffffffff;
1017*9c0476a2SJoe Komlodi     pid >>= 32;
1018*9c0476a2SJoe Komlodi     s->regs[dev_index + 1] = FIELD_DP32(s->regs[dev_index + 1],
1019*9c0476a2SJoe Komlodi                                         DEVICE_CHARACTERISTIC_TABLE_LOC2,
1020*9c0476a2SJoe Komlodi                                         MSB_PID, pid);
1021*9c0476a2SJoe Komlodi     s->regs[dev_index + 2] = FIELD_DP32(s->regs[dev_index + 2],
1022*9c0476a2SJoe Komlodi                                         DEVICE_CHARACTERISTIC_TABLE_LOC3, DCR,
1023*9c0476a2SJoe Komlodi                                         dcr);
1024*9c0476a2SJoe Komlodi     s->regs[dev_index + 2] = FIELD_DP32(s->regs[dev_index + 2],
1025*9c0476a2SJoe Komlodi                                         DEVICE_CHARACTERISTIC_TABLE_LOC3, BCR,
1026*9c0476a2SJoe Komlodi                                         bcr);
1027*9c0476a2SJoe Komlodi     s->regs[dev_index + 3] = FIELD_DP32(s->regs[dev_index + 3],
1028*9c0476a2SJoe Komlodi                                         DEVICE_CHARACTERISTIC_TABLE_LOC4,
1029*9c0476a2SJoe Komlodi                                         DEV_DYNAMIC_ADDR, addr);
1030*9c0476a2SJoe Komlodi 
1031*9c0476a2SJoe Komlodi     /* Increment PRESENT_DEV_CHAR_TABLE_INDEX. */
1032*9c0476a2SJoe Komlodi     uint8_t idx = ARRAY_FIELD_EX32(s->regs, DEV_CHAR_TABLE_POINTER,
1033*9c0476a2SJoe Komlodi                      PRESENT_DEV_CHAR_TABLE_INDEX);
1034*9c0476a2SJoe Komlodi     /* Increment and rollover. */
1035*9c0476a2SJoe Komlodi     idx++;
1036*9c0476a2SJoe Komlodi     if (idx >= ARRAY_FIELD_EX32(s->regs, DEV_CHAR_TABLE_POINTER,
1037*9c0476a2SJoe Komlodi                                DEV_CHAR_TABLE_DEPTH) / 4) {
1038*9c0476a2SJoe Komlodi         idx = 0;
1039*9c0476a2SJoe Komlodi     }
1040*9c0476a2SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, DEV_CHAR_TABLE_POINTER,
1041*9c0476a2SJoe Komlodi                      PRESENT_DEV_CHAR_TABLE_INDEX, idx);
1042*9c0476a2SJoe Komlodi }
1043*9c0476a2SJoe Komlodi 
1044*9c0476a2SJoe Komlodi static void dw_i3c_addr_assign_cmd(DWI3C *s, DWI3CAddrAssignCmd cmd)
1045*9c0476a2SJoe Komlodi {
1046*9c0476a2SJoe Komlodi     uint8_t i = 0;
1047*9c0476a2SJoe Komlodi     uint8_t err = DW_I3C_RESP_QUEUE_ERR_NONE;
1048*9c0476a2SJoe Komlodi 
1049*9c0476a2SJoe Komlodi     /* Tell everyone to ENTDAA. If these error, no one is on the bus. */
1050*9c0476a2SJoe Komlodi     if (dw_i3c_send_start(s, I3C_BROADCAST, /*is_recv=*/false,
1051*9c0476a2SJoe Komlodi                                      /*is_i2c=*/false)) {
1052*9c0476a2SJoe Komlodi         err = DW_I3C_RESP_QUEUE_ERR_BROADCAST_NACK;
1053*9c0476a2SJoe Komlodi         goto transfer_done;
1054*9c0476a2SJoe Komlodi     }
1055*9c0476a2SJoe Komlodi     if (dw_i3c_send_byte(s, cmd.cmd, /*is_i2c=*/false)) {
1056*9c0476a2SJoe Komlodi         err = DW_I3C_RESP_QUEUE_ERR_BROADCAST_NACK;
1057*9c0476a2SJoe Komlodi         goto transfer_done;
1058*9c0476a2SJoe Komlodi     }
1059*9c0476a2SJoe Komlodi 
1060*9c0476a2SJoe Komlodi     /* Go through each device in the table and assign it an address. */
1061*9c0476a2SJoe Komlodi     for (i = 0; i < cmd.dev_count; i++) {
1062*9c0476a2SJoe Komlodi         uint8_t addr = dw_i3c_target_addr(s, cmd.dev_index + i);
1063*9c0476a2SJoe Komlodi         union {
1064*9c0476a2SJoe Komlodi             uint64_t pid:48;
1065*9c0476a2SJoe Komlodi             uint8_t bcr;
1066*9c0476a2SJoe Komlodi             uint8_t dcr;
1067*9c0476a2SJoe Komlodi             uint32_t w[2];
1068*9c0476a2SJoe Komlodi             uint8_t b[8];
1069*9c0476a2SJoe Komlodi         } target_info;
1070*9c0476a2SJoe Komlodi 
1071*9c0476a2SJoe Komlodi         /* If this fails, there was no one left to ENTDAA. */
1072*9c0476a2SJoe Komlodi         if (dw_i3c_send_start(s, I3C_BROADCAST, /*is_recv=*/false,
1073*9c0476a2SJoe Komlodi                                          /*is_i2c=*/false)) {
1074*9c0476a2SJoe Komlodi             err = DW_I3C_RESP_QUEUE_ERR_BROADCAST_NACK;
1075*9c0476a2SJoe Komlodi             break;
1076*9c0476a2SJoe Komlodi         }
1077*9c0476a2SJoe Komlodi 
1078*9c0476a2SJoe Komlodi         /*
1079*9c0476a2SJoe Komlodi          * In ENTDAA, we read 8 bytes from the target, which will be the
1080*9c0476a2SJoe Komlodi          * target's PID, BCR, and DCR. After that, we send it the dynamic
1081*9c0476a2SJoe Komlodi          * address.
1082*9c0476a2SJoe Komlodi          * Don't bother checking the number of bytes received, it must send 8
1083*9c0476a2SJoe Komlodi          * bytes during ENTDAA.
1084*9c0476a2SJoe Komlodi          */
1085*9c0476a2SJoe Komlodi         uint32_t num_read;
1086*9c0476a2SJoe Komlodi         if (dw_i3c_recv_data(s, /*is_i2c=*/false, target_info.b,
1087*9c0476a2SJoe Komlodi                                         I3C_ENTDAA_SIZE, &num_read)) {
1088*9c0476a2SJoe Komlodi             g_autofree char *path = object_get_canonical_path(OBJECT(s));
1089*9c0476a2SJoe Komlodi             qemu_log_mask(LOG_GUEST_ERROR, "%s: Target NACKed ENTDAA CCC\n",
1090*9c0476a2SJoe Komlodi                           path);
1091*9c0476a2SJoe Komlodi             err = DW_I3C_RESP_QUEUE_ERR_DAA_NACK;
1092*9c0476a2SJoe Komlodi             goto transfer_done;
1093*9c0476a2SJoe Komlodi         }
1094*9c0476a2SJoe Komlodi         if (dw_i3c_send_byte(s, addr, /*is_i2c=*/false)) {
1095*9c0476a2SJoe Komlodi             g_autofree char *path = object_get_canonical_path(OBJECT(s));
1096*9c0476a2SJoe Komlodi             qemu_log_mask(LOG_GUEST_ERROR, "%s: Target NACKed addr 0x%.2x "
1097*9c0476a2SJoe Komlodi                           "during ENTDAA\n", path, addr);
1098*9c0476a2SJoe Komlodi             err = DW_I3C_RESP_QUEUE_ERR_DAA_NACK;
1099*9c0476a2SJoe Komlodi             break;
1100*9c0476a2SJoe Komlodi         }
1101*9c0476a2SJoe Komlodi         dw_i3c_update_char_table(s, cmd.dev_index + i,
1102*9c0476a2SJoe Komlodi                                             target_info.pid, target_info.bcr,
1103*9c0476a2SJoe Komlodi                                             target_info.dcr, addr);
1104*9c0476a2SJoe Komlodi 
1105*9c0476a2SJoe Komlodi         /* Push the PID, BCR, and DCR to the RX queue. */
1106*9c0476a2SJoe Komlodi         dw_i3c_push_rx(s, target_info.w[0]);
1107*9c0476a2SJoe Komlodi         dw_i3c_push_rx(s, target_info.w[1]);
1108*9c0476a2SJoe Komlodi     }
1109*9c0476a2SJoe Komlodi 
1110*9c0476a2SJoe Komlodi transfer_done:
1111*9c0476a2SJoe Komlodi     /* Do we send a STOP? */
1112*9c0476a2SJoe Komlodi     if (cmd.toc) {
1113*9c0476a2SJoe Komlodi         dw_i3c_end_transfer(s, /*is_i2c=*/false);
1114*9c0476a2SJoe Komlodi     }
1115*9c0476a2SJoe Komlodi     /*
1116*9c0476a2SJoe Komlodi      * For addr assign commands, the length field is the number of devices
1117*9c0476a2SJoe Komlodi      * left to assign. CCCT is always 0 in controller mode.
1118*9c0476a2SJoe Komlodi      */
1119*9c0476a2SJoe Komlodi     if (cmd.roc) {
1120*9c0476a2SJoe Komlodi         dw_i3c_resp_queue_push(s, err, cmd.tid, /*ccc_type=*/0,
1121*9c0476a2SJoe Komlodi                                          cmd.dev_count - i);
1122*9c0476a2SJoe Komlodi     }
1123*9c0476a2SJoe Komlodi }
1124*9c0476a2SJoe Komlodi 
1125*9c0476a2SJoe Komlodi static uint32_t dw_i3c_cmd_queue_pop(DWI3C *s)
1126*9c0476a2SJoe Komlodi {
1127*9c0476a2SJoe Komlodi     if (fifo32_is_empty(&s->cmd_queue)) {
1128*9c0476a2SJoe Komlodi         g_autofree char *path = object_get_canonical_path(OBJECT(s));
1129*9c0476a2SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: Tried to dequeue command queue "
1130*9c0476a2SJoe Komlodi                       "when it was empty\n", path);
1131*9c0476a2SJoe Komlodi         return 0;
1132*9c0476a2SJoe Komlodi     }
1133*9c0476a2SJoe Komlodi     uint32_t val = fifo32_pop(&s->cmd_queue);
1134*9c0476a2SJoe Komlodi 
1135*9c0476a2SJoe Komlodi     uint8_t empty_threshold = ARRAY_FIELD_EX32(s->regs, QUEUE_THLD_CTRL,
1136*9c0476a2SJoe Komlodi                                                CMD_BUF_EMPTY_THLD);
1137*9c0476a2SJoe Komlodi     uint8_t cmd_queue_empty_loc = ARRAY_FIELD_EX32(s->regs,
1138*9c0476a2SJoe Komlodi                                                    QUEUE_STATUS_LEVEL,
1139*9c0476a2SJoe Komlodi                                                    CMD_QUEUE_EMPTY_LOC);
1140*9c0476a2SJoe Komlodi     cmd_queue_empty_loc++;
1141*9c0476a2SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, QUEUE_STATUS_LEVEL, CMD_QUEUE_EMPTY_LOC,
1142*9c0476a2SJoe Komlodi                      cmd_queue_empty_loc);
1143*9c0476a2SJoe Komlodi     if (cmd_queue_empty_loc >= empty_threshold) {
1144*9c0476a2SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, INTR_STATUS, CMD_QUEUE_RDY, 1);
1145*9c0476a2SJoe Komlodi         dw_i3c_update_irq(s);
1146*9c0476a2SJoe Komlodi     }
1147*9c0476a2SJoe Komlodi 
1148*9c0476a2SJoe Komlodi     return val;
1149*9c0476a2SJoe Komlodi }
1150*9c0476a2SJoe Komlodi 
1151*9c0476a2SJoe Komlodi static void dw_i3c_cmd_queue_execute(DWI3C *s)
1152*9c0476a2SJoe Komlodi {
1153*9c0476a2SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS,
1154*9c0476a2SJoe Komlodi                      DW_I3C_TRANSFER_STATE_IDLE);
1155*9c0476a2SJoe Komlodi     if (!dw_i3c_can_transmit(s)) {
1156*9c0476a2SJoe Komlodi         return;
1157*9c0476a2SJoe Komlodi     }
1158*9c0476a2SJoe Komlodi 
1159*9c0476a2SJoe Komlodi     /*
1160*9c0476a2SJoe Komlodi      * We only start executing when a command is passed into the FIFO.
1161*9c0476a2SJoe Komlodi      * We expect there to be a multiple of 2 items in the queue. The first item
1162*9c0476a2SJoe Komlodi      * should be an argument to a command, and the command should be the second
1163*9c0476a2SJoe Komlodi      * item.
1164*9c0476a2SJoe Komlodi      */
1165*9c0476a2SJoe Komlodi     if (fifo32_num_used(&s->cmd_queue) & 1) {
1166*9c0476a2SJoe Komlodi         return;
1167*9c0476a2SJoe Komlodi     }
1168*9c0476a2SJoe Komlodi 
1169*9c0476a2SJoe Komlodi     while (!fifo32_is_empty(&s->cmd_queue)) {
1170*9c0476a2SJoe Komlodi         DWI3CCmdQueueData arg;
1171*9c0476a2SJoe Komlodi         arg.word = dw_i3c_cmd_queue_pop(s);
1172*9c0476a2SJoe Komlodi         DWI3CCmdQueueData cmd;
1173*9c0476a2SJoe Komlodi         cmd.word = dw_i3c_cmd_queue_pop(s);
1174*9c0476a2SJoe Komlodi         trace_dw_i3c_cmd_queue_execute(s->cfg.id, cmd.word, arg.word);
1175*9c0476a2SJoe Komlodi 
1176*9c0476a2SJoe Komlodi         uint8_t cmd_attr = FIELD_EX32(cmd.word, COMMAND_QUEUE_PORT, CMD_ATTR);
1177*9c0476a2SJoe Komlodi         switch (cmd_attr) {
1178*9c0476a2SJoe Komlodi         case DW_I3C_CMD_ATTR_TRANSFER_CMD:
1179*9c0476a2SJoe Komlodi             dw_i3c_transfer_cmd(s, cmd.transfer_cmd, arg);
1180*9c0476a2SJoe Komlodi             break;
1181*9c0476a2SJoe Komlodi         case DW_I3C_CMD_ATTR_ADDR_ASSIGN_CMD:
1182*9c0476a2SJoe Komlodi             /* Arg is discarded for addr assign commands. */
1183*9c0476a2SJoe Komlodi             dw_i3c_addr_assign_cmd(s, cmd.addr_assign_cmd);
1184*9c0476a2SJoe Komlodi             break;
1185*9c0476a2SJoe Komlodi         case DW_I3C_CMD_ATTR_TRANSFER_ARG:
1186*9c0476a2SJoe Komlodi         case DW_I3C_CMD_ATTR_SHORT_DATA_ARG:
1187*9c0476a2SJoe Komlodi             {
1188*9c0476a2SJoe Komlodi                 g_autofree char *path = object_get_canonical_path(OBJECT(s));
1189*9c0476a2SJoe Komlodi                 qemu_log_mask(LOG_GUEST_ERROR, "%s: Command queue received "
1190*9c0476a2SJoe Komlodi                               "argument packet when it expected a command "
1191*9c0476a2SJoe Komlodi                               "packet\n", path);
1192*9c0476a2SJoe Komlodi             }
1193*9c0476a2SJoe Komlodi             break;
1194*9c0476a2SJoe Komlodi         default:
1195*9c0476a2SJoe Komlodi             /*
1196*9c0476a2SJoe Komlodi              * The caller's check before queueing an item should prevent this
1197*9c0476a2SJoe Komlodi              * from happening.
1198*9c0476a2SJoe Komlodi              */
1199*9c0476a2SJoe Komlodi             g_assert_not_reached();
1200*9c0476a2SJoe Komlodi             break;
1201*9c0476a2SJoe Komlodi         }
1202*9c0476a2SJoe Komlodi     }
1203*9c0476a2SJoe Komlodi }
1204*9c0476a2SJoe Komlodi 
1205*9c0476a2SJoe Komlodi static void dw_i3c_cmd_queue_push(DWI3C *s, uint32_t val)
1206*9c0476a2SJoe Komlodi {
1207*9c0476a2SJoe Komlodi     if (fifo32_is_full(&s->cmd_queue)) {
1208*9c0476a2SJoe Komlodi         g_autofree char *path = object_get_canonical_path(OBJECT(s));
1209*9c0476a2SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s: Command queue received packet when "
1210*9c0476a2SJoe Komlodi                       "already full\n", path);
1211*9c0476a2SJoe Komlodi         return;
1212*9c0476a2SJoe Komlodi     }
1213*9c0476a2SJoe Komlodi     trace_dw_i3c_cmd_queue_push(s->cfg.id, val);
1214*9c0476a2SJoe Komlodi     fifo32_push(&s->cmd_queue, val);
1215*9c0476a2SJoe Komlodi 
1216*9c0476a2SJoe Komlodi     uint8_t empty_threshold = ARRAY_FIELD_EX32(s->regs, QUEUE_THLD_CTRL,
1217*9c0476a2SJoe Komlodi                                                CMD_BUF_EMPTY_THLD);
1218*9c0476a2SJoe Komlodi     uint8_t cmd_queue_empty_loc = ARRAY_FIELD_EX32(s->regs,
1219*9c0476a2SJoe Komlodi                                                    QUEUE_STATUS_LEVEL,
1220*9c0476a2SJoe Komlodi                                                    CMD_QUEUE_EMPTY_LOC);
1221*9c0476a2SJoe Komlodi     if (cmd_queue_empty_loc) {
1222*9c0476a2SJoe Komlodi         cmd_queue_empty_loc--;
1223*9c0476a2SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, QUEUE_STATUS_LEVEL, CMD_QUEUE_EMPTY_LOC,
1224*9c0476a2SJoe Komlodi                          cmd_queue_empty_loc);
1225*9c0476a2SJoe Komlodi     }
1226*9c0476a2SJoe Komlodi     if (cmd_queue_empty_loc < empty_threshold) {
1227*9c0476a2SJoe Komlodi         ARRAY_FIELD_DP32(s->regs, INTR_STATUS, CMD_QUEUE_RDY, 0);
1228*9c0476a2SJoe Komlodi         dw_i3c_update_irq(s);
1229*9c0476a2SJoe Komlodi     }
1230*9c0476a2SJoe Komlodi }
1231*9c0476a2SJoe Komlodi 
1232*9c0476a2SJoe Komlodi static void dw_i3c_cmd_queue_port_w(DWI3C *s, uint32_t val)
1233*9c0476a2SJoe Komlodi {
1234*9c0476a2SJoe Komlodi     uint8_t cmd_attr = FIELD_EX32(val, COMMAND_QUEUE_PORT, CMD_ATTR);
1235*9c0476a2SJoe Komlodi 
1236*9c0476a2SJoe Komlodi     switch (cmd_attr) {
1237*9c0476a2SJoe Komlodi     /* If a command is received we can start executing it. */
1238*9c0476a2SJoe Komlodi     case DW_I3C_CMD_ATTR_TRANSFER_CMD:
1239*9c0476a2SJoe Komlodi     case DW_I3C_CMD_ATTR_ADDR_ASSIGN_CMD:
1240*9c0476a2SJoe Komlodi         dw_i3c_cmd_queue_push(s, val);
1241*9c0476a2SJoe Komlodi         dw_i3c_cmd_queue_execute(s);
1242*9c0476a2SJoe Komlodi         break;
1243*9c0476a2SJoe Komlodi     /* If we get an argument just push it. */
1244*9c0476a2SJoe Komlodi     case DW_I3C_CMD_ATTR_TRANSFER_ARG:
1245*9c0476a2SJoe Komlodi     case DW_I3C_CMD_ATTR_SHORT_DATA_ARG:
1246*9c0476a2SJoe Komlodi         dw_i3c_cmd_queue_push(s, val);
1247*9c0476a2SJoe Komlodi         break;
1248*9c0476a2SJoe Komlodi     default:
1249*9c0476a2SJoe Komlodi         {
1250*9c0476a2SJoe Komlodi             g_autofree char *path = object_get_canonical_path(OBJECT(s));
1251*9c0476a2SJoe Komlodi             qemu_log_mask(LOG_GUEST_ERROR, "%s: Command queue received packet "
1252*9c0476a2SJoe Komlodi                           "with unknown cmd attr 0x%x\n", path, cmd_attr);
1253*9c0476a2SJoe Komlodi         }
1254*9c0476a2SJoe Komlodi         break;
1255*9c0476a2SJoe Komlodi     }
1256*9c0476a2SJoe Komlodi }
1257*9c0476a2SJoe Komlodi 
1258c52aaabdSJoe Komlodi static void dw_i3c_write(void *opaque, hwaddr offset, uint64_t value,
1259c52aaabdSJoe Komlodi                          unsigned size)
1260c52aaabdSJoe Komlodi {
1261c52aaabdSJoe Komlodi     DWI3C *s = DW_I3C(opaque);
1262c52aaabdSJoe Komlodi     uint32_t addr = offset >> 2;
12637e5e02dbSJoe Komlodi     uint32_t val32 = (uint32_t)value;
1264c52aaabdSJoe Komlodi 
1265*9c0476a2SJoe Komlodi     trace_dw_i3c_write(s->cfg.id, offset, value);
1266c52aaabdSJoe Komlodi 
12677e5e02dbSJoe Komlodi     val32 &= ~dw_i3c_ro[addr];
1268c52aaabdSJoe Komlodi     switch (addr) {
1269c52aaabdSJoe Komlodi     case R_HW_CAPABILITY:
1270c52aaabdSJoe Komlodi     case R_RESPONSE_QUEUE_PORT:
1271c52aaabdSJoe Komlodi     case R_IBI_QUEUE_DATA:
1272c52aaabdSJoe Komlodi     case R_QUEUE_STATUS_LEVEL:
1273c52aaabdSJoe Komlodi     case R_PRESENT_STATE:
1274c52aaabdSJoe Komlodi     case R_CCC_DEVICE_STATUS:
1275c52aaabdSJoe Komlodi     case R_DEVICE_ADDR_TABLE_POINTER:
1276c52aaabdSJoe Komlodi     case R_VENDOR_SPECIFIC_REG_POINTER:
1277c52aaabdSJoe Komlodi     case R_SLV_CHAR_CTRL:
1278c52aaabdSJoe Komlodi     case R_SLV_MAX_LEN:
1279c52aaabdSJoe Komlodi     case R_MAX_READ_TURNAROUND:
1280c52aaabdSJoe Komlodi     case R_I3C_VER_ID:
1281c52aaabdSJoe Komlodi     case R_I3C_VER_TYPE:
1282c52aaabdSJoe Komlodi     case R_EXTENDED_CAPABILITY:
1283c52aaabdSJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR,
1284c52aaabdSJoe Komlodi                       "%s: write to readonly register[0x%02" HWADDR_PRIx
1285c52aaabdSJoe Komlodi                       "] = 0x%08" PRIx64 "\n",
1286c52aaabdSJoe Komlodi                       __func__, offset, value);
1287c52aaabdSJoe Komlodi         break;
1288c52aaabdSJoe Komlodi     case R_RX_TX_DATA_PORT:
1289*9c0476a2SJoe Komlodi         dw_i3c_push_tx(s, val32);
1290*9c0476a2SJoe Komlodi         break;
1291*9c0476a2SJoe Komlodi     case R_COMMAND_QUEUE_PORT:
1292*9c0476a2SJoe Komlodi         dw_i3c_cmd_queue_port_w(s, val32);
1293c52aaabdSJoe Komlodi         break;
1294c52aaabdSJoe Komlodi     case R_RESET_CTRL:
1295c52aaabdSJoe Komlodi         break;
1296a825bbb7SJoe Komlodi     case R_INTR_STATUS:
1297a825bbb7SJoe Komlodi         dw_i3c_intr_status_w(s, val32);
1298a825bbb7SJoe Komlodi         break;
1299a825bbb7SJoe Komlodi     case R_INTR_STATUS_EN:
1300a825bbb7SJoe Komlodi         dw_i3c_intr_status_en_w(s, val32);
1301a825bbb7SJoe Komlodi         break;
1302a825bbb7SJoe Komlodi     case R_INTR_SIGNAL_EN:
1303a825bbb7SJoe Komlodi         dw_i3c_intr_signal_en_w(s, val32);
1304a825bbb7SJoe Komlodi         break;
1305a825bbb7SJoe Komlodi     case R_INTR_FORCE:
1306a825bbb7SJoe Komlodi         dw_i3c_intr_force_w(s, val32);
1307a825bbb7SJoe Komlodi         break;
1308c52aaabdSJoe Komlodi     default:
13097e5e02dbSJoe Komlodi         s->regs[addr] = val32;
1310c52aaabdSJoe Komlodi         break;
1311c52aaabdSJoe Komlodi     }
1312c52aaabdSJoe Komlodi }
1313c52aaabdSJoe Komlodi 
1314c52aaabdSJoe Komlodi const VMStateDescription vmstate_dw_i3c = {
1315c52aaabdSJoe Komlodi     .name = TYPE_DW_I3C,
1316c52aaabdSJoe Komlodi     .version_id = 1,
1317c52aaabdSJoe Komlodi     .minimum_version_id = 1,
1318c52aaabdSJoe Komlodi     .fields = (VMStateField[]){
1319c52aaabdSJoe Komlodi         VMSTATE_UINT32_ARRAY(regs, DWI3C, DW_I3C_NR_REGS),
1320c52aaabdSJoe Komlodi         VMSTATE_END_OF_LIST(),
1321c52aaabdSJoe Komlodi     }
1322c52aaabdSJoe Komlodi };
1323c52aaabdSJoe Komlodi 
1324c52aaabdSJoe Komlodi static const MemoryRegionOps dw_i3c_ops = {
1325c52aaabdSJoe Komlodi     .read = dw_i3c_read,
1326c52aaabdSJoe Komlodi     .write = dw_i3c_write,
1327c52aaabdSJoe Komlodi     .endianness = DEVICE_LITTLE_ENDIAN,
1328c52aaabdSJoe Komlodi };
1329c52aaabdSJoe Komlodi 
1330c52aaabdSJoe Komlodi static void dw_i3c_reset_enter(Object *obj, ResetType type)
1331c52aaabdSJoe Komlodi {
1332c52aaabdSJoe Komlodi     DWI3C *s = DW_I3C(obj);
1333c52aaabdSJoe Komlodi 
1334c52aaabdSJoe Komlodi     memcpy(s->regs, dw_i3c_resets, sizeof(s->regs));
1335*9c0476a2SJoe Komlodi     /*
1336*9c0476a2SJoe Komlodi      * The user config for these may differ from our resets array, set them
1337*9c0476a2SJoe Komlodi      * manually.
1338*9c0476a2SJoe Komlodi      */
1339*9c0476a2SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, DEVICE_ADDR_TABLE_POINTER, ADDR,
1340*9c0476a2SJoe Komlodi                      s->cfg.dev_addr_table_pointer);
1341*9c0476a2SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, DEVICE_ADDR_TABLE_POINTER, DEPTH,
1342*9c0476a2SJoe Komlodi                      s->cfg.dev_addr_table_depth);
1343*9c0476a2SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, DEV_CHAR_TABLE_POINTER,
1344*9c0476a2SJoe Komlodi                      P_DEV_CHAR_TABLE_START_ADDR,
1345*9c0476a2SJoe Komlodi                      s->cfg.dev_char_table_pointer);
1346*9c0476a2SJoe Komlodi     ARRAY_FIELD_DP32(s->regs, DEV_CHAR_TABLE_POINTER, DEV_CHAR_TABLE_DEPTH,
1347*9c0476a2SJoe Komlodi                      s->cfg.dev_char_table_depth);
1348c52aaabdSJoe Komlodi }
1349c52aaabdSJoe Komlodi 
1350c52aaabdSJoe Komlodi static void dw_i3c_realize(DeviceState *dev, Error **errp)
1351c52aaabdSJoe Komlodi {
1352c52aaabdSJoe Komlodi     DWI3C *s = DW_I3C(dev);
1353*9c0476a2SJoe Komlodi     g_autofree char *name = g_strdup_printf(TYPE_DW_I3C ".%d", s->cfg.id);
1354c52aaabdSJoe Komlodi 
1355c52aaabdSJoe Komlodi     sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq);
1356c52aaabdSJoe Komlodi 
1357c52aaabdSJoe Komlodi     memory_region_init_io(&s->mr, OBJECT(s), &dw_i3c_ops, s, name,
1358c52aaabdSJoe Komlodi                           DW_I3C_NR_REGS << 2);
1359c52aaabdSJoe Komlodi     sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mr);
1360*9c0476a2SJoe Komlodi 
1361*9c0476a2SJoe Komlodi     fifo32_create(&s->cmd_queue, s->cfg.cmd_resp_queue_capacity_bytes);
1362*9c0476a2SJoe Komlodi     fifo32_create(&s->resp_queue, s->cfg.cmd_resp_queue_capacity_bytes);
1363*9c0476a2SJoe Komlodi     fifo32_create(&s->tx_queue, s->cfg.tx_rx_queue_capacity_bytes);
1364*9c0476a2SJoe Komlodi     fifo32_create(&s->rx_queue, s->cfg.tx_rx_queue_capacity_bytes);
1365*9c0476a2SJoe Komlodi 
1366*9c0476a2SJoe Komlodi     s->bus = i3c_init_bus(DEVICE(s), name);
1367c52aaabdSJoe Komlodi }
1368c52aaabdSJoe Komlodi 
1369c52aaabdSJoe Komlodi static const Property dw_i3c_properties[] = {
1370*9c0476a2SJoe Komlodi     DEFINE_PROP_UINT8("device-id", DWI3C, cfg.id, 0),
1371*9c0476a2SJoe Komlodi     DEFINE_PROP_UINT8("command-response-queue-capacity-bytes", DWI3C,
1372*9c0476a2SJoe Komlodi                       cfg.cmd_resp_queue_capacity_bytes, 0x10),
1373*9c0476a2SJoe Komlodi     DEFINE_PROP_UINT16("tx-rx-queue-capacity-bytes", DWI3C,
1374*9c0476a2SJoe Komlodi                       cfg.tx_rx_queue_capacity_bytes, 0x40),
1375*9c0476a2SJoe Komlodi     DEFINE_PROP_UINT8("num-addressable-devices", DWI3C,
1376*9c0476a2SJoe Komlodi                       cfg.num_addressable_devices, 8),
1377*9c0476a2SJoe Komlodi     DEFINE_PROP_UINT16("dev-addr-table-pointer", DWI3C,
1378*9c0476a2SJoe Komlodi                        cfg.dev_addr_table_pointer, 0x280),
1379*9c0476a2SJoe Komlodi     DEFINE_PROP_UINT16("dev-addr-table-depth", DWI3C,
1380*9c0476a2SJoe Komlodi                        cfg.dev_addr_table_depth, 0x08),
1381*9c0476a2SJoe Komlodi     DEFINE_PROP_UINT16("dev-char-table-pointer", DWI3C,
1382*9c0476a2SJoe Komlodi                        cfg.dev_char_table_pointer, 0x200),
1383*9c0476a2SJoe Komlodi     DEFINE_PROP_UINT16("dev-char-table-depth", DWI3C,
1384*9c0476a2SJoe Komlodi                        cfg.dev_char_table_depth, 0x20),
1385c52aaabdSJoe Komlodi };
1386c52aaabdSJoe Komlodi 
1387c52aaabdSJoe Komlodi static void dw_i3c_class_init(ObjectClass *klass, const void *data)
1388c52aaabdSJoe Komlodi {
1389c52aaabdSJoe Komlodi     DeviceClass *dc = DEVICE_CLASS(klass);
1390c52aaabdSJoe Komlodi     ResettableClass *rc = RESETTABLE_CLASS(klass);
1391c52aaabdSJoe Komlodi 
1392c52aaabdSJoe Komlodi     rc->phases.enter = dw_i3c_reset_enter;
1393c52aaabdSJoe Komlodi 
1394c52aaabdSJoe Komlodi     dc->desc = "DesignWare I3C Controller";
1395c52aaabdSJoe Komlodi     dc->realize = dw_i3c_realize;
1396c52aaabdSJoe Komlodi     dc->vmsd = &vmstate_dw_i3c;
1397c52aaabdSJoe Komlodi     device_class_set_props(dc, dw_i3c_properties);
1398c52aaabdSJoe Komlodi }
1399c52aaabdSJoe Komlodi 
1400c52aaabdSJoe Komlodi static const TypeInfo dw_i3c_info = {
1401c52aaabdSJoe Komlodi     .name = TYPE_DW_I3C,
1402c52aaabdSJoe Komlodi     .parent = TYPE_SYS_BUS_DEVICE,
1403c52aaabdSJoe Komlodi     .instance_size = sizeof(DWI3C),
1404c52aaabdSJoe Komlodi     .class_init = dw_i3c_class_init,
1405c52aaabdSJoe Komlodi };
1406c52aaabdSJoe Komlodi 
1407c52aaabdSJoe Komlodi static void dw_i3c_register_types(void)
1408c52aaabdSJoe Komlodi {
1409c52aaabdSJoe Komlodi     type_register_static(&dw_i3c_info);
1410c52aaabdSJoe Komlodi }
1411c52aaabdSJoe Komlodi 
1412c52aaabdSJoe Komlodi type_init(dw_i3c_register_types);
1413