Linux驱动编程【pinctrl和gpio子系统】

目录

一、6ull的GPIO使用

二、pinctrl子系统

三、gpio子系统

四、驱动编写

五、完成代码

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");

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值