程序项目代做,有需求私信(vue、React、Java、爬虫、电路板设计、嵌入式linux等)

linux驱动移植-GPIO子系统

----------------------------------------------------------------------------------------------------------------------------
内核版本:linux 5.2.8
根文件系统:busybox 1.25.0
u-boot:2016.05
----------------------------------------------------------------------------------------------------------------------------

一、GPIO子系统

1.1 GPIO概述

GPIO(General purpose input/putput)也就是通用输入/输出口,片上系统 (SoC) 处理器对 GPIO 有很大的依赖。在某些情况下,每个非专用引脚都可配置为GPIO,且大多数芯片都最少有一些GPIO

可编程逻辑器件(类似 FPGA) 可以方便地提供 GPIO。像电源管理和音频编解码器这样的多功能芯片经常留有一些这样的引脚来帮助那些引脚匮乏的 SoC。同时还有通过I2C或SPI串行总线连接的“GPIO扩展器”芯片。

GPIO的实际功能因系统而异。通常用法有:

  • 输出值可写 (高电平=1,低电平=0)。一些芯片也有如何驱动这些值的选项,例如只允许输出一个值、支持“线与”及其他取值类似的模式(值得注意的是“开漏”信号);
  • 输入值可读(1、0)。一些芯片支持引脚在配置为“输出”时回读,这对于类似“线与”的情况(以支持双向信号)是非常有用的。GPIO控制器可能有输入去毛刺/消抖逻辑,这有时需要软件控制;
  • 输入通常可作为 IRQ 信号,一般是沿触发,但有时是电平触发。这样的 IRQ可能配置为系统唤醒事件,以将系统从低功耗状态下唤醒;
  • 通常一个GPIO根据不同产品电路板的需求,可以配置为输入或输出,也有仅支持单向的;
  • 大部分GPIO可以在持有自旋锁时访问,但是通常由串行总线扩展的GPIO不允许持有自旋锁。但某些系统也支持这种类型;

比如我们所使用的的S3C2440这款SoC共有9个GPIO控制器,每个控制器下有多个GPIO,一共有130个多功能输入/输出引脚。具体如下:

  • 端口A(GPA):25位输出端口 ;
  • 端口B(GPB):11 位输入/输出端口 ;
  • 端口C(GPC):16 位输入/输出端口;
  • 端口D(GPD):16 位输入/输出端口;
  • 端口E(GPE):16 位输入/输出端口 ;
  • 端口F(GPF):8 位输入/输出端口;
  • 端口G(GPG):16 位输入/输出端口;
  • 端口H(GPH):9 位输入/输出端口;
  • 端口J(GPJ):13 位输入/输出端口;

对于给定的电路板,每个GPIO都用于某个特定的目的,如监控 MMC/SD 卡的插入/移除、检测卡的写保护状态、驱动 LED、配置收发器、模拟串行总线、复位硬件看门狗、感知开关状态等等。

1.2 GPIO子系统框架

在linux内核中,要想使用GPIO口,首先就要了解gpiolib,linux内核对GPIO资源进行了抽象,抽象出来的概念就是gpiolib。

中间层是gpiolib,用于管理系统中的GPIO。gpiolib汇总了GPIO的通用操作,根据GPIO的特性:

  • 对上gpiolib提供的一套统一通用的操作GPIO的软件接口,屏蔽了不同芯片的具体实现;
  • 对下gpiolib 提供了针对不同芯片操作的一套framework,针对不同芯片,只需要实现gpio controller driver ,然后使用 gpiolib 提供的注册函数,将其挂接到gpiolib 上,这样就完成了这一套东西;

对于其它驱动来说,比如在linux驱动移植-SPI驱动移植(OLED SSD1306),就用到通用的gpiolib的函数来进行I/O口的操作。

1.3 目录结构

linux内核将GPIO驱动相关的代码放在drivers/gpio目录下,这下面的文件还是比较多的,我们大概了解一下即可。

GPIO核心功能文件gpiolib.c,提供的一套统一通用的操作GPIO的软件接口。

1.4 标识GPIO

在linux内核,每GPIO是通过无符号整型来标识的,范围是0到 MAX_INT。保留“负”数用于其他目的,例如标识信号在这个板子上不可用”或指示错误。

平台会定义这些整数的用法,且通常使用 #define来定义GPIO,这样板级特定的启动代码可以直接关联相应的原理图。

例如:一个平台使用编号32-159来标识GPIO;而在另一个平台使用编号0-63标识一组 GPIO控制器,64-79标识另一类 GPIO 控制器;且在一个含有FPGA 的特定板子上使用 80-95来标识GPIO;编号不一定要连续,那些平台中,也可以使用编号2000-2063来标识一个 I2C接口的GPIO扩展器中的GPIO。

二、GPIO核心数据结构

学习GPIO驱动,首先要了解驱动框架涉及到的数据结构,知道每个数据结构以及成员的含义之后,再去看源码就容易了。

2.1 struct gpio_chip

看到struct gpio_chip这个结构体,不由而然的想起了中断子系统中我们介绍过的struct irq_chip;内核其使用struct irq_chip对中断控制器的接口抽象;其中的成员大多用于操作底层硬件,比如设置寄存器以屏蔽中断,使能中断,清除中断等。

那么我们不难猜到struct gpio_chip对GPIO控制器的接口抽象,是用于适配不同芯片的一个通用结构。

它提供了一些成员变量和函数指针,用于描述GPIO控制器的行为,其中的函数指针用于操作底层的硬件,比如配置为输入/输出模式、设置输出值等。

它的定义在 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.
 *      It is recommended to always implement this function, even on
 *      input-only or output-only gpio chips.
 * @direction_input: configures signal "offset" as input, or returns error
 *      This can be omitted on input-only or output-only gpio chips.
 * @direction_output: configures signal "offset" as output, or returns error
 *      This can be omitted on input-only or output-only gpio chips.
 * @get: returns value for signal "offset", 0=low, 1=high, or negative error
 * @get_multiple: reads values for multiple signals defined by "mask" and
 *      stores them in "bits", returns 0 on success 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
 * @be_bits: if the generic GPIO has big endian bit order (bit 31 is representing
 *      line 0, bit 30 is line 1 ... bit 0 is line 31) this is set to true by the
 *      generic GPIO core. It is for internal housekeeping only.
 * @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_out: direction out setting register for generic GPIO
 * @reg_dir_in: direction in setting register for generic GPIO
 * @bgpio_dir_unreadable: indicates that the direction register(s) cannot
 *      be read and we need to rely on out internal state tracking.
 * @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. A "1" in this word means the line is set as
 *      output.
 *
 * 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);
        int                     (*get_multiple)(struct gpio_chip *chip,
                                                unsigned long *mask,
                                                unsigned long *bits);
        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                     (*init_valid_mask)(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);
        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

