当前位置:首页>Linux>深度解析Linux MMC子系统

深度解析Linux MMC子系统

  • 2026-10-11 08:00:53
深度解析Linux MMC子系统

一、前言

1.1 业务场景

MMC(MultiMedia Card)和 SD(Secure Digital)卡是嵌入式系统中最常用的存储介质之一,广泛应用于:

  • 消费电子:手机、平板、相机、游戏机
  • 工业控制:PLC、嵌入式终端、数据采集器
  • 物联网设备:路由器、网关、智能传感器
  • 车载系统:行车记录仪、车载娱乐系统

这些设备都需要高性能、高可靠性的存储方案,而 MMC/SD 卡驱动就是连接硬件和文件系统的桥梁。

1.2 应用价值

  • 数据存储:为用户提供可扩展的存储空间
  • 系统启动:许多嵌入式设备从 SD 卡启动
  • 固件升级:通过 SD 卡更新系统固件
  • 日志记录:保存系统运行日志和调试信息

1.3 驱动作用

MMC/SD 驱动在内核中的作用:

  1. 硬件抽象:将复杂的 MMC 协议简化为标准块设备接口
  2. 请求调度:通过 blk-mq 优化 I/O 请求的处理顺序
  3. 错误处理:自动重试、错误恢复、坏块管理
  4. 电源管理:支持动态电压调整、休眠唤醒

二、技术原理

2.1 MMC 子系统整体架构

MMC 子系统采用分层设计,从上到下分为:

2.2 核心层次详解

层次一:MMC 核心层(core/)

MMC 核心层是整个子系统的中枢,负责:

功能模块
职责说明
卡管理
卡的检测、识别、初始化和移除
命令处理
命令发送、响应解析、错误处理
数据传输
块读写、DMA 管理、请求调度
块设备接口
与通用块层交互,提供标准块设备接口
SDIO 支持
SDIO 设备枚举、函数管理、中断处理
电源管理
电压切换、时钟控制、省电模式

核心文件详解:

文件
主要功能
关键函数
core/core.c
核心初始化、命令发送、卡状态管理
mmc_start_request()
, mmc_wait_for_req()
core/bus.c
设备总线管理、驱动匹配、设备枚举
mmc_bus_probe()
, mmc_bus_remove()
core/sd.c
SD 卡特定逻辑、ACMD 命令、SDIO 检测
mmc_sd_init_card()
, mmc_sd_setup_card()
core/mmc.c
MMC/eMMC 卡特定逻辑
mmc_init_card()
, mmc_setup_card()
core/block.c
块设备接口、请求队列管理
mmc_blk_issue_rq()
, mmc_blk_complete_rq()
core/sdio.c
SDIO 功能支持
sdio_init_func()
, sdio_read_bytes()
core/host.c
主机控制器注册、注销、配置
mmc_alloc_host()
, mmc_add_host()
core/pwrseq.c
电源序列管理
mmc_pwrseq_power_on()
, mmc_pwrseq_power_off()

层次二:主机驱动层(host/)

主机驱动层负责与具体的 MMC 控制器硬件交互。不同芯片厂商的控制器差异较大,但都遵循相同的核心接口。

主要主机驱动:

驱动文件
适用控制器
特点
dw_mmc.c
DesignWare MMC Controller
通用驱动,支持多种平台
dw_mmc-pltfm.c
DesignWare 平台驱动
平台设备绑定
dw_mmc-rockchip.c
瑞芯微平台特定
瑞芯微 RK 系列优化
sdhci.c
SD Host Controller Interface
标准 SDHCI 控制器
sdhci-pltfm.c
SDHCI 平台驱动
平台设备绑定
renesas_sdhi_internal_dmac.c
瑞萨 SDHI
瑞萨特定实现
mxc_mmc.c
飞思卡尔 MMC
i.MX 系列

层次三:SDIO 层(sdio/)

SDIO(Secure Digital Input Output)层支持 SDIO 设备(如 WiFi 模块、蓝牙模块等):

  • SDIO 总线枚举:发现总线上的 SDIO 设备
  • SDIO 函数驱动:为每个 SDIO 函数提供驱动支持
  • 中断处理:处理 SDIO 设备产生的中断

2.3 MMC 协议基础

2.3.1 MMC/SD 命令类型

MMC/SD 卡使用命令-响应协议进行通信:

命令类型
范围
说明
BC
CMD0-127
广播命令(无响应)
BCR
CMD0-127
广播命令(带响应)
AC
CMD0-127
寻址命令(无响应)
ACR
CMD0-127
寻址命令(带响应)
ADTC
CMD128-255
带数据的寻址命令

