[linux api] of_irq_init

2024-04-14 11:36
文章标签 linux init api irq

本文主要是介绍[linux api] of_irq_init,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

总结

以如下级联的中断控制器为例:
在这里插入图片描述

of_irq_init会确保先初始化父控制器再初始化子控制器,也即整体按照层序遍历的顺序进行初始化,以上图为例,其初始化顺序为:

  1. intc0
  2. intc1-2
  3. intc3-6

具体实现则分为两个阶段:

第一阶段

遍历所有设备节点,并与参数matches进行匹配,找出所有的中断控制器节点,串到一条名为intc_desc_list的链表上(以下节点在链表中的顺序可能和设备节点在DTS文件中的位置有关,这里仅做示例):

intc_desc_list
intc0
intc1
intc2
intc3
intc4
intc5
intc6

第二阶段

parent(初始值为NULL)作为当前的父控制器,从intc_desc_list中找出以其为父控制器的中断控制器,对其进行初始化后,从intc_desc_list取出,并添加到另一条链表intc_parent_list,然后取出intc_parent_list的首元素,作为新一轮的parent再重复上述步骤。这是典型的基于队列实现层序遍历的做法,不再赘述。

参数

matchesstruct of_device_id类型的数组,该结构定义如下:

struct of_device_id {char	name[32];char	type[32];char	compatible[128];const void *data;
};

其中最重要的是compatible字段,用来匹配中断控制器设备节点的compatible属性。

返回值


源码分析如下:

void __init of_irq_init(const struct of_device_id *matches)
{const struct of_device_id *match;struct device_node *np, *parent = NULL;struct of_intc_desc *desc, *temp_desc;struct list_head intc_desc_list, intc_parent_list;/* 初始化两条链表 *//* intc_desc_list串起所有中断控制器设备节点 *//* intc_parent_list用来实现基于队列的层序遍历 */INIT_LIST_HEAD(&intc_desc_list);INIT_LIST_HEAD(&intc_parent_list);/* 遍历所有设备树中的设备节点 *//* 用每个节点和matches数组的每个成员中的compatible字符串比较 *//* 把匹配中的成员赋值给match */for_each_matching_node_and_match(np, matches, &match) {/* 一般来讲,matches数组就是在中断控制器驱动中用IRQCHIP_DECLARE生成的 */         /* 能匹配上就肯定是中断控制器对应的设备节点 *//* 但是严谨起见,还是检擦interrupt-controller属性 */if (!of_property_read_bool(np, "interrupt-controller") ||/* 确定是中断控制器之后,还要检查status属性 *//* status属性不存在或者status="ok"/"okay"表明设备可用 *//* status="disable"表明设备不可用 *//* 设备不可用就继续遍历下一个 */!of_device_is_available(np))continue;/* 到这里,确定设备节点是中断控制器节点且可用 *//* match->data指向中断控制器的初始化函数 *//* 肯定得有初始化函数,不然折腾半天折腾了个寂寞 */if (WARN(!match->data, "of_irq_init: no init function for %s\n",match->compatible))continue;/* 申请of_intc_desc内存 */desc = kzalloc(sizeof(*desc), GFP_KERNEL);if (!desc) {of_node_put(np);goto err;}/* 记录下中断控制器的初始化函数以及设备节点 */desc->irq_init_cb = match->data;desc->dev = of_node_get(np);/* 查找并记录当前中断控制器的父控制器 */desc->interrupt_parent = of_parse_phandle(np, "interrupts-extended", 0);if (!desc->interrupt_parent)desc->interrupt_parent = of_irq_find_parent(np);if (desc->interrupt_parent == np) {of_node_put(desc->interrupt_parent);desc->interrupt_parent = NULL;}/* 将中断控制器串入intc_desc_list */list_add_tail(&desc->list, &intc_desc_list);}/* 到这里,intc_desc_list里面就串上了所有的中断控制器节点 *//* 接下来就是按照层序遍历所有中断控制器并进行初始化 *//* 第一层是root domain,然后第二场是root domain的子控制器,以此类推 *//* intc_desc_list非空表示还有中断控制器尚未完成初始化 */while (!list_empty(&intc_desc_list)) {/* 处理当前所有以parent为父控制器的中断控制器 *//* parent初始值为NULL,NULL表示没有父控制器 *//* 也即第一轮处理root domain(root domain的parent为空) */list_for_each_entry_safe(desc, temp_desc, &intc_desc_list, list) {int ret;/* 不是我们要找的中断控制器则继续遍历下一个 */if (desc->interrupt_parent != parent)continue;/* 是我们要找的控制器,则将该控制器从链表中摘出来 */list_del(&desc->list);/* 设置OF_POPULATED标志标识设备(中断控制器)已经被创建 */of_node_set_flag(desc->dev, OF_POPULATED);pr_debug("of_irq_init: init %pOF (%p), parent %p\n",desc->dev,desc->dev, desc->interrupt_parent);/* 中断控制器初始化 */ret = desc->irq_init_cb(desc->dev,desc->interrupt_parent);if (ret) {pr_err("%s: Failed to init %pOF (%p), parent %p\n",__func__, desc->dev, desc->dev,desc->interrupt_parent);/* 中断控制器初始化失败则要取消OF_POPULATED标志 */of_node_clear_flag(desc->dev, OF_POPULATED);kfree(desc);continue;}/* 将当前完成初始化的中断控制器推入intc_parent_list链表 *//*   intc_parent_list本质上是一个双向链表实现的队列 *//*   是实现层序遍历的关键 */list_add_tail(&desc->list, &intc_parent_list);}/* 至此,当前parent的所有孩子节点初始化完毕 *//* 获取intc_parent_list队列的队首 */desc = list_first_entry_or_null(&intc_parent_list,typeof(*desc), list);if (!desc) {/* intc_parent_list链表为空 *//* 也就是没有爹了(或者说当前爹是最后一个爹,没有可供匹配的新爹了) *//* 此时intc_desc_list有两种可能: *//*   1. intc_desc_list中的节点一个都没与当前的爹匹配上 *//*      而可供尝试匹配的新爹又没有了 *//*   2. intc_desc_list中的节点都与当前的爹匹配上了 *//*      但都初始化失败了,intc_desc_list此时为空 */pr_err("of_irq_init: children remain, but no parents\n");break;}list_del(&desc->list);/* 取出的队首作为新一轮的parent */parent = desc->dev;kfree(desc);}/* 正常执行到这里 *//* intc_parent_list和intc_desc_list都会是空的 *//* 出现错误时,跳出循环,两条链表非空,要做如下的清理工作: */list_for_each_entry_safe(desc, temp_desc, &intc_parent_list, list) {list_del(&desc->list);kfree(desc);}
err:list_for_each_entry_safe(desc, temp_desc, &intc_desc_list, list) {list_del(&desc->list);of_node_put(desc->dev);kfree(desc);}
}

