diff options
author | Adrian Hunter <adrian.hunter@intel.com> | 2016-04-15 07:06:57 -0400 |
---|---|---|
committer | Ulf Hansson <ulf.hansson@linaro.org> | 2016-04-18 05:22:53 -0400 |
commit | 6e1c7d6103fe7031035cec321307c6356809adf4 (patch) | |
tree | d0c2e4002b8c4ad7d882a1046aef4f3c72f2ea8d | |
parent | c3b46c73264b03000d1e18b22f5caf63332547c9 (diff) |
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>
-rw-r--r-- | drivers/mmc/host/Kconfig | 1 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci-acpi.c | 81 |
2 files changed, 82 insertions, 0 deletions
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 04feea8354cb..e657af0e95fa 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig | |||
@@ -97,6 +97,7 @@ config MMC_RICOH_MMC | |||
97 | config MMC_SDHCI_ACPI | 97 | config MMC_SDHCI_ACPI |
98 | tristate "SDHCI support for ACPI enumerated SDHCI controllers" | 98 | tristate "SDHCI support for ACPI enumerated SDHCI controllers" |
99 | depends on MMC_SDHCI && ACPI | 99 | depends on MMC_SDHCI && ACPI |
100 | select IOSF_MBI if X86 | ||
100 | help | 101 | help |
101 | This selects support for ACPI enumerated SDHCI controllers, | 102 | This selects support for ACPI enumerated SDHCI controllers, |
102 | identified by ACPI Compatibility ID PNP0D40 or specific | 103 | identified by ACPI Compatibility ID PNP0D40 or specific |
diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c index 6839e41c6d58..bed6a494f52c 100644 --- a/drivers/mmc/host/sdhci-acpi.c +++ b/drivers/mmc/host/sdhci-acpi.c | |||
@@ -41,6 +41,11 @@ | |||
41 | #include <linux/mmc/pm.h> | 41 | #include <linux/mmc/pm.h> |
42 | #include <linux/mmc/slot-gpio.h> | 42 | #include <linux/mmc/slot-gpio.h> |
43 | 43 | ||
44 | #ifdef CONFIG_X86 | ||
45 | #include <asm/cpu_device_id.h> | ||
46 | #include <asm/iosf_mbi.h> | ||
47 | #endif | ||
48 | |||
44 | #include "sdhci.h" | 49 | #include "sdhci.h" |
45 | 50 | ||
46 | enum { | 51 | enum { |
@@ -116,6 +121,75 @@ static const struct sdhci_acpi_chip sdhci_acpi_chip_int = { | |||
116 | .ops = &sdhci_acpi_ops_int, | 121 | .ops = &sdhci_acpi_ops_int, |
117 | }; | 122 | }; |
118 | 123 | ||
124 | #ifdef CONFIG_X86 | ||
125 | |||
126 | static bool sdhci_acpi_byt(void) | ||
127 | { | ||
128 | static const struct x86_cpu_id byt[] = { | ||
129 | { X86_VENDOR_INTEL, 6, 0x37 }, | ||
130 | {} | ||
131 | }; | ||
132 | |||
133 | return x86_match_cpu(byt); | ||
134 | } | ||
135 | |||
136 | #define BYT_IOSF_SCCEP 0x63 | ||
137 | #define BYT_IOSF_OCP_NETCTRL0 0x1078 | ||
138 | #define BYT_IOSF_OCP_TIMEOUT_BASE GENMASK(10, 8) | ||
139 | |||
140 | static void sdhci_acpi_byt_setting(struct device *dev) | ||
141 | { | ||
142 | u32 val = 0; | ||
143 | |||
144 | if (!sdhci_acpi_byt()) | ||
145 | return; | ||
146 | |||
147 | if (iosf_mbi_read(BYT_IOSF_SCCEP, MBI_CR_READ, BYT_IOSF_OCP_NETCTRL0, | ||
148 | &val)) { | ||
149 | dev_err(dev, "%s read error\n", __func__); | ||
150 | return; | ||
151 | } | ||
152 | |||
153 | if (!(val & BYT_IOSF_OCP_TIMEOUT_BASE)) | ||
154 | return; | ||
155 | |||
156 | val &= ~BYT_IOSF_OCP_TIMEOUT_BASE; | ||
157 | |||
158 | if (iosf_mbi_write(BYT_IOSF_SCCEP, MBI_CR_WRITE, BYT_IOSF_OCP_NETCTRL0, | ||
159 | val)) { | ||
160 | dev_err(dev, "%s write error\n", __func__); | ||
161 | return; | ||
162 | } | ||
163 | |||
164 | dev_dbg(dev, "%s completed\n", __func__); | ||
165 | } | ||
166 | |||
167 | static bool sdhci_acpi_byt_defer(struct device *dev) | ||
168 | { | ||
169 | if (!sdhci_acpi_byt()) | ||
170 | return false; | ||
171 | |||
172 | if (!iosf_mbi_available()) | ||
173 | return true; | ||
174 | |||
175 | sdhci_acpi_byt_setting(dev); | ||
176 | |||
177 | return false; | ||
178 | } | ||
179 | |||
180 | #else | ||
181 | |||
182 | static inline void sdhci_acpi_byt_setting(struct device *dev) | ||
183 | { | ||
184 | } | ||
185 | |||
186 | static inline bool sdhci_acpi_byt_defer(struct device *dev) | ||
187 | { | ||
188 | return false; | ||
189 | } | ||
190 | |||
191 | #endif | ||
192 | |||
119 | static int bxt_get_cd(struct mmc_host *mmc) | 193 | static int bxt_get_cd(struct mmc_host *mmc) |
120 | { | 194 | { |
121 | int gpio_cd = mmc_gpio_get_cd(mmc); | 195 | int gpio_cd = mmc_gpio_get_cd(mmc); |
@@ -322,6 +396,9 @@ static int sdhci_acpi_probe(struct platform_device *pdev) | |||
322 | if (acpi_bus_get_status(device) || !device->status.present) | 396 | if (acpi_bus_get_status(device) || !device->status.present) |
323 | return -ENODEV; | 397 | return -ENODEV; |
324 | 398 | ||
399 | if (sdhci_acpi_byt_defer(dev)) | ||
400 | return -EPROBE_DEFER; | ||
401 | |||
325 | hid = acpi_device_hid(device); | 402 | hid = acpi_device_hid(device); |
326 | uid = device->pnp.unique_id; | 403 | uid = device->pnp.unique_id; |
327 | 404 | ||
@@ -447,6 +524,8 @@ static int sdhci_acpi_resume(struct device *dev) | |||
447 | { | 524 | { |
448 | struct sdhci_acpi_host *c = dev_get_drvdata(dev); | 525 | struct sdhci_acpi_host *c = dev_get_drvdata(dev); |
449 | 526 | ||
527 | sdhci_acpi_byt_setting(&c->pdev->dev); | ||
528 | |||
450 | return sdhci_resume_host(c->host); | 529 | return sdhci_resume_host(c->host); |
451 | } | 530 | } |
452 | 531 | ||
@@ -470,6 +549,8 @@ static int sdhci_acpi_runtime_resume(struct device *dev) | |||
470 | { | 549 | { |
471 | struct sdhci_acpi_host *c = dev_get_drvdata(dev); | 550 | struct sdhci_acpi_host *c = dev_get_drvdata(dev); |
472 | 551 | ||
552 | sdhci_acpi_byt_setting(&c->pdev->dev); | ||
553 | |||
473 | return sdhci_runtime_resume_host(c->host); | 554 | return sdhci_runtime_resume_host(c->host); |
474 | } | 555 | } |
475 | 556 | ||