]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - drivers/mmc/host/sdhci-pci-core.c
mmc: sdhci-pci: Fix 3.3V voltage switch for some BYT-based Intel controllers
[linux.git] / drivers / mmc / host / sdhci-pci-core.c
index 82c4f05f91d8781528a365d97b3033472df987f4..77dd3521daae4aee806190db9f7970b44b87daa7 100644 (file)
@@ -41,18 +41,25 @@ static void sdhci_pci_hw_reset(struct sdhci_host *host);
 static int sdhci_pci_init_wakeup(struct sdhci_pci_chip *chip)
 {
        mmc_pm_flag_t pm_flags = 0;
+       bool cap_cd_wake = false;
        int i;
 
        for (i = 0; i < chip->num_slots; i++) {
                struct sdhci_pci_slot *slot = chip->slots[i];
 
-               if (slot)
+               if (slot) {
                        pm_flags |= slot->host->mmc->pm_flags;
+                       if (slot->host->mmc->caps & MMC_CAP_CD_WAKE)
+                               cap_cd_wake = true;
+               }
        }
 
-       return device_set_wakeup_enable(&chip->pdev->dev,
-                                       (pm_flags & MMC_PM_KEEP_POWER) &&
-                                       (pm_flags & MMC_PM_WAKE_SDIO_IRQ));
+       if ((pm_flags & MMC_PM_KEEP_POWER) && (pm_flags & MMC_PM_WAKE_SDIO_IRQ))
+               return device_wakeup_enable(&chip->pdev->dev);
+       else if (!cap_cd_wake)
+               return device_wakeup_disable(&chip->pdev->dev);
+
+       return 0;
 }
 
 static int sdhci_pci_suspend_host(struct sdhci_pci_chip *chip)
@@ -76,6 +83,9 @@ static int sdhci_pci_suspend_host(struct sdhci_pci_chip *chip)
                ret = sdhci_suspend_host(host);
                if (ret)
                        goto err_pci_suspend;
+
+               if (device_may_wakeup(&chip->pdev->dev))
+                       mmc_gpio_set_cd_wake(host->mmc, true);
        }
 
        return 0;
@@ -99,6 +109,8 @@ int sdhci_pci_resume_host(struct sdhci_pci_chip *chip)
                ret = sdhci_resume_host(slot->host);
                if (ret)
                        return ret;
+
+               mmc_gpio_set_cd_wake(slot->host->mmc, false);
        }
 
        return 0;
@@ -441,6 +453,7 @@ static const struct sdhci_pci_fixes sdhci_intel_pch_sdio = {
 enum {
        INTEL_DSM_FNS           =  0,
        INTEL_DSM_V18_SWITCH    =  3,
+       INTEL_DSM_V33_SWITCH    =  4,
        INTEL_DSM_DRV_STRENGTH  =  9,
        INTEL_DSM_D3_RETUNE     = 10,
 };
@@ -608,17 +621,37 @@ static void intel_hs400_enhanced_strobe(struct mmc_host *mmc,
        sdhci_writel(host, val, INTEL_HS400_ES_REG);
 }
 
-static void sdhci_intel_voltage_switch(struct sdhci_host *host)
+static int intel_start_signal_voltage_switch(struct mmc_host *mmc,
+                                            struct mmc_ios *ios)
 {
+       struct device *dev = mmc_dev(mmc);
+       struct sdhci_host *host = mmc_priv(mmc);
        struct sdhci_pci_slot *slot = sdhci_priv(host);
        struct intel_host *intel_host = sdhci_pci_priv(slot);
-       struct device *dev = &slot->chip->pdev->dev;
+       unsigned int fn;
        u32 result = 0;
        int err;
 
-       err = intel_dsm(intel_host, dev, INTEL_DSM_V18_SWITCH, &result);
-       pr_debug("%s: %s DSM error %d result %u\n",
-                mmc_hostname(host->mmc), __func__, err, result);
+       err = sdhci_start_signal_voltage_switch(mmc, ios);
+       if (err)
+               return err;
+
+       switch (ios->signal_voltage) {
+       case MMC_SIGNAL_VOLTAGE_330:
+               fn = INTEL_DSM_V33_SWITCH;
+               break;
+       case MMC_SIGNAL_VOLTAGE_180:
+               fn = INTEL_DSM_V18_SWITCH;
+               break;
+       default:
+               return 0;
+       }
+
+       err = intel_dsm(intel_host, dev, fn, &result);
+       pr_debug("%s: %s DSM fn %u error %d result %u\n",
+                mmc_hostname(mmc), __func__, fn, err, result);
+
+       return 0;
 }
 
 static const struct sdhci_ops sdhci_intel_byt_ops = {
@@ -629,7 +662,6 @@ static const struct sdhci_ops sdhci_intel_byt_ops = {
        .reset                  = sdhci_reset,
        .set_uhs_signaling      = sdhci_set_uhs_signaling,
        .hw_reset               = sdhci_pci_hw_reset,
-       .voltage_switch         = sdhci_intel_voltage_switch,
 };
 
 static const struct sdhci_ops sdhci_intel_glk_ops = {
@@ -640,7 +672,6 @@ static const struct sdhci_ops sdhci_intel_glk_ops = {
        .reset                  = sdhci_reset,
        .set_uhs_signaling      = sdhci_set_uhs_signaling,
        .hw_reset               = sdhci_pci_hw_reset,
-       .voltage_switch         = sdhci_intel_voltage_switch,
        .irq                    = sdhci_cqhci_irq,
 };
 
@@ -679,6 +710,7 @@ static void byt_probe_slot(struct sdhci_pci_slot *slot)
        byt_read_dsm(slot);
 
        ops->execute_tuning = intel_execute_tuning;
+       ops->start_signal_voltage_switch = intel_start_signal_voltage_switch;
 }
 
 static int byt_emmc_probe_slot(struct sdhci_pci_slot *slot)
@@ -712,26 +744,8 @@ static int glk_emmc_probe_slot(struct sdhci_pci_slot *slot)
        return ret;
 }
 
-static void glk_cqe_enable(struct mmc_host *mmc)
-{
-       struct sdhci_host *host = mmc_priv(mmc);
-       u32 reg;
-
-       /*
-        * CQE gets stuck if it sees Buffer Read Enable bit set, which can be
-        * the case after tuning, so ensure the buffer is drained.
-        */
-       reg = sdhci_readl(host, SDHCI_PRESENT_STATE);
-       while (reg & SDHCI_DATA_AVAILABLE) {
-               sdhci_readl(host, SDHCI_BUFFER);
-               reg = sdhci_readl(host, SDHCI_PRESENT_STATE);
-       }
-
-       sdhci_cqe_enable(mmc);
-}
-
 static const struct cqhci_host_ops glk_cqhci_ops = {
-       .enable         = glk_cqe_enable,
+       .enable         = sdhci_cqe_enable,
        .disable        = sdhci_cqe_disable,
        .dumpregs       = sdhci_pci_dumpregs,
 };
@@ -838,6 +852,10 @@ static int byt_sd_probe_slot(struct sdhci_pci_slot *slot)
            slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_GLK_SD)
                slot->host->mmc_host_ops.get_cd = bxt_get_cd;
 
