Linux GPIO 的注册与申请
Linux Kernel, GPIO, ARM
在Linux kernel代码中,经常会使用 GPIO 来作为一个特殊的信号,如作为芯片的片选信号等。
GPIO 申请的函数,我们经常用到,如 gpio_request ,那么 GPIO 是何时,以及如何注册的,本文就来探索一下。
基于的平台上 freesccale 的 i.MX6
先从函数 gpio_request 的实现开始。
-
-
-
-
-
int gpio_request(unsigned gpio, const charchar *label)
-
{
-
struct gpio_desc *desc;
-
struct gpio_chip *chip;
-
int status = -EINVAL;
-
unsigned long flags;
-
-
spin_lock_irqsave(&gpio_lock, flags);
-
-
if (!gpio_is_valid(gpio))
-
goto done;
-
-
-
-
-
desc = &gpio_desc[gpio];
-
chip = desc->chip;
-
if (chip == NULL)
-
goto done;
-
-
if (!try_module_get(chip->owner))
-
goto done;
-
-
-
-
-
-
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) {
-
-
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;
-
}
以数组 gpio_desc 为线索。
既然我们申请 GPIO 的时候是从这个数字中取数据,那么注册 GPIO 的时候就应该往这个数字中添加数据了。
反过来,往这个数组添加数据的地方应该也就是注册 GPIO 的地方了。
这个数组定义在 Gpiolib.c 文件中:
-
static struct gpio_desc gpio_desc[ARCH_NR_GPIOS];
搜索发现, gpiochip_add 函数中有给数组 gpio_desc 赋值。
看看谁调用了函数 gpiochip_add 。
平台相关目录下的 Gpio.c 文件中的 mxc_gpio_init 函数调用了 gpiochip_add :
-
if (!initialed)
-
-
BUG_ON(gpiochip_add(&port[i].chip) < 0);
继续往上找,平台相关目录下的 Devices.c 有如下函数:
-
int mx6q_register_gpios(void)
-
{
-
-
return mxc_gpio_init(mxc_gpio_ports, 7);
-
}
mxc_gpio_ports 的定义:
-
static struct mxc_gpio_port mxc_gpio_ports[] = {
-
{
-
.chip.label = "gpio-0",
-
.base = IO_ADDRESS(GPIO1_BASE_ADDR),
-
.irq = MXC_INT_GPIO1_INT15_0_NUM,
-
.irq_high = MXC_INT_GPIO1_INT31_16_NUM,
-
.virtual_irq_start = MXC_GPIO_IRQ_START
-
},
-
{
-
.chip.label = "gpio-1",
-
.base = IO_ADDRESS(GPIO2_BASE_ADDR),
-
.irq = MXC_INT_GPIO2_INT15_0_NUM,
-
.irq_high = MXC_INT_GPIO2_INT31_16_NUM,
-
.virtual_irq_start = MXC_GPIO_IRQ_START + 332 * 1
-
},
-
{
-
.chip.label = "gpio-2",
-
.base = IO_ADDRESS(GPIO3_BASE_ADDR),
-
.irq = MXC_INT_GPIO3_INT15_0_NUM,
-
.irq_high = MXC_INT_GPIO3_INT31_16_NUM,
-
.virtual_irq_start = MXC_GPIO_IRQ_START + 332 * 2
-
},
-
{
-
.chip.label = "gpio-3",
-
.base = IO_ADDRESS(GPIO4_BASE_ADDR),
-
.irq = MXC_INT_GPIO4_INT15_0_NUM,
-
.irq_high = MXC_INT_GPIO4_INT31_16_NUM,
-
.virtual_irq_start = MXC_GPIO_IRQ_START + 332 * 3
-
},
-
{
-
.chip.label = "gpio-4",
-
.base = IO_ADDRESS(GPIO5_BASE_ADDR),
-
.irq = MXC_INT_GPIO5_INT15_0_NUM,
-
.irq_high = MXC_INT_GPIO5_INT31_16_NUM,
-
.virtual_irq_start = MXC_GPIO_IRQ_START + 332 * 4
-
},
-
{
-
.chip.label = "gpio-5",
-
.base = IO_ADDRESS(GPIO6_BASE_ADDR),
-
.irq = MXC_INT_GPIO6_INT15_0_NUM,
-
.irq_high = MXC_INT_GPIO6_INT31_16_NUM,
-
.virtual_irq_start = MXC_GPIO_IRQ_START + 332 * 5
-
},
-
{
-
.chip.label = "gpio-6",
-
.base = IO_ADDRESS(GPIO7_BASE_ADDR),
-
.irq = MXC_INT_GPIO7_INT15_0_NUM,
-
.irq_high = MXC_INT_GPIO7_INT31_16_NUM,
-
.virtual_irq_start = MXC_GPIO_IRQ_START + 332 * 6
-
},
-
};
继续往上,找到了同目录下 Irq.c 文件中的 mx6_init_irq 函数调用了 mx6q_register_gpios 。
board.c 文件中将 mx6_init_irq 函数赋值给了 machine_desc 结构体的 init_irq 函数:
-
MACHINE_START(MX6XXXX, "Freescale i.MX 6 Board")
-
.boot_params = MX6_PHYS_OFFSET + 0x100,
-
.fixup = fixup_mxc_board,
-
.map_io = mx6_map_io,
-
.init_irq = mx6_init_irq,
-
.init_machine = mx6_board_init,
-
.timer = &mxc_timer,
-
.reserve = mx6q_reserve,
-
MACHINE_END
arch/arm/kernel/irq.c 文件中有以下函数:
-
void __init init_IRQ(void)
-
{
-
machine_desc->init_irq();
-
}
init/main.c 文件中的 start_kernel 函数调用了 init_IRQ 。
至于 start_kernel 函数何时被调用,有时间再作研究。
总结一下 GPIO 的注册过程:
start_kernel 函数会调用 init_IRQ 函数。
init_IRQ 函数调用了 machine_desc 结构体的 init_irq 函数。
machine_desc 结构体在 board.c 文件中定义,其中 init_irq 被赋值为 mx6_init_irq 。
mx6_init_irq 函数中调用了 mx6q_register_gpios 函数。
mx6q_register_gpios 函数的定义见前文,其中调用了函数 mxc_gpio_init 。
函数 mxc_gpio_init 的实现:
-
int mxc_gpio_init(struct mxc_gpio_port *port, int cnt)
-
{
-
int i, j;
-
static bool initialed;
-
-
-
-
mxc_gpio_ports = port;
-
gpio_table_size = cnt;
-
-
printk(KERN_INFO "MXC GPIO hardware\n");
-
-
for (i = 0; i < cnt; i++) {
-
-
__raw_writel(0, port[i].base + GPIO_IMR);
-
__raw_writel(~0, port[i].base + GPIO_ISR);
-
for (j = port[i].virtual_irq_start;
-
j < port[i].virtual_irq_start + 32; j++) {
-
irq_set_lockdep_class(j, &gpio_lock_class);
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
irq_set_chip_and_handler(j, &gpio_irq_chip,
-
handle_level_irq);
-
set_irq_flags(j, IRQF_VALID);
-
}
-
-
-
-
-
port[i].chip.direction_input = mxc_gpio_direction_input;
-
port[i].chip.direction_output = mxc_gpio_direction_output;
-
-
port[i].chip.get = mxc_gpio_get;
-
port[i].chip.set = mxc_gpio_set;
-
port[i].chip.base = i * 32;
-
port[i].chip.ngpio = 32;
-
-
spin_lock_init(&port[i].lock);
-
-
if (!initialed)
-
-
-
BUG_ON(gpiochip_add(&port[i].chip) < 0);
-
-
if (cpu_is_mx1() || cpu_is_mx3() || cpu_is_mx25() ||
-
cpu_is_mx51() || cpu_is_mx53() || cpu_is_mx6q() ||
-
cpu_is_mx6dl() || cpu_is_mx6sl()) {
-
-
irq_set_chained_handler(port[i].irq,
-
mx3_gpio_irq_handler);
-
irq_set_handler_data(port[i].irq, &port[i]);
-
if (port[i].irq_high) {
-
-
irq_set_chained_handler(port[i].irq_high,
-
mx3_gpio_irq_handler);
-
irq_set_handler_data(port[i].irq_high,
-
&port[i]);
-
}
-
}
-
}
-
initialed = true;
-
if (cpu_is_mx2()) {
-
-
irq_set_chained_handler(port[0].irq, mx2_gpio_irq_handler);
-
irq_set_handler_data(port[0].irq, port);
-
}
-
-
return 0;
-
}
gpiochip_add 函数的实现:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
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);
-
-
if (base < 0) {
-
base = gpiochip_find_base(chip->ngpio);
-
if (base < 0) {
-
status = base;
-
goto unlock;
-
}
-
chip->base = base;
-
}
-
-
-
for (id = base; id < base + chip->ngpio; id++) {
-
if (gpio_desc[id].chip != NULL) {
-
status = -EBUSY;
-
break;
-
}
-
}
-
if (status == 0) {
-
for (id = base; id < base + chip->ngpio; id++) {
-
-
gpio_desc[id].chip = chip;
-
-
-
-
-
-
-
-
gpio_desc[id].flags = !chip->direction_input
-
? (1 << FLAG_IS_OUT)
-
: 0;
-
}
-
}
-
-
of_gpiochip_add(chip);
-
-
unlock:
-
spin_unlock_irqrestore(&gpio_lock, flags);
-
-
if (status)
-
goto fail;
-
-
-
status = gpiochip_export(chip);
-
if (status)
-
goto fail;
-
-
return 0;
-
fail:
-
-
pr_err("gpiochip_add: gpios %d..%d (%s) failed to register\n",
-
chip->base, chip->base + chip->ngpio - 1,
-
chip->label ? : "generic");
-
return status;
-
}
gpio_desc 结构体的定义:
-
struct gpio_desc {
-
struct gpio_chip *chip;
-
unsigned long flags;
-
-
#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
-
};
阅读(2320) | 评论(0) | 转发(0) |