嵌入式系统内核镜像相关(十六)
文章目录
- 前言
- 一、翻译bindings文档
- 1.1 interrupts.txt内容解释
- 1.1.1 中断客户端节点(Interrupt client nodes)
- 1.1.2 中断控制器节点(Interrupt controller nodes)
- 1.1.2.1 一个单元格(one cell)
- 1.1.2.2 两个单元格(two cells)
- 1.2 xilinx,intc.txt内容解释
- 1.2.1 必需属性(Required properties):
- 1.2.2 可选属性(Optional properties):
- 1.2.3 2个示例:
- 1.3 arm,gic.txt内容解释
- 1.3.1 主要节点所需属性
- 1.3.2 可选属性
- 1.3.3 示例
- 1.3.3.1 基本用例
- 1.3.3.2 GIC 虚拟化扩展(VGIC)
- 1.3.3.3 GICv2m 扩展支持 MSI/MSI-x(可选)
- 1.4 arm,gic-v3.txt内容解释
- 1.4.1 主要节点所需属性
- 1.4.2 可选属性
- 1.4.3 子节点
- 1.4.4 示例
- 1.5 arm,nvic.txt内容解释
- 1.5.1 主要节点所需属性
- 1.5.2 示例
- 1.6 st,stm32-exti.txt内容解释
- 1.6.1 必需属性
- 1.6.2 示例
- 1.7 st,sti-irq-syscfg.txt内容解释
- 1.7.1 必需属性
- 1.7.2 可选属性
- 1.7.3 示例
- 二、中断移植(卡在设备树中断触发类型的赋值)
- 2.1 前情概述——不支持gpio_to_irq函数
- 2.2 irq_of_parse_and_map函数
- 2.3 interrupts和interrupt-parent的格式以及编号占用情况
- 2.4 UG585中中断一章的翻译
- 2.4.1 环境
- 2.4.2 功能描述
- 2.4.3 寄存器概述
- 2.4.4 编程模型
- 2.5 回到实验部分
- 总结
前言
本篇处理中断相关例程的移植。在《嵌入式系统内核镜像相关(十二)》中提到了中断移植的难点,本篇着手解决该问题。
一、翻译bindings文档
先找到devicetree
的bindings
中与中断
相关的文档:
dention@ubuntu:~/petalinux_proj/test_peta/proj_peta$ find ./ -name "xilinx,intc.txt"
./build/tmp/work-shared/plnx-zynq7/kernel-source/Documentation/devicetree/bindings/interrupt-controller/xilinx,intc.txt
如下,选中的5个文档可能与我们的项目移植有关!
包括了arm,gic.txt
、arm,gic-v3.txt
、arm,nvic.txt
、interrupts.txt
和xilinx,intc.txt
等5个文件。
参考野火的中断子系统教程讲解了GIC
和NVIC
的区别!
贴点kimichat
整理的内容:
GIC(Generic Interrupt Controller)和 NVIC(Nested Vectored Interrupt Controller)是两种不同的中断控制器。
GIC:
- 是ARM架构中的一部分,用于处理来自多个源的中断请求,并将其分发到多个处理器核心。GIC设计用于多核处理器系统,能够支持多个中断源和多个处理器核心之间的中断管理。
- GIC分为不同的版本,如GICv1(已弃用)、GICv2、GICv3和GICv4,每个版本都支持不同的中断处理特性和系统规模。GICv3引入了属性层次(affinity hierarchies),以支持更多的核心,并且增加了redistributor组件和LPI(Locality-Partitioned Interrupts)来处理消息中断。
- 通常集成在系统级芯片(SoC)中,与CPU核心紧密集成,提供高效的中断处理。
NVIC:
- 是Cortex-M系列微控制器中的中断控制器,专为单核或简单的多核系统设计。
- 提供了中断优先级设置,支持抢占式和子优先级,允许中断嵌套。
- 通常与Cortex-M内核紧密集成,提供快速的中断响应和处理。
- 支持有限数量的中断源,适合中低端的微控制器应用,而不是为大型多核系统设计的。
- 设计相对简单,易于在微控制器中实现,通常用于嵌入式系统和简单的控制应用。
GIC适用于需要处理大量中断和多核处理器的复杂系统,而NVIC则适用于中断源较少、对中断处理要求不是特别高的单核或简单多核微控制器系统。GIC设计用于多核处理器系统,能够支持多个中断源和多个处理器核心之间的中断管理。NVIC是Cortex-M系列微控制器中的中断控制器,专为单核或简单的多核系统设计,提供了中断优先级设置,支持抢占式和子优先级,允许中断嵌套。GIC通常集成在系统级芯片(SoC)中,与CPU核心紧密集成,提供高效的中断处理。NVIC通常与Cortex-M内核紧密集成,提供快速的中断响应和处理。GIC和NVIC的主要区别在于它们的设计目的、应用场景和支持的系统规模。GIC适用于需要处理大量中断和多核处理器的复杂系统,而NVIC则适用于中断源较少、对中断处理要求不是特别高的单核或简单多核微控制器系统。
1.1 interrupts.txt内容解释
直接翻译内容了,如下:
1.1.1 中断客户端节点(Interrupt client nodes)
描述生成中断的设备的节点必须包含“interrupts”属性、“interrupts-extended”属性或两者都有。如果两者都存在,后者应优先;前者可能仅出于与不认识后者的软件兼容而提供。这些属性包含中断说明符的列表,每个输出中断一个。中断说明符的格式由中断被路由到的中断控制器决定;有关详细信息,请参见下面的第2节。
示例:
interrupt-parent = <&intc1>; // 中断父控制器
interrupts = <5 0>, <6 0>; // 中断说明符列表
“interrupt-parent”属性用于指定中断被路由到的控制器,并包含一个指向中断控制器节点的单一phandle。此属性是可继承的,因此可以在中断客户端节点或其任何父节点中指定。“interrupts”属性中列出的中断始终相对于节点的中断父控制器。
“interrupts-extended”属性是当节点需要引用多个中断父控制器时使用的特殊形式。此属性中的每个条目都包含父phandle和中断说明符。“interrupts-extended”仅当设备具有多个中断父控制器时才应使用。
示例:
interrupts-extended = <&intc1 5 1>, <&intc2 1 0>; // 扩展中断说明符列表
1.1.2 中断控制器节点(Interrupt controller nodes)
设备通过“interrupt-controller”属性被标记为中断控制器。这是一个空的布尔属性。额外的“#interrupt-cells”属性定义了指定单个中断所需的单元格数。
中断控制器的绑定负责定义中断说明符的长度和格式。下面两种变体常用:
1.1.2.1 一个单元格(one cell)
“#interrupt-cells”属性设置为1,单个单元格定义了中断在控制器内的索引。
示例:
vic: intc@10140000 {compatible = "arm,versatile-vic";interrupt-controller;#interrupt-cells = <1>;reg = <0x10140000 0x1000>;
};sic: intc@10003000 {compatible = "arm,versatile-sic";interrupt-controller;#interrupt-cells = <1>;reg = <0x10003000 0x1000>;interrupt-parent = <&vic>;interrupts = <31>; /* 级联到 vic */
};
1.1.2.2 两个单元格(two cells)
“#interrupt-cells”属性设置为2,第一个单元格定义了中断在控制器内的索引,而第二个单元格用于指定以下任何标志:
bits[3:0] 触发类型和电平标志1 = 低到高边沿触发2 = 高到低边沿触发4 = 高电平电平敏感8 = 低电平电平敏感
示例:
i2c@7000c000 {gpioext: gpio-adnp@41 {compatible = "ad,gpio-adnp";reg = <0x41>;interrupt-parent = <&gpio>;interrupts = <160 1>;gpio-controller;#gpio-cells = <1>;interrupt-controller;#interrupt-cells = <2>;nr-gpios = <64>;};sx8634@22b {compatible = "sm,sx8634";reg = <0x2b>;interrupt-parent = <&gpioext>;interrupts = <3 0x8>;#address-cells = <1>;#size-cells = <0>;threshold = <0x40>;sensitivity = <7>;};
};
1.2 xilinx,intc.txt内容解释
直接翻译内容,如下:
Xilinx 中断控制器(Xilinx Interrupt Controller)
是一个在构建时配置的软核IP,用于指定中断的数量和每种中断的类型。这些细节在运行时不能更改。
1.2.1 必需属性(Required properties):
- compatible:应为 “xlnx,xps-intc-1.00.a”
- reg:指定寄存器的基本物理地址和大小。
- interrupt-controller:标识该节点为中断控制器。
- #interrupt-cells:指定编码一个中断源所需的单元格数。该值应至少为1。Xilinx设备树通常使用2,但第二个值未被使用。
- xlnx,kind-of-intr:一个32位值,指定每个可能的中断类型(1 = 边沿触发,0 = 电平触发)。中断类型通常通过生成中断的设备树节点来确定,但在这种情况下,中断类型由中断控制器基于其实现方式来确定。
- xlnx,num-intr-inputs:指定控制器特定实现支持的中断数量(1-32)。
1.2.2 可选属性(Optional properties):
- interrupt-parent:指定它所链接(级联)的中断控制器。
- interrupts:指定它所链接的父控制器的中断。
1.2.3 2个示例:
示例1:
axi_intc_0: interrupt-controller@41800000 {#interrupt-cells = <2>;compatible = "xlnx,xps-intc-1.00.a";interrupt-controller;reg = <0x41800000 0x10000>;xlnx,kind-of-intr = <0x1>;xlnx,num-intr-inputs = <0x1>;
};
级联示例(Chained Example):
中断链接到Zynq的GIC(Generic Interrupt Controller)的硬件中断61(29 + 32)。
axi_intc_0: interrupt-controller@41800000 {#interrupt-cells = <2>;compatible = "xlnx,xps-intc-1.00.a";interrupt-controller;interrupt-parent = <&ps7_scugic_0>;interrupts = <0 29 4>;reg = <0x41800000 0x10000>;xlnx,kind-of-intr = <0x1>;xlnx,num-intr-inputs = <0x1>;
};
1.3 arm,gic.txt内容解释
翻译如下:
ARM 通用中断控制器(ARM Generic Interrupt Controller):ARM SMP 核心通常与 GIC 相关联,提供每个处理器的中断(Private Interrupts,PI)、共享处理器中断(Shared Processor Interrupts,SPI)和软件生成的中断(Software Generated Interrupts,SGI)。
主 GIC 直接连接到 CPU,通常具有 PI 和 SGI。
次级 GIC 级联到上级中断控制器,并且没有 PI 或 SGI。
1.3.1 主要节点所需属性
-
compatible:应为以下之一:
- “arm,arm1176jzf-devchip-gic”
- “arm,arm11mp-gic”
- “arm,cortex-a15-gic”
- “arm,cortex-a7-gic”
- “arm,cortex-a9-gic”
- “arm,eb11mp-gic”
- “arm,gic-400”
- “arm,pl390”
- “arm,tc11mp-gic”
- “brcm,brahma-b15-gic”
- “nvidia,tegra210-agic”
- “qcom,msm-8660-qgic”
- “qcom,msm-qgic2”
-
interrupt-controller:标识该节点为中断控制器。
-
#interrupt-cells:指定编码一个中断源所需的单元格数。类型应为
<u32>
,值应为 3。第 1 个单元是中断类型;0 表示 SPI 中断,1 表示 PI 中断。
第 2 个单元包含中断类型的中断号。
SPI 中断在范围 [0-987] 内。PI 中断在范围 [0-15] 内。第 3 个单元是标志位,编码如下:
- bits[3:0] 触发类型和电平标志。
- 1 = 低到高边沿触发
- 2 = 高到低边沿触发(SPIs 无效)
- 4 = 高电平电平敏感
- 8 = 低电平电平敏感(SPIs 无效)。
- bits[15:8] PI 中断 CPU 掩码。每个位对应连接到 GIC 的 8 个可能的 CPU。位设置为 ‘1’ 表示中断连接到该 CPU。仅对 PI 中断有效。
- 请注意 PI 中断的可配置性是 IMPLEMENTATION DEFINED,因此不能保证存在(2014 年的大多数 SoC 似乎忽略此标志的设置并使用硬件默认值)。
- bits[3:0] 触发类型和电平标志。
-
reg:指定 GIC 寄存器的基本物理地址(s)和大小。第一个区域是 GIC 分发器寄存器基本地址和大小。第二个区域是 GIC CPU 接口寄存器基本地址和大小。
1.3.2 可选属性
-
interrupts:次级 GIC 的父中断控制器上的中断源,或主 GIC 上的 VGIC 维护中断(见下文)。
-
cpu-offset:在分发器和 CPU 接口区域内每个 CPU 的偏移,当 GIC 没有分区寄存器时使用。偏移是 cpu-offset * cpu-nr。
-
clocks:phandle 和特定时钟对的列表,每个时钟名称中的每个条目一个。
-
clock-names:GIC 时钟输入(s)的名称列表。有效的时钟名称取决于 GIC 变体:
- “ic_clk”(对于 “arm,arm11mp-gic”)
- “PERIPHCLKEN”(对于 “arm,cortex-a15-gic”)
- “PERIPHCLK”,“PERIPHCLKEN”(对于 “arm,cortex-a9-gic”)
- “clk”(对于 “arm,gic-400” 和 “nvidia,tegra210”)
- “gclk”(对于 “arm,pl390”)
-
power-domains:由 phandle 指定的电源控制器的绑定定义的 phandle 和 PM 域指定符,当 GIC 是电源或时钟域的一部分时使用。
1.3.3 示例
1.3.3.1 基本用例
示例如下:
intc: interrupt-controller@fff11000 {compatible = "arm,cortex-a9-gic";#interrupt-cells = <3>;#address-cells = <1>;interrupt-controller;reg = <0xfff11000 0x1000>,<0xfff10100 0x100>;
};
1.3.3.2 GIC 虚拟化扩展(VGIC)
对于支持虚拟化扩展的 ARM 核心,必须描述附加属性(仅当 GIC 是主中断控制器时存在)。
必需属性:
-
reg:额外的区域指定 VGIC 寄存器的基本物理地址和大小。第一个附加区域是 GIC 虚拟接口控制寄存器基本地址和大小。第二个附加区域是 GIC 虚拟 CPU 接口寄存器基本地址和大小。
-
interrupts:VGIC 维护中断。
示例:
interrupt-controller@2c001000 {compatible = "arm,cortex-a15-gic";#interrupt-cells = <3>;interrupt-controller;reg = <0x2c001000 0x1000>,<0x2c002000 0x2000>,<0x2c004000 0x2000>,<0x2c006000 0x2000>;interrupts = <1 9 0xf04>;
};
1.3.3.3 GICv2m 扩展支持 MSI/MSI-x(可选)
某些 GIC-400 版本的支持通过 V2M 寄存器框架的 MSI/MSI-x。这通过指定 v2m 子节点来启用。
必需属性:
-
compatible:这里的值应包含 “arm,gic-v2m-frame”。
-
msi-controller:标识该节点为 MSI 控制器。
-
reg:GICv2m MSI 接口寄存器基本地址和大小
可选属性:
-
arm,msi-base-spi:当 MSI_TYPER 寄存器包含不正确的值时,此属性应包含 MSI 帧的 SPI 基本值,覆盖硬件值。
-
arm,msi-num-spis:当 MSI_TYPER 寄存器包含不正确的值时,此属性应包含分配给帧的 SPI 数量,覆盖硬件值。
示例:
interrupt-controller@e1101000 {compatible = "arm,gic-400";#interrupt-cells = <3>;#address-cells = <2>;#size-cells = <2>;interrupt-controller;interrupts = <1 8 0xf04>;ranges = <0 0 0 0xe1100000 0 0x100000>;reg = <0x0 0xe1110000 0 0x01000>,<0x0 0xe112f000 0 0x02000>,<0x0 0xe1140000 0 0x10000>,<0x0 0xe1160000 0 0x10000>;v2m0: v2m@8000 {compatible = "arm,gic-v2m-frame";msi-controller;reg = <0x0 0x80000 0 0x1000>;};....v2mN: v2m@9000 {compatible = "arm,gic-v2m-frame";msi-controller;reg = <0x0 0x90000 0 0x1000>;};
};
1.4 arm,gic-v3.txt内容解释
翻译如下:
ARM 通用中断控制器,版本3(ARM Generic Interrupt Controller, version 3)
:在Arch64 SMP核心中,经常与GICv3相关联,提供私有外设中断(Private Peripheral Interrupts,PI)、共享外设中断(Shared Peripheral Interrupts,SPI)、软件生成中断(Software Generated Interrupts,SGI)以及特定于区域的外设中断(Locality-specific Peripheral Interrupts,LPI)。
1.4.1 主要节点所需属性
-
compatible:至少应包含 “arm,gic-v3”。
-
interrupt-controller:标识该节点为中断控制器。
-
#interrupt-cells:指定编码一个中断源所需的单元格数。必须是一个至少为3的单一单元格。
如果系统需要描述PI亲和性(affinity),则该值必须至少为4。第1个单元是中断类型;0表示SPI中断,1表示PI中断。其他值保留供将来使用。
第2个单元包含中断类型的中断号。
SPI中断在范围[0-987]内。PI中断在范围[0-15]内。第3个单元是标志位,编码如下:
- bits[3:0] 触发类型和电平标志。
- 1 = 边沿触发
- 4 = 电平触发
第4个单元是一个指向描述一组CPU的节点的phandle,此中断属于这些CPU。中断必须是PI,并且指向的节点必须是 “ppi-partitions” 子节点的子节点。对于非PI中断类型或未分区的PI,此单元必须为零。见下面的 “ppi-partitions” 节点描述。
单元格5及之后保留供将来使用,如果存在则必须具有0的值。
- bits[3:0] 触发类型和电平标志。
-
reg:指定GIC寄存器的基本物理地址(s)和大小,按照以下顺序:
- GIC 分发器接口(GICD)
- GIC 重发器(GICR),每个重发器区域一个范围
- GIC CPU 接口(ICC)
- GIC 管理程序接口(GICH)
- GIC 虚拟CPU接口(GICV)
ICC、GICH 和 GICV 是可选的。
-
interrupts:VGIC维护中断的中断源。
1.4.2 可选属性
-
re-distributor-stride:如果使用填充页,则指定连续重发器的跨度。必须是64kB的倍数。
-
#redistributor-regions:重发器所占据的独立连续区域的数量。如果存在多个此类区域,则必须存在。
-
msi-controller:布尔属性。标识该节点为MSI控制器。仅当硬件通过mbi-ranges属性公开基于消息的中断功能时才存在。
-
mbi-ranges:对的列表,其中 “intid” 是可用于MBI的范围内的第一个SPI,而 “span” 是该范围的大小。可以提供多个范围。需要设置 “msi-controller”。
-
mbi-alias:地址属性。仅包含{SET,CLR}SPI寄存器的GICD区域的别名的基本地址,如果需要隔离,并且如果硬件支持。
1.4.3 子节点
PI亲和性可以表示为单个 “ppi-partitions” 节点,包含一组子节点,每个子节点具有以下属性:
- affinity:应为CPU节点的phandle列表(如Documentation/devicetree/bindings/arm/cpus.txt中所述)。
GICv3有一个或多个中断翻译服务(ITS),用于将消息信号中断(MSI)路由到CPU。
这些节点必须具有以下属性:
- compatible:至少应包含 “arm,gic-v3-its”。
- msi-controller:布尔属性。标识该节点为MSI控制器。
- #msi-cells:必须为<1>。单个msi单元格是将生成MSI的设备的DeviceID。
- reg:指定ITS寄存器的基本物理地址和大小。
可选属性:
- socio-next,synquacer-preits:(u32, u32)元组,描述未翻译的地址和pre-ITS窗口的大小。
主要GIC节点必须包含所有ITS节点的reg属性的适当#address-cells、#size-cells和ranges属性。
1.4.4 示例
gic: interrupt-controller@2cf00000 {compatible = "arm,gic-v3";#interrupt-cells = <3>;#address-cells = <2>;#size-cells = <2>;ranges;interrupt-controller;reg = <0x0 0x2f000000 0 0x10000>, // GICD<0x0 0x2f10000 0 0x20000000>, // GICR<0x0 0x2c00000 0 0x2000>, // ICC<0x0 0x2c010000 0 0x2000>, // GICH<0x0 0x2c020000 0 0x2000>; // GICVinterrupts = <1 9 4>;msi-controller;mbi-ranges = <256 128>;gic-its@2c200000 {compatible = "arm,gic-v3-its";msi-controller;#msi-cells = <1>;reg = <0x0 0x2c200000 0 0x20000>;};
};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 0x2d00000 0 0x800000>, // GICR 1: CPUs 0-31<0x0 0x2e00000 0 0x800000>; // GICR 2: CPUs 32-63<0x0 0x2c040000 0 0x2000>, // ICC<0x0 0x2c060000 0 0x2000>, // GICH<0x0 0x2c080000 0 0x2000>; // GICVinterrupts = <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>;};ppi-partitions {part0: interrupt-partition-0 {affinity = <&cpu0 &cpu2>;};part1: interrupt-partition-1 {affinity = <&cpu1 &cpu3>;};};
};
device@0 {reg = <0 0 0 4>;interrupts = <1 1 4 &part0>;
};
1.5 arm,nvic.txt内容解释
翻译如下:
ARM 嵌套向量中断控制器(ARM Nested Vectored Interrupt Controller,简称 NVIC)
:NVIC 提供了一个与基于 Cortex-M 处理器核心紧密耦合的中断控制器。不同 SoC(System on Chip)上实现的 NVIC 在中断数量和每个中断的优先级位数上有所不同。
1.5.1 主要节点所需属性
-
compatible:应为以下之一:
- “arm,v6m-nvic”
- “arm,v7m-nvic”
- “arm,v8m-nvic”
-
interrupt-controller:标识该节点为中断控制器。
-
#interrupt-cells:指定编码一个中断源所需的单元格数。类型应为
<u32>
,且值应为 2。第 1 个单元包含中断类型的中断号。
第 2 个单元是中断的优先级。
-
reg:指定 NVIC 寄存器的基本物理地址和大小。这是一个固定地址(0xe000e100)和大小(0xc00)。
-
arm,num-irq-priority-bits:给定 SoC 实现的优先级位数。
1.5.2 示例
示例如下:
intc: interrupt-controller@e000e100 {compatible = "arm,v7m-nvic";#interrupt-cells = <2>;#address-cells = <1>;interrupt-controller;reg = <0xe000e100 0xc00>;arm,num-irq-priority-bits = <4>;
};
为了补充理解野火教程里面的中断例子,补充2个文档翻译!
1.6 st,stm32-exti.txt内容解释
翻译如下:
STM32 外部中断控制器(STM32 External Interrupt Controller)
1.6.1 必需属性
- compatible:应为以下之一:
- “st,stm32-exti”
- “st,stm32h7-exti”
- “st,stm32mp1-exti”
- reg:指定寄存器的基本物理地址和大小。
- interrupt-controller:标识该节点为中断控制器。
- #interrupt-cells:指定编码一个中断标识符所需的单元格数,应为 2。
- interrupts:中断引用主中断控制器(仅当 exti 控制器在同一父中断下有多个 exti 时需要:如 “st,stm32-exti” 和 “st,stm32h7-exti”)
1.6.2 示例
示例如下:
exti: interrupt-controller@40013c00 {compatible = "st,stm32-exti";interrupt-controller;#interrupt-cells = <2>;reg = <0x40013C00 0x400>;interrupts = <1>, <2>, <3>, <6>, <7>, <8>, <9>, <10>, <23>, <40>, <41>, <42>, <76>;
};
1.7 st,sti-irq-syscfg.txt内容解释
翻译如下:
STMicroelectronics STi 系统配置控制的 IRQ
:在基于 STi 的系统中;外部、CTI(Core Sight)、PMU(性能管理)和 PL310 缓存 IRQ 通过系统配置寄存器进行控制。
此驱动程序用于在使用前解除这些 IRQ 的屏蔽。
1.7.1 必需属性
- compatible:应设置为以下之一:
- “st,stih415-irq-sys-cfg”
- “st,stih416-irq-sys-cfg”
- “st,stih407-irq-sys-cfg”
- “st,stid127-irq-sys-cfg”
- st,syscfg:指向 Cortex-A9 IRQ 系统配置寄存器的 Phandle。
- st,irq-device:要启用的 IRQ 数组 - 应为长度为 2 的数组。
- st,fiq-device:要启用的 FIQ 数组 - 应为长度为 2 的数组。
1.7.2 可选属性
- st,invert-ext:外部 IRQ 可以按意愿反转。此属性使用按位逻辑反转这些 IRQ。为了方便,已提供一些定义的数值:
- ST_IRQ_SYSCFG_EXT_1_INV
- ST_IRQ_SYSCFG_EXT_2_INV
- ST_IRQ_SYSCFG_EXT_3_INV
1.7.3 示例
示例如下:
irq-syscfg {compatible = "st,stih416-irq-sys-cfg";st,syscfg = <&syscfg_cpu>;st,irq-device = <ST_IRQ_SYSCFG_PMU_0>,<ST_IRQ_SYSCFG_PMU_1>;st,fiq-device = <ST_IRQ_SYSCFG_DISABLED>,<ST_IRQ_SYSCFG_DISABLED>;st,invert-ext = <(ST_IRQ_SYSCFG_EXT_1_INV | ST_IRQ_SYSCFG_EXT_3_INV)>;
};
二、中断移植(卡在设备树中断触发类型的赋值)
2.1 前情概述——不支持gpio_to_irq函数
紧接着对《嵌入式系统内核镜像相关(十二)》的七
进行移植。
由于璞致的开发板不支持让我使用基于GPIO
引脚的MIO-LED
灯,因此按照原计划,就无法使用gpio_to_irq
函数,如下:
2.2 irq_of_parse_and_map函数
因此,只能另寻他法!
其中
unsigned int irq_of_parse_and_map(struct device_node *dev, int index);
可以代替gpio_to_irq
函数实现中断信息获取!
但麻烦的是,需要在设备树中注册interrupts
属性以及相关的信息!
下图来自Alinx
的中断开发教程:
下图来自野火
的中断子系统开发教程:
2.3 interrupts和interrupt-parent的格式以及编号占用情况
很显然,我们需要了解设备树中的已有中断相关的写法,最直接的资料就是./components/plnx_workspace/device-tree/device-tree/zynq-7000.dtsi
,内容如下:
// SPDX-License-Identifier: GPL-2.0+
/** Copyright (C) 2011 - 2015 Xilinx** This software is licensed under the terms of the GNU General Public* License version 2, as published by the Free Software Foundation, and* may be copied, distributed, and modified under those terms.** This program is distributed in the hope that it will be useful,* but WITHOUT ANY WARRANTY; without even the implied warranty of* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the* GNU General Public License for more details.*// {#address-cells = <1>;#size-cells = <1>;compatible = "xlnx,zynq-7000";cpus {#address-cells = <1>;#size-cells = <0>;cpu0: cpu@0 {compatible = "arm,cortex-a9";device_type = "cpu";reg = <0>;clocks = <&clkc 3>;clock-latency = <1000>;cpu0-supply = <®ulator_vccpint>;operating-points = </* kHz uV */666667 1000000333334 1000000>;};cpu1: cpu@1 {compatible = "arm,cortex-a9";device_type = "cpu";reg = <1>;clocks = <&clkc 3>;};};fpga_full: fpga-full {compatible = "fpga-region";fpga-mgr = <&devcfg>;#address-cells = <1>;#size-cells = <1>;ranges;};pmu@f8891000 {compatible = "arm,cortex-a9-pmu";interrupts = <0 5 4>, <0 6 4>;interrupt-parent = <&intc>;reg = < 0xf8891000 0x1000 0xf8893000 0x1000 >;};regulator_vccpint: fixedregulator {compatible = "regulator-fixed";regulator-name = "VCCPINT";regulator-min-microvolt = <1000000>;regulator-max-microvolt = <1000000>;regulator-boot-on;regulator-always-on;};amba: amba {u-boot,dm-pre-reloc;compatible = "simple-bus";#address-cells = <1>;#size-cells = <1>;interrupt-parent = <&intc>;ranges;adc: adc@f8007100 {compatible = "xlnx,zynq-xadc-1.00.a";reg = <0xf8007100 0x20>;interrupts = <0 7 4>;interrupt-parent = <&intc>;clocks = <&clkc 12>;};can0: can@e0008000 {compatible = "xlnx,zynq-can-1.0";status = "disabled";clocks = <&clkc 19>, <&clkc 36>;clock-names = "can_clk", "pclk";reg = <0xe0008000 0x1000>;interrupts = <0 28 4>;interrupt-parent = <&intc>;tx-fifo-depth = <0x40>;rx-fifo-depth = <0x40>;};can1: can@e0009000 {compatible = "xlnx,zynq-can-1.0";status = "disabled";clocks = <&clkc 20>, <&clkc 37>;clock-names = "can_clk", "pclk";reg = <0xe0009000 0x1000>;interrupts = <0 51 4>;interrupt-parent = <&intc>;tx-fifo-depth = <0x40>;rx-fifo-depth = <0x40>;};gpio0: gpio@e000a000 {compatible = "xlnx,zynq-gpio-1.0";#gpio-cells = <2>;clocks = <&clkc 42>;gpio-controller;interrupt-controller;#interrupt-cells = <2>;interrupt-parent = <&intc>;interrupts = <0 20 4>;reg = <0xe000a000 0x1000>;};i2c0: i2c@e0004000 {compatible = "cdns,i2c-r1p10";status = "disabled";clocks = <&clkc 38>;interrupt-parent = <&intc>;interrupts = <0 25 4>;reg = <0xe0004000 0x1000>;#address-cells = <1>;#size-cells = <0>;};i2c1: i2c@e0005000 {compatible = "cdns,i2c-r1p10";status = "disabled";clocks = <&clkc 39>;interrupt-parent = <&intc>;interrupts = <0 48 4>;reg = <0xe0005000 0x1000>;#address-cells = <1>;#size-cells = <0>;};intc: interrupt-controller@f8f01000 {compatible = "arm,cortex-a9-gic";#interrupt-cells = <3>;interrupt-controller;reg = <0xF8F01000 0x1000>,<0xF8F00100 0x100>;};L2: cache-controller@f8f02000 {compatible = "arm,pl310-cache";reg = <0xF8F02000 0x1000>;interrupts = <0 2 4>;arm,data-latency = <3 2 2>;arm,tag-latency = <2 2 2>;cache-unified;cache-level = <2>;};mc: memory-controller@f8006000 {compatible = "xlnx,zynq-ddrc-a05";reg = <0xf8006000 0x1000>;};ocmc: ocmc@f800c000 {compatible = "xlnx,zynq-ocmc-1.0";interrupt-parent = <&intc>;interrupts = <0 3 4>;reg = <0xf800c000 0x1000>;};uart0: serial@e0000000 {compatible = "xlnx,xuartps", "cdns,uart-r1p8";status = "disabled";clocks = <&clkc 23>, <&clkc 40>;clock-names = "uart_clk", "pclk";reg = <0xE0000000 0x1000>;interrupts = <0 27 4>;};uart1: serial@e0001000 {compatible = "xlnx,xuartps", "cdns,uart-r1p8";status = "disabled";clocks = <&clkc 24>, <&clkc 41>;clock-names = "uart_clk", "pclk";reg = <0xE0001000 0x1000>;interrupts = <0 50 4>;};spi0: spi@e0006000 {compatible = "xlnx,zynq-spi-r1p6";reg = <0xe0006000 0x1000>;status = "disabled";interrupt-parent = <&intc>;interrupts = <0 26 4>;clocks = <&clkc 25>, <&clkc 34>;clock-names = "ref_clk", "pclk";#address-cells = <1>;#size-cells = <0>;};spi1: spi@e0007000 {compatible = "xlnx,zynq-spi-r1p6";reg = <0xe0007000 0x1000>;status = "disabled";interrupt-parent = <&intc>;interrupts = <0 49 4>;clocks = <&clkc 26>, <&clkc 35>;clock-names = "ref_clk", "pclk";#address-cells = <1>;#size-cells = <0>;};qspi: spi@e000d000 {clock-names = "ref_clk", "pclk";clocks = <&clkc 10>, <&clkc 43>;compatible = "xlnx,zynq-qspi-1.0";status = "disabled";interrupt-parent = <&intc>;interrupts = <0 19 4>;reg = <0xe000d000 0x1000>;#address-cells = <1>;#size-cells = <0>;};smcc: memory-controller@e000e000 {#address-cells = <1>;#size-cells = <1>;status = "disabled";clock-names = "memclk", "apb_pclk";clocks = <&clkc 11>, <&clkc 44>;compatible = "arm,pl353-smc-r2p1", "arm,primecell";interrupt-parent = <&intc>;interrupts = <0 18 4>;ranges ;reg = <0xe000e000 0x1000>;nand0: flash@e1000000 {status = "disabled";compatible = "arm,pl353-nand-r2p1";reg = <0xe1000000 0x1000000>;#address-cells = <0x1>;#size-cells = <0x1>;};nor0: flash@e2000000 {status = "disabled";compatible = "cfi-flash";reg = <0xe2000000 0x2000000>;#address-cells = <1>;#size-cells = <1>;};};gem0: ethernet@e000b000 {compatible = "cdns,zynq-gem", "cdns,gem";reg = <0xe000b000 0x1000>;status = "disabled";interrupts = <0 22 4>;clocks = <&clkc 30>, <&clkc 30>, <&clkc 13>;clock-names = "pclk", "hclk", "tx_clk";#address-cells = <1>;#size-cells = <0>;};gem1: ethernet@e000c000 {compatible = "cdns,zynq-gem", "cdns,gem";reg = <0xe000c000 0x1000>;status = "disabled";interrupts = <0 45 4>;clocks = <&clkc 31>, <&clkc 31>, <&clkc 14>;clock-names = "pclk", "hclk", "tx_clk";#address-cells = <1>;#size-cells = <0>;};sdhci0: mmc@e0100000 {compatible = "arasan,sdhci-8.9a";status = "disabled";clock-names = "clk_xin", "clk_ahb";clocks = <&clkc 21>, <&clkc 32>;interrupt-parent = <&intc>;interrupts = <0 24 4>;reg = <0xe0100000 0x1000>;};sdhci1: mmc@e0101000 {compatible = "arasan,sdhci-8.9a";status = "disabled";clock-names = "clk_xin", "clk_ahb";clocks = <&clkc 22>, <&clkc 33>;interrupt-parent = <&intc>;interrupts = <0 47 4>;reg = <0xe0101000 0x1000>;};slcr: slcr@f8000000 {u-boot,dm-pre-reloc;#address-cells = <1>;#size-cells = <1>;compatible = "xlnx,zynq-slcr", "syscon", "simple-mfd";reg = <0xF8000000 0x1000>;ranges;clkc: clkc@100 {u-boot,dm-pre-reloc;#clock-cells = <1>;compatible = "xlnx,ps7-clkc";fclk-enable = <0xf>;clock-output-names = "armpll", "ddrpll", "iopll", "cpu_6or4x","cpu_3or2x", "cpu_2x", "cpu_1x", "ddr2x", "ddr3x","dci", "lqspi", "smc", "pcap", "gem0", "gem1","fclk0", "fclk1", "fclk2", "fclk3", "can0", "can1","sdio0", "sdio1", "uart0", "uart1", "spi0", "spi1","dma", "usb0_aper", "usb1_aper", "gem0_aper","gem1_aper", "sdio0_aper", "sdio1_aper","spi0_aper", "spi1_aper", "can0_aper", "can1_aper","i2c0_aper", "i2c1_aper", "uart0_aper", "uart1_aper","gpio_aper", "lqspi_aper", "smc_aper", "swdt","dbg_trc", "dbg_apb";reg = <0x100 0x100>;};rstc: rstc@200 {compatible = "xlnx,zynq-reset";reg = <0x200 0x48>;#reset-cells = <1>;syscon = <&slcr>;};pinctrl0: pinctrl@700 {compatible = "xlnx,pinctrl-zynq";reg = <0x700 0x200>;syscon = <&slcr>;};};dmac_s: dmac@f8003000 {compatible = "arm,pl330", "arm,primecell";reg = <0xf8003000 0x1000>;interrupt-parent = <&intc>;interrupt-names = "abort", "dma0", "dma1", "dma2", "dma3","dma4", "dma5", "dma6", "dma7";interrupts = <0 13 4>,<0 14 4>, <0 15 4>,<0 16 4>, <0 17 4>,<0 40 4>, <0 41 4>,<0 42 4>, <0 43 4>;#dma-cells = <1>;#dma-channels = <8>;#dma-requests = <4>;clocks = <&clkc 27>;clock-names = "apb_pclk";};devcfg: devcfg@f8007000 {compatible = "xlnx,zynq-devcfg-1.0";interrupt-parent = <&intc>;interrupts = <0 8 4>;reg = <0xf8007000 0x100>;clocks = <&clkc 12>, <&clkc 15>, <&clkc 16>, <&clkc 17>, <&clkc 18>;clock-names = "ref_clk", "fclk0", "fclk1", "fclk2", "fclk3";syscon = <&slcr>;};efuse: efuse@f800d000 {compatible = "xlnx,zynq-efuse";reg = <0xf800d000 0x20>;};global_timer: timer@f8f00200 {compatible = "arm,cortex-a9-global-timer";reg = <0xf8f00200 0x20>;interrupts = <1 11 0x301>;interrupt-parent = <&intc>;clocks = <&clkc 4>;};ttc0: timer@f8001000 {interrupt-parent = <&intc>;interrupts = <0 10 4>, <0 11 4>, <0 12 4>;compatible = "cdns,ttc";clocks = <&clkc 6>;reg = <0xF8001000 0x1000>;};ttc1: timer@f8002000 {interrupt-parent = <&intc>;interrupts = <0 37 4>, <0 38 4>, <0 39 4>;compatible = "cdns,ttc";clocks = <&clkc 6>;reg = <0xF8002000 0x1000>;};scutimer: timer@f8f00600 {interrupt-parent = <&intc>;interrupts = <1 13 0x301>;compatible = "arm,cortex-a9-twd-timer";reg = <0xf8f00600 0x20>;clocks = <&clkc 4>;};usb0: usb@e0002000 {compatible = "xlnx,zynq-usb-2.20a", "chipidea,usb2";status = "disabled";clocks = <&clkc 28>;interrupt-parent = <&intc>;interrupts = <0 21 4>;reg = <0xe0002000 0x1000>;phy_type = "ulpi";};usb1: usb@e0003000 {compatible = "xlnx,zynq-usb-2.20a", "chipidea,usb2";status = "disabled";clocks = <&clkc 29>;interrupt-parent = <&intc>;interrupts = <0 44 4>;reg = <0xe0003000 0x1000>;phy_type = "ulpi";};watchdog0: watchdog@f8005000 {clocks = <&clkc 45>;compatible = "cdns,wdt-r1p2";interrupt-parent = <&intc>;interrupts = <0 9 1>;reg = <0xf8005000 0x1000>;timeout-sec = <10>;};};
};
就中断控制器,zynq-7000.dtsi
而言,规定了中断控制器
与ARM Cortex-A9系列
的GIC
(通用中断控制器)兼容。如下:
其中#interrupt-cells = <3>;
指定中断源需要3个cells
来唯一标识。这3个cells
通常包含了中断类型、中断号和中断标志位掩码等信息。
其中reg = <0xF8F01000 0x1000>, <0xF8F00100 0x100>;
定义了中断控制器的寄存器映射。它包含了两个寄存器区域的物理地址和长度。第一个寄存器区域的基地址是 0xF8F01000,长度是 0x1000(即 4096 字节)。第二个寄存器区域的基地址是 0xF8F00100,长度是 0x100(即 256 字节)。
直接看下各个设备的中断信息,整理如下:
以下是提取的带有 interrupt-parent =
和 interrupts =
的行,并对应到所在的设备:
-
pmu@f8891000:
- interrupt-parent = <&intc>
- interrupts = <0 5 4>, <0 6 4>
-
adc@f8007100:
- interrupt-parent = <&intc>
- interrupts = <0 7 4>
-
can0@e0008000:
- interrupt-parent = <&intc>
- interrupts = <0 28 4>
-
can1@e0009000:
- interrupt-parent = <&intc>
- interrupts = <0 51 4>
-
gpio0@e000a000:
- interrupt-parent = <&intc>
- interrupts = <0 20 4>
-
i2c0@e0004000:
- interrupt-parent = <&intc>
- interrupts = <0 25 4>
-
i2c1@e0005000:
- interrupt-parent = <&intc>
- interrupts = <0 48 4>
-
intc@f8f01000:
- interrupt-parent = <&intc>
- interrupts = <1>
-
oc@f800c000:
- interrupt-parent = <&intc>
- interrupts = <0 3 4>
-
uart0@e0000000:
- interrupt-parent = <&intc>
- interrupts = <0 27 4>
-
uart1@e0001000:
- interrupt-parent = <&intc>
- interrupts = <0 50 4>
-
spi0@e0006000:
- interrupt-parent = <&intc>
- interrupts = <0 26 4>
-
spi1@e0007000:
- interrupt-parent = <&intc>
- interrupts = <0 49 4>
-
qspi@e000d000:
- interrupt-parent = <&intc>
- interrupts = <0 19 4>
-
mmc@e0100000:
- interrupt-parent = <&intc>
- interrupts = <0 24 4>
-
mmc@e0101000:
- interrupt-parent = <&intc>
- interrupts = <0 47 4>
-
dmac@f8003000:
- interrupt-parent = <&intc>
- interrupts = <0 13 4>, <0 14 4>, <0 15 4>, <0 16 4>, <0 17 4>, <0 40 4>, <0 41 4>, <0 42 4>, <0 43 4>
-
devcfg@f8007000:
- interrupt-parent = <&intc>
- interrupts = <0 8 4>
-
efuse@f800d000:
- interrupt-parent = <&intc>
- interrupts = <0 3 4>
-
timer@f8001000:
- interrupt-parent = <&intc>
- interrupts = <0 10 4>, <0 11 4>, <0 12 4>
-
timer@f8002000:
- interrupt-parent = <&intc>
- interrupts = <0 37 4>, <0 38 4>, <0 39 4>
-
timer@f8f00600:
- interrupt-parent = <&intc>
- interrupts = <1 13 0x301>
-
usb@e0002000:
- interrupt-parent = <&intc>
- interrupts = <0 21 4>
-
usb@e0003000:
- interrupt-parent = <&intc>
- interrupts = <0 44 4>
-
watchdog@f8005000:
- interrupt-parent = <&intc>
- interrupts = <0 9 1>
可以确定的是,如下SPI
中断编号已经被占用:
5/6 @ pmu
7 @ adc
28 @ can0
51 @ can1
20 @ gpio0
25 @ i2c0
48 @ i2c1
2 @ L2C
3 @ ocmc
27 @ uart0
50 @ uart1
26 @ spi0
49 @ spi1
19 @ qspi
18 @ smcc
22 @ gem0
45 @ gem1
24 @ sdhci0
47 @ sdhci1
13/14/15/16/17/40/41/42/43 @ dmac
8 @ devcfg
11 @ global_timer
10/11/12 @ ttc0 (timer)
37/38/39 @ ttc1 (timer)
13 @ scutimer
21 @ usb0
44 @ usb1
9 @ watchdog0
但是问题又来了,以上是明面上被占用的中断编号,我们还不清楚规则之下不允许被占用的中断编号!
因此需要回到手册,仔细琢磨琢磨!
2.4 UG585中中断一章的翻译
回到UG585
手册,和中断相关的页数不多,直接翻译文字了!
随后按照环境
、功能描述
、寄存器概述
和编程模型
依次展开!
2.4.1 环境
本章描述了系统级中断环境和中断控制器的功能(参见Figure 7-1)。PS基于Arm架构,利用两个Cortex-A9处理器(CPUs)和GIC pl390中断控制器。单核设备包含一个Cortex-A9处理器(CPU),双核设备包含两个。本章讨论双核配置。
中断结构与CPUs密切相关,并接受来自I/O外设(IOP)和可编程逻辑(PL)的中断。本章包括以下关键主题:
- 私有、共享和软件中断
- GIC功能
- 中断优先级和处理
私有、共享和软件中断
每个CPU都有一组私有外设中断(PPIs),使用分页寄存器进行私有访问。PPIs包括全局定时器、私有看门狗定时器、私有定时器和来自PL的FIQ/IRQ。软件生成的中断(SGIs)被路由到一个或两个CPUs。SGIs通过向通用中断控制器(GIC)的寄存器写入生成,参见Register Overview部分。共享外设中断(SPIs)由PS和PL中的各种I/O和内存控制器生成。它们被路由到一个或两个CPUs。来自PS外设的SPI中断也被路由到PL。
Generic Interrupt Controller (GIC)
通用中断控制器(GIC)是管理从PS和PL发送到CPUs的中断的集中资源。控制器启用、禁用、屏蔽和优先级排序中断源,并以编程方式将它们发送到选定的CPU(或CPUs),当CPU接口接受下一个中断时。此外,控制器支持安全扩展以实现安全感知系统。
该控制器基于Arm通用中断控制器架构版本1.0(GIC v1),非矢量。
寄存器通过CPU私有总线访问,以快速读写响应,避免互连中的临时阻塞或其他瓶颈。
中断分发器在将具有最高优先级的中断分派给各个CPUs之前集中所有中断源。GIC确保针对多个CPUs的中断只能由一个CPU一次处理。所有中断源都由唯一的中断ID号标识。所有中断源都有自己的可配置优先级和目标CPUs列表。
Resets and Clocks
中断控制器由重置子系统通过向SLCR中的A9_CPU_RST_CTRL寄存器的PERI_RST位写入来重置。相同的重置信号还重置CPU私有定时器和私有看门狗定时器(AWDT)。在重置时,所有挂起或正在服务的中断都被忽略。
中断控制器使用CPU_3x2x时钟(CPU频率的一半)运行。
Block Diagram
共享外设中断由包括PS中的I/O外设和PL中的逻辑的各种子系统生成。中断源在Figure 7-2中说明。
CPU Interrupt Signal Pass-through
来自PL的IRQ/FIQ可以通过GIC作为PPI#4和#1路由,或使用Figure 7-3中显示的直通多路复用器绕过GIC。此逻辑为两个CPUs实例化。直通模式通过mpcore.ICCICR寄存器启用,参见Table 7-1。
2.4.2 功能描述
软件生成中断(SGI)
每个CPU可以使用软件生成中断(SGI)中断自身、另一个CPU或两个CPUs。有16个软件生成中断(参见Table 7-2)。通过向ICDSGIR寄存器写入SGI中断号并指定目标CPU(s)生成SGI。此写入通过CPU的私有总线进行。每个CPU都有自己的一组SGI寄存器,用于生成一个或多个16个软件生成中断。通过读取ICCIAR(中断确认)寄存器或向ICDICPR(中断清除-挂起)寄存器的相应位写入1来清除中断。
所有SGIs都是边缘触发的。SGIs的灵敏度类型是固定的,不能更改;ICDICFR0寄存器是只读的,因为它指定了所有16个SGIs的灵敏度类型。
CPU私有外设中断(PPI)
每个CPU连接到一组私有的五个外设中断。PPIs列在Table 7-3中。
PPIs的灵敏度类型是固定的,不能更改;因此,ICDICFR1寄存器是只读的,因为它指定了所有5个PPIs的灵敏度类型。请注意,来自PL的快速中断(FIQ)信号和中断(IRQ)信号被反转然后发送到中断控制器。因此,它们在PS-PL接口处为高电平有效,尽管ICDICFR1寄存器将它们反映为低电平有效。
共享外设中断(SPI)
一组大约60个来自各种模块的中断可以被路由到一个或两个CPUs或PL。中断控制器管理这些中断的优先级和接收。
除了IRQ #61到#68和#84到#91,所有中断灵敏度类型都由请求源固定,不能更改。GIC必须编程以适应这些灵敏度类型。
对于电平灵敏度类型的中断,请求源必须提供一种机制,以便中断处理程序在中断被确认后清除中断。此要求适用于任何IRQF2P[n](来自PL)具有高电平灵敏度类型。
对于上升沿灵敏度类型的中断,请求源必须提供足够宽的脉冲以便GIC捕获。这通常至少为2个CPU_2x3x周期。此要求适用于任何IRQF2P[n](来自PL)具有上升沿灵敏度类型。
ICDICFR2到ICDICFR5寄存器配置所有SPIs的中断类型。每个中断都有一个2位字段,指定灵敏度类型和处理模型。
SPI中断列在Table 7-4中。
中断灵敏度、目标和处理
进入GIC的中断有三种类型,如本节中所述:SPI、PPI和SGI。一般来说,中断信号包括灵敏度设置,无论一个或两个CPUs处理中断,以及目标哪个CPU或CPUs:零、一个或两个。然而,大多数中断信号的功能包括固定设置,而其他是部分可编程的。
有两组控制寄存器用于灵敏度、处理和目标:
- mpcore.ICDICFR[5:0]寄存器:灵敏度和处理。参见Figure 7-4。
- mpcore.ICDIPTR[23:0]寄存器:目标CPU(s)。参见Figure 7-5。
共享外设中断(SPI)
SPI中断可以目标为任意数量的CPUs,但只有一个CPU处理中断。如果中断目标为两个CPUs并且它们同时响应GIC,则MPcore确保只有一个CPU读取活动中断ID#。另一个CPU接收Spurious ID# 1023中断或下一个挂起中断,具体取决于时间。这消除了中断服务程序中锁定的要求。通过ICDIPTR [23:8]寄存器完成对CPU的目标。每个SPI中断的灵敏度必须编程以匹配Table 7-4、PS和PL共享外设中断(SPI)中列出的灵敏度。灵敏度使用ICDICFR [5:2]寄存器进行编程。
私有外设中断(PPI)
每个CPU都有自己的一组私有PPI中断,具有固定功能;这些中断的灵敏度、处理和目标不可编程。每个中断只转到其自己的CPU并由该CPU处理。ICDICFR [1]寄存器是只读的,ICDIPTR [5:2]寄存器基本上保留。
软件生成中断(SGI)
SGI中断始终是边缘敏感的,当软件向ICDSGIR寄存器写入中断号时生成。所有在ICDIPTR [23:8]中定义的目标CPUs必须处理中断以清除它。参见Figure 7-4和Figure 7-5。
等待中断事件信号(WFI)
CPU可以进入等待状态,在该状态中它等待中断(或事件)信号生成。发送到PL的等待中断信号在Application Processing Unit中描述。
2.4.3 寄存器概述
ICC
和ICD
寄存器是pl390 GIC寄存器集的一部分。有60个SPI中断。这远远少于pl390可以支持的数量,因此在ICD
中的中断使能、状态、优先级和处理器目标寄存器比pl390可能的要少得多。ICC
和ICD
寄存器的摘要列在Table 7-5中。
写保护锁
中断控制器提供了防止对关键配置寄存器进行写访问的功能。这是通过向APU_CTRL[CFGSDDISABLE]
位写入1来完成的。
APU_CTRL
寄存器是SoC的系统级控制寄存器集SLCR
的一部分。它控制安全中断控制寄存器的写行为。
推荐:如果用户想要设置CFGSDDISABLE
位,建议在软件配置中断控制器寄存器后的用户软件启动过程中进行。CFGSDDISABLE
位只能通过上电复位(POR)清除。在设置CFGSDDISABLE
位后,它将受保护的寄存器位更改为只读,因此这些安全中断的行为不能更改,即使在安全域中执行的恶意代码存在。
2.4.4 编程模型
中断优先级
所有中断请求(PPI、SGI和SPI)都被分配一个唯一的ID号。控制器使用ID号进行仲裁。中断分发器保存每个CPU的挂起中断列表,然后在将其发送到CPU接口之前选择最高优先级的中断。优先级相同的中断通过选择最低ID来解决。
优先级逻辑是物理复制的,以实现同时为每个CPU选择最高优先级中断。中断分发器保存中断、处理器和激活信息的中央列表,并负责触发软件中断到CPUs。
SGI和PPI分发器寄存器是分组的,为每个连接的处理器提供单独的副本。硬件确保针对多个CPUs的中断一次只能由一个CPU处理。
中断分发器将最高挂起中断传输到CPU接口。它接收中断已被确认的信息,然后可以更改相应中断的状态。只有确认中断的CPU才能结束该中断。
中断处理
当IRQ线断言时,GIC对挂起中断的响应在Arm文档:IHI0048B_gic_architecture_specification.pdf
(参见附录B,附加资源和法律声明)中描述。有关更多信息,请参阅第1.4.2节中的注释以及第3.2.4节中的附加信息。
如果中断在GIC中挂起并且IRQ被取消断言,则中断在GIC中变为非活动状态(CPU从未看到它)。
如果中断在GIC中处于活动状态(因为CPU接口已确认中断),那么软件ISR通过首先检查GIC寄存器,然后轮询I/O外设中断状态寄存器来确定原因。
Arm编程主题
Arm GIC架构规范包括以下编程主题:
- GIC寄存器访问
- 分发器和CPU接口
- GIC安全扩展的影响
- PU接口寄存器
- 保存和恢复控制器状态
传统中断和安全扩展
当使用传统中断(IRQ、FIQ)时,如果中断处理程序以安全模式访问IRQ和FIQ(通过ICCICR[AckCtl]=1),则在读取中断ID时偶尔会发生竞态条件。在IRQ处理程序中也有看到FIQ ID的风险,因为GIC只知道处理程序正在读取的安全状态,而不知道是哪种类型的处理程序。
有两种可行的解决方案:
- 仅将IRQ信号发送到重新进入的IRQ处理程序,并在GIC中使用抢占功能。
- 使用FIQ和IRQ,ICCICR[AckCtl]=0,并使用TLB表以非安全模式处理IRQ,并以安全模式处理FIQ。
2.5 回到实验部分
如下SPI
中断编号已经被占用,对比表7-4
:
5/6 @ pmu 在表7-4中 中断编号为 37/38
7 @ adc 在表7-4中 中断编号为 39
28 @ can0 在表7-4中 中断编号为 60
51 @ can1 在表7-4中 中断编号为 83
20 @ gpio0 在表7-4中 中断编号为 52
25 @ i2c0 在表7-4中 中断编号为 57
48 @ i2c1 在表7-4中 中断编号为 80
2 @ L2C 在表7-4中 中断编号为 34
3 @ ocmc 在表7-4中 中断编号为 35
27 @ uart0 在表7-4中 中断编号为 59
50 @ uart1 在表7-4中 中断编号为 82
26 @ spi0 在表7-4中 中断编号为 58
49 @ spi1 在表7-4中 中断编号为 81
19 @ qspi 在表7-4中 中断编号为 51
18 @ smcc 在表7-4中 中断编号为 50
22 @ gem0 在表7-4中 中断编号为 54
45 @ gem1 在表7-4中 中断编号为 77
24 @ sdhci0 在表7-4中 中断编号为 56
47 @ sdhci1 在表7-4中 中断编号为 79
13/14/15/16/17/40/41/42/43 @ dmac 在表7-4中 中断编号为 45/46/47/48/49/72/73/74/75
8 @ devcfg 在表7-4中 中断编号为 40
11 @ global_timer 在表7-4中 中断编号为 43
10/11/12 @ ttc0 (timer) 在表7-4中 中断编号为 42/43/44
37/38/39 @ ttc1 (timer) 在表7-4中 中断编号为 69/70/71
21 @ usb0 在表7-4中 中断编号为 53
44 @ usb1 在表7-4中 中断编号为 76
9 @ watchdog0 在表7-4中 中断编号为 41 (SWDT)
因此从表7-4
中可以获取到的保留位有:
4 @ reserved 在表7-4中 中断编号为 36
61/62/63 @ reserved 在表7-4中 中断编号为 93/94/95
另外从设备树模板上来说,在指定好中断控制器后,我需要做的是继承中断控制器并指定中断编号和触发方式!如下:
interrupt-parent = <&intc>;interrupts = <x y z>; // 其中y可以是36、93、94、95,z是触发方式
然后看看interrupts
的要求:
-
#interrupt-cells:指定编码一个中断源所需的单元格数。类型应为
<u32>
,值应为 3。第 1 个单元是中断类型;0 表示 SPI 中断,1 表示 PI 中断。
第 2 个单元包含中断类型的中断号。
SPI 中断在范围 [0-987] 内。PI 中断在范围 [0-15] 内。第 3 个单元是标志位,编码如下:
- bits[3:0] 触发类型和电平标志。
- 1 = 低到高边沿触发
- 2 = 高到低边沿触发(SPIs 无效)
- 4 = 高电平电平敏感
- 8 = 低电平电平敏感(SPIs 无效)。
- bits[3:0] 触发类型和电平标志。
因此interrupts = <x y z>;
中的x=0
表示SPI
中断,y
同注释,但是z
就出问题了!
三处reserved
均没有指定触发类型
。
因此对于z
的赋值显得十分为难!所以没法儿再继续下去了!
但也不是没有收获!最起码中断的用户手册刷了一遍!
先把这个问题记录一下吧,以便于后续回头阅读!
如上对中断移植思路的分析,Alinx
中基于中断的阻塞IO
、非阻塞IO
和异步IO
都没法儿接着做了!不过基于Alinx
的这些教程,还是学到了知识!
驱动开发的移植暂时先告一个段落吧!
放一下后续可以参考的驱动开发代码:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/cdev.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/of_irq.h>
#include <linux/irq.h>
#include <linux/uaccess.h>
#include <asm/uaccess.h>
#include <asm/mach/map.h>
#include <asm/io.h>/* 设备节点名称 */
#define DEVICE_NAME "gpio_leds_our8"
/* 设备号个数 */
#define DEVID_COUNT 1
/* 驱动个数 */
#define DRIVE_COUNT 1
/* 主设备号 */
#define MAJOR_U
/* 次设备号 */
#define MINOR_U 0/* gpio 寄存器虚拟地址 */
static u32 *GPIO_DIRM_0;
/* gpio 使能寄存器 */
//static u32 *GPIO_OEN_0;
/* gpio 控制寄存器 */
static u32 *GPIO_DATA_0;
/* AMBA 外设时钟使能寄存器 */
//static u32 *APER_CLK_CTRL;/* 把驱动代码中会用到的数据打包进设备结构体 */
struct alinx_char_dev{dev_t devid; //设备号struct cdev cdev; //字符设备struct class *class; //类struct device *device; //设备struct device_node *nd; //设备树的设备节点
/** 并发处理 **/spinlock_t lock; //自旋锁变量
/** gpio **/int alinx_key_gpio; //gpio号int key_sts; //记录按键状态, 为1时被按下
/** 中断 **/unsigned int irq; //中断号
/** 定时器 **/struct timer_list timer; //定时器};/* 声明设备结构体 */
static struct alinx_char_dev alinx_char = {.cdev = {.owner = THIS_MODULE,},
};/* 中断服务函数 */
static irqreturn_t key_handler(int irq, void *dev)
{/* 按键按下或抬起时会进入中断 *//* 开启50毫秒的定时器用作防抖动 */mod_timer(&alinx_char.timer, jiffies + msecs_to_jiffies(50));return IRQ_RETVAL(IRQ_HANDLED);
}/* 定时器服务函数 */
void timer_function(struct timer_list *timer)
{unsigned long flags;/* 获取锁 */spin_lock_irqsave(&alinx_char.lock, flags);/* value用于获取按键值 */unsigned char value;/* 获取按键值 *///value = gpio_get_value(alinx_char.alinx_key_gpio);uint32_t reg_value = *GPIO_DATA_0;uint8_t bits_to_or = (reg_value >> 5) & 0xF;bits_to_or |= (1 << 3); // 对应于第9位bits_to_or |= (1 << 2); // 对应于第8位bits_to_or |= (1 << 1); // 对应于第7位bits_to_or |= (1 << 0); // 对应于第6位value = bits_to_or;if(value == 0){/* 按键按下, 状态置1 */alinx_char.key_sts = 1;}else{/* 按键抬起 */}/* 释放锁 */spin_unlock_irqrestore(&alinx_char.lock, flags);
}/* open 函数实现, 对应到 Linux 系统调用函数的 open 函数 */
static int gpio_leds_open(struct inode *inode_p, struct file *file_p)
{/* MIO_0 时钟使能 *///*APER_CLK_CTRL |= 0x00400000;/* MIO_0 设置成输出 */*GPIO_DIRM_0 |= 0x000001E0;/* MIO_0 使能 *///*GPIO_OEN_0 |= 0x0000000F;printk("gpio_test module open\n");return 0;
}/* write 函数实现, 对应到 Linux 系统调用函数的 write 函数 */
/*
static ssize_t gpio_leds_write(struct file *file_p, const char __user *buf, size_t len, loff_t *loff_t_p)
{int rst;char writeBuf[5] = {0};printk("gpio_test module write\n");rst = copy_from_user(writeBuf, buf, len);if(0 != rst){return -1;}if(1 != len){printk("gpio_test len err\n");return -2;}if(1 == writeBuf[0]){*GPIO_DATA_0 |= 0x0000000F;printk("gpio_test ON\n");}else if(0 == writeBuf[0]){*GPIO_DATA_0 &= 0xFFFFFFF0;printk("gpio_test OFF\n");}else{printk("gpio_test para err\n");return -3;}return 0;
}*//* read函数实现, 对应到Linux系统调用函数的write函数 */
static ssize_t gpio_leds_read(struct file *file_p, char __user *buf, size_t len, loff_t *loff_t_p)
{unsigned long flags;int ret;/* 获取锁 */spin_lock_irqsave(&alinx_char.lock, flags);/* keysts用于读取按键状态 *//* 返回按键状态值 */ret = copy_to_user(buf, &alinx_char.key_sts, sizeof(alinx_char.key_sts));/* 清除按键状态 */alinx_char.key_sts = 0;/* 释放锁 */spin_unlock_irqrestore(&alinx_char.lock, flags);return 0;
}/* release 函数实现, 对应到 Linux 系统调用函数的 close 函数 */
static int gpio_leds_release(struct inode *inode_p, struct file *file_p)
{printk("gpio_test module release\n");return 0;
}/* file_operations 结构体声明, 是上面 open、write 实现函数与系统调用函数对应的关键 */
static struct file_operations ax_char_fops = {.owner = THIS_MODULE,.open = gpio_leds_open,//.write = gpio_leds_write,.read = gpio_leds_read,.release = gpio_leds_release,
};/* 模块加载时会调用的函数 */
static int __init gpio_led_init(void)
{/* 用于接受返回值 */u32 ret = 0;/** 并发处理 **//* 初始化自旋锁 */spin_lock_init(&alinx_char.lock);/* 存放 reg 数据的数组 */u32 reg_data[10];/* 通过节点名称获取节点 */alinx_char.nd = of_find_node_by_name(NULL, "alinxkey");/* 4、获取 reg 属性内容 */ret = of_property_read_u32_array(alinx_char.nd, "reg", reg_data, 4);if(ret < 0){printk("get reg failed!\r\n");return -1;}else{/* do nothing */}/* 把需要修改的物理地址映射到虚拟地址 */GPIO_DIRM_0 = ioremap(reg_data[0], reg_data[1]);GPIO_DATA_0 = ioremap(reg_data[2], reg_data[3]);/** 中断 **//* 获取中断号 *///alinx_char.irq = gpio_to_irq(alinx_char.alinx_key_gpio);alinx_char.irq = irq_of_parse_and_map(alinx_char.nd, 1);if(alinx_char.irq < 0){printk("irq %d request failed\r\n", alinx_char.irq);return -EFAULT;} /* 申请中断 */ret = request_irq(alinx_char.irq,key_handler,IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,"alinxkey",NULL);if(ret < 0){printk("irq %d request failed\r\n", alinx_char.irq);return -EFAULT;}/** 定时器 **/__init_timer(&alinx_char.timer, timer_function, 0);/** 字符设备框架 **//* 注册设备号 */alloc_chrdev_region(&alinx_char.devid, MINOR_U, DEVID_COUNT, DEVICE_NAME);/* 初始化字符设备结构体 */cdev_init(&alinx_char.cdev, &ax_char_fops);/* 注册字符设备 */cdev_add(&alinx_char.cdev, alinx_char.devid, DRIVE_COUNT);/* 创建类 */alinx_char.class = class_create(THIS_MODULE, DEVICE_NAME);if(IS_ERR(alinx_char.class)){return PTR_ERR(alinx_char.class);}/* 创建设备节点 */alinx_char.device = device_create(alinx_char.class, NULL,alinx_char.devid, NULL, DEVICE_NAME);if (IS_ERR(alinx_char.device)){return PTR_ERR(alinx_char.device);}return 0;
}/* 卸载模块 */
static void __exit gpio_led_exit(void)
{/** 中断 **//* 释放中断 */free_irq(alinx_char.irq, NULL);/** 定时器 **//* 删除定时器 */del_timer_sync(&alinx_char.timer);/** 字符设备框架 **//* 注销字符设备 */cdev_del(&alinx_char.cdev);/* 注销设备号 */unregister_chrdev_region(alinx_char.devid, DEVID_COUNT);/* 删除设备节点 */device_destroy(alinx_char.class, alinx_char.devid);/* 删除类 */class_destroy(alinx_char.class);/* 释放对虚拟地址的占用 */iounmap(GPIO_DIRM_0);iounmap(GPIO_OEN_0);iounmap(GPIO_DATA_0);iounmap(APER_CLK_CTRL);printk("gpio_led_dev_exit_ok\n");
}/* 标记加载、卸载函数 */
module_init(gpio_led_init);
module_exit(gpio_led_exit);/* 驱动描述信息 */
MODULE_AUTHOR("Alinx");
MODULE_ALIAS("gpio_led");
MODULE_DESCRIPTION("DEVICE TREE GPIO LED driver");
MODULE_VERSION("v1.0");
MODULE_LICENSE("GPL");
以上还需要再修改!
设备树如下:
/include/ "system-conf.dtsi"/ { model ="ZYNQ7035"; compatible = "xlnx,zynq-7000"; usb_phy0: phy0@e0002000 {compatible = "ulpi-phy";#phy-cells = <0>;reg = <0xe0002000 0x1000>;view-port = <0x0170>;drv-vbus;};alinxled {compatible = "alinxled";reg = <0xE000A284 0x04 /* gpio 方向寄存器 */0xE000A288 0x04 /* gpio 使能寄存器 */0xE000A048 0x04 /* gpio 控制寄存器 */0xF800012C 0x04 /* AMBA 外设时钟使能寄存器 */>;};alinxkey {compatible = "alinxkey";reg = <0xE000A284 0x04 /* gpio 方向寄存器 */0xE000A048 0x04 /* gpio 控制寄存器 */0xF800012C 0x04 /* AMBA 外设时钟使能寄存器 */>;interrupt-parent = <&intc>;interrupts = <0 y z>; // 其中y可以是36、93、94、95};
};&usb0 {status = "okay";dr_mode = "host";usb-phy = <&usb_phy0>;
};&uart0 {
u-boot,dm-pre-reloc;
};&uart1 {
u-boot,dm-pre-reloc;
};
总结
本文总结了中断相关的设备树内容、UG585手册以及探索了基于中断的驱动存在的问题。