RK3568驱动指南|第十一篇 pinctrl 子系统-第127章 猜想验证

瑞芯微RK3568芯片是一款定位中高端的通用型SOC,采用22nm制程工艺,搭载一颗四核Cortex-A55处理器和Mali G52 2EE 图形处理器。RK3568 支持4K 解码和 1080P 编码,支持SATA/PCIE/USB3.0 外围接口。RK3568内置独立NPU,可用于轻量级人工智能应用。RK3568 支持安卓 11 和 linux 系统,主要面向物联网网关、NVR 存储、工控平板、工业检测、工控盒、卡拉 OK、云终端、车载中控等行业。


【公众号】迅为电子

【粉丝群】824412014(加群获取驱动文档+例程)

【视频观看】嵌入式学习之Linux驱动(第十一篇 pinctrl 子系统_全新升级)_基于RK3568

【购买链接】迅为RK3568开发板瑞芯微Linux安卓鸿蒙ARM核心板人工智能AI主板


第127章 猜想验证

经过了前面章节的学习,我们已经对pinctrl子系统中有了一定的了解,下面就来解决我们在120.2小节中提出的问题。

首先对问题进行一下复述,假如我们要配置一个LED外设,该LED需要使用一个管脚来进行亮灭的控制,那这个控制引脚需要复用成GPIO之后才能完成相应的功能,那pinctrl子系统是什么时候对该引脚进行的复用呢?

首先我们可以提出两种猜想,第一个猜想是在加载LED驱动的时候进行的pinctrl引脚复用,第二种猜想是在加载pinctrl驱动的时候完成的引脚复用。下面对这两种猜想进行验证。

1.猜想1验证

第一个猜想是在加载LED驱动的时候进行的pinctrl引脚复用,这就符合我们124章、125章、126章所讲解的内容。当LED灯的设备树和驱动匹配之后,就会进入驱动中编写的probe函数,在此之前会执行“drivers/base/dd.c”文件中的really probe函数中的子函数pinctrl_bind_pins,该函数会为给定的设备绑定引脚,并在绑定过程中选择和设置适当的pinctrl状态。具体的绑定细节可以去前面的章节中查找。

2.猜想2验证

第二种猜想是在加载pinctrl驱动的时候完成的引脚复用,因为pinctrl子系统也是符合设备模型的规范,也会执行相应的probe函数,所以同样的加在pinctrl驱动时也会执行“drivers/base/dd.c”文件中的really probe函数中的子函数pinctrl_bind_pins,那这时会进行pinctrl管脚的复用设置吗,接下来我们对此进行深入的分析。

在pinctrl的probe函数执行之前,会调用pinctrl_bind_pins函数,根据124.2小节中讲解到的内容可以知道,根据函数的嵌套,首先会调用的create_pinctrl函数创建struct pinctrl 类型的引脚控制器,create_pinctrl函数内容如下所示:

static struct pinctrl *create_pinctrl(struct device *dev,
				      struct pinctrl_dev *pctldev)
{
	struct pinctrl *p;
	const char *devname;
	struct pinctrl_maps *maps_node;
	int i;
	const struct pinctrl_map *map;
	int ret;

	/*
	 * 为每个映射创建状态 cookie 持有者 struct pinctrl。
	 * 这是当使用 pinctrl_get() 请求引脚控制句柄时消费者将获得的对象。
	 */
	p = kzalloc(sizeof(*p), GFP_KERNEL);
	if (!p)
		return ERR_PTR(-ENOMEM);
	p->dev = dev;
	INIT_LIST_HEAD(&p->states);
	INIT_LIST_HEAD(&p->dt_maps);

	ret = pinctrl_dt_to_map(p, pctldev);
	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;
		/*
		 * 如果 pctldev 不为空,我们正在声明它的独占使用权,
		 * 这意味着它自己提供了该设置。
		 *
		 * 因此,我们必须跳过适用于此设备但由其他设备提供的映射。
		 */
		if (pctldev &&
		    strcmp(dev_name(pctldev->dev), map->ctrl_dev_name))
			continue;

		ret = add_setting(p, pctldev, 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);

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

	return p;
}

在第22行会调用 pinctrl_dt_to_map 函数将设备树中定义的引脚映射信息转换为 struct pinctrl_map 结构,并将其添加到 p->dt_maps 链表中。该函数定义在内核源码目录下的“drivers/pinctrl/devicetree.c”文件中,具体内容如下所示:

