Skip to content
Permalink
Browse files

mci_media/sdhc: fix the 8/4bit bus width BUSTEST_W testing

This fixes the MMC 8bit or 4bit bus width testing on eMMC.

First, the eMMC needs a higher frequency, 52MHz, then, it needs
a high speed bit set.
Before we can set the bus speed to more than 1 bit, we need
to set the BLOCKLEN to 512 bytes. This is done with initally
sending a CMD16.
To switch to 4 or 8 bits, we use a CMD6 SWITCH command.
After that, the actual bus lines DAT[0:7] are being tested with
a BUSTEST_W  CMD19 command that send a pattern to the memory.
with BUSTEST_R CMD14 we read back the data and check that it's the previously
set pattern.
Then we can conclude that the bus is OK or we need to default to 1bit bus width.

There is an issue with BUSTEST_W. after sending the pattern, the SDMMC IP
doesn't trigger a TRFC status. Thus, we use a sw workaround to reset
the DAT and CMD lines by using the SRR software reset register.
The pattern still gets sent correctly , because BUSTEST_R works fine on
reading it back.

After testing, it looks like speed is about 2x better than 1bit bus, on
sama5d2_xplained board.

Signed-off-by: Eugen Hristev <eugen.hristev@microchip.com>
Tested-by: Tim Du <tim.du@microchip.com>
  • Loading branch information...
ehristev committed Jun 8, 2018
1 parent 97d5b5b commit 8b9064beb6169f2c3be089fa99c5080b2182639f
Showing with 59 additions and 18 deletions.
  1. +17 −5 driver/mci_media.c
  2. +42 −13 driver/sdhc.c
@@ -40,6 +40,9 @@ static struct sd_command sdcard_command;
static struct sd_data sdcard_data;
static struct sd_card atmel_sdcard;

static int sd_cmd_set_blocklen(struct sd_card *sdcard,
unsigned int block_len);

static int sd_cmd_go_idle_state(struct sd_card *sdcard)
{
struct sd_host *host = sdcard->host;
@@ -742,6 +745,10 @@ static int mmc_bus_width_select(struct sd_card *sdcard, unsigned int buswidth)
return ret;
}

sd_cmd_send_status(sdcard, 1000);
if (ret)
return ret;

return 0;
}

@@ -981,11 +988,10 @@ static int mmc_initialization(struct sd_card *sdcard)
if (ret)
return ret;

if (sdcard->sd_spec_version >= MMC_VERSION_4) {
ret = mmc_detect_buswidth(sdcard);
if (ret)
return ret;
}
ret = sd_cmd_set_blocklen(sdcard, DEFAULT_SD_BLOCK_LEN);

if (ret)
return 0;

if (sdcard->host->caps_high_speed) {
if (sdcard->sd_spec_version >= MMC_VERSION_4) {
@@ -1002,6 +1008,12 @@ static int mmc_initialization(struct sd_card *sdcard)
host->ops->set_clock(sdcard, 26000000);
}

if (sdcard->sd_spec_version >= MMC_VERSION_4) {
ret = mmc_detect_buswidth(sdcard);
if (ret)
return ret;
}

return 0;
}

@@ -307,23 +307,23 @@ static void sdhc_writeb(unsigned int reg, unsigned char value)
writeb(value, sdhc_get_base() + reg);
}

static void sdhc_softare_reset(void)
static void sdhc_software_reset(void)
{
sdhc_writeb(SDMMC_SRR, SDMMC_SRR_SWRSTALL);

while (sdhc_readb(SDMMC_SRR) & SDMMC_SRR_SWRSTALL)
;
}

static void sdhc_softare_reset_cmd(void)
static void sdhc_software_reset_cmd(void)
{
sdhc_writeb(SDMMC_SRR, SDMMC_SRR_SWRSTCMD);

while (sdhc_readb(SDMMC_SRR) & SDMMC_SRR_SWRSTCMD)
;
}

