Linux虚拟Pinctrl Demo驱动 -- Debug FS之Pinctrl分析

Posted ZHONGCAI0901

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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,<

以上是关于Linux虚拟Pinctrl Demo驱动 -- Debug FS之Pinctrl分析的主要内容,如果未能解决你的问题,请参考以下文章

Linux虚拟Pinctrl Demo驱动

Linux虚拟Pinctrl Demo驱动 -- Debug FS之Pinctrl分析

Linux虚拟Pinctrl Demo驱动 -- Debug FS之Pinctrl分析

Linux虚拟Pinctrl Demo驱动 -- Debug FS之Pinctrl分析

07_编写虚拟的Pinctrl驱动程序

08_调试虚拟的Pinctrl驱动程序