diff options
author | Laurent Meunier <laurent.meunier@st.com> | 2013-02-06 09:09:44 +0100 |
---|---|---|
committer | Linus Walleij <linus.walleij@linaro.org> | 2013-02-10 21:11:54 +0100 |
commit | 6f9e41f4e673bf9ae6a5c0831b1524a91ea1c440 (patch) | |
tree | 9cff86fdcbbefdbca5520cc1746163d7ad9534a6 /drivers/pinctrl/pinconf.c | |
parent | pinctrl: samsung: remove duplicated line (diff) | |
download | linux-6f9e41f4e673bf9ae6a5c0831b1524a91ea1c440.tar.xz linux-6f9e41f4e673bf9ae6a5c0831b1524a91ea1c440.zip |
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.
Signed-off-by: Laurent Meunier <laurent.meunier@st.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Diffstat (limited to 'drivers/pinctrl/pinconf.c')
-rw-r--r-- | drivers/pinctrl/pinconf.c | 207 |
1 files changed, 207 insertions, 0 deletions
diff --git a/drivers/pinctrl/pinconf.c b/drivers/pinctrl/pinconf.c index baee2cc46a17..ac8d382a79bb 100644 --- a/drivers/pinctrl/pinconf.c +++ b/drivers/pinctrl/pinconf.c @@ -574,6 +574,207 @@ static const struct file_operations pinconf_groups_ops = { .release = single_release, }; +/* 32bit read/write ressources */ +#define MAX_NAME_LEN 16 +char dbg_pinname[MAX_NAME_LEN]; /* shared: name of the state of the pin*/ +char dbg_state_name[MAX_NAME_LEN]; /* shared: state of the pin*/ +static u32 dbg_config; /* shared: config to be read/set for the pin & state*/ + +static int pinconf_dbg_pinname_print(struct seq_file *s, void *d) +{ + if (strlen(dbg_pinname)) + seq_printf(s, "%s\n", dbg_pinname); + else + seq_printf(s, "No pin name set\n"); + return 0; +} + +static int pinconf_dbg_pinname_open(struct inode *inode, struct file *file) +{ + return single_open(file, pinconf_dbg_pinname_print, inode->i_private); +} + +static int pinconf_dbg_pinname_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + int err; + + if (count > MAX_NAME_LEN) + return -EINVAL; + + err = sscanf(user_buf, "%15s", dbg_pinname); + + if (err != 1) + return -EINVAL; + + return count; +} + +static const struct file_operations pinconf_dbg_pinname_fops = { + .open = pinconf_dbg_pinname_open, + .write = pinconf_dbg_pinname_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static int pinconf_dbg_state_print(struct seq_file *s, void *d) +{ + if (strlen(dbg_state_name)) + seq_printf(s, "%s\n", dbg_pinname); + else + seq_printf(s, "No pin state set\n"); + return 0; +} + +static int pinconf_dbg_state_open(struct inode *inode, struct file *file) +{ + return single_open(file, pinconf_dbg_state_print, inode->i_private); +} + +static int pinconf_dbg_state_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + int err; + + if (count > MAX_NAME_LEN) + return -EINVAL; + + err = sscanf(user_buf, "%15s", dbg_state_name); + + if (err != 1) + return -EINVAL; + + return count; +} + +static const struct file_operations pinconf_dbg_pinstate_fops = { + .open = pinconf_dbg_state_open, + .write = pinconf_dbg_state_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +/** + * pinconf_dbg_config_print() - display the pinctrl config from the pinctrl + * map, of a pin/state pair based on pinname and state that have been + * selected with the debugfs entries pinconf-name and pinconf-state + * @s: contains the 32bits config to be written + * @d: not used + */ +static int pinconf_dbg_config_print(struct seq_file *s, void *d) +{ + struct pinctrl_maps *maps_node; + struct pinctrl_map const *map; + struct pinctrl_dev *pctldev = NULL; + struct pinconf_ops *confops = NULL; + int i, j; + bool found = false; + + mutex_lock(&pinctrl_mutex); + + /* Parse the pinctrl map and look for the elected pin/state */ + for_each_maps(maps_node, i, map) { + if (map->type != PIN_MAP_TYPE_CONFIGS_PIN) + continue; + + if (strncmp(map->name, dbg_state_name, MAX_NAME_LEN) > 0) + continue; + + for (j = 0; j < map->data.configs.num_configs; j++) { + if (0 == strncmp(map->data.configs.group_or_pin, + dbg_pinname, MAX_NAME_LEN)) { + /* We found the right pin / state, read the + * config and store the pctldev */ + dbg_config = map->data.configs.configs[j]; + pctldev = get_pinctrl_dev_from_devname + (map->ctrl_dev_name); + found = true; + break; + } + } + } + + mutex_unlock(&pinctrl_mutex); + + if (found) { + seq_printf(s, "Config of %s in state %s: 0x%08X\n", dbg_pinname, + dbg_state_name, dbg_config); + + if (pctldev) + confops = pctldev->desc->confops; + + if (confops && confops->pin_config_config_dbg_show) + confops->pin_config_config_dbg_show(pctldev, + s, dbg_config); + } else { + seq_printf(s, "No pin found for defined name/state\n"); + } + + return 0; +} + +static int pinconf_dbg_config_open(struct inode *inode, struct file *file) +{ + return single_open(file, pinconf_dbg_config_print, inode->i_private); +} + +/** + * pinconf_dbg_config_write() - overwrite the pinctrl config in thepinctrl + * map, of a pin/state pair based on pinname and state that have been + * selected with the debugfs entries pinconf-name and pinconf-state + */ +static int pinconf_dbg_config_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + int err; + unsigned long config; + struct pinctrl_maps *maps_node; + struct pinctrl_map const *map; + int i, j; + + err = kstrtoul_from_user(user_buf, count, 0, &config); + + if (err) + return err; + + dbg_config = config; + + mutex_lock(&pinctrl_mutex); + + /* Parse the pinctrl map and look for the selected pin/state */ + for_each_maps(maps_node, i, map) { + if (map->type != PIN_MAP_TYPE_CONFIGS_PIN) + continue; + + if (strncmp(map->name, dbg_state_name, MAX_NAME_LEN) > 0) + continue; + + /* we found the right pin / state, so overwrite config */ + for (j = 0; j < map->data.configs.num_configs; j++) { + if (strncmp(map->data.configs.group_or_pin, dbg_pinname, + MAX_NAME_LEN) == 0) + map->data.configs.configs[j] = dbg_config; + } + } + + mutex_unlock(&pinctrl_mutex); + + return count; +} + +static const struct file_operations pinconf_dbg_pinconfig_fops = { + .open = pinconf_dbg_config_open, + .write = pinconf_dbg_config_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + void pinconf_init_device_debugfs(struct dentry *devroot, struct pinctrl_dev *pctldev) { @@ -581,6 +782,12 @@ void pinconf_init_device_debugfs(struct dentry *devroot, devroot, pctldev, &pinconf_pins_ops); debugfs_create_file("pinconf-groups", S_IFREG | S_IRUGO, devroot, pctldev, &pinconf_groups_ops); + debugfs_create_file("pinconf-name", (S_IRUGO | S_IWUSR | S_IWGRP), + devroot, pctldev, &pinconf_dbg_pinname_fops); + debugfs_create_file("pinconf-state", (S_IRUGO | S_IWUSR | S_IWGRP), + devroot, pctldev, &pinconf_dbg_pinstate_fops); + debugfs_create_file("pinconf-config", (S_IRUGO | S_IWUSR | S_IWGRP), + devroot, pctldev, &pinconf_dbg_pinconfig_fops); } #endif |