原理:pinctrl基础

原有GPIO配置框架:
之前所有的gpio操作都是通过gpiolib来实现,常用的api包括:
staticinline int gpio_request(unsigned gpio, const char *label);
staticinline int gpio_direction_input(unsigned gpio);
staticinline int gpio_direction_output(unsigned gpio, int value);
staticinline void gpio_set_value(unsigned gpio, int value);
staticinline void gpio_free(unsigned gpio);
在硬件设计确定了某个设备需要使用哪些gpio之后,软件需要做的是(以msm8916平台tp的中断为例):
1)在msm8916-cdp.dsi中定义使用哪个gpio
i2c@f9924000{
goodix@5d{
compatible= "goodix,gt9xx";
reg= <0x5d>;
interrupt-parent= <&msmgpio>;
interrupts= <13 0x2>;
interrupt-gpios= <&msm_gpio 13 0x00>;
};
}
2)在board-8916-gpiomux.c中定义gpio的suspend和active状态
staticstruct gpiomux_setting atmel_int_act_cfg = {
.func =GPIOMUX_FUNC_GPIO,
.drv = GPIOMUX_DRV_2MA,
.pull = GPIOMUX_PULL_UP,
};
staticstruct gpiomux_setting atmel_int_sus_cfg = {
.func =GPIOMUX_FUNC_GPIO,
.drv = GPIOMUX_DRV_2MA,
.pull =GPIOMUX_PULL_NONE,
};
staticstruct msm_gpiomux_config msm_touch_configs[] __initdata = {
.gpio = 13,
.settings = {
[GPIOMUX_ACTIVE] =&atmel_int_act_cfg,
[GPIOMUX_SUSPENDED]= &atmel_int_sus_cfg,
},
},
PinControl 框架
Gpiolib方式的缺点在于:当同一套代码对应多个board设计时,需要在board--gpiomux.c文件中加宏进行区分。如在同一个分支上支持3个项目,在board-msm8974-gpiomux.c文件中添加了很多宏控。
pinctrl方式可以避免代码中的这种冗余代码,它将board--gpiomux.c文件中的配置信息移到-pinctrl.dtsi;这样,针对不同project的board设计,分别在各自project的-pinctrl.dtsi中定义各自的gpio配置信息。
Pinctrlsubsystem 分为3部分:Pinctrl core、Pinmux和Pinconf。
pinctrlcore是pincontrol子系统的核心,提供了和devicedriver交互的API;
pinmux用于实现pin的复用;
pinconf用于实现pin的配置,如输入/输出、pulldown/pull up、driverstrength等;另外还提供了用于debug的接口。
与gpio子系统的交互
虽然pinctrl提供了pinctrl_request_gpio()这样的API,但在代码中不可以直接调用pinctrl_request_gpio(),在该函数的定义处也有说明,如下:
“*This function should *ONLY* be used from gpiolib-based GPIO drivers,
*as part of their gpio_request() semantics, platforms and individualdrivers
*shall *NOT* request GPIO pins to be muxed in.”
当设备驱动申请一个gpio时,仍然需要调用gpio_request(),这里会调用pinctrl_request_gpio()。调用过程如下:
gpio_request()
gpiod_request()
chip->request(chip,gpio_chip_hwgpio(desc));
在pinctrl_msm.c中,重新定义了chip->request()。
msm_pinctrl_probe()
msm_register_gpiochip()
gc->request= msm_pinctrl_request_gpio;
这里msm_pinctrl_request_gpio()会调pinctrl_request_gpio();
同样地,对于pinctrl_free_gpio()、pinctrl_gpio_direction_input()和pinctrl_gpio_direction_output()也有类似说明。
因此在clientdevice驱动中,申请和释放gpio仍然要调gpio_request()、gpio_free();设置gpio为input/output仍然要调gpio_direction_input()和gpio_direction_output()。
Pinctrl注册
全文以msm8916平台为例进行分析。
当tlmm加载时,msm_tlmm_v4_probe()最后会调msm_pinctrl_probe(),其中会将pinctrl.dtsi中定义的pinctrlinfo解析出来,并且重新定义chip->request()、chip->free()等函数。
具体的调用关系如下图所示:
postcore_initcall(msm_tlmm_v4_drv_register); //Pinctrl-msm-tlmm-v4.c
msm_tlmm_v4_probe //匹配pinctrl.dtsi定义的compatible
msm_pinctrl_probe //pinctrl_msm.c
msm_pinctrl_get_drvdata(dd,pdev); //解析pinctrl.dtsi,保存到dd
msm_pinctrl_dt_parse_pintype(node,dd);
msm_pinctrl_dt_parse_pins(node,dd);
msm_register_gpiochip(dd); //定义gpio_request()、gpio_free()
gc->request= msm_pinctrl_request_gpio;
gc->free= msm_pinctrl_free_gpio;
msm_register_pinctrl(dd);
dd->pctl_dev= pinctrl_register(ctrl_desc, dd->dev, dd);
Pinstates
一个pinstate对应对pin脚的一种配置,一个pin脚可以配置多个状态,对状态的个数也没有限制。
state的定义和电源管理关系比较紧密,例如当设备active的时候,我们需要pincontroller将相关的一组pin设定为具体的设备功能,而当设备进入sleep状态的时候,需要pincontroller将相关的一组pin设定为普通GPIO,并精确的控制GPIO状态以便节省系统的功耗。
Pinctrl-state.h中给出了常用的3种状态:
    default
