Linux pinctrl子系统学习(一) Myth丶恋晨 2021-12-22 18:47 475阅读 0赞 # 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子系统的框架 ## ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTM4MzY5MDk_size_16_color_FFFFFF_t_70] * 驱动分层最终目的:消除复杂性。框架如上图所示,可以分为三层,分别为最顶层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: ![在这里插入图片描述][20190629211615478.png] ![在这里插入图片描述][20190629211858599.png] #### 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和其它功能同时使用。 [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTM4MzY5MDk_size_16_color_FFFFFF_t_70]: /images/20211220/44d0344c4bdf42e2bd5182946a178e2e.png [20190629211615478.png]: /images/20211220/a0f1125d8a6846ea932d30974e6913a3.png [20190629211858599.png]: /images/20211220/661678dbebb34aeba4312b396f5f04f4.png
还没有评论,来说两句吧...