linux Linux 3.x的设备树(Device Tree) dts 介绍

2023-05-16

在http://blog.csdn.net/21cnbao/article/details/8457546 进行整理修改,感谢此博主。

1. 

1.1. 简介

  Linus Torvalds2011317日的ARM Linux邮件列表宣称“this whole ARM thing is a f*cking pain in the ass”,引发ARM Linux社区的地震,随后ARM社区进行了一系列的重大修正, 在Linux 2.6中,ARM架构的板极硬件细节过多地被硬编码在arch/arm/plat-xxxarch/arm/mach-xxx,采用Device Tree后,许多硬件的细节可以直接透过它传递给Linux,而不再需要在kernel中进行大量的冗余编码。Device Tree 由一系列被命名的结点(node)和属性(property)组成,而结点本身可包含子结点。所谓属性,其实就是成对出现的 name 和 value。在 Device Tree .dts 文件是一种 ASCII 文本格式的 Device Tree 描述,

1.2. 路径:在 ARM Linux 在,一个.dts 文件对应一个 ARM 的 machine,一般放置在内核的 arch/arm/boot/dts/目录。 目前在 dts 中想使用到的宏定义都放在 include/dt-bindings/目录下。

&i2c0{ //挂载 i2c0 总线上

sensor@1d { //sensori2c 地址

compatible = "gs_mma8452"; //设备 ID 

reg = <0x1d>; //设备地址

type = <SENSOR_TYPE_ACCEL>; //sensor 类型,accel 为 gsensor

irq-gpio = <&gpio0 GPIO_B7 IRQ_TYPE_EDGE_FALLING>; //中断 GPIO

irq_enable = <1>; //使用中断

poll_delay_ms = <30>; //轮询时间

layout = <4>; //方向矩阵,1-8

};

};

 

1.3.  驱动中如何获取 dts 信息:

static struct of_device_id sensor_dt_ids[] = {

{ .compatible = "gs_mma8452" }, //这边定义必须和 dts 中定义的一样。

}

static struct i2c_driver sensor_driver = {

.probe = sensor_probe,

.remove = sensor_remove,

.shutdown = sensor_shut_down,

.id_table = sensor_id,

.driver = {

.owner = THIS_MODULE,

.name = "sensors",

.of_match_table = of_match_ptr(sensor_dt_ids), //3.10 中增加的,必须要有

}

 

1.4. 然后就可以进入到probe,在由probe进行识别:

Probe()

{ of_property_read_u32(np,"type",&(pdata->type));

}

2. 常用 OF_API

struct device_node *of_find_compatible_node(struct device_node *from,

const char *type, const char *compatible);

根据 compatible 属性,获得设备结点。遍历 Device Tree 中所有的设备结点,看看哪个结

点的类型、compatible 属性与本函数的输入参数匹配,大多数情况下,fromtype 为 NULL

int of_property_read_u8_array(const struct device_node *np,

const char *propname, u8 *out_values, size_t sz);

int of_property_read_u16_array(const struct device_node *np,

const char *propname, u16 *out_values, size_t sz);

int of_property_read_u32_array(const struct device_node *np,

const char *propname, u32 *out_values, size_t sz);int of_property_read_u64(const struct device_node *np, const char

*propname, u64 *out_value);

读取设备结点 np 的属性名为 propname,类型为8163264位整型数组的属

性。对于32位处理器来讲,最常用的是 of_property_read_u32_array()

有些情况下,整形属性的长度可能为1,于是内核为了方便调用者,又在上述 API

的基础上封装出了更加简单的读单一整形属性的 API,它们为

int of_property_read_u8();

Int of_property_read_u16();

Int of_property_read_u30();

int of_property_read_string(struct device_node *np, const char

*propname, const char **out_string);

int of_property_read_string_index(struct device_node *np, const char

*propname, int index, const char **output);

前者读取字符串属性,后者读取字符串数组属性中的第 index 个字符串。

of_get_named_gpio_flags

 

 

3.  

3.1. pin configuration 表示管脚的复用。

3.2. 配置参数包括:pull-up/down电阻的设定, tri-state设定,drive-strength的设定。

3.3. pin controller这个HW block需要是device tree中的一个节点这些device(我们称pin controllerhost,那么这些使用pin controller进行引脚配置的device叫做client device)也需要在它自己的device tree node中描述pin control的相关内容

3.4. 例如:

 device-node-name {  
        定义该device自己的属性  

        pinctrl-names = "sleep", "active";------(1 
        pinctrl-0 = <pin-config-0-a>;--------------(2 
        pinctrl-1 = <pin-config-1-a pin-config-1-b>;         
    };

pinctrl-names定义了一个state列表当设备active的时候,我们需要pin controller将相关的一组pin设定为具体的设备功能,而当设备进入sleep状态的时候,需要pin controller将相关的一组pin设定为普通GPIO>>>> state有两种标识,一种就是pinctrl-names定义的字符串列表,另外一种就是ID,这里当state ID等于0(名字是active)的state对应pinctrl-0属性,state ID等于1(名字是idle)的state对应pinctrl-1属性。

3.5. pinctrl-x的定义。pinctrl-x是一个句柄(phandle)列表,每个句柄指向一个pin configuration

 上面的如在active的时候,I2C功能有两种配置,一种是从pin ID{7,8}引出,另外一个是从pin ID{69,103}引出。

 

4. arch/arm/boot/dts/rk312x.dtsi 这个文件很多管脚定义复用功能。

4.1. Linux内核为了简化,把SoC公用的部分或者多个machine共同的部分一般提炼为.dtsi然后在对应的你机器的.dtsinclude这个.dtsi

例如

/ {

        compatible = "rockchip,rk312x";

        rockchip,sram = <&sram>;

        interrupt-parent = <&gic>;

 

        aliases {

                serial0 = &uart0;

                serial1 = &uart1;

                serial2 = &uart2;

                i2c0 = &i2c0;

                i2c1 = &i2c1;

                i2c2 = &i2c2;

                i2c3 = &i2c3;

                lcdc = &lcdc;

                spi0 = &spi0;

        };

    uart0: serial@20060000 {

compatible = "rockchip,serial";

reg = <0x20060000 0x100>;

interrupts = <GIC_SPI 20 IRQ_TYPE_LEVEL_HIGH>;

clock-frequency = <24000000>;

clocks = <&clk_uart0>, <&clk_gates8 0>;

clock-names = "sclk_uart", "pclk_uart";

reg-shift = <2>;

reg-io-width = <4>;

dmas = <&pdma 2>, <&pdma 3>;

#dma-cells = <2>;

pinctrl-names = "default";

pinctrl-0 = <&uart0_xfer &uart0_cts &uart0_rts>;

status = "okay";

};

i2c0: i2c@20072000 {

compatible = "rockchip,rk30-i2c";

reg = <0x20072000 0x1000>;

interrupts = <GIC_SPI 24 IRQ_TYPE_LEVEL_HIGH>;

#address-cells = <1>;

#size-cells = <0>;

pinctrl-names = "default", "gpio";

pinctrl-0 = <&i2c0_sda &i2c0_scl>;

pinctrl-1 = <&i2c0_gpio>;

gpios = <&gpio0 GPIO_A1 GPIO_ACTIVE_LOW>, <&gpio0 GPIO_A0 

               GPIO_ACTIVE_LOW>;

clocks = <&clk_gates8 4>;

rockchip,check-idle = <1>;

status = "disabled";

};

spi0: spi@20074000 {

compatible = "rockchip,rockchip-spi";

reg = <0x20074000 0x1000>;

interrupts = <GIC_SPI 23 IRQ_TYPE_LEVEL_HIGH>;

#address-cells = <1>;

#size-cells = <0>;

pinctrl-names = "default";

pinctrl-0 = <&spi0_txd_mux0 &spi0_rxd_mux0 &spi0_clk_mux0 &spi0_cs0_mux0 

                  &spi0_cs1_mux0>;

//pinctrl-0 = <&spi0_txd_mux1 &spi0_rxd_mux1 &spi0_clk_mux1 &spi0_cs0_mux1 

                   &spi0_cs1_mux1>;

//pinctrl-0 = <&spi0_txd_mux2 &spi0_rxd_mux2 &spi0_clk_mux2 &spi0_cs0_mux2>;

rockchip,spi-src-clk = <0>;

num-cs = <2>;

clocks =<&clk_spi0>, <&clk_gates7 12>;

clock-names = "spi","pclk_spi0";

dmas = <&pdma 8>, <&pdma 9>;

#dma-cells = <2>;

dma-names = "tx", "rx";

status = "disabled";

};

fb: fb{

compatible = "rockchip,rk-fb";

rockchip,disp-mode = <ONE_DUAL>;

};

 

rk_screen: rk_screen{

compatible = "rockchip,screen";

};

 

lvds: lvds@20038000 {

compatible = "rockchip,rk31xx-lvds";

reg = <0x20038000 0x4000>, <0x101100b0 0x01>;

reg-names = "mipi_lvds_phy", "mipi_lvds_ctl";

clocks = <&clk_gates5 0>, <&clk_gates9 6>, <&clk_gates9 5>;

clock-names = "pclk_lvds", "pclk_lvds_ctl", "hclk_vio_h2p";

status = "disabled";

};

 

lcdc: lcdc@1010e000 {

compatible = "rockchip,rk312x-lcdc";

rockchip,prop = <PRMRY>;

reg = <0x1010e000 0x1000>;

interrupts = <GIC_SPI 9 IRQ_TYPE_LEVEL_HIGH>;

clocks = <&clk_gates6 0>, <&dclk_lcdc0>, <&clk_gates6 1>, <&sclk_lcdc0>, <&pd_vop>, 

               <&clk_cpll>;

clock-names = "aclk_lcdc", "dclk_lcdc", "hclk_lcdc", "sclk_lcdc", "pd_lcdc", "sclk_pll";

rockchip,iommu-enabled = <1>;

status = "disabled";

};

 

4.2. 刚开始有一个单独的根节点: 用”/”表示,

4.3. compatible=”manufacturer,model”;例如compatible = "rockchip,rk312x";    //compatible指定了系统的名称,包含制造商以避免命名空间冲突。操作系统将使用compatible值来决定如何在设备上运行

  

4.4. 多个子节点:如:

lvds: lvds@20038000 {}

lcdc: lcdc@1010e000 {}

4.5.  节点1的一对子节点:“child-node1”和”child-node2”

4.6. 分散于树形结构当中的一些属性,属性是简单的键值对,此处的值可以为 

 空,也可以包括任意的字节流。当数据类型没有被编进数据结构时,会有一些基础数据表示法能够在device tree源文件中进行表达。

4.7. 双引号  表示文本串, compatible = "rockchip,rk312x-lcdc"

4.8. 尖括号 分隔的表示32 bit无符号整数  reg = <0x1010e000 0x1000>;

方括号 表示二进制:a  binary-property = [0x01 0x23 0x45 0x67];

 

4.9. 逗号   可以将不同示意的数据可以用串联在一起  ,也用来创建字符串列表 

          a mixed-property = "a string", [0x01 0x23 0x45 0x67], <0x12345678>;

          a  string-list = "red fish", "blue fish";

 

5. 例子:

 

/ {          这个是根节点

    compatible = "acme,coyotes-revenge";  #定义了厂家和型号

    #address-cells = <1>;//决定了serialgpiospi等结点的address字段的长度为1

#size-cells = <1>;//决定了serialgpiospi等结点的length字段的长度为1

/*

上面这两行表示父结点reg属性,它们分别决定了子结点的reg属性的addresslength字段的长度如果<0>:address 和 length 字段是可变长的

reg的组织形式为reg = <address1 length1 [address2 length2] [address3 length3] ... >

*/

 

    interrupt-parent = <&intc>;

 

    cpus {   //定义了cpu,这里是双核cpu0cpu1, cortex-a9 32位处理器

        #address-cells = <1>; 

        #size-cells = <0>;

        cpu@0 { //子节点

            compatible = "arm,cortex-a9";

            reg = <0>;

        };

        cpu@1 { //子节点

            compatible = "arm,cortex-a9";

            reg = <1>;

        };

    };

 

    serial@101f0000 { //串口1,地址在101f0000

        compatible = "arm,pl011";

        reg = <0x101f0000 0x1000 >;

        interrupts = < 1 0 >;

    };

 

    serial@101f2000 { //串口2,地址在101f2000 

        compatible = "arm,pl011";

        reg = <0x101f2000 0x1000 >;

        interrupts = < 2 0 >;

    };

 

    gpio@101f3000 { //gpio控制器,地址101f3000

        compatible = "arm,pl061";

        reg = <0x101f3000 0x1000

               0x101f4000 0x0010>;

        interrupts = < 3 0 >;

    };

 

    intc: interrupt-controller@10140000 {  //中断控制器(位于0x10140000

        compatible = "arm,pl190";

        reg = <0x10140000 0x1000 >;

        interrupt-controller;

        #interrupt-cells = <2>;

/*#interrupt-cell-这是中断控制器节点的一个属性。它代表此中断控制器的interrupt specifier有多少cells*/

    };

 

    spi@10115000 {  //SPI控制器(位于0x10170000

        compatible = "arm,pl022";

        reg = <0x10115000 0x1000 >;

        interrupts = < 4 0 >;

    };

 

    external-bus { //external bus它分别接了1) Ethernet, 2) I2C控制器, 3) 64MB NOR Flash

        #address-cells = <2>

        #size-cells = <1>;

/*

上面这两个决定了下面1) Ethernet, 2) I2C控制器3flash reg属性。

将下面的reg记录如下:

reg = <0  0  0x1000>;

reg = <1  0  0x1000>;

reg = <2  0  0x4000000>;

0.1.2 address绿色表示的是片选,0.0.0表示的是该片选的基地址。褐色0x1000,0x1000,0x4000000表示的是长度。

总结:reg = <address1 length1 [address2 length2] [address3 length3]>;

    父类address-cellssize-cells决定了子类的相关属性要包含多少个cell,如果子节点有特殊需求的话,可以自己再定义,这样就可以摆脱父节点的控制。
address-cells决定了address1/2/3包含几个cellsize-cells决定了length1/2/3包含了几个cell

   由父节点#address-cells = <2> + #size-cells = <1>; 可知,现在公有3个节点,分别表示片选序号,偏移量,地址空间长度,

*/

        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>;

/*别要留意的是i2c结点中定义的 #address-cells = <1>;#size-cells = <0>;又作用到了I2C总线上连接的RTC因为RTC设备只是被分配在一个地址上,不需要其他任何空间,所以只需要一个addresscell就可以描述完整,不需要size-cells它的address字段为0x58,是设备的I2C地址。*/

            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>;

        };

    };

};

 

 //root结点的子结点描述的是CPU的视图,因此root子结点的address区域就直接位于CPUmemory区域但是,经过总线桥后的address往往需要经过转换才能对应的CPUmemory映射。external-busranges属性定义了经过external-bus桥后的地址范围如何映射到CPUmemory区域。见上面的:

/ {  

     compatible = "acme,coyotes-revenge";  

     #address-cells = <1>;  

#size-cells = <1>;  

interrupt-parent = <&intc>;   

    ..................

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 {

......

 }

   i2c@1,0 {

.....

         }

 flash@2,0 {

....

          }

     }

}

 

ranges是地址转换表,每个项目是一个子地址、父地址以及在子地址空间的大小的映射

父节点:#address-cells = <1>  #size-cells = <1>;  

子节点:#address-cells = <2>  #size-cells = <1>;

    对于本例而言,子地址空间的#address-cells2父地址空间的#address-cells值为1因此0 0  0x10100000   0x100002cellexternal-bus后片选0上偏移03cell表示external-bus后片选0上偏移0的地址空间被映射到CPU0x10100000位置,4cell表示映射的大小为0x10000ranges的后面2个项目的含义可以类推。

.dts文件的每个设备,都有一个compatible 属性,compatible属性用户驱动和设备的绑定。

 

6. DTC (device tree compiler) :

.dts编译为.dtb的工具

7. Device Tree Blob (.dtb)  

    .dtb.dtsDTC编译后的二进制格式的Device Tree描述,可由Linux内核解析。通常在我们制作NANDSD启动image时,会为.dtb文件单独留下一个很小的区域以存放之,之后bootloader在引导kernel的过程中,会先读取该.dtb到内存。

 

8. Binding

    对于Device Tree中的结点和属性具体是如何来描述设备的硬件细节的,一般需要文档来进行讲解,文档的后缀名一般为.txt。这些文档位于内核的Documentation/devicetree/bindings目录,其下又分为很多子目录。

 

9. Bootloader

     Uboot mainline  v1.1.3开始支持Device Tree,其对ARM的支持则是和ARM内核支持Device Tree同期完成。为了使能Device Tree,需要编译Uboot的时候在config文件中加入
#define CONFIG_OF_LIBFDT

   在Uboot中,可以从NANDSD或者TFTP等任意介质.dtb读入内存,假设.dtb放入的内存地址为0x71000000,之后可在Uboot运行命令fdt addr命令设置.dtb的地址,如:
U-Boot> fdt addr 0x71000000

10.   uboot启动内核:

      bootz  kernel_addr  initrd_address   dtb_address的命令。第一个参数为内核映像的地址,第二个参数为initrd的地址,若不存在initrd,可以用 -代替,第三个dtb_address作为bootz或者bootm的最后一次参数

 

11. kernel 

 

    过去,ARM Linux针对不同的电路板会建立由MACHINE_STARTMACHINE_END包围起来的针对这个machine的一系列callback

1. 373 MACHINE_START(VEXPRESS, "ARM-Versatile Express")  

2. 374         .atag_offset    = 0x100,  

3. 375         .smp            = smp_ops(vexpress_smp_ops),  

4. 376         .map_io         = v2m_map_io,  

5. 377         .init_early     = v2m_init_early,  

6. 378         .init_irq       = v2m_init_irq,  

7. 379         .timer          = &v2m_timer,  

8. 380         .handle_irq     = gic_handle_irq,  

9. 381         .init_machine   = v2m_init,  

10. 382         .restart        = vexpress_restart,  

11. 383 MACHINE_END  

 

在旧版的kerenl里,不同的machine会有不同的MACHINE IDUboot在启动Linux内核时会将MACHINE ID存放在r1寄存器,Linux启动时会匹配Bootloader传递的MACHINE IDMACHINE_START声明的MACHINE ID,然后执行相应machine的一系列初始化函数.

新版kernel引入Device Tree之后,MACHINE_START变更为DT_MACHINE_START其中含有一个.dt_compat成员,用于表明相关的machine.dtsroot结点的compatible属性兼容关系。如果Bootloader传递给内核的Device Treeroot结点的compatible属性出现在某machine.dt_compat表中,相关的machine就与对应的Device Tree匹配,从而引发这一machine的一系列初始化函数被执行

1.  static const char * const v2m_dt_match[] __initconst = {  

2. 490         "arm,vexpress",  

3. 491         "xen,xenvm",  

4. 492         NULL,  

5. 493 };  

6. 495 DT_MACHINE_START(VEXPRESS_DT, "ARM-Versatile Express")  

7. 496         .dt_compat      = v2m_dt_match,  

8. 497         .smp            = smp_ops(vexpress_smp_ops),  

9. 498         .map_io         = v2m_dt_map_io,  

10. 499         .init_early     = v2m_dt_init_early,  

11. 500         .init_irq       = v2m_dt_init_irq,  

12. 501         .timer          = &v2m_dt_timer,  

13. 502         .init_machine   = v2m_dt_init,  

14. 503         .handle_irq     = gic_handle_irq,  

15. 504         .restart        = vexpress_restart,  

16. 505 MACHINE_END  

 

 

如:瑞芯微的rk3128:

DT_MACHINE_START(RK3128_DT, "Rockchip RK3128")

        .smp            = smp_ops(rockchip_smp_ops),

        .map_io         = rk3128_dt_map_io,

        .init_time      = rk312x_dt_init_timer,

        .dt_compat      = rk3128_dt_compat,

        .init_late      = rk312x_init_late,

        .reserve        = rk312x_reserve,

        .restart        = rk312x_restart,

MACHINE_END

 

static const char * const rk3128_dt_compat[] __initconst = {

        "rockchip,rk3128",

        NULL,

};

 

这里刚好对应了:arch/arm/boot/dts/rk3128-86v.dts

/ {

      compatible = "rockchip,rk3128";

}

 

12.  

       Linux倡导针对多个SoC、多个电路板的通用DT machine,即一个DT machine.dt_compat表含多个电路板.dts文件的root结点compatible属性字符串.

比如上面的这个可以设计成这样:

static const char * const rk3128_dt_compat[] __initconst = {

        "rockchip,rk3128",

"rockchip,rk3126",

        NULL,

};

 

它的.init_machine成员函数就针对不同的machine进行了不同的分支处理:

1. 126 static void __init exynos5_dt_machine_init(void)  

2. 127 {  

3. 128         …  

4. 149  

5. 150         if (of_machine_is_compatible("samsung,exynos5250"))  

6. 151                 of_platform_populate(NULL, of_default_bus_match_table,  

7. 152                                      exynos5250_auxdata_lookup, NULL);  

8. 153         else if (of_machine_is_compatible("samsung,exynos5440"))  

9. 154                 of_platform_populate(NULL, of_default_bus_match_table,  

10. 155                                      exynos5440_auxdata_lookup, NULL);  

11. 156 }  

 

13. 驱动与设备如何匹配从而进入probe

     使用Device Tree后,驱动需要与.dts中描述的设备结点进行匹配,从而引发驱动的probe()函数执行。对于platform_driver而言,需要添加一个OF匹配表,如前文的.dts文件的"acme,a1234-i2c-bus"兼容I2C控制器结点的OF匹配表.

例如:i2c匹配:

旧版在board需要 定义为:

1.  static struct i2c_board_info __initdata afeb9260_i2c_devices[] = {  

2. 146         {  

3. 147                 I2C_BOARD_INFO("tlv320aic23", 0x1a),  

4. 148         }, {  

5. 149                 I2C_BOARD_INFO("fm3130", 0x68),  

6. 150         }, {  

7. 151                 I2C_BOARD_INFO("24c64", 0x50),  

8. 152         },  

9. 153 };  

而新版采样dts:

1. i2c@1,0 {  

2.       compatible = "acme,a1234-i2c-bus";  

3.       …  

4.       rtc@58 {  

5.           compatible = "maxim,ds1338";  

6.           reg = <58>;  

7.           interrupts = < 7 3 >;  

8.       };  

9.   };  

 

驱动里面OF匹配表.

1.  static const struct of_device_id a1234_i2c_of_match[] = {  

2. 437         { .compatible = "acme,a1234-i2c-bus ", },  

3. 438         {},  

4. 439 };  

5. 440 MODULE_DEVICE_TABLE(of, a1234_i2c_of_match);  

6. 441  

7. 442 static struct platform_driver i2c_a1234_driver = {  

8. 443         .driver = {  

9. 444                 .name = "a1234-i2c-bus ",  

10. 445                 .owner = THIS_MODULE,  

11. 449                 .of_match_table = a1234_i2c_of_match,  

12. 450         },  

13. 451         .probe = i2c_a1234_probe,  

14. 452         .remove = i2c_a1234_remove,  

15. 453 };  

16. 454 module_platform_driver(i2c_a1234_driver);  

 

名字manufacturer可以忽略: I2CSPI外设驱动和Device Tree中设备结点的compatible 属性还有一种弱式匹配方法,就是别名匹配。compatible 属性的组织形式为<manufacturer>,<model>,别名其实就是去掉compatible 属性中逗号前的manufacturer前缀。关于这一点,可查看drivers/spi/spi.c的源代码,函数spi_match_device()暴露了更多的细节,如果别名出现在设备spi_driverid_table里面,或者别名与spi_drivername字段相同,SPI设备和驱动都可以匹配上

1. static const struct spi_device_id *spi_match_id(const struct spi_device_id *id,  

2. 72                                                 const struct spi_device *sdev)  

3. 73 {  

4. 74         while (id->name[0]) {  

5. 75                 if (!strcmp(sdev->modalias, id->name))  

6. 76                         return id;  

7. 77                 id++;  

8. 78         }  

9. 79         return NULL;  

10. 80 } 

 

 

14. 常用OF API

    在LinuxBSP和驱动代码中,还经常会使用到Linux中一组Device TreeAPI,这些API通常被冠以of_前缀,它们的实现代码位于内核的drivers/of目录。这些常用的API包括:

int of_device_is_compatible(const struct device_node *device,const char *compat);

 

struct device_node {

const char *name;

const char *type;

phandle phandle;

const char *full_name;

 

struct property *properties;

struct property *deadprops; /* removed properties */

struct device_node *parent;

struct device_node *child;

struct device_node *sibling;

struct device_node *next; /* next device of same type */

struct device_node *allnext; /* next in list of all nodes */

struct proc_dir_entry *pde; /* this node's proc directory */

struct kref kref;

unsigned long _flags;

void *data;

#if defined(CONFIG_SPARC)

const char *path_component_name;

unsigned int unique_id;

struct of_irq_controller *irq_trans;

#endif

}; 

 

    驱动可以透过Bootloader传递给内核的Device Tree中的真正结点的compatible 属性以确定究竟是哪一种设备

 

 

15.  

clk_core_div: clk_core_div {

           compatible = "rockchip,rk3188-div-con";

           rockchip,bits = <0 5>;

           clocks = <&clk_core>;

           clock-output-names = "clk_core";

           rockchip,div-type = <CLK_DIVIDER_PLUS_ONE>;

            #clock-cells = <0>;

            rockchip,clkops-idx = <CLKOPS_RATE_CORE>;

            rockchip,flags = <(CLK_GET_RATE_NOCACHE |CLK_SET_RATE_NO_REPARENT)>;

};

 

of_device_is_compatible (node,"rockchip,rk3188-div-con"))

of_property_read_string (np, "clock-output-names",&divinfo->clk_name);

of_parse_phandle_with_args(np, "clocks", "#clock-cells", index, &clkspec);

of_property_read_string_index(clkspec.np,"clock-output-names",clkspec.args_count?clkspec.args[0] : 0, &clk_name) < 0)

of_property_read_u32(np, "rockchip,clkops-idx",&divinfo->clkops_idx);

   

16. 总结

    ARM社区一贯充斥的大量垃圾代码导致Linus盛怒,因此社区在2011年到2012年进行了大量的工作。ARM Linux开始围绕Device Tree展开,Device Tree有自己的独立的语法,它的源文件为.dts,编译后得到.dtbBootloader在引导Linux内核的时候会将.dtb地址告知内核。之后内核会展开Device Tree并创建和注册相关的设备,因此arch/arm/mach-xxxarch/arm/plat-xxx中大量的用于注册platformI2CSPI板级信息的代码被删除,而驱动也以新的方式和.dts中定义的设备结点进行匹配。

 

17. 例子:

17.1.  

#include <....>

/ { 

pinctrl: pinctrl@20008000 {        //总的

compatible = "rockchip,rk312x-pinctrl";

reg = <0x20008000 0xA8>,

      <0x200080A8 0x4C>,

      <0x20008118 0x20>,

      <0x20008100 0x04>;

reg-names = "base", "mux", "pull", "drv";

#address-cells = <1>;

#size-cells = <1>;

ranges;

 

gpio0: gpio0@2007c000 {

compatible = "rockchip,gpio-bank";

reg = <0x2007c000 0x100>;

interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;

clocks = <&clk_gates8 9>;

 

gpio-controller;

#gpio-cells = <2>;

 

interrupt-controller;

#interrupt-cells = <2>;

};

 

       gpio1: gpio1@20080000 {

compatible = "rockchip,gpio-bank";

reg = <0x20080000 0x100>;

interrupts = <GIC_SPI 37 IRQ_TYPE_LEVEL_HIGH>;

clocks = <&clk_gates8 10>;

 

gpio-controller;

#gpio-cells = <2>;

 

interrupt-controller;

#interrupt-cells = <2>;

};

..........//下面的是另一个复用的功能。

..........

gpio0_uart0 {

uart0_xfer: uart0-xfer {

rockchip,pins = <UART0_SIN>,

<UART0_SOUT>;

rockchip,pull = <VALUE_PULL_UPDOWN_DISABLE>;

};

uart0_cts: uart0-cts {

rockchip,pins = <UART0_CTSN>;

rockchip,pull = <VALUE_PULL_DEFAULT>;

};

uart0_rts: uart0-rts {

rockchip,pins = <UART0_RTSN>;

rockchip,pull = <VALUE_PULL_DEFAULT>;

};

uart0_rts_gpio: uart0-rts-gpio {

rockchip,pins = <FUNC_TO_GPIO(UART0_RTSN)>;

rockchip,pull = <VALUE_PULL_DEFAULT>;

};

};

..........

.........

 

18. dts配置后uboot的读取:(uboot也是读取kernel里面的dts配置文件)

18.1.  

struct rockchip_fb {

int node;

int lcdc_node;

int lcdc_id;

struct list_head pwrlist_head;

};

 

18.2. int rk_fb_parse_dt(struct rockchip_fb *rk_fb, const void *blob)

node = fdt_node_offset_by_compatible(blob, 0, "rockchip,rk-fb");

/* arch/arm/boot/dts/rk312x.dtsi:  管脚复用等很多都在这里定义

fb: fb{

                compatible = "rockchip,rk-fb";

                rockchip,disp-mode = <ONE_DUAL>;

        };

*/

 

 

 

 

 

 

 

 

 

       

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

linux Linux 3.x的设备树(Device Tree) dts 介绍 的相关文章

  • gdb 错误 - 文件不是可执行格式:无法识别文件格式

    我正在尝试使用 gdb 调试某个名为 xdf 的程序 但是当我运行 gdb xdf 时 出现以下错误 home nealtitusthomas X ray astronomy heasoft 6 24 x86 64 pc linux gnu
  • 如何使用ffmpeg重叠和合并多个音频文件?

    我正在尝试将多个音频文件合并到一个文件中 但我可以使用以下命令来连接 而不是连接 ffmpeg v debug i file1 wav i file2 wav i file3 wav filter complex 0 0 concat n
  • 如何仅将整个嵌套目录中的头文件复制到另一个目录,在复制到新文件夹后保持相同的层次结构

    我有一个目录 其中有很多头文件 h 和其他 o 和 c 文件以及其他文件 这个目录里面有很多嵌套的目录 我只想将头文件复制到一个单独的目录 并在新目录中保留相同的结构 cp rf oldDirectory newDirectory将复制所有
  • grails 上的同步块在 Windows 上有效,但在 Linux 上无效

    我有一个 grails 应用程序 它依赖于服务中的同步块 当我在 Windows 上运行它时 同步按预期工作 但当我在 ams linux 上运行时 会出现 StaleObjectStateException 该问题在以下示例中重现 cla
  • 如何在 Linux x86_64 上模拟 iret

    我正在编写一个基于 Intel VT 的调试器 由于当 NMI Exiting 1 时 iret 指令在 vmx guest 中的性能发生了变化 所以我应该自己处理vmx主机中的NMI 否则 guest会出现nmi可重入错误 我查了英特尔手
  • 使用命令行将 MediaWiki 维基文本格式转换为 HTML

    我倾向于编写大量文档 因此 MediaWiki 格式对我来说很容易理解 而且比编写传统 HTML 节省了我很多时间 然而 我也写了一篇博客 发现一直从键盘切换到鼠标来输入正确的 HTML 标签会增加很多时间 我希望能够使用 Mediawik
  • 是否有可能在linux中找到包含特定文本的文件?

    考虑这种情况 我在文件夹 Example 下有很多文件 如果我需要找到一个包含特定短语 如 Class Example 的文件 我该如何使用 Linux shell 来做到这一点 linux中有类似 定位 的函数可以做到这一点吗 Thank
  • linux下如何获取昨天和前天?

    我想在变量中获取 sysdate 1 和 sysdate 2 并回显它 我正在使用下面的查询 它将今天的日期作为输出 bin bash tm date Y d m echo tm 如何获取昨天和前天的日期 这是另一种方法 对于昨天来说 da
  • Gradle 1.3:build.gradle 不构建类

    这里有一个新问题 我有一个 build gradle 文件apply plugin java在其中 并与 java 项目 包关联 当我跑步时gradle build从命令行我得到 compileJava UP TO DATE process
  • CentOS目录结构是树形的吗?

    CentOS 上有相当于树的东西吗 如果你的 Centos 系统上没有安装 tree 无论如何我通常建议服务器设置使用最小安装磁盘 你应该在命令行中输入以下内容 yum install tree y 如果没有安装 那是因为您没有正确的存储库
  • 套接字:监听积压并接受

    listen sock backlog 在我看来 参数backlog限制连接数量 这是我的测试代码 server initialize the sockaddr of server server sin family AF INET ser
  • 如何调用位于其他目录的Makefile?

    我正在尝试这样做 我想打电话给 make Makefile存在于其他目录中 abc可以使用位于不同目录中的 shell 脚本的路径 我该怎么做呢 由于 shell 脚本不允许我cd进入Makefile目录并执行make 我怎样才能编写she
  • 非二叉树的中序树遍历

    对于比二叉树更宽的树 术语 中序遍历 是否有明确定义的含义 或者 前 和 后 顺序是唯一有意义的 DFS 类型吗 我的意思是与n每个节点 gt 2 个子节点 我猜是为了n这甚至可能意味着之后要转到 根 n 2孩子们 但这曾经这样使用过吗 那
  • numpy 未定义符号:PyFPE_jbuf

    我正在尝试使用一百万首歌曲数据集 为此我必须安装 python 表 numpy cython hdf5 numexpr 等 昨天我设法安装了我需要的所有内容 在使用 hdf5 遇到一些麻烦之后 我下载了预编译的二进制包并将它们保存在我的 b
  • 在 LINUX 上使用 Python 连接到 OLAP 多维数据集

    我知道如何在 Windows 上使用 Python 连接到 MS OLAP 多维数据集 嗯 至少有一种方法 通常我使用 win32py 包并调用 COM 对象进行连接 import win32com client connection wi
  • 如何在Python中独立于语言安装(linux)获取用户桌面路径

    我找到了 如何找到用户桌面的路径 的几个问题和答案 但在我看来它们都已失效 至少我找到的那些 原因是 如果用户安装的 Linux 不是英语 他或她的桌面很可能位于除 Desktop 例如 对于瑞典语 我相信它是在 Skrivbord 谁知道
  • 如何让“grep”从文件中读取模式?

    假设有一个很大的文本文件 我只想打印与某些模式不匹配的行 显然 我可以使用egrep v patter1 pattern2 pattern3 现在 如果所有这些模式都在一个文本文件中怎么办 最好的制作方法是什么egrep从文件中读取模式 g
  • 从多线程程序中调用 system()

    我们正在开发一个用 C 编写的多线程内存消耗应用程序 我们必须执行大量的 shellscript linux 命令 并获取返回码 读完之后article http www linuxprogrammingblog com threads a
  • 无需 cron 在后台发送邮件

    我想知道是否有一种方法可以运行 PHP 循环 以便在后台向订阅者发送几百封电子邮件 我的目标是格式化新闻通讯 单击发送 然后关闭浏览器或更改页面 当然 发送电子邮件的实际过程将在后台运行 不会因浏览器关闭而中断 我知道这可以通过 cron
  • 将 Access 数据库转换为 SQL Microsoft DTS - 数据类型“130”不在映射文件中

    我正在尝试将大型 Access mdb 数据库导出到 SQL Server 数据库 但遇到了 Microsoft DTS 无法识别 Access 数据库中特定类型字段的数据类型的问题 我查看了相关的访问表 它们被设置为长度为 1 的 文本

随机推荐

  • ROS学习(一):Navigation中GNSS与IMU数据融合定位

    1 参考博客 主要参考以下博客 感谢各位博主的分享 link https blog csdn net qinqinxiansheng article details 107108475 utm medium 61 distribute pc
  • msOS学习之路(1)

    msOS学习之路 xff08 1 xff09 1 msOS的初步认识 刚刚拿到msOS开发板的时候 xff0c 看了一下开发板 xff0c 感觉非常高端 xff0c 再看一下芯片 xff0c 用的是stm32 当时我就觉得我得先学习stm3
  • msOS学习之路(2)

    基于msOS自动回火机的实现 1 简介 1 1 背景 基于msOS自动回火机的实现的实例是学习msOS比较好的入门实例 xff0c 它包括msOS界面的设计 数据库的使用 系统节拍的使用 按键的使用 系统节拍使用等 xff0c 通过这个例子
  • msOS学习之路(4)

    设备层简单理解 1 设备层相关定义 设备层的相关定义是在device h文件中定义的 xff0c 包括按键 模拟量输入 数字量输入 输出枚举或者类型定义等 xff0c 对于一些结构体的理解 xff0c 例如 xff1a ADC结构体 xff
  • 【STM32】STM32CubeIDE HAL库Ring-buffer例程

    板子G474RE STM32HAL库Ring buffer使用 注意 xff1a HAL库中 xff0c 中断每执行一次 xff0c 就关闭 xff0c 所以需要重新开启中断 第一次开启在main函数中 HAL UART Receive I
  • 【ROS】多机协同ROS安装使用

    目录 通信框架ROS 安装ROS 测试ROS 控制协同 协同感知 通信框架ROS ROS是一种分布式软件框架 xff0c 节点之间通过松耦合的方式进行组合 xff0c 在很多应用场景下 xff0c 节点可以运行在不同的计算平台上 xff0c
  • 【无人机】PX4,ROS,Ubuntu20仿真运行

    参考 xff1a https docs px4 io master en ros mavros installation html https docs px4 io master en dev setup dev env linux ub
  • 【无人机】PX4,源码简要分析

    看看源码目的很单纯 xff0c 就是想有没有控制方面的代码可以移植到其它地方 无人机 PX4 xff0c ubuntu18仿真运行 从官网可能下载代码不全 xff0c 这是编译完成的源码 xff0c 有点大 xff01 xff01 xff0
  • 内核层读写应用层文件,使用filp_open函数。

    转载 xff1a http soft chinabyte com os 421 11398421 shtml 有时候需要在Linux kernel 大多是在需要调试的驱动程序 中读写文件数据 在kernel中操作文件没有标准库可用 xff0
  • 怎样去理解异常SVC和PendSV

    目录 什么是SVC和PendSV 什么是SVC和PendSV SVC xff08 系统服务调用 xff09 和 PendSV xff08 可悬挂系统调用 xff09 它们多用于在操作系统之上的软件开发中 SVC 用于产生系统函数的调用请求
  • vscode开发ROS基本配置订阅PX4中imu数据

    目录 安装vscode Linux版本 安装vscode插件 配置头文件路径 订阅PX4中imu数据 参考 xff1a ROS基本操作 ROS中订阅节点消息测试 ROS下如何订阅任意话题 步步清风皆是你的博客 CSDN博客 安装vscode
  • 执行 install_geographiclib_datasets.sh 错误!

    执行 install geographiclib datasets sh 错误 xff01 原因被墙 xff01 解决 xff1f Ubuntu 18 04 在 usr share 下目录新建 GeographicLib 目录 先将 geo
  • ubuntu查看文件依赖、安装libopencv

    ubuntu查看文件依赖 安装libopencv 1 ubuntu中 xff0c 当需要查看某个文件运行时 xff0c 需要那些依赖库时 xff0c 可以在终端输入指令 ldd name name为文件名字 2 出现libopencv相关依
  • VNC远程连接不上ubuntu服务器,显示time out

    原因 xff1a 可能是出现了wired unmanaged并且启动不了屏幕共享 xff08 screen sharing xff09 xff0c 即开关打不到on上面 解决方案1 xff1a https askubuntu com que
  • OpenCV(三):一步步实现图像定位(Python版)

    一 预期目标 如下图 xff0c 要识别图中的国旗 xff0c 然后框选出来 xff0c 并且返回国旗的中心位置 xff0c 效果如下 彩色图像大小 400 264 目标中心位置 225 218 二 准备工作 1 将下面的图像另存为在本地
  • PX4教程翻译(1)序+前言

    序 最近打算实现树莓派控制 Pixhawk 无人机 xff0c 做了近一个星期 xff0c 还是没有成功 由于没有系统的中文教程 xff0c 零散地看了许多博客 正如万山圈子里 xff0c 一山放过一山拦 各种Bugs层出不穷 于是决心静心
  • raise ResourceNotFound(name, ros_paths=self._ros_paths) ResourceNotFound: gazebo_ros

    电脑为Ubuntu16 04 xff0c 安装来ROS xff08 kinetic版本 xff09 和 gazebo xff08 9版本 xff09 xff0c 在做PX4固件仿真 xff0c 想实现外部控制Pixhawk xff0c 但是
  • ROS综合应用(一)树莓派外部控制 Pixhawk(一站到底)

    序言 上一次 说到学习 ROS xff0c 主要是因为在做多无人机协同控制项目 听说可以使用 ROS 来实现树莓派控制 Pixhawk 无人机 xff0c 这样可以不用修改飞控源码 xff0c 而且可以用树莓派做图像处理 于是开始了一场浩浩
  • 卡尔曼滤波器MATLAB实现(从一维到三维)

    一 场景设置与效果图 假设一架无人机携带了一个气压计和GPS xff0c 定位精度都为1m xff0c 数据采样频率都为5Hz xff0c 100s时间螺旋上升40m xff0c 螺旋半径为20m 在无人机飞行过程中 xff0c 1m的精度
  • linux Linux 3.x的设备树(Device Tree) dts 介绍

    在http blog csdn net 21cnbao article details 8457546 进行整理修改 xff0c 感谢此博主 1 1 1 简介 Linus Torvalds在 2011 年 3 月 17 日的 ARM Lin