pinctrl和gpio子系统

pinctrl子系统

1. 关于pinctrl

1.1 作用
  1. pin的枚举和命名
  2. pin的复用
  3. pin的配置(上下拉、驱动能力、是否开漏等)
1.2 pinctrl的核心数据结构
  • 使用struct pinctrl_desc描述一个pin ctroller
1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct pinctrl_desc {
const char *name;
const struct pinctrl_pin_desc *pins; //array 描述所有的pins 每一个元素描述一个单独的pin
unsigned int npins; //size of array pins和npins构成一个索引
const struct pinctrl_ops *pctlops; //引脚控制操作func 用来获取某组引脚 解析设备树节点
const struct pinmux_ops *pmxops; //引脚复用操作func
const struct pinconf_ops *confops; //引脚配置操作func
struct module *owner;
#ifdef CONFIG_GENERIC_PINCONF
unsigned int num_custom_params;
const struct pinconf_generic_params *custom_params;
const struct pin_config_item *custom_conf_items;
#endif
};
  • 使用struct pinctrl_pin_desc描述单个引脚

    在pinctrl_pin_desc中以数组形式存在,与npins成员构成索引,方便驱动和具体的pin对应

1
2
3
4
5
struct pinctrl_pin_desc {
unsigned number; //引脚序号
const char *name; //引脚名
void *drv_data;
};
  • 使用group_desc描述一组特定引脚,如IIC、UART等

    1
    2
    3
    4
    5
    6
    struct group_desc{
    const char *name;
    int *pins; //应该group中pins对应的npins成员的下标数组
    int num_pins; //group中pin的个数
    void *data;
    };
  • pinctrl_ops函数族 主要从设备树中获取group节点信息并map

1
2
3
4
5
6
7
8
struct pinctrl_ops {   
int (*get_groups_count) (struct pinctrl_dev *pctldev); //获取整个pin_controller中的group个数并建立索引,后续根根索引操作group
const char *(*get_group_name) (struct pinctrl_dev *pctldev, unsigned selector ); //获取组名,selector就是需要获得的group的索引号
int (*get_group_pins) (struct pinctrl_dev *pctldev, unsigned selector, const unsigned **pins, unsigned *num_pins); //获取某组的引脚 获取到的信息保存到pins和num_pins指针中
void (*pin_dbg_show) (); //这玩意儿感觉没啥用 debug相关?
int (*dt_node_to_map) (struct pinctrl_dev *pctldev, struct device_node *np_config, struct pinctrl_map **map, unsigned *num_maps); //解析设备树节点,转换成pinctrl_map,这是一个重点函数
void (*dt_free_map) (); //释放map
};
  • pinmux_ops函数族 引脚复用相关
1
2
3
4
5
6
7
8
9
10
11
12
 struct pinmux_ops {
int (*request) (struct pinctrl_dev *pctldev, unsigned offset); //如果一个pin已作他用 request失败
int (*free) (struct pinctrl_dev *pctldev, unsigned offset);
int (*get_functions_count) (struct pinctrl_dev *pctldev); //获取function数量
const char *(*get_function_name) (struct pinctrl_dev *pctldev, unsigned selector); //获取function名
int (*get_function_groups) (struct pinctrl_dev *pctldev, unsigned selector, const char * const **groups, unsigned *num_groups); //获取function下的group
int (*set_mux) (struct pinctrl_dev *pctldev, unsigned func_selector, unsigned group_selector); //设置复用 将指定的group设置为指定的function
int (*gpio_request_enable) ();
void (*gpio_disable_free) ();
int (*gpio_set_direction) ();
bool strict; //设为true时说明不允许pin作为gpio和其他功能同时使用
};
  • pinconf_ops函数族 引脚配置相关
1
2
3
4
5
6
7
8
9
10
11
12
13
struct pinconf_ops {
#ifdef CONFIG_GENERIC_PINCONF
bool is_generic;
#endif
int (*pin_config_get) (struct pinctrl_dev *pctldev, unsigned pin, unsigned long *config); //获取单个引脚配置 保存在config指针中
int (*pin_config_set) (struct pinctrl_dev *pctldev, unsigned pin, unsigned long *configs, unsigned num_configs); //使用config指针配置单个引脚
int (*pin_config_group_get) (); //获取某组引脚配置
int (*pin_config_group_set) (); //配置某组引脚
int (*pin_config_dbg_parse_modify) (); //用以debugfs修改pin配置信息
void (*pin_config_dbg_show) (); //用以debugfs提供pin配置信息
void (*pin_config_group_dbg_show) (); //用以debugfs提供group配置信息
void (*pin_config_config_dbg_show) (s); //用以debugfs解析并显示pin的配置
};
1.3 pin state

设备在某一状态下,其pin(group)、function(功能)、configuratio(配置)是唯一确定的,把这三个元素组成的状态抽象为pin state.核心数据结构为pictrl_map。由pinctrl_ops函数族中的dt_node_to_map函数完成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
struct pinctrl_map {
const char *dev_name; //device的名称
const char *name; //pin state的名称
enum pinctrl_map_type type; //该map的类型
const char *ctrl_dev_name; //pin controller device的名字
union {
struct pinctrl_map_mux mux;
struct pinctrl_map_configs configs;
} data;
};

enum pinctrl_map_type {
PIN_MAP_TYPE_INVALID,
PIN_MAP_TYPE_DUMMY_STATE, //不需要任何配置,仅仅为了表示state的存在
PIN_MAP_TYPE_MUX_GROUP, //配置管脚复用
PIN_MAP_TYPE_CONFIGS_PIN, //配置pin
PIN_MAP_TYPE_CONFIGS_GROUP, //配置pin group
};

struct pinctrl_map_mux {
const char *group; //group的名字
const char *function; //function的名字
};

struct pinctrl_map_configs {
const char *group_or_pin; //该pin或者pin group的名字
unsigned long *configs; //config数组
unsigned num_configs; //配置项的个数
};

2 pinctrl和GPIO的设备树和基本API

(源码位置 drivers/pinctrl)

2.1 pin配置信息详解

一般在设备树中创建一个节点描述pin信息,以imx6u为例:
imx6ull.dtsi中有iomuxc节点描述外设pin信息

1
2
3
4
iomuxc: iomuxc@020e0000 {
compatible = "fsl,imx6ul-iomuxc";
reg = <0x020e0000 0x4000>;
};

reg属性中的0x020e0000为iomuxc外设节点的首地址。
在.dts文件中以&iomuxc引用方式向.dtsi文件中iomuxc节点追加信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
&iomuxc {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_hog_1>;
imx6ul-evk {
pinctrl_hog_1: hoggrp-1 {
fsl,pins = <
MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x17059
MX6UL_PAD_GPIO1_IO05__USDHC1_VSELECT 0x17059
MX6UL_PAD_GPIO1_IO09__GPIO1_IO09 0x17059
MX6UL_PAD_GPIO1_IO00__ANATOP_OTG1_ID 0x13058
>;
};
...
};
};

在内核源码中全局搜索iomuxc节点的compatible属性即可得到pinctrl驱动文件源码。
以pinctrl_hog_1节点为例,MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x17059

  • MX6UL_PAD_UART1_RTS_B__GPIO1_IO19是一个宏定义,定义在arch/arm/boot/dts/imx6ul-pinfunc.h。定义如下:
    #define MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x0090 0x031C 0x0000 0x5 0x0
    • 0x0090(mux_reg寄存器偏移地址): 为IOMUXC_SW_MUX_CTL_PAD_UART1_RTS_B寄存器地址基于IOMUXC外设首地址的偏移量(复用配置)
    • 0x031C(conf_reg寄存器偏移地址): 为IOMUXC_SW_PAD_CTL_PAD_UART1_RTS_B寄存器地址基于IOMUXC外设首地址的偏移量(电器属性配置)
    • 0x0000(input_reg寄存器偏移地址): 有些外设有input_reg寄存器,此值为input_reg寄存器偏移量
    • 0x5(mux_reg寄存器值): 设置IOMUXC_SW_MUX_CTL_PAD_UART1_RTS_B寄存器值为0x5
    • 0x0(input_reg寄存器值): 设置input_reg寄存器值,此处无效
  • 0x17059 为confg_reg寄存器的值,根据具体需求配置

