/* 
 | 
 * Copyright 2008, Freescale Semiconductor, Inc 
 | 
 * Andy Fleming 
 | 
 * 
 | 
 * Based vaguely on the Linux code 
 | 
 * 
 | 
 * SPDX-License-Identifier:    GPL-2.0+ 
 | 
 */ 
 | 
  
 | 
#include <config.h> 
 | 
#include <common.h> 
 | 
#include <command.h> 
 | 
#include <dm.h> 
 | 
#include <dm/device-internal.h> 
 | 
#include <errno.h> 
 | 
#include <mmc.h> 
 | 
#include <part.h> 
 | 
#include <power/regulator.h> 
 | 
#include <malloc.h> 
 | 
#include <memalign.h> 
 | 
#include <linux/list.h> 
 | 
#include <div64.h> 
 | 
#include "mmc_private.h" 
 | 
  
 | 
static const unsigned int sd_au_size[] = { 
 | 
    0,        SZ_16K / 512,        SZ_32K / 512, 
 | 
    SZ_64K / 512,    SZ_128K / 512,        SZ_256K / 512, 
 | 
    SZ_512K / 512,    SZ_1M / 512,        SZ_2M / 512, 
 | 
    SZ_4M / 512,    SZ_8M / 512,        (SZ_8M + SZ_4M) / 512, 
 | 
    SZ_16M / 512,    (SZ_16M + SZ_8M) / 512,    SZ_32M / 512,    SZ_64M / 512, 
 | 
}; 
 | 
  
 | 
static char mmc_ext_csd[512]; 
 | 
  
 | 
#if CONFIG_IS_ENABLED(MMC_TINY) 
 | 
static struct mmc mmc_static; 
 | 
struct mmc *find_mmc_device(int dev_num) 
 | 
{ 
 | 
    return &mmc_static; 
 | 
} 
 | 
  
 | 
void mmc_do_preinit(void) 
 | 
{ 
 | 
    struct mmc *m = &mmc_static; 
 | 
#ifdef CONFIG_FSL_ESDHC_ADAPTER_IDENT 
 | 
    mmc_set_preinit(m, 1); 
 | 
#endif 
 | 
    if (m->preinit) 
 | 
        mmc_start_init(m); 
 | 
} 
 | 
  
 | 
struct blk_desc *mmc_get_blk_desc(struct mmc *mmc) 
 | 
{ 
 | 
    return &mmc->block_dev; 
 | 
} 
 | 
#endif 
 | 
  
 | 
#if !CONFIG_IS_ENABLED(DM_MMC) 
 | 
__weak int 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->cfg->ops->getwp) 
 | 
            wp = mmc->cfg->ops->getwp(mmc); 
 | 
        else 
 | 
            wp = 0; 
 | 
    } 
 | 
  
 | 
    return wp; 
 | 
} 
 | 
  
 | 
__weak int board_mmc_getcd(struct mmc *mmc) 
 | 
{ 
 | 
    return -1; 
 | 
} 
 | 
#endif 
 | 
  
 | 
#ifdef CONFIG_MMC_TRACE 
 | 
void mmmc_trace_before_send(struct mmc *mmc, struct mmc_cmd *cmd) 
 | 
{ 
 | 
    printf("CMD_SEND:%d\n", cmd->cmdidx); 
 | 
    printf("\t\tARG\t\t\t 0x%08X\n", cmd->cmdarg); 
 | 
} 
 | 
  
 | 
void mmmc_trace_after_send(struct mmc *mmc, struct mmc_cmd *cmd, int ret) 
 | 
{ 
 | 
    int i; 
 | 
    u8 *ptr; 
 | 
  
 | 
    if (ret) { 
 | 
        printf("\t\tRET\t\t\t %d\n", ret); 
 | 
    } else { 
 | 
        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; 
 | 
        } 
 | 
    } 
 | 
} 
 | 
  
 | 
void mmc_trace_state(struct mmc *mmc, struct mmc_cmd *cmd) 
 | 
{ 
 | 
    int status; 
 | 
  
 | 
    status = (cmd->response[0] & MMC_STATUS_CURR_STATE) >> 9; 
 | 
    printf("CURR STATE:%d\n", status); 
 | 
} 
 | 
#endif 
 | 
  
 | 
#if !CONFIG_IS_ENABLED(DM_MMC) 
 | 
int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) 
 | 
{ 
 | 
    int ret; 
 | 
  
 | 
    mmmc_trace_before_send(mmc, cmd); 
 | 
    ret = mmc->cfg->ops->send_cmd(mmc, cmd, data); 
 | 
    mmmc_trace_after_send(mmc, cmd, ret); 
 | 
  
 | 
    return ret; 
 | 
} 
 | 
#endif 
 | 
  
 | 
int mmc_send_status(struct mmc *mmc, int timeout) 
 | 
{ 
 | 
    struct mmc_cmd cmd; 
 | 
    int err, retries = 5; 
 | 
  
 | 
    cmd.cmdidx = MMC_CMD_SEND_STATUS; 
 | 
    cmd.resp_type = MMC_RSP_R1; 
 | 
    if (!mmc_host_is_spi(mmc)) 
 | 
        cmd.cmdarg = mmc->rca << 16; 
 | 
  
 | 
    while (1) { 
 | 
        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 -ECOMM; 
 | 
            } 
 | 
        } else if (--retries < 0) 
 | 
            return err; 
 | 
  
 | 
        if (timeout-- <= 0) 
 | 
            break; 
 | 
  
 | 
        udelay(1000); 
 | 
    } 
 | 
  
 | 
    mmc_trace_state(mmc, &cmd); 
 | 
    if (timeout <= 0) { 
 | 
#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) 
 | 
        printf("Timeout waiting card ready\n"); 
 | 
#endif 
 | 
        return -ETIMEDOUT; 
 | 
    } 
 | 
  
 | 
    return 0; 
 | 
} 
 | 
  
 | 
int mmc_set_blocklen(struct mmc *mmc, int len) 
 | 
{ 
 | 
    struct mmc_cmd cmd; 
 | 
  
 | 
    if (mmc_card_ddr(mmc)) 
 | 
        return 0; 
 | 
  
 | 
    cmd.cmdidx = MMC_CMD_SET_BLOCKLEN; 
 | 
    cmd.resp_type = MMC_RSP_R1; 
 | 
    cmd.cmdarg = len; 
 | 
  
 | 
    return mmc_send_cmd(mmc, &cmd, NULL); 
 | 
} 
 | 
  
 | 
static int mmc_read_blocks(struct mmc *mmc, void *dst, lbaint_t start, 
 | 
               lbaint_t blkcnt) 
 | 
{ 
 | 
    struct mmc_cmd cmd; 
 | 
    struct mmc_data data; 
 | 
  
 | 
    if (blkcnt > 1) 
 | 
        cmd.cmdidx = MMC_CMD_READ_MULTIPLE_BLOCK; 
 | 
    else 
 | 
        cmd.cmdidx = MMC_CMD_READ_SINGLE_BLOCK; 
 | 
  
 | 
    if (mmc->high_capacity) 
 | 
        cmd.cmdarg = start; 
 | 
    else 
 | 
        cmd.cmdarg = start * mmc->read_bl_len; 
 | 
  
 | 
    cmd.resp_type = MMC_RSP_R1; 
 | 
  
 | 
    data.dest = dst; 
 | 
    data.blocks = blkcnt; 
 | 
    data.blocksize = mmc->read_bl_len; 
 | 
    data.flags = MMC_DATA_READ; 
 | 
  
 | 
    if (mmc_send_cmd(mmc, &cmd, &data)) 
 | 
        return 0; 
 | 
  
 | 
    if (blkcnt > 1) { 
 | 
        cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION; 
 | 
        cmd.cmdarg = 0; 
 | 
        cmd.resp_type = MMC_RSP_R1b; 
 | 
        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; 
 | 
        } 
 | 
    } 
 | 
  
 | 
    return blkcnt; 
 | 
} 
 | 
  
 | 
#ifdef CONFIG_SPL_BLK_READ_PREPARE 
 | 
static int mmc_read_blocks_prepare(struct mmc *mmc, void *dst, lbaint_t start, 
 | 
                   lbaint_t blkcnt) 
 | 
{ 
 | 
    struct mmc_cmd cmd; 
 | 
    struct mmc_data data; 
 | 
  
 | 
    if (blkcnt > 1) 
 | 
        cmd.cmdidx = MMC_CMD_READ_MULTIPLE_BLOCK; 
 | 
    else 
 | 
        cmd.cmdidx = MMC_CMD_READ_SINGLE_BLOCK; 
 | 
  
 | 
    if (mmc->high_capacity) 
 | 
        cmd.cmdarg = start; 
 | 
    else 
 | 
        cmd.cmdarg = start * mmc->read_bl_len; 
 | 
  
 | 
    cmd.resp_type = MMC_RSP_R1; 
 | 
  
 | 
    data.dest = dst; 
 | 
    data.blocks = blkcnt; 
 | 
    data.blocksize = mmc->read_bl_len; 
 | 
    data.flags = MMC_DATA_READ; 
 | 
  
 | 
    if (mmc_send_cmd_prepare(mmc, &cmd, &data)) 
 | 
        return 0; 
 | 
  
 | 
    return blkcnt; 
 | 
} 
 | 
#endif 
 | 
  
 | 
#ifdef CONFIG_SPL_BLK_READ_PREPARE 
 | 
#if CONFIG_IS_ENABLED(BLK) 
 | 
ulong mmc_bread_prepare(struct udevice *dev, lbaint_t start, lbaint_t blkcnt, void *dst) 
 | 
#else 
 | 
ulong mmc_bread_prepare(struct blk_desc *block_dev, lbaint_t start, lbaint_t blkcnt, 
 | 
            void *dst) 
 | 
#endif 
 | 
{ 
 | 
#if CONFIG_IS_ENABLED(BLK) 
 | 
    struct blk_desc *block_dev = dev_get_uclass_platdata(dev); 
 | 
#endif 
 | 
    int dev_num = block_dev->devnum; 
 | 
    int timeout = 0; 
 | 
    int err; 
 | 
  
 | 
    if (blkcnt == 0) 
 | 
        return 0; 
 | 
  
 | 
    struct mmc *mmc = find_mmc_device(dev_num); 
 | 
  
 | 
    if (!mmc) 
 | 
        return 0; 
 | 
  
 | 
    if (CONFIG_IS_ENABLED(MMC_TINY)) 
 | 
        err = mmc_switch_part(mmc, block_dev->hwpart); 
 | 
    else 
 | 
        err = blk_dselect_hwpart(block_dev, block_dev->hwpart); 
 | 
  
 | 
    if (err < 0) 
 | 
        return 0; 
 | 
  
 | 
    if ((start + blkcnt) > block_dev->lba) { 
 | 
#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) 
 | 
        printf("MMC: block number 0x" LBAF " exceeds max(0x" LBAF ")\n", 
 | 
               start + blkcnt, block_dev->lba); 
 | 
#endif 
 | 
        return 0; 
 | 
    } 
 | 
  
 | 
    if (mmc_set_blocklen(mmc, mmc->read_bl_len)) { 
 | 
        debug("%s: Failed to set blocklen\n", __func__); 
 | 
        return 0; 
 | 
    } 
 | 
  
 | 
    if (mmc_read_blocks_prepare(mmc, dst, start, blkcnt) != blkcnt) { 
 | 
        debug("%s: Failed to read blocks\n", __func__); 
 | 
re_init_retry: 
 | 
        timeout++; 
 | 
        /* 
 | 
         * Try re-init seven times. 
 | 
         */ 
 | 
        if (timeout > 7) { 
 | 
            printf("Re-init retry timeout\n"); 
 | 
            return 0; 
 | 
        } 
 | 
  
 | 
        mmc->has_init = 0; 
 | 
        if (mmc_init(mmc)) 
 | 
            return 0; 
 | 
  
 | 
        if (mmc_read_blocks_prepare(mmc, dst, start, blkcnt) != blkcnt) { 
 | 
            printf("%s: Re-init mmc_read_blocks_prepare error\n", 
 | 
                   __func__); 
 | 
            goto re_init_retry; 
 | 
        } 
 | 
    } 
 | 
  
 | 
    return blkcnt; 
 | 
} 
 | 
#endif 
 | 
  
 | 
#if CONFIG_IS_ENABLED(BLK) 
 | 
ulong mmc_bread(struct udevice *dev, lbaint_t start, lbaint_t blkcnt, void *dst) 
 | 
#else 
 | 
ulong mmc_bread(struct blk_desc *block_dev, lbaint_t start, lbaint_t blkcnt, 
 | 
        void *dst) 
 | 
#endif 
 | 
{ 
 | 
#if CONFIG_IS_ENABLED(BLK) 
 | 
    struct blk_desc *block_dev = dev_get_uclass_platdata(dev); 
 | 
#endif 
 | 
    int dev_num = block_dev->devnum; 
 | 
    int err; 
 | 
    lbaint_t cur, blocks_todo = blkcnt; 
 | 
  
 | 
#ifdef CONFIG_SPL_BLK_READ_PREPARE 
 | 
    if (block_dev->op_flag == BLK_PRE_RW) 
 | 
#if CONFIG_IS_ENABLED(BLK) 
 | 
        return mmc_bread_prepare(dev, start, blkcnt, dst); 
 | 
#else 
 | 
        return mmc_bread_prepare(block_dev, start, blkcnt, dst); 
 | 
#endif 
 | 
#endif 
 | 
    if (blkcnt == 0) 
 | 
        return 0; 
 | 
  
 | 
    struct mmc *mmc = find_mmc_device(dev_num); 
 | 
    if (!mmc) 
 | 
        return 0; 
 | 
  
 | 
    if (CONFIG_IS_ENABLED(MMC_TINY)) 
 | 
        err = mmc_switch_part(mmc, block_dev->hwpart); 
 | 
    else 
 | 
        err = blk_dselect_hwpart(block_dev, block_dev->hwpart); 
 | 
  
 | 
    if (err < 0) 
 | 
        return 0; 
 | 
  
 | 
    if ((start + blkcnt) > block_dev->lba) { 
 | 
#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) 
 | 
        printf("MMC: block number 0x" LBAF " exceeds max(0x" LBAF ")\n", 
 | 
            start + blkcnt, block_dev->lba); 
 | 
#endif 
 | 
        return 0; 
 | 
    } 
 | 
  
 | 
    if (mmc_set_blocklen(mmc, mmc->read_bl_len)) { 
 | 
        debug("%s: Failed to set blocklen\n", __func__); 
 | 
        return 0; 
 | 
    } 
 | 
  
 | 
    do { 
 | 
        cur = (blocks_todo > mmc->cfg->b_max) ? 
 | 
            mmc->cfg->b_max : blocks_todo; 
 | 
        if (mmc_read_blocks(mmc, dst, start, cur) != cur) { 
 | 
            debug("%s: Failed to read blocks\n", __func__); 
 | 
            int timeout = 0; 
 | 
re_init_retry: 
 | 
            timeout++; 
 | 
            /* 
 | 
             * Try re-init seven times. 
 | 
             */ 
 | 
            if (timeout > 7) { 
 | 
                printf("Re-init retry timeout\n"); 
 | 
                return 0; 
 | 
            } 
 | 
  
 | 
            mmc->has_init = 0; 
 | 
            if (mmc_init(mmc)) 
 | 
                return 0; 
 | 
  
 | 
            if (mmc_read_blocks(mmc, dst, start, cur) != cur) { 
 | 
                printf("%s: Re-init mmc_read_blocks error\n", 
 | 
                       __func__); 
 | 
                goto re_init_retry; 
 | 
            } 
 | 
        } 
 | 
        blocks_todo -= cur; 
 | 
        start += cur; 
 | 
        dst += cur * mmc->read_bl_len; 
 | 
    } while (blocks_todo > 0); 
 | 
  
 | 
    return blkcnt; 
 | 
} 
 | 
  
 | 
void mmc_set_clock(struct mmc *mmc, uint clock) 
 | 
{ 
 | 
    if (clock > mmc->cfg->f_max) 
 | 
        clock = mmc->cfg->f_max; 
 | 
  
 | 
    if (clock < mmc->cfg->f_min) 
 | 
        clock = mmc->cfg->f_min; 
 | 
  
 | 
    mmc->clock = clock; 
 | 
  
 | 
    mmc_set_ios(mmc); 
 | 
} 
 | 
  
 | 
static void mmc_set_bus_width(struct mmc *mmc, uint width) 
 | 
{ 
 | 
    mmc->bus_width = width; 
 | 
  
 | 
    mmc_set_ios(mmc); 
 | 
} 
 | 
  
 | 
static void mmc_set_timing(struct mmc *mmc, uint timing) 
 | 
{ 
 | 
    mmc->timing = timing; 
 | 
    mmc_set_ios(mmc); 
 | 
} 
 | 
  
 | 
static int mmc_go_idle(struct mmc *mmc) 
 | 
{ 
 | 
    struct mmc_cmd cmd; 
 | 
    int err; 
 | 
  
 | 
    udelay(1000); 
 | 
  
 | 
    cmd.cmdidx = MMC_CMD_GO_IDLE_STATE; 
 | 
    cmd.cmdarg = 0; 
 | 
    cmd.resp_type = MMC_RSP_NONE; 
 | 
  
 | 
    err = mmc_send_cmd(mmc, &cmd, NULL); 
 | 
  
 | 
    if (err) 
 | 
        return err; 
 | 
  
 | 
    udelay(2000); 
 | 
  
 | 
    return 0; 
 | 
} 
 | 
  
 | 
#ifndef CONFIG_MMC_USE_PRE_CONFIG 
 | 
static int sd_send_op_cond(struct mmc *mmc) 
 | 
{ 
 | 
    int timeout = 1000; 
 | 
    int err; 
 | 
    struct mmc_cmd cmd; 
 | 
  
 | 
    while (1) { 
 | 
        cmd.cmdidx = MMC_CMD_APP_CMD; 
 | 
        cmd.resp_type = MMC_RSP_R1; 
 | 
        cmd.cmdarg = 0; 
 | 
  
 | 
        err = mmc_send_cmd(mmc, &cmd, NULL); 
 | 
  
 | 
        if (err) 
 | 
            return err; 
 | 
  
 | 
        cmd.cmdidx = SD_CMD_APP_SEND_OP_COND; 
 | 
        cmd.resp_type = MMC_RSP_R3; 
 | 
  
 | 
        /* 
 | 
         * Most cards do not answer if some reserved bits 
 | 
         * in the ocr are set. However, Some controller 
 | 
         * can set bit 7 (reserved for low voltages), but 
 | 
         * how to manage low voltages SD card is not yet 
 | 
         * specified. 
 | 
         */ 
 | 
        cmd.cmdarg = mmc_host_is_spi(mmc) ? 0 : 
 | 
            (mmc->cfg->voltages & 0xff8000); 
 | 
  
 | 
        if (mmc->version == SD_VERSION_2) 
 | 
            cmd.cmdarg |= OCR_HCS; 
 | 
  
 | 
        err = mmc_send_cmd(mmc, &cmd, NULL); 
 | 
  
 | 
        if (err) 
 | 
            return err; 
 | 
  
 | 
        if (cmd.response[0] & OCR_BUSY) 
 | 
            break; 
 | 
  
 | 
        if (timeout-- <= 0) 
 | 
            return -EOPNOTSUPP; 
 | 
  
 | 
        udelay(1000); 
 | 
    } 
 | 
  
 | 
    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); 
 | 
    mmc->rca = 0; 
 | 
  
 | 
    return 0; 
 | 
} 
 | 
#endif 
 | 
  
 | 
static int mmc_send_op_cond_iter(struct mmc *mmc, int use_arg) 
 | 
{ 
 | 
    struct mmc_cmd cmd; 
 | 
    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 = OCR_HCS | 
 | 
            (mmc->cfg->voltages & 
 | 
            (mmc->ocr & OCR_VOLTAGE_MASK)) | 
 | 
            (mmc->ocr & OCR_ACCESS_MODE); 
 | 
  
 | 
    err = mmc_send_cmd(mmc, &cmd, NULL); 
 | 
    if (err) 
 | 
        return err; 
 | 
    mmc->ocr = cmd.response[0]; 
 | 
    return 0; 
 | 
} 
 | 
  
 | 
#ifndef CONFIG_MMC_USE_PRE_CONFIG 
 | 
static int mmc_send_op_cond(struct mmc *mmc) 
 | 
{ 
 | 
    int err, i; 
 | 
  
 | 
    /* Some cards seem to need this */ 
 | 
    mmc_go_idle(mmc); 
 | 
  
 | 
     /* Asking to the card its capabilities */ 
 | 
    for (i = 0; i < 2; i++) { 
 | 
        err = mmc_send_op_cond_iter(mmc, i != 0); 
 | 
        if (err) 
 | 
            return err; 
 | 
  
 | 
        /* exit if not busy (flag seems to be inverted) */ 
 | 
        if (mmc->ocr & OCR_BUSY) 
 | 
            break; 
 | 
    } 
 | 
    mmc->op_cond_pending = 1; 
 | 
    return 0; 
 | 
} 
 | 
#endif 
 | 
static int mmc_complete_op_cond(struct mmc *mmc) 
 | 
{ 
 | 
    struct mmc_cmd cmd; 
 | 
    int timeout = 1000; 
 | 
    uint start; 
 | 
    int err; 
 | 
  
 | 
    mmc->op_cond_pending = 0; 
 | 
    if (!(mmc->ocr & OCR_BUSY)) { 
 | 
        /* Some cards seem to need this */ 
 | 
        mmc_go_idle(mmc); 
 | 
  
 | 
        start = get_timer(0); 
 | 
        while (1) { 
 | 
            err = mmc_send_op_cond_iter(mmc, 1); 
 | 
            if (err) 
 | 
                return err; 
 | 
            if (mmc->ocr & OCR_BUSY) 
 | 
                break; 
 | 
            if (get_timer(start) > timeout) 
 | 
                return -EOPNOTSUPP; 
 | 
            udelay(100); 
 | 
        } 
 | 
    } 
 | 
  
 | 
    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->version = MMC_VERSION_UNKNOWN; 
 | 
  
 | 
    mmc->high_capacity = ((mmc->ocr & OCR_HCS) == OCR_HCS); 
 | 
    mmc->rca = 1; 
 | 
  
 | 
    return 0; 
 | 
} 
 | 
  
 | 
  
 | 
static int mmc_send_ext_csd(struct mmc *mmc, u8 *ext_csd) 
 | 
{ 
 | 
    struct mmc_cmd cmd; 
 | 
    struct mmc_data data; 
 | 
    int err; 
 | 
  
 | 
#ifdef CONFIG_MMC_USE_PRE_CONFIG 
 | 
    static int initialized; 
 | 
    if (initialized) { 
 | 
        memcpy(ext_csd, mmc_ext_csd, 512); 
 | 
        return 0; 
 | 
    } 
 | 
  
 | 
    initialized = 1; 
 | 
#endif 
 | 
    /* Get the Card Status Register */ 
 | 
    cmd.cmdidx = MMC_CMD_SEND_EXT_CSD; 
 | 
    cmd.resp_type = MMC_RSP_R1; 
 | 
    cmd.cmdarg = 0; 
 | 
  
 | 
    data.dest = (char *)ext_csd; 
 | 
    data.blocks = 1; 
 | 
    data.blocksize = MMC_MAX_BLOCK_LEN; 
 | 
    data.flags = MMC_DATA_READ; 
 | 
  
 | 
    err = mmc_send_cmd(mmc, &cmd, &data); 
 | 
    memcpy(mmc_ext_csd, ext_csd, 512); 
 | 
#if defined(CONFIG_MMC_USE_PRE_CONFIG) && defined(CONFIG_SPL_BUILD) 
 | 
    char *mmc_ecsd_base = NULL; 
 | 
    ulong mmc_ecsd; 
 | 
  
 | 
    mmc_ecsd = dev_read_u32_default(mmc->dev, "mmc-ecsd", 0); 
 | 
    mmc_ecsd_base = (char *)mmc_ecsd; 
 | 
    if (mmc_ecsd_base) { 
 | 
        memcpy(mmc_ecsd_base, ext_csd, 512); 
 | 
        *(unsigned int *)(mmc_ecsd_base + 512) = 0x55aa55aa; 
 | 
    } 
 | 
#endif 
 | 
    return err; 
 | 
} 
 | 
  
 | 
static int mmc_poll_for_busy(struct mmc *mmc, u8 send_status) 
 | 
{ 
 | 
    struct mmc_cmd cmd; 
 | 
    u8 busy = true; 
 | 
    uint start; 
 | 
    int ret; 
 | 
    int timeout = 1000; 
 | 
  
 | 
    cmd.cmdidx = MMC_CMD_SEND_STATUS; 
 | 
    cmd.resp_type = MMC_RSP_R1; 
 | 
    cmd.cmdarg = mmc->rca << 16; 
 | 
  
 | 
    start = get_timer(0); 
 | 
  
 | 
    if (!send_status && !mmc_can_card_busy(mmc)) { 
 | 
        mdelay(timeout); 
 | 
        return 0; 
 | 
    } 
 | 
  
 | 
    do { 
 | 
        if (!send_status) { 
 | 
            busy = mmc_card_busy(mmc); 
 | 
        } else { 
 | 
            ret = mmc_send_cmd(mmc, &cmd, NULL); 
 | 
  
 | 
            if (ret) 
 | 
                return ret; 
 | 
  
 | 
            if (cmd.response[0] & MMC_STATUS_SWITCH_ERROR) 
 | 
                return -EBADMSG; 
 | 
            busy = (cmd.response[0] & MMC_STATUS_CURR_STATE) == 
 | 
                MMC_STATE_PRG; 
 | 
        } 
 | 
  
 | 
        if (get_timer(start) > timeout && busy) 
 | 
            return -ETIMEDOUT; 
 | 
    } while (busy); 
 | 
  
 | 
    return 0; 
 | 
} 
 | 
  
 | 
static int __mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value, 
 | 
            u8 send_status) 
 | 
{ 
 | 
    struct mmc_cmd cmd; 
 | 
    int retries = 3; 
 | 
    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); 
 | 
  
 | 
    do { 
 | 
        ret = mmc_send_cmd(mmc, &cmd, NULL); 
 | 
  
 | 
        if (!ret) 
 | 
            return mmc_poll_for_busy(mmc, send_status); 
 | 
    } while (--retries > 0 && ret); 
 | 
  
 | 
    return ret; 
 | 
} 
 | 
  
 | 
int mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value) 
 | 
{ 
 | 
    return __mmc_switch(mmc, set, index, value, true); 
 | 
} 
 | 
  
 | 
static int mmc_select_bus_width(struct mmc *mmc) 
 | 
{ 
 | 
    u32 ext_csd_bits[] = { 
 | 
        EXT_CSD_BUS_WIDTH_8, 
 | 
        EXT_CSD_BUS_WIDTH_4, 
 | 
    }; 
 | 
    u32 bus_widths[] = { 
 | 
        MMC_BUS_WIDTH_8BIT, 
 | 
        MMC_BUS_WIDTH_4BIT, 
 | 
    }; 
 | 
    ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN); 
 | 
    ALLOC_CACHE_ALIGN_BUFFER(u8, test_csd, MMC_MAX_BLOCK_LEN); 
 | 
    u32 idx, bus_width = 0; 
 | 
    int err = 0; 
 | 
  
 | 
    if (mmc->version < MMC_VERSION_4 || 
 | 
        !(mmc->cfg->host_caps & (MMC_MODE_4BIT | MMC_MODE_8BIT))) 
 | 
        return 0; 
 | 
  
 | 
    err = mmc_send_ext_csd(mmc, ext_csd); 
 | 
  
 | 
    if (err) 
 | 
        return err; 
 | 
  
 | 
    idx = (mmc->cfg->host_caps & MMC_MODE_8BIT) ? 0 : 1; 
 | 
  
 | 
    /* 
 | 
     * Unlike SD, MMC cards dont have a configuration register to notify 
 | 
     * supported bus width. So bus test command should be run to identify 
 | 
     * the supported bus width or compare the ext csd values of current 
 | 
     * bus width and ext csd values of 1 bit mode read earlier. 
 | 
     */ 
 | 
    for (; idx < ARRAY_SIZE(bus_widths); idx++) { 
 | 
        /* 
 | 
         * Host is capable of 8bit transfer, then switch 
 | 
         * the device to work in 8bit transfer mode. If the 
 | 
         * mmc switch command returns error then switch to 
 | 
         * 4bit transfer mode. On success set the corresponding 
 | 
         * bus width on the host. 
 | 
         */ 
 | 
        err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, 
 | 
                 EXT_CSD_BUS_WIDTH, ext_csd_bits[idx]); 
 | 
        if (err) 
 | 
            continue; 
 | 
  
 | 
        bus_width = bus_widths[idx]; 
 | 
        mmc_set_bus_width(mmc, bus_width); 
 | 
  
 | 
        err = mmc_send_ext_csd(mmc, test_csd); 
 | 
  
 | 
        if (err) 
 | 
            continue; 
 | 
  
 | 
        /* Only compare read only fields */ 
 | 
        if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] == 
 | 
            test_csd[EXT_CSD_PARTITIONING_SUPPORT]) && 
 | 
            (ext_csd[EXT_CSD_HC_WP_GRP_SIZE] == 
 | 
            test_csd[EXT_CSD_HC_WP_GRP_SIZE]) && 
 | 
            (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)) { 
 | 
            err = bus_width; 
 | 
            break; 
 | 
        } else { 
 | 
            err = -EBADMSG; 
 | 
        } 
 | 
    } 
 | 
  
 | 
    return err; 
 | 
} 
 | 
  
 | 
#ifndef CONFIG_MMC_SIMPLE 
 | 
static const u8 tuning_blk_pattern_4bit[] = { 
 | 
    0xff, 0x0f, 0xff, 0x00, 0xff, 0xcc, 0xc3, 0xcc, 
 | 
    0xc3, 0x3c, 0xcc, 0xff, 0xfe, 0xff, 0xfe, 0xef, 
 | 
    0xff, 0xdf, 0xff, 0xdd, 0xff, 0xfb, 0xff, 0xfb, 
 | 
    0xbf, 0xff, 0x7f, 0xff, 0x77, 0xf7, 0xbd, 0xef, 
 | 
    0xff, 0xf0, 0xff, 0xf0, 0x0f, 0xfc, 0xcc, 0x3c, 
 | 
    0xcc, 0x33, 0xcc, 0xcf, 0xff, 0xef, 0xff, 0xee, 
 | 
    0xff, 0xfd, 0xff, 0xfd, 0xdf, 0xff, 0xbf, 0xff, 
 | 
    0xbb, 0xff, 0xf7, 0xff, 0xf7, 0x7f, 0x7b, 0xde, 
 | 
}; 
 | 
  
 | 
static const u8 tuning_blk_pattern_8bit[] = { 
 | 
    0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 
 | 
    0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, 0xcc, 
 | 
    0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, 0xff, 
 | 
    0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, 0xff, 
 | 
    0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, 0xdd, 
 | 
    0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb, 
 | 
    0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 0xff, 
 | 
    0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, 0xff, 
 | 
    0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 
 | 
    0x00, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, 
 | 
    0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, 
 | 
    0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, 
 | 
    0xff, 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, 
 | 
    0xdd, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 
 | 
    0xbb, 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 
 | 
    0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, 
 | 
}; 
 | 
  
 | 
int mmc_send_tuning(struct mmc *mmc, u32 opcode) 
 | 
{ 
 | 
    struct mmc_cmd cmd; 
 | 
    struct mmc_data data; 
 | 
    const u8 *tuning_block_pattern; 
 | 
    int size, err = 0; 
 | 
    u8 *data_buf; 
 | 
  
 | 
    if (mmc->bus_width == MMC_BUS_WIDTH_8BIT) { 
 | 
        tuning_block_pattern = tuning_blk_pattern_8bit; 
 | 
        size = sizeof(tuning_blk_pattern_8bit); 
 | 
    } else if (mmc->bus_width == MMC_BUS_WIDTH_4BIT) { 
 | 
        tuning_block_pattern = tuning_blk_pattern_4bit; 
 | 
        size = sizeof(tuning_blk_pattern_4bit); 
 | 
    } else { 
 | 
        return -EINVAL; 
 | 
    } 
 | 
  
 | 
    data_buf = calloc(1, size); 
 | 
    if (!data_buf) 
 | 
        return -ENOMEM; 
 | 
  
 | 
    cmd.cmdidx = opcode; 
 | 
    cmd.resp_type = MMC_RSP_R1; 
 | 
    cmd.cmdarg = 0; 
 | 
  
 | 
    data.dest = (char *)data_buf; 
 | 
    data.blocksize = size; 
 | 
    data.blocks = 1; 
 | 
    data.flags = MMC_DATA_READ; 
 | 
  
 | 
    err = mmc_send_cmd(mmc, &cmd, &data); 
 | 
    if (err) 
 | 
        goto out; 
 | 
  
 | 
    if (memcmp(data_buf, tuning_block_pattern, size)) 
 | 
        err = -EIO; 
 | 
out: 
 | 
    free(data_buf); 
 | 
    return err; 
 | 
} 
 | 
  
 | 
