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