Linux设备树实例分析

合集下载

设备树解析【转】

设备树解析【转】

设备树解析【转】转⾃:⼀、描述ARM Device Tree起源于OpenFirmware (OF),在过去的中,arch/arm/plat-xxx和arch/arm/mach-xxx中充斥着⼤量的垃圾代码,相当多数的代码只是在描述板级细节,⽽这些板级细节对于内核来讲,不过是垃圾,如板上的platform设备、resource、i2c_board_info、spi_board_info以及各种硬件的platform_data。

为了改变这种局⾯,Linux社区的⼤⽜们参考了PowerPC等体系中使⽤的Flattened Device Tree(FDT),也采⽤了Device Tree结构,许多硬件的细节可以直接透过它传递给Linux,⽽不再需要在kernel中进⾏⼤量的冗余编码。

Device Tree是⼀种描述硬件的,它起源于 OpenFirmware (OF)。

在Linux 2.6中,ARM架构的板极硬件细节过多地被硬编码在arch/arm/plat-xxx和arch/arm/mach-xxx,采⽤Device Tree后,许多硬件的细节可以直接透过它传递给Linux,⽽不再需要在kernel中进⾏⼤量的冗余编码。

Device Tree由⼀系列被命名的结点(node)和属性(property)组成,⽽结点本⾝可包含⼦结点。

所谓属性,其实就是成对出现的name和value。

在Device Tree中,可描述的信息包括(原先这些信息⼤多被hard code到kernel中):CPU的数量和类别内存基地址和⼤⼩总线和桥外设连接中断控制器和中断使⽤情况GPIO控制器和GPIO使⽤情况Clock控制器和Clock使⽤情况它基本上就是画⼀棵电路板上CPU、总线、设备组成的树,Bootloader会将这棵树传递给内核,然后内核可以识别这棵树,并根据它展开出Linux内核中的platform_device、i2c_client、spi_device等设备,⽽这些设备⽤到的内存、IRQ等资源,也被传递给了内核,内核会将这些资源绑定给展开的相应的设备。

linux设备树

linux设备树

linux设备树初识linux设备树1,什么是设备树DTS即Device Tree Source设备树源码,是⼀种描述硬件的数据结构。

以树状节点的⽅式描述⼀个设备的各种硬件信息细节:CPU、GPIO、时钟、中断、内存等,形成类似⽂本⽂件dts,直接透过它传递给linux,使得驱动程序和硬件分离,只需要修改dts⽂件,便能实现需求。

设备树易于扩展,硬件有变动时不需要重新编译内核或驱动程序,只需要提供不⼀样的dtb⽂件。

2,dts、dtsi、dtb⽂件作⽤、关系dtsi——类似于c语⾔的头⽂件dts——类似于c语⾔的源⽂件dtb——类似于c语⾔的编译产物、⼆进制⽂件由于⼀个soc可能有多个不同的电路板、⽽每个电路板拥有⼀个.dts。

这些dts势必会存在许多共同部分,为了减少代码的冗余,设备树将这些共同部分提炼保存在.dtsi⽂件中,供不同的dts共同使⽤。

编译⼯具编译.dts⽣成的⼆进制⽂件(.dtb),uboot在引导内核时,会预先读取.dtb到内存,进⽽由内核解析,系统启动。

3,基本构造{}包围起来的结构称之为节点,dts中最开头的/{},成为根节点。

节点的标准结构是xxx@yyy{...},xxx是节点的名字,yyy则不是必须的,其值为节点的地址(寄存器地址或其他地址),⽐如i2c1:i2c@021a0000中的就是⼀个i2c控制器的寄存器及地址,rtc:pcf8523@68中的就是这个rtc设备的i2c地址4,属性:地址有关节点的地址,⽐如i2c@021a0000,虽然它在名字后⾯跟了地址,但是正式的设置是在reg属性中设置的⽐如:reg =<0x021a0000 0x4000>; reg的格式通常为,0x021a0000是寄存器基地址,0x4000是长度。

address和length的个数是可变的,由⽗节点的属性#address-cells和#size-cells决定,⽐如节点i2c@021a0000的⽗节点是aips-bus@02000000;其#address-cells和#size-cells均为1,所以下⾯的i2c 节点的reg属性就有⼀个address和length,⽽i2c节点本⾝#address-cells和#size-cells分别为1和0,所以其下的rtc:pcf8523@68的reg属性就只有⼀个0x68(i2c地址)了。

LinuxDTS(DeviceTreeSource)设备树详解

LinuxDTS(DeviceTreeSource)设备树详解

LinuxDTS(DeviceTreeSource)设备树详解⼀.什么是DTS?为什么要引⼊DTS?DTS即Device Tree Source 设备树源码, Device Tree是⼀种描述硬件的数据结构,它起源于 OpenFirmware (OF)。

在Linux 2.6中,ARM架构的板极硬件细节过多地被硬编码在arch/arm/plat-xxx和arch/arm/mach-xxx,⽐如板上的platform设备、resource、i2c_board_info、spi_board_info 以及各种硬件的platform_data,这些板级细节代码对内核来讲只不过是垃圾代码。

⽽采⽤Device Tree后,许多硬件的细节可以直接透过它传递给Linux,⽽不再需要在kernel中进⾏⼤量的冗余编码。

每次正式的linux kernel release之后都会有两周的merge window,在这个窗⼝期间,kernel各个部分的维护者都会提交各⾃的patch,将⾃⼰测试稳定的代码请求并⼊kernel main line。

每到这个时候,Linus就会⽐较繁忙,他需要从各个内核维护者的分⽀上取得最新代码并merge到⾃⼰的kernel source tree中。

Tony Lindgren,内核OMAP development tree的维护者,发送了⼀个邮件给Linus,请求提交OMAP平台代码修改,并给出了⼀些细节描述:1)简单介绍本次改动2)关于如何解决merge conficts。

有些git mergetool就可以处理,不能处理的,给出了详细介绍和解决⽅案⼀切都很平常,也给出了⾜够的信息,然⽽,正是这个pull request引发了⼀场针对ARM linux的内核代码的争论。

我相信Linus⼀定是对ARM相关的代码早就不爽了,ARM的merge⼯作量较⼤倒在其次,主要是他认为ARM很多的代码都是垃圾,代码⾥⾯有若⼲愚蠢的table,⽽多个⼈在维护这个table,从⽽导致了冲突。

Linuxkernel有关spi设备树参数解析

Linuxkernel有关spi设备树参数解析

Linuxkernel有关spi设备树参数解析⼀、最近做了⼀个 spi 设备驱动从板级设备驱动升级到设备树设备驱动,这其中要了解 spi 设备树代码的解析。