#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

        /**
         * @need_valid_mask:
         *
         * If set core allocates @valid_mask with all its values initialized
         * with init_valid_mask() or set to one if init_valid_mask() is not
         * defined
         */
        bool need_valid_mask;

        /**
         * @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
};

其中部分参数含义如下:

  • label:GPIO控制器标签,比如GPA、GPB;
  • gpiodev:指向一个struct gpio_device,GPIO设备的内部状态持有者,不透明结构,包含了一个GPIO控制器所有gpio对应的gpio_desc等信息;
  • parent:提供GPIO的可选父设备;
  • request :用于SoC激活的可选回调函数,比如启用模块电源或时钟。如果提供了,在调用gpio_request()或gpiod_get()时,它会在分配GPIO之前执行;
  • free: 是一个可选的回调函数,用于特定芯片的释放,比如禁用模块电源或时钟。如果提供了,那么在调用gpiod_put()或gpio_free()时,它会在GPIO被释放之前执行;
  • get_direction :用于判断偏移为offset的GPIO是输入还是输出模式。返回值应为0表示out,,1表示in,或负错误;
  • direction_input :将偏移为offset的GPIO配置为输入,或返回错误;这可以在仅输入或仅输出的gpio芯片上省略;
  • direction_output :将偏移为offset的GPIO配置为输出,或返回错误;这可以在仅输入或仅输出的gpio芯片上省略;
  • get:获取偏移为offset的GPIO的的输出电平,0低电平,1电平,或负错误;
  • get_multiple:读取由“mask”定义的多个信号的值,并将它们存储在“bits”中,成功返回0或负错误;
  • set:设置偏移为offset的GPIO的的输出电平,0低电平,1电平;
  • set_multiple:为由“mask”定义的多个信号分配输出值;
  • set_config:用于各种设置的可选挂钩。使用与通用引脚配置相同的打包配置格式;
  • base:标识GPIO控制器的第一个GPIO编号;或者,在注册时为负,请求动态ID分配。DEPRECATION;提供任何非负数并固定GPIO芯片的基础偏移量已经过时。请将-1作为基础传递,以便在所有可能的情况下让gpiolib选择芯片基础。我们希望在长期内摆脱静态GPIO编号空间;
  • ngpio:GPIO控制器包含的GPIO数量;最后一个GPIO编号是(base + ngpio-1);
  • to_irq:将偏移为offset的GPIO映射到IRQ并返回相关的中断编号;
  • names:如果设置,必须是用作该芯片中GPIO的替代名称的字符串数组。数组中的任何条目如果没有GPIO的别名,则可以为NULL,但是数组必须为@ngpio个条目。名称可以包括一个单独的printk格式说明符,用于无符号int。它将由gpio的实际数字替换;
  • can_sleep:必须设置标志位iff get()/set()方法会睡眠,因为它们必须在通过I2C或SPI访问GPIO扩展器芯片时进行睡眠。这意味着如果芯片支持IRQ,这些IRQ需要作为线程,因为当例如读取IRQ状态寄存器时,芯片访问可能会休眠;
  • read_reg:通用GPIO的读取器函数;
  • write_reg:通用GPIO的写入器函数;
  • be_bits:如果通用GPIO具有大端位序(位31表示线0,位30表示线1...位0表示线31),则通用GPIO核心将其设置为true。仅用于内部记录;
  • reg_dat:通用GPIO数据(输入)寄存器;
  • reg_set:通用GPIO输出设置寄存器(out=high);
  • reg_clr:通用GPIO输出清除寄存器(out=low);
  • reg_dir_out:通用GPIO方向出设置寄存器;
  • reg_dir_in:通用GPIO方向进设置寄存器;
  • bgpio_dir_unreadable:指示无法读取方向寄存器,我们需要依赖内部状态跟踪。;
  • bgpio_bits:用于通用GPIO的寄存器位数,即<register width> * 8;
  • bgpio_lock:用于锁定chip->bgpio_data。此外,这需要将影子和实际数据寄存器写在一起;
  • bgpio_data:通用GPIO的阴影数据寄存器,以安全地清除/设置位;
  • bgpio_dir:通用GPIO的阴影方向寄存器,以安全地清除/设置方向。在此字中,“1”表示该行被设置为输出;

需要注意的上面说的偏移offset指的是从当前GPIO控制器base编号算起的GPIO口:

  • 比如offset=0,代表的是编号为base的GPIO口;
  • 比如offset=1,代表的是编号为base+1的GPIO口;

一般而言,一款芯片的所有GPIO引脚都是支持配置的,默认情况下一般为输入。除了作为输入/输出,可能还提供引脚复用的功能,要使用GPIO,我们首先需要配置为输入/输出。

针对GPIO,有一些通用的特性,比如设置GPIO的方向,读取GPIO的电平,设置GPIO输出的电平,将GPIO作为外部中断输入等。

struct gpio_chip的抽象,实际上就是对一个GPIO控制器的抽象,通常在硬件上,一个SoC就IO口而言,分为了很多个GPIO控制器,每个GPIO控制下又有许多个GPIO端口。

比如:S3C2440分为了9 个GPIO控制器:

GPIO控制器 GPIO端口名称 GPIO端口数量
GPIOA GPA0~GPA24 25
GPIOB GPB0~GPB10 11
GPIOC GPC0~GPC15 16
GPIOD GPD0~GPD15 16
GPIOE GPE0~GPE15 16
GPIOF GPF0~GPF7 8
GPIOG GPG0~GPG15 16 
GPIOH GPH0~GPH10 11 (这里明明11个,datasheet说的总共9个)
GPIOJ GPJ0~GPJ12 13 

每个GPIO控制器都有n个寄存器来控制GPIO的操作,比如,GPIOA控制器有以下几个寄存器:

GPACON 配置端口A的引脚:输入、输出、功能复用
GPADAT 配置端口A的数据寄存器
GPAUP 端口A的上拉使能寄存器

2.2 struct gpio_desc

struct gpio_desc同中断子系统中的struct irq_desc类似,在中断子系统中使用struct irq_desc来描述一个IRQ,那这里就是通过struct gpio_desc去描述某一个具体的GPIO口。它的定义在drivers/gpio/gpiolib.h文件,如下:

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

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

其中部分参数含义如下:

  • gdev:指向了一个gpio_device;内核是通过这个指针将gpio_chip和gpio_desc连接起来的,这个也和中断子系统中的irq_data数据结构类似;
  • flags:GPIO属性标志,比如输出、上拉使能,开漏输出等;
  • lable:标签;
  • name:名称;

2.3 struct gpio_device

struct gpio_device这个结构是一个比较重要的数据结构,这是一个描述GPIO设备内部状态的结构体gpio_device,其定义在drivers/gpio/gpiolib.h文件,如下:

/**
 * 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;
        const 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
};

其中部分参数含义如下:

  • idGPIO控制器的ID;
  • dev:GPIO设备的内核设备结构体;把gpio_device看做是device的子类;
  • chrdev:GPIO设备的字符设备;
  • chip:指向一个gpio_chip,描述当前GPIO控制器的操作集;
  • descs:指向一个gpio_desc数组,由ngpio个gpio_desc构成的数组,用于描述该GPIO控制器上的每一个GPIO;
  • base:在全局GPIO编号空间中的编号基址;
  • ngpio:支持多少个GPIO,等于descs数组的大小;
  • label:GPIO设备的描述性名称
  • data:驱动程序分配的私有数据;
  • list:用于构建双向链表,将gpio_device链接在一起;

该结构体gpio_device用于保存GPIO控制器的运行时变量数据,一个SoC有多个 GPIO控制器,所以linux内核中,将gpio_device链接成一个双向链表,在 drivers/gpio/gpiolib.c定义:

LIST_HEAD(gpio_devices);

 三、GPIO控制器驱动核心API

GPIO控制器驱动编写,实际上就是去根据SoC的GPIO控制器的数量去构gpio_chip数组,然后去根据SoC GPIO控制器寄存器去编写gpio_chip的操作函数。最后将其注册到gpiolib即可。

下图显示了注册具有两个GPIO引脚的GPIO控制器之后,gpio_chip、gpio_desc、gepio_device、gpio_devices之间的关系:

3.1 注册GPIO控制器

将gpio_chip注册到gpiolib子系统,一般通过gpiochip_add函数来完成。gpiochip_add函数定义在include/linux/gpio/driver.h文件中:

static inline int gpiochip_add(struct gpio_chip *chip)
{
        return gpiochip_add_data(chip, NULL);
}

函数的参数为gpio_chip,也就是一个GPIO控制器的描述,那么实际上, SoC有多个GPIO控制器时,就需要多次调用这个接口,

3.1.1 gpiochip_add_data

gpiochip_add其内部调用了gpiochip_add_data,同样是在该文件中定义:

/**
 * 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.
 */
#ifdef CONFIG_LOCKDEP
#define gpiochip_add_data(chip, data) ({                \
                static struct lock_class_key lock_key;  \
                static struct lock_class_key request_key;         \
                gpiochip_add_data_with_key(chip, data, &lock_key, \
                                           &request_key);         \
        })
#else
#define gpiochip_add_data(chip, data) gpiochip_add_data_with_key(chip, data, NULL, NULL)
#endif

gpiochip_add_data函数的第二个参数为驱动的私有数据。gpiochip_add在调用gpiochip_add_data的时候第二个参数传入了NULL。

3.1.2 devm_gpiochip_add_data

此外,gpiolib还提供了带devm的接口,管理其内存,定义如下:

/**
 * devm_gpiochip_add_data() - Resource manager gpiochip_add_data()
 * @dev: pointer to the device that gpio_chip belongs to.
 * @chip: the chip to register, with chip->base initialized
 * @data: driver-private data associated with this chip
 *
 * Context: potentially before irqs will work
 *
 * The gpio chip automatically be released when the device is unbound.
 *
 * 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 devm_gpiochip_add_data(struct device *dev, struct gpio_chip *chip,
                           void *data)
{
        struct gpio_chip **ptr;
        int ret;

        ptr = devres_alloc(devm_gpio_chip_release, sizeof(*ptr),
                             GFP_KERNEL);
        if (!ptr)
                return -ENOMEM;

        ret = gpiochip_add_data(chip, data);
        if (ret < 0) {
                devres_free(ptr);
                return ret;
        }

        *ptr = chip;
        devres_add(dev, ptr);

        return 0;
}
3.1.3 gpiochip_add_data_with_key

gpiochip_add_data函数又调用了gpiochip_add_data_with_key,其定义在drivers/gpio/gpiolib.c文件

int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data,
                               struct lock_class_key *lock_key,
                               struct lock_class_key *request_key)
{
        unsigned long   flags;
        int             status = 0;
        unsigned        i;
        int             base = chip->base;   // 当前GPIO控制器的第一个GPIO编号
        struct gpio_device *gdev;

        /*
         * First: allocate and populate the internal stat container, and
         * set up the struct device.
         */
        gdev = kzalloc(sizeof(*gdev), GFP_KERNEL);          // 分配一个gpio_device
        if (!gdev)
                return -ENOMEM;
        gdev->dev.bus = &gpio_bus_type;                    // 设置bus
        gdev->chip = chip;                                 // 设置chip 
        chip->gpiodev = gdev;                              // 设置gpiodev  
        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;
        else
                chip->of_node = gdev->dev.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);   // 设置名称 goiochip%d
        device_initialize(&gdev->dev);                      // 初始化dev  
        dev_set_drvdata(&gdev->dev, gdev);                  // 设置gdev->dev.driver_data = 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;

        gdev->descs = kcalloc(chip->ngpio, sizeof(gdev->descs[0]), GFP_KERNEL);   // 根据一个GPIO控制器所拥有的GPIO数量,申请gpio_desc
        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->ngpio > FASTPATH_NGPIO)
                chip_warn(chip, "line cnt %u is greater than fast path cnt %u\n",
                chip->ngpio, FASTPATH_NGPIO);

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

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

        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);     // 将当前gdev添加到双向链表gpio_devices
        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++)        // 设置每个gpio_desc所指向的gdev
                gdev->descs[i].gdev = gdev;

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

        status = gpiochip_set_desc_names(chip);  // 设置每个gpio_desc的名字,值取值chip->names[i]
        if (status)
                goto err_remove_from_list;

        status = gpiochip_irqchip_init_valid_mask(chip); // 中断配置
        if (status)
                goto err_remove_from_list;

        status = gpiochip_alloc_valid_mask(chip);
        if (status)
                goto err_remove_irqchip_mask;

        status = gpiochip_add_irqchip(chip, lock_key, request_key);
        if (status)
                goto err_free_gpiochip_mask;

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

        status = gpiochip_init_valid_mask(chip);
        if (status)
                goto err_remove_of_chip;

        for (i = 0; i < chip->ngpio; i++) {     // 初始化每一个GPIO对应的gpio_desc
                struct gpio_desc *desc = &gdev->descs[i];

                if (chip->get_direction && gpiochip_line_is_valid(chip, i)) {
                        if (!chip->get_direction(chip, i))           // 0输出 进入
                                set_bit(FLAG_IS_OUT, &desc->flags);  // 设置标志位为输出
                        else
                                clear_bit(FLAG_IS_OUT, &desc->flags); // 清除输出标志位
                } else {
                        if (!chip->direction_input)
                                set_bit(FLAG_IS_OUT, &desc->flags);  
                        else
                                clear_bit(FLAG_IS_OUT, &desc->flags);
                }
        }

        acpi_gpiochip_add(chip);

        machine_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_acpi_chip;
        }
        return 0;

err_remove_acpi_chip:
        acpi_gpiochip_remove(chip);
err_remove_of_chip:
        gpiochip_free_hogs(chip);
        of_gpiochip_remove(chip);
err_remove_chip:
        gpiochip_irqchip_remove(chip);
err_free_gpiochip_mask:
        gpiochip_free_valid_mask(chip);
err_remove_irqchip_mask:
        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_const(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, %d\n", __func__,
               gdev->base, gdev->base + gdev->ngpio - 1,
               chip->label ? : "generic", status);
        kfree(gdev);
        return status;
}

该函数:

  • 首先分配一个gpio_device结构,然后初始化其成员,建立起与chip的关系;
  • 根据一个GPIO控制器所拥有的GPIO数,申请gpio_desc;每一个GPIO对应一个gpio_desc;
  • 如果没有为GPIO控制器指定起始编号,那内核将会自动分配,需要注意每个GPIO控制器内每个GPIO对应的编号是全局唯一的;
  • 将申请的gpio_device添加到双向链表gpio_devices;需要注意将gpio_device添加到链表的时候,会按照顺序进行排序,比如31~40位于20~30和41~50之前;
  • 配置chip中断相关的;
  • 初始化每个GPIO对应的gpio_desc成员,包括gdev、name、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_acpi_chip;
        }

