Pinctrl子需要中client端使用pinctrl过程的驱动分析

news2024/11/22 1:55:34

往期内容

本专栏往期内容:

  1. Pinctrl子系统和其主要结构体引入
  2. Pinctrl子系统pinctrl_desc结构体进一步介绍
  3. Pinctrl子系统中client端设备树相关数据结构介绍和解析
  4. inctrl子系统中Pincontroller构造过程驱动分析:imx_pinctrl_soc_info结构体

input子系统专栏:

  1. 专栏地址:input子系统
  2. input角度:I2C触摸屏驱动分析和编写一个简单的I2C驱动程序
    – 末片,有往期内容观看顺序

I2C子系统专栏:

  1. 专栏地址:IIC子系统
  2. 具体芯片的IIC控制器驱动程序分析:i2c-imx.c-CSDN博客
    – 末篇,有往期内容观看顺序

总线和设备树专栏:

  1. 专栏地址:总线和设备树
  2. 设备树与 Linux 内核设备驱动模型的整合-CSDN博客
    – 末篇,有往期内容观看顺序

img

前言

本节就不提供相关源码文件了,各个代码块处都有标明该函数的文件路径,需要的可以自行去查看。

主要讲解作为使用者来说去使用pinctrl,其相关驱动程序是如何去进行获取pinctrl信息,对其进行解析将引脚转为map,在转为setting存储起来,去进行使用,也就是其如何去配置、复用引脚的。

1.回顾client的数据结构

看之前的文章:Pinctrl子系统中client端设备树相关数据结构介绍和解析

右侧是Pinctrl节点,左侧是client端节点。Pinctrl节点中设置了要使用的引脚的信息以及复用的功能,client节点则是对要使用的引脚进行引用,其实就是模块的设备节点。

img

img

  Pin Controller:有自己的驱动程序
virtual_pincontroller {
    compatible = "100ask,virtual_pinctrl";
    myled_pin: myled_pin {
            functions = "gpio";
            groups = "pin0";
            configs = <0x11223344>;
    };
};

  GPIO Controller:有自己的驱动程序
gpio_virt: virtual_gpiocontroller {
    compatible = "100ask,virtual_gpio";
    gpio-controller;
    #gpio-cells = <2>;
    ngpios = <4>;
};

  Client:有自己的驱动程序
myled {
    compatible = "100ask,leddrv";
    led-gpios = <&gpio_virt 0 GPIO_ACTIVE_LOW>;
    pinctrl-names = "default";
    pinctrl-0 = <&myled_pin>;	
};

另一种写法(正确写法):让GPIO和Pinctrl之间建立联系
/ {
   pinctrl_virt: virtual_pincontroller {
        compatible = "100ask,virtual_pinctrl";
        myled_pin: myled_pin {
                        functions = "gpio";
                        groups = "pin0";
                        configs = <0x11223344>;
        };
        i2cgrp: i2cgrp {
                functions = "i2c", "i2c";
                groups = "pin0", "pin1";
                configs = <0x11223344  0x55667788>;
        };
    };

    gpio_virt: virtual_gpiocontroller {
        compatible = "100ask,virtual_gpio";
        gpio-controller;
        #gpio-cells = <2>;
        ngpios = <4>;
        gpio-ranges = <&pinctrl_virt 0 0 4>;  
        //GPIO控制器的第0号引脚对应pinctrl_virt的第0号引脚(也就是对应myled_pin),数量为4
    };

    myled {
        compatible = "100ask,leddrv";
        led-gpios = <&gpio_virt 2 GPIO_ACTIVE_LOW>;
    };
};

2.client的pinctrl构造过程

相同state的描述引脚的设备树节点如何转换为pinctrl_map,这些pinctrl_map又如何转换为pinctrl_setting存放在pinctrl_state结构体中的settings链表中

2.1 总图

比较长,看下图就行了。

img

really_probe 
	pinctrl_bind_pins
		dev->pins = devm_kzalloc(dev, sizeof(*(dev->pins)), GFP_KERNEL);
		
		dev->pins->p = devm_pinctrl_get(dev); 
								create_pinctrl(dev);
									ret = pinctrl_dt_to_map(p);
									
                                    for_each_maps(maps_node, i, map) {
	                                    ret = add_setting(p, map);
                                    }
		
		dev->pins->default_state = pinctrl_lookup_state(dev->pins->p,
					PINCTRL_STATE_DEFAULT);	

2.2 解析

如果想深入了解各个代码的含义,可以继续看:

really_probe  //\Linux-4.9.88\drivers\base\dd.c
    //* If using pinctrl, bind pins now before probing */
	pinctrl_bind_pins //\Linux-4.9.88\drivers\base\dd.c
		dev->pins->p = devm_pinctrl_get(dev); 
            //直接从该函数进入看
            p = pinctrl_get(dev);//D\Linux-4.9.88\drivers\pinctrl\core.c

pinctrl_get内部实现如下,根据其注释可以知道,去获取到pinctrl的句柄,但是在第一次的时候肯定是没有的,所以最后会去调用create_pinctrl函数去创建pinctrl

