【Linux】虚拟Pinctrl Demo驱动(二) -- Debug FS之Pinctrl分析

1. 前言

我们在调试Pinctrl子系统时,会使用到Debug Filesystem。在/sys/kernel/debug/目录下就会有pinctrl目录,如果该目录下没有任何目录或文件,说明debugfs功能没有被打开。可以参考这篇博客该功能:《问题一:/sys/kernel/debug/下没有任何文件》
在这里插入图片描述

2. DEFINE_SHOW_ATTRIBUTE

我们在kernel内核源码中,经常会发现DEFINE_SHOW_ATTRIBUTE的定义。它具体是什么作用呢?
在这里插入图片描述
Path:Kernel\include\linux\seq_file.h头文件中,我们找到了它的定义:
在这里插入图片描述
通过上面的例子,定义一个DEFINE_SHOW_ATTRIBUTE(pinctrl),就相当于实现了如下代码:

#define DEFINE_SHOW_ATTRIBUTE(pinctrl)					\
static int pinctrl_open(struct inode *inode, struct file *file)	\
{									\
	return single_open(file, pinctrl_show, inode->i_private);	\
}									\
									\
static const struct file_operations pinctrl_fops = {			\
	.owner		= THIS_MODULE,					\
	.open		= pinctrl_open,				\
	.read		= seq_read,					\
	.llseek		= seq_lseek,					\
	.release	= single_release,				\
}

以上可以知,这里定义了一个pinctrl_fops,它肯定会被其它地方用到。在kernel内核源码中搜索pinctrl_fops关键字,发现在下面文件中被引用:
Path:Kernel\drivers\pinctrl\core.c
在这里插入图片描述
通过系统在调用debugfs_create_file函数时,就会在/sys/kernel/debug/pinctrl目录下,创建一个pinctrl-handles文件。

debugfs_create_file("pinctrl-handles", S_IFREG | S_IRUGO, debugfs_root, NULL, &pinctrl_fops);

当系统启动完成后,确实发现了该节点:
在这里插入图片描述
所以,当运行cat命令cat /sys/kernel/debug/pinctrl/pinctrl-handles时,就相当于调用了这些函数:

pinctrl-handles -> pinctrl_open -> pinctrl_show

下面分析在/sys/kernel/debug/pinctrl目录下,所有节点的实现的功能。

3. pinctrl-handles

/sys/kernel/debug/pinctrl目录下的pinctrl-handles,最终会调用pinctrl_show

debugfs_create_file("pinctrl-handles", S_IFREG | S_IRUGO, debugfs_root, NULL, &pinctrl_fops);

pinctrl_show会遍历全局的链表头pinctrl_list,它保存着所有使用pinctrl的设备。这里会遍历每个pinctrl device,并打印它每个state的pinctrl_setting。架构大致如下:

在这里插入图片描述

static int pinctrl_show(struct seq_file *s, void *what)
{
	struct pinctrl *p;
	struct pinctrl_state *state;
	struct pinctrl_setting *setting;

	seq_puts(s, "Requested pin control handlers their pinmux maps:\n");

	mutex_lock(&pinctrl_list_mutex);

	/* 遍历3个pinctrl:12340000.vt_pinctrl、2290000.iomuxc-snvs和20e0000.iomuxc */
	list_for_each_entry(p, &pinctrl_list, node) {
		seq_printf(s, "device: %s current state: %s\n",
			   dev_name(p->dev),
			   p->state ? p->state->name : "none");
		
		/* 打印这个pinctrl device的每个state */
		list_for_each_entry(state, &p->states, node) {
			seq_printf(s, "  state: %s\n", state->name);
			
			/* 打印当前state的pinctrl_setting */
			list_for_each_entry(setting, &state->settings, node) {
				struct pinctrl_dev *pctldev = setting->pctldev;

				seq_printf(s, "    type: %s controller %s ",
					   map_type(setting->type),
					   pinctrl_dev_get_name(pctldev)); // 打印pinctrl的name,如:12340000.vt_pinctrl、2290000.iomuxc-snvs和20e0000.iomuxc。

				switch (setting->type) {
				case PIN_MAP_TYPE_MUX_GROUP:
					/* 打印pin mux复用模式的pinctrl_setting信息 */
					pinmux_show_setting(s, setting);
					break;
				case PIN_MAP_TYPE_CONFIGS_PIN:
				case PIN_MAP_TYPE_CONFIGS_GROUP:
					/* 打印pin config电气特性配置的pinctrl_setting信息 */
					pinconf_show_setting(s, setting);
					break;
				default:
					break;
				}
			}
		}
	}

	mutex_unlock(&pinctrl_list_mutex);

	return 0;
}
DEFINE_SHOW_ATTRIBUTE(pinctrl);

