From patchwork Tue Aug 11 10:34:38 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 52300 Return-Path: X-Original-To: linaro@patches.linaro.org Delivered-To: linaro@patches.linaro.org Received: from mail-lb0-f197.google.com (mail-lb0-f197.google.com [209.85.217.197]) by patches.linaro.org (Postfix) with ESMTPS id D4F9822FEC for ; Tue, 11 Aug 2015 10:35:50 +0000 (UTC) Received: by lbsm2 with SMTP id m2sf34805820lbs.1 for ; Tue, 11 Aug 2015 03:35:49 -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:in-reply-to:references :sender:precedence:list-id:x-original-sender :x-original-authentication-results:mailing-list:list-post:list-help :list-archive:list-unsubscribe; bh=x26TicVKpXID6BM4Nr5DmMF9EzsJm2aRkBcDQOuSIac=; b=UppPY/sDLDOGC4D0Bk2zb/ezrMsXpbR9lQ3eJ5IHQDNo6abSvdQccKatzGHvGeMzmN hKB3LW6LK/FeGeTNiQWYeBdJCkIgagrde3EdcIrWxV5keEUNOdY0tUcKbyvzH9nGgf6T HirinhcGJh3TGHHrwaf+jYzTi1tNPkWXiwgaDYlfJrSUoGMBOY0DANyfmDErJPwiLhD2 gWvcONMr5gsXWGU5eGeIyDJ3ZRvfXan/SRiPtcaL7gh3R3jB6n19Jay/IV6U1XxgZ3dR uvrxpH7/Ijv52mndTaRxGSNEBtmHgbUJdXeg9tCWVVMxr1qSG+bVUfsb9wXxZ/x4/9zb J0aA== X-Gm-Message-State: ALoCoQk9zbKJqkt2KtbgZ5fEG0J+iywJ/qWvxyWOtuZWA1gdQ+V9HDZbnmyY3QVmcYzxqDN2iz7o X-Received: by 10.152.1.164 with SMTP id 4mr1750206lan.2.1439289349436; Tue, 11 Aug 2015 03:35:49 -0700 (PDT) MIME-Version: 1.0 X-BeenThere: patchwork-forward@linaro.org Received: by 10.152.10.33 with SMTP id f1ls927392lab.49.gmail; Tue, 11 Aug 2015 03:35:49 -0700 (PDT) X-Received: by 10.152.28.193 with SMTP id d1mr11836836lah.72.1439289349243; Tue, 11 Aug 2015 03:35:49 -0700 (PDT) Received: from mail-lb0-f179.google.com (mail-lb0-f179.google.com. [209.85.217.179]) by mx.google.com with ESMTPS id z1si1028407lbw.159.2015.08.11.03.35.49 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 11 Aug 2015 03:35:49 -0700 (PDT) Received-SPF: pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.217.179 as permitted sender) client-ip=209.85.217.179; Received: by lbbtg9 with SMTP id tg9so3571703lbb.1 for ; Tue, 11 Aug 2015 03:35:49 -0700 (PDT) X-Received: by 10.112.140.68 with SMTP id re4mr25474455lbb.72.1439289349077; Tue, 11 Aug 2015 03:35:49 -0700 (PDT) X-Forwarded-To: patchwork-forward@linaro.org X-Forwarded-For: patch@linaro.org patchwork-forward@linaro.org Delivered-To: patch@linaro.org Received: by 10.112.7.198 with SMTP id l6csp2434512lba; Tue, 11 Aug 2015 03:35:47 -0700 (PDT) X-Received: by 10.68.162.195 with SMTP id yc3mr54794642pbb.161.1439289346603; Tue, 11 Aug 2015 03:35:46 -0700 (PDT) Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id df1si2753783pdb.85.2015.08.11.03.35.45; Tue, 11 Aug 2015 03:35:46 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S964989AbbHKKfn (ORCPT + 28 others); Tue, 11 Aug 2015 06:35:43 -0400 Received: from mail-pd0-f173.google.com ([209.85.192.173]:36009 "EHLO mail-pd0-f173.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S964942AbbHKKfj (ORCPT ); Tue, 11 Aug 2015 06:35:39 -0400 Received: by pdco4 with SMTP id o4so82431223pdc.3 for ; Tue, 11 Aug 2015 03:35:38 -0700 (PDT) X-Received: by 10.70.54.196 with SMTP id l4mr55057907pdp.42.1439289338498; Tue, 11 Aug 2015 03:35:38 -0700 (PDT) Received: from localhost ([122.171.186.190]) by smtp.gmail.com with ESMTPSA id i10sm2103913pdr.78.2015.08.11.03.35.34 (version=TLSv1.2 cipher=RC4-SHA bits=128/128); Tue, 11 Aug 2015 03:35:37 -0700 (PDT) From: Viresh Kumar To: Rafael Wysocki , nm@ti.com, sboyd@codeaurora.org Cc: linaro-kernel@lists.linaro.org, linux-pm@vger.kernel.org, khilman@linaro.org, Viresh Kumar , Anand Moon , Bartlomiej Zolnierkiewicz , Dmitry Torokhov , Greg Kroah-Hartman , Len Brown , linux-kernel@vger.kernel.org (open list), Pavel Machek Subject: [PATCH V2 5/6] PM / OPP: Move cpu specific code to opp/cpu.c Date: Tue, 11 Aug 2015 16:04:38 +0530 Message-Id: <7aa4377582c0b86ecaa63aa6686f61219b54f1aa.1439288881.git.viresh.kumar@linaro.org> X-Mailer: git-send-email 2.4.0 In-Reply-To: References: In-Reply-To: References: Sender: linux-kernel-owner@vger.kernel.org Precedence: list List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-Removed-Original-Auth: Dkim didn't pass. X-Original-Sender: viresh.kumar@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.217.179 as permitted sender) smtp.mailfrom=patch+caf_=patchwork-forward=linaro.org@linaro.org Mailing-list: list patchwork-forward@linaro.org; contact patchwork-forward+owners@linaro.org X-Google-Group-Id: 836684582541 List-Post: , List-Help: , List-Archive: List-Unsubscribe: , Move cpu device specific code out of generic opp library, and add it to cpu.c. Along with that, create a core-internal opp.h header, which will be used to share structures and function prototypes within opp core. Reviewed-by: Stephen Boyd Signed-off-by: Viresh Kumar --- drivers/base/power/opp/core.c | 276 +----------------------------------------- drivers/base/power/opp/cpu.c | 160 +++++++++++++++++++++++- drivers/base/power/opp/opp.h | 143 ++++++++++++++++++++++ 3 files changed, 304 insertions(+), 275 deletions(-) create mode 100644 drivers/base/power/opp/opp.h diff --git a/drivers/base/power/opp/core.c b/drivers/base/power/opp/core.c index ccc636ad8e0f..29a603772992 100644 --- a/drivers/base/power/opp/core.c +++ b/drivers/base/power/opp/core.c @@ -11,131 +11,14 @@ * published by the Free Software Foundation. */ -#include -#include #include #include #include #include -#include -#include -#include -#include #include #include -/* - * Internal data structure organization with the OPP layer library is as - * follows: - * dev_opp_list (root) - * |- device 1 (represents voltage domain 1) - * | |- opp 1 (availability, freq, voltage) - * | |- opp 2 .. - * ... ... - * | `- opp n .. - * |- device 2 (represents the next voltage domain) - * ... - * `- device m (represents mth voltage domain) - * device 1, 2.. are represented by dev_opp structure while each opp - * is represented by the opp structure. - */ - -/** - * struct dev_pm_opp - Generic OPP description structure - * @node: opp list node. The nodes are maintained throughout the lifetime - * of boot. It is expected only an optimal set of OPPs are - * added to the library by the SoC framework. - * RCU usage: opp list is traversed with RCU locks. node - * modification is possible realtime, hence the modifications - * are protected by the dev_opp_list_lock for integrity. - * IMPORTANT: the opp nodes should be maintained in increasing - * order. - * @dynamic: not-created from static DT entries. - * @available: true/false - marks if this OPP as available or not - * @turbo: true if turbo (boost) OPP - * @rate: Frequency in hertz - * @u_volt: Target voltage in microvolts corresponding to this OPP - * @u_volt_min: Minimum voltage in microvolts corresponding to this OPP - * @u_volt_max: Maximum voltage in microvolts corresponding to this OPP - * @u_amp: Maximum current drawn by the device in microamperes - * @clock_latency_ns: Latency (in nanoseconds) of switching to this OPP's - * frequency from any other OPP's frequency. - * @dev_opp: points back to the device_opp struct this opp belongs to - * @rcu_head: RCU callback head used for deferred freeing - * @np: OPP's device node. - * - * This structure stores the OPP information for a given device. - */ -struct dev_pm_opp { - struct list_head node; - - bool available; - bool dynamic; - bool turbo; - unsigned long rate; - - unsigned long u_volt; - unsigned long u_volt_min; - unsigned long u_volt_max; - unsigned long u_amp; - unsigned long clock_latency_ns; - - struct device_opp *dev_opp; - struct rcu_head rcu_head; - - struct device_node *np; -}; - -/** - * struct device_list_opp - devices managed by 'struct device_opp' - * @node: list node - * @dev: device to which the struct object belongs - * @rcu_head: RCU callback head used for deferred freeing - * - * This is an internal data structure maintaining the list of devices that are - * managed by 'struct device_opp'. - */ -struct device_list_opp { - struct list_head node; - const struct device *dev; - struct rcu_head rcu_head; -}; - -/** - * struct device_opp - Device opp structure - * @node: list node - contains the devices with OPPs that - * have been registered. Nodes once added are not modified in this - * list. - * RCU usage: nodes are not modified in the list of device_opp, - * however addition is possible and is secured by dev_opp_list_lock - * @srcu_head: notifier head to notify the OPP availability changes. - * @rcu_head: RCU callback head used for deferred freeing - * @dev_list: list of devices that share these OPPs - * @opp_list: list of opps - * @np: struct device_node pointer for opp's DT node. - * @shared_opp: OPP is shared between multiple devices. - * - * This is an internal data structure maintaining the link to opps attached to - * a device. This structure is not meant to be shared to users as it is - * meant for book keeping and private to OPP library. - * - * Because the opp structures can be used from both rcu and srcu readers, we - * need to wait for the grace period of both of them before freeing any - * resources. And so we have used kfree_rcu() from within call_srcu() handlers. - */ -struct device_opp { - struct list_head node; - - struct srcu_notifier_head srcu_head; - struct rcu_head rcu_head; - struct list_head dev_list; - struct list_head opp_list; - - struct device_node *np; - unsigned long clock_latency_ns_max; - bool shared_opp; - struct dev_pm_opp *suspend_opp; -}; +#include "opp.h" /* * The root of the list of all devices. All device_opp structures branch off @@ -200,7 +83,7 @@ static struct device_opp *_managed_opp(const struct device_node *np) * is a RCU protected pointer. This means that device_opp is valid as long * as we are under RCU lock. */ -static struct device_opp *_find_device_opp(struct device *dev) +struct device_opp *_find_device_opp(struct device *dev) { struct device_opp *dev_opp; @@ -551,8 +434,8 @@ static void _remove_list_dev(struct device_list_opp *list_dev, _kfree_list_dev_rcu); } -static struct device_list_opp *_add_list_dev(const struct device *dev, - struct device_opp *dev_opp) +struct device_list_opp *_add_list_dev(const struct device *dev, + struct device_opp *dev_opp) { struct device_list_opp *list_dev; @@ -1230,28 +1113,8 @@ void of_free_opp_table(struct device *dev) } EXPORT_SYMBOL_GPL(of_free_opp_table); -void dev_pm_opp_cpumask_free_table(cpumask_var_t cpumask) -{ - struct device *cpu_dev; - int cpu; - - WARN_ON(cpumask_empty(cpumask)); - - for_each_cpu(cpu, cpumask) { - cpu_dev = get_cpu_device(cpu); - if (!cpu_dev) { - pr_err("%s: failed to get cpu%d device\n", __func__, - cpu); - continue; - } - - of_free_opp_table(cpu_dev); - } -} -EXPORT_SYMBOL_GPL(dev_pm_opp_cpumask_free_table); - /* Returns opp descriptor node for a device, caller must do of_node_put() */ -static struct device_node *_of_get_opp_desc_node(struct device *dev) +struct device_node *_of_get_opp_desc_node(struct device *dev) { /* * TODO: Support for multiple OPP tables. @@ -1396,133 +1259,4 @@ int of_init_opp_table(struct device *dev) return ret; } EXPORT_SYMBOL_GPL(of_init_opp_table); - -int dev_pm_opp_cpumask_init_opp(cpumask_var_t cpumask) -{ - struct device *cpu_dev; - int cpu, ret = 0; - - WARN_ON(cpumask_empty(cpumask)); - - for_each_cpu(cpu, cpumask) { - cpu_dev = get_cpu_device(cpu); - if (!cpu_dev) { - pr_err("%s: failed to get cpu%d device\n", __func__, - cpu); - continue; - } - - ret = of_init_opp_table(cpu_dev); - if (ret) { - pr_err("%s: couldn't find opp table for cpu:%d, %d\n", - __func__, cpu, ret); - - /* Free all other OPPs */ - dev_pm_opp_cpumask_free_table(cpumask); - break; - } - } - - return ret; -} -EXPORT_SYMBOL_GPL(dev_pm_opp_cpumask_init_opp); - -/* Required only for V1 bindings, as v2 can manage it from DT itself */ -int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, cpumask_var_t cpumask) -{ - struct device_list_opp *list_dev; - struct device_opp *dev_opp; - struct device *dev; - int cpu, ret = 0; - - rcu_read_lock(); - - dev_opp = _find_device_opp(cpu_dev); - if (IS_ERR(dev_opp)) { - ret = -EINVAL; - goto out_rcu_read_unlock; - } - - for_each_cpu(cpu, cpumask) { - if (cpu == cpu_dev->id) - continue; - - dev = get_cpu_device(cpu); - if (!dev) { - dev_err(cpu_dev, "%s: failed to get cpu%d device\n", - __func__, cpu); - continue; - } - - list_dev = _add_list_dev(dev, dev_opp); - if (!list_dev) { - dev_err(dev, "%s: failed to add list-dev for cpu%d device\n", - __func__, cpu); - continue; - } - } -out_rcu_read_unlock: - rcu_read_unlock(); - - return 0; -} -EXPORT_SYMBOL_GPL(dev_pm_opp_set_sharing_cpus); - -/* - * Works only for OPP v2 bindings. - * - * cpumask should be already set to mask of cpu_dev->id. - * Returns -ENOENT if operating-points-v2 bindings aren't supported. - */ -int dev_pm_opp_get_sharing_cpus(struct device *cpu_dev, cpumask_var_t cpumask) -{ - struct device_node *np, *tmp_np; - struct device *tcpu_dev; - int cpu, ret = 0; - - /* Get OPP descriptor node */ - np = _of_get_opp_desc_node(cpu_dev); - if (!np) { - dev_dbg(cpu_dev, "%s: Couldn't find opp node: %ld\n", __func__, - PTR_ERR(np)); - return -ENOENT; - } - - /* OPPs are shared ? */ - if (!of_property_read_bool(np, "opp-shared")) - goto put_cpu_node; - - for_each_possible_cpu(cpu) { - if (cpu == cpu_dev->id) - continue; - - tcpu_dev = get_cpu_device(cpu); - if (!tcpu_dev) { - dev_err(cpu_dev, "%s: failed to get cpu%d device\n", - __func__, cpu); - ret = -ENODEV; - goto put_cpu_node; - } - - /* Get OPP descriptor node */ - tmp_np = _of_get_opp_desc_node(tcpu_dev); - if (!tmp_np) { - dev_err(tcpu_dev, "%s: Couldn't find opp node: %ld\n", - __func__, PTR_ERR(tmp_np)); - ret = PTR_ERR(tmp_np); - goto put_cpu_node; - } - - /* CPUs are sharing opp node */ - if (np == tmp_np) - cpumask_set_cpu(cpu, cpumask); - - of_node_put(tmp_np); - } - -put_cpu_node: - of_node_put(np); - return ret; -} -EXPORT_SYMBOL_GPL(dev_pm_opp_get_sharing_cpus); #endif diff --git a/drivers/base/power/opp/cpu.c b/drivers/base/power/opp/cpu.c index 0dd033016e9d..7a56e98a7ba6 100644 --- a/drivers/base/power/opp/cpu.c +++ b/drivers/base/power/opp/cpu.c @@ -10,17 +10,18 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +#include #include -#include #include #include #include -#include -#include -#include +#include #include +#include "opp.h" + #ifdef CONFIG_CPU_FREQ + /** * dev_pm_opp_init_cpufreq_table() - create a cpufreq table for a device * @dev: device for which we do this operation @@ -114,3 +115,154 @@ void dev_pm_opp_free_cpufreq_table(struct device *dev, } EXPORT_SYMBOL_GPL(dev_pm_opp_free_cpufreq_table); #endif /* CONFIG_CPU_FREQ */ + +/* Required only for V1 bindings, as v2 can manage it from DT itself */ +int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, cpumask_var_t cpumask) +{ + struct device_list_opp *list_dev; + struct device_opp *dev_opp; + struct device *dev; + int cpu, ret = 0; + + rcu_read_lock(); + + dev_opp = _find_device_opp(cpu_dev); + if (IS_ERR(dev_opp)) { + ret = -EINVAL; + goto out_rcu_read_unlock; + } + + for_each_cpu(cpu, cpumask) { + if (cpu == cpu_dev->id) + continue; + + dev = get_cpu_device(cpu); + if (!dev) { + dev_err(cpu_dev, "%s: failed to get cpu%d device\n", + __func__, cpu); + continue; + } + + list_dev = _add_list_dev(dev, dev_opp); + if (!list_dev) { + dev_err(dev, "%s: failed to add list-dev for cpu%d device\n", + __func__, cpu); + continue; + } + } +out_rcu_read_unlock: + rcu_read_unlock(); + + return 0; +} +EXPORT_SYMBOL_GPL(dev_pm_opp_set_sharing_cpus); + +#ifdef CONFIG_OF +void dev_pm_opp_cpumask_free_table(cpumask_var_t cpumask) +{ + struct device *cpu_dev; + int cpu; + + WARN_ON(cpumask_empty(cpumask)); + + for_each_cpu(cpu, cpumask) { + cpu_dev = get_cpu_device(cpu); + if (!cpu_dev) { + pr_err("%s: failed to get cpu%d device\n", __func__, + cpu); + continue; + } + + of_free_opp_table(cpu_dev); + } +} +EXPORT_SYMBOL_GPL(dev_pm_opp_cpumask_free_table); + +int dev_pm_opp_cpumask_init_opp(cpumask_var_t cpumask) +{ + struct device *cpu_dev; + int cpu, ret = 0; + + WARN_ON(cpumask_empty(cpumask)); + + for_each_cpu(cpu, cpumask) { + cpu_dev = get_cpu_device(cpu); + if (!cpu_dev) { + pr_err("%s: failed to get cpu%d device\n", __func__, + cpu); + continue; + } + + ret = of_init_opp_table(cpu_dev); + if (ret) { + pr_err("%s: couldn't find opp table for cpu:%d, %d\n", + __func__, cpu, ret); + + /* Free all other OPPs */ + dev_pm_opp_cpumask_free_table(cpumask); + break; + } + } + + return ret; +} +EXPORT_SYMBOL_GPL(dev_pm_opp_cpumask_init_opp); + +/* + * Works only for OPP v2 bindings. + * + * cpumask should be already set to mask of cpu_dev->id. + * Returns -ENOENT if operating-points-v2 bindings aren't supported. + */ +int dev_pm_opp_get_sharing_cpus(struct device *cpu_dev, cpumask_var_t cpumask) +{ + struct device_node *np, *tmp_np; + struct device *tcpu_dev; + int cpu, ret = 0; + + /* Get OPP descriptor node */ + np = _of_get_opp_desc_node(cpu_dev); + if (!np) { + dev_dbg(cpu_dev, "%s: Couldn't find opp node: %ld\n", __func__, + PTR_ERR(np)); + return -ENOENT; + } + + /* OPPs are shared ? */ + if (!of_property_read_bool(np, "opp-shared")) + goto put_cpu_node; + + for_each_possible_cpu(cpu) { + if (cpu == cpu_dev->id) + continue; + + tcpu_dev = get_cpu_device(cpu); + if (!tcpu_dev) { + dev_err(cpu_dev, "%s: failed to get cpu%d device\n", + __func__, cpu); + ret = -ENODEV; + goto put_cpu_node; + } + + /* Get OPP descriptor node */ + tmp_np = _of_get_opp_desc_node(tcpu_dev); + if (!tmp_np) { + dev_err(tcpu_dev, "%s: Couldn't find opp node: %ld\n", + __func__, PTR_ERR(tmp_np)); + ret = PTR_ERR(tmp_np); + goto put_cpu_node; + } + + /* CPUs are sharing opp node */ + if (np == tmp_np) + cpumask_set_cpu(cpu, cpumask); + + of_node_put(tmp_np); + } + +put_cpu_node: + of_node_put(np); + return ret; +} +EXPORT_SYMBOL_GPL(dev_pm_opp_get_sharing_cpus); +#endif diff --git a/drivers/base/power/opp/opp.h b/drivers/base/power/opp/opp.h new file mode 100644 index 000000000000..dcb38f78dae4 --- /dev/null +++ b/drivers/base/power/opp/opp.h @@ -0,0 +1,143 @@ +/* + * Generic OPP Interface + * + * Copyright (C) 2009-2010 Texas Instruments Incorporated. + * Nishanth Menon + * Romit Dasgupta + * Kevin Hilman + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __DRIVER_OPP_H__ +#define __DRIVER_OPP_H__ + +#include +#include +#include +#include +#include +#include + +/* + * Internal data structure organization with the OPP layer library is as + * follows: + * dev_opp_list (root) + * |- device 1 (represents voltage domain 1) + * | |- opp 1 (availability, freq, voltage) + * | |- opp 2 .. + * ... ... + * | `- opp n .. + * |- device 2 (represents the next voltage domain) + * ... + * `- device m (represents mth voltage domain) + * device 1, 2.. are represented by dev_opp structure while each opp + * is represented by the opp structure. + */ + +/** + * struct dev_pm_opp - Generic OPP description structure + * @node: opp list node. The nodes are maintained throughout the lifetime + * of boot. It is expected only an optimal set of OPPs are + * added to the library by the SoC framework. + * RCU usage: opp list is traversed with RCU locks. node + * modification is possible realtime, hence the modifications + * are protected by the dev_opp_list_lock for integrity. + * IMPORTANT: the opp nodes should be maintained in increasing + * order. + * @dynamic: not-created from static DT entries. + * @available: true/false - marks if this OPP as available or not + * @turbo: true if turbo (boost) OPP + * @rate: Frequency in hertz + * @u_volt: Target voltage in microvolts corresponding to this OPP + * @u_volt_min: Minimum voltage in microvolts corresponding to this OPP + * @u_volt_max: Maximum voltage in microvolts corresponding to this OPP + * @u_amp: Maximum current drawn by the device in microamperes + * @clock_latency_ns: Latency (in nanoseconds) of switching to this OPP's + * frequency from any other OPP's frequency. + * @dev_opp: points back to the device_opp struct this opp belongs to + * @rcu_head: RCU callback head used for deferred freeing + * @np: OPP's device node. + * + * This structure stores the OPP information for a given device. + */ +struct dev_pm_opp { + struct list_head node; + + bool available; + bool dynamic; + bool turbo; + unsigned long rate; + + unsigned long u_volt; + unsigned long u_volt_min; + unsigned long u_volt_max; + unsigned long u_amp; + unsigned long clock_latency_ns; + + struct device_opp *dev_opp; + struct rcu_head rcu_head; + + struct device_node *np; +}; + +/** + * struct device_list_opp - devices managed by 'struct device_opp' + * @node: list node + * @dev: device to which the struct object belongs + * @rcu_head: RCU callback head used for deferred freeing + * + * This is an internal data structure maintaining the list of devices that are + * managed by 'struct device_opp'. + */ +struct device_list_opp { + struct list_head node; + const struct device *dev; + struct rcu_head rcu_head; +}; + +/** + * struct device_opp - Device opp structure + * @node: list node - contains the devices with OPPs that + * have been registered. Nodes once added are not modified in this + * list. + * RCU usage: nodes are not modified in the list of device_opp, + * however addition is possible and is secured by dev_opp_list_lock + * @srcu_head: notifier head to notify the OPP availability changes. + * @rcu_head: RCU callback head used for deferred freeing + * @dev_list: list of devices that share these OPPs + * @opp_list: list of opps + * @np: struct device_node pointer for opp's DT node. + * @shared_opp: OPP is shared between multiple devices. + * + * This is an internal data structure maintaining the link to opps attached to + * a device. This structure is not meant to be shared to users as it is + * meant for book keeping and private to OPP library. + * + * Because the opp structures can be used from both rcu and srcu readers, we + * need to wait for the grace period of both of them before freeing any + * resources. And so we have used kfree_rcu() from within call_srcu() handlers. + */ +struct device_opp { + struct list_head node; + + struct srcu_notifier_head srcu_head; + struct rcu_head rcu_head; + struct list_head dev_list; + struct list_head opp_list; + + struct device_node *np; + unsigned long clock_latency_ns_max; + bool shared_opp; + struct dev_pm_opp *suspend_opp; +}; + +/* Routines internal to opp core */ +struct device_opp *_find_device_opp(struct device *dev); +struct device_list_opp *_add_list_dev(const struct device *dev, + struct device_opp *dev_opp); +struct device_node *_of_get_opp_desc_node(struct device *dev); + +#endif /* __DRIVER_OPP_H__ */