Pinctrl子需要中client端使用pinctrl过程的驱动分析
往期内容
本专栏往期内容:
- Pinctrl子系统和其主要结构体引入
- Pinctrl子系统pinctrl_desc结构体进一步介绍
- Pinctrl子系统中client端设备树相关数据结构介绍和解析
- inctrl子系统中Pincontroller构造过程驱动分析:imx_pinctrl_soc_info结构体
input子系统专栏:
- 专栏地址:input子系统
- input角度:I2C触摸屏驱动分析和编写一个简单的I2C驱动程序
– 末片,有往期内容观看顺序I2C子系统专栏:
- 专栏地址:IIC子系统
- 具体芯片的IIC控制器驱动程序分析:i2c-imx.c-CSDN博客
– 末篇,有往期内容观看顺序总线和设备树专栏:
- 专栏地址:总线和设备树
- 设备树与 Linux 内核设备驱动模型的整合-CSDN博客
– 末篇,有往期内容观看顺序
前言
本节就不提供相关源码文件了,各个代码块处都有标明该函数的文件路径,需要的可以自行去查看。
主要讲解作为使用者来说去使用pinctrl,其相关驱动程序是如何去进行获取pinctrl信息,对其进行解析将引脚转为map,在转为setting存储起来,去进行使用,也就是其如何去配置、复用引脚的。
1.回顾client的数据结构
看之前的文章:Pinctrl子系统中client端设备树相关数据结构介绍和解析
右侧是Pinctrl节点,左侧是client端节点。Pinctrl节点中设置了要使用的引脚的信息以及复用的功能,client节点则是对要使用的引脚进行引用,其实就是模块的设备节点。
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 总图
比较长,看下图就行了。
really_probe pinctrl_bind_pinsdev->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.cdev->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 个字符,使用属性名中的 IDstatename = prop->name + 8;}// 遍历每个引用的引脚配置节点for (config = 0; config < size; config++) {phandle = be32_to_cpup(list++); // 获取 phandle 值// 查找引脚配置节点 ---- 下图中标注 1np_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;}// 解析引脚配置节点,并创建对应的映射 --- 下图中标注 2ret = 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;
}
来看看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.cstatic 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 系列硬件平台,通过设备树节点的信息生成引脚配置和复用映射。
-
获取引脚组:
- 使用
imx_pinctrl_find_group_by_name
函数,根据节点名称 (np->name
) 查找对应的引脚组 (grp
)。
- 使用
-
计算映射数量:
- 如果
info->flags
标记中设置了IMX8_USE_SCU
,则每个引脚都需要一个配置映射,因此总映射数为1 + grp->npins
。 - 否则,遍历每个引脚,仅为需要配置的引脚增加映射数。
- 如果
-
内存分配:
- 使用
kmalloc
为映射数组分配内存,并初始化map
和num_maps
指针。 - 如果内存分配失败,函数返回
-ENOMEM
错误。
- 使用
-
创建复用映射:
- 获取父节点,检查其名称并为其创建复用 (
MUX
) 映射。 - 将父节点的
name
作为function
,当前节点的name
作为group
。
- 获取父节点,检查其名称并为其创建复用 (
-
创建配置映射:
- 遍历引脚组中的每个引脚,若符合条件,则创建配置映射。
- 使用
pin_get_name
函数获取引脚名称,并设置相应的配置。 IMX8_USE_SCU
标志影响配置内容;如果未设置此标志,则仅为未设置IMX_NO_PAD_CTL
的引脚创建配置。
-
调试信息:
- 使用
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);}//...............}
直接省略掉其它内容,对于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_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
}
再来看看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
字段中。
- 单引脚配置 (PIN_MAP_TYPE_CONFIGS_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情景分析
这一部分看下图简单了解一下就行了。
really_probepinctrl_bind_pinspinctrl_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;}
相关文章:

Pinctrl子需要中client端使用pinctrl过程的驱动分析
往期内容 本专栏往期内容: Pinctrl子系统和其主要结构体引入Pinctrl子系统pinctrl_desc结构体进一步介绍Pinctrl子系统中client端设备树相关数据结构介绍和解析inctrl子系统中Pincontroller构造过程驱动分析:imx_pinctrl_soc_info结构体 input子系统专栏…...

【网络】传输层协议TCP
目录 四位首部长度 序号 捎带应答 标记位 超时重传机制 连接管理机制(RST标记位) 三次握手及四次挥手的原因 TCP的全称是传输控制协议(Transmission Control Protocol),也就是说,对于放到TCP发送缓冲…...

00-开发环境 MPLAB IDE 配置
MPLAB IDE V8.83 File 菜单简介 New (CtrlN): 创建一个新文件,用于编写新的代码。 Add New File to Project...: 将新文件添加到当前项目中。 Open... (CtrlO): 打开现有文件。 Close (CtrlE): 关闭当前打开的文件。 …...

