Linux之MHI(常用于PCIe接口的5G模组)
Linux之MHI(常用于PCIe接口的5G模组)
1. MHI 是什么
MHI = Modem Host Interface,最初是 Qualcomm 定义的一套主机与 Modem/加速器之间的通信协议。
它常见承载在 PCIe 上,用于主机和 PCIe Modem 之间高速通信,例如:
- Qualcomm SDX55 / SDX62 / SDX65 / SDX75 等 5G Modem;
- 一些车载/工业网关里的蜂窝模组;
- 通过 PCIe 接入的 AI/加速器设备,也可能复用 MHI 框架。
可以把 MHI 理解为:
在 PCIe 之上的一套基于 Ring/DMA/中断的通信协议,向操作系统暴露多个逻辑通道,用于承载 QMI、MBIM、AT、DIAG、LOG、网络数据等。
它和 QMI、MBIM 的关系是:
MHI 是传输通道/总线;
QMI / MBIM / AT / DIAG 是运行在 MHI 通道上的协议或业务。
例如一个 5G 模组通过 PCIe 接入 Linux 后,可能同时暴露:
MHI channel: IP_HW0 -> 网络数据
MHI channel: MBIM -> MBIM 控制
MHI channel: QMI/QRTR -> QMI 控制
MHI channel: DUN/AT -> AT 串口
MHI channel: DIAG -> 诊断
MHI channel: LOGGING -> 日志
2. Linux PCIe MHI 总体架构
Linux 中 PCIe MHI 不是一个单独驱动,而是一套分层驱动体系:
+--------------------------------------------------+
| 用户态 |
| ModemManager / qmicli / mbimcli / AT工具 / 自研程序 |
+--------------------------------------------------+
| 客户端驱动 |
| mhi_wwan_ctrl / mhi_wwan_mbim / mhi_net / |
| mhi_uci / qrtr_mhi / 自定义 mhi client driver |
+--------------------------------------------------+
| MHI Core |
| channel/event/command/ring/power/firmware load |
+--------------------------------------------------+
| MHI Controller Driver |
| mhi_pci_generic / qcom mhi glue / vendor driver |
+--------------------------------------------------+
| PCIe 子系统 |
| BAR0/MMIO, MSI/MSI-X, DMA, Bus Master, PM |
+--------------------------------------------------+
| 硬件 Modem/加速器 |
| MHI device side |
+--------------------------------------------------+
3. 内核源码位置
不同内核版本目录略有差异。常见路径如下:
drivers/bus/mhi/host/
main.c
init.c
boot.c
pci_generic.c
...
drivers/bus/mhi/ep/
MHI endpoint 侧驱动,如果 Linux 跑在 device/endpoint 侧时使用
drivers/net/wwan/
mhi_wwan_ctrl.c
mhi_wwan_mbim.c
net/qrtr/
qrtr-mhi 或类似 QRTR over MHI 支持
4. PCIe 层需要什么资源
对于主机侧 Linux,PCIe MHI 设备首先是一个 PCIe 设备。lspci 通常能看到类似 Qualcomm 的设备:
lspci -nn | grep -i qualcomm
lspci -vvv -s <BDF>
MHI 驱动依赖以下 PCIe 资源:
4.1 BAR0 / MMIO
MHI 寄存器通常映射在 PCIe BAR0 上,包括:
BHI / BHIe 寄存器
MHI 控制寄存器
Channel Doorbell
Event Doorbell
中断状态寄存器
电源状态寄存器
Linux MHI controller driver 一般会做:
pci_request_regions(pdev, "mhi");
mhi_cntrl->reg_base = pci_iomap(pdev, 0, 0);
如果 BAR 没有分配成功,常见现象是:
can't allocate BAR
MHI register base invalid
PCIe device not ready
4.2 MSI / MSI-X 中断
MHI 是中断驱动的。常见中断用途:
数据事件中断
控制事件中断
BHI 固件加载中断
PM/wake 中断
错误中断
通常 MHI controller 会申请一个或多个 MSI/MSI-X vector。
如果 BIOS、PCIe Root Complex 或设备树/ACPI 配置异常,可能出现:
No IRQ
mhi_power_up timeout
event ring not progressing
4.3 DMA / Bus Master
MHI 的核心数据路径是 DMA:
主机内存中的 ring buffer / context / data buffer
被 PCIe Modem 通过 DMA 读取或写入
因此必须:
pci_set_master(pdev);
dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
如果启用了 IOMMU/SMMU,还需要保证 PCIe 设备对该内存有 DMA 权限。
常见错误:
DMA mapping error
IOMMU fault
SMMU context fault
4.4 电源和边带信号
实际平台里,Modem 往往还涉及:
PERST#
WAKE#
CLKREQ#
模组供电
SIM 电源
天线/射频使能
在 x86 平台,这些可能由 BIOS/ACPI 管理;在 ARM 嵌入式平台,可能需要设备树、GPIO、regulator、pinctrl 配合。
5. MHI 通信模型
MHI 的核心抽象是:
Channel
Transfer Ring
Event Ring
Command Ring
Doorbell
DMA buffer
5.1 Channel
Channel 是逻辑通道。一个 MHI 设备通常由多个 channel 组成。
常见 channel 举例:
IP_HW0 硬件加速 IP 数据
IP_SW0 软件 IP 数据
MBIM MBIM 控制/数据
QMI/QRTR QMI 控制
DUN AT 串口
DIAG 诊断
LOGGING 日志
LOOPBACK 测试
SAHI 某些控制/辅助通道
Linux MHI core 会根据控制器配置和模组状态创建若干 mhi_device。
可以查看:
ls /sys/bus/mhi/devices/
通常会看到包含通道名的设备,例如类似:
..._IP_HW0
..._MBIM
..._DUN
..._DIAG
具体命名依赖内核版本和模组固件。
5.2 Transfer Ring,TRE
每个 channel 有对应的传输环,里面存放传输描述符,通常称为:
TRE / Transfer Ring Element
一个 TRE 通常包含:
数据缓冲区物理地址
长度
控制标志
方向信息或完成控制信息
主机向设备发数据时,大致流程是:
1. 客户端驱动准备 skb/buffer;
2. MHI core 做 DMA mapping;
3. 生成 TRE,写入 channel transfer ring;
4. 写 doorbell 通知 Modem;
5. Modem DMA 读取数据;
6. Modem 写 event ring 并触发 MSI;
7. Linux MHI core 处理完成,回调客户端驱动。
5.3 Event Ring
Event Ring 用于设备向主机上报事件:
传输完成
命令完成
状态变化
电源事件
错误事件
MHI controller driver 收到中断后,会处理 event ring。
简化逻辑:
MSI/IRQ
-> mhi_irq_handler()
-> 找到对应 event ring
-> 读取 event descriptor
-> 判断事件类型
-> 调用 channel/client callback
5.4 Command Ring
Command Ring 用于主机向设备发送控制命令,例如:
START CHANNEL
STOP CHANNEL
RESET CHANNEL
NOOP
电源相关命令
命令执行结果通过 control/event ring 返回。
5.5 Doorbell
Doorbell 是一种 MMIO 通知机制。
例如主机往 transfer ring 里放了新的 TRE 后,需要写 channel doorbell 通知设备:
Host writes CHDB register
-> Modem knows there are new transfer requests
事件方向上也有 event doorbell 或类似机制,用于主机告知设备事件消费情况。
6. MHI 控制器驱动
MHI controller driver 是连接 PCIe 和 MHI core 的胶水层。
主线常见的是:
pci_generic.c
它可以匹配 PCIe Vendor/Device ID,然后初始化 MHI controller。
一个典型 probe 流程如下:
PCIe 枚举发现设备
-> mhi pci controller probe
-> 使能 PCIe 设备
-> 申请 BAR
-> 设置 DMA mask
-> pci_set_master()
-> 申请中断
-> 分配 mhi_controller
-> 填写寄存器基址、channel/event 配置
-> 注册 MHI controller
-> MHI core 初始化
-> 固件加载/启动
-> MHI power up
-> 创建 mhi_device
-> client driver probe
7. MHI 固件加载与启动
很多 PCIe Modem 上电后并不直接进入完整运行态,而是需要主机下载固件或触发启动。
MHI 在早期启动阶段常用:
BHI = Boot Host Interface
BHIe = Boot Host Interface extended
BHI 主要用于:
固件下载
启动状态查询
错误状态上报
典型启动流程:
1. PCIe link up
2. Modem 处于 BOOT/BHI 状态
3. Linux MHI controller 分配 DMA buffer
4. 加载固件镜像到内存
5. 通过 BHI 寄存器告诉设备固件地址和长度
6. 写 BHI doorbell
7. 等待 BHI 中断或状态变化
8. Modem 校验并启动固件
9. MHI 进入 READY
10. MHI power up 到 M0
11. 创建 channel/client 设备
固件路径通常在:
/lib/firmware/
/lib/firmware/mhi/
常见固件名类似:
amss.mbn
sbl1.mbn
modem.mbn
firehose programmer
vendor specific firmware
具体名字取决于厂商和内核配置。
8. MHI 状态机
MHI 有设备状态和电源状态。
常见 MHI state:
MHI_STATE_RESET
MHI_STATE_READY
MHI_STATE_M0
MHI_STATE_M1
MHI_STATE_M2
MHI_STATE_M3
粗略理解:
| 状态 | 含义 |
|---|---|
| RESET | MHI 复位/未初始化 |
| READY | MHI 基础初始化完成,可进一步启动 |
| M0 | Active,正常工作 |
| M1 | 低功耗,可较快恢复 |
| M2 | 更深低功耗 |
| M3 | 挂起/关闭状态,通常需要唤醒或重新 power up |
此外还有:
SYS_ERR
FW_DL_ERR
POR
等异常或上电状态。
Linux MHI core 会维护类似:
device state
pm state
execution environment
channel state
event ring state
电源状态迁移大致:
RESET
-> READY
-> M0
M0
-> M1/M2/M3 低功耗
M3
-> wake -> M0
9. MHI 客户端驱动
MHI core 只负责传输,真正的业务由客户端驱动完成。
9.1 mhi_net
mhi_net 是一个比较基础的 MHI 网络驱动,把 MHI channel 暴露成 Linux netdev。
可能生成类似:
mhi0
rmnet_mhi0
wwan0
具体名字取决于驱动和配置。
它负责:
接收 IP 包
发送 IP 包
NAPI polling
DMA buffer 管理
netdev 统计
9.2 mhi_wwan_ctrl
这是 WWAN 控制框架驱动,通常配合 Linux WWAN 子系统。
它会创建:
/dev/wwan0
wwan port
control port
并可能注册多个端口:
QMI port
MBIM port
AT port
NMEA port
DIAG port
具体暴露哪些端口取决于模组和固件。
9.3 mhi_wwan_mbim
用于 MBIM 数据网络。
MBIM 是移动宽带常见控制/数据协议,Linux 用户态可通过:
mbimcli
ModemManager
操作。
典型链路:
Modem
-> MHI MBIM channel
-> mhi_wwan_mbim
-> Linux netdev
-> IPv4/IPv6
9.4 QRTR / QMI over MHI
Qualcomm 平台常使用 QRTR 承载 QMI。
Linux 中可能有:
CONFIG_QRTR
CONFIG_QRTR_MHI
链路:
Modem QMI service
-> QRTR
-> MHI channel
-> Linux qrtr-mhi
-> qmicli / ModemManager / qrtr clients
用户态可能使用:
qmicli
qmi-proxy
qrtr-ns
具体设备节点和用法取决于发行版和内核配置。
9.5 mhi_uci
mhi_uci 提供用户态字符设备接口,便于用户态直接读写 MHI channel。
常见设备节点类似:
/dev/mhi_xxx
例如:
/dev/mhi_DUN
/dev/mhi_DIAG
/dev/mhi_LOGGING
具体命名视内核版本和 channel 名而定。
用途:
AT 命令透传
DIAG 工具
LOG 抓取
厂商私有协议调试
10. 数据传输路径
以网络上行/下行为例。
10.1 上行:Linux 发包到 Modem
应用层
-> socket
-> Linux TCP/IP 协议栈
-> netdev
-> mhi_net/wwan 数据驱动
-> MHI core
-> DMA map skb
-> 生成 TRE
-> 写 doorbell
-> PCIe Modem DMA 读取数据
-> 完成事件
-> MHI IRQ
-> ul_xfer_cb
-> 释放/回收 buffer
10.2 下行:Modem 发包到 Linux
Modem
-> DMA 写入 host buffer
-> 写 event ring
-> MSI 中断
-> MHI IRQ handler
-> MHI core 解析 event
-> dl_xfer_cb
-> mhi_net/wwan 驱动
-> netif_receive_skb()
-> Linux TCP/IP 协议栈
-> 应用层
11. 电源管理
MHI 的电源管理比较复杂,因为它同时涉及:
PCIe PM
Linux runtime PM
MHI 自身 M-state
Modem 固件电源状态
边带 WAKE 信号
11.1 System Suspend/Resume
系统休眠时,Linux 可能执行:
MHI power down
MHI state -> M3
PCIe device -> D3hot/D3cold
唤醒时:
PCIe link resume
MHI power up
Modem 恢复
client driver resume
常见问题:
resume timeout
MHI power up failed after suspend
PCIe link retrain failed
modem not waking host
11.2 Runtime PM
运行时电源管理可能让 Modem 在空闲时进入低功耗。
相关节点:
/sys/bus/pci/devices/<BDF>/power/control
/sys/bus/pci/devices/<BDF>/power/runtime_status
/sys/bus/pci/devices/<BDF>/power/autosuspend_delay_ms
常见值:
auto
on
调试时可临时关闭 runtime PM:
echo on > /sys/bus/pci/devices/<BDF>/power/control
但生产环境不建议长期关闭,需要结合平台稳定性测试。
11.3 Wake 机制
Modem 唤醒主机通常依赖:
PCIe PME
WAKE#
MSI/MSI-X wake interrupt
MHI PM interrupt
如果唤醒异常,需要检查:
ACPI/设备树是否描述 wake 能力
PCIe Root Port 是否支持 wake
BIOS 是否启用 PCIe wake
MHI controller 是否注册 wake IRQ
12. 常见内核配置
不同内核配置名可能略有差异,常见相关选项包括:
CONFIG_MHI_BUS
CONFIG_MHI_BUS_DEBUG
CONFIG_MHI_BUS_PCI_GENERIC
CONFIG_MHI_NET
CONFIG_MHI_WWAN_CTRL
CONFIG_MHI_WWAN_MBIM
CONFIG_MHI_UCI
CONFIG_QRTR
CONFIG_QRTR_MHI
还需要启用:
CONFIG_PCI
CONFIG_PCI_MSI
CONFIG_DMADEVICES
CONFIG_NET
CONFIG_WWAN
CONFIG_USB_NET_QMI_WWAN 不一定需要,取决于是否走 USB
如果你使用 ModemManager,还需要用户态组件:
modemmanager
libmbim
libqmi
13. 一个典型启动流程示例
假设一切正常,内核日志可能类似:
pci 0000:01:00.0: [17cb:xxxx] type 00 class 0x...
pci 0000:01:00.0: BAR 0: assigned ...
mhi_pci_generic 0000:01:00.0: enabling device
mhi_pci_generic 0000:01:00.0: MHI PCI device found
mhi mhi0: firmware loading
mhi mhi0: BHI firmware download completed
mhi mhi0: MHI state: READY
mhi mhi0: MHI power up to M0
mhi mhi0: Channel IP_HW0 started
mhi_net mhi0_IP_HW0: registering network
具体日志格式会因内核和厂商驱动不同而变化。
14. MHI Endpoint 简述
前面讲的都是 Host Side。
Linux 也逐步支持 MHI Endpoint,用于 Linux 运行在设备侧/Endpoint 侧,例如某个 SoC 通过 PCIe Endpoint 连接到主机。
相关代码可能在:
drivers/bus/mhi/ep/
这种场景下:
Linux 是 MHI device/endpoint;
对端主机是 MHI host;
如果你是在 x86/主机侧调试蜂窝模组,一般不需要关心 endpoint 侧。
15. 总结
Linux PCIe MHI 驱动本质上是一套围绕 PCIe 设备构建的高速通信框架。理解它可以抓住几个核心点:
PCIe 是物理承载
依赖 BAR、MSI、DMA、Bus Master、PM。MHI 是传输协议
使用 channel、transfer ring、event ring、command ring、doorbell。MHI controller driver 是平台胶水层
常见是pci_generic.c,负责 PCIe 资源和 MHI core 对接。MHI core 管理状态、固件、电源和数据环
包括固件加载、power up、channel start、event 处理。客户端驱动决定业务形态
网络、MBIM、QMI、AT、DIAG、LOG 都是挂在 MHI channel 上的客户端。调试重点是 PCIe、固件、中断、DMA、电源五个方向
大多数 MHI 问题都落在这几类。





