linux GPIO子系统

0、说明

        GPIO完成对CPU支持GPIO的访问,如拉高,拉低等。本文分析linux下gpio子系统。

1、环境

1.1 硬件环境

  • Xilinx ZYNQ开发板

1.2 软件环境

  • VM ubuntu 18.04
  • windows 10

2、GPIO子系统数据结构

2.1 代码路径

drivers/gpio/gpiolib.c
drivers/gpio/gpio-zynq.c

2.2 关键结构体

gpio_device

        一个GPIO控制器对应一个gpio_device。

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; //数组,支持的所有gpio描述信息                                              
    int         base;  //编号基数                                                      
    u16         ngpio; //支持多少个GPIO                                                      
    const char      *label;                                                  
    void            *data;                                                   
    struct list_head        list;                                            
    struct blocking_notifier_head notifier;                                  
                                                                             
#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                                                                       
};

gpio_desc 

        一个gpio对应一个gpio_desc,在使用gpiod_get时返回该GPIO对应的gpio_desc,gpio_desc中存有gpio_device信息,可以找到所属控制器。

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_IRQ_IS_ENABLED 10  /* GPIO is connected to an enabled IRQ */                                            
#define FLAG_IS_HOGGED  11  /* GPIO is hogged */                                                                     
#define FLAG_TRANSITORY 12  /* GPIO may lose value in sleep or reset */                                              
#define FLAG_PULL_UP    13  /* GPIO has pull up enabled */                                                           
#define FLAG_PULL_DOWN  14  /* GPIO has pull down enabled */                                                         
#define FLAG_BIAS_DISABLE    15 /* GPIO has pull disabled */                                                         
#define FLAG_EDGE_RISING     16 /* GPIO CDEV detects rising edge events */                                           
#define FLAG_EDGE_FALLING    17 /* GPIO CDEV detects falling edge events */                                          
                                                                                                                     
    /* Connection label */                                                                                           
    const char      *label;                                                                                          
    /* Name of the GPIO */                                                                                           
    const char      *name;                                                                                           
#ifdef CONFIG_OF_DYNAMIC                                                                                             
    struct device_node  *hog;                                                                                        
#endif                                                                                                               
#ifdef CONFIG_GPIO_CDEV                                                                                              
    /* debounce period in microseconds */                                                                            
    unsigned int        debounce_period_us;                                                                          
#endif                                                                                                               
}; 

gpio_chip 

        GPIO控制器驱动中创建gpio_chip,通过gpiochip_add_data向系统注册gpio_device来描述一个GPIO控制器。

struct gpio_chip {
	const char		*label;
	struct gpio_device	*gpiodev;
	struct device		*parent;
	struct module		*owner;

	int			(*request)(struct gpio_chip *gc,
						unsigned int offset);
	void			(*free)(struct gpio_chip *gc,
						unsigned int offset);
	int			(*get_direction)(struct gpio_chip *gc,
						unsigned int offset);
	int			(*direction_input)(struct gpio_chip *gc,
						unsigned int offset);
	int			(*direction_output)(struct gpio_chip *gc,
						unsigned int offset, int value);
	int			(*get)(struct gpio_chip *gc,
						unsigned int offset);
	int			(*get_multiple)(struct gpio_chip *gc,
						unsigned long *mask,
						unsigned long *bits);
	void			(*set)(struct gpio_chip *gc,
						unsigned int offset, int value);
	void			(*set_multiple)(struct gpio_chip *gc,
						unsigned long *mask,
						unsigned long *bits);
	int			(*set_config)(struct gpio_chip *gc,
					      unsigned int offset,
					      unsigned long config);
	int			(*to_irq)(struct gpio_chip *gc,
						unsigned int offset);

	void			(*dbg_show)(struct seq_file *s,
						struct gpio_chip *gc);

	int			(*init_valid_mask)(struct gpio_chip *gc,
						   unsigned long *valid_mask,
						   unsigned int ngpios);

	int			(*add_pin_ranges)(struct gpio_chip *gc);

