diff options
Diffstat (limited to 'drivers/mmc')
| -rw-r--r-- | drivers/mmc/core/block.c | 15 | ||||
| -rw-r--r-- | drivers/mmc/host/omap.c | 11 | ||||
| -rw-r--r-- | drivers/mmc/host/sdhci-omap.c | 12 | ||||
| -rw-r--r-- | drivers/mmc/host/sdhci-pci-core.c | 86 | ||||
| -rw-r--r-- | drivers/mmc/host/sdhci.c | 18 | 
5 files changed, 123 insertions, 19 deletions
diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c index c35b5b08bb33..111934838da2 100644 --- a/drivers/mmc/core/block.c +++ b/drivers/mmc/core/block.c @@ -472,7 +472,7 @@ out:  static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md,  			       struct mmc_blk_ioc_data *idata)  { -	struct mmc_command cmd = {}; +	struct mmc_command cmd = {}, sbc = {};  	struct mmc_data data = {};  	struct mmc_request mrq = {};  	struct scatterlist sg; @@ -550,10 +550,15 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md,  	}  	if (idata->rpmb) { -		err = mmc_set_blockcount(card, data.blocks, -			idata->ic.write_flag & (1 << 31)); -		if (err) -			return err; +		sbc.opcode = MMC_SET_BLOCK_COUNT; +		/* +		 * We don't do any blockcount validation because the max size +		 * may be increased by a future standard. We just copy the +		 * 'Reliable Write' bit here. +		 */ +		sbc.arg = data.blocks | (idata->ic.write_flag & BIT(31)); +		sbc.flags = MMC_RSP_R1 | MMC_CMD_AC; +		mrq.sbc = &sbc;  	}  	if ((MMC_EXTRACT_INDEX_FROM_ARG(cmd.arg) == EXT_CSD_SANITIZE_START) && diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c index adf32682f27a..c60a7625b1fa 100644 --- a/drivers/mmc/host/omap.c +++ b/drivers/mmc/host/omap.c @@ -104,6 +104,7 @@ struct mmc_omap_slot {  	unsigned int		vdd;  	u16			saved_con;  	u16			bus_mode; +	u16			power_mode;  	unsigned int		fclk_freq;  	struct tasklet_struct	cover_tasklet; @@ -1157,7 +1158,7 @@ static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)  	struct mmc_omap_slot *slot = mmc_priv(mmc);  	struct mmc_omap_host *host = slot->host;  	int i, dsor; -	int clk_enabled; +	int clk_enabled, init_stream;  	mmc_omap_select_slot(slot, 0); @@ -1167,6 +1168,7 @@ static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)  		slot->vdd = ios->vdd;  	clk_enabled = 0; +	init_stream = 0;  	switch (ios->power_mode) {  	case MMC_POWER_OFF:  		mmc_omap_set_power(slot, 0, ios->vdd); @@ -1174,13 +1176,17 @@ static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)  	case MMC_POWER_UP:  		/* Cannot touch dsor yet, just power up MMC */  		mmc_omap_set_power(slot, 1, ios->vdd); +		slot->power_mode = ios->power_mode;  		goto exit;  	case MMC_POWER_ON:  		mmc_omap_fclk_enable(host, 1);  		clk_enabled = 1;  		dsor |= 1 << 11; +		if (slot->power_mode != MMC_POWER_ON) +			init_stream = 1;  		break;  	} +	slot->power_mode = ios->power_mode;  	if (slot->bus_mode != ios->bus_mode) {  		if (slot->pdata->set_bus_mode != NULL) @@ -1196,7 +1202,7 @@ static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)  	for (i = 0; i < 2; i++)  		OMAP_MMC_WRITE(host, CON, dsor);  	slot->saved_con = dsor; -	if (ios->power_mode == MMC_POWER_ON) { +	if (init_stream) {  		/* worst case at 400kHz, 80 cycles makes 200 microsecs */  		int usecs = 250; @@ -1234,6 +1240,7 @@ static int mmc_omap_new_slot(struct mmc_omap_host *host, int id)  	slot->host = host;  	slot->mmc = mmc;  	slot->id = id; +	slot->power_mode = MMC_POWER_UNDEFINED;  	slot->pdata = &host->pdata->slots[id];  	host->slots[id] = slot; diff --git a/drivers/mmc/host/sdhci-omap.c b/drivers/mmc/host/sdhci-omap.c index 88347ce78f23..d264391616f9 100644 --- a/drivers/mmc/host/sdhci-omap.c +++ b/drivers/mmc/host/sdhci-omap.c @@ -288,9 +288,9 @@ static int sdhci_omap_execute_tuning(struct mmc_host *mmc, u32 opcode)  	struct device *dev = omap_host->dev;  	struct mmc_ios *ios = &mmc->ios;  	u32 start_window = 0, max_window = 0; +	bool dcrc_was_enabled = false;  	u8 cur_match, prev_match = 0;  	u32 length = 0, max_len = 0; -	u32 ier = host->ier;  	u32 phase_delay = 0;  	int ret = 0;  	u32 reg; @@ -317,9 +317,10 @@ static int sdhci_omap_execute_tuning(struct mmc_host *mmc, u32 opcode)  	 * during the tuning procedure. So disable it during the  	 * tuning procedure.  	 */ -	ier &= ~SDHCI_INT_DATA_CRC; -	sdhci_writel(host, ier, SDHCI_INT_ENABLE); -	sdhci_writel(host, ier, SDHCI_SIGNAL_ENABLE); +	if (host->ier & SDHCI_INT_DATA_CRC) { +		host->ier &= ~SDHCI_INT_DATA_CRC; +		dcrc_was_enabled = true; +	}  	while (phase_delay <= MAX_PHASE_DELAY) {  		sdhci_omap_set_dll(omap_host, phase_delay); @@ -366,6 +367,9 @@ tuning_error:  ret:  	sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); +	/* Reenable forbidden interrupt */ +	if (dcrc_was_enabled) +		host->ier |= SDHCI_INT_DATA_CRC;  	sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);  	sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);  	return ret; diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c index 7bfd366d970d..c4115bae5db1 100644 --- a/drivers/mmc/host/sdhci-pci-core.c +++ b/drivers/mmc/host/sdhci-pci-core.c @@ -12,6 +12,7 @@   *     - JMicron (hardware and technical support)   */ +#include <linux/bitfield.h>  #include <linux/string.h>  #include <linux/delay.h>  #include <linux/highmem.h> @@ -462,6 +463,9 @@ struct intel_host {  	u32	dsm_fns;  	int	drv_strength;  	bool	d3_retune; +	bool	rpm_retune_ok; +	u32	glk_rx_ctrl1; +	u32	glk_tun_val;  };  static const guid_t intel_dsm_guid = @@ -791,6 +795,77 @@ cleanup:  	return ret;  } +#ifdef CONFIG_PM +#define GLK_RX_CTRL1	0x834 +#define GLK_TUN_VAL	0x840 +#define GLK_PATH_PLL	GENMASK(13, 8) +#define GLK_DLY		GENMASK(6, 0) +/* Workaround firmware failing to restore the tuning value */ +static void glk_rpm_retune_wa(struct sdhci_pci_chip *chip, bool susp) +{ +	struct sdhci_pci_slot *slot = chip->slots[0]; +	struct intel_host *intel_host = sdhci_pci_priv(slot); +	struct sdhci_host *host = slot->host; +	u32 glk_rx_ctrl1; +	u32 glk_tun_val; +	u32 dly; + +	if (intel_host->rpm_retune_ok || !mmc_can_retune(host->mmc)) +		return; + +	glk_rx_ctrl1 = sdhci_readl(host, GLK_RX_CTRL1); +	glk_tun_val = sdhci_readl(host, GLK_TUN_VAL); + +	if (susp) { +		intel_host->glk_rx_ctrl1 = glk_rx_ctrl1; +		intel_host->glk_tun_val = glk_tun_val; +		return; +	} + +	if (!intel_host->glk_tun_val) +		return; + +	if (glk_rx_ctrl1 != intel_host->glk_rx_ctrl1) { +		intel_host->rpm_retune_ok = true; +		return; +	} + +	dly = FIELD_PREP(GLK_DLY, FIELD_GET(GLK_PATH_PLL, glk_rx_ctrl1) + +				  (intel_host->glk_tun_val << 1)); +	if (dly == FIELD_GET(GLK_DLY, glk_rx_ctrl1)) +		return; + +	glk_rx_ctrl1 = (glk_rx_ctrl1 & ~GLK_DLY) | dly; +	sdhci_writel(host, glk_rx_ctrl1, GLK_RX_CTRL1); + +	intel_host->rpm_retune_ok = true; +	chip->rpm_retune = true; +	mmc_retune_needed(host->mmc); +	pr_info("%s: Requiring re-tune after rpm resume", mmc_hostname(host->mmc)); +} + +static void glk_rpm_retune_chk(struct sdhci_pci_chip *chip, bool susp) +{ +	if (chip->pdev->device == PCI_DEVICE_ID_INTEL_GLK_EMMC && +	    !chip->rpm_retune) +		glk_rpm_retune_wa(chip, susp); +} + +static int glk_runtime_suspend(struct sdhci_pci_chip *chip) +{ +	glk_rpm_retune_chk(chip, true); + +	return sdhci_cqhci_runtime_suspend(chip); +} + +static int glk_runtime_resume(struct sdhci_pci_chip *chip) +{ +	glk_rpm_retune_chk(chip, false); + +	return sdhci_cqhci_runtime_resume(chip); +} +#endif +  #ifdef CONFIG_ACPI  static int ni_set_max_freq(struct sdhci_pci_slot *slot)  { @@ -879,8 +954,8 @@ static const struct sdhci_pci_fixes sdhci_intel_glk_emmc = {  	.resume			= sdhci_cqhci_resume,  #endif  #ifdef CONFIG_PM -	.runtime_suspend	= sdhci_cqhci_runtime_suspend, -	.runtime_resume		= sdhci_cqhci_runtime_resume, +	.runtime_suspend	= glk_runtime_suspend, +	.runtime_resume		= glk_runtime_resume,  #endif  	.quirks			= SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,  	.quirks2		= SDHCI_QUIRK2_PRESET_VALUE_BROKEN | @@ -1762,8 +1837,13 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot(  		device_init_wakeup(&pdev->dev, true);  	if (slot->cd_idx >= 0) { -		ret = mmc_gpiod_request_cd(host->mmc, NULL, slot->cd_idx, +		ret = mmc_gpiod_request_cd(host->mmc, "cd", slot->cd_idx,  					   slot->cd_override_level, 0, NULL); +		if (ret && ret != -EPROBE_DEFER) +			ret = mmc_gpiod_request_cd(host->mmc, NULL, +						   slot->cd_idx, +						   slot->cd_override_level, +						   0, NULL);  		if (ret == -EPROBE_DEFER)  			goto remove; diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 99bdae53fa2e..451b08a818a9 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -216,8 +216,12 @@ void sdhci_reset(struct sdhci_host *host, u8 mask)  	timeout = ktime_add_ms(ktime_get(), 100);  	/* hw clears the bit when it's done */ -	while (sdhci_readb(host, SDHCI_SOFTWARE_RESET) & mask) { -		if (ktime_after(ktime_get(), timeout)) { +	while (1) { +		bool timedout = ktime_after(ktime_get(), timeout); + +		if (!(sdhci_readb(host, SDHCI_SOFTWARE_RESET) & mask)) +			break; +		if (timedout) {  			pr_err("%s: Reset 0x%x never completed.\n",  				mmc_hostname(host->mmc), (int)mask);  			sdhci_dumpregs(host); @@ -1608,9 +1612,13 @@ void sdhci_enable_clk(struct sdhci_host *host, u16 clk)  	/* Wait max 20 ms */  	timeout = ktime_add_ms(ktime_get(), 20); -	while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL)) -		& SDHCI_CLOCK_INT_STABLE)) { -		if (ktime_after(ktime_get(), timeout)) { +	while (1) { +		bool timedout = ktime_after(ktime_get(), timeout); + +		clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); +		if (clk & SDHCI_CLOCK_INT_STABLE) +			break; +		if (timedout) {  			pr_err("%s: Internal clock never stabilised.\n",  			       mmc_hostname(host->mmc));  			sdhci_dumpregs(host);  | 
