Add ufs_test_tmr_handling() to verify UTMRL UFS_QUERY_TASK execution (both interrupt-driven I=1 and polling I=0 UTMRDs), SQCTI/CQIACR register access, SQRTC/SQRTS queue stop/start and self-clearing ICU transitions across running, stopped, and disabled SQ states, nullified SQE (CT = 0xF) completion with UFS_OCS_ABORTED, and CQIS.TEPS write-1-to-clear synchronization with IS.CQES.
Signed-off-by: Stanley Jhu <[email protected]> --- tests/qtest/ufs-test.c | 161 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 160 insertions(+), 1 deletion(-) diff --git a/tests/qtest/ufs-test.c b/tests/qtest/ufs-test.c index 019107d818..bbc90f2218 100644 --- a/tests/qtest/ufs-test.c +++ b/tests/qtest/ufs-test.c @@ -1899,7 +1899,7 @@ static void ufstest_hid(void *obj, void *data, QGuestAllocator *alloc) ufs_exit(ufs, alloc); } -static G_GNUC_UNUSED void ufs_send_tmr(QUfs *ufs, uint64_t utmrlba, int slot, +static void ufs_send_tmr(QUfs *ufs, uint64_t utmrlba, int slot, uint8_t tm_func, uint8_t lun, uint32_t task_tag, UtpTaskReqDesc *utrd_out) @@ -2026,6 +2026,164 @@ static void ufs_test_hce_reset(void *obj, void *data, QGuestAllocator *alloc) ufs_exit(ufs, alloc); } +static void ufs_test_tmr_handling(void *obj, void *data, QGuestAllocator *alloc) +{ + QUfs *ufs = obj; + uint64_t utmrlba, desc_addr, req_upiu_addr, rsp_upiu_addr; + UtpTaskReqDesc desc = { 0 }, utrd_result; + UtpTransferReqDesc null_utrd; + UtpUpiuReq req_upiu = { 0 }; + UfsCqEntry cqe; + uint32_t mcq_reg_offset, sqdao, cqisao, sqrts, sqrtc, orig_sqattr; + int null_slot; + enum UtpOcsCodes ocs; + UtpUpiuRsp rsp_upiu; + + ufs_init(ufs, alloc); + g_assert_true(ufs->support_mcq); + + /* Set up UTMRL */ + utmrlba = guest_alloc(alloc, 8 * sizeof(UtpTaskReqDesc)); + ufs_wreg(ufs, A_UTMRLBA, utmrlba & 0xffffffff); + ufs_wreg(ufs, A_UTMRLBAU, utmrlba >> 32); + ufs_wreg(ufs, A_UTMRLRSR, 1); + + /* Issue UFS_QUERY_TASK for inactive tag 15 (Interrupt bit I=1) */ + ufs_send_tmr(ufs, utmrlba, 0, UFS_QUERY_TASK, 0, 15, &utrd_result); + g_assert_cmpuint(le32_to_cpu(utrd_result.header.dword_2) & UFS_MASK_OCS, + ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(utrd_result.upiu_rsp.rsp_header.response, + ==, UFS_COMMAND_RESULT_SUCCESS); + g_assert_cmpuint(be32_to_cpu(utrd_result.upiu_rsp.output_param1), + ==, UFS_UPIU_TASK_MANAGEMENT_FUNC_COMPL); + + /* Issue polling UTMRD with I=0 (slot 1): clears doorbell without UTMRCS */ + desc_addr = utmrlba + 1 * sizeof(UtpTaskReqDesc); + desc.header.dword_0 = 0; + desc.header.dword_2 = cpu_to_le32(UFS_OCS_INVALID_COMMAND_STATUS); + desc.upiu_req.req_header.trans_type = UFS_UPIU_TRANSACTION_TASK_REQ; + desc.upiu_req.req_header.query_func = UFS_QUERY_TASK; + desc.upiu_req.req_header.lun = 0; + desc.upiu_req.req_header.task_tag = 0x11; + desc.upiu_req.input_param1 = cpu_to_be32(0); + desc.upiu_req.input_param2 = cpu_to_be32(15); + qtest_memwrite(ufs->dev.bus->qts, desc_addr, &desc, sizeof(desc)); + + ufs_wreg(ufs, A_UTMRLDBR, 1 << 1); + g_assert_cmpuint(ufs_rreg(ufs, A_UTMRLDBR) & (1 << 1), ==, 0); + g_assert_cmpuint(FIELD_EX32(ufs_rreg(ufs, A_IS), IS, UTMRCS), ==, 0); + qtest_memread(ufs->dev.bus->qts, desc_addr, &utrd_result, + sizeof(utrd_result)); + g_assert_cmpuint(le32_to_cpu(utrd_result.header.dword_2) & UFS_MASK_OCS, + ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(utrd_result.upiu_rsp.rsp_header.response, + ==, UFS_COMMAND_RESULT_SUCCESS); + g_assert_cmpuint(be32_to_cpu(utrd_result.upiu_rsp.output_param1), + ==, UFS_UPIU_TASK_MANAGEMENT_FUNC_COMPL); + + /* Verify SQCTI, CQIACR, SQRTS/SQRTC, and CQIS/IS.CQES W1C sync */ + sqdao = ufs->sqdao[TEST_QID]; + mcq_reg_offset = + FIELD_EX32(ufs_rreg(ufs, A_MCQCAP), MCQCAP, QCFGPTR) * 0x200 + + TEST_QID * 0x40; + cqisao = ufs_rreg(ufs, mcq_reg_offset + A_CQISAO); + + /* SQCTI and CQIACR read/write */ + ufs_wreg(ufs, sqdao + A_SQCTI, (1 << 8) | 0x2b); + g_assert_cmpuint(ufs_rreg(ufs, sqdao + A_SQCTI), ==, (1 << 8) | 0x2b); + ufs_wreg(ufs, cqisao + offsetof(UfsMcqCqIntReg, iacr), 0x101); + g_assert_cmpuint(ufs_rreg(ufs, cqisao + offsetof(UfsMcqCqIntReg, iacr)), + ==, 0x101); + + /* SQ_ICU while running (STS=0) -> sets SQRTS.CUS=1, RTC=2 */ + ufs_wreg(ufs, sqdao + A_SQRTC, FIELD_DP32(0, SQRTC, ICU, 1)); + sqrts = ufs_rreg(ufs, sqdao + A_SQRTS); + sqrtc = ufs_rreg(ufs, sqdao + A_SQRTC); + g_assert_cmpuint(FIELD_EX32(sqrts, SQRTS, STS), ==, 0); + g_assert_cmpuint(FIELD_EX32(sqrts, SQRTS, CUS), ==, 1); + g_assert_cmpuint(FIELD_EX32(sqrts, SQRTS, RTC), ==, 2); + g_assert_cmpuint(FIELD_EX32(sqrtc, SQRTC, ICU), ==, 0); + + /* SQ_STOP -> sets SQRTS.STS = 1 */ + ufs_wreg(ufs, sqdao + A_SQRTC, FIELD_DP32(0, SQRTC, STOP, 1)); + sqrts = ufs_rreg(ufs, sqdao + A_SQRTS); + g_assert_cmpuint(FIELD_EX32(sqrts, SQRTS, STS), ==, 1); + + /* + * SQ_ICU while stopped (no matching req) -> sets SQRTS.CUS=1, RTC=1 and + * self-clears ICU. + */ + ufs_wreg(ufs, sqdao + A_SQRTC, + FIELD_DP32(0, SQRTC, STOP, 1) | FIELD_DP32(0, SQRTC, ICU, 1)); + sqrts = ufs_rreg(ufs, sqdao + A_SQRTS); + sqrtc = ufs_rreg(ufs, sqdao + A_SQRTC); + g_assert_cmpuint(FIELD_EX32(sqrts, SQRTS, STS), ==, 1); + g_assert_cmpuint(FIELD_EX32(sqrts, SQRTS, CUS), ==, 1); + g_assert_cmpuint(FIELD_EX32(sqrts, SQRTS, RTC), ==, 1); + g_assert_cmpuint(FIELD_EX32(sqrtc, SQRTC, ICU), ==, 0); + + /* + * Queue a nullified SQE (CT = 0xF) while SQ is stopped, then resume SQ + * (STOP 1->0): SQRTS.STS clears to 0 while SQRTS.CUS remains 1, and the + * nullified SQE posts CQE with UFS_OCS_ABORTED without writing rsp_upiu. + */ + null_slot = alloc_cmd_desc_slot(ufs); + req_upiu_addr = + ufs->cmd_desc_addr + null_slot * UTP_COMMAND_DESCRIPTOR_SIZE; + rsp_upiu_addr = req_upiu_addr + UTP_RESPONSE_UPIU_OFFSET; + req_upiu.header.trans_type = UFS_UPIU_TRANSACTION_NOP_OUT; + req_upiu.header.task_tag = null_slot; + qtest_memwrite(ufs->dev.bus->qts, req_upiu_addr, &req_upiu, + sizeof(req_upiu)); + memset(&rsp_upiu, 0xa5, sizeof(rsp_upiu)); + qtest_memwrite(ufs->dev.bus->qts, rsp_upiu_addr, &rsp_upiu, + sizeof(rsp_upiu)); + + null_utrd = ufs_build_req_utrd(req_upiu_addr, UFS_UTP_NO_DATA_TRANSFER, 0); + null_utrd.header.dword_0 = cpu_to_le32((UFS_UTP_CMD_TYPE_NULL << 28) | + UFS_UTP_NO_DATA_TRANSFER | + UFS_UTP_REQ_DESC_INT_CMD); + __ufs_send_transfer_request_mcq_async(ufs, 0, &null_utrd); + + /* SQ_START (STOP 1->0) -> clears SQRTS.STS = 0 while preserving CUS = 1 */ + ufs_wreg(ufs, sqdao + A_SQRTC, 0); + sqrts = ufs_rreg(ufs, sqdao + A_SQRTS); + g_assert_cmpuint(FIELD_EX32(sqrts, SQRTS, STS), ==, 0); + g_assert_cmpuint(FIELD_EX32(sqrts, SQRTS, CUS), ==, 1); + + ufs_wait_for_irq(ufs); + g_assert_cmpuint(ufs_mcq_poll_cq(ufs, &cqe, 1), ==, 1); + g_assert_cmpuint(cqe.status, ==, UFS_OCS_ABORTED); + g_assert_cmpuint(cqe.task_tag, ==, null_slot); + qtest_memread(ufs->dev.bus->qts, rsp_upiu_addr, &rsp_upiu, + sizeof(rsp_upiu)); + g_assert_cmpuint(rsp_upiu.header.trans_type, ==, 0xa5); + ufs_wreg(ufs, cqisao + A_CQIS, FIELD_DP32(0, CQIS, TEPS, 1)); + release_cmd_desc_slot(ufs, null_slot); + + /* Verify MCQ command sets CQIS.TEPS=1 and W1C clears CQIS.TEPS & IS.CQES */ + ocs = ufs_send_nop_out(ufs, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(FIELD_EX32(ufs_rreg(ufs, cqisao + A_CQIS), CQIS, TEPS), + ==, 1); + ufs_wreg(ufs, cqisao + A_CQIS, FIELD_DP32(0, CQIS, TEPS, 1)); + g_assert_cmpuint(FIELD_EX32(ufs_rreg(ufs, cqisao + A_CQIS), CQIS, TEPS), + ==, 0); + g_assert_cmpuint(FIELD_EX32(ufs_rreg(ufs, A_IS), IS, CQES), ==, 0); + + /* SQ_ICU on disabled SQ (SQEN=0) -> sets SQRTS.CUS=1, RTC=3 */ + orig_sqattr = ufs_rreg(ufs, mcq_reg_offset + A_SQATTR); + ufs_wreg(ufs, mcq_reg_offset + A_SQATTR, 0); + ufs_wreg(ufs, sqdao + A_SQRTC, FIELD_DP32(0, SQRTC, ICU, 1)); + sqrts = ufs_rreg(ufs, sqdao + A_SQRTS); + g_assert_cmpuint(FIELD_EX32(sqrts, SQRTS, CUS), ==, 1); + g_assert_cmpuint(FIELD_EX32(sqrts, SQRTS, RTC), ==, 3); + ufs_wreg(ufs, mcq_reg_offset + A_SQATTR, orig_sqattr); + + guest_free(alloc, utmrlba); + ufs_exit(ufs, alloc); +} + #define ZONE_SIZE (2 * 1024 * 1024) #define ZONE_BLOCKS (ZONE_SIZE / 4096) #define MAX_OPEN_ZONE 6 @@ -3205,6 +3363,7 @@ static void ufs_register_nodes(void) qos_add_test("query-desciptor", "ufs", ufstest_query_desc_request, &io_test_opts); qos_add_test("hce-reset", "ufs", ufs_test_hce_reset, &mcq_test_opts); + qos_add_test("tmr-handling", "ufs", ufs_test_tmr_handling, &mcq_test_opts); qos_add_test("wb-init", "ufs", ufstest_wb_init, &wb_test_opts); qos_add_test("wb-read-write", "ufs", ufstest_wb_read_write, &wb_test_opts); qos_add_test("hid", "ufs", ufstest_hid, &io_test_opts); -- 2.56.0.218.g1ec904b173-goog
