目录

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;
#endifgdev->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;elsegdev->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 lines\n");status = -EINVAL;goto err_free_descs;}if (chip->label)gdev->label = kstrdup(chip->label, GFP_KERNEL);elsegdev->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_PINCTRLINIT_LIST_HEAD(&gdev->pin_ranges);
#endifstatus = 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 register\n", __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 chip\n");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 type\n");return ret;}ret = alloc_chrdev_region(&gpio_devt, 0, GPIO_DEV_MAX, "gpiochip");if (ret < 0) {pr_err("gpiolib: failed to allocate char dev region\n");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. Linux内核4.14版本——watchdog看门狗框架分析

    目录 0 简介 1. 设备的注册 1.1 dw_wdt_drv_probe 1.2 watchdog_register_device 1.3 __watchdog_register_device 1. ...

  2. Linux内核4.14版本——Nand子系统(1)——hisi504_nand.c分析

    1. 简介 2. DTS 3. hisi_nfc_probe函数 3.1 获取dts中的资源 3.2 设置nand-chip结构体中必要的字段 3.3 nand_scan_ident扫描识别nand控 ...

  3. Linux内核4.14版本:ARM64的内核启动过程(二)——start_kernel

    目录 1. rest_init 2. init 进程(kernel_init) 2.1 kernel_init_freeable 2.1.1 do_basic_setup 2.1.2 prepare_ ...

  4. Linux内核4.14版本——DMA Engine框架分析(6)-实战(测试dma驱动)

    1. dw-axi-dmac驱动 2. dma的测试程序 2.1 内核程序 2.2 用户测试程序 1. dw-axi-dmac驱动 dw-axi-dmac驱动4.14版本没有,是从5.4版本移植的,基 ...

  5. Linux内核4.14版本——alsa框架分析(1)—alsa简介

    目录 一,ALSA声音编程介绍 二,ALSA历史 三,数字音频基础 四,ALSA基础 五,ALSA体系结构 六,设备命名 七,声音缓存和数据传输 八,Over and Under Run 九,一个典型 ...

  6. Linux内核4.14版本——drm框架分析(1)——drm简介

    目录 1. DRM简介(Direct Rendering Manager) 1.1 DRM发展历史 1.2 DRM架构对比FB架构优势 1.3 DRM图形显示框架 1.4 DRM图形显示框架涉及元素 ...

  7. Linux内核4.14版本——SPI NOR子系统(2)——spi-nor.c分析

    1. 简介 2. spi_nor_scan 2.1 检查结构体struct spi_nor是否合格,匹配支持的nor flash ID得到info 2.1.1 spi_nor_check 2.1.2 ...

  8. Linux内核4.14版本——SPI NOR子系统(3)——cadence-quadspi.c分析

    目录 1. 芯片简介 1.1 模块与接口 1.2 访问模式 2. DTS 2.1 reg 2.2 cdns,trigger-address 2.3 匹配DTS的驱动程序入口 3. cqspi_prob ...

  9. Linux内核4.14版本——alsa框架分析(8)-ASoC(Codec)

    1. 概述 ASOC的出现是为了让Codec独立于CPU,减少和CPU之间的耦合,这样同一个Codec驱动无需修 改就可以适用任何一款平台.还是以下图做参考例子: 在Machine中已经知道,snd_ ...

最新文章

  1. 常用代码整理(重要)
  2. python中的open函数
  3. QDoc文件结构documentstructure
  4. 神奇的sstream头文件(整型与字符串自由转换)
  5. 基于ARM IP的看门狗设计与功能验证
  6. 论文笔记:Visual Question Answering as a Meta Learning Task
  7. Android APK代码混淆与资源混淆详解,你确定不看?
  8. 自学JAVA-2:数据类型与运算符
  9. 计算机显示无法格式化,计算机格式化SD卡提示Windows无法完成格式化解决方法
  10. 面试官问如何优化慢 SQL ?(附两万字SQL面试题)
  11. 高仿富途牛牛-组件化(六)-炒鸡牛逼的布局记忆功能(序列化和反序列化)
  12. Java 8计算两个日期之间的月份
  13. hive之full outer join(全连接)使用方法
  14. 新手在IDEA如何创建一个Web项目
  15. 自己动手制作Windows RE(Windows恢复环境)的启动U盘
  16. 因特网上的英语学习资源
  17. remote_message
  18. 网站忘记密码怎么找回?
  19. 蓝牙扫描枪直连蓝牙打印机
  20. IC设计职位介绍之“模拟版图工程师”

热门文章

  1. echart仪表盘旋转_使用echarts绘制漂亮的渐变键盘仪表盘
  2. diango使用体验
  3. 写给自己,对自己的反思
  4. DHT11的使用资料
  5. C# 频繁申请数组偶尔报错OutOfMemoryException
  6. 各个大学论坛大全,大学校园美女
  7. 为什么你的平面设计字体排版总是那么LOW?
  8. VS2019——easyx新版图形库图形化界面及音频播放的疑难杂症
  9. Apereo CAS 4.1 反序列化 RCE 漏洞复现实验报告
  10. uniapp小程序端使用腾讯地图