《基本概念》
高级模型机
现在,我们已经掌握了基本的定义,接下来让我们往模型机里添加一些硬件,以讨论一些更复杂的用例。
高级模型机添加了一个 PCI 主桥,其控制寄存器映射到内存0x10180000,并且 BARs 编程至以地址 0x80000000 为起始。
既然关于设备树我们已经有所了解了,那么我们就从以下所示新增加的节点来介绍 PCI 主桥。
pci@10180000 {
compatible = "arm,versatile-pci-hostbridge", "pci";
reg = <0x10180000 0x1000>;
interrupts = <8 0>;
};
PCI主桥
注,本节将假定读者了解 PCI 的一些基本知识。本文并不是 PCI 教程,想要了解更深入的信息,请阅读 [1]。你也可以参考 ePAPR 或 PCI Bus Binding to Open Firmware(http://playground.sun.com/1275/bindings/pci/pci2_1.pdf)。还可以访问 http://devicetree.org/MPC5200:PCI,这里可以找到 Freescale MPC5200 的一个完整工作的例子
PCI 总线编号
每个 PCI 总线段都是唯一编号的,并且总线的编号是通过使用 bus-ranges 属性在 pci 节点中暴露出来的,这个属性有两个 cell。第一个 cell 给出分配给该节点的总线号;第二个 cell 给出任何次级 PCI 总线最大总线号。
模型机只有一个 pci 总线,所以两个 cell 都是 0。
pci@0x10180000 {
compatible = "arm,versatile-pci-hostbridge", "pci";
reg = <0x10180000 0x1000>;
interrupts = <8 0>;
bus-ranges = <0 0>;
};
PCI 地址转换
类似于前面所描述的本地总线,PCI 地址空间和 CPU 地址空间是完全分离的,所以需要一个从 PCI 地址到 CPU 地址的转换。同样,完成这样的转换将使用 ranges、#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 个 cell,同时 PCI rangs 被编码为 2 个 cell。那么第一个问题就可能是,为什么我们要用三个 32 位 cell 去指定一个 PCI 地址?这三个 cell 分别标记了 phys.hi、phys.mid 和 phys.low[2]。
■ phys.hi cell: npt000ss bbbbbbbb dddddfff rrrrrrrr
■ phys.mid cell: hhhhhhhh hhhhhhhh hhhhhhhh hhhhhhhh
■ phys.low cell: llllllll llllllll llllllll llllllll
PCI 地址是 64 位的,并编码进了 phys.mid 和 phys.low。然而真正有意思的是在 phys.high 里面,这是一个位域。
n:重定位区域标志(在这里不起作用)
p:预取(可缓存)区标志
t:地址别名标志(在这里不起作用)
ss:空间代码
00:配置空间
01:I/O 空间
10:32 位内存空间
11:64 位内存空间
bbbbbbbb:PCI 总线号。PCI 可以是分层结构,所以我们可能有 PCI/PCI 桥,这可以定义子总线。
ddddd:设备号,通常与 IDSEL 型号相关联。
fff:功能号。用于多功能 PCI 设备。
rrrrrrrr:寄存器号,用于配置周期。
对于 PCI 地址转换来说,p 和 ss 是最重要的字段。在 phys.hi 里的 p 和 ss 的值决定了访问哪个 PCI 地址空间。因此,通过查找 ranges 属性,我们将得到三个区域。
为阻止这些工作,phys.hi 位域的存在就意味着操作系统必须知道该节点代表了一个 PCI 桥,这样操作系统才能为了地址转换而忽略那些不相关的字段。为了判断应该掩码哪些额外的字段,操作系统需要在 PCI 总线节点中寻找“pci”字符串。
高级中断映射
现在我们来到了最有趣的部分,PCI 中断映射。一个 PCI 设备可以使用引线 #INTA、#INTB、#INTC 和 #INTD 来触发中断。如果我们没有多功能 PCI 设备,那么设备中断必须使用 #INTA。然而,每个 PCI 插槽或设备通常会连接到中断控制器上不同的输入端。所以设备树需要一种能将各个 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&intc10 3
0xc000 0 0 3&intc11 3
0xc000 0 0 4&intc12 3
0xc800 0 0 1&intc10 3 // 2nd slot
0xc800 0 0 2&intc11 3
0xc800 0 0 3&intc12 3
0xc800 0 0 4&intc 9 3>;
};
首先你会发现,PCI 中断号只使用了一个 cell,不像系统中断控制器,它使用两个 cell,一个用于中断号,另一个用于标志。PCI 中断只使用了一个 cell,因为 PCI 中断确定为始终是低电平触发。
在这个示例板上,我们有 2 个分别包含 4 个中断线的 PCI 插槽,所以我们需要映射 8 个中断线到中断控制器上。这已经在 interrupt-map 属性中完成了。关于中断映射的具体步骤请参考 [3]。
因为要区分单一 PCI 总线上的若干 PCI 设备中断号(#INA 等)是不够用的,所以我们还需要指出是哪个 PCI 设备触发了中断线。幸运的是我们还可以使用每个设备所拥有的唯一设备号。为了区分这些 PCI 设备,我们需要一个元组,该元组由 PCI 设备号和 PCI 中断号组成。通俗的说,我们构造了由四个 cell 组成的设备中断指示符。
三个 #address-cells 由 phys.hi、phys.mid、phys.low 组成,然后
一个 #interrupt-cell(#INTA、#INTB、#INTC、#INTD)
因为我们只需要 PCI 地址中的设备号部分,所以 interrupt-map-mask 发挥了作用。interrupt-map-mask 也是 4 元组,就像设备中断指示符一样。掩码的第一部分指出我们应该考虑设备中断指示符中哪一部分。在本例中,我们可以看到在 phys.hi 中只需要设备号部分,另外我们还需要 3 位来区分四个中断线(PCI 中断线是从 1 开始计数的,不是 0!)。
现在。我们可以构建 interrupt-map 属性了。该属性是一个表,这个表的每一项都由一个子(PCI 总线)设备中断指示符、一个父句柄(用于中断服务的中断控制器)和一个父设备中断指示符组成。因此,在第一行中我们可以知道 PCI 中断 #INTA 将被映射到中断控制器的 IRQ 9,并且是低电平有效。[4] 目前为止,唯一没有讨论的就是 PCI 总线设备中断指示符里古怪的数字了。来自 phys.hi 位域的设备号是设备中断指示符中的重要组成部分。设备号是平台特定的,并取决于 PCI 主控制器如何激活各个设备的 IDSEL 管脚。在本例中,PCI slot 1 分配设备 id 24(0x18),PCI slot 2 分配设备 id 25(0x19)。每个 slot 的 phys.hi 值是通过将设备号左移 11 位至位域的 ddddd 段得到的,就像下面:
slot 1 的 phys.hi 就是 0xC000,并且slot 2 的 phys.hi 就是 0xC800。
把这些放在一起之后,interrupt-map 属性就显示为:
属性 interrupts = <8 0>; 描述了主控制器或 PCI 桥控制器本身有可能触发中断。不要与 PCI 设备触发的中断(使用 INTA,INTB,...)告混了。
最后需要注意的事。就像 interrupt-parent 属性一样,节点中 interrupt-map 属性的存在将改变子节点和孙节点的默认中断控制器。在这个 PCI 示例中,这意味着 PCI 主桥变成了默认中断控制器。如果一个通过 PCI 总线连接的设备同时还直接连接至另一个中断控制器,这时就需要指定它自己的 interrupt-parent 属性。
内容参考资料书写。
本文分享自 Rice 嵌入式开发技术分享 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!