對於任何的知識來說,了解了理論的知識,知道了設備樹怎么解析用以代替傳統的范式之后,我們需要知道怎么使用設備樹。對於使用我們分兩部分,一部分是它有哪些接口,能做些什么,至於怎么編寫dts文件本章不討論。主要包括兩部分:
(1)對於設備樹,編譯和設備啟動后,怎么來查看設備樹的信息,怎么用來debug
(2)設備樹的操作函數提供了哪些接口,基本的方法有哪些
1 文件系統下設備樹
一部分是出現問題后,怎么用來debug,對於內核來說一切皆是文件的思想,設備樹與文件系統的關系,在Linux系統起來后,會將解析完成的設備樹導出到用戶空間。
kernel啟動在of_init()函數中在sys/firmware/devicetree/base目錄下面為設備樹展開成sysfs的目錄和二進制屬性文件,所有的node節點就是一個目錄,所有的property屬性就是一個二進制屬性文件。
1 static int __init of_init(void) 2 { 3 struct device_node *np; 4 5 /* Create the kset, and register existing nodes */ 6 mutex_lock(&of_mutex); 7 of_kset = kset_create_and_add("devicetree", NULL, firmware_kobj); 8 if (!of_kset) { 9 mutex_unlock(&of_mutex); 10 return -ENOMEM; 11 } 12 for_each_of_allnodes(np) 13 __of_attach_node_sysfs(np); 14 mutex_unlock(&of_mutex); 15 16 /* Symlink in /proc as required by userspace ABI */ 17 if (of_allnodes) 18 proc_symlink("device-tree", NULL, "/sys/firmware/devicetree/base"); 19 20 return 0; 21 } 22 core_initcall(of_init);
該函數主要完成下面三件事情:
(1)創建/sys/firmware/devicetree/目錄
(2)遍歷所有的設備樹節點,並且在./sys/firmware/devicetree/目錄下創建對應的sysfs,以目錄結構程現的dtb文件, 根節點對應base目錄, 每一個節點對應一個目錄, 每一個屬性對應一個文件
(3)在/proc/device-tree文件是軟連接,軟連接到/sys/firmware/devicetree/base 。
通過kernel_path/drivers/of/Kconfig 中的config:PROC_DEVICETREE ,重新編譯內核,系統就會生成文件 /proc/device-tree 。
2 設備樹的操作函數
學習完了設備樹的文件系統查看方法,那么對於在源碼中去拿到設備樹的節點或者屬性信息,內核提供了那些操作函數呢?內核中開放出來的接口函數的聲明大多在include/linux/下面,關於設備樹的都是以of形式開頭的命名:
對應各個文件主要完成什么功能如下:
文件名 功能描述
of.h :提供設備樹的一般處理函數,大部分的功能函數都在該文件中
of_address.h :地址相關的函數, 比如 of_get_address(獲得reg屬性中的addr, size值)
of_device.h :設備相關的函數, 比如 of_match_device、of_device_register
of_dma.h :設備樹中DMA相關屬性的函數
of_fdt.h dtb:文件的相關操作函數,我們一般不使用
of_gpio.h :GPIO相關的函數
of_graph.h: GPU相關驅動中用到的函數, 從設備樹中獲得GPU信息
of_iommu.h: IOMMU相關函數
of_irq.h :中斷相關的函數
of_mdio.h: mdio相關的函數
of_mtd.h: mtd相關的函數
of_net.h: net相關的函數
of_pci.h :pci相關的函數
of_platform.h :把device_node轉換為platform_device時用到的函數
of_reserved_mem.h :reserved_mem的相關函數
定義位於:drivers/of目錄下。
2.1 查找節點
通過compatible屬性查找指定節點,根據兼容屬性,獲得設備節點。遍歷設備樹中的設備節點,看看哪個節點的類型、兼容屬性與本函數的輸入參數匹配,在大多數情況下,from、type為NULL,則表示遍歷了所有節點。
struct device_node *of_find_compatible_node(struct device_node *from, const char *type, const char *compat);
使用方法可以如下
np = of_find_compatible_node(NULL, NULL, “fsl,imx23-digctl”); digctrl = of_iomap(np, 0);
dts中對應如下:
1 digctl: digctl@8001c000 { 2 compatible = "fsl,imx28-digctl", "fsl,imx23-digctl"; 3 reg = <0x8001c000 0x2000>; 4 interrupts = <89>; 5 status = "disabled"; 6 };
通過節點名查找指定節點
1 static const struct of_device_id exynos_dt_mcpm_match[] = { 2 { .compatible = "samsung,exynos5420" }, 3 { .compatible = "samsung,exynos5800" }, 4 {}, 5 }; 6 7 node = of_find_matching_node(NULL, exynos_dt_mcpm_match); 8 if (!node) 9 return -ENODEV; 10 of_node_put(node);
通過路徑查找指定節點
struct device_node *of_find_node_by_path(const char *path); /* 參數: const char *path - 帶全路徑的節點名,也可以是節點的別名 */
data->current_node = of_find_node_by_path("/");
通過節點名查找指定節點
1 sscg_np = of_find_node_by_name(NULL, "sscg"); 2 if (sscg_np == NULL) { 3 pr_err("cannot get SSCG register node\n"); 4 return system_clk; 5 } 6 7 sscg_map = of_iomap(sscg_np, 0); 8 if (sscg_map == NULL) { 9 pr_err("cannot map SSCG register\n"); 10 goto out; 11 }
2.2 讀取屬性
1 int of_property_read_u8_array(const struct device_node *np, 2 const char *propname, u8 *out_values, size_t sz); 3 int of_property_read_u16_array(const struct device_node *np, 4 const char *propname, u16 *out_values, size_t sz); 5 int of_property_read_u32_array(const struct device_node *np, 6 const char *propname, u32 *out_values, size_t sz); 7 int of_property_read_u64(const struct device_node *np, 8 const char*propname, u64 *out_value);
讀取設備節點np的屬性名,為propname,屬性類型為8、16、32、64位整型數組。對於32位處理器來講,最常用的是of_property_read_u32_array()。
除了整型屬性外,字符串屬性也比較常用,其對應的API包括:
1 int of_property_read_string(struct device_node *np, 2 const char*propname,const char **out_string); 3 int of_property_read_string_index(struct device_node *np, 4 const char*propname,int index, const char **output);
前者讀取字符串屬性,后者讀取字符串數組屬性中的第index個字符串。
除整型、字符串以外的最常用屬性類型就是布爾型,其對應的API很簡單:
static inline bool of_property_read_bool(const struct device_node *np, const char *propname);
如果設備節點np含有propname屬性,則返回true,否則返回false。一般用於檢查空屬性是否存在。
2.3 內存映射
void __iomem *of_iomap(struct device_node *node, int index);
此API可以直接通過設備節點進行設備內存區間的ioremap(),index是內存段的索引。若設備節點的reg屬性有多段,可通過index標示要ioremap()的是哪一段,在只有1段的情況,index為0。采用設備樹后,一些設備驅動通過of_iomap()而不再通過傳統的ioremap()進行映射,當然,傳統的ioremap()的用戶也不少。
int of_address_to_resource(struct device_node *dev, int index,struct resource *r);
API通過設備節點獲取與它對應的內存資源的resource結構體。其本質是分析reg屬性以獲取內存基地址、大小等信息並填充到struct resource*r參數指向的結構體中。
2.4 解析中斷
unsigned int irq_of_parse_and_map(struct device_node *dev, int index);
通過設備樹獲得設備的中斷號,實際上是從.dts中的interrupts屬性里解析出中斷號。若設備使用了多個中斷,index指定中斷的索引號。
2.5 獲取與節點對應的platform device
拿到device_node的情況下,使用以下API獲取對應的platform_device
struct platform_device *of_find_device_by_node(struct device_node *np);
當然,在已知platform_device的情況下,也可獲取device_node
static int sirfsoc_dma_probe(struct platform_device *op) { struct device_node *dn = op->dev.of_node; … }
參考博文:
https://blog.csdn.net/u012489236/article/details/97395748
https://blog.csdn.net/baidu_38661691/article/details/97013406