Re: [RFC]: mmc bus width testing - MMC_RSP_SPI_R1 question

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

 



see below -- thanks to Takashi Iwai for pointing this out.

On Nov 23, 2010, at 5:17 PM, Philip Rakity wrote:

> 
> 
> On Nov 23, 2010, at 7:56 AM, Philip Rakity wrote:
> 
>> 
>> A long time (2.6.2x days) I tried to submit a patch to do bus width testing on mmc and would appreciate some feedback before I do a formal submission.
>> 
>> Bus width testing is defined in JEDEC Standard No. 84-A441: section A.8.3 Changing the data bus width
>> 
>> The problem is that it does NOT work in all cases.  Some controllers do not handle the CMD19/CMD14 exchange.
>> (BUSTEST_W/BUSTEST_R)
>> 
>> When this failure happens it is not at all clear what to do....
>> 
>> We could default to 1 bit mode and just do what we do today and take 4 bit bus width -- assuming CAPS are appropriately enabled.
>> 
>> I was thinking of adding a new MMC_CAP saying controller supports bus width testing.      Any thoughts ?
>> 
>> <snippet of code from today implementation == based on 2.6.32 but with DDR support back ported>
>> 
>> look for mmc_test_bus_width()
>> 
> 

<snip> 
> 
> 
> Diff made against linux-next (see below) and tested against marvell mmp2 controller using mmp2
> 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@xxxxxxxxxxx>
> 
> 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..5795e1b 100644
> --- a/drivers/mmc/core/mmc_ops.c
> +++ b/drivers/mmc/core/mmc_ops.c
> @@ -20,6 +20,126 @@
> #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;

> +       cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;

should this be changed to add MMC_RSP_SPI_R1 ?

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

 
> +       cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;

should this be changed to add MMC_RSP_SPI_R1 ?

+	/* 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 */
> 
> 
>> 
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo@xxxxxxxxxxxxxxx
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

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


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

  Powered by Linux