字元裝置驅動:LED平臺匯流排實現方式
阿新 • • 發佈:2021-10-14
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 #defineView CodeGPIO_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 #endif
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. 注意平臺裝置驅動與平臺驅動匹配的條件