Implement Multi-Circular Queue (MCQ) runtime operational registers (SQnRTC, SQnRTS, SQnCTI, SQnIS/IE, CQnIS/IE, CQnIACR) per JEDEC UFSHCI:
- Handle SQRTC.STOP transitions (0 -> 1 stop and 1 -> 0 resume) and SQRTC.ICU cleanup using Submission Queue Cleanup Task Information (SQCTI). Immediately clear SQRTS.CUS and SQRTS.RTC on ICU = 1, post a CQE with OCS = UFS_OCS_ABORTED on an active matching task, and set SQRTS.CUS = 1 with SQRTS.RTC = 0 (success), 1 (task not found), 2 (SQ not stopped), or 3 (SQ disabled). - Implement write-1-to-clear per-queue interrupt status (SQnIS, CQnIS) and synchronize the global IS.CQES bit. Signed-off-by: Stanley Jhu <[email protected]> --- hw/ufs/trace-events | 1 + hw/ufs/ufs.c | 112 +++++++++++++++++++++++++++++++++++++++++++- include/block/ufs.h | 15 ++++++ 3 files changed, 126 insertions(+), 2 deletions(-) diff --git a/hw/ufs/trace-events b/hw/ufs/trace-events index 922293355..d8173b12b 100644 --- a/hw/ufs/trace-events +++ b/hw/ufs/trace-events @@ -14,6 +14,7 @@ ufs_process_uiccmd(uint32_t uiccmd, uint32_t ucmdarg1, uint32_t ucmdarg2, uint32 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"" +ufs_write_mcq_op_reg(uint8_t qid, uint32_t offset, uint32_t data) "qid %"PRIu8", offset 0x%"PRIx32", data 0x%"PRIx32"" ufs_hce_reset(void) "HCE 1 -> 0 reset: cancelling BHs, resetting MCQ and request lists" # error condition diff --git a/hw/ufs/ufs.c b/hw/ufs/ufs.c index adae6639e..956ef9c87 100644 --- a/hw/ufs/ufs.c +++ b/hw/ufs/ufs.c @@ -446,13 +446,14 @@ static void ufs_mcq_process_sq(void *opaque) { UfsSq *sq = opaque; UfsHc *u = sq->u; + UfsMcqOpReg *opr = &u->mcq_op_reg[sq->sqid]; UfsSqEntry sqe; UfsRequest *req; hwaddr addr; uint16_t head = ufs_mcq_sq_head(u, sq->sqid); int err; - if (u->resetting) { + if (u->resetting || FIELD_EX32(opr->sq.rts, SQRTS, STS)) { return; } @@ -536,6 +537,7 @@ static void ufs_mcq_process_cq(void *opaque) } ufs_clear_req(req); + req->state = UFS_REQUEST_IDLE; QTAILQ_INSERT_TAIL(&req->sq->req_list, req, entry); } @@ -970,6 +972,56 @@ static void ufs_mcq_process_db(UfsHc *u, uint8_t qid, uint32_t db) qemu_bh_schedule(sq->bh); } +static void ufs_mcq_sq_cleanup(UfsHc *u, uint8_t qid) +{ + UfsMcqOpReg *opr = &u->mcq_op_reg[qid]; + UfsSq *sq = u->sq[qid]; + uint8_t task_tag = FIELD_EX32(opr->sq.cti, SQCTI, TASK_TAG); + uint8_t lun = FIELD_EX32(opr->sq.cti, SQCTI, LUN); + UfsRequest *req = NULL; + uint8_t rtc; + + opr->sq.rts = FIELD_DP32(opr->sq.rts, SQRTS, CUS, 0); + opr->sq.rts = FIELD_DP32(opr->sq.rts, SQRTS, RTC, 0); + + if (!sq) { + rtc = 3; + } else if (!FIELD_EX32(opr->sq.rts, SQRTS, STS)) { + rtc = 2; + } else { + for (int i = 0; i < sq->size; i++) { + UfsRequest *candidate = &sq->req[i]; + if ((candidate->state == UFS_REQUEST_RUNNING || + candidate->state == UFS_REQUEST_ERROR) && + candidate->req_upiu.header.task_tag == task_tag && + candidate->req_upiu.header.lun == lun) { + req = candidate; + break; + } + } + + if (req) { + if (req->sreq != NULL) { + SCSIRequest *sreq = req->sreq; + req->sreq = NULL; + scsi_req_cancel(sreq); + } + req->utrd.header.dword_2 = cpu_to_le32( + (le32_to_cpu(req->utrd.header.dword_2) & ~UFS_MASK_OCS) | + UFS_OCS_ABORTED); + req->state = UFS_REQUEST_COMPLETE; + QTAILQ_INSERT_TAIL(&sq->cq->req_list, req, entry); + ufs_mcq_process_cq(sq->cq); + rtc = 0; + } else { + rtc = 1; + } + } + + opr->sq.rts = FIELD_DP32(opr->sq.rts, SQRTS, CUS, 1); + opr->sq.rts = FIELD_DP32(opr->sq.rts, SQRTS, RTC, rtc); +} + static void ufs_write_mcq_op_reg(UfsHc *u, hwaddr offset, uint32_t data, unsigned size) { @@ -983,6 +1035,9 @@ static void ufs_write_mcq_op_reg(UfsHc *u, hwaddr offset, uint32_t data, opr = &u->mcq_op_reg[qid]; + trace_ufs_write_mcq_op_reg(qid, (uint32_t)(offset % sizeof(UfsMcqOpReg)), + data); + switch (offset % sizeof(UfsMcqOpReg)) { case offsetof(UfsMcqOpReg, sq.tp): if (opr->sq.tp != data) { @@ -990,6 +1045,40 @@ static void ufs_write_mcq_op_reg(UfsHc *u, hwaddr offset, uint32_t data, } opr->sq.tp = data; break; + case offsetof(UfsMcqOpReg, sq.rtc): { + bool prev_stop = FIELD_EX32(opr->sq.rtc, SQRTC, STOP); + bool new_stop = FIELD_EX32(data, SQRTC, STOP); + + if (!prev_stop && new_stop) { + /* SQ_STOP (0 -> 1): Stop queue */ + opr->sq.rts = FIELD_DP32(opr->sq.rts, SQRTS, STS, 1); + if (u->sq[qid] && u->sq[qid]->bh) { + qemu_bh_cancel(u->sq[qid]->bh); + } + } else if (prev_stop && !new_stop) { + /* SQ_START (1 -> 0): Resume queue */ + opr->sq.rts = FIELD_DP32(opr->sq.rts, SQRTS, STS, 0); + if (u->sq[qid] && u->sq[qid]->bh) { + qemu_bh_schedule(u->sq[qid]->bh); + } + } + if (FIELD_EX32(data, SQRTC, ICU)) { + ufs_mcq_sq_cleanup(u, qid); + data = FIELD_DP32(data, SQRTC, ICU, 0); + } + opr->sq.rtc = data; + break; + } + case offsetof(UfsMcqOpReg, sq.cti): + opr->sq.cti = data; + break; + case offsetof(UfsMcqOpReg, sq_int.is): + opr->sq_int.is &= ~data; + ufs_irq_check(u); + break; + case offsetof(UfsMcqOpReg, sq_int.ie): + opr->sq_int.ie = data; + break; case offsetof(UfsMcqOpReg, cq.hp): { UfsCq *cq = u->cq[qid]; @@ -1006,8 +1095,27 @@ static void ufs_write_mcq_op_reg(UfsHc *u, hwaddr offset, uint32_t data, ufs_mcq_update_cq_head(u, qid, data); break; } - case offsetof(UfsMcqOpReg, cq_int.is): + case offsetof(UfsMcqOpReg, cq_int.is): { + bool pending = false; + opr->cq_int.is &= ~data; + for (int i = 0; i < ARRAY_SIZE(u->mcq_op_reg); i++) { + if (u->mcq_op_reg[i].cq_int.is) { + pending = true; + break; + } + } + if (!pending) { + u->reg.is = FIELD_DP32(u->reg.is, IS, CQES, 0); + } + ufs_irq_check(u); + break; + } + case offsetof(UfsMcqOpReg, cq_int.ie): + opr->cq_int.ie = data; + break; + case offsetof(UfsMcqOpReg, cq_int.iacr): + opr->cq_int.iacr = data; break; default: trace_ufs_err_invalid_register_offset(offset); diff --git a/include/block/ufs.h b/include/block/ufs.h index d19b3c65e..b72403c47 100644 --- a/include/block/ufs.h +++ b/include/block/ufs.h @@ -224,6 +224,21 @@ typedef struct QEMU_PACKED UfsMcqSqReg { uint32_t rts; } UfsMcqSqReg; +REG32(SQRTC, offsetof(UfsMcqSqReg, rtc)) + FIELD(SQRTC, STOP, 0, 1) + FIELD(SQRTC, ICU, 1, 1) + +REG32(SQCTI, offsetof(UfsMcqSqReg, cti)) + FIELD(SQCTI, TASK_TAG, 0, 8) + FIELD(SQCTI, LUN, 8, 8) + FIELD(SQCTI, IID, 16, 4) + FIELD(SQCTI, EXT_IID, 20, 4) + +REG32(SQRTS, offsetof(UfsMcqSqReg, rts)) + FIELD(SQRTS, STS, 0, 1) + FIELD(SQRTS, CUS, 1, 1) + FIELD(SQRTS, RTC, 4, 4) + typedef struct QEMU_PACKED UfsMcqCqReg { uint32_t hp; uint32_t tp; -- 2.56.0.rc1.315.gc6ed9934b7-goog