这篇关于[linux api] of_irq_init的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/902903

相关文章

防止Linux rm命令误操作的多场景防护方案与实践

《防止Linuxrm命令误操作的多场景防护方案与实践》在Linux系统中,rm命令是删除文件和目录的高效工具,但一旦误操作,如执行rm-rf/或rm-rf/*,极易导致系统数据灾难,本文针对不同场景... 目录引言理解 rm 命令及误操作风险rm 命令基础常见误操作案例防护方案使用 rm编程 别名及安全删除

Linux下MySQL数据库定时备份脚本与Crontab配置教学

《Linux下MySQL数据库定时备份脚本与Crontab配置教学》在生产环境中,数据库是核心资产之一,定期备份数据库可以有效防止意外数据丢失,本文将分享一份MySQL定时备份脚本,并讲解如何通过cr... 目录备份脚本详解脚本功能说明授权与可执行权限使用 Crontab 定时执行编辑 Crontab添加定

PHP应用中处理限流和API节流的最佳实践

《PHP应用中处理限流和API节流的最佳实践》限流和API节流对于确保Web应用程序的可靠性、安全性和可扩展性至关重要,本文将详细介绍PHP应用中处理限流和API节流的最佳实践,下面就来和小编一起学习... 目录限流的重要性在 php 中实施限流的最佳实践使用集中式存储进行状态管理(如 Redis)采用滑动

使用docker搭建嵌入式Linux开发环境

《使用docker搭建嵌入式Linux开发环境》本文主要介绍了使用docker搭建嵌入式Linux开发环境,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面... 目录1、前言2、安装docker3、编写容器管理脚本4、创建容器1、前言在日常开发全志、rk等不同

linux系统上安装JDK8全过程

《linux系统上安装JDK8全过程》文章介绍安装JDK的必要性及Linux下JDK8的安装步骤,包括卸载旧版本、下载解压、配置环境变量等,强调开发需JDK,运行可选JRE,现JDK已集成JRE... 目录为什么要安装jdk?1.查看linux系统是否有自带的jdk:2.下载jdk压缩包2.解压3.配置环境

Linux搭建ftp服务器的步骤

《Linux搭建ftp服务器的步骤》本文给大家分享Linux搭建ftp服务器的步骤,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录ftp搭建1:下载vsftpd工具2:下载客户端工具3:进入配置文件目录vsftpd.conf配置文件4:

Linux实现查看某一端口是否开放

《Linux实现查看某一端口是否开放》文章介绍了三种检查端口6379是否开放的方法:通过lsof查看进程占用,用netstat区分TCP/UDP监听状态,以及用telnet测试远程连接可达性... 目录1、使用lsof 命令来查看端口是否开放2、使用netstat 命令来查看端口是否开放3、使用telnet

Linux系统管理与进程任务管理方式

《Linux系统管理与进程任务管理方式》本文系统讲解Linux管理核心技能,涵盖引导流程、服务控制(Systemd与GRUB2)、进程管理(前台/后台运行、工具使用)、计划任务(at/cron)及常用... 目录引言一、linux系统引导过程与服务控制1.1 系统引导的五个关键阶段1.2 GRUB2的进化优

Linux查询服务器 IP 地址的命令详解

《Linux查询服务器IP地址的命令详解》在服务器管理和网络运维中,快速准确地获取服务器的IP地址是一项基本但至关重要的技能,下面我们来看看Linux中查询服务器IP的相关命令使用吧... 目录一、hostname 命令:简单高效的 IP 查询工具命令详解实际应用技巧注意事项二、ip 命令:新一代网络配置全

linux安装、更新、卸载anaconda实践

《linux安装、更新、卸载anaconda实践》Anaconda是基于conda的科学计算环境,集成1400+包及依赖,安装需下载脚本、接受协议、设置路径、配置环境变量,更新与卸载通过conda命令... 目录随意找一个目录下载安装脚本检查许可证协议,ENTER就可以安装完毕之后激活anaconda安装更