linux設備驅動(21)設備樹詳解5-dts的應用


對於任何的知識來說,了解了理論的知識,知道了設備樹怎么解析用以代替傳統的范式之后,我們需要知道怎么使用設備樹。對於使用我們分兩部分,一部分是它有哪些接口,能做些什么,至於怎么編寫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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM