Commit 09443a14 authored by Linus Torvalds's avatar Linus Torvalds

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
parents 16864755 477865af
......@@ -1482,6 +1482,8 @@ static void mmc_blk_cqe_complete_rq(struct mmc_queue *mq, struct request *req)
blk_mq_requeue_request(req, true);
else
__blk_mq_end_request(req, BLK_STS_OK);
} else if (mq->in_recovery) {
blk_mq_requeue_request(req, true);
} else {
blk_mq_end_request(req, BLK_STS_OK);
}
......
......@@ -551,7 +551,9 @@ int mmc_cqe_recovery(struct mmc_host *host)
cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
cmd.flags &= ~MMC_RSP_CRC; /* Ignore CRC */
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));
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_CRC; /* Ignore CRC */
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);
if (err)
err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
mmc_retune_release(host);
return err;
......
......@@ -942,8 +942,8 @@ static bool cqhci_clear_all_tasks(struct mmc_host *mmc, unsigned int timeout)
ret = cqhci_tasks_cleared(cq_host);
if (!ret)
pr_debug("%s: cqhci: Failed to clear tasks\n",
mmc_hostname(mmc));
pr_warn("%s: cqhci: Failed to clear tasks\n",
mmc_hostname(mmc));
return ret;
}
......@@ -976,7 +976,7 @@ static bool cqhci_halt(struct mmc_host *mmc, unsigned int timeout)
ret = cqhci_halted(cq_host);
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;
}
......@@ -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
* 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
* generous command timeout.
* layers will need to send a STOP command), however failing to halt complicates
* 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)
{
......@@ -1075,28 +1075,28 @@ static void cqhci_recovery_finish(struct mmc_host *mmc)
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
* 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.
* Have a go anyway.
*/
if (!ok) {
pr_debug("%s: cqhci: disable / re-enable\n", mmc_hostname(mmc));
cqcfg = cqhci_readl(cq_host, CQHCI_CFG);
cqcfg &= ~CQHCI_ENABLE;
cqhci_writel(cq_host, cqcfg, CQHCI_CFG);
cqcfg |= CQHCI_ENABLE;
cqhci_writel(cq_host, cqcfg, CQHCI_CFG);
/* Be sure that there are no tasks */
ok = cqhci_halt(mmc, CQHCI_FINISH_HALT_TIMEOUT);
if (!cqhci_clear_all_tasks(mmc, CQHCI_CLEAR_TIMEOUT))
ok = false;
WARN_ON(!ok);
}
if (!cqhci_clear_all_tasks(mmc, CQHCI_CLEAR_TIMEOUT))
ok = false;
/* Disable to make sure tasks really are cleared */
cqcfg = cqhci_readl(cq_host, CQHCI_CFG);
cqcfg &= ~CQHCI_ENABLE;
cqhci_writel(cq_host, cqcfg, CQHCI_CFG);
cqcfg = cqhci_readl(cq_host, CQHCI_CFG);
cqcfg |= CQHCI_ENABLE;
cqhci_writel(cq_host, cqcfg, CQHCI_CFG);
cqhci_halt(mmc, CQHCI_FINISH_HALT_TIMEOUT);
if (!ok)
cqhci_clear_all_tasks(mmc, CQHCI_CLEAR_TIMEOUT);
cqhci_recover_mrqs(cq_host);
......
......@@ -1189,6 +1189,32 @@ static void gl9763e_hs400_enhanced_strobe(struct mmc_host *mmc,
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,
unsigned int timing)
{
......@@ -1297,6 +1323,9 @@ static int gl9763e_add_host(struct sdhci_pci_slot *slot)
if (ret)
goto cleanup;
/* Disable LPM negotiation to avoid entering L1 state. */
gl9763e_set_low_power_negotiation(slot, false);
return 0;
cleanup:
......@@ -1340,31 +1369,6 @@ static void gli_set_gl9763e(struct sdhci_pci_slot *slot)
}
#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)
{
struct sdhci_pci_slot *slot = chip->slots[0];
......
......@@ -416,12 +416,33 @@ static void sdhci_sprd_request_done(struct sdhci_host *host,
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 = {
.read_l = sdhci_sprd_readl,
.write_l = sdhci_sprd_writel,
.write_w = sdhci_sprd_writew,
.write_b = sdhci_sprd_writeb,
.set_clock = sdhci_sprd_set_clock,
.set_power = sdhci_sprd_set_power,
.get_max_clock = sdhci_sprd_get_max_clock,
.get_min_clock = sdhci_sprd_get_min_clock,
.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 |
SDHCI_SUPPORT_DDR50);
ret = mmc_regulator_get_supply(host->mmc);
if (ret)
goto pm_runtime_disable;
ret = sdhci_setup_host(host);
if (ret)
goto pm_runtime_disable;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment