pinctrl子系统和gpio子系统简化了我们驱动工程师的开发,将芯片的底层操作和驱动分割开来了,底层由bsp工程师完成,他来实现pinctrl子系统和gpio子系统,而我们只需要使用该系统就可完成相关的设备的初始化和配置。pinctrl完成的是引脚的配置,比如引脚的复用,引脚属性(上下拉,电阻大小等等)。gpio完成的是gpio的输入输出功能,给驱动提供接口来控制引脚的输出电平或读取引脚的值。这两者的配置我们只需要在设备树中添加就OK了。
1. pinctrl子系统使用步骤
1.1 节点的基本结构
该节点用于定义
pinctrl的节点应该添加到&iomuxc节点下, GPIO5相关的添加到&iomuxc_snvs下
// 基本结构如下: pinctrl_test: testgrp { fsl,pins = < 3 /* 设备所使用的 PIN 配置信息 */ >; }; // eg: pinctrl_rgb_led:rgb_led { fsl,pins = < MX6UL_PAD_GPIO1_IO04__GPIO1_IO04 0x000010B1 MX6UL_PAD_CSI_HSYNC__GPIO4_IO20 0x000010B1 MX6UL_PAD_CSI_VSYNC__GPIO4_IO19 0x000010B1 >; };
- pinctrl_test: 为pinctrl节点的别名,注意:前缀pinctrl_是必须有的
- testgrp: 为pinctrl节点的名字
- fsl, pins = <> , 规则如此没什么说的。里面的PIN配置信息结构为复用功能宏定义 设置pad的值。这些宏定义定义在文件源码根目录/arch/arm/boot/dts/imx6ul-pinfunc.h中,pad的值一般设置为10b1就好了。
1.2 需要在设备节点下调用前面定义的节点
需要添加的属性有:
- pinctrl-nams = “default” , 该属性用于指定该设备节点的状态
- pinctrl-n = <&pinctrl_test>, 该属性指定该设备的第n个状态的引脚配置魏pinctrl_test对应的配置
// eg: my_rgb_led { #address-cells = <1>; #size-cells = <1>; pinctrl-names = "default"; compatible = "fire,my_rgb_led"; pinctrl-0 = <&pinctrl_rgb_led>; // 后面的与GPIO子系统有关 rgb_led_red = <&gpio1 4 GPIO_ACTIVE_LOW>; rgb_led_green = <&gpio4 20 GPIO_ACTIVE_LOW>; rgb_led_blue = <&gpio4 19 GPIO_ACTIVE_LOW>; status = "okay"; };
2. GPIO子系统使用步骤
2.1 需要在对应的设备树节点添加
属性:gpios = <>,属性值有三个,第一个指定使用的引脚属于那一组(格式为&gpio1, &gpio2,…),第二个为引脚号,第三个为一个宏定义,指定什么电平有效(GPIO_ACTIVE_HIGH, GPIO_ACTIVE_LOW) 。
// eg: my_rgb_led { ... rgb_led_red = <&gpio1 4 GPIO_ACTIVE_LOW>; rgb_led_green = <&gpio4 20 GPIO_ACTIVE_LOW>; rgb_led_blue = <&gpio4 19 GPIO_ACTIVE_LOW>; status = "okay"; };
2.2 GPIO子系统提供的API
2.2.1 gpio_request
用于申请一个 GPIO 管脚,在使用一个 GPIO 之前一定要使用 gpio_request进行申请
int gpio_request(unsigned gpio, const char *label)
- gpio:要申请的 gpio 标号,使用 of_get_named_gpio 函数从设备树获取指定 GPIO 属性信息,此函数会返回这个 GPIO 的标号。
- label:给 gpio 设置个名字。
2.2.2 gpio_free
对申请的gpio进行释放
void gpio_free(unsigned gpio)
2.2.3 gpio_direction_input
设置某个GPIO为输入
int gpio_direction_input(unsigned gpio)
2.2.4 gpio_direction_output
此函数用于设置某个 GPIO 为输出,并且设置默认输出值
int gpio_direction_output(unsigned gpio, int value)
2.2.5 gpio_get_value
此函数用于获取某个 GPIO 的值(0 或 1)
#define gpio_get_value __gpio_get_value int __gpio_get_value(unsigned gpio)
2.2.6 gpio_set_value
此函数用于设置某个 GPIO 的值
#define gpio_set_value __gpio_set_value void __gpio_set_value(unsigned gpio, int value)
3. 与gpio相关的of函数
3.1 of_gpio_named_count
用于获取设备树某个属性里面定义了几个 GPIO 信息
int of_gpio_named_count(struct device_node *np, const char *propname)
3.2 of_gpio_count
与上一个函数功能相同,不同的是该函数指定了统计属性为"gpios"
int of_gpio_count(struct device_node *np)
3.3 of_get_named_gpio
此函数获取 GPIO 编号,因为 Linux 内核中关于 GPIO 的 API 函数都要使用 GPIO 编号,此函数会将设备树中类似<&gpio5 7 GPIO_ACTIVE_LOW>的属性信息转换为对应的 GPIO 编号
int of_get_named_gpio(struct device_node *np, const char *propname, int index)
4. 实例(野火i.MX6ULL PRO开发板)
4.1 设备树添加的内容
// 根节点下添加 my_rgb_led { #address-cells = <1>; #size-cells = <1>; pinctrl-names = "default"; compatible = "fire,my_rgb_led"; pinctrl-0 = <&pinctrl_rgb_led>; rgb_led_red = <&gpio1 4 GPIO_ACTIVE_LOW>; rgb_led_green = <&gpio4 20 GPIO_ACTIVE_LOW>; rgb_led_blue = <&gpio4 19 GPIO_ACTIVE_LOW>; status = "okay"; }; // &iomuxc节点下添加 pinctrl_rgb_led:rgb_led { fsl,pins = < MX6UL_PAD_GPIO1_IO04__GPIO1_IO04 0x000010B1 MX6UL_PAD_CSI_HSYNC__GPIO4_IO20 0x000010B1 MX6UL_PAD_CSI_VSYNC__GPIO4_IO19 0x000010B1 >; };
4.2 驱动文件
/* 编写要点 1. 完成平台驱动(platform_driver_register())的注册,probe, remove函数的实现 2. 完成驱动(driver_register())的注册,增加设备节点,实现file_operation结构体的相关函数 */ #include <linux/module.h> #include <linux/fs.h> #include <linux/errno.h> #include <linux/miscdevice.h> #include <linux/kernel.h> #include <linux/major.h> #include <linux/mutex.h> #include <linux/proc_fs.h> #include <linux/seq_file.h> #include <linux/stat.h> #include <linux/init.h> #include <linux/device.h> #include <linux/tty.h> #include <linux/kmod.h> #include <linux/gfp.h> #include <linux/of.h> #include <linux/platform_device.h> #include <asm/mach/map.h> #include <linux/of_gpio.h> #include <linux/gpio.h> #include <asm/io.h> static int major = 0; // 主设备号 static struct class *led_class; static int rgb_led_red; static int rgb_led_green; static int rgb_led_blue; static int current_led; static int led_drv_open(struct inode *node, struct file *file) { int minor = iminor(node); switch(minor) { case 0: current_led = rgb_led_red; break; case 1: current_led = rgb_led_green; break; case 2: current_led = rgb_led_blue; break; default: printk("========error: not minor=========\n"); } printk("current_led: %d\n", current_led); printk("===============%s===============\n", __FUNCTION__); return 0; } static int led_drv_close(struct inode *node, struct file *file) { printk("%s\n", __FUNCTION__); return 0; } static ssize_t led_drv_read(struct file *file, char __user * buf, size_t size, loff_t *offset) { printk("%s\n", __FUNCTION__); return 0; } static ssize_t led_drv_write (struct file *file, const char __user *buf, size_t size, loff_t *offset) { int minor; char status; struct inode *node = file_inode(file); minor = iminor(node); copy_from_user(&status, buf, 1); if (status) { // 开灯 gpio_direction_output(current_led, 0); } else { // 关灯 gpio_direction_output(current_led, 1); } printk("%s\n", __FUNCTION__); return 0; } static struct file_operations led_drv = { .owner = THIS_MODULE, //gcc用法 .open = led_drv_open, .read = led_drv_read, .write = led_drv_write, .release = led_drv_close, }; static int rgb_led_probe(struct platform_device *pdev) { int err; // 获取资源 struct device_node *rgb_device_node = NULL; // led节点的父节点 rgb_device_node = of_find_node_by_path("/my_rgb_led"); if (rgb_device_node == NULL) { printk("%s,%s,%d error: not find /my_reg_led\n", __FILE__, __FUNCTION__, __LINE__); return -1; } // 获取引脚编号 rgb_led_red = of_get_named_gpio(rgb_device_node, "rgb_led_red", 0); rgb_led_green = of_get_named_gpio(rgb_device_node, "rgb_led_green", 0); rgb_led_blue = of_get_named_gpio(rgb_device_node, "rgb_led_blue", 0); printk("========red_red: %d=========\n", rgb_led_red); printk("========red_green: %d=========\n", rgb_led_green); printk("========red_blue: %d=========\n", rgb_led_blue); gpio_direction_output(rgb_led_red, 1); gpio_direction_output(rgb_led_green, 1); gpio_direction_output(rgb_led_blue, 1); // 完成字符设备的注册 major = register_chrdev(0, "rgb_led", &led_drv); // 注册设备类 led_class = class_create(THIS_MODULE, "hxdled_class"); err = PTR_ERR(led_class); if (IS_ERR(led_class)) { unregister_chrdev(major, "rgb_led"); return -1; } // 注册设备节点,/dev/rgb_led% device_create(led_class, NULL, MKDEV(major, 0), NULL, "led_red"); device_create(led_class, NULL, MKDEV(major, 1), NULL, "led_green"); device_create(led_class, NULL, MKDEV(major, 2), NULL, "led_blue"); printk("=======================%s========================\n", __FUNCTION__); return 0; } static int rgb_led_remove(struct platform_device *dev) { // 释放设备节点 device_destroy(led_class, MKDEV(major, 0)); device_destroy(led_class, MKDEV(major, 1)); device_destroy(led_class, MKDEV(major, 2)); // 释放设备类 class_destroy(led_class); // 释放注册字符设备 unregister_chrdev(major, "rgb_led"); printk("%s\n", __FUNCTION__); return 0; } static const struct of_device_id rgb_led[] = { {.compatible = "fire,my_rgb_led"}, }; static struct platform_driver rgb_led_platform_driver = { .probe = rgb_led_probe, .remove = rgb_led_remove, .driver = { .name = "rgb-leds-device-tree", .owner = THIS_MODULE, //.remove = rgb_led_remove, .of_match_table = rgb_led, // match匹配的时候会用到这个列表里面的compatible和设备树里面的compatible对比 } }; // 平台驱动入口函数 static int __init platform_rgb_led_init(void) { int ret = 0; // 完成平台驱动的注册 ret = platform_driver_register(&rgb_led_platform_driver); printk("%s\n", __FUNCTION__); return 0; } // 平台驱动出口函数 static void __exit platform_rgb_led_exit(void) { // 完成平台驱动的移除 platform_driver_unregister(&rgb_led_platform_driver); printk("%s\n", __FUNCTION__); return; } module_init(platform_rgb_led_init); module_exit(platform_rgb_led_exit); MODULE_LICENSE("GPL");
4.3 应用程序
#include<stdio.h> #include<sys/types.h> #include<unistd.h> #include<fcntl.h> #include<sys/stat.h> #include<string.h> int main(int argc, char **argv) { int fd; char status; if (argc != 3) { printf("Usage %s <dev-path> <on/off>\n", argv[0]); return -1; } fd = open(argv[1], O_RDWR); if (fd < 0) { perror("open error"); return -1; } if (strcmp(argv[2], "on") == 0) { // 开灯 status = 1; write(fd, &status, 1); } else if (strcmp(argv[2], "off") == 0) { // 关灯 status = 0; write(fd, &status, 1); } else { printf("Usage %s <dev-path> <on/off>\n", argv[0]); return -1; } close(fd); return 0; }
4.4 Makefile
ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- export ARCH CROSS_COMPILE KERN_DIR = /home/hxd/workdir/ebf_linux_kernel_6ull_depth1/build_image/build all: make -C $(KERN_DIR) M=`pwd` modules $(CROSS_COMPILE)gcc -o led_test led_test.c clean: make -C $(KERN_DIR) M=`pwd` modules clean rm -rf modules.order rm -f hello_drv_test obj-m += led_driver.o