【MMC Core + hc16 Host(WiFi SDIO 如何落到控制器)】

第 5 课:MMC Core + hc16 Host(WiFi SDIO 如何落到控制器)

接第 1~4 课。

前四课站在 8733 WiFi 驱动 视角;本课下沉到 MMC/SDIO 子系统 + hc16 Host 。

目标:任意一次 sdio_readb / memcpy_toio / DAT1 中断,都能点名到 dw_mci_request / mmc_signal_sdio_irq。

正文自包含,不必翻代码。

对应源码(备查):

  • MMC:mmcdriver-*/drivers/mmc/core/
    • core.c(rescan)
    • sdio.c(attach/init)
    • sdio_ops.c / sdio_io.c / sdio_irq.c / sdio_bus.c / sdio_cis.c
  • Host:mmcdriver-*/drivers/mmc/host/hc16/
    • dw_mmc.c、dw_mmc-pltfm-hc.c
  • 头文件:linux/mmc/host.h、sdio.h、sdio_func.h

0. 本课总画面

把整条栈再叠一层:

text 复制代码
第1~4课:RTL8733BS WiFi 驱动
        │  sdio_* API
        ▼
本课上半:MMC SDIO Core(协议与总线模型)
        │  mmc_host_ops.request / enable_sdio_irq / set_ios
        ▼
本课下半:hc16 DW MMC Host(控制器驱动)
        │
        ▼
SDIO 物理线:CMD / CLK / DAT0-3(DAT1 兼中断)

一句话:

8733 是客户;MMC core 是翻译官;hc16 是干活的硬件管家。


1. 启动顺序:谁先谁后

结合第 1 课,完整顺序应是:

text 复制代码
1. Host 驱动 probe(hc16)
   → mmc_alloc_host → 填 mmc->ops → mmc_add_host → mmc_start_host

2. MMC core rescan
   → mmc_rescan → mmc_rescan_try_freq
   → 优先 mmc_attach_sdio(WiFi 场景)

3. SDIO 识卡成功
   → 读 CCCR/CIS,创建 sdio_func
   → sdio_register_bus 上的驱动 match

4. 8733 驱动已 register 或稍后 register
   → match 0x024c:0xB733/B73A
   → rtw_drv_init(第 1 课)

关键结论:

  • WiFi probe 之前,MMC 往往已经用 CMD5/CMD52 等把卡识别完了
  • 所以第 1 课说:sdio_register_driver 时"本驱动"未必读过寄存器,但 MMC 可能已经读过

2. MMC 对象模型(本课必记)

对象 含义
mmc_host 一个 SDIO/MMC 控制器抽象
mmc_host_ops Host 回调表:request / set_ios / enable_sdio_irq...
mmc_card 已识别的卡
sdio_func 卡上某个 Function(WiFi 通常 Func1)
mmc_request 一次完整请求:命令 + 可选数据
mmc_command 一条命令(含 opcode/arg/resp)
mmc_data 数据阶段(读/写、block、sg)
mmc_ios 时钟、位宽、电压、时序等 IO 设置

WiFi 驱动拿到的是 sdio_func *;

它内部挂着 func->card,再挂着 card->host,最终打到 host->ops->request。

text 复制代码
sdio_func
  → mmc_card
    → mmc_host
      → ops->request(host, mrq)     // hc16: dw_mci_request

3. 识卡路径:mmc_attach_sdio(WiFi 能 probe 的前提)

3.1 rescan 入口

text 复制代码
mmc_start_host
  → detect work
  → mmc_rescan
  → mmc_rescan_try_freq
       power_up
       sdio_reset / go_idle / CMD8(if_cond)
       依次尝试:
         mmc_attach_sdio
         mmc_attach_sd
         mmc_attach_mmc

WiFi 模组走 mmc_attach_sdio。

3.2 mmc_sdio_init_card 做什么(概念顺序)

text 复制代码
CMD5(IO OCR)------ 询问/设定工作电压
分配 mmc_card,类型 SDIO
(可选)1.8V/UHS 切换 ------ 本树可能被关掉
读 RCA、select card
读 CCCR(能力、速度、总线宽度等)
读 CIS(vendor/device,供驱动 match)
切高速 / 4bit(若双方支持)
为每个 Function 建 sdio_func,读 FBR/CIS
mmc_add_card → sdio_add_func
  → 总线 match → 调用 WiFi probe

3.3 和第 1 课的衔接点

MMC 已完成 8733 probe 再做
认出卡、建 sdio_func enable_func(确保 Function 开着)
读出 CIS ID id_table match
可能已设总线宽度/时钟 set_block_size(512)
尚未挂 WiFi 业务中断 handler claim_irq(sd_sync_int_hdl)

4. 命令路径:从 WiFi API 到 dw_mci_request

4.1 CMD52:sdio_readb 等

text 复制代码
8733: sdio_readb / sdio_writeb / sdio_f0_readb
  → MMC: mmc_io_rw_direct
       构造 mmc_command
         opcode = SD_IO_RW_DIRECT (CMD52)
         arg = 写方向 | fn | addr | data
  → mmc_wait_for_cmd(host, &cmd)
  → host->ops->request(host, mrq)   // 实际常包在 wait 路径里
  → hc16: dw_mci_request
  → 控制器发命令,等响应
  → mmc_request_done

CMD52 通常没有 data 阶段,只有命令 + 响应里带 1 字节。

4.2 CMD53:sdio_memcpy_* / readsb/writesb

text 复制代码
8733: sdio_memcpy_fromio / toio / readsb / writesb
  → MMC: mmc_io_rw_extended
       构造 mrq:
         cmd.opcode = SD_IO_RW_EXTENDED (CMD53)
         cmd.arg 含:写方向、fn、addr、是否递增、byte/block
         data: 读或写、blksz、blocks、sg 缓冲区
  → mmc_wait_for_req(host, &mrq)
  → dw_mci_request
  → 命令 + 数据(可用 DMA)
  → mmc_request_done
WiFi/内核 API incr_addr 模式直觉
memcpy_fromio/toio 1(递增) 连续地址窗
readsb/writesb 0(固定) FIFO 口

第 2/4 课提过:8733 主路径常 fixed=0 走 memcpy_*,但 TX/RX 地址是特殊编码。

4.3 mmc_request 长什么样(概念)

text 复制代码
mrq
 ├─ cmd   : opcode/arg/flags/resp/error
 ├─ data  : blksz/blocks/flags/sg/...(CMD52 常无)
 └─ stop  : 某些传输才有(SDIO 常见路径未必用)

Host 驱动只认 mrq,不关心上面是 WiFi 还是存储。


5. hc16 Host:dw_mci_ops 与 dw_mci_request

5.1 Host 注册时挂上的 ops(概念)

text 复制代码
dw_mci_ops:
  .request        = dw_mci_request      ← 所有命令入口
  .set_ios        = dw_mci_set_ios      ← 时钟/位宽/电压时序
  .get_cd         = ...
  .enable_sdio_irq= dw_mci_enable_sdio_irq
  .execute_tuning = ...
  .start_signal_voltage_switch = ...
  ...

平台文件 dw_mmc-pltfm-hc.c 负责:

  • 从 FDT/DTS 取基址、时钟、pinmux、相位延迟等
  • 调用 dw_mci_pltfm_register → dw_mci_probe
  • mmc_alloc_host / mmc_add_host

5.2 dw_mci_request 做什么

text 复制代码
dw_mci_request(mmc, mrq)
  若卡不在 → mrq->cmd->error = -ENOMEDIUM; request_done; 返回
  加锁
  把 mrq 排入该 slot 队列(或立刻启动)
  解锁
  (后续在状态机/中断里发 CMD、搬数据、完成后 mmc_request_done)

完成路径:

text 复制代码
控制器中断:命令完成 / 数据完成 / 错误
  → 填 cmd->error / data->error
  → mmc_request_done(mmc, mrq)
  → 唤醒 mmc_wait_for_req/cmd 的等待者
  → 返回到 sdio_io / 8733 驱动

5.3 set_ios 何时出现

MMC core 在识卡、切高速、改总线宽度、调时钟时调用:

text 复制代码
mmc_set_clock / mmc_set_bus_width / mmc_set_timing ...
  → host->ops->set_ios
  → dw_mci_set_ios

WiFi 驱动一般不直接调 set_ios;它改的是芯片寄存器。

总线时钟/位宽多在 MMC 识卡或电源管理路径里改。


6. 中断路径:从 DAT1 到第 3 课回调

这是把第 3 课和下层钉死的关键图。

6.1 Host 硬件中断

text 复制代码
芯片拉 DAT1
  → DW MMC 产生 SDIO 中断状态位(INT_SDIO)
  → dw_mci IRQ handler
  → 清 RINTSTS 对应位
  → mmc_signal_sdio_irq(slot->mmc)

6.2 MMC SDIO IRQ 线程

text 复制代码
mmc_signal_sdio_irq
  → 唤醒 sdio_irq_thread(ksdioirqd/...)
  → 线程里:
       claim_host
       process_sdio_pending_irqs
         (可读 CCCR INTx,或单功能直调 handler)
       调用 func->irq_handler
         → 8733: sd_sync_int_hdl
       release_host
  → enable_sdio_irq(1) 再等下一次

sdio_claim_irq(第 1 课)时:

  1. 写 CCCR 中断使能(IENx)
  2. sdio_card_irq_get → 创建/引用 IRQ 线程
  3. 保存 func->irq_handler = sd_sync_int_hdl

6.3 为何处理中常 enable_sdio_irq(0/1)

text 复制代码
收到 DAT1 → 先关 SDIO IRQ → 处理 → 再开

防止处理期间中断风暴;

8733 在 handler 里还会读 HISR、搬 FIFO(第 3 课),这段时间 Host 侧通常保持"已消费本次门铃"的状态。


7. 一张总对照表(8733 ↔ MMC ↔ Host)

8733 / 内核 API MMC Core hc16 Host
sdio_register_driver sdio_bus match/probe (卡已由 host+core 建好)
sdio_enable_func 写 CCCR IOEx(CMD52) dw_mci_request
sdio_set_block_size 写 FBR/相关(CMD52) dw_mci_request
sdio_readb/writeb mmc_io_rw_direct CMD52 dw_mci_request
memcpy_* / readsb/writesb mmc_io_rw_extended CMD53 dw_mci_request + DMA/FIFO
sdio_claim_irq sdio_irq.c + CCCR IENx enable_sdio_irq
DAT1 来临 mmc_signal_sdio_irq → handler IRQ 里看 INT_SDIO
(间接)改时钟位宽 set_ios 路径 dw_mci_set_ios

8. 三条端到端路径(请背)

8.1 识卡到 WiFi probe

text 复制代码
hc16 probe → mmc_add_host → mmc_start_host
  → mmc_rescan → mmc_attach_sdio → mmc_sdio_init_card
  → CIS 得到 0x024c:0xB733
  → sdio_add_func → match → rtw_drv_init

8.2 TX 一帧落到线(接第 4 课)

text 复制代码
write_cmd53 → raw_write → sdio_memcpy_toio/writesb
  → mmc_io_rw_extended(CMD53)
  → mmc_wait_for_req
  → dw_mci_request
  → DMA/FIFO 写出
  → mmc_request_done
  → 返回 8733

8.3 RX 中断落到 8733(接第 3 课)

text 复制代码
DAT1 → dw_mci IRQ → mmc_signal_sdio_irq
  → sdio_irq_thread → sd_sync_int_hdl
  → sd_int_hdl → read_port → 再次 dw_mci_request(CMD53 读)

注意:一次 RX 至少两段 Host 事务------

先可能有寄存器读(HISR),再有 FIFO CMD53 读;都走 dw_mci_request。


9. 平台相关:hc16 上你还该知道的点

9.1 能力与限制

Host 的 caps / f_max / ocr_avail 决定:

  • 能否 4bit
  • 最高时钟
  • 是否声明支持 SDIO IRQ
  • 电压范围

这些来自平台数据 / DTS,在 dw_mmc-pltfm-hc.c 一类文件配置。

9.2 Tuning / 1.8V

高速 SDIO 可能涉及:

  • execute_tuning
  • start_signal_voltage_switch

