我是靠谱客的博主 重要草莓,最近开发中收集的这篇文章主要介绍Linux内核4.14版本——GPIO子系统(1)——gpiolib分析1、简述2、Gpiolib 相关数据结构分析 3 Gpiolib 对接芯片底层4、Gpiolib 为其他驱动提供的 APIs,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

目录

1、简述

2、Gpiolib 相关数据结构分析

2.1 gpio_chip 结构   

2.2 gpio_desc 结构

2.3 gpio_device 结构

 3 Gpiolib 对接芯片底层

3.1 注册 GPIO 资源(gpiochip_add_data)

3.2 part1

3.2 part 2

3.3 part 3

4、Gpiolib 为其他驱动提供的 APIs


参考文章:GPIO子系统 - 蜗窝科技

1、简述

      GPIO 资源是相对来说较为简单,而且比较通用(比如 LED 灯),而 Linux 的 GPIO 驱动属于 Linux Driver 中较为容易上手的部分,但是简单归简单,在 Linux 系统中,要使用 GPIO 资源,还是需要了解一些内容。

      Linux Kernel 中对 GPIO 资源进行了抽象,抽象出一个叫做 Gpiolib 的东东,这个东东作为 GPIO 资源的管理核心存在:

       中间层是 Gpiolib,用于管理系统中的 GPIO。Gpiolib 汇总了 GPIO 的通用操作,根据 GPIO 的特性,Gpiolib 对上(其他 Drivers)提供的一套统一通用的操作 GPIO 的软件接口,屏蔽了不同芯片的具体实现。对下,Gpiolib 提供了针对不同芯片操作的一套 framework,针对不同芯片,只需要实现 Specific Chip Driver ,然后使用 Gpiolib 提供的注册函数,将其挂接到 Gpiolib 上,这样就完成了这一套东西。

       对于其他驱动来说,比如 LED 灯驱动,就需要用到通用的 Gpiolib 的函数来进行 I/O 口的操作。

2、Gpiolib 相关数据结构分析

      先分析数据结构,Gpiolib 其实就是围绕几个数据结构在做文章,数据结构以及抽象层次清楚了,代码自然很快。

       数据结构主要定义在 include/linux/gpio/driver.h 和 /drivers/gpio/gpiolib.h 中

      首先看一个数据结构,叫 struct gpio_chip (include/linux/gpio/driver.h):


