目录
Linux内核提供了pinctrl和gpio子系统用于GPIO驱动
一、6ull的GPIO使用
1、设置PIN的复用和电气属性
2、配置GPIO
二、pinctrl子系统
借助pinctrl来设置一个PIN的复用和电气属性
①、获取设备树中pin信息
②、根据获取到的pin信息来设置pin复用功能
③、根据获取到的pin信息来设置pin的电气属性,比如上下拉、速度、驱动能力
/* 官方板级信息,根据自己板子追加信息 */
1.1 IOMUXC SNVS控制器
iomuxc_snvs:iomuxc-snvs@02290000{
compatible="fslimx6ull-iomuxc-snvs;
reg= <0x022900000 x10000>;
};
1.2 IOMUXC控制器
iomuxc: iomuxc@020e0000{
compatible="fslimx6ul-iomuxc";
reg= <0x020e0000 0x4000>;
};
根据设备的类型,创建对应的子节点,然后设备所有PIN放到此结点
1.3gpr控制器
gpr:iomuxc-gpr@020e4000{
compatible="fsl,imx6ul-iomuxc-gpr,
"fsl,imx6q-iomuxc-gpr","syscon";
reg=<0x020e40000x4000>;
};
1.4 添加一个PIN的信息
pinctrl_hog_1:hoggrp-1 {
fsl.pin = <
MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0X17059 电气属性
MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0X0090 0X031C 0X0000 0X5 0X0
iomuxc 父节点首地址:020e0000
mux_reg conf_reg input_reg mux_mode input_val
0X0090 0X031C 0X0000 0X5 0X0
mux_reg:020e0000+0x0090
conf_reg:020e0000 + 0X031C //电气属性配置寄存器
input_reg:偏移为0,表示MX6UL_PAD_UART1_RTS_B没有input功能
mux_mode:5表示复用为GPIO1_IO19,将其写入 020e 0090
input_val:0,就是写入input_reg的值
>
};
三、gpio子系统
使用gpio子系统来使用gpio
1、gpio在设备树中的表示方法
&usdhc1{
pinctrl-names="default”,"state_100mhz","state_200mhz”;
pinctri-0=<&pinctrl_usdhc1>;
pinctri-1=<&pinctrlusdhc1 100mhz>;
pinctrl-2=<&pinctrlusdhc1_200mhz>;
cd-gpios=<gpio119 GPIO ACTIVE LOW>; 定义了一个 cd-gpios 属性。
/* 引脚检测,GPIO1_IO19 低电平有效 此处使用GPIO1_IO19*/
status="okay";
};
如何从设备树中获取要使用的GPIO信息 ==> OF函数
2、驱动中对GPIO的操作函数
①、获取到GPIO所处的设备节点
②、获取GPIO:of_get_named_gpio,返回值是GPIO编号
③、请求此编号的GPIO:gpio_request
④、设置GPIO,输入或输出。gpio_direcion_output/input
⑤、如果是输入,那么通过gpio_get_value函数读取
如果是输出,那么通过gpio_set_value函数设置
四、驱动编写
1、添加pinctrl信息
pinctrl_gpioled: ledgrp{
fsl,pins = <
MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0x10b0 /* GPIO1_IO03 */
>;
};
2、在设备树中添加节点
gpioled{
compatible = "alientek.gpioled";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_gpioled>;
led-gpios = <&gpio1 3 GPIO_ACTIVE_LOW>;
status = "okay";
};
3、添加设备节点,在设备节点中创建一个属性,此属性描述所使用的gpio,申请节点
/* get node */
gpioled.nd = of_find_node_by_path("/gpioled");
if(gpioled.nd == NULL) {
ret = -EINVAL;
goto fail_findnode;
}
/* get led-->gpio */
gpioled.led_gpio = of_get_named_gpio(gpioled.nd, "led-gpios", 0);
if(gpioled.led_gpio < 0) {
printk("can't find lde_gpio\r\n");
ret = -EINVAL;
goto fail_findnode;
}
printk("led_gpio number = %d\r\n", gpioled.led_gpio);
/* alloc */
ret = gpio_request(gpioled.led_gpio, "led-gpio");
if(ret) {
printk("fail to request led gpio\r\n");
ret = -EINVAL;
goto fail_findnode;
}
4、直接使用IO输入输出功能
ret = gpio_direction_output(gpioled.led_gpio, 1);
if(ret) {
goto fail_setoutput;
}
gpio_set_value(gpioled.led_gpio, 0); //设置LED亮
5、编写驱动,获取对象的gpio编号,并且申请IO,成功以后可以使用此IO
static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
int ret;
unsigned char databuf[1];
struct gpioled_dev *dev = filp->private_data;
ret = copy_from_user(databuf, buf, cnt);
if(ret < 0) {
return -EINVAL;
}
if(databuf[0] == LED_ON) {
gpio_set_value(dev->led_gpio, 0);
} else if(databuf[0] == LED_OFF) {
gpio_set_value(dev->led_gpio, 1);
}
return 0;
}
五、完成代码
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#define GPIOLED_CNT 1
#define GPIOLED_NAME "gpioled"
#define LED_OFF 0
#define LED_ON 1
struct gpioled_dev{
dev_t devid;
int major;
int minor;
struct cdev cdev;
struct class *class;
struct device *device;
struct device_node *nd;
int led_gpio;
};
struct gpioled_dev gpioled;
static int led_open(struct inode *inode, struct file *filp)
{
filp->private_data = &gpioled;
return 0;
}
static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
int ret;
unsigned char databuf[1];
struct gpioled_dev *dev = filp->private_data;
ret = copy_from_user(databuf, buf, cnt);
if(ret < 0) {
return -EINVAL;
}
if(databuf[0] == LED_ON) {
gpio_set_value(dev->led_gpio, 0);
} else if(databuf[0] == LED_OFF) {
gpio_set_value(dev->led_gpio, 1);
}
return 0;
}
static int led_release(struct inode *inode, struct file *filp)
{
return 0;
}
static const struct file_operations gpioled_fops = {
.owner = THIS_MODULE,
.open = led_open,
.write = led_write,
.release= led_release,
};
static int __init led_init(void)
{
int ret = 0;
gpioled.major = 0;
if(gpioled.major) {
gpioled.devid = MKDEV(gpioled.major, 0);
register_chrdev_region(gpioled.devid, GPIOLED_CNT, GPIOLED_NAME);
} else {
alloc_chrdev_region(&gpioled.devid, 0, GPIOLED_CNT, GPIOLED_NAME);
gpioled.major = MAJOR(gpioled.devid);
gpioled.minor = MINOR(gpioled.devid);
}
printk("gpioled:major = %d, minor = %d\r\n", gpioled.major, gpioled.minor);
gpioled.cdev.owner = THIS_MODULE;
cdev_init(&gpioled.cdev, &gpioled_fops);
cdev_add(&gpioled.cdev, gpioled.devid, GPIOLED_CNT);
gpioled.class = class_create(THIS_MODULE, GPIOLED_NAME);
if(IS_ERR(gpioled.class)) {
return PTR_ERR(gpioled.class);
}
gpioled.device = device_create(gpioled.class, NULL, gpioled.devid, NULL, GPIOLED_NAME);
if(IS_ERR(gpioled.device)) {
return PTR_ERR(gpioled.device);
}
/* get node */
gpioled.nd = of_find_node_by_path("/gpioled");
if(gpioled.nd == NULL) {
ret = -EINVAL;
goto fail_findnode;
}
/* get led-->gpio */
gpioled.led_gpio = of_get_named_gpio(gpioled.nd, "led-gpios", 0);
if(gpioled.led_gpio < 0) {
printk("can't find lde_gpio\r\n");
ret = -EINVAL;
goto fail_findnode;
}
printk("led_gpio number = %d\r\n", gpioled.led_gpio);
/* alloc */
ret = gpio_request(gpioled.led_gpio, "led-gpio");
if(ret) {
printk("fail to request led gpio\r\n");
ret = -EINVAL;
goto fail_findnode;
}
/* use IO */
ret = gpio_direction_output(gpioled.led_gpio, 1);
if(ret) {
goto fail_setoutput;
}
gpio_set_value(gpioled.led_gpio, 0);
return 0;
fail_setoutput:
gpio_free(gpioled.led_gpio);
fail_findnode:
return ret;
}
static void __exit led_exit(void)
{
gpio_set_value(gpioled.led_gpio, 1);
cdev_del(&gpioled.cdev);
unregister_chrdev_region(gpioled.devid, GPIOLED_CNT);
device_destroy(gpioled.class, gpioled.devid);
class_destroy(gpioled.class);
gpio_free(gpioled.led_gpio);
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ZHANG");