第一篇.学习中断系统

关键词:GIC中断控制器,ARM中断控制体系,ARM中断控制体系V2.0标准,SPI, PPI ,SGI, TrustZone , IRQ ,FIQ, 中断ID号

以下关于ARM的GIC的介绍转自该网址作者的博客:

https://blog.csdn.net/sunsissy/article/details/73791470

供自己学习使用。

看过SOC架构的同学知道,CPU接受外部的中断处理请求,并进行处理,其实是一个被动接受的过程,这样好处是既能

保证主任务的执行效率,又能及时获知外部的请求,从而处理重要的设备请求操作。

GIC的全称为general interrupt controller,主要作用可以归结为:

接受硬件中断信号,并进行简单处理,通过一定的设置策略,分给对应的CPU进行处理。

这样的图比较简单,可以看下在ARM完整的SOC结构中,其位置是什么样的:

这是ARM比较新的架构图,其中CORELINK CCI-500是片上互联总线,也就是AMBA,在这总线上面挂了不同的设备。比如NIC-400设备,这可以理解为network interface设备,在这个设备上使用者可以再连接其它需要的器件。那么cpu,也就是cortex-72(大核)cortex-53(小核)也是挂在总线上。上接GIC-500,也就是我们说的中断控制器,这个图其实是逻辑上的,实际中,GIC-500也是有线连接到CCI-500上,通过CCI-500cpu连接交互,ARMDistributor应该是为表示cpuGIC之间的关系,才将其逻辑图表示成大家看到的。详细的可以参考ARM的官方网站介绍。

2.硬件中断的分类和GIC的组成

2.1 GIC_V2的介绍

我们知道了GIC的大致用途,那么它包含了那些部分,如果工作的,有什么特殊的地方呢。

OK,我们一个一个来。先来看下GIC的大致组成结构:


这是经典的GIC V2图,实际上大家能看到的经典的图也是这个。

2.1.1中断源的分类

这里面把硬件中断源分为了3类:

SPI:这是shared peripheral interrupt , 这是常见的外部设备中断,也定义为共享中断,比如按键触发一个中断,手机触摸屏触发的中断,共享的意思是说

可以多个Cpu或者说Core处理,不限定特定的Cpu。一般定义的硬件中断号范围31~1019.

PPI:这里指的是private peripheral interrupt16~31,私有中断,为什么这样说呢,这些中断一般是发送给特定的Cpu的,比如每个Cpu有自己对应的Physicaltimer,产生的中断信号就发送给这个特定的cpu进行处理。比如ARM_V8平台上的:

 

在设备上,cat /proc/interrupts可以看到:


 

SGI:这个中断有些同学遇到的比较少,software generatedinterrupt,软件出发产生的中断,中断号范围0~15,也就是最前的16个中断。如果在X86平台上做过开发工作的同学可能有影响,其实这就是相当于IPI,简单的说Cpu_1要给Cpu_2发送特定信息,比如时间同步,全局进程调度信息,就通过软件中断方式,目标Cpu接受到这样的中断信息,可以获取到信息是哪个Cpu发送过来的,具体的中断ID是哪个数字,从而找到对应处理方式进行处理。比如MTK上的:


2.1.2 GIC的组成

这是硬件中断的分类。

那么GIC控制器完成这样的中断信号收集和分发工作,也划分了几个功能部分,见上面第2个图。包含2个部分:


DistributorCPU interface.

Distributor:

The Distributor block performs interruptprioritization anddistribution to the CPU interface
blocks that connect to the processors in the system. The Distributor blockregisters are identified by the GICD_ prefix

The Distributor provides a programminginterface for:
• Globally enabling the forwarding of interrupts to the CPU interfaces.

• Enabling or disabling eachinterrupt.
• Setting the priority level of each interrupt.
• Setting the target processor list of each interrupt.
• Setting each peripheral interrupt to be level-sensitive oredge-triggered.
• Setting each interrupt as either Group 0 or Group 1.

CPU Interface:

Each CPU interface block provides theinterface for a processorthat is connected to the GIC. Each CPUinterfaceprovides

a programming interface for:
• enabling the signaling of interrupt requests to the processor
• acknowledging an interrupt

• indicating completion of the processing of an interrupt
• setting an interrupt priority mask for the 
processor
• defining the preemption policy for the processor
• determiningthe highest priority pending interrupt for the processor .

这是其英文解释,其实总的来说:

Distributor,做为一个中断路由的设备,主要完成工作:

对整个中断控制器设备的使能等操作。

对每一个中断的优先级控制。

对每一个中断的触发方式设置。

对没一个中断的目标发送CPU进行设置,决定分发到哪个具体的CPU上进行处理。

对中断的去向,是到G0还是G1,这个是安全域的概念。我们后面补充GIC V3V4的概念再展开说。

记录每个中断的状态,是否到来,是否处理中,是否处理完,是否在等待发送状态等等。

那么,CPUInterface主要工作是:

使能和发送一个具体的中断信号到特定对应的CPU上,

确认具体中断已经被CPU接受,处理,以及处理完成。

设置cpu能接受的中断的优先级别。以及对应的基于级别的中断抢断等处理。

所以,从第二个图来说。中断信号先到Distributor,然后根据设定目标CPU,送到CPU对应的Interface上,在这里仲裁是否优先级足够高,

是否可以抢断或者打断当前的终端处理等,如果可以,那么CPU Interface就发送一个物理的SIGNALCPUIRQ接线上,CPU感知到中断信号,

从而转到中断模式进行处理。

那么途中带*号的IRQFIQ是什么意思呢?

FIQFastinterrupt request,这是ARM上定义的一种中断处理方式,某些终端需要快速处理完成,在这种情况下,CPU会简化操作,然后退出中断。

那么从留向来说,途中是直接到CPU侧的,没有经过Distributor?

是的,我们说过,Distributor可以被使能,等各种操作,那么当其主功能暂时关闭了怎么办呢,这就又了BYPASS功能,一个已将IRQ直接绕过它,

送到CPU侧,在某些情况下这可以作为一个唤醒的信号源去WAKE UP 对应的CPU,或者可以设置成为X86上的NMI,也就是不可屏蔽中断来处理。

2.2 GIC_V3的介绍

在现在市面上看到的手机或者其它设备产品中,既有老的V2版本的中断控制器,也有比较新的V3结构的,而且是在不断向后演进,我们有必要依照新的V3的来说明,毕竟,这是后面的趋势,比如现在大家现在可以看到GIC_V4的介绍了。

好,我们切入正题,在之前介绍GIC_V2的基础上我们扩展下。


在上图中,我们在GIC_V3的基础上整理出这样的结构图,其实主题结构和V2是大致一样的,但是我们会发现,多了Redistributor这样的组件,这是为啥呢?另外,我也也看到cpu interface移到GIC外面了,为啥这样表示逻辑图呢?多出来的LPI这个中断是做什么用的?

2.2.2 GIC中断

其实,LPI解释为LPI (Locality-specific Peripheral Interrupt)。

所有的中断类型可以分为:

SGI:software generated Interrupt

软件触发的中断,这个和之前解释的一致。

PPI:Private Peripheral Interrupt

私有中断类型,这个和之前解释的也是一致。但是PPI直接到Redistributor,绕过, 这是因为增加的Redistributor一个是为LPI需要,特殊的中断类型,既然PPI是各自CPU都有的,就没必要再经过之前的Distributor

来分发了,这样也导致了其中的寄存器等也做了修改。我们后面介绍寄存器时候再说明。

SPI:SharedPeripheral InterruptDistributor

共享外围硬件中断,这个也是和GIC_V2解释一样的ID32-ID1019。

但是,多了LPI(Locality-specific Peripheral Interrupt)
LPIs are new in GICv3, and they are different to the other types of interruptin a number of ways . In particular, LPIs are always message-based interrupts,and their configuration is held in tables in memory rather than registers.

这是一个基于消息的中断类型,是ARM为后续的SERVER等产品做的扩展。


在传统的GIC流程中如上图,外围设备的中断触发线是引出到GIC上的,这样可以理解为一个物理的SIGNAL,比如一个高电平信号,边沿触发信号。


但是实际上在现在GIC_V3中,中断可以依照MESSAGEBASED方式来触发,外围设备的终端信号先到INTERCONNECT(AMBA总线),然后片上总线再给GIC发送一个消息,这个作为一个IRQ来源,这就是基于消息的中断的简单解释,当然这个需要总线,SOC等做调整适配,在这里我们不展开来说,有兴趣的同学可以看看CCI-500等总线如何定义信号灯细节的。

我们这样来表示下整个图:


从ITS是Interrupt Translate service,如果我们只看图右侧的部分,X86上经典的PCI总线,经过一系列转换发送一个中断到X86的CPU上,从而处理。那么现在我要直接把这部分搬到arm,由于体系架构不同,中断控制器逻辑和流程都不一样, X86对应APIC-IO 和Local APIC这样的终端控制逻辑。

但是我们可以这样来处理:

  1. 我们增加一个ITS,在这里定义好特定的消息,包含消息从哪里来的,比如PCI-ROOT,具体的设备编号,比如PCIe-endpoint对应的设备,可能是个音频设备,或者简单的按键,这都不是重点。关键是ITS可以根据定义好的配置,来把对PCI发来的中断消息进行转换,比如转换成IRQ_ID:8200终端

  2. 这样就可以发送到Redistributor上进行分发处理,和一般的IRQ类似,但是不经过Distributor,其中断也没有Pending状态,如果处理不过来丢失了那就丢失了,不会特别的关注。

从现在我们看到的资料来说,还没有看到LPI的具体应用,但是ARM已经在在很多方面为SERVER的架构做了调整,这也是其中一个,所以在这里会提示下,但不是我们介绍的重点。

 

2.2.2 GIC的组成

我们在上面图中,给出GIC_V3已经之后对应的逻辑图,从中可以看到几个部分:

 

Distributor

The Distributor registers arememory-mapped, and contain global settings that affect all PEs
connected to the interrupt controller. The Distributor provides a programminginterface for:
· Interrupt prioritization and distribution of SPIs.
· Enabling and disabling SPIs.
· Setting the priority level of each SPI.
· Routing information for each SPI.
· Setting each SPI to be level-sensitive or edge-triggered.
· Generating message-based SPIs.
· Controlling the active and pending state of SPIs.
· Controls to determine the programmers’ model that is used in each Securitystate (affinity routing or legacy).

CPU Interface

Each Redistributor is connected to aCPU interface. The CPU interface provides a programming interface for:
· General control and configuration to enable interrupt handling.
· Acknowledging an interrupt.
· Performing a priority drop and deactivation of interrupts.
· Setting an interrupt priority mask for the PE.
· Defining the preemption policy for the PE.
· Determining the highest priority pending interrupt for the PE

Redistribute (V3,V4)