	int			base;
	u16			ngpio;
	const char		*const *names;
	bool			can_sleep;

#if IS_ENABLED(CONFIG_GPIO_GENERIC)
	unsigned long (*read_reg)(void __iomem *reg);
	void (*write_reg)(void __iomem *reg, unsigned long data);
	bool be_bits;
	void __iomem *reg_dat;
	void __iomem *reg_set;
	void __iomem *reg_clr;
	void __iomem *reg_dir_out;
	void __iomem *reg_dir_in;
	bool bgpio_dir_unreadable;
	int bgpio_bits;
	spinlock_t bgpio_lock;
	unsigned long bgpio_data;
	unsigned long bgpio_dir;
#endif /* CONFIG_GPIO_GENERIC */

#ifdef CONFIG_GPIOLIB_IRQCHIP
	/*
	 * With CONFIG_GPIOLIB_IRQCHIP we get an irqchip inside the gpiolib
	 * to handle IRQs for most practical cases.
	 */

	/**
	 * @irq:
	 *
	 * Integrates interrupt chip functionality with the GPIO chip. Can be
	 * used to handle IRQs for most practical cases.
	 */
	struct gpio_irq_chip irq;
#endif /* CONFIG_GPIOLIB_IRQCHIP */

	/**
	 * @valid_mask:
	 *
	 * If not %NULL holds bitmask of GPIOs which are valid to be used
	 * from the chip.
	 */
	unsigned long *valid_mask;

#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
	 */

	/**
	 * @of_node:
	 *
	 * Pointer to a device tree node representing this GPIO controller.
	 */
	struct device_node *of_node;

	/**
	 * @of_gpio_n_cells:
	 *
	 * Number of cells used to form the GPIO specifier.
	 */
	unsigned int of_gpio_n_cells;

	/**
	 * @of_xlate:
	 *
	 * Callback to translate a device tree GPIO specifier into a chip-
	 * relative GPIO number and flags.
	 */
	int (*of_xlate)(struct gpio_chip *gc,
			const struct of_phandle_args *gpiospec, u32 *flags);
#endif /* CONFIG_OF_GPIO */
};

of_phandle_args

2.3 关键API

gpiod_get

led_gpio = gpiod_get(&pdev->dev, "led", 0);

struct gpio_desc *__must_check gpiod_get(struct device *dev, const char *con_id, enum gpiod_flags flags)
	gpiod_get_index(dev, "led", 0, 0);
		desc = of_find_gpio(dev, "led", 0, &lookupflags);
			of_get_named_gpiod_flags(dev->of_node, led-gpios, 0, &of_flags);
				of_parse_phandle_with_args_map(np, led-gpios, "gpio", 0, &gpiospec);
					__of_parse_phandle_with_args(np, led-gpios, gpio-cells, -1, 0, out_args);
				chip = of_find_gpiochip_by_xlate(&gpiospec);
				desc = of_xlate_and_get_gpiod_flags(chip, &gpiospec, flags);

gpio_request

gpiod_set_value

gpiod_set_value(led_gpio_desc, 0);
gpiod_set_value(struct gpio_desc *desc, int value)
	 gpiod_set_value_nocheck(desc, value);
		if (test_bit(FLAG_ACTIVE_LOW, &desc->flags))
			value = !value;
		gpiod_set_raw_value_commit(desc, value);
			gc = desc->gdev->chip;

3、GPIO子系统

3.1 设备树中的描述

        gpio0: gpio@e000a000 {
            compatible = "xlnx,zynq-gpio-1.0";
            #gpio-cells = <2>;
            clocks = <&clkc 42>;
            gpio-controller;
            interrupt-controller;
            #interrupt-cells = <2>;
            interrupt-parent = <&intc>;
            interrupts = <0 20 4>;
            reg = <0xe000a000 0x1000>;
        };

3.2 GPIO控制器驱动

3.2.1 GPIO控制器驱动主要任务

        如下分析,gpio控制器驱动中主要完成:

  • 匹配设备树与驱动中的私有数据
  • 获取设备树中的io资源与中断资源
  • 初始化配置gpio_chip中的函数
  • 设置gpio_chip中的irq资源
  • gpiochip_add_data完成gpio注册到内核中
/**
 * zynq_gpio_probe - Initialization method for a zynq_gpio device
 * @pdev:	platform device instance
 *
 * This function allocates memory resources for the gpio device and registers
 * all the banks of the device. It will also set up interrupts for the gpio
 * pins.
 * Note: Interrupts are disabled for all the banks during initialization.
 *
 * Return: 0 on success, negative error otherwise.
 */
