[linux-yocto] [PATCH 1/2] valleyisland-io: add patch for valley island SDHC controllers support

rebecca.swee.fun.chang at intel.com rebecca.swee.fun.chang at intel.com
Mon Dec 2 18:51:47 PST 2013


From: "Chang, Rebecca Swee Fun" <rebecca.swee.fun.chang at intel.com>

mmc: Valley Island has three SDHC controllers, namely eMMC controller,
SDIO controller and SDCARD controller. The controllers can be PCI or
ACPI enumerated.

Signed-off-by: Chang, Rebecca Swee Fun <rebecca.swee.fun.chang at intel.com>
---
 ...port-for-SDHC-controllers-on-Intel-Baytra.patch |  383 ++++++++++++++++++++
 1 file changed, 383 insertions(+)
 create mode 100644 meta/cfg/kernel-cache/features/valleyisland-io/0015-mmc-Add-support-for-SDHC-controllers-on-Intel-Baytra.patch

diff --git a/meta/cfg/kernel-cache/features/valleyisland-io/0015-mmc-Add-support-for-SDHC-controllers-on-Intel-Baytra.patch b/meta/cfg/kernel-cache/features/valleyisland-io/0015-mmc-Add-support-for-SDHC-controllers-on-Intel-Baytra.patch
new file mode 100644
index 0000000..f2e155f
--- /dev/null
+++ b/meta/cfg/kernel-cache/features/valleyisland-io/0015-mmc-Add-support-for-SDHC-controllers-on-Intel-Baytra.patch
@@ -0,0 +1,383 @@
+mmc: Add support for SDHC controllers on Intel Valley Island platform
+
+Valley Island has three SDHC controllers, namely eMMC controller, SDIO controller
+and SDCARD controller. The controllers can be PCI or ACPI enumerated.
+
+Signed-off-by: Chang, Rebecca Swee Fun <rebecca.swee.fun.chang at intel.com>
+---
+ drivers/mmc/core/core.c       |    5 ++-
+ drivers/mmc/core/sdio_bus.c   |   20 ++++++++++-
+ drivers/mmc/host/sdhci-acpi.c |   71 +++++++++++++++++++++++++++++++++------
+ drivers/mmc/host/sdhci-pci.c  |   73 +++++++++++++++++++++++++++++++++++++++++
+ drivers/mmc/host/sdhci.c      |   19 +++++++++++
+ include/linux/mmc/host.h      |    3 ++
+ include/linux/mmc/sdhci.h     |    4 +++
+ 7 files changed, 183 insertions(+), 12 deletions(-)
+
+diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
+index aaed768..155185f 100644
+--- a/drivers/mmc/core/core.c
++++ b/drivers/mmc/core/core.c
+@@ -2223,7 +2223,10 @@ void mmc_start_host(struct mmc_host *host)
+ {
+ 	host->f_init = max(freqs[0], host->f_min);
+ 	host->rescan_disable = 0;
+-	mmc_power_up(host);
++	if (host->caps2 & MMC_CAP2_NO_PRESCAN_POWERUP)
++		mmc_power_off(host);
++	else
++		mmc_power_up(host);
+ 	mmc_detect_change(host, 0);
+ }
+
+diff --git a/drivers/mmc/core/sdio_bus.c b/drivers/mmc/core/sdio_bus.c
+index 5e57048..8d6bb18 100644
+--- a/drivers/mmc/core/sdio_bus.c
++++ b/drivers/mmc/core/sdio_bus.c
+@@ -16,6 +16,7 @@
+ #include <linux/export.h>
+ #include <linux/slab.h>
+ #include <linux/pm_runtime.h>
++#include <linux/acpi.h>
+
+ #include <linux/mmc/card.h>
+ #include <linux/mmc/host.h>
+@@ -299,6 +300,19 @@ struct sdio_func *sdio_alloc_func(struct mmc_card *card)
+ 	return func;
+ }
+
++#ifdef CONFIG_ACPI
++static void sdio_acpi_set_handle(struct sdio_func *func)
++{
++	struct mmc_host *host = func->card->host;
++	u64 addr = (host->slotno << 16) | func->num;
++
++	ACPI_HANDLE_SET(&func->dev,
++			acpi_get_child(ACPI_HANDLE(host->parent), addr));
++}
++#else
++static inline void sdio_acpi_set_handle(struct sdio_func *func) {}
++#endif
++
+ /*
+  * Register a new SDIO function with the driver model.
+  */
+@@ -308,9 +322,12 @@ int sdio_add_func(struct sdio_func *func)
+
+ 	dev_set_name(&func->dev, "%s:%d", mmc_card_id(func->card), func->num);
+
++	sdio_acpi_set_handle(func);
+ 	ret = device_add(&func->dev);
+-	if (ret == 0)
++	if (ret == 0) {
+ 		sdio_func_set_present(func);
++		acpi_dev_pm_attach(&func->dev, false);
++	}
+
+ 	return ret;
+ }
+@@ -326,6 +343,7 @@ void sdio_remove_func(struct sdio_func *func)
+ 	if (!sdio_func_present(func))
+ 		return;
+
++	acpi_dev_pm_detach(&func->dev, false);
+ 	device_del(&func->dev);
+ 	put_device(&func->dev);
+ }
+diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c
+index 2592ddd..a0fd8a1 100644
+--- a/drivers/mmc/host/sdhci-acpi.c
++++ b/drivers/mmc/host/sdhci-acpi.c
+@@ -87,30 +87,78 @@ static const struct sdhci_ops sdhci_acpi_ops_dflt = {
+ 	.enable_dma = sdhci_acpi_enable_dma,
+ };
+
++static const struct sdhci_acpi_slot sdhci_acpi_slot_int_emmc = {
++	.caps    = MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE,
++	.caps2   = MMC_CAP2_HC_ERASE_SZ,
++};
++
+ static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sdio = {
+ 	.quirks2 = SDHCI_QUIRK2_HOST_OFF_CARD_ON,
+ 	.caps    = MMC_CAP_NONREMOVABLE | MMC_CAP_POWER_OFF_CARD,
+-	.flags   = SDHCI_ACPI_RUNTIME_PM,
+ 	.pm_caps = MMC_PM_KEEP_POWER,
+ };
+
++static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sd = {
++};
++
++struct sdhci_acpi_uid_slot {
++	const char *hid;
++	const char *uid;
++	const struct sdhci_acpi_slot *slot;
++};
++
++static const struct sdhci_acpi_uid_slot sdhci_acpi_uids[] = {
++	{ "80860F14" , "1" , &sdhci_acpi_slot_int_emmc },
++	{ "80860F14" , "3" , &sdhci_acpi_slot_int_sd   },
++	{ "INT33BB"  , "2" , &sdhci_acpi_slot_int_sdio },
++	{ "INT33C6"  , NULL, &sdhci_acpi_slot_int_sdio },
++	{ "PNP0D40"  },
++	{ },
++};
++
+ static const struct acpi_device_id sdhci_acpi_ids[] = {
+-	{ "INT33C6", (kernel_ulong_t)&sdhci_acpi_slot_int_sdio },
+-	{ "PNP0D40" },
++	{ "80860F14" },
++	{ "INT33BB"  },
++	{ "INT33C6"  },
++	{ "PNP0D40"  },
+ 	{ },
+ };
+ MODULE_DEVICE_TABLE(acpi, sdhci_acpi_ids);
+
+-static const struct sdhci_acpi_slot *sdhci_acpi_get_slot(const char *hid)
++static const struct sdhci_acpi_slot *sdhci_acpi_get_slot_by_ids(const char *hid,
++								const char *uid)
+ {
+-	const struct acpi_device_id *id;
+-
+-	for (id = sdhci_acpi_ids; id->id[0]; id++)
+-		if (!strcmp(id->id, hid))
+-			return (const struct sdhci_acpi_slot *)id->driver_data;
++	const struct sdhci_acpi_uid_slot *u;
++
++	for (u = sdhci_acpi_uids; u->hid; u++) {
++		if (strcmp(u->hid, hid))
++			continue;
++		if (!u->uid)
++			return u->slot;
++		if (uid && !strcmp(u->uid, uid))
++			return u->slot;
++	}
+ 	return NULL;
+ }
+
++static const struct sdhci_acpi_slot *sdhci_acpi_get_slot(acpi_handle handle,
++							 const char *hid)
++{
++	const struct sdhci_acpi_slot *slot;
++	struct acpi_device_info *info;
++	const char *uid = NULL;
++	acpi_status status;
++
++	status = acpi_get_object_info(handle, &info);
++	if (!ACPI_FAILURE(status) && (info->valid & ACPI_VALID_UID))
++		uid = info->unique_id.string;
++
++	slot = sdhci_acpi_get_slot_by_ids(hid, uid);
++
++	kfree(info);
++	return slot;
++}
++
+ static int sdhci_acpi_probe(struct platform_device *pdev)
+ {
+ 	struct device *dev = &pdev->dev;
+@@ -148,7 +196,7 @@ static int sdhci_acpi_probe(struct platform_device *pdev)
+
+ 	c = sdhci_priv(host);
+ 	c->host = host;
+-	c->slot = sdhci_acpi_get_slot(hid);
++	c->slot = sdhci_acpi_get_slot(handle, hid);
+ 	c->pdev = pdev;
+ 	c->use_runtime_pm = sdhci_acpi_flag(c, SDHCI_ACPI_RUNTIME_PM);
+
+@@ -195,11 +243,14 @@ static int sdhci_acpi_probe(struct platform_device *pdev)
+ 		host->mmc->pm_caps  |= c->slot->pm_caps;
+ 	}
+
++	host->mmc->caps2 |= MMC_CAP2_NO_PRESCAN_POWERUP;
++
+ 	err = sdhci_add_host(host);
+ 	if (err)
+ 		goto err_free;
+ 
+ 	if (c->use_runtime_pm) {
++		pm_runtime_set_active(dev);
+ 		pm_suspend_ignore_children(dev, 1);
+ 		pm_runtime_set_autosuspend_delay(dev, 50);
+ 		pm_runtime_use_autosuspend(dev);
+diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c
+index c7dd0cb..9f662c6 100644
+--- a/drivers/mmc/host/sdhci-pci.c
++++ b/drivers/mmc/host/sdhci-pci.c
+@@ -48,6 +48,12 @@
+
+ #define MAX_SLOTS			8
+
++#define PCI_DEVICE_ID_INTEL_BYT_EMMC0	0x0F14 /*eMMC4.41*/
++#define PCI_DEVICE_ID_INTEL_BYT_EMMC1    0x0F50 /*eMMC4.5*/
++#define PCI_DEVICE_ID_INTEL_BYT_SDIO	0x0F15
++#define PCI_DEVICE_ID_INTEL_BYT_SD	0x0F16
++
++
+ struct sdhci_pci_chip;
+ struct sdhci_pci_slot;
+
+@@ -304,6 +310,39 @@ static const struct sdhci_pci_fixes sdhci_intel_pch_sdio = {
+ 	.probe_slot	= pch_hc_probe_slot,
+ };
+
++static int byt_emmc_probe_slot(struct sdhci_pci_slot *slot)
++{
++	slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE;
++	slot->host->mmc->caps2 |= MMC_CAP2_HC_ERASE_SZ;
++	return 0;
++}
++
++static int byt_sdio_probe_slot(struct sdhci_pci_slot *slot)
++{
++	slot->host->mmc->caps |= MMC_CAP_POWER_OFF_CARD | MMC_CAP_NONREMOVABLE;
++	return 0;
++}
++
++static const struct sdhci_pci_fixes sdhci_intel_byt_emmc0 = {
++	.allow_runtime_pm = false,
++	.probe_slot	= byt_emmc_probe_slot,
++};
++
++static const struct sdhci_pci_fixes sdhci_intel_byt_emmc1 = {
++        .allow_runtime_pm = false,
++        .probe_slot     = byt_emmc_probe_slot,
++};
++
++static const struct sdhci_pci_fixes sdhci_intel_byt_sdio = {
++	.quirks2	= SDHCI_QUIRK2_HOST_OFF_CARD_ON,
++	.allow_runtime_pm = false,
++	.probe_slot	= byt_sdio_probe_slot,
++};
++
++static const struct sdhci_pci_fixes sdhci_intel_byt_sd = {
++	.quirks2	= SDHCI_QUIRK2_NO_PRESET | SDHCI_QUIRK2_DDR50_LOW_CLOCK,
++};
++
+ /* O2Micro extra registers */
+ #define O2_SD_LOCK_WP		0xD3
+ #define O2_SD_MULTI_VCC3V	0xEE
+@@ -856,6 +895,38 @@ static const struct pci_device_id pci_ids[] = {
+ 	},
+
+ 	{
++		.vendor		= PCI_VENDOR_ID_INTEL,
++		.device		= PCI_DEVICE_ID_INTEL_BYT_EMMC0,
++		.subvendor	= PCI_ANY_ID,
++		.subdevice	= PCI_ANY_ID,
++		.driver_data	= (kernel_ulong_t)&sdhci_intel_byt_emmc0,
++	},
++
++	{
++                .vendor         = PCI_VENDOR_ID_INTEL,
++                .device         = PCI_DEVICE_ID_INTEL_BYT_EMMC1,
++                .subvendor      = PCI_ANY_ID,
++                .subdevice      = PCI_ANY_ID,
++                .driver_data    = (kernel_ulong_t)&sdhci_intel_byt_emmc1,
++        },
++
++	{
++		.vendor		= PCI_VENDOR_ID_INTEL,
++		.device		= PCI_DEVICE_ID_INTEL_BYT_SDIO,
++		.subvendor	= PCI_ANY_ID,
++		.subdevice	= PCI_ANY_ID,
++		.driver_data	= (kernel_ulong_t)&sdhci_intel_byt_sdio,
++	},
++
++	{
++		.vendor		= PCI_VENDOR_ID_INTEL,
++		.device		= PCI_DEVICE_ID_INTEL_BYT_SD,
++		.subvendor	= PCI_ANY_ID,
++		.subdevice	= PCI_ANY_ID,
++		.driver_data	= (kernel_ulong_t)&sdhci_intel_byt_sd,
++	},
++
++	{
+ 		.vendor		= PCI_VENDOR_ID_O2,
+ 		.device		= PCI_DEVICE_ID_O2_8120,
+ 		.subvendor	= PCI_ANY_ID,
+@@ -1279,6 +1350,8 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot(
+ 	}
+
+ 	host->mmc->pm_caps = MMC_PM_KEEP_POWER | MMC_PM_WAKE_SDIO_IRQ;
++	host->mmc->slotno = slotno;
++	host->mmc->caps2 |= MMC_CAP2_NO_PRESCAN_POWERUP;
+
+ 	ret = sdhci_add_host(host);
+ 	if (ret)
+diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
+index 6f0bfc0..a6f5c14 100644
+--- a/drivers/mmc/host/sdhci.c
++++ b/drivers/mmc/host/sdhci.c
+@@ -1148,6 +1148,15 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
+ 			}
+ 			real_div = div;
+ 			div >>= 1;
++
++			if(host->quirks2 & SDHCI_QUIRK2_DDR50_LOW_CLOCK){
++				/* DDR50 speed at 25MHz and lower */
++				if (div == 1 && (sdhci_readw(host, SDHCI_HOST_CONTROL2) &
++								SDHCI_CTRL_UHS_DDR50)){
++					div <<= 1;
++					real_div = (div <<= 1);
++				}
++			}
+ 		}
+ 	} else {
+ 		/* Version 2.00 divisors must be a power of 2. */
+@@ -1968,6 +1977,16 @@ static void sdhci_do_enable_preset_value(struct sdhci_host *host, bool enable)
+
+	ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+
++	if(host->quirks2 & SDHCI_QUIRK2_NO_PRESET){
++		if(ctrl & SDHCI_CTRL_PRESET_VAL_ENABLE){
++			ctrl &= ~SDHCI_CTRL_PRESET_VAL_ENABLE;
++			sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
++			host->flags &= ~SDHCI_PV_ENABLED;
++		}
++		spin_unlock_irqrestore(&host->lock, flags);
++		return;
++	}
++
+ 	/*
+ 	 * We only enable or disable Preset Value if they are not already
+ 	 * enabled or disabled respectively. Otherwise, we bail out.
+diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
+index 61a10c1..b4a8293 100644
+--- a/include/linux/mmc/host.h
++++ b/include/linux/mmc/host.h
+@@ -258,6 +258,7 @@ struct mmc_host {
+ #define MMC_CAP2_HC_ERASE_SZ	(1 << 9)	/* High-capacity erase size */
+ #define MMC_CAP2_CD_ACTIVE_HIGH	(1 << 10)	/* Card-detect signal active high */
+ #define MMC_CAP2_RO_ACTIVE_HIGH	(1 << 11)	/* Write-protect signal active high */
++#define MMC_CAP2_NO_PRESCAN_POWERUP (1 << 14)	/* Don't power up before scan */
+
+ 	mmc_pm_flag_t		pm_caps;	/* supported pm features */
+
+@@ -338,6 +339,8 @@ struct mmc_host {
+
+ 	unsigned int		actual_clock;	/* Actual HC clock rate */
+
++	unsigned int		slotno;	/* used for sdio acpi binding */
++
+ 	unsigned long		private[0] ____cacheline_aligned;
+ };
+
+diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h
+index 4bbc330..f53100b 100644
+--- a/include/linux/mmc/sdhci.h
++++ b/include/linux/mmc/sdhci.h
+@@ -94,6 +94,10 @@ struct sdhci_host {
+ #define SDHCI_QUIRK2_HOST_NO_CMD23			(1<<1)
+ /* The system physically doesn't support 1.8v, even if the host does */
+ #define SDHCI_QUIRK2_NO_1_8_V				(1<<2)
++/* Controller allows driver to take over preset value */
++#define SDHCI_QUIRK2_NO_PRESET				(1<<3)
++/* Controller needs low clock speed for DDR50 */
++#define SDHCI_QUIRK2_DDR50_LOW_CLOCK			(1<<4)
+
+ 	int irq;		/* Device IRQ */
+ 	void __iomem *ioaddr;	/* Mapped address */
+--
+1.7.10.4
+
-- 
1.7.10.4



More information about the linux-yocto mailing list