7.7.5. 设计说明

7.7.5.1. 源码说明

PINCTRL模块的底层驱动位于:linux-5.10/drivers/pinctrl/zx/

驱动文件如下:

文件

说明

pinctrl-aic.h

pinctrl公用头文件,一些结构体和宏定义

pinctrl-aic.c

pinctrl驱动的核心文件,实现了gpio/pinctrl/irq的驱动

pinctrl-aic-v1.c

pinctrl v1.0的pin脚功能列表,并将该功能列表注册到内核中

7.7.5.2. 模块架构

在linux内核中,pin脚的配置涉及到pinctrl,gpio和irqchip三个子系统。在内核子系统的划分中,pinctrl子系统用来配置pin脚的电气属性及功能复用;gpio子系统主要是GPIO的输入输出设置;同时,GPIO口使用中断时,内核将GPIO controller视为一个级联到GIC的中断控制器,又涉及到irqchip子系统的内容。

在把pin脚复用为GPIO口时,pinctrl子系统和gpio子系统具有一些功能相同的接口,所以,一些SOC厂商将gpio的驱动和pinctrl的驱动合并在了一起,PINCTRL模块的驱动实现也是采用的这种方式。所以,PINCTRL模块的驱动主要包括了三部分内容:

  • 电气属性和功能复用配置

  • gpio输入输出设置

  • gpio controller作为irqchip的驱动实现

7.7.5.2.1. pinctrl驱动

pinctrl子系统对pin controller进行了软件抽象,并由pin controller所实现的操作函数集来管理各个pin脚的属性和复用。子系统中主要的数据结构关系如下图:

../../../_images/pinctrl_data_struct.png

其相应的软件基本框架为:

../../../_images/pinctrl_subsystem.png

pinctrl子系统的底层驱动实现,主要分为三部分:

  • struct pinconf_ops函数集实现,主要用来设置pin脚的电气参数,如上下拉,驱动能力等。

  • struct pinctrl_ops函数集实现,主要用来实现对DTS的解析,获取实现某一功能所需的pin脚信息。

  • struct pinmux_ops函数集实现,主要用来实现功能复用,依据获取到的pin脚信息,实现底层的寄存器配置等。

7.7.5.2.2. gpio驱动

常见的gpio有外挂的gpio芯片以及SOC自身的gpio控制器,linux内核将这两种gpio统一看作是gpio chip进行处理。gpio子系统整体框架如下图:

../../../_images/gpio_subsystem.png

gpio子系统的核心就是gpiolib。它的主要作用是:

  1. 向下为gpio chip driver提供注册struct gpio_chip的接口:gpiochip_xxx()

  2. 向上为gpio consumer提供引用gpio的接口:gpiod_xxx()

  3. 实现字符设备的功能

  4. 注册sysfs

作为SOC厂商,需要实现的驱动就是gpio chip driver部分,所以,这部分的主要工作就是实现struct gpio_chip结构体中的函数集,并注册gpio controller。

7.7.5.2.3. irqchip驱动

当gpio口接收中断时,linux内核是将gpio作为一个级联到GIC上的二级中断控制器处理的。一个典型的拓扑结构如下图:

../../../_images/irq_arch.png

linux内核将此时的GPIO控制器看作是一个中断控制器,用struct irq_chip进行软件抽象。所以需要实现相应的gpio中断控制器的驱动。按照irqchip子系统的框架,每一个中断线对应一个struct irq_desc结构体,该结构体包含一个handle_irq,是该中断线的high-level中断处理函数。该函数的主要作用是:打开或关闭相应中断号的中断,通知CPU中断处理完成;调用底层的中断处理函数。子系统中定义了几个不同的high-level函数,可以依据不同的中断类型和不同的中断控制器,选择不同的high-level函数。

与struct gpio_chip的驱动实现有些类似,irqchip的驱动也主要是实现struct irq_chip中的函数集。由于gpio中断控制器是一个二级中断控制器,所以驱动中需要做的工作有:

  • 实现gpio中断控制器的驱动,主要工作是实现struct irq_chip中的函数集

  • 实现每个gpio controller的中断线所对应的中断描述符的hand_irq,用于在gpio触发中断时查找触发中断的gpio line

