zoukankan      html  css  js  c++  java
  • Linux设备树(1)——先前总结 Hello

    一、设备树编译

    1.编译设备树:cd linux-x.xx & make dtbs,生成的dtb在目录linux-x.xx/arch/xxx/boot/dts下

    2.反编译dtb,生成dts: linux-x.xx/scripts/dtc/dtc -I dtb -O dts xxxx.dtb -o xxxx.dts 

    3.将.dts编译为.dtb的工具。DTC的源代码位于内核的scripts/dtc目录,在Linux内核使能了Device Tree的情况下,编译内核的时候主机工具dtc会被编译出来,对应scripts/dtc/Makefile中的“hostprogs-y := dtc”这一hostprogs编译target。
      在Linux内核的arch/arm/boot/dts/Makefile中,描述了当某种SoC被选中后,哪些.dtb文件会被编译出来。

    在Linux下,我们可以单独编译Device Tree文件。当我们在Linux内核下运行make dtbs时,若我们之前选择了ARCH_VEXPRESS,上述.dtb都会由对应的.dts编译出来。因为arch/arm/Makefile中含有一个dtbs编译target项目。

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

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

    6.Uboot从 v1.1.3开始支持Device Tree,其对ARM的支持则是和ARM内核支持Device Tree同期完成。
      为了使能Device Tree,需要编译Uboot的时候在config文件中加入
      #define CONFIG_OF_LIBFDT 
      在Uboot中,可以从NAND、SD或者TFTP等任意介质将.dtb读入内存,假设.dtb放入的内存地址为0x71000000,之后可在Uboot运行命令fdt addr命令设置.dtb的地址,如:
      U-Boot>  fdt addr 0x71000000
      fdt的其他命令就变地可以使用,如fdt resize、fdt print等。
      对于ARM来讲,可以透过bootz kernel_addr initrd_address dtb_address的命令来启动内核,即dtb_address作为bootz或者bootm的最后一次参数,第一个参数为内核映像的地址,第二个参数为initrd的地址,若不存在initrd,可以用 -代替。

    二、设备树的组成格式

    “/"代表根节点;

      “model”是板的ID;

      "compatible"是平台兼容,一般格式是"manufacturer,model"。内核或者uboot依靠这个属性找到相对应driver,若"compatible"出现多个属性,按序匹配driver;

      “#address-cells”是address的单位(32bit),可寻址的设备使用它、#size-cells、reg在Device Tree中编码地址信息,reg中的address 和 length 字段是可变长的,父结点的#address-cells和#size-cells分别决定了子结点的reg属性的address和length字段的长度。如果root结点的#address-cells = <1>;和#size-cells = <1>;决定了serial、gpio、spi等结点的address和length字段的长度分别为1(reg中描述address和length的字段都只能有1个)。cpus 结点的#address-cells = <1>;和#size-cells = <0>;决定了2个cpu子结点的address为1,而length为空。可以理解为地址需要几个维度来描述,如片选0的偏移0地址处,就需要#address-cells为2.

    “#size-cells”是length的单位(32bit);

      "reg"是寄存器,格式是"<address,length>",作为平台内存资源,组织形式为reg = <address1 length1 [address2 length2] [address3 length3] ... >,length则为cell的列表或者为空(若#size-cells = 0)

      "aliase" 是别名,必须节点全称,可以通过地址引用获取;

      ”chosen“是板级启动参数;

      "cpus"是SOC的CPU信息,可以改变运行频率或者开关CPU,命名遵循的组织形式为:<name>[@<unit-address>],多个相同类型设备结点的name可以一样,只要unit-address不同即可,如本例中含有cpu@0、cpu@1,设备的unit-address地址也经常在其对应结点的reg属性中给出。

      "memory"是板级内存的信息。

      "interrupts"是中断控制器,根据SOC自定义格式,这里是<输入类型 中断号 触发方式>,作为平台中断资源;输入类型:0是SPI中断,1是PPI中断;触发类型:0上升沿,2下降沿(对SPI无效),4高电平,8低电平(对SPI无效),见Documentation\devicetree\bindings\interrupt-controller\arm,gic.txt

      “interrupt-controller”指示这个节点是中断控制节点,它的属性为空,中断控制器应该加上此属性表明自己的身份(直接在{}中写上interrupt-controller即可)

      “interrupt-cells”与#address-cells 和 #size-cells相似,它表明连接此中断控制器的设备的interrupts属性的cell大小

      "interrupt-parent"设备结点透过它来指定它所依附的中断控制器的phandle,当结点没有指定interrupt-parent 时,则从父级结点继承。

      "[label:]"如gic: interrupt-controller@1c81000,这个标签可以作为地址赋值到其他节点的属性;

      “device_type":设备类型,寻找节点可以依据这个属性;

      "status"是开关节点设备的状态,取值"okay"或者"ok"表示使能,"disabled"表示失能。

      “ranges” 经过总线桥后的address往往需要经过转换才能对应的CPU的memory映射。external-bus的ranges属性定义了经过external-bus桥后的地址范围如何映射到CPU的memory区域。ranges是地址转换表,其中的每个项目是一个子地址、父地址以及在子地址空间的大小的映射。映射表中的子地址、父地址分别采用子地址空间的#address-cells和父地址空间的#address-cells大小。

    例如:ranges = <0 0  0x10100000   0x10000 将子地址空间的片选0的0偏移地址处映射到地址0x10100000处,映射大小为0x10000 字节   

                              1 0  0x10160000   0x10000>; 将子地址空间的片选1的0偏移地址处映射到地址0x10160000处,映射大小为0x10000 字节  

     三、系统通过以下步骤来生成树:

    1. CPU 经过初始化后搜索固件。

    2. 主要固件(OpenBoot、基本输入/输出系统 (Basic Input/Output System, BIOS) 或 Bootconf)初始化并创建包含已知或自标识硬件的设备树。

    3. 当主要固件在设备中发现兼容固件时,主要固件将初始化该设备并检索设备属性。

    4. 该固件将查找并引导操作系统。

    5. 内核从树的根节点开始,搜索匹配的设备驱动程序并将该驱动程序绑定到设备。

    6. 如果设备是结点,则内核会查找固件尚未检测到的子设备。内核会将所有子设备都添加到树的子树节点下面。

    7. 内核从步骤 5 开始重复该过程,直到无需再创建设备节点。

    主要相关函数在/drivers/of/base.c中,使用上比较打的区别是platform_driver结构被替换为了of_device_id ,而platform_driver不变

    四、设备与确定的匹配

      .dts文件中,root结点"/"的compatible 属性定义了系统的名称,它的组织形式为:<manufacturer>,<model>。Linux内核通过它判断启动的是什么machine。
      .dts文件的每个设备,都有一个compatible 属性,compatible属性用户驱动和设备的绑定。compatible 属性是一个字符串的列表(第一个“”内的字符串),列表中的第一个字符串表征了结点代表的确切设备,形式为"<manufacturer>,<model>",其后的字符串表征可兼容的其他设备。可以说前面的是特指,后面的则涵盖更广的范围。如compatible = "arm,vexpress-flash", "cfi-flash";  在与驱动进行绑定主要靠mode域,前面的manufacturer不重要。

    五、常用的of API

    1.判断设备结点的compatible 属性是否包含compat指定的字符串

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

     2.根据compatible属性,遍历Device Tree中所有的设备结点,获得设备结点

      struct device_node *of_find_compatible_node(struct device_node *from,const char *type, const char *compatible)

    3. 读取设备结点np的属性名为propname,类型为8、16、32、64位整型数组的属性。对于32位处理器来讲,最常用的是of_property_read_u32_array()

      of_property_read_u32_array() / of_property_read_u64_array()还有u8,u16的

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

      of_property_read_string() / of_property_read_string_index()

    5.如果设备结点np含有propname属性,则返回true,否则返回false。一般用于检查空属性是否存在。

       of_property_read_bool()

    6.通过设备结点直接进行设备内存区间的 ioremap(),index是内存段的索引。若设备结点的reg属性有多段,可通过index标示要ioremap的是哪一段,只有1段的情况,index为0。采用Device Tree后,大量的设备驱动通过of_iomap()进行映射,而不再通过传统的ioremap

       void __iomem *of_iomap(struct device_node *np, int index)

    7.透过Device Tree或者设备的中断号,实际上是从.dts中的interrupts属性解析出中断号。若设备使用了多个中断,index指定中断的索引号。

      irq_of_parse_and_map

    还有一些OF API,这里不一一列举,具体可参考include/linux/of.h头文件。

    六、总结

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

    补充:

    flash_SY7803:flashlight {  
                compatible = "qcom,leds-gpio-flash";   //匹配参数  
                status = "okay";  
                pinctrl-names = "flash_default";  
                pinctrl-0 = <&SY7803_default>;  
                qcom,flash-en = <&msm_gpio 31 0>;  
                qcom,flash-now = <&msm_gpio 32 0>;  
                qcom,op-seq = "flash_en", "flash_now";    
                qcom,torch-seq-val = <0 1>;  
                qcom,flash-seq-val = <1 0>;  
                linux,name = "flashlight";  //属性 linux,name    
                linux,default-trigger = "flashlight-trigger";  
                }; 

    使用: 在代码中获取节点的所有信息

    0.先把节点获取到 struct device_node *np = NULL; np = of_find_node_by_path("/test_nod@12345678");

    1.of_get_named_gpio(node, "qcom,flash-en", 0);返回31;

    2.获取结点中的属性:of_find_property()

    3.读取到属性中的整数的数组:uint32_t array_flash_seq[2];  rc = of_property_read_u32_array(node, "qcom,flash-seq-val",array_flash_seq, 2);  ==》array_flash_seq <1 0>

    4.读取到属性中的字符串的数组:rc = of_property_read_string_index(node,    "qcom,op-seq", i,     &seq_name);  //"flash_en", "flash_now";

    5.获取到中断的号码:irqno = irq_of_parse_and_map(np, 0);

    6.可以使用ret = request_irq验证中断号码是否有效

    7.获取设备属性名字:of_property_read_string(node, "linux,name", &flash_led->cdev.name)  // flash_led->cdev.name = “flashlight ”;

     

     1. 设备树中 compatible 
        键值对
    2.driver中  
     platform_driver 结构体
         probe    
         remove 

         of_match_table 

         probe 中
         1.通过of函数获得相关的资源信息,
         2. 申请引脚信息  pinctrl 
         3.注册设备 classdev

    参考1:http://www.cnblogs.com/kevinhwang/p/5647021.html

    参考2:http://blog.csdn.net/21cnbao/article/details/8457546(好)

    补充参考:http://www.cnblogs.com/zzb-Dream-90Time/p/6474526.html

  • 相关阅读:
    2017.3.10组合数学学习——多重集合的排列、组合,有限概率
    poj 3169 Layout
    poj 1201 Intervals
    poj 1716 Integer Intervals
    2017.3.9 组合数学学习——组合、多重集排列
    [HNOI 2013]切糕
    思维相似处总结(未完待续)
    bzoj 3673: 可持久化并查集 by zky
    SDOI2013 森林
    标题还没想好
  • 原文地址:https://www.cnblogs.com/hellokitty2/p/7406181.html
Copyright © 2011-2022 走看看