diff --git a/tests/qtest/ufs-test.c b/tests/qtest/ufs-test.c index 8cfdc41d3c..7f99944155 100644 --- a/tests/qtest/ufs-test.c +++ b/tests/qtest/ufs-test.c @@ -167,6 +167,7 @@ __ufs_send_transfer_request_mcq(QUfs *ufs, uint8_t lun, cqhp = ufs_rreg(ufs, ufs->cqdao[TEST_QID]); cqentry_addr = ufs->cqlba[TEST_QID] + cqhp; qtest_memread(ufs->dev.bus->qts, cqentry_addr, &cqentry, sizeof(cqentry)); + cqhp = (cqhp + sizeof(UfsCqEntry)) % (QUEUE_SIZE * sizeof(UfsCqEntry)); ufs_wreg(ufs, ufs->cqdao[TEST_QID], cqhp); return cqentry.status; @@ -209,6 +210,81 @@ static enum UtpOcsCodes ufs_send_nop_out(QUfs *ufs, UtpUpiuRsp *rsp_out) return ret; } +static bool ufs_mcq_sq_has_space(QUfs *ufs) +{ + uint32_t sqhp = ufs_rreg(ufs, ufs->sqdao[TEST_QID]); + uint32_t sqtp = ufs_rreg(ufs, ufs->sqdao[TEST_QID] + 0x4); + uint32_t next_sqtp = + (sqtp + sizeof(UfsSqEntry)) % (QUEUE_SIZE * sizeof(UfsSqEntry)); + return next_sqtp != sqhp; +} + +static void +__ufs_send_transfer_request_mcq_async(QUfs *ufs, uint8_t lun, + const UtpTransferReqDesc *utrd) +{ + uint32_t sqtp; + uint64_t utrd_addr; + + /* Wait for SQ space */ + while (!ufs_mcq_sq_has_space(ufs)) { + qtest_clock_step(ufs->dev.bus->qts, 100); + } + + sqtp = ufs_rreg(ufs, ufs->sqdao[TEST_QID] + 0x4); + utrd_addr = ufs->sqlba[TEST_QID] + sqtp; + qtest_memwrite(ufs->dev.bus->qts, utrd_addr, utrd, sizeof(*utrd)); + sqtp = (sqtp + sizeof(UfsSqEntry)) % (QUEUE_SIZE * sizeof(UfsSqEntry)); + ufs_wreg(ufs, ufs->sqdao[TEST_QID] + 0x4, sqtp); +} + +static int ufs_mcq_send_nop_out_async(QUfs *ufs) +{ + int cmd_desc_slot = alloc_cmd_desc_slot(ufs); + uint64_t req_upiu_addr = + ufs->cmd_desc_addr + cmd_desc_slot * UTP_COMMAND_DESCRIPTOR_SIZE; + + /* Build up request upiu */ + UtpUpiuReq req_upiu = { 0 }; + req_upiu.header.trans_type = UFS_UPIU_TRANSACTION_NOP_OUT; + req_upiu.header.task_tag = cmd_desc_slot; + qtest_memwrite(ufs->dev.bus->qts, req_upiu_addr, &req_upiu, + sizeof(req_upiu)); + + /* Build up utp transfer request descriptor */ + UtpTransferReqDesc utrd = + ufs_build_req_utrd(req_upiu_addr, UFS_UTP_NO_DATA_TRANSFER, 0); + + /* Send Transfer Request */ + __ufs_send_transfer_request_mcq_async(ufs, 0, &utrd); + + return cmd_desc_slot; +} + +static int ufs_mcq_poll_cq(QUfs *ufs, UfsCqEntry *cqe, uint16_t n_cqe) +{ + uint32_t cqhp, cqtp; + uint64_t cqe_addr; + int ix = 0; + + cqhp = ufs_rreg(ufs, ufs->cqdao[TEST_QID]); + cqtp = ufs_rreg(ufs, ufs->cqdao[TEST_QID] + 0x4); + + while (cqhp != cqtp && ix < n_cqe) { + /* read completion entry */ + cqe_addr = ufs->cqlba[TEST_QID] + cqhp; + qtest_memread(ufs->dev.bus->qts, cqe_addr, &cqe[ix], sizeof(cqe[ix])); + + /* advance completion queue head pointer */ + cqhp = (cqhp + sizeof(UfsCqEntry)) % (QUEUE_SIZE * sizeof(UfsCqEntry)); + ix++; + } + + ufs_wreg(ufs, ufs->cqdao[TEST_QID], cqhp); + + return ix; +} + static enum UtpOcsCodes ufs_send_query(QUfs *ufs, uint8_t query_function, uint8_t query_opcode, uint8_t idn, uint8_t index, uint8_t selector, @@ -417,6 +493,7 @@ static void ufs_init(QUfs *ufs, QGuestAllocator *alloc) ufs_wreg(ufs, A_UTRIACR, 0); /* Enable transfer request */ + bitmap_zero(ufs->cmd_desc_bitmap, UFS_MAX_CMD_DESC); ufs->cmd_desc_addr = guest_alloc(alloc, UFS_MAX_CMD_DESC * UTP_COMMAND_DESCRIPTOR_SIZE); ufs->data_buffer_addr = @@ -680,6 +757,53 @@ static void ufstest_read_write(void *obj, void *data, QGuestAllocator *alloc) ufs_exit(ufs, alloc); } +static void ufstest_mcq_cq_wraparound(void *obj, void *data, + QGuestAllocator *alloc) +{ + QUfs *ufs = obj; + UfsCqEntry cqe[QUEUE_SIZE]; + const int num_requests = QUEUE_SIZE; + int i, completed = 0; + + ufs_init(ufs, alloc); + + /* Ensure MCQ is supported */ + g_assert_true(ufs->support_mcq); + + for (i = 0; i < num_requests; ++i) { + ufs_mcq_send_nop_out_async(ufs); + } + + while (completed != num_requests) { + int n_cqe = ufs_mcq_poll_cq(ufs, cqe, ARRAY_SIZE(cqe)); + if (!n_cqe) { + break; + } + + for (i = 0; i < n_cqe; ++i) { + uint64_t ucdba; + uint64_t rsp_upiu_addr; + UtpUpiuRsp rsp_upiu; + uint8_t tag; + + g_assert_cmpuint(cqe[i].status, ==, UFS_OCS_SUCCESS); + + ucdba = le64_to_cpu(cqe[i].utp_addr) & MAKE_64BIT_MASK(7, 57); + rsp_upiu_addr = ucdba + UTP_RESPONSE_UPIU_OFFSET; + qtest_memread(ufs->dev.bus->qts, rsp_upiu_addr, &rsp_upiu, + sizeof(rsp_upiu)); + + tag = rsp_upiu.header.task_tag; + release_cmd_desc_slot(ufs, tag); + } + + completed += n_cqe; + } + + g_assert_cmpint(completed, ==, num_requests); + ufs_exit(ufs, alloc); +} + static void ufstest_query_flag_request(void *obj, void *data, QGuestAllocator *alloc) { @@ -1130,6 +1254,8 @@ static void ufs_register_nodes(void) qos_add_test("init", "ufs", ufstest_init, NULL); qos_add_test("legacy-read-write", "ufs", ufstest_read_write, &io_test_opts); qos_add_test("mcq-read-write", "ufs", ufstest_read_write, &mcq_test_opts); + qos_add_test("mcq-cq-wraparound", "ufs", ufstest_mcq_cq_wraparound, + &mcq_test_opts); qos_add_test("query-flag", "ufs", ufstest_query_flag_request, &io_test_opts); qos_add_test("query-attribute", "ufs", ufstest_query_attr_request,