/**
 * struct gpio_chip - abstract a GPIO controller
 * @label: a functional name for the GPIO device, such as a part
 *	number or the name of the SoC IP-block implementing it.
 * @gpiodev: the internal state holder, opaque struct
 * @parent: optional parent device providing the GPIOs
 * @owner: helps prevent removal of modules exporting active GPIOs
 * @request: optional hook for chip-specific activation, such as
 *	enabling module power and clock; may sleep
 * @free: optional hook for chip-specific deactivation, such as
 *	disabling module power and clock; may sleep
 * @get_direction: returns direction for signal "offset", 0=out, 1=in,
 *	(same as GPIOF_DIR_XXX), or negative error
 * @direction_input: configures signal "offset" as input, or returns error
 * @direction_output: configures signal "offset" as output, or returns error
 * @get: returns value for signal "offset", 0=low, 1=high, or negative error
 * @set: assigns output value for signal "offset"
 * @set_multiple: assigns output values for multiple signals defined by "mask"
 * @set_config: optional hook for all kinds of settings. Uses the same
 *	packed config format as generic pinconf.
 * @to_irq: optional hook supporting non-static gpio_to_irq() mappings;
 *	implementation may not sleep
 * @dbg_show: optional routine to show contents in debugfs; default code
 *	will be used when this is omitted, but custom code can show extra
 *	state (such as pullup/pulldown configuration).
 * @base: identifies the first GPIO number handled by this chip;
 *	or, if negative during registration, requests dynamic ID allocation.
 *	DEPRECATION: providing anything non-negative and nailing the base
 *	offset of GPIO chips is deprecated. Please pass -1 as base to
 *	let gpiolib select the chip base in all possible cases. We want to
 *	get rid of the static GPIO number space in the long run.
 * @ngpio: the number of GPIOs handled by this controller; the last GPIO
 *	handled is (base + ngpio - 1).
 * @names: if set, must be an array of strings to use as alternative
 *      names for the GPIOs in this chip. Any entry in the array
 *      may be NULL if there is no alias for the GPIO, however the
 *      array must be @ngpio entries long.  A name can include a single printk
 *      format specifier for an unsigned int.  It is substituted by the actual
 *      number of the gpio.
 * @can_sleep: flag must be set iff get()/set() methods sleep, as they
 *	must while accessing GPIO expander chips over I2C or SPI. This
 *	implies that if the chip supports IRQs, these IRQs need to be threaded
 *	as the chip access may sleep when e.g. reading out the IRQ status
 *	registers.
 * @read_reg: reader function for generic GPIO
 * @write_reg: writer function for generic GPIO
 * @pin2mask: some generic GPIO controllers work with the big-endian bits
 *	notation, e.g. in a 8-bits register, GPIO7 is the least significant
 *	bit. This callback assigns the right bit mask.
 * @reg_dat: data (in) register for generic GPIO
 * @reg_set: output set register (out=high) for generic GPIO
 * @reg_clr: output clear register (out=low) for generic GPIO
 * @reg_dir: direction setting register for generic GPIO
 * @bgpio_bits: number of register bits used for a generic GPIO i.e.
 *	<register width> * 8
 * @bgpio_lock: used to lock chip->bgpio_data. Also, this is needed to keep
 *	shadowed and real data registers writes together.
 * @bgpio_data:	shadowed data register for generic GPIO to clear/set bits
 *	safely.
 * @bgpio_dir: shadowed direction register for generic GPIO to clear/set
 *	direction safely.
 * @irqchip: GPIO IRQ chip impl, provided by GPIO driver
 * @irqdomain: Interrupt translation domain; responsible for mapping
 *	between GPIO hwirq number and linux irq number
 * @irq_base: first linux IRQ number assigned to GPIO IRQ chip (deprecated)
 * @irq_handler: the irq handler to use (often a predefined irq core function)
 *	for GPIO IRQs, provided by GPIO driver
 * @irq_default_type: default IRQ triggering type applied during GPIO driver
 *	initialization, provided by GPIO driver
 * @irq_chained_parent: GPIO IRQ chip parent/bank linux irq number,
 *	provided by GPIO driver for chained interrupt (not for nested
 *	interrupts).
 * @irq_nested: True if set the interrupt handling is nested.
 * @irq_need_valid_mask: If set core allocates @irq_valid_mask with all
 *	bits set to one
 * @irq_valid_mask: If not %NULL holds bitmask of GPIOs which are valid to
 *	be included in IRQ domain of the chip
 * @lock_key: per GPIO IRQ chip lockdep class
 *
 * A gpio_chip can help platforms abstract various sources of GPIOs so
 * they can all be accessed through a common programing interface.
 * Example sources would be SOC controllers, FPGAs, multifunction
 * chips, dedicated GPIO expanders, and so on.
 *
 * Each chip controls a number of signals, identified in method calls
 * by "offset" values in the range 0..(@ngpio - 1).  When those signals
 * are referenced through calls like gpio_get_value(gpio), the offset
 * is calculated by subtracting @base from the gpio number.
 */
struct gpio_chip {
	const char		*label;
	struct gpio_device	*gpiodev;
	struct device		*parent;
	struct module		*owner;

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

	void			(*dbg_show)(struct seq_file *s,
						struct gpio_chip *chip);
	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);
	unsigned long (*pin2mask)(struct gpio_chip *gc, unsigned int pin);
	void __iomem *reg_dat;
	void __iomem *reg_set;
	void __iomem *reg_clr;
	void __iomem *reg_dir;
	int bgpio_bits;
	spinlock_t bgpio_lock;
	unsigned long bgpio_data;
	unsigned long bgpio_dir;
#endif

#ifdef CONFIG_GPIOLIB_IRQCHIP
	/*
	 * With CONFIG_GPIOLIB_IRQCHIP we get an irqchip inside the gpiolib
	 * to handle IRQs for most practical cases.
	 */
	struct irq_chip		*irqchip;
	struct irq_domain	*irqdomain;
	unsigned int		irq_base;
	irq_flow_handler_t	irq_handler;
	unsigned int		irq_default_type;
	unsigned int		irq_chained_parent;
	bool			irq_nested;
	bool			irq_need_valid_mask;
	unsigned long		*irq_valid_mask;
	struct lock_class_key	*lock_key;
#endif

#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
};

      gpio_chip 这个数据结构一看,很多函数指针结构,明眼人秒懂,此结构是为了抽象 GPIO 的所有操作,同时适配不同芯片的一个 common 的结构,所以,这个结构是要开出去给其他芯片进行特定的操作赋值的,比如你是 Qcom 的芯片,那么你需要实现你的这些 gpio_chip 的内容。

2.1 gpio_chip 结构   

       一般的,在一个芯片中,针对所有的 I/O 口都会有配置,默认状态有些是 I/O 口全部默认 GPIO 输入(稳当)。一般芯片会提供管脚复用的功能(后期的 Linux 版本中,使用 pin control 来抽象),要使用 GPIO ,则首先需要配置他为 GPIO 功能,而不是其他的服用功能。

      而针对 GPIO 呢,有一些通用的特性,比如设置 GPIO 的方向,读 GPIO 的电平(输入的时候),写 GPIO 的电平(输出的时候),GPIO 作为外部中断输入,等等。

      gpio_chip 的抽象,其实是对 GPIO 一组 Bank 的抽象,通常在硬件上,一个芯片对 IO 口来说,分为了很多个 Bank,每个 Bank 分为了 N 组 GPIO。

比如:1 个 SoC 将 I/O 分为了 4 个 Bank:

Bank 1:GPIOA ~ GPIOB

Bank 2:GPIOC ~ GPIOD

Bank 3:GPIOE ~ GPIOF

Bank 4:GPIOG ~ GPIOH

然鹅,每个 Bank 都有 N 组寄存器来表示 GPIO 的操作,比如:

Bank 1 中,针对 GPIO A:

GPIOA_CFG 来表示对 GPIO A 的配置

GPIOA_PULL 来表示对 GPIO A 的上下拉的配置

GPIOA_DIR 来表示对 GPIO A 配置成为输入或者输出

GPIOA_DATA 来表示 GPIO A 设置为输出的时候设置为高低或者输入的时候读高低

当然,Bank 1 中 针对 GPIO B,也是一样的操作:

GPIOB_CFG 来表示对 GPIO B 的配置

GPIOB_PULL 来表示对 GPIO B 的上下拉的配置

GPIOB_DIR 来表示对 GPIO B 配置成为输入或者输出

GPIOB_DATA 来表示 GPIO B 设置为输出的时候设置为高低或者输入的时候读高低

      上面说的是一个 Bank 的情况,那么芯片有好几个 Bank,所以它们都是类似的,这里不在赘述。

       所以整体结构是如下所示(这里只是打个比方,有的芯片 Bank 很多,寄存器也很多):

       Linux Driver Gpiolib 对他们的抽象,使用 gpio_chip 对应了一组 Bank 描述,比如 Bank ·1,用一个 gpio_chip 来抽象:

       那么多个 Bank ,就用指针,或者数组来表示咯。当然这里可能说得有点不准确,gpio_chip 只是抽象了一组 Bank 的统一的接口而已。

       那么对于一颗芯片底层来说,需要根据芯片手册 Datasheet,来实现这些结构的接口。

2.2 gpio_desc 结构

      既然系统分为多个 Bank,每个 Bank 又由几组组成,那么每个 GPIO 实体就由一个 gpio_desc 来描述:


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 */
#define FLAG_SLEEP_MAY_LOOSE_VALUE 12	/* GPIO may loose value in sleep */

	/* Connection label */
	const char		*label;
	/* Name of the GPIO */
	const char		*name;
};

      这个结构比较简单,可以看到,他包含了一个 gpio_device 的结构和 flag,以及 lable 和 name;

      gdev 指针指向了这个 gpio_desc 所属的 gpio_device(马上描述),flag 代表了这个 GPIO 的属性状态;

       看起来 gpio_chip 和 gpio_desc 应该是包含关系,但是 Kernel 中并没有直接将其两个结构联系上,而是通过另外一个结构将其联系在一起,这个结构就是 gpio_device。

