zoukankan      html  css  js  c++  java
  • 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 ", 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
    ");
    
    	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
    ",
    		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
    };


     

    版权声明:本文博主原创文章,博客,未经同意不得转载。

  • 相关阅读:
    管理员必备的Linux系统监控工具
    kafka入门:简介、使用场景、设计原理、主要配置及集群搭建(转)
    RedHat linux配置yum本地资源
    RedHat Linux RHEL6配置本地YUM源
    c语言中的fgets函数
    sprintf()函数的用法
    spring boot整合JWT例子
    spring boot 自定义过滤器链
    (转)ArrayList和LinkedList的几种循环遍历方式及性能对比分析
    (转)Springboot 中filter 注入对象
  • 原文地址:https://www.cnblogs.com/zfyouxi/p/4794642.html
Copyright © 2011-2022 走看看