pinctrl/pinconfig: add debug interface
authorLaurent Meunier <laurent.meunier@st.com>
Wed, 6 Feb 2013 08:09:44 +0000 (09:09 +0100)
committerLinus Walleij <linus.walleij@linaro.org>
Sun, 10 Feb 2013 20:11:54 +0000 (21:11 +0100)
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>
drivers/pinctrl/core.c
drivers/pinctrl/core.h
drivers/pinctrl/pinconf.c

index b0a8e9a3839341ec672c93b587eb0949fe8799ee..b0de6e7f1fdb8cb68566b24f04eee8722ab77862 100644 (file)
 #include "pinmux.h"
 #include "pinconf.h"
 
-/**
- * struct pinctrl_maps - a list item containing part of the mapping table
- * @node: mapping table list node
- * @maps: array of mapping table entries
- * @num_maps: the number of entries in @maps
- */
-struct pinctrl_maps {
-       struct list_head node;
-       struct pinctrl_map const *maps;
-       unsigned num_maps;
-};
 
 static bool pinctrl_dummy_state;
 
@@ -56,13 +45,8 @@ LIST_HEAD(pinctrldev_list);
 static LIST_HEAD(pinctrl_list);
 
 /* List of pinctrl maps (struct pinctrl_maps) */
-static LIST_HEAD(pinctrl_maps);
+LIST_HEAD(pinctrl_maps);
 
-#define for_each_maps(_maps_node_, _i_, _map_) \
-       list_for_each_entry(_maps_node_, &pinctrl_maps, node) \
-               for (_i_ = 0, _map_ = &_maps_node_->maps[_i_]; \
-                       _i_ < _maps_node_->num_maps; \
-                       _i_++, _map_ = &_maps_node_->maps[_i_])
 
 /**
  * pinctrl_provide_dummies() - indicate if pinctrl provides dummy state support
index fdd350d639f60bd2744d5461e65285b699aedbec..ee72f1f6d86239ac91ba6e29be716356502ced5b 100644 (file)
@@ -155,6 +155,18 @@ struct pin_desc {
 #endif
 };
 
+/**
+ * struct pinctrl_maps - a list item containing part of the mapping table
+ * @node: mapping table list node
+ * @maps: array of mapping table entries
+ * @num_maps: the number of entries in @maps
+ */
+struct pinctrl_maps {
+       struct list_head node;
+       struct pinctrl_map const *maps;
+       unsigned num_maps;
+};
+
 struct pinctrl_dev *get_pinctrl_dev_from_devname(const char *dev_name);
 int pin_get_from_name(struct pinctrl_dev *pctldev, const char *name);
 const char *pin_get_name(struct pinctrl_dev *pctldev, const unsigned pin);
@@ -176,3 +188,10 @@ extern int pinctrl_force_default(struct pinctrl_dev *pctldev);
 
 extern struct mutex pinctrl_mutex;
 extern struct list_head pinctrldev_list;
+extern struct list_head pinctrl_maps;
+
+#define for_each_maps(_maps_node_, _i_, _map_) \
+       list_for_each_entry(_maps_node_, &pinctrl_maps, node) \
+               for (_i_ = 0, _map_ = &_maps_node_->maps[_i_]; \
+                       _i_ < _maps_node_->num_maps; \
+                       _i_++, _map_ = &_maps_node_->maps[_i_])
index baee2cc46a17a7c5c328f98c7eaa25f8f10d8fba..ac8d382a79bbfe43de8769ea43d1c13ab2fac036 100644 (file)
@@ -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