diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/mmc/imx_esdhc.c | 29 | ||||
-rw-r--r-- | drivers/mmc/mmc.c | 849 |
2 files changed, 552 insertions, 326 deletions
diff --git a/drivers/mmc/imx_esdhc.c b/drivers/mmc/imx_esdhc.c index 093a1b2..052b747 100644 --- a/drivers/mmc/imx_esdhc.c +++ b/drivers/mmc/imx_esdhc.c @@ -204,8 +204,8 @@ esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) /* Figure out the transfer arguments */ xfertyp = esdhc_xfertyp(cmd, data); - 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) xfertyp |= XFERTYP_DDR_EN; /* Send the command */ @@ -327,8 +327,8 @@ void set_sysctl(struct mmc *mmc, uint clock) div -= 1; /* for USDHC, pre_div requires another shift in DDR mode */ - if (cfg->is_usdhc && (mmc->bus_width == EMMC_MODE_4BIT_DDR || - mmc->bus_width == EMMC_MODE_8BIT_DDR)) + if (cfg->is_usdhc && (mmc->card_caps & EMMC_MODE_4BIT_DDR || + mmc->card_caps & EMMC_MODE_8BIT_DDR)) pre_div >>= 1; clk = (pre_div << 8) | (div << 4); @@ -451,15 +451,11 @@ static void esdhc_set_ios(struct mmc *mmc) } else if (mmc->bus_width == 8) { tmp = readl(®s->proctl) | PROCTL_DTW_8; writel(tmp, ®s->proctl); - } else if (mmc->bus_width == EMMC_MODE_4BIT_DDR) { - tmp = readl(®s->proctl) | PROCTL_DTW_4; - writel(tmp, ®s->proctl); - esdhc_dll_setup(mmc); - } else if (mmc->bus_width == EMMC_MODE_8BIT_DDR) { - tmp = readl(®s->proctl) | PROCTL_DTW_8; - writel(tmp, ®s->proctl); - esdhc_dll_setup(mmc); } + + if (mmc->card_caps & EMMC_MODE_4BIT_DDR || + mmc->card_caps & EMMC_MODE_8BIT_DDR) + esdhc_dll_setup(mmc); } static int esdhc_init(struct mmc *mmc) @@ -546,11 +542,11 @@ int fsl_esdhc_initialize(bd_t *bis, struct fsl_esdhc_cfg *cfg) mmc->host_caps = MMC_MODE_4BIT; if (caps & ESDHC_HOSTCAPBLT_HSS) - mmc->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS; + mmc->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS | MMC_MODE_HC; -/* Do not advertise DDR capability for uSDHC on MX50 since - * it is to be used in SDR mode only. Use eSDHC for DDR mode. - */ + /* Do not advertise DDR capability for uSDHC on MX50 since + * it is to be used in SDR mode only. Use eSDHC for DDR mode. + */ #ifndef CONFIG_MX50_ENABLE_USDHC_SDR if (cfg->is_usdhc) mmc->host_caps |= EMMC_MODE_4BIT_DDR; @@ -559,7 +555,6 @@ int fsl_esdhc_initialize(bd_t *bis, struct fsl_esdhc_cfg *cfg) if (detect_mmc_emmc_ddr_port(cfg)) mmc->host_caps |= EMMC_MODE_4BIT_DDR; #endif - #endif /* #ifndef CONFIG_MX50_ENABLE_USDHC_SDR */ mmc->f_min = 400000; 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; } - |