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

Commit fee686b7 authored by Adrian Hunter's avatar Adrian Hunter Committed by Ulf Hansson
Browse files

mmc: sdhci-pci: Fix bus power failing to enable for some Intel controllers



Some Intel controllers (e.g. BXT) might fail to set bus power after a
D3 -> D0 transition due to the present state not yet having propagated.
Retry for up to 2 milliseconds.

Signed-off-by: default avatarAdrian Hunter <adrian.hunter@intel.com>
Signed-off-by: default avatarUlf Hansson <ulf.hansson@linaro.org>
parent 6bc09063
Loading
Loading
Loading
Loading
+50 −0
Original line number Diff line number Diff line
@@ -32,6 +32,14 @@
#include "sdhci-pci.h"
#include "sdhci-pci-o2micro.h"

static int sdhci_pci_enable_dma(struct sdhci_host *host);
static void sdhci_pci_set_bus_width(struct sdhci_host *host, int width);
static void sdhci_pci_hw_reset(struct sdhci_host *host);
static int sdhci_pci_select_drive_strength(struct sdhci_host *host,
					   struct mmc_card *card,
					   unsigned int max_dtr, int host_drv,
					   int card_drv, int *drv_type);

/*****************************************************************************\
 *                                                                           *
 * Hardware specific quirk handling                                          *
@@ -390,6 +398,45 @@ static int byt_sd_probe_slot(struct sdhci_pci_slot *slot)
	return 0;
}

#define SDHCI_INTEL_PWR_TIMEOUT_CNT	20
#define SDHCI_INTEL_PWR_TIMEOUT_UDELAY	100

static void sdhci_intel_set_power(struct sdhci_host *host, unsigned char mode,
				  unsigned short vdd)
{
	int cntr;
	u8 reg;

	sdhci_set_power(host, mode, vdd);

	if (mode == MMC_POWER_OFF)
		return;

	/*
	 * Bus power might not enable after D3 -> D0 transition due to the
	 * present state not yet having propagated. Retry for up to 2ms.
	 */
	for (cntr = 0; cntr < SDHCI_INTEL_PWR_TIMEOUT_CNT; cntr++) {
		reg = sdhci_readb(host, SDHCI_POWER_CONTROL);
		if (reg & SDHCI_POWER_ON)
			break;
		udelay(SDHCI_INTEL_PWR_TIMEOUT_UDELAY);
		reg |= SDHCI_POWER_ON;
		sdhci_writeb(host, reg, SDHCI_POWER_CONTROL);
	}
}

static const struct sdhci_ops sdhci_intel_byt_ops = {
	.set_clock		= sdhci_set_clock,
	.set_power		= sdhci_intel_set_power,
	.enable_dma		= sdhci_pci_enable_dma,
	.set_bus_width		= sdhci_pci_set_bus_width,
	.reset			= sdhci_reset,
	.set_uhs_signaling	= sdhci_set_uhs_signaling,
	.hw_reset		= sdhci_pci_hw_reset,
	.select_drive_strength	= sdhci_pci_select_drive_strength,
};

static const struct sdhci_pci_fixes sdhci_intel_byt_emmc = {
	.allow_runtime_pm = true,
	.probe_slot	= byt_emmc_probe_slot,
@@ -397,6 +444,7 @@ static const struct sdhci_pci_fixes sdhci_intel_byt_emmc = {
	.quirks2	= SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
			  SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400 |
			  SDHCI_QUIRK2_STOP_WITH_TC,
	.ops		= &sdhci_intel_byt_ops,
};

static const struct sdhci_pci_fixes sdhci_intel_byt_sdio = {
@@ -405,6 +453,7 @@ static const struct sdhci_pci_fixes sdhci_intel_byt_sdio = {
			SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
	.allow_runtime_pm = true,
	.probe_slot	= byt_sdio_probe_slot,
	.ops		= &sdhci_intel_byt_ops,
};

static const struct sdhci_pci_fixes sdhci_intel_byt_sd = {
@@ -415,6 +464,7 @@ static const struct sdhci_pci_fixes sdhci_intel_byt_sd = {
	.allow_runtime_pm = true,
	.own_cd_for_runtime_pm = true,
	.probe_slot	= byt_sd_probe_slot,
	.ops		= &sdhci_intel_byt_ops,
};

/* Define Host controllers for Intel Merrifield platform */