0%

设备树

设备树

这个不是linus设计的,是linus借鉴别人的。这个是Open Firmware的设计,常见于powerPC等架构中,linus觉得很好,就把他引进到了linux中了,使用它的目的,也是为了统一管理和分配硬件,减少代码量。所以,遇到哪个of开头的文件和api,因为它来自Open Firmware哦。

使用设备树的时候,先看看uboot是否支持设备树,不支持就找打uboot的一些头文件增加#define CONFIG_OF_LIBFDT /* Device Tress support */试试

再看看菜单中是否开启了设备树功能,在cpu.c中引用了头文件 。比如kernel/arch/arm/mach-s5p6818/cpu.c。

#include <linux/of.h>

#include <linux/of_address.h>

#include <linux/of_fdt.h>

#include <linux/of_platform.h>

在cpu_init_machine添加“加载设备树函数of_platform_populate”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const struct of_device_id of_default_bus_match_table[] = {
{ .compatible = "simple-bus", },
#ifdef CONFIG_ARM_AMBA
{ .compatible = "arm,amba-bus", },
#endif /* CONFIG_ARM_AMBA */
{} /* Empty terminated list */
};

static void __init cpu_init_machine(void)
{
/* set shutdown */
pm_power_off = nxp_cpu_shutdown;
arm_pm_restart = nxp_cpu_reset;
/*
* register platform device
*/
nxp_cpu_devs_register();
nxp_board_devs_register();

//加载设备树
of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
}

在比较新的linux内核中,设备树dts已经取代了传统的machine board device description,dts

在内核中以各种device node的形式存在,而这些device node对于大部分的内核驱动模块

platform_driver来说,最终需要有对应的platform device来与他匹配才可以完成一次device和

driver的probe过程。

所有有必要将dts中需要加载为device的device node转为platform device,而这个过程是交给

of_platform_populate来完成的(dts相关的device node tree是在main.c中的start_kernel-

>setup_arch->unflatten_device_tree来加载dtb并解析)。

1
2
3
4
5
6
7
8
9
10
11
12
void __init unflatten_device_tree(void)

{
__unflatten_device_tree(initial_boot_params,

&allnodes,early_init_dt_alloc_memory_arch);

/* Get pointer to "/chosen" and "/aliasas" nodes for use everywhere */

of_alias_scan(early_init_dt_alloc_memory_arch);

}

MACHINE_START 改为DT_MACHINE_START,并在其中增加设备树匹配信息。

设备树文件

DTS 、DTC、DTB、

DTS是设备树源码,就是描绘了硬件的配置;如地址、大小、种类、外设配置等

DTC是编译设备树的编译工具,这个需要install下单编译器

1
2
3
4
5
6
7
8
9
10
11
#dtc 工具安装:
sudo apt-get install device-tree-compiler

#编译设备树
dtc -I dts -O dtb -o xxx.dtb xxx.dts

#编译设备树插件
dtc -I dts -O dtb -o xxx.dtbo xxx.dts

#设备树反汇编
dtc -I dtb -O dts -o xxx.dts xxx.dtb

DTB是可执行文件,经过上面的编译之后,可以被编译器识别的文件。可以加到特定位置同内核一起运行,也可以加到内核所在设备树目录中一起编译到内核之中。

设备树语法

设备树该怎么写呢?首先,一个树肯定就是有主干,这个主干,不是我们这些小卡拉米写的,所以,顶多是在芯片原厂的基础上增加一些自己的设备。

枝叶这个该怎么写呢?首先,你得让编译器和主干识别到我这个枝叶吧,这就像一个叶子,一定有叶柄才能和主干连接,而我们修改不过是叶子的形状和功能,所以,开头一定要有一个和主干一模一样的开头。这个就是叶柄了。如下面这种结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/*
* Copyright (c) 2012 Samsung Electronics Co., Ltd.
* http://www.samsung.com
** 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/;

/ {
model = "csm6818 based on Samsung s5p6818"; /*这一段就是叶柄了,有了它才能正常工作*/
compatible = "csm,s5p6818";
chosen {
compatible = "gec,chosen";
bootargs = "lcd=at070tn92 tp=gslx680-linux root=/dev/mmcblk0p2 rootfstype=ext4 rw ";
};

/*这下面此时就是叶子了,可以随意定义*/
myled{
compatible = "csm,myled"; /*这个compatible不要改,也算是开头和索引号,后面的随便改都行*/
led-num = <4>; /*这个led-num是自定义的,可以修改哦。比如led-nums*/
led-names ="led-d7","led-d8","led-d9","led-d10"; /*字符串*/
led-gpios = <141 81 72 71>; /*数据*/
default-state = "off"; /*字符串 表示状态,当然用数据也行*/
};

};

语法开始:这个就简单的说一下设备树语法,只有使用的时候再去仔细看一下需要的。

Linux设备树学习笔记(一、设备树语法规范)_reg = <0x00 0x80000000 0x00 0x60000000>;-CSDN博客

设备树5个使用方法
1.将dtb文件拷贝到根文件系统(推荐,利于调试)

/* 从根文件系统拷贝dtb文件,更方便,更利于调试 */

setenv bootcmd ext4load mmc 2:2 0x42000000 gec6818.dtb ;ext4load mmc 2:1 0x48000000

uImage ;bootm 0x48000000 - 0x42000000

saveenv

2.挂载开发板内核镜像分区,将dtb拷贝到该分区(安全性高,用户不能随意修改或删除)

mount -t ext4 /dev/mmcblk0p1 /mnt

cp gec6818.dtb /mnt

umount /mnt

/* 拷贝到内核镜像的分区 */

setenv bootcmd ext4load mmc 2:1 0x42000000 gec6818.dtb ;ext4load mmc 2:1 0x48000000

uImage ;bootm 0x48000000 - 0x42000000

saveenv

3.使用网络,配置uboot环境变量。

setenv bootcmd tftp 0x42000000 gec6818.dtb ; ext4load mmc 2:1 0x48000000 uImage ;

bootm 0x48000000 - 0x42000000

4.不使用网络,可以使用串口下载dtb文件到内存地址0x42000000。

loady 0x42000000

setenv bootcmd ext4load mmc 2:1 0x48000000 uImage ; bootm 0x48000000 - 0x42000000

saveenv

5.将dtb文件编译到boot.img,重新烧录内核。

在ubuntu中

将文件拷贝到一个/target/product/6818/boot的目录之下

./mk -k

在uboot中

setenv bootcmd ext4load mmc 2:1 0x42000000 gec6818.dtb ;ext4load mmc 2:1 0x48000000

uImage ;bootm 0x48000000 - 0x42000000

驱动使用设备树节点

驱动想要使用设备树,只能使用平台设备方法的,此时平台设备platform_driver中的dirver结构体中的of_match_table这个就要指向所满足需求的

1
2
3
4
5
6
7
8
9
10
11
12
13
static const struct of_device_id of_led_match[]={
{.compatible="gec,myled",}
}

static struct platform_driver led_plat_driver = {
.probe = myled_probe,
.remove = __devexit_p(myled_remove),
.driver = {
.name = "myled",
.onwer = THIS_MODULE,
.of_match_table = of_led_match, //指针指向
}
}

此时,这个驱动就知道,该找到compatible=”gec,myled”的要求的设备树节点,读取这个节点上面的数据(引脚号和介绍),此时驱动就知道有这个led设备了,就可以控制对应的GPIO引脚的高低电平。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
struct platform_device *pdev;//已经对应上的平台设备

/* init */

char Buff[30]={0};
//设置节点指向
struct device_node * pdevnode = pdev->dev.of_node;

//混杂设备注册

//申请gpio设备

//调用读取节点字符串函数
of_property_read_string(pdevnode,"目标",&Buff);
of_property_read_string_index(pdevnode,"目标",&Buff); //出现 "xxx","yyy","zzz"的时候
//Buff就是目标字符串了 目标可以是名字介绍,这个是设备树中自定义的

//调用读取节点数据函数
of_property_read_u8_array(pdevnode,"目标",Buff,Numsize);
//Buff就是目标数据了,Numsize就是个数
/*还有of_property_read_u16_array;of_property_read_u32_array;
of_property_read_u64_array*/

//得到了所需要的数据,就可以写对应的驱动了
//gpio操作

/* init结束 */
总结

设备树,就是省去了,对硬件的描述的复杂和多种写法,统一写法,节约空间和利于管理。

-------------下次的来访是什么时候呢[doge]-------------