2.3.2 常用命令

命令
名称
用途
CMD0
GO_IDLE_STATE
复位卡到空闲状态
CMD1
SEND_OP_COND
发送操作条件
CMD2
ALL_SEND_CID
请求卡 ID
CMD3
SEND_REL_ADDR
设置相对卡地址
CMD6
SWITCH_FUNC
切换功能
CMD7
SELECT/DESELECT_CARD
选择/取消选择卡
CMD8
SEND_EXT_CSD
发送扩展 CSD
CMD9
SEND_CSD
请求卡特定数据
CMD10
SEND_CID
请求卡 ID
CMD12
STOP_TRANSMISSION
停止数据传输
CMD13
SEND_STATUS
请求卡状态
CMD16
SET_BLOCKLEN
设置块长度
CMD17
READ_SINGLE_BLOCK
读单个块
CMD18
READ_MULTIPLE_BLOCK
读多个块
CMD24
WRITE_BLOCK
写单个块
CMD25
WRITE_MULTIPLE_BLOCK
写多个块

2.3.3 SD 卡特有命令(ACMD)

SD 卡有一些特殊的应用命令(Application-Specific Commands),需要先发送 CMD55 才能发送:

ACMD
名称
用途
ACMD41
SD_SEND_OP_COND
SD 卡操作条件
ACMD51
SEND_SCR
发送 SD 配置寄存器
ACMD6
SET_BUS_WIDTH
设置总线宽度

2.4 核心数据结构详解

2.4.1 struct mmc_host - 主机控制器描述