本树 SDIO 1.8V UHS 路径可能被裁掉(#if 0)。

若 WiFi 只能跑在 3.3V HS,排障时先确认 实际 ios.timing / clock,不要假设一定进了 UHS。

9.3 和存储卡共享认知

同一套 dw_mci_request 既服务 SD 存储,也服务 SDIO WiFi。

差别在 core 上层协议(SD 命令集 vs CMD52/53),不在 Host 入口。


10. 排障:从 WiFi 症状定位到哪一层

现象 优先怀疑
根本没有 WiFi probe Host 没扫到卡 / attach_sdio 失败 / CIS ID 不匹配
probe 到但 enable_func 失败 电源、F0/CCCR、claim_host 异常
能读写寄存器,不能收包 DAT1 / enable_sdio_irq / HIMR / claim_irq
CMD53 大数据总超时 时钟、相位/tuning、DMA、对齐、信号完整性
TX 很慢但 CMD 成功 多半仍在 8733 tx_allowed/聚合(第 4 课),不是 Host 发不出去
偶发 -ENOMEDIUM 掉电、复位、接触、surprise remove

分层口诀:

probe 问题先看 MMC 识卡;中断问题看 DAT1;快慢问题先分清是 page 门禁还是 SDIO 线。


11. 五课总串联图

text 复制代码
[第1课] register/probe/enable_func/claim_irq
[第2课] CMD52/53 与 raw_read/write
[第3课] DAT1 → HISR → read_port(CMD53)
[第4课] enqueue → tx_allowed → write_port(CMD53)
[第5课] 以上全部经 MMC core 落到 dw_mci_request / SDIO IRQ
text 复制代码
应用/协议栈
    │
    ▼
RTL8733BS(第1~4课)
    │ sdio_* 
    ▼
MMC SDIO Core(本课)
    │ mmc_host_ops
    ▼
hc16 DW MMC(本课)
    │
    ▼
SDIO 物理总线 ↔ 8733 芯片

12. 本课关键名词

名词 一句话
mmc_host 控制器抽象
mmc_host_ops Host 回调表
mmc_request 一次命令(+数据)请求
mmc_io_rw_direct CMD52 实现
mmc_io_rw_extended CMD53 实现
mmc_attach_sdio SDIO 识卡入口
CCCR/CIS 公共控制与识别信息
dw_mci_request hc16 接收 mrq 的入口
set_ios 配时钟/位宽/时序
mmc_signal_sdio_irq Host 把 DAT1 事件交给 SDIO 核心
sdio_irq_thread 调用各 func handler 的线程
INT_SDIO DW MMC 的 SDIO 中断状态位

13. 本课必须记住的 8 句话

  1. WiFi 驱动是 SDIO 客户,MMC core 先识卡再建 func。
  2. 所有 CMD52/53 最终变成 mmc_request 交给 host->ops->request。
  3. hc16 入口是 dw_mci_request,出口是 mmc_request_done。
  4. CMD52 基本无 data;CMD53 有 data(可 DMA)。
  5. DAT1 → Host IRQ → mmc_signal_sdio_irq → sd_sync_int_hdl。
  6. claim_irq 同时动 CCCR 与 Host enable_sdio_irq。
  7. set_ios 管总线参数,不是 8733 自己的 WiFi 寄存器。
  8. 排障先分层:识卡 / 中断 / 命令超时 / 驱动 page 门禁。

14. 自测题

题 1

为什么说 8733 的 rtw_drv_init 能被调用,通常意味着 MMC 已经成功 attach_sdio?

题 2

sdio_readb 与 sdio_memcpy_toio 分别对应 MMC 里哪两个函数?命令号是什么?

题 3

dw_mci_request 如果不存在或卡住,对 8733 的 TX/RX 会有什么影响?

题 4

画出从 DAT1 到 sd_sync_int_hdl 的 Host/MMC 关键字链。

题 5

TX 很慢,但抓到 CMD53 都成功返回:更应先查第 4 课的哪一环,还是先查 Host tuning?

题 6

mmc_rescan_try_freq 里为何优先 attach_sdio 再 attach_sd?

题 7

enable_sdio_irq(0) 和 8733 的 rtw_hal_disable_interrupt 是同一层吗?

题 8

按层列出一次 RX 收包会触发的两类(或更多)dw_mci_request 事务。


15. 自测题标准答案

题 1

rtw_drv_init 是 sdio_driver.probe,只有总线里已经有匹配的 sdio_func 才会调用;

而 sdio_func 是 mmc_attach_sdio / mmc_sdio_init_card 创建并 sdio_add_func 的。

题 2

  • sdio_readb → mmc_io_rw_direct → CMD52
  • sdio_memcpy_toio → mmc_io_rw_extended → CMD53

题 3

所有 SDIO 命令(含 enable、寄存器、FIFO)都无法真正下到硬件;

TX/RX 会超时失败,最终可能 surprise_removed。

题 4(参考)

text 复制代码
DAT1
 → DW MMC INT_SDIO
 → mmc_signal_sdio_irq
 → sdio_irq_thread / process_sdio_pending_irqs
 → sd_sync_int_hdl

题 5

更应先查第 4 课 tx_allowed / 聚合 / page 分配 。

CMD53 都成功说明 Host 传输大体健康,瓶颈常在"等 FIFO 空间/软件调度"。

题 6

同一槽位可能是 SDIO、SD 或 MMC;

WiFi 模组是 SDIO,优先 SDIO 可更快正确识别,避免按存储卡流程误判。

(代码注释也写:Order's important: SDIO, then SD, then MMC。)

题 7

不是。

  • enable_sdio_irq:Host/MMC 层,管 DAT1 中断路由
  • rtw_hal_disable_interrupt:芯片 HIMR 层,管芯片上报哪些事件

题 8(参考)

  1. 读 HISR/长度(寄存器,CMD52 或短读)→ 一次 request
  2. read_port 读 RX FIFO(CMD53)→ 又一次 request
    若循环收多包,CMD53 可能多次。

16. 学完五课后你应具备的能力

  1. 能讲述 WiFi SDIO 从上电到 probe 的顺序
  2. 能区分 CMD52/CMD53/F0/FIFO
  3. 能讲清 RX 三层中断与 TX tx_allowed
  4. 能把任意 sdio_* 调用映射到 mmc_io_rw_* 与 dw_mci_request
  5. 能按层排障:识卡 / 中断 / 总线超时 / 驱动门禁

附录 A:关键调用链速查

识卡

text 复制代码
dw_mci_probe → mmc_add_host → mmc_start_host
  → mmc_rescan → mmc_attach_sdio → mmc_sdio_init_card
  → sdio_add_func → rtw_drv_init

CMD52

text 复制代码
sdio_readb
  → mmc_io_rw_direct
  → mmc_wait_for_cmd
  → dw_mci_request → mmc_request_done

CMD53

text 复制代码
sdio_memcpy_toio / fromio / writesb / readsb
  → mmc_io_rw_extended
  → mmc_wait_for_req
  → dw_mci_request → mmc_request_done

SDIO IRQ

text 复制代码
INT_SDIO → mmc_signal_sdio_irq
  → sdio_irq_thread → sd_sync_int_hdl → sd_int_hdl

相关推荐
等我调个Bug1 小时前
【Linux系统篇】权限
linux·服务器
~光~~2 小时前
【嵌入式linux学习】Cache 策略:Write-Through / Write-Back + Allocation
linux·学习·spring
峥无2 小时前
Linux NPTL线程:创建/终止/等待/分离/栈布局内核原理|线程下篇
linux·运维·线程
是jin奥3 小时前
Ubuntu 访问 Windows 共享目录
linux·运维·ubuntu
星恒随风3 小时前
Linux基础IO万字详解:从文件描述符fd到重定向、FILE缓冲机制及MiniShell升级
linux·运维·笔记·学习·状态模式
yunwei374 小时前
eBPF 入门开发实践教程一:Hello World,基本框架和开发流程
linux·后端·性能优化
小雪崩4 小时前
嵌入式学习 day65:Linux驱动进阶——pinctrl 子系统和 platform 驱动框架
linux·学习·驱动
wixzjsh4 小时前
嵌入式Linux系统启动与内核驱动开发全解
linux·运维·驱动开发
晴天的雨.9924 小时前
【Linux】基础开发工具---软件包管理器
linux·运维·服务器