diff options
author | Terry Lv <r65388@freescale.com> | 2011-08-12 12:54:58 +0800 |
---|---|---|
committer | Terry Lv <r65388@freescale.com> | 2011-09-01 11:45:02 +0800 |
commit | 72ac6998a7700c2c64a4a7f4d6d44bbc980558f6 (patch) | |
tree | 1c07090998bd507e3cde3c481e0fa859ec25a3d5 /drivers/mmc/mmc.c | |
parent | d187eb3675f1408d206e9013bcde38793f5d1ca8 (diff) | |
download | u-boot-imx-72ac6998a7700c2c64a4a7f4d6d44bbc980558f6.zip u-boot-imx-72ac6998a7700c2c64a4a7f4d6d44bbc980558f6.tar.gz u-boot-imx-72ac6998a7700c2c64a4a7f4d6d44bbc980558f6.tar.bz2 |
ENGR00154666-3: Align u-boot mmc command with community
This patch will enhance mmc command.
1. Add erase command.
2. Abandon dev_no in mmc command. User need to switch slot with "mmc
dev" command.
3. Add mmc part switch command. Enhance partition switch support.
4. Add mmc bootpart. Boot partition support is more flexible.
Signed-off-by: Terry Lv <r65388@freescale.com>
Diffstat (limited to 'drivers/mmc/mmc.c')
-rw-r--r-- | drivers/mmc/mmc.c | 849 |
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; } - |