华为鲲鹏920s处理器在统信系统下接收外部GPIO中断问题

1、问题描述

目前项目中使用华为鲲鹏920s处理器,操作系统为统信V20,测试场景为连续接收外部GPIO中断,四种中断类型,周期分别为1s,50ms,10ms和500us。在连续拷机24小时后发现接收不到GPIO中断,系统下看到中断计数如下,不再增加:

此时输入dmesg,发现出错情况如下:

2、问题分析

将dmesg完整日志提交给Gemini,结论如下:

根据您提供的日志和背景信息,Linux 系统运行一段时间后收不到中断,其核心原因并非中断周期太短,而是触发了内核的不良中断处理机制(Spurious IRQ handling),导致系统主动禁用了该中断。

以下是详细的原因分析:

1. 根本原因:中断被系统强制禁用 (Disabling IRQ #444)

日志显示:Disabling IRQ #444。这意味着内核在第 444 号中断上检测到了异常行为。

内核检测逻辑 :Linux 内核会监控每个中断。如果在短时间内(通常是 100,000 次中断内)有超过 99% 的中断被认为是"未处理"的(即中断发生了,但没有任何注册的处理函数宣称处理了它),内核会认为该中断线发生了"中断风暴"或硬件故障 。

保护机制 :为了防止未处理的中断持续抢占 CPU 资源导致系统卡死(即 dump_stack 中显示的从 do_idlegic_handle_irq 的路径),内核会通过 note_interrupt 函数强制关闭该中断线 。

2. 为何中断计数不再增加?

一旦内核打印出 Disabling IRQ #444,它就会在硬件或中断控制器(GIC)级别屏蔽该中断 。此后,硬件产生的电信号无法再触发内核的中断服务例程(ISR),因此中断计数停止增长,系统表现为"收不到中断"。

3. 是否与 500us 的中断周期有关?

中断周期短(高频率)本身不是原因,但它加速了问题的触发。

处理不当 :如果您的驱动程序 gpio_irq_handler(来自 gpio_shared_irq 模块)在被调用时,没有正确识别硬件状态并返回 IRQ_HANDLED,而是返回了 IRQ_NONE,那么每一次 500us 的脉冲都会被记为一次"不良中断" 。

  • 触发速度 :在 500us 的周期下,每秒产生 2000 次中断。如果处理函数一直返回 IRQ_NONE,只需约 50 秒左右就会达到内核的错误阈值,从而导致中断被禁用。

4. 故障排查建议

要解决此问题,请重点检查驱动程序源代码:

  1. 返回值检查 :确保 gpio_irq_handler 只有在确定处理了该中断时才返回 IRQ_HANDLED。如果因为某种原因(如寄存器状态未清除)导致处理函数返回了 IRQ_NONE,内核就会认为这个中断是误触发。

  2. 寄存器清除 :确认在中断处理函数中是否正确清除了 GPIO 控制器的中断挂起位(Pending bit)。如果没有清除,电平触发的中断会立即再次触发,而此时处理函数可能认为没有新数据,从而返回 IRQ_NONE

  3. 共享中断冲突 :日志中提到了 gpio_shared_irq。如果是共享中断,请确认该线上是否有其他设备也在产生中断,而您的驱动程序错误地忽略了它们。

  4. 临时恢复 :作为测试,您可以通过向 /proc/irq/444/enable 重新写入来尝试开启中断,但如果根本逻辑不改,它很快会再次被禁用。

总结: 您的系统由于 gpio_irq_handler 频繁返回"未处理"状态,触发了内核的自我保护机制,导致 IRQ #444 被强制关闭。您需要修正驱动程序对中断状态的判断逻辑 。

gpio中断驱动代码如下,如果Gemini解释是正确的,那就是出现异常中断或者驱动中的中断函数没有及时返回IRQ_HANDLED,运行时间长了就会出现累积。

cpp 复制代码
#include <linux/percpu.h>
#include <linux/module.h>        // 包含Linux内核模块的头文件
#include <linux/interrupt.h>     // 包含中断处理相关的函数和宏定义
#include <linux/io.h>            // 包含I/O内存映射函数
#include <linux/delay.h>         // 包含延时函数
#include <linux/acpi.h>          // 包含ACPI(高级配置和电源接口)相关函数

// GPIO基地址宏定义
#define GPIO_A_BASE 0x9C100000
#define GPIO_B_BASE 0x94100000
#define GPIO_SIZE 0x1000         // GPIO内存映射区域的大小
#define GPIO_STATUS_OFFSET 0x0    // GPIO状态寄存器的偏移量
#define GPIO_IO_CONTROL_OFFSET 0x4 // GPIO I/O控制寄存器的偏移量
#define MASK1 0x3E00
#define MASK2 0x6000

// GPIO信息结构体定义
struct gpio_info {
	void __iomem *base;          // GPIO基地址
	int irq;                     // GPIO中断号
	int status_bit;              // GPIO状态位
	int virq;                    // 虚拟中断号
//#define GPIO_REG_READ(reg_base, offset)          (*(volatile u32 *)((reg_base) + (offset)))
//#define GPIO_REG_WRITE(reg_base, offset, value)  (*(volatile u32 *)((reg_base) + (offset)) = (value))

};

// GPIO数组,初始化GPIO信息
static struct gpio_info gpios[] = {
	{ .base = (void __iomem *)GPIO_B_BASE, .irq = 126, .status_bit = 13 },
	{ .base = (void __iomem *)GPIO_B_BASE, .irq = 127, .status_bit = 14 },
	{ .base = (void __iomem *)GPIO_A_BASE, .irq = 74, .status_bit = 9 },
	{ .base = (void __iomem *)GPIO_A_BASE, .irq = 75, .status_bit = 10 },
	{ .base = (void __iomem *)GPIO_A_BASE, .irq = 76, .status_bit = 11 },
	{ .base = (void __iomem *)GPIO_A_BASE, .irq = 77, .status_bit = 12 },
	{ .base = (void __iomem *)GPIO_A_BASE, .irq = 78, .status_bit = 13 },
};

// GPIO中断处理函数
static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
{
	struct gpio_info *gpio = dev_id;
	u32 status;

	// 检查中断号是否有效
	if (irq > ARRAY_SIZE(gpios) || irq < 0 )
	{
		return IRQ_NONE;
	}

	// 读取GPIO状态寄存器
	status = readl(gpio->base + GPIO_STATUS_OFFSET);
	// 检查状态位是否被设置
	if (status & (1 << gpio->status_bit)) 
	{
		printk(KERN_INFO "GPIO interrupt %d\n", gpio->irq); // 打印中断信息
		writel(~(1 << gpio->status_bit), gpio->base + GPIO_STATUS_OFFSET); // 清除信号位
		udelay(1); // 延时1微秒
		writel((1 << gpio->status_bit), gpio->base + GPIO_STATUS_OFFSET); // 重置信号位
	}

	return IRQ_HANDLED; // 返回中断已处理
}

// GPIO初始化函数
static int __init gpio_init(void)
{
	void __iomem *base;
	pr_info("[Info] gpio_init.\n");

	// 映射GPIO内存区域
	base = ioremap(GPIO_B_BASE, GPIO_SIZE);
	if (!base) {
		printk(KERN_ERR "Failed to remap GPIO baseB\n"); // 映射失败
		return -ENOMEM;
	}
	gpios[0].base = base;
	gpios[1].base = base;

	base = ioremap(GPIO_A_BASE, GPIO_SIZE);
	if (!base) {
		printk(KERN_ERR "Failed to remap GPIO baseA\n"); // 映射失败
		iounmap(gpios[2].base);
		return -ENOMEM;
	}
	gpios[2].base = base;
	gpios[3].base = base;
	gpios[4].base = base;
	gpios[5].base = base;
	gpios[6].base = base;
	return 0;
}

int __init gpio_irq_init(void)
{
	//u32 reg_val;
	unsigned i;
	volatile u32* reg1, *reg2, *reg3, *reg4;

	// 映射GPIO内存区域
	pr_info("[Info] hisi_irq_init.\n");

	if (gpio_init() != 0) {
		return -1;
	}

	pr_info("[Info] gpio extint test init.\n");
	// 读取地址为 GPIO_A_BASE 的寄存器值并设置位
	reg1 = (void*)(gpios[2].base + GPIO_IO_CONTROL_OFFSET);
	reg2 = (void*)(gpios[2].base + GPIO_STATUS_OFFSET);
	*reg1 |= MASK1;
	*reg2 |= MASK1;

	// 读取地址为 GPIO_B_BASE 的寄存器值并设置位
	reg3 = (void*)(gpios[0].base + GPIO_IO_CONTROL_OFFSET);
	reg4 = (void*)(gpios[0].base + GPIO_STATUS_OFFSET);
	*reg3 |= MASK2;
	*reg4 |= MASK2;


	// 遍历GPIO数组,初始化每个GPIO
	for (i = 0; i < ARRAY_SIZE(gpios); i++) {
		int irq;

		// 注册GPIO中断
		irq = acpi_register_gsi(NULL, gpios[i].irq, ACPI_EDGE_SENSITIVE, ACPI_ACTIVE_LOW);
		//irq = acpi_register_gsi(NULL, gpios[i].irq, ACPI_LEVEL_SENSITIVE, ACPI_ACTIVE_LOW);
		if (irq < 0) {
			printk(KERN_ERR "Failed to register GSI\n"); // 注册失败
								     //continue;
			return irq;//continue?
		}
		gpios[i].virq = irq;

		// 请求GPIO中断
		if (request_irq(irq, gpio_irq_handler, IRQF_TRIGGER_RISING, "gpio_extint", &gpios[i])) {
			printk(KERN_ERR "Failed to request IRQ %i\n", gpios[i].irq); // 请求失败
										     //continue;
										     //continue;
			return -EIO;//continue?
		}
		pr_info("[Info] gpio irq %i init.\n", irq);
	}

	return 0; // 初始化成功
}

// GPIO退出函数
static void __exit gpio_exit(void)
{
	int i;

	// 遍历GPIO数组,释放资源
	for (i = 0; i < ARRAY_SIZE(gpios); i++) {
		acpi_unregister_gsi(gpios[i].virq); // 注销中断
		free_irq(gpios[i].virq, &gpios[i]); // 释放中断
	}
	iounmap(gpios[0].base); // 取消内存映射
	iounmap(gpios[2].base); // 取消内存映射
}

module_init(gpio_irq_init); // 注册模块初始化函数
module_exit(gpio_exit); // 注册模块退出函数

MODULE_LICENSE("GPL v2"); // 指定许可证
MODULE_AUTHOR("Wuzhou Co.,Ltd."); // 指定作者
MODULE_DESCRIPTION("GPIO extint driver for Linux"); // 指定模块描述
相关推荐
funnycoffee1232 小时前
思科,华为,华三交换机清空端口配置命令
华为·清空接口配置
猛扇赵四那边好嘴.2 小时前
Flutter 框架跨平台鸿蒙开发 - 诗词鉴赏应用开发教程
flutter·华为·harmonyos
funnycoffee1232 小时前
华为USG防火墙 直连 ping不通是啥问题?以及策略查看命令
华为·华为usg·usg直连不通
时光慢煮2 小时前
行旅迹 · 基于 Flutter × OpenHarmony 的旅行记录应用— 构建高体验旅行记录列表视图的跨端实践
flutter·华为·开源·openharmony
时光慢煮2 小时前
行走的记忆卡片:基于 Flutter × OpenHarmony 的旅行记录应用实践——单个旅行记录卡片构建详解
flutter·华为·开源·openharmony
大雷神2 小时前
HarmonyOS智慧农业管理应用开发教程--高高种地---第1篇:项目初始化与环境搭建
华为·harmonyos
小白阿龙2 小时前
鸿蒙+flutter 跨平台开发——智力迷宫挑战的实现
flutter·游戏·华为·harmonyos·鸿蒙
时光慢煮3 小时前
行走在多端之间:基于 Flutter × OpenHarmony 的旅行记录应用实践 —— 旅行详情查看模块解析
flutter·华为·开源·wpf·openharmony
时光慢煮4 小时前
基于 Flutter × OpenHarmony 的旅行记录应用实践 —— 添加新的旅行记录
flutter·华为·开源·openharmony