linux內核的gpiolib詳解


#include <linux/init.h>            // __init   __exit
#include <linux/module.h>      // module_init  module_exit
#include <mach/regs-gpio.h>
#include <mach/gpio-bank.h>

#include <asm/io.h>     //writel

#include <mach/gpio.h> 
#include <linux/leds.h>
#include <asm/string.h> 

#define X210_LED_OFF 1U
#define X210_LED_ON  0U

struct led_classdev    cdev1;
struct led_classdev cdev2;
struct led_classdev cdev3;

void s5pv210_led1_set(struct led_classdev *led_cdev,enum led_brightness brightness);
void s5pv210_led2_set(struct led_classdev *led_cdev,enum led_brightness brightness);
void s5pv210_led3_set(struct led_classdev *led_cdev,enum led_brightness brightness);


static struct gpio x210_led_gpio[] = 
{
    { S5PV210_GPJ0(3), GPIOF_OUT_INIT_HIGH,  "LED1" }, /* default to OFF */
    { S5PV210_GPJ0(4), GPIOF_OUT_INIT_HIGH,  "LED2" }, /* default to OFF */
    { S5PV210_GPJ0(5), GPIOF_OUT_INIT_HIGH,  "LED3" } /* default to OFF */
};
void s5pv210_led1_set(struct led_classdev *led_cdev,enum led_brightness brightness)
{
    printk(KERN_INFO "s5pv210_led1_set successful %d\n",brightness);
    if(brightness == LED_OFF)
    {
        gpio_set_value(x210_led_gpio[0].gpio,X210_LED_OFF);
    }
    else
    {
        gpio_set_value(x210_led_gpio[0].gpio,X210_LED_ON);
        
    }
}    
void s5pv210_led2_set(struct led_classdev *led_cdev,enum led_brightness brightness)
{
    printk(KERN_INFO "s5pv210_led2_set successful %d\n",brightness);
    if(brightness == LED_OFF)
    {
        gpio_set_value(x210_led_gpio[1].gpio,X210_LED_OFF);
    }
    else
    {
        gpio_set_value(x210_led_gpio[1].gpio,X210_LED_ON);
        
    }
}    
void s5pv210_led3_set(struct led_classdev *led_cdev,enum led_brightness brightness)
{
    printk(KERN_INFO "s5pv210_led3_set successful %d\n",brightness);
    if(brightness == LED_OFF)
    {
        gpio_set_value(x210_led_gpio[2].gpio,X210_LED_OFF);
    }
    else
    {
        gpio_set_value(x210_led_gpio[2].gpio,X210_LED_ON);
        
    }
    
}                      
static int __init s5pv210_led_init(void)
{
    int ret = -1;
    printk(KERN_INFO "s5pv210_led_init successful \n");
    cdev1.brightness_set = s5pv210_led1_set;
    cdev1.name = "led1";    
    ret = led_classdev_register(NULL, &cdev1);
    if (ret < 0) 
    {
        printk(KERN_WARNING "led_classdev_register fail \n");
        goto reg_err1;
    }
    
    cdev2.brightness_set = s5pv210_led2_set;
    cdev2.name = "led2";    
    ret = led_classdev_register(NULL, &cdev2);
    if (ret < 0) 
    {
        printk(KERN_WARNING "led_classdev_register fail \n");
        goto reg_err2;
    }
    
    cdev3.brightness_set = s5pv210_led3_set;
    cdev3.name = "led3";    
    ret = led_classdev_register(NULL, &cdev3);
    if (ret < 0) 
    {
        printk(KERN_WARNING "led_classdev_register fail \n");
        goto reg_err3;
    }
    ret = gpio_request_array(x210_led_gpio, ARRAY_SIZE(x210_led_gpio));
    if (ret)
    {
        goto gpio_err;
    }
    
    return 0;

gpio_err:
    led_classdev_unregister(&cdev3);
    
reg_err3:
    led_classdev_unregister(&cdev2);
    
reg_err2:
    led_classdev_unregister(&cdev1);
    
reg_err1:
    return ret;
}

static void __exit s5pv210_led_exit(void)
{    
    printk(KERN_INFO "s5pv210_led_exit successful \n");
    gpio_free_array(x210_led_gpio, ARRAY_SIZE(x210_led_gpio));
    
    led_classdev_unregister(&cdev1);
    led_classdev_unregister(&cdev2);
    led_classdev_unregister(&cdev3);
    
}




module_init(s5pv210_led_init);
module_exit(s5pv210_led_exit);

// MODULE_xxx這種宏作用是用來添加模塊描述信息
MODULE_LICENSE("GPL");                // 描述模塊的許可證
MODULE_AUTHOR("musk");                // 描述模塊的作者
MODULE_DESCRIPTION("x210 LED driver");    // 描述模塊的介紹信息
MODULE_ALIAS("led_driver");            // 描述模塊的別名信息
View Code

一. 什么是gpiolib

    1.1. linux中從2.6.35以后就開始有gpiolib庫了,gpiolib的作用是對所有的gpio實行統一管理,因為驅動在工作的時候,會出現好幾個驅動共同使用同一個gpio的情況;這會造成混亂。所以內核提供了一些方法來管理gpio資源;

二. gpiolib在內核中實現流程

    2.1. gpiolib初始化哪里

        2.1.1. smdkc110_map_io函數是gpiolib初始化的入口

            a. 前面介紹虛擬地址映射時,介紹過下面的結構體在linux初始化會被調用。其中smdkc110_map_io我們值得關注。

#ifdef CONFIG_MACH_SMDKC110
MACHINE_START(SMDKC110, "SMDKC110")
#elif CONFIG_MACH_SMDKV210
MACHINE_START(SMDKV210, "SMDKV210")
#endif
    /* Maintainer: Kukjin Kim <kgene.kim@samsung.com> */
    .phys_io    = S3C_PA_UART & 0xfff00000,
    .io_pg_offst    = (((u32)S3C_VA_UART) >> 18) & 0xfffc,
    .boot_params    = S5P_PA_SDRAM + 0x100,
    //.fixup        = smdkv210_fixup,
    .init_irq    = s5pv210_init_irq,
    .map_io        = smdkc110_map_io,
    .init_machine    = smdkc110_machine_init,
    .timer        = &s5p_systimer,
View Code

            b. s5pv210_gpiolib_init的調用就會初始化gpiolib

static void __init smdkc110_map_io(void)
{
    s5p_init_io(NULL, 0, S5P_VA_CHIPID);                 //  靜態物理地址到虛擬地址的映射初始化
    s3c24xx_init_clocks(24000000);                          //   系統時鍾初始化
    s5pv210_gpiolib_init();                                       //   gpiolib管理器初始化
    s3c24xx_init_uarts(smdkc110_uartcfgs, ARRAY_SIZE(smdkc110_uartcfgs));
    s5p_reserve_bootmem(smdkc110_media_devs, ARRAY_SIZE(smdkc110_media_devs));
#ifdef CONFIG_MTD_ONENAND
    s5pc110_device_onenand.name = "s5pc110-onenand";
#endif
#ifdef CONFIG_MTD_NAND
    s3c_device_nand.name = "s5pv210-nand";
#endif
    s5p_device_rtc.name = "smdkc110-rtc";
}
View Code

        2.1.2. s5pv210_gpiolib_init函數

            a. 函數中有個重要結構體函數s5pv210_gpio_4bit。這個我們后面講

            b. samsung_gpiolib_add_4bit_chips這個函數用於掛載硬件數據到內核中

__init int s5pv210_gpiolib_init(void)
{
    struct s3c_gpio_chip *chip = s5pv210_gpio_4bit;    //  4bit表示的是一個gpio使用4位來配置描述     s5pv210_gpio_4bit是一個struct s3c_gpio_chip數組,是三星移植時寫好的
    int nr_chips = ARRAY_SIZE(s5pv210_gpio_4bit);     //  獲取端口數量(注意一組端口和一個具體的gpio)
    int i = 0;

    for (i = 0; i < nr_chips; i++, chip++) {
        if (chip->config == NULL)              //  如果我們的gpio端口沒有  配置方法  則使用  gpio_cfg  進行默認配置
            chip->config = &gpio_cfg;
        if (chip->base == NULL)                 //  如果我們的gpio端口結構體中沒有填充 基准編號   則使用下面進行填充
            chip->base = S5PV210_BANK_BASE(i);
    }

    samsung_gpiolib_add_4bit_chips(s5pv210_gpio_4bit, nr_chips);   //  添加gpiolib

    return 0;
}
View Code

    2.2. 與硬件相關函數

        2.2.1.  samsung_gpiolib_4bit_input&samsung_gpiolib_4bit_output函數

            a. 這個函數是真正進行寄存器操作的函數,最終驅動工程師進行io設置時會間接指向該函數指針

/* The samsung_gpiolib_4bit routines are to control the gpio banks where
 * the gpio configuration register (GPxCON) has 4 bits per GPIO, as the
 * following example:
 *
 * base + 0x00: Control register, 4 bits per gpio
 *        gpio n: 4 bits starting at (4*n)
 *        0000 = input, 0001 = output, others mean special-function
 * base + 0x04: Data register, 1 bit per gpio
 *        bit n: data bit n
 *
 * Note, since the data register is one bit per gpio and is at base + 0x4
 * we can use s3c_gpiolib_get and s3c_gpiolib_set to change the state of
 * the output.
*/

static int samsung_gpiolib_4bit_input(struct gpio_chip *chip,
                      unsigned int offset)
{
    struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip);
    void __iomem *base = ourchip->base;
    unsigned long con;

    con = __raw_readl(base + GPIOCON_OFF);
    con &= ~(0xf << con_4bit_shift(offset));
    __raw_writel(con, base + GPIOCON_OFF);

    gpio_dbg("%s: %p: CON now %08lx\n", __func__, base, con);

    return 0;
}
static int samsung_gpiolib_4bit_output(struct gpio_chip *chip,
                       unsigned int offset, int value)
{
    struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip);
    void __iomem *base = ourchip->base;
    unsigned long con;
    unsigned long dat;

    con = __raw_readl(base + GPIOCON_OFF);
    con &= ~(0xf << con_4bit_shift(offset));
    con |= 0x1 << con_4bit_shift(offset);

    dat = __raw_readl(base + GPIODAT_OFF);

    if (value)
        dat |= 1 << offset;
    else
        dat &= ~(1 << offset);

    __raw_writel(dat, base + GPIODAT_OFF);
    __raw_writel(con, base + GPIOCON_OFF);
    __raw_writel(dat, base + GPIODAT_OFF);

    gpio_dbg("%s: %p: CON %08lx, DAT %08lx\n", __func__, base, con, dat);

    return 0;
}
View Code

        2.2.2. gpio_get_value & gpio_set_value

            a. 這兩個紅替代gpio的讀取和設置函數。一般函數前加"_"或"__"是給內核調用的,這里使用宏定義就很好的避免了驅動開發調用內核調用的函數       

            b. 這個函數也是真正進行寄存器操作的函數,最終驅動工程師進行io讀寫時也會間接指向該函數指針  

#define gpio_get_value __gpio_get_value
#define gpio_set_value __gpio_set_value

/**
 * __gpio_get_value() - return a gpio's value
 * @gpio: gpio whose value will be returned
 * Context: any
 *
 * This is used directly or indirectly to implement gpio_get_value().
 * It returns the zero or nonzero value provided by the associated
 * gpio_chip.get() method; or zero if no such method is provided.
 */
int __gpio_get_value(unsigned gpio)
{
    struct gpio_chip    *chip;

    chip = gpio_to_chip(gpio);
    WARN_ON(extra_checks && chip->can_sleep);
    return chip->get ? chip->get(chip, gpio - chip->base) : 0;
}

/**
 * __gpio_set_value() - assign a gpio's value
 * @gpio: gpio whose value will be assigned
 * @value: value to assign
 * Context: any
 *
 * This is used directly or indirectly to implement gpio_set_value().
 * It invokes the associated gpio_chip.set() method.
 */
