首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

Pinctrl子系统中client端使用pinctrl过程的驱动分析

  • 25-03-04 14:01
  • 2254
  • 13963
blog.csdn.net

往期内容

本专栏往期内容:

  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>;
    };
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56

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);	
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

进入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;  // 返回创建的引脚控制结构体
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71

其中最主要的就是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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92

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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 查找引脚控制器设备:函数首先查找与 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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87

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 为映射数组分配内存,并初始化 map 和 num_maps 指针。
    • 如果内存分配失败,函数返回 -ENOMEM 错误。
  4. 创建复用映射:

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

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

    • 使用 dev_dbg 输出映射的 function、group 和映射数量 (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);
	
	}

     //...............
	
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

直接省略掉其它内容,对于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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74

主要就是pinmux_map_to_setting和pinconf_map_to_setting函数, 根据映射类型(复用组、引脚配置等),调用相应的函数 (pinmux_map_to_setting 或 pinconf_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
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70

再来看看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
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

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;
			}	
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
文章知识点与官方知识档案匹配,可进一步学习相关知识
C技能树首页概览220410 人正在系统学习中
注:本文转载自blog.csdn.net的憧憬一下的文章"https://blog.csdn.net/caiji0169/article/details/143458408"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

101
推荐
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top