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
225a2a5453SJoe Komlodi /*
235a2a5453SJoe Komlodi * Disable event command values. sent along with a DISEC CCC to disable certain
245a2a5453SJoe Komlodi * events on targets.
255a2a5453SJoe Komlodi */
265a2a5453SJoe Komlodi #define DISEC_HJ 0x08
275a2a5453SJoe Komlodi #define DISEC_CR 0x02
285a2a5453SJoe Komlodi #define DISEC_INT 0x01
295a2a5453SJoe 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
364a59884aaSJoe Komlodi static void dw_i3c_cmd_queue_execute(DWI3C *s);
365a59884aaSJoe Komlodi
dw_i3c_has_hdr_ts(DWI3C * s)3669c0476a2SJoe Komlodi static inline bool dw_i3c_has_hdr_ts(DWI3C *s)
3679c0476a2SJoe Komlodi {
3689c0476a2SJoe Komlodi return ARRAY_FIELD_EX32(s->regs, HW_CAPABILITY, HDR_TS);
3699c0476a2SJoe Komlodi }
3709c0476a2SJoe Komlodi
dw_i3c_has_hdr_ddr(DWI3C * s)3719c0476a2SJoe Komlodi static inline bool dw_i3c_has_hdr_ddr(DWI3C *s)
3729c0476a2SJoe Komlodi {
3739c0476a2SJoe Komlodi return ARRAY_FIELD_EX32(s->regs, HW_CAPABILITY, HDR_DDR);
3749c0476a2SJoe Komlodi }
3759c0476a2SJoe Komlodi
dw_i3c_can_transmit(DWI3C * s)3769c0476a2SJoe Komlodi static inline bool dw_i3c_can_transmit(DWI3C *s)
3779c0476a2SJoe Komlodi {
3789c0476a2SJoe Komlodi /*
3799c0476a2SJoe Komlodi * We can only transmit if we're enabled and the resume bit is cleared.
3809c0476a2SJoe Komlodi * The resume bit is set on a transaction error, and software must clear it.
3819c0476a2SJoe Komlodi */
3829c0476a2SJoe Komlodi return ARRAY_FIELD_EX32(s->regs, DEVICE_CTRL, I3C_EN) &&
3839c0476a2SJoe Komlodi !ARRAY_FIELD_EX32(s->regs, DEVICE_CTRL, I3C_RESUME);
3849c0476a2SJoe Komlodi }
3859c0476a2SJoe Komlodi
dw_i3c_ibi_slice_size(DWI3C * s)3865a2a5453SJoe Komlodi static inline uint8_t dw_i3c_ibi_slice_size(DWI3C *s)
3875a2a5453SJoe Komlodi {
3885a2a5453SJoe Komlodi uint8_t ibi_slice_size = ARRAY_FIELD_EX32(s->regs, QUEUE_THLD_CTRL,
3895a2a5453SJoe Komlodi IBI_DATA_THLD);
3905a2a5453SJoe Komlodi /* The minimum supported slice size is 4 bytes. */
3915a2a5453SJoe Komlodi if (ibi_slice_size == 0) {
3925a2a5453SJoe Komlodi ibi_slice_size = 1;
3935a2a5453SJoe Komlodi }
3945a2a5453SJoe Komlodi ibi_slice_size *= sizeof(uint32_t);
3955a2a5453SJoe Komlodi /* maximum supported size is 63 bytes. */
3965a2a5453SJoe Komlodi if (ibi_slice_size >= 64) {
3975a2a5453SJoe Komlodi ibi_slice_size = 63;
3985a2a5453SJoe Komlodi }
3995a2a5453SJoe Komlodi
4005a2a5453SJoe Komlodi return ibi_slice_size;
4015a2a5453SJoe Komlodi }
4025a2a5453SJoe Komlodi
dw_i3c_fifo_threshold_from_reg(uint8_t regval)4039c0476a2SJoe Komlodi static inline uint8_t dw_i3c_fifo_threshold_from_reg(uint8_t regval)
4049c0476a2SJoe Komlodi {
4059c0476a2SJoe Komlodi return regval = regval ? (2 << regval) : 1;
4069c0476a2SJoe Komlodi }
4079c0476a2SJoe Komlodi
dw_i3c_update_irq(DWI3C * s)408a825bbb7SJoe Komlodi static void dw_i3c_update_irq(DWI3C *s)
409a825bbb7SJoe Komlodi {
410a825bbb7SJoe Komlodi bool level = !!(s->regs[R_INTR_SIGNAL_EN] & s->regs[R_INTR_STATUS]);
411a825bbb7SJoe Komlodi qemu_set_irq(s->irq, level);
412a825bbb7SJoe Komlodi }
413a825bbb7SJoe Komlodi
dw_i3c_end_transfer(DWI3C * s,bool is_i2c)4149c0476a2SJoe Komlodi static void dw_i3c_end_transfer(DWI3C *s, bool is_i2c)
4159c0476a2SJoe Komlodi {
4169c0476a2SJoe Komlodi if (is_i2c) {
4179c0476a2SJoe Komlodi legacy_i2c_end_transfer(s->bus);
4189c0476a2SJoe Komlodi } else {
4199c0476a2SJoe Komlodi i3c_end_transfer(s->bus);
4209c0476a2SJoe Komlodi }
4219c0476a2SJoe Komlodi }
4229c0476a2SJoe Komlodi
dw_i3c_send_start(DWI3C * s,uint8_t addr,bool is_recv,bool is_i2c)4239c0476a2SJoe Komlodi static int dw_i3c_send_start(DWI3C *s, uint8_t addr, bool is_recv, bool is_i2c)
4249c0476a2SJoe Komlodi {
4259c0476a2SJoe Komlodi int ret;
4269c0476a2SJoe Komlodi
4279c0476a2SJoe Komlodi if (is_i2c) {
4289c0476a2SJoe Komlodi ret = legacy_i2c_start_transfer(s->bus, addr, is_recv);
4299c0476a2SJoe Komlodi } else {
4309c0476a2SJoe Komlodi ret = i3c_start_transfer(s->bus, addr, is_recv);
4319c0476a2SJoe Komlodi }
4329c0476a2SJoe Komlodi if (ret) {
4339c0476a2SJoe Komlodi g_autofree char *path = object_get_canonical_path(OBJECT(s));
4349c0476a2SJoe Komlodi qemu_log_mask(LOG_GUEST_ERROR, "%s: NACKed on TX with addr 0x%.2x\n",
4359c0476a2SJoe Komlodi path, addr);
4369c0476a2SJoe Komlodi ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS,
4379c0476a2SJoe Komlodi DW_I3C_TRANSFER_STATE_HALT);
4389c0476a2SJoe Komlodi ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_STATUS,
4399c0476a2SJoe Komlodi DW_I3C_TRANSFER_STATUS_HALT);
4409c0476a2SJoe Komlodi ARRAY_FIELD_DP32(s->regs, INTR_STATUS, TRANSFER_ERR, 1);
4419c0476a2SJoe Komlodi ARRAY_FIELD_DP32(s->regs, DEVICE_CTRL, I3C_RESUME, 1);
4429c0476a2SJoe Komlodi }
4439c0476a2SJoe Komlodi
4449c0476a2SJoe Komlodi return ret;
4459c0476a2SJoe Komlodi }
4469c0476a2SJoe Komlodi
dw_i3c_send(DWI3C * s,const uint8_t * data,uint32_t num_to_send,uint32_t * num_sent,bool is_i2c)4479c0476a2SJoe Komlodi static int dw_i3c_send(DWI3C *s, const uint8_t *data, uint32_t num_to_send,
4489c0476a2SJoe Komlodi uint32_t *num_sent, bool is_i2c)
4499c0476a2SJoe Komlodi {
4509c0476a2SJoe Komlodi int ret;
4519c0476a2SJoe Komlodi uint32_t i;
4529c0476a2SJoe Komlodi
4539c0476a2SJoe Komlodi *num_sent = 0;
4549c0476a2SJoe Komlodi if (is_i2c) {
4559c0476a2SJoe Komlodi /* Legacy I2C must be byte-by-byte. */
4569c0476a2SJoe Komlodi for (i = 0; i < num_to_send; i++) {
4579c0476a2SJoe Komlodi ret = legacy_i2c_send(s->bus, data[i]);
4589c0476a2SJoe Komlodi if (ret) {
4599c0476a2SJoe Komlodi break;
4609c0476a2SJoe Komlodi }
4619c0476a2SJoe Komlodi (*num_sent)++;
4629c0476a2SJoe Komlodi }
4639c0476a2SJoe Komlodi } else {
4649c0476a2SJoe Komlodi ret = i3c_send(s->bus, data, num_to_send, num_sent);
4659c0476a2SJoe Komlodi }
4669c0476a2SJoe Komlodi if (ret) {
4679c0476a2SJoe Komlodi g_autofree char *path = object_get_canonical_path(OBJECT(s));
4689c0476a2SJoe Komlodi qemu_log_mask(LOG_GUEST_ERROR, "%s: NACKed sending byte 0x%.2x\n",
4699c0476a2SJoe Komlodi path, data[*num_sent]);
4709c0476a2SJoe Komlodi ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS,
4719c0476a2SJoe Komlodi DW_I3C_TRANSFER_STATE_HALT);
4729c0476a2SJoe Komlodi ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_STATUS,
4739c0476a2SJoe Komlodi DW_I3C_TRANSFER_STATUS_HALT);
4749c0476a2SJoe Komlodi ARRAY_FIELD_DP32(s->regs, INTR_STATUS, TRANSFER_ERR, 1);
4759c0476a2SJoe Komlodi ARRAY_FIELD_DP32(s->regs, DEVICE_CTRL, I3C_RESUME, 1);
4769c0476a2SJoe Komlodi }
4779c0476a2SJoe Komlodi
4789c0476a2SJoe Komlodi trace_dw_i3c_send(s->cfg.id, *num_sent);
4799c0476a2SJoe Komlodi
4809c0476a2SJoe Komlodi return ret;
4819c0476a2SJoe Komlodi }
4829c0476a2SJoe Komlodi
dw_i3c_send_byte(DWI3C * s,uint8_t byte,bool is_i2c)4839c0476a2SJoe Komlodi static int dw_i3c_send_byte(DWI3C *s, uint8_t byte, bool is_i2c)
4849c0476a2SJoe Komlodi {
4859c0476a2SJoe Komlodi /*
4869c0476a2SJoe Komlodi * Ignored, the caller will know if we sent 0 or 1 bytes depending on if
4879c0476a2SJoe Komlodi * we were ACKed/NACKed.
4889c0476a2SJoe Komlodi */
4899c0476a2SJoe Komlodi uint32_t num_sent;
4909c0476a2SJoe Komlodi return dw_i3c_send(s, &byte, 1, &num_sent, is_i2c);
4919c0476a2SJoe Komlodi }
4929c0476a2SJoe Komlodi
dw_i3c_recv_data(DWI3C * s,bool is_i2c,uint8_t * data,uint16_t num_to_read,uint32_t * num_read)4939c0476a2SJoe Komlodi static int dw_i3c_recv_data(DWI3C *s, bool is_i2c, uint8_t *data,
4949c0476a2SJoe Komlodi uint16_t num_to_read, uint32_t *num_read)
4959c0476a2SJoe Komlodi {
4969c0476a2SJoe Komlodi int ret;
4979c0476a2SJoe Komlodi
4989c0476a2SJoe Komlodi if (is_i2c) {
4999c0476a2SJoe Komlodi for (uint16_t i = 0; i < num_to_read; i++) {
5009c0476a2SJoe Komlodi data[i] = legacy_i2c_recv(s->bus);
5019c0476a2SJoe Komlodi }
5029c0476a2SJoe Komlodi /* I2C devices can neither NACK a read, nor end transfers early. */
5039c0476a2SJoe Komlodi *num_read = num_to_read;
5049c0476a2SJoe Komlodi trace_dw_i3c_recv_data(s->cfg.id, *num_read);
5059c0476a2SJoe Komlodi return 0;
5069c0476a2SJoe Komlodi }
5079c0476a2SJoe Komlodi /* I3C devices can NACK if the controller sends an unsupported CCC. */
5089c0476a2SJoe Komlodi ret = i3c_recv(s->bus, data, num_to_read, num_read);
5099c0476a2SJoe Komlodi if (ret) {
5109c0476a2SJoe Komlodi qemu_log_mask(LOG_GUEST_ERROR, "%s: NACKed receiving byte\n",
5119c0476a2SJoe Komlodi object_get_canonical_path(OBJECT(s)));
5129c0476a2SJoe Komlodi ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS,
5139c0476a2SJoe Komlodi DW_I3C_TRANSFER_STATE_HALT);
5149c0476a2SJoe Komlodi ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_STATUS,
5159c0476a2SJoe Komlodi DW_I3C_TRANSFER_STATUS_HALT);
5169c0476a2SJoe Komlodi ARRAY_FIELD_DP32(s->regs, INTR_STATUS, TRANSFER_ERR, 1);
5179c0476a2SJoe Komlodi ARRAY_FIELD_DP32(s->regs, DEVICE_CTRL, I3C_RESUME, 1);
5189c0476a2SJoe Komlodi }
5199c0476a2SJoe Komlodi
5209c0476a2SJoe Komlodi trace_dw_i3c_recv_data(s->cfg.id, *num_read);
5219c0476a2SJoe Komlodi
5229c0476a2SJoe Komlodi return ret;
5239c0476a2SJoe Komlodi }
5249c0476a2SJoe Komlodi
dw_i3c_ctrl_w(DWI3C * s,uint32_t val)525a59884aaSJoe Komlodi static void dw_i3c_ctrl_w(DWI3C *s, uint32_t val)
526a59884aaSJoe Komlodi {
527a59884aaSJoe Komlodi /*
528a59884aaSJoe Komlodi * If the user is setting I3C_RESUME, the controller was halted.
529a59884aaSJoe Komlodi * Try and resume execution and leave the bit cleared.
530a59884aaSJoe Komlodi */
531a59884aaSJoe Komlodi if (FIELD_EX32(val, DEVICE_CTRL, I3C_RESUME)) {
532a59884aaSJoe Komlodi dw_i3c_cmd_queue_execute(s);
533a59884aaSJoe Komlodi val = FIELD_DP32(val, DEVICE_CTRL, I3C_RESUME, 0);
534a59884aaSJoe Komlodi }
535a59884aaSJoe Komlodi /*
536a59884aaSJoe Komlodi * I3C_ABORT being set sends an I3C STOP. It's cleared when the STOP is
537a59884aaSJoe Komlodi * sent.
538a59884aaSJoe Komlodi */
539a59884aaSJoe Komlodi if (FIELD_EX32(val, DEVICE_CTRL, I3C_ABORT)) {
540a59884aaSJoe Komlodi dw_i3c_end_transfer(s, /*is_i2c=*/true);
541a59884aaSJoe Komlodi dw_i3c_end_transfer(s, /*is_i2c=*/false);
542a59884aaSJoe Komlodi val = FIELD_DP32(val, DEVICE_CTRL, I3C_ABORT, 0);
543a59884aaSJoe Komlodi ARRAY_FIELD_DP32(s->regs, INTR_STATUS, TRANSFER_ABORT, 1);
544a59884aaSJoe Komlodi dw_i3c_update_irq(s);
545a59884aaSJoe Komlodi }
546a59884aaSJoe Komlodi /* Update present state. */
547a59884aaSJoe Komlodi ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS,
548a59884aaSJoe Komlodi DW_I3C_TRANSFER_STATE_IDLE);
549a59884aaSJoe Komlodi ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_STATUS,
550a59884aaSJoe Komlodi DW_I3C_TRANSFER_STATUS_IDLE);
551a59884aaSJoe Komlodi
552a59884aaSJoe Komlodi s->regs[R_DEVICE_CTRL] = val;
553a59884aaSJoe Komlodi }
554a59884aaSJoe Komlodi
dw_i3c_target_is_i2c(DWI3C * s,uint16_t offset)5559c0476a2SJoe Komlodi static inline bool dw_i3c_target_is_i2c(DWI3C *s, uint16_t offset)
5569c0476a2SJoe Komlodi {
5579c0476a2SJoe Komlodi /* / sizeof(uint32_t) because we're indexing into our 32-bit reg array. */
5589c0476a2SJoe Komlodi uint16_t dev_index = (ARRAY_FIELD_EX32(s->regs, DEVICE_ADDR_TABLE_POINTER,
5599c0476a2SJoe Komlodi ADDR) / sizeof(uint32_t)) + offset;
5609c0476a2SJoe Komlodi return FIELD_EX32(s->regs[dev_index], DEVICE_ADDR_TABLE_LOC1,
5619c0476a2SJoe Komlodi LEGACY_I2C_DEVICE);
5629c0476a2SJoe Komlodi }
5639c0476a2SJoe Komlodi
dw_i3c_target_addr(DWI3C * s,uint16_t offset)5649c0476a2SJoe Komlodi static uint8_t dw_i3c_target_addr(DWI3C *s, uint16_t offset)
5659c0476a2SJoe Komlodi {
5669c0476a2SJoe Komlodi if (offset > s->cfg.num_addressable_devices) {
5679c0476a2SJoe Komlodi g_autofree char *path = object_get_canonical_path(OBJECT(s));
5689c0476a2SJoe Komlodi qemu_log_mask(LOG_GUEST_ERROR, "%s: Device addr table offset %d out of "
5699c0476a2SJoe Komlodi "bounds\n", path, offset);
5709c0476a2SJoe Komlodi /* If we're out of bounds, return an address of 0. */
5719c0476a2SJoe Komlodi return 0;
5729c0476a2SJoe Komlodi }
5739c0476a2SJoe Komlodi
5749c0476a2SJoe Komlodi /* / sizeof(uint32_t) because we're indexing into our 32-bit reg array. */
5759c0476a2SJoe Komlodi uint16_t dev_index = (ARRAY_FIELD_EX32(s->regs, DEVICE_ADDR_TABLE_POINTER,
5769c0476a2SJoe Komlodi ADDR) / sizeof(uint32_t)) + offset;
5779c0476a2SJoe Komlodi /* I2C devices use a static address. */
5789c0476a2SJoe Komlodi if (dw_i3c_target_is_i2c(s, offset)) {
5799c0476a2SJoe Komlodi return FIELD_EX32(s->regs[dev_index], DEVICE_ADDR_TABLE_LOC1,
5809c0476a2SJoe Komlodi DEV_STATIC_ADDR);
5819c0476a2SJoe Komlodi }
5829c0476a2SJoe Komlodi return FIELD_EX32(s->regs[dev_index], DEVICE_ADDR_TABLE_LOC1,
5839c0476a2SJoe Komlodi DEV_DYNAMIC_ADDR);
5849c0476a2SJoe Komlodi }
5859c0476a2SJoe Komlodi
dw_i3c_addr_table_index_from_addr(DWI3C * s,uint8_t addr)5865a2a5453SJoe Komlodi static int dw_i3c_addr_table_index_from_addr(DWI3C *s, uint8_t addr)
5875a2a5453SJoe Komlodi {
5885a2a5453SJoe Komlodi uint8_t table_size = ARRAY_FIELD_EX32(s->regs, DEVICE_ADDR_TABLE_POINTER,
5895a2a5453SJoe Komlodi DEPTH);
5905a2a5453SJoe Komlodi for (uint8_t i = 0; i < table_size; i++) {
5915a2a5453SJoe Komlodi if (dw_i3c_target_addr(s, i) == addr) {
5925a2a5453SJoe Komlodi return i;
5935a2a5453SJoe Komlodi }
5945a2a5453SJoe Komlodi }
5955a2a5453SJoe Komlodi return -1;
5965a2a5453SJoe Komlodi }
5975a2a5453SJoe Komlodi
dw_i3c_send_disec(DWI3C * s)5985a2a5453SJoe Komlodi static void dw_i3c_send_disec(DWI3C *s)
5995a2a5453SJoe Komlodi {
6005a2a5453SJoe Komlodi uint8_t ccc = I3C_CCC_DISEC;
6015a2a5453SJoe Komlodi if (s->ibi_data.send_direct_disec) {
6025a2a5453SJoe Komlodi ccc = I3C_CCCD_DISEC;
6035a2a5453SJoe Komlodi }
6045a2a5453SJoe Komlodi
6055a2a5453SJoe Komlodi dw_i3c_send_start(s, I3C_BROADCAST, /*is_recv=*/false,
6065a2a5453SJoe Komlodi /*is_i2c=*/false);
6075a2a5453SJoe Komlodi dw_i3c_send_byte(s, ccc, /*is_i2c=*/false);
6085a2a5453SJoe Komlodi if (s->ibi_data.send_direct_disec) {
6095a2a5453SJoe Komlodi dw_i3c_send_start(s, s->ibi_data.disec_addr,
6105a2a5453SJoe Komlodi /*is_recv=*/false, /*is_i2c=*/false);
6115a2a5453SJoe Komlodi }
6125a2a5453SJoe Komlodi dw_i3c_send_byte(s, s->ibi_data.disec_byte, /*is_i2c=*/false);
6135a2a5453SJoe Komlodi }
6145a2a5453SJoe Komlodi
dw_i3c_handle_hj(DWI3C * s)6155a2a5453SJoe Komlodi static int dw_i3c_handle_hj(DWI3C *s)
6165a2a5453SJoe Komlodi {
6175a2a5453SJoe Komlodi if (ARRAY_FIELD_EX32(s->regs, IBI_QUEUE_CTRL, NOTIFY_REJECTED_HOT_JOIN)) {
6185a2a5453SJoe Komlodi s->ibi_data.notify_ibi_nack = true;
6195a2a5453SJoe Komlodi }
6205a2a5453SJoe Komlodi
6215a2a5453SJoe Komlodi bool nack_and_disable = ARRAY_FIELD_EX32(s->regs, DEVICE_CTRL,
6225a2a5453SJoe Komlodi HOT_JOIN_ACK_NACK_CTRL);
6235a2a5453SJoe Komlodi if (nack_and_disable) {
6245a2a5453SJoe Komlodi s->ibi_data.ibi_queue_status = FIELD_DP32(s->ibi_data.ibi_queue_status,
6255a2a5453SJoe Komlodi IBI_QUEUE_STATUS,
6265a2a5453SJoe Komlodi IBI_STATUS, 1);
6275a2a5453SJoe Komlodi s->ibi_data.ibi_nacked = true;
6285a2a5453SJoe Komlodi s->ibi_data.disec_byte = DISEC_HJ;
6295a2a5453SJoe Komlodi return -1;
6305a2a5453SJoe Komlodi }
6315a2a5453SJoe Komlodi return 0;
6325a2a5453SJoe Komlodi }
6335a2a5453SJoe Komlodi
dw_i3c_handle_ctlr_req(DWI3C * s,uint8_t addr)6345a2a5453SJoe Komlodi static int dw_i3c_handle_ctlr_req(DWI3C *s, uint8_t addr)
6355a2a5453SJoe Komlodi {
6365a2a5453SJoe Komlodi if (ARRAY_FIELD_EX32(s->regs, IBI_QUEUE_CTRL, NOTIFY_REJECTED_MASTER_REQ)) {
6375a2a5453SJoe Komlodi s->ibi_data.notify_ibi_nack = true;
6385a2a5453SJoe Komlodi }
6395a2a5453SJoe Komlodi
6405a2a5453SJoe Komlodi int table_offset = dw_i3c_addr_table_index_from_addr(s, addr);
6415a2a5453SJoe Komlodi /* Doesn't exist in the table, NACK it, don't DISEC. */
6425a2a5453SJoe Komlodi if (table_offset < 0) {
6435a2a5453SJoe Komlodi return -1;
6445a2a5453SJoe Komlodi }
6455a2a5453SJoe Komlodi
6465a2a5453SJoe Komlodi /* / sizeof(uint32_t) because we're indexing into our 32-bit reg array. */
6475a2a5453SJoe Komlodi table_offset += (ARRAY_FIELD_EX32(s->regs, DEVICE_ADDR_TABLE_POINTER,
6485a2a5453SJoe Komlodi ADDR) / sizeof(uint32_t));
6495a2a5453SJoe Komlodi if (FIELD_EX32(s->regs[table_offset], DEVICE_ADDR_TABLE_LOC1, MR_REJECT)) {
6505a2a5453SJoe Komlodi s->ibi_data.ibi_queue_status = FIELD_DP32(s->ibi_data.ibi_queue_status,
6515a2a5453SJoe Komlodi IBI_QUEUE_STATUS,
6525a2a5453SJoe Komlodi IBI_STATUS, 1);
6535a2a5453SJoe Komlodi s->ibi_data.ibi_nacked = true;
6545a2a5453SJoe Komlodi s->ibi_data.disec_addr = addr;
6555a2a5453SJoe Komlodi /* Tell the requester to disable controller role requests. */
6565a2a5453SJoe Komlodi s->ibi_data.disec_byte = DISEC_CR;
6575a2a5453SJoe Komlodi s->ibi_data.send_direct_disec = true;
6585a2a5453SJoe Komlodi return -1;
6595a2a5453SJoe Komlodi }
6605a2a5453SJoe Komlodi return 0;
6615a2a5453SJoe Komlodi }
6625a2a5453SJoe Komlodi
dw_i3c_handle_targ_irq(DWI3C * s,uint8_t addr)6635a2a5453SJoe Komlodi static int dw_i3c_handle_targ_irq(DWI3C *s, uint8_t addr)
6645a2a5453SJoe Komlodi {
6655a2a5453SJoe Komlodi if (ARRAY_FIELD_EX32(s->regs, IBI_QUEUE_CTRL, NOTIFY_REJECTED_SLAVE_IRQ)) {
6665a2a5453SJoe Komlodi s->ibi_data.notify_ibi_nack = true;
6675a2a5453SJoe Komlodi }
6685a2a5453SJoe Komlodi
6695a2a5453SJoe Komlodi int table_offset = dw_i3c_addr_table_index_from_addr(s, addr);
6705a2a5453SJoe Komlodi /* Doesn't exist in the table, NACK it, don't DISEC. */
6715a2a5453SJoe Komlodi if (table_offset < 0) {
6725a2a5453SJoe Komlodi return -1;
6735a2a5453SJoe Komlodi }
6745a2a5453SJoe Komlodi
6755a2a5453SJoe Komlodi /* / sizeof(uint32_t) because we're indexing into our 32-bit reg array. */
6765a2a5453SJoe Komlodi table_offset += (ARRAY_FIELD_EX32(s->regs, DEVICE_ADDR_TABLE_POINTER,
6775a2a5453SJoe Komlodi ADDR) / sizeof(uint32_t));
6785a2a5453SJoe Komlodi if (FIELD_EX32(s->regs[table_offset], DEVICE_ADDR_TABLE_LOC1, SIR_REJECT)) {
6795a2a5453SJoe Komlodi s->ibi_data.ibi_queue_status = FIELD_DP32(s->ibi_data.ibi_queue_status,
6805a2a5453SJoe Komlodi IBI_QUEUE_STATUS,
6815a2a5453SJoe Komlodi IBI_STATUS, 1);
6825a2a5453SJoe Komlodi s->ibi_data.ibi_nacked = true;
6835a2a5453SJoe Komlodi s->ibi_data.disec_addr = addr;
6845a2a5453SJoe Komlodi /* Tell the requester to disable interrupts. */
6855a2a5453SJoe Komlodi s->ibi_data.disec_byte = DISEC_INT;
6865a2a5453SJoe Komlodi s->ibi_data.send_direct_disec = true;
6875a2a5453SJoe Komlodi return -1;
6885a2a5453SJoe Komlodi }
6895a2a5453SJoe Komlodi return 0;
6905a2a5453SJoe Komlodi }
6915a2a5453SJoe Komlodi
dw_i3c_ibi_handle(I3CBus * bus,uint8_t addr,bool is_recv)6925a2a5453SJoe Komlodi static int dw_i3c_ibi_handle(I3CBus *bus, uint8_t addr, bool is_recv)
6935a2a5453SJoe Komlodi {
6945a2a5453SJoe Komlodi DWI3C *s = DW_I3C(bus->qbus.parent);
6955a2a5453SJoe Komlodi
6965a2a5453SJoe Komlodi trace_dw_i3c_ibi_handle(s->cfg.id, addr, is_recv);
6975a2a5453SJoe Komlodi s->ibi_data.ibi_queue_status = FIELD_DP32(s->ibi_data.ibi_queue_status,
6985a2a5453SJoe Komlodi IBI_QUEUE_STATUS, IBI_ID,
6995a2a5453SJoe Komlodi (addr << 1) | is_recv);
7005a2a5453SJoe Komlodi /* Is this a hot join request? */
7015a2a5453SJoe Komlodi if (addr == I3C_HJ_ADDR) {
7025a2a5453SJoe Komlodi return dw_i3c_handle_hj(s);
7035a2a5453SJoe Komlodi }
7045a2a5453SJoe Komlodi /* Is secondary controller requesting access? */
7055a2a5453SJoe Komlodi if (!is_recv) {
7065a2a5453SJoe Komlodi return dw_i3c_handle_ctlr_req(s, addr);
7075a2a5453SJoe Komlodi }
7085a2a5453SJoe Komlodi /* Is this a target IRQ? */
7095a2a5453SJoe Komlodi if (is_recv) {
7105a2a5453SJoe Komlodi return dw_i3c_handle_targ_irq(s, addr);
7115a2a5453SJoe Komlodi }
7125a2a5453SJoe Komlodi
7135a2a5453SJoe Komlodi /* At this point the IBI should have been ACKed or NACKed. */
7145a2a5453SJoe Komlodi g_assert_not_reached();
7155a2a5453SJoe Komlodi return -1;
7165a2a5453SJoe Komlodi }
7175a2a5453SJoe Komlodi
dw_i3c_ibi_recv(I3CBus * bus,uint8_t data)7185a2a5453SJoe Komlodi static int dw_i3c_ibi_recv(I3CBus *bus, uint8_t data)
7195a2a5453SJoe Komlodi {
7205a2a5453SJoe Komlodi DWI3C *s = DW_I3C(bus->qbus.parent);
7215a2a5453SJoe Komlodi if (fifo8_is_full(&s->ibi_data.ibi_intermediate_queue)) {
7225a2a5453SJoe Komlodi return -1;
7235a2a5453SJoe Komlodi }
7245a2a5453SJoe Komlodi
7255a2a5453SJoe Komlodi fifo8_push(&s->ibi_data.ibi_intermediate_queue, data);
7265a2a5453SJoe Komlodi trace_dw_i3c_ibi_recv(s->cfg.id, data);
7275a2a5453SJoe Komlodi return 0;
7285a2a5453SJoe Komlodi }
7295a2a5453SJoe Komlodi
dw_i3c_ibi_queue_push(DWI3C * s)7305a2a5453SJoe Komlodi static void dw_i3c_ibi_queue_push(DWI3C *s)
7315a2a5453SJoe Komlodi {
7325a2a5453SJoe Komlodi /* Stored value is in 32-bit chunks, convert it to byte chunks. */
7335a2a5453SJoe Komlodi uint8_t ibi_slice_size = dw_i3c_ibi_slice_size(s);
7345a2a5453SJoe Komlodi uint8_t num_slices = (fifo8_num_used(&s->ibi_data.ibi_intermediate_queue) /
7355a2a5453SJoe Komlodi ibi_slice_size) +
7365a2a5453SJoe Komlodi ((fifo8_num_used(&s->ibi_data.ibi_intermediate_queue) %
7375a2a5453SJoe Komlodi ibi_slice_size) ? 1 : 0);
7385a2a5453SJoe Komlodi uint8_t ibi_status_count = num_slices;
7395a2a5453SJoe Komlodi union {
7405a2a5453SJoe Komlodi uint8_t b[sizeof(uint32_t)];
7415a2a5453SJoe Komlodi uint32_t val32;
7425a2a5453SJoe Komlodi } ibi_data = {
7435a2a5453SJoe Komlodi .val32 = 0
7445a2a5453SJoe Komlodi };
7455a2a5453SJoe Komlodi
7465a2a5453SJoe Komlodi /* The report was suppressed, do nothing. */
7475a2a5453SJoe Komlodi if (s->ibi_data.ibi_nacked && !s->ibi_data.notify_ibi_nack) {
7485a2a5453SJoe Komlodi ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS,
7495a2a5453SJoe Komlodi DW_I3C_TRANSFER_STATE_IDLE);
7505a2a5453SJoe Komlodi ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_STATUS,
7515a2a5453SJoe Komlodi DW_I3C_TRANSFER_STATUS_IDLE);
7525a2a5453SJoe Komlodi return;
7535a2a5453SJoe Komlodi }
7545a2a5453SJoe Komlodi
7555a2a5453SJoe Komlodi /* If we don't have any slices to push, just push the status. */
7565a2a5453SJoe Komlodi if (num_slices == 0) {
7575a2a5453SJoe Komlodi s->ibi_data.ibi_queue_status =
7585a2a5453SJoe Komlodi FIELD_DP32(s->ibi_data.ibi_queue_status, IBI_QUEUE_STATUS,
7595a2a5453SJoe Komlodi LAST_STATUS, 1);
7605a2a5453SJoe Komlodi fifo32_push(&s->ibi_queue, s->ibi_data.ibi_queue_status);
7615a2a5453SJoe Komlodi ibi_status_count = 1;
7625a2a5453SJoe Komlodi }
7635a2a5453SJoe Komlodi
7645a2a5453SJoe Komlodi for (uint8_t i = 0; i < num_slices; i++) {
7655a2a5453SJoe Komlodi /* If this is the last slice, set LAST_STATUS. */
7665a2a5453SJoe Komlodi if (fifo8_num_used(&s->ibi_data.ibi_intermediate_queue) <
7675a2a5453SJoe Komlodi ibi_slice_size) {
7685a2a5453SJoe Komlodi s->ibi_data.ibi_queue_status =
7695a2a5453SJoe Komlodi FIELD_DP32(s->ibi_data.ibi_queue_status, IBI_QUEUE_STATUS,
7705a2a5453SJoe Komlodi IBI_DATA_LEN,
7715a2a5453SJoe Komlodi fifo8_num_used(&s->ibi_data.ibi_intermediate_queue));
7725a2a5453SJoe Komlodi s->ibi_data.ibi_queue_status =
7735a2a5453SJoe Komlodi FIELD_DP32(s->ibi_data.ibi_queue_status, IBI_QUEUE_STATUS,
7745a2a5453SJoe Komlodi LAST_STATUS, 1);
7755a2a5453SJoe Komlodi } else {
7765a2a5453SJoe Komlodi s->ibi_data.ibi_queue_status =
7775a2a5453SJoe Komlodi FIELD_DP32(s->ibi_data.ibi_queue_status, IBI_QUEUE_STATUS,
7785a2a5453SJoe Komlodi IBI_DATA_LEN, ibi_slice_size);
7795a2a5453SJoe Komlodi }
7805a2a5453SJoe Komlodi
7815a2a5453SJoe Komlodi /* Push the IBI status header. */
7825a2a5453SJoe Komlodi fifo32_push(&s->ibi_queue, s->ibi_data.ibi_queue_status);
7835a2a5453SJoe Komlodi /* Move each IBI byte into a 32-bit word and push it into the queue. */
7845a2a5453SJoe Komlodi for (uint8_t j = 0; j < ibi_slice_size; ++j) {
7855a2a5453SJoe Komlodi if (fifo8_is_empty(&s->ibi_data.ibi_intermediate_queue)) {
7865a2a5453SJoe Komlodi break;
7875a2a5453SJoe Komlodi }
7885a2a5453SJoe Komlodi
7895a2a5453SJoe Komlodi ibi_data.b[j & 3] = fifo8_pop(&s->ibi_data.ibi_intermediate_queue);
7905a2a5453SJoe Komlodi /* We have 32-bits, push it to the IBI FIFO. */
7915a2a5453SJoe Komlodi if ((j & 0x03) == 0x03) {
7925a2a5453SJoe Komlodi fifo32_push(&s->ibi_queue, ibi_data.val32);
7935a2a5453SJoe Komlodi ibi_data.val32 = 0;
7945a2a5453SJoe Komlodi }
7955a2a5453SJoe Komlodi }
7965a2a5453SJoe Komlodi /* If the data isn't 32-bit aligned, push the leftover bytes. */
7975a2a5453SJoe Komlodi if (ibi_slice_size & 0x03) {
7985a2a5453SJoe Komlodi fifo32_push(&s->ibi_queue, ibi_data.val32);
7995a2a5453SJoe Komlodi }
8005a2a5453SJoe Komlodi
8015a2a5453SJoe Komlodi /* Clear out the data length for the next iteration. */
8025a2a5453SJoe Komlodi s->ibi_data.ibi_queue_status = FIELD_DP32(s->ibi_data.ibi_queue_status,
8035a2a5453SJoe Komlodi IBI_QUEUE_STATUS, IBI_DATA_LEN, 0);
8045a2a5453SJoe Komlodi }
8055a2a5453SJoe Komlodi
8065a2a5453SJoe Komlodi ARRAY_FIELD_DP32(s->regs, QUEUE_STATUS_LEVEL, IBI_BUF_BLR,
8075a2a5453SJoe Komlodi fifo32_num_used(&s->ibi_queue));
8085a2a5453SJoe Komlodi ARRAY_FIELD_DP32(s->regs, QUEUE_STATUS_LEVEL, IBI_STATUS_CNT,
8095a2a5453SJoe Komlodi ibi_status_count);
8105a2a5453SJoe Komlodi /* Threshold is the register value + 1. */
8115a2a5453SJoe Komlodi uint8_t threshold = ARRAY_FIELD_EX32(s->regs, QUEUE_THLD_CTRL,
8125a2a5453SJoe Komlodi IBI_STATUS_THLD) + 1;
8135a2a5453SJoe Komlodi if (fifo32_num_used(&s->ibi_queue) >= threshold) {
8145a2a5453SJoe Komlodi ARRAY_FIELD_DP32(s->regs, INTR_STATUS, IBI_THLD, 1);
8155a2a5453SJoe Komlodi dw_i3c_update_irq(s);
8165a2a5453SJoe Komlodi }
8175a2a5453SJoe Komlodi
8185a2a5453SJoe Komlodi /* State update. */
8195a2a5453SJoe Komlodi ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS,
8205a2a5453SJoe Komlodi DW_I3C_TRANSFER_STATE_IDLE);
8215a2a5453SJoe Komlodi ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_STATUS,
8225a2a5453SJoe Komlodi DW_I3C_TRANSFER_STATUS_IDLE);
8235a2a5453SJoe Komlodi }
8245a2a5453SJoe Komlodi
dw_i3c_ibi_finish(I3CBus * bus)8255a2a5453SJoe Komlodi static int dw_i3c_ibi_finish(I3CBus *bus)
8265a2a5453SJoe Komlodi {
8275a2a5453SJoe Komlodi DWI3C *s = DW_I3C(bus->qbus.parent);
8285a2a5453SJoe Komlodi bool nack_and_disable_hj = ARRAY_FIELD_EX32(s->regs, DEVICE_CTRL,
8295a2a5453SJoe Komlodi HOT_JOIN_ACK_NACK_CTRL);
8305a2a5453SJoe Komlodi if (nack_and_disable_hj || s->ibi_data.send_direct_disec) {
8315a2a5453SJoe Komlodi dw_i3c_send_disec(s);
8325a2a5453SJoe Komlodi }
8335a2a5453SJoe Komlodi dw_i3c_ibi_queue_push(s);
8345a2a5453SJoe Komlodi
8355a2a5453SJoe Komlodi /* Clear out the intermediate values. */
8365a2a5453SJoe Komlodi s->ibi_data.ibi_queue_status = 0;
8375a2a5453SJoe Komlodi s->ibi_data.disec_addr = 0;
8385a2a5453SJoe Komlodi s->ibi_data.disec_byte = 0;
8395a2a5453SJoe Komlodi s->ibi_data.send_direct_disec = false;
8405a2a5453SJoe Komlodi s->ibi_data.notify_ibi_nack = false;
8415a2a5453SJoe Komlodi s->ibi_data.ibi_nacked = false;
8425a2a5453SJoe Komlodi
8435a2a5453SJoe Komlodi return 0;
8445a2a5453SJoe Komlodi }
8455a2a5453SJoe Komlodi
dw_i3c_intr_status_r(DWI3C * s)846a825bbb7SJoe Komlodi static uint32_t dw_i3c_intr_status_r(DWI3C *s)
847a825bbb7SJoe Komlodi {
848a825bbb7SJoe Komlodi /* Only return the status whose corresponding EN bits are set. */
849a825bbb7SJoe Komlodi return s->regs[R_INTR_STATUS] & s->regs[R_INTR_STATUS_EN];
850a825bbb7SJoe Komlodi }
851a825bbb7SJoe Komlodi
dw_i3c_intr_status_w(DWI3C * s,uint32_t val)852a825bbb7SJoe Komlodi static void dw_i3c_intr_status_w(DWI3C *s, uint32_t val)
853a825bbb7SJoe Komlodi {
854a825bbb7SJoe Komlodi /* INTR_STATUS[13:5] is w1c, other bits are RO. */
855a825bbb7SJoe Komlodi val &= 0x3fe0;
856a825bbb7SJoe Komlodi s->regs[R_INTR_STATUS] &= ~val;
857a825bbb7SJoe Komlodi
858a825bbb7SJoe Komlodi dw_i3c_update_irq(s);
859a825bbb7SJoe Komlodi }
860a825bbb7SJoe Komlodi
dw_i3c_intr_status_en_w(DWI3C * s,uint32_t val)861a825bbb7SJoe Komlodi static void dw_i3c_intr_status_en_w(DWI3C *s, uint32_t val)
862a825bbb7SJoe Komlodi {
863a825bbb7SJoe Komlodi s->regs[R_INTR_STATUS_EN] = val;
864a825bbb7SJoe Komlodi dw_i3c_update_irq(s);
865a825bbb7SJoe Komlodi }
866a825bbb7SJoe Komlodi
dw_i3c_intr_signal_en_w(DWI3C * s,uint32_t val)867a825bbb7SJoe Komlodi static void dw_i3c_intr_signal_en_w(DWI3C *s, uint32_t val)
868a825bbb7SJoe Komlodi {
869a825bbb7SJoe Komlodi s->regs[R_INTR_SIGNAL_EN] = val;
870a825bbb7SJoe Komlodi dw_i3c_update_irq(s);
871a825bbb7SJoe Komlodi }
872a825bbb7SJoe Komlodi
dw_i3c_intr_force_w(DWI3C * s,uint32_t val)873a825bbb7SJoe Komlodi static void dw_i3c_intr_force_w(DWI3C *s, uint32_t val)
874a825bbb7SJoe Komlodi {
875a825bbb7SJoe Komlodi /* INTR_FORCE is WO, just set the corresponding INTR_STATUS bits. */
876a825bbb7SJoe Komlodi s->regs[R_INTR_STATUS] = val;
877a825bbb7SJoe Komlodi dw_i3c_update_irq(s);
878a825bbb7SJoe Komlodi }
879a825bbb7SJoe Komlodi
dw_i3c_cmd_queue_reset(DWI3C * s)880*358d6be9SJoe Komlodi static void dw_i3c_cmd_queue_reset(DWI3C *s)
881*358d6be9SJoe Komlodi {
882*358d6be9SJoe Komlodi fifo32_reset(&s->cmd_queue);
883*358d6be9SJoe Komlodi
884*358d6be9SJoe Komlodi ARRAY_FIELD_DP32(s->regs, QUEUE_STATUS_LEVEL, CMD_QUEUE_EMPTY_LOC,
885*358d6be9SJoe Komlodi fifo32_num_free(&s->cmd_queue));
886*358d6be9SJoe Komlodi uint8_t empty_threshold = ARRAY_FIELD_EX32(s->regs, QUEUE_THLD_CTRL,
887*358d6be9SJoe Komlodi CMD_BUF_EMPTY_THLD);
888*358d6be9SJoe Komlodi if (fifo32_num_free(&s->cmd_queue) >= empty_threshold) {
889*358d6be9SJoe Komlodi ARRAY_FIELD_DP32(s->regs, INTR_STATUS, CMD_QUEUE_RDY, 1);
890*358d6be9SJoe Komlodi dw_i3c_update_irq(s);
891*358d6be9SJoe Komlodi };
892*358d6be9SJoe Komlodi }
893*358d6be9SJoe Komlodi
dw_i3c_resp_queue_reset(DWI3C * s)894*358d6be9SJoe Komlodi static void dw_i3c_resp_queue_reset(DWI3C *s)
895*358d6be9SJoe Komlodi {
896*358d6be9SJoe Komlodi fifo32_reset(&s->resp_queue);
897*358d6be9SJoe Komlodi
898*358d6be9SJoe Komlodi ARRAY_FIELD_DP32(s->regs, QUEUE_STATUS_LEVEL, RESP_BUF_BLR,
899*358d6be9SJoe Komlodi fifo32_num_used(&s->resp_queue));
900*358d6be9SJoe Komlodi /*
901*358d6be9SJoe Komlodi * This interrupt will always be cleared because the threshold is a minimum
902*358d6be9SJoe Komlodi * of 1 and the queue size is 0.
903*358d6be9SJoe Komlodi */
904*358d6be9SJoe Komlodi ARRAY_FIELD_DP32(s->regs, INTR_STATUS, RESP_RDY, 0);
905*358d6be9SJoe Komlodi dw_i3c_update_irq(s);
906*358d6be9SJoe Komlodi }
907*358d6be9SJoe Komlodi
dw_i3c_ibi_queue_reset(DWI3C * s)908*358d6be9SJoe Komlodi static void dw_i3c_ibi_queue_reset(DWI3C *s)
909*358d6be9SJoe Komlodi {
910*358d6be9SJoe Komlodi fifo32_reset(&s->ibi_queue);
911*358d6be9SJoe Komlodi
912*358d6be9SJoe Komlodi ARRAY_FIELD_DP32(s->regs, QUEUE_STATUS_LEVEL, IBI_BUF_BLR,
913*358d6be9SJoe Komlodi fifo32_num_used(&s->resp_queue));
914*358d6be9SJoe Komlodi /*
915*358d6be9SJoe Komlodi * This interrupt will always be cleared because the threshold is a minimum
916*358d6be9SJoe Komlodi * of 1 and the queue size is 0.
917*358d6be9SJoe Komlodi */
918*358d6be9SJoe Komlodi ARRAY_FIELD_DP32(s->regs, INTR_STATUS, IBI_THLD, 0);
919*358d6be9SJoe Komlodi dw_i3c_update_irq(s);
920*358d6be9SJoe Komlodi }
921*358d6be9SJoe Komlodi
dw_i3c_tx_queue_reset(DWI3C * s)922*358d6be9SJoe Komlodi static void dw_i3c_tx_queue_reset(DWI3C *s)
923*358d6be9SJoe Komlodi {
924*358d6be9SJoe Komlodi fifo32_reset(&s->tx_queue);
925*358d6be9SJoe Komlodi
926*358d6be9SJoe Komlodi ARRAY_FIELD_DP32(s->regs, DATA_BUFFER_STATUS_LEVEL, TX_BUF_EMPTY_LOC,
927*358d6be9SJoe Komlodi fifo32_num_free(&s->tx_queue));
928*358d6be9SJoe Komlodi /* TX buf is empty, so this interrupt will always be set. */
929*358d6be9SJoe Komlodi ARRAY_FIELD_DP32(s->regs, INTR_STATUS, TX_THLD, 1);
930*358d6be9SJoe Komlodi dw_i3c_update_irq(s);
931*358d6be9SJoe Komlodi }
932*358d6be9SJoe Komlodi
dw_i3c_rx_queue_reset(DWI3C * s)933*358d6be9SJoe Komlodi static void dw_i3c_rx_queue_reset(DWI3C *s)
934*358d6be9SJoe Komlodi {
935*358d6be9SJoe Komlodi fifo32_reset(&s->rx_queue);
936*358d6be9SJoe Komlodi
937*358d6be9SJoe Komlodi ARRAY_FIELD_DP32(s->regs, DATA_BUFFER_STATUS_LEVEL, RX_BUF_BLR,
938*358d6be9SJoe Komlodi fifo32_num_used(&s->resp_queue));
939*358d6be9SJoe Komlodi /*
940*358d6be9SJoe Komlodi * This interrupt will always be cleared because the threshold is a minimum
941*358d6be9SJoe Komlodi * of 1 and the queue size is 0.
942*358d6be9SJoe Komlodi */
943*358d6be9SJoe Komlodi ARRAY_FIELD_DP32(s->regs, INTR_STATUS, RX_THLD, 0);
944*358d6be9SJoe Komlodi dw_i3c_update_irq(s);
945*358d6be9SJoe Komlodi }
946*358d6be9SJoe Komlodi
dw_i3c_reset(DeviceState * dev)947*358d6be9SJoe Komlodi static void dw_i3c_reset(DeviceState *dev)
948*358d6be9SJoe Komlodi {
949*358d6be9SJoe Komlodi DWI3C *s = DW_I3C(dev);
950*358d6be9SJoe Komlodi trace_dw_i3c_reset(s->cfg.id);
951*358d6be9SJoe Komlodi
952*358d6be9SJoe Komlodi memcpy(s->regs, dw_i3c_resets, sizeof(s->regs));
953*358d6be9SJoe Komlodi /*
954*358d6be9SJoe Komlodi * The user config for these may differ from our resets array, set them
955*358d6be9SJoe Komlodi * manually.
956*358d6be9SJoe Komlodi */
957*358d6be9SJoe Komlodi ARRAY_FIELD_DP32(s->regs, DEVICE_ADDR_TABLE_POINTER, ADDR,
958*358d6be9SJoe Komlodi s->cfg.dev_addr_table_pointer);
959*358d6be9SJoe Komlodi ARRAY_FIELD_DP32(s->regs, DEVICE_ADDR_TABLE_POINTER, DEPTH,
960*358d6be9SJoe Komlodi s->cfg.dev_addr_table_depth);
961*358d6be9SJoe Komlodi ARRAY_FIELD_DP32(s->regs, DEV_CHAR_TABLE_POINTER,
962*358d6be9SJoe Komlodi P_DEV_CHAR_TABLE_START_ADDR,
963*358d6be9SJoe Komlodi s->cfg.dev_char_table_pointer);
964*358d6be9SJoe Komlodi ARRAY_FIELD_DP32(s->regs, DEV_CHAR_TABLE_POINTER, DEV_CHAR_TABLE_DEPTH,
965*358d6be9SJoe Komlodi s->cfg.dev_char_table_depth);
966*358d6be9SJoe Komlodi
967*358d6be9SJoe Komlodi dw_i3c_cmd_queue_reset(s);
968*358d6be9SJoe Komlodi dw_i3c_resp_queue_reset(s);
969*358d6be9SJoe Komlodi dw_i3c_ibi_queue_reset(s);
970*358d6be9SJoe Komlodi dw_i3c_tx_queue_reset(s);
971*358d6be9SJoe Komlodi dw_i3c_rx_queue_reset(s);
972*358d6be9SJoe Komlodi }
973*358d6be9SJoe Komlodi
dw_i3c_reset_ctrl_w(DWI3C * s,uint32_t val)974*358d6be9SJoe Komlodi static void dw_i3c_reset_ctrl_w(DWI3C *s, uint32_t val)
975*358d6be9SJoe Komlodi {
976*358d6be9SJoe Komlodi if (FIELD_EX32(val, RESET_CTRL, CORE_RESET)) {
977*358d6be9SJoe Komlodi dw_i3c_reset(DEVICE(s));
978*358d6be9SJoe Komlodi }
979*358d6be9SJoe Komlodi if (FIELD_EX32(val, RESET_CTRL, CMD_QUEUE_RESET)) {
980*358d6be9SJoe Komlodi dw_i3c_cmd_queue_reset(s);
981*358d6be9SJoe Komlodi }
982*358d6be9SJoe Komlodi if (FIELD_EX32(val, RESET_CTRL, RESP_QUEUE_RESET)) {
983*358d6be9SJoe Komlodi dw_i3c_resp_queue_reset(s);
984*358d6be9SJoe Komlodi }
985*358d6be9SJoe Komlodi if (FIELD_EX32(val, RESET_CTRL, TX_BUF_RESET)) {
986*358d6be9SJoe Komlodi dw_i3c_tx_queue_reset(s);
987*358d6be9SJoe Komlodi }
988*358d6be9SJoe Komlodi if (FIELD_EX32(val, RESET_CTRL, RX_BUF_RESET)) {
989*358d6be9SJoe Komlodi dw_i3c_rx_queue_reset(s);
990*358d6be9SJoe Komlodi }
991*358d6be9SJoe Komlodi if (FIELD_EX32(val, RESET_CTRL, IBI_QUEUE_RESET)) {
992*358d6be9SJoe Komlodi dw_i3c_ibi_queue_reset(s);
993*358d6be9SJoe Komlodi }
994*358d6be9SJoe Komlodi }
995*358d6be9SJoe Komlodi
dw_i3c_pop_rx(DWI3C * s)9969c0476a2SJoe Komlodi static uint32_t dw_i3c_pop_rx(DWI3C *s)
9979c0476a2SJoe Komlodi {
9989c0476a2SJoe Komlodi if (fifo32_is_empty(&s->rx_queue)) {
9999c0476a2SJoe Komlodi g_autofree char *path = object_get_canonical_path(OBJECT(s));
10009c0476a2SJoe Komlodi qemu_log_mask(LOG_GUEST_ERROR, "%s: Tried to read RX FIFO when empty\n",
10019c0476a2SJoe Komlodi path);
10029c0476a2SJoe Komlodi return 0;
10039c0476a2SJoe Komlodi }
10049c0476a2SJoe Komlodi
10059c0476a2SJoe Komlodi uint32_t val = fifo32_pop(&s->rx_queue);
10069c0476a2SJoe Komlodi ARRAY_FIELD_DP32(s->regs, DATA_BUFFER_STATUS_LEVEL, RX_BUF_BLR,
10079c0476a2SJoe Komlodi fifo32_num_used(&s->rx_queue));
10089c0476a2SJoe Komlodi
10099c0476a2SJoe Komlodi /* Threshold is 2^RX_BUF_THLD. */
10109c0476a2SJoe Komlodi uint8_t threshold = ARRAY_FIELD_EX32(s->regs, DATA_BUFFER_THLD_CTRL,
10119c0476a2SJoe Komlodi RX_BUF_THLD);
10129c0476a2SJoe Komlodi threshold = dw_i3c_fifo_threshold_from_reg(threshold);
10139c0476a2SJoe Komlodi if (fifo32_num_used(&s->rx_queue) < threshold) {
10149c0476a2SJoe Komlodi ARRAY_FIELD_DP32(s->regs, INTR_STATUS, RX_THLD, 0);
10159c0476a2SJoe Komlodi dw_i3c_update_irq(s);
10169c0476a2SJoe Komlodi }
10179c0476a2SJoe Komlodi
10189c0476a2SJoe Komlodi trace_dw_i3c_pop_rx(s->cfg.id, val);
10199c0476a2SJoe Komlodi return val;
10209c0476a2SJoe Komlodi }
10219c0476a2SJoe Komlodi
dw_i3c_ibi_queue_r(DWI3C * s)10225a2a5453SJoe Komlodi static uint32_t dw_i3c_ibi_queue_r(DWI3C *s)
10235a2a5453SJoe Komlodi {
10245a2a5453SJoe Komlodi if (fifo32_is_empty(&s->ibi_queue)) {
10255a2a5453SJoe Komlodi return 0;
10265a2a5453SJoe Komlodi }
10275a2a5453SJoe Komlodi
10285a2a5453SJoe Komlodi uint32_t val = fifo32_pop(&s->ibi_queue);
10295a2a5453SJoe Komlodi ARRAY_FIELD_DP32(s->regs, QUEUE_STATUS_LEVEL, IBI_BUF_BLR,
10305a2a5453SJoe Komlodi fifo32_num_used(&s->ibi_queue));
10315a2a5453SJoe Komlodi /* Threshold is the register value + 1. */
10325a2a5453SJoe Komlodi uint8_t threshold = ARRAY_FIELD_EX32(s->regs, QUEUE_THLD_CTRL,
10335a2a5453SJoe Komlodi IBI_STATUS_THLD) + 1;
10345a2a5453SJoe Komlodi if (fifo32_num_used(&s->ibi_queue) < threshold) {
10355a2a5453SJoe Komlodi ARRAY_FIELD_DP32(s->regs, INTR_STATUS, IBI_THLD, 0);
10365a2a5453SJoe Komlodi dw_i3c_update_irq(s);
10375a2a5453SJoe Komlodi }
10385a2a5453SJoe Komlodi return val;
10395a2a5453SJoe Komlodi }
10405a2a5453SJoe Komlodi
dw_i3c_resp_queue_port_r(DWI3C * s)10419c0476a2SJoe Komlodi static uint32_t dw_i3c_resp_queue_port_r(DWI3C *s)
10429c0476a2SJoe Komlodi {
10439c0476a2SJoe Komlodi if (fifo32_is_empty(&s->resp_queue)) {
10449c0476a2SJoe Komlodi g_autofree char *path = object_get_canonical_path(OBJECT(s));
10459c0476a2SJoe Komlodi qemu_log_mask(LOG_GUEST_ERROR, "%s: Tried to read response FIFO when "
10469c0476a2SJoe Komlodi "empty\n", path);
10479c0476a2SJoe Komlodi return 0;
10489c0476a2SJoe Komlodi }
10499c0476a2SJoe Komlodi
10509c0476a2SJoe Komlodi uint32_t val = fifo32_pop(&s->resp_queue);
10519c0476a2SJoe Komlodi ARRAY_FIELD_DP32(s->regs, QUEUE_STATUS_LEVEL, RESP_BUF_BLR,
10529c0476a2SJoe Komlodi fifo32_num_used(&s->resp_queue));
10539c0476a2SJoe Komlodi
10549c0476a2SJoe Komlodi /* Threshold is the register value + 1. */
10559c0476a2SJoe Komlodi uint8_t threshold = ARRAY_FIELD_EX32(s->regs, QUEUE_THLD_CTRL,
10569c0476a2SJoe Komlodi RESP_BUF_THLD) + 1;
10579c0476a2SJoe Komlodi if (fifo32_num_used(&s->resp_queue) < threshold) {
10589c0476a2SJoe Komlodi ARRAY_FIELD_DP32(s->regs, INTR_STATUS, RESP_RDY, 0);
10599c0476a2SJoe Komlodi dw_i3c_update_irq(s);
10609c0476a2SJoe Komlodi }
10619c0476a2SJoe Komlodi
10629c0476a2SJoe Komlodi return val;
10639c0476a2SJoe Komlodi }
10649c0476a2SJoe Komlodi
dw_i3c_read(void * opaque,hwaddr offset,unsigned size)1065c52aaabdSJoe Komlodi static uint64_t dw_i3c_read(void *opaque, hwaddr offset, unsigned size)
1066c52aaabdSJoe Komlodi {
1067c52aaabdSJoe Komlodi DWI3C *s = DW_I3C(opaque);
1068c52aaabdSJoe Komlodi uint32_t addr = offset >> 2;
1069c52aaabdSJoe Komlodi uint64_t value;
1070c52aaabdSJoe Komlodi
1071c52aaabdSJoe Komlodi switch (addr) {
10725f31322eSJoe Komlodi /* RAZ */
1073c52aaabdSJoe Komlodi case R_COMMAND_QUEUE_PORT:
10745f31322eSJoe Komlodi case R_RESET_CTRL:
10755f31322eSJoe Komlodi case R_INTR_FORCE:
1076c52aaabdSJoe Komlodi value = 0;
1077c52aaabdSJoe Komlodi break;
10785a2a5453SJoe Komlodi case R_IBI_QUEUE_DATA:
10795a2a5453SJoe Komlodi value = dw_i3c_ibi_queue_r(s);
10805a2a5453SJoe Komlodi break;
1081a825bbb7SJoe Komlodi case R_INTR_STATUS:
1082a825bbb7SJoe Komlodi value = dw_i3c_intr_status_r(s);
1083a825bbb7SJoe Komlodi break;
10849c0476a2SJoe Komlodi case R_RX_TX_DATA_PORT:
10859c0476a2SJoe Komlodi value = dw_i3c_pop_rx(s);
10869c0476a2SJoe Komlodi break;
10879c0476a2SJoe Komlodi case R_RESPONSE_QUEUE_PORT:
10889c0476a2SJoe Komlodi value = dw_i3c_resp_queue_port_r(s);
10899c0476a2SJoe Komlodi break;
1090c52aaabdSJoe Komlodi default:
1091c52aaabdSJoe Komlodi value = s->regs[addr];
1092c52aaabdSJoe Komlodi break;
1093c52aaabdSJoe Komlodi }
1094c52aaabdSJoe Komlodi
10959c0476a2SJoe Komlodi trace_dw_i3c_read(s->cfg.id, offset, value);
1096c52aaabdSJoe Komlodi
1097c52aaabdSJoe Komlodi return value;
1098c52aaabdSJoe Komlodi }
1099c52aaabdSJoe Komlodi
dw_i3c_resp_queue_push(DWI3C * s,uint8_t err,uint8_t tid,uint8_t ccc_type,uint16_t data_len)11009c0476a2SJoe Komlodi static void dw_i3c_resp_queue_push(DWI3C *s, uint8_t err, uint8_t tid,
11019c0476a2SJoe Komlodi uint8_t ccc_type, uint16_t data_len)
11029c0476a2SJoe Komlodi {
11039c0476a2SJoe Komlodi uint32_t val = 0;
11049c0476a2SJoe Komlodi val = FIELD_DP32(val, RESPONSE_QUEUE_PORT, ERR_STATUS, err);
11059c0476a2SJoe Komlodi val = FIELD_DP32(val, RESPONSE_QUEUE_PORT, TID, tid);
11069c0476a2SJoe Komlodi val = FIELD_DP32(val, RESPONSE_QUEUE_PORT, CCCT, ccc_type);
11079c0476a2SJoe Komlodi val = FIELD_DP32(val, RESPONSE_QUEUE_PORT, DL, data_len);
11089c0476a2SJoe Komlodi if (!fifo32_is_full(&s->resp_queue)) {
11099c0476a2SJoe Komlodi trace_dw_i3c_resp_queue_push(s->cfg.id, val);
11109c0476a2SJoe Komlodi fifo32_push(&s->resp_queue, val);
11119c0476a2SJoe Komlodi }
11129c0476a2SJoe Komlodi
11139c0476a2SJoe Komlodi ARRAY_FIELD_DP32(s->regs, QUEUE_STATUS_LEVEL, RESP_BUF_BLR,
11149c0476a2SJoe Komlodi fifo32_num_used(&s->resp_queue));
11159c0476a2SJoe Komlodi /* Threshold is the register value + 1. */
11169c0476a2SJoe Komlodi uint8_t threshold = ARRAY_FIELD_EX32(s->regs, QUEUE_THLD_CTRL,
11179c0476a2SJoe Komlodi RESP_BUF_THLD) + 1;
11189c0476a2SJoe Komlodi if (fifo32_num_used(&s->resp_queue) >= threshold) {
11199c0476a2SJoe Komlodi ARRAY_FIELD_DP32(s->regs, INTR_STATUS, RESP_RDY, 1);
11209c0476a2SJoe Komlodi dw_i3c_update_irq(s);
11219c0476a2SJoe Komlodi }
11229c0476a2SJoe Komlodi }
11239c0476a2SJoe Komlodi
dw_i3c_push_tx(DWI3C * s,uint32_t val)11249c0476a2SJoe Komlodi static void dw_i3c_push_tx(DWI3C *s, uint32_t val)
11259c0476a2SJoe Komlodi {
11269c0476a2SJoe Komlodi if (fifo32_is_full(&s->tx_queue)) {
11279c0476a2SJoe Komlodi qemu_log_mask(LOG_GUEST_ERROR, "%s: Tried to push to TX FIFO when "
11289c0476a2SJoe Komlodi "full\n", object_get_canonical_path(OBJECT(s)));
11299c0476a2SJoe Komlodi return;
11309c0476a2SJoe Komlodi }
11319c0476a2SJoe Komlodi
11329c0476a2SJoe Komlodi trace_dw_i3c_push_tx(s->cfg.id, val);
11339c0476a2SJoe Komlodi fifo32_push(&s->tx_queue, val);
11349c0476a2SJoe Komlodi ARRAY_FIELD_DP32(s->regs, DATA_BUFFER_STATUS_LEVEL, TX_BUF_EMPTY_LOC,
11359c0476a2SJoe Komlodi fifo32_num_free(&s->tx_queue));
11369c0476a2SJoe Komlodi
11379c0476a2SJoe Komlodi /* Threshold is 2^TX_BUF_THLD. */
11389c0476a2SJoe Komlodi uint8_t empty_threshold = ARRAY_FIELD_EX32(s->regs, DATA_BUFFER_THLD_CTRL,
11399c0476a2SJoe Komlodi TX_BUF_THLD);
11409c0476a2SJoe Komlodi empty_threshold =
11419c0476a2SJoe Komlodi dw_i3c_fifo_threshold_from_reg(empty_threshold);
11429c0476a2SJoe Komlodi if (fifo32_num_free(&s->tx_queue) < empty_threshold) {
11439c0476a2SJoe Komlodi ARRAY_FIELD_DP32(s->regs, INTR_STATUS, TX_THLD, 0);
11449c0476a2SJoe Komlodi dw_i3c_update_irq(s);
11459c0476a2SJoe Komlodi }
11469c0476a2SJoe Komlodi }
11479c0476a2SJoe Komlodi
dw_i3c_pop_tx(DWI3C * s)11489c0476a2SJoe Komlodi static uint32_t dw_i3c_pop_tx(DWI3C *s)
11499c0476a2SJoe Komlodi {
11509c0476a2SJoe Komlodi if (fifo32_is_empty(&s->tx_queue)) {
11519c0476a2SJoe Komlodi g_autofree char *path = object_get_canonical_path(OBJECT(s));
11529c0476a2SJoe Komlodi qemu_log_mask(LOG_GUEST_ERROR, "%s: Tried to pop from TX FIFO when "
11539c0476a2SJoe Komlodi "empty\n", path);
11549c0476a2SJoe Komlodi return 0;
11559c0476a2SJoe Komlodi }
11569c0476a2SJoe Komlodi
11579c0476a2SJoe Komlodi uint32_t val = fifo32_pop(&s->tx_queue);
11589c0476a2SJoe Komlodi trace_dw_i3c_pop_tx(s->cfg.id, val);
11599c0476a2SJoe Komlodi ARRAY_FIELD_DP32(s->regs, DATA_BUFFER_STATUS_LEVEL, TX_BUF_EMPTY_LOC,
11609c0476a2SJoe Komlodi fifo32_num_free(&s->tx_queue));
11619c0476a2SJoe Komlodi
11629c0476a2SJoe Komlodi /* Threshold is 2^TX_BUF_THLD. */
11639c0476a2SJoe Komlodi uint8_t empty_threshold = ARRAY_FIELD_EX32(s->regs, DATA_BUFFER_THLD_CTRL,
11649c0476a2SJoe Komlodi TX_BUF_THLD);
11659c0476a2SJoe Komlodi empty_threshold =
11669c0476a2SJoe Komlodi dw_i3c_fifo_threshold_from_reg(empty_threshold);
11679c0476a2SJoe Komlodi if (fifo32_num_free(&s->tx_queue) >= empty_threshold) {
11689c0476a2SJoe Komlodi ARRAY_FIELD_DP32(s->regs, INTR_STATUS, TX_THLD, 1);
11699c0476a2SJoe Komlodi dw_i3c_update_irq(s);
11709c0476a2SJoe Komlodi }
11719c0476a2SJoe Komlodi return val;
11729c0476a2SJoe Komlodi }
11739c0476a2SJoe Komlodi
dw_i3c_push_rx(DWI3C * s,uint32_t val)11749c0476a2SJoe Komlodi static void dw_i3c_push_rx(DWI3C *s, uint32_t val)
11759c0476a2SJoe Komlodi {
11769c0476a2SJoe Komlodi if (fifo32_is_full(&s->rx_queue)) {
11779c0476a2SJoe Komlodi g_autofree char *path = object_get_canonical_path(OBJECT(s));
11789c0476a2SJoe Komlodi qemu_log_mask(LOG_GUEST_ERROR, "%s: Tried to push to RX FIFO when "
11799c0476a2SJoe Komlodi "full\n", path);
11809c0476a2SJoe Komlodi return;
11819c0476a2SJoe Komlodi }
11829c0476a2SJoe Komlodi trace_dw_i3c_push_rx(s->cfg.id, val);
11839c0476a2SJoe Komlodi fifo32_push(&s->rx_queue, val);
11849c0476a2SJoe Komlodi
11859c0476a2SJoe Komlodi ARRAY_FIELD_DP32(s->regs, DATA_BUFFER_STATUS_LEVEL, RX_BUF_BLR,
11869c0476a2SJoe Komlodi fifo32_num_used(&s->rx_queue));
11879c0476a2SJoe Komlodi /* Threshold is 2^RX_BUF_THLD. */
11889c0476a2SJoe Komlodi uint8_t threshold = ARRAY_FIELD_EX32(s->regs, DATA_BUFFER_THLD_CTRL,
11899c0476a2SJoe Komlodi RX_BUF_THLD);
11909c0476a2SJoe Komlodi threshold = dw_i3c_fifo_threshold_from_reg(threshold);
11919c0476a2SJoe Komlodi if (fifo32_num_used(&s->rx_queue) >= threshold) {
11929c0476a2SJoe Komlodi ARRAY_FIELD_DP32(s->regs, INTR_STATUS, RX_THLD, 1);
11939c0476a2SJoe Komlodi dw_i3c_update_irq(s);
11949c0476a2SJoe Komlodi }
11959c0476a2SJoe Komlodi }
11969c0476a2SJoe Komlodi
dw_i3c_short_transfer(DWI3C * s,DWI3CTransferCmd cmd,DWI3CShortArg arg)11979c0476a2SJoe Komlodi static void dw_i3c_short_transfer(DWI3C *s, DWI3CTransferCmd cmd,
11989c0476a2SJoe Komlodi DWI3CShortArg arg)
11999c0476a2SJoe Komlodi {
12009c0476a2SJoe Komlodi uint8_t err = DW_I3C_RESP_QUEUE_ERR_NONE;
12019c0476a2SJoe Komlodi uint8_t addr = dw_i3c_target_addr(s, cmd.dev_index);
12029c0476a2SJoe Komlodi bool is_i2c = dw_i3c_target_is_i2c(s, cmd.dev_index);
12039c0476a2SJoe Komlodi uint8_t data[4]; /* Max we can send on a short transfer is 4 bytes. */
12049c0476a2SJoe Komlodi uint8_t len = 0;
12059c0476a2SJoe Komlodi uint32_t bytes_sent; /* Ignored on short transfers. */
12069c0476a2SJoe Komlodi
12079c0476a2SJoe Komlodi /* Can't do reads on a short transfer. */
12089c0476a2SJoe Komlodi if (cmd.rnw) {
12099c0476a2SJoe Komlodi g_autofree char *path = object_get_canonical_path(OBJECT(s));
12109c0476a2SJoe Komlodi qemu_log_mask(LOG_GUEST_ERROR, "%s: Cannot do a read on a short "
12119c0476a2SJoe Komlodi "transfer\n", path);
12129c0476a2SJoe Komlodi return;
12139c0476a2SJoe Komlodi }
12149c0476a2SJoe Komlodi
12159c0476a2SJoe Komlodi if (dw_i3c_send_start(s, addr, /*is_recv=*/false, is_i2c)) {
12169c0476a2SJoe Komlodi err = DW_I3C_RESP_QUEUE_ERR_I2C_NACK;
12179c0476a2SJoe Komlodi goto transfer_done;
12189c0476a2SJoe Komlodi }
12199c0476a2SJoe Komlodi
12209c0476a2SJoe Komlodi /* Are we sending a command? */
12219c0476a2SJoe Komlodi if (cmd.cp) {
12229c0476a2SJoe Komlodi data[len] = cmd.cmd;
12239c0476a2SJoe Komlodi len++;
12249c0476a2SJoe Komlodi /*
12259c0476a2SJoe Komlodi * byte0 is the defining byte for a command, and is only sent if a
12269c0476a2SJoe Komlodi * command is present and if the command has a defining byte present.
12279c0476a2SJoe Komlodi * (byte_strb & 0x01) is always treated as set by the controller, and is
12289c0476a2SJoe Komlodi * ignored.
12299c0476a2SJoe Komlodi */
12309c0476a2SJoe Komlodi if (cmd.dbp) {
12319c0476a2SJoe Komlodi data[len] += arg.byte0;
12329c0476a2SJoe Komlodi len++;
12339c0476a2SJoe Komlodi }
12349c0476a2SJoe Komlodi }
12359c0476a2SJoe Komlodi
12369c0476a2SJoe Komlodi /* Send the bytes passed in the argument. */
12379c0476a2SJoe Komlodi if (arg.byte_strb & 0x02) {
12389c0476a2SJoe Komlodi data[len] = arg.byte1;
12399c0476a2SJoe Komlodi len++;
12409c0476a2SJoe Komlodi }
12419c0476a2SJoe Komlodi if (arg.byte_strb & 0x04) {
12429c0476a2SJoe Komlodi data[len] = arg.byte2;
12439c0476a2SJoe Komlodi len++;
12449c0476a2SJoe Komlodi }
12459c0476a2SJoe Komlodi
12469c0476a2SJoe Komlodi if (dw_i3c_send(s, data, len, &bytes_sent, is_i2c)) {
12479c0476a2SJoe Komlodi err = DW_I3C_RESP_QUEUE_ERR_I2C_NACK;
12489c0476a2SJoe Komlodi } else {
12499c0476a2SJoe Komlodi /* Only go to an idle state on a successful transfer. */
12509c0476a2SJoe Komlodi ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS,
12519c0476a2SJoe Komlodi DW_I3C_TRANSFER_STATE_IDLE);
12529c0476a2SJoe Komlodi }
12539c0476a2SJoe Komlodi
12549c0476a2SJoe Komlodi transfer_done:
12559c0476a2SJoe Komlodi if (cmd.toc) {
12569c0476a2SJoe Komlodi dw_i3c_end_transfer(s, is_i2c);
12579c0476a2SJoe Komlodi }
12589c0476a2SJoe Komlodi if (cmd.roc) {
12599c0476a2SJoe Komlodi /*
12609c0476a2SJoe Komlodi * ccc_type is always 0 in controller mode, data_len is 0 in short
12619c0476a2SJoe Komlodi * transfers.
12629c0476a2SJoe Komlodi */
12639c0476a2SJoe Komlodi dw_i3c_resp_queue_push(s, err, cmd.tid, /*ccc_type=*/0,
12649c0476a2SJoe Komlodi /*data_len=*/0);
12659c0476a2SJoe Komlodi }
12669c0476a2SJoe Komlodi }
12679c0476a2SJoe Komlodi
12689c0476a2SJoe Komlodi /* Returns number of bytes transmitted. */
dw_i3c_tx(DWI3C * s,uint16_t num,bool is_i2c)12699c0476a2SJoe Komlodi static uint16_t dw_i3c_tx(DWI3C *s, uint16_t num, bool is_i2c)
12709c0476a2SJoe Komlodi {
12719c0476a2SJoe Komlodi uint16_t bytes_sent = 0;
12729c0476a2SJoe Komlodi union {
12739c0476a2SJoe Komlodi uint8_t b[sizeof(uint32_t)];
12749c0476a2SJoe Komlodi uint32_t val;
12759c0476a2SJoe Komlodi } val32;
12769c0476a2SJoe Komlodi
12779c0476a2SJoe Komlodi while (bytes_sent < num) {
12789c0476a2SJoe Komlodi val32.val = dw_i3c_pop_tx(s);
12799c0476a2SJoe Komlodi for (uint8_t i = 0; i < sizeof(val32.val); i++) {
12809c0476a2SJoe Komlodi if (dw_i3c_send_byte(s, val32.b[i], is_i2c)) {
12819c0476a2SJoe Komlodi return bytes_sent;
12829c0476a2SJoe Komlodi }
12839c0476a2SJoe Komlodi bytes_sent++;
12849c0476a2SJoe Komlodi
12859c0476a2SJoe Komlodi /* We're not sending the full 32-bits, break early. */
12869c0476a2SJoe Komlodi if (bytes_sent >= num) {
12879c0476a2SJoe Komlodi break;
12889c0476a2SJoe Komlodi }
12899c0476a2SJoe Komlodi }
12909c0476a2SJoe Komlodi }
12919c0476a2SJoe Komlodi
12929c0476a2SJoe Komlodi return bytes_sent;
12939c0476a2SJoe Komlodi }
12949c0476a2SJoe Komlodi
12959c0476a2SJoe Komlodi /* Returns number of bytes received. */
dw_i3c_rx(DWI3C * s,uint16_t num,bool is_i2c)12969c0476a2SJoe Komlodi static uint16_t dw_i3c_rx(DWI3C *s, uint16_t num, bool is_i2c)
12979c0476a2SJoe Komlodi {
12989c0476a2SJoe Komlodi /*
12999c0476a2SJoe Komlodi * Allocate a temporary buffer to read data from the target.
13009c0476a2SJoe Komlodi * Zero it and word-align it as well in case we're reading unaligned data.
13019c0476a2SJoe Komlodi */
13029c0476a2SJoe Komlodi g_autofree uint8_t *data = g_new0(uint8_t, num + (4 - (num & 0x03)));
13039c0476a2SJoe Komlodi uint32_t *data32 = (uint32_t *)data;
13049c0476a2SJoe Komlodi /*
13059c0476a2SJoe Komlodi * 32-bits since the I3C API wants a 32-bit number, even though the
13069c0476a2SJoe Komlodi * controller can only do 16-bit transfers.
13079c0476a2SJoe Komlodi */
13089c0476a2SJoe Komlodi uint32_t num_read = 0;
13099c0476a2SJoe Komlodi
13109c0476a2SJoe Komlodi /* Can NACK if the target receives an unsupported CCC. */
13119c0476a2SJoe Komlodi if (dw_i3c_recv_data(s, is_i2c, data, num, &num_read)) {
13129c0476a2SJoe Komlodi return 0;
13139c0476a2SJoe Komlodi }
13149c0476a2SJoe Komlodi
13159c0476a2SJoe Komlodi for (uint16_t i = 0; i < num_read / 4; i++) {
13169c0476a2SJoe Komlodi dw_i3c_push_rx(s, *data32);
13179c0476a2SJoe Komlodi data32++;
13189c0476a2SJoe Komlodi }
13199c0476a2SJoe Komlodi /*
13209c0476a2SJoe Komlodi * If we're pushing data that isn't 32-bit aligned, push what's left.
13219c0476a2SJoe Komlodi * It's software's responsibility to know what bits are valid in the partial
13229c0476a2SJoe Komlodi * data.
13239c0476a2SJoe Komlodi */
13249c0476a2SJoe Komlodi if (num_read & 0x03) {
13259c0476a2SJoe Komlodi dw_i3c_push_rx(s, *data32);
13269c0476a2SJoe Komlodi }
13279c0476a2SJoe Komlodi
13289c0476a2SJoe Komlodi return num_read;
13299c0476a2SJoe Komlodi }
13309c0476a2SJoe Komlodi
dw_i3c_transfer_ccc(DWI3C * s,DWI3CTransferCmd cmd,DWI3CTransferArg arg)13319c0476a2SJoe Komlodi static int dw_i3c_transfer_ccc(DWI3C *s, DWI3CTransferCmd cmd,
13329c0476a2SJoe Komlodi DWI3CTransferArg arg)
13339c0476a2SJoe Komlodi {
13349c0476a2SJoe Komlodi /* CCC start is always a write. CCCs cannot be done on I2C devices. */
13359c0476a2SJoe Komlodi if (dw_i3c_send_start(s, I3C_BROADCAST, /*is_recv=*/false,
13369c0476a2SJoe Komlodi /*is_i2c=*/false)) {
13379c0476a2SJoe Komlodi return DW_I3C_RESP_QUEUE_ERR_BROADCAST_NACK;
13389c0476a2SJoe Komlodi }
13399c0476a2SJoe Komlodi trace_dw_i3c_transfer_ccc(s->cfg.id, cmd.cmd);
13409c0476a2SJoe Komlodi if (dw_i3c_send_byte(s, cmd.cmd, /*is_i2c=*/false)) {
13419c0476a2SJoe Komlodi return DW_I3C_RESP_QUEUE_ERR_I2C_NACK;
13429c0476a2SJoe Komlodi }
13439c0476a2SJoe Komlodi
13449c0476a2SJoe Komlodi /* On a direct CCC, we do a restart and then send the target's address. */
13459c0476a2SJoe Komlodi if (CCC_IS_DIRECT(cmd.cmd)) {
13469c0476a2SJoe Komlodi bool is_recv = cmd.rnw;
13479c0476a2SJoe Komlodi uint8_t addr = dw_i3c_target_addr(s, cmd.dev_index);
13489c0476a2SJoe Komlodi if (dw_i3c_send_start(s, addr, is_recv, /*is_i2c=*/false)) {
13499c0476a2SJoe Komlodi return DW_I3C_RESP_QUEUE_ERR_BROADCAST_NACK;
13509c0476a2SJoe Komlodi }
13519c0476a2SJoe Komlodi }
13529c0476a2SJoe Komlodi
13539c0476a2SJoe Komlodi return DW_I3C_RESP_QUEUE_ERR_NONE;
13549c0476a2SJoe Komlodi }
13559c0476a2SJoe Komlodi
dw_i3c_transfer(DWI3C * s,DWI3CTransferCmd cmd,DWI3CTransferArg arg)13569c0476a2SJoe Komlodi static void dw_i3c_transfer(DWI3C *s, DWI3CTransferCmd cmd,
13579c0476a2SJoe Komlodi DWI3CTransferArg arg)
13589c0476a2SJoe Komlodi {
13599c0476a2SJoe Komlodi bool is_recv = cmd.rnw;
13609c0476a2SJoe Komlodi uint8_t err = DW_I3C_RESP_QUEUE_ERR_NONE;
13619c0476a2SJoe Komlodi uint8_t addr = dw_i3c_target_addr(s, cmd.dev_index);
13629c0476a2SJoe Komlodi bool is_i2c = dw_i3c_target_is_i2c(s, cmd.dev_index);
13639c0476a2SJoe Komlodi uint16_t bytes_transferred = 0;
13649c0476a2SJoe Komlodi
13659c0476a2SJoe Komlodi if (cmd.cp) {
13669c0476a2SJoe Komlodi /* We're sending a CCC. */
13679c0476a2SJoe Komlodi err = dw_i3c_transfer_ccc(s, cmd, arg);
13689c0476a2SJoe Komlodi if (err != DW_I3C_RESP_QUEUE_ERR_NONE) {
13699c0476a2SJoe Komlodi goto transfer_done;
13709c0476a2SJoe Komlodi }
13719c0476a2SJoe Komlodi } else {
13729c0476a2SJoe Komlodi if (ARRAY_FIELD_EX32(s->regs, DEVICE_CTRL, I3C_BROADCAST_ADDR_INC) &&
13739c0476a2SJoe Komlodi is_i2c == false) {
13749c0476a2SJoe Komlodi if (dw_i3c_send_start(s, I3C_BROADCAST,
13759c0476a2SJoe Komlodi /*is_recv=*/false, is_i2c)) {
13769c0476a2SJoe Komlodi err = DW_I3C_RESP_QUEUE_ERR_I2C_NACK;
13779c0476a2SJoe Komlodi goto transfer_done;
13789c0476a2SJoe Komlodi }
13799c0476a2SJoe Komlodi }
13809c0476a2SJoe Komlodi /* Otherwise we're doing a private transfer. */
13819c0476a2SJoe Komlodi if (dw_i3c_send_start(s, addr, is_recv, is_i2c)) {
13829c0476a2SJoe Komlodi err = DW_I3C_RESP_QUEUE_ERR_I2C_NACK;
13839c0476a2SJoe Komlodi goto transfer_done;
13849c0476a2SJoe Komlodi }
13859c0476a2SJoe Komlodi }
13869c0476a2SJoe Komlodi
13879c0476a2SJoe Komlodi if (is_recv) {
13889c0476a2SJoe Komlodi bytes_transferred = dw_i3c_rx(s, arg.data_len, is_i2c);
13899c0476a2SJoe Komlodi } else {
13909c0476a2SJoe Komlodi bytes_transferred = dw_i3c_tx(s, arg.data_len, is_i2c);
13919c0476a2SJoe Komlodi }
13929c0476a2SJoe Komlodi
13939c0476a2SJoe Komlodi ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS,
13949c0476a2SJoe Komlodi DW_I3C_TRANSFER_STATE_IDLE);
13959c0476a2SJoe Komlodi
13969c0476a2SJoe Komlodi transfer_done:
13979c0476a2SJoe Komlodi if (cmd.toc) {
13989c0476a2SJoe Komlodi dw_i3c_end_transfer(s, is_i2c);
13999c0476a2SJoe Komlodi }
14009c0476a2SJoe Komlodi if (cmd.roc) {
14019c0476a2SJoe Komlodi /*
14029c0476a2SJoe Komlodi * data_len is the number of bytes that still need to be TX'd, or the
14039c0476a2SJoe Komlodi * number of bytes RX'd.
14049c0476a2SJoe Komlodi */
14059c0476a2SJoe Komlodi uint16_t data_len = is_recv ? bytes_transferred : arg.data_len -
14069c0476a2SJoe Komlodi bytes_transferred;
14079c0476a2SJoe Komlodi /* CCCT is always 0 in controller mode. */
14089c0476a2SJoe Komlodi dw_i3c_resp_queue_push(s, err, cmd.tid, /*ccc_type=*/0,
14099c0476a2SJoe Komlodi data_len);
14109c0476a2SJoe Komlodi }
14119c0476a2SJoe Komlodi
14129c0476a2SJoe Komlodi dw_i3c_update_irq(s);
14139c0476a2SJoe Komlodi }
14149c0476a2SJoe Komlodi
dw_i3c_transfer_cmd(DWI3C * s,DWI3CTransferCmd cmd,DWI3CCmdQueueData arg)14159c0476a2SJoe Komlodi static void dw_i3c_transfer_cmd(DWI3C *s, DWI3CTransferCmd cmd,
14169c0476a2SJoe Komlodi DWI3CCmdQueueData arg)
14179c0476a2SJoe Komlodi {
14189c0476a2SJoe Komlodi uint8_t arg_attr = FIELD_EX32(arg.word, COMMAND_QUEUE_PORT, CMD_ATTR);
14199c0476a2SJoe Komlodi
14209c0476a2SJoe Komlodi ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CMD_TID, cmd.tid);
14219c0476a2SJoe Komlodi
14229c0476a2SJoe Komlodi /* User is trying to do HDR transfers, see if we can do them. */
14239c0476a2SJoe Komlodi if (cmd.speed == 0x06 && !dw_i3c_has_hdr_ddr(s)) {
14249c0476a2SJoe Komlodi g_autofree char *path = object_get_canonical_path(OBJECT(s));
14259c0476a2SJoe Komlodi qemu_log_mask(LOG_GUEST_ERROR, "%s: HDR DDR is not supported\n", path);
14269c0476a2SJoe Komlodi ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS,
14279c0476a2SJoe Komlodi DW_I3C_TRANSFER_STATE_HALT);
14289c0476a2SJoe Komlodi return;
14299c0476a2SJoe Komlodi }
14309c0476a2SJoe Komlodi if (cmd.speed == 0x05 && !dw_i3c_has_hdr_ts(s)) {
14319c0476a2SJoe Komlodi g_autofree char *path = object_get_canonical_path(OBJECT(s));
14329c0476a2SJoe Komlodi qemu_log_mask(LOG_GUEST_ERROR, "%s: HDR TS is not supported\n", path);
14339c0476a2SJoe Komlodi ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS,
14349c0476a2SJoe Komlodi DW_I3C_TRANSFER_STATE_HALT);
14359c0476a2SJoe Komlodi return;
14369c0476a2SJoe Komlodi }
14379c0476a2SJoe Komlodi
14389c0476a2SJoe Komlodi if (arg_attr == DW_I3C_CMD_ATTR_TRANSFER_ARG) {
14399c0476a2SJoe Komlodi dw_i3c_transfer(s, cmd, arg.transfer_arg);
14409c0476a2SJoe Komlodi } else if (arg_attr == DW_I3C_CMD_ATTR_SHORT_DATA_ARG) {
14419c0476a2SJoe Komlodi dw_i3c_short_transfer(s, cmd, arg.short_arg);
14429c0476a2SJoe Komlodi } else {
14439c0476a2SJoe Komlodi g_autofree char *path = object_get_canonical_path(OBJECT(s));
14449c0476a2SJoe Komlodi qemu_log_mask(LOG_GUEST_ERROR, "%s: Unknown command queue cmd_attr 0x%x"
14459c0476a2SJoe Komlodi "\n", path, arg_attr);
14469c0476a2SJoe Komlodi ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS,
14479c0476a2SJoe Komlodi DW_I3C_TRANSFER_STATE_HALT);
14489c0476a2SJoe Komlodi }
14499c0476a2SJoe Komlodi }
14509c0476a2SJoe Komlodi
dw_i3c_update_char_table(DWI3C * s,uint8_t offset,uint64_t pid,uint8_t bcr,uint8_t dcr,uint8_t addr)14519c0476a2SJoe Komlodi static void dw_i3c_update_char_table(DWI3C *s, uint8_t offset, uint64_t pid,
14529c0476a2SJoe Komlodi uint8_t bcr, uint8_t dcr, uint8_t addr)
14539c0476a2SJoe Komlodi {
14549c0476a2SJoe Komlodi if (offset > s->cfg.num_addressable_devices) {
14559c0476a2SJoe Komlodi g_autofree char *path = object_get_canonical_path(OBJECT(s));
14569c0476a2SJoe Komlodi qemu_log_mask(LOG_GUEST_ERROR, "%s: Device char table offset %d out of "
14579c0476a2SJoe Komlodi "bounds\n", path, offset);
14589c0476a2SJoe Komlodi /* If we're out of bounds, do nothing. */
14599c0476a2SJoe Komlodi return;
14609c0476a2SJoe Komlodi }
14619c0476a2SJoe Komlodi
14629c0476a2SJoe Komlodi /*
14639c0476a2SJoe Komlodi * Each device offset is 128 bits apart in the table, since each device gets
14649c0476a2SJoe Komlodi * 4 * 32-bits of entries in the table.
14659c0476a2SJoe Komlodi * / sizeof(uint32_t) because we're indexing into our 32-bit reg array.
14669c0476a2SJoe Komlodi */
14679c0476a2SJoe Komlodi uint16_t dev_index = (ARRAY_FIELD_EX32(s->regs, DEV_CHAR_TABLE_POINTER,
14689c0476a2SJoe Komlodi P_DEV_CHAR_TABLE_START_ADDR) /
14699c0476a2SJoe Komlodi sizeof(uint32_t)) +
14709c0476a2SJoe Komlodi (offset * sizeof(uint32_t));
14719c0476a2SJoe Komlodi s->regs[dev_index] = pid & 0xffffffff;
14729c0476a2SJoe Komlodi pid >>= 32;
14739c0476a2SJoe Komlodi s->regs[dev_index + 1] = FIELD_DP32(s->regs[dev_index + 1],
14749c0476a2SJoe Komlodi DEVICE_CHARACTERISTIC_TABLE_LOC2,
14759c0476a2SJoe Komlodi MSB_PID, pid);
14769c0476a2SJoe Komlodi s->regs[dev_index + 2] = FIELD_DP32(s->regs[dev_index + 2],
14779c0476a2SJoe Komlodi DEVICE_CHARACTERISTIC_TABLE_LOC3, DCR,
14789c0476a2SJoe Komlodi dcr);
14799c0476a2SJoe Komlodi s->regs[dev_index + 2] = FIELD_DP32(s->regs[dev_index + 2],
14809c0476a2SJoe Komlodi DEVICE_CHARACTERISTIC_TABLE_LOC3, BCR,
14819c0476a2SJoe Komlodi bcr);
14829c0476a2SJoe Komlodi s->regs[dev_index + 3] = FIELD_DP32(s->regs[dev_index + 3],
14839c0476a2SJoe Komlodi DEVICE_CHARACTERISTIC_TABLE_LOC4,
14849c0476a2SJoe Komlodi DEV_DYNAMIC_ADDR, addr);
14859c0476a2SJoe Komlodi
14869c0476a2SJoe Komlodi /* Increment PRESENT_DEV_CHAR_TABLE_INDEX. */
14879c0476a2SJoe Komlodi uint8_t idx = ARRAY_FIELD_EX32(s->regs, DEV_CHAR_TABLE_POINTER,
14889c0476a2SJoe Komlodi PRESENT_DEV_CHAR_TABLE_INDEX);
14899c0476a2SJoe Komlodi /* Increment and rollover. */
14909c0476a2SJoe Komlodi idx++;
14919c0476a2SJoe Komlodi if (idx >= ARRAY_FIELD_EX32(s->regs, DEV_CHAR_TABLE_POINTER,
14929c0476a2SJoe Komlodi DEV_CHAR_TABLE_DEPTH) / 4) {
14939c0476a2SJoe Komlodi idx = 0;
14949c0476a2SJoe Komlodi }
14959c0476a2SJoe Komlodi ARRAY_FIELD_DP32(s->regs, DEV_CHAR_TABLE_POINTER,
14969c0476a2SJoe Komlodi PRESENT_DEV_CHAR_TABLE_INDEX, idx);
14979c0476a2SJoe Komlodi }
14989c0476a2SJoe Komlodi
dw_i3c_addr_assign_cmd(DWI3C * s,DWI3CAddrAssignCmd cmd)14999c0476a2SJoe Komlodi static void dw_i3c_addr_assign_cmd(DWI3C *s, DWI3CAddrAssignCmd cmd)
15009c0476a2SJoe Komlodi {
15019c0476a2SJoe Komlodi uint8_t i = 0;
15029c0476a2SJoe Komlodi uint8_t err = DW_I3C_RESP_QUEUE_ERR_NONE;
15039c0476a2SJoe Komlodi
15049c0476a2SJoe Komlodi /* Tell everyone to ENTDAA. If these error, no one is on the bus. */
15059c0476a2SJoe Komlodi if (dw_i3c_send_start(s, I3C_BROADCAST, /*is_recv=*/false,
15069c0476a2SJoe Komlodi /*is_i2c=*/false)) {
15079c0476a2SJoe Komlodi err = DW_I3C_RESP_QUEUE_ERR_BROADCAST_NACK;
15089c0476a2SJoe Komlodi goto transfer_done;
15099c0476a2SJoe Komlodi }
15109c0476a2SJoe Komlodi if (dw_i3c_send_byte(s, cmd.cmd, /*is_i2c=*/false)) {
15119c0476a2SJoe Komlodi err = DW_I3C_RESP_QUEUE_ERR_BROADCAST_NACK;
15129c0476a2SJoe Komlodi goto transfer_done;
15139c0476a2SJoe Komlodi }
15149c0476a2SJoe Komlodi
15159c0476a2SJoe Komlodi /* Go through each device in the table and assign it an address. */
15169c0476a2SJoe Komlodi for (i = 0; i < cmd.dev_count; i++) {
15179c0476a2SJoe Komlodi uint8_t addr = dw_i3c_target_addr(s, cmd.dev_index + i);
15189c0476a2SJoe Komlodi union {
15199c0476a2SJoe Komlodi uint64_t pid:48;
15209c0476a2SJoe Komlodi uint8_t bcr;
15219c0476a2SJoe Komlodi uint8_t dcr;
15229c0476a2SJoe Komlodi uint32_t w[2];
15239c0476a2SJoe Komlodi uint8_t b[8];
15249c0476a2SJoe Komlodi } target_info;
15259c0476a2SJoe Komlodi
15269c0476a2SJoe Komlodi /* If this fails, there was no one left to ENTDAA. */
15279c0476a2SJoe Komlodi if (dw_i3c_send_start(s, I3C_BROADCAST, /*is_recv=*/false,
15289c0476a2SJoe Komlodi /*is_i2c=*/false)) {
15299c0476a2SJoe Komlodi err = DW_I3C_RESP_QUEUE_ERR_BROADCAST_NACK;
15309c0476a2SJoe Komlodi break;
15319c0476a2SJoe Komlodi }
15329c0476a2SJoe Komlodi
15339c0476a2SJoe Komlodi /*
15349c0476a2SJoe Komlodi * In ENTDAA, we read 8 bytes from the target, which will be the
15359c0476a2SJoe Komlodi * target's PID, BCR, and DCR. After that, we send it the dynamic
15369c0476a2SJoe Komlodi * address.
15379c0476a2SJoe Komlodi * Don't bother checking the number of bytes received, it must send 8
15389c0476a2SJoe Komlodi * bytes during ENTDAA.
15399c0476a2SJoe Komlodi */
15409c0476a2SJoe Komlodi uint32_t num_read;
15419c0476a2SJoe Komlodi if (dw_i3c_recv_data(s, /*is_i2c=*/false, target_info.b,
15429c0476a2SJoe Komlodi I3C_ENTDAA_SIZE, &num_read)) {
15439c0476a2SJoe Komlodi g_autofree char *path = object_get_canonical_path(OBJECT(s));
15449c0476a2SJoe Komlodi qemu_log_mask(LOG_GUEST_ERROR, "%s: Target NACKed ENTDAA CCC\n",
15459c0476a2SJoe Komlodi path);
15469c0476a2SJoe Komlodi err = DW_I3C_RESP_QUEUE_ERR_DAA_NACK;
15479c0476a2SJoe Komlodi goto transfer_done;
15489c0476a2SJoe Komlodi }
15499c0476a2SJoe Komlodi if (dw_i3c_send_byte(s, addr, /*is_i2c=*/false)) {
15509c0476a2SJoe Komlodi g_autofree char *path = object_get_canonical_path(OBJECT(s));
15519c0476a2SJoe Komlodi qemu_log_mask(LOG_GUEST_ERROR, "%s: Target NACKed addr 0x%.2x "
15529c0476a2SJoe Komlodi "during ENTDAA\n", path, addr);
15539c0476a2SJoe Komlodi err = DW_I3C_RESP_QUEUE_ERR_DAA_NACK;
15549c0476a2SJoe Komlodi break;
15559c0476a2SJoe Komlodi }
15569c0476a2SJoe Komlodi dw_i3c_update_char_table(s, cmd.dev_index + i,
15579c0476a2SJoe Komlodi target_info.pid, target_info.bcr,
15589c0476a2SJoe Komlodi target_info.dcr, addr);
15599c0476a2SJoe Komlodi
15609c0476a2SJoe Komlodi /* Push the PID, BCR, and DCR to the RX queue. */
15619c0476a2SJoe Komlodi dw_i3c_push_rx(s, target_info.w[0]);
15629c0476a2SJoe Komlodi dw_i3c_push_rx(s, target_info.w[1]);
15639c0476a2SJoe Komlodi }
15649c0476a2SJoe Komlodi
15659c0476a2SJoe Komlodi transfer_done:
15669c0476a2SJoe Komlodi /* Do we send a STOP? */
15679c0476a2SJoe Komlodi if (cmd.toc) {
15689c0476a2SJoe Komlodi dw_i3c_end_transfer(s, /*is_i2c=*/false);
15699c0476a2SJoe Komlodi }
15709c0476a2SJoe Komlodi /*
15719c0476a2SJoe Komlodi * For addr assign commands, the length field is the number of devices
15729c0476a2SJoe Komlodi * left to assign. CCCT is always 0 in controller mode.
15739c0476a2SJoe Komlodi */
15749c0476a2SJoe Komlodi if (cmd.roc) {
15759c0476a2SJoe Komlodi dw_i3c_resp_queue_push(s, err, cmd.tid, /*ccc_type=*/0,
15769c0476a2SJoe Komlodi cmd.dev_count - i);
15779c0476a2SJoe Komlodi }
15789c0476a2SJoe Komlodi }
15799c0476a2SJoe Komlodi
dw_i3c_cmd_queue_pop(DWI3C * s)15809c0476a2SJoe Komlodi static uint32_t dw_i3c_cmd_queue_pop(DWI3C *s)
15819c0476a2SJoe Komlodi {
15829c0476a2SJoe Komlodi if (fifo32_is_empty(&s->cmd_queue)) {
15839c0476a2SJoe Komlodi g_autofree char *path = object_get_canonical_path(OBJECT(s));
15849c0476a2SJoe Komlodi qemu_log_mask(LOG_GUEST_ERROR, "%s: Tried to dequeue command queue "
15859c0476a2SJoe Komlodi "when it was empty\n", path);
15869c0476a2SJoe Komlodi return 0;
15879c0476a2SJoe Komlodi }
15889c0476a2SJoe Komlodi uint32_t val = fifo32_pop(&s->cmd_queue);
15899c0476a2SJoe Komlodi
15909c0476a2SJoe Komlodi uint8_t empty_threshold = ARRAY_FIELD_EX32(s->regs, QUEUE_THLD_CTRL,
15919c0476a2SJoe Komlodi CMD_BUF_EMPTY_THLD);
15929c0476a2SJoe Komlodi uint8_t cmd_queue_empty_loc = ARRAY_FIELD_EX32(s->regs,
15939c0476a2SJoe Komlodi QUEUE_STATUS_LEVEL,
15949c0476a2SJoe Komlodi CMD_QUEUE_EMPTY_LOC);
15959c0476a2SJoe Komlodi cmd_queue_empty_loc++;
15969c0476a2SJoe Komlodi ARRAY_FIELD_DP32(s->regs, QUEUE_STATUS_LEVEL, CMD_QUEUE_EMPTY_LOC,
15979c0476a2SJoe Komlodi cmd_queue_empty_loc);
15989c0476a2SJoe Komlodi if (cmd_queue_empty_loc >= empty_threshold) {
15999c0476a2SJoe Komlodi ARRAY_FIELD_DP32(s->regs, INTR_STATUS, CMD_QUEUE_RDY, 1);
16009c0476a2SJoe Komlodi dw_i3c_update_irq(s);
16019c0476a2SJoe Komlodi }
16029c0476a2SJoe Komlodi
16039c0476a2SJoe Komlodi return val;
16049c0476a2SJoe Komlodi }
16059c0476a2SJoe Komlodi
dw_i3c_cmd_queue_execute(DWI3C * s)16069c0476a2SJoe Komlodi static void dw_i3c_cmd_queue_execute(DWI3C *s)
16079c0476a2SJoe Komlodi {
16089c0476a2SJoe Komlodi ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS,
16099c0476a2SJoe Komlodi DW_I3C_TRANSFER_STATE_IDLE);
16109c0476a2SJoe Komlodi if (!dw_i3c_can_transmit(s)) {
16119c0476a2SJoe Komlodi return;
16129c0476a2SJoe Komlodi }
16139c0476a2SJoe Komlodi
16149c0476a2SJoe Komlodi /*
16159c0476a2SJoe Komlodi * We only start executing when a command is passed into the FIFO.
16169c0476a2SJoe Komlodi * We expect there to be a multiple of 2 items in the queue. The first item
16179c0476a2SJoe Komlodi * should be an argument to a command, and the command should be the second
16189c0476a2SJoe Komlodi * item.
16199c0476a2SJoe Komlodi */
16209c0476a2SJoe Komlodi if (fifo32_num_used(&s->cmd_queue) & 1) {
16219c0476a2SJoe Komlodi return;
16229c0476a2SJoe Komlodi }
16239c0476a2SJoe Komlodi
16249c0476a2SJoe Komlodi while (!fifo32_is_empty(&s->cmd_queue)) {
16259c0476a2SJoe Komlodi DWI3CCmdQueueData arg;
16269c0476a2SJoe Komlodi arg.word = dw_i3c_cmd_queue_pop(s);
16279c0476a2SJoe Komlodi DWI3CCmdQueueData cmd;
16289c0476a2SJoe Komlodi cmd.word = dw_i3c_cmd_queue_pop(s);
16299c0476a2SJoe Komlodi trace_dw_i3c_cmd_queue_execute(s->cfg.id, cmd.word, arg.word);
16309c0476a2SJoe Komlodi
16319c0476a2SJoe Komlodi uint8_t cmd_attr = FIELD_EX32(cmd.word, COMMAND_QUEUE_PORT, CMD_ATTR);
16329c0476a2SJoe Komlodi switch (cmd_attr) {
16339c0476a2SJoe Komlodi case DW_I3C_CMD_ATTR_TRANSFER_CMD:
16349c0476a2SJoe Komlodi dw_i3c_transfer_cmd(s, cmd.transfer_cmd, arg);
16359c0476a2SJoe Komlodi break;
16369c0476a2SJoe Komlodi case DW_I3C_CMD_ATTR_ADDR_ASSIGN_CMD:
16379c0476a2SJoe Komlodi /* Arg is discarded for addr assign commands. */
16389c0476a2SJoe Komlodi dw_i3c_addr_assign_cmd(s, cmd.addr_assign_cmd);
16399c0476a2SJoe Komlodi break;
16409c0476a2SJoe Komlodi case DW_I3C_CMD_ATTR_TRANSFER_ARG:
16419c0476a2SJoe Komlodi case DW_I3C_CMD_ATTR_SHORT_DATA_ARG:
16429c0476a2SJoe Komlodi {
16439c0476a2SJoe Komlodi g_autofree char *path = object_get_canonical_path(OBJECT(s));
16449c0476a2SJoe Komlodi qemu_log_mask(LOG_GUEST_ERROR, "%s: Command queue received "
16459c0476a2SJoe Komlodi "argument packet when it expected a command "
16469c0476a2SJoe Komlodi "packet\n", path);
16479c0476a2SJoe Komlodi }
16489c0476a2SJoe Komlodi break;
16499c0476a2SJoe Komlodi default:
16509c0476a2SJoe Komlodi /*
16519c0476a2SJoe Komlodi * The caller's check before queueing an item should prevent this
16529c0476a2SJoe Komlodi * from happening.
16539c0476a2SJoe Komlodi */
16549c0476a2SJoe Komlodi g_assert_not_reached();
16559c0476a2SJoe Komlodi break;
16569c0476a2SJoe Komlodi }
16579c0476a2SJoe Komlodi }
16589c0476a2SJoe Komlodi }
16599c0476a2SJoe Komlodi
dw_i3c_cmd_queue_push(DWI3C * s,uint32_t val)16609c0476a2SJoe Komlodi static void dw_i3c_cmd_queue_push(DWI3C *s, uint32_t val)
16619c0476a2SJoe Komlodi {
16629c0476a2SJoe Komlodi if (fifo32_is_full(&s->cmd_queue)) {
16639c0476a2SJoe Komlodi g_autofree char *path = object_get_canonical_path(OBJECT(s));
16649c0476a2SJoe Komlodi qemu_log_mask(LOG_GUEST_ERROR, "%s: Command queue received packet when "
16659c0476a2SJoe Komlodi "already full\n", path);
16669c0476a2SJoe Komlodi return;
16679c0476a2SJoe Komlodi }
16689c0476a2SJoe Komlodi trace_dw_i3c_cmd_queue_push(s->cfg.id, val);
16699c0476a2SJoe Komlodi fifo32_push(&s->cmd_queue, val);
16709c0476a2SJoe Komlodi
16719c0476a2SJoe Komlodi uint8_t empty_threshold = ARRAY_FIELD_EX32(s->regs, QUEUE_THLD_CTRL,
16729c0476a2SJoe Komlodi CMD_BUF_EMPTY_THLD);
16739c0476a2SJoe Komlodi uint8_t cmd_queue_empty_loc = ARRAY_FIELD_EX32(s->regs,
16749c0476a2SJoe Komlodi QUEUE_STATUS_LEVEL,
16759c0476a2SJoe Komlodi CMD_QUEUE_EMPTY_LOC);
16769c0476a2SJoe Komlodi if (cmd_queue_empty_loc) {
16779c0476a2SJoe Komlodi cmd_queue_empty_loc--;
16789c0476a2SJoe Komlodi ARRAY_FIELD_DP32(s->regs, QUEUE_STATUS_LEVEL, CMD_QUEUE_EMPTY_LOC,
16799c0476a2SJoe Komlodi cmd_queue_empty_loc);
16809c0476a2SJoe Komlodi }
16819c0476a2SJoe Komlodi if (cmd_queue_empty_loc < empty_threshold) {
16829c0476a2SJoe Komlodi ARRAY_FIELD_DP32(s->regs, INTR_STATUS, CMD_QUEUE_RDY, 0);
16839c0476a2SJoe Komlodi dw_i3c_update_irq(s);
16849c0476a2SJoe Komlodi }
16859c0476a2SJoe Komlodi }
16869c0476a2SJoe Komlodi
dw_i3c_cmd_queue_port_w(DWI3C * s,uint32_t val)16879c0476a2SJoe Komlodi static void dw_i3c_cmd_queue_port_w(DWI3C *s, uint32_t val)
16889c0476a2SJoe Komlodi {
16899c0476a2SJoe Komlodi uint8_t cmd_attr = FIELD_EX32(val, COMMAND_QUEUE_PORT, CMD_ATTR);
16909c0476a2SJoe Komlodi
16919c0476a2SJoe Komlodi switch (cmd_attr) {
16929c0476a2SJoe Komlodi /* If a command is received we can start executing it. */
16939c0476a2SJoe Komlodi case DW_I3C_CMD_ATTR_TRANSFER_CMD:
16949c0476a2SJoe Komlodi case DW_I3C_CMD_ATTR_ADDR_ASSIGN_CMD:
16959c0476a2SJoe Komlodi dw_i3c_cmd_queue_push(s, val);
16969c0476a2SJoe Komlodi dw_i3c_cmd_queue_execute(s);
16979c0476a2SJoe Komlodi break;
16989c0476a2SJoe Komlodi /* If we get an argument just push it. */
16999c0476a2SJoe Komlodi case DW_I3C_CMD_ATTR_TRANSFER_ARG:
17009c0476a2SJoe Komlodi case DW_I3C_CMD_ATTR_SHORT_DATA_ARG:
17019c0476a2SJoe Komlodi dw_i3c_cmd_queue_push(s, val);
17029c0476a2SJoe Komlodi break;
17039c0476a2SJoe Komlodi default:
17049c0476a2SJoe Komlodi {
17059c0476a2SJoe Komlodi g_autofree char *path = object_get_canonical_path(OBJECT(s));
17069c0476a2SJoe Komlodi qemu_log_mask(LOG_GUEST_ERROR, "%s: Command queue received packet "
17079c0476a2SJoe Komlodi "with unknown cmd attr 0x%x\n", path, cmd_attr);
17089c0476a2SJoe Komlodi }
17099c0476a2SJoe Komlodi break;
17109c0476a2SJoe Komlodi }
17119c0476a2SJoe Komlodi }
17129c0476a2SJoe Komlodi
dw_i3c_write(void * opaque,hwaddr offset,uint64_t value,unsigned size)1713c52aaabdSJoe Komlodi static void dw_i3c_write(void *opaque, hwaddr offset, uint64_t value,
1714c52aaabdSJoe Komlodi unsigned size)
1715c52aaabdSJoe Komlodi {
1716c52aaabdSJoe Komlodi DWI3C *s = DW_I3C(opaque);
1717c52aaabdSJoe Komlodi uint32_t addr = offset >> 2;
17187e5e02dbSJoe Komlodi uint32_t val32 = (uint32_t)value;
1719c52aaabdSJoe Komlodi
17209c0476a2SJoe Komlodi trace_dw_i3c_write(s->cfg.id, offset, value);
1721c52aaabdSJoe Komlodi
17227e5e02dbSJoe Komlodi val32 &= ~dw_i3c_ro[addr];
1723c52aaabdSJoe Komlodi switch (addr) {
1724c52aaabdSJoe Komlodi case R_HW_CAPABILITY:
1725c52aaabdSJoe Komlodi case R_RESPONSE_QUEUE_PORT:
1726c52aaabdSJoe Komlodi case R_IBI_QUEUE_DATA:
1727c52aaabdSJoe Komlodi case R_QUEUE_STATUS_LEVEL:
1728c52aaabdSJoe Komlodi case R_PRESENT_STATE:
1729c52aaabdSJoe Komlodi case R_CCC_DEVICE_STATUS:
1730c52aaabdSJoe Komlodi case R_DEVICE_ADDR_TABLE_POINTER:
1731c52aaabdSJoe Komlodi case R_VENDOR_SPECIFIC_REG_POINTER:
1732c52aaabdSJoe Komlodi case R_SLV_CHAR_CTRL:
1733c52aaabdSJoe Komlodi case R_SLV_MAX_LEN:
1734c52aaabdSJoe Komlodi case R_MAX_READ_TURNAROUND:
1735c52aaabdSJoe Komlodi case R_I3C_VER_ID:
1736c52aaabdSJoe Komlodi case R_I3C_VER_TYPE:
1737c52aaabdSJoe Komlodi case R_EXTENDED_CAPABILITY:
1738c52aaabdSJoe Komlodi qemu_log_mask(LOG_GUEST_ERROR,
1739c52aaabdSJoe Komlodi "%s: write to readonly register[0x%02" HWADDR_PRIx
1740c52aaabdSJoe Komlodi "] = 0x%08" PRIx64 "\n",
1741c52aaabdSJoe Komlodi __func__, offset, value);
1742c52aaabdSJoe Komlodi break;
1743a59884aaSJoe Komlodi case R_DEVICE_CTRL:
1744a59884aaSJoe Komlodi dw_i3c_ctrl_w(s, val32);
1745a59884aaSJoe Komlodi break;
1746c52aaabdSJoe Komlodi case R_RX_TX_DATA_PORT:
17479c0476a2SJoe Komlodi dw_i3c_push_tx(s, val32);
17489c0476a2SJoe Komlodi break;
17499c0476a2SJoe Komlodi case R_COMMAND_QUEUE_PORT:
17509c0476a2SJoe Komlodi dw_i3c_cmd_queue_port_w(s, val32);
1751c52aaabdSJoe Komlodi break;
1752c52aaabdSJoe Komlodi case R_RESET_CTRL:
1753*358d6be9SJoe Komlodi dw_i3c_reset_ctrl_w(s, val32);
1754c52aaabdSJoe Komlodi break;
1755a825bbb7SJoe Komlodi case R_INTR_STATUS:
1756a825bbb7SJoe Komlodi dw_i3c_intr_status_w(s, val32);
1757a825bbb7SJoe Komlodi break;
1758a825bbb7SJoe Komlodi case R_INTR_STATUS_EN:
1759a825bbb7SJoe Komlodi dw_i3c_intr_status_en_w(s, val32);
1760a825bbb7SJoe Komlodi break;
1761a825bbb7SJoe Komlodi case R_INTR_SIGNAL_EN:
1762a825bbb7SJoe Komlodi dw_i3c_intr_signal_en_w(s, val32);
1763a825bbb7SJoe Komlodi break;
1764a825bbb7SJoe Komlodi case R_INTR_FORCE:
1765a825bbb7SJoe Komlodi dw_i3c_intr_force_w(s, val32);
1766a825bbb7SJoe Komlodi break;
1767c52aaabdSJoe Komlodi default:
17687e5e02dbSJoe Komlodi s->regs[addr] = val32;
1769c52aaabdSJoe Komlodi break;
1770c52aaabdSJoe Komlodi }
1771c52aaabdSJoe Komlodi }
1772c52aaabdSJoe Komlodi
1773c52aaabdSJoe Komlodi const VMStateDescription vmstate_dw_i3c = {
1774c52aaabdSJoe Komlodi .name = TYPE_DW_I3C,
1775c52aaabdSJoe Komlodi .version_id = 1,
1776c52aaabdSJoe Komlodi .minimum_version_id = 1,
1777c52aaabdSJoe Komlodi .fields = (VMStateField[]){
1778c52aaabdSJoe Komlodi VMSTATE_UINT32_ARRAY(regs, DWI3C, DW_I3C_NR_REGS),
1779c52aaabdSJoe Komlodi VMSTATE_END_OF_LIST(),
1780c52aaabdSJoe Komlodi }
1781c52aaabdSJoe Komlodi };
1782c52aaabdSJoe Komlodi
1783c52aaabdSJoe Komlodi static const MemoryRegionOps dw_i3c_ops = {
1784c52aaabdSJoe Komlodi .read = dw_i3c_read,
1785c52aaabdSJoe Komlodi .write = dw_i3c_write,
1786c52aaabdSJoe Komlodi .endianness = DEVICE_LITTLE_ENDIAN,
1787c52aaabdSJoe Komlodi };
1788c52aaabdSJoe Komlodi
dw_i3c_reset_enter(Object * obj,ResetType type)1789c52aaabdSJoe Komlodi static void dw_i3c_reset_enter(Object *obj, ResetType type)
1790c52aaabdSJoe Komlodi {
1791c52aaabdSJoe Komlodi DWI3C *s = DW_I3C(obj);
1792c52aaabdSJoe Komlodi
1793c52aaabdSJoe Komlodi memcpy(s->regs, dw_i3c_resets, sizeof(s->regs));
17949c0476a2SJoe Komlodi /*
17959c0476a2SJoe Komlodi * The user config for these may differ from our resets array, set them
17969c0476a2SJoe Komlodi * manually.
17979c0476a2SJoe Komlodi */
17989c0476a2SJoe Komlodi ARRAY_FIELD_DP32(s->regs, DEVICE_ADDR_TABLE_POINTER, ADDR,
17999c0476a2SJoe Komlodi s->cfg.dev_addr_table_pointer);
18009c0476a2SJoe Komlodi ARRAY_FIELD_DP32(s->regs, DEVICE_ADDR_TABLE_POINTER, DEPTH,
18019c0476a2SJoe Komlodi s->cfg.dev_addr_table_depth);
18029c0476a2SJoe Komlodi ARRAY_FIELD_DP32(s->regs, DEV_CHAR_TABLE_POINTER,
18039c0476a2SJoe Komlodi P_DEV_CHAR_TABLE_START_ADDR,
18049c0476a2SJoe Komlodi s->cfg.dev_char_table_pointer);
18059c0476a2SJoe Komlodi ARRAY_FIELD_DP32(s->regs, DEV_CHAR_TABLE_POINTER, DEV_CHAR_TABLE_DEPTH,
18069c0476a2SJoe Komlodi s->cfg.dev_char_table_depth);
1807c52aaabdSJoe Komlodi }
1808c52aaabdSJoe Komlodi
dw_i3c_realize(DeviceState * dev,Error ** errp)1809c52aaabdSJoe Komlodi static void dw_i3c_realize(DeviceState *dev, Error **errp)
1810c52aaabdSJoe Komlodi {
1811c52aaabdSJoe Komlodi DWI3C *s = DW_I3C(dev);
18129c0476a2SJoe Komlodi g_autofree char *name = g_strdup_printf(TYPE_DW_I3C ".%d", s->cfg.id);
1813c52aaabdSJoe Komlodi
1814c52aaabdSJoe Komlodi sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq);
1815c52aaabdSJoe Komlodi
1816c52aaabdSJoe Komlodi memory_region_init_io(&s->mr, OBJECT(s), &dw_i3c_ops, s, name,
1817c52aaabdSJoe Komlodi DW_I3C_NR_REGS << 2);
1818c52aaabdSJoe Komlodi sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mr);
18199c0476a2SJoe Komlodi
18209c0476a2SJoe Komlodi fifo32_create(&s->cmd_queue, s->cfg.cmd_resp_queue_capacity_bytes);
18219c0476a2SJoe Komlodi fifo32_create(&s->resp_queue, s->cfg.cmd_resp_queue_capacity_bytes);
18229c0476a2SJoe Komlodi fifo32_create(&s->tx_queue, s->cfg.tx_rx_queue_capacity_bytes);
18239c0476a2SJoe Komlodi fifo32_create(&s->rx_queue, s->cfg.tx_rx_queue_capacity_bytes);
18245a2a5453SJoe Komlodi fifo32_create(&s->ibi_queue, s->cfg.ibi_queue_capacity_bytes);
18255a2a5453SJoe Komlodi /* Arbitrarily large enough to not be an issue. */
18265a2a5453SJoe Komlodi fifo8_create(&s->ibi_data.ibi_intermediate_queue,
18275a2a5453SJoe Komlodi s->cfg.ibi_queue_capacity_bytes * 8);
18289c0476a2SJoe Komlodi
18299c0476a2SJoe Komlodi s->bus = i3c_init_bus(DEVICE(s), name);
18305a2a5453SJoe Komlodi I3CBusClass *bc = I3C_BUS_GET_CLASS(s->bus);
18315a2a5453SJoe Komlodi bc->ibi_handle = dw_i3c_ibi_handle;
18325a2a5453SJoe Komlodi bc->ibi_recv = dw_i3c_ibi_recv;
18335a2a5453SJoe Komlodi bc->ibi_finish = dw_i3c_ibi_finish;
1834c52aaabdSJoe Komlodi }
1835c52aaabdSJoe Komlodi
1836c52aaabdSJoe Komlodi static const Property dw_i3c_properties[] = {
18379c0476a2SJoe Komlodi DEFINE_PROP_UINT8("device-id", DWI3C, cfg.id, 0),
18389c0476a2SJoe Komlodi DEFINE_PROP_UINT8("command-response-queue-capacity-bytes", DWI3C,
18399c0476a2SJoe Komlodi cfg.cmd_resp_queue_capacity_bytes, 0x10),
18409c0476a2SJoe Komlodi DEFINE_PROP_UINT16("tx-rx-queue-capacity-bytes", DWI3C,
18419c0476a2SJoe Komlodi cfg.tx_rx_queue_capacity_bytes, 0x40),
18425a2a5453SJoe Komlodi DEFINE_PROP_UINT8("ibi-queue-capacity-bytes", DWI3C,
18435a2a5453SJoe Komlodi cfg.ibi_queue_capacity_bytes, 0x10),
18449c0476a2SJoe Komlodi DEFINE_PROP_UINT8("num-addressable-devices", DWI3C,
18459c0476a2SJoe Komlodi cfg.num_addressable_devices, 8),
18469c0476a2SJoe Komlodi DEFINE_PROP_UINT16("dev-addr-table-pointer", DWI3C,
18479c0476a2SJoe Komlodi cfg.dev_addr_table_pointer, 0x280),
18489c0476a2SJoe Komlodi DEFINE_PROP_UINT16("dev-addr-table-depth", DWI3C,
18499c0476a2SJoe Komlodi cfg.dev_addr_table_depth, 0x08),
18509c0476a2SJoe Komlodi DEFINE_PROP_UINT16("dev-char-table-pointer", DWI3C,
18519c0476a2SJoe Komlodi cfg.dev_char_table_pointer, 0x200),
18529c0476a2SJoe Komlodi DEFINE_PROP_UINT16("dev-char-table-depth", DWI3C,
18539c0476a2SJoe Komlodi cfg.dev_char_table_depth, 0x20),
1854c52aaabdSJoe Komlodi };
1855c52aaabdSJoe Komlodi
dw_i3c_class_init(ObjectClass * klass,const void * data)1856c52aaabdSJoe Komlodi static void dw_i3c_class_init(ObjectClass *klass, const void *data)
1857c52aaabdSJoe Komlodi {
1858c52aaabdSJoe Komlodi DeviceClass *dc = DEVICE_CLASS(klass);
1859c52aaabdSJoe Komlodi ResettableClass *rc = RESETTABLE_CLASS(klass);
1860c52aaabdSJoe Komlodi
1861c52aaabdSJoe Komlodi rc->phases.enter = dw_i3c_reset_enter;
1862c52aaabdSJoe Komlodi
1863c52aaabdSJoe Komlodi dc->desc = "DesignWare I3C Controller";
1864c52aaabdSJoe Komlodi dc->realize = dw_i3c_realize;
1865c52aaabdSJoe Komlodi dc->vmsd = &vmstate_dw_i3c;
1866c52aaabdSJoe Komlodi device_class_set_props(dc, dw_i3c_properties);
1867c52aaabdSJoe Komlodi }
1868c52aaabdSJoe Komlodi
1869c52aaabdSJoe Komlodi static const TypeInfo dw_i3c_info = {
1870c52aaabdSJoe Komlodi .name = TYPE_DW_I3C,
1871c52aaabdSJoe Komlodi .parent = TYPE_SYS_BUS_DEVICE,
1872c52aaabdSJoe Komlodi .instance_size = sizeof(DWI3C),
1873c52aaabdSJoe Komlodi .class_init = dw_i3c_class_init,
1874c52aaabdSJoe Komlodi };
1875c52aaabdSJoe Komlodi
dw_i3c_register_types(void)1876c52aaabdSJoe Komlodi static void dw_i3c_register_types(void)
1877c52aaabdSJoe Komlodi {
1878c52aaabdSJoe Komlodi type_register_static(&dw_i3c_info);
1879c52aaabdSJoe Komlodi }
1880c52aaabdSJoe Komlodi
1881c52aaabdSJoe Komlodi type_init(dw_i3c_register_types);
1882