Skip to content

Commit f07512e

Browse files
LMESTMlinusw
authored andcommitted
pinctrl/pinconfig: add debug interface
This update adds a debugfs interface to modify a pin configuration for a given state in the pinctrl map. This allows to modify the configuration for a non-active state, typically sleep state. This configuration is not applied right away, but only when the state will be entered. This solution is mandated for us by HW validation: in order to test and verify several pin configurations during sleep without recompiling the software. Change log in this patch set; Take into account latest feedback from Stephen Warren: - stale comments update - improved code efficiency and readibility - limit size of global variable pinconf_dbg_conf - remove req_type as it can easily be added later when add/delete requests support is implemented Signed-off-by: Laurent Meunier <[email protected]> Signed-off-by: Linus Walleij <[email protected]>
1 parent 06b62d8 commit f07512e

File tree

2 files changed

+238
-0
lines changed

2 files changed

+238
-0
lines changed

drivers/pinctrl/pinconf.c

Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include <linux/slab.h>
1818
#include <linux/debugfs.h>
1919
#include <linux/seq_file.h>
20+
#include <linux/uaccess.h>
2021
#include <linux/pinctrl/machine.h>
2122
#include <linux/pinctrl/pinctrl.h>
2223
#include <linux/pinctrl/pinconf.h>
@@ -574,13 +575,244 @@ static const struct file_operations pinconf_groups_ops = {
574575
.release = single_release,
575576
};
576577

