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);
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");
list_for_each_entry(state, &p->states, node) {
seq_printf(s, " state: %s\n", state->name);
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));
switch (setting->type) {
case PIN_MAP_TYPE_MUX_GROUP:
pinmux_show_setting(s, setting);
break;
case PIN_MAP_TYPE_CONFIGS_PIN:
case PIN_MAP_TYPE_CONFIGS_GROUP:
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);
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);
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:
pinmux_show_map(s, map);
break;
case PIN_MAP_TYPE_CONFIGS_PIN:
case PIN_MAP_TYPE_CONFIGS_GROUP:
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);
for (i = 0; i < pctldev->desc->npins; i++) {
struct pin_desc *desc;
pin = pctldev->desc->pins[i].number;
desc = pin_desc_get(pctldev, pin);
if (!desc)
continue;
seq_printf(s, "pin %d (%s): ", pin, desc->name);
pinconf_dump_pin(pctldev, s, pin);
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);
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);
seq_printf(s, "%u (%s): ", selector, gname);
pinconf_dump_group(pctldev, s, selector, gname);
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);
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);
const char *pname;
int ret = 0;
int i;
if (ops->get_group_pins)
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);
for (i = 0; i < num_pins; i++) {
pname = pin_get_name(pctldev, pins[i]);
if (WARN_ON(!pname)) {
mutex_unlock(&pctldev->mutex);
return -EINVAL;
}
seq_printf(s, "pin %d (%s)\n", pins[i], pname);
}
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,
.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);
for (i = 0; i < pctldev->desc->npins; i++) {
struct pin_desc *desc;
pin = pctldev->desc->pins[i].number;
desc = pin_desc_get(pctldev, pin);
if (!desc)
continue;
seq_printf(s, "pin %d (%s) ", pin, desc->name);
if (ops->pin_dbg_show)
ops->pin_dbg_show(pctldev, s, pin);
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));
}
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);
nfuncs = pmxops->get_functions_count(pctldev);
while (func_selector < nfuncs) {
const char *func = pmxops->get_function_name(pctldev,
func_selector);
const char * const *groups;
unsigned num_groups;
int ret;
int i;
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;
}
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);
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);
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 {
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 (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
|