【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子系统能够在各种应用场景下提供稳定、高效、低功耗的存储解决方案。


相关推荐
Yana.nice1 小时前
openssl将证书从p7b转换为crt格式
java·linux
AI逐月1 小时前
tmux 常用命令总结:从入门到稳定使用的一篇实战博客
linux·服务器·ssh·php
小白跃升坊2 小时前
基于1Panel的AI运维
linux·运维·人工智能·ai大模型·教学·ai agent
跃渊Yuey2 小时前
【Linux】线程同步与互斥
linux·笔记
舰长1152 小时前
linux 实现文件共享的实现方式比较
linux·服务器·网络
zmjjdank1ng2 小时前
Linux 输出重定向
linux·运维
路由侠内网穿透.2 小时前
本地部署智能家居集成解决方案 ESPHome 并实现外部访问( Linux 版本)
linux·运维·服务器·网络协议·智能家居
VekiSon3 小时前
Linux内核驱动——基础概念与开发环境搭建
linux·运维·服务器·c语言·arm开发
zl_dfq3 小时前
Linux 之 【进程信号】(signal、kill、raise、abort、alarm、Core Dump核心转储机制)
linux
Ankie Wan3 小时前
cgroup(Control Group)是 Linux 内核提供的一种机制,用来“控制、限制、隔离、统计”进程对系统资源的使用。
linux·容器·cgroup·lxc