MMC core:

- Fix CQE error recovery path
 
 MMC host:
  - cqhci: Fix CQE error recovery path
  - sdhci-pci-gli: Fix initialization of LPM
  - sdhci-sprd: Fix enabling/disabling of the vqmmc regulator
 -----BEGIN PGP SIGNATURE-----
 
 iQJLBAABCgA1FiEEugLDXPmKSktSkQsV/iaEJXNYjCkFAmVoZ1gXHHVsZi5oYW5z
 c29uQGxpbmFyby5vcmcACgkQ/iaEJXNYjCmiRBAAjNrmB5Jg7n52+Oc5RjmTEb1y
 xrwhFo8zPAsGkZY2rTRRabW/0n7W+v0d3d2DK/v0flbv0gBUMsSIKqrNtjfjAYEf
 GRoEdt8esHuRwNLiDdPxUV20HQd/VW64nxCucX1YoO4y8MqOaOXAltB0OfGL5OJ2
 59x76k9e/eXmfCE53Ace3h9xq4oeL0aeg8o+P494fEoYNesKIcj699uRuSJeXZ8B
 lu42jrQjRtsFEmPUxT7nkZnPVNg6ebX4V1Nh00uaqYR7uhuiE0qMW5uCbH5XQvAW
 YSS9JuL0eSJkh5ni7/v1ubbijZAheCrHgfXBIj4tFNAmHXK+HCyABueJLUVpXpu7
 ZbVSJVPmzSOpJn9iJlARovh4vfIhEdgFaryoepOmIgGXn1EY+jR4BqCGytE4wSh+
 OWwvOJo3RImcGpBrOQy9ds0rRLj2Yuz0ifYzU6W6qFmxjvth6druhHIQZWarOURT
 +/FdnG2F0GApGtAJZE4hWobk3FPXLHEstkZXqsuxDE7p6Os1w2gbKmfOmDvNZWHr
 tgBC2qVmQSXyDcxylF/yDIX6B0eAjrZnq7px3uD6IdSF30KN2w1SQ7giaYBGqExr
 TAbom/u2u7i73h4XaRQHoftBouzc6QBpo2OzBYeolkJgVJCx8sHj7BU1gBstp033
 /kgogUJSgMGBcZeREhg=
 =G0uq
 -----END PGP SIGNATURE-----

Merge tag 'mmc-v6.7-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc

Pull MMC fixes from Ulf Hansson:
 "MMC core:
   - Fix CQE error recovery path

  MMC host:
   - cqhci: Fix CQE error recovery path
   - sdhci-pci-gli: Fix initialization of LPM
   - sdhci-sprd: Fix enabling/disabling of the vqmmc regulator"

* tag 'mmc-v6.7-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc:
  mmc: sdhci-sprd: Fix vqmmc not shutting down after the card was pulled
  mmc: sdhci-pci-gli: Disable LPM during initialization
  mmc: cqhci: Fix task clearing in CQE error recovery
  mmc: cqhci: Warn of halt or task clear failure
  mmc: block: Retry commands in CQE error recovery
  mmc: block: Be sure to wait while busy in CQE error recovery
  mmc: cqhci: Increase recovery halt timeout
  mmc: block: Do not lose cache flush during CQE error recovery
This commit is contained in:
Linus Torvalds 2023-12-01 08:15:05 +09:00
commit 09443a144c
5 changed files with 85 additions and 49 deletions

View File

@ -1482,6 +1482,8 @@ static void mmc_blk_cqe_complete_rq(struct mmc_queue *mq, struct request *req)
blk_mq_requeue_request(req, true); blk_mq_requeue_request(req, true);
else else
__blk_mq_end_request(req, BLK_STS_OK); __blk_mq_end_request(req, BLK_STS_OK);
} else if (mq->in_recovery) {
blk_mq_requeue_request(req, true);
} else { } else {
blk_mq_end_request(req, BLK_STS_OK); blk_mq_end_request(req, BLK_STS_OK);
} }

View File