default状态表示设备处于active时的状态,一般在设备驱动的.resume中配置,另外在启动时也会配置pin脚为default状态。
    idle
idle状态表示系统处于idle时需要配置的pin脚状态,此时系统并没有进入深度休眠。
    sleep
sleep状态表示系统处于深度休眠时的pin脚状态,一般在设备驱动的.suspend中配置。
当然我们也可以定义任意形式的state,如“on”、“off”等。
goodix@5d{
compatible= "goodix,gt9xx";
reg= <0x5d>;
pinctrl-names= "gt9xx_int_active", "gt9xx_int_suspend";
pinctrl-0= <&gt9xx_int_active>;
pinctrl-1= <&gt9xx_int_sleep>;
interrupt-parent= <&msm_gpio>;
interrupts= <13 0x2>;
……
}
pinctrl-names定义了clientdevice用到的state列表。state有两种标识,一种就是pinctrl-names定义的字符串列表,另外一种就是ID。ID从0开始,依次加一。根据例子中的定义,stateID等于0(名字是"gt9xx_int_active")的state对应pinctrl-0属性,stateID等于1(名字是"gt9xx_int_suspend")的state对应pinctrl-1属性。
pinctrl-x是一个句柄(phandle)列表,每个句柄指向一个pinconfiguration。
Boot时配置default状态
如果pin只定义了default状态,那么在设备驱动中不需要再对该pin作处理,因为在启动时会自动设为default状态。
在加载驱动模块时,如果驱动和设备匹配,最终就会调到driver定义的probe函数。在这个过程中,如果使能了pinctrl,而且定义了pin的default状态,就会配置pin脚为该状态。
具体代码流程如下:
driver_probe_device(structdevice_driver *drv, struct device *dev)
really_probe(dev,drv);
pinctrl_bind_pins(dev);
if(dev->bus->probe) {
ret= dev->bus->probe(dev);
}else if (drv->probe) {
ret= drv->probe(dev);
}
pinctrl_bind_pins(dev)的调用过程如下:
pinctrl_bind_pins(dev);
dev->pins->p= devm_pinctrl_get(dev);
dev->pins->default_state= pinctrl_lookup_state(dev->pins->p,
PINCTRL_STATE_DEFAULT);
pinctrl_select_state(dev->pins->p,dev->pins->default_state);
对于不使用pinctrl的平台,pinctrl_bind_pins(dev)直接返回0;
Pingroups
SOC上需要同时配置一组gpio来支持某些功能,如I2C、SPI、UART、SDC等,在-pinctrl.dtsi中将这些pin定义为一个group。
配置统一的情况
一组gpio在各个状态下的配置都相同,这种配置比较常见也比较简单。如i2c,在active和suspend状态下,SDA和SCL的配置都相同:active状态下都是配置为8mA上拉,suspend状态下都配置为2mA和nopull。
首先在msm8916_pinctrl.c中定义suspend和active状态下的pin脚配置信息,如下:
pmx_i2c_0{

qcom,pins= <&gp 7>, <&gp 6>; //使用gpio_6和gpio_7
qcom,num-grp-pins= <2>; //共两个gpio
qcom,pin-func= <3>; //复用功能为i2c
label= "pmx_i2c_0"; //表示同一组
i2c_0_active:i2c_0_active {
drive-strength= <8>;
bias-pull-up;
};
i2c_0_sleep:i2c_0_sleep {
drive-strength= <2>;
bias-disable;
};
};
然后在msm8916.dtsi中增加pinctrlinfo的引用:
i2c_0:i2c@78b6000 {
compatible= "qcom,i2c-msm-v2";
reg-names= "qup_phys_addr", "bam_phys_addr";
reg= <0x78b6000 0x600>,
<0x7884000 0x23000>;
interrupt-names= "qup_irq", "bam_irq";
interrupts= <0 96 0>, <0 238 0>;
clocks= <&clock_gcc clk_gcc_blsp1_ahb_clk>,
<&clock_gcc clk_gcc_blsp1_qup2_i2c_apps_clk>;
clock-names= "iface_clk", "core_clk";
qcom,clk-freq-out= <100000>;
qcom,clk-freq-in = <19200000>;
pinctrl-names= "i2c_active", "i2c_sleep";
pinctrl-0= <&i2c_0_active>;
pinctrl-1= <&i2c_0_sleep>;
qcom,master-id= <86>;
};
配置不统一的情况
如SDC,每个pin脚的active和sleep状态配置各不相同,需要分开设置。
1)在msm8916_pinctrl.c中分别定义各个pin的suspend和active状态下的配置信息,如下:

sdc:sdc {
qcom,pin-type-sdc;

qcom,num-pins= <6>;



#qcom,pin-cells= <1>;
};
pmx_sdc1_clk{
qcom,pins= <&sdc 0>;
qcom,num-grp-pins= <1>;
label= "sdc1-clk";
sdc1_clk_on:clk_on {
bias-disable;
drive-strength= <16>;
};
sdc1_clk_off:clk_off {
bias-disable;
drive-strength= <2>;
};
};
pmx_sdc1_cmd{
qcom,pins= <&sdc 1>;
qcom,num-grp-pins= <1>;
label= "sdc1-cmd";
sdc1_cmd_on:cmd_on {
bias-pull-up;
drive-strength= <10>;
};
sdc1_cmd_off:cmd_off {
bias-pull-up;
drive-strength= <2>;
};
};
pmx_sdc1_data{
qcom,pins= <&sdc 2>;
qcom,num-grp-pins= <1>;
label= "sdc1-data";
sdc1_data_on:data_on {
bias-pull-up;
drive-strength= <10>;
};
sdc1_data_off:data_off {
bias-pull-up;
drive-strength= <2>;
};
};
2)msm8916-cdp.dtsi中作出相应修改:
&sdhc_1{
vdd-supply= <&pm8916_l8>;
qcom,vdd-voltage-level= <2900000 2900000>;
qcom,vdd-current-level= <200 400000>;
vdd-io-supply= <&pm8916_l5>;
qcom,vdd-io-always-on;
qcom,vdd-io-lpm-sup;
qcom,vdd-io-voltage-level= <1800000 1800000>;
qcom,vdd-io-current-level= <200 60000>;
//qcom,pad-pull-on= <0x0 0x3 0x3>;
//qcom,pad-pull-off= <0x0 0x3 0x3>;
//qcom,pad-drv-on= <0x4 0x4 0x4>;
//qcom,pad-drv-off= <0x0 0x0 0x0>;
pinctrl-names= "active", "sleep";
pinctrl-0= <&sdc1_clk_on &sdc1_cmd_on &sdc1_data_on>;
pinctrl-1= <&sdc1_clk_off &sdc1_cmd_off &sdc1_data_off>;
qcom,nonremovable;
status= "ok";
};
对sdhc_2也是同样的配置。
另外还有一种情况,对于一组gpio,在不同state下pin_func定义不同的情况,如wifi,在active状态设置为wifi功能,在suspend状态下设置为普通gpio。
pinctrl@fd511000{
...
pmx-wcnss-5wire-active{
qcom,pins= <&gp 40>, <&gp 41>, <&gp 42>, <&gp43>.
<&gp44>;
qcom,pin-func= <1>;
qcom,num-grp-pins= <5>;
label= "wcnss-5wire-active";
wcnss-5wire-active:wcnss-active {
drive-strength= <6>; / * 6MA */
bias-pull-up;
};
};
pmx-wcnss-5wire-suspend{
qcom,pins= <&gp 40>, <&gp 41>, <&gp 42>, <&gp43>.
<&gp44>;
qcom,pin-func= <0>;
qcom,num-grp-pins= <5>;
label= "wcnss-5wire-suspend";
wcnss-5wire-sleep:wcnss-sleep {
drive-strength= <6>; / * 6MA */
bias-pull-down;
};
};
};
PinctrlAPI
structpinctrl *devm_pinctrl_get(struct device *dev);
获取该device对应的pinctrlhandler。
structpinctrl_state *pinctrl_lookup_state(struct pinctrl *p, const char*name);
查找name指定的pinctrlstate。
intpinctrl_select_state(struct pinctrl *p, struct pinctrl_state *state);
配置pin脚为指定的state。
Usecase
下面举例配置tp的中断脚。
1)在msm8916-pinctrl.dtsi中定义pinctrlinfo:
&soc{
tlmm_pinmux:pinctrl@1000000
gt9xx_int_pin{
qcom,pins= <&gp 13>;
qcom,num-grp-pins= <1>;
qcom,pin-func= <0>;
label= "gt9xx_int_pin";

gt9xx_int_active:active {
drive-strength= <2>;    
bias-pull-up;
};

gt9xx_int_sleep:sleep {
drive-strength= <2>;
bias-disable;
};
};
}
2)在msm8916-cdp.dtsi中tp的节点中添加引用:
goodix@5d{
compatible= "goodix,gt9xx";
reg= <0x5d>;
pinctrl-names= "gt9xx_int_active", "gt9xx_int_suspend";
pinctrl-0= <&gt9xx_int_active>;
pinctrl-1= <&gt9xx_int_sleep>;
interrupt-parent= <&msm_gpio>;
interrupts= <13 0x2>;
……
}
3)在tp驱动中添加配置。
a.定义pinctrl_info:
#defineGOODIX_PINCTRL_STATE_SLEEP "gt9xx_int_suspend"
#defineGOODIX_PINCTRL_STATE_DEFAULT "gt9xx_int_active"
structgtp_pinctrl_info{
structpinctrl *pinctrl;
structpinctrl_state *gpio_state_active;
structpinctrl_state *gpio_state_suspend;
};
staticstruct gtp_pinctrl_info gt9xx_pctrl;
staticint gtp_pinctrl_init(struct device *dev)
{
gt9xx_pctrl.pinctrl= devm_pinctrl_get(dev);
if(IS_ERR_OR_NULL(gt9xx_pctrl.pinctrl)) {
pr_err("%s:%dGetting pinctrl handle failed\n",
__func__,__LINE__);
return-EINVAL;
}
gt9xx_pctrl.gpio_state_active= pinctrl_lookup_state(
gt9xx_pctrl.pinctrl,
GOODIX_PINCTRL_STATE_DEFAULT);
if(IS_ERR_OR_NULL(gt9xx_pctrl.gpio_state_active)) {
pr_err("%s:�ailed to get the active state pinctrl handle\n",
__func__,__LINE__);
return-EINVAL;
}
gt9xx_pctrl.gpio_state_suspend= pinctrl_lookup_state(
gt9xx_pctrl.pinctrl,
GOODIX_PINCTRL_STATE_SLEEP);
if(IS_ERR_OR_NULL(gt9xx_pctrl.gpio_state_suspend)) {
pr_err("%s:�ailed to get the suspend state pinctrl handle\n",
__func__,__LINE__);
return-EINVAL;
}
return0;
}
b.在probe函数中初始化pinctrl_info,并设置state:
staticint goodix_ts_probe(struct i2c_client *client, const structi2c_device_id *id)
{
goodix_parse_dt(&client->dev,pdata);
gtp_request_io_port(ts);
gtp_pinctrl_init(&ts->client->dev);
pinctrl_select_state(gt9xx_pctrl.pinctrl,gt9xx_pctrl.gpio_state_active);
……
}
c.在suspend()和resume()中分别设置为activestate和suspendstate:
staticint goodix_ts_suspend(struct device *dev)
{
structgoodix_ts_data *ts = dev_get_drvdata(dev);
intret = 0, i;
ret= pinctrl_select_state(gt9xx_pctrl.pinctrl,
gt9xx_pctrl.gpio_state_suspend);
if(ret)
pr_err("%s:�annot set pin to suspend state",
__func__,__LINE__);
……
if(ts->use_irq)
gtp_irq_disable(ts);
……
returnret;
}
staticint goodix_ts_resume(struct device *dev)
{
structgoodix_ts_data *ts = dev_get_drvdata(dev);
intret = 0;
ret= pinctrl_select_state(gt9xx_pctrl.pinctrl,
gt9xx_pctrl.gpio_state_active);
if(ret)
pr_err("%s:�annot set pin to suspend state",
__func__,__LINE__);
……
if(ts->use_irq)
gtp_irq_enable(ts);
……
returnret;
}
<think>嗯,用户想深入了解Linux驱动开发中的pinctrl子系统,这属于嵌入式Linux的核心内容。从用户提问的精准度看,应该是有一定驱动开发基础的技术人员,可能正在调试板级引脚配置。 用户提供的引用材料很全面,覆盖了pinctrl的架构设计、设备树集成、GPIO子系统联动等关键点。特别注意到引用[3]详细描述了pinctrl_dev结构体,这是理解子系统内部机制的重要突破口。用户可能最关心的是如何在实际驱动中正确应用pinctrl,而不仅是理论概念。 需要重点解释清楚三个层次:首先是子系统存在的必要性——为什么裸机寄存器操作被淘汰(如引用[5]对比的STM32/IMX6ULL案例);其次是设备树配置的实践细节(引用[1][2]强调的设备树依赖);最后是驱动代码中的API调用时机(引用[4]的LED控制示例很典型)。 用户可能隐含的需求是解决实际引脚冲突问题,比如某个GPIO无法正确复用为I2C功能。因此回复中要突出“状态切换”这个易错点,通过休眠/唤醒的状态机示例说明配置流程。对于结构体字段这类底层细节,采用表格对比更直观,但需控制深度避免信息过载。 最后生成的问题要引导用户向具体场景延伸:比如针对功耗优化场景询问pinctrl的省电配置,或者结合GPIO子系统讨论中断处理。这样既能检验理解程度,又切合实际开发需求。</think>好的,我们来详细解析 Linux 驱动开发中的 `pinctrl` 子系统。 ## 1. 为什么需要 `pinctrl` 子系统? 在现代复杂的系统级芯片(SoC)上,一个物理引脚(Pin)通常具有多种功能(复用功能)。例如,一个引脚可能可以被配置为: * 通用输入/输出引脚(GPIO) * I²C 总线的时钟线(SCL)或数据线(SDA) * SPI 总线的片选(CS)、时钟(SCK)、数据输入(MISO)或数据输出(MOSI) * UART 的发送(TX)或接收(RX) * PWM 输出 * 等等 同时,每个引脚可能还需要配置其电气特性,如上拉/下拉电阻、驱动强度(输出电流能力)、施密特触发器使能、输入/输出模式(开漏、推挽)等。 在 `pinctrl` 子系统出现之前,这些配置通常分散在板级支持包(BSP)代码、具体驱动代码或 `arch/` 目录下的平台相关代码中。这导致了: 1. **代码重复:** 不同驱动需要重复实现引脚配置逻辑。 2. **维护困难:** 引脚配置与具体驱动和平台代码耦合紧密,改动困难。 3. **缺乏抽象:** 没有统一的接口管理引脚复用和配置。 4. **板级定制麻烦:** 为不同开发板适配引脚配置需要修改多处代码。 `pinctrl` 子系统的引入就是为了**统一管理 SoC 引脚的复用(Multiplexing)和配置(Configuration)**,提供一套标准的、与具体硬件无关的接口给内核其他部分(尤其是设备驱动)使用,并将引脚配置信息从驱动代码中剥离出来,放入设备树(Device Tree),极大地提高了代码的可重用性和可维护性[^1][^2][^5]。 ## 2. `pinctrl` 子系统核心概念 理解 `pinctrl` 子系统需要掌握几个关键概念: 1. **Pin Controller Device (引脚控制器设备):** * 这是物理上控制 SoC 上引脚复用和配置的硬件模块(通常是一个寄存器块)。 * 在设备树中,它表现为一个节点(例如 `&iomuxc` 或 `&pinctrl`),描述了这个控制器所能控制的所有引脚以及这些引脚可用的功能组(Pin Groups)和状态(Pin States)。 * 一个 SoC 可能包含多个 Pin Controller(例如,一个负责 Bank A,一个负责 Bank B)。 2. **Client Device (客户端设备):** * 这是需要使用引脚的设备,如 I2C 控制器、SPI 控制器、UART 控制器、MMC/SD 控制器、GPIO 控制器本身,甚至是一个简单的 LED 设备。 * 在设备树中,它表现为一个设备节点(例如 `&i2c1`, `&usdhc1`)。 * 它通过 `pinctrl-` 属性(如 `pinctrl-0`, `pinctrl-1`, ... `pinctrl-names`)来引用 Pin Controller 中定义好的引脚状态(Pin States),告诉内核它需要哪种引脚配置。 3. **Pin State (引脚状态):** * 这是 `pinctrl` 子系统的核心抽象。它代表了一组引脚在某个特定时刻的配置集合。 * 在 Pin Controller 的设备树节点中,使用子节点来定义不同的状态。常见的预定义状态名包括: * `default`: 设备正常操作时需要的状态。 * `init`: 设备初始化时可能需要的特殊状态(有时与 `default` 合并)。 * `sleep`, `idle`: 设备进入低功耗状态(睡眠、空闲)时需要的状态。 * 驱动也可以定义和使用自定义状态名(如 `active`, `inactive`)。 * 每个状态节点内部使用 `pinctrl` 绑定规定的属性(如 `fsl,pins`,具体属性名由 SoC 厂商的 Pin Controller 驱动定义)来描述该状态下涉及的**引脚组(Pin Group)**及其**复用功能**和**电气属性配置**。 4. **Pin Group (引脚组):** * 指逻辑上属于同一功能的一组引脚。例如,一个 UART 的 TX 和 RX 引脚可以组成一个名为 `uart1grp` 的组;一个 SPI 接口的 4 个引脚(CS, SCK, MISO, MOSI)可以组成名为 `spi0grp` 的组。 * 在 Pin Controller 设备树节点中定义的状态节点里,配置信息通常就是按组(或单个引脚)来指定的。组名通常作为状态节点的属性值出现。 5. **Pin Bank:** * 指物理上属于同一个寄存器块控制的一组引脚。通常一个 Pin Controller 可能管理多个 Bank。例如,一个 32 位寄存器控制 Bank A 的 32 个引脚,另一个 32 位寄存器控制 Bank B 的 32 个引脚。 * 这个概念更多存在于 Pin Controller 驱动实现的底层,对使用 `pinctrl` 的设备驱动通常是透明的[^1][^3]。 ## 3. `pinctrl` 子系统的工作原理与流程 1. **设备树描述:** * **Pin Controller 节点:** SoC 厂商或板级支持包(BSP)开发者在设备树(`.dtsi` 或 `.dts` 文件)中定义 Pin Controller 节点。在这个节点下,定义各种**状态子节点**(如 `pinctrl_mydevice_default`),在每个状态子节点内,使用 SoC 特定的属性(如 `fsl,pins`, `rockchip,pins`)列出该状态需要配置的**引脚组**(或单个引脚),并指定每个引脚的**复用功能编号**和**电气属性位掩码**。 * **Client Device 节点:** 设备驱动开发者或板级开发者在需要使用引脚的设备节点(如 `&i2c1`)中添加 `pinctrl-names` 和 `pinctrl-` 属性。例如: ```dts &i2c1 { pinctrl-names = "default", "sleep"; pinctrl-0 = <&pinctrl_i2c1_default>; // 引用Pin Controller中定义的状态节点 pinctrl-1 = <&pinctrl_i2c1_sleep>; ... }; ``` 2. **内核启动/驱动加载:** * **Pin Controller 驱动注册:** 在内核启动过程中,特定 SoC 的 Pin Controller 驱动(通常是平台驱动)被加载。它解析设备树中对应的 Pin Controller 节点,创建并注册一个 `struct pinctrl_desc` 和 `struct pinctrl_dev` 结构体实例到 `pinctrl` 子系统核心[^3]。这个 `pinctrl_dev` 代表了物理的 Pin Controller 硬件。 * **状态解析与存储:** Pin Controller 驱动解析设备树中该 Pin Controller 节点下定义的所有状态子节点,将配置信息存储在其私有数据结构中。 * **Client Device 驱动加载:** 当 Client Device 的驱动(如 `i2c-imx.c`)被加载时,其 `probe` 函数被调用。 * **获取 `pinctrl` 句柄:** 在 `probe` 函数中,驱动通常调用 `devm_pinctrl_get(dev)` 或 `pinctrl_get(dev)`。这个函数: * 根据设备节点(`dev->of_node`)查找 `pinctrl-names` 和 `pinctrl-` 属性。 * 找到关联的 Pin Controller (`pinctrl_dev`)。 * 遍历 `pinctrl-` 属性引用的所有状态节点(在 Pin Controller 节点下定义),并请求对应的 Pin Controller 驱动解析这些状态(如果还没解析的话)。 * 创建一个 `struct pinctrl` 对象来管理这个设备的所有状态,并返回一个 `struct pinctrl` 的句柄。 3. **状态选择与应用:** * **查找状态:** 驱动使用 `pinctrl_lookup_state(pinctrl, name)` 根据状态名(如 `"default"`)查找对应的 `struct pinctrl_state` 对象。 * **应用状态:** 驱动调用 `pinctrl_select_state(pinctrl, state)` 来应用该状态。这个函数最终会调用到关联的 Pin Controller 驱动注册的 `->set_state()` 或 `->set_mux()` / `->config_set()` 等操作函数。 * **硬件配置:** Pin Controller 驱动根据 `pinctrl_state` 中存储的配置信息(复用功能ID、电气属性掩码),操作 SoC 上实际的配置寄存器(MUX 寄存器、PAD 控制寄存器等),完成引脚复用功能和电气属性的设置[^3][^5]。 4. **状态切换(如休眠唤醒):** * 当设备需要进入低功耗状态(如 `suspend`)时,驱动(或 PM 子系统)会调用 `pinctrl_select_state(pinctrl, sleep_state)` 切换到休眠状态(如 `"sleep"`)。 * 当设备从低功耗状态恢复(`resume`)时,驱动会再次调用 `pinctrl_select_state(pinctrl, default_state)` 切换回默认状态。 ## 4. `pinctrl` 子系统与 GPIO 子系统的关系 这两个子系统紧密协作,但职责分明: * **`pinctrl` 子系统:** 负责引脚的**复用(Muxing)** 和 **配置(Configuration)**。它决定一个引脚当前是作为 GPIO 功能,还是作为某个特定外设(如 I2C、SPI)的功能。如果配置为 GPIO,它还负责设置该引脚作为 GPIO 时的基本电气属性(如上下拉、驱动强度)[^4][^5]。 * **GPIO 子系统:** 当引脚通过 `pinctrl` **配置为 GPIO 功能**后,GPIO 子系统负责提供 API 来**操作**这个 GPIO 引脚:设置方向(输入/输出)、读取输入电平、设置输出电平、申请/释放 GPIO、设置中断(如果该 GPIO 支持中断)等[^2][^4]。 **简单来说:`pinctrl` 决定了“这个引脚现在是什么(功能)”,GPIO 子系统决定了“当它是 GPIO 时,怎么操作它”。** 一个引脚必须先由 `pinctrl` 配置为 GPIO 功能,然后才能通过 GPIO 子系统 API 进行操作[^4][^5]。 ## 5. 关键数据结构(驱动开发者视角) 虽然驱动开发者通常不需要直接操作这些结构体内部,但了解它们有助于理解流程: 1. **`struct pinctrl_dev`:** * 代表一个注册到内核的 Pin Controller 硬件实例。 * 包含指向 `pinctrl_desc` 的指针、管理的引脚描述树、GPIO 范围列表、私有数据指针等[^3]。 * 由 SoC 厂商的 Pin Controller 驱动创建和注册。 2. **`struct pinctrl_desc`:** * 描述 Pin Controller 的能力和操作函数集。由 Pin Controller 驱动填充并传递给 `pinctrl_register()`。 * 关键成员: * `name`: 控制器名称。 * `pins`: `struct pinctrl_pin_desc` 数组,描述该控制器管理的所有物理引脚(名称、编号)。 * `npins`: `pins` 数组的大小。 * `pctlops`: `struct pinctrl_ops`,提供引脚/组枚举等操作。 * `pmxops`: `struct pinmux_ops`,提供引脚复用操作(核心)。 * `confops`: `struct pinconf_ops`,提供引脚配置操作(核心)。 * `owner`: 通常是 `THIS_MODULE`。 3. **`struct pinctrl`:** * 代表一个 Client Device 与 `pinctrl` 子系统的关联。通过 `devm_pinctrl_get()` 或 `pinctrl_get()` 获得。 * 包含该设备在设备树中引用的所有状态。 4. **`struct pinctrl_state`:** * 代表一个引脚配置状态(如 `"default"`, `"sleep"`)。 * 包含该状态下需要应用的配置设置列表(指向底层 Pin Controller 驱动存储的实际配置数据)。 * 通过 `pinctrl_lookup_state()` 获得。 5. **`struct pinctrl_setting`:** * `pinctrl_state` 内部包含一个或多个 `pinctrl_setting`。 * 每个 `setting` 代表一个具体的操作:复用(`PIN_MAP_TYPE_MUX_GROUP`)或配置(`PIN_MAP_TYPE_CONFIGS_*`)一个引脚组。 ## 6. 在 Client Device 驱动中使用 `pinctrl`(典型代码片段) ```c #include <linux/pinctrl/consumer.h> // 必须包含 static int my_driver_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct pinctrl *pinctrl; struct pinctrl_state *default_state, *sleep_state; int ret; /* 1. 获取该设备的 pinctrl 句柄 */ pinctrl = devm_pinctrl_get(dev); if (IS_ERR(pinctrl)) { ret = PTR_ERR(pinctrl); dev_err(dev, "Failed to get pinctrl: %d\n", ret); return ret; // 或 probe deferral } /* 2. 查找需要的状态 */ default_state = pinctrl_lookup_state(pinctrl, PINCTRL_STATE_DEFAULT); // "default" if (IS_ERR(default_state)) { ret = PTR_ERR(default_state); dev_err(dev, "Failed to get default pinctrl state: %d\n", ret); goto err_pinctrl; } sleep_state = pinctrl_lookup_state(pinctrl, PINCTRL_STATE_SLEEP); // "sleep" if (IS_ERR(sleep_state)) { dev_warn(dev, "No sleep pinctrl state defined, ignoring\n"); sleep_state = NULL; // 不是所有设备都需要sleep状态 } /* 3. 应用默认状态 (通常在probe早期应用) */ ret = pinctrl_select_state(pinctrl, default_state); if (ret) { dev_err(dev, "Failed to apply default pinctrl state: %d\n", ret); goto err_pinctrl; } /* ... 驱动其他初始化代码 ... */ /* 存储 pinctrl 和 states 以备后用 (例如在 suspend/resume 中) */ my_priv_data->pinctrl = pinctrl; my_priv_data->default_state = default_state; my_priv_data->sleep_state = sleep_state; return 0; err_pinctrl: devm_pinctrl_put(pinctrl); // 如果使用 pinctrl_get() 则需要手动调用 return ret; } static int my_driver_suspend(struct device *dev) { struct my_priv_data *priv = dev_get_drvdata(dev); int ret = 0; if (priv->sleep_state) { ret = pinctrl_select_state(priv->pinctrl, priv->sleep_state); if (ret) dev_warn(dev, "Failed to apply sleep pinctrl state: %d\n", ret); } /* ... 其他 suspend 操作 ... */ return ret; } static int my_driver_resume(struct device *dev) { struct my_priv_data *priv = dev_get_drvdata(dev); int ret = 0; /* ... 其他 resume 操作 ... */ ret = pinctrl_select_state(priv->pinctrl, priv->default_state); if (ret) { dev_err(dev, "Failed to apply default pinctrl state on resume: %d\n", ret); return ret; } return 0; } static const struct dev_pm_ops my_driver_pm_ops = { .suspend = my_driver_suspend, .resume = my_driver_resume, // 可能还有 .suspend_noirq, .resume_noirq 等 }; ``` ## 7. 总结 * `pinctrl` 子系统是 Linux 内核中**统一管理 SoC 引脚复用和配置**的核心框架。 * 它通过**设备树**描述硬件引脚资源和设备所需的引脚状态(`default`, `sleep` 等),实现了**配置与驱动代码的分离**。 * 核心概念包括 **Pin Controller**、**Client Device**、**Pin State**、**Pin Group**。 * Client Device 驱动通过 `pinctrl_get()` / `devm_pinctrl_get()` 获取句柄,通过 `pinctrl_lookup_state()` 查找状态,通过 `pinctrl_select_state()` 应用状态。 * 状态切换(如休眠唤醒)是 `pinctrl` 的关键应用场景之一。 * `pinctrl` 负责**决定引脚功能(复用)和基本配置**,**GPIO 子系统**负责**操作配置为 GPIO 功能的引脚**,两者分工明确,协同工作[^4][^5]。 理解并正确使用 `pinctrl` 子系统是编写可移植、易维护的现代 Linux 设备驱动(尤其是涉及外设接口的驱动)的基础
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值