linux驱动i2c设备树中断,linux-kernel – 将设备树中断标志映射到devm_request_irq

在Linux驱动程序中,特别是在I2C和平台驱动中,可以方便地从设备树获取IRQ号和IRQ标志。I2C驱动的probe()函数中,`client->irq`会包含IRQ编号,而`devm_request_irq()`会自动应用设备树中的IRQ标志。对于平台驱动,`platform_get_irq()`用于获取IRQ编号,同样适用于自动设置标志。如果手动处理,可以使用`irq_of_parse_and_map()`获取IRQ信息,然后让`devm_request_irq()`处理标志。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

此外,我将展示如何在一些常见情况下从设备树获取IRQ号和IRQ标志:

>在I2C驱动程序中

>在平台驱动程序中

>手动

在I2C驱动程序中

简而言之

如果您正在编写I2C驱动程序,则无需手动从DT读取IRQ参数.您可以依靠I2C内核为您填充IRQ参数:

>在您的probe()函数中,client-> irq将包含IRQ编号

> devm_request_irq()将自动使用DT的IRQ标志(只是不要将任何IRQ触发器标志传递给该函数).

细节

让我们看一下i2c_device_probe()函数(它是调用驱动程序的probe()函数的地方):

static int i2c_device_probe(struct device *dev)

{

...

if (dev->of_node) {

...

irq = of_irq_get(dev->of_node,0);

}

...

client->irq = irq;

...

status = driver->probe(client,i2c_match_id(driver->id_table,client));

}

因此,client-> irq已经在驱动程序的探测函数中包含IRQ编号.

至于IRQ标志:of_irq_get()(在上面的代码中)最终调用irqd_set_trigger_type(),它在内部存储IRQ标志(从设备树读取)作为中断号.因此,当您调用devm_request_irq()时,它最终会在__setup_irq()中结束,然后执行下一步:

