| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306 |
- #include <linux/types.h>
- #include <linux/kernel.h>
- #include <linux/delay.h>
- #include <linux/ide.h>
- #include <linux/init.h>
- #include <linux/module.h>
- #include <linux/errno.h>
- #include <linux/gpio.h>
- #include <linux/cdev.h>
- #include <linux/device.h>
- #include <linux/of.h>
- #include <linux/of_address.h>
- #include <asm/mach/map.h>
- #include <asm/uaccess.h>
- #include <asm/io.h>
- /***************************************************************
- Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.
- 文件名 : dtsled.c
- 作者 : 左忠凯
- 版本 : V1.0
- 描述 : LED驱动文件。
- 其他 : 无
- 论坛 : www.openedv.com
- 日志 : 初版V1.0 2019/7/9 左忠凯创建
- ***************************************************************/
- #define DTSLED_CNT 1 /* 设备号个数 */
- #define DTSLED_NAME "dtsled" /* 名字 */
- #define LEDOFF 0 /* 关灯 */
- #define LEDON 1 /* 开灯 */
- /* 映射后的寄存器虚拟地址指针 */
- static void __iomem *IMX6U_CCM_CCGR1;
- static void __iomem *SW_MUX_GPIO1_IO03;
- static void __iomem *SW_PAD_GPIO1_IO03;
- static void __iomem *GPIO1_DR;
- static void __iomem *GPIO1_GDIR;
- /* dtsled设备结构体 */
- struct dtsled_dev{
- dev_t devid; /* 设备号 */
- struct cdev cdev; /* cdev */
- struct class *class; /* 类 */
- struct device *device; /* 设备 */
- int major; /* 主设备号 */
- int minor; /* 次设备号 */
- struct device_node *nd; /* 设备节点 */
- };
- struct dtsled_dev dtsled; /* led设备 */
- /*
- * @description : LED打开/关闭
- * @param - sta : LEDON(0) 打开LED,LEDOFF(1) 关闭LED
- * @return : 无
- */
- void led_switch(u8 sta)
- {
- u32 val = 0;
- if(sta == LEDON) {
- val = readl(GPIO1_DR);
- val &= ~(1 << 3);
- writel(val, GPIO1_DR);
- }else if(sta == LEDOFF) {
- val = readl(GPIO1_DR);
- val|= (1 << 3);
- writel(val, GPIO1_DR);
- }
- }
- /*
- * @description : 打开设备
- * @param - inode : 传递给驱动的inode
- * @param - filp : 设备文件,file结构体有个叫做private_data的成员变量
- * 一般在open的时候将private_data指向设备结构体。
- * @return : 0 成功;其他 失败
- */
- static int led_open(struct inode *inode, struct file *filp)
- {
- filp->private_data = &dtsled; /* 设置私有数据 */
- return 0;
- }
- /*
- * @description : 从设备读取数据
- * @param - filp : 要打开的设备文件(文件描述符)
- * @param - buf : 返回给用户空间的数据缓冲区
- * @param - cnt : 要读取的数据长度
- * @param - offt : 相对于文件首地址的偏移
- * @return : 读取的字节数,如果为负值,表示读取失败
- */
- static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
- {
- return 0;
- }
- /*
- * @description : 向设备写数据
- * @param - filp : 设备文件,表示打开的文件描述符
- * @param - buf : 要写给设备写入的数据
- * @param - cnt : 要写入的数据长度
- * @param - offt : 相对于文件首地址的偏移
- * @return : 写入的字节数,如果为负值,表示写入失败
- */
- static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
- {
- int retvalue;
- unsigned char databuf[1];
- unsigned char ledstat;
- retvalue = copy_from_user(databuf, buf, cnt);
- 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;
- }
- /*
- * @description : 关闭/释放设备
- * @param - filp : 要关闭的设备文件(文件描述符)
- * @return : 0 成功;其他 失败
- */
- static int led_release(struct inode *inode, struct file *filp)
- {
- return 0;
- }
- /* 设备操作函数 */
- static struct file_operations dtsled_fops = {
- .owner = THIS_MODULE,
- .open = led_open,
- .read = led_read,
- .write = led_write,
- .release = led_release,
- };
- /*
- * @description : 驱动出口函数
- * @param : 无
- * @return : 无
- */
- static int __init led_init(void)
- {
- u32 val = 0;
- int ret;
- u32 regdata[14];
- const char *str;
- struct property *proper;
- /* 获取设备树中的属性数据 */
- /* 1、获取设备节点:alphaled */
- dtsled.nd = of_find_node_by_name("my_backlight");
- if(dtsled.nd == NULL) {
- printk("alphaled node nost find!\r\n");
- return -EINVAL;
- } else {
- printk("alphaled node find!\r\n");
- }
- /* 2、获取compatible属性内容 */
- proper = of_find_property(dtsled.nd, "compatible", NULL);
- if(proper == NULL) {
- printk("compatible property find failed\r\n");
- } else {
- printk("compatible = %s\r\n", (char*)proper->value);
- }
- /* 3、获取status属性内容 */
- ret = of_property_read_string(dtsled.nd, "status", &str);
- if(ret < 0){
- printk("status read failed!\r\n");
- } else {
- printk("status = %s\r\n",str);
- }
- /* 4、获取reg属性内容 */
- ret = of_property_read_u32_array(dtsled.nd, "reg", regdata, 10);
- if(ret < 0) {
- printk("reg property read failed!\r\n");
- } else {
- u8 i = 0;
- printk("reg data:\r\n");
- for(i = 0; i < 10; i++)
- printk("%#X ", regdata[i]);
- printk("\r\n");
- }
- /* 初始化LED */
- #if 0
- /* 1、寄存器地址映射 */
- IMX6U_CCM_CCGR1 = ioremap(regdata[0], regdata[1]);
- SW_MUX_GPIO1_IO03 = ioremap(regdata[2], regdata[3]);
- SW_PAD_GPIO1_IO03 = ioremap(regdata[4], regdata[5]);
- GPIO1_DR = ioremap(regdata[6], regdata[7]);
- GPIO1_GDIR = ioremap(regdata[8], regdata[9]);
- #else
- IMX6U_CCM_CCGR1 = of_iomap(dtsled.nd, 0);
- SW_MUX_GPIO1_IO03 = of_iomap(dtsled.nd, 1);
- SW_PAD_GPIO1_IO03 = of_iomap(dtsled.nd, 2);
- GPIO1_DR = of_iomap(dtsled.nd, 3);
- GPIO1_GDIR = of_iomap(dtsled.nd, 4);
- #endif
- /* 2、使能GPIO1时钟 */
- val = readl(IMX6U_CCM_CCGR1);
- val &= ~(3 << 26); /* 清楚以前的设置 */
- val |= (3 << 26); /* 设置新值 */
- writel(val, IMX6U_CCM_CCGR1);
- /* 3、设置GPIO1_IO03的复用功能,将其复用为
- * GPIO1_IO03,最后设置IO属性。
- */
- writel(5, SW_MUX_GPIO1_IO03);
-
- /*寄存器SW_PAD_GPIO1_IO03设置IO属性
- *bit 16:0 HYS关闭
- *bit [15:14]: 00 默认下拉
- *bit [13]: 0 kepper功能
- *bit [12]: 1 pull/keeper使能
- *bit [11]: 0 关闭开路输出
- *bit [7:6]: 10 速度100Mhz
- *bit [5:3]: 110 R0/6驱动能力
- *bit [0]: 0 低转换率
- */
- writel(0x10B0, SW_PAD_GPIO1_IO03);
- /* 4、设置GPIO1_IO03为输出功能 */
- val = readl(GPIO1_GDIR);
- val &= ~(1 << 3); /* 清除以前的设置 */
- val |= (1 << 3); /* 设置为输出 */
- writel(val, GPIO1_GDIR);
- /* 5、默认关闭LED */
- val = readl(GPIO1_DR);
- val |= (1 << 3);
- writel(val, GPIO1_DR);
- /* 注册字符设备驱动 */
- /* 1、创建设备号 */
- if (dtsled.major) { /* 定义了设备号 */
- dtsled.devid = MKDEV(dtsled.major, 0);
- register_chrdev_region(dtsled.devid, DTSLED_CNT, DTSLED_NAME);
- } else { /* 没有定义设备号 */
- alloc_chrdev_region(&dtsled.devid, 0, DTSLED_CNT, DTSLED_NAME); /* 申请设备号 */
- dtsled.major = MAJOR(dtsled.devid); /* 获取分配号的主设备号 */
- dtsled.minor = MINOR(dtsled.devid); /* 获取分配号的次设备号 */
- }
- printk("dtsled major=%d,minor=%d\r\n",dtsled.major, dtsled.minor);
-
- /* 2、初始化cdev */
- dtsled.cdev.owner = THIS_MODULE;
- cdev_init(&dtsled.cdev, &dtsled_fops);
-
- /* 3、添加一个cdev */
- cdev_add(&dtsled.cdev, dtsled.devid, DTSLED_CNT);
- /* 4、创建类 */
- dtsled.class = class_create(THIS_MODULE, DTSLED_NAME);
- if (IS_ERR(dtsled.class)) {
- return PTR_ERR(dtsled.class);
- }
- /* 5、创建设备 */
- dtsled.device = device_create(dtsled.class, NULL, dtsled.devid, NULL, DTSLED_NAME);
- if (IS_ERR(dtsled.device)) {
- return PTR_ERR(dtsled.device);
- }
-
- return 0;
- }
- /*
- * @description : 驱动出口函数
- * @param : 无
- * @return : 无
- */
- static void __exit led_exit(void)
- {
- /* 取消映射 */
- iounmap(IMX6U_CCM_CCGR1);
- iounmap(SW_MUX_GPIO1_IO03);
- iounmap(SW_PAD_GPIO1_IO03);
- iounmap(GPIO1_DR);
- iounmap(GPIO1_GDIR);
- /* 注销字符设备驱动 */
- cdev_del(&dtsled.cdev);/* 删除cdev */
- unregister_chrdev_region(dtsled.devid, DTSLED_CNT); /* 注销设备号 */
- device_destroy(dtsled.class, dtsled.devid);
- class_destroy(dtsled.class);
- }
- module_init(led_init);
- module_exit(led_exit);
- MODULE_LICENSE("GPL");
- MODULE_AUTHOR("zuozhongkai");
|