arm 架构中断

2 篇文章 0 订阅
订阅专栏

中断是什么

中断就是在cpu执行程序的过程中,突然发生异常 (包括复位、指令错误等等异常,中断只是异常其中一种),可以打断当前正在执行的程序,临时先处理比较紧急的事情,当处理完成了,再回到原来的程序继续执行。

中断如何发生

首先,在一个cpu中 中断源有很多(比如gpio中断、定时器中断等等),那么为了管理这些中断,就需要一个中断控制器。
当发生中断时,相应的中断源会给中断控制器发出信号,中断控制器再给cpu发信号,最后cpu处理中断。
在这里插入图片描述

中断的大概流程

  1. 初始化:
  • 使能中断源(允许发生中断)
  • 中断控制器可以选择屏蔽或不屏蔽中断,设置中断优先级等
  • cpu 使能中断总开关
  1. 异常跳转:
  • cpu 每执行完一条指令就会查看有无异常发生
  • 发生异常,cpu 分辩异常类型
  • cpu 被强制跳转到异常向量表(汇编)中的跳转地址
  • 跳转到相应的异常服务函数
  1. IRQ 服务函数(汇编):
  • 保存现场,保证当前执行的程序能完好返回(存储 指令寄存器(PC),以及存数据的寄存器等等各种寄存器的值,会采用压栈的方式)
  • 获取中断id(a7 架构,其中可能还需要切换处理器模式等等),根据id跳转到对应的中断处理函数。
  • 中断处理函数可以是我们自己编写的,代表的是中断发生后要处理的事情
  • 处理完成,返回中断服务函数。
  • 还原现场(将各个寄存器的值还回,指令寄存器需要 -4 再还回,涉及到arm 处理器的3级指令流水线)

arm三级指令流水线:
取指-》译指-》执行
比如有3条指令(因为3条指令的地址肯定时连续的,所以假设它们的偏移地址分别是0、4、8),CPU 正在执行一条指令,下一条指令在译指状态,第3条指令在取指状态。
那么执行当前指令时,指令寄存器中存储的指令地址其实时第3条指令的地址,如果按原模原样返回,就把第二条指令遗漏了,这会造成程序崩溃等等很大的错误。

中断控制器(cotex-a架构 —— GIC)

不了解,先空着

异常向量表

异常向量表 是一个地址表,存储着各大类异常服务函数的首地址,当对应的异常发生时cpu会被强制跳转到该地址。

以下是a7 内核的异常向量表,当不同异常发生时会进入各自的服务函数:
在这里插入图片描述
异常向量表在程序的最开始被定义,可以参考uboot arch/arm/cpu/armv7/start.s (这是uboot最开始的代码),imx6ull的异常向量表如下:

_start:
ldr pc, =Reset_Handler /* 复位中断 */
ldr pc, =Undefined_Handler /* 未定义指令中断 */
ldr pc, =SVC_Handler /* SVC(Supervisor)中断 */
ldr pc, =PrefAbort_Handler /* 预取终止中断 */
ldr pc, =DataAbort_Handler /* 数据终止中断 */
ldr pc, =NotUsed_Handler /* 未使用中断 */
ldr pc, =IRQ_Handler /* IRQ 中断 */
ldr pc, =FIQ_Handler /* FIQ(快速中断)未定义中断 */

异常向量表偏移

之前说异常向量表在程序最开始的地址定义(地址 0x00000000),但是这是不可取的,地址0x00000000 是arm 内核iROM的起始地址,存放着cpu最早的启动代码,更早于uboot,uboot靠它来引导(初始化时钟等等,有关这部分可以看uboot的启动过程);iROM 中的代码是由CPU 厂商决定的,用户是不可修改的,所以我们的异常向量表无法放在0x0000_0000 这个地址。

那么它的解决办法是:CPU 厂商已经解决了这个问题,在cpu 的内部ram 中设计一个指向异常向量表地址寄存器Exception Vector Table,只要将异常向量表的首地址写入这个寄存器,发生异常时CPU 就可以通过它找到异常向量表啦。
根据不同处理器设计的不同,它们的异常向量表偏移地址也会不同,比如说 0X87800000(这是内存地址) 这是6ull裸机程序的起始地址,那么中断向量表也就放在这里。
在这里插入图片描述

中断与线程的切换

Linux 中断的演进

硬中断和软中断

Linux 系统把中断的意义扩展了,对于按键中断等硬件产生的中断,称之为“硬件中断” (hard irq)。
每个硬件中断都有对应的处理函数,比如按键中断、网卡中断的处理函数肯定不一样。
为方便理解,你可以先认为对硬件中断的处理是用数组来实现的,数组里存放的是函数指针:
在这里插入图片描述
注意:上图是简化的, Linux 中这个数组复杂多了。
当发生 A 中断时,对应的 irq_function_A 函数被调用。硬件导致该函数被调用。

相对的,还可以人为地制造中断:软件中断(soft irq),如下图所示:
在这里插入图片描述
注意:上图是简化的, Linux 中这个数组复杂多了。
问题来了:
a. 软件中断何时生产?
由软件决定,对于 X 号软件中断,只需要把它的 flag 设置为 1 就表示发生了该中断。
b. 软件中断何时处理?
软件中断嘛,并不是那么十万火急,有空再处理它好了。
什么时候有空?不能让它一直等吧?
Linux 系统中,各种硬件中断频繁发生,至少定时器中断每 10ms 发生一次,那取个巧?
在处理完硬件中断后,再去处理软件中断?就这么办!

