From patchwork Sat Dec 7 00:24:22 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Saravana Kannan X-Patchwork-Id: 11277201 X-Patchwork-Delegate: viresh.linux@gmail.com Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 1B4B313B6 for ; Sat, 7 Dec 2019 00:24:40 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id EDCB5205ED for ; Sat, 7 Dec 2019 00:24:39 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="bYSAiSYK" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726453AbfLGAYc (ORCPT ); Fri, 6 Dec 2019 19:24:32 -0500 Received: from mail-pj1-f74.google.com ([209.85.216.74]:57311 "EHLO mail-pj1-f74.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726425AbfLGAYc (ORCPT ); Fri, 6 Dec 2019 19:24:32 -0500 Received: by mail-pj1-f74.google.com with SMTP id y11so4456625pjr.23 for ; Fri, 06 Dec 2019 16:24:32 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=t/DVJ0mhY4OY2TWA/vdfuk+GWdXq6Kiwb0UmMVH0gpI=; b=bYSAiSYKDu4ERcSkfVZ+l8+jpWOtQ+s5bofEgPY+pKejypGhEnX6NK/M8pUc6AwcWa eN8TKaBDYOVVsaudPCIwKIMgofceRBJ8X3/TvzLzkrniROqEu++WTp69ch3/0kVKx3L3 Su83ADGEUpTSF5udUsbWdZE9uFsYjc/tg72TBip72DpiwycMWk6HFbrEUaFBTsYivpBh 4IPRImdHJn9Shpo61BERYBMkZqNhbIyBwOg88W2MA2Eg6e8N3Kev+o+Vkftcfdi+x0Fr VQZk3ycPIKyS6FB+bcBY5GgOnSfNbV3BO27h/KyhksK17rNacDd2yJEb7iFPhg6canpg TXXA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=t/DVJ0mhY4OY2TWA/vdfuk+GWdXq6Kiwb0UmMVH0gpI=; b=iMZ4NxFbyAIMXZCjNQcJxEwd3m5U5jYUDwVKDzuOBPnhrUe3/2QcFy5OpooU5GpU1Q 3dWaHC/XO9ePv3YJsWolGCemRtU4LRME3CuhgW599QLbhF5RZe5IjvLvfWWuevEMhQgp fb3gK2Zy8OqrEt7tl4dWMiNx78mTAXraCkwMDgtJQ8S8aDXJ//vySwId3N5tPth8vE/Q HKujuUyys4vNPckH6sBL/27TJnwq9OuThtjKSHAKSn1LRiSZmp3jBpZjjlLDTCl6NPar wEWc2gmz8vSFGJQBtLw5Pyoi6i5LIBV4BLHBQnpCugzGFZhK2ZgFFEJyKRfjm71BmRPF ef2w== X-Gm-Message-State: APjAAAWa2G7slSxBpxIHBsLUAI7/UWz90y5vQP+3arHwhHrfU9MOv50a QvhRIILxVWsiVkOBdY3DCnczBiNOWYT0zMQ= X-Google-Smtp-Source: APXvYqylZQVLkOpfER8t/yZZCmSULmwiBhOYyCnd/zwW3zgp94dvh+qSpoiDQrfXfG3g6tHz9kd3kPAHqWauttk= X-Received: by 2002:a63:d108:: with SMTP id k8mr6468524pgg.434.1575678271932; Fri, 06 Dec 2019 16:24:31 -0800 (PST) Date: Fri, 6 Dec 2019 16:24:22 -0800 In-Reply-To: <20191207002424.201796-1-saravanak@google.com> Message-Id: <20191207002424.201796-2-saravanak@google.com> Mime-Version: 1.0 References: <20191207002424.201796-1-saravanak@google.com> X-Mailer: git-send-email 2.24.0.393.g34dc348eaf-goog Subject: [PATCH v6 1/3] dt-bindings: opp: Introduce opp-peak-kBps and opp-avg-kBps bindings From: Saravana Kannan To: Rob Herring , Mark Rutland , Viresh Kumar , Nishanth Menon , Stephen Boyd , "Rafael J. Wysocki" Cc: Saravana Kannan , Georgi Djakov , vincent.guittot@linaro.org, seansw@qti.qualcomm.com, daidavid1@codeaurora.org, adharmap@codeaurora.org, Rajendra Nayak , sibis@codeaurora.org, bjorn.andersson@linaro.org, evgreen@chromium.org, kernel-team@android.com, linux-pm@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, Rob Herring Sender: linux-pm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org Interconnects often quantify their performance points in terms of bandwidth. So, add opp-peak-kBps (required) and opp-avg-kBps (optional) to allow specifying Bandwidth OPP tables in DT. opp-peak-kBps is a required property that replaces opp-hz for Bandwidth OPP tables. opp-avg-kBps is an optional property that can be used in Bandwidth OPP tables. Signed-off-by: Saravana Kannan Reviewed-by: Rob Herring --- Documentation/devicetree/bindings/opp/opp.txt | 15 ++++++++++++--- .../devicetree/bindings/property-units.txt | 4 ++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/opp/opp.txt b/Documentation/devicetree/bindings/opp/opp.txt index 68592271461f..dbad8eb6c746 100644 --- a/Documentation/devicetree/bindings/opp/opp.txt +++ b/Documentation/devicetree/bindings/opp/opp.txt @@ -83,9 +83,14 @@ properties. Required properties: - opp-hz: Frequency in Hz, expressed as a 64-bit big-endian integer. This is a - required property for all device nodes but devices like power domains. The - power domain nodes must have another (implementation dependent) property which - uniquely identifies the OPP nodes. + required property for all device nodes except for devices like power domains + or bandwidth opp tables. The power domain nodes must have another + (implementation dependent) property which uniquely identifies the OPP nodes. + The interconnect opps are required to have the opp-peak-kBps property. + +- opp-peak-kBps: Peak bandwidth in kilobytes per second, expressed as a 32-bit + big-endian integer. This is a required property for all devices that don't + have opp-hz. For example, bandwidth OPP tables for interconnect paths. Optional properties: - opp-microvolt: voltage in micro Volts. @@ -132,6 +137,10 @@ Optional properties: - opp-level: A value representing the performance level of the device, expressed as a 32-bit integer. +- opp-avg-kBps: Average bandwidth in kilobytes per second, expressed as a + 32-bit big-endian integer. This property is only meaningful in OPP tables + where opp-peak-kBps is present. + - clock-latency-ns: Specifies the maximum possible transition latency (in nanoseconds) for switching to this OPP from any other OPP. diff --git a/Documentation/devicetree/bindings/property-units.txt b/Documentation/devicetree/bindings/property-units.txt index e9b8360b3288..c80a110c1e26 100644 --- a/Documentation/devicetree/bindings/property-units.txt +++ b/Documentation/devicetree/bindings/property-units.txt @@ -41,3 +41,7 @@ Temperature Pressure ---------------------------------------- -kpascal : kilopascal + +Throughput +---------------------------------------- +-kBps : kilobytes per second From patchwork Sat Dec 7 00:24:23 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Saravana Kannan X-Patchwork-Id: 11277203 X-Patchwork-Delegate: viresh.linux@gmail.com Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 460B917EF for ; Sat, 7 Dec 2019 00:24:40 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 24908205ED for ; Sat, 7 Dec 2019 00:24:40 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="fBPot67V" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726484AbfLGAYg (ORCPT ); Fri, 6 Dec 2019 19:24:36 -0500 Received: from mail-pf1-f201.google.com ([209.85.210.201]:47656 "EHLO mail-pf1-f201.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726427AbfLGAYg (ORCPT ); Fri, 6 Dec 2019 19:24:36 -0500 Received: by mail-pf1-f201.google.com with SMTP id e62so4959835pfh.14 for ; Fri, 06 Dec 2019 16:24:35 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=SV8i18v4GwCQzlFHpNagXzdfAXCmjcaelWYEg2MgtX0=; b=fBPot67V4Sr6ihA96kRmN3gOwO52BtcVhar2RPHsFQS1Iiix4vcedwd8b53ksqiMyj 2yeNsp3pTQ0wtdRfc7OV1ZV0RHmRZTjG9u5LpgPo0rgX+WLKgqQTUAFbRjd9xvywQuPZ rj3S1zW3LQOTWjPOXQvJn3kKK2Tv8u72p41G5YAB/+ohzNAimBBuw0nI+KSgUuEooecc v3v4cL6tGvESh9/4w5buCfd/UubqjqBSYGv884Fd+79zTI44Od3KCsaFl+qfTyQiZv/l ZKuzL00hfvepPD/5MkKkp0wcYY7uTz5Pbretw0oiyZKgjVkkX/bFhTbc58XAY9SPes8N ES+g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=SV8i18v4GwCQzlFHpNagXzdfAXCmjcaelWYEg2MgtX0=; b=YGd5Mh1QlNqhDteB/LkoL5EFR4G8dicCvBKETIndYB+2z13vu4PgAcGhhiZpClVm0p hqMXEdI6hKvYhDrhhnFh6D45soOjzziAbGRQsPaMOt2QKMZMdKW5Pfds9oUBBjN0XX2W WFF/LeTzZMQ06a53Reaxk/JgSQkUO/ZF4PiC7yvzVTo0VJrtXNdSXfH1r7HZzT7QBzsP gpgS6Bn2+5PdVnlBsm/c/7OWV4L/z7x+6M/qTL/KVx5EsmJkW3/asQ2gEDR7avMyROg3 CwUykKagNTUCC3ylrF3S7uzhWH7enQSVnhjR2FE+YSq/UCxC77M5VKByAvcG8CBr6K7F nrlA== X-Gm-Message-State: APjAAAUblY1QK87FKXm37L8w0PiwibPte5MRwHHKaZ/V0S5vWhOyPGy3 aaoSUVe341TWXfIfUAYlBOtHBU+2cCrrU1U= X-Google-Smtp-Source: APXvYqw9xkOLUzACH5NGXsGojYyQ6wPkEr/kwbjNXJQocK4PvMVxIEC1zoQq+nZxKhCAeRcPadlGWu/Toa1PCLE= X-Received: by 2002:a63:5206:: with SMTP id g6mr6727247pgb.49.1575678275167; Fri, 06 Dec 2019 16:24:35 -0800 (PST) Date: Fri, 6 Dec 2019 16:24:23 -0800 In-Reply-To: <20191207002424.201796-1-saravanak@google.com> Message-Id: <20191207002424.201796-3-saravanak@google.com> Mime-Version: 1.0 References: <20191207002424.201796-1-saravanak@google.com> X-Mailer: git-send-email 2.24.0.393.g34dc348eaf-goog Subject: [PATCH v6 2/3] OPP: Add support for bandwidth OPP tables From: Saravana Kannan To: Rob Herring , Mark Rutland , Viresh Kumar , Nishanth Menon , Stephen Boyd , "Rafael J. Wysocki" Cc: Saravana Kannan , Georgi Djakov , vincent.guittot@linaro.org, seansw@qti.qualcomm.com, daidavid1@codeaurora.org, adharmap@codeaurora.org, Rajendra Nayak , sibis@codeaurora.org, bjorn.andersson@linaro.org, evgreen@chromium.org, kernel-team@android.com, linux-pm@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Sender: linux-pm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org Not all devices quantify their performance points in terms of frequency. Devices like interconnects quantify their performance points in terms of bandwidth. We need a way to represent these bandwidth levels in OPP. So, add support for parsing bandwidth OPPs from DT. Signed-off-by: Saravana Kannan --- drivers/opp/core.c | 15 +++++++++-- drivers/opp/of.c | 63 ++++++++++++++++++++++++++++++++-------------- drivers/opp/opp.h | 5 ++++ 3 files changed, 62 insertions(+), 21 deletions(-) diff --git a/drivers/opp/core.c b/drivers/opp/core.c index be7a7d332332..c79bbfac7289 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -1282,11 +1282,21 @@ static bool _opp_supported_by_regulators(struct dev_pm_opp *opp, return true; } +int opp_compare_key(struct dev_pm_opp *opp1, struct dev_pm_opp *opp2) +{ + if (opp1->rate != opp2->rate) + return opp1->rate < opp2->rate ? -1 : 1; + if (opp1->peak_bw != opp2->peak_bw) + return opp1->peak_bw < opp2->peak_bw ? -1 : 1; + return 0; +} + static int _opp_is_duplicate(struct device *dev, struct dev_pm_opp *new_opp, struct opp_table *opp_table, struct list_head **head) { struct dev_pm_opp *opp; + int opp_cmp; /* * Insert new OPP in order of increasing frequency and discard if @@ -1297,12 +1307,13 @@ static int _opp_is_duplicate(struct device *dev, struct dev_pm_opp *new_opp, * loop. */ list_for_each_entry(opp, &opp_table->opp_list, node) { - if (new_opp->rate > opp->rate) { + opp_cmp = opp_compare_key(new_opp, opp); + if (opp_cmp > 0) { *head = &opp->node; continue; } - if (new_opp->rate < opp->rate) + if (opp_cmp < 0) return 0; /* Duplicate OPPs */ diff --git a/drivers/opp/of.c b/drivers/opp/of.c index 1cbb58240b80..b565da5a2b1f 100644 --- a/drivers/opp/of.c +++ b/drivers/opp/of.c @@ -521,6 +521,44 @@ void dev_pm_opp_of_remove_table(struct device *dev) } EXPORT_SYMBOL_GPL(dev_pm_opp_of_remove_table); +static int _read_opp_key(struct dev_pm_opp *new_opp, struct device_node *np, + bool *rate_not_available) +{ + int ret; + u64 rate; + u32 bw; + + ret = of_property_read_u64(np, "opp-hz", &rate); + if (!ret) { + /* + * Rate is defined as an unsigned long in clk API, and so + * casting explicitly to its type. Must be fixed once rate is 64 + * bit guaranteed in clk API. + */ + new_opp->rate = (unsigned long)rate; + goto out; + } + + ret = of_property_read_u32(np, "opp-peak-kBps", &bw); + if (!ret) { + new_opp->peak_bw = bw; + + if (!of_property_read_u32(np, "opp-avg-kBps", &bw)) + new_opp->avg_bw = bw; + } + +out: + *rate_not_available = !!ret; + /* + * If ret is 0 at this point, we have already found a key. If we + * haven't found a key yet, then ret already has an error value. In + * either case, we don't need to update ret. + */ + of_property_read_u32(np, "opp-level", &new_opp->level); + + return ret; +} + /** * _opp_add_static_v2() - Allocate static OPPs (As per 'v2' DT bindings) * @opp_table: OPP table @@ -558,26 +596,12 @@ static struct dev_pm_opp *_opp_add_static_v2(struct opp_table *opp_table, if (!new_opp) return ERR_PTR(-ENOMEM); - ret = of_property_read_u64(np, "opp-hz", &rate); - if (ret < 0) { - /* "opp-hz" is optional for devices like power domains. */ - if (!opp_table->is_genpd) { - dev_err(dev, "%s: opp-hz not found\n", __func__); - goto free_opp; - } - - rate_not_available = true; - } else { - /* - * Rate is defined as an unsigned long in clk API, and so - * casting explicitly to its type. Must be fixed once rate is 64 - * bit guaranteed in clk API. - */ - new_opp->rate = (unsigned long)rate; + ret = _read_opp_key(new_opp, np, &rate_not_available); + if (ret) { + dev_err(dev, "%s: opp key field not found\n", __func__); + goto free_opp; } - of_property_read_u32(np, "opp-level", &new_opp->level); - /* Check if the OPP supports hardware's hierarchy of versions or not */ if (!_opp_is_supported(dev, opp_table, np)) { dev_dbg(dev, "OPP not supported by hardware: %llu\n", rate); @@ -616,7 +640,8 @@ static struct dev_pm_opp *_opp_add_static_v2(struct opp_table *opp_table, if (of_property_read_bool(np, "opp-suspend")) { if (opp_table->suspend_opp) { /* Pick the OPP with higher rate as suspend OPP */ - if (new_opp->rate > opp_table->suspend_opp->rate) { + if (opp_compare_key(new_opp, + opp_table->suspend_opp) > 1) { opp_table->suspend_opp->suspend = false; new_opp->suspend = true; opp_table->suspend_opp = new_opp; diff --git a/drivers/opp/opp.h b/drivers/opp/opp.h index 01a500e2c40a..0def3154d07b 100644 --- a/drivers/opp/opp.h +++ b/drivers/opp/opp.h @@ -57,6 +57,8 @@ extern struct list_head opp_tables; * @suspend: true if suspend OPP * @pstate: Device's power domain's performance state. * @rate: Frequency in hertz + * @peak_bw: Peak bandwidth in kilobytes per second + * @avg_bw: Average bandwidth in kilobytes per second * @level: Performance level * @supplies: Power supplies voltage/current values * @clock_latency_ns: Latency (in nanoseconds) of switching to this OPP's @@ -78,6 +80,8 @@ struct dev_pm_opp { bool suspend; unsigned int pstate; unsigned long rate; + unsigned int peak_bw; + unsigned int avg_bw; unsigned int level; struct dev_pm_opp_supply *supplies; @@ -213,6 +217,7 @@ struct opp_device *_add_opp_dev(const struct device *dev, struct opp_table *opp_ void _dev_pm_opp_find_and_remove_table(struct device *dev); struct dev_pm_opp *_opp_allocate(struct opp_table *opp_table); void _opp_free(struct dev_pm_opp *opp); +int opp_compare_key(struct dev_pm_opp *opp1, struct dev_pm_opp *opp2); int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, struct opp_table *opp_table, bool rate_not_available); int _opp_add_v1(struct opp_table *opp_table, struct device *dev, unsigned long freq, long u_volt, bool dynamic); void _dev_pm_opp_cpumask_remove_table(const struct cpumask *cpumask, int last_cpu); From patchwork Sat Dec 7 00:24:24 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Saravana Kannan X-Patchwork-Id: 11277205 X-Patchwork-Delegate: viresh.linux@gmail.com Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id E61F0159A for ; Sat, 7 Dec 2019 00:24:42 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id B0DE021835 for ; Sat, 7 Dec 2019 00:24:42 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="gM6r1m2J" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726534AbfLGAYl (ORCPT ); Fri, 6 Dec 2019 19:24:41 -0500 Received: from mail-pf1-f202.google.com ([209.85.210.202]:32966 "EHLO mail-pf1-f202.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726483AbfLGAYj (ORCPT ); Fri, 6 Dec 2019 19:24:39 -0500 Received: by mail-pf1-f202.google.com with SMTP id c72so3779643pfc.0 for ; Fri, 06 Dec 2019 16:24:38 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=7F9wb0NeoAKdzSfGIj2zPf1bY1Y5o8kRPmqEXmlUWks=; b=gM6r1m2Jt3TZbjyVT2Qgup37OFOpREzlnoZ0YQj2opNISflbi53vOFHeIBXHlqf0Ay u54WROsH2hReAtfCxsDWD6QGd0X9z9CZnbyLV1rlXSuSyAhDfha5KAa31B/mxbAjyt8p 5WqaeTs7PnO/auaQJyzC6qLBBpQ/t18Cyh9/9XmiEof3j66DhBzIxzLfaPiBnxOCJWkf KypK6AlNgRUIW/4SYNh+9X61dZBrLSILvjlmXgv03FVfG13hfegVR7oaG9sULBT7FoU8 knoatL0fNlaWIMEGiDkt/oQugWlJC3U0rHrp5s7OS9dnOd55MqpDnWq7rcF3vO1KatPo 9Xjw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=7F9wb0NeoAKdzSfGIj2zPf1bY1Y5o8kRPmqEXmlUWks=; b=f1Az9/j/chPQPYlhluEZxvlxrAePaGrNUXVmIHASlxxY9mCMjhKhkFcl4HbcPpKORb W7CmaRWr/GHCbQYZpz/Cy3eE029681wndykQC2hd1vXLsudQ6wWN4Jd93Cyndkha+a6G StXwKN7e16OGjHlj8z/NpUqhPVEyUVwzIfO29gDtIKJDenh706ZVEAQdN8hhFIUHfxzx YHIKBpQpZoVookVIcUOSXfOAtzczrSP2cEhhda4eVFD8ovctjhJyr9KfvYzTV3jh07RT t7dWJkSRDjsi3GawMpwajRLKd9YRow4GriZetHgE0d4SU0KQO5/jZLed4BvQfrT4A7sQ 18Bg== X-Gm-Message-State: APjAAAVAw9oMC72NgsFrs/ow3Jt8cpAANWwrxgff+hEh3aLLmaBgZxVr 9NFhkmHCIUfP6L8yVz5P1v+1GBd4PClF+hA= X-Google-Smtp-Source: APXvYqzcTrFr9aq9fGrzZa2rf927VYsYaEXCY55XTj+oMy5jDEFCOUsdHu6zkV9aZcXcJ6L8H7bB4A9RmoBUM18= X-Received: by 2002:a63:d551:: with SMTP id v17mr6694357pgi.365.1575678278342; Fri, 06 Dec 2019 16:24:38 -0800 (PST) Date: Fri, 6 Dec 2019 16:24:24 -0800 In-Reply-To: <20191207002424.201796-1-saravanak@google.com> Message-Id: <20191207002424.201796-4-saravanak@google.com> Mime-Version: 1.0 References: <20191207002424.201796-1-saravanak@google.com> X-Mailer: git-send-email 2.24.0.393.g34dc348eaf-goog Subject: [PATCH v6 3/3] OPP: Add helper function for bandwidth OPP tables From: Saravana Kannan To: Rob Herring , Mark Rutland , Viresh Kumar , Nishanth Menon , Stephen Boyd , "Rafael J. Wysocki" Cc: Saravana Kannan , Georgi Djakov , vincent.guittot@linaro.org, seansw@qti.qualcomm.com, daidavid1@codeaurora.org, adharmap@codeaurora.org, Rajendra Nayak , sibis@codeaurora.org, bjorn.andersson@linaro.org, evgreen@chromium.org, kernel-team@android.com, linux-pm@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Sender: linux-pm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org The frequency OPP tables have helper functions to search for entries in the table based on frequency and get the frequency values for a given (or suspend) OPP entry. Add similar helper functions for bandwidth OPP tables to search for entries in the table based on peak bandwidth and to get the peak and average bandwidth for a given (or suspend) OPP entry. Signed-off-by: Saravana Kannan --- drivers/opp/core.c | 301 +++++++++++++++++++++++++++++++++++------ include/linux/pm_opp.h | 43 ++++++ 2 files changed, 305 insertions(+), 39 deletions(-) diff --git a/drivers/opp/core.c b/drivers/opp/core.c index c79bbfac7289..3ff33a08198e 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -127,6 +127,29 @@ unsigned long dev_pm_opp_get_freq(struct dev_pm_opp *opp) } EXPORT_SYMBOL_GPL(dev_pm_opp_get_freq); +/** + * dev_pm_opp_get_bw() - Gets the bandwidth corresponding to an available opp + * @opp: opp for which peak bandwidth has to be returned for + * @avg_bw: Pointer where the corresponding average bandwidth is stored. + * Can be NULL. + * + * Return: Peak bandwidth in kBps corresponding to the opp, else + * return 0 + */ +unsigned long dev_pm_opp_get_bw(struct dev_pm_opp *opp, unsigned long *avg_bw) +{ + if (IS_ERR_OR_NULL(opp) || !opp->available) { + pr_err("%s: Invalid parameters\n", __func__); + return 0; + } + + if (avg_bw) + *avg_bw = opp->avg_bw; + + return opp->peak_bw; +} +EXPORT_SYMBOL_GPL(dev_pm_opp_get_bw); + /** * dev_pm_opp_get_level() - Gets the level corresponding to an available opp * @opp: opp for which level value has to be returned for @@ -299,6 +322,34 @@ unsigned long dev_pm_opp_get_suspend_opp_freq(struct device *dev) } EXPORT_SYMBOL_GPL(dev_pm_opp_get_suspend_opp_freq); +/** + * dev_pm_opp_get_suspend_opp_bw() - Get peak bandwidth of suspend opp in kBps + * @dev: device for which we do this operation + * @avg_bw: Pointer where the corresponding average bandwidth is stored. + * Can be NULL. + * + * Return: This function returns the peak bandwidth of the OPP marked as + * suspend_opp if one is available, else returns 0; + */ +unsigned long dev_pm_opp_get_suspend_opp_bw(struct device *dev, + unsigned long *avg_bw) +{ + struct opp_table *opp_table; + unsigned long peak_bw = 0; + + opp_table = _find_opp_table(dev); + if (IS_ERR(opp_table)) + return 0; + + if (opp_table->suspend_opp && opp_table->suspend_opp->available) + peak_bw = dev_pm_opp_get_bw(opp_table->suspend_opp, avg_bw); + + dev_pm_opp_put_opp_table(opp_table); + + return peak_bw; +} +EXPORT_SYMBOL_GPL(dev_pm_opp_get_suspend_opp_bw); + int _get_opp_count(struct opp_table *opp_table) { struct dev_pm_opp *opp; @@ -343,6 +394,40 @@ int dev_pm_opp_get_opp_count(struct device *dev) } EXPORT_SYMBOL_GPL(dev_pm_opp_get_opp_count); +struct dev_pm_opp *dev_pm_opp_find_opp_exact(struct device *dev, + struct dev_pm_opp *opp_key, + bool available) +{ + struct opp_table *opp_table; + struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE); + + opp_table = _find_opp_table(dev); + if (IS_ERR(opp_table)) { + int r = PTR_ERR(opp_table); + + dev_err(dev, "%s: OPP table not found (%d)\n", __func__, r); + return ERR_PTR(r); + } + + mutex_lock(&opp_table->lock); + + list_for_each_entry(temp_opp, &opp_table->opp_list, node) { + if (temp_opp->available == available && + !opp_compare_key(temp_opp, opp_key)) { + opp = temp_opp; + + /* Increment the reference count of OPP */ + dev_pm_opp_get(opp); + break; + } + } + + mutex_unlock(&opp_table->lock); + dev_pm_opp_put_opp_table(opp_table); + + return opp; +} + /** * dev_pm_opp_find_freq_exact() - search for an exact frequency * @dev: device for which we do this operation @@ -370,36 +455,53 @@ struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev, unsigned long freq, bool available) { - struct opp_table *opp_table; - struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE); - - opp_table = _find_opp_table(dev); - if (IS_ERR(opp_table)) { - int r = PTR_ERR(opp_table); + struct dev_pm_opp opp_key; - dev_err(dev, "%s: OPP table not found (%d)\n", __func__, r); - return ERR_PTR(r); - } + opp_key.rate = freq; + opp_key.peak_bw = 0; + opp_key.level = 0; - mutex_lock(&opp_table->lock); + return dev_pm_opp_find_opp_exact(dev, &opp_key, available); +} +EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_exact); - list_for_each_entry(temp_opp, &opp_table->opp_list, node) { - if (temp_opp->available == available && - temp_opp->rate == freq) { - opp = temp_opp; +/** + * dev_pm_opp_find_peak_bw_exact() - search for an exact peak bandwidth + * @dev: device for which we do this operation + * @peak_bw: peak bandwidth to search for + * @available: true/false - match for available opp + * + * Return: Searches for exact match in the opp table and returns pointer to the + * matching opp if found, else returns ERR_PTR in case of error and should + * be handled using IS_ERR. Error return values can be: + * EINVAL: for bad pointer + * ERANGE: no match found for search + * ENODEV: if device not found in list of registered devices + * + * Note: available is a modifier for the search. if available=true, then the + * match is for exact matching peak bandwidth and is available in the stored + * OPP table. if false, the match is for exact peak bandwidth which is not + * available. + * + * This provides a mechanism to enable an opp which is not available currently + * or the opposite as well. + * + * The callers are required to call dev_pm_opp_put() for the returned OPP after + * use. + */ +struct dev_pm_opp *dev_pm_opp_find_peak_bw_exact(struct device *dev, + unsigned int peak_bw, + bool available) +{ + struct dev_pm_opp opp_key; - /* Increment the reference count of OPP */ - dev_pm_opp_get(opp); - break; - } - } + opp_key.rate = 0; + opp_key.peak_bw = peak_bw; + opp_key.level = 0; - mutex_unlock(&opp_table->lock); - dev_pm_opp_put_opp_table(opp_table); - - return opp; + return dev_pm_opp_find_opp_exact(dev, &opp_key, available); } -EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_exact); +EXPORT_SYMBOL_GPL(dev_pm_opp_find_peak_bw_exact); /** * dev_pm_opp_find_level_exact() - search for an exact level @@ -449,18 +551,17 @@ struct dev_pm_opp *dev_pm_opp_find_level_exact(struct device *dev, } EXPORT_SYMBOL_GPL(dev_pm_opp_find_level_exact); -static noinline struct dev_pm_opp *_find_freq_ceil(struct opp_table *opp_table, - unsigned long *freq) +static struct dev_pm_opp *_find_opp_ceil(struct opp_table *opp_table, + struct dev_pm_opp *opp_key) { struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE); mutex_lock(&opp_table->lock); list_for_each_entry(temp_opp, &opp_table->opp_list, node) { - if (temp_opp->available && temp_opp->rate >= *freq) { + if (temp_opp->available && + opp_compare_key(temp_opp, opp_key) >= 0) { opp = temp_opp; - *freq = opp->rate; - /* Increment the reference count of OPP */ dev_pm_opp_get(opp); break; @@ -472,6 +573,23 @@ static noinline struct dev_pm_opp *_find_freq_ceil(struct opp_table *opp_table, return opp; } +static noinline struct dev_pm_opp *_find_freq_ceil(struct opp_table *opp_table, + unsigned long *freq) +{ + struct dev_pm_opp opp_key, *opp; + + opp_key.rate = *freq; + opp_key.peak_bw = 0; + opp_key.level = 0; + + opp = _find_opp_ceil(opp_table, &opp_key); + + if (!IS_ERR(opp)) + *freq = opp->rate; + + return opp; +} + /** * dev_pm_opp_find_freq_ceil() - Search for an rounded ceil freq * @dev: device for which we do this operation @@ -514,14 +632,14 @@ struct dev_pm_opp *dev_pm_opp_find_freq_ceil(struct device *dev, EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_ceil); /** - * dev_pm_opp_find_freq_floor() - Search for a rounded floor freq + * dev_pm_opp_find_peak_bw_ceil() - Search for an rounded ceil peak bandwidth * @dev: device for which we do this operation - * @freq: Start frequency + * @peak_bw: Start peak bandwidth * - * Search for the matching floor *available* OPP from a starting freq + * Search for the matching ceil *available* OPP from a starting peak bandwidth * for a device. * - * Return: matching *opp and refreshes *freq accordingly, else returns + * Return: matching *opp and refreshes *peak_bw accordingly, else returns * ERR_PTR in case of error and should be handled using IS_ERR. Error return * values can be: * EINVAL: for bad pointer @@ -531,17 +649,43 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_ceil); * The callers are required to call dev_pm_opp_put() for the returned OPP after * use. */ -struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev, - unsigned long *freq) +struct dev_pm_opp *dev_pm_opp_find_peak_bw_ceil(struct device *dev, + unsigned long *peak_bw) { struct opp_table *opp_table; - struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE); + struct dev_pm_opp *opp; + struct dev_pm_opp opp_key; - if (!dev || !freq) { - dev_err(dev, "%s: Invalid argument freq=%p\n", __func__, freq); + if (!dev || !peak_bw) { + dev_err(dev, "%s: Invalid argument peak_bw=%p\n", __func__, + peak_bw); return ERR_PTR(-EINVAL); } + opp_table = _find_opp_table(dev); + if (IS_ERR(opp_table)) + return ERR_CAST(opp_table); + + opp_key.rate = 0; + opp_key.peak_bw = *peak_bw; + opp_key.level = 0; + opp = _find_opp_ceil(opp_table, &opp_key); + + if (!IS_ERR(opp)) + *peak_bw = opp->rate; + + dev_pm_opp_put_opp_table(opp_table); + + return opp; +} +EXPORT_SYMBOL_GPL(dev_pm_opp_find_peak_bw_ceil); + +struct dev_pm_opp *dev_pm_opp_find_opp_floor(struct device *dev, + struct dev_pm_opp *opp_key) +{ + struct opp_table *opp_table; + struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE); + opp_table = _find_opp_table(dev); if (IS_ERR(opp_table)) return ERR_CAST(opp_table); @@ -551,7 +695,7 @@ struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev, list_for_each_entry(temp_opp, &opp_table->opp_list, node) { if (temp_opp->available) { /* go to the next node, before choosing prev */ - if (temp_opp->rate > *freq) + if (opp_compare_key(temp_opp, opp_key) > 0) break; else opp = temp_opp; @@ -564,6 +708,43 @@ struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev, mutex_unlock(&opp_table->lock); dev_pm_opp_put_opp_table(opp_table); + return opp; +} + +/** + * dev_pm_opp_find_freq_floor() - Search for a rounded floor freq + * @dev: device for which we do this operation + * @freq: Start frequency + * + * Search for the matching floor *available* OPP from a starting freq + * for a device. + * + * Return: matching *opp and refreshes *freq accordingly, else returns + * ERR_PTR in case of error and should be handled using IS_ERR. Error return + * values can be: + * EINVAL: for bad pointer + * ERANGE: no match found for search + * ENODEV: if device not found in list of registered devices + * + * The callers are required to call dev_pm_opp_put() for the returned OPP after + * use. + */ +struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev, + unsigned long *freq) +{ + struct dev_pm_opp opp_key, *opp; + + if (!dev || !freq) { + dev_err(dev, "%s: Invalid argument freq=%p\n", __func__, freq); + return ERR_PTR(-EINVAL); + } + + opp_key.rate = *freq; + opp_key.peak_bw = 0; + opp_key.level = 0; + + opp = dev_pm_opp_find_opp_floor(dev, &opp_key); + if (!IS_ERR(opp)) *freq = opp->rate; @@ -571,6 +752,48 @@ struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev, } EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_floor); +/** + * dev_pm_opp_find_peak_bw_floor() - Search for a rounded floor peak bandwidth + * @dev: device for which we do this operation + * @peak_bw: Start peak bandwidth + * + * Search for the matching floor *available* OPP from a starting peak bandwidth + * for a device. + * + * Return: matching *opp and refreshes *peak_bw accordingly, else returns + * ERR_PTR in case of error and should be handled using IS_ERR. Error return + * values can be: + * EINVAL: for bad pointer + * ERANGE: no match found for search + * ENODEV: if device not found in list of registered devices + * + * The callers are required to call dev_pm_opp_put() for the returned OPP after + * use. + */ +struct dev_pm_opp *dev_pm_opp_find_peak_bw_floor(struct device *dev, + unsigned int *peak_bw) +{ + struct dev_pm_opp opp_key, *opp; + + if (!dev || !peak_bw) { + dev_err(dev, "%s: Invalid argument peak_bw=%p\n", __func__, + peak_bw); + return ERR_PTR(-EINVAL); + } + + opp_key.rate = 0; + opp_key.peak_bw = *peak_bw; + opp_key.level = 0; + + opp = dev_pm_opp_find_opp_floor(dev, &opp_key); + + if (!IS_ERR(opp)) + *peak_bw = opp->peak_bw; + + return opp; +} +EXPORT_SYMBOL_GPL(dev_pm_opp_find_peak_bw_floor); + /** * dev_pm_opp_find_freq_ceil_by_volt() - Find OPP with highest frequency for * target voltage. diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h index 747861816f4f..aab1b9c143ac 100644 --- a/include/linux/pm_opp.h +++ b/include/linux/pm_opp.h @@ -83,6 +83,7 @@ void dev_pm_opp_put_opp_table(struct opp_table *opp_table); unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp); unsigned long dev_pm_opp_get_freq(struct dev_pm_opp *opp); +unsigned long dev_pm_opp_get_bw(struct dev_pm_opp *opp, unsigned long *avg_bw); unsigned int dev_pm_opp_get_level(struct dev_pm_opp *opp); @@ -93,20 +94,29 @@ unsigned long dev_pm_opp_get_max_clock_latency(struct device *dev); unsigned long dev_pm_opp_get_max_volt_latency(struct device *dev); unsigned long dev_pm_opp_get_max_transition_latency(struct device *dev); unsigned long dev_pm_opp_get_suspend_opp_freq(struct device *dev); +unsigned long dev_pm_opp_get_suspend_opp_bw(struct device *dev, + unsigned long *avg_bw); struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev, unsigned long freq, bool available); +struct dev_pm_opp *dev_pm_opp_find_peak_bw_exact(struct device *dev, + unsigned int peak_bw, + bool available); struct dev_pm_opp *dev_pm_opp_find_level_exact(struct device *dev, unsigned int level); struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev, unsigned long *freq); +struct dev_pm_opp *dev_pm_opp_find_peak_bw_floor(struct device *dev, + unsigned int *peak_bw); struct dev_pm_opp *dev_pm_opp_find_freq_ceil_by_volt(struct device *dev, unsigned long u_volt); struct dev_pm_opp *dev_pm_opp_find_freq_ceil(struct device *dev, unsigned long *freq); +struct dev_pm_opp *dev_pm_opp_find_peak_bw_ceil(struct device *dev, + unsigned long *peak_bw); void dev_pm_opp_put(struct dev_pm_opp *opp); int dev_pm_opp_add(struct device *dev, unsigned long freq, @@ -165,6 +175,11 @@ static inline unsigned long dev_pm_opp_get_freq(struct dev_pm_opp *opp) { return 0; } +static inline unsigned long dev_pm_opp_get_bw(struct dev_pm_opp *opp, + unsigned long *avg_bw) +{ + return 0; +} static inline unsigned int dev_pm_opp_get_level(struct dev_pm_opp *opp) { @@ -201,12 +216,26 @@ static inline unsigned long dev_pm_opp_get_suspend_opp_freq(struct device *dev) return 0; } +static inline unsigned long dev_pm_opp_get_suspend_opp_bw(struct device *dev, + unsigned long *avg_bw) +{ + return 0; +} + static inline struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev, unsigned long freq, bool available) { return ERR_PTR(-ENOTSUPP); } +static inline struct dev_pm_opp *dev_pm_opp_find_peak_bw_exact( + struct device *dev, + unsigned int peak_bw, + bool available) +{ + return ERR_PTR(-ENOTSUPP); +} + static inline struct dev_pm_opp *dev_pm_opp_find_level_exact(struct device *dev, unsigned int level) { @@ -219,6 +248,13 @@ static inline struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev, return ERR_PTR(-ENOTSUPP); } +static inline struct dev_pm_opp *dev_pm_opp_find_peak_bw_floor( + struct device *dev, + unsigned int *peak_bw) +{ + return ERR_PTR(-ENOTSUPP); +} + static inline struct dev_pm_opp *dev_pm_opp_find_freq_ceil_by_volt(struct device *dev, unsigned long u_volt) { @@ -231,6 +267,13 @@ static inline struct dev_pm_opp *dev_pm_opp_find_freq_ceil(struct device *dev, return ERR_PTR(-ENOTSUPP); } +static inline struct dev_pm_opp *dev_pm_opp_find_peak_bw_ceil( + struct device *dev, + unsigned long *peak_bw) +{ + return ERR_PTR(-ENOTSUPP); +} + static inline void dev_pm_opp_put(struct dev_pm_opp *opp) {} static inline int dev_pm_opp_add(struct device *dev, unsigned long freq,