578+
#define MAX_NAME_LEN 15
579+
580+
struct dbg_cfg {
581+
enum pinctrl_map_type map_type;
582+
char dev_name[MAX_NAME_LEN+1];
583+
char state_name[MAX_NAME_LEN+1];
584+
char pin_name[MAX_NAME_LEN+1];
585+
};
586+
587+
/*
588+
* Goal is to keep this structure as global in order to simply read the
589+
* pinconf-config file after a write to check config is as expected
590+
*/
591+
static struct dbg_cfg pinconf_dbg_conf;
592+
593+
/**
594+
* pinconf_dbg_config_print() - display the pinctrl config from the pinctrl
595+
* map, of the dev/pin/state that was last written to pinconf-config file.
596+
* @s: string filled in with config description
597+
* @d: not used
598+
*/
599+
static int pinconf_dbg_config_print(struct seq_file *s, void *d)
600+
{
601+
struct pinctrl_maps *maps_node;
602+
const struct pinctrl_map *map;
603+
struct pinctrl_dev *pctldev = NULL;
604+
const struct pinconf_ops *confops = NULL;
605+
const struct pinctrl_map_configs *configs;
606+
struct dbg_cfg *dbg = &pinconf_dbg_conf;
607+
int i, j;
608+
bool found = false;
609+
unsigned long config;
610+
611+
mutex_lock(&pinctrl_mutex);
612+
613+
/* Parse the pinctrl map and look for the elected pin/state */
614+
for_each_maps(maps_node, i, map) {
615+
if (map->type != dbg->map_type)
616+
continue;
617+
if (strcmp(map->dev_name, dbg->dev_name))
618+
continue;
619+
if (strcmp(map->name, dbg->state_name))
620+
continue;
621+
622+
for (j = 0; j < map->data.configs.num_configs; j++) {
623+
if (!strcmp(map->data.configs.group_or_pin,
624+
dbg->pin_name)) {
625+
/*
626+
* We found the right pin / state, read the
627+
* config and he pctldev for later use
628+
*/
629+
configs = &map->data.configs;
630+
pctldev = get_pinctrl_dev_from_devname
631+
(map->ctrl_dev_name);
632+
found = true;
633+
break;
634+
}
635+
}
636+
}
637+
638+
if (!found) {
639+
seq_printf(s, "No config found for dev/state/pin, expected:\n");
640+
seq_printf(s, "Searched dev:%s\n", dbg->dev_name);
641+
seq_printf(s, "Searched state:%s\n", dbg->state_name);
642+
seq_printf(s, "Searched pin:%s\n", dbg->pin_name);
643+
seq_printf(s, "Use: modify config_pin <devname> "\
644+
"<state> <pinname> <value>\n");
645+
goto exit;
646+
}
647+
648+
config = *(configs->configs);
649+
seq_printf(s, "Dev %s has config of %s in state %s: 0x%08lX\n",
650+
dbg->dev_name, dbg->pin_name,
651+
dbg->state_name, config);
652+
653+
if (pctldev)
654+
confops = pctldev->desc->confops;
655+
656+
if (confops && confops->pin_config_config_dbg_show)
657+
confops->pin_config_config_dbg_show(pctldev, s, config);
658+
659+
exit:
660+
mutex_unlock(&pinctrl_mutex);
661+
662+
return 0;
663+
}
664+
665+
/**
666+
* pinconf_dbg_config_write() - modify the pinctrl config in the pinctrl
667+
* map, of a dev/pin/state entry based on user entries to pinconf-config
668+
* @user_buf: contains the modification request with expected format:
669+
* modify config_pin <devicename> <state> <pinname> <newvalue>
670+
* modify is literal string, alternatives like add/delete not supported yet
671+
* config_pin is literal, alternatives like config_mux not supported yet
672+
* <devicename> <state> <pinname> are values that should match the pinctrl-maps
673+
* <newvalue> reflects the new config and is driver dependant
674+
*/
675+
static int pinconf_dbg_config_write(struct file *file,
676+
const char __user *user_buf, size_t count, loff_t *ppos)
677+
{
678+
struct pinctrl_maps *maps_node;
679+
const struct pinctrl_map *map;
680+
struct pinctrl_dev *pctldev = NULL;
681+
const struct pinconf_ops *confops = NULL;
682+
struct dbg_cfg *dbg = &pinconf_dbg_conf;
683+
const struct pinctrl_map_configs *configs;
684+
char config[MAX_NAME_LEN+1];
685+
bool found = false;
686+
char buf[128];
687+
char *b = &buf[0];
688+
int buf_size;
689+
char *token;
690+
int i;
691+
692+
/* Get userspace string and assure termination */
693+
buf_size = min(count, (sizeof(buf)-1));
694+
if (copy_from_user(buf, user_buf, buf_size))
695+
return -EFAULT;
696+
buf[buf_size] = 0;
697+
698+
/*
699+
* need to parse entry and extract parameters:
700+
* modify configs_pin devicename state pinname newvalue
701+
*/
702+
703+
/* Get arg: 'modify' */
704+
token = strsep(&b, " ");
705+
if (!token)
706+
return -EINVAL;
707+
if (strcmp(token, "modify"))
708+
return -EINVAL;
709+
710+
/* Get arg type: "config_pin" type supported so far */
711+
token = strsep(&b, " ");
712+
if (!token)
713+
return -EINVAL;
714+
if (strcmp(token, "config_pin"))
715+
return -EINVAL;
716+
dbg->map_type = PIN_MAP_TYPE_CONFIGS_PIN;
717+
718+
/* get arg 'device_name' */
719+
token = strsep(&b, " ");
720+
if (token == NULL)
721+
return -EINVAL;
722+
if (strlen(token) >= MAX_NAME_LEN)
723+
return -EINVAL;
724+
strncpy(dbg->dev_name, token, MAX_NAME_LEN);
725+
726+
/* get arg 'state_name' */
727+
token = strsep(&b, " ");
728+
if (token == NULL)
729+
return -EINVAL;
730+
if (strlen(token) >= MAX_NAME_LEN)
731+
return -EINVAL;
732+
strncpy(dbg->state_name, token, MAX_NAME_LEN);
733+
734+
/* get arg 'pin_name' */
735+
token = strsep(&b, " ");
736+
if (token == NULL)
737+
return -EINVAL;
738+
if (strlen(token) >= MAX_NAME_LEN)
739+
return -EINVAL;
740+
strncpy(dbg->pin_name, token, MAX_NAME_LEN);
741+
742+
/* get new_value of config' */
743+
token = strsep(&b, " ");
744+
if (token == NULL)
745+
return -EINVAL;
746+
if (strlen(token) >= MAX_NAME_LEN)
747+
return -EINVAL;
748+
strncpy(config, token, MAX_NAME_LEN);
749+
750+
mutex_lock(&pinctrl_mutex);
751+
752+
/* Parse the pinctrl map and look for the selected dev/state/pin */
753+
for_each_maps(maps_node, i, map) {
754+
if (strcmp(map->dev_name, dbg->dev_name))
755+
continue;
756+
if (map->type != dbg->map_type)
757+
continue;
758+
if (strcmp(map->name, dbg->state_name))
759+
continue;
760+
761+
/* we found the right pin / state, so overwrite config */
762+
if (!strcmp(map->data.configs.group_or_pin, dbg->pin_name)) {
763+
found = true;
764+
pctldev = get_pinctrl_dev_from_devname(
765+
map->ctrl_dev_name);
766+
configs = &map->data.configs;
767+
break;
768+
}
769+
}
770+
771+
if (!found) {
772+
goto exit;
773+
count = -EINVAL;
774+
}
775+
776+
if (pctldev)
777+
confops = pctldev->desc->confops;
778+
779+
if (confops && confops->pin_config_dbg_parse_modify) {
780+
for (i = 0; i < configs->num_configs; i++) {
781+
confops->pin_config_dbg_parse_modify(pctldev,
782+
config,
783+
&configs->configs[i]);
784+
}
785+
}
786+
787+
exit:
788+
mutex_unlock(&pinctrl_mutex);
789+
790+
return count;
791+
}
792+
793+
static int pinconf_dbg_config_open(struct inode *inode, struct file *file)
794+
{
795+
return single_open(file, pinconf_dbg_config_print, inode->i_private);
796+
}
797+
798+
static const struct file_operations pinconf_dbg_pinconfig_fops = {
799+
.open = pinconf_dbg_config_open,
800+
.write = pinconf_dbg_config_write,
801+
.read = seq_read,
802+
.llseek = seq_lseek,
803+
.release = single_release,
804+
.owner = THIS_MODULE,
805+
};
806+
577807
void pinconf_init_device_debugfs(struct dentry *devroot,
578808
struct pinctrl_dev *pctldev)
579809
{
580810
debugfs_create_file("pinconf-pins", S_IFREG | S_IRUGO,
581811
devroot, pctldev, &pinconf_pins_ops);
582812
debugfs_create_file("pinconf-groups", S_IFREG | S_IRUGO,
583813
devroot, pctldev, &pinconf_groups_ops);
814+
debugfs_create_file("pinconf-config", (S_IRUGO | S_IWUSR | S_IWGRP),
815+
devroot, pctldev, &pinconf_dbg_pinconfig_fops);
584816
}
585817

