pinctrl和GPIO子系统

本文详细介绍了Linux系统中的Pinctrl和GPIO子系统如何简化框架开发,包括pinctrl子系统管理pin的复用和电气特性的功能,以及GPIO子系统在驱动程序中的API应用。通过修改设备树实例,展示了如何使用GPIO进行设备控制。
摘要由CSDN通过智能技术生成

1.介绍

为了满足框架开发,linux系统中使用pinctrl和GPIO子系统来简化开发。

2.pinctrl子系统

pinctrl 子系统主要工作内容如下:

  • 获取设备树中 pin 信息。
  • 根据获取到的 pin 信息来设置 pin 的复用功能
  • 根据获取到的 pin 信息来设置 pin 的电气特性,比如上/下拉、速度、驱动能力等。
    对于我们使用者来讲,只需要在设备树里面设置好某个 pin 的相关属性即可,其他的初始化工作均由 pinctrl 子系统来完成,pinctrl 子系统源码目录为drivers/pinctrl。imageimageimageimageimageimage

3.gpio子系统

上一小节讲解了 pinctrl 子系统,pinctrl 子系统重点是设置 PIN(有的 SOC 叫做 PAD)的复用和电气属性,如果 pinctrl 子系统将一个 PIN 复用为 GPIO 的话,那么接下来就要用到 gpio 子系统了。gpio 子系统顾名思义,就是用于初始化 GPIO 并且提供相应的 API 函数,比如设置 GPIO为输入输出,读取 GPIO 的值等。gpio 子系统的主要目的就是方便驱动开发者使用 gpio,驱动开发者在设备树中添加 gpio 相关信息,然后就可以在驱动程序中使用 gpio 子系统提供的 API函数来操作 GPIO,Linux 内核向驱动开发者屏蔽掉了 GPIO 的设置过程,极大的方便了驱动开发者使用 GPIO。imageimageimageimageimageimage

imageimageimage

4.代码

4.1 修改设备树的代码

imageimageimage

#include <linux/types.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/ide.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/errno.h> 
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <asm/mach/map.h>

#define GPIOLED_CNT     1
#define GPIOLED_NAME    "gpioled"
#define LEDOFF          0            /* 关灯 */
#define LEDON           1            /* 开灯 */

/* gpioled设备结构体 */
struct gpioled_dev{
    dev_t           devid;          /* 设备号 */
    struct cdev     cdev;           /* 字符设备 */
    struct class    *class;         /* 类 */
    struct device   *device;        /* 设备 */
    int             major;          /* 注设备号 */
    int             minor;          /* 次设备号 */
    struct device_node *nd;         /* 设备节点 */
    int led_gpio;                   /* 设备树里面的编号 */
};

struct gpioled_dev gpioled; /* 定义LED结构体 */

void led_switch(struct gpioled_dev *dev, u8 sta)
{
    if(sta == LEDON) {
        gpio_set_value(dev->led_gpio, 0);
    }else if(sta == LEDOFF) {
        gpio_set_value(dev->led_gpio, 1);
    } else {
        printk("Error Number! \r\n");
    }
}

static int gpioled_open(struct inode *inode, struct file *filp)
{
    filp->private_data = &gpioled;
    return 0;
}

static int gpioled_release(struct inode *inode, struct file *filp)
{
    //struct gpioled_dev *dev = (struct gpioled_dev *)filp->private_data;
    return 0;
}

static ssize_t gpioled_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos)
{
    int retvalue;
    unsigned char databuf[1];
    // unsigned char ledstat; 
    struct gpioled_dev *dev = (struct gpioled_dev *)filp->private_data;

    retvalue = copy_from_user(databuf, buf, count);
    if(retvalue < 0) {
        printk("kernel write failed!\r\n");
        return -EFAULT;
    }
    led_switch(dev, databuf[0]);   
    
    return 0;
}

static ssize_t gpioled_read(struct file *filp, char __user *buf,
size_t cnt, loff_t *offt)
{
    return 0;
}

/* 字符设备操作集 */
static const struct file_operations gpioled_fops = {
    .owner = THIS_MODULE,
    .write = gpioled_write,
    .open = gpioled_open,
    .read = gpioled_read,
    .release = gpioled_release,
};