@ -551,7 +551,9 @@ int mmc_cqe_recovery(struct mmc_host *host)
cmd.flags = MMC_RSP_R1B | MMC_CMD_AC; cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
cmd.flags &= ~MMC_RSP_CRC; /* Ignore CRC */ cmd.flags &= ~MMC_RSP_CRC; /* Ignore CRC */
cmd.busy_timeout = MMC_CQE_RECOVERY_TIMEOUT; cmd.busy_timeout = MMC_CQE_RECOVERY_TIMEOUT;
mmc_wait_for_cmd(host, &cmd, 0); mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
mmc_poll_for_busy(host->card, MMC_CQE_RECOVERY_TIMEOUT, true, MMC_BUSY_IO);
memset(&cmd, 0, sizeof(cmd)); memset(&cmd, 0, sizeof(cmd));
cmd.opcode = MMC_CMDQ_TASK_MGMT; cmd.opcode = MMC_CMDQ_TASK_MGMT;
@ -559,10 +561,13 @@ int mmc_cqe_recovery(struct mmc_host *host)
cmd.flags = MMC_RSP_R1B | MMC_CMD_AC; cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
cmd.flags &= ~MMC_RSP_CRC; /* Ignore CRC */ cmd.flags &= ~MMC_RSP_CRC; /* Ignore CRC */
cmd.busy_timeout = MMC_CQE_RECOVERY_TIMEOUT; cmd.busy_timeout = MMC_CQE_RECOVERY_TIMEOUT;
err = mmc_wait_for_cmd(host, &cmd, 0); err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
host->cqe_ops->cqe_recovery_finish(host); host->cqe_ops->cqe_recovery_finish(host);
if (err)
err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
mmc_retune_release(host); mmc_retune_release(host);
return err; return err;

View File

