设备树和Platform架构--1--设备树基础

本小节讲解设备树相关的基础知识。

参考链接:
https://www.cnblogs.com/liuwanpeng/p/7421661.html

设备数语法参考链接:
https://elinux.org/Device_Tree_Usage

设备树(dts)是节点和属性的树型结构。属性即为键-值对,节点包含子节点和属性



描述上图属性结构的示例dts为:

##########################################
//root根节点
/ {
    compatible = "acme,coyotes-rev“; //厂商 型号
       // 父节点的address-cells和size-cells决定子节点的reg的address和lenth字段的长度,cell的单位为32bit
    #address-cells = <1>;
    #size-cells = <1>;
    interrupt-parent = <&intc>;

      //根节点的子节点
    cpus {
      #address-cells = <1>; //下一级节点reg的address字段长度为1个32bit,下一级节点为cpu@0
      #size-cells = <0>;
      cpu@0 {
        compatible = "arm,cortex-a9";   // device兼容性,用于与driver匹配
        reg = <0>;
      };
      cpu@1 {
        compatible = "arm,cortex-a9";
        reg = <1>;
      };
   };
   serial@101f0000 {        // 地址
     compatible = "arm,pl011";
     reg = <0x101f0000 0x1000 >;
     interrupts = < 1 0 >;
   };
   serial@101f2000 {
     compatible = "arm,pl011";
     reg = <0x101f2000 0x1000 >;
     interrupts = < 2 0 >;
   };
    gpio@101f3000 {
     compatible = "arm,pl061";
     reg = <0x101f3000 0x1000
     0x101f4000 0x0010>;
     interrupts = < 3 0 >;
   };
   intc: interrupt-controller@10140000 {
     compatible = "arm,pl190";
     reg = <0x10140000 0x1000 >;
     interrupt-controller;
     #interrupt-cells = <2>;
   };
   spi@10115000 {
     compatible = "arm,pl022";
     reg = <0x10115000 0x1000 >;
     interrupts = < 4 0 >;50    };
   external-bus {
     #address-cells = <2>
     #size-cells = <1>;
     ranges = <0 0 0x10100000 0x10000 // Chipselect 1, Ethernet
           1 0 0x10160000 0x10000 // Chipselect 2, i2c controller
           2 0 0x30000000 0x1000000>; // Chipselect 3, NOR Flash

     ethernet@0,0 {
       compatible = "smc,smc91c111";
       reg = <0 0 0x1000>;
       interrupts = < 5 2 >;
     };
     i2c@1,0 {
       compatible = "acme,a1234-i2c-bus";
       #address-cells = <1>;
       #size-cells = <0>;
       reg = <1 0 0x1000>;
       interrupts = < 6 2 >;
       rtc@58 {
         compatible = "maxim,ds1338";
         reg = <58>;
         interrupts = < 7 3 >;
       };
    };
    flash@2,0 {
      compatible = "samsung,k8f1315ebm", "cfi-flash";
      reg = <2 0 0x4000000>;
    };
  };// end of external-bus
};
##########################################

(1)compatible 指定唯一的系统名称 包含 生产厂商 模型 <manufacturer>,<model>
compatible是一个关键字,操作系统使用次关键字决定哪一个设备驱动绑定哪一个设备

(2)节点

1)每个节点必须有自己的名字,格式: <name>[@<unit-address>]

<name>是简单的ascii字符串,最长31个字节。一般节点根据它所代表的设备来命名。

unit-address代表设备所在的地址。一般,单元地址是设备访问时的首地址,并且在节点的寄存器属性中列出。

2)设备节点采用的格式
<name>[@<unit-address>]
[]为可选项。挂到内存空间的设备,unit-address一般是内存地址。
3)可以为节点创建标签,别的地方引用时可以用标签。

ps7_gpio_0: ps7-gpio@e000a000 {    // 标签 : 节点
    #gpio-cells = <2>;
    clocks = <&clkc 42>;
    compatible = "xlnx,zynq-gpio-1.0";
    emio-gpio-width = <64>;
    gpio-controller ;
    gpio-mask-high = <0x0>;
    gpio-mask-low = <0x5600>;
    interrupt-parent = <&ps7_scugic_0>;
    interrupts = <0 20 4>;
    reg = <0xe000a000 0x1000>;
} ;

ps7_ethernet_0: ps7-ethernet@e000b000 {
    ...
    enet-reset = <&ps7_gpio_0 11 0>;  // 引用标签
    ...
} ;

 

(3)设备
系统中的每个设备用设备树中的节点表示。

(4)编址方式
可编址设备使用以下属性编址地址信息到设备树
1)reg
reg = <address1 length1 [address2 length2] [address3 length3] ... >
每个域代表该设备使用的地址范围

2) #address-cells和 #size-cells
每个地址占用的32bits个数(1个或多个32bits) 称为cells
每个长度占用的32bits个数(0个或多个32bits) 称为cells
在当前节点中#address-cells和#size-cells properties用于表示各自使用cells的个数

ps7_axi_interconnect_0: amba@0 {
    #address-cells = <1>;  // 下一级节点reg的address字段长度为1个32bit
    #size-cells = <1>;   // 下一级节点reg的len字段长度为1个32bit

    ...
    ps7_i2c_1: ps7-i2c@e0005000 {
        clock-frequency = <400000>;
        clocks = <&clkc 39>;
        compatible = "cdns,i2c-r1p10";
        interrupt-parent = <&ps7_scugic_0>;
        interrupts = <0 48 4>;
        reg = <0xe0005000 0x1000>;  // 地址为0xe0005000,长度为0x1000
        xlnx,has-interrupt = <0x0>;
        #address-cells = <1>;  // 下一级eeprom的reg属性
        #size-cells = <0>;   // 没有len字段
        eeprom@52 {
            compatible = "at,24c512";
            reg = <0x52>;    // 地址为0x52,没有长度字段
        };
    };
    ...
};


(5)CPU编址
在CPU节点中,#address-cells设置为1,#size-cells设置为0,这表明子reg值为单个uint32代表地址域,没有长度域。In this case, the two cpus are assigned addresses 0 and 1. #size-cells is 0 for cpu nodes because each cpu is only assigned a single address.

cpus {
    #address-cells = <1>;
    #size-cells = <0>;
    cpu@0 {
        compatible = "arm,cortex-a9";
        reg = <0>;
    };

    cpu@1 {
        compatible = "arm,cortex-a9";
        reg = <1>;
    };
};

(6)内存映射的设备
内存映射的设备需要分配一个地址范围

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

    ...

    serial@101f0000 {
        compatible = "arm,pl011";
        reg = <0x101f0000 0x1000 >;
    };

    serial@101f2000 {
        compatible = "arm,pl011";
        reg = <0x101f2000 0x1000 >;
    };

    gpio@101f3000 {
        compatible = "arm,pl061";
        reg = <0x101f3000 0x1000
        0x101f4000 0x0010>;
    };

    interrupt-controller@10140000 {
        compatible = "arm,pl190";
        reg = <0x10140000 0x1000 >;
    };

    spi@10115000 {
        compatible = "arm,pl022";
        reg = <0x10115000 0x1000 >;
    };

    ...

};

每个设备将分配一个基地址,同时指定一个地址范围的大小。在此例中,GPIO设备地址分配了2个地址域,0x101f3000...0x101f3fff 和0x101f4000..0x101f400f.

external-bus {
    #address-cells = <2>;
    #size-cells = <1>;

    ethernet@0,0 {
        compatible = "smc,smc91c111";
        reg = <0 0 0x1000>;
    };

    i2c@1,0 {
        compatible = "acme,a1234-i2c-bus";
        reg = <1 0 0x1000>;
        rtc@58 {
            compatible = "maxim,ds1338";
        };
    };

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

external-bus地址值使用2个cells:1个用于芯片选择,另一个用于当前选择chip的基地址偏移。长度域使用1个cell,因为只有offset需要偏移。因此,每个reg包含3个cells:the chipselect number, the offset, and the length.

(7)非内存映射的设备
They can have address ranges, but they are not directly accessible by the CPU. Instead the parent device's driver would perform indirect access on behalf of the CPU.


i2c@1,0 {
    compatible = "acme,a1234-i2c-bus";
    #address-cells = <1>;
    #size-cells = <0>;
    reg = <1 0 0x1000>;
    rtc@58 {
        compatible = "maxim,ds1338";
        reg = <58>;
    };
};



(8)ranges(地址转换)
在本地设备树节点上分配设备地址,如何映射使CPU能够访问这些地址???
1)The root node always describes the CPU's view of the address space. Child nodes of the root are already using the CPU's address domain, and so do not need any explicit mapping.
根节点已经描述了CPU可以访问的地址范围,根节点的子节点常常使用CPU的地址域,无需特殊映射。

For example, the serial@101f0000 device is directly assigned the address 0x101f0000.

2)Nodes that are not direct children of the root do not use the CPU's address domain. In order to get a memory mapped address the device tree must specify how to translate addresses from one domain to another. The ranges property is used for this purpose.
未直接连接到根节点的子节点不能够使用CPU的地址域空间。为了获得内存映射的地址,设备树必须指定如何进行地址转换。

ranges属性用于此目的。

/dts-v1/;

/ {
    compatible = "acme,coyotes-revenge";
    #address-cells = <1>;
    #size-cells = <1>;
    ...
    external-bus {
        #address-cells = <2>
        #size-cells = <1>;
        ranges = <0 0 0x10100000 0x10000 // Chipselect 1, Ethernet
                    1 0 0x10160000 0x10000 // Chipselect 2, i2c controller
                    2 0 0x30000000 0x1000000>; // Chipselect 3, NOR Flash

        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>;
        rtc@58 {
            compatible = "maxim,ds1338";
            reg = <58>;
        };
    };

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

ranges是一个地址转换列表
Each entry in the ranges table is a tuple containing the child address, the parent address, and the size of the region in the child address space.

• Offset 0 from chip select 0 is mapped to address range 0x10100000..0x1010ffff
• Offset 0 from chip select 1 is mapped to address range 0x10160000..0x1016ffff
• Offset 0 from chip select 2 is mapped to address range 0x30000000..0x30ffffff

1)ranges属性值的格式 <local地址, parent地址, size>, 表示将local地址向parent地址的转换。

比如对于#address-cells和#size-cells都为1的话,以<0x0  0x10 0x20>为例,表示将local的从0x0~(0x0 + 0x20)的地址空间映射到parent的0x10~(0x10 + 0x20)

 

其中,local地址的个数取决于当前含有ranges属性的节点的#address-cells属性的值,size取决于当前含有ranges属性的节点的#size-cells属性的值。

parent地址的个数取决于当前含有ranges属性的节点的parent节点的#address-cells的值。

 

2)对于含有ranges属性的节点的子节点来说,其reg都是基于local地址

 

3)ranges属性值为空的话,表示1:1映射

 

4)对于没有ranges属性的节点,代表不是memory map区域


(8)中断连接
• interrutt-controller,属性为空,表明该节点设备接受中断信号
• #interrupt-cells,表明连接此中断控制器的设备的中断属性的cell大小,也就是interrupt = <>属性的cell大小
• interrupt-parent,设备节点通过这个关键字指定其依附的中断控制器phandle,如果没有指定,则继承父节点的interrupt-parent配置
• interrupts,设备节点里使用,一般包含中断号、触发方法 等。具体有多少个cell,由#interrupt-cells决定,每个cell的具体含义,一般由驱动的实决定,一般会在绑定文件里说明,下面是 arm interrupt的绑定文件:3个cell,1st是种类(spi/ppi),2st是中断号,3st是flag


ps7_scugic_0: ps7-scugic@f8f01000 {
    #address-cells = <2>;   
    #interrupt-cells = <3>;   // 设备节点的interrupt属性有3个cells
    #size-cells = <1>;
    compatible = "arm,cortex-a9-gic", "arm,gic";
    interrupt-controller ;   // 我是中断控制器
    num_cpus = <2>;
    num_interrupts = <96>;
    reg = <0xf8f01000 0x1000>, <0xf8f00100 0x100>;
} ;

ps7_i2c_1: ps7-i2c@e0005000 {
    clock-frequency = <400000>;
    clocks = <&clkc 39>;
    compatible = "cdns,i2c-r1p10";
    interrupt-parent = <&ps7_scugic_0>;
    interrupts = <0 48 4>;   // 0,spi中断;48号中断,4表示高电平触发
    reg = <0xe0005000 0x1000>;
    xlnx,has-interrupt = <0x0>;
    #address-cells = <1>;
    #size-cells = <0>;
    eeprom@52 {
        compatible = "at,24c512";
        reg = <0x52>;
    };
} ;  





/dts-v1/;

/ {
compatible = "acme,coyotes-revenge";
#address-cells = <1>;
#size-cells = <1>;
interrupt-parent = <&intc>;


intc: interrupt-controller@10140000 {
compatible = "arm,pl190";
reg = <0x10140000 0x1000 >;
interrupt-controller;
#interrupt-cells = <2>;
};

spi@10115000 {
compatible = "arm,pl022";
reg = <0x10115000 0x1000 >;
interrupts = < 4 0 >;
};

external-bus {
#address-cells = <2>
#size-cells = <1>;
ranges = <0 0 0x10100000 0x10000 // Chipselect 1, Ethernet
1 0 0x10160000 0x10000 // Chipselect 2, i2c controller
2 0 0x30000000 0x1000000>; // Chipselect 3, NOR Flash

ethernet@0,0 {
compatible = "smc,smc91c111";
reg = <0 0 0x1000>;
interrupts = < 5 2 >;
};

i2c@1,0 {
compatible = "acme,a1234-i2c-bus";
#address-cells = <1>;
#size-cells = <0>;
reg = <1 0 0x1000>;
interrupts = < 6 2 >;
rtc@58 {
compatible = "maxim,ds1338";
reg = <58>;
interrupts = < 7 3 >;
};
};

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


Some things to notice:
• The machine has a single interrupt controller, interrupt-controller@10140000.
这个board只有一个中断控制器interrupt-controller@10140000

• The label 'intc:' has been added to the interrupt controller node, and the label was used to assign a phandle to the interrupt-parent property in the root node. This interrupt-parent value becomes the default for the system because all child nodes inherit it unless it is explicitly overridden.
label "intc:"加到中断控制器的节点上,用于根节点的interrupt-parent属性分配一个phandle。interrupt-parent值作为系统的默认值。
• Each device uses an interrupt property to specify a different interrupt input line.
每个设备使用interrupts属性指定不同的中断输入线
• #interrupt-cells is 2, so each interrupt specifier has 2 cells. This example uses the common pattern of using the first cell to encode the interrupt line number, and the second cell to encode flags such as active high vs. active low, or edge vs. level sensitive. For any given interrupt controller, refer to the controller's binding documentation to learn how the specifier is encoded.
#interrupt-cells为2,说明每个特定的中断有2个域:first cell 表明中断线号 second cell表明中断触发标志


对于平台设备而言,可通过以下API获取哪个中断
int platform_get_irq()
platform_get_irq_ny_name()


of_property_read_string()
of_property_read_u32()

 

(9)设备特定的数据
每个设备可以指定特定的数据,设备驱动可以解析这些数据



(10)特殊节点

aliases Node节点(别名)
特定的设备节点正常情况下是需要全路径引用的。例如 /external-bus/ethernet@0,0,这种获取设备的方式非常的繁琐。
aliases节点用于为设备的全路径分配一个别名。例如:

aliases {
    ethernet0 = &eth0;
    serial0 = &serial0;
};


当为设备分配标号时,Linux os常用别名aliases

chosen Node节点

The chosen node doesn't represent a real device, but serves as a place for passing data between firmware and the operating system, like boot arguments. Data in the chosen node does not represent the hardware. Typically the chosen node is left empty in .dts source files and populated at boot time.
In our example system, firmware might add the following to the chosen node:

chosen {
    bootargs = "root=/dev/nfs rw nfsroot=192.168.1.1 console=ttyS0,115200";
};

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值