<meta property=“og:type“ content=“website“>
<meta property"og:type" content"website"> 这段代码是HTML中的一部分,具体来说,它是一个用于定义Open Graph协议的meta标签。 代码分析 <meta> 标签:这是一个HTML标签,用于在HTML文档的头…...

C++ 实现俄罗斯方块游戏
✅作者简介:2022年博客新星 第八。热爱国学的Java后端开发者,修心和技术同步精进。 🍎个人主页:Java Fans的博客 🍊个人信条:不迁怒,不贰过。小知识,大智慧。 💞当前专栏…...

QT打包Macosx应用发布App Store简易流程
1、QC里编译工程,生成Release版的的app文件; 2、运行macdeployqt把需要的文件打包进app文件中; % ~/Qt/5.15.0/clang_64/bin/macdeployqt {编译的app文件所在路径}/Release/xxxx.app 3、使用codesign对app进行签名,如果要发App…...

untiy mlagents 飞机大战 ai训练
前言 之前那个python源码的飞机大战bug过多,还卡顿,难以继续训练。可直接放弃的话又不甘心,所以找了个unity版本的飞机大战继续(终于不卡了),这次直接使用现成的mlagents库。 过程 前前后后花了两周时间,甚至因此拖…...

从0开始学统计-什么是中心极限定理
引言 中心极限定理(Central Limit Theorem, CLT)是统计学中的一块基石,它揭示了一个难以置信的数学现象:无论一个随机变量的原始分布如何,只要我们取足够大的样本量,这些样本的平均值(或总和&a…...

工具方法 - 个人活动的分类
人类活动的分类是一个复杂的话题,因为人类的活动范围非常广泛且相互交叉。然而,我们可以尝试将人类的活动大致分为以下几个主要类别: 工作活动 工作活动是人类生活中不可或缺的一部分,通常包括以下方面: 1. 职业工作&a…...

11.1组会汇报-基于区块链的安全多方计算研究现状与展望
基础知识 *1.背书,这个词源来自银行票据业务,是指票据转让时,原持有人在票据背面加盖自己的印鉴,证明该票据真实有效、如果有问题就可以找原持有人。 区块链中的背书就好理解了。可以简单的理解为验证交易并声明此交易合法&…...

ubuntu【桌面】 配置NAT模式固定IP
DHCP分配导致虚拟机IP老变,SSH老要重新配置,设成静态方便些 一、设NAT模式 1、设为NAT模式 2、看模式对应的虚拟网卡 - VMnet8 3、共享主机网卡网络到虚拟网卡 - VMnet8 二、为虚拟网卡设置静态IP 记住这个IP 三、设置ubuntu固定IP 1、关闭DHCP并…...

评估 机器学习 回归模型 的性能和准确度
回归 是一种常用的预测模型,用于预测一个连续因变量和一个或多个自变量之间的关系。 那么,最后评估 回归模型 的性能和准确度非常重要,可以帮助我们判断模型是否有效并进行改进。 接下来,和大家分享如何评估 回归模型 的性能和准…...

如何下载安装TestLink?
一、下载TestLink、XAMPP TestLink 下载 |SourceForge.net 备用:GitHub - TestLinkOpenSourceTRMS/testlink-code: TestLink开源测试和需求管理系统 下载XAMPP: Download XAMPP 注意:TestLink与PHP版本有关系,所以XA…...

基于SSM+微信小程序的订餐管理系统(点餐2)
👉文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1、项目介绍 基于SSM微信小程序的订餐管理系统实现了管理员和用户。管理端实现了 首页、个人中心、用户管理、菜品分类管理、菜品信息管理、订单信息管理、配送信息管理、菜品评价管理、订单投诉管理、…...

【C++排序 双指针】1996. 游戏中弱角色的数量|1996
本文涉及的基础知识点 排序 C算法:滑动窗口及双指针总结 本题其它解法 【C单调栈 排序】1996. 游戏中弱角色的数量|1996 LeetCode1996. 游戏中弱角色的数量 你正在参加一个多角色游戏,每个角色都有两个主要属性:攻击 和 防御 。给你一个…...

GESP4级考试语法知识(捕捉异常)
参考程序代码: #include <iostream> using namespace std;double divide(double a, double b) {if (b 0) {throw "Division by zero error"; // 抛出异常}return a / b; }int main() {double num1, num2;cout << "Enter two numbers:…...

HTML 基础标签——元数据标签 <meta>
文章目录 1. `<meta>` 标签概述2. 属性详解2.1 `charset` 属性2.2 `name` 属性2.3 `content` 属性2.4 `http-equiv` 属性3. 其他常见属性小结在 HTML 文档中,元数据标签 <meta> 是一种重要的标签,用于提供关于文档的信息,这些信息不直接显示在网页内容中,但对于…...

栈虚拟机和寄存器虚拟机,有什么不同?
本来这节内容是打算直接讲字节码指令的,但讲之前又必须得先讲指令集架构,而指令集架构又分为两种,一种是基于栈的,一种是基于寄存器的。 那不妨我们这节就单独来讲讲栈虚拟机和寄存器虚拟机,它们有什么不同࿰…...

Windows下基于fping进行批量IP测试
fping是Linux下一个很好用的IP测试工具,结合代码可以完成批量的IP测试,在网络调试中用途很广。本文是基于fping for Windows结合bat批处理,定制的测试脚本样例。 一、程序信息 本次测试使用fpingV5.1 for Windows版,版本信息如下…...

一款实用的Word文档图片转换与水印保护工具
目录 前言软件功能简介软件实现方法及关键代码 1. Word 文档转图片的实现2. 图片水印添加功能3. 生成数字指纹(哈希值)4. 保存图片信息到 JSON 文件 软件的实际使用场景软件操作指南 1. 下载和安装2. 操作流程 总结 1,前言 在日常办公和内…...

优化用于传感应用的衬底集成波导技术
ANSYS HFSS 是一款功能强大的电磁仿真软件,支持为微流体生物传感器应用设计和分析衬底集成波导 (SIW) 技术。它为快速设计优化、材料选择、系统集成和虚拟原型制作提供了一个强大的平台。借助 ANSYS HFSS,研究人员和工程师可以高效…...

Java多态特性的向上转型
Java的多态特性通过向上转型来实现。向上转型指的是将子类对象赋值给父类引用变量的操作。这样做的好处是可以使用父类引用变量来调用子类对象的方法。 例如,有一个父类Animal和一个子类Dog,可以这样进行向上转型: Animal animal new Dog(…...

C++ 判断语句的深入解析
C 判断语句的深入解析 C 是一种广泛使用的编程语言,以其高效性和灵活性著称。在 C 中,判断语句是控制程序流程的关键组成部分,它们允许程序根据不同的条件执行不同的代码路径。本文将深入探讨 C 中的判断语句,包括 if、else if、…...

15分钟学 Go 第 33 天:项目结构
第33天:项目结构 目标:了解Go项目的典型结构 在Go语言的开发中,项目结构的合理性直接影响着代码的可维护性、可扩展性和团队协作效率。本篇文章将深入探讨Go语言的典型项目结构,并提供实际示例代码和相关的流程图。 一、Go项目…...

conda迁移虚拟环境路径
方法一:使用软连接 ln -s ~/Anaconda3/envs /new/path/envs 方法二:修改~/.condarc文件 1.打开~/.condarc文件 #添加下面参数 envs_dirs: - /newpath/anaconda3/envs pkgs_dirs: - /newpath/anaconda3/pkgs 2. source ~/.bashrc 3.查看是否成功con…...

(八)JavaWeb后端开发——Tomcat
目录 1.Web服务器概念 2.tomcat 1.Web服务器概念 服务器:安装了服务器软件的计算机服务器软件:接收用户的请求,处理请求,做出响应web服务器软件:在web服务器软件中,可以部署web项目,让用户通…...

yocto中通常不直接修改提供的recipes的bb文件
不直接在 Yocto 官方提供的 recipe 中修改 通常是创建新的 metadata 和 recipe 来配置相关软件编译等过程 主要有以下几个原因: 1. 便于维护和升级 隔离自定义修改:Yocto 官方的 recipe 可能会随着版本更新而变化。如果直接修改官方 recipe࿰…...

智能座舱相关术语全解及多模态交互在智能座舱中的应用
文章目录 座舱相关术语全解1. 智能座舱2. UFS3. 多模态交互4. 3D虚拟引擎5. AR/VR6. GNSS7. TTS8. DPU9. 摄像头10. 屏幕/显示器11. 音频12. 无线连接13. 其他组件 多模态交互在智能座舱中有以下一些应用 座舱相关术语全解 1. 智能座舱 智能座舱(intelligent cabi…...

【Fastjson反序列化漏洞:深入了解与防范】
一、Fastjson反序列化漏洞概述 Fastjson是一款高性能的Java语言JSON处理库,广泛应用于Web开发、数据交换等领域。然而,由于fastjson在解析JSON数据时存在安全漏洞,攻击者可以利用该漏洞执行任意代码,导致严重的安全威胁。 二、F…...

【OJ题解】C++实现反转字符串中的每个单词
💵个人主页: 起名字真南 💵个人专栏:【数据结构初阶】 【C语言】 【C】 【OJ题解】 题目要求:给定一个字符串 s ,你需要反转字符串中每个单词的字符顺序,同时仍保留空格和单词的初始顺序。 题目链接: 反转字符串中的所…...