gpiolib庫詳解

/************************************************************************************html

*本文爲我的學習記錄,若有錯誤,歡迎指正。linux

*本文參考資料: 
數組

*        https://www.cnblogs.com/wenhuisun/archive/2013/04/11/3013951.html
函數

*        http://www.javashuo.com/article/p-odvhciht-bw.html
學習

*        http://www.javashuo.com/article/p-drbohsrg-ne.htmlui

************************************************************************************/spa

1. gpiolib庫簡介

linux中從2.6.35之後就開始有gpiolib庫了,gpiolib的做用是對全部的gpio實行統一管理,由於驅動在工做的時候,會出現好幾個驅動共同使用同一個gpio的狀況;這會形成混亂。因此內核提供了一些方法來管理gpio資源。.net

2. gpiolib庫的創建

gpiolib庫創建的目標函數:code

//所在文件:/kernel/arch/arm/mach-s5pv210/mach-smdkc110.c
static void __init smdkc110_map_io(void)
{
    ......
    s5pv210_gpiolib_init();
    ......
}


//所在文件:/kernel/arch/arm/mach-s5pv210/gpiolib.c
__init int s5pv210_gpiolib_init(void)
{
    struct s3c_gpio_chip *chip = s5pv210_gpio_4bit;
    int nr_chips = ARRAY_SIZE(s5pv210_gpio_4bit);
    int i = 0;
        
    for (i = 0; i < nr_chips; i++, chip++) 
    {
        if (chip->config == NULL)
            chip->config = &gpio_cfg;
        if (chip->base == NULL)
            chip->base = S5PV210_BANK_BASE(i);
    }

    samsung_gpiolib_add_4bit_chips(s5pv210_gpio_4bit, nr_chips);

    return 0;
}

 s5pv210_gpiolib_init()函數分析:htm

(1)gpiolib庫的初始化實質就是對s3c_gpio_chip結構體數組進行賦值;struct s3c_gpio_chip用以描述一個GPIO端口。

struct s3c_gpio_chip 
{
    struct gpio_chip    chip;  
    struct s3c_gpio_cfg *config;
    struct s3c_gpio_pm  *pm;
    void __iomem        *base;  //存放GPIO的虛擬地址
    int            eint_offset;
    spinlock_t         lock;
#ifdef CONFIG_PM
    u32            pm_save[7];
#endif
};

struct gpio_chip 
{
  ......
  const char  *label; //GPIO端口名稱
  int  base;         //GPIO端口號
  ......
}

(2)內核中創建了static struct s3c_gpio_chip s5pv210_gpio_4bit[] 這個數組,將全部的gpio的.chip結構體中的一些元素初始化,這個數組的全部元素是與數據手冊中的全部gpio是一一對應的。

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是GPIO的編號,用宏定義表示。S5PV210_GPA0(0)宏解析以下,即S5PV210_GPA0(0) = 0,S5PV210_GPA0(1) = 1。

#define S5PV210_GPA0(_nr) (S5PV210_GPIO_A0_START + (_nr))
#define S5PV210_GPA1(_nr) (S5PV210_GPIO_A1_START + (_nr)) 

S5PV210_GPIO_A0_START = 0,
S5PV210_GPIO_A1_START = S5PV210_GPIO_NEXT(S5PV210_GPIO_A0),

#define S5PV210_GPIO_NEXT(__gpio) \
((__gpio##_START) + (__gpio##_NR) + CONFIG_S3C_GPIO_SPACE + 1) 

#define S5PV210_GPIO_A0_NR (8)
#define S5PV210_GPIO_A1_NR (4)
#define S5PV210_GPIO_B_NR  (8)
#define S5PV210_GPIO_C0_NR (5)

(3)chip->base = S5PV210_BANK_BASE(i),將GPIO的虛擬地址寫入。

 //每一個gpio的地址差0x20
#define S5PV210_BANK_BASE(bank_nr) (S5P_VA_GPIO + ((bank_nr) * 0x20)

(4)samsung_gpiolib_add_4bit_chips()函數的做用是將全部GPIO向內核註冊。註冊的實質是:在linux內核中有一個gpio_desc結構體數組,註冊就是把咱們封裝的gpio的全部信息的結構體放到數組的格子中。

void __init samsung_gpiolib_add_4bit_chips(struct s3c_gpio_chip *chip, int nr_chips)
{
    for (; nr_chips > 0; nr_chips--, chip++) 
    {
        samsung_gpiolib_add_4bit(chip);
        s3c_gpiolib_add(chip);
    }
}

3. gpiolib庫的使用

(1)申請GPIO

/*
*參數:
*        unsigned gpio:GPIO編號
*        const char *label:GPIO名稱
*返回值:
*        返回值爲0,GPIO申請成功;不然,GPIO申請失敗
*/
int gpio_request(unsigned gpio, const char *label); 

(2)設置GPIO方向

1)設置爲輸入

/*
*參數:
*        unsigned gpio:GPIO編號
*返回值:
*        返回值爲<0,設置失敗
*/
int gpio_direction_input(unsigned gpio);

2)設置爲輸出

/*
*參數:
*        unsigned gpio:GPIO編號
*        int value:GPIO輸出值
*返回值:
*        返回值爲<0,設置失敗
*/
int gpio_direction_output(unsigned gpio, int value);

(3)獲取/設置GPIO的值

a. 可睡眠

對於有些掛載在I2C,SPI總線上的擴展GPIO,讀寫操做可能會致使睡眠,所以不能在中斷函數中使用可睡眠操做。使用下面的函數以區別於正常的GPIO:

1)獲取GPIO的值

/*
*參數:
*        unsigned gpio:GPIO編號
*返回值:
*        返回GPIO的值,0或1
*/
int gpio_get_value_cansleep(unsigned gpio);

2)設置GPIO的值

/*
*參數:
*        unsigned gpio:GPIO編號
*        int value:GPIO設置值
*返回值:
*        無
*/
void gpio_set_value_cansleep(unsigned gpio, int value);

b. 不可睡眠

1)獲取GPIO的值

/*
*參數:
*        unsigned gpio:GPIO編號
*返回值:
*        返回GPIO的值,0或1
*/
int gpio_get_value(unsigned gpio);

2)設置GPIO的值

/*
*參數:
*        unsigned gpio:GPIO編號
*        int value:GPIO設置值
*返回值:
*        無
*/
void gpio_set_value(unsigned gpio, int value);

(4)釋放GPIO

/*
*參數:
*        unsigned gpio:GPIO編號
*返回值:
*        無
*/
void gpio_free(unsigned gpio);

(5)批量初始化/釋放GPIO

1)批量初始化GPIO

/*
*參數:
*        unsigned gpio  *array:GPIO數組(多個GPIO編號)
*        size_t num:GPIO數組大小,GPIO即的個數
*返回值:
*        返回0,初始化成功;不然,初始化失敗
*/
int gpio_request_array(struct gpio *array, size_t num);

2)批量釋放GPIO

/*
*參數:
*        unsigned gpio  *array:GPIO數組(多個GPIO編號)
*        size_t num:GPIO數組大小,GPIO即的個數
*返回值:
*        無
*/
void gpio_free_array(struct gpio *array, size_t num);

 4. S3C平臺的GPIO操做接口

kernel/arch/arm/plat-s3c/include/plat/gpio-cfg.h文件中提供了S3C平臺的GPIO操做接口,如下列舉一些經常使用的GPIO操做接口。

(1)GPIO的工做模式設置

Linux內核中GPIO的工做模式的定義。

/*
*GPIO的工做模式定義
*/
#define S3C_GPIO_INPUT    (S3C_GPIO_SPECIAL(0))   //輸出模式
#define S3C_GPIO_OUTPUT   (S3C_GPIO_SPECIAL(1))   //輸入模式
#define S3C_GPIO_SFN(x)   (S3C_GPIO_SPECIAL(x))   //其餘模式,根據參數x決定

