通过A64手册可以看出共有两个普通gpio控制器:
gpio控制器是由厂商负责驱动好的,在设备树里关于gpio控制器的描述:
pio: pinctrl@01c20800 { compatible = "allwinner,sun50i-pinctrl"; reg = <0x0 0x01c20800 0x0 0x400>; interrupts = <0 11 4>, <0 17 4>, <0 21 4>; device_type = "pio"; clocks = <&clk_pio>; gpio-controller; interrupt-controller; #interrupt-cells = <2>; #size-cells = <0>; #gpio-cells = <6>; r_pio: pinctrl@01f02c00 { compatible = "allwinner,sun50i-r-pinctrl"; reg = <0x0 0x01f02c00 0x0 0x400>; interrupts = <0 45 4>; clocks = <&clk_cpurpio>; device_type = "r_pio"; gpio-controller; interrupt-controller; #interrupt-cells = <2>; #size-cells = <0>; #gpio-cells = <6>;
关于设备树设置GPIO的文档描述:
其中“ #gpio-cells = <6>”表示在设备树里描述使用一个gpio口需要提供6个指定的参数. 通过文档,可以得知6个参数的分别作用: gpio = <&pio 1 1 1 1 1 0>; | | | | | | | |-------------------表示有效电平 | | | | | | |----------------------上下拉, 0关闭功能, 1上拉, 2下拉, 3保留 | | | | | |-------------------------驱动力,电流等级(0 - 3),级别越高,输出电流越大 | | | | |----------------------------gpio功能类型,0输入, 1输出, 6和外部中断,7关闭功能(具体查手册) | | | |------------------------------pin bank 内偏移(即组内第几个io口). | | |---------------------------------哪组gpio, PA(0),PB(1),PC(2),PD(3),PE(4),PF(5),PG(6),PH(7),PI(8),PJ(9),PK(10),PL(11) | |--------------------------------------指向哪个gpio控制器, pio / r_pio(PL组) |-----------------------------------------------------属性名字(随便命名)
那么在Linux驱动里如何获取设备树里设备节点的gpio口信息呢?
#include <linux/of_gpio.h> //只需一个函数即可 int of_get_named_gpio_flags(struct device_node *np, const char *propname, int index, enum of_gpio_flags *flags); //返回值为int类型的gpio口. //np为设备或设备子节点对象, propname为指定的属性名字, index表示获取属性里的第几个值 // 其中flags一定得注意,按文档里的说明应就是一个int类型的值,但根本就不能为int参数(会导致kernel panic), // 通过阅读内核里的代码得出, flags的参数应为struct gpio_config类型. 定义在下面文件: "include/linux/sys_config.h" struct gpio_config { u32 gpio; /* gpio global index, must be unique */ u32 mul_sel; /* multi sel val: 0 - input, 1 - output... */ u32 pull; /* pull val: 0 - pull up/down disable, 1 - pull up... */ u32 drv_level; /* driver level val: 0 - level 0, 1 - level 1... */ u32 data; /* data val: 0 - low, 1 - high, only vaild when mul_sel is input/output */ };
获取到int类型的gpio口后,就可以使用linux/gpio.h里的gpio口操作函数:
#include <linux/gpio.h> //里面声明io口的操作函数 int gpio_request(unsigned gpio, const char *label);//每个io只能被请求一次,可防止多个驱动来控制同一个IO口 void gpio_free(unsigned gpio); //释放已请求的io口 int gpio_direction_input(unsigned gpio); //把指定的IO口作输入功能, gpio用于指定具体哪个io口 int gpio_direction_output(unsigned gpio, int value); //作输出功能,并根据value的值输出高低电平 int gpio_get_value(unsigned gpio); //获取指定IO口的电平 void gpio_set_value(unsigned gpio, int value); //设置IO口的电平为value(0/1) int gpio_to_irq(unsigned gpio); //根据io口,获取到它对应的中断号(io口大都有外部中断功能)
应用例子,如图板上有两个led,和一个蜂鸣器:
设备树里添加对这两个LED和蜂鸣器的描述:
jkbuzzer { compatible = "jk,buzzer"; gpios = <&pio 3 24 1 1 1 1>; }; jkleds { compatible = "jk,leds"; gpios = <&r_pio 11 10 1 1 1 1>, <&r_pio 11 12 1 1 1 1>; };
在Linux驱动中编写蜂鸣器的测试代码 如下:
#include <linux/init.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/of.h> #include <linux/of_gpio.h> #include <linux/gpio.h> #include <linux/sys_config.h> #include <linux/delay.h> int myprobe(struct platform_device *pdev) { struct device_node *nd = pdev->dev.of_node; int gpio; struct gpio_config config; printk("gpio count:%d\n", of_gpio_named_count(nd, "gpios")); gpio = of_get_named_gpio_flags(nd, "gpios", 0, (enum of_gpio_flags *)&config); if (!gpio_is_valid(gpio)) printk("gpio isn't valid\n"); if (gpio_request(gpio, pdev->name) < 0) printk("gpio request failed %d\n", gpio); gpio_direction_output(gpio, 1); msleep(3000); gpio_direction_input(gpio); gpio_free(gpio); return 0; } int myremove(struct platform_device *pdev) { printk("in myremove ...\n"); return 0; } struct of_device_id ids[] = { {.compatible = "jk,buzzer"}, {}, }; struct platform_driver mydrv = { .probe = myprobe, .remove = myremove, .driver = { .owner = THIS_MODULE, .name = "mydrv" , .of_match_table = ids, }, }; module_platform_driver(mydrv); MODULE_LICENSE("GPL");
在Linux驱动中编写两个LED的驱动测试代码如下:
#include <linux/init.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/of.h> #include <linux/of_gpio.h> #include <linux/gpio.h> #include <linux/sys_config.h> #include <linux/delay.h> int myprobe(struct platform_device *pdev) { struct device_node *nd = pdev->dev.of_node; int gpio, n, i; struct gpio_config config; n = of_gpio_named_count(nd, "gpios"); for (i = 0; i < n ; i++) { gpio = of_get_named_gpio_flags(nd, "gpios", i, (enum of_gpio_flags *)&config); if (!gpio_is_valid(gpio)) printk("gpio isn't valid\n"); if (gpio_request(gpio, pdev->name) < 0) printk("gpio request failed %d\n", gpio); gpio_direction_output(gpio, 1); msleep(3000); gpio_direction_input(gpio); gpio_free(gpio); } return 0; } int myremove(struct platform_device *pdev) { printk("in myremove ...\n"); return 0; } struct of_device_id ids[] = { {.compatible = "jk,leds"}, {}, }; struct platform_driver mydrv = { .probe = myprobe, .remove = myremove, .driver = { .owner = THIS_MODULE, .name = "mydrv" , .of_match_table = ids, }, };