/*

* If the trigger type is not spe

<think>好的,用户之前问过Linux字符设备驱动模板的实现,现在他想了解基于platform平台驱动编写,并且要增加设备树信息读写和中断处理。首先,我需要确定用户的需求。他可能正在开发一个需要与硬件交互的驱动,特别是使用设备树来管理硬件资源,同时需要处理中断。这可能涉及到嵌入式Linux开发,比如在树莓派或类似开发板上工作。 用户之前已经了解了基本的字符设备驱动结构,现在需要更高级的内容。Platform驱动设备树Linux内核中管理硬件资源的重要机制,尤其是当硬件不是即插即用时。设备树允许将硬件描述从内核代码中分离出来,提高可移植性。中断处理则是驱动中处理异步事件的必要部分。 接下来,我需要考虑如何组织回答。首先应该介绍platform驱动的基本结构,包括platform_driver和platform_device的注册,或者使用设备树替代platform_device。然后,详细说明如何在驱动中读取设备树信息,比如使用of_函数族获取资源。中断处理部分需要包括申请中断号、注册中断处理函数,以及处理中断的注意事项,比如顶半部和底半部的区分。 用户可能还关心如何将设备树信息与驱动绑定,比如在设备树中添加节点,以及驱动如何匹配这些节点。需要注意设备树节点的compatible属性与驱动中的匹配表是否一致。另外,中断部分需要说明设备树中如何指定中断号、触发方式,以及驱动中如何获取这些信息。 可能用户在实际操作中会遇到问题,比如设备树信息未正确解析,或者中断无法触发,因此需要强调调试方法,比如使用of_get_property是否正确,检查中断申请是否成功,以及中断处理函数是否被正确调用。 另外,需要提醒用户关于并发和资源管理的问题,比如在中断处理中使用自旋锁而不是可能导致休眠的函数。同时,模块卸载时要正确释放资源,比如释放中断、注销平台驱动等。 最后,提供一个完整的代码示例会帮助用户更好地理解,但需要注意代码的结构和关键步骤的注释,确保示例的正确性。同时,总结关键点和注意事项,帮助用户避免常见错误。</think>$$ 基于platform框架的驱动开发模板(含设备树中断处理) $$ ```c // 设备树节点示例(.dts文件) /* my_device { compatible = "my_vendor,my_device"; reg = <0x12345000 0x100>; interrupt-parent = <&gpio>; interrupts = <5 IRQ_TYPE_EDGE_RISING>; my-special-param = <0x55>; }; */ // 驱动代码核心结构 #include <linux/platform_device.h> #include <linux/of.h> #include <linux/interrupt.h> /* 私有数据结构体 */ struct my_priv { void __iomem *reg_base; int irq_num; u32 special_param; }; /* 中断处理函数(顶半部) */ static irqreturn_t my_irq_handler(int irq, void *dev_id) { struct my_priv *priv = dev_id; // 1.快速处理关键操作 // 2.清除中断标志(根据硬件特性) // 3.触发底半部处理(如tasklet/workqueue) return IRQ_HANDLED; } /* 设备树信息解析 */ static int parse_dt(struct platform_device *pdev, struct my_priv *priv) { struct device_node *np = pdev->dev.of_node; // 获取寄存器地址 priv->reg_base = of_iomap(np, 0); if (!priv->reg_base) { dev_err(&pdev->dev, "Failed to iomap\n"); return -ENOMEM; } // 获取中断号 priv->irq_num = platform_get_irq(pdev, 0); if (priv->irq_num < 0) { dev_err(&pdev->dev, "Failed to get IRQ\n"); return priv->irq_num; } // 读取自定义参数 if (of_property_read_u32(np, "my-special-param", &priv->special_param)) { dev_warn(&pdev->dev, "Using default parameter\n"); priv->special_param = 0x00; } return 0; } /* Platform驱动主体 */ static int my_probe(struct platform_device *pdev) { struct my_priv *priv; int ret; // 1.分配私有数据结构 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); // 2.解析设备树 if ((ret = parse_dt(pdev, priv)) != 0) return ret; // 3.注册中断处理 ret = devm_request_irq(&pdev->dev, priv->irq_num, my_irq_handler, IRQF_TRIGGER_RISING, "my_irq", priv); if (ret) { dev_err(&pdev->dev, "Failed to request IRQ %d\n", priv->irq_num); return ret; } // 4.初始化硬件 // iowrite32(0x01, priv->reg_base + REG_CTRL); // 5.注册字符设备(可选) // cdev_add(...); platform_set_drvdata(pdev, priv); return 0; } static int my_remove(struct platform_device *pdev) { struct my_priv *priv = platform_get_drvdata(pdev); // 释放资源 // iounmap(priv->reg_base); // free_irq(...); return 0; } /* 设备树匹配表 */ static const struct of_device_id my_of_match[] = { { .compatible = "my_vendor,my_device" }, {} }; MODULE_DEVICE_TABLE(of, my_of_match); /* Platform驱动结构体 */ static struct platform_driver my_driver = { .probe = my_probe, .remove = my_remove, .driver = { .name = "my_driver", .of_match_table = my_of_match, }, }; module_platform_driver(my_driver); ``` $$ 关键实现要素分析 $$ 1. **设备树集成** - 通过$of_iomap()$获取寄存器物理地址映射 - 使用$platform_get_irq()$获取设备树中定义的中断- 通过$of_property_read_xxx()$系列函数读取自定义参数 2. **中断处理机制** - 申请中断:`devm_request_irq()` - 中断处理函数需返回$IRQ_HANDLED$或$IRQ_NONE$ - 顶半部要求快速执行,复杂操作应使用: - Tasklet(原子上下文) - Workqueue(可休眠) 3. **资源管理** - 使用$devm_$系列API(自动释放资源): - $devm_kzalloc()$ - $devm_request_irq()$ - $devm_ioremap()$ $$ 开发流程要点 $$ 1. **设备树配置** ```dts // 修改设备树文件(如my_device.dtsi) my_device { compatible = "my_vendor,my_device"; reg = <0x12345000 0x100>; // 寄存器物理地址及长度 interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>; // 中断号及触发方式 my-custom-param = <0xabcd>; // 自定义参数 }; ``` 2. **驱动编译配置** ```makefile obj-$(CONFIG_MY_DRIVER) += my_driver.o ``` 3. **调试命令** ```bash # 查看已加载驱动 lsmod | grep my_driver # 查看设备树节点信息 dtc -I fs /proc/device-tree # 检查中断注册情况 cat /proc/interrupts | grep my_irq ``` $$ 注意事项 $$ 1. **并发与同步** - 中断上下文中**不能**使用可能休眠的函数(如$mutex_lock$) - 共享数据保护建议使用$spin_lock_irqsave()$ 2. **错误处理** - 检查所有可能失败的操作: ```c if (!priv->reg_base) { dev_err(&pdev->dev, "iomap failed at %pa\n", &res->start); return -ENOMEM; } ``` 3. **兼容性验证** - 确保设备树节点的compatible字符串与驱动中的定义完全匹配 4. **中断调试** - 使用$cat /proc/interrupts$验证中断触发计数 - 通过$devm_request_irq()$的返回值判断中断申请是否成功 $$ 扩展功能实现 $$ 1. **IOCTL控制接口** ```c long my_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct my_priv *priv = file->private_data; switch (cmd) { case GET_SPECIAL_PARAM: return put_user(priv->special_param, (int __user *)arg); case SET_SPECIAL_PARAM: return get_user(priv->special_param, (int __user *)arg); default: return -ENOTTY; } } ``` 2. **Proc文件系统接口** ```c static int my_proc_show(struct seq_file *m, void *v) { struct my_priv *priv = m->private; seq_printf(m, "Current status:\n"); seq_printf(m, "Special param: 0x%x\n", priv->special_param); return 0; } // 在probe函数中创建 proc_create_single_data("my_device", 0, NULL, my_proc_show, priv); ``` 该模板完整展示了结合设备树中断处理和平台驱动框架的实现方式,适用于需要精确硬件控制的设备(如GPIO控制器、自定义外设等)。实际开发中需根据具体硬件手册补充寄存器操作逻辑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值