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() > > My thought was to replace > > 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 (mmc_test_bus_width (card, 1<<bus_width)) > break; > } > > with > > 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 ((mmc->caps & MMC_CAPS_CONTROLLER_SUPPORTS_BUSTEST) == 0) > break; > if (mmc_test_bus_width (card, 1<<bus_width)) > break; > } > > and let the platform code set MMC_CAPS_CONTROLLER_SUPPORTS_BUSTEST > > > > from mmc.c > ========== > > static int mmc_init_card(struct mmc_host *host, u32 ocr, > struct mmc_card *oldcard) > { > struct mmc_card *card; > int err, ddr = 0; > u32 cid[4]; > unsigned int max_dtr; > u32 rocr; > > BUG_ON(!host); > WARN_ON(!host->claimed); > > /* > * Since we're changing the OCR value, we seem to > * need to tell some cards to go back to the idle > * state. We wait 1ms to give cards time to > * respond. > */ > mmc_go_idle(host); > > /* The extra bit indicates that we support high capacity */ > err = mmc_send_op_cond(host, ocr | MMC_CARD_SECTOR_ADDR, &rocr); > if (err) > goto err; > > /* > * For SPI, enable CRC as appropriate. > */ > if (mmc_host_is_spi(host)) { > err = mmc_spi_set_crc(host, use_spi_crc); > if (err) > goto err; > } > > /* > * Fetch CID from card. > */ > if (mmc_host_is_spi(host)) > err = mmc_send_cid(host, cid); > else > err = mmc_all_send_cid(host, cid); > if (err) > goto err; > > if (oldcard) { > if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0) { > err = -ENOENT; > goto err; > } > > card = oldcard; > } else { > /* > * Allocate card structure. > */ > card = mmc_alloc_card(host, &mmc_type); > if (IS_ERR(card)) { > err = PTR_ERR(card); > goto err; > } > > card->type = MMC_TYPE_MMC; > card->rca = 1; > memcpy(card->raw_cid, cid, sizeof(card->raw_cid)); > } > > /* > * For native busses: set card RCA and quit open drain mode. > */ > if (!mmc_host_is_spi(host)) { > err = mmc_set_relative_addr(card); > if (err) > goto free_card; > > mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL); > } > > if (!oldcard) { > /* > * Fetch CSD from card. > */ > err = mmc_send_csd(card, card->raw_csd); > if (err) > goto free_card; > > err = mmc_decode_csd(card); > if (err) > goto free_card; > err = mmc_decode_cid(card); > if (err) > goto free_card; > } > > /* > * Select card, as all following commands rely on that. > */ > if (!mmc_host_is_spi(host)) { > err = mmc_select_card(card); > if (err) > goto free_card; > } > > #if 0 > if (mmc_card_mmc(card)) { > /* work around of Micorn 16G emmc */ > /* set bus_width to 1 bit as init state */ > mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, > EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH_1); > > } > #endif > > if (!oldcard) { > /* > * Fetch and process extended CSD. > */ > err = mmc_read_ext_csd(card); > if (err) > goto free_card; > > if (rocr & MMC_CARD_SECTOR_ADDR) > mmc_card_set_blockaddr(card); > } > > > /* switch to user partition, emmc may stay in boot partition after boot */ > mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, > EXT_CSD_PART_CONF, 0); > > /* > * Activate high speed (if supported) > */ > if ((card->ext_csd.hs_max_dtr != 0) && > (host->caps & MMC_CAP_MMC_HIGHSPEED)) { > err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, > EXT_CSD_HS_TIMING, 1); > if (err && err != -EBADMSG) > goto free_card; > > if (err) { > printk(KERN_WARNING "%s: switch to highspeed failed\n", > mmc_hostname(card->host)); > err = 0; > } else { > mmc_card_set_highspeed(card); > mmc_set_timing(card->host, MMC_TIMING_MMC_HS); > } > } > > /* > * Compute bus speed. > */ > max_dtr = (unsigned int)-1; > > if (mmc_card_highspeed(card)) { > if (max_dtr > card->ext_csd.hs_max_dtr) > max_dtr = card->ext_csd.hs_max_dtr; > } else if (max_dtr > card->csd.max_dtr) { > max_dtr = card->csd.max_dtr; > } > > mmc_set_clock(host, max_dtr); > > /* > * Indicate DDR mode (if supported). > */ > if (mmc_card_highspeed(card)) { > if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_8V) > && (host->caps & (MMC_CAP_1_8V_DDR))) > ddr = MMC_1_8V_DDR_MODE; > else if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_2V) > && (host->caps & (MMC_CAP_1_2V_DDR))) > ddr = MMC_1_2V_DDR_MODE; > } > > /* > * Activate wide bus and DDR (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); > > 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 (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 > ext_csd_bit = EXT_CSD_BUS_WIDTH_8; > bus_width = MMC_BUS_WIDTH_8; > } else { > if (ddr) > ext_csd_bit = EXT_CSD_DDR_BUS_WIDTH_4; > 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 (ddr) { > mmc_card_set_ddr_mode(card); > mmc_set_bus_width_ddr(card->host, bus_width, ddr); > } else > mmc_set_bus_width_ddr(card->host, bus_width, MMC_SDR_MODE); > > if (err && err != -EBADMSG) > goto free_card; > } > > if (!oldcard) > host->card = card; > > return 0; > > free_card: > if (!oldcard) > mmc_remove_card(card); > err: > > return err; > } > > > from mmc_ops.c > ============= > 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: > return 0; > } > > 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; > > data.flags = MMC_DATA_WRITE; > data.blksz = 64; > data.blocks = 1; > data.sg = &sg; > data.sg_len = 1; > > mrq.cmd = &cmd; > mrq.data = &data; > > sg_init_one(&sg, &test_data_write, 64); > > /* > * The spec states that MMC_BUSTEST_W and BUSTEST_R accesses have a 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 "Failed to send CMD19: %d %d\n", cmd.error, data.error); > return 0; > } > > /* 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; > > data.flags = MMC_DATA_READ; > data.blksz = sizeof(test_data_read); > data.blocks = 1; > data.sg = &sg; > data.sg_len = 1; > > mrq.cmd = &cmd; > mrq.data = &data; > > sg_init_one(&sg, &test_data_read, sizeof(test_data_read)); > > /* > * The spec states that MMC_BUSTEST_W and BUSTEST_R accesses have a timeout > * of 64 clock cycles. > */ > data.timeout_ns = 0; > data.timeout_clks = 64; > > mmc_wait_for_req(card->host, &mrq); > > if (cmd.error) { > printk(KERN_INFO "Failed to send CMD14: %d %d\n", cmd.error, data.error); > return 0; > } > > #if 0 > #warning PRINT RESULTS FROM CMD14 > printk (KERN_INFO "%s: Got %02X %02X %02X %02X\n", __FUNCTION__, > 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); > } > return 0; > } 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; + + 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; + + 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