\Linux-4.9.88\drivers\pinctrl\core.c

/**
 * pinctrl_get() - retrieves the pinctrl handle for a device
 * @dev: the device to obtain the handle for
 */
struct pinctrl *pinctrl_get(struct device *dev)
{
	struct pinctrl *p;

	if (WARN_ON(!dev))
		return ERR_PTR(-EINVAL);

	/*
	 * See if somebody else (such as the device core) has already
	 * obtained a handle to the pinctrl for this device. In that case,
	 * return another pointer to it.
	 */
	p = find_pinctrl(dev);
	if (p != NULL) {
		dev_dbg(dev, "obtain a copy of previously claimed pinctrl\n");
		kref_get(&p->users);
		return p;
	}

	return create_pinctrl(dev);
}

进入create_pinctrl函数看看:

\Linux-4.9.88\drivers\pinctrl\core.c
/static struct pinctrl *create_pinctrl(struct device *dev)
{
	struct pinctrl *p;               // 指向创建的引脚控制结构体
	const char *devname;             // 设备的名称
	struct pinctrl_maps *maps_node;  // 引脚映射节点指针
	int i;                           // 循环计数器
	struct pinctrl_map const *map;   // 当前的引脚映射
	int ret;                         // 存储返回值,用于错误检查

	/* 
	 * 为每个映射创建状态存储结构体 pinctrl
	 * 消费者可以通过 pinctrl_get() 请求该引脚控制句柄
	 */
	p = kzalloc(sizeof(*p), GFP_KERNEL);
	if (p == NULL) {
		dev_err(dev, "failed to alloc struct pinctrl\n");
		return ERR_PTR(-ENOMEM);
	}
	p->dev = dev;                    // 关联设备指针
	INIT_LIST_HEAD(&p->states);      // 初始化引脚控制的状态列表
	INIT_LIST_HEAD(&p->dt_maps);     // 初始化设备树的引脚映射列表

	// 将设备树映射到引脚控制结构体中
	ret = pinctrl_dt_to_map(p);
	if (ret < 0) {                   // 检查映射是否成功
		kfree(p);                 // 释放已分配的内存
		return ERR_PTR(ret);      // 返回错误指针
	}

	devname = dev_name(dev);          // 获取设备名称

	mutex_lock(&pinctrl_maps_mutex);  // 加锁以保护全局映射
	/* 遍历所有引脚控制映射以找到合适的映射 */
	for_each_maps(maps_node, i, map) {
		/* 检查映射是否属于当前设备 */
		if (strcmp(map->dev_name, devname))
			continue;

		// 将映射添加到引脚控制结构体中
		ret = add_setting(p, map);
		/*
		 * 此时添加设置可能会:
		 * - 延迟:如果引脚控制设备尚未可用
		 * - 失败:如果设置是 hog 类型并且引脚控制设备尚不可用
		 * 如果返回的错误不是 -EPROBE_DEFER,则积累错误
		 * 以检查是否有 -EPROBE_DEFER 的情况,因为那是最糟糕的情况。
		 */
		if (ret == -EPROBE_DEFER) {
			pinctrl_free(p, false);     // 释放 pinctrl 结构体
			mutex_unlock(&pinctrl_maps_mutex);  // 解锁
			return ERR_PTR(ret);        // 返回延迟错误指针
		}
	}
	mutex_unlock(&pinctrl_maps_mutex);  // 解锁映射互斥量

	if (ret < 0) {
		/* 如果发生延迟以外的其他错误,返回错误 */
		pinctrl_free(p, false);
		return ERR_PTR(ret);
	}

	kref_init(&p->users);            // 初始化引用计数

	/* 将 pinctrl 句柄添加到全局列表中 */
	mutex_lock(&pinctrl_list_mutex);
	list_add_tail(&p->node, &pinctrl_list);
	mutex_unlock(&pinctrl_list_mutex);

	return p;  // 返回创建的引脚控制结构体
}

其中最主要的就是ret = pinctrl_dt_to_map(p);将设备树节点转为mapping,ret = add_setting(p, map);将mapping转为setting并添加进pinctrl结构体中。这个在之前对client端的相关结构体介绍的时候也有讲解过,下面来看看代码中是如何实现的:

2.2.1 转mapping

\Linux-4.9.88\drivers\pinctrl\devicetree.c
int pinctrl_dt_to_map(struct pinctrl *p)
{
	struct device_node *np = p->dev->of_node;   // 获取设备树节点
	int state, ret;
	char *propname;
	struct property *prop;
	const char *statename;
	const __be32 *list;
	int size, config;
	phandle phandle;
	struct device_node *np_config;

	/* CONFIG_OF 启用时,p->dev 可能没有从 DT 中实例化 */
	if (!np) {
		if (of_have_populated_dt())
			dev_dbg(p->dev, "no of_node; not parsing pinctrl DT\n");
		return 0;
	}

	// 断言引脚控制设置
	ret = dt_gpio_assert_pinctrl(p);
	if (ret) {
		dev_dbg(p->dev, "failed to assert pinctrl setting: %d\n", ret);
		return ret;
	}

	// 获取节点引用
	of_node_get(np);

	/* 遍历每个状态 ID */
	for (state = 0; ; state++) {
		// 获取当前状态的引脚控制属性名,如 "pinctrl-0", "pinctrl-1" 等
		propname = kasprintf(GFP_KERNEL, "pinctrl-%d", state);
		prop = of_find_property(np, propname, &size);   // 查找属性
		kfree(propname);
		if (!prop) {
			// 若状态为 0 但找不到属性,说明没有定义
			if (state == 0) {
				of_node_put(np);
				return -ENODEV;
			}
			break;   // 没有更多状态
		}
		list = prop->value;       // 获取属性值列表
		size /= sizeof(*list);    // 计算列表中的项数

		// 从 "pinctrl-names" 中读取状态名
		ret = of_property_read_string_index(np, "pinctrl-names", state, &statename);
		/*
		 * 如果没有在 `pinctrl-names` 中找到状态名,将状态名设置为 `pinctrl-*`
		 * 属性名中的 ID 值。例如,"pinctrl-0" 中的 `0` 可以作为状态名
		 */
		if (ret < 0) {
			// 跳过 "pinctrl-" 的 8 个字符,使用属性名中的 ID
			statename = prop->name + 8;
		}

		// 遍历每个引用的引脚配置节点
		for (config = 0; config < size; config++) {
			phandle = be32_to_cpup(list++);    // 获取 phandle 值

			// 查找引脚配置节点 ---- 下图中标注  1
			np_config = of_find_node_by_phandle(phandle);
			if (!np_config) {
				dev_err(p->dev, "prop %s index %i invalid phandle\n",
				        prop->name, config);
				ret = -EINVAL;
				goto err;
			}

			// 解析引脚配置节点,并创建对应的映射 --- 下图中标注  2
			ret = dt_to_map_one_config(p, statename, np_config);
			of_node_put(np_config);
			if (ret < 0)
				goto err;
		}

		// 如果 DT 中没有项,生成一个空状态表项
		if (!size) {
			ret = dt_remember_dummy_state(p, statename);
			if (ret < 0)
				goto err;
		}
	}

	return 0;

err:
	pinctrl_dt_free_maps(p);  // 如果有错误,释放分配的映射
	return ret;
}

img

来看看ret = dt_to_map_one_config(p, statename, np_config);是如何解析设备树节点中的引脚,转为map。

dt_to_map_one_config 函数用于解析一个设备树中的引脚配置节点 (np_config),并将其转换为内核 pinctrl 映射表。这个过程涉及查找引脚控制器设备 (pinctrl_dev),并调用设备特定的解析函数来生成映射表。 其中最重要的就是调用到了pinctrl_desc->pinctrl_ops->dt_node_to_map,这个在之前的pincontroller的数据结构讲解中有提过(Pinctrl子系统pinctrl_desc结构体进一步介绍)。接下来看代码:

\Linux-4.9.88\Linux-4.9.88\drivers\pinctrl\devicetree.c

static int dt_to_map_one_config(struct pinctrl *p, const char *statename,
				struct device_node *np_config)
{
	struct device_node *np_pctldev;
	struct pinctrl_dev *pctldev;
	const struct pinctrl_ops *ops;
	int ret;
	struct pinctrl_map *map;
	unsigned num_maps;

	/* 查找包含 np_config 的引脚控制器节点 */
	np_pctldev = of_node_get(np_config);  // 获取配置节点的引用
	for (;;) {
		np_pctldev = of_get_next_parent(np_pctldev);  // 获取上级节点
		if (!np_pctldev || of_node_is_root(np_pctldev)) {
			dev_info(p->dev, "could not find pctldev for node %s, deferring probe\n",
				np_config->full_name);
			of_node_put(np_pctldev);  // 释放节点引用
			/* 如果未找到引脚控制器,假设稍后会出现 */
			return -EPROBE_DEFER;
		}
		pctldev = get_pinctrl_dev_from_of_node(np_pctldev);  // 获取引脚控制器设备
		if (pctldev)
			break;  // 找到引脚控制器设备,退出循环

		/* 不要延迟对 hog 配置的探测(避免循环依赖) */
		if (np_pctldev == p->dev->of_node) {
			of_node_put(np_pctldev);
			return -ENODEV;
		}
	}
	of_node_put(np_pctldev);  // 释放父节点的引用

	/*
	 * 调用引脚控制器驱动解析设备树节点,并生成映射表项
	 */
	ops = pctldev->desc->pctlops;  // 获取引脚控制器的操作函数
	if (!ops->dt_node_to_map) {
		dev_err(p->dev, "pctldev %s doesn't support DT\n",
			dev_name(pctldev->dev));
		return -ENODEV;
	}
	ret = ops->dt_node_to_map(pctldev, np_config, &map, &num_maps);  // 解析配置
	if (ret < 0)
		return ret;

