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