[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使用nload监控网络流量的方法

《Linux使用nload监控网络流量的方法》Linux中的nload命令是一个用于实时监控网络流量的工具,它提供了传入和传出流量的可视化表示,帮助用户一目了然地了解网络活动,本文给大家介绍了Linu... 目录简介安装示例用法基础用法指定网络接口限制显示特定流量类型指定刷新率设置流量速率的显示单位监控多个

ElasticSearch+Kibana通过Docker部署到Linux服务器中操作方法

《ElasticSearch+Kibana通过Docker部署到Linux服务器中操作方法》本文介绍了Elasticsearch的基本概念,包括文档和字段、索引和映射,还详细描述了如何通过Docker... 目录1、ElasticSearch概念2、ElasticSearch、Kibana和IK分词器部署

Java调用DeepSeek API的最佳实践及详细代码示例

《Java调用DeepSeekAPI的最佳实践及详细代码示例》:本文主要介绍如何使用Java调用DeepSeekAPI,包括获取API密钥、添加HTTP客户端依赖、创建HTTP请求、处理响应、... 目录1. 获取API密钥2. 添加HTTP客户端依赖3. 创建HTTP请求4. 处理响应5. 错误处理6.

Linux流媒体服务器部署流程

《Linux流媒体服务器部署流程》文章详细介绍了流媒体服务器的部署步骤,包括更新系统、安装依赖组件、编译安装Nginx和RTMP模块、配置Nginx和FFmpeg,以及测试流媒体服务器的搭建... 目录流媒体服务器部署部署安装1.更新系统2.安装依赖组件3.解压4.编译安装(添加RTMP和openssl模块

linux下多个硬盘划分到同一挂载点问题

《linux下多个硬盘划分到同一挂载点问题》在Linux系统中,将多个硬盘划分到同一挂载点需要通过逻辑卷管理(LVM)来实现,首先,需要将物理存储设备(如硬盘分区)创建为物理卷,然后,将这些物理卷组成... 目录linux下多个硬盘划分到同一挂载点需要明确的几个概念硬盘插上默认的是非lvm总结Linux下多

Deepseek R1模型本地化部署+API接口调用详细教程(释放AI生产力)

《DeepseekR1模型本地化部署+API接口调用详细教程(释放AI生产力)》本文介绍了本地部署DeepSeekR1模型和通过API调用将其集成到VSCode中的过程,作者详细步骤展示了如何下载和... 目录前言一、deepseek R1模型与chatGPT o1系列模型对比二、本地部署步骤1.安装oll

浅析如何使用Swagger生成带权限控制的API文档

《浅析如何使用Swagger生成带权限控制的API文档》当涉及到权限控制时,如何生成既安全又详细的API文档就成了一个关键问题,所以这篇文章小编就来和大家好好聊聊如何用Swagger来生成带有... 目录准备工作配置 Swagger权限控制给 API 加上权限注解查看文档注意事项在咱们的开发工作里,API

linux进程D状态的解决思路分享

《linux进程D状态的解决思路分享》在Linux系统中,进程在内核模式下等待I/O完成时会进入不间断睡眠状态(D状态),这种状态下,进程无法通过普通方式被杀死,本文通过实验模拟了这种状态,并分析了如... 目录1. 问题描述2. 问题分析3. 实验模拟3.1 使用losetup创建一个卷作为pv的磁盘3.

一分钟带你上手Python调用DeepSeek的API

《一分钟带你上手Python调用DeepSeek的API》最近DeepSeek非常火,作为一枚对前言技术非常关注的程序员来说,自然都想对接DeepSeek的API来体验一把,下面小编就来为大家介绍一下... 目录前言免费体验API-Key申请首次调用API基本概念最小单元推理模型智能体自定义界面总结前言最

JAVA调用Deepseek的api完成基本对话简单代码示例

《JAVA调用Deepseek的api完成基本对话简单代码示例》:本文主要介绍JAVA调用Deepseek的api完成基本对话的相关资料,文中详细讲解了如何获取DeepSeekAPI密钥、添加H... 获取API密钥首先,从DeepSeek平台获取API密钥,用于身份验证。添加HTTP客户端依赖使用Jav