	/* 将映射表项保存以备后用 */
	return dt_remember_or_free_map(p, statename, pctldev, map, num_maps);
}
  • 查找引脚控制器设备:函数首先查找与 np_config 对应的引脚控制器节点。通过向上遍历父节点,找到第一个包含配置的 pinctrl_dev,这是引脚控制器的核心设备。如果找不到合适的引脚控制器,返回 -EPROBE_DEFER 表示稍后再次尝试探测。
  • 避免循环依赖:在遍历父节点时,如果发现父节点是当前设备自身(hog 配置),则返回 -ENODEV 以避免循环依赖。
  • 解析设备树节点:找到引脚控制器设备后,通过 pctlops->dt_node_to_map 调用设备特定的函数来解析 np_config 节点并生成映射表项。
  • 存储映射表项:最后,通过 dt_remember_or_free_map 函数将生成的映射表项存储到 pinctrl 结构体中,以便后续使用。

ret = ops->dt_node_to_map(pctldev, np_config, &map, &num_maps);调用的是哪些map函数呢????这就得去Pincontroller的驱动程序中去看了,因为这个函数是pinctrl_desc->pinctrl_ops的,是属于Pincontroller的:\Linux-4.9.88\drivers\pinctrl\freescale\pinctrl-imx.c📎pinctrl-imx.c

static const struct pinctrl_ops imx_pctrl_ops = {
	.get_groups_count = imx_get_groups_count,
	.get_group_name = imx_get_group_name,
	.get_group_pins = imx_get_group_pins,
	.pin_dbg_show = imx_pin_dbg_show,
	.dt_node_to_map = imx_dt_node_to_map,  //就是这个函数
	.dt_free_map = imx_dt_free_map,
};
static int imx_dt_node_to_map(struct pinctrl_dev *pctldev,
			struct device_node *np,
			struct pinctrl_map **map, unsigned *num_maps)
{
	struct imx_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev);
	const struct imx_pinctrl_soc_info *info = ipctl->info;
	const struct imx_pin_group *grp;
	struct pinctrl_map *new_map;
	struct device_node *parent;
	int map_num = 1;
	int i, j;

	/*
	 * 首先找到该节点的引脚组,并检查是否需要为引脚创建配置映射
	 */
	grp = imx_pinctrl_find_group_by_name(info, np->name);
	if (!grp) {
		dev_err(info->dev, "unable to find group for node %s\n",
			np->name);
		return -EINVAL;
	}

	// 确定所需的映射数量
	if (info->flags & IMX8_USE_SCU) {
		map_num += grp->npins;
	} else {
		for (i = 0; i < grp->npins; i++) {
			if (!(grp->pins[i].pin_conf.pin_memmap.config &
			    IMX_NO_PAD_CTL))
				map_num++;
		}
	}

	// 分配内存存储新映射
	new_map = kmalloc(sizeof(struct pinctrl_map) * map_num, GFP_KERNEL);
	if (!new_map)
		return -ENOMEM;

	*map = new_map;
	*num_maps = map_num;

	/* 创建复用映射 */
	parent = of_get_parent(np);
	if (!parent) {
		kfree(new_map);
		return -EINVAL;
	}
	new_map[0].type = PIN_MAP_TYPE_MUX_GROUP;
	new_map[0].data.mux.function = parent->name;
	new_map[0].data.mux.group = np->name;
	of_node_put(parent);

	/* 创建配置映射 */
	new_map++;
	for (i = j = 0; i < grp->npins; i++) {
		if (info->flags & IMX8_USE_SCU) {
			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 =
				(unsigned long *)&grp->pins[i].pin_conf.pin_scu.mux;
			new_map[j].data.configs.num_configs = 2;
			j++;
		} else if (!(grp->pins[i].pin_conf.pin_memmap.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].pin_conf.pin_memmap.config;
			new_map[j].data.configs.num_configs = 1;
			j++;
		}
	}

	dev_dbg(pctldev->dev, "maps: function %s group %s num %d\n",
		(*map)->data.mux.function, (*map)->data.mux.group, map_num);

	return 0;
}

imx_dt_node_to_map 函数用于将指定设备树节点 (np) 转换为 pinctrl_map 映射表,并将其存储在 map 指针中,供引脚控制子系统使用。此函数特定于 i.MX 系列硬件平台,通过设备树节点的信息生成引脚配置和复用映射。

  1. 获取引脚组

    • 使用 imx_pinctrl_find_group_by_name 函数,根据节点名称 (np->name) 查找对应的引脚组 (grp)。
  2. 计算映射数量

    • 如果 info->flags 标记中设置了 IMX8_USE_SCU,则每个引脚都需要一个配置映射,因此总映射数为 1 + grp->npins
    • 否则,遍历每个引脚,仅为需要配置的引脚增加映射数。
  3. 内存分配

    • 使用 kmalloc 为映射数组分配内存,并初始化 mapnum_maps 指针。
    • 如果内存分配失败,函数返回 -ENOMEM 错误。
  4. 创建复用映射

    • 获取父节点,检查其名称并为其创建复用 (MUX) 映射。
    • 将父节点的 name 作为 function,当前节点的 name 作为 group
  5. 创建配置映射

    • 遍历引脚组中的每个引脚,若符合条件,则创建配置映射。
    • 使用 pin_get_name 函数获取引脚名称,并设置相应的配置。
    • IMX8_USE_SCU 标志影响配置内容;如果未设置此标志,则仅为未设置 IMX_NO_PAD_CTL 的引脚创建配置。
  6. 调试信息

    • 使用 dev_dbg 输出映射的 functiongroup 和映射数量 (map_num) 以供调试。

请添加图片描述

2.2.2 mapping转setting

那么解析获取到mapping后,就要去转化为setting,回到create_pinctrl函数:

\Linux-4.9.88\drivers\pinctrl\core.c
/static struct pinctrl *create_pinctrl(struct device *dev)
{
	struct pinctrl *p;               // 指向创建的引脚控制结构体
	const char *devname;             // 设备的名称
	struct pinctrl_maps *maps_node;  // 引脚映射节点指针
	int i;                           // 循环计数器
	struct pinctrl_map const *map;   // 当前的引脚映射
	int ret;                         // 存储返回值,用于错误检查
    //...............

	// 将设备树映射到引脚控制结构体中
	ret = pinctrl_dt_to_map(p);

	devname = dev_name(dev);          // 获取设备名称

	mutex_lock(&pinctrl_maps_mutex);  // 加锁以保护全局映射
	/* 遍历所有引脚控制映射以找到合适的映射 */
	for_each_maps(maps_node, i, map) {
		/* 检查映射是否属于当前设备 */
		if (strcmp(map->dev_name, devname))
			continue;

		// 将映射添加到引脚控制结构体中
		ret = add_setting(p, map);
	
	}

     //...............
	
}

直接省略掉其它内容,对于pinctrl_dt_to_map在上文讲到过,那么接下来就是add_setting函数,把每一个pinctrl_map转换为pinctrl_setting:

static int add_setting(struct pinctrl *p, struct pinctrl_map const *map)
{
	struct pinctrl_state *state;
	struct pinctrl_setting *setting;
	int ret;

	// 查找指定状态,如果不存在则创建一个新的状态
	state = find_state(p, map->name);
	if (!state)
		state = create_state(p, map->name);
	if (IS_ERR(state))
		return PTR_ERR(state);

	// 如果映射类型是 PIN_MAP_TYPE_DUMMY_STATE,则无需添加设置,直接返回
	if (map->type == PIN_MAP_TYPE_DUMMY_STATE)
		return 0;

	// 为 pinctrl_setting 结构体分配内存,用于存储此设置的信息
	setting = kzalloc(sizeof(*setting), GFP_KERNEL);
	if (setting == NULL) {
		dev_err(p->dev,
			"failed to alloc struct pinctrl_setting\n");
		return -ENOMEM;
	}

	// 设置类型,表示这是一个复用组或引脚配置
	setting->type = map->type;

	// 获取映射中指定的 pinctrl_dev(引脚控制设备)
	setting->pctldev = get_pinctrl_dev_from_devname(map->ctrl_dev_name);
	if (setting->pctldev == NULL) {
		// 如果设备名称无法解析,释放内存并判断是否需要延迟加载驱动
		kfree(setting);

		// 对于 hog 引脚,不允许延迟加载,防止循环依赖
		if (!strcmp(map->ctrl_dev_name, map->dev_name))
			return -ENODEV;

		// 设备驱动还未加载,输出提示信息并返回 -EPROBE_DEFER 表示延迟加载
		dev_info(p->dev, "unknown pinctrl device %s in map entry, deferring probe",
			map->ctrl_dev_name);
		return -EPROBE_DEFER;
	}

	// 设置设备名称
	setting->dev_name = map->dev_name;

	// 根据映射类型,将映射转换为具体的设置内容
	switch (map->type) {
	case PIN_MAP_TYPE_MUX_GROUP:
		// 如果是引脚复用组,调用 pinmux_map_to_setting 进行设置转换
		ret = pinmux_map_to_setting(map, setting);
		break;
	case PIN_MAP_TYPE_CONFIGS_PIN:
	case PIN_MAP_TYPE_CONFIGS_GROUP:
		// 如果是引脚或组的配置映射,调用 pinconf_map_to_setting 进行设置转换
		ret = pinconf_map_to_setting(map, setting);
		break;
	default:
		// 其他类型无效,返回错误
		ret = -EINVAL;
		break;
	}
	if (ret < 0) {
		// 如果设置转换失败,释放分配的内存并返回错误码
		kfree(setting);
		return ret;
	}

	// 将新的设置添加到状态的设置列表末尾
	list_add_tail(&setting->node, &state->settings);

	return 0;
}

主要就是pinmux_map_to_settingpinconf_map_to_setting函数, 根据映射类型(复用组、引脚配置等),调用相应的函数 (pinmux_map_to_settingpinconf_map_to_setting) 将映射转换为具体的引脚设置。 pinmux和pinconf在Pinccontroller结构体的讲解中也讲过

