From patchwork Fri Sep 11 12:02:03 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 53455 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 22725215BF for ; Fri, 11 Sep 2015 12:03:34 +0000 (UTC) Received: by lbcao8 with SMTP id ao8sf23633982lbc.1 for ; Fri, 11 Sep 2015 05:03:33 -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=Jz8FLlKceUs5ZTcKZqfFDr4qlX/zGXI8OLKlfIYn0Bw=; b=akM5X7u46h6t/GPF76meZx/yvwN1KwrQGiNDM7xTg/G3MtUXgFr9wfUlNKt94tb3sT B/Wmn91V8qnMLiUCYcVHB3ajQAAqx1akyBdLL+IfogxPh+NmR8w66C9X2Hnwj0RcsTOy oqhbxt2IH318wiQKnMVLcYYIj94xl5j46FZCK8Q6Dr9ooepG02+n/bDWSdnYrbI+v5cg cWsTfmi2q8OIk8oVsKs66vpHspjdtEe5zoZxhYAPOtThklgYz5ptCjf+0IlwpvomO5mo dG7Y8TVZzTb05166O8XcMwInIOe+pqSU68l5OEaIm7tnfF7w+4yY57OWNO44Kd3ipKSL tFhQ== X-Gm-Message-State: ALoCoQngO0vE4DZiz2L6xroL0tccppySCRzanplQTKSCvCihkL0QywShMN27JFzH5aKQ9LfE3Tev X-Received: by 10.112.170.67 with SMTP id ak3mr11155874lbc.6.1441973013127; Fri, 11 Sep 2015 05:03:33 -0700 (PDT) MIME-Version: 1.0 X-BeenThere: patchwork-forward@linaro.org Received: by 10.152.36.70 with SMTP id o6ls350324laj.2.gmail; Fri, 11 Sep 2015 05:03:32 -0700 (PDT) X-Received: by 10.152.2.227 with SMTP id 3mr2244618lax.54.1441973012937; Fri, 11 Sep 2015 05:03:32 -0700 (PDT) Received: from mail-lb0-f178.google.com (mail-lb0-f178.google.com. [209.85.217.178]) by mx.google.com with ESMTPS id z5si10095lae.138.2015.09.11.05.03.32 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Fri, 11 Sep 2015 05:03:32 -0700 (PDT) Received-SPF: pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.217.178 as permitted sender) client-ip=209.85.217.178; Received: by lbpo4 with SMTP id o4so38448159lbp.2 for ; Fri, 11 Sep 2015 05:03:32 -0700 (PDT) X-Received: by 10.152.22.133 with SMTP id d5mr41379821laf.112.1441973012772; Fri, 11 Sep 2015 05:03:32 -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.59.35 with SMTP id w3csp1545367lbq; Fri, 11 Sep 2015 05:03:31 -0700 (PDT) X-Received: by 10.50.61.172 with SMTP id q12mr15323907igr.43.1441973011390; Fri, 11 Sep 2015 05:03:31 -0700 (PDT) Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id hj5si131222igb.64.2015.09.11.05.03.30; Fri, 11 Sep 2015 05:03:31 -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 S1752990AbbIKMD2 (ORCPT + 28 others); Fri, 11 Sep 2015 08:03:28 -0400 Received: from mail-pa0-f52.google.com ([209.85.220.52]:33388 "EHLO mail-pa0-f52.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752083AbbIKMDZ (ORCPT ); Fri, 11 Sep 2015 08:03:25 -0400 Received: by pacex6 with SMTP id ex6so73880049pac.0 for ; Fri, 11 Sep 2015 05:03:24 -0700 (PDT) X-Received: by 10.66.165.5 with SMTP id yu5mr37852257pab.109.1441973004680; Fri, 11 Sep 2015 05:03:24 -0700 (PDT) Received: from localhost ([122.171.186.190]) by smtp.gmail.com with ESMTPSA id j16sm70123pbq.23.2015.09.11.05.03.23 (version=TLSv1.2 cipher=RC4-SHA bits=128/128); Fri, 11 Sep 2015 05:03:24 -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, rob.herring@linaro.org, lee.jones@linaro.org, Viresh Kumar , Bartlomiej Zolnierkiewicz , Dmitry Torokhov , Greg Kroah-Hartman , Len Brown , linux-kernel@vger.kernel.org (open list), Pavel Machek Subject: [PATCH 07/16] PM / OPP: Add multiple regulators support Date: Fri, 11 Sep 2015 17:32:03 +0530 Message-Id: <5ffbcd0ecb41500851dc5eb3a5393b21ebea7b76.1441972771.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.178 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: , This adds support to parse multiple regulators or power-supplies in OPP core. This doesn't use those values yet. Signed-off-by: Viresh Kumar --- drivers/base/power/opp/core.c | 197 +++++++++++++++++++++++++++++++-------- drivers/base/power/opp/debugfs.c | 53 +++++++---- drivers/base/power/opp/opp.h | 4 + 3 files changed, 201 insertions(+), 53 deletions(-) diff --git a/drivers/base/power/opp/core.c b/drivers/base/power/opp/core.c index 14e5fa10be2d..d6e945ec6467 100644 --- a/drivers/base/power/opp/core.c +++ b/drivers/base/power/opp/core.c @@ -502,21 +502,27 @@ static struct device_opp *_add_device_opp(struct device *dev, int supply_count) { struct device_opp *dev_opp; struct device_list_opp *list_dev; + size_t size; /* Check for existing list for 'dev' first */ dev_opp = _find_device_opp(dev); if (!IS_ERR(dev_opp)) return dev_opp; + /* Allocate size for supply-names with dev_opp */ + size = sizeof(*dev_opp) + supply_count * sizeof(*dev_opp->supply_names); + /* * Allocate a new device OPP table. In the infrequent case where a new * device is needed to be added, we pay this penalty. */ - dev_opp = kzalloc(sizeof(*dev_opp), GFP_KERNEL); + dev_opp = kzalloc(size, GFP_KERNEL); if (!dev_opp) return NULL; dev_opp->supply_count = supply_count; + dev_opp->supply_names = (const char **)(dev_opp + 1); + INIT_LIST_HEAD(&dev_opp->dev_list); list_dev = _add_list_dev(dev, dev_opp); @@ -525,6 +531,13 @@ static struct device_opp *_add_device_opp(struct device *dev, int supply_count) return NULL; } + /* + * Initialize supply-name as dev-name for single supplies. This is + * required for the debugfs code. + */ + if (supply_count == 1) + *dev_opp->supply_names = dev_name(dev); + srcu_init_notifier_head(&dev_opp->srcu_head); INIT_LIST_HEAD(&dev_opp->opp_list); @@ -682,6 +695,23 @@ _allocate_opp(struct device *dev, struct device_opp **dev_opp, int supply_count) return opp; } +static bool _supplies_match(struct device_opp *dev_opp, + struct dev_pm_opp *old_opp, + struct dev_pm_opp *new_opp) +{ + struct opp_supply *old = old_opp->supplies; + struct opp_supply *new = new_opp->supplies; + int i; + + for (i = 0; i < dev_opp->supply_count; i++) + if (old[i].u_volt != new[i].u_volt || + old[i].u_volt_min != new[i].u_volt_min || + old[i].u_volt_max != new[i].u_volt_max) + return false; + + return true; +} + static int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, struct device_opp *dev_opp) { @@ -712,9 +742,8 @@ static int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, opp->available, new_opp->rate, new_opp->supplies[0].u_volt, new_opp->available); - return opp->available && - opp->supplies[0].u_volt == new_opp->supplies[0].u_volt ? - 0 : -EEXIST; + return opp->available && _supplies_match(dev_opp, opp, new_opp) + ? 0 : -EEXIST; } new_opp->dev_opp = dev_opp; @@ -797,41 +826,99 @@ static int _opp_add_v1(struct device *dev, unsigned long freq, long u_volt, return ret; } -/* TODO: Support multiple regulators */ -static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev) +/* returns the number of entries used from microvolt */ +static void opp_parse_single_supply(struct opp_supply *supply, bool triplet, + u32 *microvolt, u32 *microamp) { - struct opp_supply *supply = &opp->supplies[0]; - u32 microvolt[3] = {0}; - u32 val; - int count, ret; + if (triplet) { + supply->u_volt = microvolt[0]; + supply->u_volt_min = microvolt[1]; + supply->u_volt_max = microvolt[2]; + } else { + supply->u_volt = microvolt[0]; + supply->u_volt_min = microvolt[0]; + supply->u_volt_max = microvolt[0]; + } + + supply->u_amp = *microamp; +} + +static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev, + int supply_count, bool triplets) +{ + struct opp_supply *supply = opp->supplies; + int i, vcount, icount, ret, step; + u32 *microvolt, *microamp; - count = of_property_count_u32_elems(opp->np, "opp-microvolt"); - if (!count) + vcount = of_property_count_u32_elems(opp->np, "opp-microvolt"); + if (!vcount) return 0; - /* There can be one or three elements here */ - if (count != 1 && count != 3) { - dev_err(dev, "%s: Invalid number of elements in opp-microvolt property (%d)\n", - __func__, count); - return -EINVAL; - } + icount = of_property_count_u32_elems(opp->np, "opp-microamp"); + if (!icount) + return 0; + + /* Allocate memory for volt/amp */ + microvolt = kcalloc(vcount, sizeof(*microvolt), GFP_KERNEL); + microamp = kcalloc(icount, sizeof(*microamp), GFP_KERNEL); + if (!microvolt || !microamp) + return -ENOMEM; ret = of_property_read_u32_array(opp->np, "opp-microvolt", microvolt, - count); + vcount); if (ret) { dev_err(dev, "%s: error parsing opp-microvolt: %d\n", __func__, ret); - return -EINVAL; + ret = -EINVAL; + goto free_microvolt; } - supply->u_volt = microvolt[0]; - supply->u_volt_min = microvolt[1]; - supply->u_volt_max = microvolt[2]; + ret = of_property_read_u32_array(opp->np, "opp-microamp", microamp, + icount); + if (ret) { + dev_err(dev, "%s: error parsing opp-microamp: %d\n", __func__, + ret); + ret = -EINVAL; + goto free_microvolt; + } - if (!of_property_read_u32(opp->np, "opp-microamp", &val)) - supply->u_amp = val; + /* + * For single supply, "opp-microvolt-triplets" is not mandatory and we + * need to find it ourselves. + */ + if (supply_count == 1) { + if (vcount == 1) { + triplets = false; + } else if (vcount == 3) { + triplets = true; + } else { + dev_err(dev, "%s: opp-microvolt property should have 1 or 3 elements (%d)\n", + __func__, vcount); + ret = -EINVAL; + goto free_microvolt; + } + } - return 0; + step = triplets ? 3 : 1; + + /* microvolt sanity check */ + if ((vcount != supply_count * step) || (icount != supply_count)) { + dev_err(dev, "%s: Invalid number of elements in opp-microvolt/amp property (v=%d i=%d c=%d t=%d)\n", + __func__, vcount, icount, supply_count * step, + triplets); + ret = -EINVAL; + goto free_microvolt; + } + + for (i = 0; i < supply_count; i++) + opp_parse_single_supply(supply + i, triplets, + microvolt + step * i, microamp + i); + +free_microvolt: + kfree(microamp); + kfree(microvolt); + + return ret; } /** @@ -839,6 +926,8 @@ static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev) * @dev: device for which we do this operation * @np: device node * @supply_count: Number of supplies available for each OPP + * @triplets: If true, microvolt property should be in form , + * else . * * This function adds an opp definition to the opp list and returns status. The * opp can be controlled using dev_pm_opp_enable/disable functions and may be @@ -859,7 +948,7 @@ static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev) * -EINVAL Failed parsing the OPP node */ static int _opp_add_static_v2(struct device *dev, struct device_node *np, - int supply_count) + int supply_count, bool triplets) { struct device_opp *dev_opp; struct dev_pm_opp *new_opp; @@ -898,7 +987,7 @@ static int _opp_add_static_v2(struct device *dev, struct device_node *np, if (!of_property_read_u32(np, "clock-latency-ns", &val)) new_opp->clock_latency_ns = val; - ret = opp_parse_supplies(new_opp, dev); + ret = opp_parse_supplies(new_opp, dev, supply_count, triplets); if (ret) goto free_opp; @@ -1165,6 +1254,10 @@ void dev_pm_opp_of_remove_table(struct device *dev) /* Find if dev_opp manages a single device */ if (list_is_singular(&dev_opp->dev_list)) { + /* Free dev_opp if no OPPs are added yet */ + if (list_empty(&dev_opp->opp_list)) + _remove_device_opp(dev_opp); + /* Free static OPPs */ list_for_each_entry_safe(opp, tmp, &dev_opp->opp_list, node) { if (!opp->dynamic) @@ -1197,7 +1290,9 @@ static int _of_add_opp_table_v2(struct device *dev, struct device_node *opp_np) { struct device_node *np; struct device_opp *dev_opp; - int ret = 0, count = 0; + const char **name; + int ret = 0, count, supply_count, string_count; + bool triplets; dev_opp = _managed_opp(opp_np); if (dev_opp) { @@ -1207,12 +1302,44 @@ static int _of_add_opp_table_v2(struct device *dev, struct device_node *opp_np) return ret; } + triplets = of_property_read_bool(opp_np, "opp-microvolt-triplets"); + string_count = of_property_count_strings(opp_np, "supply-names"); + + /* Fallback to single power-supply if multiple aren't present */ + if (string_count <= 0) { + supply_count = 1; + string_count = 0; + } else { + supply_count = string_count; + } + + /* + * We need to add dev_opp before adding any OPPs, so that supply_names + * are valid while the OPPs are getting added. + */ + dev_opp = _add_device_opp(dev, supply_count); + if (!dev_opp) + return -ENOMEM; + + /* Parse supply names */ + name = dev_opp->supply_names; + for (count = 0; count < string_count; count++, name++) { + /* Parse supply names */ + ret = of_property_read_string_index(opp_np, "supply-names", + count, name); + if (ret) { + dev_err(dev, "%s: read supply names (%s) error (%d)\n", + __func__, opp_np->name, ret); + goto free_table; + } + } + /* We have opp-list node now, iterate over it and add OPPs */ + count = 0; for_each_available_child_of_node(opp_np, np) { count++; - /* Todo: Add support for multiple supplies */ - ret = _opp_add_static_v2(dev, np, 1); + ret = _opp_add_static_v2(dev, np, supply_count, triplets); if (ret) { dev_err(dev, "%s: Failed to add OPP, %d\n", __func__, ret); @@ -1221,12 +1348,8 @@ static int _of_add_opp_table_v2(struct device *dev, struct device_node *opp_np) } /* There should be one of more OPP defined */ - if (WARN_ON(!count)) - return -ENOENT; - - dev_opp = _find_device_opp(dev); - if (WARN_ON(IS_ERR(dev_opp))) { - ret = PTR_ERR(dev_opp); + if (WARN_ON(!count)) { + ret = -ENOENT; goto free_table; } diff --git a/drivers/base/power/opp/debugfs.c b/drivers/base/power/opp/debugfs.c index e6ba29c04513..de2083d69297 100644 --- a/drivers/base/power/opp/debugfs.c +++ b/drivers/base/power/opp/debugfs.c @@ -31,12 +31,44 @@ void opp_debug_remove_one(struct dev_pm_opp *opp) debugfs_remove_recursive(opp->dentry); } +int opp_debug_create_supplies(struct dev_pm_opp *opp, + struct device_opp *dev_opp, struct dentry *dentry) +{ + struct opp_supply *supply = opp->supplies; + char name[NAME_MAX]; + const char **supply_name = dev_opp->supply_names; + int i; + + for (i = 0; i < dev_opp->supply_count; i++, supply_name++) { + snprintf(name, sizeof(name), "%s_u_volt_target", *supply_name); + if (!debugfs_create_u32(name, S_IRUGO, dentry, + (u32 *)&supply->u_volt)) + return -ENOMEM; + + snprintf(name, sizeof(name), "%s_u_volt_min", *supply_name); + if (!debugfs_create_u32(name, S_IRUGO, dentry, + (u32 *)&supply->u_volt_min)) + return -ENOMEM; + + snprintf(name, sizeof(name), "%s_u_volt_max", *supply_name); + if (!debugfs_create_u32(name, S_IRUGO, dentry, + (u32 *)&supply->u_volt_max)) + return -ENOMEM; + } + + if (!debugfs_create_u32("u_amp", S_IRUGO, dentry, + (u32 *)&supply->u_amp)) + return -ENOMEM; + + return 0; +} + int opp_debug_create_one(struct dev_pm_opp *opp, struct device_opp *dev_opp) { struct dentry *pdentry = dev_opp->dentry; - struct opp_supply *supply = &opp->supplies[0]; struct dentry *d; char name[15]; + int ret; /* Rate is unique to each OPP, use it to give opp-name */ sprintf(name, "opp:%lu", opp->rate); @@ -59,25 +91,14 @@ int opp_debug_create_one(struct dev_pm_opp *opp, struct device_opp *dev_opp) if (!debugfs_create_u32("rate_hz", S_IRUGO, d, (u32 *)&opp->rate)) return -ENOMEM; - if (!debugfs_create_u32("u_volt_target", S_IRUGO, d, - (u32 *)&supply->u_volt)) - return -ENOMEM; - - if (!debugfs_create_u32("u_volt_min", S_IRUGO, d, - (u32 *)&supply->u_volt_min)) - return -ENOMEM; - - if (!debugfs_create_u32("u_volt_max", S_IRUGO, d, - (u32 *)&supply->u_volt_max)) - return -ENOMEM; - - if (!debugfs_create_u32("u_amp", S_IRUGO, d, (u32 *)&supply->u_amp)) - return -ENOMEM; - if (!debugfs_create_u32("clock_latency_ns", S_IRUGO, d, (u32 *)&opp->clock_latency_ns)) return -ENOMEM; + ret = opp_debug_create_supplies(opp, dev_opp, d); + if (ret) + return ret; + opp->dentry = d; return 0; } diff --git a/drivers/base/power/opp/opp.h b/drivers/base/power/opp/opp.h index a7a6917d6fbd..16575268f6ce 100644 --- a/drivers/base/power/opp/opp.h +++ b/drivers/base/power/opp/opp.h @@ -135,6 +135,7 @@ struct device_list_opp { * @opp_list: list of opps * @np: struct device_node pointer for opp's DT node. * @supply_count: Number of power-supplies + * @supply_names: Array of strings containing names of the power-supplies * @shared_opp: OPP is shared between multiple devices. * @dentry: debugfs dentry pointer of the real device directory (not links). * @dentry_name: Name of the real dentry. @@ -157,7 +158,10 @@ struct device_opp { struct device_node *np; unsigned long clock_latency_ns_max; + unsigned int supply_count; + const char **supply_names; + bool shared_opp; struct dev_pm_opp *suspend_opp;