2.3 gpio_device 结构

      gpio_device 应该算是大内总管了(最新的内核有,Linux 3 版本的内核没有这个),如果说 gpio_chip 是对一个 Bank 的 GPIO 的硬件的具体抽象的话,那么 gpio_device 就是软件层面上对一个 Bank 的 GPIO 进行管理的单元,它的数据结构是:


enum of_gpio_flags;
enum gpiod_flags;
enum gpio_lookup_flags;
struct acpi_device;

/**
 * struct gpio_device - internal state container for GPIO devices
 * @id: numerical ID number for the GPIO chip
 * @dev: the GPIO device struct
 * @chrdev: character device for the GPIO device
 * @mockdev: class device used by the deprecated sysfs interface (may be
 * NULL)
 * @owner: helps prevent removal of modules exporting active GPIOs
 * @chip: pointer to the corresponding gpiochip, holding static
 * data for this device
 * @descs: array of ngpio descriptors.
 * @ngpio: the number of GPIO lines on this GPIO device, equal to the size
 * of the @descs array.
 * @base: GPIO base in the DEPRECATED global Linux GPIO numberspace, assigned
 * at device creation time.
 * @label: a descriptive name for the GPIO device, such as the part number
 * or name of the IP component in a System on Chip.
 * @data: per-instance data assigned by the driver
 * @list: links gpio_device:s together for traversal
 *
 * This state container holds most of the runtime variable data
 * for a GPIO device and can hold references and live on after the
 * GPIO chip has been removed, if it is still being used from
 * userspace.
 */
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
};

      在这个 gpio_device 结构中,包含了 gpio_chip(对接芯片的操作集),gpio_desc(一些 GPIO 的描述)这个结构贯穿了整个 Gpiolib,因为 gpio_device 代表的是一个 Bank,一般的 GPIO 有多个 Bank,所以 Kernel 中,对这 gpio_device 的组织是由一个 gpio_devices 的链表构成(此处是多个 device,所以后面加了 s),在 gpiolib.c:

LIST_HEAD(gpio_devices);

 3 Gpiolib 对接芯片底层

      先聊聊 Gpiolib 是怎么对接到底层实际的驱动的。在前面的 2.1 部分讲过,底层需要对接的,其实对接的部分只有那些通用的操作,其实也就是 gpio_chip 这个玩意,所以,对接底层的部分,主要关心的是这个结构体,并且对这个结构体进行赋值的过程。

      在底层对接到 Gpiolib 的时候,主要是对 gpio_chip 进行实现,然后调用 gpiochip_add 的接口,向 Gpiolib 注册你的 GPIO 。

      实现的过程,主要是根据芯片手册,实现对应的 GPIO 的操作,也就是说,把寄存器操作编程成为函数,对接到这个 gpio_chip 结构体上。

       gpio_chip 结构体相关的成员进行了赋值,并最终调用到了 gpiochip_add_data或者devm_gpiochip_add_data函数,将其注册到了内核的 Gpiolib 子系统。大致的流程是这样的,一些细节,可能由于版本不一致,内容有细微区别。接下来我们看看这个注册 GPIO 的函数。

3.1 注册 GPIO 资源(gpiochip_add_data)

      传入的结构是 gpio_chip,也就是一个 Bank 的描述,那么实际上, CPU 的 GPIO 控制器有多个 I/O Bank,那换句话说,需要 Kernel 管理的 GPIO Bank 的部分,都需要调用这个接口,注册进内核(当然,你也可以野蛮的 Bypass);

int gpiochip_add_data(struct gpio_chip *chip, void *data);

3.2 part1

这个函数分成几次来看,先是 part 1:


/**
 * gpiochip_add_data() - register a gpio_chip
 * @chip: the chip to register, with chip->base initialized
 * @data: driver-private data associated with this chip
 *
 * Context: potentially before irqs will work
 *
 * When gpiochip_add_data() is called very early during boot, so that GPIOs
 * can be freely used, the chip->parent device must be registered before
 * the gpio framework's arch_initcall().  Otherwise sysfs initialization
 * for GPIOs will fail rudely.
 *
 * gpiochip_add_data() must only be called after gpiolib initialization,
 * ie after core_initcall().
 *
 * If chip->base is negative, this requests dynamic assignment of
 * a range of valid GPIOs.
 *
 * 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.
 */