注:.dtsi文件引用imx6ull-pinfunc.h文件,而imx6ull-pinfunc.h文件再引用imx6ull-pinfunc.h

2.2设备树中添加pinctrl节点

例:虚拟一个名为“test”的设备,设备使用GPIO1_IO00这个PIN的GPIO功能。
具体步骤如下:

  1. 在dts文件中iomuxc节点下的imx6ul-evk节点下添加”pinctrl_test”节点,前缀必须为”pinctrl_”
    1
    2
    3
    pinctrl_test:testgrp{
    待添加的具体的PIN信息
    };
  2. 添加”fsl,pins”属性,pinctrl驱动通过读取设备树中”fsl,pins”属性的内容来获取PIN的配置信息,不同芯片属性可能会有差别
    1
    2
    3
    4
    5
    pinctrl_test:testgrp{
    fsl,pins<
    待添加的PIN的配置信息
    >;
    };
  3. 添加pin的配置信息,即复用引脚和config值
    1
    2
    3
    4
    5
    pinctrl_test:testgrp{
    fsl,pins<
    MX6UL_PAD_GPIO1_IO00__GPIO1_IO00 config
    >;
    };
2.3 gpio API

gpio子系统的作用:初始化GPIO并提供相应的API函数

  • 设备树中的GPIO信息
  1. 添加pinctrl名字
    pinctrl-name = "defaul";
  2. 在设备节点中添加设备所需的PIN的pinctrl信息所在子节点信息
    pinctrl-n = <&pinctrl_xxx>;
    驱动根据pinctrl信息设置pin的复用功能和电气属性
  3. 在设备节点中添加描述GPIO属性的语句
    xxx_gpios = <&GPIO组 pin号 有效电平>
    比如
    cd_gpios = <&gpio1 19 GPIO_ACTIVE_LOW>;
  • gpio子系统函数
  1. API函数 <linux/gpio.h>
    int gpio_request(unsigned gpio, const char *label)
    gpio:要申请的gpio标号,通过of_get_named_gpio函数获取此标号
    label:给此gpio设置一个名字
    返回值:0-申请成功 其他值-申请失败
    1. 释放GPIO管脚
      void gpio_free(unsigned gpio)
      gpio:要释放的gpio标号
    2. 设置gpio为输入模式
      int gpio_direction_input(unsigned gpio)
      gpio:要设置的gpio标号
      返回值:0-设置成功 其他值-失败
    3. 设置gpio为输出模式
      int gpio_direction_output(unsigned gpio)
      gpio:要设置的gpio标号
      返回值:0-设置成功 其他值-失败
    4. 获取gpio的值(宏函数)
      int gpio_get_value(unsigned gpio)
      gpio:要获取的gpio标号
      返回值:gpio值
    5. 设置gpio的值(宏函数)
      void gpio_set_value(unsigned gpio, int value)
      gpio:要设置的gpio标号
      value:要设置的gpio值
  2. gpio相关的of函数 <linux/of_gpio.h>
    1. 获取某属性中定义gpio信息的个数
      int of_gpio_named_count(struct device_node* np, const char *propname)
      np:设备节点
      propname:要统计gpio个数的属性名
      返回值:gpio数目 负值表示失败
    2. 获取”gpios”属性的gpio信息的个数
      int of_gpio_count(struct device_node* np)
      np:设备节点
      返回值:gpio数目 负值表示失败
    3. 获取GPIO标号
      int of_get_named_gpio(struct device_node* np, const char *propname, int index)
      np:设备节点
      propname:要获取的GPIO所属属性名
      index:GPIO索引,一个属性中可能含有多个GPIO信息
      返回值:gpio标号 负值表示失败