Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6fa49fee authored by Bao D. Nguyen's avatar Bao D. Nguyen
Browse files

mmc: sdhci-msm: Restore SDCC Registers After CX Power Collapse



On some targets, the SDCC register contents are not retained
after resuming from a CX Power Collapse event. As a result, the
SD/SDIO/eMMC would fail to resume normal operation. This change allows
software to save and restore the SDCC register contents following a
CX Power Collapse event. On targets that the SDCC register contents
are retained by hardware, the software save-and-restore feature
can be disabled using the associated flag in the device tree file.

Change-Id: Ib03af741cd4818971e6b9b9b390724e1b0c4e5cc
Signed-off-by: default avatarBao D. Nguyen <nguyenb@codeaurora.org>
parent 2f4cbe5b
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -77,6 +77,11 @@ Optional Properties:
			  register dumps on CRC errors and also downgrade bus speed mode to
			  SDR50/DDR50 in case of continuous CRC errors. Set this flag to enable
			  this workaround.
	- qcom,restore-after-cx-collapse - specifies whether the SDCC registers contents need
	  to be saved and restored by software when the CX Power Collapse feature is enabled.
	  On certain chipsets, coming out of the CX Power Collapse event, the SDCC registers
	  contents will not be retained. It is software responsibility to restore the
	  SDCC registers before resuming to normal operation.

In the following, <supply> can be vdd (flash core voltage) or vdd-io (I/O voltage).
	- qcom,<supply>-always-on - specifies whether supply should be kept "on" always.
+102 −6
Original line number Diff line number Diff line
@@ -1940,6 +1940,8 @@ struct sdhci_msm_pltfm_data *sdhci_msm_populate_pdata(struct device *dev,
		msm_host->core_3_0v_support = true;

	pdata->sdr104_wa = of_property_read_bool(np, "qcom,sdr104-wa");
	msm_host->regs_restore.is_supported =
		of_property_read_bool(np, "qcom,restore-after-cx-collapse");

	return pdata;
out:
@@ -2831,6 +2833,103 @@ static unsigned int sdhci_msm_get_sup_clk_rate(struct sdhci_host *host,
	return sel_clk;
}

static void sdhci_msm_registers_save(struct sdhci_host *host)
{
	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
	struct sdhci_msm_host *msm_host = pltfm_host->priv;
	const struct sdhci_msm_offset *msm_host_offset =
					msm_host->offset;

	if (!msm_host->regs_restore.is_supported)
		return;

	msm_host->regs_restore.vendor_func = readl_relaxed(host->ioaddr +
		msm_host_offset->CORE_VENDOR_SPEC);
	msm_host->regs_restore.vendor_pwrctl_mask =
		readl_relaxed(host->ioaddr +
		msm_host_offset->CORE_PWRCTL_MASK);
	msm_host->regs_restore.vendor_func2 =
		readl_relaxed(host->ioaddr +
		msm_host_offset->CORE_VENDOR_SPEC_FUNC2);
	msm_host->regs_restore.vendor_func3 =
		readl_relaxed(host->ioaddr +
		msm_host_offset->CORE_VENDOR_SPEC3);
	msm_host->regs_restore.hc_2c_2e =
		sdhci_readl(host, SDHCI_CLOCK_CONTROL);
	msm_host->regs_restore.hc_3c_3e =
		sdhci_readl(host, SDHCI_AUTO_CMD_ERR);
	msm_host->regs_restore.vendor_pwrctl_ctl =
		readl_relaxed(host->ioaddr +
		msm_host_offset->CORE_PWRCTL_CTL);
	msm_host->regs_restore.hc_38_3a =
		sdhci_readl(host, SDHCI_SIGNAL_ENABLE);
	msm_host->regs_restore.hc_34_36 =
		sdhci_readl(host, SDHCI_INT_ENABLE);
	msm_host->regs_restore.hc_28_2a =
		sdhci_readl(host, SDHCI_HOST_CONTROL);
	msm_host->regs_restore.vendor_caps_0 =
		readl_relaxed(host->ioaddr +
		msm_host_offset->CORE_VENDOR_SPEC_CAPABILITIES0);
	msm_host->regs_restore.hc_caps_1 =
		sdhci_readl(host, SDHCI_CAPABILITIES_1);
	msm_host->regs_restore.testbus_config = readl_relaxed(host->ioaddr +
		msm_host_offset->CORE_TESTBUS_CONFIG);
	msm_host->regs_restore.is_valid = true;

	pr_debug("%s: %s: registers saved. PWRCTL_MASK = 0x%x\n",
		mmc_hostname(host->mmc), __func__,
		readl_relaxed(host->ioaddr +
			msm_host_offset->CORE_PWRCTL_MASK));
}

static void sdhci_msm_registers_restore(struct sdhci_host *host)
{
	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
	struct sdhci_msm_host *msm_host = pltfm_host->priv;
	const struct sdhci_msm_offset *msm_host_offset =
					msm_host->offset;

	if (!msm_host->regs_restore.is_supported ||
		!msm_host->regs_restore.is_valid)
		return;

	writel_relaxed(msm_host->regs_restore.vendor_func, host->ioaddr +
			msm_host_offset->CORE_VENDOR_SPEC);
	writel_relaxed(msm_host->regs_restore.vendor_pwrctl_mask,
			host->ioaddr + msm_host_offset->CORE_PWRCTL_MASK);
	writel_relaxed(msm_host->regs_restore.vendor_func2,
			host->ioaddr +
			msm_host_offset->CORE_VENDOR_SPEC_FUNC2);
	writel_relaxed(msm_host->regs_restore.vendor_func3,
			host->ioaddr +
			msm_host_offset->CORE_VENDOR_SPEC3);
	sdhci_writel(host, msm_host->regs_restore.hc_2c_2e,
			SDHCI_CLOCK_CONTROL);
	sdhci_writel(host, msm_host->regs_restore.hc_3c_3e,
			SDHCI_AUTO_CMD_ERR);
	writel_relaxed(msm_host->regs_restore.vendor_pwrctl_ctl,
			host->ioaddr + msm_host_offset->CORE_PWRCTL_CTL);
	sdhci_writel(host, msm_host->regs_restore.hc_38_3a,
			SDHCI_SIGNAL_ENABLE);
	sdhci_writel(host, msm_host->regs_restore.hc_34_36,
			SDHCI_INT_ENABLE);
	sdhci_writel(host, msm_host->regs_restore.hc_28_2a,
			SDHCI_HOST_CONTROL);
	writel_relaxed(msm_host->regs_restore.vendor_caps_0,
			host->ioaddr +
			msm_host_offset->CORE_VENDOR_SPEC_CAPABILITIES0);
	sdhci_writel(host, msm_host->regs_restore.hc_caps_1,
			SDHCI_CAPABILITIES_1);
	writel_relaxed(msm_host->regs_restore.testbus_config, host->ioaddr +
			msm_host_offset->CORE_TESTBUS_CONFIG);
	msm_host->regs_restore.is_valid = false;

	pr_debug("%s: %s: registers restored. PWRCTL_MASK = 0x%x\n",
		mmc_hostname(host->mmc), __func__,
		readl_relaxed(host->ioaddr +
			msm_host_offset->CORE_PWRCTL_MASK));
}

static int sdhci_msm_enable_controller_clock(struct sdhci_host *host)
{
	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@@ -2861,6 +2960,7 @@ static int sdhci_msm_enable_controller_clock(struct sdhci_host *host)
	atomic_set(&msm_host->controller_clock, 1);
	pr_debug("%s: %s: enabled controller clock\n",
			mmc_hostname(host->mmc), __func__);
	sdhci_msm_registers_restore(host);
	goto out;

disable_pclk:
@@ -2879,6 +2979,7 @@ static void sdhci_msm_disable_controller_clock(struct sdhci_host *host)
	struct sdhci_msm_host *msm_host = pltfm_host->priv;

	if (atomic_read(&msm_host->controller_clock)) {
		sdhci_msm_registers_save(host);
		if (!IS_ERR(msm_host->clk))
			clk_disable_unprepare(msm_host->clk);
		if (!IS_ERR(msm_host->pclk))
@@ -2957,14 +3058,9 @@ static int sdhci_msm_prepare_clocks(struct sdhci_host *host, bool enable)
			clk_disable_unprepare(msm_host->sleep_clk);
		if (!IS_ERR_OR_NULL(msm_host->ff_clk))
			clk_disable_unprepare(msm_host->ff_clk);
		clk_disable_unprepare(msm_host->clk);
		if (!IS_ERR(msm_host->pclk))
			clk_disable_unprepare(msm_host->pclk);
		if (!IS_ERR_OR_NULL(msm_host->bus_clk))
			clk_disable_unprepare(msm_host->bus_clk);

		atomic_set(&msm_host->controller_clock, 0);
		sdhci_msm_bus_voting(host, 0);
		sdhci_msm_disable_controller_clock(host);
	}
	atomic_set(&msm_host->clks_on, enable);
	goto out;
+19 −0
Original line number Diff line number Diff line
@@ -170,6 +170,24 @@ struct sdhci_msm_ice_data {
	int state;
};

struct sdhci_msm_regs_restore {
	bool is_supported;
	bool is_valid;
	u32 vendor_pwrctl_mask;
	u32 vendor_pwrctl_ctl;
	u32 vendor_caps_0;
	u32 vendor_func;
	u32 vendor_func2;
	u32 vendor_func3;
	u32 hc_2c_2e;
	u32 hc_28_2a;
	u32 hc_34_36;
	u32 hc_38_3a;
	u32 hc_3c_3e;
	u32 hc_caps_1;
	u32 testbus_config;
};

struct sdhci_msm_debug_data {
	struct mmc_host copy_mmc;
	struct mmc_card copy_card;
@@ -226,6 +244,7 @@ struct sdhci_msm_host {
	const struct sdhci_msm_offset *offset;
	bool core_3_0v_support;
	bool pltfm_init_done;
	struct sdhci_msm_regs_restore regs_restore;
};

extern char *saved_command_line;