7.7.5.3. 关键流程设计

7.7.5.3.1. pin脚功能定义

在pinctrl子系统中,有function和group的概念。function是指某个具体的功能,如uart0、spi1、twi2等。soc的几个pin脚可以构成一个group,形成特定的功能,如PA0和PA1可以组成uart0。一个function往往包含一个或多个group,例如,uart0这个function可以由PA0和PA1组成,也可以由PA2和PA3组成。pinctrl子系统就是通过function和group来确定最终需要设置的pin脚。

PINCTRL模块的驱动定义了一个结构体数组,存储每个pin脚的所有可复用功能。如下所示,PA0是该pin脚的名称,下面依次是PA0可实现的功能复用。AIC_FUNCTION(index, func_name)用来定义pin脚功能复用时所对应的索引值。即PA0作为GPIOA0时,是使用的function 1;作为uart0时,是使用的function 5。驱动中并不会区分该pin脚是uart0的RX还是TX这些细节。

static struct aic_desc_pin aic_pins_v1[] = {
    AIC_PIN(
        PINCTRL_PIN(0, "PA0"),
        AIC_FUNCTION(1, "GPIOA0"),
        AIC_FUNCTION(2, "GPAI0"),
        AIC_FUNCTION(3, "jtag"),
        AIC_FUNCTION(5, "uart0")
    ),
    AIC_PIN(
        PINCTRL_PIN(1, "PA1"),
        AIC_FUNCTION(1, "GPIOA1"),
        AIC_FUNCTION(2, "GPAI1"),
        AIC_FUNCTION(3, "jtag"),
        AIC_FUNCTION(5, "uart0")
    ),
    /* 此处省略其它pin脚配置 */
}

在pinctrl子系统中,一个group一般会包含多个pin脚。而在PINCTRL模块实现的驱动中,是将每个pin脚都看作一个group,这样做的优点是:

  • 不需要再单独定义每个group的pin脚组成情况,

  • 不需要再定义function与group的对应关系

  • 驱动源码简单明了,由上面的数组可以快速直观的了解到每个pin脚可复用的功能

备注

按照pinctrl子系统对function和group的定义,gpio模块的uart0包含2个group,每个group包含2个pin脚,通过uart0可以找到这4个pin脚。而按照gpio模块实现的驱动,uart0包含4个group,每个group包含1个pin脚,最终通过uart0也可以找到4个pin脚。

7.7.5.3.2. 初始化流程

  1. 释放reset和clock

  2. 调用aic_pctrl_build_state,构建function与group的关系

  3. 初始化struct aic_pinctrl结构体变量

  4. 注册pin controller设备

  5. 调用aic_gpiolib_register_bank,注册各个gpio bank

  6. 初始化完成

7.7.5.3.3. 设备pinmux配置流程

在各个外设的驱动中,并没有调用与pin脚复用相关的接口,那么各个外设的pin脚复用功能是什么时候生效的呢?pin脚复用功能是如何进行初始化的?了解这个过程,有助于加深对pinctrl子系统的了解。外设的pin脚复用初始化流程如下:

../../../_images/pinmux_initialize.png

7.7.5.3.4. 中断处理流程

../../../_images/irq_flow.png

7.7.5.4. 数据结构设计

7.7.5.4.1. aic_desc_function

struct aic_desc_function {
    const unsigned char num;  //pin脚功能所对应的,最终写入寄存器的索引值
    const char *name;         //pin脚对应的功能的名字
};

7.7.5.4.2. aic_desc_pin

struct aic_desc_pin {
    struct pinctrl_pin_desc pin;                //pin脚的描述符
    const struct aic_desc_function *functions;  //该pin脚所能设置的功能列表
};

7.7.5.4.3. aic_pinctrl_group

struct aic_pinctrl_group {
    const char *name;       //group名字
    u32 config;             //pin脚的配置参数
    u32 pin;                //pin脚索引值
};

