Bootstrap

Linux内核之Pinctrl子系统介绍

引言

在许多soc内部都包含有pin控制器,通过pin控制器的寄存器,我们可以配置一个或者一组引脚的功能和特性。在软件方面,Linux内核提供了pinctrl子系统,目的是为了统一各soc厂商的pin脚管理。

Pinctrl子系统

一、Pinctrl子系统说明

在许多soc内部都包含有pin控制器,通过pin控制器的寄存器,我们可以配置一个或者一组引脚的功能和特性。各个厂商soc的pin脚在使用中,都有许多共同的特性,要么配置,要么复用pin脚。所以内核提供了一套代码来管理这些pin,这就是pinctrl子系统。主要实现的功能:
1、管理系统中所有的可以控制的pin,在系统初始化的时候,枚举所有可以控制的pin,并标识这些pin。
2、管理这些pin的复用(Multiplexing)。对于SOC而言,其引脚除了配置成普通的GPIO之外,若干个引脚还可以组成一个pin group,行程特定的功能。pin control subsystem要管理所有的pin group。
3、配置这些pin的特性。例如使能或关闭引脚上的pull-up、pull-down电阻,配置引脚的driver strength。

实现pinctrl子系统的功能:core在初始化时,由处理器抽象pin描述,在pinctrl core中枚举所有的pin描述。当我们的驱动层driver使用pinctrl时,会进入pinctrl core会去分析这些pin的枚举,映射,设置,配置pin和pin group。

pinctrl相关概念

普通driver调用pin control subsystem的主要目标有两个:

(1)设定该设备的功能复用。

(2)设定该device对应的pin的电气特性。

1、设定设备的功能复用需要了解两个概念,一个是function,另外一个是pin group。
function是功能抽象,对应一个HW逻辑block,例如SPI0.虽然给定了具体的function name,我们并不能确定其使用的pins的情况。例如为了设计灵活,芯片内部的SPI0的功能引出到pin group{C6,C7,C8,C9},也可能引出的另外一个pin group{C22.C23,C24,C25},但毫无疑问,这两个pin group不能同时active,毕竟芯片内部的SPI0的逻辑功能电路只有一个,因此只有给出function selector以及function的pin group selector才能进行function mux 的设定。

此外,由于电源管理的要求,某个device可能处于某个电源管理状态,例如idle或者sleep,这时候,属于device的所有pin就会需要处于另外的状态。

综合上述的需求,就定义了pin control state的概念,也就是说设备可能处于非常多的状态中的一个,device driver可以切换设备处于的状态。为了方便管理pin control state 就有了pin control state holder的概念,用来管理一个设备的所有的pin control状态。

综上所述,普通的driver调用pin control subsystem 的接口就是只有三个步骤:

(1)驱动加载或是运行时,获取pin control state holder的句柄

(2)设定pin control的状态

(3)驱动卸载或是退出时,释放pin control state holder的句柄

与GPIO子系统的关系

在这里插入图片描述
上图显示了gpio子系统和pinctrl子系统之间的关系,即pinctrl子系统实际上也把gpio一起管理起来,所有的gpio操作也需要透过pinctrl子系统来完成,这样,如果一个pin已经被申请为gpio,再通过pinctrl子系统申请为某个function时就会返回错误。

二、pinctrl 在dts文件中的描述
1、在使用pincrtl时,就涉及pinctrl在设备树中是如何描述的,在dts文件中,将一个处理器所使用的的pin用bank和group来描述。
a、何为bank?

所谓pin bank,就是以引脚名为依据,就是一组GPIO控制器的描述,如s3c2440A这块芯片,阅读它的datasheet就可以知道有9组GPIO,如图所示:
在这里插入图片描述
所以我们在dts中就把这9组GPIO枚举为pin bank。这里分析一部分,其他的类似。

gpa: gpa {                               
        gpio-controller;                     
        #gpio-cells = <2>;                  
    };   
.........
gpf: gpf {      
 		gpio-controller;
		interrupt-controller;    
		#gpio-cells = <2>;    
		#interrupt-cells = <2>;                                                                                                                                                                      								                                                                                                                                                                      		                                           
	}; 

如gpa: gpa 这个child node 就是描述GPA这个组,也就是gpa bank.。当然了 gpio-controller;表示这是一个GPIO控制器,有的GPIO控制器也可以是中断控制器,如gpf: gpf。#gpio-cells = <2>;表示使用这个bank的GPIO时,需要用两个32位数去描述。肯定是GPIO number和初始电平,具体可查阅
linux-4.19.8\Documentation\devicetree\bindings\pinctrl\samsung-pinctrl.txt