/* 模块入口函数 */
static int __init gpioled_init(void)
{
    /* 定义一些所需变量 */
    int ret = 0;

    /* 1. 注册字符设备驱动 */
    gpioled.major = 0;
    if(gpioled.major) {
        gpioled.devid = MKDEV(gpioled.major, 0);
        ret = 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);
    }
    if(ret < 0){
        goto fail_devid;
    }
    printk("Make devid success! \r\n");
    printk("major = %d, minor = %d \r\n", gpioled.major, gpioled.minor);

    /* 2. 初始化cdev */
    gpioled.cdev.owner = THIS_MODULE;
    cdev_init(&gpioled.cdev, &gpioled_fops);
    ret = cdev_add(&gpioled.cdev, gpioled.devid, GPIOLED_CNT);
    if (ret < 0){
        goto fail_cdev;
    } else {
        printk("Cdev add sucess! \r\n");
    }

    /* 3. 自动创建设备节点 */
    gpioled.class = class_create(THIS_MODULE, GPIOLED_NAME);
    if(IS_ERR(gpioled.class)) {
        ret = PTR_ERR(gpioled.class);
        goto fail_class;
    } else {
        printk("Class create sucess! \r\n");
    }

    gpioled.device = device_create(gpioled.class, NULL, gpioled.devid, NULL, GPIOLED_NAME);
    if(IS_ERR(gpioled.device)) {
        ret = PTR_ERR(gpioled.device);
        goto fail_device;
    } else {
        printk("Device create sucess! \r\n");
    }

    /* 1. 获取设备节点 */
    gpioled.nd = of_find_node_by_path("/gpioled");
    if( gpioled.nd == NULL){
        ret = -EINVAL;
        printk("gpioled node can not found!\r\n");
        goto fail_findnd;
    }

    /* 2. 获取设备所对应的gpio */
    /* 注意第三个参数,因为只有一个索引,所以是0 */
    gpioled.led_gpio = of_get_named_gpio(gpioled.nd, "led-gpios", 0);
    if( gpioled.led_gpio < 0) {
        printk("Can't find led gpio \r\n");
        ret = -EINVAL;
        goto fail_findnd;
    }
    printk("Led gpio num = %d. \r\n", gpioled.led_gpio);
    
    /* 3. 获取到IO之后,申请IO */
    /* 作用是申请之后,就能知道该IO是否被其他函数占用 */
    /* 如果申请失败,说明被战用啦 */
    /* 解决方法:1.搜索所需管脚 2.在搜索复用管脚,例如 &gpio1 3 */
    ret = gpio_request(gpioled.led_gpio, "led-gpio");
    if(ret) {
        printk("Failed to request the led gpio! \r\n");
        ret = -EINVAL;
        goto fail_findnd;
    }

    /* 4. 使用IO */
    ret = gpio_direction_output(gpioled.led_gpio, 1); /* 设置输出,默认高电平不点亮 */
    if(ret < 0){
        printk("Failed to set input or output! \r\n");
        goto fail_set_output;
    }

    /* 5. 输出低电平,点亮LED */
    gpio_set_value(gpioled.led_gpio, 0);

    printk("gpioled init! \r\n");
    return 0;

/* 错误处理 */
fail_set_output:
    gpio_free(gpioled.led_gpio);
fail_findnd:
    device_destroy(gpioled.class, gpioled.devid);
fail_device:
    class_destroy(gpioled.class);
fail_class:
    cdev_del(&gpioled.cdev);
fail_cdev:
    unregister_chrdev_region(gpioled.devid, GPIOLED_CNT);
fail_devid:
    return ret;
}

/* 模块出口函数 */
static void __exit gpioled_exit(void)
{
    /* 关灯 */
    gpio_set_value(gpioled.led_gpio, 1);

    /* 1. 释放设备号 */
    cdev_del(&gpioled.cdev);
    /* 2. 注销设备号 */
    unregister_chrdev_region(gpioled.devid, GPIOLED_CNT);
    /* 3. 摧毁设备 */
    device_destroy(gpioled.class, gpioled.devid);
    /* 4.摧毁类 */
    class_destroy(gpioled.class);

    /* 释放IO, 和上面的申请IO对应 */
    gpio_free(gpioled.led_gpio);

    printk("gpioled exit! \r\n");
}

/* 模块入口和出口注册 */
module_init(gpioled_init);
module_exit(gpioled_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Shao Zheming");
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值