GPIO开发是Linux开发不可或缺的重要部分,它是ARM开发中最普遍也是最基础的知识。
在ARM开发过程中,引脚复用严重,导致问题的原因软硬件并存,因此在实际的开发中常常需要使用GPIO来探测所配置的路径或设备是否畅通,可用,以此来判定是设备的问题,还是驱动的问题或是程序的问题。
在Linux内核中提供一个标准的GPIO LIB框架,它位于:
--<*>Device Drivers
--<*>GPIO Support
--
GPIO Support选项的选入,内核会将标准GPIO库进行编译。标准GPIO库的源码位于内核中的如下位置:
/driver/gpio/gpiolib.c
在该文件中,提供如下的接口函数用于配置和操作GPIO:
int gpio_request(unsigned gpio, const char *label); //获取GPIO Pin的使用权,并为该Pin命名为label。
void gpio_free(unsigned gpio); //释放GPIO Pin的使用权。
int gpio_direction_input(unsigned gpio); //设置GPIO Pin为输入模式。
int gpio_direction_output(unsigned gpio, int value);//设置GPIO Pin为输出模式,并指定输出值value。
int gpio_get_value(unsigned gpio); //获得 GPIO Pin 上的电平。
void gpio_set_value(unsigned gpio, int value); //设置 GPIO Pin 上的电平。
int gpio_to_irq(unsigned gpio); //通过获得GPIO Pin 对应的 irq number。
对于上述函数的gpio参数的计算方式为:
GPIOx_n-->gpio=x*32+n。
上面主要是对接口的介绍,下面我们来对源码进行分析,分析源码从什么地方开始呢?就从第一个调用的函数开始吧!
Function1:int gpio_request(unsigned gpio,const char *label):
Note: 该函数调用可能会失败,因此必须检测其返回值。
-
int gpio_request(unsigned gpio, const char *label)
-
{
-
struct gpio_desc *desc; //gpio pin结构体指针,参见下面相关数据结构描述。
-
struct gpio_chip *chip; //gpio pin controler结构体指针,参见下面相关数据结构描述
-
int status = -EINVAL;
-
unsigned long flags;
-
-
spin_lock_irqsave(&gpio_lock, flags);
-
-
if (!gpio_is_valid(gpio)) //检测argument gpio的合法性。
-
goto done;
-
desc = &gpio_desc[gpio]; //从gpio数组gpio_desc从获取对应的Pin结构体。
-
chip = desc->chip; //获取gpio pin的控制器
-
if (chip == NULL)
-
goto done;
-
-
if (!try_module_get(chip->owner))
-
goto done;
-
-
/* NOTE: gpio_request() can be called in early boot,
-
* before IRQs are enabled, for non-sleeping (SOC) GPIOs.
-
*/
-
-
if (test_and_set_bit(FLAG_REQUESTED, &desc->flags) == 0) {
-
desc_set_label(desc, label ? : "?");
-
status = 0;
-
} else {
-
status = -EBUSY;
-
module_put(chip->owner);
-
goto done;
-
}
-
-
if (chip->request) {
-
/* chip->request may sleep */
-
spin_unlock_irqrestore(&gpio_lock, flags);
-
status = chip->request(chip, gpio - chip->base);
-
spin_lock_irqsave(&gpio_lock, flags);
-
-
if (status < 0) {
-
desc_set_label(desc, NULL);
-
module_put(chip->owner);
-
clear_bit(FLAG_REQUESTED, &desc->flags);
-
}
-
}
-
-
done:
-
if (status)
-
pr_debug("gpio_request: gpio-%d (%s) status %d\n",gpio, label ? : "?", status);
-
spin_unlock_irqrestore(&gpio_lock, flags);
-
return status;
-
}
-
EXPORT_SYMBOL_GPL(gpio_request);
Function1相关数据结构:
struct gpio_desc *desc (driver/gpio/gpiolib.c)
每一个GPIO Pin对应一个gpio_desc.
-
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
-
#define FLAG_EXPORT 3 /* protected by sysfs_lock */
-
#define FLAG_SYSFS 4 /* exported via /sys/class/gpio/control */
-
#define FLAG_TRIG_FALL 5 /* trigger on falling edge */
-
#define FLAG_TRIG_RISE 6 /* trigger on rising edge */
-
#define FLAG_ACTIVE_LOW 7 /* sysfs value has active low */
-
-
#define ID_SHIFT 16 /* add new flags before this 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
-
const char *label;
-
#endif
-
};
-
-
static struct gpio_desc gpio_desc[ARCH_NR_GPIOS];
-
/*gpio pin数组,包含控制器所有的gpio pin,ARCH_NR_GPIOS通常定义在板级文件中(可能需要自己根据板子进行修改):
-
* arch/arm/mach-***/include/**/gpio.h
-
*或
-
* arch/arm/plat-**/include/**/gpio.h
-
* 若没有定义系统则会使用定义在include/asm-generic/gpio.h中的默认值:
-
* #ifndef ARCH_NR_GPIOS
-
* #define ARCH_NR_GPIOS 256
-
* #endif
-
*/
struct gpio_chip *chip(include/linux/gpio.h----->
CONFIG_GENERIC_GPIO---->include/asm-generic/gpio.h)
gpio_chip代表GPIO控制器。
-
/*
-
* struct gpio_chip - abstract a GPIO controller
-
* 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, multifunctio 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; //@label: for diagnostics
-
struct device *dev; //@dev: optional device providing the GPIOs
-
struct module *owner; //@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
-
int (*request)(struct gpio_chip *chip, unsigned offset);
-
-
//@free: optional hook for chip-specific deactivation, such as disabling module power and clock; may sleep
-
void (*free)(struct gpio_chip *chip, unsigned offset);
-
-
//@direction_input: configures signal "offset" as input, or returns error
-
int (*direction_input)(struct gpio_chip *chip, unsigned offset);
-
-
//@get: returns value for signal "offset"; for output signals this returns either the value actually sensed, or zero
-
int (*get)(struct gpio_chip *chip, unsigned offset);
-
-
//@direction_output: configures signal "offset" as output, or returns error
-
int (*direction_output)(struct gpio_chip *chip, unsigned offset, int value);
-
-
int (*set_debounce)(struct gpio_chip *chip, unsigned offset, unsigned debounce);
-
-
//@set: assigns output value for signal "offset"
-
void (*set)(struct gpio_chip *chip, unsigned offset, int value);
-
-
// @to_irq: optional hook supporting non-static gpio_to_irq() mappings;implementation may not sleep
-
int (*to_irq)(struct gpio_chip *chip,unsigned offset);
-
-
//@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).
-
void (*dbg_show)(struct seq_file *s,struct gpio_chip *chip);
-
-
//@base: identifies the first GPIO number handled by this chip; or, if negative during registration, requests dynamic ID allocation.
-
int base;
-
-
//@ngpio: the number of GPIOs handled by this controller; the last GPIO handled is (base + ngpio - 1).
-
u16 ngpio;
-
-
//@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.
-
const char *const *names;
-
-
//@can_sleep: flag must be set iff get()/set() methods sleep, as they must while accessing GPIO expander chips over I2C or SPI
-
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, struct device_node *np,
-
const void *gpio_spec, u32 *flags);
-
#endif
-
};
上述探讨的过程中一直存在一个问题,那就是开发板上的gpio是如何通过gpio框架注册到内核中,供驱动调用的呢?
一般在内核的板级目录中存在对应的gpio.c文件,里面完成了将板级gpio设备到内核的映射的初始化工作,对应文件位置通常如下:
/arch/arm/mach-***/gpio.c
这里以TI的am33xx系列的板子为例进行探究:
/arch/arm/mach-omap2/gpio.c
-
static int omap2_gpio_dev_init(struct omap_hwmod *oh, void *unused)
-
{
-
struct platform_device *pdev;
-
struct omap_gpio_platform_data *pdata;
-
struct omap_gpio_dev_attr *dev_attr;
-
char *name = "omap_gpio";
-
int id;
-
-
/*
-
* extract the device id from name field available in the hwmod database and use the same for constructing ids for gpio devices.
-
* CAUTION: Make sure the name in the hwmod database does not change. If changed, make corresponding change here or make use of
-
* static variable mechanism to handle this.
-
*/
-
sscanf(oh->name, "gpio%d", &id);
-
-
pdata = kzalloc(sizeof(struct omap_gpio_platform_data), GFP_KERNEL);
-
if (!pdata) {
-
pr_err("gpio%d: Memory allocation failed\n", id);
-
return -ENOMEM;
-
}
-
-
dev_attr = (struct omap_gpio_dev_attr *)oh->dev_attr;
-
pdata->bank_width = dev_attr->bank_width;
-
pdata->dbck_flag = dev_attr->dbck_flag;
-
pdata->virtual_irq_start = IH_GPIO_BASE + 32 * (id - 1);
-
-
pdata->regs = kzalloc(sizeof(struct omap_gpio_reg_offs), GFP_KERNEL);
-
if (!pdata) {
-
pr_err("gpio%d: Memory allocation failed\n", id);
-
return -ENOMEM;
-
}
-
-
switch (oh->class->rev) {
-
case 0:
-
case 1:
-
pdata->bank_type = METHOD_GPIO_24XX;
-
pdata->regs->revision = OMAP24XX_GPIO_REVISION;
-
pdata->regs->direction = OMAP24XX_GPIO_OE;
-
pdata->regs->datain = OMAP24XX_GPIO_DATAIN;
-
pdata->regs->dataout = OMAP24XX_GPIO_DATAOUT;
-
pdata->regs->set_dataout = OMAP24XX_GPIO_SETDATAOUT;
-
pdata->regs->clr_dataout = OMAP24XX_GPIO_CLEARDATAOUT;
-
pdata->regs->irqstatus = OMAP24XX_GPIO_IRQSTATUS1;
-
pdata->regs->irqstatus2 = OMAP24XX_GPIO_IRQSTATUS2;
-
pdata->regs->irqenable = OMAP24XX_GPIO_IRQENABLE1;
-
pdata->regs->set_irqenable = OMAP24XX_GPIO_SETIRQENABLE1;
-
pdata->regs->clr_irqenable = OMAP24XX_GPIO_CLEARIRQENABLE1;
-
pdata->regs->debounce = OMAP24XX_GPIO_DEBOUNCE_VAL;
-
pdata->regs->debounce_en = OMAP24XX_GPIO_DEBOUNCE_EN;
-
break;
-
case 2:
-
pdata->bank_type = METHOD_GPIO_44XX;
-
pdata->regs->revision = OMAP4_GPIO_REVISION;
-
pdata->regs->direction = OMAP4_GPIO_OE;
-
pdata->regs->datain = OMAP4_GPIO_DATAIN;
-
pdata->regs->dataout = OMAP4_GPIO_DATAOUT;
-
pdata->regs->set_dataout = OMAP4_GPIO_SETDATAOUT;
-
pdata->regs->clr_dataout = OMAP4_GPIO_CLEARDATAOUT;
-
pdata->regs->irqstatus = OMAP4_GPIO_IRQSTATUS0;
-
pdata->regs->irqstatus2 = OMAP4_GPIO_IRQSTATUS1;
-
pdata->regs->irqenable = OMAP4_GPIO_IRQSTATUSSET0;
-
pdata->regs->set_irqenable = OMAP4_GPIO_IRQSTATUSSET0;
-
pdata->regs->clr_irqenable = OMAP4_GPIO_IRQSTATUSCLR0;
-
pdata->regs->debounce = OMAP4_GPIO_DEBOUNCINGTIME;
-
pdata->regs->debounce_en = OMAP4_GPIO_DEBOUNCENABLE;
-
break;
-
default:
-
WARN(1, "Invalid gpio bank_type\n");
-
kfree(pdata);
-
return -EINVAL;
-
}
-
-
pdev = omap_device_build(name, id - 1, oh, pdata,
-
sizeof(*pdata), NULL, 0, false);
-
kfree(pdata);
-
-
if (IS_ERR(pdev)) {
-
WARN(1, "Can't build omap_device for %s:%s.\n",
-
name, oh->name);
-
return PTR_ERR(pdev);
-
}
-
-
omap_device_disable_idle_on_suspend(pdev);
-
-
gpio_bank_count++;
-
return 0;
-
}
-
-
/*
-
* gpio_init needs to be done before
-
* machine_init functions access gpio APIs.
-
* Hence gpio_init is a postcore_initcall.
-
*/
-
static int __init omap2_gpio_init(void)
-
{
-
return omap_hwmod_for_each_by_class("gpio", omap2_gpio_dev_init,NULL);
-
}
-
postcore_initcall(omap2_gpio_init);
继续追踪代码:
arch/arm/mach-omap2/omap_hwmod.c
-
/**
-
* omap_hwmod_for_each_by_class - call @fn for each hwmod of class @classname
-
* @classname: struct omap_hwmod_class name to search for
-
* @fn: callback function pointer to call for each hwmod in class @classname
-
* @user: arbitrary context data to pass to the callback function
-
*
-
* For each omap_hwmod of class @classname, call @fn.
-
* If the callback function returns something other than
-
* zero, the iterator is terminated, and the callback function's return
-
* value is passed back to the caller. Returns 0 upon success, -EINVAL
-
* if @classname or @fn are NULL, or passes back the error code from @fn.
-
*/
-
int omap_hwmod_for_each_by_class(const char *classname,int (*fn)(struct omap_hwmod *oh,void *user), void *user)
-
{
-
struct omap_hwmod *temp_oh;
-
int ret = 0;
-
-
if (!classname || !fn)
-
return -EINVAL;
-
-
pr_debug("omap_hwmod: %s: looking for modules of class %s\n",
-
__func__, classname);
-
-
list_for_each_entry(temp_oh, &omap_hwmod_list, node) {
-
if (!strcmp(temp_oh->class->name, classname)) {
-
pr_debug("omap_hwmod: %s: %s: calling callback fn\n",
-
__func__, temp_oh->name);
-
ret = (*fn)(temp_oh, user);
-
if (ret)
-
break;
-
}
-
}
-
-
if (ret)
-
pr_debug("omap_hwmod: %s: iterator terminated early: %d\n",
-
__func__, ret);
-
-
return ret;
-
}
上面最终回归到对链表进行操作,那么链表上的这些节点又定义在哪里?链表又是何时建立的呢?
在板级目录下,还存在一个omap_hwmod_33xx_data.c文件,里面定义了该系列开发板的各个模块。
*****未完待续
阅读(2343) | 评论(0) | 转发(1) |