全志_基于dts设备树驱动开发
基于设备树开发 平台: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
Stay hungry, stay foolish
待续。。。
分类:
1—ARM_Allwinner
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)