RT-Thread SDIO/MMC 设备模型(DM):基于 Device Model 的宿主控制器驱动框架详解 操作系统嵌入式物联网嵌入式OSRTOS【免费下载链接】rt-threadRT-Thread is an open source IoT Real-Time Operating System (RTOS). https://rt-thread.github.io/rt-thread/项目地址https://gitcode.com/gh_mirrors/rt/rt-thread点击查看免费下载RT-Thread 的 SDIO/MMC 设备模型Device Model简称 DM是在传统 MMC/SD 核心层之上新增的一层驱动辅助框架它让 MMC/SD 宿主控制器Host Controller的命名、设备树OFW解析、电源供给vmmc/vqmmc以及 SDHCI 平台驱动全部纳入统一的 DM 设备管理体系中。本文以components/drivers/sdio目录下的实际源码为准讲解 DM 层的编译开关、核心 APIsdio_host_set_name、sdio_ofw_parse、sdio_regulator_*与 SDHCI 平台接入方式帮助读者理解如何基于 DM 快速编写可移植、可配置的 SD/MMC 宿主驱动。一、MMC/SD Core 与 DM 层的分层关系RT-Thread 的 SDIO 驱动体系分为两个层次MMC/SD 核心层由dev_mmcsd_core.c、dev_sd.c、dev_sdio.c、dev_mmc.c、dev_block.c组成只需要开启RT_USING_SDIO即可参与编译它实现了 SD/MMC/eMMC 协议栈与块设备接入。DM 辅助层由dev_sdio_dm.c以及dev_sdhci*.c、dev_regulator.c组成只有在开启RT_USING_DM后才会被构建系统加入。这一编译依赖关系可以直接在 components/drivers/sdio/SConscript 中看到当RT_USING_SDIO未开启时整个目录直接跳过开启后默认编译核心源文件当GetDepend([RT_USING_DM])为真时追加dev_sdio_dm.c若同时开启RT_USING_SDHCI则继续追加dev_sdhci.c、dev_sdhci_dm.c、dev_sdhci_host.c若同时开启RT_USING_REGULATOR则再追加dev_regulator.c。各特性的 Kconfig 开关与对应源码可汇总如下表特性Kconfig 开关典型配置对应源码仅RT_USING_DM时编译Host 命名、OFW 解析RT_USING_DMRT_USING_OFWdev_sdio_dm.cSDHCI 栈RT_USING_DMRT_USING_SDIORT_USING_SDHCIdev_sdhci.c、dev_sdhci_dm.c、dev_sdhci_host.cvmmc/vqmmc电源供给RT_USING_DMRT_USING_REGULATORdev_regulator.cSoC SDHCI / DW MMC 宿主RT_USING_DMRT_USING_SDIOsdio/host/*见 components/drivers/sdio/host/Kconfig注意host/Kconfig中可见RT_SDIO_DW_MMCSynopsys DesignWare MMC还额外依赖RT_USING_PINCTRL、RT_USING_RESET、RT_USING_REGULATOR并自动选中RT_USING_DEVICE_IPC与RT_USING_SYSTEM_WORKQUEUERT_SDIO_SDHCI_DWCMSHCSynopsys DWC MSHC SDHCI则依赖RT_USING_OFW并自动选中RT_USING_SDHCI。不使用 DM 时的替代方案若RT_USING_DM未开启框架不会编译任何dev_sdhci_*或sdio_regulator_*源码此时需要自行实现struct rt_mmcsd_host_ops并直接对接 SDIO 设备模型文档 描述的接口所有解析、命名、供电逻辑均由驱动自行处理。二、Host 命名sdio_host_set_namesdio_host_set_name(host, out_devname)是 DM 层提供的稳定 Host 命名接口实现位于 dev_sdio_dm.c。其核心行为是为host-name填充一个稳定的sd%u格式字符串命名策略分两级OFW 别名优先当RT_USING_OFW开启且host-ofw_node已设置时先调用rt_ofw_get_alias_id(host-ofw_node, mmc)尝试取设备树aliases { mmc0 ...; }中定义的别名序号使 Host 名与板级连线保持一致。原子自增兜底若别名解析失败返回负值则通过rt_atomic_add(uid, 1)分配一个单调递增的数字 id。为防止纯软件 Host 与已占用别名序号冲突首次分配时以rt_ofw_get_alias_last_id(mmc)为种子uid_min 别名最大索引 1。可选的out_devname参数会收到相同字符串的副本长度受RT_NAME_MAX限制方便驱动在 probe 阶段同时保存一份设备名。实际调用示例#include dev_sdio_dm.h int rt_sdio_host_probe(struct rt_platform_device *pdev) { struct rt_mmcsd_host *host; host rt_mmcsd_alloc_host(pdev-parent); if (!host) return -RT_ENOMEM; /* 尽早命名确保后续枚举时设备名稳定 */ sdio_host_set_name(host, RT_NULL); /* 填充 host-ops 等字段后注册到核心层 ... */ }从源码结构看该函数被设计为在宿主控制器的probe阶段调用而不是在首次数据传输时才命名以保证上层块设备名如sd0、sd1从一开始就是确定的。三、Open Firmware 解析sdio_ofw_parse当RT_USING_OFW开启时sdio_ofw_parse(dev_np, host)负责把设备树节点dev_np中的通用 MMC 控制器属性解析进struct rt_mmcsd_host。完整实现见 dev_sdio_dm.c可解析属性与对应行为如下表设备树属性解析效果bus-width取值为1/4/8分别设置host-flags中的MMCSD_BUSWIDTH_8/MMCSD_BUSWIDTH_41为默认不置位非法值打印LOG_E(Invalid \bus-width\ value %d)并返回-RT_EIOmax-frequency写入host-freq_maxnon-removable置位MMCSD_SUP_NONREMOVABLEcap-sdio-irq置位MMCSD_SUP_SDIO_IRQcap-sd-highspeed/cap-mmc-highspeed置位MMCSD_SUP_HIGHSPEEDmmc-ddr-3_3v/mmc-ddr-1_8v/mmc-ddr-1_2v分别置位MMCSD_SUP_DDR_3V3/MMCSD_SUP_DDR_1V8/MMCSD_SUP_DDR_1V2mmc-hs200-1_8v/mmc-hs200-1_2v置位MMCSD_SUP_HS200_1V8/MMCSD_SUP_HS200_1V2mmc-hs400-1_8v/mmc-hs400-1_2v置位MMCSD_SUP_HS400_1V8/MMCSD_SUP_HS400_1V2sd-uhs-sdr50/sd-uhs-sdr104/sd-uhs-ddr50置位MMCSD_SUP_SDR50/MMCSD_SUP_SDR104/MMCSD_SUP_DDR50此外sdio_ofw_parse还有两个固定副作用若host-ofw_node尚未赋值则补设为dev_np源码中即host-ofw_node ? : dev_np无条件在host-flags中强制置位MMCSD_MUTBLKWRITE多块写支持。当RT_USING_OFW未开启时dev_sdio_dm.h中提供了rt_inline的桩实现sdio_ofw_parse直接返回RT_EOK不做任何解析。解析时机必须在枚举开始前官方文档强调调用sdio_ofw_parse应当在填充struct rt_mmcsd_host的过程中、核心层开始枚举card enumeration之前完成。理由很直接——如果bus-width非法函数会返回-RT_EIO此时应把错误处理放在宿主驱动自己的probe函数中返回而不是拖到第一次事务时才报错。推荐的 DTS 节点示例mmc0: mmc10000000 { compatible vendor,sdhci; reg 0x10000000 0x1000; interrupts 0x2a; bus-width 4; max-frequency 50000000; non-removable; cap-sd-highspeed; };四、电源供给钩子sdio_regulator_*DM Regulatordev_regulator.c仅在RT_USING_DM与RT_USING_REGULATOR同时开启时编译Regulator 框架本身也依赖 DM。它围绕vmmc主电源与vqmmc信号电平电源两个供给展开相关 API 声明见 dev_sdio_dm.hAPI作用sdio_regulator_get_supply(dev, host)通过rt_regulator_get(dev, vmmc)/rt_regulator_get(dev, vqmmc)从设备获取两路电源引用并存入host-supply任一路获取失败返回错误指针时统一走_fail分支释放已获取的供给并返回错误sdio_regulator_set_ocr(host, supply, vdd_bit)把 OCR 位图换算为电压区间后调用rt_regulator_set_voltagevdd_bit为 0 时关闭电源rt_regulator_disable否则设置电压并在需要时使能sdio_regulator_set_vqmmc(host, ios)根据ios-signal_voltageMMCSD_SIGNAL_VOLTAGE_120/_180/_330切换 vqmmc 电压1.8V 切换失败时回退到 3.3V 逻辑sdio_regulator_enable_vqmmc(host)/sdio_regulator_disable_vqmmc(host)使能 / 失能 vqmmc带vqmmc_enabled状态跟踪sdio_regulator_supports_vqmmc_voltage(regulator, min, target, max)判断 regulator 是否支持给定电压区间或精确目标电压实现细节上ocrbitnum_to_vdd把 OCR 位号换算成电压位对应 1.65–1.95V 区间时取 1650–1950mV其余按1900mV (vdd_bit - ilog2(VDD_165_195)) * 100mV逐档递增。sdio_regulator_set_vqmmc的 3.3V 分支还会结合host-io_cfg.vdd计算允许的电压区间下限不低于 2.7V、上限不高于 3.6V并先尝试区间设置、失败后再尝试直接设置目标电压——这是为了兼容仅声明 1.8V 的 GPIO regulator。完整电源语义可进一步参考 SDIO Regulator 文档 与 Regulator 文档。五、SDHCI 平台驱动dev_sdhci_dm.cDM 专属RT_USING_SDHCI只在 DM 模式下有仓内实现。平台驱动直接使用 dev_sdhci_dm.c 提供的一组便捷接口完成标准 SDHCI 宿主接入接口声明见 dev_sdhci_dm.h接口作用rt_sdhci_pltfm_init(pdev, pdata, priv_size)依次执行rt_dm_dev_iomap(dev, 0)映射寄存器、rt_dm_dev_get_irq(dev, 0)获取中断、rt_sdhci_alloc_host分配宿主随后填充irq、ioaddr、hw_name来自rt_dm_dev_get_name、ops默认sdhci_pltfm_opsset_clock/set_bus_width/reset/set_uhs_signaling与quirks/quirks2来自pdatart_sdhci_pltfm_init_and_add_host(pdev, pdata, priv_size)一键完成initrt_sdhci_get_propertyrt_sdhci_init_host失败时自动rt_sdhci_pltfm_freert_sdhci_get_property(pdev)从设备树读取 SDHCI 特有 quirk 属性见下表rt_sdhci_pltfm_remove(pdev)检测寄存器是否全0xffffffff判断控制器是否已掉电执行rt_sdhci_uninit_host并释放rt_sdhci_pltfm_clk_get_max_clock(host)通过rt_clk_get_rate读取平台时钟频率作为最大时钟rt_sdhci_pltfm_priv(host)返回平台私有数据区host-private柔性数组rt_sdhci_get_property解析的属性与效果设备树属性效果sdhci,auto-cmd12置位RT_SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12sdhci,1-bit-only或bus-width 1置位RT_SDHCI_QUIRK_FORCE_1_BIT_DATAbroken-cd置位RT_SDHCI_QUIRK_BROKEN_CARD_DETECTIONno-1-8-v置位RT_SDHCI_QUIRK2_NO_1_8_Vclock-frequency写入pltfm_host-clockkeep-power-in-suspend置位host-mmc-pm_caps的MMC_PM_KEEP_POWERwakeup-source/enable-sdio-wakeuplegacy置位MMC_PM_WAKE_SDIO_IRQ典型的 DM SDHCI 平台驱动 probe 可以这样写#include dev_sdhci_dm.h static const struct rt_sdhci_pltfm_data my_pdata { .quirks RT_SDHCI_QUIRK_BROKEN_CARD_DETECTION, .quirks2 0, }; static int my_sdhci_probe(struct rt_platform_device *pdev) { return rt_sdhci_pltfm_init_and_add_host(pdev, my_pdata, sizeof(struct my_priv)); }SDHCI 栈的完整实现与寄存器级细节参见 SDHCI 文档。针对具体 SoCsdio/host/目录还提供了sdhci-dwcmshcSynopsys DWC MSHC与sdio-dwDesignWare MMC等现成宿主实现以及 PCI 总线上挂接 SDHCI/DW MMC 的sdhci-pci.c、sdio-dw-pci.c。六、BSP 驱动接入实战建议综合官方文档 dm.md 与源码编写 DM 模式下的宿主驱动时建议遵循以下要点解析先于卡片活动在填充struct rt_mmcsd_host的过程中调用sdio_ofw_parse且必须在核心层开始枚举之前对非法bus-width返回的-RT_EIO要在probe中处理不要拖到首次事务。优先使用稳定的 Host 名在 DTS 中声明aliases { mmc0 ...; }让sdio_host_set_name的命名与板级连线、用户态脚本保持一致纯软件 Host 仍依赖rt_ofw_get_alias_last_id(mmc)避免与已占用的别名序号冲突。电源供给按需声明vmmc/vqmmc依赖 DM Regulator二者缺一不可DTS 中通过vmmc-supply、vqmmc-supply指向对应的 regulator 节点。SDHCI 只走 DMRT_USING_SDHCI仅在 DM 下实现平台驱动应使用dev_sdhci_dm.c的rt_sdhci_pltfm_init、rt_dm_dev_iomap、rt_dm_dev_get_irq、rt_sdhci_get_property组合而不是自行编写寄存器初始化。七、关联文档与深入阅读DM 层源码与头文件components/drivers/sdio/dev_sdio_dm.c、components/drivers/sdio/dev_sdio_dm.h构建依赖components/drivers/sdio/SConscriptSDHCI DM 平台层components/drivers/sdio/dev_sdhci_dm.c、components/drivers/sdio/dev_sdhci_dm.hRegulator 钩子components/drivers/sdio/dev_regulator.cSoC 宿主 Kconfigcomponents/drivers/sdio/host/Kconfig协议栈入口SDIO 设备模型文档、SDHCI 文档、SDIO Regulator 文档基础机制总线/驱动绑定文档、OFW 运行时 API 文档赞分享操作系统嵌入式物联网嵌入式OSRTOS【免费下载链接】rt-threadRT-Thread is an open source IoT Real-Time Operating System (RTOS). https://rt-thread.github.io/rt-thread/项目地址https://gitcode.com/gh_mirrors/rt/rt-thread点击查看免费下载相关推荐RT-Thread SDIO / MMC 核心驱动dev_mmcsd_core架构与 API 详解RT Thread SDIO / MMC 核心驱动dev_mmcsd_core架构与 API 详解 RT Thread 的 SDIO / MMC core操作系统嵌入式物联网嵌入式OSRTOSRT-Thread DM 框架下的 NVMe 设备模型DM架构与移植指南RT Thread DM 框架下的 NVMe 设备模型DM架构与移植指南 RT Thread 的 Device ModelDM框架为存储类外设提供了一套操作系统嵌入式物联网嵌入式OSRTOSRT-Thread Mailbox 设备模型DM详解控制器注册、设备树绑定与 PIC 邮箱实战RT Thread Mailbox 设备模型DM详解控制器注册、设备树绑定与 PIC 邮箱实战 本文以 RT Thread 驱动框架DMDevice操作系统嵌入式物联网嵌入式OSRTOS上一篇解锁抖音数据价值Douyin_Spider工具全解析下一篇ComfyUI-AnimateDiff-Evolved 终极避坑指南10个技巧让AI动画生成更稳定创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考