void __gpio_set_value(unsigned gpio, int value)
{
    struct gpio_chip    *chip;

    chip = gpio_to_chip(gpio);
    WARN_ON(extra_checks && chip->can_sleep);
    chip->set(chip, gpio - chip->base, value);
}
View Code

    2.3. gpiolib的attribute部分

        2.3.1. attribute用於定義文件的屬性,當我們echo 和cat時都由其決定

        2.3.2. 源碼中export_store&unexport_store函數與查看gpio的其他屬性有關,比如查看IO電平值等。。。

        2.3.3. CONFIG_GPIO_SYSFS該宏決定是否開啟gpiolib的attribute部分。可以在menuconfig進行配置

[root@musk210 led3]# cd /sys/class/gpio/                                                         //shell測試結果
[root@musk210 gpio]# ls
export       gpiochip164  gpiochip235  gpiochip316  gpiochip396  gpiochip56
gpiochip0    gpiochip172  gpiochip244  gpiochip325  gpiochip40   gpiochip62
gpiochip104  gpiochip181  gpiochip253  gpiochip334  gpiochip405  gpiochip71
gpiochip112  gpiochip188  gpiochip262  gpiochip343  gpiochip414  gpiochip80
gpiochip120  gpiochip197  gpiochip271  gpiochip35   gpiochip423  gpiochip89
gpiochip128  gpiochip206  gpiochip280  gpiochip351  gpiochip431  gpiochip9
gpiochip137  gpiochip212  gpiochip289  gpiochip360  gpiochip438  gpiochip96
gpiochip14   gpiochip221  gpiochip29   gpiochip369  gpiochip447  unexport
gpiochip146  gpiochip226  gpiochip298  gpiochip378  gpiochip456
gpiochip155  gpiochip23   gpiochip307  gpiochip387  gpiochip47
[root@musk210 gpio]# cd gpiochip0
[root@musk210 gpiochip0]# ls
base       label      ngpio      power      subsystem  uevent
[root@musk210 gpiochip0]# cat base 
0
[root@musk210 gpiochip0]# echo 0 > base 
-sh: can't create base: Permission denied
[root@musk210 gpiochip0]#                                                                      // shell測試 end

/*
 * /sys/class/gpio/gpiochipN/
 *   /base ... matching gpio_chip.base (N)
 *   /label ... matching gpio_chip.label
 *   /ngpio ... matching gpio_chip.ngpio
 */

static ssize_t chip_base_show(struct device *dev,
                   struct device_attribute *attr, char *buf)
{
    const struct gpio_chip    *chip = dev_get_drvdata(dev);

    return sprintf(buf, "%d\n", chip->base);
}
static DEVICE_ATTR(base, 0444, chip_base_show, NULL);

static ssize_t chip_label_show(struct device *dev,
                   struct device_attribute *attr, char *buf)
{
    const struct gpio_chip    *chip = dev_get_drvdata(dev);

    return sprintf(buf, "%s\n", chip->label ? : "");
}
static DEVICE_ATTR(label, 0444, chip_label_show, NULL);

static ssize_t chip_ngpio_show(struct device *dev,
                   struct device_attribute *attr, char *buf)
{
    const struct gpio_chip    *chip = dev_get_drvdata(dev);

    return sprintf(buf, "%u\n", chip->ngpio);
}
static DEVICE_ATTR(ngpio, 0444, chip_ngpio_show, NULL);

static const struct attribute *gpiochip_attrs[] = {
    &dev_attr_base.attr,
    &dev_attr_label.attr,
    &dev_attr_ngpio.attr,
    NULL,
};

static const struct attribute_group gpiochip_attr_group = {
    .attrs = (struct attribute **) gpiochip_attrs,
};
View Code

    2.4. linux中查看gpio使用情況

        2.4.1. 使用方法:mount -t debugfs debugfs /tmp,然后cat /tmp/gpio即可得到gpio的所有信息

        2.4.2. 使用完后umount /tmp卸載掉debugfs

三. struct s3c_gpio_chip s5pv210_gpio_4bit[]

    3.1. 這個結構體數據就是三星SOC廠商編寫的code,把SOC是所有的port,虛擬地址,操作function和內核掛接起來

        3.1.1. 這里講解下*base與base

            a. struct s3c_gpio_chip結構體下的*base是每個port起始虛擬地址

            b. struct gpio_chip結構體下的base可以理解為每個port下具體IO的編號。這個編號從小到大排列,可以是不連續單必須唯一,這樣可以定位到每個IO口上

/**
 * struct s3c_gpio_chip - wrapper for specific implementation of gpio
 * @chip: The chip structure to be exported via gpiolib.
 * @base: The base pointer to the gpio configuration registers.
 * @config: special function and pull-resistor control information.
 * @lock: Lock for exclusive access to this gpio bank.
 * @pm_save: Save information for suspend/resume support.
 *
 * This wrapper provides the necessary information for the Samsung
 * specific gpios being registered with gpiolib.
 *
 * The lock protects each gpio bank from multiple access of the shared
 * configuration registers, or from reading of data whilst another thread
 * is writing to the register set.
 *
 * Each chip has its own lock to avoid any  contention between different
 * CPU cores trying to get one lock for different GPIO banks, where each
 * bank of GPIO has its own register space and configuration registers.
 */
struct s3c_gpio_chip {
    struct gpio_chip    chip;
    struct s3c_gpio_cfg    *config;
    struct s3c_gpio_pm    *pm;
    void __iomem        *base;
    int            eint_offset;
    spinlock_t         lock;
#ifdef CONFIG_PM
    u32            pm_save[7];
#endif
};

/**
 * struct gpio_chip - abstract a GPIO controller
 * @label: for diagnostics
 * @dev: optional device providing the GPIOs
 * @owner: helps prevent removal of modules exporting active GPIOs
 * @request: optional hook for chip-specific activation, such as
 *    enabling module power and clock; may sleep
 * @free: optional hook for chip-specific deactivation, such as
 *    disabling module power and clock; may sleep
 * @direction_input: configures signal "offset" as input, or returns error
 * @get: returns value for signal "offset"; for output signals this
 *    returns either the value actually sensed, or zero
 * @direction_output: configures signal "offset" as output, or returns error
 * @set: assigns output value for signal "offset"
 * @to_irq: optional hook supporting non-static gpio_to_irq() mappings;
 *    implementation may not sleep
 * @dbg_show: optional routine to show contents in debugfs; default code
 *    will be used when this is omitted, but custom code can show extra
 *    state (such as pullup/pulldown configuration).
 * @base: identifies the first GPIO number handled by this chip; or, if
 *    negative during registration, requests dynamic ID allocation.
 * @ngpio: the number of GPIOs handled by this controller; the last GPIO
 *    handled is (base + ngpio - 1).
 * @can_sleep: flag must be set iff get()/set() methods sleep, as they
 *    must while accessing GPIO expander chips over I2C or SPI
 * @names: if set, must be an array of strings to use as alternative
 *      names for the GPIOs in this chip. Any entry in the array
 *      may be NULL if there is no alias for the GPIO, however the
 *      array must be @ngpio entries long.  A name can include a single printk
 *      format specifier for an unsigned int.  It is substituted by the actual
 *      number of the gpio.
 *
 * A gpio_chip can help platforms abstract various sources of GPIOs so
 * they can all be accessed through a common programing interface.
 * Example sources would be SOC controllers, FPGAs, multifunction
 * chips, dedicated GPIO expanders, and so on.
 *
 * Each chip controls a number of signals, identified in method calls
 * by "offset" values in the range 0..(@ngpio - 1).  When those signals
 * are referenced through calls like gpio_get_value(gpio), the offset
 * is calculated by subtracting @base from the gpio number.
 */
struct gpio_chip {
    const char        *label;
    struct device        *dev;
    struct module        *owner;

    int            (*request)(struct gpio_chip *chip,
                        unsigned offset);
    void            (*free)(struct gpio_chip *chip,
                        unsigned offset);

    int            (*direction_input)(struct gpio_chip *chip,
                        unsigned offset);
    int            (*get)(struct gpio_chip *chip,
                        unsigned offset);
    int            (*direction_output)(struct gpio_chip *chip,
                        unsigned offset, int value);
    int            (*set_debounce)(struct gpio_chip *chip,
                        unsigned offset, unsigned debounce);

