首先我们需要看emmc手册协议 这是我看的 Embedded Multi-Media Card (eMMC) Electrical Standard (5.1)

主机需要发送一些cmd 让设备进入transfer传输状态。

初始化流程:

eMMC (embedded MultiMediaCard) 是一种用于嵌入式设备的数据存储解决方案,广泛应用于手机、平板电脑、电视等设备。它通过一系列命令进行初始化和配置,以便主机设备能够正确识别和使用eMMC存储卡。以下是eMMC的初始化过程的详细步骤:

电源上电: eMMC上电后进入BOOT模式,此时eMMC的引脚配置为SDIO模式。这是eMMC开始初始化的第一步。 1:CMD0命令(进入IDLE状态): 主机设备向eMMC发送CMD0命令,使eMMC进入IDLE状态。这表示eMMC处于待命状态,准备接收后续的命令。

2:CMD1命令(电压和容量查询): 主机发送CMD1命令,询问eMMC卡的电压范围、容量等基本信息。eMMC根据查询返回相关信息,帮助主机确认其是否能够兼容。

3:CMD2命令(CID返回): 主机发送CMD2命令,eMMC返回其CID(Card Identification)寄存器的内容。CID包含卡的唯一标识信息,如制造商ID、产品编号等。

4:CMD3命令(CSD返回): 主机发送CMD3命令,eMMC返回CSD(Card Specific Data)寄存器内容。CSD包含eMMC卡的特性、支持的模式、读写速度、最大容量等信息。

5:CMD7命令(进入Transfer状态): 主机发送CMD7命令,eMMC进入Transfer状态。此时,eMMC可以进行数据传输,主机和eMMC之间可以开始实际的数据读写操作。

6:CMD6命令(配置工作模式): 主机发送ACMD6命令,eMMC配置其工作模式。包括设置时钟频率、总线宽度等参数,以优化数据传输性能。

7:CMD13命令(查询状态寄存器): 主机发送CMD13命令,eMMC返回其状态寄存器内容。状态寄存器包含eMMC的当前状态,如是否存在错误、是否处于忙碌状态等信息。

8:初始化完成: 初始化过程完成后,主机与eMMC的通信已建立,主机可以通过读取和写入操作来访问eMMC中的数据存储区域。

drive/mmc.c 

int ava_init(struct mmc *mmc) {
    struct mmc_cmd cmd;
    int err = 0;
    int i = 0;
#if  z_ere
    /* Reset the Card */
    err = mmc_power_init(mmc);
    if (err) {
        printf(" mmc_power_init err \n");
        return err;
    }

    err = mmc->cfg->ops->init(mmc);
    if (err) {
         printf(" mmc->cfg->ops->init \n");
        return err;
    }
    mmc_set_bus_width(mmc, 1);
    mmc_set_clock(mmc, 1);
    mmc->ddr_mode = 0;
    debug("03\n");
#endif
    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)
    {
        printf("MMC_CMD_GO_IDLE_STATE \n err");
        return err;
    }
    // device is of not busy
    for (i = 0; i < 10; i++) {
        cmd.cmdidx = MMC_CMD_SEND_OP_COND;
        cmd.resp_type = MMC_RSP_R2;
        cmd.cmdarg = 0x40FF8080;
        err = mmc_send_cmd(mmc, &cmd, NULL);
        if (err) {
            debug("mmc go idle cmd error\n");
            return err;
        }
        printf("cmd.response[0]:%d\n ",cmd.response[0]);
        mmc->ocr = cmd.response[0];
        if (mmc->ocr & OCR_BUSY) {
            printf("\n");
            break;
        }
        udelay(100);
    }
    cmd.cmdidx = MMC_CMD_ALL_SEND_CID;
    cmd.resp_type = MMC_RSP_R2;
    cmd.cmdarg = 0;
    err = mmc_send_cmd(mmc, &cmd, NULL);
    if (err) {
        debug("CMD 2 err \n");
        return err;
    }
#if z_ere
    memcpy(mmc->cid, cmd.response, 16);
#endif
    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) {
        debug("CMD3 err \n");
        return err;
    }

    cmd.cmdidx = MMC_CMD_SELECT_CARD;
    cmd.resp_type = MMC_RSP_R1;
    cmd.cmdarg = mmc->rca << 16;
    err = mmc_send_cmd(mmc, &cmd, NULL);

    err = mmc_send_status(mmc, 1000);
    if (err) {
        debug("send err cmd13\n");
        return err;
    }
#if z_ere
    err = mmc_ext_csd(mmc);
    if (err) {
        debug("mmc_ext_csd\n");
        return err;
    }
#endif
    return 0;
}

/cmd/mmc.c

static int do_mmc_ava_init(cmd_tbl_t *cmdtp, int flag,
                            int argc, char *const argv[]) {
    int ret;
    struct mmc *mmc;
    int err = 0;

    if (curr_device < 0) {
        if (get_mmc_num() > 0) {
            curr_device = 0;
        } else {
            puts("no emmmc avaiable");
            return 1;
        }
    }
      mmc = find_mmc_device(1);

    if (!mmc) {
        return CMD_RET_FAILURE;
    }

    else {
        ret = ava_init(mmc); // 为0执行成功
        if (ret) {
            printf("mmc_ava_init err\n");
        }else{
             printf("mmc_ava_init success\n");
        }
        return CMD_RET_SUCCESS;
    }                      
}
U_BOOT_CMD(
    mmc_ava_init, 1, 0, do_mmc_ava_init,
    "mmc_ava_init",
    "- display info of the mmc_ava_init");

uboot日志:

逻辑分析仪采集的时序:

初始化完成之后就可以愉快的进行读写擦等操作了

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