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