设备树的概念(二):表示和寻址设备

表示和寻址设备

每个设备在DT中至少有一个节点。有些属性对于许多设备类型都是通用的,特别是位于内核已知总线(SPI、I2C、Platform、MDIO等等)上的设备。这些属性是reg、#address-cells和#size-cells。这些属性的目的是在它们所在的总线上进行设备寻址。也就是说,主要的寻址属性是reg,这是一个通用属性,其含义取决于设备所在的总线。size-cell和address-cell前面的#(hash)可以转换为length。

每个可寻址设备都具有reg属性,该属性是一个元组列表,形式为reg = <address0 size0 [address1size1] [address2size2]…>;,其中每个元组表示设备使用的地址范围。#size-cells表示使用多少个32位单元来表示大小,如果与大小无关,则可以为0。另一方面,#address-cells表示使用多少个32位单元来表示地址。换句话说,每个元组的address元素根据#address-cell来解释;size元素也是如此,它根据#size-cell进行解释。

实际上,可寻址设备继承了它们父节点的#size-cell和#address-cell,父节点是代表总线控制器的节点。给定设备中#size cell和#address-cell的存在不会影响设备本身,但会影响它的子设备。换句话说,在解释给定节点的reg属性之前,必须知道父节点的#address-cells和#size-cells值。父节点可以自由定义任何适合设备子节点的寻址方案。

SPI和I2C寻址

SPI和I2C设备都属于非内存映射设备,因为它们的地址不能被CPU访问。相反,父设备的驱动程序(即总线控制器驱动程序)将代表CPU执行间接访问。每个I2C/SPI设备总是表示为该设备所在的I2C/SPI总线节点的子节点。对于非内存映射的设备,#size-cells属性为0,寻址元组中的size元素为空。这意味着这种设备的reg属性总是只有一个单元:

&i2c3 {
    [...]
    status = "okay";
    temperature-sensor@49 {
        compatible = "national,lm73";
        reg = <0x49>;
    };
    
    pcf8523: rtc@68 {
        compatible = "nxp,pcf8523";
        reg = <0x68>;
    };
};

&ecspi1 {
    fsl,spi-num-chipselects = <3>;
    cs-gpios = <&gpio5 16 0>, <&gpio5 17 0>, <&gpio5 18 0>;
    status = "okay";
    [...]

    ad7606r8_0: ad7606r8@1 {
        compatible = "ad7606-8";
        reg = <1>;
        spi-max-frequency = <1000000>;
        interrupt-parent = <&gpio4>;
        interrupts = <30 0x0>;
        convst-gpio = <&gpio6 18 0>;
    };
};

如果你查看soc级别文件arch/arm/boot/dts/imx6qdl.dtsi,你将注意到i2c和spi节点中的#size-cells和#address-cells分别被设置为0和1,这两个节点分别是上面列举的i2c和spi设备的父节点。这有助于我们理解它们的reg属性,即address值只有一个元素,size值没有。

I2C设备的reg属性用于指定设备在总线上的地址。对于SPI设备,reg表示在控制节点的片选列表中分配给该设备的片选的索引。例如ad7606r8 ADC,片选索引为1,对应于cs-gpios中的<&gpio5 17 0>,cs-gpios是控制节点中的芯片选择列表。

为什么以I2C/SPI节点的phandle为例?因为I2C/SPI设备应该在板级文件(.dts)中声明,而I2C/SPI总线控制器则在soc级文件(.dtsi)中声明。

平台设备寻址

本文介绍简单内存映射设备,这些设备的内存可以被CPU访问。这里,reg属性仍然定义了设备的地址,这是一个可以访问设备的内存区域列表。每个区域都用一个cells元组表示,其中第一个cell是内存区域的基址,第二个cell是该区域的大小。然后它的形式是reg = <base0 length0 [base1 length1] [address2 length2]…>。每个元组表示设备使用的一个地址范围。

在现实世界中,如果不知道其他两个属性#size-cells和#address-cells的值,就不应该分解reg属性。#size-cells告诉我们每个子reg元组中长度字段有多大。#address-cell也是一样,它告诉我们必须使用多少cell来指定一个地址。

这种设备应该在一个特殊值的节点中声明,compatible = "simple-bus",意思是一个简单的内存映射总线,没有特定的处理或驱动程序:

soc {
    #address-cells = <1>;
    #size-cells = <1>;
    compatible = "simple-bus";
    aips-bus@02000000 { /* AIPS1 */
        compatible = "fsl,aips-bus", "simple-bus";
        #address-cells = <1>;
        #size-cells = <1>;
        reg = <0x02000000 0x100000>;
        [...];
        spba-bus@02000000 {
            compatible = "fsl,spba-bus", "simple-bus";
            #address-cells = <1>;
            #size-cells = <1>;
            reg = <0x02000000 0x40000>;
            [...]
        };
        
        ecspi1: ecspi@02008000 {
            #address-cells = <1>;
            #size-cells = <0>;
            compatible = "fsl,imx6q-ecspi", "fsl,imx51-ecspi";
            reg = <0x02008000 0x4000>;
            [...]
        };
        
        i2c1: i2c@021a0000 {
            #address-cells = <1>;
            #size-cells = <0>;
            compatible = "fsl,imx6q-i2c", "fsl,imx21-i2c";
            reg = <0x021a0000 0x4000>;
            [...]
        };
    };
};

在前面的例子中,其父节点在compatible属性中具有simple-bus的子节点将被注册为平台设备。你还可以看到I2C和SPI总线控制器如何通过设置#size-cells = <0>;因为这与他们无关。查找binding信息的一个众所周知的位置是在内核设备树的文档中:documentation /devicetree/bindings/。

posted @ 2023-03-12 17:55  闹闹爸爸  阅读(119)  评论(0编辑  收藏  举报