Linux MMC驱动开发实战:从零开始搭建SD卡读写模块(Kernel 5.4版)
本文详细介绍了在Linux Kernel 5.4环境下开发MMC驱动的实战过程,从SD卡读写模块的搭建到Host控制器驱动的开发与优化。内容涵盖硬件配置、设备树设置、卡检测机制及常见问题解决方案,适合嵌入式Linux开发者提升SD卡驱动开发技能。
·
Linux MMC驱动开发实战:从零开始搭建SD卡读写模块(Kernel 5.4版)
在嵌入式Linux开发中,SD卡作为最常用的存储介质之一,其驱动稳定性直接影响产品可靠性。本文将基于Kernel 5.4源码,带你深入MMC子系统内部,从host控制器注册到block设备创建的完整流程,并分享实际开发中遇到的典型问题解决方案。
1. 开发环境准备与基础概念
1.1 硬件与工具链配置
推荐使用以下开发环境组合:
- 硬件平台:树莓派4B或i.MX6ULL开发板(自带SDIO控制器)
- 工具链:arm-linux-gnueabihf-gcc 8.3以上版本
- 调试工具:
mmc-utils:官方SD卡调试工具集devmem2:内存寄存器查看工具sysfs接口:/sys/kernel/debug/mmc*/下的调试节点
关键依赖安装命令:
sudo apt install build-essential libncurses-dev bison flex libssl-dev
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
1.2 MMC子系统架构速览
Linux内核中MMC子系统采用分层设计:
| 层级 | 组件 | 功能描述 |
|---|---|---|
| 硬件抽象层 | host控制器驱动 | 如sdhci-esdhc-imx(i.MX系列) |
| 核心层 | drivers/mmc/core | 提供总线注册、协议处理等基础服务 |
| 块设备层 | drivers/mmc/block | 实现块设备读写接口 |
典型SD卡初始化流程:
- 控制器探测(
mmc_alloc_host) - 卡检测(
mmc_detect_change) - 协议协商(
mmc_sd_init_card) - 块设备注册(
mmc_blk_probe)
2. Host控制器驱动开发
2.1 控制器注册关键代码
以i.MX6ULL平台为例,注册host控制器的核心步骤:
static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
{
struct sdhci_host *host;
struct esdhc_platform_data *boarddata;
// 1. 分配host结构体
host = sdhci_pltfm_init(pdev, &sdhci_esdhc_imx_pdata, 0);
// 2. 配置硬件特性
host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;
host->mmc->caps |= MMC_CAP_NEEDS_POLL;
// 3. 注册到MMC核心层
ret = sdhci_add_host(host);
if (ret)
goto err_add_host;
return 0;
}
常见问题排查:
- 时钟未启用:检查
clk_prepare_enable调用 - DMA配置错误:确认
dma-ranges设备树属性 - 电压不匹配:通过
mmc_ocr_voltages设置正确电压
2.2 设备树配置详解
典型SDIO控制器设备树节点:
usdhc1: mmc@2194000 {
compatible = "fsl,imx6ull-usdhc";
reg = <0x02194000 0x4000>;
interrupts = <GIC_SPI 22 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6UL_CLK_USDHC1>;
clock-names = "ipg", "ahb", "per";
bus-width = <4>;
vmmc-supply = <®_sd1_vmmc>;
status = "okay";
};
关键参数说明:
bus-width:4线或8线模式max-frequency:建议初始设为25MHzno-1-8-v:禁用1.8V低电压模式(兼容性选项)
3. SD卡初始化流程深度解析
3.1 卡检测机制实现
内核通过以下函数实现热插拔检测:
static void mmc_rescan(struct work_struct *work)
{
struct mmc_host *host = container_of(work, struct mmc_host, detect);
// 1. 发送CMD0复位卡
mmc_go_idle(host);
// 2. 发送CMD8检查电压范围
err = mmc_send_if_cond(host, host->ocr_avail);
// 3. 识别卡类型(SDv1/SDv2/SDHC)
if (!mmc_attach_sd(host))
return;
// 4. 初始化块设备
mmc_blk_alloc(host);
}
调试技巧:
- 使用
mmc_test工具强制触发重新检测:echo 1 > /sys/bus/mmc/devices/mmc0\:0001/uevent - 查看检测日志:
dmesg | grep mmc0
3.2 典型初始化失败场景
| 错误现象 | 可能原因 | 解决方案 |
|---|---|---|
| 卡无法识别 | 电源不稳定 | 增加电源滤波电容 |
| CMD5超时 | 卡处于睡眠模式 | 发送CMD0唤醒 |
| CRC校验错误 | 信号质量差 | 调整PCB走线阻抗 |
| 容量识别错误 | 不支持SDHC | 升级内核到4.19+ |
4. 块设备操作优化
4.1 请求队列调优
修改/sys/block/mmcblk0/queue/下参数提升性能:
# 调整调度器为deadline
echo deadline > /sys/block/mmcblk0/queue/scheduler
# 增大NR_requests值
echo 128 > /sys/block/mmcblk0/queue/nr_requests
# 启用写入缓存(需确保供电稳定)
echo 1 > /sys/block/mmcblk0/device/cache_ctrl
4.2 读写超时问题处理
在驱动代码中添加超时处理逻辑:
static void sdhci_esdhc_imx_timeout_timer(struct timer_list *t)
{
struct sdhci_host *host = from_timer(host, t, timeout_timer);
if (sdhci_readl(host, SDHCI_INT_STATUS) & SDHCI_INT_DATA_TIMEOUT) {
pr_warn("Detected SD card timeout, resetting controller\n");
sdhci_reset(host, SDHCI_RESET_CMD);
sdhci_reset(host, SDHCI_RESET_DATA);
}
}
实际项目中遇到频繁超时的情况,最终发现是SD卡槽接触不良导致。更换卡槽后稳定性显著提升,这也提醒我们在硬件设计阶段就要重视连接器的选型。
openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。
更多推荐
所有评论(0)