From patchwork Sun Dec 18 23:06:33 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Linus Walleij X-Patchwork-Id: 5848 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 2540323E18 for ; Sun, 18 Dec 2011 23:06:59 +0000 (UTC) Received: from mail-ey0-f180.google.com (mail-ey0-f180.google.com [209.85.215.180]) by fiordland.canonical.com (Postfix) with ESMTP id 09198A182D0 for ; Sun, 18 Dec 2011 23:06:59 +0000 (UTC) Received: by eaac11 with SMTP id c11so1342137eaa.11 for ; Sun, 18 Dec 2011 15:06:58 -0800 (PST) Received: by 10.204.136.194 with SMTP id s2mr809534bkt.96.1324249618697; Sun, 18 Dec 2011 15:06:58 -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.204.40.4 with SMTP id i4cs110639bke; Sun, 18 Dec 2011 15:06:58 -0800 (PST) Received: by 10.213.10.65 with SMTP id o1mr1329005ebo.86.1324249616964; Sun, 18 Dec 2011 15:06:56 -0800 (PST) Received: from eu1sys200aog115.obsmtp.com (eu1sys200aog115.obsmtp.com. [207.126.144.139]) by mx.google.com with SMTP id m45si4195392eef.56.2011.12.18.15.06.45 (version=TLSv1/SSLv3 cipher=OTHER); Sun, 18 Dec 2011 15:06:56 -0800 (PST) Received-SPF: neutral (google.com: 207.126.144.139 is neither permitted nor denied by best guess record for domain of linus.walleij@stericsson.com) client-ip=207.126.144.139; Authentication-Results: mx.google.com; spf=neutral (google.com: 207.126.144.139 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-ap.st.com ([138.198.100.35]) (using TLSv1) by eu1sys200aob115.postini.com ([207.126.147.11]) with SMTP ID DSNKTu5yA6G1IPoNErqKBF+0K5RsHWIcdG6a@postini.com; Sun, 18 Dec 2011 23:06:56 UTC Received: from zeta.dmz-ap.st.com (ns6.st.com [138.198.234.13]) by beta.dmz-ap.st.com (STMicroelectronics) with ESMTP id 97F87A3; Sun, 18 Dec 2011 22:58:13 +0000 (GMT) Received: from relay2.stm.gmessaging.net (unknown [10.230.100.18]) by zeta.dmz-ap.st.com (STMicroelectronics) with ESMTP id 645A58A8; Sun, 18 Dec 2011 23:06:38 +0000 (GMT) Received: from exdcvycastm003.EQ1STM.local (alteon-source-exch [10.230.100.61]) (using TLSv1 with cipher RC4-MD5 (128/128 bits)) (Client CN "exdcvycastm003", Issuer "exdcvycastm003" (not verified)) by relay2.stm.gmessaging.net (Postfix) with ESMTPS id 75EF7A8074; Mon, 19 Dec 2011 00:06:37 +0100 (CET) Received: from steludxu4075.lud.stericsson.com (10.230.100.153) by smtp.stericsson.com (10.230.100.1) with Microsoft SMTP Server (TLS) id 8.3.83.0; Mon, 19 Dec 2011 00:06:37 +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 v2] pinctrl: add support for group and pinmux states Date: Mon, 19 Dec 2011 00:06:33 +0100 Message-ID: <1324249593-3051-1-git-send-email-linus.walleij@stericsson.com> X-Mailer: git-send-email 1.7.8 MIME-Version: 1.0 From: Linus Walleij This makes it possible for pin groups and pin muxes to go into different states, which is especially useful for power management, both runtime and across suspend/resume. ChangeLog v1->v2: - Use a pin controller name instead of the device itself as parameter to the state set function, as indicated by Stephens similar patch to config this is more helpful. Signed-off-by: Linus Walleij --- Documentation/pinctrl.txt | 71 +++++++++++++++++++++++++++++++++++++++ drivers/pinctrl/core.c | 28 +++++++++++++++ drivers/pinctrl/pinmux.c | 34 ++++++++++++++++++ include/linux/pinctrl/pinctrl.h | 32 +++++++++++++++++ include/linux/pinctrl/pinmux.h | 7 ++++ 5 files changed, 172 insertions(+), 0 deletions(-) diff --git a/Documentation/pinctrl.txt b/Documentation/pinctrl.txt index 98aeda6..e76306d 100644 --- a/Documentation/pinctrl.txt +++ b/Documentation/pinctrl.txt @@ -194,6 +194,53 @@ just a simple example - in practice you may need more entries in your group structure, for example specific register ranges associated with each group and so on. +Pin group states +================ + +To simplify handling of specific group states, such as when a group of +pins need to be deactivated or put in a specific sleep state, the pin controller +may implement a .pin_group_set_state() callback. This will for example be +called automatically to set a certain pin group in active state when the groups +are selected for use in a certain pin multiplexing configuration (see below). +This is a good opportunity to set up any default pin configuration (see below) +for the pins in a group. + +While the driver will have to maintain states for it's pin groups internally +or in the hardware registers, as well as for setting up the pins on boot, +it will know through these calls if some special action is needed on behalf +of the users. + +For example, a board file may issue: + +pinctrl_set_group_state("foo-dev", "i2c7-group", PIN_GROUP_STATE_DISABLED); + +In this case it could be that I2C7 is not used at all in this board, and +whereas the driver would normally set the I2C pins to pull-up (this happens to +be required for I2C), in this case since they are not even used on this board, +it may just as well ground the pins instead and save some power. + +The driver will know what to do through this specific callback: + +static int foo_set_group_state(struct pinctrl_dev *pctldev, + unsigned selector, + enum pin_group_state state) +{ + switch(state) { + ... + } +} + +static struct pinctrl_ops foo_pctrl_ops = { + ... + .set_group_state = foo_set_group_state, +}; + + +static struct pinctrl_desc foo_desc = { + ... + .pctlops = &foo_pctrl_ops, +}; + Pin configuration ================= @@ -1066,3 +1113,27 @@ foo_switch() } The above has to be done from process context. + + +Pinmux states +============= + +Pinmuxes can have states just like groups, and in fact setting the state of +a certain pinmux to a certain enumerator will in practice loop over the pin +groups used by the pinmux and set the state for each of these groups. Any calls +to set the pinmux state will be silently ignored if the pin controller does not +implement handling of group states. + +For example a driver, or centralized power management code for the platform +(wherever the struct pinmux *pmx pointer is handled) can do power saving calls +like this: + +pinmux_set_state(pmx, PIN_GROUP_STATE_ACTIVE); +pinmux_set_state(pmx, PIN_GROUP_STATE_IDLE); +pinmux_set_state(pmx, PIN_GROUP_STATE_SLEEP); + +Each call may or may not result in the corresponding pin control driver setting +pins in special low-power modes, disabling pull-ups or anything else (likely +pin configuration related). It may also just ignore the call, which is useful +when the same driver is used with different pin controllers, some of which can +do something meaningful with this information, some which can't. diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c index 9e32ea3..4a65d83 100644 --- a/drivers/pinctrl/core.c +++ b/drivers/pinctrl/core.c @@ -344,6 +344,34 @@ int pinctrl_get_group_selector(struct pinctrl_dev *pctldev, return -EINVAL; } +/** + * pinctrl_set_group_state() - set pin group state + * @dev_name: name of the pin controller holding the group to change state for + * @name: the name of the pin group to change the state of + * @state: the new state for the pin group + */ +int pinctrl_set_group_state(const char *dev_name, + const char *name, + enum pin_group_state state) +{ + struct pinctrl_dev *pctldev; + const struct pinctrl_ops *ops; + int selector; + + pctldev = get_pinctrl_dev_from_dev(NULL, dev_name); + if (!pctldev) + return -EINVAL; + ops = pctldev->desc->pctlops; + + selector = pinctrl_get_group_selector(pctldev, name); + if (selector < 0) + return selector; + if (!ops->set_group_state) + return 0; + return ops->set_group_state(pctldev, selector, state); +} +EXPORT_SYMBOL_GPL(pinctrl_set_group_state); + #ifdef CONFIG_DEBUG_FS static int pinctrl_pins_show(struct seq_file *s, void *what) diff --git a/drivers/pinctrl/pinmux.c b/drivers/pinctrl/pinmux.c index 3bcc641..0a0b05f 100644 --- a/drivers/pinctrl/pinmux.c +++ b/drivers/pinctrl/pinmux.c @@ -817,6 +817,9 @@ struct pinmux *pinmux_get(struct device *dev, const char *name) list_add(&pmx->node, &pinmux_list); mutex_unlock(&pinmux_list_mutex); + /* Set to default state, if any */ + pinmux_set_state(pmx, PIN_GROUP_STATE_ACTIVE); + return pmx; } EXPORT_SYMBOL_GPL(pinmux_get); @@ -904,6 +907,37 @@ void pinmux_disable(struct pinmux *pmx) } EXPORT_SYMBOL_GPL(pinmux_disable); +int pinmux_set_state(struct pinmux *pmx, enum pin_group_state state) +{ + struct pinctrl_dev *pctldev = pmx->pctldev; + const struct pinctrl_ops *pctlops = pctldev->desc->pctlops; + struct pinmux_group *grp; + int ret; + + /* Does not implement group states, bail out */ + if (!pctlops->set_group_state) + return 0; + + /* + * Loop over the currently used groups for this pinmux and set the + * state for each and every one of them. + */ + list_for_each_entry(grp, &pmx->groups, node) { + ret = pctlops->set_group_state(pctldev, grp->group_selector, + state); + if (ret) { + dev_err(pctldev->dev, "unable to set state %d for " + "group %u on %s\n", + state, grp->group_selector, + pinctrl_dev_get_name(pctldev)); + return ret; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(pinmux_set_state); + int pinmux_check_ops(const struct pinmux_ops *ops) { /* Check that we implement required operations */ diff --git a/include/linux/pinctrl/pinctrl.h b/include/linux/pinctrl/pinctrl.h index 9809a94..ac22827 100644 --- a/include/linux/pinctrl/pinctrl.h +++ b/include/linux/pinctrl/pinctrl.h @@ -12,6 +12,23 @@ #ifndef __LINUX_PINCTRL_PINCTRL_H #define __LINUX_PINCTRL_PINCTRL_H +/** + * enum pin_group_state - different pin group states + * @PIN_GROUP_STATE_DISABLED: disabled state + * @PIN_GROUP_STATE_ACTIVE: active state, pins are in active use for their + * purpose, this should be the default state when pins are muxed in for + * example + * @PIN_GROUP_STATE_IDLE: the pin group is not actively used now + * @PIN_GROUP_STATE_SLEEP: the pin group is in low-power mode for a longer + * period of time + */ +enum pin_group_state { + PIN_GROUP_STATE_DISABLED, + PIN_GROUP_STATE_ACTIVE, + PIN_GROUP_STATE_IDLE, + PIN_GROUP_STATE_SLEEP, +}; + #ifdef CONFIG_PINCTRL #include @@ -69,6 +86,8 @@ struct pinctrl_gpio_range { * @get_group_name: return the group name of the pin group * @get_group_pins: return an array of pins corresponding to a certain * group selector @pins, and the size of the array in @num_pins + * @set_group_state: optional callback to set the state of a certain pin + * group. Not defining this will make all calls to set state succeed. * @pin_dbg_show: optional debugfs display hook that will provide per-device * info for a certain pin in debugfs */ @@ -80,6 +99,9 @@ struct pinctrl_ops { unsigned selector, const unsigned **pins, unsigned *num_pins); + int (*set_group_state) (struct pinctrl_dev *pctldev, + unsigned selector, + enum pin_group_state state); void (*pin_dbg_show) (struct pinctrl_dev *pctldev, struct seq_file *s, unsigned offset); }; @@ -125,6 +147,9 @@ extern void pinctrl_remove_gpio_range(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range); extern const char *pinctrl_dev_get_name(struct pinctrl_dev *pctldev); extern void *pinctrl_dev_get_drvdata(struct pinctrl_dev *pctldev); +extern int pinctrl_set_group_state(const char *dev_name, + const char *name, + enum pin_group_state state); #else struct pinctrl_dev; @@ -135,6 +160,13 @@ static inline bool pin_is_valid(struct pinctrl_dev *pctldev, int pin) return pin >= 0; } +static inline int pinctrl_set_group_state(const char *dev_name, + const char *name, + enum pin_group_state state) +{ + return 0; +} + #endif /* !CONFIG_PINCTRL */ #endif /* __LINUX_PINCTRL_PINCTRL_H */ diff --git a/include/linux/pinctrl/pinmux.h b/include/linux/pinctrl/pinmux.h index 937b3e2..8d084ad 100644 --- a/include/linux/pinctrl/pinmux.h +++ b/include/linux/pinctrl/pinmux.h @@ -97,6 +97,7 @@ extern struct pinmux * __must_check pinmux_get(struct device *dev, const char *n extern void pinmux_put(struct pinmux *pmx); extern int pinmux_enable(struct pinmux *pmx); extern void pinmux_disable(struct pinmux *pmx); +extern int pinmux_set_state(struct pinmux *pmx, enum pin_group_state state); #else /* !CONFIG_PINMUX */ @@ -137,6 +138,12 @@ static inline void pinmux_disable(struct pinmux *pmx) { } +static inline int pinmux_set_state(struct pinmux *pmx, + enum pin_group_state state) +{ + return 0; +} + #endif /* CONFIG_PINMUX */ #endif /* __LINUX_PINCTRL_PINMUX_H */