Linux字符设备驱动详解六(设备树实现RGB灯驱动)
2021/11/28 7:10:38
本文主要是介绍Linux字符设备驱动详解六(设备树实现RGB灯驱动),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
前言
请先阅读:
Linux字符设备驱动详解
Linux字符设备驱动详解二(使用设备驱动模型)
Linux字符设备驱动详解三(使用class)
Linux字符设备驱动详解四(使用自属的xbus驱动总线)
Linux字符设备驱动详解五(使用platform虚拟平台总线)
本文主要来自正点原子、野火Linux教程及本人理解,若有侵权请及时联系本人删除。
正文
一、DTS设备树基本语法从上到下
背景
硬件设备中种类逐年递增,板级platform平台设备文件越来越多,这么多的设备如果都要自己写platform_device.c文件,那将需要跟每个外设的寄存器打交道,并且需要很多的platform_device.c文件
设备树简介
- DTS(device tree source):设备树源文件,ASCII 格式
- DTC(device tree compiler):设备树编译工具
- DTB(device tree blob):二进制设备树
设备树使用
uboot负责加载到内存,内核解析使用
设备树源文件
ebf-buster-linux/arch/arm/boot/dts/imx6ull-seeed-npi.dts
二进制设备树
pc:ebf-buster-linux/arch/arm/boot/dts/imx6ull-seeed-npi.dtb
开发板:/boot/dtbs/4.19.71-imx-r1/imx6ull-seeed-npi.dtb
设备树编译工具
内核编译
//进行内核配置 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- npi_v7_defconfig //编译dts make ARCH=arm -j4 CROSS_COMPILE=arm-linux-gnueabihf- dtbs
手工编译
./scripts/dtc/dtc -I dts -O dtb -o xxx.dtb arch/arm/boot/dts/xxx.dts // 编译 dts 为 dtb ./scripts/dtc/dtc -I dtb -O dts -o xxx.dts arch/arm/boot/dts/xxx.dtb // 反编译 dtb 为 dts
- -I:指定输入格式
- -O:指定输出格式
- -o:指定输出文件
设备树框架
- 从上到下
- 头文件
- 主体
- 子节点追加内容
- 从外到内
- 属性
- 其他子节点
- 属性
- 其他子节点
- …
头文件:
#include <dt-bindings/input/input.h> #include "imx6ull.dtsi"
主体:
/ { model = "Seeed i.MX6 ULL NPi Board"; compatible = "fsl,imx6ull-14x14-evk", "fsl,imx6ull"; aliases { pwm0 = &pwm1; pwm1 = &pwm2; pwm2 = &pwm3; pwm3 = &pwm4; }; 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; }; }; ... };
- 多个根节点合并
- 根节点下包含多个子节点
子节点追加内容
&cpu0 { dc-supply = <®_gpio_dvfs>; clock-frequency = <800000000>; }; &clks { assigned-clocks = <&clks IMX6UL_CLK_PLL4_AUDIO_DIV>; assigned-clock-rates = <786432000>; }; &fec1 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_enet1>; phy-mode = "rmii"; phy-handle = <ðphy0>; status = "okay"; };
节点命令
基本方法
node-name@unit-address{ 属性1 = … 属性2 = … 属性3= … 子节点… }
- node-name:指定节点的名称
- “unit-address”用于指定“单元地址”
节点标签
cpu0: cpu@0 { compatible = "arm,cortex-a7"; device_type = "cpu"; reg = <0>; }
- cpu0:为节点名称器一个别名
别名子节点
aliases {
二、DTS设备树基本语法从外到内
-
从上到下
- 头文件
- 主体
- 子节点追加内容
-
从外到内
- 属性
- 其他子节点
- 属性
- 其他子节点
- …
常见节点属性
compatible属性:
值类型:字符串
intc: interrupt-controller@a01000 { compatible = "arm,cortex-a7-gic"; #interrupt-cells = <3>; interrupt-controller; reg = <0xa01000 0x1000>, <0xa02000 0x100>; };
- arm:芯片厂商
- cortex-a7-gic:模块对应的驱动名字
model属性:
值类型:字符串
model = "embedfire i.MX6 ULL NPi Board";
- 准确描述当前板子型号信息
status属性:
值类型:字符串
reg属性:
值类型:一系列《地址、长度》对
ocrams: sram@900000 { compatible = "fsl,lpm-sram"; reg = <0x900000 0x4000>; };
- 地址:外设寄存器组的起始地址
- 长度:外设寄存器组的字节长度
#address-cells和#size-cells属性:
值类型:u32
soc { #address-cells = <1>; #size-cells = <0>; compatible = "simple-bus"; interrupt-parent = <&gpc>; ranges; ocrams: sram@900000 { compatible = "fsl,lpm-sram"; reg = <0x900000>; }; };
- #address-cells :设置子节点中reg地址的数量
- #size-cells:设置子节点中reg地址的长度的数量
linux系统中查看设备树
ls /sys/firmware/devicetree/base
或者
ls /proc/device-tree
- 以目录的形式体现设备树结构
添加子节点
test_led{ #address-cells = <1>; #size-cells = <1>; rgb_led_red@0x0209C000{ compatible = "fire,rgb_led_red"; reg = <0x0209C000 0x00000020>; status = "okay"; }; };
三、获取DTS属性信息
- 查属性所在的节点
- 查节点的属性值
节点表示
struct device_node { const char *name; //节点名 const char *type; //设备类型 phandle phandle; const char *full_name; //完整名字 struct fwnode_handle fwnode; struct property *properties; //属性 struct property *deadprops; struct device_node *parent; //父节点 struct device_node *child; //子节点 struct device_node *sibling; #if defined(CONFIG_OF_KOBJ) struct kobject kobj; #endif unsigned long _flags; void *data; #if defined(CONFIG_SPARC) const char *path_component_name; unsigned int unique_id; struct of_irq_controller *irq_trans; #endif };
查节点
- 路径/类型/名字/compatible
of_find_node_by_path()函数,根据路径找到节点
struct device_node *of_find_node_by_path(struct device_node *from,const char *path);
参数:
- from:开始查找的节点,NULL表示从根节点开始查找
- path:查找的节点名
返回值:
成功:device_node表示的节点
失败:NULL
of_find_node_by_type()函数,根据“device_type“属性来查找节点,不建议使用
struct device_node *of_find_node_by_type(struct device_node *from, const char *type);
of_find_node_by_name()函数,根据"name"属性来查找节点,不建议使用
struct device_node *of_find_node_by_name(struct device_node *from,const char *name);
of_find_compatible_node()函数
struct device_node *of_find_compatible_node(struct device_node *from,const char *type, const char *compat);
参数:
- from:开始查找的节点,NULL表示从根节点开始查找
- type:指定 device_type 属性值
- compat:指定 compatible 属性值
返回值:
成功:device_node表示的节点
失败:NULL
查节点的属性值
struct property { char *name; //属性名 int length; //属性长度 void *value; //属性值 struct property *next; //下一个属性 #if defined(CONFIG_OF_DYNAMIC) || defined(CONFIG_SPARC) unsigned long _flags; #endif #if defined(CONFIG_OF_PROMTREE) unsigned int unique_id; #endif #if defined(CONFIG_OF_KOBJ) struct bin_attribute attr; #endif };
of_find_property()函数
- 节点+属性名
查找节点中的属性
struct property *of_find_property(const struct device_node *np,const char *name,int *lenp);
参数:
- np:device_node表示的节点
- name:查找的属性名字
- lenp:属性值的字节数
返回值:
成功:property表示的属性
失败:NULL
案例:
test_property { test_name = “hello”; };
name:“hello”
lenp = 6
of_property_read_u32()函数,读取一个32位无符号整数
static inline int of_property_read_u32(const struct device_node *np,const char *propname,
参数:
- np:device_node表示的节点
- propname:查找的属性名字
- out_value:属性值的整数值
返回值:
成功:0
失败:负值
of_property_read_u32_array()函数,读取32位无符号整数数组
int of_property_read_u32_array(const struct device_node *np,const char *propname,u32 *out_values,size_t sz)
- np:device_node表示的节点
- name:查找的属性名字
- out_value:读取到的数组值
- sz :要读取的数组元素数量
of_property_read_string()函数,读字符串
int of_property_read_string(struct device_node *np,const char *propname,const char **out_string)
参数:
- np:device_node表示的节点
- proname:查找的属性名字
- out_string:读取到的字符串值
返回值:
成功:0
失败:负值
代码示例
以野火设备驱动模型代码为例
get_dts_info.c
#include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/uaccess.h> #include <linux/types.h> #include <linux/kernel.h> #include <linux/delay.h> #include <linux/ide.h> #include <linux/errno.h> #include <linux/gpio.h> #include <asm/mach/map.h> #include <asm/io.h> #include <linux/of.h> #include <linux/of_address.h> #define DEV_NAME "get_dts_info" #define DEV_CNT (1) //定义字符设备的设备号 static dev_t led_devno; //定义字符设备结构体chr_dev static struct cdev led_chr_dev; //创建类 struct class *led_chrdev_class; struct device_node *led_device_node; //led的设备树节点 struct device_node *rgb_led_red_device_node; //rgb_led_red 红灯节点 struct property *rgb_led_red_property; //定义属性结构体指针 int size = 0 ; unsigned int out_values[18]; //保存读取得到的REG 属性值 /*.open 函数*/ static int led_chr_dev_open(struct inode *inode, struct file *filp) { int error_status = -1; printk("\n open form device \n"); /*获取DTS属性信息*/ led_device_node = of_find_node_by_path("/test_led"); if(led_device_node == NULL) { printk(KERN_ALERT "\n get led_device_node failed ! \n"); return -1; } /*根据 led_device_node 设备节点结构体输出节点的基本信息*/ printk(KERN_ALERT "name: %s",led_device_node->name); //输出节点名 printk(KERN_ALERT "child name: %s",led_device_node->child->name); //输出子节点的节点名 /*获取 rgb_led_red_device_node 的子节点*/ rgb_led_red_device_node = of_get_next_child(led_device_node,NULL); if(rgb_led_red_device_node == NULL) { printk(KERN_ALERT "\n get rgb_led_red_device_node failed ! \n"); return -1; } printk(KERN_ALERT "name: %s",rgb_led_red_device_node->name); //输出节点名 printk(KERN_ALERT "parent name: %s",rgb_led_red_device_node->parent->name); //输出父节点的节点名 /*获取 rgb_led_red_device_node 节点 的"compatible" 属性 */ rgb_led_red_property = of_find_property(rgb_led_red_device_node,"compatible",&size); if(rgb_led_red_property == NULL) { printk(KERN_ALERT "\n get rgb_led_red_property failed ! \n"); return -1; } printk(KERN_ALERT "size = : %d",size); //实际读取得到的长度 printk(KERN_ALERT "name: %s",rgb_led_red_property->name); //输出属性名 printk(KERN_ALERT "length: %d",rgb_led_red_property->length); //输出属性长度 printk(KERN_ALERT "value : %s",(char*)rgb_led_red_property->value); //属性值 /*获取 reg 地址属性*/ error_status = of_property_read_u32_array(rgb_led_red_device_node,"reg",out_values, 2); if(error_status != 0) { printk(KERN_ALERT "\n get out_values failed ! \n"); return -1; } printk(KERN_ALERT"0x%08X ", out_values[0]); printk(KERN_ALERT"0x%08X ", out_values[1]); return 0; } /*.release 函数*/ static int led_chr_dev_release(struct inode *inode, struct file *filp) { printk("\nrelease\n"); return 0; } /*字符设备操作函数集*/ static struct file_operations led_chr_dev_fops = { .owner = THIS_MODULE, .open = led_chr_dev_open, .release = led_chr_dev_release, }; /* *驱动初始化函数 */ static int __init led_chrdev_init(void) { int ret = 0; printk("led chrdev init\n"); //第一步 //采用动态分配的方式,获取设备编号,次设备号为0, //设备名称为EmbedCharDev,可通过命令cat /proc/devices查看 //DEV_CNT为1,当前只申请一个设备编号 ret = alloc_chrdev_region(&led_devno, 0, DEV_CNT, DEV_NAME); if(ret < 0){ printk("fail to alloc led_devno\n"); goto alloc_err; } led_chrdev_class = class_create(THIS_MODULE, "led_chrdev"); //第二步 //关联字符设备结构体cdev与文件操作结构体file_operations cdev_init(&led_chr_dev, &led_chr_dev_fops); //第三步 //添加设备至cdev_map散列表中 ret = cdev_add(&led_chr_dev, led_devno, DEV_CNT); if(ret < 0) { printk("fail to add cdev\n"); goto add_err; } //创建设备 device_create(led_chrdev_class, NULL, led_devno, NULL, DEV_NAME); return 0; add_err: //添加设备失败时,需要注销设备号 unregister_chrdev_region(led_devno, DEV_CNT); alloc_err: return ret; } /* *驱动注销函数 */ static void __exit led_chrdev_exit(void) { printk("chrdev exit\n"); device_destroy(led_chrdev_class, led_devno); //清除设备 cdev_del(&led_chr_dev); //清除设备号 unregister_chrdev_region(led_devno, DEV_CNT); //取消注册字符设备 class_destroy(led_chrdev_class); //清除类 } module_init(led_chrdev_init); module_exit(led_chrdev_exit); MODULE_LICENSE("GPL");
四、 设备树实现RGB灯驱动
设备树添加节点信息
RGB灯的相关寄存器
/* *CCM_CCGR1 0x020C406C *IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO04 0x020E006C *IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO04 0x020E02F8 *GPIO1_GD 0x0209C000 *GPIO1_GDIR 0x0209C004 */ /* *CCM_CCGR3 0x020C4074 *IOMUXC_SW_MUX_CTL_PAD_CSI_HSYNC 0x020E01E0 *IOMUXC_SW_PAD_CTL_PAD_CSI_HSYNC 0x020E046C *GPIO4_GD 0x020A8000 *GPIO4_GDIR 0x020A8004 */ /* *CCM_CCGR3 0x020C4074 *IOMUXC_SW_MUX_CTL_PAD_CSI_VSYNC 0x020E01DC *IOMUXC_SW_PAD_CTL_PAD_CSI_VSYNC 0x020E0468 *GPIO4_GD 0x020A8000 *GPIO4_GDIR 0x020A8004 */ /*添加led节点*/ rgb_led{ #address-cells = <1>; #size-cells = <1>; compatible = "fire,rgb_led"; /*红灯节点*/ ranges; rgb_led_red@0x020C406C{ reg = <0x020C406C 0x00000004 0x020E006C 0x00000004 0x020E02F8 0x00000004 0x0209C000 0x00000004 0x0209C004 0x00000004>; status = "okay"; }; /*绿灯节点*/ rgb_led_green@0x020C4074{ reg = <0x020C4074 0x00000004 0x020E01E0 0x00000004 0x020E046C 0x00000004 0x020A8000 0x00000004 0x020A8004 0x00000004>; status = "okay"; }; /*蓝灯节点*/ rgb_led_blue@0x020C4074{ reg = <0x020C4074 0x00000004 0x020E01DC 0x00000004 0x020E0468 0x00000004 0x020A8000 0x00000004 0x020A8004 0x00000004>; status = "okay"; }; };
reg属性内存映射
of_iomap()函数将reg属性值的物理地址转化为虚拟地址
void __iomem *of_iomap(struct device_node *np, int index)
参数:
- np:device_node表示的节点
- index:通常情况下reg属性包含多段,index 用于指定映射那一段,标号从0开始。
代码示例
以野火设备驱动模型代码为例
dts_led.c
#include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/uaccess.h> #include <linux/string.h> #include <linux/types.h> #include <linux/kernel.h> #include <linux/delay.h> #include <linux/ide.h> #include <linux/errno.h> #include <linux/gpio.h> #include <asm/mach/map.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_gpio.h> #include <asm/io.h> #include <linux/device.h> #include <linux/platform_device.h> /*------------------字符设备内容----------------------*/ #define DEV_NAME "rgb_led" #define DEV_CNT (1) /*定义 led 资源结构体,保存获取得到的节点信息以及转换后的虚拟寄存器地址*/ struct led_resource { struct device_node *device_node; //rgb_led_red的设备树节点 void __iomem *virtual_CCM_CCGR; void __iomem *virtual_IOMUXC_SW_MUX_CTL_PAD; void __iomem *virtual_IOMUXC_SW_PAD_CTL_PAD; void __iomem *virtual_DR; void __iomem *virtual_GDIR; }; static dev_t led_devno; //定义字符设备的设备号 static struct cdev led_chr_dev; //定义字符设备结构体chr_dev struct class *class_led; //保存创建的类 struct device *device; // 保存创建的设备 struct device_node *rgb_led_device_node; //rgb_led的设备树节点结构体 /*定义 R G B 三个灯的led_resource 结构体,保存获取得到的节点信息*/ struct led_resource led_red; struct led_resource led_green; struct led_resource led_blue; /*字符设备操作函数集,open函数*/ static int led_chr_dev_open(struct inode *inode, struct file *filp) { printk("\n open form driver \n"); return 0; } /*字符设备操作函数集,write函数*/ static ssize_t led_chr_dev_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt) { int ret,error; unsigned int register_data = 0; //暂存读取得到的寄存器数据 unsigned char receive_data[10]; //用于保存接收到的数据 unsigned int write_data; //用于保存接收到的数据 if(cnt>10) cnt =10; error = copy_from_user(receive_data, buf, cnt); if (error < 0) { return -1; } ret = kstrtoint(receive_data, 16, &write_data); if (ret) { return -1; } /*设置 GPIO1_04 输出电平*/ if (write_data & 0x04) { register_data = ioread32(led_red.virtual_DR); register_data &= ~(0x01 << 4); iowrite32(register_data, led_red.virtual_DR); // GPIO1_04引脚输出低电平,红灯亮 } else { register_data = ioread32(led_red.virtual_DR); register_data |= (0x01 << 4); iowrite32(register_data, led_red.virtual_DR); // GPIO1_04引脚输出高电平,红灯灭 } /*设置 GPIO4_20 输出电平*/ if (write_data & 0x02) { register_data = ioread32(led_green.virtual_DR); register_data &= ~(0x01 << 20); iowrite32(register_data, led_green.virtual_DR); // GPIO4_20引脚输出低电平,绿灯亮 } else { register_data = ioread32(led_green.virtual_DR); register_data |= (0x01 << 20); iowrite32(register_data, led_green.virtual_DR); // GPIO4_20引脚输出高电平,绿灯灭 } /*设置 GPIO4_19 输出电平*/ if (write_data & 0x01) { register_data = ioread32(led_blue.virtual_DR); register_data &= ~(0x01 << 19); iowrite32(register_data, led_blue.virtual_DR); //GPIO4_19引脚输出低电平,蓝灯亮 } else { register_data = ioread32(led_blue.virtual_DR); register_data |= (0x01 << 19); iowrite32(register_data, led_blue.virtual_DR); //GPIO4_19引脚输出高电平,蓝灯灭 } return cnt; } /*字符设备操作函数集*/ static struct file_operations led_chr_dev_fops = { .owner = THIS_MODULE, .open = led_chr_dev_open, .write = led_chr_dev_write, }; /*----------------平台驱动函数集-----------------*/ static int led_probe(struct platform_device *pdv) { int ret = -1; //保存错误状态码 unsigned int register_data = 0; printk(KERN_ALERT "\t match successed \n"); /*获取rgb_led的设备树节点*/ rgb_led_device_node = of_find_node_by_path("/rgb_led"); if (rgb_led_device_node == NULL) { printk(KERN_ERR "\t get rgb_led failed! \n"); return -1; } /*获取rgb_led节点的红灯子节点*/ led_red.device_node = of_find_node_by_name(rgb_led_device_node,"rgb_led_red"); if (led_red.device_node == NULL) { printk(KERN_ERR "\n get rgb_led_red_device_node failed ! \n"); return -1; } /*获取 reg 属性并转化为虚拟地址*/ led_red.virtual_CCM_CCGR = of_iomap(led_red.device_node, 0); led_red.virtual_IOMUXC_SW_MUX_CTL_PAD = of_iomap(led_red.device_node, 1); led_red.virtual_IOMUXC_SW_PAD_CTL_PAD = of_iomap(led_red.device_node, 2); led_red.virtual_DR = of_iomap(led_red.device_node, 3); led_red.virtual_GDIR = of_iomap(led_red.device_node, 4); /*初始化红灯*/ register_data = ioread32(led_red.virtual_CCM_CCGR); register_data |= (0x03 << 26); iowrite32(register_data, led_red.virtual_CCM_CCGR); //开启时钟 register_data = ioread32(led_red.virtual_IOMUXC_SW_MUX_CTL_PAD); register_data &= ~(0xf << 0); register_data |= (0x05 << 0); iowrite32(register_data, led_red.virtual_IOMUXC_SW_MUX_CTL_PAD); //设置复用功能 register_data = ioread32(led_red.virtual_IOMUXC_SW_PAD_CTL_PAD); register_data = (0x10B0); iowrite32(register_data, led_red.virtual_IOMUXC_SW_PAD_CTL_PAD); //设置PAD 属性 register_data = ioread32(led_red.virtual_GDIR); register_data |= (0x01 << 4); iowrite32(register_data, led_red.virtual_GDIR); //设置GPIO1_04 为输出模式 register_data = ioread32(led_red.virtual_DR); register_data |= (0x01 << 4); iowrite32(register_data, led_red.virtual_DR); //设置 GPIO1_04 默认输出高电平 /*获取rgb_led节点的绿灯子节点*/ led_green.device_node = of_find_node_by_name(rgb_led_device_node,"rgb_led_green"); if (led_green.device_node == NULL) { printk(KERN_ERR "\n get rgb_led_green_device_node failed ! \n"); return -1; } /*获取 reg 属性并转化为虚拟地址*/ led_green.virtual_CCM_CCGR = of_iomap(led_green.device_node, 0); led_green.virtual_IOMUXC_SW_MUX_CTL_PAD = of_iomap(led_green.device_node, 1); led_green.virtual_IOMUXC_SW_PAD_CTL_PAD = of_iomap(led_green.device_node, 2); led_green.virtual_DR = of_iomap(led_green.device_node, 3); led_green.virtual_GDIR = of_iomap(led_green.device_node, 4); /*初始化绿灯*/ register_data = ioread32(led_green.virtual_CCM_CCGR); register_data |= (0x03 << 12); iowrite32(register_data, led_green.virtual_CCM_CCGR); //开启时钟 register_data = ioread32(led_green.virtual_IOMUXC_SW_MUX_CTL_PAD); register_data &= ~(0xf << 0); register_data |= (0x05 << 0); iowrite32(register_data, led_green.virtual_IOMUXC_SW_MUX_CTL_PAD); //设置复用功能 register_data = ioread32(led_green.virtual_IOMUXC_SW_PAD_CTL_PAD); register_data = (0x10B0); iowrite32(register_data, led_green.virtual_IOMUXC_SW_PAD_CTL_PAD); //设置PAD 属性 register_data = ioread32(led_green.virtual_GDIR); register_data |= (0x01 << 20); iowrite32(register_data, led_green.virtual_GDIR); //设置GPIO4_IO20 为输出模式 register_data = ioread32(led_green.virtual_DR); register_data |= (0x01 << 20); iowrite32(register_data, led_green.virtual_DR); //设置 GPIO4_IO20 默认输出高电平 /*获取rgb_led节点的蓝灯子节点*/ led_blue.device_node = of_find_node_by_name(rgb_led_device_node,"rgb_led_blue"); if (led_blue.device_node == NULL) { printk(KERN_ERR "\n get rgb_led_blue_device_node failed ! \n"); return -1; } /*获取 reg 属性并转化为虚拟地址*/ led_blue.virtual_CCM_CCGR = of_iomap(led_blue.device_node, 0); led_blue.virtual_IOMUXC_SW_MUX_CTL_PAD = of_iomap(led_blue.device_node, 1); led_blue.virtual_IOMUXC_SW_PAD_CTL_PAD = of_iomap(led_blue.device_node, 2); led_blue.virtual_DR = of_iomap(led_blue.device_node, 3); led_blue.virtual_GDIR = of_iomap(led_blue.device_node, 4); /*初始化蓝灯*/ register_data = ioread32(led_blue.virtual_CCM_CCGR); register_data |= (0x03 << 12); iowrite32(register_data, led_blue.virtual_CCM_CCGR); //开启时钟 register_data = ioread32(led_blue.virtual_IOMUXC_SW_MUX_CTL_PAD); register_data &= ~(0xf << 0); register_data |= (0x05 << 0); iowrite32(register_data, led_blue.virtual_IOMUXC_SW_MUX_CTL_PAD); //设置复用功能 register_data = ioread32(led_blue.virtual_IOMUXC_SW_PAD_CTL_PAD); register_data = (0x10B0); iowrite32(register_data, led_blue.virtual_IOMUXC_SW_PAD_CTL_PAD); //设置PAD 属性 register_data = ioread32(led_blue.virtual_GDIR); register_data |= (0x01 << 19); iowrite32(register_data, led_blue.virtual_GDIR); //设置GPIO4_IO19 为输出模式 register_data = ioread32(led_blue.virtual_DR); register_data |= (0x01 << 19); iowrite32(register_data, led_blue.virtual_DR); //设置 GPIO4_IO19 默认输出高电平 /*---------------------注册 字符设备部分-----------------*/ //第一步 //采用动态分配的方式,获取设备编号,次设备号为0, //设备名称为rgb-leds,可通过命令cat /proc/devices查看 //DEV_CNT为1,当前只申请一个设备编号 ret = alloc_chrdev_region(&led_devno, 0, DEV_CNT, DEV_NAME); if (ret < 0) { printk("fail to alloc led_devno\n"); goto alloc_err; } //第二步 //关联字符设备结构体cdev与文件操作结构体file_operations led_chr_dev.owner = THIS_MODULE; cdev_init(&led_chr_dev, &led_chr_dev_fops); //第三步 //添加设备至cdev_map散列表中 ret = cdev_add(&led_chr_dev, led_devno, DEV_CNT); if (ret < 0) { printk("fail to add cdev\n"); goto add_err; } //第四步 /*创建类 */ class_led = class_create(THIS_MODULE, DEV_NAME); /*创建设备*/ device = device_create(class_led, NULL, led_devno, NULL, DEV_NAME); return 0; add_err: //添加设备失败时,需要注销设备号 unregister_chrdev_region(led_devno, DEV_CNT); printk("\n error! \n"); alloc_err: return -1; } static const struct of_device_id rgb_led[] = { {.compatible = "fire,rgb_led"}, {/* sentinel */}}; /*定义平台设备结构体*/ struct platform_driver led_platform_driver = { .probe = led_probe, .driver = { .name = "rgb-leds-platform", .owner = THIS_MODULE, .of_match_table = rgb_led, }}; /* *驱动初始化函数 */ static int __init led_platform_driver_init(void) { int DriverState; DriverState = platform_driver_register(&led_platform_driver); printk(KERN_ALERT "\tDriverState is %d\n", DriverState); return 0; } /* *驱动注销函数 */ static void __exit led_platform_driver_exit(void) { /*取消物理地址映射到虚拟地址*/ iounmap(led_green.virtual_CCM_CCGR); iounmap(led_green.virtual_IOMUXC_SW_MUX_CTL_PAD); iounmap(led_green.virtual_IOMUXC_SW_PAD_CTL_PAD); iounmap(led_green.virtual_DR); iounmap(led_green.virtual_GDIR); iounmap(led_red.virtual_CCM_CCGR); iounmap(led_red.virtual_IOMUXC_SW_MUX_CTL_PAD); iounmap(led_red.virtual_IOMUXC_SW_PAD_CTL_PAD); iounmap(led_red.virtual_DR); iounmap(led_red.virtual_GDIR); iounmap(led_blue.virtual_CCM_CCGR); iounmap(led_blue.virtual_IOMUXC_SW_MUX_CTL_PAD); iounmap(led_blue.virtual_IOMUXC_SW_PAD_CTL_PAD); iounmap(led_blue.virtual_DR); iounmap(led_blue.virtual_GDIR); /*删除设备*/ device_destroy(class_led, led_devno); //清除设备 class_destroy(class_led); //清除类 cdev_del(&led_chr_dev); //清除设备号 unregister_chrdev_region(led_devno, DEV_CNT); //取消注册字符设备 /*注销字符设备*/ platform_driver_unregister(&led_platform_driver); printk(KERN_ALERT "led_platform_driver exit!\n"); } module_init(led_platform_driver_init); module_exit(led_platform_driver_exit); MODULE_LICENSE("GPL"); /**/
总结
第三点get_dts_info.c仅为获取DTS属性信息演示代码。
这篇关于Linux字符设备驱动详解六(设备树实现RGB灯驱动)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-12如何创建可引导的 ESXi USB 安装介质 (macOS, Linux, Windows)
- 2024-11-08linux的 vi编辑器中搜索关键字有哪些常用的命令和技巧?-icode9专业技术文章分享
- 2024-11-08在 Linux 的 vi 或 vim 编辑器中什么命令可以直接跳到文件的结尾?-icode9专业技术文章分享
- 2024-10-22原生鸿蒙操作系统HarmonyOS NEXT(HarmonyOS 5)正式发布
- 2024-10-18操作系统入门教程:新手必看的基本操作指南
- 2024-10-18初学者必看:操作系统入门全攻略
- 2024-10-17操作系统入门教程:轻松掌握操作系统基础知识
- 2024-09-11Linux部署Scrapy学习:入门级指南
- 2024-09-11Linux部署Scrapy:入门级指南
- 2024-08-21【Linux】分区向左扩容的方法