static int mmc_execute_tuning(struct mmc *mmc) 
 | 
{ 
 | 
#ifdef CONFIG_DM_MMC 
 | 
    struct dm_mmc_ops *ops = mmc_get_ops(mmc->dev); 
 | 
#endif 
 | 
    u32 opcode; 
 | 
  
 | 
    if (IS_SD(mmc)) 
 | 
        opcode = MMC_SEND_TUNING_BLOCK; 
 | 
    else 
 | 
        opcode = MMC_SEND_TUNING_BLOCK_HS200; 
 | 
  
 | 
#ifndef CONFIG_DM_MMC 
 | 
    if (mmc->cfg->ops->execute_tuning) { 
 | 
        return mmc->cfg->ops->execute_tuning(mmc, opcode); 
 | 
#else 
 | 
    if (ops->execute_tuning) { 
 | 
        return ops->execute_tuning(mmc->dev, opcode); 
 | 
#endif 
 | 
    } else { 
 | 
        debug("Tuning feature required for HS200 mode.\n"); 
 | 
        return -EIO; 
 | 
    } 
 | 
} 
 | 
  
 | 
static int mmc_hs200_tuning(struct mmc *mmc) 
 | 
{ 
 | 
    return mmc_execute_tuning(mmc); 
 | 
} 
 | 
  
 | 
#else 
 | 
int mmc_send_tuning(struct mmc *mmc, u32 opcode) { return 0; } 
 | 
int mmc_execute_tuning(struct mmc *mmc) { return 0; } 
 | 
static int mmc_hs200_tuning(struct mmc *mmc) { return 0; } 
 | 
#endif 
 | 
  
 | 
static int mmc_select_hs(struct mmc *mmc) 
 | 
{ 
 | 
    int ret; 
 | 
  
 | 
    ret = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, 
 | 
             EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS); 
 | 
  
 | 
    if (!ret) 
 | 
        mmc_set_timing(mmc, MMC_TIMING_MMC_HS); 
 | 
  
 | 
    return ret; 
 | 
} 
 | 
  
 | 
static int mmc_select_hs_ddr(struct mmc *mmc) 
 | 
{ 
 | 
    u32 ext_csd_bits; 
 | 
    int err = 0; 
 | 
  
 | 
    if (mmc->bus_width == MMC_BUS_WIDTH_1BIT) 
 | 
        return 0; 
 | 
  
 | 
    ext_csd_bits = (mmc->bus_width == MMC_BUS_WIDTH_8BIT) ? 
 | 
            EXT_CSD_DDR_BUS_WIDTH_8 : EXT_CSD_DDR_BUS_WIDTH_4; 
 | 
  
 | 
    err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, 
 | 
             EXT_CSD_BUS_WIDTH, ext_csd_bits); 
 | 
    if (err) 
 | 
        return err; 
 | 
  
 | 
    mmc_set_timing(mmc, MMC_TIMING_MMC_DDR52); 
 | 
  
 | 
    return 0; 
 | 
} 
 | 
  
 | 
#ifndef CONFIG_MMC_SIMPLE 
 | 
static int mmc_select_hs200(struct mmc *mmc) 
 | 
{ 
 | 
    int ret; 
 | 
  
 | 
    /* 
 | 
     * Set the bus width(4 or 8) with host's support and 
 | 
     * switch to HS200 mode if bus width is set successfully. 
 | 
     */ 
 | 
    ret = mmc_select_bus_width(mmc); 
 | 
  
 | 
    if (ret > 0) { 
 | 
        ret = __mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, 
 | 
                   EXT_CSD_HS_TIMING, 
 | 
                   EXT_CSD_TIMING_HS200, false); 
 | 
  
 | 
        if (ret) 
 | 
            return ret; 
 | 
  
 | 
        mmc_set_timing(mmc, MMC_TIMING_MMC_HS200); 
 | 
    } 
 | 
  
 | 
    return ret; 
 | 
} 
 | 
  
 | 
static int mmc_select_hs400(struct mmc *mmc) 
 | 
{ 
 | 
    int ret; 
 | 
  
 | 
    /* Reduce frequency to HS frequency */ 
 | 
    mmc_set_clock(mmc, MMC_HIGH_52_MAX_DTR); 
 | 
  
 | 
    /* Switch card to HS mode */ 
 | 
    ret = __mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, 
 | 
               EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS, false); 
 | 
    if (ret) 
 | 
        return ret; 
 | 
  
 | 
    /* Set host controller to HS timing */ 
 | 
    mmc_set_timing(mmc, MMC_TIMING_MMC_HS); 
 | 
  
 | 
    ret = mmc_send_status(mmc, 1000); 
 | 
    if (ret) 
 | 
        return ret; 
 | 
  
 | 
    /* Switch card to DDR */ 
 | 
    ret = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, 
 | 
             EXT_CSD_BUS_WIDTH, 
 | 
             EXT_CSD_DDR_BUS_WIDTH_8); 
 | 
    if (ret) 
 | 
        return ret; 
 | 
  
 | 
    /* Switch card to HS400 */ 
 | 
    ret = __mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, 
 | 
               EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS400, false); 
 | 
    if (ret) 
 | 
        return ret; 
 | 
  
 | 
    /* Set host controller to HS400 timing and frequency */ 
 | 
    mmc_set_timing(mmc, MMC_TIMING_MMC_HS400); 
 | 
  
 | 
    return ret; 
 | 
} 
 | 
#else 
 | 
static int mmc_select_hs200(struct mmc *mmc) { return 0; } 
 | 
static int mmc_select_hs400(struct mmc *mmc) { return 0; } 
 | 
#endif 
 | 
  
 | 
static u32 mmc_select_card_type(struct mmc *mmc, u8 *ext_csd) 
 | 
{ 
 | 
    u8 card_type; 
 | 
    u32 host_caps, avail_type = 0; 
 | 
  
 | 
    card_type = ext_csd[EXT_CSD_CARD_TYPE]; 
 | 
    host_caps = mmc->cfg->host_caps; 
 | 
  
 | 
    if ((host_caps & MMC_MODE_HS) && 
 | 
        (card_type & EXT_CSD_CARD_TYPE_26)) 
 | 
        avail_type |= EXT_CSD_CARD_TYPE_26; 
 | 
  
 | 
    if ((host_caps & MMC_MODE_HS) && 
 | 
        (card_type & EXT_CSD_CARD_TYPE_52)) 
 | 
        avail_type |= EXT_CSD_CARD_TYPE_52; 
 | 
  
 | 
    /* 
 | 
     * For the moment, u-boot doesn't support signal voltage 
 | 
     * switch, therefor we assume that host support ddr52 
 | 
     * at 1.8v or 3.3v I/O(1.2v I/O not supported, hs200 and 
 | 
     * hs400 are the same). 
 | 
     */ 
 | 
    if ((host_caps & MMC_MODE_DDR_52MHz) && 
 | 
        (card_type & EXT_CSD_CARD_TYPE_DDR_1_8V)) 
 | 
        avail_type |= EXT_CSD_CARD_TYPE_DDR_1_8V; 
 | 
  
 | 
    if ((host_caps & MMC_MODE_HS200) && 
 | 
        (card_type & EXT_CSD_CARD_TYPE_HS200_1_8V)) 
 | 
        avail_type |= EXT_CSD_CARD_TYPE_HS200_1_8V; 
 | 
  
 | 
    /* 
 | 
     * If host can support HS400, it means that host can also 
 | 
     * support HS200. 
 | 
     */ 
 | 
    if ((host_caps & MMC_MODE_HS400) && 
 | 
        (host_caps & MMC_MODE_8BIT) && 
 | 
        (card_type & EXT_CSD_CARD_TYPE_HS400_1_8V)) 
 | 
        avail_type |= EXT_CSD_CARD_TYPE_HS200_1_8V | 
 | 
                EXT_CSD_CARD_TYPE_HS400_1_8V; 
 | 
  
 | 
    if ((host_caps & MMC_MODE_HS400ES) && 
 | 
        (host_caps & MMC_MODE_8BIT) && 
 | 
        ext_csd[EXT_CSD_STROBE_SUPPORT] && 
 | 
        (avail_type & EXT_CSD_CARD_TYPE_HS400_1_8V)) 
 | 
        avail_type |= EXT_CSD_CARD_TYPE_HS200_1_8V | 
 | 
                EXT_CSD_CARD_TYPE_HS400_1_8V | 
 | 
                EXT_CSD_CARD_TYPE_HS400ES; 
 | 
  
 | 
    return avail_type; 
 | 
} 
 | 
  
 | 
static void mmc_set_bus_speed(struct mmc *mmc, u8 avail_type) 
 | 
{ 
 | 
    int clock = 0; 
 | 
  
 | 
    if (mmc_card_hs(mmc)) 
 | 
        clock = (avail_type & EXT_CSD_CARD_TYPE_52) ? 
 | 
            MMC_HIGH_52_MAX_DTR : MMC_HIGH_26_MAX_DTR; 
 | 
    else if (mmc_card_hs200(mmc) || 
 | 
         mmc_card_hs400(mmc) || 
 | 
         mmc_card_hs400es(mmc)) 
 | 
        clock = MMC_HS200_MAX_DTR; 
 | 
  
 | 
    mmc_set_clock(mmc, clock); 
 | 
} 
 | 
  
 | 
static int mmc_change_freq(struct mmc *mmc) 
 | 
{ 
 | 
    ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN); 
 | 
    u32 avail_type; 
 | 
    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 | MMC_MODE_8BIT; 
 | 
  
 | 
    err = mmc_send_ext_csd(mmc, ext_csd); 
 | 
  
 | 
    if (err) 
 | 
        return err; 
 | 
  
 | 
    avail_type = mmc_select_card_type(mmc, ext_csd); 
 | 
  
 | 
    if (avail_type & EXT_CSD_CARD_TYPE_HS200) 
 | 
        err = mmc_select_hs200(mmc); 
 | 
    else if (avail_type & EXT_CSD_CARD_TYPE_HS) 
 | 
        err = mmc_select_hs(mmc); 
 | 
    else 
 | 
        err = -EINVAL; 
 | 
  
 | 
    if (err) 
 | 
        return err; 
 | 
  
 | 
    mmc_set_bus_speed(mmc, avail_type); 
 | 
  
 | 
    if (mmc_card_hs200(mmc)) { 
 | 
        err = mmc_hs200_tuning(mmc); 
 | 
        if (avail_type & EXT_CSD_CARD_TYPE_HS400 && 
 | 
            mmc->bus_width == MMC_BUS_WIDTH_8BIT) { 
 | 
            err = mmc_select_hs400(mmc); 
 | 
            mmc_set_bus_speed(mmc, avail_type); 
 | 
        } 
 | 
    } else if (!mmc_card_hs400es(mmc)) { 
 | 
        err = mmc_select_bus_width(mmc) > 0 ? 0 : err; 
 | 
        if (!err && avail_type & EXT_CSD_CARD_TYPE_DDR_52) 
 | 
            err = mmc_select_hs_ddr(mmc); 
 | 
    } 
 | 
  
 | 
    return err; 
 | 
} 
 | 
  
 | 
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_get_blk_desc(mmc)->lba = lldiv(mmc->capacity, mmc->read_bl_len); 
 | 
  
 | 
    return 0; 
 | 
} 
 | 
  
 | 
