# SDIO 使用说明

## 概述

- X5 支持 SDIO（Secure Digital Input Output）接口，用于高速数据传输和扩展外设。
- X5 提供完整的 SDIO 硬件支持与驱动框架，支持多种传输模式及协议版本，允许通过标准接口连接多种外设如 Wi-Fi 模块、蓝牙模块。

## 特点

- 支持协议版本：兼容 SDIO Ver3.0 协议（含 eSDIO）。
- 高速时钟支持：接口最高支持 200 MHz 时钟输入。
- 多种传输模式：支持 4 线 数据传输，包括 SD High Speed、SDR12、SDR25、SDR50、SDR104 等模式，详细规格如下：

| Bus Speed Mode          | Maximum Frequency | Signal Voltage | Bus Maximum Performance | Spec. Version |
|--------------------------|-------------------|----------------|--------------------------|---------------|
| Default Speed (DS)       | 25MHz            | 3.3V           | 12.5MB/sec              | 1.01          |
| High Speed (HS)          | 50MHz            | 3.3V           | 25MB/sec                | 1.10          |
| UHS-I: SDR12             | 25MHz            | 1.8V           | 12.5MB/sec              | 3.01          |
| UHS-I: SDR25             | 50MHz            | 1.8V           | 25MB/sec                | 3.01          |
| UHS-I: SDR50             | 100MHz           | 1.8V           | 50MB/sec                | 3.01          |
| UHS-I: SDR104            | 208MHz           | 1.8V           | 104MB/sec               | 3.01          |