7.7.5.4.4. aic_pinctrl_function

struct aic_pinctrl_function {
    const char *name;       //function名字
    const char **groups;    //function所对应的所有group列表
    u32 ngroups;            //function所对应的group个数
};

7.7.5.4.5. aic_gpio_bank

struct aic_gpio_bank {
    u32 bank_nr;        //gpio port索引值,GPIOA为0,GPIOB为1…
    int irq;
    u32 saved_mask;
    spinlock_t lock;
    struct gpio_chip gpio_chip;         //gpio port的gpio_chip结构
    struct pinctrl_gpio_range range;    //该gpio port的range范围
    struct irq_domain *domain;          //转换hwirq到irq的结构体指针
    struct aic_gpio_regs regs;          //GPIO控制器的寄存器
    struct aic_pinctrl *pctl;
};

7.7.5.4.6. aic_pinctrl

struct aic_pinctrl {
    void __iomem *base;
    struct device *dev;
    struct pinctrl_dev *pctl_dev;
    struct pinctrl_desc pctl_desc;
    struct aic_pinctrl_group *groups;       //pin controller包含的group列表
    u32 ngroups;                            //pin controller包含的group个数
    const char **grp_names;                 //pin controller包含的group名字
    struct aic_pinctrl_function     *functions; //pin controller包含的functions列表
    u32 nfunctions;                         //pin controller包含的functions个数
    struct aic_gpio_bank *banks;            //pin controller包含的gpio bank列表
    u32 nbanks;                             //pin controller包含的gpio bank个数
    struct aic_desc_pin *pins;              //pin controller包含的pin列表
    u32 npins;                              //pin controller包含的pin个数
    struct reset_control *reset;
    struct clk *clk;
};

7.7.5.5. 接口设计

7.7.5.5.1. aic_pconf_group_get

函数原型

static int aic_pconf_group_get(struct pinctrl_dev *pctldev, unsigned group, unsigned long *config)

功能说明

从pin controller的groups数组中,获取索引值为group的配置参数

参数定义

pctldev:pin controller设备
group:索引值
config:指向获取到的pctldev->groups[group]配置参数

返回值

0

注意事项

7.7.5.5.2. aic_pctrl_dt_node_to_map

函数原型

static int aic_pctrl_dt_node_to_map(struct pinctrl_dev *pctldev, struct device_node *node,
struct pinctrl_map **map, unsigned *num_maps)

功能说明

解析pinctrl结点的子结点,并将各个子结点的参数转换为struct pinctrl_map的结构存储

参数定义

pctldev:pin controller设备
node:pin controller的DTS结点
map:指向动态申请的类型为struct pinctrl_map *的数组,存储各个子结点配置参数
num_maps:struct pinctrl_map *数组中存储的个数

返回值

0:执行成功
<0:执行错误

注意事项

7.7.5.5.3. aic_pctrl_get_groups_count

函数原型

static int aic_pctrl_get_groups_count(struct pinctrl_dev *pctldev)

功能说明

获取pin controller中的group个数

参数定义

pctldev:pin controller设备指针

返回值

返回group个数

注意事项

7.7.5.5.4. aic_pctrl_get_group_name

函数原型

static const char *aic_pctrl_get_group_name(struct pinctrl_dev *pctldev, unsigned group)

功能说明

根据索引值group,获取相应group的名字

参数定义

pctldev:pin controller设备指针
group:指向pctldev->groups数组元素的索引值

返回值

返回相应的group的名字

注意事项

7.7.5.5.5. aic_pctrl_get_group_pins

函数原型

static int aic_pctrl_get_group_pins(struct pinctrl_dev *pctldev,unsigned group,
const unsigned **pins, unsigned *num_pins)

功能说明

获取group所对应的pin脚信息

参数定义

pctldev:pin controller设备指针
group:指向pctldev->groups数组元素的索引值
pins:指向用来存储pin脚信息的数组
num_pins:该group所包含的pin脚个数

返回值

0

注意事项

7.7.5.5.6. aic_pmx_get_funcs_cnt

函数原型

