All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH]  mmc: Resubmit: Bus Width testing for eMMC and MMC Cards
@ 2010-11-27 19:00 Philip Rakity
  2010-11-27 19:32 ` Chris Ball
  0 siblings, 1 reply; 3+ messages in thread
From: Philip Rakity @ 2010-11-27 19:00 UTC (permalink / raw)
  To: linux-mmc; +Cc: Takashi Iwai


Change from previous patch;
Added MMC_RSP_SPI_R1 to mmc_ops command for DATA_READ and DATA_Write
(Thanks to Takashi Lwai for pointing this out)
Patch made against linux-next (see below) and tested against marvell mmp2
controller using Marvell linux

eMMC cards do not have a card specific field indicating the bus width that
they support.
The bus width needs to be figured out by probing the bus.

JEDEC STANDARD
Embedded MultiMediaCard(e•MMC) e•MMC/Card Product Standard, High Capacity,
including Reliable Write, Boot, Sleep Modes, Dual Data Rate,
Multiple Partitions Supports, Security Enhancement, Background Operation and
High Priority Interrupt (MMCA, 4.41)
JESD84-A441

defines what needs to be done in Annex A.8.3.

In earlier testing (2.6.2x) this code did not work on some PCIe SD controllers.
We define a new MMC_CAP: MMC_CAP_BUS_WIDTH_WORKS that the host adaptation
layer can set if the controller can support bus width testing.
If the CAP is not defined the behavior defaults to what is done today;
the largest bit width is selected.

Transcend 1GB and 2GB  MMC cards work when this code is enabled and fail otherwise.

Signed-off-by: Philip Rakity <prakity@marvell.com>
---
 drivers/mmc/core/mmc.c     |   47 +++++++++++++++-
 drivers/mmc/core/mmc_ops.c |  132 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/mmc/core/mmc_ops.h |    1 +
 include/linux/mmc/host.h   |    1 +
 include/linux/mmc/mmc.h    |    2 +
 5 files changed, 182 insertions(+), 1 deletions(-)

diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 77f93c3..d20237b 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -531,12 +531,57 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
 
 	/*
 	 * Activate wide bus and DDR (if supported).
+	 * Determine mmc bus width supported by probing card (if supported)
 	 */
 	if ((card->csd.mmca_vsn >= CSD_SPEC_VER_4) &&
 	    (host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA))) {
 		unsigned ext_csd_bit, bus_width;
+		int temp_caps = host->caps & (MMC_CAP_8_BIT_DATA | MMC_CAP_4_BIT_DATA);
 
-		if (host->caps & MMC_CAP_8_BIT_DATA) {
+		do {
+			if (temp_caps & MMC_CAP_8_BIT_DATA) {
+				ext_csd_bit = EXT_CSD_BUS_WIDTH_8;
+				bus_width = MMC_BUS_WIDTH_8;
+			} else {
+				ext_csd_bit = EXT_CSD_BUS_WIDTH_4;
+				bus_width = MMC_BUS_WIDTH_4;
+			}
+
+			err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+					 EXT_CSD_BUS_WIDTH, ext_csd_bit);
+			if (err) {
+				printk(KERN_WARNING "%s: switch to bus width %d ddr %d "
+					   "failed\n", mmc_hostname(card->host),
+					   1 << bus_width, ddr);
+				err = 0;
+			} else {
+				mmc_set_bus_width_ddr(card->host, bus_width, MMC_SDR_MODE);
+				/*
+				 * if controller can't handle bus width test
+				 * try to use the highest bus width to
+				 * maintain compatibility with previous linux
+				 */
+				if ((host->caps & MMC_CAP_BUS_WIDTH_WORKS) == 0)
+					break;
+				if (mmc_test_bus_width (card, 1<<bus_width))
+					break;
+			}
+
+			if (bus_width == MMC_BUS_WIDTH_8)
+				temp_caps &= ~MMC_CAP_8_BIT_DATA;
+			else
+				temp_caps &= ~MMC_CAP_4_BIT_DATA;
+
+			if (temp_caps == 0) {
+				ext_csd_bit = EXT_CSD_BUS_WIDTH_1;
+				bus_width = MMC_BUS_WIDTH_1;
+			}
+		} while (temp_caps);
+
+		if (temp_caps == 0) {
+			ext_csd_bit = EXT_CSD_BUS_WIDTH_1;
+			bus_width = MMC_BUS_WIDTH_1;
+		} else if (temp_caps & MMC_CAP_8_BIT_DATA) {
 			if (ddr)
 				ext_csd_bit = EXT_CSD_DDR_BUS_WIDTH_8;
 			else
diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
index 326447c..2b115a3 100644
--- a/drivers/mmc/core/mmc_ops.c
+++ b/drivers/mmc/core/mmc_ops.c
@@ -20,6 +20,138 @@
 #include "core.h"
 #include "mmc_ops.h"
 
+int mmc_test_bus_width(struct mmc_card *card, int bits)
+{
+	struct mmc_request mrq;
+	struct mmc_command cmd;
+	struct mmc_data data;
+	struct scatterlist sg;
+	int len;
+	u8 test_data_write[8];
+	u8 test_data_read[64];
+
+	switch (bits) {
+	case 8:
+		test_data_write[0] = 0x55;
+		test_data_write[1] = 0xaa;
+		test_data_write[2] = 0x00;
+		test_data_write[3] = 0x00;
+		test_data_write[4] = 0x00;
+		test_data_write[5] = 0x00;
+		test_data_write[6] = 0x00;
+		test_data_write[7] = 0x00;
+		len = 8;
+		break;
+	case 4:
+		test_data_write[0] = 0x5a;
+		test_data_write[1] = 0x00;
+		test_data_write[2] = 0x00;
+		test_data_write[3] = 0x00;
+		len = 4;
+		break;
+	default:
+		/* 1 bit bus cards ALWAYS work */
+		return 1;
+	}
+
+	memset(&mrq, 0, sizeof(struct mmc_request));
+	memset(&cmd, 0, sizeof(struct mmc_command));
+	memset(&data, 0, sizeof(struct mmc_data));
+
+	cmd.opcode = MMC_BUSTEST_W;
+	cmd.arg = 0;
+
+	/* NOTE HACK:  the MMC_RSP_SPI_R1 is always correct here, but we
+	 * rely on callers to never use this with "native" calls for reading
+	 * CSD or CID.  Native versions of those commands use the R2 type,
+	 * not R1 plus a data block.
+	 */
+	cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
+
+	data.flags = MMC_DATA_WRITE;
+	data.blksz = len;
+	data.blocks = 1;
+	data.sg = &sg;
+	data.sg_len = 1;
+
+	mrq.cmd = &cmd;
+	mrq.data = &data;
+
+	sg_init_one(&sg, &test_data_write, len);
+
+	/*
+	 * The spec states that MMC_BUSTEST_W and BUSTEST_R accesses
+	 * have a maximum timeout of 64 clock cycles.
+	 */
+	data.timeout_ns = 0;
+	data.timeout_clks = 64;
+
+	mmc_wait_for_req(card->host, &mrq);
+
+	if (cmd.error || data.error ) {
+		printk(KERN_INFO "%s: Failed to send (BUSTEST_W) CMD19: %d %d\n",
+			mmc_hostname(card->host), cmd.error, data.error);
+	}
+
+	/* Now read back */
+	memset(&mrq, 0, sizeof(struct mmc_request));
+	memset(&cmd, 0, sizeof(struct mmc_command));
+	memset(&data, 0, sizeof(struct mmc_data));
+	memset (&test_data_read, 0, sizeof(test_data_read));
+
+	cmd.opcode = MMC_BUSTEST_R;
+	cmd.arg = 0;
+
+	/* NOTE HACK:  the MMC_RSP_SPI_R1 is always correct here, but we
+	 * rely on callers to never use this with "native" calls for reading
+	 * CSD or CID.  Native versions of those commands use the R2 type,
+	 * not R1 plus a data block.
+	 */
+	cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
+
+	data.flags = MMC_DATA_READ;
+	data.blksz = len;
+	data.blocks = 1;
+	data.sg = &sg;
+	data.sg_len = 1;
+
+	mrq.cmd = &cmd;
+	mrq.data = &data;
+
+	sg_init_one(&sg, &test_data_read, len);
+
+	data.timeout_ns = 0;
+	data.timeout_clks = 64;
+
+	mmc_wait_for_req(card->host, &mrq);
+
+	if (cmd.error) {
+		printk(KERN_INFO "%s: Failed to send CMD14: %d %d\n",
+			mmc_hostname(card->host), cmd.error, data.error);
+		return 0;
+	}
+
+#if 0
+#warning PRINT RESULTS FROM CMD14
+	printk (KERN_INFO "%s: Bits = %d, Got %02X %02X %02X %02X\n", __FUNCTION__,
+		bits,
+		test_data_read[0],
+		test_data_read[1],
+		test_data_read[2],
+		test_data_read[3]);
+#endif
+
+	switch (bits) {
+	case 8:
+		return (test_data_read[0] == 0xaa && test_data_read[1] == 0x55);
+	case 4:
+		return (test_data_read[0] == 0xa5);
+	case 1:
+		return (test_data_read[0] == 0x80);
+	}
+	return 0;
+}
+
 static int _mmc_select_card(struct mmc_host *host, struct mmc_card *card)
 {
 	int err;
diff --git a/drivers/mmc/core/mmc_ops.h b/drivers/mmc/core/mmc_ops.h
index 653eb8e..c08b9ad 100644
--- a/drivers/mmc/core/mmc_ops.h
+++ b/drivers/mmc/core/mmc_ops.h
@@ -26,6 +26,7 @@ int mmc_send_cid(struct mmc_host *host, u32 *cid);
 int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp);
 int mmc_spi_set_crc(struct mmc_host *host, int use_crc);
 int mmc_card_sleepawake(struct mmc_host *host, int sleep);
+int mmc_test_bus_width(struct mmc_card *card, int bits);
 
 #endif
 
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 53496bb..1e3d9d2 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -169,6 +169,7 @@ struct mmc_host {
 #define MMC_CAP_1_2V_DDR	(1 << 12)	/* can support */
 						/* DDR mode at 1.2V */
 
+#define MMC_CAP_BUS_WIDTH_WORKS	(1 << 13)	/* CMD14/CMD19 bus width ok */
 	mmc_pm_flag_t		pm_caps;	/* supported pm features */
 
 #ifdef CONFIG_MMC_CLKGATE
diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
index 956fbd8..8e0d047 100644
--- a/include/linux/mmc/mmc.h
+++ b/include/linux/mmc/mmc.h
@@ -40,7 +40,9 @@
 #define MMC_READ_DAT_UNTIL_STOP  11   /* adtc [31:0] dadr        R1  */
 #define MMC_STOP_TRANSMISSION    12   /* ac                      R1b */
 #define MMC_SEND_STATUS          13   /* ac   [31:16] RCA        R1  */
+#define MMC_BUSTEST_R            14   /* adtc                    R1  */
 #define MMC_GO_INACTIVE_STATE    15   /* ac   [31:16] RCA            */
+#define MMC_BUSTEST_W            19   /* adtc                    R1  */
 #define MMC_SPI_READ_OCR         58   /* spi                  spi_R3 */
 #define MMC_SPI_CRC_ON_OFF       59   /* spi  [0:0] flag      spi_R1 */
 
-- 
1.6.0.4


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

* Re: [PATCH]  mmc: Resubmit: Bus Width testing for eMMC and MMC Cards
  2010-11-27 19:00 [PATCH] mmc: Resubmit: Bus Width testing for eMMC and MMC Cards Philip Rakity
@ 2010-11-27 19:32 ` Chris Ball
  2010-11-27 23:37   ` Philip Rakity
  0 siblings, 1 reply; 3+ messages in thread
From: Chris Ball @ 2010-11-27 19:32 UTC (permalink / raw)
  To: Philip Rakity; +Cc: linux-mmc, Takashi Iwai

Hi Philip,

Some early stylistic review comments:

On Sat, Nov 27, 2010 at 11:00:19AM -0800, Philip Rakity wrote:
> Patch made against linux-next (see below) and tested against marvell mmp2
> controller using Marvell linux

The patch doesn't apply against current linux-next or Linus HEAD, due to
Ohad's recent runtime PM change to host.h.

> We define a new MMC_CAP: MMC_CAP_BUS_WIDTH_WORKS that the host adaptation
> layer can set if the controller can support bus width testing.

"BUS_WIDTH_WORKS" is a bit vague.  Maybe MMC_CAP_BUS_WIDTH_TEST?

>  	if ((card->csd.mmca_vsn >= CSD_SPEC_VER_4) &&
>  	    (host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA))) {
>  		unsigned ext_csd_bit, bus_width;
> +		int temp_caps = host->caps & (MMC_CAP_8_BIT_DATA | MMC_CAP_4_BIT_DATA);
>  
> -		if (host->caps & MMC_CAP_8_BIT_DATA) {
> +		do {
> +			if (temp_caps & MMC_CAP_8_BIT_DATA) {
> +				ext_csd_bit = EXT_CSD_BUS_WIDTH_8;
> +				bus_width = MMC_BUS_WIDTH_8;
> +			} else {
> +				ext_csd_bit = EXT_CSD_BUS_WIDTH_4;
> +				bus_width = MMC_BUS_WIDTH_4;
> +			}
> +
> +			err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
> +					 EXT_CSD_BUS_WIDTH, ext_csd_bit);
> +			if (err) {
> +				printk(KERN_WARNING "%s: switch to bus width %d ddr %d "

Please stick to 80 cols where possible.

> +					   "failed\n", mmc_hostname(card->host),
> +					   1 << bus_width, ddr);
> +				err = 0;
> +			} else {
> +				mmc_set_bus_width_ddr(card->host, bus_width, MMC_SDR_MODE);
> +				/*
> +				 * if controller can't handle bus width test
> +				 * try to use the highest bus width to
> +				 * maintain compatibility with previous linux
> +				 */
> +				if ((host->caps & MMC_CAP_BUS_WIDTH_WORKS) == 0)
> +					break;
> +				if (mmc_test_bus_width (card, 1<<bus_width))

Extra space here.

> +					break;
> +			}
> +
> +			if (bus_width == MMC_BUS_WIDTH_8)
> +				temp_caps &= ~MMC_CAP_8_BIT_DATA;
> +			else
> +				temp_caps &= ~MMC_CAP_4_BIT_DATA;
> +
> +			if (temp_caps == 0) {
> +				ext_csd_bit = EXT_CSD_BUS_WIDTH_1;
> +				bus_width = MMC_BUS_WIDTH_1;
> +			}
> +		} while (temp_caps);
> +
> +		if (temp_caps == 0) {
> +			ext_csd_bit = EXT_CSD_BUS_WIDTH_1;
> +			bus_width = MMC_BUS_WIDTH_1;
> +		} else if (temp_caps & MMC_CAP_8_BIT_DATA) {
>  			if (ddr)
>  				ext_csd_bit = EXT_CSD_DDR_BUS_WIDTH_8;
>  			else

Why is the "temp_caps == 0" test inside the while loop necessary, rather
than just relying on the same test outside of the loop?

> diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
> index 326447c..2b115a3 100644
> --- a/drivers/mmc/core/mmc_ops.c
> +++ b/drivers/mmc/core/mmc_ops.c
> @@ -20,6 +20,138 @@
>  #include "core.h"
>  #include "mmc_ops.h"
>  
> +int mmc_test_bus_width(struct mmc_card *card, int bits)
> +{
> +	struct mmc_request mrq;
> +	struct mmc_command cmd;
> +	struct mmc_data data;
> +	struct scatterlist sg;
> +	int len;
> +	u8 test_data_write[8];
> +	u8 test_data_read[64];
> +
> +	switch (bits) {
> +	case 8:
> +		test_data_write[0] = 0x55;
> +		test_data_write[1] = 0xaa;
> +		test_data_write[2] = 0x00;
> +		test_data_write[3] = 0x00;
> +		test_data_write[4] = 0x00;
> +		test_data_write[5] = 0x00;
> +		test_data_write[6] = 0x00;
> +		test_data_write[7] = 0x00;
> +		len = 8;
> +		break;
> +	case 4:
> +		test_data_write[0] = 0x5a;
> +		test_data_write[1] = 0x00;
> +		test_data_write[2] = 0x00;
> +		test_data_write[3] = 0x00;
> +		len = 4;
> +		break;
> +	default:
> +		/* 1 bit bus cards ALWAYS work */
> +		return 1;
> +	}
> +
> +	memset(&mrq, 0, sizeof(struct mmc_request));
> +	memset(&cmd, 0, sizeof(struct mmc_command));
> +	memset(&data, 0, sizeof(struct mmc_data));
> +
> +	cmd.opcode = MMC_BUSTEST_W;
> +	cmd.arg = 0;
> +
> +	/* NOTE HACK:  the MMC_RSP_SPI_R1 is always correct here, but we
> +	 * rely on callers to never use this with "native" calls for reading
> +	 * CSD or CID.  Native versions of those commands use the R2 type,
> +	 * not R1 plus a data block.
> +	 */
> +	cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
> +
> +	data.flags = MMC_DATA_WRITE;
> +	data.blksz = len;
> +	data.blocks = 1;
> +	data.sg = &sg;
> +	data.sg_len = 1;
> +
> +	mrq.cmd = &cmd;
> +	mrq.data = &data;
> +
> +	sg_init_one(&sg, &test_data_write, len);
> +
> +	/*
> +	 * The spec states that MMC_BUSTEST_W and BUSTEST_R accesses
> +	 * have a maximum timeout of 64 clock cycles.
> +	 */
> +	data.timeout_ns = 0;
> +	data.timeout_clks = 64;
> +
> +	mmc_wait_for_req(card->host, &mrq);
> +
> +	if (cmd.error || data.error ) {

Extra space here.

> +		printk(KERN_INFO "%s: Failed to send (BUSTEST_W) CMD19: %d %d\n",
> +			mmc_hostname(card->host), cmd.error, data.error);
> +	}
> +
> +	/* Now read back */
> +	memset(&mrq, 0, sizeof(struct mmc_request));
> +	memset(&cmd, 0, sizeof(struct mmc_command));
> +	memset(&data, 0, sizeof(struct mmc_data));
> +	memset (&test_data_read, 0, sizeof(test_data_read));
> +
> +	cmd.opcode = MMC_BUSTEST_R;
> +	cmd.arg = 0;
> +
> +	/* NOTE HACK:  the MMC_RSP_SPI_R1 is always correct here, but we
> +	 * rely on callers to never use this with "native" calls for reading
> +	 * CSD or CID.  Native versions of those commands use the R2 type,
> +	 * not R1 plus a data block.
> +	 */
> +	cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
> +
> +	data.flags = MMC_DATA_READ;
> +	data.blksz = len;
> +	data.blocks = 1;
> +	data.sg = &sg;
> +	data.sg_len = 1;
> +
> +	mrq.cmd = &cmd;
> +	mrq.data = &data;
> +
> +	sg_init_one(&sg, &test_data_read, len);
> +
> +	data.timeout_ns = 0;
> +	data.timeout_clks = 64;
> +
> +	mmc_wait_for_req(card->host, &mrq);
> +
> +	if (cmd.error) {
> +		printk(KERN_INFO "%s: Failed to send CMD14: %d %d\n",
> +			mmc_hostname(card->host), cmd.error, data.error);
> +		return 0;
> +	}
> +
> +#if 0
> +#warning PRINT RESULTS FROM CMD14
> +	printk (KERN_INFO "%s: Bits = %d, Got %02X %02X %02X %02X\n", __FUNCTION__,

Extra space, and please don't submit #if 0'd code.  You can use a debug
level printk if you want to condition it on CONFIG_MMC_DEBUG.  Also,
__func__ instead of __FUNCTION__.

Thanks!

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

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

* Re: [PATCH]  mmc: Resubmit: Bus Width testing for eMMC and MMC Cards
  2010-11-27 19:32 ` Chris Ball