⼆、设备树配置如下:503 &spi0 {504 status = "okay";505 pinctrl-name = "default";506 pinctrl-0 = <&spi0_pins>;507 ti,pindir-d0-out-d1-in;508509 wk2124A {510 compatible = "wk2124A"; // 匹配字符串511 reg = <0>; // cs512 # spi-cpha = <1>; // 配置 spi 的模式513 # spi-tx-bus-width = <1>; // 这是是 spi-tx 的总线宽度514 # spi-rx-bus-width = <1>;515 spi-max-frequency = <10000000>; // spi 最⼤速率配置516 };517 };三、代码跟踪// drivers/spi/spi.c2772 postcore_initcall(spi_init); // spi_init2733 static int __init spi_init(void)2734 {2735 int status;27362737 buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL);2738 if (!buf) {2739 status = -ENOMEM;2740 goto err0;2741 }27422743 status = bus_register(&spi_bus_type);2744 if (status < 0)2745 goto err1;27462747 status = class_register(&spi_master_class);2748 if (status < 0)2749 goto err2;27502751 if (IS_ENABLED(CONFIG_OF_DYNAMIC))2752 WARN_ON(of_reconfig_notifier_register(&spi_of_notifier)); // 这⾥要注册主机和从机27532754 return 0;27552756 err2:2757 bus_unregister(&spi_bus_type);2758 err1:2759 kfree(buf);2760 buf = NULL;2761 err0:2762 return status;2763 }2726 static struct notifier_block spi_of_notifier = {2727 .notifier_call = of_spi_notify,2728 };2686 static int of_spi_notify(struct notifier_block *nb, unsigned long action,2687 void *arg)2688 {2689 struct of_reconfig_data *rd = arg;2690 struct spi_master *master;2691 struct spi_device *spi;26922693 switch (of_reconfig_get_state_change(action, arg)) {2694 case OF_RECONFIG_CHANGE_ADD:2695 master = of_find_spi_master_by_node(rd->dn->parent); // 找到主机节点2696 if (master == NULL)2697 return NOTIFY_OK; /* not for us */26982699 spi = of_register_spi_device(master, rd->dn); // ---> 注册设备2700 put_device(&master->dev);27222723 return NOTIFY_OK;2724 }1428 #if defined(CONFIG_OF)1429 static struct spi_device *1430 of_register_spi_device(struct spi_master *master, struct device_node *nc)1431 {1432 struct spi_device *spi;1433 int rc;1434 u32 value;14351436 /* Alloc an spi_device */1437 spi = spi_alloc_device(master);1438 if (!spi) {1439 dev_err(&master->dev, "spi_device alloc error for %s\n",1440 nc->full_name);1441 rc = -ENOMEM;1442 goto err_out;1443 }14441445 /* Select device driver */1446 rc = of_modalias_node(nc, spi->modalias, // 匹配到从机1447 sizeof(spi->modalias));1448 if (rc < 0) {1449 dev_err(&master->dev, "cannot find modalias for %s\n",1450 nc->full_name);1451 goto err_out;1452 }14531454 /* Device address */1455 rc = of_property_read_u32(nc, "reg", &value); // 设备节点 reg 表⽰ cs1456 if (rc) {1457 dev_err(&master->dev, "%s has no valid 'reg' property (%d)\n",1458 nc->full_name, rc);1459 goto err_out;1460 }1461 spi->chip_select = value;14621463 /* Mode (clock phase/polarity/etc.) */ // 选择 spi 的模式1464 if (of_find_property(nc, "spi-cpha", NULL))1465 spi->mode |= SPI_CPHA;1466 if (of_find_property(nc, "spi-cpol", NULL))1467 spi->mode |= SPI_CPOL;1468 if (of_find_property(nc, "spi-cs-high", NULL)) // 选择 spi cs 是⾼有效还是低有效 1469 spi->mode |= SPI_CS_HIGH;1470 if (of_find_property(nc, "spi-3wire", NULL))1471 spi->mode |= SPI_3WIRE;1472 if (of_find_property(nc, "spi-lsb-first", NULL))1473 spi->mode |= SPI_LSB_FIRST;14741475 /* Device DUAL/QUAD mode */ // 选择单线还是双线通道1476 if (!of_property_read_u32(nc, "spi-tx-bus-width", &value)) {1477 switch (value) {1478 case 1:1479 break;1480 case 2:1481 spi->mode |= SPI_TX_DUAL;1482 break;1483 case 4:1484 spi->mode |= SPI_TX_QUAD;1485 break;1486 default:1487 dev_warn(&master->dev,1488 "spi-tx-bus-width %d not supported\n",1489 value);1490 break;1491 }1492 }14931494 if (!of_property_read_u32(nc, "spi-rx-bus-width", &value)) {1495 switch (value) {1496 case 1:1497 break;1498 case 2:1499 spi->mode |= SPI_RX_DUAL;1500 break;1501 case 4:1502 spi->mode |= SPI_RX_QUAD;1503 break;1504 default:1505 dev_warn(&master->dev,1506 "spi-rx-bus-width %d not supported\n",1508 break;1509 }1510 }15111512 /* Device speed */ // 设备速度配置1513 rc = of_property_read_u32(nc, "spi-max-frequency", &value);1514 if (rc) {1515 dev_err(&master->dev, "%s has no valid 'spi-max-frequency' property (%d)\n", 1516 nc->full_name, rc);1517 goto err_out;1518 }1519 spi->max_speed_hz = value;15201521 /* Store a pointer to the node in the device structure */1522 of_node_get(nc);1523 spi->dev.of_node = nc; // 保存设备结构体15241525 /* Register the new device */1526 rc = spi_add_device(spi);1527 if (rc) {1528 dev_err(&master->dev, "spi_device register error %s\n",1529 nc->full_name);1530 goto err_out;1531 }15321533 return spi;15341535 err_out:1536 spi_dev_put(spi);1537 return ERR_PTR(rc);1538 }。

我所认知的LINUX设备树

我所认知的LINUX设备树

设备树(Device tree)是一套用来描述硬件属相的规则.设备树是从软件使用的角度描述硬件的,不是从硬件设计的角度描述的。

我们在写设备树时没有必要按照硬件逻辑生搬硬套,也不要指望通过阅读设备树弄清楚硬件是如何设计的。

对于软件可以自动识别的硬件,如USB设备,PCI设备,也是没有必要通过设备树描述的。

我个人觉得规范内容是可以分为两个层次的。

第一层是关于设备树组织形式的,如设备树结构,节点名字的构成等,第一个层次是基础,是理解第二个层次的前提。

第二层是关于设备树内容的,如多核CPU怎样描述,一个具体的设备如何描述。

第二层可以看成是第一层的具体应用。

DTS(Device tree syntax,另一种说法是Device tree source)是设备树源文件,为了方便阅读及修改,采用文本格式。

DTC(Device tree compiler)是一个小工具,负责将DTS转换成DTB(Device tree blob)。

设备树首先是一个树形结构,并且是一棵树。

除了根节点外其他子节点都有唯一的父节点,节点下可以有子节点和属性(子节点可以看成是树枝,属性可以看成是叶子)。

属性由名字和值组成(名字是必须的,但是值不是必须的,如果只要根据是否存在这个属性就可以表示我们想要的功能,那么可以不需要有值)。

节点(node)的表示首先说节点的表示方法,除了根节点只用一个斜杠“/”表示外,其他节点的表示形式如“node-name@unit-address”。

@前边是节点名字,后边是节点地址。

节点地址是用来区别同名节点的,不是软件意义上的地址,但是有些情况可以用软件地址作为这个地址。

除此之外规范还要求,如果节点有地址,那么节点下边必须有一个叫reg的属性,并且该地址必须和reg的属性的第一个地址相同。

如果节点没有reg属性,那么节点地址及前边的@必须都不能有。

除了名字和地址外,节点前边还可以有一个标签(label),这个标签不是必须的,一般只有在别个地方需要引用这个节点时才会用标签标示这个节点,因为如果用全路径太繁琐了。

Linux内核驱动基础(7)设备树相关总线使用实例

Linux内核驱动基础(7)设备树相关总线使用实例

mmc总线使用实例broken-cd 表示没有热插拔探测引脚,使用轮询检测cd-gpios 使用gpio管脚作为热插拔探测引脚non-removable 表示不能进行热插拔,设备一直连接(比如eMMC) 上面三个选项用于指定热插拔探测选项,如果三个选项都没有指定,则使用主机自带的热插拔引脚sdcdbus-width 数据总线位宽,默认值是<1>,也可以是<4>,<8>wp-gpios 使用gpio管脚作为写保护引脚max-frequency 表示mmc总线最大操作频率cd-inverted 表示cd引脚是active highwp-inverted 表示wp引脚是active highno-1-8-v 该选项存在的话,表示该总线控制器不支持1.8v设备卡(即使该控制器硬件支持1.8v设备卡)cap-power-off-card 关闭该卡电源很安全cap-sdio-irq 使能SDIO总线IRQfull-pwr-cycle 支持设备卡的整个电源周期cap-sd-highspeed 支持SD high-speed时序cap-mmc-highspeed 支持MMC high-speed时序sd-uhs-sdr12 支持SD UHS SDR12 speedsd-uhs-sdr25 支持SD UHS SDR25 speedsd-uhs-sdr50 支持SD UHS SDR50 speedsd-uhs-sdr104 支持SD UHS SDR104 speedsd-uhs-ddr12 支持SD UHS DDR12 speedmmc-ddr-1_8v 支持eMMC high-speed DDR(1.8v)mmc-ddr-1_2v 支持eMMC high-speed DDR(1.2v)mmc-hs200-1_8v 支持eMMC HS200 modemmc-hs200-1_2v 支持eMMC HS200 modemmc-hs400-1_8v 支持eMMC HS400 modemmc-hs400-1_2v 支持eMMC HS400 modekeep-power-in-suspend 表示待机时不掉电enable-sdio-wakeup SDIO中断发生时允许唤醒主机系统mmc-pwrseq 指定电源上电顺序dsr 表示卡的DSR寄存器的值[从0x00 … 0xffff]vmmc-supply 指向regulator设备树节点mmc控制器存在子节点时,必须指定下面两个选项#address-cells 值必须是1#size-cells 值必须是0MMC子节点属性reg 该值必须是0~7,其中0表示SD卡,1~7表示SDIO设备比如SDIO接口的WIFI芯片上面有两个图,都是用来定义mmc控制器节点属性第一个图描述mmc控制器的不变属性,比如mmc控制器的寄存器地址,中断,dma通道,一般定义在.dtsi文件中第二个图描述mmc控制器的可变属性及其子节点属性,比如mmc 控制器的引脚复用情况,总线位宽,一般定义在.dts文件中注意:一般在公用的.dtsi文件中,添加不变属性(比如reg属性,中断,dma通道),在具体板卡的 .dts文件中添加可变属性(比如GPIO 使用情况,引脚复用情况),以及子结点信息,这样使得.dtsi文件和.dts 文件解耦status 表示使能该总线控制器,enable或者disableclock-frequency 设置i2c总线频率,一般为400KHZ或者100KHZi2c控制器节点存在子节点时,该控制器节点必须指定下面两个选项#address-cells 值必须是1#size-cells 值必须是0由于I2C子节点属性比较简单,因此不进行任何讲解上面有两个图,都是用来定义i2c控制器节点属性第一个描述i2c控制器的不变属性,比如i2c控制器的寄存器地址,中断,一般定义在.dtsi文件中第二个描述子节点属性,一般定义在.dts文件中status 表示使能该总线控制器,enable或者disablecs-gpios 使用gpio管脚作为片选信号num-cs 表示片选信号数量,包括原生片选信号和gpio片选信号比如:cs-gpios = <0> <0> <&gpio1 18 GPIO_ACTIVE_LOW> num-cs = <3>上述spi控制器片选信号的映射关系如下图所示spi控制器节点存在子节点时,该控制器节点必须指定下面两个选项#address-cells 值必须是1#size-cells 值必须是0SPI子节点属性spi-max-frequency 设置spi总线频率spi-cs-high 表示spi子设备片选信号为高电平spi-tx-bus-width 设置spi控制器输出总线位宽,默认为1(仅有一根数据线作为MOSI),QSPI(QUAD)总线位宽为4,spi-tx-bus-width = <4> spi-rx-bus-width 设置spi控制器输入总线位宽,默认为1(仅有一根数据线作为MISO),QSPI(QUAD)总线位宽为4,spi-rx-bus-width = <4>上图为spi控制器节点及其子节点属性:网络phy芯片ks8995m使用cs0片选信号,spi总线频率为1MHZ 音频芯片tlv320aic26使用cs1片选信号,spi总线频率为100KHZ。

转:Linux设备树(DeviceTree)机制

转:Linux设备树(DeviceTree)机制

转:Linux设备树(DeviceTree)机制⽬录1. 设备树(Device Tree)基本概念及作⽤在内核源码中,存在⼤量对板级细节信息描述的代码。

这些代码充斥在/arch/arm/plat-xxx和/arch/arm/mach-xxx⽬录,对内核⽽⾔这些platform设备、resource、i2c_board_info、spi_board_info以及各种硬件的platform_data绝⼤多数纯属垃圾冗余代码。

为了解决这⼀问题,ARM内核版本3.x之后引⼊了原先在Power PC等其他体系架构已经使⽤的Flattened Device Tree。

“A data structure by which bootloaders pass hardware layout to Linux in a device-independent manner, simplifying hardware probing.”开源⽂档中对设备树的描述是,⼀种描述硬件资源的数据结构,它通过bootloader将硬件资源传给内核,使得内核和硬件资源描述相对独⽴(也就是说*.dtb⽂件由Bootloader读⼊内存,之后由内核来解析)。

Device Tree可以描述的信息包括CPU的数量和类别、内存基地址和⼤⼩、总线和桥、外设连接、中断控制器和中断使⽤情况、GPIO控制器和GPIO使⽤情况、Clock控制器和Clock使⽤情况。

另外,设备树对于可热插拔的热备不进⾏具体描述,它只描述⽤于控制该热插拔设备的控制器。

设备树的主要优势:对于同⼀SOC的不同主板,只需更换设备树⽂件.dtb即可实现不同主板的⽆差异⽀持,⽽⽆需更换内核⽂件。

注:要使得3.x之后的内核⽀持使⽤设备树,除了内核编译时需要打开相对应的选项外,bootloader也需要⽀持将设备树的数据结构传给内核。

2. 设备树的组成和使⽤设备树包含DTC(device tree compiler),DTS(device tree source和DTB(device tree blob)。

我眼中的Linux设备树(Devicetree)

我眼中的Linux设备树(Devicetree)

我眼中的Linux设备树(Devicetree)转自 /Linux/2016-01/127337.htm概述设备树(Device tree)是一套用来描述硬件属相的规则。

ARM Linux采用设备树机制源于2011年3月份Linux创始人Linus Torvalds 发的一封邮件,在这封邮件中他提倡ARM平台应该参考其他平台如PowerPC的设备树机制描述硬件。

因为在此之前,ARM平台还是采用旧的机制,在kernel/arch/arm/plat-xxx目录和kernel/arch/arm/mach-xxx目录下用代码描述硬件,如注册platform设备,声明设备的resource等。

因为这些代码都是用来描述芯片平台及板级差异的,所以对于内核来讲都是垃圾代码。

因为嵌入式平台中很多公司的芯片采用的都是ARM架构,随着Android的成功,这些代码越来越多。

据说常见的平台如s3c2410板级目录下边的代码有数万行,难怪Linux Torvalds会说“this whole ARM thing is a fucking pain in the ass”。

内核中关于设备树的文档位于kernel/Documentation/devicetree/目录。

设备树是组织定义的一套规范,规范文档可以在官网上找到,目前最新的版本是/documentation/epapr-version-1-1/。

内核中设备树相关的函数都是以of开头的,我推测原因是设备树机制是源于IEEE 1275 Open Firmware standard规范的,相关的代码都是继承下来的。

如果想快速了解下设备树怎么用,可以参考 /Device_Tree_Usage。

设备树是从软件使用的角度描述硬件的,不是从硬件设计的角度描述的。

我们在写设备树时没有必要按照硬件逻辑生搬硬套,也不要指望通过阅读设备树弄清楚硬件是如何设计的。

对于软件可以自动识别的硬件,如USB设备,PCI设备,也是没有必要通过设备树描述的。

linux 设备树 usb相关解读

linux 设备树 usb相关解读

linux 设备树usb相关解读摘要:1.Linux设备树概述B设备在Linux系统中的识别与使用3.Linux系统中的USB设备驱动程序4.详细解析i2c-adapter和设备树的关联5.Linux系统中的USB设备管理正文:一、Linux设备树概述Linux设备树(Device Tree,简称DTS)是一种描述硬件资源的数据结构,它起源于Open Firmware项目。

在Linux系统中,设备树提供了一种标准的方式来描述硬件设备及其属性,使得操作系统能够正确地识别和管理硬件设备。

二、USB设备在Linux系统中的识别与使用在Linux系统中,USB设备的识别和使用主要依赖于设备树。

当USB设备插入电脑时,Linux系统会根据设备树中的信息来识别设备类型、驱动程序等信息。

此外,Linux系统还提供了umount、df等命令来查看和操作USB设备。

三、Linux系统中的USB设备驱动程序在Linux系统中,USB设备驱动程序负责管理和操作USB设备。

驱动程序根据设备树中的信息来初始化设备,进行数据传输和控制。

对于不同的USB设备,Linux系统提供了相应的驱动程序,如i2c-adapter。

四、详细解析i2c-adapter和设备树的关联i2c-adapter是Linux系统中一种常用的USB设备驱动程序。

在设备树中,i2c-adapter关联到i2c总线,负责管理i2c设备。

i2c-adapter的probe 部分会在/dev目录下创建文件,用于与设备进行通信。

五、Linux系统中的USB设备管理Linux系统提供了丰富的命令和工具来管理USB设备,如usb-util、lsusb 等。

用户可以通过这些命令查看USB设备的状态、信息等。

此外,Linux系统还支持通过设备树动态加载和卸载USB设备驱动程序,使得USB设备的管理更加灵活。

总结:Linux系统中的USB设备管理依赖于设备树和驱动程序。

linux设备树底层实现原理

linux设备树底层实现原理

linux设备树底层实现原理Linux设备树是一种用于描述硬件设备的数据结构,它的底层实现原理是如何呢?本文将详细介绍Linux设备树的底层实现原理。

一、什么是Linux设备树在传统的嵌入式系统中,硬件设备的信息通常是写死在内核源代码中的,这样一来,开发人员需要手动修改内核源代码以适配不同的硬件平台,这种方式非常不灵活且容易出错。

为了解决这个问题,Linux引入了设备树的概念。

Linux设备树是一种以树状结构组织的数据格式,用于描述硬件设备的层次结构和属性。

它将硬件设备的信息从内核源代码中分离出来,使得内核可以根据设备树的描述自动识别和驱动硬件设备,从而实现了硬件与操作系统之间的解耦。

二、设备树的结构设备树的结构可以分为三个层次:根节点、设备节点和属性节点。

1. 根节点根节点是设备树的最顶层,它是整个设备树的入口。

在根节点中定义了设备树的版本、厂商信息等基本属性。

2. 设备节点设备节点用于描述硬件设备,每个设备节点对应一个硬件设备。

设备节点中包含了设备的类型、地址、中断等信息,还可以包含子节点和属性节点。

3. 属性节点属性节点用于描述设备的属性,例如设备的名称、寄存器地址、中断号等。

属性节点是设备节点的直接子节点。

三、设备树的编译和加载过程设备树的编译和加载过程是Linux设备树实现的关键步骤。

1. 设备树的编译设备树是以源码的形式存在的,需要经过编译才能被内核所识别和使用。

设备树的编译过程包括语法检查、语义检查和编译生成二进制文件等步骤。

编译生成的设备树二进制文件通常存放在内核镜像的某个位置。

2. 设备树的加载设备树的加载是指将编译生成的设备树二进制文件加载到内存中,供内核使用。

设备树的加载过程通常在引导加载器中完成,引导加载器将设备树的二进制文件加载到内存中的某个固定位置,并将该位置的地址传递给内核。

3. 内核的解析和使用内核在启动过程中会解析设备树,并根据设备树的描述自动识别和驱动硬件设备。

Linux内核启动流程中设备树相关内容

Linux内核启动流程中设备树相关内容

Linux内核启动流程----设备树的识别曹忠明ARM Linux内核在Linux-3.x内核有了很大的变化,对一些新的平台的支持取消了传统的设备文件而用设备树取代,这里以FS4412设备树识别为例说明Linux是如何识别设备树的。

1、设备树文件FS4412设备树文件(exynos4412-fs4412.dts)部分内容:1 2 3 4 5 6 7 8 910111213141516171819202122232425262728293031 /** Insignal's Exynos4412 based Origen board device tree source** Copyright (c) 2012-2013 Samsung Electronics Co., Ltd.* ** Device tree source file for Insignal's Origen board which is based on* Samsung's Exynos4412 SoC.** This program is free software; you can redistribute it and/or modify* it under the terms of the GNU General Public License version 2 as* published by the Free Software Foundation.*// dts - v1 /;#include"exynos4412.dtsi"/{model = "Insignal Origen evaluation board based on Exynos4412";compatible = "insignal,origen4412", "samsung,exynos4412";memory {reg = <0x40000000 0x40000000>;};chosen {bootargs = "root=/dev/nfs nfsroot=192.168.9.120:/source/rootfs init=/linuxrc console=ttySA };firmware@0203F000 {compatible = "samsung,secure-firmware";3233343536373839404142434445464748495051525354555657585960616263646566676869707172737475 reg = <0x0203F000 0x1000>;};srom - cs1@5000000 {compatible = "simple-bus";#address-cells = <1>;#size-cells = <1>;reg = <0x5000000 0x1000000>;ranges;ethernet@5000000 {compatible = "davicom,dm9000";reg = <0x5000000 0x2 0x5000004 0x2>;interrupt - parent = <&gpx0>;interrupts = <64>;davicom, no - eeprom;mac - address = [00 0a 2d a6 55 a2];};};regulators {compatible = "simple-bus";#address-cells = <1>;#size-cells = <0>;mmc_reg:regulator@0 {compatible = "regulator-fixed";reg = <0>;regulator - name = "VMEM_VDD_2.8V";regulator - min - microvolt = <2800000>; regulator - max - microvolt = <2800000>; gpio = <&gpx1 10>;enable - active - high;};};g2d@10800000 {status = "okay";};sdhci@12530000 {7677787980818283 bus - width = <4>;pinctrl - 0 = <&sd2_clk &sd2_cmd &sd2_bus4 &sd2_cd>;pinctrl - names = "default";vmmc - supply = <&mmc_reg>;status = "okay";};......};编译设备树文件,内核顶层目录下执行如下命令可以编译设备树文件:$ make dtbs编译后生成文件为exynos4412-fs4412.dtb,dtb文件是使用大端字节序存储,显示其内容如下:$ hexdump exynos4412-fs4412.dtb内容如下:0000000 0dd0 edfe 0000 a588 0000 3800 0000 0c820000010 0000 2800 0000 1100 0000 1000 0000 00000000020 0000 9906 0000 d481 0000 0000 0000 00000000030 0000 0000 0000 0000 0000 0100 0000 00000000040 0000 0300 0000 0400 0000 0000 0000 01000000050 0000 0300 0000 0400 0000 0f00 0000 01000000060 0000 0300 0000 0400 0000 1b00 0000 01000000070 0000 0300 0000 2700 0000 2c00 6e69 69730000080 6e67 6c61 6f2c 6972 6567 346e 3134 00320000090 6173 736d 6e75 2c67 7865 6e79 736f 3434.....0008880 006e 6276 6361 2d6b 6f70 6372 0068 66760008890 6f72 746e 702d 726f 6863 7600 7973 636e00088a0 6c2d 6e65 000000088a5在uboot引导内核之前,会将设备树文件加载到内存中,以备Linux内核使用,这里就不详细说明了2、Linux内核启动Linux内核启动分几个阶段:1)Linux内核自解压2)Linux内核初始化----汇编3)Linux内核初始化----C这里从第三阶段开始说明,分析这个阶段,主要是查看函数start_kernel,在start_kernel中有几个函数这里重点分析:2.1setup_archsetup_arch(&command_line);void __init setup_arch(char **cmdline_p){conststructmachine_desc *mdesc;…mdesc = setup_machine_fdt(__atags_pointer);if (!mdesc)mdesc = setup_machine_tags(__atags__pointer, __machine_arch_type);…}setup_machine_fdt:structmachine_desc *setup_machine_fdt(unsigned int dt_phys)函数是用来识别设备树,Linux内核中是这样描述这个函数的:/** Machine setup when an dtb was passed to the kernel,dt_phys* @dt_phys: physical address of dt blob** If a dtb was passed to the kernel in r2, then use it to choose the correct machine_desc * and to setup the system*/也就是说bootloader如果将一个设备树文件加载到内存中,其通过r2寄存器将设备树的物理地址传递到Linux内核中,Linux内核来选择正确的机器且对其进行设置setup_machine_tags:这函数是在Linux内核不使用设备树的情况是使用,这里就不分析了。

Linux 设备树详解

Linux 设备树详解

1、ARM Linux社区为什么要引入设备树Linux之父Linus Torvalds闲来无事,在翻看ARM Linux代码的时候,有一天终于忍不住了。

他在2011年3月17日的ARM Linux邮件列表中说道:“This whole ARM thing is a f*cking pain in the ass”。

这句话迫使ARM Linux社区引入了设备树。

Linus Torvalds为什么会发飙呢?而ARM Linux社区的牛人为什么又乖乖地听话了?你得首先理解Linux设备驱动框架中一个非常好的设计:设备信息和驱动分离。

为了说明设备信息和驱动分离的概念,这里用一个简单的模拟代码来解释:【例-1】实现一个代码,把要使用的信息简单写死在代码中:int add() /*模拟驱动代码*/{return 3+5; /*模拟设备信息*/}优点:简单缺点:一旦加数和被加数发生变化就得改代码改进设计如下:【例-2】实现一个代码,把要使用的信息和操作代码分离开来:struct dev{int id;int x;int y;}; /*模拟设备信息结构*/strcut drv{int id;int (*add)(struct dev *info);}; /*模拟驱动结构*/int add(struct dev *info) /*模拟驱动代码*/{return info->x + info->y; /*模拟设备信息-通过参数传递进来*/}struct drv drv = {.id = 1,.add = add,};/*模拟设备信息*/struct dev dev = {.id = 1,.x = 3,.y = 5,};/*模拟总线初始化匹配设备信息和驱动代码*/int bus(){if(dev.id == drv.id){return drv.add(&dev);}...}优点:不管加数和被加数怎么变化,不需要修改代码,仅需要修改信息缺点:结构比较复杂那这个设备信息和驱动分离的设计跟驱动有什么关系呢?熟悉硬件编程的同学都知道,硬件一般的构成可以使用下图简单表述:操作外设的驱动代码逻辑,只要硬件是一样的,就不会变化。

Linux总线、设备、驱动模型与设备树

Linux总线、设备、驱动模型与设备树

Linux总线、设备、驱动模型与设备树
1.总线、设备、驱动模型
本着⾼内聚、低耦合的原则,Linux 把设备驱动模型分为了总线、设备和驱动三个实体,这三个实体在内核⾥的职责分别如下:
设备和驱动向总线进⾏注册,总线负责把设备和对应的驱动绑定起来。

驱动通过总线 API 接⼝platform_get_resource()取得板级设备信息,这样驱动和设备之间就实现了⾼内聚、低耦合的设计,
⽆论设备怎么换,驱动都可以岿然不动。

代码架构如下图所⽰:
2、设备树
引⼊设备树之前,关于硬件设备的描述信息⼀般放在⼀个个类似 arch/xxx/mach-xxx/board-xxx.c 的⽂件中,
这些代码中除了描述的设备信息不同,其代码逻辑都是⼀样的。

我们有理由,把这些设备端的信息,⽤⼀个⾮ C 的脚本语⾔来描述,这个脚本⽂件,就是 Device Tree(设备树)。

设备树是⼀种 dts ⽂件,它⽤简单的语法描述每个板⼦上的所有设备,以及这些设备的连接信息。

设备树⽂件存储在⽬录 arch/xxx/boot/dts 中,每⼀个 board 对应⼀个 dts ⽂件。

引⼊设备树之后,⼤量重复的 C 代码(arch/xxx/mach-xxx/board-xxx.c)被去除——驱动的归于驱动 C 代码,设备的归于设备树脚本⽂件。

arch/arm/mach-xxx/board-a.c 这样的⽂件永远地进⼊了历史的故纸堆,换个板⼦,只要换个 Device Tree ⽂件就好。

代码架构如下图所⽰:。

Linux设备树语法详解【转】

Linux设备树语法详解【转】

Linux设备树语法详解【转】转⾃:概念Linux内核从3.x开始引⼊设备树的概念,⽤于实现驱动代码与设备信息相分离。

在设备树出现以前,所有关于设备的具体信息都要写在驱动⾥,⼀旦外围设备变化,驱动代码就要重写。

引⼊了设备树之后,驱动代码只负责处理驱动的逻辑,⽽关于设备的具体信息存放到设备树⽂件中,这样,如果只是硬件接⼝信息的变化⽽没有驱动逻辑的变化,驱动开发者只需要修改设备树⽂件信息,不需要改写驱动代码。

⽐如在ARM Linux内,⼀个.dts(device tree source)⽂件对应⼀个ARM的machine,⼀般放置在内核的"arch/arm/boot/dts/"⽬录内,⽐如exynos4412参考板的板级设备树⽂件就是"arch/arm/boot/dts/exynos4412-origen.dts"。

这个⽂件可以通过$make dtbs命令编译成⼆进制的.dtb ⽂件供内核驱动使⽤。

基于同样的软件分层设计的思想,由于⼀个SoC可能对应多个machine,如果每个machine的设备树都写成⼀个完全独⽴的.dts⽂件,那么势必相当⼀些.dts⽂件有重复的部分,为了解决这个问题,Linux设备树⽬录把⼀个SoC公⽤的部分或者多个machine共同的部分提炼为相应的.dtsi⽂件。

这样每个.dts就只有⾃⼰差异的部分,公有的部分只需要"include"相应的.dtsi⽂件, 这样就是整个设备树的管理更加有序。

我这⾥⽤`Linux4.8.5源码⾃带的dm9000⽹卡为例来分析设备树的使⽤和移植。

这个⽹卡的设备树节点信息在"Documentation/devicetree/bindings/net/davicom-dm9000.txt"有详细说明,其⽹卡驱动源码是"drivers/net/ethernet/davicom/dm9000.c"。

linux 设备树 usb相关解读

linux 设备树 usb相关解读

linux 设备树usb相关解读摘要:1.设备树的概念及作用2.Linux 内核对USB 设备的识别和加载过程B 设备在Linux 系统中的识别和加载过程4.i2c-adapter 与设备树的关系5.结语正文:一、设备树的概念及作用设备树是Linux 系统中一种描述硬件设备信息和驱动的机制。

它以树状结构组织设备,并提供了统一的接口,方便内核和用户空间进行设备操作。

在设备树中,每个节点表示一个硬件设备或驱动,节点下的属性用于描述设备的具体信息,如设备类型、驱动参数等。

通过设备树,Linux 内核可以自动识别并加载对应的设备驱动,从而使设备能够正常工作。

二、Linux 内核对USB 设备的识别和加载过程1.当我们插入一个USB 设备时,USB 控制器会检测到电压变化并发出一个中断信号。

2.这个中断信号被送到处理器上的USB 控制器中断线上,告诉Linux 内核有新的USB 设备插入。

3.当内核接收到USB 控制器发出的中断信号时,它会调用USB 子系统中的模块,该模块负责检测新的USB 设备并加载相应的驱动程序。

4.模块首先会检测设备的描述符,这个描述符包括设备的厂商ID、产品ID、类别码等信息。

5.如果已经存在一个匹配的驱动程序,那么模块就会加载这个驱动程序。

如果没有匹配的驱动程序,则会尝试加载一个通用的驱动程序,这个驱动程序能够支持大多数USB 设备。

6.一旦正确的驱动程序被加载,它会向USB 子系统注册并告诉它自己可以处理哪些设备。

三、USB 设备在Linux 系统中的识别和加载过程在Linux 系统中,USB 设备的识别和加载过程主要分为以下几个步骤:1.插入USB 设备后,USB 控制器发送中断信号,通知Linux 内核有新设备接入。

2.Linux 内核调用USB 子系统模块,开始检测新设备并加载相应的驱动程序。

B 模块通过查询设备描述符,获取设备的厂商ID、产品ID 和类别码等信息。

4.根据设备信息,内核会加载对应的设备驱动程序。

Linux内核驱动基础(5)设备树简介

Linux内核驱动基础(5)设备树简介

一arm-linux内核设备树来源在过去的arm-linux内核源码树中arch/arm/plat-xxx和arch/arm/mach-xxx 等目录下边充斥着大量的垃圾代码,相当多数的代码只是在描述板级细节信息,而这些板级细节信息对于内核来说都是垃圾代码,比如板上的platform_device、resource、i2c_board_info、spi_board_info 以及各种硬件的platform_data.为了改变这种局面,ARM 社区开始使用PowerPC 等其他体系架构下已经使用的Flattened Device Tree(FDT). Device Tree 是一种描述硬件的数据结构,它起源于OpenFirmware(OF). 采用Device Tree后,许多板级细节信息可以直接通过Device Tree传递给linux内核,而不需要在arch/arm/plat-xxx和arch/arm/mach-xxx中进行大量的冗余编码,内核启动时会展开Device Tree并创建和注册相关的设备(比如platform_device,i2c_client,spi_device),同时驱动程序也会以新的方式和Devie Tree中定义的设备结点进行匹配. Device Tree的主要优势:对于相同SOC的不同板卡,只需更换设备树文件.dtb即可实现不同板卡的无差异支持二arm-linux内核设备树简介Device Tree由一系列被命名的结点(node)和属性(property)组成,而结点本身可包含子结点,所谓属性其实就是成对出现的name和value. 在Device Tree中可描述的细节信息包括system runtime参数(主要设置bootargs)cpu的数量和类别memory基地址和大小(设置DDR内存)总线控制器和桥总线外设内核基础设施使用情况,比如:中断控制器和中断使用情况DMA控制器和DMA使用情况GPIO控制器和GPIO使用情况CLOCK控制器和ClOCK使用情况PINCTRL控制器和PINCTRL使用情况从上述信息可知Device Tree并不能描述所有硬件信息,一般可以动态识别的设备(如usb设备,pci设备)是不需要设备树进行描述,它们是在设备热插拔时,由内核自动进行探测设备树包含DTC(device tree compiler)、DTS(device tree source) 和DTB(device tree binary),其对应关系如下图1所示:图1下面简单描述.dts文件.dtb文件和DTC.dts文件是一种ASCII文本文件,放置在arch/arm/boot/dts目录.由于每个SOC可能有多个不同的电路板,每个电路板都有一个 .dts文件,这些.dts文件势必会存在许多共同部分,Linux 内核为了简化,把SOC公用的部分或者多个machine 共同的部分保存到.dtsi文件中,供不同的.dts文件引用. 例如vexpress-v2f.dts引用了vexpress-v2.dtsi,此时vexpress-v2f.dts中会有如下行:#include "vexpress-v2.dtsi",告诉编译器vexpress-v2f.dts需要引用vexpress-v2.dtsi. 类似于C语言的头文件,.dtsi 也可以include 其他的.dtsi,例如几乎所有ARM SOC的.dtsi 都引用了skeleton.dtsiDTC为编译工具,它可以将.dts文件编译成.dtb文件,DTC的源码位于scripts/dtc目录,在内核arch/arm/boot/dts/Makefile 中,描述了某个SOC 被选中后,哪些.dtb文件会被编译出来,例如与VEXPRESS 对应的.dtb 包括:dtb-$(CONFIG_ARCH_VEXPRESS) += vexpress-v2o.dtb \vexpress-v2p.dtb \vexpress-v2q.dtb \vexpress-v2f.dtb从上述Makefile可知,若选择CONFIG_ARCH_VEXPRESS,上述.dtb 文件,都会从对应的.dts文件编译生成bootloader在引导内核时,会预先读取.dtb文件到内存,进而由内核解析三arm-linux内核设备树语法下面以最简单的machine 为例来看如何编写.dts 文件,假设此machine 的配置如下:双核ARM Cortex-A9处理器2 个串口(分别位于0x101F1000和0x101F2000)GPIO 控制器(位于0x101F3000)SPI 控制器(位于0x101F4000)I2C 控制器(位于0x101F5000)I2C 控制器连接了maxim DS1338 实时钟(i2c地址为0x58)中断控制器(位于0x10100000)图2上图2即一个简单的设备树,下面着重分析相关概念compatible属性.dts文件的每个设备结点,都有一个compatible属性,该属性用来决定device_driver和device的相互匹配. compatible是一个字符串列表,列表的第一个字符串表征了结点所代表的确切设备,该字符串的组织形式为:“<制造商>,<型号>”,其后的字符串则表示与它兼容的设备. 如上图2所示串口设备的compatible属性为:“ti,omap4-uart”,表示该设备节点是omap4芯片的串口设备再比如飞思卡尔mpc8349芯片拥有一个兼容美国国家半导体ns16550的串口设备,那么该串口设备的compatible属性应该是:compatible = “fsl,mpc8349-uart”,“nsl16550”,其中mpc8349-uart指定了确切的设备,而nsl16550则说明与美国国家半导体的nsl16550 UART保持了寄存器兼容注意:nsl16550没有制造商前缀,这是历史原因造成的,这种做法可以使现有驱动程序能够兼容新的设备,并仍然能够表征确切的设备. 记住以后compatible属性都应该使用这种形式:“<制造商>,<型号>”root结点compatible = “ti,omap4430”它定义了系统名称,linux内核通过root结点compatible属性来匹配machine typecpu结点compatible = “arm,cortex-a9”它为machine指定了确切的cpu型号从上可知,不论是root结点还是普通设备结点,它们都存在compatible 属性,而且组织形式都是:“<制造商>,<型号>”结点名称每个结点必须有“name@0x20000000”形式的名称,其中name是一个不超过31位的ASCII字符串,0x20000000是设备地址,用来访问设备的主地址. 结点名称应该描述设备类型而不是特定的型号,比如3com公司的ethernet适配器对应的结点名称应该是ethernet而不是3com509. 同级结点名称必须是唯一的,当然设备地址不同,多个结点也可以使用相同的名称,比如serial@0x101f1000和serial@0x101f2000,设备地址是用来访问设备的主地址,并且该地址也在结点的reg属性中列出.label用来唯一标识一个结点,定义label使得引用结点变得简单,只要以&label的形式就可以访问该结点,而不需要使用全路径的形式访问该结点.图3上图5定义了两个label,如下所示标签mmc0表示结点/mmc@50001000标签mmc1表示结点/mmc@50002000通过&mmc0访问结点/mmc@50001000,将其status属性置为disable,使得/mmc@50001000处于disable状态通过&mmc1访问结点/mmc@50002000,将其status属性置为okay,使得/mmc@50002000处于enable状态.注意:label习惯以<设备类型><index>方式进行命名设备树习惯以property = <&label>方式使用label#address-cells和#size-cells#address-cells 和#size-cells 用来决定子结点reg属性的address字段和length字段的长度. 上图2中root结点的#address-cells = <1>和#size-cells = <1> 决定了serial、gpio、spi、i2c等子结点的address字段和length字段分别为1.cpus结点的#address = <1>和#size-cells = <0> 决定了2个cpu子结点的address为1,而length为空,于是形成了2个cpu的reg=<0>和reg=<1>reg属性reg属性的组织形式为:reg = <address1 length1 [address2 length2] [address3 length3] …>每一组address和length决定了设备使用的一个地址范围,某些设备可能存在两段寄存器空间,比如:mmc@0x101f8000 {compatible = “ti,omap-hsmmc”;reg = <0x101f6000 0x10000x101f8000 0x1000>;interrupts = <7 1>;};该mmc总线就有两个寄存器空间:0x101f6000 ~ 0x101f7000和0x101f8000 ~ 0x101f90003.1 关于skeleton.dtsiskeleton.dtsi是所有ARM SOC公用的.dtsi文件,它定义了各ARM vendor共用的一些硬件信息,详细内容如下图4所示图4/表示整个设备树root结点#address-cells = <1>表示root结点的子结点中,reg属性存在一个address值#size-cells = <1>表示root结点的子结点中,reg属性存在一个size值由上可知父结点#address-cells值和#size-cells值决定了子结点的address和size的长度chosen {} 该结点主要描述系统runtime parameter(但不会描述任何硬件结点信息),比如原先通过uboot传递的bootargs参数,现在可以通过chosen结点来传递. 需要注意的是,如果存在chosen结点,其父结点必须是根结点.下图5即chosen结点实例信息图5aliases {}当板卡存在多个同类设备时,需要使用aliases{} 结点来进行编号,aliases{} 向内核提供识别符号,使得驱动程序可以获得硬件设备的id 编号,进而指定其生成的设备文件id编号(ttyO0还是ttyO1).图6上图6:2个uart串口,compatible属性相同,如果没有定义aliases{},驱动程序探测时,无法知道当前设备id(0还是1). 通过定义aliases{},驱动程序可以调用of_alias_get_id() 来获得对应设备的id编号,进而指定其生成的设备文件为ttyO0,ttyO1图7上图7通过aliases{} 告诉内核mcp_rtc为rtc0,tps659038_rtc位rtc1.memory {}顾名思义为内存结点,该结点中device_type必须是memory,一般而言.dts文件不对内存结点进行描述,而是通过bootargs = “mem=512M@0x80000000 console=ttyO0”方式传递给内核. 下图8即memory结点实例信息图83.2 注意事项1 在.dtb文件中,有且仅有一个root结点,因为编译器DTC在编译.dts和.dtsi 文件时,会对结点进行合并,使得最终生成的.dtb文件只有一个root 结点2 除了root结点外,每个结点有且仅有一个父结点3 某些设备结点的属性,不仅仅需要添加设备本身的属性,还需要添加内核基础设施的使用情况(比如interrupt、gpio、dma、clock、regulator、pinctrl)以及子结点信息.4 添加设备结点的属性,一般在公用的.dtsi文件中,添加不变属性(比如reg属性,中断号,DMA通道),在具体板卡的.dts文件中添加可变属性(比如GPIO使用情况,引脚复用情况),以及子结点信息. 这样使得.dtsi文件和.dts文件解耦。

你知道Linux设备树是怎样使用的?

你知道Linux设备树是怎样使用的?

你知道Linux设备树是怎样使用的?
本文通过为一个新machine写一个设备树来介绍设备树相关的概念,以及如何来描述一个machine。

关于设备树的技术细节描述,需要参考ePAPR文档,ePAPR文档中包含了大量的基础语法之外的细节,如果你需要了解更多本文之外的设备树细节,请参考ePAPR文档。

基本数据格式
设备树是一个由节点及属性组成的简单树结构。

属性是基于key-value对的,节点则可以包含子节点以及属性。

如,下面这个树就是一个典型结构:
/ {
node1 {
a-string-property = "A string";
a-string-list-property = "first string", "second string";
a-byte-data-property = [0x01 0x23 0x34 0x56];
child-node1 {
first-child-property;
second-child-property = ;
a-string-property = "Hello, world";
};
child-node2 {
};
};
node2 {
an-empty-property;
a-cell-property = ; /* each number (cell) is a uint32 */
child-node1 {
};
};
};
这颗树显然没什么实际用途,因为它没有描述任何有意义的内容,但是,我们通过它可以。

LINUX技术资料之设备树

LINUX技术资料之设备树

引入设备树之后: 1) 内核不再包含对硬件的描述,它以二进制的形式单独存储在另外的位置:DTB(the device tree blob) 2) bootloader 需要加载两个二进制文件:内核镜像和 DTB
内核镜像仍然是 uImage 或者 zImage DTB 文件:在 arch/arm/boot/dts 中,每一个 board 对应一个 dts 文件 3) bootloader 通过 r2 寄存器来传递 DTB 地址,通过修改 DTB 可以修改内存信息,kernel command line, 以及潜在的其它信息; 4) 不再有 machine type 5) U-Boot 的内核启动命令:bootm <kernel img addr> - <dtb addr> 6) Barebox 变量:bootm.image, bootm.oftree
LINUX 技术资料之设备树
Organized by Tanshi
LINUX 技术资料之设备树
1 设备树的概念
在过去的 ARM Linux 中,arch/arm/plat-xxx 和 arch/arm/mach-xxx 中充斥着大量的垃圾代码只是在描 述板级细节。而这个些板级细节对于内核来讲,不过是垃圾,如板上的 platform 设备、resource、 i2c_board_info、spi_board_info 以及各种硬件的 platform_data。
在 linux2.6 中,ARM 架构的板级硬件细节过多地被硬编码在 arch/arm/plat-xxx 和 arch/arm/mach-xxx 中,采用设备树后,许多硬件的细节可以直接通过它传递给 Linux,而不再需要在内核中进行大量的冗 余编码。

Linux设备树实例分析

Linux设备树实例分析

我们可以从LED程序中榨取很多知识:基本的驱动框架、驱动的简单分层、驱动的分层+分离思想、总线设备驱动模型、设备树等。

这大多都是结合韦老师的教程学的。

这篇笔记结合LEDdemo(基于设备树)来学习、分析:下面是LED程序的几个层次结构图:注意:层与层之间的箭头指向是相对的,从哪指向哪看你怎么理解。

比如有两个函数:函数A和函数B,我们可以说函数A调用函数B,也可以说函数B 被函数A调用。

本篇基于第⑤个图来分析。

我们先来体验一下使用设备树描述引脚信息的方式来点灯。

修改内核目录Linux-4.9.88/arch/arm/boot/dts下的100ask_imx6ull-14x14.dts设备树文件。

把出厂带的设备树文件的led相关节点给屏蔽掉,然后添加如下节点信息至根节点:#define GROUP_PIN(g,p) ((g<<16) | (p))100ask_led@0 {compatible = "100as,leddrv";pin = <GROUP_PIN(5, 3)>;};修改后的设备树文件内容如:在内核根目录下使用如下命令编译设备树源文件:make dtbs V=1然后把设备树文件与可加载的led驱动模块、led应用程序上传到板子里:上传成功的文件如下:运行测试:实验过程分析这个实验的led驱动同样依赖的是总线设备驱动模型。

描述设备有两种方法:一种是直接用platform_device结构体来指定,另一种是用设备树来指定。

在本次的实验中我们就是用设备树来描述设备。

之前我们用platform_device结构体来指定设备信息时,platform_driver 是直接从platform_device结构体里拿资源的,如:现在我们用设备树来指定设备信息时,platform_driver是如何获取相关资源的呢?大致过程如下:这里我们还需要注意的一点是:并不是所有的设备树节点都可以转换为platform_device。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

L i n u x设备树实例分析-CAL-FENGHAI-(2020YEAR-YICAI)_JINGBIAN我们可以从LED程序中榨取很多知识:基本的驱动框架、驱动的简单分层、驱动的分层+分离思想、总线设备驱动模型、设备树等。

这大多都是结合韦老师的教程学的。

这篇笔记结合LEDdemo(基于设备树)来学习、分析:下面是LED程序的几个层次结构图:注意:层与层之间的箭头指向是相对的,从哪指向哪看你怎么理解。

比如有两个函数:函数A和函数B,我们可以说函数A调用函数B,也可以说函数B 被函数A调用。

本篇基于第⑤个图来分析。

我们先来体验一下使用设备树描述引脚信息的方式来点灯。

修改内核目录下的设备树文件。

把出厂带的设备树文件的led相关节点给屏蔽掉,然后添加如下节点信息至根节点:#define GROUP_PIN(g,p) ((g<<16) | (p))100ask_led@0 {compatible = "100as,leddrv";pin = <GROUP_PIN(5, 3)>;};修改后的设备树文件内容如:在内核根目录下使用如下命令编译设备树源文件:make dtbs V=1然后把设备树文件与可加载的led驱动模块、led应用程序上传到板子里:上传成功的文件如下:运行测试:实验过程分析这个实验的led驱动同样依赖的是总线设备驱动模型。

描述设备有两种方法:一种是直接用platform_device结构体来指定,另一种是用设备树来指定。

在本次的实验中我们就是用设备树来描述设备。

之前我们用platform_device结构体来指定设备信息时,platform_driver 是直接从platform_device结构体里拿资源的,如:现在我们用设备树来指定设备信息时,platform_driver是如何获取相关资源的呢大致过程如下:这里我们还需要注意的一点是:并不是所有的设备树节点都可以转换为platform_device。

下面看看几条规则:根节点下含有 compatile 属性的子节点能转换为platform_device;含有特定 compatile 属性(它的值是 "simple-bus","simplemfd","isa","arm,amba-bus" 四者之一)的节点的子节点能转换为platform_device;I2C、 SPI 总线节点下的子节点不不不能转换为platform_device,这些总线下的子节点,应该交给对应的总线驱动程序来处理。

下面看一个例子:接下来,我们简单来看一下platform_device与platform_driver匹配的函数:这里,我们来看第二种匹配方式(使用设备树时的匹配方式)。

下面看看具体如何匹配:其中过程①优先匹配,其次是过程②,最后是过程③。

但是,实际上现在主要使用的是过程①的匹配,即匹配compatible属性。

过程②与过程③已经过时了,Linux内核不推荐使用这两种匹配方法。

在本次实验中,我们的匹配示意图如下:实验代码1、应用程序:int main(int argc, char **argv){int fd;char status;/* 1. 判断参数*/if (argc != 3){printf("Usage: %s <dev> <on | off>\n", argv[0]);return -1;}/* 2. 打开文件*/fd = open(argv[1], O_RDWR);if (fd == -1){printf("can not open file %s\n", argv[1]);return -1;}/* 3. 写文件*/if (0 == strcmp(argv[2], "on")){status = 1;write(fd, &status, 1);}else{status = 0;write(fd, &status, 1);}close(fd);return 0;}运行测试命令:./ledtest /dev/100ask_led0 on./ledtest /dev/100ask_led0 offint main(int argc, char **argv)形式的main函数相关笔记:main()函数有哪几种形式。

2、驱动层这一层主要是放一些通用的驱动操作函数,核心代码如:驱动程序入口函数:open、write函数:其它代码:其中led的操作结构体如下:3、硬件层:这一层主要是一些寄存器相关的操作,及platform_driver相关。

与上一个实验代码不同的部分就是这个文件。

(1)驱动初始化函数:(2)probe函数:当设备树的compatible属性与platform_driver中的设备匹配表中的compatible成员互相匹配时会执行此函数获取设备信息。

这里的pin属性与compatible属性(标准属性)类别不同,pin属性是个自定义属性。

我们可以使用of_property_read_u32函数来获取这些自定义属性的内容。

这些函数大多在文件include/linux/ 中可以找到:(3)led寄存器操作相关的代码:/* 寄存器物理地址*/#define CCM_CCGR1_BASE (0X020C406C)#define SW_MUX_GPIO5_IO03_BASE (0X02290014)#define GPIO5_DR_BASE (0X020AC000)#define GPIO5_GDIR_BASE (0X020AC004)/* 映射后的寄存器虚拟地址指针*/static void __iomem *CCM_CCGR1;static void __iomem *SW_MUX_GPIO5_IO03;static void __iomem *GPIO5_DR;static void __iomem *GPIO5_GDIR;/* 初始化LED, which-哪个LED */static int board_demo_led_init (int which){int group, pin;unsigned int val;group = GROUP(g_ledpins[which]);pin = PIN(g_ledpins[which]);printk("init gpio: group %d, pin %d\n", group, pin);/* 100ask_IMX6uLL_Board LED:GPIO5_3 */if ((5 == group) && (3 == pin)){/* 相关寄存器物理地址与虚拟地址之间的映射*//* 1、地址映射:时钟寄存器*/CCM_CCGR1 = ioremap(CCM_CCGR1_BASE, 4);/* 2、地址映射:模式寄存器*/SW_MUX_GPIO5_IO03 = ioremap(SW_MUX_GPIO5_IO03_BASE, 4);/* 3、地址映射:数据寄存器*/GPIO5_DR = ioremap(GPIO5_DR_BASE, 4);/* 地址映射:方向寄存器*/GPIO5_GDIR = ioremap(GPIO5_GDIR_BASE, 4);/* 使能GPIO5时钟*/val = readl(CCM_CCGR1); /* 读出当前CCM_CCGR1配置值*/ val &= ~(3 << 30); /* 清除以前的设置*/val |= (3 << 30); /* 设置新值*/writel(val, CCM_CCGR1);/* 设置GPIO5_IO03的为IO模式*/writel(5, SW_MUX_GPIO5_IO03);/* 设置GPIO5_IO03方向为输出*/val = readl(GPIO5_GDIR);val &= ~(1 << 3);val |= (1 << 3);writel(val, GPIO5_GDIR);}else{printk("This is not 100ask_IMX6ULL_Board!\n");}return 0;}/* 控制LED, which-哪个LED, status:1-亮,0-灭*/static int board_demo_led_ctl (int which, char status) {int group, pin;unsigned int val;group = GROUP(g_ledpins[which]);pin = PIN(g_ledpins[which]);printk("init gpio: group %d, pin %d\n", group, pin);/* 100ask_IMX6uLL_Board LED:GPIO5_3 */if ((5 == group) && (3 == pin)){/* 点灯*/if (1 == status){printk("<<<<<<<<led on>>>>>>>>>>\n");val = readl(GPIO5_DR);val &= ~(1 << 3);writel(val, GPIO5_DR);}/* 灭灯*/else if (0 == status){printk("<<<<<<<<led off>>>>>>>>>>\n");val = readl(GPIO5_DR);val|= (1 << 3);writel(val, GPIO5_DR);}else{}}else{printk("This is not 100ask_IMX6ULL_Board!\n"); }return 0;}4、Makefile文件运行测试这在文章开头的体验设备树一节中也有演示测试结果:同时,在目录/sys/firmware/devicetree/base下,我们可以查看设备树节点:可以看到,我们创建的设备树节点100ask_led@0也在该目录下。

相关文档
最新文档