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

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

        };

*/

 

 

 

 

 

 

 

 

 

       

 

  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值