meiyao 发表于 2024-3-25 20:37

《原子Linux驱动开发》阅读DTB和函数篇(设备树)

<p>smdk2440_b_info&nbsp;是一个结构体变量,它用于描述 SMDK2440 开发板上的 LCD 信息。而&nbsp;smdk2440_devices&nbsp;是一个结构体指针数组,它用于描述 SMDK2440 开发板上的所有平台相关信息。</p>

<p>在嵌入式 Linux 系统中,特别是在为 ARM 架构的开发板(如 SMDK2440)编写驱动程序或进行设备树配置时,我们经常需要定义这样的结构体来描述硬件设备的属性和配置。这些结构体通常包含设备的类型、地址、中断号、资源等信息,这些信息对于内核在启动时初始化和配置硬件是必要的。</p>

<p>下面看一下树状图:</p>

<p>&nbsp;</p>

<p> &nbsp;我贴一个简化的例子来说明这种结构体和指针数组的用法,这里我只贴一部分,:</p>

<p><strong>LCD 信息结构体</strong>:</p>

<pre>
<code>typedef struct {
    char *name;          // LCD 名称
    unsigned long addr;// LCD 控制器的物理地址
    int width;         // 屏幕宽度
    int height;          // 屏幕高度
    // ... 其他与 LCD 相关的属性
} lcd_info_t;</code></pre>

<p>&nbsp;</p>

<p><strong>平台设备信息结构体</strong>:</p>

<pre>
<code>typedef struct {
    char *name;          // 设备名称
    void *platform_data; // 设备特定的数据
    // ... 其他与平台设备相关的属性
} platform_device_info_t;</code></pre>

<p>&nbsp;</p>

<p><strong>定义 LCD 信息结构体变量</strong>:</p>

<pre>
<code>lcd_info_t smdk2440_b_info = {
    .name = "SMDK2440 LCD",
    .addr = 0xXXXXXX,// 假设的物理地址
    .width = 800,
    .height = 600,
    // ... 其他初始化值
};</code></pre>

<p>指针:</p>

<pre>
<code>platform_device_info_t *smdk2440_devices[] = {
    &amp;(platform_device_info_t){
      .name = "SMDK2440 LCD",
      .platform_data = &amp;smdk2440_b_info,// 指向 LCD 信息结构体的指针
      // ... 其他初始化值
    },
    // ... 其他平台设备信息结构体指针
    NULL// 数组结束标记
};</code></pre>

<p>再说一下</p>

<p>串口设备节点的reg属性定义了serial设备寄存器的起始地址和长度,这在Linux设备树(Device Tree)中是很常见的做法。reg属性描述了设备在物理内存中的地址空间,使得操作系统内核能够知道如何与硬件设备进行通信。</p>

<p>在您提供的例子中,serial设备寄存器的起始地址被定义为0x4600,寄存器长度为0x100。这意味着从地址0x4600开始,有0x100(即256)个字节的空间被用于这个serial设备的寄存器。</p>

<p>经过地址转换,serial设备的新起始地址变为了0xe0004600。这个转换是通过将原始地址0x4600加上一个偏移量0xe0000000来实现的。这种地址转换通常在内存映射或者总线映射的场景下使用,特别是当设备被映射到不同的物理地址空间时。</p>

<p>在Linux内核中,当驱动程序或内核的其他部分需要访问这个serial设备的寄存器时,它会使用这个转换后的地址0xe0004600作为起始点,并根据reg属性中定义的长度(0x100)来确定可以访问的地址范围。</p>

<p>SOC串口设备节点,reg属性定义了serial设备寄存器的起始地址为 0x4600,寄存器长度为0x100。经过地址转换,seria!设备可以从0xe0004600开始进行读写操作,0xe000460=0x4600+0xe0000000.</p>

<p></p>

<p>&nbsp;</p>

<p>在Linux内核中,特别是在ARM架构的内核中,MACHINE_START和MACHINE_END宏通常用于定义特定于机器的信息,这些信息用于在内核启动时初始化硬件。这些宏使得内核能够以一种模块化的方式来支持多种不同的硬件平台。</p>

<p>arch/arm/include/asm/mach/arch.h文件包含了这些宏的定义,以及相关的数据结构,用于描述不同的ARM机器类型。</p>

<p>MACHINE_START宏通常用于定义一个静态的机器描述结构,该结构包含了机器的名称、类型、启动函数、定时器、I/O空间映射等信息。这些信息在内核初始化阶段非常重要,因为它告诉内核如何与特定硬件平台进行交互。</p>

<p>&nbsp;</p>

<p>&nbsp; \</p>

<p>&nbsp;</p>

<p>再贴 一个BOOtz的流程图:</p>

<p> &nbsp;Linux内核解析 DTB 文件:</p>

<p>&nbsp;</p>

<p> &nbsp;</p>

<p>在Linux内核中,尤其是当使用设备树(Device Tree)来描述硬件平台时,内核会在启动时解析设备树二进制文件(Device Tree Blob,简称DTB)。这个DTB文件包含了关于平台硬件布局和配置的详细信息。</p>

<p>设备树最初是为了解决在嵌入式系统中硬件描述多样性的问题而引入的。它允许将硬件描述从内核源代码中分离出来,使得内核能够支持更多的硬件平台而不需要对源代码进行大量的修改。</p>

<p>在Linux内核启动过程中,一旦设备树被解析,内核会根据解析得到的信息来配置和初始化硬件。同时,内核还会在/proc/device-tree目录下创建对应的设备节点文件。这些节点文件提供了一种用户空间可访问的方式来查看和查询内核是如何理解和配置硬件的。</p>

<p>&nbsp;</p>

<p>of find node by name()函数、of find _node by_type()函数、of find compatible node()函数的含义:</p>

<p> &nbsp;</p>

<p>总结:</p>

<p>Linux内核在启动过程中会解析设备树二进制文件(DTB),该文件包含了关于硬件平台布局和配置的详细信息。通过解析DTB,内核能够理解和配置各种硬件组件。为了在用户空间提供硬件配置的可见性,内核会在<code>/proc/device-tree</code>目录下生成相应的设备节点文件。这些文件允许开发者调试和验证设备树配置的正确性,并了解内核如何识别和配置不同的硬件组件。因此,设备树和设备树二进制文件是Linux内核实现硬件抽象和配置的关键部分,而<code>/proc/device-tree</code>目录则为开发者提供了一个查看这些信息的接口。</p>

beyond_笑谈 发表于 2024-4-6 16:32

<p>设备树讲的不错,在芯片厂提供的SDK和BSP基础上开发自己的控制器时,设备树需要根据自己控制器的硬件设计而更改。</p>

meiyao 发表于 2024-4-6 20:57

beyond_笑谈 发表于 2024-4-6 16:32
设备树讲的不错,在芯片厂提供的SDK和BSP基础上开发自己的控制器时,设备树需要根据自己控制器的硬件设计而 ...

<p><img height="48" src="https://bbs.eeworld.com.cn/static/editor/plugins/hkemoji/sticker/facebook/wanwan88.gif" width="59" /></p>
页: [1]
查看完整版本: 《原子Linux驱动开发》阅读DTB和函数篇(设备树)