老铁们,大家好,相信还有很多朋友对于深入解析:Linux操作系统与设备树技术和的相关问题不太懂,没关系,今天就由我来为大家分享分享深入解析:Linux操作系统与设备树技术以及的问题,文章篇幅可能偏长,希望可以帮助到大家,下面一起来看看吧!
Linux 设备树数据的使用模型
作者: 格兰特Likelygrant.likely@secretlab.ca
本文介绍Linux如何使用设备树。概述
设备树数据格式可以在设备树使用页面上找到
位于https://github.com/devicetree-org/devicetree-specation
“开放固件设备树”或简称设备树(DT)是一个数据
描述硬件的结构和语言。更具体地说,是操作系统可读的硬件的描述。这样,操作系统就不需要对应用程序的详细信息进行硬编码。
从结构上讲,DT 是具有命名节点的树或无环图,节点可以封装任意数据的任意数量的命名属性。还存在一种机制来创建从一个节点到自然树结构之外的另一个节点的任何链接。
从概念上讲,一组通用的使用约定(称为“绑定”)定义了数据应如何出现在树中以描述典型的
硬件功能,包括数据总线、中断线、GPIO、连接和外设。
尽可能使用现有的绑定来描述硬件,以最大限度地利用现有的支持代码,但由于属性和节点名称只是文本字符串,因此可以轻松扩展现有的绑定或通过定义新的节点和属性来创建新的绑定。节点。警报,
但是,无需先做任何功课,就可以在已存在的内容上创建新的绑定。目前存在两种不同的不兼容性,因为新i2c 总线的绑定是在没有首先研究如何在现有系统中枚举i2c 设备的情况下创建的。
1.历史
DT 最初是由Open Firmware 创建的,作为一种通信方法程序(类似于操作系统),用于将数据从Open Firmware 传递到客户端。操作系统在运行时使用设备树来发现硬件的拓扑结构,并且
这支持大多数可用的硬件,无需硬编码信息(假设驱动程序适用于所有设备)。
由于开放固件通常在PowerPC 和SPARC 平台上使用,因此Linux 对这些架构的支持长期以来一直使用设备树。
2005 年,当PowerPC Linux 开始重大清理并合并32 位和64 位支持时,决定要求所有
powerpc平台,无论是否使用开放固件(Open Firmware)。为此,DT 代表称为扁平设备(Flattened Device)
树(FDT) 已创建,并且可以作为二进制文件传递到内核blob,而不需要真正的开放固件实现。 U盘启动,
kexec 和其他引导加载程序经过修改,以支持传递设备树二进制文件(dtb) 并在引导时修改dtb。 DT 是
还添加到PowerPC 启动包装器(arch/powerpc/boot/*),以便dtb 可以与内核映像一起包装,以支持启动现有的非DT 感知固件。
一段时间后,FDT 基础设施被所有架构普遍使用。在撰写本文时,有6 个主线架构(arm、microblaze、mips、powerpc、sparc 和x86)和1 个非主线架构(nios) 具有一定程度的DT 支持。
2.数据模型
如果您还没有阅读设备树用法[1]页面,
那么现在就读吧。没关系,我会等.
2.1高级视图
最重要的是要理解DT只是描述硬件的数据结构。它没有什么神奇之处,也不会神奇地解决所有硬件配置的问题。它的作用是提供一种将主板中的硬件配置与设备驱动程序中的支持相结合的方法
Linux 内核(或任何其他操作系统)。使用它可以使板和设备支持变得数据驱动;使能
基于传递到内核的数据,而不是基于每台机器的硬编码选择。
理想情况下,数据驱动的平台设置应减少代码重复量,并更轻松地使用单个内核映像支持各种硬件。
Linux 使用DT 数据的三个主要目的:
1)平台标识;
2) 运行时配置,以及
3)设备数量。
2.2平台识别
首先,内核会使用DT中的数据来识别具体的机器。在理想的情况下,特定平台对于内核来说不应该是至关重要的,因为所描述的所有平台细节都可以通过设备树以一致且可靠的方式完美实现。尽管硬件并不完美,但内核必须识别出机器在早期启动期间正在运行,以便有机会运行特定于机器的修复程序。
大多数情况下,机器身份是无关紧要的,内核会根据计算机的内核选择来设置代码
CPU 或SoC。例如,在ARM 上,位于arch/arm/kernel/setup.c 的setup_arch() 将调用在machine_desc 中搜索的arch/arm/kernel/devtree.c 中的setup_machine_fdt()
表并选择与设备树最匹配的machine_desc 数据。它通过查看根设备树节点中的“兼容”属性并将其与struct machine_desc 中的dt_compat 列表进行比较(在arch/arm/include/asm/mach/arch.h 中(如果您好奇的话)来确定最佳匹配)。
“兼容”属性包含一个有序列表,该列表以包含机器确切名称的字符串开头,后跟兼容主板的可选列表(从最兼容到最不兼容)。例如TI BeagleBoard 及其根兼容属性
后续产品BeagleBoard xM 板可能分别如下所示:
兼容="ti,omap3-beagleboard", "ti,omap3450", "ti,omap3";
兼容="ti,omap3-beagleboard-xm", "ti,omap3450", "ti,omap3";如果“ti,omap3-beagleboard-xm”指定了确切的型号,它还会声称它与OMAP 3450 SoC 兼容,并且omap3 系列与通用SoC 兼容。您会注意到该列表按从最具体(精密板)到最不具体(SoC 系列)的顺序排序。
精明的读者可能会指出,Beagle xM 也可能声称与原始Beagle 板兼容。然而,应该注意的是,通常在板级上进行此操作,因为即使在同一产品线中,从一块板到另一块板的变化也很大,因此很难准确确定一块板何时声称兼容与另一个。对于顶层,最好保持谨慎,不要声称一个板与另一个板兼容。值得注意的例外是当主板是另一个主板的载体时,例如连接到主板的CPU 模块载板。
关于兼容值的另一个注释。兼容性中使用的任何字符串属性都必须记录它们所指示的内容。在文档/设备树/绑定中添加兼容字符串的文档。
同样在ARM 上,对于每个machine_desc,内核都会查看是否有任何dt_compat 列表条目出现在兼容属性中。如果是,则machine_desc 是驱动程序机器。搜索整个machine_descs 表后,
setup_machine_fdt() 根据每个machine_desc 匹配的兼容性属性中的条目返回“最兼容”的machine_desc。如果没有找到匹配的machine_desc,则返回NULL。
该方案背后的原因是观察到在某些情况下单个machine_desc 可以支持大量板
如果它们都使用相同的SoC 或相同的SoC 系列。然而,总会有一些例外,特定的董事会会
需要特殊的设置代码,在正常情况下没有用处。可以通过以下方式显式检查特殊情况是否存在
通常会在代码中设置麻烦的板,但如果板数量多的话,很快它就会变得丑陋和/或难以维护。
相反,兼容性列表允许通用machine_desc 规定通过在dt_compat 列表中指定“less”值来支持各种通用板。在上面的示例中,通用板支持可能需要与“ti,omap3”或“ti,omap3450”兼容。如果在原始Beagleboard 上发现错误,需要在早期启动期间使用特殊的解决方法代码,然后重新启动machine_desc 可以添加以实现解决方法,并且仅匹配“ti,omap3-beagleboard”。
PowerPC 使用稍微不同的方案,它从每个machine_desc 调用.probe() 挂钩,并使用第一个挂钩返回TRUE。但是,这种方法没有考虑兼容性列表,并且应避免支持新架构。
2.3运行时配置
在大多数情况下,DT 是从固件向内核传递数据的唯一方式,因此也习惯于传递运行时和
配置数据,例如内核参数字符串和initrd 镜像位置。
大多数数据包含在/chosen 节点和启动时
Linux 看起来像这样:
选择了{
bootargs="console=ttyS0,115200 loglevel=8";
initrd-start=0xc8000000;
initrd-end=0xc8200000;
}; bootargs属性包含内核参数和initrd-*
属性定义initrd Blob 的地址和大小。注意
initrd-end 是initrd 映像之后的第一个地址,所以这不是
匹配结构化资源的常用语义。所选节点也可以是
(可选)包含任意数量的附加属性
特定于平台的配置数据。
在早期启动期间,架构设置代码调用of_scan_flat_dt()
多次使用不同的辅助回调来解析设备树
分页前设置数据。 of_scan_flat_dt() 代码扫描通过
设备树并使用助手提取所需信息
在早期启动期间。通常,early_init_dt_scan_chosen() 帮助程序
用于解析选定的节点,包括内核参数,
Early_init_dt_scan_root()初始化DT地址空间模型,
Early_init_dt_scan_memory() 确定大小和
可用RAM 的位置。
在ARM 上,函数setup_machine_fdt() 负责早期
选择正确的machine_desc后扫描设备树
支持董事会。
2.4设备数量
确定板卡并解析早期配置数据后,内核初始化可以正常进行。在此过程中的某个时刻,会调用unflatten_device_tree() 将数据转换为更有效的运行时表示形式。
这也是调用特定于机器的安装挂钩的时候,例如ARM 上的machine_desc .init_early()、init_irq() 和.init_machine() 挂钩。本节的其余部分使用ARM 的示例实现,但所有体系结构在使用DT 时都会执行几乎相同的操作。
正如您从名称中可以猜到的那样,init_early() 用于需要在启动过程早期执行的任何特定于机器的设置,而.init_irq() 用于设置中断处理。使用DT 本身并不会改变这两个函数的行为。
如果提供了DT,init_early() 和.init_irq() 都可以调用任何DT 查询函数(include/linux/of*.h 中的of_*)来获取有关平台的附加数据。
DT 上下文中最有趣的钩子是.init_machine(),它主要负责使用以下命令填充Linux 设备模型
有关平台的数据。从历史上看,嵌入式平台通过定义一组静态时钟结构、platform_devices 和板上的其他数据,并将它们注册到.init_machine() 中来支持.c 文件。如果使用DT,则不必为每个平台硬编码静态设备,而是可以通过解析DT并分配设备来获取设备动态结构。
最简单的情况是.init_machine() 只负责注册platform_devices 块。 platform_device 是Linux 使用的一个概念,用于表示硬件无法检测到的内存或I/O 映射设备,以及“复合”或“虚拟”设备(稍后将详细介绍这些设备)。尽管DT没有“平台设备”术语,但平台设备大致对应于根目录中的设备节点树和简单的内存映射总线节点的后代。
现在是展示你的例子的好时机。这是NVIDIA Tegra 主板的设备树。
/{
兼容="nvidia,和谐","nvidia,tegra20";
#地址单元=1;
#大小单元格=1;
中断父级=intc;
选择了{};
别名{ };
记忆{
device_type="内存";
reg=0x000000000x40000000;
};
社会{
兼容="nvidia,tegra20-soc","简单总线";
#地址单元=1;
#大小单元格=1;
范围;
intc: 中断控制器@50041000 {
兼容="nvidia,tegra20-gic";
中断控制器;
#中断单元=1;
reg=0x500410000x1000,0x500401000x0100;
};
序列号@70006300 {
兼容="nvidia,tegra20-uart";
reg=0x700063000x100;
中断=122;
};
i2s1: i2s@70002800 {
兼容="nvidia,tegra20-i2s";
reg=0x700028000x100;
中断=77;
编解码器=wm8903;
};
i2c@7000c000 {
兼容="nvidia,tegra20-i2c";
#地址单元=1;
#大小单元格=0;
reg=0x7000c0000x100;
中断=70;
wm8903: 编解码器@1a {
兼容="wlf,wm8903";
reg=0x1a;
中断=347;
};
};
};
声音{
兼容="nvidia,和谐声音";
i2s-控制器=i2s1;
i2s-编解码器=wm8903;
};
};init_machine()时,需要检查Tegra板支持代码
该DT 并标识要为其创建platform_devices 的节点。
然而,单看这棵树,却无法立刻分辨出它是什么树。
每个节点代表的设备数量,或者即使一个节点代表一台设备
没有/chosen、/aliases 和/memory 节点根本不提供信息
没有描述设备的节点(尽管可以说内存可能是
被视为设备)。 /soc 节点的子节点是内存映射的
设备,但codec@1a是一个i2c设备,声音节点
它并不代表设备,而是代表其他设备如何连接。
一起创建音频子系统。我知道每个设备是什么
因为我熟悉电路板设计,但是内核呢?
知道如何处理每个节点吗?
诀窍是内核从树的根部开始,看起来像
具有“兼容”属性的节点。首先,通常
任何具有“兼容”属性的节点都被假定代表一个设备
其次,可以假设根的任意节点
该树要么直接连接到处理器总线,要么是
无法以任何其他方式描述的其他系统设备。
对于每个节点,Linux 分配并注册一个
platform_device,它又可以绑定到platform_driver。
为什么对这些节点使用platform_device 是一个安全的假设?
好吧,对于Linux 设备建模的方式来说,几乎所有的bus_types
假定其设备是总线控制器的子设备。为了
例如,每个i2c_client 都是i2c_master 的子级。每个spi_device
是SPI 总线的后代。同样适用于USB、PCI、MDIO 等。
DT 中也存在相同的层次结构,其中只有I2C 设备节点
曾经作为I2C总线节点的后代出现。与上面的SPI、MDIO、USB 相同
等唯一不需要特定父类型的设备
设备是platform_devices (和amba_devices,但更多关于
稍后),它会很高兴地存在于Linux /sys/devices 之上
树。因此,如果DT节点位于树的根部,那么它
事实上,最好将其注册为platform_device。
Linux 板支持代码调用of_platform_populate(NULL, NULL, NULL, NULL)
设备开始在树的底部被发现。的
参数全部为NULL,因为来自
树,不需要提供起始节点(第一个NULL),
父结构设备(最后一个NULL)并且我们没有使用匹配
表(还)。对于只需要设备注册的板,
.init_machine() 可以完全为空,除了
of_platform_populate() 调用。
在Tegra 示例中,这解释了/soc 和/sound 节点,但是
那么SoC节点的后代呢?他们不应该注册吗?
作为平台设备?对于Linux DT 支持,一般行为
用于由父设备驱动程序注册子设备
driver.probe() 时间。因此,i2c总线设备驱动程序会注册一个
每个子节点都有一个SPI 总线驱动程序将注册的i2c_client
它的spi_device 子级,以及类似的其他总线_类型。
根据这一模型,我们可以将绑定写入
SoC节点,只需为每个节点注册platform_devices
孩子们。主板支持代码将分配并注册SoC
设备,(理论上)SoC设备驱动程序可以绑定到SoC设备,
并为/soc/interrupt-controller、/soc/serial 注册platform_devices,
/soc/i2s 和/soc/i2c 在其.probe() 挂钩中。容易吧?
事实上,事实证明,注册一些
platform_devices 和更多platform_devices 是一种常见模式,
设备树支持代码反映了这一点并给出了上面的示例
更简单。 _platform_populate()的第二个参数是
of_device_id 表以及与该表中的条目匹配的任何节点
它的子节点也将被注册。对于Tegra,代码
它可以看起来像这样:
静态无效__init Harmony_init_machine(无效)
{
/* . */
of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
将“简单总线”定义为设备树规范中的属性
表示简单的内存映射总线,因此是of_platform_populate() 代码
可以写成假设节点与简单总线兼容
总是被遍历。但是,我们将其作为参数传递给
默认行为始终可以被主板支持代码覆盖。
[需要添加有关添加i2c/spi/etc 子设备的讨论]
附录A:AMBA设备
ARM Primecell 是连接到ARM AMBA 的某种设备
总线,其中包括对硬件检测和电源的一些支持
管理。在Linux中,struct amba_device和amba_bus_type是
用于表示Primecell 设备。但奇怪的是
并非AMBA 总线上的所有设备都是Primecell,对于Linux 来说是
amba_device 和platform_device 实例的典型情况是
同一公交车段的兄弟姐妹。
好了,关于深入解析:Linux操作系统与设备树技术和的问题到这里结束啦,希望可以解决您的问题哈!
【深入解析:Linux操作系统与设备树技术】相关文章:
2.米颠拜石
3.王羲之临池学书
8.郑板桥轶事十则
用户评论
我一直想了解一下 Linux 和设备树之间的关系,这篇文章应该能解决我的疑惑。
有10位网友表示赞同!
最近在学习嵌入式系统,感觉设备树这个概念非常重要,希望能在这篇文章中深入理解。
有8位网友表示赞同!
以前总是在用 BIOS 配置硬件,不过后来听说 Linux 使用设备树更方便,想看看是不是真的。
有15位网友表示赞同!
看标题,这篇文章应该能介绍怎么在 Linux 系统中配置各种设备。
有15位网友表示赞同!
我已经开始尝试用 Linux 构建我的嵌入式平台了,对设备树这个东西很感兴趣。
有18位网友表示赞同!
我觉得学习硬件驱动开发离不开对设备树的理解,希望这篇文章能让我系统地学习。
有20位网友表示赞同!
我听说设备树可以提升 Linux 的模块化和可移植性,很想深入了解背后的原理。
有7位网友表示赞同!
看来设备树是一个很重要的概念,它为 Linux 系统提供了强有力的硬件配置机制。
有8位网友表示赞同!
想看看这篇文章是否会介绍一些实际案例,让我能更直观地理解设备树的工作方式。
有9位网友表示赞同!
我一直对开放源代码的软件开发非常感兴趣,这篇文章正好与我的研究方向相关。
有18位网友表示赞同!
希望这篇文章能够清晰解释设备树的一些复杂概念,例如它的文件结构和语法规则。
有14位网友表示赞同!
学习 Linux 始终是一个有趣的旅程,现在又多了一门新的知识点——设备树!
有19位网友表示赞同!
我觉得嵌入式系统工程师必须要熟悉设备树,因为它直接影响硬件的配置和性能。
有19位网友表示赞同!
这篇文章应该能帮助我更好地理解为什么设备树在硬件驱动开发中扮演这么重要的角色。
有12位网友表示赞同!
Linux 系统的功能非常强大,而设备树则是其中的一部分,我想了解它究竟是如何实现这一切的。
有17位网友表示赞同!
对于想要学习嵌入式系统开发的人来说,这篇文章应该是一份很好的入门指南。
有13位网友表示赞同!
期待看到一些图解和代码示例,让我能够更深入地掌握设备树的使用技巧。
有13位网友表示赞同!