Commit 09443a14 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
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
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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);
	}
+7 −2
Original line number Diff line number Diff line
@@ -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;
+22 −22
Original line number Diff line number Diff line
@@ -942,7 +942,7 @@ 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",
		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));
	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);
		/* 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);
	}

	cqhci_halt(mmc, CQHCI_FINISH_HALT_TIMEOUT);

	if (!ok)
		cqhci_clear_all_tasks(mmc, CQHCI_CLEAR_TIMEOUT);

	cqhci_recover_mrqs(cq_host);

+29 −25
Original line number Diff line number Diff line
@@ -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];
+25 −0
Original line number Diff line number Diff line
@@ -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;