linux设备树-pinctrl子系统
目录
----------------------------------------------------------------------------------------------------------------------------
内核版本:linux 5.2.8
根文件系统:busybox 1.25.0
u-boot:2016.05
----------------------------------------------------------------------------------------------------------------------------
一、IO概述
1.1 硬件功能分类
ARM based SoC的datasheet中总有一个章节叫做GPIO controller(或者I/O ports)的章节来描述如何配置、使用SoC的引脚。虽然GPIO controller的硬件描述中充满了大量的寄存器的描述,但是这些寄存器的功能大概分成下面三个类别:
(1) 有些硬件逻辑是和IO port本身的功能设定相关的,我们称这个HW block为pin controller。软件通过设定pin controller这个硬件单元的寄存器可以实现:
- 引脚功能配置:例如该I/O pin是一个普通的GPIO还是一些特殊功能引脚(例如memeory bank上CS信号);
- 引脚特性配置:例如pull-up/down电阻的设定,drive-strength的设定等;
(2) 如果一组GPIO被配置成SPI,那么这些pin脚被连接到了SPI controller,如果配置成GPIO,那么控制这些引脚的就是GPIO controller。通过访问GPIO controller的寄存器,软件可以:
- 配置GPIO的方向;
- 如果是输出,可以配置high level或者low level;
- 如果是输入,可以获取GPIO引脚上的电平状态;
(3) 如果一组GPIO有中断控制器的功能,虽然控制寄存器在datasheet中的I/O ports章节描述,但是实际上这些GPIO已经被组织成了一个interrupt controller的硬件block,它更像是一个GPIO类型的中断控制器,通过访问GPIO中断控制寄存器,软件可以:
- 中断的enable和disable(mask和unmask);
- 触发方式;
- 中断状态清除;
1.2 抽象硬件差异
传统的GPIO driver是负责上面三大类的控制,而新的linux kernel中的GPIO subsystem则用三个软件模块来对应上面三类硬件功能:
- pin control subsystem(或者简称pinctrl subsystem):驱动pin controller硬件的软件子系统;
- GPIO subsystem:驱动GPIO controller硬件的软件子系统;关于GPIO子系统我们之前已经介绍过了,具体可以参考:linux驱动移植-GPIO子系统;
- GPIO interrupt chip driver:这个模块是作为一个interrupt subsystem中的一个底层硬件驱动模块存在的;
总体来说,pin controller和GPIO controller都是数字输入/输出控制的IP核,但其控制的对象不同,前者控制的引脚可用于GPIO功能、I2C功能等;后者只是把引脚配置为输入、输出等简单的功能。两者的关系是先用pin controller把引脚配置为GPIO,再用GPIO controller把引脚配置为输入或输出。
1.3 外部中断
1.3.1 外部中断资源
s3c2440一共有24个外部中断,分别对应24个GPIO引脚:
- EINT0~7对应的GPIO是GPF0~7;
- EINT8~23对应的GPIO是GPG0~15。
1.3.2 外部中断初始化
以GPF7为例,如果将该引脚配置为上升沿外部中断:
- 首先需要配置GPFCON寄存器GPF7引脚功能复用为EINT;
- 然后配置EINTMASK寄存器EINT7使能中断;
- 最后配置INTMSK寄存器使能EINT7对应的主中断EINT4~EINT7;
二、pinctrl子系统
在许多SoC内部都包含有pin controller,通过pin controller的寄存器,我们可以配置一个或者一组引脚的功能和特性。
各个厂商SoC的pin脚在使用中,都有许多共同的特性,要么配置,要么复用pin脚。所以内核提供了一套代码来管理这些pin,这就是pinctrl subsystem。主要实现的功能:
- 管理系统中所有的可以控制的pin,在系统初始化的时候,枚举所有可以控制的pin,并标识这些pin;每个pin都有的唯一的ID;
- 管理这些pin的复用(Multiplexing),对于SoC而言,其引脚除了配置成普通的GPIO之外,若干个引脚还可以组成一个pin group,形成特定的功能, pinctrl subsystem要管理所有的pin group;
- 配置这些pin的电气特性,例如使能或禁止引脚的上拉、下拉电阻,配置引脚的driver strength;
2.1 重要概念
pinctrl subsystem涉及到了两个对象:
- pin controller device:其主要目的是提供服务,可以用来复用引脚、配置引脚;
- client device:使用服务,即使用pinctrl系统的设备。声明自己要使用哪些引脚的哪些功能,怎么配置它们;因此需要在client device设备节点中描述与pin相关的信息。
2.2 设备节点
以arch/arm/boot/dts/s3c2440-pinctrl.dtsi为例,描述了s3c2440 pin controller的dts结构,内容如下:
pinctrl_0: pinctrl@56000000 { compatible = "samsung,s3c2440-pinctrl"; reg = <0x56000000 0x1000>; wakeup-interrupt-controller { compatible = "samsung,s3c2410-wakeup-eint"; interrupts = <0 0 0 3>, <0 0 1 3>, <0 0 2 3>, <0 0 3 3>, <0 0 4 4>, <0 0 5 4>; }; /* * Pin banks */ gpa: gpa { gpio-controller; #gpio-cells = <2>; }; ...... gpj: gpj { gpio-controller; #gpio-cells = <2>; }; /* * Pin groups */ uart0_data: uart0-data { samsung,pins = "gph-2", "gph-3"; samsung,pin-function = <EXYNOS_PIN_FUNC_2>; }; ..... };
如上所示:pinctrl@56000000内部定义了一些自己的属性,比如compatible、reg,此外还定义了大量的子节点,这些子节点我们称之为引脚配置(pin configuration)。
定义pin configuration的目的是为了让client device引用。例如串口设备:
- 无device tree,我们一般会在初始化代码中配置相关的引脚功能为串口功能;
- 有了device tree,我们可以通过device tree来传递这样的信息;设备节点可以通过自己节点的属性来指向pin controller的某个child node。
在pin controller node中定义的pin configuration可以分为两大类:pin bank和pin group。
2.2.1 bank
所谓的pin bank,个人理解就是一组GPIO端口,这一组GPIO端口同属于一个GPIO控制器。以s3c2440为例,分为了9 个GPIO控制器:
GPIO控制器 | GPIO端口名称 | GPIO端口数量 |
GPIOA | GPA0~GPA24 | 25 |
GPIOB | GPB0~GPB10 | 11 |
GPIOC | GPC0~GPC15 | 16 |
GPIOD | GPD0~GPD15 | 16 |
GPIOE | GPE0~GPE15 | 16 |
GPIOF | GPF0~GPF7 | 8 |
GPIOG | GPG0~GPG15 | 16 |
GPIOH | GPH0~GPH10 | 11 (这里明明11个,datasheet说的总共9个) |
GPIOJ | GPJ0~GPJ12 | 13 |
所以在arch/arm/boot/dts/s3c2440-pinctrl.dtsi文件中就把这9组GPIO端口枚举成pin bank,如下:

/* * Pin banks */ gpa: gpa { gpio-controller; #gpio-cells = <2>; }; gpb: gpb { gpio-controller; #gpio-cells = <2>; }; gpc: gpc { gpio-controller; #gpio-cells = <2>; }; gpd: gpd { gpio-controller; #gpio-cells = <2>; }; gpe: gpe { gpio-controller; #gpio-cells = <2>; }; gpf: gpf { gpio-controller; #gpio-cells = <2>; interrupt-controller; #interrupt-cells = <2>; }; gpg: gpg { gpio-controller; #gpio-cells = <2>; interrupt-controller; #interrupt-cells = <2>; }; gph: gph { gpio-controller; #gpio-cells = <2>; }; gpj: gpj { gpio-controller; #gpio-cells = <2>; };
如gpa: gpa 这个child node就是描述GPIOA这个组,也就是gpa bank.。pin bank中支持如下属性:
- gpio-controller:表示这是一个GPIO控制器;这样就可以使用GPIO子系统API来操作管脚了;
- interrupt-controller:表示这是一个中断控制器,有的GPIO控制器也可以是中断控制器,如gpf;
- #gpio-cells:表示使用这个bank的GPIO时,需要用两个32位数去描述;为什么要用2个数?其实使用多个cell来描述一个引脚,这是GPIO Controller自己决定的。比如可以用其中一个cell来表示那是哪一个引脚,用另一个cell来表示它是高电平有效还是低电平有效,甚至还可以用更多的cell来示其他特性;
gpf、gpg本身也充当一个中断控制器,它的interrupt parent也是interrupt-controller@4a000000,gpf的interrupt cell是2,表示引用gpf的一个中断需要2个参数来描述。
GPIO控制器支持两种类型的外部中断:外部GPIO中断和外部唤醒中断。两者之间的区别在于,外部唤醒中断可以用作系统唤醒事件。
更多信息可以参考: Documentation/devicetree/bindings/pinctrl/samsung-pinctrl.txt。
2.2.2 group
以功能为依据,具有相同功能的引脚称为一个pin group,比如:
- 串口0使用的GPH2、GPH3引脚,因此可以将GPH2、GPH3分为一组;
- I2C使用的GPE14、GPE15引脚,因此可以将GPE14、GPE15分为一组;
所以在arch/arm/boot/dts/s3c2440-pinctrl.dtsi文件中定义到了大量的pin group,如下:

/* * Pin groups */ uart0_data: uart0-data { samsung,pins = "gph-2", "gph-3"; samsung,pin-function = <EXYNOS_PIN_FUNC_2>; }; uart1_data: uart1-data { samsung,pins = "gph-4", "gph-5"; samsung,pin-function = <EXYNOS_PIN_FUNC_2>; }; uart2_data: uart2-data { samsung,pins = "gph-6", "gph-7"; samsung,pin-function = <EXYNOS_PIN_FUNC_2>; }; uart2_fctl: uart2-fctl { samsung,pins = "gph-6", "gph-7"; samsung,pin-function = <EXYNOS_PIN_FUNC_2>; }; extuart_clk: extuart-clk { samsung,pins = "gph-8"; samsung,pin-function = <EXYNOS_PIN_FUNC_2>; }; i2c0_bus: i2c0-bus { samsung,pins = "gpe-14", "gpe-15"; samsung,pin-function = <EXYNOS_PIN_FUNC_2>; }; spi0_bus: spi0-bus { samsung,pins = "gpe-11", "gpe-12", "gpe-13"; samsung,pin-function = <EXYNOS_PIN_FUNC_2>; }; sd0_clk: sd0-clk { samsung,pins = "gpe-5"; samsung,pin-function = <EXYNOS_PIN_FUNC_2>; }; sd0_cmd: sd0-cmd { samsung,pins = "gpe-6"; samsung,pin-function = <EXYNOS_PIN_FUNC_2>; }; sd0_bus1: sd0-bus1 { samsung,pins = "gpe-7"; samsung,pin-function = <EXYNOS_PIN_FUNC_2>; }; sd0_bus4: sd0-bus4 { samsung,pins = "gpe-8", "gpe-9", "gpe-10"; samsung,pin-function = <EXYNOS_PIN_FUNC_2>; }; /*添加Nand Flash所用的管脚*/ nand_pinctrl: nand_pinctrl { samsung,pins = "gpa-17", "gpa-18", "gpa-19", "gpa-20", "gpa-22"; samsung,pin-function = <1>; };
其中:
- samsung,pins:包含要应用特定引脚功能选择或引脚配置(或两者)的引脚列表;该属性至少需要指定一个引脚,并且没有指定引脚数目的上限;引脚使用从SoC硬件手册中派生出来的引脚名称来指定。例如,pin控制器中GPA0组的引脚可以表示为“gpa0-0”、“gpa0-1”、“gpa0-2”等,名称应该采用小写字母。引脚名称的格式应该是(根据硬件手册)“[引脚组名称]-[组内引脚编号]”;
-
samsung,pin-function:指定应用于列在“samsung,pins”属性中的每个引脚上的引脚功能选择,将这些GPIO初始值设置为2,该属性的值应该从所指定的引脚组的SoC硬件手册中选择,具体是什么功能,有datasheet解释;
还可以选择性地指定应用于“samsung,pins”属性中列出的所有引脚上的一个或多个引脚配置。支持以下引脚配置属性:
- samsung,pin-val:引脚输出缓冲区的初始值;
- samsung,pin-pud:上下拉配置;
- samsung,pin-drv:驱动器强度配置;
- samsung,pin-pud-pdn:低功耗模式下的上下拉配置;
- samsung,pin-drv-pdn:低功耗模式下的驱动器强度配置;
更多信息可以参考: Documentation/devicetree/bindings/pinctrl/samsung-pinctrl.txt。
2.2.3 client device引用pinctrl
当一个client device想引用某个"引脚配置节点"应该如何进行描述呢?一个典型的device tree中的外设节点定义如下:
device-node-name { ....... pinctrl-names = "sleep", "active"; pinctrl-0 = <pin-config-0-a>; pinctrl-1 = <pin-config-1-a pin-config-1-b>; };
其中pinctrl-names属性和pinctrl-%d属性格外重要,因为它们是内核设定的属性,我们下面来介绍;
(1) pinctrl-names定义了一个state列表,那么什么是state呢?
对于一个client device,比如它是一个串口设备,它有多种状态,比如default、sleep等。那么对应的引脚也有这些状态,比如:
- 在默认状态下,串口设备是工作的,那么所用的pin都要复用为UART功能;
- 在休眠状态下,为了省电,可以把这些引脚复用为GPIO功能,或者直接把它们配置输出高电平;
state有两种标识:一种就是pinctrl-names定义的字符串列表,另外一种就是ID。ID从0开始,依次加一。以上面例子为例:
- state ID等于0(名字是sleep)的state对应pinctrl-0属性;
- state ID等于1(名字是active)的state对应pinctrl-1属性;
内核自己定义了"default","init","idel","sleep"状态;也可以是其它自己定义的状态, 比如串口的"flow_ctrl"状态(使用流量控制)。
(2) pinctrl-x的定义。pinctrl-x是一个句柄(phandle)列表,每个句柄指向一个"引脚配置节点",有时候,一个state可以用到多组pin,比如A1、A2两组pin,A1组pin复用为F1功能,A2组pin复用为F2功能。
我们以s3c2440串口0设备节点定义为例:
&uart_0 { status = "okay"; pinctrl-names = "default"; pinctrl-0 = <&uart0_data>; };
其中:
- pinctrl-names:这里只定义了一个state就是default,对应pinctrl-0属性定义;
- pinctrl-0:pinctrl-0是一个句柄(phandle)列表,每个句柄指向一个"引脚配置节点";这里配置为uart0-data。
2.3 pinctrl subsystem框架
下图描述了pinctrl subsystem的模块图:
中间层是pin control core,用于管理系统中的pin controller。pin control core汇总了pin controller的通用操作:
- 对上pin control core提供的一套统一通用的操作pin controller硬件的软件接口,屏蔽了不同芯片的具体实现;
- 对下pin control core提供了针对不同芯片操作的一套framework,针对不同芯片,只需要实现pin controller driver ,然后使用pin control core提供的注册函数,将其挂接到pinctrl subsystem上,这样就完成了这一套东西;
基本上这个软件框架图和GPIO subsystem是一样的,其软件抽象的思想也是一样的,当然其内部具体的实现不一样。
2.4 目录结构
2.4.1 源文件
linux内核将pinctrl驱动相关的代码放在drivers/pinctrl目录下,这下面的文件还是比较多的,我们大概了解一下即可。
其中:
- core.c、core.h :pinctrl subsystem的core driver;
- pinctrl-utils.c、pinctrl-utils.h:pinctrl subsystem的一些utility接口函数;
- pinmux.c pinmux.h:pinctrl subsystem的core driver(pin muxing部分的代码,也称为pinmux driver);
- pinconf.c、pinconf.h:pinctrl subsystem的core driver(pin config部分的代码,也称为pin config driver);
- devicetree.c、devicetree.h:pinctrl subsystem的device tree代码;
- pinctrl-xxxx.c:各种pin controller的low level driver;
在pin controller driver文档中 ,我们以s3c2440的pin controller为例,描述了一个具体的low level的driver,这个driver涉及的文件包括pinctrl-samsung.c,pinctrl-samsung.h和pinctrl-s3c24xx.c。
2.4.2 头文件
pinctrl subsystem会向系统中的其它driver提供接口以便进行该driver的引脚配置和引脚复用功能的设定,下面这些头文件就定义了pinctrl subsystem的外部接口以及相关的数据结构:
- consumer.h:其它的driver要使用pinctrl subsystem的以下功能:
- a、设置引脚复用功能;
- b、配置引脚的电气特性;
- devinfo.h:这是for linux内核的驱动模型模块(driver model)使用的接口。struct device中包括了一个struct dev_pin_info *pins的成员,这个成员描述了该设备的引脚的初始状态信息,在probe之前,driver model中的core driver在调用driver的probe函数之前会先设定pin state;
- machine.h:machine模块的接口;
pinctrl subsystem提供给底层pin controller driver的头文件列表如下:
- pinconf-generic.h:这个接口主要是提供给各种pin controller driver使用的,不是外部接口;
- pinconf.h:pin configuration 接口;
- pinctrl-state.h:pin control state状态定义;
- pinmux.h:pin mux function接口;
2.5 数据结构
学习pin controller driver,首先要了解pinctrl subsystem涉及到的数据结构,知道每个数据结构以及成员的含义之后,再去看源码就容易了。
我们从pin controller device和client device视角去介绍pinctrl subsystem涉及到的数据结构,其中:
- pin controller device相关的数据结构主要有:pinctrl_desc、pinctrl_ops、pinmux_ops、pinconf_ops、pinctrl_dev;
- client device相关的数据结构主要有:pinctrl、pinctrl_state、pinctrl_setting、pinctrl_map、pinctrl_dt_map;
三、pin controller device
3.1 struct pinctrl_dev
pin control core使用struct pinctrl_dev抽象一个pin controller device,其定义在drivers/pinctrl/core.h文件,如下:
/** * struct pinctrl_dev - pin control class device * @node: node to include this pin controller in the global pin controller list * @desc: the pin controller descriptor supplied when initializing this pin * controller * @pin_desc_tree: each pin descriptor for this pin controller is stored in * this radix tree * @pin_group_tree: optionally each pin group can be stored in this radix tree * @num_groups: optionally number of groups can be kept here * @pin_function_tree: optionally each function can be stored in this radix tree * @num_functions: optionally number of functions can be kept here * @gpio_ranges: a list of GPIO ranges that is handled by this pin controller, * ranges are added to this list at runtime * @dev: the device entry for this pin controller * @owner: module providing the pin controller, used for refcounting * @driver_data: driver data for drivers registering to the pin controller * subsystem * @p: result of pinctrl_get() for this device * @hog_default: default state for pins hogged by this device * @hog_sleep: sleep state for pins hogged by this device * @mutex: mutex taken on each pin controller specific action * @device_root: debugfs root for this device */ struct pinctrl_dev { struct list_head node; struct pinctrl_desc *desc; struct radix_tree_root pin_desc_tree; #ifdef CONFIG_GENERIC_PINCTRL_GROUPS struct radix_tree_root pin_group_tree; unsigned int num_groups; #endif #ifdef CONFIG_GENERIC_PINMUX_FUNCTIONS struct radix_tree_root pin_function_tree; unsigned int num_functions; #endif struct list_head gpio_ranges; struct device *dev; struct module *owner; void *driver_data; struct pinctrl *p; struct pinctrl_state *hog_default; struct pinctrl_state *hog_sleep; struct mutex mutex; #ifdef CONFIG_DEBUG_FS struct dentry *device_root; #endif };
其中部分参数含义如下:
- node:用于构建双向链表,将此pinctrldev添加到全局链表pinctrldev_list;
- desc:初始化此 pin controller时提供的 pin controller描述符;
- pin_desc_tree:存储此pin controller的下每个pin对应的pin_desc的基数树;在注册pin时,会为每个pin分配一个struct pin_desc结构体,并将其添加到该基数树上,其中键为pin的编号;
- pin_group_tree:可选,每个引脚组都可以存储在此基数树中;
- num_groups:可选,可以在此处保存组数;
- pin_function_tree:可选,每个函数都可以存储在此基数树中;
- num_functions:可选,在此处可以保存函数数量;
- gpio_ranges:此 pin controller处理的 GPIO 范围列表,范围在运行时添加到此列表中;
- dev:pin controller的父设备;一般设置为平台设备的dev成员;
- owner:提供 pin controller的模块,用于引用计数;
- driver_data:驱动程序的私有数据;
- p:pinctrl_get(dev) 结果;
- hog_default:此设备占用的引脚的默认状态;
- hog_sleep:此设备占用的引脚的睡眠状态;
- mutex:在执行每个 pin controller特定操作时采取的互斥锁;
- device_root:此设备的 debugfs 根目录;
linux内核中,将pinctrl_dev链接成一个双向链表,链表头节点在drivers/pinctrl/core.c定义:
static LIST_HEAD(pinctrldev_list);
怎么构造出pinctrl_dev?开发人员需要提供一个指向相应struct pinctrl_desc的指针,然后调用pinctrl_register就可以。
3.2 struct pinctrl_desc
pin control core使用struct pinctrl_desc描述一个pin controller。该结构体结构体描述了一组可控制的引脚,通常与设备相关联,它描述了每个引脚的名称、编号、默认状态和其他属性,是用于适配不同芯片的一个通用结构。它的定义在 include/linux/pinctrl/pinctrl.h 文件,如下:
/** * 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 * @num_custom_params: Number of driver-specific custom parameters to be parsed * from the hardware description * @custom_params: List of driver_specific custom parameters to be parsed from * the hardware description * @custom_conf_items: Information how to print @params in debugfs, must be * the same size as the @custom_params, i.e. @num_custom_params */ struct pinctrl_desc { const char *name; const struct pinctrl_pin_desc *pins; unsigned int npins; const struct pinctrl_ops *pctlops; const struct pinmux_ops *pmxops; const struct pinconf_ops *confops; 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 };
其中部分参数含义如下:
- name:pin controller的名称;
- pins:描述该pin controller处理的所有引脚的引脚描述符数组;在pinctrl subsystem中,struct pinctrl_pin_desc用来描述一个可以控制的引脚,称之为引脚描述符;
- npins:数组中描述符的数量,通常是上面的pins字段的ARRAY_SIZE()宏;
- pctlops:引脚控制操作,用来获取某组引脚,解析设备树节点创建映射;
- pmxops:复用操作;
- confops:配置操作;
- owner:提供pin controller的模块,用于引用计数;
- num_custom_params:从硬件描述中解析出的特定于驱动程序的自定义参数的数量;
- custom_params:从硬件描述中解析出的特定于驱动程序的自定义参数列表;
- custom_conf_items:如何在debugfs中打印@params的信息,必须与@custom_params具有相同的大小,即@num_custom_params;
3.2.1 struct pinctrl_pin_desc
pinctrl subsystem要想管理好系统的pin资源,第一个要搞明白的问题就是:系统中到底有多少个pin?用软件语言来表述就是:要把系统中所有的pin描述出来,并建立索引。这由上面struct pinctrl_desc结构中pins和npins来完成。
对pin control core来说,它只关心系统中有多少个pin,并使用自然数为这些pin编号,后续的操作,都是以这些编号为操作对象。至于编号怎样和具体的pin对应上,完全是pin ctontroller driver自己的事情。
因此,pin ctontroller driver需要根据实际情况,将系统中所有的pin组织成一个struct pinctrl_pin_desc类型的数组,该类型的定义在 include/linux/pinctrl/pinctrl.h 文件,如下:
/** * 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; };
- number:ID,在全局引脚编号空间中唯一的引脚编号;
- name:这个引脚的名称;
- drv_data:驱动程序定义的每个引脚的私有数据,pinctrl 核心不会修改此数据;
number和name完全由driver自己决定,不过要遵循有利于代码编写、有利于理解等原则。另外,为了便于driver的编写,可以在drv_data中保存driver的私有数据结构(可以包含相关的寄存器偏移等信息)。
3.2.2 struct pinctrl_ops
在SoC系统中,有时需要将很多pin组合在一起,以实现特定的功能,例如SPI接口、I2C接口等。因此pin controller需要以group为单位,访问、控制多个pin,这就是pin group。
相应地,pin controller subsystem需要提供一些机制,来获取系统中到底有多少groups、每个groups包含哪些pins、等等。
因此,pinctrl core在struct pinctrl_ops中抽象出三个回调函数,用来获取pin groups相关信息;此外提供dt_node_to_map函数为设备树中pin controller中的子节点创建映射,即device_node转换为一系列的pinctrl_map。
该类型的定义在 include/linux/pinctrl/pinctrl.h文件,如下:
/** * struct pinctrl_ops - global pin control operations, to be implemented by * pin controller drivers. * @get_groups_count: Returns the count of total number of groups registered. * @get_group_name: return the group name of the pin group * @get_group_pins: return an array of pins corresponding to a certain * group selector @pins, and the size of the array in @num_pins * @pin_dbg_show: optional debugfs display hook that will provide per-device * info for a certain pin in debugfs * @dt_node_to_map: parse a device tree "pin configuration node", and create * mapping table entries for it. These are returned through the @map and * @num_maps output parameters. This function is optional, and may be * omitted for pinctrl drivers that do not support device tree. * @dt_free_map: free mapping table entries created via @dt_node_to_map. The * top-level @map pointer must be freed, along with any dynamically * allocated members of the mapping table entries themselves. This * function is optional, and may be omitted for pinctrl drivers that do * not support device tree. */ 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 group总数;
- get_group_name:返回pin group selector指定的分组名称;
- get_group_pins:返回与特定pin group选择器@pins对应的引脚数组,并将数组的大小放入@num_pins 中;
- pin_dbg_show:可选的 debugfs 显示钩子,提供特定引脚的每个设备的信息;
- dt_node_to_map:解析设备树 "引脚配置节点",并为其创建映射表条目。这些通过输出参数@map和@num_maps返回。这个函数是可选的,在不支持设备树的pinctrl驱动程序中可能会省略;
- dt_free_map:释放通过@dt_node_to_map创建的映射表条目。必须释放顶层@mapptr,以及映射表条目本身的任何动态分配成员。这个函数是可选的,在不支持设备树的pinctrl驱动程序中可能会省略;
3.2.3 struct pinconf_ops
介绍了pin ctntrol subsystem中的操作对象(pin or pin group)以及抽象方法。我们都知道SoC中的管脚有些属性可以配置,例如上拉、下拉、高阻、驱动能力等。
pinctrl subsystem使用struct pinconf_ops来抽象配置有关的操作,,定义在include/linux/pinctrl/pinconf.h文件,如下:
/** * struct pinconf_ops - pin config operations, to be implemented by * pin configuration capable drivers. * @is_generic: for pin controllers that want to use the generic interface, * this flag tells the framework that it's generic. * @pin_config_get: get the config of a certain pin, if the requested config * is not available on this controller this should return -ENOTSUPP * and if it is available but disabled it should return -EINVAL * @pin_config_set: configure an individual pin * @pin_config_group_get: get configurations for an entire pin group; should * return -ENOTSUPP and -EINVAL using the same rules as pin_config_get. * @pin_config_group_set: configure all pins in a group * @pin_config_dbg_show: optional debugfs display hook that will provide * per-device info for a certain pin in debugfs * @pin_config_group_dbg_show: optional debugfs display hook that will provide * per-device info for a certain group in debugfs * @pin_config_config_dbg_show: optional debugfs display hook that will decode * and display a driver's pin configuration parameter */ 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); 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); };
其中部分参数含义如下:
- is_generic:对于希望使用通用接口的引脚控制器,该标志告诉框架它是通用的;
- pin_config_get:获取某个引脚的配置。如果在此控制器上请求的配置不可用,则应返回 -ENOTSUPP;如果其可用但被禁用,则应返回 -EINVAL;
- pin_config_set:配置单个引脚;
- pin_config_group_get:获取整个引脚组的配置。应使用与 pin_config_get 相同的规则返回 -ENOTSUPP 和 -EINVAL;
- pin_config_group_set:配置引脚组中的所有引脚;
- pin_config_dbg_show:可选的debugfs显示钩子,提供特定引脚的每个设备的信息;
- pin_config_group_dbg_show:可选的debugfs显示钩子,提供特定引脚组的每个设备的信息;
- pin_config_config_dbg_show:可选的debugfs显示钩子,解码并显示驱动程序的引脚配置参数;
pinctrl subsystem并不关心configuration的具体内容是什么,它只提供pin configuration get/set的通用机制,至于get到的东西,以及set的东西,到底是什么,是pinctrl driver自己的事情。
3.2.4 struct pinmux_ops
为了照顾不同类型的产品、不同的应用场景,SoC中的很多管脚可以配置为不同的功能,例如GPE14和GPE14两个管脚,既可以当作普通的GPIO使用,又可以配置为I2C0的的SCL和SDA这称作管脚的复用(pin multiplexing,简称为pinmux)。
pinctrl subsystem使用struct pinmux_ops来抽象pinmux有关的操作,定义在include/linux/pinctrl/pinmux.h文件,如下:
/** * struct pinmux_ops - pinmux operations, to be implemented by pin controller * drivers that support pinmuxing * @request: called by the core to see if a certain pin can be made * available for muxing. This is called by the core to acquire the pins * before selecting any actual mux setting across a function. The driver * is allowed to answer "no" by returning a negative error code * @free: the reverse function of the request() callback, frees a pin after * being requested * @get_functions_count: returns number of selectable named functions available * in this pinmux driver * @get_function_name: return the function name of the muxing selector, * called by the core to figure out which mux setting it shall map a * certain device to * @get_function_groups: return an array of groups names (in turn * referencing pins) connected to a certain function selector. The group * name can be used with the generic @pinctrl_ops to retrieve the * actual pins affected. The applicable groups will be returned in * @groups and the number of groups in @num_groups * @set_mux: enable a certain muxing function with a certain pin group. The * driver does not need to figure out whether enabling this function * conflicts some other use of the pins in that group, such collisions * are handled by the pinmux subsystem. The @func_selector selects a * certain function whereas @group_selector selects a certain set of pins * to be used. On simple controllers the latter argument may be ignored * @gpio_request_enable: requests and enables GPIO on a certain pin. * Implement this only if you can mux every pin individually as GPIO. The * affected GPIO range is passed along with an offset(pin number) into that * specific GPIO range - function selectors and pin groups are orthogonal * to this, the core will however make sure the pins do not collide. * @gpio_disable_free: free up GPIO muxing on a certain pin, the reverse of * @gpio_request_enable * @gpio_set_direction: Since controllers may need different configurations * depending on whether the GPIO is configured as input or output, * a direction selector function may be implemented as a backing * to the GPIO controllers that need pin muxing. * @strict: do not allow simultaneous use of the same pin for GPIO and another * function. Check both gpio_owner and mux_owner strictly before approving * the pin request. */ 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; };
其中部分参数含义如下:
- request:由核心调用,以查看特定引脚是否可以用于引脚复用。在选择任何实际的复用设置之前,应由核心调用此函数来获取引脚。如果该驱动程序无法处理表述的引脚,则应返回负错误代码来拒绝请求;
- free:与 request() 回调的相反函数,在申请后释放引脚;
- get_functions_count:返回pin controller device支持的function的数目;
- get_function_name:给定一个function selector(index),获取指定function的name;
- get_function_groups:给定一个function selector(index),获取指定function的pin groups信息;
- set_mux:使用某个引脚组启用某个复用函数。驱动程序不需要弄清楚启用此函数是否与该组中其他引脚的使用冲突,这些冲突由引脚复用子系统处理。@func_selector 选择某个函数,而 @group_selector 选择要使用的某个引脚集。在简单的控制器上,后者可能被忽略;
- gpio_request_enable:在某个引脚上请求并启用GPIO。仅在可以将每个引脚单独复用为GPIO时实现。受影响的GPIO范围与特定GPIO范围内的偏移量(引脚编号)一起传递-功能选择器和引脚组是对此正交的,但核心将确保引脚不冲突;
- gpio_disable_free:在某个引脚上释放GPIO复用,即 @gpio_request_enable 的相反操作;
- gpio_set_direction:由于控制器可能需要根据GPIO配置为输入或输出而需要不同的配置,因此可以实现方向选择器函数作为需要引脚复用的GPIO控制器的支持。它用于设置GPIO的输入或输出方向;
- strict:不允许同时使用相同的引脚用于GPIO和其他函数。在批准引脚请求之前,应严格检查 gpio_owner 和 mux_owner;
那什么是function?
function是功能抽象,对应一个HW逻辑block。例如SPI0,虽然给定了具体的function name,我们并不能确定其使用的pins的情况。为了设计灵活,芯片内部的SPI0的功能可能引出到pin group { A8, A7, A6, A5 },也可能引出到另外一个pin group{ G4, G3, G2, G1 },但毫无疑问,这两个pin group不能同时active,毕竟芯片内部的SPI0的逻辑功能电路只有一个。 因此,只有给出function selector(所谓selector就是一个ID或者index)以及function的pin group selector才能进行function mux的设定。
3.2.5 数据结构关系图
下图绘制了pinctrl_desc、pinctrl_ops、pinmux_ops、pinconf_ops之间的关系:
四、client device
一个典型的device tree中的外设节点定义如下:
device-node-name { ....... pinctrl-names = "sleep", "active"; pinctrl-0 = <pin-config-0-a>; pinctrl-1 = <pin-config-1-a pin-config-1-b>; };
在内核启动阶段,设备节点一般会被转换为platform_device,或者其它结构体(比如i2c_client、spi_device),它们内部都会有一个struct device成员。
4.1 struct device
struct device结构体里有一个dev_pin_info结构体,用来保存设备的引脚状态信息:
struct device { struct kobject kobj; struct device *parent; struct device_private *p; ...... #ifdef CONFIG_PINCTRL struct dev_pin_info *pins; #endif ...... }
4.2 struct dev_pin_info
struct dev_pin_info是一个用于存储client device引脚状态信息的结构体,定义在include/linux/pinctrl/devinfo.h:文件:
/** * struct dev_pin_info - pin state container for devices * @p: pinctrl handle for the containing device * @default_state: the default state for the handle, if found * @init_state: the state at probe time, if found * @sleep_state: the state at suspend time, if found * @idle_state: the state at idle (runtime suspend) time, if found */ struct dev_pin_info { struct pinctrl *p; struct pinctrl_state *default_state; struct pinctrl_state *init_state; #ifdef CONFIG_PM struct pinctrl_state *sleep_state; struct pinctrl_state *idle_state; #endif };
该结构体中包含5个成员变量:
- p:指向pinctrl句柄的指针,用于保存该设备的所有状态信息;
- default_state:如果找到,则指向默认状态的指针,表示该设备上引脚的默认状态;
- init_state:如果找到,则指向探测时状态的指针,表示在探测期间初始化引脚的状态;
- sleep_state:如果在系统的电源管理模块(Power Management,简称 PM)中启用了与设备关联的引脚状态保持功能,则指向睡眠时状态的指针,表示在设备进入睡眠状态时退出的状态;
- idle_state:如果在 PM 模块中启用了运行时挂起功能,并且与设备关联的引脚状态与 idling 状态有关,则指向 idling 时状态的指针;
4.3 struct pinctrl
pinctrl subsytem抽象了struct pinctrl来描述一个client device的所有状态。其中又抽象了:
- struct pinctrl_state描述client device的某一种状态;在设备节点中由pinctrl-names属性指定的列表中某一个元素;
- struct pinctrl_setting描述client device处于某一种状态下其中一个"引脚配置";在设备节点中由pinctrl-n属性指定的列表中某一个元素;
内核使用struct pinctrl来表示client device的的所有状态。它在drivers/pinctrl/core.c定义:
/** * struct pinctrl - per-device pin control state holder * @node: global list node * @dev: the device using this pin control handle * @states: a list of states for this device * @state: the current state * @dt_maps: the mapping table chunks dynamically parsed from device tree for * this device, if any * @users: reference count */ struct pinctrl { struct list_head node; struct device *dev; struct list_head states; struct pinctrl_state *state; struct list_head dt_maps; struct kref users; };
其中部分参数含义如下:
- node:用于构建双向链表,系统中的所有client device的pin control state holder被添加到了全局链表pinctrl_list中;
- dev:该pin control state holder对应的client device;
- states:双向链表头,该client device的所有的状态被添加到这个链表中;
- state:当前的状态;
- dt_maps:双向链表头,用于保存从设备树动态解析出来的映射信息,数据成员为struct pinctrl_dt_map类型;
- users:引用计数;
系统中的每一个需要和pinctrl subsystem进行交互的client device在进行设定之前都需要首先获取这个句柄。而属于该设备的所有的状态都是挂入到一个链表中,链表头就是pin control state holder的states成员。关于client device设备节点中pinctrl属性信息如何被解析得到struct pinctrl,后面我们会介绍。
4.3.1 struct pinctrl_state
内核使用struct pinctrl_state来表示client device的状态。它在drivers/pinctrl/core.c定义:
/** * struct pinctrl_state - a pinctrl state for a device * @node: list node for struct pinctrl's @states field * @name: the name of this state * @settings: a list of settings for this state */ struct pinctrl_state { struct list_head node; const char *name; struct list_head settings; };
其中部分参数含义如下:
- node:用于构建双向链表,client device的状态被添加到pinctrl的states链表中;
- name:此状态的名称;
- settings:双向链表头,保存属于该状态的所有的settings;由于一个状态可以对应多个settings,所以这里使用链表来表示;
4.3.2 struct pinctrl_setting
一个状态包含若干个setting,所有的settings被挂入一个链表中,链表头就是struct pinctrl_setting的settings成员,定义如下:
struct pinctrl_setting { struct list_head node; enum pinctrl_map_type type; struct pinctrl_dev *pctldev; const char *dev_name; // 设备名称 union { struct pinctrl_setting_mux mux; // mux配置数据 struct pinctrl_setting_configs configs; // config配置数据 } data; };
当某个driver设定一个pin state的时候,pinctrl subsystem内部会遍历该state的settings链表,将一个一个的setting进行设定。这些settings有各种类型,定义如下:
enum pinctrl_map_type { PIN_MAP_TYPE_INVALID, PIN_MAP_TYPE_DUMMY_STATE, PIN_MAP_TYPE_MUX_GROUP, // 功能复用的 PIN_MAP_TYPE_CONFIGS_PIN, // 设定单一一个pin的电气特性 PIN_MAP_TYPE_CONFIGS_GROUP, // 设定单pin group的电气特性 };
struct pinctrl_setting_mux是 struct pinctrl_setting 中的一个联合体,用于描述PIN_MAP_TYPE_MUX_GROUP类型的映射项的具体数据。如下:
/** * struct pinctrl_setting_mux - setting data for MAP_TYPE_MUX_GROUP * @group: the group selector to program * @func: the function selector to program */ struct pinctrl_setting_mux { unsigned group; unsigned func; };
具体成员变量如下:
- group: 表示要配置的pin group selector;
- function: 表示要配置的pin function selector;
struct pinctrl_setting_configs 是 struct pinctrl_setting 中的一个联合体,用于描述 PIN_MAP_TYPE_CONFIGS_PIN 和 PIN_MAP_TYPE_CONFIGS_GROUP 两种类型的映射项的具体数据。如下:
/** * struct pinctrl_setting_configs - setting data for MAP_TYPE_CONFIGS_* * @group_or_pin: the group selector or pin ID to program * @configs: a pointer to an array of config parameters/values to program into * hardware. Each individual pin controller defines the format and meaning * of config parameters. * @num_configs: the number of entries in array @configs */ struct pinctrl_setting_configs { unsigned group_or_pin; unsigned long *configs; unsigned num_configs; };
具体成员变量如下:
- group_or_pin: 表示需要配置的pin编号或者pin group selector;
- configs: 表示指向一个数组的指针,该数组存储了一组配置参数或值,用于设置特定引脚或引脚组的硬件参数;
- num_configs: 需要写入的配置参数个数;
4.3.3 pinctrl_map
struct pinctrl_map它用于描述client device的映射配置,使用pin controller device的pinctrl_desc->ops->dt_node_to_map来处理设备树中的"引脚配置节点"。
例如 pinctrl-0 = <&uart0_xfer &uart0_cts &uart0_rts>,uart0_xfer 、uart0_cts 、uart0_rts节点均会被dt_node_to_map函数解析为一系列的pinctrl_map,并以数组指针形式返回。
定义在include/linux/pinctrl/machine.h:
/** * struct pinctrl_map - boards/machines shall provide this map for devices * @dev_name: the name of the device using this specific mapping, the name * must be the same as in your struct device*. If this name is set to the * same name as the pin controllers own dev_name(), the map entry will be * hogged by the driver itself upon registration * @name: the name of this specific map entry for the particular machine. * This is the parameter passed to pinmux_lookup_state() * @type: the type of mapping table entry * @ctrl_dev_name: the name of the device controlling this specific mapping, * the name must be the same as in your struct device*. This field is not * used for PIN_MAP_TYPE_DUMMY_STATE * @data: Data specific to the mapping type */ struct pinctrl_map { const char *dev_name; const char *name; enum pinctrl_map_type type; const char *ctrl_dev_name; union { struct pinctrl_map_mux mux; struct pinctrl_map_configs configs; } data; };
具体成员变量如下:
- dev_name:设备名称,需要与该client device对应的struct device中的名称一致;
- name:状态名称,是传递给 pinmux_lookup_state() 函数的参数;
- type:该映射项的类型,包括 PIN_MAP_TYPE_MUX_GROUP、PIN_MAP_TYPE_GPIO_MUX、PIN_MAP_TYPE_CONFIGS_PIN、PIN_MAP_TYPE_DUMMY_STATE 四种类型;
- ctrl_dev_name:设备名称,与该client device对应的struct device中的名称一致;
- data:该映射项的具体数据,包括mux和configs两种类型,分别表示复用选择和引脚配置;
struct pinctrl_map_mux是 struct pinctrl_map 中的一个联合体,用于描述PIN_MAP_TYPE_MUX_GROUP类型的映射项的具体数据。如下:
/** * struct pinctrl_map_mux - mapping table content for MAP_TYPE_MUX_GROUP * @group: the name of the group whose mux function is to be configured. This * field may be left NULL, and the first applicable group for the function * will be used. * @function: the mux function to select for the group */ struct pinctrl_map_mux { const char *group; const char *function; };
具体成员变量如下:
- group: 表示需要配置复用功能的pin group名称;
- function: 表示要选择的复用功能名称;
struct pinctrl_map_configs 是 struct pinctrl_map 中的一个联合体,用于描述 PIN_MAP_TYPE_CONFIGS_PIN 和 PIN_MAP_TYPE_CONFIGS_GROUP 两种类型的映射项的具体数据。如下:
/** * struct pinctrl_map_configs - mapping table content for MAP_TYPE_CONFIGS_* * @group_or_pin: the name of the pin or group whose configuration parameters * are to be configured. * @configs: a pointer to an array of config parameters/values to program into * hardware. Each individual pin controller defines the format and meaning * of config parameters. * @num_configs: the number of entries in array @configs */ struct pinctrl_map_configs { const char *group_or_pin; unsigned long *configs; unsigned num_configs; };
具体成员变量如下:
- group_or_pin: 表示需要配置的pin或者pin group名称;
- configs: 表示指向一个数组的指针,该数组存储了一组配置参数或值,用于设置特定引脚或引脚组的硬件参数;
- num_configs: 需要写入的配置参数个数;
4.3.4 struct pinctrl_dt_map
在前面我们说过:例如 pinctrl-0 = <&uart0_xfer &uart0_cts &uart0_rts>,uart0_xfer 、uart0_cts 、uart0_rts节点均会被dt_node_to_map函数解析为pinctrl_map,并以数组指针形式返回。
而struct pinctrl_dt_map就是一个用于存储从设备树解析出来的映射表数据的结构体,其成员map指向这个pinctrl_map数组。该类型定义在drivers/pinctrl/devicetree.c:
/** * struct pinctrl_dt_map - mapping table chunk parsed from device tree * @node: list node for struct pinctrl's @dt_maps field * @pctldev: the pin controller that allocated this struct, and will free it * @maps: the mapping table entries */ struct pinctrl_dt_map { struct list_head node; struct pinctrl_dev *pctldev; struct pinctrl_map *map; unsigned num_maps; };
该结构体中包含4个成员变量:
- node:用于构建双向链表,用于将当前pinctrl_dt_map添加到pinctr的dt_maps链表;
- pctldev:指向分配该映射表的pin controller device,并在需要时对其进行释放;
- map: 指向实际的映射表数据;
- num_maps:表示映射表中的条目数量;
五、注册pin controller device
pinctrl driver的编写,实际上就是去根据SoC的pin controller信息去构建pinctr_desc,然后去根据SoC pin controller寄存器去编写pinctr_desc的操作函数,最后将其注册到内核即可。
下图显示了注册pin controller之后,pinctrl_dev、pin_desc、pinctrl_desc等数据结构之间的关系:
5.1 注册pinctrl
将pin controller device注册到内核,一般通过pinctrl_register函数来完成。该函数比较复杂,函数调用流程如下:
pinctrl_register(pctldesc, dev, driver_data); pinctrl_init_controller(pctldesc, dev, driver_data); pctldev = kzalloc(sizeof(*pctldev), GFP_KERNEL);//分配一个pctldev内存,这是pinctrl core核心数据结构 pctldev->owner = pctldesc->owner; pctldev->desc = pctldesc; pctldev->driver_data = driver_data; pinctrl_register_pins(pctldev, pctldesc->pins, pctldesc->npins);//注册一组pin pinctrl_register_one_pin(pctldev, &pins[i]);//注册每一个pin pindesc = kzalloc(sizeof(*pindesc), GFP_KERNEL); pindesc->pctldev = pctldev; pindesc->name = pin->name; pindesc->drv_data = pin->drv_data; radix_tree_insert(&pctldev->pin_desc_tree, pin->number, pindesc);//保存在pctldev->pin_desc_tree pinctrl_enable(pctldev); list_add_tail(&pctldev->node, &pinctrldev_list); //将这个pctldev保存在链表pinctrldev_list
pinctrl_register函数定义在drivers/pinctrl/core.c文件中:
/** * pinctrl_register() - register a pin controller device * @pctldesc: descriptor for this pin controller * @dev: parent device for this pin controller * @driver_data: private pin controller data for this pin controller * * Note that pinctrl_register() is known to have problems as the pin * controller driver functions are called before the driver has a * struct pinctrl_dev handle. To avoid issues later on, please use the * new pinctrl_register_and_init() below instead. */ struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc, struct device *dev, void *driver_data) { struct pinctrl_dev *pctldev; int error; pctldev = pinctrl_init_controller(pctldesc, dev, driver_data); // 初始化pin controller设备 if (IS_ERR(pctldev)) return pctldev; error = pinctrl_enable(pctldev); // 启用设备 if (error) return ERR_PTR(error); return pctldev; }
函数接收三个参数:
- pctldesc:pin controller的描述符;
- dev:pin controller的父设备;一般设置为平台设备的dev成员;
- driver_data:pin controller的私有数据,由控制器的驱动程序提供;
该函数内部调用了两个函数:
- 使用pinctrl_init_controller函数初始化pin controller device;并返回一个struct pinctrl_dev结构体的指针;
- 使用pinctrl_enable函数启用化pin controller device;
如果成功,返回一个指向新创建的struct pinctrl_dev结构体的指针,表示已成功注册了这个pin controller device,否则返回指向错误代码的指针ERR_PTR。
注意:pinctrl_register函数是用于注册pin controller device的。然而,由于其存在某些问题,因此更推荐使用新的pinctrl_register_and_init函数来注册pin controller device。
5.1.1 pinctrl_init_controller
pinctrl_init_controller函数用于初始化一个pin controller device的。函数定义在drivers/pinctrl/core.c文件中:
/** * pinctrl_init_controller() - init a pin controller device * @pctldesc: descriptor for this pin controller * @dev: parent device for this pin controller * @driver_data: private pin controller data for this pin controller */ static struct pinctrl_dev * pinctrl_init_controller(struct pinctrl_desc *pctldesc, struct device *dev, void *driver_data) { struct pinctrl_dev *pctldev; int ret; if (!pctldesc) return ERR_PTR(-EINVAL); if (!pctldesc->name) return ERR_PTR(-EINVAL); pctldev = kzalloc(sizeof(*pctldev), GFP_KERNEL); // 动态分配pinctrl_dev if (!pctldev) return ERR_PTR(-ENOMEM); /* Initialize pin control device struct */ pctldev->owner = pctldesc->owner; pctldev->desc = pctldesc; pctldev->driver_data = driver_data; INIT_RADIX_TREE(&pctldev->pin_desc_tree, GFP_KERNEL); #ifdef CONFIG_GENERIC_PINCTRL_GROUPS INIT_RADIX_TREE(&pctldev->pin_group_tree, GFP_KERNEL); #endif #ifdef CONFIG_GENERIC_PINMUX_FUNCTIONS INIT_RADIX_TREE(&pctldev->pin_function_tree, GFP_KERNEL); #endif INIT_LIST_HEAD(&pctldev->gpio_ranges); INIT_LIST_HEAD(&pctldev->node); pctldev->dev = dev; mutex_init(&pctldev->mutex); /* check core ops for sanity */ ret = pinctrl_check_ops(pctldev); // 检查核心操作 if (ret) { dev_err(dev, "pinctrl ops lacks necessary functions\n"); goto out_err; } /* If we're implementing pinmuxing, check the ops for sanity */ if (pctldesc->pmxops) { ret = pinmux_check_ops(pctldev); if (ret) goto out_err; } /* If we're implementing pinconfig, check the ops for sanity */ if (pctldesc->confops) { ret = pinconf_check_ops(pctldev); if (ret) goto out_err; } /* Register all the pins */ dev_dbg(dev, "try to register %d pins ...\n", pctldesc->npins); ret = pinctrl_register_pins(pctldev, pctldesc->pins, pctldesc->npins); // 注册所有的pin if (ret) { // 如果注册失败 dev_err(dev, "error during pin registration\n"); pinctrl_free_pindescs(pctldev, pctldesc->pins, pctldesc->npins); goto out_err; } return pctldev; out_err: mutex_destroy(&pctldev->mutex); kfree(pctldev); return ERR_PTR(ret); }
函数接受三个参数:
- pctldesc:pin controller描述符,描述了这个pin controller device的特定属性和行为;
- dev:pin controller的父设备;一般设置为平台设备的dev成员;
- driver_data:控制器的私有数据;
具体流程如下:
- 首先为pin controller device动态分配一个struct pinctrl_dev结构体;
- 然后初始化pinctrl_dev结构体成员;
- 设置desc,设置为传入的pin controller描述符指针;
- 设置driver_data:设置为传入私有数据driver_data;
- 初始化了一些用于管理GPIO和pin资源的基数树(radix tree)等数据结构,比如pin_desc_tree、pin_group_tree、pin_function_tree;
- 检查pin controller device的核心操作,以确保该设备的操作能够正常工作;比如pctlops、pmxops、confops;
- 在初始化完毕后,下一步是注册所有的pin,即调用pinctrl_register_pins函数来完成注册过程;
- 如果没有出现错误,最终将返回pinctrl_dev的指针,表示成功创建一个pin controller device。若出现错误,会将之前分配的内存释放,返回相应的错误码;
5.1.2 pinctrl_enable
pinctrl_init_controller函数用于启用一个pin controller device。函数定义在drivers/pinctrl/core.c文件中:
int pinctrl_enable(struct pinctrl_dev *pctldev) { int error; error = pinctrl_claim_hogs(pctldev); if (error) { dev_err(pctldev->dev, "could not claim hogs: %i\n", error); mutex_destroy(&pctldev->mutex); kfree(pctldev); return error; } mutex_lock(&pinctrldev_list_mutex); list_add_tail(&pctldev->node, &pinctrldev_list); mutex_unlock(&pinctrldev_list_mutex); pinctrl_init_device_debugfs(pctldev); return 0; }
它接收一个struct pinctrl_dev指针作为参数,该指针代表了需要被启用的pin controller device。
具体流程如下:
- 调用 pinctrl_claim_hogs 函数来获取所有 hog pin 并配置它们。如果该函数返回错误,则打印一条错误信息,并释放 pctldev 以及相关资源;
- 在锁定 pinctrldev_list_mutex 后,使用 list_add_tail 将 pctldev 添加到 pinctrldev_list 队列的尾部。并在解锁 pinctrldev_list_mutex 前完成添加操作;
- 调用 pinctrl_init_device_debugfs 函数初始化debugfs接口,用于调试和诊断有关pinctrl设备的问题;
- 当执行成功时,返回 0;
5.2 pinctrl_register_pins
pinctrl_register_pins函数实现了为一个pin controller device注册一组pins的功能。函数定义在drivers/pinctrl/core.c文件中:
static int pinctrl_register_pins(struct pinctrl_dev *pctldev, const struct pinctrl_pin_desc *pins, unsigned num_descs) { unsigned i; int ret = 0; for (i = 0; i < num_descs; i++) { // 循环遍历,注册每一个pin ret = pinctrl_register_one_pin(pctldev, &pins[i]); if (ret) return ret; } return 0; }
函数接收3个参数:
- pctldev:要注册pins的pin controller device;
- pins:一个指向pinctrl_pin_desc结构体数组的指针,描述了要注册的所有pins的信息;
- num_descs:要注册的pins的数量;
该函数循环遍历所有要注册的pins,对于每个pin,使用pinctrl_register_one_pin函数对其进行单独注册,如果注册成功则继续处理下一个pin,否则返回错误码。所有的pins都成功注册后,返回0表示注册过程全部成功。
5.2.1 pinctrl_register_one_pin
pinctrl_register_pins函数实现了为一个pin controller device注册一个pin的功能。函数定义在drivers/pinctrl/core.c文件中:
亲爱的读者和支持者们,自动博客加入了打赏功能,陆陆续续收到了各位老铁的打赏。在此,我想由衷地感谢每一位对我们博客的支持和打赏。你们的慷慨与支持,是我们前行的动力与源泉。
日期 | 姓名 | 金额 |
---|---|---|
2023-09-06 | *源 | 19 |
2023-09-11 | *朝科 | 88 |
2023-09-21 | *号 | 5 |
2023-09-16 | *真 | 60 |
2023-10-26 | *通 | 9.9 |
2023-11-04 | *慎 | 0.66 |
2023-11-24 | *恩 | 0.01 |
2023-12-30 | I*B | 1 |
2024-01-28 | *兴 | 20 |
2024-02-01 | QYing | 20 |
2024-02-11 | *督 | 6 |
2024-02-18 | 一*x | 1 |
2024-02-20 | c*l | 18.88 |
2024-01-01 | *I | 5 |
2024-04-08 | *程 | 150 |
2024-04-18 | *超 | 20 |
2024-04-26 | .*V | 30 |
2024-05-08 | D*W | 5 |
2024-05-29 | *辉 | 20 |
2024-05-30 | *雄 | 10 |
2024-06-08 | *: | 10 |
2024-06-23 | 小狮子 | 666 |
2024-06-28 | *s | 6.66 |
2024-06-29 | *炼 | 1 |
2024-06-30 | *! | 1 |
2024-07-08 | *方 | 20 |
2024-07-18 | A*1 | 6.66 |
2024-07-31 | *北 | 12 |
2024-08-13 | *基 | 1 |
2024-08-23 | n*s | 2 |
2024-09-02 | *源 | 50 |
2024-09-04 | *J | 2 |
2024-09-06 | *强 | 8.8 |
2024-09-09 | *波 | 1 |
2024-09-10 | *口 | 1 |
2024-09-10 | *波 | 1 |
2024-09-12 | *波 | 10 |
2024-09-18 | *明 | 1.68 |
2024-09-26 | B*h | 10 |
2024-09-30 | 岁 | 10 |
2024-10-02 | M*i | 1 |
2024-10-14 | *朋 | 10 |
2024-10-22 | *海 | 10 |
2024-10-23 | *南 | 10 |
2024-10-26 | *节 | 6.66 |
2024-10-27 | *o | 5 |
2024-10-28 | W*F | 6.66 |
2024-10-29 | R*n | 6.66 |
2024-11-02 | *球 | 6 |
2024-11-021 | *鑫 | 6.66 |
2024-11-25 | *沙 | 5 |
2024-11-29 | C*n | 2.88 |

【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了