续上文《ARM Device Tree 0》, 本篇看一下中断部分和一些Device Tree的相关总结。
Overview
1 | / { |
我们再来看一次SMBus的内容,这次我们主要看中断映射部分。
SMB并不是中断控制器,
我们可以发现,在SMB上的设备只用一个中断cell, 比如ethernet@2,
02000000的interrupts = <15>
. 这个中断需要被映射到GIC上的中断,这里就需要用interrupt-map
。
Interrupt 的模型也同样是一个树形结构,子节点有自己的中断空间(Interrupt Space), 如果需要把子节点的中断空间与父节点的联系起来,与地址空间类似, 中断也就需要映射。Interrupt-map描述的是一种接线的规则, 或者中断路由的规则(子设备的中断如何均衡地发给中断控制器).
具有interrupt-map
的结点必须也同时定义#interrupt-cells
,
#interrupt-cells
定义了子节点的interrupts
要用几个cell来描述。
如果需要interrupt-map
但是没有发现这个属性,那么映射被认为是1:1的映射。
interrupt-map-mask
会对unit interrupt specifier进行Mask后再进行map的映射,
unit interrupt specifier顾名思义,就是子节点的地址child-unit-address
+中断说明interrupt specifier
interrupt-map
里面数据项的格式一般为:
child-unit-address |
child-interrupt-specifier |
interrupt-parent |
parent-unit-address |
parent-interrupt-specifier |
---|---|---|---|---|
0 0 |
1 |
&gic |
0 0 |
0 1 4 |
0 0 |
2 |
&gic |
0 0 |
0 2 4 |
0 0 |
3 |
&gic |
0 0 |
0 3 4 |
… | ||||
0 0 |
42 |
&gic |
0 0 |
0 42 4 |
child-unit-address
: 0 0, 这个是12行的#address-cells = <2>
决定child-interrupt-specifier
: 1, 这个是22行的#interrupt-cells = <1>
决定interrupt-parent
: &gic, 这个的值就是phandle, smb继承自4行root指定的interrupt-parent = <&gic>
parent-unit-address
:0 0
, 这个是45行GIC的#address-cells = <2>
决定parent-interrupt-specifier
:0 1 4
, 由root下第44行, GIC中的#interrupt-cells = <3>
决定
现在我们再次看ethernet@2,02000000的15号中断,通过interrupt-map我们知道,
smb下设备的地址和gic中的地址没有区别, interrupt-map-mask
为<0 0 63>
or <0 0 0x002f>
.
也就是最大支持64个child-interrupt-specifier
, 其中15号中断会被映射为GIC的<0 15 4>
,
也就是SPI的15号,flag为active high level-sensitive.
ARM GIC
1 | / { |
从Linux中ARM GIC的文档我们可以知道,
第一个reg区域是GIC distributor
的基址和大小,第二个区域是GIC cpu interface register
的基址和大小, 后面两个区域和interrupts又表示什么呢?
interrupts有两种情况可以出现,第一个是作为二级GIC, 那么其自身会将中断连到上一个GIC上,也就需要指明自己的中断;
第二种就是支持虚拟化的primary gic,GIC virtualization extensions (VGIC).
第三个区域<0x0 0x2c004000 0 0x2000>
就是GIC virtual interface control register 的基址和大小;
第四个区域是GIC virtual cpu interface register
的基址和大小,
virtual cpu interface使得GIC能在不退出guest的情况下发送IRQ ACKs 和 EOIs,从而减少VMExit。
interrupts
则指定了VGIC maintenance interrupt的中断号,
在ARM GIC-400(2.3.2 PPIs)的文档中:
This is a configurable event generated by the corresponding virtual CPU interface to indicate a situation that might require hypervisor action.
说明了当vGIC需要Hypervisor的处理时会注入这个中断,从而产生下陷(VMExit),之后Hypervisor就可以进行处理。 关于vGIC的用法如果有机会了我之后再虚拟化的相关文章中再介绍,本文不再赘述。
PCI(e)
现在我们再来看看设备中的一大类PCI(Peripheral Component Interconnect)设备, 现在新的是PCI-e, 这类设备都会接到PCI-e总线上, 而设备的地址也更加复杂。
1 | / { |
foundation上没有pcie, 所以我们看看hikey960上的pcie。 从compatible可以知道这是hisilicon的kirin960-pcie, 所以我们可以从linux的源码里面找到它的文档: designware-pcie.txt, kirin-pcie.txt(kirin-pcie是基于上面设计的).
reg
就是配置的物理地址reg-names
:dbi
: controller configuration registers;apb
: apb Ctrl register defined by Kirin;phy
: apb PHY register defined by Kirin;config
: PCIe configuration space registers.
reset-gpios
: The GPIO to generate PCIe PERST# assert and deassert signal.num-lanes:
number of lanes to use (this property should be specified unless the link is brought already up in BIOS)clocks
: Must contain an entry for each entry in clock-names. See ../clocks/clock-bindings.txt for details.clock-names
: Must include the following entries:pcie
pcie_bus
上面这些是PCIe里面新增或者比较特殊的一些property, 从binding的文档可以很简单看出来这些用法, 我们主要看看PCI-e的地址映射。
PCI-e Address Mapping
pice总线都是被唯一标记的,总线号通过bus-range
暴露出来,
第一个cell记录的是这条总线被分配的总线号, 第二个cell是它下属PCI总线的最大总线号。
pcie也同样具有自己的地址空间,类似的使用range
, #address-cells
和#size-cells
,
ranges = <0x02000000 0x0 0x00000000 0x0 0xf6000000 0x0 0x02000000>;
这里的问题在于,为什么PCI-e使用3个cell来描述子节点的地址,外部的地址空间(64-bit)是正常的2个cell.
PCI-e的子节点地址分为三个部分:
phys.hi: npt000ss bbbbbbbb dddddfff rrrrrrrr
phys.mid: hhhhhhhh hhhhhhhh hhhhhhhh hhhhhhhh
phys.low: llllllll llllllll llllllll llllllll
PCI-e地址总长为64位,地址是在phys.mid与phys.low里面,而phys.hi中的bit则有着特殊的意义:
n
: is 0 if the address is relocatable, 1 otherwise (doesn’t play a role here)p
: is 1 if prefetchable (cacheable) region flagt
: is 1 if the address is aliased (for non-relocatable I/O), below 1 MB (for Memory), or below 64 KB (for relocatable I/O)ss
: space code00
: configuration space01
: I/O space10
: 32 bit memory space11
: 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.
所以我们的range 0x0200_0000
表示ss
为10
,我们的PCI-e有一个32-bit的 non-prefetchabled,大小为32MB的地址空间0x0000_0000
-0x01FF_FFFF
将被映射到0xf600_0000 - 0xf7FF_FFFF
这段内存区域。Kernel会找到pci,并将相关的详细数据解读出来。
关于地址这部分,ss
会影响phys.hi里面的其他位的含义,具体的可以参考文档PCI Bus Binding to: IEEE Std 1275-1994 Standard for Boot (Initialization Configuration) Firmware, v2.1。
PCI-e DMA Address Translation
如果PCI-e支持DMA (Direct Memory Access)的话,还会有一个类似与dma-ranges = <0x02000000 0 0x00000000 0x80000000 0 0x20000000>
property.
这个是告诉系统PCI-e会认为PCIe地址空间中大小为512MB,从0x0000_0000开始的地址空间会被映射到主存0x8000_0000的地方,ss
为02
表示这是一段32-bit的内存。
Interrupt
在PCI-e这边的中断,我们发现interrupt-map-mask
多了一个cell, 这也是因为PCI-e这边的#address-cells
为3的原因,
剩余的内容和上面的类似,就不再赘述了。
PCI-e Devices
配置PCI-e上的设备不能像直接挂在root下设备一样被CPU直接访问,
而是统一通过PCI-e的Configuration Space (最后一个reg region)进行配置。
phys.hi
中的device-num、function-number等等就可以用上了;
从这个phys.hi
的格式我们可以知道,每个PCI-e可以支持最多256 buses 32devices 8 functions,
找一个设备使用其Bus, Device, Function (BDF)就可以唯一确定,
关于PCI-e or PCI设备的访问和配置,wiki上有更详细的介绍。