From patchwork Tue Aug 26 12:07:10 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ulf Hansson X-Patchwork-Id: 36000 Return-Path: X-Original-To: linaro@patches.linaro.org Delivered-To: linaro@patches.linaro.org Received: from mail-ig0-f197.google.com (mail-ig0-f197.google.com [209.85.213.197]) by ip-10-151-82-157.ec2.internal (Postfix) with ESMTPS id 4917F2054F for ; Tue, 26 Aug 2014 12:07:52 +0000 (UTC) Received: by mail-ig0-f197.google.com with SMTP id r2sf20269974igi.8 for ; Tue, 26 Aug 2014 05:07:51 -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=ciI9W/APO9hz22RsztmDhJZcjfWvn+Q/7kY+y2HrKLw=; b=VzdjH2hZxTZBze2ynnciF/Dv0gY6PJT2aPotWahtq0SLfrl9D8MUa3j683jBvMdHh8 3OCxHFFRqm03pFgt1X8yV8VwaYjNnL8LK0ibBNZDKks4U3UFSSJVDVzW9dR6rdLsrM7G mbVcaU63Qd/3usbV2dMdx6B7TiCiev+U2XTGR9lD8CS6spN2FzSBL24IWQlU2na2Xm8W 7Ds7m4b7kobAhvSYQ39N6onvXjcUnKnRb5ylnXKe7u8tRQMiKk3j4MGI+RjSBcMI/kxX TyQBV6TjEAWRZ0C2t7QB1i3hRVLtd1wqrnMsxXpGxavox1cTOMIs8v/wS/FcFln9G4z4 8EEA== X-Gm-Message-State: ALoCoQkklfS41yFmM8TmouiG4wCyL7xGdPDCuip3I5/3xS+69RCX/4TACgxEg3d6BcYOMZ13J3U1 X-Received: by 10.50.136.194 with SMTP id qc2mr13912742igb.7.1409054871679; Tue, 26 Aug 2014 05:07:51 -0700 (PDT) MIME-Version: 1.0 X-BeenThere: patchwork-forward@linaro.org Received: by 10.140.47.242 with SMTP id m105ls2548049qga.84.gmail; Tue, 26 Aug 2014 05:07:51 -0700 (PDT) X-Received: by 10.52.170.69 with SMTP id ak5mr346182vdc.63.1409054871528; Tue, 26 Aug 2014 05:07:51 -0700 (PDT) Received: from mail-vc0-f169.google.com (mail-vc0-f169.google.com [209.85.220.169]) by mx.google.com with ESMTPS id ir4si1294682vcb.19.2014.08.26.05.07.51 for (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Tue, 26 Aug 2014 05:07:51 -0700 (PDT) Received-SPF: pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.220.169 as permitted sender) client-ip=209.85.220.169; Received: by mail-vc0-f169.google.com with SMTP id le20so17029229vcb.0 for ; Tue, 26 Aug 2014 05:07:51 -0700 (PDT) X-Received: by 10.220.44.136 with SMTP id a8mr10674752vcf.42.1409054871431; Tue, 26 Aug 2014 05:07:51 -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.45.67 with SMTP id uj3csp193017vcb; Tue, 26 Aug 2014 05:07:50 -0700 (PDT) X-Received: by 10.152.115.196 with SMTP id jq4mr26000575lab.43.1409054870217; Tue, 26 Aug 2014 05:07:50 -0700 (PDT) Received: from mail-la0-f47.google.com (mail-la0-f47.google.com [209.85.215.47]) by mx.google.com with ESMTPS id ez3si3281886lac.80.2014.08.26.05.07.49 for (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Tue, 26 Aug 2014 05:07:50 -0700 (PDT) Received-SPF: pass (google.com: domain of ulf.hansson@linaro.org designates 209.85.215.47 as permitted sender) client-ip=209.85.215.47; Received: by mail-la0-f47.google.com with SMTP id mc6so14683237lab.20 for ; Tue, 26 Aug 2014 05:07:49 -0700 (PDT) X-Received: by 10.112.98.140 with SMTP id ei12mr26210073lbb.6.1409054869651; Tue, 26 Aug 2014 05:07:49 -0700 (PDT) Received: from uffe-Latitude-E6430s.lan (90-231-160-185-no158.tbcn.telia.com. [90.231.160.185]) by mx.google.com with ESMTPSA id ai1sm4908979lbd.12.2014.08.26.05.07.47 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Tue, 26 Aug 2014 05:07:49 -0700 (PDT) From: Ulf Hansson To: "Rafael J. Wysocki" , Len Brown , Pavel Machek , Greg Kroah-Hartman , linux-pm@vger.kernel.org Cc: linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, Geert Uytterhoeven , Kevin Hilman , Alan Stern , Daniel Lezcano , Tomasz Figa , devicetree@vger.kernel.org, Linus Walleij , Simon Horman , Magnus Damm , Ben Dooks , Kukjin Kim , Stephen Boyd , Philipp Zabel , Mark Brown , Wolfram Sang , Chris Ball , Russell King , Tomasz Figa , Ulf Hansson Subject: [PATCH 2/9] PM / Domains: Add generic OF-based power domain look-up Date: Tue, 26 Aug 2014 14:07:10 +0200 Message-Id: <1409054837-5667-3-git-send-email-ulf.hansson@linaro.org> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1409054837-5667-1-git-send-email-ulf.hansson@linaro.org> References: <1409054837-5667-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.220.169 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: , From: Tomasz Figa This patch introduces generic code to perform power domain look-up using device tree and automatically bind devices to their power domains. Generic device tree binding is introduced to specify power domains of devices in their device tree nodes. Backwards compatibility with legacy Samsung-specific power domain bindings is provided, but for now the new code is not compiled when CONFIG_ARCH_EXYNOS is selected to avoid collision with legacy code. This will change as soon as Exynos power domain code gets converted to use the generic framework in further patch. Signed-off-by: Tomasz Figa Signed-off-by: Ulf Hansson [Ulf:Add attach|detach functions] Tested-by: Philipp Zabel --- .../devicetree/bindings/power/power_domain.txt | 51 ++++ drivers/base/power/domain.c | 291 +++++++++++++++++++++ include/linux/pm_domain.h | 46 ++++ kernel/power/Kconfig | 4 + 4 files changed, 392 insertions(+) create mode 100644 Documentation/devicetree/bindings/power/power_domain.txt diff --git a/Documentation/devicetree/bindings/power/power_domain.txt b/Documentation/devicetree/bindings/power/power_domain.txt new file mode 100644 index 0000000..303d0a9 --- /dev/null +++ b/Documentation/devicetree/bindings/power/power_domain.txt @@ -0,0 +1,51 @@ +* Generic power domains + +System on chip designs are often divided into multiple power domains that +can be used for power gating of selected IP blocks for power saving by +reduced leakage current. + +This device tree binding can be used to bind power domain consumer devices +with their power domains provided by power domain providers. A power domain +provider can be represented by any node in the device tree and can provide +one or more power domains. A consumer node can refer to the provider by +a phandle and a set of phandle arguments (so called power domain specifier) +of length specified by #power-domain-cells property in the power domain +provider node. + +==Power domain providers== + +Required properties: + - #power-domain-cells : Number of cells in a power domain specifier; + Typically 0 for nodes representing a single power domain and 1 for nodes + providing multiple power domains (e.g. power controllers), but can be + any value as specified by device tree binding documentation of particular + provider. + +Example: + + power: power-controller@12340000 { + compatible = "foo,power-controller"; + reg = <0x12340000 0x1000>; + #power-domain-cells = <1>; + }; + +The node above defines a power controller that is a power domain provider +and expects one cell as its phandle argument. + +==Power domain consumers== + +Required properties: + - power-domains : A phandle and power domain specifier as defined by bindings + of power controller specified by phandle. + +Example: + + leaky-device@12350000 { + compatible = "foo,i-leak-current"; + reg = <0x12350000 0x1000>; + power-domains = <&power 0>; + }; + +The node above defines a typical power domain consumer device, which is located +inside power domain with index 0 of power controller represented by node with +label "power". diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index eee55c1..0fb26e5 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -2189,3 +2190,293 @@ void pm_genpd_init(struct generic_pm_domain *genpd, list_add(&genpd->gpd_list_node, &gpd_list); mutex_unlock(&gpd_list_lock); } + +#ifdef CONFIG_PM_GENERIC_DOMAINS_OF +/* + * Device Tree based power domain providers. + * + * The code below implements generic device tree based power domain providers + * that bind device tree nodes with generic power domains registered in the + * system. + * + * Any driver that registers generic power domains and need to support binding + * of devices to these domains is supposed to register a power domain provider, + * which maps a power domain specifier retrieved from device tree to a power + * domain. + * + * Two simple mapping functions have been provided for convenience: + * - of_genpd_xlate_simple() for 1:1 device tree node to domain mapping, + * - of_genpd_xlate_onecell() for mapping of multiple domains per node + * by index. + */ + +/** + * struct of_genpd_provider - Power domain provider registration structure + * @link: Entry in global list of domain providers + * @node: Pointer to device tree node of domain provider + * @xlate: Provider-specific xlate callback mapping a set of specifier cells + * into a power domain. + * @data: context pointer to be passed into @xlate callback + */ +struct of_genpd_provider { + struct list_head link; + struct device_node *node; + genpd_xlate_t xlate; + void *data; +}; + +/* List of registered power domain providers. */ +static LIST_HEAD(of_genpd_providers); +/* Mutex to protect the list above. */ +static DEFINE_MUTEX(of_genpd_mutex); + +/** + * of_genpd_xlate_simple() - Xlate function for direct node-domain mapping + * @genpdspec: OF phandle args to map into a power domain + * @data: xlate function private data - pointer to struct generic_pm_domain + * + * This is a generic xlate function that can be used to model power domains + * that have their own device tree nodes. The private data of xlate function + * needs to be a valid pointer to struct generic_pm_domain. + */ +struct generic_pm_domain *of_genpd_xlate_simple( + struct of_phandle_args *genpdspec, + void *data) +{ + if (genpdspec->args_count != 0) + return ERR_PTR(-EINVAL); + return data; +} +EXPORT_SYMBOL_GPL(of_genpd_xlate_simple); + +/** + * of_genpd_xlate_onecell() - Xlate function for providers using single index. + * @genpdspec: OF phandle args to map into a power domain + * @data: xlate function private data - pointer to struct genpd_onecell_data + * + * This is a generic xlate function that can be used to model simple power + * domain controllers that have one device tree node and provide multiple + * power domains. A single cell is used as an index to an array of power + * domains specified in genpd_onecell_data struct when registering the + * provider. + */ +struct generic_pm_domain *of_genpd_xlate_onecell( + struct of_phandle_args *genpdspec, + void *data) +{ + struct genpd_onecell_data *genpd_data = data; + unsigned int idx = genpdspec->args[0]; + + if (genpdspec->args_count != 1) + return ERR_PTR(-EINVAL); + + if (idx >= genpd_data->domain_num) { + pr_err("%s: invalid domain index %d\n", __func__, idx); + return ERR_PTR(-EINVAL); + } + + return genpd_data->domains[idx]; +} +EXPORT_SYMBOL_GPL(of_genpd_xlate_onecell); + +/** + * of_genpd_add_provider() - Register a domain provider for a node + * @np: Device node pointer associated with domain provider. + * @xlate: Callback for decoding domain from phandle arguments. + * @data: Context pointer for @genpd_src_get callback. + */ +int of_genpd_add_provider(struct device_node *np, genpd_xlate_t xlate, + void *data) +{ + struct of_genpd_provider *cp; + + cp = kzalloc(sizeof(*cp), GFP_KERNEL); + if (!cp) + return -ENOMEM; + + cp->node = of_node_get(np); + cp->data = data; + cp->xlate = xlate; + + mutex_lock(&of_genpd_mutex); + list_add(&cp->link, &of_genpd_providers); + mutex_unlock(&of_genpd_mutex); + pr_debug("Added domain provider from %s\n", np->full_name); + + return 0; +} +EXPORT_SYMBOL_GPL(of_genpd_add_provider); + +/** + * of_genpd_del_provider() - Remove a previously registered domain provider + * @np: Device node pointer associated with domain provider + */ +void of_genpd_del_provider(struct device_node *np) +{ + struct of_genpd_provider *cp; + + mutex_lock(&of_genpd_mutex); + list_for_each_entry(cp, &of_genpd_providers, link) { + if (cp->node == np) { + list_del(&cp->link); + of_node_put(cp->node); + kfree(cp); + break; + } + } + mutex_unlock(&of_genpd_mutex); +} +EXPORT_SYMBOL_GPL(of_genpd_del_provider); + +/** + * of_genpd_get_from_provider() - Look-up power domain + * @genpdspec: OF phandle args to use for look-up + * + * Looks for domain provider under node specified by @genpdspec and if found + * uses xlate function of the provider to map phandle args to a power domain. + * + * Returns a valid pointer to struct generic_pm_domain on success or ERR_PTR() + * on failure. + */ +static struct generic_pm_domain *of_genpd_get_from_provider( + struct of_phandle_args *genpdspec) +{ + struct generic_pm_domain *genpd = ERR_PTR(-ENOENT); + struct of_genpd_provider *provider; + + mutex_lock(&of_genpd_mutex); + + /* Check if we have such a provider in our array */ + list_for_each_entry(provider, &of_genpd_providers, link) { + if (provider->node == genpdspec->np) + genpd = provider->xlate(genpdspec, provider->data); + if (!IS_ERR(genpd)) + break; + } + + mutex_unlock(&of_genpd_mutex); + + return genpd; +} + +/** + * genpd_dev_pm_attach - Attach a device to it's power domain using DT. + * @dev: Device to attach. + * + * Parse device's OF node to find a power domain specifier. If such is found, + * attaches the device to retrieved pm_domain ops. + * + * Both generic and legacy Samsung-specific DT bindings are supported to + * keep backwards compatibility with existing DTBs. + * + * Returns 0 on successfully attached power domain or negative error code. + */ +int genpd_dev_pm_attach(struct device *dev) +{ + struct of_phandle_args pd_args; + struct generic_pm_domain *pd; + int ret; + + if (!dev->of_node) + return -ENODEV; + + if (dev->pm_domain) + return -EEXIST; + + ret = of_parse_phandle_with_args(dev->of_node, "power-domains", + "#power-domain-cells", 0, &pd_args); + if (ret < 0) { + if (ret != -ENOENT) + return ret; + + /* + * Try legacy Samsung-specific bindings + * (for backwards compatibility of DT ABI) + */ + pd_args.args_count = 0; + pd_args.np = of_parse_phandle(dev->of_node, + "samsung,power-domain", 0); + if (!pd_args.np) + return -ENOENT; + } + + pd = of_genpd_get_from_provider(&pd_args); + if (IS_ERR(pd)) { + dev_dbg(dev, "%s() failed to find power domain: %ld\n", + __func__, PTR_ERR(pd)); + of_node_put(dev->of_node); + return PTR_ERR(pd); + } + + dev_dbg(dev, "adding to power domain %s\n", pd->name); + + while (1) { + ret = pm_genpd_add_device(pd, dev); + if (ret != -EAGAIN) + break; + cond_resched(); + } + + if (ret < 0) { + dev_err(dev, "failed to add to power domain %s: %d", + pd->name, ret); + of_node_put(dev->of_node); + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(genpd_dev_pm_attach); + +/** + * genpd_dev_pm_detach - Detach a device from it's power domain. + * @dev: Device to attach. + * + * Try to locate a corresponding generic power domain, which the device + * then previously were attached to. If found the device is detached from + * the power domain. + * + * Returns 0 on successfully detached power domain or negative error code. + */ +int genpd_dev_pm_detach(struct device *dev) +{ + struct generic_pm_domain *pd = NULL, *gpd; + int ret = 0; + + if (!dev->pm_domain) + return -ENODEV; + + mutex_lock(&gpd_list_lock); + list_for_each_entry(gpd, &gpd_list, gpd_list_node) { + if (&gpd->domain == dev->pm_domain) { + pd = gpd; + break; + } + } + mutex_unlock(&gpd_list_lock); + + if (!pd) + return -ENOENT; + + dev_dbg(dev, "removing from power domain %s\n", pd->name); + + while (1) { + ret = pm_genpd_remove_device(pd, dev); + if (ret != -EAGAIN) + break; + cond_resched(); + } + + if (ret < 0) { + dev_err(dev, "failed to remove from power domain %s: %d", + pd->name, ret); + return ret; + } + + /* Check if domain can be powered off after removing this device. */ + genpd_queue_power_off_work(pd); + + return 0; +} +EXPORT_SYMBOL_GPL(genpd_dev_pm_detach); +#endif diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index 7c1d252..5989758 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h @@ -310,4 +310,50 @@ static inline void pm_genpd_syscore_poweron(struct device *dev) pm_genpd_syscore_switch(dev, false); } +/* OF power domain providers */ +struct of_device_id; + +struct genpd_onecell_data { + struct generic_pm_domain **domains; + unsigned int domain_num; +}; + +typedef struct generic_pm_domain *(*genpd_xlate_t)(struct of_phandle_args *args, + void *data); + +#ifdef CONFIG_PM_GENERIC_DOMAINS_OF +int of_genpd_add_provider(struct device_node *np, genpd_xlate_t xlate, + void *data); +void of_genpd_del_provider(struct device_node *np); + +struct generic_pm_domain *of_genpd_xlate_simple( + struct of_phandle_args *genpdspec, + void *data); +struct generic_pm_domain *of_genpd_xlate_onecell( + struct of_phandle_args *genpdspec, + void *data); + +int genpd_dev_pm_attach(struct device *dev); +int genpd_dev_pm_detach(struct device *dev); +#else /* !CONFIG_PM_GENERIC_DOMAINS_OF */ +static inline int of_genpd_add_provider(struct device_node *np, + genpd_xlate_t xlate, void *data) +{ + return 0; +} +static inline void of_genpd_del_provider(struct device_node *np) {} + +#define of_genpd_xlate_simple NULL +#define of_genpd_xlate_onecell NULL + +static inline int genpd_dev_pm_attach(struct device *dev) +{ + return -ENODEV; +} +static inline int genpd_dev_pm_detach(struct device *dev) +{ + return -ENODEV; +} +#endif /* CONFIG_PM_GENERIC_DOMAINS_OF */ + #endif /* _LINUX_PM_DOMAIN_H */ diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig index e4e4121..897619b 100644 --- a/kernel/power/Kconfig +++ b/kernel/power/Kconfig @@ -302,6 +302,10 @@ config PM_GENERIC_DOMAINS_RUNTIME def_bool y depends on PM_RUNTIME && PM_GENERIC_DOMAINS +config PM_GENERIC_DOMAINS_OF + def_bool y + depends on PM_GENERIC_DOMAINS && OF && !ARCH_EXYNOS + config CPU_PM bool depends on SUSPEND || CPU_IDLE