structmmc_host {structdevice       *parent;// 父设备(平台设备)structdeviceclass_dev;// 类设备,用于用户空间访问conststructmmc_host_ops *ops;// 主机操作函数指针集structmmc_iosios;// 当前 I/O 设置(时钟、电压、总线宽度)    u32         f_min;                  // 最小时钟频率(Hz)    u32         f_max;                  // 最大时钟频率(Hz)    u32         f_init;                 // 初始化时钟频率(通常 400kHz)unsignedlong   caps;               // 主机能力标志(支持的功能)unsignedlong   caps2;              // 扩展能力标志structmmc_card *card;// 当前连接的卡(最多一张)structrequest_queue *queue;// 块设备请求队列structgendisk  *disk;// 块设备描述符structmmc_host_statsstats;// 统计信息structwork_structdetect;// 卡检测工作队列structdelayed_workrw_timeout_work;// 读写超时工作/* 私有数据指针,主机驱动可以存储自己的数据 */void *private;/* 电源管理相关 */structmmc_pwrseq *pwrseq;// 电源序列unsignedint power_mode;             // 当前电源模式/* 队列相关 */structmmc_queue *mq;// MMC 请求队列spinlock_t    queue_lock;           // 队列锁/* 中断相关 */int         irq;                    // 中断号structtasklet_structcomplete;// 完成 tasklet    ...};

关键字段详解:

字段
说明
ops
指向主机驱动实现的操作函数,如 request、set_ios 等
ios
当前的 I/O 设置,包括时钟频率、电压级别、总线宽度、电源模式
caps
能力标志,如 MMC_CAP_4_BIT_DATA(支持4位总线)、MMC_CAP_SD_HIGHSPEED(支持高速模式)等
card
当前连接的卡,如果没有卡则为 NULL
queue
请求队列,用于接收来自块层的 I/O 请求
mq
MMC 专用的请求队列,管理 mmc_request

2.4.2 struct mmc_ios - I/O 设置

structmmc_ios {unsignedint    clock;              // 当前时钟频率(Hz)unsigned short  vdd;                // 电压级别(见 MMC_VDD_* 定义)unsignedchar   bus_mode;           // 总线模式(开漏/推挽)unsignedchar   chip_select;        // 片选信号unsignedchar   power_mode;         // 电源模式(关/低功耗/正常)unsignedchar   bus_width;          // 总线宽度(1/4/8位)unsignedchar   timing;             // 时序模式(legacy/high-speed等)unsignedchar   signal_voltage;     // 信号电压(1.8V/3.3V)unsignedchar   drv_type;           // 驱动类型(A/B/C/D)};

2.4.3 struct mmc_card - 卡描述

structmmc_card {structmmc_host   *host;// 所属主机控制器structdevicedev;// 设备对象unsignedint      rca;              // 相对卡地址(卡识别后分配)unsignedint      type;             // 卡类型(MMC/SD/SDIO/组合卡)structmmc_cidcid;// 卡唯一标识(Card ID)structmmc_csdcsd;// 卡特定数据(Card Specific Data)structmmc_scrscr;// SD 卡配置寄存器(SD Configuration Register)structmmc_ext_csdext_csd;// 扩展 CSD(eMMC 专用)unsignedlong    state;             // 卡状态标志unsignedint    flags;              // 卡特性标志structmmc_card_data *data;// 私有数据/* 分区信息 */unsignedint      nr_erase_groups;  // 擦除组数量unsignedint      erase_group_size; // 擦除组大小/* 速度信息 */unsignedint      max_dtr;          // 最大数据传输速率unsignedint      actual_dtr;       // 当前数据传输速率/* 块设备信息 */unsignedint      block_bits;       // 块大小的位数(如 9 表示 512 字节)sector_t         capacity;          // 总容量(扇区数)    ...};

卡类型常量:

常量
值
说明
MMC_TYPE_MMC
0
MMC 卡
MMC_TYPE_SD
1
SD 卡
MMC_TYPE_SDIO
2
SDIO 卡
MMC_TYPE_SD_COMBO
3
SD 组合卡(同时支持 SD 和 SDIO)

2.4.4 struct mmc_request - 请求描述

structmmc_request {structmmc_command *cmd;// 命令描述符structmmc_command *stop;// 停止命令(用于多块传输)structmmc_data   *data;// 数据描述符structmmc_host  *host;// 目标主机structmmc_card  *card;// 目标卡void (*done)(struct mmc_request *); // 请求完成回调函数unsignedint      retries;          // 重试次数unsignedint      error;            // 错误码/* 统计信息 */unsignedlong    start_time;        // 请求开始时间unsignedint      nr_sectors;       // 扇区数unsignedint      bytes_xfered;     // 已传输字节数    ...};

2.4.5 struct mmc_command - 命令描述

structmmc_command {    u32         opcode;                 // 命令操作码(CMDx)    u32         arg;                    // 命令参数structmmc_data   *data;// 关联的数据传输structmmc_request *mrq;// 所属请求unsignedint      flags;             // 命令标志/* 响应 */    u32         resp[4];               // 响应数据(最多4个32位字)int         error;                  // 错误码/* 状态 */unsignedint      retries;          // 重试次数unsignedint      busy_timeout;      // 忙等待超时时间    ...};

命令标志:

标志
说明
MMC_RSP_NONE
无响应
MMC_RSP_R1
R1 类型响应
MMC_RSP_R1B
R1B 类型响应(带忙信号)
MMC_RSP_R2
R2 类型响应(CID/CSD)
MMC_RSP_R3
R3 类型响应(OCR)
MMC_RSP_R4
R4 类型响应(SDIO)
MMC_RSP_R5
R5 类型响应(SDIO)
MMC_RSP_R6
R6 类型响应(RCA)
MMC_RSP_R7
R7 类型响应(接口条件)
MMC_CMD_ADTC
带数据的命令

2.4.6 struct mmc_data - 数据描述

structmmc_data {unsignedint      timeout_ns;       // 超时时间(纳秒)unsignedint      blksz;            // 块大小(字节)unsignedint      blocks;           // 块数量unsignedint      flags;             // 数据标志(读/写等)structscatterlist *sg;// 分散/聚集列表unsignedint      sg_len;           // SG 元素数量structmmc_request *mrq;// 所属请求/* 状态 */int         error;                  // 错误码unsignedint      bytes_xfered;     // 已传输字节数/* DMA 相关 */dma_addr_t       dma_addr;          // DMA 地址unsignedint      dma_len;          // DMA 长度    ...};

数据标志:

标志
说明
MMC_DATA_READ
读操作
MMC_DATA_WRITE
写操作
MMC_DATA_STREAM
流式传输
MMC_DATA_MULTI
多块传输

2.4.7 数据结构关系图

2.5 MMC 请求处理流程详解

2.6 卡识别与初始化流程

卡的识别和初始化是一个复杂的过程,包含多个阶段:

初始化阶段详解:

阶段
命令
目的
1. 复位
CMD0
将卡置于空闲状态
2. 电压检测
CMD8
检查卡支持的电压范围
3. 操作条件
CMD1/ACMD41
发送操作条件,等待卡就绪
4. 获取CID
CMD2
获取卡唯一标识
5. 获取RCA
CMD3
获取相对卡地址
6. 获取CSD
CMD9
获取卡特定数据(容量等)
7. 选择卡
CMD7
选中卡进行后续操作
8. 获取EXT_CSD
CMD8
eMMC 卡获取扩展信息
9. 配置
ACMD6/ACMD51
设置总线宽度、获取配置

2.7 关键 API 详解

2.7.1 主机控制器管理

API
功能
详细说明
mmc_alloc_host(size, parent)
分配主机控制器
分配 mmc_host 结构体及私有数据空间
mmc_add_host(host)
注册主机控制器
将主机添加到系统,创建设备节点
mmc_remove_host(host)
注销主机控制器
从系统移除主机,清理资源
mmc_free_host(host)
释放主机控制器
释放分配的内存
mmc_of_parse(host)
解析设备树
从设备树获取配置信息

2.7.2 命令和数据传输

API
功能
详细说明
mmc_wait_for_req(host, mrq)
同步发送请求
发送请求并等待完成
mmc_start_request(host, mrq)
异步发送请求
发送请求,通过回调通知完成
mmc_send_cmd(host, cmd, timeout)
发送单个命令
发送命令并等待响应
mmc_read_blocks(card, buf, blk, cnt)
读块
读取指定数量的块
mmc_write_blocks(card, buf, blk, cnt)
写块
写入指定数量的块

2.7.3 卡管理

API
功能
详细说明
mmc_attach(host)
识别并初始化卡
执行卡识别流程
mmc_detach(host)
移除卡
清理卡相关资源
mmc_rescan(host)
重新扫描卡
检测卡插拔状态
mmc_sd_init_card(host)
初始化 SD 卡
SD 卡特定初始化
mmc_init_card(host)
初始化 MMC 卡
MMC 卡特定初始化

2.7.4 电源和时钟管理

API
功能
详细说明
mmc_set_ios(host)
设置 I/O 参数
配置时钟、电压、总线宽度
mmc_set_clock(host, freq)
设置时钟频率
动态调整时钟
mmc_bus_reset(host)
总线复位
复位 MMC 总线
mmc_power_up(host)
上电
为卡供电
mmc_power_off(host)
断电
关闭卡电源

三、core 层核心代码详解

3.1 core/core.c - 核心功能实现

3.1.1 命令发送流程

intmmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq){int ret;    mrq->done = mmc_wait_done;  // 设置完成回调/* 发送请求 */    mmc_start_request(host, mrq);/* 等待完成(可中断) */    wait_event_interruptible(host->wq, mrq->completed);    ret = mrq->error;return ret;}staticvoidmmc_start_request(struct mmc_host *host, struct mmc_request *mrq){structmmc_queue *mq = host->mq;/* 设置请求的主机和卡 */    mrq->host = host;    mrq->card = host->card;/* 调用主机驱动的请求函数 */if (host->ops->request) {        host->ops->request(host, mrq);    }}

3.1.2 卡识别流程

intmmc_attach_mmc(struct mmc_host *host){structmmc_card *card;int err;/* 分配卡结构体 */    card = mmc_alloc_card(host);if (!card)return -ENOMEM;/* 发送CMD0复位 */    err = mmc_send_cmd(host, MMC_CMD_GO_IDLE_STATE, 0, MMC_RSP_NONE);if (err)goto err;/* 发送CMD1直到卡就绪 */for (i = 0; i < MMC_MAX_OP_COND_RETRIES; i++) {        err = mmc_send_cmd(host, MMC_CMD_SEND_OP_COND, ocr, MMC_RSP_R3);if (!(err & MMC_CARD_BUSY))break;        msleep(10);    }/* 获取CID */    err = mmc_send_cmd(host, MMC_CMD_ALL_SEND_CID, 0, MMC_RSP_R2);if (err)goto err;/* 获取RCA */    err = mmc_send_cmd(host, MMC_CMD_SEND_REL_ADDR, 0, MMC_RSP_R6);if (err)goto err;/* 获取CSD */    err = mmc_send_cmd(host, MMC_CMD_SEND_CSD, card->rca << 16, MMC_RSP_R2);if (err)goto err;/* 选择卡 */    err = mmc_send_cmd(host, MMC_CMD_SELECT_CARD, card->rca << 16, MMC_RSP_R1B);if (err)goto err;/* 完成初始化 */    mmc_attach_card(host, card);return0;err:    mmc_free_card(card);return err;}

3.2 core/block.c - 块设备接口

3.2.1 请求处理

staticblk_status_tmmc_blk_issue_rq(struct blk_mq_hw_ctx *hctx,const struct blk_mq_queue_data *bd){structrequest *req = bd->rq;structmmc_queue *mq = hctx->queue->queuedata;structmmc_card *card = mq->card;structmmc_blk_request *brq;/* 分配请求 */    brq = mmc_blk_get_request(mq);if (!brq)return BLK_STS_RESOURCE;/* 设置请求参数 */    brq->mrq.cmd->opcode = rq_data_dir(req) == READ ?        MMC_CMD_READ_MULTIPLE_BLOCK : MMC_CMD_WRITE_MULTIPLE_BLOCK;    brq->mrq.cmd->arg = blk_rq_pos(req) * 512;    brq->mrq.data->blksz = 512;    brq->mrq.data->blocks = blk_rq_sectors(req);    brq->mrq.data->flags = rq_data_dir(req) == READ ?        MMC_DATA_READ : MMC_DATA_WRITE;/* 映射SG列表 */    brq->mrq.data->sg = blk_rq_sg(req);    brq->mrq.data->sg_len = blk_rq_sg_nents(req);/* 发送请求 */    mmc_start_request(card->host, &brq->mrq);return BLK_STS_OK;}

3.2.2 请求完成

voidmmc_blk_complete_rq(struct mmc_request *mrq){structmmc_blk_request *brq = container_of(mrq, structmmc_blk_request, mrq);structrequest *req = brq->req;/* 设置完成状态 */if (mrq->error) {        blk_mq_end_request(req, BLK_STS_IOERR);    } else {        blk_mq_end_request(req, BLK_STS_OK);    }/* 释放请求 */    mmc_blk_put_request(brq);}

3.3 core/host.c - 主机控制器管理

3.3.1 主机分配和注册

struct mmc_host *mmc_alloc_host(int extra, struct device *parent){structmmc_host *host;size_t size;/* 计算所需内存大小 */    size = sizeof(struct mmc_host) + extra;/* 分配并清零内存 */    host = kzalloc(size, GFP_KERNEL);if (!host)returnNULL;/* 初始化设备 */    host->parent = parent;    device_initialize(&host->class_dev);    host->class_dev.parent = parent;/* 初始化工作队列 */    INIT_DELAYED_WORK(&host->detect, mmc_rescan);/* 初始化定时器 */    setup_timer(&host->watchdog_timer, mmc_watchdog_timer, (unsignedlong)host);return host;}intmmc_add_host(struct mmc_host *host){int err;/* 设置默认时钟范围 */if (!host->f_min)        host->f_min = MMC_MIN_FREQ;if (!host->f_max)        host->f_max = MMC_MAX_FREQ;/* 注册类设备 */    err = device_add(&host->class_dev);if (err)return err;/* 启动卡检测 */    schedule_delayed_work(&host->detect, 0);return0;}

3.4 core/bus.c - 总线驱动

3.4.1 设备匹配和探测

staticintmmc_bus_match(struct device *dev, struct device_driver *drv){structmmc_card *card = mmc_dev_to_card(dev);structmmc_driver *drv = to_mmc_driver(drv);/* 检查驱动是否支持此卡类型 */if (drv->id_table) {conststructmmc_device_id *id;for (id = drv->id_table; id->name[0]; id++) {if (!strcmp(id->name, mmc_card_name(card)))return1;        }    }return0;}staticintmmc_bus_probe(struct device *dev){structmmc_card *card = mmc_dev_to_card(dev);structmmc_driver *drv = to_mmc_driver(dev->driver);if (drv->probe)return drv->probe(card);return0;}staticstructbus_typemmc_bus_type = {    .name       = "mmc",    .match      = mmc_bus_match,    .probe      = mmc_bus_probe,    .remove     = mmc_bus_remove,};

四、主机驱动开发详解

4.1 DesignWare MMC 控制器驱动

DesignWare MMC 控制器是一种通用的 MMC/SD/SDIO 控制器,被许多芯片厂商使用。

4.1.1 驱动结构

structdw_mci {structmmc_host   *mmc;// MMC 主机structdevice     *dev;// 设备void __iomem     *regs;          // 寄存器基地址int              irq;            // 中断号/* 时钟 */structclk       *ciu_clk;// 控制器时钟structclk       *biu_clk;// 总线接口时钟structclk       *sample_clk;// 采样时钟/* DMA */structdma_chan  *dma_rx;// 接收 DMA 通道structdma_chan  *dma_tx;// 发送 DMA 通道/* 请求处理 */structmmc_request *mrq;// 当前请求structmmc_request *pending_mrq;// 挂起请求/* 状态 */unsignedint     cur_slot;        // 当前槽位bool            pending;          // 是否有挂起请求/* 锁 */spinlock_t       lock;            // 自旋锁/* 私有数据 */conststructdw_mci_drv_data *drv_data;};

4.1.2 平台驱动注册

staticconststructof_device_iddw_mci_rockchip_match[] = {    { .compatible = "rockchip,rk3568-dw-mshc",      .data = &rockchip_drv_data },    { .compatible = "rockchip,rk3588-dw-mshc",      .data = &rockchip_drv_data },    { }};MODULE_DEVICE_TABLE(of, dw_mci_rockchip_match);staticstructplatform_driverdw_mci_rockchip_pltfm_driver = {    .probe      = dw_mci_pltfm_register,    .remove     = dw_mci_pltfm_remove,    .driver     = {        .name       = "dwmmc_rockchip",        .probe_type = PROBE_PREFER_ASYNCHRONOUS,        .of_match_table = dw_mci_rockchip_match,        .pm     = &dw_mci_rockchip_dev_pm_ops,    },};module_platform_driver(dw_mci_rockchip_pltfm_driver);

4.1.3 Probe 函数

staticintdw_mci_pltfm_register(struct platform_device *pdev){structdw_mci *host;structresource *res;int err;/* 分配主机结构 */    host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL);if (!host)return -ENOMEM;/* 获取寄存器地址 */    res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dwmmc");    host->regs = devm_ioremap_resource(&pdev->dev, res);if (IS_ERR(host->regs))return PTR_ERR(host->regs);/* 获取中断 */    host->irq = platform_get_irq(pdev, 0);if (host->irq < 0)return host->irq;/* 获取时钟 */    host->ciu_clk = devm_clk_get(&pdev->dev, "ciu");    host->biu_clk = devm_clk_get(&pdev->dev, "biu");/* 初始化 MMC 主机 */    host->mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), &pdev->dev);if (!host->mmc)return -ENOMEM;/* 设置主机操作函数 */    host->mmc->ops = &dw_mci_ops;/* 设置能力标志 */    host->mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA |                      MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;/* 注册中断 */    err = devm_request_irq(&pdev->dev, host->irq, dw_mci_interrupt,                           IRQF_SHARED, "dwmmc", host);if (err)return err;/* 添加主机到系统 */    err = mmc_add_host(host->mmc);if (err)return err;    platform_set_drvdata(pdev, host);return0;}

4.1.4 请求处理

staticvoiddw_mci_request(struct mmc_host *mmc, struct mmc_request *mrq){structdw_mci *host = mmc_priv(mmc);unsignedlong flags;    spin_lock_irqsave(&host->lock, flags);/* 如果当前有请求在处理,加入pending队列 */if (host->cur_slot) {        host->pending = true;        host->pending_mrq = mrq;        spin_unlock_irqrestore(&host->lock, flags);return;    }/* 开始处理请求 */    dw_mci_start_request(host, mrq);    spin_unlock_irqrestore(&host->lock, flags);}staticvoiddw_mci_start_request(struct dw_mci *host, struct mmc_request *mrq){structmmc_command *cmd = mrq->cmd;structmmc_data *data = mrq->data;/* 保存当前请求 */    host->mrq = mrq;/* 设置命令 */    dw_mci_set_cmd(host, cmd);/* 如果有数据传输 */if (data) {/* 配置DMA */        dw_mci_prep_dma(host, data);/* 使能DMA */        dw_mci_enable_dma(host);    }/* 发送命令 */    dw_mci_send_cmd(host);}

4.1.5 中断处理

staticirqreturn_tdw_mci_interrupt(int irq, void *dev_id){structdw_mci *host = dev_id;    u32 status;/* 读取中断状态 */    status = dw_mci_readl(host, DW_MCI_STATUS);/* 清除中断 */    dw_mci_writel(host, status, DW_MCI_STATUS);/* 检查命令完成 */if (status & DW_MCI_INT_CMD_DONE) {        dw_mci_cmd_complete(host);    }/* 检查数据完成 */if (status & DW_MCI_INT_DATA_DONE) {        dw_mci_data_complete(host);    }/* 检查错误 */if (status & DW_MCI_INT_ERROR) {        dw_mci_error(host, status);    }return IRQ_HANDLED;}staticvoiddw_mci_cmd_complete(struct dw_mci *host){structmmc_request *mrq = host->mrq;structmmc_command *cmd = mrq->cmd;/* 读取响应 */    cmd->resp[0] = dw_mci_readl(host, DW_MCI_RESPONSE0);    cmd->resp[1] = dw_mci_readl(host, DW_MCI_RESPONSE1);    cmd->resp[2] = dw_mci_readl(host, DW_MCI_RESPONSE2);    cmd->resp[3] = dw_mci_readl(host, DW_MCI_RESPONSE3);/* 如果没有数据传输,完成请求 */if (!mrq->data) {        dw_mci_request_complete(host);    }}staticvoiddw_mci_request_complete(struct dw_mci *host){structmmc_request *mrq = host->mrq;/* 调用完成回调 */if (mrq->done)        mrq->done(mrq);/* 检查pending请求 */if (host->pending) {        host->pending = false;        dw_mci_start_request(host, host->pending_mrq);    } else {        host->cur_slot = 0;    }}

4.2 主机操作函数集

staticconststructmmc_host_opsdw_mci_ops = {    .request        = dw_mci_request,    .set_ios        = dw_mci_set_ios,    .get_ro         = dw_mci_get_ro,    .card_present   = dw_mci_card_present,    .enable_sdio_irq = dw_mci_enable_sdio_irq,#ifdef CONFIG_PM    .suspend        = dw_mci_suspend,    .resume         = dw_mci_resume,#endif};staticvoiddw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios){structdw_mci *host = mmc_priv(mmc);/* 设置时钟频率 */if (ios->clock != host->cur_clk) {        clk_set_rate(host->ciu_clk, ios->clock);        host->cur_clk = ios->clock;    }/* 设置总线宽度 */if (ios->bus_width != host->cur_bus_width) {        dw_mci_set_bus_width(host, ios->bus_width);        host->cur_bus_width = ios->bus_width;    }/* 设置电源模式 */if (ios->power_mode != host->cur_power_mode) {        dw_mci_set_power_mode(host, ios->power_mode);        host->cur_power_mode = ios->power_mode;    }}

五、编译测试

5.1 内核配置

要启用 MMC/SD 支持,需要在内核配置中启用以下选项:

Device Drivers --->  <*> MMC/SD/SDIO card support --->    <*> MMC block device driver    [*] Secure Digital Host Controller Interface support    <*> Arasan Multimedia Card Interface support    <*> Synopsys Designware Memory Card Interface support    <*> Renesas SDHI SD/SDIO controller support    <*> MMC host driver debugging

关键配置选项:

配置选项
说明
CONFIG_MMC
启用 MMC/SD/SDIO 支持
CONFIG_MMC_BLOCK
启用 MMC 块设备驱动
CONFIG_MMC_DW
启用 DesignWare MMC 控制器支持
CONFIG_MMC_DW_ROCKCHIP
启用瑞芯微 DesignWare MMC 支持
CONFIG_MMC_SDHCI
启用 SDHCI 控制器支持
CONFIG_MMC_DEBUG
启用调试信息

5.2 编译

# 配置内核make ARCH=arm64 menuconfig# 编译内核make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j$(nproc)# 编译模块make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- modules -j$(nproc)# 安装模块make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- INSTALL_MOD_PATH=./modules modules_install

5.3 加载测试

# 查看设备树节点ls /sys/bus/platform/devices/ | grep mmc# 查看已加载的 MMC 模块lsmod | grep mmc# 如果驱动是模块形式,手动加载insmod dw_mmc_pltfm.koinsmod dw_mmc_rockchip.ko# 查看内核日志dmesg | tail -30

5.4 验证设备识别

# 查看块设备lsblk# 查看 MMC 设备ls -la /dev/mmcblk*# 查看分区信息cat /proc/partitions# 查看卡信息cat /sys/class/mmc_host/mmc0/mmc0:*/cidcat /sys/class/mmc_host/mmc0/mmc0:*/csd# 挂载测试mkdir -p /mnt/sdcardmount /dev/mmcblk0p1 /mnt/sdcardls /mnt/sdcard

5.5 性能测试

# 读取测试dd if=/dev/mmcblk0 of=/dev/null bs=1M count=100 conv=fsync# 写入测试dd if=/dev/zero of=/tmp/testfile bs=1M count=100dd if=/tmp/testfile of=/dev/mmcblk0 bs=1M count=100 conv=fsync# 使用 hdparm 测试hdparm -t /dev/mmcblk0# 使用 fio 进行基准测试fio --name=mmc_test --filename=/dev/mmcblk0 --size=512M --bs=4k --rw=read --iodepth=32 --direct=1

六、排坑总结

6.1 常见编译错误

错误
原因
解决方案
error: linux/mmc/host.h: No such file or directory
没有启用 MMC 支持
确保 CONFIG_MMC=y
undefined reference to 'mmc_alloc_host'
缺少 MMC 核心模块
确保 CONFIG_MMC=y
macro 'MMC_CAP_8_BIT_DATA' undeclared
内核版本差异
检查内核版本,使用正确的能力标志
dma_mapping_error() failed
DMA 配置问题
检查 DMA mask 设置和设备树配置

6.2 常见运行时问题

问题一:卡无法识别

现象:dmesg 显示 "mmc0: error -110 whilst initialising SD card"

原因分析:

  • 电源电压配置错误
  • 时钟频率过高
  • 卡检测引脚配置错误
  • 卡本身有问题

解决方案:

// 检查电源配置structmmc_iosios = {0};ios.vdd = MMC_VDD_32_33 | MMC_VDD_33_34;mmc_set_ios(host, &ios);// 降低初始化时钟host->f_init = 400000;  // 设置为 400kHz// 检查卡检测引脚if (!mmc_card_present(host)) {    dev_warn(host->dev, "No card present\n");return -ENODEV;}

问题二:传输速度慢

现象:读取速度远低于预期

原因分析:

  • 总线宽度配置为 1-bit
  • 时钟频率没有提升到高速模式
  • DMA 没有启用
  • I/O 调度器配置不当

解决方案:

// 启用 4-bit 或 8-bit 总线host->caps |= MMC_CAP_4_BIT_DATA;// 设置最大时钟频率host->f_max = 150000000;  // 150MHz// 启用 DMAhost->caps |= MMC_CAP_DMA;// 使用 noop 调度器(对于闪存设备更合适)echo noop > /sys/block/mmcblk0/queue/scheduler

问题三:写入失败

现象:dd 写入时出现 I/O 错误

原因分析:

  • 写保护开关开启
  • 卡已满或只读
  • 坏块问题
  • 电压不稳定

解决方案:

// 检查写保护状态if (mmc_get_ro(host)) {    dev_warn(host->dev, "Card is read-only\n");return -EROFS;}// 检查卡状态if (card->state & MMC_CARD_READ_ONLY) {    dev_warn(host->dev, "Card is locked\n");return -EROFS;}// 尝试重新初始化mmc_attach(host);

问题四:热插拔问题

现象:卡拔出后再插入无法识别

原因分析:

  • 热插拔中断没有正确配置
  • 卡检测工作队列没有正确调度
  • 电源没有正确复位

解决方案:

// 确保热插拔中断已注册request_irq(host->irq, dw_mci_interrupt, IRQF_TRIGGER_RISING, "mmc", host);// 调度卡检测schedule_delayed_work(&host->detect, HZ);// 复位卡mmc_bus_reset(host);

6.3 版本兼容性问题

内核版本
变化
影响
5.10+
blk-mq 成为默认
需要使用 blk-mq API
5.15+
mmc_alloc_host
 接口变化
需要检查参数
6.0+
DMA 映射 API 变化
需要更新 DMA 相关代码
6.10+
电源管理优化
需要检查 PM 回调

七、总结延伸

7.1 知识点回顾

  1. MMC 子系统架构

    • 三层架构:核心层、主机驱动层、SDIO 层
    • 分层设计提高代码复用性
    • 通过 ops 指针实现硬件抽象
  2. 核心数据结构

    • struct mmc_host:主机控制器描述
    • struct mmc_card:卡描述
    • struct mmc_request:请求描述
    • struct mmc_command:命令描述
    • struct mmc_data:数据描述
  3. 请求处理流程

    • 用户空间 → 文件系统 → 通用块层 → MMC 核心层 → 主机驱动 → 硬件
    • 使用异步回调机制
    • 支持多请求排队
  4. 卡初始化流程

    • 复位 → 电压检测 → 获取 CID → 获取 RCA → 获取 CSD → 选择卡 → 配置

7.2 拓展学习方向

📚 进阶内容

  1. 深入理解 MMC 协议

    • 学习 SD Specifications Part 1 Physical Layer
    • 理解 UHS-I/UHS-II 高速模式
    • 研究 DDR50/DDR104 模式
  2. 探索 eMMC 驱动

    • eMMC 特有功能(BOOT 分区、RPMB)
    • EXT_CSD 寄存器详解
    • 分区管理和命名空间
  3. 研究 SD Express

    • PCIe 接口的 SD 卡
    • NVMe over PCIe
    • 高性能存储优化
  4. 分析 SDIO 驱动

    • SDIO 设备枚举流程
    • SDIO 函数驱动开发
    • WiFi/蓝牙 SDIO 设备驱动
  5. 电源管理优化

    • 运行时 PM 支持
    • 低功耗模式配置
    • 唤醒事件处理

🔧 相关项目

  1. 为新的 MMC 控制器添加驱动支持
  2. 实现 MMC 测试工具
  3. 优化 MMC 驱动性能
  4. 添加 SD Express 支持
  5. 实现 eMMC BOOT 分区功能

最新文章

随机文章