先来看pinmux_map_to_setting函数, 将一个 pinctrl_map(引脚控制映射)条目中的复用功能映射转换为一个具体的 pinctrl_setting 设置,用于配置指定设备的引脚复用功能。

  • 设备和操作集获取:获取 pinctrl_dev(引脚控制设备)和 pinmux_ops 操作集,用于查询功能对应的引脚组并设置引脚复用。
  • 功能选择器索引转换:将 map->data.mux.function 中指定的功能名称转换为功能选择器索引,并存储在 setting->data.mux.func 中。
  • 功能支持组查询:通过 pmxops->get_function_groups 函数获取该功能支持的引脚组列表和数量,确保该功能可以选择特定的引脚组。
  • 引脚组验证:如果映射条目中指定了具体的引脚组名称,则检查该组是否在功能的支持列表中;如果未指定,则使用默认的第一个组。
  • 组选择器索引转换:将引脚组名称转换为组选择器索引,并存储在 setting->data.mux.group 中。
  • 返回设置:将转换后的功能和组选择器索引存入 pinctrl_setting,用于后续的引脚控制配置操作。如果过程中发生错误,则返回相应的错误码。
\Linux-4.9.88\drivers\pinctrl\pinmux.c
int pinmux_map_to_setting(struct pinctrl_map const *map,
			  struct pinctrl_setting *setting)
{
	// 获取 pinctrl_dev 设备和 pinmux_ops 操作集,用于执行引脚复用操作
	struct pinctrl_dev *pctldev = setting->pctldev;
	const struct pinmux_ops *pmxops = pctldev->desc->pmxops;
	char const * const *groups;  // 存储功能所支持的组名
	unsigned num_groups;         // 功能所支持的组的数量
	int ret;
	const char *group;

	// 如果设备不支持引脚复用操作,返回错误
	if (!pmxops) {
		dev_err(pctldev->dev, "does not support mux function\n");
		return -EINVAL;
	}

	// 将功能名称转换为功能选择器索引
	ret = pinmux_func_name_to_selector(pctldev, map->data.mux.function);
	if (ret < 0) {
		dev_err(pctldev->dev, "invalid function %s in map table\n",
			map->data.mux.function);
		return ret;
	}
	setting->data.mux.func = ret;  // 设置功能选择器

	// 查询该功能对应的引脚组,获取组的列表和组数
	ret = pmxops->get_function_groups(pctldev, setting->data.mux.func,
					  &groups, &num_groups);
	if (ret < 0) {
		dev_err(pctldev->dev, "can't query groups for function %s\n",
			map->data.mux.function);
		return ret;
	}

	// 如果功能不支持任何引脚组,返回错误
	if (!num_groups) {
		dev_err(pctldev->dev,
			"function %s can't be selected on any group\n",
			map->data.mux.function);
		return -EINVAL;
	}

	// 如果映射指定了特定的引脚组,验证该组是否存在于支持的组列表中
	if (map->data.mux.group) {
		group = map->data.mux.group;
		ret = match_string(groups, num_groups, group);
		if (ret < 0) {
			dev_err(pctldev->dev,
				"invalid group \"%s\" for function \"%s\"\n",
				group, map->data.mux.function);
			return ret;
		}
	} else {
		// 如果未指定引脚组,则使用默认的第一个组
		group = groups[0];
	}

	// 将组名转换为组选择器索引
	ret = pinctrl_get_group_selector(pctldev, group);
	if (ret < 0) {
		dev_err(pctldev->dev, "invalid group %s in map table\n",
			map->data.mux.group);
		return ret;
	}
	setting->data.mux.group = ret;  // 设置组选择器

	return 0;  // 成功返回 0
}

再来看看pinconf_map_to_setting函数, 将 pinctrl_map(引脚控制映射)中的引脚或引脚组的配置映射转换为 pinctrl_setting 设置,用于对指定设备的引脚或引脚组进行特定的配置

  • 设备获取:从 pinctrl_setting 中获取 pinctrl_dev 设备,用于引脚或引脚组的配置映射。

  • 配置类型判断:根据 setting->type 判断配置类型。

    • 单引脚配置 (PIN_MAP_TYPE_CONFIGS_PIN):根据引脚名称获取引脚索引,并存储在 setting->data.configs.group_or_pin 字段中。
    • 引脚组配置 (PIN_MAP_TYPE_CONFIGS_GROUP):根据引脚组名称获取引脚组选择器索引,并存储在 setting->data.configs.group_or_pin 字段中。
  • 配置项存储:将 map 中的配置数量和配置数组存储到 setting 中,以便后续的配置应用。

  • 返回状态:如果成功执行映射和存储,则返回 0;如果引脚或组未找到,或类型无效,则返回相应的错误码。

int pinconf_map_to_setting(struct pinctrl_map const *map,
			  struct pinctrl_setting *setting)
{
	// 获取与当前设置相关联的 pinctrl_dev 设备
	struct pinctrl_dev *pctldev = setting->pctldev;
	int pin;

