您当前的位置:首页 > 计算机 > 系统应用 > Linux

linux 设备树

时间:07-21来源:作者:点击数:

最终还是搞到这来了,学习中,记录点笔记

设备树从/根开始,这点接触过Linux的人都懂。

设备树的内容其实还是为了给驱动用的,所以其实内容都是由驱动决定的,不过实际写的时候遵循一定语法


/*  ethernet@e000b000 {
            compatible = "cdns,zynq-gem", "cdns,gem";
            reg = <0xe000b000 0x0000012e>;
            status = "okay";
            interrupts = <0x00000000 0x00000018 0x00000001>;
            clocks = <0x00000001 0x00000001 0x00000135 0x5f636c6b 0x00000000 0x0000000f>;
            clock-names = "pclk", "hclk", "tx_clk";
            #address-cells = <0x00000001>;
            #size-cells = <0x00000000>;
            local-mac-address = [00 0a 35 00 00 00];
            phy-mode = "mii";
            ethernet-phy@0 {
                reg = <0x00000000>;
            };
        }; */

例如上面这一段 ethernet@e000b000 这个节点,ethernet表示节点名字,字面看是网卡,其实完全可以起一个其他名字诸如wangka1这样的,后面@e000b000表示设备寄存器首地址,多个网卡可以用相同的节点名字,但寄存器地址不一样就行。节点包含了寄存器地址后,属性里就必须包含reg= <>;这个属性,地址嘛还是@后面那个地址。

ethernet_phy: ethernet-phy@0 {
                reg = <0>;
        };

另外,像这样写的 ethernet_phy : 这个冒号前面的就是个标签。其他地方可以引用。

 clock-names = "pclk", "hclk", "tx_clk";
            #address-cells = <0x00000001>;
            #size-cells = <0x00000000>;

上面的就是节点的属性了。比节点名还可以多2个字符 ? #,搞不懂为啥这么搞,第一次看到#还以为是注释。。。。。

  • compatible 这个属性用来匹配驱动 如
    -”compatible = “fsl,mpc8641-uart”, “ns16550"
    用逗号隔开的字符串数组,第一个最精确,第二格次之,以此类推,越到后面越不精确。
  • model 表示设备型号,一般只一个字符串就行。
  • 下面的拷贝别人的。"#address-cells","#size-cells",“reg”,“ranges”,"dma-ranges"属性都是和地址有关的。不同的平台,不同的总线,地址位长度可能不同,有32位地址,有64位地址,为了适应这个,规范规定一个32位的长度为一个cell。
  • "#address-cells"属性用来表示总线地址需要几个cell表示,该属性本身是u32类型的。
  • “#size-cells"属性用来表示子总线地址空间的长度需要几个cell表示(地址空间大小范围 ?),属性本身的类型也是u32。可以这么理解父节点表示总线,总线上每个设备的地址长度以及地址范围是总线的一个特性,用”#address-cells","#size-cells"属性表示,比如总线是32位,那么"#address-cells"设置成1就可以了。这两个属性不可以继承,就是说在未定义这两个属性的时候,不会继承更高一级父节点的设置,如果没有设置的话,内核默认认为"#address-cells"为2,"#size-cells"为1。
  • “reg"属性用来表示节点地址资源的,比如常见的就是寄存器的起始地址及大小。要想表示一块连续地址,必须包含起始地址和空间大小两个参数,如果有多块地址,那么就需要多组这样的值表示。还有另外一个问题,地址和大小用几个u32表示呢?这个就由父节点的”#address-cells","#size-cells"属性确定。总线上设备在总线地址和总线本身的地址可能不同,"ranges"属性用来表示如何转换。和"reg"属性类似,"ranges"属性也是类型的属性,不同的是’ranges’属性的每个元素是三元组,按照前后顺序分别是(子总线地址,父总线地址,大小)。子总线地址需要几个u32表示由’ranges’属性所在节点的’#address-cells’属性决定,父总线地址需要几个u32表示由上一级节点的’#address-cells’属性决定,大小需要几个u32表示由当前节点的’#size-cells’属性确定。
  • 有的时候在一个节点中需要引用另外一个节点,比如某个外设的中断连在哪个中断控制器上。在讲节点那一节我们说过,可以通过节点的全路径指定是哪个节点,但这种方法非常繁琐。
  • "phandle"属性是专门为方便引用节点设计的,想要引用哪个节点就在该节点下边增加一个’phandle’属性,设定值为一个u32,如’phandle = <1>’,引用的地方直接使用数字1就可以引用该节点,如’interrupt-parent = <1>’。以上是规范中描述的方法,实际上这样也不方便,我在实际的代码中没有看到这么用的。还记得节点那节说过节点名字前边可以定义一个标签吧,实际情况是都用标签引用,比如节点标签为intc1,那么用’interrupt-parent = <&intc1>'就可以引用了。
  • "status"属性用来表示节点的状态的,其实就是硬件的状态,用字符串表示。‘okay’表示硬件正常工作,“disabled”表示硬件当前不可用,“fail”表示因为出错不可用,“fail-sss”表示因为某种原因出错不可用,sss表示具体的出错原因。实际中,基本只用’okay’和’disabled’。

中断一般包括中断产生设备和中断处理设备。中断控制器负责处理中断,每一个中断都有对应的中断号及触发条件。中断产生设备可能有多个中断源,有时多个中断源对应中断控制器中的一个中断,这种情况中断产生设备的中断源称之为中断控制器中对应中断的子中断。一般情况中断产生设备数量要多于中断控制器,多个中断产生设备的中断都由一个中断控制器处理,这种多对一的关系也很像一个树形结构,所以在设备树中,中断也被描述成树,叫中断树。以下表述的时候为了明确是在说中断树,在父节点和子节点前边我们都加上“中断”二字,是为了防止和设备树的父节点、子节点混淆(虽然大部分情况设备树的父子关系就是中断树的父子关系,但是因为存在特例,所以我们还是强调是中断父子关系)。

中断产生设备用interrupts属性描述中断源(interrupt specifier),因为不同的硬件描述中断源需要的数据量不同,所以interrupts属性的类型也是。为了明确表示一个中断由几个u32表示,又引入了#interrupt-cells属性,#interrupt-cells属性的类型是u32,假如一个中断源需要2个u32表示(一个表示中断号,另一个表示中断类型),那么#interrupt-cells就设置成2。有些情况下,设备树的父节点不是中断的父节点(主要是中断控制器一般不是父节点),为此引入了interrupt-parent属性,该属性的类型是,用来引用中断父节点(我们前边说过,一般用父节点的标签,这个地方说中断父节点而不是中断控制器是有原因的)。如果设备树的父节点就是中断父节点,那么可以不用设置interrupt-parent属性。interrupts属性和interrupt-parent属性都是中断产生设备节点的属性,但是#interrupt-cells属性不是,#interrupt-cells属性是中断控制器节点以及interrupt nexus节点的属性,这两类节点都可能是中断父节点。

中断控制器节点用interrupt-controller属性表示自己是中断控制器,这个属性的类型是空,不用设置值,只要存在这个节点就表示该节点是中断控制器。除了这个属性外,中断控制器节点还有#interrupt-cells属性,用来表示该中断控制器直接管理下的interrupt domain(后边我们会讲中断控制器的中断子节点interrupt nexus节点有单独的interrupt domain)用几个u32表示一个中断源(interrupt specifier)。中断控制器节点就包括interrupt-controller和#interrupt-cells两个关于中断的属性。中断控制器的#address-cells属性和中断映射有关系,但是该属性不是为中断设计的,中断映射只是用到了这个属性而已。

从软件的层面讲model属性仅仅表示一个名字而已,没有更多的作用。compatible属性则不同,该属性决定软件如何匹配硬件对硬件进行初始化。属性那一节我们说过compatible属性的类型是字符串数组,按照范围从小到大的顺序排列,每个字符串表示一种匹配类型。根节点的compatible属性表示平台如何匹配,比如‘compatible = “samsung,smdk5420”, “samsung,exynos5420”, “samsung,exynos5”’,表示软件应该首先匹配’samsung,smdk5420’,这个是一款开发板。如果无法匹配,再试着匹配"samsung,exynos5420",这个是一款芯片平台。如果还是无法匹配,还可以试着匹配 “samsung,exynos5”,这是一个系列的芯片平台。这里说的匹配是指软件根据该信息找到对应的代码,如对应的初始化函数。

关于根节点的"compatible"属性我们就说到这,一句话总结下就是内核通过"compatible"属性找到对应的平台描述信息,按照范围从小到大尽量匹配范围最小的,如果匹配不到,那么说明内核不支持该平台,系统将在初始化的时候就出错。

根节点还可能包含的属性为#address-cells和#size-cells,规范中说明这两个属性是必须的,实际应用时是可选的,还记得属性那一节说这两个属性如果没有都是有默认值的,#address-cells默认值为2,#size-cells默认值为1。根节点下必须包含的子节点为cpus和memory,后边会说明cpus下边还有每个cpu的子节点,memory节点下边定义的就是memory的起始地址及大小,所以根节点的#address-cells和#size-cells属性实际上说明的就是从cpu角度看系统总线的地址长度和大小。

根节点那一节我们说过,最简单的设备树也必须包含cpus节点和memory节点。memory节点用来描述硬件内存布局的。如果有多块内存,既可以通过多个memory节点表示,也可以通过一个memory节点的reg属性的多个元素支持。举一个例子,假如某个64位的系统有两块内存,分别是

• RAM: 起始地址 0x0, 长度 0x80000000 (2GB)

• RAM: 起始地址 0x100000000, 长度 0x100000000 (4GB)

对于64位的系统,根节点的#address-cells属性和#size-cells属性都设置成2。一个memory节点的形式如下(还记得前几节说过节点地址必须和reg属性第一个地址相同的事情吧):

memory@0 {

device_type = “memory”;

reg = <0x000000000 0x00000000 0x00000000 0x80000000

0x000000001 0x00000000 0x00000001 0x00000000>;

};

两个memory节点的形式如下:

memory@0 {

device_type = “memory”;

reg = <0x000000000 0x00000000 0x00000000 0x80000000>;

};

memory@100000000 {

device_type = “memory”;

reg = <0x000000001 0x00000000 0x00000001 0x00000000>;

};

chosen节点也位于根节点下,该节点用来给内核传递参数(不代表实际硬件)。对于Linux内核,该节点下最有用的属性是bootargs,该属性的类型是字符串,用来向Linux内核传递cmdline。规范中还定义了stdout-path和stdin-path两个可选的、字符串类型的属性,这两个属性的目的是用来指定标准输入输出设备的,在linux中,这两个属性基本不用。

方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门