一般并不会进入这个分支,因为gpiochip_setup_dev会在gpiolib的模块入口函数gpiolib_dev_init中调用。

3.2 驱动入口

core_initcall(gpiolib_dev_init);

这里我们可以把core_initcall当做我们之前介绍的module_init(等价device_initcall),只不过是linux内核启动期间调用它们的顺序优先级不一样,core_initcall优先级更高。

static int __init gpiolib_dev_init(void)
{
        int ret;

        /* Register GPIO sysfs bus */
        ret = bus_register(&gpio_bus_type);   // 注册GPIO总线,总线名称为gpio 会创建/syc/bus/gpio文件夹
        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");    // 动态申请主设备编号,次设备编号为0,申请次设备个数为256
        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;
}

gpiolib_dev_init函数内部调用了gpiochip_setup_dev。

3.2.1 gpiochip_setup_dev

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

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);     // 主设备编号为MAJOR(gpio_devt),次设备编号为gdev->id  

        status = cdev_device_add(&gdev->chrdev, &gdev->dev);    // 注册字符设备gdev->chrdev  并注册设备gdev->dev 由于gdev->dev.bus = &gpio_bus_type
所以会在/sys/bus/gpio目录下创建gpiochip%d链接,并指向/sys/devices/gpiochip%d
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; }
3.2.2 cdev_device_add

gpiochip_setup_dev又调用了cdev_device_add函数,该函数实际上调用了cdev_add和device_add:

int cdev_device_add(struct cdev *cdev, struct device *dev)
{
    int rc = 0;
 
    if (dev->devt) {
        cdev_set_parent(cdev, &dev->kobj);
 
        rc = cdev_add(cdev, dev->devt, 1);   
        if (rc)
            return rc;
    }
 
    rc = device_add(dev);
    if (rc)
        cdev_del(cdev);
 
    return rc;
}
3.2.3 gpiochip_sysfs_register

gpiochip_setup_devs函数最后调用了gpiochip_sysfs_register,gpiochip_sysfs_register函数定义在drivers/gpio/gpiolib-sysfs.c:

int gpiochip_sysfs_register(struct gpio_device *gdev)
{
        struct device   *dev;
        struct device   *parent;
        struct gpio_chip *chip = gdev->chip;

        /*
         * Many systems add gpio chips for SOC support very early,
         * before driver model support is available.  In those cases we
         * register later, in gpiolib_sysfs_init() ... here we just
         * verify that _some_ field of gpio_class got initialized.
         */
        if (!gpio_class.p)    // 这是一个class类,name为"gpio"
                return 0;

        /*
         * For sysfs backward compatibility we need to preserve this
         * preferred parenting to the gpio_chip parent field, if set.
         */
        if (chip->parent)
                parent = chip->parent;
        else
                parent = &gdev->dev;

        /* use chip->base for the ID; it's already known to be unique */
        dev = device_create_with_groups(&gpio_class, parent,
                                        MKDEV(0, 0),
                                        chip, gpiochip_groups,
                                        "gpiochip%d", chip->base);
        if (IS_ERR(dev))
                return PTR_ERR(dev);

        mutex_lock(&sysfs_lock);
        gdev->mockdev = dev;
        mutex_unlock(&sysfs_lock);

        return 0;
}

类gpio_class会在gpiolib_sys模块入口函数中注册到内核,但是gpio_class.p并未初始化,所以该函数直接返回了。

gpiolib_dev_init整体看下来,就是遍历gpio_devices链表,为每个gpio_device注册一个字符设备,并且添加到了sysfs。需要注意的是这些gpio_device所注册的字符设备主设备号都是一样的,只是次设备号不一样。我们可以ls -l /sys/dev/char看到看到这些字符设备:

但是在/dev/下并没有设备节点,这主要是因为/dev下的是被节点是由mdev创建的,那mdev的创建原理是什么呢?

mdev扫描/sys/class和/sys/block中所有的类设备目录,如果在目录中含有名为"dev"的文件,且文件中包含的是设备号,则mdev就利用这些信息为这个设备在/dev下创建设备节点用法。很显然我们上面在注册字符设备的时候并没有创建class类,而是将gdev->dev.bus=&gpio_bus_type,为什么不同时设定gdev->dev.class呢?这是因为bus和class不能同时指定。

四、为其它驱动提供的API

gpiolib为其它的驱动提供了一些列的调用接口,这些函数一般定义在drivers/gpio/gpiolib-devres.c文件。

  • gpio_request/devm_gpio_request:向内核申请 gpio,要使用GPIO首先应该向内核进行申请,返回0,代表申请成功,可以进行后续操作;
  • gpio_free: 对应gpio_request,是使用完gpio以后把gpio释放掉;
  • gpio_direction_input :设置GPIO为输入;
  • gpio_direction_output:设置GPIO为输出;
  • gpio_get_value :读取GPIO的值;
  • gpio_set_value:设置GPIO口的值

4.1 gpio_request

其它驱动使用GPIO的时候呢,需要先调用这个接口,向gpiolib进行申请GPIO,定义在drivers/gpio/gpiolib-legacy.c,函数如下:

int gpio_request(unsigned gpio, const char *label)
{
        struct gpio_desc *desc = gpio_to_desc(gpio);

        /* Compatibility: assume unavailable "valid" GPIOs will appear later */
        if (!desc && gpio_is_valid(gpio))
                return -EPROBE_DEFER;

        return gpiod_request(desc, label);
}

传入的参数有一个gpio和一个label,gpio参数是一个数字,代表着板子上GPIO的编号,什么叫编号呢?请注意,这里的编号,有别于datasheet中的编号,要知道这个,我们先好好了解一下gpio_chip->base的成员,打个比如,很多人都了解过三星的2440或者6410的芯片,对于他们的GPIO定义,我们来看看如何定义这个gpio_chip->base成员的,定位到arch/arm/plat-samsung/gpio-samsung.c文件:

struct samsung_gpio_chip s3c24xx_gpios[] = {
#ifdef CONFIG_PLAT_S3C24XX
        {
                .config = &s3c24xx_gpiocfg_banka,
                .chip   = {
                        .base                   = S3C2410_GPA(0),
                        .owner                  = THIS_MODULE,
                        .label                  = "GPIOA",
                        .ngpio                  = 27,
                        .direction_input        = s3c24xx_gpiolib_banka_input,
                        .direction_output       = s3c24xx_gpiolib_banka_output,
                },
        }, {
                .chip   = {
                        .base   = S3C2410_GPB(0),
                        .owner  = THIS_MODULE,
                        .label  = "GPIOB",
                        .ngpio  = 11,
                },
        }, {
                .chip   = {
                        .base   = S3C2410_GPC(0),
                        .owner  = THIS_MODULE,
                        .label  = "GPIOC",
                        .ngpio  = 16,
                },
        }, {
                .chip   = {
                        .base   = S3C2410_GPD(0),
                        .owner  = THIS_MODULE,
                        .label  = "GPIOD",
                        .ngpio  = 16,
                },
        }, {
                .chip   = {
                        .base   = S3C2410_GPE(0),
                        .label  = "GPIOE",
                        .owner  = THIS_MODULE,
                        .ngpio  = 16,
                },
        }, {
                .chip   = {
                        .base   = S3C2410_GPF(0),
                        .owner  = THIS_MODULE,
                        .label  = "GPIOF",
                        .ngpio  = 8,
                        .to_irq = s3c24xx_gpiolib_fbank_to_irq,
                },
        }, {
                .irq_base = IRQ_EINT8,
                .chip   = {
                        .base   = S3C2410_GPG(0),
                        .owner  = THIS_MODULE,
                        .label  = "GPIOG",
                        .ngpio  = 16,
                        .to_irq = samsung_gpiolib_to_irq,
                },
       }, {
                .chip   = {
                        .base   = S3C2410_GPH(0),
                        .owner  = THIS_MODULE,
                        .label  = "GPIOH",
                        .ngpio  = 15,
                },
        },
                /* GPIOS for the S3C2443 and later devices. */
        {
                .base   = S3C2440_GPJCON,
                .chip   = {
                        .base   = S3C2410_GPJ(0),
                        .owner  = THIS_MODULE,
                        .label  = "GPIOJ",
                        .ngpio  = 16,
                },
        }, {
                .base   = S3C2443_GPKCON,
                .chip   = {
                        .base   = S3C2410_GPK(0),
                        .owner  = THIS_MODULE,
                        .label  = "GPIOK",
                        .ngpio  = 16,
                },
        }, {
                .base   = S3C2443_GPLCON,
                .chip   = {
                        .base   = S3C2410_GPL(0),
                        .owner  = THIS_MODULE,
                        .label  = "GPIOL",
                        .ngpio  = 15,
                },
        }, {
                .base   = S3C2443_GPMCON,
                .chip   = {
                        .base   = S3C2410_GPM(0),
                        .owner  = THIS_MODULE,
                        .label  = "GPIOM",
                        .ngpio  = 2,
                },
        },
#endif
};
View Code

这里我们看到的.base 就是 gpio_chip->base ,定义为 S3C2410_GPA(0)、S3C2410_GPB(0)、S3C2410_GPC(0),定义在arch/arm/mach-s3c24xx/include/mach/gpio-samsung.h:

/* some boards require extra gpio capacity to support external
 * devices that need GPIO.
 */

#ifndef GPIO_SAMSUNG_S3C24XX_H
#define GPIO_SAMSUNG_S3C24XX_H

/*
 * GPIO sizes for various SoCs:
 *
 *   2410 2412 2440 2443 2416
 *             2442
 *   ---- ---- ---- ---- ----
 * A  23   22   25   16   27
 * B  11   11   11   11   11
 * C  16   16   16   16   16
 * D  16   16   16   16   16
 * E  16   16   16   16   16
 * F  8    8    8    8    8
 * G  16   16   16   16   8
 * H  11   11   11   15   15
 * J  --   --   13   16   --
 * K  --   --   --   --   16
 * L  --   --   --   15   14
 * M  --   --   --   2    2
 */

/* GPIO bank sizes */

#define S3C2410_GPIO_A_NR       (32)
#define S3C2410_GPIO_B_NR       (32)
#define S3C2410_GPIO_C_NR       (32)
#define S3C2410_GPIO_D_NR       (32)
#define S3C2410_GPIO_E_NR       (32)
#define S3C2410_GPIO_F_NR       (32)
#define S3C2410_GPIO_G_NR       (32)
#define S3C2410_GPIO_H_NR       (32)
#define S3C2410_GPIO_J_NR       (32)    /* technically 16. */
#define S3C2410_GPIO_K_NR       (32)    /* technically 16. */
#define S3C2410_GPIO_L_NR       (32)    /* technically 15. */
#define S3C2410_GPIO_M_NR       (32)    /* technically 2. */

#if CONFIG_S3C_GPIO_SPACE != 0
#error CONFIG_S3C_GPIO_SPACE cannot be nonzero at the moment
#endif

#define S3C2410_GPIO_NEXT(__gpio) \
        ((__gpio##_START) + (__gpio##_NR) + CONFIG_S3C_GPIO_SPACE + 0)

#ifndef __ASSEMBLY__
enum s3c_gpio_number {
        S3C2410_GPIO_A_START = 0,
        S3C2410_GPIO_B_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_A),
        S3C2410_GPIO_C_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_B),
        S3C2410_GPIO_D_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_C),
        S3C2410_GPIO_E_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_D),
        S3C2410_GPIO_F_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_E),
        S3C2410_GPIO_G_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_F),
        S3C2410_GPIO_H_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_G),
        S3C2410_GPIO_J_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_H),
        S3C2410_GPIO_K_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_J),
        S3C2410_GPIO_L_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_K),
        S3C2410_GPIO_M_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_L),
};

#endif /* __ASSEMBLY__ */

/* S3C2410 GPIO number definitions. */

#define S3C2410_GPA(_nr)        (S3C2410_GPIO_A_START + (_nr))
#define S3C2410_GPB(_nr)        (S3C2410_GPIO_B_START + (_nr))
#define S3C2410_GPC(_nr)        (S3C2410_GPIO_C_START + (_nr))
#define S3C2410_GPD(_nr)        (S3C2410_GPIO_D_START + (_nr))
#define S3C2410_GPE(_nr)        (S3C2410_GPIO_E_START + (_nr))
#define S3C2410_GPF(_nr)        (S3C2410_GPIO_F_START + (_nr))
#define S3C2410_GPG(_nr)        (S3C2410_GPIO_G_START + (_nr))
#define S3C2410_GPH(_nr)        (S3C2410_GPIO_H_START + (_nr))
#define S3C2410_GPJ(_nr)        (S3C2410_GPIO_J_START + (_nr))
#define S3C2410_GPK(_nr)        (S3C2410_GPIO_K_START + (_nr))
#define S3C2410_GPL(_nr)        (S3C2410_GPIO_L_START + (_nr))
#define S3C2410_GPM(_nr)        (S3C2410_GPIO_M_START + (_nr))

#ifdef CONFIG_CPU_S3C244X
#define S3C_GPIO_END    (S3C2410_GPJ(0) + 32)
#elif defined(CONFIG_CPU_S3C2443) || defined(CONFIG_CPU_S3C2416)
#define S3C_GPIO_END    (S3C2410_GPM(0) + 32)
#else
#define S3C_GPIO_END    (S3C2410_GPH(0) + 32)
#endif
View Code

有上面的定义可知:

GPIO A base -> S3C2410_GPA(0) = 0
GPIO B base -> S3C2410_GPB(0) = (S3C2410_GPIO_B_START + (0)) =  S3C2410_GPIO_NEXT(S3C2410_GPIO_A) =((S3C2410_GPIO_A_START) + (S3C2410_GPIO_A_NR) + CONFIG_S3C_GPIO_SPACE + 0) = 0 + 32 + 0  = 32
GPIO C base -> S3C2410_GPC(0) = (S3C2410_GPIO_C_START + (0)) =  S3C2410_GPIO_NEXT(S3C2410_GPIO_B) =((S3C2410_GPIO_B_START) + (S3C2410_GPIO_B_NR) + CONFIG_S3C_GPIO_SPACE + 0) = 32 + 32 + 0  = 64
......

所以呢,所有的GPIO都是按照这个从0开始的顺序进行编号的,在这款芯片软件层的对接gpiolib 的实现上,每一个GPIO控制器的base被设计成为了上一个GPIO控制器的起始位置,加上一个 gpio的个数,最终得出,所有的GPIO都是按照顺序,在软件层面上,进行统一编号,这个gpio_chip->base值,就是指的每个GPIO控制器的起始编号。

4.1.1 gpio_to_desc

在gpio_request函数内部先是调用gpio_to_desc函数,这个函数根据gpio编号获取对应的gpio_desc,函数定义在drivers/gpio/gpiolib.c:

/**
 * gpio_to_desc - Convert a GPIO number to its descriptor
 * @gpio: global GPIO number
 *
 * Returns:
 * The GPIO descriptor associated with the given GPIO, or %NULL if no GPIO
 * with the given number exists in the system.
 */
struct gpio_desc *gpio_to_desc(unsigned gpio)
{
        struct gpio_device *gdev;
        unsigned long flags;

        spin_lock_irqsave(&gpio_lock, flags);

        list_for_each_entry(gdev, &gpio_devices, list) {  // 遍历
                if (gdev->base <= gpio &&
                    gdev->base + gdev->ngpio > gpio) {
                        spin_unlock_irqrestore(&gpio_lock, flags);
                        return &gdev->descs[gpio - gdev->base];
                }
        }

        spin_unlock_irqrestore(&gpio_lock, flags);

        if (!gpio_is_valid(gpio))
                WARN(1, "invalid GPIO %d\n", gpio);

        return NULL;
}

可以看到这里其实就是遍历gpio_devices双向链表,然后获取每个gpio_device中gpio的编号范围,如果传入的gpio编号在该编号范围内,就从当前gpio_device获取该gpio对应的gpio_desc。

下图以查找gpio编号为446进行说明:

4.1.2  gpiod_request

在gpio_request函数后面调用gpiod_request函数,函数定义在drivers/gpio/gpiolib.c:

int gpiod_request(struct gpio_desc *desc, const char *label)
{
        int status = -EPROBE_DEFER;
        struct gpio_device *gdev;

        VALIDATE_DESC(desc);
        gdev = desc->gdev;

        if (try_module_get(gdev->owner)) {
                status = gpiod_request_commit(desc, label);
                if (status < 0)
                        module_put(gdev->owner);
                else
                        get_device(&gdev->dev);
        }

        if (status)
                gpiod_dbg(desc, "%s: status %d\n", __func__, status);

        return status;
}

定位到gpiod_request_commit函数:

/* 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.
 */
static int gpiod_request_commit(struct gpio_desc *desc, const char *label)
{
        struct gpio_chip        *chip = desc->gdev->chip;
        int                     status;
        unsigned long           flags;
        unsigned                offset;

        if (label) {
                label = kstrdup_const(label, GFP_KERNEL);
                if (!label)
                        return -ENOMEM;
        }

        spin_lock_irqsave(&gpio_lock, flags);

        /* 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) {  // 如果没有设置FLAG_REQUESTED标志,则设置
                desc_set_label(desc, label ? : "?");    // 设置desc->label
                status = 0;
        } else {
                kfree_const(label);
                status = -EBUSY;
                goto done;
        }

        if (chip->request) {
                /* chip->request may sleep */
                spin_unlock_irqrestore(&gpio_lock, flags);
                offset = gpio_chip_hwgpio(desc);
                if (gpiochip_line_is_valid(chip, offset))
                        status = chip->request(chip, offset);
                else
                        status = -EINVAL;
                spin_lock_irqsave(&gpio_lock, flags);

                if (status < 0) {
                        desc_set_label(desc, NULL);
                        kfree_const(label);
                        clear_bit(FLAG_REQUESTED, &desc->flags);
                        goto done;
                }
        }
        if (chip->get_direction) {     // 指定获取方向
                /* chip->get_direction may sleep */
                spin_unlock_irqrestore(&gpio_lock, flags);
                gpiod_get_direction(desc);
                spin_lock_irqsave(&gpio_lock, flags);
        }
done:
        spin_unlock_irqrestore(&gpio_lock, flags);
        return status;
}

针对这个gpio,如果没有设置FLAG_REQUESTED标志的话,那么按照传入的label设置它的标签,然后再获得它的方向;

如果已经被设置了FLAG_REQUESTED标志的话,返回 -EBUSY,请求GPIO失败。

4.2 gpio_direction_input

gpio_direction_input用于配置GPIO为输入模式,这样我们就可用来读取GPIO的输入值了,函数定义在include/asm-generic/gpio.h:

static inline int gpio_direction_input(unsigned gpio)
{
        return gpiod_direction_input(gpio_to_desc(gpio));  // GPIO编号转换为gpio_desc,调用gpiod_drection_input
}

gpiod_direction_input定义在drivers/gpio/gpiolib.c:

/**
 * gpiod_direction_input - set the GPIO direction to input
 * @desc:       GPIO to set to input
 *
 * Set the direction of the passed GPIO to input, such as gpiod_get_value() can
 * be called safely on it.
 *
 * Return 0 in case of success, else an error code.
 */
int gpiod_direction_input(struct gpio_desc *desc)
{
        struct gpio_chip        *chip;
        int                     status = 0;

        VALIDATE_DESC(desc);
        chip = desc->gdev->chip;       // 获取GPIO控制器的gpio_chip结构

        /*
         * It is legal to have no .get() and .direction_input() specified if
         * the chip is output-only, but you can't specify .direction_input()
         * and not support the .get() operation, that doesn't make sense.
         */
        if (!chip->get && chip->direction_input) {  
                gpiod_warn(desc,
                           "%s: missing get() but have direction_input()\n",
                           __func__);
                return -EIO;
        }

        /*
         * If we have a .direction_input() callback, things are simple,
         * just call it. Else we are some input-only chip so try to check the
         * direction (if .get_direction() is supported) else we silently
         * assume we are in input mode after this.
         */
        if (chip->direction_input) {     // 实际上就是调用注册gpio_chip时提供的direction_input方法
                status = chip->direction_input(chip, gpio_chip_hwgpio(desc));
        } else if (chip->get_direction &&
                  (chip->get_direction(chip, gpio_chip_hwgpio(desc)) != 1)) {
                gpiod_warn(desc,
                           "%s: missing direction_input() operation and line is output\n",
                           __func__);
                return -EIO;
        }
        if (status == 0)
                clear_bit(FLAG_IS_OUT, &desc->flags);

        if (test_bit(FLAG_PULL_UP, &desc->flags))     //查看是否设置了上拉标志
                gpio_set_config(chip, gpio_chip_hwgpio(desc),
                                PIN_CONFIG_BIAS_PULL_UP);
        else if (test_bit(FLAG_PULL_DOWN, &desc->flags))  // 查看是否设置了下拉标志
                gpio_set_config(chip, gpio_chip_hwgpio(desc),
                                PIN_CONFIG_BIAS_PULL_DOWN);

        trace_gpio_direction(desc_to_gpio(desc), 1, status);

        return status;
}

上面分析下来,就是根据传入的gpio的编号,也是转换成为了desc 后,获得了GPIO控制器的的gpio_chip结构,然后调用了挂接到chip上去direction_input而已。

4.3 gpio_direction_output

gpio_direction_output用于配置GPIO为输出模式,这样我们就可以通过第二个值设置GPIO的输出值了,函数定义在include/asm-generic/gpio.h:

static inline int gpio_direction_output(unsigned gpio,int value)
{
        return gpiod_direction_output(gpio_to_desc(gpio),value);  // GPIO编号转换为gpio_desc,调用gpiod_direction_output
}

gpiod_direction_output定义在drivers/gpio/gpiolib.c:

/**
 * gpiod_direction_output - set the GPIO direction to output
 * @desc:       GPIO to set to output
 * @value:      initial output value of the GPIO
 *
 * Set the direction of the passed GPIO to output, such as gpiod_set_value() can
 * be called safely on it. The initial value of the output must be specified
 * as the logical value of the GPIO, i.e. taking its ACTIVE_LOW status into
 * account.
 *
 * Return 0 in case of success, else an error code.
 */
int gpiod_direction_output(struct gpio_desc *desc, int value)
{
        struct gpio_chip *gc;
        int ret;

        VALIDATE_DESC(desc);
        if (test_bit(FLAG_ACTIVE_LOW, &desc->flags))
                value = !value;
        else
                value = !!value;

        /* GPIOs used for enabled IRQs shall not be set as output */
        if (test_bit(FLAG_USED_AS_IRQ, &desc->flags) &&
            test_bit(FLAG_IRQ_IS_ENABLED, &desc->flags)) {
                gpiod_err(desc,
                          "%s: tried to set a GPIO tied to an IRQ as output\n",
                          __func__);
                return -EIO;
        }

        gc = desc->gdev->chip;       // 获取GPIO控制器的gpio_chip结构
        if (test_bit(FLAG_OPEN_DRAIN, &desc->flags)) {
                /* First see if we can enable open drain in hardware */
                ret = gpio_set_config(gc, gpio_chip_hwgpio(desc),
                                      PIN_CONFIG_DRIVE_OPEN_DRAIN);
                if (!ret)
                        goto set_output_value;
                /* Emulate open drain by not actively driving the line high */
                if (value)
                        return gpiod_direction_input(desc);
        }
        else if (test_bit(FLAG_OPEN_SOURCE, &desc->flags)) {
                ret = gpio_set_config(gc, gpio_chip_hwgpio(desc),
                                      PIN_CONFIG_DRIVE_OPEN_SOURCE);
                if (!ret)
                        goto set_output_value;
                /* Emulate open source by not actively driving the line low */
                if (!value)
                        return gpiod_direction_input(desc);
        } else {
                gpio_set_config(gc, gpio_chip_hwgpio(desc),
                                PIN_CONFIG_DRIVE_PUSH_PULL);
        }

set_output_value:
        return gpiod_direction_output_raw_commit(desc, value);
}
static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value)
{
        struct gpio_chip *gc = desc->gdev->chip;
        int val = !!value;
        int ret = 0;

        /*
         * It's OK not to specify .direction_output() if the gpiochip is
         * output-only, but if there is then not even a .set() operation it
         * is pretty tricky to drive the output line.
         */
        if (!gc->set && !gc->direction_output) {
                gpiod_warn(desc,
                           "%s: missing set() and direction_output() operations\n",
                           __func__);
                return -EIO;
        }

        if (gc->direction_output) {
                ret = gc->direction_output(gc, gpio_chip_hwgpio(desc), val);  // 实际上就是调用注册gpio_chip时提供的direction_output方法
        } else {
                /* Check that we are in output mode if we can */
                if (gc->get_direction &&
                    gc->get_direction(gc, gpio_chip_hwgpio(desc))) {
                        gpiod_warn(desc,
                                "%s: missing direction_output() operation\n",
                                __func__);
                        return -EIO;
                }
                /*
                 * If we can't actively set the direction, we are some
                 * output-only chip, so just drive the output as desired.
                 */
                gc->set(gc, gpio_chip_hwgpio(desc), val);
        }

        if (!ret)
                set_bit(FLAG_IS_OUT, &desc->flags);
        trace_gpio_value(desc_to_gpio(desc), 0, val);
        trace_gpio_direction(desc_to_gpio(desc), 0, ret);
        return ret;
}

上面分析下来,就是根据传入的gpio的编号,也是转换成为了desc后,获得了GPIO控制器的gpio_chip结构,然后调用了挂接到chop上的direction_output而已。

4.4 gpio_set_value

gpio_set_value用于设置GPIO的输出值,函数定义在定义在include/linux/gpio.h:

static inline void gpio_set_value(unsigned gpio, int value)
{
        __gpio_set_value(gpio, value);
}

__gpio_set_value定义在include/asm-generic/gpio.h:

static inline void __gpio_set_value(unsigned gpio, int value)
{
        return gpiod_set_raw_value(gpio_to_desc(gpio), value);
}

而gpiod_set_raw_value定义在drivers/gpio/gpiolib.c:

/**
 * gpiod_set_raw_value() - assign a gpio's raw value
 * @desc: gpio whose value will be assigned
 * @value: value to assign
 *
 * Set the raw value of the GPIO, i.e. the value of its physical line without
 * regard for its ACTIVE_LOW status.
 *
 * This function should be called from contexts where we cannot sleep, and will
 * complain if the GPIO chip functions potentially sleep.
 */
void gpiod_set_raw_value(struct gpio_desc *desc, int value)
{
        VALIDATE_DESC_VOID(desc);
        /* Should be using gpiod_set_raw_value_cansleep() */
        WARN_ON(desc->gdev->chip->can_sleep);
        gpiod_set_raw_value_commit(desc, value);
}
static void gpiod_set_raw_value_commit(struct gpio_desc *desc, bool value)
{
        struct gpio_chip        *chip;

        chip = desc->gdev->chip;
        trace_gpio_value(desc_to_gpio(desc), 0, value);
        chip->set(chip, gpio_chip_hwgpio(desc), value);
}

上面分析下来,就是根据传入的gpio的编号,也是转换成为了desc后,获得了GPIO控制器的gpio_chip结构,然后调用了挂接到chip上的set而已。

4.5 gpio_get_value

gpio_get_value用于获取GPIO的输入值,函数定义在定义在include/linux/gpio.h:

static inline int gpio_get_value(unsigned int gpio)
{
        return __gpio_get_value(gpio);
}

__gpio_get_value定义在include/asm-generic/gpio.h:

/* A platform's <asm/gpio.h> code may want to inline the I/O calls when
 * the GPIO is constant and refers to some always-present controller,
 * giving direct access to chip registers and tight bitbanging loops.
 */
