pinctrl设备树节点映射详细分析imx_dt_node_to_map

news2024/12/23 19:28:50

pinctrl设备树节点映射详细分析imx_dt_node_to_map


文章目录

  • pinctrl设备树节点映射详细分析imx_dt_node_to_map
  • struct pinctrl_map
  • really_probe
  • pinctrl_bind_pins
    • create_pinctrl
    • pinctrl_dt_to_map
      • dt_to_map_one_config
      • dt_remember_or_free_map
      • pinctrl_register_map
    • add_setting
      • pinmux_map_to_setting
      • pinconf_map_to_setting
    • pinctrl_lookup_state
    • pinctrl_select_state


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

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读写控制脚进行控制。

really_probe

所以我们是不是可以猜测,在进到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

在这里插入图片描述

/**
 * 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
    /*
     * 如果启用了电源管理,我们还会查找可选的睡眠和空闲引脚状态,
     * 其语义如<linux/pinctrl/pinctrl-state.h>中所定义
     */
    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子系统,用于配置设备的引脚。

create_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句柄,并将其添加到全局列表中供后续使用。

pinctrl_dt_to_map

在这里插入图片描述

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映射,以便后续使用。

dt_to_map_one_config

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; // 释放映射
};

dt_remember_or_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

/**
 * 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链表中。在进行映射表的浅拷贝时,会分配新的内存来存储拷贝的映射表。最后,函数返回注册的结果。

add_setting

  /* 遍历引脚控制映射以找到正确的映射 */
    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结构体添加到状态的设置链表中。
返回成功。
需要注意的是,该函数会查找给定名称的状态,如果状态不存在,则会创建一个新的状态,并将设置添加到该状态的设置链表中。根据映射的类型,将映射转换为相应的设置结构体,并将其添加到状态中。最后,函数返回操作的结果。

pinmux_map_to_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函数获取与引脚映射中的组名匹配的引脚组选择器。
将获取到的功能选择器和引脚组选择器设置到设置结构体中。
返回成功。
需要注意的是,该函数依赖于引脚复用操作集的实现来获取功能名称和引脚组信息,并通过引脚控制器设备的函数来获取引脚组选择器。函数根据映射中的信息进行处理,并将获取到的结果设置到设置结构体中。最后,函数返回操作的结果。

pinconf_map_to_setting

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_lookup_state

// 查找设备的默认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。
函数返回找到的状态句柄或错误码。
需要注意的是,该函数可能使用虚拟状态作为默认状态,如果没有找到具体的状态。虚拟状态可以用于处理特殊情况,例如在特定条件下创建默认状态,以确保在没有明确状态的情况下能够进行正常操作。

pinctrl_select_state

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 状态到硬件,并处理相关的设置项。如果在应用设置项时出现错误,函数将回滚到之前的状态,并返回错误码。

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

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

相关文章

Linux网络服务之部署yum仓库

yum &#xff1f; yum ! 一、YUM概述1.1 yum简介1.2 yum工作原理 二、yum 配置文件2.1 yum主配置文件2.2 yum仓库设置文件2.2.1 配置文件主要格式2.2.2 软件仓库的提供方式2.2.3 日志文件 三、yum命令详解3.1 安装和升级3.2 查询3.2.1 显示可用的安装包 ----- yum list3.2.2 显…

数据结构笔记--归并排序及其拓展题(小和问题、逆序对问题)

目录 1--归并排序 2--小和问题 3--逆序对问题 1--归并排序 归并排序的核心思想&#xff1a;将一个无序的序列归并排序为一个有序的系列&#xff1b;通过递归将无序的序列二分&#xff0c;从底层开始将二分的序列归并排序为有序序列&#xff1b; #include <iostream> #…

手工测试VS自动化测试到底那个更胜一筹?

手工与自动化只是一种形式&#xff0c;真正的核心是测试用例、业务模型和测试分析。当企业的产品规模开始膨胀的时候&#xff0c;尤其是产品迭代加快是不是能及时得到测试验证支持是很重要的。这些靠手工测试是基本无法实现的&#xff0c;手工测试会严重的拖慢产品进度&#xf…

快速排序和qsort函数详解详解qsort函数

&#x1f495;是非成败转头空&#xff0c;青山依旧在&#xff0c;几度夕阳红&#x1f495; 作者&#xff1a;Mylvzi 文章主要内容&#xff1a;快速排序和qsort函数详解 前言&#xff1a; 我们之前学习过冒泡排序&#xff0c;冒泡排序尽管很方便&#xff0c;但也存在一些局限性…

类图的6种关系和golang应用

文章目录 1. 依赖和关联1.1 依赖&#xff08;Dependency&#xff09;概念类图示例代码示例 1.2 关联&#xff08;Association&#xff09;概念类图示例代码示例 2. 组合和聚合&#xff08;特殊的关联关系&#xff09;2.1 聚合&#xff08;Aggregation&#xff09;概念类图示例代…

nginx编译以及通过自定义生成证书配置https

1. 环境准备 1.1 软件安装 nginx安装编译安装以及配置https&#xff0c;需要gcc-c pcre-devel openssl openssl-devel软件。因此需要先安装相关软件。 yum -y install gcc-c pcre-devel openssl openssl-devel wgetopenssl/openssl-devel&#xff1a;主要用于nginx编译的htt…

Unity限制在一个范围内移动

Unity限制在一个范围内移动 这个例子中&#xff0c;我们学习Vector3.ClampMagnitude的用法&#xff0c;限制小球在范围内移动。 在地图上放了一个小球&#xff0c;让他移动&#xff0c;但是不想让他掉下去&#xff0c;限制在一个球星范围内&#xff0c;就好像绳子拴住了一样&…

MySQL流程控制(二十八)

二八佳人体似酥&#xff0c;腰悬利剑斩愚夫&#xff0c;虽然不见人头落,暗里教君骨髓枯。 上一章简单介绍了MySQL变量(二十七) ,如果没有看过,请观看上一章 一. 定义条件与处理程序 定义条件是事先定义程序执行过程中可能遇到的问题&#xff0c;处理程序定义了在遇到问题时应…

跨境B2B2C多用户购物网站源码快速部署

​ 搭建跨境B2B2C多用户购物网站需要以下步骤&#xff1a; 1. 确定业务模式和定位&#xff1a;确定网站的业务模式&#xff0c;包括跨境B2B2C的商业模式以及目标用户定位。 2. 营业执照和域名注册&#xff1a;根据当地法律要求&#xff0c;注册一家具有法人资格的公司&#xff…

java Springboot02--Controller,文件上传,拦截器

因为前后端分离了&#xff0c;所以这个项目基本用不到controller 这句话意思&#xff1a; controller只能用get接受前端的请求 RequestMapping(value "/hello",method RequestMethod.GET) GetMapping("/hello") 这两句等价的 前段传递参数&#xff0…

炸裂,靠“吹牛”过京东一面,月薪40K

说在前面 在40岁老架构师尼恩的&#xff08;50&#xff09;读者社区中&#xff0c;经常有小伙伴&#xff0c;需要面试美团、京东、阿里、 百度、头条等大厂。 下面是一个5年小伙伴成功拿到通过了京东一面面试&#xff0c;并且最终拿到offer&#xff0c;月薪40K。 现在把面试…

搜索综合训练

搜索综合训练 选数详细注释的代码 小木棍详细注释的代码 费解的开关详细注释的代码 选数 详细注释的代码 #include <iostream> #include <vector>using namespace std;// 判断一个数是否为素数 bool isPrime(int num) {if (num < 1)return false;// 判断从2到s…

利用状态监测和机器学习提高冷却塔性能的具体方法

在现代工业生产中&#xff0c;冷却塔扮演着至关重要的角色&#xff0c;它们的性能直接影响着工艺流程的稳定性和效率。为了确保冷却塔的正常运行和减少系统故障&#xff0c;状态监测和机器学习成为了关键技术。 图.冷却塔&#xff08;PreMaint&#xff09; 在前文《基于人工智…

P1629 邮递员送信(最短路)(内附封面)

邮递员送信 题目描述 有一个邮递员要送东西&#xff0c;邮局在节点 1 1 1。他总共要送 n − 1 n-1 n−1 样东西&#xff0c;其目的地分别是节点 2 2 2 到节点 n n n。由于这个城市的交通比较繁忙&#xff0c;因此所有的道路都是单行的&#xff0c;共有 m m m 条道路。这…

02_kafka_基本概念_基础架构

文章目录 常见的消息队列工作模式基本概念kafka 特性Kafka 基本架构topic 分区的 目的/ 好处 日志存储形式消费者&#xff0c;消费方式 逻辑消费组 高性能写入&#xff1a; 顺序写 mmap读取&#xff1a;零拷贝DMA 使用场景 常见的消息队列工作模式 至多一次&#xff1a;消息被…

cnvd通用型证书获取姿势

因为技术有限&#xff0c;只能挖挖不用脑子的漏洞&#xff0c;平时工作摸鱼的时候通过谷歌引擎引擎搜索找找有没有大点的公司有sql注入漏洞&#xff0c;找的方法就很简单&#xff0c;网站结尾加上’&#xff0c;有异常就测试看看&#xff0c;没有马上下一家&#xff0c;效率至上…

基于Python++PyQt5马尔科夫模型的智能AI即兴作曲—深度学习算法应用(含全部工程源码+测试数据)

目录 前言总体设计系统整体结构图系统流程图 运行环境Python 环境PC环境配置 模块实现1. 钢琴伴奏制作1&#xff09;和弦的实现2&#xff09;和弦级数转为当前调式音阶3&#xff09;根据预置节奏生成伴奏 2. 乐句生成1&#xff09;添加音符2&#xff09;旋律生成3&#xff09;节…

Llama2 评测大公开!知识库场景下能否赶超 ChatGPT?

AIGC 人狂喜&#xff01;最近&#xff0c;Meta AI 发布了大语言模型 Llama2&#xff0c;为大模型的开发者注入了一剂强心针&#xff0c;因为无论从其灵活性、竞争力还是便捷性来看&#xff0c;都有不小的优势。 具体来看&#xff1a; Llama2 为开源产品且可免费商用&#xff0c…

【Spring AOP】什么是AOP

文章目录 1、AOP思想2、AOP入门案例3、AOP工作流程4、AOP切入点表达式5、AOP的五种通知类型6、AOP通知获取数据7、案例&#xff1a;百度网盘密码数据兼容处理8、AOP总结 1、AOP思想 AOP&#xff0c;即Aspect Oriented Programming&#xff0c;面向切面编程。是一种编程范式&am…

8.18信号量Semaphore和CountDownLatch

一 .Semaphore: 1.Semaphore是一个计数器(变量),描述可用资源的个数,用来判断是否有临界资源可用. 2.临界资源:多个进程或线程并发执行的实体可以公共使用到的资源. 3.pv操作:p操作(accquire(申请))表示计数器减一,v操作(release(释放))表示计数器加一. 4.锁是特殊的信号量…