mmc: Test bus-width for old MMC devices
Some old MMC devices fail with the 4/8 bits the driver tries to use exclusively. This patch adds a test for the given bus setup and falls back to the lower bit mode (until 1-bit mode) when the test fails. [Major rework and refactoring by tiwai] [Quirk addition and many fixes by prakity] Signed-off-by: Aries Lee <arieslee@jmicron.com> Signed-off-by: Takashi Iwai <tiwai@suse.de> Signed-off-by: Philip Rakity <prakity@marvell.com> Tested-by: Philip Rakity <prakity@marvell.com> Signed-off-by: Chris Ball <cjb@laptop.org>
This commit is contained in:
parent
e6f29a8dc1
commit
22113efd00
7 changed files with 158 additions and 29 deletions
|
@ -534,39 +534,57 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
||||||
*/
|
*/
|
||||||
if ((card->csd.mmca_vsn >= CSD_SPEC_VER_4) &&
|
if ((card->csd.mmca_vsn >= CSD_SPEC_VER_4) &&
|
||||||
(host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA))) {
|
(host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA))) {
|
||||||
unsigned ext_csd_bit, bus_width;
|
static unsigned ext_csd_bits[][2] = {
|
||||||
|
{ EXT_CSD_BUS_WIDTH_8, EXT_CSD_DDR_BUS_WIDTH_8 },
|
||||||
|
{ EXT_CSD_BUS_WIDTH_4, EXT_CSD_DDR_BUS_WIDTH_4 },
|
||||||
|
{ EXT_CSD_BUS_WIDTH_1, EXT_CSD_BUS_WIDTH_1 },
|
||||||
|
};
|
||||||
|
static unsigned bus_widths[] = {
|
||||||
|
MMC_BUS_WIDTH_8,
|
||||||
|
MMC_BUS_WIDTH_4,
|
||||||
|
MMC_BUS_WIDTH_1
|
||||||
|
};
|
||||||
|
unsigned idx, bus_width = 0;
|
||||||
|
|
||||||
if (host->caps & MMC_CAP_8_BIT_DATA) {
|
if (host->caps & MMC_CAP_8_BIT_DATA)
|
||||||
if (ddr)
|
idx = 0;
|
||||||
ext_csd_bit = EXT_CSD_DDR_BUS_WIDTH_8;
|
else
|
||||||
else
|
idx = 1;
|
||||||
ext_csd_bit = EXT_CSD_BUS_WIDTH_8;
|
for (; idx < ARRAY_SIZE(bus_widths); idx++) {
|
||||||
bus_width = MMC_BUS_WIDTH_8;
|
bus_width = bus_widths[idx];
|
||||||
} else {
|
if (bus_width == MMC_BUS_WIDTH_1)
|
||||||
if (ddr)
|
ddr = 0; /* no DDR for 1-bit width */
|
||||||
ext_csd_bit = EXT_CSD_DDR_BUS_WIDTH_4;
|
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||||
else
|
EXT_CSD_BUS_WIDTH,
|
||||||
ext_csd_bit = EXT_CSD_BUS_WIDTH_4;
|
ext_csd_bits[idx][0]);
|
||||||
bus_width = MMC_BUS_WIDTH_4;
|
if (!err) {
|
||||||
|
/*
|
||||||
|
* If controller can't handle bus width test,
|
||||||
|
* use the highest bus width to maintain
|
||||||
|
* compatibility with previous MMC behavior.
|
||||||
|
*/
|
||||||
|
if (!(host->caps & MMC_CAP_BUS_WIDTH_TEST))
|
||||||
|
break;
|
||||||
|
mmc_set_bus_width_ddr(card->host,
|
||||||
|
bus_width, MMC_SDR_MODE);
|
||||||
|
err = mmc_bus_test(card, bus_width);
|
||||||
|
if (!err)
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
if (!err && ddr) {
|
||||||
EXT_CSD_BUS_WIDTH, ext_csd_bit);
|
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||||
|
EXT_CSD_BUS_WIDTH,
|
||||||
if (err && err != -EBADMSG)
|
ext_csd_bits[idx][1]);
|
||||||
goto free_card;
|
}
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
printk(KERN_WARNING "%s: switch to bus width %d ddr %d "
|
printk(KERN_WARNING "%s: switch to bus width %d ddr %d "
|
||||||
"failed\n", mmc_hostname(card->host),
|
"failed\n", mmc_hostname(card->host),
|
||||||
1 << bus_width, ddr);
|
1 << bus_width, ddr);
|
||||||
err = 0;
|
goto free_card;
|
||||||
} else {
|
} else if (ddr) {
|
||||||
if (ddr)
|
mmc_card_set_ddr_mode(card);
|
||||||
mmc_card_set_ddr_mode(card);
|
|
||||||
else
|
|
||||||
ddr = MMC_SDR_MODE;
|
|
||||||
|
|
||||||
mmc_set_bus_width_ddr(card->host, bus_width, ddr);
|
mmc_set_bus_width_ddr(card->host, bus_width, ddr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -462,3 +462,104 @@ int mmc_send_status(struct mmc_card *card, u32 *status)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
mmc_send_bus_test(struct mmc_card *card, struct mmc_host *host, u8 opcode,
|
||||||
|
u8 len)
|
||||||
|
{
|
||||||
|
struct mmc_request mrq;
|
||||||
|
struct mmc_command cmd;
|
||||||
|
struct mmc_data data;
|
||||||
|
struct scatterlist sg;
|
||||||
|
u8 *data_buf;
|
||||||
|
u8 *test_buf;
|
||||||
|
int i, err;
|
||||||
|
static u8 testdata_8bit[8] = { 0x55, 0xaa, 0, 0, 0, 0, 0, 0 };
|
||||||
|
static u8 testdata_4bit[4] = { 0x5a, 0, 0, 0 };
|
||||||
|
|
||||||
|
/* dma onto stack is unsafe/nonportable, but callers to this
|
||||||
|
* routine normally provide temporary on-stack buffers ...
|
||||||
|
*/
|
||||||
|
data_buf = kmalloc(len, GFP_KERNEL);
|
||||||
|
if (!data_buf)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
if (len == 8)
|
||||||
|
test_buf = testdata_8bit;
|
||||||
|
else if (len == 4)
|
||||||
|
test_buf = testdata_4bit;
|
||||||
|
else {
|
||||||
|
printk(KERN_ERR "%s: Invalid bus_width %d\n",
|
||||||
|
mmc_hostname(host), len);
|
||||||
|
kfree(data_buf);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opcode == MMC_BUS_TEST_W)
|
||||||
|
memcpy(data_buf, test_buf, len);
|
||||||
|
|
||||||
|
memset(&mrq, 0, sizeof(struct mmc_request));
|
||||||
|
memset(&cmd, 0, sizeof(struct mmc_command));
|
||||||
|
memset(&data, 0, sizeof(struct mmc_data));
|
||||||
|
|
||||||
|
mrq.cmd = &cmd;
|
||||||
|
mrq.data = &data;
|
||||||
|
cmd.opcode = opcode;
|
||||||
|
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.blksz = len;
|
||||||
|
data.blocks = 1;
|
||||||
|
if (opcode == MMC_BUS_TEST_R)
|
||||||
|
data.flags = MMC_DATA_READ;
|
||||||
|
else
|
||||||
|
data.flags = MMC_DATA_WRITE;
|
||||||
|
|
||||||
|
data.sg = &sg;
|
||||||
|
data.sg_len = 1;
|
||||||
|
sg_init_one(&sg, data_buf, len);
|
||||||
|
mmc_wait_for_req(host, &mrq);
|
||||||
|
err = 0;
|
||||||
|
if (opcode == MMC_BUS_TEST_R) {
|
||||||
|
for (i = 0; i < len / 4; i++)
|
||||||
|
if ((test_buf[i] ^ data_buf[i]) != 0xff) {
|
||||||
|
err = -EIO;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
kfree(data_buf);
|
||||||
|
|
||||||
|
if (cmd.error)
|
||||||
|
return cmd.error;
|
||||||
|
if (data.error)
|
||||||
|
return data.error;
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
int mmc_bus_test(struct mmc_card *card, u8 bus_width)
|
||||||
|
{
|
||||||
|
int err, width;
|
||||||
|
|
||||||
|
if (bus_width == MMC_BUS_WIDTH_8)
|
||||||
|
width = 8;
|
||||||
|
else if (bus_width == MMC_BUS_WIDTH_4)
|
||||||
|
width = 4;
|
||||||
|
else if (bus_width == MMC_BUS_WIDTH_1)
|
||||||
|
return 0; /* no need for test */
|
||||||
|
else
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ignore errors from BUS_TEST_W. BUS_TEST_R will fail if there
|
||||||
|
* is a problem. This improves chances that the test will work.
|
||||||
|
*/
|
||||||
|
mmc_send_bus_test(card, card->host, MMC_BUS_TEST_W, width);
|
||||||
|
err = mmc_send_bus_test(card, card->host, MMC_BUS_TEST_R, width);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
|
@ -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_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp);
|
||||||
int mmc_spi_set_crc(struct mmc_host *host, int use_crc);
|
int mmc_spi_set_crc(struct mmc_host *host, int use_crc);
|
||||||
int mmc_card_sleepawake(struct mmc_host *host, int sleep);
|
int mmc_card_sleepawake(struct mmc_host *host, int sleep);
|
||||||
|
int mmc_bus_test(struct mmc_card *card, u8 bus_width);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
|
|
||||||
#include <linux/leds.h>
|
#include <linux/leds.h>
|
||||||
|
|
||||||
|
#include <linux/mmc/mmc.h>
|
||||||
#include <linux/mmc/host.h>
|
#include <linux/mmc/host.h>
|
||||||
|
|
||||||
#include "sdhci.h"
|
#include "sdhci.h"
|
||||||
|
@ -1521,7 +1522,11 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
|
||||||
|
|
||||||
if (intmask & SDHCI_INT_DATA_TIMEOUT)
|
if (intmask & SDHCI_INT_DATA_TIMEOUT)
|
||||||
host->data->error = -ETIMEDOUT;
|
host->data->error = -ETIMEDOUT;
|
||||||
else if (intmask & (SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_END_BIT))
|
else if (intmask & SDHCI_INT_DATA_END_BIT)
|
||||||
|
host->data->error = -EILSEQ;
|
||||||
|
else if ((intmask & SDHCI_INT_DATA_CRC) &&
|
||||||
|
SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND))
|
||||||
|
!= MMC_BUS_TEST_R)
|
||||||
host->data->error = -EILSEQ;
|
host->data->error = -EILSEQ;
|
||||||
else if (intmask & SDHCI_INT_ADMA_ERROR) {
|
else if (intmask & SDHCI_INT_ADMA_ERROR) {
|
||||||
printk(KERN_ERR "%s: ADMA error\n", mmc_hostname(host->mmc));
|
printk(KERN_ERR "%s: ADMA error\n", mmc_hostname(host->mmc));
|
||||||
|
|
|
@ -52,6 +52,7 @@
|
||||||
#define SDHCI_CMD_RESP_SHORT_BUSY 0x03
|
#define SDHCI_CMD_RESP_SHORT_BUSY 0x03
|
||||||
|
|
||||||
#define SDHCI_MAKE_CMD(c, f) (((c & 0xff) << 8) | (f & 0xff))
|
#define SDHCI_MAKE_CMD(c, f) (((c & 0xff) << 8) | (f & 0xff))
|
||||||
|
#define SDHCI_GET_CMD(c) ((c>>8) & 0x3f)
|
||||||
|
|
||||||
#define SDHCI_RESPONSE 0x10
|
#define SDHCI_RESPONSE 0x10
|
||||||
|
|
||||||
|
|
|
@ -172,6 +172,7 @@ struct mmc_host {
|
||||||
#define MMC_CAP_1_2V_DDR (1 << 12) /* can support */
|
#define MMC_CAP_1_2V_DDR (1 << 12) /* can support */
|
||||||
/* DDR mode at 1.2V */
|
/* DDR mode at 1.2V */
|
||||||
#define MMC_CAP_POWER_OFF_CARD (1 << 13) /* Can power off after boot */
|
#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 */
|
mmc_pm_flag_t pm_caps; /* supported pm features */
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,9 @@
|
||||||
#define MMC_READ_DAT_UNTIL_STOP 11 /* adtc [31:0] dadr R1 */
|
#define MMC_READ_DAT_UNTIL_STOP 11 /* adtc [31:0] dadr R1 */
|
||||||
#define MMC_STOP_TRANSMISSION 12 /* ac R1b */
|
#define MMC_STOP_TRANSMISSION 12 /* ac R1b */
|
||||||
#define MMC_SEND_STATUS 13 /* ac [31:16] RCA R1 */
|
#define MMC_SEND_STATUS 13 /* ac [31:16] RCA R1 */
|
||||||
|
#define MMC_BUS_TEST_R 14 /* adtc R1 */
|
||||||
#define MMC_GO_INACTIVE_STATE 15 /* ac [31:16] RCA */
|
#define MMC_GO_INACTIVE_STATE 15 /* ac [31:16] RCA */
|
||||||
|
#define MMC_BUS_TEST_W 19 /* adtc R1 */
|
||||||
#define MMC_SPI_READ_OCR 58 /* spi spi_R3 */
|
#define MMC_SPI_READ_OCR 58 /* spi spi_R3 */
|
||||||
#define MMC_SPI_CRC_ON_OFF 59 /* spi [0:0] flag spi_R1 */
|
#define MMC_SPI_CRC_ON_OFF 59 /* spi [0:0] flag spi_R1 */
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue