SD Retimer驱动架构重构:从嵌入式到独立模块化设计

本文介绍了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
// mtk-sd.c 中的原始实现
#ifdef CONFIG_MMC_SDHCI_BH201
#include "sdhci-bayhub.h"
#endif

struct msdc_host {
// ... host成员 ...
#ifdef CONFIG_MMC_SDHCI_BH201
struct platform_device *pdev;
struct ggc_platform_t ggc; // 嵌入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, // 替换init_card
.execute_tuning = sdhci_bht_execute_tuning, // 替换tuning
#else
.init_card = msdc_init_card,
.execute_tuning = msdc_execute_tuning,
#endif
};

// 文件末尾直接include源码
#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在代码审查中提出了明确的改进要求:

  1. DTS独立: sd-retimer节点不应放在mtk mmc节点内部
  2. Probe时机: 不应在mtk probe中调用retimer probe,考虑mmc_add_host回调
  3. 接口精简: set_bus_width/get_clock应从core层获取,而非底层驱动
  4. 代码整合: 两份.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
// 原始:需要Host实现这些私有ops
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
// 概念:通过notifier chain监听mmc事件
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:
// Host添加时注册retimer
break;
case MMC_EVENT_CARD_INSERTED:
// 卡插入时初始化
break;
}
}

优点

  • 标准Linux机制
  • 解耦良好

缺点

  • ❌ Linux MMC子系统当前没有提供此类notifier接口
  • ❌ 需要修改mmc core,不现实

3.2 方案B:mmc_add_host回调

1
2
3
4
5
6
7
8
9
// 概念:在mmc_add_host中增加回调
int mmc_add_host(struct mmc_host *host)
{
// ... 现有逻辑 ...

// 新增:调用注册的retimer回调
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
// 概念:动态替换mmc->ops
struct o2_retimer {
struct mmc_host *mmc;
const struct mmc_host_ops *host_ops; // 保存原始ops
struct mmc_host_ops wrapped_ops; // 包装后的ops
};

static int o2_retimer_probe(struct platform_device *pdev)
{
// 1. 通过DTS phandle获取mmc_host
mmc = get_mmc_from_phandle(np, "mmc");

// 2. 保存并包装ops
o2->host_ops = mmc->ops;
o2->wrapped_ops = *mmc->ops;

// 3. 替换特定回调
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;

// 4. 安装包装后的ops
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
/*
* 包装模式:保持对原始ops的引用,在包装函数中:
* 1. 先调用原始实现
* 2. 再执行Retimer特定逻辑
*/
static void o2_wrapped_init_card(struct mmc_host *mmc, struct mmc_card *card)
{
struct o2_retimer *o2 = o2_get_retimer(mmc);

/* 1. 调用原始init_card(如果存在) */
if (o2->host_ops && o2->host_ops->init_card)
o2->host_ops->init_card(mmc, card);

/* 2. 执行Retimer硬件初始化 */
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);

/* Retimer接管tuning流程 */
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
/*============================================================================
* Helper Functions - 使用MMC Core接口,不依赖Host私有接口
*============================================================================*/

/**
* 获取时钟:直接从mmc->ios读取
*/
static inline unsigned int o2_get_host_clock(struct o2_retimer *o2)
{
return o2->mmc->ios.clock; // ✅ 使用core公开字段
}

/**
* 设置总线宽度:通过set_ios回调
*/
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;

/* 通过原始的set_ios设置 */
if (o2->host_ops && o2->host_ops->set_ios)
o2->host_ops->set_ios(mmc, &ios); // ✅ 使用标准ops回调

mmc->ios.bus_width = width;
}

/**
* 总线复位:通过时钟切换模拟
* 注:不再依赖Host私有的reset_hw
*/
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
/* 全局链表存储retimer实例 */
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;
};

/**
* 根据mmc_host查找对应的retimer实例
*/
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
/* ❌ 不推荐:retimer作为mmc子节点 */
mmc1: mmc@11240000 {
compatible = "mediatek,mt8189-mmc";
/* ... mmc配置 ... */

sd-retimer {
compatible = "o2micro,sd-retimer";
/* retimer配置 */
};
};

新设计(phandle引用)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/* ✅ 推荐:独立节点 + phandle引用 */
mmc1: mmc@11240000 {
compatible = "mediatek,mt8189-mmc";
/* ... mmc配置 ... */
/* 无需任何retimer相关配置 */
};

sd_retimer: sd-retimer {
compatible = "o2micro,sd-retimer";
mmc = <&mmc1>; /* phandle引用 */
status = "okay";

/* Retimer可选配置 */
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 可移植性提升

重构前:移植到新平台需要:

  1. 修改Host驱动结构体
  2. 添加私有接口导出
  3. 修改Host probe流程
  4. 修改mmc_host_ops替换
  5. 处理平台差异宏

重构后:移植到新平台只需:

  1. 在DTS中添加retimer节点,引用目标mmc host
  2. 编译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. 总结

本次重构遵循以下设计原则:

  1. 开闭原则:对修改关闭(不改Host),对扩展开放(wrap ops)
  2. 单一职责:Retimer驱动只负责Retimer功能
  3. 依赖倒置:依赖MMC Core抽象,不依赖Host实现
  4. 最小知识:Retimer只需知道mmc_host,不需了解Host内部

核心技术点

  • 使用-EPROBE_DEFER处理probe时序
  • 通过包装mmc_host_ops实现透明Hook
  • 使用mmc->ios获取状态,避免私有接口
  • 使用phandle建立设备关联

这种架构使得SD Retimer驱动可以零侵入地与任何符合MMC框架的Host驱动配合工作,大大提高了代码的可维护性和可移植性。


作者:O2Micro驱动团队
日期:2024年