Pinctrl子系统

Pinctrl作用

../../_images/image-20221124150346431.png

如图所示,Pinctrl主要完成以下工作

  • 引脚枚举与命名(Enumerating and naming)

    芯片原厂提供,定义了芯片引脚定义和复用功能等

  • 引脚复用(Multiplexing):比如用作GPIO、I2C或其他功能

  • 引脚配置(Configuration):比如上拉、下来、open drain、驱动强度等

基本概念

../../_images/image-20221124155702607.png

如上图所示:

  • pin controller

    图中左边所示,软件上的概念,配置IO复用功能 ,里面会有下面两个概念

    • Generic pin multiplexing node

      例如上图中的state_0_node_a节点中

      • function

        该节点复用为什么功能

      • groups

        需要用到哪些引脚

    • Generic pin configuration node

      配置io上下拉等

  • client device

    芯片的外设,比如I2C会使用Pinctrl系统,他就是个device,如上图中右边所示,里面有两个概念

    • pinctrl-0、pinctrl-1

      里面指明了pinctrl-0 使用 state_0_node_a节点,pinctrl-1使用state_0_node_a节点

    • pin state

      例如上面通过pinctrl-name指明device 有两种状态defaultsleep状态,状态是自定义的 只要驱动能识别就行。

      ../../_images/image-20221124160715890.png

      其中

      • default 状态使用pinctrl-0

      • sleep状态使用pinctrl-1

下面是实际的例子,可能和上面的模型不一样,具体如下所示:

../../_images/image-20221124162817695.png

代码中何时调用pinctrl

当设备切换状态,比如设备在probe的时候,驱动会调用状态切换如下:

../../_images/image-20221124163215455.png

或者设备从default切换为sleep状态的时候,也会进行调用。这些都是自动的过程,无需手动调用,我们也可以自己调用,

devm_pinctrl_get_select_default(struct device *dev);      	// 使用"default"状态的引脚
pinctrl_get_select(struct device *dev, const char *name); 	// 根据name选择某种状态的引脚
pinctrl_put(struct pinctrl *p);   							// 不再使用, 退出时调用

pin controller数据结构

设备树关于pin controller的定义

如图所示:设备树imx6ull.dtsiiomuxc即使imx6ull的pin controller

../../_images/image-20221129141155509.pngimx6ull.dtsi

imx6ull-14x14-evk.dts里面新增了imx6ul-evk这一个节点,即类似于我们上面模型中的function功能,

imx6ul-evk下又有很多子节点,用来描述外设组的引脚配置。

../../_images/image-20221129141232334.pngimx6ull-14x14-evk.dts

从上面可以得知当 compatible = "fsl,imx6ul-iomuxc"属性在平台设备链表中,被查到时候就会调用对应的probe函数,实际就是imx_6ul_pinctrl_probe函数,如下图所示:

../../_images/image-20221129141317173.png

那么在imx_6ul_pinctrl_probe函数中,设备树中的iomuxc节点是如何转换为c语言结构体的呢?继续分析如下。

如何构建pinctrl_dev

pincontroller虽然是一个软件的概念,抽象到Pinctrl子系统后会用pinctrl_dev来描述它在软件层面的定义。

构造出pinctrl_dev直接调用:pinctrl_register函数即可,正如在pinctrl-imx6ul.c文件中,如下代码所示:

imx6ul_pinctrl_probe(struct platform_device *pdev)
	imx_pinctrl_probe(struct platform_device *pdev,struct imx_pinctrl_soc_info *info)
    	// 下面的ipctl->pctl即为pinctrl_dev类型的结构体
        // 创建 pinctrl_dev 类型的设备,用来描述iomuxc
		ipctl->pctl = pinctrl_register(imx_pinctrl_desc, &pdev->dev, ipctl);
			

在pinctrl_register里面会把imx_pinctrl_desc 赋值给pinctrl_dev里面的desc,如下图

 pinctrl_register
    	struct pinctrl_dev *pctldev;
		pctldev = kzalloc(sizeof(*pctldev), GFP_KERNEL);

		pctldev->owner = pctldesc->owner;
		pctldev->desc = pctldesc;// 1-------------------------这里赋值
		pctldev->driver_data = driver_data;

		/* check core ops for sanity */
		ret = pinctrl_check_ops(pctldev);

		/* If we're implementing pinmuxing, check the ops for sanity */
		ret = pinmux_check_ops(pctldev);

		/* If we're implementing pinconfig, check the ops for sanity */
		ret = pinconf_check_ops(pctldev);

		/* Register all the pins */
		ret = pinctrl_register_pins(pctldev, pctldesc->pins, pctldesc->npins);

		list_add_tail(&pctldev->node, &pinctrldev_list);

