[RFC] sdhci: add support for sd 3.0 host and hooks for dual data rate eMMC

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This code has not been tested on mmc-test but has been patched against.  I have run it against 2.6.32 (with some minor changes).

I wanted to give folks a chance to comment before making it a formal patch.  The code add the hooks into the sdhci layer for 
version 3.0 of the controller, including mmc/eMMC dual data rate support (ddr).    The next RFC adds support for DDR at the mmc layer

The question is how to provide DDR support.  I punted this and added hooks to let the h/w adaption code handle this since
a) tuning may be required and it is unclear what values to set the registers to that will work in all cases
b) higher speed single data -- not sure how to handle this


Philip

>From 0b03d8838cc4a55f05f1bcad950843024a353d3d Mon Sep 17 00:00:00 2001
From: Philip Rakity <prakity@xxxxxxxxxxx>
Date: Wed, 29 Sep 2010 20:46:16 -0700
Subject: [RFC] sdhci: add support for sd 3.0 host and hooks for dual data rate eMMC
 Signed-off-by: Philip Rakity <prakity@xxxxxxxxxxx>

---
 drivers/mmc/host/sdhci.c |   81 +++++++++++++++++++++++++++++++++++++++-------
 drivers/mmc/host/sdhci.h |   51 +++++++++++++++++++++++++++--
 include/linux/mmc/host.h |    1 +
 3 files changed, 118 insertions(+), 15 deletions(-)

diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 401527d..b17e438 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -76,8 +76,10 @@ static void sdhci_dumpregs(struct sdhci_host *host)
 	printk(KERN_DEBUG DRIVER_NAME ": AC12 err: 0x%08x | Slot int: 0x%08x\n",
 		sdhci_readw(host, SDHCI_ACMD12_ERR),
 		sdhci_readw(host, SDHCI_SLOT_INT_STATUS));
