forked from mirrors/linux
		
	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>
This commit is contained in:
		
							parent
							
								
									c3b46c7326
								
							
						
					
					
						commit
						6e1c7d6103
					
				
					 2 changed files with 82 additions and 0 deletions
				
			
		| 
						 | 
					@ -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
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue