diff mbox

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

Message ID 6E692E1B-FB67-4FB0-9316-9E990CEB89ED@marvell.com (mailing list archive)
State New, archived
Headers show

Commit Message

Philip Rakity Sept. 30, 2010, 5:27 a.m. UTC
None
diff mbox

Patch

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 */