Implement UTP Task Management Request List (UTMRL) doorbell and TMR processing per JEDEC UFSHCI:
- Support UTMRLDBR, UTMRLCLR (clear-on-zero), and UTMRLRSR registers, and gate IS.UTMRCS interrupt assertion on the UTMRD Interrupt (I) bit. - Handle UFS_QUERY_TASK and UFS_ABORT_TASK across legacy UTRL and MCQ queues, reporting overall Target Success/Failure in rsp_header.response and the TM Service Response in output_param1. For MCQ, UFS_ABORT_TASK cancels the active SCSIRequest and defers CQE posting (OCS = UFS_OCS_ABORTED) and slot recycling to subsequent SQRTC.ICU cleanup. - Reset UTMRLRSR on HCE reset. Signed-off-by: Stanley Jhu <[email protected]> --- hw/ufs/trace-events | 1 + hw/ufs/ufs.c | 133 +++++++++++++++++++++++++++++++++++++++++++- include/block/ufs.h | 1 + 3 files changed, 134 insertions(+), 1 deletion(-) diff --git a/hw/ufs/trace-events b/hw/ufs/trace-events index d8173b12b..5e5a54a3f 100644 --- a/hw/ufs/trace-events +++ b/hw/ufs/trace-events @@ -15,6 +15,7 @@ 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_process_tmr(uint8_t func, uint32_t tag, uint8_t resp) "query_func 0x%"PRIx8", task_tag %"PRIu32", tm_resp 0x%"PRIx8"" 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 956ef9c87..4c505e0bf 100644 --- a/hw/ufs/ufs.c +++ b/hw/ufs/ufs.c @@ -772,6 +772,7 @@ static void ufs_hce_reset(UfsHc *u) u->reg.utmrldbr = 0; u->reg.utrlcnr = 0; u->reg.utrlrsr = 0; + u->reg.utmrlrsr = 0; u->reg.is = 0; /* 4. Free MCQ Queues and reset MCQ dynamic registers */ @@ -809,6 +810,126 @@ static void ufs_hce_reset(UfsHc *u) ufs_irq_check(u); } +static UfsRequest *ufs_find_req_by_tag(UfsHc *u, uint8_t lun, uint32_t task_tag) +{ + if (task_tag < u->params.nutrs) { + UfsRequest *req = &u->req_list[task_tag]; + if ((req->state == UFS_REQUEST_RUNNING || + req->state == UFS_REQUEST_READY) && + req->req_upiu.header.lun == lun && + req->req_upiu.header.task_tag == task_tag) { + return req; + } + } + + if (u->params.mcq) { + for (int q = 0; q < ARRAY_SIZE(u->sq); q++) { + UfsSq *sq = u->sq[q]; + if (!sq) { + continue; + } + for (int i = 0; i < sq->size; i++) { + UfsRequest *req = &sq->req[i]; + if (req->state == UFS_REQUEST_RUNNING && + req->req_upiu.header.lun == lun && + req->req_upiu.header.task_tag == task_tag) { + return req; + } + } + } + } + + return NULL; +} + +static void ufs_process_tmr(UfsHc *u, uint32_t val) +{ + hwaddr base_addr = (((hwaddr)u->reg.utmrlbau) << 32) + u->reg.utmrlba; + uint32_t completed_int_mask = 0; + + u->reg.utmrldbr |= val; + + for (int i = 0; i < u->params.nutmrs; i++) { + if (val & (1 << i)) { + uint64_t desc_addr = base_addr + i * sizeof(UtpTaskReqDesc); + UtpTaskReqDesc desc; + uint8_t tm_func, tm_resp, lun; + uint32_t task_tag; + + if (ufs_addr_read(u, desc_addr, &desc, sizeof(desc))) { + continue; + } + + tm_func = desc.upiu_req.req_header.query_func; + lun = desc.upiu_req.req_header.lun; + task_tag = be32_to_cpu(desc.upiu_req.input_param2); + + if (tm_func == UFS_QUERY_TASK) { + UfsRequest *req = ufs_find_req_by_tag(u, lun, task_tag); + + if (req) { + tm_resp = UFS_UPIU_TASK_MANAGEMENT_FUNC_SUCCEEDED; + } else { + tm_resp = UFS_UPIU_TASK_MANAGEMENT_FUNC_COMPL; + } + } else if (tm_func == UFS_ABORT_TASK) { + UfsRequest *req = ufs_find_req_by_tag(u, lun, task_tag); + + if (req) { + if (ufs_mcq_req(req)) { + if (req->sreq != NULL) { + SCSIRequest *sreq = req->sreq; + req->sreq = NULL; + scsi_req_cancel(sreq); + } + req->state = UFS_REQUEST_ERROR; + } else { + ufs_clear_req(req); + req->state = UFS_REQUEST_IDLE; + u->reg.utrldbr &= ~(1 << req->slot); + } + } + tm_resp = UFS_UPIU_TASK_MANAGEMENT_FUNC_COMPL; + } else { + tm_resp = UFS_UPIU_TASK_MANAGEMENT_FUNC_NOT_SUPPORTED; + } + + memset(&desc.upiu_rsp, 0, sizeof(desc.upiu_rsp)); + desc.header.dword_2 = cpu_to_le32( + (le32_to_cpu(desc.header.dword_2) & ~UFS_MASK_OCS) | + UFS_OCS_SUCCESS); + desc.upiu_rsp.rsp_header.trans_type = + UFS_UPIU_TRANSACTION_TASK_RSP; + desc.upiu_rsp.rsp_header.flags = 0; + desc.upiu_rsp.rsp_header.lun = desc.upiu_req.req_header.lun; + desc.upiu_rsp.rsp_header.task_tag = + desc.upiu_req.req_header.task_tag; + if (tm_resp == UFS_UPIU_TASK_MANAGEMENT_FUNC_COMPL || + tm_resp == UFS_UPIU_TASK_MANAGEMENT_FUNC_SUCCEEDED) { + desc.upiu_rsp.rsp_header.response = UFS_COMMAND_RESULT_SUCCESS; + } else { + desc.upiu_rsp.rsp_header.response = UFS_COMMAND_RESULT_FAIL; + } + desc.upiu_rsp.output_param1 = cpu_to_be32(tm_resp); + + if (ufs_addr_write(u, desc_addr, &desc, sizeof(desc))) { + continue; + } + + trace_ufs_process_tmr(tm_func, task_tag, tm_resp); + u->reg.utmrldbr &= ~(1 << i); + if (le32_to_cpu(desc.header.dword_0) & UFS_UTP_REQ_DESC_INT_CMD) { + completed_int_mask |= (1 << i); + } + } + } + + if (completed_int_mask) { + u->reg.is = FIELD_DP32(u->reg.is, IS, UTMRCS, 1); + ufs_irq_check(u); + } +} + static void ufs_write_reg(UfsHc *u, hwaddr offset, uint32_t data, unsigned size) { switch (offset) { @@ -871,10 +992,20 @@ static void ufs_write_reg(UfsHc *u, hwaddr offset, uint32_t data, unsigned size) case A_MCQCONFIG: u->reg.mcqconfig = data; break; - case A_UTRLCLR: case A_UTMRLDBR: + if (!FIELD_EX32(u->reg.hce, HCE, HCE) || + !FIELD_EX32(u->reg.utmrlrsr, UTMRLRSR, UTMRLRSR)) { + break; + } + ufs_process_tmr(u, data); + break; case A_UTMRLCLR: + u->reg.utmrldbr &= data; + break; case A_UTMRLRSR: + u->reg.utmrlrsr = data; + break; + case A_UTRLCLR: trace_ufs_err_unsupport_register_offset(offset); break; default: diff --git a/include/block/ufs.h b/include/block/ufs.h index b72403c47..b0515325f 100644 --- a/include/block/ufs.h +++ b/include/block/ufs.h @@ -141,6 +141,7 @@ REG32(UTMRLBAU, offsetof(UfsReg, utmrlbau)) REG32(UTMRLDBR, offsetof(UfsReg, utmrldbr)) REG32(UTMRLCLR, offsetof(UfsReg, utmrlclr)) REG32(UTMRLRSR, offsetof(UfsReg, utmrlrsr)) + FIELD(UTMRLRSR, UTMRLRSR, 0, 1) REG32(UICCMD, offsetof(UfsReg, uiccmd)) REG32(UCMDARG1, offsetof(UfsReg, ucmdarg1)) REG32(UCMDARG2, offsetof(UfsReg, ucmdarg2)) -- 2.56.0.rc1.315.gc6ed9934b7-goog
