Rk3568驱动开发_设备树点亮LED_11
代码:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>#define DTSLED_CNT 1 // 设备号个数
#define DTSLED_NAME "dtsled" // 名字
#define LEDOFF 0 /* 关灯 */
#define LEDON 1 /* 开灯 *//* 映射后的寄存器虚拟地址指针 */
static void __iomem *PMU_GRF_GPIO0C_IOMUX_L_PI;
static void __iomem *PMU_GRF_GPIO0C_DS_0_PI;
static void __iomem *GPIO0_SWPORT_DR_H_PI;
static void __iomem *GPIO0_SWPORT_DDR_H_PI;void led_unmap(void){/* 取消映射 */iounmap(PMU_GRF_GPIO0C_IOMUX_L_PI);iounmap(PMU_GRF_GPIO0C_DS_0_PI);iounmap(GPIO0_SWPORT_DR_H_PI);iounmap(GPIO0_SWPORT_DDR_H_PI);
}/*dtsled设备结构体*/struct dtsled_dev{dev_t devid; // 设备号struct cdev cdev; // 字符设备struct class* class; // 类struct device* device; // 设备int major; // 主设备号int minor; // 次设备号struct device_node *nd; // 设备节点
};struct dtsled_dev dtsled; // led设备void led_switch(u8 sta)
{u32 val = 0;if(sta == LEDON) {val = readl(GPIO0_SWPORT_DR_H_PI);val &= ~(0X1 << 0); /* bit0 清零*/val |= ((0X1 << 16) | (0X1 << 0)); /* bit16 置1,允许写bit0,bit0,高电平*/writel(val, GPIO0_SWPORT_DR_H_PI);}else if(sta == LEDOFF) { val = readl(GPIO0_SWPORT_DR_H_PI);val &= ~(0X1 << 0); /* bit0 清零*/val |= ((0X1 << 16) | (0X0 << 0)); /* bit16 置1,允许写bit0,bit0,低电平 */writel(val, GPIO0_SWPORT_DR_H_PI);}
}static int dtsled_open(struct inode* inode, struct file* filp){filp->private_data = &dtsled;return 0;
}
static int dtsled_release(struct inode* inode, struct file* filp){//struct dtsled_dev* dev = (struct dtsled_dev* )filp->private_data;return 0;
}
static ssize_t dtsled_write(struct file* filp, const char __user* buf, size_t count, loff_t* ppos){//struct dtsled_dev* dev = (struct dtsled_dev* )filp->private_data;int retvalue;unsigned char databuf[1];unsigned char ledstat;retvalue = copy_from_user(databuf, buf, count);if(retvalue < 0) {printk("kernel write failed!\r\n");return -EFAULT;}ledstat = databuf[0]; /* 获取状态值 */if(ledstat == LEDON) { led_switch(LEDON); /* 打开LED灯 */} else if(ledstat == LEDOFF) {led_switch(LEDOFF); /* 关闭LED灯 */}return 0;}// dtsled 字符设备操作集
static const struct file_operations dtsled_fops = {.owner = THIS_MODULE,.write = dtsled_write,.open = dtsled_open,.release = dtsled_release,
};/*入口函数*/static int __init dtsled_init(void){int ret = 0;const char* str;u32 regdata[16];int i = 0;unsigned int val = 0;// 注册字符设备,新的开发方式dtsled.major = 0; // 设备号由内核分配if(dtsled.major){ // 定义了设备号dtsled.devid = MKDEV(dtsled.major, 0);ret = register_chrdev_region(dtsled.devid, DTSLED_CNT, DTSLED_NAME);}else{ // 没有给定设备号ret = alloc_chrdev_region(&dtsled.devid, 0, DTSLED_CNT, DTSLED_NAME);dtsled.major = MAJOR(dtsled.devid);dtsled.minor = MINOR(dtsled.minor);}if(ret < 0){printk("dtsled init error \r\n");return -1;}// 添加字符设备dtsled.cdev.owner = THIS_MODULE;cdev_init(&dtsled.cdev, &dtsled_fops); // 初始化ret = cdev_add(&dtsled.cdev, dtsled.devid, DTSLED_CNT); // 添加到内核if(ret < 0){printk("dtsled add failed \r\n");unregister_chrdev_region(dtsled.devid, DTSLED_CNT); // 释放设备号return -1;}// 自动创建设备节点dtsled.class = class_create(THIS_MODULE, DTSLED_NAME); // 创建类if(IS_ERR(dtsled.class)){ret= PTR_ERR(dtsled.class);cdev_del(&dtsled.cdev); // 删除设备unregister_chrdev_region(dtsled.devid, DTSLED_CNT); // 释放设备号return -1;}dtsled.device = device_create(dtsled.class, NULL, dtsled.devid, NULL, DTSLED_NAME);if(IS_ERR(dtsled.device)){ret = PTR_ERR(dtsled.device);cdev_del(&dtsled.cdev); // 删除设备unregister_chrdev_region(dtsled.devid, DTSLED_CNT); // 释放设备号class_destroy(dtsled.class); // 删除类return -1;}/*获取设备树属性函数*/dtsled.nd = of_find_node_by_path("/alphaled");if(dtsled.nd == NULL){ret = -EINVAL;printk("find node by path error\r\n");return -1;}ret = of_property_read_string(dtsled.nd, "status", &str);if(ret < 0){printk("get status failed!\r\n");}else{printk("status= %s \r\n", str);}ret = of_property_read_string(dtsled.nd, "compatible", &str);if(ret < 0){printk("get compatible failed!\r\n");}else{printk("compatible= %s \r\n", str);}ret = of_property_read_u32_array(dtsled.nd, "reg", regdata, 16);if(ret < 0){printk("read reg error \r\n");cdev_del(&dtsled.cdev); // 删除字符设备unregister_chrdev_region(dtsled.devid, DTSLED_CNT); // 删除设备号 device_destroy(dtsled.class, dtsled.devid); // 删除设备 (设备用到类)class_destroy(dtsled.class); // 删除类return -1;}else{printk("reg data: \r\n");for(i = 0; i < 16; i ++){printk("%#X ", regdata[i]);}printk("\r\n");}/* LED灯初始化 */ // 内存映射PMU_GRF_GPIO0C_IOMUX_L_PI = of_iomap(dtsled.nd, 0);PMU_GRF_GPIO0C_DS_0_PI = of_iomap(dtsled.nd, 1);GPIO0_SWPORT_DR_H_PI = of_iomap(dtsled.nd, 2);GPIO0_SWPORT_DDR_H_PI = of_iomap(dtsled.nd, 3);// 初始化io/* 2、设置GPIO0_C0为GPIO功能。*/val = readl(PMU_GRF_GPIO0C_IOMUX_L_PI);val &= ~(0X7 << 0); /* bit2:0,清零 */val |= ((0X7 << 16) | (0X0 << 0)); /* bit18:16 置1,允许写bit2:0,bit2:0:0,用作GPIO0_C0 */writel(val, PMU_GRF_GPIO0C_IOMUX_L_PI);/* 3、设置GPIO0_C0驱动能力为level5 */val = readl(PMU_GRF_GPIO0C_DS_0_PI);val &= ~(0X3F << 0); /* bit5:0清零*/val |= ((0X3F << 16) | (0X3F << 0)); /* bit21:16 置1,允许写bit5:0,bit5:0:0,用作GPIO0_C0 */writel(val, PMU_GRF_GPIO0C_DS_0_PI);/* 4、设置GPIO0_C0为输出 */val = readl(GPIO0_SWPORT_DDR_H_PI);val &= ~(0X1 << 0); /* bit0 清零*/val |= ((0X1 << 16) | (0X1 << 0)); /* bit16 置1,允许写bit0,bit0,高电平 */writel(val, GPIO0_SWPORT_DDR_H_PI);/* 5、设置GPIO0_C0为低电平,关闭LED灯。*/val = readl(GPIO0_SWPORT_DR_H_PI);val &= ~(0X1 << 0); /* bit0 清零*/val |= ((0X1 << 16) | (0X0 << 0)); /* bit16 置1,允许写bit0,bit0,低电平 */writel(val, GPIO0_SWPORT_DR_H_PI);return 0;
}/*出口*/static void __exit dtsled_exit(void){led_switch(LEDOFF);led_unmap(); // 取消地址映射cdev_del(&dtsled.cdev); // 删除字符设备unregister_chrdev_region(dtsled.devid, DTSLED_CNT); // 删除设备号 device_destroy(dtsled.class, dtsled.devid); // 删除设备 (设备用到类)class_destroy(dtsled.class); // 删除类
}/* 注册驱动和卸载驱动 */module_init(dtsled_init);module_exit(dtsled_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("Narnat");
代码中通过读取alphaled节点数据,alphaled中存放的是led灯引脚地址,这样led灯引脚发生变化,只需要更改设备树中地址的具体值,无需更改代码
结果: