imx_dt_node_to_map
static int imx_dt_node_to_map(struct pinctrl_dev *pctldev,
struct device_node *np,
struct pinctrl_map **map, unsigned *num_maps)
{
// 获取 imx_pinctrl 结构体指针
struct imx_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev);
// 获取 imx_pinctrl_soc_info 结构体指针
const struct imx_pinctrl_soc_info *info = ipctl->info;
// 定义 imx_pin_group 结构体指针
const struct imx_pin_group *grp;
// 定义 pinctrl_map 结构体指针
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, "无法找到节点 %s 的组\n",
np->name);
return -EINVAL;
}
for (i = 0; i < grp->npins; i++) {
// 如果引脚的配置不是 IMX_NO_PAD_CTL,则增加映射数量
if (!(grp->pins[i].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 (!(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++;
}
}
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 的函数,用于将设备树中的节点映射为引脚控制器的配置映射。
以下是该函数的详细分析:
首先,函数从引脚控制器设备结构体 pctldev 中获取指向 imx_pinctrl 结构体的指针 ipctl,以及指向 imx_pinctrl_soc_info 结构体的指针 info。
函数查找节点 np 所属的引脚组,通过调用 imx_pinctrl_find_group_by_name 函数来查找。如果找不到组,则会打印错误信息并返回错误码 -EINVAL。
函数计算需要创建的配置映射的数量。对于引脚组中的每个引脚,如果其配置不是 IMX_NO_PAD_CTL,则增加映射数量。
分配内存空间来存储映射,通过调用 kmalloc 函数分配大小为 sizeof(struct pinctrl_map) * map_num 的内存空间。如果分配失败,则返回错误码 -ENOMEM。
设置输出参数 map 和 num_maps,将分配的内存空间指针和映射数量赋值给它们。
创建复用映射。首先,通过调用 of_get_parent 函数获取父节点的指针 parent。然后,将复用映射的类型设置为 PIN_MAP_TYPE_MUX_GROUP,函数名和组名分别设置为父节点和当前节点的名称。最后,调用 of_node_put 函数释放父节点的引用。
创建配置映射。使用 new_map 指针偏移一个位置,遍历引脚组中的每个引脚。对于每个具有配置的引脚,将配置映射的类型设置为 PIN_MAP_TYPE_CONFIGS_PIN,引脚名设置为引脚的名称,配置数组设置为引脚的配置,配置数量设置为 1。
打印调试信息,表示成功创建了映射。
返回 0,表示成功将节点映射为引脚控制器的配置映射。
综上所述,imx_dt_node_to_map 函数用于根据设备树中的节点创建引脚控制器的配置映射。它会查找节点所属的引脚组,并根据组中的引脚和配置信息创建映射。最后,它将映射存储在内存中,并返回相应的信息。
struct pinctrl_map {
const char *dev_name; // 设备名称,用于指定使用该映射的设备,名称必须与struct device*中的名称相同。如果该名称设置为与引脚控制器自身的dev_name()相同的名称,则该映射条目将在注册时由驱动程序自身占用
const char *name; // 映射条目的名称,用于特定机器的特定映射。这是传递给pinmux_lookup_state()的参数
enum pinctrl_map_type type; // 映射表条目的类型
const char *ctrl_dev_name; // 控制该特定映射的设备的名称,名称必须与struct device*中的名称相同。对于PIN_MAP_TYPE_DUMMY_STATE,此字段不使用
union {
struct pinctrl_map_mux mux; // 映射表类型为MAP_TYPE_MUX_GROUP时的特定数据
struct pinctrl_map_configs configs; // 映射表类型为MAP_TYPE_CONFIGS_*时的特定数据
} data;
};
这段代码定义了结构体 struct pinctrl_map,该结构体用于描述引脚控制器的映射关系。
结构体成员如下:
const char dev_name:设备名称,用于指定使用该映射的设备。名称必须与 struct device 中的名称相同。如果该名称设置为与引脚控制器自身的 dev_name() 相同的名称,则该映射条目将在注册时由驱动程序自身占用。
const char *name:映射条目的名称,用于特定机器的特定映射。这是传递给pinmux_lookup_state() 的参数。
enum pinctrl_map_type type:映射表条目的类型,表示映射的类型。可能的取值为 PIN_MAP_TYPE_MUX_GROUP、PIN_MAP_TYPE_CONFIGS_PIN、PIN_MAP_TYPE_CONFIGS_GROUP 和 PIN_MAP_TYPE_DUMMY_STATE。
const char ctrl_dev_name:控制该特定映射的设备的名称,名称必须与 struct device 中的名称相同。对于 PIN_MAP_TYPE_DUMMY_STATE,此字段不使用。
union data:联合体类型的数据,根据 type 字段的值决定使用哪个字段。
struct pinctrl_map_mux mux:当 type 为 PIN_MAP_TYPE_MUX_GROUP 时使用,包含特定于该类型的数据。
struct pinctrl_map_configs configs:当 type 为 PIN_MAP_TYPE_CONFIGS_PIN 或 PIN_MAP_TYPE_CONFIGS_GROUP 时使用,包含特定于该类型的数据。
struct pinctrl_map 结构体用于描述引脚控制器的映射关系,其中包含了设备名称、映射条目名称、映射类型以及特定类型的数据。这些信息可以用于配置和控制引脚的功能和状态。
enum pinctrl_map_type {
PIN_MAP_TYPE_INVALID, // 无效的映射类型
PIN_MAP_TYPE_DUMMY_STATE, // 虚拟状态映射类型
PIN_MAP_TYPE_MUX_GROUP, // 复用组映射类型
PIN_MAP_TYPE_CONFIGS_PIN, // 引脚配置映射类型
PIN_MAP_TYPE_CONFIGS_GROUP, // 组配置映射类型
};
struct pinctrl_map_configs {
const char *group_or_pin; // 需要配置参数的引脚或者组的名称
unsigned long *configs; // 需要写入硬件的配置参数/值的数组的指针。每个引脚控制器定义配置参数的格式和含义
unsigned num_configs; // 数组configs中的条目数
};
struct pinctrl_map_mux {
const char *group; // 需要配置复用功能的组的名称。此字段可以为空,将使用该功能的第一个适用组。
const char *function; // 选择用于该组的复用功能
};
i
mx6ul-evk {
pinctrl_gpio_keys: gpio-keys {
fsl,pins = <
MX6UL_PAD_UART1_CTS_B__GPIO1_IO18 0x80000000
>;
};
};
gpio_keys: gpio_keys@0 {
compatible = "gpio-keys";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_gpio_keys>;
#address-cells = <1>;
#size-cells = <0>;
autorepeat;
key1@1 {
label = "USER-KEY1";
linux,code = <114>;
gpios = <&gpio1 18 GPIO_ACTIVE_LOW>;
gpio-key,wakeup;
};
};
hoggrp的驱动也符合设备模型,所以设备和驱动匹配成功以后,也会进到probe函数,然后可以对hoggrp读写控制脚进行控制。
所以我们是不是可以猜测,在进到hoggrp驱动的probe函数之前,就调用了pinctrl子系统对hoggrp的读写控制引脚进行了配置呢。
probe函数是在drivers/base/dd.c文件中执行的,找到really_probe函数。我们可以看到以下代码。
static int really_probe(struct device *dev, struct device_driver *drv)
{
int ret = 0;
int local_trigger_count = atomic_read(&deferred_trigger_count);
atomic_inc(&probe_count);
pr_debug("bus: '%s': %s: probing driver %s with device %s\n",
drv->bus->name, __func__, drv->name, dev_name(dev));
WARN_ON(!list_empty(&dev->devres_head));
dev->driver = drv;
/* 如果使用了pinctrl,在探测之前绑定引脚 */
ret = pinctrl_bind_pins(dev);
if (ret)
goto probe_failed;
/* 添加驱动的sysfs属性 */
if (driver_sysfs_add(dev)) {
printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
__func__, dev_name(dev));
goto probe_failed;
}
/* 如果设备有电源域并且有激活函数,激活电源域 */
if (dev->pm_domain && dev->pm_domain->activate) {
ret = dev->pm_domain->activate(dev);
if (ret)
goto probe_failed;
}
if (dev->bus->probe) {
// 如果总线有probe函数,则调用总线的probe函数
ret = dev->bus->probe(dev);
if (ret)
goto probe_failed;
} else if (drv->probe) {
// 如果驱动有probe函数,则调用驱动的probe函数
ret = drv->probe(dev);
if (ret)
goto probe_failed;
}
if (dev->pm_domain && dev->pm_domain->sync)
// 如果设备有电源域并且有同步函数,则调用电源域的同步函数
dev->pm_domain->sync(dev);
driver_bound(dev);
ret = 1;
pr_debug("bus: '%s': %s: bound device %s to driver %s\n",
drv->bus->name, __func__, dev_name(dev), drv->name);
goto done;
probe_failed:
// 探测失败的处理
devres_release_all(dev);
driver_sysfs_remove(dev);
dev->driver = NULL;
dev_set_drvdata(dev, NULL);
if (dev->pm_domain && dev->pm_domain->dismiss)
// 如果设备有电源域并且有解除函数,则调用电源域的解除函数
dev->pm_domain->dismiss(dev);
switch (ret) {
case -EPROBE_DEFER:
// 驱动请求推迟探测
dev_dbg(dev, "Driver %s requests probe deferral\n", drv->name);
driver_deferred_probe_add(dev);
// 在探测过程中是否发生了触发?如果是,则需要重新触发
if (local_trigger_count != atomic_read(&deferred_trigger_count))
driver_deferred_probe_trigger();
break;
case -ENODEV:
case -ENXIO:
pr_debug("%s: probe of %s rejects match %d\n",
drv->name, dev_name(dev), ret);
break;
default:
// 驱动匹配但探测失败
printk(KERN_WARNING
"%s: probe of %s failed with error %d\n",
drv->name, dev_name(dev), ret);
}
/*
* 忽略->probe返回的错误,以便下一个驱动程序可以尝试运行
*/
ret = 0;
done:
atomic_dec(&probe_count);
wake_up(&probe_waitqueue);
return ret;
}
这段代码是Linux设备模型中的really_probe函数,用于执行驱动程序的探测(probe)操作。它在设备和驱动之间建立关联并调用相关的探测函数。
函数主要的步骤如下:
增加probe_count计数,用于跟踪当前正在进行探测的驱动数量。
设置设备的driver成员为当前驱动。
调用pinctrl_bind_pins函数,如果使用了pinctrl,则在探测之前绑定引脚。
调用driver_sysfs_add函数,为驱动添加sysfs属性。
如果设备有电源域并且具有激活函数,则调用电源域的激活函数。
如果总线有probe函数,则调用总线的probe函数;否则,如果驱动有probe函数,则调用驱动的probe函数。
如果设备有电源域并且具有同步函数,则调用电源域的同步函数。
调用driver_bound函数,标记设备已与驱动关联。
设置返回值为1,表示探测成功。
跳转到done标签,结束函数。如果探测过程中出现错误,则执行以下操作:
释放设备的资源和驱动的sysfs属性。
清除设备的driver成员和私有数据。
如果设备有电源域并且具有解除函数,则调用电源域的解除函数。
根据错误码采取不同的处理措施:如果是-EPROBE_DEFER,表示驱动请求推迟探测,将设备添加到延迟探测列表,并检查是否需要触发延迟探测;如果是-ENODEV或-ENXIO,表示驱动不匹配设备,忽略探测;否则,打印警告信息表示探测失败。
设置返回值为0,表示探测失败。
最后,减少probe_count计数,唤醒等待队列并返回最终的返回值。
/**
* pinctrl_bind_pins() - called by the device core before probe
* @dev: the device that is just about to probe
*/
int pinctrl_bind_pins(struct device *dev)
{
// 绑定引脚函数,由设备核心在探测之前调用
int ret;
// 为设备的pins成员分配内存空间
dev->pins = devm_kzalloc(dev, sizeof(*(dev->pins)), GFP_KERNEL);
if (!dev->pins)
return -ENOMEM;
// 获取设备的pinctrl句柄
dev->pins->p = devm_pinctrl_get(dev);
if (IS_ERR(dev->pins->p)) {
dev_dbg(dev, "no pinctrl handle\n");
ret = PTR_ERR(dev->pins->p);
goto cleanup_alloc;
}
// 查找设备的默认pinctrl状态
dev->pins->default_state = pinctrl_lookup_state(dev->pins->p,
PINCTRL_STATE_DEFAULT);
if (IS_ERR(dev->pins->default_state)) {
dev_dbg(dev, "no default pinctrl state\n");
ret = 0;
goto cleanup_get;
}
// 选择设备的默认pinctrl状态
ret = pinctrl_select_state(dev->pins->p, dev->pins->default_state);
if (ret) {
dev_dbg(dev, "failed to activate default pinctrl state\n");
goto cleanup_get;
}
#ifdef CONFIG_PM
/*
* 如果启用了电源管理,我们还会查找可选的睡眠和空闲引脚状态,
* 其语义如中所定义
*/
dev->pins->sleep_state = pinctrl_lookup_state(dev->pins->p,
PINCTRL_STATE_SLEEP);
if (IS_ERR(dev->pins->sleep_state))
/* 不提供此状态是完全合法的 */
dev_dbg(dev, "no sleep pinctrl state\n");
dev->pins->idle_state = pinctrl_lookup_state(dev->pins->p,
PINCTRL_STATE_IDLE);
if (IS_ERR(dev->pins->idle_state))
/* 不提供此状态是完全合法的 */
dev_dbg(dev, "no idle pinctrl state\n");
#endif
return 0;
/*
* 如果没有找到设备的pinctrl句柄或默认状态,
* 让我们显式释放设备中的引脚容器,没有保留它的意义。
*/
cleanup_get:
devm_pinctrl_put(dev->pins->p);
cleanup_alloc:
devm_kfree(dev, dev->pins);
dev->pins = NULL;
/* 只返回延迟 */
if (ret != -EPROBE_DEFER)
ret = 0;
return ret;
}
这段代码是pinctrl_bind_pins函数,由设备核心在设备探测之前调用,用于绑定设备的引脚配置。
函数的主要步骤如下:
分配设备的dev->pins结构体的内存空间。
获取设备的pinctrl句柄,使用devm_pinctrl_get函数。
检查获取pinctrl句柄的结果,如果出错则返回错误码。
查找设备的默认pinctrl状态,使用pinctrl_lookup_state函数。
检查查找默认状态的结果,如果出错则表示设备没有默认状态,打印调试信息。
选择设备的默认pinctrl状态,使用pinctrl_select_state函数。
检查选择默认状态的结果,如果出错则表示无法激活默认状态,打印调试信息。
(可选)如果启用了电源管理,则查找设备的睡眠和空闲引脚状态。
返回0表示绑定引脚成功。如果在绑定引脚的过程中出现错误,则执行以下清理操作:
调用devm_pinctrl_put函数释放pinctrl句柄。
调用devm_kfree函数释放dev->pins的内存空间。
将dev->pins设置为NULL。
如果错误码不是-EPROBE_DEFER,则将错误码设置为0。
返回错误码。
需要注意的是,这段代码涉及到Linux设备模型中的pinctrl子系统,用于配置设备的引脚。
pinctrl_bind_pins
devm_pinctrl_get
pinctrl_get
create_pinctrl
/**
* pinctrl_get() - 获取设备的 pinctrl 句柄
* @dev: 要获取句柄的设备
*/
struct pinctrl *pinctrl_get(struct device *dev)
{
struct pinctrl *p;
if (WARN_ON(!dev))
return ERR_PTR(-EINVAL);
/*
* 看看是否有其他人(如设备核心)已经获取了该设备的 pinctrl 句柄。
* 在这种情况下,返回另一个指向它的指针。
*/
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() - 创建 pinctrl 句柄
* @dev: 要创建句柄的设备
*
* 为每个映射创建一个状态 cookie 持有者 struct pinctrl,当使用 pinctrl_get() 请求一个引脚控制句柄时,消费者将得到这个句柄。
*/
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;
/*
* 为每个映射创建状态 cookie 持有者 struct pinctrl,这是消费者请求引脚控制句柄时得到的内容。
*/
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);
/*
* 在这一点上,添加设置可能会出现以下情况:
*
* - 延迟,如果引脚控制设备尚不可用
* - 失败,如果引脚控制设备尚不可用,并且设置是一个独占资源。我们不能延迟它,因为该独占资源将在设备注册后立即生效。
*
* 如果返回的错误不是 -EPROBE_DEFER,则我们累积错误以查看是否最终出现 -EPROBE_DEFER,因为这是最糟糕的情况。
*/
if (ret == -EPROBE_DEFER) {
pinctrl_free(p, false);
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;
}
这段代码是create_pinctrl函数,用于创建pinctrl句柄。
函数的主要步骤如下:
分配一个struct pinctrl结构体的内存空间,用于保存pinctrl句柄的信息。
对struct pinctrl结构体进行初始化,包括设备指针、状态列表和设备树映射列表的初始化。
调用pinctrl_dt_to_map函数将设备树映射转换为pinctrl映射。
获取设备的名称。
使用互斥锁锁定pinctrl_maps_mutex,遍历pinctrl映射,找到适用于该设备的映射。
调用add_setting函数将找到的映射添加到pinctrl句柄中。
检查add_setting的返回值,如果返回的错误码是-EPROBE_DEFER,表示出现了延迟探测,需要释放pinctrl句柄并返回该错误码。
解锁pinctrl_maps_mutex互斥锁。
检查添加映射的结果,如果出现其他错误,则释放pinctrl句柄并返回该错误码。
初始化pinctrl句柄的用户引用计数。
使用互斥锁锁定pinctrl_list_mutex,将pinctrl句柄添加到全局列表中。
解锁pinctrl_list_mutex互斥锁。
返回创建的pinctrl句柄。
需要注意的是,这段代码涉及到pinctrl子系统中的设备树映射和pinctrl句柄的管理。它根据设备的设备树映射和设备名称创建一个pinctrl句柄,并将其添加到全局列表中供后续使用。
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 enabled, p->dev not instantiated from DT */
if (!np) { // 如果没有设备节点,则不解析pinctrl DT
if (of_have_populated_dt())
dev_dbg(p->dev,
"no of_node; not parsing pinctrl DT\n"); // 如果已经填充了设备树,则不解析pinctrl DT
return 0;
}
ret = dt_gpio_assert_pinctrl(p); // 断言pinctrl设置
if (ret) {
dev_dbg(p->dev, "failed to assert pinctrl setting: %d\n", ret); // 断言pinctrl设置失败
return ret;
}
/* We may store pointers to property names within the node */
of_node_get(np); // 增加设备节点的引用计数
/* For each defined state ID */
for (state = 0; ; state++) { // 遍历每个定义的状态ID
/* Retrieve the pinctrl-* property */
propname = kasprintf(GFP_KERNEL, "pinctrl-%d", state); // 构造pinctrl-*属性的名称
prop = of_find_property(np, propname, &size); // 查找pinctrl-*属性
kfree(propname); // 释放属性名称的内存
if (!prop)
break;
list = prop->value; // 获取属性的值
size /= sizeof(*list); // 计算属性值的大小
/* Determine whether pinctrl-names property names the state */
ret = of_property_read_string_index(np, "pinctrl-names",
state, &statename); // 读取pinctrl-names属性的值
/*
* If not, statename is just the integer state ID. But rather
* than dynamically allocate it and have to free it later,
* just point part way into the property name for the string.
*/
if (ret < 0) {
/* strlen("pinctrl-") == 8 */
statename = prop->name + 8; // 如果pinctrl-names属性没有命名状态,则状态名称为整数状态ID
}
/* For every referenced pin configuration node in it */
for (config = 0; config < size; config++) { // 遍历每个引用的引脚配置节点
phandle = be32_to_cpup(list++); // 获取引脚配置节点的句柄
/* Look up the pin configuration node */
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;
}
/* Parse the node */
ret = dt_to_map_one_config(p, statename, np_config); // 解析节点
of_node_put(np_config); // 释放节点的引用
if (ret < 0)
goto err;
}
/* No entries in DT? Generate a dummy state table entry */
if (!size) { // 如果在设备树中没有条目,则生成一个虚拟状态表条目
ret = dt_remember_dummy_state(p, statename); // 生成虚拟状态表条目
if (ret < 0)
goto err;
}
}
return 0;
err:
pinctrl_dt_free_maps(p); // 释放映射
return ret;
}
这段代码是pinctrl_dt_to_map函数,用于将设备树中的pinctrl信息转换为pinctrl映射。
函数的主要步骤如下:
获取设备的设备树节点np。
调用dt_gpio_assert_pinctrl函数断言pinctrl设置,确保设备的pinctrl设置正确。
增加设备节点np的引用计数,以防止在函数执行期间被释放。
遍历每个定义的状态ID。
构造pinctrl-*属性的名称,例如"pinctrl-0"、"pinctrl-1"等。
使用of_find_property函数查找设备树节点np中的对应属性。
如果找到了pinctrl-*属性,获取其值和大小。
检查设备树节点np的pinctrl-names属性,判断是否命名了状态。
如果pinctrl-names属性没有命名状态,则将状态名称设置为整数状态ID。
遍历pinctrl-*属性中的每个引用的引脚配置节点。
获取引脚配置节点的句柄(phandle)。
使用句柄查找引脚配置节点。
解析引脚配置节点,将其转换为pinctrl映射。
释放引脚配置节点的引用。
如果pinctrl-*属性中没有条目,则生成一个虚拟的状态表条目。
返回0表示转换成功。
如果在转换过程中发生错误,会执行错误处理代码,包括释放pinctrl映射并返回相应的错误码。
需要注意的是,这段代码涉及到设备树中pinctrl信息的解析和转换为pinctrl映射。它遍历设备树节点的pinctrl-*属性,将其中的引脚配置节点转换为pinctrl映射,并处理无效的句柄和生成虚拟状态表条目的情况。最终,将设备树中的pinctrl信息转换为pinctrl映射,以便后续使用。
static int dt_to_map_one_config(struct pinctrl *p, const char *statename,
struct device_node *np_config)
{
// 查找包含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, "无法找到节点%s的引脚控制器,推迟探测\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;
// 不要推迟探测独占引脚(循环)
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);
}
这段代码是dt_to_map_one_config函数,用于将设备树中的单个引脚配置节点转换为pinctrl映射。
函数的主要步骤如下:
查找包含引脚配置节点np_config的引脚控制器节点np_pctldev。
循环向上遍历父节点,直到找到引脚控制器节点或达到根节点。如果找不到引脚控制器节点,则推迟探测,并返回错误码-EPROBE_DEFER。
获取引脚控制器设备的pinctrl_dev结构体指针pctldev。
调用引脚控制器驱动程序的dt_node_to_map函数,解析设备树节点np_config并生成映射表条目。
将生成的映射表条目存储在struct pinctrl的映射表块中。
返回转换结果。
需要注意的是,该函数通过查找引脚控制器节点和调用引脚控制器驱动程序的dt_node_to_map函数来实现引脚配置节点到pinctrl映射的转换。它在查找引脚控制器节点时可能会推迟探测,并处理找不到引脚控制器节点和不支持设备树的情况。最终,将引脚配置节点转换为映射表条目,并将其存储在pinctrl的映射表块中。
/*
* 调用引脚控制器驱动程序解析设备树节点,并生成映射表条目
*/
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;
调用
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 dt_remember_or_free_map(struct pinctrl *p, const char *statename,
struct pinctrl_dev *pctldev,
struct pinctrl_map *map, unsigned num_maps)
{
int i;
struct pinctrl_dt_map *dt_map;
/* 初始化公共映射表条目字段 */
for (i = 0; i < num_maps; i++) {
map[i].dev_name = dev_name(p->dev); // 设置设备名称
map[i].name = statename; // 设置状态名称
if (pctldev)
map[i].ctrl_dev_name = dev_name(pctldev->dev); // 设置控制器设备名称
}
/* 记录转换后的映射表条目 */
dt_map = kzalloc(sizeof(*dt_map), GFP_KERNEL); // 分配内存以存储映射表条目
if (!dt_map) {
dev_err(p->dev, "failed to alloc struct pinctrl_dt_map\n");
dt_free_map(pctldev, map, num_maps);
return -ENOMEM;
}
dt_map->pctldev = pctldev; // 设置引脚控制器设备
dt_map->map = map; // 设置映射表
dt_map->num_maps = num_maps; // 设置映射表数量
list_add_tail(&dt_map->node, &p->dt_maps); // 将映射表添加到链表中
return pinctrl_register_map(map, num_maps, false); // 注册映射表
}
struct pinctrl_dt_map {
struct list_head node;
struct pinctrl_dev *pctldev;
struct pinctrl_map *map;
unsigned num_maps;
};
这段代码是dt_remember_or_free_map函数,用于在转换设备树的引脚配置节点到映射表条目后,记录映射表条目或释放相关资源。
函数的主要步骤如下:
初始化映射表条目的公共字段,包括设备名称、状态名称和控制器设备名称。
分配内存以存储映射表条目的结构体struct pinctrl_dt_map。
如果内存分配失败,释放先前分配的映射表内存,并返回错误码。
设置struct pinctrl_dt_map结构体的成员,包括引脚控制器设备、映射表和映射表数量。
将struct pinctrl_dt_map结构体添加到struct pinctrl的映射表链表中。
调用pinctrl_register_map函数注册映射表。
返回注册结果。
需要注意的是,该函数负责在转换设备树的引脚配置节点到映射表条目后,记录映射表条目的相关信息,并将其添加到struct pinctrl的映射表链表中。同时,它还会调用pinctrl_register_map函数注册映射表。如果内存分配失败,将释放先前分配的映射表内存,并返回错误码。
/**
* pinctrl_register_map() - 注册一组引脚控制器映射
* @maps: 要注册的引脚控制器映射表。这个表应该标记为 __initdata,以便在引导后可以丢弃。此函数将对映射条目进行浅拷贝。
* @num_maps: 映射表中的映射数量
*/
int pinctrl_register_map(struct pinctrl_map const *maps, unsigned num_maps,
bool dup)
{
int i, ret;
struct pinctrl_maps *maps_node;
pr_debug("add %d pinmux maps\n", num_maps);
/* 首先对新映射进行合法性检查 */
for (i = 0; i < num_maps; i++) {
if (!maps[i].dev_name) {
pr_err("failed to register map %s (%d): no device given\n",
maps[i].name, i);
return -EINVAL;
}
if (!maps[i].name) {
pr_err("failed to register map %d: no map name given\n",
i);
return -EINVAL;
}
if (maps[i].type != PIN_MAP_TYPE_DUMMY_STATE &&
!maps[i].ctrl_dev_name) {
pr_err("failed to register map %s (%d): no pin control device given\n",
maps[i].name, i);
return -EINVAL;
}
switch (maps[i].type) {
case PIN_MAP_TYPE_DUMMY_STATE:
break;
case PIN_MAP_TYPE_MUX_GROUP:
ret = pinmux_validate_map(&maps[i], i);
if (ret < 0)
return ret;
break;
case PIN_MAP_TYPE_CONFIGS_PIN:
case PIN_MAP_TYPE_CONFIGS_GROUP:
ret = pinconf_validate_map(&maps[i], i);
if (ret < 0)
return ret;
break;
default:
pr_err("failed to register map %s (%d): invalid type given\n",
maps[i].name, i);
return -EINVAL;
}
}
maps_node = kzalloc(sizeof(*maps_node), GFP_KERNEL);
if (!maps_node) {
pr_err("failed to alloc struct pinctrl_maps\n");
return -ENOMEM;
}
// 分配内存给maps_node结构体,并将其初始化为0
maps_node->num_maps = num_maps;
if (dup) {
// 如果dup为真,则复制映射表
maps_node->maps = kmemdup(maps, sizeof(*maps) * num_maps,
GFP_KERNEL);
if (!maps_node->maps) {
pr_err("failed to duplicate mapping table\n");
kfree(maps_node);
return -ENOMEM;
}
} else {
// 如果dup为假,则直接使用传入的映射表
maps_node->maps = maps;
}
mutex_lock(&pinctrl_maps_mutex);
// 将maps_node添加到pinctrl_maps链表的末尾
list_add_tail(&maps_node->node, &pinctrl_maps);
mutex_unlock(&pinctrl_maps_mutex);
return 0;
}
这段代码是pinctrl_register_map函数,用
于注册一组引脚控制器的映射表。
函数的主要步骤如下:
对传入的映射表进行合法性检查,包括检查设备名称、映射名称和引脚控制器设备名称是否存在,以及检查映射类型是否有效。
分配内存以存储struct pinctrl_maps结构体。
如果dup参数为真,则进行映射表的浅拷贝,否则直接使用传入的映射表。
将struct pinctrl_maps结构体添加到pinctrl_maps链表的末尾。
返回注册结果。
需要注意的是,该函数首先对传入的映射表进行合法性检查,然后将映射表存储在struct pinctrl_maps结构体中,并将其添加到全局的pinctrl_maps链表中。在进行映射表的浅拷贝时,会分配新的内存来存储拷贝的映射表。最后,函数返回注册的结果。
/* 遍历引脚控制映射以找到正确的映射 */
for_each_maps(maps_node, i, map) {
/* 映射必须是针对该设备的 */
if (strcmp(map->dev_name, devname))
continue;
ret = add_setting(p, map);
/*
* 在这一点上,添加设置可能会出现以下情况:
*
* - 延迟,如果引脚控制设备尚不可用
* - 失败,如果引脚控制设备尚不可用,并且设置是一个独占资源。我们不能延迟它,因为该独占资源将在设备注册后立即生效。
*
* 如果返回的错误不是 -EPROBE_DEFER,则我们累积错误以查看是否最终出现 -EPROBE_DEFER,因为这是最糟糕的情况。
*/
if (ret == -EPROBE_DEFER) {
pinctrl_free(p, false);
mutex_unlock(&pinctrl_maps_mutex);
return ERR_PTR(ret);
}
}
#define for_each_maps(_maps_node_, _i_, _map_) \
list_for_each_entry(_maps_node_, &pinctrl_maps, node) \
for (_i_ = 0, _map_ = &_maps_node_->maps[_i_]; \
_i_ < _maps_node_->num_maps; \
_i_++, _map_ = &_maps_node_->maps[_i_])
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;
// 分配设置结构体
struct pinctrl_setting *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; // 设置设置结构体的类型为映射的类型
setting->pctldev = get_pinctrl_dev_from_devname(map->ctrl_dev_name); // 通过设备名称获取 pinctrl 设备
if (setting->pctldev == NULL) { // 如果获取的 pinctrl 设备为空
kfree(setting); // 释放设置结构体的内存
/* 不要推迟探测独占资源(循环引用) */
if (!strcmp(map->ctrl_dev_name, map->dev_name))
return -ENODEV;
/*
* 好吧,让我们猜测驱动程序还不存在,并且推迟获取此 pinctrl 句柄到以后...
*/
dev_info(p->dev, "unknown pinctrl device %s in map entry, deferring probe",
map->ctrl_dev_name); // 打印日志,推迟探测 pinctrl 设备
return -EPROBE_DEFER;
}
setting->dev_name = map->dev_name; // 设置设置结构体的设备名称为映射的设备名称
switch (map->type) { // 根据映射的类型进行不同的操作
case PIN_MAP_TYPE_MUX_GROUP: // 如果映射的类型为 PIN_MAP_TYPE_MUX_GROUP
ret = pinmux_map_to_setting(map, setting); // 将映射转换为设置结构体
break;
case PIN_MAP_TYPE_CONFIGS_PIN: // 如果映射的类型为 PIN_MAP_TYPE_CONFIGS_PIN
case PIN_MAP_TYPE_CONFIGS_GROUP: // 如果映射的类型为 PIN_MAP_TYPE_CONFIGS_GROUP
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; // 返回成功
}
这段代码是add_setting函数,用于向引脚控制器的状态添加设置。
函数的主要步骤如下:
首先查找给定名称的状态,如果找不到,则创建一个新的状态。
如果映射类型为PIN_MAP_TYPE_DUMMY_STATE,则直接返回。
分配内存以存储struct pinctrl_setting结构体。
根据映射的类型进行不同的操作:
如果映射类型为PIN_MAP_TYPE_MUX_GROUP,则将映射转换为struct pinctrl_setting结构体。
如果映射类型为PIN_MAP_TYPE_CONFIGS_PIN或PIN_MAP_TYPE_CONFIGS_GROUP,则将映射转换为struct pinctrl_setting结构体。
如果转换过程中出现错误,则释放分配的内存并返回错误码。
将struct pinctrl_setting结构体添加到状态的设置链表中。
返回成功。
需要注意的是,该函数会查找给定名称的状态,如果状态不存在,则会创建一个新的状态,并将设置添加到该状态的设置链表中。根据映射的类型,将映射转换为相应的设置结构体,并将其添加到状态中。最后,函数返回操作的结果。
int pinmux_map_to_setting(struct pinctrl_map const *map,
struct pinctrl_setting *setting)
{
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;
int i;
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) {
bool found = false;
group = map->data.mux.group;
for (i = 0; i < num_groups; i++) {
if (!strcmp(group, groups[i])) {
found = true;
break;
}
}
if (!found) {
dev_err(pctldev->dev,
"invalid group \"%s\" for function \"%s\"\n",
group, map->data.mux.function);
return -EINVAL;
}
} 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;
}
这段代码是pinmux_map_to_setting函数,用于将引脚映射转换为引脚控制器设置。
函数的主要步骤如下:
获取设置结构体中的引脚控制器设备和引脚复用操作集。
通过引脚复用操作集的get_function_groups函数获取与引脚映射中的功能名称匹配的引脚组。
如果没有找到任何引脚组,或者映射中指定的组无效,则返回错误。
如果映射中指定了组名,则检查该组名是否有效,如果无效,则返回错误。
如果映射中未指定组名,则默认选择第一个引脚组。
通过引脚控制器设备的pinctrl_get_group_selector函数获取与引脚映射中的组名匹配的引脚组选择器。
将获取到的功能选择器和引脚组选择器设置到设置结构体中。
返回成功。
需要注意的是,该函数依赖于引脚复用操作集的实现来获取功能名称和引脚组信息,并通过引脚控制器设备的函数来获取引脚组选择器。函数根据映射中的信息进行处理,并将获取到的结果设置到设置结构体中。最后,函数返回操作的结果。
int pinconf_map_to_setting(struct pinctrl_map const *map,
struct pinctrl_setting *setting)
{
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;
}
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;
}
setting->data.configs.group_or_pin = pin;
break;
default:
return -EINVAL;
}
setting->data.configs.num_configs = map->data.configs.num_configs;
setting->data.configs.configs = map->data.configs.configs;
return 0;
}
这段代码是pinconf_map_to_setting函数,用于将引脚映射转换为引脚配置设置。
函数的主要步骤如下:
获取设置结构体中的引脚控制器设备。
根据设置结构体的类型进行不同的操作:
如果类型为PIN_MAP_TYPE_CONFIGS_PIN,则通过引脚控制器设备的pin_get_from_name函数获取与引脚映射中的引脚配置组或引脚名匹配的引脚编号。
如果类型为PIN_MAP_TYPE_CONFIGS_GROUP,则通过引脚控制器设备的pinctrl_get_group_selector函数获取与引脚映射中的引脚配置组名匹配的引脚组选择器。
如果获取引脚编号或引脚组选择器失败,则返回错误。
将获取到的引脚编号或引脚组选择器设置到设置结构体中的group_or_pin字段。
将引脚映射中的配置数量和配置数组设置到设置结构体中。
返回成功。
需要注意的是,该函数依赖于引脚控制器设备的函数来获取引脚编号和引脚组选择器,并根据引脚映射中的信息进行处理。最后,函数将获取到的结果设置到设置结构体中,并返回操作的结果。
// 查找设备的默认pinctrl状态
dev->pins->default_state = pinctrl_lookup_state(dev->pins->p,
PINCTRL_STATE_DEFAULT);
if (IS_ERR(dev->pins->default_state)) {
dev_dbg(dev, "no default pinctrl state\n");
ret = 0;
goto cleanup_get;
}
#define PINCTRL_STATE_DEFAULT "default"
#define PINCTRL_STATE_IDLE "idle"
#define PINCTRL_STATE_SLEEP "sleep"
/**
* pinctrl_lookup_state() - retrieves a state handle from a pinctrl handle
* @p: the pinctrl handle to retrieve the state from
* @name: the state name to retrieve
*/
struct pinctrl_state *pinctrl_lookup_state(struct pinctrl *p,
const char *name)
{
struct pinctrl_state *state;
state = find_state(p, name);
if (!state) {
if (pinctrl_dummy_state) {
/* create dummy state */
dev_dbg(p->dev, "using pinctrl dummy state (%s)\n",
name);
state = create_state(p, name);
} else
state = ERR_PTR(-ENODEV);
}
return state;
}
EXPORT_SYMBOL_GPL(pinctrl_lookup_state);
这段代码实现了pinctrl_lookup_state函数,用于从 pinctrl 句柄中检索状态句柄。
函数的主要步骤如下:
调用 find_state 函数从 pinctrl 句柄中查找指定名称的状态。
如果找到了对应的状态,则返回该状态句柄。
如果未找到对应的状态,则检查是否存在 pinctrl 的虚拟状态(dummy state)。
如果存在虚拟状态,创建一个带有指定名称的新状态,并返回该状态句柄。
如果不存在虚拟状态,返回错误码 -ENODEV。
函数返回找到的状态句柄或错误码。
需要注意的是,该函数可能使用虚拟状态作为默认状态,如果没有找到具体的状态。虚拟状态可以用于处理特殊情况,例如在特定条件下创建默认状态,以确保在没有明确状态的情况下能够进行正常操作。
ret = pinctrl_select_state(dev->pins->p, dev->pins->default_state);
/**
* pinctrl_select_state() - select/activate/program a pinctrl state to HW
* @p: the pinctrl handle for the device that requests configuration
* @state: the state handle to select/activate/program
*/
int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *state)
{
struct pinctrl_setting *setting, *setting2;
struct pinctrl_state *old_state = p->state;
int ret;
if (p->state == state)
return 0;
if (p->state) {
/*
* For each pinmux setting in the old state, forget SW's record
* of mux owner for that pingroup. Any pingroups which are
* still owned by the new state will be re-acquired by the call
* to pinmux_enable_setting() in the loop below.
*/
list_for_each_entry(setting, &p->state->settings, node) {
if (setting->type != PIN_MAP_TYPE_MUX_GROUP)
continue;
pinmux_disable_setting(setting);
}
}
p->state = NULL;
/* 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);
break;
case PIN_MAP_TYPE_CONFIGS_PIN:
case PIN_MAP_TYPE_CONFIGS_GROUP:
ret = pinconf_apply_setting(setting);
break;
default:
ret = -EINVAL;
break;
}
if (ret < 0) {
goto unapply_new_state;
}
}
p->state = state;
return 0;
unapply_new_state:
dev_err(p->dev, "Error applying setting, reverse things back\n");
list_for_each_entry(setting2, &state->settings, node) {
if (&setting2->node == &setting->node)
break;
/*
* All we can do here is pinmux_disable_setting.
* That means that some pins are muxed differently now
* than they were before applying the setting (We can't
* "unmux a pin"!), but it's not a big deal since the pins
* are free to be muxed by another apply_setting.
*/
if (setting2->type == PIN_MAP_TYPE_MUX_GROUP)
pinmux_disable_setting(setting2);
}
/* There's no infinite recursive loop here because p->state is NULL */
if (old_state)
pinctrl_select_state(p, old_state);
return ret;
}
这段代码实现了pinctrl_select_state函数,用于选择/激活/编程一个 pinctrl 状态到硬件。
函数的主要步骤如下:
检查当前状态是否与目标状态相同,如果相同则直接返回成功。
如果当前状态存在,则对旧状态中的所有 pinmux 设置进行处理,将 SW 记录的对应 pingroup 的 mux 所有者清除。
将 pinctrl 句柄的当前状态设置为 NULL,表示没有选定的状态。
遍历目标状态中的所有设置项,并根据设置项的类型执行相应的操作:
如果设置项类型为 PIN_MAP_TYPE_MUX_GROUP,则调用 pinmux_enable_setting 函数启用设置项。
如果设置项类型为 PIN_MAP_TYPE_CONFIGS_PIN 或 PIN_MAP_TYPE_CONFIGS_GROUP,则调用 pinconf_apply_setting 函数应用设置项。
其他类型的设置项返回无效参数错误(-EINVAL)。
如果在应用设置项的过程中出现错误,则需要撤销已应用的新状态,并恢复到旧状态。
恢复新状态之前应用的设置项,即调用 pinmux_disable_setting 函数将这些设置项禁用。
如果存在旧状态,则递归调用 pinctrl_select_state 函数将 pinctrl 句柄恢复到旧状态。
返回应用设置项过程中的错误码。
总体来说,该函数负责选择/激活/编程指定的 pinctrl 状态到硬件,并处理相关的设置项。如果在应用设置项时出现错误,函数将回滚到之前的状态,并返回错误码。