[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磁盘分区、格式化和挂载方式

《Linux磁盘分区、格式化和挂载方式》本文详细介绍了Linux系统中磁盘分区、格式化和挂载的基本操作步骤和命令,包括MBR和GPT分区表的区别、fdisk和gdisk命令的使用、常见的文件系统格式以... 目录一、磁盘分区表分类二、fdisk命令创建分区1、交互式的命令2、分区主分区3、创建扩展分区,然后

Linux中chmod权限设置方式

《Linux中chmod权限设置方式》本文介绍了Linux系统中文件和目录权限的设置方法,包括chmod、chown和chgrp命令的使用,以及权限模式和符号模式的详细说明,通过这些命令,用户可以灵活... 目录设置基本权限命令:chmod1、权限介绍2、chmod命令常见用法和示例3、文件权限详解4、ch

Linux内核之内核裁剪详解

《Linux内核之内核裁剪详解》Linux内核裁剪是通过移除不必要的功能和模块,调整配置参数来优化内核,以满足特定需求,裁剪的方法包括使用配置选项、模块化设计和优化配置参数,图形裁剪工具如makeme... 目录简介一、 裁剪的原因二、裁剪的方法三、图形裁剪工具四、操作说明五、make menuconfig

Linux使用nohup命令在后台运行脚本

《Linux使用nohup命令在后台运行脚本》在Linux或类Unix系统中,后台运行脚本是一项非常实用的技能,尤其适用于需要长时间运行的任务或服务,本文我们来看看如何使用nohup命令在后台... 目录nohup 命令简介基本用法输出重定向& 符号的作用后台进程的特点注意事项实际应用场景长时间运行的任务服

什么是cron? Linux系统下Cron定时任务使用指南

《什么是cron?Linux系统下Cron定时任务使用指南》在日常的Linux系统管理和维护中,定时执行任务是非常常见的需求,你可能需要每天执行备份任务、清理系统日志或运行特定的脚本,而不想每天... 在管理 linux 服务器的过程中,总有一些任务需要我们定期或重复执行。就比如备份任务,通常会选在服务器资

Linux限制ip访问的解决方案

《Linux限制ip访问的解决方案》为了修复安全扫描中发现的漏洞,我们需要对某些服务设置访问限制,具体来说,就是要确保只有指定的内部IP地址能够访问这些服务,所以本文给大家介绍了Linux限制ip访问... 目录背景:解决方案:使用Firewalld防火墙规则验证方法深度了解防火墙逻辑应用场景与扩展背景:

Linux下MySQL8.0.26安装教程

《Linux下MySQL8.0.26安装教程》文章详细介绍了如何在Linux系统上安装和配置MySQL,包括下载、解压、安装依赖、启动服务、获取默认密码、设置密码、支持远程登录以及创建表,感兴趣的朋友... 目录1.找到官网下载位置1.访问mysql存档2.下载社区版3.百度网盘中2.linux安装配置1.

Linux使用粘滞位 (t-bit)共享文件的方法教程

《Linux使用粘滞位(t-bit)共享文件的方法教程》在Linux系统中,共享文件是日常管理和协作中的常见任务,而粘滞位(StickyBit或t-bit)是实现共享目录安全性的重要工具之一,本文将... 目录文件共享的常见场景基础概念linux 文件权限粘滞位 (Sticky Bit)设置共享目录并配置粘

linux-基础知识3

打包和压缩 zip 安装zip软件包 yum -y install zip unzip 压缩打包命令: zip -q -r -d -u 压缩包文件名 目录和文件名列表 -q:不显示命令执行过程-r:递归处理,打包各级子目录和文件-u:把文件增加/替换到压缩包中-d:从压缩包中删除指定的文件 解压:unzip 压缩包名 打包文件 把压缩包从服务器下载到本地 把压缩包上传到服务器(zip