Linux的GPIO驱动模型框架为structgpio_chip(include/asm-generic/gpio.h)。依据此框架,Linux自身提供了一套操控函数(drivers/gpio/gpiolib.c)。而SAMSUNG根据自身SoC的GPIO寄存器特性,将该框架封装,并定义了一套自己的GPIO操控函数(drivers/gpio/gpio-samsung.c)。
Linux对GPIO以数组的思想进行管理,每个GPIO管脚对应一个序号(这里并非指数组元素),连续的若干个序号组成一个GPIO组(例如GPA、GPB等等),而这每一个组就对应一个struct gpio_chip。此种管理方式相对于s3c6410体现于arch/arm/mach-s3c64xx/include/mach/gpio.h文件中,其中形如S3C64XX_GPIO_*_NR的宏定义了每组GPIO有多少个管脚;形如S3C64XX_GPIO_*_START的宏定义了每组GPIO第一个管脚的序号;形如S3C64XX_GP*(_nr)的宏隐藏了序号,方便了程序调用(例如S3C64xx_GPI(7)就代表了第I组GPIO的第7个管脚的序号)。
下面来看看Linux的GPIO驱动模型框架,并简要叙述其中部分成员:
/** * 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; #if defined(CONFIG_OF_GPIO) /* * If CONFIG_OF is enabled, then all GPIO controllers described in the * device tree automatically may have an OF translation */ struct device_node *of_node; int of_gpio_n_cells; int (*of_xlate)(struct gpio_chip *gc, const struct of_phandle_args *gpiospec, u32 *flags); #endif };
base存储的就是该组GPIO第一个管脚的序号。ngpio存储该组GPIO共有多少个管脚。direction_input和direction_output函数指针分别指向将GPIO管脚配置成输入和输出的函数。set和get函数指针分别指向设置和获取GPIO管脚值的函数。request和free函数指针分别指向申请和释放GPIO管脚使用权的函数。
我们首先来看看Linux基于该框架为我们提供了什么(由于知识面有限,在此仅分析其中部分函数):
structgpio_desc {
structgpio_chip *chip;
unsignedlong flags;
/* flag symbols are bit numbers */
#define FLAG_REQUESTED 0
#define FLAG_IS_OUT 1
#define FLAG_RESERVED 2
#define FLAG_EXPORT 3 /* protected bysysfs_lock */
#define FLAG_SYSFS 4 /* exported via/sys/class/gpio/control */
#define FLAG_TRIG_FALL 5 /* trigger on fallingedge */
#define FLAG_TRIG_RISE 6 /* trigger on risingedge */
#define FLAG_ACTIVE_LOW 7 /* sysfs value hasactive low */
#define FLAG_OPEN_DRAIN 8 /* Gpio is opendrain type */
#define FLAG_OPEN_SOURCE 9 /* Gpio is open source type */
#define ID_SHIFT 16 /* add new flags beforethis one */
#define GPIO_FLAGS_MASK ((1 << ID_SHIFT) - 1)
#define GPIO_TRIGGER_MASK (BIT(FLAG_TRIG_FALL) | BIT(FLAG_TRIG_RISE))
#ifdef CONFIG_DEBUG_FS
constchar *label;
#endif
};
static struct gpio_desc gpio_desc[ARCH_NR_GPIOS];
该全局数组主要用于描述每个GPIO脚当前的一些特性,用unsigned long flags的各bit指明。ARCH_NR_GPIOS表示总GPIO管脚数。结构体中的部分标识位(FLAG_*)会在接下来的某些函数说明中提到。
int gpiochip_add(struct gpio_chip *chip)
int gpiochip_remove(struct gpio_chip *chip)
每个struct gpio_chip表明一个GPIO组,该组中包含着几个GPIO管脚。那么gpiochip_add()就是将这几个管脚在gpio_desc全局数组中的位置的chip成员指向这个struct gpio_chip。这样在使用该管脚时,就能找到该管脚所在GPIO组所对应的struct gpio_chip。该函数最后调用of_gpiochip_add()向内核注册该struct gpio_chip。
有注册就有注销,gpiochip_remove()做相反的操作。调用of_gpiochip_remove()从内核中注销该struct gpio_chip,并将该struct gpio_chip所包含的几个管脚在全局数组gpio_desc中所对应的chip分别赋为NULL。
int gpio_request(unsigned gpio, const char*label)
int gpio_request_one(unsigned gpio, unsignedlong flags, const char *label)
int gpio_request_array(const struct gpio*array, size_t num)
static int gpio_ensure_requested(structgpio_desc *desc, unsigned offset)
const char *gpiochip_is_requested(structgpio_chip *chip, unsigned offset)
void gpio_free(unsigned gpio)
void gpio_free_array(const struct gpio *array,size_t num)
gpio_request()申请某个GPIO管脚的使用权。
struct gpio_desc *desc;
struct gpio_chip *chip;
desc = &gpio_desc[gpio];
chip = desc->chip;
这段代码便体现了gpio_desc全局数组的作用,只要简单传入GPIO的序号(gpio),就可以方便的找到该GPIO管脚对应的struct gpio_chip。该函数还置位了gpio_desc中的FLAG_REQUESTED位,表明该GPIO管脚已被分配使用权。最后函数调用struct gpio_chip中的request函数指针,执行具体的申请使用权函数,该函数应由板卡驱动实现。
gpio_request_one()首先调用gpio_request()申请使用权,接着根据传入的flags对该GPIO管脚进行一些初始化配置。
gpio_request_array()通过多次调用gpio_request_one()申请多个GPIO(并不一定是GPIO组)的使用权以及初始化这些GPIO。
gpio_ensure_requested()一般在配置GPIO管脚前调用,它查看你是否request过该管脚。如果没有,则打印警告(该函数却并没有实际调用gpio_request作为弥补)。
gpiochip_is_requested()类似于gpio_ensure_requested(),它一般用在GPIO调试信息输出函数中。传入的GPIO序号非法、未注册该GPIO以及未申请该GPIO的使用权,该函数都会返回NULL,否则返回一个无意义的字符串。
同样,有申请就有释放。gpio_free()调用struct gpio_chip中的free函数指针,执行具体的释放使用权函数,该函数同样应由板卡驱动实现。
gpio_free_array()通过多次调用gpio_free()释放多个GPIO的使用权(并不一定是GPIO组)。
int __init gpiochip_reserve(int start, intngpio)
该函数通过设置全局数组gpio_desc对应序号的FLAG_RESERVED标志位预留一段连续的序号(start ~ start+ngpio-1)。如果其中有任意序号被注册或是预留过,则返回错误,成功返回0。该函数可以保护一段GPIO序号不被自动分配(关于GPIO序号的自动分配会在接下来的gpiochip_find_base()中提及),注释中写的很明白,例如GPx的驱动由于某些原因需要延时加载,则属于该GPx组的序号可以先进行预留。
struct gpio_chip *gpiochip_find(void *data, int(*match)(struct gpio_chip *chip, void *data))
static int gpiochip_find_base(int ngpio)
gpiochip_find()根据提供的data找寻匹配的struct gpio_chip,match函数指针指向具体的找寻函数,该函数应由板卡驱动实现(一般采用如下方法:strcmp(chip->label, data))。
gpiochip_find_base()为若干个GPIO管脚自动分配一段连续的未被注册或未被预留的序号。现在回头来看看arch/arm/mach-s3c64xx/include/mach/gpio.h文件尾部,ARCH_NR_GPIOS代表该板卡支持的GPIO总数量,而其中不仅包含实际的GPIO序号(GPIO_BOARD_START),还包含了一些额外的序号(BOARD_NR_GPIOS)。例如使用热插拔设备时,这些额外的序号就可以被用来自动分配。实际的序号在前,额外的序号在后,这也就解释了为何函数中从最后一个序号开始寻找(for (i = ARCH_NR_GPIOS - 1; i >= 0 ; i--))。
int gpio_direction_input(unsigned gpio)
int gpio_direction_output(unsigned gpio, intvalue)
gpio_direction_input()设置GPIO为输入模式,它通过调用struct gpio_chip中的direction_input函数指针调用实际的设置函数。在函数中可以看到,全局数组gpio_desc对应序号的FLAG_IS_OUT标志位为0表示该GPIO管脚为输入模式,那么标志位为1则代表输出模式。
gpio_direction_output()将GPIO设置为输出模式,依旧是通过调用struct gpio_chip中的direction_output函数指针的方式调用实际的设置函数,最后置位FLAG_IS_OUT。不过输出有两种特殊的模式,全局数组gpio_desc中的FLAG_OPEN_DRAIN和FLAG_OPEN_SOURCE标志位分别指定这两种模式(第一个是开漏模式,第二个不太清楚 +_+)。它们分别不能被设置为1和0,如果强行这么设置,函数中会调用gpio_direction_input()将该GPIO管脚设置为输入模式。
int gpio_set_debounce(unsigned gpio, unsigneddebounce)
gpio_set_debounce()设定抖动判定时间(微秒)。struct gpio_chip中有该函数指针。
void __gpio_set_value(unsigned gpio, intvalue)
static void _gpio_set_open_drain_value(unsignedgpio, struct gpio_chip *chip, int value)
static void_gpio_set_open_source_value(unsigned gpio, struct gpio_chip *chip, int value)
int __gpio_get_value(unsigned gpio)
这些函数分别调用struct gpio_chip中的set和get函数指针去调用具体的函数。在设置输出模式的GPIO管脚的值时,既可以使用不同模式的设置函数(_gpio_set_open_drain_value()和_gpio_set_open_source_value()),也可以使用通用模式的设置函数__gpio_set_value()(该函数会根据全局数组gpio_desc中的标志位调用对应的设置函数)。
未完待续……