18.2.5 地址编码可寻址的设备使用如下信息在设备树中编码地址信息:
reg
#address-cells
#size-cells其中,reg的组织形式为reg=<address1length1[address2length2][address3length3]...>,其中的每一组address length表明了设备使用的一个地址范围。address为1个或多个32位的整型(即cell),而length的意义则意味着从address到address+length–1的地址范围都属于该节点。若#size-cells=0,则length字段为空。
address和length字段是可变长的,父节点的#address-cells和#size-cells分别决定了子节点reg属性的address和length字段的长度。
在代码清单18.2中,根节点的#address-cells=<1>;和#size-cells=<1>;决定了serial、gpio、spi等节点的address和length字段的长度分别为1。
cpus节点的#address-cells=<1>;和#size-cells=<0>;决定了两个cpu子节点的address为1,而length为空,于是形成了两个cpu的reg=<0>;和reg=<1>;。
external-bus节点的#address-cells=<2>和#size-cells=<1>;决定了其下的ethernet、i2c、flash的reg字段形如reg=<0 0 0x1000>;、reg=<1 0 0x1000>;和reg=<2 0 0x4000000>;。其中,address字段长度为2,开始的第一个cell(即“<”后的0、1、2)是对应的片选,第2个cell(即<0 0 0x1000>、<1 0 0x1000>和<2 0 0x1000000>中间的0,0,0)是相对该片选的基地址,第3个cell(即“>”前的0x1000、0x1000、0x1000000)为length。
特别要留意的是i2c节点中定义的#address-cells=<1>;和#size-cells=<0>;,其作用到了I2C总线上连接的RTC,它的address字段为0x58,是RTC设备的I2C地址。
根节点的直接子节点描述的是CPU的视图,因此根子节点的address区域就直接位于CPU的内存区域。但是,经过总线桥后的address往往需要经过转换才能对应CPU的内存映射。external-bus的ranges属性定义了经过external-bus桥后的地址范围如何映射到CPU的内存区域。ranges = <0 0 0x10100000 0x10000 // Chipselect 1, Ethernet
1 0 0x10160000 0x10000 // Chipselect 2, i2c controller
2 0 0x30000000 0x1000000>; // Chipselect 3, NOR Flash
对于本例而言,子地址空间的#address-cells为2,父地址空间的#address-cells值为1,因此0 0 0x10100000 0x10000的前2个cell为external-bus桥后external-bus上片选0偏移0,第3个cell表示external-bus上片选0偏移0的地址空间被映射到CPU的本地总线的0x10100000位置,第4个cell表示映射的大小为0x10000。ranges后面两个项目的含义可以类推。
18.2.6 中断连接
设备树中还可以包含中断连接信息,对于中断控制器而言,它提供如下属性:
interrupt-controller–这个属性为空,中断控制器应该加上此属性表明自己的身份;
#interrupt-cells–与#address-cells和#size-cells相似,它表明连接此中断控制器的设备的中断属性的cell大小。
在整个设备树中,与中断相关的属性还包括:
interrupt-parent–设备节点通过它来指定它所依附的中断控制器的phandle,当节点没有指定interrupt-parent时,则从父级节点继承。对于本例(代码清单18.2)而言,根节点指定了interrupt-parent=<&intc>;,其对应于intc:interrupt-controller@10140000,而根节点的子节点并未指定interrupt-parent,因此它们都继承了intc,即位于0x10140000的中断控制器中。interrupts–用到了中断的设备节点,通过它指定中断号、触发方法等,这个属性具体含有多少个cell,由它依附的中断控制器节点的#interrupt-cells属性决定。而每个cell具体又是什么含义,一般由驱动的实现决定,而且也会在设备树的绑定文档中说明。
譬如,对于ARM GIC中断控制器而言,#interrupt-cells为3,3个cell的具体含义在Documentation/devicetree/bindings/arm/gic.txt中就有如下文字说明:
The 1st cell is the interrupt type; 0 for SPI interrupts, 1 for PPI interrupts.
The 2nd cell contains the interrupt number for the interrupt type. SPI interrupts are in the range [0-987]. PPI interrupts are in the range [0-15].
The 3rd cell is the flags, encoded as follows:
bits[3:0] trigger type and level flags.
1 = low-to-high edge triggered
2 = high-to-low edge triggered
4 = active high level-sensitive
8 = active low level-sensitive
bits[15:8] PPI interrupt cpu mask. Each bit corresponds to each of the 8 possible cpus attached to the GIC. A bit set to '1' indicated the interrupt is wired to that CPU. Only valid for PPI interrupts.
值得注意的是,一个设备还可能用到多个中断号。对于ARM GIC而言,若某设备使用了SPI的168号、169号两个中断,而且都是高电平触发,则该设备节点的中断属性可定义为interrupts=<0 168 4>,<0 169 4>;。
对于平台设备而言,简单的通过如下API就可以指定想取哪一个中断,其中的参数num就是中断的index。
int platform_get_irq(struct platform_device *dev, unsigned int num);
在.dts文件中可以对中断进行命名,而后在驱动中通过platform_get_irq_byname()来获取对应的中断号。
譬如代码清单18.14演示了在drivers/dma/fsl-edma.c中通过platform_get_irq_byname()获取IRQ,以及arch/arm/boot/dts/vf610.dtsi与fsl-edma驱动对应节点的中断描述。代码清单18.14 设备树中的中断名称以及驱动获取中断
1static int
2fsl_edma_irq_init(struct platform_device *pdev,struct fsl_edma_engine *fsl_edma)
3{
4 fsl_edma->txirq = platform_get_irq_byname(pdev, "edma-tx");
5 fsl_edma->errirq = platform_get_irq_byname(pdev, "edma-err");
6}
7
8edma0: dma-controller@40018000 {
9 #dma-cells = <2>;
10 compatible = "fsl,vf610-edma";
11 reg = <0x40018000 0x2000>,
12 <0x40024000 0x1000>,
13 <0x40025000 0x1000>;
14 interrupts = <0 8 IRQ_TYPE_LEVEL_HIGH>,
15 <0 9 IRQ_TYPE_LEVEL_HIGH>;
16 interrupt-names = "edma-tx", "edma-err";
17 dma-channels = <32>;
18 clock-names = "dmamux0", "dmamux1";
19 clocks = <&clks VF610_CLK_DMAMUX0>,
20 <&clks VF610_CLK_DMAMUX1>;
21};
第4行、第5行的platform_get_irq_byname()的第2个参数与.dts中的interrupt-names是一致的。