- interrupt-controller - 一个空属性,声明一个接收中断信号的设备节点
- #interrupt-cells - 这是中断控制器节点的一个属性。它声明中断控制器的 interrupt specifier(中断描述符)占用多少单元格(类似于#address-cells和#size-cells)。
- interrupt-parent - 一种包含指向中断控制器句柄指针的属性;如果没有该属性,节点也可以从其父节点继承该属性
- interrupts - 包含一系列的interrupt specifier的属性,每一个interrupt specifier表示设备发出的一个中断信号
/dts-v1/; / { compatible = "acme,coyotes-revenge"; #address-cells = <1>; #size-cells = <1>; interrupt-parent = <&intc>; cpus { #address-cells = <1>; #size-cells = <0>; cpu@0 { compatible = "arm,cortex-a9"; reg = <0>; }; cpu@1 { compatible = "arm,cortex-a9"; reg = <1>; }; }; serial@101f0000 { compatible = "arm,pl011"; reg = <0x101f0000 0x1000 >; interrupts = < 1 0 >; }; serial@101f2000 { compatible = "arm,pl011"; reg = <0x101f2000 0x1000 >; interrupts = < 2 0 >; }; gpio@101f3000 { compatible = "arm,pl061"; reg = <0x101f3000 0x1000 0x101f4000 0x0010>; interrupts = < 3 0 >; }; intc: interrupt-controller@10140000 { compatible = "arm,pl190"; reg = <0x10140000 0x1000 >; interrupt-controller; #interrupt-cells = <2>; }; spi@10115000 { compatible = "arm,pl022"; reg = <0x10115000 0x1000 >; interrupts = < 4 0 >; }; external-bus { #address-cells = <2> #size-cells = <1>; ranges = <0 0 0x10100000 0x10000 // Chipselect 1, Ethernet 1 0 0x10160000 0x10000 // Chipselect 2, i2c controller 2 0 0x30000000 0x1000000>; // Chipselect 3, NOR Flash ethernet@0,0 { compatible = "smc,smc91c111"; reg = <0 0 0x1000>; interrupts = < 5 2 >; }; i2c@1,0 { compatible = "acme,a1234-i2c-bus"; #address-cells = <1>; #size-cells = <0>; reg = <1 0 0x1000>; interrupts = < 6 2 >; rtc@58 { compatible = "maxim,ds1338"; reg = <58>; interrupts = < 7 3 >; }; }; flash@2,0 { compatible = "samsung,k8f1315ebm", "cfi-flash"; reg = <2 0 0x4000000>; }; }; };
需要注意的是:
- 本机只有一个中断控制器: interrupt-controller@10140000
- “intc”标签, 已添加到中断控制器节点,该标签用于为根节点中的“interrupt-parent”属性分配一个phandle。这个“interrupt-parent”属性成为系统的默认值,因为所有子节点都会继承它,除非显式地重写它。
- 每个设备使用属性“interrupts”来指定一条不同的中断信号输入线
- “ #interrupt-cells”属性等于2,所以每个interrupt specifier有2个cells, 本例使用了一种常见的模式,即使用第一个单元格对中断行号进行编码,使用第二个单元格一些flags进行编码。比如高有效、低有效或者边缘有效与敏感程度。对于任何给定的中断控制器,请参阅控制器的对应文档,以了解specifier是如何编码的。
aliases { ethernet0 = ð0; serial0 = &serial0; };在为设备分配标识符时,操作系统支持使用别名。 可以发现,在这里使用了一个新的语法。“属性= &label;”这个语法将标签引用的完整节点路径指定为字符串属性。这与文章前面出现的,phandle = < &label >不同(前面是把一个phandle插入到cell中); chosen 节点 chosen节点不代表真实的设备,而是用作在固件和操作系统之间传递数据的地方,比如boot参数。所选节点中的数据不代表硬件。通常,所选节点在.dts源文件中为空,并在启动过程中时填充。 在我们的示例系统中,固件可能会将以下内容添加到chosen节点:
chosen { bootargs = "root=/dev/nfs rw nfsroot=192.168.1.1 console=ttyS0,115200"; };7 高级主题 高级示例机器 之前,我们已经学习了一些基础的定义。现在让我们在示例机器里增加一些新的硬件,以便讨论一些更复杂的用例。高级的示例主板里增加了一个PCI主桥,其控制寄存器映射到地址空间0x10180000,BARS空间从地址0x80000000开始。 根据我们对设备树的了解,我们可以开始添加以下节点来描述PCI主机桥接。
pci@10180000 { compatible = "arm,versatile-pci-hostbridge", "pci"; reg = <0x10180000 0x1000>; interrupts = <8 0>; };
PCI Host Bridge
本节将描述 Host/PCI bridge node. 注意,理解本节需要有一些PCI的基本知识。但这不是一个关于PCI的教程,如果你需要更深入的信息,请阅读[1]。您还可以参考ePAPR v1.1,或者kernel的Documentation\devicetree\bindings目录(在那里可以找到飞思卡尔MPC5200的完整工作示例)PCI Bus numbering
每个PCI总线都被唯一编号,总线编号通过使用bus-range属性在PCI节点中公开,该属性包含两个单元。第一个单元给出分配给这个节点的总线号,第二个单元给出任何从属PCI总线的最大总线号。 下面的例子只有一个简单的PCI总线,所以两个单元格全是0.pci@0x10180000 { compatible = "arm,versatile-pci-hostbridge", "pci"; reg = <0x10180000 0x1000>; interrupts = <8 0>; bus-ranges = <0 0>; };
PCI Address Translation
与之前描述的本地总线类似,PCI地址空间与CPU的地址空间是完全分离的,所以需要有地址转换,将PCI地址空间转换到CPU的地址空间。像之前一样,转换的过程依赖于属性“ range,“”#address-cells,“和“#size-cells”。pci@0x10180000 { compatible = "arm,versatile-pci-hostbridge", "pci"; reg = <0x10180000 0x1000>; interrupts = <8 0>; bus-ranges = <0 0>; #address-cells = <3> #size-cells = <2>; ranges = <0x42000000 0 0x80000000 0x80000000 0 0x20000000 0x02000000 0 0xa0000000 0xa0000000 0 0x10000000 0x01000000 0 0x00000000 0xb0000000 0 0x01000000>; }红色的PCI地址占用3个单元格。PCI地址范围占用2个单元格。为什么地址占用三个单元格?这是与PCI的 phys.hi, phys.mid ,phys.low相对应。 * phys.hi cell: npt000ss bbbbbbbb dddddfff rrrrrrrr * phys.mid cell: hhhhhhhh hhhhhhhh hhhhhhhh hhhhhhhh * phys.low cell: llllllll llllllll llllllll llllllll PCI地址是64位宽,编码成为了三段:phys.hi, phys.mid ,phys.low。需要重点注意的是phys.hi地址段: * n: relocatable region flag (doesn't play a role here) * p: prefetchable (cacheable) region flag * t: aliased address flag (doesn't play a role here) * ss: space code * 00: configuration space * 01: I/O space * 10: 32 bit memory space * 11: 64 bit memory space * bbbbbbbb: The PCI bus number. PCI may be structured hierarchically. So we may have PCI/PCI bridges which will define sub busses. * ddddd: The device number, typically associated with IDSEL signal connections. * fff: The function number. Used for multifunction PCI devices. * rrrrrrrr: Register number; used for configuration cycles. 在PCI地址转换中,重要的字段是p和ss, phys.hi 中的p和ss字段决定了哪个地址PCI地址空间正在被访问。 看一下range属性,我们有三个区域: 从PCI地址0x80000000开始的32位可预取内存区域,大小为512 MByte,将映射到主机CPU上的地址0x80000000。 从PCI地址0xa0000000开始的32位不可预取内存区域,大小为256 MByte,将映射到主机CPU上的地址0xa0000000。 从PCI地址0x00000000开始的一个I/O区域,其大小为16 MByte,将映射到主机CPU上的地址0xb0000000。 比较麻烦的是,phys.hi 的存在意味着, 操作系统需要知道节点表示PCI桥接器,以便在转换时忽略不相关的字段。 操作系统将在pci总线节点中查找字符串“pci”,以确定是否需要屏蔽额外字段。
PCI DMA Address Translation
上述“ranges”属性定义了CPU如何查看PCI内存,并帮助CPU设置正确的内存窗口,并将正确的参数写入各种PCI设备寄存器。这有时称为“ outbound memory”。 地址转换的一个特殊情况涉及PCI主设备如何查看系统的核心内存。当PCI控制器充当主设备并独立访问系统的核心内存时,就会发生这种情况。由于这通常是与CPU不同的视图(与内存线如何连接有关),因此可能需要在初始化时将其编程到PCI主控制器中。 这被看作是一种DMA,因为PCI总线独立地执行直接内存访问,因此这些映射被命名为DMA -range。这种类型的内存映射有时称为“ inbound memory”,不属于PCI设备树规范的一部分。 在某些情况下,ROM (BIOS)或类似的引导程序将在引导时设置这些寄存器,但在其他情况下,PCI控制器完全未初始化,需要从设备树中读取信息,并完成这些转换。然后,PCI主机驱动程序通常将解析dma-ranges属性,并相应地在主机控制器中设置一些寄存器。 继续扩展上面的例子:pci@0x10180000 { compatible = "arm,versatile-pci-hostbridge", "pci"; reg = <0x10180000 0x1000>; interrupts = <8 0>; bus-ranges = <0 0>; #address-cells = <3> #size-cells = <2>; ranges = <0x42000000 0 0x80000000 0x80000000 0 0x20000000 0x02000000 0 0xa0000000 0xa0000000 0 0x10000000 0x01000000 0 0x00000000 0xb0000000 0 0x01000000 dma-ranges = <0x02000000 0 0x00000000 0x80000000 0 0x20000000>; };“dma-ranges”表示,从PCI主机控制器的角度看,从PCI-E地址0x00000000开始的512MB内存将出现在CPU core地址的0x80000000.你也能看到,“ss”地址被设置为0x02来表示它是一个32位的内存空间。
Advanced Interrupt Mapping
现在我们开始更有趣的部分,PCI中断映射。一个PCI设备可以触发中断,通过信号线#INTA,#INTB,#INTC,#INTD。在中断信号名字前面的“#”表示这个中断时低有效(这是一个常见的约定)。PCI的中断信号一直是低有效。 单功能设备必须使用#INTA来中断。多功能设备必须使用#INTA如果它使用一个中断引脚,#INTA和#INTB如果它使用两个中断引脚,等等。 由于这些规则,#INTA通常比#INTB、#INTC和#INTD被更多的函数使用。 为了将负载分配到支持#INTA到#INTD的四条IRQ线路上,PCI插槽或者设备通过rotate的方式连接到中断控制器的不同信号线上,以避免所有的#INTA都连接到相同的信号线上。 这个过程称为对中断进行swizzling。 因此,设备树需要一种方法将每个PCI中断信号映射到中断控制器的输入。“ #interrupt-cells”,“ interrupt-map”,“ interrupt-map-mask”属性被用来描述中断信号的映射。 实际上,这里描述的中断映射并不局限于PCI总线,任何节点都可以指定复杂的中断映射,但是到目前为止,PCI用例是最常见的。pci@0x10180000 { compatible = "arm,versatile-pci-hostbridge", "pci"; reg = <0x10180000 0x1000>; interrupts = <8 0>; bus-ranges = <0 0>; #address-cells = <3> #size-cells = <2>; ranges = <0x42000000 0 0x80000000 0x80000000 0 0x20000000 0x02000000 0 0xa0000000 0xa0000000 0 0x10000000 0x01000000 0 0x00000000 0xb0000000 0 0x01000000>; #interrupt-cells = <1>; interrupt-map-mask = <0xf800 0 0 7>; interrupt-map = <0xc000 0 0 1 &intc 9 3 // 1st slot 0xc000 0 0 2 &intc 10 3 0xc000 0 0 3 &intc 11 3 0xc000 0 0 4 &intc 12 3 0xc800 0 0 1 &intc 10 3 // 2nd slot 0xc800 0 0 2 &intc 11 3 0xc800 0 0 3 &intc 12 3 0xc800 0 0 4 &intc 9 3>; };
首先,你会注意到,PCI的中断号只占用1个单元格,不像系统其他的中断控制器使用2个单元格;一个对应中断信号线的序号,一个对应flag。PCI只需要一个单元格来表示中断,是因为PCI中断都是低有效。 在我们的示例板卡中,我们有2个PCI插槽,每一个有4个中断线。所以我们需要映射8个中断信号线到中断控制器中。这是通过“interrupt-map”属性来完成的。 由于中断号(例如#INTA)并不足以区分是PCI总线上哪个设备触发的中断。我们也需要表示出来是哪个PCI设备触发的中断。 幸运的是,每个PCI设备都有一个我们可以使用的唯一设备号。为了区分多个PCI设备的中断,我们需要一个由PCI设备号和PCI中断号组成的元组。 更进一步,我们构造了一个单元中断说明符,它有四个单元: 3个单元格表示地址,包含phys.hi,phys.mid,phys.low 1个单元格表示中断号,(#INTA、#INTB、#INTC、#INTD) 因为我们只需要PCI地址的设备号部分,中断映射掩码属性就发挥作用了。“ interrupt-map-mask”也是一个由四部分组成的元组,像“ unit interrupt specifier”一样。掩码的第一段表示“ unit interrupt specifier”的哪一部分是有用的。 在我们的示例中,我们可以看到只有phys的设备编号只有phys.hi是必需的,我们需要3位来区分这4个中断。 现在我们可以构造中断映射属性。此属性是一个表,该表中的每个条目由子(PCI总线)单元中断说明符、父句柄(负责服务中断的中断控制器)和父单元中断说明符组成。因此,在第一行中,我们可以读到PCI中断#INTA被映射到irq9上,这是我们中断控制器的低灵敏度级别。 目前唯一缺少的部分是PCI总线单元中断描述符中的数字。单元中断描述符的重要部分是来自“ phys.hi ”的设备编号。而设备编号是板卡相关的,不同的板卡的设备编号可能不同,它取决于每个PCI主机控制器如何激活每个设备上的IDSEL pin。在本例中,为PCI插槽1分配设备id为24 (0x18),为PCI插槽2分配设备id为25 (0x19)。每个PCI插槽的“ phys.hi”取值,是通过将设备号左移11位,得到ddddd部分来确定的,如下所示: * phys.hi for slot 1 is 0xC000, and * phys.hi for slot 2 is 0xC800. slot1的0x18左移11位,就是0xC000;slot2的0x19左移11位,就是0xC800。 综合一起考虑,中断映射表如下: 插槽1的INTA为IRQ9,低有效 插槽1的INTB为IRQ10,低有效 插槽1的INTC为IRQ11,低有效 插槽1的INTD为IRQ12,低有效 以及 插槽2的INTA为IRQ10,低有效 插槽2的INTB为IRQ11,低有效 插槽2的INTC为IRQ12,低有效 插槽2的INTD为IRQ9,低有效 属性“ interrupts = <8 0>;”表示PCI中断控制器本身可能触发的中断,不要与PCI设备触发的中断相混淆 ( INTA, INTB, ...)。 最后要注意的一点是,就像“ interrupt-parent”属性一样,节点上存在“interrupt-map”属性将更改所有子节点和子节点的默认中断控制器。在本PCI示例中,这意味着PCI主机桥接器成为默认中断控制器。如果通过PCI总线连接的设备与另一个中断控制器有直接连接,那么它还需要指定自己的“ interrupt-parent”属性。