有哪些软件中断?
查内核源码 include/linux/interrupt.h

enum
{
	HI_SOFTIRQ=0,
	TIMER_SOFTIRQ,
	NET_TX_SOFTIRQ,
	NET_RX_SOFTIRQ,
	BLOCK_SOFTIRQ,
	IRQ_POLL_SOFTIRQ,
	TASKLET_SOFTIRQ,
	SCHED_SOFTIRQ,
	HRTIMER_SOFTIRQ, /* Unused, but kept as tools rely on the
			    numbering. Sigh! */
	RCU_SOFTIRQ,    /* Preferable RCU should always be the last softirq */

	NR_SOFTIRQS
};

怎么触发软件中断?最核心的函数是 raise_softirq,简单地理解就是设置 softirq_veq[nr]的标记位:

extern void raise_softirq(unsigned int nr);

怎么设置软件中断的处理函数:

extern void open_softirq(int nr, void (*action) (struct soft_action*));

硬中断遵循两个规则

  1. 不能被打断(不允许被其它线程或中断打断(同一个中断或是优先级更高也不行))。
  2. 中断程序必须简洁,精炼,不允许执行太长的时间。

在硬件中断执行过程中,硬件中断使能是被关闭的,中断程序执行完后才会打开。
对于第一点,解释是: 假如中断可以嵌套,当执行A中断时发生了B中断,被B打断,需要保存A的现场到栈里,那么B也有可能被C打断。如果不停的被打断,那么意味着需要若干各栈,最后内存空间不够用了,系统就会崩溃。所以为了简单,中断不允许嵌套。

第二点,在执行中断函数时,其它线程无法执行,如果中断时间过长,就显得系统卡顿,其他中断也无法快速得到处理。

在 Linux 系统中使用中断是挺简单的,为某个中断 irq 注册中断处理函数 handler,可以使用 request_irq 函数:

static inline int __must_check
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
	    const char *name, void *dev)

在 handler 函数中,代码尽可能高效。

但是,处理某个中断要做的事情就是很多,没办法加快。比如对于按键中断,我们需要等待几十毫秒消除机械抖动。难道要在 handler 中等待吗?对于计算机来说,这可是一个段很长的时间。
怎么办?

中断函数耗时长的处理方式

要处理的事情实在太多,拆分为:上半部、下半部

当然,在很多情况下中断程序无奈需要执行耗时的操作,比如网卡中断,接收数据时就很耗时。
Linux系统对中断做出扩展,中断函数可以分为上半部下半部 。上半部用于处理 比较紧急且快速 的事情(比如清除中断标志位,防止同一次中断信号不断触发),而下半部则处理耗时长的事。

在这里插入图片描述

下半部以软中断的形式处理,软中断根据Linux 系统的不断优化有以下几类:

下半部要做的事情耗时不是太长: 小任务 tasklet

当下半部比较耗时但是能忍受,并且它的处理比较简单时,可以用 tasklet 来处理下半部。 tasklet 是使用软件中断来实现。
在这里插入图片描述
Linux 中硬件中断与软件中断调用流程图:
在这里插入图片描述
注意:在开始处理软中断前后,都会对preempt_count做++ 和-- 处理,preempt_count 保证不会执行其他多余的软中断。
软中断是可以被硬中断所打断的,当执行完硬件中断后会用 !in_interrupt() 判断被打断前是否有软中断正在执行,如果有那么将不会执行其他软中断,而是恢复被打断的软中断现场继续执行。
硬件中断与软件中断,始终保持多对1 的关系。

preempt:抢占。

耗时的下半部中断函数就可以交给软件中断函数来处理。它们是可以被硬件中断函数打断的,不能被普通线程打断。(这里单指软中断的一种方式:小任务;后面介绍的work线程,和软中断线程也属于软中断,它们是可以被调度的)
此种小任务方式的缺点是:如果软中断要处理的事务过多,那么它会一直占用cpu时间,普通线程得不到执行,导致系统卡钝。

下半部要做的事情太多并且很复杂:工作队列(线程化中断处理方式)

在中断下半部 tasklet的执行过程中,虽然是开中断的,期间可以处理各类中断。但是毕竟整个中断的处理还没走完,这期间 APP 是无法执行的。
假设下半部要执行 1、 2 分钟,在这 1、 2 分钟里 APP 都是无法响应的。
这谁受得了?
所以,如果中断要做的事情实在太耗时,那就不能用软件中断来做,而应该用内核线程来做:在中断上半部唤醒内核线程。内核线程和 APP 都一样竞争执行, APP 有机会执行,系统不会卡顿。
这个内核线程是系统帮我们创建的,一般是 kworker 线程,内核中有很多这样的线程:
在这里插入图片描述
kworker 线程要去“工作队列” (work queue)上取出一个一个“工作” (work),来执行它里面的函数。

那我们怎么使用 work、 work queue 呢?
a. 创建 work:
你得先写出一个函数,然后用这个函数填充一个 work 结构体。比如:
static DECLARE_WORK(work_struct,work_func);
b. 要执行这个函数时,把 work 提交给 work queue 就可以了:
schedule_work(work_struct);
上述函数会把 work 提供给系统默认的 work queue: system_wq,它是一个队列。
c. 谁来执行 work 中的函数?
不用我们管, schedule_work 函数不仅仅是把 work 放入队列,还会把 kworker 线程唤醒。此线程抢到
时间运行时,它就会从队列中取出 work,执行里面的函数。
d. 谁把 work 提交给 work queue?
在中断场景中,可以在中断上半部调用 schedule_work 函数。

工作队列相关内核函数被定义在:include/linux/workqueue.h 工作队列的使用

新技术: threaded irq

使用线程来处理中断,并不是什么新鲜事。使用 work 就可以实现,但是需要定义 work、调用schedule_work,好麻烦啊。
太懒了太懒了,就这 2 步你们都不愿意做。
好,内核是为懒人服务的,再杀出一个函数:
在这里插入图片描述
你可以只提供 thread_fn,系统会为这个函数创建一个内核线程。发生中断时,内核线程就会执行这个函数。

说你懒是开玩笑,内核开发者也不会那么在乎懒人。
以前用 work 来线程化地处理中断,一个 worker 线程只能由一个 CPU 执行,多个中断的 work 都由同一个 worker 线程来处理,在单 CPU 系统中也只能忍着了。但是在 SMP 系统中,明明有那么多 CPU 空着,你偏偏让多个中断挤在这个 CPU 上?
新技术 threaded irq,为每一个中断都创建一个内核线程;多个中断的内核线程可以分配到多个 CPU上执行,这提高了效率。

Linux 中断系统中的重要数据

本节内容,可以从 request_irq(include/linux/interrupt.h)函数一路分析得到。
能弄清楚下面这个图,对 Linux 中断系统的掌握也基本到位了。
在这里插入图片描述在这里插入图片描述

最核心的结构体是 irq_desc,之前为了易于理解,我们说在 Linux 内核中有一个中断数组,对于每一个硬件中断,都有一个数组项,这个数组就是 irq_desc 数组。
注意:如果内核配置了 CONFIG_SPARSE_IRQ,那么它就会用基数树(radix tree)来代替 irq_desc 数组。SPARSE 的意思是“稀疏”,假设大小为 1000 的数组中只用到 2 个数组项,那不是浪费嘛?所以在中断比较 “稀疏” 的情况下可以用基数树来代替数组。

1、irq_desc 数组

irq_desc 结构体在 include/linux/irqdesc.h 中定义,主要内容如下图:
在这里插入图片描述
每一个 irq_desc 数组项中都有一个函数: handle_irq,还有一个 action 链表。要理解它们,需要先看中断结构图:
在这里插入图片描述
外部设备 1、外部设备 n 共享一个 GPIO 中断 B,多个 GPIO 中断汇聚到 GIC(通用中断控制器)的 A 号中断, GIC 再去中断 CPU。那么软件处理时就是反过来,CPU先读取 GIC 获得中断号 A(哪一个模块发生了中断),再细分出 GPIO 中断 B(哪一个GPIO 发生了中断),最后判断是哪一个外部芯片发生了中断。

所以,中断的处理函数来源有三:
① GIC 的处理函数:
当CPU 从GIC 中读出中断号A后,就会调用irq_desc[A].handle_irq。
假设 irq_desc[A].handle_irq 是 XXX_gpio_irq_handler(XXX 指厂家),这个函数需要读取芯片的 GPIO控制器,细分发生的是哪一个 GPIO 中断(假设是 B),再去调用 irq_desc[B]. handle_irq。
注意: irq_desc[A].handle_irq 细分出中断号 B,调用对应的 irq_desc[B].handle_irq。
显然中断 A 是 CPU 感受到的顶层的中断, GIC 中断 CPU 时, CPU 读取 GIC 状态得到中断 A。

② 模块的中断处理函数:
比如对于 GPIO 模块向 GIC 发出的中断 B,它的处理函数是 irq_desc[B].handle_irq。
BSP 开发人员会设置对应的处理函数,一般是 handle_level_irq 或 handle_edge_irq,从名字上看是用来处理电平触发的中断、边沿触发的中断。
注意: 导致 GPIO 中断 B 发生的原因很多,可能是外部设备 1,可能是外部设备 n,可能只是某一个设备,也可能是多个设备。所以 irq_desc[B].handle_irq 会调用某个链表里的函数,这些函数由外部设备提供。这些函数自行判断该中断是否自己产生,若是则处理。

③ 外部设备提供的处理函数:
这里说的“外部设备”可能是芯片,也可能总是简单的按键。它们的处理函数由自己驱动程序提供,这是最熟悉这个设备的“人”:它知道如何判断设备是否发生了中断,如何处理中断。
对于共享中断,比如 GPIO 中断 B,它的中断来源可能有多个,每个中断源对应一个中断处理函数。所以 irq_desc[B]中应该有一个链表,存放着多个中断源的处理函数。
一旦程序确定发生了 GPIO 中断 B,那么就会从链表里把那些函数取出来,一一执行。这个链表就是 action 链表。

对于我们举的这个例子来说, irq_desc 数组如下:
在这里插入图片描述

2、irqaction 结构体

irqaction 结构体在 include/linux/interrupt.h 中定义,主要内容如下图:
在这里插入图片描述
当调用 request_irq、 request_threaded_irq 注册中断处理函数时,内核就会构造一个 irqaction 结
构体。

在里面保存 name、 dev_id 等,最重要的是 handler、 thread_fn、 thread。
handler 是中断处理的上半部函数,用来处理紧急的事情。
thread_fn 对应一个内核线程 thread,当 handler 执行完毕, Linux 内核会唤醒对应的内核线程。在内核线程里,会调用 thread_fn 函数。
可以提供 handler 而不提供 thread_fn,就退化为一般的 request_irq 函数。
可以不提供 handler 只提供 thread_fn,完全由内核线程来处理中断。
也可以既提供 handler 也提供 thread_fn,这就是中断上半部、下半部。

里面还有一个名为 sedondary 的 irqaction 结构体,它的作用以后再分析。
在 reqeust_irq 时可以传入 dev_id,为何需要 dev_id?作用有 2:
① 中断处理函数执行时,可以使用 dev_id
② 卸载中断时要传入 dev_id,这样才能在 action 链表中根据 dev_id 找到对应项所以在共享中断中必须提供 dev_id,非共享中断可以不提供。

3、irq_data 结构体

irq_data 结构体在 include/linux/irq.h 中定义,主要内容如下图:
在这里插入图片描述
它就是个中转站,里面有 irq_chip 指针 irq_domain 指针,都是指向别的结构体。
比较有意思的是 irq、 hwirq, irq 是软件中断号(并非上面所讲的软中断), hwirq 是硬件中断号。比如上面我们举的例子,在 GPIO中断 B 是软件中断号,可以找到 irq_desc[B]这个数组项; GPIO 里的第 x 号中断,这就是 hwirq。
谁来建立 irq、 hwirq 之间的联系呢?由 irq_domain 来建立。 irq_domain 会把本地的 hwirq 映射为全局的 irq ,什么意思?比如 GPIO 控制器里有第 1 号中断, UART 模块里也有第 1 号中断,这两个“第 1 号中断”是不一样的,它们属于不同的“域”──irq_domain。

4、irq_domain 结构体

irq_domain 结构体在 include/linux/irqdomain.h 中定义,主要内容如下图:
在这里插入图片描述
当我们后面从设备树讲起,如何在设备树中指定中断,设备树的中断如何被转换为 irq 时, irq_domain将会起到极大的作为。
这里基于入门的解度简单讲讲,在设备树中你会看到这样的属性:

interrupt-parent = <&gpio1>;
interrupts = <5 IRQ_TYPE_EDGE_RISING>;

它表示要使用 gpio1 里的第 5 号中断, hwirq 就是 5。
但是我们在驱动中会使用 request_irq(irq, handler)这样的函数来注册中断, irq 是什么?它是软件中断号,它应该从“ gpio1 的第 5 号中断”转换得来。
谁把 hwirq 转换为 irq?由 gpio1 的相关数据结构,就是 gpio1 对应的 irq_domain 结构体。
irq_domain 结构体中有一个 irq_domain_ops 结构体,里面有各种操作函数,主要是:
① xlate
用来解析设备树的中断属性,提取出 hwirq、 type 等信息。
② map
把 hwirq 转换为 irq

5、irq_chip 结构体

irq_chip 结构体在 include/linux/irq.h 中定义,主要内容如下图:
在这里插入图片描述
这个结构体跟“ chip”即芯片相关,里面各成员的作用在头文件中也列得很清楚,摘录部分如下:

* @irq_startup: start up the interrupt (defaults to ->enable if NULL)
* @irq_shutdown: shut down the interrupt (defaults to ->disable if NULL)
* @irq_enable: enable the interrupt (defaults to chip->unmask if NULL)
* @irq_disable: disable the interrupt
* @irq_ack: start of a new interrupt
* @irq_mask: mask an interrupt source
* @irq_mask_ack: ack and mask an interrupt source
* @irq_unmask: unmask an interrupt source
* @irq_eoi: end of interrupt

我们在 request_irq 后,并不需要手工去使能中断,原因就是系统调用对应的 irq_chip 里的函数帮我们使能了中断
我们提供的中断处理函数中,也不需要执行主芯片相关的清中断操作,也是系统帮我们调用 irq_chip中的相关函数。
但是对于外部设备相关的清中断操作,还是需要我们自己做的。
就像上面图里的“外部设备 1“、“外部设备 n”,外设备千变万化,内核里可没有对应的清除中断操作。

在设备树中指定中断_在代码中获得中断

1、设备树里中断节点的语法

参考文档:
内核 Documentation\devicetree\bindings\interrupt-controller\interrupts.txt

设备树里的中断控制器

中断的硬件框图如下:
在这里插入图片描述
在硬件上,“中断控制器” 只有 GIC 这一个,但是我们在软件上也可以把上图中的“ GPIO”称为“中断控制器”。很多芯片有多个 GPIO 模块,比如 GPIO1、 GPIO2 等等。所以软件上的“中断控制器”就有很多个:GIC、 GPIO1、 GPIO2 等等。

GPIO1 连接到 GIC, GPIO2 连接到 GIC,所以 GPIO1 的父亲是 GIC, GPIO2 的父亲是 GIC。
假设 GPIO1 有 32 个中断源(一组gpio中有32个引脚),但是它把其中的 16 个汇聚起来向 GIC 发出一个中断(16个gpio引脚共享一个软件中断号,可以查看imx6ull参考手册 ),把另外 16 个汇聚起来向 GIC 发出另一个中断。这就意味着 GPIO1 会用到 GIC 的两个中断,会涉及 GIC 里的 2 个 hwirq。

这些层级关系、中断号(hwirq),都会在设备树中有所体现。

在设备树中,中断控制器节点中必须有一个属性: interrupt-controller,表明它是“中断控制器”。
还必须有一个属性: #interrupt-cells,表明引用这个中断控制器的话需要多少个 cell。
#interrupt-cells 的值一般有如下取值:
① #interrupt-cells=<1>
别的节点要使用这个中断控制器时,只需要一个 cell 来表明使用“哪一个中断”。
② #interrupt-cells=<2>
别的节点要使用这个中断控制器时,需要一个 cell 来表明使用“哪一个中断”;
还需要另一个 cell 来描述中断,一般是表明触发类型:

2 个 cell 的 bits[3:0] 用来表示中断触发类型(trigger type and level flags)1 = low-to-high edge triggered,上升沿触发
2 = high-to-low edge triggered,下降沿触发
4 = active high level-sensitive,高电平触发
8 = active low level-sensitive,低电平触发

示例如下:

vic: intc@10140000 {
	compatible = "arm,versatile-vic";
	interrupt-controller;
	#interrupt-cells = <1>;
	reg = <0x10140000 0x1000>;
};

如果中断控制器有级联关系,下级的中断控制器还需要表明它的“ interrupt-parent”是谁(使用了哪个一中断控制器),用了interrupt-parent”中的哪一个“ interrupts”,请看下一小节。

2、设备树里使用中断

一个外设,它的中断信号接到哪个“中断控制器”的哪个“中断引脚”,这个中断的触发方式是怎样的?
这 3 个问题,在设备树里使用中断时,都要有所体现。

① interrupt-parent=<&XXXX>
你要用哪一个中断控制器里的中断?
② interrupts
你要用哪一个中断?
Interrupts 里要用几个 cell,由 interrupt-parent 对应的中断控制器决定。在中断控制器里有“ #interrupt-cells”属性,它指明了要用几个 cell 来描述中断。

比如:

i2c@7000c000 {
	gpioext: gpio-adnp@41 {
		compatible = "ad,gpio-adnp";		//保证驱动程序与之匹配
		interrupt-parent = <&gpio>;			//说明它所使用的中断属于该中断控制器
		interrupts = <160 1>;				//表明使用了该中断控制器中的160 号中断,触发类型是上升沿触发
		
		gpio-controller;					//表明它是一个中断控制器
		#gpio-cells = <1>;					//如果有要使用它的中断,需要用一个 cell来描述所使用的中断
		interrupt-controller;
		#interrupt-cells = <2>;
	};
	......
};

③ 新写法: interrupts-extended
一个“ interrupts-extended”属性就可以既指定“ interrupt-parent”,也指定“ interrupts”,比如:

interrupts-extended = <&intc1 5 1>, <&intc2 1 0>;		//表明了所属中断控制器的同时,亦表明描述了所使用的中断

3、设备树里中断节点的示例

以 100ASK_IMX6ULL 开发板为例,在 arch/arm/boot/dts 目录下可以看到 2 个文件: imx6ull.dtsi、100ask_imx6ull-14x14.dts,把里面有关中断的部分内容抽取出来。
设备树节点中需要一个 compatible 属性来标注,让驱动程序能找到对应的节点。
在这里插入图片描述
观察图中所见:

  1. intc 节点描述了顶级中断控制器 GIC,#interrupt-cells = <3> 表明 使用GIC 中断控制器需要用 3个cells 来描述一个中断。
    为什么是三个?
    中断类型:第一个cells 用来表明中断类型。在一个SOC 中允许有多个核,在这种情况下,就会有多种中断类型:SPI(Shared Peripheral Interrupt 共享外设中断)中断信号可以发到多个核、PPI(Private Peripheral Interrupt 私有外设中断)外设中断信号只允许发到指定内核、SGI (Software Generated Interrupt 软件通用中断) 用于多个核之间互发信号。
    其它两个 cells 分别表明 是哪一号中断(软件中断号,参考手册可查看)和 中断触发类型。

  2. gpc 描述了它使用GIC 的中断。

  3. 观察gpio1 节点可以发现它并没有标注自己的父节点时谁,此时它会继承父节点的一些属性,比如“ interrupt-parent”:
    在SOC 节点中 写到 “interrupt-parent = <&gpc>” ,所以它的父亲是gpc,于是中断结构图有了如下的变化。

  4. 以上都是BSP 工程师写好的,只需要了解,我们使用的例如 spidev 节点。

从以上设备树反推 IMX6ULL 的中断体系,如下,比之前的框图多了一个“ GPC INTC”:
在这里插入图片描述
GPC INTC 的英文是: General Power Controller, Interrupt Controller。它提供中断屏蔽、中断状态查询功能,实际上这些功能在 GIC 里也实现了,个人觉得有点多余。除此之外,它还提供唤醒功能 ,这才是保留它的原因。

在代码中获得中断

对于 platform_device
对于注册在platform 总线下的驱动,可以使用platform_get_resource 来获取:
一 个 节 点 能 被 转 换 为 platform_device , 如 果它 的 设 备 树 里 指 定 了中 断 属 性 , 那 么 可 以 从platform_device 中获得“中断资源”,函数如下,可以使用下列函数获得 IORESOURCE_IRQ 资源,即中断号:

/**
* platform_get_resource - get a resource for a device
* @dev: platform device
* @type: resource type // 取哪类资源? IORESOURCE_MEM、 IORESOURCE_REG
* // IORESOURCE_IRQ 等
* @num: resource index // 这类资源中的哪一个?
*/
struct resource *platform_get_resource(struct platform_device *dev,unsigned int type, unsigned int num);

对于 I2C 设备、 SPI 设备
对于 I2C 设备节点, I2C 总线驱动在处理设备树里的 I2C 子节点时,也会处理其中的中断信息。一个I2C 设备会被转换为一个 i2c_client 结构体,中断号会保存在 i2c_client 的 irq 成员里,代码如下(drivers/i2c/i2c-core.c):
在这里插入图片描述
对于 SPI 设备节点, SPI 总线驱动在处理设备树里的 SPI 子节点时,也会处理其中的中断信息。一个SPI 设备会被转换为一个 spi_device 结构体 ,中断号会保存在 spi_device 的 irq 成员里,代码如下(drivers/spi/spi.c):
在这里插入图片描述

调用 of_irq_get 获得中断号
如果你的设备节点既不能转换为 platform_device,它也不是 I2C 设备,不是 SPI 设备,那么在驱动程序中可以自行调用 of_irq_get 函数去解析设备树,得到中断号。

对于 GPIO
参考: drivers/input/keyboard/gpio_keys.c
可以使用 gpio_to_irq 或 gpiod_to_irq 获得中断号。
举例,假设在设备树中有如下节点:

gpio-keys {
	compatible = "gpio-keys";
	pinctrl-names = "default";
	user {
		label = "User Button";
		gpios = <&gpio5 1 GPIO_ACTIVE_HIGH>;
		gpio-key,wakeup;
		linux,code = <KEY_1>;
	};
};

那么可以使用下面的函数获得引脚和 flag:
button->gpio = of_get_gpio_flags(property_name, 0, &flags); bdata->gpiod = gpio_to_desc(button->gpio);
再去使用 gpiod_to_irq 获得中断号:
irq = gpiod_to_irq(bdata->gpiod);

ARM架构异常和中断
m0_49476241的博客
04-13 1249
本篇文章将带大家学习ARM架构中的异常和中断中断是大家都比较熟悉的,异常大家可能不是那么熟悉,那么这篇文章将会以ARM架构出发给大家介绍这些内容。本篇文章主要讲解了ARM架构异常和中断的基础概念,下篇文章我们将开始写代码。
【嵌入式环境下linux内核及驱动学习笔记-(11-设备树)】
weixin_45499326的博客
05-13 3308
1、ranges属性值的格式 , 表示将local地址向parent地址的转换。比如对于#address-cells和#size-cells都为1的话,以为例,表示将local的从0x0—(0x0 + 0x20)的地址空间映射到parent的0x10—(0x10 + 0x20)
arm中断及其处理
浩ZI
07-02 2040
1,关于硬件部分的介绍。 1.1ARM920T的中断 两种中断模式:FIQ,IRQ。 1.2几个寄存器 SRCPND:请求中断中断源。可以有多个位被置为1,可读可写,用完清0。只对irq模式有效。INTPND:当前正在执行的中断服务程序。只有一个为被置为1,可读可写,用完清0只对irq模式有效。INTMOD:某一位置1,则该位的中断源被设置为FIQ模式,否则为IRQ模式。INTMSK:某
在设备树中指定中断_在代码中获得中断
最新发布
d0203的博客
08-10 602
对于SPI设备节点, SPI总线驱动在处理设备树里的 SPI 子节点时,也会处理其中的中断信息。假设GPIO1有32 个中断源,但是它把其中的16个汇聚起来向GIC发出一个中断,把另外16个汇聚起来向GIC发出另一个中断。如果中断控制器有级联关系,下级的中断控制器还需要表明它的“interrupt-parent”是谁,用了interrupt-parent ” 中的哪一个“ interrupts”。一个外设,它的中断信号接到哪个“中断控制器”的哪个“中断引脚”,这个中断的触发方式是怎样的?
arm中断体系结构
weixin_30706507的博客
12-31 101
ARM处理器中有7种类型的异常,按优先级从高到低的排列如下: 复位异常(Reset)、 数据异常(Data Abort)、 快速中断异常(FIQ)、 外部中断异常(IRQ)、 预取异常(Prefetch Abort)、 软件中断(SWI)、 未定义指令异常(Undefined instruction)。 ...
ARM中断体系结构(以S3C2440为例)
weixin_42750185的博客
02-26 576
本文参考韦东山嵌入式视频 一、ARM体系CPU工作模式 1、用户模式(usr) 2、快速中断模式(fiq) 3、中断模式(irq) 4、管理模式(svc) 5、数据访问终止模式(abt) 6、系统模式(sys) 7、未定义指令中止模式(und) ①每种工作模式有不同的寄存器:一共有37个32位寄存器(以ARM920T CPU为例,在ARM状态下(ARM体系的CPU有两种工作状态)), ②有不同的权限 :配合MMU使用 ③有不同的出发条件:例如上电后处于管理模式 发生中断进入irq模式等 二、异常与中断 1
ARM——中断体系结构
weixin_38251305的博客
02-11 284
一,RAM异常及中断基本概念 1,什么是异常,什么是中断中断<——>异常 2,ARM异常种类及对应的处理器模式 1)复位异常 2)未定义指令异常 3)软中断异常 4)预取异常 5)数据异常 6)IRQ:外部中断异常 7)FIQ:快速中断异常 3,异常发生时cpu处理步骤 4,sample_异常发生时伪指令 5,ARM异常优先级 6,异常中断总结表 ...
ARM中断
fay8048的博客
10-04 5501
S3C2440系统中断 分类: ARM体系结构 2011-06-20 14:17 196人阅读评论(0) 收藏举报 1.1   S3C2440系统中断 CPU和外设构成了计算机系统,CPU和外设之间通过总线进行连接,用于数据通信和控制,CPU管理监视计算机系统中所有硬件
ARM GIC中断架构描述
10-08
ARM GIC(Generic Interrupt Controller)是ARM公司设计的一种中断控制器架构,用于管理并处理系统中的中断事件。这个中断控制器在嵌入式系统和基于ARM处理器的设备中扮演着至关重要的角色,因为它允许高效的中断...
ARM对异常中断的响应过程
07-19
ARM处理器是一种广泛使用的微处理器架构,它的异常中断响应机制是其重要组成部分,关系到程序运行的稳定性和可靠性。当ARM处理器遇到异常中断时,它会遵循一定的处理流程来保证系统能够妥善地处理异常,然后继续执行...
最通俗易懂的方式讲解ARM中断原理以及中断嵌套
07-13
通过了解中断信号如何被处理器识别、处理,以及中断控制器如何管理多个中断源,读者可以更好地理解ARM架构下的中断机制,并为开发基于ARM的系统提供指导。此外,文章还简要提到了Linux操作系统中的中断处理方式,这...
ARM Linux 中断分析
08-14
ARM架构中,中断被归类为“异常”,包括复位、中断、快速中断等。当异常发生时,CPU会跳转到对应的地址执行处理程序。 中断服务程序的注册通常通过`request_irq()`函数完成。该函数允许驱动程序将自己的中断处理...
ARM---中断(一)
Alfred的博客
01-01 3694
今天来看一下中断ARM体系中对中断的处理,直接进入正题。 中断是指计算机运行过程中,出现某些意外情况需主机干预时,机器能自动停止正在运行的程序并转入处理新情况的程序,处理完毕后又返回原被暂停的程序继续运行。 中断的几个主要目的。第一,通过中断可以提高CPU效率。假设一种场景,CPU通知其它设备完成某项工作,当设备完成任务后,CPU如何知道呢?一种方式是设备标记状态寄存器,等待CPU来查询,这种做法弊端是程序员在编写程序时不能确切知道设备完成任务所需的时间,也就是说需要定时去查询设备的状态。我们知道,现代C
ARM 中断总结
yuwenliang的专栏
12-29 793
 几天前一个学生问我ARM中断嵌套的问题,我才发现原在我心中理所当然的事对学生来说理解实属不易。     ARM有七种模式,我们这里只讨论SVC、IRQ和FIQ模式。     我们可以假设ARM核心有两根中断引脚(实际上是看不见的),一根叫 irq pin, 一根叫fiq pin.     在ARM的cpsr中,有一个I位和一个F位,分别用来禁止IRQ和FIQ的。    
《嵌入式 - ARM》第4章 ARM中断
不问归期的博客
01-31 1481
4.1 SWI中断处理 前面我们学习ARM工作模式中,处理器模式切换可以通过软件控制进行切换,即修改CPSR模式位,但这是在特权模式下,当我们处于用户模式下,是没有权限实现模式转换的。若想实现模式切换,只能由另一种方法来实现,即通过外部中断或是异常处理过程进行切换。于是ARM指令集中提供了两条产生异常的指令,通过这两条指令可以用软件的方法实现异常,其中一个就是中断指令SWI 。 4.1.1
ARM---中断(二)
Alfred的博客
01-01 916
开始之前,先声明一下,ARM系列的全部文章都参考的是公开文档。如果大家有需要,可以去ARM的官方网站下载。我手里没有任何非公开的文档。 闲话少说,今天开始扒GIC-600。 为了适应大规模的SoC设计,GIC-600被设计成分布式IP。所谓分布式,GIC-600由几种组件构成,每个组件可以跟其它相关模块在物理设计上摆放在一起,并与其拥有共同的电源域;组件之间通过片上网络(network on chip,简称NoC)通信,从而达到更好的时序。 GIC-600的重要组件包括以下几种: Distributor:G
arm(v7)架构 --- 中断
weixin_43604927的博客
12-10 2115
中断 arm
ARM中断异常
weixin_43327540的博客
04-05 605
基础简介 中断工作模式: 各模式下对应的寄存器: 特别说明: R13 别称SP,用于指定栈指针。 R14 别称LR,当使用bl指令进行跳转时,在跳转后的处理函数执行 b lr,即可返回执行bl指令的位置。 R15 别称PC,指向当前执行的指令。 CPSR:当前程序状态寄存器。用于判断当前中断模式、CPU状态等信息,如下(2440手册)。 SPSR:程序状态保存寄存器。当程序进入异...
ARM架构中断机制详解(S5PV210芯片)
weixin_42031299的博客
10-04 3274
(1)中断是指计算机运行过程中,出现某些意外情况需主机干预时,机器能自动停止正在运行的程序并转入处理新情况的程序,处理完毕后又返回原被暂停的程序继续运行;(2)中断是为了实现宏观上的并发。比如我们有一台单核CPU的电脑,我们可以在看视频的同时去操作键盘和鼠标,但是单核CPU在同一时刻是只能做一件事情。
arm架构中断调用代码
06-01
ARM架构中,中断调用的代码通常由以下几部分组成: 1. 中断向量表:存储着处理器所有可能的中断类型对应的处理函数地址。当中断发生时,处理器会根据中断类型在中断向量表中查找相应的处理函数地址。 2. 中断...
写文章

热门文章

  • shell脚本 100232
  • 什么是ARM? 61297
  • git工具 11306
  • pcie 总线 9551
  • tcp/ip的模型与原理 9172

分类专栏

  • pcie 总线 1篇
  • Linux驱动 13篇
  • Linux内核 2篇
  • Linux 应用编程 1篇
  • 笔记 14篇
  • 通信协议 1篇
  • uboot 移植 1篇
  • 网络 2篇
  • 树莓派开发
  • openwrt 1篇
  • linux 命令行
  • Linux kernel移植
  • Linux 学习 3篇
  • ARM 2篇
  • c语言 1篇
  • Linux应用层 2篇
  • Linux移植 1篇
  • ARM汇编 1篇
  • ARM架构的了解 1篇

最新评论

  • 编译openwrt

    windy_20150214: 这文章质量太高了,博主厉害

  • shell脚本

    流火极光: 写的特别好,感谢

  • imx6ull 以太网

    windy_20150214: 博主,我记得之前有一篇讲解ls1046的博客,现在怎么看不到了

  • pcie 总线

    微风的日子: 写的太好啦,感谢大佬

  • 交叉编译

    CSDN-Ada助手: 多亏了你这篇博客, 解决了问题: https://ask.csdn.net/questions/8056878, 请多输出高质量博客, 帮助更多的人

最新文章

  • spi 子系统
  • i2c子系统
  • Linux 驱动基础
2023年6篇
2022年10篇
2021年18篇
2020年24篇

目录

目录

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值

玻璃钢生产厂家河北玻璃钢雕塑图片美陈玻璃钢动物雕塑代理价格大武口玻璃钢雕塑厂家焦作商场美陈植物墙奉贤区拉丝玻璃钢雕塑价格西宁仿真玻璃钢雕塑制作浅蓝色玻璃钢花盆中山玻璃钢卡通雕塑游乐场道具太原玻璃钢农耕雕塑银川现代人物玻璃钢雕塑古代玻璃钢卡通雕塑供应商家喀什玻璃钢花盆玻璃钢奶牛雕塑玻璃钢装饰雕塑惠州玻璃钢公仔雕塑定制山西景区玻璃钢雕塑生产厂家玻璃钢雕塑定制吉祥物摆件大型卡佛山玻璃钢雕塑工艺品龙岩玻璃钢广场雕塑厂家温州玻璃钢雕塑厂厦门佛山玻璃钢卡通雕塑咸阳火烈鸟玻璃钢雕塑厂家赣州玻璃钢果蔬雕塑广西酒店玻璃钢雕塑商场美陈装饰设计制作公司泰州仿石材玻璃钢花盆柘城玻璃钢雕塑价格江苏玻璃钢仿铜雕塑价格龙岩模压法玻璃钢雕塑玻璃钢花盆超低价香港通过《维护国家安全条例》两大学生合买彩票中奖一人不认账让美丽中国“从细节出发”19岁小伙救下5人后溺亡 多方发声单亲妈妈陷入热恋 14岁儿子报警汪小菲曝离婚始末遭遇山火的松茸之乡雅江山火三名扑火人员牺牲系谣言何赛飞追着代拍打萧美琴窜访捷克 外交部回应卫健委通报少年有偿捐血浆16次猝死手机成瘾是影响睡眠质量重要因素高校汽车撞人致3死16伤 司机系学生315晚会后胖东来又人满为患了小米汽车超级工厂正式揭幕中国拥有亿元资产的家庭达13.3万户周杰伦一审败诉网易男孩8年未见母亲被告知被遗忘许家印被限制高消费饲养员用铁锨驱打大熊猫被辞退男子被猫抓伤后确诊“猫抓病”特朗普无法缴纳4.54亿美元罚金倪萍分享减重40斤方法联合利华开始重组张家界的山上“长”满了韩国人?张立群任西安交通大学校长杨倩无缘巴黎奥运“重生之我在北大当嫡校长”黑马情侣提车了专访95后高颜值猪保姆考生莫言也上北大硕士复试名单了网友洛杉矶偶遇贾玲专家建议不必谈骨泥色变沉迷短剧的人就像掉进了杀猪盘奥巴马现身唐宁街 黑色着装引猜测七年后宇文玥被薅头发捞上岸事业单位女子向同事水杯投不明物质凯特王妃现身!外出购物视频曝光河南驻马店通报西平中学跳楼事件王树国卸任西安交大校长 师生送别恒大被罚41.75亿到底怎么缴男子被流浪猫绊倒 投喂者赔24万房客欠租失踪 房东直发愁西双版纳热带植物园回应蜉蝣大爆发钱人豪晒法院裁定实锤抄袭外国人感慨凌晨的中国很安全胖东来员工每周单休无小长假白宫:哈马斯三号人物被杀测试车高速逃费 小米:已补缴老人退休金被冒领16年 金额超20万

玻璃钢生产厂家 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化