static int zynq_gpio_probe(struct platform_device *pdev)
{
	int ret, bank_num;
	struct zynq_gpio *gpio;
	struct gpio_chip *chip;
	struct gpio_irq_chip *girq;
	const struct of_device_id *match;

    //分配zynq_gpio,同时分配了gpio_chip
	gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
	if (!gpio)
		return -ENOMEM;

    //通过设备树中的配置匹配zynq_gpio_of_match并获取对应的私有数据
	match = of_match_node(zynq_gpio_of_match, pdev->dev.of_node);
	if (!match) {
		dev_err(&pdev->dev, "of_match_node() failed\n");
		return -EINVAL;
	}
	gpio->p_data = match->data;
	platform_set_drvdata(pdev, gpio);

    //从设备树获取IORESOURCE_MEM资源,即reg属性中的GPIO寄存器基址
	gpio->base_addr = devm_platform_ioremap_resource(pdev, 0);
	if (IS_ERR(gpio->base_addr))
		return PTR_ERR(gpio->base_addr);

    //从设备树获取GPIO IRQ资源,记录硬件中断号
	gpio->irq = platform_get_irq(pdev, 0);
	if (gpio->irq < 0)
		return gpio->irq;

    //设置gpio_chip的操作函数等,在get/set/request等时调用到chip中函数
	/* configure the gpio chip */
	chip = &gpio->chip;
	chip->label = gpio->p_data->label; //label = "zynq_gpio"
	chip->owner = THIS_MODULE;
	chip->parent = &pdev->dev;
	chip->get = zynq_gpio_get_value;
	chip->set = zynq_gpio_set_value;
	chip->request = zynq_gpio_request;
	chip->free = zynq_gpio_free;
	chip->direction_input = zynq_gpio_dir_in;
	chip->direction_output = zynq_gpio_dir_out;
	chip->get_direction = zynq_gpio_get_direction;
    //设置gpio_chip的编号基址
	chip->base = of_alias_get_id(pdev->dev.of_node, "gpio");
    //支持的个数  118个
	chip->ngpio = gpio->p_data->ngpio;

	/* Retrieve GPIO clock */
	gpio->clk = devm_clk_get(&pdev->dev, NULL);
	if (IS_ERR(gpio->clk))
		return dev_err_probe(&pdev->dev, PTR_ERR(gpio->clk), "input clock not found.\n");

	ret = clk_prepare_enable(gpio->clk);
	if (ret) {
		dev_err(&pdev->dev, "Unable to enable clock.\n");
		return ret;
	}

	spin_lock_init(&gpio->dirlock);

	pm_runtime_set_active(&pdev->dev);
	pm_runtime_enable(&pdev->dev);
	ret = pm_runtime_resume_and_get(&pdev->dev);
	if (ret < 0)
		goto err_pm_dis;

	/* disable interrupts for all banks */
	for (bank_num = 0; bank_num < gpio->p_data->max_bank; bank_num++) {
		writel_relaxed(ZYNQ_GPIO_IXR_DISABLE_ALL, gpio->base_addr +
			       ZYNQ_GPIO_INTDIS_OFFSET(bank_num));
		if (gpio->p_data->quirks & GPIO_QUIRK_VERSAL)
			bank_num = bank_num + VERSAL_UNUSED_BANKS;
	}

    //设置中断资源,在中断子系统中,需要gpio子系统提供中断handle函数
	/* Set up the GPIO irqchip */
	girq = &chip->irq;
	girq->chip = &zynq_gpio_edge_irqchip; //提供irq_chip相关中断预处理函数
	girq->parent_handler = zynq_gpio_irqhandler;//父级handle
	girq->num_parents = 1;
	girq->parents = devm_kcalloc(&pdev->dev, 1,
				     sizeof(*girq->parents),
				     GFP_KERNEL);
	if (!girq->parents) {
		ret = -ENOMEM;
		goto err_pm_put;
	}
	girq->parents[0] = gpio->irq;
	girq->default_type = IRQ_TYPE_NONE;
	girq->handler = handle_level_irq;

    //注册gpio_chip,将在core层产生gpio_device
   	ret = gpiochip_add_data(chip, gpio);
	if (ret) {
		dev_err(&pdev->dev, "Failed to add gpio chip\n");
		goto err_pm_put;
	}

	irq_set_status_flags(gpio->irq, IRQ_DISABLE_UNLAZY);
	device_init_wakeup(&pdev->dev, 1);
	pm_runtime_put(&pdev->dev);

	return 0;

err_pm_put:
	pm_runtime_put(&pdev->dev);
err_pm_dis:
	pm_runtime_disable(&pdev->dev);
	clk_disable_unprepare(gpio->clk);

	return ret;
}

3.2.2 gpiochip_add_data函数分析

总结

结构体关系

        一个GPIO控制器,对应一个gpio_chip结构体在驱动中实现其内部函数的配置,最终通过gpiochip_add_data注册进内核,内核同时生成gpio_device结构体。所有gpio_device被放置在gpio_devices全局链表中。

 

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Linux GPIO子系统是一个用于控制嵌入式系统中通用输入/输出(GPIO)的软件子系统。它提供了一种标准的接口,使得应用程序可以通过文件系统接口来访问GPIO。这个子系统可以用于控制各种设备,例如LED、按钮、传感器等等。如果你需要更多的信息,可以查看Linux内核文档。 ### 回答2: Linux GPIO子系统是一种用于管理通用输入输出(GPIO)引脚的软件层。GPIO引脚是一种通用可编程引脚,可以在嵌入式系统中用来通过读取输入或设置输出与外部设备进行通信。 Linux GPIO子系统负责将底层硬件 GPIO 引脚的操作抽象为文件系统的接口,使开发者可以通过读写文件的方式来访问和控制 GPIO 引脚。通过该子系统,可以实现对 GPIO 引脚的配置、读取和写入等操作,以满足不同应用下对 GPIO 的需求。 Linux GPIO子系统的核心是GPIO驱动程序,它与底层硬件层进行交互,完成对GPIO引脚的操作。驱动程序将GPIO引脚映射到内存,通过读写该内存地址即可对引脚进行操作。用户通过访问特定目录下的文件来和引脚进行交互,例如将引脚配置为输入模式、输出模式,以及读取或写入引脚的状态。 通过Linux GPIO子系统,开发者可以方便地进行GPIO引脚的控制。可以根据不同的应用需求,灵活配置引脚的输入输出模式,监听引脚上的状态变化,并根据需要对其他外设进行控制。 总之,Linux GPIO子系统为开发者提供了便捷的接口,使得在嵌入式系统中使用GPIO引脚更加简单和灵活。它允许开发者通过读写文件的方式访问和控制GPIO引脚,满足各种不同嵌入式应用对GPIO的需求。 ### 回答3: LinuxGPIO(General Purpose Input/Output)子系统是通过软件对硬件上的通用输入/输出引脚进行控制的一种机制。它使得开发者可以利用这些GPIO引脚实现各种功能,比如控制LED灯、读取外部传感器的数据等。 LinuxGPIO子系统提供了许多功能和接口来管理和操作GPIO。首先,它使用sysfs文件系统来组织GPIO资源的目录树,并通过文件的方式来读取和写入GPIO的状态。在/sys/class/gpio目录下,每个GPIO引脚都会有一个对应的目录,在该目录中的文件可以用于配置GPIO的方向(输入或输出)、读取和写入GPIO的电平状态。开发者可以使用命令行工具或者编程语言(如Python、C等)来操作这些文件,从而控制GPIO引脚的行为。 其次,LinuxGPIO子系统还提供了设备树(Device Tree)来描述硬件平台上的GPIO资源。设备树是一种描述硬件的数据结构,在启动时通过设备树绑定机制将设备树中定义的GPIO资源与内核驱动程序关联起来。这样,开发者就可以通过调用相应的驱动程序来控制GPIO引脚,而不需要手动操作sysfs文件系统。 此外,LinuxGPIO子系统还支持中断机制,可以让GPIO引脚在特定事件发生时触发中断。通过注册中断处理函数,开发者可以实现对GPIO输入信号的快速响应,提高系统的实时性。 总之,LinuxGPIO子系统为开发者提供了一种方便且灵活的方式来控制硬件上的GPIO引脚。通过sysfs文件系统或设备树,开发者可以轻松地配置、读取和控制GPIO的状态,从而实现各种功能和应用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值