From c95ecab515064933a8186fadac06a0100e94cef0 Mon Sep 17 00:00:00 2001 From: Yifeng Zhao Date: Tue, 1 Aug 2023 15:43:13 +0800 Subject: [PATCH] mmc: add timeout for write data and reset while recovery Some problematic TF cards may stop at busy state during the data writing process, and the mmc controller needs to add a timeout processing. Signed-off-by: Yifeng Zhao Change-Id: I11b75910f99eb040fa364990049f5cfe74fccfbf --- drivers/mmc/core/block.c | 11 +++++++++-- drivers/mmc/host/dw_mmc.c | 27 +++++++++++++++++++-------- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c index cc355156ebef..c533fbf523b4 100644 --- a/drivers/mmc/core/block.c +++ b/drivers/mmc/core/block.c @@ -416,7 +416,7 @@ static int mmc_blk_ioctl_copy_to_user(struct mmc_ioc_cmd __user *ic_ptr, static int card_busy_detect(struct mmc_card *card, unsigned int timeout_ms, u32 *resp_errs) { - unsigned long timeout = jiffies + msecs_to_jiffies(timeout_ms); + unsigned long timeout = jiffies + msecs_to_jiffies(timeout_ms > 2000 ? 2000 : timeout_ms); int err = 0; u32 status; @@ -1784,8 +1784,15 @@ static void mmc_blk_mq_rw_recovery(struct mmc_queue *mq, struct request *req) * bytes transferred to zero in that case. */ err = __mmc_send_status(card, &status, 0); - if (err || mmc_blk_status_error(req, status)) + if (err || mmc_blk_status_error(req, status)) { brq->data.bytes_xfered = 0; + if (mmc_card_sd(card) && !mmc_card_removed(card)) { + mmc_blk_reset_success(mq->blkdata, type); + if (!mmc_blk_reset(md, card->host, type)) + return; + pr_err("%s: pre recovery failed!\n", req->rq_disk->disk_name); + } + } mmc_retune_release(card->host); diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index b224bcd7fced..59970108daf7 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -527,8 +527,7 @@ static void dw_mci_dmac_complete_dma(void *arg) tasklet_schedule(&host->tasklet); } - if (host->need_xfer_timer && - host->dir_status == DW_MCI_RECV_STATUS) + if (host->need_xfer_timer) del_timer(&host->xfer_timer); } @@ -1871,6 +1870,9 @@ static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq) WARN_ON(host->cmd || host->data); + if (host->need_xfer_timer) + del_timer(&host->xfer_timer); + host->slot->mrq = NULL; host->mrq = NULL; if (!list_empty(&host->queue)) { @@ -2016,8 +2018,10 @@ static void dw_mci_set_xfer_timeout(struct dw_mci *host) host->bus_hz); /* add a bit spare time */ - xfer_ms += 100; - + if (host->dir_status == DW_MCI_RECV_STATUS) + xfer_ms += 100; + else + xfer_ms += 2500; spin_lock_irqsave(&host->irq_lock, irqflags); if (!test_bit(EVENT_XFER_COMPLETE, &host->pending_events)) mod_timer(&host->xfer_timer, @@ -2152,6 +2156,13 @@ static void dw_mci_tasklet_func(unsigned long priv) send_stop_abort(host, data); dw_mci_stop_dma(host); state = STATE_DATA_ERROR; + if (host->dir_status == DW_MCI_SEND_STATUS) { + data->bytes_xfered = 0; + data->error = -ETIMEDOUT; + host->data = NULL; + dw_mci_request_end(host, mrq); + goto unlock; + } break; } @@ -2163,8 +2174,7 @@ static void dw_mci_tasklet_func(unsigned long priv) */ if (host->dir_status == DW_MCI_RECV_STATUS) dw_mci_set_drto(host); - if (host->need_xfer_timer && - host->dir_status == DW_MCI_RECV_STATUS) + if (host->need_xfer_timer) dw_mci_set_xfer_timeout(host); break; } @@ -2206,6 +2216,8 @@ static void dw_mci_tasklet_func(unsigned long priv) */ if (host->dir_status == DW_MCI_RECV_STATUS) dw_mci_set_drto(host); + if (host->need_xfer_timer && host->dir_status == DW_MCI_SEND_STATUS) + dw_mci_set_xfer_timeout(host); break; } @@ -2765,8 +2777,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) del_timer(&host->cto_timer); mci_writel(host, RINTSTS, DW_MCI_CMD_ERROR_FLAGS); host->cmd_status = pending; - if ((host->need_xfer_timer) && - host->dir_status == DW_MCI_RECV_STATUS) + if (host->need_xfer_timer) del_timer(&host->xfer_timer); smp_wmb(); /* drain writebuffer */ set_bit(EVENT_CMD_COMPLETE, &host->pending_events);