X-Git-Url: https://git.kernelconcepts.de/?a=blobdiff_plain;f=drivers%2Fmmc%2Fmmc.c;h=c6a1c23fbf1cdf268d79bf1da3a7a3ed80dd220c;hb=ab71188ce87ebb66192a5bdbbb9d58052bd32d93;hp=1355735a52e7bbeff7b7303bb83cd393567ebd04;hpb=ce0fbcd2e1bd8c7b0975589b9853f3e8914aa406;p=karo-tx-uboot.git diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 1355735a52..c6a1c23fbf 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -4,23 +4,7 @@ * * Based vaguely on the Linux code * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA + * SPDX-License-Identifier: GPL-2.0+ */ #include @@ -30,8 +14,8 @@ #include #include #include -#include #include +#include "mmc_private.h" /* Set block count limit because of 16 bit register limit on some hardware*/ #ifndef CONFIG_SYS_MMC_MAX_BLK_COUNT @@ -41,16 +25,138 @@ static struct list_head mmc_devices; static int cur_dev_num = -1; -int __board_mmc_getcd(u8 *cd, struct mmc *mmc) { +int __weak board_mmc_getwp(struct mmc *mmc) +{ + return -1; +} + +int mmc_getwp(struct mmc *mmc) +{ + int wp; + + wp = board_mmc_getwp(mmc); + + if (wp < 0) { + if (mmc->getwp) + wp = mmc->getwp(mmc); + else + wp = 0; + } + + return wp; +} + +int __board_mmc_getcd(struct mmc *mmc) { return -1; } -int board_mmc_getcd(u8 *cd, struct mmc *mmc)__attribute__((weak, +int board_mmc_getcd(struct mmc *mmc)__attribute__((weak, alias("__board_mmc_getcd"))); int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) { - return mmc->send_cmd(mmc, cmd, data); + int ret; + +#ifdef CONFIG_MMC_TRACE + int i; + u8 *ptr; + + printf("CMD_SEND:%d\n", cmd->cmdidx); + printf("\t\tARG\t\t\t 0x%08X\n", cmd->cmdarg); + 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 = (u8 *)&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; + } +#else + ret = mmc->send_cmd(mmc, cmd, data); +#endif + return ret; +} + +int mmc_send_status(struct mmc *mmc, int timeout) +{ + struct mmc_cmd cmd; + int err, retries = 5; +#ifdef CONFIG_MMC_TRACE + int status; +#endif + + cmd.cmdidx = MMC_CMD_SEND_STATUS; + cmd.resp_type = MMC_RSP_R1; + if (!mmc_host_is_spi(mmc)) + cmd.cmdarg = mmc->rca << 16; + + do { + err = mmc_send_cmd(mmc, &cmd, NULL); + if (!err) { + if ((cmd.response[0] & MMC_STATUS_RDY_FOR_DATA) && + (cmd.response[0] & MMC_STATUS_CURR_STATE) != + MMC_STATE_PRG) + break; + else if (cmd.response[0] & MMC_STATUS_MASK) { +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) + printf("Status Error: 0x%08X\n", + cmd.response[0]); +#endif + return COMM_ERR; + } + } else if (--retries < 0) + return err; + + udelay(1000); + + } while (timeout--); + +#ifdef CONFIG_MMC_TRACE + status = (cmd.response[0] & MMC_STATUS_CURR_STATE) >> 9; + printf("CURR STATE:%d\n", status); +#endif + if (timeout <= 0) { +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) + printf("Timeout waiting card ready\n"); +#endif + return TIMEOUT; + } + + return 0; } int mmc_set_blocklen(struct mmc *mmc, int len) @@ -60,7 +166,6 @@ int mmc_set_blocklen(struct mmc *mmc, int len) cmd.cmdidx = MMC_CMD_SET_BLOCKLEN; cmd.resp_type = MMC_RSP_R1; cmd.cmdarg = len; - cmd.flags = 0; return mmc_send_cmd(mmc, &cmd, NULL); } @@ -77,86 +182,15 @@ struct mmc *find_mmc_device(int dev_num) return m; } +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) printf("MMC Device %d not found\n", dev_num); +#endif return NULL; } -static ulong -mmc_write_blocks(struct mmc *mmc, ulong start, lbaint_t blkcnt, const void*src) -{ - struct mmc_cmd cmd; - struct mmc_data data; - - if ((start + blkcnt) > mmc->block_dev.lba) { - printf("MMC: block number 0x%lx exceeds max(0x%lx)\n", - start + blkcnt, mmc->block_dev.lba); - return 0; - } - - if (blkcnt > 1) - cmd.cmdidx = MMC_CMD_WRITE_MULTIPLE_BLOCK; - else - cmd.cmdidx = MMC_CMD_WRITE_SINGLE_BLOCK; - - if (mmc->high_capacity) - cmd.cmdarg = start; - else - cmd.cmdarg = start * mmc->write_bl_len; - - cmd.resp_type = MMC_RSP_R1; - cmd.flags = 0; - - data.src = src; - data.blocks = blkcnt; - data.blocksize = mmc->write_bl_len; - data.flags = MMC_DATA_WRITE; - - if (mmc_send_cmd(mmc, &cmd, &data)) { - printf("mmc write failed\n"); - return 0; - } - - if (blkcnt > 1) { - cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION; - cmd.cmdarg = 0; - cmd.resp_type = MMC_RSP_R1b; - cmd.flags = 0; - if (mmc_send_cmd(mmc, &cmd, NULL)) { - printf("mmc fail to send stop cmd\n"); - return 0; - } - } - - return blkcnt; -} - -static ulong -mmc_bwrite(int dev_num, ulong start, lbaint_t blkcnt, const void*src) -{ - lbaint_t cur, blocks_todo = blkcnt; - - struct mmc *mmc = find_mmc_device(dev_num); - if (!mmc) - return 0; - - if (mmc_set_blocklen(mmc, mmc->write_bl_len)) - return 0; - - do { - cur = (blocks_todo > CONFIG_SYS_MMC_MAX_BLK_COUNT) ? - CONFIG_SYS_MMC_MAX_BLK_COUNT : blocks_todo; - if(mmc_write_blocks(mmc, start, cur, src) != cur) - return 0; - blocks_todo -= cur; - start += cur; - src += cur * mmc->write_bl_len; - } while (blocks_todo > 0); - - return blkcnt; -} - -int mmc_read_blocks(struct mmc *mmc, void *dst, ulong start, lbaint_t blkcnt) +static int mmc_read_blocks(struct mmc *mmc, void *dst, lbaint_t start, + lbaint_t blkcnt) { struct mmc_cmd cmd; struct mmc_data data; @@ -172,7 +206,6 @@ int mmc_read_blocks(struct mmc *mmc, void *dst, ulong start, lbaint_t blkcnt) cmd.cmdarg = start * mmc->read_bl_len; cmd.resp_type = MMC_RSP_R1; - cmd.flags = 0; data.dest = dst; data.blocks = blkcnt; @@ -186,9 +219,10 @@ int mmc_read_blocks(struct mmc *mmc, void *dst, ulong start, lbaint_t blkcnt) cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION; cmd.cmdarg = 0; cmd.resp_type = MMC_RSP_R1b; - cmd.flags = 0; if (mmc_send_cmd(mmc, &cmd, NULL)) { +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) printf("mmc fail to send stop cmd\n"); +#endif return 0; } } @@ -196,7 +230,7 @@ int mmc_read_blocks(struct mmc *mmc, void *dst, ulong start, lbaint_t blkcnt) return blkcnt; } -static ulong mmc_bread(int dev_num, ulong start, lbaint_t blkcnt, void *dst) +static ulong mmc_bread(int dev_num, lbaint_t start, lbaint_t blkcnt, void *dst) { lbaint_t cur, blocks_todo = blkcnt; @@ -208,8 +242,10 @@ static ulong mmc_bread(int dev_num, ulong start, lbaint_t blkcnt, void *dst) return 0; if ((start + blkcnt) > mmc->block_dev.lba) { - printf("MMC: block number 0x%lx exceeds max(0x%lx)\n", +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) + printf("MMC: block number 0x" LBAF " exceeds max(0x" LBAF ")\n", start + blkcnt, mmc->block_dev.lba); +#endif return 0; } @@ -217,8 +253,7 @@ static ulong mmc_bread(int dev_num, ulong start, lbaint_t blkcnt, void *dst) return 0; do { - cur = (blocks_todo > CONFIG_SYS_MMC_MAX_BLK_COUNT) ? - CONFIG_SYS_MMC_MAX_BLK_COUNT : 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; @@ -229,7 +264,7 @@ static ulong mmc_bread(int dev_num, ulong start, lbaint_t blkcnt, void *dst) return blkcnt; } -int mmc_go_idle(struct mmc* mmc) +static int mmc_go_idle(struct mmc *mmc) { struct mmc_cmd cmd; int err; @@ -239,7 +274,6 @@ int mmc_go_idle(struct mmc* mmc) cmd.cmdidx = MMC_CMD_GO_IDLE_STATE; cmd.cmdarg = 0; cmd.resp_type = MMC_RSP_NONE; - cmd.flags = 0; err = mmc_send_cmd(mmc, &cmd, NULL); @@ -251,8 +285,7 @@ int mmc_go_idle(struct mmc* mmc) return 0; } -int -sd_send_op_cond(struct mmc *mmc) +static int sd_send_op_cond(struct mmc *mmc) { int timeout = 1000; int err; @@ -262,7 +295,6 @@ sd_send_op_cond(struct mmc *mmc) cmd.cmdidx = MMC_CMD_APP_CMD; cmd.resp_type = MMC_RSP_R1; cmd.cmdarg = 0; - cmd.flags = 0; err = mmc_send_cmd(mmc, &cmd, NULL); @@ -279,7 +311,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; @@ -298,6 +331,17 @@ 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; + + 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); @@ -306,31 +350,81 @@ sd_send_op_cond(struct mmc *mmc) return 0; } +/* We pass in the cmd since otherwise the init seems to fail */ +static int mmc_send_op_cond_iter(struct mmc *mmc, struct mmc_cmd *cmd, + int use_arg) +{ + int err; + + cmd->cmdidx = MMC_CMD_SEND_OP_COND; + cmd->resp_type = MMC_RSP_R3; + cmd->cmdarg = 0; + if (use_arg && !mmc_host_is_spi(mmc)) { + cmd->cmdarg = + (mmc->voltages & + (mmc->op_cond_response & OCR_VOLTAGE_MASK)) | + (mmc->op_cond_response & OCR_ACCESS_MODE); + + if (mmc->host_caps & MMC_MODE_HC) + cmd->cmdarg |= OCR_HCS; + } + err = mmc_send_cmd(mmc, cmd, NULL); + if (err) + return err; + mmc->op_cond_response = cmd->response[0]; + return 0; +} + int mmc_send_op_cond(struct mmc *mmc) { - int timeout = 1000; struct mmc_cmd cmd; - int err; + int err, i; /* Some cards seem to need this */ mmc_go_idle(mmc); + /* Asking to the card its capabilities */ + mmc->op_cond_pending = 1; + for (i = 0; i < 2; i++) { + err = mmc_send_op_cond_iter(mmc, &cmd, i != 0); + if (err) + return err; + + /* exit if not busy (flag seems to be inverted) */ + if (mmc->op_cond_response & OCR_BUSY) + return 0; + } + return IN_PROGRESS; +} + +int mmc_complete_op_cond(struct mmc *mmc) +{ + struct mmc_cmd cmd; + int timeout = 1000; + uint start; + int err; + + mmc->op_cond_pending = 0; + start = get_timer(0); do { - cmd.cmdidx = MMC_CMD_SEND_OP_COND; + err = mmc_send_op_cond_iter(mmc, &cmd, 1); + if (err) + return err; + if (get_timer(start) > timeout) + return UNUSABLE_ERR; + udelay(100); + } while (!(mmc->op_cond_response & OCR_BUSY)); + + 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 = OCR_HCS | mmc->voltages; - cmd.flags = 0; + cmd.cmdarg = 0; err = mmc_send_cmd(mmc, &cmd, NULL); if (err) return err; - - udelay(1000); - } while (!(cmd.response[0] & OCR_BUSY) && timeout--); - - if (timeout <= 0) - return UNUSABLE_ERR; + } mmc->version = MMC_VERSION_UNKNOWN; mmc->ocr = cmd.response[0]; @@ -342,7 +436,7 @@ int mmc_send_op_cond(struct mmc *mmc) } -int mmc_send_ext_csd(struct mmc *mmc, char *ext_csd) +static int mmc_send_ext_csd(struct mmc *mmc, u8 *ext_csd) { struct mmc_cmd cmd; struct mmc_data data; @@ -352,11 +446,10 @@ int mmc_send_ext_csd(struct mmc *mmc, char *ext_csd) cmd.cmdidx = MMC_CMD_SEND_EXT_CSD; cmd.resp_type = MMC_RSP_R1; cmd.cmdarg = 0; - cmd.flags = 0; - data.dest = ext_csd; + data.dest = (char *)ext_csd; data.blocks = 1; - data.blocksize = 512; + data.blocksize = MMC_MAX_BLOCK_LEN; data.flags = MMC_DATA_READ; err = mmc_send_cmd(mmc, &cmd, &data); @@ -365,43 +458,49 @@ int mmc_send_ext_csd(struct mmc *mmc, char *ext_csd) } -int mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value) +static 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); - cmd.flags = 0; + (index << 16) | + (value << 8); + + ret = mmc_send_cmd(mmc, &cmd, NULL); + + /* Waiting for the ready status */ + if (!ret) + ret = mmc_send_status(mmc, timeout); + + return ret; - return mmc_send_cmd(mmc, &cmd, NULL); } -int mmc_change_freq(struct mmc *mmc) +static int mmc_change_freq(struct mmc *mmc) { - char ext_csd[512]; + ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN); 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_MODE_4BIT; - err = mmc_send_ext_csd(mmc, ext_csd); if (err) return err; - if (ext_csd[212] || ext_csd[213] || ext_csd[214] || ext_csd[215]) - mmc->high_capacity = 1; - - cardtype = ext_csd[196] & 0xf; + cardtype = ext_csd[EXT_CSD_CARD_TYPE] & 0xf; err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, 1); @@ -415,7 +514,7 @@ int mmc_change_freq(struct mmc *mmc) return err; /* No high-speed support */ - if (!ext_csd[185]) + if (!ext_csd[EXT_CSD_HS_TIMING]) return 0; /* High Speed is set, there are two types: 52MHz and 26MHz */ @@ -427,7 +526,68 @@ int mmc_change_freq(struct mmc *mmc) return 0; } -int sd_switch(struct mmc *mmc, int mode, int group, u8 value, u8 *resp) +static int mmc_set_capacity(struct mmc *mmc, int part_num) +{ + switch (part_num) { + case 0: + mmc->capacity = mmc->capacity_user; + break; + case 1: + case 2: + mmc->capacity = mmc->capacity_boot; + break; + case 3: + mmc->capacity = mmc->capacity_rpmb; + break; + case 4: + case 5: + case 6: + case 7: + mmc->capacity = mmc->capacity_gp[part_num - 4]; + break; + default: + return -1; + } + + mmc->block_dev.lba = lldiv(mmc->capacity, mmc->read_bl_len); + + return 0; +} + +int mmc_switch_part(int dev_num, unsigned int part_num) +{ + struct mmc *mmc = find_mmc_device(dev_num); + int ret; + + if (!mmc) + return -1; + + ret = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_PART_CONF, + (mmc->part_config & ~PART_ACCESS_MASK) + | (part_num & PART_ACCESS_MASK)); + if (ret) + return ret; + + return mmc_set_capacity(mmc, part_num); +} + +int mmc_getcd(struct mmc *mmc) +{ + int cd; + + cd = board_mmc_getcd(mmc); + + if (cd < 0) { + if (mmc->getcd) + cd = mmc->getcd(mmc); + else + cd = 1; + } + + return cd; +} + +static int sd_switch(struct mmc *mmc, int mode, int group, u8 value, u8 *resp) { struct mmc_cmd cmd; struct mmc_data data; @@ -438,7 +598,6 @@ int sd_switch(struct mmc *mmc, int mode, int group, u8 value, u8 *resp) cmd.cmdarg = (mode << 31) | 0xffffff; cmd.cmdarg &= ~(0xf << (group * 4)); cmd.cmdarg |= value << (group * 4); - cmd.flags = 0; data.dest = (char *)resp; data.blocksize = 64; @@ -449,22 +608,24 @@ int sd_switch(struct mmc *mmc, int mode, int group, u8 value, u8 *resp) } -int sd_change_freq(struct mmc *mmc) +static int sd_change_freq(struct mmc *mmc) { int err; struct mmc_cmd cmd; - uint scr[2]; - uint switch_status[16]; + ALLOC_CACHE_ALIGN_BUFFER(uint, scr, 2); + ALLOC_CACHE_ALIGN_BUFFER(uint, switch_status, 16); struct mmc_data data; int timeout; 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; cmd.cmdarg = mmc->rca << 16; - cmd.flags = 0; err = mmc_send_cmd(mmc, &cmd, NULL); @@ -474,12 +635,11 @@ int sd_change_freq(struct mmc *mmc) cmd.cmdidx = SD_CMD_APP_SEND_SCR; cmd.resp_type = MMC_RSP_R1; cmd.cmdarg = 0; - cmd.flags = 0; timeout = 3; retry_scr: - data.dest = (char *)&scr; + data.dest = (char *)scr; data.blocksize = 8; data.blocks = 1; data.flags = MMC_DATA_READ; @@ -505,6 +665,8 @@ retry_scr: break; case 2: mmc->version = SD_VERSION_2; + if ((mmc->scr[0] >> 15) & 0x1) + mmc->version = SD_VERSION_3; break; default: mmc->version = SD_VERSION_1_0; @@ -521,7 +683,7 @@ retry_scr: timeout = 4; while (timeout--) { err = sd_switch(mmc, SD_SWITCH_CHECK, 0, 1, - (u8 *)&switch_status); + (u8 *)switch_status); if (err) return err; @@ -535,7 +697,17 @@ retry_scr: if (!(__be32_to_cpu(switch_status[3]) & SD_HIGHSPEED_SUPPORTED)) return 0; - err = sd_switch(mmc, SD_SWITCH_SWITCH, 0, 1, (u8 *)&switch_status); + /* + * If the host doesn't support SD_HIGHSPEED, do not switch card to + * HIGHSPEED mode even if the card support SD_HIGHSPPED. + * This can avoid furthur problem when the card runs in different + * mode between the host. + */ + if (!((mmc->host_caps & MMC_MODE_HS_52MHz) && + (mmc->host_caps & MMC_MODE_HS))) + return 0; + + err = sd_switch(mmc, SD_SWITCH_SWITCH, 0, 1, (u8 *)switch_status); if (err) return err; @@ -548,7 +720,7 @@ retry_scr: /* frequency bases */ /* divided by 10 to be nice to platforms without floating point */ -int fbase[] = { +static const int fbase[] = { 10000, 100000, 1000000, @@ -558,7 +730,7 @@ int fbase[] = { /* Multiplier values for TRAN_SPEED. Multiplied by 10 to be nice * to platforms without floating point. */ -int multipliers[] = { +static const int multipliers[] = { 0, /* reserved */ 10, 12, @@ -577,7 +749,7 @@ int multipliers[] = { 80, }; -void mmc_set_ios(struct mmc *mmc) +static void mmc_set_ios(struct mmc *mmc) { mmc->set_ios(mmc); } @@ -595,26 +767,40 @@ void mmc_set_clock(struct mmc *mmc, uint clock) mmc_set_ios(mmc); } -void mmc_set_bus_width(struct mmc *mmc, uint width) +static void mmc_set_bus_width(struct mmc *mmc, uint width) { mmc->bus_width = width; mmc_set_ios(mmc); } -int mmc_startup(struct mmc *mmc) +static int mmc_startup(struct mmc *mmc) { - int err; + int err, i; uint mult, freq; - u64 cmult, csize; + u64 cmult, csize, capacity; struct mmc_cmd cmd; - char ext_csd[512]; + ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN); + ALLOC_CACHE_ALIGN_BUFFER(u8, test_csd, MMC_MAX_BLOCK_LEN); + int timeout = 1000; + +#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; + err = mmc_send_cmd(mmc, &cmd, NULL); + + if (err) + return err; + } +#endif /* 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; err = mmc_send_cmd(mmc, &cmd, NULL); @@ -628,27 +814,30 @@ 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; - 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; cmd.resp_type = MMC_RSP_R2; cmd.cmdarg = mmc->rca << 16; - cmd.flags = 0; err = mmc_send_cmd(mmc, &cmd, NULL); + /* Waiting for the ready status */ + mmc_send_status(mmc, timeout); + if (err) return err; @@ -688,6 +877,7 @@ int mmc_startup(struct mmc *mmc) mmc->tran_speed = freq * mult; + mmc->dsr_imp = ((cmd.response[1] >> 12) & 0x1); mmc->read_bl_len = 1 << ((cmd.response[1] >> 16) & 0xf); if (IS_SD(mmc)) @@ -705,35 +895,128 @@ int mmc_startup(struct mmc *mmc) cmult = (mmc->csd[2] & 0x00038000) >> 15; } - mmc->capacity = (csize + 1) << (cmult + 2); - mmc->capacity *= mmc->read_bl_len; - - if (mmc->read_bl_len > 512) - mmc->read_bl_len = 512; - - if (mmc->write_bl_len > 512) - mmc->write_bl_len = 512; + mmc->capacity_user = (csize + 1) << (cmult + 2); + mmc->capacity_user *= mmc->read_bl_len; + mmc->capacity_boot = 0; + mmc->capacity_rpmb = 0; + for (i = 0; i < 4; i++) + mmc->capacity_gp[i] = 0; + + if (mmc->read_bl_len > MMC_MAX_BLOCK_LEN) + mmc->read_bl_len = MMC_MAX_BLOCK_LEN; + + if (mmc->write_bl_len > MMC_MAX_BLOCK_LEN) + mmc->write_bl_len = MMC_MAX_BLOCK_LEN; + + if ((mmc->dsr_imp) && (0xffffffff != mmc->dsr)) { + cmd.cmdidx = MMC_CMD_SET_DSR; + cmd.cmdarg = (mmc->dsr & 0xffff) << 16; + cmd.resp_type = MMC_RSP_NONE; + if (mmc_send_cmd(mmc, &cmd, NULL)) + printf("MMC: SET_DSR failed\n"); + } /* 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_R1; + cmd.cmdarg = mmc->rca << 16; + 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->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; + if (!err && (ext_csd[EXT_CSD_REV] >= 2)) { + /* + * According to the JEDEC Standard, the value of + * ext_csd's capacity is valid if the value is more + * than 2GB + */ + capacity = ext_csd[EXT_CSD_SEC_CNT] << 0 + | ext_csd[EXT_CSD_SEC_CNT + 1] << 8 + | ext_csd[EXT_CSD_SEC_CNT + 2] << 16 + | ext_csd[EXT_CSD_SEC_CNT + 3] << 24; + capacity *= MMC_MAX_BLOCK_LEN; + if ((capacity >> 20) > 2 * 1024) + mmc->capacity_user = capacity; + } + + switch (ext_csd[EXT_CSD_REV]) { + case 1: + mmc->version = MMC_VERSION_4_1; + break; + case 2: + mmc->version = MMC_VERSION_4_2; + break; + case 3: + mmc->version = MMC_VERSION_4_3; + break; + case 5: + mmc->version = MMC_VERSION_4_41; + break; + case 6: + mmc->version = MMC_VERSION_4_5; + break; + } + + /* + * Host needs to enable ERASE_GRP_DEF bit if device is + * partitioned. This bit will be lost every time after a reset + * or power off. This will affect erase size. + */ + if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) && + (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & PART_ENH_ATTRIB)) { + err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_ERASE_GROUP_DEF, 1); + + if (err) + return err; + + /* Read out group size from ext_csd */ + mmc->erase_grp_size = + ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] * + MMC_MAX_BLOCK_LEN * 1024; + } else { + /* Calculate the group size from the csd value. */ + 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[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) || + ext_csd[EXT_CSD_BOOT_MULT]) + mmc->part_config = ext_csd[EXT_CSD_PART_CONF]; + + mmc->capacity_boot = ext_csd[EXT_CSD_BOOT_MULT] << 17; + + mmc->capacity_rpmb = ext_csd[EXT_CSD_RPMB_MULT] << 17; + + for (i = 0; i < 4; i++) { + int idx = EXT_CSD_GP_SIZE_MULT + i * 3; + mmc->capacity_gp[i] = (ext_csd[idx + 2] << 16) + + (ext_csd[idx + 1] << 8) + ext_csd[idx]; + mmc->capacity_gp[i] *= + ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]; + mmc->capacity_gp[i] *= ext_csd[EXT_CSD_HC_WP_GRP_SIZE]; } } + err = mmc_set_capacity(mmc, mmc->part_num); + if (err) + return err; + if (IS_SD(mmc)) err = sd_change_freq(mmc); else @@ -750,7 +1033,6 @@ int mmc_startup(struct mmc *mmc) cmd.cmdidx = MMC_CMD_APP_CMD; cmd.resp_type = MMC_RSP_R1; cmd.cmdarg = mmc->rca << 16; - cmd.flags = 0; err = mmc_send_cmd(mmc, &cmd, NULL); if (err) @@ -759,7 +1041,6 @@ int mmc_startup(struct mmc *mmc) cmd.cmdidx = SD_CMD_APP_SET_BUS_WIDTH; cmd.resp_type = MMC_RSP_R1; cmd.cmdarg = 2; - cmd.flags = 0; err = mmc_send_cmd(mmc, &cmd, NULL); if (err) return err; @@ -768,59 +1049,105 @@ int mmc_startup(struct mmc *mmc) } if (mmc->card_caps & MMC_MODE_HS) - mmc_set_clock(mmc, 50000000); + mmc->tran_speed = 50000000; else - mmc_set_clock(mmc, 25000000); + mmc->tran_speed = 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, - EXT_CSD_BUS_WIDTH, - EXT_CSD_BUS_WIDTH_4); - - if (err) - return err; + int idx; + + /* An array of possible bus widths in order of preference */ + static unsigned ext_csd_bits[] = { + EXT_CSD_BUS_WIDTH_8, + EXT_CSD_BUS_WIDTH_4, + EXT_CSD_BUS_WIDTH_1, + }; + + /* An array to map CSD bus widths to host cap bits */ + static unsigned ext_to_hostcaps[] = { + [EXT_CSD_BUS_WIDTH_4] = MMC_MODE_4BIT, + [EXT_CSD_BUS_WIDTH_8] = MMC_MODE_8BIT, + }; + + /* An array to map chosen bus width to an integer */ + static unsigned widths[] = { + 8, 4, 1, + }; + + for (idx=0; idx < ARRAY_SIZE(ext_csd_bits); idx++) { + unsigned int extw = ext_csd_bits[idx]; + + /* + * Check to make sure the controller supports + * this bus width, if it's more than 1 + */ + if (extw != EXT_CSD_BUS_WIDTH_1 && + !(mmc->host_caps & ext_to_hostcaps[extw])) + continue; - mmc_set_bus_width(mmc, 4); - } else if (mmc->card_caps & MMC_MODE_8BIT) { - /* Set the card to use 8 bit*/ err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, - EXT_CSD_BUS_WIDTH, - EXT_CSD_BUS_WIDTH_8); + EXT_CSD_BUS_WIDTH, extw); if (err) - return err; - - mmc_set_bus_width(mmc, 8); + continue; + + mmc_set_bus_width(mmc, widths[idx]); + + err = mmc_send_ext_csd(mmc, test_csd); + if (!err && ext_csd[EXT_CSD_PARTITIONING_SUPPORT] \ + == test_csd[EXT_CSD_PARTITIONING_SUPPORT] + && ext_csd[EXT_CSD_ERASE_GROUP_DEF] \ + == test_csd[EXT_CSD_ERASE_GROUP_DEF] \ + && ext_csd[EXT_CSD_REV] \ + == test_csd[EXT_CSD_REV] + && ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] \ + == test_csd[EXT_CSD_HC_ERASE_GRP_SIZE] + && memcmp(&ext_csd[EXT_CSD_SEC_CNT], \ + &test_csd[EXT_CSD_SEC_CNT], 4) == 0) { + + mmc->card_caps |= ext_to_hostcaps[extw]; + break; + } } if (mmc->card_caps & MMC_MODE_HS) { if (mmc->card_caps & MMC_MODE_HS_52MHz) - mmc_set_clock(mmc, 52000000); + mmc->tran_speed = 52000000; else - mmc_set_clock(mmc, 26000000); - } else - mmc_set_clock(mmc, 20000000); + mmc->tran_speed = 26000000; + } } + mmc_set_clock(mmc, mmc->tran_speed); + /* fill in device description */ mmc->block_dev.lun = 0; mmc->block_dev.type = 0; mmc->block_dev.blksz = mmc->read_bl_len; + mmc->block_dev.log2blksz = LOG2(mmc->block_dev.blksz); mmc->block_dev.lba = lldiv(mmc->capacity, mmc->read_bl_len); - sprintf(mmc->block_dev.vendor, "Man %06x Snr %08x", mmc->cid[0] >> 8, - (mmc->cid[2] << 8) | (mmc->cid[3] >> 24)); - sprintf(mmc->block_dev.product, "%c%c%c%c%c", mmc->cid[0] & 0xff, - (mmc->cid[1] >> 24), (mmc->cid[1] >> 16) & 0xff, - (mmc->cid[1] >> 8) & 0xff, mmc->cid[1] & 0xff); - sprintf(mmc->block_dev.revision, "%d.%d", mmc->cid[2] >> 28, - (mmc->cid[2] >> 24) & 0xf); +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) + sprintf(mmc->block_dev.vendor, "Man %06x Snr %04x%04x", + mmc->cid[0] >> 24, (mmc->cid[2] & 0xffff), + (mmc->cid[3] >> 16) & 0xffff); + sprintf(mmc->block_dev.product, "%c%c%c%c%c%c", mmc->cid[0] & 0xff, + (mmc->cid[1] >> 24), (mmc->cid[1] >> 16) & 0xff, + (mmc->cid[1] >> 8) & 0xff, mmc->cid[1] & 0xff, + (mmc->cid[2] >> 24) & 0xff); + sprintf(mmc->block_dev.revision, "%d.%d", (mmc->cid[2] >> 20) & 0xf, + (mmc->cid[2] >> 16) & 0xf); +#else + mmc->block_dev.vendor[0] = 0; + mmc->block_dev.product[0] = 0; + mmc->block_dev.revision[0] = 0; +#endif +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBDISK_SUPPORT) init_part(&mmc->block_dev); +#endif return 0; } -int mmc_send_if_cond(struct mmc *mmc) +static int mmc_send_if_cond(struct mmc *mmc) { struct mmc_cmd cmd; int err; @@ -829,7 +1156,6 @@ int mmc_send_if_cond(struct mmc *mmc) /* We set the bit if the host supports voltages between 2.7 and 3.6 V */ cmd.cmdarg = ((mmc->voltages & 0xff8000) != 0) << 8 | 0xaa; cmd.resp_type = MMC_RSP_R7; - cmd.flags = 0; err = mmc_send_cmd(mmc, &cmd, NULL); @@ -846,12 +1172,18 @@ int mmc_send_if_cond(struct mmc *mmc) int mmc_register(struct mmc *mmc) { + /* Setup dsr related values */ + mmc->dsr_imp = 0; + mmc->dsr = 0xffffffff; /* Setup the universal parts of the block interface just once */ mmc->block_dev.if_type = IF_TYPE_MMC; mmc->block_dev.dev = cur_dev_num++; 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); @@ -860,17 +1192,32 @@ 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); + if (!mmc || mmc_init(mmc)) + return NULL; - return mmc ? &mmc->block_dev : NULL; + return &mmc->block_dev; } +#endif -int mmc_init(struct mmc *mmc) +int mmc_start_init(struct mmc *mmc) { int err; + if (mmc_getcd(mmc) == 0) { + mmc->has_init = 0; +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) + printf("MMC: no card present\n"); +#endif + return NO_CARD_ERR; + } + + if (mmc->has_init) + return 0; + err = mmc->init(mmc); if (err) @@ -885,6 +1232,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); @@ -895,13 +1245,57 @@ int mmc_init(struct mmc *mmc) if (err == TIMEOUT) { err = mmc_send_op_cond(mmc); - if (err) { + if (err && err != IN_PROGRESS) { +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) printf("Card did not respond to voltage select!\n"); +#endif return UNUSABLE_ERR; } } - return mmc_startup(mmc); + if (err == IN_PROGRESS) + mmc->init_in_progress = 1; + + return err; +} + +static int mmc_complete_init(struct mmc *mmc) +{ + int err = 0; + + if (mmc->op_cond_pending) + err = mmc_complete_op_cond(mmc); + + if (!err) + err = mmc_startup(mmc); + if (err) + mmc->has_init = 0; + else + mmc->has_init = 1; + mmc->init_in_progress = 0; + return err; +} + +int mmc_init(struct mmc *mmc) +{ + int err = IN_PROGRESS; + unsigned start = get_timer(0); + + if (mmc->has_init) + return 0; + if (!mmc->init_in_progress) + err = mmc_start_init(mmc); + + if (!err || err == IN_PROGRESS) + err = mmc_complete_init(mmc); + debug("%s: %d, time %lu\n", __func__, err, get_timer(start)); + return err; +} + +int mmc_set_dsr(struct mmc *mmc, u16 val) +{ + mmc->dsr = val; + return 0; } /* @@ -916,6 +1310,8 @@ static int __def_mmc_init(bd_t *bis) int cpu_mmc_init(bd_t *bis) __attribute__((weak, alias("__def_mmc_init"))); int board_mmc_init(bd_t *bis) __attribute__((weak, alias("__def_mmc_init"))); +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) + void print_mmc_devices(char separator) { struct mmc *m; @@ -933,6 +1329,34 @@ void print_mmc_devices(char separator) printf("\n"); } +#else +void print_mmc_devices(char separator) { } +#endif + +int get_mmc_num(void) +{ + return cur_dev_num; +} + +void mmc_set_preinit(struct mmc *mmc, int preinit) +{ + mmc->preinit = preinit; +} + +static void do_preinit(void) +{ + struct mmc *m; + struct list_head *entry; + + list_for_each(entry, &mmc_devices) { + m = list_entry(entry, struct mmc, link); + + if (m->preinit) + mmc_start_init(m); + } +} + + int mmc_initialize(bd_t *bis) { INIT_LIST_HEAD (&mmc_devices); @@ -941,7 +1365,144 @@ int mmc_initialize(bd_t *bis) if (board_mmc_init(bis) < 0) cpu_mmc_init(bis); +#ifndef CONFIG_SPL_BUILD print_mmc_devices(','); +#endif + do_preinit(); return 0; } + +#ifdef CONFIG_SUPPORT_EMMC_BOOT +/* + * This function changes the size of boot partition and the size of rpmb + * partition present on EMMC devices. + * + * Input Parameters: + * struct *mmc: pointer for the mmc device strcuture + * bootsize: size of boot partition + * rpmbsize: size of rpmb partition + * + * Returns 0 on success. + */ + +int mmc_boot_partition_size_change(struct mmc *mmc, unsigned long bootsize, + unsigned long rpmbsize) +{ + int err; + struct mmc_cmd cmd; + + /* Only use this command for raw EMMC moviNAND. Enter backdoor mode */ + cmd.cmdidx = MMC_CMD_RES_MAN; + cmd.resp_type = MMC_RSP_R1b; + cmd.cmdarg = MMC_CMD62_ARG1; + + err = mmc_send_cmd(mmc, &cmd, NULL); + if (err) { + debug("mmc_boot_partition_size_change: Error1 = %d\n", err); + return err; + } + + /* Boot partition changing mode */ + cmd.cmdidx = MMC_CMD_RES_MAN; + cmd.resp_type = MMC_RSP_R1b; + cmd.cmdarg = MMC_CMD62_ARG2; + + err = mmc_send_cmd(mmc, &cmd, NULL); + if (err) { + debug("mmc_boot_partition_size_change: Error2 = %d\n", err); + return err; + } + /* boot partition size is multiple of 128KB */ + bootsize = (bootsize * 1024) / 128; + + /* Arg: boot partition size */ + cmd.cmdidx = MMC_CMD_RES_MAN; + cmd.resp_type = MMC_RSP_R1b; + cmd.cmdarg = bootsize; + + err = mmc_send_cmd(mmc, &cmd, NULL); + if (err) { + debug("mmc_boot_partition_size_change: Error3 = %d\n", err); + return err; + } + /* RPMB partition size is multiple of 128KB */ + rpmbsize = (rpmbsize * 1024) / 128; + /* Arg: RPMB partition size */ + cmd.cmdidx = MMC_CMD_RES_MAN; + cmd.resp_type = MMC_RSP_R1b; + cmd.cmdarg = rpmbsize; + + err = mmc_send_cmd(mmc, &cmd, NULL); + if (err) { + debug("mmc_boot_partition_size_change: Error4 = %d\n", err); + return err; + } + return 0; +} + +/* + * This function shall form and send the commands to open / close the + * boot partition specified by user. + * + * Input Parameters: + * ack: 0x0 - No boot acknowledge sent (default) + * 0x1 - Boot acknowledge sent during boot operation + * part_num: User selects boot data that will be sent to master + * 0x0 - Device not boot enabled (default) + * 0x1 - Boot partition 1 enabled for boot + * 0x2 - Boot partition 2 enabled for boot + * access: User selects partitions to access + * 0x0 : No access to boot partition (default) + * 0x1 : R/W boot partition 1 + * 0x2 : R/W boot partition 2 + * 0x3 : R/W Replay Protected Memory Block (RPMB) + * + * Returns 0 on success. + */ +int mmc_boot_part_access(struct mmc *mmc, u8 ack, u8 part_num, u8 access) +{ + int err; + struct mmc_cmd cmd; + + /* Boot ack enable, boot partition enable , boot partition access */ + cmd.cmdidx = MMC_CMD_SWITCH; + cmd.resp_type = MMC_RSP_R1b; + + cmd.cmdarg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | + (EXT_CSD_PART_CONF << 16) | + ((EXT_CSD_BOOT_ACK(ack) | + EXT_CSD_BOOT_PART_NUM(part_num) | + EXT_CSD_PARTITION_ACCESS(access)) << 8); + + err = mmc_send_cmd(mmc, &cmd, NULL); + if (err) { + if (access) { + debug("mmc boot partition#%d open fail:Error1 = %d\n", + part_num, err); + } else { + debug("mmc boot partition#%d close fail:Error = %d\n", + part_num, err); + } + return err; + } + + if (access) { + /* 4bit transfer mode at booting time. */ + cmd.cmdidx = MMC_CMD_SWITCH; + cmd.resp_type = MMC_RSP_R1b; + + cmd.cmdarg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | + (EXT_CSD_BOOT_BUS_WIDTH << 16) | + ((1 << 0) << 8); + + err = mmc_send_cmd(mmc, &cmd, NULL); + if (err) { + debug("mmc boot partition#%d open fail:Error2 = %d\n", + part_num, err); + return err; + } + } + return 0; +} +#endif