本文介绍了O2Micro SD Retimer (BH201/BH202)驱动的重构过程,从紧耦合的嵌入式设计演进到完全独立的模块化架构。
1. 背景与问题
1.1 什么是SD Retimer?
SD Retimer是一种信号中继芯片,位于SD Host控制器和SD卡之间,主要功能包括:
1 2 3 4 5
| ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ SD Host │────▶│ SD Retimer │────▶│ SD Card │ │ (MTK/QCOM) │◀────│ (BH201) │◀────│ │ └─────────────┘ └─────────────┘ └─────────────┘ SoC侧 中继芯片 存储设备
|
核心价值:
- 🔌 信号中继:延长SD信号传输距离
- ⚡ 信号完整性:在SDR104/SDR50高速模式下保持信号质量
- 🔧 时序补偿:通过DLL进行采样点微调(Tuning)
1.2 原始架构的问题
原始驱动采用嵌入式架构,将Retimer代码通过预编译宏注入到SD Host驱动中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| #ifdef CONFIG_MMC_SDHCI_BH201 #include "sdhci-bayhub.h" #endif
struct msdc_host { #ifdef CONFIG_MMC_SDHCI_BH201 struct platform_device *pdev; struct ggc_platform_t ggc; unsigned long private[0] ____cacheline_aligned; #endif };
static const struct mmc_host_ops msdc_ops = { #ifdef CONFIG_MMC_SDHCI_BH201 .init_card = bht_load, .execute_tuning = sdhci_bht_execute_tuning, #else .init_card = msdc_init_card, .execute_tuning = msdc_execute_tuning, #endif };
#ifdef CONFIG_MMC_SDHCI_BH201 #include "sdhci-bayhub.c" #endif
|
问题分析:
| 问题 |
描述 |
影响 |
| 紧耦合 |
Retimer代码嵌入Host结构体 |
Host驱动维护困难,升级内核需重新适配 |
| DTS嵌套 |
Retimer节点作为MMC子节点 |
违反设备树设计原则 |
| 私有接口依赖 |
需要Host暴露reset_hw、set_buswidth等私有函数 |
每个平台都需修改Host驱动 |
| 文件散乱 |
sdhci-bayhub.c + sdhci-bayhub.h + 宿主修改 |
代码难以维护和移植 |
| 平台判断硬编码 |
strcmp(dev_name(), "11240000.mmc") |
可移植性差 |
1.3 MTK提出的优化方向
MTK在代码审查中提出了明确的改进要求:
- DTS独立: sd-retimer节点不应放在mtk mmc节点内部
- Probe时机: 不应在mtk probe中调用retimer probe,考虑mmc_add_host回调
- 接口精简: set_bus_width/get_clock应从core层获取,而非底层驱动
- 代码整合: 两份.c一份.h没有必要
2. 技术挑战
2.1 核心挑战:无侵入式Hook
需要在不修改Host驱动代码的前提下,实现以下Hook点:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| SD卡枚举流程 │ ├─▶ mmc_power_up() │ └─▶ [Hook点1] set_ios - Power状态变化时等待Retimer POR │ ├─▶ mmc_init_card() │ ├─▶ CMD0 (GO_IDLE) │ ├─▶ CMD8 (SEND_IF_COND) │ ├─▶ ACMD41 (SD_SEND_OP_COND) │ ├─▶ CMD2 (ALL_SEND_CID) │ ├─▶ CMD3 (SEND_RELATIVE_ADDR) │ │ └─▶ [Hook点2] init_card - 此时需要配置Retimer │ └─▶ ... │ └─▶ mmc_sd_init_uhs_card() └─▶ execute_tuning() └─▶ [Hook点3] 执行Retimer tuning
|
2.2 时序依赖问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| Host Probe完成 │ ▼ ┌─────────────────────┐ │ mmc_host已创建 │ │ 但card未枚举 │ └─────────────────────┘ │ │◀──── Retimer Probe应该在此时机 ▼ ┌─────────────────────┐ │ SD卡插入/枚举 │ │ 触发init_card │ └─────────────────────┘ │ ▼ ┌─────────────────────┐ │ Retimer初始化 │ │ 配置DLL/Tuning │ └─────────────────────┘
|
挑战:Retimer probe必须在Host probe之后,但在SD卡枚举之前完成。
2.3 接口获取问题
原始设计依赖Host暴露私有接口:
1 2 3 4 5 6
| struct host_priv_ops { void (*reset_hw)(struct mmc_host *mmc); void (*set_bus_width)(struct mmc_host *mmc, unsigned char width); unsigned int (*get_clock)(struct mmc_host *mmc); };
|
问题:不同平台的Host驱动内部实现各异,很难统一接口。
3. 方案对比
3.1 方案A:MMC Notifier机制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| static struct notifier_block o2_mmc_notifier = { .notifier_call = o2_mmc_notify, };
static int o2_mmc_notify(struct notifier_block *nb, unsigned long event, void *data) { struct mmc_host *mmc = data; switch (event) { case MMC_EVENT_HOST_ADDED: break; case MMC_EVENT_CARD_INSERTED: break; } }
|
优点:
缺点:
- ❌ Linux MMC子系统当前没有提供此类notifier接口
- ❌ 需要修改mmc core,不现实
3.2 方案B:mmc_add_host回调
1 2 3 4 5 6 7 8 9
| int mmc_add_host(struct mmc_host *host) { if (host->retimer_ops && host->retimer_ops->init) host->retimer_ops->init(host); }
|
优点:
缺点:
- ❌ 需要修改mmc_host结构体
- ❌ 需要修改mmc core代码
- ❌ 上游接受度低
3.3 方案C:包装mmc_host_ops(✅ 采用方案)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| struct o2_retimer { struct mmc_host *mmc; const struct mmc_host_ops *host_ops; struct mmc_host_ops wrapped_ops; };
static int o2_retimer_probe(struct platform_device *pdev) { mmc = get_mmc_from_phandle(np, "mmc"); o2->host_ops = mmc->ops; o2->wrapped_ops = *mmc->ops; o2->wrapped_ops.init_card = o2_wrapped_init_card; o2->wrapped_ops.execute_tuning = o2_wrapped_execute_tuning; o2->wrapped_ops.set_ios = o2_wrapped_set_ios; mmc->ops = &o2->wrapped_ops; }
|
优点:
- ✅ 完全不修改Host驱动代码
- ✅ 完全不修改MMC Core代码
- ✅ 使用标准Linux机制(deferred probe)
- ✅ 透明代理模式
缺点:
- 需要管理retimer实例与mmc_host的映射
3.4 方案对比总结
| 维度 |
方案A (Notifier) |
方案B (mmc_add_host) |
方案C (Wrap Ops) |
| 修改Core代码 |
需要添加notifier |
需要添加回调接口 |
不需要 |
| 修改Host驱动 |
不需要 |
不需要 |
不需要 |
| 时序控制 |
依赖事件顺序 |
精确 |
依赖deferred probe |
| 上游接受度 |
低 |
低 |
高 |
| 实现复杂度 |
中 |
中 |
低 |
4. 架构设计
4.1 新架构总览
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| ┌─────────────────────────────────────────────────────────────┐ │ Device Tree │ ├────────────────────────┬────────────────────────────────────┤ │ mmc1: mmc@11240000 │ sd_retimer: sd-retimer │ │ { │ { │ │ compatible = ...; │ compatible = "o2micro,sd- │ │ ... │ retimer"; │ │ } │ mmc = <&mmc1>; // phandle │ │ │ } │ └────────────────────────┴────────────────────────────────────┘ │ │ ▼ ▼ ┌─────────────────────┐ ┌─────────────────────────┐ │ Host Driver │ │ Retimer Driver │ │ (mtk-sd.c) │ │ (sd-retimer-o2.c) │ │ │ │ │ │ 不需要任何修改 │ │ 完全独立的platform │ │ │ │ driver │ └─────────────────────┘ └─────────────────────────┘ │ │ ▼ ▼ ┌─────────────────────────────────────────────────────────────┐ │ MMC Subsystem │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ mmc_host │ │ │ │ ┌───────────────────────────────────────────────┐ │ │ │ │ │ ops: &wrapped_ops ◀─── Retimer安装的包装ops │ │ │ │ │ └───────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────┘
|
4.2 Probe流程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| ┌──────────────────────────────────────────────────────────────┐ │ 系统启动 │ └──────────────────────────────────────────────────────────────┘ │ ▼ ┌──────────────────────────────────────────────────────────────┐ │ 1. Host Driver Probe (mtk-sd.c) │ │ - 初始化硬件 │ │ - 创建mmc_host │ │ - platform_set_drvdata(pdev, mmc) │ │ - mmc_add_host() │ └──────────────────────────────────────────────────────────────┘ │ ▼ ┌──────────────────────────────────────────────────────────────┐ │ 2. Retimer Driver Probe (sd-retimer-o2.c) │ │ ┌────────────────────────────────────────────────────┐ │ │ │ mmc_np = of_parse_phandle(np, "mmc", 0); │ │ │ │ mmc_pdev = of_find_device_by_node(mmc_np); │ │ │ │ mmc = platform_get_drvdata(mmc_pdev); │ │ │ │ │ │ │ │ if (!mmc) │ │ │ │ return -EPROBE_DEFER; // ◀── 关键:延迟probe │ │ │ └────────────────────────────────────────────────────┘ │ │ │ │ ┌────────────────────────────────────────────────────┐ │ │ │ // 保存并包装ops │ │ │ │ o2->host_ops = mmc->ops; │ │ │ │ o2->wrapped_ops = *mmc->ops; │ │ │ │ o2->wrapped_ops.init_card = o2_wrapped_init_card; │ │ │ │ o2->wrapped_ops.execute_tuning = o2_wrapped_...; │ │ │ │ o2->wrapped_ops.set_ios = o2_wrapped_set_ios; │ │ │ │ │ │ │ │ mmc->ops = &o2->wrapped_ops; // ◀── 安装包装ops │ │ │ └────────────────────────────────────────────────────┘ │ └──────────────────────────────────────────────────────────────┘ │ ▼ ┌──────────────────────────────────────────────────────────────┐ │ 3. SD卡枚举时自动触发Retimer初始化 │ │ mmc_init_card() → ops->init_card() → o2_wrapped_init_card│ │ ↓ │ │ o2_retimer_hw_init() │ └──────────────────────────────────────────────────────────────┘
|
4.3 Ops包装机制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
static void o2_wrapped_init_card(struct mmc_host *mmc, struct mmc_card *card) { struct o2_retimer *o2 = o2_get_retimer(mmc);
if (o2->host_ops && o2->host_ops->init_card) o2->host_ops->init_card(mmc, card);
o2_retimer_hw_init(o2, card); }
static int o2_wrapped_execute_tuning(struct mmc_host *mmc, u32 opcode) { struct o2_retimer *o2 = o2_get_retimer(mmc); return o2_execute_tuning(o2, opcode); }
|
4.4 接口层设计
原则:使用MMC Core公开接口,避免依赖Host私有实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
|
static inline unsigned int o2_get_host_clock(struct o2_retimer *o2) { return o2->mmc->ios.clock; }
static void o2_set_host_bus_width(struct o2_retimer *o2, unsigned char width) { struct mmc_host *mmc = o2->mmc; struct mmc_ios ios;
memcpy(&ios, &mmc->ios, sizeof(ios)); ios.bus_width = width;
if (o2->host_ops && o2->host_ops->set_ios) o2->host_ops->set_ios(mmc, &ios);
mmc->ios.bus_width = width; }
static void o2_reset_host_bus(struct o2_retimer *o2) { struct mmc_host *mmc = o2->mmc; struct mmc_ios ios; unsigned int saved_clock = mmc->ios.clock;
memcpy(&ios, &mmc->ios, sizeof(ios));
ios.clock = 0; o2->host_ops->set_ios(mmc, &ios); udelay(10);
ios.clock = saved_clock; o2->host_ops->set_ios(mmc, &ios); udelay(10); }
|
4.5 Retimer实例管理
由于mmc_host结构体无法扩展,需要建立映射关系:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| static LIST_HEAD(o2_retimer_list); static DEFINE_MUTEX(o2_retimer_mutex);
struct o2_retimer_entry { struct list_head list; struct mmc_host *mmc; struct o2_retimer *o2; };
static struct o2_retimer *o2_get_retimer(struct mmc_host *mmc) { struct o2_retimer_entry *entry; struct o2_retimer *o2 = NULL;
mutex_lock(&o2_retimer_mutex); list_for_each_entry(entry, &o2_retimer_list, list) { if (entry->mmc == mmc) { o2 = entry->o2; break; } } mutex_unlock(&o2_retimer_mutex);
return o2; }
|
5. DTS设计
5.1 新设计 vs 旧设计
旧设计(子节点方式):
1 2 3 4 5 6 7 8 9 10
| mmc1: mmc@11240000 { compatible = "mediatek,mt8189-mmc"; sd-retimer { compatible = "o2micro,sd-retimer"; }; };
|
新设计(phandle引用):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| mmc1: mmc@11240000 { compatible = "mediatek,mt8189-mmc"; };
sd_retimer: sd-retimer { compatible = "o2micro,sd-retimer"; mmc = <&mmc1>; status = "okay"; o2,drive-strength = <0x66602310>; o2,sdr50-sela-inject = <0x47F>; o2,sdr50-selb-inject = <0x00725777>; o2,sdr104-selb-inject = <0x77316200>; };
|
5.2 设计优势
| 维度 |
子节点方式 |
phandle方式 |
| 设备关系 |
隐式父子关系 |
显式引用关系 |
| Host驱动修改 |
需要解析子节点 |
无需修改 |
| Probe顺序 |
依赖子节点probe |
依赖deferred probe |
| 复用性 |
绑定特定Host |
可引用任意Host |
| 符合DT规范 |
可能违反 |
符合 |
6. 文件结构对比
6.1 重构前
1 2 3 4 5 6 7 8 9
| mmc/host/ ├── mtk-sd.c # Host驱动(被大量#ifdef污染) │ ├── #ifdef CONFIG_MMC_SDHCI_BH201 │ ├── struct msdc_host { ... struct ggc_platform_t ggc; ... } │ ├── msdc_reset_hw_bh() / msdc_set_buswidth_bh() / ... │ └── #include "sdhci-bayhub.c" // 直接include源码 ├── sdhci-bayhub.c # Retimer核心实现(~4000行) ├── sdhci-bayhub.h # Retimer头文件 └── Kconfig/Makefile
|
问题:
- mtk-sd.c被17处
#ifdef污染
- sdhci-bayhub.c通过
#include而非独立编译
- 难以移植到QCOM/Phytium等其他平台
6.2 重构后
1 2 3 4 5
| mmc/host/ ├── mtk-sd.c # Host驱动(零修改,原生内核代码) ├── sd-retimer-o2.c # 独立Retimer驱动(~1400行,单一文件) ├── Kconfig # 独立配置项 └── Makefile # 独立编译
|
改进:
- Host驱动保持原生状态
- Retimer作为独立模块
- 单一文件,便于维护
7. 效果与收益
7.1 代码量对比
| 指标 |
重构前 |
重构后 |
改进 |
| Host驱动修改行数 |
~200行 |
0行 |
100%减少 |
| Retimer代码文件数 |
2个(.c+.h) |
1个 |
50%减少 |
| 预编译宏使用 |
17处 |
0处 |
100%消除 |
7.2 可移植性提升
重构前:移植到新平台需要:
- 修改Host驱动结构体
- 添加私有接口导出
- 修改Host probe流程
- 修改mmc_host_ops替换
- 处理平台差异宏
重构后:移植到新平台只需:
- 在DTS中添加retimer节点,引用目标mmc host
- 编译sd-retimer-o2.ko
7.3 维护性提升
| 场景 |
重构前 |
重构后 |
| 内核升级 |
需要重新适配Host驱动修改 |
通常无需修改 |
| Bug修复 |
可能影响Host驱动稳定性 |
完全隔离 |
| 新平台支持 |
需要修改平台Host驱动 |
只需DTS配置 |
7.4 架构对比图
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| 【重构前:嵌入式架构】 ┌─────────────────────────────────────────┐ │ mtk-sd.c (被污染) │ │ ┌─────────────────────────────────┐ │ │ │ #ifdef CONFIG_MMC_SDHCI_BH201 │ │ │ │ ┌───────────────────────────┐ │ │ │ │ │ sdhci-bayhub.c │ │ │ │ │ │ (直接include) │ │ │ │ │ └───────────────────────────┘ │ │ │ │ struct ggc_platform_t ggc; │ │ │ └─────────────────────────────────┘ │ └─────────────────────────────────────────┘
【重构后:模块化架构】 ┌─────────────────┐ ┌─────────────────┐ │ mtk-sd.c │ │ sd-retimer-o2.c │ │ (原生不修改) │ │ (独立模块) │ │ │ │ │ │ mmc->ops ──────┼────┼──▶ wrapped_ops │ └─────────────────┘ └─────────────────┘ │ │ └──────────┬───────────┘ ▼ ┌───────────────┐ │ MMC Core │ └───────────────┘
|
8. 总结
本次重构遵循以下设计原则:
- 开闭原则:对修改关闭(不改Host),对扩展开放(wrap ops)
- 单一职责:Retimer驱动只负责Retimer功能
- 依赖倒置:依赖MMC Core抽象,不依赖Host实现
- 最小知识:Retimer只需知道mmc_host,不需了解Host内部
核心技术点:
- 使用
-EPROBE_DEFER处理probe时序
- 通过包装
mmc_host_ops实现透明Hook
- 使用
mmc->ios获取状态,避免私有接口
- 使用phandle建立设备关联
这种架构使得SD Retimer驱动可以零侵入地与任何符合MMC框架的Host驱动配合工作,大大提高了代码的可维护性和可移植性。
作者:O2Micro驱动团队
日期:2024年