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;
}
--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html