From patchwork Thu Aug 25 20:03:16 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lina Iyer X-Patchwork-Id: 9299875 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 6E293607D8 for ; Thu, 25 Aug 2016 20:11:11 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 5FA67292E9 for ; Thu, 25 Aug 2016 20:11:11 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 54473292EB; Thu, 25 Aug 2016 20:11:11 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.8 required=2.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_HI,T_DKIM_INVALID autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 98E1C29357 for ; Thu, 25 Aug 2016 20:11:10 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751723AbcHYULG (ORCPT ); Thu, 25 Aug 2016 16:11:06 -0400 Received: from mail-pa0-f48.google.com ([209.85.220.48]:35790 "EHLO mail-pa0-f48.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1755789AbcHYULF (ORCPT ); Thu, 25 Aug 2016 16:11:05 -0400 Received: by mail-pa0-f48.google.com with SMTP id hb8so19813085pac.2 for ; Thu, 25 Aug 2016 13:11:05 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=gXEGqQUPAO3aKAtRQKl5SQRCk4rVDGjrDQq1P7fYe5U=; b=C5hW+vq2pwVu045X++TCNg+SIdqvtwc2+M5zLLUkuu8ZsS3wdrODQUHFcdLpu94sa6 oyXat1H9mgl8bDeWft5G/pnwLKQYARzPesCduYxLAa7+9evacPBNsdGSW39kDYHRNMA1 SmTe2lD+9ft5Jb05Qxo6FyyMJ7AtbTIZzN9mk= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=gXEGqQUPAO3aKAtRQKl5SQRCk4rVDGjrDQq1P7fYe5U=; b=VP7lUg9A6L0kGl2IbCYJtTDrumcetTdF8zhxmw5P29sT9KxNhGeJUfUNBCujigO8NF Fe8Aqs4LGNPO5TQydXLGpDApp8z5djBvvXL3Zct3Ho9H+tUIwmC7p0h+D5UzmpIEm7DM hciHO8pddaOvF7h23NWl97m30H3Zx9tWPGiFevdQ4UafWP6X37Kqg4yotIcmS67QKz0Q Yqj6vbfvYpz5NeO3DbPJQW42BL0+wz6qpzpZL47E3WkiRoLaN77PaK/0B4zPT4Mp04+Y 0Lmw05eyyH+XUd44uEYOsrAr2CeirIlRgnmC/2O0xE9DXlyHIe7ZDzU0ivUWHW7/5g2P b5Xg== X-Gm-Message-State: AE9vXwOjEx+gh0EUcCWLqoksx2RvaHyALS7vaaE2fU5xxZfb6p/VmT8apA/ApIwp50yTHmUa X-Received: by 10.66.193.163 with SMTP id hp3mr19742416pac.73.1472155431461; Thu, 25 Aug 2016 13:03:51 -0700 (PDT) Received: from ubuntu.localdomain (i-global254.qualcomm.com. [199.106.103.254]) by smtp.gmail.com with ESMTPSA id u1sm22841644pfu.12.2016.08.25.13.03.49 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 25 Aug 2016 13:03:50 -0700 (PDT) From: Lina Iyer To: ulf.hansson@linaro.org, khilman@kernel.org, rjw@rjwysocki.net, linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org Cc: andy.gross@linaro.org, sboyd@codeaurora.org, linux-arm-msm@vger.kernel.org, brendan.jackman@arm.com, lorenzo.pieralisi@arm.com, sudeep.holla@arm.com, Juri.Lelli@arm.com, Lina Iyer Subject: [PATCH v4 07/16] PM / cpu_domains: Initialize CPU PM domains from DT Date: Thu, 25 Aug 2016 14:03:16 -0600 Message-Id: <1472155405-41841-8-git-send-email-lina.iyer@linaro.org> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1472155405-41841-1-git-send-email-lina.iyer@linaro.org> References: <1472155405-41841-1-git-send-email-lina.iyer@linaro.org> Sender: linux-pm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Add helper functions to parse DT and initialize the CPU PM domains and attach CPU to their respective domains using information provided in the DT. For each CPU in the DT, we identify the domain provider; initialize and register the PM domain if isn't already registered and attach all the CPU devices to the domain. Usually, when there are multiple clusters of CPUs, there is a top level coherency domain that is dependent on these individual domains. All domains thus created are marked IRQ safe automatically and therefore may be powered down when the CPUs in the domain are powered down by cpuidle. Cc: Kevin Hilman Suggested-by: Ulf Hansson Signed-off-by: Lina Iyer --- drivers/base/power/cpu_domains.c | 190 +++++++++++++++++++++++++++++++++++++++ include/linux/cpu_domains.h | 18 ++++ 2 files changed, 208 insertions(+) diff --git a/drivers/base/power/cpu_domains.c b/drivers/base/power/cpu_domains.c index 73e493b..8bf61e2 100644 --- a/drivers/base/power/cpu_domains.c +++ b/drivers/base/power/cpu_domains.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -189,3 +190,192 @@ fail: return ERR_PTR(ret); } EXPORT_SYMBOL(cpu_pd_init); + +static struct generic_pm_domain *alloc_genpd(const char *name) +{ + struct generic_pm_domain *genpd; + + genpd = kzalloc(sizeof(*genpd), GFP_KERNEL); + if (!genpd) + return ERR_PTR(-ENOMEM); + + genpd->name = kstrndup(name, CPU_PD_NAME_MAX, GFP_KERNEL); + if (!genpd->name) { + kfree(genpd); + return ERR_PTR(-ENOMEM); + } + + return genpd; +} + +/** + * of_init_cpu_pm_domain() - Initialize a CPU PM domain from a device node + * + * @dn: The domain provider's device node + * @ops: The power_on/_off callbacks for the domain + * + * Returns the generic_pm_domain (genpd) pointer to the domain on success + */ +static struct generic_pm_domain *of_init_cpu_pm_domain(struct device_node *dn, + const struct cpu_pd_ops *ops) +{ + struct cpu_pm_domain *pd = NULL; + struct generic_pm_domain *genpd = NULL; + int ret = -ENOMEM; + + if (!of_device_is_available(dn)) + return ERR_PTR(-ENODEV); + + genpd = alloc_genpd(dn->full_name); + if (IS_ERR(genpd)) + return genpd; + + genpd->of_node = dn; + + /* Populate platform specific states from DT */ + if (ops->populate_state_data) { + struct device_node *np; + int i; + + /* Initialize the arm,idle-state properties */ + ret = pm_genpd_of_parse_power_states(genpd); + if (ret) { + pr_warn("%s domain states not initialized (%d)\n", + dn->full_name, ret); + goto fail; + } + for (i = 0; i < genpd->state_count; i++) { + ret = ops->populate_state_data(genpd->states[i].of_node, + &genpd->states[i].param); + of_node_put(np); + if (ret) + goto fail; + } + } + + genpd = cpu_pd_init(genpd, ops); + if (IS_ERR(genpd)) + goto fail; + + ret = of_genpd_add_provider_simple(dn, genpd); + if (ret) + pr_warn("Unable to add genpd %s as provider\n", + pd->genpd->name); + + return genpd; +fail: + kfree(genpd->name); + kfree(genpd); + if (pd) + kfree(pd->cpus); + kfree(pd); + return ERR_PTR(ret); +} + +static struct generic_pm_domain *of_get_cpu_domain(struct device_node *dn, + const struct cpu_pd_ops *ops, int cpu) +{ + struct of_phandle_args args; + struct generic_pm_domain *genpd, *parent; + int ret; + + /* Do we have this domain? If not, create the domain */ + args.np = dn; + args.args_count = 0; + + genpd = of_genpd_get_from_provider(&args); + if (!IS_ERR(genpd)) + return genpd; + + genpd = of_init_cpu_pm_domain(dn, ops); + if (IS_ERR(genpd)) + return genpd; + + /* Is there a domain provider for this domain? */ + ret = of_parse_phandle_with_args(dn, "power-domains", + "#power-domain-cells", 0, &args); + if (ret < 0) + goto skip_parent; + + /* Find its parent and attach this domain to it, recursively */ + parent = of_get_cpu_domain(args.np, ops, cpu); + if (IS_ERR(parent)) + goto skip_parent; + + ret = cpu_pd_attach_domain(parent, genpd); + if (ret) + pr_err("Unable to attach domain %s to parent %s\n", + genpd->name, parent->name); + +skip_parent: + of_node_put(dn); + return genpd; +} + +/** + * of_setup_cpu_pd_single() - Setup the PM domains for a CPU + * + * @cpu: The CPU for which the PM domain is to be set up. + * @ops: The PM domain suspend/resume ops for the CPU's domain + * + * If the CPU PM domain exists already, then the CPU is attached to + * that CPU PD. If it doesn't, the domain is created, the @ops are + * set for power_on/power_off callbacks and then the CPU is attached + * to that domain. If the domain was created outside this framework, + * then we do not attach the CPU to the domain. + */ +int of_setup_cpu_pd_single(int cpu, const struct cpu_pd_ops *ops) +{ + + struct device_node *dn, *np; + struct generic_pm_domain *genpd; + struct cpu_pm_domain *cpu_pd; + + np = of_get_cpu_node(cpu, NULL); + if (!np) + return -ENODEV; + + dn = of_parse_phandle(np, "power-domains", 0); + of_node_put(np); + if (!dn) + return -ENODEV; + + /* Find the genpd for this CPU, create if not found */ + genpd = of_get_cpu_domain(dn, ops, cpu); + of_node_put(dn); + if (IS_ERR(genpd)) + return PTR_ERR(genpd); + + cpu_pd = to_cpu_pd(genpd); + if (!cpu_pd) { + pr_err("%s: Genpd was created outside CPU PM domains\n", + __func__); + return -ENOENT; + } + + return cpu_pd_attach_cpu(genpd, cpu); +} +EXPORT_SYMBOL(of_setup_cpu_pd_single); + +/** + * of_setup_cpu_pd() - Setup the PM domains for all CPUs + * + * @ops: The PM domain suspend/resume ops for all the domains + * + * Setup the CPU PM domain and attach all possible CPUs to their respective + * domains. The domains are created if not already and then attached. + */ +int of_setup_cpu_pd(const struct cpu_pd_ops *ops) +{ + int cpu; + int ret; + + for_each_possible_cpu(cpu) { + ret = of_setup_cpu_pd_single(cpu, ops); + if (ret) + break; + } + + return ret; +} +EXPORT_SYMBOL(of_setup_cpu_pd); diff --git a/include/linux/cpu_domains.h b/include/linux/cpu_domains.h index 3a0a027..736d9e6 100644 --- a/include/linux/cpu_domains.h +++ b/include/linux/cpu_domains.h @@ -14,8 +14,10 @@ #include struct cpumask; +struct device_node; struct cpu_pd_ops { + int (*populate_state_data)(struct device_node *n, u32 *param); int (*power_off)(u32 state_idx, u32 param, const struct cpumask *mask); int (*power_on)(void); }; @@ -46,4 +48,20 @@ static inline int cpu_pd_attach_cpu(struct generic_pm_domain *genpd, int cpu) #endif /* CONFIG_PM_GENERIC_DOMAINS */ +#ifdef CONFIG_PM_GENERIC_DOMAINS_OF + +int of_setup_cpu_pd_single(int cpu, const struct cpu_pd_ops *ops); + +int of_setup_cpu_pd(const struct cpu_pd_ops *ops); + +#else + +static inline int of_setup_cpu_pd_single(int cpu, const struct cpu_pd_ops *ops) +{ return -ENODEV; } + +static inline int of_setup_cpu_pd(const struct cpu_pd_ops *ops) +{ return -ENODEV; } + +#endif /* CONFIG_PM_GENERIC_DOMAINS_OF */ + #endif /* __CPU_DOMAINS_H__ */