+       if (slot->chip->pdev->subsystem_vendor == PCI_VENDOR_ID_NI &&
+           slot->chip->pdev->subsystem_device == PCI_SUBDEVICE_ID_NI_78E3)
+               slot->host->mmc->caps2 |= MMC_CAP2_AVOID_3_3V;
+
        return 0;
 }
 
@@ -1318,7 +1336,7 @@ static void amd_enable_manual_tuning(struct pci_dev *pdev)
        pci_write_config_dword(pdev, AMD_SD_MISC_CONTROL, val);
 }
 
-static int amd_execute_tuning(struct sdhci_host *host, u32 opcode)
+static int amd_execute_tuning_hs200(struct sdhci_host *host, u32 opcode)
 {
        struct sdhci_pci_slot *slot = sdhci_priv(host);
        struct pci_dev *pdev = slot->chip->pdev;
@@ -1357,6 +1375,27 @@ static int amd_execute_tuning(struct sdhci_host *host, u32 opcode)
        return 0;
 }
 
+static int amd_execute_tuning(struct mmc_host *mmc, u32 opcode)
+{
+       struct sdhci_host *host = mmc_priv(mmc);
+
+       /* AMD requires custom HS200 tuning */
+       if (host->timing == MMC_TIMING_MMC_HS200)
+               return amd_execute_tuning_hs200(host, opcode);
+
+       /* Otherwise perform standard SDHCI tuning */
+       return sdhci_execute_tuning(mmc, opcode);
+}
+
+static int amd_probe_slot(struct sdhci_pci_slot *slot)
+{
+       struct mmc_host_ops *ops = &slot->host->mmc_host_ops;
+
+       ops->execute_tuning = amd_execute_tuning;
+
+       return 0;
+}
+
 static int amd_probe(struct sdhci_pci_chip *chip)
 {
        struct pci_dev  *smbus_dev;
@@ -1391,12 +1430,12 @@ static const struct sdhci_ops amd_sdhci_pci_ops = {
        .set_bus_width                  = sdhci_set_bus_width,
        .reset                          = sdhci_reset,
        .set_uhs_signaling              = sdhci_set_uhs_signaling,
-       .platform_execute_tuning        = amd_execute_tuning,
 };
 
 static const struct sdhci_pci_fixes sdhci_amd = {
        .probe          = amd_probe,
        .ops            = &amd_sdhci_pci_ops,
+       .probe_slot     = amd_probe_slot,
 };
 
 static const struct pci_device_id pci_ids[] = {
@@ -1716,6 +1755,9 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot(
        if (device_can_wakeup(&pdev->dev))
                host->mmc->pm_caps |= MMC_PM_WAKE_SDIO_IRQ;
 
+       if (host->mmc->caps & MMC_CAP_CD_WAKE)
+               device_init_wakeup(&pdev->dev, true);
+
        if (slot->cd_idx >= 0) {
                ret = mmc_gpiod_request_cd(host->mmc, NULL, slot->cd_idx,
                                           slot->cd_override_level, 0, NULL);