From patchwork Wed Oct 26 06:33:02 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 79330 Delivered-To: patch@linaro.org Received: by 10.140.97.247 with SMTP id m110csp252038qge; Tue, 25 Oct 2016 23:34:23 -0700 (PDT) X-Received: by 10.98.153.204 with SMTP id t73mr1217891pfk.144.1477463663257; Tue, 25 Oct 2016 23:34:23 -0700 (PDT) Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id e83si898677pfb.195.2016.10.25.23.34.22; Tue, 25 Oct 2016 23:34:23 -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; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754150AbcJZGeL (ORCPT + 27 others); Wed, 26 Oct 2016 02:34:11 -0400 Received: from mail-pf0-f181.google.com ([209.85.192.181]:34761 "EHLO mail-pf0-f181.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753760AbcJZGeG (ORCPT ); Wed, 26 Oct 2016 02:34:06 -0400 Received: by mail-pf0-f181.google.com with SMTP id n85so17480984pfi.1 for ; Tue, 25 Oct 2016 23:34: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 :in-reply-to:references; bh=eh0Gynbsl/EJHe2m3eeEFJTFKFhWM4X1mHIHhMyO+vo=; b=AErzAlP5Zd7ne8I8mS9TNHZQK6JK0ojcBhmOW1OjBUjvsxIbV8VhwDiLVRDZxA75QH /aTTLDczcnfb/JE/JzU7sttOA9oYyMBf42Uzxza23WFtulNThaxQJHbU6G6Ty4kPxfOh XhD0guKQJ95fq39egY3C0TU79BfSi5NLtbEsY= 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:in-reply-to:references; bh=eh0Gynbsl/EJHe2m3eeEFJTFKFhWM4X1mHIHhMyO+vo=; b=TAYq/qWOGj0kHisIr3M9AtbuXEdbwF8ipa+C+3k2LtDiCvRaZTI5wEwiyHPB4KKpEh D/KZqqGYigH6cSEIzd1pwI4puzWQHFG/hITGbgXuJ/G7T980xaSam+fJmZX5JSIenHJb sRjR8gCh/ojv5rKANKnD28hjy0hSIjMwm7PdhF/TJO/WYwEr7PbqsqK/mBKazTol4O72 xZYrfpqy+fnA/gTqoA/SR3QxygMi25nohQLS1IYXwbDg6bBuZrKZkIdTBYivnE2zpMCM 3+VsxXJezuzHN6GS+yq/x3tFu+1SsY12Dg7spRAeh4wZPLEZ1v+O5uNjWShSEQl8GCXU pZ+g== X-Gm-Message-State: ABUngvclrGpG+zIzsMaCTtbBcUel0czVKOKB0mkEe3iSV7a1HnIfT861jKChR39E8Bo+RAfi X-Received: by 10.98.19.208 with SMTP id 77mr1258540pft.102.1477463645201; Tue, 25 Oct 2016 23:34:05 -0700 (PDT) Received: from localhost ([171.61.116.10]) by smtp.gmail.com with ESMTPSA id a7sm1312089pan.34.2016.10.25.23.34.04 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 25 Oct 2016 23:34:04 -0700 (PDT) From: Viresh Kumar To: Rafael Wysocki , nm@ti.com, sboyd@codeaurora.org, Viresh Kumar Cc: linaro-kernel@lists.linaro.org, linux-pm@vger.kernel.org, linux-kernel@vger.kernel.org, Vincent Guittot , robh@kernel.org, d-gerlach@ti.com, broonie@kernel.org, Viresh Kumar Subject: [PATCH V3 7/9] PM / OPP: Allow platform specific custom set_opp() callbacks Date: Wed, 26 Oct 2016 12:03:02 +0530 Message-Id: <9d34e3d6e36f0a6c7aae40b5e7192ae9f8be841c.1477463128.git.viresh.kumar@linaro.org> X-Mailer: git-send-email 2.7.1.410.g6faf27b In-Reply-To: References: In-Reply-To: References: Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org The generic set_opp() handler isn't sufficient for platforms with complex DVFS. For example, some TI platforms have multiple regulators for a CPU device. The order in which various supplies need to be programmed is only known to the platform code and its best to leave it to it. This patch implements APIs to register platform specific set_opp() callback. Signed-off-by: Viresh Kumar Tested-by: Dave Gerlach --- drivers/base/power/opp/core.c | 116 +++++++++++++++++++++++++++++++++++++++++- drivers/base/power/opp/opp.h | 1 + include/linux/pm_opp.h | 10 ++++ 3 files changed, 125 insertions(+), 2 deletions(-) -- 2.7.1.410.g6faf27b diff --git a/drivers/base/power/opp/core.c b/drivers/base/power/opp/core.c index dedb08a66e99..f4f6b1fdbe06 100644 --- a/drivers/base/power/opp/core.c +++ b/drivers/base/power/opp/core.c @@ -673,6 +673,7 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq) { struct opp_table *opp_table; unsigned long freq, old_freq; + int (*set_opp)(struct device *dev, struct dev_pm_set_opp_data *data); struct dev_pm_opp *old_opp, *opp; struct regulator **regulators; struct dev_pm_set_opp_data *data; @@ -737,6 +738,11 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq) return _generic_set_opp_clk_only(dev, clk, old_freq, freq); } + if (opp_table->set_opp) + set_opp = opp_table->set_opp; + else + set_opp = _generic_set_opp; + data = opp_table->set_opp_data; data->regulators = regulators; data->regulator_count = opp_table->regulator_count; @@ -754,7 +760,7 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq) rcu_read_unlock(); - return _generic_set_opp(dev, data); + return set_opp(dev, data); } EXPORT_SYMBOL_GPL(dev_pm_opp_set_rate); @@ -888,6 +894,9 @@ static void _remove_opp_table(struct opp_table *opp_table) if (opp_table->regulators) return; + if (opp_table->set_opp) + return; + /* Release clk */ if (!IS_ERR(opp_table->clk)) clk_put(opp_table->clk); @@ -1486,7 +1495,7 @@ int dev_pm_opp_set_regulators(struct device *dev, const char * const names[], opp_table->regulator_count = count; - /* Allocate block only once to pass to ->set_rate() */ + /* Allocate block only once to pass to ->set_opp() */ ret = _allocate_set_opp_data(opp_table); if (ret) goto free_regulators; @@ -1561,6 +1570,109 @@ void dev_pm_opp_put_regulators(struct device *dev) EXPORT_SYMBOL_GPL(dev_pm_opp_put_regulators); /** + * dev_pm_opp_register_set_opp_helper() - Register custom OPP set rate helper + * @dev: Device for which the helper is getting registered. + * @set_opp: Custom set OPP helper. + * + * This is useful to support complex platforms (like platforms with multiple + * regulators per device), instead of the generic OPP set rate helper. + * + * This must be called before any OPPs are initialized for the device. + * + * Locking: The internal opp_table and opp structures are RCU protected. + * Hence this function internally uses RCU updater strategy with mutex locks + * to keep the integrity of the internal data structures. Callers should ensure + * that this function is *NOT* called under RCU protection or in contexts where + * mutex cannot be locked. + */ +int dev_pm_opp_register_set_opp_helper(struct device *dev, + int (*set_opp)(struct device *dev, struct dev_pm_set_opp_data *data)) +{ + struct opp_table *opp_table; + int ret; + + if (!set_opp) + return -EINVAL; + + mutex_lock(&opp_table_lock); + + opp_table = _add_opp_table(dev); + if (!opp_table) { + ret = -ENOMEM; + goto unlock; + } + + /* This should be called before OPPs are initialized */ + if (WARN_ON(!list_empty(&opp_table->opp_list))) { + ret = -EBUSY; + goto err; + } + + /* Already have custom set_opp helper */ + if (WARN_ON(opp_table->set_opp)) { + ret = -EBUSY; + goto err; + } + + opp_table->set_opp = set_opp; + + mutex_unlock(&opp_table_lock); + return 0; + +err: + _remove_opp_table(opp_table); +unlock: + mutex_unlock(&opp_table_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(dev_pm_opp_register_set_opp_helper); + +/** + * dev_pm_opp_register_put_opp_helper() - Releases resources blocked for + * set_opp helper + * @dev: Device for which custom set_opp helper has to be cleared. + * + * Locking: The internal opp_table and opp structures are RCU protected. + * Hence this function internally uses RCU updater strategy with mutex locks + * to keep the integrity of the internal data structures. Callers should ensure + * that this function is *NOT* called under RCU protection or in contexts where + * mutex cannot be locked. + */ +void dev_pm_opp_register_put_opp_helper(struct device *dev) +{ + struct opp_table *opp_table; + + mutex_lock(&opp_table_lock); + + /* Check for existing table for 'dev' first */ + opp_table = _find_opp_table(dev); + if (IS_ERR(opp_table)) { + dev_err(dev, "Failed to find opp_table: %ld\n", + PTR_ERR(opp_table)); + goto unlock; + } + + if (!opp_table->set_opp) { + dev_err(dev, "%s: Doesn't have custom set_opp helper set\n", + __func__); + goto unlock; + } + + /* Make sure there are no concurrent readers while updating opp_table */ + WARN_ON(!list_empty(&opp_table->opp_list)); + + opp_table->set_opp = NULL; + + /* Try freeing opp_table if this was the last blocking resource */ + _remove_opp_table(opp_table); + +unlock: + mutex_unlock(&opp_table_lock); +} +EXPORT_SYMBOL_GPL(dev_pm_opp_register_put_opp_helper); + +/** * dev_pm_opp_add() - Add an OPP table from a table definitions * @dev: device for which we do this operation * @freq: Frequency in Hz for this OPP diff --git a/drivers/base/power/opp/opp.h b/drivers/base/power/opp/opp.h index 26bc6c1b8c60..62a6020b2c3d 100644 --- a/drivers/base/power/opp/opp.h +++ b/drivers/base/power/opp/opp.h @@ -178,6 +178,7 @@ struct opp_table { struct regulator **regulators; unsigned int regulator_count; + int (*set_opp)(struct device *dev, struct dev_pm_set_opp_data *data); struct dev_pm_set_opp_data *set_opp_data; #ifdef CONFIG_DEBUG_FS diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h index 2969519bf5f7..cb5bc4747502 100644 --- a/include/linux/pm_opp.h +++ b/include/linux/pm_opp.h @@ -113,6 +113,8 @@ int dev_pm_opp_set_prop_name(struct device *dev, const char *name); void dev_pm_opp_put_prop_name(struct device *dev); int dev_pm_opp_set_regulators(struct device *dev, const char * const names[], unsigned int count); void dev_pm_opp_put_regulators(struct device *dev); +int dev_pm_opp_register_set_opp_helper(struct device *dev, int (*set_opp)(struct device *dev, struct dev_pm_set_opp_data *data)); +void dev_pm_opp_register_put_opp_helper(struct device *dev); int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq); int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, const struct cpumask *cpumask); int dev_pm_opp_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask); @@ -212,6 +214,14 @@ static inline int dev_pm_opp_set_supported_hw(struct device *dev, static inline void dev_pm_opp_put_supported_hw(struct device *dev) {} +static inline int dev_pm_opp_register_set_opp_helper(struct device *dev, + int (*set_opp)(struct device *dev, struct dev_pm_set_opp_data *data)) +{ + return -ENOTSUPP; +} + +static inline void dev_pm_opp_register_put_opp_helper(struct device *dev) {} + static inline int dev_pm_opp_set_prop_name(struct device *dev, const char *name) { return -ENOTSUPP;