@ -942,8 +942,8 @@ static bool cqhci_clear_all_tasks(struct mmc_host *mmc, unsigned int timeout)
ret = cqhci_tasks_cleared(cq_host); ret = cqhci_tasks_cleared(cq_host);
if (!ret) if (!ret)
pr_debug("%s: cqhci: Failed to clear tasks\n", pr_warn("%s: cqhci: Failed to clear tasks\n",
mmc_hostname(mmc)); mmc_hostname(mmc));
return ret; return ret;
} }
@ -976,7 +976,7 @@ static bool cqhci_halt(struct mmc_host *mmc, unsigned int timeout)
ret = cqhci_halted(cq_host); ret = cqhci_halted(cq_host);
if (!ret) if (!ret)
pr_debug("%s: cqhci: Failed to halt\n", mmc_hostname(mmc)); pr_warn("%s: cqhci: Failed to halt\n", mmc_hostname(mmc));
return ret; return ret;
} }
@ -984,10 +984,10 @@ static bool cqhci_halt(struct mmc_host *mmc, unsigned int timeout)
/* /*
* After halting we expect to be able to use the command line. We interpret the * After halting we expect to be able to use the command line. We interpret the
* failure to halt to mean the data lines might still be in use (and the upper * failure to halt to mean the data lines might still be in use (and the upper
* layers will need to send a STOP command), so we set the timeout based on a * layers will need to send a STOP command), however failing to halt complicates
* generous command timeout. * the recovery, so set a timeout that would reasonably allow I/O to complete.
*/ */
#define CQHCI_START_HALT_TIMEOUT 5 #define CQHCI_START_HALT_TIMEOUT 500
static void cqhci_recovery_start(struct mmc_host *mmc) static void cqhci_recovery_start(struct mmc_host *mmc)
{ {
@ -1075,28 +1075,28 @@ static void cqhci_recovery_finish(struct mmc_host *mmc)
ok = cqhci_halt(mmc, CQHCI_FINISH_HALT_TIMEOUT); ok = cqhci_halt(mmc, CQHCI_FINISH_HALT_TIMEOUT);
if (!cqhci_clear_all_tasks(mmc, CQHCI_CLEAR_TIMEOUT))
ok = false;
/* /*
* The specification contradicts itself, by saying that tasks cannot be * The specification contradicts itself, by saying that tasks cannot be
* cleared if CQHCI does not halt, but if CQHCI does not halt, it should * cleared if CQHCI does not halt, but if CQHCI does not halt, it should
* be disabled/re-enabled, but not to disable before clearing tasks. * be disabled/re-enabled, but not to disable before clearing tasks.
* Have a go anyway. * Have a go anyway.
*/ */
if (!ok) { if (!cqhci_clear_all_tasks(mmc, CQHCI_CLEAR_TIMEOUT))
pr_debug("%s: cqhci: disable / re-enable\n", mmc_hostname(mmc)); ok = false;
cqcfg = cqhci_readl(cq_host, CQHCI_CFG);
cqcfg &= ~CQHCI_ENABLE; /* Disable to make sure tasks really are cleared */
cqhci_writel(cq_host, cqcfg, CQHCI_CFG); cqcfg = cqhci_readl(cq_host, CQHCI_CFG);
cqcfg |= CQHCI_ENABLE; cqcfg &= ~CQHCI_ENABLE;
cqhci_writel(cq_host, cqcfg, CQHCI_CFG); cqhci_writel(cq_host, cqcfg, CQHCI_CFG);
/* Be sure that there are no tasks */
ok = cqhci_halt(mmc, CQHCI_FINISH_HALT_TIMEOUT); cqcfg = cqhci_readl(cq_host, CQHCI_CFG);
if (!cqhci_clear_all_tasks(mmc, CQHCI_CLEAR_TIMEOUT)) cqcfg |= CQHCI_ENABLE;
ok = false; cqhci_writel(cq_host, cqcfg, CQHCI_CFG);
WARN_ON(!ok);
} cqhci_halt(mmc, CQHCI_FINISH_HALT_TIMEOUT);
if (!ok)
cqhci_clear_all_tasks(mmc, CQHCI_CLEAR_TIMEOUT);
cqhci_recover_mrqs(cq_host); cqhci_recover_mrqs(cq_host);

View File

@ -1189,6 +1189,32 @@ static void gl9763e_hs400_enhanced_strobe(struct mmc_host *mmc,
sdhci_writel(host, val, SDHCI_GLI_9763E_HS400_ES_REG); sdhci_writel(host, val, SDHCI_GLI_9763E_HS400_ES_REG);
} }
static void gl9763e_set_low_power_negotiation(struct sdhci_pci_slot *slot,
bool enable)
{
struct pci_dev *pdev = slot->chip->pdev;
u32 value;
pci_read_config_dword(pdev, PCIE_GLI_9763E_VHS, &value);
value &= ~GLI_9763E_VHS_REV;
value |= FIELD_PREP(GLI_9763E_VHS_REV, GLI_9763E_VHS_REV_W);
pci_write_config_dword(pdev, PCIE_GLI_9763E_VHS, value);
pci_read_config_dword(pdev, PCIE_GLI_9763E_CFG, &value);
if (enable)
value &= ~GLI_9763E_CFG_LPSN_DIS;
else
value |= GLI_9763E_CFG_LPSN_DIS;
pci_write_config_dword(pdev, PCIE_GLI_9763E_CFG, value);
pci_read_config_dword(pdev, PCIE_GLI_9763E_VHS, &value);
value &= ~GLI_9763E_VHS_REV;
value |= FIELD_PREP(GLI_9763E_VHS_REV, GLI_9763E_VHS_REV_R);
pci_write_config_dword(pdev, PCIE_GLI_9763E_VHS, value);
}
static void sdhci_set_gl9763e_signaling(struct sdhci_host *host, static void sdhci_set_gl9763e_signaling(struct sdhci_host *host,
unsigned int timing) unsigned int timing)
{ {
@ -1297,6 +1323,9 @@ static int gl9763e_add_host(struct sdhci_pci_slot *slot)
if (ret) if (ret)
goto cleanup; goto cleanup;
/* Disable LPM negotiation to avoid entering L1 state. */
gl9763e_set_low_power_negotiation(slot, false);
return 0; return 0;
cleanup: cleanup:
@ -1340,31 +1369,6 @@ static void gli_set_gl9763e(struct sdhci_pci_slot *slot)
} }
#ifdef CONFIG_PM #ifdef CONFIG_PM
static void gl9763e_set_low_power_negotiation(struct sdhci_pci_slot *slot, bool enable)
{
struct pci_dev *pdev = slot->chip->pdev;
u32 value;
pci_read_config_dword(pdev, PCIE_GLI_9763E_VHS, &value);
value &= ~GLI_9763E_VHS_REV;
value |= FIELD_PREP(GLI_9763E_VHS_REV, GLI_9763E_VHS_REV_W);
pci_write_config_dword(pdev, PCIE_GLI_9763E_VHS, value);
pci_read_config_dword(pdev, PCIE_GLI_9763E_CFG, &value);
if (enable)
value &= ~GLI_9763E_CFG_LPSN_DIS;
else
value |= GLI_9763E_CFG_LPSN_DIS;
pci_write_config_dword(pdev, PCIE_GLI_9763E_CFG, value);
pci_read_config_dword(pdev, PCIE_GLI_9763E_VHS, &value);
value &= ~GLI_9763E_VHS_REV;
value |= FIELD_PREP(GLI_9763E_VHS_REV, GLI_9763E_VHS_REV_R);
pci_write_config_dword(pdev, PCIE_GLI_9763E_VHS, value);
}
static int gl9763e_runtime_suspend(struct sdhci_pci_chip *chip) static int gl9763e_runtime_suspend(struct sdhci_pci_chip *chip)
{ {
struct sdhci_pci_slot *slot = chip->slots[0]; struct sdhci_pci_slot *slot = chip->slots[0];

View File

@ -416,12 +416,33 @@ static void sdhci_sprd_request_done(struct sdhci_host *host,
mmc_request_done(host->mmc, mrq); mmc_request_done(host->mmc, mrq);
} }
static void sdhci_sprd_set_power(struct sdhci_host *host, unsigned char mode,
unsigned short vdd)
{
struct mmc_host *mmc = host->mmc;
switch (mode) {
case MMC_POWER_OFF:
mmc_regulator_set_ocr(host->mmc, mmc->supply.vmmc, 0);
mmc_regulator_disable_vqmmc(mmc);
break;
case MMC_POWER_ON:
mmc_regulator_enable_vqmmc(mmc);
break;
case MMC_POWER_UP:
mmc_regulator_set_ocr(host->mmc, mmc->supply.vmmc, vdd);
break;
}
}
static struct sdhci_ops sdhci_sprd_ops = { static struct sdhci_ops sdhci_sprd_ops = {
.read_l = sdhci_sprd_readl, .read_l = sdhci_sprd_readl,
.write_l = sdhci_sprd_writel, .write_l = sdhci_sprd_writel,
.write_w = sdhci_sprd_writew, .write_w = sdhci_sprd_writew,
.write_b = sdhci_sprd_writeb, .write_b = sdhci_sprd_writeb,
.set_clock = sdhci_sprd_set_clock, .set_clock = sdhci_sprd_set_clock,
.set_power = sdhci_sprd_set_power,
.get_max_clock = sdhci_sprd_get_max_clock, .get_max_clock = sdhci_sprd_get_max_clock,
.get_min_clock = sdhci_sprd_get_min_clock, .get_min_clock = sdhci_sprd_get_min_clock,
.set_bus_width = sdhci_set_bus_width, .set_bus_width = sdhci_set_bus_width,
@ -823,6 +844,10 @@ static int sdhci_sprd_probe(struct platform_device *pdev)
host->caps1 &= ~(SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_SDR104 | host->caps1 &= ~(SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_SDR104 |
SDHCI_SUPPORT_DDR50); SDHCI_SUPPORT_DDR50);
ret = mmc_regulator_get_supply(host->mmc);
if (ret)
goto pm_runtime_disable;
ret = sdhci_setup_host(host); ret = sdhci_setup_host(host);
if (ret) if (ret)
goto pm_runtime_disable; goto pm_runtime_disable;