diff options
Diffstat (limited to 'drivers/mmc/host/sdhci-pci-core.c')
| -rw-r--r-- | drivers/mmc/host/sdhci-pci-core.c | 63 | 
1 files changed, 61 insertions, 2 deletions
diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c index eaffa85bc728..5091e2c1c0e5 100644 --- a/drivers/mmc/host/sdhci-pci-core.c +++ b/drivers/mmc/host/sdhci-pci-core.c @@ -21,11 +21,13 @@  #include <linux/mmc/mmc.h>  #include <linux/scatterlist.h>  #include <linux/io.h> +#include <linux/iopoll.h>  #include <linux/gpio.h>  #include <linux/pm_runtime.h>  #include <linux/mmc/slot-gpio.h>  #include <linux/mmc/sdhci-pci-data.h>  #include <linux/acpi.h> +#include <linux/dmi.h>  #ifdef CONFIG_X86  #include <asm/iosf_mbi.h> @@ -782,11 +784,18 @@ static int byt_emmc_probe_slot(struct sdhci_pci_slot *slot)  	return 0;  } +static bool glk_broken_cqhci(struct sdhci_pci_slot *slot) +{ +	return slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_GLK_EMMC && +	       dmi_match(DMI_BIOS_VENDOR, "LENOVO"); +} +  static int glk_emmc_probe_slot(struct sdhci_pci_slot *slot)  {  	int ret = byt_emmc_probe_slot(slot); -	slot->host->mmc->caps2 |= MMC_CAP2_CQE; +	if (!glk_broken_cqhci(slot)) +		slot->host->mmc->caps2 |= MMC_CAP2_CQE;  	if (slot->chip->pdev->device != PCI_DEVICE_ID_INTEL_GLK_EMMC) {  		slot->host->mmc->caps2 |= MMC_CAP2_HS400_ES, @@ -1590,11 +1599,59 @@ static int amd_probe(struct sdhci_pci_chip *chip)  	return 0;  } +static u32 sdhci_read_present_state(struct sdhci_host *host) +{ +	return sdhci_readl(host, SDHCI_PRESENT_STATE); +} + +static void amd_sdhci_reset(struct sdhci_host *host, u8 mask) +{ +	struct sdhci_pci_slot *slot = sdhci_priv(host); +	struct pci_dev *pdev = slot->chip->pdev; +	u32 present_state; + +	/* +	 * SDHC 0x7906 requires a hard reset to clear all internal state. +	 * Otherwise it can get into a bad state where the DATA lines are always +	 * read as zeros. +	 */ +	if (pdev->device == 0x7906 && (mask & SDHCI_RESET_ALL)) { +		pci_clear_master(pdev); + +		pci_save_state(pdev); + +		pci_set_power_state(pdev, PCI_D3cold); +		pr_debug("%s: power_state=%u\n", mmc_hostname(host->mmc), +			pdev->current_state); +		pci_set_power_state(pdev, PCI_D0); + +		pci_restore_state(pdev); + +		/* +		 * SDHCI_RESET_ALL says the card detect logic should not be +		 * reset, but since we need to reset the entire controller +		 * we should wait until the card detect logic has stabilized. +		 * +		 * This normally takes about 40ms. +		 */ +		readx_poll_timeout( +			sdhci_read_present_state, +			host, +			present_state, +			present_state & SDHCI_CD_STABLE, +			10000, +			100000 +		); +	} + +	return sdhci_reset(host, mask); +} +  static const struct sdhci_ops amd_sdhci_pci_ops = {  	.set_clock			= sdhci_set_clock,  	.enable_dma			= sdhci_pci_enable_dma,  	.set_bus_width			= sdhci_set_bus_width, -	.reset				= sdhci_reset, +	.reset				= amd_sdhci_reset,  	.set_uhs_signaling		= sdhci_set_uhs_signaling,  }; @@ -1673,6 +1730,8 @@ static const struct pci_device_id pci_ids[] = {  	SDHCI_PCI_DEVICE(INTEL, CML_EMMC,  intel_glk_emmc),  	SDHCI_PCI_DEVICE(INTEL, CML_SD,    intel_byt_sd),  	SDHCI_PCI_DEVICE(INTEL, CMLH_SD,   intel_byt_sd), +	SDHCI_PCI_DEVICE(INTEL, JSL_EMMC,  intel_glk_emmc), +	SDHCI_PCI_DEVICE(INTEL, JSL_SD,    intel_byt_sd),  	SDHCI_PCI_DEVICE(O2, 8120,     o2),  	SDHCI_PCI_DEVICE(O2, 8220,     o2),  	SDHCI_PCI_DEVICE(O2, 8221,     o2),  |