【Linux驱动开发】Linux MMC子系统技术分析报告 - 第二部分:协议实现与性能优化

Linux MMC子系统技术分析报告 - 第二部分:协议实现与性能优化

基于 t41-kernel-4.4.94 内核代码的深度分析

1. 协议实现细节

1.1 MMC协议实现

1.1.1 命令类型与编码

MMC协议定义了多种命令类型,每种类型都有特定的格式和用途:

c 复制代码
// include/linux/mmc/mmc.h
#define MMC_CMD_AC   (0 << 5)    // 地址控制命令
#define MMC_CMD_ADTC (1 << 5)    // 地址数据传输命令
#define MMC_CMD_BC   (2 << 5)    // 广播命令
#define MMC_CMD_BCR  (3 << 5)    // 广播响应命令

// 标准MMC命令定义
#define MMC_GO_IDLE_STATE         0    // 复位到空闲状态
#define MMC_SEND_OP_COND          1    // 发送操作条件
#define MMC_ALL_SEND_CID          2    // 获取所有CID
#define MMC_SET_RELATIVE_ADDR     3    // 设置相对地址
#define MMC_SELECT_CARD           7    // 选择卡
#define MMC_SEND_EXT_CSD          8    // 发送扩展CSD
#define MMC_READ_MULTIPLE_BLOCK  18    // 读取多块数据
#define MMC_WRITE_MULTIPLE_BLOCK 25    // 写入多块数据
1.1.2 响应格式处理

MMC响应有多种格式,内核提供了完整的解析机制:

c 复制代码
// drivers/mmc/core/mmc.c
static int mmc_decode_cid(struct mmc_card *card)
{
    struct mmc_cid *cid = &card->cid;
    u32 *resp = card->raw_cid;
    
    cid->manfid        = UNSTUFF_BITS(resp, 120, 8);
    cid->prod_name[0]  = UNSTUFF_BITS(resp, 96, 8);
    cid->prod_name[1]  = UNSTUFF_BITS(resp, 88, 8);
    cid->prod_name[2]  = UNSTUFF_BITS(resp, 80, 8);
    cid->prod_name[3]  = UNSTUFF_BITS(resp, 72, 8);
    cid->prod_name[4]  = UNSTUFF_BITS(resp, 64, 8);
    cid->prod_name[5]  = UNSTUFF_BITS(resp, 56, 8);
    cid->serial        = UNSTUFF_BITS(resp, 16, 32);
    cid->month         = UNSTUFF_BITS(resp, 12, 4);
    cid->year          = UNSTUFF_BITS(resp, 8, 4) + 1997;
    
    return 0;
}
1.1.3 扩展CSD处理

扩展CSD包含设备的高级特性和配置:

c 复制代码
// drivers/mmc/core/mmc.c
static int mmc_read_ext_csd(struct mmc_card *card)
{
    struct mmc_command cmd = {0};
    struct mmc_data data = {0};
    struct mmc_request mrq = {0};
    u8 *ext_csd;
    
    ext_csd = kmalloc(512, GFP_KERNEL);
    if (!ext_csd)
        return -ENOMEM;
    
    cmd.opcode = MMC_SEND_EXT_CSD;
    cmd.arg = 0;
    cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
    
    data.blksz = 512;
    data.blocks = 1;
    data.flags = MMC_DATA_READ;
    data.sg = &sg;
    sg_init_one(&sg, ext_csd, 512);
    
    mmc_wait_for_req(card->host, &mrq);
    
    // 解析重要字段
    card->ext_csd.rev = ext_csd[EXT_CSD_REV];
    card->ext_csd.cmdq_support = ext_csd[EXT_CSD_CMDQ_SUPPORT];
    card->ext_csd.cache_size = ext_csd[EXT_CSD_CACHE_SIZE] * 512;
    
    kfree(ext_csd);
    return 0;
}

1.2 SD协议实现

1.2.1 SD卡识别流程

SD卡识别与MMC有所不同,需要特殊的处理流程:

c 复制代码
// drivers/mmc/core/sd.c
static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
                           struct mmc_card *oldcard)
{
    struct mmc_card *card;
    int err;
    
    // 发送SD初始化命令序列
    mmc_go_idle(host);
    
    // 发送SD_SEND_IF_COND命令,检测SDv2.0
    err = mmc_send_if_cond(host, ocr);
    if (!err)
        ocr |= SD_OCR_CCS;  // 支持高容量
    
    // 发送ACMD41,初始化SD卡
    err = mmc_send_app_op_cond(host, ocr, NULL);
    if (err)
        goto err;
    
    // 获取CID
    err = mmc_all_send_cid(host, card->raw_cid);
    if (err)
        goto err;
    
    // 设置RCA
    err = mmc_set_relative_addr(card);
    if (err)
        goto err;
    
    return 0;
err:
    mmc_remove_card(card);
    return err;
}
1.2.2 SD SCR寄存器处理

SCR(SD Configuration Register)包含SD卡特定信息:

c 复制代码
// drivers/mmc/core/sd.c
static int mmc_read_scr(struct mmc_card *card)
{
    int err;
    u32 scr[2];
    
    err = mmc_app_send_scr(card, scr);
    if (err)
        return err;
    
    card->scr.scr_struct = UNSTUFF_BITS(scr, 0, 4);
    card->scr.sd_spec = UNSTUFF_BITS(scr, 4, 4);
    card->scr.bus_widths = UNSTUFF_BITS(scr, 16, 4);
    
    if (card->scr.sd_spec >= 2) {
        card->scr.sd_spec3 = UNSTUFF_BITS(scr, 47, 1);
        if (card->scr.sd_spec3)
            card->scr.sd_spec4 = UNSTUFF_BITS(scr, 42, 1);
    }
    
    return 0;
}

1.3 SDIO协议实现

1.3.1 SDIO设备检测

SDIO设备有独特的检测和初始化流程:

c 复制代码
// drivers/mmc/core/sdio.c
static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
                             struct mmc_card *oldcard)
{
    int err;
    
    // SDIO设备不需要完整的MMC初始化序列
    mmc_go_idle(host);
    
    // 发送IO_SEND_OP_COND命令
    err = mmc_send_io_op_cond(host, ocr, NULL);
    if (err)
        return err;
    
    // 获取SDIO设备的CIA(Common I/O Area)信息
    err = mmc_read_cccr(card);
    if (err)
        return err;
    
    // 初始化功能
    for (func = 0; func <= card->sdio_funcs; func++) {
        err = sdio_init_func(card, func);
        if (err)
            goto remove;
    }
    
    return 0;
}
1.3.2 SDIO中断处理

SDIO支持中断机制,需要特殊的中断处理:

c 复制代码
// drivers/mmc/core/sdio_irq.c
void sdio_run_irqs(struct mmc_host *host)
{
    struct mmc_card *card = host->card;
    unsigned int i;
    
    if (!card || !card->sdio_funcs)
        return;
    
    // 检查每个功能的中断状态
    for (i = 0; i < card->sdio_funcs; i++) {
        struct sdio_func *func = card->sdio_func[i];
        
        if (func && func->irq_handler) {
            if (sdio_func_intr_pending(func)) {
                func->irq_handler(func);
            }
        }
    }
}

2. 高级性能优化机制

2.1 CMD23多块传输优化

CMD23命令允许预定义传输块数,显著提高传输效率:

c 复制代码
// drivers/mmc/core/block.c
static int mmc_blk_set_blksize(struct mmc_blk_data *md,
                               struct mmc_card *card)
{
    struct mmc_command cmd = {0};
    int err;
    
    // 使用CMD23设置块数
    if (card->ext_csd.cmdq_support) {
        cmd.opcode = MMC_SET_BLOCK_COUNT;
        cmd.arg = md->queue.blocks;
        cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
        
        err = mmc_wait_for_cmd(card->host, &cmd, 0);
        if (err)
            return err;
    }
    