这里是virt_pinctrl_client.c驱动使用了pinctrl virtual_i2c,用到了2个pin:VIRT_PINCTRL_PAD_PIN6和VIRT_PINCTRL_PAD_PIN7,并且复用成I2C功能。
在这里插入图片描述

4. pinctrl-devices

/sys/kernel/debug/pinctrl目录下的pinctrl-devices,最终会调用pinctrl_devices_show

debugfs_create_file("pinctrl-devices", S_IFREG | S_IRUGO, debugfs_root, NULL, &pinctrl_devices_fops);

pinctrl_devices_show会遍历所有的pinctrl控制器,这里有3个:12340000.vt_pinctrl、2290000.iomuxc-snvs和20e0000.iomuxc。

static int pinctrl_devices_show(struct seq_file *s, void *what)
{
	struct pinctrl_dev *pctldev;

	seq_puts(s, "name [pinmux] [pinconf]\n");

	mutex_lock(&pinctrldev_list_mutex);

	/* 遍历所有的pinctrl控制器,如果pmxops和confops就打印yes */
	list_for_each_entry(pctldev, &pinctrldev_list, node) {
		seq_printf(s, "%s ", pctldev->desc->name);
		if (pctldev->desc->pmxops)
			seq_puts(s, "yes ");
		else
			seq_puts(s, "no ");
		if (pctldev->desc->confops)
			seq_puts(s, "yes");
		else
			seq_puts(s, "no");
		seq_puts(s, "\n");
	}

	mutex_unlock(&pinctrldev_list_mutex);

	return 0;
}
DEFINE_SHOW_ATTRIBUTE(pinctrl_devices);

运行命令cat /sys/kernel/debug/pinctrl/pinctrl-devices后的效果如下:
在这里插入图片描述

5. pinctrl-maps

/sys/kernel/debug/pinctrl目录下的pinctrl-maps,最终会调用pinctrl_maps_show

debugfs_create_file("pinctrl-maps", S_IFREG | S_IRUGO, debugfs_root, NULL, &pinctrl_maps_fops);

pinctrl_maps_show会遍历全局的链表头pinctrl_maps,它保存着所有使用pinctrl的设备。这里会遍历每个pinctrl device,并打印它每个pinctrl_map。架构大致如下:
在这里插入图片描述

static int pinctrl_maps_show(struct seq_file *s, void *what)
{
	struct pinctrl_maps *maps_node;
	int i;
	const struct pinctrl_map *map;

	seq_puts(s, "Pinctrl maps:\n");

	mutex_lock(&pinctrl_maps_mutex);
	/* 遍历全局的链表头pinctrl_maps,它保存着所有使用pinctrl的设备。*/
	for_each_maps(maps_node, i, map) {
		seq_printf(s, "device %s\nstate %s\ntype %s (%d)\n",
			   map->dev_name, map->name, map_type(map->type),
			   map->type);

		if (map->type != PIN_MAP_TYPE_DUMMY_STATE)
			seq_printf(s, "controlling device %s\n",
				   map->ctrl_dev_name);

		switch (map->type) {
		case PIN_MAP_TYPE_MUX_GROUP:
			/* 打印pin mux复用模式的pinctrl_map信息 */
			pinmux_show_map(s, map);
			break;
		case PIN_MAP_TYPE_CONFIGS_PIN:
		case PIN_MAP_TYPE_CONFIGS_GROUP:
			/* 打印pin config电气特性配置的pinctrl_map信息 */
			pinconf_show_map(s, map);
			break;
		default:
			break;
		}

		seq_putc(s, '\n');
	}
	mutex_unlock(&pinctrl_maps_mutex);

	return 0;
}
DEFINE_SHOW_ATTRIBUTE(pinctrl_maps);

运行命令cat /sys/kernel/debug/pinctrl/pinctrl-maps后的效果如下:
在这里插入图片描述

6. 12340000.vt_pinctrl

12340000.vt_pinctrl是上一篇博客《虚拟Pinctrl Demo驱动(一)-- Demo Code》生成的pinctrl控制器。接下来会分析/sys/kernel/debug/pinctrl/12340000.vt_pinctrl目录下所有节点的作用。
在这里插入图片描述

6.1 pinconf-pins

/sys/kernel/debug/pinctrl/12340000.vt_pinctrl目录下的pinconf-pins,最终会调用pinconf_pins_show

debugfs_create_file("pinconf-pins", S_IFREG | S_IRUGO, devroot, pctldev, &pinconf_pins_fops);

pinconf_pins_show主要是打印该pinctrl每个pin当前的config,这些pin id和pin name信息都保存在struct pinctrl_pin_desc中,然后通过pin id找到pin config的信息并打印出来。
在这里插入图片描述

static int pinconf_pins_show(struct seq_file *s, void *what)
{
	struct pinctrl_dev *pctldev = s->private;
	unsigned i, pin;

	seq_puts(s, "Pin config settings per pin\n");
	seq_puts(s, "Format: pin (name): configs\n");

	mutex_lock(&pctldev->mutex);

	/* The pin number can be retrived from the pin controller descriptor */
	for (i = 0; i < pctldev->desc->npins; i++) {
		struct pin_desc *desc;

		pin = pctldev->desc->pins[i].number; // 获取pin id
		desc = pin_desc_get(pctldev, pin); // 根据pin id获取struct pin_desc描述符
		/* Skip if we cannot search the pin */
		if (!desc)
			continue;

		seq_printf(s, "pin %d (%s): ", pin, desc->name);

		pinconf_dump_pin(pctldev, s, pin); // 根据pin id打印pin config信息
		seq_putc(s, '\n');
	}

	mutex_unlock(&pctldev->mutex);

	return 0;
}

通过pinconf_dump_pin间接调用virt_pinctrl_driver.c驱动里面的virt_pinconf_dbg_show,这里是模拟打印pin mux寄存器和pin config寄存器里面的内容。

static void pinconf_dump_pin(struct pinctrl_dev *pctldev, struct seq_file *s, int pin)
{
	const struct pinconf_ops *ops = pctldev->desc->confops;
	if (ops && ops->pin_config_dbg_show)
		ops->pin_config_dbg_show(pctldev, s, pin);
}

static void virt_pinconf_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s, unsigned pin_id)
{
  struct virt_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev);

  seq_printf(s, "Mux=0x%x Config=0x%x", ipctl->configs[pin_id].mux_mode, ipctl->configs[pin_id].pin_conf);
}

static const struct pinconf_ops virt_pinconf_ops = {
	.pin_config_set = virt_pinconf_set,
	.pin_config_dbg_show = virt_pinconf_dbg_show,
	.pin_config_group_dbg_show = virt_pinconf_group_dbg_show,
};

运行命令cat /sys/kernel/debug/pinctrl/12340000.vt_pinctrl/pinconf-pins后的效果如下:
在这里插入图片描述
这里是模拟打印pin mux寄存器和pin config寄存器里面的内容,对于未使用的pin,默认都是0。

6.2 pinconf-groups

/sys/kernel/debug/pinctrl/12340000.vt_pinctrl目录下的pinconf-groups,最终会调用pinconf_groups_show

debugfs_create_file("pinconf-groups", S_IFREG | S_IRUGO, devroot, pctldev, &pinconf_groups_fops);

pinconf_groups_show主要是打印该pinctrl中每个group的pin信息,这些group信息都保存在pin_group_tree中,通过它就可以找到group的每个pin的配置信息。这些配置信息是在pinctrl driver加载时,解析device tree得到的。
在这里插入图片描述

static int pinconf_groups_show(struct seq_file *s, void *what)
{
	struct pinctrl_dev *pctldev = s->private;
	const struct pinctrl_ops *pctlops = pctldev->desc->pctlops;
	unsigned ngroups = pctlops->get_groups_count(pctldev); // 获取group的个数,它被定义在virt_pinctrl_driver.c驱动文件里面。
	unsigned selector = 0;

	seq_puts(s, "Pin config settings per pin group\n");
	seq_puts(s, "Format: group (name): configs\n");

	while (selector < ngroups) {
		const char *gname = pctlops->get_group_name(pctldev, selector); // 获取group的name,它被定义在virt_pinctrl_driver.c驱动文件里面。

		seq_printf(s, "%u (%s): ", selector, gname);
		pinconf_dump_group(pctldev, s, selector, gname); // 打印group中每个pin的配置信息,它被定义在virt_pinctrl_driver.c驱动文件里面。
		seq_putc(s, '\n');
		selector++;
	}

	return 0;
}

