Bootstrap

DTS入门知识

一、dts产生原因… 2

二、dts基本知识… 2

2.1 dts加载过程… 2

2.2 dts描述信息… 3

2.3 dts组成结构示例… 3

2.4 dts语法… 4

2.4.1 compatible … 6

2.4.2 name@unit-address… 6

2.4.3 reg address-cells size-cells… 6

2.4.5 interrupt… 9

三、dts与驱动… 10

四、具体案例… 11

4.1 gpio… 12

4.2 interrupt… 12

4.3 pinctl… 12

4.4 clk… 13

dts入门

本文目标是了解dts基本语法,可以尝试去看内核dts中的文件,follow去配置。

一、dts产生原因
DTS即Device Tree Source 设备树源码, DeviceTree是一种描述硬件的数据结构,它起源于OpenFirmware (OF)。

它替代arch/arm/plat-xxx和arch/arm/mach-xxx中的板级spec代码,便于code管理。

ARM平台的相关code相关规范调整:

1、ARM的核心代码仍然保存在arch/arm目录下

2、ARM SOC core architecture code保存在arch/arm目录下

3、ARM SOC的周边外设模块的驱动保存在drivers目录下

4、ARM SOC的特定代码在arch/arm/mach-xxx目录下

5、ARM SOC board specific的代码被移除,由Device Tree机制来负责传递硬件拓扑和硬件资源信息。

本质上,Device Tree改变了原来用hardcode方式将HW配置信息嵌入到内核代码的方法,改用bootloader传递一些参数。

如果我们认为kernel是一个black box,那么其输入参数应该包括:

a.识别platform的信息 b. runtime的配置参数 c.设备的拓扑结构以及特性

对于嵌入式系统,在系统启动阶段,bootloader会加载内核并将控制权转交给内核,此外,还需要把上述的三个参数信息传递给kernel,以便kernel可以有较大的灵活性。在linux kernel中,Device Tree的设计目标就

是如此。

二、dts基本知识
2.1 dts加载过程
如果要使用Device Tree,首先用户要了解自己的硬件配置和系统运行参数,并把这些信息组织成Device Tree source file。通过DTC(Device Tree Compiler),可以将这些适合人类阅读的Device Tree source file变成适合机器处理的Device Tree binary file(有一个更好听的名字,DTB,device tree blob)。在系统启动的时候,boot program(例如:firmware、bootloader)可以将保存在flash中的DTB copy到内存(当然也可以通过其他方式,例如可以通过bootloader的交互式命令加载DTB,或者firmware可以探测到device的信息,组织成DTB保存在内存中),并把DTB的起始地址传递给client program(例如OS kernel,bootloader或者其他特殊功能的程序)。对于计算机系统(computer system),一般是firmware->bootloader->OS,对于嵌入式系统,一般是bootloader->OS。

在高通msm8992平台:

sbl将烧写在emmc中的cdt.bin进行读取和处理,获得nPlatform和nSubtype,如果没有CDT分区,那么就使用代码里配置的cdt,存入共享内存;

lk将从共享内存中获得nPlatform和nSubtype进行相应处理,确定dtb entry,然后选择一个对应的dtb(所有dtb保存在boot.img中)从emmc中读进内存地址hdr->tags_addr,这个参数会传递给kernel;

kernel通过传入的dtb地址进行设备的创建。

2.2 dts描述信息
Device Tree由一系列被命名的结点(node)和属性(property)组成,而结点本身可包含子结点。所谓属性,其实就是成对出现的name和value。在Device Tree中,可描述的信息包括(原先这些信息大多被hard code到kernel中):

CPU的数量和类别

内存基地址和大小

总线和桥

外设连接

中断控制器和中断使用情况

GPIO控制器和GPIO使用情况

Clock控制器和Clock使用情况

它基本上就是画一棵电路板上CPU、总线、设备组成的树,Bootloader会将这棵树传递给内核,然后内核可以识别这棵树,并根据它展开出Linux内核中的platform_device、i2c_client、spi_device等设备,而这些设备用到的内存、IRQ等资源,也被传递给了内核,内核会将这些资源绑定给展开的相应的设备。

是否Device Tree要描述系统中的所有硬件信息?答案是否定的。基本上,那些可以动态探测到的设备是不需要描述的,例如USB device。不过对于SOC上的usb host controller,它是无法动态识别的,需要在devicetree中描述。同样的道理,在computersystem中,PCI device可以被动态探测到,不需要在device tree中描述,但是PCI bridge如果不能被探测,那么就需要描述之。

.dts文件是一种ASCII 文本格式的Device Tree描述,此文本格式非常人性化,适合人类的阅读习惯。基本上,在ARM Linux在,一个.dts文件对应一个ARM的machine,一般放置在内核的arch/arm/boot/dts/目录。由于一个SoC可能对应多个machine(一个SoC可以对应多个产品和电路板),势必这些.dts文件需包含许多共同的部分,Linux内核为了简化,把SoC公用的部分或者多个machine共同的部分一般提炼为.dtsi,类似于C语言的头文件。其他的machine对应的.dts就include这个.dtsi。

譬如在arch/arm/boot/dts/qcom/目录下,高通的很多.dtsi都include了skeleton.dtsi或者skeleton64.dtsi。

正常情况下所有的dts文件以及dtsi文件都含有一个根节点”/”,这样include之后就会造成有很多个“根节点”。按理说 device tree既然是一个树,那么其只能有一个根节点,所有其他的节点都是派生于根节点的child node。其实Device TreeCompiler会对DTS的node进行合并,最终生成的DTB中只有一个root node.

device tree的基本单元是node。这些node被组织成树状结构,除了root node,每个node都只有一个parent。一个device tree文件中只能有一个root node。每个node中包含了若干的property/value来描述该node的一些特性。每个node用节点名字(node name)标识,节点名字的格式是node-name@unit-address。如果该node没有reg属性(后面会描述这个property),那么该节点名字中必须不能包括@和unit-address。unit-address的具体格式是和设备挂在那个bus上相关。例如对于cpu,其unit-address就是从0开始编址,依次加一。而具体的设备,例如以太网控制器,其unit-address就是寄存器地址。rootnode的node name是确定的,必须是“/”。

在一个树状结构的device tree中,如何引用一个node呢?要想唯一指定一个node必须使用full path,例如/node-name-1/node-name-2/node-name-N

2.3 dts组成结构示例

/ { "/" 表示root结点,该结点下有两个子结点node1和node2

    node1 { 结点"node1"下又含有子结点,本例中为"child-node1""child-node2",各结点都有一系列属性

        a-string-property = "Astring";属性是字符串

        a-string-list-property = "firststring", "second string";字符串数组

        a-byte-data-property = [0x01 0x23 0x340x56];二进制数组

        child-node1 {
            first-child-property;

            second-child-property = <1>;Cells(由u32整数组成)

            a-string-property = "Hello,world";

        };

        child-node2 {
        };

    };

    node2 {
        an-empty-property;属性为空

        a-cell-property = <1 2 3 4>; /* each number (cell) is a uint32 */

        child-node1 {
        };

    };

};

上述.dts文件并没有什么真实的用途,但它基本表征了一个DeviceTree源文件的结构。

2.4 dts语法
完整的Device Tree可以将一个PCB摆在你眼前,下面我们一起来看一下:

下面以一个最简单的machine为例来看如何写一个.dts文件。假设此machine的配置如下:
1个双核ARMCortex-A9 32位处理器;
ARM的local bus上的内存映射区域分布了2个串口(分别位于0x101F1000 和 0x101F2000)、GPIO控制器(位于0x101F3000)、SPI控制器(位于0x10115000)、中断控制器(位于0x10140000)和一个external bus桥;
External bus桥上又连接了SMC SMC91111 Ethernet(位于0x10100000)、I2C控制器(位于0x10160000)、64MB NOR Flash(位于0x30000000);
External bus桥上连接的I2C控制器所对应的I2C总线上又连接了Maxim DS1338实时钟(I2C地址为0x58)。

其对应的.dts文件为:

/ {  

    compatible = "acme,coyotes-revenge";  

    #address-cells = <1>;  子结点需要一个cell描述地址

    #size-cells = <1>;  子结点需要一个cell描述长度

    interrupt-parent = <&intc>;  

  

    cpus {  

        #address-cells = <1>;  

        #size-cells = <0>;  

        cpu@0 {  

            compatible = "arm,cortex-a9";  

            reg = <0>;  

        };  

        cpu@1 {  

            compatible = "arm,cortex-a9";  

            reg = <1>;  

        };  

    };  

  

    serial@101f1000 {  串口

        compatible = "arm,pl011";  

        reg = <0x101f1000 0x1000 >;  

        interrupts = < 1 0 >;  

    };  

  

    serial@101f2000 {  串口

        compatible = "arm,pl011";  

        reg = <0x101f2000 0x1000 >;  

        interrupts = < 2 0 >;  

    };  

  gpio@101f3000 {  GPIO控制器

        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 {  spi控制器

        compatible = "arm,pl022";  

        reg = <0x10115000 0x1000 >;  起始地址为0x10115000,长度为0x1000

        interrupts = < 4 0 >;  

};  

  external-bus {  external bus桥

        #address-cells = <2>  子结点需要两个cell描述地址,片选

        #size-cells = <1>;  子结点需要一个cell描述长度

        ranges = <0 0  0x10100000   0x10000     // Chipselect 1, Ethernet  片选0 0,地址0x10100000 ,长度0x10000

                      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>;  rtc需要一个cell描述地址

            #size-cells = <0>;  rtc不需要0描述长度

            reg = <1 0 0x1000>;  

            rtc@58 {  

                compatible = "maxim,ds1338";  

                reg = <58>;  

                interrupts = < 7 3 >;  

            };  

        };  

        flash@2,0 {  

            compatible = "samsung,k8f1315ebm", "cfi-flash";  

            reg = <2 0 0x4000000>;  

        };  

    };  

};  

注释不是太多,下面来详细分类解释:

2.4.1 compatible
上述.dts文件中,root结点"/“的compatible 属性compatible = “acme,coyotes-revenge”;定义了系统的名称,它的组织形式为:,。Linux内核透过root结点”/“的compatible 属性即可判断它启动的是什么machine。
在.dts文件的每个设备,都有一个compatible属性,compatible属性用户驱动和设备的绑定。compatible属性是一个字符串的列表,列表中的第一个字符串表征了结点代表的确切设备,形式为”,",其后的字符串表征可兼容的其他设备。可以说前面的是特指,后面的则涵盖更广的范围。

如在arch/arm/boot/dts/vexpress-v2m.dtsi中的Flash结点:

flash@0,00000000 {
     compatible = "arm,vexpress-flash", "cfi-flash";  

     reg = <0 0x00000000 0x04000000>,  

     <1 0x00000000 0x04000000>;  

     bank-width = <4>;  

 }; 

compatible属性的第2个字符串"cfi-flash"明显比第1个字符串"arm,vexpress-flash"涵盖的范围更广。

2.4.2 name@unit-address
接下来root结点"/“的cpus子结点下面又包含2个cpu子结点,描述了此machine上的2个CPU,并且二者的compatible 属性为"arm,cortex-a9”。
注意cpus和cpus的2个cpu子结点的命名,它们遵循的组织形式为:[@],<>中的内容是必选项,[]中的则为可选项。name是一个ASCII字符串,用于描述结点对应的设备类型,如网卡适配器对应的结点name宜为ethernet,表示这个是网卡。如果一个结点描述的设备有地址,则应该给出@unit-address。多个相同类型设备结点的name可以一样,只要unit-address不同即可,如本例中含有cpu@0、cpu@1以及serial@101f0000与serial@101f2000这样的同名结点。设备的unit-address地址也经常在其对应结点的reg属性中给出。

2.4.3 reg address-cells size-cells
设备的地址特性根据一下几个属性来控制:

● reg

● #address-cells

● #size-cells

reg意为region,区域。格式为:

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

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

address-cells决定了address1/2/3包含几个cell,size-cells决定了length1/2/3包含了几个cell,用上面的dts文件内容举例子说明:

例子2.4.3.1

root结点的#address-cells = <1>;和#size-cells = <1>;决定了serial、gpio、spi等结点的address和length字段的长度分别为1

serial@101f1000 { 

        compatible = "arm,pl011";  

        reg = <0x101f1000 0x1000 >;地址0x101f1000,长度 0x1000

        interrupts = < 1 0 >;  

 };  

例子2.4.3.2

i2c控制器模块下的rtc模块。因为I2C设备只是被分配在一个地址上,不需要其他任何空间,所以只需要一个address的cell就可以描述完整,不需要size-cells

/ {  

    compatible = "acme,coyotes-revenge";  

    #address-cells = <1>;  子结点需要一个cell描述地址

    #size-cells = <1>;  子结点需要一个cell描述长度

    interrupt-parent = <&intc>; 

...

  external-bus {
    #address-cells = <2>

#size-cells = <1>;

...

    i2c@1,0 {
        compatible = "acme,a1234-i2c-bus";

        #address-cells = <1>;重新写address-cells

        #size-cells = <0>;重新写address-cells

        reg = <1 0 0x1000>;

        rtc@58 {
            compatible ="maxim,ds1338";

            reg = <58>;只需要一个address cell,不需要cell再描述长度

        };

  };

...

};

例子2.4.3.3

当需要描述的设备不是本地设备时,就需要描述一个从设备地址空间到CPU地址空间的映射关系,这里就需要用到ranges属性。还是以上边的external-bus举例

ranges属性为一个地址转换表。表中的每一行都包含了子地址、父地址、在自地址空间内的区域大小。他们的大小(包含的cell)分别由子节点的address-cells的值、父节点的address-cells的值和子节点的size-cells来决定。

/ {  

    compatible = "acme,coyotes-revenge";  

    #address-cells = <1>;  子结点需要一个cell描述地址

    #size-cells = <1>;  子结点需要一个cell描述长度

    interrupt-parent = <&intc>; 

...

 

0 0 两个cell,由子节点external-bus的address-cells=<2>决定;

0x10100000 一个cell,由父节点(/)的address-cells=<1>决定;

0x10000 一个cell,由子节点external-bus的size-cells=<1>决定。

最终第一行说明的意思就是:片选0,偏移0(选中了网卡),被映射到CPU地址空间的0x10100000~0x10110000中,地址长度为0x10000。

 

external-bus {
    #address-cells = <2>

    #size-cells = <1>;

    ranges = <00  0x10100000   0x10000     //Chipselect 1, Ethernet

            1 0 0x10160000   0x10000     // Chipselect 2,i2c controller

            2 0 0x30000000   0x1000000>;  // Chipselect 3, NORFlash

};

进阶例子2.4.3.4

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 0x800000000x80000000 0 0x20000000

                0x02000000 0 0xa00000000xa0000000 0 0x10000000

                0x01000000 0 0x000000000xb0000000 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空间

1032位存储空间

1164位存储空间

bbbbbbbb: PCI总线号。PCI有可能是层次性架构,所以我们可能需要区分一些子-总线

ddddd:设备号,通常由初始化设备选择信号IDSEL连接时申请。

fff:功能序号,有些多功能PCI设备可能用到。

rrrrrrrr:注册号,在设置周期使用。

回头再看这个ranges分表代表了什么。

ranges =<0x42000000 0 0x80000000 0x80000000 0 0x20000000

        0x02000000 0 0xa0000000 0xa0000000 00x10000000

        0x01000000 0 0x00000000 0xb0000000 00x01000000>;

父节点address-cells为1,子节点address-cells为3,子节点size-cells为2。则第一行可以这样划分:

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

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

2.4.5 interrupt
描述中断连接需要四个属性:

  1. interrupt-controller 一个空属性用来声明这个node接收中断信号;

  2. #interrupt-cells 这是中断控制器节点的属性,用来标识这个控制器需要几个单位做中断描述符;

  3. interrupt-parent 标识此设备节点属于哪一个中断控制器,如果没有设置这个属性,会自动依附父节点的;

  4. interrupts 一个中断标识符列表,表示每一个中断输出信号。

如果有两个,第一个是中断号,第二个是中断类型,如高电平、低电平、边缘触发等触发特性。对于给定的中断控制器,应该仔细阅读相关文档来确定其中断标识该如何解析。

/ {  

    compatible = "acme,coyotes-revenge";  

    #address-cells = <1>;  

    #size-cells = <1>;  

    interrupt-parent = <&intc>; root结点指定中断控制器intc

...

 

    intc: interrupt-controller@10140000 { 

        compatible = "arm,pl190";  

        reg = <0x10140000 0x1000 >;  

        interrupt-controller;  这个node接收中断信号

        #interrupt-cells = <2>;  2个cell做中断描述符

    };  

对于ARM GIC中断控制器而言,#interrupt-cells为3,它3个cell的具体含义需要参考文档

kernel/Documentation/devicetree/bindings/arm/gic.txt

#interrupt-cells : Specifies the number of cells needed to encode an

  interrupt source.  The type shall be a <u32> and the valueshall be 3.

 

  The 1st cell is the interrupt type; 0 for SPIinterrupts, 1 for PPI

  interrupts.

 

  The 2nd cell contains the interrupt numberfor the interrupt type.

  SPI interrupts are in the range [0-987].  PPI interrupts are in the

  range [0-15].

 

  The 3rd cell is the flags, encoded asfollows:

         bits[3:0] trigger type and level flags.

                  1= low-to-high edge triggered

                  2= high-to-low edge triggered

                  4= active high level-sensitive

                  8= active low level-sensitive

一个设备还可能用到多个中断号。对于ARM GIC而言,若某设备使用了SPI的168、169号2个中断,且都是高电平触发,则该设备结点的interrupts属性可定义为:

interrupts =<0 168 4>, <0 169 4>;

三、dts与驱动
●没有使用dts之前的bsp和driver

板级spec代码

#ifdefCONFIG_LEDS_CTL

structplatform_device s3c_device_leds_ctl = {
        .name  = "leds",

        .id             = -1,

};

#endif

driver

static structplatform_driver leds_driver = {
         .probe = leds_probe,

         .remove = leds_remove,

         .suspend = leds_suspend,

         .resume = leds_resume,

         .driver = {
                  .name= DRIVER_NAME,//leds

                  .owner= THIS_MODULE,

         },

};

●使用dts之后的bsp和driver

dts文件

&soc {
         i2c@f9928000 { /* BLSP1 QUP6 */

                  status= "ok";

                  nfc-nci@76{
                           compatible = "brcm,bcm2079x";

         ...

};

driver

static structof_device_id bcm_match_table[] = {
         {.compatible = "brcm,bcm2079x"},

         {}

}

 

static structi2c_driver bcm2079x_driver = {
         .id_table = bcm2079x_id,

         .probe = bcm2079x_probe,

         .remove = bcm2079x_remove,

         .driver = {
                  .owner= THIS_MODULE,

                  .name= "bcm2079x-i2c",

                  .of_match_table= bcm_match_table,

         },

};

四、具体案例
用第三节提到的nfc进行分析,里面有很多的内容可以去Document/devicetree/bindings查找

root@icesky_msm8992:/sys/bus/i2c/devices/6-0076# cat name

bcm2079x
 

&soc {
         i2c@f9928000 {/* BLSP1 QUP6 */

                  status= "ok";

                  nfc-nci@76 {
                           compatible = "brcm,bcm2079x";匹配

                           reg = <0x76>;地址cell=0x76,没有长度描述cell

           

            gpio控制器msm_gpio

                           qcom,wake-gpio =<&msm_gpio 29 0x00>; 29pin,0x00 -- unused

                           qcom,en-gpio =<&msm_gpio 30 0x00>;30pin

                           qcom,irq-gpio =<&msm_gpio 9 0x00>;9pin

 

                           qcom,clk-src = "BBCLK2";nfc时钟源,跟驱动有关

                           interrupt-parent =<&msm_gpio>;属于msm_gpio中断控制器

                           interrupts = <9 0>;中断标识符列表 nfc readinterrupt, gpio-clk-req interrupt

                           interrupt-names = "nfc_irq";中断名称字符串类型,与驱动有关kernel/drivers/of/irq.c

                           pinctrl-names ="nfc_active","nfc_suspend";pin ctrl字符串类型

                           pinctrl-0 = <&nfc_int_active&nfc_disable_active &nfc_wake_active>;4.3节

                           pinctrl-1 = <&nfc_int_suspend&nfc_disable_suspend &nfc_wake_suspend>;

                           clocks = <&clock_rpm clk_bb_clk2_pin>;

                           clock-names = "brcm_nfc_clk";

                  };

         };

};

4.1 gpio
上面的wake-gpio、en-gpio、irq-gpio与driver相关

structbcm2079x_dev {
...

         unsigned int wake_gpio;

         unsigned int en_gpio;

         unsigned int irq_gpio;

...

};

4.2 interrupt
msm_gpio既是中断控制器,也是gpio控制器

gp: gp{
qcom,num-pins = <146>;

#qcom,pin-cells = <1>;

    msm_gpio: msm_gpio {
            compatible = "qcom,msm-tlmm-gp";

            gpio-controller;可以作为gpio控制器

            #gpio-cells = <2>;2个cell做gpio描述符,第一个是pin number,第二个是指定可选参数

            interrupt-controller;接收中断信号

            #interrupt-cells = <2>;2个cell做中断描述符,第一个是pin number,第二个是指定可选参数中断类型

            qcom,direct-connect-irqs = <8>;8个GPIO可以直接连中断信号

            num_irqs = <146>;146个pins可以作为中断源

    };

};

4.3 pinctl
我们挑下nfc_wake_active查查dts里面是什么

pmx_nfc_wake_reg{
         qcom,pins = <&gp 9>; gp的配置见4.2节 pin number == 9

         qcom,pin-func = <0>; 为pin设置function

         qcom,num-grp-pins = <1>;pin group number = 1,因为本例只有一个gp 9

         label = "pmx_nfc_wak";pin group中的身份识别

 

         nfc_wake_active:active {
                  drive-strength= <6>;信号强度 6MA

                  bias-pull-up;上拉引脚

                  output-low;输出模式low level

         };

 

         nfc_wake_suspend: suspend {
                  drive-strength= <6>;

                  bias-pull-down;/* pull down */

                  output-low;

         };

};

4.4 clk

nfc-nci@76 {
        ...

                           clocks = <&clock_rpm clk_bb_clk2_pin>;#defineclk_bb_clk2_pin 0x498938e5

                           clock-names = "brcm_nfc_clk";drivers将使用该name去匹配

                  };
clock_rpm: qcom,rpmcc@fc401880 {
         compatible ="qcom,rpmcc-8992";

         reg = <0xfc401880 0x4>;

         reg-names = "cc_base";

         #clock-cells = <1>;     0 表示单一时钟输出,1表示多时钟

};
;