Linux pinctrl子系统学习(一)
1 Pinctrl子系统介绍
-
众所周知,ARM SoC提供了十分丰富的硬件接口,而接口物理上的表现就是一个个的pin(或者叫做pad, finger等)。为了实现丰富的硬件功能,SoC的pin需要实现复用功能,即单独的pin需要提供不同功能,例如,pin0既可以作为GPIO,可以也用于i2c的SCL,通过pin相关的复用寄存器来切换不同的功能。除此之外,软件还可以通过寄存器配置pin相关的电气特性,例如,上拉/下拉、驱动能力、开漏等。
-
Linux kernel 3.0之前的内核,对于pin的功能配置都是通过目标板的配置文件(arch/arm/mach-*)来初始化的,这种配置方式比较繁琐,十分容易出现问题(例如,pin的功能配置冲突)。所以,Linux kernel 3.0之后,实现了DT的板级配置信息管理机制,大大改善了对于pin的配置方式,随之一起实现的就是pinctrl子系统。
-
pinctrl子系统主要负责以下功能:
1、通过DTS配置的pin的功能;
2、对于pin实现复用功能;
3、配置pin的电器特性,例如,上拉/下拉、驱动能力、开漏等。; -
可见,pinctrl子系统地位相当于kernel全局的pin管理中心,kernel中所有需要pin资源的驱动、子系统都需要通过pinctrl子系统来申请、配置、释放。对于pin的操作来说,pinctrl子系统十分重要的。
2 Pinctrl子系统的框架
- 驱动分层最终目的:消除复杂性。框架如上图所示,可以分为三层,分别为最顶层consumer,即各种需要使用到pin的一些功能外设,如spi,i2c,sdio等等。中间层为pinctrl-core,是完成底层驱动与上层之间的连接。而pinctrl-driver为最底层,直接控制相关寄存器。
2.1 pinctrl-core
-
pinctrl-core抽象层主要的功能就是为Consumer提供访问pin的能力,即driver配置pin复用能、配置引脚的电气特性。
-
其实,对于pinctrl-core抽象层如何为上层提供服务完全不需要关心,此过程由Linux内核完成。那么,pinctrl-core如何完成与pinctrl-driver连接呢? pinctrl-core与pinctrl-driver是通过pin controller descriptor进行通信的。该结构定义如下:
/** * struct pinctrl_desc - pin controller descriptor, register this to pin * control subsystem * @name: name for the pin controller * @pins: an array of pin descriptors describing all the pins handled by * this pin controller * @npins: number of descriptors in the array, usually just ARRAY_SIZE() * of the pins field above * @pctlops: pin control operation vtable, to support global concepts like * grouping of pins, this is optional. * @pmxops: pinmux operations vtable, if you support pinmuxing in your driver * @confops: pin config operations vtable, if you support pin configuration in * your driver * @owner: module providing the pin controller, used for refcounting */ struct pinctrl_desc { /*pinctrl-driver属性*/ const char *name; const struct pinctrl_pin_desc *pins; unsigned int npins; /*pinctrl-drive抽象接口*/ const struct pinctrl_ops *pctlops; const struct pinmux_ops *pmxops; const struct pinconf_ops *confops; struct module *owner; };
2.2 pinctrl-driver
- 实际上在bsp层,我们需要做的就是定义一个pinctrl_desc结构体,并将此结构体注册到pinctrl子系统中去,上层即可连接到我们所写的bsp层驱动。
- 在编写一个pinctrl_desc结构体之前,首先要清楚一下几个概念:pin,pin groups,pin configuration和pin multiplexing。
2.2.1 pin
-
kernel的pin controller子系统要想管理好系统的pin资源,第一个要搞明白的问题就是:系统中到底有多少个pin?用软件语言来表述就是:要把系统中所有的pin描述出来,并建立索引。这由上面struct pinctrl_desc结构中pins和npins来完成。
-
对pinctrl core来说,它只关心系统中有多少个pin,并使用自然数为这些pin编号,后续的操作,都是以这些编号为操作对象。至于编号怎样和具体的pin对应上,完全是pinctrl driver自己的事情。
因此,pinctrl driver需要根据实际情况,将系统中所有的pin组织成一个struct pinctrl_pin_desc类型的数组,该类型的定义为:/** * struct pinctrl_pin_desc - boards/machines provide information on their * pins, pads or other muxable units in this struct * @number: unique pin number from the global pin number space * @name: a name for this pin * @drv_data: driver-defined per-pin data. pinctrl core does not touch this */ struct pinctrl_pin_desc { unsigned number; const char *name; void *drv_data; };
2.2.2 Pin groups
-
在SoC系统中,有时需要将很多pin组合在一起,以实现特定的功能,例如SPI接口、I2C接口等。因此pin controller需要以group为单位,访问、控制多个pin,这就是pin groups。相应地,pinctrl-driver需要提供一些机制,来获取系统中到底有多少groups、每个groups包含哪些pins、等等。
-
在struct pinctrl_ops中抽象出回调函数,用来获取pin groups相关信息,如下:
struct pinctrl_ops { int (*get_groups_count) (struct pinctrl_dev *pctldev); const char *(*get_group_name) (struct pinctrl_dev *pctldev, unsigned selector); 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); 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); };
-
get_groups_count,获取系统中pin groups的个数,后续的操作,将以相应的索引为单位(类似数组的下标,个数为数组的大小)。
-
get_group_name,获取指定group(由索引selector指定)的名称。
-
get_group_pins,获取指定group的所有pins(由索引selector指定),结果保存在pins(指针数组)和num_pins(指针)中。
-
dt_node_to_map用于将device tree中的pin state信息转换为pin map,consumer driver在需要的时候,可以调用pinctrl_get/devm_pinctrl_get接口。pinctrl subsystem在pinctrl get的过程中,解析consumer device的dts node,找到相应的pin state,进行调用pinctrl driver提供的dt_node_to_map API,解析pin state并转换为pin map。
注:pin state
- consumer在某一状态下(如工作状态、休眠状态、等等),所使用的pin(pin group)、的function和configuration,是唯一确定的。就是说pin(pin group)以及相应的function和configuration的组合,可以确定一个设备的一个“状态”。consumer可以调用pinctrl subsystem提供的API(例如pinctrl_select_state),使自己的某个pin state生效。pinctrl subsystem进而调用pinctrl driver提供的各种回调函数,配置pin controller的硬件。
在设备树下定义的pin_state:
2.2.3 Pin configuration
SoC中的管脚有些属性可以配置,例如上拉、下拉、高阻、驱动能力等。pinctrl subsystem使用pin configuration来封装这些功能,具体体现在struct pinconf_ops数据结构中,如下:
struct pinconf_ops {
#ifdef CONFIG_GENERIC_PINCONF
bool is_generic;
#endif
int (*pin_config_get) (struct pinctrl_dev *pctldev,
unsigned pin,
unsigned long *config);
int (*pin_config_set) (struct pinctrl_dev *pctldev,
unsigned pin,
unsigned long *configs,
unsigned num_configs);
int (*pin_config_group_get) (struct pinctrl_dev *pctldev,
unsigned selector,
unsigned long *config);
int (*pin_config_group_set) (struct pinctrl_dev *pctldev,
unsigned selector,
unsigned long *configs,
unsigned num_configs);
int (*pin_config_dbg_parse_modify) (struct pinctrl_dev *pctldev,
const char *arg,
unsigned long *config);
void (*pin_config_dbg_show) (struct pinctrl_dev *pctldev,
struct seq_file *s,
unsigned offset);
void (*pin_config_group_dbg_show) (struct pinctrl_dev *pctldev,
struct seq_file *s,
unsigned selector);
void (*pin_config_config_dbg_show) (struct pinctrl_dev *pctldev,
struct seq_file *s,
unsigned long config);
};
- pin_config_get,获取指定pin当前配置,保存在config指针中(配置的具体含义,只有pinctrl driver自己知道,下同)。
- pin_config_set,设置指定pin的配置(可以同时配置多个config,具体意义要由相应pinctrl driver解释)。
- pin_config_group_get、pin_config_group_set,获取或者设置指定pin group的配置项。剩下的是一些debug用的api。
2.2.4 Pin multiplexing
-
为了照顾不同类型的产品、不同的应用场景,SoC中的很多管脚可以配置为不同的功能,例如A2和B5两个管脚,既可以当作普通的GPIO使用,又可以配置为I2C0的的SCL和SDA,也可以配置为UART5的TX和RX,这称作管脚的复用(pin multiplexing,简称为pinmux)。kernel pinctrl subsystem使用struct pinmux_ops来抽象pinmux有关的操作,如下:
struct pinmux_ops { int (*request) (struct pinctrl_dev *pctldev, unsigned offset); int (*free) (struct pinctrl_dev *pctldev, unsigned offset); int (*get_functions_count) (struct pinctrl_dev *pctldev); const char *(*get_function_name) (struct pinctrl_dev *pctldev, unsigned selector); int (*get_function_groups) (struct pinctrl_dev *pctldev, unsigned selector, const char * const **groups, unsigned *num_groups); int (*set_mux) (struct pinctrl_dev *pctldev, unsigned func_selector, unsigned group_selector); 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); bool strict; };
-
get_functions_count,获取系统中function的个数。
-
get_function_name,获取指定function的名称。
-
get_function_groups,获取指定function所占用的pin group(可以有多个)。
-
set_mux,将指定的pin group(group_selector)设置为指定的function(func_selector)。
-
request,检查某个pin是否已作它用,用于管脚复用时的互斥(避免多个功能同时使用某个pin而不知道,导致奇怪的错误)。
-
free,request的反操作。
-
strict,为true时,说明该pin controller不允许某个pin作为gpio和其它功能同时使用。