int gpiochip_add_data(struct gpio_chip *chip, void *data)
{
	unsigned long	flags;
	int		status = 0;
	unsigned	i;
	int		base = chip->base;
	struct gpio_device *gdev;

	/*
	 * First: allocate and populate the internal stat container, and
	 * set up the struct device.
	 */
	gdev = kzalloc(sizeof(*gdev), GFP_KERNEL);
	if (!gdev)
		return -ENOMEM;
	gdev->dev.bus = &gpio_bus_type;
	gdev->chip = chip;
	chip->gpiodev = gdev;
	if (chip->parent) {
		gdev->dev.parent = chip->parent;
		gdev->dev.of_node = chip->parent->of_node;
	}

#ifdef CONFIG_OF_GPIO
	/* If the gpiochip has an assigned OF node this takes precedence */
	if (chip->of_node)
		gdev->dev.of_node = chip->of_node;
#endif

	gdev->id = ida_simple_get(&gpio_ida, 0, 0, GFP_KERNEL);
	if (gdev->id < 0) {
		status = gdev->id;
		goto err_free_gdev;
	}
	dev_set_name(&gdev->dev, "gpiochip%d", gdev->id);
	device_initialize(&gdev->dev);
	dev_set_drvdata(&gdev->dev, gdev);
	if (chip->parent && chip->parent->driver)
		gdev->owner = chip->parent->driver->owner;
	else if (chip->owner)
		/* TODO: remove chip->owner */
		gdev->owner = chip->owner;
	else
		gdev->owner = THIS_MODULE;

.......................
part 1 end
.......................

}

      part 1 中,因为传入的结构是 gpio_chip,他代表了是一个 Bank,但是并没有 gpio_device 的结构,所以,在这个函数中,首先分配一个 gpio_device 的结构,并将其结构体成员的 chip ,等等进行赋值,建立起相关的结构联系。

3.2 part 2

再看 part 2:

..............
part 2 start
..............

	gdev->descs = kcalloc(chip->ngpio, sizeof(gdev->descs[0]), GFP_KERNEL);
	if (!gdev->descs) {
		status = -ENOMEM;
		goto err_free_ida;
	}

	if (chip->ngpio == 0) {
		chip_err(chip, "tried to insert a GPIO chip with zero linesn");
		status = -EINVAL;
		goto err_free_descs;
	}

	if (chip->label)
		gdev->label = kstrdup(chip->label, GFP_KERNEL);
	else
		gdev->label = kstrdup("unknown", GFP_KERNEL);
	if (!gdev->label) {
		status = -ENOMEM;
		goto err_free_descs;
	}

	gdev->ngpio = chip->ngpio;
	gdev->data = data;

..............
part 2 end
..............

      part 2 中,由于 1 个 Bank不仅仅只有一个 GPIO,所以 gpio_chip->ngpio 的结构表示了这个 Bank 一共的 GPIO 个数,每一个 GPIO 使用一个 gpio_desc 表示,所以,这里分配了 ngpio 个 descs;

3.3 part 3


.............
part 3 start
.............    
	spin_lock_irqsave(&gpio_lock, flags);

	/*
	 * TODO: this allocates a Linux GPIO number base in the global
	 * GPIO numberspace for this chip. In the long run we want to
	 * get *rid* of this numberspace and use only descriptors, but
	 * it may be a pipe dream. It will not happen before we get rid
	 * of the sysfs interface anyways.
	 */
	if (base < 0) {
		base = gpiochip_find_base(chip->ngpio);
		if (base < 0) {
			status = base;
			spin_unlock_irqrestore(&gpio_lock, flags);
			goto err_free_label;
		}
		/*
		 * TODO: it should not be necessary to reflect the assigned
		 * base outside of the GPIO subsystem. Go over drivers and
		 * see if anyone makes use of this, else drop this and assign
		 * a poison instead.
		 */
		chip->base = base;
	}
	gdev->base = base;

	status = gpiodev_add_to_list(gdev);
	if (status) {
		spin_unlock_irqrestore(&gpio_lock, flags);
		goto err_free_label;
	}

	spin_unlock_irqrestore(&gpio_lock, flags);

	for (i = 0; i < chip->ngpio; i++) {
		struct gpio_desc *desc = &gdev->descs[i];

		desc->gdev = gdev;

		/* 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, and in case chip->get_direction is not set, we may
		 * expose the wrong direction in sysfs.
		 */
		desc->flags = !chip->direction_input ? (1 << FLAG_IS_OUT) : 0;
	}

#ifdef CONFIG_PINCTRL
	INIT_LIST_HEAD(&gdev->pin_ranges);
#endif

	status = gpiochip_set_desc_names(chip);
	if (status)
		goto err_remove_from_list;

	status = gpiochip_irqchip_init_valid_mask(chip);
	if (status)
		goto err_remove_from_list;

	status = of_gpiochip_add(chip);
	if (status)
		goto err_remove_chip;

	acpi_gpiochip_add(chip);

	/*
	 * By first adding the chardev, and then adding the device,
	 * we get a device node entry in sysfs under
	 * /sys/bus/gpio/devices/gpiochipN/dev that can be used for
	 * coldplug of device nodes and other udev business.
	 * We can do this only if gpiolib has been initialized.
	 * Otherwise, defer until later.
	 */
	if (gpiolib_initialized) {
		status = gpiochip_setup_dev(gdev);
		if (status)
			goto err_remove_chip;
	}
	return 0;

err_remove_chip:
	acpi_gpiochip_remove(chip);
	gpiochip_free_hogs(chip);
	of_gpiochip_remove(chip);
	gpiochip_irqchip_free_valid_mask(chip);
err_remove_from_list:
	spin_lock_irqsave(&gpio_lock, flags);
	list_del(&gdev->list);
	spin_unlock_irqrestore(&gpio_lock, flags);
err_free_label:
	kfree(gdev->label);
err_free_descs:
	kfree(gdev->descs);
err_free_ida:
	ida_simple_remove(&gpio_ida, gdev->id);
err_free_gdev:
	/* failures here can mean systems won't boot... */
	pr_err("%s: GPIOs %d..%d (%s) failed to registern", __func__,
	       gdev->base, gdev->base + gdev->ngpio - 1,
	       chip->label ? : "generic");
	kfree(gdev);
	return status;

        在 part 3 中,base 代表了每个 Bank 的编号,将其赋值;然后通过 gpiodev_add_to_list(gdev) 将这个 gdev 挂到全局的 gpio_devices :


/*
 * Add a new chip to the global chips list, keeping the list of chips sorted
 * by range(means [base, base + ngpio - 1]) order.
 *
 * Return -EBUSY if the new chip overlaps with some other chip's integer
 * space.
 */
static int gpiodev_add_to_list(struct gpio_device *gdev)
{
	struct gpio_device *prev, *next;

	if (list_empty(&gpio_devices)) {
		/* initial entry in list */
		list_add_tail(&gdev->list, &gpio_devices);
		return 0;
	}

	next = list_entry(gpio_devices.next, struct gpio_device, list);
	if (gdev->base + gdev->ngpio <= next->base) {
		/* add before first entry */
		list_add(&gdev->list, &gpio_devices);
		return 0;
	}

	prev = list_entry(gpio_devices.prev, struct gpio_device, list);
	if (prev->base + prev->ngpio <= gdev->base) {
		/* add behind last entry */
		list_add_tail(&gdev->list, &gpio_devices);
		return 0;
	}

	list_for_each_entry_safe(prev, next, &gpio_devices, list) {
		/* at the end of the list */
		if (&next->list == &gpio_devices)
			break;

		/* add between prev and next */
		if (prev->base + prev->ngpio <= gdev->base
				&& gdev->base + gdev->ngpio <= next->base) {
			list_add(&gdev->list, &prev->list);
			return 0;
		}
	}

	dev_err(&gdev->dev, "GPIO integer space overlap, cannot add chipn");
	return -EBUSY;
}

接着就是设置一些 name 字段,配置中断之类的,初始化每个 desc[] 结构的 flags,最后调用:

	/*
	 * By first adding the chardev, and then adding the device,
	 * we get a device node entry in sysfs under
	 * /sys/bus/gpio/devices/gpiochipN/dev that can be used for
	 * coldplug of device nodes and other udev business.
	 * We can do this only if gpiolib has been initialized.
	 * Otherwise, defer until later.
	 */
	if (gpiolib_initialized) {
		status = gpiochip_setup_dev(gdev);
		if (status)
			goto err_remove_chip;
	}

        然后,不出意外的话,返回 0;

        这里说一下 gpiochip_setup_dev 调用,这个是在 Gpiolib init 的时候调用 gpiochip_setup_devs:

static int __init gpiolib_dev_init(void)
{
	int ret;

	/* Register GPIO sysfs bus */
	ret  = bus_register(&gpio_bus_type);
	if (ret < 0) {
		pr_err("gpiolib: could not register GPIO bus typen");
		return ret;
	}

	ret = alloc_chrdev_region(&gpio_devt, 0, GPIO_DEV_MAX, "gpiochip");
	if (ret < 0) {
		pr_err("gpiolib: failed to allocate char dev regionn");
		bus_unregister(&gpio_bus_type);
	} else {
		gpiolib_initialized = true;
		gpiochip_setup_devs();
	}
	return ret;
}
core_initcall(gpiolib_dev_init);

而这个 gpiochip_setup_devs 对每一个 gpio_devicecs 节点调用:gpiochip_setup_dev:

static void gpiochip_setup_devs(void)
{
	struct gpio_device *gdev;
	int err;

	list_for_each_entry(gdev, &gpio_devices, list) {
		err = gpiochip_setup_dev(gdev);
		if (err)
			pr_err("%s: Failed to initialize gpio device (%d)n",
			       dev_name(&gdev->dev), err);
	}
}

最后到:


static int gpiochip_setup_dev(struct gpio_device *gdev)
{
	int status;

	cdev_init(&gdev->chrdev, &gpio_fileops);
	gdev->chrdev.owner = THIS_MODULE;
	gdev->dev.devt = MKDEV(MAJOR(gpio_devt), gdev->id);

	status = cdev_device_add(&gdev->chrdev, &gdev->dev);
	if (status)
		return status;

	chip_dbg(gdev->chip, "added GPIO chardev (%d:%d)n",
		 MAJOR(gpio_devt), gdev->id);

	status = gpiochip_sysfs_register(gdev);
	if (status)
		goto err_remove_device;

	/* From this point, the .release() function cleans up gpio_device */
	gdev->dev.release = gpiodevice_release;
	pr_debug("%s: registered GPIOs %d to %d on device: %s (%s)n",
		 __func__, gdev->base, gdev->base + gdev->ngpio - 1,
		 dev_name(&gdev->dev), gdev->chip->label ? : "generic");

	return 0;

err_remove_device:
	cdev_device_del(&gdev->chrdev, &gdev->dev);
	return status;
}

      其实就是注册了字符设备,并且添加到了 sysfs;

      从注释上看,因为不知道这个 init 和我们的对接底层的驱动的 init 谁先执行到,所以用了一个变量 gpiolib_initialized 来表示当前的 Gpiolib 是不是已经完成了相关的字符设备的注册,如果是 Gpiolib 先去 init 的话,那么 gpiolib_initialized  ture,芯片对接底层的部分错过 gpio_chip setup 的机会,所以需要重新调用这个 gpiochip_setup_dev 接口,反之 OK;

      到这里,对接底层驱动的部分基本上 OK 了,小伙伴们需要按照自己芯片的 Specific 去做自己的 gpio_chip 结构并最终通过 gpiochip_add_data 添加到 Gpiolib 子系统中;

       还有一点需要注意到的是,小伙伴们需要自行定义一些结构,来获得并表示自己 Bank 的虚拟地址等等,这样才能操作到实际的硬件寄存器(后面文章具体分析一个gpio control);

4、Gpiolib 为其他驱动提供的 APIs

4.1 gpio_to_desc

4.2 gpiod_put

4.3 gpiod_direction_input

4.4 gpiod_direction_output

最后

以上就是重要草莓为你收集整理的Linux内核4.14版本——GPIO子系统(1)——gpiolib分析1、简述2、Gpiolib 相关数据结构分析 3 Gpiolib 对接芯片底层4、Gpiolib 为其他驱动提供的 APIs的全部内容,希望文章能够帮你解决Linux内核4.14版本——GPIO子系统(1)——gpiolib分析1、简述2、Gpiolib 相关数据结构分析 3 Gpiolib 对接芯片底层4、Gpiolib 为其他驱动提供的 APIs所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(50)

评论列表共有 0 条评论

立即
投稿
返回
顶部