b、何为group?

从字面意思理解就是一个类似集群,将不同的pin number组合在一个。group以功能为依据,我们在JZ2440的LCD驱动中,需要使用gpc bank中的(1、2、3、4、8、9、10、11、12、13、14、15)gpd bank中的(0、1、2、3、4、5、6、7、8、9、10、11、12、13、14、15)来读写数据,我们把这些具体pin number组合在一起,我们称之为一个group。
同时这些组合在一起的pin number又可能实现不同的功能,就用samsung,pin-function、samsung,pin-val等来描述区分。这样子samsung,pins和samsung,pin-function构成child node。来描述设备使用pinctrl时的具体功能。

lcd_pinctrl: lcd_pinctrl {
        samsung,pins = "gpc-8", "gpc-9", "gpc-10", "gpc-11", "gpc-12", "gpc-13", "gpc-14", "gpc-15",
                "gpd-0", "gpd-1", "gpd-2", "gpd-3", "gpd-4", "gpd-5", "gpd-6", "gpd-7",
                "gpd-8", "gpd-9", "gpd-10", "gpd-11", "gpd-12", "gpd-13", "gpd-14", "gpd-15",
                "gpc-1", "gpc-2", "gpc-3", "gpc-4";
        samsung,pin-function = <2>;
    };

samsung,pins:描述了LCD读写数据所使用的pin number,

samsung,pin-function:将这些GPIO初始值设置为2,具体是什么功能,有datasheet解释。这一个关于初始值的描述并不只是 samsung,pin-function,对于samsung来说,还有如下几种:具体阅读linux-4.19.8\Documentation\devicetree\bindings\pinctrl\samsung-pinctrl.txt

  • samsung,pin-val: Initial value of pin output buffer.
  • samsung,pin-pud: Pull up/down configuration.
  • samsung,pin-drv: Drive strength configuration.
  • samsung,pin-pud-pdn: Pull up/down configuration in power down mode.
  • samsung,pin-drv-pdn: Drive strength configuration in power down mode.
2、设备节点引用pinctrl

在lcd这个节点中dts文件是如下描述的,省略其他不需要的东西。

fb0: fb@4d000000{
        compatible = "jz2440,lcd";
        ...
        pinctrl-names = "default";
        pinctrl-0 = <&lcd_pinctrl &lcd_backlight>;
        ...
}

pinctrl-names其实就是设置设备的某种初始状态,比如内核自己定义的"default",“init”,“idel”,"sleep"状态;也可以是其他自己定义的状态, 比如串口的"flow_ctrl"状态(使用流量控制)。

pinctrl-0 就是正常引用pin config,也就是调用lcd_pinctrl这个group配置LCD读写数据的GPIO。

三、在设备驱动模型中的应用

驱动模型中调用driver的probe函数的地方:

static int really_probe(struct device *dev, struct device_driver *drv)
{
	int ret = 0;
        ......
	/* If using pinctrl, bind pins now before probing */
	ret = pinctrl_bind_pins(dev); //对该device涉及的pin进行pin control相关设定
	if (ret)
		goto probe_failed;
 
	if (driver_sysfs_add(dev)) {
		printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
			__func__, dev_name(dev));
		goto probe_failed;
	}
 
	if (dev->bus->probe) { //下面是真正的probe过程。
		ret = dev->bus->probe(dev);
		if (ret)
			goto probe_failed;
	} else if (drv->probe) {
		ret = drv->probe(dev);
		if (ret)
			goto probe_failed;
	}
 
	driver_bound(dev);
	ret = 1;
	pr_debug("bus: '%s': %s: bound device %s to driver %s\n",
        .....
}

pinctrl_bind_pins函数定义:

