From patchwork Thu Jun 19 13:04:50 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ulf Hansson X-Patchwork-Id: 32215 Return-Path: X-Original-To: linaro@patches.linaro.org Delivered-To: linaro@patches.linaro.org Received: from mail-pd0-f198.google.com (mail-pd0-f198.google.com [209.85.192.198]) by ip-10-151-82-157.ec2.internal (Postfix) with ESMTPS id 77420206A0 for ; Thu, 19 Jun 2014 13:05:25 +0000 (UTC) Received: by mail-pd0-f198.google.com with SMTP id y10sf7090071pdj.5 for ; Thu, 19 Jun 2014 06:05:24 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:delivered-to:from:to:cc:subject :date:message-id:in-reply-to:references:x-original-sender :x-original-authentication-results:precedence:mailing-list:list-id :list-post:list-help:list-archive:list-unsubscribe; bh=GZriTrETmk892gsxyKVz8Gs+YbrbYSPePjOoP+a6yrE=; b=JRRUUyyIt6igI9P0HHNohh9QYJHZa3ELqqFMeh/tvf4I8oOAeWNXGARTgxOeF4xGw9 3hQcPrNkzH+bFYjdouA55e2LcHpwxLmEMpvJH+phhM+qTaVHnyG8PHuLTxvQzD7cSgLD B7MkMw8UY4DFCcwg9wCxKzpQ2cKiF8FPpOpF6uifHqDR6rIC7/9CFjn7LWDvcbbPyMjZ KIpso9qpYu+wGPFUjVaHj2boi/C0qv0fGKWEk1JqUzGNeN3J5JZYIgv+RnXLV81OZkFu 27A1Ut5I3E4yW4EluZDD7yhwz9VCsNz8Ka4kttoc4beIxeOKQmR38JkLaFFQ0QpgCFAM ZPtw== X-Gm-Message-State: ALoCoQmp5hy9s5LCN+IqD1zZp36Dux4WYsDFl1knlpDcND7R/MWxgcS58TN/KW3Y4YdQbsa+2kri X-Received: by 10.66.138.17 with SMTP id qm17mr2253946pab.34.1403183124784; Thu, 19 Jun 2014 06:05:24 -0700 (PDT) MIME-Version: 1.0 X-BeenThere: patchwork-forward@linaro.org Received: by 10.140.85.138 with SMTP id n10ls548323qgd.4.gmail; Thu, 19 Jun 2014 06:05:24 -0700 (PDT) X-Received: by 10.58.208.228 with SMTP id mh4mr3884047vec.22.1403183124626; Thu, 19 Jun 2014 06:05:24 -0700 (PDT) Received: from mail-ve0-f173.google.com (mail-ve0-f173.google.com [209.85.128.173]) by mx.google.com with ESMTPS id xs1si2335910vec.64.2014.06.19.06.05.24 for (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Thu, 19 Jun 2014 06:05:24 -0700 (PDT) Received-SPF: pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.128.173 as permitted sender) client-ip=209.85.128.173; Received: by mail-ve0-f173.google.com with SMTP id db11so2240956veb.4 for ; Thu, 19 Jun 2014 06:05:24 -0700 (PDT) X-Received: by 10.58.220.230 with SMTP id pz6mr3805085vec.9.1403183124521; Thu, 19 Jun 2014 06:05:24 -0700 (PDT) X-Forwarded-To: patchwork-forward@linaro.org X-Forwarded-For: patch@linaro.org patchwork-forward@linaro.org Delivered-To: patches@linaro.org Received: by 10.221.54.6 with SMTP id vs6csp360281vcb; Thu, 19 Jun 2014 06:05:23 -0700 (PDT) X-Received: by 10.152.25.229 with SMTP id f5mr1301125lag.87.1403183123407; Thu, 19 Jun 2014 06:05:23 -0700 (PDT) Received: from mail-la0-f51.google.com (mail-la0-f51.google.com [209.85.215.51]) by mx.google.com with ESMTPS id w8si4291403laz.45.2014.06.19.06.05.22 for (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Thu, 19 Jun 2014 06:05:23 -0700 (PDT) Received-SPF: pass (google.com: domain of ulf.hansson@linaro.org designates 209.85.215.51 as permitted sender) client-ip=209.85.215.51; Received: by mail-la0-f51.google.com with SMTP id mc6so1411571lab.24 for ; Thu, 19 Jun 2014 06:05:22 -0700 (PDT) X-Received: by 10.112.149.71 with SMTP id ty7mr3178048lbb.34.1403183122894; Thu, 19 Jun 2014 06:05:22 -0700 (PDT) Received: from linaro-ulf.lan (90-231-160-185-no158.tbcn.telia.com. [90.231.160.185]) by mx.google.com with ESMTPSA id ar1sm4133975lbc.3.2014.06.19.06.05.19 for (version=TLSv1.1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Thu, 19 Jun 2014 06:05:22 -0700 (PDT) From: Ulf Hansson To: linux-kernel@vger.kernel.org, linux-mmc@vger.kernel.org, linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org Cc: Greg Kroah-Hartman , Linus Walleij , Mark Brown , Arnd Bergmann , Alexandre Courbot , Arend van Spriel , Sascha Hauer , Chris Ball , Chen-Yu Tsai , Olof Johansson , Russell King , Tomasz Figa , Hans de Goede , Rob Herring , Pawel Moll , Mark Rutland , Ulf Hansson Subject: [RFC 1/2] pwrseq: Add subsystem to handle complex power sequences Date: Thu, 19 Jun 2014 15:04:50 +0200 Message-Id: <1403183091-27876-2-git-send-email-ulf.hansson@linaro.org> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1403183091-27876-1-git-send-email-ulf.hansson@linaro.org> References: <1403183091-27876-1-git-send-email-ulf.hansson@linaro.org> X-Removed-Original-Auth: Dkim didn't pass. X-Original-Sender: ulf.hansson@linaro.org X-Original-Authentication-Results: mx.google.com; spf=pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.128.173 as permitted sender) smtp.mail=patch+caf_=patchwork-forward=linaro.org@linaro.org Precedence: list Mailing-list: list patchwork-forward@linaro.org; contact patchwork-forward+owners@linaro.org List-ID: X-Google-Group-Id: 836684582541 List-Post: , List-Help: , List-Archive: List-Unsubscribe: , The pwrseq subsystem handles complex power sequences, typically useful for subsystems that makes use of discoverable buses, like for example MMC and I2C. The pwrseq subsystem is dependant on CONFIG_OF to be able to parse a DT childnode to find out what power sequence method to bind for a device. >From the DT childnode, the pwrseq DT parser tries to locate a "power-method" property, which string is matched towards the list of supported pwrseq methods. Each pwrseq method implements it's own power sequence and interfaces the pwrseq core through a few callback functions. To instantiate a pwrseq method, clients shall use the devm_pwrseq_get() API. If needed, clients can explicity drop the references to a pwrseq method using devm_pwrseq_put() API. Besides instantiation, the pwrseq API provides clients opportunity to select a certain power state. In this intial version, PWRSEQ_POWER_ON and PWRSEQ_POWER_OFF are supported. Those are also mandatory for each pwrseq method to support. Signed-off-by: Ulf Hansson --- .../devicetree/bindings/pwrseq/pwrseq.txt | 48 ++++++ drivers/Makefile | 2 +- drivers/pwrseq/Makefile | 2 + drivers/pwrseq/core.c | 175 ++++++++++++++++++++ drivers/pwrseq/core.h | 37 +++++ drivers/pwrseq/method.c | 38 +++++ include/linux/pwrseq.h | 50 ++++++ 7 files changed, 351 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/pwrseq/pwrseq.txt create mode 100644 drivers/pwrseq/Makefile create mode 100644 drivers/pwrseq/core.c create mode 100644 drivers/pwrseq/core.h create mode 100644 drivers/pwrseq/method.c create mode 100644 include/linux/pwrseq.h diff --git a/Documentation/devicetree/bindings/pwrseq/pwrseq.txt b/Documentation/devicetree/bindings/pwrseq/pwrseq.txt new file mode 100644 index 0000000..80848ae --- /dev/null +++ b/Documentation/devicetree/bindings/pwrseq/pwrseq.txt @@ -0,0 +1,48 @@ +Power sequence DT bindings + +Each power sequence method has a corresponding "power-method" property string. +This property shall be set in a subnode for a device. That subnode should also +describe resourses which are specific to that power method. + +Do note, power sequences as such isn't encoded through DT. Instead those are +implemented by each power method. + +Required subnode properties: +- power-method: should contain the string for the power method to bind. + + Supported power methods: None. + +Example: + +Note, the "clock" power method in this example isn't actually supported, but +used to visualize how a childnode could be described. + +// WLAN SDIO channel +sdi1_per2@80118000 { + compatible = "arm,pl18x", "arm,primecell"; + reg = <0x80118000 0x1000>; + interrupts = <0 50 IRQ_TYPE_LEVEL_HIGH>; + + dmas = <&dma 32 0 0x2>, /* Logical - DevToMem */ + <&dma 32 0 0x0>; /* Logical - MemToDev */ + dma-names = "rx", "tx"; + + clocks = <&prcc_kclk 2 4>, <&prcc_pclk 2 6>; + clock-names = "sdi", "apb_pclk"; + + arm,primecell-periphid = <0x10480180>; + max-frequency = <100000000>; + bus-width = <4>; + non-removable; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&sdi1_default_mode>; + pinctrl-1 = <&sdi1_sleep_mode>; + + status = "okay"; + + pwrseq: pwrseq1 { + power-method = "clock"; + clocks = <&someclk 1 2>, <&someclk 3 4>; + clock-names = "pwrseq1", "pwrseq2"; + }; +}; diff --git a/drivers/Makefile b/drivers/Makefile index f98b50d..ac1bf5b 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -128,7 +128,7 @@ endif obj-$(CONFIG_DCA) += dca/ obj-$(CONFIG_HID) += hid/ obj-$(CONFIG_PPC_PS3) += ps3/ -obj-$(CONFIG_OF) += of/ +obj-$(CONFIG_OF) += of/ pwrseq/ obj-$(CONFIG_SSB) += ssb/ obj-$(CONFIG_BCMA) += bcma/ obj-$(CONFIG_VHOST_RING) += vhost/ diff --git a/drivers/pwrseq/Makefile b/drivers/pwrseq/Makefile new file mode 100644 index 0000000..afb914f --- /dev/null +++ b/drivers/pwrseq/Makefile @@ -0,0 +1,2 @@ +#Support for the power sequence subsystem +obj-y = core.o method.o diff --git a/drivers/pwrseq/core.c b/drivers/pwrseq/core.c new file mode 100644 index 0000000..baf7bb6 --- /dev/null +++ b/drivers/pwrseq/core.c @@ -0,0 +1,175 @@ +/* + * Core of the power sequence subsystem + * + * Copyright (C) 2014 Linaro Ltd + * + * Author: Ulf Hansson + * + * License terms: GNU General Public License (GPL) version 2 + */ + +#include +#include +#include +#include +#include +#include + +#include +#include "core.h" + +static int pwrseq_find(const char *power_method) +{ + int i; + + for (i = 0; pwrseq_methods[i].bind_method != NULL; i++) + if (!strcasecmp(power_method, pwrseq_methods[i].method)) + return i; + return -ENODEV; +} + +static void devm_pwrseq_release(struct device *dev, void *res) +{ + struct pwrseq *ps = res; + + dev_dbg(dev, "%s: Release method=%s\n", __func__, ps->method); + + ps->ps_ops->release(ps); + of_node_put(ps->np_child); +} + +static struct pwrseq *pwrseq_get(struct device *dev, + struct device_node *np, + const char *power_method) +{ + struct pwrseq *ps; + int ret; + + ret = pwrseq_find(power_method); + if (ret < 0) + return ERR_PTR(ret); + + ps = devres_alloc(devm_pwrseq_release, sizeof(ps), GFP_KERNEL); + if (!ps) + return ERR_PTR(-ENOMEM); + + ps->dev = dev; + ps->np_child = np; + ps->method = pwrseq_methods[ret].method; + + /* + * The on/off states is mandatory to be supported. Additional states + * shall be added by each method during binding. + */ + ps->supported_states = PWRSEQ_POWER_OFF | PWRSEQ_POWER_ON; + /* + * The default current state is PWRSEQ_POWER_OFF, it may be updated + * during binding. + */ + ps->current_state = PWRSEQ_POWER_OFF; + + ret = pwrseq_methods[ret].bind_method(ps); + if (ret) { + devres_free(ps); + return ERR_PTR(ret); + } + + devres_add(dev, ps); + return ps; +} + +static int pwrseq_of_find(struct device *dev, + struct device_node **np_child, + const char **power_method) +{ + struct device_node *np; + const char *pm; + + if (!dev->of_node) + return -ENODEV; + + /* Parse childnodes to find a power-method property. */ + for_each_child_of_node(dev->of_node, np) { + if (!of_property_read_string(np, "power-method", &pm)) { + *np_child = np; + *power_method = pm; + return 1; + } + } + + return 0; +} + +static struct pwrseq *_devm_pwrseq_get(struct device *dev, bool optional) +{ + struct pwrseq *ps; + struct device_node *np; + const char *pm; + int ret; + + if (!dev) + return ERR_PTR(-ENODEV); + + ret = pwrseq_of_find(dev, &np, &pm); + if (ret < 0) { + return ERR_PTR(ret); + } else if (ret) { + ps = pwrseq_get(dev, np, pm); + if (IS_ERR(ps)) + of_node_put(np); + } else if (optional) { + ps = pwrseq_get(dev, NULL, "dummy"); + } else { + return ERR_PTR(-ENODEV); + } + + if (!IS_ERR(ps)) + dev_dbg(dev, "%s: Bound method=%s\n", __func__, ps->method); + + return ps; +} + +struct pwrseq *devm_pwrseq_get_optional(struct device *dev) +{ + return _devm_pwrseq_get(dev, true); +} +EXPORT_SYMBOL_GPL(devm_pwrseq_get_optional); + +struct pwrseq *devm_pwrseq_get(struct device *dev) +{ + return _devm_pwrseq_get(dev, false); +} +EXPORT_SYMBOL_GPL(devm_pwrseq_get); + +static int devm_pwrseq_match(struct device *dev, void *res, void *data) +{ + struct pwrseq **ps = res; + + return *ps == data; +} + +void devm_pwrseq_put(struct pwrseq *ps) +{ + devres_release(ps->dev, devm_pwrseq_release, devm_pwrseq_match, ps); +} +EXPORT_SYMBOL_GPL(devm_pwrseq_put); + +int pwrseq_select_state(struct pwrseq *ps, enum pwrseq_state ps_state) +{ + int ret = 0; + + if (ps->current_state != ps_state) + ret = ps->ps_ops->select_state(ps, ps_state); + + if (!ret) + ps->current_state = ps_state; + + return ret; +} +EXPORT_SYMBOL_GPL(pwrseq_select_state); + +unsigned int pwrseq_supported_states(struct pwrseq *ps) +{ + return ps->supported_states; +} +EXPORT_SYMBOL_GPL(pwrseq_supported_states); diff --git a/drivers/pwrseq/core.h b/drivers/pwrseq/core.h new file mode 100644 index 0000000..84a6449 --- /dev/null +++ b/drivers/pwrseq/core.h @@ -0,0 +1,37 @@ +/* + * Core private header for the power sequence subsystem + * + * Copyright (C) 2014 Linaro Ltd + * + * Author: Ulf Hansson + * + * License terms: GNU General Public License (GPL) version 2 + */ + +#ifndef __PWRSEQ_PWRSEQ_H +#define __PWRSEQ_PWRSEQ_H + +#include + +struct pwrseq_ops { + int (*select_state)(struct pwrseq *, enum pwrseq_state); + void (*release)(struct pwrseq *); +}; + +struct pwrseq { + struct device *dev; + struct device_node *np_child; + const char *method; + enum pwrseq_state supported_states; + enum pwrseq_state current_state; + struct pwrseq_ops *ps_ops; + void *data; +}; + +struct pwrseq_method { + int (*bind_method)(struct pwrseq *); + const char *method; +}; + +extern struct pwrseq_method pwrseq_methods[]; +#endif diff --git a/drivers/pwrseq/method.c b/drivers/pwrseq/method.c new file mode 100644 index 0000000..fe16579 --- /dev/null +++ b/drivers/pwrseq/method.c @@ -0,0 +1,38 @@ +/* + * Implements a dummy power method for power sequence subsystem + * + * Copyright (C) 2014 Linaro Ltd + * + * Author: Ulf Hansson + * + * License terms: GNU General Public License (GPL) version 2 + */ + +#include + +#include +#include "core.h" + +static int pwrseq_dummy_select_state(struct pwrseq *ps, enum pwrseq_state s) +{ + return 0; +} + +static void pwrseq_dummy_release(struct pwrseq *ps) { } + +static struct pwrseq_ops pwrseq_dummy_ops = { + .select_state = pwrseq_dummy_select_state, + .release = pwrseq_dummy_release, +}; + +static int pwrseq_dummy_bind(struct pwrseq *ps) +{ + ps->ps_ops = &pwrseq_dummy_ops; + return 0; +} + +/* The extern list of supported power sequence_methods */ +struct pwrseq_method pwrseq_methods[] = { + { pwrseq_dummy_bind, "dummy" }, + { NULL, NULL }, +}; diff --git a/include/linux/pwrseq.h b/include/linux/pwrseq.h new file mode 100644 index 0000000..528b544 --- /dev/null +++ b/include/linux/pwrseq.h @@ -0,0 +1,50 @@ +/* + * Interface for the power sequence subsystem + * + * Copyright (C) 2014 Linaro Ltd + * + * Author: Ulf Hansson + * + * License terms: GNU General Public License (GPL) version 2 + */ +#ifndef __LINUX_PWRSEQ_PWRSEQ_H +#define __LINUX_PWRSEQ_PWRSEQ_H + +#include + +struct device; +struct pwrseq; + +enum pwrseq_state { + PWRSEQ_POWER_OFF = BIT(0), + PWRSEQ_POWER_ON = BIT(1), +}; + +#ifdef CONFIG_OF +extern struct pwrseq *devm_pwrseq_get(struct device *dev); +extern struct pwrseq *devm_pwrseq_get_optional(struct device *dev); +extern void devm_pwrseq_put(struct pwrseq *ps); +extern int pwrseq_select_state(struct pwrseq *ps, unsigned int ps_state); +extern unsigned int pwrseq_supported_states(struct pwrseq *ps); +#else +static inline struct pwrseq *devm_pwrseq_get(struct device *dev) +{ + return ERR_PTR(-ENODEV); +} +static inline struct pwrseq *devm_pwrseq_get_optional(struct device *dev) +{ + return ERR_PTR(-ENODEV); +} +static inline void devm_pwrseq_put(struct pwrseq *ps) {} +static inline int pwrseq_select_state(struct pwrseq *ps, + enum pwrseq_state ps_state) +{ + return 0; +} +static inline unsigned int pwrseq_supported_states(struct pwrseq *ps) +{ + return 0; +} +#endif + +#endif /* __LINUX_PWRSEQ_PWRSEQ_H */