1 QUP原理

STM32(以及绝大多数传统 MCU)与高通(以及现代先进应用处理器 SoC)在底层外设的设计哲学上,走的是两条完全不同的进化路线。习惯的 STM32 属于“专职专责”的硬件硬编码架构,而高通 QUPv3 则属于“软硬件可编程的通用流处理架构”。

在 STM32 里面,查数据手册,会清晰地看到 I2C1SPI1TIM1(PWM) 都有各自独立的物理基地址、独立的硬件状态机和内部寄存器。意法半导体(ST)在设计芯片时,硅片上这块区域画的就是 I2C 的逻辑门,那块区域画的就是 SPI 的移位寄存器。它们井水不犯河水。

每个外设的寄存器行为在出厂时就死掉了,完全由硬件逻辑控制,几乎没有软件开销,响应极其精准(符合工业控制的死脑筋需求)。

高通的 SoC(尤其是像 AR1+ 这种物理尺寸暴减 26% 的特化芯片)面临的生存环境极为恶劣:寸土寸金的硅片面积、极其复杂的客户外设需求、以及对引脚数量近乎变态的压缩。

于是,高通采用了 QUPv3 (Qualcomm Universal Peripheral) 架构。在 QUPv3 模块内部,每一个所谓的 SE (Serial Engine,串行引擎),本质上是一台微型的、高度优化的硬件流处理器。个 SE 内部没有焊死任何具体的 I2C 或 SPI 逻辑,它只包含通用的数据 FIFO、移位器、时钟分频器和一个可控的状态机控制器。这也是QUP 最小可编程单元,单 SE 同一时间仅支持一种协议(UART/SPI/I2C/I3C),由固件决定协议;SoC 内置两组 QUP 模块,合计16 个 SE(QUP0+QUP1 各 8 路)。

当系统开机启动时(在 TrustZone 或者是音频 DSP 微内核初始化的阶段),系统会向这个 SE 注入一段极小的微代码(Microcode)或协议固件。

同时还有GPI(通用 DMA 引擎)概念。每个 SE 配套独立 GPI,成对生产者 / 消费者 DMA 通道,负责内存与外设 FIFO 高速搬运;TZ 负责分配 GPI 通道权限(GPI Pipe 分配)

  • 为什么设备树里叫 i2c_geni 高通的 QUPv3 驱动在内核里的名字叫 i2c_geni(或者是 spi_geni)。这个 GENI 就是 Generic Interface(通用接口) 的缩写。它时刻在提醒你:它不是一个纯粹的 I2C 驱动,而是一个“把通用接口强行伪装成 I2C”的驱动。

  • 为什么高通的 I2C 能调到惊人的高频,甚至容易波形畸变? 因为它是用高频流处理器模拟出来的时序。如果你的 Pinctrl(上拉电阻、驱动电流)和 QUPv3 的时钟分频没匹配好,由于它的硬件不是纯 ASIC,在高速传输下对电气特性的容错率有时比 STM32 还要敏感,因此在调眼镜的传感器和屏幕时,更需要死盯着 Pinctrl 的配置。

从本质和设计哲学上来说,高通的 QUPv3 串行引擎(Serial Engine)和树莓派 RP2040/RP2350 芯片里的 PIO(Programmable I/O,可编程输入输出)在思想上完全是同胞兄弟!

不过。。。

  • 树莓派 PIO —— “轻量级、自由度极高的百变怪” 树莓派的 PIO 是完全对开发者开放的。你可以发挥无限的想象力,用 9 条汇编指令去手写任何奇葩的非标协议(比如强行用 PIO 去模拟一个 VGA 显卡信号,或者解码旧式红外遥控器)。它非常轻量,每个状态机只有几十个字的指令空间。

  • 高通 QUPv3 —— “重量级、工业级标准的黑盒流处理器” 高通的 QUPv3 内部其实是一个非常复杂的 GENI(Generic Interface)状态机管理器

    • 不对外开放写汇编:高通不允许普通开发者自己去写 QUPv3 的底层汇编时序。高通已经把最标准的 I2C、SPI、UART、UART-LIN 等工业级标准协议的固件包全部写好、固化在系统底层了。

    • 更高的吞吐量与 DMA 集成:QUPv3 内部深度绑定了高通专用的 BAM / GSI(Generic Software Interface)DMA 引擎。这意味着在 AR1+ 上,高通的 I2C/SPI 可以直接通过硬件 DMA 大批量、无感地向内存(DDR)吞吐传感器数据,其数据处理带宽和抗死锁能力,是 MCU 级别的 PIO 无法比拟的。

高通的QUP具有以下模式:

FIFO:CPU 轮询,低速调试、普通外设,HLOS 常规使用;
CPU_DMA:传统 CPU 侧 DMA;
GSI:硬件直通零 CPU 干预,高速、安全域 TZ 专用(指纹、NFC eSE)。

上面三种模式,FIFO模式和CPU_DMA模型切换,可以由驱动来完成。而GSI模式,需要在设备树中增加dmas配置。

统一兼容三大低速总线:

UART:波特率 300bps~4Mbps,支持 2 线无流控 / 4 线 CTS/RTS 硬件流控;
SPI:最高 50MHz,4~32bit 位宽,单总线最多 4 片选,支持全 / 半双工、半周期采样补偿;
I2C:标准多从设备总线,支持 400kHz 高速,适配触摸、传感器、EEPROM;
外设用途:蓝牙 HCI、触控、各类传感器、NFC、指纹、串口调试、运动控制。

协议 最大速率 关键特性 典型应用场景
UART 4 Mbps 支持 HS (DMA) 和 LS (FIFO) 模式。 蓝牙 (WCN)、调试串口、IrDA。
SPI 50 MHz 仅支持主模式;4-32 bit 数据宽度;可编程间隔。 传感器、指纹识别、触摸屏。
I2C 1 MHz 支持标准 (100k)、快速 (400k)、快速+ (1M) 模式。 充电 IC、NFC、LED 驱动。
I3C 高于 I2C 无需上拉电阻;性能更高。 IMU 传感器 (传感枢纽)。

2 命令行操作

cat /sys/kernel/debug/pinctrl/f000000.pinctrl/pinmux-pins

输出是

sh-5.0# cat /sys/kernel/debug/pinctrl/*.pinctrl/pinmux-pins
Pinmux settings per pin
Format: pin (name): mux_owner gpio_owner hog?
pin 0 (GPIO_0): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 1 (GPIO_1): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 2 (GPIO_2): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 3 (GPIO_3): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 4 (GPIO_4): 984000.i2c (GPIO UNCLAIMED) function gpio group gpio4
pin 5 (GPIO_5): 984000.i2c (GPIO UNCLAIMED) function gpio group gpio5
pin 6 (GPIO_6): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 7 (GPIO_7): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 8 (GPIO_8): 990000.i2c (GPIO UNCLAIMED) function gpio group gpio8
pin 9 (GPIO_9): 990000.i2c (GPIO UNCLAIMED) function gpio group gpio9
pin 10 (GPIO_10): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 11 (GPIO_11): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 12 (GPIO_12): 994000.i2c (GPIO UNCLAIMED) function gpio group gpio12
pin 13 (GPIO_13): 994000.i2c (GPIO UNCLAIMED) function gpio group gpio13
pin 14 (GPIO_14): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 15 (GPIO_15): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 16 (GPIO_16): 998000.qcom,qup_uart (GPIO UNCLAIMED) function qup6 group gpio16
pin 17 (GPIO_17): 998000.qcom,qup_uart (GPIO UNCLAIMED) function qup6 group gpio17
pin 18 (GPIO_18): 998000.qcom,qup_uart (GPIO UNCLAIMED) function qup6 group gpio18
pin 19 (GPIO_19): 998000.qcom,qup_uart (GPIO UNCLAIMED) function qup6 group gpio19
pin 20 (GPIO_20): b0000000.qcom,cnss-qca6390 (GPIO UNCLAIMED) function gpio group gpio20
pin 21 (GPIO_21): vendor:bt_qca6390 (GPIO UNCLAIMED) function gpio group gpio21
pin 22 (GPIO_22): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 23 (GPIO_23): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 24 (GPIO_24): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 25 (GPIO_25): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 26 (GPIO_26): soc:wsa_spkr_en1_pinctrl (GPIO UNCLAIMED) function gpio group gpio26
pin 27 (GPIO_27): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 28 (GPIO_28): 980000.spi (GPIO UNCLAIMED) function gpio group gpio28
pin 29 (GPIO_29): 980000.spi (GPIO UNCLAIMED) function gpio group gpio29
pin 30 (GPIO_30): 980000.spi (GPIO UNCLAIMED) function gpio group gpio30
pin 31 (GPIO_31): 980000.spi (GPIO UNCLAIMED) function gpio group gpio31
pin 32 (GPIO_32): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 33 (GPIO_33): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 34 (GPIO_34): a90000.qcom,qup_uart (GPIO UNCLAIMED) function qup12 group gpio34
pin 35 (GPIO_35): a90000.qcom,qup_uart (GPIO UNCLAIMED) function qup12 group gpio35
pin 36 (GPIO_36): a94000.qcom,qup_uart (GPIO UNCLAIMED) function qup13 group gpio36
pin 37 (GPIO_37): a94000.qcom,qup_uart (GPIO UNCLAIMED) function qup13 group gpio37
pin 38 (GPIO_38): a94000.qcom,qup_uart (GPIO UNCLAIMED) function qup13 group gpio38
pin 39 (GPIO_39): a94000.qcom,qup_uart (GPIO UNCLAIMED) function qup13 group gpio39
pin 40 (GPIO_40): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 41 (GPIO_41): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 42 (GPIO_42): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 43 (GPIO_43): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 44 (GPIO_44): 884000.i2c (GPIO UNCLAIMED) function gpio group gpio44
pin 45 (GPIO_45): 884000.i2c (GPIO UNCLAIMED) function gpio group gpio45
pin 46 (GPIO_46): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 47 (GPIO_47): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 48 (GPIO_48): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 49 (GPIO_49): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 50 (GPIO_50): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 51 (GPIO_51): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 52 (GPIO_52): 88c000.spi (GPIO UNCLAIMED) function gpio group gpio52
pin 53 (GPIO_53): 88c000.spi (GPIO UNCLAIMED) function gpio group gpio53
pin 54 (GPIO_54): 88c000.spi (GPIO UNCLAIMED) function gpio group gpio54
pin 55 (GPIO_55): 88c000.spi (GPIO UNCLAIMED) function gpio group gpio55
pin 56 (GPIO_56): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 57 (GPIO_57): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 58 (GPIO_58): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 59 (GPIO_59): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 60 (GPIO_60): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 61 (GPIO_61): soc:display_gpio_regulator@1 f000000.pinctrl:389 function gpio group gpio61
pin 62 (GPIO_62): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 63 (GPIO_63): 2-002b f000000.pinctrl:391 function gpio group gpio63
pin 64 (GPIO_64): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 65 (GPIO_65): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 66 (GPIO_66): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 67 (GPIO_67): (MUX UNCLAIMED) f000000.pinctrl:395
pin 68 (GPIO_68): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 69 (GPIO_69): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 70 (GPIO_70): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 71 (GPIO_71): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 72 (GPIO_72): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 73 (GPIO_73): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 74 (GPIO_74): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 75 (GPIO_75): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 76 (GPIO_76): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 77 (GPIO_77): (MUX UNCLAIMED) f000000.pinctrl:405
pin 78 (GPIO_78): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 79 (GPIO_79): 1c00000.qcom,pcie f000000.pinctrl:407 function gpio group gpio79
pin 80 (GPIO_80): 1c00000.qcom,pcie (GPIO UNCLAIMED) function pci_e0 group gpio80
pin 81 (GPIO_81): 1c00000.qcom,pcie f000000.pinctrl:409 function gpio group gpio81
pin 82 (GPIO_82): 1c08000.qcom,pcie f000000.pinctrl:410 function gpio group gpio82
pin 83 (GPIO_83): 1c08000.qcom,pcie (GPIO UNCLAIMED) function pci_e1 group gpio83
pin 84 (GPIO_84): 1c08000.qcom,pcie f000000.pinctrl:412 function gpio group gpio84
pin 85 (GPIO_85): 1c10000.qcom,pcie f000000.pinctrl:413 function gpio group gpio85
pin 86 (GPIO_86): 1c10000.qcom,pcie (GPIO UNCLAIMED) function pci_e2 group gpio86
pin 87 (GPIO_87): 1c10000.qcom,pcie f000000.pinctrl:415 function gpio group gpio87
pin 88 (GPIO_88): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 89 (GPIO_89): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 90 (GPIO_90): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 91 (GPIO_91): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 92 (GPIO_92): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 93 (GPIO_93): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 94 (GPIO_94): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 95 (GPIO_95): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 96 (GPIO_96): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 97 (GPIO_97): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 98 (GPIO_98): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 99 (GPIO_99): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 100 (GPIO_100): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 101 (GPIO_101): (MUX UNCLAIMED) f000000.pinctrl:429
pin 102 (GPIO_102): (MUX UNCLAIMED) f000000.pinctrl:430
pin 103 (GPIO_103): (MUX UNCLAIMED) f000000.pinctrl:431
pin 104 (GPIO_104): (MUX UNCLAIMED) f000000.pinctrl:432
pin 105 (GPIO_105): (MUX UNCLAIMED) f000000.pinctrl:433
pin 106 (GPIO_106): (MUX UNCLAIMED) f000000.pinctrl:434
pin 107 (GPIO_107): (MUX UNCLAIMED) f000000.pinctrl:435
pin 108 (GPIO_108): (MUX UNCLAIMED) f000000.pinctrl:436
pin 109 (GPIO_109): (MUX UNCLAIMED) f000000.pinctrl:437
pin 110 (GPIO_110): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 111 (GPIO_111): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 112 (GPIO_112): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 113 (GPIO_113): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 114 (GPIO_114): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 115 (GPIO_115): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 116 (GPIO_116): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 117 (GPIO_117): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 118 (GPIO_118): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 119 (GPIO_119): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 120 (GPIO_120): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 121 (GPIO_121): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 122 (GPIO_122): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 123 (GPIO_123): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 124 (GPIO_124): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 125 (GPIO_125): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 126 (GPIO_126): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 127 (GPIO_127): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 128 (GPIO_128): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 129 (GPIO_129): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 130 (GPIO_130): soc:wsa_spkr_en2_pinctrl (GPIO UNCLAIMED) function gpio group gpio130
pin 131 (GPIO_131): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 132 (GPIO_132): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 133 (GPIO_133): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 134 (GPIO_134): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 135 (GPIO_135): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 136 (GPIO_136): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 137 (GPIO_137): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 138 (GPIO_138): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 139 (GPIO_139): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 140 (GPIO_140): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 141 (GPIO_141): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 142 (GPIO_142): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 143 (GPIO_143): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 144 (GPIO_144): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 145 (GPIO_145): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 146 (GPIO_146): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 147 (GPIO_147): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 148 (GPIO_148): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 149 (GPIO_149): (MUX UNCLAIMED) f000000.pinctrl:477
pin 150 (GPIO_150): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 151 (GPIO_151): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 152 (GPIO_152): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 153 (GPIO_153): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 154 (GPIO_154): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 155 (GPIO_155): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 156 (GPIO_156): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 157 (GPIO_157): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 158 (GPIO_158): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 159 (GPIO_159): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 160 (GPIO_160): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 161 (GPIO_161): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 162 (GPIO_162): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 163 (GPIO_163): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 164 (GPIO_164): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 165 (GPIO_165): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 166 (GPIO_166): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 167 (GPIO_167): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 168 (GPIO_168): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 169 (GPIO_169): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 170 (GPIO_170): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 171 (GPIO_171): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 172 (GPIO_172): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 173 (GPIO_173): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 174 (GPIO_174): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 175 (GPIO_175): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 176 (GPIO_176): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 177 (GPIO_177): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 178 (GPIO_178): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 179 (GPIO_179): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 180 (SDC2_CLK): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 181 (SDC2_CMD): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 182 (SDC2_DATA): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 183 (UFS_RESET): (MUX UNCLAIMED) f000000.pinctrl:511

可以看到,此时的QUP分配如下:

硬件 QUP 编号 内核物理基地址 (Hex) 占用的物理引脚 (Pins) 当前被激活的协议/功能 内核中的驱动设备节点名称 剩余未用引脚 (可当常规GPIO)
QUP_0 980000 GPIO 28, 29, 30, 31 SPI 模式 (4线满载) 980000.spi 无(全被 SPI 占用)
QUP_1 984000 GPIO 4, 5, 6, 7 I2C 模式 (仅用2线) 984000.i2c GPIO 6, GPIO 7
QUP_4 990000 GPIO 8, 9, 10, 11 I2C 模式 (仅用2线) 990000.i2c GPIO 10, GPIO 11
QUP_5 994000 GPIO 12, 13, 14, 15 I2C 模式 (仅用2线) 994000.i2c GPIO 14, GPIO 15
QUP_6 998000 GPIO 16, 17, 18, 19 UART 模式 (4线带流控) 998000.qcom,qup_uart 无(全被 串口 占用)
QUP_12 a90000 GPIO 32, 33, 34, 35 UART 模式 (仅用2线) a90000.qcom,qup_uart GPIO 32, GPIO 33
QUP_13 a94000 GPIO 36, 37, 38, 39 UART 模式 (4线带流控) a94000.qcom,qup_uart 无(全被 串口 占用)
QUP_14 884000 GPIO 44, 45, 46, 47 I2C 模式 (仅用2线) 884000.i2c GPIO 46, GPIO 47
QUP_15 88c000 GPIO 52, 53, 54, 55 SPI 模式 (4线满载) 88c000.spi 无(全被 SPI 占用)

3 配置

3.1 前置配置

主要有以下几个:

1 QUPAC 访问控制(QUPAC_Access.c)。全平台 SE 资源分配总入口,决定SE 归属 EE、协议、传输模式,核心数组字段:
{SE编号, 协议, 模式, 所有者AC, bAllowFifo, bLoad固件, bModExcl独占}

3 QUP SE 固件加载机制。每个 SE 必须加载对应协议固件(UART/SPI/I2C),固件存放路径common/core_qupv3fw/<芯片>/qupv3fw.elf。

3.2 协议配置

首先,目前没拿到板子,所有的操作都是根据网上或者AI来的。对于QUP,应该是自己不用写什么代码,基本上用设备树就可以了。这里主要有三个部分。

DTS 设备树(内核侧)。定义 SE 基地址、时钟、中断、状态status="ok"启用。

文件路径:kernel/<version>/arch/arm64/boot/dts/vendor/qcom/<target>-qupv3.dtsi

  • 找到对应的 qupv3_seX_protocol 节点。
  • 将 status = "disabled"; 修改为 status = "ok";
  • 示例:
  • &qupv3_se3_spi {
        status = "ok";
        pinctrl-0 = <&qup_se3_spi_active>;
        pinctrl-1 = <&qup_se3_spi_sleep>;
        pinctrl-names = "default", "sleep";
        
        /* 指纹传感器挂载在 CS1 */
        fingerprint@1 {
            compatible = "qcom,fingerprint"; 
            reg = <1>; /* <--- 这里必须改为 1,表示使用 CS1 */
            spi-max-frequency = <24000000>;
            
            /* 辅助引脚 */
            interrupt-parent = <&tlmm>;
            interrupts = <20 IRQ_TYPE_EDGE_RISING>; 
        };
    };
     

Pinctrl 引脚配置。<芯片>-pinctrl.dtsi,配置 GPIO 复用功能、驱动强度、上下拉、休眠引脚状态。

在设备树中定义引脚的复用功能。

/* 示例:在 tlmm 节点下 */
qup_se3_spi_active: qup_se3_spi_active {
    mux {
        /* 增加 gpio41 */
        pins = "gpio16", "gpio17", "gpio18", "gpio41"; 
        function = "qup0_se3";
    };
    config {
        pins = "gpio16", "gpio17", "gpio18", "gpio41";
        drive-strength = <16>;
        bias-disable;
    };
};

TZ 权限表。QUPAC_Access.c 分配 SE 归属、协议、GSI/FIFO 模式。

你必须修改 TZ 侧的配置文件,否则内核无法控制 SPI 控制器。

  • 文件: trustzone_images/core/settings/buses/qup_accesscontrol/qupv3/config/<target>/QUPAC_Access.c
  • 配置内容: 找到 QUP0_SE3 对应的结构体,确保包含以下设置:
    • protocol 设为 QUPV3_PROTOCOL_SPI
    • mode 通常设为 QUPV3_MODE_GSI (指纹通常是大块数据传输) 或 FIFO
    • ns_access 设为 ACCESS_FULL (允许非安全域即 Kernel 访问)

原来的DTS

&old_i2c_1 {
    status = okay;
    imu@68 { ... };
}

使用QUP I2C的DTS

&qupv3_se12_i2c { // 替换父节点为QUP I2C
    status = "okay";
    // 高通独有必须补充:双态pinctrl、时钟、DMA、电源域
    pinctrl-names = "default", "sleep";
    pinctrl-0 = <&qup12_active>;
    pinctrl-1 = <&qup12_sleep>;
    dmas = <&blsp_dma ...>, <&blsp_dma ...>;
    clocks = <&gcc ...>, <&gcc ...>;
    power-domains = <&rpmhpd ...>;

    imu@68 { // 你的外设节点内容基本照搬旧平台
        compatible = "xxx,mpu6050";
        reg = <0x68>;
        reset-gpios = <&tlmm ...>;
        interrupts = <...>;
        vdd-supply = <&pmic_lxx>;
        qcom,slpi-vote; // 高通低功耗专属标签,按需加
    };
}

或者SPI的例子

qupv3_se3_spi: spi@98c000 {
    compatible = "qcom,spi-geni";
    reg = <0x98c000 0x4000>;
    #address-cells = <1>;
    #size-cells = <0>;
    reg-names = "se_phys";
    interrupts = <GIC_SPI 604 IRQ_TYPE_LEVEL_HIGH>;
    clock-names = "se-clk";
    clocks = <&gcc GCC_QUPV3_WRAP0_S3_CLK>;
    pinctrl-names = "default", "sleep";
    pinctrl-0 = <&qupv3_se3_spi_active>, <&qupv3_se3_spi_cs0_active>;
    pinctrl-1 = <&qupv3_se3_spi_sleep>, <&qupv3_se3_spi_cs0_sleep>;
    dmas = <&gpi_dma0 0 3 1 64 0>,
           <&gpi_dma0 1 3 1 64 0>;
    dma-names = "tx", "rx";
    spi-max-frequency = <50000000>;
    status = "disabled";  /* 需要在目标dts中 enable */
};

增加tlmm的配置

tlmm {
    qup12_active: qup12_active {
        pins = "gpioXX", "gpioYY";
        function = "qup12"; // 复用为QUP I2C功能
        drive-strength = <2>;
        bias-pull-up;
    };
    qup12_sleep: qup12_sleep {
        pins = "gpioXX", "gpioYY";
        function = "gpio"; // 休眠切普通GPIO高阻输入
        bias-disable;
    };
};

之后要打开编译宏,不过我感觉一般都是开的。

CONFIG_I2C_QUP=y

CONFIG_BLSP_BAM=y

检查还是简单,编译出img后烧写即可。

烧录 dtb → dmesg | grep qup12

正常打印:qupv3_se12_i2c probe success、pinctrl state default selected;

报错:failed to get pinctrl state,就是 tlmm 里标签缺失 / 名字写错。

/* arch/arm64/boot/dts/qcom/你的单板.dts */

&qupv3_se3_spi {
    status = "ok";
    pinctrl-names = "default", "sleep";
    pinctrl-0 = <&qup_se3_spi_active>;
    pinctrl-1 = <&qup_se3_spi_sleep>;
    
    /* 标准子节点写法 */
    fingerprint@1 {
        compatible = "goodix,gf3208"; /* 用于和 C 代码匹配的字符串 */
        reg = <1>;                    /* 使用 CS1 片选线 */
        spi-max-frequency = <24000000>; /* 24MHz,不需要在C代码里写死了 */
        spi-cpol;                     /* 如果需要,可以在这里配置时钟极性 */
        spi-cpha;
        
        /* 辅助引脚 */
        interrupt-parent = <&tlmm>;
        interrupts = <20 IRQ_TYPE_EDGE_RISING>; 
    };
};

4 驱动示例

标准配置和驱动如下:

/* arch/arm64/boot/dts/qcom/你的单板.dts */

&qupv3_se3_spi {
    status = "ok";
    pinctrl-names = "default", "sleep";
    pinctrl-0 = <&qup_se3_spi_active>;
    pinctrl-1 = <&qup_se3_spi_sleep>;
    
    /* 标准子节点写法 */
    fingerprint@1 {
        compatible = "goodix,gf3208"; /* 用于和 C 代码匹配的字符串 */
        reg = <1>;                    /* 使用 CS1 片选线 */
        spi-max-frequency = <24000000>; /* 24MHz,不需要在C代码里写死了 */
        spi-cpol;                     /* 如果需要,可以在这里配置时钟极性 */
        spi-cpha;
        
        /* 辅助引脚 */
        interrupt-parent = <&tlmm>;
        interrupts = <20 IRQ_TYPE_EDGE_RISING>; 
    };
};

驱动,这种写法就是最为推荐的。

#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/of.h>

/* 自定义的指纹设备结构体 */
struct gf_device {
    struct spi_device *spi;
    void *spi_buffer;
    /* 其他成员如 gpio, irq 等 */
};

/* 1. 标准的 Probe 初始化函数 */
static int gf_spi_probe(struct spi_device *spi)
{
    struct gf_device *gf_dev;

    dev_info(&spi->dev, "gf_spi probe started!\n");

    /* 分配驱动私有结构体 */
    gf_dev = devm_kzalloc(&spi->dev, sizeof(*gf_dev), GFP_KERNEL);
    if (!gf_dev)
        return -ENOMEM;

    /* 核心:内核已经把设备树里的参数(CS=1, 速度=24M)全部解析好并填进 spi 指针里了! */
    gf_dev->spi = spi;
    spi_set_drvdata(spi, gf_dev); /* 将私有数据挂载到 spi 设备中 */

    /* 分配缓冲区 */
    gf_dev->spi_buffer = devm_kzalloc(&spi->dev, 4096, GFP_KERNEL);
    if (!gf_dev->spi_buffer)
        return -ENOMEM;

    /* 在这里继续初始化字符设备、注册中断等操作... */

    dev_info(&spi->dev, "gf_spi probed successfully on CS%d\n", spi->chip_select);
    return 0;
}

/* 2. 标准的 Remove 卸载函数 */
static int gf_spi_remove(struct spi_device *spi)
{
    struct gf_device *gf_dev = spi_get_drvdata(spi);
    
    /* 释放资源(注:使用 devm_ 开头的内存分配函数会在卸载时自动释放,无需手动 kfree) */
    return 0;
}

/* 3. 设备树匹配表(必须与 DTS 中的 compatible 一致) */
static const struct of_device_id gf_spi_of_match[] = {
    { .compatible = "goodix,gf3208", },
    { /* 哨兵,必须留空 */ }
};
MODULE_DEVICE_TABLE(of, gf_spi_of_match);

/* 4. 传统的本地匹配表(防老版本内核或非DT平台兼容) */
static const struct spi_device_id gf_spi_id[] = {
    { "gf_spi", 0 },
    { }
};
MODULE_DEVICE_TABLE(spi, gf_spi_id);

/* 5. 标准的 SPI 驱动结构体 */
static struct spi_driver gf_spi_driver = {
    .driver = {
        .name = "goodix_fp",
        .of_match_table = gf_spi_of_match, /* 绑定设备树匹配表 */
    },
    .probe = gf_spi_probe,
    .remove = gf_spi_remove,
    .id_table = gf_spi_id,
};

/* 6. 宏函数:自动生成 module_init 和 module_exit,一键注册 */
module_spi_driver(gf_spi_driver);

MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Author");
MODULE_DESCRIPTION("Standard Goodix SPI Driver");

此时,可以直接在驱动中控制,这种写法野一些。下面是一个SPI驱动的例子。

void gf_reg_spi_driver(struct gf_device *gf_dev)
{
    struct device_node *np = NULL;
    struct platform_device *pdev = NULL;
    struct spi_controller *ctlr = NULL;
    struct spi_device *spi = NULL;

    /* 1. 查找SPI控制器 */
    np = of_find_compatible_node(NULL, NULL, "qcom,spi-geni");
    while (np) {
        u32 reg = 0;
        of_property_read_u32(np, "reg", &reg);
        if (reg == 0x98c000) {  /* SE3的基地址 */
            /* 2. 获取platform_device */
            pdev = of_find_device_by_node(np);  /* 需要 #include <linux/of_platform.h> */
            if (pdev) {
                /* 3. 获取spi_controller */
                ctlr = platform_get_drvdata(pdev);
                if (ctlr) {
                    /* 4. 分配并添加SPI设备 */
                    spi = spi_alloc_device(ctlr);
                    if (spi) {
                        spi->chip_select = 0;
                        spi->max_speed_hz = 50000000;
                        spi->mode = SPI_MODE_0;
                        spi->bits_per_word = 8;
                        strlcpy(spi->modalias, "gf_spi", sizeof(spi->modalias));
                        if (spi_add_device(spi) == 0) {
                            gf_dev->spi = spi;
                            gf_dev->spi_buffer = kzalloc(4096, GFP_KERNEL);
                        }
                    }
                }
            }
            of_node_put(np);
            return;
        }
        np = of_find_compatible_node(np, NULL, "qcom,spi-geni");
    }
}

无法发现 SPI 设备: 检查 dmesg | grep spi,看是否有 qup_geni_spi 的报错。如果有 "access denied",说明 TZ 配置没生效。

这部分主要还是要看高通的文档。详细的UART,I2C的配置,后面再看写不写吧。

Qualcomm Documentation

05-SA8155 QNX通过QUB配置GPIO/INT/SPI/I2C/SPI等_i2cdbgr-CSDN博客

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