summaryrefslogtreecommitdiff
path: root/drivers/mmc/mmc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc/mmc.c')
-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)
{