Linux GPIO 的注册与申请

Linux GPIO 的注册与申请

Linux Kernel, GPIO, ARM

在Linux kernel代码中,经常会使用 GPIO 来作为一个特殊的信号,如作为芯片的片选信号等。
GPIO 申请的函数,我们经常用到,如 gpio_request ,那么 GPIO 是何时,以及如何注册的,本文就来探索一下。
基于的平台上 freesccale 的 i.MX6

先从函数 gpio_request 的实现开始。

/* These "optional" allocation calls help prevent drivers from stomping
 * on each other, and help provide better diagnostics in debugfs.
 * They're called even less than the "set direction" calls.
 */
int gpio_request(unsigned gpio, const char *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;
	// 这儿从 gpio_desc 数组中取了一个 gpio_desc 结构体
	// 后面的代码基本上都是基于这个结构体进行的操作
	// 我们是从数组中取了一个 gpio 的描述,这个描述应该是在 gpio 注册的时候添加到这个数组的
	// 以这个数组为线索,看看 gpio 是如何注册的
	desc = &gpio_desc[gpio];
	chip = desc->chip;
	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;
}


以数组 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)
			/* its a serious configuration bug when it fails */
			BUG_ON(gpiochip_add(&port[i].chip) < 0);


继续往上找,平台相关目录下的 Devices.c 有如下函数:

int mx6q_register_gpios(void)
{
	/* 7 ports for Mx6 */
	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 + 32 * 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 + 32 * 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 + 32 * 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 + 32 * 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 + 32 * 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 + 32 * 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;

	/* save for local usage */
	// port 是前面定义的数组 mxc_gpio_ports , cnt 是数组中元素的个数
	mxc_gpio_ports = port;
	gpio_table_size = cnt;

	printk(KERN_INFO "MXC GPIO hardware\n");

	for (i = 0; i < cnt; i++) {
		/* disable the interrupt and clear the status */
		__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);
			/*
			static struct irq_chip gpio_irq_chip = {
				.name = "GPIO",
				.irq_ack = gpio_ack_irq,
				.irq_mask = gpio_mask_irq,
				.irq_unmask = gpio_unmask_irq,
				.irq_set_type = gpio_set_irq_type,
				.irq_set_wake = gpio_set_wake_irq,
			};
			*/
			/**
			 *	handle_level_irq - Level type irq handler
			 *	@irq:	the interrupt number
			 *	@desc:	the interrupt description structure for this irq
			 *
			 *	Level type interrupts are active as long as the hardware line has
			 *	the active level. This may require to mask the interrupt and unmask
			 *	it after the associated handler has acknowledged the device, so the
			 *	interrupt line is back to inactive.
			 */
			irq_set_chip_and_handler(j, &gpio_irq_chip,
						 handle_level_irq);
			set_irq_flags(j, IRQF_VALID);
		}

		/* register gpio chip */
		// mxc_gpio_direction_input 将对应 gpio 设置为输入, mxc_gpio_direction_output 将对应 gpio 设置为输出,并会设置一个初始值
		// 这儿的输入/输出是对 cpu 来说的
		port[i].chip.direction_input = mxc_gpio_direction_input;
		port[i].chip.direction_output = mxc_gpio_direction_output;
		// 获取/设置 gpio 状态
		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)
			/* its a serious configuration bug when it fails */
			// 添加 gpio chip , 调用的是我们前面用到的一个线索函数, 该函数中有给 gpio_desc 数组赋值
			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()) {
			/* setup one handler for each entry */
			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) {
				/* setup handler for GPIO 16 to 31 */
				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()) {
		/* setup one handler for all GPIO interrupts */
		irq_set_chained_handler(port[0].irq, mx2_gpio_irq_handler);
		irq_set_handler_data(port[0].irq, port);
	}

	return 0;
}


gpiochip_add 函数的实现:

/**
 * gpiochip_add() - register a gpio_chip
 * @chip: the chip to register, with chip->base initialized
 * Context: potentially before irqs or kmalloc will work
 *
 * Returns a negative errno if the chip can't be registered, such as
 * because the chip->base is invalid or already associated with a
 * different chip.  Otherwise it returns zero as a success code.
 *
 * When gpiochip_add() is called very early during boot, so that GPIOs
 * can be freely used, the chip->dev device must be registered before
 * the gpio framework's arch_initcall().  Otherwise sysfs initialization
 * for GPIOs will fail rudely.
 *
 * If chip->base is negative, this requests dynamic assignment of
 * a range of valid GPIOs.
 */
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;
	}

	/* these GPIO numbers must not be managed by another gpio_chip */
	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 成员的 chip 成员
			gpio_desc[id].chip = chip;

			/* REVISIT:  most hardware initializes GPIOs as
			 * inputs (often with pullups enabled) so power
			 * usage is minimized.  Linux code should set the
			 * gpio direction first thing; but until it does,
			 * we may expose the wrong direction in sysfs.
			 */
			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;

	// 创建设备, 并添加对应的 sysfs
	status = gpiochip_export(chip);
	if (status)
		goto fail;

	return 0;
fail:
	/* failures here can mean systems won't boot... */
	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;
/* 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
};


 

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。