All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/12] add support for sd host controller v3.00
@ 2011-02-15  9:34 Arindam Nath
  2011-02-15  9:34 ` [PATCH 01/12] mmc: sdhci: add support for auto CMD23 Arindam Nath
                   ` (13 more replies)
  0 siblings, 14 replies; 45+ messages in thread
From: Arindam Nath @ 2011-02-15  9:34 UTC (permalink / raw)
  To: cjb; +Cc: linux-mmc, henry.su, aaron.lu, anath.amd, Arindam Nath

The patches below add support for Host Controller v3.00 as per the
spec v3.00. It also adds support for UHS-I cards as per Physical
Layer Specification v3.01.

Thanks for review.

Regards,
Arindam

Arindam Nath (12):
  [PATCH 01/12] mmc: sdhci: add support for auto CMD23
  [PATCH 02/12] mmc: sd: add support for signal voltage switch procedure
  [PATCH 03/12] mmc: sd: query function modes for uhs cards
  [PATCH 04/12] mmc: sd: add support for driver type selection
  [PATCH 05/12] mmc: sdhci: reset sdclk before setting high speed enable
  [PATCH 06/12] mmc: sd: add support for uhs bus speed mode selection
  [PATCH 07/12] mmc: sd: set current limit for uhs cards
  [PATCH 08/12] mmc: sd: report correct speed and capacity of uhs cards
  [PATCH 09/12] mmc: sd: add support for tuning during uhs initialization
  [PATCH 10/12] mmc: sdhci: enable preset value after uhs initialization
  [PATCH 11/12] mmc: sdhci: add support for programmable clock mode
  [PATCH 12/12] mmc: sdhci: add support for retuning mode 1

 drivers/mmc/core/bus.c    |   11 +-
 drivers/mmc/core/core.c   |    9 +
 drivers/mmc/core/core.h   |    1 +
 drivers/mmc/core/sd.c     |  420 +++++++++++++++++++++++++---
 drivers/mmc/core/sd.h     |    3 +-
 drivers/mmc/core/sd_ops.c |   25 ++
 drivers/mmc/core/sd_ops.h |    1 +
 drivers/mmc/core/sdio.c   |    3 +-
 drivers/mmc/host/sdhci.c  |  692 ++++++++++++++++++++++++++++++++++++++++++---
 drivers/mmc/host/sdhci.h  |   45 +++-
 include/linux/mmc/card.h  |   43 +++
 include/linux/mmc/host.h  |   24 ++
 include/linux/mmc/mmc.h   |    1 +
 include/linux/mmc/sd.h    |    3 +-
 include/linux/mmc/sdhci.h |   11 +
 15 files changed, 1200 insertions(+), 92 deletions(-)


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH 01/12] mmc: sdhci: add support for auto CMD23
  2011-02-15  9:34 [PATCH 00/12] add support for sd host controller v3.00 Arindam Nath
@ 2011-02-15  9:34 ` Arindam Nath
  2011-02-15 20:58   ` Nicolas Pitre
  2011-02-18 10:45   ` zhangfei gao
  2011-02-15  9:35 ` [PATCH 02/12] mmc: sd: add support for signal voltage switch procedure Arindam Nath
                   ` (12 subsequent siblings)
  13 siblings, 2 replies; 45+ messages in thread
From: Arindam Nath @ 2011-02-15  9:34 UTC (permalink / raw)
  To: cjb; +Cc: linux-mmc, henry.su, aaron.lu, anath.amd, Arindam Nath

Host Controller v3.00 and later support Auto CMD23 in the Transfer
Mode register. Since Auto CMD23 can be used with or without DMA,
and if used with DMA, it should _only_ be ADMA, we check against
SDHCI_USE_SDMA not being set. This flag is reset when SDHCI_USE_ADMA
is set.

A new definition for SDHCI_ARGUMENT2 register has been added
in v3.00 spec, which is the same as SDHCI_DMA_ADDRESS. We program the
block count for CMD23 in SDHCI_ARGUMENT2, so we don't need CMD12 to
stop multiple block transfers. But during error recovery procedure,
we will need to send Abort command, so we use a global variable
abort_cmd to save the stop command to be used later.

Two bits are added to SCR register as per the Physical Layer Spec v3.01,
which specify whether the card supports CMD20 and/or CMD23. We use this
as one of the conditions to decide whether to enable Auto CMD23 or not.

Signed-off-by: Arindam Nath <arindam.nath@amd.com>
---
 drivers/mmc/core/sd.c    |    6 ++++
 drivers/mmc/host/sdhci.c |   66 +++++++++++++++++++++++++++++++++++++++++++--
 drivers/mmc/host/sdhci.h |    2 +
 include/linux/mmc/card.h |    4 +++
 include/linux/mmc/sd.h   |    2 +-
 5 files changed, 76 insertions(+), 4 deletions(-)

diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index d18c32b..b3f8a3c 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -188,6 +188,12 @@ static int mmc_decode_scr(struct mmc_card *card)
 
 	scr->sda_vsn = UNSTUFF_BITS(resp, 56, 4);
 	scr->bus_widths = UNSTUFF_BITS(resp, 48, 4);
+	if (scr->sda_vsn == SCR_SPEC_VER_2) {
+		/* Check if Physical Layer Spec v3.0 is supported*/
+		scr->sda_spec3 = UNSTUFF_BITS(resp, 47, 1);
+		if (scr->sda_spec3)
+			scr->cmd_support = UNSTUFF_BITS(resp, 32, 2);
+	}
 
 	if (UNSTUFF_BITS(resp, 55, 1))
 		card->erased_byte = 0xFF;
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 9e15f41..0b537cf 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -25,6 +25,7 @@
 
 #include <linux/mmc/mmc.h>
 #include <linux/mmc/host.h>
+#include <linux/mmc/card.h>
 
 #include "sdhci.h"
 
@@ -46,6 +47,8 @@ static void sdhci_finish_data(struct sdhci_host *);
 static void sdhci_send_command(struct sdhci_host *, struct mmc_command *);
 static void sdhci_finish_command(struct sdhci_host *);
 
+static struct mmc_command *abort_cmd;
+
 static void sdhci_dumpregs(struct sdhci_host *host)
 {
 	printk(KERN_DEBUG DRIVER_NAME ": =========== REGISTER DUMP (%s)===========\n",
@@ -812,6 +815,30 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data)
 	sdhci_writew(host, data->blocks, SDHCI_BLOCK_COUNT);
 }
 
+/*
+ * Does the Host Controller support Auto CMD23?
+ *
+ * There are four preconditions for Auto CMD23 to be supported:
+ *  1. Host Controller v3.00 or later
+ *  2. Card supports CMD23
+ *  3. If DMA is used, it should be ADMA
+ *  4. Only when CMD18 or CMD25 is issued
+ */
+static int sdhci_host_auto_cmd23_supported(struct sdhci_host *host,
+	struct mmc_request *mrq)
+{
+	struct mmc_card *card = host->mmc->card;
+
+	if ((host->version >= SDHCI_SPEC_300) &&
+	   card && (card->scr.cmd_support & SD_SCR_CMD23_SUPPORT) &&
+	   !(host->flags & SDHCI_USE_SDMA) &&
+	   ((mrq->cmd->opcode == MMC_READ_MULTIPLE_BLOCK) ||
+	   (mrq->cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK)))
+		return 1;
+
+	return 0;
+}
+
 static void sdhci_set_transfer_mode(struct sdhci_host *host,
 	struct mmc_data *data)
 {
@@ -824,11 +851,23 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host,
 
 	mode = SDHCI_TRNS_BLK_CNT_EN;
 	if (data->blocks > 1) {
+		if (sdhci_host_auto_cmd23_supported(host, host->mrq)) {
+			/*
+			 * Host Controller v3.00 can automatically send CMD23
+			 * before CMD18 or CMD25. For that, we need to enable
+			 * Auto CMD23 in the Transfer Mode register and
+			 * program the block count in Argument 2 register.
+			 */
+			mode |= SDHCI_TRNS_AUTO_CMD23;
+			sdhci_writel(host, data->blocks, SDHCI_ARGUMENT2);
+		}
+
 		if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12)
 			mode |= SDHCI_TRNS_MULTI | SDHCI_TRNS_ACMD12;
 		else
 			mode |= SDHCI_TRNS_MULTI;
 	}
+
 	if (data->flags & MMC_DATA_READ)
 		mode |= SDHCI_TRNS_READ;
 	if (host->flags & SDHCI_REQ_USE_DMA)
@@ -1131,6 +1170,23 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
 #ifndef SDHCI_USE_LEDS_CLASS
 	sdhci_activate_led(host);
 #endif
+	/*
+	 * Since the block count for CMD23 has already been specified in the
+	 * Argument 2 register, we don't need CMD12 to stop multiple block
+	 * transfers.
+	 */
+	if (sdhci_host_auto_cmd23_supported(host, mrq)) {
+		if (mrq->stop) {
+			/*
+			 * Save the stop command here to be used during
+			 * error recovery
+			 */
+			abort_cmd = mrq->data->stop;
+			mrq->data->stop = NULL;
+			mrq->stop = NULL;
+		}
+	}
+
 	if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12) {
 		if (mrq->stop) {
 			mrq->data->stop = NULL;
@@ -1396,6 +1452,7 @@ static void sdhci_timeout_timer(unsigned long data)
 
 		if (host->data) {
 			host->data->error = -ETIMEDOUT;
+			host->data->stop = abort_cmd;
 			sdhci_finish_data(host);
 		} else {
 			if (host->cmd)
@@ -1534,9 +1591,10 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
 		host->data->error = -EIO;
 	}
 
-	if (host->data->error)
+	if (host->data->error) {
+		host->data->stop = abort_cmd;
 		sdhci_finish_data(host);
-	else {
+	} else {
 		if (intmask & (SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL))
 			sdhci_transfer_pio(host);
 
@@ -1783,8 +1841,10 @@ int sdhci_add_host(struct sdhci_host *host)
 		host->flags &= ~SDHCI_USE_SDMA;
 	}
 
-	if ((host->version >= SDHCI_SPEC_200) && (caps & SDHCI_CAN_DO_ADMA2))
+	if ((host->version >= SDHCI_SPEC_200) && (caps & SDHCI_CAN_DO_ADMA2)) {
+		host->flags &= ~SDHCI_USE_SDMA;
 		host->flags |= SDHCI_USE_ADMA;
+	}
 
 	if ((host->quirks & SDHCI_QUIRK_BROKEN_ADMA) &&
 		(host->flags & SDHCI_USE_ADMA)) {
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 6e0969e..223762c 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -25,6 +25,7 @@
  */
 
 #define SDHCI_DMA_ADDRESS	0x00
+#define SDHCI_ARGUMENT2		SDHCI_DMA_ADDRESS
 
 #define SDHCI_BLOCK_SIZE	0x04
 #define  SDHCI_MAKE_BLKSZ(dma, blksz) (((dma & 0x7) << 12) | (blksz & 0xFFF))
@@ -37,6 +38,7 @@
 #define  SDHCI_TRNS_DMA		0x01
 #define  SDHCI_TRNS_BLK_CNT_EN	0x02
 #define  SDHCI_TRNS_ACMD12	0x04
+#define  SDHCI_TRNS_AUTO_CMD23	0x08
 #define  SDHCI_TRNS_READ	0x10
 #define  SDHCI_TRNS_MULTI	0x20
 
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 8ce0827..22b0335 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -58,9 +58,13 @@ struct mmc_ext_csd {
 
 struct sd_scr {
 	unsigned char		sda_vsn;
+	unsigned char		sda_spec3;
 	unsigned char		bus_widths;
 #define SD_SCR_BUS_WIDTH_1	(1<<0)
 #define SD_SCR_BUS_WIDTH_4	(1<<2)
+	unsigned char		cmd_support;
+#define SD_SCR_CMD20_SUPPORT	(1<<0)
+#define SD_SCR_CMD23_SUPPORT	(1<<1)
 };
 
 struct sd_ssr {
diff --git a/include/linux/mmc/sd.h b/include/linux/mmc/sd.h
index 3fd85e0..178363b 100644
--- a/include/linux/mmc/sd.h
+++ b/include/linux/mmc/sd.h
@@ -59,7 +59,7 @@
 
 #define SCR_SPEC_VER_0		0	/* Implements system specification 1.0 - 1.01 */
 #define SCR_SPEC_VER_1		1	/* Implements system specification 1.10 */
-#define SCR_SPEC_VER_2		2	/* Implements system specification 2.00 */
+#define SCR_SPEC_VER_2		2	/* Implements system specification 2.00 - 3.0x */
 
 /*
  * SD bus widths
-- 
1.7.1


^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH 02/12] mmc: sd: add support for signal voltage switch procedure
  2011-02-15  9:34 [PATCH 00/12] add support for sd host controller v3.00 Arindam Nath
  2011-02-15  9:34 ` [PATCH 01/12] mmc: sdhci: add support for auto CMD23 Arindam Nath
@ 2011-02-15  9:35 ` Arindam Nath
  2011-02-15 10:16   ` Wolfram Sang
                     ` (2 more replies)
  2011-02-15  9:35 ` [PATCH 03/12] mmc: sd: query function modes for uhs cards Arindam Nath
                   ` (11 subsequent siblings)
  13 siblings, 3 replies; 45+ messages in thread
From: Arindam Nath @ 2011-02-15  9:35 UTC (permalink / raw)
  To: cjb; +Cc: linux-mmc, henry.su, aaron.lu, anath.amd, Arindam Nath

Host Controller v3.00 adds another Capabilities register. We can
directly read this register without any version checking since the
new register bits will be considred as reserved in older controllers,
and will be read as 0. Apart from other things, this new register
indicates whether the Host Controller supports SDR50, SDR104, and
DDR50 UHS-I modes. So depending on the host support, we set
the corresponding MMC_CAP_* flags. One more new register. Host Control2
is added in v3.00, which is used during Signal Voltage Switch prcedure
described below.

Since as per v3.00 spec, UHS-I supported hosts should set S18R to 1,
we set S18R (bit 24) of OCR before sending ACMD41. We also need to set
XPC (bit 28) of OCR in case the host can supply >150mA. This support is
indicated by the Maximum Current Capabilities register of the Host
Controller.

If the response of ACMD41 has both CCS and S18A set, we start the
signal voltage switch procedure, which if successfull, will switch
the card from 3.3V signalling to 1.8V signalling. Signal voltage
switch procedure adds support for a new command CMD11 in the Physical
Layer Spec v3.01. As part of this procedure, we need to set 1.8V Signalling
Enable (bit 3) of Host Control2 register, which if remains set after 5ms,
means the switch to 1.8V signalling is successfull. Otherwise, we clear
bit 24 of OCR and retry the initialization sequence.

Signed-off-by: Arindam Nath <arindam.nath@amd.com>
---
 drivers/mmc/core/sd.c     |   37 ++++++++++-
 drivers/mmc/core/sd_ops.c |   25 ++++++++
 drivers/mmc/core/sd_ops.h |    1 +
 drivers/mmc/host/sdhci.c  |  147 +++++++++++++++++++++++++++++++++++++++++----
 drivers/mmc/host/sdhci.h  |   18 +++++-
 include/linux/mmc/host.h  |    8 +++
 include/linux/mmc/sd.h    |    1 +
 7 files changed, 219 insertions(+), 18 deletions(-)

diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index b3f8a3c..e968d5c 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -408,6 +408,7 @@ struct device_type sd_type = {
 int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid)
 {
 	int err;
+	u32 rocr;
 
 	/*
 	 * Since we're changing the OCR value, we seem to
@@ -427,10 +428,25 @@ int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid)
 	if (!err)
 		ocr |= 1 << 30;
 
-	err = mmc_send_app_op_cond(host, ocr, NULL);
+	/* If the host can supply more than 150mA, XPC should be set to 1. */
+	if (host->caps & (MMC_CAP_SET_XPC_330 | MMC_CAP_SET_XPC_300 |
+	    MMC_CAP_SET_XPC_180))
+		ocr |= 1 << 28;
+
+	err = mmc_send_app_op_cond(host, ocr, &rocr);
 	if (err)
 		return err;
 
+	/*
+	 * In case CCS and S18A in the response is set, start Signal Voltage
+	 * Switch procedure. SPI mode doesn't support CMD11.
+	 */
+	if (!mmc_host_is_spi(host) && (rocr & 0x41000000)) {
+		err = mmc_start_voltage_switch(host);
+		if (err)
+			return err;
+	}
+
 	if (mmc_host_is_spi(host))
 		err = mmc_send_cid(host, cid);
 	else
@@ -827,11 +843,26 @@ int mmc_attach_sd(struct mmc_host *host)
 	}
 
 	/*
+	 * If the host supports one of UHS-I modes, request the card
+	 * to switch to 1.8V signaling level.
+	 */
+	if (host->caps & (MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 |
+	    MMC_CAP_UHS_DDR50))
+		host->ocr |= (1 << 24);
+
+	/*
 	 * Detect and init the card.
 	 */
 	err = mmc_sd_init_card(host, host->ocr, NULL);
-	if (err)
-		goto err;
+	if (err == -EAGAIN) {
+		/*
+		 * Retry initialization with S18R set to 0.
+		 */
+		host->ocr &= ~(1 << 24);
+		err = mmc_sd_init_card(host, host->ocr, NULL);
+		if (err)
+			goto err;
+	}
 
 	mmc_release_host(host);
 	err = mmc_add_card(host->card);
diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c
index 797cdb5..a0f97c9 100644
--- a/drivers/mmc/core/sd_ops.c
+++ b/drivers/mmc/core/sd_ops.c
@@ -146,6 +146,31 @@ int mmc_app_set_bus_width(struct mmc_card *card, int width)
 	return 0;
 }
 
+int mmc_start_voltage_switch(struct mmc_host *host)
+{
+	struct mmc_command cmd;
+	int err;
+
+	BUG_ON(!host);
+
+	memset(&cmd, 0, sizeof(struct mmc_command));
+
+	cmd.opcode = SD_SWITCH_VOLTAGE;
+	cmd.arg = 0;
+	cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+
+	err = mmc_wait_for_cmd(host, &cmd, 0);
+	if (err)
+		return err;
+
+	if (!mmc_host_is_spi(host) && (cmd.resp[0] & R1_ERROR))
+		return -EIO;
+
+	err = host->ops->start_signal_voltage_switch(host);
+
+	return err;
+}
+
 int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
 {
 	struct mmc_command cmd;
diff --git a/drivers/mmc/core/sd_ops.h b/drivers/mmc/core/sd_ops.h
index ffc2305..3cfba59 100644
--- a/drivers/mmc/core/sd_ops.h
+++ b/drivers/mmc/core/sd_ops.h
@@ -20,6 +20,7 @@ int mmc_app_send_scr(struct mmc_card *card, u32 *scr);
 int mmc_sd_switch(struct mmc_card *card, int mode, int group,
 	u8 value, u8 *resp);
 int mmc_app_sd_status(struct mmc_card *card, void *ssr);
+int mmc_start_voltage_switch(struct mmc_host *host);
 
 #endif
 
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 0b537cf..6d7a276 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -87,6 +87,8 @@ static void sdhci_dumpregs(struct sdhci_host *host)
 	printk(KERN_DEBUG DRIVER_NAME ": Cmd:      0x%08x | Max curr: 0x%08x\n",
 		sdhci_readw(host, SDHCI_COMMAND),
 		sdhci_readl(host, SDHCI_MAX_CURRENT));
+	printk(KERN_DEBUG DRIVER_NAME ": Host ctl2: 0x%08x\n",
+		sdhci_readw(host, SDHCI_HOST_CONTROL2));
 
 	if (host->flags & SDHCI_USE_ADMA)
 		printk(KERN_DEBUG DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x\n",
@@ -1340,11 +1342,76 @@ out:
 	spin_unlock_irqrestore(&host->lock, flags);
 }
 
+static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc)
+{
+	struct sdhci_host *host;
+	u8 pwr;
+	u16 clk, ctrl;
+	u32 present_state;
+	unsigned long flags;
+
+	host = mmc_priv(mmc);
+
+	spin_lock_irqsave(&host->lock, flags);
+
+	/* Stop SDCLK */
+	host = mmc_priv(mmc);
+	clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+	clk &= ~SDHCI_CLOCK_CARD_EN;
+	sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+
+	/* Check whether DAT[3:0] is 0000 */
+	present_state = sdhci_readl(host, SDHCI_PRESENT_STATE);
+	if (!((present_state & SDHCI_DATA_LVL_MASK) >> SDHCI_DATA_LVL_SHIFT)) {
+		/* Enable 1.8V Signal Enable in the Host Control2 register */
+		ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+		ctrl |= SDHCI_CTRL_VDD_180;
+		sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
+
+		/* Wait for 5ms */
+		mdelay(5);
+
+		ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+		if (ctrl & SDHCI_CTRL_VDD_180) {
+			/* Provide SDCLK again and wait for 1ms*/
+			clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+			clk |= SDHCI_CLOCK_CARD_EN;
+			sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+			mdelay(1);
+
+			/*
+			 * If DAT[3:0] level is 1111b, then the card was
+			 * successfully switched to 1.8V signaling.
+			 */
+			present_state = sdhci_readl(host, SDHCI_PRESENT_STATE);
+			if ((present_state & SDHCI_DATA_LVL_MASK) ==
+			     SDHCI_DATA_LVL_MASK) {
+				spin_unlock_irqrestore(&host->lock, flags);
+				return 0;
+			}
+		}
+	}
+
+	/*
+	 * If we are here, that means the switch to 1.8V signaling failed. Stop
+	 * power to the card, and retry initialization sequence by setting S18R
+	 * to 0.
+	 */
+	pwr = sdhci_readb(host, SDHCI_POWER_CONTROL);
+	pwr &= ~SDHCI_POWER_ON;
+	sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
+
+	spin_unlock_irqrestore(&host->lock, flags);
+
+	return -EAGAIN;
+}
+
 static const struct mmc_host_ops sdhci_ops = {
 	.request	= sdhci_request,
 	.set_ios	= sdhci_set_ios,
 	.get_ro		= sdhci_get_ro,
 	.enable_sdio_irq = sdhci_enable_sdio_irq,
+	.start_signal_voltage_switch	= sdhci_start_signal_voltage_switch,
 };
 
 /*****************************************************************************\
@@ -1802,7 +1869,9 @@ EXPORT_SYMBOL_GPL(sdhci_alloc_host);
 int sdhci_add_host(struct sdhci_host *host)
 {
 	struct mmc_host *mmc;
-	unsigned int caps, ocr_avail;
+	u32 caps[2];
+	u32 max_current_caps;
+	unsigned int ocr_avail;
 	int ret;
 
 	WARN_ON(host == NULL);
@@ -1825,12 +1894,13 @@ int sdhci_add_host(struct sdhci_host *host)
 			host->version);
 	}
 
-	caps = (host->quirks & SDHCI_QUIRK_MISSING_CAPS) ? host->caps :
+	caps[0] = (host->quirks & SDHCI_QUIRK_MISSING_CAPS) ? host->caps :
 		sdhci_readl(host, SDHCI_CAPABILITIES);
+	caps[1] = sdhci_readl(host, SDHCI_CAPABILITIES_1);
 
 	if (host->quirks & SDHCI_QUIRK_FORCE_DMA)
 		host->flags |= SDHCI_USE_SDMA;
-	else if (!(caps & SDHCI_CAN_DO_SDMA))
+	else if (!(caps[0] & SDHCI_CAN_DO_SDMA))
 		DBG("Controller doesn't have SDMA capability\n");
 	else
 		host->flags |= SDHCI_USE_SDMA;
@@ -1841,7 +1911,8 @@ int sdhci_add_host(struct sdhci_host *host)
 		host->flags &= ~SDHCI_USE_SDMA;
 	}
 
-	if ((host->version >= SDHCI_SPEC_200) && (caps & SDHCI_CAN_DO_ADMA2)) {
+	if ((host->version >= SDHCI_SPEC_200) &&
+		(caps[0] & SDHCI_CAN_DO_ADMA2)) {
 		host->flags &= ~SDHCI_USE_SDMA;
 		host->flags |= SDHCI_USE_ADMA;
 	}
@@ -1893,10 +1964,10 @@ int sdhci_add_host(struct sdhci_host *host)
 	}
 
 	if (host->version >= SDHCI_SPEC_300)
-		host->max_clk = (caps & SDHCI_CLOCK_V3_BASE_MASK)
+		host->max_clk = (caps[0] & SDHCI_CLOCK_V3_BASE_MASK)
 			>> SDHCI_CLOCK_BASE_SHIFT;
 	else
-		host->max_clk = (caps & SDHCI_CLOCK_BASE_MASK)
+		host->max_clk = (caps[0] & SDHCI_CLOCK_BASE_MASK)
 			>> SDHCI_CLOCK_BASE_SHIFT;
 
 	host->max_clk *= 1000000;
@@ -1912,7 +1983,7 @@ int sdhci_add_host(struct sdhci_host *host)
 	}
 
 	host->timeout_clk =
-		(caps & SDHCI_TIMEOUT_CLK_MASK) >> SDHCI_TIMEOUT_CLK_SHIFT;
+		(caps[0] & SDHCI_TIMEOUT_CLK_MASK) >> SDHCI_TIMEOUT_CLK_SHIFT;
 	if (host->timeout_clk == 0) {
 		if (host->ops->get_timeout_clock) {
 			host->timeout_clk = host->ops->get_timeout_clock(host);
@@ -1924,7 +1995,7 @@ int sdhci_add_host(struct sdhci_host *host)
 			return -ENODEV;
 		}
 	}
-	if (caps & SDHCI_TIMEOUT_CLK_UNIT)
+	if (caps[0] & SDHCI_TIMEOUT_CLK_UNIT)
 		host->timeout_clk *= 1000;
 
 	/*
@@ -1951,21 +2022,71 @@ int sdhci_add_host(struct sdhci_host *host)
 	if (!(host->quirks & SDHCI_QUIRK_FORCE_1_BIT_DATA))
 		mmc->caps |= MMC_CAP_4_BIT_DATA;
 
-	if (caps & SDHCI_CAN_DO_HISPD)
+	if (caps[0] & SDHCI_CAN_DO_HISPD)
 		mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
 
 	if ((host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) &&
 	    mmc_card_is_removable(mmc))
 		mmc->caps |= MMC_CAP_NEEDS_POLL;
 
+	/* UHS-I mode(s) supported by the host controller. */
+	if (caps[1] & SDHCI_SUPPORT_SDR50)
+		mmc->caps |= MMC_CAP_UHS_SDR50;
+	if (caps[1] & SDHCI_SUPPORT_SDR104)
+		mmc->caps |= MMC_CAP_UHS_SDR104;
+	if (caps[1] & SDHCI_SUPPORT_DDR50)
+		mmc->caps |= MMC_CAP_UHS_DDR50;
+
 	ocr_avail = 0;
-	if (caps & SDHCI_CAN_VDD_330)
+	/*
+	 * According to SD Host Controller spec v3.00, if the Host System
+	 * can afford more than 150mA, Host Driver should set XPC to 1. Also
+	 * the value is meaningful only if Voltage Support in the Capabilities
+	 * register is set. The actual current value is 4 times the register
+	 * value.
+	 */
+	max_current_caps = sdhci_readl(host, SDHCI_MAX_CURRENT);
+
+	if (caps[0] & SDHCI_CAN_VDD_330) {
+		int max_current_330;
+
 		ocr_avail |= MMC_VDD_32_33 | MMC_VDD_33_34;
-	if (caps & SDHCI_CAN_VDD_300)
+
+		max_current_330 = ((max_current_caps &
+				   SDHCI_MAX_CURRENT_330_MASK) >>
+				   SDHCI_MAX_CURRENT_330_SHIFT) *
+				   SDHCI_MAX_CURRENT_MULTIPLIER;
+
+		if (max_current_330 > 150)
+			mmc->caps |= MMC_CAP_SET_XPC_330;
+	}
+	if (caps[0] & SDHCI_CAN_VDD_300) {
+		int max_current_300;
+
 		ocr_avail |= MMC_VDD_29_30 | MMC_VDD_30_31;
-	if (caps & SDHCI_CAN_VDD_180)
+
+		max_current_300 = ((max_current_caps &
+				   SDHCI_MAX_CURRENT_300_MASK) >>
+				   SDHCI_MAX_CURRENT_300_SHIFT) *
+				   SDHCI_MAX_CURRENT_MULTIPLIER;
+
+		if (max_current_300 > 150)
+			mmc->caps |= MMC_CAP_SET_XPC_300;
+	}
+	if (caps[0] & SDHCI_CAN_VDD_180) {
+		int max_current_180;
+
 		ocr_avail |= MMC_VDD_165_195;
 
+		max_current_180 = ((max_current_caps &
+				   SDHCI_MAX_CURRENT_180_MASK) >>
+				   SDHCI_MAX_CURRENT_180_SHIFT) *
+				   SDHCI_MAX_CURRENT_MULTIPLIER;
+
+		if (max_current_180 > 150)
+			mmc->caps |= MMC_CAP_SET_XPC_180;
+	}
+
 	mmc->ocr_avail = ocr_avail;
 	mmc->ocr_avail_sdio = ocr_avail;
 	if (host->ocr_avail_sdio)
@@ -2025,7 +2146,7 @@ int sdhci_add_host(struct sdhci_host *host)
 	if (host->quirks & SDHCI_QUIRK_FORCE_BLK_SZ_2048) {
 		mmc->max_blk_size = 2;
 	} else {
-		mmc->max_blk_size = (caps & SDHCI_MAX_BLOCK_MASK) >>
+		mmc->max_blk_size = (caps[0] & SDHCI_MAX_BLOCK_MASK) >>
 				SDHCI_MAX_BLOCK_SHIFT;
 		if (mmc->max_blk_size >= 3) {
 			printk(KERN_WARNING "%s: Invalid maximum block size, "
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 223762c..95d70e6 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -69,6 +69,8 @@
 #define  SDHCI_DATA_AVAILABLE	0x00000800
 #define  SDHCI_CARD_PRESENT	0x00010000
 #define  SDHCI_WRITE_PROTECT	0x00080000
+#define  SDHCI_DATA_LVL_MASK	0x00F00000
+#define   SDHCI_DATA_LVL_SHIFT	20
 
 #define SDHCI_HOST_CONTROL 	0x28
 #define  SDHCI_CTRL_LED		0x01
@@ -147,7 +149,8 @@
 
 #define SDHCI_ACMD12_ERR	0x3C
 
-/* 3E-3F reserved */
+#define SDHCI_HOST_CONTROL2		0x3E
+#define  SDHCI_CTRL_VDD_180		0x0008
 
 #define SDHCI_CAPABILITIES	0x40
 #define  SDHCI_TIMEOUT_CLK_MASK	0x0000003F
@@ -168,9 +171,20 @@
 #define  SDHCI_CAN_VDD_180	0x04000000
 #define  SDHCI_CAN_64BIT	0x10000000
 
+#define  SDHCI_SUPPORT_SDR50	0x00000001
+#define  SDHCI_SUPPORT_SDR104	0x00000002
+#define  SDHCI_SUPPORT_DDR50	0x00000004
+
 #define SDHCI_CAPABILITIES_1	0x44
 
-#define SDHCI_MAX_CURRENT	0x48
+#define SDHCI_MAX_CURRENT		0x48
+#define  SDHCI_MAX_CURRENT_330_MASK	0x0000FF
+#define  SDHCI_MAX_CURRENT_330_SHIFT	0
+#define  SDHCI_MAX_CURRENT_300_MASK	0x00FF00
+#define  SDHCI_MAX_CURRENT_300_SHIFT	8
+#define  SDHCI_MAX_CURRENT_180_MASK	0xFF0000
+#define  SDHCI_MAX_CURRENT_180_SHIFT	16
+#define   SDHCI_MAX_CURRENT_MULTIPLIER	4
 
 /* 4C-4F reserved for more max current */
 
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index bcb793e..ec09b32 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -117,6 +117,8 @@ struct mmc_host_ops {
 
 	/* optional callback for HC quirks */
 	void	(*init_card)(struct mmc_host *host, struct mmc_card *card);
+
+	int	(*start_signal_voltage_switch)(struct mmc_host *host);
 };
 
 struct mmc_card;
@@ -173,6 +175,12 @@ struct mmc_host {
 						/* DDR mode at 1.2V */
 #define MMC_CAP_POWER_OFF_CARD	(1 << 13)	/* Can power off after boot */
 #define MMC_CAP_BUS_WIDTH_TEST	(1 << 14)	/* CMD14/CMD19 bus width ok */
+#define MMC_CAP_UHS_SDR50	(1 << 15)	/* Host supports UHS SDR50 mode */
+#define MMC_CAP_UHS_SDR104	(1 << 16)	/* Host supports UHS SDR104 mode */
+#define MMC_CAP_UHS_DDR50	(1 << 17)	/* Host supports UHS DDR50 mode */
+#define MMC_CAP_SET_XPC_330	(1 << 18)	/* Host supports >150mA current at 3.3V */
+#define MMC_CAP_SET_XPC_300	(1 << 19)	/* Host supports >150mA current at 3.0V */
+#define MMC_CAP_SET_XPC_180	(1 << 20)	/* Host supports >150mA current at 1.8V */
 
 	mmc_pm_flag_t		pm_caps;	/* supported pm features */
 
diff --git a/include/linux/mmc/sd.h b/include/linux/mmc/sd.h
index 178363b..3ba5aa6 100644
--- a/include/linux/mmc/sd.h
+++ b/include/linux/mmc/sd.h
@@ -17,6 +17,7 @@
 /* This is basically the same command as for MMC with some quirks. */
 #define SD_SEND_RELATIVE_ADDR     3   /* bcr                     R6  */
 #define SD_SEND_IF_COND           8   /* bcr  [11:0] See below   R7  */
+#define SD_SWITCH_VOLTAGE         11  /* ac                      R1  */
 
   /* class 10 */
 #define SD_SWITCH                 6   /* adtc [31:0] See below   R1  */
-- 
1.7.1


^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH 03/12] mmc: sd: query function modes for uhs cards
  2011-02-15  9:34 [PATCH 00/12] add support for sd host controller v3.00 Arindam Nath
  2011-02-15  9:34 ` [PATCH 01/12] mmc: sdhci: add support for auto CMD23 Arindam Nath
  2011-02-15  9:35 ` [PATCH 02/12] mmc: sd: add support for signal voltage switch procedure Arindam Nath
@ 2011-02-15  9:35 ` Arindam Nath
  2011-02-15  9:35 ` [PATCH 04/12] mmc: sd: add support for driver type selection Arindam Nath
                   ` (10 subsequent siblings)
  13 siblings, 0 replies; 45+ messages in thread
From: Arindam Nath @ 2011-02-15  9:35 UTC (permalink / raw)
  To: cjb; +Cc: linux-mmc, henry.su, aaron.lu, anath.amd, Arindam Nath

SD cards which conform to Physical Layer Spec v3.01 can support
additional Bus Speed Modes, Driver Strength, and Current Limit
other than the default values. We use CMD6 mode 0 to read these
additional card functions. The values read here will be used during
UHS-I initialization steps.

Signed-off-by: Arindam Nath <arindam.nath@amd.com>
---
 drivers/mmc/core/sd.c    |   99 +++++++++++++++++++++++++++++++++++++++-------
 include/linux/mmc/card.h |    3 +
 2 files changed, 87 insertions(+), 15 deletions(-)

diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index e968d5c..2a84396 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -283,25 +283,94 @@ static int mmc_read_switch(struct mmc_card *card)
 		return -ENOMEM;
 	}
 
-	err = mmc_sd_switch(card, 0, 0, 1, status);
-	if (err) {
-		/* If the host or the card can't do the switch,
-		 * fail more gracefully. */
-		if ((err != -EINVAL)
-		 && (err != -ENOSYS)
-		 && (err != -EFAULT))
+	if (card->scr.sda_spec3) {
+		/* First find out the supported Bus Speed Modes. */
+		err = mmc_sd_switch(card, 0, 0, 1, status);
+		if (err) {
+			/*
+			 * If the host or the card can't do the switch,
+			 * fail more gracefully.
+			 */
+			if ((err != -EINVAL)
+			&& (err != -ENOSYS)
+			&& (err != -EFAULT))
+				goto out;
+
+			printk(KERN_WARNING "%s: problem reading "
+				"Bus Speed modes.\n",
+				mmc_hostname(card->host));
+			err = 0;
+
 			goto out;
+		}
 
-		printk(KERN_WARNING "%s: problem reading switch "
-			"capabilities, performance might suffer.\n",
-			mmc_hostname(card->host));
-		err = 0;
+		card->sw_caps.uhs_bus_mode = status[13];
+
+		/* Find out Driver Strengths supported by the card */
+		err = mmc_sd_switch(card, 0, 2, 1, status);
+		if (err) {
+			/*
+			 * If the host or the card can't do the switch,
+			 * fail more gracefully.
+			 */
+			if ((err != -EINVAL)
+			&& (err != -ENOSYS)
+			&& (err != -EFAULT))
+				goto out;
+
+			printk(KERN_WARNING "%s: problem reading "
+				"Driver Strength.\n",
+				mmc_hostname(card->host));
+			err = 0;
 
-		goto out;
-	}
+			goto out;
+		}
 
-	if (status[13] & 0x02)
-		card->sw_caps.hs_max_dtr = 50000000;
+		card->sw_caps.uhs_drv_type = status[9];
+
+		/* Find out Current Limits supported by the card */
+		err = mmc_sd_switch(card, 0, 3, 1, status);
+		if (err) {
+			/*
+			 * If the host or the card can't do the switch,
+			 * fail more gracefully.
+			 */
+			if ((err != -EINVAL)
+			&& (err != -ENOSYS)
+			&& (err != -EFAULT))
+				goto out;
+
+			printk(KERN_WARNING "%s: problem reading "
+				"Current Limit.\n",
+				mmc_hostname(card->host));
+			err = 0;
+
+			goto out;
+		}
+
+		card->sw_caps.uhs_curr_limit = status[7];
+	} else {
+		err = mmc_sd_switch(card, 0, 0, 1, status);
+		if (err) {
+			/*
+			 * If the host or the card can't do the switch,
+			 * fail more gracefully.
+			 */
+			if ((err != -EINVAL)
+			&& (err != -ENOSYS)
+			&& (err != -EFAULT))
+				goto out;
+
+			printk(KERN_WARNING "%s: problem reading switch "
+				"capabilities, performance might suffer.\n",
+				mmc_hostname(card->host));
+			err = 0;
+			goto out;
+		}
+
+		if (status[13] & 0x02)
+			card->sw_caps.hs_max_dtr = 50000000;
+	}
 
 out:
 	kfree(status);
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 22b0335..7080f22 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -75,6 +75,9 @@ struct sd_ssr {
 
 struct sd_switch_caps {
 	unsigned int		hs_max_dtr;
+	unsigned int		uhs_bus_mode;
+	unsigned int		uhs_drv_type;
+	unsigned int		uhs_curr_limit;
 };
 
 struct sdio_cccr {
-- 
1.7.1


^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH 04/12] mmc: sd: add support for driver type selection
  2011-02-15  9:34 [PATCH 00/12] add support for sd host controller v3.00 Arindam Nath
                   ` (2 preceding siblings ...)
  2011-02-15  9:35 ` [PATCH 03/12] mmc: sd: query function modes for uhs cards Arindam Nath
@ 2011-02-15  9:35 ` Arindam Nath
  2011-02-15  9:35 ` [PATCH 05/12] mmc: sdhci: reset sdclk before setting high speed enable Arindam Nath
                   ` (9 subsequent siblings)
  13 siblings, 0 replies; 45+ messages in thread
From: Arindam Nath @ 2011-02-15  9:35 UTC (permalink / raw)
  To: cjb; +Cc: linux-mmc, henry.su, aaron.lu, anath.amd, Arindam Nath

This patch adds support for setting driver strength during UHS-I
initialization prcedure. Since UHS-I cards set S18A (bit 24) in
response to ACMD41, we use this as a base for UHS-I initialization.
We modify the parameter list of mmc_sd_get_cid() so that we can save
the ROCR from ACMD41 to check whether bit 24 is set.

We decide whether the Host Controller supports A, C, or D driver type
depending on the Capabilities register. We then set the appropriate
driver type for the card using CMD6 mode 1. As per Host Controller
spec v3.00, we set driver type for the host only if Preset Value Enable
in the Host Control2 register is not set. SDHCI_HOST_CONTROL has been
renamed to SDHCI_HOST_CONTROL1 to conform to the spec.

Signed-off-by: Arindam Nath <arindam.nath@amd.com>
---
 drivers/mmc/core/core.c  |    9 +++
 drivers/mmc/core/core.h  |    1 +
 drivers/mmc/core/sd.c    |  140 +++++++++++++++++++++++++++++++++++++---------
 drivers/mmc/core/sd.h    |    3 +-
 drivers/mmc/core/sdio.c  |    3 +-
 drivers/mmc/host/sdhci.c |   48 ++++++++++++----
 drivers/mmc/host/sdhci.h |   10 +++-
 include/linux/mmc/card.h |    4 +
 include/linux/mmc/host.h |    8 +++
 9 files changed, 186 insertions(+), 40 deletions(-)

diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 6625c05..daa535a 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -947,6 +947,15 @@ void mmc_set_timing(struct mmc_host *host, unsigned int timing)
 }
 
 /*
+ * Select appropriate driver type for host.
+ */
+void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type)
+{
+	host->ios.drv_type = drv_type;
+	mmc_set_ios(host);
+}
+
+/*
  * Apply power to the MMC stack.  This is a two-stage process.
  * First, we enable power to the card without the clock running.
  * We then wait a bit for the power to stabilise.  Finally,
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
index ca1fdde..6114ca5 100644
--- a/drivers/mmc/core/core.h
+++ b/drivers/mmc/core/core.h
@@ -42,6 +42,7 @@ void mmc_set_bus_width_ddr(struct mmc_host *host, unsigned int width,
 			   unsigned int ddr);
 u32 mmc_select_voltage(struct mmc_host *host, u32 ocr);
 void mmc_set_timing(struct mmc_host *host, unsigned int timing);
+void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type);
 
 static inline void mmc_delay(unsigned int ms)
 {
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 2a84396..9e4574d 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -426,6 +426,86 @@ out:
 	return err;
 }
 
+static int sd_select_driver_type(struct mmc_card *card, u8 *status)
+{
+	int host_set_drv_type, card_set_drv_type;
+	int err;
+
+	/*
+	 * If the host doesn't support any of the Driver Types A,C or D,
+	 * default Driver Type B is used.
+	 */
+	if (!(card->host->caps & (MMC_CAP_DRIVER_TYPE_A | MMC_CAP_DRIVER_TYPE_C
+	    | MMC_CAP_DRIVER_TYPE_D)))
+		return 0;
+
+	if (card->host->caps & MMC_CAP_DRIVER_TYPE_A) {
+		host_set_drv_type = MMC_SET_DRIVER_TYPE_A;
+		if (card->sw_caps.uhs_drv_type & SD_DRIVER_TYPE_A)
+			card_set_drv_type = MMC_SET_DRIVER_TYPE_A;
+		else if (card->sw_caps.uhs_drv_type & SD_DRIVER_TYPE_C)
+			card_set_drv_type = MMC_SET_DRIVER_TYPE_C;
+	} else if (card->host->caps & MMC_CAP_DRIVER_TYPE_C) {
+		host_set_drv_type = MMC_SET_DRIVER_TYPE_C;
+		if (card->sw_caps.uhs_drv_type & SD_DRIVER_TYPE_C)
+			card_set_drv_type = MMC_SET_DRIVER_TYPE_C;
+	}
+
+	err = mmc_sd_switch(card, 1, 2, card_set_drv_type, status);
+	if (err)
+		return err;
+
+	if ((status[15] & 0xF) != card_set_drv_type)
+		printk(KERN_WARNING "%s: Problem setting driver strength!\n",
+			mmc_hostname(card->host));
+	else
+		mmc_set_driver_type(card->host, host_set_drv_type);
+
+	return 0;
+}
+
+/*
+ * UHS-I specific initialization procedure
+ */
+static int mmc_sd_init_uhs_card(struct mmc_card *card)
+{
+	int err;
+	u8 *status;
+
+	if (!card->scr.sda_spec3)
+		return 0;
+
+	if (!(card->csd.cmdclass & CCC_SWITCH))
+		return 0;
+
+	err = -EIO;
+
+	status = kmalloc(64, GFP_KERNEL);
+	if (!status) {
+		printk(KERN_ERR "%s: could not allocate a buffer for "
+			"switch capabilities.\n", mmc_hostname(card->host));
+		return -ENOMEM;
+	}
+
+	/* Set 4-bit bus width */
+	if ((card->host->caps & MMC_CAP_4_BIT_DATA) &&
+	    (card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) {
+		err = mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4);
+		if (err)
+			goto out;
+
+		mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);
+	}
+
+	/* Set the driver strength for the card */
+	err = sd_select_driver_type(card, status);
+
+out:
+	kfree(status);
+
+	return err;
+}
+
 MMC_DEV_ATTR(cid, "%08x%08x%08x%08x\n", card->raw_cid[0], card->raw_cid[1],
 	card->raw_cid[2], card->raw_cid[3]);
 MMC_DEV_ATTR(csd, "%08x%08x%08x%08x\n", card->raw_csd[0], card->raw_csd[1],
@@ -474,10 +554,10 @@ struct device_type sd_type = {
 /*
  * Fetch CID from card.
  */
-int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid)
+int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid,
+	u32 *rocr)
 {
 	int err;
-	u32 rocr;
 
 	/*
 	 * Since we're changing the OCR value, we seem to
@@ -502,7 +582,7 @@ int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid)
 	    MMC_CAP_SET_XPC_180))
 		ocr |= 1 << 28;
 
-	err = mmc_send_app_op_cond(host, ocr, &rocr);
+	err = mmc_send_app_op_cond(host, ocr, rocr);
 	if (err)
 		return err;
 
@@ -510,7 +590,7 @@ int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid)
 	 * In case CCS and S18A in the response is set, start Signal Voltage
 	 * Switch procedure. SPI mode doesn't support CMD11.
 	 */
-	if (!mmc_host_is_spi(host) && (rocr & 0x41000000)) {
+	if (!mmc_host_is_spi(host) && rocr && (*rocr & 0x41000000)) {
 		err = mmc_start_voltage_switch(host);
 		if (err)
 			return err;
@@ -643,11 +723,12 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
 	struct mmc_card *card;
 	int err;
 	u32 cid[4];
+	u32 rocr;
 
 	BUG_ON(!host);
 	WARN_ON(!host->claimed);
 
-	err = mmc_sd_get_cid(host, ocr, cid);
+	err = mmc_sd_get_cid(host, ocr, cid, &rocr);
 	if (err)
 		return err;
 
@@ -700,30 +781,37 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
 	if (err)
 		goto free_card;
 
-	/*
-	 * Attempt to change to high-speed (if supported)
-	 */
-	err = mmc_sd_switch_hs(card);
-	if (err > 0)
-		mmc_sd_go_highspeed(card);
-	else if (err)
-		goto free_card;
-
-	/*
-	 * Set bus speed.
-	 */
-	mmc_set_clock(host, mmc_sd_get_max_clock(card));
-
-	/*
-	 * Switch to wider bus (if supported).
-	 */
-	if ((host->caps & MMC_CAP_4_BIT_DATA) &&
-		(card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) {
-		err = mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4);
+	/* Initialization sequence for UHS-I cards */
+	if (rocr & 0x01000000) {
+		err = mmc_sd_init_uhs_card(card);
 		if (err)
 			goto free_card;
+	} else {
+		/*
+		 * Attempt to change to high-speed (if supported)
+		 */
+		err = mmc_sd_switch_hs(card);
+		if (err > 0)
+			mmc_sd_go_highspeed(card);
+		else if (err)
+			goto free_card;
+
+		/*
+		 * Set bus speed.
+		 */
+		mmc_set_clock(host, mmc_sd_get_max_clock(card));
 
-		mmc_set_bus_width(host, MMC_BUS_WIDTH_4);
+		/*
+		 * Switch to wider bus (if supported).
+		 */
+		if ((host->caps & MMC_CAP_4_BIT_DATA) &&
+			(card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) {
+			err = mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4);
+			if (err)
+				goto free_card;
+
+			mmc_set_bus_width(host, MMC_BUS_WIDTH_4);
+		}
 	}
 
 	host->card = card;
diff --git a/drivers/mmc/core/sd.h b/drivers/mmc/core/sd.h
index 3d8800f..5106b44 100644
--- a/drivers/mmc/core/sd.h
+++ b/drivers/mmc/core/sd.h
@@ -5,7 +5,8 @@
 
 extern struct device_type sd_type;
 
-int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid);
+int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid,
+	u32 *rocr);
 int mmc_sd_get_csd(struct mmc_host *host, struct mmc_card *card);
 void mmc_decode_cid(struct mmc_card *card);
 int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card,
diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c
index 5c4a54d..6d16684 100644
--- a/drivers/mmc/core/sdio.c
+++ b/drivers/mmc/core/sdio.c
@@ -364,7 +364,8 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
 	}
 
 	if (ocr & R4_MEMORY_PRESENT
-	    && mmc_sd_get_cid(host, host->ocr & ocr, card->raw_cid) == 0) {
+	    && mmc_sd_get_cid(host, host->ocr & ocr, card->raw_cid,
+	    NULL) == 0) {
 		card->type = MMC_TYPE_SD_COMBO;
 
 		if (oldcard && (oldcard->type != MMC_TYPE_SD_COMBO ||
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 6d7a276..b768839 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -65,7 +65,7 @@ static void sdhci_dumpregs(struct sdhci_host *host)
 		sdhci_readw(host, SDHCI_TRANSFER_MODE));
 	printk(KERN_DEBUG DRIVER_NAME ": Present:  0x%08x | Host ctl: 0x%08x\n",
 		sdhci_readl(host, SDHCI_PRESENT_STATE),
-		sdhci_readb(host, SDHCI_HOST_CONTROL));
+		sdhci_readb(host, SDHCI_HOST_CONTROL1));
 	printk(KERN_DEBUG DRIVER_NAME ": Power:    0x%08x | Blk gap:  0x%08x\n",
 		sdhci_readb(host, SDHCI_POWER_CONTROL),
 		sdhci_readb(host, SDHCI_BLOCK_GAP_CONTROL));
@@ -218,18 +218,18 @@ static void sdhci_activate_led(struct sdhci_host *host)
 {
 	u8 ctrl;
 
-	ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
+	ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL1);
 	ctrl |= SDHCI_CTRL_LED;
-	sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
+	sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL1);
 }
 
 static void sdhci_deactivate_led(struct sdhci_host *host)
 {
 	u8 ctrl;
 
-	ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
+	ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL1);
 	ctrl &= ~SDHCI_CTRL_LED;
-	sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
+	sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL1);
 }
 
 #ifdef SDHCI_USE_LEDS_CLASS
@@ -788,14 +788,14 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data)
 	 * is ADMA.
 	 */
 	if (host->version >= SDHCI_SPEC_200) {
-		ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
+		ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL1);
 		ctrl &= ~SDHCI_CTRL_DMA_MASK;
 		if ((host->flags & SDHCI_REQ_USE_DMA) &&
 			(host->flags & SDHCI_USE_ADMA))
 			ctrl |= SDHCI_CTRL_ADMA32;
 		else
 			ctrl |= SDHCI_CTRL_SDMA;
-		sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
+		sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL1);
 	}
 
 	if (!(host->flags & SDHCI_REQ_USE_DMA)) {
@@ -1255,7 +1255,7 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 	if (host->ops->platform_8bit_width)
 		host->ops->platform_8bit_width(host, ios->bus_width);
 	else {
-		ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
+		ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL1);
 		if (ios->bus_width == MMC_BUS_WIDTH_8) {
 			ctrl &= ~SDHCI_CTRL_4BITBUS;
 			if (host->version >= SDHCI_SPEC_300)
@@ -1268,10 +1268,10 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 			else
 				ctrl &= ~SDHCI_CTRL_4BITBUS;
 		}
-		sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
+		sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL1);
 	}
 
-	ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
+	ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL1);
 
 	if ((ios->timing == MMC_TIMING_SD_HS ||
 	     ios->timing == MMC_TIMING_MMC_HS)
@@ -1280,7 +1280,25 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 	else
 		ctrl &= ~SDHCI_CTRL_HISPD;
 
-	sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
+	sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL1);
+
+	if (host->version >= SDHCI_SPEC_300) {
+		u16 ctrl_2;
+
+		ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+		if (!(ctrl_2 & SDHCI_CTRL_PRESET_VAL_ENABLE)) {
+			/*
+			 * We only need to set Driver Strength if the
+			 * preset value enable is not set.
+			 */
+			if (ios->drv_type == MMC_SET_DRIVER_TYPE_A)
+				ctrl_2 |= SDHCI_CTRL_DRV_TYPE_A;
+			else if (ios->drv_type == MMC_SET_DRIVER_TYPE_C)
+				ctrl_2 |= SDHCI_CTRL_DRV_TYPE_C;
+
+			sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
+		}
+	}
 
 	/*
 	 * Some (ENE) controllers go apeshit on some ios operation,
@@ -2037,6 +2055,14 @@ int sdhci_add_host(struct sdhci_host *host)
 	if (caps[1] & SDHCI_SUPPORT_DDR50)
 		mmc->caps |= MMC_CAP_UHS_DDR50;
 
+	/* Driver Type(s) (A, C, D) supported by the host */
+	if (caps[1] & SDHCI_DRIVER_TYPE_A)
+		mmc->caps |= MMC_CAP_DRIVER_TYPE_A;
+	if (caps[1] & SDHCI_DRIVER_TYPE_C)
+		mmc->caps |= MMC_CAP_DRIVER_TYPE_C;
+	if (caps[1] & SDHCI_DRIVER_TYPE_D)
+		mmc->caps |= MMC_CAP_DRIVER_TYPE_D;
+
 	ocr_avail = 0;
 	/*
 	 * According to SD Host Controller spec v3.00, if the Host System
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 95d70e6..a407b5b 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -72,7 +72,7 @@
 #define  SDHCI_DATA_LVL_MASK	0x00F00000
 #define   SDHCI_DATA_LVL_SHIFT	20
 
-#define SDHCI_HOST_CONTROL 	0x28
+#define SDHCI_HOST_CONTROL1	0x28
 #define  SDHCI_CTRL_LED		0x01
 #define  SDHCI_CTRL_4BITBUS	0x02
 #define  SDHCI_CTRL_HISPD	0x04
@@ -151,6 +151,11 @@
 
 #define SDHCI_HOST_CONTROL2		0x3E
 #define  SDHCI_CTRL_VDD_180		0x0008
+#define  SDHCI_CTRL_DRV_TYPE_B		0x0000
+#define  SDHCI_CTRL_DRV_TYPE_A		0x0010
+#define  SDHCI_CTRL_DRV_TYPE_C		0x0020
+#define  SDHCI_CTRL_DRV_TYPE_D		0x0030
+#define  SDHCI_CTRL_PRESET_VAL_ENABLE	0x8000
 
 #define SDHCI_CAPABILITIES	0x40
 #define  SDHCI_TIMEOUT_CLK_MASK	0x0000003F
@@ -174,6 +179,9 @@
 #define  SDHCI_SUPPORT_SDR50	0x00000001
 #define  SDHCI_SUPPORT_SDR104	0x00000002
 #define  SDHCI_SUPPORT_DDR50	0x00000004
+#define  SDHCI_DRIVER_TYPE_A	0x00000010
+#define  SDHCI_DRIVER_TYPE_C	0x00000020
+#define  SDHCI_DRIVER_TYPE_D	0x00000040
 
 #define SDHCI_CAPABILITIES_1	0x44
 
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 7080f22..2d7f7a3 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -77,6 +77,10 @@ struct sd_switch_caps {
 	unsigned int		hs_max_dtr;
 	unsigned int		uhs_bus_mode;
 	unsigned int		uhs_drv_type;
+#define SD_DRIVER_TYPE_B	0x01
+#define SD_DRIVER_TYPE_A	0x02
+#define SD_DRIVER_TYPE_C	0x04
+#define SD_DRIVER_TYPE_D	0x08
 	unsigned int		uhs_curr_limit;
 };
 
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index ec09b32..e6ae4c4 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -56,6 +56,11 @@ struct mmc_ios {
 #define MMC_SDR_MODE		0
 #define MMC_1_2V_DDR_MODE	1
 #define MMC_1_8V_DDR_MODE	2
+
+	unsigned char	drv_type;		/* driver type (A, C, D) */
+
+#define MMC_SET_DRIVER_TYPE_A	1
+#define MMC_SET_DRIVER_TYPE_C	2
 };
 
 struct mmc_host_ops {
@@ -181,6 +186,9 @@ struct mmc_host {
 #define MMC_CAP_SET_XPC_330	(1 << 18)	/* Host supports >150mA current at 3.3V */
 #define MMC_CAP_SET_XPC_300	(1 << 19)	/* Host supports >150mA current at 3.0V */
 #define MMC_CAP_SET_XPC_180	(1 << 20)	/* Host supports >150mA current at 1.8V */
+#define MMC_CAP_DRIVER_TYPE_A	(1 << 21)	/* Host supports Driver Type A */
+#define MMC_CAP_DRIVER_TYPE_C	(1 << 22)	/* Host supports Driver Type C */
+#define MMC_CAP_DRIVER_TYPE_D	(1 << 23)	/* Host supports Driver Type D */
 
 	mmc_pm_flag_t		pm_caps;	/* supported pm features */
 
-- 
1.7.1


^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH 05/12] mmc: sdhci: reset sdclk before setting high speed enable
  2011-02-15  9:34 [PATCH 00/12] add support for sd host controller v3.00 Arindam Nath
                   ` (3 preceding siblings ...)
  2011-02-15  9:35 ` [PATCH 04/12] mmc: sd: add support for driver type selection Arindam Nath
@ 2011-02-15  9:35 ` Arindam Nath
  2011-02-15  9:35 ` [PATCH 06/12] mmc: sd: add support for uhs bus speed mode selection Arindam Nath
                   ` (8 subsequent siblings)
  13 siblings, 0 replies; 45+ messages in thread
From: Arindam Nath @ 2011-02-15  9:35 UTC (permalink / raw)
  To: cjb; +Cc: linux-mmc, henry.su, aaron.lu, anath.amd, Arindam Nath

As per Host Controller spec v3.00, we reset SDCLK before setting
High Speed Enable, and then set it back to avoid generating clock
gliches.

Signed-off-by: Arindam Nath <arindam.nath@amd.com>
---
 drivers/mmc/host/sdhci.c |   25 ++++++++++++++++++++++---
 1 files changed, 22 insertions(+), 3 deletions(-)

diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index b768839..5db0109 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1280,13 +1280,12 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 	else
 		ctrl &= ~SDHCI_CTRL_HISPD;
 
-	sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL1);
-
 	if (host->version >= SDHCI_SPEC_300) {
 		u16 ctrl_2;
 
 		ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
 		if (!(ctrl_2 & SDHCI_CTRL_PRESET_VAL_ENABLE)) {
+			sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL1);
 			/*
 			 * We only need to set Driver Strength if the
 			 * preset value enable is not set.
@@ -1297,8 +1296,28 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 				ctrl_2 |= SDHCI_CTRL_DRV_TYPE_C;
 
 			sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
+		} else {
+			/*
+			 * According to SDHC Spec v3.00, if the Preset Value
+			 * Enable in the Host Control 2 register is set, we
+			 * need to reset SD Clock Enable before changing High
+			 * Speed Enable to avoid generating clock gliches.
+			 */
+			u16 clk;
+
+			/* Reset SD Clock Enable */
+			clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+			clk &= ~SDHCI_CLOCK_CARD_EN;
+			sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+
+			sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL1);
+
+			/* Re-enable SD Clock */
+			clk |= SDHCI_CLOCK_CARD_EN;
+			sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
 		}
-	}
+	} else
+		sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL1);
 
 	/*
 	 * Some (ENE) controllers go apeshit on some ios operation,
-- 
1.7.1


^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH 06/12] mmc: sd: add support for uhs bus speed mode selection
  2011-02-15  9:34 [PATCH 00/12] add support for sd host controller v3.00 Arindam Nath
                   ` (4 preceding siblings ...)
  2011-02-15  9:35 ` [PATCH 05/12] mmc: sdhci: reset sdclk before setting high speed enable Arindam Nath
@ 2011-02-15  9:35 ` Arindam Nath
  2011-03-09 21:43   ` Philip Rakity
  2011-02-15  9:35 ` [PATCH 07/12] mmc: sd: set current limit for uhs cards Arindam Nath
                   ` (7 subsequent siblings)
  13 siblings, 1 reply; 45+ messages in thread
From: Arindam Nath @ 2011-02-15  9:35 UTC (permalink / raw)
  To: cjb; +Cc: linux-mmc, henry.su, aaron.lu, anath.amd, Arindam Nath

This patch adds support for setting UHS-I bus speed mode during UHS-I
initialization procedure. Since both the host and card can support
more than one bus speed, we select the highest speed based on both of
their capabilities. First we set the bus speed mode for the card using
CMD6 mode 1, and then we program the host controller to support the
required speed mode. We also set High Speed Enable in case one of the
UHS-I modes is selected. Since MMC_TIMING_UHS_SDR25 is same as
MMC_TIMING_SD_HS, we don't need to set the SDHCI_CTRL_HISPD flag for
UHS SDR25 again. We also take care to reset SD clock before setting
UHS mode in the Host Control2 register, and then re-enable it as per
the Host Controller spec v3.00. We set the clock frequency for the
UHS-I mode selected.

Signed-off-by: Arindam Nath <arindam.nath@amd.com>
---
 drivers/mmc/core/sd.c    |   82 ++++++++++++++++++++++++++++++++++++++++++++++
 drivers/mmc/host/sdhci.c |   32 +++++++++++++++++-
 drivers/mmc/host/sdhci.h |    5 +++
 include/linux/mmc/card.h |   16 +++++++++
 include/linux/mmc/host.h |    4 ++
 5 files changed, 137 insertions(+), 2 deletions(-)

diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 9e4574d..e681385 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -464,6 +464,83 @@ static int sd_select_driver_type(struct mmc_card *card, u8 *status)
 	return 0;
 }
 
+static int sd_set_bus_speed_mode(struct mmc_card *card, u8 *status)
+{
+	unsigned int bus_speed, timing;
+	int err;
+
+	/*
+	 * If the host doesn't support any of the UHS-I modes, fallback on
+	 * default speed.
+	 */
+	if (!(card->host->caps & (MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 |
+	    MMC_CAP_UHS_DDR50)))
+		return 0;
+
+	if (card->host->caps & MMC_CAP_UHS_SDR104) {
+		if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR104) {
+			bus_speed = UHS_SDR104_BUS_SPEED;
+			timing = MMC_TIMING_UHS_SDR104;
+			card->sw_caps.uhs_max_dtr = UHS_SDR104_MAX_DTR;
+		} else if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR50) {
+			bus_speed = UHS_SDR50_BUS_SPEED;
+			timing = MMC_TIMING_UHS_SDR50;
+			card->sw_caps.uhs_max_dtr = UHS_SDR50_MAX_DTR;
+		} else if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_DDR50) {
+			bus_speed = UHS_DDR50_BUS_SPEED;
+			timing = MMC_TIMING_UHS_DDR50;
+			card->sw_caps.uhs_max_dtr = UHS_DDR50_MAX_DTR;
+		} else if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR25) {
+			bus_speed = UHS_SDR25_BUS_SPEED;
+			timing = MMC_TIMING_UHS_SDR25;
+			card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR;
+		}
+	} else if (card->host->caps & MMC_CAP_UHS_SDR50) {
+		if ((card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR104) ||
+		    (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR50)) {
+			bus_speed = UHS_SDR50_BUS_SPEED;
+			timing = MMC_TIMING_UHS_SDR50;
+			card->sw_caps.uhs_max_dtr = UHS_SDR50_MAX_DTR;
+		} else if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_DDR50) {
+			bus_speed = UHS_DDR50_BUS_SPEED;
+			timing = MMC_TIMING_UHS_DDR50;
+			card->sw_caps.uhs_max_dtr = UHS_DDR50_MAX_DTR;
+		} else if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR25) {
+			bus_speed = UHS_SDR25_BUS_SPEED;
+			timing = MMC_TIMING_UHS_SDR25;
+			card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR;
+		}
+	} else if (card->host->caps & MMC_CAP_UHS_DDR50) {
+		if ((card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR104) ||
+		    (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR50) ||
+		    (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_DDR50)) {
+			bus_speed = UHS_DDR50_BUS_SPEED;
+			timing = MMC_TIMING_UHS_DDR50;
+			card->sw_caps.uhs_max_dtr = UHS_DDR50_MAX_DTR;
+		} else if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR25) {
+			bus_speed = UHS_SDR25_BUS_SPEED;
+			timing = MMC_TIMING_UHS_SDR25;
+			card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR;
+		}
+	}
+
+	err = mmc_sd_switch(card, 1, 0, bus_speed, status);
+	if (err)
+		return err;
+
+	if ((status[16] & 0xF) != bus_speed)
+		printk(KERN_WARNING "%s: Problem setting bus speed mode!\n",
+			mmc_hostname(card->host));
+	else {
+		if (bus_speed) {
+			mmc_set_timing(card->host, timing);
+			mmc_set_clock(card->host, card->sw_caps.uhs_max_dtr);
+		}
+	}
+
+	return 0;
+}
+
 /*
  * UHS-I specific initialization procedure
  */
@@ -499,6 +576,11 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card)
 
 	/* Set the driver strength for the card */
 	err = sd_select_driver_type(card, status);
+	if (err)
+		goto out;
+
+	/* Set bus speed mode of the card */
+	err = sd_set_bus_speed_mode(card, status);
 
 out:
 	kfree(status);
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 5db0109..ae91c6b 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1281,7 +1281,13 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 		ctrl &= ~SDHCI_CTRL_HISPD;
 
 	if (host->version >= SDHCI_SPEC_300) {
-		u16 ctrl_2;
+		u16 clk, ctrl_2;
+
+		/* In case of UHS-I modes, set High Speed Enable */
+		if ((ios->timing == MMC_TIMING_UHS_SDR50) ||
+		    (ios->timing == MMC_TIMING_UHS_SDR104) ||
+		    (ios->timing == MMC_TIMING_UHS_DDR50))
+			ctrl |= SDHCI_CTRL_HISPD;
 
 		ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
 		if (!(ctrl_2 & SDHCI_CTRL_PRESET_VAL_ENABLE)) {
@@ -1303,7 +1309,6 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 			 * need to reset SD Clock Enable before changing High
 			 * Speed Enable to avoid generating clock gliches.
 			 */
-			u16 clk;
 
 			/* Reset SD Clock Enable */
 			clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
@@ -1316,6 +1321,29 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 			clk |= SDHCI_CLOCK_CARD_EN;
 			sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
 		}
+
+		ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+
+		/* Select Bus Speed Mode for host */
+		if (ios->timing == MMC_TIMING_UHS_SDR25)
+			ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
+		else if (ios->timing == MMC_TIMING_UHS_SDR50)
+			ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
+		else if (ios->timing == MMC_TIMING_UHS_SDR104)
+			ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
+		else if (ios->timing == MMC_TIMING_UHS_DDR50)
+			ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
+
+		/* Reset SD Clock Enable */
+		clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+		clk &= ~SDHCI_CLOCK_CARD_EN;
+		sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+
+		sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
+
+		/* Re-enable SD Clock */
+		clk |= SDHCI_CLOCK_CARD_EN;
+		sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
 	} else
 		sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL1);
 
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index a407b5b..5bf244d 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -150,6 +150,11 @@
 #define SDHCI_ACMD12_ERR	0x3C
 
 #define SDHCI_HOST_CONTROL2		0x3E
+#define  SDHCI_CTRL_UHS_SDR12		0x0000
+#define  SDHCI_CTRL_UHS_SDR25		0x0001
+#define  SDHCI_CTRL_UHS_SDR50		0x0002
+#define  SDHCI_CTRL_UHS_SDR104		0x0003
+#define  SDHCI_CTRL_UHS_DDR50		0x0004
 #define  SDHCI_CTRL_VDD_180		0x0008
 #define  SDHCI_CTRL_DRV_TYPE_B		0x0000
 #define  SDHCI_CTRL_DRV_TYPE_A		0x0010
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 2d7f7a3..0b24c41 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -75,7 +75,23 @@ struct sd_ssr {
 
 struct sd_switch_caps {
 	unsigned int		hs_max_dtr;
+	unsigned int		uhs_max_dtr;
+#define UHS_SDR104_MAX_DTR	208000000
+#define UHS_SDR50_MAX_DTR	100000000
+#define UHS_DDR50_MAX_DTR	50000000
+#define UHS_SDR25_MAX_DTR	50000000
 	unsigned int		uhs_bus_mode;
+#define UHS_SDR12_BUS_SPEED	0
+#define UHS_SDR25_BUS_SPEED	1
+#define UHS_SDR50_BUS_SPEED	2
+#define UHS_SDR104_BUS_SPEED	3
+#define UHS_DDR50_BUS_SPEED	4
+
+#define SD_MODE_UHS_SDR12	(1 << UHS_SDR12_BUS_SPEED)
+#define SD_MODE_UHS_SDR25	(1 << UHS_SDR25_BUS_SPEED)
+#define SD_MODE_UHS_SDR50	(1 << UHS_SDR50_BUS_SPEED)
+#define SD_MODE_UHS_SDR104	(1 << UHS_SDR104_BUS_SPEED)
+#define SD_MODE_UHS_DDR50	(1 << UHS_DDR50_BUS_SPEED)
 	unsigned int		uhs_drv_type;
 #define SD_DRIVER_TYPE_B	0x01
 #define SD_DRIVER_TYPE_A	0x02
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index e6ae4c4..0a21839 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -50,6 +50,10 @@ struct mmc_ios {
 #define MMC_TIMING_LEGACY	0
 #define MMC_TIMING_MMC_HS	1
 #define MMC_TIMING_SD_HS	2
+#define MMC_TIMING_UHS_SDR25	MMC_TIMING_SD_HS
+#define MMC_TIMING_UHS_SDR50	3
+#define MMC_TIMING_UHS_SDR104	4
+#define MMC_TIMING_UHS_DDR50	5
 
 	unsigned char	ddr;			/* dual data rate used */
 
-- 
1.7.1


^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH 07/12] mmc: sd: set current limit for uhs cards
  2011-02-15  9:34 [PATCH 00/12] add support for sd host controller v3.00 Arindam Nath
                   ` (5 preceding siblings ...)
  2011-02-15  9:35 ` [PATCH 06/12] mmc: sd: add support for uhs bus speed mode selection Arindam Nath
@ 2011-02-15  9:35 ` Arindam Nath
  2011-02-15  9:35 ` [PATCH 08/12] mmc: sd: report correct speed and capacity of " Arindam Nath
                   ` (6 subsequent siblings)
  13 siblings, 0 replies; 45+ messages in thread
From: Arindam Nath @ 2011-02-15  9:35 UTC (permalink / raw)
  To: cjb; +Cc: linux-mmc, henry.su, aaron.lu, anath.amd, Arindam Nath

We decide on the current limit to be set for the card based on the
Capability of Host Controller to provide current at 1.8V signalling,
and the maximum current limit of the card as indicated by CMD6 mode 0.
We then set the current limit for the card using CMD6 mode 1.

Signed-off-by: Arindam Nath <arindam.nath@amd.com>
---
 drivers/mmc/core/sd.c    |   41 +++++++++++++++++++++++++++++++++++++++++
 drivers/mmc/host/sdhci.c |   24 ++++++++++++++++++++++++
 include/linux/mmc/card.h |    9 +++++++++
 include/linux/mmc/host.h |    1 +
 4 files changed, 75 insertions(+), 0 deletions(-)

diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index e681385..80c3831 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -541,6 +541,42 @@ static int sd_set_bus_speed_mode(struct mmc_card *card, u8 *status)
 	return 0;
 }
 
+static int sd_set_current_limit(struct mmc_card *card, u8 *status)
+{
+	struct mmc_host *host = card->host;
+	int mmc_host_max_current_180, current_limit;
+	int err;
+
+	/* Maximum current supported by host at 1.8V */
+	mmc_host_max_current_180 = host->ops->get_max_current_180(host);
+
+	if (mmc_host_max_current_180 >= 800) {
+		if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_800)
+			current_limit = SD_SET_CURRENT_LIMIT_800;
+		else if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_600)
+			current_limit = SD_SET_CURRENT_LIMIT_600;
+		else if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_400)
+			current_limit = SD_SET_CURRENT_LIMIT_400;
+	} else if (mmc_host_max_current_180 >= 600) {
+		if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_600)
+			current_limit = SD_SET_CURRENT_LIMIT_600;
+		else if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_400)
+			current_limit = SD_SET_CURRENT_LIMIT_400;
+	} else if (mmc_host_max_current_180 >= 400)
+		if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_400)
+			current_limit = SD_SET_CURRENT_LIMIT_400;
+
+	err = mmc_sd_switch(card, 1, 3, current_limit, status);
+	if (err)
+		return err;
+
+	if (((status[15] >> 4) & 0x0F) != current_limit)
+		printk(KERN_WARNING "%s: Problem setting current limit!\n",
+			mmc_hostname(card->host));
+
+	return 0;
+}
+
 /*
  * UHS-I specific initialization procedure
  */
@@ -581,6 +617,11 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card)
 
 	/* Set bus speed mode of the card */
 	err = sd_set_bus_speed_mode(card, status);
+	if (err)
+		goto out;
+
+	/* Set current limit for the card */
+	err = sd_set_current_limit(card, status);
 
 out:
 	kfree(status);
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index ae91c6b..fc2cba6 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1471,12 +1471,36 @@ static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc)
 	return -EAGAIN;
 }
 
+static int sdhci_get_max_current_180(struct mmc_host *mmc)
+{
+	struct sdhci_host *host;
+	u32 max_current_caps;
+	unsigned long flags;
+	int max_current_180;
+
+	host = mmc_priv(mmc);
+
+	spin_lock_irqsave(&host->lock, flags);
+
+	max_current_caps = sdhci_readl(host, SDHCI_MAX_CURRENT);
+
+	spin_unlock_irqrestore(&host->lock, flags);
+
+	/* Maximum current is 4 times the register value for 1.8V */
+	max_current_180 = ((max_current_caps & SDHCI_MAX_CURRENT_180_MASK) >>
+			   SDHCI_MAX_CURRENT_180_SHIFT) *
+			   SDHCI_MAX_CURRENT_MULTIPLIER;
+
+	return max_current_180;
+}
+
 static const struct mmc_host_ops sdhci_ops = {
 	.request	= sdhci_request,
 	.set_ios	= sdhci_set_ios,
 	.get_ro		= sdhci_get_ro,
 	.enable_sdio_irq = sdhci_enable_sdio_irq,
 	.start_signal_voltage_switch	= sdhci_start_signal_voltage_switch,
+	.get_max_current_180		= sdhci_get_max_current_180,
 };
 
 /*****************************************************************************\
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 0b24c41..a6811ae 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -98,6 +98,15 @@ struct sd_switch_caps {
 #define SD_DRIVER_TYPE_C	0x04
 #define SD_DRIVER_TYPE_D	0x08
 	unsigned int		uhs_curr_limit;
+#define SD_SET_CURRENT_LIMIT_200	0
+#define SD_SET_CURRENT_LIMIT_400	1
+#define SD_SET_CURRENT_LIMIT_600	2
+#define SD_SET_CURRENT_LIMIT_800	3
+
+#define SD_MAX_CURRENT_200	(1 << SD_SET_CURRENT_LIMIT_200)
+#define SD_MAX_CURRENT_400	(1 << SD_SET_CURRENT_LIMIT_400)
+#define SD_MAX_CURRENT_600	(1 << SD_SET_CURRENT_LIMIT_600)
+#define SD_MAX_CURRENT_800	(1 << SD_SET_CURRENT_LIMIT_800)
 };
 
 struct sdio_cccr {
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 0a21839..65a003d 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -128,6 +128,7 @@ struct mmc_host_ops {
 	void	(*init_card)(struct mmc_host *host, struct mmc_card *card);
 
 	int	(*start_signal_voltage_switch)(struct mmc_host *host);
+	int	(*get_max_current_180)(struct mmc_host *mmc);
 };
 
 struct mmc_card;
-- 
1.7.1


^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH 08/12] mmc: sd: report correct speed and capacity of uhs cards
  2011-02-15  9:34 [PATCH 00/12] add support for sd host controller v3.00 Arindam Nath
                   ` (6 preceding siblings ...)
  2011-02-15  9:35 ` [PATCH 07/12] mmc: sd: set current limit for uhs cards Arindam Nath
@ 2011-02-15  9:35 ` Arindam Nath
  2011-02-15  9:35 ` [PATCH 09/12] mmc: sd: add support for tuning during uhs initialization Arindam Nath
                   ` (5 subsequent siblings)
  13 siblings, 0 replies; 45+ messages in thread
From: Arindam Nath @ 2011-02-15  9:35 UTC (permalink / raw)
  To: cjb; +Cc: linux-mmc, henry.su, aaron.lu, anath.amd, Arindam Nath

Since only UHS-I cards respond with S18A set in response to ACMD41,
we set the card as ultra-high-speed after successfull initialization.
We can have SDHC or SDXC UHS-I cards, so we need to decide based on
C_SIZE field of CSDv2.0 register. According to Physical Layer spec v3.01,
the minimum value of C_SIZE for SDXC card is 00FFFFh.

Signed-off-by: Arindam Nath <arindam.nath@amd.com>
---
 drivers/mmc/core/bus.c   |   11 ++++++++---
 drivers/mmc/core/sd.c    |   10 +++++++++-
 include/linux/mmc/card.h |    7 +++++++
 3 files changed, 24 insertions(+), 4 deletions(-)

diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
index 63667a8..ceeefa4 100644
--- a/drivers/mmc/core/bus.c
+++ b/drivers/mmc/core/bus.c
@@ -274,8 +274,12 @@ int mmc_add_card(struct mmc_card *card)
 		break;
 	case MMC_TYPE_SD:
 		type = "SD";
-		if (mmc_card_blockaddr(card))
-			type = "SDHC";
+		if (mmc_card_blockaddr(card)) {
+			if (mmc_card_ext_capacity(card))
+				type = "SDXC";
+			else
+				type = "SDHC";
+		}
 		break;
 	case MMC_TYPE_SDIO:
 		type = "SDIO";
@@ -298,7 +302,8 @@ int mmc_add_card(struct mmc_card *card)
 	} else {
 		printk(KERN_INFO "%s: new %s%s%s card at address %04x\n",
 			mmc_hostname(card->host),
-			mmc_card_highspeed(card) ? "high speed " : "",
+			mmc_card_ultrahighspeed(card) ? "ultra high speed " :
+			(mmc_card_highspeed(card) ? "high speed " : ""),
 			mmc_card_ddr_mode(card) ? "DDR " : "",
 			type, card->rca);
 	}
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 80c3831..c52d427 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -129,7 +129,7 @@ static int mmc_decode_csd(struct mmc_card *card)
 		break;
 	case 1:
 		/*
-		 * This is a block-addressed SDHC card. Most
+		 * This is a block-addressed SDHC or SDXC card. Most
 		 * interesting fields are unused and have fixed
 		 * values. To avoid getting tripped by buggy cards,
 		 * we assume those fixed values ourselves.
@@ -143,6 +143,7 @@ static int mmc_decode_csd(struct mmc_card *card)
 		e = UNSTUFF_BITS(resp, 96, 3);
 		csd->max_dtr	  = tran_exp[e] * tran_mant[m];
 		csd->cmdclass	  = UNSTUFF_BITS(resp, 84, 12);
+		csd->c_size	  = UNSTUFF_BITS(resp, 48, 22);
 
 		m = UNSTUFF_BITS(resp, 48, 22);
 		csd->capacity     = (1 + m) << 10;
@@ -909,6 +910,13 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
 		err = mmc_sd_init_uhs_card(card);
 		if (err)
 			goto free_card;
+
+		/* Card is an ultra-high-speed card */
+		mmc_card_set_ultrahighspeed(card);
+
+		/* SDXC cards have a minimum C_SIZE of 0x00FFFF */
+		if (card->csd.c_size >= 0xFFFF)
+			mmc_card_set_ext_capacity(card);
 	} else {
 		/*
 		 * Attempt to change to high-speed (if supported)
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index a6811ae..61459aa 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -29,6 +29,7 @@ struct mmc_csd {
 	unsigned short		cmdclass;
 	unsigned short		tacc_clks;
 	unsigned int		tacc_ns;
+	unsigned int		c_size;
 	unsigned int		r2w_factor;
 	unsigned int		max_dtr;
 	unsigned int		erase_size;		/* In sectors */
@@ -151,6 +152,8 @@ struct mmc_card {
 #define MMC_STATE_HIGHSPEED	(1<<2)		/* card is in high speed mode */
 #define MMC_STATE_BLOCKADDR	(1<<3)		/* card uses block-addressing */
 #define MMC_STATE_HIGHSPEED_DDR (1<<4)		/* card is in high speed mode */
+#define MMC_STATE_ULTRAHIGHSPEED (1<<5)		/* card is in ultra high speed mode */
+#define MMC_CARD_SDXC		(1<<6)		/* card is SDXC */
 	unsigned int		quirks; 	/* card quirks */
 #define MMC_QUIRK_LENIENT_FN0	(1<<0)		/* allow SDIO FN0 writes outside of the VS CCCR range */
 #define MMC_QUIRK_BLKSZ_FOR_BYTE_MODE (1<<1)	/* use func->cur_blksize */
@@ -193,12 +196,16 @@ struct mmc_card {
 #define mmc_card_highspeed(c)	((c)->state & MMC_STATE_HIGHSPEED)
 #define mmc_card_blockaddr(c)	((c)->state & MMC_STATE_BLOCKADDR)
 #define mmc_card_ddr_mode(c)	((c)->state & MMC_STATE_HIGHSPEED_DDR)
+#define mmc_card_ultrahighspeed(c) ((c)->state & MMC_STATE_ULTRAHIGHSPEED)
+#define mmc_card_ext_capacity(c) ((c)->state & MMC_CARD_SDXC)
 
 #define mmc_card_set_present(c)	((c)->state |= MMC_STATE_PRESENT)
 #define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY)
 #define mmc_card_set_highspeed(c) ((c)->state |= MMC_STATE_HIGHSPEED)
 #define mmc_card_set_blockaddr(c) ((c)->state |= MMC_STATE_BLOCKADDR)
 #define mmc_card_set_ddr_mode(c) ((c)->state |= MMC_STATE_HIGHSPEED_DDR)
+#define mmc_card_set_ultrahighspeed(c) ((c)->state |= MMC_STATE_ULTRAHIGHSPEED)
+#define mmc_card_set_ext_capacity(c) ((c)->state |= MMC_CARD_SDXC)
 
 static inline int mmc_card_lenient_fn0(const struct mmc_card *c)
 {
-- 
1.7.1


^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH 09/12] mmc: sd: add support for tuning during uhs initialization
  2011-02-15  9:34 [PATCH 00/12] add support for sd host controller v3.00 Arindam Nath
                   ` (7 preceding siblings ...)
  2011-02-15  9:35 ` [PATCH 08/12] mmc: sd: report correct speed and capacity of " Arindam Nath
@ 2011-02-15  9:35 ` Arindam Nath
  2011-02-18 10:50   ` zhangfei gao
  2011-02-15  9:35 ` [PATCH 10/12] mmc: sdhci: enable preset value after " Arindam Nath
                   ` (4 subsequent siblings)
  13 siblings, 1 reply; 45+ messages in thread
From: Arindam Nath @ 2011-02-15  9:35 UTC (permalink / raw)
  To: cjb; +Cc: linux-mmc, henry.su, aaron.lu, anath.amd, Arindam Nath

Host Controller needs tuning during initialization to operate SDR50
and SDR104 UHS-I cards. Whether SDR50 mode actually needs tuning is
indicated by bit 45 of the Host Controller Capabilities register.
A new command CMD19 has been defined in the Physical Layer spec v3.01
to request the card to send tuning pattern.

We enable Buffer Read Ready interrupt at the very begining of tuning
procedure, because that is the only interrupt generated by the Host
Controller during tuning. Since there is no actual data transfer
between card and host memory, we don't set DMA Enable (bit 0) in the
Transfer Mode register. The tuning block is sent by the card to the
Host Controller using DAT lines, so we set Data Present Select (bit 5)
in the Command register. The Host Controller is responsible for doing
the verfication of tuning block sent by the card at the hardware level.
After sending CMD19, we wait for Buffer Read Ready interrupt. In case
we don't receive an interrupt after the specified timeout value, we
fall back on fixed sampling clock by setting Execute Tuning (bit 6)
and Sampling Clock Seletc (bit 7) of Host Control2 register to 0.
Before exiting the tuning procedure, we disable Buffer Read Ready interrupt.

Signed-off-by: Arindam Nath <arindam.nath@amd.com>
---
 drivers/mmc/core/sd.c     |    4 +
 drivers/mmc/host/sdhci.c  |  142 ++++++++++++++++++++++++++++++++++++++++++++-
 drivers/mmc/host/sdhci.h  |    3 +
 include/linux/mmc/host.h  |    1 +
 include/linux/mmc/mmc.h   |    1 +
 include/linux/mmc/sdhci.h |    4 +
 6 files changed, 153 insertions(+), 2 deletions(-)

diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index c52d427..3113cb6 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -624,6 +624,10 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card)
 	/* Set current limit for the card */
 	err = sd_set_current_limit(card, status);
 
+	/* SPI mode doesn't define CMD19 */
+	if (!mmc_host_is_spi(card->host))
+		card->host->ops->execute_tuning(card->host);
+
 out:
 	kfree(status);
 
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index fc2cba6..6f3f0dc 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -39,6 +39,8 @@
 #define SDHCI_USE_LEDS_CLASS
 #endif
 
+#define MAX_TUNING_LOOP 40
+
 static unsigned int debug_quirks = 0;
 
 static void sdhci_prepare_data(struct sdhci_host *, struct mmc_data *);
@@ -872,7 +874,10 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host,
 
 	if (data->flags & MMC_DATA_READ)
 		mode |= SDHCI_TRNS_READ;
-	if (host->flags & SDHCI_REQ_USE_DMA)
+
+	/* CMD19 requires DMA Enable to be set to 0 */
+	if ((host->flags & SDHCI_REQ_USE_DMA) &&
+	   (host->cmd->opcode != MMC_SEND_TUNING_BLOCK))
 		mode |= SDHCI_TRNS_DMA;
 
 	sdhci_writew(host, mode, SDHCI_TRANSFER_MODE);
@@ -988,7 +993,9 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
 		flags |= SDHCI_CMD_CRC;
 	if (cmd->flags & MMC_RSP_OPCODE)
 		flags |= SDHCI_CMD_INDEX;
-	if (cmd->data)
+
+	/* CMD19 is special in that the Data Present Select should be set */
+	if (cmd->data || (cmd->opcode == MMC_SEND_TUNING_BLOCK))
 		flags |= SDHCI_CMD_DATA;
 
 	sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND);
@@ -1494,6 +1501,119 @@ static int sdhci_get_max_current_180(struct mmc_host *mmc)
 	return max_current_180;
 }
 
+static void sdhci_execute_tuning(struct mmc_host *mmc)
+{
+	struct sdhci_host *host;
+	u16 ctrl;
+	int tuning_loop_counter = MAX_TUNING_LOOP;
+	unsigned long flags;
+	unsigned long timeout;
+
+	host = mmc_priv(mmc);
+
+	spin_lock_irqsave(&host->lock, flags);
+
+	ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+
+	/*
+	 * Host Controller needs tuning only in case of SDR104 mode
+	 * and for SDR50 mode when Use Tuning for SDR50 is set in
+	 * Capabilities register.
+	 */
+	if ((ctrl & SDHCI_CTRL_UHS_SDR104) ||
+	    ((ctrl & SDHCI_CTRL_UHS_SDR50) &&
+	    (host->flags & SDHCI_SDR50_NEEDS_TUNING)))
+		ctrl |= SDHCI_CTRL_EXEC_TUNING;
+	else {
+		spin_unlock_irqrestore(&host->lock, flags);
+		return;
+	}
+
+	sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
+
+	/*
+	 * As per the Host Controller spec v3.00, tuning command
+	 * generates Buffer Read Ready interrupt, so enable that.
+	 */
+	sdhci_unmask_irqs(host, SDHCI_INT_DATA_AVAIL);
+
+	/*
+	 * Issue CMD19 repeatedly till Execute Tuning is set to 0 or the number
+	 * of loops reaches 40 times or a timeout of 150ms occurs.
+	 */
+	timeout = 150;
+	do {
+		struct mmc_command cmd;
+		struct mmc_request mrq;
+
+		if (!tuning_loop_counter && !timeout)
+			break;
+
+		memset(&cmd, 0, sizeof(struct mmc_command));
+		cmd.opcode = MMC_SEND_TUNING_BLOCK;
+		cmd.arg = 0;
+		cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+
+		memset(&cmd.resp, 0, sizeof(cmd.resp));
+		cmd.retries = 0;
+
+		cmd.data = NULL;
+		cmd.error = 0;
+
+		memset(&mrq, 0, sizeof(struct mmc_request));
+		mrq.cmd = &cmd;
+		host->mrq = &mrq;
+		sdhci_send_command(host, &cmd);
+
+		host->cmd = NULL;
+		host->mrq = NULL;
+
+		spin_unlock_irqrestore(&host->lock, flags);
+
+		/* Wait for Buffer Read Ready interrupt */
+		wait_event_interruptible_timeout(host->buf_ready_int,
+					(host->tuning_done == 1),
+					msecs_to_jiffies(50));
+		spin_lock_irqsave(&host->lock, flags);
+
+		if (!host->tuning_done) {
+			printk(KERN_INFO DRIVER_NAME ": Tuning procedure"
+				" failed, falling back to fixed sampling"
+				" clock\n");
+			ctrl &= ~SDHCI_CTRL_TUNED_CLK;
+			ctrl &= ~SDHCI_CTRL_EXEC_TUNING;
+			sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
+
+			goto out;
+		}
+
+		host->tuning_done = 0;
+
+		ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+		tuning_loop_counter--;
+		timeout--;
+		mdelay(1);
+	} while (ctrl & SDHCI_CTRL_EXEC_TUNING);
+
+	/*
+	 * The Host Driver has exhausted the maximum number of loops allowed,
+	 * so use fixed sampling frequency.
+	 */
+	if (!tuning_loop_counter || !timeout) {
+		ctrl &= ~SDHCI_CTRL_TUNED_CLK;
+		sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
+	} else {
+		if (!(ctrl & SDHCI_CTRL_TUNED_CLK))
+			printk(KERN_INFO DRIVER_NAME ": Tuning procedure"
+				" failed, falling back to fixed sampling"
+				" clock\n");
+	}
+
+out:
+	sdhci_mask_irqs(host, SDHCI_INT_DATA_AVAIL);
+	spin_unlock_irqrestore(&host->lock, flags);
+}
+
 static const struct mmc_host_ops sdhci_ops = {
 	.request	= sdhci_request,
 	.set_ios	= sdhci_set_ios,
@@ -1501,6 +1621,7 @@ static const struct mmc_host_ops sdhci_ops = {
 	.enable_sdio_irq = sdhci_enable_sdio_irq,
 	.start_signal_voltage_switch	= sdhci_start_signal_voltage_switch,
 	.get_max_current_180		= sdhci_get_max_current_180,
+	.execute_tuning			= sdhci_execute_tuning,
 };
 
 /*****************************************************************************\
@@ -1712,6 +1833,16 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
 {
 	BUG_ON(intmask == 0);
 
+	/* CMD19 generates _only_ Buffer Read Ready interrupt */
+	if (intmask & SDHCI_INT_DATA_AVAIL) {
+		if (SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND))
+				== MMC_SEND_TUNING_BLOCK) {
+			host->tuning_done = 1;
+			wake_up(&host->buf_ready_int);
+			return;
+		}
+	}
+
 	if (!host->data) {
 		/*
 		 * The "data complete" interrupt is also used to
@@ -2126,6 +2257,10 @@ int sdhci_add_host(struct sdhci_host *host)
 	if (caps[1] & SDHCI_SUPPORT_DDR50)
 		mmc->caps |= MMC_CAP_UHS_DDR50;
 
+	/* Does the host needs tuning for SDR50? */
+	if (caps[1] & SDHCI_USE_SDR50_TUNING)
+		host->flags |= SDHCI_SDR50_NEEDS_TUNING;
+
 	/* Driver Type(s) (A, C, D) supported by the host */
 	if (caps[1] & SDHCI_DRIVER_TYPE_A)
 		mmc->caps |= MMC_CAP_DRIVER_TYPE_A;
@@ -2269,6 +2404,9 @@ int sdhci_add_host(struct sdhci_host *host)
 
 	setup_timer(&host->timer, sdhci_timeout_timer, (unsigned long)host);
 
+	if (host->version >= SDHCI_SPEC_300)
+		init_waitqueue_head(&host->buf_ready_int);
+
 	ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED,
 		mmc_hostname(mmc), host);
 	if (ret)
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 5bf244d..4746879 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -160,6 +160,8 @@
 #define  SDHCI_CTRL_DRV_TYPE_A		0x0010
 #define  SDHCI_CTRL_DRV_TYPE_C		0x0020
 #define  SDHCI_CTRL_DRV_TYPE_D		0x0030
+#define  SDHCI_CTRL_EXEC_TUNING		0x0040
+#define  SDHCI_CTRL_TUNED_CLK		0x0080
 #define  SDHCI_CTRL_PRESET_VAL_ENABLE	0x8000
 
 #define SDHCI_CAPABILITIES	0x40
@@ -187,6 +189,7 @@
 #define  SDHCI_DRIVER_TYPE_A	0x00000010
 #define  SDHCI_DRIVER_TYPE_C	0x00000020
 #define  SDHCI_DRIVER_TYPE_D	0x00000040
+#define  SDHCI_USE_SDR50_TUNING	0x00002000
 
 #define SDHCI_CAPABILITIES_1	0x44
 
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 65a003d..4abb964 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -129,6 +129,7 @@ struct mmc_host_ops {
 
 	int	(*start_signal_voltage_switch)(struct mmc_host *host);
 	int	(*get_max_current_180)(struct mmc_host *mmc);
+	void	(*execute_tuning)(struct mmc_host *host);
 };
 
 struct mmc_card;
diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
index 612301f..9194f9e 100644
--- a/include/linux/mmc/mmc.h
+++ b/include/linux/mmc/mmc.h
@@ -50,6 +50,7 @@
 #define MMC_SET_BLOCKLEN         16   /* ac   [31:0] block len   R1  */
 #define MMC_READ_SINGLE_BLOCK    17   /* adtc [31:0] data addr   R1  */
 #define MMC_READ_MULTIPLE_BLOCK  18   /* adtc [31:0] data addr   R1  */
+#define MMC_SEND_TUNING_BLOCK    19   /* adtc                    R1  */
 
   /* class 3 */
 #define MMC_WRITE_DAT_UNTIL_STOP 20   /* adtc [31:0] data addr   R1  */
diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h
index 83bd9f7..26b6278 100644
--- a/include/linux/mmc/sdhci.h
+++ b/include/linux/mmc/sdhci.h
@@ -109,6 +109,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_SDR50_NEEDS_TUNING (1<<4)	/* SDR50 needs tuning */
 
 	unsigned int version;	/* SDHCI spec. version */
 
@@ -145,6 +146,9 @@ struct sdhci_host {
 	unsigned int            ocr_avail_sd;
 	unsigned int            ocr_avail_mmc;
 
+	wait_queue_head_t	buf_ready_int;	/* Waitqueue for Buffer Read Ready interrupt */
+	unsigned int		tuning_done;	/* Condition flag set when CMD19 succeeds */
+
 	unsigned long private[0] ____cacheline_aligned;
 };
 #endif /* __SDHCI_H */
-- 
1.7.1


^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH 10/12] mmc: sdhci: enable preset value after uhs initialization
  2011-02-15  9:34 [PATCH 00/12] add support for sd host controller v3.00 Arindam Nath
                   ` (8 preceding siblings ...)
  2011-02-15  9:35 ` [PATCH 09/12] mmc: sd: add support for tuning during uhs initialization Arindam Nath
@ 2011-02-15  9:35 ` Arindam Nath
  2011-02-15  9:35 ` [PATCH 11/12] mmc: sdhci: add support for programmable clock mode Arindam Nath
                   ` (3 subsequent siblings)
  13 siblings, 0 replies; 45+ messages in thread
From: Arindam Nath @ 2011-02-15  9:35 UTC (permalink / raw)
  To: cjb; +Cc: linux-mmc, henry.su, aaron.lu, anath.amd, Arindam Nath

According to the Host Controller spec v3.00, setting Preset Value Enable
in the Host Control2 register lets SDCLK Frequency Select, Clock Generator
Select and Driver Strength Select to be set automatically by the Host
Controller based on the UHS-I mode set. This patch enables this feature. We
also reset Preset Value Enable when the card is removed from the slot.

Signed-off-by: Arindam Nath <arindam.nath@amd.com>
---
 drivers/mmc/core/sd.c    |    6 ++++++
 drivers/mmc/host/sdhci.c |   36 ++++++++++++++++++++++++++++++++++++
 include/linux/mmc/host.h |    1 +
 3 files changed, 43 insertions(+), 0 deletions(-)

diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 3113cb6..1a625cc 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -949,6 +949,12 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
 		}
 	}
 
+	/*
+	 * Since initialization is now complete, enable preset
+	 * value registers.
+	 */
+	host->ops->enable_preset_value(host);
+
 	host->card = card;
 	return 0;
 
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 6f3f0dc..722f668 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1614,6 +1614,40 @@ out:
 	spin_unlock_irqrestore(&host->lock, flags);
 }
 
+static void sdhci_enable_preset_value(struct mmc_host *mmc)
+{
+	struct sdhci_host *host;
+	u16 ctrl;
+	unsigned long flags;
+
+	host = mmc_priv(mmc);
+
+	/* Host Controller v3.00 defines preset value registers */
+	if (host->version < SDHCI_SPEC_300)
+		return;
+
+	spin_lock_irqsave(&host->lock, flags);
+
+	ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+	ctrl |= SDHCI_CTRL_PRESET_VAL_ENABLE;
+	sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
+
+	spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static void sdhci_disable_preset_value(struct sdhci_host *host)
+{
+	u16 ctrl;
+
+	/* Only for Host Controller version >= v3.00 */
+	if (host->version < SDHCI_SPEC_300)
+		return;
+
+	ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+	ctrl &= ~SDHCI_CTRL_PRESET_VAL_ENABLE;
+	sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
+}
+
 static const struct mmc_host_ops sdhci_ops = {
 	.request	= sdhci_request,
 	.set_ios	= sdhci_set_ios,
@@ -1622,6 +1656,7 @@ static const struct mmc_host_ops sdhci_ops = {
 	.start_signal_voltage_switch	= sdhci_start_signal_voltage_switch,
 	.get_max_current_180		= sdhci_get_max_current_180,
 	.execute_tuning			= sdhci_execute_tuning,
+	.enable_preset_value		= sdhci_enable_preset_value,
 };
 
 /*****************************************************************************\
@@ -1932,6 +1967,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
 		sdhci_writel(host, intmask & (SDHCI_INT_CARD_INSERT |
 			SDHCI_INT_CARD_REMOVE), SDHCI_INT_STATUS);
 		tasklet_schedule(&host->card_tasklet);
+		sdhci_disable_preset_value(host);
 	}
 
 	intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE);
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 4abb964..a47accd 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -130,6 +130,7 @@ struct mmc_host_ops {
 	int	(*start_signal_voltage_switch)(struct mmc_host *host);
 	int	(*get_max_current_180)(struct mmc_host *mmc);
 	void	(*execute_tuning)(struct mmc_host *host);
+	void	(*enable_preset_value)(struct mmc_host *host);
 };
 
 struct mmc_card;
-- 
1.7.1


^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH 11/12] mmc: sdhci: add support for programmable clock mode
  2011-02-15  9:34 [PATCH 00/12] add support for sd host controller v3.00 Arindam Nath
                   ` (9 preceding siblings ...)
  2011-02-15  9:35 ` [PATCH 10/12] mmc: sdhci: enable preset value after " Arindam Nath
@ 2011-02-15  9:35 ` Arindam Nath
  2011-02-15  9:35 ` [PATCH 12/12] mmc: sdhci: add support for retuning mode 1 Arindam Nath
                   ` (2 subsequent siblings)
  13 siblings, 0 replies; 45+ messages in thread
From: Arindam Nath @ 2011-02-15  9:35 UTC (permalink / raw)
  To: cjb; +Cc: linux-mmc, henry.su, aaron.lu, anath.amd, Arindam Nath

Host Controller v3.00 supports programmable clock mode as an optional
feature. The support for this mode is indicated by non-zero value in
bits 48-55 of the Capabilities register. If supported, the actual
value of Clock Multiplier is one more than the value provided in the
bit fields. We only set Clock Generator Select (bit 5) and SDCLK Frequency
Select (bits 8-15) of the Clock Control register in case Preset Value
Enable is not set, otherwise these fields are automatically set by the
Host Controller based on the UHS mode selected. Also, since the maximum
and minimum clock frequency in this mode can be (Base Clock * Clock Mul)
and (Base Clock * Clock Mul)/1024 respectively, f_max and f_min have been
recalculated to reflect this change.

Signed-off-by: Arindam Nath <arindam.nath@amd.com>
---
 drivers/mmc/host/sdhci.c  |   77 +++++++++++++++++++++++++++++++++++++-------
 drivers/mmc/host/sdhci.h  |    3 ++
 include/linux/mmc/sdhci.h |    1 +
 3 files changed, 68 insertions(+), 13 deletions(-)

diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 722f668..9f8fb84 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1036,7 +1036,7 @@ static void sdhci_finish_command(struct sdhci_host *host)
 
 static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
 {
-	int div;
+	int div = 0; /* Initialized for compiler warning */
 	u16 clk;
 	unsigned long timeout;
 
@@ -1055,14 +1055,45 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
 		goto out;
 
 	if (host->version >= SDHCI_SPEC_300) {
-		/* Version 3.00 divisors must be a multiple of 2. */
-		if (host->max_clk <= clock)
-			div = 1;
-		else {
-			for (div = 2; div < SDHCI_MAX_DIV_SPEC_300; div += 2) {
-				if ((host->max_clk / div) <= clock)
-					break;
+		/*
+		 * Check if the Host Controller supports Programmable Clock
+		 * Mode.
+		 */
+		if (host->clk_mul) {
+			u16 ctrl;
+
+			/*
+			 * We need to figure out whether the Host Driver needs
+			 * to select Programmable Clock Mode, or the value can
+			 * be set automatically by the Host Controller based on
+			 * the Preset Value registers.
+			 */
+			ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+			if (!(ctrl & SDHCI_CTRL_PRESET_VAL_ENABLE)) {
+				for (div = 1; div <= 1024; div++) {
+					if (((host->max_clk * host->clk_mul) /
+					      div) <= clock)
+						break;
+				}
+				/*
+				 * Set Programmable Clock Mode in the Clock
+				 * Control register.
+				 */
+				clk |= SDHCI_PROG_CLOCK_MODE;
+				div--;
 			}
+		} else {
+			/* Version 3.00 divisors must be a multiple of 2. */
+			if (host->max_clk <= clock)
+				div = 1;
+			else {
+				for (div = 2; div < SDHCI_MAX_DIV_SPEC_300;
+				     div += 2) {
+					if ((host->max_clk / div) <= clock)
+						break;
+				}
+			}
+			div >>= 1;
 		}
 	} else {
 		/* Version 2.00 divisors must be a power of 2. */
@@ -1070,8 +1101,8 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
 			if ((host->max_clk / div) <= clock)
 				break;
 		}
+		div >>= 1;
 	}
-	div >>= 1;
 
 	clk = (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT;
 	clk |= ((div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN)
@@ -2255,17 +2286,37 @@ int sdhci_add_host(struct sdhci_host *host)
 		host->timeout_clk *= 1000;
 
 	/*
+	 * In case of Host Controller v3.00, find out whether clock
+	 * multiplier is supported.
+	 */
+	host->clk_mul = (caps[1] & SDHCI_CLOCK_MUL_MASK) >>
+			SDHCI_CLOCK_MUL_SHIFT;
+
+	/*
+	 * In case the value in Clock Multiplier is 0, then programmable
+	 * clock mode is not supported, otherwise the actual clock
+	 * multiplier is one more than the value of Clock Multiplier
+	 * in the Capabilities Register.
+	 */
+	if (host->clk_mul)
+		host->clk_mul += 1;
+
+	/*
 	 * Set host parameters.
 	 */
 	mmc->ops = &sdhci_ops;
+	mmc->f_max = host->max_clk;
 	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 / SDHCI_MAX_DIV_SPEC_300;
-	else
+	else if (host->version >= SDHCI_SPEC_300) {
+		if (host->clk_mul) {
+			mmc->f_min = (host->max_clk * host->clk_mul) / 1024;
+			mmc->f_max = host->max_clk * host->clk_mul;
+		} else
+			mmc->f_min = host->max_clk / SDHCI_MAX_DIV_SPEC_300;
+	} else
 		mmc->f_min = host->max_clk / SDHCI_MAX_DIV_SPEC_200;
 
-	mmc->f_max = host->max_clk;
 	mmc->caps |= MMC_CAP_SDIO_IRQ;
 
 	/*
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 4746879..37a8c32 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -102,6 +102,7 @@
 #define  SDHCI_DIV_MASK	0xFF
 #define  SDHCI_DIV_MASK_LEN	8
 #define  SDHCI_DIV_HI_MASK	0x300
+#define  SDHCI_PROG_CLOCK_MODE	0x0020
 #define  SDHCI_CLOCK_CARD_EN	0x0004
 #define  SDHCI_CLOCK_INT_STABLE	0x0002
 #define  SDHCI_CLOCK_INT_EN	0x0001
@@ -190,6 +191,8 @@
 #define  SDHCI_DRIVER_TYPE_C	0x00000020
 #define  SDHCI_DRIVER_TYPE_D	0x00000040
 #define  SDHCI_USE_SDR50_TUNING	0x00002000
+#define  SDHCI_CLOCK_MUL_MASK	0x00FF0000
+#define  SDHCI_CLOCK_MUL_SHIFT	16
 
 #define SDHCI_CAPABILITIES_1	0x44
 
diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h
index 26b6278..1e7a654 100644
--- a/include/linux/mmc/sdhci.h
+++ b/include/linux/mmc/sdhci.h
@@ -115,6 +115,7 @@ struct sdhci_host {
 
 	unsigned int max_clk;	/* Max possible freq (MHz) */
 	unsigned int timeout_clk;	/* Timeout freq (KHz) */
+	unsigned int clk_mul;	/* Clock Muliplier value */
 
 	unsigned int clock;	/* Current clock (MHz) */
 	u8 pwr;			/* Current voltage */
-- 
1.7.1


^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH 12/12] mmc: sdhci: add support for retuning mode 1
  2011-02-15  9:34 [PATCH 00/12] add support for sd host controller v3.00 Arindam Nath
                   ` (10 preceding siblings ...)
  2011-02-15  9:35 ` [PATCH 11/12] mmc: sdhci: add support for programmable clock mode Arindam Nath
@ 2011-02-15  9:35 ` Arindam Nath
  2011-02-15 19:43 ` [PATCH 00/12] add support for sd host controller v3.00 Chris Ball
  2011-02-16 20:55 ` Chris Ball
  13 siblings, 0 replies; 45+ messages in thread
From: Arindam Nath @ 2011-02-15  9:35 UTC (permalink / raw)
  To: cjb; +Cc: linux-mmc, henry.su, aaron.lu, anath.amd, Arindam Nath

Host Controller v3.00 can support retuning modes 1,2 or 3 depending on
the bits 46-47 of the Capabilities register. Also, the timer count for
retuning is indicated by bits 40-43 of the same register. We initialize
timer_list for retuning after successfull UHS-I initialization. Since
retuning mode 1 sets a limit of 4MB on the maximum data length, we set
max_blk_count appropriately. Once the tuning timer expires, we set
SDHCI_NEEDS_RETUNING flag, and if the flag is set, we execute tuning
procedure before sending the next command. We need to restore mmc_request
structure after executing retuning procedure since host->mrq is used
inside the procedure to send CMD19. We also disable and re-enable this
flag during suspend and resume respectively, as per the spec v3.00.

Signed-off-by: Arindam Nath <arindam.nath@amd.com>
---
 drivers/mmc/core/sd.c     |    3 +
 drivers/mmc/host/sdhci.c  |  109 ++++++++++++++++++++++++++++++++++++++++++++-
 drivers/mmc/host/sdhci.h  |    6 ++-
 include/linux/mmc/host.h  |    1 +
 include/linux/mmc/sdhci.h |    6 +++
 5 files changed, 122 insertions(+), 3 deletions(-)

diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 1a625cc..e3daafb 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -628,6 +628,9 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card)
 	if (!mmc_host_is_spi(card->host))
 		card->host->ops->execute_tuning(card->host);
 
+	/* Initialize and start re-tuning timer */
+	card->host->ops->start_retuning_timer(card->host);
+
 out:
 	kfree(status);
 
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 9f8fb84..8c1ec6c 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -48,6 +48,8 @@ static void sdhci_finish_data(struct sdhci_host *);
 
 static void sdhci_send_command(struct sdhci_host *, struct mmc_command *);
 static void sdhci_finish_command(struct sdhci_host *);
+static void sdhci_execute_tuning(struct mmc_host *mmc);
+static void sdhci_tuning_timer(unsigned long data);
 
 static struct mmc_command *abort_cmd;
 
@@ -1246,8 +1248,34 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
 	if (!present || host->flags & SDHCI_DEVICE_DEAD) {
 		host->mrq->cmd->error = -ENOMEDIUM;
 		tasklet_schedule(&host->finish_tasklet);
-	} else
+	} else {
+		u32 present_state;
+
+		present_state = sdhci_readl(host, SDHCI_PRESENT_STATE);
+		/*
+		 * Check if the re-tuning timer has already expired and there
+		 * is no on-going data transfer. If so, we need to execute
+		 * tuning procedure before sending command.
+		 */
+		if ((host->flags & SDHCI_NEEDS_RETUNING) &&
+		    !(present_state & (SDHCI_DOING_WRITE |
+		     SDHCI_DOING_READ))) {
+			host->flags &= ~SDHCI_NEEDS_RETUNING;
+			/* Reload the new initial value for timer */
+			if (host->tuning_mode == SDHCI_TUNING_MODE_1)
+				mod_timer(&host->tuning_timer, jiffies +
+					host->tuning_count * HZ);
+
+			spin_unlock_irqrestore(&host->lock, flags);
+			sdhci_execute_tuning(mmc);
+			spin_lock_irqsave(&host->lock, flags);
+
+			/* Restore original mmc_request structure */
+			host->mrq = mrq;
+		}
+
 		sdhci_send_command(host, mrq->cmd);
+	}
 
 	mmiowb();
 	spin_unlock_irqrestore(&host->lock, flags);
@@ -1679,6 +1707,26 @@ static void sdhci_disable_preset_value(struct sdhci_host *host)
 	sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
 }
 
+static void sdhci_start_retuning_timer(struct mmc_host *mmc)
+{
+	struct sdhci_host *host;
+	unsigned long flags;
+
+	host = mmc_priv(mmc);
+
+	spin_lock_irqsave(&host->lock, flags);
+
+	if (host->tuning_count &&
+	    (host->tuning_mode == SDHCI_TUNING_MODE_1)) {
+		mod_timer(&host->tuning_timer, jiffies + host->tuning_count *
+			HZ);
+		/* Tuning mode 1 limits the maximum data length to 4MB */
+		mmc->max_blk_count = (4 * 1024 * 1024) / mmc->max_blk_size;
+	}
+
+	spin_unlock_irqrestore(&host->lock, flags);
+}
+
 static const struct mmc_host_ops sdhci_ops = {
 	.request	= sdhci_request,
 	.set_ios	= sdhci_set_ios,
@@ -1688,6 +1736,7 @@ static const struct mmc_host_ops sdhci_ops = {
 	.get_max_current_180		= sdhci_get_max_current_180,
 	.execute_tuning			= sdhci_execute_tuning,
 	.enable_preset_value		= sdhci_enable_preset_value,
+	.start_retuning_timer		= sdhci_start_retuning_timer,
 };
 
 /*****************************************************************************\
@@ -1737,6 +1786,10 @@ static void sdhci_tasklet_finish(unsigned long param)
 
 	del_timer(&host->timer);
 
+	if (host->version >= SDHCI_SPEC_300)
+		del_timer(&host->tuning_timer);
+
+
 	mrq = host->mrq;
 
 	/*
@@ -1811,6 +1864,20 @@ static void sdhci_timeout_timer(unsigned long data)
 	spin_unlock_irqrestore(&host->lock, flags);
 }
 
+static void sdhci_tuning_timer(unsigned long data)
+{
+	struct sdhci_host *host;
+	unsigned long flags;
+
+	host = (struct sdhci_host *)data;
+
+	spin_lock_irqsave(&host->lock, flags);
+
+	host->flags |= SDHCI_NEEDS_RETUNING;
+
+	spin_unlock_irqrestore(&host->lock, flags);
+}
+
 /*****************************************************************************\
  *                                                                           *
  * Interrupt handling                                                        *
@@ -2069,6 +2136,15 @@ int sdhci_suspend_host(struct sdhci_host *host, pm_message_t state)
 
 	sdhci_disable_card_detection(host);
 
+	/* Disable tuning since we are suspending */
+	if ((host->version >= SDHCI_SPEC_300) &&
+	    host->tuning_count &&
+	    (host->tuning_mode == SDHCI_TUNING_MODE_1)) {
+		host->flags &= ~SDHCI_NEEDS_RETUNING;
+		mod_timer(&host->tuning_timer, jiffies +
+			host->tuning_count * HZ);
+	}
+
 	ret = mmc_suspend_host(host->mmc);
 	if (ret)
 		return ret;
@@ -2110,6 +2186,12 @@ int sdhci_resume_host(struct sdhci_host *host)
 	ret = mmc_resume_host(host->mmc);
 	sdhci_enable_card_detection(host);
 
+	/* Set the re-tuning expiration flag */
+	if ((host->version >= SDHCI_SPEC_300) &&
+	    host->tuning_count &&
+	    (host->tuning_mode == SDHCI_TUNING_MODE_1))
+		host->flags |= SDHCI_NEEDS_RETUNING;
+
 	return ret;
 }
 
@@ -2356,6 +2438,21 @@ int sdhci_add_host(struct sdhci_host *host)
 	if (caps[1] & SDHCI_DRIVER_TYPE_D)
 		mmc->caps |= MMC_CAP_DRIVER_TYPE_D;
 
+	/* Initial value for re-tuning timer count */
+	host->tuning_count = (caps[1] & SDHCI_RETUNING_TIMER_COUNT_MASK) >>
+			      SDHCI_RETUNING_TIMER_COUNT_SHIFT;
+
+	/*
+	 * In case Re-tuning Timer is not disabled, the actual value of
+	 * re-tuning timer will be 2 ^ (n - 1).
+	 */
+	if (host->tuning_count)
+		host->tuning_count = 1 << (host->tuning_count - 1);
+
+	/* Re-tuning mode supported by the Host Controller */
+	host->tuning_mode = (caps[1] & SDHCI_RETUNING_MODE_MASK) >>
+			     SDHCI_RETUNING_MODE_SHIFT;
+
 	ocr_avail = 0;
 	/*
 	 * According to SD Host Controller spec v3.00, if the Host System
@@ -2491,9 +2588,15 @@ int sdhci_add_host(struct sdhci_host *host)
 
 	setup_timer(&host->timer, sdhci_timeout_timer, (unsigned long)host);
 
-	if (host->version >= SDHCI_SPEC_300)
+	if (host->version >= SDHCI_SPEC_300) {
 		init_waitqueue_head(&host->buf_ready_int);
 
+		/* Initialize re-tuning timer */
+		init_timer(&host->tuning_timer);
+		host->tuning_timer.data = (unsigned long)host;
+		host->tuning_timer.function = sdhci_tuning_timer;
+	}
+
 	ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED,
 		mmc_hostname(mmc), host);
 	if (ret)
@@ -2587,6 +2690,8 @@ void sdhci_remove_host(struct sdhci_host *host, int dead)
 	free_irq(host->irq, host);
 
 	del_timer_sync(&host->timer);
+	if (host->version >= SDHCI_SPEC_300)
+		del_timer_sync(&host->tuning_timer);
 
 	tasklet_kill(&host->card_tasklet);
 	tasklet_kill(&host->finish_tasklet);
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 37a8c32..53d5909 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -190,7 +190,11 @@
 #define  SDHCI_DRIVER_TYPE_A	0x00000010
 #define  SDHCI_DRIVER_TYPE_C	0x00000020
 #define  SDHCI_DRIVER_TYPE_D	0x00000040
-#define  SDHCI_USE_SDR50_TUNING	0x00002000
+#define  SDHCI_RETUNING_TIMER_COUNT_MASK	0x00000F00
+#define  SDHCI_RETUNING_TIMER_COUNT_SHIFT	8
+#define  SDHCI_USE_SDR50_TUNING			0x00002000
+#define  SDHCI_RETUNING_MODE_MASK		0x0000C000
+#define  SDHCI_RETUNING_MODE_SHIFT		14
 #define  SDHCI_CLOCK_MUL_MASK	0x00FF0000
 #define  SDHCI_CLOCK_MUL_SHIFT	16
 
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index a47accd..a8b8fdb 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -131,6 +131,7 @@ struct mmc_host_ops {
 	int	(*get_max_current_180)(struct mmc_host *mmc);
 	void	(*execute_tuning)(struct mmc_host *host);
 	void	(*enable_preset_value)(struct mmc_host *host);
+	void	(*start_retuning_timer)(struct mmc_host *host);
 };
 
 struct mmc_card;
diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h
index 1e7a654..8bd9244 100644
--- a/include/linux/mmc/sdhci.h
+++ b/include/linux/mmc/sdhci.h
@@ -110,6 +110,7 @@ struct sdhci_host {
 #define SDHCI_REQ_USE_DMA	(1<<2)	/* Use DMA for this req. */
 #define SDHCI_DEVICE_DEAD	(1<<3)	/* Device unresponsive */
 #define SDHCI_SDR50_NEEDS_TUNING (1<<4)	/* SDR50 needs tuning */
+#define SDHCI_NEEDS_RETUNING	(1<<5)	/* Host needs retuning */
 
 	unsigned int version;	/* SDHCI spec. version */
 
@@ -150,6 +151,11 @@ struct sdhci_host {
 	wait_queue_head_t	buf_ready_int;	/* Waitqueue for Buffer Read Ready interrupt */
 	unsigned int		tuning_done;	/* Condition flag set when CMD19 succeeds */
 
+	unsigned int		tuning_count;	/* Timer count for re-tuning */
+	unsigned int		tuning_mode;	/* Re-tuning mode supported by host */
+#define SDHCI_TUNING_MODE_1	0
+	struct timer_list	tuning_timer;	/* Timer for tuning */
+
 	unsigned long private[0] ____cacheline_aligned;
 };
 #endif /* __SDHCI_H */
-- 
1.7.1


^ permalink raw reply related	[flat|nested] 45+ messages in thread

* Re: [PATCH 02/12] mmc: sd: add support for signal voltage switch procedure
  2011-02-15  9:35 ` [PATCH 02/12] mmc: sd: add support for signal voltage switch procedure Arindam Nath
@ 2011-02-15 10:16   ` Wolfram Sang
  2011-02-16  7:55     ` Nath, Arindam
  2011-02-15 21:18   ` Nicolas Pitre
  2011-03-01 16:02   ` subhashj
  2 siblings, 1 reply; 45+ messages in thread
From: Wolfram Sang @ 2011-02-15 10:16 UTC (permalink / raw)
  To: Arindam Nath; +Cc: cjb, linux-mmc, henry.su, aaron.lu, anath.amd

On Tue, Feb 15, 2011 at 03:05:00PM +0530, Arindam Nath wrote:

> Host Controller v3.00 adds another Capabilities register. We can
> directly read this register without any version checking since the
> new register bits will be considred as reserved in older controllers,
> and will be read as 0.

I so much wish you were right, but reality is different :(

The i.MX35-processor, for example, has a custom watermark-register there. I
could fix this by intercepting the read using io-accessors, but to minimize
potential regressions on older controllers in general, I'd say the check for v3
might be justified?

Regards,

   Wolfram

-- 
Pengutronix e.K.                           | Wolfram Sang                |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 00/12] add support for sd host controller v3.00
  2011-02-15  9:34 [PATCH 00/12] add support for sd host controller v3.00 Arindam Nath
                   ` (11 preceding siblings ...)
  2011-02-15  9:35 ` [PATCH 12/12] mmc: sdhci: add support for retuning mode 1 Arindam Nath
@ 2011-02-15 19:43 ` Chris Ball
  2011-02-16  8:00   ` Nath, Arindam
  2011-02-16 20:55 ` Chris Ball
  13 siblings, 1 reply; 45+ messages in thread
From: Chris Ball @ 2011-02-15 19:43 UTC (permalink / raw)
  To: Arindam Nath
  Cc: linux-mmc, henry.su, aaron.lu, anath.amd, Philip Rakity,
	zhangfei gao, David Vrabel

Hi Arindam,

On Tue, Feb 15, 2011 at 03:04:58PM +0530, Arindam Nath wrote:
> The patches below add support for Host Controller v3.00 as per the
> spec v3.00. It also adds support for UHS-I cards as per Physical
> Layer Specification v3.01.

Thanks for the patchset!  It might take us some time to review these,
both because there's a lot of code and because we don't all have access
to the SD 3.0 specs.  I'm CC'ing some folks who've worked on 3.0 patches
in the past -- if anyone's willing to help review, that'd be great.

-- 
Chris Ball   <cjb@laptop.org>   <http://printf.net/>
One Laptop Per Child

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 01/12] mmc: sdhci: add support for auto CMD23
  2011-02-15  9:34 ` [PATCH 01/12] mmc: sdhci: add support for auto CMD23 Arindam Nath
@ 2011-02-15 20:58   ` Nicolas Pitre
  2011-02-16  8:04     ` Nath, Arindam
  2011-02-18 10:45   ` zhangfei gao
  1 sibling, 1 reply; 45+ messages in thread
From: Nicolas Pitre @ 2011-02-15 20:58 UTC (permalink / raw)
  To: Arindam Nath; +Cc: cjb, linux-mmc, henry.su, aaron.lu, anath.amd

On Tue, 15 Feb 2011, Arindam Nath wrote:

> Host Controller v3.00 and later support Auto CMD23 in the Transfer
> Mode register. Since Auto CMD23 can be used with or without DMA,
> and if used with DMA, it should _only_ be ADMA, we check against
> SDHCI_USE_SDMA not being set. This flag is reset when SDHCI_USE_ADMA
> is set.
> 
> A new definition for SDHCI_ARGUMENT2 register has been added
> in v3.00 spec, which is the same as SDHCI_DMA_ADDRESS. We program the
> block count for CMD23 in SDHCI_ARGUMENT2, so we don't need CMD12 to
> stop multiple block transfers. But during error recovery procedure,
> we will need to send Abort command, so we use a global variable
> abort_cmd to save the stop command to be used later.

This is wrong.  Don't use global variables for such things.  Imagine 
what might happen if you have more than one sdhci controller in a 
system. This is a per instance issue certainly not a global driver 
issue.


Nicolas

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 02/12] mmc: sd: add support for signal voltage switch procedure
  2011-02-15  9:35 ` [PATCH 02/12] mmc: sd: add support for signal voltage switch procedure Arindam Nath
  2011-02-15 10:16   ` Wolfram Sang
@ 2011-02-15 21:18   ` Nicolas Pitre
  2011-02-16  8:08     ` Nath, Arindam
  2011-03-01 16:02   ` subhashj
  2 siblings, 1 reply; 45+ messages in thread
From: Nicolas Pitre @ 2011-02-15 21:18 UTC (permalink / raw)
  To: Arindam Nath; +Cc: cjb, linux-mmc, henry.su, aaron.lu, anath.amd

On Tue, 15 Feb 2011, Arindam Nath wrote:

> @@ -1340,11 +1342,76 @@ out:
>  	spin_unlock_irqrestore(&host->lock, flags);
>  }
>  
> +static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc)
> +{
> +	struct sdhci_host *host;
> +	u8 pwr;
> +	u16 clk, ctrl;
> +	u32 present_state;
> +	unsigned long flags;
> +
> +	host = mmc_priv(mmc);
> +
> +	spin_lock_irqsave(&host->lock, flags);
> +
> +	/* Stop SDCLK */
> +	host = mmc_priv(mmc);
> +	clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
> +	clk &= ~SDHCI_CLOCK_CARD_EN;
> +	sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
> +
> +	/* Check whether DAT[3:0] is 0000 */
> +	present_state = sdhci_readl(host, SDHCI_PRESENT_STATE);
> +	if (!((present_state & SDHCI_DATA_LVL_MASK) >> SDHCI_DATA_LVL_SHIFT)) {
> +		/* Enable 1.8V Signal Enable in the Host Control2 register */
> +		ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> +		ctrl |= SDHCI_CTRL_VDD_180;
> +		sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> +
> +		/* Wait for 5ms */
> +		mdelay(5);

You're busy-waiting for 5 ms while holding a spinlock and with 
interrupts masked off.  This is totally unacceptable.

There shouldn't be any other concurrent access to the controller here as 
the 
core code should have claimed the host through mmc_claim_host(), if not 
this is a bug.  So if the only thing you're worried about is some sdhci 
specific interrupts then simply mask interrupts at the controller level, 
do your business including this 5 ms delay using msleep() and _not_ 
mdelay(), and unmask them afterwards.  If this is called in a non 
sleepable context then please make it so.


Nicolas

^ permalink raw reply	[flat|nested] 45+ messages in thread

* RE: [PATCH 02/12] mmc: sd: add support for signal voltage switch procedure
  2011-02-15 10:16   ` Wolfram Sang
@ 2011-02-16  7:55     ` Nath, Arindam
  0 siblings, 0 replies; 45+ messages in thread
From: Nath, Arindam @ 2011-02-16  7:55 UTC (permalink / raw)
  To: Wolfram Sang; +Cc: cjb, linux-mmc, Su, Henry, Lu, Aaron, anath.amd

Hi Wolfram,

> -----Original Message-----
> From: Wolfram Sang [mailto:w.sang@pengutronix.de]
> Sent: Tuesday, February 15, 2011 3:46 PM
> To: Nath, Arindam
> Cc: cjb@laptop.org; linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
> anath.amd@gmail.com
> Subject: Re: [PATCH 02/12] mmc: sd: add support for signal voltage
> switch procedure
> 
> On Tue, Feb 15, 2011 at 03:05:00PM +0530, Arindam Nath wrote:
> 
> > Host Controller v3.00 adds another Capabilities register. We can
> > directly read this register without any version checking since the
> > new register bits will be considred as reserved in older controllers,
> > and will be read as 0.
> 
> I so much wish you were right, but reality is different :(
> 
> The i.MX35-processor, for example, has a custom watermark-register
> there. I
> could fix this by intercepting the read using io-accessors, but to
> minimize
> potential regressions on older controllers in general, I'd say the
> check for v3
> might be justified?

Thanks for the suggestion. I will add the changes in V2 of the patchset.

Regards,
Arindam


^ permalink raw reply	[flat|nested] 45+ messages in thread

* RE: [PATCH 00/12] add support for sd host controller v3.00
  2011-02-15 19:43 ` [PATCH 00/12] add support for sd host controller v3.00 Chris Ball
@ 2011-02-16  8:00   ` Nath, Arindam
  0 siblings, 0 replies; 45+ messages in thread
From: Nath, Arindam @ 2011-02-16  8:00 UTC (permalink / raw)
  To: Chris Ball
  Cc: linux-mmc, Su, Henry, Lu, Aaron, anath.amd, Philip Rakity,
	zhangfei gao, David Vrabel

Hi Chris,

> -----Original Message-----
> From: Chris Ball [mailto:cjb@laptop.org]
> Sent: Wednesday, February 16, 2011 1:14 AM
> To: Nath, Arindam
> Cc: linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
> anath.amd@gmail.com; Philip Rakity; zhangfei gao; David Vrabel
> Subject: Re: [PATCH 00/12] add support for sd host controller v3.00
> 
> Hi Arindam,
> 
> On Tue, Feb 15, 2011 at 03:04:58PM +0530, Arindam Nath wrote:
> > The patches below add support for Host Controller v3.00 as per the
> > spec v3.00. It also adds support for UHS-I cards as per Physical
> > Layer Specification v3.01.
> 
> Thanks for the patchset!  It might take us some time to review these,
> both because there's a lot of code and because we don't all have access
> to the SD 3.0 specs.  I'm CC'ing some folks who've worked on 3.0
> patches
> in the past -- if anyone's willing to help review, that'd be great.

I would appreciate getting comments on the patches.

Regards,
Arindam


^ permalink raw reply	[flat|nested] 45+ messages in thread

* RE: [PATCH 01/12] mmc: sdhci: add support for auto CMD23
  2011-02-15 20:58   ` Nicolas Pitre
@ 2011-02-16  8:04     ` Nath, Arindam
  0 siblings, 0 replies; 45+ messages in thread
From: Nath, Arindam @ 2011-02-16  8:04 UTC (permalink / raw)
  To: Nicolas Pitre; +Cc: cjb, linux-mmc, Su, Henry, Lu, Aaron, anath.amd

Hi Nicolas,

> -----Original Message-----
> From: Nicolas Pitre [mailto:nico@fluxnic.net]
> Sent: Wednesday, February 16, 2011 2:29 AM
> To: Nath, Arindam
> Cc: cjb@laptop.org; linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
> anath.amd@gmail.com
> Subject: Re: [PATCH 01/12] mmc: sdhci: add support for auto CMD23
> 
> On Tue, 15 Feb 2011, Arindam Nath wrote:
> 
> > Host Controller v3.00 and later support Auto CMD23 in the Transfer
> > Mode register. Since Auto CMD23 can be used with or without DMA,
> > and if used with DMA, it should _only_ be ADMA, we check against
> > SDHCI_USE_SDMA not being set. This flag is reset when SDHCI_USE_ADMA
> > is set.
> >
> > A new definition for SDHCI_ARGUMENT2 register has been added
> > in v3.00 spec, which is the same as SDHCI_DMA_ADDRESS. We program the
> > block count for CMD23 in SDHCI_ARGUMENT2, so we don't need CMD12 to
> > stop multiple block transfers. But during error recovery procedure,
> > we will need to send Abort command, so we use a global variable
> > abort_cmd to save the stop command to be used later.
> 
> This is wrong.  Don't use global variables for such things.  Imagine
> what might happen if you have more than one sdhci controller in a
> system. This is a per instance issue certainly not a global driver
> issue.

Thanks for the review. I will make the changes in V2 of the patchset.

Regards,
Arindam


^ permalink raw reply	[flat|nested] 45+ messages in thread

* RE: [PATCH 02/12] mmc: sd: add support for signal voltage switch procedure
  2011-02-15 21:18   ` Nicolas Pitre
@ 2011-02-16  8:08     ` Nath, Arindam
  0 siblings, 0 replies; 45+ messages in thread
From: Nath, Arindam @ 2011-02-16  8:08 UTC (permalink / raw)
  To: Nicolas Pitre; +Cc: cjb, linux-mmc, Su, Henry, Lu, Aaron, anath.amd

> -----Original Message-----
> From: Nicolas Pitre [mailto:nico@fluxnic.net]
> Sent: Wednesday, February 16, 2011 2:48 AM
> To: Nath, Arindam
> Cc: cjb@laptop.org; linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
> anath.amd@gmail.com
> Subject: Re: [PATCH 02/12] mmc: sd: add support for signal voltage
> switch procedure
> 
> On Tue, 15 Feb 2011, Arindam Nath wrote:
> 
> > @@ -1340,11 +1342,76 @@ out:
> >  	spin_unlock_irqrestore(&host->lock, flags);
> >  }
> >
> > +static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc)
> > +{
> > +	struct sdhci_host *host;
> > +	u8 pwr;
> > +	u16 clk, ctrl;
> > +	u32 present_state;
> > +	unsigned long flags;
> > +
> > +	host = mmc_priv(mmc);
> > +
> > +	spin_lock_irqsave(&host->lock, flags);
> > +
> > +	/* Stop SDCLK */
> > +	host = mmc_priv(mmc);
> > +	clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
> > +	clk &= ~SDHCI_CLOCK_CARD_EN;
> > +	sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
> > +
> > +	/* Check whether DAT[3:0] is 0000 */
> > +	present_state = sdhci_readl(host, SDHCI_PRESENT_STATE);
> > +	if (!((present_state & SDHCI_DATA_LVL_MASK) >>
> SDHCI_DATA_LVL_SHIFT)) {
> > +		/* Enable 1.8V Signal Enable in the Host Control2 register
> */
> > +		ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> > +		ctrl |= SDHCI_CTRL_VDD_180;
> > +		sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> > +
> > +		/* Wait for 5ms */
> > +		mdelay(5);
> 
> You're busy-waiting for 5 ms while holding a spinlock and with
> interrupts masked off.  This is totally unacceptable.
> 
> There shouldn't be any other concurrent access to the controller here
> as
> the
> core code should have claimed the host through mmc_claim_host(), if not
> this is a bug.  So if the only thing you're worried about is some sdhci
> specific interrupts then simply mask interrupts at the controller
> level,
> do your business including this 5 ms delay using msleep() and _not_
> mdelay(), and unmask them afterwards.  If this is called in a non
> sleepable context then please make it so.

Yes, it makes sense. I will use msleep() instead of mdelay() and the changes will reflect in V2 of the patchset.

Regards,
Arindam


^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 00/12] add support for sd host controller v3.00
  2011-02-15  9:34 [PATCH 00/12] add support for sd host controller v3.00 Arindam Nath
                   ` (12 preceding siblings ...)
  2011-02-15 19:43 ` [PATCH 00/12] add support for sd host controller v3.00 Chris Ball
@ 2011-02-16 20:55 ` Chris Ball
  2011-02-17  2:03   ` Nath, Arindam
  13 siblings, 1 reply; 45+ messages in thread
From: Chris Ball @ 2011-02-16 20:55 UTC (permalink / raw)
  To: Arindam Nath; +Cc: linux-mmc, henry.su, aaron.lu, anath.amd

Hi Arindam,

On Tue, Feb 15, 2011 at 03:04:58PM +0530, Arindam Nath wrote:
> The patches below add support for Host Controller v3.00 as per the
> spec v3.00. It also adds support for UHS-I cards as per Physical
> Layer Specification v3.01.

Could you base v2 of the patchset on top of my mmc-next branch¹, please?
I'm not sure what v1 was prepared against, but patch [2/12] onwards
don't apply successfully here.

Thanks!

¹: git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc.git mmc-next
-- 
Chris Ball   <cjb@laptop.org>   <http://printf.net/>
One Laptop Per Child

^ permalink raw reply	[flat|nested] 45+ messages in thread

* RE: [PATCH 00/12] add support for sd host controller v3.00
  2011-02-16 20:55 ` Chris Ball
@ 2011-02-17  2:03   ` Nath, Arindam
  0 siblings, 0 replies; 45+ messages in thread
From: Nath, Arindam @ 2011-02-17  2:03 UTC (permalink / raw)
  To: Chris Ball; +Cc: linux-mmc, Su, Henry, Lu, Aaron, anath.amd

Hi Chris,

> -----Original Message-----
> From: Chris Ball [mailto:cjb@laptop.org]
> Sent: Thursday, February 17, 2011 2:25 AM
> To: Nath, Arindam
> Cc: linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
> anath.amd@gmail.com
> Subject: Re: [PATCH 00/12] add support for sd host controller v3.00
> 
> Hi Arindam,
> 
> On Tue, Feb 15, 2011 at 03:04:58PM +0530, Arindam Nath wrote:
> > The patches below add support for Host Controller v3.00 as per the
> > spec v3.00. It also adds support for UHS-I cards as per Physical
> > Layer Specification v3.01.
> 
> Could you base v2 of the patchset on top of my mmc-next branch¹,
> please?
> I'm not sure what v1 was prepared against, but patch [2/12] onwards
> don't apply successfully here.

My patches have been created against Linus' git tree. I will make sure to rebase it against mmc-next for V2. Thanks for the patience.

Regards,
Arindam


^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 01/12] mmc: sdhci: add support for auto CMD23
  2011-02-15  9:34 ` [PATCH 01/12] mmc: sdhci: add support for auto CMD23 Arindam Nath
  2011-02-15 20:58   ` Nicolas Pitre
@ 2011-02-18 10:45   ` zhangfei gao
  2011-02-18 10:50     ` Arnd Bergmann
  2011-02-18 11:53     ` [PATCH 01/12] mmc: sdhci: add support for auto CMD23 Nath, Arindam
  1 sibling, 2 replies; 45+ messages in thread
From: zhangfei gao @ 2011-02-18 10:45 UTC (permalink / raw)
  To: Arindam Nath; +Cc: cjb, linux-mmc, henry.su, aaron.lu, anath.amd

On Tue, Feb 15, 2011 at 4:34 AM, Arindam Nath <arindam.nath@amd.com> wrote:
> Host Controller v3.00 and later support Auto CMD23 in the Transfer
> Mode register. Since Auto CMD23 can be used with or without DMA,
> and if used with DMA, it should _only_ be ADMA, we check against
> SDHCI_USE_SDMA not being set. This flag is reset when SDHCI_USE_ADMA
> is set.
>
> A new definition for SDHCI_ARGUMENT2 register has been added
> in v3.00 spec, which is the same as SDHCI_DMA_ADDRESS. We program the
> block count for CMD23 in SDHCI_ARGUMENT2, so we don't need CMD12 to
> stop multiple block transfers. But during error recovery procedure,
> we will need to send Abort command, so we use a global variable
> abort_cmd to save the stop command to be used later.
>
> Two bits are added to SCR register as per the Physical Layer Spec v3.01,
> which specify whether the card supports CMD20 and/or CMD23. We use this
> as one of the conditions to decide whether to enable Auto CMD23 or not.
>
> Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> ---
>  drivers/mmc/core/sd.c    |    6 ++++
>  drivers/mmc/host/sdhci.c |   66 +++++++++++++++++++++++++++++++++++++++++++--
>  drivers/mmc/host/sdhci.h |    2 +
>  include/linux/mmc/card.h |    4 +++
>  include/linux/mmc/sd.h   |    2 +-
>  5 files changed, 76 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> index d18c32b..b3f8a3c 100644
> --- a/drivers/mmc/core/sd.c
> +++ b/drivers/mmc/core/sd.c
> @@ -188,6 +188,12 @@ static int mmc_decode_scr(struct mmc_card *card)
>
>        scr->sda_vsn = UNSTUFF_BITS(resp, 56, 4);
>        scr->bus_widths = UNSTUFF_BITS(resp, 48, 4);
> +       if (scr->sda_vsn == SCR_SPEC_VER_2) {
> +               /* Check if Physical Layer Spec v3.0 is supported*/
> +               scr->sda_spec3 = UNSTUFF_BITS(resp, 47, 1);
> +               if (scr->sda_spec3)
> +                       scr->cmd_support = UNSTUFF_BITS(resp, 32, 2);
> +       }
>
>        if (UNSTUFF_BITS(resp, 55, 1))
>                card->erased_byte = 0xFF;
> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> index 9e15f41..0b537cf 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -25,6 +25,7 @@
>
>  #include <linux/mmc/mmc.h>
>  #include <linux/mmc/host.h>
> +#include <linux/mmc/card.h>
>
>  #include "sdhci.h"
>
> @@ -46,6 +47,8 @@ static void sdhci_finish_data(struct sdhci_host *);
>  static void sdhci_send_command(struct sdhci_host *, struct mmc_command *);
>  static void sdhci_finish_command(struct sdhci_host *);
>
> +static struct mmc_command *abort_cmd;
> +
>  static void sdhci_dumpregs(struct sdhci_host *host)
>  {
>        printk(KERN_DEBUG DRIVER_NAME ": =========== REGISTER DUMP (%s)===========\n",
> @@ -812,6 +815,30 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data)
>        sdhci_writew(host, data->blocks, SDHCI_BLOCK_COUNT);
>  }
>
> +/*
> + * Does the Host Controller support Auto CMD23?
> + *
> + * There are four preconditions for Auto CMD23 to be supported:
> + *  1. Host Controller v3.00 or later
> + *  2. Card supports CMD23
> + *  3. If DMA is used, it should be ADMA
> + *  4. Only when CMD18 or CMD25 is issued
> + */
> +static int sdhci_host_auto_cmd23_supported(struct sdhci_host *host,
> +       struct mmc_request *mrq)
> +{
> +       struct mmc_card *card = host->mmc->card;
> +
> +       if ((host->version >= SDHCI_SPEC_300) &&
> +          card && (card->scr.cmd_support & SD_SCR_CMD23_SUPPORT) &&
> +          !(host->flags & SDHCI_USE_SDMA) &&
> +          ((mrq->cmd->opcode == MMC_READ_MULTIPLE_BLOCK) ||
> +          (mrq->cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK)))
> +               return 1;
> +
> +       return 0;
> +}
> +
>  static void sdhci_set_transfer_mode(struct sdhci_host *host,
>        struct mmc_data *data)
>  {
> @@ -824,11 +851,23 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host,
>
>        mode = SDHCI_TRNS_BLK_CNT_EN;
>        if (data->blocks > 1) {
> +               if (sdhci_host_auto_cmd23_supported(host, host->mrq)) {
> +                       /*
> +                        * Host Controller v3.00 can automatically send CMD23
> +                        * before CMD18 or CMD25. For that, we need to enable
> +                        * Auto CMD23 in the Transfer Mode register and
> +                        * program the block count in Argument 2 register.
> +                        */
> +                       mode |= SDHCI_TRNS_AUTO_CMD23;
> +                       sdhci_writel(host, data->blocks, SDHCI_ARGUMENT2);
> +               }
> +
>                if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12)
>                        mode |= SDHCI_TRNS_MULTI | SDHCI_TRNS_ACMD12;
>                else
>                        mode |= SDHCI_TRNS_MULTI;
>        }
> +
>        if (data->flags & MMC_DATA_READ)
>                mode |= SDHCI_TRNS_READ;
>        if (host->flags & SDHCI_REQ_USE_DMA)
> @@ -1131,6 +1170,23 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
>  #ifndef SDHCI_USE_LEDS_CLASS
>        sdhci_activate_led(host);
>  #endif
> +       /*
> +        * Since the block count for CMD23 has already been specified in the
> +        * Argument 2 register, we don't need CMD12 to stop multiple block
> +        * transfers.
> +        */
> +       if (sdhci_host_auto_cmd23_supported(host, mrq)) {
> +               if (mrq->stop) {
> +                       /*
> +                        * Save the stop command here to be used during
> +                        * error recovery
> +                        */
> +                       abort_cmd = mrq->data->stop;
> +                       mrq->data->stop = NULL;
> +                       mrq->stop = NULL;
> +               }
> +       }
> +
>        if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12) {
>                if (mrq->stop) {
>                        mrq->data->stop = NULL;
> @@ -1396,6 +1452,7 @@ static void sdhci_timeout_timer(unsigned long data)
>
>                if (host->data) {
>                        host->data->error = -ETIMEDOUT;
> +                       host->data->stop = abort_cmd;
>                        sdhci_finish_data(host);
>                } else {
>                        if (host->cmd)
> @@ -1534,9 +1591,10 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
>                host->data->error = -EIO;
>        }
>
> -       if (host->data->error)
> +       if (host->data->error) {
> +               host->data->stop = abort_cmd;
>                sdhci_finish_data(host);
> -       else {
> +       } else {
>                if (intmask & (SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL))
>                        sdhci_transfer_pio(host);
>
> @@ -1783,8 +1841,10 @@ int sdhci_add_host(struct sdhci_host *host)
>                host->flags &= ~SDHCI_USE_SDMA;
>        }
>
> -       if ((host->version >= SDHCI_SPEC_200) && (caps & SDHCI_CAN_DO_ADMA2))
> +       if ((host->version >= SDHCI_SPEC_200) && (caps & SDHCI_CAN_DO_ADMA2)) {
> +               host->flags &= ~SDHCI_USE_SDMA;

Here may have issue if controller use only support SDMA with
SDHCI_QUIRK_BROKEN_ADMA.
After this, both SDHCI_USE_SDMA and SDHCI_USE_ADMA are cleared.

>                host->flags |= SDHCI_USE_ADMA;
> +       }
>
>        if ((host->quirks & SDHCI_QUIRK_BROKEN_ADMA) &&
>                (host->flags & SDHCI_USE_ADMA)) {

> diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
> index 6e0969e..223762c 100644
> --- a/drivers/mmc/host/sdhci.h
> +++ b/drivers/mmc/host/sdhci.h
> @@ -25,6 +25,7 @@
>  */
>
>  #define SDHCI_DMA_ADDRESS      0x00
> +#define SDHCI_ARGUMENT2                SDHCI_DMA_ADDRESS
>
>  #define SDHCI_BLOCK_SIZE       0x04
>  #define  SDHCI_MAKE_BLKSZ(dma, blksz) (((dma & 0x7) << 12) | (blksz & 0xFFF))
> @@ -37,6 +38,7 @@
>  #define  SDHCI_TRNS_DMA                0x01
>  #define  SDHCI_TRNS_BLK_CNT_EN 0x02
>  #define  SDHCI_TRNS_ACMD12     0x04
> +#define  SDHCI_TRNS_AUTO_CMD23 0x08
>  #define  SDHCI_TRNS_READ       0x10
>  #define  SDHCI_TRNS_MULTI      0x20
>
> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
> index 8ce0827..22b0335 100644
> --- a/include/linux/mmc/card.h
> +++ b/include/linux/mmc/card.h
> @@ -58,9 +58,13 @@ struct mmc_ext_csd {
>
>  struct sd_scr {
>        unsigned char           sda_vsn;
> +       unsigned char           sda_spec3;
>        unsigned char           bus_widths;
>  #define SD_SCR_BUS_WIDTH_1     (1<<0)
>  #define SD_SCR_BUS_WIDTH_4     (1<<2)
> +       unsigned char           cmd_support;
> +#define SD_SCR_CMD20_SUPPORT   (1<<0)
> +#define SD_SCR_CMD23_SUPPORT   (1<<1)
>  };
>
>  struct sd_ssr {
> diff --git a/include/linux/mmc/sd.h b/include/linux/mmc/sd.h
> index 3fd85e0..178363b 100644
> --- a/include/linux/mmc/sd.h
> +++ b/include/linux/mmc/sd.h
> @@ -59,7 +59,7 @@
>
>  #define SCR_SPEC_VER_0         0       /* Implements system specification 1.0 - 1.01 */
>  #define SCR_SPEC_VER_1         1       /* Implements system specification 1.10 */
> -#define SCR_SPEC_VER_2         2       /* Implements system specification 2.00 */
> +#define SCR_SPEC_VER_2         2       /* Implements system specification 2.00 - 3.0x */
>
>  /*
>  * SD bus widths
> --
> 1.7.1
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 09/12] mmc: sd: add support for tuning during uhs initialization
  2011-02-15  9:35 ` [PATCH 09/12] mmc: sd: add support for tuning during uhs initialization Arindam Nath
@ 2011-02-18 10:50   ` zhangfei gao
  2011-02-18 11:33     ` Nath, Arindam
  0 siblings, 1 reply; 45+ messages in thread
From: zhangfei gao @ 2011-02-18 10:50 UTC (permalink / raw)
  To: Arindam Nath; +Cc: cjb, linux-mmc, henry.su, aaron.lu, anath.amd

On Tue, Feb 15, 2011 at 4:35 AM, Arindam Nath <arindam.nath@amd.com> wrote:
> Host Controller needs tuning during initialization to operate SDR50
> and SDR104 UHS-I cards. Whether SDR50 mode actually needs tuning is
> indicated by bit 45 of the Host Controller Capabilities register.
> A new command CMD19 has been defined in the Physical Layer spec v3.01
> to request the card to send tuning pattern.
>
> We enable Buffer Read Ready interrupt at the very begining of tuning
> procedure, because that is the only interrupt generated by the Host
> Controller during tuning. Since there is no actual data transfer
> between card and host memory, we don't set DMA Enable (bit 0) in the
> Transfer Mode register. The tuning block is sent by the card to the
> Host Controller using DAT lines, so we set Data Present Select (bit 5)
> in the Command register. The Host Controller is responsible for doing
> the verfication of tuning block sent by the card at the hardware level.
> After sending CMD19, we wait for Buffer Read Ready interrupt. In case
> we don't receive an interrupt after the specified timeout value, we
> fall back on fixed sampling clock by setting Execute Tuning (bit 6)
> and Sampling Clock Seletc (bit 7) of Host Control2 register to 0.
> Before exiting the tuning procedure, we disable Buffer Read Ready interrupt.
>
> Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> ---
>  drivers/mmc/core/sd.c     |    4 +
>  drivers/mmc/host/sdhci.c  |  142 ++++++++++++++++++++++++++++++++++++++++++++-
>  drivers/mmc/host/sdhci.h  |    3 +
>  include/linux/mmc/host.h  |    1 +
>  include/linux/mmc/mmc.h   |    1 +
>  include/linux/mmc/sdhci.h |    4 +
>  6 files changed, 153 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> index c52d427..3113cb6 100644
> --- a/drivers/mmc/core/sd.c
> +++ b/drivers/mmc/core/sd.c
> @@ -624,6 +624,10 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card)
>        /* Set current limit for the card */
>        err = sd_set_current_limit(card, status);
>
> +       /* SPI mode doesn't define CMD19 */
> +       if (!mmc_host_is_spi(card->host))
> +               card->host->ops->execute_tuning(card->host);
> +
>  out:
>        kfree(status);
>
> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> index fc2cba6..6f3f0dc 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -39,6 +39,8 @@
>  #define SDHCI_USE_LEDS_CLASS
>  #endif
>
> +#define MAX_TUNING_LOOP 40
> +
>  static unsigned int debug_quirks = 0;
>
>  static void sdhci_prepare_data(struct sdhci_host *, struct mmc_data *);
> @@ -872,7 +874,10 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host,
>
>        if (data->flags & MMC_DATA_READ)
>                mode |= SDHCI_TRNS_READ;
> -       if (host->flags & SDHCI_REQ_USE_DMA)
> +
> +       /* CMD19 requires DMA Enable to be set to 0 */
> +       if ((host->flags & SDHCI_REQ_USE_DMA) &&
> +          (host->cmd->opcode != MMC_SEND_TUNING_BLOCK))
>                mode |= SDHCI_TRNS_DMA;

Here may have no chance to execute, since sdhci_set_transfer_mode will
return directly if data is NULL.

>
>        sdhci_writew(host, mode, SDHCI_TRANSFER_MODE);
> @@ -988,7 +993,9 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
>                flags |= SDHCI_CMD_CRC;
>        if (cmd->flags & MMC_RSP_OPCODE)
>                flags |= SDHCI_CMD_INDEX;
> -       if (cmd->data)
> +
> +       /* CMD19 is special in that the Data Present Select should be set */
> +       if (cmd->data || (cmd->opcode == MMC_SEND_TUNING_BLOCK))
>                flags |= SDHCI_CMD_DATA;
>
>        sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND);
> @@ -1494,6 +1501,119 @@ static int sdhci_get_max_current_180(struct mmc_host *mmc)
>        return max_current_180;
>  }
>
> +static void sdhci_execute_tuning(struct mmc_host *mmc)
> +{
> +       struct sdhci_host *host;
> +       u16 ctrl;
> +       int tuning_loop_counter = MAX_TUNING_LOOP;
> +       unsigned long flags;
> +       unsigned long timeout;
> +
> +       host = mmc_priv(mmc);
> +
> +       spin_lock_irqsave(&host->lock, flags);
> +
> +       ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> +
> +       /*
> +        * Host Controller needs tuning only in case of SDR104 mode
> +        * and for SDR50 mode when Use Tuning for SDR50 is set in
> +        * Capabilities register.
> +        */
> +       if ((ctrl & SDHCI_CTRL_UHS_SDR104) ||
> +           ((ctrl & SDHCI_CTRL_UHS_SDR50) &&
> +           (host->flags & SDHCI_SDR50_NEEDS_TUNING)))
> +               ctrl |= SDHCI_CTRL_EXEC_TUNING;
> +       else {
> +               spin_unlock_irqrestore(&host->lock, flags);
> +               return;
> +       }
> +
> +       sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> +
> +       /*
> +        * As per the Host Controller spec v3.00, tuning command
> +        * generates Buffer Read Ready interrupt, so enable that.
> +        */
> +       sdhci_unmask_irqs(host, SDHCI_INT_DATA_AVAIL);
> +
> +       /*
> +        * Issue CMD19 repeatedly till Execute Tuning is set to 0 or the number
> +        * of loops reaches 40 times or a timeout of 150ms occurs.
> +        */
> +       timeout = 150;
> +       do {
> +               struct mmc_command cmd;
> +               struct mmc_request mrq;
> +
> +               if (!tuning_loop_counter && !timeout)
> +                       break;
> +
> +               memset(&cmd, 0, sizeof(struct mmc_command));
> +               cmd.opcode = MMC_SEND_TUNING_BLOCK;
> +               cmd.arg = 0;
> +               cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
> +
> +               memset(&cmd.resp, 0, sizeof(cmd.resp));
> +               cmd.retries = 0;
> +
> +               cmd.data = NULL;

Set cmd.data = null, then sdhci_set_transfer_mode will return.

> +               cmd.error = 0;
> +
> +               memset(&mrq, 0, sizeof(struct mmc_request));
> +               mrq.cmd = &cmd;
> +               host->mrq = &mrq;
> +               sdhci_send_command(host, &cmd);
> +
> +               host->cmd = NULL;
> +               host->mrq = NULL;
> +
> +               spin_unlock_irqrestore(&host->lock, flags);
> +
> +               /* Wait for Buffer Read Ready interrupt */
> +               wait_event_interruptible_timeout(host->buf_ready_int,
> +                                       (host->tuning_done == 1),
> +                                       msecs_to_jiffies(50));
> +               spin_lock_irqsave(&host->lock, flags);
> +
> +               if (!host->tuning_done) {
> +                       printk(KERN_INFO DRIVER_NAME ": Tuning procedure"
> +                               " failed, falling back to fixed sampling"
> +                               " clock\n");
> +                       ctrl &= ~SDHCI_CTRL_TUNED_CLK;
> +                       ctrl &= ~SDHCI_CTRL_EXEC_TUNING;
> +                       sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> +
> +                       goto out;
> +               }
> +
> +               host->tuning_done = 0;
> +
> +               ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> +               tuning_loop_counter--;
> +               timeout--;
> +               mdelay(1);
> +       } while (ctrl & SDHCI_CTRL_EXEC_TUNING);
> +
> +       /*
> +        * The Host Driver has exhausted the maximum number of loops allowed,
> +        * so use fixed sampling frequency.
> +        */
> +       if (!tuning_loop_counter || !timeout) {
> +               ctrl &= ~SDHCI_CTRL_TUNED_CLK;
> +               sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> +       } else {
> +               if (!(ctrl & SDHCI_CTRL_TUNED_CLK))
> +                       printk(KERN_INFO DRIVER_NAME ": Tuning procedure"
> +                               " failed, falling back to fixed sampling"
> +                               " clock\n");
> +       }
> +
> +out:
> +       sdhci_mask_irqs(host, SDHCI_INT_DATA_AVAIL);
> +       spin_unlock_irqrestore(&host->lock, flags);
> +}
> +
>  static const struct mmc_host_ops sdhci_ops = {
>        .request        = sdhci_request,
>        .set_ios        = sdhci_set_ios,
> @@ -1501,6 +1621,7 @@ static const struct mmc_host_ops sdhci_ops = {
>        .enable_sdio_irq = sdhci_enable_sdio_irq,
>        .start_signal_voltage_switch    = sdhci_start_signal_voltage_switch,
>        .get_max_current_180            = sdhci_get_max_current_180,
> +       .execute_tuning                 = sdhci_execute_tuning,
>  };
>
>  /*****************************************************************************\
> @@ -1712,6 +1833,16 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
>  {
>        BUG_ON(intmask == 0);
>
> +       /* CMD19 generates _only_ Buffer Read Ready interrupt */
> +       if (intmask & SDHCI_INT_DATA_AVAIL) {
> +               if (SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND))
> +                               == MMC_SEND_TUNING_BLOCK) {
> +                       host->tuning_done = 1;
> +                       wake_up(&host->buf_ready_int);
> +                       return;
> +               }
> +       }
> +
>        if (!host->data) {
>                /*
>                 * The "data complete" interrupt is also used to
> @@ -2126,6 +2257,10 @@ int sdhci_add_host(struct sdhci_host *host)
>        if (caps[1] & SDHCI_SUPPORT_DDR50)
>                mmc->caps |= MMC_CAP_UHS_DDR50;
>
> +       /* Does the host needs tuning for SDR50? */
> +       if (caps[1] & SDHCI_USE_SDR50_TUNING)
> +               host->flags |= SDHCI_SDR50_NEEDS_TUNING;
> +
>        /* Driver Type(s) (A, C, D) supported by the host */
>        if (caps[1] & SDHCI_DRIVER_TYPE_A)
>                mmc->caps |= MMC_CAP_DRIVER_TYPE_A;
> @@ -2269,6 +2404,9 @@ int sdhci_add_host(struct sdhci_host *host)
>
>        setup_timer(&host->timer, sdhci_timeout_timer, (unsigned long)host);
>
> +       if (host->version >= SDHCI_SPEC_300)
> +               init_waitqueue_head(&host->buf_ready_int);
> +
>        ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED,
>                mmc_hostname(mmc), host);
>        if (ret)
> diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
> index 5bf244d..4746879 100644
> --- a/drivers/mmc/host/sdhci.h
> +++ b/drivers/mmc/host/sdhci.h
> @@ -160,6 +160,8 @@
>  #define  SDHCI_CTRL_DRV_TYPE_A         0x0010
>  #define  SDHCI_CTRL_DRV_TYPE_C         0x0020
>  #define  SDHCI_CTRL_DRV_TYPE_D         0x0030
> +#define  SDHCI_CTRL_EXEC_TUNING                0x0040
> +#define  SDHCI_CTRL_TUNED_CLK          0x0080
>  #define  SDHCI_CTRL_PRESET_VAL_ENABLE  0x8000
>
>  #define SDHCI_CAPABILITIES     0x40
> @@ -187,6 +189,7 @@
>  #define  SDHCI_DRIVER_TYPE_A   0x00000010
>  #define  SDHCI_DRIVER_TYPE_C   0x00000020
>  #define  SDHCI_DRIVER_TYPE_D   0x00000040
> +#define  SDHCI_USE_SDR50_TUNING        0x00002000
>
>  #define SDHCI_CAPABILITIES_1   0x44
>
> diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
> index 65a003d..4abb964 100644
> --- a/include/linux/mmc/host.h
> +++ b/include/linux/mmc/host.h
> @@ -129,6 +129,7 @@ struct mmc_host_ops {
>
>        int     (*start_signal_voltage_switch)(struct mmc_host *host);
>        int     (*get_max_current_180)(struct mmc_host *mmc);
> +       void    (*execute_tuning)(struct mmc_host *host);
>  };
>
>  struct mmc_card;
> diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
> index 612301f..9194f9e 100644
> --- a/include/linux/mmc/mmc.h
> +++ b/include/linux/mmc/mmc.h
> @@ -50,6 +50,7 @@
>  #define MMC_SET_BLOCKLEN         16   /* ac   [31:0] block len   R1  */
>  #define MMC_READ_SINGLE_BLOCK    17   /* adtc [31:0] data addr   R1  */
>  #define MMC_READ_MULTIPLE_BLOCK  18   /* adtc [31:0] data addr   R1  */
> +#define MMC_SEND_TUNING_BLOCK    19   /* adtc                    R1  */
>
>   /* class 3 */
>  #define MMC_WRITE_DAT_UNTIL_STOP 20   /* adtc [31:0] data addr   R1  */
> diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h
> index 83bd9f7..26b6278 100644
> --- a/include/linux/mmc/sdhci.h
> +++ b/include/linux/mmc/sdhci.h
> @@ -109,6 +109,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_SDR50_NEEDS_TUNING (1<<4)        /* SDR50 needs tuning */
>
>        unsigned int version;   /* SDHCI spec. version */
>
> @@ -145,6 +146,9 @@ struct sdhci_host {
>        unsigned int            ocr_avail_sd;
>        unsigned int            ocr_avail_mmc;
>
> +       wait_queue_head_t       buf_ready_int;  /* Waitqueue for Buffer Read Ready interrupt */
> +       unsigned int            tuning_done;    /* Condition flag set when CMD19 succeeds */
> +
>        unsigned long private[0] ____cacheline_aligned;
>  };
>  #endif /* __SDHCI_H */
> --
> 1.7.1
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 01/12] mmc: sdhci: add support for auto CMD23
  2011-02-18 10:45   ` zhangfei gao
@ 2011-02-18 10:50     ` Arnd Bergmann
  2011-02-18 12:08       ` Nath, Arindam
  2011-02-18 11:53     ` [PATCH 01/12] mmc: sdhci: add support for auto CMD23 Nath, Arindam
  1 sibling, 1 reply; 45+ messages in thread
From: Arnd Bergmann @ 2011-02-18 10:50 UTC (permalink / raw)
  To: zhangfei gao; +Cc: Arindam Nath, cjb, linux-mmc, henry.su, aaron.lu, anath.amd

On Friday 18 February 2011 11:45:27 zhangfei gao wrote:
> On Tue, Feb 15, 2011 at 4:34 AM, Arindam Nath <arindam.nath@amd.com> wrote:
> > Host Controller v3.00 and later support Auto CMD23 in the Transfer
> > Mode register. Since Auto CMD23 can be used with or without DMA,
> > and if used with DMA, it should only be ADMA, we check against
> > SDHCI_USE_SDMA not being set. This flag is reset when SDHCI_USE_ADMA
> > is set.
> >
> > A new definition for SDHCI_ARGUMENT2 register has been added
> > in v3.00 spec, which is the same as SDHCI_DMA_ADDRESS. We program the
> > block count for CMD23 in SDHCI_ARGUMENT2, so we don't need CMD12 to
> > stop multiple block transfers. But during error recovery procedure,
> > we will need to send Abort command, so we use a global variable
> > abort_cmd to save the stop command to be used later.
> >
> > Two bits are added to SCR register as per the Physical Layer Spec v3.01,
> > which specify whether the card supports CMD20 and/or CMD23. We use this
> > as one of the conditions to decide whether to enable Auto CMD23 or not.
> >
> > Signed-off-by: Arindam Nath <arindam.nath@amd.com>

Why are you doing this in the SDHC code and not in the block driver code?

AFAICT, whether CMD23 can be used or not is primarily a decision based on
the card, and the block driver might want issue it for a longer range
if it needs to split a block request into multiple MMC requests.

You you add another flag to the host capabilities instead that tells
the lower driver if CMD23 is supported, and then issue it from there?

	Arnd

^ permalink raw reply	[flat|nested] 45+ messages in thread

* RE: [PATCH 09/12] mmc: sd: add support for tuning during uhs initialization
  2011-02-18 10:50   ` zhangfei gao
@ 2011-02-18 11:33     ` Nath, Arindam
  0 siblings, 0 replies; 45+ messages in thread
From: Nath, Arindam @ 2011-02-18 11:33 UTC (permalink / raw)
  To: zhangfei gao; +Cc: cjb, linux-mmc, Su, Henry, Lu, Aaron, anath.amd

Hi Zhangfei,

> -----Original Message-----
> From: zhangfei gao [mailto:zhangfei.gao@gmail.com]
> Sent: Friday, February 18, 2011 4:20 PM
> To: Nath, Arindam
> Cc: cjb@laptop.org; linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
> anath.amd@gmail.com
> Subject: Re: [PATCH 09/12] mmc: sd: add support for tuning during uhs
> initialization
> 
> On Tue, Feb 15, 2011 at 4:35 AM, Arindam Nath <arindam.nath@amd.com>
> wrote:
> > Host Controller needs tuning during initialization to operate SDR50
> > and SDR104 UHS-I cards. Whether SDR50 mode actually needs tuning is
> > indicated by bit 45 of the Host Controller Capabilities register.
> > A new command CMD19 has been defined in the Physical Layer spec v3.01
> > to request the card to send tuning pattern.
> >
> > We enable Buffer Read Ready interrupt at the very begining of tuning
> > procedure, because that is the only interrupt generated by the Host
> > Controller during tuning. Since there is no actual data transfer
> > between card and host memory, we don't set DMA Enable (bit 0) in the
> > Transfer Mode register. The tuning block is sent by the card to the
> > Host Controller using DAT lines, so we set Data Present Select (bit
> 5)
> > in the Command register. The Host Controller is responsible for doing
> > the verfication of tuning block sent by the card at the hardware
> level.
> > After sending CMD19, we wait for Buffer Read Ready interrupt. In case
> > we don't receive an interrupt after the specified timeout value, we
> > fall back on fixed sampling clock by setting Execute Tuning (bit 6)
> > and Sampling Clock Seletc (bit 7) of Host Control2 register to 0.
> > Before exiting the tuning procedure, we disable Buffer Read Ready
> interrupt.
> >
> > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > ---
> >  drivers/mmc/core/sd.c     |    4 +
> >  drivers/mmc/host/sdhci.c  |  142
> ++++++++++++++++++++++++++++++++++++++++++++-
> >  drivers/mmc/host/sdhci.h  |    3 +
> >  include/linux/mmc/host.h  |    1 +
> >  include/linux/mmc/mmc.h   |    1 +
> >  include/linux/mmc/sdhci.h |    4 +
> >  6 files changed, 153 insertions(+), 2 deletions(-)
> >
> > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> > index c52d427..3113cb6 100644
> > --- a/drivers/mmc/core/sd.c
> > +++ b/drivers/mmc/core/sd.c
> > @@ -624,6 +624,10 @@ static int mmc_sd_init_uhs_card(struct mmc_card
> *card)
> >        /* Set current limit for the card */
> >        err = sd_set_current_limit(card, status);
> >
> > +       /* SPI mode doesn't define CMD19 */
> > +       if (!mmc_host_is_spi(card->host))
> > +               card->host->ops->execute_tuning(card->host);
> > +
> >  out:
> >        kfree(status);
> >
> > diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> > index fc2cba6..6f3f0dc 100644
> > --- a/drivers/mmc/host/sdhci.c
> > +++ b/drivers/mmc/host/sdhci.c
> > @@ -39,6 +39,8 @@
> >  #define SDHCI_USE_LEDS_CLASS
> >  #endif
> >
> > +#define MAX_TUNING_LOOP 40
> > +
> >  static unsigned int debug_quirks = 0;
> >
> >  static void sdhci_prepare_data(struct sdhci_host *, struct mmc_data
> *);
> > @@ -872,7 +874,10 @@ static void sdhci_set_transfer_mode(struct
> sdhci_host *host,
> >
> >        if (data->flags & MMC_DATA_READ)
> >                mode |= SDHCI_TRNS_READ;
> > -       if (host->flags & SDHCI_REQ_USE_DMA)
> > +
> > +       /* CMD19 requires DMA Enable to be set to 0 */
> > +       if ((host->flags & SDHCI_REQ_USE_DMA) &&
> > +          (host->cmd->opcode != MMC_SEND_TUNING_BLOCK))
> >                mode |= SDHCI_TRNS_DMA;
> 
> Here may have no chance to execute, since sdhci_set_transfer_mode will
> return directly if data is NULL.

Thanks. Since we are setting data to NULL inside sdhci_execute_tuning() below, this condition check becomes redundant. I will remove this check in V2 of the patch.

> 
> >
> >        sdhci_writew(host, mode, SDHCI_TRANSFER_MODE);
> > @@ -988,7 +993,9 @@ static void sdhci_send_command(struct sdhci_host
> *host, struct mmc_command *cmd)
> >                flags |= SDHCI_CMD_CRC;
> >        if (cmd->flags & MMC_RSP_OPCODE)
> >                flags |= SDHCI_CMD_INDEX;
> > -       if (cmd->data)
> > +
> > +       /* CMD19 is special in that the Data Present Select should be
> set */
> > +       if (cmd->data || (cmd->opcode == MMC_SEND_TUNING_BLOCK))
> >                flags |= SDHCI_CMD_DATA;
> >
> >        sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags),
> SDHCI_COMMAND);
> > @@ -1494,6 +1501,119 @@ static int sdhci_get_max_current_180(struct
> mmc_host *mmc)
> >        return max_current_180;
> >  }
> >
> > +static void sdhci_execute_tuning(struct mmc_host *mmc)
> > +{
> > +       struct sdhci_host *host;
> > +       u16 ctrl;
> > +       int tuning_loop_counter = MAX_TUNING_LOOP;
> > +       unsigned long flags;
> > +       unsigned long timeout;
> > +
> > +       host = mmc_priv(mmc);
> > +
> > +       spin_lock_irqsave(&host->lock, flags);
> > +
> > +       ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> > +
> > +       /*
> > +        * Host Controller needs tuning only in case of SDR104 mode
> > +        * and for SDR50 mode when Use Tuning for SDR50 is set in
> > +        * Capabilities register.
> > +        */
> > +       if ((ctrl & SDHCI_CTRL_UHS_SDR104) ||
> > +           ((ctrl & SDHCI_CTRL_UHS_SDR50) &&
> > +           (host->flags & SDHCI_SDR50_NEEDS_TUNING)))
> > +               ctrl |= SDHCI_CTRL_EXEC_TUNING;
> > +       else {
> > +               spin_unlock_irqrestore(&host->lock, flags);
> > +               return;
> > +       }
> > +
> > +       sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> > +
> > +       /*
> > +        * As per the Host Controller spec v3.00, tuning command
> > +        * generates Buffer Read Ready interrupt, so enable that.
> > +        */
> > +       sdhci_unmask_irqs(host, SDHCI_INT_DATA_AVAIL);
> > +
> > +       /*
> > +        * Issue CMD19 repeatedly till Execute Tuning is set to 0 or
> the number
> > +        * of loops reaches 40 times or a timeout of 150ms occurs.
> > +        */
> > +       timeout = 150;
> > +       do {
> > +               struct mmc_command cmd;
> > +               struct mmc_request mrq;
> > +
> > +               if (!tuning_loop_counter && !timeout)
> > +                       break;
> > +
> > +               memset(&cmd, 0, sizeof(struct mmc_command));
> > +               cmd.opcode = MMC_SEND_TUNING_BLOCK;
> > +               cmd.arg = 0;
> > +               cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
> > +
> > +               memset(&cmd.resp, 0, sizeof(cmd.resp));
> > +               cmd.retries = 0;
> > +
> > +               cmd.data = NULL;
> 
> Set cmd.data = null, then sdhci_set_transfer_mode will return.

Thanks. Will take care of that.

Regards,
Arindam


^ permalink raw reply	[flat|nested] 45+ messages in thread

* RE: [PATCH 01/12] mmc: sdhci: add support for auto CMD23
  2011-02-18 10:45   ` zhangfei gao
  2011-02-18 10:50     ` Arnd Bergmann
@ 2011-02-18 11:53     ` Nath, Arindam
  1 sibling, 0 replies; 45+ messages in thread
From: Nath, Arindam @ 2011-02-18 11:53 UTC (permalink / raw)
  To: zhangfei gao; +Cc: cjb, linux-mmc, Su, Henry, Lu, Aaron, anath.amd

Hi Zhangfei,

> -----Original Message-----
> From: zhangfei gao [mailto:zhangfei.gao@gmail.com]
> Sent: Friday, February 18, 2011 4:15 PM
> To: Nath, Arindam
> Cc: cjb@laptop.org; linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
> anath.amd@gmail.com
> Subject: Re: [PATCH 01/12] mmc: sdhci: add support for auto CMD23
> 
> On Tue, Feb 15, 2011 at 4:34 AM, Arindam Nath <arindam.nath@amd.com>
> wrote:
> > Host Controller v3.00 and later support Auto CMD23 in the Transfer
> > Mode register. Since Auto CMD23 can be used with or without DMA,
> > and if used with DMA, it should _only_ be ADMA, we check against
> > SDHCI_USE_SDMA not being set. This flag is reset when SDHCI_USE_ADMA
> > is set.
> >
> > A new definition for SDHCI_ARGUMENT2 register has been added
> > in v3.00 spec, which is the same as SDHCI_DMA_ADDRESS. We program the
> > block count for CMD23 in SDHCI_ARGUMENT2, so we don't need CMD12 to
> > stop multiple block transfers. But during error recovery procedure,
> > we will need to send Abort command, so we use a global variable
> > abort_cmd to save the stop command to be used later.
> >
> > Two bits are added to SCR register as per the Physical Layer Spec
> v3.01,
> > which specify whether the card supports CMD20 and/or CMD23. We use
> this
> > as one of the conditions to decide whether to enable Auto CMD23 or
> not.
> >
> > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > ---
> >  drivers/mmc/core/sd.c    |    6 ++++
> >  drivers/mmc/host/sdhci.c |   66
> +++++++++++++++++++++++++++++++++++++++++++--
> >  drivers/mmc/host/sdhci.h |    2 +
> >  include/linux/mmc/card.h |    4 +++
> >  include/linux/mmc/sd.h   |    2 +-
> >  5 files changed, 76 insertions(+), 4 deletions(-)
> >
> > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> > index d18c32b..b3f8a3c 100644
> > --- a/drivers/mmc/core/sd.c
> > +++ b/drivers/mmc/core/sd.c
> > @@ -188,6 +188,12 @@ static int mmc_decode_scr(struct mmc_card *card)
> >
> >        scr->sda_vsn = UNSTUFF_BITS(resp, 56, 4);
> >        scr->bus_widths = UNSTUFF_BITS(resp, 48, 4);
> > +       if (scr->sda_vsn == SCR_SPEC_VER_2) {
> > +               /* Check if Physical Layer Spec v3.0 is supported*/
> > +               scr->sda_spec3 = UNSTUFF_BITS(resp, 47, 1);
> > +               if (scr->sda_spec3)
> > +                       scr->cmd_support = UNSTUFF_BITS(resp, 32, 2);
> > +       }
> >
> >        if (UNSTUFF_BITS(resp, 55, 1))
> >                card->erased_byte = 0xFF;
> > diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> > index 9e15f41..0b537cf 100644
> > --- a/drivers/mmc/host/sdhci.c
> > +++ b/drivers/mmc/host/sdhci.c
> > @@ -25,6 +25,7 @@
> >
> >  #include <linux/mmc/mmc.h>
> >  #include <linux/mmc/host.h>
> > +#include <linux/mmc/card.h>
> >
> >  #include "sdhci.h"
> >
> > @@ -46,6 +47,8 @@ static void sdhci_finish_data(struct sdhci_host *);
> >  static void sdhci_send_command(struct sdhci_host *, struct
> mmc_command *);
> >  static void sdhci_finish_command(struct sdhci_host *);
> >
> > +static struct mmc_command *abort_cmd;
> > +
> >  static void sdhci_dumpregs(struct sdhci_host *host)
> >  {
> >        printk(KERN_DEBUG DRIVER_NAME ": =========== REGISTER DUMP
> (%s)===========\n",
> > @@ -812,6 +815,30 @@ static void sdhci_prepare_data(struct sdhci_host
> *host, struct mmc_data *data)
> >        sdhci_writew(host, data->blocks, SDHCI_BLOCK_COUNT);
> >  }
> >
> > +/*
> > + * Does the Host Controller support Auto CMD23?
> > + *
> > + * There are four preconditions for Auto CMD23 to be supported:
> > + *  1. Host Controller v3.00 or later
> > + *  2. Card supports CMD23
> > + *  3. If DMA is used, it should be ADMA
> > + *  4. Only when CMD18 or CMD25 is issued
> > + */
> > +static int sdhci_host_auto_cmd23_supported(struct sdhci_host *host,
> > +       struct mmc_request *mrq)
> > +{
> > +       struct mmc_card *card = host->mmc->card;
> > +
> > +       if ((host->version >= SDHCI_SPEC_300) &&
> > +          card && (card->scr.cmd_support & SD_SCR_CMD23_SUPPORT) &&
> > +          !(host->flags & SDHCI_USE_SDMA) &&
> > +          ((mrq->cmd->opcode == MMC_READ_MULTIPLE_BLOCK) ||
> > +          (mrq->cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK)))
> > +               return 1;
> > +
> > +       return 0;
> > +}
> > +
> >  static void sdhci_set_transfer_mode(struct sdhci_host *host,
> >        struct mmc_data *data)
> >  {
> > @@ -824,11 +851,23 @@ static void sdhci_set_transfer_mode(struct
> sdhci_host *host,
> >
> >        mode = SDHCI_TRNS_BLK_CNT_EN;
> >        if (data->blocks > 1) {
> > +               if (sdhci_host_auto_cmd23_supported(host, host->mrq))
> {
> > +                       /*
> > +                        * Host Controller v3.00 can automatically
> send CMD23
> > +                        * before CMD18 or CMD25. For that, we need
> to enable
> > +                        * Auto CMD23 in the Transfer Mode register
> and
> > +                        * program the block count in Argument 2
> register.
> > +                        */
> > +                       mode |= SDHCI_TRNS_AUTO_CMD23;
> > +                       sdhci_writel(host, data->blocks,
> SDHCI_ARGUMENT2);
> > +               }
> > +
> >                if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12)
> >                        mode |= SDHCI_TRNS_MULTI | SDHCI_TRNS_ACMD12;
> >                else
> >                        mode |= SDHCI_TRNS_MULTI;
> >        }
> > +
> >        if (data->flags & MMC_DATA_READ)
> >                mode |= SDHCI_TRNS_READ;
> >        if (host->flags & SDHCI_REQ_USE_DMA)
> > @@ -1131,6 +1170,23 @@ static void sdhci_request(struct mmc_host
> *mmc, struct mmc_request *mrq)
> >  #ifndef SDHCI_USE_LEDS_CLASS
> >        sdhci_activate_led(host);
> >  #endif
> > +       /*
> > +        * Since the block count for CMD23 has already been specified
> in the
> > +        * Argument 2 register, we don't need CMD12 to stop multiple
> block
> > +        * transfers.
> > +        */
> > +       if (sdhci_host_auto_cmd23_supported(host, mrq)) {
> > +               if (mrq->stop) {
> > +                       /*
> > +                        * Save the stop command here to be used
> during
> > +                        * error recovery
> > +                        */
> > +                       abort_cmd = mrq->data->stop;
> > +                       mrq->data->stop = NULL;
> > +                       mrq->stop = NULL;
> > +               }
> > +       }
> > +
> >        if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12) {
> >                if (mrq->stop) {
> >                        mrq->data->stop = NULL;
> > @@ -1396,6 +1452,7 @@ static void sdhci_timeout_timer(unsigned long
> data)
> >
> >                if (host->data) {
> >                        host->data->error = -ETIMEDOUT;
> > +                       host->data->stop = abort_cmd;
> >                        sdhci_finish_data(host);
> >                } else {
> >                        if (host->cmd)
> > @@ -1534,9 +1591,10 @@ static void sdhci_data_irq(struct sdhci_host
> *host, u32 intmask)
> >                host->data->error = -EIO;
> >        }
> >
> > -       if (host->data->error)
> > +       if (host->data->error) {
> > +               host->data->stop = abort_cmd;
> >                sdhci_finish_data(host);
> > -       else {
> > +       } else {
> >                if (intmask & (SDHCI_INT_DATA_AVAIL |
> SDHCI_INT_SPACE_AVAIL))
> >                        sdhci_transfer_pio(host);
> >
> > @@ -1783,8 +1841,10 @@ int sdhci_add_host(struct sdhci_host *host)
> >                host->flags &= ~SDHCI_USE_SDMA;
> >        }
> >
> > -       if ((host->version >= SDHCI_SPEC_200) && (caps &
> SDHCI_CAN_DO_ADMA2))
> > +       if ((host->version >= SDHCI_SPEC_200) && (caps &
> SDHCI_CAN_DO_ADMA2)) {
> > +               host->flags &= ~SDHCI_USE_SDMA;
> 
> Here may have issue if controller use only support SDMA with
> SDHCI_QUIRK_BROKEN_ADMA.
> After this, both SDHCI_USE_SDMA and SDHCI_USE_ADMA are cleared.

Thanks for the careful review. I will fix it in V2 of the patchset.

Regards,
Arindam


^ permalink raw reply	[flat|nested] 45+ messages in thread

* RE: [PATCH 01/12] mmc: sdhci: add support for auto CMD23
  2011-02-18 10:50     ` Arnd Bergmann
@ 2011-02-18 12:08       ` Nath, Arindam
  2011-02-18 13:08         ` Arnd Bergmann
  0 siblings, 1 reply; 45+ messages in thread
From: Nath, Arindam @ 2011-02-18 12:08 UTC (permalink / raw)
  To: Arnd Bergmann, zhangfei gao
  Cc: cjb, linux-mmc, Su, Henry, Lu, Aaron, anath.amd

Hi Arnd,

> -----Original Message-----
> From: Arnd Bergmann [mailto:arnd@arndb.de]
> Sent: Friday, February 18, 2011 4:21 PM
> To: zhangfei gao
> Cc: Nath, Arindam; cjb@laptop.org; linux-mmc@vger.kernel.org; Su,
> Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: Re: [PATCH 01/12] mmc: sdhci: add support for auto CMD23
> 
> On Friday 18 February 2011 11:45:27 zhangfei gao wrote:
> > On Tue, Feb 15, 2011 at 4:34 AM, Arindam Nath <arindam.nath@amd.com>
> wrote:
> > > Host Controller v3.00 and later support Auto CMD23 in the Transfer
> > > Mode register. Since Auto CMD23 can be used with or without DMA,
> > > and if used with DMA, it should only be ADMA, we check against
> > > SDHCI_USE_SDMA not being set. This flag is reset when
> SDHCI_USE_ADMA
> > > is set.
> > >
> > > A new definition for SDHCI_ARGUMENT2 register has been added
> > > in v3.00 spec, which is the same as SDHCI_DMA_ADDRESS. We program
> the
> > > block count for CMD23 in SDHCI_ARGUMENT2, so we don't need CMD12 to
> > > stop multiple block transfers. But during error recovery procedure,
> > > we will need to send Abort command, so we use a global variable
> > > abort_cmd to save the stop command to be used later.
> > >
> > > Two bits are added to SCR register as per the Physical Layer Spec
> v3.01,
> > > which specify whether the card supports CMD20 and/or CMD23. We use
> this
> > > as one of the conditions to decide whether to enable Auto CMD23 or
> not.
> > >
> > > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> 
> Why are you doing this in the SDHC code and not in the block driver
> code?
> 
> AFAICT, whether CMD23 can be used or not is primarily a decision based
> on
> the card, and the block driver might want issue it for a longer range
> if it needs to split a block request into multiple MMC requests.

Thanks for the comments. Yes, whether a card supports CMD23 depends on the card, but Auto CMD23 is a feature added to the Host Controller v3.00 just like Auto CMD12. I am already doing a check inside sdhci_host_auto_cmd23_supported() whether the card supports CMD23, and whether the request is for multiple block transfers. Also I tried to make my implementation consistent with Auto CMD12 implementation already there in the sdhc code.

Regards,
Arindam


^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 01/12] mmc: sdhci: add support for auto CMD23
  2011-02-18 12:08       ` Nath, Arindam
@ 2011-02-18 13:08         ` Arnd Bergmann
  2011-02-18 13:35           ` Nath, Arindam
  0 siblings, 1 reply; 45+ messages in thread
From: Arnd Bergmann @ 2011-02-18 13:08 UTC (permalink / raw)
  To: Nath, Arindam
  Cc: zhangfei gao, cjb, linux-mmc, Su, Henry, Lu, Aaron, anath.amd

On Friday 18 February 2011, Nath, Arindam wrote:
> > AFAICT, whether CMD23 can be used or not is primarily a decision based
> > on
> > the card, and the block driver might want issue it for a longer range
> > if it needs to split a block request into multiple MMC requests.
> 
> Thanks for the comments. Yes, whether a card supports CMD23 depends on
> the card, but Auto CMD23 is a feature added to the Host Controller v3.00
> just like Auto CMD12. I am already doing a check inside 
> sdhci_host_auto_cmd23_supported() whether the card supports CMD23, and
> whether the request is for multiple block transfers. Also I tried to make
> my implementation consistent with Auto CMD12 implementation already
> there in the sdhc code.

Ok, I see.

But, assuming that the block driver was changed to always to CMD23 where
appropriate, what is the advantage auf Auto CMD23?

As far as I can tell, changing the block driver would make all hosts
faster, not just sdhci, and it would be able to cover the split requests
I mentioned.

	Arnd


^ permalink raw reply	[flat|nested] 45+ messages in thread

* RE: [PATCH 01/12] mmc: sdhci: add support for auto CMD23
  2011-02-18 13:08         ` Arnd Bergmann
@ 2011-02-18 13:35           ` Nath, Arindam
  2011-02-18 15:40             ` Arnd Bergmann
  0 siblings, 1 reply; 45+ messages in thread
From: Nath, Arindam @ 2011-02-18 13:35 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: zhangfei gao, cjb, linux-mmc, Su, Henry, Lu, Aaron, anath.amd

Hi Arnd,

> -----Original Message-----
> From: Arnd Bergmann [mailto:arnd@arndb.de]
> Sent: Friday, February 18, 2011 6:39 PM
> To: Nath, Arindam
> Cc: zhangfei gao; cjb@laptop.org; linux-mmc@vger.kernel.org; Su, Henry;
> Lu, Aaron; anath.amd@gmail.com
> Subject: Re: [PATCH 01/12] mmc: sdhci: add support for auto CMD23
> 
> On Friday 18 February 2011, Nath, Arindam wrote:
> > > AFAICT, whether CMD23 can be used or not is primarily a decision
> based
> > > on
> > > the card, and the block driver might want issue it for a longer
> range
> > > if it needs to split a block request into multiple MMC requests.
> >
> > Thanks for the comments. Yes, whether a card supports CMD23 depends
> on
> > the card, but Auto CMD23 is a feature added to the Host Controller
> v3.00
> > just like Auto CMD12. I am already doing a check inside
> > sdhci_host_auto_cmd23_supported() whether the card supports CMD23,
> and
> > whether the request is for multiple block transfers. Also I tried to
> make
> > my implementation consistent with Auto CMD12 implementation already
> > there in the sdhc code.
> 
> Ok, I see.
> 
> But, assuming that the block driver was changed to always to CMD23
> where
> appropriate, what is the advantage auf Auto CMD23?

Quoting verbatim from the Host Controller Spec v3.00, section 1.15 --

Auto CMD23 is a feature that automatically issues a CMD23 before a CMD18 or CMD25 is sent. Objective of this function is to avoid performance deterioration during memory access by removing interrupt service of CMD23.

Regards,
Arindam


^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 01/12] mmc: sdhci: add support for auto CMD23
  2011-02-18 13:35           ` Nath, Arindam
@ 2011-02-18 15:40             ` Arnd Bergmann
  2011-02-18 15:49               ` [PATCH 01/12] mmc: sdhci: add support for SD Host Controller 3.0 Philip Rakity
  0 siblings, 1 reply; 45+ messages in thread
From: Arnd Bergmann @ 2011-02-18 15:40 UTC (permalink / raw)
  To: Nath, Arindam
  Cc: zhangfei gao, cjb, linux-mmc, Su, Henry, Lu, Aaron, anath.amd

On Friday 18 February 2011, Nath, Arindam wrote:
> Auto CMD23 is a feature that automatically issues a CMD23 before
> a CMD18 or CMD25 is sent. Objective of this function is to avoid
> performance deterioration during memory access by removing interrupt
> service of CMD23.

I feel a bit stupid now, I confused CMD23 and ACMD23. I thought that
this was about automatic insertion of ACMD23, which it is clearly
not. I still think we should do *ACMD23* in the block driver,
but see no reason for CMD23.

	Arnd

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH 01/12] mmc: sdhci: add support for SD Host Controller 3.0
  2011-02-18 15:40             ` Arnd Bergmann
@ 2011-02-18 15:49               ` Philip Rakity
  2011-02-18 17:17                 ` Nath, Arindam
  0 siblings, 1 reply; 45+ messages in thread
From: Philip Rakity @ 2011-02-18 15:49 UTC (permalink / raw)
  To: Arindam Nath
  Cc: Zhangfei Gao, Chris Ball, linux-mmc, Su, Henry, Lu, Aaron,
	anath.amd, Nicolas Pitre


Arindam,

Could you look at making (Tuning)  CMD19 part of the sdhc.ci driver?   It flow is part of the Host Controller and Physical layer specs.

I am not sure that it belongs in core/ as it is transparent to the other functions and is unclear why it needs to be exposed

adding Nico for comments

PHilip

^ permalink raw reply	[flat|nested] 45+ messages in thread

* RE: [PATCH 01/12] mmc: sdhci: add support for SD Host Controller 3.0
  2011-02-18 15:49               ` [PATCH 01/12] mmc: sdhci: add support for SD Host Controller 3.0 Philip Rakity
@ 2011-02-18 17:17                 ` Nath, Arindam
  2011-02-18 18:55                   ` Nicolas Pitre
  2011-02-18 19:06                   ` Philip Rakity
  0 siblings, 2 replies; 45+ messages in thread
From: Nath, Arindam @ 2011-02-18 17:17 UTC (permalink / raw)
  To: Philip Rakity
  Cc: Zhangfei Gao, Chris Ball, linux-mmc, Su, Henry, Lu, Aaron,
	anath.amd, Nicolas Pitre

Hi Philip,


> -----Original Message-----
> From: Philip Rakity [mailto:prakity@marvell.com]
> Sent: Friday, February 18, 2011 9:19 PM
> To: Nath, Arindam
> Cc: Zhangfei Gao; Chris Ball; linux-mmc@vger.kernel.org; Su, Henry; Lu,
> Aaron; anath.amd@gmail.com; Nicolas Pitre
> Subject: [PATCH 01/12] mmc: sdhci: add support for SD Host Controller
> 3.0
> 
> 
> Arindam,
> 
> Could you look at making (Tuning)  CMD19 part of the sdhc.ci driver?
> It flow is part of the Host Controller and Physical layer specs.

There are two main reasons for exposing the callback of tuning procedure from sdhci.c:

1. The tuning procedure is part of UHS-I initialization sequence as described in Physical Layer Spec v3.01. Since the UHS-I init sequence takes places inside sd.c, so I thought it better to place it there instead.
2. In future, if we need to add support for UHS-I sequence for sdio cards too, we can use the same callback from sdhci.c without any modifications.

I am open to suggestions.

Regards,
Arindam


^ permalink raw reply	[flat|nested] 45+ messages in thread

* RE: [PATCH 01/12] mmc: sdhci: add support for SD Host Controller 3.0
  2011-02-18 17:17                 ` Nath, Arindam
@ 2011-02-18 18:55                   ` Nicolas Pitre
  2011-02-18 19:08                     ` Philip Rakity
  2011-02-18 19:06                   ` Philip Rakity
  1 sibling, 1 reply; 45+ messages in thread
From: Nicolas Pitre @ 2011-02-18 18:55 UTC (permalink / raw)
  To: Nath, Arindam
  Cc: Philip Rakity, Zhangfei Gao, Chris Ball, linux-mmc, Su, Henry,
	Lu, Aaron, anath.amd

On Fri, 18 Feb 2011, Nath, Arindam wrote:

> Hi Philip,
> 
> 
> > -----Original Message-----
> > From: Philip Rakity [mailto:prakity@marvell.com]
> > Sent: Friday, February 18, 2011 9:19 PM
> > To: Nath, Arindam
> > Cc: Zhangfei Gao; Chris Ball; linux-mmc@vger.kernel.org; Su, Henry; Lu,
> > Aaron; anath.amd@gmail.com; Nicolas Pitre
> > Subject: [PATCH 01/12] mmc: sdhci: add support for SD Host Controller
> > 3.0
> > 
> > 
> > Arindam,
> > 
> > Could you look at making (Tuning)  CMD19 part of the sdhc.ci driver?
> > It flow is part of the Host Controller and Physical layer specs.

Still, if this is something that is common to all host controllers then 
it should live in the core code.  The host controller driver should be 
as dumb as possible and try to be command agnostic.

> There are two main reasons for exposing the callback of tuning procedure from sdhci.c:
> 
> 1. The tuning procedure is part of UHS-I initialization sequence as described in Physical Layer Spec v3.01. Since the UHS-I init sequence takes places inside sd.c, so I thought it better to place it there instead.
> 2. In future, if we need to add support for UHS-I sequence for sdio cards too, we can use the same callback from sdhci.c without any modifications.

And this won't have to be duplicated in all the host controller drivers.


Nicolas

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 01/12] mmc: sdhci: add support for SD Host Controller 3.0
  2011-02-18 17:17                 ` Nath, Arindam
  2011-02-18 18:55                   ` Nicolas Pitre
@ 2011-02-18 19:06                   ` Philip Rakity
  1 sibling, 0 replies; 45+ messages in thread
From: Philip Rakity @ 2011-02-18 19:06 UTC (permalink / raw)
  To: Nath, Arindam
  Cc: Zhangfei Gao, Chris Ball, linux-mmc, Su, Henry, Lu, Aaron,
	anath.amd, Nicolas Pitre


On Feb 18, 2011, at 9:17 AM, Nath, Arindam wrote:

> Hi Philip,
> 
> 
>> -----Original Message-----
>> From: Philip Rakity [mailto:prakity@marvell.com]
>> Sent: Friday, February 18, 2011 9:19 PM
>> To: Nath, Arindam
>> Cc: Zhangfei Gao; Chris Ball; linux-mmc@vger.kernel.org; Su, Henry; Lu,
>> Aaron; anath.amd@gmail.com; Nicolas Pitre
>> Subject: [PATCH 01/12] mmc: sdhci: add support for SD Host Controller
>> 3.0
>> 
>> 
>> Arindam,
>> 
>> Could you look at making (Tuning)  CMD19 part of the sdhc.ci driver?
>> It flow is part of the Host Controller and Physical layer specs.
> 
> There are two main reasons for exposing the callback of tuning procedure from sdhci.c:
> 
> 1. The tuning procedure is part of UHS-I initialization sequence as described in Physical Layer Spec v3.01. Since the UHS-I init sequence takes places inside sd.c, so I thought it better to place it there instead.

understand -- I think it should be in the driver as part of the init sequence.  Once you have some code I can prototype this.  We can do it offline.

> 2. In future, if we need to add support for UHS-I sequence for sdio cards too, we can use the same callback from sdhci.c without any modifications.
> 

SDIO is not a problem  -- almost follows same code sequence.  I have this almost working.

> I am open to suggestions.
> 
> Regards,
> Arindam
> 


^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 01/12] mmc: sdhci: add support for SD Host Controller 3.0
  2011-02-18 18:55                   ` Nicolas Pitre
@ 2011-02-18 19:08                     ` Philip Rakity
  0 siblings, 0 replies; 45+ messages in thread
From: Philip Rakity @ 2011-02-18 19:08 UTC (permalink / raw)
  To: Nicolas Pitre
  Cc: Nath, Arindam, Zhangfei Gao, Chris Ball, linux-mmc, Su, Henry,
	Lu, Aaron, anath.amd


Thanks Nico 

Anindam,  Lets follow Nico advise -- please ignore my last post on moving it into the host driver.

Philip

On Feb 18, 2011, at 10:55 AM, Nicolas Pitre wrote:

> On Fri, 18 Feb 2011, Nath, Arindam wrote:
> 
>> Hi Philip,
>> 
>> 
>>> -----Original Message-----
>>> From: Philip Rakity [mailto:prakity@marvell.com]
>>> Sent: Friday, February 18, 2011 9:19 PM
>>> To: Nath, Arindam
>>> Cc: Zhangfei Gao; Chris Ball; linux-mmc@vger.kernel.org; Su, Henry; Lu,
>>> Aaron; anath.amd@gmail.com; Nicolas Pitre
>>> Subject: [PATCH 01/12] mmc: sdhci: add support for SD Host Controller
>>> 3.0
>>> 
>>> 
>>> Arindam,
>>> 
>>> Could you look at making (Tuning)  CMD19 part of the sdhc.ci driver?
>>> It flow is part of the Host Controller and Physical layer specs.
> 
> Still, if this is something that is common to all host controllers then 
> it should live in the core code.  The host controller driver should be 
> as dumb as possible and try to be command agnostic.
> 
>> There are two main reasons for exposing the callback of tuning procedure from sdhci.c:
>> 
>> 1. The tuning procedure is part of UHS-I initialization sequence as described in Physical Layer Spec v3.01. Since the UHS-I init sequence takes places inside sd.c, so I thought it better to place it there instead.
>> 2. In future, if we need to add support for UHS-I sequence for sdio cards too, we can use the same callback from sdhci.c without any modifications.
> 
> And this won't have to be duplicated in all the host controller drivers.
> 
> 
> Nicolas


^ permalink raw reply	[flat|nested] 45+ messages in thread

* RE: [PATCH 02/12] mmc: sd: add support for signal voltage switch procedure
  2011-02-15  9:35 ` [PATCH 02/12] mmc: sd: add support for signal voltage switch procedure Arindam Nath
  2011-02-15 10:16   ` Wolfram Sang
  2011-02-15 21:18   ` Nicolas Pitre
@ 2011-03-01 16:02   ` subhashj
  2011-03-02  8:06     ` Nath, Arindam
  2 siblings, 1 reply; 45+ messages in thread
From: subhashj @ 2011-03-01 16:02 UTC (permalink / raw)
  To: 'Arindam Nath', cjb; +Cc: linux-mmc, henry.su, aaron.lu, anath.amd



> -----Original Message-----
> From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> owner@vger.kernel.org] On Behalf Of Arindam Nath
> Sent: Tuesday, February 15, 2011 3:05 PM
> To: cjb@laptop.org
> Cc: linux-mmc@vger.kernel.org; henry.su@amd.com; aaron.lu@amd.com;
> anath.amd@gmail.com; Arindam Nath
> Subject: [PATCH 02/12] mmc: sd: add support for signal voltage switch
> procedure
> 
> Host Controller v3.00 adds another Capabilities register. We can
> directly read this register without any version checking since the
> new register bits will be considred as reserved in older controllers,
> and will be read as 0. Apart from other things, this new register
> indicates whether the Host Controller supports SDR50, SDR104, and
> DDR50 UHS-I modes. So depending on the host support, we set
> the corresponding MMC_CAP_* flags. One more new register. Host Control2
> is added in v3.00, which is used during Signal Voltage Switch prcedure
> described below.
> 
> Since as per v3.00 spec, UHS-I supported hosts should set S18R to 1,
> we set S18R (bit 24) of OCR before sending ACMD41. We also need to set
> XPC (bit 28) of OCR in case the host can supply >150mA. This support is
> indicated by the Maximum Current Capabilities register of the Host
> Controller.
> 
> If the response of ACMD41 has both CCS and S18A set, we start the
> signal voltage switch procedure, which if successfull, will switch
> the card from 3.3V signalling to 1.8V signalling. Signal voltage
> switch procedure adds support for a new command CMD11 in the Physical
> Layer Spec v3.01. As part of this procedure, we need to set 1.8V
> Signalling
> Enable (bit 3) of Host Control2 register, which if remains set after
> 5ms,
> means the switch to 1.8V signalling is successfull. Otherwise, we clear
> bit 24 of OCR and retry the initialization sequence.
> 
> Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> ---
>  drivers/mmc/core/sd.c     |   37 ++++++++++-
>  drivers/mmc/core/sd_ops.c |   25 ++++++++
>  drivers/mmc/core/sd_ops.h |    1 +
>  drivers/mmc/host/sdhci.c  |  147
> +++++++++++++++++++++++++++++++++++++++++----
>  drivers/mmc/host/sdhci.h  |   18 +++++-
>  include/linux/mmc/host.h  |    8 +++
>  include/linux/mmc/sd.h    |    1 +
>  7 files changed, 219 insertions(+), 18 deletions(-)
> 
> diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> index b3f8a3c..e968d5c 100644
> --- a/drivers/mmc/core/sd.c
> +++ b/drivers/mmc/core/sd.c
> @@ -408,6 +408,7 @@ struct device_type sd_type = {
>  int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid)
>  {
>  	int err;
> +	u32 rocr;
> 
>  	/*
>  	 * Since we're changing the OCR value, we seem to
> @@ -427,10 +428,25 @@ int mmc_sd_get_cid(struct mmc_host *host, u32
> ocr, u32 *cid)
>  	if (!err)
>  		ocr |= 1 << 30;
> 
> -	err = mmc_send_app_op_cond(host, ocr, NULL);
> +	/* If the host can supply more than 150mA, XPC should be set to
> 1. */
> +	if (host->caps & (MMC_CAP_SET_XPC_330 | MMC_CAP_SET_XPC_300 |
> +	    MMC_CAP_SET_XPC_180))
> +		ocr |= 1 << 28;
> +

Why do you need MMC_CAP_SET_XPC_300 here? MMC_CAP_SET_XPC_330 &
MMC_CAP_SET_XPC_180 is not enough?

> +	err = mmc_send_app_op_cond(host, ocr, &rocr);
>  	if (err)
>  		return err;
> 
> +	/*
> +	 * In case CCS and S18A in the response is set, start Signal
> Voltage
> +	 * Switch procedure. SPI mode doesn't support CMD11.
> +	 */	
> +	if (!mmc_host_is_spi(host) && (rocr & 0x41000000)) {
> +		err = mmc_start_voltage_switch(host);
> +		if (err)
> +			return err;
> +	}
> +

Looks good.

>  	if (mmc_host_is_spi(host))
>  		err = mmc_send_cid(host, cid);
>  	else
> @@ -827,11 +843,26 @@ int mmc_attach_sd(struct mmc_host *host)
>  	}
> 
>  	/*
> +	 * If the host supports one of UHS-I modes, request the card
> +	 * to switch to 1.8V signaling level.
> +	 */
> +	if (host->caps & (MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 |
> +	    MMC_CAP_UHS_DDR50))
> +		host->ocr |= (1 << 24);

What about SDR12 and SDR25 modes? Are we ignoring them?

> +
> +	/*
>  	 * Detect and init the card.
>  	 */
>  	err = mmc_sd_init_card(host, host->ocr, NULL);
> -	if (err)
> -		goto err;
> +	if (err == -EAGAIN) {
> +		/*
> +		 * Retry initialization with S18R set to 0.
> +		 */
> +		host->ocr &= ~(1 << 24);
> +		err = mmc_sd_init_card(host, host->ocr, NULL);
> +		if (err)
> +			goto err;
> +	}
> 
>  	mmc_release_host(host);
>  	err = mmc_add_card(host->card);
> diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c
> index 797cdb5..a0f97c9 100644
> --- a/drivers/mmc/core/sd_ops.c
> +++ b/drivers/mmc/core/sd_ops.c
> @@ -146,6 +146,31 @@ int mmc_app_set_bus_width(struct mmc_card *card,
> int width)
>  	return 0;
>  }
> 
> +int mmc_start_voltage_switch(struct mmc_host *host)
> +{
> +	struct mmc_command cmd;
> +	int err;
> +
> +	BUG_ON(!host);
> +
> +	memset(&cmd, 0, sizeof(struct mmc_command));
> +
> +	cmd.opcode = SD_SWITCH_VOLTAGE;
> +	cmd.arg = 0;
> +	cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
> +
> +	err = mmc_wait_for_cmd(host, &cmd, 0);
> +	if (err)
> +		return err;
> +
> +	if (!mmc_host_is_spi(host) && (cmd.resp[0] & R1_ERROR))
> +		return -EIO;
> +
> +	err = host->ops->start_signal_voltage_switch(host);

No Null check for start_signal_voltage_switch ops?

> +
> +	return err;
> +}
> +
>  int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
>  {
>  	struct mmc_command cmd;
> diff --git a/drivers/mmc/core/sd_ops.h b/drivers/mmc/core/sd_ops.h
> index ffc2305..3cfba59 100644
> --- a/drivers/mmc/core/sd_ops.h
> +++ b/drivers/mmc/core/sd_ops.h
> @@ -20,6 +20,7 @@ int mmc_app_send_scr(struct mmc_card *card, u32
> *scr);
>  int mmc_sd_switch(struct mmc_card *card, int mode, int group,
>  	u8 value, u8 *resp);
>  int mmc_app_sd_status(struct mmc_card *card, void *ssr);
> +int mmc_start_voltage_switch(struct mmc_host *host);
> 
>  #endif
> 
> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> index 0b537cf..6d7a276 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -87,6 +87,8 @@ static void sdhci_dumpregs(struct sdhci_host *host)
>  	printk(KERN_DEBUG DRIVER_NAME ": Cmd:      0x%08x | Max curr:
> 0x%08x\n",
>  		sdhci_readw(host, SDHCI_COMMAND),
>  		sdhci_readl(host, SDHCI_MAX_CURRENT));
> +	printk(KERN_DEBUG DRIVER_NAME ": Host ctl2: 0x%08x\n",
> +		sdhci_readw(host, SDHCI_HOST_CONTROL2));
> 
>  	if (host->flags & SDHCI_USE_ADMA)
>  		printk(KERN_DEBUG DRIVER_NAME ": ADMA Err: 0x%08x | ADMA
> Ptr: 0x%08x\n",
> @@ -1340,11 +1342,76 @@ out:
>  	spin_unlock_irqrestore(&host->lock, flags);
>  }
> 
> +static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc)
> +{
> +	struct sdhci_host *host;
> +	u8 pwr;
> +	u16 clk, ctrl;
> +	u32 present_state;
> +	unsigned long flags;
> +
> +	host = mmc_priv(mmc);
> +
> +	spin_lock_irqsave(&host->lock, flags);
> +
> +	/* Stop SDCLK */
> +	host = mmc_priv(mmc);
> +	clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
> +	clk &= ~SDHCI_CLOCK_CARD_EN;
> +	sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
> +
> +	/* Check whether DAT[3:0] is 0000 */
> +	present_state = sdhci_readl(host, SDHCI_PRESENT_STATE);
> +	if (!((present_state & SDHCI_DATA_LVL_MASK) >>
> SDHCI_DATA_LVL_SHIFT)) {
> +		/* Enable 1.8V Signal Enable in the Host Control2 register
> */
> +		ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> +		ctrl |= SDHCI_CTRL_VDD_180;
> +		sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> +
> +		/* Wait for 5ms */
> +		mdelay(5);

msleep would be more appropriate here.

> +
> +		ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> +		if (ctrl & SDHCI_CTRL_VDD_180) {
> +			/* Provide SDCLK again and wait for 1ms*/
> +			clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
> +			clk |= SDHCI_CLOCK_CARD_EN;
> +			sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
> +			mdelay(1);
> +
> +			/*
> +			 * If DAT[3:0] level is 1111b, then the card was
> +			 * successfully switched to 1.8V signaling.
> +			 */
> +			present_state = sdhci_readl(host,
> SDHCI_PRESENT_STATE);
> +			if ((present_state & SDHCI_DATA_LVL_MASK) ==
> +			     SDHCI_DATA_LVL_MASK) {
> +				spin_unlock_irqrestore(&host->lock, flags);
> +				return 0;
> +			}

Sequence looks fine according to spec.


> +		}
> +	}
> +
> +	/*
> +	 * If we are here, that means the switch to 1.8V signaling
> failed. Stop
> +	 * power to the card, and retry initialization sequence by
> setting S18R
> +	 * to 0.
> +	 */
> +	pwr = sdhci_readb(host, SDHCI_POWER_CONTROL);
> +	pwr &= ~SDHCI_POWER_ON;
> +	sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
> +
> +	spin_unlock_irqrestore(&host->lock, flags);
> +
> +	return -EAGAIN;
> +}
> +
>  static const struct mmc_host_ops sdhci_ops = {
>  	.request	= sdhci_request,
>  	.set_ios	= sdhci_set_ios,
>  	.get_ro		= sdhci_get_ro,
>  	.enable_sdio_irq = sdhci_enable_sdio_irq,
> +	.start_signal_voltage_switch	=
> sdhci_start_signal_voltage_switch,
>  };

If possible can you shorthand ops name "start_signal_voltage_switch"?


> 
> 
> /**********************************************************************
> *******\
> @@ -1802,7 +1869,9 @@ EXPORT_SYMBOL_GPL(sdhci_alloc_host);
>  int sdhci_add_host(struct sdhci_host *host)
>  {
>  	struct mmc_host *mmc;
> -	unsigned int caps, ocr_avail;
> +	u32 caps[2];
> +	u32 max_current_caps;
> +	unsigned int ocr_avail;
>  	int ret;
> 
>  	WARN_ON(host == NULL);
> @@ -1825,12 +1894,13 @@ int sdhci_add_host(struct sdhci_host *host)
>  			host->version);
>  	}
> 
> -	caps = (host->quirks & SDHCI_QUIRK_MISSING_CAPS) ? host->caps :
> +	caps[0] = (host->quirks & SDHCI_QUIRK_MISSING_CAPS) ? host->caps
> :
>  		sdhci_readl(host, SDHCI_CAPABILITIES);
> +	caps[1] = sdhci_readl(host, SDHCI_CAPABILITIES_1);
> 
>  	if (host->quirks & SDHCI_QUIRK_FORCE_DMA)
>  		host->flags |= SDHCI_USE_SDMA;
> -	else if (!(caps & SDHCI_CAN_DO_SDMA))
> +	else if (!(caps[0] & SDHCI_CAN_DO_SDMA))
>  		DBG("Controller doesn't have SDMA capability\n");
>  	else
>  		host->flags |= SDHCI_USE_SDMA;
> @@ -1841,7 +1911,8 @@ int sdhci_add_host(struct sdhci_host *host)
>  		host->flags &= ~SDHCI_USE_SDMA;
>  	}
> 
> -	if ((host->version >= SDHCI_SPEC_200) && (caps &
> SDHCI_CAN_DO_ADMA2)) {
> +	if ((host->version >= SDHCI_SPEC_200) &&
> +		(caps[0] & SDHCI_CAN_DO_ADMA2)) {
>  		host->flags &= ~SDHCI_USE_SDMA;
>  		host->flags |= SDHCI_USE_ADMA;
>  	}
> @@ -1893,10 +1964,10 @@ int sdhci_add_host(struct sdhci_host *host)
>  	}
> 
>  	if (host->version >= SDHCI_SPEC_300)
> -		host->max_clk = (caps & SDHCI_CLOCK_V3_BASE_MASK)
> +		host->max_clk = (caps[0] & SDHCI_CLOCK_V3_BASE_MASK)
>  			>> SDHCI_CLOCK_BASE_SHIFT;
>  	else
> -		host->max_clk = (caps & SDHCI_CLOCK_BASE_MASK)
> +		host->max_clk = (caps[0] & SDHCI_CLOCK_BASE_MASK)
>  			>> SDHCI_CLOCK_BASE_SHIFT;
> 
>  	host->max_clk *= 1000000;
> @@ -1912,7 +1983,7 @@ int sdhci_add_host(struct sdhci_host *host)
>  	}
> 
>  	host->timeout_clk =
> -		(caps & SDHCI_TIMEOUT_CLK_MASK) >> SDHCI_TIMEOUT_CLK_SHIFT;
> +		(caps[0] & SDHCI_TIMEOUT_CLK_MASK) >>
> SDHCI_TIMEOUT_CLK_SHIFT;
>  	if (host->timeout_clk == 0) {
>  		if (host->ops->get_timeout_clock) {
>  			host->timeout_clk = host->ops-
> >get_timeout_clock(host);
> @@ -1924,7 +1995,7 @@ int sdhci_add_host(struct sdhci_host *host)
>  			return -ENODEV;
>  		}
>  	}
> -	if (caps & SDHCI_TIMEOUT_CLK_UNIT)
> +	if (caps[0] & SDHCI_TIMEOUT_CLK_UNIT)
>  		host->timeout_clk *= 1000;
> 
>  	/*
> @@ -1951,21 +2022,71 @@ int sdhci_add_host(struct sdhci_host *host)
>  	if (!(host->quirks & SDHCI_QUIRK_FORCE_1_BIT_DATA))
>  		mmc->caps |= MMC_CAP_4_BIT_DATA;
> 
> -	if (caps & SDHCI_CAN_DO_HISPD)
> +	if (caps[0] & SDHCI_CAN_DO_HISPD)
>  		mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
> 
>  	if ((host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) &&
>  	    mmc_card_is_removable(mmc))
>  		mmc->caps |= MMC_CAP_NEEDS_POLL;
> 
> +	/* UHS-I mode(s) supported by the host controller. */
> +	if (caps[1] & SDHCI_SUPPORT_SDR50)
> +		mmc->caps |= MMC_CAP_UHS_SDR50;
> +	if (caps[1] & SDHCI_SUPPORT_SDR104)
> +		mmc->caps |= MMC_CAP_UHS_SDR104;

If SDR104 is supported then that SDR50 is by default supported. You may want
to take care of that.

> +	if (caps[1] & SDHCI_SUPPORT_DDR50)
> +		mmc->caps |= MMC_CAP_UHS_DDR50;
> +
>  	ocr_avail = 0;
> -	if (caps & SDHCI_CAN_VDD_330)
> +	/*
> +	 * According to SD Host Controller spec v3.00, if the Host System
> +	 * can afford more than 150mA, Host Driver should set XPC to 1.
> Also
> +	 * the value is meaningful only if Voltage Support in the
> Capabilities
> +	 * register is set. The actual current value is 4 times the
> register
> +	 * value.
> +	 */
> +	max_current_caps = sdhci_readl(host, SDHCI_MAX_CURRENT);
> +
> +	if (caps[0] & SDHCI_CAN_VDD_330) {
> +		int max_current_330;
> +
>  		ocr_avail |= MMC_VDD_32_33 | MMC_VDD_33_34;
> -	if (caps & SDHCI_CAN_VDD_300)
> +
> +		max_current_330 = ((max_current_caps &
> +				   SDHCI_MAX_CURRENT_330_MASK) >>
> +				   SDHCI_MAX_CURRENT_330_SHIFT) *
> +				   SDHCI_MAX_CURRENT_MULTIPLIER;
> +
> +		if (max_current_330 > 150)
> +			mmc->caps |= MMC_CAP_SET_XPC_330;
> +	}
> +	if (caps[0] & SDHCI_CAN_VDD_300) {
> +		int max_current_300;
> +
>  		ocr_avail |= MMC_VDD_29_30 | MMC_VDD_30_31;
> -	if (caps & SDHCI_CAN_VDD_180)
> +
> +		max_current_300 = ((max_current_caps &
> +				   SDHCI_MAX_CURRENT_300_MASK) >>
> +				   SDHCI_MAX_CURRENT_300_SHIFT) *
> +				   SDHCI_MAX_CURRENT_MULTIPLIER;
> +
> +		if (max_current_300 > 150)
> +			mmc->caps |= MMC_CAP_SET_XPC_300;
> +	}
> +	if (caps[0] & SDHCI_CAN_VDD_180) {
> +		int max_current_180;
> +
>  		ocr_avail |= MMC_VDD_165_195;
> 
> +		max_current_180 = ((max_current_caps &
> +				   SDHCI_MAX_CURRENT_180_MASK) >>
> +				   SDHCI_MAX_CURRENT_180_SHIFT) *
> +				   SDHCI_MAX_CURRENT_MULTIPLIER;
> +
> +		if (max_current_180 > 150)
> +			mmc->caps |= MMC_CAP_SET_XPC_180;
> +	}
> +
>  	mmc->ocr_avail = ocr_avail;
>  	mmc->ocr_avail_sdio = ocr_avail;
>  	if (host->ocr_avail_sdio)
> @@ -2025,7 +2146,7 @@ int sdhci_add_host(struct sdhci_host *host)
>  	if (host->quirks & SDHCI_QUIRK_FORCE_BLK_SZ_2048) {
>  		mmc->max_blk_size = 2;
>  	} else {
> -		mmc->max_blk_size = (caps & SDHCI_MAX_BLOCK_MASK) >>
> +		mmc->max_blk_size = (caps[0] & SDHCI_MAX_BLOCK_MASK) >>
>  				SDHCI_MAX_BLOCK_SHIFT;
>  		if (mmc->max_blk_size >= 3) {
>  			printk(KERN_WARNING "%s: Invalid maximum block size,
> "
> diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
> index 223762c..95d70e6 100644
> --- a/drivers/mmc/host/sdhci.h
> +++ b/drivers/mmc/host/sdhci.h
> @@ -69,6 +69,8 @@
>  #define  SDHCI_DATA_AVAILABLE	0x00000800
>  #define  SDHCI_CARD_PRESENT	0x00010000
>  #define  SDHCI_WRITE_PROTECT	0x00080000
> +#define  SDHCI_DATA_LVL_MASK	0x00F00000
> +#define   SDHCI_DATA_LVL_SHIFT	20
> 
>  #define SDHCI_HOST_CONTROL 	0x28
>  #define  SDHCI_CTRL_LED		0x01
> @@ -147,7 +149,8 @@
> 
>  #define SDHCI_ACMD12_ERR	0x3C
> 
> -/* 3E-3F reserved */
> +#define SDHCI_HOST_CONTROL2		0x3E
> +#define  SDHCI_CTRL_VDD_180		0x0008
> 
>  #define SDHCI_CAPABILITIES	0x40
>  #define  SDHCI_TIMEOUT_CLK_MASK	0x0000003F
> @@ -168,9 +171,20 @@
>  #define  SDHCI_CAN_VDD_180	0x04000000
>  #define  SDHCI_CAN_64BIT	0x10000000
> 
> +#define  SDHCI_SUPPORT_SDR50	0x00000001
> +#define  SDHCI_SUPPORT_SDR104	0x00000002
> +#define  SDHCI_SUPPORT_DDR50	0x00000004
> +
>  #define SDHCI_CAPABILITIES_1	0x44
> 
> -#define SDHCI_MAX_CURRENT	0x48
> +#define SDHCI_MAX_CURRENT		0x48
> +#define  SDHCI_MAX_CURRENT_330_MASK	0x0000FF
> +#define  SDHCI_MAX_CURRENT_330_SHIFT	0
> +#define  SDHCI_MAX_CURRENT_300_MASK	0x00FF00
> +#define  SDHCI_MAX_CURRENT_300_SHIFT	8
> +#define  SDHCI_MAX_CURRENT_180_MASK	0xFF0000
> +#define  SDHCI_MAX_CURRENT_180_SHIFT	16
> +#define   SDHCI_MAX_CURRENT_MULTIPLIER	4
> 
>  /* 4C-4F reserved for more max current */
> 
> diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
> index bcb793e..ec09b32 100644
> --- a/include/linux/mmc/host.h
> +++ b/include/linux/mmc/host.h
> @@ -117,6 +117,8 @@ struct mmc_host_ops {
> 
>  	/* optional callback for HC quirks */
>  	void	(*init_card)(struct mmc_host *host, struct mmc_card *card);
> +
> +	int	(*start_signal_voltage_switch)(struct mmc_host *host);
>  };
> 
>  struct mmc_card;
> @@ -173,6 +175,12 @@ struct mmc_host {
>  						/* DDR mode at 1.2V */
>  #define MMC_CAP_POWER_OFF_CARD	(1 << 13)	/* Can power off
after
> boot */
>  #define MMC_CAP_BUS_WIDTH_TEST	(1 << 14)	/* CMD14/CMD19 bus
> width ok */
> +#define MMC_CAP_UHS_SDR50	(1 << 15)	/* Host supports UHS SDR50
> mode */
> +#define MMC_CAP_UHS_SDR104	(1 << 16)	/* Host supports UHS SDR104
> mode */
> +#define MMC_CAP_UHS_DDR50	(1 << 17)	/* Host supports UHS DDR50
> mode */
> +#define MMC_CAP_SET_XPC_330	(1 << 18)	/* Host supports >150mA
> current at 3.3V */
> +#define MMC_CAP_SET_XPC_300	(1 << 19)	/* Host supports >150mA
> current at 3.0V */
> +#define MMC_CAP_SET_XPC_180	(1 << 20)	/* Host supports >150mA
> current at 1.8V */
> 
>  	mmc_pm_flag_t		pm_caps;	/* supported pm features */
> 
> diff --git a/include/linux/mmc/sd.h b/include/linux/mmc/sd.h
> index 178363b..3ba5aa6 100644
> --- a/include/linux/mmc/sd.h
> +++ b/include/linux/mmc/sd.h
> @@ -17,6 +17,7 @@
>  /* This is basically the same command as for MMC with some quirks. */
>  #define SD_SEND_RELATIVE_ADDR     3   /* bcr                     R6
> */
>  #define SD_SEND_IF_COND           8   /* bcr  [11:0] See below   R7
> */
> +#define SD_SWITCH_VOLTAGE         11  /* ac                      R1
> */
> 
>    /* class 10 */
>  #define SD_SWITCH                 6   /* adtc [31:0] See below   R1
> */
> --
> 1.7.1
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


^ permalink raw reply	[flat|nested] 45+ messages in thread

* RE: [PATCH 02/12] mmc: sd: add support for signal voltage switch procedure
  2011-03-01 16:02   ` subhashj
@ 2011-03-02  8:06     ` Nath, Arindam
  2011-03-03 13:16       ` subhashj
  0 siblings, 1 reply; 45+ messages in thread
From: Nath, Arindam @ 2011-03-02  8:06 UTC (permalink / raw)
  To: subhashj, cjb; +Cc: linux-mmc, Su, Henry, Lu, Aaron, anath.amd

Hi Subhash,


> -----Original Message-----
> From: subhashj@codeaurora.org [mailto:subhashj@codeaurora.org]
> Sent: Tuesday, March 01, 2011 9:33 PM
> To: Nath, Arindam; cjb@laptop.org
> Cc: linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
> anath.amd@gmail.com
> Subject: RE: [PATCH 02/12] mmc: sd: add support for signal voltage
> switch procedure
>
>
>
> > -----Original Message-----
> > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > owner@vger.kernel.org] On Behalf Of Arindam Nath
> > Sent: Tuesday, February 15, 2011 3:05 PM
> > To: cjb@laptop.org
> > Cc: linux-mmc@vger.kernel.org; henry.su@amd.com; aaron.lu@amd.com;
> > anath.amd@gmail.com; Arindam Nath
> > Subject: [PATCH 02/12] mmc: sd: add support for signal voltage switch
> > procedure
> >
> > Host Controller v3.00 adds another Capabilities register. We can
> > directly read this register without any version checking since the
> > new register bits will be considred as reserved in older controllers,
> > and will be read as 0. Apart from other things, this new register
> > indicates whether the Host Controller supports SDR50, SDR104, and
> > DDR50 UHS-I modes. So depending on the host support, we set
> > the corresponding MMC_CAP_* flags. One more new register. Host
> Control2
> > is added in v3.00, which is used during Signal Voltage Switch
> prcedure
> > described below.
> >
> > Since as per v3.00 spec, UHS-I supported hosts should set S18R to 1,
> > we set S18R (bit 24) of OCR before sending ACMD41. We also need to
> set
> > XPC (bit 28) of OCR in case the host can supply >150mA. This support
> is
> > indicated by the Maximum Current Capabilities register of the Host
> > Controller.
> >
> > If the response of ACMD41 has both CCS and S18A set, we start the
> > signal voltage switch procedure, which if successfull, will switch
> > the card from 3.3V signalling to 1.8V signalling. Signal voltage
> > switch procedure adds support for a new command CMD11 in the Physical
> > Layer Spec v3.01. As part of this procedure, we need to set 1.8V
> > Signalling
> > Enable (bit 3) of Host Control2 register, which if remains set after
> > 5ms,
> > means the switch to 1.8V signalling is successfull. Otherwise, we
> clear
> > bit 24 of OCR and retry the initialization sequence.
> >
> > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > ---
> >  drivers/mmc/core/sd.c     |   37 ++++++++++-
> >  drivers/mmc/core/sd_ops.c |   25 ++++++++
> >  drivers/mmc/core/sd_ops.h |    1 +
> >  drivers/mmc/host/sdhci.c  |  147
> > +++++++++++++++++++++++++++++++++++++++++----
> >  drivers/mmc/host/sdhci.h  |   18 +++++-
> >  include/linux/mmc/host.h  |    8 +++
> >  include/linux/mmc/sd.h    |    1 +
> >  7 files changed, 219 insertions(+), 18 deletions(-)
> >
> > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> > index b3f8a3c..e968d5c 100644
> > --- a/drivers/mmc/core/sd.c
> > +++ b/drivers/mmc/core/sd.c
> > @@ -408,6 +408,7 @@ struct device_type sd_type = {
> >  int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid)
> >  {
> >     int err;
> > +   u32 rocr;
> >
> >     /*
> >      * Since we're changing the OCR value, we seem to
> > @@ -427,10 +428,25 @@ int mmc_sd_get_cid(struct mmc_host *host, u32
> > ocr, u32 *cid)
> >     if (!err)
> >             ocr |= 1 << 30;
> >
> > -   err = mmc_send_app_op_cond(host, ocr, NULL);
> > +   /* If the host can supply more than 150mA, XPC should be set to
> > 1. */
> > +   if (host->caps & (MMC_CAP_SET_XPC_330 | MMC_CAP_SET_XPC_300 |
> > +       MMC_CAP_SET_XPC_180))
> > +           ocr |= 1 << 28;
> > +
>
> Why do you need MMC_CAP_SET_XPC_300 here? MMC_CAP_SET_XPC_330 &
> MMC_CAP_SET_XPC_180 is not enough?

According to the Host Controller Spec v3.00, section 2.2.26:

"If a Host System can afford more than 150mA, Host Driver set XPC to 1."

Now since >150mA can be set for 1.8V, 3.0V and 3.3V, the above condition checks for all these cases.

>
> > +   err = mmc_send_app_op_cond(host, ocr, &rocr);
> >     if (err)
> >             return err;
> >
> > +   /*
> > +    * In case CCS and S18A in the response is set, start Signal
> > Voltage
> > +    * Switch procedure. SPI mode doesn't support CMD11.
> > +    */
> > +   if (!mmc_host_is_spi(host) && (rocr & 0x41000000)) {
> > +           err = mmc_start_voltage_switch(host);
> > +           if (err)
> > +                   return err;
> > +   }
> > +
>
> Looks good.
>
> >     if (mmc_host_is_spi(host))
> >             err = mmc_send_cid(host, cid);
> >     else
> > @@ -827,11 +843,26 @@ int mmc_attach_sd(struct mmc_host *host)
> >     }
> >
> >     /*
> > +    * If the host supports one of UHS-I modes, request the card
> > +    * to switch to 1.8V signaling level.
> > +    */
> > +   if (host->caps & (MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 |
> > +       MMC_CAP_UHS_DDR50))
> > +           host->ocr |= (1 << 24);
>
> What about SDR12 and SDR25 modes? Are we ignoring them?

Yes, point noted. I will fix this in V2. Thanks.

>
> > +
> > +   /*
> >      * Detect and init the card.
> >      */
> >     err = mmc_sd_init_card(host, host->ocr, NULL);
> > -   if (err)
> > -           goto err;
> > +   if (err == -EAGAIN) {
> > +           /*
> > +            * Retry initialization with S18R set to 0.
> > +            */
> > +           host->ocr &= ~(1 << 24);
> > +           err = mmc_sd_init_card(host, host->ocr, NULL);
> > +           if (err)
> > +                   goto err;
> > +   }
> >
> >     mmc_release_host(host);
> >     err = mmc_add_card(host->card);
> > diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c
> > index 797cdb5..a0f97c9 100644
> > --- a/drivers/mmc/core/sd_ops.c
> > +++ b/drivers/mmc/core/sd_ops.c
> > @@ -146,6 +146,31 @@ int mmc_app_set_bus_width(struct mmc_card *card,
> > int width)
> >     return 0;
> >  }
> >
> > +int mmc_start_voltage_switch(struct mmc_host *host)
> > +{
> > +   struct mmc_command cmd;
> > +   int err;
> > +
> > +   BUG_ON(!host);
> > +
> > +   memset(&cmd, 0, sizeof(struct mmc_command));
> > +
> > +   cmd.opcode = SD_SWITCH_VOLTAGE;
> > +   cmd.arg = 0;
> > +   cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
> > +
> > +   err = mmc_wait_for_cmd(host, &cmd, 0);
> > +   if (err)
> > +           return err;
> > +
> > +   if (!mmc_host_is_spi(host) && (cmd.resp[0] & R1_ERROR))
> > +           return -EIO;
> > +
> > +   err = host->ops->start_signal_voltage_switch(host);
>
> No Null check for start_signal_voltage_switch ops?

Will fix this in V2.

>
> > +
> > +   return err;
> > +}
> > +
> >  int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
> >  {
> >     struct mmc_command cmd;
> > diff --git a/drivers/mmc/core/sd_ops.h b/drivers/mmc/core/sd_ops.h
> > index ffc2305..3cfba59 100644
> > --- a/drivers/mmc/core/sd_ops.h
> > +++ b/drivers/mmc/core/sd_ops.h
> > @@ -20,6 +20,7 @@ int mmc_app_send_scr(struct mmc_card *card, u32
> > *scr);
> >  int mmc_sd_switch(struct mmc_card *card, int mode, int group,
> >     u8 value, u8 *resp);
> >  int mmc_app_sd_status(struct mmc_card *card, void *ssr);
> > +int mmc_start_voltage_switch(struct mmc_host *host);
> >
> >  #endif
> >
> > diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> > index 0b537cf..6d7a276 100644
> > --- a/drivers/mmc/host/sdhci.c
> > +++ b/drivers/mmc/host/sdhci.c
> > @@ -87,6 +87,8 @@ static void sdhci_dumpregs(struct sdhci_host *host)
> >     printk(KERN_DEBUG DRIVER_NAME ": Cmd:      0x%08x | Max curr:
> > 0x%08x\n",
> >             sdhci_readw(host, SDHCI_COMMAND),
> >             sdhci_readl(host, SDHCI_MAX_CURRENT));
> > +   printk(KERN_DEBUG DRIVER_NAME ": Host ctl2: 0x%08x\n",
> > +           sdhci_readw(host, SDHCI_HOST_CONTROL2));
> >
> >     if (host->flags & SDHCI_USE_ADMA)
> >             printk(KERN_DEBUG DRIVER_NAME ": ADMA Err: 0x%08x | ADMA
> > Ptr: 0x%08x\n",
> > @@ -1340,11 +1342,76 @@ out:
> >     spin_unlock_irqrestore(&host->lock, flags);
> >  }
> >
> > +static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc)
> > +{
> > +   struct sdhci_host *host;
> > +   u8 pwr;
> > +   u16 clk, ctrl;
> > +   u32 present_state;
> > +   unsigned long flags;
> > +
> > +   host = mmc_priv(mmc);
> > +
> > +   spin_lock_irqsave(&host->lock, flags);
> > +
> > +   /* Stop SDCLK */
> > +   host = mmc_priv(mmc);
> > +   clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
> > +   clk &= ~SDHCI_CLOCK_CARD_EN;
> > +   sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
> > +
> > +   /* Check whether DAT[3:0] is 0000 */
> > +   present_state = sdhci_readl(host, SDHCI_PRESENT_STATE);
> > +   if (!((present_state & SDHCI_DATA_LVL_MASK) >>
> > SDHCI_DATA_LVL_SHIFT)) {
> > +           /* Enable 1.8V Signal Enable in the Host Control2 register
> > */
> > +           ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> > +           ctrl |= SDHCI_CTRL_VDD_180;
> > +           sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> > +
> > +           /* Wait for 5ms */
> > +           mdelay(5);
>
> msleep would be more appropriate here.

Nicolas already commented on this one. I will take care of it in V2.

>
> > +
> > +           ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> > +           if (ctrl & SDHCI_CTRL_VDD_180) {
> > +                   /* Provide SDCLK again and wait for 1ms*/
> > +                   clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
> > +                   clk |= SDHCI_CLOCK_CARD_EN;
> > +                   sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
> > +                   mdelay(1);
> > +
> > +                   /*
> > +                    * If DAT[3:0] level is 1111b, then the card was
> > +                    * successfully switched to 1.8V signaling.
> > +                    */
> > +                   present_state = sdhci_readl(host,
> > SDHCI_PRESENT_STATE);
> > +                   if ((present_state & SDHCI_DATA_LVL_MASK) ==
> > +                        SDHCI_DATA_LVL_MASK) {
> > +                           spin_unlock_irqrestore(&host->lock, flags);
> > +                           return 0;
> > +                   }
>
> Sequence looks fine according to spec.
>
>
> > +           }
> > +   }
> > +
> > +   /*
> > +    * If we are here, that means the switch to 1.8V signaling
> > failed. Stop
> > +    * power to the card, and retry initialization sequence by
> > setting S18R
> > +    * to 0.
> > +    */
> > +   pwr = sdhci_readb(host, SDHCI_POWER_CONTROL);
> > +   pwr &= ~SDHCI_POWER_ON;
> > +   sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
> > +
> > +   spin_unlock_irqrestore(&host->lock, flags);
> > +
> > +   return -EAGAIN;
> > +}
> > +
> >  static const struct mmc_host_ops sdhci_ops = {
> >     .request        = sdhci_request,
> >     .set_ios        = sdhci_set_ios,
> >     .get_ro         = sdhci_get_ro,
> >     .enable_sdio_irq = sdhci_enable_sdio_irq,
> > +   .start_signal_voltage_switch    =
> > sdhci_start_signal_voltage_switch,
> >  };
>
> If possible can you shorthand ops name "start_signal_voltage_switch"?

I wanted the name to be descriptive, so couldn't think of anything else. Can you suggest something?

>
>
> >
> >
> >
> /**********************************************************************
> > *******\
> > @@ -1802,7 +1869,9 @@ EXPORT_SYMBOL_GPL(sdhci_alloc_host);
> >  int sdhci_add_host(struct sdhci_host *host)
> >  {
> >     struct mmc_host *mmc;
> > -   unsigned int caps, ocr_avail;
> > +   u32 caps[2];
> > +   u32 max_current_caps;
> > +   unsigned int ocr_avail;
> >     int ret;
> >
> >     WARN_ON(host == NULL);
> > @@ -1825,12 +1894,13 @@ int sdhci_add_host(struct sdhci_host *host)
> >                     host->version);
> >     }
> >
> > -   caps = (host->quirks & SDHCI_QUIRK_MISSING_CAPS) ? host->caps :
> > +   caps[0] = (host->quirks & SDHCI_QUIRK_MISSING_CAPS) ? host->caps
> > :
> >             sdhci_readl(host, SDHCI_CAPABILITIES);
> > +   caps[1] = sdhci_readl(host, SDHCI_CAPABILITIES_1);
> >
> >     if (host->quirks & SDHCI_QUIRK_FORCE_DMA)
> >             host->flags |= SDHCI_USE_SDMA;
> > -   else if (!(caps & SDHCI_CAN_DO_SDMA))
> > +   else if (!(caps[0] & SDHCI_CAN_DO_SDMA))
> >             DBG("Controller doesn't have SDMA capability\n");
> >     else
> >             host->flags |= SDHCI_USE_SDMA;
> > @@ -1841,7 +1911,8 @@ int sdhci_add_host(struct sdhci_host *host)
> >             host->flags &= ~SDHCI_USE_SDMA;
> >     }
> >
> > -   if ((host->version >= SDHCI_SPEC_200) && (caps &
> > SDHCI_CAN_DO_ADMA2)) {
> > +   if ((host->version >= SDHCI_SPEC_200) &&
> > +           (caps[0] & SDHCI_CAN_DO_ADMA2)) {
> >             host->flags &= ~SDHCI_USE_SDMA;
> >             host->flags |= SDHCI_USE_ADMA;
> >     }
> > @@ -1893,10 +1964,10 @@ int sdhci_add_host(struct sdhci_host *host)
> >     }
> >
> >     if (host->version >= SDHCI_SPEC_300)
> > -           host->max_clk = (caps & SDHCI_CLOCK_V3_BASE_MASK)
> > +           host->max_clk = (caps[0] & SDHCI_CLOCK_V3_BASE_MASK)
> >                     >> SDHCI_CLOCK_BASE_SHIFT;
> >     else
> > -           host->max_clk = (caps & SDHCI_CLOCK_BASE_MASK)
> > +           host->max_clk = (caps[0] & SDHCI_CLOCK_BASE_MASK)
> >                     >> SDHCI_CLOCK_BASE_SHIFT;
> >
> >     host->max_clk *= 1000000;
> > @@ -1912,7 +1983,7 @@ int sdhci_add_host(struct sdhci_host *host)
> >     }
> >
> >     host->timeout_clk =
> > -           (caps & SDHCI_TIMEOUT_CLK_MASK) >> SDHCI_TIMEOUT_CLK_SHIFT;
> > +           (caps[0] & SDHCI_TIMEOUT_CLK_MASK) >>
> > SDHCI_TIMEOUT_CLK_SHIFT;
> >     if (host->timeout_clk == 0) {
> >             if (host->ops->get_timeout_clock) {
> >                     host->timeout_clk = host->ops-
> > >get_timeout_clock(host);
> > @@ -1924,7 +1995,7 @@ int sdhci_add_host(struct sdhci_host *host)
> >                     return -ENODEV;
> >             }
> >     }
> > -   if (caps & SDHCI_TIMEOUT_CLK_UNIT)
> > +   if (caps[0] & SDHCI_TIMEOUT_CLK_UNIT)
> >             host->timeout_clk *= 1000;
> >
> >     /*
> > @@ -1951,21 +2022,71 @@ int sdhci_add_host(struct sdhci_host *host)
> >     if (!(host->quirks & SDHCI_QUIRK_FORCE_1_BIT_DATA))
> >             mmc->caps |= MMC_CAP_4_BIT_DATA;
> >
> > -   if (caps & SDHCI_CAN_DO_HISPD)
> > +   if (caps[0] & SDHCI_CAN_DO_HISPD)
> >             mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
> >
> >     if ((host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) &&
> >         mmc_card_is_removable(mmc))
> >             mmc->caps |= MMC_CAP_NEEDS_POLL;
> >
> > +   /* UHS-I mode(s) supported by the host controller. */
> > +   if (caps[1] & SDHCI_SUPPORT_SDR50)
> > +           mmc->caps |= MMC_CAP_UHS_SDR50;
> > +   if (caps[1] & SDHCI_SUPPORT_SDR104)
> > +           mmc->caps |= MMC_CAP_UHS_SDR104;
>
> If SDR104 is supported then that SDR50 is by default supported. You may
> want
> to take care of that.

As per the spec, for SDR50 support:

"If SDR104 is supported, this bit shall be set to 1."

Since the spec uses the word _shall_, I thought it is more of a suggestion than a requirement. Please correct me if wrong.

>
> > +   if (caps[1] & SDHCI_SUPPORT_DDR50)
> > +           mmc->caps |= MMC_CAP_UHS_DDR50;
> > +
> >     ocr_avail = 0;
> > -   if (caps & SDHCI_CAN_VDD_330)
> > +   /*
> > +    * According to SD Host Controller spec v3.00, if the Host System
> > +    * can afford more than 150mA, Host Driver should set XPC to 1.
> > Also
> > +    * the value is meaningful only if Voltage Support in the
> > Capabilities
> > +    * register is set. The actual current value is 4 times the
> > register
> > +    * value.
> > +    */
> > +   max_current_caps = sdhci_readl(host, SDHCI_MAX_CURRENT);
> > +
> > +   if (caps[0] & SDHCI_CAN_VDD_330) {
> > +           int max_current_330;
> > +
> >             ocr_avail |= MMC_VDD_32_33 | MMC_VDD_33_34;
> > -   if (caps & SDHCI_CAN_VDD_300)
> > +
> > +           max_current_330 = ((max_current_caps &
> > +                              SDHCI_MAX_CURRENT_330_MASK) >>
> > +                              SDHCI_MAX_CURRENT_330_SHIFT) *
> > +                              SDHCI_MAX_CURRENT_MULTIPLIER;
> > +
> > +           if (max_current_330 > 150)
> > +                   mmc->caps |= MMC_CAP_SET_XPC_330;
> > +   }
> > +   if (caps[0] & SDHCI_CAN_VDD_300) {
> > +           int max_current_300;
> > +
> >             ocr_avail |= MMC_VDD_29_30 | MMC_VDD_30_31;
> > -   if (caps & SDHCI_CAN_VDD_180)
> > +
> > +           max_current_300 = ((max_current_caps &
> > +                              SDHCI_MAX_CURRENT_300_MASK) >>
> > +                              SDHCI_MAX_CURRENT_300_SHIFT) *
> > +                              SDHCI_MAX_CURRENT_MULTIPLIER;
> > +
> > +           if (max_current_300 > 150)
> > +                   mmc->caps |= MMC_CAP_SET_XPC_300;
> > +   }
> > +   if (caps[0] & SDHCI_CAN_VDD_180) {
> > +           int max_current_180;
> > +
> >             ocr_avail |= MMC_VDD_165_195;
> >
> > +           max_current_180 = ((max_current_caps &
> > +                              SDHCI_MAX_CURRENT_180_MASK) >>
> > +                              SDHCI_MAX_CURRENT_180_SHIFT) *
> > +                              SDHCI_MAX_CURRENT_MULTIPLIER;
> > +
> > +           if (max_current_180 > 150)
> > +                   mmc->caps |= MMC_CAP_SET_XPC_180;
> > +   }
> > +
> >     mmc->ocr_avail = ocr_avail;
> >     mmc->ocr_avail_sdio = ocr_avail;
> >     if (host->ocr_avail_sdio)
> > @@ -2025,7 +2146,7 @@ int sdhci_add_host(struct sdhci_host *host)
> >     if (host->quirks & SDHCI_QUIRK_FORCE_BLK_SZ_2048) {
> >             mmc->max_blk_size = 2;
> >     } else {
> > -           mmc->max_blk_size = (caps & SDHCI_MAX_BLOCK_MASK) >>
> > +           mmc->max_blk_size = (caps[0] & SDHCI_MAX_BLOCK_MASK) >>
> >                             SDHCI_MAX_BLOCK_SHIFT;
> >             if (mmc->max_blk_size >= 3) {
> >                     printk(KERN_WARNING "%s: Invalid maximum block size,
> > "
> > diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
> > index 223762c..95d70e6 100644
> > --- a/drivers/mmc/host/sdhci.h
> > +++ b/drivers/mmc/host/sdhci.h
> > @@ -69,6 +69,8 @@
> >  #define  SDHCI_DATA_AVAILABLE      0x00000800
> >  #define  SDHCI_CARD_PRESENT        0x00010000
> >  #define  SDHCI_WRITE_PROTECT       0x00080000
> > +#define  SDHCI_DATA_LVL_MASK       0x00F00000
> > +#define   SDHCI_DATA_LVL_SHIFT     20
> >
> >  #define SDHCI_HOST_CONTROL         0x28
> >  #define  SDHCI_CTRL_LED            0x01
> > @@ -147,7 +149,8 @@
> >
> >  #define SDHCI_ACMD12_ERR   0x3C
> >
> > -/* 3E-3F reserved */
> > +#define SDHCI_HOST_CONTROL2                0x3E
> > +#define  SDHCI_CTRL_VDD_180                0x0008
> >
> >  #define SDHCI_CAPABILITIES 0x40
> >  #define  SDHCI_TIMEOUT_CLK_MASK    0x0000003F
> > @@ -168,9 +171,20 @@
> >  #define  SDHCI_CAN_VDD_180 0x04000000
> >  #define  SDHCI_CAN_64BIT   0x10000000
> >
> > +#define  SDHCI_SUPPORT_SDR50       0x00000001
> > +#define  SDHCI_SUPPORT_SDR104      0x00000002
> > +#define  SDHCI_SUPPORT_DDR50       0x00000004
> > +
> >  #define SDHCI_CAPABILITIES_1       0x44
> >
> > -#define SDHCI_MAX_CURRENT  0x48
> > +#define SDHCI_MAX_CURRENT          0x48
> > +#define  SDHCI_MAX_CURRENT_330_MASK        0x0000FF
> > +#define  SDHCI_MAX_CURRENT_330_SHIFT       0
> > +#define  SDHCI_MAX_CURRENT_300_MASK        0x00FF00
> > +#define  SDHCI_MAX_CURRENT_300_SHIFT       8
> > +#define  SDHCI_MAX_CURRENT_180_MASK        0xFF0000
> > +#define  SDHCI_MAX_CURRENT_180_SHIFT       16
> > +#define   SDHCI_MAX_CURRENT_MULTIPLIER     4
> >
> >  /* 4C-4F reserved for more max current */
> >
> > diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
> > index bcb793e..ec09b32 100644
> > --- a/include/linux/mmc/host.h
> > +++ b/include/linux/mmc/host.h
> > @@ -117,6 +117,8 @@ struct mmc_host_ops {
> >
> >     /* optional callback for HC quirks */
> >     void    (*init_card)(struct mmc_host *host, struct mmc_card *card);
> > +
> > +   int     (*start_signal_voltage_switch)(struct mmc_host *host);
> >  };
> >
> >  struct mmc_card;
> > @@ -173,6 +175,12 @@ struct mmc_host {
> >                                             /* DDR mode at 1.2V */
> >  #define MMC_CAP_POWER_OFF_CARD     (1 << 13)       /* Can power off
> after
> > boot */
> >  #define MMC_CAP_BUS_WIDTH_TEST     (1 << 14)       /* CMD14/CMD19 bus
> > width ok */
> > +#define MMC_CAP_UHS_SDR50  (1 << 15)       /* Host supports UHS SDR50
> > mode */
> > +#define MMC_CAP_UHS_SDR104 (1 << 16)       /* Host supports UHS SDR104
> > mode */
> > +#define MMC_CAP_UHS_DDR50  (1 << 17)       /* Host supports UHS DDR50
> > mode */
> > +#define MMC_CAP_SET_XPC_330        (1 << 18)       /* Host supports >150mA
> > current at 3.3V */
> > +#define MMC_CAP_SET_XPC_300        (1 << 19)       /* Host supports >150mA
> > current at 3.0V */
> > +#define MMC_CAP_SET_XPC_180        (1 << 20)       /* Host supports >150mA
> > current at 1.8V */
> >
> >     mmc_pm_flag_t           pm_caps;        /* supported pm features */
> >
> > diff --git a/include/linux/mmc/sd.h b/include/linux/mmc/sd.h
> > index 178363b..3ba5aa6 100644
> > --- a/include/linux/mmc/sd.h
> > +++ b/include/linux/mmc/sd.h
> > @@ -17,6 +17,7 @@
> >  /* This is basically the same command as for MMC with some quirks.
> */
> >  #define SD_SEND_RELATIVE_ADDR     3   /* bcr                     R6
> > */
> >  #define SD_SEND_IF_COND           8   /* bcr  [11:0] See below   R7
> > */
> > +#define SD_SWITCH_VOLTAGE         11  /* ac                      R1
> > */
> >
> >    /* class 10 */
> >  #define SD_SWITCH                 6   /* adtc [31:0] See below   R1
> > */
> > --
> > 1.7.1
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-mmc"
> in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at  http://vger.kernel.org/majordomo-info.html
>

Thanks,
Arindam


^ permalink raw reply	[flat|nested] 45+ messages in thread

* RE: [PATCH 02/12] mmc: sd: add support for signal voltage switch procedure
  2011-03-02  8:06     ` Nath, Arindam
@ 2011-03-03 13:16       ` subhashj
  2011-03-03 13:34         ` Nath, Arindam
  0 siblings, 1 reply; 45+ messages in thread
From: subhashj @ 2011-03-03 13:16 UTC (permalink / raw)
  To: 'Nath, Arindam', cjb
  Cc: linux-mmc, 'Su, Henry', 'Lu, Aaron', anath.amd



> -----Original Message-----
> From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> owner@vger.kernel.org] On Behalf Of Nath, Arindam
> Sent: Wednesday, March 02, 2011 1:36 PM
> To: subhashj@codeaurora.org; cjb@laptop.org
> Cc: linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
> anath.amd@gmail.com
> Subject: RE: [PATCH 02/12] mmc: sd: add support for signal voltage
> switch procedure
> 
> Hi Subhash,
> 
> 
> > -----Original Message-----
> > From: subhashj@codeaurora.org [mailto:subhashj@codeaurora.org]
> > Sent: Tuesday, March 01, 2011 9:33 PM
> > To: Nath, Arindam; cjb@laptop.org
> > Cc: linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
> > anath.amd@gmail.com
> > Subject: RE: [PATCH 02/12] mmc: sd: add support for signal voltage
> > switch procedure
> >
> >
> >
> > > -----Original Message-----
> > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > > owner@vger.kernel.org] On Behalf Of Arindam Nath
> > > Sent: Tuesday, February 15, 2011 3:05 PM
> > > To: cjb@laptop.org
> > > Cc: linux-mmc@vger.kernel.org; henry.su@amd.com; aaron.lu@amd.com;
> > > anath.amd@gmail.com; Arindam Nath
> > > Subject: [PATCH 02/12] mmc: sd: add support for signal voltage
> switch
> > > procedure
> > >
> > > Host Controller v3.00 adds another Capabilities register. We can
> > > directly read this register without any version checking since the
> > > new register bits will be considred as reserved in older
> controllers,
> > > and will be read as 0. Apart from other things, this new register
> > > indicates whether the Host Controller supports SDR50, SDR104, and
> > > DDR50 UHS-I modes. So depending on the host support, we set
> > > the corresponding MMC_CAP_* flags. One more new register. Host
> > Control2
> > > is added in v3.00, which is used during Signal Voltage Switch
> > prcedure
> > > described below.
> > >
> > > Since as per v3.00 spec, UHS-I supported hosts should set S18R to
> 1,
> > > we set S18R (bit 24) of OCR before sending ACMD41. We also need to
> > set
> > > XPC (bit 28) of OCR in case the host can supply >150mA. This
> support
> > is
> > > indicated by the Maximum Current Capabilities register of the Host
> > > Controller.
> > >
> > > If the response of ACMD41 has both CCS and S18A set, we start the
> > > signal voltage switch procedure, which if successfull, will switch
> > > the card from 3.3V signalling to 1.8V signalling. Signal voltage
> > > switch procedure adds support for a new command CMD11 in the
> Physical
> > > Layer Spec v3.01. As part of this procedure, we need to set 1.8V
> > > Signalling
> > > Enable (bit 3) of Host Control2 register, which if remains set
> after
> > > 5ms,
> > > means the switch to 1.8V signalling is successfull. Otherwise, we
> > clear
> > > bit 24 of OCR and retry the initialization sequence.
> > >
> > > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > > ---
> > >  drivers/mmc/core/sd.c     |   37 ++++++++++-
> > >  drivers/mmc/core/sd_ops.c |   25 ++++++++
> > >  drivers/mmc/core/sd_ops.h |    1 +
> > >  drivers/mmc/host/sdhci.c  |  147
> > > +++++++++++++++++++++++++++++++++++++++++----
> > >  drivers/mmc/host/sdhci.h  |   18 +++++-
> > >  include/linux/mmc/host.h  |    8 +++
> > >  include/linux/mmc/sd.h    |    1 +
> > >  7 files changed, 219 insertions(+), 18 deletions(-)
> > >
> > > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> > > index b3f8a3c..e968d5c 100644
> > > --- a/drivers/mmc/core/sd.c
> > > +++ b/drivers/mmc/core/sd.c
> > > @@ -408,6 +408,7 @@ struct device_type sd_type = {
> > >  int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid)
> > >  {
> > >     int err;
> > > +   u32 rocr;
> > >
> > >     /*
> > >      * Since we're changing the OCR value, we seem to
> > > @@ -427,10 +428,25 @@ int mmc_sd_get_cid(struct mmc_host *host, u32
> > > ocr, u32 *cid)
> > >     if (!err)
> > >             ocr |= 1 << 30;
> > >
> > > -   err = mmc_send_app_op_cond(host, ocr, NULL);
> > > +   /* If the host can supply more than 150mA, XPC should be set to
> > > 1. */
> > > +   if (host->caps & (MMC_CAP_SET_XPC_330 | MMC_CAP_SET_XPC_300 |
> > > +       MMC_CAP_SET_XPC_180))
> > > +           ocr |= 1 << 28;
> > > +
> >
> > Why do you need MMC_CAP_SET_XPC_300 here? MMC_CAP_SET_XPC_330 &
> > MMC_CAP_SET_XPC_180 is not enough?
> 
> According to the Host Controller Spec v3.00, section 2.2.26:
> 
> "If a Host System can afford more than 150mA, Host Driver set XPC to
> 1."
> 
> Now since >150mA can be set for 1.8V, 3.0V and 3.3V, the above
> condition checks for all these cases.
> 

3.0v and 3.3v is part of High voltage range supported by card (2.7v-3.3v).
Now if host supports any voltage from this range with 150ma driver strength 
then I think only config say MMC_CAP_SET_XPC_HV (high voltage) should be set
by
host and then only one check required here. Does it make sense?


> >
> > > +   err = mmc_send_app_op_cond(host, ocr, &rocr);
> > >     if (err)
> > >             return err;
> > >
> > > +   /*
> > > +    * In case CCS and S18A in the response is set, start Signal
> > > Voltage
> > > +    * Switch procedure. SPI mode doesn't support CMD11.
> > > +    */
> > > +   if (!mmc_host_is_spi(host) && (rocr & 0x41000000)) {
> > > +           err = mmc_start_voltage_switch(host);
> > > +           if (err)
> > > +                   return err;
> > > +   }
> > > +
> >
> > Looks good.
> >
> > >     if (mmc_host_is_spi(host))
> > >             err = mmc_send_cid(host, cid);
> > >     else
> > > @@ -827,11 +843,26 @@ int mmc_attach_sd(struct mmc_host *host)
> > >     }
> > >
> > >     /*
> > > +    * If the host supports one of UHS-I modes, request the card
> > > +    * to switch to 1.8V signaling level.
> > > +    */
> > > +   if (host->caps & (MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 |
> > > +       MMC_CAP_UHS_DDR50))
> > > +           host->ocr |= (1 << 24);
> >
> > What about SDR12 and SDR25 modes? Are we ignoring them?
> 
> Yes, point noted. I will fix this in V2. Thanks.
> 
> >
> > > +
> > > +   /*
> > >      * Detect and init the card.
> > >      */
> > >     err = mmc_sd_init_card(host, host->ocr, NULL);
> > > -   if (err)
> > > -           goto err;
> > > +   if (err == -EAGAIN) {
> > > +           /*
> > > +            * Retry initialization with S18R set to 0.
> > > +            */
> > > +           host->ocr &= ~(1 << 24);
> > > +           err = mmc_sd_init_card(host, host->ocr, NULL);
> > > +           if (err)
> > > +                   goto err;
> > > +   }
> > >
> > >     mmc_release_host(host);
> > >     err = mmc_add_card(host->card);
> > > diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c
> > > index 797cdb5..a0f97c9 100644
> > > --- a/drivers/mmc/core/sd_ops.c
> > > +++ b/drivers/mmc/core/sd_ops.c
> > > @@ -146,6 +146,31 @@ int mmc_app_set_bus_width(struct mmc_card
> *card,
> > > int width)
> > >     return 0;
> > >  }
> > >
> > > +int mmc_start_voltage_switch(struct mmc_host *host)
> > > +{
> > > +   struct mmc_command cmd;
> > > +   int err;
> > > +
> > > +   BUG_ON(!host);
> > > +
> > > +   memset(&cmd, 0, sizeof(struct mmc_command));
> > > +
> > > +   cmd.opcode = SD_SWITCH_VOLTAGE;
> > > +   cmd.arg = 0;
> > > +   cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
> > > +
> > > +   err = mmc_wait_for_cmd(host, &cmd, 0);
> > > +   if (err)
> > > +           return err;
> > > +
> > > +   if (!mmc_host_is_spi(host) && (cmd.resp[0] & R1_ERROR))
> > > +           return -EIO;
> > > +
> > > +   err = host->ops->start_signal_voltage_switch(host);
> >
> > No Null check for start_signal_voltage_switch ops?
> 
> Will fix this in V2.
> 
> >
> > > +
> > > +   return err;
> > > +}
> > > +
> > >  int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32
> *rocr)
> > >  {
> > >     struct mmc_command cmd;
> > > diff --git a/drivers/mmc/core/sd_ops.h b/drivers/mmc/core/sd_ops.h
> > > index ffc2305..3cfba59 100644
> > > --- a/drivers/mmc/core/sd_ops.h
> > > +++ b/drivers/mmc/core/sd_ops.h
> > > @@ -20,6 +20,7 @@ int mmc_app_send_scr(struct mmc_card *card, u32
> > > *scr);
> > >  int mmc_sd_switch(struct mmc_card *card, int mode, int group,
> > >     u8 value, u8 *resp);
> > >  int mmc_app_sd_status(struct mmc_card *card, void *ssr);
> > > +int mmc_start_voltage_switch(struct mmc_host *host);
> > >
> > >  #endif
> > >
> > > diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> > > index 0b537cf..6d7a276 100644
> > > --- a/drivers/mmc/host/sdhci.c
> > > +++ b/drivers/mmc/host/sdhci.c
> > > @@ -87,6 +87,8 @@ static void sdhci_dumpregs(struct sdhci_host
> *host)
> > >     printk(KERN_DEBUG DRIVER_NAME ": Cmd:      0x%08x | Max curr:
> > > 0x%08x\n",
> > >             sdhci_readw(host, SDHCI_COMMAND),
> > >             sdhci_readl(host, SDHCI_MAX_CURRENT));
> > > +   printk(KERN_DEBUG DRIVER_NAME ": Host ctl2: 0x%08x\n",
> > > +           sdhci_readw(host, SDHCI_HOST_CONTROL2));
> > >
> > >     if (host->flags & SDHCI_USE_ADMA)
> > >             printk(KERN_DEBUG DRIVER_NAME ": ADMA Err: 0x%08x |
> ADMA
> > > Ptr: 0x%08x\n",
> > > @@ -1340,11 +1342,76 @@ out:
> > >     spin_unlock_irqrestore(&host->lock, flags);
> > >  }
> > >
> > > +static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc)
> > > +{
> > > +   struct sdhci_host *host;
> > > +   u8 pwr;
> > > +   u16 clk, ctrl;
> > > +   u32 present_state;
> > > +   unsigned long flags;
> > > +
> > > +   host = mmc_priv(mmc);
> > > +
> > > +   spin_lock_irqsave(&host->lock, flags);
> > > +
> > > +   /* Stop SDCLK */
> > > +   host = mmc_priv(mmc);
> > > +   clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
> > > +   clk &= ~SDHCI_CLOCK_CARD_EN;
> > > +   sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
> > > +
> > > +   /* Check whether DAT[3:0] is 0000 */
> > > +   present_state = sdhci_readl(host, SDHCI_PRESENT_STATE);
> > > +   if (!((present_state & SDHCI_DATA_LVL_MASK) >>
> > > SDHCI_DATA_LVL_SHIFT)) {
> > > +           /* Enable 1.8V Signal Enable in the Host Control2
> register
> > > */
> > > +           ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> > > +           ctrl |= SDHCI_CTRL_VDD_180;
> > > +           sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> > > +
> > > +           /* Wait for 5ms */
> > > +           mdelay(5);
> >
> > msleep would be more appropriate here.
> 
> Nicolas already commented on this one. I will take care of it in V2.
> 
> >
> > > +
> > > +           ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> > > +           if (ctrl & SDHCI_CTRL_VDD_180) {
> > > +                   /* Provide SDCLK again and wait for 1ms*/
> > > +                   clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
> > > +                   clk |= SDHCI_CLOCK_CARD_EN;
> > > +                   sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
> > > +                   mdelay(1);
> > > +
> > > +                   /*
> > > +                    * If DAT[3:0] level is 1111b, then the card
> was
> > > +                    * successfully switched to 1.8V signaling.
> > > +                    */
> > > +                   present_state = sdhci_readl(host,
> > > SDHCI_PRESENT_STATE);
> > > +                   if ((present_state & SDHCI_DATA_LVL_MASK) ==
> > > +                        SDHCI_DATA_LVL_MASK) {
> > > +                           spin_unlock_irqrestore(&host->lock,
> flags);
> > > +                           return 0;
> > > +                   }
> >
> > Sequence looks fine according to spec.
> >
> >
> > > +           }
> > > +   }
> > > +
> > > +   /*
> > > +    * If we are here, that means the switch to 1.8V signaling
> > > failed. Stop
> > > +    * power to the card, and retry initialization sequence by
> > > setting S18R
> > > +    * to 0.
> > > +    */
> > > +   pwr = sdhci_readb(host, SDHCI_POWER_CONTROL);
> > > +   pwr &= ~SDHCI_POWER_ON;
> > > +   sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
> > > +
> > > +   spin_unlock_irqrestore(&host->lock, flags);
> > > +
> > > +   return -EAGAIN;
> > > +}
> > > +
> > >  static const struct mmc_host_ops sdhci_ops = {
> > >     .request        = sdhci_request,
> > >     .set_ios        = sdhci_set_ios,
> > >     .get_ro         = sdhci_get_ro,
> > >     .enable_sdio_irq = sdhci_enable_sdio_irq,
> > > +   .start_signal_voltage_switch    =
> > > sdhci_start_signal_voltage_switch,
> > >  };
> >
> > If possible can you shorthand ops name "start_signal_voltage_switch"?
> 
> I wanted the name to be descriptive, so couldn't think of anything
> else. Can you suggest something?

I cannot think of other name. you can keep this name unless someone suggest
Better name.


> 
> >
> >
> > >
> > >
> > >
> >
> /**********************************************************************
> > > *******\
> > > @@ -1802,7 +1869,9 @@ EXPORT_SYMBOL_GPL(sdhci_alloc_host);
> > >  int sdhci_add_host(struct sdhci_host *host)
> > >  {
> > >     struct mmc_host *mmc;
> > > -   unsigned int caps, ocr_avail;
> > > +   u32 caps[2];
> > > +   u32 max_current_caps;
> > > +   unsigned int ocr_avail;
> > >     int ret;
> > >
> > >     WARN_ON(host == NULL);
> > > @@ -1825,12 +1894,13 @@ int sdhci_add_host(struct sdhci_host *host)
> > >                     host->version);
> > >     }
> > >
> > > -   caps = (host->quirks & SDHCI_QUIRK_MISSING_CAPS) ? host->caps :
> > > +   caps[0] = (host->quirks & SDHCI_QUIRK_MISSING_CAPS) ? host-
> >caps
> > > :
> > >             sdhci_readl(host, SDHCI_CAPABILITIES);
> > > +   caps[1] = sdhci_readl(host, SDHCI_CAPABILITIES_1);
> > >
> > >     if (host->quirks & SDHCI_QUIRK_FORCE_DMA)
> > >             host->flags |= SDHCI_USE_SDMA;
> > > -   else if (!(caps & SDHCI_CAN_DO_SDMA))
> > > +   else if (!(caps[0] & SDHCI_CAN_DO_SDMA))
> > >             DBG("Controller doesn't have SDMA capability\n");
> > >     else
> > >             host->flags |= SDHCI_USE_SDMA;
> > > @@ -1841,7 +1911,8 @@ int sdhci_add_host(struct sdhci_host *host)
> > >             host->flags &= ~SDHCI_USE_SDMA;
> > >     }
> > >
> > > -   if ((host->version >= SDHCI_SPEC_200) && (caps &
> > > SDHCI_CAN_DO_ADMA2)) {
> > > +   if ((host->version >= SDHCI_SPEC_200) &&
> > > +           (caps[0] & SDHCI_CAN_DO_ADMA2)) {
> > >             host->flags &= ~SDHCI_USE_SDMA;
> > >             host->flags |= SDHCI_USE_ADMA;
> > >     }
> > > @@ -1893,10 +1964,10 @@ int sdhci_add_host(struct sdhci_host *host)
> > >     }
> > >
> > >     if (host->version >= SDHCI_SPEC_300)
> > > -           host->max_clk = (caps & SDHCI_CLOCK_V3_BASE_MASK)
> > > +           host->max_clk = (caps[0] & SDHCI_CLOCK_V3_BASE_MASK)
> > >                     >> SDHCI_CLOCK_BASE_SHIFT;
> > >     else
> > > -           host->max_clk = (caps & SDHCI_CLOCK_BASE_MASK)
> > > +           host->max_clk = (caps[0] & SDHCI_CLOCK_BASE_MASK)
> > >                     >> SDHCI_CLOCK_BASE_SHIFT;
> > >
> > >     host->max_clk *= 1000000;
> > > @@ -1912,7 +1983,7 @@ int sdhci_add_host(struct sdhci_host *host)
> > >     }
> > >
> > >     host->timeout_clk =
> > > -           (caps & SDHCI_TIMEOUT_CLK_MASK) >>
> SDHCI_TIMEOUT_CLK_SHIFT;
> > > +           (caps[0] & SDHCI_TIMEOUT_CLK_MASK) >>
> > > SDHCI_TIMEOUT_CLK_SHIFT;
> > >     if (host->timeout_clk == 0) {
> > >             if (host->ops->get_timeout_clock) {
> > >                     host->timeout_clk = host->ops-
> > > >get_timeout_clock(host);
> > > @@ -1924,7 +1995,7 @@ int sdhci_add_host(struct sdhci_host *host)
> > >                     return -ENODEV;
> > >             }
> > >     }
> > > -   if (caps & SDHCI_TIMEOUT_CLK_UNIT)
> > > +   if (caps[0] & SDHCI_TIMEOUT_CLK_UNIT)
> > >             host->timeout_clk *= 1000;
> > >
> > >     /*
> > > @@ -1951,21 +2022,71 @@ int sdhci_add_host(struct sdhci_host *host)
> > >     if (!(host->quirks & SDHCI_QUIRK_FORCE_1_BIT_DATA))
> > >             mmc->caps |= MMC_CAP_4_BIT_DATA;
> > >
> > > -   if (caps & SDHCI_CAN_DO_HISPD)
> > > +   if (caps[0] & SDHCI_CAN_DO_HISPD)
> > >             mmc->caps |= MMC_CAP_SD_HIGHSPEED |
> MMC_CAP_MMC_HIGHSPEED;
> > >
> > >     if ((host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) &&
> > >         mmc_card_is_removable(mmc))
> > >             mmc->caps |= MMC_CAP_NEEDS_POLL;
> > >
> > > +   /* UHS-I mode(s) supported by the host controller. */
> > > +   if (caps[1] & SDHCI_SUPPORT_SDR50)
> > > +           mmc->caps |= MMC_CAP_UHS_SDR50;
> > > +   if (caps[1] & SDHCI_SUPPORT_SDR104)
> > > +           mmc->caps |= MMC_CAP_UHS_SDR104;
> >
> > If SDR104 is supported then that SDR50 is by default supported. You
> may
> > want
> > to take care of that.
> 
> As per the spec, for SDR50 support:
> 
> "If SDR104 is supported, this bit shall be set to 1."
> 
> Since the spec uses the word _shall_, I thought it is more of a
> suggestion than a requirement. Please correct me if wrong.

Ok.

> 
> >
> > > +   if (caps[1] & SDHCI_SUPPORT_DDR50)
> > > +           mmc->caps |= MMC_CAP_UHS_DDR50;
> > > +
> > >     ocr_avail = 0;
> > > -   if (caps & SDHCI_CAN_VDD_330)
> > > +   /*
> > > +    * According to SD Host Controller spec v3.00, if the Host
> System
> > > +    * can afford more than 150mA, Host Driver should set XPC to 1.
> > > Also
> > > +    * the value is meaningful only if Voltage Support in the
> > > Capabilities
> > > +    * register is set. The actual current value is 4 times the
> > > register
> > > +    * value.
> > > +    */
> > > +   max_current_caps = sdhci_readl(host, SDHCI_MAX_CURRENT);
> > > +
> > > +   if (caps[0] & SDHCI_CAN_VDD_330) {
> > > +           int max_current_330;
> > > +
> > >             ocr_avail |= MMC_VDD_32_33 | MMC_VDD_33_34;
> > > -   if (caps & SDHCI_CAN_VDD_300)
> > > +
> > > +           max_current_330 = ((max_current_caps &
> > > +                              SDHCI_MAX_CURRENT_330_MASK) >>
> > > +                              SDHCI_MAX_CURRENT_330_SHIFT) *
> > > +                              SDHCI_MAX_CURRENT_MULTIPLIER;
> > > +
> > > +           if (max_current_330 > 150)
> > > +                   mmc->caps |= MMC_CAP_SET_XPC_330;
> > > +   }
> > > +   if (caps[0] & SDHCI_CAN_VDD_300) {
> > > +           int max_current_300;
> > > +
> > >             ocr_avail |= MMC_VDD_29_30 | MMC_VDD_30_31;
> > > -   if (caps & SDHCI_CAN_VDD_180)
> > > +
> > > +           max_current_300 = ((max_current_caps &
> > > +                              SDHCI_MAX_CURRENT_300_MASK) >>
> > > +                              SDHCI_MAX_CURRENT_300_SHIFT) *
> > > +                              SDHCI_MAX_CURRENT_MULTIPLIER;
> > > +
> > > +           if (max_current_300 > 150)
> > > +                   mmc->caps |= MMC_CAP_SET_XPC_300;
> > > +   }
> > > +   if (caps[0] & SDHCI_CAN_VDD_180) {
> > > +           int max_current_180;
> > > +
> > >             ocr_avail |= MMC_VDD_165_195;
> > >
> > > +           max_current_180 = ((max_current_caps &
> > > +                              SDHCI_MAX_CURRENT_180_MASK) >>
> > > +                              SDHCI_MAX_CURRENT_180_SHIFT) *
> > > +                              SDHCI_MAX_CURRENT_MULTIPLIER;
> > > +
> > > +           if (max_current_180 > 150)
> > > +                   mmc->caps |= MMC_CAP_SET_XPC_180;
> > > +   }
> > > +
> > >     mmc->ocr_avail = ocr_avail;
> > >     mmc->ocr_avail_sdio = ocr_avail;
> > >     if (host->ocr_avail_sdio)
> > > @@ -2025,7 +2146,7 @@ int sdhci_add_host(struct sdhci_host *host)
> > >     if (host->quirks & SDHCI_QUIRK_FORCE_BLK_SZ_2048) {
> > >             mmc->max_blk_size = 2;
> > >     } else {
> > > -           mmc->max_blk_size = (caps & SDHCI_MAX_BLOCK_MASK) >>
> > > +           mmc->max_blk_size = (caps[0] & SDHCI_MAX_BLOCK_MASK) >>
> > >                             SDHCI_MAX_BLOCK_SHIFT;
> > >             if (mmc->max_blk_size >= 3) {
> > >                     printk(KERN_WARNING "%s: Invalid maximum block
> size,
> > > "
> > > diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
> > > index 223762c..95d70e6 100644
> > > --- a/drivers/mmc/host/sdhci.h
> > > +++ b/drivers/mmc/host/sdhci.h
> > > @@ -69,6 +69,8 @@
> > >  #define  SDHCI_DATA_AVAILABLE      0x00000800
> > >  #define  SDHCI_CARD_PRESENT        0x00010000
> > >  #define  SDHCI_WRITE_PROTECT       0x00080000
> > > +#define  SDHCI_DATA_LVL_MASK       0x00F00000
> > > +#define   SDHCI_DATA_LVL_SHIFT     20
> > >
> > >  #define SDHCI_HOST_CONTROL         0x28
> > >  #define  SDHCI_CTRL_LED            0x01
> > > @@ -147,7 +149,8 @@
> > >
> > >  #define SDHCI_ACMD12_ERR   0x3C
> > >
> > > -/* 3E-3F reserved */
> > > +#define SDHCI_HOST_CONTROL2                0x3E
> > > +#define  SDHCI_CTRL_VDD_180                0x0008
> > >
> > >  #define SDHCI_CAPABILITIES 0x40
> > >  #define  SDHCI_TIMEOUT_CLK_MASK    0x0000003F
> > > @@ -168,9 +171,20 @@
> > >  #define  SDHCI_CAN_VDD_180 0x04000000
> > >  #define  SDHCI_CAN_64BIT   0x10000000
> > >
> > > +#define  SDHCI_SUPPORT_SDR50       0x00000001
> > > +#define  SDHCI_SUPPORT_SDR104      0x00000002
> > > +#define  SDHCI_SUPPORT_DDR50       0x00000004
> > > +
> > >  #define SDHCI_CAPABILITIES_1       0x44
> > >
> > > -#define SDHCI_MAX_CURRENT  0x48
> > > +#define SDHCI_MAX_CURRENT          0x48
> > > +#define  SDHCI_MAX_CURRENT_330_MASK        0x0000FF
> > > +#define  SDHCI_MAX_CURRENT_330_SHIFT       0
> > > +#define  SDHCI_MAX_CURRENT_300_MASK        0x00FF00
> > > +#define  SDHCI_MAX_CURRENT_300_SHIFT       8
> > > +#define  SDHCI_MAX_CURRENT_180_MASK        0xFF0000
> > > +#define  SDHCI_MAX_CURRENT_180_SHIFT       16
> > > +#define   SDHCI_MAX_CURRENT_MULTIPLIER     4
> > >
> > >  /* 4C-4F reserved for more max current */
> > >
> > > diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
> > > index bcb793e..ec09b32 100644
> > > --- a/include/linux/mmc/host.h
> > > +++ b/include/linux/mmc/host.h
> > > @@ -117,6 +117,8 @@ struct mmc_host_ops {
> > >
> > >     /* optional callback for HC quirks */
> > >     void    (*init_card)(struct mmc_host *host, struct mmc_card
> *card);
> > > +
> > > +   int     (*start_signal_voltage_switch)(struct mmc_host *host);
> > >  };
> > >
> > >  struct mmc_card;
> > > @@ -173,6 +175,12 @@ struct mmc_host {
> > >                                             /* DDR mode at 1.2V */
> > >  #define MMC_CAP_POWER_OFF_CARD     (1 << 13)       /* Can power
> off
> > after
> > > boot */
> > >  #define MMC_CAP_BUS_WIDTH_TEST     (1 << 14)       /* CMD14/CMD19
> bus
> > > width ok */
> > > +#define MMC_CAP_UHS_SDR50  (1 << 15)       /* Host supports UHS
> SDR50
> > > mode */
> > > +#define MMC_CAP_UHS_SDR104 (1 << 16)       /* Host supports UHS
> SDR104
> > > mode */
> > > +#define MMC_CAP_UHS_DDR50  (1 << 17)       /* Host supports UHS
> DDR50
> > > mode */
> > > +#define MMC_CAP_SET_XPC_330        (1 << 18)       /* Host
> supports >150mA
> > > current at 3.3V */
> > > +#define MMC_CAP_SET_XPC_300        (1 << 19)       /* Host
> supports >150mA
> > > current at 3.0V */
> > > +#define MMC_CAP_SET_XPC_180        (1 << 20)       /* Host
> supports >150mA
> > > current at 1.8V */
> > >
> > >     mmc_pm_flag_t           pm_caps;        /* supported pm
> features */
> > >
> > > diff --git a/include/linux/mmc/sd.h b/include/linux/mmc/sd.h
> > > index 178363b..3ba5aa6 100644
> > > --- a/include/linux/mmc/sd.h
> > > +++ b/include/linux/mmc/sd.h
> > > @@ -17,6 +17,7 @@
> > >  /* This is basically the same command as for MMC with some quirks.
> > */
> > >  #define SD_SEND_RELATIVE_ADDR     3   /* bcr
> R6
> > > */
> > >  #define SD_SEND_IF_COND           8   /* bcr  [11:0] See below
> R7
> > > */
> > > +#define SD_SWITCH_VOLTAGE         11  /* ac
> R1
> > > */
> > >
> > >    /* class 10 */
> > >  #define SD_SWITCH                 6   /* adtc [31:0] See below
> R1
> > > */
> > > --
> > > 1.7.1
> > >
> > > --
> > > To unsubscribe from this list: send the line "unsubscribe linux-
> mmc"
> > in
> > > the body of a message to majordomo@vger.kernel.org
> > > More majordomo info at  http://vger.kernel.org/majordomo-info.html
> >
> 
> Thanks,
> Arindam
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


^ permalink raw reply	[flat|nested] 45+ messages in thread

* RE: [PATCH 02/12] mmc: sd: add support for signal voltage switch procedure
  2011-03-03 13:16       ` subhashj
@ 2011-03-03 13:34         ` Nath, Arindam
  2011-03-03 21:08           ` subhashj
  0 siblings, 1 reply; 45+ messages in thread
From: Nath, Arindam @ 2011-03-03 13:34 UTC (permalink / raw)
  To: subhashj, cjb; +Cc: linux-mmc, Su, Henry, Lu, Aaron, anath.amd

Hi Subhash,

> -----Original Message-----
> From: subhashj@codeaurora.org [mailto:subhashj@codeaurora.org]
> Sent: Thursday, March 03, 2011 6:46 PM
> To: Nath, Arindam; cjb@laptop.org
> Cc: linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
> anath.amd@gmail.com
> Subject: RE: [PATCH 02/12] mmc: sd: add support for signal voltage
> switch procedure
> 
> 
> 
> > -----Original Message-----
> > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > owner@vger.kernel.org] On Behalf Of Nath, Arindam
> > Sent: Wednesday, March 02, 2011 1:36 PM
> > To: subhashj@codeaurora.org; cjb@laptop.org
> > Cc: linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
> > anath.amd@gmail.com
> > Subject: RE: [PATCH 02/12] mmc: sd: add support for signal voltage
> > switch procedure
> >
> > Hi Subhash,
> >
> >
> > > -----Original Message-----
> > > From: subhashj@codeaurora.org [mailto:subhashj@codeaurora.org]
> > > Sent: Tuesday, March 01, 2011 9:33 PM
> > > To: Nath, Arindam; cjb@laptop.org
> > > Cc: linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
> > > anath.amd@gmail.com
> > > Subject: RE: [PATCH 02/12] mmc: sd: add support for signal voltage
> > > switch procedure
> > >
> > >
> > >
> > > > -----Original Message-----
> > > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > > > owner@vger.kernel.org] On Behalf Of Arindam Nath
> > > > Sent: Tuesday, February 15, 2011 3:05 PM
> > > > To: cjb@laptop.org
> > > > Cc: linux-mmc@vger.kernel.org; henry.su@amd.com;
> aaron.lu@amd.com;
> > > > anath.amd@gmail.com; Arindam Nath
> > > > Subject: [PATCH 02/12] mmc: sd: add support for signal voltage
> > switch
> > > > procedure
> > > >
> > > > Host Controller v3.00 adds another Capabilities register. We can
> > > > directly read this register without any version checking since
> the
> > > > new register bits will be considred as reserved in older
> > controllers,
> > > > and will be read as 0. Apart from other things, this new register
> > > > indicates whether the Host Controller supports SDR50, SDR104, and
> > > > DDR50 UHS-I modes. So depending on the host support, we set
> > > > the corresponding MMC_CAP_* flags. One more new register. Host
> > > Control2
> > > > is added in v3.00, which is used during Signal Voltage Switch
> > > prcedure
> > > > described below.
> > > >
> > > > Since as per v3.00 spec, UHS-I supported hosts should set S18R to
> > 1,
> > > > we set S18R (bit 24) of OCR before sending ACMD41. We also need
> to
> > > set
> > > > XPC (bit 28) of OCR in case the host can supply >150mA. This
> > support
> > > is
> > > > indicated by the Maximum Current Capabilities register of the
> Host
> > > > Controller.
> > > >
> > > > If the response of ACMD41 has both CCS and S18A set, we start the
> > > > signal voltage switch procedure, which if successfull, will
> switch
> > > > the card from 3.3V signalling to 1.8V signalling. Signal voltage
> > > > switch procedure adds support for a new command CMD11 in the
> > Physical
> > > > Layer Spec v3.01. As part of this procedure, we need to set 1.8V
> > > > Signalling
> > > > Enable (bit 3) of Host Control2 register, which if remains set
> > after
> > > > 5ms,
> > > > means the switch to 1.8V signalling is successfull. Otherwise, we
> > > clear
> > > > bit 24 of OCR and retry the initialization sequence.
> > > >
> > > > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > > > ---
> > > >  drivers/mmc/core/sd.c     |   37 ++++++++++-
> > > >  drivers/mmc/core/sd_ops.c |   25 ++++++++
> > > >  drivers/mmc/core/sd_ops.h |    1 +
> > > >  drivers/mmc/host/sdhci.c  |  147
> > > > +++++++++++++++++++++++++++++++++++++++++----
> > > >  drivers/mmc/host/sdhci.h  |   18 +++++-
> > > >  include/linux/mmc/host.h  |    8 +++
> > > >  include/linux/mmc/sd.h    |    1 +
> > > >  7 files changed, 219 insertions(+), 18 deletions(-)
> > > >
> > > > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> > > > index b3f8a3c..e968d5c 100644
> > > > --- a/drivers/mmc/core/sd.c
> > > > +++ b/drivers/mmc/core/sd.c
> > > > @@ -408,6 +408,7 @@ struct device_type sd_type = {
> > > >  int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid)
> > > >  {
> > > >     int err;
> > > > +   u32 rocr;
> > > >
> > > >     /*
> > > >      * Since we're changing the OCR value, we seem to
> > > > @@ -427,10 +428,25 @@ int mmc_sd_get_cid(struct mmc_host *host,
> u32
> > > > ocr, u32 *cid)
> > > >     if (!err)
> > > >             ocr |= 1 << 30;
> > > >
> > > > -   err = mmc_send_app_op_cond(host, ocr, NULL);
> > > > +   /* If the host can supply more than 150mA, XPC should be set
> to
> > > > 1. */
> > > > +   if (host->caps & (MMC_CAP_SET_XPC_330 | MMC_CAP_SET_XPC_300 |
> > > > +       MMC_CAP_SET_XPC_180))
> > > > +           ocr |= 1 << 28;
> > > > +
> > >
> > > Why do you need MMC_CAP_SET_XPC_300 here? MMC_CAP_SET_XPC_330 &
> > > MMC_CAP_SET_XPC_180 is not enough?
> >
> > According to the Host Controller Spec v3.00, section 2.2.26:
> >
> > "If a Host System can afford more than 150mA, Host Driver set XPC to
> > 1."
> >
> > Now since >150mA can be set for 1.8V, 3.0V and 3.3V, the above
> > condition checks for all these cases.
> >
> 
> 3.0v and 3.3v is part of High voltage range supported by card (2.7v-
> 3.3v).
> Now if host supports any voltage from this range with 150ma driver
> strength
> then I think only config say MMC_CAP_SET_XPC_HV (high voltage) should
> be set
> by
> host and then only one check required here. Does it make sense?

You have a valid point here. But I kept the configs *XPC_300 and *XPC_330 separate so that in future if someone wants to base his/her code on either of the flags being set, but not both, they can easily do so. If you still think it is a good idea to have a common *CAP for both of them, please let me know. I will modify the code accordingly.

Thanks,
Arindam


^ permalink raw reply	[flat|nested] 45+ messages in thread

* RE: [PATCH 02/12] mmc: sd: add support for signal voltage switch procedure
  2011-03-03 13:34         ` Nath, Arindam
@ 2011-03-03 21:08           ` subhashj
  2011-03-03 22:49             ` Philip Rakity
  0 siblings, 1 reply; 45+ messages in thread
From: subhashj @ 2011-03-03 21:08 UTC (permalink / raw)
  To: 'Nath, Arindam', cjb
  Cc: linux-mmc, 'Su, Henry', 'Lu, Aaron', anath.amd


> -----Original Message-----
> From: Nath, Arindam [mailto:Arindam.Nath@amd.com]
> Sent: Thursday, March 03, 2011 7:05 PM
> To: subhashj@codeaurora.org; cjb@laptop.org
> Cc: linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
> anath.amd@gmail.com
> Subject: RE: [PATCH 02/12] mmc: sd: add support for signal voltage
> switch procedure
> 
> Hi Subhash,
> 
> > -----Original Message-----
> > From: subhashj@codeaurora.org [mailto:subhashj@codeaurora.org]
> > Sent: Thursday, March 03, 2011 6:46 PM
> > To: Nath, Arindam; cjb@laptop.org
> > Cc: linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
> > anath.amd@gmail.com
> > Subject: RE: [PATCH 02/12] mmc: sd: add support for signal voltage
> > switch procedure
> >
> >
> >
> > > -----Original Message-----
> > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > > owner@vger.kernel.org] On Behalf Of Nath, Arindam
> > > Sent: Wednesday, March 02, 2011 1:36 PM
> > > To: subhashj@codeaurora.org; cjb@laptop.org
> > > Cc: linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
> > > anath.amd@gmail.com
> > > Subject: RE: [PATCH 02/12] mmc: sd: add support for signal voltage
> > > switch procedure
> > >
> > > Hi Subhash,
> > >
> > >
> > > > -----Original Message-----
> > > > From: subhashj@codeaurora.org [mailto:subhashj@codeaurora.org]
> > > > Sent: Tuesday, March 01, 2011 9:33 PM
> > > > To: Nath, Arindam; cjb@laptop.org
> > > > Cc: linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
> > > > anath.amd@gmail.com
> > > > Subject: RE: [PATCH 02/12] mmc: sd: add support for signal
> voltage
> > > > switch procedure
> > > >
> > > >
> > > >
> > > > > -----Original Message-----
> > > > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > > > > owner@vger.kernel.org] On Behalf Of Arindam Nath
> > > > > Sent: Tuesday, February 15, 2011 3:05 PM
> > > > > To: cjb@laptop.org
> > > > > Cc: linux-mmc@vger.kernel.org; henry.su@amd.com;
> > aaron.lu@amd.com;
> > > > > anath.amd@gmail.com; Arindam Nath
> > > > > Subject: [PATCH 02/12] mmc: sd: add support for signal voltage
> > > switch
> > > > > procedure
> > > > >
> > > > > Host Controller v3.00 adds another Capabilities register. We
> can
> > > > > directly read this register without any version checking since
> > the
> > > > > new register bits will be considred as reserved in older
> > > controllers,
> > > > > and will be read as 0. Apart from other things, this new
> register
> > > > > indicates whether the Host Controller supports SDR50, SDR104,
> and
> > > > > DDR50 UHS-I modes. So depending on the host support, we set
> > > > > the corresponding MMC_CAP_* flags. One more new register. Host
> > > > Control2
> > > > > is added in v3.00, which is used during Signal Voltage Switch
> > > > prcedure
> > > > > described below.
> > > > >
> > > > > Since as per v3.00 spec, UHS-I supported hosts should set S18R
> to
> > > 1,
> > > > > we set S18R (bit 24) of OCR before sending ACMD41. We also need
> > to
> > > > set
> > > > > XPC (bit 28) of OCR in case the host can supply >150mA. This
> > > support
> > > > is
> > > > > indicated by the Maximum Current Capabilities register of the
> > Host
> > > > > Controller.
> > > > >
> > > > > If the response of ACMD41 has both CCS and S18A set, we start
> the
> > > > > signal voltage switch procedure, which if successfull, will
> > switch
> > > > > the card from 3.3V signalling to 1.8V signalling. Signal
> voltage
> > > > > switch procedure adds support for a new command CMD11 in the
> > > Physical
> > > > > Layer Spec v3.01. As part of this procedure, we need to set
> 1.8V
> > > > > Signalling
> > > > > Enable (bit 3) of Host Control2 register, which if remains set
> > > after
> > > > > 5ms,
> > > > > means the switch to 1.8V signalling is successfull. Otherwise,
> we
> > > > clear
> > > > > bit 24 of OCR and retry the initialization sequence.
> > > > >
> > > > > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > > > > ---
> > > > >  drivers/mmc/core/sd.c     |   37 ++++++++++-
> > > > >  drivers/mmc/core/sd_ops.c |   25 ++++++++
> > > > >  drivers/mmc/core/sd_ops.h |    1 +
> > > > >  drivers/mmc/host/sdhci.c  |  147
> > > > > +++++++++++++++++++++++++++++++++++++++++----
> > > > >  drivers/mmc/host/sdhci.h  |   18 +++++-
> > > > >  include/linux/mmc/host.h  |    8 +++
> > > > >  include/linux/mmc/sd.h    |    1 +
> > > > >  7 files changed, 219 insertions(+), 18 deletions(-)
> > > > >
> > > > > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> > > > > index b3f8a3c..e968d5c 100644
> > > > > --- a/drivers/mmc/core/sd.c
> > > > > +++ b/drivers/mmc/core/sd.c
> > > > > @@ -408,6 +408,7 @@ struct device_type sd_type = {
> > > > >  int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid)
> > > > >  {
> > > > >     int err;
> > > > > +   u32 rocr;
> > > > >
> > > > >     /*
> > > > >      * Since we're changing the OCR value, we seem to
> > > > > @@ -427,10 +428,25 @@ int mmc_sd_get_cid(struct mmc_host *host,
> > u32
> > > > > ocr, u32 *cid)
> > > > >     if (!err)
> > > > >             ocr |= 1 << 30;
> > > > >
> > > > > -   err = mmc_send_app_op_cond(host, ocr, NULL);
> > > > > +   /* If the host can supply more than 150mA, XPC should be
> set
> > to
> > > > > 1. */
> > > > > +   if (host->caps & (MMC_CAP_SET_XPC_330 | MMC_CAP_SET_XPC_300
> |
> > > > > +       MMC_CAP_SET_XPC_180))
> > > > > +           ocr |= 1 << 28;
> > > > > +
> > > >
> > > > Why do you need MMC_CAP_SET_XPC_300 here? MMC_CAP_SET_XPC_330 &
> > > > MMC_CAP_SET_XPC_180 is not enough?
> > >
> > > According to the Host Controller Spec v3.00, section 2.2.26:
> > >
> > > "If a Host System can afford more than 150mA, Host Driver set XPC
> to
> > > 1."
> > >
> > > Now since >150mA can be set for 1.8V, 3.0V and 3.3V, the above
> > > condition checks for all these cases.
> > >
> >
> > 3.0v and 3.3v is part of High voltage range supported by card (2.7v-
> > 3.3v).
> > Now if host supports any voltage from this range with 150ma driver
> > strength
> > then I think only config say MMC_CAP_SET_XPC_HV (high voltage) should
> > be set
> > by
> > host and then only one check required here. Does it make sense?
> 
> You have a valid point here. But I kept the configs *XPC_300 and
> *XPC_330 separate so that in future if someone wants to base his/her
> code on either of the flags being set, but not both, they can easily do
> so. If you still think it is a good idea to have a common *CAP for both
> of them, please let me know. I will modify the code accordingly.
> 

Ok. it's fine. you keep them as 2 separate caps.

Thanks,
Subhash
	
> Thanks,
> Arindam



^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 02/12] mmc: sd: add support for signal voltage switch procedure
  2011-03-03 21:08           ` subhashj
@ 2011-03-03 22:49             ` Philip Rakity
  2011-03-04  6:10               ` Nath, Arindam
  0 siblings, 1 reply; 45+ messages in thread
From: Philip Rakity @ 2011-03-03 22:49 UTC (permalink / raw)
  To: Arindam Nath, anath.amd
  Cc: Chris Ball, linux-mmc, Henry Su, Aaron Lu, subhashj@codeaurora.org>


Anath,

Is it sufficient to check the CAPS for voltage etc ?  Now that we have a regulator framework do we need to also look at that ?

Philip



On Mar 3, 2011, at 1:08 PM, <subhashj@codeaurora.org> <subhashj@codeaurora.org> wrote:

> 
>> -----Original Message-----
>> From: Nath, Arindam [mailto:Arindam.Nath@amd.com]
>> Sent: Thursday, March 03, 2011 7:05 PM
>> To: subhashj@codeaurora.org; cjb@laptop.org
>> Cc: linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
>> anath.amd@gmail.com
>> Subject: RE: [PATCH 02/12] mmc: sd: add support for signal voltage
>> switch procedure
>> 
>> Hi Subhash,
>> 
>>> -----Original Message-----
>>> From: subhashj@codeaurora.org [mailto:subhashj@codeaurora.org]
>>> Sent: Thursday, March 03, 2011 6:46 PM
>>> To: Nath, Arindam; cjb@laptop.org
>>> Cc: linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
>>> anath.amd@gmail.com
>>> Subject: RE: [PATCH 02/12] mmc: sd: add support for signal voltage
>>> switch procedure
>>> 
>>> 
>>> 
>>>> -----Original Message-----
>>>> From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
>>>> owner@vger.kernel.org] On Behalf Of Nath, Arindam
>>>> Sent: Wednesday, March 02, 2011 1:36 PM
>>>> To: subhashj@codeaurora.org; cjb@laptop.org
>>>> Cc: linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
>>>> anath.amd@gmail.com
>>>> Subject: RE: [PATCH 02/12] mmc: sd: add support for signal voltage
>>>> switch procedure
>>>> 
>>>> Hi Subhash,
>>>> 
>>>> 
>>>>> -----Original Message-----
>>>>> From: subhashj@codeaurora.org [mailto:subhashj@codeaurora.org]
>>>>> Sent: Tuesday, March 01, 2011 9:33 PM
>>>>> To: Nath, Arindam; cjb@laptop.org
>>>>> Cc: linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
>>>>> anath.amd@gmail.com
>>>>> Subject: RE: [PATCH 02/12] mmc: sd: add support for signal
>> voltage
>>>>> switch procedure
>>>>> 
>>>>> 
>>>>> 
>>>>>> -----Original Message-----
>>>>>> From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
>>>>>> owner@vger.kernel.org] On Behalf Of Arindam Nath
>>>>>> Sent: Tuesday, February 15, 2011 3:05 PM
>>>>>> To: cjb@laptop.org
>>>>>> Cc: linux-mmc@vger.kernel.org; henry.su@amd.com;
>>> aaron.lu@amd.com;
>>>>>> anath.amd@gmail.com; Arindam Nath
>>>>>> Subject: [PATCH 02/12] mmc: sd: add support for signal voltage
>>>> switch
>>>>>> procedure
>>>>>> 
>>>>>> Host Controller v3.00 adds another Capabilities register. We
>> can
>>>>>> directly read this register without any version checking since
>>> the
>>>>>> new register bits will be considred as reserved in older
>>>> controllers,
>>>>>> and will be read as 0. Apart from other things, this new
>> register
>>>>>> indicates whether the Host Controller supports SDR50, SDR104,
>> and
>>>>>> DDR50 UHS-I modes. So depending on the host support, we set
>>>>>> the corresponding MMC_CAP_* flags. One more new register. Host
>>>>> Control2
>>>>>> is added in v3.00, which is used during Signal Voltage Switch
>>>>> prcedure
>>>>>> described below.
>>>>>> 
>>>>>> Since as per v3.00 spec, UHS-I supported hosts should set S18R
>> to
>>>> 1,
>>>>>> we set S18R (bit 24) of OCR before sending ACMD41. We also need
>>> to
>>>>> set
>>>>>> XPC (bit 28) of OCR in case the host can supply >150mA. This
>>>> support
>>>>> is
>>>>>> indicated by the Maximum Current Capabilities register of the
>>> Host
>>>>>> Controller.
>>>>>> 
>>>>>> If the response of ACMD41 has both CCS and S18A set, we start
>> the
>>>>>> signal voltage switch procedure, which if successfull, will
>>> switch
>>>>>> the card from 3.3V signalling to 1.8V signalling. Signal
>> voltage
>>>>>> switch procedure adds support for a new command CMD11 in the
>>>> Physical
>>>>>> Layer Spec v3.01. As part of this procedure, we need to set
>> 1.8V
>>>>>> Signalling
>>>>>> Enable (bit 3) of Host Control2 register, which if remains set
>>>> after
>>>>>> 5ms,
>>>>>> means the switch to 1.8V signalling is successfull. Otherwise,
>> we
>>>>> clear
>>>>>> bit 24 of OCR and retry the initialization sequence.
>>>>>> 
>>>>>> Signed-off-by: Arindam Nath <arindam.nath@amd.com>
>>>>>> ---
>>>>>> drivers/mmc/core/sd.c     |   37 ++++++++++-
>>>>>> drivers/mmc/core/sd_ops.c |   25 ++++++++
>>>>>> drivers/mmc/core/sd_ops.h |    1 +
>>>>>> drivers/mmc/host/sdhci.c  |  147
>>>>>> +++++++++++++++++++++++++++++++++++++++++----
>>>>>> drivers/mmc/host/sdhci.h  |   18 +++++-
>>>>>> include/linux/mmc/host.h  |    8 +++
>>>>>> include/linux/mmc/sd.h    |    1 +
>>>>>> 7 files changed, 219 insertions(+), 18 deletions(-)
>>>>>> 
>>>>>> diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
>>>>>> index b3f8a3c..e968d5c 100644
>>>>>> --- a/drivers/mmc/core/sd.c
>>>>>> +++ b/drivers/mmc/core/sd.c
>>>>>> @@ -408,6 +408,7 @@ struct device_type sd_type = {
>>>>>> int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid)
>>>>>> {
>>>>>>    int err;
>>>>>> +   u32 rocr;
>>>>>> 
>>>>>>    /*
>>>>>>     * Since we're changing the OCR value, we seem to
>>>>>> @@ -427,10 +428,25 @@ int mmc_sd_get_cid(struct mmc_host *host,
>>> u32
>>>>>> ocr, u32 *cid)
>>>>>>    if (!err)
>>>>>>            ocr |= 1 << 30;
>>>>>> 
>>>>>> -   err = mmc_send_app_op_cond(host, ocr, NULL);
>>>>>> +   /* If the host can supply more than 150mA, XPC should be
>> set
>>> to
>>>>>> 1. */
>>>>>> +   if (host->caps & (MMC_CAP_SET_XPC_330 | MMC_CAP_SET_XPC_300
>> |
>>>>>> +       MMC_CAP_SET_XPC_180))
>>>>>> +           ocr |= 1 << 28;
>>>>>> +
>>>>> 
>>>>> Why do you need MMC_CAP_SET_XPC_300 here? MMC_CAP_SET_XPC_330 &
>>>>> MMC_CAP_SET_XPC_180 is not enough?
>>>> 
>>>> According to the Host Controller Spec v3.00, section 2.2.26:
>>>> 
>>>> "If a Host System can afford more than 150mA, Host Driver set XPC
>> to
>>>> 1."
>>>> 
>>>> Now since >150mA can be set for 1.8V, 3.0V and 3.3V, the above
>>>> condition checks for all these cases.
>>>> 
>>> 
>>> 3.0v and 3.3v is part of High voltage range supported by card (2.7v-
>>> 3.3v).
>>> Now if host supports any voltage from this range with 150ma driver
>>> strength
>>> then I think only config say MMC_CAP_SET_XPC_HV (high voltage) should
>>> be set
>>> by
>>> host and then only one check required here. Does it make sense?
>> 
>> You have a valid point here. But I kept the configs *XPC_300 and
>> *XPC_330 separate so that in future if someone wants to base his/her
>> code on either of the flags being set, but not both, they can easily do
>> so. If you still think it is a good idea to have a common *CAP for both
>> of them, please let me know. I will modify the code accordingly.
>> 
> 
> Ok. it's fine. you keep them as 2 separate caps.
> 
> Thanks,
> Subhash
> 	
>> Thanks,
>> Arindam
> 
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


^ permalink raw reply	[flat|nested] 45+ messages in thread

* RE: [PATCH 02/12] mmc: sd: add support for signal voltage switch procedure
  2011-03-03 22:49             ` Philip Rakity
@ 2011-03-04  6:10               ` Nath, Arindam
  0 siblings, 0 replies; 45+ messages in thread
From: Nath, Arindam @ 2011-03-04  6:10 UTC (permalink / raw)
  To: Philip Rakity, anath.amd
  Cc: Chris Ball, linux-mmc, Su, Henry, Lu, Aaron, subhashj@codeaurora.org>

Hi Philip,


> -----Original Message-----
> From: Philip Rakity [mailto:prakity@marvell.com]
> Sent: Friday, March 04, 2011 4:20 AM
> To: Nath, Arindam; anath.amd@gmail.com
> Cc: Chris Ball; linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
> subhashj@codeaurora.org>
> Subject: Re: [PATCH 02/12] mmc: sd: add support for signal voltage
> switch procedure
> 
> 
> Anath,
> 
> Is it sufficient to check the CAPS for voltage etc ?  Now that we have
> a regulator framework do we need to also look at that ?

The spec doesn't mention anything about the regulator voltage during signal voltage switch sequence. I am not sure whether it is required for embedded platforms. May be somebody else can respond to your query better.

Thanks,
Arindam

> 
> Philip
> 
> 
> 
> On Mar 3, 2011, at 1:08 PM, <subhashj@codeaurora.org>
> <subhashj@codeaurora.org> wrote:
> 
> >
> >> -----Original Message-----
> >> From: Nath, Arindam [mailto:Arindam.Nath@amd.com]
> >> Sent: Thursday, March 03, 2011 7:05 PM
> >> To: subhashj@codeaurora.org; cjb@laptop.org
> >> Cc: linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
> >> anath.amd@gmail.com
> >> Subject: RE: [PATCH 02/12] mmc: sd: add support for signal voltage
> >> switch procedure
> >>
> >> Hi Subhash,
> >>
> >>> -----Original Message-----
> >>> From: subhashj@codeaurora.org [mailto:subhashj@codeaurora.org]
> >>> Sent: Thursday, March 03, 2011 6:46 PM
> >>> To: Nath, Arindam; cjb@laptop.org
> >>> Cc: linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
> >>> anath.amd@gmail.com
> >>> Subject: RE: [PATCH 02/12] mmc: sd: add support for signal voltage
> >>> switch procedure
> >>>
> >>>
> >>>
> >>>> -----Original Message-----
> >>>> From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> >>>> owner@vger.kernel.org] On Behalf Of Nath, Arindam
> >>>> Sent: Wednesday, March 02, 2011 1:36 PM
> >>>> To: subhashj@codeaurora.org; cjb@laptop.org
> >>>> Cc: linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
> >>>> anath.amd@gmail.com
> >>>> Subject: RE: [PATCH 02/12] mmc: sd: add support for signal voltage
> >>>> switch procedure
> >>>>
> >>>> Hi Subhash,
> >>>>
> >>>>
> >>>>> -----Original Message-----
> >>>>> From: subhashj@codeaurora.org [mailto:subhashj@codeaurora.org]
> >>>>> Sent: Tuesday, March 01, 2011 9:33 PM
> >>>>> To: Nath, Arindam; cjb@laptop.org
> >>>>> Cc: linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
> >>>>> anath.amd@gmail.com
> >>>>> Subject: RE: [PATCH 02/12] mmc: sd: add support for signal
> >> voltage
> >>>>> switch procedure
> >>>>>
> >>>>>
> >>>>>
> >>>>>> -----Original Message-----
> >>>>>> From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> >>>>>> owner@vger.kernel.org] On Behalf Of Arindam Nath
> >>>>>> Sent: Tuesday, February 15, 2011 3:05 PM
> >>>>>> To: cjb@laptop.org
> >>>>>> Cc: linux-mmc@vger.kernel.org; henry.su@amd.com;
> >>> aaron.lu@amd.com;
> >>>>>> anath.amd@gmail.com; Arindam Nath
> >>>>>> Subject: [PATCH 02/12] mmc: sd: add support for signal voltage
> >>>> switch
> >>>>>> procedure
> >>>>>>
> >>>>>> Host Controller v3.00 adds another Capabilities register. We
> >> can
> >>>>>> directly read this register without any version checking since
> >>> the
> >>>>>> new register bits will be considred as reserved in older
> >>>> controllers,
> >>>>>> and will be read as 0. Apart from other things, this new
> >> register
> >>>>>> indicates whether the Host Controller supports SDR50, SDR104,
> >> and
> >>>>>> DDR50 UHS-I modes. So depending on the host support, we set
> >>>>>> the corresponding MMC_CAP_* flags. One more new register. Host
> >>>>> Control2
> >>>>>> is added in v3.00, which is used during Signal Voltage Switch
> >>>>> prcedure
> >>>>>> described below.
> >>>>>>
> >>>>>> Since as per v3.00 spec, UHS-I supported hosts should set S18R
> >> to
> >>>> 1,
> >>>>>> we set S18R (bit 24) of OCR before sending ACMD41. We also need
> >>> to
> >>>>> set
> >>>>>> XPC (bit 28) of OCR in case the host can supply >150mA. This
> >>>> support
> >>>>> is
> >>>>>> indicated by the Maximum Current Capabilities register of the
> >>> Host
> >>>>>> Controller.
> >>>>>>
> >>>>>> If the response of ACMD41 has both CCS and S18A set, we start
> >> the
> >>>>>> signal voltage switch procedure, which if successfull, will
> >>> switch
> >>>>>> the card from 3.3V signalling to 1.8V signalling. Signal
> >> voltage
> >>>>>> switch procedure adds support for a new command CMD11 in the
> >>>> Physical
> >>>>>> Layer Spec v3.01. As part of this procedure, we need to set
> >> 1.8V
> >>>>>> Signalling
> >>>>>> Enable (bit 3) of Host Control2 register, which if remains set
> >>>> after
> >>>>>> 5ms,
> >>>>>> means the switch to 1.8V signalling is successfull. Otherwise,
> >> we
> >>>>> clear
> >>>>>> bit 24 of OCR and retry the initialization sequence.
> >>>>>>
> >>>>>> Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> >>>>>> ---
> >>>>>> drivers/mmc/core/sd.c     |   37 ++++++++++-
> >>>>>> drivers/mmc/core/sd_ops.c |   25 ++++++++
> >>>>>> drivers/mmc/core/sd_ops.h |    1 +
> >>>>>> drivers/mmc/host/sdhci.c  |  147
> >>>>>> +++++++++++++++++++++++++++++++++++++++++----
> >>>>>> drivers/mmc/host/sdhci.h  |   18 +++++-
> >>>>>> include/linux/mmc/host.h  |    8 +++
> >>>>>> include/linux/mmc/sd.h    |    1 +
> >>>>>> 7 files changed, 219 insertions(+), 18 deletions(-)
> >>>>>>
> >>>>>> diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> >>>>>> index b3f8a3c..e968d5c 100644
> >>>>>> --- a/drivers/mmc/core/sd.c
> >>>>>> +++ b/drivers/mmc/core/sd.c
> >>>>>> @@ -408,6 +408,7 @@ struct device_type sd_type = {
> >>>>>> int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid)
> >>>>>> {
> >>>>>>    int err;
> >>>>>> +   u32 rocr;
> >>>>>>
> >>>>>>    /*
> >>>>>>     * Since we're changing the OCR value, we seem to
> >>>>>> @@ -427,10 +428,25 @@ int mmc_sd_get_cid(struct mmc_host *host,
> >>> u32
> >>>>>> ocr, u32 *cid)
> >>>>>>    if (!err)
> >>>>>>            ocr |= 1 << 30;
> >>>>>>
> >>>>>> -   err = mmc_send_app_op_cond(host, ocr, NULL);
> >>>>>> +   /* If the host can supply more than 150mA, XPC should be
> >> set
> >>> to
> >>>>>> 1. */
> >>>>>> +   if (host->caps & (MMC_CAP_SET_XPC_330 | MMC_CAP_SET_XPC_300
> >> |
> >>>>>> +       MMC_CAP_SET_XPC_180))
> >>>>>> +           ocr |= 1 << 28;
> >>>>>> +
> >>>>>
> >>>>> Why do you need MMC_CAP_SET_XPC_300 here? MMC_CAP_SET_XPC_330 &
> >>>>> MMC_CAP_SET_XPC_180 is not enough?
> >>>>
> >>>> According to the Host Controller Spec v3.00, section 2.2.26:
> >>>>
> >>>> "If a Host System can afford more than 150mA, Host Driver set XPC
> >> to
> >>>> 1."
> >>>>
> >>>> Now since >150mA can be set for 1.8V, 3.0V and 3.3V, the above
> >>>> condition checks for all these cases.
> >>>>
> >>>
> >>> 3.0v and 3.3v is part of High voltage range supported by card
> (2.7v-
> >>> 3.3v).
> >>> Now if host supports any voltage from this range with 150ma driver
> >>> strength
> >>> then I think only config say MMC_CAP_SET_XPC_HV (high voltage)
> should
> >>> be set
> >>> by
> >>> host and then only one check required here. Does it make sense?
> >>
> >> You have a valid point here. But I kept the configs *XPC_300 and
> >> *XPC_330 separate so that in future if someone wants to base his/her
> >> code on either of the flags being set, but not both, they can easily
> do
> >> so. If you still think it is a good idea to have a common *CAP for
> both
> >> of them, please let me know. I will modify the code accordingly.
> >>
> >
> > Ok. it's fine. you keep them as 2 separate caps.
> >
> > Thanks,
> > Subhash
> >
> >> Thanks,
> >> Arindam
> >
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-mmc"
> in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 



^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 06/12] mmc: sd: add support for uhs bus speed mode selection
  2011-02-15  9:35 ` [PATCH 06/12] mmc: sd: add support for uhs bus speed mode selection Arindam Nath
@ 2011-03-09 21:43   ` Philip Rakity
  0 siblings, 0 replies; 45+ messages in thread
From: Philip Rakity @ 2011-03-09 21:43 UTC (permalink / raw)
  To: Arindam Nath; +Cc: cjb, linux-mmc, henry.su, aaron.lu, anath.amd


On Feb 15, 2011, at 1:35 AM, Arindam Nath wrote:

> This patch adds support for setting UHS-I bus speed mode during UHS-I
> initialization procedure. Since both the host and card can support
> more than one bus speed, we select the highest speed based on both of
> their capabilities. First we set the bus speed mode for the card using
> CMD6 mode 1, and then we program the host controller to support the
> required speed mode. We also set High Speed Enable in case one of the
> UHS-I modes is selected. Since MMC_TIMING_UHS_SDR25 is same as
> MMC_TIMING_SD_HS, we don't need to set the SDHCI_CTRL_HISPD flag for
> UHS SDR25 again. We also take care to reset SD clock before setting
> UHS mode in the Host Control2 register, and then re-enable it as per
> the Host Controller spec v3.00. We set the clock frequency for the
> UHS-I mode selected.
> 
> Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> ---
> drivers/mmc/core/sd.c    |   82 ++++++++++++++++++++++++++++++++++++++++++++++
> drivers/mmc/host/sdhci.c |   32 +++++++++++++++++-
> drivers/mmc/host/sdhci.h |    5 +++
> include/linux/mmc/card.h |   16 +++++++++
> include/linux/mmc/host.h |    4 ++
> 5 files changed, 137 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> index 9e4574d..e681385 100644
> --- a/drivers/mmc/core/sd.c
> +++ b/drivers/mmc/core/sd.c
> @@ -464,6 +464,83 @@ static int sd_select_driver_type(struct mmc_card *card, u8 *status)
> 	return 0;
> }
> 
> +static int sd_set_bus_speed_mode(struct mmc_card *card, u8 *status)
> +{
> +	unsigned int bus_speed, timing;

warning may not be initialized.  bus_speed

> +	int err;
> +
> +	/*
> +	 * If the host doesn't support any of the UHS-I modes, fallback on
> +	 * default speed.
> +	 */
> +	if (!(card->host->caps & (MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 |
> +	    MMC_CAP_UHS_DDR50)))
> +		return 0;
> +
> +	if (card->host->caps & MMC_CAP_UHS_SDR104) {
> +		if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR104) {
> +			bus_speed = UHS_SDR104_BUS_SPEED;
> +			timing = MMC_TIMING_UHS_SDR104;
> +			card->sw_caps.uhs_max_dtr = UHS_SDR104_MAX_DTR;
> +		} else if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR50) {
> +			bus_speed = UHS_SDR50_BUS_SPEED;
> +			timing = MMC_TIMING_UHS_SDR50;
> +			card->sw_caps.uhs_max_dtr = UHS_SDR50_MAX_DTR;
> +		} else if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_DDR50) {
> +			bus_speed = UHS_DDR50_BUS_SPEED;
> +			timing = MMC_TIMING_UHS_DDR50;
> +			card->sw_caps.uhs_max_dtr = UHS_DDR50_MAX_DTR;
> +		} else if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR25) {
> +			bus_speed = UHS_SDR25_BUS_SPEED;
> +			timing = MMC_TIMING_UHS_SDR25;
> +			card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR;
> +		}
> +	} else if (card->host->caps & MMC_CAP_UHS_SDR50) {
> +		if ((card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR104) ||
> +		    (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR50)) {
> +			bus_speed = UHS_SDR50_BUS_SPEED;
> +			timing = MMC_TIMING_UHS_SDR50;
> +			card->sw_caps.uhs_max_dtr = UHS_SDR50_MAX_DTR;
> +		} else if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_DDR50) {
> +			bus_speed = UHS_DDR50_BUS_SPEED;
> +			timing = MMC_TIMING_UHS_DDR50;
> +			card->sw_caps.uhs_max_dtr = UHS_DDR50_MAX_DTR;
> +		} else if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR25) {
> +			bus_speed = UHS_SDR25_BUS_SPEED;
> +			timing = MMC_TIMING_UHS_SDR25;
> +			card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR;
> +		}
> +	} else if (card->host->caps & MMC_CAP_UHS_DDR50) {
> +		if ((card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR104) ||
> +		    (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR50) ||
> +		    (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_DDR50)) {
> +			bus_speed = UHS_DDR50_BUS_SPEED;
> +			timing = MMC_TIMING_UHS_DDR50;
> +			card->sw_caps.uhs_max_dtr = UHS_DDR50_MAX_DTR;
> +		} else if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR25) {
> +			bus_speed = UHS_SDR25_BUS_SPEED;
> +			timing = MMC_TIMING_UHS_SDR25;
> +			card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR;
> +		}
> +	}
> +
> +	err = mmc_sd_switch(card, 1, 0, bus_speed, status);
> +	if (err)
> +		return err;
> +
> +	if ((status[16] & 0xF) != bus_speed)
> +		printk(KERN_WARNING "%s: Problem setting bus speed mode!\n",
> +			mmc_hostname(card->host));
> +	else {
> +		if (bus_speed) {
> +			mmc_set_timing(card->host, timing);
> +			mmc_set_clock(card->host, card->sw_caps.uhs_max_dtr);
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> /*
>  * UHS-I specific initialization procedure
>  */
> @@ -499,6 +576,11 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card)
> 
> 	/* Set the driver strength for the card */
> 	err = sd_select_driver_type(card, status);
> +	if (err)
> +		goto out;
> +
> +	/* Set bus speed mode of the card */
> +	err = sd_set_bus_speed_mode(card, status);
> 
> out:
> 	kfree(status);
> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> index 5db0109..ae91c6b 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -1281,7 +1281,13 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
> 		ctrl &= ~SDHCI_CTRL_HISPD;
> 
> 	if (host->version >= SDHCI_SPEC_300) {
> -		u16 ctrl_2;
> +		u16 clk, ctrl_2;
> +
> +		/* In case of UHS-I modes, set High Speed Enable */
> +		if ((ios->timing == MMC_TIMING_UHS_SDR50) ||
> +		    (ios->timing == MMC_TIMING_UHS_SDR104) ||
> +		    (ios->timing == MMC_TIMING_UHS_DDR50))
> +			ctrl |= SDHCI_CTRL_HISPD;
> 
> 		ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> 		if (!(ctrl_2 & SDHCI_CTRL_PRESET_VAL_ENABLE)) {
> @@ -1303,7 +1309,6 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
> 			 * need to reset SD Clock Enable before changing High
> 			 * Speed Enable to avoid generating clock gliches.
> 			 */
> -			u16 clk;
> 
> 			/* Reset SD Clock Enable */
> 			clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
> @@ -1316,6 +1321,29 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
> 			clk |= SDHCI_CLOCK_CARD_EN;
> 			sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
> 		}
> +
> +		ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> +
> +		/* Select Bus Speed Mode for host */
> +		if (ios->timing == MMC_TIMING_UHS_SDR25)
> +			ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
> +		else if (ios->timing == MMC_TIMING_UHS_SDR50)
> +			ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
> +		else if (ios->timing == MMC_TIMING_UHS_SDR104)
> +			ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
> +		else if (ios->timing == MMC_TIMING_UHS_DDR50)
> +			ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
> +
> +		/* Reset SD Clock Enable */
> +		clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
> +		clk &= ~SDHCI_CLOCK_CARD_EN;
> +		sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
> +
> +		sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
> +
> +		/* Re-enable SD Clock */
> +		clk |= SDHCI_CLOCK_CARD_EN;
> +		sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
> 	} else
> 		sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL1);
> 
> diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
> index a407b5b..5bf244d 100644
> --- a/drivers/mmc/host/sdhci.h
> +++ b/drivers/mmc/host/sdhci.h
> @@ -150,6 +150,11 @@
> #define SDHCI_ACMD12_ERR	0x3C
> 
> #define SDHCI_HOST_CONTROL2		0x3E
> +#define  SDHCI_CTRL_UHS_SDR12		0x0000
> +#define  SDHCI_CTRL_UHS_SDR25		0x0001
> +#define  SDHCI_CTRL_UHS_SDR50		0x0002
> +#define  SDHCI_CTRL_UHS_SDR104		0x0003
> +#define  SDHCI_CTRL_UHS_DDR50		0x0004
> #define  SDHCI_CTRL_VDD_180		0x0008
> #define  SDHCI_CTRL_DRV_TYPE_B		0x0000
> #define  SDHCI_CTRL_DRV_TYPE_A		0x0010
> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
> index 2d7f7a3..0b24c41 100644
> --- a/include/linux/mmc/card.h
> +++ b/include/linux/mmc/card.h
> @@ -75,7 +75,23 @@ struct sd_ssr {
> 
> struct sd_switch_caps {
> 	unsigned int		hs_max_dtr;
> +	unsigned int		uhs_max_dtr;
> +#define UHS_SDR104_MAX_DTR	208000000
> +#define UHS_SDR50_MAX_DTR	100000000
> +#define UHS_DDR50_MAX_DTR	50000000
> +#define UHS_SDR25_MAX_DTR	50000000
> 	unsigned int		uhs_bus_mode;
> +#define UHS_SDR12_BUS_SPEED	0
> +#define UHS_SDR25_BUS_SPEED	1
> +#define UHS_SDR50_BUS_SPEED	2
> +#define UHS_SDR104_BUS_SPEED	3
> +#define UHS_DDR50_BUS_SPEED	4
> +
> +#define SD_MODE_UHS_SDR12	(1 << UHS_SDR12_BUS_SPEED)
> +#define SD_MODE_UHS_SDR25	(1 << UHS_SDR25_BUS_SPEED)
> +#define SD_MODE_UHS_SDR50	(1 << UHS_SDR50_BUS_SPEED)
> +#define SD_MODE_UHS_SDR104	(1 << UHS_SDR104_BUS_SPEED)
> +#define SD_MODE_UHS_DDR50	(1 << UHS_DDR50_BUS_SPEED)
> 	unsigned int		uhs_drv_type;
> #define SD_DRIVER_TYPE_B	0x01
> #define SD_DRIVER_TYPE_A	0x02
> diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
> index e6ae4c4..0a21839 100644
> --- a/include/linux/mmc/host.h
> +++ b/include/linux/mmc/host.h
> @@ -50,6 +50,10 @@ struct mmc_ios {
> #define MMC_TIMING_LEGACY	0
> #define MMC_TIMING_MMC_HS	1
> #define MMC_TIMING_SD_HS	2
> +#define MMC_TIMING_UHS_SDR25	MMC_TIMING_SD_HS
> +#define MMC_TIMING_UHS_SDR50	3
> +#define MMC_TIMING_UHS_SDR104	4
> +#define MMC_TIMING_UHS_DDR50	5
> 
> 	unsigned char	ddr;			/* dual data rate used */
> 
> -- 
> 1.7.1
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


^ permalink raw reply	[flat|nested] 45+ messages in thread

end of thread, other threads:[~2011-03-09 21:46 UTC | newest]

Thread overview: 45+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-02-15  9:34 [PATCH 00/12] add support for sd host controller v3.00 Arindam Nath
2011-02-15  9:34 ` [PATCH 01/12] mmc: sdhci: add support for auto CMD23 Arindam Nath
2011-02-15 20:58   ` Nicolas Pitre
2011-02-16  8:04     ` Nath, Arindam
2011-02-18 10:45   ` zhangfei gao
2011-02-18 10:50     ` Arnd Bergmann
2011-02-18 12:08       ` Nath, Arindam
2011-02-18 13:08         ` Arnd Bergmann
2011-02-18 13:35           ` Nath, Arindam
2011-02-18 15:40             ` Arnd Bergmann
2011-02-18 15:49               ` [PATCH 01/12] mmc: sdhci: add support for SD Host Controller 3.0 Philip Rakity
2011-02-18 17:17                 ` Nath, Arindam
2011-02-18 18:55                   ` Nicolas Pitre
2011-02-18 19:08                     ` Philip Rakity
2011-02-18 19:06                   ` Philip Rakity
2011-02-18 11:53     ` [PATCH 01/12] mmc: sdhci: add support for auto CMD23 Nath, Arindam
2011-02-15  9:35 ` [PATCH 02/12] mmc: sd: add support for signal voltage switch procedure Arindam Nath
2011-02-15 10:16   ` Wolfram Sang
2011-02-16  7:55     ` Nath, Arindam
2011-02-15 21:18   ` Nicolas Pitre
2011-02-16  8:08     ` Nath, Arindam
2011-03-01 16:02   ` subhashj
2011-03-02  8:06     ` Nath, Arindam
2011-03-03 13:16       ` subhashj
2011-03-03 13:34         ` Nath, Arindam
2011-03-03 21:08           ` subhashj
2011-03-03 22:49             ` Philip Rakity
2011-03-04  6:10               ` Nath, Arindam
2011-02-15  9:35 ` [PATCH 03/12] mmc: sd: query function modes for uhs cards Arindam Nath
2011-02-15  9:35 ` [PATCH 04/12] mmc: sd: add support for driver type selection Arindam Nath
2011-02-15  9:35 ` [PATCH 05/12] mmc: sdhci: reset sdclk before setting high speed enable Arindam Nath
2011-02-15  9:35 ` [PATCH 06/12] mmc: sd: add support for uhs bus speed mode selection Arindam Nath
2011-03-09 21:43   ` Philip Rakity
2011-02-15  9:35 ` [PATCH 07/12] mmc: sd: set current limit for uhs cards Arindam Nath
2011-02-15  9:35 ` [PATCH 08/12] mmc: sd: report correct speed and capacity of " Arindam Nath
2011-02-15  9:35 ` [PATCH 09/12] mmc: sd: add support for tuning during uhs initialization Arindam Nath
2011-02-18 10:50   ` zhangfei gao
2011-02-18 11:33     ` Nath, Arindam
2011-02-15  9:35 ` [PATCH 10/12] mmc: sdhci: enable preset value after " Arindam Nath
2011-02-15  9:35 ` [PATCH 11/12] mmc: sdhci: add support for programmable clock mode Arindam Nath
2011-02-15  9:35 ` [PATCH 12/12] mmc: sdhci: add support for retuning mode 1 Arindam Nath
2011-02-15 19:43 ` [PATCH 00/12] add support for sd host controller v3.00 Chris Ball
2011-02-16  8:00   ` Nath, Arindam
2011-02-16 20:55 ` Chris Ball
2011-02-17  2:03   ` Nath, Arindam

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.