Bootstrap

dts详解及举例

一.ranges
    当需要描述的设备不是本地设备时,就需要描述一个"从设备地址空间到CPU地址空间的映射关系",就需要用到ranges属性。

ranges = <local地址, parent地址,size> 表示将local地址向parent地址的转换
eg1:
#address-cells = <1>;
#size-cells = <1>;
...
external-bus {
    #address-cells = <2>  //可以理解为reanges中的local地址为2个,eg下面第二行的0x1 0x0 
    #size-cells = <1>;
    ranges = <0x0 0x0  0x10100000   0x10000     // Chipselect 1, Ethernet
              0x1 0x0  0x10160000   0x10000     // Chipselect 2, i2c controller
              0x2 0x0  0x30000000   0x1000000>; // Chipselect 3, NOR Flash
};

ranges属性为一个地址转换表,表中的每一行都包含子地址、父地址、在各自的地址空间内的
区域大小。他们的大小(包含的cell)分别由子节点的address-cells的值、父节点的address-cells的值
和子节点的size-cells来决定。
以第一行为例:<这好难知道到底哪个地址有父节点决定,哪个由子节点决定>
* 0x0 0x0 两个cell,由子节点 external-bus 的 address-cells = <2> 决定;
* 0x10100000 一个cell,由父节点的 address-cells=<1> 决定;
* 0x10000 一个cell,由子节点external-bus的size-cells=<1>决定。
最终第一行的意思为: 片选0,偏移0(选中了网卡),被映射到地址空间的
0x0~(0x0 + 0x10000)  --> 0x10100000 ~ (0x10100000 + 0x10000)

eg2:
	#address-cells = <1>; //可以理解为reanges中的local地址为2个,eg下面的0x0
	#size-cells = <1>;
	ranges = <0x0 0x10 0x20>

表示将local的从0x0~(0x0 + 0x20)的地址空间映射到parent的0x10~(0x10 + 0x20)
* local 地址的个数取决于当前含有ranges属性的节点的#address-cells属性的值,
* size 取决于当前含有ranges属性的节点的#size-cells属性的值。
* parent 地址的个数取决于当前含有ranges属性的节点的parent节点的#address-cells的值。 

eg3:
	对于
	#size-cells = <2>;的情况,以后再研究。。。
	
eg4:
	ranges; 	//ranges属性值为空的话,表示1:1映射
对于没有ranges属性的节点,代表不是memory map区域

eg5: 进阶
参考:https://www.cnblogs.com/aaronLinux/p/5496546.html
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地址空间与CPU地址空间是完全分离的,所以这里需要通过定义ranges属
性进行地址转化。
#address-cells定义PCI使用3个cell,并且PCI的地址范围通过两个单位就可以解读。所以,首先的问题就
是,为什么需要用3个32位的cell来描述一个PCI地址。

这三个cell分别代表物理地址高位、中位、低位:
	1 phys.high cell : npt000ss bbbbbbbb dddddfff rrrrrrrr
	2 phys.mid cell : hhhhhhh hhhhhhhh hhhhhhhh hhhhhhh
	3 phys.low cell : llllllll llllllll llllllll llllllll

PCI地址为64位宽度,编码在phys.mid和phys.low中。真正重要的东西在于phys.high这一位空间中:
	n:代表重申请空间标志(这里没有使用)
	p:代表预读空间(缓存)标志
	t:别名地址标志(这里没有使用)
	ss:空间代码
	00: 设置空间
	01:IO空间
	10:32位存储空间
	11:64位存储空间

	bbbbbbbb: PCI总线号。PCI有可能是层次性架构,所以我们可能需要区分一些子-总线
	ddddd:设备号,通常由初始化设备选择信号IDSEL连接时申请。
	fff:功能序号,有些多功能PCI设备可能用到。
	rrrrrrrr:注册号,在设置周期使用。

ranges = <0x42000000 0 0x80000000 0x80000000 0 0x20000000
          0x02000000 0 0xa0000000 0xa0000000 0 0x10000000
          0x01000000 0 0x00000000 0xb0000000 0 0x01000000>;
回头再看这个ranges分表代表了什么。父节点address-cells为1,子节点address-cells为3,子节点s
ize-cells为2。则第一行可以这样划分(怎么理解?):

0x42000000 0 0x80000000 子节点地址 | 0x80000000 父节点地址 | 0 0x20000000 地址空间长度|

0x42000000 为phys.high,第一位为 01000010,则p为1,ss为10,即申请32位存储空间为缓存空间。
phys.mid为0,phys.low为0x80000000,他们共同组成了PCI地址。
即表示从PCI总线的0x80000000地址处申请出一个32位的存储空间作为缓存。后边的那个
cell 0x80000000 0 0x20000000代表到CPU空间后的参数,申请的地址被映射到CPU空间的
0x80000000地址处,大小共计0x20000000(512MB)。

二、interrupts
描述中断连接需要4个属性:
1. interrupt-controller 一个空属性用来声明这个node接收中断信号(固定位空属性)
2. interrupt-cells 这是中断控制器节点的属性,用来标识一个控制器需要几个单位做中断描述符。
3. interrupt-parent 标识此设备节点属于哪一个中断控制器,如果没有此属性,会自动依附父节点的。
4. interrupts 中断标识符列表,表示每个中断输出信号。
如果有两个,第一个是中断号,第二个是中断类型,如高电平、低电平、边缘触发等触发特性。对于给定的中断控制器,应该仔细阅读相关文档来确定其中断标识该如何解析。

eg1: 
interrupt-parent = <&gpio2>;
interrupts = <5 1>;
表示中断控制器是GPIO2, 使用GPIO2的第5号中断指的是5号引脚,1指中断触发方式为上升沿触发。

eg2:
中断类型有:
IPI: inter-processor interrupts 中断号 0~15
PPI: per processor inerrupts 中断号 16~31
SPI: shared processor interrupts 32~32+224
SGI: software generated interrupts

interrupts = <GIC_SPI 66 1>;
表示中断类型为共享处理中断SPI,中断号为SPI中断类型中的第66号中断,计算出来的
实际中断号为32+66=98号中断,1表示上升沿触发中断。

三、reg
子节点的reg和range是由父节点的#address-cells和#size-cells决定的
reg意为region,区域。格式为:
reg = <address1 length1 [address2 length2] [address3 length3]>;

address-cells决定了address1/2/3包含几个cell,size-cells决定了length1/2/3包含了几个cell。

eg1:
spi@10115000 {
	compatible = "arm,pl022";
	reg = <0x10115000 0x1000 >;
};
位于0x10115000的SPI设备申请地址空间,起始地址为0x10115000,长度为0x1000,
即属于这个SPI设备的地址范围是 0x10115000 ~ (0x10115000 + 0x1000)


实际应用中,有另外一种情况,就是通过外部芯片片选激活模块。例如,挂载在外部总线上,需要通过片选线
工作的一些模块:

eg2:
external-bus {							/* 外部总线片选 */
    #address-cells = <2>				/* 使用2个cell来表示地址 */
    #size-cells = <1>;
	/* 所以以下的子设备们都需要3个cell来描述地址空间属性——片选、偏移量、地址长度 */
	
    ethernet@0,0 {						/* 一个片选号,一个片选号上的偏移量*/
        compatible = "smc,smc91c111";
        reg = <0 0 0x1000>;				/* 子设备:片选号/偏移量/地址长度 */
    };

    i2c@1,0 {
        compatible = "acme,a1234-i2c-bus";
        #address-cells = <1>;
        #size-cells = <0>;
        reg = <1 0 0x1000>;				/* 片选为1,偏移量0,地址长度0x1000 */
        rtc@58 {
            compatible = "maxim,ds1338";
			
			/* 因为I2C设备只是被分配在一个地址上,不需要其他任何空间,所以只
			 * 需要一个address的cell就可以描述完整,不需要size-cells 
			 */
            reg = <58>;					
        };
    };

    flash@2,0 {
        compatible = "samsung,k8f1315ebm", "cfi-flash";
        reg = <2 0 0x4000000>;
    };
};

eg3: 
如果node "soc" 中 "#address-cells=<1>"、"#size-cells=<1>",那么子node "serial" 中 "reg"
属性的解析为
"addr1 = 0x0, size1 = 0x100, addr2 = 0x0, size2 = 0x200":
soc {
    #address-cells = <1>;
    #size-cells = <1>;
    serial {
        reg = <0x0 0x100 0x0 0x200>;
    }
}

/ { 
	#address-cells = <1>; 
	#size-cells = <1>; 

	gpio@101F3000 { 
		compatible = "arm,pl061"; 
		reg = <0x101f3000 0x1000 0x101f4000 0x0010>
	};
}
GPIO设备地址被分成两个地址范围:0x101f3000 ~ (0x101f3000 + 0x1000) 和 
0x101f4000 ~ (0x101f4000 + 0x0010)。
上面地址不包含 (0x101f3000 + 0x1000) 和 (0x101f4000 + 0x0010)。


eg4:
如果node "soc" 中 "#address-cells=<2>"、"#size-cells=<2>",那么子node "serial"中"reg"
属性的解析为
"addr1 = 0x100, size1 = 0x200":
soc {
    #address-cells = <2>;
    #size-cells = <2>;
    serial {
        reg = <0x0 0x100 0x0 0x200>;
    }
}

&usb1 {
	#address-cells = <2>;
	#size-cells = <2>;
	dwc3 {
		compatible = "snps,dwc3";
		reg = <0x0 0x3110000 0x0 0x10000>;   //addr1 = 0x3110000; size1 = 0x10000
	};
};

eg5:
如果node "soc" 中 "#address-cells=<2>"、"#size-cells=<0>",那么子node "serial"中"reg"
属性的解析为
"addr1 = 0x100, addr2 = 0x200":
soc {
    #address-cells = <2>;
    #size-cells = <0>;
    serial {
        reg = <0x0 0x100 0x0 0x200>;
    }
}

参考:

Device Tree 详解

[dts]Device Tree格式解析
 

;