int mmc_switch_part(struct mmc *mmc, unsigned int part_num) 
 | 
{ 
 | 
    int ret; 
 | 
  
 | 
    ret = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_PART_CONF, 
 | 
             (mmc->part_config & ~PART_ACCESS_MASK) 
 | 
             | (part_num & PART_ACCESS_MASK)); 
 | 
  
 | 
    /* 
 | 
     * Set the capacity if the switch succeeded or was intended 
 | 
     * to return to representing the raw device. 
 | 
     */ 
 | 
    if ((ret == 0) || ((ret == -ENODEV) && (part_num == 0))) { 
 | 
        ret = mmc_set_capacity(mmc, part_num); 
 | 
        mmc_get_blk_desc(mmc)->hwpart = part_num; 
 | 
    } 
 | 
  
 | 
    return ret; 
 | 
} 
 | 
  
 | 
int mmc_hwpart_config(struct mmc *mmc, 
 | 
              const struct mmc_hwpart_conf *conf, 
 | 
              enum mmc_hwpart_conf_mode mode) 
 | 
{ 
 | 
    u8 part_attrs = 0; 
 | 
    u32 enh_size_mult; 
 | 
    u32 enh_start_addr; 
 | 
    u32 gp_size_mult[4]; 
 | 
    u32 max_enh_size_mult; 
 | 
    u32 tot_enh_size_mult = 0; 
 | 
    u8 wr_rel_set; 
 | 
    int i, pidx, err; 
 | 
    ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN); 
 | 
  
 | 
    if (mode < MMC_HWPART_CONF_CHECK || mode > MMC_HWPART_CONF_COMPLETE) 
 | 
        return -EINVAL; 
 | 
  
 | 
    if (IS_SD(mmc) || (mmc->version < MMC_VERSION_4_41)) { 
 | 
        printf("eMMC >= 4.4 required for enhanced user data area\n"); 
 | 
        return -EMEDIUMTYPE; 
 | 
    } 
 | 
  
 | 
    if (!(mmc->part_support & PART_SUPPORT)) { 
 | 
        printf("Card does not support partitioning\n"); 
 | 
        return -EMEDIUMTYPE; 
 | 
    } 
 | 
  
 | 
    if (!mmc->hc_wp_grp_size) { 
 | 
        printf("Card does not define HC WP group size\n"); 
 | 
        return -EMEDIUMTYPE; 
 | 
    } 
 | 
  
 | 
    /* check partition alignment and total enhanced size */ 
 | 
    if (conf->user.enh_size) { 
 | 
        if (conf->user.enh_size % mmc->hc_wp_grp_size || 
 | 
            conf->user.enh_start % mmc->hc_wp_grp_size) { 
 | 
            printf("User data enhanced area not HC WP group " 
 | 
                   "size aligned\n"); 
 | 
            return -EINVAL; 
 | 
        } 
 | 
        part_attrs |= EXT_CSD_ENH_USR; 
 | 
        enh_size_mult = conf->user.enh_size / mmc->hc_wp_grp_size; 
 | 
        if (mmc->high_capacity) { 
 | 
            enh_start_addr = conf->user.enh_start; 
 | 
        } else { 
 | 
            enh_start_addr = (conf->user.enh_start << 9); 
 | 
        } 
 | 
    } else { 
 | 
        enh_size_mult = 0; 
 | 
        enh_start_addr = 0; 
 | 
    } 
 | 
    tot_enh_size_mult += enh_size_mult; 
 | 
  
 | 
    for (pidx = 0; pidx < 4; pidx++) { 
 | 
        if (conf->gp_part[pidx].size % mmc->hc_wp_grp_size) { 
 | 
            printf("GP%i partition not HC WP group size " 
 | 
                   "aligned\n", pidx+1); 
 | 
            return -EINVAL; 
 | 
        } 
 | 
        gp_size_mult[pidx] = conf->gp_part[pidx].size / mmc->hc_wp_grp_size; 
 | 
        if (conf->gp_part[pidx].size && conf->gp_part[pidx].enhanced) { 
 | 
            part_attrs |= EXT_CSD_ENH_GP(pidx); 
 | 
            tot_enh_size_mult += gp_size_mult[pidx]; 
 | 
        } 
 | 
    } 
 | 
  
 | 
    if (part_attrs && ! (mmc->part_support & ENHNCD_SUPPORT)) { 
 | 
        printf("Card does not support enhanced attribute\n"); 
 | 
        return -EMEDIUMTYPE; 
 | 
    } 
 | 
  
 | 
    err = mmc_send_ext_csd(mmc, ext_csd); 
 | 
    if (err) 
 | 
        return err; 
 | 
  
 | 
    max_enh_size_mult = 
 | 
        (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT+2] << 16) + 
 | 
        (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT+1] << 8) + 
 | 
        ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT]; 
 | 
    if (tot_enh_size_mult > max_enh_size_mult) { 
 | 
        printf("Total enhanced size exceeds maximum (%u > %u)\n", 
 | 
               tot_enh_size_mult, max_enh_size_mult); 
 | 
        return -EMEDIUMTYPE; 
 | 
    } 
 | 
  
 | 
    /* The default value of EXT_CSD_WR_REL_SET is device 
 | 
     * dependent, the values can only be changed if the 
 | 
     * EXT_CSD_HS_CTRL_REL bit is set. The values can be 
 | 
     * changed only once and before partitioning is completed. */ 
 | 
    wr_rel_set = ext_csd[EXT_CSD_WR_REL_SET]; 
 | 
    if (conf->user.wr_rel_change) { 
 | 
        if (conf->user.wr_rel_set) 
 | 
            wr_rel_set |= EXT_CSD_WR_DATA_REL_USR; 
 | 
        else 
 | 
            wr_rel_set &= ~EXT_CSD_WR_DATA_REL_USR; 
 | 
    } 
 | 
    for (pidx = 0; pidx < 4; pidx++) { 
 | 
        if (conf->gp_part[pidx].wr_rel_change) { 
 | 
            if (conf->gp_part[pidx].wr_rel_set) 
 | 
                wr_rel_set |= EXT_CSD_WR_DATA_REL_GP(pidx); 
 | 
            else 
 | 
                wr_rel_set &= ~EXT_CSD_WR_DATA_REL_GP(pidx); 
 | 
        } 
 | 
    } 
 | 
  
 | 
    if (wr_rel_set != ext_csd[EXT_CSD_WR_REL_SET] && 
 | 
        !(ext_csd[EXT_CSD_WR_REL_PARAM] & EXT_CSD_HS_CTRL_REL)) { 
 | 
        puts("Card does not support host controlled partition write " 
 | 
             "reliability settings\n"); 
 | 
        return -EMEDIUMTYPE; 
 | 
    } 
 | 
  
 | 
    if (ext_csd[EXT_CSD_PARTITION_SETTING] & 
 | 
        EXT_CSD_PARTITION_SETTING_COMPLETED) { 
 | 
        printf("Card already partitioned\n"); 
 | 
        return -EPERM; 
 | 
    } 
 | 
  
 | 
    if (mode == MMC_HWPART_CONF_CHECK) 
 | 
        return 0; 
 | 
  
 | 
    /* Partitioning requires high-capacity size definitions */ 
 | 
    if (!(ext_csd[EXT_CSD_ERASE_GROUP_DEF] & 0x01)) { 
 | 
        err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, 
 | 
                 EXT_CSD_ERASE_GROUP_DEF, 1); 
 | 
  
 | 
        if (err) 
 | 
            return err; 
 | 
  
 | 
        ext_csd[EXT_CSD_ERASE_GROUP_DEF] = 1; 
 | 
  
 | 
        /* update erase group size to be high-capacity */ 
 | 
        mmc->erase_grp_size = 
 | 
            ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] * 1024; 
 | 
  
 | 
    } 
 | 
  
 | 
    /* all OK, write the configuration */ 
 | 
    for (i = 0; i < 4; i++) { 
 | 
        err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, 
 | 
                 EXT_CSD_ENH_START_ADDR+i, 
 | 
                 (enh_start_addr >> (i*8)) & 0xFF); 
 | 
        if (err) 
 | 
            return err; 
 | 
    } 
 | 
    for (i = 0; i < 3; i++) { 
 | 
        err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, 
 | 
                 EXT_CSD_ENH_SIZE_MULT+i, 
 | 
                 (enh_size_mult >> (i*8)) & 0xFF); 
 | 
        if (err) 
 | 
            return err; 
 | 
    } 
 | 
    for (pidx = 0; pidx < 4; pidx++) { 
 | 
        for (i = 0; i < 3; i++) { 
 | 
            err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, 
 | 
                     EXT_CSD_GP_SIZE_MULT+pidx*3+i, 
 | 
                     (gp_size_mult[pidx] >> (i*8)) & 0xFF); 
 | 
            if (err) 
 | 
                return err; 
 | 
        } 
 | 
    } 
 | 
    err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, 
 | 
             EXT_CSD_PARTITIONS_ATTRIBUTE, part_attrs); 
 | 
    if (err) 
 | 
        return err; 
 | 
  
 | 
    if (mode == MMC_HWPART_CONF_SET) 
 | 
        return 0; 
 | 
  
 | 
    /* The WR_REL_SET is a write-once register but shall be 
 | 
     * written before setting PART_SETTING_COMPLETED. As it is 
 | 
     * write-once we can only write it when completing the 
 | 
     * partitioning. */ 
 | 
    if (wr_rel_set != ext_csd[EXT_CSD_WR_REL_SET]) { 
 | 
        err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, 
 | 
                 EXT_CSD_WR_REL_SET, wr_rel_set); 
 | 
        if (err) 
 | 
            return err; 
 | 
    } 
 | 
  
 | 
    /* Setting PART_SETTING_COMPLETED confirms the partition 
 | 
     * configuration but it only becomes effective after power 
 | 
     * cycle, so we do not adjust the partition related settings 
 | 
     * in the mmc struct. */ 
 | 
  
 | 
    err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, 
 | 
             EXT_CSD_PARTITION_SETTING, 
 | 
             EXT_CSD_PARTITION_SETTING_COMPLETED); 
 | 
    if (err) 
 | 
        return err; 
 | 
  
 | 
    return 0; 
 | 
} 
 | 
  
 | 
#if !CONFIG_IS_ENABLED(DM_MMC) 
 | 
int mmc_getcd(struct mmc *mmc) 
 | 
{ 
 | 
    int cd; 
 | 
  
 | 
    cd = board_mmc_getcd(mmc); 
 | 
  
 | 
    if (cd < 0) { 
 | 
        if (mmc->cfg->ops->getcd) 
 | 
            cd = mmc->cfg->ops->getcd(mmc); 
 | 
        else 
 | 
            cd = 1; 
 | 
    } 
 | 
  
 | 
    return cd; 
 | 
} 
 | 
#endif 
 | 
  
 | 
static int sd_switch(struct mmc *mmc, int mode, int group, u8 value, u8 *resp) 
 | 
{ 
 | 
    struct mmc_cmd cmd; 
 | 
    struct mmc_data data; 
 | 
  
 | 
    /* Switch the frequency */ 
 | 
    cmd.cmdidx = SD_CMD_SWITCH_FUNC; 
 | 
    cmd.resp_type = MMC_RSP_R1; 
 | 
    cmd.cmdarg = (mode << 31) | 0xffffff; 
 | 
    cmd.cmdarg &= ~(0xf << (group * 4)); 
 | 
    cmd.cmdarg |= value << (group * 4); 
 | 
  
 | 
    data.dest = (char *)resp; 
 | 
    data.blocksize = 64; 
 | 
    data.blocks = 1; 
 | 
    data.flags = MMC_DATA_READ; 
 | 
  
 | 
    return mmc_send_cmd(mmc, &cmd, &data); 
 | 
} 
 | 
  
 | 
  
 | 
static int sd_change_freq(struct mmc *mmc) 
 | 
{ 
 | 
    int err; 
 | 
    struct mmc_cmd cmd; 
 | 
    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; 
 | 
  
 | 
    err = mmc_send_cmd(mmc, &cmd, NULL); 
 | 
  
 | 
    if (err) 
 | 
        return err; 
 | 
  
 | 
    cmd.cmdidx = SD_CMD_APP_SEND_SCR; 
 | 
    cmd.resp_type = MMC_RSP_R1; 
 | 
    cmd.cmdarg = 0; 
 | 
  
 | 
    timeout = 3; 
 | 
  
 | 
retry_scr: 
 | 
    data.dest = (char *)scr; 
 | 
    data.blocksize = 8; 
 | 
    data.blocks = 1; 
 | 
    data.flags = MMC_DATA_READ; 
 | 
  
 | 
    err = mmc_send_cmd(mmc, &cmd, &data); 
 | 
  
 | 
    if (err) { 
 | 
        if (timeout--) 
 | 
            goto retry_scr; 
 | 
  
 | 
        return err; 
 | 
    } 
 | 
  
 | 
    mmc->scr[0] = __be32_to_cpu(scr[0]); 
 | 
    mmc->scr[1] = __be32_to_cpu(scr[1]); 
 | 
  
 | 
    switch ((mmc->scr[0] >> 24) & 0xf) { 
 | 
    case 0: 
 | 
        mmc->version = SD_VERSION_1_0; 
 | 
        break; 
 | 
    case 1: 
 | 
        mmc->version = SD_VERSION_1_10; 
 | 
        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; 
 | 
        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; 
 | 
  
 | 
    timeout = 4; 
 | 
    while (timeout--) { 
 | 
        err = sd_switch(mmc, SD_SWITCH_CHECK, 0, 1, 
 | 
                (u8 *)switch_status); 
 | 
  
 | 
        if (err) 
 | 
            return err; 
 | 
  
 | 
        /* The high-speed function is busy.  Try again */ 
 | 
        if (!(__be32_to_cpu(switch_status[7]) & SD_HIGHSPEED_BUSY)) 
 | 
            break; 
 | 
    } 
 | 
  
 | 
    /* If high-speed isn't supported, we return */ 
 | 
    if (!(__be32_to_cpu(switch_status[3]) & SD_HIGHSPEED_SUPPORTED)) 
 | 
        return 0; 
 | 
  
 | 
    /* 
 | 
     * 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->cfg->host_caps & MMC_MODE_HS_52MHz) && 
 | 
        (mmc->cfg->host_caps & MMC_MODE_HS))) 
 | 
        return 0; 
 | 
  
 | 
    err = sd_switch(mmc, SD_SWITCH_SWITCH, 0, 1, (u8 *)switch_status); 
 | 
  
 | 
    if (err) 
 | 
        return err; 
 | 
  
 | 
    if ((__be32_to_cpu(switch_status[4]) & 0x0f000000) == 0x01000000) 
 | 
        mmc->card_caps |= MMC_MODE_HS; 
 | 
  
 | 
    return 0; 
 | 
} 
 | 
  
 | 
static int sd_read_ssr(struct mmc *mmc) 
 | 
{ 
 | 
    int err, i; 
 | 
    struct mmc_cmd cmd; 
 | 
    ALLOC_CACHE_ALIGN_BUFFER(uint, ssr, 16); 
 | 
    struct mmc_data data; 
 | 
    int timeout = 3; 
 | 
    unsigned int au, eo, et, es; 
 | 
  
 | 
    cmd.cmdidx = MMC_CMD_APP_CMD; 
 | 
    cmd.resp_type = MMC_RSP_R1; 
 | 
    cmd.cmdarg = mmc->rca << 16; 
 | 
  
 | 
    err = mmc_send_cmd(mmc, &cmd, NULL); 
 | 
    if (err) 
 | 
        return err; 
 | 
  
 | 
    cmd.cmdidx = SD_CMD_APP_SD_STATUS; 
 | 
    cmd.resp_type = MMC_RSP_R1; 
 | 
    cmd.cmdarg = 0; 
 | 
  
 | 
retry_ssr: 
 | 
    data.dest = (char *)ssr; 
 | 
    data.blocksize = 64; 
 | 
    data.blocks = 1; 
 | 
    data.flags = MMC_DATA_READ; 
 | 
  
 | 
    err = mmc_send_cmd(mmc, &cmd, &data); 
 | 
    if (err) { 
 | 
        if (timeout--) 
 | 
            goto retry_ssr; 
 | 
  
 | 
        return err; 
 | 
    } 
 | 
  
 | 
    for (i = 0; i < 16; i++) 
 | 
        ssr[i] = be32_to_cpu(ssr[i]); 
 | 
  
 | 
    au = (ssr[2] >> 12) & 0xF; 
 | 
    if ((au <= 9) || (mmc->version == SD_VERSION_3)) { 
 | 
        mmc->ssr.au = sd_au_size[au]; 
 | 
        es = (ssr[3] >> 24) & 0xFF; 
 | 
        es |= (ssr[2] & 0xFF) << 8; 
 | 
        et = (ssr[3] >> 18) & 0x3F; 
 | 
        if (es && et) { 
 | 
            eo = (ssr[3] >> 16) & 0x3; 
 | 
            mmc->ssr.erase_timeout = (et * 1000) / es; 
 | 
            mmc->ssr.erase_offset = eo * 1000; 
 | 
        } 
 | 
    } else { 
 | 
        debug("Invalid Allocation Unit Size.\n"); 
 | 
    } 
 | 
  
 | 
    return 0; 
 | 
} 
 | 
  
 | 
/* frequency bases */ 
 | 
/* divided by 10 to be nice to platforms without floating point */ 
 | 
static const int fbase[] = { 
 | 
    10000, 
 | 
    100000, 
 | 
    1000000, 
 | 
    10000000, 
 | 
}; 
 | 
  
 | 
/* Multiplier values for TRAN_SPEED.  Multiplied by 10 to be nice 
 | 
 * to platforms without floating point. 
 | 
 */ 
 | 
static const u8 multipliers[] = { 
 | 
    0,    /* reserved */ 
 | 
    10, 
 | 
    12, 
 | 
    13, 
 | 
    15, 
 | 
    20, 
 | 
    25, 
 | 
    30, 
 | 
    35, 
 | 
    40, 
 | 
    45, 
 | 
    50, 
 | 
    55, 
 | 
    60, 
 | 
    70, 
 | 
    80, 
 | 
}; 
 | 
  
 | 
#if !CONFIG_IS_ENABLED(DM_MMC) 
 | 
static void mmc_set_ios(struct mmc *mmc) 
 | 
{ 
 | 
    if (mmc->cfg->ops->set_ios) 
 | 
        mmc->cfg->ops->set_ios(mmc); 
 | 
} 
 | 
  
 | 
static bool mmc_card_busy(struct mmc *mmc) 
 | 
{ 
 | 
    if (!mmc->cfg->ops->card_busy) 
 | 
        return -ENOSYS; 
 | 
  
 | 
    return mmc->cfg->ops->card_busy(mmc); 
 | 
} 
 | 
  
 | 
static bool mmc_can_card_busy(struct mmc *) 
 | 
{ 
 | 
    return !!mmc->cfg->ops->card_busy; 
 | 
} 
 | 
#endif 
 | 
  
 | 
static int mmc_startup(struct mmc *mmc) 
 | 
{ 
 | 
    int err, i; 
 | 
    uint mult, freq, tran_speed; 
 | 
    u64 cmult, csize, capacity; 
 | 
    struct mmc_cmd cmd; 
 | 
    ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN); 
 | 
    bool has_parts = false; 
 | 
    bool part_completed; 
 | 
    struct blk_desc *bdesc; 
 | 
  
 | 
#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 
 | 
#ifndef CONFIG_MMC_USE_PRE_CONFIG 
 | 
    /* Put the Card in Identify Mode */ 
 | 
    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; 
 | 
  
 | 
    err = mmc_send_cmd(mmc, &cmd, NULL); 
 | 
  
 | 
    if (err) 
 | 
        return err; 
 | 
  
 | 
    memcpy(mmc->cid, cmd.response, 16); 
 | 
  
 | 
    /* 
 | 
     * For MMC cards, set the Relative Address. 
 | 
     * For SD cards, get the Relatvie Address. 
 | 
     * This also puts the cards into Standby State 
 | 
     */ 
 | 
    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); 
 | 
  
 | 
        if (err) 
 | 
            return err; 
 | 
  
 | 
        if (IS_SD(mmc)) 
 | 
            mmc->rca = (cmd.response[0] >> 16) & 0xffff; 
 | 
    } 
 | 
