* [PATCH 0/2] hw/ufs: Add support MCQ
[not found] <CGME20240521111643epcas2p119387edb838377d98d269e62d994cc3a@epcas2p1.samsung.com>
@ 2024-05-21 11:05 ` Minwoo Im
[not found] ` <CGME20240521111643epcas2p3c86343fde1fdd6dbd868d09188084ae0@epcas2p3.samsung.com>
[not found] ` <CGME20240521111643epcas2p4ab1c2610d26878c405288e9006bc4f92@epcas2p4.samsung.com>
0 siblings, 2 replies; 5+ messages in thread
From: Minwoo Im @ 2024-05-21 11:05 UTC (permalink / raw)
To: Jeuk Kim, Kevin Wolf, Hanna Reitz
Cc: qemu-devel, qemu-block, gost.dev, Minwoo Im
UFSHCI 4.0 spec introduced MCQ(Multi-Circular Queue) to support multiple
command queues for UFS controller. To test ufs-mcq path of kernel, MCQ
emulated device would be a good choice to go with.
The first patch added newly introduced fields in UFSHCI 4.0 to support
MCQ. The other one made the actual changes for MCQ.
Please review.
Thanks,
Minwoo Im (2):
hw/ufs: Update MCQ-related fields to block/ufs.h
hw/ufs: Add support MCQ of UFSHCI 4.0
hw/ufs/trace-events | 17 ++
hw/ufs/ufs.c | 474 ++++++++++++++++++++++++++++++++++++++++++--
hw/ufs/ufs.h | 98 ++++++++-
include/block/ufs.h | 131 +++++++++++-
4 files changed, 698 insertions(+), 22 deletions(-)
--
2.34.1
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 1/2] hw/ufs: Update MCQ-related fields to block/ufs.h
[not found] ` <CGME20240521111643epcas2p3c86343fde1fdd6dbd868d09188084ae0@epcas2p3.samsung.com>
@ 2024-05-21 11:05 ` Minwoo Im
0 siblings, 0 replies; 5+ messages in thread
From: Minwoo Im @ 2024-05-21 11:05 UTC (permalink / raw)
To: Jeuk Kim, Kevin Wolf, Hanna Reitz
Cc: qemu-devel, qemu-block, gost.dev, Minwoo Im
This patch is a prep patch for the following MCQ support patch for
hw/ufs. This patch updated minimal mandatory fields to support MCQ
based on UFSHCI 4.0.
Signed-off-by: Minwoo Im <minwoo.im@samsung.com>
---
include/block/ufs.h | 108 +++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 106 insertions(+), 2 deletions(-)
diff --git a/include/block/ufs.h b/include/block/ufs.h
index d61598b8f3..3513b6e772 100644
--- a/include/block/ufs.h
+++ b/include/block/ufs.h
@@ -7,7 +7,7 @@
typedef struct QEMU_PACKED UfsReg {
uint32_t cap;
- uint32_t rsvd0;
+ uint32_t mcqcap;
uint32_t ver;
uint32_t rsvd1;
uint32_t hcpid;
@@ -46,6 +46,13 @@ typedef struct QEMU_PACKED UfsReg {
uint32_t rsvd7[4];
uint32_t rsvd8[16];
uint32_t ccap;
+ uint32_t rsvd9[127];
+ uint32_t config;
+ uint32_t rsvd10[3];
+ uint32_t rsvd11[28];
+ uint32_t mcqconfig;
+ uint32_t esilba;
+ uint32_t esiuba;
} UfsReg;
REG32(CAP, offsetof(UfsReg, cap))
@@ -57,6 +64,15 @@ REG32(CAP, offsetof(UfsReg, cap))
FIELD(CAP, OODDS, 25, 1)
FIELD(CAP, UICDMETMS, 26, 1)
FIELD(CAP, CS, 28, 1)
+ FIELD(CAP, LSDBS, 29, 1)
+ FIELD(CAP, MCQS, 30, 1)
+REG32(MCQCAP, offsetof(UfsReg, mcqcap))
+ FIELD(MCQCAP, MAXQ, 0, 8)
+ FIELD(MCQCAP, SP, 8, 1)
+ FIELD(MCQCAP, RRP, 9, 1)
+ FIELD(MCQCAP, EIS, 10, 1)
+ FIELD(MCQCAP, QCFGPTR, 16, 8)
+ FIELD(MCQCAP, MIAG, 24, 8)
REG32(VER, offsetof(UfsReg, ver))
REG32(HCPID, offsetof(UfsReg, hcpid))
REG32(HCMID, offsetof(UfsReg, hcmid))
@@ -78,6 +94,7 @@ REG32(IS, offsetof(UfsReg, is))
FIELD(IS, HCFES, 16, 1)
FIELD(IS, SBFES, 17, 1)
FIELD(IS, CEFES, 18, 1)
+ FIELD(IS, CQES, 20, 1)
REG32(IE, offsetof(UfsReg, ie))
FIELD(IE, UTRCE, 0, 1)
FIELD(IE, UDEPRIE, 1, 1)
@@ -95,6 +112,7 @@ REG32(IE, offsetof(UfsReg, ie))
FIELD(IE, HCFEE, 16, 1)
FIELD(IE, SBFEE, 17, 1)
FIELD(IE, CEFEE, 18, 1)
+ FIELD(IE, CQEE, 20, 1)
REG32(HCS, offsetof(UfsReg, hcs))
FIELD(HCS, DP, 0, 1)
FIELD(HCS, UTRLRDY, 1, 1)
@@ -128,6 +146,10 @@ REG32(UCMDARG1, offsetof(UfsReg, ucmdarg1))
REG32(UCMDARG2, offsetof(UfsReg, ucmdarg2))
REG32(UCMDARG3, offsetof(UfsReg, ucmdarg3))
REG32(CCAP, offsetof(UfsReg, ccap))
+REG32(CONFIG, offsetof(UfsReg, config))
+ FIELD(CONFIG, QT, 0, 1)
+REG32(MCQCONFIG, offsetof(UfsReg, mcqconfig))
+ FIELD(MCQCONFIG, MAC, 8, 8)
#define UFS_INTR_MASK \
((1 << R_IS_CEFES_SHIFT) | (1 << R_IS_SBFES_SHIFT) | \
@@ -157,6 +179,69 @@ REG32(CCAP, offsetof(UfsReg, ccap))
((be32_to_cpu(dword2) >> UFS_UPIU_HEADER_DATA_SEGMENT_LENGTH_SHIFT) & \
UFS_UPIU_HEADER_DATA_SEGMENT_LENGTH_MASK)
+typedef struct QEMU_PACKED UfsMcqReg {
+ uint32_t sqattr;
+ uint32_t sqlba;
+ uint32_t squba;
+ uint32_t sqdao;
+ uint32_t sqisao;
+ uint32_t sqcfg;
+ uint32_t rsvd0[2];
+ uint32_t cqattr;
+ uint32_t cqlba;
+ uint32_t cquba;
+ uint32_t cqdao;
+ uint32_t cqisao;
+ uint32_t cqcfg;
+ uint32_t rsvd1[2];
+} UfsMcqReg;
+
+REG32(SQATTR, offsetof(UfsMcqReg, sqattr))
+ FIELD(SQATTR, SIZE, 0, 16)
+ FIELD(SQATTR, CQID, 16, 8)
+ FIELD(SQATTR, SQPL, 28, 3)
+ FIELD(SQATTR, SQEN, 31, 1)
+REG32(SQLBA, offsetof(UfsMcqReg, sqlba))
+REG32(SQUBA, offsetof(UfsMcqReg, squba))
+REG32(SQDAO, offsetof(UfsMcqReg, sqdao))
+REG32(SQISAO, offsetof(UfsMcqReg, sqisao))
+REG32(SQCFG, offsetof(UfsMcqReg, sqcfg))
+REG32(CQATTR, offsetof(UfsMcqReg, cqattr))
+ FIELD(CQATTR, SIZE, 0, 16)
+ FIELD(CQATTR, CQEN, 31, 1)
+REG32(CQLBA, offsetof(UfsMcqReg, cqlba))
+REG32(CQUBA, offsetof(UfsMcqReg, cquba))
+REG32(CQDAO, offsetof(UfsMcqReg, cqdao))
+REG32(CQISAO, offsetof(UfsMcqReg, cqisao))
+REG32(CQCFG, offsetof(UfsMcqReg, cqcfg))
+
+typedef struct QEMU_PACKED UfsMcqSqReg {
+ uint32_t hp;
+ uint32_t tp;
+ uint32_t rtc;
+ uint32_t cti;
+ uint32_t rts;
+} UfsMcqSqReg;
+
+typedef struct QEMU_PACKED UfsMcqCqReg {
+ uint32_t hp;
+ uint32_t tp;
+} UfsMcqCqReg;
+
+typedef struct QEMU_PACKED UfsMcqSqIntReg {
+ uint32_t is;
+ uint32_t ie;
+} UfsMcqSqIntReg;
+
+typedef struct QEMU_PACKED UfsMcqCqIntReg {
+ uint32_t is;
+ uint32_t ie;
+ uint32_t iacr;
+} UfsMcqCqIntReg;
+
+REG32(CQIS, offsetof(UfsMcqCqIntReg, is))
+ FIELD(CQIS, TEPS, 0, 1)
+
typedef struct QEMU_PACKED DeviceDescriptor {
uint8_t length;
uint8_t descriptor_idn;
@@ -1064,9 +1149,26 @@ typedef struct QEMU_PACKED UtpUpiuRsp {
};
} UtpUpiuRsp;
+/*
+ * MCQ Completion Queue Entry
+ */
+typedef UtpTransferReqDesc UfsSqEntry;
+typedef struct QEMU_PACKED UfsCqEntry {
+ uint64_t utp_addr;
+ uint16_t resp_len;
+ uint16_t resp_off;
+ uint16_t prdt_len;
+ uint16_t prdt_off;
+ uint8_t status;
+ uint8_t error;
+ uint16_t rsvd1;
+ uint32_t rsvd2[3];
+} UfsCqEntry;
+
static inline void _ufs_check_size(void)
{
- QEMU_BUILD_BUG_ON(sizeof(UfsReg) != 0x104);
+ QEMU_BUILD_BUG_ON(sizeof(UfsReg) != 0x38C);
+ QEMU_BUILD_BUG_ON(sizeof(UfsMcqReg) != 64);
QEMU_BUILD_BUG_ON(sizeof(DeviceDescriptor) != 89);
QEMU_BUILD_BUG_ON(sizeof(GeometryDescriptor) != 87);
QEMU_BUILD_BUG_ON(sizeof(UnitDescriptor) != 45);
@@ -1086,5 +1188,7 @@ static inline void _ufs_check_size(void)
QEMU_BUILD_BUG_ON(sizeof(UtpTaskReqDesc) != 80);
QEMU_BUILD_BUG_ON(sizeof(UtpCmdRsp) != 40);
QEMU_BUILD_BUG_ON(sizeof(UtpUpiuRsp) != 288);
+ QEMU_BUILD_BUG_ON(sizeof(UfsSqEntry) != 32);
+ QEMU_BUILD_BUG_ON(sizeof(UfsCqEntry) != 32);
}
#endif
--
2.34.1
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 2/2] hw/ufs: Add support MCQ of UFSHCI 4.0
[not found] ` <CGME20240521111643epcas2p4ab1c2610d26878c405288e9006bc4f92@epcas2p4.samsung.com>
@ 2024-05-21 11:05 ` Minwoo Im
2024-05-28 1:00 ` Jeuk Kim
0 siblings, 1 reply; 5+ messages in thread
From: Minwoo Im @ 2024-05-21 11:05 UTC (permalink / raw)
To: Jeuk Kim, Kevin Wolf, Hanna Reitz
Cc: qemu-devel, qemu-block, gost.dev, Minwoo Im
This patch adds support for MCQ defined in UFSHCI 4.0. This patch
utilized the legacy I/O codes as much as possible to support MCQ.
MCQ operation & runtime register is placed at 0x1000 offset of UFSHCI
register statically with no spare space among four registers (48B):
UfsMcqSqReg, UfsMcqSqIntReg, UfsMcqCqReg, UfsMcqCqIntReg
The maxinum number of queue is 32 as per spec, and the default
MAC(Multiple Active Commands) are 32 in the device.
Example:
-device ufs,serial=foo,id=ufs0,mcq=true,mcq-maxq=8
Signed-off-by: Minwoo Im <minwoo.im@samsung.com>
---
hw/ufs/trace-events | 17 ++
hw/ufs/ufs.c | 474 ++++++++++++++++++++++++++++++++++++++++++--
hw/ufs/ufs.h | 98 ++++++++-
include/block/ufs.h | 23 ++-
4 files changed, 592 insertions(+), 20 deletions(-)
diff --git a/hw/ufs/trace-events b/hw/ufs/trace-events
index 665e1a942b..dda7f8a2e5 100644
--- a/hw/ufs/trace-events
+++ b/hw/ufs/trace-events
@@ -11,13 +11,18 @@ ufs_exec_nop_cmd(uint32_t slot) "UTRLDBR slot %"PRIu32""
ufs_exec_scsi_cmd(uint32_t slot, uint8_t lun, uint8_t opcode) "slot %"PRIu32", lun 0x%"PRIx8", opcode 0x%"PRIx8""
ufs_exec_query_cmd(uint32_t slot, uint8_t opcode) "slot %"PRIu32", opcode 0x%"PRIx8""
ufs_process_uiccmd(uint32_t uiccmd, uint32_t ucmdarg1, uint32_t ucmdarg2, uint32_t ucmdarg3) "uiccmd 0x%"PRIx32", ucmdarg1 0x%"PRIx32", ucmdarg2 0x%"PRIx32", ucmdarg3 0x%"PRIx32""
+ufs_mcq_complete_req(uint8_t qid) "sqid %"PRIu8""
+ufs_mcq_create_sq(uint8_t sqid, uint8_t cqid, uint64_t addr, uint16_t size) "mcq create sq sqid %"PRIu8", cqid %"PRIu8", addr 0x%"PRIx64", size %"PRIu16""
+ufs_mcq_create_cq(uint8_t cqid, uint64_t addr, uint16_t size) "mcq create cq cqid %"PRIu8", addr 0x%"PRIx64", size %"PRIu16""
# error condition
ufs_err_dma_read_utrd(uint32_t slot, uint64_t addr) "failed to read utrd. UTRLDBR slot %"PRIu32", UTRD dma addr %"PRIu64""
ufs_err_dma_read_req_upiu(uint32_t slot, uint64_t addr) "failed to read req upiu. UTRLDBR slot %"PRIu32", request upiu addr %"PRIu64""
ufs_err_dma_read_prdt(uint32_t slot, uint64_t addr) "failed to read prdt. UTRLDBR slot %"PRIu32", prdt addr %"PRIu64""
+ufs_err_dma_read_sq(uint8_t qid, uint64_t addr) "failed to read sqe. SQ qid %"PRIu8", sqe addr %"PRIu64""
ufs_err_dma_write_utrd(uint32_t slot, uint64_t addr) "failed to write utrd. UTRLDBR slot %"PRIu32", UTRD dma addr %"PRIu64""
ufs_err_dma_write_rsp_upiu(uint32_t slot, uint64_t addr) "failed to write rsp upiu. UTRLDBR slot %"PRIu32", response upiu addr %"PRIu64""
+ufs_err_dma_write_cq(uint32_t cqid, uint64_t addr) "failed to write cq entry. cqid %"PRIu8", hwaddr %"PRIu64""
ufs_err_utrl_slot_error(uint32_t slot) "UTRLDBR slot %"PRIu32" is in error"
ufs_err_utrl_slot_busy(uint32_t slot) "UTRLDBR slot %"PRIu32" is busy"
ufs_err_unsupport_register_offset(uint32_t offset) "Register offset 0x%"PRIx32" is not yet supported"
@@ -31,3 +36,15 @@ ufs_err_query_invalid_opcode(uint8_t opcode) "query request has invalid opcode.
ufs_err_query_invalid_idn(uint8_t opcode, uint8_t idn) "query request has invalid idn. opcode: 0x%"PRIx8", idn 0x%"PRIx8""
ufs_err_query_invalid_index(uint8_t opcode, uint8_t index) "query request has invalid index. opcode: 0x%"PRIx8", index 0x%"PRIx8""
ufs_err_invalid_trans_code(uint32_t slot, uint8_t trans_code) "request upiu has invalid transaction code. slot: %"PRIu32", trans_code: 0x%"PRIx8""
+ufs_err_mcq_db_wr_invalid_sqid(uint8_t qid) "invalid mcq sqid %"PRIu8""
+ufs_err_mcq_db_wr_invalid_db(uint8_t qid, uint32_t db) "invalid mcq doorbell sqid %"PRIu8", db %"PRIu32""
+ufs_err_mcq_create_sq_invalid_sqid(uint8_t qid) "invalid mcq sqid %"PRIu8""
+ufs_err_mcq_create_sq_invalid_cqid(uint8_t qid) "invalid mcq cqid %"PRIu8""
+ufs_err_mcq_create_sq_already_exists(uint8_t qid) "mcq sqid %"PRIu8 "already exists"
+ufs_err_mcq_delete_sq_invalid_sqid(uint8_t qid) "invalid mcq sqid %"PRIu8""
+ufs_err_mcq_delete_sq_not_exists(uint8_t qid) "mcq sqid %"PRIu8 "not exists"
+ufs_err_mcq_create_cq_invalid_cqid(uint8_t qid) "invalid mcq cqid %"PRIu8""
+ufs_err_mcq_create_cq_already_exists(uint8_t qid) "mcq cqid %"PRIu8 "already exists"
+ufs_err_mcq_delete_cq_invalid_cqid(uint8_t qid) "invalid mcq cqid %"PRIu8""
+ufs_err_mcq_delete_cq_not_exists(uint8_t qid) "mcq cqid %"PRIu8 "not exists"
+ufs_err_mcq_delete_cq_sq_not_deleted(uint8_t sqid, uint8_t cqid) "mcq sq %"PRIu8" still has cq %"PRIu8""
diff --git a/hw/ufs/ufs.c b/hw/ufs/ufs.c
index bac78a32bb..d8b301f12d 100644
--- a/hw/ufs/ufs.c
+++ b/hw/ufs/ufs.c
@@ -9,7 +9,7 @@
*/
/**
- * Reference Specs: https://www.jedec.org/, 3.1
+ * Reference Specs: https://www.jedec.org/, 4.0
*
* Usage
* -----
@@ -28,10 +28,45 @@
#include "trace.h"
#include "ufs.h"
-/* The QEMU-UFS device follows spec version 3.1 */
-#define UFS_SPEC_VER 0x0310
+/* The QEMU-UFS device follows spec version 4.0 */
+#define UFS_SPEC_VER 0x0400
#define UFS_MAX_NUTRS 32
#define UFS_MAX_NUTMRS 8
+#define UFS_MCQ_QCFGPTR 2
+
+static void ufs_exec_req(UfsRequest *req);
+static void ufs_clear_req(UfsRequest *req);
+
+static inline uint64_t ufs_mcq_reg_addr(UfsHc *u, int qid)
+{
+ /* Submission Queue MCQ Registers offset (400h) */
+ return (UFS_MCQ_QCFGPTR * 0x200) + qid * 0x40;
+}
+
+static inline uint64_t ufs_mcq_op_reg_addr(UfsHc *u, int qid)
+{
+ /* MCQ Operation & Runtime Registers offset (1000h) */
+ return UFS_MCQ_OPR_START + qid * 48;
+}
+
+static inline uint64_t ufs_reg_size(UfsHc *u)
+{
+ /* Total UFS HCI Register size in bytes */
+ return ufs_mcq_op_reg_addr(u, 0) + sizeof(u->mcq_op_reg);
+}
+
+static inline bool ufs_is_mcq_reg(UfsHc *u, uint64_t addr)
+{
+ uint64_t mcq_reg_addr = ufs_mcq_reg_addr(u, 0);
+ return addr >= mcq_reg_addr && addr < mcq_reg_addr + sizeof(u->mcq_reg);
+}
+
+static inline bool ufs_is_mcq_op_reg(UfsHc *u, uint64_t addr)
+{
+ uint64_t mcq_op_reg_addr = ufs_mcq_op_reg_addr(u, 0);
+ return (addr >= mcq_op_reg_addr &&
+ addr < mcq_op_reg_addr + sizeof(u->mcq_op_reg));
+}
static MemTxResult ufs_addr_read(UfsHc *u, hwaddr addr, void *buf, int size)
{
@@ -181,9 +216,14 @@ static MemTxResult ufs_dma_read_upiu(UfsRequest *req)
{
MemTxResult ret;
- ret = ufs_dma_read_utrd(req);
- if (ret) {
- return ret;
+ /*
+ * In case of MCQ, UTRD has already been read from a SQ, so skip it.
+ */
+ if (!ufs_mcq_req(req)) {
+ ret = ufs_dma_read_utrd(req);
+ if (ret) {
+ return ret;
+ }
}
ret = ufs_dma_read_req_upiu(req);
@@ -335,6 +375,221 @@ static void ufs_process_uiccmd(UfsHc *u, uint32_t val)
ufs_irq_check(u);
}
+static void ufs_mcq_init_req(UfsHc *u, UfsRequest *req, UfsSq *sq)
+{
+ memset(req, 0, sizeof(*req));
+
+ req->hc = u;
+ req->state = UFS_REQUEST_IDLE;
+ req->slot = UFS_INVALID_SLOT;
+ req->sq = sq;
+}
+
+static void ufs_mcq_process_sq(void *opaque)
+{
+ UfsSq *sq = opaque;
+ UfsHc *u = sq->u;
+ UfsSqEntry sqe;
+ UfsRequest *req;
+ hwaddr addr;
+ uint16_t head = ufs_mcq_sq_head(u, sq->sqid);
+ int err;
+
+ while (!(ufs_mcq_sq_empty(u, sq->sqid) || QTAILQ_EMPTY(&sq->req_list))) {
+ addr = sq->addr + head;
+ err = ufs_addr_read(sq->u, addr, (void *)&sqe, sizeof(sqe));
+ if (err) {
+ trace_ufs_err_dma_read_sq(sq->sqid, addr);
+ return;
+ }
+
+ head = (head + sizeof(sqe)) % (sq->size * sizeof(sqe));
+ ufs_mcq_update_sq_head(u, sq->sqid, head);
+
+ req = QTAILQ_FIRST(&sq->req_list);
+ QTAILQ_REMOVE(&sq->req_list, req, entry);
+
+ ufs_mcq_init_req(sq->u, req, sq);
+ memcpy(&req->utrd, &sqe, sizeof(req->utrd));
+
+ req->state = UFS_REQUEST_RUNNING;
+ ufs_exec_req(req);
+ }
+}
+
+static void ufs_mcq_process_cq(void *opaque)
+{
+ UfsCq *cq = opaque;
+ UfsHc *u = cq->u;
+ UfsRequest *req, *next;
+ MemTxResult ret;
+ uint32_t tail = ufs_mcq_cq_tail(u, cq->cqid);
+
+ QTAILQ_FOREACH_SAFE(req, &cq->req_list, entry, next) {
+ ufs_dma_write_rsp_upiu(req);
+
+ req->cqe.utp_addr =
+ ((uint64_t)req->utrd.command_desc_base_addr_hi << 32ULL) |
+ req->utrd.command_desc_base_addr_lo;
+ req->cqe.utp_addr |= req->sq->sqid;
+ req->cqe.resp_len = req->utrd.response_upiu_length;
+ req->cqe.resp_off = req->utrd.response_upiu_offset;
+ req->cqe.prdt_len = req->utrd.prd_table_length;
+ req->cqe.prdt_off = req->utrd.prd_table_offset;
+ req->cqe.status = req->utrd.header.dword_2 & 0xf;
+ req->cqe.error = 0;
+
+ ret = ufs_addr_write(u, cq->addr + tail, &req->cqe,
+ sizeof(req->cqe));
+ if (ret) {
+ trace_ufs_err_dma_write_cq(cq->cqid, cq->addr + tail);
+ }
+ QTAILQ_REMOVE(&cq->req_list, req, entry);
+
+ tail = (tail + sizeof(req->cqe)) % (cq->size * sizeof(req->cqe));
+ ufs_mcq_update_cq_tail(u, cq->cqid, tail);
+
+ ufs_clear_req(req);
+ QTAILQ_INSERT_TAIL(&req->sq->req_list, req, entry);
+ }
+
+ if (!ufs_mcq_cq_empty(u, cq->cqid)) {
+ u->mcq_op_reg[cq->cqid].cq_int.is =
+ FIELD_DP32(u->mcq_op_reg[cq->cqid].cq_int.is, CQIS, TEPS, 1);
+
+ u->reg.is = FIELD_DP32(u->reg.is, IS, CQES, 1);
+ ufs_irq_check(u);
+ }
+}
+
+static bool ufs_mcq_create_sq(UfsHc *u, uint8_t qid, uint32_t attr)
+{
+ UfsMcqReg *reg = &u->mcq_reg[qid];
+ UfsSq *sq;
+ uint8_t cqid = FIELD_EX32(attr, SQATTR, CQID);
+
+ if (qid >= u->params.mcq_maxq) {
+ trace_ufs_err_mcq_create_sq_invalid_sqid(qid);
+ return false;
+ }
+
+ if (u->sq[qid]) {
+ trace_ufs_err_mcq_create_sq_already_exists(qid);
+ return false;
+ }
+
+ if (!u->cq[cqid]) {
+ trace_ufs_err_mcq_create_sq_invalid_cqid(qid);
+ return false;
+ }
+
+ sq = g_malloc0(sizeof(*sq));
+ sq->u = u;
+ sq->sqid = qid;
+ sq->cq = u->cq[cqid];
+ sq->addr = ((uint64_t)reg->squba << 32) | reg->sqlba;
+ sq->size =
+ ((FIELD_EX32(attr, SQATTR, SIZE) + 1) << 2) / sizeof(UfsSqEntry);
+
+ sq->bh = qemu_bh_new_guarded(ufs_mcq_process_sq, sq,
+ &DEVICE(u)->mem_reentrancy_guard);
+ sq->req = g_new0(UfsRequest, sq->size);
+ QTAILQ_INIT(&sq->req_list);
+ for (int i = 0; i < sq->size; i++) {
+ ufs_mcq_init_req(u, &sq->req[i], sq);
+ QTAILQ_INSERT_TAIL(&sq->req_list, &sq->req[i], entry);
+ }
+
+ u->sq[qid] = sq;
+
+ trace_ufs_mcq_create_sq(sq->sqid, sq->cq->cqid, sq->addr, sq->size);
+ return true;
+}
+
+static bool ufs_mcq_delete_sq(UfsHc *u, uint8_t qid)
+{
+ UfsSq *sq;
+
+ if (qid >= u->params.mcq_maxq) {
+ trace_ufs_err_mcq_delete_sq_invalid_sqid(qid);
+ return false;
+ }
+
+ if (!u->sq[qid]) {
+ trace_ufs_err_mcq_delete_sq_not_exists(qid);
+ return false;
+ }
+
+ sq = u->sq[qid];
+
+ qemu_bh_delete(sq->bh);
+ g_free(sq->req);
+ g_free(sq);
+ u->sq[qid] = NULL;
+ return true;
+}
+
+static bool ufs_mcq_create_cq(UfsHc *u, uint8_t qid, uint32_t attr)
+{
+ UfsMcqReg *reg = &u->mcq_reg[qid];
+ UfsCq *cq;
+
+ if (qid >= u->params.mcq_maxq) {
+ trace_ufs_err_mcq_create_cq_invalid_cqid(qid);
+ return false;
+ }
+
+ if (u->cq[qid]) {
+ trace_ufs_err_mcq_create_cq_already_exists(qid);
+ return false;
+ }
+
+ cq = g_malloc0(sizeof(*cq));
+ cq->u = u;
+ cq->cqid = qid;
+ cq->addr = ((uint64_t)reg->cquba << 32) | reg->cqlba;
+ cq->size =
+ ((FIELD_EX32(attr, CQATTR, SIZE) + 1) << 2) / sizeof(UfsCqEntry);
+
+ cq->bh = qemu_bh_new_guarded(ufs_mcq_process_cq, cq,
+ &DEVICE(u)->mem_reentrancy_guard);
+ QTAILQ_INIT(&cq->req_list);
+
+ u->cq[qid] = cq;
+
+ trace_ufs_mcq_create_cq(cq->cqid, cq->addr, cq->size);
+ return true;
+}
+
+static bool ufs_mcq_delete_cq(UfsHc *u, uint8_t qid)
+{
+ UfsCq *cq;
+
+ if (qid >= u->params.mcq_maxq) {
+ trace_ufs_err_mcq_delete_cq_invalid_cqid(qid);
+ return false;
+ }
+
+ if (!u->cq[qid]) {
+ trace_ufs_err_mcq_delete_cq_not_exists(qid);
+ return false;
+ }
+
+ for (int i = 0; i < ARRAY_SIZE(u->sq); i++) {
+ if (u->sq[i] && u->sq[i]->cq->cqid == qid) {
+ trace_ufs_err_mcq_delete_cq_sq_not_deleted(i, qid);
+ return false;
+ }
+ }
+
+ cq = u->cq[qid];
+
+ qemu_bh_delete(cq->bh);
+ g_free(cq);
+ u->cq[qid] = NULL;
+ return true;
+}
+
static void ufs_write_reg(UfsHc *u, hwaddr offset, uint32_t data, unsigned size)
{
switch (offset) {
@@ -390,6 +645,12 @@ static void ufs_write_reg(UfsHc *u, hwaddr offset, uint32_t data, unsigned size)
case A_UCMDARG3:
u->reg.ucmdarg3 = data;
break;
+ case A_CONFIG:
+ u->reg.config = data;
+ break;
+ case A_MCQCONFIG:
+ u->reg.mcqconfig = data;
+ break;
case A_UTRLCLR:
case A_UTMRLDBR:
case A_UTMRLCLR:
@@ -402,18 +663,138 @@ static void ufs_write_reg(UfsHc *u, hwaddr offset, uint32_t data, unsigned size)
}
}
+static void ufs_write_mcq_reg(UfsHc *u, hwaddr offset, uint32_t data,
+ unsigned size)
+{
+ int qid = offset / sizeof(UfsMcqReg);
+ UfsMcqReg *reg = &u->mcq_reg[qid];
+
+ switch (offset % sizeof(UfsMcqReg)) {
+ case A_SQATTR:
+ if (!FIELD_EX32(reg->sqattr, SQATTR, SQEN) &&
+ FIELD_EX32(data, SQATTR, SQEN)) {
+ if (!ufs_mcq_create_sq(u, qid, data)) {
+ break;
+ }
+ } else if (FIELD_EX32(reg->sqattr, SQATTR, SQEN) &&
+ !FIELD_EX32(data, SQATTR, SQEN)) {
+ if (!ufs_mcq_delete_sq(u, qid)) {
+ break;
+ }
+ }
+ reg->sqattr = data;
+ break;
+ case A_SQLBA:
+ reg->sqlba = data;
+ break;
+ case A_SQUBA:
+ reg->squba = data;
+ break;
+ case A_SQCFG:
+ reg->sqcfg = data;
+ break;
+ case A_CQATTR:
+ if (!FIELD_EX32(reg->cqattr, CQATTR, CQEN) &&
+ FIELD_EX32(data, CQATTR, CQEN)) {
+ if (!ufs_mcq_create_cq(u, qid, data)) {
+ break;
+ }
+ } else if (FIELD_EX32(reg->cqattr, CQATTR, CQEN) &&
+ !FIELD_EX32(data, CQATTR, CQEN)) {
+ if (!ufs_mcq_delete_cq(u, qid)) {
+ break;
+ }
+ }
+ reg->cqattr = data;
+ break;
+ case A_CQLBA:
+ reg->cqlba = data;
+ break;
+ case A_CQUBA:
+ reg->cquba = data;
+ break;
+ case A_CQCFG:
+ reg->cqcfg = data;
+ break;
+ case A_SQDAO:
+ case A_SQISAO:
+ case A_CQDAO:
+ case A_CQISAO:
+ trace_ufs_err_unsupport_register_offset(offset);
+ break;
+ default:
+ trace_ufs_err_invalid_register_offset(offset);
+ break;
+ }
+}
+
+static void ufs_mcq_process_db(UfsHc *u, uint8_t qid, uint32_t db)
+{
+ UfsSq *sq;
+
+ if (qid >= u->params.mcq_maxq) {
+ trace_ufs_err_mcq_db_wr_invalid_sqid(qid);
+ return;
+ }
+
+ sq = u->sq[qid];
+ if (sq->size * sizeof(UfsSqEntry) <= db) {
+ trace_ufs_err_mcq_db_wr_invalid_db(qid, db);
+ return;
+ }
+
+ ufs_mcq_update_sq_tail(u, sq->sqid, db);
+ qemu_bh_schedule(sq->bh);
+}
+
+static void ufs_write_mcq_op_reg(UfsHc *u, hwaddr offset, uint32_t data,
+ unsigned size)
+{
+ int qid = offset / sizeof(UfsMcqOpReg);
+ UfsMcqOpReg *opr = &u->mcq_op_reg[qid];
+
+ switch (offset % sizeof(UfsMcqOpReg)) {
+ case offsetof(UfsMcqOpReg, sq.tp):
+ if (opr->sq.tp != data) {
+ ufs_mcq_process_db(u, qid, data);
+ }
+ opr->sq.tp = data;
+ break;
+ case offsetof(UfsMcqOpReg, cq.hp):
+ opr->cq.hp = data;
+ ufs_mcq_update_cq_head(u, qid, data);
+ break;
+ case offsetof(UfsMcqOpReg, cq_int.is):
+ opr->cq_int.is &= ~data;
+ break;
+ default:
+ trace_ufs_err_invalid_register_offset(offset);
+ break;
+ }
+}
+
static uint64_t ufs_mmio_read(void *opaque, hwaddr addr, unsigned size)
{
UfsHc *u = (UfsHc *)opaque;
- uint8_t *ptr = (uint8_t *)&u->reg;
+ uint8_t *ptr;
uint64_t value;
-
- if (addr > sizeof(u->reg) - size) {
+ uint64_t offset;
+
+ if (addr < sizeof(u->reg)) {
+ offset = addr;
+ ptr = (uint8_t *)&u->reg;
+ } else if (ufs_is_mcq_reg(u, addr)) {
+ offset = addr - ufs_mcq_reg_addr(u, 0);
+ ptr = (uint8_t *)&u->mcq_reg;
+ } else if (ufs_is_mcq_op_reg(u, addr)) {
+ offset = addr - ufs_mcq_op_reg_addr(u, 0);
+ ptr = (uint8_t *)&u->mcq_op_reg;
+ } else {
trace_ufs_err_invalid_register_offset(addr);
return 0;
}
- value = *(uint32_t *)(ptr + addr);
+ value = *(uint32_t *)(ptr + offset);
trace_ufs_mmio_read(addr, value, size);
return value;
}
@@ -423,13 +804,18 @@ static void ufs_mmio_write(void *opaque, hwaddr addr, uint64_t data,
{
UfsHc *u = (UfsHc *)opaque;
- if (addr > sizeof(u->reg) - size) {
+ trace_ufs_mmio_write(addr, data, size);
+
+ if (addr < sizeof(u->reg)) {
+ ufs_write_reg(u, addr, data, size);
+ } else if (ufs_is_mcq_reg(u, addr)) {
+ ufs_write_mcq_reg(u, addr - ufs_mcq_reg_addr(u, 0), data, size);
+ } else if (ufs_is_mcq_op_reg(u, addr)) {
+ ufs_write_mcq_op_reg(u, addr - ufs_mcq_op_reg_addr(u, 0),
+ data, size);
+ } else {
trace_ufs_err_invalid_register_offset(addr);
- return;
}
-
- trace_ufs_mmio_write(addr, data, size);
- ufs_write_reg(u, addr, data, size);
}
static const MemoryRegionOps ufs_mmio_ops = {
@@ -1086,9 +1472,16 @@ void ufs_complete_req(UfsRequest *req, UfsReqResult req_result)
req->utrd.header.dword_2 = cpu_to_le32(UFS_OCS_INVALID_CMD_TABLE_ATTR);
}
- trace_ufs_complete_req(req->slot);
req->state = UFS_REQUEST_COMPLETE;
- qemu_bh_schedule(u->complete_bh);
+
+ if (ufs_mcq_req(req)) {
+ trace_ufs_mcq_complete_req(req->sq->sqid);
+ QTAILQ_INSERT_TAIL(&req->sq->cq->req_list, req, entry);
+ qemu_bh_schedule(req->sq->cq->bh);
+ } else {
+ trace_ufs_complete_req(req->slot);
+ qemu_bh_schedule(u->complete_bh);
+ }
}
static void ufs_clear_req(UfsRequest *req)
@@ -1158,6 +1551,11 @@ static bool ufs_check_constraints(UfsHc *u, Error **errp)
return false;
}
+ if (u->params.mcq_maxq >= UFS_MAX_MCQ_QNUM) {
+ error_setg(errp, "mcq-maxq must be less than %d", UFS_MAX_MCQ_QNUM);
+ return false;
+ }
+
return true;
}
@@ -1189,15 +1587,24 @@ static void ufs_init_state(UfsHc *u)
&DEVICE(u)->mem_reentrancy_guard);
u->complete_bh = qemu_bh_new_guarded(ufs_sendback_req, u,
&DEVICE(u)->mem_reentrancy_guard);
+
+ if (u->params.mcq) {
+ memset(u->sq, 0, sizeof(u->sq));
+ memset(u->cq, 0, sizeof(u->cq));
+ }
}
static void ufs_init_hc(UfsHc *u)
{
uint32_t cap = 0;
+ uint32_t mcqconfig = 0;
+ uint32_t mcqcap = 0;
- u->reg_size = pow2ceil(sizeof(UfsReg));
+ u->reg_size = pow2ceil(ufs_reg_size(u));
memset(&u->reg, 0, sizeof(u->reg));
+ memset(&u->mcq_reg, 0, sizeof(u->mcq_reg));
+ memset(&u->mcq_op_reg, 0, sizeof(u->mcq_op_reg));
cap = FIELD_DP32(cap, CAP, NUTRS, (u->params.nutrs - 1));
cap = FIELD_DP32(cap, CAP, RTT, 2);
cap = FIELD_DP32(cap, CAP, NUTMRS, (u->params.nutmrs - 1));
@@ -1206,7 +1613,29 @@ static void ufs_init_hc(UfsHc *u)
cap = FIELD_DP32(cap, CAP, OODDS, 0);
cap = FIELD_DP32(cap, CAP, UICDMETMS, 0);
cap = FIELD_DP32(cap, CAP, CS, 0);
+ cap = FIELD_DP32(cap, CAP, LSDBS, 1);
+ cap = FIELD_DP32(cap, CAP, MCQS, u->params.mcq);
u->reg.cap = cap;
+
+ if (u->params.mcq) {
+ mcqconfig = FIELD_DP32(mcqconfig, MCQCONFIG, MAC, 0x1f);
+ u->reg.mcqconfig = mcqconfig;
+
+ mcqcap = FIELD_DP32(mcqcap, MCQCAP, MAXQ, u->params.mcq_maxq - 1);
+ mcqcap = FIELD_DP32(mcqcap, MCQCAP, RRP, 1);
+ mcqcap = FIELD_DP32(mcqcap, MCQCAP, QCFGPTR, UFS_MCQ_QCFGPTR);
+ u->reg.mcqcap = mcqcap;
+
+ for (int i = 0; i < ARRAY_SIZE(u->mcq_reg); i++) {
+ uint64_t addr = ufs_mcq_op_reg_addr(u, i);
+ u->mcq_reg[i].sqdao = addr;
+ u->mcq_reg[i].sqisao = addr + sizeof(UfsMcqSqReg);
+ addr += sizeof(UfsMcqSqReg);
+ u->mcq_reg[i].cqdao = addr + sizeof(UfsMcqSqIntReg);
+ addr += sizeof(UfsMcqSqIntReg);
+ u->mcq_reg[i].cqisao = addr + sizeof(UfsMcqCqReg);
+ }
+ }
u->reg.ver = UFS_SPEC_VER;
memset(&u->device_desc, 0, sizeof(DeviceDescriptor));
@@ -1288,12 +1717,21 @@ static void ufs_exit(PCIDevice *pci_dev)
ufs_clear_req(&u->req_list[i]);
}
g_free(u->req_list);
+
+ for (int i = 0; i < ARRAY_SIZE(u->sq); i++) {
+ ufs_mcq_delete_sq(u, i);
+ }
+ for (int i = 0; i < ARRAY_SIZE(u->cq); i++) {
+ ufs_mcq_delete_cq(u, i);
+ }
}
static Property ufs_props[] = {
DEFINE_PROP_STRING("serial", UfsHc, params.serial),
DEFINE_PROP_UINT8("nutrs", UfsHc, params.nutrs, 32),
DEFINE_PROP_UINT8("nutmrs", UfsHc, params.nutmrs, 8),
+ DEFINE_PROP_BOOL("mcq", UfsHc, params.mcq, false),
+ DEFINE_PROP_UINT8("mcq-maxq", UfsHc, params.mcq_maxq, 1),
DEFINE_PROP_END_OF_LIST(),
};
diff --git a/hw/ufs/ufs.h b/hw/ufs/ufs.h
index 8fda94f4ef..6c9382cbc4 100644
--- a/hw/ufs/ufs.h
+++ b/hw/ufs/ufs.h
@@ -16,6 +16,7 @@
#include "block/ufs.h"
#define UFS_MAX_LUS 32
+#define UFS_MAX_MCQ_QNUM 32
#define UFS_BLOCK_SIZE_SHIFT 12
#define UFS_BLOCK_SIZE (1 << UFS_BLOCK_SIZE_SHIFT)
@@ -45,10 +46,11 @@ typedef enum UfsReqResult {
UFS_REQUEST_NO_COMPLETE = 2,
} UfsReqResult;
+#define UFS_INVALID_SLOT (-1)
typedef struct UfsRequest {
struct UfsHc *hc;
UfsRequestState state;
- int slot;
+ int slot; /* -1 when it's a MCQ request */
UtpTransferReqDesc utrd;
UtpUpiuReq req_upiu;
@@ -57,8 +59,18 @@ typedef struct UfsRequest {
/* for scsi command */
QEMUSGList *sg;
uint32_t data_len;
+
+ /* for MCQ */
+ struct UfsSq *sq;
+ struct UfsCqEntry cqe;
+ QTAILQ_ENTRY(UfsRequest) entry;
} UfsRequest;
+static inline bool ufs_mcq_req(UfsRequest *req)
+{
+ return req->sq != NULL;
+}
+
struct UfsLu;
typedef UfsReqResult (*UfsScsiOp)(struct UfsLu *, UfsRequest *);
@@ -76,13 +88,43 @@ typedef struct UfsParams {
char *serial;
uint8_t nutrs; /* Number of UTP Transfer Request Slots */
uint8_t nutmrs; /* Number of UTP Task Management Request Slots */
+ bool mcq; /* Multiple Command Queue support */
+ uint8_t mcq_qcfgptr; /* MCQ Queue Configuration Pointer in MCQCAP */
+ uint8_t mcq_maxq; /* MCQ Maximum number of Queues */
} UfsParams;
+/*
+ * MCQ Properties
+ */
+typedef struct UfsSq {
+ struct UfsHc *u;
+ uint8_t sqid;
+ struct UfsCq *cq;
+ uint64_t addr;
+ uint16_t size; /* A number of entries (qdepth) */
+
+ QEMUBH *bh; /* Bottom half to process requests in async */
+ UfsRequest *req;
+ QTAILQ_HEAD(, UfsRequest) req_list; /* Free request list */
+} UfsSq;
+
+typedef struct UfsCq {
+ struct UfsHc *u;
+ uint8_t cqid;
+ uint64_t addr;
+ uint16_t size; /* A number of entries (qdepth) */
+
+ QEMUBH *bh;
+ QTAILQ_HEAD(, UfsRequest) req_list;
+} UfsCq;
+
typedef struct UfsHc {
PCIDevice parent_obj;
UfsBus bus;
MemoryRegion iomem;
UfsReg reg;
+ UfsMcqReg mcq_reg[UFS_MAX_MCQ_QNUM];
+ UfsMcqOpReg mcq_op_reg[UFS_MAX_MCQ_QNUM];
UfsParams params;
uint32_t reg_size;
UfsRequest *req_list;
@@ -100,8 +142,62 @@ typedef struct UfsHc {
qemu_irq irq;
QEMUBH *doorbell_bh;
QEMUBH *complete_bh;
+
+ /* MCQ properties */
+ UfsSq *sq[UFS_MAX_MCQ_QNUM];
+ UfsCq *cq[UFS_MAX_MCQ_QNUM];
} UfsHc;
+static inline uint32_t ufs_mcq_sq_tail(UfsHc *u, uint32_t qid)
+{
+ return u->mcq_op_reg[qid].sq.tp;
+}
+
+static inline void ufs_mcq_update_sq_tail(UfsHc *u, uint32_t qid, uint32_t db)
+{
+ u->mcq_op_reg[qid].sq.tp = db;
+}
+
+static inline uint32_t ufs_mcq_sq_head(UfsHc *u, uint32_t qid)
+{
+ return u->mcq_op_reg[qid].sq.hp;
+}
+
+static inline void ufs_mcq_update_sq_head(UfsHc *u, uint32_t qid, uint32_t db)
+{
+ u->mcq_op_reg[qid].sq.hp = db;
+}
+
+static inline bool ufs_mcq_sq_empty(UfsHc *u, uint32_t qid)
+{
+ return ufs_mcq_sq_tail(u, qid) == ufs_mcq_sq_head(u, qid);
+}
+
+static inline uint32_t ufs_mcq_cq_tail(UfsHc *u, uint32_t qid)
+{
+ return u->mcq_op_reg[qid].cq.tp;
+}
+
+static inline void ufs_mcq_update_cq_tail(UfsHc *u, uint32_t qid, uint32_t db)
+{
+ u->mcq_op_reg[qid].cq.tp = db;
+}
+
+static inline uint32_t ufs_mcq_cq_head(UfsHc *u, uint32_t qid)
+{
+ return u->mcq_op_reg[qid].cq.hp;
+}
+
+static inline void ufs_mcq_update_cq_head(UfsHc *u, uint32_t qid, uint32_t db)
+{
+ u->mcq_op_reg[qid].cq.hp = db;
+}
+
+static inline bool ufs_mcq_cq_empty(UfsHc *u, uint32_t qid)
+{
+ return ufs_mcq_cq_tail(u, qid) == ufs_mcq_cq_head(u, qid);
+}
+
#define TYPE_UFS "ufs"
#define UFS(obj) OBJECT_CHECK(UfsHc, (obj), TYPE_UFS)
diff --git a/include/block/ufs.h b/include/block/ufs.h
index 3513b6e772..92da7a89b9 100644
--- a/include/block/ufs.h
+++ b/include/block/ufs.h
@@ -152,7 +152,8 @@ REG32(MCQCONFIG, offsetof(UfsReg, mcqconfig))
FIELD(MCQCONFIG, MAC, 8, 8)
#define UFS_INTR_MASK \
- ((1 << R_IS_CEFES_SHIFT) | (1 << R_IS_SBFES_SHIFT) | \
+ ((1 << R_IS_CQES_SHIFT) | \
+ (1 << R_IS_CEFES_SHIFT) | (1 << R_IS_SBFES_SHIFT) | \
(1 << R_IS_HCFES_SHIFT) | (1 << R_IS_UTPES_SHIFT) | \
(1 << R_IS_DFES_SHIFT) | (1 << R_IS_UCCS_SHIFT) | \
(1 << R_IS_UTMRCS_SHIFT) | (1 << R_IS_ULSS_SHIFT) | \
@@ -242,6 +243,21 @@ typedef struct QEMU_PACKED UfsMcqCqIntReg {
REG32(CQIS, offsetof(UfsMcqCqIntReg, is))
FIELD(CQIS, TEPS, 0, 1)
+/*
+ * Provide MCQ Operation & Runtime Registers as a contiguous addressed
+ * registers for the simplicity.
+ * DAO(Doorbell Address Offset) and ISAO(Interrupt Status Register Address
+ * Offset) registers should be properly configured with the following
+ * structure.
+ */
+#define UFS_MCQ_OPR_START 0x1000
+typedef struct QEMU_PACKED UfsMcqOpReg {
+ UfsMcqSqReg sq;
+ UfsMcqSqIntReg sq_int;
+ UfsMcqCqReg cq;
+ UfsMcqCqIntReg cq_int;
+} UfsMcqOpReg;
+
typedef struct QEMU_PACKED DeviceDescriptor {
uint8_t length;
uint8_t descriptor_idn;
@@ -1169,6 +1185,11 @@ static inline void _ufs_check_size(void)
{
QEMU_BUILD_BUG_ON(sizeof(UfsReg) != 0x38C);
QEMU_BUILD_BUG_ON(sizeof(UfsMcqReg) != 64);
+ QEMU_BUILD_BUG_ON(sizeof(UfsMcqSqReg) != 20);
+ QEMU_BUILD_BUG_ON(sizeof(UfsMcqCqReg) != 8);
+ QEMU_BUILD_BUG_ON(sizeof(UfsMcqSqIntReg) != 8);
+ QEMU_BUILD_BUG_ON(sizeof(UfsMcqCqIntReg) != 12);
+ QEMU_BUILD_BUG_ON(sizeof(UfsMcqOpReg) != 48);
QEMU_BUILD_BUG_ON(sizeof(DeviceDescriptor) != 89);
QEMU_BUILD_BUG_ON(sizeof(GeometryDescriptor) != 87);
QEMU_BUILD_BUG_ON(sizeof(UnitDescriptor) != 45);
--
2.34.1
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH 2/2] hw/ufs: Add support MCQ of UFSHCI 4.0
2024-05-21 11:05 ` [PATCH 2/2] hw/ufs: Add support MCQ of UFSHCI 4.0 Minwoo Im
@ 2024-05-28 1:00 ` Jeuk Kim
2024-05-28 1:54 ` Minwoo Im
0 siblings, 1 reply; 5+ messages in thread
From: Jeuk Kim @ 2024-05-28 1:00 UTC (permalink / raw)
To: Minwoo Im
Cc: qemu-devel, qemu-block, gost.dev, Jeuk Kim, Hanna Reitz,
Kevin Wolf
Thanks for your contribution!
There are only two minor comments.
Please check it and send patch v2.
Thank you!
On 5/21/2024 8:05 PM, Minwoo Im wrote:
> @@ -1288,12 +1717,21 @@ static void ufs_exit(PCIDevice *pci_dev)
> ufs_clear_req(&u->req_list[i]);
> }
> g_free(u->req_list);
> +
> + for (int i = 0; i < ARRAY_SIZE(u->sq); i++) {
> + ufs_mcq_delete_sq(u, i);
Isn't it possible that trace_ufs_err_mcq_delete_cq_not_exists is printed
even in a normal shutdown situation?
If true, please fix it so that the ufs_err log is not printed in normal
situation.
> + }
> + for (int i = 0; i < ARRAY_SIZE(u->cq); i++) {
> + ufs_mcq_delete_cq(u, i);
> + }
> }
>
> static Property ufs_props[] = {
> DEFINE_PROP_STRING("serial", UfsHc, params.serial),
> DEFINE_PROP_UINT8("nutrs", UfsHc, params.nutrs, 32),
> DEFINE_PROP_UINT8("nutmrs", UfsHc, params.nutmrs, 8),
> + DEFINE_PROP_BOOL("mcq", UfsHc, params.mcq, false),
> + DEFINE_PROP_UINT8("mcq-maxq", UfsHc, params.mcq_maxq, 1),
Please change this value to a value greater than or equal to 2.
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH 2/2] hw/ufs: Add support MCQ of UFSHCI 4.0
2024-05-28 1:00 ` Jeuk Kim
@ 2024-05-28 1:54 ` Minwoo Im
0 siblings, 0 replies; 5+ messages in thread
From: Minwoo Im @ 2024-05-28 1:54 UTC (permalink / raw)
To: Jeuk Kim
Cc: qemu-devel, qemu-block, gost.dev, Jeuk Kim, Hanna Reitz,
Kevin Wolf, Minwoo Im
[-- Attachment #1: Type: text/plain, Size: 1437 bytes --]
On 24-05-28 10:00:35, Jeuk Kim wrote:
> Thanks for your contribution!
>
> There are only two minor comments.
Thanks for your review.
>
> Please check it and send patch v2.
>
>
> Thank you!
>
> On 5/21/2024 8:05 PM, Minwoo Im wrote:
> > @@ -1288,12 +1717,21 @@ static void ufs_exit(PCIDevice *pci_dev)
> > ufs_clear_req(&u->req_list[i]);
> > }
> > g_free(u->req_list);
> > +
> > + for (int i = 0; i < ARRAY_SIZE(u->sq); i++) {
> > + ufs_mcq_delete_sq(u, i);
>
> Isn't it possible that trace_ufs_err_mcq_delete_cq_not_exists is printed
> even in a normal shutdown situation?
>
> If true, please fix it so that the ufs_err log is not printed in normal
> situation.
I will make sure that the normal shut-down case will not print out the error
event trace.
>
> > + }
> > + for (int i = 0; i < ARRAY_SIZE(u->cq); i++) {
> > + ufs_mcq_delete_cq(u, i);
> > + }
> > }
> > static Property ufs_props[] = {
> > DEFINE_PROP_STRING("serial", UfsHc, params.serial),
> > DEFINE_PROP_UINT8("nutrs", UfsHc, params.nutrs, 32),
> > DEFINE_PROP_UINT8("nutmrs", UfsHc, params.nutmrs, 8),
> > + DEFINE_PROP_BOOL("mcq", UfsHc, params.mcq, false),
> > + DEFINE_PROP_UINT8("mcq-maxq", UfsHc, params.mcq_maxq, 1),
> Please change this value to a value greater than or equal to 2.
Oh yeah, it should be to support device command handling in MCQ mode. I will
update it in v2.
[-- Attachment #2: Type: text/plain, Size: 0 bytes --]
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2024-05-28 2:06 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <CGME20240521111643epcas2p119387edb838377d98d269e62d994cc3a@epcas2p1.samsung.com>
2024-05-21 11:05 ` [PATCH 0/2] hw/ufs: Add support MCQ Minwoo Im
[not found] ` <CGME20240521111643epcas2p3c86343fde1fdd6dbd868d09188084ae0@epcas2p3.samsung.com>
2024-05-21 11:05 ` [PATCH 1/2] hw/ufs: Update MCQ-related fields to block/ufs.h Minwoo Im
[not found] ` <CGME20240521111643epcas2p4ab1c2610d26878c405288e9006bc4f92@epcas2p4.samsung.com>
2024-05-21 11:05 ` [PATCH 2/2] hw/ufs: Add support MCQ of UFSHCI 4.0 Minwoo Im
2024-05-28 1:00 ` Jeuk Kim
2024-05-28 1:54 ` Minwoo Im
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).