summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorMartin LUNDHOLM <martin.xa.lundholm@stericsson.com>2010-08-23 17:31:29 +0200
committerMichael BRANDT <michael.brandt@stericsson.com>2010-08-30 19:39:16 +0200
commit3a5b27b58258ff61dfae3167bc46fed0146ddffd (patch)
treef185f10ccb07c693998340343afc4a680949aea7 /drivers
parent6e7432129841a6177b097c5d2388c6aa9ed257c3 (diff)
U-boot: MMC update.
MMC has been updated with several improvements. Primarily MMC performance has been improved by using assembler code for low level FIFO handling. Also some MMC functionality has been added, e.g. support for DDR and reliable write. Data and command delay times were incremented, otherwise hangups and timeouts were observed. Tested on HREF+ 1.1 V21 and HREF+ 1.1 V32 (Micron PoP). Following WP depends on this change (more reliable SD card write support): ST-Ericsson ID: WP264488 Change-Id: Ic92abffe1640aa9375b8d43a6b8522ca8296a368 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/4056 Reviewed-by: Michael BRANDT <michael.brandt@stericsson.com> Tested-by: Michael BRANDT <michael.brandt@stericsson.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/mmc/mmc.c170
1 files changed, 125 insertions, 45 deletions
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c
index 364dd8005..31af3ad87 100644
--- a/drivers/mmc/mmc.c
+++ b/drivers/mmc/mmc.c
@@ -53,6 +53,21 @@ static int mmc_set_blocklen(struct mmc *mmc, uint len)
return mmc_send_cmd(mmc, &cmd, NULL);
}
+static int mmc_set_block_count(struct mmc *mmc, uint blkcnt)
+{
+ struct mmc_cmd cmd;
+
+ cmd.cmdidx = MMC_CMD_SET_BLOCK_COUNT;
+ cmd.resp_type = MMC_RSP_R1;
+ if (mmc->card_caps & MMC_MODE_REL_WR)
+ cmd.cmdarg = 0x80000000 | blkcnt;
+ else
+ cmd.cmdarg = blkcnt;
+ cmd.flags = 0;
+
+ return mmc_send_cmd(mmc, &cmd, NULL);
+}
+
struct mmc *find_mmc_device(int dev_num)
{
struct mmc *m;
@@ -79,6 +94,7 @@ mmc_bwrite_multi(struct mmc *mmc, ulong start, ulong blkcnt, const void *src)
ulong blkwritecnt;
ulong blkleftcnt = blkcnt;
void *src_p = (void *) src;
+ uint max_block_cnt = 0xffff;
/*
* Each mmc host controller has a size limit in it's register, used
@@ -88,9 +104,14 @@ mmc_bwrite_multi(struct mmc *mmc, ulong start, ulong blkcnt, const void *src)
* instead.
*/
+ if ((mmc->card_caps & MMC_MODE_REL_WR) &&
+ !(mmc->wr_rel_param & EXT_CSD_WR_REL_PARAM_EN_REL_WR))
+ max_block_cnt = mmc->rel_wr_sec_c;
+
while (blkleftcnt > 0) {
- if (blkleftcnt > 0xffff)
- blkwritecnt = 0xffff;
+
+ if (blkleftcnt > max_block_cnt)
+ blkwritecnt = max_block_cnt;
else
blkwritecnt = blkleftcnt;
@@ -99,6 +120,15 @@ mmc_bwrite_multi(struct mmc *mmc, ulong start, ulong blkcnt, const void *src)
else
cmd.cmdarg = start * mmc->write_bl_len;
+ if (mmc->card_caps & MMC_MODE_REL_WR) {
+ err = mmc_set_block_count(mmc, blkwritecnt);
+ if (err) {
+ printf("MMC set block count failed, err=%d\n",
+ err);
+ return 0;
+ }
+ }
+
cmd.cmdidx = MMC_CMD_WRITE_MULTIPLE_BLOCK;
cmd.resp_type = MMC_RSP_R1;
cmd.flags = 0;
@@ -114,15 +144,19 @@ mmc_bwrite_multi(struct mmc *mmc, ulong start, ulong blkcnt, const void *src)
return 0;
}
- cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION;
- cmd.cmdarg = 0;
- cmd.resp_type = MMC_RSP_R1b;
- cmd.flags = 0;
+ if (!(mmc->card_caps & MMC_MODE_REL_WR) ||
+ (max_block_cnt != mmc->rel_wr_sec_c)) {
+ cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION;
+ cmd.cmdarg = 0;
+ cmd.resp_type = MMC_RSP_R1b;
+ cmd.flags = 0;
- err = mmc_send_cmd(mmc, &cmd, NULL);
- if (err) {
- printf("MMC write - stop cmd failed, err=%d\n", err);
- return 0;
+ err = mmc_send_cmd(mmc, &cmd, NULL);
+ if (err) {
+ printf("MMC write - stop cmd failed, err=%d\n",
+ err);
+ return 0;
+ }
}
blkleftcnt -= blkwritecnt;
@@ -174,12 +208,6 @@ mmc_bwrite(int dev_num, unsigned long start, lbaint_t blkcnt, const void *src)
return 0;
}
- err = mmc_set_blocklen(mmc, mmc->write_bl_len);
- if (err) {
- printf("MMC set write bl len failed, err=%d\n", err);
- return 0;
- }
-
if (blkcnt > 1)
return mmc_bwrite_multi(mmc, start, blkcnt, src);
else if (blkcnt == 1)
@@ -291,12 +319,6 @@ mmc_bread(int dev_num, unsigned long start, lbaint_t blkcnt, void *dst)
return 0;
}
- err = mmc_set_blocklen(mmc, mmc->read_bl_len);
- if (err) {
- printf("MMC set read bl len failed, err=%d\n", err);
- return 0;
- }
-
if (blkcnt > 1)
return mmc_bread_multi(mmc, start, blkcnt, dst);
else if (blkcnt == 1)
@@ -455,9 +477,10 @@ static int mmc_change_freq(struct mmc *mmc)
* Instead of probing according to the bus testing procedure,
* the buswitdh that is supported from the MMC device is hardcoded
* to both 8 and/or 4 bit. It is up to the host driver to set
- * other limitations.
+ * other limitations. This also applies to DDR mode.
*/
- mmc->card_caps = MMC_MODE_4BIT | MMC_MODE_8BIT;
+ mmc->card_caps = MMC_MODE_4BIT | MMC_MODE_8BIT | MMC_MODE_DDR |
+ MMC_MODE_REL_WR;
/* Only version 4 supports high-speed */
if (mmc->version < MMC_VERSION_4)
@@ -477,16 +500,18 @@ static int mmc_change_freq(struct mmc *mmc)
* read.
*/
- cardtype = ext_csd[EXT_CSD_CARD_TYPE] & 0xf;
+ mmc->wr_rel_param = ext_csd[EXT_CSD_WR_REL_PARAM];
+ mmc->rel_wr_sec_c = ext_csd[EXT_CSD_REL_WR_SEC_C];
- err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, 1);
+ if (mmc->rel_wr_sec_c == 1)
+ mmc->card_caps &= ~MMC_MODE_REL_WR;
+ err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, 1);
if (err)
return err;
/* Now check to see that it worked */
err = mmc_send_ext_csd(mmc, ext_csd);
-
if (err)
return err;
@@ -498,11 +523,16 @@ static int mmc_change_freq(struct mmc *mmc)
* High Speed mode is set, two types: SDR 52MHz or SDR 26MHz
* DDR mode is not supported yet.
*/
+ cardtype = ext_csd[EXT_CSD_CARD_TYPE];
if (cardtype & MMC_HS_52MHZ)
mmc->card_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS;
else
mmc->card_caps |= MMC_MODE_HS;
+ if (mmc->wr_rel_param & EXT_CSD_WR_REL_PARAM_HS_CTRL_REL)
+ err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_WR_REL_SET, 1);
+
return 0;
}
@@ -556,19 +586,15 @@ static int sd_change_freq(struct mmc *mmc)
timeout = 3;
-retry_scr:
- data.dest = (char *)&scr;
- data.blocksize = 8;
- data.blocks = 1;
- data.flags = MMC_DATA_READ;
-
- err = mmc_send_cmd(mmc, &cmd, &data);
- if (err) {
- if (timeout--)
- goto retry_scr;
-
+ do {
+ data.dest = (char *)&scr;
+ data.blocksize = 8;
+ data.blocks = 1;
+ data.flags = MMC_DATA_READ;
+ err = mmc_send_cmd(mmc, &cmd, &data);
+ } while (err && timeout--);
+ if (!timeout)
return err;
- }
mmc->scr[0] = __be32_to_cpu(scr[0]);
mmc->scr[1] = __be32_to_cpu(scr[1]);
@@ -781,13 +807,11 @@ static int mmc_startup(struct mmc *mmc)
* Check for SD!
*/
if (mmc->high_capacity) {
- csize = (mmc->csd[1] & 0x3f) << 16
- | (mmc->csd[2] & 0xffff0000) >> 16;
+ csize = CSD_HC_SIZE(mmc->csd);
cmult = 8;
} else {
- csize = (mmc->csd[1] & 0x3ff) << 2
- | (mmc->csd[2] & 0xc0000000) >> 30;
- cmult = (mmc->csd[2] & 0x00038000) >> 15;
+ csize = CSD_C_SIZE(mmc->csd);
+ cmult = CSD_C_SIZE_MULT(mmc->csd);
}
/* This is only correct for MMC cards up to 2GB. SD? */
@@ -855,7 +879,41 @@ static int mmc_startup(struct mmc *mmc)
else
mmc_set_clock(mmc, 25000000);
} else {
- if (mmc->card_caps & MMC_MODE_8BIT) {
+ if ((mmc->card_caps & MMC_MODE_DDR_8BIT) == MMC_MODE_DDR_8BIT) {
+ /* Set the card to use 8 bit*/
+ printf("EXT_CSD_BUS_WIDTH_DDR_8\n");
+ err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_BUS_WIDTH,
+ EXT_CSD_BUS_WIDTH_DDR_8);
+ if (err)
+ return err;
+
+ err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_POWER_CLASS,
+ 0xAA);
+ if (err)
+ return err;
+ printf("EXT_CSD_BUS_WIDTH_DDR_8\n");
+ mmc->ddr_en = 1;
+ mmc_set_bus_width(mmc, 8);
+ } else if ((mmc->card_caps & MMC_MODE_DDR_4BIT) ==
+ MMC_MODE_DDR_4BIT) {
+ /* Set the card to use 4 bit*/
+ err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_BUS_WIDTH,
+ EXT_CSD_BUS_WIDTH_DDR_4);
+ if (err)
+ return err;
+
+ err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_POWER_CLASS,
+ 0xAA);
+ if (err)
+ return err;
+ printf("EXT_CSD_BUS_WIDTH_DDR_4\n");
+ mmc->ddr_en = 1;
+ mmc_set_bus_width(mmc, 4);
+ } else if (mmc->card_caps & MMC_MODE_8BIT) {
/* Set the card to use 8 bit*/
err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_BUS_WIDTH,
@@ -884,6 +942,15 @@ static int mmc_startup(struct mmc *mmc)
mmc_set_clock(mmc, 26000000);
} else
mmc_set_clock(mmc, 20000000);
+
+ if (mmc->card_caps & MMC_MODE_REL_WR) {
+ err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_WR_REL_SET,
+ 0x1F);
+
+ if (err)
+ return err;
+ }
}
/* fill in device description */
@@ -987,6 +1054,13 @@ int mmc_init(struct mmc *mmc)
}
err = mmc_startup(mmc);
+
+ if (!err) {
+ err = mmc_set_blocklen(mmc, 512);
+ if (err)
+ printf("MMC set write bl len failed, err=%d\n", err);
+ }
+
debug("mmc_startup returns %d\n", err);
return err;
}
@@ -1001,6 +1075,12 @@ static int __def_mmc_init(bd_t *bis)
}
int cpu_mmc_init(bd_t *bis) __attribute__((weak, alias("__def_mmc_init")));
+/*
+ * It seems attribute 'weak' does not work as intended. With gcc 4.4.1 and
+ * optimization O2 it always links in the weak function. Declare board_mmc_init
+ * as external.
+ */
+extern int board_mmc_init(bd_t *bis);
void print_mmc_devices(char separator)
{