diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 5d6c44b00bc2..e9f1a19dfe3f 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -2189,6 +2189,17 @@ static int mmc_blk_err_check(struct mmc_card *card, int need_retune = card->host->need_retune; int ecc_err = 0, gen_err = 0; + if (card->host->sdr104_wa && mmc_card_sd(card) && + (card->host->ios.timing == MMC_TIMING_UHS_SDR104) && + !card->sdr104_blocked && + (brq->data.error == -EILSEQ || + brq->data.error == -EIO || + brq->data.error == -ETIMEDOUT || + brq->cmd.error == -EILSEQ || + brq->cmd.error == -EIO || + brq->cmd.error == -ETIMEDOUT)) + card->err_in_sdr104 = true; + /* * sbc.error indicates a problem with the set block count * command. No data will have been transferred. @@ -3645,6 +3656,7 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc) struct mmc_async_req *areq; const u8 packed_nr = 2; u8 reqs = 0; + bool reset = false; #ifdef CONFIG_MMC_SIMULATE_MAX_SPEED unsigned long waitfor = jiffies; #endif @@ -3690,6 +3702,26 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc) type = rq_data_dir(req) == READ ? MMC_BLK_READ : MMC_BLK_WRITE; mmc_queue_bounce_post(mq_rq); + if (card->err_in_sdr104) { + /* + * Data CRC/timeout errors will manifest as CMD/DATA + * ERR. But we'd like to retry these too. + * Moreover, no harm done if this fails too for multiple + * times, we anyway reduce the bus-speed and retry the + * same request. + * If that fails too, we don't override this status. + */ + if (status == MMC_BLK_ABORT || + status == MMC_BLK_CMD_ERR || + status == MMC_BLK_DATA_ERR || + status == MMC_BLK_RETRY) + /* reset on all of these errors and retry */ + reset = true; + + status = MMC_BLK_RETRY; + card->err_in_sdr104 = false; + } + switch (status) { case MMC_BLK_SUCCESS: case MMC_BLK_PARTIAL: @@ -3730,8 +3762,32 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc) break; case MMC_BLK_RETRY: retune_retry_done = brq->retune_retry_done; - if (retry++ < MMC_BLK_MAX_RETRIES) + if (retry++ < MMC_BLK_MAX_RETRIES) { break; + } else if (reset) { + reset = false; + /* + * If we exhaust all the retries due to + * CRC/timeout errors in SDR140 mode with UHS SD + * cards, re-configure the card in SDR50 + * bus-speed mode. + * All subsequent re-init of this card will be + * in SDR50 mode, unless it is removed and + * re-inserted. When new UHS SD cards are + * inserted, it may start at SDR104 mode if + * supported by the card. + */ + pr_err("%s: blocked SDR104, lower the bus-speed (SDR50 / DDR50)\n", + req->rq_disk->disk_name); + mmc_host_clear_sdr104(card->host); + mmc_suspend_clk_scaling(card->host); + mmc_blk_reset(md, card->host, type); + /* SDR104 mode is blocked from now on */ + card->sdr104_blocked = true; + /* retry 5 times again */ + retry = 0; + break; + } /* Fall through */ case MMC_BLK_ABORT: if (!mmc_blk_reset(md, card->host, type) && diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 5396e1d00178..11a83126ad1b 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -4033,6 +4033,10 @@ int _mmc_detect_card_removed(struct mmc_host *host) if (ret) { mmc_card_set_removed(host->card); + if (host->card->sdr104_blocked) { + mmc_host_set_sdr104(host); + host->card->sdr104_blocked = false; + } pr_debug("%s: card remove detected\n", mmc_hostname(host)); } diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index 1116544eebc1..c66187299598 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -80,7 +80,6 @@ void mmc_init_context_info(struct mmc_host *host); extern bool mmc_can_scale_clk(struct mmc_host *host); extern int mmc_init_clk_scaling(struct mmc_host *host); -extern int mmc_suspend_clk_scaling(struct mmc_host *host); extern int mmc_resume_clk_scaling(struct mmc_host *host); extern int mmc_exit_clk_scaling(struct mmc_host *host); extern unsigned long mmc_get_max_frequency(struct mmc_host *host); diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 9e147a667edf..5b4d5d74fe55 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -1285,6 +1285,8 @@ static int _mmc_sd_resume(struct mmc_host *host) #endif mmc_card_clr_suspended(host->card); + if (host->card->sdr104_blocked) + goto out; err = mmc_resume_clk_scaling(host); if (err) { pr_err("%s: %s: fail to resume clock scaling (%d)\n", diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 1c87478b5fc0..e7e172a23a16 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -435,6 +435,8 @@ struct mmc_card { u8 *cached_ext_csd; bool cmdq_init; struct mmc_bkops_info bkops; + bool err_in_sdr104; + bool sdr104_blocked; }; /* diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h index 1068953943d8..2a1a6fec179f 100644 --- a/include/linux/mmc/core.h +++ b/include/linux/mmc/core.h @@ -169,6 +169,7 @@ extern int __mmc_switch_cmdq_mode(struct mmc_command *cmd, u8 set, u8 index, extern int mmc_send_tuning(struct mmc_host *host, u32 opcode, int *cmd_error); extern int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd); extern int mmc_set_auto_bkops(struct mmc_card *card, bool enable); +extern int mmc_suspend_clk_scaling(struct mmc_host *host); #define MMC_ERASE_ARG 0x00000000 #define MMC_SECURE_ERASE_ARG 0x80000000 diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 37069eb12861..d9e12c1b1748 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -729,6 +729,16 @@ static inline int mmc_host_uhs(struct mmc_host *host) MMC_CAP_UHS_DDR50); } +static inline void mmc_host_clear_sdr104(struct mmc_host *host) +{ + host->caps &= ~MMC_CAP_UHS_SDR104; +} + +static inline void mmc_host_set_sdr104(struct mmc_host *host) +{ + host->caps |= MMC_CAP_UHS_SDR104; +} + static inline int mmc_host_packed_wr(struct mmc_host *host) { return host->caps2 & MMC_CAP2_PACKED_WR;