通过前面硬件原理的介绍,我们知道配置一个pin需要经过mux控制寄存器和pad控制寄存器,对于输入引脚的配置,还需要另外配置输入选择寄存器。那么把这些概念用软件来实现就是 pin 驱动控制器的本质。在了解驱动前我们先来看几个关键结构体。
pinctrl_desc 结构体
使用 struct pinctrl_desc 抽象一个 pin controller,包含控制器的名字、pin脚的数量、pinmux功能、pinconf功能和pinctl功能。其中结构体 pinmux_ops 和 pinconf_ops 分别用于配置mux模式和pad电气属性,而 pinctrl_ops 则是控制一组pin,如uart、i2c、spi等外设的pin组。该结构的定义如下:
struct pinctrl_desc {
const char *name;//pin控制器名字
const struct pinctrl_pin_desc *pins;//描述芯片的物理引脚pad资源
unsigned int npins;
const struct pinctrl_ops *pctlops;//全局pin配置
const struct pinmux_ops *pmxops; //mux配置
const struct pinconf_ops *confops;//电气属性配置
struct module *owner;
bool link_consumers;
};
其中:
- pins
变量 pins 和 npins 把系统中所有的 pin 描述出来,并建立索引。驱动为了和具体的 pin 对应上,再将这些描述的这些 pin 组织成一个 struct pinctrl_pin_desc 类型的数组,该类型的定义为:
struct pinctrl_pin_desc {
unsigned number;
const char *name;
void *drv_data;
};
SoC中,有时需要将很多 pin 组合在一起,以实现特定的功能,例如 eqos 接口、i2c 接口等。因此 pin controller 需要以 group 为单位,访问、控制多个 pin,这就是 pin groups。
struct group_desc {
const char *name;
int *pins;
int num_pins;
void *data;
};
在设备树里表示就是:
pinctrl_eqos: eqosgrp {
fsl,pins = <
MX93_PAD_ENET1_MDC__ENET_QOS_MDC 0x57e
MX93_PAD_ENET1_MDIO__ENET_QOS_MDIO 0x57e
MX93_PAD_ENET1_RD0__ENET_QOS_RGMII_RD0 0x57e
MX93_PAD_ENET1_RD1__ENET_QOS_RGMII_RD1 0x57e
MX93_PAD_ENET1_RD2__ENET_QOS_RGMII_RD2 0x57e
MX93_PAD_ENET1_RD3__ENET_QOS_RGMII_RD3 0x57e
MX93_PAD_ENET1_RXC__CCM_ENET_QOS_CLOCK_GENERATE_RX_CLK 0x5fe
MX93_PAD_ENET1_RX_CTL__ENET_QOS_RGMII_RX_CTL 0x57e
MX93_PAD_ENET1_TD0__ENET_QOS_RGMII_TD0 0x57e
MX93_PAD_ENET1_TD1__ENET_QOS_RGMII_TD1 0x57e
MX93_PAD_ENET1_TD2__ENET_QOS_RGMII_TD2 0x57e
MX93_PAD_ENET1_TD3__ENET_QOS_RGMII_TD3 0x57e
MX93_PAD_ENET1_TXC__CCM_ENET_QOS_CLOCK_GENERATE_TX_CLK 0x5fe
MX93_PAD_ENET1_TX_CTL__ENET_QOS_RGMII_TX_CTL 0x57e
>;
};
pinctrl core 会在 struct pinctrl_ops 中抽象出三个回调函数,用来获取 pin groups 相关信息,如下:
pinctrl_ops
struct pinctrl_ops {
//获取系统中pin groups的个数,后续的操作,将以相应的索引为单位(类似数组的下标,个数为数组的大小)
int (*get_groups_count) (struct pinctrl_dev *pctldev);
//获取指定group(由索引selector指定)的名称
const char *(*get_group_name) (struct pinctrl_dev *pctldev, unsigned selector);
//获取指定group的所有pins(由索引selector指定),结果保存在pins(指针数组)和num_pins(指针)中
int (*get_group_pins) (struct pinctrl_dev *pctldev, unsigned selector, const unsigned **pins, unsigned *num_pins);
void (*pin_dbg_show) (struct pinctrl_dev *pctldev, struct seq_file *s, unsigned offset);
//用于将device tree中的pin state信息转换为pin map
int (*dt_node_to_map) (struct pinctrl_dev *pctldev, struct device_node *np_config, struct pinctrl_map **map, unsigned *num_maps);
void (*dt_free_map) (struct pinctrl_dev *pctldev, struct pinctrl_map *map, unsigned num_maps);
};
pinmux_ops
为了兼容不同的应用场景,有很多管脚可以配置为不同的功能,例如A和B两个管脚,既可以当作普通的GPIO使用,又可以配置为I2C的的SCL和SDA,也可以配置为UART的TX和RX,这称作管脚的复用(简称 pin mux)。使用 struct pinmux_ops 来抽象 pin mux 有关的操作,如下:
struct pinmux_ops {
//检查某个pin是否已作它用,用于管脚复用时的互斥
int (*request) (struct pinctrl_dev *pctldev, unsigned offset);
//request的反操作
int (*free) (struct pinctrl_dev *pctldev, unsigned offset);
//获取系统中function的个数
int (*get_functions_count) (struct pinctrl_dev *pctldev);
//获取指定function的名称
const char *(*get_function_name) (struct pinctrl_dev *pctldev, unsigned selector);
//获取指定function所占用的pin group
int (*get_function_groups) (struct pinctrl_dev *pctldev, unsigned selector, const char * const **groups, unsigned *num_groups);
//将指定的pin group(group_selector)设置为指定的function(func_selector)
int (*set_mux) (struct pinctrl_dev *pctldev, unsigned func_selector, unsigned group_selector);
//以下是gpio相关的操作
int (*gpio_request_enable) (struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, unsigned offset);
void (*gpio_disable_free) (struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, unsigned offset);
int (*gpio_set_direction) (struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, unsigned offset, bool input);
//为true时,说明该pin controller不允许某个pin作为gpio和其它功能同时使用
bool strict;
};
pinconf_ops
除了上面的 pin 和 pin group,有些管脚可以配置,比如上拉,下拉,高阻等。具体体现在 struct pinconf_ops 数据结构中,如下:
struct pinconf_ops {
#ifdef CONFIG_GENERIC_PINCONF
bool is_generic;
#endif
//获取指定 pin 的当前配置,保存在 config 指针中
int (*pin_config_get) (struct pinctrl_dev *pctldev, unsigned pin, unsigned long *config);
//设置指定pin的配置
int (*pin_config_set) (struct pinctrl_dev *pctldev, unsigned pin, unsigned long *configs, unsigned num_configs);
//获取指定pin group的配置项
int (*pin_config_group_get) (struct pinctrl_dev *pctldev, unsigned selector, unsigned long *config);
//设置指定pin group的配置项
int (*pin_config_group_set) (struct pinctrl_dev *pctldev, unsigned selector, unsigned long *configs, unsigned num_configs);
......
- pin state
根据前面的描述,pinctrl driver 抽象出来了一些离散的对象,并实现了这些对象的控制和配置方式。然后我们回到某一个具体的 device 上(如 lpuart,usdhc)。一个设备在某一状态下(如工作状态、休眠状态、等等),所使用的pin(pin group)、pin(pin group)的 function 和 configuration,是唯一确定的。所以固定的组合可以确定固定的状态,在设备树里用 pinctrl-names 指明状态名字,pinctrl-x 指明状态引脚。
- pin map
pin state 有关的信息是通过 pin map 收集,相关的数据结构如下:
struct pinctrl_map {
//device的名称
const char *dev_name;
//pin state的名称
const char *name;
//该map的类型
enum pinctrl_map_type type;
//pin controller device的名字
const char *ctrl_dev_name;
union {
struct pinctrl_map_mux mux;
struct pinctrl_map_configs configs;
} data;
};
enum pinctrl_map_type {
PIN_MAP_TYPE_INVALID,
//不需要任何配置,仅仅为了表示state的存在
PIN_MAP_TYPE_DUMMY_STATE,
//配置管脚复用
PIN_MAP_TYPE_MUX_GROUP,
//配置pin
PIN_MAP_TYPE_CONFIGS_PIN,
//配置pin group
PIN_MAP_TYPE_CONFIGS_GROUP,
};
struct pinctrl_map_mux {
//group的名字
const char *group;
//function的名字
const char *function;
};
struct pinctrl_map_configs {
//该pin或者pin group的名字
const char *group_or_pin;
//configuration数组
unsigned long *configs;
//配置项的个数
unsigned num_configs;
};
pinctrl driver 确定了 pin map 各个字段的格式之后,就可以在 dts 文件中维护 pin state 以及相应的 mapping table。pinctrl core 在初始化的时候,会读取并解析 dts,并生成 pin map。
而各个 client device 可以在自己的 dts node 中,直接引用 pinctrl driver 定义的 pin state,并在设备驱动的相应的位置,调用 pinctrl subsystem 提供的 API(pinctrl_lookup_state,pinctrl_select_state),active 或者 deactive 这些 state。
pin controller 驱动初始化
pin controller 的设备树如下所示:
iomuxc: pinctrl@443c0000 {
compatible = "fsl,imx93-iomuxc";
reg = <0x443c0000 0x10000>;
status = "okay";
};
&iomuxc {
pinctrl_eqos: eqosgrp {
fsl,pins = <
......
>;
};
pinctrl_eqos_sleep: eqosgrpsleep {
fsl,pins = <
......
>;
};
pinctrl_fec: fecgrp {
fsl,pins = <
......
>;
};
pinctrl_fec_sleep: fecsleepgrp {
fsl,pins = <
......
>;
};
......
pinctrl_sai1: sai1grp {
fsl,pins = <
......
>;
};
pinctrl_sai1_sleep: sai1grpsleep {
fsl,pins = <
......
>;
};
......
};
pin controller驱动的初始化大致如下: