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.c849
1 files changed, 540 insertions, 309 deletions
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c
index cf2a9a6..b5d200f 100644
--- a/drivers/mmc/mmc.c
+++ b/drivers/mmc/mmc.c
@@ -1,5 +1,5 @@
/*
- * (C) Copyright 2008-2011 Freescale Semiconductor, Inc.
+ * Copyright (C) 2008-2011 Freescale Semiconductor, Inc.
* Terry Lv
*
* Copyright 2008, Freescale Semiconductor, Inc
@@ -33,9 +33,13 @@
#include <part.h>
#include <malloc.h>
#include <linux/list.h>
-#include <mmc.h>
#include <div64.h>
+/* Set block count limit because of 16 bit register limit on some hardware*/
+#ifndef CONFIG_SYS_MMC_MAX_BLK_COUNT
+#define CONFIG_SYS_MMC_MAX_BLK_COUNT 65535
+#endif
+
static struct list_head mmc_devices;
static int cur_dev_num = -1;
@@ -47,13 +51,105 @@ int __board_mmc_getcd(u8 *cd, struct mmc *mmc)
int board_mmc_getcd(u8 *cd, struct mmc *mmc)__attribute__((weak,
alias("__board_mmc_getcd")));
-static int mmc_send_cmd(struct mmc *mmc,
- struct mmc_cmd *cmd, struct mmc_data *data)
+int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
{
+#ifdef CONFIG_MMC_TRACE
+ int ret;
+ int i;
+ u8 *ptr;
+
+ printf("CMD_SEND:%d\n", cmd->cmdidx);
+ printf("\t\tARG\t\t\t 0x%08X\n", cmd->cmdarg);
+ printf("\t\tFLAG\t\t\t %d\n", cmd->flags);
+ ret = mmc->send_cmd(mmc, cmd, data);
+ switch (cmd->resp_type) {
+ case MMC_RSP_NONE:
+ printf("\t\tMMC_RSP_NONE\n");
+ break;
+ case MMC_RSP_R1:
+ printf("\t\tMMC_RSP_R1,5,6,7 \t 0x%08X\n",
+ cmd->response[0]);
+ break;
+ case MMC_RSP_R1b:
+ printf("\t\tMMC_RSP_R1b\t\t 0x%08X\n",
+ cmd->response[0]);
+ break;
+ case MMC_RSP_R2:
+ printf("\t\tMMC_RSP_R2\t\t 0x%08X\n",
+ cmd->response[0]);
+ printf("\t\t \t\t 0x%08X\n",
+ cmd->response[1]);
+ printf("\t\t \t\t 0x%08X\n",
+ cmd->response[2]);
+ printf("\t\t \t\t 0x%08X\n",
+ cmd->response[3]);
+ printf("\n");
+ printf("\t\t\t\t\tDUMPING DATA\n");
+ for (i = 0; i < 4; i++) {
+ int j;
+ printf("\t\t\t\t\t%03d - ", i*4);
+ ptr = &cmd->response[i];
+ ptr += 3;
+ for (j = 0; j < 4; j++)
+ printf("%02X ", *ptr--);
+ printf("\n");
+ }
+ break;
+ case MMC_RSP_R3:
+ printf("\t\tMMC_RSP_R3,4\t\t 0x%08X\n",
+ cmd->response[0]);
+ break;
+ default:
+ printf("\t\tERROR MMC rsp not supported\n");
+ break;
+ }
+ return ret;
+#else
return mmc->send_cmd(mmc, cmd, data);
+#endif
+}
+
+int mmc_send_status(struct mmc *mmc, int timeout)
+{
+ struct mmc_cmd cmd;
+ int err;
+#ifdef CONFIG_MMC_TRACE
+ int status;
+#endif
+
+ cmd.cmdidx = MMC_CMD_SEND_STATUS;
+ cmd.resp_type = MMC_RSP_R1;
+ cmd.cmdarg = 0;
+ cmd.flags = 0;
+
+ do {
+ err = mmc_send_cmd(mmc, &cmd, NULL);
+ if (err)
+ return err;
+ else if (cmd.response[0] & MMC_STATUS_RDY_FOR_DATA)
+ break;
+
+ udelay(1000);
+
+ if (cmd.response[0] & MMC_STATUS_MASK) {
+ printf("Status Error: 0x%08X\n", cmd.response[0]);
+ return COMM_ERR;
+ }
+ } while (timeout--);
+
+#ifdef CONFIG_MMC_TRACE
+ status = (cmd.response[0] & MMC_STATUS_CURR_STATE) >> 9;
+ printf("CURR STATE:%d\n", status);
+#endif
+ if (!timeout) {
+ printf("Timeout waiting card ready\n");
+ return TIMEOUT;
+ }
+
+ return 0;
}
-static int mmc_set_blocklen(struct mmc *mmc, int len)
+int mmc_set_blocklen(struct mmc *mmc, int len)
{
struct mmc_cmd cmd;
@@ -82,11 +178,94 @@ struct mmc *find_mmc_device(int dev_num)
return NULL;
}
+static ulong mmc_erase_t(struct mmc *mmc, ulong start, lbaint_t blkcnt)
+{
+ struct mmc_cmd cmd;
+ ulong end;
+ int err, start_cmd, end_cmd;
+
+ if (mmc->high_capacity)
+ end = start + blkcnt - 1;
+ else {
+ end = (start + blkcnt - 1) * mmc->write_bl_len;
+ start *= mmc->write_bl_len;
+ }
+
+ if (IS_SD(mmc)) {
+ start_cmd = SD_CMD_ERASE_WR_BLK_START;
+ end_cmd = SD_CMD_ERASE_WR_BLK_END;
+ } else {
+ start_cmd = MMC_CMD_ERASE_GROUP_START;
+ end_cmd = MMC_CMD_ERASE_GROUP_END;
+ }
+
+ cmd.cmdidx = start_cmd;
+ cmd.cmdarg = start;
+ cmd.resp_type = MMC_RSP_R1;
+ cmd.flags = 0;
+
+ err = mmc_send_cmd(mmc, &cmd, NULL);
+ if (err)
+ goto err_out;
+
+ cmd.cmdidx = end_cmd;
+ cmd.cmdarg = end;
+
+ err = mmc_send_cmd(mmc, &cmd, NULL);
+ if (err)
+ goto err_out;
+
+ cmd.cmdidx = MMC_CMD_ERASE;
+ cmd.cmdarg = SECURE_ERASE;
+ cmd.resp_type = MMC_RSP_R1b;
+
+ err = mmc_send_cmd(mmc, &cmd, NULL);
+ if (err)
+ goto err_out;
+
+ return 0;
+
+err_out:
+ puts("mmc erase failed\n");
+ return err;
+}
+
+static unsigned long
+mmc_berase(int dev_num, unsigned long start, lbaint_t blkcnt)
+{
+ int err = 0;
+ struct mmc *mmc = find_mmc_device(dev_num);
+ lbaint_t blk = 0, blk_r = 0;
+
+ if (!mmc)
+ return -1;
+
+ if ((start % mmc->erase_grp_size) || (blkcnt % mmc->erase_grp_size))
+ printf("\n\nCaution! Your devices Erase group is 0x%x\n"
+ "The erase range would be change to 0x%lx~0x%lx\n\n",
+ mmc->erase_grp_size, start & ~(mmc->erase_grp_size - 1),
+ ((start + blkcnt + mmc->erase_grp_size)
+ & ~(mmc->erase_grp_size - 1)) - 1);
+
+ while (blk < blkcnt) {
+ blk_r = ((blkcnt - blk) > mmc->erase_grp_size) ?
+ mmc->erase_grp_size : (blkcnt - blk);
+ err = mmc_erase_t(mmc, start + blk, blk_r);
+ if (err)
+ break;
+
+ blk += blk_r;
+ }
+
+ return blk;
+}
+
static ulong
mmc_write_blocks(struct mmc *mmc, ulong start, lbaint_t blkcnt, const void*src)
{
struct mmc_cmd cmd;
struct mmc_data data;
+ int timeout = 1000;
if ((start + blkcnt) > mmc->block_dev.lba) {
printf("MMC: block number 0x%lx exceeds max(0x%lx)\n",
@@ -117,7 +296,10 @@ mmc_write_blocks(struct mmc *mmc, ulong start, lbaint_t blkcnt, const void*src)
return 0;
}
- if (blkcnt > 1) {
+ /* SPI multiblock writes terminate using a special
+ * token, not a STOP_TRANSMISSION request.
+ */
+ if (!mmc_host_is_spi(mmc) && blkcnt > 1) {
cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION;
cmd.cmdarg = 0;
cmd.resp_type = MMC_RSP_R1b;
@@ -126,6 +308,9 @@ mmc_write_blocks(struct mmc *mmc, ulong start, lbaint_t blkcnt, const void*src)
printf("mmc fail to send stop cmd\n");
return 0;
}
+
+ /* Waiting for the ready status */
+ mmc_send_status(mmc, timeout);
}
return blkcnt;
@@ -143,8 +328,8 @@ mmc_bwrite(int dev_num, ulong start, lbaint_t blkcnt, const void *src)
blklen = mmc->write_bl_len;
- if (mmc->bus_width == EMMC_MODE_4BIT_DDR ||
- mmc->bus_width == EMMC_MODE_8BIT_DDR) {
+ if (mmc->card_caps & EMMC_MODE_4BIT_DDR ||
+ mmc->card_caps & EMMC_MODE_8BIT_DDR) {
err = 0;
blklen = 512;
} else
@@ -156,11 +341,7 @@ mmc_bwrite(int dev_num, ulong start, lbaint_t blkcnt, const void *src)
}
do {
- /*
- * The 65535 constraint comes from some hardware has
- * only 16 bit width block number counter
- */
- cur = (blocks_todo > 65535) ? 65535 : blocks_todo;
+ cur = (blocks_todo > mmc->b_max) ? mmc->b_max : blocks_todo;
if (mmc_write_blocks(mmc, start, cur, src) != cur)
return 0;
blocks_todo -= cur;
@@ -176,6 +357,7 @@ static int mmc_read_blocks(struct mmc *mmc, void *dst,
{
struct mmc_cmd cmd;
struct mmc_data data;
+ int timeout = 1000;
if (blkcnt > 1)
cmd.cmdidx = MMC_CMD_READ_MULTIPLE_BLOCK;
@@ -207,6 +389,9 @@ static int mmc_read_blocks(struct mmc *mmc, void *dst,
printf("mmc fail to send stop cmd\n");
return 0;
}
+
+ /* Waiting for the ready status */
+ mmc_send_status(mmc, timeout);
}
return blkcnt;
@@ -230,8 +415,8 @@ static ulong mmc_bread(int dev_num, ulong start, lbaint_t blkcnt, void *dst)
return 0;
}
- if (mmc->bus_width == EMMC_MODE_4BIT_DDR ||
- mmc->bus_width == EMMC_MODE_8BIT_DDR) {
+ if (mmc->card_caps & EMMC_MODE_4BIT_DDR ||
+ mmc->card_caps & EMMC_MODE_8BIT_DDR) {
blklen = 512;
err = 0;
} else {
@@ -245,11 +430,7 @@ static ulong mmc_bread(int dev_num, ulong start, lbaint_t blkcnt, void *dst)
}
do {
- /*
- * The 65535 constraint comes from some hardware has
- * only 16 bit width block number counter
- */
- cur = (blocks_todo > 65535) ? 65535 : blocks_todo;
+ cur = (blocks_todo > mmc->b_max) ? mmc->b_max : blocks_todo;
if (mmc_read_blocks(mmc, dst, start, cur) != cur)
return 0;
blocks_todo -= cur;
@@ -282,7 +463,7 @@ static int mmc_go_idle(struct mmc *mmc)
return 0;
}
-static int
+int
sd_send_op_cond(struct mmc *mmc)
{
int timeout = 1000;
@@ -310,7 +491,8 @@ sd_send_op_cond(struct mmc *mmc)
* how to manage low voltages SD card is not yet
* specified.
*/
- cmd.cmdarg = mmc->voltages & 0xff8000;
+ cmd.cmdarg = mmc_host_is_spi(mmc) ? 0 :
+ (mmc->voltages & 0xff8000);
if (mmc->version == SD_VERSION_2)
cmd.cmdarg |= OCR_HCS;
@@ -329,6 +511,18 @@ sd_send_op_cond(struct mmc *mmc)
if (mmc->version != SD_VERSION_2)
mmc->version = SD_VERSION_1_0;
+ if (mmc_host_is_spi(mmc)) { /* read OCR for spi */
+ cmd.cmdidx = MMC_CMD_SPI_READ_OCR;
+ cmd.resp_type = MMC_RSP_R3;
+ cmd.cmdarg = 0;
+ cmd.flags = 0;
+
+ err = mmc_send_cmd(mmc, &cmd, NULL);
+
+ if (err)
+ return err;
+ }
+
mmc->ocr = cmd.response[0];
mmc->high_capacity = ((mmc->ocr & OCR_HCS) == OCR_HCS);
@@ -339,17 +533,37 @@ sd_send_op_cond(struct mmc *mmc)
static int mmc_send_op_cond(struct mmc *mmc)
{
- int timeout = 1000;
+ int timeout = 10000;
struct mmc_cmd cmd;
int err;
/* Some cards seem to need this */
mmc_go_idle(mmc);
+ /* Asking to the card its capabilities */
+ cmd.cmdidx = MMC_CMD_SEND_OP_COND;
+ cmd.resp_type = MMC_RSP_R3;
+ cmd.cmdarg = 0;
+ cmd.flags = 0;
+
+ err = mmc_send_cmd(mmc, &cmd, NULL);
+
+ if (err)
+ return err;
+
+ udelay(1000);
+
do {
cmd.cmdidx = MMC_CMD_SEND_OP_COND;
cmd.resp_type = MMC_RSP_R3;
- cmd.cmdarg = OCR_HCS | mmc->voltages;
+ cmd.cmdarg = (mmc_host_is_spi(mmc) ? 0 :
+ (mmc->voltages &
+ (cmd.response[0] & OCR_VOLTAGE_MASK)) |
+ (cmd.response[0] & OCR_ACCESS_MODE));
+
+ if (mmc->host_caps & MMC_MODE_HC)
+ cmd.cmdarg |= OCR_HCS;
+
cmd.flags = 0;
err = mmc_send_cmd(mmc, &cmd, NULL);
@@ -363,6 +577,18 @@ static int mmc_send_op_cond(struct mmc *mmc)
if (timeout <= 0)
return UNUSABLE_ERR;
+ if (mmc_host_is_spi(mmc)) { /* read OCR for spi */
+ cmd.cmdidx = MMC_CMD_SPI_READ_OCR;
+ cmd.resp_type = MMC_RSP_R3;
+ cmd.cmdarg = 0;
+ cmd.flags = 0;
+
+ err = mmc_send_cmd(mmc, &cmd, NULL);
+
+ if (err)
+ return err;
+ }
+
mmc->version = MMC_VERSION_UNKNOWN;
mmc->ocr = cmd.response[0];
@@ -373,7 +599,7 @@ static int mmc_send_op_cond(struct mmc *mmc)
}
-static int mmc_send_ext_csd(struct mmc *mmc, char *ext_csd)
+int mmc_send_ext_csd(struct mmc *mmc, char *ext_csd)
{
struct mmc_cmd cmd;
struct mmc_data data;
@@ -396,68 +622,67 @@ static int mmc_send_ext_csd(struct mmc *mmc, char *ext_csd)
}
-static int mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value)
+int mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value)
{
struct mmc_cmd cmd;
+ int timeout = 1000;
+ int ret;
cmd.cmdidx = MMC_CMD_SWITCH;
cmd.resp_type = MMC_RSP_R1b;
cmd.cmdarg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
- (index << 16) |
- (value << 8);
+ (index << 16) |
+ (value << 8);
cmd.flags = 0;
- return mmc_send_cmd(mmc, &cmd, NULL);
+ ret = mmc_send_cmd(mmc, &cmd, NULL);
+
+ /* Waiting for the ready status */
+ mmc_send_status(mmc, timeout);
+
+ return ret;
+
}
-static int mmc_change_freq(struct mmc *mmc)
+int mmc_change_freq(struct mmc *mmc)
{
- char *ext_csd;
+ char ext_csd[512];
char cardtype;
int err;
mmc->card_caps = 0;
+ if (mmc_host_is_spi(mmc))
+ return 0;
+
/* Only version 4 supports high-speed */
if (mmc->version < MMC_VERSION_4)
return 0;
mmc->card_caps |= ((mmc->host_caps & MMC_MODE_8BIT)
- ? MMC_MODE_8BIT : MMC_MODE_4BIT);
-
- ext_csd = (char *)malloc(512);
-
- if (!ext_csd) {
- puts("Could not allocate buffer for MMC ext csd!\n");
- return -1;
- }
+ ? MMC_MODE_8BIT : MMC_MODE_4BIT);
err = mmc_send_ext_csd(mmc, ext_csd);
if (err)
- goto err_rtn;
-
- /* Cards with density > 2GiB are sector addressed */
- if ((ext_csd[212] || ext_csd[213] || ext_csd[214] || ext_csd[215]) &&
- ((mmc->capacity > (2u * 1024 * 1024 * 1024) / 512)))
- mmc->high_capacity = 1;
+ return err;
- cardtype = ext_csd[EXT_CSD_CARD_TYPE] & 0xf;
+ cardtype = ext_csd[196] & 0xf;
err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, 1);
if (err)
- goto err_rtn;
+ return err;
/* Now check to see that it worked */
err = mmc_send_ext_csd(mmc, ext_csd);
if (err)
- goto err_rtn;
+ return err;
/* No high-speed support */
- if (!ext_csd[EXT_CSD_HS_TIMING])
- goto no_err_rtn;
+ if (!ext_csd[185])
+ return 0;
/* High Speed is set, there are two types: 52MHz and 26MHz */
if (cardtype & MMC_HS_52MHZ)
@@ -472,16 +697,146 @@ static int mmc_change_freq(struct mmc *mmc)
mmc->card_caps |= EMMC_MODE_4BIT_DDR;
}
-no_err_rtn:
- free(ext_csd);
return 0;
+}
+
+int mmc_switch_part(int dev_num, unsigned int part_num)
+{
+ struct mmc *mmc = find_mmc_device(dev_num);
+
+ if (!mmc)
+ return -1;
+
+ return mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_PART_CONF,
+ (mmc->part_config & ~PART_ACCESS_MASK)
+ | (part_num & PART_ACCESS_MASK));
+}
+
+int sd_switch_part(int dev_num, unsigned int part_num)
+{
+ struct mmc *mmc = find_mmc_device(dev_num);
+ struct mmc_cmd cmd;
+ int err;
+
+ if (!mmc)
+ return -1;
+
+ cmd.cmdidx = SD_CMD_SELECT_PARTITION;
+ cmd.resp_type = MMC_RSP_R1;
+ cmd.cmdarg = (part_num & PART_ACCESS_MASK) << 24;
+ cmd.flags = 0;
+
+ err = mmc_send_cmd(mmc, &cmd, NULL);
+
+ if (err)
+ return -1;
+
+ return 0;
+}
+
+int mmc_switch_boot_part(int dev_num, unsigned int part_num)
+{
+ struct mmc *mmc = find_mmc_device(dev_num);
+ char ext_csd[512] = { 0 };
+ int err;
+ char boot_config;
+ char boot_bus_width, card_boot_bus_width;
+
+ /* Partition must be -
+ 0 - user area
+ 1 - boot partition 1
+ 2 - boot partition 2
+ */
+ if (part_num > 2) {
+ printf("Wrong partition id - "
+ "0 (user area), 1 (boot1), 2 (boot2)\n");
+ return 1;
+ }
+
+ /* Before calling this func, "mmc" struct must have been initialized */
+ if (mmc->version < MMC_VERSION_4) {
+ printf("Error: invalid mmc version! "
+ "mmc version is below version 4!");
+ return -1;
+ }
+
+ err = mmc_send_ext_csd(mmc, ext_csd);
+ if (err) {
+ printf("Warning: fail to get ext csd for MMC!\n");
+ goto err_rtn;
+ }
+
+ boot_config = ext_csd[EXT_CSD_PART_CONF] &
+ EXT_CSD_BOOT_PARTITION_ACCESS_MASK;
+
+ /* Enable access plus boot from that partition and boot_ack bit */
+ boot_config |= (char)(part_num << 3 | 1 << 6);
+
+ err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_PART_CONF, boot_config);
+ if (err) {
+ printf("Error: fail to send SWITCH command to card "
+ "to swich partition for access!\n");
+ goto err_rtn;
+ }
+
+ /* Now check whether it works */
+ err = mmc_send_ext_csd(mmc, ext_csd);
+ if (err) {
+ printf("Warning: fail to get ext csd for MMC!\n");
+ goto err_rtn;
+ }
+
+ if (boot_config != ext_csd[EXT_CSD_PART_CONF]) {
+ printf("Warning: Boot partition switch failed!\n");
+ goto err_rtn;
+ }
+
+ /* Program boot_bus_width field for eMMC 4.4 boot mode */
+ if (ext_csd[EXT_CSD_CARD_TYPE] & 0xC) {
+ /* Configure according to this card's capabilities */
+ if (mmc->card_caps & EMMC_MODE_8BIT_DDR)
+ boot_bus_width = EXT_CSD_BOOT_BUS_WIDTH_DDR |
+ EXT_CSD_BOOT_BUS_WIDTH_8BIT;
+ else if (mmc->card_caps & EMMC_MODE_4BIT_DDR)
+ boot_bus_width = EXT_CSD_BOOT_BUS_WIDTH_DDR |
+ EXT_CSD_BOOT_BUS_WIDTH_4BIT;
+ else if (mmc->card_caps & MMC_MODE_8BIT)
+ boot_bus_width = EXT_CSD_BOOT_BUS_WIDTH_8BIT;
+ else if (mmc->card_caps & MMC_MODE_4BIT)
+ boot_bus_width = EXT_CSD_BOOT_BUS_WIDTH_4BIT;
+ else
+ boot_bus_width = 0;
+
+ err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_BOOT_BUS_WIDTH, boot_bus_width);
+ /* Ensure that it programmed properly */
+ err = mmc_send_ext_csd(mmc, ext_csd);
+ if (err) {
+ printf("Warning: fail to get ext csd for MMC!\n");
+ goto err_rtn;
+ }
+
+ card_boot_bus_width = ext_csd[EXT_CSD_BOOT_BUS_WIDTH];
+ if (card_boot_bus_width != boot_bus_width) {
+ printf("Warning: current boot_bus_width, 0x%x, is "
+ "not same as requested boot_bus_width 0x%x!\n",
+ card_boot_bus_width, boot_bus_width);
+ goto err_rtn;
+ }
+ }
+
+ return 0;
err_rtn:
- free(ext_csd);
- return err;
+ return -1;
}
-static int sd_switch(struct mmc *mmc, int mode, int group, u8 value, u8 *resp)
+int sd_switch_boot_part(int dev_num, unsigned int part_num)
+{
+ return 0;
+}
+int sd_switch(struct mmc *mmc, int mode, int group, u8 value, u8 *resp)
{
struct mmc_cmd cmd;
struct mmc_data data;
@@ -503,7 +858,7 @@ static int sd_switch(struct mmc *mmc, int mode, int group, u8 value, u8 *resp)
}
-static int sd_change_freq(struct mmc *mmc)
+int sd_change_freq(struct mmc *mmc)
{
int err;
struct mmc_cmd cmd;
@@ -514,6 +869,9 @@ static int sd_change_freq(struct mmc *mmc)
mmc->card_caps = 0;
+ if (mmc_host_is_spi(mmc))
+ return 0;
+
/* Read the SCR to find out if this card supports higher speeds */
cmd.cmdidx = MMC_CMD_APP_CMD;
cmd.resp_type = MMC_RSP_R1;
@@ -565,6 +923,9 @@ retry_scr:
break;
}
+ if (mmc->scr[0] & SD_DATA_4BIT)
+ mmc->card_caps |= MMC_MODE_4BIT;
+
/* Version 1.0 doesn't support switching */
if (mmc->version == SD_VERSION_1_0)
return 0;
@@ -582,9 +943,6 @@ retry_scr:
break;
}
- if (mmc->scr[0] & SD_DATA_4BIT)
- mmc->card_caps |= MMC_MODE_4BIT;
-
/* If high-speed isn't supported, we return */
if (!(__be32_to_cpu(switch_status[3]) & SD_HIGHSPEED_SUPPORTED))
return 0;
@@ -602,7 +960,7 @@ retry_scr:
/* frequency bases */
/* divided by 10 to be nice to platforms without floating point */
-static int fbase[] = {
+static const int fbase[] = {
10000,
100000,
1000000,
@@ -612,7 +970,7 @@ static int fbase[] = {
/* Multiplier values for TRAN_SPEED. Multiplied by 10 to be nice
* to platforms without floating point.
*/
-static int multipliers[] = {
+static const int multipliers[] = {
0, /* reserved */
10,
12,
@@ -631,7 +989,7 @@ static int multipliers[] = {
80,
};
-static void mmc_set_ios(struct mmc *mmc)
+void mmc_set_ios(struct mmc *mmc)
{
mmc->set_ios(mmc);
}
@@ -649,214 +1007,38 @@ void mmc_set_clock(struct mmc *mmc, uint clock)
mmc_set_ios(mmc);
}
-static void mmc_set_bus_width(struct mmc *mmc, uint width)
+void mmc_set_bus_width(struct mmc *mmc, uint width)
{
mmc->bus_width = width;
mmc_set_ios(mmc);
}
-#ifdef CONFIG_BOOT_PARTITION_ACCESS
-/* Return 0/1/2 for partition id before switch; Return -1 if fail to switch */
-int mmc_switch_partition(struct mmc *mmc, uint part, uint enable_boot)
+int mmc_startup(struct mmc *mmc)
{
- char *ext_csd;
int err;
- uint old_part, new_part;
- char boot_config;
- char boot_bus_width, card_boot_bus_width;
-
- /* partition must be -
- 0 - user area
- 1 - boot partition 1
- 2 - boot partition 2
- */
- if (part > 2) {
- printf("\nWrong partition id - "
- "0 (user area), 1 (boot1), 2 (boot2)\n");
- return 1;
- }
-
- /* Before calling this func, "mmc" struct must have been initialized */
- if (mmc->version < MMC_VERSION_4) {
- puts("\nError: invalid mmc version! "
- "mmc version is below version 4!");
- return -1;
- }
-
- if (mmc->boot_size_mult <= 0) {
- /* it's a normal SD/MMC but user request to boot partition */
- printf("\nError: This is a normal SD/MMC card but you"
- "request to access boot partition\n");
- return -1;
- }
-
- /*
- * Part must be 0 (user area), 1 (boot partition1)
- * or 2 (boot partition2)
- */
- if (part > 2) {
- puts("\nError: partition id must be 0(user area), "
- "1(boot partition1) or 2(boot partition2)\n");
- return -1;
- }
-
- ext_csd = (char *)malloc(512);
- if (!ext_csd) {
- puts("\nError: Could not allocate buffer for MMC ext csd!\n");
- return -1;
- }
-
- err = mmc_send_ext_csd(mmc, ext_csd);
- if (err) {
- puts("\nWarning: fail to get ext csd for MMC!\n");
- goto err_rtn;
- }
-
- old_part = ext_csd[EXT_CSD_BOOT_CONFIG] &
- EXT_CSD_BOOT_PARTITION_ACCESS_MASK;
-
- /* Send SWITCH command to change partition for access */
- boot_config = (ext_csd[EXT_CSD_BOOT_CONFIG] &
- ~EXT_CSD_BOOT_PARTITION_ACCESS_MASK) |
- (char)part;
-
- /* enable access plus boot from that partition and boot_ack bit */
- if (enable_boot != 0)
- boot_config = (char)(part) | (char)(part << 3) | (char)(1 << 6);
-
- err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
- EXT_CSD_BOOT_CONFIG, boot_config);
- if (err) {
- puts("\nError: fail to send SWITCH command to card "
- "to swich partition for access!\n");
- goto err_rtn;
- }
-
- /* Now check whether it works */
- err = mmc_send_ext_csd(mmc, ext_csd);
- if (err) {
- puts("\nWarning: fail to get ext csd for MMC!\n");
- goto err_rtn;
- }
-
- new_part = ext_csd[EXT_CSD_BOOT_CONFIG] &
- EXT_CSD_BOOT_PARTITION_ACCESS_MASK;
- if ((char)part != new_part) {
- printf("\nWarning: after SWITCH, current part id %d is "
- "not same as requested partition %d!\n",
- new_part, part);
- goto err_rtn;
- }
-
- /* Program boot_bus_width field for eMMC 4.4 boot mode */
- if ((ext_csd[EXT_CSD_CARD_TYPE] & 0xC) && enable_boot != 0) {
-
- /* Configure according to this host's capabilities */
- if (mmc->host_caps & EMMC_MODE_8BIT_DDR)
- boot_bus_width = EXT_CSD_BOOT_BUS_WIDTH_DDR |
- EXT_CSD_BOOT_BUS_WIDTH_8BIT;
- else if (mmc->host_caps & EMMC_MODE_4BIT_DDR)
- boot_bus_width = EXT_CSD_BOOT_BUS_WIDTH_DDR |
- EXT_CSD_BOOT_BUS_WIDTH_4BIT;
- else if (mmc->host_caps & MMC_MODE_8BIT)
- boot_bus_width = EXT_CSD_BOOT_BUS_WIDTH_8BIT;
- else if (mmc->host_caps & MMC_MODE_4BIT)
- boot_bus_width = EXT_CSD_BOOT_BUS_WIDTH_4BIT;
- else
- boot_bus_width = 0;
-
- err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
- EXT_CSD_BOOT_BUS_WIDTH, boot_bus_width);
-
- /* Ensure that it programmed properly */
- err = mmc_send_ext_csd(mmc, ext_csd);
- if (err) {
- puts("\nWarning: fail to get ext csd for MMC!\n");
- goto err_rtn;
- }
-
- card_boot_bus_width = ext_csd[EXT_CSD_BOOT_BUS_WIDTH];
- if (card_boot_bus_width != boot_bus_width) {
- printf("\nWarning: current boot_bus_width, 0x%x, is "
- "not same as requested boot_bus_width 0x%x!\n",
- card_boot_bus_width, boot_bus_width);
- goto err_rtn;
- }
- }
-
- /* Seems everything is ok, return the partition id before switch */
- free(ext_csd);
- return old_part;
-
-err_rtn:
- free(ext_csd);
- return -1;
-}
-
-int sd_switch_partition(struct mmc *mmc, uint part)
-{
+ uint mult, freq;
+ u64 cmult, csize, capacity;
struct mmc_cmd cmd;
- int err;
-
- if (part > 1) {
- printf("\nWrong partition id - 0 (user area), 1 (boot1)\n");
- return 1;
- }
-
- cmd.cmdidx = SD_CMD_SELECT_PARTITION;
- cmd.resp_type = MMC_RSP_R1;
- cmd.cmdarg = part << 24;
- cmd.flags = 0;
-
- err = mmc_send_cmd(mmc, &cmd, NULL);
-
- if (err)
- return -1;
-
- return 0;
-}
-
-static int mmc_get_cur_boot_partition(struct mmc *mmc)
-{
- char *ext_csd;
- int err;
-
- ext_csd = (char *)malloc(512);
-
- if (!ext_csd) {
- puts("\nError! Could not allocate buffer for MMC ext csd!\n");
- return -1;
- }
+ char ext_csd[512];
+ int timeout = 1000;
- err = mmc_send_ext_csd(mmc, ext_csd);
+#ifdef CONFIG_MMC_SPI_CRC_ON
+ if (mmc_host_is_spi(mmc)) { /* enable CRC check for spi */
+ cmd.cmdidx = MMC_CMD_SPI_CRC_ON_OFF;
+ cmd.resp_type = MMC_RSP_R1;
+ cmd.cmdarg = 1;
+ cmd.flags = 0;
+ err = mmc_send_cmd(mmc, &cmd, NULL);
- if (err) {
- mmc->boot_config = 0;
- mmc->boot_size_mult = 0;
- /* continue since it's not a fatal error */
- } else {
- mmc->boot_config = ext_csd[EXT_CSD_BOOT_CONFIG];
- mmc->boot_size_mult = ext_csd[EXT_CSD_BOOT_SIZE_MULT];
+ if (err)
+ return err;
}
-
- free(ext_csd);
-
- return err;
-}
-
#endif
-static int mmc_startup(struct mmc *mmc)
-{
- int err;
- uint mult, freq;
- u64 cmult, csize;
- struct mmc_cmd cmd;
- char ext_csd[512];
-
/* Put the Card in Identify Mode */
- cmd.cmdidx = MMC_CMD_ALL_SEND_CID;
+ cmd.cmdidx = mmc_host_is_spi(mmc) ? MMC_CMD_SEND_CID :
+ MMC_CMD_ALL_SEND_CID; /* cmd not supported in spi */
cmd.resp_type = MMC_RSP_R2;
cmd.cmdarg = 0;
cmd.flags = 0;
@@ -873,18 +1055,20 @@ static int mmc_startup(struct mmc *mmc)
* For SD cards, get the Relatvie Address.
* This also puts the cards into Standby State
*/
- cmd.cmdidx = SD_CMD_SEND_RELATIVE_ADDR;
- cmd.cmdarg = mmc->rca << 16;
- cmd.resp_type = MMC_RSP_R6;
- cmd.flags = 0;
+ if (!mmc_host_is_spi(mmc)) { /* cmd not supported in spi */
+ cmd.cmdidx = SD_CMD_SEND_RELATIVE_ADDR;
+ cmd.cmdarg = mmc->rca << 16;
+ cmd.resp_type = MMC_RSP_R6;
+ cmd.flags = 0;
- err = mmc_send_cmd(mmc, &cmd, NULL);
+ err = mmc_send_cmd(mmc, &cmd, NULL);
- if (err)
- return err;
+ if (err)
+ return err;
- if (IS_SD(mmc))
- mmc->rca = (cmd.response[0] >> 16) & 0xffff;
+ if (IS_SD(mmc))
+ mmc->rca = (cmd.response[0] >> 16) & 0xffff;
+ }
/* Get the Card-Specific Data */
cmd.cmdidx = MMC_CMD_SEND_CSD;
@@ -894,6 +1078,9 @@ static int mmc_startup(struct mmc *mmc)
err = mmc_send_cmd(mmc, &cmd, NULL);
+ /* Waiting for the ready status */
+ mmc_send_status(mmc, timeout);
+
if (err)
return err;
@@ -960,22 +1147,58 @@ static int mmc_startup(struct mmc *mmc)
mmc->write_bl_len = 512;
/* Select the card, and put it into Transfer Mode */
- cmd.cmdidx = MMC_CMD_SELECT_CARD;
- cmd.resp_type = MMC_RSP_R1b;
- cmd.cmdarg = mmc->rca << 16;
- cmd.flags = 0;
- err = mmc_send_cmd(mmc, &cmd, NULL);
+ if (!mmc_host_is_spi(mmc)) { /* cmd not supported in spi */
+ cmd.cmdidx = MMC_CMD_SELECT_CARD;
+ cmd.resp_type = MMC_RSP_R1b;
+ cmd.cmdarg = mmc->rca << 16;
+ cmd.flags = 0;
+ err = mmc_send_cmd(mmc, &cmd, NULL);
- if (err)
- return err;
+ if (err)
+ return err;
+ }
+ /*
+ * For SD, its erase group is always one sector
+ */
+ mmc->erase_grp_size = 1;
+ mmc->boot_part_num = 0;
+ mmc->part_config = MMCPART_NOAVAILABLE;
if (!IS_SD(mmc) && (mmc->version >= MMC_VERSION_4)) {
/* check ext_csd version and capacity */
err = mmc_send_ext_csd(mmc, ext_csd);
if (!err & (ext_csd[192] >= 2)) {
- mmc->capacity = ext_csd[212] << 0 | ext_csd[213] << 8 |
- ext_csd[214] << 16 | ext_csd[215] << 24;
- mmc->capacity *= 512;
+ /*
+ * According to the JEDEC Standard, the value of
+ * ext_csd's capacity is valid if the value is more
+ * than 2GB
+ */
+ capacity = ext_csd[212] << 0 | ext_csd[213] << 8 |
+ ext_csd[214] << 16 | ext_csd[215] << 24;
+ capacity *= 512;
+ if ((capacity >> 20) > 2 * 1024)
+ mmc->capacity = capacity;
+ }
+
+ /*
+ * Check whether GROUP_DEF is set, if yes, read out
+ * group size from ext_csd directly, or calculate
+ * the group size from the csd value.
+ */
+ if (ext_csd[175])
+ mmc->erase_grp_size = ext_csd[224] * 512 * 1024;
+ else {
+ int erase_gsz, erase_gmul;
+ erase_gsz = (mmc->csd[2] & 0x00007c00) >> 10;
+ erase_gmul = (mmc->csd[2] & 0x000003e0) >> 5;
+ mmc->erase_grp_size = (erase_gsz + 1)
+ * (erase_gmul + 1);
+ }
+
+ /* store the partition info of emmc */
+ if (ext_csd[160] & PART_SUPPORT) {
+ mmc->part_config = ext_csd[179];
+ mmc->boot_part_num = (ext_csd[179] >> 3) & 0x7;
}
}
@@ -1017,21 +1240,36 @@ static int mmc_startup(struct mmc *mmc)
else
mmc_set_clock(mmc, 25000000);
} else {
+
if (mmc->card_caps & MMC_MODE_4BIT) {
- /* Set the card to use 4 bit*/
- err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
+ if (mmc->card_caps & EMMC_MODE_4BIT_DDR) {
+ /* Set the card to use 4 bit DDR mode */
+ err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_BUS_WIDTH,
+ EXT_CSD_BUS_WIDTH_4_DDR);
+ } else {
+ /* Set the card to use 4 bit*/
+ err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_BUS_WIDTH,
EXT_CSD_BUS_WIDTH_4);
+ }
if (err)
return err;
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,
+ if (mmc->card_caps & EMMC_MODE_8BIT_DDR) {
+ /* Set the card to use 8 bit DDR mode */
+ err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_BUS_WIDTH,
+ EXT_CSD_BUS_WIDTH_8_DDR);
+ } else {
+ /* Set the card to use 8 bit*/
+ err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_BUS_WIDTH,
EXT_CSD_BUS_WIDTH_8);
+ }
if (err)
return err;
@@ -1039,29 +1277,6 @@ static int mmc_startup(struct mmc *mmc)
mmc_set_bus_width(mmc, 8);
}
- if (mmc->card_caps & EMMC_MODE_8BIT_DDR) {
- /* Set the card to use 8 bit DDR mode */
- err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
- EXT_CSD_BUS_WIDTH,
- EXT_CSD_BUS_WIDTH_8_DDR);
- if (err)
- return err;
-
-
- /* Setup the host controller for DDR mode */
- mmc_set_bus_width(mmc, EMMC_MODE_8BIT_DDR);
- } else if (mmc->card_caps & EMMC_MODE_4BIT_DDR) {
- /* Set the card to use 4 bit DDR mode */
- err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
- EXT_CSD_BUS_WIDTH,
- EXT_CSD_BUS_WIDTH_4_DDR);
- if (err)
- return err;
-
- /* Setup the host controller for DDR mode */
- mmc_set_bus_width(mmc, EMMC_MODE_4BIT_DDR);
- }
-
if (mmc->card_caps & MMC_MODE_HS) {
if (mmc->card_caps & MMC_MODE_HS_52MHz)
mmc_set_clock(mmc, 52000000);
@@ -1069,10 +1284,6 @@ static int mmc_startup(struct mmc *mmc)
mmc_set_clock(mmc, 26000000);
} else
mmc_set_clock(mmc, 20000000);
-
-#ifdef CONFIG_BOOT_PARTITION_ACCESS
- mmc_get_cur_boot_partition(mmc);
-#endif
}
/* fill in device description */
@@ -1092,7 +1303,7 @@ static int mmc_startup(struct mmc *mmc)
return 0;
}
-static int mmc_send_if_cond(struct mmc *mmc)
+int mmc_send_if_cond(struct mmc *mmc)
{
struct mmc_cmd cmd;
int err;
@@ -1124,6 +1335,9 @@ int mmc_register(struct mmc *mmc)
mmc->block_dev.removable = 1;
mmc->block_dev.block_read = mmc_bread;
mmc->block_dev.block_write = mmc_bwrite;
+ mmc->block_dev.block_erase = mmc_berase;
+ if (!mmc->b_max)
+ mmc->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
INIT_LIST_HEAD (&mmc->link);
@@ -1132,17 +1346,22 @@ int mmc_register(struct mmc *mmc)
return 0;
}
+#ifdef CONFIG_PARTITIONS
block_dev_desc_t *mmc_get_dev(int dev)
{
struct mmc *mmc = find_mmc_device(dev);
return mmc ? &mmc->block_dev : NULL;
}
+#endif
int mmc_init(struct mmc *mmc)
{
int err;
+ if (mmc->has_init)
+ return 0;
+
err = mmc->init(mmc);
if (err)
@@ -1157,6 +1376,9 @@ int mmc_init(struct mmc *mmc)
if (err)
return err;
+ /* The internal partition reset to user partition(0) at every CMD0*/
+ mmc->part_num = 0;
+
/* Test for SD version 2 */
err = mmc_send_if_cond(mmc);
@@ -1173,7 +1395,12 @@ int mmc_init(struct mmc *mmc)
}
}
- return mmc_startup(mmc);
+ err = mmc_startup(mmc);
+ if (err)
+ mmc->has_init = 0;
+ else
+ mmc->has_init = 1;
+ return err;
}
/*
@@ -1199,12 +1426,17 @@ void print_mmc_devices(char separator)
printf("%s: %d", m->name, m->block_dev.dev);
if (entry->next != &mmc_devices)
- printf("%c ", separator);
+ printf("%c", separator);
}
printf("\n");
}
+int get_mmc_num(void)
+{
+ return cur_dev_num;
+}
+
int mmc_initialize(bd_t *bis)
{
INIT_LIST_HEAD (&mmc_devices);
@@ -1217,4 +1449,3 @@ int mmc_initialize(bd_t *bis)
return 0;
}
-