static inline int __gpio_get_value(unsigned gpio)
{
        return gpiod_get_raw_value(gpio_to_desc(gpio));
}

而gpiod_get_raw_value定义在drivers/gpio/gpiolib.c:

/**
 * gpiod_get_raw_value() - return a gpio's raw value
 * @desc: gpio whose value will be returned
 *
 * Return the GPIO's raw value, i.e. the value of the physical line disregarding
 * its ACTIVE_LOW status, or negative errno on failure.
 *
 * This function should be called from contexts where we cannot sleep, and will
 * complain if the GPIO chip functions potentially sleep.
 */
int gpiod_get_raw_value(const struct gpio_desc *desc)
{
        VALIDATE_DESC(desc);
        /* Should be using gpiod_get_raw_value_cansleep() */
        WARN_ON(desc->gdev->chip->can_sleep);
        return gpiod_get_raw_value_commit(desc);
}

/* I/O calls are only valid after configuration completed; the relevant
 * "is this a valid GPIO" error checks should already have been done.
 *
 * "Get" operations are often inlinable as reading a pin value register,
 * and masking the relevant bit in that register.
 *
 * When "set" operations are inlinable, they involve writing that mask to
 * one register to set a low value, or a different register to set it high.
 * Otherwise locking is needed, so there may be little value to inlining.
 *
 *------------------------------------------------------------------------
 *
 * IMPORTANT!!!  The hot paths -- get/set value -- assume that callers
 * have requested the GPIO.  That can include implicit requesting by
 * a direction setting call.  Marking a gpio as requested locks its chip
 * in memory, guaranteeing that these table lookups need no more locking
 * and that gpiochip_remove() will fail.
 *
 * REVISIT when debugging, consider adding some instrumentation to ensure
 * that the GPIO was actually requested.
 */

static int gpiod_get_raw_value_commit(const struct gpio_desc *desc)
{
        struct gpio_chip        *chip;
        int offset;
        int value;

        chip = desc->gdev->chip;
        offset = gpio_chip_hwgpio(desc);
        value = chip->get ? chip->get(chip, offset) : -EIO;
        value = value < 0 ? value : !!value;
        trace_gpio_value(desc_to_gpio(desc), 1, value);
        return value;
}

上面分析下来,就是根据传入的gpio的编号,也是转换成为了desc后,获得了GPIO控制器的gpio_chip结构,然后调用了挂接到chip上的get而已。

4.6 gpio_to_irq

GPIO编号是无符号整数;IRQ编号也是。这些构成了两个逻辑上不同的命名空间(GPIO 0 不一定使用 IRQ 0)。你可以通过以下函数在它们之间实现映射:

        /* 映射 GPIO 编号到 IRQ 编号 */
        int gpio_to_irq(unsigned gpio);

        /* 映射 IRQ 编号到 GPIO 编号 (尽量避免使用) */
        int irq_to_gpio(unsigned irq);

它们的返回值为对应命名空间的相关编号,或是负的错误代码(如果无法映射)。

(例如,某些GPIO无法做为IRQ使用)以下的编号错误是未经检测的:使用一个未通过gpio_direction_input配置为输入的GPIO 编号,或者使用一个并非来源于gpio_to_irq的 IRQ编号。gpio_to_irq返回的非错误值可以传递给 request_irq()或者 free_irq()。它们通常通过板级特定的初始化代码存放到平台设备的IRQ资源中。注意:IRQ触发选项 IRQ接口的一部分,如 IRQF_TRIGGER_FALLING,系统唤醒能力也是如此。

irq_to_gpio返回的非错误值大多数通常可以被gpio_get_value所使用,比如在IRQ是沿触发时初始化或更新驱动状态。注意某些平台不支持反映射,所以你应该尽量避免使用它。

4.7 desc_to_gpio

desc_to_gpio用于将gpio_desc转换为GPIO编号:

/**
 * desc_to_gpio - convert a GPIO descriptor to the integer namespace
 * @desc: GPIO descriptor
 *
 * This should disappear in the future but is needed since we still
 * use GPIO numbers for error messages and sysfs nodes.
 *
 * Returns:
 * The global GPIO number for the GPIO specified by its descriptor.
 */
int desc_to_gpio(const struct gpio_desc *desc)
{
        return desc->gdev->base + (desc - &desc->gdev->descs[0]);
}

4.8 of_get_named_gpio

此函数获取GPIO 编号,因为Linux内核中关于GPIO的API函数都要使用GPIO编号,此函数会将设备数中类似key_1 = <&gpg 0 GPIO_ACTIVE_HIGH>;的属性信息转换为对应的GPIO编号,函数定义在include/linux/of_gpio.h文件:

/**
 * of_get_named_gpio() - Get a GPIO number to use with GPIO API
 * @np:         device node to get GPIO from
 * @propname:   Name of property containing gpio specifier(s)
 * @index:      index of the GPIO
 *
 * Returns GPIO number to use with Linux generic GPIO API, or one of the errno
 * value on the error condition.
 */
static inline int of_get_named_gpio(struct device_node *np,
                                   const char *propname, int index)
{
        return of_get_named_gpio_flags(np, propname, index, NULL);
}

函数接收3个参数:

  • np:需要获取GPIO编号的设备节点;
  • propname:包含GPIO的属性名称;
  • index:GPIO索引;

成功返回 GPIO编号,  失败返回负数。比如:

ret = of_get_named_gpio(pdev->dev.of_node, "key_1", 0);
if (ret< 0)
{
    printk("of get named gpio is error!\n");
    return ret;
}

4.9 of_gpio_named_count

of_gpio_named_count函数用于获取设备GPIO端口的数量,函数定义在include/linux/of_gpio.h文件:

/**
 * of_gpio_named_count() - Count GPIOs for a device
 * @np:         device node to count GPIOs for
 * @propname:   property name containing gpio specifier(s)
 *
 * The function returns the count of GPIOs specified for a node.
 * Note that the empty GPIO specifiers count too. Returns either
 *   Number of gpios defined in property,
 *   -EINVAL for an incorrectly formed gpios property, or
 *   -ENOENT for a missing gpios property
 *
 * Example:
 * gpios = <0
 *          &gpio1 1 2
 *          0
 *          &gpio2 3 4>;
 *
 * The above example defines four GPIOs, two of which are not specified.
 * This function will return '4'
 */
static inline int of_gpio_named_count(struct device_node *np, const char* propname)
{
        return of_count_phandle_with_args(np, propname, "#gpio-cells");
}

五、代码下载

在SPI OLED中我们使用了GPIO资源,具体参考SPI OLED驱动代码,Young / s3c2440_project[drivers]

参考文章

[1] Linux GPIO 驱动 (gpiolib)

[2]linux GPIO子系统

[3]Documentation/translations/zh_CN/gpio.txt(官方文档)

[4]GPIO Subsystem -1-

[5]GPIO Subsystem -2-

[6]GPIO Subsystem -3- (Device Tree)

[7]GPIO Subsystem -4- (new Interface)

posted @ 2023-03-01 23:21  大奥特曼打小怪兽  阅读(605)  评论(0编辑  收藏  举报
如果有任何技术小问题,欢迎大家交流沟通,共同进步