通过pinconf_dump_group间接调用virt_pinctrl_driver.c驱动里面的virt_pinconf_group_dbg_show,这里是模拟打印group中pin相关寄存器里面的内容。

static void pinconf_dump_group(struct pinctrl_dev *pctldev, struct seq_file *s, unsigned selector, const char *gname)
{
	const struct pinconf_ops *ops = pctldev->desc->confops;
	if (ops && ops->pin_config_group_dbg_show)
		ops->pin_config_group_dbg_show(pctldev, s, selector);
}


 static void virt_pinconf_group_dbg_show(struct pinctrl_dev *pctldev,
                      struct seq_file *s, unsigned group)
 {
     struct group_desc *grp;
     const char *name;
     int i;
 
     if (group >= pctldev->num_groups)
         return;
 
     seq_puts(s, "\n");
     grp = pinctrl_generic_get_group(pctldev, group);
     if (!grp)
         return;
 
     for (i = 0; i < grp->num_pins; i++) {
         struct virt_pin *pin = &((struct virt_pin *)(grp->data))[i];
 
         name = pin_get_name(pctldev, pin->pin);
         seq_printf(s, "  %s: Mux=0x%x Config=0x%x\n", name, pin->config.mux_mode, pin->config.pin_conf);
     }
 }
 
static const struct pinconf_ops virt_pinconf_ops = {
	.pin_config_set = virt_pinconf_set,
	.pin_config_dbg_show = virt_pinconf_dbg_show,
	.pin_config_group_dbg_show = virt_pinconf_group_dbg_show,
};

static const struct pinctrl_ops virt_pctrl_ops = {
	.get_groups_count = pinctrl_generic_get_group_count,
	.get_group_name = pinctrl_generic_get_group_name,
	.get_group_pins = pinctrl_generic_get_group_pins,
	.pin_dbg_show = virt_pin_dbg_show,
	.dt_node_to_map = virt_dt_node_to_map,
	.dt_free_map = virt_dt_free_map,
};

运行命令cat /sys/kernel/debug/pinctrl/12340000.vt_pinctrl/pinconf-groups后的效果如下:
在这里插入图片描述

6.3 pingroups

/sys/kernel/debug/pinctrl/12340000.vt_pinctrl目录下的pingroups,最终会调用pinctrl_groups_show

debugfs_create_file("pingroups", S_IFREG | S_IRUGO, device_root, pctldev, &pinctrl_groups_fops);

pinctrl_groups_show主要是打印保存在pin_group_tree中的group,打印包含group中pin number和pin name的信息。

static int pinctrl_groups_show(struct seq_file *s, void *what)
{
	struct pinctrl_dev *pctldev = s->private;
	const struct pinctrl_ops *ops = pctldev->desc->pctlops;
	unsigned ngroups, selector = 0;

	mutex_lock(&pctldev->mutex);

	ngroups = ops->get_groups_count(pctldev); // 获取group的个数

	seq_puts(s, "registered pin groups:\n");
	while (selector < ngroups) {
		const unsigned *pins = NULL;
		unsigned num_pins = 0;
		const char *gname = ops->get_group_name(pctldev, selector); // 获取group的name
		const char *pname;
		int ret = 0;
		int i;

		if (ops->get_group_pins)
		    /* 获取当前group中所有的pin的信息 */
			ret = ops->get_group_pins(pctldev, selector,
						  &pins, &num_pins);
		if (ret)
			seq_printf(s, "%s [ERROR GETTING PINS]\n",
				   gname);
		else {
			seq_printf(s, "group: %s\n", gname); // 打印group的name
			for (i = 0; i < num_pins; i++) {
				pname = pin_get_name(pctldev, pins[i]); // 获取pin的name
				if (WARN_ON(!pname)) {
					mutex_unlock(&pctldev->mutex);
					return -EINVAL;
				}
				seq_printf(s, "pin %d (%s)\n", pins[i], pname); // 打印pin id和pin name信息
			}
			seq_puts(s, "\n");
		}
		selector++;
	}

	mutex_unlock(&pctldev->mutex);

	return 0;
}

这些get_groups_countget_group_nameget_group_pins都被定义在virt_pinctrl_driver.c驱动里面,其实也是调用pinctrl_generic_get_group_count等公用的内核函数接口,因为这些信息已经保存在了pin_group_tree和num_groups中,所以只有遍历它就可以了。
在这里插入图片描述

