【版权申明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权)
linux gpio_to_irq()源码分析
背景说明:
在Linux设备树(linux 3.x版本引入)中, 设备的中断号不再在"irq.h"中硬编码定义, 而是在需要时自己手动去申请获得对应的硬件中断的软件中断号.( 前提是GPIO的相关模块已经被编入内核 )
1. gpio_to_irq()的函数作用
/**
* include/linux/gpio.h
*
* @param gpio 为要操作的GPIO编号, 该编号等于GPIO组号*8 + 组内偏移号, 例如GPIO4_2的编号为4*8 + 2 = 34 (每组GPIO有8个GPIO管脚)
*/
static inline int gpio_to_irq(unsigned int gpio)
{
return __gpio_to_irq(gpio);
}
通过GPIO号得到对应的软件中断号, 该中断号是request_irq()函数的第一个参数.
2. gpio_to_irq()的源码分析
/**
* include/asm-generic/gpio.h
*/
static inline int __gpio_to_irq(unsigned gpio)
{
return gpiod_to_irq(gpio_to_desc(gpio));
}
2.1 首先, 我们来看看gpio_to_desc(gpio)的含义:
/**
* list_for_each_entry - iterate over list of given type
迭代给定类型的列表, head为typeof(*pos)结构体中的其中一员且为一个双向链表, 我们通过
遍历这个链表循环得到每一个typeof(*pos)结构体.
* @pos: the type * to use as a loop cursor.
用作循环游标, 每一次得到typeof(*pos)类型的变量.
* @head: the head for your list.
列表头, 该变量为typeof(*pos)结构体中的其中一员.
* @member: the name of the list_head within the struct.
在结构体中list_head的名称
*/
#define list_for_each_entry(pos, head, member) \
for (pos = list_first_entry(head, typeof(*pos), member); \
&pos->member != (head); \
pos = list_next_entry(pos, member))
/**
* Convert a GPIO number to its descriptor
*/
struct gpio_desc *gpio_to_desc(unsigned gpio)
{
struct gpio_device *gdev;
unsigned long flags;
/**
* 内核同步自旋锁.
* 禁止内核抢占, 关闭中断, 保存中断状态寄存器的标志位.
*/
spin_lock_irqsave(&gpio_lock, flags);
list_for_each_entry(gdev, &gpio_devices, list) {
if (gdev->base <= gpio &&
gdev->base + gdev->ngpio > gpio) {
/**
* 当查找到gpio所属的组时, 返回该GPIO的descs, 即&gdev->descs[gpio - gdev->base]
*/
spin_unlock_irqrestore(&gpio_lock, flags);
return &gdev->descs[gpio - gdev->base];
}
}
spin_unlock_irqrestore(&gpio_lock, flags);
if (!gpio_is_valid(gpio))
WARN(1, "invalid GPIO %d\n", gpio);
return NULL;
}
关键处: 当查找到gpio所属的组时, 返回该GPIO的descs, 即&gdev->descs[gpio - gdev->base].
在这里, 我们拿到了该GPIO的描述信息. 该结构体的定义如下:(drivers/gpio/gpiolib.h)
/**
* GPIO设备的内部状态容器.
*/
struct gpio_device {
int id;
struct device dev;
struct cdev chrdev;
struct device *mockdev;
struct module *owner;
struct gpio_chip *chip;
struct gpio_desc *descs;
int base;
u16 ngpio;
char *label;
void *data;
struct list_head list;
#ifdef CONFIG_PINCTRL
/*
* If CONFIG_PINCTRL is enabled, then gpio controllers can optionally
* describe the actual pin range which they serve in an SoC. This
* information would be used by pinctrl subsystem to configure
* corresponding pins for gpio usage.
*/
struct list_head pin_ranges;
#endif
};
struct gpio_desc {
struct gpio_device *gdev;
unsigned long flags;
/* flag symbols are bit numbers */
#define FLAG_REQUESTED 0
#define FLAG_IS_OUT 1
#define FLAG_EXPORT 2 /* protected by sysfs_lock */
#define FLAG_SYSFS 3 /* exported via /sys/class/gpio/control */
#define FLAG_ACTIVE_LOW 6 /* value has active low */
#define FLAG_OPEN_DRAIN 7 /* Gpio is open drain type */
#define FLAG_OPEN_SOURCE 8 /* Gpio is open source type */
#define FLAG_USED_AS_IRQ 9 /* GPIO is connected to an IRQ */
#define FLAG_IS_HOGGED 11 /* GPIO is hogged */
/* Connection label */
const char *label;
/* Name of the GPIO */
const char *name;
};
2.2 然后, 我们来看看gpiod_to_irq(const struct gpio_desc *desc);的含义:
/**
* gpiod_to_irq() - return the IRQ corresponding to a GPIO
* @desc: gpio whose IRQ will be returned (already requested)
*
* Return the IRQ corresponding to the passed GPIO, or an error code in case of
* error.
*/
int gpiod_to_irq(const struct gpio_desc *desc)
{
struct gpio_chip *chip;
int offset;
/*
* Cannot VALIDATE_DESC() here as gpiod_to_irq() consumer semantics
* requires this function to not return zero on an invalid descriptor
* but rather a negative error number.
*/
if (!desc || IS_ERR(desc) || !desc->gdev || !desc->gdev->chip)
return -EINVAL;
chip = desc->gdev->chip;
offset = gpio_chip_hwgpio(desc);
if (chip->to_irq) {
int retirq = chip->to_irq(chip, offset);
/* Zero means NO_IRQ */
if (!retirq)
return -ENXIO;
return retirq;
}
return -ENXIO;
}