最近要深一步用到GPIO口控制,寫個博客記錄下Kernel層的GPIO學習過程!
一、概念
General Purpose Input Output (通用輸入/輸出)簡稱為GPIO,或 總線擴展器。也就是芯片的引腳,當微控制器或芯片組沒有足夠的I/O端口,或當系統需要采用遠端串行通信或控制時,GPIO產品能夠提供額外的控制和監視功能。通常在ARM里,所有I/O都是通用的,
每個GPIO端口包含8個管腳,如PA端口是PA0~PA7,而且GPIO口至少有兩個寄存器,即"通用IO控制寄存器"與"通用IO數據寄存器"。數據寄存器的各位都直接引到芯片外部,而對這種寄存器中每一位的作用,即每一位的信號流通方向,則可以通過控制寄存器中對應位獨立地加以設置。比如,可以設置某個引腳的屬性為輸入、輸出或其他特殊功能。
常用的應該是:高阻輸入、推挽輸出、開漏輸出。
通俗理解為 :
高阻輸入—— 保持高阻抗狀態,徹底斷開輸出,避免干擾,對總線狀態不起作用,此時總線可由其他器件占用。
推挽輸出——可以輸出高,低電平,連接數字器件。
開漏輸出——輸出端相當於三極管的集電極. 要得到高電平狀態需要上拉電阻才行。
軟件上就是通過設置IO口的模式,然后控制IO的上拉下拉,寫入對應寄存器,通過寄存器控制電路:
上拉寄存器是控制對應端口上拉使能(DE)的。當對應位為0時,設置對應引腳上拉使能,為1時,禁止對應引腳上拉使能。如果上拉寄存器使能,無論引腳功能寄存器如何設置(輸入,輸出,數據,中斷等),對應引腳輸出高電平。
上拉是一個電阻接到一個電壓,其實就是增強IO的驅動能力,IO口是高電平。
下拉是一個電阻接到地,保證IO口是低電平。
二、kernel層調用接口實現
GPIO操作,在Kernel 2.6.32版本以上提供了gpio口管理的庫文件/kernel/drivers/gpio/gpiolib.c,里面就是我們需要的接口函數實現!
幾個主要的方法:
申請一個pin腳作為gpio口,命名為 * label,如果經過判斷空閑的 申請成功了做一些初始的bit位設置。
int gpio_request(unsigned gpio, const char *label) { struct gpio_desc *desc; struct gpio_chip *chip; int status = -EINVAL; unsigned long flags; spin_lock_irqsave(&gpio_lock, flags); ... if (test_and_set_bit(FLAG_REQUESTED, &desc->flags) == 0) ... }
釋放這個gpio口,還原bit。
void gpio_free(unsigned gpio) { unsigned long flags; struct gpio_desc *desc; struct gpio_chip *chip; might_sleep(); ... clear_bit(FLAG_ACTIVE_LOW, &desc->flags); clear_bit(FLAG_REQUESTED, &desc->flags); clear_bit(FLAG_OPEN_DRAIN, &desc->flags); clear_bit(FLAG_OPEN_SOURCE, &desc->flags); ... }
設置gpio口為輸入模式:
int gpio_direction_input(unsigned gpio) { unsigned long flags; struct gpio_chip *chip; struct gpio_desc *desc = &gpio_desc[gpio]; int status = -EINVAL; spin_lock_irqsave(&gpio_lock, flags); if (!gpio_is_valid(gpio)) goto fail; chip = desc->chip; ... status = chip->direction_input(chip, gpio); ... }
其中的chip->direction_input(chip, gpio)為實現!
設置gpio口為輸出模式 value為初始值 0為高電平/1為低電平:
int gpio_direction_output(unsigned gpio, int value) { unsigned long flags; struct gpio_chip *chip; struct gpio_desc *desc = &gpio_desc[gpio]; int status = -EINVAL; /* Open drain pin should not be driven to 1 */ if (value && test_bit(FLAG_OPEN_DRAIN, &desc->flags)) return gpio_direction_input(gpio); ... status = chip->direction_output(chip, gpio, value); ... }
其中的chip->direction_output(chip, gpio, value)為實現!
設置gpio口的值:
void __gpio_set_value(unsigned gpio, int value) { struct gpio_chip *chip; chip = gpio_to_chip(gpio); WARN_ON(chip->can_sleep); trace_gpio_value(gpio, 0, value); if (test_bit(FLAG_OPEN_DRAIN, &gpio_desc[gpio].flags)) _gpio_set_open_drain_value(gpio, chip, value); else if (test_bit(FLAG_OPEN_SOURCE, &gpio_desc[gpio].flags)) _gpio_set_open_source_value(gpio, chip, value); else chip->set(chip, gpio - chip->base, value); }
獲取gpio口的值:
int __gpio_get_value(unsigned gpio) { struct gpio_chip *chip; int value; chip = gpio_to_chip(gpio); WARN_ON(chip->can_sleep); value = chip->get ? chip->get(chip, gpio - chip->base) : 0; trace_gpio_value(gpio, 1, value); return value; }
對於有些掛載在I2C,SPI總線上的擴展GPIO,讀寫操作可能會導致睡眠,因此不能在中斷函數中
使用。使用下面的函數以區別於正常的GPIO
int gpio_get_value_cansleep(unsigned gpio);//讀GPIO void gpio_set_value_cansleep(unsigned gpio, int value);//寫GPIO
三、gpiolib.c關聯芯片接口
以上為gpiolib.c的基本方法都是向下調用到對應芯片的gpio實現!
所以每個方法里面的實現都是通過 struct gpio_chip*chip 這個指針調用結構體中關聯的相關接口!
下面是gplolib怎么關聯到特定的芯片(mach-at91)的幾個主要方法:
mach-at91的/kernel/arch/arm/mach-at91/gpio.c中的
static struct at91_gpio_chip gpio_chip[] = { AT91_GPIO_CHIP("A", 0x00 + PIN_BASE, 32), AT91_GPIO_CHIP("B", 0x20 + PIN_BASE, 32), AT91_GPIO_CHIP("C", 0x40 + PIN_BASE, 32), AT91_GPIO_CHIP("D", 0x60 + PIN_BASE, 32), AT91_GPIO_CHIP("E", 0x80 + PIN_BASE, 32), };
gpiolib.c中的接口與mach-at91函數接口對應關系如下:
#define AT91_GPIO_CHIP(name, base_gpio, nr_gpio) \ { \ .chip = { \ .label = name, \ .direction_input = at91_gpiolib_direction_input, \ .direction_output = at91_gpiolib_direction_output, \ .get = at91_gpiolib_get, \ .set = at91_gpiolib_set, \ .dbg_show = at91_gpiolib_dbg_show, \ .base = base_gpio, \ .ngpio = nr_gpio, \ }, \ }
從上面可以看到 在gpiolib.c中的方法調用芯片(mach-at91)的映射關系:
比如:gpio_direction_output() 設置gpio口為輸出模式中最終的實現調用chip->direction_output(chip, gpio, value)。
從上面可以看出來,也就是調用mach-at91的at91_gpiolib_direction_output()!
其它的類似。
gpiolib.c中的
int gpiochip_add(struct gpio_chip *chip) { unsigned long flags; int status = 0; unsigned id; int base = chip->base; if ((!gpio_is_valid(base) || !gpio_is_valid(base + chip->ngpio - 1)) && base >= 0) { status = -EINVAL; goto fail; } spin_lock_irqsave(&gpio_lock, flags); .... }
添加到結構體:
struct gpio_desc { struct gpio_chip *chip; unsigned long flags; /* flag symbols are bit numbers */ #define FLAG_REQUESTED 0 #define FLAG_IS_OUT 1 #define FLAG_RESERVED 2 ... }
這就保存到了 chip 指針。
這一部分在Kernel初始化的時候調用執行!
四、芯片(mach-at91)gpio口的接口
由gpiolib.c根據映射調用對應接口
static int at91_gpiolib_direction_output(struct gpio_chip *chip, unsigned offset, int val) { struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip); void __iomem *pio = at91_gpio->regbase; unsigned mask = 1 << offset; __raw_writel(mask, pio + (val ? PIO_SODR : PIO_CODR)); __raw_writel(mask, pio + PIO_OER); return 0; }
定義為輸出模式,初始設置val 上拉還是下拉 , 寫入數據寄存器。
#define PIO_SODR 0x30 /* Set Output Data Register */ #define PIO_CODR 0x34 /* Clear Output Data Register */ #define PIO_ODR 0x14 /* Output Disable Register */ #define PIO_PDSR 0x3c /* Pin Data Status Register */
同樣設置為輸入:
static int at91_gpiolib_direction_input(struct gpio_chip *chip, unsigned offset) { struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip); void __iomem *pio = at91_gpio->regbase; unsigned mask = 1 << offset; __raw_writel(mask, pio + PIO_ODR); return 0; }
設置GPIO口的value 值 0或者1,已經設置為輸出模式下:
static void at91_gpiolib_set(struct gpio_chip *chip, unsigned offset, int val) { struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip); void __iomem *pio = at91_gpio->regbase; unsigned mask = 1 << offset; __raw_writel(mask, pio + (val ? PIO_SODR : PIO_CODR)); }
讀寄存器獲取該GPIO口的值:
static int at91_gpiolib_get(struct gpio_chip *chip, unsigned offset) { struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip); void __iomem *pio = at91_gpio->regbase; unsigned mask = 1 << offset; u32 pdsr; pdsr = __raw_readl(pio + PIO_PDSR); return (pdsr & mask) != 0; }
大體流程就這樣了。
撰寫不易,轉載請注明出處http://blog.csdn.net/jscese/article/details/16823519