int pinctrl_dt_to_map(struct pinctrl *p, struct pinctrl_dev *pctldev)
{
    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 不是从设备树实例化而来 */
    if (!np) {
        if (of_have_populated_dt())
            dev_dbg(p->dev, "no of_node; not parsing pinctrl DT\n");
        return 0;
    }

    /* 节点内部存储属性名称的指针 */
    of_node_get(np);

    /* 对于每个定义的状态 ID */
    for (state = 0;; state++) {
        /* 获取 pinctrl-* 属性 */
        propname = kasprintf(GFP_KERNEL, "pinctrl-%d", state);
        prop = of_find_property(np, propname, &size);
        kfree(propname);
        if (!prop) {
            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);
        /*
         * 如果未命名,则 statename 仅是整数状态 ID。但是,为了避免动态分配和之后要释放的麻烦,
         * 可以直接将 statename 指向属性名称的一部分。
         */
        if (ret < 0) {
            /* strlen("pinctrl-") == 8 */
            statename = prop->name + 8;
        }

        /* 对于其中的每个引用的引脚配置节点 */
        for (config = 0; config < size; config++) {
            phandle = be32_to_cpup(list++);

            /* 查找引脚配置节点 */
            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;
            }

            /* 解析节点 */
            ret = dt_to_map_one_config(p, pctldev, statename, np_config);
            of_node_put(np_config);
            if (ret < 0)
                goto err;
        }

        /* 如果在设备树中没有条目,则生成一个虚拟状态表条目 */
        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的设备树节点,在24-76行的for循环中会获取 pinctrl-* 属性,而在pinctrl节点中并没有该属性,pinctrl-* 属性是在一系列的设备节点中添加的,所以会在这里返回错误,同样的错误会一层层的向上级函数传递,最终导致pinctrl_bind_pins函数返回错误,从而不能设置引脚的复用,所以猜想2是不正确的。

在121章中也讲解了瑞芯微的pinctrl的probe函数,在该函数中有一个这样的调用关系:

rockchip pinctrl_probe
	rockchip_pinctrl_registér
		devm_pinctrl_register
			pinctrl_register
				pinctrl_enable
					pinctrl_claim_hogs
						create_pinctrl

从上面的调用关系可以得到pinctrl的probe函数最后也会调用create_pinctrl来创建struct pinctrl 类型的引脚控制器,从而实现pinctrl引脚复用设置,同样的这是的设置也是不成功的,pinctrl_claim_hogs函数定义在内核源码目录下的“drivers/pinctrl/core.c”文件中,具体内容如下所示:

static int pinctrl_claim_hogs(struct pinctrl_dev *pctldev)
{
	pctldev->p = create_pinctrl(pctldev->dev, pctldev);
	if (PTR_ERR(pctldev->p) == -ENODEV) {
		dev_dbg(pctldev->dev, "no hogs found\n");

		return 0;
	}

	if (IS_ERR(pctldev->p)) {
		dev_err(pctldev->dev, "error claiming hogs: %li\n",
			PTR_ERR(pctldev->p));

		return PTR_ERR(pctldev->p);
	}

	pctldev->hog_default =
		pinctrl_lookup_state(pctldev->p, PINCTRL_STATE_DEFAULT);
	if (IS_ERR(pctldev->hog_default)) {
		dev_dbg(pctldev->dev,
			"failed to lookup the default state\n");
	} else {
		if (pinctrl_select_state(pctldev->p,
					 pctldev->hog_default))
			dev_err(pctldev->dev,
				"failed to select default state\n");
	}

	pctldev->hog_sleep =
		pinctrl_lookup_state(pctldev->p,
				     PINCTRL_STATE_SLEEP);
	if (IS_ERR(pctldev->hog_sleep))
		dev_dbg(pctldev->dev,
			"failed to lookup the sleep state\n");

	return 0;
}

该函数和pinctrl_bind_pins函数内容相似,所以也会在第二行的create_pinctrl函数返回错误,所以也就无法执行后面的pinctrl状态的选择和设置了,所以猜想2不成立。

至此,关于pinctrl的相关知识和疑问就都讲解和解答完成了。

你可能感兴趣的:(RK3568驱动开发指南,#,RK3568驱动指南,第十一期,linux,驱动开发)