	// 根据配置类型执行相应的操作
	switch (setting->type) {
	case PIN_MAP_TYPE_CONFIGS_PIN:  // 单个引脚配置
		// 根据引脚名称获取引脚索引
		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;
		}
		// 将引脚索引存储在设置的 group_or_pin 字段中
		setting->data.configs.group_or_pin = pin;
		break;
	case PIN_MAP_TYPE_CONFIGS_GROUP:  // 引脚组配置
		// 根据引脚组名称获取引脚组选择器索引
		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;
		}
		// 将组选择器索引存储在设置的 group_or_pin 字段中
		setting->data.configs.group_or_pin = pin;
		break;
	default:
		// 如果配置类型无效,返回 -EINVAL 错误码
		return -EINVAL;
	}

	// 设置配置项的数量和配置数组
	setting->data.configs.num_configs = map->data.configs.num_configs;
	setting->data.configs.configs = map->data.configs.configs;

	return 0;  // 成功返回 0
}

3.切换state情景分析

这一部分看下图简单了解一下就行了。

img

really_probe
	pinctrl_bind_pins
		pinctrl_select_state
			/* Apply all the settings for the new state */
			list_for_each_entry(setting, &state->settings, node) {
				switch (setting->type) {
				case PIN_MAP_TYPE_MUX_GROUP:
					ret = pinmux_enable_setting(setting);
							ret = ops->set_mux(...);
				break;
				case PIN_MAP_TYPE_CONFIGS_PIN:
				case PIN_MAP_TYPE_CONFIGS_GROUP:
					ret = pinconf_apply_setting(setting);
							ret = ops->pin_config_group_set(...);
					break;
				default:
					ret = -EINVAL;
				break;
			}	

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2230975.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

初探Flink的序列化

Flink中的序列化应用场景 程序通常使用(至少)两种不同的数据表示形式[2]&#xff1a; 1. 在内存中&#xff0c;数据保存在对象、结构体、列表、数组、哈希表和树等结构中。 2. 将数据写入文件或通过网络发送时&#xff0c;必须将其序列化为字节序列。 从内存中的表示到字节序列…

项目一:使用 Spring + SpringMVC + Mybatis + lombok 实现网络五子棋

一&#xff1a;系统展示: 二&#xff1a;约定前后端接口 2.1 登陆 登陆请求&#xff1a; GET /login HTTP/1.1 Content-Type: application/x-www-form-urlencodedusernamezhangsan&password123登陆响应&#xff1a; 正常对象&#xff1a;正常对象会在数据库中存储&…

CentOS7配置静态IP(非解决方法指导,纯笨蛋记录)

一、这篇博客算是记录我终于解决我安装的虚拟机ping不通外网的问题&#xff0c;前前后后我尝试了很多次花了很多时间&#xff0c;最后弄完发现其实都是因为我之前根本不知道什么是虚拟机的网络配置。。。。。 这个链接介绍了vmware虚拟机三种网络模式及配置详解_vmware 特定虚…

opencv-day2-图像预处理1

图像预处理 在计算机视觉和图像处理领域&#xff0c;图像预处理能够提高后续处理&#xff08;如特征提取、目标检测等&#xff09;的准确性和效率。 常见的图像预处理操作&#xff1a; 图像色彩空间转换 图像大小调整 图像仿射变换 图像翻转 图像裁剪 图像二值化处理 图…

3DDFA-V3——基于人脸分割几何信息指导下的三维人脸重建

1. 研究背景 从二维图像中重建三维人脸是计算机视觉研究的一项关键任务。在虚拟现实、医疗美容、计算机生成图像等领域中&#xff0c;研究人员通常依赖三维可变形模型&#xff08;3DMM&#xff09;进行人脸重建&#xff0c;以定位面部特征和捕捉表情。然而&#xff0c;现有的方…

Ubuntu系统如何实现键盘按键映射到其他按键(以 Ctrl+c 映射到 F3,Ctrl+v 映射到 F4 为例)

文章目录 写在前面1. 功能描述2. 实现步骤2.1 安装AutoKey2.2 软件设置2.2.1 软件设置 2.3 测试是否安装成功 参考链接 写在前面 自己的测试环境&#xff1a; Ubuntu20.04 1. 功能描述 Ubuntu系统使用Ctrlc 、Ctrlv 进行复制粘贴操作的时候&#xff0c;时间长了就会出现小拇指…

【Clickhouse】客户端连接工具配置

ClickHouse 是什么 ClickHouse 是一个分布式实时分析型列式存储数据库。具备高性能&#xff0c;支撑PB级数据&#xff0c;提供实时分析&#xff0c;稳定可扩展等特性。适用于数据仓库、BI报表、监控系统、互联网用户行为分析、广告投放业务以及工业、物联网等分析和时序应用场…

postman的脚本设置接口关联

pm常用的对象 变量基础知识 postman获取响应结果的脚本的编写 下面是购物场景存在接口信息的关联 登录进入---搜索商品---进入商品详情---加入购物车 资源在附件中&#xff0c;可以私聊单独发送 postman的SHA256加密 var CryptoJS require(crypto-js);// 需要加密的字符串 …

Qt 文件目录操作

Qt 文件目录操作 QDir 类提供访问系统目录结构 QDir 类提供对目录结构及其内容的访问。QDir 用于操作路径名、访问有关路径和文件的信息以及操作底层文件系统。它还可以用于访问 Qt 的资源系统。 Qt 使用“/”作为通用目录分隔符&#xff0c;与“/”在 URL 中用作路径分隔符…

qt QCheckBox详解

QCheckBox 是 Qt 框架中的一个控件&#xff0c;用于创建复选框&#xff0c;允许用户进行选择和取消选择。它通常用于表单、设置界面和任何需要用户选择的场景。 QCheckBox继承自QAbstractButton类&#xff0c;因此继承了按钮的特性。它表示一个复选框&#xff0c;用户可以通过…

读数据工程之道:设计和构建健壮的数据系统26数据建模

1. 数据建模 1.1. 良好的数据架构必须反映出使用这些数据的组织的业务目标和业务逻辑 1.2. 数据湖1.0、NoSQL和大数据系统的兴起&#xff0c;使工程师们有时是为了合理的性能提升去忽略传统的数据建模 1.3. 数据在企业中的地位急剧上升&#xff0c;人们越来越认识到&#xf…

2025生物发酵展(济南)为生物制造产业注入新活力共谱行业新篇章

2025第十四届国际生物发酵展将于3月3-5日济南盛大举办&#xff01;产业链逐步完整&#xff0c;展会面积再创历史新高&#xff0c;展览面积较上届增涨至60000平方米&#xff0c;专业观众40000&#xff0c;品牌展商800&#xff0c;同期活动会议增加至50场&#xff0c;展会同期将举…

Windows版 nginx安装,启动,目录解析,常用命令

Windows版 nginx安装&#xff0c;启动&#xff0c;目录解析&#xff0c;常用命令 一级目录二级目录三级目录 1. 下载2. 启动方式一&#xff1a;方式二&#xff1a; 3. 验证是否启动4. 安装目录解析5. 常用命令 一级目录 二级目录 三级目录 1. 下载 官网下载&#xff1a;ngi…

【maven】idea执行了maven的install命令给本地安装项目依赖包 安装后删除

目录 事件起因环境和工具操作过程解决办法1、找到对应的目录下的文件&#xff0c;手动去删除&#xff0c;比如我的依赖库的路径是D:\qc_code\apache-maven-3.8.2\repository 我只需要找到这个目录下对应的依赖包进行手动删除即可&#xff08;不推荐&#xff0c;强行删除文件夹文…

技术选型不当对项目的影响与补救措施

在项目管理中&#xff0c;初期技术选型与项目需求不匹配的情况并不罕见&#xff0c;这可能导致项目延误、成本增加和最终成果的不理想。补救的关键措施包括&#xff1a;重新评估技术选型、加强团队沟通、实施有效的需求管理以及建立持续的反馈机制。其中&#xff0c;重新评估技…

基于SSM+VUE宠物医院后台管理系统JAVA|VUE|Springboot计算机毕业设计源代码+数据库+LW文档+开题报告+答辩稿+部署教+代码讲解

源代码数据库LW文档&#xff08;1万字以上&#xff09;开题报告答辩稿 部署教程代码讲解代码时间修改教程 一、开发工具、运行环境、开发技术 开发工具 1、操作系统&#xff1a;Window操作系统 2、开发工具&#xff1a;IntelliJ IDEA或者Eclipse 3、数据库存储&#xff1a…

MySQL 8.0在windows环境安装及配置

文章目录 一、下载二、安装三、配置环境变量 一、下载 1、先彻底卸载之前的MySQL&#xff0c;并清理其 残留文件 。 2、登录网址https://www.mysql.com/ 3、点击网址左下角“中文”按钮&#xff0c;切换到中文界面 4、点击网页上方的“下载”按钮&#xff0c;然后点击网页…

【传知代码】用于图像识别的判别图正则化技术

&#x1f351;个人主页&#xff1a;Jupiter. &#x1f680; 所属专栏&#xff1a;传知代码 欢迎大家点赞收藏评论&#x1f60a; 目录 论文概述图正则化技术及其优点参考文献&#xff1a; 算法流程在标准BLS中嵌入判别图正则化的方法 模型整体架构代码复现图拉普拉斯矩阵的构建—…

第二十五章 Vue父子通信之sync修饰符

目录 一、概述 二、完整代码 2.1. main.js 2.2. App.vue 2.3. BaseDialog.vue 三、运行效果 一、概述 前面的章节我们讲到&#xff0c;通过v-model我们可以实现父子组件间的通信&#xff0c;但是使用v-model的时候&#xff0c;子组件接收的prop属性名必须固定为valu…

头歌——机器学习(逻辑回归)

文章目录 逻辑回归简述代码 sklearn逻辑回归 - 手写数字识别代码 逻辑回归算法详解似然与概率的区别逻辑回归算法的代码实现代码 逻辑回归案例 - 癌细胞精准识别代码 逻辑回归简述 什么是逻辑回归 当一看到“回归”这两个字&#xff0c;可能会认为逻辑回归是一种解决回归问题的…