Loading drivers/mmc/host/cmdq_hci.c +79 −6 Original line number Diff line number Diff line Loading @@ -761,6 +761,13 @@ static int cmdq_request(struct mmc_host *mmc, struct mmc_request *mrq) ring_doorbell: /* Ensure the task descriptor list is flushed before ringing doorbell */ wmb(); if (test_bit(CMDQ_STATE_ERR, &mmc->cmdq_ctx.curr_state)) { pr_err("%s: %s: CQ in err state, ending current req\n", mmc_hostname(mmc), __func__); return 0; } if (cmdq_readl(cq_host, CQTDBR) & (1 << tag)) { cmdq_dumpregs(cq_host); BUG_ON(1); Loading Loading @@ -796,10 +803,10 @@ static void cmdq_finish_data(struct mmc_host *mmc, unsigned int tag) irqreturn_t cmdq_irq(struct mmc_host *mmc, int err) { u32 status; unsigned long tag = 0, comp_status; unsigned long tag = 0, err_tag = 0, comp_status = 0; struct cmdq_host *cq_host = (struct cmdq_host *)mmc_cmdq_private(mmc); unsigned long err_info = 0; struct mmc_request *mrq; struct mmc_request *mrq = NULL, *err_mrq; int ret; u32 dbr_set = 0; Loading Loading @@ -886,7 +893,7 @@ irqreturn_t cmdq_irq(struct mmc_host *mmc, int err) mrq->cmd->error = err; else mrq->data->error = err; } else if (err_info & CQ_DTEFV) { } else { tag = GET_DAT_ERR_TAG(err_info); pr_err("%s: Dat err tag: %lu\n", __func__, tag); mrq = get_req_by_tag(cq_host, tag); Loading @@ -913,6 +920,7 @@ skip_cqterri: * CQE detected a reponse error from device * In most cases, this would require a reset. */ comp_status = cmdq_readl(cq_host, CQTCN); if (status & CQIS_RED) { /* * will check if the RED error is due to a bkops Loading @@ -926,10 +934,31 @@ skip_cqterri: mrq->cmdq_req->resp_err = true; pr_err("%s: Response error (0x%08x) from card !!!", mmc_hostname(mmc), cmdq_readl(cq_host, CQCRA)); } else { /* * If RED error is detected for WP violation, there is * a chance that the error occurred on the previous * executed task which has a completion notification. * So handle request completion in error handler */ if (cmdq_readl(cq_host, CQCRA) & CQ_WP_RED) { for_each_set_bit(err_tag, &comp_status, cq_host->num_slots) { /* set err the corresponding mrq */ err_mrq = get_req_by_tag(cq_host, err_tag); err_mrq->cmdq_req->resp_err = true; } } } /* * The following register info are needed for error recovery */ mrq->cmdq_req->resp_idx = cmdq_readl(cq_host, CQCRI); mrq->cmdq_req->resp_arg = cmdq_readl(cq_host, CQCRA); } mrq->cmdq_req->dev_pend = cmdq_readl(cq_host, CQDPT); mrq->cmdq_req->err_info = err_info; mrq->cmdq_req->cqtcn = comp_status; cmdq_finish_data(mmc, tag); } else { Loading Loading @@ -964,6 +993,9 @@ skip_cqterri: MMC_TRACE(mmc, "%s: completing tag -> %lu\n", __func__, tag); cmdq_finish_data(mmc, tag); } else { pr_err("%s: tag:%lu finish_data already done\n", mmc_hostname(mmc), tag); } } } Loading Loading @@ -1125,6 +1157,45 @@ static void cmdq_dumpstate(struct mmc_host *mmc) cmdq_runtime_pm_put(cq_host); } static struct mmc_request *cmdq_get_mrq_by_tag(struct mmc_host *mmc, int tag) { struct cmdq_host *cq_host = (struct cmdq_host *)mmc_cmdq_private(mmc); struct mmc_request *mrq = get_req_by_tag(cq_host, tag); return mrq; } static void cmdq_err_info(struct mmc_host *mmc, struct mmc_cmdq_err_info *err_data, struct mmc_request *mrq) { err_data->remove_task = false; err_data->fail_comp_task = false; err_data->fail_dev_pend = false; err_data->timedout = false; err_data->max_slot = NUM_SLOTS; err_data->dcmd_slot = DCMD_SLOT; err_data->comp_status = mrq->cmdq_req->cqtcn; err_data->cq_terri = mrq->cmdq_req->err_info; err_data->dev_pend = mrq->cmdq_req->dev_pend; err_data->resp_err = mrq->cmdq_req->resp_err; err_data->tag = mrq->cmdq_req->tag; mrq->cmdq_req->cqtcn = 0; mrq->cmdq_req->err_info = 0; mrq->cmdq_req->dev_pend = 0; if (mrq->cmdq_req->resp_err) mrq->cmdq_req->resp_err = false; err_data->data_cmd = GET_DAT_ERR_CMD(err_data->cq_terri); err_data->data_tag = GET_DAT_ERR_TAG(err_data->cq_terri); err_data->data_valid = (err_data->cq_terri & CQ_DTEFV); err_data->cmd = GET_CMD_ERR_CMD(err_data->cq_terri); err_data->cmd_tag = GET_CMD_ERR_TAG(err_data->cq_terri); err_data->cmd_valid = (err_data->cq_terri & CQ_RMEFV); } static int cmdq_late_init(struct mmc_host *mmc) { struct sdhci_host *host = mmc_priv(mmc); Loading Loading @@ -1152,6 +1223,8 @@ static const struct mmc_cmdq_host_ops cmdq_host_ops = { .halt = cmdq_halt, .reset = cmdq_reset, .dumpstate = cmdq_dumpstate, .err_info = cmdq_err_info, .get_mrq_by_tag = cmdq_get_mrq_by_tag, }; struct cmdq_host *cmdq_pltfm_init(struct platform_device *pdev) Loading drivers/mmc/host/cmdq_hci.h +2 −0 Original line number Diff line number Diff line Loading @@ -103,6 +103,8 @@ #define GET_CMD_ERR_TAG(__r__) ((__r__ & CQ_RMETI) >> 8) #define GET_DAT_ERR_TAG(__r__) ((__r__ & CQ_DTETI) >> 24) #define GET_CMD_ERR_CMD(__r__) (__r__ & 0x3F) #define GET_DAT_ERR_CMD(__r__) ((__r__ & 0x3F0000) >> 16) /* command response index */ #define CQCRI 0x58 Loading include/linux/mmc/host.h +42 −1 Original line number Diff line number Diff line Loading @@ -90,6 +90,41 @@ enum mmc_load { MMC_LOAD_LOW, }; /* RED error flags to detect type of RED error */ #define CQ_CMD_RED_ERR (R1_BLOCK_LEN_ERROR | R1_ERASE_SEQ_ERROR | \ R1_ERASE_PARAM | R1_LOCK_UNLOCK_FAILED | R1_ILLEGAL_COMMAND | \ R1_CID_CSD_OVERWRITE | R1_SWITCH_ERROR) #define CQ_WP_RED (R1_WP_VIOLATION | R1_WP_ERASE_SKIP) #define CQ_RED_RETRY (R1_COM_CRC_ERROR | R1_CARD_ECC_FAILED | R1_CC_ERROR |\ R1_ERROR | R1_ERASE_RESET | R1_EXCEPTION_EVENT) #define CQ_ADDR_ERR (R1_OUT_OF_RANGE | R1_ADDRESS_ERROR) #define CQ_RED (CQ_WP_RED | CQ_CMD_RED_ERR | CQ_RED_RETRY | CQ_ADDR_ERR) struct mmc_cmdq_err_info { bool remove_task; bool fail_comp_task; bool fail_dev_pend; bool timedout; unsigned long comp_status; u32 cq_terri; unsigned long dev_pend; bool resp_err; int tag; int data_cmd; int data_tag; bool data_valid; int cmd; int cmd_tag; bool cmd_valid; int max_slot; int dcmd_slot; }; struct mmc_cmdq_host_ops { int (*init)(struct mmc_host *host); int (*enable)(struct mmc_host *host); Loading @@ -99,6 +134,10 @@ struct mmc_cmdq_host_ops { int (*halt)(struct mmc_host *host, bool halt); void (*reset)(struct mmc_host *host, bool soft); void (*dumpstate)(struct mmc_host *host); void (*err_info)(struct mmc_host *host, struct mmc_cmdq_err_info *err_data, struct mmc_request *mrq); struct mmc_request *(*get_mrq_by_tag)(struct mmc_host *mmc, int tag); }; struct mmc_host_ops { Loading Loading @@ -201,10 +240,12 @@ struct mmc_cmdq_req { unsigned int resp_idx; unsigned int resp_arg; unsigned int dev_pend_tasks; unsigned int dev_pend; bool resp_err; int tag; /* used for command queuing */ u8 ctx_id; u32 err_info; unsigned long cqtcn; }; struct mmc_async_req { Loading include/uapi/linux/mmc/mmc.h +6 −0 Original line number Diff line number Diff line Loading @@ -65,4 +65,10 @@ #define MMC_APP_CMD 55 /* ac [31:16] RCA R1 */ #define MMC_GEN_CMD 56 /* adtc [0] RD/WR R1 */ /* class 11 */ #define MMC_CMDQ_TASK_PARAM 44 /* ac R1 */ #define MMC_CMDQ_TASK_ADDR 45 /* ac [31:0] blk addr R1 */ #define MMC_CMDQ_READ_TASK 46 /* adtc [20:16] Task ID R1 */ #define MMC_CMDQ_WRITE_TASK 47 /* adtc [20:16] Task ID R1 */ #endif /* UAPI_MMC_MMC_H */ Loading
drivers/mmc/host/cmdq_hci.c +79 −6 Original line number Diff line number Diff line Loading @@ -761,6 +761,13 @@ static int cmdq_request(struct mmc_host *mmc, struct mmc_request *mrq) ring_doorbell: /* Ensure the task descriptor list is flushed before ringing doorbell */ wmb(); if (test_bit(CMDQ_STATE_ERR, &mmc->cmdq_ctx.curr_state)) { pr_err("%s: %s: CQ in err state, ending current req\n", mmc_hostname(mmc), __func__); return 0; } if (cmdq_readl(cq_host, CQTDBR) & (1 << tag)) { cmdq_dumpregs(cq_host); BUG_ON(1); Loading Loading @@ -796,10 +803,10 @@ static void cmdq_finish_data(struct mmc_host *mmc, unsigned int tag) irqreturn_t cmdq_irq(struct mmc_host *mmc, int err) { u32 status; unsigned long tag = 0, comp_status; unsigned long tag = 0, err_tag = 0, comp_status = 0; struct cmdq_host *cq_host = (struct cmdq_host *)mmc_cmdq_private(mmc); unsigned long err_info = 0; struct mmc_request *mrq; struct mmc_request *mrq = NULL, *err_mrq; int ret; u32 dbr_set = 0; Loading Loading @@ -886,7 +893,7 @@ irqreturn_t cmdq_irq(struct mmc_host *mmc, int err) mrq->cmd->error = err; else mrq->data->error = err; } else if (err_info & CQ_DTEFV) { } else { tag = GET_DAT_ERR_TAG(err_info); pr_err("%s: Dat err tag: %lu\n", __func__, tag); mrq = get_req_by_tag(cq_host, tag); Loading @@ -913,6 +920,7 @@ skip_cqterri: * CQE detected a reponse error from device * In most cases, this would require a reset. */ comp_status = cmdq_readl(cq_host, CQTCN); if (status & CQIS_RED) { /* * will check if the RED error is due to a bkops Loading @@ -926,10 +934,31 @@ skip_cqterri: mrq->cmdq_req->resp_err = true; pr_err("%s: Response error (0x%08x) from card !!!", mmc_hostname(mmc), cmdq_readl(cq_host, CQCRA)); } else { /* * If RED error is detected for WP violation, there is * a chance that the error occurred on the previous * executed task which has a completion notification. * So handle request completion in error handler */ if (cmdq_readl(cq_host, CQCRA) & CQ_WP_RED) { for_each_set_bit(err_tag, &comp_status, cq_host->num_slots) { /* set err the corresponding mrq */ err_mrq = get_req_by_tag(cq_host, err_tag); err_mrq->cmdq_req->resp_err = true; } } } /* * The following register info are needed for error recovery */ mrq->cmdq_req->resp_idx = cmdq_readl(cq_host, CQCRI); mrq->cmdq_req->resp_arg = cmdq_readl(cq_host, CQCRA); } mrq->cmdq_req->dev_pend = cmdq_readl(cq_host, CQDPT); mrq->cmdq_req->err_info = err_info; mrq->cmdq_req->cqtcn = comp_status; cmdq_finish_data(mmc, tag); } else { Loading Loading @@ -964,6 +993,9 @@ skip_cqterri: MMC_TRACE(mmc, "%s: completing tag -> %lu\n", __func__, tag); cmdq_finish_data(mmc, tag); } else { pr_err("%s: tag:%lu finish_data already done\n", mmc_hostname(mmc), tag); } } } Loading Loading @@ -1125,6 +1157,45 @@ static void cmdq_dumpstate(struct mmc_host *mmc) cmdq_runtime_pm_put(cq_host); } static struct mmc_request *cmdq_get_mrq_by_tag(struct mmc_host *mmc, int tag) { struct cmdq_host *cq_host = (struct cmdq_host *)mmc_cmdq_private(mmc); struct mmc_request *mrq = get_req_by_tag(cq_host, tag); return mrq; } static void cmdq_err_info(struct mmc_host *mmc, struct mmc_cmdq_err_info *err_data, struct mmc_request *mrq) { err_data->remove_task = false; err_data->fail_comp_task = false; err_data->fail_dev_pend = false; err_data->timedout = false; err_data->max_slot = NUM_SLOTS; err_data->dcmd_slot = DCMD_SLOT; err_data->comp_status = mrq->cmdq_req->cqtcn; err_data->cq_terri = mrq->cmdq_req->err_info; err_data->dev_pend = mrq->cmdq_req->dev_pend; err_data->resp_err = mrq->cmdq_req->resp_err; err_data->tag = mrq->cmdq_req->tag; mrq->cmdq_req->cqtcn = 0; mrq->cmdq_req->err_info = 0; mrq->cmdq_req->dev_pend = 0; if (mrq->cmdq_req->resp_err) mrq->cmdq_req->resp_err = false; err_data->data_cmd = GET_DAT_ERR_CMD(err_data->cq_terri); err_data->data_tag = GET_DAT_ERR_TAG(err_data->cq_terri); err_data->data_valid = (err_data->cq_terri & CQ_DTEFV); err_data->cmd = GET_CMD_ERR_CMD(err_data->cq_terri); err_data->cmd_tag = GET_CMD_ERR_TAG(err_data->cq_terri); err_data->cmd_valid = (err_data->cq_terri & CQ_RMEFV); } static int cmdq_late_init(struct mmc_host *mmc) { struct sdhci_host *host = mmc_priv(mmc); Loading Loading @@ -1152,6 +1223,8 @@ static const struct mmc_cmdq_host_ops cmdq_host_ops = { .halt = cmdq_halt, .reset = cmdq_reset, .dumpstate = cmdq_dumpstate, .err_info = cmdq_err_info, .get_mrq_by_tag = cmdq_get_mrq_by_tag, }; struct cmdq_host *cmdq_pltfm_init(struct platform_device *pdev) Loading
drivers/mmc/host/cmdq_hci.h +2 −0 Original line number Diff line number Diff line Loading @@ -103,6 +103,8 @@ #define GET_CMD_ERR_TAG(__r__) ((__r__ & CQ_RMETI) >> 8) #define GET_DAT_ERR_TAG(__r__) ((__r__ & CQ_DTETI) >> 24) #define GET_CMD_ERR_CMD(__r__) (__r__ & 0x3F) #define GET_DAT_ERR_CMD(__r__) ((__r__ & 0x3F0000) >> 16) /* command response index */ #define CQCRI 0x58 Loading
include/linux/mmc/host.h +42 −1 Original line number Diff line number Diff line Loading @@ -90,6 +90,41 @@ enum mmc_load { MMC_LOAD_LOW, }; /* RED error flags to detect type of RED error */ #define CQ_CMD_RED_ERR (R1_BLOCK_LEN_ERROR | R1_ERASE_SEQ_ERROR | \ R1_ERASE_PARAM | R1_LOCK_UNLOCK_FAILED | R1_ILLEGAL_COMMAND | \ R1_CID_CSD_OVERWRITE | R1_SWITCH_ERROR) #define CQ_WP_RED (R1_WP_VIOLATION | R1_WP_ERASE_SKIP) #define CQ_RED_RETRY (R1_COM_CRC_ERROR | R1_CARD_ECC_FAILED | R1_CC_ERROR |\ R1_ERROR | R1_ERASE_RESET | R1_EXCEPTION_EVENT) #define CQ_ADDR_ERR (R1_OUT_OF_RANGE | R1_ADDRESS_ERROR) #define CQ_RED (CQ_WP_RED | CQ_CMD_RED_ERR | CQ_RED_RETRY | CQ_ADDR_ERR) struct mmc_cmdq_err_info { bool remove_task; bool fail_comp_task; bool fail_dev_pend; bool timedout; unsigned long comp_status; u32 cq_terri; unsigned long dev_pend; bool resp_err; int tag; int data_cmd; int data_tag; bool data_valid; int cmd; int cmd_tag; bool cmd_valid; int max_slot; int dcmd_slot; }; struct mmc_cmdq_host_ops { int (*init)(struct mmc_host *host); int (*enable)(struct mmc_host *host); Loading @@ -99,6 +134,10 @@ struct mmc_cmdq_host_ops { int (*halt)(struct mmc_host *host, bool halt); void (*reset)(struct mmc_host *host, bool soft); void (*dumpstate)(struct mmc_host *host); void (*err_info)(struct mmc_host *host, struct mmc_cmdq_err_info *err_data, struct mmc_request *mrq); struct mmc_request *(*get_mrq_by_tag)(struct mmc_host *mmc, int tag); }; struct mmc_host_ops { Loading Loading @@ -201,10 +240,12 @@ struct mmc_cmdq_req { unsigned int resp_idx; unsigned int resp_arg; unsigned int dev_pend_tasks; unsigned int dev_pend; bool resp_err; int tag; /* used for command queuing */ u8 ctx_id; u32 err_info; unsigned long cqtcn; }; struct mmc_async_req { Loading
include/uapi/linux/mmc/mmc.h +6 −0 Original line number Diff line number Diff line Loading @@ -65,4 +65,10 @@ #define MMC_APP_CMD 55 /* ac [31:16] RCA R1 */ #define MMC_GEN_CMD 56 /* adtc [0] RD/WR R1 */ /* class 11 */ #define MMC_CMDQ_TASK_PARAM 44 /* ac R1 */ #define MMC_CMDQ_TASK_ADDR 45 /* ac [31:0] blk addr R1 */ #define MMC_CMDQ_READ_TASK 46 /* adtc [20:16] Task ID R1 */ #define MMC_CMDQ_WRITE_TASK 47 /* adtc [20:16] Task ID R1 */ #endif /* UAPI_MMC_MMC_H */