@ 2010-11-27 23:37   ` Philip Rakity
  0 siblings, 0 replies; 3+ messages in thread
From: Philip Rakity @ 2010-11-27 23:37 UTC (permalink / raw)
  To: Chris Ball; +Cc: linux-mmc, Takashi Iwai


Chris,

Thanks for the review.  Revised complete patch below incorporating  your comments.

Philip

On Nov 27, 2010, at 11:32 AM, Chris Ball wrote:

> Hi Philip,
>
> Some early stylistic review comments:
>
> On Sat, Nov 27, 2010 at 11:00:19AM -0800, Philip Rakity wrote:
>> Patch made against linux-next (see below) and tested against marvell mmp2
>> controller using Marvell linux
>
> The patch doesn't apply against current linux-next or Linus HEAD, due to
> Ohad's recent runtime PM change to host.h.
>
>> We define a new MMC_CAP: MMC_CAP_BUS_WIDTH_WORKS that the host adaptation
>> layer can set if the controller can support bus width testing.
>
> "BUS_WIDTH_WORKS" is a bit vague.  Maybe MMC_CAP_BUS_WIDTH_TEST?
>
>>      if ((card->csd.mmca_vsn >= CSD_SPEC_VER_4) &&
>>          (host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA))) {
>>              unsigned ext_csd_bit, bus_width;
>> +            int temp_caps = host->caps & (MMC_CAP_8_BIT_DATA | MMC_CAP_4_BIT_DATA);
>>
>> -            if (host->caps & MMC_CAP_8_BIT_DATA) {
>> +            do {
>> +                    if (temp_caps & MMC_CAP_8_BIT_DATA) {
>> +                            ext_csd_bit = EXT_CSD_BUS_WIDTH_8;
>> +                            bus_width = MMC_BUS_WIDTH_8;
>> +                    } else {
>> +                            ext_csd_bit = EXT_CSD_BUS_WIDTH_4;
>> +                            bus_width = MMC_BUS_WIDTH_4;
>> +                    }
>> +
>> +                    err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
>> +                                     EXT_CSD_BUS_WIDTH, ext_csd_bit);
>> +                    if (err) {
>> +                            printk(KERN_WARNING "%s: switch to bus width %d ddr %d "
>
> Please stick to 80 cols where possible.
>
>> +                                       "failed\n", mmc_hostname(card->host),
>> +                                       1 << bus_width, ddr);
>> +                            err = 0;
>> +                    } else {
>> +                            mmc_set_bus_width_ddr(card->host, bus_width, MMC_SDR_MODE);
>> +                            /*
>> +                             * if controller can't handle bus width test
>> +                             * try to use the highest bus width to
>> +                             * maintain compatibility with previous linux
>> +                             */
>> +                            if ((host->caps & MMC_CAP_BUS_WIDTH_WORKS) == 0)
>> +                                    break;
>> +                            if (mmc_test_bus_width (card, 1<<bus_width))
>
> Extra space here.
>
>> +                                    break;
>> +                    }
>> +
>> +                    if (bus_width == MMC_BUS_WIDTH_8)
>> +                            temp_caps &= ~MMC_CAP_8_BIT_DATA;
>> +                    else
>> +                            temp_caps &= ~MMC_CAP_4_BIT_DATA;
>> +
>> +                    if (temp_caps == 0) {
>> +                            ext_csd_bit = EXT_CSD_BUS_WIDTH_1;
>> +                            bus_width = MMC_BUS_WIDTH_1;
>> +                    }
>> +            } while (temp_caps);
>> +
>> +            if (temp_caps == 0) {
>> +                    ext_csd_bit = EXT_CSD_BUS_WIDTH_1;
>> +                    bus_width = MMC_BUS_WIDTH_1;
>> +            } else if (temp_caps & MMC_CAP_8_BIT_DATA) {
>>                      if (ddr)
>>                              ext_csd_bit = EXT_CSD_DDR_BUS_WIDTH_8;
>>                      else
>
> Why is the "temp_caps == 0" test inside the while loop necessary, rather
> than just relying on the same test outside of the loop?
>
>> diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
>> index 326447c..2b115a3 100644
>> --- a/drivers/mmc/core/mmc_ops.c
>> +++ b/drivers/mmc/core/mmc_ops.c
>> @@ -20,6 +20,138 @@
>> #include "core.h"
>> #include "mmc_ops.h"
>>
>> +int mmc_test_bus_width(struct mmc_card *card, int bits)
>> +{
>> +    struct mmc_request mrq;
>> +    struct mmc_command cmd;
>> +    struct mmc_data data;
>> +    struct scatterlist sg;
>> +    int len;
>> +    u8 test_data_write[8];
>> +    u8 test_data_read[64];
>> +
>> +    switch (bits) {
>> +    case 8:
>> +            test_data_write[0] = 0x55;
>> +            test_data_write[1] = 0xaa;
>> +            test_data_write[2] = 0x00;
>> +            test_data_write[3] = 0x00;
>> +            test_data_write[4] = 0x00;
>> +            test_data_write[5] = 0x00;
>> +            test_data_write[6] = 0x00;
>> +            test_data_write[7] = 0x00;
>> +            len = 8;
>> +            break;
>> +    case 4:
>> +            test_data_write[0] = 0x5a;
>> +            test_data_write[1] = 0x00;
>> +            test_data_write[2] = 0x00;
>> +            test_data_write[3] = 0x00;
>> +            len = 4;
>> +            break;
>> +    default:
>> +            /* 1 bit bus cards ALWAYS work */
>> +            return 1;
>> +    }
>> +
>> +    memset(&mrq, 0, sizeof(struct mmc_request));
>> +    memset(&cmd, 0, sizeof(struct mmc_command));
>> +    memset(&data, 0, sizeof(struct mmc_data));
>> +
>> +    cmd.opcode = MMC_BUSTEST_W;
>> +    cmd.arg = 0;
>> +
>> +    /* NOTE HACK:  the MMC_RSP_SPI_R1 is always correct here, but we
>> +     * rely on callers to never use this with "native" calls for reading
>> +     * CSD or CID.  Native versions of those commands use the R2 type,
>> +     * not R1 plus a data block.
>> +     */
>> +    cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
>> +
>> +    data.flags = MMC_DATA_WRITE;
>> +    data.blksz = len;
>> +    data.blocks = 1;
>> +    data.sg = &sg;
>> +    data.sg_len = 1;
>> +
>> +    mrq.cmd = &cmd;
>> +    mrq.data = &data;
>> +
>> +    sg_init_one(&sg, &test_data_write, len);
>> +
>> +    /*
>> +     * The spec states that MMC_BUSTEST_W and BUSTEST_R accesses
>> +     * have a maximum timeout of 64 clock cycles.
>> +     */
>> +    data.timeout_ns = 0;
>> +    data.timeout_clks = 64;
>> +
>> +    mmc_wait_for_req(card->host, &mrq);
>> +
>> +    if (cmd.error || data.error ) {
>
> Extra space here.
>
>> +            printk(KERN_INFO "%s: Failed to send (BUSTEST_W) CMD19: %d %d\n",
>> +                    mmc_hostname(card->host), cmd.error, data.error);
>> +    }
>> +
>> +    /* Now read back */
>> +    memset(&mrq, 0, sizeof(struct mmc_request));
>> +    memset(&cmd, 0, sizeof(struct mmc_command));
>> +    memset(&data, 0, sizeof(struct mmc_data));
>> +    memset (&test_data_read, 0, sizeof(test_data_read));
>> +
>> +    cmd.opcode = MMC_BUSTEST_R;
>> +    cmd.arg = 0;
>> +
>> +    /* NOTE HACK:  the MMC_RSP_SPI_R1 is always correct here, but we
>> +     * rely on callers to never use this with "native" calls for reading
>> +     * CSD or CID.  Native versions of those commands use the R2 type,
>> +     * not R1 plus a data block.
>> +     */
>> +    cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
>> +
>> +    data.flags = MMC_DATA_READ;
>> +    data.blksz = len;
>> +    data.blocks = 1;
>> +    data.sg = &sg;
>> +    data.sg_len = 1;
>> +
>> +    mrq.cmd = &cmd;
>> +    mrq.data = &data;
>> +
>> +    sg_init_one(&sg, &test_data_read, len);
>> +
>> +    data.timeout_ns = 0;
>> +    data.timeout_clks = 64;
>> +
>> +    mmc_wait_for_req(card->host, &mrq);
>> +
>> +    if (cmd.error) {
>> +            printk(KERN_INFO "%s: Failed to send CMD14: %d %d\n",
>> +                    mmc_hostname(card->host), cmd.error, data.error);
>> +            return 0;
>> +    }
>> +
>> +#if 0
>> +#warning PRINT RESULTS FROM CMD14
>> +    printk (KERN_INFO "%s: Bits = %d, Got %02X %02X %02X %02X\n", __FUNCTION__,
>
> Extra space, and please don't submit #if 0'd code.  You can use a debug
> level printk if you want to condition it on CONFIG_MMC_DEBUG.  Also,
> __func__ instead of __FUNCTION__.
>
> Thanks!
>
> - Chris.
> --
> Chris Ball   <cjb@laptop.org>   <http://printf.net/>
> One Laptop Per Child

====== REVISED PATCH BELOW =======

From feabf089d8b1eff6e809ffe1a174ed1365cd6464 Mon Sep 17 00:00:00 2001
From: Philip Rakity <prakity@marvell.com>
Date: Sat, 27 Nov 2010 15:26:40 -0800
Subject: [PATCH] mmc: Add Bus Width testing for mmc/eMMC Cards
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 8bit

Change from previous patch:

MMC_CAP_BUS_WIDTH_WORKS renamed MMC_CAP_BUS_WIDTH_TEST
        value changed from 1<<13 to 1<<14 to not conflict with earlier patch
if 0 / endif removed in mmc_ops.c -- pr_debug used instead
        __FUNCTION__ no longer used in print changed  to __func__
extra spaces removed
column size adjusted to fit 80 columns

Change from previous patch;
Added MMC_RSP_SPI_R1 to mmc_ops command for DATA_READ and DATA_Write
(Thanks to Takashi Lwai for pointing this out)

eMMC cards do not have a card specific field indicating the bus width that
they support.
The bus width needs to be figured out by probing the bus.

JEDEC STANDARD
Embedded MultiMediaCard(e•MMC) e•MMC/Card Product Standard, High Capacity,
including Reliable Write, Boot, Sleep Modes, Dual Data Rate,
Multiple Partitions Supports, Security Enhancement, Background Operation and
High Priority Interrupt (MMCA, 4.41)
JESD84-A441

defines what needs to be done in Annex A.8.3.

In earlier testing (2.6.2x) this code did not work on some PCIe SD controllers.
We define a new MMC_CAP: MMC_CAP_BUS_WIDTH_WORKS that the host adaptation
layer can set if the controller can support bus width testing.
If the CAP is not defined the behavior defaults to what is done today;
the largest bit width is selected.

Transcend 1GB and 2GB  MMC cards work when this code is enabled and fail otherwise.

Signed-off-by: Philip Rakity <prakity@marvell.com>
---
 drivers/mmc/core/mmc.c     |   50 ++++++++++++++++-
 drivers/mmc/core/mmc_ops.c |  130 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/mmc/core/mmc_ops.h |    1 +
 include/linux/mmc/host.h   |    1 +
 include/linux/mmc/mmc.h    |    2 +
 5 files changed, 181 insertions(+), 3 deletions(-)

diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 77f93c3..0eccd96 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -531,12 +531,56 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,

        /*
         * Activate wide bus and DDR (if supported).
+        * Determine mmc bus width supported by probing card (if supported)
         */
        if ((card->csd.mmca_vsn >= CSD_SPEC_VER_4) &&
            (host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA))) {
                unsigned ext_csd_bit, bus_width;
+               int temp_caps = host->caps &
+                       (MMC_CAP_8_BIT_DATA | MMC_CAP_4_BIT_DATA);

-               if (host->caps & MMC_CAP_8_BIT_DATA) {
+               do {
+                       if (temp_caps & MMC_CAP_8_BIT_DATA) {
+                               ext_csd_bit = EXT_CSD_BUS_WIDTH_8;
+                               bus_width = MMC_BUS_WIDTH_8;
+                       } else {
+                               ext_csd_bit = EXT_CSD_BUS_WIDTH_4;
+                               bus_width = MMC_BUS_WIDTH_4;
+                       }
+
+                       err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+                                        EXT_CSD_BUS_WIDTH, ext_csd_bit);
+                       if (err) {
+                               printk(KERN_WARNING "%s: switch to bus width %d"
+                                       " ddr %d failed\n",
+                                       mmc_hostname(card->host),
+                                       1 << bus_width, ddr);
+                               err = 0;
+                       } else {
+                               mmc_set_bus_width_ddr(card->host, bus_width,
+                                       MMC_SDR_MODE);
+                               /*
+                                * if controller can't handle bus width test
+                                * try to use the highest bus width to
+                                * maintain compatibility with previous linux
+                                */
+                               if ((host->caps & MMC_CAP_BUS_WIDTH_TEST) == 0)
+                                       break;
+                               if (mmc_test_bus_width (card, 1<<bus_width))
+                                       break;
+                       }
+
+                       if (bus_width == MMC_BUS_WIDTH_8)
+                               temp_caps &= ~MMC_CAP_8_BIT_DATA;
+                       else
+                               temp_caps &= ~MMC_CAP_4_BIT_DATA;
+
+               } while (temp_caps);
+
+               if (temp_caps == 0) {
+                       ext_csd_bit = EXT_CSD_BUS_WIDTH_1;
+                       bus_width = MMC_BUS_WIDTH_1;
+               } else if (temp_caps & MMC_CAP_8_BIT_DATA) {
                        if (ddr)
                                ext_csd_bit = EXT_CSD_DDR_BUS_WIDTH_8;
                        else
@@ -557,8 +601,8 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
                        goto free_card;

                if (err) {
-                       printk(KERN_WARNING "%s: switch to bus width %d ddr %d "
-                              "failed\n", mmc_hostname(card->host),
+                       printk(KERN_WARNING "%s: switch to bus width %d "
+                              "ddr %d failed\n", mmc_hostname(card->host),
                               1 << bus_width, ddr);
                        err = 0;
                } else {
diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
index 326447c..4ea4a82 100644
--- a/drivers/mmc/core/mmc_ops.c
+++ b/drivers/mmc/core/mmc_ops.c
@@ -20,6 +20,136 @@
 #include "core.h"
 #include "mmc_ops.h"

+int mmc_test_bus_width(struct mmc_card *card, int bits)
+{
+       struct mmc_request mrq;
+       struct mmc_command cmd;
+       struct mmc_data data;
+       struct scatterlist sg;
+       int len;
+       u8 test_data_write[8];
+       u8 test_data_read[64];
+
+       switch (bits) {
+       case 8:
+               test_data_write[0] = 0x55;
+               test_data_write[1] = 0xaa;
+               test_data_write[2] = 0x00;
+               test_data_write[3] = 0x00;
+               test_data_write[4] = 0x00;
+               test_data_write[5] = 0x00;
+               test_data_write[6] = 0x00;
+               test_data_write[7] = 0x00;
+               len = 8;
+               break;
+       case 4:
+               test_data_write[0] = 0x5a;
+               test_data_write[1] = 0x00;
+               test_data_write[2] = 0x00;
+               test_data_write[3] = 0x00;
+               len = 4;
+               break;
+       default:
+               /* 1 bit bus cards ALWAYS work */
+               return 1;
+       }
+
+       memset(&mrq, 0, sizeof(struct mmc_request));
+       memset(&cmd, 0, sizeof(struct mmc_command));
+       memset(&data, 0, sizeof(struct mmc_data));
+
+       cmd.opcode = MMC_BUSTEST_W;
+       cmd.arg = 0;
+
+       /* NOTE HACK:  the MMC_RSP_SPI_R1 is always correct here, but we
+        * rely on callers to never use this with "native" calls for reading
+        * CSD or CID.  Native versions of those commands use the R2 type,
+        * not R1 plus a data block.
+        */
+       cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
+
+       data.flags = MMC_DATA_WRITE;
+       data.blksz = len;
+       data.blocks = 1;
+       data.sg = &sg;
+       data.sg_len = 1;
+
+       mrq.cmd = &cmd;
+       mrq.data = &data;
+
+       sg_init_one(&sg, &test_data_write, len);
+
+       /*
+        * The spec states that MMC_BUSTEST_W and BUSTEST_R accesses
+        * have a maximum timeout of 64 clock cycles.
+        */
+       data.timeout_ns = 0;
+       data.timeout_clks = 64;
+
+       mmc_wait_for_req(card->host, &mrq);
+
+       if (cmd.error || data.error ) {
+               printk(KERN_INFO "%s: Failed to send (BUSTEST_W) CMD19: %d %d\n",
+                       mmc_hostname(card->host), cmd.error, data.error);
+       }
+
+       /* Now read back */
+       memset(&mrq, 0, sizeof(struct mmc_request));
+       memset(&cmd, 0, sizeof(struct mmc_command));
+       memset(&data, 0, sizeof(struct mmc_data));
+       memset (&test_data_read, 0, sizeof(test_data_read));
+
+       cmd.opcode = MMC_BUSTEST_R;
+       cmd.arg = 0;
+
+       /* NOTE HACK:  the MMC_RSP_SPI_R1 is always correct here, but we
+        * rely on callers to never use this with "native" calls for reading
+        * CSD or CID.  Native versions of those commands use the R2 type,
+        * not R1 plus a data block.
+        */
+       cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
+
+       data.flags = MMC_DATA_READ;
+       data.blksz = len;
+       data.blocks = 1;
+       data.sg = &sg;
+       data.sg_len = 1;
+
+       mrq.cmd = &cmd;
+       mrq.data = &data;
+
+       sg_init_one(&sg, &test_data_read, len);
+
+       data.timeout_ns = 0;
+       data.timeout_clks = 64;
+
+       mmc_wait_for_req(card->host, &mrq);
+
+       if (cmd.error) {
+               printk(KERN_INFO "%s: Failed to send CMD14: %d %d\n",
+                       mmc_hostname(card->host), cmd.error, data.error);
+               return 0;
+       }
+
+       pr_debug ("%s: Bits = %d, Got %02X %02X %02X %02X\n",
+                __func__,
+               bits,
+               test_data_read[0],
+               test_data_read[1],
+               test_data_read[2],
+               test_data_read[3]);
+
+       switch (bits) {
+       case 8:
+               return (test_data_read[0] == 0xaa && test_data_read[1] == 0x55);
+       case 4:
+               return (test_data_read[0] == 0xa5);
+       case 1:
+               return (test_data_read[0] == 0x80);
+       }
+       return 0;
+}
+
 static int _mmc_select_card(struct mmc_host *host, struct mmc_card *card)
 {
        int err;
diff --git a/drivers/mmc/core/mmc_ops.h b/drivers/mmc/core/mmc_ops.h
index 653eb8e..c08b9ad 100644
--- a/drivers/mmc/core/mmc_ops.h
+++ b/drivers/mmc/core/mmc_ops.h
@@ -26,6 +26,7 @@ int mmc_send_cid(struct mmc_host *host, u32 *cid);
 int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp);
 int mmc_spi_set_crc(struct mmc_host *host, int use_crc);
 int mmc_card_sleepawake(struct mmc_host *host, int sleep);
+int mmc_test_bus_width(struct mmc_card *card, int bits);

 #endif

diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 381c77f..078cff7 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -170,6 +170,7 @@ 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 */
        mmc_pm_flag_t           pm_caps;        /* supported pm features */

 #ifdef CONFIG_MMC_CLKGATE
diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
index 956fbd8..8e0d047 100644
--- a/include/linux/mmc/mmc.h
+++ b/include/linux/mmc/mmc.h
@@ -40,7 +40,9 @@
 #define MMC_READ_DAT_UNTIL_STOP  11   /* adtc [31:0] dadr        R1  */
 #define MMC_STOP_TRANSMISSION    12   /* ac                      R1b */
 #define MMC_SEND_STATUS          13   /* ac   [31:16] RCA        R1  */
+#define MMC_BUSTEST_R            14   /* adtc                    R1  */
 #define MMC_GO_INACTIVE_STATE    15   /* ac   [31:16] RCA            */
+#define MMC_BUSTEST_W            19   /* adtc                    R1  */
 #define MMC_SPI_READ_OCR         58   /* spi                  spi_R3 */
 #define MMC_SPI_CRC_ON_OFF       59   /* spi  [0:0] flag      spi_R1 */

--
1.6.0.4



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

end of thread, other threads:[~2010-11-27 23:37 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-11-27 19:00 [PATCH] mmc: Resubmit: Bus Width testing for eMMC and MMC Cards Philip Rakity
2010-11-27 19:32 ` Chris Ball
2010-11-27 23:37   ` Philip Rakity

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.