S3C_GPIO_SFN(x)中x的值對應的功能可經過數據手冊來查閱。譬如,根據D5PV210的數據手冊可知:

/*
*x = 0,GPH0_0爲輸入模式
*x = 1,GPH0_0爲輸出模式
*x = 0x0f,GPH0_0爲外部中斷模式
*/

S3C_GPIO_SFN(0)   //輸入模式 
S3C_GPIO_SFN(1)   //輸出模式 
S3C_GPIO_SFN(0x0f)//外部中斷模式 

1)s3c_gpio_cfgpin()設置指定引腳的工做模式

/*
*功能:設置指定引腳的工做模式
*參數:
*        unsigned int pin:須要設置的引腳號
*        unsigned int to:須要設置的工做模式
*/
int s3c_gpio_cfgpin(unsigned int pin, unsigned int to);

s3c_gpio_cfgpin(S5PV210_GPA0(0), S3C_GPIO_SFN(0));   //設置GPH0_0爲輸入模式 
s3c_gpio_cfgpin(S5PV210_GPA0(0), S3C_GPIO_SFN(1));   //設置GPH0_0爲輸出模式 
s3c_gpio_cfgpin(S5PV210_GPA0(0), S3C_GPIO_SFN(0x0f));//設置GPH0_0爲外部中斷模式

2)s3c_gpio_getcfg()讀取指定引腳的設置值

/*
*功能:讀取指定引腳的設置值
*參數:
*        unsigned int pin:須要獲取的引腳號
*返回值:獲取G引腳工做模式的值
*/
unsigned s3c_gpio_getcfg(unsigned int pin);

 3)s3c_gpio_cfgin_range()批量設置多個引腳的工做模式

/*
*功能:批量設置多個引腳的工做模式
*參數:
*        unsigned int start:起始引腳號
*        unsigned int nr:   須要設置的引腳個數
*        unsigned int cfg:  須要設置的工做模式
*/
int s3c_gpio_cfgpin_range(unsigned int start, unsigned int nr, unsigned int cfg);

 (2)GPIO的上下拉設置

 Linux內核中,上下拉的狀態值的定義。

#define S3C_GPIO_PULL_NONE  ((__force s3c_gpio_pull_t)0x00)  //關閉,上拉下拉都關閉
#define S3C_GPIO_PULL_DOWN  ((__force s3c_gpio_pull_t)0x01)  //下拉
#define S3C_GPIO_PULL_UP    ((__force s3c_gpio_pull_t)0x02)  //上拉

 1)s3c_gpio_setpull()設置GPIO引腳的上下拉使能

/*
*功能:設置單個引腳的上下拉模式
*參數:
*        unsigned int pin:須要設置的引腳
*        s3c_gpio_pull_t pull:上下拉的狀態值
*/
int s3c_gpio_setpull(unsigned int pin, s3c_gpio_pull_t pull);

 2)s3c_gpio_getpull()讀取指定引腳的上下拉狀態

/*
*功能:讀取指定引腳的上下拉狀態
*參數:
*        unsigned int pin:須要讀取的引腳
*返回值:        
*        s3c_gpio_pull_t :上下拉的狀態值
*/
s3c_gpio_pull_t s3c_gpio_getpull(unsigned int pin);

3) s3c_gpio_cfgall()批量設置多個引腳的工做模式和上下拉狀態

/*
*功能:批量設置多個引腳的工做模式和上下拉狀態
*參數:
*       unsigned int start:  起始引腳號
*       unsigned int nr:     須要設置的引腳個數
*       unsigned int cfg:    須要設置的引腳工做模式
*       s3c_gpio_pull_t pull:須要設置的引腳的上下拉狀態
*/
int s3c_gpio_cfgall_range(unsigned int start, unsigned int nr, unsigned int cfg, s3c_gpio_pull_t pull);
相關文章
相關標籤/搜索