586818
#endif

include/linux/pinctrl/pinconf.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414

1515
#ifdef CONFIG_PINCONF
1616

17+
#include <linux/pinctrl/machine.h>
18+
1719
struct pinctrl_dev;
1820
struct seq_file;
1921

@@ -28,6 +30,7 @@ struct seq_file;
2830
* @pin_config_set: configure an individual pin
2931
* @pin_config_group_get: get configurations for an entire pin group
3032
* @pin_config_group_set: configure all pins in a group
33+
* @pin_config_group_dbg_set: optional debugfs to modify a pin configuration
3134
* @pin_config_dbg_show: optional debugfs display hook that will provide
3235
* per-device info for a certain pin in debugfs
3336
* @pin_config_group_dbg_show: optional debugfs display hook that will provide
@@ -51,6 +54,9 @@ struct pinconf_ops {
5154
int (*pin_config_group_set) (struct pinctrl_dev *pctldev,
5255
unsigned selector,
5356
unsigned long config);
57+
int (*pin_config_dbg_parse_modify) (struct pinctrl_dev *pctldev,
58+
const char *arg,
59+
unsigned long *config);
5460
void (*pin_config_dbg_show) (struct pinctrl_dev *pctldev,
5561
struct seq_file *s,
5662
unsigned offset);

0 commit comments

Comments
 (0)