1. 什么是設(shè)備樹
設(shè)備樹的本質(zhì)也是操作寄存器,只不過(guò)寄存器的相關(guān)信息放在了設(shè)備樹中,配置寄存器時(shí)需要使用OF函數(shù)從設(shè)備樹中讀取寄存器數(shù)據(jù)后再進(jìn)行配置
Linux 3.x之前是沒(méi)有設(shè)備樹的,Linux通過(guò)內(nèi)核源碼中arch/arm/mach-xxx和arch/arm/plat-xxx文件夾里的板級(jí)描述文件來(lái)描述ARM架構(gòu)中的板級(jí)信息。 隨著ARM硬件種類增多,與板子相關(guān)的設(shè)備文件也越來(lái)越多,導(dǎo)致內(nèi)核越來(lái)越大,而實(shí)際上這些硬件板級(jí)信息與內(nèi)核并無(wú)相關(guān)關(guān)系
2011年,Linus發(fā)現(xiàn)該問(wèn)題后,引入了PowerPC等架構(gòu)已經(jīng)采用的設(shè)備樹機(jī)制,將板級(jí)信息從內(nèi)核中分離開來(lái),用一個(gè)專屬的文件格式(.dts文件)來(lái)描述。 設(shè)備樹的作用就是描述硬件平臺(tái)的硬件資源,它可被bootloader傳遞到內(nèi)核,內(nèi)核可以從設(shè)備樹中獲取硬件信息。 設(shè)備樹描述硬件資源時(shí)有兩個(gè)特點(diǎn):
- 以樹狀結(jié)構(gòu)描述硬件資源
- 可以像頭文件那樣,一個(gè)設(shè)備樹文件可以引用另一個(gè)設(shè)備樹文件,實(shí)現(xiàn)代碼重用
DTS、DTSI、DTB、DTC文件的區(qū)別及定義:
DTC工具源碼在Linux內(nèi)核的scripts/dtc/Makefile文件中:
hostprogs-y:= dtc
always:= $(hostprogs-y)
dtc-objs:= dtc.o flattree.o fstree.o data.o livetree.o treesource.o srcpos.o checks.o util.o
dtc-objs+= dtc-lexer.lex.o dtc-parser.tab.o
......
由上可見DTC工具依賴于dtc.c, flattree.c, fstree.c等文件,最終編譯并鏈接出DTC這個(gè)主機(jī)文件。 若要編譯DTS文件,只需要進(jìn)入到Linux源碼根目錄下,然后執(zhí)行如下命令:
make all #編譯Linux源碼中的所有東西,包括zImage、.ko驅(qū)動(dòng)模塊以及設(shè)備樹
make dtbs #只是編譯設(shè)備樹
基于ARM架構(gòu)的SOC有很多,一種SOC又可制作出多款板子,每個(gè)板子都有對(duì)應(yīng)的DTS文件,那么如何確定編譯哪一個(gè)DTS文件呢? 以I.MX6ULL芯片的板子為例,打開arch/arm/boot/dts/Makefile,有如下內(nèi)容:
dtb-$(CONFIG_SOC_IMX6UL) += \\
imx6ul-14x14-ddr3-arm2.dtb \\
imx6ul-14x14-ddr3-arm2-emmc.dtb \\
......
dtb-$(CONFIG_SOC_IMX6ULL) += \\
imx6ull-14x14-ddr3-arm2.dtb \\
imx6ull-14x14-ddr3-arm2-adc.dtb \\
......
選中I.MX6ULL后(即CONFIG_SOC_IMX6ULL=y),所有使用該SOC的板子對(duì)應(yīng)的.dts文件都會(huì)被編譯為.dtb。 若新做一個(gè)板子,只需要新建一個(gè)對(duì)應(yīng)的.dts文件,再將對(duì)應(yīng)的.dtb文件名添加到 dtb-$(CONFIG_SOC_IMX6ULL)下,這樣在編譯設(shè)備樹時(shí)就會(huì)將對(duì)應(yīng)的.dts編譯為二進(jìn)制的.dtb文件
2. 設(shè)備樹語(yǔ)法介紹
2.1 設(shè)備樹代碼分析
關(guān)于i. MX6ULL已有的設(shè)備樹文件,大致有以下幾種
imx6ull-14x14-evk-emmc.dts文件:在/arch/arm/boot/dts/文件夾中,描述了emmc板子的usdhc信息。 該文件通過(guò)頭文件的形式包含了另一個(gè)設(shè)備樹文件
#include "imx6ull-14x14-evk.dts"
&usdhc2 {
pinctrl-names = "default", "state_100mhz", "state_200mhz";
pinctrl-0 = <&pinctrl_usdhc2_8bit>;
pinctrl-1 = <&pinctrl_usdhc2_8bit_100mhz>;
pinctrl-2 = <&pinctrl_usdhc2_8bit_200mhz>;
bus-width = <8>;
non-removable;
status = "okay";
};
imx6ull-14x14-evk.dts文件:在/arch/arm/boot/dts/文件夾中,包含了根節(jié)點(diǎn)、子節(jié)點(diǎn)及其他追加內(nèi)容
#include
#include "imx6ull.dtsi"
/ {
model = "Freescale i.MX6 ULL 14x14 EVK Board";
compatible = "fsl,imx6ull-14x14-evk", "fsl,imx6ull";
chosen {
stdout-path = &uart1;
};
memory {
reg = <0x80000000 0x20000000>;
};
reserved-memory {
#address-cells = <1>;
#size-cells = <1>;
ranges;
linux,cma {
compatible = "shared-dma-pool";
reusable;
size = <0x14000000>;
linux,cma-default;
};
};
backlight {
compatible = "pwm-backlight";
pwms = <&pwm1 0 5000000>;
brightness-levels = <0 4 8 16 32 64 128 255>;
default-brightness-level = <6>;
status = "okay";
};
pxp_v4l2 {
compatible = "fsl,imx6ul-pxp-v4l2", "fsl,imx6sx-pxp-v4l2", "fsl,imx6sl-pxp-v4l2";
status = "okay";
};
regulators {
compatible = "simple-bus";
......
};
......
};
&cpu0 {
arm-supply = <®_arm>;
soc-supply = <®_soc>;
dc-supply = <®_gpio_dvfs>;
};
&clks {
assigned-clocks = <&clks IMX6UL_CLK_PLL4_AUDIO_DIV>;
assigned-clock-rates = <786432000>;
};
......
&wdog1 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_wdog>;
fsl,wdog_b;
};
imx6ull.dtsi文件:是設(shè)備樹的頭文件,其格式與設(shè)備樹基本相同
#include
#include "imx6dl-pinfunc.h"
#include "imx6qdl.dtsi"
/ {
aliases {
i2c3 = &i2c4;
};
cpus {
#address-cells = <1>;
#size-cells = <0>;
cpu0: cpu@0 {
compatible = "arm,cortex-a9";
device_type = "cpu";
......
};
cpu@1 {
compatible = "arm,cortex-a9";
device_type = "cpu";
reg = <1>;
next-level-cache = <&L2>;
};
};
reserved-memory {
......
};
soc {
......
ocram: sram@00905000 {
compatible = "mmio-sram";
reg = <0x00905000 0x1B000>;
clocks = <&clks IMX6QDL_CLK_OCRAM>;
};
......
};
};
......
&vpu_fsl {
iramsize = <0>;
};
2.2 DTS語(yǔ)法介紹
設(shè)備樹采用樹形結(jié)構(gòu)來(lái)描述板子上的設(shè)備信息,每個(gè)設(shè)備都是一個(gè)節(jié)點(diǎn),叫做設(shè)備節(jié)點(diǎn),每個(gè)節(jié)點(diǎn)都通過(guò)一些屬性信息來(lái)描述節(jié)點(diǎn)信息,屬性就是鍵-值對(duì)
node-name@unit-address{
屬性1 = ...
屬性2 = ...
子節(jié)點(diǎn)...
}
節(jié)點(diǎn)名稱:node-name用于指定節(jié)點(diǎn)名稱,應(yīng)使用字母開頭,并能描述設(shè)備類別(根節(jié)點(diǎn)用斜杠表示)
單元地址:@unit-address用于指定單元地址,@為分隔符,后面是實(shí)際的單元地址,它的值與節(jié)點(diǎn)reg屬性的第一個(gè)地址一致,若沒(méi)有reg屬性值,則可以省略單元地址
節(jié)點(diǎn)屬性:節(jié)點(diǎn)的大括號(hào){ }中包含的內(nèi)容是節(jié)點(diǎn)屬性, 一個(gè)節(jié)點(diǎn)可以包含多個(gè)屬性信息,設(shè)備樹最主要的內(nèi)容就是編寫節(jié)點(diǎn)的屬性。 屬性包括自定義屬性和標(biāo)準(zhǔn)屬性
- model屬性:用于指定設(shè)備的制造商和型號(hào),多個(gè)字符串使用“,”分隔開
- compatible屬性:由一個(gè)或多個(gè)字符串組成,是用來(lái)查找節(jié)點(diǎn)的方法之一
- status屬性:用于指示設(shè)備的操作狀態(tài),通過(guò)status可以禁用或啟用設(shè)備
- reg屬性:描述設(shè)備資源在其父總線定義的地址空間內(nèi)的地址,通常用于表示一塊寄存器的起始地址和長(zhǎng)度
- #address-cells 和 #size-cells:這兩個(gè)屬性同時(shí)存在,前者決定了子節(jié)點(diǎn)reg屬性中地址信息所占用的字長(zhǎng),后者決定了長(zhǎng)度信息所占的字長(zhǎng)
- ranges屬性:是一個(gè)地址映射/轉(zhuǎn)換表,由子地址、父地址和地址空間長(zhǎng)度這三部分組成
- child-bus-address:子總線地址空間的物理地址, 由父節(jié)點(diǎn)的#address-cells 確定此物理地址所占用的字長(zhǎng)
- parent-bus-address:父總線地址空間的物理地址,同樣由父節(jié)點(diǎn)的#address-cells 確定此物理地址所占用的字長(zhǎng)
- length:子地址空間的長(zhǎng)度,由父節(jié)點(diǎn)的#size-cells 確定此地址長(zhǎng)度所占用的字長(zhǎng)
特殊節(jié)點(diǎn):aliases子節(jié)點(diǎn)、chosen子節(jié)點(diǎn)
//為其他節(jié)點(diǎn)起一個(gè)別名
aliases {
i2c3 = &i2c4;
};
//該節(jié)點(diǎn)位于根節(jié)點(diǎn)下,它不代表實(shí)際硬件,主要用于給內(nèi)核傳遞參數(shù)
//下面代碼表示系統(tǒng)標(biāo)準(zhǔn)輸出stdout使用串口uart1
chosen {
stdout-path = &uart1;
};
3. 設(shè)備樹OF函數(shù)
內(nèi)核提供了一系列函數(shù)用于從設(shè)備節(jié)點(diǎn)獲取節(jié)點(diǎn)中定義的屬性,這些函數(shù)以of_開頭,稱為OF函數(shù)。 在編寫設(shè)備樹版的驅(qū)動(dòng)時(shí),在進(jìn)行硬件配置方面,就是要用這些OF函數(shù),將寄存器地址等信息從設(shè)備樹文件中獲取出來(lái),然后進(jìn)行配置
3.1 查找節(jié)點(diǎn)的OF函數(shù)
設(shè)備都是以節(jié)點(diǎn)的形式掛到設(shè)備樹上的,因此要獲取這個(gè)設(shè)備的其他屬性信息,必須先獲取到這個(gè)設(shè)備的節(jié)點(diǎn)。 內(nèi)核使用device_node結(jié)構(gòu)體來(lái)描述一個(gè)節(jié)點(diǎn),此結(jié)構(gòu)體定義在文件include/linux/of.h中,定義如下:
struct device_node {
const char *name; /* 節(jié)點(diǎn)名字 */
const char *type; /* 設(shè)備類型 */
phandle phandle;
const char *full_name; /* 節(jié)點(diǎn)全名 */
struct fwnode_handle fwnode;
struct property *properties; /* 屬性 */
struct property *deadprops; /* removed 屬性 */
struct device_node *parent; /* 父節(jié)點(diǎn) */
struct device_node *child; /* 子節(jié)點(diǎn) */
struct device_node *sibling;
struct kobject kobj;
unsigned long _flags;
void *data;
#if defined(CONFIG_SPARC)
const char *path_component_name;
unsigned int unique_id;
struct of_irq_controller *irq_trans;
#endif
};
of_find_node_by_name:通過(guò)節(jié)點(diǎn)名字查找指定的節(jié)點(diǎn)
struct device_node *of_find_node_by_name(struct device_node *from, const char *name);
//from:開始查找的節(jié)點(diǎn),NULL表示從根節(jié)點(diǎn)開始查找整個(gè)設(shè)備樹
//name:要查找的節(jié)點(diǎn)名字
//返回值:找到的節(jié)點(diǎn),NULL表示查找失敗
of_find_node_by_type:通過(guò)device_type屬性查找指定的節(jié)點(diǎn)
struct device_node *of_find_node_by_type(struct device_node *from, const char *type)
//from:開始查找的節(jié)點(diǎn),NULL表示從根節(jié)點(diǎn)開始查找整個(gè)設(shè)備樹
//type:要查找的節(jié)點(diǎn)對(duì)應(yīng)的type字符串,即device_type屬性值
//返回值:找到的節(jié)點(diǎn),NULL表示查找失敗
of_find_compatible_node:根據(jù)device_type和compatible這兩個(gè)屬性查找指定的節(jié)點(diǎn)
struct device_node *of_find_compatible_node(struct device_node *from, const char *type, const char *compatible)
//from:開始查找的節(jié)點(diǎn),NULL表示從根節(jié)點(diǎn)開始查找整個(gè)設(shè)備樹
//type:要查找的節(jié)點(diǎn)對(duì)應(yīng)的device_type屬性值,為NULL時(shí)表示忽略掉該屬性
//compatible:要查找的節(jié)點(diǎn)所對(duì)應(yīng)的compatible屬性列表
//返回值:找到的節(jié)點(diǎn),NULL表示查找失敗
of_find_matching_node_and_match:通過(guò)of_device_id匹配表來(lái)查找指定的節(jié)點(diǎn)
struct device_node *of_find_matching_node_and_match(struct device_node *from,
const struct of_device_id *matches,
const struct of_device_id **match)
//from:開始查找的節(jié)點(diǎn),NULL表示從根節(jié)點(diǎn)開始查找整個(gè)設(shè)備樹
//matches:of_device_id匹配表,也就是在此匹配表里面查找節(jié)點(diǎn)
//match:找到的匹配的of_device_id
//返回值:找到的節(jié)點(diǎn),NULL表示查找失敗
of_find_node_by_path:通過(guò)路徑來(lái)查找指定的節(jié)點(diǎn)
inline struct device_node *of_find_node_by_path(const char *path)
//path:帶有全路徑的節(jié)點(diǎn)名
//返回值:找到的節(jié)點(diǎn),NULL表示查找失敗
3.2 查找父/子節(jié)點(diǎn)的OF函數(shù)
of_get_parent:用于查找父節(jié)點(diǎn)
struct device_node *of_get_parent(const struct device_node *node)
//node:要查找的父節(jié)點(diǎn)的節(jié)點(diǎn)
//返回值:找到的父節(jié)點(diǎn)
of_get_next_child:用迭代的方式查找子節(jié)點(diǎn)
struct device_node *of_get_next_child(const struct device_node *node, struct device_node *prev)
//node:父節(jié)點(diǎn)
//prev:前一個(gè)子節(jié)點(diǎn),即從哪一個(gè)子節(jié)點(diǎn)開始迭代的查找下一個(gè)子節(jié)點(diǎn),若為NULL,表示從第一個(gè)子節(jié)點(diǎn)開始
//返回值: 找到的下一個(gè)子節(jié)點(diǎn)
3.3 提取屬性值的OF函數(shù)
節(jié)點(diǎn)的屬性信息里面保存了驅(qū)動(dòng)所需要的內(nèi)容,因此對(duì)于屬性值的提取非常重要,內(nèi)核中使用結(jié)構(gòu)體property表示屬性,定義在include/linux/of.h中,內(nèi)容如下:
struct property {
char *name; /* 屬性名字 */
int length; /* 屬性長(zhǎng)度 */
void *value; /* 屬性值 */
struct property *next; /* 下一個(gè)屬性 */
unsigned long _flags;
unsigned int unique_id;
struct bin_attribute attr;
};
of_find_property:查找指定的屬性
property *of_find_property(const struct device_node *np, const char *name, int *lenp)
//np:設(shè)備節(jié)點(diǎn)
//name:屬性名字
//lenp:屬性值的字節(jié)數(shù)
//返回值:找到的屬性
of_property_count_elems_of_size:獲取屬性中元素的數(shù)量
int of_property_count_elems_of_size(const struct device_node *np, const char *propname, int elem_size)
//np:設(shè)備節(jié)點(diǎn)
//proname:需要統(tǒng)計(jì)元素?cái)?shù)量的屬性名字
//elem_size:元素長(zhǎng)度
//返回值:得到的屬性元素?cái)?shù)量
of_property_read_u32_index:從屬性中獲取指定標(biāo)號(hào)的u32類型數(shù)據(jù)值
int of_property_read_u32_index(const struct device_node *np, const char *propname, u32 index, u32 *out_value)
//np:設(shè)備節(jié)點(diǎn)
//proname:要讀取的屬性名字
//index:要讀取的值標(biāo)號(hào)
//out_value:讀取到的值
//返回值:0 讀取成功,負(fù)值,讀取失敗,-EINVAL 表示屬性不存在,
// -ENODATA 表示沒(méi)有要讀取的數(shù)據(jù),-EOVERFLOW 表示屬性值列表大小
of_property_read_u8_array:讀取屬性中u8類型的數(shù)組數(shù)據(jù)(類似的還有u16、u32 和 u64)
int of_property_read_u8_array(const struct device_node *np, const char *propname, u8 *out_values, size_t sz)
//np:設(shè)備節(jié)點(diǎn)
//proname:要讀取的屬性名字
//out_value:讀取到的數(shù)組值
//sz:要讀取的數(shù)組元素?cái)?shù)量
//返回值:0 讀取成功,負(fù)值,讀取失敗,-EINVAL 表示屬性不存在,
// -ENODATA 表示沒(méi)有要讀取的數(shù)據(jù),-EOVERFLOW 表示屬性值列表太
of_property_read_u8:讀取只有一個(gè)整形值的屬性(類似的還有u16、u32和u64)
int of_property_read_u8(const struct device_node *np, const char *propname, u8 *out_value)
//np:設(shè)備節(jié)點(diǎn)
//proname:要讀取的屬性名字
//out_value:讀取到的數(shù)組值
//返回值:0 讀取成功,負(fù)值,讀取失敗,-EINVAL 表示屬性不存在,
// -ENODATA 表示沒(méi)有要讀取的數(shù)據(jù),-EOVERFLOW 表示屬性值列表太小
of_property_read_string:讀取屬性中字符串值
int of_property_read_string(struct device_node *np, const char *propname, const char **out_string)
//np:設(shè)備節(jié)點(diǎn)
//proname:要讀取的屬性名字
//out_string:讀取到的字符串值
//返回值:0,讀取成功,負(fù)值,讀取失敗
of_n_addr_cells:獲取#address-cells 屬性值
int of_n_addr_cells(struct device_node *np)
//np:設(shè)備節(jié)點(diǎn)
//返回值:獲取到的#address-cells屬性值
of_n_size_cells:獲取#size-cells 屬性值
int of_n_size_cells(struct device_node *np)
//np:設(shè)備節(jié)點(diǎn)
//返回值:獲取到的#size-cells屬性值
3.4 其他常用OF函數(shù)
of_device_is_compatible:查看節(jié)點(diǎn)的compatible屬性是否有包含compat指定的字符串,也就是檢查設(shè)備節(jié)點(diǎn)的兼容性
int of_device_is_compatible(const struct device_node *device, const char *compat)
//device:設(shè)備節(jié)點(diǎn)
//compat:要查看的字符串
//返回值: 0,節(jié)點(diǎn)的 compatible 屬性中不包含 compat 指定的字符串;
// 正數(shù),節(jié)點(diǎn)的 compatible 屬性中包含 compat 指定的字符串
of_get_address:獲取地址相關(guān)屬性
const __be32 *of_get_address(struct device_node *dev, int index, u64 *size, unsigned int *flags)
//dev:設(shè)備節(jié)點(diǎn)
//index:要讀取的地址標(biāo)號(hào)
//size:地址長(zhǎng)度
//flags:參數(shù),比如IORESOURCE_IO、IORESOURCE_MEM等
//返回值:讀取到的地址數(shù)據(jù)首地址,表示讀取失敗
of_translate_address:將設(shè)備樹讀取到的地址轉(zhuǎn)換為物理地址
u64 of_translate_address(struct device_node *dev, const __be32 *in_addr)
//dev:設(shè)備節(jié)點(diǎn)
//in_addr:要轉(zhuǎn)換的地址
//返回值:得到的物理地址,為OF_BAD_ADDR表示轉(zhuǎn)換失敗
of_address_to_resource:將reg屬性值,轉(zhuǎn)換為resource結(jié)構(gòu)體類型
int of_address_to_resource(struct device_node *dev, int index, struct resource *r)
//dev:設(shè)備節(jié)點(diǎn)
//index:地址資源標(biāo)號(hào)
//r:得到的 resource 類型的資源值
//返回值: 0,成功;負(fù)值,失敗
of_iomap:用于直接內(nèi)存映射
void __iomem *of_iomap(struct device_node *np, int index)
//np:設(shè)備節(jié)點(diǎn)
//index:reg屬性中要完成內(nèi)存映射的段,如果reg屬性只有一段的話index就設(shè)置為0
//返回值:經(jīng)過(guò)內(nèi)存映射后的虛擬內(nèi)存首地址,NULL表示內(nèi)存映射失敗
-
ARM
+關(guān)注
關(guān)注
134文章
9164瀏覽量
368800 -
寄存器
+關(guān)注
關(guān)注
31文章
5363瀏覽量
121011 -
源碼
+關(guān)注
關(guān)注
8文章
652瀏覽量
29412 -
Linu
+關(guān)注
關(guān)注
0文章
26瀏覽量
19851 -
設(shè)備樹
+關(guān)注
關(guān)注
0文章
39瀏覽量
3149
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論