1
0
Fork 0

mmc: sdhci-acpi: Reduce Baytrail eMMC/SD/SDIO hangs

Baytrail eMMC/SD/SDIO host controllers have been known to
hang.  A change to a hardware setting has been found to
reduce the occurrence of such hangs.  This patch ensures
the correct setting.

This patch applies cleanly to v4.4+.  It could go to
earlier kernels also, so I will send backports to the
stable list in due course.

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
Cc: stable@vger.kernel.org # v4.4+
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
hifive-unleashed-5.1
Adrian Hunter 2016-04-15 14:06:57 +03:00 committed by Ulf Hansson
parent c3b46c7326
commit 6e1c7d6103
2 changed files with 82 additions and 0 deletions

View File

@ -97,6 +97,7 @@ config MMC_RICOH_MMC
config MMC_SDHCI_ACPI config MMC_SDHCI_ACPI
tristate "SDHCI support for ACPI enumerated SDHCI controllers" tristate "SDHCI support for ACPI enumerated SDHCI controllers"
depends on MMC_SDHCI && ACPI depends on MMC_SDHCI && ACPI
select IOSF_MBI if X86
help help
This selects support for ACPI enumerated SDHCI controllers, This selects support for ACPI enumerated SDHCI controllers,
identified by ACPI Compatibility ID PNP0D40 or specific identified by ACPI Compatibility ID PNP0D40 or specific

View File

@ -41,6 +41,11 @@
#include <linux/mmc/pm.h> #include <linux/mmc/pm.h>
#include <linux/mmc/slot-gpio.h> #include <linux/mmc/slot-gpio.h>
#ifdef CONFIG_X86
#include <asm/cpu_device_id.h>
#include <asm/iosf_mbi.h>
#endif
#include "sdhci.h" #include "sdhci.h"
enum { enum {
@ -116,6 +121,75 @@ static const struct sdhci_acpi_chip sdhci_acpi_chip_int = {
.ops = &sdhci_acpi_ops_int, .ops = &sdhci_acpi_ops_int,
}; };
#ifdef CONFIG_X86
static bool sdhci_acpi_byt(void)
{
static const struct x86_cpu_id byt[] = {
{ X86_VENDOR_INTEL, 6, 0x37 },
{}
};
return x86_match_cpu(byt);
}
#define BYT_IOSF_SCCEP 0x63
#define BYT_IOSF_OCP_NETCTRL0 0x1078
#define BYT_IOSF_OCP_TIMEOUT_BASE GENMASK(10, 8)
static void sdhci_acpi_byt_setting(struct device *dev)
{
u32 val = 0;
if (!sdhci_acpi_byt())
return;
if (iosf_mbi_read(BYT_IOSF_SCCEP, MBI_CR_READ, BYT_IOSF_OCP_NETCTRL0,
&val)) {
dev_err(dev, "%s read error\n", __func__);
return;
}
if (!(val & BYT_IOSF_OCP_TIMEOUT_BASE))
return;
val &= ~BYT_IOSF_OCP_TIMEOUT_BASE;
if (iosf_mbi_write(BYT_IOSF_SCCEP, MBI_CR_WRITE, BYT_IOSF_OCP_NETCTRL0,
val)) {
dev_err(dev, "%s write error\n", __func__);
return;
}
dev_dbg(dev, "%s completed\n", __func__);
}
static bool sdhci_acpi_byt_defer(struct device *dev)
{
if (!sdhci_acpi_byt())
return false;
if (!iosf_mbi_available())
return true;
sdhci_acpi_byt_setting(dev);
return false;
}
#else
static inline void sdhci_acpi_byt_setting(struct device *dev)
{
}
static inline bool sdhci_acpi_byt_defer(struct device *dev)
{
return false;
}
#endif
static int bxt_get_cd(struct mmc_host *mmc) static int bxt_get_cd(struct mmc_host *mmc)
{ {
int gpio_cd = mmc_gpio_get_cd(mmc); int gpio_cd = mmc_gpio_get_cd(mmc);
@ -322,6 +396,9 @@ static int sdhci_acpi_probe(struct platform_device *pdev)
if (acpi_bus_get_status(device) || !device->status.present) if (acpi_bus_get_status(device) || !device->status.present)
return -ENODEV; return -ENODEV;
if (sdhci_acpi_byt_defer(dev))
return -EPROBE_DEFER;
hid = acpi_device_hid(device); hid = acpi_device_hid(device);
uid = device->pnp.unique_id; uid = device->pnp.unique_id;
@ -447,6 +524,8 @@ static int sdhci_acpi_resume(struct device *dev)
{ {
struct sdhci_acpi_host *c = dev_get_drvdata(dev); struct sdhci_acpi_host *c = dev_get_drvdata(dev);
sdhci_acpi_byt_setting(&c->pdev->dev);
return sdhci_resume_host(c->host); return sdhci_resume_host(c->host);
} }
@ -470,6 +549,8 @@ static int sdhci_acpi_runtime_resume(struct device *dev)
{ {
struct sdhci_acpi_host *c = dev_get_drvdata(dev); struct sdhci_acpi_host *c = dev_get_drvdata(dev);
sdhci_acpi_byt_setting(&c->pdev->dev);
return sdhci_runtime_resume_host(c->host); return sdhci_runtime_resume_host(c->host);
} }