    return 0;
}

// 优化的多块读取
static int mmc_blk_read_multiblock(struct mmc_blk_data *md,
                                   struct mmc_card *card,
                                   unsigned int blocks)
{
    struct mmc_request mrq = {0};
    struct mmc_command cmd = {0};
    struct mmc_data data = {0};
    
    // 首先使用CMD23设置块数
    if (card->ext_csd.cmdq_support) {
        mmc_blk_set_blksize(md, card);
    }
    
    // 然后执行多块读取
    cmd.opcode = MMC_READ_MULTIPLE_BLOCK;
    cmd.arg = md->queue.curr_sector;
    cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
    
    data.blksz = 512;
    data.blocks = blocks;
    data.flags = MMC_DATA_READ;
    data.sg = md->queue.sg;
    data.sg_len = md->queue.sg_len;
    
    mmc_wait_for_req(card->host, &mrq);
    
    return 0;
}

2.2 ADMA(高级DMA)优化

ADMA支持复杂的散聚传输,减少CPU干预:

c 复制代码
// drivers/mmc/host/sdhci.c
static void sdhci_adma_table_pre(struct sdhci_host *host,
                                struct mmc_data *data)
{
    struct sdhci_adma2_64_desc *desc;
    unsigned int sg_len;
    int i;
    
    desc = host->adma_table;
    sg_len = data->sg_len;
    
    for (i = 0; i < sg_len; i++) {
        struct scatterlist *sg = &data->sg[i];
        dma_addr_t addr = sg_dma_address(sg);
        unsigned int len = sg_dma_len(sg);
        
        // 设置ADMA描述符
        desc->addr = cpu_to_le32(addr);
        desc->len = cpu_to_le16(len);
        desc->attr = SDHCI_ADMA2_VALID | SDHCI_ADMA2_TRAN;
        
        // 处理跨页边界的情况
        if (host->flags & SDHCI_USE_64_BIT_DMA) {
            desc->addr_hi = cpu_to_le32(upper_32_bits(addr));
        }
        
        desc++;
    }
    
    // 设置结束描述符
    desc->attr = SDHCI_ADMA2_VALID | SDHCI_ADMA2_END;
}

// ADMA中断处理
static void sdhci_adma_irq(struct sdhci_host *host, u32 intmask)
{
    struct mmc_data *data = host->data;
    
    if (intmask & SDHCI_INT_ADMA_ERROR) {
        // ADMA错误处理
        data->error = -EIO;
        sdhci_adma_error_recovery(host);
    } else if (intmask & SDHCI_INT_DATA_END) {
        // 数据传输完成
        tasklet_schedule(&host->finish_tasklet);
    }
}

2.3 动态时钟调整

根据工作负载和卡能力动态调整时钟频率:

c 复制代码
// drivers/mmc/core/core.c
unsigned int mmc_set_clock(struct mmc_host *host, unsigned int freq)
{
    unsigned int old_freq = host->ios.clock;
    
    // 频率限制检查
    if (freq < host->f_min)
        freq = host->f_min;
    if (freq > host->f_max)
        freq = host->f_max;
    
    // 根据卡类型和能力调整频率
    if (host->card) {
        switch (host->card->type) {
        case MMC_TYPE_MMC:
            if (host->card->ext_csd.hs_timing)
                freq = min(freq, 52000000);  // HS模式最高52MHz
            if (host->card->ext_csd.strobe_support)
                freq = min(freq, 200000000); // HS400模式最高200MHz
            break;
            
        case MMC_TYPE_SD:
            if (host->card->sw_caps.uhs_max_dtr)
                freq = min(freq, host->card->sw_caps.uhs_max_dtr);
            break;
        }
    }
    
    if (freq != old_freq) {
        host->ios.clock = freq;
        host->ops->set_ios(host, &host->ios);
        
        // 等待时钟稳定
        mmc_delay(1);
    }
    
    return freq;
}

