From b5f1a9c00bca5dfb03cd3e5abda4a5758d72de42 Mon Sep 17 00:00:00 2001 From: Troy Date: Wed, 18 Mar 2026 14:11:55 +0800 Subject: [PATCH 01/30] k3: device: peripheral_driver: I2C: initial version --- .../device/peripheral_driver/06-I2C.md | 459 ++++++++++++++++++ 1 file changed, 459 insertions(+) diff --git a/zh/k3_buildroot/device/peripheral_driver/06-I2C.md b/zh/k3_buildroot/device/peripheral_driver/06-I2C.md index ead7c29..61d4a04 100644 --- a/zh/k3_buildroot/device/peripheral_driver/06-I2C.md +++ b/zh/k3_buildroot/device/peripheral_driver/06-I2C.md @@ -1,2 +1,461 @@ # I2C +介绍 K3 平台 I2C 控制器驱动的功能、配置方式和典型设备接入方法。 + +## 模块介绍 + +I2C(Inter-Integrated Circuit)是一种两线式串行总线,使用 SCL(时钟)和 SDA(数据)完成主设备与从设备之间的通信,常用于连接 EEPROM、PMIC、触摸屏、摄像头 sensor、Type-C 控制器等低速外设。 + +在 Linux 中,I2C 软件栈主要分为三层: + +1. 用户空间:通过 `/dev/i2c-x` 访问总线设备; +2. 内核空间:I2C core、adapter 驱动和各类 I2C client 驱动; +3. 硬件层:SoC 内部 I2C 控制器和挂接在总线上的外设。 + +### 功能介绍 + +Linux I2C 子系统中: + +- I2C core 负责统一管理 adapter、client 和消息传输流程; +- I2C bus driver 负责把具体控制器硬件能力抽象成 `i2c_adapter`; +- I2C client driver 负责具体外设功能实现,如摄像头、EEPROM、PMIC、触摸屏等; +- 用户态程序可通过 `i2c-dev` 提供的字符设备接口进行访问。 + +K3 的 I2C 控制器节点虽然位于 K3 的 DTS/DTSI 中,但其 `compatible` 仍为 `spacemit,k1-i2c`,对应驱动仍然是 SpacemiT 自研 I2C 控制器驱动,因此 K3 在控制器驱动模型上与 K1 保持一致。 + +### 源码结构介绍 + +K3 I2C 控制器驱动及相关描述主要位于以下位置: + +```text +linux-6.18/ +|-- drivers/i2c/ +| |-- i2c-core-base.c # I2C 子系统核心框架 +| |-- i2c-dev.c # /dev/i2c-x 字符设备接口 +| `-- busses/i2c-k1.c # SpacemiT I2C 控制器驱动(K3 也使用该驱动) +|-- Documentation/devicetree/bindings/i2c/ +| `-- spacemit,k1-i2c.yaml # SpacemiT I2C 控制器 DT binding +`-- arch/riscv/boot/dts/spacemit/ + |-- k3.dtsi # K3 A-domain I2C 控制器节点 + |-- k3-rdomain.dtsi # K3 R-domain I2C 控制器节点 + `-- k3*.dts # 各板级 I2C 控制器和从设备使用示例 +``` + +对比 K1 SDK 可见: + +- K1(`linux-6.6`)中的控制器驱动文件为 `drivers/i2c/busses/i2c-k1x.c`; +- K3(`linux-6.18`)中的控制器驱动文件演进为 `drivers/i2c/busses/i2c-k1.c`; +- 两者匹配的 `compatible` 都是 `spacemit,k1-i2c`,说明 K3 仍复用同一套控制器 IP 描述; +- K1 老驱动支持 PIO / 中断 / FIFO / DMA / 高速模式等更多传输路径,而 K3 当前使用的新驱动实现更聚焦标准/快速模式主机传输,驱动结构更简洁,DT 属性也更收敛。 + +## 关键特性 + +### 特性 + +| 特性 | 特性说明 | +| :----- | :---- | +| 支持多组 I2C 控制器 | K3 在 A-domain 提供 `i2c0/i2c1/i2c2/i2c3/i2c4/i2c5/i2c6/i2c8`,在 R-domain 提供 `r_i2c0/r_i2c1` | +| 支持标准模式与快速模式 | 驱动根据 `clock-frequency` 设置总线速率,支持 100kHz / 400kHz | +| 统一 DT binding | K3 继续使用 `spacemit,k1-i2c` binding | +| 支持总线恢复 | 驱动通过寄存器和总线状态检测实现 bus busy 恢复和 bus reset | +| 支持 SDA glitch fix 配置 | 可通过 `spacemit,sda-glitch-nofix` 控制是否旁路 SDA glitch fix 逻辑 | + +## 配置介绍 + +主要包括驱动使能配置和 DTS 配置。 + +### CONFIG 配置 + +K3 平台使用 Linux 标准 I2C 子系统和 SpacemiT 控制器驱动,常见配置如下: + +- `CONFIG_I2C` +- `CONFIG_I2C_CHARDEV` +- `CONFIG_I2C_SPACEMIT_K1` + +菜单路径如下: + +```text +Device Drivers + I2C support + I2C support (I2C [=y]) + I2C device interface (I2C_CHARDEV [=y]) + I2C Hardware Bus support + Spacemit k1 I2C adapter (I2C_SPACEMIT_K1 [=y]) +``` + +### DTS 配置 + +#### 控制器基础节点 + +K3 的 I2C 控制器节点定义在 `k3.dtsi` 和 `k3-rdomain.dtsi` 中,典型定义如下: + +```dts + i2c6: i2c@d4018800 { + compatible = "spacemit,k1-i2c"; + reg = <0x0 0xd4018800 0x0 0x38>; + #address-cells = <1>; + #size-cells = <0>; + interrupt-parent = <&saplic>; + interrupts = <70 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&syscon_apbc CLK_APBC_TWSI6>, + <&syscon_apbc CLK_APBC_TWSI6_BUS>; + clock-names = "func", "bus"; + clock-frequency = <400000>; + resets = <&syscon_apbc RESET_APBC_TWSI6>; + power-domains = <&power K3_PMU_DUMMY_PWR_DOMAIN>; + status = "disabled"; + }; +``` + +各属性含义如下: + +| 属性 | 说明 | +| ---- | ---- | +| `compatible` | 固定为 `spacemit,k1-i2c`,用于匹配控制器驱动 | +| `reg` | 控制器寄存器地址空间 | +| `interrupts` | 控制器中断号 | +| `clocks` / `clock-names` | 功能时钟与总线时钟,名称固定为 `func` 和 `bus` | +| `clock-frequency` | I2C 总线目标频率,常用 100000 或 400000 | +| `resets` | 控制器复位 | +| `status` | 设为 `okay` 表示启用该控制器 | + +#### binding 支持的属性 + +根据 `Documentation/devicetree/bindings/i2c/spacemit,k1-i2c.yaml`,K3 当前主要使用以下属性: + +| 配置 | 说明 | +| ---- | ---- | +| `clock-frequency` | 设置 I2C 总线频率,默认 400000,最大 3300000 | +| `spacemit,sda-glitch-nofix` | 旁路控制器内部 SDA glitch fix 逻辑 | + +其中,K3 当前驱动实际聚焦标准模式和快速模式,因此板级配置通常使用: + +- `clock-frequency = <100000>;` +- `clock-frequency = <400000>;` + +例如将 i2c6 设置为 400kHz: + +```dts +&i2c6 { + clock-frequency = <400000>; +}; +``` + +如果需要旁路 SDA glitch fix: + +```dts +&i2c6 { + spacemit,sda-glitch-nofix; +}; +``` + +> 说明:K1 文档中曾使用 `spacemit,i2c-fast-mode`、`spacemit,i2c-high-mode`、`spacemit,dma-disable` 等属性说明速度模式和 DMA 传输。但在 K3 当前 SDK 中,这些属性已不再是主线 binding 的标准写法,控制器驱动也已经调整为主要依据 `clock-frequency` 和现有 binding 属性完成配置,因此 K3 文档建议以 `clock-frequency` 为准。 + +#### pinctrl 配置 + +I2C 的 SCL/SDA 复用由 pinctrl 完成。使用前需结合原理图和 pinctrl 定义,选择对应引脚组。 + +以 K3 EVB 上的 `i2c6` 为例: + +```dts +&i2c6 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c6_0_cfg>; + status = "okay"; +}; +``` + +如果是其他板型,则可能使用不同 pinctrl 组,例如: + +- `&i2c0 { pinctrl-0 = <&i2c0_0_cfg>; };` +- `&i2c3 { pinctrl-0 = <&i2c3_2_cfg>; };` +- `&i2c8 { pinctrl-0 = <&i2c8_cfg>; };` + +#### I2C 从设备配置 + +控制器节点启用后,可在其下添加具体的 I2C 从设备节点。常见步骤如下。 + +##### 1. 确认设备类型 + +首先根据所接外设确定 `compatible`,以便匹配对应 client 驱动。例如: + +- EEPROM:`atmel,24c02` +- 摄像头:`ovti,ov5647`、`sony,imx219` +- PMIC:`spacemit,p1` +- Type-C 控制器:`fcs,fusb302` + +##### 2. 确认设备地址 + +I2C 从设备需通过 `reg` 指定 7-bit 从地址。 + +例如 AT24C02 EEPROM 地址为 `0x50`: + +```dts +eeprom@50 { + compatible = "atmel,24c02"; + reg = <0x50>; +}; +``` + +##### 3. 配置设备控制信号 + +若设备需要额外 GPIO,如 reset、irq、pwdn 等,也应一并补充。 + +例如摄像头 sensor: + +```dts +ov5647@36 { + compatible = "ovti,ov5647"; + reg = <0x36>; + csi-id = <0>; + vdd-supply = <&aldo3>; + pwdn-gpios = <&gpio 1 18 GPIO_ACTIVE_HIGH>; + status = "okay"; +}; +``` + +##### 4. 根据设备手册补充功能属性 + +不同 client 设备需要的属性不同,例如: + +- EEPROM 可增加 `read-only`、`pagesize`、`nvmem-layout`; +- 触摸屏可增加 `irq-gpios`、`reset-gpios`、尺寸参数; +- PMIC 可增加 regulator 子节点; +- 摄像头可增加供电、时钟、lane、endpoint 等属性。 + +### DTS 示例 + +#### 示例 1:K3 EVB 上启用 i2c6 并挂接 EEPROM + +该示例参考 `k3_evb.dts`: + +```dts +&i2c6 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c6_0_cfg>; + status = "okay"; + + eeprom@50 { + compatible = "atmel,24c02"; + reg = <0x50>; + read-only; + status = "okay"; + + nvmem-layout { + compatible = "onie,tlv-layout"; + + product_name: product-name { + }; + }; + }; +}; +``` + +#### 示例 2:K3 EVB 上启用 i2c0 并挂接 OV5647 + +该示例参考 `k3_evb.dts`: + +```dts +&i2c0 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c0_2_cfg>; + status = "okay"; + + ov5647@36 { + compatible = "ovti,ov5647"; + reg = <0x36>; + csi-id = <0>; + vdd-supply = <&aldo3>; + pwdn-gpios = <&gpio 1 18 GPIO_ACTIVE_HIGH>; + status = "okay"; + }; +}; +``` + +#### 示例 3:K3 COM260 上启用 i2c0 并挂接 Type-C 控制器 + +该示例参考 `k3_com260.dts` / `k3_com260_kit_v02.dts`: + +```dts +&i2c0 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c0_0_cfg>; + status = "okay"; + + tcpc@25 { + compatible = "fcs,fusb302"; + reg = <0x25>; + interrupt-parent = <&gpio>; + interrupts = <114 IRQ_TYPE_LEVEL_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&fusb302_irq>; + vbus-5v-gpios = <&gpio 1 10 GPIO_ACTIVE_HIGH>; + status = "okay"; + }; +}; +``` + +## K1 与 K3 驱动差异说明 + +为了更好理解 K1 文档写法,需要结合 K1/K3 驱动实现差异来看: + +### 1. 控制器 IP 和 compatible 基本一致 + +K1 与 K3 的 DTS 都使用: + +```dts +compatible = "spacemit,k1-i2c"; +``` + +说明 K3 沿用了同一类 I2C 控制器 IP,因此 K1 文档中的“控制器节点 + pinctrl + 子设备挂接”的总体写法仍然成立。 + +### 2. 驱动文件发生了演进 + +- K1 SDK:`drivers/i2c/busses/i2c-k1x.c` +- K3 SDK:`drivers/i2c/busses/i2c-k1.c` + +K1 老驱动实现中包含更多模式和扩展逻辑,例如: + +- PIO / interrupt / FIFO / DMA 多种传输路径; +- 更显式的 fast / high mode 控制; +- 一些旧版 DT 属性配合使用。 + +K3 当前驱动实现则更简洁: + +- 仍保留控制器复位、总线恢复、中断传输等核心能力; +- 通过 `clock-frequency` 配置目标速率; +- 使用 binding 中定义的 `spacemit,sda-glitch-nofix` 作为可选控制项; +- DTS 节点中增加了 `resets`、`power-domains` 等 K3 平台资源描述。 + +### 3. K3 文档写法应以当前 SDK 为准 + +因此,在编写 K3 文档时: + +- 可以继承 K1 文档的组织结构; +- 但具体属性说明应以 `linux-6.18` 中的驱动、binding 和 DTS 用法为准; +- 不再直接照搬 K1 文档中已不匹配当前 K3 SDK 的旧属性描述。 + +## 接口介绍 + +### API 介绍 + +内核态: + +I2C client 驱动开发可直接使用 Linux 标准 I2C 接口,例如: + +- `i2c_transfer()` +- `i2c_master_send()` +- `i2c_master_recv()` +- SMBus 系列接口 + +具体可参考内核文档: + +- `Documentation/i2c/writing-clients.rst` + +用户态: + +使能 `CONFIG_I2C_CHARDEV` 后,用户空间可通过 `/dev/i2c-x` 访问对应总线设备,常结合 `ioctl(I2C_SLAVE)`、`read()`、`write()` 使用。可参考: + +- `Documentation/i2c/dev-interface.rst` + +### demo 示例 + +下面给出一个简单的用户态读写示例: + +```c +#include +#include +#include +#include +#include +#include +#include + +#define I2C_DEV_FILE "/dev/i2c-1" +#define DEVICE_ADDR 0x50 + +int write_to_i2c(int fd, uint8_t reg, uint8_t value) +{ + uint8_t buf[2] = { reg, value }; + + if (write(fd, buf, 2) != 2) { + perror("I2C write failed"); + return -1; + } + + return 0; +} + +int read_from_i2c(int fd, uint8_t reg, uint8_t *data, size_t len) +{ + if (write(fd, ®, 1) != 1) { + perror("I2C write register failed"); + return -1; + } + + if (read(fd, data, len) != (ssize_t)len) { + perror("I2C read failed"); + return -1; + } + + return 0; +} + +int main(void) +{ + int fd; + uint8_t data = 0; + + fd = open(I2C_DEV_FILE, O_RDWR); + if (fd < 0) { + perror("open i2c device failed"); + return -1; + } + + if (ioctl(fd, I2C_SLAVE, DEVICE_ADDR) < 0) { + perror("set slave address failed"); + close(fd); + return -1; + } + + if (write_to_i2c(fd, 0x00, 0x12) < 0) + goto out; + + if (read_from_i2c(fd, 0x00, &data, 1) < 0) + goto out; + + printf("read value: 0x%02x\n", data); + +out: + close(fd); + return 0; +} +``` + +## 调试方法 + +### 查看 I2C 控制器是否注册成功 + +```bash +dmesg | grep -i i2c +ls /dev/i2c-* +``` + +### 扫描总线设备 + +安装 `i2c-tools` 后可使用: + +```bash +i2cdetect -y 6 +``` + +例如扫描 `i2c6`。 + +### 读写 EEPROM 示例 + +```bash +i2cget -y 6 0x50 0x00 +i2cset -y 6 0x50 0x00 0x12 +``` + +> 注意:对 EEPROM、PMIC 等器件进行写操作前,请确认设备支持写入,避免误改关键配置。 From 218c05323d0827705c7b848df1c40c6555f4bf68 Mon Sep 17 00:00:00 2001 From: Troy Date: Wed, 18 Mar 2026 14:24:50 +0800 Subject: [PATCH 02/30] k3: device: peripheral_driver: SPI: initial version --- .../device/peripheral_driver/SPI.md | 592 ++++++++++++++---- 1 file changed, 476 insertions(+), 116 deletions(-) diff --git a/zh/k3_buildroot/device/peripheral_driver/SPI.md b/zh/k3_buildroot/device/peripheral_driver/SPI.md index 8cb620d..9b49883 100644 --- a/zh/k3_buildroot/device/peripheral_driver/SPI.md +++ b/zh/k3_buildroot/device/peripheral_driver/SPI.md @@ -1,200 +1,560 @@ # SPI -介绍 SPI 的功能和使用方法。 +介绍 K3 平台 SPI 控制器驱动的能力、设备树配置方法、典型 consumer 接入方式,以及调试与验证方法。 ## 模块介绍 -**SPI(Serial Peripheral Interface)** 是一种 SoC 与外设之间的串行通信接口,仅支持 x1 模式。SPI 有主设备(Master)和从设备(Slave)两种模式,通常为一个主设备控制一个或多个从设备进行通信。主设备选择一个从设备进行通信,完成数据交互。主设备负责提供时钟,并发起读写操作。K3 SPI 当前仅支持主设备模式。 +SPI(Serial Peripheral Interface)是一种同步串行总线,常用于连接 SPI NOR Flash、SPI NAND、ADC、DAC、TPM、安全芯片、显示控制器、传感器、EtherCAT 从站芯片等外设。SPI 通常由一个主控制器(Master)配合一个或多个从设备(Slave)组成,主控制器负责产生时钟并发起数据传输。 +从客户使用角度看,SPI 文档最关心的通常不是“SPI 控制器内部寄存器长什么样”,而是以下问题: + +- K3 上有哪些 SPI 控制器可用; +- 某个 SPI 外设应该挂在哪个控制器下面; +- 设备树节点怎么写; +- `compatible`、`reg`、`spi-max-frequency`、`mode` 这些参数怎么选; +- 控制器支持哪些能力,什么时候会走 DMA; +- 怎么验证板级连接和驱动是否工作正常。 + +K3 当前提供两类 SPI 控制器相关接口: + +- **普通 SPI 控制器**:用于连接常规 SPI 外设; +- **QSPI 控制器**:更适合连接 SPI NOR 等高吞吐 Flash,单独由 QSPI 驱动支持。 + +本文档主要描述 **普通 SPI 控制器**;QSPI 在独立文档中介绍。 ### 功能介绍 -![](static/linux_spi.png) +Linux SPI 软件栈主要分为三层: -Linux SPI 驱动框架分为三层:**SPI Core**、**SPI 控制器驱动**、**SPI 设备驱动**。 -- **SPI Core** 主要作用: - - 负责 SPI 总线和 `spi_master` 类的注册 - - SPI 控制器添加和删除 - - SPI 设备添加和删除 - - SPI 设备驱动注册与注销 +1. **SPI Core**:负责统一注册控制器、匹配 SPI 设备与 SPI 设备驱动、调度数据传输; +2. **SPI 控制器驱动**:负责把 SoC SPI 控制器抽象为 `spi_controller`; +3. **SPI 设备驱动(consumer driver)**:具体外设驱动,如 SPI NOR、TPM、ADC、显示控制器等。 -- **SPI 控制器驱动:** - - SPI Master 控制器驱动,对 SPI Master 控制器进行操作 +K3 SPI 控制器驱动实现的是第 2 层,它为上层 consumer 提供标准 Linux SPI 接口,因此客户在接入设备时,通常重点放在: -- **SPI 设备驱动:** - - 实现与具体 SPI 外设的通信。 +- 控制器节点是否启用; +- pinctrl 是否正确; +- SPI 外设子节点是否挂在正确的控制器下面; +- 外设的 `compatible`、片选、最大频率和模式是否与硬件一致。 ### 源码结构介绍 -控制器驱动代码位于 `drivers/spi` 目录下: - -``` -|-- drivers/spi/spi-spacemit-k1.c #K3 SPI 驱动 +K3 SPI 相关代码和描述主要位于以下位置: + +```text +linux-6.18/ +|-- drivers/spi/ +| |-- spi.c # SPI core 框架 +| |-- Kconfig # SPI 相关配置项 +| `-- spi-spacemit-k1.c # K3 普通 SPI 控制器驱动 +|-- Documentation/devicetree/bindings/spi/ +| `-- spi-controller.yaml # SPI 控制器通用 binding 约束 +`-- arch/riscv/boot/dts/spacemit/ + |-- k3.dtsi # K3 A-domain SPI 控制器节点 + |-- k3-rdomain.dtsi # K3 R-domain SPI 控制器节点 + `-- k3*.dts # 板级 consumer 使用示例 ``` +对比 K1 SDK 可见: + +- K1 SDK 中驱动文件为 `drivers/spi/spi-k1x.c`; +- K3 SDK 中驱动文件为 `drivers/spi/spi-spacemit-k1.c`; +- K1 DTS 中控制器 `compatible = "spacemit,k1x-spi"`; +- K3 DTS 中控制器 `compatible = "spacemit,k3-spi"`。 + +说明 K3 SPI 文档不能简单照搬 K1,而应结合 K3 当前驱动和 DTS 实际用法来写。 + ## 关键特性 -### 特性 +### 控制器能力 + +结合 `drivers/spi/spi-spacemit-k1.c` 可知,K3 SPI 控制器具备如下能力: + +| 特性 | 说明 | +| :----- | :---- | +| 工作模式 | 当前驱动仅支持 **主机模式(Master)** | +| SPI 模式 | 支持 `CPOL` / `CPHA`,可组合为 SPI mode 0/1/2/3 | +| 回环测试 | 支持 `SPI_LOOP`,便于板级联调 | +| 位宽 | 支持 `bits_per_word = 4 ~ 32` | +| 频率范围 | 驱动限定范围约为 **6.25 KHz ~ 51.2 MHz** | +| DMA/PIO | 同时支持 **PIO** 和 **DMA** 两种传输路径 | +| DMA 对齐约束 | DMA 对齐要求 64B,超小包或超大包时可能退回 PIO | +| 片选数 | 当前驱动将 `num_chipselect` 固定为 **1** | +| 典型 consumer | SPI NOR、各类寄存器型 SPI 外设、自定义板级设备 | -| 特性 | 特性说明 | -| :-----| :----| -| 通信协议 | 支持 SSP/SPI/MicroWire/PSP 协议 | -| 通信频率 | 最高频率支持 52Mbps, 最低频率支持 6.3Kbps | -| 通信倍数 | x1 | -| 支持外设 | 支持 SPI-NOR 和 SPI-NAND 闪存 | +### K3 驱动中的几个关键实现点 -### 性能参数 +从驱动代码看,客户在使用时尤其需要关注以下几点: -- **通信频率** -通讯频率只支持 51.2M / 25.6M / 12.8M / 6.4M / 3.2M / 1.6M / 1M / 200k +1. **最大频率来源于控制器节点的 `spi-max-frequency`** + 驱动在 probe 时会读取控制器节点上的 `spi-max-frequency`,若缺省或非法,则回退到默认值 `25600000`。 -- **通信倍速** -SPI 通信倍速支持 x1。 +2. **每个 transfer 也可以带自己的 `speed_hz`** + SPI core 会在传输时根据设备和 transfer 的配置调整速度,但前提是不能超过控制器支持范围。 -**测试方法** -使用示波器或逻辑分析仪检测 SCK 信号频率。 +3. **DMA 不是所有传输都一定启用** + 仅当: + - 控制器成功申请到 `tx/rx` DMA 通道; + - 传输长度足够大; + - 长度不超过驱动上限; + - DMA mapping 成功; + 时才会走 DMA。否则自动回退到 PIO。 + +4. **控制器当前只暴露 1 个片选** + 驱动中 `host->num_chipselect = 1`,因此当前 DTS 使用时通常只挂一个片选设备,`reg` 一般写 `0`。 ## 配置介绍 -主要包括 **驱动使能配置** 和 **DTS 配置** +主要包括驱动使能配置、控制器节点配置和 consumer 节点配置。 ### CONFIG 配置 -`CONFIG_SPI`:为 SPI 总线协议提供支持,默认情况,此选项为 `Y` -``` -Device Drivers - SPI support (SPI [=y]) -``` +K3 SPI 相关常用配置如下: -`CONFIG_SPI_K1`:为 K1/K3 SPI 控制器驱动提供支持,默认情况下,此选型为 `Y` -``` +- `CONFIG_SPI` +- `CONFIG_SPI_MASTER` +- `CONFIG_SPI_K1` +- 与具体 consumer 对应的配置,例如: + - `CONFIG_MTD_SPI_NOR` + - `CONFIG_SPI_SPIDEV`(如 SDK 开启且允许) + +菜单路径如下: + +```text Device Drivers + SPI support SPI support (SPI [=y]) - Spacemit K1 SPI Controller (SPI_K1 [=y]) - + SPI Master Controller Drivers + Spacemit K1 SPI Controller (SPI_K1 [=y]) ``` +> 说明:Kconfig 名称虽然仍叫 `SPI_K1`,但在 K3 SDK 中该驱动同样用于 `spacemit,k3-spi` 控制器。 + ### DTS 配置 -#### pinctrl +#### 1. 控制器节点 + +K3 的普通 SPI 控制器节点位于: + +- `arch/riscv/boot/dts/spacemit/k3.dtsi` +- `arch/riscv/boot/dts/spacemit/k3-rdomain.dtsi` + +A-domain 控制器包括: + +- `spi0` +- `spi1` +- `spi2` +- `spi3` + +R-domain 控制器包括: + +- `rspi0` +- `rspi1` +- `rspi2` + +典型控制器节点如下: + +```dts +spi0: spi@d4040000 { + compatible = "spacemit,k3-spi"; + reg = <0x0 0xd4040000 0x0 0x30>; + dmas = <&pdma DMA_SSP0_TX + &pdma DMA_SSP0_RX>; + dma-names = "tx", "rx"; + interrupt-parent = <&saplic>; + interrupts = <283 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&syscon_apbc CLK_APBC_SPI0>, + <&syscon_apbc CLK_APBC_SPI0_BUS>; + clock-names = "func", "bus"; + resets = <&syscon_apbc RESET_APBC_SPI0>; + spi-max-frequency = <51200000>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; +}; +``` -参考方案原理图,查找 SPI 所使用的引脚组。参考 [PINCTRL](01-PINCTRL.md),确认所使用的引脚配置,例如: +各属性含义如下: -假设 spi3 可以直接采用 `k1-x_pinctrl.dtsi` 中定义 `pinctrl_ssp3_0` 组。 +| 属性 | 说明 | +| ---- | ---- | +| `compatible` | 固定为 `spacemit,k3-spi`,匹配 K3 SPI 控制器驱动 | +| `reg` | 控制器寄存器基址和长度 | +| `dmas` / `dma-names` | DMA 发送和接收通道,名称固定为 `tx`、`rx` | +| `interrupts` | SPI 控制器中断号 | +| `clocks` / `clock-names` | 功能时钟与总线时钟,名称为 `func`、`bus` | +| `resets` | SPI 控制器复位 | +| `spi-max-frequency` | 控制器支持的最大时钟频率上限 | +| `#address-cells` / `#size-cells` | 子设备地址格式,SPI 设备通常使用 `reg = ` | +| `status` | 设为 `okay` 表示启用控制器 | -#### SPI 设备配置 +#### 2. pinctrl 配置 -配置 SPI 设备时需要确认**设备类型**以及**通信频率**相关参数。 +SPI 引脚通常至少包括: -- **设备类型** -确认挂载在 SPI 总线下的设备类型,如 SPI-NOR 或 SPI-NAND。 +- SCLK +- MOSI / TXD +- MISO / RXD +- CS / SS -- **通信频率** -明确 SPI 控制器与 SPI 设备之间的最大通信速率。 +客户接板时,最容易出问题的环节就是 **引脚复用和片选脚**,因此建议严格按以下步骤确认: -- **通信倍速** -SPI 通信倍速支持 x1 模式。 +1. 根据原理图确认控制器实例,例如 SPI0 / SPI1 / SPI3; +2. 根据板级 pinctrl dtsi 确认该 SPI 的 pin group 名称; +3. 确认片选脚是否由控制器原生 CS 输出,还是经 GPIO 扩展; +4. 再启用控制器节点。 -SPI 设备 DTS 配置示例: -以 SPI NOR 为例,配置最大通信频率为 26 MHz,收发均采用 x1 模式。 +典型写法如下: -```c -&spi3 { - pinctrl-0 = <&ssp3_0_cfg>; - pinctrl-names = "default"; - status = "okay"; - - flash@0 { - compatible = "jedec,spi-nor"; - reg = <0>; - spi-max-frequency = <26500000>; - m25p,fast-read; - broken-flash-reset; - vcc-supply = <&vcc_3v3_sys>; - status = "okay"; - }; +```dts +&spi0 { + pinctrl-names = "default"; + pinctrl-0 = <&spi0_0_cfg>; + status = "okay"; }; ``` -## 接口介绍 +不同板型上 pinctrl 组名称可能不同,实际请以对应 `k3*.dts` / pinctrl dtsi 为准。 -### API 介绍 +#### 3. consumer(SPI 从设备)节点配置 -**设备驱动注册与注销** +这部分是客户最关心的内容。控制器节点启用后,需要在其下面添加具体 SPI 设备子节点。 -``` -int __spi_register_driver(struct module *owner, struct spi_driver *sdrv); -void spi_unregister_driver(struct spi_driver *sdrv); -``` +一个 SPI consumer 节点通常至少包含: -**数据传输 API** +- `compatible`:匹配设备驱动; +- `reg`:片选号,当前 K3 普通 SPI 控制器一般用 `0`; +- `spi-max-frequency`:该设备可承受的最大 SPI 时钟; +- 视设备需要补充 mode、GPIO、中断、电源、reset 等属性。 -- 初始化 `spi_message` +##### 3.1 `compatible` -``` -void spi_message_init(struct spi_message *m); -``` +它决定该设备由哪个 Linux driver 接管。必须以 **设备真实型号的数据手册** 和 **内核现有驱动支持情况** 为准。 -- 添加 `spi_transfer` 到 `spi_message` 的 transfer 列表 +例如: -``` -void spi_message_add_tail(struct spi_transfer *t, struct spi_message *m); -``` +- SPI NOR:`jedec,spi-nor` +- 某些通用测试设备:`spidev`(仅限 SDK 允许时) +- 其他传感器/控制器:请查其对应 binding 文档 -- 写数据 +##### 3.2 `reg` -``` -int spi_write(struct spi_device *spi, const void *buf, size_t len); +对 SPI 子设备来说,`reg` 表示 **chip select 编号**。在当前 K3 驱动实现里,由于控制器只暴露 1 个片选,因此一般写: + +```dts +reg = <0>; ``` -- 读数据 +##### 3.3 `spi-max-frequency` -``` -int spi_read(struct spi_device *spi, void *buf, size_t len); -``` +这是 consumer 侧最关键的参数之一。它并不是“越大越好”,而应综合考虑: -- 同步传输 `spi_message` +- 外设手册允许的最大时钟; +- 板级走线质量; +- 电平、电源完整性; +- 当前工作模式(mode 0/1/2/3); +- 是否有高速连续读需求。 -``` -int spi_sync(struct spi_device *spi, struct spi_message *message); -``` +建议初期联调从 **较低频率** 开始,例如 1MHz / 6.4MHz / 12.8MHz,确认稳定后再逐步升高。 + +##### 3.4 mode 配置 + +若外设要求非默认 mode 0,需要增加: + +- `spi-cpol;` +- `spi-cpha;` -## Debug 介绍 +两者组合关系如下: -### sysfs +| 模式 | 属性 | +| ---- | ---- | +| mode 0 | 默认,无需额外属性 | +| mode 1 | `spi-cpha;` | +| mode 2 | `spi-cpol;` | +| mode 3 | `spi-cpol;` + `spi-cpha;` | -查看系统 SPI 总线设备和驱动信息 -`/sys/bus/spi` +##### 3.5 其他常见设备属性 +根据 consumer 不同,还可能需要: + +- `interrupts` / `interrupt-parent` +- `reset-gpios` +- `wp-gpios` +- `hold-gpios` +- `vcc-supply` +- 分区表、校准信息、厂商私有属性等 + +### DTS 示例 + +#### 示例 1:普通 SPI 控制器挂接一个自定义寄存器型设备 + +```dts +&spi0 { + pinctrl-names = "default"; + pinctrl-0 = <&spi0_0_cfg>; + status = "okay"; + + my_device@0 { + compatible = "vendor,my-spi-device"; + reg = <0>; + spi-max-frequency = <10000000>; + status = "okay"; + }; +}; ``` -|-- devices //spi总线上的设备 -|-- drivers //spi总线上注册的设备驱动 -|-- drivers_autoprobe -|-- drivers_probe -`-- uevent + +这个示例的含义是: + +- 启用 `spi0` 控制器; +- 在片选 0 下挂一个 SPI 从设备; +- 该设备最高工作在 10MHz; +- 具体设备驱动通过 `vendor,my-spi-device` 匹配。 + +#### 示例 2:设备要求 SPI mode 3 + +```dts +&spi1 { + pinctrl-names = "default"; + pinctrl-0 = <&spi1_0_cfg>; + status = "okay"; + + sensor@0 { + compatible = "vendor,my-sensor"; + reg = <0>; + spi-max-frequency = <8000000>; + spi-cpol; + spi-cpha; + status = "okay"; + }; +}; +``` + +#### 示例 3:K3 板级上挂接 SPI NOR 的真实用法应优先考虑 QSPI + +在 K3 SDK 现有板级 DTS 中,SPI NOR 的真实量产用法主要挂在 **QSPI 控制器** 下,而不是普通 SPI 控制器。例如: + +```dts +&qspi { + pinctrl-names = "default"; + pinctrl-0 = <&qspi_cfg>; + spacemit,qspi-tx-dma = <0>; + spacemit,qspi-rx-dma = <0>; + status = "okay"; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <26500000>; + m25p,fast-read; + broken-flash-reset; + status = "okay"; + }; +}; ``` -### debugfs +这说明从客户选型角度: + +- 如果是 **普通寄存器类 SPI 外设**,优先使用普通 SPI 控制器; +- 如果是 **启动 Flash / 大容量 SPI NOR**,优先评估 QSPI 控制器是否更合适。 + +## K1 与 K3 SPI 驱动差异说明 -用于查看系统中 SPI 设备信息 -`/sys/kernel/debug/spi-nor/spi3.0` +理解 K1/K3 差异,有助于理解为什么 K3 文档不能原样照搬 K1。 -## 测试介绍 +### 1. compatible 已变化 -### SPI NAND/NOR 读写速率测试 +- K1:`spacemit,k1x-spi` +- K3:`spacemit,k3-spi` -1. 打开 `CONFIG_MTD_TESTS` +这说明虽然两代控制器架构相近,但在 DTS 侧已经明确区分平台。 +### 2. 驱动文件不同 + +- K1:`drivers/spi/spi-k1x.c` +- K3:`drivers/spi/spi-spacemit-k1.c` + +K3 驱动实现里可直接看到: + +- 通过 `of_match_table` 匹配 `spacemit,k3-spi`; +- `mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP`; +- `bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32)`; +- `num_chipselect = 1`; +- 按条件自动选择 DMA 或 PIO。 + +因此 K3 文档应重点突出 **consumer 怎么写、频率怎么选、DMA 何时生效、mode 如何配置**,而不是沿用 K1 时代不一定还成立的旧描述。 + +### 3. K3 DTS 资源描述更完整 + +K3 控制器节点里普遍加入了: + +- `resets` +- 双时钟 `func/bus` +- `dmas` / `dma-names` +- A-domain 和 R-domain 双域分布 + +这些都是 K3 板级文档中需要向客户讲清楚的内容。 + +## 接口介绍 + +### 内核态 API + +SPI consumer 驱动通常使用 Linux 标准 SPI API: + +- `spi_write()` +- `spi_read()` +- `spi_sync()` +- `spi_sync_transfer()` +- `spi_message_init()` +- `spi_message_add_tail()` + +如果客户是开发自己的 SPI 设备驱动,建议优先使用 `spi_sync_transfer()` 或标准 `spi_message` 机制,不要直接依赖控制器私有实现。 + +### 用户态接口 + +若系统启用了 spidev,并且对应设备节点使用 `compatible = "spidev"` 或 SDK 允许的兼容方式绑定到 spidev,则用户空间可通过: + +- `/dev/spidevB.C` + +进行访问,其中: + +- `B` 是 SPI bus 号; +- `C` 是 chip select 号。 + +用户态通常通过 `ioctl()` 配置: + +- mode +- bits per word +- max speed + +然后发起全双工传输。 + +### 用户态 demo 示例 + +```c +#include +#include +#include +#include +#include +#include + +int main(void) +{ + int fd; + uint8_t mode = SPI_MODE_0; + uint8_t bits = 8; + uint32_t speed = 1000000; + uint8_t tx[2] = {0x9f, 0x00}; + uint8_t rx[2] = {0}; + struct spi_ioc_transfer tr = { + .tx_buf = (unsigned long)tx, + .rx_buf = (unsigned long)rx, + .len = sizeof(tx), + .speed_hz = speed, + .bits_per_word = bits, + }; + + fd = open("/dev/spidev0.0", O_RDWR); + if (fd < 0) { + perror("open"); + return -1; + } + + ioctl(fd, SPI_IOC_WR_MODE, &mode); + ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits); + ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed); + + if (ioctl(fd, SPI_IOC_MESSAGE(1), &tr) < 0) { + perror("SPI transfer"); + close(fd); + return -1; + } + + printf("rx: %02x %02x\n", rx[0], rx[1]); + close(fd); + return 0; +} ``` -Device Drivers - Memory Technology Device (MTD) support (MTD [=y]) - MTD tests support (DANGEROUS) (MTD_TESTS [=m]) + +## 调试方法 + +### 1. 查看控制器是否 probe 成功 + +```bash +dmesg | grep -i spi ``` -2. 运行测试命令 +### 2. 查看 SPI 总线和设备 +```bash +ls /sys/bus/spi/devices/ +ls /sys/bus/spi/drivers/ ``` -insmod mtd_speedtest.ko dev=0 #0表示spi-nand/nor的mtd设备号 + +### 3. 查看是否生成 spidev 节点 + +```bash +ls /dev/spidev* ``` +### 4. 关注 consumer 是否绑定成功 + +例如 SPI NOR: + +```bash +dmesg | grep -Ei "spi|mtd|spi-nor" +cat /proc/mtd +``` + +### 5. 回环联调建议 + +如果板上允许把 MOSI 与 MISO 短接,可配合驱动支持的 loopback 模式进行最小闭环验证。若客户在自定义设备 bring-up 早期怀疑硬件连线、时钟或 mode 配置有问题,建议: + +1. 先降频到 1MHz 左右; +2. 先确认 mode 0; +3. 再切换到设备要求的 mode; +4. 使用逻辑分析仪观察 SCLK / CS / MOSI / MISO; +5. 再逐步提高频率。 + +## 测试建议 + +### 面向 consumer 的验证思路 + +客户验证一个 SPI 设备是否接通,建议优先按以下顺序进行: + +1. **看控制器是否起来**:SPI 控制器 probe 成功; +2. **看设备是否枚举**:子节点是否被创建为 `spiB.C`; +3. **看设备驱动是否绑定**:是否进入对应 consumer 驱动; +4. **看读写是否正确**:寄存器读 ID、读状态寄存器是否正确; +5. **看高频是否稳定**:低频可用后再加速; +6. **看 DMA 是否生效**:大包传输时观察性能与日志。 + +### 对 SPI NOR 类设备的验证 + +如果最终设备是 Flash,建议额外做: + +- 识别 JEDEC ID; +- 擦写测试; +- 分区读写测试; +- 重启后数据一致性检查。 + ## FAQ + +### 1. 为什么设备树写了 SPI 节点,但设备没有工作? + +优先检查: + +- pinctrl 是否正确; +- 片选脚是否真的接到了该设备; +- `compatible` 是否与驱动匹配; +- `spi-max-frequency` 是否过高; +- mode 是否配置错误; +- 外设电源、reset、中断 GPIO 是否已就绪。 + +### 2. 为什么频率设置成 26MHz,但示波器看到不是精确 26MHz? + +驱动底层受时钟分频约束,实际输出频率通常是若干离散值逼近目标值,不一定完全等于请求值。这是正常现象,关键是 **不超过设备时序约束且通信稳定**。 + +### 3. 为什么有时走 DMA,有时走 PIO? + +这是驱动设计使然。DMA 只有在 DMA 通道存在、长度合适、对齐满足且映射成功时才启用;否则自动回落到 PIO。 From 4412c7706d4ebc95b4cd5e3dec494ba1ee66b4cd Mon Sep 17 00:00:00 2001 From: Troy Date: Wed, 18 Mar 2026 14:30:36 +0800 Subject: [PATCH 03/30] k3: device: peripheral_driver: QSPI: initial version --- .../device/peripheral_driver/07-QSPI.md | 525 ++++++++++++++++++ 1 file changed, 525 insertions(+) diff --git a/zh/k3_buildroot/device/peripheral_driver/07-QSPI.md b/zh/k3_buildroot/device/peripheral_driver/07-QSPI.md index c625be1..27f8a2e 100644 --- a/zh/k3_buildroot/device/peripheral_driver/07-QSPI.md +++ b/zh/k3_buildroot/device/peripheral_driver/07-QSPI.md @@ -1,2 +1,527 @@ # QSPI +介绍 K3 平台 QSPI 控制器驱动的能力、面向 Flash consumer 的设备树配置方法、典型板级用法以及调试方式。 + +## 模块介绍 + +QSPI(Quad SPI)是在 SPI 基础上扩展出来的高速串行接口,常用于连接 SPI NOR Flash。相比普通 SPI 控制器,QSPI 更适合处理: + +- 启动 Flash; +- 大容量固件存储; +- 高频连续读; +- 通过 memory-mapped / AHB 映射方式进行高效访问的场景。 + +从客户角度看,QSPI 文档最重要的不是控制器寄存器定义,而是: + +- K3 上 QSPI 怎么启用; +- 该控制器主要适合接哪类设备; +- `jedec,spi-nor` 这类 consumer 节点怎么写; +- 分区表怎么配; +- DTS 里那些 `spacemit,qspi-*` 属性是什么意思; +- 为什么同样是 Flash,K3 板级更倾向于挂在 QSPI 而不是普通 SPI 控制器下面。 + +K3 SDK 的实际板级 DTS 已经给出了很明确的答案: + +- **普通 SPI 控制器** 更适合通用寄存器型 SPI 外设; +- **QSPI 控制器** 更适合 SPI NOR Flash 等高吞吐存储型设备。 + +### 功能介绍 + +Linux 中,K3 QSPI 方案涉及三层: + +1. **SPI Core / SPI Memory Framework**:为 Flash 类设备提供统一访问模型; +2. **QSPI 控制器驱动**:实现 `spi-mem` 控制器能力; +3. **Flash consumer 驱动**:通常是 SPI NOR 驱动,如 `jedec,spi-nor`。 + +K3 QSPI 驱动采用的是 **spi-mem 框架**,也就是说它不是简单地把 Flash 当成一个“普通 SPI 字节流设备”去处理,而是围绕 Flash 的典型操作来优化,例如: + +- opcode / address / dummy / data 组合操作; +- LUT(Look-Up Table)序列配置; +- AHB 映射读; +- DMA 辅助收发; +- 针对读写/擦除后 AHB buffer 的失效处理。 + +这也是为什么 K3 上的 Flash consumer 文档,必须结合 **驱动代码 + DTS + consumer 设备树用法** 来写,而不能只从 generic SPI 角度描述。 + +### 源码结构介绍 + +K3 QSPI 相关代码主要位于: + +```text +linux-6.18/ +|-- drivers/spi/ +| |-- spi-mem.c # SPI memory framework +| |-- spi-nor/ # SPI NOR 相关支持(由 MTD 子系统接管) +| |-- Kconfig +| `-- spi-k1-qspi.c # K3 QSPI 控制器驱动 +`-- arch/riscv/boot/dts/spacemit/ + |-- k3.dtsi # K3 QSPI 控制器节点 + `-- k3*.dts # 板级 QSPI Flash 使用示例 +``` + +K3 的控制器节点 `compatible` 为: + +```dts +compatible = "spacemit,k3-qspi"; +``` + +K1 的板级 DTS 中也存在 QSPI 节点和使用方式,说明两代平台都把 QSPI 作为 Flash 接口的重要方案;但 K3 文档仍需以 `linux-6.18` 当前驱动和 DTS 实际写法为准。 + +## 关键特性 + +### 控制器能力 + +结合 `drivers/spi/spi-k1-qspi.c` 可知,K3 QSPI 驱动具备如下能力: + +| 特性 | 说明 | +| :----- | :---- | +| 框架模型 | 基于 `spi-mem` 框架实现 | +| 适用设备 | 重点面向 SPI NOR Flash 类设备 | +| 访问模式 | 支持命令模式(IP command)和 AHB memory-mapped read | +| LUT 序列 | 根据 `spi_mem_op` 动态生成 LUT 序列 | +| 多线模式 | 支持 single / dual / quad 线宽操作(取决于 Flash op) | +| DMA | 支持 TX DMA / RX DMA,可通过 DTS 打开或关闭 | +| 时钟控制 | 驱动会设置 QSPI 功能时钟 | +| 缓冲失效 | 写/擦除后会触发 AHB buffer 失效处理 | +| 调试接口 | 提供 sysfs 信息节点查看 DMA / buffer / 错误状态 | + +### 从客户使用角度要重点理解的能力 + +#### 1. 它不是普通 SPI 控制器的替代写法,而是更适合 Flash + +普通 SPI 控制器也能理论上挂 Flash,但在 K3 实际 SDK 中,板级 DTS 明确优先把 SPI NOR 放在 `&qspi` 下面。原因是 QSPI 驱动: + +- 更适合 Flash 的命令访问模型; +- 支持 memory mapped 读; +- 支持更高效的连续读; +- 已经在 K3 板级 DTS 中形成标准用法。 + +#### 2. DTS 中的 consumer 几乎总是 Flash + +K3 现有板级 DTS 中,QSPI 下面的典型 consumer 为: + +```dts +flash@0 { + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <26500000>; + m25p,fast-read; + broken-flash-reset; + status = "okay"; +}; +``` + +因此客户如果接的是启动 Flash 或 NOR Flash,优先参考 QSPI 文档,而不是普通 SPI 文档。 + +#### 3. DTS 可控制 DMA 开关 + +K3 板级 DTS 中常见: + +```dts +spacemit,qspi-tx-dma = <0>; +spacemit,qspi-rx-dma = <0>; +``` + +这说明 QSPI 驱动支持通过设备树控制 TX / RX DMA 是否启用。对于 bring-up 阶段,如果怀疑 DMA 路径有问题,可以先关闭 DMA,优先验证基础读写是否正常。 + +## 配置介绍 + +### CONFIG 配置 + +K3 QSPI 常用配置包括: + +- `CONFIG_SPI` +- `CONFIG_SPI_MEM` +- `CONFIG_SPI_K1_QSPI` +- `CONFIG_MTD` +- `CONFIG_MTD_SPI_NOR` + +菜单路径大致如下: + +```text +Device Drivers + SPI support + SPI support (SPI [=y]) + SPI Master Controller Drivers + Spacemit K1 QSPI Controller (SPI_K1_QSPI [=y]) + +Device Drivers + Memory Technology Device (MTD) support + SPI-NOR device support (MTD_SPI_NOR [=y]) +``` + +> 说明:Kconfig 名称虽然仍带 `K1`,但 K3 SDK 中同样用于 `spacemit,k3-qspi` 控制器。 + +### DTS 配置 + +#### 1. 控制器节点 + +K3 的 QSPI 控制器节点位于 `arch/riscv/boot/dts/spacemit/k3.dtsi`: + +```dts +qspi: spi@d420c000 { + compatible = "spacemit,k3-qspi"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x0 0xd420c000 0x0 0x1000>, + <0x0 0xb8000000 0x0 0xc00000>; + reg-names = "QuadSPI", "QuadSPI-memory"; + clocks = <&syscon_apmu CLK_APMU_QSPI>, + <&syscon_apmu CLK_APMU_QSPI_BUS>; + clock-names = "qspi", "qspi_en"; + resets = <&syscon_apmu RESET_APMU_QSPI>, + <&syscon_apmu RESET_APMU_QSPI_BUS>; + reset-names = "qspi_reset", "qspi_bus_reset"; + interrupts = <117 IRQ_TYPE_LEVEL_HIGH>; + interrupt-parent = <&saplic>; + status = "disabled"; +}; +``` + +各属性含义如下: + +| 属性 | 说明 | +| ---- | ---- | +| `compatible` | 固定为 `spacemit,k3-qspi` | +| `reg` | 第一段是 QSPI 控制器寄存器,第二段是 QSPI memory-mapped window | +| `reg-names` | 控制器寄存器区与 memory window 的名称 | +| `clocks` / `clock-names` | QSPI 功能时钟和总线时钟 | +| `resets` / `reset-names` | 控制器和 bus 复位 | +| `interrupts` | QSPI 中断号 | +| `status` | `okay` 表示启用控制器 | + +这里最值得客户注意的是:**QSPI 比普通 SPI 多了一个 memory-mapped 的地址窗口**,这正是它适合 Flash 访问的重要原因。 + +#### 2. pinctrl 配置 + +QSPI 使用前需要先确认板级引脚复用,一般包括: + +- CLK +- CS +- DAT0 +- DAT1 +- DAT2 +- DAT3 + +典型启用方式如下: + +```dts +&qspi { + pinctrl-names = "default"; + pinctrl-0 = <&qspi_cfg>; + status = "okay"; +}; +``` + +不同板级 DTS 中 pin group 名称可能不同,但 K3 现有板级大多使用 `&qspi_cfg`。 + +#### 3. QSPI 控制器私有属性 + +结合 K3 驱动和板级 DTS,可看到以下常用属性: + +| 属性 | 含义 | +| ---- | ---- | +| `spacemit,qspi-tx-dma` | 控制 TX DMA 是否启用,板级常设为 `<0>` 关闭 | +| `spacemit,qspi-rx-dma` | 控制 RX DMA 是否启用,板级常设为 `<0>` 关闭 | + +板级示例: + +```dts +&qspi { + spacemit,qspi-tx-dma = <0>; + spacemit,qspi-rx-dma = <0>; +}; +``` + +从客户调试角度建议: + +- **首板 bring-up**:建议先关闭 DMA,优先验证基础识别、读 ID、分区枚举; +- **性能调优阶段**:再按需打开 DMA 做吞吐测试。 + +#### 4. Flash consumer 节点配置 + +这是 QSPI 文档最核心的部分。K3 现有 DTS 中,QSPI 下面一般挂的是 `jedec,spi-nor`。 + +典型 consumer 写法如下: + +```dts +flash@0 { + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <26500000>; + m25p,fast-read; + broken-flash-reset; + status = "okay"; +}; +``` + +各字段说明: + +| 属性 | 说明 | +| ---- | ---- | +| `compatible` | 使用通用 SPI NOR 驱动 | +| `reg` | 片选号,通常为 0 | +| `spi-max-frequency` | Flash 工作频率,建议参考芯片手册并从较低频率起调 | +| `m25p,fast-read` | 启用 fast read 能力 | +| `broken-flash-reset` | 某些 Flash 没有可靠 reset 时使用 | +| `status` | 设为 `okay` 表示启用该 Flash | + +#### 5. Flash 分区配置 + +如果该 Flash 用于启动或固件存储,通常还需要添加固定分区: + +```dts +partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + bootinfo@0 { + reg = <0x00000000 0x00020000>; + }; + + fsbl@20000 { + reg = <0x00020000 0x00080000>; + }; + + env@a0000 { + reg = <0x000a0000 0x00010000>; + }; +}; +``` + +客户在配置分区时,应特别注意: + +- 分区地址和大小必须与实际 Flash 容量匹配; +- 启动链各阶段(FSBL、OpenSBI、U-Boot、环境区)的地址必须和固件烧写方案一致; +- 修改分区表前要先确认量产工具、升级流程、启动 ROM 约束。 + +### DTS 示例 + +#### 示例 1:最小可用 QSPI + SPI NOR 配置 + +```dts +&qspi { + pinctrl-names = "default"; + pinctrl-0 = <&qspi_cfg>; + spacemit,qspi-tx-dma = <0>; + spacemit,qspi-rx-dma = <0>; + status = "okay"; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <26500000>; + m25p,fast-read; + broken-flash-reset; + status = "okay"; + }; +}; +``` + +这个例子适合客户在新板 bring-up 早期做最基础验证。 + +#### 示例 2:带固定分区的启动 Flash 配置 + +该形式参考 `k3_deb1.dts`: + +```dts +&qspi { + pinctrl-names = "default"; + pinctrl-0 = <&qspi_cfg>; + spacemit,qspi-tx-dma = <0>; + spacemit,qspi-rx-dma = <0>; + status = "okay"; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <26500000>; + m25p,fast-read; + broken-flash-reset; + status = "okay"; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + bootinfo@0 { + reg = <0x00000000 0x00020000>; + }; + fsbl@20000 { + reg = <0x00020000 0x00080000>; + }; + env@a0000 { + reg = <0x000a0000 0x00010000>; + }; + esos@b0000 { + reg = <0x000b0000 0x00100000>; + }; + opensbi@1b0000 { + reg = <0x001b0000 0x00060000>; + }; + uboot@210000 { + reg = <0x00210000 0x005f0000>; + }; + }; + }; +}; +``` + +## K1 与 K3 的差异理解 + +为了理解 K3 文档应该怎么写,也需要参考 K1 的做法。 + +### 1. 两代平台都把 QSPI 作为 Flash 重要接口 + +从 K1 DTS 可以看到,QSPI 在多块板卡上都被启用并挂接 Flash;K3 也延续了这个方向。因此在面向客户写文档时,QSPI 的重点天然应该放在 **Flash consumer**,而不是泛泛介绍 SPI 总线。 + +### 2. K3 设备树更强调控制器资源完整描述 + +K3 `qspi` 节点中明确给出了: + +- 两段 `reg`:控制器 + memory window; +- `clock-names = "qspi", "qspi_en"`; +- `reset-names = "qspi_reset", "qspi_bus_reset"`; +- 板级 DTS 通过 `spacemit,qspi-tx-dma` / `spacemit,qspi-rx-dma` 控制 DMA。 + +这些都是 K3 文档里必须明确写给客户的内容。 + +### 3. K3 驱动是典型 spi-mem/Flash 优化写法 + +驱动里可以直接看到: + +- 通过 `spi_mem_op` 生成 LUT; +- 支持 AHB 读; +- 写/擦除后做 AHB buffer invalid; +- 暴露 sysfs 调试节点查看 DMA 和 buffer 情况; +- 适合 SPI NOR 这类存储设备。 + +所以 K3 的 QSPI 文档写法应围绕: + +- consumer 节点如何写; +- 分区如何规划; +- bring-up 阶段如何先保守配置; +- 性能和稳定性如何逐步调优。 + +## 接口介绍 + +### 内核态接口 + +K3 QSPI 控制器驱动对上层主要提供的是 `spi-mem` 能力,因此 consumer 侧通常不直接调用控制器私有 API,而是通过: + +- SPI NOR 框架; +- MTD 子系统; +- `spi-mem` 标准操作接口; + +完成识别、读写和擦除。 + +如果最终挂接的是 `jedec,spi-nor`,那客户更多接触到的是: + +- `/proc/mtd` +- `/dev/mtdX` +- `/dev/mtdblockX` + +### 用户态接口 + +当 Flash 作为 MTD 设备暴露后,用户态可通过: + +- `cat /proc/mtd` +- `mtd_debug` +- `flash_erase` +- `nandwrite`(不适用于 NOR) +- `dd`(谨慎使用) + +等方式进行验证和维护。 + +## 调试方法 + +### 1. 查看 QSPI 和 SPI NOR 是否识别成功 + +```bash +dmesg | grep -Ei "qspi|spi-nor|mtd" +cat /proc/mtd +``` + +### 2. 查看 sysfs 调试节点 + +K3 QSPI 驱动会创建调试属性,可用于查看当前 DMA / buffer / 错误情况。可以在对应设备目录下查看,例如: + +```bash +find /sys -path '*qspi_dev*' 2>/dev/null +``` + +典型可看到类似信息: + +- `qspi_info`:RX/TX DMA 开关、buffer 大小、AHB read 状态; +- `qspi_err_resp`:TX underrun、RX overflow、AHB overflow 等错误计数。 + +### 3. 验证分区是否正确创建 + +```bash +cat /proc/mtd +``` + +如果 DTS 中配置了固定分区,系统启动后应能看到对应分区名,例如: + +- `bootinfo` +- `fsbl` +- `env` +- `opensbi` +- `uboot` + +### 4. 读 ID / 读写验证 + +可结合 `mtd_debug` 做基础验证,例如: + +```bash +mtd_debug info /dev/mtd0 +mtd_debug read /dev/mtd0 0x0 0x100 /tmp/qspi_read.bin +``` + +### 5. bring-up 调试建议 + +如果首板上 QSPI Flash 无法识别,建议按以下顺序排查: + +1. 确认 pinctrl 与原理图一致; +2. 确认 Flash 型号、电压和接线方式(尤其 DAT2/DAT3); +3. 先关闭 TX/RX DMA; +4. 先降低 `spi-max-frequency`; +5. 确认 `compatible = "jedec,spi-nor"` 是否合适; +6. 观察 `dmesg` 是否有读 ID 失败、超时或中断错误; +7. 再检查分区表是否越界。 + +## 测试建议 + +### 面向客户的最小验证路径 + +推荐按下面顺序验证: + +1. **控制器起得来**:`qspi` probe 成功; +2. **Flash 识别成功**:能看到 `spi-nor` 相关日志; +3. **MTD 创建设备成功**:`/proc/mtd` 中有目标设备; +4. **分区正确**:分区名和地址范围符合预期; +5. **基础读正常**:可读 JEDEC ID / 读 flash 内容; +6. **擦写正常**:擦除后重读验证; +7. **重启一致性正常**:重启后数据与分区状态一致。 + +## FAQ + +### 1. 为什么建议 Flash 优先挂 QSPI,而不是普通 SPI? + +因为 K3 QSPI 驱动基于 `spi-mem`,更适合 Flash 的命令模型,并支持 memory-mapped read、LUT 序列和更高效的数据访问,板级 DTS 也已经把这作为标准用法。 + +### 2. 为什么 DTS 里要先把 `spacemit,qspi-tx-dma` / `spacemit,qspi-rx-dma` 设为 0? + +这是 bring-up 阶段常见做法。先用最简单路径确认 Flash 基础访问正确,再逐步启用 DMA 做性能优化,能更快定位问题。 + +### 3. 为什么系统里能看到 QSPI 控制器,但看不到 `/proc/mtd` 分区? + +通常说明控制器已经 probe,但 Flash consumer 没有正确识别。请检查: + +- `flash@0` 子节点是否存在; +- `compatible` 是否正确; +- 频率是否过高; +- Flash 供电、复位、引脚连接是否正常; +- 分区表是否写在正确节点下。 From e6ca8a5fca64571db1bdd7073c73a674ed462681 Mon Sep 17 00:00:00 2001 From: Troy Date: Wed, 18 Mar 2026 14:35:22 +0800 Subject: [PATCH 04/30] k3: device: peripheral_driver: CAN: initial version --- .../device/peripheral_driver/15-CAN.md | 505 ++++++++++++++++++ 1 file changed, 505 insertions(+) diff --git a/zh/k3_buildroot/device/peripheral_driver/15-CAN.md b/zh/k3_buildroot/device/peripheral_driver/15-CAN.md index 6103df0..df7066d 100644 --- a/zh/k3_buildroot/device/peripheral_driver/15-CAN.md +++ b/zh/k3_buildroot/device/peripheral_driver/15-CAN.md @@ -1,2 +1,507 @@ # CAN +介绍 K3 平台 CAN / CAN FD 控制器驱动的能力、设备树配置方法、用户空间使用方式以及调试方法。 + +## 模块介绍 + +CAN(Controller Area Network)是一种广泛应用于工业控制、汽车电子、机器人和边缘设备的串行总线。相比 UART、SPI 这类点对点接口,CAN 的典型特点是: + +- 多节点共享同一总线; +- 以报文为中心,而不是字节流; +- 具有仲裁、校验、错误处理和自动重发机制; +- 更适合控制类网络和现场总线场景。 + +从用户角度出发,CAN 文档通常最需要回答的是: + +- K3 上有哪些 CAN 控制器; +- 控制器对应哪个 Linux 驱动; +- 设备树该怎么写; +- 收发器电源、终端电阻、引脚复用怎么配; +- Linux 下如何起网口、收发报文、做回环测试; +- 如果波特率不通、总线报错,应该怎么排查。 + +K3 平台的 CAN 控制器使用的是 **FlexCAN 驱动体系**,K3 DTS 中控制器节点的 `compatible` 为: + +```dts +compatible = "spacemit,k1-flexcan"; +``` + +因此,K3 文档应同时结合: + +- K1 文档写法; +- K3 DTS / DTSI 中控制器节点和板级用法; +- K3 Linux 驱动 `drivers/net/can/flexcan/flexcan-core.c`; +- DT binding `Documentation/devicetree/bindings/net/can/fsl,flexcan.yaml`; + +来说明当前 SDK 的真实使用方式。 + +### 功能介绍 + +Linux CAN 软件栈主要分为三层: + +1. **CAN 网络核心(PF_CAN / SocketCAN)** + 为用户态提供标准 socket 接口; +2. **CAN 控制器驱动** + 负责驱动 SoC 内部 CAN / CAN FD 控制器; +3. **CAN 收发器与板级硬件** + 负责把控制器的数字信号转换为真正的 CAN 总线差分信号。 + +K3 的 CAN 控制器在 Linux 中表现为网络设备,例如: + +- `can0` +- `can1` +- `can2` +- `can3` +- `can4` + +如果启用了 R-domain CAN,则还可能出现对应的 rdomain 控制器实例。 + +### 源码结构介绍 + +K3 CAN 相关代码主要位于: + +```text +linux-6.18/ +|-- drivers/net/can/ +| |-- dev/ # CAN 核心框架 +| `-- flexcan/flexcan-core.c # FlexCAN 控制器驱动 +|-- Documentation/devicetree/bindings/net/can/ +| |-- fsl,flexcan.yaml # FlexCAN 控制器 binding +| `-- can-transceiver.yaml # CAN transceiver 子节点约束 +`-- arch/riscv/boot/dts/spacemit/ + |-- k3.dtsi # A-domain CAN 控制器节点 + |-- k3-rdomain.dtsi # R-domain CAN 控制器节点 + `-- k3*.dts # 板级使能和 pinctrl 示例 +``` + +结合驱动代码可以看到,K3 当前匹配的 compatible 包括: + +- `spacemit,k1-flexcan` +- `spacemit,k1-flexcan-can2.0` + +说明 K3 继续沿用 SpacemiT 这套 FlexCAN 兼容描述,文档中关于 DTS 属性解释必须以 binding 和 K3 实际代码为准。 + +## 关键特性 + +### 控制器能力 + +从 K3 的 DTS 和 FlexCAN 驱动可以提炼出以下能力: + +| 特性 | 说明 | +| :----- | :---- | +| 协议类型 | 支持 CAN,驱动体系也支持 CAN FD 能力(取决于具体 compatible / 控制器能力) | +| Linux 接口 | 使用 SocketCAN,表现为标准网络设备 | +| 控制器数量 | K3 A-domain 提供 `flexcan0 ~ flexcan4`,R-domain 提供 `rflexcan0 ~ rflexcan4` | +| 时钟源选择 | 支持 `fsl,clk-source` 属性 | +| 收发器支持 | 支持 `xceiver-supply` 和 `can-transceiver` 描述外部收发器 | +| 板级终端控制 | binding 支持 `termination-gpios` | +| 回环测试 | 可通过 SocketCAN 的 loopback / listen-only / berr-reporting 进行调试 | + +### 从用户角度最值得关注的点 + +#### 1. CAN 控制器本身不能直接上总线,必须配收发器 + +这是用户最容易忽略的地方。SoC 内部 CAN 控制器只输出控制器侧数字信号,真正接到 CANH/CANL 总线上,还需要外部 CAN transceiver 芯片。因此 DTS / 硬件设计里通常要关注: + +- 收发器供电是否打开; +- 收发器 standby / enable 引脚是否控制正确; +- 总线两端是否有 120Ω 终端电阻; +- 引脚复用是否和板子连线一致。 + +#### 2. Linux 中它是网络设备,不是字符设备 + +CAN 的用户态访问方式和 UART/I2C 不一样,不是去打开 `/dev/xxx`,而是通过: + +- `ip link set can0 up type can bitrate ...` +- `candump can0` +- `cansend can0 ...` + +来使用。这一点在文档里必须向用户讲清楚。 + +#### 3. 能不能通信,通常不是“驱动有没有加载”这么简单 + +CAN 联调要同时满足: + +- 控制器 probe 正常; +- pinctrl 正确; +- 收发器供电正常; +- 对端节点存在; +- 双方波特率一致; +- 终端电阻合理; +- 总线未短路、未悬空。 + +所以文档里不能只讲驱动,也必须讲板级和用户空间 bring-up 方法。 + +## 配置介绍 + +### CONFIG 配置 + +K3 CAN 常用配置包括: + +- `CONFIG_CAN` +- `CONFIG_CAN_RAW` +- `CONFIG_CAN_BCM` +- `CONFIG_CAN_GW` +- `CONFIG_CAN_DEV` +- `CONFIG_CAN_FLEXCAN` + +常见菜单路径如下: + +```text +Networking support + CAN bus subsystem support + CAN bus subsystem support (CAN [=y]) + Raw CAN Protocol (CAN_RAW [=y]) + Broadcast Manager CAN Protocol (CAN_BCM [=y]) + CAN Device Drivers + Platform CAN drivers with Netlink support + Support for Freescale FLEXCAN based chips +``` + +> 说明:K3 虽然使用的是 SpacemiT compatible,但底层驱动仍然是 FlexCAN 驱动框架,因此配置项仍然是 `CONFIG_CAN_FLEXCAN`。 + +### DTS 配置 + +#### 1. 控制器基础节点 + +K3 A-domain 的控制器节点位于 `k3.dtsi`,例如: + +```dts +flexcan0: fdcan@d4028000 { + compatible = "spacemit,k1-flexcan"; + reg = <0x0 0xd4028000 0x0 0x4000>; + interrupts = <161 IRQ_TYPE_LEVEL_HIGH>; + interrupt-parent = <&saplic>; + fsl,clk-source = <0>; + clocks = <&syscon_apbc CLK_APBC_CAN0>, + <&syscon_apbc CLK_APBC_CAN0_BUS>; + clock-names = "per", "ipg"; + resets = <&syscon_apbc RESET_APBC_CAN0>; + status = "disabled"; +}; +``` + +K3 R-domain 中也有对应节点,例如: + +```dts +rflexcan0: fdcan@c0883000 { + compatible = "spacemit,k1-flexcan"; + reg = <0x0 0xc0883000 0x0 0x4000>; + interrupts = <45 IRQ_TYPE_LEVEL_HIGH>; + interrupt-parent = <&saplic>; + fsl,clk-source = <0>; + clocks = <&syscon_rcpu_sysctrl CLK_RCPU_SYSCTRL_RCAN0>, + <&syscon_rcpu_sysctrl CLK_RCPU_SYSCTRL_RCAN0_BUS>; + clock-names = "per", "ipg"; + resets = <&syscon_rcpu_sysctrl RESET_RCPU_SYSCTRL_RCAN0>; + status = "disabled"; +}; +``` + +各属性含义如下: + +| 属性 | 说明 | +| ---- | ---- | +| `compatible` | 控制器匹配字符串,K3 当前使用 `spacemit,k1-flexcan` | +| `reg` | 控制器寄存器空间 | +| `interrupts` | 中断号 | +| `fsl,clk-source` | 时钟源选择,当前 DTS 中常设为 `<0>` | +| `clocks` / `clock-names` | CAN 外设时钟与 IPG 时钟 | +| `resets` | 控制器复位 | +| `status` | `okay` 表示启用该控制器 | + +#### 2. pinctrl 配置 + +CAN 使用前,必须先确认 TX / RX 对应引脚复用是否正确。典型方式如下: + +```dts +&flexcan0 { + pinctrl-names = "default"; + pinctrl-0 = <&can0_0_cfg>; + status = "okay"; +}; +``` + +具体 pin group 名称请以实际板级 DTS / pinctrl dtsi 为准。对用户来说,CAN bring-up 时最常见问题之一就是: + +- DTS 里启用了 `flexcan0`; +- 但实际上 pinctrl 配到了别的 CAN 实例,或用了错误的复用组; +- 最终表现为 `can0` 能创建,但总线永远不通。 + +#### 3. 收发器与电源描述 + +根据 `fsl,flexcan.yaml`,FlexCAN 节点支持: + +- `xceiver-supply` +- `can-transceiver` +- `termination-gpios` + +这些属性的意义非常重要。 + +##### `xceiver-supply` + +用于描述外部 CAN 收发器供电。如果板上收发器由可控 regulator 供电,建议显式配置。 + +示例: + +```dts +&flexcan0 { + xceiver-supply = <&vcc_3v3_can>; +}; +``` + +##### `can-transceiver` + +用于描述外部收发器的最大速率等约束,符合 `can-transceiver.yaml` 约定。 + +示例: + +```dts +&flexcan0 { + can-transceiver { + max-bitrate = <5000000>; + }; +}; +``` + +##### `termination-gpios` + +如果板上终端电阻通过 GPIO 控制,可增加该属性,用于软件使能或关闭终端匹配。 + +示例: + +```dts +&flexcan0 { + termination-gpios = <&gpio 1 12 GPIO_ACTIVE_LOW>; +}; +``` + +#### 4. 板级启用示例 + +一个更接近实际项目的 CAN 节点可以写成: + +```dts +&flexcan0 { + pinctrl-names = "default"; + pinctrl-0 = <&can0_0_cfg>; + xceiver-supply = <&vcc_3v3_can>; + status = "okay"; + + can-transceiver { + max-bitrate = <5000000>; + }; +}; +``` + +### 用户空间使用 + +#### 1. 查看设备是否创建成功 + +系统启动后可查看: + +```bash +ip link show | grep can +``` + +如果控制器 probe 成功,通常可看到 `can0`、`can1` 等设备。 + +#### 2. 设置波特率并启动接口 + +最常用的 bring-up 方式: + +```bash +ip link set can0 down +ip link set can0 type can bitrate 500000 +ip link set can0 up +``` + +如果需要 CAN FD: + +```bash +ip link set can0 down +ip link set can0 type can bitrate 500000 dbitrate 2000000 fd on +ip link set can0 up +``` + +> 前提是控制器、收发器和对端都支持 CAN FD。 + +#### 3. 回环测试 + +如果想先不接外部总线,只验证控制器软件通路,可启用 loopback: + +```bash +ip link set can0 down +ip link set can0 type can bitrate 500000 loopback on +ip link set can0 up +``` + +发送测试报文: + +```bash +cansend can0 123#11223344 +``` + +抓取报文: + +```bash +candump can0 +``` + +#### 4. 正常总线测试 + +在双节点或多节点 CAN 网络中: + +发送端: + +```bash +cansend can0 123#1122334455667788 +``` + +接收端: + +```bash +candump can1 +``` + +如果是单机双口回环,也可以用两路控制器互连验证。 + +## K1 与 K3 的差异理解 + +### 1. 文档结构可以参考 K1,但 K3 要更强调用户使用方式 + +K1 的 CAN 文档更多是基础介绍,而 K3 文档需要进一步强调: + +- 用户空间通过 SocketCAN 使用; +- 收发器和终端电阻必须同时考虑; +- DTS 不只是写控制器节点,还要考虑 transceiver 供电和板级限制。 + +### 2. K3 DTS 同时包含 A-domain 与 R-domain CAN 控制器 + +这意味着在 K3 上做方案设计时,需要先明确: + +- 哪一路 CAN 属于 A-domain; +- 哪一路属于 R-domain; +- 是否涉及 RCPU 场景; +- 板子实际把哪一路引到了外部接口。 + +用户如果仅凭 `can0/can1` 名称理解,很容易和原理图实例号混淆,所以文档里必须强调“先对照 DTS 和原理图确认控制器实例”。 + +### 3. K3 继续沿用 FlexCAN 驱动体系 + +从驱动 `flexcan-core.c` 可见,K3 compatible 已经接入 FlexCAN 驱动,因此 Linux 用户空间使用方式与标准 SocketCAN 保持一致,这对应用开发是好事: + +- 可以直接使用现成工具链; +- 可以沿用 Linux CAN 网络调试经验; +- 无需学习私有字符设备接口。 + +## 接口介绍 + +### 内核态接口 + +上层如果是内核态开发,一般通过 Linux 标准 CAN / netdev 机制工作,而不是直接调用控制器私有 API。应用驱动一般不需要和 FlexCAN 私有寄存器交互。 + +### 用户态接口 + +K3 CAN 用户态推荐使用 SocketCAN 工具链: + +- `ip` +- `candump` +- `cansend` +- `cangen` +- `canfdtest` + +这些工具通常来自 `iproute2` 与 `can-utils`。 + +## 调试方法 + +### 1. 查看驱动是否加载、设备是否创建 + +```bash +dmesg | grep -i can +ip -details link show can0 +``` + +### 2. 查看错误状态 + +```bash +ip -details -statistics link show can0 +``` + +重点关注: + +- state(ERROR-ACTIVE / ERROR-PASSIVE / BUS-OFF) +- bitrate +- sample-point +- restart-ms +- error counters + +### 3. 常用联调命令 + +启动接口: + +```bash +ip link set can0 type can bitrate 500000 berr-reporting on +ip link set can0 up +``` + +发送: + +```bash +cansend can0 123#DEADBEEF +``` + +接收: + +```bash +candump can0 +``` + +压力测试: + +```bash +cangen can0 -g 0 -I 123 -L 8 +``` + +### 4. 排查总线不通的建议顺序 + +推荐用户按以下顺序排查: + +1. **控制器是否创建成功**:`ip link show` 能看到 `can0`; +2. **引脚复用是否正确**:TX/RX 是否接到了正确的实例; +3. **收发器是否上电**:`xceiver-supply`、使能脚是否正常; +4. **波特率是否一致**:两端必须一致; +5. **终端电阻是否正确**:总线两端通常各 120Ω; +6. **总线物理连接是否正常**:CANH/CANL 是否接反、短路、悬空; +7. **错误状态是否已经 BUS-OFF**:若是,需要先恢复后再测。 + +## FAQ + +### 1. 为什么 `can0` 创建成功了,但收不到报文? + +通常说明驱动已经加载,但物理链路未通。优先检查: + +- pinctrl 是否正确; +- 收发器是否上电; +- 波特率是否一致; +- 是否真的接入了有报文的 CAN 总线; +- 终端电阻是否合适。 + +### 2. 为什么一发送就报错,甚至进入 BUS-OFF? + +常见原因: + +- 总线上没有其他节点应答; +- 波特率不匹配; +- 收发器异常; +- 总线接线错误; +- 终端匹配不正确。 + +### 3. 为什么只做软件联调时建议先开 loopback? + +因为 loopback 可以先验证: + +- 驱动是否正常; +- netdev 配置是否正确; +- 用户空间工具链是否能正常收发; + +这样可以把“软件问题”和“总线物理问题”先分开。 From b8b6fc9933e95dcba1b0d61e498475ded1065db5 Mon Sep 17 00:00:00 2001 From: Troy Date: Wed, 18 Mar 2026 14:49:59 +0800 Subject: [PATCH 05/30] k3: device: peripheral_driver: SDHC: initial version --- .../device/peripheral_driver/08-SDHC.md | 468 ++++++++++++++++++ 1 file changed, 468 insertions(+) diff --git a/zh/k3_buildroot/device/peripheral_driver/08-SDHC.md b/zh/k3_buildroot/device/peripheral_driver/08-SDHC.md index 0593294..1dc1f0e 100644 --- a/zh/k3_buildroot/device/peripheral_driver/08-SDHC.md +++ b/zh/k3_buildroot/device/peripheral_driver/08-SDHC.md @@ -1,2 +1,470 @@ # SDHC +介绍 K3 平台 SDHC 的功能、设备树配置方法以及调试方式。 + +## 模块介绍 + +SDHC 是 MMC/SD/SDIO 设备的主控制器。K3 平台的 SDHC 文档主要面向三类典型场景: + +- **SD 卡**:可插拔存储卡; +- **SDIO**:常用于 Wi‑Fi 等板载外设; +- **eMMC**:板载固定存储,通常用于系统启动和根文件系统。 + +从用户角度看,最重要的不是控制器寄存器细节,而是: + +- K3 上有几个 SDHC 控制器、分别适合什么用途; +- `sdcard` / `sdio` / `emmc` 节点怎么写; +- `bus-width`、`non-removable`、`cd-gpios`、`vmmc-supply`、`vqmmc-supply` 这些属性是什么意思; +- eMMC HS400、SD 卡 UHS、电压切换这些能力怎么体现; +- Linux 下怎么看设备有没有识别成功、工作在哪个模式。 + +### 功能介绍 + +Linux MMC 框架大致分为三层: + +- **MMC Host**:主控制器驱动层,负责控制器初始化、命令发送、数据收发、电压切换和调谐; +- **MMC Core**:MMC 核心层,统一管理卡枚举、协议处理和状态机; +- **MMC Block / SDIO 功能层**:为块设备或 SDIO 外设驱动提供上层接口。 + +K3 上,用户通常能看到这几类结果: + +- eMMC 枚举后形成 `/dev/mmcblkX`; +- SD 卡插入后形成 `/dev/mmcblkX`; +- SDIO 外设不会形成块设备,而是进一步枚举出 Wi‑Fi 等功能驱动。 + +### 源码结构介绍 + +控制器相关代码主要位于: + +```text +linux-6.18/ +|-- drivers/mmc/host/ +| |-- sdhci.c # SDHCI 通用框架 +| |-- sdhci-pltfm.c # 平台层辅助代码 +| `-- sdhci-of-k1.c # SpacemiT K1/K3 SDHCI 驱动 +|-- Documentation/devicetree/bindings/mmc/ +| `-- spacemit,sdhci.yaml # SpacemiT SDHCI binding +`-- arch/riscv/boot/dts/spacemit/ + |-- k3.dtsi # K3 控制器节点定义 + `-- k3*.dts # 板级用法示例 +``` + +K3 控制器节点在 DTS 中使用: + +```dts +compatible = "spacemit,k3-sdhci"; +``` + +而驱动 `drivers/mmc/host/sdhci-of-k1.c` 同时兼容: + +- `spacemit,k1-sdhci` +- `spacemit,k3-sdhci` + +这意味着 K3 继续沿用了同一套 SDHCI 驱动框架,但文档应以 K3 当前 DTS、binding 和驱动实际行为为准。 + +## 关键特性 + +### 特性 + +| 特性 | 特性说明 | +| :----- | :---- | +| 控制器数量 | K3 提供 3 个主控制器:`sdcard`、`sdio`、`emmc` | +| 支持设备类型 | 支持 SD、SDIO、eMMC | +| eMMC 高速模式 | 支持 `mmc-hs400-1_8v` 和 `mmc-hs400-enhanced-strobe` | +| SD 卡电压切换 | 支持 UHS 电压切换,驱动会配合 pinctrl 切换到 `uhs` 状态 | +| SDIO 场景 | 支持 `mmc-pwrseq`、`keep-power-in-suspend` | +| 软件调谐 | 驱动支持 tuning,并允许通过 DTS / sysfs 调整 `tx_delaycode` | +| 调试接口 | 对 SD/SDIO 控制器暴露 `tx_delaycode` sysfs 调试节点 | + +### K3 的三个 slot 用法 + +结合 `k3.dtsi` 和板级 DTS,可把 K3 的三个控制器理解为: + +- **`sdcard`**:通常接外部 SD 卡槽; +- **`sdio`**:通常接板载 SDIO 设备,例如 Wi‑Fi 模组; +- **`emmc`**:通常接板载 eMMC。 + +这和 K1 文档中的整体思路是一致的,但 K3 命名更直接,用户更容易从 DTS 上看懂控制器用途。 + +## 配置介绍 + +主要包括驱动使能配置和 DTS 配置。 + +### CONFIG 配置 + +K3 SDHC 常见配置如下: + +`CONFIG_MMC` 为 MMC 总线协议提供支持,通常为 `Y`: + +```text +Device Drivers + MMC/SD/SDIO card support (MMC [=y]) +``` + +`CONFIG_MMC_BLOCK` 为 MMC 块设备提供支持,通常为 `Y`: + +```text +Device Drivers + MMC/SD/SDIO card support (MMC [=y]) + MMC block device driver (MMC_BLOCK [=y]) +``` + +`CONFIG_MMC_SDHCI` / `CONFIG_MMC_SDHCI_PLTFM` / `CONFIG_MMC_SDHCI_OF_K1` 为 SpacemiT SDHCI 控制器提供支持: + +```text +Device Drivers + MMC/SD/SDIO card support (MMC [=y]) + Secure Digital Host Controller Interface support (MMC_SDHCI [=y]) + SDHCI platform and OF driver helper (MMC_SDHCI_PLTFM [=y]) + SDHCI OF support for the SpacemiT SDHCI controller (MMC_SDHCI_OF_K1 [=y]) +``` + +> 注意:Kconfig 名称仍然是 `MMC_SDHCI_OF_K1`,但该驱动已经支持 `spacemit,k3-sdhci`。 + +### DTS 配置 + +#### 控制器节点 + +K3 的三个控制器节点位于 `k3.dtsi`: + +```dts +sdcard: mmc@d4280000 { + compatible = "spacemit,k3-sdhci"; + reg = <0x0 0xd4280000 0x0 0x200>; + clocks = <&syscon_apmu CLK_APMU_SDH_AXI>, + <&syscon_apmu CLK_APMU_SDH0>; + clock-names = "core", "io"; + interrupts = <99 IRQ_TYPE_LEVEL_HIGH>; + resets = <&syscon_apmu RESET_APMU_SDH_AXI>, + <&syscon_apmu RESET_APMU_SDH0>; + reset-names = "sdh_axi", "sdh0"; + status = "disabled"; +}; +``` + +`sdio` 与 `emmc` 节点写法类似,只是寄存器地址、中断号和时钟复位实例不同。 + +根据 `Documentation/devicetree/bindings/mmc/spacemit,sdhci.yaml`,用户至少需要理解这些基础资源: + +- `compatible` +- `reg` +- `interrupts` +- `clocks` +- `clock-names` + +K3 的实际 DTS 还增加了: + +- `resets` +- `reset-names` + +这部分是 K3 平台实际启用控制器所必须的资源描述。 + +#### pinctrl 配置 + +K3 板级中,SD 卡控制器通常会配置三组 pinctrl: + +- `default` +- `uhs` +- `debug` + +例如: + +```dts +&sdcard { + pinctrl-names = "default","uhs","debug"; + pinctrl-0 = <&mmc1_cfg>; + pinctrl-1 = <&mmc1_uhs_cfg>; + pinctrl-2 = <&mmc1_debug_cfg>; + status = "okay"; +}; +``` + +其中: + +- `default`:普通工作模式; +- `uhs`:在 1.8V / UHS 场景下由驱动切换使用; +- `debug`:用于调试或特殊板级场景。 + +驱动 `sdhci-of-k1.c` 中也能看到它会显式获取: + +- `pins_default` +- `pins_uhs` +- `pins_debug` + +并在电压切换时对 `pins_uhs` 做切换。 + +#### 电源配置 + +对 SD 卡和 SDIO 来说,通常需要配置: + +- `vmmc-supply`:卡本体供电; +- `vqmmc-supply`:IO 供电,常参与 3.3V / 1.8V 切换。 + +例如: + +```dts +&sdcard { + vmmc-supply = <&p3v3>; + vqmmc-supply = <&aldo1>; +}; +``` + +对 eMMC,板级设计通常会保证供电稳定,实际是否在 DTS 中显式补充 regulator,要看板级电源设计;但从用户角度,最重要的是理解: + +- **SD / SDIO 更依赖动态 IO 电压配置**; +- **eMMC 更关注总线位宽和高速模式配置**。 + +#### 卡检测配置 + +如果是外部 SD 卡槽,通常需要卡检测 GPIO,例如: + +```dts +&sdcard { + cd-gpios = <&gpio 2 22 GPIO_ACTIVE_LOW>; +}; +``` + +K3 `k3_evb.dts` 中同时还配置了: + +```dts +wp-inverted; +``` + +这说明该板级还对写保护相关极性做了约定。对用户来说,这类 GPIO 极性必须按原理图和插槽设计来配,配错了会表现为: + +- 卡一直“插着”; +- 或一直“没插卡”; +- 或写保护状态异常。 + +#### SD 卡配置示例 + +K3 EVB 上的 `sdcard` 典型写法如下: + +```dts +&sdcard { + pinctrl-names = "default","uhs","debug"; + pinctrl-0 = <&mmc1_cfg>; + pinctrl-1 = <&mmc1_uhs_cfg>; + pinctrl-2 = <&mmc1_debug_cfg>; + bus-width = <4>; + wp-inverted; + cd-gpios = <&gpio 2 22 GPIO_ACTIVE_LOW>; + vmmc-supply = <&p3v3>; + vqmmc-supply = <&aldo1>; + no-mmc; + no-sdio; + clock-frequency = <204800000>; + spacemit,tx_delaycode = <0x1f>; + status = "okay"; +}; +``` + +其中关键点是: + +- `bus-width = <4>`:SD 卡通常为 4-bit; +- `no-mmc` / `no-sdio`:明确这个 slot 只用于 SD 卡; +- `clock-frequency`:指定目标时钟; +- `spacemit,tx_delaycode`:指定调谐相关发送延时参数。 + +#### SDIO 配置示例 + +K3 EVB 中的 `sdio` 写法如下: + +```dts +&sdio { + pinctrl-names = "default"; + pinctrl-0 = <&mmc2_cfg>; + bus-width = <4>; + non-removable; + vmmc-supply = <&vmmc_sdio>; + vqmmc-supply = <&p1v8>; + mmc-pwrseq = <&sdio_pwrseq>; + no-mmc; + no-sd; + keep-power-in-suspend; + clock-frequency = <375000000>; + status = "disabled"; +}; +``` + +这类配置通常用于板载 SDIO Wi‑Fi,关键点是: + +- `non-removable`:板载设备不可插拔; +- `mmc-pwrseq`:由电源时序节点控制上电/复位; +- `keep-power-in-suspend`:休眠时保持供电,常见于 Wi‑Fi 场景; +- `no-mmc` / `no-sd`:明确这个 slot 只用于 SDIO。 + +#### eMMC 配置示例 + +K3 EVB 上的 `emmc` 节点: + +```dts +&emmc { + bus-width = <8>; + non-removable; + mmc-hs400-1_8v; + mmc-hs400-enhanced-strobe; + no-sd; + no-sdio; + clock-frequency = <375000000>; + status = "okay"; +}; +``` + +关键点如下: + +- `bus-width = <8>`:eMMC 通常使用 8-bit; +- `non-removable`:板载固定器件; +- `mmc-hs400-1_8v`:启用 HS400 1.8V 模式; +- `mmc-hs400-enhanced-strobe`:启用 enhanced strobe; +- `no-sd` / `no-sdio`:明确该 slot 用于 eMMC。 + +### tuning 与时序相关配置 + +K3 驱动中保留了调谐相关逻辑,并支持: + +- `spacemit,tx_delaycode` +- 软件 tuning +- HS400 / HS200 相关准备与切换流程 +- enhanced strobe 控制 + +从驱动看,`spacemit,tx_delaycode` 若 DTS 未指定,会回落到默认值;而对 SD/SDIO 类型控制器,驱动还会创建 sysfs 节点,方便在线调整。 + +这意味着在用户调试高速 SD 卡或 SDIO 模组时,如果出现: + +- 高速模式不稳定; +- 读写偶发报错; +- 某些板卡批次差异导致 timing margin 变化; + +就可以围绕 `tx_delaycode` 做进一步验证。 + +## 接口介绍 + +### API 介绍 + +Linux 操作系统中,MMC/SD/SDIO 设备统一通过 MMC 子系统访问。K3 的 `sdhci-of-k1.c` 驱动在标准 SDHCI 基础上补充了以下平台能力: + +- reset 控制; +- pinctrl 状态切换; +- 电压切换辅助; +- HS400 / HS200 相关流程; +- tuning 与延时参数设置; +- SDIO 重新扫描辅助接口 `spacemit_sdio_detect_change()`。 + +其中 `spacemit_sdio_detect_change()` 被导出,说明 K3 上有些场景可能需要触发 SDIO 设备重新扫描。 + +### Debug 介绍 + +#### sysfs + +K3 驱动对 SD / SDIO 控制器会创建: + +```text +tx_delaycode +``` + +该节点可用于查看和修改当前的 `tx_delaycode`,方便调试高速模式问题。 + +例如: + +```bash +cat /sys/devices/platform/*.mmc/tx_delaycode 2>/dev/null +``` + +#### debugfs + +MMC 子系统仍然支持通过 debugfs 查看 host 当前工作状态,例如: + +```bash +cat /sys/kernel/debug/mmc0/ios +``` + +常可用于查看: + +- 当前时钟; +- 实际工作频率; +- 总线位宽; +- 时序模式; +- 信号电压。 + +## 测试介绍 + +MMC/SD 等存储可以通过标准 Linux 工具完成功能和性能测试,例如: + +- `fio` +- `dd` +- `hdparm`(针对块设备场景,谨慎使用) + +### 识别与枚举检查 + +```bash +dmesg | grep -Ei "mmc|sdhci|sdio" +lsblk +cat /proc/partitions +``` + +### 查看当前 host 状态 + +```bash +cat /sys/kernel/debug/mmc0/ios +``` + +示例输出通常包括: + +- `clock` +- `actual clock` +- `bus width` +- `timing spec` +- `signal voltage` + +### eMMC / SD 卡性能测试 + +可参考如下 `fio` 方法: + +```bash +fio -name=randread -direct=1 -iodepth=64 -rw=randread -ioengine=libaio -bs=4k -size=1G -numjobs=1 -runtime=1000 -group_reporting -filename=/testfile +fio -name=randwrite -direct=1 -iodepth=64 -rw=randwrite -ioengine=libaio -bs=4k -size=1G -numjobs=1 -runtime=1000 -group_reporting -filename=/testfile +fio -name=read -direct=1 -iodepth=64 -rw=read -ioengine=libaio -bs=512k -size=1G -numjobs=1 -runtime=1000 -group_reporting -filename=/testfile +fio -name=write -direct=1 -iodepth=64 -rw=write -ioengine=libaio -bs=512k -size=1G -numjobs=1 -runtime=1000 -group_reporting -filename=/testfile +``` + +### SDIO 设备检查 + +如果 `sdio` 用于 Wi‑Fi,则应进一步查看: + +```bash +dmesg | grep -Ei "sdio|wifi|wlan" +ip link +``` + +此时是否出现块设备并不重要,关键是 SDIO 功能驱动是否成功枚举。 + +## FAQ + +### 1. 为什么 `sdcard` 控制器起来了,但插卡后没有块设备? + +常见原因包括: + +- `cd-gpios` 极性错误; +- pinctrl 配置不对; +- `vmmc-supply` / `vqmmc-supply` 没有真正打开; +- 该 slot 被错误写成了 `no-mmc` / `no-sd` / `no-sdio` 组合; +- 卡本身或插槽硬件异常。 + +### 2. 为什么 eMMC 能识别,但速度不对或者高速模式不稳定? + +重点检查: + +- `mmc-hs400-1_8v` / `mmc-hs400-enhanced-strobe` 是否正确; +- 板级时钟与供电是否满足; +- 是否需要进一步调试 timing; +- 驱动日志里是否有 tuning 失败或 CRC 错误。 + +### 3. 为什么 SDIO 设备没起来? + +常见排查点: + +- `non-removable` 是否配置; +- `mmc-pwrseq` 是否正确控制模组上电/复位; +- `keep-power-in-suspend` 是否符合模组要求; +- 对应的 Wi‑Fi/SDIO 功能驱动和固件是否存在。 From af4e5d2d968d3f053a9df295e05e99ea9297c98b Mon Sep 17 00:00:00 2001 From: Troy Date: Wed, 18 Mar 2026 14:54:10 +0800 Subject: [PATCH 06/30] k3: device: peripheral_driver: GMAC: initial version --- .../device/peripheral_driver/09-GMAC.md | 540 ++++++++++++++++++ 1 file changed, 540 insertions(+) diff --git a/zh/k3_buildroot/device/peripheral_driver/09-GMAC.md b/zh/k3_buildroot/device/peripheral_driver/09-GMAC.md index 79c0d80..0888d27 100644 --- a/zh/k3_buildroot/device/peripheral_driver/09-GMAC.md +++ b/zh/k3_buildroot/device/peripheral_driver/09-GMAC.md @@ -1,2 +1,542 @@ # GMAC +介绍 K3 平台 GMAC 以太网控制器驱动的能力、设备树配置方法以及调试方式。 + +## 模块介绍 + +GMAC(Gigabit Media Access Controller)是 K3 平台用于千兆以太网通信的 MAC 控制器。它本身负责: + +- 以太网 MAC 层收发; +- DMA 描述符和数据搬运; +- 中断处理; +- 与 PHY 通过 MDIO 交互; +- 与 Linux 网络栈对接。 + +从用户角度看,GMAC 文档最重要的不是 MAC 内部寄存器,而是: + +- K3 上有几路网口; +- 控制器节点和 PHY 节点怎么写; +- `phy-mode`、`phy-handle`、`mdio`、`snps,reset-gpios` 分别是什么意思; +- RGMII 场景下时钟延时参数怎么理解; +- Linux 下怎么看链路是否起来、协商是否正常、吞吐是否正常。 + +### 功能介绍 + +Linux 以太网软件栈大致分为: + +- **MAC 控制器驱动**:负责 SoC 内部以太网控制器; +- **PHY 驱动**:负责外部 PHY 芯片; +- **MDIO 总线**:MAC 通过它访问 PHY 寄存器; +- **Linux 网络栈**:通过 `eth0` / `eth1` 等接口对用户提供能力。 + +K3 上的 GMAC 使用的是 **Synopsys DesignWare MAC(stmmac)** 框架,K3 DTS 中控制器节点使用: + +```dts +compatible = "spacemit,k3-gmac", "snps,dwmac-5.10a"; +``` + +也就是说,K3 的 GMAC 不是一套完全独立的私有网络框架,而是基于主线常见的 `stmmac` / `dwmac` 体系,再叠加 SpacemiT 的 glue layer 和平台参数。 + +### 源码结构介绍 + +K3 GMAC 相关代码主要位于: + +```text +linux-6.18/ +|-- drivers/net/ethernet/stmicro/stmmac/ +| |-- stmmac_main.c # stmmac 主体驱动 +| |-- stmmac_platform.c # 平台设备公共逻辑 +| |-- dwmac-spacemit.c / glue层 # SpacemiT 平台 glue layer +| `-- Kconfig +|-- Documentation/devicetree/bindings/net/ +| `-- snps,dwmac.yaml # DWMAC/stmmac 通用 binding +`-- arch/riscv/boot/dts/spacemit/ + |-- k3.dtsi # K3 GMAC 控制器节点 + `-- k3*.dts # 板级 GMAC / PHY / MDIO 实际用法 +``` + +结合 `Kconfig` 可以看到 K3 平台使用: + +```text +CONFIG_STMMAC_ETH +CONFIG_STMMAC_PLATFORM +CONFIG_DWMAC_SPACEMIT_ETHQOS +``` + +因此写 K3 GMAC 文档时,应该同时参考: + +- K1 现有文档结构; +- K3 的 `k3.dtsi` 控制器定义; +- K3 板级 `k3_evb.dts` / `k3_deb1.dts` / `k3_gemini_c0.dts` 等真实用法; +- `snps,dwmac.yaml` 中的通用属性语义。 + +## 关键特性 + +### 特性 + +| 特性 | 特性说明 | +| :----- | :---- | +| 控制器框架 | 基于 `stmmac` / `dwmac` | +| K3 compatible | `spacemit,k3-gmac` | +| MAC IP 版本 | `snps,dwmac-5.10a` | +| 网口数量 | K3 `k3.dtsi` 中定义 `eth0` ~ `eth3` | +| PHY 管理 | 通过 `mdio` 子节点管理外部 PHY | +| PHY 连接模式 | 板级当前主要使用 `rgmii` | +| 时间戳 | 提供 `ptp_ref` 时钟,支持 PTP/硬件时间戳能力 | +| DMA 优化 | 板级 DTS 中常使用 `snps,tso`、`snps,force_sf_dma_mode` | +| 时钟调优 | 支持 `spacemit,clk-tuning-enable`、`spacemit,tx-phase`、`spacemit,rx-phase` | +| 唤醒中断 | 支持 `eth_wake_irq`,可配合 `spacemit,wake-irq-enable` | + +### 从用户角度最关键的几个点 + +#### 1. GMAC 不是单独就能通信的,必须配 PHY + +K3 的 GMAC 是 MAC 控制器,真正接网线的通常是外部 PHY 芯片。要让网口正常工作,设备树里至少要有: + +- `phy-mode` +- `phy-handle` +- `mdio` 子节点 +- `ethernet-phy@X` 节点 + +否则即使 MAC 控制器 probe 成功,也可能没有链路。 + +#### 2. `phy-mode` 要和硬件走线一致 + +K3 板级实际用法里,常见为: + +```dts +phy-mode = "rgmii"; +``` + +这不是随便填的。如果板级原理图走的是 RGMII,就必须写 RGMII;如果板子实际上做了别的 PHY 接口模式,写错会导致: + +- 链路起不来; +- 协商异常; +- 能 ping 但丢包严重; +- 千兆退化到百兆甚至不稳定。 + +#### 3. RGMII 延时参数是板级成败关键 + +K3 板级 DTS 中广泛使用: + +```dts +spacemit,clk-tuning-enable; +spacemit,clk-tuning-by-delayline; +spacemit,tx-phase = <65>; +spacemit,rx-phase = <50>; +``` + +这说明 K3 的千兆网口不是简单“启用就行”,而是很依赖板级时钟和采样点调优。对于用户 bring-up 来说,这些参数非常关键。 + +## 配置介绍 + +### CONFIG 配置 + +K3 GMAC 常见配置包括: + +- `CONFIG_NETDEVICES` +- `CONFIG_ETHERNET` +- `CONFIG_STMMAC_ETH` +- `CONFIG_STMMAC_PLATFORM` +- `CONFIG_DWMAC_SPACEMIT_ETHQOS` +- `CONFIG_PHYLIB` +- `CONFIG_PTP_1588_CLOCK`(若需要时间戳/PTP 功能) + +菜单路径大致如下: + +```text +Device Drivers + Network device support + Ethernet driver support + STMicroelectronics devices + STMMAC Ethernet driver support (STMMAC_ETH) + STMMAC Platform bus support (STMMAC_PLATFORM) + Spacemit ETHQOS support (DWMAC_SPACEMIT_ETHQOS) +``` + +### DTS 配置 + +#### 控制器节点 + +K3 `k3.dtsi` 中定义了 4 路以太网控制器: + +- `eth0` +- `eth1` +- `eth2` +- `eth3` + +典型控制器节点如下: + +```dts +eth0: ethernet@cac80000 { + compatible = "spacemit,k3-gmac", "snps,dwmac-5.10a"; + reg = <0x0 0xcac80000 0x0 0x2000>; + spacemit,apmu = <&syscon_apmu>; + spacemit,ctrl-offset = <0x3e4>; + spacemit,dline-offset = <0x3e8>; + clocks = <&syscon_apmu CLK_APMU_EMAC0_BUS>, + <&syscon_apmu CLK_APMU_EMAC0_1588>; + clock-names = "stmmaceth", "ptp_ref"; + resets = <&syscon_apmu RESET_APMU_EMAC0>; + reset-names = "stmmaceth"; + interrupts = <131 IRQ_TYPE_LEVEL_HIGH>, <276 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "macirq", "eth_wake_irq"; + mac-address = [00 00 00 00 00 00]; + snps,axi-config = <&gmac_axi_setup>; + status = "disabled"; +}; +``` + +各属性含义如下: + +| 属性 | 说明 | +| ---- | ---- | +| `compatible` | K3 平台 GMAC 控制器匹配字符串 | +| `reg` | MAC 寄存器空间 | +| `spacemit,apmu` | 平台时钟/控制寄存器所在 syscon | +| `spacemit,ctrl-offset` | 平台控制寄存器偏移 | +| `spacemit,dline-offset` | 平台 delayline 控制寄存器偏移 | +| `clocks` / `clock-names` | MAC 主时钟和 PTP 参考时钟 | +| `resets` / `reset-names` | MAC 复位 | +| `interrupts` | MAC 中断和唤醒中断 | +| `mac-address` | MAC 地址,可由 bootloader 填充 | +| `snps,axi-config` | AXI 总线参数配置引用 | + +#### AXI 配置 + +K3 `k3.dtsi` 中定义了: + +```dts +gmac_axi_setup: stmmac-axi-config { + snps,wr_osr_lmt = <4>; + snps,rd_osr_lmt = <8>; + snps,blen = <0 0 0 0 16 8 4>; +}; +``` + +这部分用于配置 STMMAC 的 AXI 访问行为。对大多数用户来说一般不用改,但需要知道它是控制器节点的一部分,并且会影响 DMA 访问特性。 + +#### 板级网口启用示例 + +K3 EVB 上 `eth0` 的典型用法如下: + +```dts +ð0 { + pinctrl-names = "default"; + pinctrl-0 = <&gmac0_cfg>; + + max-speed = <1000>; + tx-fifo-depth = <8192>; + rx-fifo-depth = <8192>; + snps,tso; + snps,force_sf_dma_mode; + phy-mode = "rgmii"; + phy-handle = <&gmac0_phy>; + snps,reset-gpios = <&gpio 0 15 GPIO_ACTIVE_LOW>; + snps,reset-delays-us = <0 20000 100000>; + + spacemit,wake-irq-enable; + + spacemit,clk-tuning-enable; + spacemit,clk-tuning-by-delayline; + spacemit,tx-phase = <65>; + spacemit,rx-phase = <50>; + + status = "okay"; + + mdio { + #address-cells = <1>; + #size-cells = <0>; + compatible = "snps,dwmac-mdio"; + + gmac0_phy: ethernet-phy@1 { + compatible = "ethernet-phy-id001c.c916", + "ethernet-phy-ieee802.3-c22"; + reg = <1>; + device_type = "ethernet-phy"; + }; + }; +}; +``` + +这个例子基本涵盖了用户最关心的所有要点。 + +#### `phy-mode` + +K3 当前板级 DTS 主要使用: + +```dts +phy-mode = "rgmii"; +``` + +它表示 MAC 和 PHY 之间使用 RGMII 接口。用户必须按原理图来写,不能凭经验乱填。 + +#### `phy-handle` + +```dts +phy-handle = <&gmac0_phy>; +``` + +用于把 MAC 节点和 MDIO 总线下具体那个 PHY 节点绑定起来。没有它,MAC 可能找不到对应 PHY。 + +#### `mdio` 子节点 + +K3 板级中一般这样写: + +```dts +mdio { + #address-cells = <1>; + #size-cells = <0>; + compatible = "snps,dwmac-mdio"; + + gmac0_phy: ethernet-phy@1 { + reg = <1>; + }; +}; +``` + +这里的 `reg = <1>` 表示 PHY 地址是 1。用户如果板上 PHY strap 地址不同,这里也必须同步改。 + +#### PHY reset GPIO + +K3 板级大量使用: + +```dts +snps,reset-gpios = <&gpio 0 15 GPIO_ACTIVE_LOW>; +snps,reset-delays-us = <0 20000 100000>; +``` + +这说明 PHY 的 reset 由 MAC 节点统一控制。其含义通常是: + +- reset GPIO 是哪根脚; +- reset 是高有效还是低有效; +- assert / deassert 前后的延时要求。 + +如果 reset GPIO 配错,常见现象是: + +- PHY ID 读不出来; +- 网口始终无 link; +- 偶发能通但重启后不稳定。 + +#### 时钟调优参数 + +K3 板级 DTS 里非常关键的一组属性: + +```dts +spacemit,clk-tuning-enable; +spacemit,clk-tuning-by-delayline; +spacemit,tx-phase = <58>; +spacemit,rx-phase = <70>; +``` + +不同板子的 `tx-phase` / `rx-phase` 数值不同,说明这些参数与: + +- 板级布线; +- PHY 型号; +- 时钟路径; +- 实际信号质量; + +都有关系。 + +用户在新板 bring-up 时,应把这类参数看成 **和 pinctrl 一样重要的板级参数**,而不是普通可有可无的 tuning。 + +#### 其他常见属性 + +K3 板级常见还会使用: + +- `max-speed = <1000>` +- `tx-fifo-depth` +- `rx-fifo-depth` +- `snps,tso` +- `snps,force_sf_dma_mode` +- `spacemit,wake-irq-enable` + +这些属性主要影响: + +- 目标链路速率; +- DMA/FIFO 路径; +- 分段卸载能力; +- 唤醒和省电场景。 + +## 接口介绍 + +### 用户空间接口 + +GMAC 在 Linux 中表现为标准网卡,例如: + +- `eth0` +- `eth1` +- `eth2` +- `eth3` + +用户通常通过以下工具使用: + +- `ip` +- `ethtool` +- `ping` +- `iperf3` +- `mii-tool`(较旧) + +### 常用命令 + +查看接口: + +```bash +ip link +``` + +拉起接口: + +```bash +ip link set eth0 up +ip addr add 192.168.1.10/24 dev eth0 +``` + +查看链路和驱动信息: + +```bash +ethtool eth0 +ethtool -i eth0 +``` + +查看统计信息: + +```bash +ethtool -S eth0 +ip -s link show eth0 +``` + +查看 PHY 协商: + +```bash +ethtool eth0 +``` + +吞吐测试: + +```bash +iperf3 -s +iperf3 -c +``` + +## 调试介绍 + +### 1. 检查驱动是否 probe 成功 + +```bash +dmesg | grep -Ei "stmmac|dwmac|eth" +``` + +用户应重点看: + +- 控制器是否成功注册为 `ethX`; +- PHY 是否成功识别; +- 是否有 reset / mdio / clock 相关报错。 + +### 2. 查看网口是否创建 + +```bash +ip link +``` + +如果 DTS 已启用且驱动 probe 正常,通常能看到: + +- `eth0` +- `eth1` +- 等接口。 + +### 3. 查看链路状态和协商信息 + +```bash +ethtool eth0 +``` + +重点关注: + +- `Speed` +- `Duplex` +- `Auto-negotiation` +- `Link detected` + +如果 `Link detected: no`,通常先不要怀疑网络栈,而要先排查: + +- PHY reset 是否正常; +- PHY 地址是否写对; +- `phy-mode` 是否正确; +- 网线/交换机/对端是否正常。 + +### 4. 查看统计信息 + +```bash +ethtool -S eth0 +ip -s link show eth0 +``` + +统计信息可以帮助判断: + +- 是否有大量 CRC 错误; +- 是否有 RX/TX drop; +- 是否有 FIFO / DMA 异常; +- 是否存在链路虽然能起但数据面不稳定的情况。 + +### 5. 吞吐和稳定性测试 + +```bash +iperf3 -s +iperf3 -c -t 30 +``` + +如果存在: + +- 吞吐上不去; +- 大包丢包; +- 千兆协商异常; +- 长时间压测掉线; + +建议重点回到: + +- `tx-phase` / `rx-phase` +- PHY reset 时序 +- RGMII 时钟和板级布线 +- `phy-mode` 与硬件一致性 + +## FAQ + +### 1. 为什么 `eth0` 出来了,但 `Link detected` 一直是 `no`? + +常见原因: + +- `phy-handle` 指向错了; +- `reg` 里的 PHY 地址不对; +- `snps,reset-gpios` 或极性错误; +- PHY 没供电或没出 reset; +- 网线、对端设备或交换机异常。 + +### 2. 为什么能通但只有百兆,或者千兆不稳定? + +重点检查: + +- `phy-mode` 是否正确; +- 板级是否真的是 RGMII; +- `tx-phase` / `rx-phase` 是否需要重新调优; +- PHY 侧 strap 和供电是否正确; +- 线材和对端是否满足千兆要求。 + +### 3. 为什么不同板子的 `tx-phase` / `rx-phase` 不一样? + +因为这些参数和板级设计直接相关。不同板卡的: + +- 走线长度; +- PHY 型号; +- 时钟路径; +- SI 裕量; + +都可能不同,所以不能简单照搬别的板子数值。 + +### 4. 为什么 DTS 里 `mac-address` 是全 0? + +这通常表示 MAC 地址预留由 bootloader 或上层系统在启动时填充。用户需要确认: + +- bootloader 是否正确写入 MAC 地址; +- 最终 Linux 中 `ip link` 看到的地址是否有效; +- 多个网口是否存在 MAC 地址冲突。 From a2ac3cf3d88cc5a17184ec2e5b7fea5ee7569f2d Mon Sep 17 00:00:00 2001 From: Troy Date: Wed, 18 Mar 2026 14:58:59 +0800 Subject: [PATCH 07/30] k3: device: peripheral_driver: PCIe: initial version --- .../device/peripheral_driver/11-PCIe.md | 445 ++++++++++++++++++ 1 file changed, 445 insertions(+) diff --git a/zh/k3_buildroot/device/peripheral_driver/11-PCIe.md b/zh/k3_buildroot/device/peripheral_driver/11-PCIe.md index 989cf84..07ceec2 100644 --- a/zh/k3_buildroot/device/peripheral_driver/11-PCIe.md +++ b/zh/k3_buildroot/device/peripheral_driver/11-PCIe.md @@ -1,2 +1,447 @@ # PCIe +介绍 K3 平台 PCIe 控制器驱动的能力、设备树配置方法以及调试方式。 + +## 模块介绍 + +PCIe(Peripheral Component Interconnect Express)是一种高速串行点对点扩展总线,常用于连接: + +- NVMe SSD; +- PCIe Wi‑Fi 模组; +- 网卡、采集卡、AI/加速卡; +- 其他标准 PCIe 外设。 + +从用户角度看,PCIe 文档最需要回答的是: + +- K3 上有几个 PCIe 控制器; +- 当前支持 RC 还是 EP; +- 每个控制器支持多少 lane、最高什么速率; +- DTS 里 `phys`、`phy-names`、`num-lanes`、`max-link-speed`、`pinctrl` 应该怎么配; +- PERST# / CLKREQ# / WAKE# 对应哪组 pin; +- Linux 下怎么看链路训练和设备枚举是否成功。 + +### 功能介绍 + +Linux PCIe 软件栈大致分为三层: + +1. **PCIe 核心** + 负责总线枚举、资源分配、配置空间访问和中断管理; +2. **PCIe 主机控制器驱动** + 负责 SoC 内部 PCIe RC 控制器初始化和链路训练; +3. **PCIe 终端设备驱动** + 如 NVMe、Wi‑Fi、网卡等具体设备驱动。 + +K3 当前 PCIe 方案基于 **Synopsys DesignWare PCIe** 框架实现,控制器驱动位于 `drivers/pci/controller/dwc/` 目录中。 + +### 源码结构介绍 + +K3 PCIe 相关代码主要位于: + +```text +linux-6.18/ +|-- drivers/pci/controller/dwc/ +| |-- pcie-designware.c # DWC PCIe 公共代码 +| |-- pcie-designware-host.c # DWC Host 公共逻辑 +| |-- pcie-spacemit-k1.c # SpacemiT PCIe Host 驱动 +| |-- spacemit_pcie_phy.c # SpacemiT PCIe PHY 驱动 +| `-- Kconfig +`-- arch/riscv/boot/dts/spacemit/ + |-- k3.dtsi # K3 PCIe / PCIe PHY 节点定义 + |-- k3-pinctrl.dtsi # PCIe pinctrl 组定义 + `-- k3*.dts # 各板级 PCIe 使用示例 +``` + +K3 当前 PCIe 控制器在 DTS 中使用: + +```dts +compatible = "spacemit,k1-pcie"; +``` + +PCIe PHY 在 DTS 中使用: + +```dts +compatible = "spacemit,k3-pcie-phy"; +``` + +这说明 K3 控制器继续复用 `pcie-spacemit-k1.c` 这套主机驱动,而 PHY 侧则引入了 K3 平台自己的 `spacemit,k3-pcie-phy` 节点。 + +## 关键特性 + +### 特性 + +| 特性 | 特性说明 | +| :----- | :---- | +| 工作模式 | 当前文档对应 **RC(Root Complex)模式** | +| 驱动框架 | 基于 DesignWare PCIe Host 框架 | +| 控制器数量 | `pcie0_rc` ~ `pcie4_rc` 共 5 路 | +| PHY 数量 | `phy0` ~ `phy5` 共 6 路 PCIe PHY | +| 最大速率 | `max-link-speed = <3>`,即 Gen3 | +| lane 规模 | 根据控制器不同支持 x8 / x2 / x1,板级可按实际裁剪 | +| 中断 | 支持 `pcie_irq`,同时接入 MSI | +| IOMMU | 节点中包含 `iommu-map` | +| 板级扩展 | 支持 `spacemit,bifurcation-gpios`、`spacemit,device-detect-gpios` 等私有属性 | + +### K3 的控制器与 PHY 关系 + +结合 `k3.dtsi` 可看到: + +- `pcie0_rc` 默认可配到更大的 lane 宽度; +- `pcie1_rc`、`pcie2_rc` 为中等规模控制器; +- `pcie3_rc`、`pcie4_rc` 为 x1 控制器; +- PHY 节点单独存在,通过 `phys` / `phy-names` 进行绑定。 + +也就是说,K3 的 PCIe 设备树配置不能只盯着 RC 节点,还必须同时理解: + +- 这个 RC 对应哪几个 PHY; +- 板级 pinctrl 用的是哪一组 PERST#/WAKE#/CLKREQ#; +- 当前板子实际走了多少 lane。 + +## 配置介绍 + +主要包括 **驱动使能配置** 和 **DTS 配置**。 + +### CONFIG 配置 + +K3 PCIe 常见配置包括: + +- `CONFIG_PCI` +- `CONFIG_PCIEPORTBUS` +- `CONFIG_PCIE_DW_HOST` +- `CONFIG_PCIE_SPACEMIT_K1` +- `CONFIG_PCI_MSI` + +对应菜单路径大致如下: + +```text +Device Drivers + PCI support (PCI [=y]) + PCI controller drivers + DesignWare-based PCIe controllers + SpacemiT K1 PCIe controller (host mode) (PCIE_SPACEMIT_K1 [=y]) +``` + +> 注意:Kconfig 名称仍然是 `PCIE_SPACEMIT_K1`,但 K3 当前 DTS 仍在复用 `spacemit,k1-pcie` compatible。 + +### DTS 配置 + +#### PCIe PHY 节点 + +K3 `k3.dtsi` 中先定义了 PCIe PHY,例如: + +```dts +phy0: phy-pcie@81d00000 { + compatible = "spacemit,k3-pcie-phy"; + reg = <0x0 0x81d00000 0x0 0x1000>; + spacemit,syscon-apb-spare = <&pll>; + num-lanes = <2>; + spacemit,phy-id = <0>; + #phy-cells = <0>; + status = "disabled"; +}; +``` + +几个关键点: + +- `num-lanes` 表示这个 PHY 本身支持的 lane 数; +- `spacemit,phy-id` 表示 PHY 编号; +- RC 节点通过 `phys = <&phyX>` 把它们关联起来。 + +#### RC 控制器节点 + +以 `pcie1_rc` 为例: + +```dts +pcie1_rc: pcie@80400000 { + compatible = "spacemit,k1-pcie"; + reg = <0x0 0x80400000 0x0 0x00001000>, + <0x0 0x80500000 0x0 0x00001000>, + <0x0 0x80700000 0x0 0x00003f20>, + <0x11 0x80000000 0x0 0x00010000>, + <0x0 0x82c00000 0x0 0x00001000>; + reg-names = "dbi", "dbi2", "atu", "config", "link"; + bus-range = <0x00 0xff>; + max-link-speed = <3>; + num-lanes = <2>; + device_type = "pci"; + #address-cells = <3>; + #size-cells = <2>; + interrupts = <142 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "pcie_irq"; + clocks = <&syscon_apmu CLK_APMU_PCIE_PORTB_BUS>, + <&syscon_apmu CLK_APMU_PCIE_PORTB_MSTR>, + <&syscon_apmu CLK_APMU_PCIE_PORTB_SLV>; + clock-names = "dbi", "mstr", "slv"; + resets = <&syscon_apmu RESET_APMU_PCIE_PORTB_DBI>, + <&syscon_apmu RESET_APMU_PCIE_PORTB_MSTR>, + <&syscon_apmu RESET_APMU_PCIE_PORTB_SLV>; + reset-names = "dbi", "mstr", "slv"; + linux,pci-domain = <1>; + msi-parent = <&simsic>; + spacemit,pcie-port = <1>; + spacemit,apmu = <&syscon_apmu 0x1d0>; + status = "disabled"; +}; +``` + +控制器节点的关键点如下: + +| 属性 | 说明 | +| ---- | ---- | +| `compatible` | 当前 K3 使用 `spacemit,k1-pcie` | +| `reg` / `reg-names` | DBI、DBI2、ATU、config、link 等地址空间 | +| `max-link-speed` | 最大链路速率,`<3>` 表示 Gen3 | +| `num-lanes` | 链路 lane 数 | +| `ranges` | I/O、MEM、prefetchable MEM 地址空间映射 | +| `interrupts` | 控制器中断 | +| `clocks` / `resets` | 控制器时钟与复位 | +| `linux,pci-domain` | PCI domain 编号 | +| `msi-parent` | MSI 控制器 | +| `spacemit,pcie-port` | 平台私有端口号标识 | +| `spacemit,apmu` | 平台 PMU/APMU 配置寄存器引用 | +| `iommu-map` | 和 IOMMU 的地址映射关系 | + +#### pinctrl 配置 + +K3 的 pinctrl 中为 PCIe 定义了多组复用,例如: + +- `pcie0_1_cfg` +- `pcie1_1_cfg` +- `pcie2_2_cfg` +- `pcie3_1_cfg` +- `pcie4_1_cfg` + +这些 pin 组一般包含: + +- `PERST#` +- `WAKE#` +- `CLKREQ#` + +例如: + +```dts +pcie4_1_cfg: pcie4-1-cfg { + pcie4-0-pins { + pinmux = , /* pcie4 perst */ + , /* pcie4 wake */ + ; /* pcie4 clkreq */ + }; +}; +``` + +因此用户在新板 bring-up 时,一定要先根据原理图确认: + +- 用的是哪一路 PCIe; +- PERST#/WAKE#/CLKREQ# 实际接到了哪组 pad; +- 该选哪组 `pcieX_Y_cfg`。 + +#### 板级启用示例 + +K3 EVB 上的 `pcie2_rc` 典型配置如下: + +```dts +&pcie2_rc { + pinctrl-names = "default"; + pinctrl-0 = <&pcie2_2_cfg>; + phys = <&phy2>, <&phy3>; + phy-names = "phy0", "phy1"; + status = "okay"; +}; +``` + +这说明: + +- 该 RC 使用两路 PHY; +- PHY 通过 `phys` 和 `phy-names` 绑定; +- 板级 pinmux 选择了 `pcie2_2_cfg`。 + +#### lane 数裁剪示例 + +K3 不同板级会根据实际连接裁剪 `num-lanes`。例如: + +```dts +&pcie0_rc { + pinctrl-names = "default"; + pinctrl-0 = <&pcie0_1_cfg>; + phys = <&phy0>; + phy-names = "phy0"; + num-lanes = <2>; + status = "okay"; +}; +``` + +而在另一些板级中,同一路控制器可能会配置成: + +```dts +num-lanes = <4>; +``` + +这说明用户不能只看 `k3.dtsi` 默认值,还必须结合板级连接实际确定: + +- 真实接了几个 lane; +- 是否使用了 bifurcation; +- 绑定了几个 PHY。 + +#### 板级私有扩展属性 + +在 `k3_deb1.dts` 中还能看到: + +```dts +spacemit,bifurcation-gpios = <&gpio 2 25 GPIO_ACTIVE_HIGH>; +spacemit,device-detect-gpios = <&gpio 2 25 GPIO_ACTIVE_HIGH>; +``` + +这说明 K3 板级 PCIe 还会额外使用一些辅助 GPIO 来做: + +- bifurcation 模式选择; +- 外设存在检测; +- 某些特定板级的控制逻辑。 + +这类属性不是所有板子都要配,但在用户迁移 DTS 时需要特别注意,不能简单漏掉。 + +## 驱动实现角度的几个关键信息 + +结合 `drivers/pci/controller/dwc/pcie-spacemit-k1.c`,可以确认: + +- 驱动通过 `of_match` 匹配 `spacemit,k1-pcie`; +- 会读取 `spacemit,pcie-port`; +- 会读取 `num-lanes`,无效时回落到默认值; +- 使用 `dw_pcie_host_init()` 走标准 DWC Host 初始化流程; +- 通过 LTSSM 相关寄存器控制链路训练和状态机; +- 驱动日志里会输出与 LTSSM / L2 进入超时相关的错误信息。 + +这意味着在用户调试链路失败时,`dmesg` 中的 LTSSM 相关报错很有价值。 + +## 接口介绍 + +### 用户空间接口 + +PCIe 在 Linux 中不会表现成单一 `/dev/pcieX` 设备,而是通过 PCI 总线统一管理。用户最常用的观察入口是: + +- `lspci` +- `lspci -vv` +- `/sys/bus/pci/devices` +- `dmesg` + +### 常用命令 + +查看 PCIe 设备拓扑: + +```bash +lspci +``` + +查看某个设备详细信息: + +```bash +lspci -vvvs 0001:01:00.0 +``` + +查看 sysfs: + +```bash +ls /sys/bus/pci/devices +``` + +如果接的是 NVMe: + +```bash +nvme list +lsblk +``` + +## Debug 介绍 + +### 1. 查看控制器是否枚举成功 + +```bash +dmesg | grep -Ei "pcie|pci" +lspci +``` + +如果 RC probe 成功且链路训练正常,`lspci` 中应能看到: + +- PCI bridge; +- 终端设备(如 NVMe、Wi‑Fi 模组等)。 + +### 2. 检查链路训练与 LTSSM 相关报错 + +K3 驱动里有 LTSSM 相关状态检查,因此建议重点关注: + +```bash +dmesg | grep -Ei "LTSSM|pcie|link" +``` + +若出现: + +- link training timeout; +- LTSSM 异常; +- L2 entry timeout; + +通常应优先回到硬件连接和 PHY 绑定侧排查。 + +### 3. 检查设备是否真正枚举到 + +即使 RC 起了,如果终端设备没枚举出来,也需要继续区分: + +- 是链路根本没起来; +- 还是链路起来了,但对端设备没供电 / 没出 reset / 没被扫描到。 + +这时可重点检查: + +- `phys` / `phy-names` 是否正确; +- `num-lanes` 是否和硬件一致; +- `pinctrl` 是否选对; +- PERST# / CLKREQ# / WAKE# 是否接对; +- 终端设备供电是否正常。 + +### 4. 典型功能测试 + +#### NVMe 设备检查 + +```bash +lspci | grep -i nvme +nvme list +lsblk +``` + +#### NVMe 顺序读测试 + +```bash +fio --name read --eta-newline=5s --filename=/dev/nvme0n1 --rw=read --size=2g --io_size=10g --blocksize=1024k --ioengine=libaio --fsync=10000 --iodepth=32 --direct=1 --numjobs=1 --runtime=60 --group_reporting +``` + +#### NVMe 顺序写测试 + +```bash +fio --name write --eta-newline=5s --filename=/dev/nvme0n1 --rw=write --size=2g --io_size=60g --blocksize=1024k --ioengine=libaio --fsync=10000 --iodepth=32 --direct=1 --numjobs=1 --runtime=60 --group_reporting +``` + +## FAQ + +### 1. 为什么 `lspci` 里看不到终端设备? + +常见原因包括: + +- RC 控制器没启用; +- PHY 没有正确绑定; +- `num-lanes` 配错; +- pinctrl 选错,导致 PERST#/CLKREQ#/WAKE# 不对; +- 对端设备没供电; +- 对端设备一直处于 reset。 + +### 2. 为什么同一路控制器在不同板子上 `num-lanes` 不一样? + +因为 K3 平台支持根据板级连线裁剪 lane 规模,不同板子可能把同一路控制器接成 x1 / x2 / x4。文档和 DTS 都必须以板级实际硬件为准。 + +### 3. 为什么 K3 里控制器 compatible 还是 `spacemit,k1-pcie`? + +因为 K3 当前 SDK 继续复用了 `pcie-spacemit-k1.c` 这套控制器驱动。也就是说,驱动框架延续了 K1 的 compatible,但 PHY 和板级资源组织已经是 K3 的实现方式。 + +### 4. 用户调试 PCIe 时最优先看什么? + +建议顺序: + +1. `dmesg` 看 RC probe 是否成功; +2. `dmesg` 看 link / LTSSM 是否异常; +3. `lspci` 看是否枚举出桥和终端设备; +4. 再回到 pinctrl / PHY / lane / reset / 电源逐项排查。 From 952a4d2fd04ba45ab2833f78ba1101b1b6c616d2 Mon Sep 17 00:00:00 2001 From: Troy Date: Wed, 18 Mar 2026 15:07:35 +0800 Subject: [PATCH 08/30] k3: device: peripheral_driver: Display: initial version --- .../device/peripheral_driver/12-Display.md | 715 ++++++++++++++++++ 1 file changed, 715 insertions(+) diff --git a/zh/k3_buildroot/device/peripheral_driver/12-Display.md b/zh/k3_buildroot/device/peripheral_driver/12-Display.md index cda9573..6a4dfb7 100644 --- a/zh/k3_buildroot/device/peripheral_driver/12-Display.md +++ b/zh/k3_buildroot/device/peripheral_driver/12-Display.md @@ -1,2 +1,717 @@ # Display +介绍 SpacemiT K3 平台 Display 子系统的功能、设备树组织方式、板级使用方式以及常见调试方法。 + +## 模块介绍 + +K3 的 Display 子系统明显比 K1 更复杂,不能只按 K1 现有章节照搬。结合 K3 SDK 的驱动、`k3.dtsi` / `k3-*.dtsi` / `k3*.dts` 可以看到,K3 显示链路至少包含下面这些实际硬件和软件模块: + +- **DRM/KMS 核心驱动**; +- **DPU(Display Processing Unit)/ CRTC**; +- **MIPI DSI Host**; +- **MIPI D-PHY**; +- **DP / eDP 输出控制器**; +- **Writeback(WB)路径**; +- **MIPI 屏驱动 / Raspberry Pi 7 寸屏驱动**; +- **背光控制**,既有通用 `pwm-backlight`,也有 `spacemit-backlight`; +- **多套板级显示拓扑**,包括: + - DSI 直连 MIPI 面板; + - DSI + DPI/桥接屏; + - eDP 输出; + - DP 输出; + - DP 音频。 + +所以 K3 的显示文档必须围绕 **真实链路** 来写,而不是只围绕单个控制器写。 + +### 功能介绍 + +K3 平台基于 Linux **DRM(Direct Rendering Manager)** 框架实现显示子系统。显示数据路径大致可以抽象为: + +1. **用户空间** + 通过 libdrm、Wayland、Weston、modetest、kmscube 等工具发起显示配置和缓冲区提交; +2. **DRM/KMS 驱动层** + 管理 CRTC、plane、connector、encoder、framebuffer、atomic commit; +3. **SoC 显示硬件层** + 包括 DPU、DSI Host、DP/eDP 控制器、D-PHY、writeback 等; +4. **面板 / Bridge / 背光 / 外部显示设备** + 最终将像素输出到 LCD、eDP 面板、DP 显示器等终端设备。 + +K3 的一个关键点是:**它不是单一路显示输出**。从 DTS 可以确认至少存在两类主路径: + +- **DSI 路径**:`DPU -> DSI Host -> DPHY -> MIPI panel / bridge panel` +- **DP/eDP 路径**:`DPU -> DP/eDP controller -> external monitor / eDP panel` + +此外,K3 还存在: + +- **LCD0 / LCD1 两个 power domain**; +- **多套 DPU/CRTC 定义**(`dpu_crtc0` / `dpu_crtc1` / `dpu0_crtc0` / `dpu1_crtc0`); +- **writeback 通道**; +- **DP 音频节点**(如 `sound_card_dp0`)。 + +这几项都比 K1 文档里的模型更丰富。 + +### DRM 架构说明 + +DRM/KMS 里几个最关键的抽象如下: + +- **Framebuffer**:显示缓冲区; +- **Plane**:图层; +- **CRTC**:负责时序与扫描输出; +- **Encoder**:负责把像素流转换为具体输出接口格式; +- **Connector**:代表外部连接器或面板; +- **Panel / Bridge**:表示真实显示终端或桥接器件。 + +K3 当前驱动实现使用 atomic 模式配置,`spacemit_drm.c` 中可以看到: + +- 使用 `drm_atomic_helper_commit_*` 完成提交流程; +- 支持 framebuffer / GEM DMA helper; +- mode check 中对仅调整部分垂直时序(如 vfp)做了特殊处理。 + +## 源码结构介绍 + +K3 显示驱动主要位于: + +```text +linux-6.18/drivers/gpu/drm/spacemit/ +|-- Kconfig +|-- Makefile +|-- spacemit_drm.c # DRM core +|-- spacemit_crtc.c # CRTC 相关 +|-- spacemit_planes.c # plane 相关 +|-- spacemit_gem.c # GEM / buffer 管理 +|-- spacemit_dmmu.c # DMMU +|-- spacemit_cmdlist.c # cmdlist +|-- spacemit_wb.c # writeback +|-- spacemit_dsi.c # DSI 逻辑封装 +|-- spacemit_dphy.c # DPHY 逻辑封装 +|-- spacemit_inno_dp.c # DP / eDP 输出驱动 +|-- spacemit_mipi_panel.c # MIPI panel 驱动 +|-- raspberrypi_touchscreen.c # RPi 7-inch touchscreen panel 驱动 +|-- spacemit_bootloader.c # bootloader 相关接口 +|-- backlight/ +| `-- spacemit-backlight.c # SpacemiT backlight 驱动 +|-- dpu/ +| |-- dpu_saturn.c +| |-- dpu_saturn_hee.c +| `-- post_process_hee.c +|-- dsi/ +| |-- spacemit_dsi_drv.c +| `-- spacemit_dptc_drv.c +|-- dphy/ +| `-- spacemit_dphy_drv.c +`-- sysfs/ + |-- sysfs_dpu.c + |-- sysfs_dsi.c + |-- sysfs_dphy.c + `-- sysfs_mipi_panel.c +``` + +相比 K1,K3 源码结构里更值得注意的点有: + +- 新增了 `dpu_saturn_hee.c` / `post_process_hee.c`,说明 DPU/后处理能力有演进; +- 使用 `spacemit_inno_dp.c` 同时承载 DP / eDP 路径; +- 单独存在 `backlight/spacemit-backlight.c`; +- 单独支持 `raspberrypi_touchscreen.c`,表明 K3 板级已支持树莓派 7 寸触摸屏这一真实场景; +- 存在 `writeback` 路径,不是只有最简单的 scanout。 + +## 关键特性 + +### 特性 + +| 特性 | 特性说明 | +| :----- | :---- | +| DRM/KMS | 基于 DRM + atomic helper | +| DPU | 使用 `spacemit,dpu-saturn` | +| DSI | 支持 `spacemit,dsi-host` | +| DPHY | 支持 `spacemit,dsi-phy` | +| DP/eDP | 支持 `spacemit,inno-dp0` / `spacemit,inno-dp1` / `spacemit,inno-edp0` | +| MIPI Panel | 支持 `spacemit,mipi-panel` | +| Raspberry Pi 屏 | 支持 `raspberrypi,dpi-panel` 与 `raspberrypi,7inch-touchscreen-panel` | +| 背光 | 支持 `pwm-backlight` 与 `spacemit-backlight` | +| 双显示域 | 存在 `K3_PMU_LCD0_PWR_DOMAIN`、`K3_PMU_LCD1_PWR_DOMAIN` | +| Writeback | 存在 `wb0` 节点 | +| DP Audio | 板级 DTS 中可见 `sound_card_dp0` | + +### K3 实际比 K1 多出来的内容 + +这部分是 K3 文档必须显式讲清楚的,不能只照抄 K1: + +1. **DP / eDP 拓扑更明确** + K3 有独立的: + - `k3-edp0.dtsi` + - `k3-dp0.dtsi` + - `k3-dp1.dtsi` + +2. **双 DPU/双显示域更清晰** + 从 DTS 可见: + - `dpu0_crtc0` 挂在 `LCD0` power domain; + - `dpu1_crtc0` 挂在 `LCD1` power domain。 + +3. **板级显示场景明显更多** + K3 板级不止一种 LCD 方案,而是实际存在: + - EVB / Gemini 上的 MIPI 面板; + - COM260 上的 Raspberry Pi 7-inch 触摸屏方案; + - DC board / DEB1 上的 eDP; + - EVB / DC board / DEB1 上的 DP1 输出。 + +4. **DPU bitclk 可板级调参** + 如 `k3_com260.dts` 中: + + ```dts + spacemit-dpu-bitclk = <700000000>; + ``` + +5. **Writeback 路径在 K3 DTS 中被建模** + `lcd_dsi_panel.dtsi` 中有 `wb0`,并且 `dpu_crtc0` / `dpu_crtc1` 都连接到 WB 输入。 + +6. **Panel 驱动场景更真实** + 例如 `force-attached` 使用: + - `lcd_icnl9911c_mipi` + - `lcd_gc9503v_mipi` + - `lcd_tc358762xbg_dpi_800x480` + +7. **DP/eDP 配置里还带背光、电源 GPIO、使能 GPIO** + 例如: + - `backlight = <&backlight>;` + - `gpios-power = <101>;` + - `gpios-enable = <118>;` + +## 配置介绍 + +主要包括: + +- DRM / Display 驱动 CONFIG; +- DPU / DSI / DP(eDP) / panel / backlight 的 DTS 配置; +- 板级显示拓扑配置。 + +### CONFIG 配置 + +K3 显示常见内核配置包括: + +- `CONFIG_DRM` +- `CONFIG_DRM_SPACEMIT` +- `CONFIG_SPACEMIT_INNO_DP` +- `CONFIG_SPACEMIT_MIPI_PANEL` +- `CONFIG_BACKLIGHT_CLASS_DEVICE` +- `CONFIG_DRM_PANEL` +- `CONFIG_DRM_MIPI_DSI` +- `CONFIG_DRM_DISPLAY_HELPER` +- `CONFIG_DRM_DISPLAY_DP_HELPER` +- `CONFIG_DRM_DISPLAY_DP_AUX_BUS` + +菜单路径大致如下: + +```text +Device Drivers + Graphics support + Direct Rendering Manager + DRM Support for Spacemit (DRM_SPACEMIT) + Spacemit specific extensions for inno dp/edp (SPACEMIT_INNO_DP) + mipi panel support for SPACEMIT SOCs platform (SPACEMIT_MIPI_PANEL) +``` + +从 `drivers/gpu/drm/spacemit/Kconfig` 可以确认: + +- `DRM_SPACEMIT` 选择了 `DRM_GEM_DMA_HELPER`、`DRM_FBDEV_EMULATION`、`DRM_MIPI_DSI`、`BACKLIGHT_CLASS_DEVICE`; +- `SPACEMIT_INNO_DP` 用于 DP / eDP; +- `SPACEMIT_MIPI_PANEL` 用于 MIPI 屏; +- 还提供了 `DRM_RASPBERRYPI_TOUCHSCREEN` 以支持树莓派 7 寸屏。 + +## DTS 配置 + +K3 的显示 DTS 组织建议按“**子系统 -> DPU -> 输出控制器 -> 外设/面板/背光**”理解。 + +### 1. DSI 显示路径 + +参考 `lcd_dsi_panel.dtsi`,K3 的 DSI 路径包含: + +- `display-subsystem-dsi` +- `dpu_crtc0` +- `dpu_crtc1` +- `dsi0` +- `dphy0` +- `wb0` + +#### DPU CRTC 节点 + +例如: + +```dts +dpu_crtc0: dpu_crtc0 { + compatible = "spacemit,dpu-saturn"; + clocks = <&syscon_apmu CLK_APMU_LCD_PXCLK>, + <&syscon_apmu CLK_APMU_LCD_MCLK>, + <&syscon_apmu CLK_APMU_LCD_HCLK>, + <&syscon_apmu CLK_APMU_DSI_ESC>, + <&syscon_apmu CLK_APMU_DPU_ACLK>, + <&syscon_apmu CLK_APMU_LCD_DSC>; + power-domains = <&power K3_PMU_LCD0_PWR_DOMAIN>; + pipeline-id = <1>; + is_edp = <0>; + dpu-id = <0>; + spacemit-dpu-min-mclk = <40960000>; + spacemit-dpu-dsipll; + status = "disabled"; +}; +``` + +几个关键点: + +- `compatible = "spacemit,dpu-saturn"`:K3 DPU 使用 Saturn 系列实现; +- `power-domains`:显示电源域必须匹配; +- `spacemit-dpu-min-mclk`:最小时钟门限; +- `spacemit-dpu-dsipll`:DSI 场景下启用 DSI PLL 相关逻辑; +- 板级还可能附加 `spacemit-dpu-bitclk`。 + +#### DSI Host 节点 + +```dts +dsi0: dsi0@d421a800 { + compatible = "spacemit,dsi-host"; + reg = <0 0xd421a800 0 0x200>; + interrupts = <79 IRQ_TYPE_LEVEL_HIGH>; + ip = "synopsys-dhost"; + dev-id = <2>; + status = "disabled"; +}; +``` + +#### DPHY 节点 + +```dts +dphy0: dphy0@d421a800 { + compatible = "spacemit,dsi-phy"; + reg = <0 0xd421a800 0 0x200>; + ip = "spacemit-dphy"; + dev-id = <2>; + status = "ok"; +}; +``` + +#### graph 连接关系 + +DSI 方案不是简单 parent-child,而是通过 `ports/endpoint` 连接: + +```dts +DPU -> DSI Host -> DPHY -> Panel +``` + +例如: + +- `dpu_crtc0_out0 -> dsi0_in` +- `dsi0_out -> dphy0_in` +- 板级 `panel_in -> dsi1_output` + +这是 K3 显示 DTS 的一个关键读法。 + +### 2. MIPI 面板方案 + +EVB / Gemini / FPGA 这类板级通常在 `&dsi0` 下挂 MIPI panel: + +```dts +&dsi0 { + status = "disabled"; + + panel0: panel0@0 { + status = "ok"; + compatible = "spacemit,mipi-panel"; + reg = <0>; + reset-gpios = <&gpio 1 31 GPIO_ACTIVE_HIGH>; + bl-gpios = <&gpio 1 29 GPIO_ACTIVE_HIGH>; + dc0-gpios = <&gpio 1 24 GPIO_ACTIVE_HIGH>; + dc1-gpios = <&gpio 1 25 GPIO_ACTIVE_HIGH>; + id = <2>; + force-attached = "lcd_icnl9911c_mipi"; + }; +}; +``` + +这说明 K3 MIPI panel 实际会依赖: + +- `reset-gpios` +- `bl-gpios` +- `dc0-gpios` +- `dc1-gpios` +- `force-attached` + +而且不同板子实际挂的 panel 型号不同,例如: + +- `lcd_icnl9911c_mipi` +- `lcd_gc9503v_mipi` + +不能把 K3 面板写成“只有一个通用面板”。 + +### 3. Raspberry Pi 7-inch 触摸屏方案 + +在 `k3_com260.dts` / `k3_com260_kit_v02.dts` 中,DSI 显示路径不是传统 MIPI panel,而是树莓派 7 寸屏方案: + +```dts +&dsi0 { + status = "okay"; + + ports { + port@0 { + dsi1_output: endpoint@1 { + remote-endpoint = <&panel_in>; + }; + }; + }; + + panel0: panel0@0 { + status = "ok"; + compatible = "raspberrypi,dpi-panel"; + force-attached = "lcd_tc358762xbg_dpi_800x480"; + }; +}; +``` + +同时在 I2C 上还能看到: + +```dts +raspits-panel@45 { + compatible = "raspberrypi,7inch-touchscreen-panel"; + reg = <0x45>; + + port { + panel_in: endpoint { + remote-endpoint = <&dsi1_output>; + }; + }; +}; +``` + +这说明 K3 的真实显示场景已经覆盖: + +- DSI 输出; +- 外部桥接 / DPI 屏; +- I2C 控制的触摸屏面板; +- 触摸 IC(如 `raspits_ft5426`)配合工作。 + +这是 K3 相对 K1 非常值得补充的硬件事实。 + +### 4. eDP 路径 + +参考 `k3-edp0.dtsi`: + +```dts +dpu0_crtc0: dpu0_crtc0 { + compatible = "spacemit,dpu-saturn"; + power-domains = <&power K3_PMU_LCD0_PWR_DOMAIN>; + is_edp = <1>; + dpu-id = <0>; + status = "disabled"; + + ports { + port@0 { + dpu0_crtc0_out0: endpoint@0 { + remote-endpoint = <&edp0_in>; + }; + }; + }; +}; + +edp0: edp0@cac84000 { + compatible = "spacemit,inno-edp0"; + reg = <0x0 0xcac84000 0x0 0x4000>; + clocks = <&syscon_apmu CLK_APMU_EDP0_PXCLK>; + resets = <&syscon_apmu RESET_APMU_EDP0>; + color_format = <1>; + ref_clock = <24000000>; + edp-id = <0>; + status = "disabled"; +}; +``` + +在板级 `k3_dc_board.dts` / `k3_deb1.dts` 中,实际启用方式为: + +```dts +&edp0 { + pinctrl-names = "default"; + pinctrl-0 = <&dp0_1_cfg>; + backlight = <&backlight>; + gpios-power = <101>; + gpios-enable = <118>; + status = "okay"; +}; +``` + +这说明 eDP 方案的板级 bring-up 还必须关心: + +- eDP pinctrl; +- 背光节点; +- power GPIO; +- enable GPIO。 + +### 5. DP 路径 + +K3 存在至少两路 DP 输出: + +- `dp0` +- `dp1` + +参考 `k3-dp0.dtsi` / `k3-dp1.dtsi`: + +```dts +dp0: dp0@cac84000 { + compatible = "spacemit,inno-dp0"; + ref_clock = <24000000>; + dp-id = <0>; + #sound-dai-cells = <0>; + status = "disabled"; +}; +``` + +```dts +dp1: dp1@cac88000 { + compatible = "spacemit,inno-dp1"; + ref_clock = <24000000>; + dp-id = <1>; + #sound-dai-cells = <0>; + status = "disabled"; +}; +``` + +板级常见启用方式: + +```dts +&dp0 { + pinctrl-names = "default"; + pinctrl-0 = <&dp0_2_cfg>; + status = "okay"; +}; + +&dp1 { + pinctrl-names = "default"; + pinctrl-0 = <&dp1_4_cfg>; + status = "okay"; +}; +``` + +因此 K3 的 Display 文档里必须把 **dp0 / dp1 / edp0 的差异和板级连接方式** 单独说清楚。 + +### 6. 背光配置 + +K3 板级当前既能看到: + +- `pwm-backlight` +- `spacemit,backlight` + +例如 `k3_deb1.dts` / `k3_dc_board.dts` 中用了 `pwm-backlight`: + +```dts +backlight: backlight { + compatible = "pwm-backlight"; + ... +}; +``` + +同时驱动树里又存在: + +```c +compatible = "spacemit,backlight" +``` + +因此对用户来说,背光并不是只有一种做法,具体要看板级方案: + +- 若是 PWM 背光,优先看 `pwm-backlight`; +- 若是 SoC 自定义背光路径,则看 `spacemit-backlight`; +- 对 MIPI panel,还要关注面板驱动内部是否通过 DCS brightness 控制亮度。 + +### 7. pinctrl 配置 + +K3 `k3-pinctrl.dtsi` 中可以看到多组 DSI TE pin: + +- `dsi_0_cfg` +- `dsi_1_cfg` +- `dsi_2_cfg` +- `dsi_3_cfg` +- `dsi_4_cfg` +- `dsi_5_cfg` +- `dsi_6_cfg` + +例如: + +```dts +dsi_0_cfg: dsi-0-cfg { + dsi-0-pins { + pinmux = ; /* dsi te */ + }; +}; +``` + +这说明 K3 的 DSI TE 信号复用位置较多,板级迁移时不能只复制一个已有例子,必须对照原理图选择正确 pin group。 + +## 用户视角的几种典型显示拓扑 + +### 方案一:MIPI 屏(EVB / Gemini / FPGA) + +```text +DPU(CRTC0) -> DSI Host -> DPHY -> spacemit,mipi-panel +``` + +典型关心点: + +- `&dpu_crtc0` 是否启用; +- `&dsi0` 是否启用; +- panel 子节点是否存在; +- `force-attached` 是否匹配正确面板; +- reset/bl/dc GPIO 是否接对; +- `memory-region` 是否配置给 DPU。 + +### 方案二:DSI + Raspberry Pi 7-inch 屏(COM260) + +```text +DPU(CRTC0) -> DSI Host -> endpoint graph -> raspberrypi,7inch-touchscreen-panel + \-> raspberrypi,dpi-panel +``` + +典型关心点: + +- `ports` / `endpoint` 是否正确互连; +- I2C 面板地址是否正确; +- 触摸 IC 是否一并启用; +- `spacemit-dpu-bitclk` 是否按板级需求配置。 + +### 方案三:eDP 面板(DC board / DEB1) + +```text +DPU0 -> edp0 -> eDP panel + backlight + enable/power gpio +``` + +典型关心点: + +- `&dpu0_crtc0` 是否启用; +- `&edp0` pinctrl / backlight / GPIO 是否完整; +- 背光 PWM 是否打开; +- LCD0 power domain 是否正常。 + +### 方案四:DP 外接显示器 + +```text +DPU0/DPU1 -> dp0/dp1 -> DP connector -> monitor +``` + +典型关心点: + +- `dp0` / `dp1` 哪一路实际接出; +- pinctrl 组是否正确; +- 是否需要同时打开 DP 音频卡; +- 是否能读到 EDID 并完成模式协商。 + +## 使用与调试 + +### 1. 查看 DRM 设备 + +```bash +ls /dev/dri/ +``` + +一般会看到: + +- `card0` / `card1` ... +- `renderD128` 等节点。 + +### 2. 查看 connector / encoder / mode + +```bash +modetest -M spacemit -c +modetest -M spacemit -e +modetest -M spacemit -p +``` + +这些命令适合确认: + +- connector 是否 `connected`; +- 支持哪些 mode; +- encoder / CRTC / plane 的关联关系。 + +### 3. 点亮测试 + +例如: + +```bash +modetest -M spacemit -s @:1920x1080 +``` + +如果是 MIPI 屏或 eDP 屏,通常先用 `modetest -c` 找到正确 connector,再做模式设置。 + +### 4. 查看内核日志 + +```bash +dmesg | grep -Ei "drm|dpu|dsi|dp|edp|panel|backlight" +``` + +重点关注: + +- DPU probe 是否成功; +- DSI host / DPHY 是否初始化成功; +- panel prepare / enable 是否成功; +- DP/eDP 是否检测到连接 / HPD; +- 背光是否注册。 + +### 5. 查看 sysfs / DRM 状态 + +```bash +ls /sys/class/drm/ +``` + +可用于观察: + +- connector 状态; +- EDID; +- mode 列表; +- DP 链路状态等。 + +### 6. 常见板级排查顺序 + +如果屏不亮,建议按下面顺序查: + +1. **先看 DPU 是否启用** + `memory-region`、电源域、clock/reset 是否齐全; +2. **再看输出接口是否启用** + 是 `dsi0`、`dp0`、`dp1` 还是 `edp0`; +3. **再看 graph 拓扑是否连通** + `ports/endpoint/remote-endpoint` 是否一一对应; +4. **再看 panel / bridge / backlight** + GPIO、I2C、PWM、背光节点是否完整; +5. **最后看板级特定参数** + 如 `spacemit-dpu-bitclk`、`force-attached`、`gpios-power`、`gpios-enable`。 + +## FAQ + +### 1. 为什么 K3 的 Display 文档不能只参考 K1? + +因为 K3 的真实显示硬件拓扑比 K1 丰富得多。仅从 DTS 就能确认: + +- 有 DSI; +- 有 DP0 / DP1; +- 有 eDP0; +- 有双 DPU / 双显示域; +- 有 writeback; +- 有树莓派 7 寸屏方案; +- 有板级 bitclk 调参; +- 有 DP 音频。 + +如果只按 K1 写,会遗漏 K3 真正在用的硬件功能。 + +### 2. 为什么同样是 K3,有的板子走 DSI,有的走 eDP / DP? + +因为不同板级产品的显示器件和连接方式不同。K3 SoC 本身支持多种显示输出链路,具体启用哪一条由板级 DTS 决定。 + +### 3. `force-attached` 是干什么的? + +它用于强制指定当前 panel 驱动所绑定的面板型号/配置,在没有标准自动识别机制时尤其常见。不同板子可能配置不同的 `force-attached` 字符串,不能混用。 + +### 4. 为什么有的板级要加 `spacemit-dpu-bitclk`? + +这是板级对 DPU bit clock 的额外调参,用来适配某些实际屏幕或桥接方案。不是所有板子都需要,但在 COM260 这类方案里已经实际使用。 + +### 5. eDP/DP 屏不亮优先查什么? + +优先查: + +- `dpu0_crtc0` / `dpu1_crtc0` 是否启用; +- `edp0` / `dp0` / `dp1` 是否启用; +- pinctrl 是否正确; +- `backlight` / `gpios-power` / `gpios-enable` 是否完整; +- `dmesg` 里是否有 DP/eDP link 训练或 HPD 相关日志。 From 07d688f7c4c06945aa6a544f6e39aceb88550ad6 Mon Sep 17 00:00:00 2001 From: Troy Date: Wed, 18 Mar 2026 15:12:55 +0800 Subject: [PATCH 09/30] k3: device: peripheral_driver: Audio: initial version --- .../device/peripheral_driver/17-Audio.md | 585 ++++++++++++++++++ 1 file changed, 585 insertions(+) diff --git a/zh/k3_buildroot/device/peripheral_driver/17-Audio.md b/zh/k3_buildroot/device/peripheral_driver/17-Audio.md index 14906d3..d0c41d0 100644 --- a/zh/k3_buildroot/device/peripheral_driver/17-Audio.md +++ b/zh/k3_buildroot/device/peripheral_driver/17-Audio.md @@ -1,2 +1,587 @@ # Audio +介绍 K3 平台 Audio 子系统的功能、内核配置方法、设备树配置方法以及调试方式。 + +## 模块介绍 + +K3 的音频子系统已经不适合简单按 K1 的“2 路 I2S + 1 路 HDMI 音频”来描述。根据 K3 SDK 驱动、`k3.dtsi` / `k3-rdomain.dtsi` / 各板级 DTS 的实际情况,K3 当前至少存在下面几类真实音频路径: + +- **A-domain I2S / SSPA 控制器**,用于板级外接 Codec; +- **R-domain RI2S 控制器**,主要用于 DP 音频链路; +- **DP 音频**,由 `simple-audio-card` 把 `ri2s2/ri2s3` 和 `dp0/dp1` 连接成声卡; +- **I2S + 外部 ES8326 Codec 声卡**,支持播放与录制; +- **DMIC 相关板级供电与 pinctrl**,在部分板级 DTS 中可见; +- **多板型差异**:不同板子启用的是 `sound_card_dp0`、`sound_card_dp1` 或 `sound_card1`。 + +也就是说,K3 的 Audio 文档必须按 **真实板级方案** 来写,而不能只按 K1 老的 HDMI/I2S 模型写。 + +### 功能介绍 + +K3 基于 Linux **ALSA + ASoC** 架构实现音频功能。整体可以分为以下几层: + +- **ALSA Library** + 提供用户空间音频 API; +- **ALSA Core** + 提供 PCM、Control、Mixer 等通用接口; +- **ASoC Core** + 把 SoC 音频驱动拆分为 CPU DAI / Codec / Machine; +- **Hardware Driver** + 包括 K3 I2S/RI2S 控制器、外部 Codec、板级声卡 Machine。 + +在 K3 上,用户实际看到的“声卡”并不一定是单独某个 I2S 控制器,而往往是由 `simple-audio-card` 把: + +- CPU DAI(如 `i2s1` / `ri2s2` / `ri2s3`) +- Codec DAI(如 `es8326` / `dp0` / `dp1`) + +组合成一个完整声卡节点。 + +### K3 的实际音频方案 + +从板级 DTS 可以确认,K3 当前至少有下面几类实际方案: + +#### 方案一:I2S + ES8326 Codec + +典型出现在: + +- `k3_dc_board.dts` +- `k3_deb1.dts` + +链路大致为: + +```text +I2S1 -> ES8326 Codec -> Speaker / Headphone / Mic +``` + +特点: + +- 支持播放和录制; +- 通过 I2C 挂载 ES8326; +- 需要 I2S pinctrl、Codec I2C 节点、`simple-audio-card` 共同配合。 + +#### 方案二:DP0 音频 + +典型出现在: + +- `k3_evb.dts` +- `k3_gemini_c0.dts` + +链路大致为: + +```text +RI2S2 -> DP0 -> DP 显示器音频输出 +``` + +特点: + +- `simple-audio-card,name = "snd-dp0"`; +- `format = "left_j"`; +- `playback-only`; +- 依赖 DP0 显示输出已启用。 + +#### 方案三:DP1 音频 + +典型出现在: + +- `k3_com260.dts` +- `k3_com260_kit_v02.dts` +- `k3_deb1.dts` + +链路大致为: + +```text +RI2S3 -> DP1 -> DP 显示器音频输出 +``` + +特点与 DP0 类似,但 CPU DAI 和 DP 端口不同。 + +## 源码结构介绍 + +K3 音频相关代码主要位于: + +```text +linux-6.18/sound/soc/spacemit/ +├── Kconfig +├── Makefile +├── k1_i2s.c # A-domain I2S/SSPA 控制器驱动 +└── k3_ri2s.c # R-domain RI2S 控制器驱动 +``` + +另外,板级声卡大多复用标准 ASoC 的 `simple-audio-card` 方式,不再像旧方案那样一定需要一套单独的私有 machine 驱动。 + +外部 Codec 例如 ES8326 仍由通用 Codec 驱动承担,位于: + +```text +sound/soc/codecs/ +├── es8326.c +└── es8326.h +``` + +### 驱动实现差异 + +K3 实际上同时存在两套音频控制器驱动: + +#### `k1_i2s.c` + +用于 A-domain I2S/SSPA 控制器,特点包括: + +- 支持 DMAengine PCM; +- 支持播放和录制; +- 驱动中可见 `SND_SOC_DAIFMT_I2S`、`DSP_A`、`DSP_B`; +- 采样格式包含 `S16_LE` / `S32_LE`; +- 运行参数中可见最大 2 声道; +- 用于典型的 SoC + 外部 Codec 方案。 + +#### `k3_ri2s.c` + +用于 R-domain RI2S 控制器,特点包括: + +- `CONFIG_SND_SOC_K3_RI2S`; +- 也走 DMAengine PCM; +- 支持 `I2S` / `LEFT_J` / `RIGHT_J` / `DSP_A` / `DSP_B`; +- 在常规 I2S / Left-justified / Right-justified 模式下约束为 2 声道; +- 在 DSP A/B 模式下可支持 1~4 声道; +- K3 板级目前主要把它用于 DP 音频。 + +这点是 K3 相对 K1 一个明显变化:**K3 把 DP 音频链路专门落到了 R-domain RI2S 上。** + +## 关键特性 + +### 特性 + +| 特性 | 特性说明 | +| :----- | :---- | +| 音频框架 | ALSA + ASoC | +| A-domain I2S | `k1_i2s.c`,用于外接 Codec | +| R-domain RI2S | `k3_ri2s.c`,用于 K3 R-domain 音频场景 | +| 声卡组织 | 主要通过 `simple-audio-card` 建模 | +| 外部 Codec | 支持 ES8326 | +| DP 音频 | 支持 `dp0` / `dp1` 音频输出 | +| 录音能力 | I2S + ES8326 方案支持录制 | +| DP 音频方向 | `playback-only` | +| 支持格式 | I2S / LEFT_J / RIGHT_J / DSP_A / DSP_B(取决于控制器) | +| 支持采样格式 | S16_LE / S32_LE | + +### K3 相比 K1 值得单独强调的点 + +1. **新增 RI2S 路径** + K3 `Kconfig` 中新增了: + + - `SND_SOC_K3_RI2S` + + 对应 DTS 节点: + + - `ri2s0` + - `ri2s1` + - `ri2s2` + - `ri2s3` + +2. **DP 音频已成为真实板级方案** + 而且不是抽象支持,是多个板子都在用: + + - EVB / Gemini:`sound_card_dp0` + - COM260 / DEB1:`sound_card_dp1` + +3. **声卡建模以 simple-audio-card 为主** + K3 当前板级 DTS 里,很多声卡都是标准 `simple-audio-card` 节点,不需要引入单独私有 machine 驱动说明。 + +4. **板级差异更明显** + 不同板子选用不同: + + - CPU DAI:`i2s1` / `ri2s2` / `ri2s3` + - Codec DAI:`es8326` / `dp0` / `dp1` + - pinctrl:`sspa1_1_cfg` / 各种 DP HPD pinctrl + +## 配置介绍 + +主要包括: + +- ALSA / ASoC 基础支持; +- K3 I2S / RI2S 控制器支持; +- 板级 Codec / simple-audio-card DTS 配置; +- DP 音频依赖的显示侧配置。 + +### CONFIG 配置 + +#### 基础音频功能支持 + +```text +Device Drivers + Sound card support (SOUND [=y]) + Advanced Linux Sound Architecture (SND [=y]) + ALSA for SoC audio support (SND_SOC [=y]) +``` + +#### K3 音频控制器支持 + +`drivers/sound/soc/spacemit/Kconfig` 中可确认: + +- `CONFIG_SND_SOC_K1_I2S` +- `CONFIG_SND_SOC_K3_RI2S` + +菜单大致如下: + +```text +Device Drivers + Sound card support + Advanced Linux Sound Architecture + ALSA for SoC audio support + SpacemiT + K1 I2S Device Driver (SND_SOC_K1_I2S) + K3 RI2S Device Driver (SND_SOC_K3_RI2S) +``` + +其中: + +- `SND_SOC_K1_I2S`:用于 K3 A-domain I2S 控制器; +- `SND_SOC_K3_RI2S`:用于 K3 R-domain RI2S 控制器。 + +#### 外部 Codec 支持 + +如果板级使用 ES8326,需要打开: + +```text +Device Drivers + Sound card support + Advanced Linux Sound Architecture + ALSA for SoC audio support + CODEC drivers + Everest Semi ES8326 CODEC (SND_SOC_ES8326) +``` + +## DTS 配置 + +### 1. A-domain I2S 控制器 + +K3 的板级 I2S 方案中,常用的是 `i2s1`。例如在 `k3_dc_board.dts` / `k3_deb1.dts` 中: + +```dts +&i2s1 { + status = "okay"; + pinctrl-0 = <&sspa1_1_cfg>; + pinctrl-names = "default"; +}; +``` + +这说明用户至少需要配置: + +- 控制器节点 `status = "okay"`; +- 选对 pinctrl; +- 板级再通过 `simple-audio-card` 把它和 Codec 连起来。 + +### 2. I2S pinctrl 配置 + +K3 `k3-pinctrl.dtsi` 中定义了多组 `sspa` 引脚,例如: + +- `sspa0_0_cfg` +- `sspa0_1_cfg` +- `sspa1_0_cfg` +- `sspa1_1_cfg` +- `sspa2_0_cfg` +- `sspa3_0_cfg` +- `sspa4_0_cfg` +- `sspa5_0_cfg` + +例如: + +```dts +sspa1_1_cfg: sspa1-1-cfg { + sspa1-0-pins { + pinmux = , /* sspa1 clk */ + , /* sspa1 frm */ + , /* sspa1 tx */ + , /* sspa1 rx */ + ; /* sspa1 sysclk */ + }; +}; +``` + +和前面的 Display / PCIe 一样,K3 的 pinmux 方案很多,迁移 DTS 时必须按原理图选正确那一组,不能机械复用别的板子的配置。 + +### 3. ES8326 Codec 配置 + +以 `k3_dc_board.dts` 为例: + +```dts +es8326: es8326@19 { + compatible = "everest,es8326"; + reg = <0x19>; + #sound-dai-cells = <0>; + interrupt-parent = <&gpio>; + interrupts = <3 31 IRQ_TYPE_EDGE_RISING>; + spk-ctl-gpio = <&gpio 3 23 0>; + everest,mic1-src = [44]; + everest,mic2-src = [66]; + everest,jack-detect-inverted; + status = "okay"; +}; +``` + +这说明板级 Codec 还依赖: + +- I2C 总线; +- 耳机插拔检测中断; +- 扬声器 GPIO; +- 麦克风源配置; +- jack detect 极性配置。 + +在 `k3_deb1.dts` 中,`spk-ctl-gpio` 被注释掉了,这也说明 **不同板型的板级音频外围并不完全相同**。 + +### 4. I2S + Codec 声卡配置 + +K3 典型 I2S + ES8326 声卡配置如下: + +```dts +&sound_card1 { + status = "okay"; + simple-audio-card,mclk-fs = <256>; + simple-audio-card,dai-link@0 { + format = "i2s"; + frame-master = <&link1_cpu>; + bitclock-master = <&link1_cpu>; + + link1_cpu: cpu { + sound-dai = <&i2s1>; + }; + + link1_codec: codec { + sound-dai = <&es8326>; + }; + }; +}; +``` + +几个关键点: + +- `format = "i2s"`; +- `mclk-fs = <256>`; +- CPU 端用 `i2s1`; +- Codec 端用 `es8326`。 + +这种方案对应的是完整本地音频声卡,用户可用于: + +- 扬声器播放; +- 耳机输出; +- 麦克风录音。 + +### 5. R-domain RI2S 控制器 + +K3 `k3-rdomain.dtsi` 中定义了: + +- `ri2s0` +- `ri2s1` +- `ri2s2` +- `ri2s3` + +例如: + +```dts +ri2s2: ri2s2@c0883900 { + compatible = "spacemit,k3-ri2s"; + #sound-dai-cells = <0>; + status = "disabled"; +}; +``` + +这类节点在当前板级主要用于 DP 音频,不是传统外接模拟 Codec 方案。 + +### 6. DP0 音频声卡配置 + +K3 EVB / Gemini 上典型配置如下: + +```dts +&ri2s2 { + status = "okay"; +}; + +&sound_card_dp0 { + status = "okay"; + simple-audio-card,name = "snd-dp0"; + simple-audio-card,mclk-fs = <512>; + simple-audio-card,dai-link@0 { + format = "left_j"; + frame-master = <&dp0_link_cpu>; + bitclock-master = <&dp0_link_cpu>; + dp0_link_codec: codec { + playback-only; + sound-dai = <&dp0>; + }; + }; +}; +``` + +几个关键点: + +- CPU 端是 `ri2s2`; +- Codec 端并不是传统音频 Codec,而是 `dp0`; +- `playback-only` 明确表示只支持播放; +- `format = "left_j"`。 + +### 7. DP1 音频声卡配置 + +K3 COM260 / DEB1 上常见: + +```dts +&ri2s3 { + status = "okay"; +}; + +&sound_card_dp1 { + status = "okay"; + simple-audio-card,name = "snd-dp1"; + simple-audio-card,mclk-fs = <512>; + simple-audio-card,dai-link@0 { + format = "left_j"; + frame-master = <&dp1_link_cpu>; + bitclock-master = <&dp1_link_cpu>; + dp1_link_codec: codec { + playback-only; + sound-dai = <&dp1>; + }; + }; +}; +``` + +与 DP0 类似,只是切换到了: + +- `ri2s3` +- `dp1` +- `snd-dp1` + +### 8. DP 音频依赖关系 + +DP 音频并不是单独一块能工作的功能,它依赖显示链路本身已正常起来。因此要启用 DP 音频,通常还要保证: + +- `dp0` / `dp1` 显示输出已启用; +- 对应 HPD pinctrl 正确; +- 外接 DP 显示器已正常枚举; +- DRM / DP 显示侧已经工作正常。 + +也就是说,K3 上 **Display 和 Audio 在 DP 场景里是联动的**。 + +## 接口与使用方法 + +### 1. 查看声卡 + +```bash +cat /proc/asound/cards +aplay -l +arecord -l +``` + +通过这些命令可以确认: + +- 系统枚举出了哪些声卡; +- 是 `snd-es8326`、`snd-dp0` 还是 `snd-dp1`; +- 哪些设备支持播放,哪些支持录制。 + +### 2. 播放测试 + +对于 I2S + ES8326 或 DP 音频,可用: + +```bash +aplay -D hw:0,0 test.wav +``` + +或者: + +```bash +speaker-test -D hw:0,0 -c 2 -r 48000 -F S16_LE -t sine +``` + +### 3. 录音测试 + +若板级为 ES8326 方案,可测试: + +```bash +arecord -D hw:0,0 -f S16_LE -r 48000 -c 2 -d 5 test.wav +``` + +如果是 DP 音频方案,一般不支持录音,因为 DTS 已标明 `playback-only`。 + +### 4. Mixer / 控件查看 + +```bash +amixer controls +amixer contents +alsamixer +``` + +适合查看: + +- Codec 控件是否存在; +- 耳机/扬声器通路是否打开; +- Capture 开关是否打开。 + +## Debug 介绍 + +### 1. 先确认声卡是否注册 + +```bash +dmesg | grep -Ei "asoc|snd|i2s|ri2s|es8326|dp0|dp1" +cat /proc/asound/cards +``` + +重点看: + +- ASoC card 是否注册成功; +- CPU DAI / Codec DAI 是否绑定成功; +- 是否出现 deferred probe 或 clock/reset 相关错误。 + +### 2. I2S + ES8326 不出声时怎么查 + +优先顺序: + +1. `i2s1` 是否启用; +2. `sspa1_1_cfg` 是否正确; +3. `es8326@19` 是否探测成功; +4. `sound_card1` 是否注册成功; +5. `amixer` 里通路是否打开; +6. 耳机插拔中断和 `spk-ctl-gpio` 是否正常。 + +### 3. DP 音频不出声时怎么查 + +优先顺序: + +1. 先确认 DP 显示本身已经正常; +2. 再确认 `ri2s2/ri2s3` 是否启用; +3. 再确认 `sound_card_dp0` / `sound_card_dp1` 是否注册成功; +4. 检查 `aplay -l` 是否能看到 `snd-dp0` / `snd-dp1`; +5. 检查外接显示器是否真的支持音频。 + +### 4. pinctrl 迁移问题 + +K3 的 `sspa` / `dp hpd` pin 组很多,迁移 DTS 时非常容易把 pinctrl 用错。若出现: + +- Codec 正常探测但无 BCLK/LRCLK; +- DP 显示正常但音频链路不稳定; + +建议回到: + +- `k3-pinctrl.dtsi` +- 板级原理图 +- 实际波形 + +逐项核对。 + +## FAQ + +### 1. K3 的 Audio 为什么不能只沿用 K1 文档? + +因为 K3 实际已经多了: + +- R-domain `ri2s0~ri2s3`; +- DP0 / DP1 音频声卡; +- 多块板子真实使用 `simple-audio-card` + DP codec 模型; +- 不同板型间音频链路差异明显。 + +### 2. K3 的 DP 音频为什么写在 Audio 文档里,而不是只写在 Display? + +因为从 ASoC 角度看,DP0/DP1 在板级 DTS 里已经被当作 `sound-dai` 参与声卡建模,用户最终也是通过 ALSA 设备来播放音频,所以它属于 Display 与 Audio 的交叉功能,但必须在 Audio 文档中专门说明。 + +### 3. 为什么 `sound_card_dp0` / `sound_card_dp1` 里用的是 `left_j`? + +因为这是当前 K3 板级 DTS 的真实配置。文档应以实际 DTS 为准,而不是按通用习惯默认写成 `i2s`。 + +### 4. 为什么 DP 音频通常没有录音? + +因为板级 DTS 里对 DP codec 侧已经使用了 `playback-only`,说明当前设计只用于向外部显示器输出音频,不用于采集。 From 078ec726de29a2ec9d5cc073fc621b1aea2439a2 Mon Sep 17 00:00:00 2001 From: Troy Date: Wed, 18 Mar 2026 15:18:50 +0800 Subject: [PATCH 10/30] k3: device: usb: general guide: initial version --- .../10-USB/1-USB-General-Developer-Guide.md | 578 ++++++++++++++++++ 1 file changed, 578 insertions(+) diff --git a/zh/k3_buildroot/device/peripheral_driver/10-USB/1-USB-General-Developer-Guide.md b/zh/k3_buildroot/device/peripheral_driver/10-USB/1-USB-General-Developer-Guide.md index 2f40e45..78222b0 100644 --- a/zh/k3_buildroot/device/peripheral_driver/10-USB/1-USB-General-Developer-Guide.md +++ b/zh/k3_buildroot/device/peripheral_driver/10-USB/1-USB-General-Developer-Guide.md @@ -2,3 +2,581 @@ sidebar_position: 1 # USB 通用开发指南 +介绍 K3 USB 的基本功能、驱动组成、设备树配置方法以及常见调试方式。 + +适用范围:SpacemiT Linux 6.18(K3 SDK) + +## 模块介绍 + +K3 的 USB 子系统比 K1 明显更复杂,不能只按 K1 的“三个控制器”来理解。根据 `k3.dtsi`、各板级 DTS 和实际驱动,可以确认 K3 当前至少包含: + +- **1 路 USB2.0 Host**:`usb2_host` +- **4 路 USB3.x 控制器端口**:`usb3_porta`、`usb3_portb`、`usb3_portc`、`usb3_portd` +- **1 路可切换 DRD/OTG 端口**:通常是 `usb3_porta` +- **多路 Host 端口**:通常是 `usb3_portb/c/d` +- **独立 USB2 PHY**:`spacemit,k3-usb2-phy` +- **独立 USB3 PHY**:`spacemit,k3-usb3-phy` +- **Type-C / role switch / orientation switch 相关逻辑**: + - `usb-role-switch` + - `usb-c-connector` + - `onsemi,fusb301` + - `spacemit,k3-typec-switch` + +也就是说,K3 上的 USB 不是简单“一个 USB3 控制器”,而是一个由 **DWC3 控制器 + USB2 PHY + USB3 PHY + Type-C 检测/切换 + 板级 GPIO/pinctrl** 组成的完整系统。 + +### K3 实际控制器分布 + +从 `k3.dtsi` 可以确认: + +- `usb2_host`:固定 Host,`maximum-speed = "high-speed"` +- `usb3_porta`:默认作为 DRD/OTG 使用的主要端口 +- `usb3_portb`:固定 Host +- `usb3_portc`:固定 Host +- `usb3_portd`:固定 Host + +其中控制器节点当前仍使用: + +```dts +compatible = "spacemit,k1-dwc3"; +``` + +这说明 **K3 继续复用了 K1 的 DWC3 glue compatible**,但 PHY、Type-C 和板级连接方式已经是 K3 自己的实现。 + +### 功能介绍 + +Linux 中,USB 主要支持以下三种角色: + +- **Host**:外接 U 盘、键盘、鼠标、摄像头、网卡等 USB 设备; +- **Device / Peripheral**:开发板作为 U 盘、网卡、ADB、UVC 摄像头等 USB 外设接入上位机; +- **OTG / DRD**:可在 Host 和 Device 之间切换。 + +K3 实际上覆盖了这三种场景: + +- `usb2_host`、`usb3_portb/c/d` 主要是 Host; +- `usb3_porta` 可配置为 `host` / `peripheral` / `otg`; +- 板级若加上 Type-C CC 控制器(如 FUSB301),就能做真实的 Type-C DRD 角色切换。 + +### 功能框架说明 + +#### USB Host + +USB Host 角色驱动框架可以分为: + +- **Host Controller Driver**:如 DWC3 Host / xHCI; +- **USB Core**:负责设备枚举、URB、配置描述符处理; +- **Class Driver**:如 Storage、HID、UVC、CDC ECM/NCM 等。 + +#### USB Device + +USB Device 角色驱动框架可以分为: + +- **UDC Driver**:DWC3 gadget / UDC; +- **UDC Core**; +- **Composite / ConfigFS**; +- **Function Driver**:如 mass-storage、RNDIS、NCM、UVC、ADB 等。 + +#### Type-C / Role Switch + +K3 的 Type-C 方案中,还会多出一层: + +- **Type-C Port Controller(如 FUSB301)**; +- **usb-role-switch**; +- **orientation / SuperSpeed switch**; +- **USB-C connector graph**。 + +这层是 K3 相比 K1 很值得单独写清楚的点。 + +## 源码结构介绍 + +K3 USB 相关代码主要位于: + +```text +linux-6.18/drivers/usb/ +|-- dwc3/ +| |-- core.c +| |-- drd.c +| |-- gadget.c +| |-- host.c +| `-- dwc3-generic-plat.c +|-- host/ +| `-- xhci-plat.c +`-- typec/ + |-- fusb301.c + |-- class.c + |-- mux/ + `-- altmodes/ +``` + +结合 DTS 可见,K3 当前主要依赖的是: + +- **DWC3 Core**:Host / Gadget / DRD 主逻辑 +- **平台 DWC3 glue compatible**:`spacemit,k1-dwc3` +- **Type-C Port Controller**:`fusb301.c` +- **Type-C switch / orientation 处理**:与 `spacemit,k3-typec-switch` 配合 + +K3 的 USB PHY 节点由 DTS 中的专用 compatible 描述: + +- `spacemit,k3-usb2-phy` +- `spacemit,k3-usb3-phy` +- `spacemit,k3-typec-switch`(用于部分 DRD/Type-C 场景) + +## 关键特性 + +### 特性 + +| 特性 | 特性说明 | +| :----- | :---- | +| 控制器数量 | `usb2_host` + `usb3_porta/b/c/d` | +| 角色支持 | Host / Device / OTG | +| USB2 Host | `usb2_host` 固定 Host | +| DRD 端口 | `usb3_porta` 可做 OTG / Device / Host | +| 固定 Host 端口 | `usb3_portb/c/d` | +| PHY 组织 | USB2 PHY 与 USB3 PHY 分离 | +| Type-C 支持 | `usb-role-switch` + `fusb301` + `usb-c-connector` | +| SuperSpeed 方向切换 | 部分板级通过 `spacemit,k3-typec-switch` + graph 端点连接 | +| 板级约束 | 大量依赖 `pinctrl` / `monitor-vbus` / role switch graph | + +### K3 相比 K1 值得特别说明的点 + +1. **USB 端口数明显更多** + K3 不是 K1 那种“USB0/USB1/USB2-3”固定描述,而是明确分成: + - `usb2_host` + - `usb3_porta` + - `usb3_portb` + - `usb3_portc` + - `usb3_portd` + +2. **PHY 拆分更清晰** + 每个 USB3 端口通常都包含: + - 一个 `usb2-phy` + - 一个 `usb3-phy` + +3. **Type-C 建模更完整** + 板级 DTS 中已经存在完整的: + - `tcpc@25` + - `usb-c-connector` + - `ports/endpoint` + - `usb-role-switch` + +4. **SuperSpeed 切换图连接真实存在** + 在 `k3_com260.dts` / `k3_com260_kit_v02.dts` 中,`fusb301` 甚至通过额外 endpoint 与 `usb3_porta_u3phy` 的 `usb3_phy_switch` 相连,说明板级还处理了 Type-C 插头方向与 SS lane 切换。 + +5. **板级大量使用 maximum-speed 限速** + 多个板子把部分 USB3 端口限制为: + + ```dts + maximum-speed = "high-speed"; + ``` + + 这说明实际产品并不一定把所有端口都做成 SuperSpeed,文档必须按板级事实说明。 + +## 配置介绍 + +主要包括: + +- DWC3 / xHCI / Gadget / Type-C 内核配置; +- PHY 节点与控制器节点配置; +- Type-C role switch 图连接; +- 板级 Host / OTG / Device 具体使用方式。 + +### CONFIG 配置 + +K3 USB 常见配置包括: + +- `CONFIG_USB` +- `CONFIG_USB_XHCI_HCD` +- `CONFIG_USB_XHCI_PLATFORM` +- `CONFIG_USB_DWC3` +- `CONFIG_USB_DWC3_HOST` +- `CONFIG_USB_DWC3_GADGET` +- `CONFIG_USB_DWC3_DUAL_ROLE` +- `CONFIG_USB_ROLE_SWITCH` +- `CONFIG_TYPEC` +- `CONFIG_TYPEC_FUSB301` +- `CONFIG_USB_GADGET` +- `CONFIG_USB_CONFIGFS` + +如果板子需要 USB Gadget 功能,还需要打开对应的 configfs functions,例如: + +- `CONFIG_USB_CONFIGFS_ACM` +- `CONFIG_USB_CONFIGFS_RNDIS` +- `CONFIG_USB_CONFIGFS_NCM` +- `CONFIG_USB_CONFIGFS_MASS_STORAGE` +- `CONFIG_USB_CONFIGFS_UVC` + +## DTS 配置 + +K3 USB 建议按下面几个层次理解: + +1. **USB PHY 节点** +2. **DWC3 控制器节点** +3. **role switch / Type-C graph** +4. **板级 Hub / 外设 / pinctrl / power** + +### 1. USB2 Host 节点 + +K3 `usb2_host` 是固定 Host: + +```dts +usb2_host_u2phy: phy@c0a20000 { + compatible = "spacemit,k3-usb2-phy"; + reg = <0x0 0xc0a20000 0x0 0x200>; + #phy-cells = <0>; + status = "disabled"; +}; + +usb2_host: usb2@c0a00000 { + compatible = "spacemit,k1-dwc3"; + reg = <0x0 0xc0a00000 0x0 0x10000>; + phys = <&usb2_host_u2phy>; + phy-names = "usb2-phy"; + phy_type = "utmi"; + dr_mode = "host"; + maximum-speed = "high-speed"; + status = "disabled"; +}; +``` + +板级启用时通常很简单: + +```dts +&usb2_host_u2phy { + status = "okay"; +}; + +&usb2_host { + status = "okay"; +}; +``` + +### 2. USB3 DRD 端口 `usb3_porta` + +K3 的 `usb3_porta` 是最值得重点写的端口。其基础定义为: + +```dts +usb3_porta_u2phy: phy@cad20000 { + compatible = "spacemit,k3-usb2-phy"; + ... +}; + +usb3_porta_u3phy: phy@cad30000 { + compatible = "spacemit,k3-usb3-phy", "spacemit,k3-typec-switch"; + ... +}; + +usb3_porta: usb3@cad00000 { + compatible = "spacemit,k1-dwc3"; + phys = <&usb3_porta_u3phy>, <&usb3_porta_u2phy>; + phy-names = "usb3-phy", "usb2-phy"; + ... +}; +``` + +几个关键点: + +- `usb3_porta` 同时绑了 USB2/USB3 两个 PHY; +- `usb3_porta_u3phy` 额外带了 `spacemit,k3-typec-switch`; +- 这是板级做 DRD / Type-C 的核心端口。 + +### 3. 固定 Host 端口 `usb3_portb/c/d` + +例如: + +```dts +usb3_portb: usb3@81400000 { + compatible = "spacemit,k1-dwc3"; + phys = <&usb3_portb_u2phy>, <&usb3_portb_u3phy>; + phy-names = "usb2-phy", "usb3-phy"; + dr_mode = "host"; + status = "disabled"; +}; +``` + +`usb3_portc` / `usb3_portd` 结构类似。注意: + +- 默认在 SoC dtsi 中就是 `dr_mode = "host"`; +- 某些板子会把它们限速为 `high-speed`; +- 某些板级还会在 Host 端口下面挂板载 HUB。 + +例如 `k3_com260.dts` 中: + +```dts +&usb3_portb { + status = "okay"; + #address-cells = <1>; + #size-cells = <0>; + + hub_2_0: hub@1 { + compatible = "usb2109,2817"; + reg = <1>; + }; +}; +``` + +这说明文档里不能只停留在“控制器使能”,还要考虑真实板上挂的 downstream HUB / device。 + +### 4. OTG / role switch 配置 + +以 `k3_dc_board.dts` 为例: + +```dts +&usb3_porta { + dr_mode = "otg"; + usb-role-switch; + role-switch-default-mode = "peripheral"; + monitor-vbus; + status = "okay"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0x0>; + porta_role_switch: endpoint { + remote-endpoint = <&fusb301_ep>; + }; + }; + }; +}; +``` + +这说明 DRD 不只是 `dr_mode = "otg"`,还需要: + +- `usb-role-switch` +- `role-switch-default-mode` +- `monitor-vbus` +- 和 Type-C 控制器的 graph 连接 + +### 5. Type-C / FUSB301 配置 + +例如: + +```dts +tcpc@25 { + compatible = "onsemi,fusb301"; + reg = <0x25>; + pinctrl-names = "default"; + pinctrl-0 = <&fusb301_cfg>; + irq-gpios = <&gpio 2 21 GPIO_ACTIVE_LOW>; + wakeup-source; + status = "okay"; + + typec_0: connector@0 { + compatible = "usb-c-connector"; + label = "USB-C"; + data-role = "dual"; + power-role = "dual"; + try-power-role = "sink"; + typec-power-opmode = "default"; + pd-disable; + + ports { + #address-cells = <0x1>; + #size-cells = <0x0>; + port@0 { + reg = <0x0>; + fusb301_ep: endpoint { + remote-endpoint = <&porta_role_switch>; + }; + }; + }; + }; +}; +``` + +几个关键点: + +- 当前板级使用 `FUSB301` 做 Type-C 角色检测; +- `data-role = "dual"` / `power-role = "dual"`; +- `pd-disable` 说明这里只做 Type-C/role,不做 USB PD 协议; +- 通过 endpoint 与 DWC3 role switch 相连。 + +### 6. SuperSpeed 方向切换 + +在 `k3_com260.dts` / `k3_com260_kit_v02.dts` 中还能看到: + +```dts +&usb3_porta_u3phy { + ports { + port@0 { + usb3_phy_switch: endpoint { + remote-endpoint = <&fusb301_sw_ep>; + }; + }; + }; +}; +``` + +同时在 FUSB301 connector 里还有: + +```dts +port@1 { + reg = <0x1>; + fusb301_sw_ep: endpoint { + remote-endpoint = <&usb3_phy_switch>; + }; +}; +``` + +这表示 K3 某些 Type-C 板级不只是做 USB2 role switch,还真正把 **Type-C 插头方向和 SuperSpeed PHY switch** 建模进了 DTS。 + +### 7. pinctrl 配置 + +K3 `k3-pinctrl.dtsi` 中有大量 USB 相关 pin group,例如: + +- `usb30_drd_0_cfg` +- `usb30_drd_1_cfg` +- `usb30_drd_2_cfg` +- `usb30_drd_int_0_cfg` +- `usb30_drd_int_1_cfg` +- `usb30_drd_dir_0_cfg` +- `usb30_drd_dir_1_cfg` +- `usb30_b_drv_0_cfg` +- `usb30_c_drv_0_cfg` +- `usb30_d_drv_0_cfg` +- `usb30h1_drv_0_cfg` +- `usb30h2_drv_0_cfg` + +比如: + +```dts +usb30_drd_dir_0_cfg: usb30_drd_dir-0-cfg { + usb30_drd_dir-pins { + pinmux = ; /* usb30 drd dir */ + }; +}; +``` + +USB bring-up 时,pinctrl 选择非常关键,尤其是: + +- DRD 的 ID / VBUSON / DIR / INT; +- Host 端口的 `drv` 控制信号; +- Type-C 中断脚。 + +## 常见板级场景 + +### 场景一:普通 Host 口 + +例如 `usb2_host` / `usb3_portb/c/d`: + +```text +DWC3(host) -> PHY -> USB-A / 板载 HUB / 外设 +``` + +用户最关心的是: + +- `status = "okay"` +- PHY 是否打开 +- 是否需要 `maximum-speed = "high-speed"` +- 是否有板载 HUB + +### 场景二:Type-C DRD 口 + +例如 `usb3_porta`: + +```text +DWC3(DRD) <-> usb-role-switch <-> FUSB301 <-> usb-c-connector + \-> usb3 PHY orientation switch +``` + +用户最关心的是: + +- `dr_mode = "otg"` +- `usb-role-switch` +- `monitor-vbus` +- FUSB301 I2C 节点 +- endpoint graph 是否正确 + +### 场景三:USB Gadget / 烧录 / 调试口 + +如果某板级把 `usb3_porta` 设成: + +```dts +dr_mode = "peripheral"; +maximum-speed = "high-speed"; +``` + +那么它就主要作为 USB Device / UDC 来用,可用于: + +- ADB +- gadget configfs +- U 盘 gadget +- 网卡 gadget +- UVC gadget + +## 调试方法 + +### 1. 查看控制器和总线枚举 + +```bash +dmesg | grep -Ei "usb|dwc3|xhci|typec|fusb" +lsusb +``` + +### 2. 查看 role switch / Type-C 状态 + +```bash +ls /sys/class/usb_role/ +ls /sys/class/typec/ +``` + +如果板级使用 role switch,可进一步查看: + +```bash +cat /sys/class/usb_role/*/role +``` + +### 3. Host 口测试 + +插入 U 盘后: + +```bash +dmesg | tail -n 50 +lsusb +lsblk +``` + +如果是 SuperSpeed,通常会在枚举日志中看到 `5000M` 或 USB 3.x 相关信息;如果被板级限速为 `high-speed`,则只会看到 USB2.0 速率。 + +### 4. Gadget 口测试 + +先确认 UDC: + +```bash +ls /sys/class/udc +``` + +然后使用 gadget 脚本或 configfs 配置功能。具体见下一篇《USB Gadget 开发指南》。 + +### 5. Type-C 相关排查 + +若 Type-C DRD 不工作,优先检查: + +- FUSB301 是否探测成功; +- `irq-gpios` 是否正确; +- `fusb301_ep <-> porta_role_switch` 是否连接正确; +- 若用到了 SS 方向切换,`fusb301_sw_ep <-> usb3_phy_switch` 是否连接正确; +- `usb3_porta_u3phy` 的 pinctrl 是否正确。 + +## FAQ + +### 1. 为什么 K3 USB 文档不能只照 K1 写? + +因为 K3 的硬件组织已经明显更复杂: + +- USB 端口更多; +- 每个端口的 USB2/USB3 PHY 分离; +- Type-C role switch / connector / orientation switch 已经真实进入 DTS; +- 不同板级对端口速度和角色限制差异很大。 + +### 2. 为什么 K3 的 DWC3 compatible 还是 `spacemit,k1-dwc3`? + +因为 K3 当前继续复用了 K1 的 DWC3 glue compatible;但 USB2 PHY、USB3 PHY、Type-C switch 和板级连接是 K3 的实际实现。 + +### 3. 为什么有些 USB3 端口在板级 DTS 里却写 `maximum-speed = "high-speed"`? + +因为实际产品可能只接出了 USB2 通道,或者板级方案为了兼容/成本/稳定性做了限速。文档必须以板级 DTS 和硬件为准,而不是看到“USB3 控制器”就默认写成 SuperSpeed。 + +### 4. Type-C 口只用了 FUSB301,为什么还写了 `pd-disable`? + +因为 FUSB301 在这些板级里主要承担 CC 检测和角色判断,不承担完整 USB PD 协议协商,所以 DTS 中明确写了 `pd-disable`。 From 6f084a97e29eb21e3d8f33340434aca836192654 Mon Sep 17 00:00:00 2001 From: Troy Date: Wed, 18 Mar 2026 15:26:09 +0800 Subject: [PATCH 11/30] k3: device: usb: gadget guide: initial version --- .../10-USB/2-USB-Gadget-Developer-Guide.md | 537 ++++++++++++++++++ 1 file changed, 537 insertions(+) diff --git a/zh/k3_buildroot/device/peripheral_driver/10-USB/2-USB-Gadget-Developer-Guide.md b/zh/k3_buildroot/device/peripheral_driver/10-USB/2-USB-Gadget-Developer-Guide.md index 12ebf4d..7554be3 100644 --- a/zh/k3_buildroot/device/peripheral_driver/10-USB/2-USB-Gadget-Developer-Guide.md +++ b/zh/k3_buildroot/device/peripheral_driver/10-USB/2-USB-Gadget-Developer-Guide.md @@ -2,3 +2,540 @@ sidebar_position: 2 # USB Gadget 开发指南 +适用范围:SpacemiT Linux 6.18,K3 平台。 + +本文重点介绍 K3 平台在 **USB Device / USB Gadget** 模式下的实际使用方式。写这篇时要特别注意:K3 不是“所有 USB 口都能随便做 gadget”,当前从 DTS 和 USB 拓扑看,**真正适合做 Device / OTG 的核心端口是 `usb3_porta`**,其余端口大多按固定 Host 使用。 + +## Linux USB Gadget API 框架 + +### 概述 + +USB Gadget 允许开发板作为一个 USB 外设接入上位机,例如: + +- U 盘 +- 网卡 +- 串口 +- ADB 设备 +- UVC 摄像头 +- 自定义协议设备 + +K3 平台中,这条链路自底向上大致如下: + +- **USB Device Controller Driver(UDC)**:底层由 DWC3 Gadget/UDC 提供; +- **UDC Core**:向上抽象 USB Device 控制器; +- **Composite Layer**:组合多个 USB function; +- **Function Driver**:如 ACM、RNDIS、NCM、Mass Storage、UVC、HID、FunctionFS; +- **Configfs**:用户空间通过 configfs 组装 gadget; +- **Userspace**:如 ADB、MTP、UVC 应用、定制协议应用等。 + +K3 这里仍然是标准 Linux USB Gadget 思路,不需要引入私有框架;真正和 K1/K2/K3 差异更大的部分,主要是: + +- **哪个控制器能做 gadget**; +- **Type-C / OTG role switch 如何配合**; +- **板级 DTS 是否真的把该端口布成 device/otg**。 + +### K3 上的实际 Gadget 入口 + +从 `k3.dtsi` 与各板级 DTS 来看: + +- `usb2_host`:固定 Host,不适合按 gadget 文档去用; +- `usb3_portb/c/d`:大多固定 Host; +- `usb3_porta`:K3 上最核心的 **DRD / OTG / Device** 入口。 + +也就是说,K3 的 gadget 开发,实际应优先围绕: + +```dts +&usb3_porta { + dr_mode = "peripheral"; +}; +``` + +或者: + +```dts +&usb3_porta { + dr_mode = "otg"; + usb-role-switch; + role-switch-default-mode = "peripheral"; + monitor-vbus; +}; +``` + +这点要先写清楚,不然后面用户会误以为任意一个 USB3 口都能直接拿来做 gadget。 + +## K3 的 UDC / Gadget 基础 + +### 控制器与驱动基础 + +K3 当前 USB 控制器节点仍复用: + +```dts +compatible = "spacemit,k1-dwc3"; +``` + +但底层 Gadget 能力来自 Linux 标准 **DWC3 Gadget**: + +- `drivers/usb/dwc3/gadget.c` +- `drivers/usb/dwc3/drd.c` +- `drivers/usb/dwc3/core.c` + +在 `drivers/usb/dwc3/gadget.c` 中可以看到标准的 gadget ops,例如: + +- `dwc3_gadget_start` +- `dwc3_gadget_stop` +- `dwc3_gadget_set_speed` +- `usb_gadget_udc_reset()` + +因此从软件栈角度看,K3 gadget 并不是一套 SpacemiT 私有 UDC,而是 **基于 DWC3 的标准 Linux UDC**。 + +### 内核 menuconfig 配置 + +K3 Gadget 侧至少建议关注这些配置: + +```text +CONFIG_USB_GADGET +CONFIG_USB_DWC3 +CONFIG_USB_DWC3_GADGET +CONFIG_USB_DWC3_DUAL_ROLE +CONFIG_USB_CONFIGFS +CONFIG_USB_ROLE_SWITCH +``` + +常见 function 还包括: + +```text +CONFIG_USB_CONFIGFS_ACM +CONFIG_USB_CONFIGFS_NCM +CONFIG_USB_CONFIGFS_RNDIS +CONFIG_USB_CONFIGFS_MASS_STORAGE +CONFIG_USB_CONFIGFS_UVC +CONFIG_USB_CONFIGFS_HID +CONFIG_USB_F_FS +``` + +如果希望调试时多一点信息,也可以打开: + +```text +CONFIG_USB_GADGET_DEBUG=y +CONFIG_USB_GADGET_VERBOSE=y +CONFIG_USB_GADGET_DEBUG_FILES=y +CONFIG_USB_GADGET_DEBUG_FS=y +``` + +### DWC3 mode 选择 + +`drivers/usb/dwc3/Kconfig` 中 DWC3 的模式选择很明确: + +- `USB_DWC3_HOST` +- `USB_DWC3_GADGET` +- `USB_DWC3_DUAL_ROLE` + +如果 K3 板级要兼顾 Host 与 Gadget,建议选: + +```text +CONFIG_USB_DWC3_DUAL_ROLE=y +``` + +如果某产品就是固定做 device 口,也可以只选 gadget mode,但对 K3 来说多数开发阶段还是 **Dual Role 更实用**。 + +## K3 DTS 配置 + +K3 gadget 文档里最重要的不是 function 本身,而是 **先把板级 USB 角色配对**。 + +### 1. 固定 Peripheral 场景 + +`k3_fpga_1x1.dts` 提供了一个很典型的固定 device 配置: + +```dts +&usb3_porta { + status = "disabled"; + maximum-speed = "high-speed"; + dr_mode = "peripheral"; + /delete-property/ phy_type; + phy_type = "utmi_wide"; +}; +``` + +这里至少说明几点: + +1. `usb3_porta` 可以直接设成 `peripheral`; +2. 某些平台会把 gadget 口限制为 `high-speed`; +3. 某些平台会改 `phy_type`,例如设成 `utmi_wide`; +4. 这类板级一般更像“固定下载口 / 固定调试口”。 + +如果你的产品不需要 OTG/Type-C 动态切换,只需要一个稳定的 device 口,这种方式最简单。 + +### 2. OTG / Dual Role 场景 + +K3 的真实产品板更常见的是 `usb3_porta` 配成 OTG: + +```dts +&usb3_porta { + dr_mode = "otg"; + usb-role-switch; + role-switch-default-mode = "peripheral"; + monitor-vbus; + status = "okay"; +}; +``` + +这几个属性分别意味着: + +- `dr_mode = "otg"`:支持角色切换; +- `usb-role-switch`:启用 role switch 框架; +- `role-switch-default-mode = "peripheral"`:默认先按 device 看待; +- `monitor-vbus`:根据 VBUS 状态协助判定角色。 + +对于 K3 来说,这通常才是 Type-C DRD 口的标准写法。 + +### 3. 与 FUSB301 的 graph 连接 + +在 `k3_dc_board.dts`、`k3_deb1.dts`、`k3_com260.dts`、`k3_com260_kit_v02.dts` 里,可以看到 K3 用 `FUSB301` 做 Type-C 角色检测,并通过 graph endpoint 和 DWC3 role switch 连接。 + +例如: + +```dts +&usb3_porta { + dr_mode = "otg"; + usb-role-switch; + role-switch-default-mode = "peripheral"; + monitor-vbus; + status = "okay"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0x0>; + porta_role_switch: endpoint { + remote-endpoint = <&fusb301_ep>; + }; + }; + }; +}; +``` + +对应的 Type-C 控制器: + +```dts +tcpc@25 { + compatible = "onsemi,fusb301"; + reg = <0x25>; + irq-gpios = <...>; + wakeup-source; + status = "okay"; + + typec_0: connector@0 { + compatible = "usb-c-connector"; + data-role = "dual"; + power-role = "dual"; + try-power-role = "sink"; + typec-power-opmode = "default"; + pd-disable; + + ports { + port@0 { + reg = <0x0>; + fusb301_ep: endpoint { + remote-endpoint = <&porta_role_switch>; + }; + }; + }; + }; +}; +``` + +这说明 K3 的 gadget / OTG 并不只是改一个 `dr_mode`,而是完整依赖: + +- `usb-role-switch` +- `usb-c-connector` +- `onsemi,fusb301` +- endpoint graph + +### 4. SuperSpeed 方向切换 + +在 `k3_com260.dts` / `k3_com260_kit_v02.dts` 中,还能看到: + +- `fusb301_sw_ep` +- `usb3_phy_switch` + +这表示 Type-C 的插头翻转方向还会影响 SS PHY 路径选择。对 gadget 用户来说,这意味着: + +- USB2 gadget 可能能通; +- 但如果板级还想跑 SuperSpeed gadget,则 SS orientation switch 也必须正确接好。 + +## K3 Gadget bring-up 流程 + +建议按下面顺序 bring-up。 + +### 第一步:确认板级角色真的支持 device + +先看 DTS: + +- 是不是 `usb3_porta` +- 是不是 `dr_mode = "peripheral"` 或 `"otg"` +- 若是 Type-C,是否有 `usb-role-switch` +- 是否有 `fusb301` +- endpoint graph 是否完整 + +如果这些没配对,后面的 configfs 再正确也没用。 + +### 第二步:确认 UDC 是否出现 + +启动系统后查看: + +```bash +ls /sys/class/udc +``` + +如果 UDC 没有出现,优先检查: + +- DWC3 是否 probe 成功; +- `usb3_porta` 是否 `status = "okay"`; +- PHY 是否正常起来; +- role switch 是否把当前口切到了 device。 + +### 第三步:看日志 + +```bash +dmesg | grep -Ei "usb|dwc3|udc|typec|fusb" +``` + +重点看: + +- dwc3 probe 成功没有; +- gadget/udc 是否注册成功; +- FUSB301 是否探测成功; +- role switch 是否切换到 device; +- 是否有 PHY / link / VBUS 相关错误。 + +### 第四步:再做 configfs gadget + +只有在 UDC 出来以后,再继续配 configfs。否则就是反着调试,容易误判。 + +## Configfs 配置方法 + +K3 这里继续使用标准 Linux configfs 用法,和 K1 基本一致。 + +先挂载 configfs: + +```bash +mount -t configfs none /sys/kernel/config +cd /sys/kernel/config/usb_gadget +mkdir k3g +cd k3g +``` + +### 1. 设置设备描述符 + +```bash +echo 0x1d6b > idVendor +echo 0x0104 > idProduct +mkdir strings/0x409 +echo "0123456789" > strings/0x409/serialnumber +echo "SpacemiT" > strings/0x409/manufacturer +echo "K3 USB Gadget" > strings/0x409/product +``` + +### 2. 创建 configuration + +```bash +mkdir configs/c.1 +mkdir configs/c.1/strings/0x409 +echo "Config 1" > configs/c.1/strings/0x409/configuration +``` + +### 3. 添加 function + +#### ACM 串口示例 + +```bash +mkdir functions/acm.usb0 +ln -s functions/acm.usb0 configs/c.1/ +``` + +#### NCM 网卡示例 + +```bash +mkdir functions/ncm.usb0 +ln -s functions/ncm.usb0 configs/c.1/ +``` + +#### Mass Storage 示例 + +```bash +mkdir functions/mass_storage.usb0 +echo /root/udisk.img > functions/mass_storage.usb0/lun.0/file +ln -s functions/mass_storage.usb0 configs/c.1/ +``` + +### 4. 绑定 UDC + +先看可用 UDC: + +```bash +ls /sys/class/udc +``` + +再绑定: + +```bash +echo > UDC +``` + +例如: + +```bash +cat UDC +``` + +可以确认当前是否已经成功绑定。 + +## 常见 gadget 场景 + +### 1. USB 串口(ACM) + +适合最先打通: + +- 配置简单; +- 日志清晰; +- 容易判断 host/device 是否真正联通。 + +推荐作为 K3 gadget bring-up 的第一步。 + +### 2. USB 网卡(NCM / RNDIS) + +适合开发联机调试、批量刷机、内网通信等场景。 + +如果使用 Type-C DRD 口做这个功能,注意: + +- role switch 必须稳定; +- 插线方向和 VBUS 检测必须正常; +- PC 侧驱动也要匹配。 + +### 3. USB U 盘(Mass Storage) + +适合验证 bulk 传输是否稳定。 + +### 4. UVC / FunctionFS / ADB + +这些功能更适合在基础 gadget 打通之后再做;否则当底层 role / UDC / PHY 还没稳定时,应用层看起来会“像协议问题”,其实根因还在底层。 + +## K3 上使用 Gadget 时的实际注意事项 + +### 1. 不是所有 USB 口都能拿来做 gadget + +这是 K3 最重要的结论之一。 + +当前从 DTS 使用方式看: + +- 绝大多数 gadget / OTG 场景围绕 `usb3_porta` +- `usb2_host`、`usb3_portb/c/d` 大多是 host-only 使用方式 + +所以写产品 DTS 时,不要默认把任意 USB 节点改成 `peripheral`。 + +### 2. OTG 场景里,Type-C 链路比 configfs 更容易先出问题 + +很多人做 gadget 时会先怀疑: + +- function 配错了 +- UVC/ADB/MTP 脚本错了 + +但 K3 上更常见的根因其实是: + +- FUSB301 没起来; +- `usb-role-switch` graph 断了; +- `monitor-vbus` 不对; +- PHY switch/orientation graph 没接好; +- 口其实没切到 device。 + +### 3. 某些板级只支持 High-Speed gadget + +例如 `k3_fpga_1x1.dts` 中直接写: + +```dts +maximum-speed = "high-speed"; +``` + +所以即便控制器是 DWC3,也不要默认期望它一定跑到 SuperSpeed gadget。 + +### 4. `phy_type` 可能需要板级调整 + +例如: + +```dts +/delete-property/ phy_type; +phy_type = "utmi_wide"; +``` + +这类写法说明某些板级 USB device 方案对 PHY 接口类型有专门要求。碰到 gadget 不出枚举时,这也是一个必须检查的点。 + +## 调试建议 + +### 1. 先看 role + +```bash +ls /sys/class/usb_role/ +cat /sys/class/usb_role/*/role +``` + +如果当前不是 `device`,gadget 通常不会正常工作。 + +### 2. 再看 UDC + +```bash +ls /sys/class/udc +``` + +### 3. 再看 gadget 是否绑定成功 + +```bash +cat /sys/kernel/config/usb_gadget/k3g/UDC +``` + +### 4. 看 debugfs / 更详细日志 + +如果内核打开了 `CONFIG_USB_GADGET_DEBUG_FS`,可以进一步结合 debugfs 看 DWC3/gadget 相关状态。 + +### 5. Type-C 板级优先看 FUSB301 + +```bash +dmesg | grep -i fusb +``` + +如果 FUSB301 没起来,role switch 往往也不会正常。 + +## FAQ + +### 1. K3 的 gadget 口到底优先用哪个? + +优先看 `usb3_porta`。从现有 DTS 使用方式看,这是 K3 上真正用于 `peripheral` / `otg` / Type-C DRD 的核心端口。 + +### 2. 为什么我 configfs 都配好了,但上位机没枚举? + +先别急着改 function,先检查: + +- 当前 role 是不是 `device` +- UDC 有没有出来 +- `usb3_porta` 是否真的 `status = "okay"` +- `dr_mode` 是否正确 +- Type-C / FUSB301 / endpoint graph 是否正确 + +### 3. 为什么 gadget 只能跑 USB2 速度? + +可能原因包括: + +- 板级 DTS 写了 `maximum-speed = "high-speed"` +- 板级没有把 SS 通道接出来 +- Type-C SuperSpeed 方向切换链路没配好 +- PHY 侧只启用了 USB2 通道 + +### 4. 为什么 OTG 口默认是 peripheral? + +因为部分 K3 板级明确写了: + +```dts +role-switch-default-mode = "peripheral"; +``` + +这样更适合把该口当下载口、调试口或 gadget 口来使用。 From 16bf2374bbe26552d44fd475a6ebdedaebc8b5d6 Mon Sep 17 00:00:00 2001 From: Troy Date: Wed, 18 Mar 2026 15:31:16 +0800 Subject: [PATCH 12/30] k3: device: usb: sq test guide: initial version --- .../10-USB/3-USB-SQ-Test-Guide.md | 461 ++++++++++++++++++ 1 file changed, 461 insertions(+) diff --git a/zh/k3_buildroot/device/peripheral_driver/10-USB/3-USB-SQ-Test-Guide.md b/zh/k3_buildroot/device/peripheral_driver/10-USB/3-USB-SQ-Test-Guide.md index bb83cd1..93b4d6f 100644 --- a/zh/k3_buildroot/device/peripheral_driver/10-USB/3-USB-SQ-Test-Guide.md +++ b/zh/k3_buildroot/device/peripheral_driver/10-USB/3-USB-SQ-Test-Guide.md @@ -2,3 +2,464 @@ sidebar_position: 3 # USB 信号质量测试指南 +本文介绍 K3 平台 USB 在 **信号质量(SQ)/ 电气测试 / 基础链路验证** 方面的建议方法。 + +先说结论:K3 这部分不能简单照 K1 原文搬。K1 里有单独的 USB2 OTG/EHCI 章节,而 K3 当前主线是 **DWC3 + USB2/USB3 PHY + Host/Device/OTG/Type-C 组合拓扑**。所以 K3 这篇更适合分成: + +- **USB2 Device 高速测试模式** +- **USB Host 侧基础链路与功能验证** +- **USB3 / DRD / Type-C 板级联调注意点** +- **debugfs / role switch / link_state 调试** + +如果后续实验室已经有更明确的 USB-IF 测试治具流程,还可以继续把本篇往“正式认证流程”补细。 + +## 测试分类建议 + +对 K3 来说,建议把 USB 测试分成三层: + +### 1. 基础 bring-up / 功能测试 + +目标是确认: + +- 控制器起来了没有 +- PHY 起来了没有 +- Host / Device 角色对不对 +- 上位机能不能枚举 +- Mass storage / ACM / NCM / UVC 等功能能不能跑通 + +这层通常最先做,也最容易定位问题。 + +### 2. Device 侧 USB2 测试模式(Test Mode) + +目标是输出标准 USB2 测试波形,例如: + +- `test_j` +- `test_k` +- `test_se0_nak` +- `test_packet` +- `test_force_enable` + +K3 的 DWC3 驱动里明确支持这些测试模式,而且 debugfs 已经提供了 `testmode` 文件。 + +### 3. 实验室/认证类 SQ 测试 + +例如: + +- USB2 eye diagram +- rise/fall time +- jitter / monotonicity +- USB3 link / compliance 相关测试 + +这部分最终还是要以 USB-IF 文档、示波器/分析仪、测试夹具和实验室流程为准。本文主要讲 K3 端如何配合输出测试状态。 + +## 参考资料 + +### USB 2.0 电气规范 + +- [USB 2.0 Electrical Compliance Specification | USB-IF](https://www.usb.org/document-library/usb-20-electrical-compliance-test-specification-version-107) + +### xHCI 电气测试工具 + +- [xHCI Electrical Test Tool](https://www.usb.org/document-library/xhsett) + +### Linux 内核中的现成测试/调试支持 + +K3 SDK 内核里能直接看到这些测试相关实现: + +- `drivers/usb/dwc3/debugfs.c` +- `drivers/usb/dwc3/gadget.c` +- `drivers/usb/dwc3/ep0.c` +- `tools/usb/testusb.c` +- `tools/usb/hcd-tests.sh` +- `drivers/usb/gadget/function/f_loopback.c` +- `drivers/usb/gadget/function/f_sourcesink.c` + +这意味着 K3 的测试能力并不是“纯靠外部工具”,内核本身已经提供了比较完整的调试抓手。 + +## K3 上和 SQ 测试最相关的 USB 端口 + +根据现有 K3 DTS 使用方式: + +- `usb2_host`:固定 USB2 Host +- `usb3_porta`:最核心的 DRD / OTG / Device 端口 +- `usb3_portb/c/d`:主要作为 Host 端口 + +如果你要做 **Device 侧 USB2 test mode**,当前最值得优先关注的是: + +- `usb3_porta` + +因为它在现有板级 DTS 中: + +- 可能被配置成 `dr_mode = "peripheral"` +- 也可能被配置成 `dr_mode = "otg"` +- 还可能与 `usb-role-switch` / `FUSB301` / Type-C connector 一起工作 + +所以对 K3 来说,SQ 测试前最重要的一步不是直接发 test packet,而是先确认 **当前这个口真的处于 device 模式**。 + +## K3 DWC3 debugfs 能力 + +K3 SDK 中 `drivers/usb/dwc3/debugfs.c` 明确创建了这些 debugfs 节点: + +- `regdump` +- `lsp_dump` +- `mode`(Dual Role 打开时) +- `testmode` +- `link_state` + +对应代码里可以看到: + +```c +if (IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)) + debugfs_create_file("mode", 0644, root, dwc, + &dwc3_mode_fops); + +if (IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE) || + IS_ENABLED(CONFIG_USB_DWC3_GADGET)) { + debugfs_create_file("testmode", 0644, root, dwc, + &dwc3_testmode_fops); + debugfs_create_file("link_state", 0644, root, dwc, + &dwc3_link_state_fops); +} +``` + +同时 debugfs 根目录名来自: + +```c +root = debugfs_create_dir(dev_name(dwc->dev), usb_debug_root); +``` + +也就是说,实际路径不是写死的固定字符串,而是**按设备名动态生成**。因此 K3 文档里不应该直接硬编码成某一个 K1 风格的路径,而应该告诉用户: + +1. 先挂载 debugfs +2. 再到 `/sys/kernel/debug/usb/` 下查看真实目录名 + +例如: + +```bash +mount -t debugfs none /sys/kernel/debug +ls /sys/kernel/debug/usb/ +``` + +## USB 2.0 Device 信号质量测试 + +### 测试波形类型 + +K3 的 DWC3 debugfs / gadget 逻辑支持这些 USB2 test mode: + +- `test_j` +- `test_k` +- `test_se0_nak` +- `test_packet` +- `test_force_enable` +- `none` + +这些字符串在 `drivers/usb/dwc3/debugfs.c` 中都能直接对应到: + +- `USB_TEST_J` +- `USB_TEST_K` +- `USB_TEST_SE0_NAK` +- `USB_TEST_PACKET` +- `USB_TEST_FORCE_ENABLE` + +### 适用前提 + +在 K3 上做 Device 侧 SQ 测试前,请先确保: + +1. 目标端口真的是 `usb3_porta` +2. 当前口已经切到 **device/peripheral** +3. 如果是 Type-C 板级,`FUSB301` 已正常工作 +4. `usb-role-switch` graph 正确 +5. UDC 已经注册成功 +6. debugfs 已挂载 + +如果当前 role 还在 host,向 `testmode` 写值通常没有意义。 + +### 典型 DTS 场景 1:固定 peripheral + +例如 `k3_fpga_1x1.dts`: + +```dts +&usb3_porta { + status = "disabled"; + maximum-speed = "high-speed"; + dr_mode = "peripheral"; + /delete-property/ phy_type; + phy_type = "utmi_wide"; +}; +``` + +这种板级最适合先做 Device 侧测试,因为路径比较单纯。 + +### 典型 DTS 场景 2:OTG + Type-C + +例如 `k3_deb1.dts`、`k3_dc_board.dts`、`k3_com260_kit_v02.dts` 中都能看到: + +```dts +&usb3_porta { + dr_mode = "otg"; + usb-role-switch; + role-switch-default-mode = "peripheral"; + monitor-vbus; + status = "okay"; +}; +``` + +这类板子做 SQ 测试前,要先解决: + +- 插线后 role 有没有切到 device +- Type-C 控制器(如 `onsemi,fusb301`)有没有起来 +- VBUS 监测是否正常 + +### K3 Device 侧 testmode 操作步骤 + +#### 1. 挂载 debugfs + +```bash +mount -t debugfs none /sys/kernel/debug +``` + +#### 2. 找到真实 USB debugfs 节点 + +```bash +ls /sys/kernel/debug/usb/ +``` + +然后进入对应的 DWC3 目录,例如: + +```bash +cd /sys/kernel/debug/usb/<实际的dwc3目录名>/ +ls +``` + +如果配置正确,通常应能看到: + +- `mode` +- `testmode` +- `link_state` +- `regdump` +- `lsp_dump` + +#### 3. 查看当前模式 + +```bash +cat mode +cat testmode +cat link_state +``` + +#### 4. 进入测试模式 + +常见顺序: + +```bash +echo test_force_enable > testmode +echo test_packet > testmode +``` + +或者直接: + +```bash +echo test_j > testmode +echo test_k > testmode +echo test_se0_nak > testmode +echo test_packet > testmode +``` + +#### 5. 查看当前状态 + +```bash +cat testmode +``` + +#### 6. 退出测试模式 + +```bash +echo none > testmode +``` + +### 上位机通过 xHCI Electrical Test Tool 触发 + +如果实验室流程是通过上位机工具控制设备进入测试模式,那么在 K3 上也可以沿用这条路线: + +- 先让 `usb3_porta` 进入 device / peripheral +- 接到测试治具和上位机 +- 使用 USB-IF 的 xHCI Electrical Test Tool 发相应测试命令 + +这条路径对 K3 仍然成立,因为底层仍是标准 USB Device / DWC3 gadget 体系。 + +## USB Host 侧测试建议 + +K3 当前 Host 端口更多,实际板级差异也更大,因此 Host 侧测试更适合分成两类: + +### 1. Host 功能回归测试 + +重点确认: + +- U 盘枚举是否正常 +- HUB 枚举是否正常 +- 键鼠/网卡/摄像头是否正常 +- USB2 / USB3 速率是否符合预期 +- Type-A / Type-C Host 口是否稳定 + +推荐最先执行: + +```bash +dmesg | grep -Ei "usb|xhci|dwc3" +lsusb +lsusb -t +``` + +如果插入 U 盘: + +```bash +lsblk +mount /dev/sdX1 /mnt +``` + +### 2. Host 侧协议/传输测试 + +K3 SDK 内核中已有: + +- `tools/usb/testusb.c` +- `tools/usb/hcd-tests.sh` + +这说明可以在 Host 场景下结合 `usbtest` 驱动做更系统的传输测试。 + +适合的场景包括: + +- bulk/control/interrupt 传输稳定性 +- loopback 类测试 +- 基础主机控制器功能验证 + +如果后续要把 USB Host 测试这部分写得更深,可以专门补充: + +- `usbtest` 驱动的配置方式 +- `testusb` 工具的编译和运行方式 +- 哪些测试项适合 K3 日常回归 + +## USB3 / DRD / Type-C 联调注意点 + +K3 上很多“像 SQ 问题”的故障,实际根因不一定是信号质量本身,而可能是: + +- 角色没切对 +- Type-C graph 没接好 +- `monitor-vbus` 不工作 +- `FUSB301` 中断脚不对 +- `usb3_phy_switch` 没配好 +- 板级把口限速成 `high-speed` + +所以建议在正式怀疑 SQ 之前,先排这些。 + +### 1. 检查 role + +```bash +ls /sys/class/usb_role/ +cat /sys/class/usb_role/*/role +``` + +### 2. 检查 Type-C + +```bash +ls /sys/class/typec/ +dmesg | grep -i fusb +``` + +### 3. 检查 UDC + +```bash +ls /sys/class/udc +``` + +### 4. 检查 link state + +如果 DWC3 debugfs 节点存在: + +```bash +cat /sys/kernel/debug/usb//link_state +``` + +### 5. 检查 mode + +```bash +cat /sys/kernel/debug/usb//mode +``` + +Dual Role 模式下,这个节点对定位当前控制器到底在 Host 还是 Device 很有用。 + +## K3 板级测试建议 + +### 场景一:固定 peripheral 下载口 + +如果某板级和 `k3_fpga_1x1.dts` 类似,建议先做: + +1. 枚举测试 +2. gadget ACM / NCM 基础功能测试 +3. testmode 输出 `test_packet` +4. 再做实验室眼图测试 + +### 场景二:Type-C OTG 口 + +如果某板级和 `k3_deb1.dts` / `k3_dc_board.dts` / `k3_com260_kit_v02.dts` 类似,建议先做: + +1. Type-C 角色切换测试 +2. Host / Device 双向枚举测试 +3. gadget ACM / NCM 基础验证 +4. 再做 testmode +5. 最后再做更严格的 SQ 测试 + +因为这种场景里,role switch 往往比纯电气问题更先出错。 + +### 场景三:固定 Host 口 + +对于 `usb2_host`、`usb3_portb/c/d`,建议优先做: + +1. `lsusb` / `lsusb -t` +2. U 盘 / HUB / 网卡 / 摄像头枚举 +3. `testusb` / `usbtest` 主机传输回归 +4. 若需要实验室电气测试,再按具体控制器/夹具流程继续 + +## FAQ + +### 1. K3 能不能像 K1 那样直接写死某个 debugfs 路径? + +不建议。因为 K3 DWC3 debugfs 目录名来自 `dev_name(dwc->dev)`,更稳妥的方式是先: + +```bash +ls /sys/kernel/debug/usb/ +``` + +再进入真实目录。 + +### 2. K3 支不支持 `test_j` / `test_k` / `test_packet`? + +支持。`drivers/usb/dwc3/debugfs.c` 明确实现了: + +- `test_j` +- `test_k` +- `test_se0_nak` +- `test_packet` +- `test_force_enable` +- `none` + +### 3. 为什么我写了 `testmode` 但没看到波形? + +优先排查: + +- 当前口是不是 `usb3_porta` +- 当前 role 是不是 `device` +- UDC 是否已注册 +- debugfs 是否挂载 +- 板级是不是 OTG/Type-C,且还没真正切到 device +- `maximum-speed` / `phy_type` 是否和板级硬件匹配 + +### 4. K3 的 SQ 文档为什么比 K1 更强调 role switch? + +因为 K3 实际产品板大量采用: + +- `dr_mode = "otg"` +- `usb-role-switch` +- `role-switch-default-mode = "peripheral"` +- `monitor-vbus` +- `onsemi,fusb301` + +所以在 K3 上,很多测试前提都取决于角色链路是否先正确工作。 From 55eff3a151b112656d9e85f9682559b16931db80 Mon Sep 17 00:00:00 2001 From: Troy Date: Wed, 18 Mar 2026 15:37:08 +0800 Subject: [PATCH 13/30] k3: device: usb: general guide: expand board-schematic guidance --- .../10-USB/1-USB-General-Developer-Guide.md | 253 +++++++++++++++++- 1 file changed, 248 insertions(+), 5 deletions(-) diff --git a/zh/k3_buildroot/device/peripheral_driver/10-USB/1-USB-General-Developer-Guide.md b/zh/k3_buildroot/device/peripheral_driver/10-USB/1-USB-General-Developer-Guide.md index 78222b0..364c1a8 100644 --- a/zh/k3_buildroot/device/peripheral_driver/10-USB/1-USB-General-Developer-Guide.md +++ b/zh/k3_buildroot/device/peripheral_driver/10-USB/1-USB-General-Developer-Guide.md @@ -26,13 +26,13 @@ K3 的 USB 子系统比 K1 明显更复杂,不能只按 K1 的“三个控制 ### K3 实际控制器分布 -从 `k3.dtsi` 可以确认: +从 `k3.dtsi` 可以确认 SoC 默认定义为: - `usb2_host`:固定 Host,`maximum-speed = "high-speed"` -- `usb3_porta`:默认作为 DRD/OTG 使用的主要端口 -- `usb3_portb`:固定 Host -- `usb3_portc`:固定 Host -- `usb3_portd`:固定 Host +- `usb3_porta`:带双 PHY 的可配置端口,板级上通常拿来做 DRD/OTG +- `usb3_portb`:SoC 默认 `dr_mode = "host"` +- `usb3_portc`:SoC 默认 `dr_mode = "host"` +- `usb3_portd`:SoC 默认 `dr_mode = "host"` 其中控制器节点当前仍使用: @@ -120,6 +120,249 @@ K3 的 USB PHY 节点由 DTS 中的专用 compatible 描述: - `spacemit,k3-usb3-phy` - `spacemit,k3-typec-switch`(用于部分 DRD/Type-C 场景) +## 基于原理图进行 USB 方案配置 + +这一部分是这次补充的重点。K3 的 USB 文档如果只写控制器和 DTS 语法,其实还不够,因为很多板级问题根本不在控制器本身,而在 **原理图怎么把 USB 控制器、PHY、Type-C 芯片、板载 HUB、M.2/BT 模组和 GPIO sideband 信号接起来**。 + +建议实际做板级移植时,固定按下面这个顺序看: + +1. **先看 Block Diagram**:确认每个 USB 控制器最终接到了什么器件; +2. **再看 USB 复合端口关系**:确认某个 USB3 控制器是不是只用了 USB2 部分; +3. **再看外围器件**:例如 Type-C CC 控制器、HUB、电源开关、M.2 模组、BT 模组; +4. **最后再写 DTS / U-Boot / EDK2 配置**。 + +### 1. 先理解 K3 的 USB 复合端口 + +K3 上每一个 USB3 控制器,本质上都要按 **复合端口(Companion Port)** 理解: + +- `1 * USB3 Companion Port = 1 * USB3 SuperSpeed PHY + 1 * USB2 PHY` + +这点非常关键。因为很多板子虽然挂的是 `usb3_portb/c/d` 这种 USB3 控制器,但 **实际原理图只把 USB2 D+/D- 这半边引出来**,SuperSpeed TX/RX 根本没接,或者和 PCIe PHY 复用。 + +这时 DTS 就不能还按完整 SuperSpeed 端口去写,而应该显式限制: + +```dts +maximum-speed = "high-speed"; +``` + +必要时还要删掉默认的双 PHY 绑定,只保留 `usb2-phy`: + +```dts +/delete-property/ phys; +/delete-property/ phy-names; +phys = <&usb3_portb_u2phy>; +phy-names = "usb2-phy"; +``` + +这类写法在 `k3_evb.dts`、`k3_deb1.dts`、`k3_gemini_c0.dts` 等板级里都能看到。 + +### 2. 通过 Block Diagram 判断哪些控制器要使能 + +这次补看的飞书文档里,这部分写得很对,我把它提炼成 K3 文档里更通用的判断方法。 + +以 DEB1 这类板子为例,通常可以先从 Block Diagram 得到下面这些结论: + +- **USB2.0 Host**:如果接了板载 USB HUB,就需要使能 `usb2_host`,并继续确认 HUB 的复位/上电/VBUS 控制; +- **USB3 PortA**:如果接的是 Type-C DRD 口,就要重点确认 `usb3_porta` + `FUSB301` + role switch + orientation switch; +- **USB3 PortB**:如果接的是 M.2/WWAN 插槽,而且只用了 USB2 通道,就要限制 `maximum-speed = "high-speed"`,同时检查 WWAN 的 `RESET` / `MODULE_TURN_ON` / `W_DISABLE#` 等 GPIO; +- **USB3 PortC**:如果接的是板载蓝牙,通常也只是 USB2 设备,需要限制 high-speed,并补上 BT enable / reset GPIO; +- **USB3 PortD**:如果接的是另一个完整 Type-C / DP Alt Mode 接口,就要继续确认对应 Type-C/PD 芯片(例如 ANX7447)以及相关 GPIO / I2C / mux。 + +换句话说,**K3 的 USB 方案配置首先是“读原理图 + 列待办”问题,不是先写 DTS 问题。** + +### 3. K3 原理图阅读时最容易漏掉的几个点 + +#### 3.1 某个 USB3 控制器可能只有 USB2 有效 + +这是 K3 最常见的板级差异之一。 + +- 可能 SuperSpeed PHY 没引出; +- 可能 SuperSpeed PHY 和 PCIe 共用; +- 可能最终只接了板载 USB2 器件。 + +这种场景下文档必须强调两件事: + +1. **不是所有 `usb3_portX` 都真能跑 SuperSpeed**; +2. **不能默认保留 `usb2-phy + usb3-phy` 的双 PHY 组合**。 + +#### 3.2 板载器件通常带有 sideband GPIO + +比如: + +- Type-C 芯片:`irq-gpios`、方向/翻转选择、I2C; +- 板载 HUB:复位脚、电源使能脚、下游端口 VBUS 开关; +- M.2 WWAN:`shutdown-gpios`、`reset-gpios`、`W_DISABLE#`; +- 板载 BT:`shutdown-gpios` 或 `reg_on` / `reset`。 + +这些 GPIO 如果不配,现象通常不是“驱动 probe 失败”,而是: + +- 设备枚举不到; +- 外设不启动; +- 端口没供电; +- Type-C 角色不对; +- 模组上电后没出 USB 设备。 + +#### 3.3 pinctrl 的 `power-source` 不能忽略 + +飞书文档里专门提到了某些 Type-C / DIR 引脚的电压域问题,这个提醒很有价值。 + +例如某些 pinctrl 子节点会显式写: + +```dts +power-source = <1800>; +``` + +这类配置要回到原理图确认实际 IO 电压,不要只看功能复用。 + +### 4. 以 `usb3_porta` Type-C DRD 为例 + +如果原理图显示 `usb3_porta` 外接的是 Type-C 接口,而且 CC 芯片是 `FUSB301`,那板级 DTS 一般至少要具备下面几部分: + +#### 4.1 FUSB301 所在 I2C 总线 + +```dts +&i2c1 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c1_3_cfg>; + status = "okay"; + + tcpc@25 { + compatible = "onsemi,fusb301"; + reg = <0x25>; + pinctrl-names = "default"; + pinctrl-0 = <&fusb301_cfg>; + irq-gpios = <&gpio 2 21 GPIO_ACTIVE_LOW>; + wakeup-source; + status = "okay"; + }; +}; +``` + +#### 4.2 Type-C connector graph + +```dts +connector@0 { + compatible = "usb-c-connector"; + data-role = "dual"; + power-role = "dual"; + try-power-role = "sink"; + pd-disable; +}; +``` + +#### 4.3 `usb3_porta` role switch + +```dts +&usb3_porta { + dr_mode = "otg"; + usb-role-switch; + role-switch-default-mode = "peripheral"; + monitor-vbus; + status = "okay"; +}; +``` + +#### 4.4 必要时还要补 SS 方向切换 + +在 `k3_com260.dts` / `k3_com260_kit_v02.dts` 中还能看到: + +- `usb3_phy_switch` +- `fusb301_sw_ep` + +这说明如果板级真想把 Type-C 的 SuperSpeed 也做完整,除了 role switch 之外,还要处理 **插头翻转导致的 SS lane 方向切换**。 + +### 5. 以 `usb2_host` + 板载 HUB 为例 + +如果 `usb2_host` 接的是板载 HUB,单纯把 Host 控制器开起来通常还不够。还要确认: + +- HUB 是否需要复位脚; +- HUB 下游 VBUS 是否需要受控; +- VBUS 开关是主控 GPIO 控,还是 HUB 自己控制。 + +如果某个 VBUS 开关需要主控 GPIO 拉高才能供电,一个常见 DTS 方案是增加 `regulator-fixed`: + +```dts +hub_vbus: regulator-hub-vbus-5v { + compatible = "regulator-fixed"; + regulator-name = "HUB_VBUS_5V"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + gpio = <&gpio 0 18 GPIO_ACTIVE_HIGH>; + regulator-always-on; + regulator-boot-on; + enable-active-high; +}; +``` + +K3 当前 USB 文档之前对这类“板载 HUB/板载供电”的强调还不够,这次一起补上。 + +### 6. 以 `usb3_portb` 接 M.2 WWAN 为例 + +飞书文档给的这个例子也很典型,值得吸收到 K3 通用文档里。 + +如果 `usb3_portb` 接的是 M.2 Key B / WWAN 模组,常见特点是: + +- 只用 USB2 通道; +- 需要限制 `maximum-speed = "high-speed"`; +- 需要额外配置 WWAN 的 `MODULE_TURN_ON`、`RESET` 等 sideband GPIO。 + +典型 DTS 可能会补一个 `rfkill-gpio` 节点: + +```dts +rfkill-usb-wwan { + compatible = "rfkill-gpio"; + label = "m.2 WWAN"; + radio-type = "wwan"; + shutdown-gpios = <&gpio 0 18 GPIO_ACTIVE_HIGH>; + reset-gpios = <&gpio 0 19 GPIO_ACTIVE_HIGH>; +}; +``` + +这类内容之前我的 USB 通用文档里写得偏“控制器视角”,这次补完以后会更贴近实际板级移植。 + +### 7. Linux / U-Boot / EDK2 三套配置思路要区分 + +飞书文档还有一个有用点:把 Linux DTS、U-Boot DTS、EDK2 DSC 三套配置分开讲了。这个对 K3 很重要。 + +#### Linux DTS + +Linux 侧重点是: + +- 控制器节点 +- PHY 绑定 +- role switch graph +- Type-C 芯片 +- 板载 GPIO / regulator / rfkill + +#### U-Boot DTS + +U-Boot 侧当前仍有一些 **nested node** 结构差异,某些属性需要写到控制器内部的 `dwc3@...` 子节点里,例如 `dr_mode`。 + +#### EDK2 DSC + +EDK2 则偏固件配置表方式,例如: + +- `PcdUsbHostConfigs.Controller[x].Enable` +- `PcdUsbHostConfigs.Controller[x].MaxSpeed` +- `PcdUsbHostVbusConfigs` +- `PcdUsbHostPinctrlConfigs` + +所以 K3 做完整 USB 板级方案时,不能只看 Linux DTS。很多量产板最终还得同步考虑 U-Boot 和 EDK2。 + +### 8. K3 USB 方案配置的推荐工作流 + +建议在文档里明确给使用者一个固定流程: + +1. **看 Block Diagram**:确认 5 个控制器谁启用; +2. **标记每个端口最终接到什么**:Type-C / HUB / M.2 / BT / 标准 A 口; +3. **判断是否只有 USB2 有效**:决定是否限速为 `high-speed`; +4. **列 sideband GPIO 待办**:reset / enable / power / INT / DIR; +5. **补 pinctrl 和 power-source**; +6. **再写 Linux DTS**; +7. **需要时同步写 U-Boot DTS / EDK2 DSC**; +8. **最后做 Host / Gadget / OTG 联调**。 + + ## 关键特性 ### 特性 From c5158d274752940cda63a9553006b8f5989b0ddc Mon Sep 17 00:00:00 2001 From: Troy Date: Wed, 18 Mar 2026 15:54:19 +0800 Subject: [PATCH 14/30] k3: device: peripheral_driver: CAN: fix dts examples and expand board usage --- .../device/peripheral_driver/15-CAN.md | 36 ++++++++++++++++--- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/zh/k3_buildroot/device/peripheral_driver/15-CAN.md b/zh/k3_buildroot/device/peripheral_driver/15-CAN.md index df7066d..aa46194 100644 --- a/zh/k3_buildroot/device/peripheral_driver/15-CAN.md +++ b/zh/k3_buildroot/device/peripheral_driver/15-CAN.md @@ -54,7 +54,7 @@ K3 的 CAN 控制器在 Linux 中表现为网络设备,例如: - `can3` - `can4` -如果启用了 R-domain CAN,则还可能出现对应的 rdomain 控制器实例。 +如果启用了 R-domain CAN,则还可能出现对应的 `r_flexcanX` 控制器实例。 ### 源码结构介绍 @@ -91,7 +91,7 @@ linux-6.18/ | :----- | :---- | | 协议类型 | 支持 CAN,驱动体系也支持 CAN FD 能力(取决于具体 compatible / 控制器能力) | | Linux 接口 | 使用 SocketCAN,表现为标准网络设备 | -| 控制器数量 | K3 A-domain 提供 `flexcan0 ~ flexcan4`,R-domain 提供 `rflexcan0 ~ rflexcan4` | +| 控制器数量 | K3 A-domain 提供 `flexcan0 ~ flexcan4`,R-domain 提供 `r_flexcan0 ~ r_flexcan4` | | 时钟源选择 | 支持 `fsl,clk-source` 属性 | | 收发器支持 | 支持 `xceiver-supply` 和 `can-transceiver` 描述外部收发器 | | 板级终端控制 | binding 支持 `termination-gpios` | @@ -132,6 +132,32 @@ CAN 联调要同时满足: 所以文档里不能只讲驱动,也必须讲板级和用户空间 bring-up 方法。 +### K3 板级现状和使用建议 + +从 K3 当前 SDK 的 `k3.dtsi`、`k3-rdomain.dtsi` 以及板级 DTS 来看,可以总结出几个很实用的事实: + +1. **A-domain 一共有 5 路 CAN**:`flexcan0 ~ flexcan4`; +2. **R-domain 也有 5 路 CAN**:`r_flexcan0 ~ r_flexcan4`; +3. 当前板级里最常见的启用方式,是直接在板级 DTS 里给某一路控制器补: + - `clock-frequency = <80000000>;` + - `pinctrl-names = "default";` + - `pinctrl-0 = <&canX_Y_cfg>;` + - `status = "okay";` +4. `k3_com260_kit_v02.dts` 是一个很好的参考板,里面一次启用了多路: + - `&flexcan0` + - `&flexcan1` + - `&flexcan2` + - `&flexcan3` + - `&flexcan4` + - `&r_flexcan2` +5. `k3_com260.dts` 里则只启用了 `&flexcan2`,这也说明不同板子通常只会打开实际引出的那几路,而不是把所有控制器都开起来。 + +所以对用户来说,**最稳妥的做法不是“看到 dtsi 里有 10 路 CAN 就全开”**,而是: + +- 先看原理图/接口定义; +- 再看板级 pinctrl 有没有对应 `canX_Y_cfg` / `rcanX_Y_cfg`; +- 再确认对应收发器、电源、终端电阻和外部接口是否真的焊出。 + ## 配置介绍 ### CONFIG 配置 @@ -184,10 +210,10 @@ flexcan0: fdcan@d4028000 { K3 R-domain 中也有对应节点,例如: ```dts -rflexcan0: fdcan@c0883000 { +r_flexcan0: fdcan@c0710000 { compatible = "spacemit,k1-flexcan"; - reg = <0x0 0xc0883000 0x0 0x4000>; - interrupts = <45 IRQ_TYPE_LEVEL_HIGH>; + reg = <0x0 0xc0710000 0x0 0x4000>; + interrupts = <241 IRQ_TYPE_LEVEL_HIGH>; interrupt-parent = <&saplic>; fsl,clk-source = <0>; clocks = <&syscon_rcpu_sysctrl CLK_RCPU_SYSCTRL_RCAN0>, From c2e57e4c37497f4fa20d76c69720e7a5e54fc928 Mon Sep 17 00:00:00 2001 From: Troy Date: Wed, 18 Mar 2026 16:02:57 +0800 Subject: [PATCH 15/30] k3: device: peripheral_driver: Cpufreq: initial version --- .../device/peripheral_driver/15-Cpufreq.md | 477 ++++++++++++++++++ 1 file changed, 477 insertions(+) diff --git a/zh/k3_buildroot/device/peripheral_driver/15-Cpufreq.md b/zh/k3_buildroot/device/peripheral_driver/15-Cpufreq.md index 73a47b6..c2ea4a0 100644 --- a/zh/k3_buildroot/device/peripheral_driver/15-Cpufreq.md +++ b/zh/k3_buildroot/device/peripheral_driver/15-Cpufreq.md @@ -1,2 +1,479 @@ # CPUFREQ +介绍 K3 平台 CPUFREQ 的功能、设备树配置方式、用户空间调频接口与常见调试方法。 + +## 模块介绍 + +CPUFREQ 子系统负责在系统运行过程中动态调整 CPU 频率,并在需要时配合 OPP(Operating Performance Points)表、时钟和电源约束一起工作,在性能与功耗之间取得平衡。 + +对用户来说,CPUFREQ 文档最需要回答的是这几个问题: + +- K3 用的是哪套 cpufreq 驱动; +- 频点是写死在驱动里,还是来自 DTS / OPP 表; +- 哪些 CPU 共用一套策略; +- 怎么查看当前频率、支持频点、governor; +- 如果频率拉不上去,应该先查哪里。 + +### 功能介绍 + +![](static/cpufreq.png) + +Linux CPUFREQ 体系通常分为四层: + +1. **cpufreq core** + 提供调频框架、sysfs 接口和通知机制; +2. **cpufreq driver** + 负责平台相关的频率切换逻辑; +3. **governor** + 负责决定什么时候升频、什么时候降频; +4. **OPP / clock / regulator** + 描述不同频率档位下所需的时钟与电压约束。 + +K3 平台的 CPUFREQ 不是单纯“改一个频率值”这么简单,而是和以下内容一起联动: + +- `drivers/cpufreq/spacemit-k3-cpufreq.c` +- `cpufreq-dt` +- `operating-points-v2` +- CPU cluster 的 PLL / clock parent +- `clst-supply` 电源约束(部分 cluster) + +### 源码结构介绍 + +K3 CPUFREQ 相关代码主要位于: + +```text +linux-6.18/ +|-- drivers/cpufreq/ +| |-- cpufreq.c +| |-- cpufreq-dt.c +| |-- cpufreq-dt-platdev.c +| |-- cpufreq_governor.c +| |-- cpufreq_ondemand.c +| |-- cpufreq_userspace.c +| |-- cpufreq_performance.c +| |-- cpufreq_powersave.c +| |-- cpufreq_stats.c +| `-- spacemit-k3-cpufreq.c # K3 平台相关 cpufreq 扩展驱动 +`-- arch/riscv/boot/dts/spacemit/ + |-- k3_opp_table.dtsi # K3 OPP 表 + |-- k3.dtsi # CPU 基础节点 + `-- k3*.dts # 板级 DTS,通常通过 include OPP 表生效 +``` + +从当前 SDK 看,K3 不是完全自定义一整套 cpufreq 框架,而是: + +- 使用 **`cpufreq-dt`** 作为基础调频驱动; +- 再通过 **`spacemit-k3-cpufreq.c`** 提前配置 OPP / clock / regulator 相关上下文; +- 通过 `operating-points-v2` 和 OPP 表驱动实际频点切换。 + +## 关键特性 + +### 特性 + +| 特性 | 特性说明 | +| :----- | :---- | +| 基于 OPP 表调频 | 频率档位主要来自 `k3_opp_table.dtsi` | +| 基于 cpufreq-dt | 底层复用标准 `cpufreq-dt` 路径 | +| K3 平台扩展驱动 | `spacemit-k3-cpufreq.c` 在 probe 前预配置 OPP / clk / regulator | +| cluster 级共享策略 | OPP 表使用 `opp-shared`,同一 cluster 的 CPU 共用频率策略 | +| 频率与时钟联动 | OPP 表中同时关联 PLL/clock 信息 | +| 部分 cluster 具备电压约束 | `cpu_0 ~ cpu_7` 通过 `clst-supply = <&edcdc1>` 参与调压 | + +### K3 当前频点现状 + +从 `k3_opp_table.dtsi` 可以看到,K3 当前至少定义了两套 cluster 频点表: + +- `clst_core_opp_table0_x100` +- `clst_core_opp_table0_a100` + +其中: + +- `cpu_0 ~ cpu_7` 绑定 `clst_core_opp_table0_x100` +- `cpu_8 ~ cpu_15` 绑定 `clst_core_opp_table0_a100` + +#### `clst_core_opp_table0_x100` 示例频点 + +| 频率 | 电压 | +| :--- | :--- | +| 2400000000 | 1000000 uV | +| 2300000000 | 880000 uV | +| 2200000000 | 880000 uV | +| 2100000000 | 880000 uV | +| 2000000000 | 880000 uV | +| 1800000000 | 880000 uV | +| 1600000000 | 880000 uV | +| 1500000000 | 850000 uV | +| 1400000000 | 850000 uV | +| 1300000000 | 850000 uV | +| 1200000000 | 850000 uV | +| 1100000000 | 850000 uV | +| 1000000000 | 800000 uV | +| 819200000 | 800000 uV | +| 614400000 | 800000 uV | + +#### `clst_core_opp_table0_a100` 示例频点 + +这套表也覆盖了: + +- `2000000000` +- `1900000000` +- `1850000000` +- `1800000000` +- `1700000000` +- `1600000000` +- `1500000000` +- `1400000000` +- `1300000000` +- `1200000000` +- `1100000000` +- `1000000000` +- `819200000` +- `614400000` + +但当前 DTS 片段里没有像 `x100` 那样逐项列出 `opp-microvolt`,因此文档里不应擅自补充不存在的电压值。 + +### 从用户角度最值得关注的点 + +#### 1. K3 不是所有 CPU 各自独立调频 + +K3 当前 DTS 使用的是 cluster 级共享 OPP 设计,`opp_table` 节点带有: + +```dts +opp-shared; +``` + +这意味着同一组共享策略的 CPU,不是你单独调一个核就能完全独立跑一个频率,而是按 policy / related CPUs 的方式成组工作。 + +#### 2. 频率是否可达,不只取决于 governor + +如果一个频点没有出现在: + +- `operating-points-v2` +- OPP 表 +- 对应供电/时钟配置 + +那么就算你在 userspace 里强行写目标频率,也未必能真正切过去。 + +#### 3. K3 cpufreq 驱动做了额外的 OPP / 时钟处理 + +`spacemit-k3-cpufreq.c` 不只是注册一个名字,它做了几件关键事: + +- 在 `cpufreq-dt` platform device 绑定时,提前为各 CPU 初始化 OPP 上下文; +- 通过 `dev_pm_opp_set_config()` 指定 regulator 名称 `clst` 和 clock 名称 `cls0` / `cls1`; +- 对 `cpu >= 8` 的 cluster,不再配置 regulator 名称; +- 通过 notifier 在 PRECHANGE 阶段处理 cluster PLL / parent 切换。 + +也就是说,K3 这套方案是 **cpufreq-dt + SpacemiT 平台补丁层**,不是完全 generic 的最小实现。 + +## 配置介绍 + +主要包括 **驱动使能配置** 和 **DTS / OPP 配置**。 + +### CONFIG 配置 + +K3 相关配置的核心是: + +- `CONFIG_CPU_FREQ` +- `CONFIG_CPUFREQ_DT` +- `CONFIG_CPUFREQ_DT_PLATDEV` +- `CONFIG_SPACEMIT_K3_CPUFREQ` +- 以及常用 governor: + - `CONFIG_CPU_FREQ_GOV_PERFORMANCE` + - `CONFIG_CPU_FREQ_GOV_POWERSAVE` + - `CONFIG_CPU_FREQ_GOV_USERSPACE` + - `CONFIG_CPU_FREQ_GOV_ONDEMAND` + - `CONFIG_CPU_FREQ_GOV_CONSERVATIVE` + +K3 平台驱动的 Kconfig 片段如下: + +```text +config SPACEMIT_K3_CPUFREQ + tristate "CPU frequency scaling driver for Spacemit K1X" + depends on OF && COMMON_CLK + select CPUFREQ_DT + select CPUFREQ_DT_PLATDEV + help + This adds the CPUFreq driver support for Spacemit K3 SoC + which are capable of changing the CPU's frequency dynamically. +``` + +> 注意:这里的 prompt 还写着 `Spacemit K1X`,但 help 和实际文件名已经明确这是 K3 路径。写文档时应按 **K3 实际实现** 说明,而不是照着 prompt 机械照抄。 + +### DTS / OPP 配置 + +#### 1. OPP 表位置 + +K3 完整 OPP 表位于: + +```text +arch/riscv/boot/dts/spacemit/k3_opp_table.dtsi +``` + +多个板级 DTS 直接 include 该文件,例如: + +- `k3_evb.dts` +- `k3_deb1.dts` +- `k3_com260.dts` +- `k3_com260_kit_v02.dts` +- `k3_gemini_c0.dts` +- `k3_dc_board.dts` + +这说明 K3 当前 CPUFREQ 频点定义是**集中维护**的,而不是每个板级 DTS 单独写一份。 + +#### 2. OPP 表的基本写法 + +示例: + +```dts +clst_core_opp_table0_x100: opp_table0_x100 { + compatible = "operating-points-v2"; + opp-shared; + + clocks = <&pll CLK_PLL3>, <&pll CLK_PLL4>, + <&pll CLK_PLL3_D1>, <&syscon_apmu CLK_APMU_CPU_C1_PLL_SRC>; + clock-names = "pll_clst0", "pll_clst1", "pll_src", "clt_pll_src"; + + opp2400000000 { + opp-hz = /bits/ 64 <2400000000>, /bits/ 64 <2400000000>; + opp-microvolt = <1000000>; + clock-latency-ns = <200000>; + }; +}; +``` + +这里最关键的字段包括: + +| 属性 | 作用 | +| :--- | :--- | +| `compatible = "operating-points-v2"` | 使用标准 OPP v2 binding | +| `opp-shared` | 表示该 OPP 表由多个 CPU 共享 | +| `opp-hz` | 目标频率 | +| `opp-microvolt` | 目标电压(如定义) | +| `clock-latency-ns` | 频点切换时延约束 | +| `clocks` / `clock-names` | OPP 表关联的 PLL / source 时钟 | + +#### 3. CPU 节点如何引用 OPP 表 + +以 `cpu_0` 为例: + +```dts +&cpu_0 { + clst-supply = <&edcdc1>; + clocks = <&syscon_apmu CLK_APMU_CPU_C0_CORE>, + <&syscon_apmu CLK_APMU_CPU_C1_CORE>; + clock-names = "cls0", "cls1"; + operating-points-v2 = <&clst_core_opp_table0_x100>; +}; +``` + +而 `cpu_8` 则引用另一套表: + +```dts +&cpu_8 { + clocks = <&syscon_apmu CLK_APMU_CPU_C2_CORE>, + <&syscon_apmu CLK_APMU_CPU_C3_CORE>; + clock-names = "cls0", "cls1"; + operating-points-v2 = <&clst_core_opp_table0_a100>; +}; +``` + +可以看到,K3 当前至少存在两组 cluster: + +- 一组带 `clst-supply = <&edcdc1>` +- 一组只给出 clock / OPP 关联 + +这也是为什么 `spacemit-k3-cpufreq.c` 里会对 `cpu >= 8` 单独处理 regulator 配置。 + +## 接口描述 + +### 用户空间常用 sysfs 路径 + +CPUFREQ 常见节点位于: + +```text +/sys/devices/system/cpu/cpufreq/policy0/ +``` + +如果系统有多组 policy,也可能看到: + +- `/sys/devices/system/cpu/cpufreq/policy0/` +- `/sys/devices/system/cpu/cpufreq/policy8/` + +具体以实际 cluster / related_cpus 划分为准。 + +常用节点如下: + +| 节点 | 作用 | +| :--- | :--- | +| `affected_cpus` / `related_cpus` | 查看该 policy 覆盖哪些 CPU | +| `scaling_driver` | 查看当前使用的调频驱动 | +| `scaling_governor` | 当前 governor | +| `scaling_available_governors` | 可用 governor 列表 | +| `scaling_available_frequencies` | 可用频点 | +| `scaling_cur_freq` | 当前逻辑频率 | +| `cpuinfo_cur_freq` | 当前硬件频率 | +| `scaling_min_freq` | 软件最小频率限制 | +| `scaling_max_freq` | 软件最大频率限制 | +| `scaling_setspeed` | userspace governor 下手动设频 | +| `cpuinfo_transition_latency` | 频率切换时延 | +| + +### 常见命令示例 + +#### 1. 查看有哪些 policy + +```shell +ls /sys/devices/system/cpu/cpufreq/ +``` + +#### 2. 查看当前驱动 + +```shell +cat /sys/devices/system/cpu/cpufreq/policy0/scaling_driver +``` + +#### 3. 查看当前 governor + +```shell +cat /sys/devices/system/cpu/cpufreq/policy0/scaling_governor +``` + +#### 4. 查看支持的频率列表 + +```shell +cat /sys/devices/system/cpu/cpufreq/policy0/scaling_available_frequencies +``` + +#### 5. 切到 userspace governor + +```shell +echo userspace > /sys/devices/system/cpu/cpufreq/policy0/scaling_governor +``` + +#### 6. 手动设置频率 + +```shell +echo 1600000 > /sys/devices/system/cpu/cpufreq/policy0/scaling_setspeed +``` + +> 这里 sysfs 通常使用的是 **kHz** 单位,因此 `1600000` 表示 `1.6 GHz`。 + +#### 7. 查看当前频率是否切换成功 + +```shell +cat /sys/devices/system/cpu/cpufreq/policy0/scaling_cur_freq +cat /sys/devices/system/cpu/cpufreq/policy0/cpuinfo_cur_freq +``` + +## Debug 介绍 + +### 1. 先看驱动是否加载成功 + +```shell +cat /sys/devices/system/cpu/cpufreq/policy0/scaling_driver +``` + +如果返回为空,或者压根没有 `policy0`,优先检查: + +- `CONFIG_CPU_FREQ` +- `CONFIG_SPACEMIT_K3_CPUFREQ` +- `CONFIG_CPUFREQ_DT` +- DTS 是否带了 `operating-points-v2` +- OPP 表是否被正确 include + +### 2. 看 policy 是怎么分组的 + +```shell +cat /sys/devices/system/cpu/cpufreq/policy0/related_cpus +cat /sys/devices/system/cpu/cpufreq/policy8/related_cpus +``` + +这一步很重要,因为 K3 大概率不是 16 个 CPU 各自独立一个 policy,而是 cluster 共享。 + +### 3. 看可用频点是否符合 OPP 表 + +```shell +cat /sys/devices/system/cpu/cpufreq/policy0/scaling_available_frequencies +``` + +如果实际频点比 DTS 里少,常见原因有: + +- 某些 OPP 没有被内核接受; +- 对应 regulator / clock 约束不满足; +- thermal / QoS / 平台限制压住了最大频率; +- cluster 当前策略未开放更高频点。 + +### 4. 用 userspace 模式做静态验证 + +```shell +echo userspace > /sys/devices/system/cpu/cpufreq/policy0/scaling_governor +echo 1200000 > /sys/devices/system/cpu/cpufreq/policy0/scaling_setspeed +cat /sys/devices/system/cpu/cpufreq/policy0/scaling_cur_freq +``` + +这个方法最适合先验证: + +- OPP 表是否生效; +- 频点切换路径是否通; +- 不是 governor 算法本身导致的“没升频”。 + +### 5. 配合负载观察 governor 行为 + +例如切到 `ondemand` 或 `schedutil` 后,用压测工具制造负载,再观察频率变化: + +```shell +cat /sys/devices/system/cpu/cpufreq/policy0/scaling_governor +watch -n 0.5 cat /sys/devices/system/cpu/cpufreq/policy0/scaling_cur_freq +``` + +## 测试介绍 + +建议按下面的顺序验证 K3 CPUFREQ: + +1. **确认 policy 节点存在** + 确认 cpufreq 驱动已经工作; +2. **确认 `scaling_driver` 正常** + 确认不是空壳 sysfs; +3. **确认 `scaling_available_frequencies`** + 对照 `k3_opp_table.dtsi`; +4. **用 userspace 逐档切频** + 先验证静态频点切换; +5. **再验证 governor 自动调频** + 观察空闲与高负载切换行为; +6. **必要时联动电源和温控一起看** + 避免把 thermal / regulator 限制误判成 cpufreq 问题。 + +## FAQ + +### 1. 为什么明明 OPP 表里有高频,sysfs 里却看不到? + +优先检查: + +- 板级 DTS 是否 include 了 `k3_opp_table.dtsi` +- CPU 节点是否正确写了 `operating-points-v2` +- 对应 cluster 的 `clst-supply` / clock 名称是否匹配驱动预期 +- 内核日志里是否有 OPP 初始化失败信息 + +### 2. 为什么写了 `scaling_setspeed` 但频率没变? + +先确认你当前 governor 是: + +```shell +userspace +``` + +如果还在 `ondemand` / `schedutil` / `performance`,那 `scaling_setspeed` 通常不会按你预期工作。 + +### 3. 为什么不同 CPU 的频率看起来一起变? + +这是正常现象。K3 当前是按 cluster / policy 共享 OPP 和调频策略,不是每个核完全独立。 + +### 4. 为什么 Kconfig 里写的是 `Spacemit K1X`,但实际上是 K3? + +这是当前 SDK 中的文案残留。判断应以这些内容为准: + +- 文件名:`spacemit-k3-cpufreq.c` +- help 文本:`Spacemit K3 SoC` +- DTS / OPP 实际内容:`k3_opp_table.dtsi` + +所以文档应按 **K3 实际平台实现** 来理解和描述。 From 5d089043d1fd4f9d9e9b89b498fa51ec4bb5d52a Mon Sep 17 00:00:00 2001 From: Troy Date: Wed, 18 Mar 2026 16:12:07 +0800 Subject: [PATCH 16/30] k3: device: peripheral_driver: Clock: initial version --- .../device/peripheral_driver/16-Clock.md | 419 ++++++++++++++++++ 1 file changed, 419 insertions(+) diff --git a/zh/k3_buildroot/device/peripheral_driver/16-Clock.md b/zh/k3_buildroot/device/peripheral_driver/16-Clock.md index 76c61c2..1bf47de 100644 --- a/zh/k3_buildroot/device/peripheral_driver/16-Clock.md +++ b/zh/k3_buildroot/device/peripheral_driver/16-Clock.md @@ -1,2 +1,421 @@ # Clock +介绍 K3 平台时钟系统的组成、驱动实现、设备树配置方式以及常见调试方法。 + +## 模块介绍 + +Clock 子系统负责给 SoC 内部各个模块提供工作时钟,并根据模块类型完成以下几类工作: + +- 从固定时钟源或 PLL 派生出不同频率; +- 为外设选择时钟父源; +- 通过 divider 调整输出频率; +- 通过 gate 控制时钟开关; +- 与 reset 控制一起,为外设提供完整的“时钟 + 复位”资源。 + +对用户来说,K3 的时钟文档最重要的不是“时钟理论”,而是下面这些问题: + +- K3 的 clock provider 在哪里; +- DTS 里的 `clocks = <&syscon_xxx ID>` 到底引用的是什么; +- 哪些模块一般要同时配 `clocks`、`clock-names`、`resets`; +- 遇到驱动 probe 失败、频率不对、模块没工作时,应该先查哪一层。 + +### 功能介绍 + +![](static/clock.png) + +Linux Common Clock Framework(CCF)通常包括: + +1. **Clock Provider** + SoC 平台侧提供时钟树、门控、分频、MUX、PLL 等实现; +2. **Clock Consumer** + 各外设驱动通过 `clk_get()` / `devm_clk_get()` 获取并使能时钟; +3. **Device Tree 描述** + DTS 用 phandle + clock ID 说明“这个设备用哪个 provider 的哪个时钟”; +4. **调试接口** + 用户通过 `clk_summary`、内核日志、consumer 节点等确认时钟是否真正启用。 + +K3 平台的时钟体系并不是单一一个 provider,而是按域拆成多组 system-controller / CCU: + +- `syscon_mpmu` +- `syscon_apmu` +- `syscon_apbc` +- `syscon_apbc2` +- `syscon_dciu` +- `syscon_rcpu_sysctrl` +- `syscon_rcpu_uartctrl` +- `syscon_rcpu_i2sctrl` +- `syscon_rcpu_spictrl` +- `syscon_rcpu_i2cctrl` +- `syscon_rpmu` +- `syscon_rcpu_pwmctrl` +- `pll` + +所以 K3 DTS 里常见的时钟引用形式是: + +```dts +clocks = <&syscon_apbc CLK_APBC_SPI0>, <&syscon_apbc CLK_APBC_SPI0_BUS>; +``` + +这表示: + +- provider 是 `syscon_apbc` +- 第一个 clock ID 是 `CLK_APBC_SPI0` +- 第二个 clock ID 是 `CLK_APBC_SPI0_BUS` + +## 源码结构介绍 + +K3 时钟相关代码主要位于: + +```text +linux-6.18/ +|-- drivers/clk/spacemit/ +| |-- ccu-k1x.c +| |-- ccu-k3.c # K3 时钟控制单元驱动 +| |-- clk-spacemit-k1x.c +| |-- reset-spacemit-k1x.c +| `-- ... +|-- include/dt-bindings/clock/ +| |-- spacemit,k1-syscon.h +| `-- spacemit,k3-syscon.h # K3 clock/reset ID 定义 +`-- arch/riscv/boot/dts/spacemit/ + |-- k3.dtsi + |-- k3-rdomain.dtsi + `-- k3*.dts +``` + +其中本轮最关键的两个文件是: + +- `drivers/clk/spacemit/ccu-k3.c` +- `include/dt-bindings/clock/spacemit,k3-syscon.h` + +### `ccu-k3.c` 做了什么 + +从当前 SDK 来看,`ccu-k3.c` 里定义了大量 K3 时钟节点,类型包括: + +- `CCU_GATE_DEFINE` +- `CCU_MUX_GATE_DEFINE` +- `CCU_MUX_DIV_GATE_FC_DEFINE` + +这意味着 K3 时钟树里常见的是: + +- gate clock +- mux + gate clock +- mux + divider + gate clock + +实际在代码里可以直接看到这些典型时钟: + +- `sdh_axi_aclk` +- `sdh0_clk` +- `sdh1_clk` +- `sdh2_clk` +- `usb2_bus_clk` +- `usb3_porta_bus_clk` +- `usb3_portb_bus_clk` +- `usb3_portc_bus_clk` +- `usb3_portd_bus_clk` +- `qspi_clk` +- `twsi0_clk ~ twsi8_clk` +- `twsi0_bus_clk ~ twsi8_bus_clk` +- `spi0_clk / spi1_clk / spi3_clk` +- `spi0_bus_clk / spi1_bus_clk / spi3_bus_clk` +- `rtc_clk / rtc_bus_clk` + +这也解释了为什么很多设备节点会同时拿到两路时钟: + +- 一路功能时钟(func/core) +- 一路总线时钟(bus/axi/apb) + +## 关键特性 + +### 特性 + +| 特性 | 特性说明 | +| :--- | :--- | +| 基于 CCF | K3 时钟系统基于 Linux Common Clock Framework | +| 多 provider 拆分 | A-domain、R-domain、PLL、PMU 等由不同 syscon/provider 提供 | +| clock/reset 紧密配对 | 多数外设 DTS 同时配置 `clocks` 与 `resets` | +| 时钟 ID 集中定义 | K3 clock/reset ID 统一放在 `spacemit,k3-syscon.h` | +| 大量使用 gate / mux / divider | 通过 `ccu-k3.c` 组合实现模块时钟树 | +| DTS 消费者写法统一 | 绝大多数设备节点按 `clocks` + `clock-names` + `resets` 模式接入 | + +### K3 常见 provider 节点 + +从 `k3.dtsi` 当前内容看,K3 主要的时钟/复位 provider 包括: + +| 节点 | 地址 | 典型用途 | +| :--- | :--- | :--- | +| `syscon_mpmu` | `0xd4050000` | MPMU 相关基础时钟/低速时钟 | +| `syscon_apmu` | `0xd4282800` | APMU 域时钟,如 QSPI、SDH、USB、CPU 等 | +| `syscon_apbc` | `0xd4015000` | APB 外设时钟,如 UART/PWM/I2C/SPI/RTC | +| `syscon_apbc2` | `0xf0610000` | 第二组 APBC / 安全域部分时钟 | +| `syscon_dciu` | `0xd8440000` | DCIU 相关控制 | +| `syscon_rcpu_sysctrl` | `0xc0880000` | R-domain 系统控制相关时钟/复位 | +| `syscon_rcpu_uartctrl` | `0xc0881f00` | R-domain UART | +| `syscon_rcpu_i2sctrl` | `0xc0882000` | R-domain I2S | +| `syscon_rcpu_spictrl` | `0xc0885f00` | R-domain SPI | +| `syscon_rcpu_i2cctrl` | `0xc0886f00` | R-domain I2C | +| `syscon_rpmu` | `0xc088c000` | R-domain PMU | +| `syscon_rcpu_pwmctrl` | `0xc088d000` | R-domain PWM | + +### K3 clock ID 的组织方式 + +`include/dt-bindings/clock/spacemit,k3-syscon.h` 里不只是一个小枚举,而是把多个域的 ID 都集中定义了,例如: + +- PLL clocks: + - `CLK_PLL1` + - `CLK_PLL2` + - `CLK_PLL3` + - `CLK_PLL4` + - `CLK_PLL1_D2` + - `CLK_PLL2_D8` + - `CLK_PLL6_80` + - ... +- MPMU clocks: + - `CLK_MPMU_APB` + - `CLK_MPMU_SLOW_UART` + - `CLK_MPMU_I2S_SYSCLK` + - ... +- APBC clocks: + - `CLK_APBC_UART0` + - `CLK_APBC_UART0_BUS` + - `CLK_APBC_PWM0` + - `CLK_APBC_PWM0_BUS` + - `CLK_APBC_TWSI0` + - `CLK_APBC_TWSI0_BUS` + - `CLK_APBC_SPI0` + - `CLK_APBC_SPI0_BUS` + - `CLK_APBC_RTC` + - `CLK_APBC_RTC_BUS` + - ... +- reset IDs: + - `RESET_MPMU_WDT` + - 以及 APBC / APMU / RCPU 各域 reset ID + +这意味着对于用户来说,**DTS 里的 clock/reset ID 不是拍脑袋写的数字**,而应该明确来自这个 dt-binding 头文件。 + +## 配置介绍 + +主要包括 **clock provider** 和 **clock consumer** 两部分。 + +### 1. provider 侧 + +K3 的 provider 通常在 `k3.dtsi` 里已经定义好,板级 DTS 一般不需要重新创建 provider,只需要作为 consumer 去引用。 + +也就是说,大多数开发工作不是“新增一个 clock controller 节点”,而是: + +- 找对 provider; +- 找对 ID; +- 填对 `clock-names`; +- 必要时再配 `assigned-clocks` / `assigned-clock-parents` / `assigned-clock-rates`。 + +### 2. consumer 侧通用写法 + +大多数 K3 外设节点写法都类似: + +```dts +xxx: device@addr { + clocks = <&provider CLK_ID0>, <&provider CLK_ID1>; + clock-names = "func", "bus"; + resets = <&provider RESET_ID>; + status = "disabled"; +}; +``` + +不同设备的 `clock-names` 会有区别,常见取值包括: + +- `func` +- `bus` +- `core` +- `gate` +- `qspi_clk` +- `qspi_bus_clk` +- `dbi` +- `mstr` +- `slv` + +因此写文档或配 DTS 时,**不要只看 clock 数量,也要看驱动里期望的 `clock-names`**。 + +## DTS 配置示例 + +### 1. SPI 控制器 + +```dts +spi0: spi@d4040000 { + compatible = "spacemit,k3-spi"; + clocks = <&syscon_apbc CLK_APBC_SPI0>, + <&syscon_apbc CLK_APBC_SPI0_BUS>; + clock-names = "func", "bus"; + resets = <&syscon_apbc RESET_APBC_SPI0>; + status = "disabled"; +}; +``` + +说明: + +- `func` 是 SPI 控制器工作时钟; +- `bus` 是 APB 总线访问时钟; +- reset 也由同一个 provider 提供。 + +### 2. QSPI 控制器 + +```dts +qspi: spi@d420c000 { + compatible = "spacemit,k3-qspi"; + clocks = <&syscon_apmu CLK_APMU_QSPI>, + <&syscon_apmu CLK_APMU_QSPI_BUS>; + clock-names = "qspi_clk", "qspi_bus_clk"; + resets = <&syscon_apmu RESET_APMU_QSPI>, + <&syscon_apmu RESET_APMU_QSPI_BUS>; + reset-names = "qspi_reset", "qspi_bus_reset"; + status = "disabled"; +}; +``` + +说明: + +- 这类高速模块通常挂在 `syscon_apmu`; +- 不仅要配 clocks,还要把 `reset-names` 对齐驱动预期。 + +### 3. R-domain UART + +```dts +r_uart0: serial@c0881000 { + compatible = "spacemit,k1-uart", "intel,xscale-uart"; + clocks = <&syscon_rcpu_uartctrl CLK_RCPU_UARTCTRL_RUART0>, + <&syscon_rcpu_uartctrl CLK_RCPU_UARTCTRL_RUART0_BUS>, + <&syscon_mpmu CLK_MPMU_SLOW_UART>; + clock-names = "core", "bus", "gate"; + resets = <&syscon_rcpu_uartctrl RESET_RCPU_UARTCTRL_RUART0>; + status = "disabled"; +}; +``` + +说明: + +- R-domain 外设经常不是两路时钟,而是三路; +- 除了 `core` 和 `bus`,还可能多一个 `gate` / slow clock; +- 所以不能把 A-domain 外设的 clock 模式简单套给 R-domain。 + +### 4. I2C 控制器 + +```dts +i2c0: i2c@d4010800 { + compatible = "spacemit,k1-i2c"; + clocks = <&syscon_apbc CLK_APBC_TWSI0>, + <&syscon_apbc CLK_APBC_TWSI0_BUS>; + clock-names = "func", "bus"; + resets = <&syscon_apbc RESET_APBC_TWSI0>; + status = "disabled"; +}; +``` + +这也是 K3 最典型的外设时钟写法之一。 + +## 使用与调试介绍 + +### 1. 优先确认 DTS 是否把时钟写对了 + +重点检查: + +- provider 对不对; +- ID 对不对; +- `clock-names` 顺序对不对; +- reset 是否也同步写了; +- 驱动里实际 `devm_clk_get()` 请求的名字是什么。 + +很多 probe 失败并不是“驱动坏了”,而是: + +- `clock-names` 和驱动期望不匹配; +- 少写一组 bus clock; +- reset 没有释放; +- provider/ID 用错域了。 + +### 2. 看内核日志里的 clock/reset 报错 + +常见失败信息包括: + +- `failed to get clk` +- `failed to enable clk` +- `failed to deassert reset` +- `probe deferred` + +如果出现 `-EPROBE_DEFER`,通常要反查: + +- provider 是否已经先注册; +- 依赖的 clock/reset 控制器驱动是否正常起来。 + +### 3. 用 debugfs 看时钟树 + +如果内核打开了 CCF 调试接口,可以查看: + +```shell +cat /sys/kernel/debug/clk/clk_summary +``` + +或者: + +```shell +grep -i spi /sys/kernel/debug/clk/clk_summary +grep -i qspi /sys/kernel/debug/clk/clk_summary +grep -i twsi /sys/kernel/debug/clk/clk_summary +grep -i uart /sys/kernel/debug/clk/clk_summary +``` + +这对确认下面这些问题很有帮助: + +- 时钟是否真的 enable; +- 父时钟是谁; +- enable count / prepare count 是否正常; +- 频率是不是你预期的值。 + +### 4. 从 consumer 视角定位问题最有效 + +调 K3 clock 时,最实用的方法通常不是先盯 provider 驱动,而是先选一个具体模块往回追: + +例如调 SPI: + +1. 看 SPI 节点的 `clocks` / `clock-names` / `resets`; +2. 看 SPI 驱动如何获取这些 clock; +3. 看 `clk_summary` 里 SPI 相关 clock 是否真正打开; +4. 再回头看 `ccu-k3.c` 里这个时钟属于哪种 gate/mux/div 结构。 + +这样比一上来就看整棵时钟树更快。 + +## FAQ + +### 1. 为什么同一个设备要配两路甚至三路时钟? + +因为设备往往不只有“功能时钟”一种,还可能需要: + +- bus / APB / AXI 访问时钟; +- 低速 gate / slow clock; +- 某个参考时钟或 parent source。 + +DTS 必须按驱动预期完整提供,不能只给一路看起来“最像主时钟”的时钟。 + +### 2. 为什么写了 clocks 设备还是不工作? + +常见原因: + +- `clock-names` 不匹配; +- reset 没释放; +- pinctrl / power-domain / regulator 还有其他依赖没满足; +- 对应 provider 还没 probe 完成。 + +所以 K3 外设 bring-up 时,clock 只是关键一环,不是唯一一环。 + +### 3. 为什么有些模块挂 `syscon_apbc`,有些却挂 `syscon_apmu`? + +因为它们处于不同时钟/电源/总线域。通常来说: + +- 低速 APB 外设更多挂在 `syscon_apbc`; +- 高速或更复杂模块更多挂在 `syscon_apmu`; +- R-domain 模块则更多挂在 `syscon_rcpu_*` 系列 provider。 + +判断时不要凭名字猜,直接看 `k3.dtsi` 和对应驱动最稳。 + +### 4. 文档里最该记住哪条经验? + +**先从 consumer 节点倒推 provider,而不是先试图背整棵时钟树。** + +对用户来说,这种方式最省时间,也最符合日常 bring-up / 调试习惯。 From dc5374e90209a9420a03de65349dc02af59bee0f Mon Sep 17 00:00:00 2001 From: Troy Date: Wed, 18 Mar 2026 16:22:58 +0800 Subject: [PATCH 17/30] k3: device: peripheral_driver: rtc: initial version --- .../device/peripheral_driver/rtc.md | 520 ++++++++++++++++++ 1 file changed, 520 insertions(+) diff --git a/zh/k3_buildroot/device/peripheral_driver/rtc.md b/zh/k3_buildroot/device/peripheral_driver/rtc.md index b17bc63..804c3b3 100644 --- a/zh/k3_buildroot/device/peripheral_driver/rtc.md +++ b/zh/k3_buildroot/device/peripheral_driver/rtc.md @@ -1,2 +1,522 @@ # RTC +本文介绍 K3 平台 RTC(实时时钟)的功能、驱动实现、DTS 配置方式以及常见使用与调试方法。 + +## 模块介绍 + +RTC(Real-Time Clock,实时时钟)主要用于: + +- 维护系统时间; +- 提供 1Hz / alarm 中断; +- 在系统掉电后,依靠备用电源或 PMIC 内部 RTC 继续保存时间; +- 在部分场景下为唤醒、闹钟、掉电后时间保持提供基础能力。 + +K3 这块不能只按“K1 那套 PMIC RTC”去理解。当前 SDK 里实际上能看到 **至少三条 RTC 路径**: + +1. SoC 内部 RTC:`rtc@d4010000` +2. RPMI RTC:`rpmi_rtc@0` +3. P1 PMIC RTC:`drivers/rtc/rtc-spacemit-p1.c` + +所以对用户来说,最重要的不是只记一个驱动名,而是先分清: + +- 你现在板子上到底启用的是哪一路 RTC; +- `/dev/rtc0` 最后绑定到的是哪个设备; +- 是否依赖 PMIC / mailbox / backup 电源; +- 你要验证的是“系统时间可读写”,还是“闹钟/唤醒/掉电保持”。 + +### 功能介绍 + +![](static/rtc.png) + +Linux RTC 子系统通常分三层: + +1. **rtc-core** + 提供 `rtc_device` 注册、标准 ioctl、sysfs 和 `/dev/rtcN` 接口; +2. **RTC 驱动层** + 实现具体硬件的读时间、写时间、闹钟、中断等; +3. **用户空间接口层** + 用户通过 `hwclock`、`date`、`ioctl()`、`/sys/class/rtc/rtcN/` 等方式访问。 + +在 K3 当前 SDK 里,这三类 RTC 来源分别对应: + +- **SoC RTC**:DTS 中 `compatible = "mrvl,mmp-rtc"` 的内部 RTC +- **RPMI RTC**:DTS 中 `compatible = "riscv,rpmi-rtc"` 的 mailbox 型 RTC +- **PMIC RTC**:`RTC_DRV_SPACEMIT_P1` 对应的 P1 PMIC RTC + +## 源码结构介绍 + +K3 RTC 相关内容主要位于: + +```text +linux-6.18/ +|-- drivers/rtc/ +| |-- class.c +| |-- dev.c +| |-- interface.c +| |-- Kconfig +| |-- Makefile +| |-- rtc-spacemit-p1.c # SpacemiT P1 PMIC RTC +| |-- rtc-rpmi.c # RPMI RTC(通用框架) +| `-- ... +|-- drivers/mfd/ +| `-- simple-mfd-i2c.c # P1 PMIC 通过 MFD 拆出 regulator / rtc 子设备 +`-- arch/riscv/boot/dts/spacemit/ + |-- k3.dtsi + `-- k3*.dts +``` + +### K3 当前可见的 RTC 节点 + +从 `k3.dtsi` 里可以直接看到两类 SoC 侧 RTC 节点: + +#### 1. SoC 内部 RTC + +```dts +rtc: rtc@d4010000 { + compatible = "mrvl,mmp-rtc"; + reg = <0x0 0xd4010000 0x0 0x100>; + interrupt-parent = <&saplic>; + interrupts = <21 IRQ_TYPE_LEVEL_HIGH>, <22 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "rtc 1Hz", "rtc alarm"; + resets = <&syscon_apbc RESET_APBC_RTC>; + status = "disabled"; +}; +``` + +#### 2. RPMI RTC + +```dts +rpmi_rtc: rpmi_rtc@0 { + compatible = "riscv,rpmi-rtc"; + mboxes = <&mpxy_mbox 0xe 0x0>; + interrupt-parent = <&saplic>; + interrupts = <64 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "rpmi rtc"; + status = "okay"; +}; +``` + +也就是说,K3 当前平台里,**并不是只有一个 RTC 节点**。 + +## 关键特性 + +### 特性 + +| 特性 | 特性说明 | +| :--- | :--- | +| 多 RTC 来源并存 | K3 当前可见 SoC RTC、RPMI RTC、P1 PMIC RTC | +| 标准 Linux RTC 接口 | 驱动注册后通过 `/dev/rtcN` 和 rtc class 提供访问 | +| SoC RTC 带 1Hz / alarm 中断 | `rtc@d4010000` DTS 中有两路中断 | +| PMIC RTC 通过 MFD 派生 | P1 PMIC 由 `simple-mfd-i2c.c` 拆出 `spacemit-p1-rtc` 子设备 | +| P1 RTC 只实现基础时间读写 | 当前 `rtc-spacemit-p1.c` 没有提供 alarm 功能 | +| clock/reset 仍然重要 | SoC 内部 RTC 依赖 `RESET_APBC_RTC`,属于 APBC 域资源 | + +### 1. SoC 内部 RTC 走 APBC 域 + +从 `k3.dtsi` 可以看到,内部 RTC 节点: + +- 地址:`0xd4010000` +- 兼容串:`mrvl,mmp-rtc` +- reset:`<&syscon_apbc RESET_APBC_RTC>` +- 中断: + - `rtc 1Hz` + - `rtc alarm` + +这说明它是 **SoC 内建 RTC 控制器**,而不是外部 I2C RTC。 + +### 2. RPMI RTC 是另一条独立路径 + +`rpmi_rtc@0` 这一路不是 MMIO RTC,而是通过: + +- `mboxes = <&mpxy_mbox 0xe 0x0>` + +来和底层固件/管理单元交互。 + +所以如果系统里最后枚举出来的主 RTC 是 RPMI 路径,那调试方法就和传统 MMIO RTC 不完全一样,重点要查: + +- mailbox 是否正常; +- RPMI 通道是否可用; +- 中断是否正常上报。 + +### 3. P1 PMIC RTC 是 MFD 子设备 + +K3 SDK 中的 P1 PMIC RTC 驱动为: + +```text +drivers/rtc/rtc-spacemit-p1.c +``` + +对应 Kconfig: + +```text +config RTC_DRV_SPACEMIT_P1 + tristate "SpacemiT P1 RTC" + depends on ARCH_SPACEMIT || COMPILE_TEST + select MFD_SPACEMIT_P1 + default ARCH_SPACEMIT +``` + +P1 不是单独一个“RTC 总线设备”,而是通过: + +```text +drivers/mfd/simple-mfd-i2c.c +``` + +里的 `simple-mfd-i2c` 先注册成 MFD,再拆出两个子设备: + +- `spacemit-p1-regulator` +- `spacemit-p1-rtc` + +对应代码里可以直接看到: + +```c +static const struct mfd_cell spacemit_p1_cells[] = { + { .name = "spacemit-p1-regulator", }, + { .name = "spacemit-p1-rtc", }, +}; +``` + +## 配置介绍 + +RTC 配置要分三种情况来看。 + +### 1. SoC 内部 RTC + +如果使用 K3 SoC 内部 RTC,核心 DTS 节点来自 `k3.dtsi`: + +```dts +rtc: rtc@d4010000 { + compatible = "mrvl,mmp-rtc"; + reg = <0x0 0xd4010000 0x0 0x100>; + interrupt-parent = <&saplic>; + interrupts = <21 IRQ_TYPE_LEVEL_HIGH>, <22 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "rtc 1Hz", "rtc alarm"; + resets = <&syscon_apbc RESET_APBC_RTC>; + status = "disabled"; +}; +``` + +如果板级要启用,通常是在 board DTS 里把它改成: + +```dts +&rtc { + status = "okay"; +}; +``` + +### 2. RPMI RTC + +RPMI RTC 在 `k3.dtsi` 当前已经是: + +```dts +status = "okay"; +``` + +如果系统实际使用这一路 RTC,需要确认: + +- `mpxy_mbox` 已正常工作; +- `riscv,rpmi-rtc` 驱动已使能; +- 中断和 mailbox 通信都正常。 + +### 3. P1 PMIC RTC + +P1 PMIC RTC 不是直接在 DTS 里声明一个 `spacemit-p1-rtc` 节点,而是先声明 P1 PMIC 本体,例如板级 DTS 里能看到: + +```dts +p1@41 { + compatible = "spacemit,p1"; + reg = <0x41>; + status = "disabled"; + ... +}; +``` + +当 `simple-mfd-i2c` 识别到: + +```dts +compatible = "spacemit,p1"; +``` + +就会在内核里自动派生出: + +- `spacemit-p1-regulator` +- `spacemit-p1-rtc` + +所以用户在 DTS 层真正要做的是: + +- 把 P1 PMIC 所在的 I2C 控制器配好; +- 把 `p1@41` 节点本身配对; +- 确保 PMIC 节点状态正确、供电关系正常; +- 然后再让 MFD 自动拆出 RTC 子设备。 + +## P1 PMIC RTC 驱动细节 + +`rtc-spacemit-p1.c` 当前实现比较克制,核心只有: + +- `read_time` +- `set_time` +- `devm_rtc_register_device` + +### 1. 只实现基础时间读写 + +代码里当前提供的 `rtc_class_ops` 为: + +```c +static const struct rtc_class_ops p1_rtc_class_ops = { + .read_time = p1_rtc_read_time, + .set_time = p1_rtc_set_time, +}; +``` + +没有看到: + +- `read_alarm` +- `set_alarm` +- `alarm_irq_enable` + +所以这一路当前更适合描述为: + +- 支持基础 RTC 时间读写 +- 不应文档化成“支持完整闹钟功能” + +### 2. P1 RTC 有硬件一致性问题,驱动做了规避 + +这点挺关键,值得写进文档。 + +驱动注释明确说明: + +- 硬件文档说读时间寄存器会锁存一致快照; +- 但实际硬件存在 bug; +- 所以驱动要循环读取,直到连续两次结果一致。 + +代码里通过: + +- `RTC_READ_TRIES = 20` +- 连续 `regmap_bulk_read()` +- 比较秒寄存器是否一致 + +来保证读出的时间快照稳定。 + +### 3. 写时间时会先关闭 RTC,再写回,再重新使能 + +驱动里还明确做了: + +- `regmap_clear_bits(... RTC_EN)` +- `regmap_bulk_write(...)` +- `regmap_set_bits(... RTC_EN)` + +原因同样是为了规避硬件实现上“不保证写入时天然锁存一致”的问题。 + +### 4. 时间范围有限 + +P1 RTC 驱动里直接设置了: + +```c +rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; +rtc->range_max = RTC_TIMESTAMP_END_2063; +``` + +因此这一路 RTC 的可表示时间范围是: + +- **2000 ~ 2063** + +文档里最好把这个限制说清,不然用户会误以为是通用 RTC 无限覆盖更大时间范围。 + +## 接口描述 + +Linux RTC 驱动注册成功后,会生成标准字符设备: + +```text +/dev/rtc0 +/dev/rtc1 +... +``` + +同时也会在: + +```text +/sys/class/rtc/ +``` + +下暴露 class 节点。 + +### 常用节点 + +例如: + +- `/sys/class/rtc/rtc0/name` +- `/sys/class/rtc/rtc0/date` +- `/sys/class/rtc/rtc0/time` +- `/sys/class/rtc/rtc0/since_epoch` +- `/sys/class/rtc/rtc0/wakealarm` + +### 常用用户态命令 + +#### 1. 查看系统中有哪些 RTC + +```shell +ls /sys/class/rtc/ +``` + +#### 2. 查看 RTC 名称 + +```shell +cat /sys/class/rtc/rtc0/name +``` + +这一步很重要,因为它能帮助你判断当前 `/dev/rtc0` 到底对应: + +- SoC RTC +- RPMI RTC +- 还是 PMIC RTC + +#### 3. 读取 RTC 时间 + +```shell +hwclock -r +``` + +或者: + +```shell +cat /sys/class/rtc/rtc0/date +cat /sys/class/rtc/rtc0/time +``` + +#### 4. 设置 RTC 时间 + +```shell +hwclock --set --date "2026-03-18 16:30:00" +hwclock -w +``` + +#### 5. 将 RTC 时间同步到系统时间 + +```shell +hwclock -s +``` + +## 测试介绍 + +### 1. 先确认系统枚举了哪个 RTC + +```shell +ls /sys/class/rtc/ +cat /sys/class/rtc/rtc0/name +``` + +如果系统里不止一个 RTC,要先分清你测的是哪一个。 + +### 2. 做基础读写验证 + +```shell +hwclock -r +hwclock --set --date "2026-03-18 16:30:00" +hwclock -r +``` + +### 3. 验证掉电保持能力 + +如果板子具备: + +- RTC backup 电源 +- 或 PMIC RTC 维持能力 + +则可以: + +1. 设置 RTC 时间; +2. 正常断主电; +3. 保留 backup 电源; +4. 重新上电后读取 RTC; +5. 确认时间是否连续增长。 + +这一步对 PMIC RTC 尤其重要。 + +### 4. 闹钟功能测试要先确认是哪一路 RTC + +不是所有 K3 RTC 路径都等价。 + +- SoC 内部 RTC 从 DTS 看有 `rtc alarm` 中断,适合进一步验证 alarm; +- P1 PMIC RTC 当前驱动代码没有实现 alarm ops,不应直接按“支持 alarm”来测; +- RPMI RTC 是否支持完整 alarm,要结合实际驱动行为和系统暴露节点确认。 + +所以别把 K1 那种“RTC 一定带完整 alarm”默认套进来。 + +## Debug 介绍 + +### 1. RTC 节点有了,但 `/dev/rtc0` 不对 + +先看: + +```shell +cat /sys/class/rtc/rtc0/name +``` + +很多 RTC 调试第一步不是“驱动有没有起来”,而是“系统把哪一路排成 rtc0 了”。 + +### 2. SoC RTC 起不来,优先检查 reset / status + +SoC 内部 RTC 当前 DTS 节点有: + +- `resets = <&syscon_apbc RESET_APBC_RTC>` +- `status = "disabled"` + +所以如果这一路没起来,先查: + +- board DTS 是否真的 `status = "okay"` +- APBC reset 是否正确释放 +- 中断号是否匹配 + +### 3. P1 PMIC RTC 起不来,先查 MFD 链路 + +因为 P1 RTC 不是直接枚举,而是从: + +- `simple-mfd-i2c` +- `compatible = "spacemit,p1"` + +这条链路里拆出来的。 + +所以调试顺序应该是: + +1. I2C 控制器是否正常; +2. `p1@41` 是否探测成功; +3. MFD 是否创建了 `spacemit-p1-rtc` 子设备; +4. 之后再看 RTC 驱动本身是否注册成功。 + +### 4. P1 RTC 读时间偶发异常,要记得硬件有一致性问题 + +这不是纯软件层面的小概率 bug。当前驱动已经明确为硬件一致性问题做了规避。 + +如果后续仍看到异常: + +- 优先怀疑底层 PMIC 通信/寄存器访问稳定性; +- 再看是否有并发访问或上电时序问题; +- 不要先入为主地把问题归因到 `hwclock` 工具本身。 + +## FAQ + +### 1. K3 到底该写内部 RTC,还是 PMIC RTC? + +这取决于你的板级方案,不是 SoC 单方面决定。 + +- 如果板级主要依赖 SoC 内部 RTC,就看 `rtc@d4010000`; +- 如果方案依赖 PMIC 保持时间,就要看 P1 PMIC RTC; +- 如果系统主时间来源走固件/管理单元,也可能最终用的是 RPMI RTC。 + +### 2. 为什么 `/dev/rtc0` 不一定是我想要的那一路? + +因为系统里可能同时存在多路 RTC,枚举顺序会影响 `rtc0/rtc1` 编号。最稳的办法始终是看: + +```shell +cat /sys/class/rtc/rtc0/name +``` + +### 3. P1 PMIC RTC 支持闹钟吗? + +就当前 `drivers/rtc/rtc-spacemit-p1.c` 来看,**没有看到 alarm 相关 rtc ops**,所以文档里不应把它写成“支持完整闹钟”。 + +### 4. 文档里最该记住哪条经验? + +**先分清当前系统实际使用的是哪一路 RTC,再谈功能和调试。** + +这件事在 K3 上尤其重要,因为这里不是单一 RTC 方案,而是 SoC / RPMI / PMIC 多条路径并存。 From a7663fe7f4c75f0095de2b7a51a2ef8220602495 Mon Sep 17 00:00:00 2001 From: Troy Date: Wed, 18 Mar 2026 16:28:49 +0800 Subject: [PATCH 18/30] k3: device: peripheral_driver: thermal: initial version --- .../device/peripheral_driver/thermal.md | 561 ++++++++++++++++++ 1 file changed, 561 insertions(+) diff --git a/zh/k3_buildroot/device/peripheral_driver/thermal.md b/zh/k3_buildroot/device/peripheral_driver/thermal.md index db48ae7..2e2d668 100644 --- a/zh/k3_buildroot/device/peripheral_driver/thermal.md +++ b/zh/k3_buildroot/device/peripheral_driver/thermal.md @@ -1,2 +1,563 @@ # Thermal +介绍 K3 平台 Thermal 的功能、驱动实现、DTS 配置方式以及常见调试与测试方法。 + +## 模块介绍 + +Thermal 是 Linux 用来做温度采集、温控策略和降温联动的一整套框架。对 K3 来说,它不是单独一个“温度传感器驱动”就结束了,而是至少包含下面几层: + +- 片上温度传感器驱动; +- thermal zone 描述; +- trip point(温控触发点); +- cooling device(降温执行者); +- thermal governor(温控策略); +- 和 cpufreq / 风扇 / 其他 cooling device 的联动。 + +从用户角度,Thermal 文档最重要的不是抽象概念,而是这几个问题: + +- K3 实际用的是哪个温度传感器驱动; +- DTS 里 thermal sensor、thermal zone、trip、cooling-maps 怎么连; +- K3 当前是靠 CPU 降频降温,还是靠风扇,还是两者都可能; +- 温度读数、trip 生效、降温动作到底怎么验证。 + +### 功能介绍 + +![](static/thermal.png) + +Linux Thermal 框架通常由五部分组成: + +1. **thermal sensor** + 提供原始温度数据; +2. **thermal zone device** + 把某个 sensor 封装成 thermal zone,提供 sysfs 节点; +3. **trip points** + 设定不同温度阈值及触发类型,如 `passive`、`critical`; +4. **cooling device** + 执行降温动作,比如 cpufreq 降频、风扇提速; +5. **thermal governor** + 决定在不同温度区间下,cooling device 应该进入什么 state。 + +K3 当前 SDK 里的 Thermal 路线很清楚: + +- 传感器驱动:`drivers/thermal/k3-thermal.c` +- DTS 传感器节点:`compatible = "spacemit,k3-tsensor"` +- thermal zone / trip / cooling-maps:在 DTS 中定义 +- cooling device:至少可见风扇类设备(如 `ctf2301`),也可和 CPU cooling 结合 + +## 源码结构介绍 + +K3 Thermal 相关代码主要位于: + +```text +linux-6.18/ +|-- drivers/thermal/ +| |-- k3-thermal.c # K3 平台温度传感器驱动 +| |-- k3-thermal.h +| |-- thermal_core.c +| |-- thermal_of.c +| |-- thermal_sysfs.c +| |-- cpufreq_cooling.c +| |-- devfreq_cooling.c +| |-- gov_step_wise.c +| |-- gov_power_allocator.c +| `-- ... +`-- arch/riscv/boot/dts/spacemit/ + |-- k3.dtsi + |-- k3_com260.dts + `-- k3*.dts +``` + +当前 K3 平台自己的 thermal 传感器驱动是: + +```text +drivers/thermal/k3-thermal.c +``` + +对应头文件: + +```text +drivers/thermal/k3-thermal.h +``` + +### `k3-thermal.c` 做了什么 + +这份驱动的核心逻辑并不复杂,但几个点很关键: + +- 从 DTS 读取 `sensor_range`; +- 从 DTS 读取 `tsensor_map`; +- 获取并使能两路时钟:`func`、`bus`; +- 获取 reset 并 `deassert`; +- 初始化传感器寄存器; +- 为每个启用的 sensor 调用 `devm_thermal_of_zone_register()`; +- 再通过 `devm_thermal_add_hwmon_sysfs()` 暴露 hwmon 节点。 + +驱动里实际可直接看到: + +- `compatible = "spacemit,k3-tsensor"` +- `devm_clk_get(dev, "func")` +- `devm_clk_get(dev, "bus")` +- `devm_reset_control_get_optional()` +- `devm_thermal_of_zone_register()` +- `devm_thermal_add_hwmon_sysfs()` + +这也说明 K3 Thermal 不是“驱动里自己硬编码几个 thermal zone”,而是: + +- 驱动只负责 sensor 采样; +- zone / trip / cooling-maps 主要由 DTS 描述。 + +## 关键特性 + +### 特性 + +| 特性 | 特性说明 | +| :--- | :--- | +| 支持最多 8 路 sensor | `MAX_SENSOR_NUMBER = 8` | +| 传感器启用可由 DTS 选择 | 通过 `sensor_range` + `tsensor_map` 控制 | +| 温度通过 raw data 转换得到 | 驱动读取寄存器后结合 offset 转成毫摄氏度 | +| 标准 thermal zone 注册方式 | 使用 `devm_thermal_of_zone_register()` | +| 支持 hwmon 导出 | 使用 `devm_thermal_add_hwmon_sysfs()` | +| 依赖 clock/reset | 节点要提供 `func` / `bus` clocks 和 reset | +| 降温动作由 DTS 决定 | trip 与 cooling-maps 不写在驱动里,而写在 DTS 里 | + +### K3 传感器节点当前配置 + +`k3.dtsi` 里的 K3 thermal 节点是: + +```dts +thermal: thermal@d4018000 { + compatible = "spacemit,k3-tsensor"; + reg = <0x0 0xd4018000 0x0 0x100>; + interrupt-parent = <&saplic>; + interrupts = <61 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "tsensor"; + clocks = <&syscon_apbc CLK_APBC_TSEN>, + <&syscon_apbc CLK_APBC_TSEN_BUS>; + clock-names = "func", "bus"; + resets = <&syscon_apbc RESET_APBC_TSEN>; + sensor_range = <0x0 0x7>; + tsensor_map = <1 0 1 1 1 1 1 1>; + temperature_offset = <274>; + #thermal-sensor-cells = <1>; + status = "okay"; +}; +``` + +这个节点里最关键的属性有: + +| 属性 | 作用 | +| :--- | :--- | +| `compatible = "spacemit,k3-tsensor"` | 绑定 K3 传感器驱动 | +| `clocks` / `clock-names` | 提供 `func` 与 `bus` 两路时钟 | +| `resets` | 提供 TSEN reset | +| `sensor_range` | 指定可枚举的 sensor 下标范围 | +| `tsensor_map` | 指定哪些 sensor 实际启用 | +| `temperature_offset` | 温度校正偏移 | +| `#thermal-sensor-cells = <1>` | thermal zone 里按 `<&thermal sensor_id>` 引用 | + +### DTS 里的 `tsensor_map` 值很关键 + +当前默认值: + +```dts +tsensor_map = <1 0 1 1 1 1 1 1>; +``` + +这表示: + +- 总范围是 sensor `0 ~ 7` +- 但不是每个 sensor 都启用 +- 第 1 号 sensor 当前是关闭的 + +驱动 probe 时会按 `sensor_range` 遍历,再根据 `tsensor_map[i]` 决定是否注册对应 thermal zone sensor。 + +所以如果某个 thermal zone 一直起不来,除了检查 zone 本身,也要反查: + +- 该 zone 用的 sensor 编号是否在 `sensor_range` 内; +- `tsensor_map` 是否真的启用了这个 sensor。 + +## 配置介绍 + +Thermal 配置主要包括两部分: + +1. **驱动使能配置** +2. **DTS thermal 节点 / thermal-zones 配置** + +### CONFIG 配置 + +K3 thermal 驱动的 Kconfig 为: + +```text +config K3_THERMAL + tristate "Spacemit K3 Thermal Support" + depends on OF && SOC_SPACEMIT + help + Enable this option if you want to have support for thermal management + controller present in Spacemit SoCs +``` + +Makefile 中可见: + +```text +obj-$(CONFIG_K3_THERMAL) += k3-thermal.o +``` + +常见还会联动这些 thermal 组件: + +- `CONFIG_THERMAL` +- `CONFIG_THERMAL_OF` +- `CONFIG_CPU_THERMAL` +- `CONFIG_THERMAL_GOV_STEP_WISE` +- `CONFIG_CPU_FREQ_THERMAL` +- `CONFIG_HWMON` + +> 具体内核配置名以实际 defconfig 为准,但 K3 平台传感器本身的核心开关就是 `CONFIG_K3_THERMAL`。 + +### DTS 配置 + +#### 1. thermal sensor 节点 + +K3 片上传感器节点已经在 `k3.dtsi` 里定义,通常板级 DTS 不需要重建,只需要正确引用它。 + +#### 2. thermal-zones 节点 + +K3 的 thermal zone 在 `k3.dtsi` 里预留了空壳: + +```dts +thermal_zones: thermal-zones { + +}; +``` + +也就是说,**真正的 zone/trip/cooling-map 往往由板级 DTS 填充**。 + +#### 3. K3 COM260 板级示例 + +从 `k3_com260.dts` 可见,板级直接补充了多组 thermal zone,例如: + +- `thermal_cluster0` +- `thermal_cluster1` +- `thermal_cluster2` +- `thermal_cluster3` + +其中 `thermal_cluster1` 的写法例如: + +```dts +thermal_cluster1 { + polling-delay = <1000>; + polling-delay-passive = <250>; + thermal-sensors = <&thermal 5>; + + trips { + thermal_cluster1_trip0: thermal_cluster1-trip0 { + temperature = <40000>; + hysteresis = <5000>; + type = "passive"; + }; + }; +}; +``` + +这说明: + +- zone 名字可以按板级方案自定义; +- `thermal-sensors = <&thermal N>` 中的 `N` 对应 K3 tsensor 的 sensor index; +- trip 的温度单位是毫摄氏度; +- `passive` 一般表示进入被动降温流程。 + +#### 4. 风扇 cooling-maps 示例 + +`k3_com260.dts` 还展示了一种很典型的 K3 用法:**温控直接映射到风扇 PWM/转速等级**。 + +例如: + +```dts +cooling-maps { + map0 { + trip = <&thermal_cluster0_trip0>; + cooling-device = <&ctf2301 0 50>; + }; + + map1 { + trip = <&thermal_cluster0_trip1>; + cooling-device = <&ctf2301 64 75>; + }; +}; +``` + +这说明 K3 当前并不只是 CPU 降频一条散热路线,板级完全可以: + +- 用风扇作 cooling device; +- 按不同 trip 配不同 cooling state 区间。 + +### Thermal 与 Cpufreq 的关系 + +虽然当前看到的 `k3_com260.dts` 示例重点是风扇 cooling,但 thermal 框架本身也完全可以和 CPU cooling 配合。 + +结合上一轮 Cpufreq 文档,用户应当理解: + +- Thermal 负责“何时该降温”; +- Cpufreq cooling 负责“怎样通过降频降温”; +- 风扇 cooling 负责“怎样通过提风量降温”; +- 最终采用哪种 cooling-device,由 DTS `cooling-maps` 决定。 + +## 接口介绍 + +### sysfs 节点 + +Thermal 相关节点通常位于: + +```text +/sys/class/thermal/ +``` + +常见包括: + +- `thermal_zone0/` +- `thermal_zone1/` +- `cooling_device0/` +- `cooling_device1/` + +每个 thermal zone 常见属性: + +- `type` +- `temp` +- `policy` +- `available_policies` +- `trip_point_*_temp` +- `trip_point_*_type` + +### hwmon 节点 + +由于驱动里调用了: + +```c +devm_thermal_add_hwmon_sysfs() +``` + +因此很多情况下也能在: + +```text +/sys/class/hwmon/ +``` + +下看到对应温度输入节点。 + +## 使用与调试介绍 + +### 1. 先看系统注册了哪些 thermal zone + +```shell +ls /sys/class/thermal/ +``` + +### 2. 看各 zone 名称和温度 + +```shell +cat /sys/class/thermal/thermal_zone0/type +cat /sys/class/thermal/thermal_zone0/temp +``` + +可以循环查看: + +```shell +for z in /sys/class/thermal/thermal_zone*; do + echo "== $z ==" + cat $z/type + cat $z/temp +done +``` + +### 3. 看 cooling device + +```shell +for c in /sys/class/thermal/cooling_device*; do + echo "== $c ==" + cat $c/type +done +``` + +这一步能帮助你确认当前系统挂上的 cooling device 到底是: + +- CPU frequency +- 风扇 +- 还是其他设备 + +### 4. 看 trip 点是否和 DTS 一致 + +```shell +cat /sys/class/thermal/thermal_zone0/trip_point_0_temp +cat /sys/class/thermal/thermal_zone0/trip_point_0_type +``` + +如果实际 sysfs 里的 trip 和 DTS 不一致,优先检查: + +- 板级 DTS 是否真的生效; +- 当前 thermal zone 是否来自你以为的那个 sensor; +- 编译进去的是不是对应板子的 dtb。 + +### 5. 联动 cpufreq 一起观察 + +如果 cooling-device 是 CPU 降频,建议同时观察: + +```shell +watch -n 0.5 cat /sys/class/thermal/thermal_zone0/temp +watch -n 0.5 cat /sys/devices/system/cpu/cpufreq/policy0/scaling_cur_freq +``` + +这样能直接看到: + +- 温度是否在升; +- 到达 trip 后是否开始降频; +- thermal 与 cpufreq 的联动是否真的发生。 + +### 6. 如果是风扇 cooling,就看风扇 state + +如果板级像 `k3_com260.dts` 一样把 `ctf2301` 挂进 cooling-map,那调试重点就不是 cpufreq,而是: + +- 风扇设备是否 probe 成功; +- thermal trip 到来后 cooling state 是否变化; +- PWM / 风扇转速是否真的提升。 + +## 测试介绍 + +建议按下面顺序测试 K3 Thermal: + +1. **确认 thermal 驱动已注册** + 看 `/sys/class/thermal/thermal_zone*` 是否存在; +2. **确认温度读数正常** + 读取 `temp`,确认不是固定值或异常值; +3. **确认 trip 点正确** + 核对 sysfs 与 DTS; +4. **确认 cooling device 枚举正确** + 看 `cooling_device*` 类型; +5. **制造温升场景** + 运行高负载程序或外部加热; +6. **观察 cooling 动作** + 看是否降频、升风扇、或进入其他 cooling state; +7. **必要时观察恢复行为** + 温度下降后 state 是否恢复。 + +### 简单测试命令 + +#### 查看温度 + +```shell +cat /sys/class/thermal/thermal_zone0/temp +``` + +#### 查看 CPU 当前频率 + +```shell +cat /sys/devices/system/cpu/cpufreq/policy0/scaling_cur_freq +``` + +#### 持续观察温度变化 + +```shell +watch -n 0.5 cat /sys/class/thermal/thermal_zone0/temp +``` + +## Debug 介绍 + +### 1. 没有 thermal zone + +优先检查: + +- `CONFIG_K3_THERMAL` 是否打开; +- `thermal@d4018000` 节点是否存在且 `status = "okay"`; +- `clocks` / `clock-names` / `resets` 是否正确; +- DTS 中是否真的定义了 `thermal-zones`。 + +因为 K3 驱动只是注册 sensor,**如果 DTS 没写 thermal zone,用户空间就看不到完整 thermal 策略结构**。 + +### 2. 某个 zone 没有温度 + +先反查: + +- `thermal-sensors = <&thermal N>` 的 `N` 是否越界; +- `N` 是否被 `tsensor_map` 启用; +- `sensor_range` 是否覆盖了这个编号。 + +K3 这里不是所有 sensor 都默认有效,这点非常关键。 + +### 3. 温度值明显不对 + +当前 K3 驱动里温度转换流程是: + +- 读取 `REG_TSEN_LITE_TEMP_DATA` +- `& BITS_TEMP_DATA` +- `/ TEMP_RAW_DATA_DIV` +- `- temp_offset` +- `* 1000` + +而 DTS 默认又给了: + +```dts +temperature_offset = <274>; +``` + +所以如果温度异常偏高或偏低,要优先检查: + +- `temperature_offset` 是否被改错; +- 原始寄存器读数是否稳定; +- 板级传感器校准是否匹配当前芯片。 + +### 4. trip 到了但没有降温动作 + +先分两种情况: + +- zone 本身有没有更新; +- cooling device 有没有挂进去。 + +重点检查: + +- `cooling-maps` 是否存在; +- `cooling-device = <...>` 是否引用对了; +- 风扇或 cpufreq cooling device 是否已经注册; +- 当前 governor / cooling state 是否真的允许动作。 + +### 5. 不要把 thermal 问题都归到 cpufreq 上 + +这点很常见。 + +在 K3 上,thermal 可能驱动的是: + +- CPU 降频 +- 风扇提速 +- 或两者结合 + +所以看到“温度高了但没降频”,不一定是 thermal 坏了,也可能是: + +- 当前 cooling-map 根本没绑 CPU; +- 板级方案主要靠风扇; +- 实际触发的是别的 cooling device。 + +## FAQ + +### 1. K3 当前 thermal 的核心驱动是哪一个? + +当前平台自己的片上传感器驱动是: + +```text +drivers/thermal/k3-thermal.c +``` + +对应 DTS compatible 为: + +```text +spacemit,k3-tsensor +``` + +### 2. K3 的 thermal zone 是驱动里写死的吗? + +不是。驱动负责 sensor 注册,zone / trip / cooling-map 主要由 DTS 描述。 + +### 3. 为什么有的板子是降频,有的板子是拉风扇? + +因为 cooling-device 是板级策略的一部分,由 `cooling-maps` 决定,不是 K3 thermal 驱动强行规定的。 + +### 4. 文档里最该记住哪条经验? + +**先看 DTS 里的 `thermal-sensors`、`trips`、`cooling-maps`,再看驱动。** + +因为 K3 这套 thermal 机制里,策略大头在 DTS,不在 `k3-thermal.c` 本身。 From 2a5411537b6f697f0ab4c1aad69caeb9a3835597 Mon Sep 17 00:00:00 2001 From: Troy Date: Wed, 18 Mar 2026 16:33:47 +0800 Subject: [PATCH 19/30] k3: device: peripheral_driver: DMA: initial version --- .../device/peripheral_driver/21-DMA.md | 542 ++++++++++++++++++ 1 file changed, 542 insertions(+) diff --git a/zh/k3_buildroot/device/peripheral_driver/21-DMA.md b/zh/k3_buildroot/device/peripheral_driver/21-DMA.md index f93b5e5..7a3b527 100644 --- a/zh/k3_buildroot/device/peripheral_driver/21-DMA.md +++ b/zh/k3_buildroot/device/peripheral_driver/21-DMA.md @@ -1,2 +1,544 @@ # DMA +介绍 K3 平台 DMA 的组成、驱动实现、DTS 配置方式、外设侧接入方法以及常见调试思路。 + +## 模块介绍 + +DMA(Direct Memory Access)用于在外设与内存、内存与内存之间搬运数据,尽量减少 CPU 逐字节参与,提高吞吐并降低中断负担。 + +对 K3 来说,DMA 不是一个单点能力,而是分成了几类: + +- 面向通用外设的 PDMA; +- DTS 中的 `udma` 占位/控制节点; +- 面向 AI/专用加速域的 `spacemit-ai-dmac`; +- 外设作为 DMA consumer,通过 `dmas` / `dma-names` 接入。 + +用户最常遇到的问题其实是这些: + +- K3 通用外设到底挂的是哪路 DMA; +- `dmas = <&pdma ...>` 里的参数表示什么; +- 一个设备为什么要同时写 `dmas` 和 `dma-names`; +- DMA 没生效时,是驱动没开、DTS 没配、还是 consumer 退回了 PIO。 + +### 功能介绍 + +![](static/dma.png) + +Linux DMAEngine 体系通常包括: + +1. **DMA controller driver** + 提供 channel 分配、描述符提交、启动/停止、中断处理; +2. **DMA client / consumer** + 各外设驱动通过 DMAEngine API 申请通道并发起搬运; +3. **DTS 绑定关系** + 用 `dmas` / `dma-names` 把 consumer 和 controller 连起来; +4. **用户调试接口** + 通过 dmesg、驱动日志、功能表现判断是否真正进入 DMA 路径。 + +K3 当前 SDK 里,**最核心、最通用的一条 DMA 路线是 PDMA**: + +- controller 节点:`pdma@d4000000` +- compatible:`spacemit,k1-pdma` +- 驱动:`drivers/dma/mmp_pdma_spacemit.c` + +也就是说,虽然平台是 K3,但通用外设 DMA 这条链路仍然复用了 `k1-pdma` 这套 binding / driver 命名。 + +## 源码结构介绍 + +K3 DMA 相关代码主要位于: + +```text +linux-6.18/ +|-- drivers/dma/ +| |-- dmaengine.c +| |-- virt-dma.c +| |-- mmp_pdma_spacemit.c # SpacemiT PDMA 主驱动 +| |-- udma.c # UDMA 相关实现 +| |-- k3dma.c # 其他通用 DMA 组件(内核自带) +| `-- dmatest.c # DMAEngine 测试模块 +|-- Documentation/devicetree/bindings/dma/ +| |-- spacemit,k1-pdma.yaml # K3 当前实际用到的 PDMA binding +| `-- k3dma.txt # 内核通用 k3dma binding(非 SpacemiT 这条主线) +`-- arch/riscv/boot/dts/spacemit/ + |-- k3.dtsi + `-- k3*.dts +``` + +本轮和 K3 最相关的几个对象是: + +- `drivers/dma/mmp_pdma_spacemit.c` +- `Documentation/devicetree/bindings/dma/spacemit,k1-pdma.yaml` +- `arch/riscv/boot/dts/spacemit/k3.dtsi` + +### `mmp_pdma_spacemit.c` 做了什么 + +从源码可见,这个驱动实现的是一套标准 DMAEngine controller,支持: + +- `DMA_SLAVE` +- `DMA_MEMCPY` +- `DMA_CYCLIC` +- `DMA_PRIVATE` + +驱动里直接可以看到: + +```c +dma_cap_set(DMA_SLAVE, pdev->device.cap_mask); +dma_cap_set(DMA_MEMCPY, pdev->device.cap_mask); +dma_cap_set(DMA_CYCLIC, pdev->device.cap_mask); +dma_cap_set(DMA_PRIVATE, pdev->device.cap_mask); +``` + +还实现了这些典型接口: + +- `device_prep_memcpy` +- `device_prep_slave_sg` +- `device_prep_dma_cyclic` +- `device_issue_pending` +- `device_pause` +- `device_terminate_all` +- `device_tx_status` + +这说明它不是只给某一个外设专用,而是标准的 **通用 DMA controller**。 + +## 关键特性 + +### 特性 + +| 特性 | 特性说明 | +| :--- | :--- | +| 通用外设 DMA 主线是 PDMA | K3 当前 DTS 中大量外设都挂 `&pdma` | +| 兼容串仍是 `spacemit,k1-pdma` | K3 通用 PDMA 复用 K1 命名体系 | +| 支持 DMAEngine 标准能力 | `DMA_SLAVE` / `DMA_MEMCPY` / `DMA_CYCLIC` | +| 通道数可配 | DTS 通过 `#dma-channels` 指定,当前默认 16 | +| consumer 通过 request number 接入 | `#dma-cells = <1>`,参数表示外设 DMA request number | +| 支持保留通道 | 驱动可解析 `reserved-channels` | +| 支持 `max-burst-size` | DTS 可控制 burst 配置 | +| 存在专用 AI DMAC | `spacemit-ai-dmac` 面向 AI/DCIU 域,不等同于通用 PDMA | + +### K3 当前通用 PDMA 节点 + +`k3.dtsi` 里可见两个 PDMA 节点: + +#### 1. 主 PDMA + +```dts +pdma: pdma@d4000000 { + compatible = "spacemit,k1-pdma"; + reg = <0x0 0xd4000000 0x0 0x4000>; + interrupt-parent = <&saplic>; + interrupts = <72 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&syscon_apmu CLK_APMU_DMA>; + resets = <&syscon_apmu RESET_APMU_DMA>; + #dma-cells = <1>; + #dma-channels = <16>; + max-burst-size = <64>; + status = "okay"; +}; +``` + +#### 2. 第二个 PDMA 控制器 + +```dts +pdma1: pdma@f0600000 { + compatible = "spacemit,k1-pdma"; + reg = <0x0 0xf0600000 0x0 0x4000>; + interrupt-parent = <&saplic>; + interrupts = <150 IRQ_TYPE_LEVEL_HIGH>; + #dma-cells = <1>; + #dma-channels = <16>; + max-burst-size = <64>; + status = "disabled"; +}; +``` + +所以从当前默认 DTS 来看: + +- 主用的是 `pdma` +- `pdma1` 目前是关闭的 + +### `#dma-cells = <1>` 的含义 + +`spacemit,k1-pdma.yaml` 里已经写明: + +```yaml +'#dma-cells': + const: 1 + description: + The DMA request number for the peripheral device. +``` + +也就是说,consumer 侧这样写: + +```dts +dmas = <&pdma DMA_SSP0_TX &pdma DMA_SSP0_RX>; +``` + +其中 `DMA_SSP0_TX` / `DMA_SSP0_RX` 不是 channel index,而是 **外设 DMA request number**。 + +这点很重要,别把它误认为“直接选第几个物理 channel”。 + +## 配置介绍 + +### CONFIG 配置 + +K3 通用 PDMA 驱动实际文件是: + +```text +drivers/dma/mmp_pdma_spacemit.c +``` + +虽然本轮没有专门展开对应 Kconfig 片段,但从源码功能与 DTS 绑定关系看,K3 通用 DMA 文档当前重点应该放在: + +- `mmp_pdma_spacemit.c` 的实际实现 +- `spacemit,k1-pdma.yaml` 的 DTS 约束 +- 各外设 consumer 的接法 + +实际编译时,一般还会涉及: + +- `CONFIG_DMADEVICES` +- `CONFIG_DMA_ENGINE` +- SpacemiT PDMA 对应选项 +- `CONFIG_DMATEST`(如果需要做 DMAEngine 测试) + +> 这里不建议在文档里硬填未核实的具体 Kconfig 名称,优先以驱动、binding 和 DTS 为准。 + +### DTS 配置 + +#### 1. controller 侧 + +PDMA 控制器节点核心字段包括: + +| 属性 | 作用 | +| :--- | :--- | +| `compatible = "spacemit,k1-pdma"` | 绑定 PDMA 控制器驱动 | +| `reg` | 控制器寄存器地址 | +| `interrupts` | DMA 中断 | +| `clocks` | DMA 控制器时钟 | +| `resets` | DMA 控制器 reset | +| `#dma-cells = <1>` | consumer 参数个数 | +| `#dma-channels = <16>` | 可用 channel 数 | +| `max-burst-size = <64>` | 最大 burst 大小 | + +#### 2. consumer 侧通用写法 + +外设一般这样接 DMA: + +```dts +xxx: device@addr { + dmas = <&pdma REQ_TX>, <&pdma REQ_RX>; + dma-names = "tx", "rx"; +}; +``` + +或者只有单向通道时: + +```dts +xxx: device@addr { + dmas = <&pdma REQ_TX>; + dma-names = "tx-dma"; +}; +``` + +### 典型 K3 consumer 示例 + +#### 1. SPI + +```dts +spi0: spi@d4040000 { + dmas = <&pdma DMA_SSP0_TX + &pdma DMA_SSP0_RX>; + dma-names = "tx", "rx"; +}; +``` + +`spi1`、`spi3` 也是同样模式。 + +而 `spi2` 当前 DTS 里相关 DMA 配置还是注释状态: + +```dts +// dmas = <&pdma 19 +// &pdma 20>; +// dma-names = "rx", "tx"; +``` + +这类细节就很适合写进文档:**不是所有控制器默认都已经开 DMA**。 + +#### 2. QSPI + +K3 QSPI 的用法更特殊: + +```dts +qspi: spi@d420c000 { + spacemit,qspi-tx-dma = <1>; + spacemit,qspi-rx-dma = <1>; + dmas = <&pdma 45>; + dma-names = "tx-dma"; +}; +``` + +它不仅有标准 `dmas` / `dma-names`,还额外用了平台私有属性: + +- `spacemit,qspi-tx-dma` +- `spacemit,qspi-rx-dma` + +而且从当前 DTS 看只给了一路: + +- `dmas = <&pdma 45>` +- `dma-names = "tx-dma"` + +所以 QSPI 这条链路不能简单按 SPI/I2S 那种双向通道模板去理解,必须按它自己的驱动逻辑看。 + +#### 3. I2S / Audio + +K3 的多个 I2S 节点都挂了 PDMA,例如: + +```dts +i2s1: i2s1@d4026800 { + dmas = <&pdma 24>, <&pdma 23>; + dma-names = "rx", "tx"; +}; +``` + +类似的还有: + +- `v2d` +- `i2s2` +- `i2s3` +- `i2s4` +- `i2s5` + +这说明 PDMA 在 K3 上不仅服务 SPI/UART 一类低速外设,也广泛参与音频/多媒体数据搬运。 + +## AI DMAC 说明 + +除了通用 PDMA,`k3.dtsi` 里还定义了 8 路 AI DMAC: + +- `ai_dmac0 ~ ai_dmac7` + +它们的节点形态例如: + +```dts +ai_dmac0: hdma0@d8804000 { + compatible = "spacemit-ai-dmac"; + reg = <0x0 0xd8804000 0x0 0x1000>; + interrupts = <7 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&syscon_dciu CLK_DCIU_HDMA>; + resets = <&syscon_dciu RESET_DCIU_HDMA>, + <&syscon_dciu RESET_DCIU_AXIDMA0>; + reset-names = "bus", "dma"; + dma-coherent; + status = "okay"; +}; +``` + +从这些节点可以确定: + +- 它们位于 DCIU / AI 相关域; +- 有独立中断、clock、双 reset; +- 标了 `dma-coherent`; +- 它们和通用 `pdma` 不是同一种控制器。 + +所以写业务文档时,**不要把 AI DMAC 和通用外设 PDMA 混为一谈**。 + +## 驱动实现细节 + +### 1. PDMA 支持的传输类型 + +`mmp_pdma_spacemit.c` 当前明确支持: + +- slave scatter-gather +- memcpy +- cyclic DMA + +其中 cyclic 对音频特别重要。 + +### 2. 支持 1/2/4 字节总线宽度 + +驱动里配置了: + +```c +DMA_SLAVE_BUSWIDTH_1_BYTE | +DMA_SLAVE_BUSWIDTH_2_BYTES | +DMA_SLAVE_BUSWIDTH_4_BYTES; +``` + +### 3. 支持 burst 配置 + +驱动里根据 `max_burst_size` 和 slave config 转成: + +- `DCMD_BURST8` +- `DCMD_BURST16` +- `DCMD_BURST32` +- `DCMD_BURST64` + +并且 DTS controller 节点允许配置: + +```dts +max-burst-size = <64>; +``` + +### 4. 支持 reserved channel + +驱动会解析: + +```dts +reserved-channels +``` + +把某些通道和 request number 预留出来。 + +这对于一些平台私有固定用途、或者不希望被通用分配逻辑抢走的场景很重要。 + +### 5. 支持 64-bit DMA 地址 + +源码里可以看到: + +```c +#ifdef CONFIG_SPACEMIT_PDMA_SUPPORT_64BIT + dma_set_mask(pdev->dev, DMA_BIT_MASK(64)); +#endif +``` + +同时描述符结构也扩展了高 32 位地址字段。 + +所以如果平台开启了这项支持,PDMA 可以覆盖 64-bit 地址空间场景。 + +## 使用与调试介绍 + +### 1. 先判断外设是否真的走了 DMA + +这一步最重要。 + +很多外设即使 DTS 写了 `dmas`,驱动也可能因为某些条件退回 PIO。判断方法通常是: + +- 看 dmesg 是否打印申请 DMA channel 成功/失败; +- 看吞吐和 CPU 占用是否符合 DMA 特征; +- 必要时在 consumer 驱动里确认 `dma_request_chan()` 是否成功。 + +### 2. 优先检查 DTS 三件套 + +DMA consumer 侧先检查: + +- `dmas` +- `dma-names` +- 对应 controller 节点是否 `status = "okay"` + +这一步在 K3 上很有效,因为大量问题都不是 controller 驱动本身坏了,而是 consumer 接法不匹配。 + +### 3. 检查 `dma-names` 是否和驱动预期一致 + +例如: + +- SPI 常见是 `"tx", "rx"` +- QSPI 当前用的是 `"tx-dma"` + +名字不对,consumer 驱动就可能拿不到通道。 + +### 4. QSPI 这类带私有 DMA 属性的设备要单独看 + +K3 QSPI 不是简单一套通用模板,它额外还要看: + +- `spacemit,qspi-tx-dma` +- `spacemit,qspi-rx-dma` + +所以如果 QSPI 没走 DMA,不要只盯 `dmas`,还要把私有属性一起核对。 + +### 5. 音频类优先看 cyclic DMA 是否跑起来 + +I2S / PCM 场景里,真正关键的不只是“有没有 DMA”,而是: + +- 有没有进入 cyclic DMA 路径; +- period 中断是否稳定; +- 是否出现 underrun / overrun。 + +因为音频类 DMA 和简单 memcpy / SPI 搬运的关注点不一样。 + +### 6. controller 侧排查项 + +如果怀疑 PDMA 控制器本身没起来,优先检查: + +- `pdma@d4000000` 是否 `status = "okay"` +- `clocks = <&syscon_apmu CLK_APMU_DMA>` 是否有效 +- `resets = <&syscon_apmu RESET_APMU_DMA>` 是否释放 +- 中断是否注册成功 + +## 测试介绍 + +### 1. 基础存在性检查 + +先看设备树和启动日志,确认: + +- `pdma` 已经 probe; +- 目标 consumer 没有报 `failed to request dma` 一类错误。 + +### 2. 外设功能压测 + +对用户来说,最实用的 DMA 测试往往不是直接测 controller,而是测外设场景: + +- SPI 大包收发 +- I2S 长时间播放/录音 +- QSPI 大块读写 + +观察: + +- 吞吐 +- CPU 占用 +- 是否丢包 / underrun / overrun + +### 3. DMAEngine 通用测试 + +如果内核使能了 `dmatest`,也可以用 `drivers/dma/dmatest.c` 做 DMAEngine 层验证。 + +不过对 K3 文档来说,更建议把它作为“补充测试手段”,而不是唯一依据,因为实际用户更关心的是**外设链路里的 DMA 是否真正可用**。 + +## Debug 介绍 + +### 1. consumer 明明配了 `dmas`,为什么还是像 PIO? + +常见原因: + +- `dma-names` 不匹配驱动预期; +- request number 配错; +- controller 没起来; +- 该外设驱动本身在某些传输模式下就不会走 DMA; +- 通道申请失败后 silently fallback 到 PIO。 + +### 2. 为什么 `dmas = <&pdma N>` 里的 `N` 不是物理通道号? + +因为根据 binding,`#dma-cells = <1>` 传的是 **DMA request number**,不是“第 N 个 channel”。 + +channel 最终由 controller 驱动按映射和分配逻辑处理。 + +### 3. 为什么 `spi2` 没用 DMA,但 `spi0/1/3` 用了? + +因为当前 DTS 就是这么写的。`spi2` 的 DMA 配置在 `k3.dtsi` 里仍然是注释状态,说明它当前默认并没有按其它 SPI 控制器那样接通 DMA。 + +### 4. 为什么 K3 文档里要同时提 PDMA 和 AI DMAC? + +因为它们确实都存在,但用途不同: + +- PDMA:通用外设 DMA 主线 +- AI DMAC:AI/DCIU 专用域 DMA + +如果不分开写,后面用户看 DTS 很容易混淆。 + +## FAQ + +### 1. K3 当前最常见的 DMA controller 是哪个? + +是: + +```text +pdma@d4000000 +compatible = "spacemit,k1-pdma" +``` + +### 2. 为什么 K3 上还是 `k1-pdma`? + +这是当前 SDK 的实际复用关系。和前面的 I2C 一样,**平台是 K3,但部分通用 IP 仍沿用 K1 的 compatible / binding / driver 命名**。 + +### 3. DMA 文档里最该记住哪条经验? + +**先从 consumer 节点看 `dmas` / `dma-names`,不要一上来就只盯 controller 驱动。** + +因为 K3 上 DMA 问题最常见的入口,不是 controller 坏了,而是 consumer 接法和驱动预期没对齐。 From 4c25a76ac2be8fba2e7169987ae91326434b2d7c Mon Sep 17 00:00:00 2001 From: Troy Date: Wed, 18 Mar 2026 16:56:35 +0800 Subject: [PATCH 20/30] k3: device: peripheral_driver: WDT: initial version --- .../device/peripheral_driver/23-WDT.md | 496 ++++++++++++++++++ 1 file changed, 496 insertions(+) diff --git a/zh/k3_buildroot/device/peripheral_driver/23-WDT.md b/zh/k3_buildroot/device/peripheral_driver/23-WDT.md index 1eae435..c13c13c 100644 --- a/zh/k3_buildroot/device/peripheral_driver/23-WDT.md +++ b/zh/k3_buildroot/device/peripheral_driver/23-WDT.md @@ -1,2 +1,498 @@ # WDT +介绍 K3 平台 Watchdog(WDT)的驱动实现、DTS 配置方式、用户态接口以及常见调试方法。 + +## 模块介绍 + +Watchdog(看门狗)用于在系统软件失去响应、长期不喂狗或重启流程异常时,强制触发复位,避免设备永久卡死。 + +对用户来说,WDT 文档最重要的不是“看门狗概念”,而是下面这些问题: + +- K3 实际使用的是哪份 watchdog 驱动; +- DTS 里怎么配置时钟、reset、寄存器和私有属性; +- 默认是自动喂狗、手动喂狗,还是仅注册重启处理器; +- 怎么判断一次重启是不是 WDT 触发的; +- 什么时候会用它做 restart handler。 + +### 功能介绍 + +![](static/watchdog.png) + +Linux watchdog 子系统通常包括: + +1. **watchdog core** + 提供 `/dev/watchdog`、标准 ioctl 和超时管理; +2. **platform watchdog driver** + 负责具体硬件的启动、停止、喂狗、超时和复位控制; +3. **board DTS 配置** + 描述寄存器、时钟、reset、中断和平台私有控制; +4. **重启联动** + 通过 restart handler 将 watchdog 作为系统重启路径的一部分。 + +K3 当前 SDK 里的实际 watchdog 主线是: + +- DTS compatible:`"spacemit-k1,wdt"` +- 驱动文件:`drivers/watchdog/spacemit-k1-wdt.c` +- Kconfig:`CONFIG_SPACEMIT_K1_WATCHDOG` + +也就是说,和 I2C / DMA 一样,**平台虽然是 K3,但 watchdog 这条通用 IP 仍沿用了 K1 的命名体系**。 + +## 源码结构介绍 + +K3 WDT 相关代码主要位于: + +```text +linux-6.18/ +|-- drivers/watchdog/ +| |-- watchdog_dev.c +| |-- watchdog_core.c +| |-- spacemit-k1-wdt.c # K3 当前实际使用的 WDT 驱动 +| |-- Kconfig +| `-- Makefile +`-- arch/riscv/boot/dts/spacemit/ + `-- k3.dtsi +``` + +本轮最关键的几个对象是: + +- `drivers/watchdog/spacemit-k1-wdt.c` +- `drivers/watchdog/Kconfig` +- `arch/riscv/boot/dts/spacemit/k3.dtsi` + +### `spacemit-k1-wdt.c` 做了什么 + +这份驱动实现的核心能力包括: + +- `start` +- `stop` +- `ping` +- `set_timeout` +- restart handler +- 通过 hrtimer 自动喂狗 +- 启动时检查上次是否由 WDT 复位引起 + +源码中可以直接看到: + +- `spa_wdt_start` +- `spa_wdt_stop` +- `spa_wdt_ping` +- `spa_wdt_set_timeout` +- `spa_wdt_restart_handler` +- `spa_wdt_feed` + +这说明它不是一个最小实现,而是把: + +- 正常 watchdog 运行 +- 自动续命 +- 系统重启托管 +- 上次复位原因判断 + +都串起来了。 + +## 关键特性 + +### 特性 + +| 特性 | 特性说明 | +| :--- | :--- | +| K3 当前复用 K1 WDT IP | DTS compatible 和驱动文件都沿用 K1 命名 | +| 支持标准 watchdog 核心接口 | `start` / `stop` / `ping` / `set_timeout` | +| 支持 restart handler | 可把 WDT 作为系统重启通路 | +| 支持自动喂狗 | 驱动内部用 hrtimer 周期性 `ping` | +| 可判断是否由 WDT 复位启动 | 通过状态寄存器读取复位原因 | +| 依赖 clock + reset | `clk` / `clk-bus` + `RESET_APBC_TIMERS0` | +| 含平台私有 DTS 属性 | `spa,wdt-disabled`、`spa,wdt-enable-restart-handler` | + +### 默认时间参数 + +驱动里直接定义了几组关键时间值: + +- `CONFIG_SPACEMIT_WATCHDOG_DEFAULT_TIME = 60` +- `SPACEMIT_WATCHDOG_MAX_TIMEOUT = 255` +- `SPACEMIT_WATCHDOG_EXPIRE_TIME = 100` +- `SPACEMIT_WATCHDOG_FEED_TIMEOUT = 30` + +其中: + +- watchdog 硬件计数频率按驱动注释是 `256Hz` +- `set_timeout()` 最终通过 `timeout << 8` 转成 tick +- `start()` 里默认会先把超时设置成 `100s` +- 自动喂狗周期是 `30s` + +所以从当前实现看,这个驱动更像是“平台侧自己管理一只长超时 watchdog”,而不是只把 `/dev/watchdog` 暴露给用户后完全不管。 + +## 配置介绍 + +### CONFIG 配置 + +Kconfig 中的开关为: + +```text +config SPACEMIT_K1_WATCHDOG + tristate "Spacemit k1 Watchdog" + depends on SOC_SPACEMIT + select WATCHDOG_CORE + help + Spacemit k1 SoC Watchdog timer. This will reboot your system when + the timeout is reached. +``` + +Makefile 对应: + +```text +obj-$(CONFIG_SPACEMIT_K1_WATCHDOG) += spacemit-k1-wdt.o +``` + +也就是说,K3 当前 watchdog 这条主线真正要开的就是: + +- `CONFIG_SPACEMIT_K1_WATCHDOG` + +### DTS 配置 + +K3 当前 watchdog 节点位于 `k3.dtsi`: + +```dts +watchdog: watchdog@d4014000 { + compatible = "spacemit-k1,wdt"; + clocks = <&syscon_apbc CLK_APBC_TIMERS0>, + <&syscon_apbc CLK_APBC_TIMERS0_BUS>; + clock-names = "clk", "clk-bus"; + resets = <&syscon_apbc RESET_APBC_TIMERS0>; + reg = <0x0 0xd4014000 0x0 0xff>, + <0x0 0xd4050000 0x0 0x1024>; + interrupts = <35 4>; + interrupt-parent = <&saplic>; + spa,wdt-disabled; + spa,wdt-enable-restart-handler; + status = "okay"; +}; +``` + +这个节点里最关键的属性包括: + +| 属性 | 作用 | +| :--- | :--- | +| `compatible = "spacemit-k1,wdt"` | 绑定 SpacemiT watchdog 驱动 | +| `clocks` | 提供 watchdog 核心时钟和 bus 时钟 | +| `clock-names = "clk", "clk-bus"` | 名字必须与驱动 `devm_clk_get()` 一致 | +| `resets = <&syscon_apbc RESET_APBC_TIMERS0>` | 提供 reset | +| `reg` | 第一段是 WDT 本体寄存器,第二段是 MPMU 相关寄存器 | +| `spa,wdt-disabled` | 平台私有属性,控制默认行为 | +| `spa,wdt-enable-restart-handler` | 平台私有属性,允许注册 restart handler | +| `status = "okay"` | 启用节点 | + +### 两段 `reg` 的含义 + +这点比较关键,K3 当前 watchdog 节点不是单段寄存器: + +```dts +reg = <0x0 0xd4014000 0x0 0xff>, + <0x0 0xd4050000 0x0 0x1024>; +``` + +结合驱动 probe 可以看出: + +- 第 0 段资源映射到 `wdt_base` +- 第 1 段资源映射到 `mpmu_base` + +后者用于处理: + +- `MPMU_APRR` +- `MPMU_ARSR` +- reboot command / restart path + +所以这不是“多写了一段无关地址”,而是驱动真的会同时访问 watchdog block 和 MPMU block。 + +## 平台私有行为说明 + +### 1. `spa,wdt-disabled` + +驱动里通过: + +```c +if (of_get_property(np, "spa,wdt-disabled", NULL)) + info->ctrl = 0; +else + info->ctrl = 1; +``` + +来决定默认控制策略。 + +也就是说: + +- 有 `spa,wdt-disabled` 时,`info->ctrl = 0` +- 没有这个属性时,`info->ctrl = 1` + +从后续逻辑看,`ctrl` 会影响: + +- 自动喂狗 hrtimer 是否持续运行 +- suspend/shutdown 时 watchdog 的处理方式 + +### 2. `spa,wdt-enable-restart-handler` + +驱动里通过: + +```c +if (of_get_property(np, "spa,wdt-enable-restart-handler", NULL)) + info->enable_restart_handler = 1; +``` + +控制是否注册: + +```c +register_restart_handler(&info->restart_handler) +``` + +启用后,系统执行 restart 时会走: + +- `spa_wdt_restart_handler()` +- 设置 reboot reason +- 打开 WDT +- 将 `WDT_WMR` 设成极小值 +- 触发硬件复位 + +所以这个属性不是“可有可无的功能开关”,而是决定 K3 是否把 watchdog 纳入系统重启通路。 + +## 驱动实现细节 + +### 1. WDT 计数与超时关系 + +驱动里注释说明: + +- watchdog timer 是 16 bit +- 频率是 `256Hz` + +`spa_wdt_set_timeout()` 的核心逻辑是: + +```c +tick = timeout << 8; +``` + +如果 tick 超过 16 bit 可表示范围,就回退到最大超时: + +- `255s` + +### 2. 启动时默认先设置 100 秒超时 + +`spa_wdt_start()` 里不是直接沿用外部传入 timeout,而是先调用: + +```c +spa_wdt_set_timeout(&info->wdt_dev, SPACEMIT_WATCHDOG_EXPIRE_TIME); +``` + +也就是: + +- 默认先设为 `100s` + +然后: + +```c +spa_wdt_write(info, WDT_WMER, 0x3); +``` + +使能 counter 和 reset/interrupt。 + +### 3. 驱动内部会自动喂狗 + +驱动用 hrtimer 周期执行: + +```c +spa_wdt_ping(&info->wdt_dev); +``` + +喂狗周期为: + +- `30s` + +这说明如果平台默认策略启用,watchdog 并不是“开了就等用户空间来喂”,而是内核驱动自己会续命。 + +### 4. 启动时会判断是否因 WDT 复位而重启 + +probe 里会读: + +```c +is_wdt_reset = spa_wdt_read(info, WDT_WSR); +``` + +并打印: + +- `System boots up because of SoC watchdog reset.` +- 或 `System boots up not because of SoC watchdog reset.` + +所以用户排查异常重启时,第一手信息其实就来自这个启动日志。 + +### 5. restart handler 会保存 reboot 命令信息 + +驱动里还维护了: + +- `reboot_cmd_mem` +- `reboot_cmd_size` +- `MPMU_ARSR_REBOOT_CMD()` + +并在 restart handler 中把 reboot reason / cmd 写入保留区和 MPMU 状态位。 + +这意味着 K3 的 watchdog 重启链路不只是“暴力复位”,还试图给下次启动留下原因信息。 + +## 接口介绍 + +### 标准 watchdog 接口 + +启用成功后,系统通常会暴露: + +```text +/dev/watchdog +/dev/watchdog0 +``` + +用户空间常见操作包括: + +- 打开 watchdog 设备 +- 发送 keepalive +- 设置 timeout +- 关闭设备(若 nowayout 允许) + +### 驱动私有 sysfs 节点 + +驱动里还创建了: + +- `dev_attr_wdt_ctrl` + +如果打开测试配置,还会额外创建: + +- `dev_attr_wdt_debug` + +这说明除了标准 `/dev/watchdog*`,平台还留了一条驱动私有调试入口。 + +## 使用与测试介绍 + +### 1. 先确认驱动是否起来 + +优先看启动日志里是否出现 watchdog 相关 probe 信息,以及是否打印: + +- 是否由 SoC watchdog reset 启动 + +### 2. 查看 watchdog 设备节点 + +```shell +ls /dev/watchdog* +``` + +### 3. 查看 watchdog class 信息 + +通常还可以看: + +```shell +ls /sys/class/watchdog/ +``` + +例如: + +```shell +cat /sys/class/watchdog/watchdog0/status +cat /sys/class/watchdog/watchdog0/timeout +cat /sys/class/watchdog/watchdog0/timeleft +``` + +### 4. 基础喂狗测试 + +如果用户空间直接测试标准 watchdog 接口,可以使用常见工具或程序周期性 keepalive,验证: + +- timeout 能否设置; +- keepalive 后 `timeleft` 是否恢复; +- 停止喂狗后系统是否按预期复位。 + +### 5. restart 路径测试 + +如果 DTS 带了: + +```dts +spa,wdt-enable-restart-handler; +``` + +可以重点测试: + +- `reboot` +- `reboot ` + +观察系统是否通过 watchdog 路径完成复位,并在下次启动时留下对应痕迹。 + +## Debug 介绍 + +### 1. 驱动没起来,先查三件套 + +优先检查: + +- `CONFIG_SPACEMIT_K1_WATCHDOG` +- DTS `compatible = "spacemit-k1,wdt"` +- `clock-names = "clk", "clk-bus"` + +因为驱动 probe 时明确会去拿: + +- `devm_clk_get(..., "clk")` +- `devm_clk_get(..., "clk-bus")` + +名字不匹配就起不来。 + +### 2. 别漏看第二段 `reg` + +如果只给了 watchdog 本体寄存器,没给 MPMU 那段: + +```dts +<0x0 0xd4050000 0x0 0x1024> +``` + +probe 也会失败,因为驱动确实会映射第二段资源。 + +### 3. 明明节点是 `okay`,为什么 watchdog 没真跑起来? + +要看 DTS 是否带了: + +```dts +spa,wdt-disabled; +``` + +这个属性会改变驱动默认控制逻辑,不能简单理解为“节点 okay 就一定持续运行”。 + +### 4. 为什么重启没有走 watchdog 路径? + +先看: + +```dts +spa,wdt-enable-restart-handler; +``` + +如果没开这个属性,驱动就不会注册 restart handler。 + +### 5. 异常重启怎么先判断是不是 WDT 导致? + +最先看启动日志里 probe 打出来的: + +- `System boots up because of SoC watchdog reset.` + +这通常比先猜原因更直接。 + +## FAQ + +### 1. K3 当前 watchdog 驱动到底是哪一个? + +是: + +```text +drivers/watchdog/spacemit-k1-wdt.c +``` + +虽然文件名是 K1,但 K3 当前 DTS 就是在用这条驱动链。 + +### 2. 为什么 K3 还在用 `spacemit-k1,wdt`? + +因为当前 SDK 复用了这套通用 watchdog IP。和 I2C、DMA 一样,**K3 平台并不意味着所有 IP 都改成了 `k3-*` 命名**。 + +### 3. K3 watchdog 是不是必须依赖用户空间喂狗? + +不一定。当前驱动内部本身就带 hrtimer 自动喂狗逻辑,不是纯用户空间 daemon 模式。 + +### 4. 文档里最该记住哪条经验? + +**先看 DTS 里的两个私有属性:`spa,wdt-disabled` 和 `spa,wdt-enable-restart-handler`。** + +K3 这条 watchdog 链路里,很多行为差异恰恰就是这两个属性决定的。 From 5f5b4d9ce3b3b364f8bbbab8b7cc980177a585ad Mon Sep 17 00:00:00 2001 From: Troy Date: Wed, 18 Mar 2026 17:07:56 +0800 Subject: [PATCH 21/30] k3: device: peripheral_driver: PMIC: initial version --- .../device/peripheral_driver/14-PMIC.md | 417 ++++++++++++++++++ 1 file changed, 417 insertions(+) diff --git a/zh/k3_buildroot/device/peripheral_driver/14-PMIC.md b/zh/k3_buildroot/device/peripheral_driver/14-PMIC.md index cb3ebec..0935323 100644 --- a/zh/k3_buildroot/device/peripheral_driver/14-PMIC.md +++ b/zh/k3_buildroot/device/peripheral_driver/14-PMIC.md @@ -1,2 +1,419 @@ # PMIC +介绍 K3 平台 PMIC 相关实现、驱动分层、DTS 配置方式,以及它与 regulator / RTC / 系统供电管理的关系。 + +## 模块介绍 + +PMIC(Power Management IC)负责为系统提供多路可控电源轨,并常常同时集成以下功能: + +- BUCK / LDO / Switch regulator +- RTC +- 电源时序控制 +- 掉电保持 +- 部分平台上的按键/中断/复位辅助能力 + +在 K3 当前 SDK 里,PMIC 不能只理解成“一个 regulator 芯片”。实际看到的是 **多类电源管理器件并存**: + +1. `spacemit,p1`:带 regulator + RTC 子功能的 PMIC +2. `spacemit,mpq8655`:单路外部 buck regulator +3. 其他固定电源 / GPIO regulator / 板级电源轨命名 + +所以对用户来说,PMIC 文档最重要的是先分清: + +- 哪些器件属于真正的 PMIC / sub-PMIC; +- 哪些只是独立 regulator; +- 哪些 DTS 节点负责“电源芯片本体”,哪些节点负责“导出的电源轨”; +- CPU / 外设 DTS 里看到的 `*-supply = <&xxx>` 最终接到了哪一路 regulator。 + +## 功能介绍 + +![](static/pmic.png) + +Linux 里的 PMIC 支持通常分成三层: + +1. **MFD / parent device** + 负责把一个复合电源芯片拆分成多个子设备; +2. **Regulator / RTC / 其他 function driver** + 分别管理芯片内的不同功能块; +3. **Board DTS 电源拓扑** + 给每一路输出命名、设置电压范围、绑定 consumer。 + +K3 当前最典型的一条 PMIC 路线就是: + +- I2C 识别 `compatible = "spacemit,p1"` +- `simple-mfd-i2c.c` 创建共享 regmap +- 再拆出两个子设备: + - `spacemit-p1-regulator` + - `spacemit-p1-rtc` + +这也是为什么前面 RTC 文档里会看到 P1 RTC,而它本质上又属于 PMIC 体系的一部分。 + +## 源码结构介绍 + +K3 PMIC 相关代码主要位于: + +```text +linux-6.18/ +|-- drivers/mfd/ +| `-- simple-mfd-i2c.c # P1 PMIC 通过这里拆分子设备 +|-- drivers/regulator/ +| |-- spacemit-p1.c # P1 PMIC regulator 驱动 +| |-- spacemit-mpq8655.c # MPQ8655 regulator 驱动 +| |-- fixed.c +| `-- gpio-regulator.c +|-- drivers/rtc/ +| `-- rtc-spacemit-p1.c # P1 PMIC RTC 子功能 +`-- arch/riscv/boot/dts/spacemit/ + `-- k3*.dts +``` + +本轮和 K3 PMIC 最相关的几个对象是: + +- `drivers/mfd/simple-mfd-i2c.c` +- `drivers/regulator/spacemit-p1.c` +- `drivers/regulator/spacemit-mpq8655.c` +- `drivers/rtc/rtc-spacemit-p1.c` +- `arch/riscv/boot/dts/spacemit/k3_evb.dts` + +### `simple-mfd-i2c.c` 在 P1 路线里的作用 + +P1 PMIC 不是直接由 regulator 驱动单独探测,而是先通过 `simple-mfd-i2c.c` 做 parent device。 + +代码里可以直接看到: + +```c +static const struct mfd_cell spacemit_p1_cells[] = { + { .name = "spacemit-p1-regulator", }, + { .name = "spacemit-p1-rtc", }, +}; +``` + +以及: + +```c +{ .compatible = "spacemit,p1", .data = &spacemit_p1, }, +``` + +这说明: + +- `spacemit,p1` 对应的不是单个功能驱动; +- 它先被当作 MFD parent; +- 再拆成 regulator + RTC 两个子设备。 + +## 关键特性 + +### 特性 + +| 特性 | 特性说明 | +| :--- | :--- | +| K3 PMIC 不是单一器件 | 当前可见 P1 PMIC + MPQ8655 + 板级固定电源共同构成供电体系 | +| P1 走 MFD 架构 | `simple-mfd-i2c.c` 先建 regmap,再拆子设备 | +| P1 同时导出 regulator 和 RTC | `spacemit-p1-regulator` + `spacemit-p1-rtc` | +| P1 regulator 数量较多 | 6 路 BUCK、4 路 ALDO、7 路 DLDO | +| MPQ8655 是独立 buck regulator | 当前实现导出 `edcdc1` | +| consumer 通过 `*-supply` 连接 | 比如 CPUFREQ/OPP 里的 `clst-supply = <&edcdc1>` | +| 电压策略主要写在 DTS | 各 rail 的 min/max/boot-on/always-on 由板级 DTS 描述 | + +### P1 PMIC 当前 regulator 组成 + +`drivers/regulator/spacemit-p1.c` 里直接定义了这些 regulator: + +- BUCK: + - `buck1` + - `buck2` + - `buck3` + - `buck4` + - `buck5` + - `buck6` +- ALDO: + - `aldo1` + - `aldo2` + - `aldo3` + - `aldo4` +- DLDO: + - `dldo1` + - `dldo2` + - `dldo3` + - `dldo4` + - `dldo5` + - `dldo6` + - `dldo7` + +也就是说,P1 这一颗 PMIC 本身就导出了: + +- **6 路 buck** +- **11 路 LDO(4 ALDO + 7 DLDO)** + +### K3 上 `dldo` 的供电关系有特殊分支 + +这个点挺关键。 + +`spacemit-p1.c` 里有: + +```c +#ifdef CONFIG_SOC_SPACEMIT_K3 +#define P1_DLDO_DESC(_n) \ + P1_REG_DESC(DLDO, dldo, _n, "buck4", 0x67, LDO_MASK, 128, p1_ldo_ranges) +#else +#define P1_DLDO_DESC(_n) \ + P1_REG_DESC(DLDO, dldo, _n, "buck5", 0x67, LDO_MASK, 128, p1_ldo_ranges) +#endif +``` + +这说明: + +- 在 **K3** 上,`dldo*` 的上游 supply_name 挂的是 `buck4` +- 不同于非 K3 路线里的 `buck5` + +这类细节非常像“平台电源拓扑知识”,文档里应该明确写出来,不然很容易照着 K1 经验误判。 + +### P1 电压范围 + +从 `spacemit-p1.c` 可见: + +#### BUCK 线性范围 + +```c +REGULATOR_LINEAR_RANGE(500000, 0, 170, 5000), +REGULATOR_LINEAR_RANGE(1375000, 171, 254, 25000), +``` + +表示大致支持: + +- `500000uV` 起步 +- 前半段 `5mV` 步进 +- 高电压段 `25mV` 步进 + +#### LDO 线性范围 + +```c +REGULATOR_LINEAR_RANGE(225000, 0, 127, 25000), +``` + +表示大致支持: + +- `225000uV` 起步 +- `25mV` 步进 + +### MPQ8655 当前实现 + +`drivers/regulator/spacemit-mpq8655.c` 里当前导出的是: + +- `edcdc1` + +它的描述符写法为: + +```c +MPQ8655_REG_DESC(BUCK, edcdc, _n, "vcc", 0x21, BUCK_MASK, 501, mpq8655_buck_ranges) +``` + +对应电压线性范围: + +```c +REGULATOR_LINEAR_RANGE(0, 0, 0x1f4, 2000), +``` + +这说明 MPQ8655 在 K3 方案里扮演的是**额外的外部可调 buck**,不是像 P1 那样的复合 PMIC。 + +## 配置介绍 + +PMIC 相关配置要分成: + +1. MFD / parent 节点 +2. regulator 子节点 +3. consumer 的 `*-supply` 引用 + +### 1. P1 PMIC 本体配置 + +板级 DTS 示例可以看到: + +```dts +p1@41 { + compatible = "spacemit,p1"; + reg = <0x41>; + status = "disabled"; + vcc-supply = <&p4v>; + + regulators { + compatible = "spacemit-p1-regulator"; + ... + }; +}; +``` + +这说明: + +- P1 是一个 I2C 从设备; +- 上游输入电源通过 `vcc-supply` 指定; +- regulator 子功能放在 `regulators {}` 子节点中; +- 真正的注册工作由 MFD + regulator 子驱动共同完成。 + +### 2. P1 regulator 子节点配置 + +P1 的每一路 regulator 都可以在 DTS 中声明自己的约束,例如: + +```dts +buck3: buck3 { + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <800000>; + regulator-ramp-delay = <5000>; + regulator-always-on; + regulator-boot-on; +}; +``` + +常见属性包括: + +| 属性 | 作用 | +| :--- | :--- | +| `regulator-min-microvolt` | 最小电压约束 | +| `regulator-max-microvolt` | 最大电压约束 | +| `regulator-ramp-delay` | 升降压斜率/延时约束 | +| `regulator-always-on` | 不允许运行中关闭 | +| `regulator-boot-on` | 启动阶段默认开启 | + +### 3. MPQ8655 本体配置 + +板级 DTS 里还能看到: + +```dts +mpq8655: mpq8655@30 { + compatible = "spacemit,mpq8655"; + reg = <0x30>; + vcc-supply = <&p12v>; + + regulators { + compatible = "spacemit,regulator,mpq8655"; + + edcdc1: edcdc1 { + regulator-min-microvolt = <534000>; + regulator-max-microvolt = <1000000>; + regulator-ramp-delay = <10>; + regulator-always-on; + regulator-boot-on; + }; + }; +}; +``` + +这一路在当前 K3 文档里尤其重要,因为前面 Cpufreq 文档里已经看到: + +```dts +clst-supply = <&edcdc1>; +``` + +所以至少部分 cluster CPU 电压就是挂在这颗外部 regulator 上。 + +### 4. consumer 侧如何引用 PMIC 输出 + +对用户来说,PMIC 的真正落点不是“定义了多少 buck/ldo”,而是后面谁在用它。 + +例如: + +- CPU 节点:`clst-supply = <&edcdc1>` +- 外设节点:`xxx-supply = <&buck3>` / `<&aldo2>` / `<&dldo4>` +- 板级供电名:`pwr_x100` / `pwr_a100` / `pwr_ext` + +所以调 PMIC 时,通常要做两步: + +1. 看 PMIC/regulator 节点本身有没有注册成功; +2. 再看 consumer 的 `*-supply` 是否真的绑到了期望 rail。 + +## 使用与调试介绍 + +### 1. 先看 regulator 是否枚举成功 + +常见接口: + +```text +/sys/class/regulator/ +``` + +例如: + +```shell +ls /sys/class/regulator/ +``` + +可以继续查看: + +```shell +for r in /sys/class/regulator/regulator*; do + echo "== $r ==" + cat $r/name 2>/dev/null || true +done +``` + +### 2. 看具体 rail 的状态 + +常见可查看属性包括: + +- `name` +- `microvolts` +- `state` +- `num_users` + +例如: + +```shell +cat /sys/class/regulator/regulator0/name +cat /sys/class/regulator/regulator0/microvolts +cat /sys/class/regulator/regulator0/state +``` + +### 3. 结合 consumer 一起看最有效 + +单看 PMIC 自己有没有起来,不够。 + +更实用的是反查 consumer,例如: + +- CPUFREQ 异常时,查 `edcdc1` +- RTC 路线异常时,查 `spacemit,p1` 是否探测成功 +- 某外设没起来时,查它的 `xxx-supply` 是不是连到了被禁用的 rail + +### 4. P1 路线排查顺序 + +P1 这条链路建议按下面顺序查: + +1. I2C 控制器是否起来; +2. `p1@41` 是否探测成功; +3. `simple-mfd-i2c` 是否把它拆成: + - `spacemit-p1-regulator` + - `spacemit-p1-rtc` +4. regulator 子设备是否成功注册多路 rail; +5. RTC 子设备是否成功注册 `/dev/rtcN`。 + +### 5. MPQ8655 路线排查顺序 + +MPQ8655 相对简单,重点查: + +1. I2C 设备是否探测成功; +2. `edcdc1` 是否成功注册; +3. CPU 节点 `clst-supply` 是否真的引用它; +4. OPP 切换时 regulator 电压是否能满足约束。 + +## FAQ + +### 1. K3 的 PMIC 是只有一颗 P1 吗? + +不是。当前 SDK / DTS 里至少能看到: + +- `spacemit,p1` +- `spacemit,mpq8655` +- 以及若干板级固定电源轨 + +所以 K3 电源体系是组合式的,不是单一 PMIC 全包。 + +### 2. 为什么 P1 不是直接一个 regulator 驱动探测完事? + +因为它是复合器件,当前走的是 MFD 路线。先由 `simple-mfd-i2c.c` 建共享 regmap,再拆出 regulator 和 RTC 两个子功能。 + +### 3. K3 上 `dldo*` 的 supply_name 为什么值得特别写? + +因为在 `CONFIG_SOC_SPACEMIT_K3` 下,`dldo*` 的 `supply_name` 是 `buck4`,不是别的平台上的 `buck5`。这属于很典型的“平台电源拓扑差异”,不写清楚后面很容易误配。 + +### 4. PMIC 文档里最该记住哪条经验? + +**不要只看 PMIC 芯片节点,要顺着 `*-supply` 一直追到具体 consumer。** + +因为对用户真正有价值的不是“这个 PMIC 有多少路输出”,而是“哪一路输出在给哪个模块供电,以及它现在是不是满足约束”。 From 2948d836a915411c9a469b8a72eefeb2795b644c Mon Sep 17 00:00:00 2001 From: Troy Date: Wed, 18 Mar 2026 17:24:22 +0800 Subject: [PATCH 22/30] k3: device: peripheral_driver: V2D: initial version --- .../device/peripheral_driver/13-V2D.md | 462 ++++++++++++++++++ 1 file changed, 462 insertions(+) diff --git a/zh/k3_buildroot/device/peripheral_driver/13-V2D.md b/zh/k3_buildroot/device/peripheral_driver/13-V2D.md index 1beaee8..7a66159 100644 --- a/zh/k3_buildroot/device/peripheral_driver/13-V2D.md +++ b/zh/k3_buildroot/device/peripheral_driver/13-V2D.md @@ -1,2 +1,464 @@ # V2D +介绍 K3 平台 V2D 模块的功能、驱动实现、DTS 配置方式以及用户态基本使用方法。 + +## 模块介绍 + +V2D 是 SpacemiT 平台上的 2D 图形加速引擎,用于把一些常见的图像处理操作从 CPU 挪到专用硬件执行。 + +从当前 K3 SDK 的实现看,V2D 这条线并不是 DRM/KMS 里的一个子模块,而是**一条独立的 platform driver + misc char device 路线**。用户空间通过字符设备和它交互,驱动内部再去处理: + +- buffer 映射 +- job 提交 +- 中断完成 +- fence 同步 +- IOMMU/TBU 地址空间管理 + +所以对用户来说,V2D 文档最重要的不是“2D 图形概念”,而是下面这几件事: + +- K3 上实际驱动文件在哪里; +- DTS 里需要哪些 clock / reset / irq; +- 用户态入口是不是 `/dev/v2d_dev`; +- 能不能调 clock; +- 做完任务后靠什么同步。 + +### 功能介绍 + +![](static/v2d.png) + +当前 K3 SDK 中,V2D 驱动提供的基本能力包括: + +- 颜色填充(Fill) +- 搬运/位块拷贝(Bitblit) +- Blend +- 缩放 +- 旋转 +- 裁剪 +- 颜色空间转换(CSC) + +从 `v2d_drv.h` 可以直接看到驱动里定义了比较完整的 V2D 任务模型,包括: + +- `V2D_CSC_MODE_E` +- `V2D_AREA_S` +- 多种 surface / task / blend 相关结构 + +另外,K3 当前实现还把同步机制做得比较完整: + +- 使用 `dma_fence` +- 可选导出 `sync_file` +- 中断完成后通过 workqueue / kthread 收尾 + +这意味着它不是一个只会“写寄存器然后等中断”的简单 demo 驱动,而是面向实际用户态任务提交设计的实现。 + +## 源码结构介绍 + +K3 V2D 相关代码位于: + +```text +linux-6.18/ +|-- drivers/soc/spacemit/v2d/ +| |-- Kconfig +| |-- Makefile +| |-- csc_matrix.h +| |-- v2d_drv.c +| |-- v2d_drv.h +| |-- v2d_hw.c +| |-- v2d_iommu.c +| |-- v2d_priv.h +| `-- v2d_reg.h +`-- arch/riscv/boot/dts/spacemit/ + `-- k3.dtsi +``` + +和 K1 文档相比,这一块在 K3 上仍然是相同的大体目录结构,但要以 K3 当前 SDK 实现为准。 + +### 关键文件说明 + +- `drivers/soc/spacemit/v2d/v2d_drv.c` + V2D 主驱动,完成 platform probe、char device 注册、IRQ、clock、reset、fence、线程与任务调度。 + +- `drivers/soc/spacemit/v2d/v2d_hw.c` + V2D 硬件编程与寄存器访问相关逻辑。 + +- `drivers/soc/spacemit/v2d/v2d_iommu.c` + V2D 地址映射 / TBU / 页表相关逻辑。 + +- `drivers/soc/spacemit/v2d/v2d_drv.h` + 用户态 / 驱动内部共用的数据结构、任务描述、CSC 模式、surface 信息等。 + +### Kconfig / Makefile + +Kconfig 中开关为: + +```text +config SPACEMIT_V2D + tristate "Spacemit V2D Engine Driver" + depends on SYNC_FILE + default m + help + This enables Spacemit V2D Engine driver +``` + +Makefile 中: + +```text +obj-$(CONFIG_SPACEMIT_V2D) += v2d.o +v2d-y := v2d_drv.o v2d_hw.o v2d_iommu.o +``` + +从这里可以直接看出两件事: + +1. V2D 当前是一个独立模块 `v2d.o` +2. 它依赖 `SYNC_FILE`,因为驱动内部支持 fence / sync_file 机制 + +## 关键特性 + +### 特性 + +| 特性 | 特性说明 | +| :--- | :--- | +| 独立 platform 驱动 | 通过 `spacemit,v2d` 进行 DTS 匹配 | +| 用户态入口是 misc 设备 | 驱动注册 `v2d_dev`,不是 DRM 节点 | +| 支持 clock 动态调频 | 通过 sysfs `clkrate` 查看/修改 core clock | +| 支持中断驱动任务完成 | 使用 `v2d_irq_handler()` 处理完成/错误中断 | +| 支持 fence 同步 | 使用 `dma_fence` 和可选 `sync_file` | +| 内建 V2D IOMMU/TBU 资源管理 | `v2d_iommu.c` 负责相关地址空间逻辑 | +| 具备工作线程模型 | 使用 workqueue + kthread 处理任务提交与完成 | + +### 当前驱动模型 + +从 `v2d_drv.c` 可以直接确认: + +- 驱动通过 `platform_driver` 注册; +- `of_match_table` 使用: + - `compatible = "spacemit,v2d"` +- 用户态通过 misc 设备访问: + - `info->mdev.name = "v2d_dev"` + +所以 K3 当前使用时,最核心的设备节点就是: + +```text +/dev/v2d_dev +``` + +这点和 K1 文档里的使用方式是一致的,而且在 K3 当前代码里能直接对上。 + +### 同步与 fence + +当前 V2D 驱动不是简单阻塞式提交,而是带同步对象的实现。 + +从 `v2d_drv.c` 可以看到: + +- `v2d_fence_generate()` +- `v2d_fence_wait()` +- `sync_file_create()` +- `dma_fence_wait_timeout()` + +说明驱动支持: + +- 为一次任务生成 fence +- 必要时导出成 sync_file fd +- 在任务链里进行同步等待 + +这对上层图形/多媒体流水线是很有用的。 + +### clock 管理 + +驱动里显式获取两路时钟: + +- `v2d-core` +- `v2d-io` + +并且在 probe 中直接设置: + +```c +clk_set_rate(info->clkcore, 409600000); +``` + +同时还提供 sysfs: + +- `clkrate` + +对应实现: + +- `v2d_sysfs_clkrate_get()` +- `v2d_sysfs_clkrate_set()` + +也就是说,K3 当前 V2D 的 core clock 默认就会在驱动 probe 时被设到 `409600000`,而且用户态还能通过 sysfs 再调。 + +## 配置介绍 + +主要包括: + +- `CONFIG` 驱动使能 +- `DTS` 节点配置 + +### CONFIG 配置 + +V2D 相关配置项为: + +- `CONFIG_SPACEMIT_V2D` +- 以及它依赖的 `CONFIG_SYNC_FILE` + +menuconfig 路径大致为: + +```text +Device Drivers ---> + SOC (System On Chip) specific Drivers ---> + <*> / Spacemit V2D Engine Driver +``` + +### DTS 配置 + +K3 当前 `k3.dtsi` 中的 V2D 节点为: + +```dts +v2d: v2d@c0100000 { + compatible = "spacemit,v2d"; + reg = <0x0 0xc0100000 0x0 0x1000>; + reg-names = "v2dreg"; + clocks = <&syscon_apmu CLK_APMU_LCD_MCLK>, + <&syscon_apmu CLK_APMU_V2D>; + clock-names = "v2d-io", "v2d-core"; + resets = <&syscon_apmu RESET_APMU_V2D>; + reset-names = "v2d_reset"; + interrupt-parent = <&saplic>; + interrupts = <86 IRQ_TYPE_LEVEL_HIGH>; + status = "okay"; +}; +``` + +这几个属性在 K3 上都是真的会被驱动用到: + +| 属性 | 作用 | +| :--- | :--- | +| `compatible = "spacemit,v2d"` | 匹配 V2D platform driver | +| `reg-names = "v2dreg"` | 驱动通过 `platform_get_resource_byname(..., "v2dreg")` 取资源 | +| `clocks` | 提供 `v2d-io` 和 `v2d-core` 两路时钟 | +| `clock-names` | 必须与 `devm_clk_get(dev, "v2d-core")` / `"v2d-io"` 对上 | +| `resets` | 提供 V2D reset 控制 | +| `reset-names = "v2d_reset"` | 文档层面建议保留一致命名 | +| `interrupts` | 提供 V2D 完成/错误中断 | +| `status = "okay"` | 启用节点 | + +### clock / reset 在驱动里的真实用法 + +probe 里能直接看到: + +- `devm_clk_get(dev, "v2d-core")` +- `devm_clk_get(dev, "v2d-io")` +- `devm_reset_control_get_exclusive(&pdev->dev, NULL)` +- `reset_control_deassert(info->v2d_reset)` + +因此如果 DTS 里的 `clock-names` 或 reset 资源不对,驱动会在 probe 阶段直接起不来。 + +## 接口描述 + +### 设备节点 + +当前驱动注册的 misc 设备名为: + +```text +/dev/v2d_dev +``` + +文件操作包括: + +- `open` +- `release` +- `read` +- `write` +- `mmap` + +在代码里对应: + +```c +static const struct file_operations v2d_dev_fops = { + .owner = THIS_MODULE, + .open = v2d_dev_open, + .release = v2d_dev_release, + .read = v2d_dev_read, + .write = v2d_dev_write, + .mmap = v2d_mmap, +}; +``` + +这说明当前 SDK 至少明确提供了: + +- 设备打开/关闭 +- 基本读写入口 +- `mmap` 映射能力 + +### API / 用户态任务模型 + +应用侧通常通过 SDK 封装 API 调用 V2D,K1 文档里列出的三类核心能力仍然适合作为 K3 用户态理解入口: + +- `Fill` +- `Bitblit` +- `Blend` + +典型接口包括: + +- `V2D_BeginJob` +- `V2D_EndJob` +- `V2D_AddFillTask` +- `V2D_AddBitblitTask` +- `V2D_AddBlendTask` + +这部分 API 结构在当前 K3 的 `v2d_drv.h` 中仍能对应上相关类型定义,因此这套使用模型没有脱节。 + +## 使用与调试介绍 + +### 1. 先确认驱动是否探测成功 + +先看启动日志: + +```shell +dmesg | grep -i v2d +``` + +驱动 probe 成功后,通常应能看到类似: + +- `probe v2d driver done!` + +### 2. 检查设备节点 + +```shell +ls -l /dev/v2d_dev +``` + +如果节点不存在,通常要优先回头检查: + +- `CONFIG_SPACEMIT_V2D` +- DTS `status = "okay"` +- clock / reset / irq 是否正常 + +### 3. 查看和调整 V2D clock + +驱动会创建 sysfs 属性: + +```text +/sys/bus/platform/devices/c0100000.v2d/clkrate +``` + +查看当前频率: + +```shell +cat /sys/bus/platform/devices/c0100000.v2d/clkrate +``` + +修改频率: + +```shell +echo 409600000 > /sys/bus/platform/devices/c0100000.v2d/clkrate +``` + +从当前驱动实现看,这个 sysfs 控制的是: + +- `clkcore` + +### 4. 基础测试 + +如果 rootfs 已带 V2D 测试程序,可以继续沿用 K1 那套常见验证方式: + +```shell +cd /usr/share/v2d +v2d_test --fill +v2d_test --blit +v2d_test --blend +``` + +测试重点不是只看命令返回,而是同时结合: + +- `dmesg` +- 设备节点 +- `clkrate` +- 任务完成/错误中断日志 + +一起判断。 + +## Debug 介绍 + +### 1. 驱动起不来,优先查这四项 + +优先检查: + +- `CONFIG_SPACEMIT_V2D` +- `CONFIG_SYNC_FILE` +- DTS `compatible = "spacemit,v2d"` +- DTS `clock-names = "v2d-io", "v2d-core"` + +因为这些在当前代码里都是硬依赖。 + +### 2. `reg-names` 别写错 + +驱动不是直接拿第 0 段 resource,而是: + +```c +platform_get_resource_byname(pdev, IORESOURCE_MEM, "v2dreg") +``` + +所以如果 DTS 里 `reg-names` 不是 `"v2dreg"`,probe 会失败。 + +### 3. 只给 core clock 不够 + +驱动会同时拿: + +- `v2d-core` +- `v2d-io` + +少一路都不行。 + +### 4. reset 没放开时,设备可能假在线 + +probe 里会执行: + +```c +reset_control_deassert(info->v2d_reset) +``` + +所以如果 reset 配置不对,即使节点是 `okay`,V2D 也未必真能工作。 + +### 5. 调试任务异常时,要同时看 IRQ 和 fence 路径 + +`v2d_irq_handler()` 会处理: + +- 正常 EOF 完成 +- 错误中断 + +而任务完成后又会继续走: + +- workqueue +- kthread +- `dma_fence` 完成/等待链路 + +所以如果你遇到的是“节点在、设备也在,但任务卡死/超时”,就别只盯寄存器,还要一起看 fence 和线程路径。 + +## FAQ + +### 1. K3 的 V2D 是不是还在? + +是,当前 K3 SDK 明确还有这条线。 + +最直接的证据有两组: + +- 驱动目录:`drivers/soc/spacemit/v2d/` +- DTS 节点:`v2d: v2d@c0100000` + +### 2. 用户态入口还是不是 `/dev/v2d_dev`? + +是。当前 K3 驱动里明确注册: + +```c +info->mdev.name = "v2d_dev"; +``` + +### 3. K3 V2D 更像 DRM 子模块还是独立设备? + +更像**独立设备**。当前实现是 platform driver + misc char device,不是 DRM render node 路线。 + +### 4. K3 这篇文档最该记住哪条经验? + +**先把它当成“独立的 2D 加速设备”来理解,而不是显示控制器附属模块。** + +这样你在查 DTS、设备节点、clock、IRQ、fence 和用户态接口时,思路会清楚很多。 From 155c00a9b3a95973c2fb805821a67f497d947e97 Mon Sep 17 00:00:00 2001 From: Troy Date: Wed, 18 Mar 2026 17:25:00 +0800 Subject: [PATCH 23/30] k3: device: usb: index: align navigation --- .../device/peripheral_driver/10-USB/index.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/zh/k3_buildroot/device/peripheral_driver/10-USB/index.md b/zh/k3_buildroot/device/peripheral_driver/10-USB/index.md index 3b1e801..072d894 100644 --- a/zh/k3_buildroot/device/peripheral_driver/10-USB/index.md +++ b/zh/k3_buildroot/device/peripheral_driver/10-USB/index.md @@ -5,10 +5,16 @@ slug: /development_guide/peripheral_driver/10-USB # USB -USB 全称 Universal Serial Bus(通用串行总线),是一种新兴的并逐渐取代其他接口标准的数据通信方式,由 Intel、 Compaq、 Digital、 IBM、 Microsoft、 NEC 及 Northern Telecom 等计算机公司和通信公司于 1995 年联合制定,并逐渐形成了行业标准。 +USB 全称 Universal Serial Bus(通用串行总线),是一种广泛使用的高速串行总线标准。K3 平台当前 USB 文档按功能拆成三部分,分别覆盖通用配置、Gadget 设备侧开发和信号质量测试。 USB 开发指南: -- [USB 通用开发指南 ](1-USB-General-Developer-Guide.md) -- [USB Gadget 开发指南 ](2-USB-Gadget-Developer-Guide.md) -- [USB 信号质量测试指南 ](3-USB-SQ-Test-Guide.md) \ No newline at end of file +- [USB 通用开发指南](1-USB-General-Developer-Guide.md) +- [USB Gadget 开发指南](2-USB-Gadget-Developer-Guide.md) +- [USB 信号质量测试指南](3-USB-SQ-Test-Guide.md) + +阅读建议: + +- 做板级端口规划、Type-C / role-switch / PHY 方案确认时,先看 **USB 通用开发指南**; +- 做设备侧模式、configfs、UDC bring-up 时,看 **USB Gadget 开发指南**; +- 做 USB2 test mode、debugfs、Host/Gadget 测试闭环时,看 **USB 信号质量测试指南**。 From 8f1d385d17ff9369d9564c3ac55f90b97bd5842e Mon Sep 17 00:00:00 2001 From: Troy Date: Wed, 18 Mar 2026 17:30:44 +0800 Subject: [PATCH 24/30] k3: device: peripheral_driver: WIFI: initial version --- .../device/peripheral_driver/WIFI.md | 405 ++++++++++++++++++ 1 file changed, 405 insertions(+) diff --git a/zh/k3_buildroot/device/peripheral_driver/WIFI.md b/zh/k3_buildroot/device/peripheral_driver/WIFI.md index ddcb183..2a9f9b5 100644 --- a/zh/k3_buildroot/device/peripheral_driver/WIFI.md +++ b/zh/k3_buildroot/device/peripheral_driver/WIFI.md @@ -1 +1,406 @@ # WIFI + +介绍 K3 平台 WIFI 模组的常见接入方式、当前 SDK 中可见的软件栈位置、DTS 配置重点以及用户态基本使用方法。 + +## 模块介绍 + +K3 平台当前仍然主要通过**外部 WiFi 模组**实现无线连接能力,而不是 SoC 内建一套独立 WLAN MAC/PHY。 + +从当前 SDK 和 DTS 的情况看,K3 上最稳、最清晰的一条主线仍然是: + +- **SDIO 接口挂载外部 WiFi 模组** +- 通过 `sdio` 控制器承载数据传输 +- 通过 `regulator-fixed` / `mmc-pwrseq-simple` 做基础上电与复位控制 +- 再由具体 WiFi 厂商驱动完成协议与射频功能 + +这意味着 K3 这篇 WIFI 文档不能像某些 SoC 一样写成“单一平台 WiFi 驱动说明”,而应该按三层来理解: + +1. **接口层**:SDIO / PCIe / USB +2. **模组驱动层**:厂商提供或内核已有的 WiFi 驱动 +3. **板级配电与复位层**:regulator / pwrseq / pinctrl / wakeup + +## 功能介绍 + +WiFi 在 Linux 里的基本软件层次可以概括为: + +![](static/wlan.png) + +1. **接口控制器** + 例如 SDIO host controller,负责总线传输; +2. **模组驱动** + 负责与具体 WiFi 芯片/模组交互; +3. **cfg80211 / mac80211 / nl80211** + 提供 Linux 无线协议栈与用户态控制接口; +4. **板级控制** + 包括供电、复位、唤醒 GPIO、keep-power-in-suspend 等。 + +### K3 当前更值得写哪条主线 + +从当前 `k3_evb.dts` 来看,K3 已明确给出了一个比较典型的 **SDIO WiFi 板级接法**: + +- `vmmc_sdio: regulator-vmmc-sdio` +- `sdio_pwrseq: sdio-pwrseq` +- `&sdio { ... mmc-pwrseq = <&sdio_pwrseq>; ... }` + +所以当前文档最适合围绕: + +- **SDIO WiFi bring-up** +- **供电 / reset / mmc-pwrseq** +- **SDIO host 的 DTS 约束** +- **用户态连接与调试** + +来展开。 + +## 源码结构介绍 + +WIFI 相关代码一般分成三块: + +```text +linux-6.18/ +|-- drivers/net/wireless/ # 具体 WiFi 驱动(厂商/主线) +|-- drivers/mmc/ # SDIO / MMC host 控制器 +|-- drivers/regulator/ # 模组供电控制 +|-- drivers/mmc/core/pwrseq* # mmc-pwrseq 通用上电/复位逻辑 +`-- arch/riscv/boot/dts/spacemit/ # 板级 DTS 配置 +``` + +### 当前 K3 里能确认下来的几件事 + +#### 1. 无线驱动主要仍在通用内核目录 + +当前 SDK 的 `drivers/net/wireless/` 下能看到大量通用/厂商无线驱动目录,例如: + +- `realtek/` +- `rtw88/` +- `rtw89/` +- `ti/` +- `silabs/` +- `ath/` +- `broadcom/` +- 等 + +这说明 K3 当前并没有表现出“唯一指定某一个平台私有 WiFi 驱动”的形态,而更像是: + +- **平台提供接口与板级接法** +- **具体模组走各自驱动栈** + +#### 2. 目前没看到 K1 文档那种明确的 `drivers/soc/spacemit/spacemit-rf` 主线 + +我这轮特意回查了当前 K3 SDK,没有直接找到 K1 文档里那种很明确的: + +- `spacemit-pwrseq.c` +- `spacemit-wlan.c` +- `spacemit-bt.c` + +所以当前 K3 文档不能照着 K1 那套“平台 rf 驱动一站式控制 WiFi/BT”去写。更稳的写法是: + +- 按 **SDIO + regulator-fixed + mmc-pwrseq-simple + 厂商 WiFi 驱动** 这条实际能对上的链路来写。 + +#### 3. K3 当前可直接确认的 host 侧核心节点是 `sdio` + +在 `k3.dtsi` 中可见: + +```dts +sdio: mmc@d4280800 { + ... +}; +``` + +在 `k3_evb.dts` 中则能看到对应板级配置: + +- `bus-width = <4>;` +- `non-removable;` +- `vmmc-supply = <&vmmc_sdio>;` +- `vqmmc-supply = <&p1v8>;` +- `mmc-pwrseq = <&sdio_pwrseq>;` +- `no-mmc;` +- `no-sd;` + +这已经构成了一条很完整的 SDIO WiFi 接入示例。 + +## 关键特性 + +### SDIO 接口相关特性 + +| 特性 | 特性说明 | +| :--- | :--- | +| 基于 K3 `sdio` 控制器接模组 | 当前 DTS 已给出板级示例 | +| 4-bit SDIO 总线 | `bus-width = <4>` | +| 非插拔模组场景 | `non-removable` | +| 模组独立上电控制 | `vmmc-supply` + `regulator-fixed` | +| 模组复位/上电时序 | `mmc-pwrseq-simple` + `reset-gpios` | +| 可保留 suspend 供电策略 | 需要时可用 `keep-power-in-suspend` | +| 通过厂商无线驱动接入 cfg80211 | 平台侧不限定唯一 WiFi 芯片驱动 | + +### 当前板级例子里的关键含义 + +K3 EVB 这套写法里最重要的不是“某个具体模组型号”,而是这一整条 bring-up 关系: + +- `vmmc_sdio` 负责模组主供电 +- `sdio_pwrseq` 负责 reset 时序 +- `&sdio` 节点声明这是一个 **non-removable 的 SDIO 外设** +- `no-mmc` / `no-sd` 明确这个口就是给 SDIO 模组用,不拿来跑 eMMC / SD 卡 + +这类写法对大部分板级 WiFi 方案都很有参考价值。 + +## 配置介绍 + +主要包括: + +1. SDIO host 控制器配置 +2. WiFi 模组供电配置 +3. mmc-pwrseq 配置 +4. 用户态网络配置 + +### 1. 模组供电配置 + +在 `k3_evb.dts` 中能看到一个固定电源示例: + +```dts +vmmc_sdio: regulator-vmmc-sdio { + compatible = "regulator-fixed"; + regulator-name = "vmmc-sdio"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + enable-active-high; + gpio = <&gpio 3 6 GPIO_ACTIVE_HIGH>; +}; +``` + +这表示: + +- WiFi 模组主供电由一个固定 3.3V rail 提供; +- 通过 GPIO 控制上电使能; +- 这一路后面被 `&sdio` 的 `vmmc-supply` 引用。 + +### 2. pwrseq 配置 + +板级 DTS 里还定义了: + +```dts +sdio_pwrseq: sdio-pwrseq { + compatible = "mmc-pwrseq-simple"; + reset-gpios = <&gpio 3 4 GPIO_ACTIVE_LOW>; +}; +``` + +这说明当前 K3 EVB 是用通用的 `mmc-pwrseq-simple` 来做: + +- 模组 reset +- 上电后的初始化时序辅助 + +对于很多 SDIO WiFi 模组来说,这已经够用了,不一定非要平台私有 pwrseq 驱动。 + +### 3. SDIO host 节点配置 + +`k3_evb.dts` 中的 `&sdio` 示例为: + +```dts +&sdio { + pinctrl-names = "default"; + pinctrl-0 = <&mmc2_cfg>; + bus-width = <4>; + non-removable; + vmmc-supply = <&vmmc_sdio>; + vqmmc-supply = <&p1v8>; + mmc-pwrseq = <&sdio_pwrseq>; + no-mmc; + no-sd; + clock-frequency = <375000000>; + status = "disabled"; +}; +``` + +这些字段里最关键的是: + +| 属性 | 作用 | +| :--- | :--- | +| `bus-width = <4>` | SDIO 4bit 总线 | +| `non-removable` | 表示模组为板载不可热插拔设备 | +| `vmmc-supply` | 模组主供电 | +| `vqmmc-supply` | SDIO IO 电压,通常是 1.8V | +| `mmc-pwrseq` | 指向模组 reset / 上电时序控制节点 | +| `no-mmc` / `no-sd` | 明确该 host 口不用于 MMC/SD 卡 | +| `status` | 板级启用时应改为 `okay` | + +### 4. 和 SDIO binding 的关系 + +`mmc-controller-common.yaml` 里可以确认这些都是标准或通用的 MMC/SDIO 属性: + +- `non-removable` +- `cap-sdio-irq` +- `keep-power-in-suspend` +- `vmmc-supply` +- `vqmmc-supply` +- `mmc-pwrseq` + +所以这条 K3 WiFi 配置路线并不是“拍脑袋定制”,而是: + +- K3 SDIO host +- 配合 Linux 通用 MMC/SDIO binding +- 再挂具体模组和驱动 + +### 5. 板级 bring-up 时的建议配置思路 + +如果你要在 K3 新板子上接一颗 SDIO WiFi 模组,建议按这个顺序想: + +1. 先确认硬件上 SDIO 接的是哪一路 host; +2. 确认模组主电源是否需要独立 enable GPIO; +3. 确认模组是否有 reset 脚,并挂到 `mmc-pwrseq-simple`; +4. 确认 IO 电压是 1.8V 还是 3.3V,对应 `vqmmc-supply`; +5. 如果需要 suspend 保持供电,再补 `keep-power-in-suspend`; +6. 最后再接入具体厂商 WiFi 驱动。 + +## 接口介绍 + +### 用户态控制接口 + +当前 K3 这条 WiFi 主线到用户态后,仍是标准 Linux 无线网络接口,例如: + +- `wlan0` +- `wpa_supplicant` +- `wpa_cli` +- `iw` +- `ip` + +也就是说,平台差异主要集中在: + +- SDIO / 供电 / reset / DTS bring-up + +而不是用户态无线工具本身。 + +## Debug 介绍 + +### 1. 先看 SDIO host 有没有起来 + +优先看: + +```shell +dmesg | grep -i mmc +``` + +以及: + +```shell +ls /sys/kernel/debug/mmc/ +``` + +如果连 SDIO host 都没起来,先别急着看 WiFi 驱动。 + +### 2. 再看 WiFi 模组有没有枚举成功 + +如果是 SDIO 模组,一般会在 `dmesg` 中看到: + +- SDIO card/function 枚举日志 +- 后续厂商 WiFi 驱动 probe 日志 + +如果停在 card init 阶段,就先查: + +- `vmmc-supply` +- `vqmmc-supply` +- `reset-gpios` +- `status = "okay"` +- `non-removable` +- `no-mmc` / `no-sd` + +### 3. 看 SDIO 工作状态 + +可以参考 MMC debugfs: + +```shell +cat /sys/kernel/debug/mmc1/ios +``` + +重点看: + +- `clock` +- `bus width` +- `timing spec` +- `signal voltage` + +如果这里电压和总线模式都不对,WiFi 大概率也跑不稳。 + +### 4. 用户态网络连通性验证 + +常见流程仍然是: + +```shell +wpa_supplicant -iwlan0 -Dnl80211 -c/wpa_supplicant.conf -B +wpa_cli -iwlan0 -p/var/run/wpa_supplicant +``` + +扫描网络: + +```shell +scan +scan_results +``` + +连接 AP 后再配合: + +```shell +ip addr +ping +iperf3 +``` + +做吞吐和稳定性验证。 + +## 测试介绍 + +### 基础连接测试 + +1. 启动 `wpa_supplicant` +2. 使用 `wpa_cli` 扫描 AP +3. 配置目标 AP 的 SSID / 密码 +4. 获取 IP 并测试联通 + +示例: + +```shell +wpa_supplicant -iwlan0 -Dnl80211 -c/wpa_supplicant.conf -B +wpa_cli -iwlan0 -p/var/run/wpa_supplicant +``` + +### 吞吐测试 + +同一局域网中可使用: + +```shell +# 服务端 +iperf3 -s + +# 客户端 +iperf3 -c -t 60 +``` + +如果你在做板级 bring-up,建议先别一上来就盯峰值吞吐,先把下面三件事确认稳: + +- 能稳定枚举 +- 能稳定关联 AP +- 长时间传输不掉线 + +## FAQ + +### 1. K3 当前 WiFi 是不是有一条很明确的平台私有驱动? + +从这轮已确认到的 SDK 情况看,**没有看到像 K1 文档里那样明确的一整套 `spacemit-rf` 平台私有主线**。更稳的结论是: + +- K3 主要提供 host + DTS + 供电/reset 方案 +- 具体 WiFi 功能仍由对应厂商驱动承担 + +### 2. K3 当前最稳的 WiFi 文档主线是什么? + +**SDIO WiFi 主线。** + +因为它在 `k3_evb.dts` 中已经有明确板级例子,比较适合做 bring-up 参考。 + +### 3. 如果以后换成 PCIe / USB WiFi,这篇还值不值看? + +值,尤其是“外部模组 + 板级供电/复位/唤醒”的思路仍然成立。但具体的数据通路和驱动,会从 `sdio` 换成对应的 PCIe/USB 路线。 + +### 4. 这篇最该记住哪条经验? + +**先把 WiFi 问题拆成两半:先看 host 和板级上电/reset,再看厂商无线驱动。** + +别一看到 `wlan0` 不起来,就直接往上层网络配置上扑。很多问题其实在 SDIO / 电源 / pwrseq 这一步就已经埋下了。 From ca191d2bcc40ad2ec0e73793bab555033cd82e84 Mon Sep 17 00:00:00 2001 From: Troy Date: Wed, 18 Mar 2026 17:35:24 +0800 Subject: [PATCH 25/30] k3: device: peripheral_driver: BT: initial version --- .../device/peripheral_driver/BT.md | 459 ++++++++++++++++++ 1 file changed, 459 insertions(+) diff --git a/zh/k3_buildroot/device/peripheral_driver/BT.md b/zh/k3_buildroot/device/peripheral_driver/BT.md index 1f61680..a1a3095 100644 --- a/zh/k3_buildroot/device/peripheral_driver/BT.md +++ b/zh/k3_buildroot/device/peripheral_driver/BT.md @@ -1,2 +1,461 @@ # BT +介绍 K3 平台 BT(Bluetooth)模组的常见接入方式、当前 SDK 中可见的软件栈位置、DTS 配置重点以及用户态基本使用方法。 + +## 模块介绍 + +K3 平台当前的蓝牙能力仍然主要依赖**外部 BT 模组**,常见接法包括: + +- UART 接口 BT 模组 +- USB 接口 BT 模组 +- WiFi/BT combo 模组中的 BT 部分 + +和前面的 WIFI 文档类似,K3 这篇 BT 文档不能假设“平台一定有一套完整私有蓝牙驱动”。从当前 SDK 和 DTS 能确认到的内容看,更稳的理解方式是: + +1. **BlueZ / 内核蓝牙协议栈** 负责 HCI/L2CAP/RFCOMM 等通用能力; +2. **具体 HCI 传输驱动** 负责 UART / USB / SDIO 等接法; +3. **板级电源与使能控制** 主要通过 GPIO / rfkill-gpio 等方式完成。 + +所以 K3 当前 BT bring-up 的重点,不在“平台自定义协议栈”,而在: + +- 模组接在哪个接口; +- 走 H4 还是 H5; +- 复位/关断 GPIO 怎么配; +- 用户态如何把 HCI 设备拉起来。 + +## 功能介绍 + +BT 软件栈一般分成以下几层: + +![](static/bt.png) + +1. **内核蓝牙核心栈** + 例如 `hci_core`、`l2cap`、`rfcomm`、`mgmt`; +2. **HCI 传输驱动** + 例如 UART H4/H5、USB HCI; +3. **具体厂商协议支持** + 例如 Realtek、Broadcom、QCA 等; +4. **板级控制** + 包括 `shutdown-gpios`、`reset-gpios`、rfkill 等。 + +### K3 当前更稳的两条蓝牙主线 + +从当前 SDK 和 DTS 看,K3 至少有两种很明确的 BT 方案痕迹: + +- **UART HCI Bluetooth**:走 Linux 标准 `BT_HCIUART` 路线 +- **USB Bluetooth + rfkill-gpio**:板级通过 `rfkill-gpio` 控制模组上下电 + +其中,当前板级 DTS 中更容易直接对上的,是 **rfkill-gpio 控制蓝牙电源/关断** 这条路径。 + +## 源码结构介绍 + +BT 相关代码主要分成三类: + +```text +linux-6.18/ +|-- net/bluetooth/ # 蓝牙核心协议栈 +|-- drivers/bluetooth/ # HCI 传输与厂商驱动 +|-- drivers/rfkill/ # rfkill 通用框架 +`-- arch/riscv/boot/dts/spacemit/ + `-- k3*.dts # 板级 BT 电源/关断 GPIO 配置 +``` + +### 1. 蓝牙核心协议栈 + +核心协议栈在: + +```text +net/bluetooth/ +``` + +这里面包括: + +- `hci_core` +- `l2cap` +- `rfcomm` +- `mgmt` +- `hci_sock` +- `hci_sysfs` + +也就是说,K3 当前 BT 依然是标准 Linux / BlueZ 路线,不是平台单独造了一套协议栈。 + +### 2. HCI 驱动与厂商支持 + +当前 `drivers/bluetooth/` 下可以直接看到: + +- `hci_h4.c` +- `hci_h5.c` +- `hci_ldisc.c` +- `hci_serdev.c` +- `btusb.c` +- `btbcm.c` +- `btrtl.c` +- `btqca.c` +- `btmtkuart.c` +- `btmtksdio.c` +- 等 + +这说明 K3 当前支持蓝牙的方式,主要还是借助 Linux 通用 HCI 驱动框架和各厂商协议扩展。 + +### 3. 当前没看到 K1 文档里那种清晰的 `spacemit-rf` 蓝牙私有主线 + +和 WIFI 那篇一样,这轮我没有在 K3 当前 SDK 中直接确认到 K1 文档里那种很完整的: + +- `spacemit-bt.c` +- `spacemit-pwrseq.c` + +这类平台私有控制主线。 + +所以 K3 这篇 BT 文档不能照 K1 原样写成“平台 RFKILL 驱动为主”。当前更稳的写法,是按: + +- 通用蓝牙协议栈 +- HCI 传输驱动 +- 板级 `rfkill-gpio` +- UART / USB 接法 + +来组织。 + +## 关键特性 + +### 蓝牙主线特性 + +| 特性 | 特性说明 | +| :--- | :--- | +| 基于标准 Linux 蓝牙栈 | `net/bluetooth/` + BlueZ 用户态 | +| 支持 UART HCI 路线 | `BT_HCIUART` + H4/H5 等协议 | +| 支持 USB HCI 路线 | `btusb` 驱动 | +| 支持多厂商协议扩展 | `btrtl` / `btbcm` / `btqca` / `btmtk*` | +| 板级可用 `rfkill-gpio` 控制 | DTS 中已能看到 BT 关断 GPIO 方案 | +| combo 模组适配方式明确 | 可将 WiFi/BT 分开做 WLAN / Bluetooth 的电源控制 | + +### 当前 DTS 里已确认的 BT 板级例子 + +#### `k3_deb1.dts` + +能直接看到: + +```dts +rfkill-usb-bt { + compatible = "rfkill-gpio"; + label = "rfkill-usb-bt"; + pinctrl-names = "default"; + pinctrl-0 = <&bt_en_cfg>; + radio-type = "bluetooth"; + shutdown-gpios = <&gpio 0 30 GPIO_ACTIVE_HIGH>; +}; +``` + +以及对应 pinctrl: + +```dts +bt_en_cfg: bt-en-cfg { + bt-en-pins { + pinmux = ; + bias-pull-up; + drive-strength = <38>; + power-source = <3300>; + }; +}; +``` + +这说明至少在这块板子上,BT 模组电源/使能是通过: + +- `rfkill-gpio` +- `shutdown-gpios` +- 板级 pinctrl + +来控制的。 + +#### `k3_com260.dts` + +还能看到另一种例子: + +```dts +rfkill-m2-bt { + compatible = "rfkill-gpio"; + label = "rfkill-m2-bt"; + pinctrl-names = "default"; + pinctrl-0 = <&m2_wdis2_cfg>; + radio-type = "bluetooth"; + shutdown-gpios = <&gpio 1 25 GPIO_ACTIVE_LOW>; +}; +``` + +这类写法更像是: + +- M.2 无线模组中的蓝牙部分 +- 通过 `rfkill-gpio` 单独控制 BT sideband + +所以从当前板级信息看,K3 的 BT 文档更应该强调: + +- **板级 GPIO 使能/关断方案** +- **模组接口类型(UART/USB/M.2 combo)** + +## 配置介绍 + +主要包括: + +1. 蓝牙协议栈配置 +2. HCI 传输驱动配置 +3. 板级 DTS 配置 +4. 用户态 bring-up 工具 + +### 1. 协议栈配置 + +蓝牙基础协议栈配置位于: + +- `net/bluetooth/Kconfig` + +常见基础配置包括: + +- `CONFIG_BT` +- `CONFIG_BT_BREDR` +- `CONFIG_BT_LE` +- `CONFIG_BT_RFCOMM` +- `CONFIG_BT_BNEP` +- `CONFIG_BT_HIDP` +- `CONFIG_BT_DEBUGFS` + +如果你还要做 HID / AVRCP 一类功能到用户态输入设备映射,常见还要配: + +- `CONFIG_UHID` +- `CONFIG_INPUT_UINPUT` + +### 2. UART HCI 配置 + +K3 若使用 UART 蓝牙,核心配置通常是: + +- `CONFIG_BT_HCIUART` +- `CONFIG_BT_HCIUART_H4` +- `CONFIG_BT_HCIUART_3WIRE` +- 以及具体厂商协议支持,例如: + - `CONFIG_BT_HCIUART_RTL` + - `CONFIG_BT_HCIUART_BCM` + - `CONFIG_BT_HCIUART_QCA` + +从 Kconfig 可以直接确认: + +- `BT_HCIUART_RTL` 会选择 `BT_HCIUART_3WIRE` +- 也就是很多 Realtek UART BT 方案默认走 **H5/3-wire** 路线 + +### 3. USB HCI 配置 + +如果板子上接的是 USB 蓝牙,核心就是: + +- `CONFIG_BT_HCIBTUSB` + +同时根据芯片可能还会依赖: + +- `CONFIG_BT_HCIBTUSB_BCM` +- `CONFIG_BT_HCIBTUSB_RTL` + +### 4. DTS 配置 + +当前 K3 板级里最明确的 BT DTS 主线,是 `rfkill-gpio` 控制。 + +例如: + +```dts +rfkill-usb-bt { + compatible = "rfkill-gpio"; + label = "rfkill-usb-bt"; + radio-type = "bluetooth"; + shutdown-gpios = <&gpio 0 30 GPIO_ACTIVE_HIGH>; +}; +``` + +或者: + +```dts +rfkill-m2-bt { + compatible = "rfkill-gpio"; + label = "rfkill-m2-bt"; + radio-type = "bluetooth"; + shutdown-gpios = <&gpio 1 25 GPIO_ACTIVE_LOW>; +}; +``` + +这些字段里最关键的是: + +| 属性 | 作用 | +| :--- | :--- | +| `compatible = "rfkill-gpio"` | 绑定通用 rfkill-gpio 驱动 | +| `radio-type = "bluetooth"` | 标明这是蓝牙 rfkill 设备 | +| `shutdown-gpios` | 控制模组关断/使能 | +| `pinctrl-0` | 配置 BT enable / shutdown 相关引脚状态 | + +### 5. UART 口选择 + +K3 在 `k3.dtsi` 中导出了很多串口别名: + +- `serial0 = &uart0` +- `serial1 = &uart1` +- ... +- `serial10 = &uart10` +- `serial11` ~ `serial16` 对应 `r_uart*` + +这说明如果后续某块板子要把 BT 接到 UART 上,**并不是只有一个固定 UART 可选**。真正该怎么写,要回到具体板级原理图和 DTS。 + +## 接口介绍 + +### 用户态控制接口 + +蓝牙最终在用户态通常表现为: + +- `hci0` +- `bluetoothctl` +- `hciconfig`(旧工具) +- `btmgmt` +- `rfkill` + +### UART attach 工具 + +如果使用 UART 蓝牙,通常需要先通过用户态工具把串口侧 HCI 拉起来,例如: + +- `hciattach` +- 或者厂商自带 attach 工具 + +不同模组的典型写法会不同,比如: + +- H4:更偏通用 UART HCI +- H5:Realtek 一类方案更常见 + +这一点在 K3 上没有变,本质仍然由具体蓝牙模组决定。 + +### rfkill 控制 + +如果板级用了 `rfkill-gpio`,用户态可以直接通过: + +```shell +rfkill list +rfkill block bluetooth +rfkill unblock bluetooth +``` + +来验证板级关断 GPIO 是否生效。 + +## Debug 介绍 + +### 1. 先分清你走的是哪条链 + +调 BT 之前,先把方案分清楚: + +- UART BT +- USB BT +- WiFi/BT combo 模组中的 BT 部分 + +这一步如果没分清,后面 DTS 和驱动都会查错方向。 + +### 2. 先看 rfkill 和 GPIO 侧是不是对的 + +如果 DTS 里已经用了 `rfkill-gpio`,优先看: + +```shell +rfkill list +``` + +如果连 BT rfkill 设备都没有,先查: + +- `compatible = "rfkill-gpio"` +- `radio-type = "bluetooth"` +- `shutdown-gpios` +- `pinctrl-0` + +### 3. UART 蓝牙起不来,先别急着看配对 + +UART 方案优先检查: + +- 串口节点是否启用 +- pinctrl 是否带上 TX/RX/CTS/RTS +- 波特率是否匹配模组默认值 +- H4 / H5 协议是否选对 +- attach 工具是否正确执行 + +### 4. USB 蓝牙起不来,先看枚举 + +USB BT 先查: + +```shell +dmesg | grep -i -e bluetooth -e btusb -e usb +``` + +如果 USB 设备都没枚举出来,先回头看: + +- 模组电源 +- `rfkill-gpio` +- USB 端口供电 +- 板级 enable/shutdown GPIO + +### 5. `hci0` 没出来时的排查顺序 + +建议顺序是: + +1. 模组是否上电 +2. 接口层是否正常(UART/USB) +3. HCI 驱动是否加载 +4. `dmesg` 是否有厂商协议初始化日志 +5. 最后再看 BlueZ 用户态 + +## 测试介绍 + +### 基础 bring-up 测试 + +1. 确认 rfkill / GPIO 使能正常 +2. 确认 `hci0` 出现 +3. 启动 BlueZ 服务 +4. 使用 `bluetoothctl` 进行扫描、配对、连接 + +例如: + +```shell +rfkill list +bluetoothctl +``` + +进入 `bluetoothctl` 后常见操作: + +```text +power on +scan on +pair +connect +trust +``` + +### 输入/音频相关测试 + +如果要做键鼠/HID 或更复杂功能,还要根据场景确认: + +- `UHID` +- `INPUT_UINPUT` +- 对应用户态守护进程 + +这些不属于 K3 平台私有差异,但在文档里需要提醒用户。 + +## FAQ + +### 1. K3 当前 BT 是不是有一条很明确的平台私有驱动? + +从这轮已确认的 SDK 情况看,**没有像 K1 文档那样直接看到一条完整且明确的 `spacemit-bt` 平台私有主线**。当前更稳的是: + +- Linux 通用蓝牙栈 +- HCI 传输驱动 +- 板级 `rfkill-gpio` + +### 2. K3 当前最明确的 BT 板级方案是什么? + +**rfkill-gpio 控制 BT 模组关断/使能** 这条线最明确,因为 `k3_deb1.dts` 和 `k3_com260.dts` 都能直接看到例子。 + +### 3. K3 的 BT 一定走 UART 吗? + +不一定。当前能确认的至少包括: + +- UART HCI 方案 +- USB 蓝牙方案 +- combo 模组中通过 sideband GPIO 控制 BT 的方案 + +### 4. 这篇最该记住哪条经验? + +**先把“接口类型”和“板级 enable/shutdown GPIO”查清楚,再看 HCI/BlueZ。** + +很多 BT bring-up 问题,根源根本不在协议栈,而在板级没有真正把模组拉起来。 From c181653d397ccee0540246d736e730b87fcdd20a Mon Sep 17 00:00:00 2001 From: Troy Date: Wed, 18 Mar 2026 17:48:29 +0800 Subject: [PATCH 26/30] k3: device: peripheral_driver: BT: add uart2 bring-up note --- .../device/peripheral_driver/BT.md | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/zh/k3_buildroot/device/peripheral_driver/BT.md b/zh/k3_buildroot/device/peripheral_driver/BT.md index a1a3095..d348006 100644 --- a/zh/k3_buildroot/device/peripheral_driver/BT.md +++ b/zh/k3_buildroot/device/peripheral_driver/BT.md @@ -290,12 +290,76 @@ K3 在 `k3.dtsi` 中导出了很多串口别名: - `serial0 = &uart0` - `serial1 = &uart1` +- `serial2 = &uart2` - ... - `serial10 = &uart10` - `serial11` ~ `serial16` 对应 `r_uart*` 这说明如果后续某块板子要把 BT 接到 UART 上,**并不是只有一个固定 UART 可选**。真正该怎么写,要回到具体板级原理图和 DTS。 +### 6. 关于你提到的“UART2 开 BT” + +这条线我又专门回查了一遍 K3 DTS。结论是: + +- `k3.dtsi` 里明确存在 `uart2: serial@d4017100` +- alias 里明确有:`serial2 = &uart2` +- 在 `k3_evb.dts` 和 `k3_deb1.dts` 里都能看到: + +```dts +&uart2 { + status = "okay"; +}; +``` + +也就是说,**K3 上确实有板子把 `uart2` 打开了**。 + +但要注意,当前 DTS 里我还**没有直接看到一个挂在 `&uart2` 下面的 `bluetooth { ... }` 子节点**,也没看到类似 serdev 直绑蓝牙协议节点的写法。 + +所以更稳的判断是: + +- `uart2` 这条口在部分板子上已经被 enable; +- 它**很可能就是留给串口类外设/蓝牙模组使用的候选口之一**; +- 但当前这版 K3 DTS 里,并没有把“`uart2` = 蓝牙”这件事直接写死在设备树里。 + +换句话说,这条链更像是: + +1. 板级先把 `&uart2` 打开; +2. Linux 提供对应串口设备; +3. 用户态再用 `hciattach` 或厂商工具把 BT HCI 拉起来。 + +这也符合很多 UART 蓝牙模组的常见 bring-up 方式。 + +### 7. 如果后续板子就是用 UART2 挂蓝牙,文档怎么落地 + +如果你的板子确认蓝牙就是接在 `uart2`,那最小可落地配置思路通常是: + +1. 使能 `&uart2` +2. 配好 TX/RX,必要时再补 CTS/RTS 流控 pinctrl +3. 配好 BT 模组的 `shutdown-gpios` / `reset-gpios` 或 `rfkill-gpio` +4. 进入系统后通过用户态 attach 工具初始化 + +示意上可以理解成: + +```dts +&uart2 { + status = "okay"; +}; +``` + +然后用户态执行类似: + +```shell +# H4 示例 +hciattach /dev/ttyS2 any 1500000 flow + +# Realtek H5 类方案要按模组工具/协议来 +``` + +这里有两个要点: + +- `/dev/ttySx` 的实际编号,要以系统启动后的串口枚举结果为准,**不要只凭 alias 名字硬猜**; +- H4 还是 H5,要以模组协议为准,尤其 Realtek 常常走 H5/3-wire。 + ## 接口介绍 ### 用户态控制接口 From f4cd94868bd6ae033570761131ac8ae1a3e7be52 Mon Sep 17 00:00:00 2001 From: Troy Date: Wed, 18 Mar 2026 17:56:56 +0800 Subject: [PATCH 27/30] k3: device: peripheral_driver: EtherCAT: initial version --- .../device/peripheral_driver/22-EtherCAT.md | 455 ++++++++++++++++++ 1 file changed, 455 insertions(+) diff --git a/zh/k3_buildroot/device/peripheral_driver/22-EtherCAT.md b/zh/k3_buildroot/device/peripheral_driver/22-EtherCAT.md index 5204bea..f4c3e84 100644 --- a/zh/k3_buildroot/device/peripheral_driver/22-EtherCAT.md +++ b/zh/k3_buildroot/device/peripheral_driver/22-EtherCAT.md @@ -1,2 +1,457 @@ # EtherCAT +介绍 K3 平台 EtherCAT 主站相关驱动结构、DTS 配置方式、与 K3 GMAC 的关系,以及 bring-up / 调试时需要注意的关键点。 + +## 模块介绍 + +K3 SDK 里的 EtherCAT 不是简单依赖用户态软件包,而是**内核里已经集成了一套 EtherCAT 主站 + 设备接口实现**。 + +从当前 SDK 代码看,K3 这条线的基本结构是: + +- EtherCAT 总开关:`CONFIG_ETHERCAT` +- IgH EtherCAT Master:`CONFIG_EC_MASTER` +- K3 GMAC 设备接口:`CONFIG_EC_K3_GMAC` + +也就是说,K3 当前文档应该围绕: + +1. **EtherCAT Master 子系统** +2. **K3 GMAC 专用 EtherCAT device 驱动** +3. **DTS 中 `ethercat_master` 与 `eth0` 的绑定关系** + +来理解,而不是写成“普通网卡如何跑以太网”。 + +## 功能介绍 + +EtherCAT 是一个运行在二层以太网帧上的工业实时总线协议。K3 当前 SDK 提供的是: + +- **主站(Master)侧能力** +- 基于 **K3 GMAC** 的低时延设备接口 +- 在主站和 K3 GMAC 之间建立直接的数据收发链路 + +### K3 当前实现的总体拓扑 + +从 `k3.dtsi` 和 `drivers/net/ethercat/` 可以对出这样一条主线: + +```text +IgH EtherCAT Master + | + v +K3 EtherCAT device interface (EC_K3_GMAC) + | + v +K3 GMAC controller (`spacemit,k3-gmac`) + | + v +PHY / RJ45 / EtherCAT slave network +``` + +所以这套实现不是“单独一颗 EtherCAT 控制器 IP”,而是: + +- 用 K3 SoC 自带的 GMAC +- 配上 EtherCAT device 适配层 +- 再由 IgH EtherCAT Master 来完成总线主站功能 + +## 源码结构介绍 + +K3 EtherCAT 相关代码位于: + +```text +linux-6.18/ +|-- drivers/net/ethercat/ +| |-- Kconfig +| |-- Makefile +| |-- master/ +| | `-- Kconfig +| `-- device/ +| |-- Kconfig +| `-- K3/ +| |-- Makefile +| |-- stmmac_main-ethercat.c +| |-- stmmac_platform-ethercat.c +| |-- dwmac-spacemit-ethqos-ethercat.c +| `-- ... +`-- arch/riscv/boot/dts/spacemit/ + `-- k3.dtsi +``` + +### 1. 总入口 + +顶层 Kconfig: + +```text +menuconfig ETHERCAT + bool "EtherCAT support" + depends on NET && NETDEVICES && ETHERNET +``` + +这说明 EtherCAT 在 K3 SDK 中是**内核内建的一套独立功能树**。 + +### 2. Master 侧 + +Master Kconfig 位于: + +```text +drivers/net/ethercat/master/Kconfig +``` + +关键项: + +```text +config EC_MASTER + tristate "EtherCAT master (IgH)" +``` + +并且还能看到: + +- `EC_MASTER_RUN_ON_CPU` +- `EC_MASTER_DEBUG_LEVEL` + +这说明当前 SDK 不只是把主站代码塞进去了,还提供了: + +- 主站运行 CPU 绑定 +- 调试级别配置 + +### 3. Device 侧 + +设备接口 Kconfig 位于: + +```text +drivers/net/ethercat/device/Kconfig +``` + +关键项: + +```text +menuconfig EC_DEVICE + bool "EtherCAT device" + +config EC_GENERIC + tristate "Generic EtherCAT device support" + +config EC_K3_GMAC + tristate "Spacemit K3 GMAC EtherCAT device support" + depends on SOC_SPACEMIT_K3 && STMMAC_ETH!=y + select PAGE_POOL +``` + +这里最关键的是: + +- **K3 有明确的 `EC_K3_GMAC` 配置项** +- 它依赖 `SOC_SPACEMIT_K3` +- 并且要求 `STMMAC_ETH != y` + +这个依赖非常关键,意味着: + +- 你不能让普通 STMMAC 主驱动和 EtherCAT 版 K3 GMAC 驱动同时以冲突方式占同一块硬件 + +### 4. K3 专用 device 驱动目录 + +K3 的 EtherCAT 设备侧实现不在普通 `stmmac` 目录,而在: + +```text +drivers/net/ethercat/device/K3/ +``` + +当前能直接看到: + +- `stmmac_main-ethercat.c` +- `stmmac_platform-ethercat.c` +- `dwmac-spacemit-ethqos-ethercat.c` +- 以及一整套 `dwmac*` / `stmmac*` 的 EtherCAT 变体文件 + +Makefile 里也能明确看到: + +```text +obj-$(CONFIG_EC_K3_GMAC) += stmmac.o +``` + +并由大量 `*-ethercat.c` 文件组成一个专门的 K3 EtherCAT STMMAC 变体。 + +这说明 K3 不是简单“复用普通网卡驱动”,而是为 EtherCAT 场景做了**专门裁剪/分支化适配**。 + +## 关键特性 + +### 特性 + +| 特性 | 特性说明 | +| :--- | :--- | +| 支持 EtherCAT 总开关 | `CONFIG_ETHERCAT` | +| 支持 IgH 主站 | `CONFIG_EC_MASTER` | +| 支持 K3 GMAC 专用设备接口 | `CONFIG_EC_K3_GMAC` | +| DTS 中有独立 `ethercat_master` 节点 | 用来定义主站实例与主设备绑定 | +| 主设备可直接绑定到 `eth0` | `main-device = <ð0>;` | +| K3 设备侧基于 STMMAC EtherCAT 变体实现 | `drivers/net/ethercat/device/K3/` | +| 设备接口与普通 STMMAC 存在互斥关系 | `depends on ... STMMAC_ETH!=y` | + +### 当前 DTS 中已确认的核心主线 + +`k3.dtsi` 里可以直接看到: + +```dts +ec_master: ethercat_master { + compatible = "spacemit,igh-ec-master"; + master-count = <1>; + status = "disabled"; + + master0 { + main-device = <ð0>; + }; +}; +``` + +这段内容非常关键,说明当前 K3 的 EtherCAT Master 配置模型是: + +- 有一个主站节点:`ethercat_master` +- 兼容串:`spacemit,igh-ec-master` +- 当前默认主站实例数:`master-count = <1>` +- `master0` 绑定主设备:`main-device = <ð0>` + +也就是说,**K3 当前主站默认是挂在 `eth0` 上跑的**。 + +### 与 K3 GMAC 的关系 + +同一份 `k3.dtsi` 里,`eth0` 节点为: + +```dts +eth0: ethernet@cac80000 { + compatible = "spacemit,k3-gmac", "snps,dwmac-5.10a"; + ... +}; +``` + +而 `drivers/net/ethercat/device/K3/dwmac-spacemit-ethqos-ethercat.c` 中又能直接看到: + +- `compatible = "spacemit,k3-gmac"` +- `spacemit_ethqos_probe()` +- `module_platform_driver(spacemit_ethqos_driver)` + +这就把 DTS 和驱动完全对上了: + +- DTS 里 `eth0` / `eth1` 使用 `spacemit,k3-gmac` +- EtherCAT 设备驱动同样匹配 `spacemit,k3-gmac` +- 设备侧底座是 K3 的 GMAC + STMMAC EtherCAT 变体 + +## 配置介绍 + +主要包括: + +1. EtherCAT 总开关 +2. Master 配置 +3. K3 GMAC 设备接口配置 +4. DTS 主站与主设备绑定 + +### 1. CONFIG 配置 + +关键配置项包括: + +- `CONFIG_ETHERCAT` +- `CONFIG_EC_MASTER` +- `CONFIG_EC_DEVICE` +- `CONFIG_EC_K3_GMAC` + +其中: + +- `CONFIG_EC_MASTER` 对应 IgH 主站 +- `CONFIG_EC_K3_GMAC` 对应 K3 GMAC 专用 EtherCAT 设备接口 + +### 2. 一个很重要的互斥约束 + +`EC_K3_GMAC` 的 Kconfig 中明确写了: + +```text +depends on SOC_SPACEMIT_K3 && STMMAC_ETH!=y +``` + +这条约束的实际含义是: + +- 如果你要让 K3 的 GMAC 跑 EtherCAT 专用设备接口,普通 STMMAC 主驱动不能以冲突方式抢占同一硬件 +- bring-up 时一定要先确认:你当前要的是**普通以太网**,还是 **EtherCAT 主站专用链路** + +这在文档里必须强调,不然很容易配出“编译能过、运行谁都起不来”的状态。 + +### 3. DTS 配置 + +当前 K3 默认 DTS 里主站节点是 disabled: + +```dts +ec_master: ethercat_master { + compatible = "spacemit,igh-ec-master"; + master-count = <1>; + status = "disabled"; + + master0 { + main-device = <ð0>; + }; +}; +``` + +所以如果板子要实际启用 EtherCAT,至少要在板级 DTS 中把它打开,例如: + +```dts +&ec_master { + status = "okay"; +}; +``` + +同时要确认 `main-device` 绑定的是你真正想拿来跑 EtherCAT 的 GMAC。 + +### 4. 主设备选择 + +当前默认是: + +```dts +main-device = <ð0>; +``` + +这不是唯一逻辑上可能的写法,但在当前 K3 默认 DTS 里,**已经明确给出的就是 `eth0`**。 + +如果后续板级设计想改成别的 GMAC 口,也要确保: + +- 对应口的 `compatible`、clock、reset、irq、phy 配置都完整; +- EtherCAT 主站节点与实际 GMAC 口绑定一致。 + +## 驱动实现要点 + +### 1. K3 设备侧基于 STMMAC EtherCAT 分支 + +从 `drivers/net/ethercat/device/K3/Makefile` 可以看出: + +- 它不是一个小薄封装 +- 而是带了一整套 `stmmac_*_ethercat.c` / `dwmac*_ethercat.c` + +这表明 K3 当前 EtherCAT 是沿着 **GMAC/STMMAC 深度适配** 的思路做的。 + +### 2. probe 入口 + +在 `dwmac-spacemit-ethqos-ethercat.c` 中可以直接看到: + +- `spacemit_ethqos_probe()` +- `devm_stmmac_probe_config_dt()` +- `devm_stmmac_pltfr_probe()` +- `compatible = "spacemit,k3-gmac"` + +所以它本质上还是: + +- 平台 glue driver +- 读取 DTS +- 再进入 STMMAC EtherCAT 设备实现 + +### 3. EtherCAT 收发链路已经在网卡驱动里打通 + +在 `stmmac_main-ethercat.c` 中可以直接看到: + +- `priv->ecdev_` +- `ecdev_set_link()` +- `ecdev_receive()` +- `get_ecdev(priv)` + +这说明 K3 GMAC 设备侧驱动已经把: + +- 链路状态 +- RX 接收 +- 主站设备对象 + +都接到了 EtherCAT 框架里,而不是用户自己在外面再套一层。 + +## 使用与调试介绍 + +### 1. 先确认你是在走“EtherCAT 模式”还是“普通以太网模式” + +这是第一步。 + +如果目标是 EtherCAT,就要优先确认: + +- `CONFIG_ETHERCAT` +- `CONFIG_EC_MASTER` +- `CONFIG_EC_K3_GMAC` +- `STMMAC_ETH` 没和它打架 + +### 2. 先看 DTS 里的 `ethercat_master` + +优先检查: + +- `compatible = "spacemit,igh-ec-master"` +- `master-count = <1>` +- `main-device = <ð0>` +- `status = "okay"` 是否在板级被打开 + +如果主站节点还是 disabled,那后面用户态工具基本也不可能正常起来。 + +### 3. 再看 K3 GMAC 口是不是按 EtherCAT 目标口配置好的 + +这里至少要看: + +- `eth0` 是否启用 +- PHY 是否接对 +- `phy-mode` 是否正确 +- clock / reset / irq 是否完整 +- 网口实际链路是否能建立 + +因为当前主站最终还是依赖 K3 GMAC 口收发帧。 + +### 4. 看启动日志 + +先查: + +```shell +dmesg | grep -i ethercat +``` + +再查: + +```shell +dmesg | grep -i -e stmmac -e gmac +``` + +重点确认: + +- EtherCAT master 是否注册成功 +- K3 GMAC EtherCAT device driver 是否 probe 成功 +- `eth0` 相关链路日志是否正常 + +### 5. 关注链路状态同步 + +因为驱动里显式走了: + +- `ecdev_set_link()` + +所以如果你遇到“主站起来了但总线一直不工作”,除了看主站本身,还要看: + +- 网口链路状态有没有被正确感知 +- PHY 协商是否正常 +- 物理连线 / 从站供电是否正常 + +## FAQ + +### 1. K3 当前 EtherCAT 是真有内核实现,还是只有文档占位? + +是**真有实现**。 + +当前能直接确认到: + +- `drivers/net/ethercat/` +- `CONFIG_EC_MASTER` +- `CONFIG_EC_K3_GMAC` +- `k3.dtsi` 里的 `ethercat_master` + +所以这不是空占位文档。 + +### 2. K3 EtherCAT 当前是基于哪块硬件口? + +从默认 DTS 看,当前主站默认绑定: + +- `main-device = <ð0>` + +也就是默认主口是 `eth0`。 + +### 3. K3 EtherCAT 是不是独立控制器? + +不是当前文档里这种理解方式。当前实现更像是: + +- **IgH Master + K3 GMAC EtherCAT 设备接口 + K3 GMAC 硬件** + +### 4. 这篇最该记住哪条经验? + +**先把“普通网卡模式”和“EtherCAT 专用模式”分清楚,再配内核和 DTS。** + +K3 这里最大的坑,不是“主站软件不会用”,而是前面配置阶段就把普通 STMMAC 和 EtherCAT K3 GMAC 关系配乱了。 From 1093c420e62c2ae4ca39981f257e0503039f09ef Mon Sep 17 00:00:00 2001 From: Troy Date: Wed, 18 Mar 2026 18:04:03 +0800 Subject: [PATCH 28/30] k3: device: peripheral_driver: ddr: initial version --- .../device/peripheral_driver/ddr.md | 426 ++++++++++++++++++ 1 file changed, 426 insertions(+) diff --git a/zh/k3_buildroot/device/peripheral_driver/ddr.md b/zh/k3_buildroot/device/peripheral_driver/ddr.md index 2a058b1..63c30a3 100644 --- a/zh/k3_buildroot/device/peripheral_driver/ddr.md +++ b/zh/k3_buildroot/device/peripheral_driver/ddr.md @@ -1,2 +1,428 @@ # DDR +介绍 K3 平台 DDR 初始化主线、当前支持的 DDR 类型、参数来源与调试入口。 + +## 概述 + +K3 平台这条 DDR 文档,不能直接照搬 K1 那套“类型 / CS / 速率 / ODT 都通过 EEPROM 或 SPL DTS 灵活覆盖”的写法。 + +这轮按 K3 SDK 实际代码回查下来,K3 DDR 的主线更明确也更收敛: + +- **DDR 初始化主要发生在 U-Boot SPL 阶段** +- 驱动位于: + - `uboot-2022.10/drivers/ddr/spacemit/k3/` +- 当前已明确支持: + - `LPDDR4X` + - `LPDDR5` +- K3 DDR 驱动优先读取: + - **TLV 中的 DDR Part Number** +- 如果 TLV 没给,则回退到: + - **SPL DTS 中的 `part-number`** +- 驱动再根据颗粒型号,匹配内部表项,得到: + - DDR 类型 + - 容量 + - 默认速率 + +所以 K3 这篇更适合写成: + +1. DDR 初始化主线 +2. 颗粒型号识别机制 +3. SPL DTS 配置方式 +4. 当前支持的 LPDDR4X / LPDDR5 颗粒表 +5. 频点 / training / 调试思路 + +## 初始化主线 + +当前 K3 DDR 驱动的核心代码在: + +```text +uboot-2022.10/drivers/ddr/spacemit/k3/ +``` + +其中最关键的文件包括: + +- `ddr_init.c` +- `ddr_freq.c` +- `k3_ddr.h` +- `lpddr4x_init.c` +- `lpddr5_init.c` +- `lpddr4x_*training_table.c` +- `lpddr5_*training_table.c` + +Makefile 里可以直接看到当前构成: + +```make +ifdef CONFIG_SPL_BUILD +obj-y += ddr_init.o ddr_freq.o +obj-$(CONFIG_K3_BOARD_FPGA) += ddr-snps-lp4x-fpga-init.o +obj-$(CONFIG_K3_BOARD_ASIC) += lpddr5_init.o lpddr5_training_table.o lpddr5_pre_training_table.o +obj-$(CONFIG_K3_BOARD_ASIC) += lpddr4x_init.o lpddr4x_training_table.o lpddr4x_pre_training_table.o +else +obj-y += ddr_freq.o +endif +``` + +这段非常关键,说明: + +- **SPL 阶段**会真正把 DDR 初始化代码带进去; +- ASIC 板当前明确支持 LPDDR4X 和 LPDDR5 两条初始化路径; +- Linux 阶段不是重新“初始化 DDR”,而是更多保留频率切换相关逻辑。 + +## 当前支持的 DDR 类型 + +从 `k3_ddr.h` 可直接确认: + +```c +typedef enum { + DDR_TYPE_LPDDR4X = 0, + DDR_TYPE_LPDDR5, + DDR_TYPE_UNKNOWN +} ddr_part_type; +``` + +也就是说,这轮 K3 当前实际能确认的类型只有: + +- `LPDDR4X` +- `LPDDR5` + +**没有看到 K1 文档里那种 LPDDR3 / LPDDR4 / LPDDR4X 三套并列的模型。** + +所以 K3 文档不能沿用 K1 那套类型说明。 + +## 参数来源与识别机制 + +### 1. 优先从 TLV 读取 DDR Part Number + +`ddr_init.c` 里能直接看到: + +```c +ret = get_tlvinfo(TLV_CODE_DDR_PARTNUMBER, ddr_part_number, sizeof(ddr_part_number) - 1); +``` + +如果 TLV 中取到颗粒型号,驱动会优先用这个值。 + +### 2. TLV 没有时,从 DTS `part-number` 读取 + +如果 TLV 读取失败,则继续读 SPL DTS: + +```c +temp = dev_read_string(dev, "part-number"); +``` + +也就是说,K3 当前更像是: + +- **TLV 优先** +- **DTS 兜底** + +而不是 K1 那种把 DDR type / CS / datarate / TX ODT 分成多个字段分别可配。 + +### 3. 通过 `part-number` 匹配内部表项 + +驱动内部有颗粒信息表: + +```c +const ddr_part_info ddr_parts_info[] = { + { "MT62F1G32D2DS", 0x0FD38DD9, DDR_TYPE_LPDDR5, 4096, CONFIG_DDR_DATARATE }, + { "MT62F2G32D4DS", 0x85D1F688, DDR_TYPE_LPDDR5, 8192, CONFIG_DDR_DATARATE }, + { "MT62F4G32D8DV", 0x3ACEF2E4, DDR_TYPE_LPDDR5, 16384, CONFIG_DDR_DATARATE }, + { "MT53E1G32D2FW", 0x75251AB8, DDR_TYPE_LPDDR4X, 4096, 4266 }, + { "MT53E2G32D4DE", 0x3EA87223, DDR_TYPE_LPDDR4X, 8192, 4266 }, + { "MT53E4G32D8CY", 0xAA9D4848, DDR_TYPE_LPDDR4X, 16384, 4266 }, +}; +``` + +表项中已经直接绑定了: + +- Part Number +- CRC32 +- DDR 类型 +- 容量(MB) +- 数据率(MT/s) + +这就是当前 K3 DDR 参数组织的核心思路。 + +### 4. 默认表项回退 + +如果找不到匹配颗粒,`find_ddr_info()` 会回退到第一项: + +- `MT62F1G32D2DS` + +这意味着如果 TLV / DTS 的 `part-number` 写错,SPL 可能会按默认颗粒去初始化,风险非常大。 + +## DTS 配置 + +### SPL DTS 节点 + +当前 U-Boot SPL 设备树里能直接看到 DDR 节点,例如: + +```dts +ddr@cb000000 { + u-boot,dm-spl; + compatible = "spacemit,snps-lp45"; + part-number = "MT62F2G32D4DS"; + reg = <0x00000000 0xcb000000 0x00000000 0x00400000>, + <0x00000000 0xcc000000 0x00000000 0x00400000>; + reg-names = "ddrc0", "ddrc1"; + status = "okay"; +}; +``` + +这里最关键的属性是: + +| 属性 | 作用 | +| :--- | :--- | +| `compatible = "spacemit,snps-lp45"` | 匹配 K3 DDR 控制器 SPL 驱动 | +| `part-number` | 当 TLV 没有颗粒信息时,作为识别输入 | +| `reg` | 两个 DDRC 寄存器空间 | +| `reg-names = "ddrc0", "ddrc1"` | 对应双通道 DDRC | +| `status = "okay"` | 启用节点 | + +### 顶层 U-Boot DTS 中也有 DDR 节点 + +在 `uboot-2022.10/arch/riscv/dts/k3.dtsi` 中也可看到: + +```dts +ddr@3000000 { + u-boot,dm-spl; + compatible = "spacemit,snps-lp45"; + reg = <0x00000000 0xc0000000 0x00000000 0x00400000>; + status = "okay"; +}; +``` + +但当前真正带 `part-number`、而且更接近 SPL 初始化入口的,是 `k3_spl.dts` 这类 SPL DTS。 + +## 驱动实现要点 + +### 1. probe 流程 + +K3 DDR SPL 驱动入口在: + +- `spacemit_ddr_probe()` + +主要步骤是: + +1. 读取 DDRC 地址 +2. 获取 DDR Part Number(TLV 优先,DTS 兜底) +3. 匹配 `ddr_parts_info[]` +4. 打印颗粒信息 +5. 调用 `lpddr_silicon_init()` 对 `ddrc0` / `ddrc1` 分别初始化 +6. 做一次基础 memory verify + +驱动里能直接看到: + +```c +printf("DDR Part Number: %s, Size: %dMB, Data Rate: %dMT/s\n", + part_info->part_number, part_info->size_mb, part_info->data_rate_mtps); +``` + +这也是 bring-up 时最值得看的早期日志之一。 + +### 2. 当前仅支持 LPDDR4X / LPDDR5 + +驱动里还显式判断: + +```c +if ((DDR_TYPE_LPDDR5 != part_info->type) && (DDR_TYPE_LPDDR4X != part_info->type)) { + pr_err("unsupported ddr type %d\n", part_info->type); + return 1; +} +``` + +这点要在文档里写死,避免用户按 K1 经验去配 LPDDR3/LPDDR4。 + +### 3. 初始化后会做基础内存校验 + +`ddr_init.c` 里有: + +- `test_pattern()` + +会在一段地址范围上做: + +- 地址写入模式校验 +- 反码写入模式校验 +- 恢复原值 + +日志成功时会打印: + +```text +memory verify pass +``` + +失败则会打印: + +```text +memory verify fail! +``` + +这能帮助你在 SPL 阶段尽早发现训练/初始化问题。 + +## 频率与训练 + +### 默认数据率 + +`k3_ddr.h` 中当前默认定义为: + +```c +#define CONFIG_DDR_DATARATE (6400) +``` + +并且注释明确写了当前支持范围: + +- `4266MT/s` +- `5500MT/s` +- `6000MT/s` +- `6400MT/s` + +### DFC 频点表 + +`ddr_freq.c` 中可见当前频点表: + +- `600` +- `800` +- `1066` +- `1200` +- `1600` +- `2400` +- `2666` + +这张表对应的是频率切换/级别控制逻辑,而不是简单等同于最终宣传口径里的 LPDDR MT/s 数字,但它能说明: + +- 当前 K3 DDR 已经考虑了多级频点切换; +- `ddr_freq.c` 不只是空壳。 + +### 2D training 配置 + +`k3_ddr.h` 中还能看到: + +```c +#define DISABLE_DDR_2D_TRAINING (0) +``` + +说明当前默认并**没有关闭** 2D training。 + +另外 LPDDR4X / LPDDR5 都有各自的: + +- `pre_training_table` +- `training_table` + +这说明 K3 DDR 初始化不是只靠固定寄存器表硬写,而是包含较完整的训练流程支持。 + +## 配置建议 + +### 1. 最常改的是 `part-number` + +如果你是在适配新的 DDR 颗粒,当前 K3 最直接、最稳的入口通常是: + +- 先确认 TLV 里有没有正确的 `DDR_PARTNUMBER` +- 如果没有,就改 SPL DTS 里的: + - `part-number = "...";` + +### 2. 改颗粒支持时,不要只改 DTS + +因为 K3 驱动不是看到任意 `part-number` 都能自动工作,它必须在: + +- `ddr_parts_info[]` + +里找到对应表项。 + +也就是说,如果你上的是一个当前表里没有的新颗粒,通常至少要补: + +- part number +- type +- size_mb +- data_rate_mtps + +并确认对应 LPDDR4X / LPDDR5 初始化与训练参数是否真的适配。 + +### 3. K3 当前文档不建议照 K1 那种颗粒参数逐项开放修改 + +这轮按源码看,K3 现阶段更像是: + +- 通过颗粒型号选择一套初始化/训练方案 + +而不是像 K1 文档那样,把: + +- `type` +- `cs-num` +- `datarate` +- `tx-odt` + +都作为 SPL DTS / EEPROM 中可直接逐项开放给用户改。 + +所以这篇文档先不写 K1 那种逐寄存器 / 逐字段调参教程,避免误导。 + +## 调试建议 + +### 1. 先看 SPL 打印的颗粒识别结果 + +优先关注: + +```text +DDR Part Number: ..., Size: ...MB, Data Rate: ...MT/s +``` + +如果这里 Part Number 就不对,后面一切都不用看了。 + +### 2. 再看 memory verify + +如果出现: + +- `memory verify pass` + +说明最基础的初始化至少过了一轮快速校验。 + +如果是: + +- `memory verify fail!` + +那就先回头查: + +- `part-number` +- TLV 内容 +- 训练表是否匹配实际颗粒 +- LPDDR4X / LPDDR5 类型是否选对 + +### 3. 注意双 DDRC 资源 + +驱动里会分别对: + +- `ddrc0` +- `ddrc1` + +调用初始化。 + +因此如果 DTS `reg` 或 `reg-names` 写错,不一定是“完全不起”,也可能表现为初始化过程异常、容量不对或稳定性问题。 + +## FAQ + +### 1. K3 DDR 当前支持哪些类型? + +按这轮源码确认,当前明确支持: + +- `LPDDR4X` +- `LPDDR5` + +### 2. K3 还是像 K1 一样支持通过 EEPROM/TLV 配一堆 DDR 字段吗? + +不是同一种模型。 + +K3 当前能明确确认的是: + +- 通过 `TLV_CODE_DDR_PARTNUMBER` 读取颗粒型号 +- 或者 DTS `part-number` 兜底 +- 再由内部颗粒表决定类型、容量和默认速率 + +### 3. K3 当前默认数据率是多少? + +从 `k3_ddr.h` 看,当前默认: + +- `CONFIG_DDR_DATARATE = 6400` + +但具体某颗粒实际使用值,还要看 `ddr_parts_info[]` 里的表项。 + +### 4. 这篇最该记住哪条经验? + +**先把 `part-number` 这条线搞对,再谈 DDR 调优。** + +K3 当前 DDR bring-up 的第一关键点,不是先改一堆寄存器,而是确保 SPL 识别到的颗粒型号就是你板子上那颗。 \ No newline at end of file From 9870877c27f71e6fb1928936edca395495370e0f Mon Sep 17 00:00:00 2001 From: Troy Date: Wed, 18 Mar 2026 18:08:17 +0800 Subject: [PATCH 29/30] k3: device: peripheral_driver: gpadc: mark unsupported status --- .../device/peripheral_driver/gpadc.md | 179 ++++++++++++++++++ 1 file changed, 179 insertions(+) diff --git a/zh/k3_buildroot/device/peripheral_driver/gpadc.md b/zh/k3_buildroot/device/peripheral_driver/gpadc.md index ed358b5..a427269 100644 --- a/zh/k3_buildroot/device/peripheral_driver/gpadc.md +++ b/zh/k3_buildroot/device/peripheral_driver/gpadc.md @@ -1,2 +1,181 @@ # GPADC +当前先给一个直接结论: + +**按这轮对 K3 SDK 的源码、binding 和 DTS 回查,暂时没有找到一条可落地的 K3 GPADC 主线。** + +也就是说,`zh/k3_buildroot/device/peripheral_driver/gpadc.md` 这个文件在当前 K3 文档目录里,更像是一个**预留占位项**,而不是已经有明确 K3 实现可对应的外设文档。 + +## 结论先行 + +这轮我按老规矩回查了三条线: + +1. Linux IIO/ADC 驱动 +2. DT binding +3. K3 DTS / DTSI 节点 + +回查结果是: + +- **没有找到 SpacemiT K3 专用 ADC / GPADC 驱动**; +- **没有找到 SpacemiT K3 对应的 ADC / GPADC binding**; +- **没有在 K3 DTS / DTSI 里找到 `adc@...` / `gpadc` / `io-channels` 这类实际使用节点**。 + +所以这篇文档目前不能像 I2C / SPI / CAN / V2D / EtherCAT 那样,直接按“已有 K3 驱动 + DTS 节点 + 用户态接口”去写。 + +## 这轮实际核实了什么 + +### 1. 查了 K1 参考文档 + +K1 的 `gpadc.md` 写法是: + +- 以 **IIO 子系统** 为基础; +- GPADC 来自 **PMIC 内嵌 ADC**; +- 配置项是: + - `SPACEMIT_P1_ADC` +- 典型路径是: + - `drivers/iio/adc/k1x_adc.c` + +也就是说,K1 那篇本质上更接近: + +- **P1 PMIC ADC 文档** + +而不是一个 SoC 内建 SAR ADC 文档。 + +### 2. 查了 K3 Linux `drivers/iio/adc/` + +这轮专门扫了: + +- `linux-6.18/drivers/iio/adc/` +- `linux-6.18/drivers/iio/` + +结果是: + +- 目录里有大量通用 ADC 驱动; +- 也能看到一些 PMIC ADC / GPADC 驱动,比如: + - `88pm886-gpadc.c` + - `ab8500-gpadc.c` + - `da9150-gpadc.c` + - `palmas_gpadc.c` + - `sun20i-gpadc-iio.c` + - `twl6030-gpadc.c` +- **但没有看到 SpacemiT K3 专用 ADC / GPADC 驱动文件**; +- **也没有看到类似 K1 文档里提到的 `k1x_adc.c` 这类 K3 可直接对应的文件**。 + +### 3. 查了 K3 IIO binding + +这轮也扫了: + +- `Documentation/devicetree/bindings/iio/` +- `Documentation/devicetree/bindings/iio/adc/` + +结果同样是: + +- 有大量通用 / 其他厂商 ADC binding; +- **没有看到 SpacemiT K3 自己的 ADC / GPADC binding**。 + +### 4. 查了 K3 DTS / DTSI + +这轮重点 grep 了: + +- `adc@` +- `gpadc` +- `#io-channel-cells` +- `io-channels` +- `spacemit.*adc` +- `spacemit.*gpadc` + +目标范围是: + +- `arch/riscv/boot/dts/spacemit/k3*.dts` +- `arch/riscv/boot/dts/spacemit/k3.dtsi` + +结果是: + +- **没有发现 K3 ADC / GPADC 设备节点**; +- **没有发现其他外设把 K3 ADC 当作 `io-channels` consumer 来使用**。 + +这点基本已经很能说明问题了: + +- 如果 K3 真有一条成熟的 GPADC 主线,通常至少应该有: + - driver + - binding + - DTS node + - 或 consumer usage +- 但这轮三条线都没对上。 + +## 当前最稳的判断 + +### 判断 1:`gpadc.md` 大概率是占位文件 + +从当前 K3 文档目录看,`gpadc.md` 更像是: + +- 文档框架阶段先占了一个名字 +- 但当前 SDK 还没有一条完整 K3 对应实现 + +### 判断 2:K1 那篇不能直接迁移到 K3 + +K1 这篇的实质是: + +- P1 PMIC 内嵌 ADC +- IIO 驱动 + pinctrl + PMIC 节点 + +但当前 K3 这轮核实下来,并没有直接看到: + +- K3 文档可稳定落地的同名驱动 +- K3 DTS 中正在实际启用的 ADC 节点 + +所以不能把 K1 的内容硬搬过来,不然就会写成“文档有,代码没对上”。 + +## 现在这篇为什么先这样写 + +因为当前最负责任的做法不是“凑一篇差不多的 ADC 文档”,而是把现状说清楚: + +- 目前**没有足够证据**证明 K3 在当前 SDK 中存在一条可独立成文的 GPADC 支持主线; +- 因此这篇文档先保留为**核查结论页**,而不是伪装成一篇已经完整可用的开发指南。 + +## 后续如果要把这篇真正补起来,需要补齐什么 + +后续如果你想把 `gpadc.md` 变成一篇真正的 K3 文档,至少需要先再确认下面任意一条是否存在: + +1. **SoC 内建 ADC 驱动** + - `drivers/iio/adc/` 下新增 SpacemiT/K3 相关驱动 +2. **PMIC ADC 驱动** + - 并且 K3 板级 DTS 中实际启用 +3. **K3 DTS 节点** + - 出现明确 `adc@...` / `gpadc` / `io-channels` +4. **用户态可验证路径** + - `/sys/bus/iio/devices/iio:deviceX/...` + - 并且能和 K3 板级原理图、引脚、通道对应上 + +只要后续源码里这几条有一条补齐,这篇就能继续往正式文档方向写。 + +## FAQ + +### 1. K3 现在到底有没有 GPADC? + +**按当前这轮 SDK 回查,不能确认有。** + +更准确地说: + +- 没找到 K3 专用驱动 +- 没找到 K3 binding +- 没找到 K3 DTS 节点 + +所以现在不能把它当成“已有明确支持的 K3 外设”来写。 + +### 2. K1 那篇为什么不能直接抄过来? + +因为 K1 那篇本质更像: + +- **P1 PMIC ADC 文档** + +而当前 K3 这边没有查到能一一对上的: + +- 驱动 +- DTS 节点 +- binding +- 用户态接口 + +### 3. 这篇现在最该记住哪句话? + +**当前 K3 的 `gpadc.md` 先当“未确认实现的占位项”处理,不要按已支持外设去宣传或指导用户配置。** From 638759ce111bd017d307424e0ac3d299e7f16374 Mon Sep 17 00:00:00 2001 From: Troy Date: Wed, 18 Mar 2026 18:28:47 +0800 Subject: [PATCH 30/30] k3: device: peripheral_driver: IR-RX: initial version --- .../device/peripheral_driver/04-IR-RX.md | 327 ++++++++++++++++++ 1 file changed, 327 insertions(+) diff --git a/zh/k3_buildroot/device/peripheral_driver/04-IR-RX.md b/zh/k3_buildroot/device/peripheral_driver/04-IR-RX.md index 38897f0..49df3f1 100644 --- a/zh/k3_buildroot/device/peripheral_driver/04-IR-RX.md +++ b/zh/k3_buildroot/device/peripheral_driver/04-IR-RX.md @@ -1,2 +1,329 @@ # IR-RX +介绍 K3 平台 IR 接收控制器的配置、驱动接口和基本调试方式。 + +## 模块介绍 + +K3 上的 IR-RX 仍然走 Linux `rc-core` 原始红外解码框架,底层控制器驱动复用了 K1 兼容 IP,而不是一条全新的 K3 专用 IR 驱动。 + +K3 SDK 当前实际对应关系如下: + +- IR 控制器驱动:`drivers/media/rc/ir-spacemit-k1.c` +- rc-core 原始事件框架:`drivers/media/rc/rc-ir-raw.c` +- 常见协议解码器: + - `drivers/media/rc/ir-nec-decoder.c` + - `drivers/media/rc/ir-rc5-decoder.c` + - `drivers/media/rc/ir-rc6-decoder.c` + - `drivers/media/rc/ir-sony-decoder.c` + - 等 + +驱动里设备名定义为: + +- `SPACEMIT_IR_DEV = "spacemit-ir"` + +K3 上实际存在 4 个 IR-RX 控制器节点: + +- A 域:`ircrx0`、`ircrx1` +- R 域:`rircrx0`、`rircrx1` + +### 功能介绍 + +K3 平台外接红外接收头(通常是已解调输出的 IR 接收器)后,控制器驱动从硬件 FIFO 读取脉宽信息,经 `rc-core` 原始事件接口送入内核解码器,再转换成用户空间可见的按键事件。 + +当前从驱动源码可确认的实现特征包括: + +- 使用 `RC_DRIVER_IR_RAW` +- 支持原始脉宽事件上报 +- `allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER` +- FIFO 深度为 `32` +- 驱动内设置了噪声阈值和比较阈值 + +### 源码结构介绍 + +```text +drivers/media/rc/ +|-- rc-ir-raw.c # rc-core 原始事件框架 +|-- ir-nec-decoder.c # NEC 协议解码器 +|-- ir-rc5-decoder.c # RC5 协议解码器 +|-- ir-rc6-decoder.c # RC6 协议解码器 +|-- ir-sony-decoder.c # Sony 协议解码器 +|-- ir-spacemit-k1.c # Spacemit IR-RX 控制器驱动 +``` + +## 关键特性 + +- K3 当前复用 `compatible = "spacemit-k1,irc"` +- 使用 `rc-core` 标准红外输入框架 +- 支持原始红外脉宽采集与内核协议解码 +- 驱动中 FIFO 深度为 `32` +- 驱动默认超时为 `10000` +- 驱动 `rx_resolution` 由 `IR_FREQ = 100000` 推导,周期分辨率为 `10us` + +## 配置介绍 + +主要包括 **CONFIG 配置** 和 **DTS 配置**。 + +### CONFIG 配置 + +K3 SDK 对应的配置项不是 K1 文档里的 `IR_SPACEMIT`,而是: + +```text +config IR_SPACEMIT_K1 + tristate "SPACEMIT IR remote Recriver control" + depends on SOC_SPACEMIT +``` + +对应 Makefile: + +```make +obj-$(CONFIG_IR_SPACEMIT_K1) += ir-spacemit-k1.o +``` + +因此使用 K3 SDK 时,至少应确认: + +- `CONFIG_RC_CORE=y` +- `CONFIG_RC_DEVICES=y` +- `CONFIG_IR_SPACEMIT_K1=y` 或 `m` +- 以及需要的协议解码器,例如: + - `CONFIG_IR_NEC_DECODER` + - `CONFIG_IR_RC5_DECODER` + - `CONFIG_IR_RC6_DECODER` + - `CONFIG_IR_SONY_DECODER` + +### DTS 配置 + +#### 1. SoC dtsi 控制器节点 + +K3 主域 `k3.dtsi` 中定义了两个 IR-RX 控制器: + +```dts +ircrx0: irc-rx0@d4017e00 { + compatible = "spacemit-k1,irc"; + reg = <0x0 0xd4017e00 0x0 0x100>; + interrupts = <69 IRQ_TYPE_LEVEL_HIGH>; + interrupt-parent = <&saplic>; + clocks = <&syscon_apbc CLK_APBC_IR0>; + resets = <&syscon_apbc RESET_APBC_IR0>; + clock-frequency = <102400000>; + status = "disabled"; +}; + +ircrx1: irc-rx1@d4017f00 { + compatible = "spacemit-k1,irc"; + reg = <0x0 0xd4017f00 0x0 0x100>; + interrupts = <20 IRQ_TYPE_LEVEL_HIGH>; + interrupt-parent = <&saplic>; + clocks = <&syscon_apbc CLK_APBC_IR1>; + resets = <&syscon_apbc RESET_APBC_IR1>; + clock-frequency = <102400000>; + status = "disabled"; +}; +``` + +R 域 `k3-rdomain.dtsi` 中还定义了两个 IR-RX 控制器: + +```dts +rircrx0: rirc-rx0@c0887000 { + compatible = "spacemit-k1,irc"; + reg = <0x0 0xc0887000 0x0 0x100>; + interrupts = <274 IRQ_TYPE_LEVEL_HIGH>; + interrupt-parent = <&saplic>; + clocks = <&syscon_rcpu_sysctrl CLK_RCPU_SYSCTRL_RIRC0>; + resets = <&syscon_rcpu_sysctrl RESET_RCPU_SYSCTRL_RIRC0>; + clock-frequency = <102400000>; + status = "disabled"; +}; + +rircrx1: rirc-rx1@c088e000 { + compatible = "spacemit-k1,irc"; + reg = <0x0 0xc088e000 0x0 0x100>; + interrupts = <275 IRQ_TYPE_LEVEL_HIGH>; + interrupt-parent = <&saplic>; + clocks = <&syscon_rcpu_sysctrl CLK_RCPU_SYSCTRL_RIRC1>; + resets = <&syscon_rcpu_sysctrl RESET_RCPU_SYSCTRL_RIRC1>; + clock-frequency = <102400000>; + status = "disabled"; +}; +``` + +这说明 K3 至少在设备树定义上给了 4 个 IR 接收控制器入口。 + +#### 2. 板级 dts 使能示例 + +当前在 `k3_evb.dts` 中能直接看到: + +```dts +&ircrx0 { + status = "okay"; +}; + +&ircrx1 { + status = "okay"; +}; + +&rircrx0 { + status = "okay"; +}; + +&rircrx1 { + status = "okay"; +}; +``` + +所以 K3 EVB 上四个 IR-RX 控制器节点都被打开了。 + +#### 3. `linux,rc-map-name` + +驱动中会读取: + +```c +ir->map_name = of_get_property(dn, "linux,rc-map-name", NULL); +ir->rc->map_name = ir->map_name ?: RC_MAP_EMPTY; +``` + +如果板级 DTS 需要绑定默认按键映射表,可以在节点中补: + +```dts +linux,rc-map-name = "rc-empty"; +``` + +或者改成你实际使用的 rc keymap。 + +如果不配,驱动会回退到: + +- `RC_MAP_EMPTY` + +## 接口描述 + +### 驱动关键接口 + +驱动主要通过 `rc-core` 提供原始红外事件: + +```c +ir_raw_event_store_with_filter(struct rc_dev *dev, struct ir_raw_event *ev) +ir_raw_event_handle(struct rc_dev *dev) +``` + +其中中断处理函数: + +- `spacemit_ir_irq()` + +会做这些事: + +1. 读取中断状态 +2. 清中断 +3. 从 FIFO 中取脉宽数据 +4. 组织 `struct ir_raw_event` +5. 调用 `ir_raw_event_store_with_filter()` 入队 +6. 调用 `ir_raw_event_handle()` 触发后续解码 + +### 驱动初始化相关实现 + +源码里几个关键函数: + +- `spacemit_ir_probe()` +- `spacemit_ir_config()` +- `spacemit_ir_hw_init()` +- `spacemit_ir_set_idle()` +- `spacemit_ir_suspend()` +- `spacemit_ir_resume()` + +能确认的实现细节包括: + +- 驱动会获取 clock / reset 资源 +- 可选读取 `clock-frequency` +- `clkdiv = DIV_ROUND_UP(ir->freq, IR_FREQ) - 1` +- 设置 noise threshold:`SPACEMIT_IR_NOISE = 1` +- 设置 FIFO compare threshold:`IRFIFO_DEF_CMP = 1` +- 使能所有 IR 中断:`IR_IRQ_ENALL` +- 使能控制器:`SPACEMIT_IR_ENABLE` + +## 调试介绍 + +### 1. 先看 dmesg 和节点注册 + +先确认: + +- IR 控制器节点是否为 `okay` +- `CONFIG_IR_SPACEMIT_K1` 是否打开 +- 启动日志里是否出现 `spacemit-ir` + +建议先看: + +```bash +dmesg | grep -i ir +dmesg | grep -i rc +dmesg | grep -i spacemit +``` + +### 2. 查看输入设备和 rc 设备 + +常见检查方式: + +```bash +ls /sys/class/rc/ +cat /sys/class/rc/rc0/protocols +``` + +如果系统正确注册了 rc 设备,通常还能看到关联输入设备。 + +### 3. 用 `ir-keytable` 查看协议和事件 + +用户态常用工具是: + +```bash +ir-keytable +ir-keytable -t +``` + +例如: + +```bash +ir-keytable -s rc0 +ir-keytable -t +``` + +按遥控器按键时,若硬件、时钟、引脚和协议都正常,应能看到脉冲 / 解码结果。 + +## 测试介绍 + +可以按下面顺序做最小验证: + +1. 板级 DTS 把目标 IR 节点设为 `okay` +2. 编译打开 `CONFIG_RC_CORE`、`CONFIG_RC_DEVICES`、`CONFIG_IR_SPACEMIT_K1` +3. 启动后确认 `/sys/class/rc/` 下出现 rc 设备 +4. 外接红外接收头并使用遥控器发码 +5. 用 `ir-keytable -t` 观察是否有原始或解码后的事件 + +如果 `ir-keytable -t` 完全无输出,优先检查: + +- 节点是不是 `okay` +- 选的是 `ircrx0/1` 还是 `rircrx0/1` +- 时钟 / reset 是否正常 +- 硬件接的是不是已经解调后的 IR-RX 信号 +- 引脚复用是否正确 +- `linux,rc-map-name` / 协议解码器是否匹配 + +## FAQ + +### 1. K3 用的是不是 K3 专用 IR 驱动? + +不是。 + +按当前 SDK 实现,K3 仍然复用: + +- `drivers/media/rc/ir-spacemit-k1.c` +- `compatible = "spacemit-k1,irc"` + +### 2. K3 上有几个 IR-RX 控制器? + +当前 DTS 里能确认 4 个: + +- `ircrx0` +- `ircrx1` +- `rircrx0` +- `rircrx1` + +### 3. 为什么板级 DTS 只写 `status = "okay";` 也能工作? + +因为寄存器、时钟、复位、中断这些基础资源已经在 `k3.dtsi` / `k3-rdomain.dtsi` 里定义好了。板级 DTS 通常只需要选择启用哪个控制器,再补充必要的 pinctrl / keymap 即可。