linux IRQ Management(三)- IRQ Framework_no interrupt controller found-程序员宅基地

技术标签: 中断子系统  

  • 了解irq management framework

1.Introduction

  Linux is a system on which devices notify the kernel about particular events by means of IRQs.The CPU exposes IRQ lines, shared or not, and used by connected devices, so that when a device needs the CPU it sends a request to the CPU. When the CPU gets this request it stops its actual job and saves its context, in order to serve the request issued by the device. After serving the device, its state is restored back to exactly where it stopped when the interruption occurred. There are so many IRQ lines, that another device is responsible for them to the CPU.

  Not only can devices raise interrupts, some processor operations can do that too. There are two different kinds of interrupts:

  • Synchronous interrupts called exceptions, produced by the CPU while processing instructions. These are non-maskable interrupts (NMI), and result from a critical malfunction such as hardware failure. They are always processed by the CPU.
  • Asynchronous interrupts called interrupts, are issued by other hardware devices. These are normal and maskable interrupts.

1.1.Exceptions

  It is as described are generated by the CPU when an ‘error’ occurs. Some exceptions are not really errors in most cases, such as page faults. Exceptions are a type of interrupt.

Exceptions are classified as:

  • Processor-detected exceptions: Those the CPU generates in response to an anomalous condition, and it is divided into three groups:

    • Faults: These can be corrected and the program may continue as if nothing happened.
    • Traps: Traps are reported immediately after the execution of the trapping instruction.
    • Aborts: Some severe unrecoverable error.
  • Programmed exception: These are requested by the programmer, handled like a trap.

1.2.Interrupts

  When an interrupt originates from an external device, it is referred to as a hardware interrupt. These signals are generated by external hardware to seek the attention of the processor on occurrence of a significant external event, for instance a key hit on the keyboard, a click on a mouse button, or moving the mouse trigger hardware interrupts through which the processor is notified about the availability of data to be read. Hardware interrupts occur asynchronously with respect to the processor clock (meaning they can occur at random times), and hence are also termed as asynchronous interrupts.

2.中断硬件描述

  一个完整的设备中,与中断相关的硬件可以划分为3类,它们分别是:设备、中断控制器和CPU本身,SMP系统中断硬件的组成结构:
在这里插入图片描述

  • 设备:设备是发起中断的源,当设备需要请求某种服务的时候,它会发起一个硬件中断信号,通常,该信号会连接至中断控制器,由中断控制器做进一步的处理。在现代的移动设备中,发起中断的设备可以位于soc(system-on-chip)芯片的外部,也可以位于soc的内部,因为目前大多数soc都集成了大量的硬件IP,例如I2C、SPI、Display Controller等等。

  • 中断控制器:中断控制器负责收集所有中断源发起的中断,现有的中断控制器几乎都是可编程的,通过对中断控制器的编程,我们可以控制每个中断源的优先级、中断的电器类型,还可以打开和关闭某一个中断源,在smp系统中,甚至可以控制某个中断源发往哪一个CPU进行处理。对于ARM架构的soc,使用较多的中断控制器是VIC(Vector Interrupt Controller),进入多核时代以后,GIC(General Interrupt Controller)的应用也开始逐渐变多。

  • CPU:CPU是最终响应中断的部件,它通过对可编程中断控制器的编程操作,控制和管理者系统中的每个中断,当中断控制器最终判定一个中断可以被处理时,它会根据事先的设定,通知其中一个或者是某几个cpu对该中断进行处理,虽然中断控制器可以同时通知数个cpu对某一个中断进行处理,实际上,最后只会有一个cpu相应这个中断请求,但具体是哪个cpu进行响应是可能是随机的,中断控制器在硬件上对这一特性进行了保证,不过这也依赖于操作系统对中断系统的软件实现。在smp系统中,cpu之间也通过IPI(inter processor interrupt)中断进行通信。

  中断硬件系统主要有三种器件:各个外设、中断控制器和CPU。各个外设提供irq request line,在发生中断事件时,通过irq request line上的电气信号向CPU系统请求处理。由于外设的irq request line太多,CPU需要一个Interrupt controller帮忙。Interrupt Controller是连接外设中断系统和CPU系统的桥梁。根据外设irq request line的多少,Interrupt Controller可以级联。CPU的主要功能是运算,因此CPU并不处理中断优先级,那是Interrupt controller的事情。

  对于CPU而言,一般有两种中断请求:

  • X86,有可屏蔽中断和不可屏蔽中断。

  • ARM,是IRQ和FIQ,分别进入IRQ mode和FIQ mode。
    在这里插入图片描述
      系统中有若干个CPU block用来接收中断事件并进行处理,若干个Interrupt controller形成树状的结构,汇集系统中所有外设的irq request line,并将中断事件分发给某一个CPU block进行处理。

  • 中断接口:

    • 一个硬件的信号线,通过电平信号传递中断事件(ARM以及GIC组成的中断系统)。
    • x86+APIC(Advanced Programmable Interrupt Controller)组成的系统,每个x86的核有一个Local APIC,这些Local APIC们通过ICC(Interrupt Controller Communication)bus连接到IO APIC上。IO APIC收集各个外设的中断,并翻译成总线上的message,传递给某个CPU上的Local APIC。因此,上面的红色线条也是逻辑层面的中断信号,可能是实际的PCB上的铜线(或者SOC内部的铜线),也可能是一个message而已。
  • 控制接口:
    CPU和Interrupt Controller之间还需要有控制信息的交流。Interrupt Controller会开放一些寄存器让CPU访问、控制。

3.通用中断子系统软件抽象

  在通用中断子系统(generic irq)出现之前,内核使用__do_IRQ处理所有的中断,这意味着__do_IRQ中要处理各种类型的中断,这会导致软件的复杂性增加,层次不分明,而且代码的可重用性也不好。到了内核版本2.6.38,__do_IRQ这种方式已经彻底在内核的代码中消失了。通用中断子系统的原型最初出现于ARM体系中,一开始内核的开发者们把3种中断类型区分出来,它们是:

  • 电平触发中断(level type)
  • 边缘触发中断(edge type)
  • 简易的中断(simple type)

  后来又针对某些需要回应eoi(end of interrupt)的中断控制器,加入了fast eoi type,针对smp加入了per cpu type。把这些不同的中断类型抽象出来后,成为了中断子系统的流控层。要使所有的体系架构都可以重用这部分的代码,中断控制器也被进一步地封装起来,形成了中断子系统中的硬件封装层。通用中断子系统的层次结构:
在这里插入图片描述
3.1.硬件封装层

  它包含了体系架构相关的所有代码,包括中断控制器的抽象封装,arch相关的中断初始化,以及各个IRQ的相关数据结构的初始化工作,cpu的中断入口也会在arch相关的代码中实现。中断通用逻辑层通过标准的封装接口(实际上就是struct irq_chip定义的接口)访问并控制中断控制器的行为,体系相关的中断入口函数在获取IRQ编号后,通过中断通用逻辑层提供的标准函数,把中断调用传递到中断流控层中。irq_chip的部分定义:

struct irq_chip {
	const char	*name;
	unsigned int	(*irq_startup)(struct irq_data *data);
	void		(*irq_shutdown)(struct irq_data *data);
	void		(*irq_enable)(struct irq_data *data);
	void		(*irq_disable)(struct irq_data *data);
 
	void		(*irq_ack)(struct irq_data *data);
	void		(*irq_mask)(struct irq_data *data);
	void		(*irq_mask_ack)(struct irq_data *data);
	void		(*irq_unmask)(struct irq_data *data);
	void		(*irq_eoi)(struct irq_data *data);
 
	int		(*irq_set_affinity)(struct irq_data *data, const struct cpumask *dest, bool force);
	int		(*irq_retrigger)(struct irq_data *data);
	int		(*irq_set_type)(struct irq_data *data, unsigned int flow_type);
	int		(*irq_set_wake)(struct irq_data *data, unsigned int on);
        ......
};

3.2.中断流控层

  中断流控是指合理并正确地处理连续发生的中断,比如一个中断在处理中,同一个中断再次到达时如何处理,何时应该屏蔽中断,何时打开中断,何时回应中断控制器等一系列的操作。该层实现了与体系和硬件无关的中断流控处理操作,它针对不同的中断电气类型(level,edge…),实现了对应的标准中断流控处理函数,在这些处理函数中,最终会把中断控制权传递到驱动程序注册中断时传入的处理函数或者是中断线程中。目前内核提供了以下几个主要的中断流控函数的实现(只列出部分):

  • handle_simple_irq();
  • handle_level_irq(); 电平中断流控处理程序
  • handle_edge_irq(); 边沿触发中断流控处理程序
  • handle_fasteoi_irq(); 需要eoi的中断处理器使用的中断流控处理程序
  • handle_percpu_irq(); 该irq只有单个cpu响应时使用的流控处理程序

3.3.中断通用逻辑层

  该层实现了对中断系统几个重要数据的管理,并提供了一系列的辅助管理函数。同时,该层还实现了中断线程的实现和管理,共享中断和嵌套中断的实现和管理,另外它还提供了一些接口函数,它们将作为硬件封装层和中断流控层以及驱动程序API层之间的桥梁,例如以下API:

  • generic_handle_irq();
  • irq_to_desc();
  • irq_set_chip();
  • irq_set_chained_handler();

3.4.驱动程序API

  该部分向驱动程序提供了一系列的API,用于向系统申请/释放中断,打开/关闭中断,设置中断类型和中断唤醒系统的特性等操作。驱动程序的开发者通常只会使用到这一层提供的这些API即可完成驱动程序的开发工作,其他的细节都由另外几个软件层较好地“隐藏”起来了,驱动程序开发者无需再关注底层的实现,其中的一些API如下:

  • enable_irq();
  • disable_irq();
  • disable_irq_nosync();
  • request_threaded_irq();
  • irq_set_affinity();

4.Interrupt controllers VS CPUs 拓扑结构

  Interrupt controller 有的是支持多个CPU core的(如GIC、APIC等),有的不支持(如S3C2410的中断控制器,X86平台的PIC等)。

  如果硬件平台中只有一个GIC的话,那么通过控制该GIC的寄存器可以将所有的外设中断,分发给连接在该interrupt controller上的CPU。

5.Interrupt controller分发中断

  一般而言,Interrupt controller可以把中断事件上报给一个CPU或者一组CPU(包括广播到所有的CPU上去)。对于外设类型的中断,当然是送到一个cpu上就OK了。如果送达了多个cpu,实际上,也应该只有一个handler实际和外设进行交互,另外一个cpu上的handler的动作应该是这样的:发现该irq number对应的中断已经被另外一个cpu处理了,直接退出handler,返回中断现场。

从用户的角度看,需求可能包括:

  • 让某个IRQ number的中断由某个特定的CPU处理
  • 让某个特定的中断由几个CPU轮流处理

6.中断子系统拓扑图
在这里插入图片描述

6.1.中断子系统相关的软件框架图
在这里插入图片描述
上面所示,中断子系统分成4个部分:

  • 硬件无关的代码,即Linux kernel通用中断处理模块。无论是哪种CPU,哪种controller,其中断处理的过程都有一些相同的内容,这些相同的内容被抽象出来,和HW无关。

  • CPU architecture相关的中断处理, 和系统使用的具体的CPU architecture相关。

  • Interrupt controller驱动代码 ,和系统使用的Interrupt controller相关。

  • 普通外设的驱动,这些驱动将使用Linux kernel通用中断处理模块的API来实现自己的驱动逻辑。

6.2.中断管理系统代码初始化

  start_kernel()函数调用trap_init()、early_irq_init()和init_IRQ()三个函数来初始化中断管理系统。

start_kernel():
asmlinkage void __init start_kernel(void)
{
   ……
   trap_init();
   ……
   early_irq_init();
   init_IRQ();
  ……

内核启动时初始化中断的入口:

6.2.1. early_irq_init:

  • 如果定义 CONFIG_SPARSE_IRQ,则所有irq_descs以radix tree的形式管理;
  • 否则所有irq_descs放在一个全局数组中,并对某些成员进行初始化。

第一种方式:使用基数树管理16个legacy中断

229 int __init early_irq_init(void)
  230 {
  231     int i, initcnt, node = first_online_node;
  232     struct irq_desc *desc;
  233
  234     init_irq_default_affinity();  
  235
  236     /* Let arch update nr_irqs and return the nr of preallocated irqs */
  237     initcnt = arch_probe_nr_irqs();      
  240
  241     if (WARN_ON(nr_irqs > IRQ_BITMAP_BITS))
  242         nr_irqs = IRQ_BITMAP_BITS;
  243
  244     if (WARN_ON(initcnt > IRQ_BITMAP_BITS))
  245         initcnt = IRQ_BITMAP_BITS;
  246
  247     if (initcnt > nr_irqs)
  248         nr_irqs = initcnt;
  249
  250     for (i = 0; i < initcnt; i++) {         //对以上的16个irq进行irq_desc的初始化
  251         desc = alloc_desc(i, node, NULL);   //分配irq_desc并对其中某些成员进行初始化
  252         set_bit(i, allocated_irqs);              //set bit in allocated_irqs
  253         irq_insert_desc(i, desc);              //插入到radix tree中
  254     }
  255     return arch_early_irq_init();     //设置以上16个legacy irq的chip_data,void类型
  256 }

重点函数:

  • alloc_desc
  • irq_insert_desc

第二种方式:使用全局数组

  260 struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned_in_smp = {
  261     [0 ... NR_IRQS-1] = {
  262         .handle_irq = handle_bad_irq,
  263         .depth      = 1,
  264         .lock       = __RAW_SPIN_LOCK_UNLOCKED(irq_desc->lock),
  265     }
  266 };

  268 int __init early_irq_init(void)
  269 {
  270     int count, i, node = first_online_node;
  271     struct irq_desc *desc;
  272
  273     init_irq_default_affinity();
  274
~ 275     printk(KERN_INFO "NR_IRQS:%d, adasda\n", NR_IRQS);
+ 276     13131
  277
  278     desc = irq_desc;
  279     count = ARRAY_SIZE(irq_desc);
  280
  281     for (i = 0; i < count; i++) { //遍历数组,对成员进行初始化
  282         desc[i].kstat_irqs = alloc_percpu(unsigned int);
  283         alloc_masks(&desc[i], GFP_KERNEL, node);
  284         raw_spin_lock_init(&desc[i].lock);
  285         lockdep_set_class(&desc[i].lock, &irq_desc_lock_class);
  286         desc_set_defaults(i, &desc[i], node, NULL);   //初始化irq_desc结构体
  287     }
  288     return arch_early_irq_init();
  289 }

重点函数:

  • desc_set_defaults

6.2.2.init_IRQ():

 arch/arm64/kernel/irq.c:
  72 void __init init_IRQ(void)
   73 {
   74     init_irq_stacks();
   75     irqchip_init();                                                                                    
   76     if (!handle_arch_irq)
   77         panic("No interrupt controller found.");
   78 }

init_irq_stacks(初始化中断栈):

  • 对于x86平台:
    x86平台上,中断栈是独立于内核栈的存在,两者并不共享,如果是多处理器架构,那么每个CPU都对应有一个中断栈。

  • 对于 ARM平台:
    中断栈和内核栈则是共享的,中断栈和内核栈共享有一个负面因素,如果中断发生嵌套,可能会造成栈溢出,从而可能会破坏到内核栈的一些重要数据。

  • 对于ARM64平台:
    中断栈的实现是独立的,并且区分两种情况,分别是vmap申请内存,还是直接静态定义,并且根据CPU个数,每个CPU对应单独的一个stack。

设备树中的中断控制器的处理入口:irqchip_init()

   22 static const struct of_device_id
   23 irqchip_of_match_end __used __section(__irqchip_of_table_end);
   24 
   25 extern struct of_device_id __irqchip_of_table[];
   26 
   27 void __init irqchip_init(void)                                                                         
   28 {
   29     of_irq_init(__irqchip_of_table);
   30     acpi_probe_device_table(irqchip);
   31 }

  分析irqchip_init之前,先看GICv3 DTS设备描述,如下所示:

gic: interrupt-controller@2c010000 {
                compatible = "arm,gic-v3";               
                #interrupt-cells = <4>;                   
                #address-cells = <2>;
                #size-cells = <2>;
                ranges;
                interrupt-controller;
                redistributor-stride = <0x0 0x40000>;   // 256kB stride
                #redistributor-regions = <2>;
                reg = <0x0 0x2c010000 0 0x10000>,       // GICD
                      <0x0 0x2d000000 0 0x800000>,      // GICR 1: CPUs 0-31
                      <0x0 0x2e000000 0 0x800000>;      // GICR 2: CPUs 32-63
                      <0x0 0x2c040000 0 0x2000>,        // GICC
                      <0x0 0x2c060000 0 0x2000>,        // GICH
                      <0x0 0x2c080000 0 0x2000>;        // GICV
                interrupts = <1 9 4>;

                gic-its@2c200000 {
                        compatible = "arm,gic-v3-its";
                        msi-controller;
                        #msi-cells = <1>;
                        reg = <0x0 0x2c200000 0 0x20000>;
                };

                gic-its@2c400000 {
                        compatible = "arm,gic-v3-its";
                        msi-controller;
                        #msi-cells = <1>;
                        reg = <0x0 0x2c400000 0 0x20000>;
                };
        };
  • compatible: 用于匹配GICv3驱动
  • #interrupt-cells: 这是一个中断控制器节点的属性。它声明了该中断控制器的中断指示符(-interrupts)中 cell 的个数
  • #address-cells , #size-cells, ranges:用于寻址, #address-cells表示reg中address元素的个数,#size-cells用来表示length元素的个数
  • interrupt-controller: 表示该节点是一个中断控制器
  • redistributor-stride: 一个GICR的大小
  • #redistributor-regions: GICR域个数。
  • reg :GIC的物理基地址,分别对应GICD,GICR,GICC…
  • interrupts: 分别代表中断类型,中断号,中断类型, PPI中断亲和, 保留字段。
    a为0表示SPI,1表示PPI;b表示中断号(注意SPI/PPI的中断号范围);c为1表示沿中断,4表示电平中断。
  • msi-controller: 表示节点是MSI控制器

  如上设备数的节点是由哪个驱动函数来解析的?带着疑问来看一下:

  irq chip driver声明:

IRQCHIP_DECLARE宏定义:
#define IRQCHIP_DECLARE(name, compat, fn) OF_DECLARE_2(irqchip, name, compat, fn)

#define OF_DECLARE_2(table, name, compat, fn) \ 
        _OF_DECLARE(table, name, compat, fn, of_init_fn_2)

#define _OF_DECLARE(table, name, compat, fn, fn_type)            \ 
    static const struct of_device_id __of_table_##name        \ 
        __used __section(__##table##_of_table)            \ 
         = { .compatible = compat,                \ 
             .data = (fn == (fn_type)NULL) ? fn : fn  }

  该宏初始化一个struct of_device_id的静态常量,并放置在__irqchip_of_table section中。gic-v3使用IRQCHIP_DECLARE定义如下:

 IRQCHIP_DECLARE(gic_v3, "arm,gic-v3", gic_of_init);   

  它定义了一个of_device_id结构体,段属性为__irqchip_of_table,在编译内核时这些段被放在__irqchip_of_table地址处。即__irqchip_of_table起始地址处,放置了一个或多个 of_device_id,它含有compatible成员;设备树中的设备节点含有compatible属性,如果双方的compatible相同, 并且设备节点含有interrupt-controller属性,则调用of_device_id中的函数gic_of_init来处理该设备节点。所以IRQCHIP_DECLARE是用来声明设备树中的中断控制器的处理函数。

  irq chip table保存kernel支持的所有的中断控制器的ID信息(最重要的是驱动代码初始化函数和DT compatible string)。struct of_device_id定义:

struct of_device_id 
{ 
    char    name[32];------要匹配的device node的名字 
    char    type[32];-------要匹配的device node的类型 
    char    compatible[128];---匹配字符串(DT compatible string),用来匹配适合的device node 
    const void *data;--------对于GIC,这里是初始化函数指针 
};

  下面以fsl为例进一步分析:

 //arch/arm/mach-imx/gpc.c 
 IRQCHIP_DECLARE(imx_gpc, "fsl,imx6q-gpc", imx_gpc_init); 

展开宏可得:

static const struct of_device_id __of_table_imx_gpc      __used __section(__irqchip_of_table) = 
{ .compatible = "fsl,imx6q-gpc",                             
  .data = imx_gpc_init  
}

  回到函数irqchip_init中调用了of_irq_init,该函数对设备树文件中每一个中断控制器节点,调用对应的处理函数;并将加入到irqchip_of_table中的IRQ控制器和设备树进行匹配后初始化对应的控制器。

Dtsi中断控制器定义如下:

intc: interrupt-controller@00a01000 {
               compatible = "arm,cortex-a9-gic";
               #interrupt-cells = <3>;
               interrupt-controller;
               reg = <0x00a01000 0x1000>,
                     <0x00a00100 0x100>;
               interrupt-parent = <&intc>;
       };
		
	gpio1: gpio@0209c000 {
                               compatible = "fsl,imx6q-gpio", "fsl,imx35-gpio";
                               reg = <0x0209c000 0x4000>;
                               interrupts = <0 66 IRQ_TYPE_LEVEL_HIGH>,
                                            <0 67 IRQ_TYPE_LEVEL_HIGH>;
                               gpio-controller;
                               #gpio-cells = <2>;
                               interrupt-controller;
                               #interrupt-cells = <2>;
                       };
                       
    //General Power Control
	gpc: gpc@020dc000 {
                               compatible = "fsl,imx6q-gpc";
                               reg = <0x020dc000 0x4000>;
                               interrupt-controller;
                               #interrupt-cells = <3>;
                               interrupts = <0 89 IRQ_TYPE_LEVEL_HIGH>,
                                            <0 90 IRQ_TYPE_LEVEL_HIGH>;
                               interrupt-parent = <&intc>;
                               pu-supply = <&reg_pu>;
                               clocks = <&clks IMX6QDL_CLK_GPU3D_CORE>,
                                        <&clks IMX6QDL_CLK_GPU3D_SHADER>,
                                        <&clks IMX6QDL_CLK_GPU2D_CORE>,
                                        <&clks IMX6QDL_CLK_GPU2D_AXI>,
                                        <&clks IMX6QDL_CLK_OPENVG_AXI>,
                                        <&clks IMX6QDL_CLK_VPU_AXI>;
                               #power-domain-cells = <1>;
                       };
				fec: ethernet@02188000 {
                               compatible = "fsl,imx6q-fec";
                               reg = <0x02188000 0x4000>;
                               interrupts-extended =
                                       <&gpc 0 118 IRQ_TYPE_LEVEL_HIGH>,
                                       <&gpc 0 119 IRQ_TYPE_LEVEL_HIGH>;
                               clocks = <&clks IMX6QDL_CLK_ENET>,
                                        <&clks IMX6QDL_CLK_ENET>,
                                        <&clks IMX6QDL_CLK_ENET_REF>;
                               clock-names = "ipg", "ahb", "ptp";
                               stop-mode = <&gpr 0x34 27>;
                               fsl,wakeup_irq = <0>;
                               status = "disabled";
                       };

设备树interrupt控制器:

  • gic “arm,cortex-a9-gic”
  • gpio”fsl,imx6q-gpio”, “fsl,imx35-gpio”
  • gpc”fsl,imx6q-gpc”;
//drivers/of/irq.c
void __init of_irq_init(const struct of_device_id *matches)
{
	struct device_node *np, *parent = NULL;
	struct intc_desc *desc, *temp_desc;
	struct list_head intc_desc_list, intc_parent_list;

	INIT_LIST_HEAD(&intc_desc_list);
	INIT_LIST_HEAD(&intc_parent_list);

	for_each_matching_node(np, matches) {
		if (!of_find_property(np, "interrupt-controller", NULL) ||
				!of_device_is_available(np))
			continue;
		desc = kzalloc(sizeof(*desc), GFP_KERNEL);
		desc->dev = np;
		desc->interrupt_parent = of_irq_find_parent(np);
		if (desc->interrupt_parent == np)
			desc->interrupt_parent = NULL;
		list_add_tail(&desc->list, &intc_desc_list);
	}

	while (!list_empty(&intc_desc_list)) {
		list_for_each_entry_safe(desc, temp_desc, &intc_desc_list, list) {
			const struct of_device_id *match;
			int ret;
			of_irq_init_cb_t irq_init_cb;

			if (desc->interrupt_parent != parent)
				continue;

			list_del(&desc->list);
			match = of_match_node(matches, desc->dev);
			irq_init_cb = (of_irq_init_cb_t)match->data;
			ret = irq_init_cb(desc->dev, desc->interrupt_parent);
			if (ret) {
				kfree(desc);
				continue;
			}
			list_add_tail(&desc->list, &intc_parent_list);
		}

		/* Get the next pending parent that might have children */
		desc = list_first_entry_or_null(&intc_parent_list,
						typeof(*desc), list);
		list_del(&desc->list);
		parent = desc->dev;
		kfree(desc);
	}

	list_for_each_entry_safe(desc, temp_desc, &intc_parent_list, list) {
		list_del(&desc->list);
		kfree(desc);
	}
}

  of_irq_init函数执行之前,系统已经完成了device tree的初始化,因此系统中的所有的设备节点都已经形成了一个树状结构,每个节点代表一个设备的device node。of_irq_init是在所有的device node中寻找中断控制器节点,形成树状结构(系统可以有多个interrupt controller,之所以形成中断控制器的树状结构,是为了让系统中所有的中断控制器驱动按照一定的顺序进行初始化)。之后,从root interrupt controller节点开始,对于每一个interrupt controller的device node,扫描irq chip table,进行匹配,一旦匹配到,就调用该interrupt controller的初始化函数,并把该中断控制器的device node以及parent中断控制器的device node作为参数传递给irq chip driver。

6.3. GIC driver初始化

gic_of_init():

 drivers/irqchip/irq-gic-v3.c:
  1273 static int __init gic_of_init(struct device_node *node, struct device_node *parent)
  1274 {
  1275     void __iomem *dist_base;
  1276     struct redist_region *rdist_regs;
  1277     u64 redist_stride;
  1278     u32 nr_redist_regions;
  1279     int err, i;
  1280 
  1281     dist_base = of_iomap(node, 0);
  1287     err = gic_validate_dist_version(dist_base);
  1292 
  1293     if (of_property_read_u32(node, "#redistributor-regions", &nr_redist_regions))
  1294         nr_redist_regions = 1;
  1295 
  1296     rdist_regs = kcalloc(nr_redist_regions, sizeof(*rdist_regs),
  1297                  GFP_KERNEL);
  1302 
  1303     for (i = 0; i < nr_redist_regions; i++) {
  1304         struct resource res;
  1305         int ret;
  1306 
  1307         ret = of_address_to_resource(node, 1 + i, &res);
  1308         rdist_regs[i].redist_base = of_iomap(node, 1 + i);
  1314         rdist_regs[i].phys_base = res.start;
  1315     }
  1316 
  1317     if (of_property_read_u64(node, "redistributor-stride", &redist_stride))
  1318         redist_stride = 0;
  1319 
  1320     err = gic_init_bases(dist_base, rdist_regs, nr_redist_regions,
  1321                  redist_stride, &node->fwnode);
  1339 }
  1340 
  1341 IRQCHIP_DECLARE(gic_v3, "arm,gic-v3", gic_of_init);

重点分析gic_init_bases:

 1071 static int __init gic_init_bases(void __iomem *dist_base,
  1072                  struct redist_region *rdist_regs,
  1073                  u32 nr_redist_regions,
  1074                  u64 redist_stride,
  1075                  struct fwnode_handle *handle)
  1076 {
  1077     u32 typer;
  1078     int gic_irqs;
  1079     int err;
  1080 
  1081     if (!is_hyp_mode_available())
  1082         static_branch_disable(&supports_deactivate_key);
  1083 
  1084     if (static_branch_likely(&supports_deactivate_key))
  1085         pr_info("GIC: Using split EOI/Deactivate mode\n");
  1086 
  1087     gic_data.fwnode = handle;
  1088     gic_data.dist_base = dist_base;
  1089     gic_data.redist_regions = rdist_regs;
  1090     gic_data.nr_redist_regions = nr_redist_regions;
  1091     gic_data.redist_stride = redist_stride;
  1092 
  1097     typer = readl_relaxed(gic_data.dist_base + GICD_TYPER);
  1098     gic_data.rdists.gicd_typer = typer;
  1099     gic_irqs = GICD_TYPER_IRQS(typer);
  1100     if (gic_irqs > 1020)
  1101         gic_irqs = 1020;
  1102     gic_data.irq_nr = gic_irqs;
  1103 
  1104     gic_data.domain = irq_domain_create_tree(handle, &gic_irq_domain_ops,
  1105                          &gic_data);
  1106     irq_domain_update_bus_token(gic_data.domain, DOMAIN_BUS_WIRED);
  1107     gic_data.rdists.rdist = alloc_percpu(typeof(*gic_data.rdists.rdist));
  1108     gic_data.rdists.has_vlpis = true;
  1109     gic_data.rdists.has_direct_lpi = true;
  1110 
  1116     gic_data.has_rss = !!(typer & GICD_TYPER_RSS);
  1119                                                                  
  1126     set_handle_irq(gic_handle_irq);
  1128     gic_update_vlpi_properties();
  1129 
  1130     if (IS_ENABLED(CONFIG_ARM_GIC_V3_ITS) && gic_dist_supports_lpis())
  1131         its_init(handle, &gic_data.rdists, gic_data.domain);
  1132 
  1133     gic_smp_init();
  1134     gic_dist_init();
  1135     gic_cpu_init();
  1136     gic_cpu_pm_init();
  1138     return 0;
  1145 }
  • 确认支持SPI 中断号最大的值为多少,GICv3最多支持1020个中断(SPI+SGI+SPI).GICD_TYPER寄存器bit[4:0], 如果该字段的值为N,则最大SPI INTID为32(N + 1)-1。 例如,0x00011指定最大SPI INTID为127。

  • 向系统中注册一个irq domain的数据结构. irq_domain主要作用是将硬件中断号映射到IRQ number。

  • 用于区分MSI域。 比如一个域用作PCI/MSI, 一个域用作wired IRQS.

  • 判断GICD 是否支持rss, rss(Range Selector Support)表示SGI中断亲和性的范围 GICD_TYPER寄存器bit[26], 如果该字段为0,表示中断路由(IRI) 支持affinity 0-15的SGI,如果该字段为1, 表示支持affinity 0 - 255的SGI

  • 判断是否支持通过写GICD寄存器生成消息中断。GICD_TYPER寄存器bit[16]

  • 设定arch相关的irq handler。gic_irq_handle是内核gic中断处理的入口函数。

  • 更新vlpi相关配置。gic虚拟化相关。

  • 初始化ITS。 Interrupt Translation Service, 用来解析LPI中断。 初始化之前需要先判断GIC是否支持LPI,该功能在ARM里是可选的。

  • 该函数主要包含两个作用。

    • 1.设置核间通信函数。当一个CPU core上的软件控制行为需要传递到其他的CPU上的时候,就会调用这个callback函数(例如在某一个CPU上运行的进程调用了系统调用进行reboot)。对于GIC v3,这个callback定义为gic_raise_softirq.
    • 2.设置CPU 上下线流程中和GIC相关的状态机。
  • 初始化GICD。

  • 初始化CPU interface.

  • 初始化GIC电源管理。

函数调用流程如下:
在这里插入图片描述

  分析之前需要了解IRQ domain,请查看linux IRQ Management(二)- IRQ Domain.

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_41028621/article/details/89713208

智能推荐

class和struct的区别-程序员宅基地

文章浏览阅读101次。4.class可以有⽆参的构造函数,struct不可以,必须是有参的构造函数,⽽且在有参的构造函数必须初始。2.Struct适⽤于作为经常使⽤的⼀些数据组合成的新类型,表示诸如点、矩形等主要⽤来存储数据的轻量。1.Class⽐较适合⼤的和复杂的数据,表现抽象和多级别的对象层次时。2.class允许继承、被继承,struct不允许,只能继承接⼝。3.Struct有性能优势,Class有⾯向对象的扩展优势。3.class可以初始化变量,struct不可以。1.class是引⽤类型,struct是值类型。

android使用json后闪退,应用闪退问题:从json信息的解析开始就会闪退-程序员宅基地

文章浏览阅读586次。想实现的功能是点击顶部按钮之后按关键字进行搜索,已经可以从服务器收到反馈的json信息,但从json信息的解析开始就会闪退,加载listview也不知道行不行public abstract class loadlistview{public ListView plv;public String js;public int listlength;public int listvisit;public..._rton转json为什么会闪退

如何使用wordnet词典,得到英文句子的同义句_get_synonyms wordnet-程序员宅基地

文章浏览阅读219次。如何使用wordnet词典,得到英文句子的同义句_get_synonyms wordnet

系统项目报表导出功能开发_积木报表 多线程-程序员宅基地

文章浏览阅读521次。系统项目报表导出 导出任务队列表 + 定时扫描 + 多线程_积木报表 多线程

ajax 如何从服务器上获取数据?_ajax 获取http数据-程序员宅基地

文章浏览阅读1.1k次,点赞9次,收藏9次。使用AJAX技术的好处之一是它能够提供更好的用户体验,因为它允许在不重新加载整个页面的情况下更新网页的某一部分。另外,AJAX还使得开发人员能够创建更复杂、更动态的Web应用程序,因为它们可以在后台与服务器进行通信,而不需要打断用户的浏览体验。在Web开发中,AJAX(Asynchronous JavaScript and XML)是一种常用的技术,用于在不重新加载整个页面的情况下,从服务器获取数据并更新网页的某一部分。使用AJAX,你可以创建异步请求,从而提供更快的响应和更好的用户体验。_ajax 获取http数据

Linux图形终端与字符终端-程序员宅基地

文章浏览阅读2.8k次。登录退出、修改密码、关机重启_字符终端

随便推点

Python与Arduino绘制超声波雷达扫描_超声波扫描建模 python库-程序员宅基地

文章浏览阅读3.8k次,点赞3次,收藏51次。前段时间看到一位发烧友制作的超声波雷达扫描神器,用到了Arduino和Processing,可惜啊,我不会Processing更看不懂人家的程序,咋办呢?嘿嘿,所以我就换了个思路解决,因为我会一点Python啊,那就动手吧!在做这个案例之前先要搞明白一个问题:怎么将Arduino通过超声波检测到的距离反馈到Python端?这个嘛,我首先想到了串行通信接口。没错!就是串口。只要Arduino将数据发送给COM口,然后Python能从COM口读取到这个数据就可以啦!我先写了一个测试程序试了一下,OK!搞定_超声波扫描建模 python库

凯撒加密方法介绍及实例说明-程序员宅基地

文章浏览阅读4.2k次。端—端加密指信息由发送端自动加密,并且由TCP/IP进行数据包封装,然后作为不可阅读和不可识别的数据穿过互联网,当这些信息到达目的地,将被自动重组、解密,而成为可读的数据。不可逆加密算法的特征是加密过程中不需要使用密钥,输入明文后由系统直接经过加密算法处理成密文,这种加密后的数据是无法被解密的,只有重新输入明文,并再次经过同样不可逆的加密算法处理,得到相同的加密密文并被系统重新识别后,才能真正解密。2.使用时,加密者查找明文字母表中需要加密的消息中的每一个字母所在位置,并且写下密文字母表中对应的字母。_凯撒加密

工控协议--cip--协议解析基本记录_cip协议embedded_service_error-程序员宅基地

文章浏览阅读5.7k次。CIP报文解析常用到的几个字段:普通类型服务类型:[0x00], CIP对象:[0x02 Message Router], ioi segments:[XX]PCCC(带cmd和func)服务类型:[0x00], CIP对象:[0x02 Message Router], cmd:[0x101], fnc:[0x101]..._cip协议embedded_service_error

如何在vs2019及以后版本(如vs2022)上添加 添加ActiveX控件中的MFC类_vs添加mfc库-程序员宅基地

文章浏览阅读2.4k次,点赞9次,收藏13次。有时候我们在MFC项目开发过程中,需要用到一些微软已经提供的功能,如VC++使用EXCEL功能,这时候我们就能直接通过VS2019到如EXCEL.EXE方式,生成对应的OLE头文件,然后直接使用功能,那么,我们上篇文章中介绍了vs2017及以前的版本如何来添加。但由于微软某些方面考虑,这种方式已被放弃。从上图中可以看出,这一功能,在从vs2017版本15.9开始,后续版本已经删除了此功能。那么我们如果仍需要此功能,我们如何在新版本中添加呢。_vs添加mfc库

frame_size (1536) was not respected for a non-last frame_frame_size (1024) was not respected for a non-last-程序员宅基地

文章浏览阅读785次。用ac3编码,执行编码函数时报错入如下:[ac3 @ 0x7fed7800f200] frame_size (1536) was not respected for anon-last frame (avcodec_encode_audio2)用ac3编码时每次送入编码器的音频采样数应该是1536个采样,不然就会报上述错误。这个数字并非刻意固定,而是跟ac3内部的编码算法原理相关。全网找不到,国内音视频之路还有很长的路,音视频人一起加油吧~......_frame_size (1024) was not respected for a non-last frame

Android移动应用开发入门_在安卓移动应用开发中要在活动类文件中声迷你一个复选框变量-程序员宅基地

文章浏览阅读230次,点赞2次,收藏2次。创建Android应用程序一个项目里面可以有很多模块,而每一个模块就对应了一个应用程序。项目结构介绍_在安卓移动应用开发中要在活动类文件中声迷你一个复选框变量

推荐文章

热门文章

相关标签