-	printk(KERN_DEBUG DRIVER_NAME ": Caps:     0x%08x | Max curr: 0x%08x\n",
+	printk(KERN_DEBUG DRIVER_NAME ": Caps:     0x%08x | Caps1:    0x%08x\n",
 		sdhci_readl(host, SDHCI_CAPABILITIES),
+		sdhci_readl(host, SDHCI_CAPABILITIES_1));
+	printk(KERN_DEBUG DRIVER_NAME ": Max curr: 0x%08x\n",
 		sdhci_readl(host, SDHCI_MAX_CURRENT));
 
 	if (host->flags & SDHCI_USE_ADMA)
@@ -1001,9 +1003,19 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
 	if (clock == 0)
 		goto out;
 
-	for (div = 1;div < 256;div *= 2) {
-		if ((host->max_clk / div) <= clock)
-			break;
+	if (host->version >= SDHCI_SPEC_300) {
+		if (host->max_clk <= clock)
+			div = 1;
+		else {
+			div = host->max_clk/clock;
+			if (host->max_clk % clock)
+				div++;
+		}
+	} else {
+		for (div = 1;div < 256;div *= 2) {
+			if ((host->max_clk / div) <= clock)
+				break;
+		}
 	}
 	div >>= 1;
 
@@ -1025,6 +1037,9 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
 		mdelay(1);
 	}
 
+	clk = (div & SDHCI_CLOCK_DIV_MASK) << SDHCI_DIVIDER_SHIFT;
+	clk |= ((div & SDHCI_CLOCK_DIV_HI_MASK) >> SDHCI_CLOCK_DIV_MASK_LEN)
+			<< SDHCI_CLOCK_DIVIDER_HI_SHIFT;
 	clk |= SDHCI_CLOCK_CARD_EN;
 	sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
 
@@ -1169,11 +1184,13 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 		sdhci_set_power(host, ios->vdd);
 
 	ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
-
-	if (ios->bus_width == MMC_BUS_WIDTH_8)
-		ctrl |= SDHCI_CTRL_8BITBUS;
-	else
-		ctrl &= ~SDHCI_CTRL_8BITBUS;
+	if (host->version >= SDHCI_SPEC_300) {
+		if (ios->bus_width == MMC_BUS_WIDTH_8) {
+			ctrl |= SDHCI_CTRL_8BITBUS;
+			ctrl &= ~SDHCI_CTRL_4BITBUS;
+		} else
+			ctrl &= ~SDHCI_CTRL_8BITBUS;
+	}
 
 	if (ios->bus_width == MMC_BUS_WIDTH_4)
 		ctrl |= SDHCI_CTRL_4BITBUS;
@@ -1189,6 +1206,14 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 	sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
 
 	/*
+	 * higher speed data rates need tuning - board specific
+	 * punt handling these speeds to the adoption layer
+	 */
+	if ((host->flags & SDHCI_DATA_RATES_300) &&
+		host->ops->program_v3_rate)
+		host->ops->program_v3_rate(host, ios);
+
+	/*
 	 * Some (ENE) controllers go apeshit on some ios operation,
 	 * signalling timeout and CRC errors even on CMD0. Resetting
 	 * it on each ios seems to solve the problem.
@@ -1692,6 +1717,7 @@ int sdhci_add_host(struct sdhci_host *host)
 {
 	struct mmc_host *mmc;
 	unsigned int caps;
+	unsigned int caps_1;
 	int ret;
 
 	WARN_ON(host == NULL);
@@ -1708,7 +1734,7 @@ int sdhci_add_host(struct sdhci_host *host)
 	host->version = sdhci_readw(host, SDHCI_HOST_VERSION);
 	host->version = (host->version & SDHCI_SPEC_VER_MASK)
 				>> SDHCI_SPEC_VER_SHIFT;
-	if (host->version > SDHCI_SPEC_200) {
+	if (host->version > SDHCI_SPEC_300) {
 		printk(KERN_ERR "%s: Unknown controller version (%d). "
 			"You may experience problems.\n", mmc_hostname(mmc),
 			host->version);
@@ -1717,6 +1743,13 @@ int sdhci_add_host(struct sdhci_host *host)
 	caps = (host->quirks & SDHCI_QUIRK_MISSING_CAPS) ? host->caps :
 		sdhci_readl(host, SDHCI_CAPABILITIES);
 
+	if (host->version >= SDHCI_SPEC_300) {
+		caps_1 = (host->quirks & SDHCI_QUIRK_MISSING_CAPS_1) ?
+			host->caps_1 : sdhci_readl(host, SDHCI_CAPABILITIES_1);
+	}
+	else
+		caps_1 = 0;
+
 	if (host->quirks & SDHCI_QUIRK_FORCE_DMA)
 		host->flags |= SDHCI_USE_SDMA;
 	else if (!(caps & SDHCI_CAN_DO_SDMA))
@@ -1779,8 +1812,14 @@ int sdhci_add_host(struct sdhci_host *host)
 		mmc_dev(host->mmc)->dma_mask = &host->dma_mask;
 	}
 
-	host->max_clk =
-		(caps & SDHCI_CLOCK_BASE_MASK) >> SDHCI_CLOCK_BASE_SHIFT;
+	if (host->version >= SDHCI_SPEC_300)
+		host->max_clk =
+			(caps & SDHCI_CLOCK_BASE_MASK_300) >>
+				SDHCI_CLOCK_BASE_SHIFT;
+	else
+		host->max_clk =
+			(caps & SDHCI_CLOCK_BASE_MASK) >>
+				SDHCI_CLOCK_BASE_SHIFT;
 	host->max_clk *= 1000000;
 	if (host->max_clk == 0 || host->quirks &
 			SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN) {
@@ -1815,6 +1854,8 @@ int sdhci_add_host(struct sdhci_host *host)
 	mmc->ops = &sdhci_ops;
 	if (host->ops->get_min_clock)
 		mmc->f_min = host->ops->get_min_clock(host);
+	else if (host->version >= SDHCI_SPEC_300)
+		mmc->f_min = host->max_clk / 2046;
 	else
 		mmc->f_min = host->max_clk / 256;
 	mmc->f_max = host->max_clk;
@@ -1829,6 +1870,22 @@ int sdhci_add_host(struct sdhci_host *host)
 	if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION)
 		mmc->caps |= MMC_CAP_NEEDS_POLL;
 
+	/* require platform code to handle v3 speeds */
+	if (host->version >= SDHCI_SPEC_300 && host->ops->program_v3_rate) {
+		if (host->ops->support_v3_data_rates &&
+			host->ops->support_v3_data_rates(host, caps_1)) {
+				mmc->caps |= MMC_CAP_DUAL_DATA_RATE;
+				host->flags |= SDHCI_DATA_RATES_300;
+		}
+		else if (caps_1 & (	SDHCI_CAN_DO_SDR50 |
+					SDHCI_CAN_DO_SDR104 |
+					SDHCI_CAN_DO_DDR50)) {
+				host->flags |= SDHCI_DATA_RATES_300;
+			if (caps_1 & SDHCI_CAN_DO_DDR50)
+				mmc->caps |= MMC_CAP_DUAL_DATA_RATE;
+		}
+	}
+
 	mmc->ocr_avail = 0;
 	if (caps & SDHCI_CAN_VDD_330)
 		mmc->ocr_avail |= MMC_VDD_32_33|MMC_VDD_33_34;
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index d316bc7..1608151 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -72,7 +72,7 @@
 #define   SDHCI_CTRL_ADMA1	0x08
 #define   SDHCI_CTRL_ADMA32	0x10
 #define   SDHCI_CTRL_ADMA64	0x18
-#define  SDHCI_CTRL_8BITBUS	0x20
+#define   SDHCI_CTRL_8BITBUS	0x20
 
 #define SDHCI_POWER_CONTROL	0x29
 #define  SDHCI_POWER_ON		0x01
@@ -86,6 +86,10 @@
 
 #define SDHCI_CLOCK_CONTROL	0x2C
 #define  SDHCI_DIVIDER_SHIFT	8
+#define  SDHCI_CLOCK_DIV_MASK	0xFF
+#define  SDHCI_CLOCK_DIVIDER_HI_SHIFT	6
+#define  SDHCI_CLOCK_DIV_MASK_LEN	8
+#define  SDHCI_CLOCK_DIV_HI_MASK	0x300
 #define  SDHCI_CLOCK_CARD_EN	0x0004
 #define  SDHCI_CLOCK_INT_STABLE	0x0002
 #define  SDHCI_CLOCK_INT_EN	0x0001
@@ -133,13 +137,32 @@
 
 #define SDHCI_ACMD12_ERR	0x3C
 
-/* 3E-3F reserved */
+#define HOST_CONTROL_2		0x3E
+#define  SDHCI_CTL2_UHS_MODE_SEL_SDR12		0
+#define  SDHCI_CTL2_UHS_MODE_SEL_SDR25		1
+#define  SDHCI_CTL2_UHS_MODE_SEL_SDR50		2
+#define  SDHCI_CTL2_UHS_MODE_SEL_SDR104		3
+#define  SDHCI_CTL2_UHS_MODE_SEL_DDR50		4
+#define  SDHCI_CTL2_UHS_MODE_MASK		0x7
+#define  SDHCI_CTL2_UHS_MODE_SHIFT		0
+#define  SDHCI_CTL2_SDH_V18_EN			0x00000008
+#define  SDHCI_CTL2_DRV_STRENGTH_SEL_B		0
+#define  SDHCI_CTL2_DRV_STRENGTH_SEL_A		1
+#define  SDHCI_CTL2_DRV_STRENGTH_SEL_C		2
+#define  SDHCI_CTL2_DRV_STRENGTH_SEL_D		3
+#define  SDHCI_CTL2_DRV_STRENGTH_MASK		0x3
+#define  SDHCI_CTL2_DRV_STRENGTH_SHIFT		4
+#define  SDHCI_CTL2_EXE_TUNING			0x00000040
+#define  SDHCI_CTL2_SAMPLING_CLK_SEL		0x00000080
+#define  SDHCI_CTL2_ASYNC_INT_EN		0x00004000
+#define  SDHCI_CTL2_PRE_VAL_EN			0x00008000
 
 #define SDHCI_CAPABILITIES	0x40
 #define  SDHCI_TIMEOUT_CLK_MASK	0x0000003F
 #define  SDHCI_TIMEOUT_CLK_SHIFT 0
 #define  SDHCI_TIMEOUT_CLK_UNIT	0x00000080
 #define  SDHCI_CLOCK_BASE_MASK	0x00003F00
+#define  SDHCI_CLOCK_BASE_MASK_300	0x0000FF00
 #define  SDHCI_CLOCK_BASE_SHIFT	8
 #define  SDHCI_MAX_BLOCK_MASK	0x00030000
 #define  SDHCI_MAX_BLOCK_SHIFT  16
@@ -152,7 +175,20 @@
 #define  SDHCI_CAN_VDD_180	0x04000000
 #define  SDHCI_CAN_64BIT	0x10000000
 
-/* 44-47 reserved for more caps */
+#define SDHCI_CAPABILITIES_1	0x44
+#define  SDHCI_CAN_DO_SDR50	0x00000001
+#define  SDHCI_CAN_DO_SDR104	0x00000002
+#define  SDHCI_CAN_DO_DDR50	0x00000004
+#define  SDHCI_DRIVER_TYPE_A	0x00000010
+#define  SDHCI_DRIVER_TYPE_C	0x00000020
+#define  SDHCI_DRIVER_TYPE_D	0x00000040
+#define  SDHCI_RETUNING_TIME_COUNT_MASK	0x00000F00
+#define  SDHCI_RETUNING_TIME_COUNT_SHIFT	8
+#define  SDHCI_USE_TUNING_DDR50	0x00002000
+#define  SDHCI_RETUNING_MODE_MASK	0x0000C000
+#define  SDHCI_RETUNING_MODE_SHIFT	14
+#define  SDHCI_CLOCK_MULTIPLIER_MASK	0x00FF0000
+#define  SDHCI_CLOCK_MULTIPLIER_SHIFT	16
 
 #define SDHCI_MAX_CURRENT	0x48
 
@@ -178,6 +214,7 @@
 #define  SDHCI_SPEC_VER_SHIFT	0
 #define   SDHCI_SPEC_100	0
 #define   SDHCI_SPEC_200	1
+#define   SDHCI_SPEC_300	2
 
 struct sdhci_ops;
 
@@ -247,6 +284,8 @@ struct sdhci_host {
 #define SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12		(1<<28)
 /* Controller doesn't have HISPD bit field in HI-SPEED SD card */
 #define SDHCI_QUIRK_NO_HISPD_BIT			(1<<29)
+/* Controller is missing capability register 1 (sd 3.0)	*/
+#define SDHCI_QUIRK_MISSING_CAPS_1			(1<<30)
 
 	int			irq;		/* Device IRQ */
 	void __iomem *		ioaddr;		/* Mapped address */
@@ -271,6 +310,7 @@ struct sdhci_host {
 #define SDHCI_USE_ADMA		(1<<1)		/* Host is ADMA capable */
 #define SDHCI_REQ_USE_DMA	(1<<2)		/* Use DMA for this req. */
 #define SDHCI_DEVICE_DEAD	(1<<3)		/* Device unresponsive */
+#define SDHCI_DATA_RATES_300	(1<<4)		/* Host can do V3 data rates */
 
 	unsigned int		version;	/* SDHCI spec. version */
 
@@ -302,6 +342,7 @@ struct sdhci_host {
 	struct timer_list	timer;		/* Timer for timeouts */
 
 	unsigned int		caps;		/* Alternative capabilities */
+	unsigned int		caps_1;		/* Alternative capabilities */
 
 	unsigned long		private[0] ____cacheline_aligned;
 };
@@ -323,6 +364,10 @@ struct sdhci_ops {
 	unsigned int	(*get_max_clock)(struct sdhci_host *host);
 	unsigned int	(*get_min_clock)(struct sdhci_host *host);
 	unsigned int	(*get_timeout_clock)(struct sdhci_host *host);
+	int	(*support_v3_data_rates)(struct sdhci_host *host,
+			unsigned int caps_1);
+	int	(*program_v3_rate)(struct sdhci_host *host,
+			struct mmc_ios *ios);
 };
 
 #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 1575b52..6e63b49 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -157,6 +157,7 @@ struct mmc_host {
 #define MMC_CAP_NONREMOVABLE	(1 << 8)	/* Nonremovable e.g. eMMC */
 #define MMC_CAP_WAIT_WHILE_BUSY	(1 << 9)	/* Waits while card is busy */
 #define MMC_CAP_ERASE		(1 << 10)	/* Allow erase/trim commands */
+#define MMC_CAP_DUAL_DATA_RATE	(1 << 11)	/* MMC can do dual data rate */
 
 	mmc_pm_flag_t		pm_caps;	/* supported pm features */
 
-- 
1.6.0.4

--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux USB Devel]     [Linux Media]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux