打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
DTS草稿之一

一、DTS文件综述

    最新的UBOOT代码支持OF(Open Firmware)结构的扁平设备树(flattened device-tree),目前还没有对OF结构的用户接口API进行支持。因此,在U-boot向Linux内核跳转的时候需要向内核传递一张描述整个硬件系统扁平设备树的的表,来描述设备、总线以及中断的信息等等。这个表被称为设备树块(device-tree block),也就是我们提供的二进制dtb文件。我们一般不对该二进制文件进行直接编辑,而是通过一定硬件描述语法创建一个dts(device-tree source)文件,再用特定的工具dtc将dts转成目标dtb文件。

1.U-BOOT传递给Linux PowerPC的参数

            Linux PowerPC有且仅有一个单一的入口点,该入口点支持下面两种调用习俗,而其中第一种正是目前UBOOT启动Linux内核采用的方法,因为UBOOT只支持OF结构的扁平设备树。(参见下表)

a)      启动方式一:BOOT支持OF扁平设备树

b)     启动方式二:BOOT全面支持OF结构(包括用户接口API和OF扁平设备树)

表-1  两种BOOT启动Linux内核的方式

Linux内核中的参数

BOOT支持OF扁平设备树

BOOT全面支持OF结构(包括用户接口API和OF扁平设备树)

通用寄存器r3

指向内存中设备树块

指向OF用户接口API的地址

通用寄存器r4

指向Linux内核所在的物理地址

Initrd的起始地址

通用寄存器r5

Initrd的长度

通用寄存器r6

命令行参数起始地址

通用寄存器r7

命令行参数结束地址

2.设备树(Device Tree)块格式

            注意:设备树块必需位于主内存中。它可以通过内存的“实模式”和“虚模式”获取到。

1)报头

            进入内核时,r3指向由include/asm-powerpc/prom.h中boot_param_header结构简要描述的内存区。

struct boot_param_header {

        u32     magic;                  /* magic word OF_DT_HEADER */

        u32     totalsize;                               /* total size of DT block */

        u32     off_dt_struct;                        /*offset to structure */

        u32     off_dt_strings;                      /*offset to strings */

        u32    off_mem_rsvmap;       /* offset to memory reserve map*/

        u32     version;               /* format version */

        u32     last_comp_version;      /* last compatible version */

        /* version 2fields below */

        u32     boot_cpuid_phys;        /* Which physical CPU id we're bootingon */

        /* version 3fields below */

        u32     size_dt_strings;                     /* size of the strings block */

        /* version 17fields below */

        u32  size_dt_struct;                                         /* size of the DT structure block */

};

            典型的DT块内存设计格式如下(地址从高端到底端):

图-1 DT块的结构图

(*)alignment gaps不是必需存在的,它们的存在和大小依赖于单个数据块的各种对齐要求。

2)设备树一般性

            设备树自身被分割成两个不同的块,一个“结构块”,和一个“字符串块”。两个块都需要四字节对齐。

            首先在祥述存储格式之前,我们快述的描述设备树概念。

            顾名思义,设备树是一颗树,所以,除了root节点没有“parent”外,所有节点有且仅有几个“parent”节点。

            每个节点都有两个名字,“实际节点名”:包含在节点属性列表中的属性类型名。它的值是一串以“0”终止的字符串。

            还有一个“单元节点名(unit name)”,它用于区分同一级别的相同名字的节点。“@”标记,和“unit address”用来定义节点所在的总线类型。

            “unit name”没有当作一个单个的属性存在,而是包含在设备树结构中。典型用于代表设备树的“path”。

            每个节点(node)实际上代表一个实际的设备(更确切的说,该节点不仅仅是许多节点的一个虚拟“容器”,例如:“/cpus”)。每个节点也需要包括一个“device_type”属性,来指示节点的类型。

            最后,每个节点都可以被其它节点参考,通过一个“linux,phandle”属性来表示。然而,如果直接使用平坦设备树,那麽该属性是可选的。

“linux,phandle”属性是一个32位的独一无二的值,用来识别一个节点。你可以自由的使用任何值或系统值、内部指针、或任何其它东西来产生这些值。唯一需求是:你提供给每一个节点的属性是独一无二的。

            下面是一个简单设备树的的实例。在该例子中,“o”指示一个节点,紧跟其后的是节点“unit name”。属性是那些紧跟节点名后的内容。这些“内容”呈现出一串ASCII码字符串(以0终止)值,而“内容”以32位十六进制值表示。

  / o device-tree

      |- name = "device-tree"

      |- model = "MyBoardName"

      |- compatible ="MyBoardFamilyName"

      |- #address-cells = <2>

      |- #size-cells = <2>

      |- linux,phandle = <0>

      |

      o cpus

      | | - name = "cpus"

      | | - linux,phandle = <1>

      | | - #address-cells = <1>

      | | - #size-cells = <0>

      | |

      | o PowerPC,970@0

      |  |- name = "PowerPC,970"

      |  |- device_type = "cpu"

      |  |- reg = <0>

      |  |- clock-frequency = <5f5e1000>

      |  |- 64-bit

      |  |- linux,phandle = <2>

      |

      o memory@0

      | |- name = "memory"

      | |- device_type = "memory"

      | |- reg = <00000000 00000000 0000000020000000>

      | |- linux,phandle = <3>

      |

      o chosen

        |- name = "chosen"

        |- bootargs = "root=/dev/sda2"

        |- linux,phandle = <4>

3)设备树“结构”块

            设备树结构是一个线性的树型结构。“OF_DT_BEGIN_NODE”标记开始一个节点,“OF_DT_END_NODE”标记结束一个节点定义。孩子节点简单的定义在“OF_DT_END_NODE”之前。一个“标记”是一个32位的值。 设备树以“OF_DT_END”标记结束。

            下面是单个节点的基本结构:

     * 标记OF_DT_BEGIN_NODE (也就是0x00000001)

     * 对于版本 1 to 3,这是一个以0中止的字符串表示的节点完全路径,

                        以“/”起始。对于版本16或更高版本,这仅仅是一个节点“node name”

                        (或对于root节点,为空字符串)

     * [四字界边界对其的间隙]

     * 对于每一个属性:

        * 标记OF_DT_PROP (也就是0x00000003)

        * 32-bit 的属性值,以字节表示的大小 (如果没有值,为0)

        * 属性名的字符串块的32为偏移值

        * 属性值数据(如果存在)

        * [四字界边界对其的间隙]

     * [孩子节点(如果存在)]

     * 标记OF_DT_END_NODE (也就是0x00000002)

            所以,节点内容可以概要为:一个起始标记,一个完整的路径,一个属性列表,一个孩子节点列表,和一个结束标记。每一个孩子节点都是上述定义的完全节点结构。

4)设备树“字符串”块

            为了节省空间,属性名通常是冗余的,被单独存储在“字符串”块中。该块是整串以0为终止字符的字符串属性名连接在一起的简单块区域。Structure块中的设备树定义会包含从字符串块开始的偏移值。

3.需要的设备树内容

1)有关cells和address表示法的注释

            通用的规则由各种各样的Open Firmware文档定义。如果你选择使用设备树来描述一条总线(bus),并且存在一条OF bus binding,那麽你应该遵守这个规格。内核不需要设备树来描述每单个设备或总线。

            通常,一个设备的地址格式由parent总线类型定义,基于#address-cells和#size-cells属性。如果缺少该属性,那麽parent’s parent被使用,依次类推…。内核需要root节点具有那些属性,来定义直接映射到处理器总线上设备的地址格式。

            这两个属性定义“cells”来表示地址(address)和大小(size)。“cell”是一个32位的号码。例如,上面例子的每个属性都包含2,那麽地址(address)和大小(size)都由2 cells组成,并且每个都有64位数字组成(cells被连接,并且被预计为big endian格式)。大多数32位的实现应该定义#address-cells和#size-cells为1,它代表一个32位的值。一些32位的处理器允许物理地址超过32位,那麽这些处理器应该定义#address-cells为2。

            “reg”属性总是“address size”类型的组合,其中cells的address和size数目由总线的“#address-cells”和“size-cells”指定。当一条总线支持各种各样的地址空间,并且其它标志与给予的地址分配对应(象:可预取的,等等),那些标志通常被加到物理地址的top位中。例如,PCI物理总线由3个cells组成,bottom 2 cells包含自身实际的地址,而top cell包含地址空间指示,标志,和PCI总线&设备号。

            对于支持动态分配的总线,可以接收的实践是:不在“reg”中提供address(keep it to 0),而是提供一个flag来指示动态分配的地址,并且提供一个包含整个分配地址的“assigned-address”属性。细节参考PCI OF bindings。

            如果能够反映你的硬件状态,通常倾向选择“无地址空间位”和“无动态分配”的简单总线,因为存在的内核地址解析函数将可以解决这种情况。如果你定义了一种非常复杂的地址格式的总线类型,你将不得不为你的总线在最近kenel的prom_parse.c文件中添加总线翻译器。

            “reg”属性仅仅在给予的bus中定义addesses和sizes(如果#size-cells不为0)。为了向上翻译地址(也就是进入parent总线地址,并且可能进入CPU物理地址),所有的busses必需包含一个“ranges”属性。如果“ranges”属性在给予的级别被忽略,那麽将假定翻译是不可能的。一条总线的“ranges”属性是一个列表:

                        bus address,parent bus address,size

            “bus address”总线节点定义的总线格式,也就是说,对于PCI设备,它是一个PCI地址。所以,(bus address,size)定义了孩子设备的地址范围。“parent bus”是该总线的parent总线格式。例如,对于PCI Host控制器,它是一个CPU地址。对PCI<->ISA桥,它是一个PCI地址。在parent总线上定义了地址映射的起始范围。

 

2)有关“compatible(兼容)”属性的注释

            这些属性是可选的,但是在设备和root节点中是推荐的。“兼容”属性的格式是一系列以0为终止字符的字符串的连接。它允许设备表示与一类相似设备的兼容属性。在某些情况下,允许单个驱动匹配几个设备,而不考虑它们的实际名字。

3)有关“name”属性的注释

    用来定义设备的名字,另外在名字属性后添加和“@”和单元号,构成设备节点的单元名字属性“unit name”,这可以进一步区分具有相同名字的同类设备。

4.构建设备树必须的节点和属性

    这里先简要介绍一下构建设备树所必须的几个节点,并列出它们的一些属性,这些属性的意义将在本文的后半部结合一个典型的dts描述文件来分析。

1)根节点

其必须的参数包括:

    - model

    -#address-cells

    -#size-cells

    - device_type

    - compatible(推荐使用)

2)/cpu节点

    该节点是所有单个cpu子节点的父节点,其属性应该至少包含:

    -#address-cells

    -#size-cells

3)/cpus/*节点

    该节点属于/cpu节点的子节点,对应单个cpu的属性描述。需要的属性包括:

    -device_type

    - reg

    -d-cache-line-size

    -i-cache-line-size

    -d-cache-size

    -i-cache-size

    -timebase-frequency(推荐)

    -clock-frequency(推荐)

4)/memory节点

    该节点用来描述单板上内存的物理地址分布情况。属性包括:

    -device_type

    - reg

5)/chosen节点

    该节点有点特殊,通常它是OF用来存放环境变量信息、参数、默认输入输出设备信息的地方。但该节点在我们的dts文件中一般不进行描述,它是由UBOOT进行创建并填写的。其需要的属性包括:

    - bootargs(存放传给linux内核的command line信息)

    -linux,stdout-path(存放标准console设备的路径)

    -interrupt-controller(存放主中断控制器的"linux,phandle"属性)

6)/soc<SOCname>节点

    该节点用来描述这个PPC芯片的内部设备的,也就是SOC(system-on-a-chip)设备。一般包括UART、I2C、eTSEC、FEC、PIC等设备。该节点是对整个SOC属性的描述,其子节点会针对每一个设备进行具体描述。需要的属性包括:

    -device_type

    - ranges

    -bus-frequency

    - reg(推荐)

    -#address-cells(推荐)

    -#size-cells(推荐)

    -#interrupt-cells(推荐)

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
Linux设备树dts移植详解
Linux设备树语法详解
NXP iMX8mm I2C挂载wm8960音频芯片思路
嵌入式 PowerPC Linux 平台扁平设备树FDT解析
一文搞定 Linux 设备树
从零开始写设备树DTS【转】
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服