- 論壇徽章:
- 0
|
平臺:迅為嵌入式linux開發(fā)板
Exynos4412 所有的 GPIO 都有固定的地址,為了方便操作這些 GPIO,Linux 內(nèi)核
在 gpio-exynos4.h 里面定義了一些 GPIO 的宏,例如:
#define EXYNOS4_GPA0(_nr) (EXYNOS4_GPIO_A0_START + (_nr))
#define EXYNOS4_GPA1(_nr) (EXYNOS4_GPIO_A1_START + (_nr))
#define EXYNOS4_GPB(_nr) (EXYNOS4_GPIO_B_START + (_nr))
.....................................
#define EXYNOS4_GPY5(_nr) (EXYNOS4_GPIO_Y5_START + (_nr))
#define EXYNOS4_GPY6(_nr) (EXYNOS4_GPIO_Y6_START + (_nr))
#define EXYNOS4_GPZ(_nr) (EXYNOS4_GPIO_Z_START + (_nr))
這些宏就是把每個 GPIO 的地址做了一下封裝,它的好處就是方便我們使用并且
根據(jù)宏的名字就能直觀的知道是在操作哪個 GPIO。
Linux 內(nèi)核中關(guān)于 GPIO 的驅(qū)動在 driver/gpio/gpio-exynos4.c 文件里面, 在這
個文件中 GPIO 驅(qū)動初始化入口函數(shù)是 exynos4_gpiolib_init, 因為這個文件同
時也支持 4210 的 GPIO,所以開始先初始化了一些通用的 GPIO (4412 和 4210 都
有的 GPIO) ,代碼如下:
chip = exynos4_gpio_common_4bit;
nr_chips = ARRAY_SIZE(exynos4_gpio_common_4bit);
for (i = 0; i < nr_chips; i++, chip++) {
if (chip->config == NULL)
chip->config = &gpio_cfg;
if (chip->base == NULL)
pr_err("No allocation of base address for [common gpio]");
}
samsung_gpiolib_add_4bit_chips(exynos4_gpio_common_4bit,
nr_chips);
變量 exynos4_gpio_common_4bit 是一個數(shù)組, 定義了一些通用的 GPIO, nr_chips
是記錄的 exynos4_gpio_common_4bit 數(shù)組里面素個數(shù)。
首先使用 for 循環(huán)遍歷 exynos4_gpio_common_4bit 所有的元素,為每個元素的
config 結(jié)構(gòu)賦值:
if (chip->config == NULL)
chip->config = &gpio_cfg;
gpio_cfg 是類型為 s3c_gpio_cfg 的結(jié)構(gòu)體,這個結(jié)構(gòu)體的定義如下:
struct s3c_gpio_cfg {
unsigned int cfg_eint;
s3c_gpio_pull_t (*get_pull)(struct s3c_gpio_chip *chip, unsigned
offs);
int (*set_pull)(struct s3c_gpio_chip *chip, unsigned offs,
s3c_gpio_pull_t pull);
unsigned (*get_config)(struct s3c_gpio_chip *chip, unsigned offs);
int (*set_config)(struct s3c_gpio_chip *chip, unsigned offs,
unsigned config);
};
通過上面的代碼我們可以看到這個結(jié)構(gòu)體里主要是一些函數(shù)指針,get_pull 是
獲取 GPIO 的上拉狀態(tài),set_pull 是設(shè)置 GPIO 上拉或下拉的,set_config 是設(shè)
置 GPIO 的工作模式,例如:輸出/輸入/其他功能。下面我們來看看 gpio_cfg
變量的定義,如下:
static struct s3c_gpio_cfg gpio_cfg = {
.set_config = s3c_gpio_setcfg_s3c64xx_4bit,
.set_pull = s3c_gpio_setpull_exynos4,
.get_pull = s3c_gpio_getpull_exynos4,
};
通過上面的代碼,可以看到分別對 gpio_cfg 結(jié)構(gòu)的三個函數(shù)指針賦值,這三個
函數(shù) 的 定義 是在 gpio-config.c 里面 實現(xiàn) 的 , 這個 文 件 在 內(nèi)核 源 碼
arch/arm/plat-samsung 目錄下,這三個函數(shù)的作用就是根據(jù)傳進(jìn)來的參數(shù),配
置 GPIO 相應(yīng)的寄存器,從而實現(xiàn)對 GPIO 的操作。
然后 我們 回到 gpio-exynos4.c , 接 著看 下 面 的代 碼, 完成 了
exynos4_gpio_common_4bit 中每個元素的 config 結(jié)構(gòu)賦值后, 接著會調(diào)用函數(shù)
samsung_gpiolib_add_4bit_chips(exynos4_gpio_common_4bit, nr_chips) 來
向系統(tǒng)注冊 GPIO 結(jié)構(gòu)體。代碼如下:
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);
}
}
上面的代碼主要有兩個函數(shù)組成分別是 samsung_gpiolib_add_4bit(chip) 和
s3c_gpiolib_add(chip), 首先我們來看下 samsung_gpiolib_add_4bit(chip)函
數(shù)的實現(xiàn):
void __init samsung_gpiolib_add_4bit(struct s3c_gpio_chip *chip)
{
chip->chip.direction_input = samsung_gpiolib_4bit_input;
chip->chip.direction_output = samsung_gpiolib_4bit_output;
chip->pm = __gpio_pm(&s3c_gpio_pm_4bit);
}
這個函數(shù)也是為函數(shù)指針賦值,direction_input 是設(shè)置 GPIO 為輸入模式,
direction_output 是設(shè)置 GPIO 為輸出。
s3c_gpiolib_add(chip)函數(shù)主要作用是給一些函數(shù)指針賦值,然后根據(jù)傳進(jìn)來
的參數(shù)把對應(yīng)的 GPIO 的信息保存到 gpio_desc 結(jié)構(gòu)里,gpio_desc 是內(nèi)核里面
定義的一個全局變量,用來保存每個 GPIO 的信息。 至此 GPIO 的驅(qū)動初始化就完
成了,其他它主要完成的功能就是為每個 GPIO 的結(jié)構(gòu)體里面的函數(shù)指針賦值,
最后把每個 GPIO 結(jié)構(gòu)信息保存到全局變量 gpio_desc 里面。
上面已經(jīng)完成了一些通用的 GPIO 驅(qū)動的初始化,我們在回到 gpio-exynos4.c,
下面是根據(jù) CPU 的型號初始化 CPU 特定的 GPIO 了,代碼如下:
/* Only 4210 GPIO part */
if (soc_is_exynos4210()) {
chip = exynos4210_gpio_4bit;
nr_chips = ARRAY_SIZE(exynos4210_gpio_4bit);
for (i = 0; i < nr_chips; i++, chip++) {
if (chip->config == NULL)
chip->config = &gpio_cfg;
if (chip->base == NULL)
pr_err("No allocation of base address [4210 gpio]");
}
samsung_gpiolib_add_4bit_chips(exynos4210_gpio_4bit,
nr_chips);
} else {
/* Only 4212/4412 GPIO part */
chip = exynos4212_gpio_4bit;
nr_chips = ARRAY_SIZE(exynos4212_gpio_4bit);
for (i = 0; i < nr_chips; i++, chip++) {
if (chip->config == NULL)
chip->config = &gpio_cfg;
if (chip->base == NULL)
pr_err("No allocation of base address [4212 gpio]");
}
samsung_gpiolib_add_4bit_chips(exynos4212_gpio_4bit,
nr_chips);
}
通過看上面的代碼,初始化過程與前面介紹的初始化通用 GPIO 原理是一樣的,
這里我們不在細(xì)介紹。對所有 GPIO 的初始化完成以后內(nèi)核中的其他驅(qū)動模塊
就可以方便的使用我們注冊到 gpio_desc 里面的 GPIO 了。內(nèi)核提供了幾個全局
函數(shù)來操作這些 GPIO:
int gpio_request(unsigned gpio, const char *label)
void gpio_free(unsigned gpio)
int s3c_gpio_setpull(unsigned int pin, s3c_gpio_pull_t pull)
int s3c_gpio_cfgpin(unsigned int pin, unsigned int config)
int gpio_direction_input(unsigned gpio)
int gpio_direction_output(unsigned gpio, int value)
gpio_request 函數(shù)是申請 GPIO 操作, 根據(jù)傳遞進(jìn)來的參數(shù) gpio, 會去全局變量
gpio_desc 里面找到對應(yīng)的 GPIO 結(jié)構(gòu),判斷 desc 的標(biāo)志位 flag 有沒有被設(shè)置
FLAG_REQUESTED,如果有設(shè)置說明其他地方在使用這個 GPIO,程序返回-EBUSY
錯誤,如果沒有設(shè)置就設(shè)置 flags 的標(biāo)記為 FLAG_REQUESTED。
gpio_free 函數(shù)是釋放 GPIO 操作,根據(jù)傳遞進(jìn)來的參數(shù),在 gpio_desc 全局變
量找到對應(yīng)的 GPIO 結(jié)構(gòu),清除掉 desc 的 flag 標(biāo)志變量的 FLAG_REQUESTED 位。
s3c_gpio_setpull 函數(shù)是設(shè)置 GPIO 的上拉或下拉的,變量 pull 的取值范圍如
下定義:
#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)
S3C_GPIO_PULL_NONE 是懸空
S3C_GPIO_PULL_DOWN 是下拉
S3C_GPIO_PULL_UP 是上拉
s3c_gpio_cfgpin 函數(shù)是設(shè)置 GPIO 的功能:輸入/輸出/其他功能,第二個參數(shù)
config 取值范圍如下:
#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))
S3C_GPIO_INPUT 是輸入模式,S3C_GPIO_OUTPUT 是輸出模式,S3C_GPIO_SFN(x)
是其他模式,例如中斷模式等。
gpio_direction_input 函數(shù)設(shè)置 GPIO 是輸入功能。
gpio_direction_output 設(shè)置 GPIO 輸出,第二個參數(shù) value 取值 0 或 1,0 代表
輸出低電平,1 代表輸出高電平。
下面我們來看幾個 GPIO 操作的例子:
if (gpio_request(EXYNOS4_GPX3(3), "MPU6050 INT"))
printk(KERN_WARNING "MPU6050 INT(GPX3.3) Port request error!!!\n");
else{
s3c_gpio_setpull(EXYNOS4_GPX3(3), S3C_GPIO_PULL_NONE);
s3c_gpio_cfgpin(EXYNOS4_GPX3(3), S3C_GPIO_SFN(0));
gpio_direction_input(EXYNOS4_GPX3(3));
gpio_free(EXYNOS4_GPX3(3));
}
上面的代碼是設(shè)置 GPIO 引腳 GPX3_3 為輸入模式,懸空。
err = gpio_request_one(EXYNOS4_GPX0(0), GPIOF_IN, "mcp251x_INT");
if (err) {
printk(KERN_ERR "failed to request mcp251x_INT\n");
return -1;
}
s3c_gpio_cfgpin(EXYNOS4_GPX0(0), S3C_GPIO_SFN(0xf));
s3c_gpio_setpull(EXYNOS4_GPX0(0), S3C_GPIO_PULL_NONE);
gpio_free(EXYNOS4_GPX0(0));
上面的代碼設(shè)置 GPIO 引腳 GPX0_0 為中斷模式。
if(gpio_request(EXYNOS4_GPK1(0), "GPK1_0"))
{
printk(KERN_ERR "failed to request GPK1_0 for "
"USI control\n");
return err;
}
gpio_direction_output(EXYNOS4_GPK1(0), 1);
s3c_gpio_cfgpin(EXYNOS4_GPK1(0), S3C_GPIO_OUTPUT);
gpio_free(EXYNOS4_GPK1(0));
上面的代碼設(shè)置 GPIO 引腳 GPK1_0 為輸出模式,并且輸出高電平。
嵌入式linux開發(fā)板之iTOP-4412開發(fā)板的 GPIO 驅(qū)動就介紹到這里,大家有興趣的話可以去內(nèi)核里面詳細(xì)的查看一下整個驅(qū)動的詳細(xì)實現(xiàn)。 |
|