LGA Check 模块分析
一、模块概述
1.1 功能定位
LGA Check (Land Grid Array Check) 是华为 MATE X5 硬件监控系统中的 LGA 主板异常检测模块,专门用于监测设备主板上 LGA 连接器(陆地网格阵列)的健康状态,通过 ADC 电压采样、GPIO 电平检测、中断触发等多种方式实时监控主板连接是否存在断裂、虚焊、接触不良等异常。
1.2 核心功能
- 多模式检测:支持 ADC(电压检测)、GPIO(电平检测)、IRQ(中断检测)三种检测模式
- 充电场景监控:在充电启动/停止时触发检测,防止充电时的热应力导致 LGA 失效
- 自动 DMD 上报:检测到异常时自动上报 DMD(Device Monitor Diagnosis)告警
- 防重复上报:限制最多上报 5 次,避免日志泛滥
- Sysfs 查询接口:提供用户态查询接口,实时获取 LGA 状态
1.3 设计背景
LGA(Land Grid Array)是一种无引脚封装技术,广泛用于主板与芯片之间的连接。在折叠屏设备(如 MATE X5)中,频繁的折叠操作、充电时的热膨胀、跌落冲击等都可能导致 LGA 触点接触不良或断裂。该模块通过在关键位置部署检测点,实现对主板健康状态的实时监控。
二、系统架构
2.1 模块组成
lga_check 模块
├── lga_check.c # 主逻辑(检测、上报、事件处理)
├── lga_check.h # 数据结构定义
├── Kconfig # 内核配置
└── Makefile # 编译配置2.2 架构分层
+---------------------------------------------------------------+
| User Space (Sysfs) |
| /sys/class/hw_power/lga_ck/status (只读,实时检测状态) |
+---------------------------------------------------------------+
↓
+---------------------------------------------------------------+
| lga_check.c (检测引擎) |
| - lga_status_check(): 遍历所有检测点 |
| - lga_status_check_adc_vol(): ADC 电压检测 |
| - lga_status_check_gpio_val(): GPIO 电平检测 |
| - lga_status_check_irq_val(): IRQ 中断检测 |
+---------------------------------------------------------------+
↓
+---------------------------------------------------------------+
| Hardware Interface Layer |
| - power_platform_get_adc_voltage(): ADC 读取 |
| - gpio_get_value(): GPIO 读取 |
| - request_irq(): 中断注册 |
+---------------------------------------------------------------+
↓
+---------------------------------------------------------------+
| Hardware (LGA Connectors) |
| - LGA 检测点 1 (ADC 分压电路) |
| - LGA 检测点 2 (GPIO 上拉/下拉) |
| - LGA 检测点 3 (IRQ 触发电路) |
+---------------------------------------------------------------+2.3 检测触发机制
触发方式 1: 充电事件驱动
POWER_NE_CHARGING_START → 延迟 30s → lga_ck_detect_work()
POWER_NE_CHARGING_STOP → 取消检测任务
触发方式 2: 中断驱动(LGA_CK_MODE_IRQ)
硬件中断触发 → lga_ck_interrupt() → 延迟 30s → lga_ck_detect_work()
触发方式 3: 主动查询(Sysfs)
用户读取 /sys/class/hw_power/lga_ck/status → 实时执行检测三、核心数据结构
3.1 检测参数结构
c
struct lga_ck_para_info {
int type; // 检测模式 (ADC/GPIO/IRQ)
char name[16]; // 检测点名称(如 "lga_check_adc_0")
int threshold; // 异常阈值
int dmd_no; // DMD 上报错误号
int dmd_switch; // DMD 上报开关 (0=关闭, 1=开启)
// ADC 模式专用
u32 adc_no; // ADC 通道号
int adc_vol; // ADC 电压值(mV)
// GPIO 模式专用
int gpio_no; // GPIO 编号
int gpio_val; // GPIO 电平值 (0/1)
// IRQ 模式专用
int irq_gpio_no; // 中断 GPIO 编号
int irq_int; // 中断号
int irq_val; // 中断 GPIO 电平值
int status; // 检测结果 (0=正常, -1=异常)
};3.2 设备管理结构
c
struct lga_ck_dev {
struct device *dev; // Sysfs 设备节点
struct notifier_block nb; // 充电事件通知器
struct delayed_work detect_work; // 延迟检测工作队列
struct lga_ck_para_data data; // 检测参数数组
int abnormal_time; // 异常上报次数计数器
};
struct lga_ck_para_data {
int total_type; // 检测点总数(最多 8 个)
struct lga_ck_para_info para[8]; // 检测点参数数组
};3.3 检测模式枚举
c
enum lga_ck_mode {
LGA_CK_MODE_ADC, // ADC 电压检测模式
LGA_CK_MODE_GPIO, // GPIO 电平检测模式
LGA_CK_MODE_IRQ, // 中断触发检测模式
};四、核心算法与工作流程
4.1 检测工作流程(lga_ck_detect_work)
c
static void lga_ck_detect_work(struct work_struct *work)
{
// 1. 检查上报次数限制(最多 5 次)
if (l_dev->abnormal_time >= LGA_CK_MAX_DMD_REPORT_TIME) {
hwlog_err("abnormal over 5 time\n");
return; // 已上报 5 次,停止检测
}
// 2. 执行所有检测点的状态检查
if (lga_status_check(l_dev)) {
// 3. 发现异常:上报 DMD
lga_dmd_report(l_dev);
// 4. 增加异常计数器
l_dev->abnormal_time++;
}
}时序图:
充电启动事件
↓
取消旧任务 (cancel_delayed_work)
↓
调度新任务 (延迟 30s)
↓
30 秒后执行 lga_ck_detect_work()
↓
遍历所有检测点 (lga_status_check)
↓
发现异常?
├─ 是 → lga_dmd_report() → abnormal_time++
└─ 否 → 结束4.2 ADC 电压检测算法(lga_status_check_adc_vol)
c
static int lga_status_check_adc_vol(struct lga_ck_para_info *info)
{
info->status = LGA_CK_FRACTURE_FREE; // 默认正常
// 1. 读取 ADC 电压(带重试机制,最多 3 次)
info->adc_vol = lga_get_adc_vol(info->adc_no);
// 2. 判断电压是否超过阈值
if (info->adc_vol > info->threshold) {
hwlog_info("adc_vol is over threshold %d\n", info->threshold);
info->status = LGA_CK_FRACTURE_FOUND; // 标记异常
}
return info->status;
}检测原理:
- LGA 连接正常时,分压电路输出电压低于阈值(如 < 500mV)
- LGA 断裂或接触不良时,电路开路,电压上升至高电平(如 > 1500mV)
- 通过 ADC 采样电压判断连接状态
ADC 读取函数(带重试):
c
static int lga_get_adc_vol(u32 adc_channel)
{
int i;
int adc_vol;
for (i = 0; i < LGA_CK_ADC_MAX_RETRYS; i++) {
adc_vol = power_platform_get_adc_voltage(adc_channel);
if (adc_vol >= 0)
break; // 读取成功
hwlog_err("adc read fail, retry=%d\n", i + 1);
}
return adc_vol;
}4.3 GPIO 电平检测算法(lga_status_check_gpio_val)
c
static int lga_status_check_gpio_val(struct lga_ck_para_info *info)
{
info->status = LGA_CK_FRACTURE_FREE;
// 1. 读取 GPIO 电平
info->gpio_val = gpio_get_value(info->gpio_no);
// 2. 判断电平是否等于阈值(阈值通常为 1 或 0)
if (info->gpio_val == info->threshold) {
hwlog_info("gpio_val is equal threshold %d\n", info->threshold);
info->status = LGA_CK_FRACTURE_FOUND;
}
return info->status;
}检测原理:
- GPIO 配置为输入模式,带上拉或下拉电阻
- LGA 正常连接时,GPIO 被拉至特定电平(如低电平 0)
- LGA 断裂时,GPIO 悬空被上拉电阻拉高(如高电平 1)
- 通过检测 GPIO 电平变化判断连接状态
4.4 中断检测算法(lga_status_check_irq_val)
c
static int lga_status_check_irq_val(struct lga_ck_para_info *info)
{
info->status = LGA_CK_FRACTURE_FREE;
// 1. 读取中断 GPIO 的电平值
info->irq_val = lga_get_gpio_val(info->irq_gpio_no);
// 2. 判断是否等于阈值
if (info->irq_val == info->threshold) {
hwlog_info("irq_val is equal threshold %d\n", info->threshold);
info->status = LGA_CK_FRACTURE_FOUND;
}
return info->status;
}中断处理函数:
c
static irqreturn_t lga_ck_interrupt(int irq, void *data)
{
// 1. 取消之前的检测任务
cancel_delayed_work(&l_dev->detect_work);
// 2. 重新调度延迟 30s 的检测任务
schedule_delayed_work(&l_dev->detect_work,
msecs_to_jiffies(LGA_CK_WORK_DELAY_TIME));
return IRQ_HANDLED;
}检测原理:
- LGA 连接点配置为下降沿触发中断(IRQF_TRIGGER_FALLING)
- 正常情况下 GPIO 保持稳定电平
- LGA 断裂瞬间产生电平跳变,触发中断
- 中断触发后延迟 30s 执行检测,避免抖动误报
4.5 DMD 上报算法(lga_dmd_report)
c
static void lga_dmd_report(struct lga_ck_dev *l_dev)
{
char dsm_buff[128] = { 0 };
// 遍历所有检测点
for (i = 0; i < l_dev->data.total_type; i++) {
// 1. 检查 DMD 上报开关
if (l_dev->data.para[i].dmd_switch == 0)
continue;
// 2. 检查是否发现异常
if (l_dev->data.para[i].status == LGA_CK_FRACTURE_FREE)
continue;
// 3. 根据检测模式格式化上报信息
switch (l_dev->data.para[i].type) {
case LGA_CK_MODE_ADC:
snprintf(dsm_buff, 127,
"lga abnormal: adc_channel=%d, adc_vol=%d[mV]\n",
l_dev->data.para[i].adc_no,
l_dev->data.para[i].adc_vol);
break;
case LGA_CK_MODE_GPIO:
snprintf(dsm_buff, 127,
"lga abnormal: gpio_no=%d, gpio_val=%d\n",
l_dev->data.para[i].gpio_no,
l_dev->data.para[i].gpio_val);
break;
case LGA_CK_MODE_IRQ:
snprintf(dsm_buff, 127,
"lga abnormal: irq_gpio_no=%d, irq_val=%d\n",
l_dev->data.para[i].irq_gpio_no,
l_dev->data.para[i].irq_val);
break;
}
// 4. 上报 DMD
power_dsm_report_dmd(POWER_DSM_PMU_OCP,
l_dev->data.para[i].dmd_no, dsm_buff);
// 5. 延迟 3s 避免上报过快
msleep(LGA_CK_DMD_DELAY_TIME);
}
}五、事件处理机制
5.1 充电事件订阅
c
static int lga_ck_probe(struct platform_device *pdev)
{
// 订阅充电事件通知
l_dev->nb.notifier_call = lga_ck_event_call;
power_event_bnc_register(POWER_BNT_CHARGING, &l_dev->nb);
}5.2 充电事件处理
c
static int lga_ck_event_call(struct notifier_block *nb,
unsigned long event, void *data)
{
switch (event) {
case POWER_NE_CHARGING_STOP:
// 充电停止:取消检测任务
cancel_delayed_work(&l_dev->detect_work);
break;
case POWER_NE_CHARGING_START:
// 充电启动:先取消旧任务,再调度新任务
cancel_delayed_work(&l_dev->detect_work);
schedule_delayed_work(&l_dev->detect_work,
msecs_to_jiffies(LGA_CK_WORK_DELAY_TIME));
break;
}
return NOTIFY_OK;
}设计意图:
- 充电时设备会发热,热膨胀可能导致 LGA 连接异常
- 在充电启动 30 秒后检测,等待系统稳定
- 充电停止时取消检测,节省系统资源
六、DTS 配置说明
6.1 配置示例
dts
lga_check {
compatible = "huawei,lga_check";
pinctrl-names = "default", "idle";
pinctrl-0 = <&lga_check_default>;
pinctrl-1 = <&lga_check_idle>;
/* 检测参数配置 */
check_para = <
/* type, name, threshold, dmd_no, dmd_switch */
"1" "lga_check_adc_0" "1500" "25001" "1"
"2" "lga_check_gpio_1" "1" "25002" "1"
"3" "lga_check_irq_2" "0" "25003" "1"
>;
};6.2 参数说明
check_para 数组格式
每个检测点需要 5 个参数:
| 序号 | 参数名 | 说明 | 示例 |
|---|---|---|---|
| 0 | type | 检测模式 1=ADC, 2=GPIO, 3=IRQ | "1" (ADC) |
| 1 | name | 检测点名称 - ADC: DTS ADC 通道名 - GPIO: DTS GPIO 属性名 - IRQ: DTS 中断 GPIO 属性名 | "lga_check_adc_0" |
| 2 | threshold | 异常阈值 - ADC: 电压阈值(mV) - GPIO: 电平阈值(0/1) - IRQ: 电平阈值(0/1) | "1500" (mV) |
| 3 | dmd_no | DMD 错误码 | "25001" |
| 4 | dmd_switch | DMD 上报开关 0=关闭, 1=开启 | "1" (开启) |
完整配置示例
dts
lga_check {
compatible = "huawei,lga_check";
/* Pinctrl 配置 */
pinctrl-names = "default", "idle";
pinctrl-0 = <&lga_default>;
pinctrl-1 = <&lga_idle>;
/* ADC 通道定义 */
lga_check_adc_0 = <5>; // ADC 通道 5
lga_check_adc_1 = <6>; // ADC 通道 6
/* GPIO 定义 */
lga_check_gpio_1 = <&gpio25 3 0>; // GPIO 25_3
lga_check_gpio_2 = <&gpio26 5 0>; // GPIO 26_5
/* 中断 GPIO 定义 */
lga_check_irq_2 = <&gpio27 2 0>; // GPIO 27_2
/* 检测参数配置 */
check_para = <
/* ADC 检测点 0: 电压超过 1500mV 为异常 */
"1" "lga_check_adc_0" "1500" "25001" "1"
/* ADC 检测点 1: 电压超过 1800mV 为异常 */
"1" "lga_check_adc_1" "1800" "25002" "1"
/* GPIO 检测点 1: 电平为 1 时异常 */
"2" "lga_check_gpio_1" "1" "25003" "1"
/* GPIO 检测点 2: 电平为 0 时异常 */
"2" "lga_check_gpio_2" "0" "25004" "1"
/* IRQ 检测点 2: 中断触发且电平为 1 时异常 */
"3" "lga_check_irq_2" "1" "25005" "1"
>;
};6.3 工厂模式特殊处理
c
// 在工厂模式下,GPIO 不执行 request_gpio 操作
// 因为工厂夹具检测可能会复用同一个 GPIO
if (power_cmdline_is_factory_mode()) {
info->gpio_no = of_get_named_gpio(np, string, 0);
return 0; // 仅获取 GPIO 编号,不申请资源
}七、Sysfs 接口
7.1 节点路径
bash
/sys/class/hw_power/lga_ck/status7.2 接口说明
c
static ssize_t lga_ck_sysfs_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
switch (info->name) {
case LGA_CK_SYSFS_STATUS:
// 执行实时检测并返回结果
return scnprintf(buf, PAGE_SIZE, "%d\n",
lga_status_check(l_dev));
}
}7.3 使用示例
bash
# 读取 LGA 检测状态
cat /sys/class/hw_power/lga_ck/status
# 返回值说明:
# 0 = 所有检测点正常(LGA_CK_FRACTURE_FREE)
# -1 = 发现 1 个异常点
# -2 = 发现 2 个异常点
# -N = 发现 N 个异常点实时检测特性:
- 每次读取该节点时都会实时执行
lga_status_check() - 不依赖后台定时检测,适用于工厂测试或手动诊断
八、典型应用场景
8.1 场景1:充电过程中 LGA 异常检测
时序流程:
1. 用户插入充电器
↓
2. POWER_NE_CHARGING_START 事件触发
↓
3. lga_ck_event_call() 调度延迟 30s 检测
↓
4. 30 秒后执行 lga_ck_detect_work()
↓
5. 检测所有配置的检测点(ADC/GPIO/IRQ)
↓
6. 发现异常:
- ADC 通道 5 电压 = 2100mV (阈值 1500mV)
- 状态 = LGA_CK_FRACTURE_FOUND
↓
7. 上报 DMD:
- 错误码:25001
- 信息:"lga abnormal: adc_channel=5, adc_vol=2100[mV]"
↓
8. abnormal_time++ (异常计数 = 1)8.2 场景2:中断触发的快速检测
时序流程:
1. 硬件异常导致 IRQ GPIO 产生下降沿
↓
2. lga_ck_interrupt() 中断处理函数执行
↓
3. 取消旧的检测任务(如果有)
↓
4. 调度延迟 30s 的新检测任务
↓
5. 30 秒后执行检测并上报 DMD8.3 场景3:工厂测试手动检测
bash
# 测试脚本示例
#!/bin/bash
echo "开始 LGA 检测..."
# 读取检测状态
status=$(cat /sys/class/hw_power/lga_ck/status)
if [ $status -eq 0 ]; then
echo "PASS: LGA 连接正常"
exit 0
else
echo "FAIL: 发现 $((0-$status)) 个异常点"
# 查看详细日志
dmesg | grep "lga abnormal"
exit 1
fi8.4 场景4:异常上报限制机制
检测流程:
第 1 次异常 → DMD 上报 → abnormal_time = 1
第 2 次异常 → DMD 上报 → abnormal_time = 2
第 3 次异常 → DMD 上报 → abnormal_time = 3
第 4 次异常 → DMD 上报 → abnormal_time = 4
第 5 次异常 → DMD 上报 → abnormal_time = 5
第 6 次异常 → 检测被跳过(abnormal_time >= 5)
...
后续异常 → 全部被跳过
设计意图:
- 避免持续上报相同错误导致日志泛滥
- 异常设备已记录足够诊断信息
- 防止 DMD 服务过载九、调试方法
9.1 日志关键点
bash
# 1. 模块初始化日志
[lga_check] probe success
# 2. ADC 检测日志
[lga_check] adc_channel=5, adc_vol=2100
[lga_check] adc_vol is over threshold 1500
# 3. GPIO 检测日志
[lga_check] gpio_no=195, gpio_val=1
[lga_check] gpio_val is equal threshold 1
# 4. IRQ 检测日志
[lga_check] irq_gpio_no=196, irq_val=0
[lga_check] irq_val is equal threshold 0
# 5. DMD 上报日志
[lga_check] lga abnormal: adc_channel=5, adc_vol=2100[mV]
# 6. 上报次数限制日志
[lga_check] abnormal over 5 time
# 7. 检测启动日志
[lga_check] start check
# 8. ADC 读取失败日志
[lga_check] adc read channel 5 fail, time=1
[lga_check] adc read channel 5 fail, time=2
[lga_check] adc read channel 5 fail, time=39.2 Sysfs 调试
bash
# 查看当前 LGA 状态
cat /sys/class/hw_power/lga_ck/status
# 持续监控(每秒检测一次)
watch -n 1 cat /sys/class/hw_power/lga_ck/status
# 触发充电事件检测
# 插拔充电器,观察 30 秒后的日志9.3 DMD 查询
bash
# 查看 DMD 上报记录
cat /sys/kernel/debug/power_dsm/power_dsm_dump
# 过滤 LGA 相关错误
cat /sys/kernel/debug/power_dsm/power_dsm_dump | grep "25001\|25002\|25003"9.4 常见问题排查
问题1:检测不触发
现象:充电时没有日志输出 "start check"
排查步骤:
- 检查驱动是否加载:bash
lsmod | grep lga_check - 检查 DTS 配置是否正确:bash
cat /proc/device-tree/lga_check/check_para - 检查充电事件是否触发:bash
dmesg | grep "CHARGING_START"
问题2:ADC 读取失败
现象:日志显示 "adc read channel X fail, time=3"
排查步骤:
- 检查 ADC 通道号是否正确
- 检查 ADC 驱动是否就绪
- 使用其他工具验证 ADC 通道:bash
cat /sys/class/hwmon/hwmon0/device/adc_channel_5
问题3:GPIO 无法申请
现象:probe 失败,返回 -EPROBE_DEFER
排查步骤:
- 检查 GPIO 是否被其他模块占用:bash
cat /sys/kernel/debug/gpio - 检查 Pinctrl 配置是否正确
- 在工厂模式下测试(跳过 GPIO 申请)
问题4:中断未触发
现象:硬件异常但中断处理函数未执行
排查步骤:
- 检查中断是否注册成功:bash
cat /proc/interrupts | grep lga - 检查 GPIO 中断配置:bash
cat /sys/kernel/debug/gpio | grep <irq_gpio_no> - 手动触发中断(短接测试点到地)
十、总结
10.1 技术特点
- 多模式融合:ADC + GPIO + IRQ 三种检测方式互补
- 事件驱动:充电事件触发 + 中断触发双重机制
- 防护机制:上报次数限制、ADC 重试、延迟检测
- 工厂兼容:工厂模式特殊处理,避免 GPIO 冲突
10.2 设计亮点
- 延迟检测:充电启动 30s 后检测,等待热稳态
- DMD 防刷:最多上报 5 次,避免日志泛滥
- 实时查询:Sysfs 接口支持主动检测,适用于工厂测试
- 硬件解耦:通过 DTS 配置检测点,无需修改代码
10.3 应用价值
- 提前预警:在 LGA 完全失效前检测到异常
- 故障定位:DMD 上报精确的检测点和异常值
- 质量控制:工厂测试阶段发现 LGA 虚焊问题
- 用户保护:防止 LGA 失效导致功能异常或安全隐患
10.4 适用硬件
- 折叠屏设备:频繁折叠可能导致 LGA 疲劳断裂
- 大功率充电设备:充电热应力可能引起 LGA 失效
- 跌落冲击场景:机械冲击可能导致 LGA 接触不良