    void            (*set)(struct gpio_chip *chip,
                        unsigned offset, int value);

    int            (*to_irq)(struct gpio_chip *chip,
                        unsigned offset);

    void            (*dbg_show)(struct seq_file *s,
                        struct gpio_chip *chip);
    int            base;
    u16            ngpio;
    const char        *const *names;
    unsigned        can_sleep:1;
    unsigned        exported:1;
};
/*
 * Following are the gpio banks in v210.
 *
 * The 'config' member when left to NULL, is initialized to the default
 * structure gpio_cfg in the init function below.
 *
 * The 'base' member is also initialized in the init function below.
 * Note: The initialization of 'base' member of s3c_gpio_chip structure
 * uses the above macro and depends on the banks being listed in order here.
 */
static struct s3c_gpio_chip s5pv210_gpio_4bit[] = {
    {
        .chip    = {
            .base    = S5PV210_GPA0(0),
            .ngpio    = S5PV210_GPIO_A0_NR,
            .label    = "GPA0",
            .to_irq = s5p_gpiolib_gpioint_to_irq,
        },
    }, {
        .chip    = {
            .base    = S5PV210_GPA1(0),
            .ngpio    = S5PV210_GPIO_A1_NR,
            .label    = "GPA1",
            .to_irq = s5p_gpiolib_gpioint_to_irq,
        },
    }, {
        .chip    = {
            .base    = S5PV210_GPB(0),
            .ngpio    = S5PV210_GPIO_B_NR,
            .label    = "GPB",
            .to_irq = s5p_gpiolib_gpioint_to_irq,
        },
    }, {
        .chip    = {
            .base    = S5PV210_GPC0(0),
            .ngpio    = S5PV210_GPIO_C0_NR,
            .label    = "GPC0",
            .to_irq = s5p_gpiolib_gpioint_to_irq,
        },
    }, {......
View Code

四. gpiolib使用方法

    4.1. 相關函數gpio_request/gpio_request_one&gpio_free

        4.1.1. gpio_request:驅動中要想使用某一個gpio,就必須先調用gpio_request接口來向內核的gpiolib部分申請,得到允許后才可以去使用這個gpio

        4.1.2. gpio_request_one:與gpio_request相似,只是在調用該函數同時多了一個設置參數,不同點具體查看內核源碼。

        4.1.3. gpio_free:對應gpio_request,用來釋放申請后用完了的gpio

    4.3. 相關函數gpio_request_array&gpio_free_array

        4.3.1. gpio_request_array:申請一組gpio。

        4.3.2. gpio_free_array:釋放一組gpio

    4.3. 相關函數gpiochip_is_requested

        4.3.1. gpiochip_is_requested:接口用來判斷某一個gpio是否已經被申請了

    4.4.相關文件目錄

        目錄和文件結構:

        mach-s5pv210/gpiolib.c s5pv210_gpiolib_init

        mach-s5pv210/include/mach/gpio.h#define S5PV210_GPA0(_nr)(S5PV210_GPIO_A0_START + (_nr))

        arch/arm/plat-samsung/gpiolib.c里面是210/6410這種4bit CON寄存器類型的操作方法

        arch/arm/plat-samsung/gpio.c里面是24XX這種2bit CON寄存器類型的操作方法

        drivers/gpio/gpiolib.c里面是內核開發者提供的gpiolib的驅動框架部分

五. 最終調試效果

[root@musk210 gpio]# cd /sys/class/leds/
[root@musk210 leds]# ls
led1    led2    led3    mmc0::  mmc1::  mmc2::  mmc3::
[root@musk210 leds]# cd led1
[root@musk210 led1]# echo 1 > brightness 
[ 8056.785758] s5pv210_led1_set successful 1
[root@musk210 led1]# echo 0 > brightness 
[ 8064.545061] s5pv210_led1_set successful 0
[root@musk210 led1]#
View Code

 

參考《朱老師.課件_5.4.驅動框架入門之LED》

索引文獻:https://blog.csdn.net/ultraman_hs/article/details/54952253

索引文獻:http://www.cnblogs.com/deng-tao/p/6366905.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM