华为鲲鹏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_idle到gic_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. 故障排查建议
要解决此问题,请重点检查驱动程序源代码:
返回值检查:确保
gpio_irq_handler只有在确定处理了该中断时才返回IRQ_HANDLED。如果因为某种原因(如寄存器状态未清除)导致处理函数返回了IRQ_NONE,内核就会认为这个中断是误触发。寄存器清除:确认在中断处理函数中是否正确清除了 GPIO 控制器的中断挂起位(Pending bit)。如果没有清除,电平触发的中断会立即再次触发,而此时处理函数可能认为没有新数据,从而返回
IRQ_NONE。共享中断冲突:日志中提到了
gpio_shared_irq。如果是共享中断,请确认该线上是否有其他设备也在产生中断,而您的驱动程序错误地忽略了它们。临时恢复:作为测试,您可以通过向
/proc/irq/444/enable重新写入来尝试开启中断,但如果根本逻辑不改,它很快会再次被禁用。总结: 您的系统由于
gpio_irq_handler频繁返回“未处理”状态,触发了内核的自我保护机制,导致 IRQ #444 被强制关闭。您需要修正驱动程序对中断状态的判断逻辑 。
gpio中断驱动代码如下,如果Gemini解释是正确的,那就是出现异常中断或者驱动中的中断函数没有及时返回IRQ_HANDLED,运行时间长了就会出现累积。
#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"); // 指定模块描述
修改后的驱动代码如下:
#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(高级配置和电源接口)相关函数
#include<linux/ktime.h> //
#include <linux/types.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/rcupdate.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/kobject.h>
#include <linux/device.h>
#include <asm/io.h>
#include <linux/hrtimer.h>
#include <linux/cpu.h>
#include <linux/irq.h>
#include <linux/bitops.h>
#include <linux/uaccess.h>
#include <linux/miscdevice.h>
#include <linux/signal.h>
#include <linux/kernel.h>
#include <linux/spinlock.h>
#include <linux/of_gpio.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/slab.h>
#include <linux/gpio/driver.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/spinlock.h>
#include <linux/poll.h>
#include <linux/pid.h>
#include <linux/uaccess.h>
#include <linux/sched/signal.h>
#include <linux/pid_namespace.h>
#include <linux/interrupt.h>
// GPIO基地址宏定义
#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
#define GPIO_INT_B_BASE 0x94190000
#define OFFSETS_ARRAY_SIZE 2
#define OFFSET_D4 0xD4
#define OFFSET_DC 0xDC
#define FUNC_INT 0x4
#define LPC_SIGNAL1 40
#define LPC_SIGNAL2 41
#define LPC_SIGNAL3 42
#define LPC_SIGNAL4 43
#define LPC_SIGNAL5 44
#define LPC_SIGNAL6 45
#define LPC_SIGNAL7 46
#define LPC_SIGNAL8 47
unsigned long offsets[OFFSETS_ARRAY_SIZE] = {OFFSET_D4,OFFSET_DC};
static void __iomem *gpio_int_base_b;
static void __iomem *device_regs;
static int g_pid = 0;
static int major = 0;
static struct class *buttondev_class = NULL;
static struct device *buttons_device = NULL;
// GPIO信息结构体定义
struct gpio_info {
void __iomem *base; // GPIO基地址
int irq; // GPIO中断号
int status_bit; // GPIO状态位
int virq; // 虚拟中断号
};
// GPIO数组,初始化GPIO信息
static struct gpio_info gpios[] = {
{ .base = (void __iomem *)GPIO_B_BASE, .irq = 126, .status_bit = 13 },//CPU GPIO13 <--> CPLD GPIO7
};
static unsigned long bit_cnt[8];
static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
{
u8 status;
struct task_struct *task = NULL;
/* 读中断状态 */
status = readb(device_regs);
/* 找用户进程(失败也不影响 ACK) */
if (g_pid)
task = pid_task(find_vpid(g_pid), PIDTYPE_PID);
if (task && status) {
if (status & 0x01) { send_sig_info(LPC_SIGNAL1, SEND_SIG_FORCED, task); bit_cnt[0]++; }
if (status & 0x02) { send_sig_info(LPC_SIGNAL2, SEND_SIG_FORCED, task); bit_cnt[1]++; }
if (status & 0x04) { send_sig_info(LPC_SIGNAL3, SEND_SIG_FORCED, task); bit_cnt[2]++; }
if (status & 0x08) { send_sig_info(LPC_SIGNAL4, SEND_SIG_FORCED, task); bit_cnt[3]++; }
if (status & 0x10) { send_sig_info(LPC_SIGNAL5, SEND_SIG_FORCED, task); bit_cnt[4]++; }
if (status & 0x20) { send_sig_info(LPC_SIGNAL6, SEND_SIG_FORCED, task); bit_cnt[5]++; }
if (status & 0x40) { send_sig_info(LPC_SIGNAL7, SEND_SIG_FORCED, task); bit_cnt[6]++; }
if (status & 0x80) { send_sig_info(LPC_SIGNAL8, SEND_SIG_FORCED, task); bit_cnt[7]++; }
}
return IRQ_HANDLED;
}
static long cpld_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
if (cmd == 0x6B09)
return copy_from_user(&g_pid, (void __user *)arg, sizeof(int));
if (cmd >= 0x6B01 && cmd <= 0x6B08) {
unsigned long v = bit_cnt[cmd - 0x6B01];
return copy_to_user((void __user *)arg, &v, sizeof(v));
}
return -EINVAL;
}
static int cpld_open(struct inode * inode, struct file * filp)
{
return 0;
}
static int cpld_release(struct inode *inode, struct file *file)
{
return 0;
}
/* File operations struct for character device */
static const struct file_operations buttons_dev_fops = {
.owner = THIS_MODULE,
.open = cpld_open,
.release = cpld_release,
.unlocked_ioctl = cpld_ioctl,
};
// 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;
return 0;
}
//功能复用为中断
static int __init gpio_reuse(void)
{
unsigned i;
void __iomem *base_b, *base_a, *gpio_offset;
pr_info("[Info] gpio_reuse.\n");
// 映射GPIO内存区域
base_b = ioremap(GPIO_INT_B_BASE, GPIO_SIZE);
if (!base_b) {
printk(KERN_ERR "Failed to remap GPIO inteerupt baseB\n"); // 映射失败
return -ENOMEM;
}
gpio_int_base_b = base_b;
for(i = 0; i < OFFSETS_ARRAY_SIZE; i++){
gpio_offset = base_b + offsets[i];
writel(FUNC_INT,gpio_offset);
}
return 0;
}
int __init gpio_irq_init(void)
{
unsigned i;
// 映射GPIO内存区域
pr_info("[Info] hisi_irq_init.\n");
if (gpio_reuse() != 0) {
return -1;
}
if (gpio_init() != 0) {
return -1;
}
device_regs = ioremap(0x800000e8,0x1);
if (!device_regs) {
pr_err("Failed to map device registers\n");
return -1;
}
if ((major = register_chrdev(0, "buttons_dev", &buttons_dev_fops)) < 0)
{
printk("failed to create dev entry.\n");
return (0);
}
buttondev_class = class_create(THIS_MODULE, "buttondev");
if (IS_ERR(buttondev_class)) {
printk("Could not create device class");
unregister_chrdev(200, "buttondev");
return EFAULT;
}
buttons_device = device_create(buttondev_class, NULL, MKDEV(major, 0), NULL, "buttons");
printk("device to create ......%d\n",IS_ERR(buttons_device));
// 遍历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);
if (irq < 0) {
printk(KERN_ERR "Failed to register GSI\n"); // 注册失败
return irq;
}
gpios[i].virq = irq;
// 请求GPIO中断
if (request_irq(irq, gpio_irq_handler, IRQF_TRIGGER_RISING|IRQF_SHARED, "gpio_extint", &gpios[i])) {
printk(KERN_ERR "Failed to request IRQ %i\n", gpios[i].irq); // 请求失败
return -EIO;
}
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(gpio_int_base_b); // 取消内存映射
//
if (device_regs){
iounmap(device_regs);
}
if (buttons_device)
device_unregister(buttons_device);
if (buttondev_class)
class_destroy(buttondev_class);
if (major)
unregister_chrdev(major, "buttons_dev");
}
module_init(gpio_irq_init); // 注册模块初始化函数
module_exit(gpio_exit); // 注册模块退出函数
MODULE_VERSION("2.0");
MODULE_LICENSE("GPL v2"); // 指定许可证
MODULE_AUTHOR("Wuzhou Co.,Ltd."); // 指定作者
MODULE_DESCRIPTION("GPIO extint driver for Linux"); // 指定模块描述
鲲鹏昇腾开发者社区是面向全社会开放的“联接全球计算开发者,聚合华为+生态”的社区,内容涵盖鲲鹏、昇腾资源,帮助开发者快速获取所需的知识、经验、软件、工具、算力,支撑开发者易学、好用、成功,成为核心开发者。
更多推荐

所有评论(0)