static const struct pinctrl_ops virt_pctrl_ops = {
	.get_groups_count = pinctrl_generic_get_group_count,
	.get_group_name = pinctrl_generic_get_group_name,
	.get_group_pins = pinctrl_generic_get_group_pins,
	.pin_dbg_show = virt_pin_dbg_show,
	.dt_node_to_map = virt_dt_node_to_map,
	.dt_free_map = virt_dt_free_map,
};

运行命令cat /sys/kernel/debug/pinctrl/12340000.vt_pinctrl/pingroups后的效果如下:
在这里插入图片描述

6.4 pins

/sys/kernel/debug/pinctrl/12340000.vt_pinctrl目录下的pins,最终会调用pinctrl_pins_show

debugfs_create_file("pins", S_IFREG | S_IRUGO, device_root, pctldev, &pinctrl_pins_fops);

pinctrl_pins_show主要是打印保存在struct pinctrl_desc中的pins,打印在virt_pinctrl_driver.c驱动定义的中pin number和pin name的信息。
在这里插入图片描述

static int pinctrl_pins_show(struct seq_file *s, void *what)
{
	struct pinctrl_dev *pctldev = s->private;
	const struct pinctrl_ops *ops = pctldev->desc->pctlops;
	unsigned i, pin;

	seq_printf(s, "registered pins: %d\n", pctldev->desc->npins);

	mutex_lock(&pctldev->mutex);

	/* The pin number can be retrived from the pin controller descriptor */
	for (i = 0; i < pctldev->desc->npins; i++) {
		struct pin_desc *desc;

		pin = pctldev->desc->pins[i].number; // 获取pin id
		desc = pin_desc_get(pctldev, pin); // 根据pin id获取struct pin_desc描述符
		/* Pin space may be sparse */
		if (!desc)
			continue;

		seq_printf(s, "pin %d (%s) ", pin, desc->name); // 打印pin id和pin name

		/* Driver-specific info per pin */
		if (ops->pin_dbg_show)
			ops->pin_dbg_show(pctldev, s, pin); // pin_dbg_show被定义在virt_pinctrl_driver.c驱动文件里面。

		seq_puts(s, "\n");
	}

	mutex_unlock(&pctldev->mutex);

	return 0;
}

通过ops->pin_dbg_show间接调用virt_pinctrl_driver.c驱动里面的virt_pin_dbg_show,这里只是打印pinctrl device name, 12340000.vt_pinctrl。

static void virt_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s,
		   unsigned offset)
{
	seq_printf(s, "%s", dev_name(pctldev->dev)); // 打印pinctrl device name, 12340000.vt_pinctrl。
}

static const struct pinctrl_ops virt_pctrl_ops = {
	.get_groups_count = pinctrl_generic_get_group_count,
	.get_group_name = pinctrl_generic_get_group_name,
	.get_group_pins = pinctrl_generic_get_group_pins,
	.pin_dbg_show = virt_pin_dbg_show,
	.dt_node_to_map = virt_dt_node_to_map,
	.dt_free_map = virt_dt_free_map,
};

运行命令cat /sys/kernel/debug/pinctrl/12340000.vt_pinctrl/pins后的效果如下:
在这里插入图片描述

6.5 pinmux-functions

/sys/kernel/debug/pinctrl/12340000.vt_pinctrl目录下的pinmux-functions,最终会调用pinmux_functions_show

debugfs_create_file("pinmux-functions", S_IFREG | S_IRUGO, devroot, pctldev, &pinmux_functions_fops);

pinmux_functions_show主要是打印保存在pin_function_tree中的function信息,里面包含了该function包含的group name列表。大致架构如下:
在这里插入图片描述

static int pinmux_functions_show(struct seq_file *s, void *what)
{
	struct pinctrl_dev *pctldev = s->private;
	const struct pinmux_ops *pmxops = pctldev->desc->pmxops;
	unsigned nfuncs;
	unsigned func_selector = 0;

	if (!pmxops)
		return 0;

	mutex_lock(&pctldev->mutex);
	/* 获取functions的个数,个数保存在struct pinctrl_dev中的num_functions变量中。*/
	nfuncs = pmxops->get_functions_count(pctldev); 
	while (func_selector < nfuncs) {
		/* 获取functions的name,个数保存在struct pinctrl_dev的pin_function_tree链表中。
		 * struct function_desc中的name就是function name。
		 */
		const char *func = pmxops->get_function_name(pctldev,
							  func_selector);
		const char * const *groups;
		unsigned num_groups;
		int ret;
		int i;
		
		/* 获取保存在pin_function_tree中的group name和group个数 */
		ret = pmxops->get_function_groups(pctldev, func_selector,
						  &groups, &num_groups);
		if (ret) {
			seq_printf(s, "function %s: COULD NOT GET GROUPS\n",
				   func);
			func_selector++;
			continue;
		}

		/* 打印该function所有的group name */
		seq_printf(s, "function: %s, groups = [ ", func);
		for (i = 0; i < num_groups; i++)
			seq_printf(s, "%s ", groups[i]);
		seq_puts(s, "]\n");

		func_selector++;
	}

	mutex_unlock(&pctldev->mutex);

	return 0;
}

运行命令cat /sys/kernel/debug/pinctrl/12340000.vt_pinctrl/pinmux-functions后的效果如下:
在这里插入图片描述

6.6 pinmux-pins

/sys/kernel/debug/pinctrl/12340000.vt_pinctrl目录下的pinmux-pins,最终会调用pinmux_pins_show

debugfs_create_file("pinmux-pins", S_IFREG | S_IRUGO, devroot, pctldev, &pinmux_pins_fops);

pinmux_pins_show主要是显示pin当前复用成什么功能,未使用的pin会显示UNCLAIMED。如果被复用成其它功能,会显示哪个驱动使用,哪组配置。

static int pinmux_pins_show(struct seq_file *s, void *what)
{
	struct pinctrl_dev *pctldev = s->private;
	const struct pinctrl_ops *pctlops = pctldev->desc->pctlops;
	const struct pinmux_ops *pmxops = pctldev->desc->pmxops;
	unsigned i, pin;

	if (!pmxops)
		return 0;

	seq_puts(s, "Pinmux settings per pin\n");
	if (pmxops->strict)
		seq_puts(s,
		 "Format: pin (name): mux_owner|gpio_owner (strict) hog?\n");
	else
		seq_puts(s,
		"Format: pin (name): mux_owner gpio_owner hog?\n");

	mutex_lock(&pctldev->mutex);

	/* The pin number can be retrived from the pin controller descriptor */
	for (i = 0; i < pctldev->desc->npins; i++) {
		struct pin_desc *desc;
		bool is_hog = false;

		pin = pctldev->desc->pins[i].number;
		desc = pin_desc_get(pctldev, pin);
		/* Skip if we cannot search the pin */
		if (desc == NULL)
			continue;

		if (desc->mux_owner &&
		    !strcmp(desc->mux_owner, pinctrl_dev_get_name(pctldev)))
			is_hog = true;

		if (pmxops->strict) {
			if (desc->mux_owner)
				seq_printf(s, "pin %d (%s): device %s%s",
					   pin, desc->name, desc->mux_owner,
					   is_hog ? " (HOG)" : "");
			else if (desc->gpio_owner)
				seq_printf(s, "pin %d (%s): GPIO %s",
					   pin, desc->name, desc->gpio_owner);
			else
				seq_printf(s, "pin %d (%s): UNCLAIMED",
					   pin, desc->name);
		} else {
			/* For non-strict controllers */
			seq_printf(s, "pin %d (%s): %s %s%s", pin, desc->name,
				   desc->mux_owner ? desc->mux_owner
				   : "(MUX UNCLAIMED)",
				   desc->gpio_owner ? desc->gpio_owner
				   : "(GPIO UNCLAIMED)",
				   is_hog ? " (HOG)" : "");
		}

		/* If mux: print function+group claiming the pin */
		if (desc->mux_setting)
			seq_printf(s, " function %s group %s\n",
				   pmxops->get_function_name(pctldev,
					desc->mux_setting->func),
				   pctlops->get_group_name(pctldev,
					desc->mux_setting->group));
		else
			seq_putc(s, '\n');
	}

	mutex_unlock(&pctldev->mutex);

	return 0;
}

运行命令cat /sys/kernel/debug/pinctrl/12340000.vt_pinctrl/pinmux-pins后的效果如下:
在这里插入图片描述

7. 工程代码下载地址

完整的实验工程Demo代码下载地址如下:
https://download.csdn.net/download/ZHONGCAI0901/86734001

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值