SDIO 协议详细文档请从[SD 联盟官网](https://www.sdcard.org/downloads/pls/)自行获取。

## 功能描述

### 典型应用

SDIO 接口的结构框图如下：

![image-20250115-213644](_static/_images/8-SDIO_Debug_Guide_zh_CN/image-20250115-213644.png)

- CLK:主机提供的时钟信号给到 SDIO 外设
- CMD:用于发送命令和接收响应
	- 主机通过这条线向 SDIO 外设发送控制命令（如初始化、配置、数据读写等）
    - 外设通过这条线将命令的响应返回给主机

- DATA: 模式不同，支持 单线（DATA0） 或 多线传输（DATA0-DATA3）
	- 单线模式：只有 DATA0 一根线用于数据传输
	- 4线模式：DATA0-DATA3 提供更高的数据传输速率

SDIO 常见的应用场景包括：

- **Bluetooth** ：通过 SDIO 接口连接蓝牙模块，实现无线通信功能。
- **Wi-Fi** ：利用 SDIO 接口与主控通信，提供无线局域网（WLAN）连接。
- **4G** ：支持 SDIO 接口的 4G 模块，用于提供高速的移动数据通信。

### 功能原理

- **协议支持**：
  基于 SDIO Ver3.0 标准，支持完整的命令接口（CMD0~CMD52），支持 eSDIO（Embedded SDIO）。

- **驱动架构**：
  内核中的 MMC 框架与 SDIO 驱动协同工作，负责完成以下任务：
  - 通过 `sdio_register_driver` 和 `sdio_unregister_driver` 完成设备注册和注销。
  - 解析设备树（DTS）中的硬件信息，初始化硬件资源。
  - 利用中断机制和 DMA 引擎高效处理数据传输。

## 驱动代码

### Kernel 驱动

在内核阶段，默认启用了 SDIO 支持，SDIO 驱动基于 MMC 框架实现，无需额外修改内核 defconfig。

#### 驱动接口说明

驱动注册函数：

```c
int sdio_register_driver(struct sdio_driver *drv)
```

驱动注销函数：

```c
void sdio_unregister_driver(struct sdio_driver *drv)
```

sdio_driver 结构体定义如下：

```c
struct sdio_driver {
	char *name;
	const struct sdio_device_id *id_table;
	int (*probe)(struct sdio_func *, const struct sdio_device_id *);
	void (*remove)(struct sdio_func *);
	struct device_driver drv;
};
```

#### 设备树配置指南

SDIO 控制器的dts位置：`arch/arm64/boot/dts/hobot/x5.dtsi`

##### 适配外设注意事项

- 根据硬件设计，确认目标 MMC 控制器的 SDIO 编号是否与设备树（DTS）中配置的物理地址和设备类型一致。
- 以 X5 EVB 为参考设计，配置如下：

| SDIO 控制器 | 物理地址        | 设备类型            |
| --------- | --------------- | ------------------- |
| SDIO0     | 0x35040000      | eMMC               |
| SDIO1     | 0x35020000      | SD 卡              |
| SDIO2     | 0x35030000      | 其它 SDIO 设备，一般为 WiFi 卡 |

常用字段解释：
- **`non-removable`**：标记不可移除设备，系统启动后不支持热插拔。
- **`cd-gpios`**：定义 SD 卡检测引脚。
- **`broken-cd`**：禁用卡槽检测功能。
- **`broken-cd`**：禁用卡槽检测功能。
- **`no-sd`**:禁用 SD 卡功能。
- **`no-mmc`**：禁用 eMMC 功能。
- **`cap-sdio-irq`**：启用 SDIO 中断。
- **`clock-frequency`**：自定义工作时钟频率，单位 Hz。

以下是不同设备类型的关键配置说明： 

<span id="eMMC"/> </span>

##### eMMC
- **关键属性**
	- `non-removable`：标记为不可移除设备。注意，此配置会使系统在启动时仅对该控制器的设备进行一次探测。
- **运行模式**：一般有以下两种选择：
  - `cap-mmc-highspeed`：High Speed 模式，最高时钟频率为 52 MHz（X5 平台实际限制为 50 MHz）。
  - `mmc-hs200-1_8v`：HS200 模式，最高时钟频率为 200 MHz。
- `bus-width`：数据线宽度，通常配置为 `8`。

<span id="SD"/> </span>

##### SD 卡

- **关键属性**
	- `no-mmc` 和 `no-sdio`：SD 卡默认 DTS 配置包含 `no-mmc`、`no-sdio` 以优化探测流程。
	- `broken-cd` 和 `cd-gpios`：
		- 如果未连接 SD 卡插拔检测中断引脚，请配置 `broken-cd` 并删除 `cd-gpios` 字段，系统将通过轮询方式检测卡槽状态。
		- 如果有插拔检测中断引脚，按照实际硬件定义配置 `cd-gpios`（包括极性）。
- **运行模式**：根据需求配置 `sd-uhs-*` 或 `cap-sd-highspeed` 模式，具体说明参考 SD 协议或 MMC 协议文档。
- **电压域调整**：
  - SD 卡默认探测电压为 3.3V，高速模式（`sd-uhs-*`）需要 1.8V IO 电压。
  - 配置项：
    - `uhs-180v-gpio` + `uhs-180v-logic`：配置用于切换 SD 卡槽 IO 供电的 GPIO 和极性，需与硬件设计一致。
	- MMC 电压域的切换由 MMC 自己控制，对应的 pinctrl 信息不需要调整。

##### 其他 SDIO 设备（如 WiFi）

- **关键属性**
	- `no-mmc` 和 `no-sd`：SDIO 设备默认 DTS 配置 `no-mmc`、 `no-sd` 优化探测流程。
	- `broken-cd`：通常 SDIO 设备不需要插拔检测中断。如果需要，配置方式与 SD 卡类似。
		- 如果 WIFI 模组没有 Card Detect 管脚，必须将"broken-cd"加上。
		- 如果需要对WiFi进行上下电，需要将"non-removable"字段删除，host端才会进行多次探测；否则 WiFi 模组不会被二次探测。
	- `cap-sdio-irq`：用于接收 SDIO 设备的中断（如 WiFi 唤醒）。
- **运行模式**：根据需求配置 `sd-uhs-*` 或 `cap-sd-highspeed` 模式。
- **电压域调整**：
  - 若默认电压为 3.3V，但需切换至 1.8V：参考 SD 卡配置方法。
  - 若默认电压为 1.8V：
    - 删除 `uhs-180v-gpio` 和 `uhs-180v-logic`等字段，以防对GPIO进行误操作
    - 配置 `mmc-fixed-voltage`：特殊 DTS 字段，用于强制指定 MMC 控制器的IO供电，定义为"1800或"3300"，以配置为对应的电压，示例：
      ```dts
      mmc-fixed-voltage = <1800>;
	  ```

完整说明请参考内核文档：`Documentation/devicetree/bindings/mmc/mmc-controller.yaml`

### Kernel SDIO 框架调试

X5 SDIO 接口基于内核 MMC 框架，可通过`Dynamic Debug`和 `ftrace`调试工具检查传输问题。

下面对`Dynamic Debug`和`ftrace`调试工具简单使用讲解，更详细的说明请参考：

1. [Dynamic Debug Linux 官方说明](https://www.kernel.org/doc/html/v6.1/admin-guide/dynamic-debug-howto.html)
2. [ftrace Linux 官方说明](https://www.kernel.org/doc/html/v6.1/trace/ftrace.html)

#### Dynamic Debug 使用指南

Dynamic debug(具体路径及开启的文件可以根据需要修改):

```bash
echo 'file drivers/mmc/core/core.c +p' >/sys/kernel/debug/dynamic_debug/control
echo 'file drivers/mmc/core/mmc.c +p' >/sys/kernel/debug/dynamic_debug/control
echo 'file drivers/mmc/core/block.c +p' >/sys/kernel/debug/dynamic_debug/control
echo 'file drivers/mmc/host/sdhci-of-dwcmshc.c +p' > /sys/kernel/debug/dynamic_debug/control
echo 'file drivers/mmc/host/sdhci.c +p' > /sys/kernel/debug/dynamic_debug/control
echo 7 7 7 7 > /proc/sys/kernel/printk
```

#### <span id="ftrace_guide"/> Ftrace 使用指南

1. 配置内核支持 Ftrace 相关选项

	```makefile
	CONFIG_FTRACE=y
	CONFIG_FUNCTION_TRACER=y
	CONFIG_DYNAMIC_FTRACE=y
	CONFIG_DEBUG_FS=y
	```

2. 完成内核配置后，重新编译并将生成的镜像烧录到目标设备。

3. 进入系统加载驱动后开始 ftrace 使用，以 MMC 传输事件为例。

	```bash
	cd /sys/kernel/debug/tracing/          #进入 Ftrace 工作目录
	echo mmc:mmc_request_start > set_event #添加事件 mmc_request_start，用于标识 MMC 请求的开始
	echo mmc:mmc_request_done >> set_event #添加事件 mmc_request_done，用于标识 MMC 请求的完成
	echo 0 > tracing_on                    #关闭实时跟踪功能，确保初始化阶段数据不被记录
	echo nop > current_tracer              #清除之前的跟踪器设置
	echo > trace                           #清空 trace 文件，避免干扰后续分析
	echo 1 > tracing_on                    #开启实时跟踪功能。
	cat /sys/kernel/debug/tracing/trace | grep mmc #查看并过滤 trace 文件中的 MMC 相关事件
	```

4. trace 文件分析

	通过 trace 文件中记录的事件，可以分析 MMC 请求的执行细节，包括请求发起、完成的具体时间及参数。以下是部分示例日志及其含义：

	```log
	kworker/5:1H-152  586.094457: mmc_request_start: mmc0: start struct mmc_request[000000007dc8561b]: cmd_opcode=25 cmd_arg=0xdca8 ....
		<idle>-0      586.094839: mmc_request_done: mmc0: end struct mmc_request[000000007dc8561b]: cmd_opcode=25 cmd_err=0  ....
	kworker/0:0H-8    586.094911: mmc_request_start: mmc0: start struct mmc_request[00000000daeefa66]: cmd_opcode=13 cmd_arg=0x10000 ....
	kworker/0:0H-8    586.094936: mmc_request_done: mmc0: end struct mmc_request[00000000daeefa66]: cmd_opcode=13 cmd_err=0 ....
	kworker/5:1H-152  586.095231: mmc_request_start: mmc0: start struct mmc_request[000000002e7e251a]: cmd_opcode=6 cmd_arg=0x3200101 ....
		<idle>-0      586.095258: mmc_request_done: mmc0: end struct mmc_request[000000002e7e251a]: cmd_opcode=6 cmd_err=0 ....
	kworker/5:1H-152  586.095866: mmc_request_start: mmc0: start struct mmc_request[000000001e3cd0e1]: cmd_opcode=13 cmd_arg=0x10000 ....
		<idle>-0      586.095889: mmc_request_done: mmc0: end struct mmc_request[000000001e3cd0e1]: cmd_opcode=13 cmd_err=0 ....
	```
- **时间戳**：586.094457 和 586.094839 分别为事件发生的时间点（单位：秒）。
- **进程名**：例如，kworker/5:1H-152 表示处理事件的内核线程。
- **事件类型**：
	- **mmc_request_start**：表示发起 MMC 请求，包含具体的请求参数（如 cmd_opcode=25，cmd_arg=0xdca8）。
	- **mmc_request_done**：表示完成 MMC 请求，记录请求的执行结果（如 cmd_err=0 表示无错误，bytes_xfered=45056 表示传输了 45056 字节）。
	- **关联请求**：通过 struct mmc_request[000000007dc8561b] 可定位每个请求的唯一标识。

#### MMC 控制器配置和运行参数

可以通过以下命令查看 MMC 控制器的配置和运行参数（以 `mmc2` 为例）：

```bash
cat /sys/kernel/debug/mmc2/ios
clock:          200000000 Hz
actual clock:   200000000 Hz
vdd:            21 (3.3 ~ 3.4 V)
bus mode:       2 (push-pull)
chip select:    0 (don't care)
power mode:     2 (on)
bus width:      2 (4 bits)
timing spec:    6 (sd uhs SDR104)
signal voltage: 1 (1.80 V)
driver type:    0 (driver type B)
```
参数解释：
- `clock`: MMC 控制器配置的时钟频率（200 MHz）。
- `actual clock`:实际运行的时钟频率（200 MHz）。
- `vdd`:供电电压范围（3.3 ~ 3.4 V）。
- `bus mode`:总线模式（2：推挽模式）。
- `chip select`:芯片选择模式（0：无关）。
- `power mode`:电源模式（2：开启）。
- `bus width`:总线宽度（4 位）。
- `timing spec`:传输模式（SDR104）。
- `signal voltage`:信号电压（1.8 V）。
- `driver type`:驱动类型（B 类型）。

### SDIO 设备调试

- 请参考设备厂商提供的驱动源码，启用其内置调试选项进行编译与调试。
- 关于 WIFI SDIO 设备调试请查阅 [WIFI_Driver_Debug_Guide](../driver_develop_guide/33-WIFI_Driver_Debug_Guide.html) 。

## 常见问题

### 1. 无法识别 SDIO 设备
- **设备树检查**：确认 SDIO 接口状态是否配置为 `okay`。
- **时钟配置检查**：确保时钟频率和模式配置正确。

### 2. 卡运行模式与预期不符  
- **DTS 配置检查**：核对设备树中目标运行模式的配置（如电压域等）。
- **硬件支持检查**：确保硬件提供目标模式所需的 IO 电压。

### 3. 控制器未初始化

- **初始化确认**：通过检查 `/sys/kernel/debug/mmc[x]` 文件夹，确认当前已初始化的 MMC 控制器数量。

#### 注意事项
- 如果 SDIO0 在 DTS 中配置为 `status="disabled"`，而 SDIO1 配置为 `status="okay"`：
  - 在 `/sys/kernel/debug` 中，SDIO1 会被枚举为 `mmc0`，依次类推。
- 可通过以下命令查看对应 SDIO 控制器的初始化状态：
	```bash
	cat /sys/firmware/devicetree/base/soc/hsio_apb/sdhci@xxx/status
	```