static void sdhc_softare_reset_dat(void)
static void sdhc_software_reset_dat(void)
{
sdhc_writeb(SDMMC_SRR, SDMMC_SRR_SWRSTDAT);

@@ -520,7 +520,7 @@ static int sdhc_init(struct sd_card *sdcard)
{
unsigned int normal_status_mask, error_status_mask;

sdhc_softare_reset();
sdhc_software_reset();

sdhc_set_power();

@@ -557,7 +557,7 @@ static int sdhc_init(struct sd_card *sdcard)

static int sdhc_read_data(struct sd_data *data)
{
unsigned int normal_status, error_status;
unsigned int normal_status, error_status, psr;
unsigned int timeout;
unsigned int i, block = 0;
unsigned int *tmp;
@@ -573,16 +573,22 @@ static int sdhc_read_data(struct sd_data *data)

sdhc_writew(SDMMC_EISTR, error_status);

sdhc_softare_reset_dat();
sdhc_software_reset_dat();

dbg_info("SDHC: Error detected in status\n");
dbg_info("SDHC: Error detected in status: %x, %x\n",
normal_status, error_status);

return -1;
}

if (normal_status & SDMMC_NISTR_BRDRDY) {
if (!(sdhc_readl(SDMMC_PSR) & SDMMC_PSR_BUFRDEN))
continue;
if (normal_status & SDMMC_NISTR_BRDRDY ||
normal_status & SDMMC_NISTR_BWRRDY) {
psr = sdhc_readl(SDMMC_PSR);
if (data->direction == SD_DATA_DIR_RD &&
!(psr & SDMMC_PSR_BUFRDEN))
continue;
if (data->direction == SD_DATA_DIR_WR &&
!(psr & SDMMC_PSR_BUFWREN))
continue;

tmp = (unsigned int *)data->buff;
for (i = 0; i < data->blocksize; i += 4) {
@@ -605,6 +611,24 @@ static int sdhc_read_data(struct sd_data *data)
}
} while (!(normal_status & SDMMC_NISTR_TRFC));

/* There is an issue when writing data,
* even after the whole data has been written, the SDMMC will not
* trigger TRFC in some cases. Check for WTACT for that.
* In this case we use a sw workaround to reset the
* CMD and DAT lines from SRR and clear the interrupts
* Otherwise we are never getting inhibits bits back on
*/
if (data->direction == SD_DATA_DIR_WR &&
(sdhc_readl(SDMMC_PSR) & SDMMC_PSR_WTACT)) {

normal_status = sdhc_readw(SDMMC_NISTR);
error_status = sdhc_readw(SDMMC_EISTR);
sdhc_software_reset_dat();
sdhc_software_reset_cmd();
sdhc_writew(SDMMC_EISTR, error_status);
sdhc_writew(SDMMC_NISTR, normal_status);

}
return 0;
}

@@ -679,7 +703,12 @@ static int sdhc_send_command(struct sd_command *sd_cmd, struct sd_data *data)
if (!timeout)
dbg_info("SDHC: Timeout waiting for command complete\n");

sdhc_writew(SDMMC_NISTR, normal_status);
/* clear the status, except for read and write ready.
* those will be cleared by the read/write data routine, which
* bases itself on the fact that the hardware is ready to receive data
* or has data ready to be read
*/
sdhc_writew(SDMMC_NISTR, normal_status & ~ (SDMMC_NISTR_BWRRDY | SDMMC_NISTR_BRDRDY));

if ((normal_status & normal_status_mask) == normal_status_mask) {
if (sd_cmd->resp_type == SD_RESP_TYPE_R2) {
@@ -696,7 +725,7 @@ static int sdhc_send_command(struct sd_command *sd_cmd, struct sd_data *data)
} else {
error_status = sdhc_readw(SDMMC_EISTR);

sdhc_softare_reset_cmd();
sdhc_software_reset_cmd();

sdhc_writew(SDMMC_EISTR, error_status);

0 comments on commit 8b9064b

Please sign in to comment.
You can’t perform that action at this time.