天天看點

pinctrl和GPIO子系統

1.介紹

為了滿足架構開發,linux系統中使用pinctrl和GPIO子系統來簡化開發。

2.pinctrl子系統

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

  • 擷取裝置樹中 pin 資訊。
  • 根據擷取到的 pin 資訊來設定 pin 的複用功能
  • 根據擷取到的 pin 資訊來設定 pin 的電氣特性,比如上/下拉、速度、驅動能力等。

    對于我們使用者來講,隻需要在裝置樹裡面設定好某個 pin 的相關屬性即可,其他的初始化工作均由 pinctrl 子系統來完成,pinctrl 子系統源碼目錄為drivers/pinctrl。

    pinctrl和GPIO子系統
    pinctrl和GPIO子系統
    pinctrl和GPIO子系統
    pinctrl和GPIO子系統
    pinctrl和GPIO子系統
    pinctrl和GPIO子系統

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。

pinctrl和GPIO子系統
pinctrl和GPIO子系統
pinctrl和GPIO子系統
pinctrl和GPIO子系統
pinctrl和GPIO子系統
pinctrl和GPIO子系統

4.代碼

4.1 修改裝置樹的代碼

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

繼續閱讀