上篇文章(【i.MX6ULL】驅(qū)動(dòng)開發(fā)4--點(diǎn)亮LED(寄存器版))介紹了在驅(qū)動(dòng)程序中,直接操作寄存器了點(diǎn)亮LED。本篇,介紹另外一種點(diǎn)亮LED的方式——設(shè)備樹,該方式的本質(zhì)也是操作寄存器,只是寄存器的相關(guān)信息放在了設(shè)備樹中,配置寄存器時(shí)需要使用OF函數(shù)從設(shè)備樹中讀取處寄存器數(shù)據(jù)后再進(jìn)行配置。
1 什么是設(shè)備樹
1.1 背景介紹
Linux3.x之前是沒有設(shè)備樹的,設(shè)備樹是用來描述一個(gè)硬件平臺(tái)的板級(jí)細(xì)節(jié)。對(duì)應(yīng)ARM-Linux開發(fā),這些板級(jí)描述文件存放在linux內(nèi)核的 /arch/arm/plat-xxx和/arch/arm/mach-xxx 中。隨著ARM硬件設(shè)備的種類增多,與板子相關(guān)的設(shè)備文件也越來越多,這就導(dǎo)致Linux內(nèi)核越來越大,而實(shí)際這些ARM硬件相關(guān)的板級(jí)信息與Linux內(nèi)核并無相關(guān)關(guān)系。
2011年,Linux之父Linus Torvalds發(fā)現(xiàn)這個(gè)問題后,就通過郵件向ARM-Linux開發(fā)社區(qū)發(fā)了一封郵件,不禁的發(fā)出了一句“This whole ARM thing is a f*cking pain in the ass”。之后,ARM社區(qū)就引入了PowerPC等架構(gòu)已經(jīng)采用的設(shè)備樹(Flattened Device Tree)機(jī)制,將板級(jí)信息內(nèi)容都從Linux內(nèi)核中分離開來,用一個(gè)專屬的文件格式來描述,即現(xiàn)在的.dts文件。
1.2 設(shè)備樹介紹
設(shè)備樹的作用就是描述硬件平臺(tái)的硬件資源。它可以被bootloader傳遞到內(nèi)核,內(nèi)核可以從設(shè)備樹中獲取硬件信息。
設(shè)備樹描述硬件資源時(shí)有兩個(gè)特點(diǎn):
以樹狀結(jié)構(gòu)描述硬件資源。以系統(tǒng)總線為樹的主干,掛載到系統(tǒng)地總線的IIC控制器、SPI控制器等為樹的枝干,IIC控制器下的IIC設(shè)備資源,又可以再分IIC1和IIC2,而IIC1上又可以連接MPU6050這類的IIC器件...
可以像頭文件那樣,一個(gè)設(shè)備樹文件引用另外一個(gè)設(shè)備樹文件,實(shí)現(xiàn)代碼重用。例如多個(gè)硬件平臺(tái)都使用i.MX6ULL作為主控芯片,可以將 i.MX6ULL 芯片的硬件資源寫到一個(gè)單獨(dú)的設(shè)備樹文件中(.dtsi文件)。
1.3 DTS、DTSI、DTB、DTC
DTS ,Device Tree Source,是設(shè)備樹源碼文件
DTSI ,Device Tree Source Include,是設(shè)備樹源碼文件要用到的頭文件
DTB ,Device Tree Binary,是將DTS 編譯以后得到的二進(jìn)制文件
DTC ,Device Tree Compiler,是將.dts 編譯為.dtb需要用到的編譯工具
DTC工具源碼在Linux內(nèi)核的scripts/dtc目錄下,scripts/dtc/文件夾下Makefile的內(nèi)容為:
-
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ī)文件
2 設(shè)備樹框架與DTS語法
2.1 設(shè)備樹代碼分析
在學(xué)習(xí)設(shè)備樹時(shí),可以先看一下NXP關(guān)于i.MX6ULL已有的設(shè)備樹文件,來大致了解一下設(shè)備樹文件是什么樣子的。
2.1.1 imx6ull-14x14-evk-emmc.dts
下面是/arch/arm/boot/dts/imx6ull-14x14-evk-emmc.dts
#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";
};
該文件就這幾行,描述了emmc版本板子的usdhc信息。該文件的主要的功能是通過頭文件的形式包含了另一個(gè)imx6ull-14x14-evk.dts設(shè)備樹文件。
DTS語法:設(shè)備樹是可以使用“#include”引用其它文件(.dts、.h、.dtsi)。
2.1.2 imx6ull-14x14-evk.dts
下面是/arch/arm/boot/dts/imx6ull-14x14-evk.dts
/dts-v1/;
#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 = ;
soc-supply = ;
dc-supply = ;
};
&clks {
assigned-clocks = <&clks IMX6UL_CLK_PLL4_AUDIO_DIV>;
assigned-clock-rates = <786432000>;
};
//省略...
&wdog1 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_wdog>;
fsl,wdog_b;
};
該文件也是先包含一些頭文件,然后是一個(gè)斜杠+一些大括號(hào),后面還出現(xiàn)了&符號(hào)。
DTS語法:
/ {?}
斜杠+大括號(hào),表示根節(jié)點(diǎn),一個(gè)設(shè)備只有一個(gè)根節(jié)點(diǎn)(注:一個(gè)dts包含另一個(gè)dts,兩個(gè)文件里的根節(jié)點(diǎn),其實(shí)也是同一個(gè)根節(jié)點(diǎn))
xxx {?}
根節(jié)點(diǎn)內(nèi)部單獨(dú)的大括號(hào),表示子節(jié)點(diǎn),如reserved-memory {...}、pxp_v4l2 {...}等
&xxx {?}
根節(jié)點(diǎn)外部單獨(dú)的&符號(hào)與大括號(hào),表示節(jié)點(diǎn)的追加內(nèi)容,如&cpu0 {...}等
2.1.3 imx6ull.dtsi
#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>;
};
該文件是設(shè)備樹的頭文件,其格式與設(shè)備樹基本相同。
DTS語法:節(jié)點(diǎn)標(biāo)簽
節(jié)點(diǎn)名“cpu”前面多了個(gè)“cpu0”, 這個(gè)“cpu0”就是我們所說的節(jié)點(diǎn)標(biāo)簽。通常節(jié)點(diǎn)標(biāo)簽是節(jié)點(diǎn)名的簡(jiǎn)寫,它的作用是當(dāng)其它位置需要引用時(shí)可以使用節(jié)點(diǎn)標(biāo)簽來向該節(jié)點(diǎn)中追加內(nèi)容。
2.2 設(shè)備節(jié)點(diǎn)基本格式
設(shè)備樹是采用樹形結(jié)構(gòu)來描述板子上的設(shè)備信息的文件,每個(gè)設(shè)備都是一個(gè)節(jié)點(diǎn),叫做設(shè)備節(jié)點(diǎn),每個(gè)節(jié)點(diǎn)都通過一些屬性信息來描述節(jié)點(diǎn)信息,屬性就是鍵-值對(duì)。
node-name@unit-address{
屬性1 = ...
屬性2 = ...
子節(jié)點(diǎn)...
}
2.2.1 節(jié)點(diǎn)名稱
node-name用于指定節(jié)點(diǎn)名稱,其長(zhǎng)度為1~31個(gè)字符:
數(shù)字:0~9
字母:a~z
A~Z
英文符號(hào):,
.
_
+
-
節(jié)點(diǎn)名應(yīng)使用字母開頭,并能描述設(shè)備類別(根節(jié)點(diǎn)用斜杠表示,不需要節(jié)點(diǎn)名)
2.2.2 單元地址
@unit-address用于指定單元地址,其中@符號(hào)表示一個(gè)分隔符,unit-address是實(shí)際的單元地址,它的值要和節(jié)點(diǎn)reg屬性的第一個(gè)地址一致,如果沒有reg屬性值,則可以省略單元地址。
2.2.3 節(jié)點(diǎn)屬性
在節(jié)點(diǎn)的大括號(hào)“{}”中包含的內(nèi)容是節(jié)點(diǎn)屬性, 一個(gè)節(jié)點(diǎn)可以包含多個(gè)屬性信息,例如根節(jié)點(diǎn)的屬性model = "Freescale i.MX6 ULL 14x14 EVK Board"
,編寫設(shè)備樹最主要的內(nèi)容是編寫節(jié)點(diǎn)的節(jié)點(diǎn)屬性。屬性包括自定義屬性和標(biāo)準(zhǔn)屬性,下面來看幾個(gè)標(biāo)準(zhǔn)屬性:
model屬性:用于指定設(shè)備的制造商和型號(hào),多個(gè)字符串使用“,”分隔開
compatible 屬性:由一個(gè)或多個(gè)字符串組成,是用來查找節(jié)點(diǎn)的方法之一
status屬性:用于指示設(shè)備的“操作狀態(tài)” ,通過status可以禁用或啟用設(shè)備
reg屬性:描述設(shè)備資源在其父總線定義的地址空間內(nèi)的地址,通常情況下用于表示一塊寄存器的起始地址(偏移地址)和長(zhǎng)度
#address-cells 和 #size-cells:這兩個(gè)屬性同時(shí)存在,在設(shè)備樹ocrams結(jié)構(gòu)中,用在有子節(jié)點(diǎn)的設(shè)備節(jié)點(diǎn),用于設(shè)置子節(jié)的“reg”屬性的“書寫格式”
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)
2.2.4 特殊節(jié)點(diǎn)
aliases子節(jié)點(diǎn):其作用是為其他節(jié)點(diǎn)起一個(gè)別名,例如:
-
aliases {
i2c3 = &i2c4;
};
chosen子節(jié)點(diǎn):該節(jié)點(diǎn)位于根節(jié)點(diǎn)下,它不代表實(shí)際硬件, 它主要用于給內(nèi)核傳遞參數(shù),例如:
-
chosen {
stdout-path = &uart1;
};
表示系統(tǒng)標(biāo)準(zhǔn)輸出 stdout 使用串口 uart1。
3 設(shè)備樹編程之OF函數(shù)
內(nèi)核提供了一系列函數(shù)用于從設(shè)備節(jié)點(diǎn)獲取設(shè)備節(jié)點(diǎn)中定義的屬性,這些函數(shù)以 of_ 開頭,稱為OF函數(shù)。在編寫設(shè)備樹版的LED驅(qū)動(dòng)時(shí),在進(jìn)行硬件配置方面,就是要用這些OF函數(shù),將寄存器地址等信息從設(shè)備樹文件中獲取出來,然后進(jìn)行GPIO配置。
先來列舉一下這些函數(shù):
3.1 查找節(jié)點(diǎn)的OF函數(shù)
of_find_node_by_name
通過節(jié)點(diǎn)名字查找指定的節(jié)點(diǎn)
/**
* from: 開始查找的節(jié)點(diǎn),若為NULL表示從根節(jié)點(diǎn)開始查找整個(gè)設(shè)備樹
* name: 要查找的節(jié)點(diǎn)名字
* return: 找到的節(jié)點(diǎn),若為NULL表示查找失敗
*/
struct device_node *of_find_node_by_name(struct device_node *from, const char *name);
of_find_compatible_node
根據(jù)device_type和compatible這兩個(gè)屬性查找指定的節(jié)點(diǎn)
/**
* from: 開始查找的節(jié)點(diǎn),若為NULL表示從根節(jié)點(diǎn)開始查找整個(gè)設(shè)備樹
* type: 要查找的節(jié)點(diǎn)對(duì)應(yīng)的type字符串,也就是device_type屬性值
* return: 找到的節(jié)點(diǎn),若為NULL表示查找失敗
*/
struct device_node *of_find_node_by_type(struct device_node *from, const char *type)
of_find_matching_node_and_match
通過of_device_id匹配表來查找指定的節(jié)點(diǎn)
/**
* from: 開始查找的節(jié)點(diǎn),若為NULL表示從根節(jié)點(diǎn)開始查找整個(gè)設(shè)備樹
* type: 要查找的節(jié)點(diǎn)對(duì)應(yīng)的type字符串,也就是device_type屬性值,為NULL表示忽略掉device_type屬性
* compatible: 要查找的節(jié)點(diǎn)所對(duì)應(yīng)的compatible屬性列表
* return: 找到的節(jié)點(diǎn),若為NULL表示查找失敗
*/
struct device_node *of_find_compatible_node(struct device_node *from,
const char *type,
const char *compatible)
of_find_node_by_path
通過路徑來查找指定的節(jié)點(diǎn)
/**
* path: 帶有全路徑的節(jié)點(diǎn)名
* return: 找到的節(jié)點(diǎn),若為NULL表示查找失敗
*/
inline struct device_node *of_find_node_by_path(const char *path)
3.2 查找父/子節(jié)點(diǎn)的OF函數(shù)
of_get_parent
用于查找父節(jié)點(diǎn)
/**
* node: 要查找的父節(jié)點(diǎn)的節(jié)點(diǎn)
* return: 找到的父節(jié)點(diǎn)
*/
struct device_node *of_get_parent(const struct device_node *node)
of_get_next_child
用迭代的方式查找子節(jié)點(diǎn)
/**
* node: 父節(jié)點(diǎn)
* prev: 前一個(gè)子節(jié)點(diǎn),也就是從哪一個(gè)子節(jié)點(diǎn)開始迭代的查找下一個(gè)子節(jié)點(diǎn),為NULL表示從第一個(gè)子節(jié)點(diǎn)開始
* return: 找到的下一個(gè)子節(jié)點(diǎn)
*/
struct device_node *of_get_next_child(const struct device_node *node,
struct device_node *prev)
3.3 提取屬性值的OF函數(shù)
of_find_property
查找指定的屬性
/**
* np: 設(shè)備節(jié)點(diǎn)
* name: 屬性名字
* lenp: 屬性值的字節(jié)數(shù)
* return: 找到的屬性
*/
property *of_find_property(const struct device_node *np,
const char *name,
int *lenp)
of_property_count_elems_of_size
用于獲取屬性中元素的數(shù)量
/**
* np: 設(shè)備節(jié)點(diǎn)
* propname: 屬性名字
* elem_size: 元素長(zhǎng)度
* return: 屬性元素?cái)?shù)量
*/
int of_property_count_elems_of_size(const struct device_node *np,
const char *propname,
int elem_size)
of_property_read_u32_index
用于從屬性中獲取指定標(biāo)號(hào)的u32類型數(shù)據(jù)值
/**
* np: 設(shè)備節(jié)點(diǎn)
* propname: 屬性名字
* index: 要讀取的值標(biāo)號(hào)
* out_value: 讀取到的值
* return: 0讀取成功,負(fù)值讀取失敗
*/
nt of_property_read_u32_index(const struct device_node *np,
const char *propname,
u32 index,
u32 *out_value)
of_property_read_u8_array
用于讀取屬性中 u8類型的數(shù)組數(shù)據(jù)(類似的函數(shù)還有u16、u32 和 u64)
/**
* np: 設(shè)備節(jié)點(diǎn)
* propname: 屬性名字
* out_values: 讀取到的數(shù)組值
* return: 0讀取成功,負(fù)值讀取失敗
*/
int of_property_read_u8_array(const struct device_node *np,
const char *propname,
u8 *out_values,
size_t sz)
of_property_read_u8
用于讀取只有一個(gè)整形值的屬性(類似的函數(shù)還有u16、u32 和 u64)
/**
* np: 設(shè)備節(jié)點(diǎn)
* propname: 屬性名字
* out_values: 讀取到的數(shù)組值
* return: 0讀取成功,負(fù)值讀取失敗
*/
int of_property_read_u8(const struct device_node *np,
const char *propname,
u8 *out_value)
of_property_read_string
用于讀取屬性中字符串值
/**
* np: 設(shè)備節(jié)點(diǎn)
* propname: 屬性名字
* out_values: 讀取到的字符串值
* return: 0讀取成功,負(fù)值讀取失敗
*/
int of_property_read_string(struct device_node *np,
const char *propname,
const char **out_string)
of_n_addr_cells
用于獲取#address-cells 屬性值
/**
* np: 設(shè)備節(jié)點(diǎn)
* return: 獲取到的#address-cells屬性值
*/
int of_n_addr_cells(struct device_node *np)
of_n_size_cells
用于獲取#size-cells 屬性值
/**
* np: 設(shè)備節(jié)點(diǎn)
* return: 獲取到的#size-cells屬性值
*/
int of_n_size_cells(struct device_node *np)
3.4 其他常用的OF函數(shù)
of_device_is_compatible
用于查看節(jié)點(diǎn)的compatible屬性是否有包含compat指定的字符串,也就是檢查設(shè)備節(jié)點(diǎn)的兼容性
/**
* device: 設(shè)備節(jié)點(diǎn)
* compat: 要查看的字符串
* return: 0不包含,正數(shù)包含
*/
int of_device_is_compatible(const struct device_node *device,
const char *compat)
of_get_address
用于獲取地址相關(guān)屬性
/**
* dev: 設(shè)備節(jié)點(diǎn)
* index: 要讀取的地址標(biāo)號(hào)
* size: 要讀取的地址標(biāo)號(hào)
* flags: 參數(shù)
* return: 讀取到的地址數(shù)據(jù)首地址,NULL表示失敗
*/
const __be32 *of_get_address(struct device_node *dev,
int index,
u64 *size,
unsigned int *flags)
of_translate_address
用于將設(shè)備樹讀取到的地址轉(zhuǎn)換為物理地址
/**
* dev 設(shè)備節(jié)點(diǎn)
* in_addr: 要轉(zhuǎn)換的地址
* return: 得到的物理地址
*/
u64 of_translate_address(struct device_node *dev,
const __be32 *in_addr)
of_address_to_resource
用于將reg屬性值,轉(zhuǎn)換為resource結(jié)構(gòu)體類型
/**
* dev: 設(shè)備節(jié)點(diǎn)
* index: 地址資源標(biāo)號(hào)
* r: 得到的 resource 類型的資源值
* return: 0成功,負(fù)值失敗
*/
int of_address_to_resource(struct device_node *dev,
int index,
struct resource *r)
of_iomap
用于直接內(nèi)存映射
/**
* np: 設(shè)備節(jié)點(diǎn)
* index: reg屬性中要完成內(nèi)存映射的段
* return: 經(jīng)過內(nèi)存映射后的虛擬內(nèi)存首地址,為NULL表示失敗
*/
void __iomem *of_iomap(struct device_node *np,
int index)
4 設(shè)備樹LED驅(qū)動(dòng)程序與實(shí)驗(yàn)
回憶之前的LED字符設(shè)備驅(qū)動(dòng)的編寫方法:直接在驅(qū)動(dòng)文件regled.c中定義有關(guān)寄存器物理地址,然后使用io_remap函數(shù)進(jìn)行內(nèi)存映射得到對(duì)應(yīng)的虛擬地址,最后操作寄存器對(duì)應(yīng)的虛擬地址完成對(duì)GPIO的初始化。
使用設(shè)備樹編寫字符設(shè)備驅(qū)動(dòng),主要的一點(diǎn)區(qū)別是:使用設(shè)備樹向Linux內(nèi)核傳遞相關(guān)的寄存器物理地址,Linux驅(qū)動(dòng)文件使用OF函數(shù)從設(shè)備樹中獲取所需的屬性值,然后使用獲取到的屬性值來初始化相關(guān)的IO,所以,其本質(zhì)還是配置寄存器。
所以,使用設(shè)備樹進(jìn)行LED驅(qū)動(dòng),需要的修改主要為:
修改imx6ull-myboard.dts設(shè)備樹文件,在其中添加RGB-LED的設(shè)備節(jié)點(diǎn)
編寫RGB-LED驅(qū)動(dòng)程序,獲取設(shè)備樹中的相關(guān)屬性值,并使用相關(guān)的屬性值進(jìn)行GPIO的初始化
編寫RGB-LED應(yīng)用程序,控制RGB-LED的亮滅
4.1 修改設(shè)備樹文件
/ {
model = "Freescale i.MX6 ULL 14x14 EVK Board";
compatible = "fsl,imx6ull-14x14-evk", "fsl,imx6ull";
//省略...
/*myboard led*/
myboardled {
#address-cells = <1>;
#size-cells = <1>;
compatible = "myboard-led";
status = "okay";
reg = < 0X020C406C 0x04 /*CCM_CCGR1_BASE*/
0X02290014 0x04 /*SW_MUX_SNVS_TAMPER3_BASE*/
0X02290058 0x04 /*SW_PAD_SNVS_TAMPER3_BASE*/
0X020AC000 0x04 /*GPIO5_DR_BASE*/
0X020AC004 0x04 >; /*GPIO5_GDIR_BASE*/
};
};
編譯設(shè)備樹,在內(nèi)核源碼的根目錄下(我的是~/myTest/imx6ull/kernel/nxp_kernel/linux-imx-rel_imx_4.1.15_2.1.0_ga),執(zhí)行如下make命令即可單獨(dú)編譯自己修改的設(shè)備樹:
make imx6ull-myboard.dtb
4.2 測(cè)試設(shè)備樹
4.2.1 測(cè)試環(huán)境切換
由于這次是修改了設(shè)備樹文件,而我的板子已經(jīng)燒錄了固件到emmc,因此,這次實(shí)驗(yàn),重新將板子設(shè)為從SD卡啟動(dòng)uboot并從網(wǎng)絡(luò)啟動(dòng)NFS文件系統(tǒng)的方式,方便修改測(cè)試設(shè)備樹。(板子從網(wǎng)絡(luò)啟動(dòng)的方式,可參考之前的文章i.MX6ULL嵌入式Linux開發(fā)4-根文件系統(tǒng)構(gòu)建),若之前SD的uboot配置還在,將板子切換到SD卡啟動(dòng),并確保網(wǎng)絡(luò)暢通,即可從網(wǎng)絡(luò)啟動(dòng)。
若nfs服務(wù)器(ubuntu虛擬器)的IP發(fā)生變化,需要和之前一樣進(jìn)行類似如下的bootargs和bootcmd配置:
setenv bootargs 'console=ttymxc0,115200 root=/dev/nfs nfsroot=192.168.5.104:/home/xxpcb/myTest/nfs/rootfs,proto=tcp,nfsvers=4 rw ip=192.168.5.102:192.168.5.104:192.168.5.1:255.255.255.0::eth1:off'
setenv bootcmd 'tftp 80800000 nxp/zImage; tftp 83000000 nxp/imx6ull-myboard.dtb; bootz 80800000 - 83000000'
saveenv
boot
注意這里的192.168.5.104是我的ubuntu的IP,192.168.5.102是板子的IP。
4.2.2 設(shè)備樹修改后的效果
在測(cè)試設(shè)備樹之前,可以先看一下目前板子的設(shè)備樹中都有什么:
將編譯后的dtb文件放到網(wǎng)絡(luò)啟動(dòng)位置,比如我的是復(fù)制到這里:
xxpcb@ubuntuTest:~/myTest/imx6ull/kernel/nxp_kernel/linux-imx-rel_imx_4.1.15_2.1.0_ga/arch/arm/boot/dts$ cp imx6ull-myboard.dtb ~/myTest/tftpboot/nxp/
然后重啟板子,再次查看/proc/device-tree/目錄:
可以看到,出現(xiàn)了新加的myboardled節(jié)點(diǎn),進(jìn)入myboardled目錄下,可以看到其屬性信息。
4.3 修改LED驅(qū)動(dòng)程序
驅(qū)動(dòng)程序整體框架和上一篇的寄存器版配置程序基本相同,主要的不同是修改硬件配置的方式,
/*
* @description : LED硬件初始化(IO映射、時(shí)鐘、GPIO配置)
* @param : 無
* @return : 0 成功;其他 失敗
*/
static int dtsled_hardware_init(void)
{
u32 val = 0;
int ret;
u32 regdata[14];
const char *str;
struct property *proper;
/* 獲取設(shè)備樹中的屬性數(shù)據(jù) */
/* 1、獲取設(shè)備節(jié)點(diǎn):myboardled */
dtsled.nd = of_find_node_by_path("/myboardled");
if(dtsled.nd == NULL)
{
printk("myboardled node nost find!\r\n");
return -EINVAL;
}
else
{
printk("myboardled node find!\r\n");
}
/* 2、獲取compatible屬性內(nèi)容 */
proper = of_find_property(dtsled.nd, "compatible", NULL);
if(proper == NULL)
{
printk("compatible property find failed\r\n");
}
else
{
printk("compatible = %s\r\n", (char*)proper->value);
}
/* 3、獲取status屬性內(nèi)容 */
ret = of_property_read_string(dtsled.nd, "status", &str);
if(ret < 0)
{
printk("status read failed!\r\n");
}
else
{
printk("status = %s\r\n",str);
}
/* 4、獲取reg屬性內(nèi)容 */
ret = of_property_read_u32_array(dtsled.nd, "reg", regdata, 10);
if(ret < 0)
{
printk("reg property read failed!\r\n");
}
else
{
u8 i = 0;
printk("reg data:\r\n");
for(i = 0; i < 10; i++)
{
printk("%#X ", regdata[i]);
}
printk("\r\n");
}
/* 初始化LED */
#if 0
/* 1、寄存器地址映射(使用ioremap) */
IMX6U_CCM_CCGR1 = ioremap(regdata[0], regdata[1]);
SW_MUX_SNVS_TAMPER3 = ioremap(regdata[2], regdata[3]);
SW_PAD_SNVS_TAMPER3 = ioremap(regdata[4], regdata[5]);
GPIO5_DR = ioremap(regdata[6], regdata[7]);
GPIO5_GDIR = ioremap(regdata[8], regdata[9]);
#else
/* 1、寄存器地址映射(直接使用of_iomap) */
IMX6U_CCM_CCGR1 = of_iomap(dtsled.nd, 0);
SW_MUX_SNVS_TAMPER3 = of_iomap(dtsled.nd, 1);
SW_PAD_SNVS_TAMPER3 = of_iomap(dtsled.nd, 2);
GPIO5_DR = of_iomap(dtsled.nd, 3);
GPIO5_GDIR = of_iomap(dtsled.nd, 4);
#endif
/* 2、使能GPIO1時(shí)鐘 */
//省略... 后面的配置與上一篇的相同
}
上面的程序修改部分,從整個(gè)LED驅(qū)動(dòng)的框架來看,修改的只是如下圖中的黃色框部分:
4.4 實(shí)驗(yàn)測(cè)試
編譯設(shè)備樹版的LED驅(qū)動(dòng)程序,并將編譯好的ko文件發(fā)送到nfs文件系統(tǒng)對(duì)應(yīng)的文件夾下。
LED是應(yīng)用程序不需要修改,仍使用上一篇文章中的程序即可。
測(cè)試方法與之前基本相同:
使用設(shè)備樹的方式,再次點(diǎn)亮LED:
5 總結(jié)
本篇介紹了設(shè)備樹的基本原理以及設(shè)備樹的使用方法,在上一篇點(diǎn)亮LED的代碼基礎(chǔ)上,通過設(shè)備樹的方式,實(shí)現(xiàn)了LED點(diǎn)燈,總結(jié)一下主要的修改就是先在設(shè)備樹中添加LED節(jié)點(diǎn),然后在驅(qū)動(dòng)文件中通過OF函數(shù)來讀取設(shè)備樹中的寄存器信息,再進(jìn)行GPIO的初始化,其它部分的程序與上一篇的基本一樣。
審核編輯:符乾江
-
嵌入式
+關(guān)注
關(guān)注
5059文章
18973瀏覽量
302062 -
驅(qū)動(dòng)
+關(guān)注
關(guān)注
12文章
1818瀏覽量
85110 -
Linux
+關(guān)注
關(guān)注
87文章
11208瀏覽量
208721
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論