#define PINCTRL_STATE_DEFAULT "default"
#define PINCTRL_STATE_IDLE "idle"
#define PINCTRL_STATE_SLEEP "sleep"
int pinctrl_bind_pins(struct device *dev)
{
	int ret;
 
	dev->pins = devm_kzalloc(dev, sizeof(*(dev->pins)), GFP_KERNEL);
	if (!dev->pins)
		return -ENOMEM;
 
	dev->pins->p = devm_pinctrl_get(dev); //获取pinctrl 
	if (IS_ERR(dev->pins->p)) {
		dev_dbg(dev, "no pinctrl handle\n");
		ret = PTR_ERR(dev->pins->p);
		goto cleanup_alloc;
	}
 
	dev->pins->default_state = pinctrl_lookup_state(dev->pins->p, //查找这个pin的default状态
					PINCTRL_STATE_DEFAULT);
	if (IS_ERR(dev->pins->default_state)) {
		dev_dbg(dev, "no default pinctrl state\n");
		ret = 0;
		goto cleanup_get;
	}
 
	ret = pinctrl_select_state(dev->pins->p, dev->pins->default_state);  //设置pin的default状态
	if (ret) {
		dev_dbg(dev, "failed to activate default pinctrl state\n");
		goto cleanup_get;
	}
 
	return 0;

从驱动模型的实现中,不难看出在驱动probe前,就已经申请到default的pin配置了,当然pinctrl的计数已经+1了。

Driver的probe函数可以通过devm_pinctrl_get获得pinctrl的句柄,再自行调用pinctrl_select_state设置pin state。

五、与device tree的关系

在device tree source 文件中可以在驱动节点中定义该驱动需要用到的pin配置。

device-node-name{
	compatible = "xxxxxxx";
	......
   //定义该device自己的属性
   pinctrl-names= "sleep","default","idle";
    pinctrl-0 = &xxx_State_sleep;
    pinctrl-1 = &xxx_state_default;
    pinctrl-2 = &xxx_state_idle;
}

pinctrl-0 pinctrl-1 pinctrl-2 …表示了该设备的一个个状态,这里我们定义了三个pinctrl-0 pinctrl-1 pinctrl-2,数字0、1、2就是pinctrl-names中对应的字符串数组的index。其中pinctrl-0就是“sleep”状态,pinctrl-1就是“default”状态,pinctrl-2就是“idle”状态。
xxx_state_sleep,xxx_state_default,xxx_state_idel就是驱动具体的pin配置项了,需要在pinctrl设备节点处定义:

pinctrl@e01b0000{
      pinctrl-names = "default";
      pinctrl-0 = <&state_default>;
 
      state_default:pinctrl_default{
      };
     
      xxx_state_sleep:xxx_sleep{
         xxx_mfp{
            actions,groups = "fmp1_3_1spi0_ss","mfp1_3_1_spi0_miso","mfp1_5_4";
            actions,function = "spi0";
         };
       };

Pinctrl子系统在加载时,会调用pinctrl_dt_to_map函数将dts文件中有关pinctrl的配置项解析出来,并根据dts各驱动节点对pinctrl的引用关系,将phandle挂到各个驱动的device tree子节点,各个驱动就可以通过自己的dev句柄获得pinctrl的配置了。

六、与主控驱动的关系

在kernel的machine driver中,需要将主控的pinctrl相关硬件操作形象成一个符合linux pinctrl子系统规范的结构pinctrl_desc,并调用pinctrl_register注册到pinctrl子系统中,这样pinctrl子系统就可以将上层行为转换成具体的硬件操作了:

struct pinctrl_desc {
	const char *name;
	struct pinctrl_pin_desc const *pins; //描述每个pin的信息为何。
	unsigned int npins; //表示可以控制多少个pin。pins和npins这两个成员就确定了一个pin controller所能控制的引脚信息。
	const struct pinctrl_ops *pctlops;//全局的控制函数
	const struct pinmux_ops *pmxops;//复用引脚的相关的操作函数
	const struct pinconf_ops *confops; //用来配置引脚特性(如pull-up/pull-down)
	struct module *owner;
};

pctlops成员callback函数说明:


struct pinctrl_ops {
	int (*get_groups_count) (struct pinctrl_dev *pctldev);  //该pin controller支持多少个pin group
	const char *(*get_group_name) (struct pinctrl_dev *pctldev,//给定一个selector(index),获取指定的pin group的name
				       unsigned selector);
	int (*get_group_pins) (struct pinctrl_dev *pctldev, //给定一个selector(index)获取指定的pin group中pin的信息(该pin group
			       unsigned selector,//包括多少个pin,每个pin的ID是什么)
			       const unsigned **pins,
			       unsigned *num_pins);
	void (*pin_dbg_show) (struct pinctrl_dev *pctldev, struct seq_file *s,//debug fs的callback接口
			  unsigned offset);
	int (*dt_node_to_map) (struct pinctrl_dev *pctldev,   //分析一个pin configuration node并把分析结果保存成mapping table              
			       struct device_node *np_config,     //entry 每一个entry表示一个setting(一个功能复用设定,或者电气特性设定)
			       struct pinctrl_map **map, unsigned *num_maps);
	void (*dt_free_map) (struct pinctrl_dev *pctldev,     //dt_node_to_map的逆函数。
			     struct pinctrl_map *map, unsigned num_maps);
};

pmxops成员callback函数说明:

struct pinmux_ops {
	int (*request) (struct pinctrl_dev *pctldev, unsigned offset);//pinctrl子系统进行具体的复用设定之前需要调用该函数,主要用来请底层的driver判断某个引脚的复用设定是否ok
	int (*free) (struct pinctrl_dev *pctldev, unsigned offset);//request的逆函数,调用request函数请求占用了某些pin的资源,调用free可以释放这些资源。
	int (*get_functions_count) (struct pinctrl_dev *pctldev);//返回pin controller支持的function的数目。
	const char *(*get_function_name) (struct pinctrl_dev *pctldev,//给定一个selector(index)获取指定function的name。
					  unsigned selector);
	int (*get_function_groups) (struct pinctrl_dev *pctldev,//给定一个selector(index)获取指定的function 的pin group信息。
				  unsigned selector,
				  const char * const **groups,
				  unsigned * const num_groups);
	int (*enable) (struct pinctrl_dev *pctldev, unsigned func_selector, //enable一个function当然要给我出function selector和pin group的selector
		       unsigned group_selector);
	void (*disable) (struct pinctrl_dev *pctldev, unsigned func_selector,//enable的逆函数
			 unsigned group_selector);
	int (*gpio_request_enable) (struct pinctrl_dev *pctldev,//request并且enable一个单独的gpio pin
				    struct pinctrl_gpio_range *range,
				    unsigned offset);
	void (*gpio_disable_free) (struct pinctrl_dev *pctldev,//gpio_request_free的逆函数
				   struct pinctrl_gpio_range *range,
				   unsigned offset);
	int (*gpio_set_direction) (struct pinctrl_dev *pctldev,//设定gpio方向的回调函数
				   struct pinctrl_gpio_range *range,
				   unsigned offset,
				   bool input);
};

confops成员的callback函数说明:

struct pinconf_ops {
#ifdef CONFIG_GENERIC_PINCONF
	bool is_generic;
#endif
	int (*pin_config_get) (struct pinctrl_dev *pctldev,//给定一个pin ID以及config type ID,获取该引脚上指定的type的配置。
			       unsigned pin,
			       unsigned long *config);
	int (*pin_config_set) (struct pinctrl_dev *pctldev,//设定一个指定pin的配置
			       unsigned pin,
			       unsigned long config);
	int (*pin_config_set_bulk) (struct pinctrl_dev *pctldev,
				    unsigned pin,
				    unsigned long *configs,
				    unsigned num_configs);
	int (*pin_config_group_get) (struct pinctrl_dev *pctldev,//以pin group为单位,获取pin上的配置信息。
				     unsigned selector,
				     unsigned long *config);
	int (*pin_config_group_set) (struct pinctrl_dev *pctldev,//以pin group为单位,设定pin group 的特性配置。
				     unsigned selector,
				     unsigned long config);
	int (*pin_config_group_set_bulk) (struct pinctrl_dev *pctldev,
					  unsigned selector,
					  unsigned long *configs,
					  unsigned num_configs);
	int (*pin_config_dbg_parse_modify) (struct pinctrl_dev *pctldev, //debug接口
					   const char *arg,
					   unsigned long *config);
	void (*pin_config_dbg_show) (struct pinctrl_dev *pctldev,  //debug接口
				     struct seq_file *s,
				     unsigned offset);
	void (*pin_config_group_dbg_show) (struct pinctrl_dev *pctldev, //debug接口
					   struct seq_file *s,
					   unsigned selector);
	void (*pin_config_config_dbg_show) (struct pinctrl_dev *pctldev,//debug接口
					    struct seq_file *s,
					    unsigned long config);
};
七、dts文件中的pinctrl关键词表
pin命名表

pin的命名遵循IC spec(集成电路标准)上的命名,以下命名表以每个pin默认的功能命名,但实际使用中各个pin的功能会随着配置发生变化。
在dts中使用关键词“actions(不同厂商不同,这是炬芯的),pins”后跟名字数组来定义需要使用哪些pin,如:

i2c0_over_uart0_pull_up{
        actions,pins = “P_UART0_RX”,“P_UART0_TX”;
        actions,pull = <2>;
}
MUX Group-Function表

1、Mux Group命名表
pin group按照寄存器的名字和bit来命名。比如:MFP_CTL1 bit22:21,定义了10个pin的mux:P_LVDS_OEP,P_LVDS_OEN,P_LVDS_ODP,P_LVDS_ODN,P_LVDS_OCP,P_LVDS_OCN,P_LVDS_OBP,P_LVDS_OBN,P_LVDS_OAP,P_LVDS_OAN。该pin group的名字为mfp1_22_21.

有些mfp寄存器的cell中,设置某一个值会将多个pin配置为不同的功能。那么这个cell中的pin就不能归为同一个pin group。需按情况拆解开,那么拆解开的pin group的名字还会加上一些后缀。

比如:根据IC SPEC,mfp1 bit[31:29]可控制3根pin:P_KS_IN0,P_KS_IN1,P_KS_IN2.该cell的0x11选项会将这2跟pin分别定义为pwm0,pwm1,pwm4.

那么就将其分拆为3个group:分别为“mfp1_31_29_ks_in2”、“mfp1_31_29_ks_in2”、“mfp1_31_29_ks_in0”。这几个mfp cell分割以后,他们之间仍然存在硬件上的互斥关系,比如将mfp1_31_29_ks_in2的function选为pwm0,mfp1_31_29_ks_in1的function选为pwm1,可以工作。但是将mfp1_31_29_ks_in2的function选为pwm0,mfp1_31_29_ks_in1的function选为jtag,就会返回错误。

在pin group的命名表中还会看到“xxx_dummy”的命名,这种pin group在pinmux设置中可能会用到。这些pin group只有一种mux功能,所以在mfp寄存器中不会表示,但是这些pin可能和gpio复用,申请这些pin有助于发现它和gpio存在的潜在复用错误。

在dts中使用关键词“actions,groups”后跟mux Group名字数组来定义需要使用哪些Mux group,如:

sd0_mfp_cmd_clk{

     actions,groups = "mfp2_8_7","mfp2_6_5";

     actions,function = "sd0";

};
MUX Group-function

Mux Group所控制的pin,可以通过设置Mux Function的方式改变IC内部连通性,使得同一组pin用作不同的功能,例如:

sd0_mfp_cmd_clk{
    actions,groups ="mfp2_8_7","mfp2_6_5";
    actions,function = "jtag";
};
Drive Group

paddrv使用的配置值完全等同于IC spec中关于PAD_DRVx寄存器的定义。

对于某些pin可以设置pin的驱动能力(即供电能力),可以通过配置driver group的等级对pin的驱动能力进行配置。

在dts中使用关键词“actions,paddrv”后跟驱动能力等级来定义“actions,groups”指定drive group所代表的pin组使用哪种驱动能力,如:

sd0_d0_d3_cmd_clk_paddrv{
 actions,groups="paddrv1_19_18","paddrv1_17_16";
 actions,paddrv = "1" /*level 1, range :0 ~ 3*/
}

表示“paddrv1_19_18”所代表的P_SD0_CMD和paddrv1_17_16 所代表的P_SD0_CLK,使用驱动能力1来提升数据传输稳定性。

Pin Pull Up/Down

在dts中使用关键词“actions,pull”后跟上下拉数据定义“actions,pin”指定的pin组使用哪种上下拉,如:

sd0_pull_d0_d3_cmd{
   actions,pins = "P_SD0_CMD","P_SD0_CLK";
   actions,pull = <1>; 
};

表示将P_SD0_CMD和P_SD0_CLK这两个pin下拉。

actions,pull = <0>,表示上下拉功能关闭

actions,pull = <1>,表示下拉

actions,pull = <2>,表示上拉

GPIO-PIN

pin除了可以复用作各种功能外,还可以配置成GPIO使用,pinctrl子系统将GPIO子系统也管理起来了,因此申请GPIO的时候会去检查该gpio所对应的pin是否已经被其他驱动申请作其他功能了。如果已经被申请则申请时会报错,反之亦然。

七、使用dts描述pinctrl配置
dts中pinctrl配置方法

所有的pinctrl-state都定义在pin controller device节点中:

在kernel/arch/arm64/boot/dts/s700_pinctrl.dtsi 中

/ {
 
        pinctrl@e01b0000 {
                compatible = "actions,s700-pinctrl";
                reg = <0 0xe01b0000 0 0x1000>;
 
                pinctrl-names = "default";
                pinctrl-0 = <&state_default>;
                clocks = <&clock CLK_GPIO>;
                clock-names = "mfp";
 
                state_default: pinctrl_default {
                };
 

而个驱动引用定义在pin controller device 节点中的子节点,即pinctrl-state节点。

各驱动使用如下方法引用pinctrl-state节点:

pinctrl-N:描述该设备需要使用的pin的一个状态(pin state),相当于上述的state。N的数值必须从0开始顺序递增。pinctrl-N属性的值为pin configuration nodes的phandle。pinctrl-N属性引用的pin configuration nodes必须是pin controller device node的直接子节点。

pinctrl-names:为每个pin state定义一个名字。每个名字顺序对应一个pin state。比如pinctrl-0的名字为“default”,pinctrl-1的名字对应“idle”。若不能指定pinctrl-names属性,这样的话,pin state的名字就是该属性的“N”字符。比如pinctrl-0的名字为字符“0”

mmc@e0330000{
      pinctrl-names = "pinctrl_mmc0","share_uart2_5";  
      pinctrl-0 = <&mm0_pinctrl_state>;
      pinctrl-1 = <&mmc_share_uart_state>;
}

上面的例子中,mmc驱动定义的pinctrl-state有两个,其中mmc0_state_default是定义pin controller device node下的直接子节点,表示sd0 的pin group 作为sd功能使用,而mmc_share_uart_state则表示作为serial功能使用,其中mmc_state_default对应的pinctrl-names 属性为default,而mmc_share_uart_state对应的pinctrl-names属性为share_uart2_5。

八、常用API分析

实例公用DTS节点

xxx {
    ....
    pinctrl-names = "turnon_tes", "turnoff_tes";
    pinctrl-0 = <&disp_teson>;
    pinctrl-1 = <&disp_tesoff>;
};

&disp_teson_pinctrl {                           //#define disp_teson_pinctrl	pinctrl_2
    disp_teson: disp_teson {
        samsung,pins = disp_teson_pin;          //#define disp_teson_pin    "gpg0-1"
        samsung,pin-function = <disp_teson_con>;//#define disp_teson_con    2 -- 对应0x2 = TEDECON_INT
    };
};
&disp_tesoff_pinctrl {
    disp_tesoff: disp_tesoff {
        samsung,pins = disp_tesoff_pin;          //#define disp_teson_pin        "gpg0-1"
        samsung,pin-function = <disp_tesoff_con>;//#define disp_teson_con         0
    };
};

1、获取一个pinctrl句柄,参数是dev是包含这个pin的device结构体即xxx这个设备的device

/**
 * struct devm_pinctrl_get() - Resource managed pinctrl_get()
 * @dev: the device to obtain the handle for
 *
 * If there is a need to explicitly destroy the returned struct pinctrl,
 * devm_pinctrl_put() should be used, rather than plain pinctrl_put().
 */
struct pinctrl *devm_pinctrl_get(struct device *dev)

2、获取这个pin对应pin_state(引脚状态-turnon_tes/turnoff_tes)

/**
 * pinctrl_lookup_state() - retrieves a state handle from a pinctrl handle
 * @p: the pinctrl handle to retrieve the state from
 * @name: the state name to retrieve
 */
struct pinctrl_state *pinctrl_lookup_state(struct pinctrl *p, const char *name)

3、设置引脚为为某个stata – turnon_tes/turnoff_tes

/**
 * pinctrl_select_state() - select/activate/program a pinctrl state to HW
 * @p: the pinctrl handle for the device that requests configuration
 * @state: the state handle to select/activate/program
 */
int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *state)

具体操作:

/* 获取pin control state holder 的句柄 */
pinctrl = devm_pinctrl_get(dev);                                     
/* 得到名字为turnon_tes和turnoff_tes对应的pin state */
struct pinctrl_state * turnon_tes = pinctrl_lookup_state(pinctrl, "turnon_tes");     
struct pinctrl_state * turnoff_tes = pinctrl_lookup_state(pinctrl, "turnoff_tes");
/* 设置名字为turnon_tes这个pinctrl对应引脚(gpg0-1)的pin state,即gpg0_1对应的寄存器位域设置为2 */
pinctrl_select_state(pinctrl, turnon_tes)
九、总结

pincrtl子系统,就是在soc初始化时枚举所有的gpio。设备注册时,分析mux和configs。mux:设备以功能为依据,所用的一组GPIO;configs:一个GPIO。

;