// 基于负载的时钟调整
static void mmc_adjust_clock_for_load(struct mmc_host *host)
{
    struct mmc_card *card = host->card;
    unsigned int freq;
    
    if (!card)
        return;
    
    // 根据当前I/O负载调整时钟
    if (host->ios.load > 80) {
        // 高负载,使用最高频率
        freq = host->f_max;
    } else if (host->ios.load > 40) {
        // 中等负载,使用中等频率
        freq = (host->f_min + host->f_max) / 2;
    } else {
        // 低负载,使用最低频率
        freq = host->f_min;
    }
    
    mmc_set_clock(host, freq);
}

2.4 I/O请求队列优化

块设备层实现了高效的请求队列管理:

c 复制代码
// drivers/mmc/card/queue.c
static int mmc_queue_thread(void *data)
{
    struct mmc_queue *mq = data;
    struct request_queue *q = mq->queue;
    struct request *req;
    
    do {
        // 等待请求到来
        wait_event_freezable(mq->wait, 
            (req = blk_fetch_request(q)) || kthread_should_stop());
        
        if (req) {
            // 请求预处理
            mmc_queue_pre_req(mq, req);
            
            // 执行请求
            mq->issue_fn(mq, req);
            
            // 请求后处理
            mmc_queue_post_req(mq, req);
        }
    } while (!kthread_should_stop());
    
    return 0;
}

// 请求合并和排序优化
static bool mmc_queue_merge_req(struct mmc_queue *mq, struct request *req)
{
    struct request *next = blk_queue_next_request(req);
    
    // 检查是否可以合并相邻请求
    if (next && blk_rq_pos(next) == blk_rq_pos(req) + blk_rq_sectors(req)) {
        if (mmc_can_merge(req, next)) {
            blk_rq_sectors(req) += blk_rq_sectors(next);
            blk_delete_request(next);
            return true;
        }
    }
    
    return false;
}

2.5 缓存优化

利用卡的内部缓存提高写入性能:

c 复制代码
// drivers/mmc/core/mmc.c
static int mmc_enable_cache(struct mmc_card *card)
{
    struct mmc_command cmd = {0};
    int err;
    
    if (!card->ext_csd.cache_size)
        return 0;
    
    // 发送CACHE_ENABLE命令
    cmd.opcode = MMC_SWITCH;
    cmd.arg = (EXT_CSD_CACHE_CTRL << 16) | (1 << 8) |
              EXT_CSD_CMD_SET_NORMAL;
    cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
    
    err = mmc_wait_for_cmd(card->host, &cmd, 0);
    if (err)
        return err;
    
    // 等待缓存启用完成
    err = mmc_wait_for_ready(card);
    if (err)
        return err;
    
    card->cache_enabled = true;
    return 0;
}

// 缓存刷写优化
static int mmc_flush_cache(struct mmc_card *card)
{
    struct mmc_command cmd = {0};
    int err;
    
    if (!card->cache_enabled)
        return 0;
    
    // 发送CACHE_FLUSH命令
    cmd.opcode = MMC_SWITCH;
    cmd.arg = (EXT_CSD_FLUSH_CACHE << 16) | (1 << 8) |
              EXT_CSD_CMD_SET_NORMAL;
    cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
    
    err = mmc_wait_for_cmd(card->host, &cmd, 0);
    if (err)
        return err;
    
    // 等待刷写完成
    return mmc_wait_for_ready(card);
}

3. 错误处理与恢复机制

3.1 高级错误检测

实现多层次的错误检测机制:

c 复制代码
// drivers/mmc/core/core.c
static int mmc_check_status(struct mmc_card *card, u32 status)
{
    struct mmc_host *host = card->host;
    
    // CRC错误检测
    if (status & R1_COM_CRC_ERROR) {
        pr_debug("%s: command CRC error\n", mmc_hostname(host));
        return -EILSEQ;
    }
    
    // 非法命令检测
    if (status & R1_ILLEGAL_COMMAND) {
        pr_debug("%s: illegal command\n", mmc_hostname(host));
        return -EINVAL;
    }
    
    // 设备状态检测
    if (status & R1_ERROR) {
        pr_debug("%s: general error\n", mmc_hostname(host));
        return -EIO;
    }
    
    // 地址错误检测
    if (status & R1_ADDRESS_ERROR) {
        pr_debug("%s: address error\n", mmc_hostname(host));
        return -EFAULT;
    }
    
    // 擦除参数错误
    if (status & R1_ERASE_PARAM_ERROR) {
        pr_debug("%s: erase parameter error\n", mmc_hostname(host));
        return -EINVAL;
    }
    
    return 0;
}

3.2 自适应重试策略

根据错误类型采用不同的重试策略:

c 复制代码
// drivers/mmc/core/core.c
static int mmc_execute_tuning(struct mmc_card *card)
{
    struct mmc_host *host = card->host;
    int err = 0;
    
    // 仅在需要时进行调优
    if (!host->ops->execute_tuning)
        return 0;
    
    // 执行调优序列
    err = host->ops->execute_tuning(host, MMC_SEND_TUNING_BLOCK);
    if (err)
        return err;
    
    // 验证调优结果
    err = mmc_send_status(card, &status);
    if (err)
        return err;
    
    return mmc_check_status(card, status);
}

// 智能重试机制
static int mmc_smart_retry(struct mmc_host *host, struct mmc_request *mrq)
{
    int retry_limit = 3;
    int err;
    
    for (int retry = 0; retry < retry_limit; retry++) {
        err = mmc_wait_for_req(host, mrq);
        
        if (!err)
            return 0;
            
        // 根据错误类型决定重试策略
        if (mrq->cmd->error == -EILSEQ) {
            // CRC错误,降低时钟频率重试
            if (host->ios.clock > host->f_min * 2) {
                mmc_set_clock(host, host->ios.clock / 2);
                continue;
            }
        } else if (mrq->cmd->error == -ETIMEDOUT) {
            // 超时错误,增加超时时间
            if (mrq->data && mrq->data->timeout_ns < 100000000) {
                mrq->data->timeout_ns *= 2;
                continue;
            }
        }
        
        // 其他错误不再重试
        break;
    }
    
    return err;
}

3.3 数据完整性保护

实现多层次的数据完整性保护:

c 复制代码
// drivers/mmc/core/block.c
static int mmc_blk_verify_data(struct mmc_card *card,
                              struct mmc_data *data,
                              struct scatterlist *sg)
{
    struct mmc_host *host = card->host;
    u32 calculated_crc;
    u32 received_crc;
    
    // 计算数据的CRC
    calculated_crc = crc32_le(~0, sg_virt(sg), data->blksz * data->blocks);
    
    // 获取接收到的CRC(如果硬件支持)
    if (host->ops->get_crc) {
        received_crc = host->ops->get_crc(host);
        
        if (calculated_crc != received_crc) {
            pr_err("%s: CRC mismatch: calc=0x%08x, recv=0x%08x\n",
                   mmc_hostname(host), calculated_crc, received_crc);
            return -EILSEQ;
        }
    }
    
    return 0;
}

4. 电源管理高级特性

4.1 动态电源管理

实现智能的电源管理策略:

c 复制代码
// drivers/mmc/core/core.c
static int mmc_power_save(struct mmc_host *host, bool save)
{
    struct mmc_card *card = host->card;
    int ret = 0;
    
    if (!card)
        return 0;
    
    if (save) {
        // 保存当前状态
        host->saved_ios = host->ios;
        
        // 进入省电模式
        if (host->ops->power_save)
            ret = host->ops->power_save(host, true);
        
        // 关闭时钟
        host->ios.clock = 0;
        host->ops->set_ios(host, &host->ios);
        
        // 通知卡设备
        if (card->type == MMC_TYPE_MMC && card->ext_csd.power_off_notification) {
            mmc_send_power_off_notification(card);
        }
    } else {
        // 恢复电源
        if (host->ops->power_restore)
            ret = host->ops->power_restore(host);
        
        // 恢复时钟
        host->ios = host->saved_ios;
        host->ops->set_ios(host, &host->ios);
        
        // 重新初始化卡
        mmc_card_reinit(host);
    }
    
    return ret;
}

4.2 运行时电源管理

支持运行时电源管理(Runtime PM):

c 复制代码
// drivers/mmc/card/block.c
static int mmc_blk_runtime_suspend(struct device *dev)
{
    struct mmc_card *card = mmc_dev_to_card(dev);
    struct mmc_host *host = card->host;
    int ret = 0;
    
    // 刷写缓存
    if (card->cache_enabled) {
        ret = mmc_flush_cache(card);
        if (ret)
            return ret;
    }
    
    // 进入休眠模式
    ret = mmc_card_sleep(host);
    if (ret)
        return ret;
    
    // 降低时钟频率到最低
    mmc_set_clock(host, host->f_min);
    
    // 关闭总线电源
    mmc_set_bus_power(host, false);
    
    return 0;
}

static int mmc_blk_runtime_resume(struct device *dev)
{
    struct mmc_card *card = mmc_dev_to_card(dev);
    struct mmc_host *host = card->host;
    int ret = 0;
    
    // 恢复总线电源
    mmc_set_bus_power(host, true);
    
    // 恢复到工作频率
    mmc_set_clock(host, host->f_max);
    
    // 唤醒卡设备
    ret = mmc_card_awake(host);
    if (ret)
        return ret;
    
    // 重新选择卡
    ret = mmc_select_card(card);
    if (ret)
        return ret;
    
    return 0;
}

5. 总结

Linux MMC子系统通过以下高级特性实现了卓越的性能和可靠性:

5.1 协议优化

  • CMD23预定义传输:减少命令开销,提高大块传输效率
  • ADMA高级DMA:支持复杂散聚传输,降低CPU占用
  • 多协议支持:无缝支持MMC/SD/SDIO设备

5.2 性能优化

  • 动态时钟调整:根据负载智能调整工作频率
  • 请求队列优化:合并相邻请求,减少总线争用
  • 缓存利用:充分利用设备内部缓存提升写入性能

5.3 可靠性保障

  • 多层次错误检测:CRC校验、状态监控、数据完整性验证
  • 智能重试策略:根据错误类型采用不同重试机制
  • 自适应恢复:动态调整参数以适应当前条件

5.4 电源效率

  • 动态电源管理:智能的省电模式切换
  • 运行时电源管理:支持Runtime PM规范
  • 时钟门控:精细的时钟控制策略

这些高级特性使Linux MMC子系统能够在各种应用场景下提供稳定、高效、低功耗的存储解决方案。


相关推荐
a123560mh2 小时前
国产信创操作系统银河麒麟常见软件适配(MongoDB、 Redis、Nginx、Tomcat)
linux·redis·nginx·mongodb·tomcat·kylin
guygg882 小时前
Linux服务器上安装配置GitLab
linux·运维·gitlab
百***35513 小时前
Linux(CentOS)安装 Nginx
linux·nginx·centos
tzhou644523 小时前
Linux文本处理工具:cut、sort、uniq、tr
linux·运维·服务器
顾安r4 小时前
11.19 脚本 最小web控制linux/termux
linux·服务器·css·flask
Saniffer_SH4 小时前
通过近期测试简单聊一下究竟是直接选择Nvidia Spark还是4090/5090 GPU自建环境
大数据·服务器·图像处理·人工智能·驱动开发·spark·硬件工程
程序媛_MISS_zhang_01104 小时前
vant-ui中List 组件可以与 PullRefresh 组件结合使用,实现下拉刷新的效果
java·linux·ui
dragoooon344 小时前
[Linux网络——Lesson2.socket套接字 && 简易UDP网络程序]
linux·网络·udp
大聪明-PLUS4 小时前
编程语言保证是安全软件开发的基础
linux·嵌入式·arm·smarc