From patchwork Sun Feb 10 20:11:10 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Linus Walleij X-Patchwork-Id: 2122921 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork1.kernel.org Received: from merlin.infradead.org (merlin.infradead.org [205.233.59.134]) by patchwork1.kernel.org (Postfix) with ESMTP id 090F83FCA4 for ; Sun, 10 Feb 2013 20:14:42 +0000 (UTC) Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.76 #1 (Red Hat Linux)) id 1U4dFn-00078Y-TN; Sun, 10 Feb 2013 20:11:59 +0000 Received: from eu1sys200aog118.obsmtp.com ([207.126.144.145]) by merlin.infradead.org with smtps (Exim 4.76 #1 (Red Hat Linux)) id 1U4dFj-00077E-8R for linux-arm-kernel@lists.infradead.org; Sun, 10 Feb 2013 20:11:56 +0000 Received: from beta.dmz-us.st.com ([167.4.1.35]) (using TLSv1) by eu1sys200aob118.postini.com ([207.126.147.11]) with SMTP ID DSNKURf/A1/1w1Pyu5yVn/vfxLoIPeeYAg+e@postini.com; Sun, 10 Feb 2013 20:11:54 UTC Received: from zeta.dmz-us.st.com (ns4.st.com [167.4.16.71]) by beta.dmz-us.st.com (STMicroelectronics) with ESMTP id 786023D; Sun, 10 Feb 2013 20:10:28 +0000 (GMT) Received: from relay2.stm.gmessaging.net (unknown [10.230.100.18]) by zeta.dmz-us.st.com (STMicroelectronics) with ESMTP id A608570; Sun, 10 Feb 2013 14:14:28 +0000 (GMT) Received: from exdcvycastm022.EQ1STM.local (alteon-source-exch [10.230.100.61]) (using TLSv1 with cipher RC4-MD5 (128/128 bits)) (Client CN "exdcvycastm022", Issuer "exdcvycastm022" (not verified)) by relay2.stm.gmessaging.net (Postfix) with ESMTPS id A4547A8074; Sun, 10 Feb 2013 21:11:06 +0100 (CET) Received: from steludxu4075.lud.stericsson.com (10.230.100.153) by smtp.stericsson.com (10.230.100.30) with Microsoft SMTP Server (TLS) id 8.3.83.0; Sun, 10 Feb 2013 21:11:13 +0100 From: Linus Walleij To: , Subject: [PATCH] pinctrl/pinconfig: add debug interface Date: Sun, 10 Feb 2013 21:11:10 +0100 Message-ID: <1360527070-18430-1-git-send-email-linus.walleij@stericsson.com> X-Mailer: git-send-email 1.7.11.3 MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20130210_151155_665686_3AA3B3BE X-CRM114-Status: GOOD ( 21.82 ) X-Spam-Score: -4.2 (----) X-Spam-Report: SpamAssassin version 3.3.2 on merlin.infradead.org summary: Content analysis details: (-4.2 points) pts rule name description ---- ---------------------- -------------------------------------------------- -2.3 RCVD_IN_DNSWL_MED RBL: Sender listed at http://www.dnswl.org/, medium trust [207.126.144.145 listed in list.dnswl.org] -1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1% [score: 0.0000] Cc: Anmar Oueja , Linus Walleij , Stephen Warren , Laurent Meunier X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: linux-arm-kernel-bounces@lists.infradead.org Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org From: Laurent Meunier 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 Signed-off-by: Linus Walleij --- drivers/pinctrl/core.c | 18 +--- drivers/pinctrl/core.h | 21 +++++ drivers/pinctrl/pinconf.c | 207 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 229 insertions(+), 17 deletions(-) diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c index b0a8e9a..b0de6e7 100644 --- a/drivers/pinctrl/core.c +++ b/drivers/pinctrl/core.c @@ -32,17 +32,6 @@ #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 diff --git a/drivers/pinctrl/core.h b/drivers/pinctrl/core.h index fdd350d..0aacc27 100644 --- a/drivers/pinctrl/core.h +++ b/drivers/pinctrl/core.h @@ -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,12 @@ 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_]) + + diff --git a/drivers/pinctrl/pinconf.c b/drivers/pinctrl/pinconf.c index baee2cc..ac8d382 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