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_count
、get_group_name
和get_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驱动 -- Debug FS之Pinctrl分析
Linux虚拟Pinctrl Demo驱动 -- Debug FS之Pinctrl分析