MMC(MultiMedia Card)和 SD(Secure Digital)卡是嵌入式系统中最常用的存储介质之一,广泛应用于:
这些设备都需要高性能、高可靠性的存储方案,而 MMC/SD 卡驱动就是连接硬件和文件系统的桥梁。
MMC/SD 驱动在内核中的作用:
MMC 子系统采用分层设计,从上到下分为:

2.2 核心层次详解
MMC 核心层是整个子系统的中枢,负责:
| 卡管理 | |
| 命令处理 | |
| 数据传输 | |
| 块设备接口 | |
| SDIO 支持 | |
| 电源管理 |
核心文件详解:
core/core.c | mmc_start_request()mmc_wait_for_req() | |
core/bus.c | mmc_bus_probe()mmc_bus_remove() | |
core/sd.c | mmc_sd_init_card()mmc_sd_setup_card() | |
core/mmc.c | mmc_init_card()mmc_setup_card() | |
core/block.c | mmc_blk_issue_rq()mmc_blk_complete_rq() | |
core/sdio.c | 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() |
主机驱动层负责与具体的 MMC 控制器硬件交互。不同芯片厂商的控制器差异较大,但都遵循相同的核心接口。
主要主机驱动:
dw_mmc.c | ||
dw_mmc-pltfm.c | ||
dw_mmc-rockchip.c | ||
sdhci.c | ||
sdhci-pltfm.c | ||
renesas_sdhi_internal_dmac.c | ||
mxc_mmc.c |
SDIO(Secure Digital Input Output)层支持 SDIO 设备(如 WiFi 模块、蓝牙模块等):
MMC/SD 卡使用命令-响应协议进行通信:
| BC | ||
| BCR | ||
| AC | ||
| ACR | ||
| ADTC |
| CMD0 | ||
| CMD1 | ||
| CMD2 | ||
| CMD3 | ||
| CMD6 | ||
| CMD7 | ||
| CMD8 | ||
| CMD9 | ||
| CMD10 | ||
| CMD12 | ||
| CMD13 | ||
| CMD16 | ||
| CMD17 | ||
| CMD18 | ||
| CMD24 | ||
| CMD25 |
SD 卡有一些特殊的应用命令(Application-Specific Commands),需要先发送 CMD55 才能发送:
| ACMD41 | ||
| ACMD51 | ||
| ACMD6 |
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 | |
ios | |
caps | |
card | |
queue | |
mq |
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)};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 | ||
MMC_TYPE_SD | ||
MMC_TYPE_SDIO | ||
MMC_TYPE_SD_COMBO |
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; // 已传输字节数 ...};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 | |
MMC_RSP_R1B | |
MMC_RSP_R2 | |
MMC_RSP_R3 | |
MMC_RSP_R4 | |
MMC_RSP_R5 | |
MMC_RSP_R6 | |
MMC_RSP_R7 | |
MMC_CMD_ADTC |
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 |


卡的识别和初始化是一个复杂的过程,包含多个阶段:
初始化阶段详解:
mmc_alloc_host(size, parent) | ||
mmc_add_host(host) | ||
mmc_remove_host(host) | ||
mmc_free_host(host) | ||
mmc_of_parse(host) |
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) |
mmc_attach(host) | ||
mmc_detach(host) | ||
mmc_rescan(host) | ||
mmc_sd_init_card(host) | ||
mmc_init_card(host) |
mmc_set_ios(host) | ||
mmc_set_clock(host, freq) | ||
mmc_bus_reset(host) | ||
mmc_power_up(host) | ||
mmc_power_off(host) |
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); }}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;}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;}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);}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;}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,};DesignWare MMC 控制器是一种通用的 MMC/SD/SDIO 控制器,被许多芯片厂商使用。
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;};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);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;}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);}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; }}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; }}要启用 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 | |
CONFIG_MMC_BLOCK | |
CONFIG_MMC_DW | |
CONFIG_MMC_DW_ROCKCHIP | |
CONFIG_MMC_SDHCI | |
CONFIG_MMC_DEBUG |
# 配置内核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# 查看设备树节点ls /sys/bus/platform/devices/ | grep mmc# 查看已加载的 MMC 模块lsmod | grep mmc# 如果驱动是模块形式,手动加载insmod dw_mmc_pltfm.koinsmod dw_mmc_rockchip.ko# 查看内核日志dmesg | tail -30# 查看块设备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# 读取测试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=1error: linux/mmc/host.h: No such file or directory | CONFIG_MMC=y | |
undefined reference to 'mmc_alloc_host' | CONFIG_MMC=y | |
macro 'MMC_CAP_8_BIT_DATA' undeclared | ||
dma_mapping_error() failed |
现象: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;}现象:读取速度远低于预期
原因分析:
解决方案:
// 启用 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);mmc_alloc_host | ||
MMC 子系统架构
核心数据结构
struct mmc_host:主机控制器描述struct mmc_card:卡描述struct mmc_request:请求描述struct mmc_command:命令描述struct mmc_data:数据描述请求处理流程
卡初始化流程
深入理解 MMC 协议
探索 eMMC 驱动
研究 SD Express
分析 SDIO 驱动
电源管理优化