For each connected PE there is aRedistributor. The Redistributors provides a programming interface for:
· Enabling and disabling SGIs and PPIs.
· Setting the priority level of SGIs and PPIs.
· Setting each PPI to be level-sensitive or edge-triggered.
· Assigning each SGI and PPI to an interrupt group.
· Controlling the state of SGIs and PPIs.
· Base address control for the data structures in memory that support theassociated interrupt properties and    pending state for LPIs.
· Power management support for the connected PE.

其实之前对应的Distributor 和CPU Interface也还是一致的,但是Distributor不再处理PPI这样的中断,PPI直接送到Redistribute来处理,另外Redistribute也会处理LPI类型中断,其它逻辑上和我们之前介绍的是相似的,在这里不再重复。

 

2.3 IRQ生命周期

从GIC角度来说,一个中断可以分为几个阶段,对应不同的状态:


当GIC上配置的中断,配置好之后,没有其对应的中断到来,或者之前处理的中断已经完全结束了,这个可以表示为

Deactive状态或者Inactive,如果中断信号到来,GIC获取到了,这个时候要经过一系列的的判断,然后送给对应的CPU来处理,在CPU确认该中断并处理之前,状态是Pending,如果CPU获取到了该终端确认要处理了,那么久变为Active状态。这个时候,对应的GIC上终端信号线可能依旧有效,比如高电平触发的终端信号还没有被拉低,或者说又来一个同样的终端,那就是Activeand Pending。

2.4 IRQ Group


在ARM中,新的架构上引入了EX的概念,如果Ex0/1/2/3,以及安全域和非安全域,这个我会在补充个Topic:TRUSTZONE来说明,或者大家也查看资料来理解ARM的工作模式,比如FIQ/IRQ等传统模式。只是现在ARM淡化了这样模式,使用Ex,exception level 0/1/2/3,比如我们看到的ANDROID APP就是在Ex0,也就是USER空间,Ex1是我们常见的KERNEL空间。Ex2是对应的Hypervisor,虚拟化对应的空间,那么Ex3就是最好的权限和异常级别,这里可以由各个芯片厂家自己定义来添加对应的如ATF底层功能。

另外还在CPU上划分了SECURE 和NON-SECURE空间,这需要ARM对应的控制寄存器等来进行权限控制。

所以,IRQ的发到一个CPU上,还需要知道送到哪个Ex去处理,是送到KERNEL(Ex1)?还是安全域里面,这也在GIC里面进行配置,然后一个IRQ到来了才会往对应的寄存器里面发送。

OK,这概念比较抽象,我们在后面详细介绍GIC主要的寄存器和功能时候,会介绍,如果能理解对应的寄存器和作用,那么GIC的组成和功能也就明白了。


连接"arm GIC介绍之一",我们补充对应的GIC 寄存器描述,和主要的用法说明,仅供参考。 

3 . GIC_V3寄存器介绍

我们把完整的图放在这里:



 从这里看到,GIC分为3个主要的部分,要完成其作用,而且给用户进行预先配置的方法,GIC就提供了一寄存器,这些寄存器的设计就是围绕着这些主要的功能来实现的

3.1Distributor Registers

在前面介绍过,Distributor主要完成对应的IRQ状态的记录,对应的IRQ分发,那么围绕这样的功能,对应的寄存器主要有:



这里面有Distributor控制寄存器,控制Distributor使能和关闭,以及对应的具体IRQ的状态寄存器,比如GICD_ISACTIVER,这里记录某一个IRQ是否ACTIVE,还有逻辑功能寄存器,比如GICD_IPRIORITY,这里记录对应

IRQ的优先级别。这些都以GICD_*为前缀打头。我们来仔细说明。


GICD_CTLR




这个是GICD_CTLR寄存器,名字和对应的位功能来看是对Distributor控制使能,比如BIT_1,就是使能GROUP_1,


并且是NON-SECURE,如果是LINUX系统,就是KERNEL空间。一般来说对这个寄存器每个位都是打开的。BIT_31是等待回应,当配置对应寄存器了,写入到硬件,需要生效,那么可以读取这个位来看是否已经写入配置信息并生效。ARE是对应的AFFINITY,其实就是分配到哪个具体的CPU,我们在ROUTER寄存器说明时候来补充。

GICD_TYPER


这个是GICD_TYPER寄存器,其主要作用是获取SOC平台厂商提供的GIC的具体信息,现在一般提供信息是该GIC能够支持最多多少个IRQ硬件中断源。


 



ITLinesNumber:

SPI numbers in specification, butthis should include (SGI, PPI actually)

比如我们现在X20平台上都出来并且换算出来ITLinesNumber: 0xb,那么每个数字表示有32IRQ      

12* 32-1= 383 IRQ numbers.

支持384IRQ,从0383,为最大数字。

最大支持

(0x1f+1)*32-1 =1023 

1024个中断,这个GIC现在通用的说明是一致的,如果不考虑LPI的话。

GICD_IGROUP&GICD_IGRPMODR

这两个在一起使用,一个IRQ对应一个BITGICD_IGROUP,以及一个BITGICD_IGRPMODR

所以如果支持384IRQ,那么真个GIC就需要384BIT来表示384GICD_IGRPMODR,同理其它对应的

BIT方式表示的也是如此。


这两个结合起来表示某个具体的IRQ应该送到哪个Ex,是安全域还是非安全域,现在一般只支持3个,如上图表示。


GICD_IROUTER


这个寄存器的名字不好理解,其实,如果我们修改下GICD_TARGET,这样,就容易理解了。简单来说,该寄存器控制一个IRQ发到哪个CPU进行处理。在现在的ARM中,cpu的拓扑结构用AFFINITY来表示。



CPUCLUSTER为单位进行管理,比如X20上有10CPU,其中4个小核为一个CLUSTER_2,另外4CPU

CLUSTER_1,两个大核为CLUSTER_0,这样有利于对CPU的上电,频率等管理控制,尤其是CPUOFF时候,当一个CLUSTER里面的CPUOFF了,那么就以CLUSTER为单位把对用的电压给关闭或做其它处理,时钟进行对应的GATE,当然这只是考虑的部分因素,这部分需要参考Big.Little进行参考。我们在这里需要知道对应的管理结构,或者说

拓扑结构就可以了,在ARM中有对应MIPDR寄存器表示对应的CPU处于哪个SYSTEM,哪个CLUSTER,以及在CLUSTER里面的对应的编号,如图中的右上角说明。

所以,对于一个IRQ,需要知道发送给哪个CPU处理,就靠GICD_IROUTER里面的信息来决策。


现在一般只使用AFFINITY 0~2,如果系统过于复杂,可能使用到AFFINITY 3,BIT 31其实是个特殊开关,如果设置1,那么来了一个IRQ,这个IRQ就给所有的CPU发送。在1*N模型中,一个IRQ可以发送给多个CPU但是只能由其中一个处理,至于是具体哪个CPU先发现并处理,并通知其它CPU,这个在ARM的文档中说明是自定义实现,也就是SOC厂商可以在硬件上自己决策使用具体的策略和方式,这里不说明,另外一个原因目前无法获取到具体SOC厂商在这块的具体策略和实现方法的细节。另外一个是软件上可以进行处理互斥,保证当前CPU处理一个IRQ,另外的CPU不处理。另外一个模型是N*N,也就是一个IRQ信号可以发送给多个CPU,这多个CPU都可以处理,现在大多厂商没有采取这种方式。而是用前者。

如果BIT 31没有设置为1,那么具体发送到哪个CPU就由AFFINITY 0/1/2来决定了,具体的对应关系见图中不同颜色的连接线。


GICD_IPRIORITYR

优先级寄存器



对于IRQ,如果只有一个到来,送给CPU处理,这个比较简单。但是实际情况远比这种场景复杂,比如某一个中断在PENDING状态,等CPU处理,这个时候来了另外一个IRQ,而这个IRQ的紧迫性更高,实际上我们想打断之前的等待流程,让这个新的IRQ插队,那么就需要给不同IRQ设置不同优先级,来表示对用的处理优先级。

在现在的GIC_V3中,一个IRQ优先级可以从0255,用8BIT来表示,见上图中红色圈出来的部分,所以一个32位的寄存器,其实可以表示4IRQ的优先级。那么数字越小,级别越高,0表示最高,255最低,我们图中给出例子。

IRQ_34的优先级别值为0b:10100000IRQ_33的优先级别值为0b:10100001IRQ_34的值比IRQ_331,所以级别就高。

但是在实际中,我们不需要这么多的级别范围255,我们需要32个级别,或者64个级别就可以了,这就需要对级别LEVEL进行优化,可以忽略其低位的BIT,现在使用的X20上每个LEVELSTEP8,忽略低3位,这是ARM寄存器设计的写入忽略原则,以及读取为0原则。途中右下部分,我们做的简单实验,对优先级寄存器里面写入0b:11111111,但是都出来的是0b:11111000,也就是最低3位被忽略了。那么LEVEL_0是从0~7LEVEL_1是从8~15

所以:原先IRQ_34的优先级别值为0b:10100000IRQ_33的优先级别值为0b:10100001IRQ_34的值比IRQ_331

但是实际能够配置到寄存器里面的值都是0b:10100000。优先级别是一样的。总的原则不变,还是值低的优先级高。所以设置时候需要使用者注意。

GICD_SGIR

这个寄存器作用是产生软件IRQ,现在GIC_V3以及之后的版本,放到CPU INTERFACE侧,这里先提示下。

3.2Redistributor Registers

对于Redistributor对应的寄存器,以GICR*为前缀打头。这里面涉及到的一部分是功能和开关寄存器,比如GICR_TYPER,GICR_CTLR。另外有针对SGIPPI的状态优先级寄存器,由于GIC_V3后这32IRQ的状态和对应的设置挪到Redistributor,所以增加了对应的GICR_ISACTIVER, GICR_IGROUPER0等,作用和Distributor里面对应的SPIIRQ)寄存器一样,这里不重复,我们只说明几个特殊的寄存器,来解释Redistributor里特殊的功能。


 GICR_CTLR


因为是每个CPU都对应一个Redistributor,所以这些寄存器上面的大多数设置都对应单个CUP,比如DPG0,表示对应的CPU是否要接受送到G0的中断,同理DPG1NSDPG1S,这些并不影响其它CPU接受并在安全域和非安全域处理对应的中断。

UWP这个其实是个Distributor通信状态的确认,这在不同芯片厂商是自己定义的,比如使用一组硬件上的信号来表示不同的状态,从一般的使用者层面来说感知不到具体的两者之间的通信交互,所以大多数开发者可以不关心这个位的设置。

 

GICR_TYPER


GICR_TYPER提供了对应的Redistributor信息,比如是否支持PLPISVLPIS,这些可能大家都用不到,可以先不用去管它。其中processor number,并且之前介绍的复杂的AFFINITY管理方式关闭后,从看到的资料理解上来说,可以直接用这里面编号来作为目标CPU,比如这里的编号15,就表示第16CPU。目前这个寄存器我们没有看到对应的使用,都是默认使能的。


GICR_WAKER


在介绍这个寄存器之前,我们介绍下WAKE UP方式。


举个例子,当设置IRQ_100这个中断固定发送到CPU_N上处理之后,实际中来个一个对应的中断信号,而CPU_N在之前因为无任务处理,进入了省电模式,其对应的TIMERGATED,对应的电压也被POWER DOMAIN给处理降低了或者关闭了。而新的架构(GIC_V3)后CPU INTERFACE以及对应的寄存器部分都和CPU在同一个电压DOMAIN中,那么,这个时后明显是不能把IRQ直接发送到CPU INTERFACE侧,然后再给CPUIRQ信号线发送信号的。那么Redistributor就需要发送一个Signal wakerequest,请求对应的POWERDOMAIN CONTROLLER先把电压给加上,TIMER给开启,CPU和对应的INTERFACE能够工作,这个时候再发送IRQ信号给其处理。这就是WAKER的开关作用。


ChildrenAsleep是读取出来的,其中的值表示1的话,说明不能发送IRQ,对应的INTERFACE休眠,发了对方也不会理会的,0就是相反作用,这个一般是读信息,不写,使用者可以读出来为0的就表示可以和这个CPU正常交互。

ProcessorSleep,就是我们介绍的WAKER方式,如果为0,表示设置CPU没有处于低电模式,1表示处于进入模式或处于低电。可以发送WakeRequest信号。

其实,这两个定义看起来比较模糊,并不明确,在ARM的资料上介绍的话那么是提到设计的理念,也就是唤醒后再处理,具体实现时候,还是IMPDEF,也就似乎IMPLEMENTED DEFINE,芯片厂家自己定义实现。

我们看下具体使用:


其实这里代码实现很简洁,不管CPU是否处于低电模式(这里第一次开启,一般是出于低电),那么设置对应PROCESSORSLEEP0,以后就可以发送WAKE REQUEST去唤起,这应该会有硬件信号去唤醒INTERFACE,等待一段时间之后查看CHILDRENASLEEP来判断INTERFACE是否已经唤醒起来了。

以上介绍Redistributor对应的寄存器。

3.3 CPUInterface


说到INTERFACE,我们看下一个IRQ的流程:


IRQ100到来,其中断优先级别为0xA0,如果优先级别不冲突,那么久经过Redistributor,送到对应的CPU Interface侧,和GIC_PMR寄存器里面设定的门槛优先级比较。高于对应的优先级,那么和正在处理的IRQ的优先级GIC_RPR寄存器值比较,也高于正在处理的IRQ,那么就抢断,如果没有正在处理的IRQ,那就不存在抢断。IRQ放到GIC_IAR寄存器里面,产生一个电信号给CPUIRQ_input引脚, CPU读取GIC_IAR里面的中断号,并处理,之后向GIC_EOI寄存器里面写入IRQ,表示IRQ处理结束。

至此,一个中断处理完成。

那么这里面就涉及到了CPU Interface侧的对应的寄存器,我们主要列举如下:

    1. 分别介绍。

      CPU Interface  对应的寄存器以GICC*为前缀。

       

      GICC_CTLR


      这个寄存器功能简单,控制FIQ/IRQBYPASS,并且EOI的配置。这个有点特殊,按照一般的解释,当IRQCPU处理完后,向GICC_EOIR里面写入IRQ号时候,那么表示中断流程完成,同时,状态要变成DE-ACTIVE,注意,是自动变成。但是EOIMODE这个配置有意思,可以决定这个“自动”的变化,如果取消“自动变为DE-ACTIVE”,那么需要使用者向GICC_DIR里面写入,这样才能把一个IRQ的最终状态变为DE-ATCIVE。目前看,这个没有使用到,也不需要手动写入GICC_DIR,从流程的逻辑性来看,这个手动写入具体用在什么样的特殊场景,还没有看到,也是个疑惑的地方。

       

       

      GICC_PMR


      这个寄存器从字面看,简单理解,中断只有优先级比这里面设置的高,才能继续向CPU侧传递,其中级别也是优化后的,只用32LEVEL表示。之前在描述Distributor不再重复说明。

       

      GICC_RPR


      这个存器记录正在处理的IRQ的优先级别,主要是用来中断抢占使用的.



    2. 正常情况下,我们看上图,低电平表示中断在处理,当IRQ_AIRQ_B两个中断到来,先后处理即可。并不会产生冲突,即使IRQ_B的优先级别更高。


    3. 即使说,当IRQ_A先到来在PENDING状态并且已经送到CPUINTERFACE侧等待CPU确认并处理,我们如果关闭抢占的话,这个时候IRQ_B到来,也不会造成一项,IRQ_B优先级别再高也得等待之前的IRQ_A处理后再进行处理。

      那么,如果抢占打开了,怎样呢?GICC_RPR就是用来做这样的信息记录和比较的。


    4. IRQ_A先到来,并且优先级高于GICC_PMR,那么得到处理,此时GICC_RPR里面的优先级从默认的0XFF变为对应的优先级,假如是0XB0;那么处理中,IRQ_B到来,优先级别也满足GICC_PMR的要求,并且值0XA0,高于GICC_RPR里面记录的IRQ_A的优先级,那么就抢占处理,执行IRQ_B对应的服务,结束之后,再继续完成对应的IRQ_A的服务。如果IRQ_A也服务完成了,那么GICC_RPR里面恢复成默认的OXFF值。

       

      GICC_IAR

       

      GICC_IAR寄存器,里面记录着等待处理的中断号,CPU从这里面读取获知到来的硬件中断号,并处理,随机对应的GIC中记录的IRQ状态从PENDING变为ACTIVE或者PENDING& ACTIVE

       

      GICC_SGI

      SGI产生软件IRQ

    5. INITD表示对应IRQ号,这里一般从IRQ0IRQ15

      IRM 0时候,需要依靠TARGETLIST来确定目标CPU,如果为1的时候,那么就是广播发送,但是不给自身发送软中断。

      TARGETLIST这里不在重复说明,只是注意,TARGETLIST里面只是表示在一个具体的CLUSTER里面的CPU编号,CLUSTER号由AFF1表示,这个TARGETLIST相当于AFF0

      在使用时候也比较简单:


      在系统INIT时候,图左侧红色框内,会使用set_smp_cross_call来注册系统的IPI消息发送函数,这里是gic_raise_softirq,那么右侧红色框内,发送一个特定IPI消息时候,当调用到__smp_cross_all时候就会链到绿色线所指的gic_raise_softirq,来产生一个IPI消息(SGI),见下面代码。

       

      GICC_EOIR

      最后介绍EOI,这个之前提到过,中断处理完成,写入对应的终端号,表示IRQ处理结束,IRQ状态变为DE-ACTIVE

GIC_V3 初始化设置 
在GIC 介绍之一和二中,可以看到GIC的逻辑结构和硬件框架。 
那么在初始化时候,需要将各个组件配置起来,Interrupt Controller中的Distributor,Redistributor,CPU Interface需要按照不同顺序配置。既然Distributor是所有CPU都可以对应的,那么这个只要在第一个CPU启动时配置就可以了,对于Redistributor和CPU Interface是PER-CPU对应的,所以,在SECONDARY CPU启动时候需要配置各自的组件。这也是下图中所表示的:

这里写图片描述
我们从Distributor初始化开始。 
GIC的初始化主要是通过配置其对应的寄存器来实现。我们介绍过 
Distributor上的主要寄存器,实际上,在实现中,也就是通过配置这些Registers来实现的。 
我们参考的代码基于X20,并且首先在Ex3的ATF安全世界里面先启动,这个并不妨碍对流程的认识: 
这里写图片描述
一个ARM设备在启动时候,首先上电,之后会从片内的BOOT ROM获取到启动初始代码,在这里做基本的芯片初始化,而后去 
加载PRE-LOADER部分,这部分在X20上面也是做如MEMORY,UART等的设置。如果说没有加载ATF(ARM TRUSTZONE FIRMWARE)的话,就会继续LK和KERNEL的流程,在X20上面因为要做TRUSTZONE的需要,所以在这里加载了ATF,同时,GIC的基本设置和最初的INIT也是在这里。 
这里的代码结构主要: 
这里写图片描述
从Gic_setup入口,在这里,首先获取到 gicd的基地址,之后才可以在这个基础上向不同寄存器写入配置数据。 
如上图,之后要最的是通过控制CTLR寄存器先把Distributor功能关闭,并配置所有的SPI发送目标域为NS-G1,在这里就是KERNEL。并重新开启Distributor。 
之后会去进入gic_cpuif_init,对相应的Redistributor和CPU Interface做设置。 
gic_cpuif_init
同样道理,先获取到Redistributor的基地址,之后用于配置。首先要设置WAKEUP,这样保证CPU Interface和对应的CPU在之后能够被唤醒。 
在Distributor配置了SPI的目标域,但是没有配置SGI和PPI,对于这32个IRQ,这里在Redistributor进行配置,也是送到NS-G1,也就是KERNEL里面处理。 
ICC_SRE的开启,是要激活CPU Interface上对应的寄存器,激活后,向对应的寄存器里面写入信息,才会有相应的动作,否则是不起作用的。 
enable_sgi_fiq,这里补充说明下,本身是可选的,X20的平台上面把 
SGI,将13(FIQ_SMP_CALL_SGI)号SGI 作为优先级最高的一个IRQ配置,在KERNEL 有HANDLE_FIQ_AS_NMI , 目的也是想DUMP 出关键信息,目前代码只有部分框架。如果可以,后面看看其它厂家是不是也是这样考虑的。 
这里在Ex3里面就做了基本的配置了,那么之后其它组件的设置好,进入KERNEL之后,还有其它部分要补充,这样,中断才能通畅到达KERNEL里面并处理。 
gic_init in kernel

上图里面体现的是第一个CPU起来之后,进入KERNEL里面,开始逐步进行INIT,调用init_IRQ,这是现在KERNEL的框架结构,一直到irqchip_init,of_irq_init,在of_irq_init里面不同的架构厂商或者说芯片的定制者,就可以增加特定的GIC代码了,比如说X86__IRQ_INIT,当然这只是设定个例子。在X20上,使用的是GIC_V3对应的GIC设备,这里增加的是gic_of_init来完成特定的初始化。 
那么在这里面做了什么呢? 
其实也比较简单: 
1. irq_domain and initialize it. 我们从GIC侧获得IRQ是硬件类型的中断号,而我们通常意义上在KERNEL里面处理的是软件层面的IRQ,比如用desc_irq表示的中,这样两者之间就需要映射,座椅就用到了irq_domain,原理如此,我们在后面会展开来说如何实现的。 
2. 给系统的中断入口设置特定体系的函数,一般是handle_irq,这里是gic_handle_irq。 
3. 设定IPI消息的句柄,当发送IPI消息时候就可以调用到gic_raise_softirq,这个在前面介绍GIC寄存器时候,在GICC_SGI部分说明到。 
另外要给出gic_cpu_notifier作为CPU ON时候的通知链条上的一环,在这里面会增加SECONDARY CPU的设置。 
4. gic_dist_init,这只开启G1的ARE功能,并将所有的SPI发送目标定位全体CPU,这由使用者自己来定义,可以将特定的IRQ直接绑定到特定的CPU上处理。 
5. gic_cpu_init对CPU对应的INTERFACE进行设置。我们可以看下里面的详细内容: 
gic_cpu_init

  1. gic_enable_redist , 设置WAKE PROCESSORSLEEP bit. 
    Read WAKEUP_ChildrenAsleep ,确保标识已经变化。这里实际上CPU 已经起来了,读取这个标识为了明确该位置,不会对后续操作产生困扰(理解如此)。
  2. gic_enable_sre 这里只是对ICC 也就是CPU interface 的Enable system registers , 并没有对EL2, EL3操作, 而不像CORE_0那样。在V3版的ATF代码里面,有提供操作的接口,并且 
    是PER_CPU的,是不是后面也提供了其它CPU进入EL3操作对应的EL2, EL3对应的GIC寄存器,应该是这样。
  3. DEFAULT_PMR_VALUE 0xf0, GICD 里面设置为0XA0, 所以X20里面中断是高于这个门限优先级。
  4. ICC_IGRPEN1 
    Controls whether Group 1 interrupts are enabled for the current Security state 。这个开启后,就可以接收到G1-NS的中断了。 
    至此,当第一个CPU起来之后,对应的GIC设置完成,可以接受驱动的终端配置,并且接受终端处理了。 
    那么,当系统中有多个CPU,比如X20上有10个CPU(CORE),其它9个如何设置呢。 
    gic init for Secondary cpu

上图中,左侧部分蓝色框,表示CPU_0在KERNEL对应的流程,这里简化。在上面介绍过:gic_cpu_notifier.notifier_call 
= gic_secondary_init,也就是主要注册了gic_secondary_init。 
在右侧绿色部分表示CPU_0~9在KERNEL里面的流程,这里只列出和GIC关联部分。入口是Secondary_start_kernel,那么之后会Notify_cpu_starting,在这里调用到Cpu_notify。其中Notify_cpu_chain就用到了之前注册的gic_secondary_init,图中下册也给出了代码的入口,这样CPU_0~9也不需要再初始化DISTRIBUTOR,只需要执行gic_cpu_init,设置自己对应的部分就可以了。 
OK,到这里,硬件上的配置流程介绍完,下面整理并介绍如何把 
HARDWARE IRQ和软件处理上的IRQ desc_irq联系起来……

GIC是ARM体系中重要的组件,在认识到GIC的组成和功能之后,了解到IRQ的大致流程,从硬件IRQ到来,到IRQ结束。我们实际在KERNEL里面,或者在设备驱动里面处理的IRQ其实是软件意义上的,那么硬件的中断和软件的中断如何联系起来的呢,大概的处理流程是如何呢? 
这章我们介绍这部分内容。

GIC 中断处理流程

我们希望理解概念和流程,总结认识和思路,所以代码细节上的解释需要忽略掉。可以看代码细节,但是总结时候要去掉。毕竟,即使是自己看过了,过了一段时间再重新读代码,也是有些陌生的。我们还是以图开始。

HW ARCH 
我们在之前介绍“arm GIC介绍之一/二/三”: 
http://blog.csdn.net/sunsissy/article/details/73791470 
http://blog.csdn.net/sunsissy/article/details/73842533 
http://blog.csdn.net/sunsissy/article/details/73842533 
一直在强调,GIC上对物理的IRQ的处理,比如上图,一个DEVICE_1上触发一个IRQ,到GIC,HWIRQ为111,如果软件侧管理中断不冲突的话,可以直接映射desc_irq 111,以此为结构并处理。但是实际中并不这么完美。 
比如图中另外一个DEVICE,同时来了3个信号,或者说,而这个设备和GIC只有一个IRQ的物理连接通路,只能传递一个IRQ信号,那么这又如何表示和区分3个信号呢,如何和CPU的软件意义上的IRQ联系起来呢? 
这就新增加了IRQ_DOMAIN的概念。

struct irq_domain {  
   struct list_head link;  
   const char *name;  
   const struct irq_domain_ops *ops; //callback函数 
   void *host_data;//this will point to irq_data, and contains gicd_base, info and so on.
    /* Optional data */   
   struct device_node *of_node;//该interrupt domain对应的interrupt controller的device node  
   struct irq_domain_chip_generic *gc; //generic irq chip concept , we ignore this.
    /* reverse map data. The linear map gets appended to the irq_domain */
    irq_hw_number_t hwirq_max; //该domain中最大的那个HW interrupt ID     
    unsigned int revmap_direct_max_irq; //
    unsigned int revmap_size; //线性映射的size,for Radix Tree map和no map,该值等于0     
    struct radix_tree_root revmap_tree; //Radix Tree map will use radix tree root node     
    unsigned int linear_revmap[]; //linear mapping lookup table, we will pay attention to it. 
    };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

简化后如图:

IRQ_DOMAIN

总结来说,IRQ_DOMAIN是以GIC 为单位,一个GIC设备对应一个IRQ_DOMAIN, 从图中可以看出,如果系统中有多个IRQ_DOMAIN,那么会形成一个list,统一管理。 
IRQ_DOMAIN里面包含了GIC的基本信息,比如host_data,可以保存对应的Distributor的基地址;最大的硬件中断数目hwirq_max;如果是线性映射,那么linear_revmap保存了线性映射的关系; 
当然,重要的irq_domain_ops里面有对应的操作,eg:gic_irq_domain_map这就是如何把硬件IRQ和软件处理侧的desc_irq对应起来的。 
只有这样映射后,软件侧才方便以desc_irq单位对IRQ进行管理和处理,desc_irq如图: 
desc_irq 
对每个desc_irq,不仅包含了一个IRQ的基本信息,也包含了对应的控制等信息。 
Irq_data中,有一个IRQ对应的硬件中断号hwirq,对应的GIC DOMAIN。当然Irq_chip以及对应的API,提供了如何向GIC写入信息比如表示IRQ处理结束写入Irq_eoi等等。 
Irq_action 里面有中断的处理入口,以及对应的具体函数HANDLER。 
这是在软件处理层面的,有了这样的接口和信息,一个驱动才可以获得一个desc_irq,把对应的IRQ注册到里面,并对触发方式,是否MASK进行控制,并找到对应的HANDLER进行注册和后续处理。所以从整个的流程来说我们给出这样的逻辑结构,见下图: 
IRQ flow
整个结构分为3部分,上面试DRIVER,可以认为是使用者,中间是软件的层面,最下面是Hardware ,这里重要的是中间的结构,为上层DRIVER提供了IRQ的注册接口request_threaded_irq,使能接口enable_irq,亲和性或者说送到具体CPU处理的配置接口irq_set_affinity等,这里不全部列举。 
那么这些接口要具体配置到GIC硬件的Hardware上,在IRQ general logic中就有对应的 irq_set_chip,这里面对应的操作会去执行和具体平台硬件相关的设置。不仅如此,当设置完成,允许中断,如果来了一个硬件中断到Hardware ,那么也在IRQ general logic进行先处理,所以这提供双向的SERVICE。会先irq_to_desc,找到对应的desc_irq,然后分类处理如果IPI走对应处理,如果是其它的走generic_handle_irq,这就转到了左侧的IRQ flow 
control-layer 。 
IRQ flow control-layer顾名思义,就是把众多的接受到的中断分流,如果是LEVEL触发类型的,走handle_level_irq,上下沿触发的走handle_edge_irq,不同的入口可能对EOI的写入时机和方式有区别等等。再如handle_percpu_irq在处理时候,因为不涉及到其它CPU,所以对于多个CPU之间共享的操作就不需要LOCK做保护。然后再去找特定的每个驱动定义的HANDLER处理。

GIC 中断处理流程实例

看下:

shell@amt6797_64_open:/ shell@amt679764open:/shell@amt679764open:/ cat /proc/interrupts 
CPU0 
29: 0 GICv3 29 arch_timer_sec_zhonghua 
30: 50721 GICv3 30 arch_timer 
96: 0 GICv3 96 mtk_cpuxgpt0 
97: 0 GICv3 97 mtk_cpuxgpt1 
…… 
184: 45 GICv3 184 mtk_cmdq 
188: 0 GICv3 188 m4u 
201: 0 GICv3 201 mt-gpt 
210: 0 GICv3 210 pmic_wrap 
211: 0 GICv3 211 mtk-kpd 
212: 0 GICv3 212 SPM 
231: 0 GICv3 231 SCP IPC_MD2HOST 
234: 720 GICv3 234 mutex

…… 
255: 0 GICv3 255 aal 
361: 0 GICv3 361 ocp_cluster2 
362: 0 GICv3 362 ocp_cluster2 
389: 1 mt-eint 5 TOUCH_PANEL-eint 
390: 0 mt-eint 6 11240000.msdc1 cd 
392: 0 mt-eint 8 iddig_eint 
400: 0 mt-eint 16 accdet-eint 
560: 1 mt-eint 176 pmic-eint 
IPI0: 7106 Rescheduling interrupts 
IPI1: 7 Function call interrupts 
IPI2: 196 Single function call interrupts 
IPI3: 0 CPU stop interrupts 
IPI4: 0 Timer broadcast interrupts 
IPI5: 112 IRQ work interrupts 
Err: 0 
shell@amt6797_64_open:/ 
上面是从X20上看到interrupts。左侧部分加粗体是硬件中断号,右侧对应的斜体数字是软件看到的desc_irq对应的编号。 
这里面可以看到,以左侧硬件中断号为索引来说。上面是29和30号中断,这个是PPI中断类型,在这里是CPU对应的TIMER,从这往后到362是SPI,在X20上支持384个硬件中断号,所以多出来的389~560就奇怪了。我们后面介绍。 
另外就是IPI消息,这个比较少。 
我们先分析下,硬件中断号如何和软件对应的。 
首先: 
开辟IRQ空间 
在进入KERNEL之后,进行GIC初始化后,我们之前提到IRQ_DOMAIN的配置,提到以GIC为单位。那么在这里通过读取参数获取到支持的HARDWARE IRQ数目是384个,去掉其中16个SGI(不需要映射),新申请384-16个desc_irq,并且把IRQ_DOMAIN其它数据如基地址等都填好,挂到LIST上统一管理。在这里,采用的是简单的线性MAPPING,所以17到384这中间的硬件中断号和软件中断号是一一对应的,打印出来: 
hwirq<->desc_irq 
第一个编号为16,最后一个编号为383的IRQ。 
之后进行基本的irq_domain_associate_many。我们看下这里做了什么: 
mapping

这里名字上说是associate,其实是对这些IRQ做些分类的基本信息填充,主要的是区分16~31号中断的描述信息,由于是PPI中断,所以将其分流入口设定为handle_percpu_devid_irq。那么其它一般的SPI 分流入口这里都设定为handle_fasteoi_irq,而不是handle_level_irq或者handle_edge_irq。


  • 0
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值