static int aic_pmx_get_funcs_cnt(struct pinctrl_dev *pctldev)

功能说明

获取pin controller中的function个数

参数定义

pctldev:pin controller设备指针

返回值

返回function个数

注意事项

7.7.5.5.7. aic_pmx_get_func_name

函数原型

static const char *aic_pmx_get_func_name(struct pinctrl_dev *pctldev, unsigned function)

功能说明

在pin controller的功能数组中,获取索引值为function的功能的名字

参数定义

pctldev:pin controller设备指针
function:功能的索引值

返回值

返回function所对应的功能的名字

注意事项

7.7.5.5.8. aic_pmx_get_func_groups

函数原型

static int aic_pmx_get_func_groups(struct pinctrl_dev *pctldev, unsigned function,
const char * const **groups, unsigned * const num_groups)

功能说明

在pin controller的所有功能的数组中,获取索引值为function的功能的所有group

参数定义

pctldev:pin controller设备指针
function:功能的索引值
groups:指向用来存储该功能所对应的所有group的数组
num_groups:该功能所对应的group的个数

返回值

0

注意事项

7.7.5.5.9. aic_pmx_set_mux

函数原型

static int aic_pmx_set_mux(struct pinctrl_dev *pctldev, unsigned function, unsigned group)

功能说明

根据索引值function和group,设置功能复用

参数值

pctldev:pin controller设备指针
function:功能的索引值
group:group索引值

返回值

0:执行成功 <0:执行错误

注意事项

7.7.5.5.10. aic_pmx_gpio_set_direction

函数原型

static int aic_pmx_gpio_set_direction(struct pinctrl_dev *pctldev,
struct pinctrl_gpio_range *range, unsigned gpio, bool input)

功能说明

设置gpio口的输入输出方向

参数定义

pctldev:pin controller设备指针
range:每个gpio port的范围
gpio:所设置的pin脚在range内的偏移
input:指示是否设置为输入

返回值

0

注意事项

7.7.5.5.11. aic_gpio_get

函数原型

static int aic_gpio_get(struct gpio_chip *chip, unsigned offset)

功能说明

获取gpio口的值

参数定义

chip:指向每个gpio port的指针
offset

返回值

返回该pin脚的高低电平值

注意事项

7.7.5.5.12. aic_gpio_set

函数原型

static void aic_gpio_set(struct gpio_chip *chip, unsigned offset, int value)

功能说明

将偏移为offset的gpio口的值设置为value

参数定义

chip:指向每个gpio port的指针
offset

返回值

注意事项

7.7.5.5.13. aic_gpio_direction_input

函数原型

static int aic_gpio_direction_input(struct gpio_chip *chip, unsigned offset)

功能说明

设置gpio口为输入

参数定义

chip:指向每个gpio port的指针
offset

返回值

0:执行成功 <0:执行错误

注意事项

7.7.5.5.14. aic_gpio_direction_output

函数原型

static int aic_gpio_direction_output(struct gpio_chip *chip, unsigned offset, int value)

功能说明

设置gpio口为输出,且输出值为value

参数定义

chip:指向每个gpio port的指针
offset
value:设置的输出值

返回值

0:执行成功 <0:执行错误

注意事项

7.7.5.5.15. aic_gpio_irq_mask

函数原型

static void aic_gpio_irq_mask(struct irq_data *d)

功能说明

屏蔽gpio口的中断

参数定义

d:指向struct irq_data的指针

返回值

注意事项

7.7.5.5.16. aic_gpio_irq_unmask

函数原型

static void aic_gpio_irq_unmask(struct irq_data *d)

功能说明

打开gpio口的中断

参数定义

d:指向struct irq_data的指针

返回值

注意事项

7.7.5.5.17. aic_gpiolib_register_bank

函数原型

static int aic_gpiolib_register_bank(struct aic_pinctrl *pctl, struct device_node *np)

功能说明

打开gpio口的中断

参数定义

pctl:pin controller设备指针
np:pin controller的DTS结点

返回值

0:执行成功
<0:执行错误

注意事项