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);
intbase;
u16ngpio;
const char*const *names;
unsignedcan_sleep:1;
unsignedexported: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中的标志位调用对应的设置函数)。
未完待续……