From patchwork Mon Jan 16 14:51:36 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Linus Walleij X-Patchwork-Id: 6231 Return-Path: X-Original-To: patchwork@peony.canonical.com Delivered-To: patchwork@peony.canonical.com Received: from fiordland.canonical.com (fiordland.canonical.com [91.189.94.145]) by peony.canonical.com (Postfix) with ESMTP id 7092F23F02 for ; Mon, 16 Jan 2012 14:52:25 +0000 (UTC) Received: from mail-bk0-f52.google.com (mail-bk0-f52.google.com [209.85.214.52]) by fiordland.canonical.com (Postfix) with ESMTP id 46664A18402 for ; Mon, 16 Jan 2012 14:52:25 +0000 (UTC) Received: by bkbzt4 with SMTP id zt4so591321bkb.11 for ; Mon, 16 Jan 2012 06:52:25 -0800 (PST) Received: by 10.205.141.72 with SMTP id jd8mr507065bkc.135.1326725544884; Mon, 16 Jan 2012 06:52:24 -0800 (PST) X-Forwarded-To: linaro-patchwork@canonical.com X-Forwarded-For: patch@linaro.org linaro-patchwork@canonical.com Delivered-To: patches@linaro.org Received: by 10.205.82.144 with SMTP id ac16cs92862bkc; Mon, 16 Jan 2012 06:52:24 -0800 (PST) Received: by 10.204.145.72 with SMTP id c8mr4844533bkv.79.1326725544153; Mon, 16 Jan 2012 06:52:24 -0800 (PST) Received: from eu1sys200aog103.obsmtp.com (eu1sys200aog103.obsmtp.com. [207.126.144.115]) by mx.google.com with SMTP id x11si12579368eem.213.2012.01.16.06.52.05 (version=TLSv1/SSLv3 cipher=OTHER); Mon, 16 Jan 2012 06:52:24 -0800 (PST) Received-SPF: neutral (google.com: 207.126.144.115 is neither permitted nor denied by best guess record for domain of linus.walleij@stericsson.com) client-ip=207.126.144.115; Authentication-Results: mx.google.com; spf=neutral (google.com: 207.126.144.115 is neither permitted nor denied by best guess record for domain of linus.walleij@stericsson.com) smtp.mail=linus.walleij@stericsson.com Received: from beta.dmz-us.st.com ([167.4.1.35]) (using TLSv1) by eu1sys200aob103.postini.com ([207.126.147.11]) with SMTP ID DSNKTxQ5lA92LPHGFag30Xad82Oi/pVLEa6H@postini.com; Mon, 16 Jan 2012 14:52:23 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 024DF59; Mon, 16 Jan 2012 14:51:37 +0000 (GMT) Received: from relay1.stm.gmessaging.net (unknown [10.230.100.17]) by zeta.dmz-us.st.com (STMicroelectronics) with ESMTP id 893DC49; Mon, 16 Jan 2012 13:58:09 +0000 (GMT) Received: from exdcvycastm004.EQ1STM.local (alteon-source-exch [10.230.100.61]) (using TLSv1 with cipher RC4-MD5 (128/128 bits)) (Client CN "exdcvycastm004", Issuer "exdcvycastm004" (not verified)) by relay1.stm.gmessaging.net (Postfix) with ESMTPS id 5459424C075; Mon, 16 Jan 2012 15:51:37 +0100 (CET) Received: from steludxu4075.lud.stericsson.com (10.230.100.153) by smtp.stericsson.com (10.230.100.2) with Microsoft SMTP Server (TLS) id 8.3.83.0; Mon, 16 Jan 2012 15:51:44 +0100 From: Linus Walleij To: , Cc: Stephen Warren , Grant Likely , Barry Song <21cnbao@gmail.com>, Shawn Guo , Thomas Abraham , Dong Aisheng , Rajendra Nayak , Haojian Zhuang , Linus Walleij Subject: [PATCH] pinctrl: pin configuration states Date: Mon, 16 Jan 2012 15:51:36 +0100 Message-ID: <1326725496-28928-1-git-send-email-linus.walleij@stericsson.com> X-Mailer: git-send-email 1.7.8 MIME-Version: 1.0 From: Linus Walleij This introduce a pin configuration state structure and activation functions similar to the pinmux map. It basically names a few states and define the custom configuration values to be applied to groups and pins alike when switching to a certain state. Signed-off-by: Linus Walleij --- Maybe this is closer to what people want for configuring the pins in their platforms? I am not fully satisfied with this solution because it basically maintains status quo of the big static tables found in many ARM SoCs, but it does instill a common scheme for doing it. Also there is no way for a pinmux and its associated device to switch states in this solution. However one does not exclude the other, we might want per-device associated pinmux and group states *also*. Comments welcome. --- Documentation/pinctrl.txt | 63 +++++++++++ drivers/pinctrl/core.c | 3 + drivers/pinctrl/pinconf.c | 231 +++++++++++++++++++++++++++++++++++++- drivers/pinctrl/pinconf.h | 11 ++ include/linux/pinctrl/machine.h | 79 +++++++++++++ 5 files changed, 380 insertions(+), 7 deletions(-) diff --git a/Documentation/pinctrl.txt b/Documentation/pinctrl.txt index 1851f1d..29845c4 100644 --- a/Documentation/pinctrl.txt +++ b/Documentation/pinctrl.txt @@ -277,6 +277,69 @@ case each individual pin will be treated by separate pin_config_set() calls as well. +Pin configuration states +======================== + +To help platforms to set up initial pin configuration and transit the entire +set of pins on a pin controller between different states, pin controller +states are supported by creating named states per controller. A board/machine +can define a pin configuration like this: + +static const struct pin_config foo_pin_config_active[] = { + PIN_CONFIG("GPIO0", PLATFORM_X_PULL_UP), + PIN_CONFIG("GPIO1", PLATFORM_X_PULL_DOWN), +}; + +static const struct pin_config foo_pin_config_idle[] = { + PIN_CONFIG("GPIO0", PLATFORM_X_GROUND), + PIN_CONFIG("GPIO1", PLATFORM_X_GROUND), +}; + +static const struct pin_config foo_pin_config_idle[] = { + PIN_CONFIG("GPIO0", PLATFORM_X_OFF), + PIN_CONFIG("GPIO1", PLATFORM_X_OFF), +}; + +static struct pin_config_state __initdata foo_pinconf_states[] = { + { + .name = "active", + .ctrl_dev_name = "foo", + .pin_configs = foo_pin_config_active, + .nr_pin_configs = ARRAY_SIZE(foo_pin_config_active), + .apply_on_init = true, + }, + { + .name = "idle", + .ctrl_dev_name = "foo", + .pin_configs = foo_pin_config_idle, + .nr_pin_configs = ARRAY_SIZE(foo_pin_config_idle), + }, + { + .name = "off", + .ctrl_dev_name = "foo", + .pin_configs = foo_pin_config_idle, + .nr_pin_configs = ARRAY_SIZE(foo_pin_config_idle), + .apply_on_exit = true, + }, +}; + +The first three arrays of configurations are simply the names of some pins +and the custom configuration that is associated with each. + +So there are three states for the pin controller, and the "active" state will +pull two GPIO pins, whereas "idle" will ground them and "off" will deactivate +them completely. In this case "active" will be auto-applied when your pin +controller registers, and "off" will be auto-applied when the controller is +removed. At runtime, the system may want to ground both pins by simply calling: + +ret = pinconf_set_state("foo", "idle"); + +Which will apply the "idle" configuration. + +For simple systems that just configure the pins on boot and then forget about +them, the first configuration table may be sufficient. + + Generic pin configuration ========================= diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c index 569bdb3..15d5adf 100644 --- a/drivers/pinctrl/core.c +++ b/drivers/pinctrl/core.c @@ -541,6 +541,7 @@ static void pinctrl_init_debugfs(void) debugfs_create_file("pinctrl-devices", S_IFREG | S_IRUGO, debugfs_root, NULL, &pinctrl_devices_ops); pinmux_init_debugfs(debugfs_root); + pinconf_init_debugfs(debugfs_root); } #else /* CONFIG_DEBUG_FS */ @@ -622,6 +623,7 @@ struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc, list_add(&pctldev->node, &pinctrldev_list); mutex_unlock(&pinctrldev_list_mutex); pinmux_hog_maps(pctldev); + pinconf_apply_initexit(pctldev, true); return pctldev; out_err: @@ -642,6 +644,7 @@ void pinctrl_unregister(struct pinctrl_dev *pctldev) return; pinmux_unhog_maps(pctldev); + pinconf_apply_initexit(pctldev, false); /* TODO: check that no pinmuxes are still active? */ mutex_lock(&pinctrldev_list_mutex); list_del(&pctldev->node); diff --git a/drivers/pinctrl/pinconf.c b/drivers/pinctrl/pinconf.c index 819fcd9..5830786 100644 --- a/drivers/pinctrl/pinconf.c +++ b/drivers/pinctrl/pinconf.c @@ -23,6 +23,10 @@ #include "core.h" #include "pinconf.h" +/* Global pin configuration states */ +static struct pin_config_state *pinconf_states; +static unsigned pinconf_states_num; + int pin_config_get_for_pin(struct pinctrl_dev *pctldev, unsigned pin, unsigned long *config) { @@ -138,11 +142,10 @@ int pin_config_group_get(const char *dev_name, const char *pin_group, } EXPORT_SYMBOL(pin_config_group_get); - -int pin_config_group_set(const char *dev_name, const char *pin_group, - unsigned long config) +static int pin_config_group_set_for_group(struct pinctrl_dev *pctldev, + const char *pin_group, + unsigned long config) { - struct pinctrl_dev *pctldev; const struct pinconf_ops *ops; const struct pinctrl_ops *pctlops; int selector; @@ -151,9 +154,6 @@ int pin_config_group_set(const char *dev_name, const char *pin_group, int ret; int i; - pctldev = get_pinctrl_dev_from_dev(NULL, dev_name); - if (!pctldev) - return -EINVAL; ops = pctldev->desc->confops; pctlops = pctldev->desc->pctlops; @@ -203,6 +203,20 @@ int pin_config_group_set(const char *dev_name, const char *pin_group, return 0; } + +int pin_config_group_set(const char *dev_name, const char *pin_group, + unsigned long config) +{ + struct pinctrl_dev *pctldev; + + pctldev = get_pinctrl_dev_from_dev(NULL, dev_name); + if (!pctldev) + return -EINVAL; + + return pin_config_group_set_for_group(pctldev, + pin_group, + config); +} EXPORT_SYMBOL(pin_config_group_set); int pinconf_check_ops(const struct pinconf_ops *ops) @@ -216,6 +230,167 @@ int pinconf_check_ops(const struct pinconf_ops *ops) return 0; } +int __init pinconf_register_pin_states(struct pin_config_state const *states, + unsigned num_states) +{ + void *tmp; + int i; + + pr_debug("add %d pin configuration states\n", num_states); + + /* First sanity check the new states */ + for (i = 0; i < num_states; i++) { + if (!states[i].name) { + pr_err("failed to register config state %d: " + "no state name given\n", i); + return -EINVAL; + } + + if (!states[i].ctrl_dev && !states[i].ctrl_dev_name) { + pr_err("failed to register config state %s (%d): " + "no pin control device given\n", + states[i].name, i); + return -EINVAL; + } + + if (!states[i].pin_configs && !states[i].pin_group_configs) { + pr_err("failed to register config state %s (%d): " + "no pin or group states defined in it\n", + states[i].name, i); + return -EINVAL; + } + else + pr_debug("register pin config state %s\n", + states[i].name); + } + + /* + * Make a copy of the config state array - string pointers will end up + * in the kernel const section anyway so these do not need to be deep + * copied. Note that the pointers to the config tuples are *not* being + * copied as of now, these cannot be declared __init_data. + */ + if (!pinconf_states_num) { + /* On first call, just copy them */ + tmp = kmemdup(states, + sizeof(struct pin_config_state) * num_states, + GFP_KERNEL); + if (!tmp) + return -ENOMEM; + } else { + /* Subsequent calls, reallocate array to new size */ + size_t oldsize = sizeof(struct pin_config_state) * + pinconf_states_num; + size_t newsize = sizeof(struct pin_config_state) * num_states; + + tmp = krealloc(pinconf_states, oldsize + newsize, GFP_KERNEL); + if (!tmp) + return -ENOMEM; + memcpy((tmp + oldsize), states, newsize); + } + + pinconf_states = tmp; + pinconf_states_num += num_states; + return 0; +} + +/** + * pinconf_apply_state() - apply a certain pin configuration state on a certain + * pin controller + * @pctldev: the pin controller to apply the state to + * @pstate: the configuration state to apply on the pin controller + */ +static int pinconf_apply_state(struct pinctrl_dev *pctldev, + struct pin_config_state const *pstate) +{ + int ret, i; + + /* Apply group configs first */ + for (i = 0; i < pstate->nr_pin_group_configs; i++) { + const struct pin_group_config *groupconf = + &pstate->pin_group_configs[i]; + + ret = pin_config_group_set_for_group(pctldev, + groupconf->group_name, + groupconf->config); + if (ret < 0) + return ret; + } + + /* Then apply individual pin configs */ + for (i = 0; i < pstate->nr_pin_configs; i++) { + const struct pin_config *pinconf = + &pstate->pin_configs[i]; + int pin; + + pin = pin_get_from_name(pctldev, pinconf->pin_name); + if (pin < 0) + return pin; + + ret = pin_config_set_for_pin(pctldev, pin, pinconf->config); + if (ret < 0) + return ret; + } + + return 0; +} + +/** + * pinconf_set_state() - sets the state for pins and groups on a pin controller + * @dev_name: the textual name for the pin controller + * @state: name of the state to set + */ +int pinconf_activate_state(const char *dev_name, const char *state) +{ + struct pinctrl_dev *pctldev; + int i; + + pctldev = get_pinctrl_dev_from_dev(NULL, dev_name); + if (!pctldev) + return -EINVAL; + + for (i = 0; i < pinconf_states_num; i++) { + struct pin_config_state const *pstate = &pinconf_states[i]; + int ret; + + if (!strcmp(pstate->name, state)) { + ret = pinconf_apply_state(pctldev, pstate); + if (ret) + dev_err(pctldev->dev, + "failed to apply state %s\n", + pstate->name); + } + } + + return 0; +} + +/** + * pinconf_apply_initexit() - check for states to be applied on init and exit + * @pctldev: pin controller to be checked + * @init: true for checking during registration of the pin controller, false + * for checking during removal of the pin controller + */ +void pinconf_apply_initexit(struct pinctrl_dev *pctldev, bool init) +{ + int i; + + for (i = 0; i < pinconf_states_num; i++) { + struct pin_config_state const *pstate = &pinconf_states[i]; + int ret; + + if ((pstate->apply_on_init && init) || + (pstate->apply_on_exit && !init)) { + ret = pinconf_apply_state(pctldev, pstate); + if (ret) + dev_err(pctldev->dev, + "failed to apply state %s on %s\n", + pstate->name, + init ? "init" : "exit"); + } + } +} + #ifdef CONFIG_DEBUG_FS static void pinconf_dump_pin(struct pinctrl_dev *pctldev, @@ -294,6 +469,30 @@ static int pinconf_groups_show(struct seq_file *s, void *what) return 0; } +static int pinconf_states_show(struct seq_file *s, void *what) +{ + int i; + + seq_puts(s, "Pinconfig state table:\n"); + + for (i = 0; i < pinconf_states_num; i++) { + struct pin_config_state const *pstate = &pinconf_states[i]; + + seq_printf(s, "%s:\n", pstate->name); + seq_printf(s, " controlling device %s\n", + pstate->ctrl_dev ? dev_name(pstate->ctrl_dev) : + pstate->ctrl_dev_name); + seq_printf(s, " %u pin configs, %u pin group configs\n", + pstate->nr_pin_configs, + pstate->nr_pin_group_configs); + seq_printf(s, " apply on init: %s\n", + pstate->apply_on_init ? "YES" : "NO"); + seq_printf(s, " apply on exit: %s\n", + pstate->apply_on_exit ? "YES" : "NO"); + } + return 0; +} + static int pinconf_pins_open(struct inode *inode, struct file *file) { return single_open(file, pinconf_pins_show, inode->i_private); @@ -304,6 +503,11 @@ static int pinconf_groups_open(struct inode *inode, struct file *file) return single_open(file, pinconf_groups_show, inode->i_private); } +static int pinconf_states_open(struct inode *inode, struct file *file) +{ + return single_open(file, pinconf_states_show, inode->i_private); +} + static const struct file_operations pinconf_pins_ops = { .open = pinconf_pins_open, .read = seq_read, @@ -318,6 +522,13 @@ static const struct file_operations pinconf_groups_ops = { .release = single_release, }; +static const struct file_operations pinconf_states_ops = { + .open = pinconf_states_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + void pinconf_init_device_debugfs(struct dentry *devroot, struct pinctrl_dev *pctldev) { @@ -327,4 +538,10 @@ void pinconf_init_device_debugfs(struct dentry *devroot, devroot, pctldev, &pinconf_groups_ops); } +void pinconf_init_debugfs(struct dentry *subsys_root) +{ + debugfs_create_file("pinconf-states", S_IFREG | S_IRUGO, + subsys_root, NULL, &pinconf_states_ops); +} + #endif diff --git a/drivers/pinctrl/pinconf.h b/drivers/pinctrl/pinconf.h index 81d71ae..f5872d4 100644 --- a/drivers/pinctrl/pinconf.h +++ b/drivers/pinctrl/pinconf.h @@ -14,8 +14,10 @@ #ifdef CONFIG_PINCONF int pinconf_check_ops(const struct pinconf_ops *ops); +void pinconf_apply_initexit(struct pinctrl_dev *pctldev, bool init); void pinconf_init_device_debugfs(struct dentry *devroot, struct pinctrl_dev *pctldev); +void pinconf_init_debugfs(struct dentry *subsys_root); int pin_config_get_for_pin(struct pinctrl_dev *pctldev, unsigned pin, unsigned long *config); int pin_config_set_for_pin(struct pinctrl_dev *pctldev, unsigned pin, @@ -28,11 +30,20 @@ static inline int pinconf_check_ops(const struct pinconf_ops *ops) return 0; } +static inline void pinconf_apply_initexit(struct pinctrl_dev *pctldev, + bool init) +{ +} + static inline void pinconf_init_device_debugfs(struct dentry *devroot, struct pinctrl_dev *pctldev) { } +static inline void pinconf_init_debugfs(struct dentry *subsys_root) +{ +} + #endif /* diff --git a/include/linux/pinctrl/machine.h b/include/linux/pinctrl/machine.h index d0aecb7..a4f78bd 100644 --- a/include/linux/pinctrl/machine.h +++ b/include/linux/pinctrl/machine.h @@ -82,6 +82,67 @@ struct pinmux_map { { .name = a, .ctrl_dev_name = b, .function = c, .group = d, \ .hog_on_boot = true } +/** + * struct pin_config - configuration tuple for a single pin + * @pin_name: name of the pin affected by this configuration + * @config: configuration of the named pin (custom format) + */ +struct pin_config { + const char *pin_name; + u32 config; +}; + +/* + * Convenience macro to set up a simple config for a named pin + */ +#define PIN_CONFIG(a, b) \ + { .pin_name = a, .config = b } + +/** + * struct pin_group_config - configuration tuple for a group + * @group_name: name of the group affected by this configuration + * @config: configuration of the named group (custom format) + */ +struct pin_group_config { + const char *group_name; + u32 config; +}; + +/* + * Convenience macro to set up a simple config for a named pin group + */ +#define PIN_GROUP_CONFIG(a, b) \ + { .group_name = a, .config = b } + +/** + * struct pin_config_state - configuration for an array of pins + * @name: name of this configuration state + * @ctrl_dev: the pin control device to be used for this state, may be NULL + * if you provide .ctrl_dev_name instead (this is more common) + * @ctrl_dev_name: the name of the device controlling this specific state, + * the name must be the same as in your struct device*, may be NULL if + * you provide .ctrl_dev instead + * @pin_configs: array of pin configuration tuples + * @nr_pin_configs: number of pin_configuration tuples in the array + * @pin_group_configs: array of pin group configuration tuples + * @nr_pin_group_configs: number of pin_group_configuration tuples in the array + * @apply_on_init: whether this configuration shall be auto-applied when + * the pin controller is registered + * @apply_on_exit: whether this configuration shall be auto-applied when + * the pin controller is unregistered + */ +struct pin_config_state { + const char *name; + struct device *ctrl_dev; + const char *ctrl_dev_name; + const struct pin_config *pin_configs; + unsigned nr_pin_configs; + const struct pin_group_config *pin_group_configs; + unsigned nr_pin_group_configs; + bool apply_on_init; + bool apply_on_exit; +}; + #ifdef CONFIG_PINMUX extern int pinmux_register_mappings(struct pinmux_map const *map, @@ -96,4 +157,22 @@ static inline int pinmux_register_mappings(struct pinmux_map const *map, } #endif /* !CONFIG_PINMUX */ + +#ifdef CONFIG_PINCONF + +extern int pinconf_register_pin_states(struct pin_config_state const + *states, + unsigned num_states); + +#else + +static inline int pinconf_register_pin_states(struct pin_config_state const + *states, + unsigned num_states) +{ + return 0; +} + +#endif /* !CONFIG_PINCONF */ + #endif