From patchwork Thu Oct 20 08:45:01 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 9386249 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 CC2DE60487 for ; Thu, 20 Oct 2016 08:46:24 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id BA39328C7E for ; Thu, 20 Oct 2016 08:46:24 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id AE93A29AA7; Thu, 20 Oct 2016 08:46:24 +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.3 required=2.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_HI, RCVD_IN_SORBS_SPAM, T_DKIM_INVALID autolearn=ham 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 5F56B28C7E for ; Thu, 20 Oct 2016 08:46:23 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932902AbcJTIqE (ORCPT ); Thu, 20 Oct 2016 04:46:04 -0400 Received: from mail-pf0-f181.google.com ([209.85.192.181]:33119 "EHLO mail-pf0-f181.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S934485AbcJTIpi (ORCPT ); Thu, 20 Oct 2016 04:45:38 -0400 Received: by mail-pf0-f181.google.com with SMTP id 128so32573435pfz.0 for ; Thu, 20 Oct 2016 01:45:37 -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=3dt7C9ihSlAWSjnOclMR7+noiFfzuMz1NfeQ9AVIHF0=; b=fw2YNGC5JzyRLfghThgrUhycGZq0GN54GE7LR53R3IAgeuJh3FWgjQ80t/R/9zkNJP +tf1ojMbhvdxlPTdPM1/SMfZ0hUHNqlLLFVtq4Ol1OdaXuXX5bZpNw+GhD7mJfLIa0Xr W1w/Yib47JfxnBaX0hStY/sGdzIoW5n3M1a04= 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=3dt7C9ihSlAWSjnOclMR7+noiFfzuMz1NfeQ9AVIHF0=; b=mEwon0AUf3BaUwMYOWcKsk4j5/9TpzqOCt4AEy53nQ2z1WxEoaJFVRIaHHiy/WU34T Zv32E2rYkWKJyPgzccjnEbhaJ8wGUg1pKPF9YcPRrc3LpwI0YG0JNQ8rBZYLVkTIcNNX 1kFQFmxk1HleEVnQ0oRxwTgYasO8YEPKgGV7CbVEx3SmwRruehg1Y4pMTiyPODTEGArp XCfYNO7tl+cZOnfgobHD42ZIg+2Yjxt8ON5d3ow2BzZPOVn2sq1CRN3P9nJ3Kb5Dmp9z CwHziLxLno9DDeYugO8paAxq33IQbIWolmX7wxSn4N0EuD5N6WQ+y31hUjC2MWQye6pM uJdg== X-Gm-Message-State: AA6/9RkXaZ4+pF/W+4O2z3WWQ5CeBq7lx5ngDePevK31/zFvb6TUYPM2SCA919K5+7+ESw0w X-Received: by 10.98.206.1 with SMTP id y1mr19349784pfg.70.1476953137272; Thu, 20 Oct 2016 01:45:37 -0700 (PDT) Received: from localhost ([171.61.116.10]) by smtp.gmail.com with ESMTPSA id oo10sm66394177pac.12.2016.10.20.01.45.36 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 20 Oct 2016 01:45:36 -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 V2 7/8] PM / OPP: Allow platform specific custom opp_set_rate() callbacks Date: Thu, 20 Oct 2016 14:15:01 +0530 Message-Id: <4786e1f0958441b03e6d91ce21817898574c45ed.1476952750.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-pm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP The generic opp_set_rate() 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 opp_set_rate() callback. Signed-off-by: Viresh Kumar --- drivers/base/power/opp/core.c | 114 +++++++++++++++++++++++++++++++++++++++++- drivers/base/power/opp/opp.h | 1 + include/linux/pm_opp.h | 10 ++++ 3 files changed, 124 insertions(+), 1 deletion(-) diff --git a/drivers/base/power/opp/core.c b/drivers/base/power/opp/core.c index 96f04392daef..f7c4ef194aac 100644 --- a/drivers/base/power/opp/core.c +++ b/drivers/base/power/opp/core.c @@ -677,6 +677,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_rate)(struct device *dev, struct dev_pm_set_rate_data *data); struct dev_pm_opp *old_opp, *opp; struct regulator **regulators; struct dev_pm_set_rate_data *data; @@ -738,6 +739,11 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq) return _generic_opp_set_rate_clk_only(dev, clk, old_freq, freq); } + if (opp_table->set_rate) + set_rate = opp_table->set_rate; + else + set_rate = _generic_opp_set_rate; + data = opp_table->set_rate_data; data->regulators = regulators; data->regulator_count = opp_table->regulator_count; @@ -755,7 +761,7 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq) rcu_read_unlock(); - return _generic_opp_set_rate(dev, data); + return set_rate(dev, data); } EXPORT_SYMBOL_GPL(dev_pm_opp_set_rate); @@ -889,6 +895,9 @@ static void _remove_opp_table(struct opp_table *opp_table) if (opp_table->regulators) return; + if (opp_table->set_rate) + return; + /* Release clk */ if (!IS_ERR(opp_table->clk)) clk_put(opp_table->clk); @@ -1562,6 +1571,109 @@ void dev_pm_opp_put_regulators(struct device *dev) EXPORT_SYMBOL_GPL(dev_pm_opp_put_regulators); /** + * dev_pm_opp_register_set_rate_helper() - Register custom OPP set rate helper + * @dev: Device for which the helper is getting registered. + * @set_rate: Custom OPP set rate 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_rate_helper(struct device *dev, + int (*set_rate)(struct device *dev, struct dev_pm_set_rate_data *data)) +{ + struct opp_table *opp_table; + int ret; + + if (!set_rate) + 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_rate helper */ + if (WARN_ON(opp_table->set_rate)) { + ret = -EBUSY; + goto err; + } + + opp_table->set_rate = set_rate; + + 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_rate_helper); + +/** + * dev_pm_opp_register_put_rate_helper() - Releases resources blocked for + * set_rate helper + * @dev: Device for which custom set_rate 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_rate_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_rate) { + dev_err(dev, "%s: Doesn't have custom set_rate 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_rate = 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_rate_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 6629c53c0aa1..86e31f06ca64 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_rate)(struct device *dev, struct dev_pm_set_rate_data *data); struct dev_pm_set_rate_data *set_rate_data; #ifdef CONFIG_DEBUG_FS diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h index 73713a8424b1..ce070257d6eb 100644 --- a/include/linux/pm_opp.h +++ b/include/linux/pm_opp.h @@ -95,6 +95,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 *names[], unsigned int count); void dev_pm_opp_put_regulators(struct device *dev); +int dev_pm_opp_register_set_rate_helper(struct device *dev, int (*set_rate)(struct device *dev, struct dev_pm_set_rate_data *data)); +void dev_pm_opp_register_put_rate_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); @@ -194,6 +196,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_rate_helper(struct device *dev, + int (*set_rate)(struct device *dev, struct dev_pm_set_rate_data *data)) +{ + return -ENOTSUPP; +} + +static inline void dev_pm_opp_register_put_rate_helper(struct device *dev) {} + static inline int dev_pm_opp_set_prop_name(struct device *dev, const char *name) { return -ENOTSUPP;