按照上面的代码描述:pinctrl_dev里面有个很重要的结构体struct pinctrl_desc

../../_images/image-20221129154017328.png

按照上面的代码分析,看下imx_pinctrl_desc的来源,如下代码所示:

../../_images/image-20221129153000149.png

总结上面的代码分析:

  1. 构建struct pinctrl_pin_desc imx6ul_pinctrl_pads[]结构体,描述板载资源的所有gpio资源,

    构建struct imx_pinctrl_soc_info imx6ul_pinctrl_info结构体里面包含imx6ul_pinctrl_pads,供下面使用

  2. 构建struct of_device_id imx6ul_pinctrl_of_match[]结构体,用来和设备树做.compatible = "fsl,imx6ul-iomuxc"属性的匹配,并且设置私有数据为imx6ul_pinctrl_info

  3. 当设备树iomuxc节点和imx6ul_pinctrl_of_match匹配时候,会调用imx6ul_pinctrl_probe函数

  4. imx6ul_pinctrl_probe函数中为imx_pinctrl_desc赋值,这样imx_pinctrl_desc中携带所有的io资源了

    ../../_images/image-20221129153637055.png

    这里面还有三个重要的ops,用来存放操作io的函数

    • imx_pinctrl_desc->pctlops = &imx_pctrl_ops

      • 来取出某组的引脚:get_groups_count、get_group_pins

      • 处理设备树中pin controller中的某个节点:dt_node_to_map,把device_node转换为一系列的pinctrl_map

      ../../_images/image-20221129154729926.png

    • imx_pinctrl_desc->pmxops = &imx_pmx_ops

      • 引脚复用相关

      ../../_images/image-20221129155111830.pngimage-20221129155111830

    • imx_pinctrl_desc->confops = &imx_pinconf_ops

      • 引脚配置

      ../../_images/image-20221129155148857.png

如何解析设备树pincontroller中组信息

前面介绍了pinctrl_dev里面pinctrl_desc中关于设备所有gpio资源的获取和一些ops的操作函数,现在介绍pinctrl_dev中关于info变量的赋值,在代码中体现为下面的位置,

	imx_pinctrl_desc->name = dev_name(&pdev->dev);
	imx_pinctrl_desc->pins = info->pins;
	imx_pinctrl_desc->npins = info->npins;
	imx_pinctrl_desc->pctlops = &imx_pctrl_ops;
	imx_pinctrl_desc->pmxops = &imx_pmx_ops;
	imx_pinctrl_desc->confops = &imx_pinconf_ops;
	imx_pinctrl_desc->owner = THIS_MODULE;

	ret = imx_pinctrl_probe_dt(pdev, info);//----把设备树&iomuxc 所有组及每个组使用的引脚赋值到info中,
	if (ret) {
		dev_err(&pdev->dev, "fail to probe dt properties\n");
		return ret;
	}

	ipctl->info = info;// --------然后这里pinctrl_dev-》info 就可以访问这些数据了

具体的解析过程如下:

static int imx6ul_pinctrl_probe(struct platform_device *pdev)
	int imx_pinctrl_probe(struct platform_device *pdev,struct imx_pinctrl_soc_info *info)
 	ret = imx_pinctrl_probe_dt(pdev, info);
		        // 这个函数用来处理设备节点
 			static int imx_pinctrl_probe_dt(struct platform_device *pdev,struct imx_pinctrl_soc_info *info)
					struct device_node *np = pdev->dev.of_node;
					//1.这里指的iomuxc中的只有一个imx6ull-evk节点 所以nfuncs=1
					nfuncs = of_get_child_count(np);
					info->nfunctions = nfuncs;
					//2.这里分配nfuncs个functions空间
					info->functions = devm_kzalloc(&pdev->dev, nfuncs * sizeof(struct imx_pmx_func),GFP_KERNEL);
					//3.遍历iomuxc下所有child,这里只有一个imx6ull-evk ,然后统计所有的child下有多少个组,
					//  因为此时只有一个imx6ull-evk ,所以就是统计imx6ull-evk 下有多少个组,存放在info->ngroups中
					for_each_child_of_node(np, child)
						info->ngroups += of_get_child_count(child);
					//4.有多少个组就分配多少个组的空间
					info->groups = devm_kzalloc(&pdev->dev, info->ngroups * sizeof(struct imx_pin_group),GFP_KERNEL);
					//5.对每个iomuxc下的孩子,遍历,然后调用imx_pinctrl_parse_functions 来解析,此时也就一个孩子imx6ull-evk
					for_each_child_of_node(np, child)
						imx_pinctrl_parse_functions(child, info, i++); //这里的i 只能到0 因为只有一个imx6ull-evk
						{
							struct device_node *child;
							struct imx_pmx_func *func;
							struct imx_pin_group *grp;
							u32 i = 0;

							dev_dbg(info->dev, "parse function(%d): %s\n", index, np->name);

							func = &info->functions[index];

							/* Initialise function */
							// 6.此时np->name="imx6ul-evk"
							func->name = np->name;
							// 7.func->num_groups 就是节点imx6ul-evk下面有多少个组,然后分配多少个组空间
							func->num_groups = of_get_child_count(np);
							func->groups = devm_kzalloc(info->dev,
									func->num_groups * sizeof(char *), GFP_KERNEL);
							// 8.遍历imx6ul-evk下所有的组,获取组名并调用imx_pinctrl_parse_groups解析组
							for_each_child_of_node(np, child) {
								/* 9. 这里面存放imx6ul-evk下的所有组的名字 下面以pinctrl_i2c2组为例说明:
								*  此时的 child->name=pinctrl_i2c2
								*	pinctrl_i2c2: i2c2grp {
								*		fsl,pins = <
								*			MX6UL_PAD_UART5_TX_DATA__I2C2_SCL 0x4001b8b0
								*			MX6UL_PAD_UART5_RX_DATA__I2C2_SDA 0x4001b8b0
								*		>;
								*	};
								*/
								func->groups[i] = child->name; 
								grp = &info->groups[info->grp_index++];
								//10. 解析每个组下的配置
								imx_pinctrl_parse_groups(child, grp, info, i++);
								{
									//11.传进来的每个节点的名字例如pinctrl_i2c2
									grp->name = np->name; 
									// 12. 获取fsl,pins下有多少个size,这里以pinctrl_i2c2为例说明
									// size 等于,并且list指向第一个
									list = of_get_property(np, "fsl,pins", &size);
									
									// 13. fsl 定义了pin_size为24 也就是24个字节
									//     得到有多少个引脚,并分配空间给grp->pins
									grp->npins = size / pin_size;
									grp->pins = devm_kzalloc(info->dev, grp->npins * sizeof(struct imx_pin),GFP_KERNEL);
									grp->pin_ids = devm_kzalloc(info->dev, grp->npins * sizeof(unsigned int),GFP_KERNEL);
									//14. 变量所有引脚,访问里面的变量赋值给grp->pins[i]里面的每个成员,
									//    到此为止,设备树&iomuxc里面的成员都被赋值给 pinctrl_desc,也就是pinctrl_dev里面包含了
									//    设备树&iomuxc的信息和板载所有io信息

									for (i = 0; i < grp->npins; i++) {
										u32 mux_reg = be32_to_cpu(*list++);
										u32 conf_reg;
										unsigned int pin_id;
										struct imx_pin_reg *pin_reg;
										struct imx_pin *pin = &grp->pins[i];

										if (!(info->flags & ZERO_OFFSET_VALID) && !mux_reg)
											mux_reg = -1;

										if (info->flags & SHARE_MUX_CONF_REG) {
											conf_reg = mux_reg;
										} else {
											conf_reg = be32_to_cpu(*list++);
											if (!(info->flags & ZERO_OFFSET_VALID) && !conf_reg)
												conf_reg = -1;
										}

										pin_id = (mux_reg != -1) ? mux_reg / 4 : conf_reg / 4;
										pin_reg = &info->pin_regs[pin_id];
										pin->pin = pin_id;
										grp->pin_ids[i] = pin_id;
										pin_reg->mux_reg = mux_reg;
										pin_reg->conf_reg = conf_reg;
										pin->input_reg = be32_to_cpu(*list++);
										pin->mux_mode = be32_to_cpu(*list++);
										pin->input_val = be32_to_cpu(*list++);

										/* SION bit is in mux register */
										config = be32_to_cpu(*list++);
										if (config & IMX_PAD_SION)
											pin->mux_mode |= IOMUXC_CONFIG_SION;
										pin->config = config & ~IMX_PAD_SION;

										dev_dbg(info->dev, "%s: 0x%x 0x%08lx", info->pins[pin_id].name,
												pin->mux_mode, pin->config);
									}
								}
							}
						}

总结:经过上面的操作,pinctrl_dev里面就有了结构体struct pinctrl_desc和info结构体,里面包含cpu所有io资源和对这些io复用、配置、设置的三个关键ops函数。

client数据结构

在设备树的理想模型中,设备使用pinctrl如下:

/* For a client device requiring named states */
device {
    pinctrl-names = "active", "idle";
    pinctrl-0 = <&state_0_node_a>;
    pinctrl-1 = <&state_1_node_a &state_1_node_b>;
};

在imx6ull设备树中,以i2c为0例使用pinctrl的方式如下:

../../_images/image-20221129194521757.png

../../_images/image-20221129194649801.png

这些设备节点要么被转换为platform_device,或者其他结构体(比如i2c_client),但是里面都会有一个device结构体,比如:

../../_images/image-20221129162034312.png

在device中有个很重要的结构体struct dev_pin_info *pins,下面重点分析这个结构体。

dev_pin_info 分析

重要结构体

每个device结构体里都有一个dev_pin_info结构体,用来保存设备的pinctrl信息,下面是一些重要的结构体,先混个脸熟:

../../_images/image-20221129162458223.png

如何存储device

/* For a client device requiring named states */
device {
    pinctrl-names = "active", "idle";
    pinctrl-0 = <&state_0_node_a>;
    pinctrl-1 = <&state_1_node_a &state_1_node_b>;
};

如上面的代码,一个device可能有多种状态,active、idle、default等等,那么怎么把这些信息转换为C语言结构?下面继续分析。

在装载设备驱动的时候会调用下面的driver_probe_device函数,完成默认状态的设置,这个过程就是把上面的device里面的pinctrl-names的状态转换为C语言的结构体过程。

int driver_probe_device(struct device_driver *drv, struct device *dev)
{
	really_probe(dev, drv);
	{
		pinctrl_bind_pins(dev);
		{	//step 1.分配device里面的 struct dev_pin_info 空间
			dev->pins = devm_kzalloc(dev, sizeof(*(dev->pins)), GFP_KERNEL);
			//step 2.获取struct pinctrl *p
			dev->pins->p = devm_pinctrl_get(dev);
			//step 3.获取上面弄好的状态
			dev->pins->default_state = pinctrl_lookup_state(dev->pins->p,PINCTRL_STATE_DEFAULT);
			//step 4.设置状态
			ret = pinctrl_select_state(dev->pins->p, dev->pins->default_state);
		}
	}	
	
}

总结4步:其实就是完成 struct dev_pin_info结构体信息的填充

  • step 1.分配device里面的struct dev_pin_info 空间

  • step 2.获取struct pinctrl *p

  • step 3.获取上面弄好的状态

  • step 4.设置状态

下面重点分析step 2和step 4如下:

step 2

//step 2.获取struct pinctrl *pdev->pins->p = 
devm_pinctrl_get(dev);
{
	pinctrl_get(dev);
	{
		create_pinctrl(dev)
		{   
			//step 1. 分配struct pinctrl类型的空间
			struct pinctrl *p = kzalloc(sizeof(*p), GFP_KERNEL);
			//step 2. 把dt转换为map
			ret = pinctrl_dt_to_map(p)
			//step 3. 遍历每个map,然后转换为setting
			for_each_maps(maps_node, i, map) {
				if (strcmp(map->dev_name, devname))
					continue;
				//step 4. 把map转换为setting
				ret = add_setting(p, map);
			}	
		}
	}	
}

总结4步骤,其中步骤step 2和step 3比较重要

  • step 1. 分配struct pinctrl类型的空间

  • step 2.把dt转换为map

    int pinctrl_dt_to_map(struct pinctrl *p)
    {
    	// step 1. 遍历device下的所有的state 解析
    	for (state = 0; ; state++) {
    		/* step 2. 查找pinctrl-%d 关键字符串得到有多少个 node 这里的 pinctrl-%d 为pinctrl-0 pinctrl-1 等等,
    		* 根据实际的device里面节点不同而不同,下面以&i2c1节点为例说明 
    		* &i2c1 {
    		*	clock_frequency = <100000>;
    		*	pinctrl-names = "default";
    		*	pinctrl-0 = <&pinctrl_i2c1>;
    		*	status = "okay";
    		*/
    		propname = kasprintf(GFP_KERNEL, "pinctrl-%d", state);
    		prop = of_find_property(np, propname, &size);
    		// step 3. list 指向&pinctrl_i2c1 且size=1 因为 &i2c1 的pinctrl-0只有一个node   &pinctrl_i2c1
    		list = prop->value;
    		size /= sizeof(*list);
    
    		// step 4. 得到pinctrl-names 节点内容
    		ret = of_property_read_string_index(np, "pinctrl-names",
    						    state, &statename);
    
    		for (config = 0; config < size; config++) {
    			phandle = be32_to_cpup(list++);
    			// step 5. 此时的 phandle 即为 &pinctrl_i2c1 根据&pinctrl_i2c1 查找对应的设备节点np_config
    			np_config = of_find_node_by_phandle(phandle);
    			/* Parse the node */
    			ret = dt_to_map_one_config(p, statename, np_config);
    				{
    					// step 6. 这个ops就是上面我们注册的imx_pctrl_ops里面的 imx_dt_node_to_map 函数
    					ret = ops->dt_node_to_map(pctldev, np_config, &map, &num_maps);
    					//static int imx_dt_node_to_map(struct pinctrl_dev *pctldev,struct device_node *np,struct pinctrl_map **map, unsigned *num_maps)函数内容如下:
    					{
    						/* 
    						* step 7. 根据np_config 找到对应的组,即pinctrl_i2c1,然后统计组中io配置的个数存放在map_num里面
    						* 此时 pinctrl_i2c1 里面有两个io配置
    						*	pinctrl_i2c1: i2c1grp {
    						*		fsl,pins = <
    						*			MX6UL_PAD_UART4_TX_DATA__I2C1_SCL 0x4001b8b0
    						*			MX6UL_PAD_UART4_RX_DATA__I2C1_SDA 0x4001b8b0
    						*		>;
    						*	};
    						*
    						*											 mux_reg conf_reg input_reg mux_mode input_val
    						*  #define MX6UL_PAD_UART4_TX_DATA__I2C1_SCL 0x00b4  0x0340    0x05a4     2 		1
    						*  #define MX6UL_PAD_UART4_RX_DATA__I2C1_SDA 0x00b8  0x0344    0x05a8     2 		1
    						*
    						*/
    						const struct imx_pin_group *grp = imx_pinctrl_find_group_by_name(info, np->name);
    						for (i = 0; i < grp->npins; i++) {
    							if (!(grp->pins[i].config & IMX_NO_PAD_CTL))
    								map_num++;
    						}
    						// step 8. 有多少个引脚就分配多少个空间new_map
    						new_map = kmalloc(sizeof(struct pinctrl_map) * map_num, GFP_KERNEL);
    
    						//  new_map[0] 存放这组引脚用来配置复用功能的
    						new_map[0].type = PIN_MAP_TYPE_MUX_GROUP; 	// --------这组引脚配置复用功能的
    						new_map[0].data.mux.function = parent->name;// --------imx6ul-evk
    						new_map[0].data.mux.group    = np->name;    // --------i2c1grp
    
    						/* create config map */
    						new_map++;
    						//  step 9. 
    						//  new_map[1] 存放第一组引脚的配置 MX6UL_PAD_UART4_TX_DATA__I2C1_SCL 0x4001b8b0
    						//  new_map[2] 存放第二组引脚的配置 MX6UL_PAD_UART4_RX_DATA__I2C1_SDA 0x4001b8b0
    						for (i = j = 0; i < grp->npins; i++) {
    							if (!(grp->pins[i].config & IMX_NO_PAD_CTL)) {
    								new_map[j].type = PIN_MAP_TYPE_CONFIGS_PIN; //--这个引脚是用来配置的
    								new_map[j].data.configs.group_or_pin =pin_get_name(pctldev, grp->pins[i].pin);
    								new_map[j].data.configs.configs = &grp->pins[i].config;
    								new_map[j].data.configs.num_configs = 1; // -- 这里表明只有一个配置项
    								j++;
    							}
    						}
    						// step 10.到这里 其实device里面使用pinctrl-%d 的内容 结合pinctrolerdata 就把配置信息存放到new_map中了
    					}
    				}
    			of_node_put(np_config);
    
    		}
    	}
    }
    

    在上面的代码中的setp10 这步时候,已经完成了dt配置到map的转换,实现的功能大致如下:用map串了起来。

    ../../_images/image-20221130141212632.png

  • step 3. 遍历每个map,然后转换为setting

    static int add_setting(struct pinctrl *p, struct pinctrl_map const *map)
    {
    	switch (map->type) {
    	case PIN_MAP_TYPE_MUX_GROUP:
    		ret = pinmux_map_to_setting(map, setting);
    		{
    			// step1. 根据func name 转换为 func index
    			ret = pinmux_func_name_to_selector(pctldev, map->data.mux.function);
    			setting->data.mux.func = ret;
    
    			//  step2. 该function下有哪些组,为了下面比较用
    			ret = pmxops->get_function_groups(pctldev, setting->data.mux.func,&groups, &num_groups);
    			//  step3. 遍历function下的所有的groups 找到自己需要设置的那个组map->data.mux.group
    			if (map->data.mux.group) {
    				bool found = false;
    				group = map->data.mux.group;
    				for (i = 0; i < num_groups; i++) {
    					if (!strcmp(group, groups[i])) {
    						found = true;// 说明找到了
    						break;
    					}
    				}
    
    			}
    			//  step4. 把找到的组名转为组索引 存起来
    			ret = pinctrl_get_group_selector(pctldev, group);
    			setting->data.mux.group = ret;
    
    		}
    		break;
    	case PIN_MAP_TYPE_CONFIGS_PIN:
    	case PIN_MAP_TYPE_CONFIGS_GROUP:
    		ret = pinconf_map_to_setting(map, setting);
    		{
    			switch (setting->type) {
    			case PIN_MAP_TYPE_CONFIGS_PIN:
    				//  step5. name 转换为index
    				pin = pin_get_from_name(pctldev,
    							map->data.configs.group_or_pin);
    				if (pin < 0) {
    					dev_err(pctldev->dev, "could not map pin config for \"%s\"",
    						map->data.configs.group_or_pin);
    					return pin;
    				}
    				setting->data.configs.group_or_pin = pin;
    				break;
    			case PIN_MAP_TYPE_CONFIGS_GROUP:
    			  //  step6. name 转换为index
    				pin = pinctrl_get_group_selector(pctldev,
    							 map->data.configs.group_or_pin);
    				if (pin < 0) {
    					dev_err(pctldev->dev, "could not map group config for \"%s\"",
    						map->data.configs.group_or_pin);
    					return pin;
    				}
    				setting->data.configs.group_or_pin = pin;
    				break;
    			default:
    				return -EINVAL;
    			}
                //  step7. 到这里就完成了map 到 setting的转换
    			setting->data.configs.num_configs = map->data.configs.num_configs;
    			setting->data.configs.configs = map->data.configs.configs;
    
    		}
    		break;
    	default:
    		ret = -EINVAL;
    		break;
    	}
    
    }
    

    根据上面的代码分析,主要完成的功能如下

    ../../_images/image-20221130145757830.png

总结过程如下

  • 分析设备树,找到pin controller

  • 对于每个状态,比如default、init,去分析pin controller中的设备树节点

    • 使用pin controller的pinctrl_ops.dt_node_to_map来处理设备树的pinctrl节点信息,得到一系列的pinctrl_map

    • 这些pinctrl_map放在pinctrl.dt_maps链表中

    • 每个pinctrl_map都被转换为pinctrl_setting,放在对应的pinctrl_state.settings链表中

step 4

这个比较简单,当状态发生变化的时候,会根据state下的setting调用ops函数操作gpio。

int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *state)
{
	list_for_each_entry(setting, &state->settings, node) {
		switch (setting->type) {
		case PIN_MAP_TYPE_MUX_GROUP:
			ret = pinmux_enable_setting(setting);
			{
				// 根具setting里面的内容,调用ops函数 操作gpio
				ret = ops->set_mux(pctldev, setting->data.mux.func,setting->data.mux.group);
			}
			break;
		case PIN_MAP_TYPE_CONFIGS_PIN:
		case PIN_MAP_TYPE_CONFIGS_GROUP:
			ret = pinconf_apply_setting(setting);
			{
				// 根具setting里面的内容,调用ops函数操作gpio
				ret = ops->pin_config_set(pctldev,
					setting->data.configs.group_or_pin,
					setting->data.configs.configs,
					setting->data.configs.num_configs);
			}
			break;
		default:
			ret = -EINVAL;
			break;
		}

		if (ret < 0) {
			goto unapply_new_state;
		}
	}
	
}

../../_images/image-20221130150218685.png