#endif 
 | 
    /* Get the Card-Specific Data */ 
 | 
    cmd.cmdidx = MMC_CMD_SEND_CSD; 
 | 
    cmd.resp_type = MMC_RSP_R2; 
 | 
    cmd.cmdarg = mmc->rca << 16; 
 | 
  
 | 
    err = mmc_send_cmd(mmc, &cmd, NULL); 
 | 
  
 | 
    if (err) 
 | 
        return err; 
 | 
  
 | 
    mmc->csd[0] = cmd.response[0]; 
 | 
    mmc->csd[1] = cmd.response[1]; 
 | 
    mmc->csd[2] = cmd.response[2]; 
 | 
    mmc->csd[3] = cmd.response[3]; 
 | 
  
 | 
    if (mmc->version == MMC_VERSION_UNKNOWN) { 
 | 
        int version = (cmd.response[0] >> 26) & 0xf; 
 | 
  
 | 
        switch (version) { 
 | 
        case 0: 
 | 
            mmc->version = MMC_VERSION_1_2; 
 | 
            break; 
 | 
        case 1: 
 | 
            mmc->version = MMC_VERSION_1_4; 
 | 
            break; 
 | 
        case 2: 
 | 
            mmc->version = MMC_VERSION_2_2; 
 | 
            break; 
 | 
        case 3: 
 | 
            mmc->version = MMC_VERSION_3; 
 | 
            break; 
 | 
        case 4: 
 | 
            mmc->version = MMC_VERSION_4; 
 | 
            break; 
 | 
        default: 
 | 
            mmc->version = MMC_VERSION_1_2; 
 | 
            break; 
 | 
        } 
 | 
    } 
 | 
  
 | 
    /* divide frequency by 10, since the mults are 10x bigger */ 
 | 
    freq = fbase[(cmd.response[0] & 0x7)]; 
 | 
    mult = multipliers[((cmd.response[0] >> 3) & 0xf)]; 
 | 
  
 | 
    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)) 
 | 
        mmc->write_bl_len = mmc->read_bl_len; 
 | 
    else 
 | 
        mmc->write_bl_len = 1 << ((cmd.response[3] >> 22) & 0xf); 
 | 
  
 | 
    if (mmc->high_capacity) { 
 | 
        csize = (mmc->csd[1] & 0x3f) << 16 
 | 
            | (mmc->csd[2] & 0xffff0000) >> 16; 
 | 
        cmult = 8; 
 | 
    } else { 
 | 
        csize = (mmc->csd[1] & 0x3ff) << 2 
 | 
            | (mmc->csd[2] & 0xc0000000) >> 30; 
 | 
        cmult = (mmc->csd[2] & 0x00038000) >> 15; 
 | 
    } 
 | 
  
 | 
    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 */ 
 | 
    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; 
 | 
    } 
 | 
  
 | 
    /* 
 | 
     * 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) 
 | 
            return err; 
 | 
        if (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; 
 | 
        case 7: 
 | 
            mmc->version = MMC_VERSION_5_0; 
 | 
            break; 
 | 
        case 8: 
 | 
            mmc->version = MMC_VERSION_5_1; 
 | 
            break; 
 | 
        } 
 | 
  
 | 
        /* The partition data may be non-zero but it is only 
 | 
         * effective if PARTITION_SETTING_COMPLETED is set in 
 | 
         * EXT_CSD, so ignore any data if this bit is not set, 
 | 
         * except for enabling the high-capacity group size 
 | 
         * definition (see below). */ 
 | 
        part_completed = !!(ext_csd[EXT_CSD_PARTITION_SETTING] & 
 | 
                    EXT_CSD_PARTITION_SETTING_COMPLETED); 
 | 
  
 | 
        /* store the partition info of emmc */ 
 | 
        mmc->part_support = ext_csd[EXT_CSD_PARTITIONING_SUPPORT]; 
 | 
        if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) || 
 | 
            ext_csd[EXT_CSD_BOOT_MULT]) 
 | 
            mmc->part_config = ext_csd[EXT_CSD_PART_CONF]; 
 | 
        if (part_completed && 
 | 
            (ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & ENHNCD_SUPPORT)) 
 | 
            mmc->part_attr = ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE]; 
 | 
        if (ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT] & EXT_CSD_SEC_GB_CL_EN) 
 | 
            mmc->esr.mmc_can_trim = 1; 
 | 
  
 | 
        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; 
 | 
            uint mult = (ext_csd[idx + 2] << 16) + 
 | 
                (ext_csd[idx + 1] << 8) + ext_csd[idx]; 
 | 
            if (mult) 
 | 
                has_parts = true; 
 | 
            if (!part_completed) 
 | 
                continue; 
 | 
            mmc->capacity_gp[i] = mult; 
 | 
            mmc->capacity_gp[i] *= 
 | 
                ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]; 
 | 
            mmc->capacity_gp[i] *= ext_csd[EXT_CSD_HC_WP_GRP_SIZE]; 
 | 
            mmc->capacity_gp[i] <<= 19; 
 | 
        } 
 | 
  
 | 
        if (part_completed) { 
 | 
            mmc->enh_user_size = 
 | 
                (ext_csd[EXT_CSD_ENH_SIZE_MULT+2] << 16) + 
 | 
                (ext_csd[EXT_CSD_ENH_SIZE_MULT+1] << 8) + 
 | 
                ext_csd[EXT_CSD_ENH_SIZE_MULT]; 
 | 
            mmc->enh_user_size *= ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]; 
 | 
            mmc->enh_user_size *= ext_csd[EXT_CSD_HC_WP_GRP_SIZE]; 
 | 
            mmc->enh_user_size <<= 19; 
 | 
            mmc->enh_user_start = 
 | 
                (ext_csd[EXT_CSD_ENH_START_ADDR+3] << 24) + 
 | 
                (ext_csd[EXT_CSD_ENH_START_ADDR+2] << 16) + 
 | 
                (ext_csd[EXT_CSD_ENH_START_ADDR+1] << 8) + 
 | 
                ext_csd[EXT_CSD_ENH_START_ADDR]; 
 | 
            if (mmc->high_capacity) 
 | 
                mmc->enh_user_start <<= 9; 
 | 
        } 
 | 
  
 | 
        /* 
 | 
         * 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 (part_completed) 
 | 
            has_parts = true; 
 | 
        if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) && 
 | 
            (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & PART_ENH_ATTRIB)) 
 | 
            has_parts = true; 
 | 
        if (has_parts) { 
 | 
            err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, 
 | 
                EXT_CSD_ERASE_GROUP_DEF, 1); 
 | 
  
 | 
            if (err) 
 | 
                return err; 
 | 
            else 
 | 
                ext_csd[EXT_CSD_ERASE_GROUP_DEF] = 1; 
 | 
        } 
 | 
  
 | 
        if (ext_csd[EXT_CSD_ERASE_GROUP_DEF] & 0x01) { 
 | 
            /* Read out group size from ext_csd */ 
 | 
            mmc->erase_grp_size = 
 | 
                ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] * 1024; 
 | 
            /* 
 | 
             * if high capacity and partition setting completed 
 | 
             * SEC_COUNT is valid even if it is smaller than 2 GiB 
 | 
             * JEDEC Standard JESD84-B45, 6.2.4 
 | 
             */ 
 | 
            if (mmc->high_capacity && part_completed) { 
 | 
                capacity = (ext_csd[EXT_CSD_SEC_CNT]) | 
 | 
                    (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; 
 | 
                mmc->capacity_user = capacity; 
 | 
            } 
 | 
        } 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); 
 | 
        } 
 | 
  
 | 
        mmc->hc_wp_grp_size = 1024 
 | 
            * ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] 
 | 
            * ext_csd[EXT_CSD_HC_WP_GRP_SIZE]; 
 | 
  
 | 
        mmc->wr_rel_set = ext_csd[EXT_CSD_WR_REL_SET]; 
 | 
    } 
 | 
  
 | 
    err = mmc_set_capacity(mmc, mmc_get_blk_desc(mmc)->hwpart); 
 | 
    if (err) 
 | 
        return err; 
 | 
  
 | 
    if (IS_SD(mmc)) 
 | 
        err = sd_change_freq(mmc); 
 | 
    else 
 | 
        err = mmc_change_freq(mmc); 
 | 
  
 | 
    if (err) 
 | 
        return err; 
 | 
  
 | 
    /* Restrict card's capabilities by what the host can do */ 
 | 
    mmc->card_caps &= mmc->cfg->host_caps; 
 | 
  
 | 
    if (IS_SD(mmc)) { 
 | 
        if (mmc->card_caps & MMC_MODE_4BIT) { 
 | 
            cmd.cmdidx = MMC_CMD_APP_CMD; 
 | 
            cmd.resp_type = MMC_RSP_R1; 
 | 
            cmd.cmdarg = mmc->rca << 16; 
 | 
  
 | 
            err = mmc_send_cmd(mmc, &cmd, NULL); 
 | 
            if (err) 
 | 
                return err; 
 | 
  
 | 
            cmd.cmdidx = SD_CMD_APP_SET_BUS_WIDTH; 
 | 
            cmd.resp_type = MMC_RSP_R1; 
 | 
            cmd.cmdarg = 2; 
 | 
            err = mmc_send_cmd(mmc, &cmd, NULL); 
 | 
            if (err) 
 | 
                return err; 
 | 
  
 | 
            mmc_set_bus_width(mmc, 4); 
 | 
        } 
 | 
  
 | 
        err = sd_read_ssr(mmc); 
 | 
        if (err) 
 | 
            return err; 
 | 
  
 | 
        if (mmc->card_caps & MMC_MODE_HS) 
 | 
            tran_speed = MMC_HIGH_52_MAX_DTR; 
 | 
        else 
 | 
            tran_speed = MMC_HIGH_26_MAX_DTR; 
 | 
  
 | 
        mmc_set_clock(mmc, tran_speed); 
 | 
    } 
 | 
  
 | 
    /* Fix the block length for DDR mode */ 
 | 
    if (mmc_card_ddr(mmc)) { 
 | 
        mmc->read_bl_len = MMC_MAX_BLOCK_LEN; 
 | 
        mmc->write_bl_len = MMC_MAX_BLOCK_LEN; 
 | 
    } 
 | 
  
 | 
    /* fill in device description */ 
 | 
    bdesc = mmc_get_blk_desc(mmc); 
 | 
    bdesc->lun = 0; 
 | 
    bdesc->hwpart = 0; 
 | 
    bdesc->type = 0; 
 | 
    bdesc->blksz = mmc->read_bl_len; 
 | 
    bdesc->log2blksz = LOG2(bdesc->blksz); 
 | 
    bdesc->lba = lldiv(mmc->capacity, mmc->read_bl_len); 
 | 
#if !defined(CONFIG_SPL_BUILD) || \ 
 | 
        (defined(CONFIG_SPL_LIBCOMMON_SUPPORT) && \ 
 | 
        !defined(CONFIG_USE_TINY_PRINTF)) 
 | 
    sprintf(bdesc->vendor, "Man %06x Snr %04x%04x", 
 | 
        mmc->cid[0] >> 24, (mmc->cid[2] & 0xffff), 
 | 
        (mmc->cid[3] >> 16) & 0xffff); 
 | 
    sprintf(bdesc->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(bdesc->revision, "%d.%d", (mmc->cid[2] >> 20) & 0xf, 
 | 
        (mmc->cid[2] >> 16) & 0xf); 
 | 
#else 
 | 
    bdesc->vendor[0] = 0; 
 | 
    bdesc->product[0] = 0; 
 | 
    bdesc->revision[0] = 0; 
 | 
#endif 
 | 
#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBDISK_SUPPORT) 
 | 
    part_init(bdesc); 
 | 
#endif 
 | 
  
 | 
    return 0; 
 | 
} 
 | 
  
 | 
#ifndef CONFIG_MMC_USE_PRE_CONFIG 
 | 
static int mmc_send_if_cond(struct mmc *mmc) 
 | 
{ 
 | 
    struct mmc_cmd cmd; 
 | 
    int err; 
 | 
  
 | 
    cmd.cmdidx = SD_CMD_SEND_IF_COND; 
 | 
    /* We set the bit if the host supports voltages between 2.7 and 3.6 V */ 
 | 
    cmd.cmdarg = ((mmc->cfg->voltages & 0xff8000) != 0) << 8 | 0xaa; 
 | 
    cmd.resp_type = MMC_RSP_R7; 
 | 
  
 | 
    err = mmc_send_cmd(mmc, &cmd, NULL); 
 | 
  
 | 
    if (err) 
 | 
        return err; 
 | 
  
 | 
    if ((cmd.response[0] & 0xff) != 0xaa) 
 | 
        return -EOPNOTSUPP; 
 | 
    else 
 | 
        mmc->version = SD_VERSION_2; 
 | 
  
 | 
    return 0; 
 | 
} 
 | 
#endif 
 | 
  
 | 
#if !CONFIG_IS_ENABLED(DM_MMC) 
 | 
/* board-specific MMC power initializations. */ 
 | 
__weak void board_mmc_power_init(void) 
 | 
{ 
 | 
} 
 | 
#endif 
 | 
  
 | 
#ifndef CONFIG_MMC_USE_PRE_CONFIG 
 | 
static int mmc_power_init(struct mmc *mmc) 
 | 
{ 
 | 
#if CONFIG_IS_ENABLED(DM_MMC) 
 | 
#if defined(CONFIG_DM_REGULATOR) && !defined(CONFIG_SPL_BUILD) 
 | 
    struct udevice *vmmc_supply; 
 | 
    int ret; 
 | 
  
 | 
    ret = device_get_supply_regulator(mmc->dev, "vmmc-supply", 
 | 
                      &vmmc_supply); 
 | 
    if (ret) { 
 | 
        debug("%s: No vmmc supply\n", mmc->dev->name); 
 | 
        return 0; 
 | 
    } 
 | 
  
 | 
    ret = regulator_set_enable(vmmc_supply, true); 
 | 
    if (ret) { 
 | 
        puts("Error enabling VMMC supply\n"); 
 | 
        return ret; 
 | 
    } 
 | 
#endif 
 | 
#else /* !CONFIG_DM_MMC */ 
 | 
    /* 
 | 
     * Driver model should use a regulator, as above, rather than calling 
 | 
     * out to board code. 
 | 
     */ 
 | 
    board_mmc_power_init(); 
 | 
#endif 
 | 
    return 0; 
 | 
} 
 | 
#endif 
 | 
#ifdef CONFIG_MMC_USE_PRE_CONFIG 
 | 
static int mmc_select_card(struct mmc *mmc, int n) 
 | 
{ 
 | 
    struct mmc_cmd cmd; 
 | 
    int err = 0; 
 | 
  
 | 
    memset(&cmd, 0, sizeof(struct mmc_cmd)); 
 | 
    if (!mmc_host_is_spi(mmc)) { /* cmd not supported in spi */ 
 | 
        mmc->rca = n; 
 | 
        cmd.cmdidx = MMC_CMD_SELECT_CARD; 
 | 
        cmd.resp_type = MMC_RSP_R1; 
 | 
        cmd.cmdarg = mmc->rca << 16; 
 | 
        err = mmc_send_cmd(mmc, &cmd, NULL); 
 | 
    } 
 | 
  
 | 
    return err; 
 | 
} 
 | 
  
 | 
int mmc_start_init(struct mmc *mmc) 
 | 
{ 
 | 
    /* 
 | 
     * We use the MMC config set by the bootrom. 
 | 
     * So it is no need to reset the eMMC device. 
 | 
     */ 
 | 
    mmc_set_bus_width(mmc, 8); 
 | 
    mmc_set_clock(mmc, 1); 
 | 
    mmc_set_timing(mmc, MMC_TIMING_LEGACY); 
 | 
    /* Send cmd7 to return stand-by state*/ 
 | 
    mmc_select_card(mmc, 0); 
 | 
    mmc->version = MMC_VERSION_UNKNOWN; 
 | 
    mmc->high_capacity = 1; 
 | 
    /* 
 | 
     * The RCA is set to 2 by rockchip bootrom, use the default 
 | 
     * value here. 
 | 
     */ 
 | 
#ifdef CONFIG_ARCH_ROCKCHIP 
 | 
    mmc->rca = 2; 
 | 
#else 
 | 
    mmc->rca = 1; 
 | 
#endif 
 | 
    return 0; 
 | 
} 
 | 
#else 
 | 
int mmc_start_init(struct mmc *mmc) 
 | 
{ 
 | 
    bool no_card; 
 | 
    int err; 
 | 
  
 | 
    /* we pretend there's no card when init is NULL */ 
 | 
    no_card = mmc_getcd(mmc) == 0; 
 | 
#if !CONFIG_IS_ENABLED(DM_MMC) 
 | 
    no_card = no_card || (mmc->cfg->ops->init == NULL); 
 | 
#endif 
 | 
    if (no_card) { 
 | 
        mmc->has_init = 0; 
 | 
#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) 
 | 
        printf("MMC: no card present\n"); 
 | 
#endif 
 | 
        return -ENOMEDIUM; 
 | 
    } 
 | 
  
 | 
    if (mmc->has_init) 
 | 
        return 0; 
 | 
  
 | 
#ifdef CONFIG_FSL_ESDHC_ADAPTER_IDENT 
 | 
    mmc_adapter_card_type_ident(); 
 | 
#endif 
 | 
    err = mmc_power_init(mmc); 
 | 
    if (err) 
 | 
        return err; 
 | 
  
 | 
#if CONFIG_IS_ENABLED(DM_MMC) 
 | 
    /* The device has already been probed ready for use */ 
 | 
#else 
 | 
    /* made sure it's not NULL earlier */ 
 | 
    err = mmc->cfg->ops->init(mmc); 
 | 
    if (err) 
 | 
        return err; 
 | 
#endif 
 | 
    mmc_set_bus_width(mmc, 1); 
 | 
    mmc_set_clock(mmc, 1); 
 | 
    mmc_set_timing(mmc, MMC_TIMING_LEGACY); 
 | 
  
 | 
    /* Reset the Card */ 
 | 
    err = mmc_go_idle(mmc); 
 | 
  
 | 
    if (err) 
 | 
        return err; 
 | 
  
 | 
    /* The internal partition reset to user partition(0) at every CMD0*/ 
 | 
    mmc_get_blk_desc(mmc)->hwpart = 0; 
 | 
  
 | 
    /* Test for SD version 2 */ 
 | 
    err = mmc_send_if_cond(mmc); 
 | 
  
 | 
    /* Now try to get the SD card's operating condition */ 
 | 
    err = sd_send_op_cond(mmc); 
 | 
  
 | 
    /* If the command timed out, we check for an MMC card */ 
 | 
    if (err == -ETIMEDOUT) { 
 | 
        err = mmc_send_op_cond(mmc); 
 | 
  
 | 
        if (err) { 
 | 
#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) 
 | 
            printf("Card did not respond to voltage select!\n"); 
 | 
#endif 
 | 
            return -EOPNOTSUPP; 
 | 
        } 
 | 
    } 
 | 
  
 | 
    if (!err) 
 | 
        mmc->init_in_progress = 1; 
 | 
  
 | 
    return err; 
 | 
} 
 | 
#endif 
 | 
  
 | 
static int mmc_complete_init(struct mmc *mmc) 
 | 
{ 
 | 
    int err = 0; 
 | 
  
 | 
    mmc->init_in_progress = 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; 
 | 
    return err; 
 | 
} 
 | 
  
 | 
int mmc_init(struct mmc *mmc) 
 | 
{ 
 | 
    int err = 0; 
 | 
    __maybe_unused unsigned start; 
 | 
#if CONFIG_IS_ENABLED(DM_MMC) 
 | 
    struct mmc_uclass_priv *upriv = dev_get_uclass_priv(mmc->dev); 
 | 
  
 | 
    upriv->mmc = mmc; 
 | 
#endif 
 | 
    if (mmc->has_init) 
 | 
        return 0; 
 | 
  
 | 
    start = get_timer(0); 
 | 
  
 | 
    if (!mmc->init_in_progress) 
 | 
        err = mmc_start_init(mmc); 
 | 
  
 | 
    if (!err) 
 | 
        err = mmc_complete_init(mmc); 
 | 
    if (err) 
 | 
        printf("%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; 
 | 
} 
 | 
  
 | 
/* CPU-specific MMC initializations */ 
 | 
__weak int cpu_mmc_init(bd_t *bis) 
 | 
{ 
 | 
    return -1; 
 | 
} 
 | 
  
 | 
/* board-specific MMC initializations. */ 
 | 
__weak int board_mmc_init(bd_t *bis) 
 | 
{ 
 | 
    return -1; 
 | 
} 
 | 
  
 | 
void mmc_set_preinit(struct mmc *mmc, int preinit) 
 | 
{ 
 | 
    mmc->preinit = preinit; 
 | 
} 
 | 
  
 | 
#if CONFIG_IS_ENABLED(DM_MMC) && defined(CONFIG_SPL_BUILD) 
 | 
static int mmc_probe(bd_t *bis) 
 | 
{ 
 | 
    return 0; 
 | 
} 
 | 
#elif CONFIG_IS_ENABLED(DM_MMC) 
 | 
static int mmc_probe(bd_t *bis) 
 | 
{ 
 | 
    int ret, i; 
 | 
    struct uclass *uc; 
 | 
    struct udevice *dev; 
 | 
  
 | 
    ret = uclass_get(UCLASS_MMC, &uc); 
 | 
    if (ret) 
 | 
        return ret; 
 | 
  
 | 
    /* 
 | 
     * Try to add them in sequence order. Really with driver model we 
 | 
     * should allow holes, but the current MMC list does not allow that. 
 | 
     * So if we request 0, 1, 3 we will get 0, 1, 2. 
 | 
     */ 
 | 
    for (i = 0; ; i++) { 
 | 
        ret = uclass_get_device_by_seq(UCLASS_MMC, i, &dev); 
 | 
        if (ret == -ENODEV) 
 | 
            break; 
 | 
    } 
 | 
    uclass_foreach_dev(dev, uc) { 
 | 
        ret = device_probe(dev); 
 | 
        if (ret) 
 | 
            printf("%s - probe failed: %d\n", dev->name, ret); 
 | 
    } 
 | 
  
 | 
    return 0; 
 | 
} 
 | 
#else 
 | 
static int mmc_probe(bd_t *bis) 
 | 
{ 
 | 
    if (board_mmc_init(bis) < 0) 
 | 
        cpu_mmc_init(bis); 
 | 
  
 | 
    return 0; 
 | 
} 
 | 
#endif 
 | 
  
 | 
int mmc_initialize(bd_t *bis) 
 | 
{ 
 | 
    static int initialized = 0; 
 | 
    int ret; 
 | 
    if (initialized)    /* Avoid initializing mmc multiple times */ 
 | 
        return 0; 
 | 
    initialized = 1; 
 | 
  
 | 
#if !CONFIG_IS_ENABLED(BLK) 
 | 
#if !CONFIG_IS_ENABLED(MMC_TINY) 
 | 
    mmc_list_init(); 
 | 
#endif 
 | 
#endif 
 | 
    ret = mmc_probe(bis); 
 | 
    if (ret) 
 | 
        return ret; 
 | 
  
 | 
#ifndef CONFIG_SPL_BUILD 
 | 
    print_mmc_devices(','); 
 | 
#endif 
 | 
  
 | 
    mmc_do_preinit(); 
 | 
    return 0; 
 | 
} 
 | 
  
 | 
#ifdef CONFIG_CMD_BKOPS_ENABLE 
 | 
int mmc_set_bkops_enable(struct mmc *mmc) 
 | 
{ 
 | 
    int err; 
 | 
    ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN); 
 | 
  
 | 
    err = mmc_send_ext_csd(mmc, ext_csd); 
 | 
    if (err) { 
 | 
        puts("Could not get ext_csd register values\n"); 
 | 
        return err; 
 | 
    } 
 | 
  
 | 
    if (!(ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1)) { 
 | 
        puts("Background operations not supported on device\n"); 
 | 
        return -EMEDIUMTYPE; 
 | 
    } 
 | 
  
 | 
    if (ext_csd[EXT_CSD_BKOPS_EN] & 0x1) { 
 | 
        puts("Background operations already enabled\n"); 
 | 
        return 0; 
 | 
    } 
 | 
  
 | 
    err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BKOPS_EN, 1); 
 | 
    if (err) { 
 | 
        puts("Failed to enable manual background operations\n"); 
 | 
        return err; 
 | 
    } 
 | 
  
 | 
    puts("Enabled manual background operations\n"); 
 | 
  
 | 
    return 0; 
 | 
} 
 | 
#endif 
 |