1、修改设备树文件
在根节点/创建一个LED的子节点
2、驱动编写
(1)入口函数
static int __init led_init(void) { u32 val=0; int ret; u32 regdata[14]; const char *str; struct property *proper; /* 获取设备树中的属性数据 */ //1、获取根节点下的设备节点 muggle_led dtsled.nd = of_find_node_by_path("/muggle_led"); //判断获得的节点是否正确 if(dtsled.nd == NULL) { printk("muggle-led node can not found!\r\n"); return -EINVAL; } else { printk("muggle-led node has been found!\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 read success: %s\r\n", str); } //4、获取reg属性内容 ret = of_property_read_u32_array(dtsled.nd, "reg", regdata, 10); if(ret < 0 ) { printk("reg 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 //寄存器地址映射 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); //映射reg的数据 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 //使能GPIO1时钟 val = readl(IMX6U_CCM_CCGR1); //读取数值 val &= ~(3<<26); //引脚清0 val |= (3<<26); //设置新值 writel(val, IMX6U_CCM_CCGR1); //写入数值 //设置GPIO_IO03的复用功能,将其复用为GPIO_IO03最后设置IO属性 writel(5, SW_MUX_GPIO1_IO03); writel(5, SW_PAD_GPIO1_IO03); //4、设置GPIO_GDIR输出功能 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.class); } return 0; }
(1.1)是遍历设备树的根节点,获取compatible、status、reg属性,获取到reg中引脚的地址数据,然后of_iomap映射备用。
(1.2)使能GPIO引脚,写入数据。
(1.3)注册字符设备驱动:创建设备号、初始化cdev、添加cdev时加入dtsled_fops结构体、创建一个类最后创建设备
(1.4)编写dtsled_fops中open、read、write和release函数
(1.4.1)open函数主要实现私有数据的设置
(1.4.2)write函数从用户空间获取数据,操作LED关停
(2)出口函数
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); }