基於設備樹開發 平臺:A64 源碼:Linux3.10 路徑:Linux/arch/arm(arm64)/boot/dts/ ***.dts Makefile //A64 是64位,選擇arm64 Linux2.6 以前沒有dts(arch/arm/plat-xxx和arch/arm/mach-xxx存在大量垃圾代碼) dts:結點+屬性(name=value) dts---(dtc)--->dtb DT DTS DTSI OF:open firmware 開源硬件 FDT DTB DTC 語法: 1.根節點 /{ 2.節點-子節點 <名稱>[@<設備地址>] cpu@1 []可省,每一個節點都須要一個compatible屬性 3.屬性 compatible屬性 做用:匹配 compatible = "jk,leds"; #address-cells和#size-cells屬性 做用:地址佔cell個數和長度佔cell個數 #address-cells = <2> #size-cells = <1> 描述子節點 reg屬性 做用:地址 reg = <address1 length1 [address2 length2][address3 length3]> 中斷信息屬性--interrupts 中斷號 #interrupt-cells 中斷控制器字節屬性 添加節點: node@12345{ compatible = "jk,leds"; reg = <0x1111 0x111> 1.內核啓動會加載dtb文件,加載信息到系統 2.節點位置:ls proc/device-tree (設備樹節點信息) static int__init dt_drv_init(void) { /* 在代碼中獲取整個節點信息 */ //1.先獲取節點 of_find_node_by_path("/text@123"); //2.獲取節點中屬性 of_find_property(); return 0; } static void__exit dt_drv_init(void) { } module_init(dt_drv_init); module_exit(de_drv_exit); modules_license("GPL"); #gpio-cells = <6>」表示在設備樹裏描述使用一個gpio口須要提供6個指定的參數. gpio = <&pio 1 1 1 1 1 0>; | | | | | | | |-------------------表示有效電平 | | | | | | |----------------------上下拉, 0關閉功能, 1上拉, 2下拉, 3保留 | | | | | |--------------------------驅動力,電流等級(0 - 3),級別越高,輸出電流越大 | | | | |------------------------------gpio功能類型(複用類型),0輸入, 1輸出, 6和外部中斷,7關閉功能(具體查手冊) | | | |----------------------------------pin bank 內偏移(即組內第幾個io口). | | |--------------------------------------哪組gpio, PA(0),PB(1),PC(2),PD(3),PE(4),PF(5),PG(6),PH(7),PI(8),PJ(9),PK(10),PL(11) | |-------------------------------------------指向哪一個gpio控制器, pio / r_pio(PL組) |--------------------------------------------------屬性名字(隨便命名) 1.兩個GPIO控制器 r_pio pio ___________________________________________________________________________________ 獲取設備樹裏設備節點的gpio口信息: #include <linux/of_gpio.h> //只需一個函數便可 int of_get_named_gpio_flags(struct device_node *np, const char *propname, int index, enum of_gpio_flags *flags); /* 功能:函數用於獲取指定名稱的 gpio 信息 np: 須要查找 GPIO 的節點; propname: GPIO 信息的屬性名字; index: 屬性 propname 中屬性值的索引(表示獲取屬性裏的第幾個值); flags: 存放 gpio 的 flags; 返回值: 成功:返回 gpio 編號, flags 存放 gpio 配置信息;失敗:返回 null */ //其中flags必定得注意,按文檔裏的說明應就是一個int類型的值,但根本就不能爲int參數(會致使kernel panic), //經過閱讀內核裏的代碼得出, flags的參數應爲struct gpio_config類型. 定義在下面文件: "include/linux/sys_config.h" struct gpio_config { u32 gpio; /* gpio global index, must be unique */ u32 mul_sel; /* multi sel val: 0 - input, 1 - output... */ u32 pull; /* pull val: 0 - pull up/down disable, 1 - pull up... */ u32 drv_level; /* driver level val: 0 - level 0, 1 - level 1... */ u32 data; /* data val: 0 - low, 1 - high, only vaild when mul_sel is input/output */ }; /* 節點 */ struct device_node { const char *name; /* 節點中屬性爲name的值 */ const char *type; /* 節點中屬性爲device_type的值 */ char *full_name; /* 節點的名字,在device_node結構體後面放一個字符串,full_name指向它 */ struct property *properties; /* 鏈表,鏈接該節點的全部屬性 */ struct device_node *parent; /* 指向父節點 */ struct device_node *child; /* 指向孩子節點 */ struct device_node *sibling; /* 指向兄弟節點 */ }; /* 屬性 */ struct property { char *name; /* 屬性的名字,指向設備樹文件的string block */ int length; /* 屬性名字的字節數 */ void *value; /* 屬性的值,指向struct block */ struct property *next; /* 鏈表,鏈接下一個屬性 */ }; Linux 系統爲 device tree 提供了標準的 API 接口: (OF: API) 1. unsigned int irq_of_parse_and_map(struct device_node *dev, int index) /* 功能:函數用於獲取中斷號 dev: 要解析中斷號的設備; index: dts 源文件中節點 interrupt 屬性值索引 返回值:解析成功返回中斷號,失敗:返回 0 */ 2. void __iomem *of_iomap(struct device_node *np, int index); /* 功能:函數用於獲取映射內存 np: 映射內存的設備節點; index: dts 源文件中節點 interrupt 屬性值索引; 返回值:映射成功 IO memory 的虛擬地址,失敗:返回 NULL */ 3. static inline int of_property_read_u32(const struct device_node *np, const char *propname, u32 *out_value) /* 功能:函數用於獲取節點中的屬性值 np: 獲取屬性值的節點 propname: 屬性名稱; out_value:屬性值 返回值: 映射成功 IO memory 的虛擬地址,失敗:返回 NULL */ 4. static inline int of_property_read_string_index(struct device_node *np, const char *propname, const char **output) /* 功能:函數用於獲取節點中屬性值 np: 獲取屬性值的節點 propname: 屬性名稱; output: 存放返回字符串; 返回值: 取值成功,返回 0 */ 5. static inline int of_property_read_string_index(struct device_node *np, const char *propname, int index, const char **output) /* 功能:函數用於獲取節點中屬性值 np: 獲取屬性值的節點 propname: 屬性名稱; index: 索引配置在 dts 中屬性爲 propname 的值; output: 存放返回字符串 返回值: 取值成功,返回 0 */ 6. extern struct device_node *of_find_node_by_name(struct device_node *from, const char *name); /* 功能:函數用於獲取指定名稱的節點 from: 開始查找節點,若是爲NULL,則從根節點開始; name: 節點名字; 返回值: 成功:獲得節點的結構體首地址;失敗:NUL */ 7. extern struct device_node *of_find_node_by_name(struct device_node *from, const char *type); /* 功能:函數用於獲取指定 device_type 的節點 from: 開始查找節點,若是爲NULL,則從根節點開始; type: 節點名字; 返回值: 成功:獲得節點的結構體首地址;失敗:NUL */ 8. extern struct device_node *of_find_node_by_path(const char *path); /* 功能:函數用於獲取指定路徑的節點 path: 經過指定路徑查找節點; 返回值: 成功:獲得節點的結構體首地址;失敗:NUL */ 9. int of_get_named_gpio_flags(struct device_node *np, const char *propname, int index, enum of_gpio_flags *flags) /* 功能:函數用於獲取指定名稱的 gpio 信息 np: 須要查找 GPIO 的節點; propname: GPIO 信息的屬性; index: 屬性 propname 中屬性值的索引; flags: 存放 gpio 的 flags; 返回值: 成功:返回 gpio 編號, flags 存放 gpio 配置信息;失敗:返回 null */ ___________________________________________________________________________________ 獲取到int類型的gpio口後,就能夠使用linux/gpio.h裏的gpio口操做函數: #include <linux/gpio.h> //裏面聲明io口的操做函數 int gpio_request(unsigned gpio, const char *label); //每一個io只能被請求一次,可防止多個驅動來控制同一個IO口 void gpio_free(unsigned gpio); //釋放已請求的io口 int gpio_direction_input(unsigned gpio); //做輸入功能, gpio用於指定具體哪一個io口 int gpio_direction_output(unsigned gpio, int value); //做輸出功能,並根據value的值輸出高低電平 int gpio_get_value(unsigned gpio); //獲取指定IO口的電平 void gpio_set_value(unsigned gpio, int value); //設置IO口的電平爲value(0/1) int gpio_to_irq(unsigned gpio); //根據io口,獲取到它對應的中斷號(io口大都有外部中斷功能) int request_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id) ___________________________________________________________________________________ 設備樹裏的描述: jkbuzzer { compatible = "jk,buzzer"; gpios = <&pio 3 24 1 1 1 1>; }; jkleds { compatible = "jk,leds"; gpios = <&r_pio 11 10 1 1 1 1>, <&r_pio 11 12 1 1 1 1>; }; //PH8 text { compatible = "text_"; gpios = <&pio 7 9 1 1 1 0>; }; ___________________________________________________________________________________ 代碼測試: #include <linux/init.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/of.h> #include <linux/of_gpio.h> #include <linux/gpio.h> #include <linux/sys_config.h> #include <linux/delay.h> int myprobe(struct platform_device *pdev) { struct device_node *nd = pdev->dev.of_node; int gpio; struct gpio_config config; printk("gpio count:%d\n", of_gpio_named_count(nd, "gpios")); gpio = of_get_named_gpio_flags(nd, "gpios", 0, (enum of_gpio_flags *)&config); if (!gpio_is_valid(gpio)) printk("gpio isn't valid\n"); if (gpio_request(gpio, pdev->name) < 0) printk("gpio request failed %d\n", gpio); gpio_direction_output(gpio, 1); msleep(3000); gpio_direction_input(gpio); gpio_free(gpio); return 0; } int myremove(struct platform_device *pdev) { printk("in myremove ...\n"); return 0; } struct of_device_id ids[] = { {.compatible = "jk,buzzer"}, {}, }; struct platform_driver mydrv = { .probe = myprobe, .remove = myremove, .driver = { .owner = THIS_MODULE, .name = "mydrv" , .of_match_table = ids, }, }; module_platform_driver(mydrv); MODULE_LICENSE("GPL"); 代碼測試: #include <linux/init.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/of.h> #include <linux/of_gpio.h> #include <linux/gpio.h> #include <linux/sys_config.h> #include <linux/delay.h> int myprobe(struct platform_device *pdev) { struct device_node *nd = pdev->dev.of_node; int gpio, n, i; struct gpio_config config; n = of_gpio_named_count(nd, "gpios"); for (i = 0; i < n ; i++) { gpio = of_get_named_gpio_flags(nd, "gpios", i, (enum of_gpio_flags *)&config); if (!gpio_is_valid(gpio)) printk("gpio isn't valid\n"); if (gpio_request(gpio, pdev->name) < 0) printk("gpio request failed %d\n", gpio); gpio_direction_output(gpio, 1); msleep(3000); gpio_direction_input(gpio); gpio_free(gpio); } return 0; } int myremove(struct platform_device *pdev) { printk("in myremove ...\n"); return 0; } struct of_device_id ids[] = { {.compatible = "jk,leds"}, {}, }; struct platform_driver mydrv = { .probe = myprobe, .remove = myremove, .driver = { .owner = THIS_MODULE, .name = "mydrv" , .of_match_table = ids, }, }; module_platform_driver(mydrv); MODULE_LICENSE("GPL"); 優質博客: https://blog.csdn.net/jklinux/article/details/82391923 http://www.pianshen.com/article/520912458/ https://www.linuxidc.com/Linux/2017-02/140818.htm