1. 环境:
1.1 开发板:正点原子 I.MX6U ALPHA V2.2
1.2 开发PC:Ubuntu20.04
1.3 U-boot:uboot-imx-rel_imx_4.1.15_2.1.0_ga.tar.bz2
1.4 LInux内核:linux-imx-rel_imx_4.1.15_2.1.0_ga.tar.bz2
1.5 rootfs:busybox-1.29.0.tar.bz2制作
1.6 交叉编译工具链:gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf.tar.xz
2. 硬件控制说明
2.1 由GPIO1 PIN3控制,高---熄灭, 低---点亮
3. 驱动代码
3.1 新建led.h头文件,添加如下代码
1 #ifndef __LED_H 2 #define __LED_H 3 #include <linux/cdev.h> 4 5 //GPIO寄存器地址,参考"i.MX 6ULL Applications Processor Reference Manual" 6 #define LED_GPIO1_3_BASE 0x0209c000 7 #define GPIO_DR 0x0 8 #define GPIO_GDIR 0x4 9 #define GPIO_PSR 0x8 //本驱动未使用到 10 #define GPIO_ICR1 0xc //本驱动未使用到 11 #define GPIO_ICR2 0x10 //本驱动未使用到 12 #define GPIO_IMR 0x14 //本驱动未使用到 13 #define GPIO_ISR 0x18 //本驱动未使用到 14 #define GPIO_EDGE_SEL 0x1c //本驱动未使用到 15 16 #define LED_GPIO1_3_MUX 0X020E0068 //GPIO复用功能选择寄存器地址 17 #define LED_GPIO1_3_PAD 0X020E02F4 //GPIO驱动方式选择寄存器地址 18 #define LED_GPIO1_3_CCM 0X020C406C //GPIO时钟寄存器地址 19 20 //led控制 21 #define LED_ON 1 22 #define LED_OFF 0 23 #define LED_MAJOR 249 //用于静态设备号申请 24 25 26 //自定义一个结构体,将一些需要的变量放在其中,以便管理 27 struct led_device 28 { 29 struct class *led_class; 30 struct device *led_device; 31 struct cdev led_cdev; 32 dev_t devno; 33 int major; //用于保存主设备号 34 void __iomem *led_dr; //保存GPIO数据寄存器内存映射后的地址 35 void __iomem *led_gdir; //保存GPIO输入输出方向选择寄存器内存映射后的地址 36 void __iomem *led_pd; //保存GPIO驱动方式选择寄存器内存映射后的地址 37 void __iomem *led_mux; //保存GPIO复用功能选择寄存器内存映射后的地址 38 void __iomem *led_ccm; //保存GPIO时钟寄存器内存映射后的地址 39 }; 40 41 42 #endifView Code
3.2 新建led_dev_platform.c文件,添加平台设备代码
1 #include <linux/init.h> 2 #include <linux/module.h> 3 #include <linux/ioport.h> 4 #include <linux/platform_device.h> 5 #include "led.h" 6 7 //填充平台资源结构体 8 struct resource led_resource[] = { 9 [0] = { //GPIO数据寄存器 10 .start = LED_GPIO1_3_BASE + GPIO_DR, 11 .end = LED_GPIO1_3_BASE + GPIO_DR + 4 -1, 12 .flags = IORESOURCE_MEM 13 }, 14 15 [1] = { //GPIO方向寄存器 16 .start = LED_GPIO1_3_BASE + GPIO_GDIR, 17 .end = LED_GPIO1_3_BASE + GPIO_GDIR + 4 -1, 18 .flags = IORESOURCE_MEM 19 }, 20 21 [2] = { //GPIO复用功能寄存器 22 .start = LED_GPIO1_3_MUX, 23 .end = LED_GPIO1_3_MUX + 4 -1, 24 .flags = IORESOURCE_MEM 25 }, 26 27 [3] = { //GPIO驱动方式寄存器 28 .start = LED_GPIO1_3_PAD, 29 .end = LED_GPIO1_3_PAD + 4 -1, 30 .flags = IORESOURCE_MEM 31 }, 32 33 [4] = { //GPIO时钟寄存器 34 .start = LED_GPIO1_3_CCM, 35 .end = LED_GPIO1_3_CCM + 4 -1, 36 .flags = IORESOURCE_MEM 37 }, 38 39 }; 40 41 42 static void led_release(struct device *dev) 43 { 44 45 } 46 47 //填充平台结构体 48 struct platform_device led_gpio_device = { 49 .name = "led_gpio", 50 // .name = "imx6ul-led", 51 .id = 1, 52 .resource = led_resource, 53 .num_resources = ARRAY_SIZE(led_resource), 54 .dev = { 55 .release = led_release, 56 }, 57 }; 58 59 60 static int __init led_dev_platform_init(void) 61 { 62 //注册平台设备 63 platform_device_register(&led_gpio_device); 64 return 0; 65 } 66 67 68 static void __exit led_dev_platform_exit(void) 69 { 70 //注销平台设备 71 platform_device_unregister(&led_gpio_device); 72 } 73 74 75 module_init(led_dev_platform_init); 76 module_exit(led_dev_platform_exit); 77 MODULE_LICENSE("Dual BSD/GPL");View Code
3.3 新建led_drv_platform.c文件,添加平台驱动代码
1 #include <linux/init.h> 2 #include <linux/module.h> 3 #include <linux/fs.h> //文件操作函数,file_operarions 4 #include <linux/device.h> //设备申请函数,device_create 5 #include <linux/platform_device.h> 6 #include <linux/slab.h> //内核空间申请函数件,kmalloc 7 #include <asm/uaccess.h> //内核与用户空间消息拷贝函数,copy_to_usser & copy_from_user 8 #include <asm/io.h> //内存地址映射函数,ioremap 9 #include <linux/cdev.h> 10 #include "led.h" 11 12 13 static struct led_device *imx_led_gpio1_3; 14 15 //设备文件的open操作函数 16 static int led_open(struct inode *inode, struct file *file) 17 { 18 19 return 0; 20 } 21 22 //设备文件的close操作函数 23 static int led_close(struct inode *inode, struct file *file) 24 { 25 return 0; 26 } 27 28 //设备文件的read操作函数 29 static ssize_t led_read(struct file *file, char __user *buf, size_t size, loff_t *f_ops) 30 { 31 return 0; 32 } 33 34 //设备文件的write操作函数 35 static ssize_t led_write(struct file *file, const char __user *buf, size_t size, loff_t *f_ops) 36 { 37 int ret; 38 unsigned char cmd[1]; 39 u32 val = 0; 40 41 ret = copy_from_user(cmd, buf, size); 42 if(ret < 0) 43 { 44 printk("kernel write failed!\r\n"); 45 return -EFAULT; 46 } 47 48 49 if(*cmd == LED_ON) 50 { 51 val = readl(imx_led_gpio1_3->led_dr); 52 val &= ~(1 << 3); 53 writel(val, imx_led_gpio1_3->led_dr); 54 } 55 else if(*cmd == LED_OFF) 56 { 57 val = readl(imx_led_gpio1_3->led_dr); 58 val|= (1 << 3); 59 writel(val, imx_led_gpio1_3->led_dr); 60 } 61 else 62 { 63 printk("led command is invalid!\n"); 64 return -2; 65 } 66 67 return 0; 68 } 69 70 static long led_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 71 { 72 u32 val = 0; 73 switch (cmd) 74 { 75 case LED_OFF: 76 val = readl(imx_led_gpio1_3->led_dr); 77 val|= (1 << 3); 78 writel(val, imx_led_gpio1_3->led_dr); 79 break; 80 case LED_ON: 81 val = readl(imx_led_gpio1_3->led_dr); 82 val &= ~(1 << 3); 83 writel(val, imx_led_gpio1_3->led_dr); 84 break; 85 default: 86 val = readl(imx_led_gpio1_3->led_dr); 87 val|= (1 << 3); 88 writel(val, imx_led_gpio1_3->led_dr); 89 break; 90 } 91 92 93 return 0; 94 } 95 96 //填充file_operations结构体 97 static const struct file_operations led_fops = { 98 .owner = THIS_MODULE, 99 .open = led_open, 100 .release = led_close, 101 .write = led_write, 102 .read = led_read, 103 .unlocked_ioctl = led_ioctl, 104 }; 105 106 107 static int led_gpio_probe(struct platform_device *pdev) 108 { 109 int ret = -2; 110 int i; 111 u32 val = 0; 112 struct resource *res[5]; 113 int ressize[5]; 114 115 //为自定义机构提申请内核内存 116 imx_led_gpio1_3 = kmalloc(sizeof(struct led_device), GFP_KERNEL); 117 if(imx_led_gpio1_3 == NULL) 118 { 119 printk(KERN_ERR "kmalloc fail!\n"); 120 return -ENOMEM; 121 } 122 123 124 //ret = register_chrdev(LED_MAJOR, "led", &led_fops); 125 //如果使用动态申请,则必须使用cdev_init() & cdev_add()这两个函数;静态则不需要 126 ret = alloc_chrdev_region(&imx_led_gpio1_3->devno, 0, 1, "led"); 127 if(ret < 0) 128 { 129 printk(KERN_ERR "register major failed!\n"); 130 ret = -EINVAL; 131 goto err1; 132 } 133 134 135 imx_led_gpio1_3->major = MAJOR(imx_led_gpio1_3->devno); 136 imx_led_gpio1_3->led_cdev.owner = THIS_MODULE; 137 138 //dev_major = MAJOR(devno); 139 imx_led_gpio1_3->led_cdev.owner = THIS_MODULE; 140 141 //将设备与文件操作函数关联 142 cdev_init(&imx_led_gpio1_3->led_cdev, &led_fops); 143 144 //注册设备 145 cdev_add(&imx_led_gpio1_3->led_cdev,imx_led_gpio1_3->devno, 1); 146 147 //创建设备类 148 imx_led_gpio1_3->led_class = class_create(THIS_MODULE, "led_class"); 149 if(IS_ERR(imx_led_gpio1_3->led_class)) 150 { 151 printk(KERN_ERR "failed to create class!\n"); 152 ret = PTR_ERR(imx_led_gpio1_3->led_class); 153 goto err2; 154 } 155 156 157 //创建设备文件,此函数最后一个参数led,其实就是设备名,应用程序open打开的也是这个文件名 158 imx_led_gpio1_3->led_device = device_create(imx_led_gpio1_3->led_class, NULL, imx_led_gpio1_3->devno, NULL, "led"); 159 if(IS_ERR(imx_led_gpio1_3->led_device)) 160 { 161 printk(KERN_ERR "failed to create device!\n"); 162 ret = PTR_ERR(imx_led_gpio1_3->led_device); 163 goto err3; 164 } 165 166 //获取设备资源 167 for(i = 0; i < 5; i++) 168 { 169 res[i] = platform_get_resource(pdev, IORESOURCE_MEM, i); 170 if(!res[i]) 171 { 172 dev_err(&pdev->dev, "No MEM resource for always on \n"); 173 return -ENXIO; 174 } 175 ressize[i] = resource_size(res[i]); 176 } 177 178 179 //对物理地址进行内存映射,以便后面操作 180 imx_led_gpio1_3->led_dr = ioremap(res[0]->start, ressize[0]); 181 if(imx_led_gpio1_3->led_dr == NULL) 182 { 183 printk(KERN_ERR "led_dr ioremap fail!\n"); 184 ret = -ENOMEM; 185 goto err4; 186 } 187 188 imx_led_gpio1_3->led_gdir = ioremap(res[1]->start, ressize[1]); 189 if(imx_led_gpio1_3->led_gdir == NULL) 190 { 191 printk(KERN_ERR "led_gdir ioremap fail!\n"); 192 ret = -ENOMEM; 193 goto err4; 194 } 195 196 imx_led_gpio1_3->led_mux = ioremap(res[2]->start, ressize[2]); 197 if(imx_led_gpio1_3->led_mux == NULL) 198 { 199 printk(KERN_ERR "led_mux ioremap fail!\n"); 200 ret = -ENOMEM; 201 goto err4; 202 } 203 204 imx_led_gpio1_3->led_pd = ioremap(res[3]->start, ressize[3]); 205 if(imx_led_gpio1_3->led_pd == NULL) 206 { 207 printk(KERN_ERR "led_pd ioremap fail!\n"); 208 ret = -ENOMEM; 209 goto err4; 210 } 211 212 213 imx_led_gpio1_3->led_ccm = ioremap(res[4]->start, ressize[4]); 214 if(imx_led_gpio1_3->led_ccm == NULL) 215 { 216 printk(KERN_ERR "led_ccm ioremap fail!\n"); 217 ret = -ENOMEM; 218 goto err4; 219 } 220 221 // 使能GPIO1时钟 222 val = readl(imx_led_gpio1_3->led_ccm); 223 val &= ~(3 << 26); //清掉旧值 224 val |= (3 << 26); // 设置新值 225 writel(val, imx_led_gpio1_3->led_ccm); 226 227 // 将GPIO1_IO03复用功能设置为GPIO 228 writel(5, imx_led_gpio1_3->led_mux); 229 230 // 设置GPIO的电流驱动能力 231 writel(0x10B0, imx_led_gpio1_3->led_pd); 232 233 // 设置GPIO的输入输出方向 234 val = readl(imx_led_gpio1_3->led_gdir); 235 val &= ~(1 << 3); // 清除以前的设置 236 val |= (1 << 3); // 设置为输出 237 writel(val, imx_led_gpio1_3->led_gdir); 238 239 240 //设置LED的默认状态:LED熄灭 241 val = readl(imx_led_gpio1_3->led_dr); 242 val |= (1 << 3); 243 writel(val, imx_led_gpio1_3->led_dr); 244 245 return 0; 246 247 //注销设备节点 248 err4: 249 device_destroy(imx_led_gpio1_3->led_class, imx_led_gpio1_3->major); 250 251 //注销设备类 252 err3: 253 class_destroy(imx_led_gpio1_3->led_class); 254 255 //注销设备号 256 err2: 257 unregister_chrdev(imx_led_gpio1_3->major, "led"); 258 259 //释放由kmalloc申请的内存 260 err1: 261 kfree(imx_led_gpio1_3); 262 return ret; 263 264 } 265 266 267 static int led_gpio_remove(struct platform_device *pdev) 268 { 269 device_destroy(imx_led_gpio1_3->led_class, imx_led_gpio1_3->major); 270 class_destroy(imx_led_gpio1_3->led_class); 271 unregister_chrdev(imx_led_gpio1_3->major, "led"); 272 iounmap(imx_led_gpio1_3->led_dr); 273 iounmap(imx_led_gpio1_3->led_gdir); 274 iounmap(imx_led_gpio1_3->led_pd); 275 iounmap(imx_led_gpio1_3->led_mux); 276 iounmap(imx_led_gpio1_3->led_ccm); 277 kfree(imx_led_gpio1_3); 278 279 return 0; 280 } 281 282 struct platform_device_id led_ids[] = { 283 [0] = { 284 .name = "led_gpio", 285 .driver_data = 0, 286 }, 287 }; 288 289 struct platform_driver led_gpio_driver = { 290 .probe = led_gpio_probe, 291 .remove = led_gpio_remove, 292 .driver = { 293 .owner = THIS_MODULE, 294 .name = "led_gpio" 295 }, 296 .id_table = led_ids //当成员driver中的name成员不匹配时,从成员id_table中寻找 297 }; 298 299 static int __init led_init(void) 300 { 301 platform_driver_register(&led_gpio_driver); 302 return 0; 303 } 304 305 static void __exit led_exit(void) 306 { 307 platform_driver_unregister(&led_gpio_driver); 308 } 309 310 module_init(led_init); 311 module_exit(led_exit); 312 MODULE_LICENSE("Dual BSD/GPL");View Code
4. 测试代码
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <sys/types.h> 4 #include <sys/stat.h> 5 #include <fcntl.h> 6 7 #define LED_ON 1 8 #define LED_OFF 0 9 10 11 int main(int argc, char **argv) 12 { 13 int fd; 14 int buf = 0; 15 16 fd = open("/dev/led", O_RDWR); 17 18 printf("fd = %d\n", fd); 19 if(fd < 0) 20 { 21 perror("open fail!\n"); 22 23 exit(1); 24 } 25 26 #if 0 27 28 if(strcmp(argv[1], "on") == 0) 29 { 30 if(ioctl(fd, LED_ON) < 0) 31 perror("ioctrl fail:led on\n"); 32 else 33 printf("ioctl ok:led on\n"); 34 } 35 else if(strcmp(argv[1], "off") == 0) 36 { 37 if(ioctl(fd, LED_OFF) < 0) 38 perror("ioctrl fail:led off\n"); 39 else 40 printf("ioctl ok:led off\n"); 41 } 42 else 43 { 44 perror("command is invalid!\n"); 45 } 46 47 48 #else 49 if(strcmp(argv[1], "on") == 0) 50 { 51 buf = 1; 52 if(write(fd, &buf, sizeof(buf)) < 0) 53 { 54 perror("write fail:led on!\n"); 55 exit(1); 56 } 57 else 58 { 59 printf("write ok:led on!\n"); 60 } 61 } 62 else if(strcmp(argv[1], "off") == 0) 63 { 64 buf = 0; 65 if(write(fd, &buf, sizeof(buf)) < 0) 66 { 67 perror("write fail:led off!\n"); 68 exit(1); 69 } 70 else 71 { 72 printf("write ok:led off!\n"); 73 } 74 } 75 else 76 { 77 perror("command is invalid!\n"); 78 } 79 80 81 #endif 82 83 close(fd); 84 return 0; 85 }View Code
总结:
1. 当平台设备有多个资源时,平台驱动需要逐个获取
2. 注意平台设备驱动与平台驱动匹配的条件