linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH 0/4] clk: qcom: Add support to vote to genpd
@ 2018-07-21 18:14 Taniya Das
  2018-07-21 18:14 ` [RFC PATCH 1/4] clk: qcom: Add support to request power domain state Taniya Das
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: Taniya Das @ 2018-07-21 18:14 UTC (permalink / raw)
  To: Stephen Boyd, Michael Turquette
  Cc: Andy Gross, David Brown, Rajendra Nayak, Amit Nischal,
	linux-arm-msm, linux-soc, linux-clk, linux-kernel, grahamr,
	Taniya Das

A clock controller could be connected to single or multiple power domains. Add
support for powerdomain_class which would help associate these power domains to
the RCGs and PLLs in the clock controller. Map the domain and the corresponding
frequencies to the clock(RCG/PLL). The clock frequency request from a consumer
would be mapped to the corresponding performance corner, aggregated at the
clock driver and would be vote/unvoted to the genpd framework for the desired
performance state.

This series add an example of power domain class for sdm845 RCG/PLLs and the
corresponding frequency mappings. This depends on power domain drivers of
SDM845 https://lkml.org/lkml/2018/6/27/7.

Taniya Das (4):
  clk: qcom: Add support to request power domain state
  clk: qcom: Initialize the power domain class for each clock
  clk: qcom: Add prepare/unprepare clock ops for PLL/RCG
  clk: qcom: sdm845: Add Power Domain to RCGs and PLL

 arch/arm64/boot/dts/qcom/sdm845.dtsi |   2 +
 drivers/clk/qcom/Makefile            |   1 +
 drivers/clk/qcom/clk-alpha-pll.c     |  52 ++++++++--
 drivers/clk/qcom/clk-pd.c            | 193 +++++++++++++++++++++++++++++++++++
 drivers/clk/qcom/clk-pd.h            |  55 ++++++++++
 drivers/clk/qcom/clk-rcg2.c          |  61 +++++++++--
 drivers/clk/qcom/clk-regmap.h        |   5 +
 drivers/clk/qcom/common.c            |  17 ++-
 drivers/clk/qcom/gcc-sdm845.c        |  83 ++++++++++++---
 9 files changed, 427 insertions(+), 42 deletions(-)
 create mode 100644 drivers/clk/qcom/clk-pd.c
 create mode 100644 drivers/clk/qcom/clk-pd.h

--
Qualcomm INDIA, on behalf of Qualcomm Innovation Center, Inc.is a member
of the Code Aurora Forum, hosted by the  Linux Foundation.


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [RFC PATCH 1/4] clk: qcom: Add support to request power domain state
  2018-07-21 18:14 [RFC PATCH 0/4] clk: qcom: Add support to vote to genpd Taniya Das
@ 2018-07-21 18:14 ` Taniya Das
  2018-07-21 18:15 ` [RFC PATCH 2/4] clk: qcom: Initialize the power domain class for each clock Taniya Das
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: Taniya Das @ 2018-07-21 18:14 UTC (permalink / raw)
  To: Stephen Boyd, Michael Turquette
  Cc: Andy Gross, David Brown, Rajendra Nayak, Amit Nischal,
	linux-arm-msm, linux-soc, linux-clk, linux-kernel, grahamr,
	Taniya Das

There could be single power domain or multiple power domains associated
with a clock controller. Add powerdomain_class support which would help
vote/unvote for any power domain performance state for a clock frequency
to the genpd framework.
A clock frequency request from a consumer would look for the corresponding
performance corner and thus would aggregate and request the desired
performance state to genpd.

Signed-off-by: Taniya Das <tdas@codeaurora.org>
---
 drivers/clk/qcom/Makefile |   1 +
 drivers/clk/qcom/clk-pd.c | 193 ++++++++++++++++++++++++++++++++++++++++++++++
 drivers/clk/qcom/clk-pd.h |  55 +++++++++++++
 3 files changed, 249 insertions(+)
 create mode 100644 drivers/clk/qcom/clk-pd.c
 create mode 100644 drivers/clk/qcom/clk-pd.h

diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile
index 599ab91..336d4da 100644
--- a/drivers/clk/qcom/Makefile
+++ b/drivers/clk/qcom/Makefile
@@ -12,6 +12,7 @@ clk-qcom-y += clk-regmap-divider.o
 clk-qcom-y += clk-regmap-mux.o
 clk-qcom-y += clk-regmap-mux-div.o
 clk-qcom-y += reset.o
+clk-qcom-y += clk-pd.o
 clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o
 
 # Keep alphabetically sorted by config
diff --git a/drivers/clk/qcom/clk-pd.c b/drivers/clk/qcom/clk-pd.c
new file mode 100644
index 0000000..d1f9df3
--- /dev/null
+++ b/drivers/clk/qcom/clk-pd.c
@@ -0,0 +1,193 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/pm_domain.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+
+#include "clk-pd.h"
+#include "clk-regmap.h"
+
+struct clk_powerdomain {
+	struct list_head list;
+	struct clk_powerdomain_class *pd;
+};
+
+static LIST_HEAD(clk_pd_list);
+
+/* Find the corner required for a given clock rate */
+static int find_rate_to_corner(struct clk_regmap *rclk, unsigned long rate)
+{
+	int corner;
+
+	for (corner = 0; corner < rclk->pd->num_corners; corner++)
+		if (rate <= rclk->rate_max[corner])
+			break;
+
+	if (corner == rclk->pd->num_corners) {
+		pr_debug("Rate %lu for %s is > than highest Fmax\n", rate,
+			 rclk->hw.init->name);
+		return -EINVAL;
+	}
+
+	return corner;
+}
+
+static int pd_update_corner_state(struct clk_powerdomain_class *pd)
+{
+	int corner, ret, *state = pd->corner, i;
+	int cur_corner = pd->cur_corner, max_corner = pd->num_corners - 1;
+
+	/* Aggregate the corner */
+	for (corner = max_corner; corner > 0; corner--) {
+		if (pd->corner_votes[corner])
+			break;
+	}
+
+	if (corner == cur_corner)
+		return 0;
+
+	pr_debug("Set performance state to genpd(%s) for state %d, cur_corner %d, num_corner %d\n",
+		 pd->pd_name, state[corner], cur_corner, pd->num_corners);
+
+	for (i = 0; i < pd->num_pd; i++) {
+		ret = dev_pm_genpd_set_performance_state(pd->powerdomain_dev[i],
+							 state[corner]);
+		if (ret)
+			return ret;
+
+		if (cur_corner == 0 || cur_corner == pd->num_corners) {
+			pd->links[i] = device_link_add(pd->dev,
+					pd->powerdomain_dev[i],
+					DL_FLAG_STATELESS |
+					DL_FLAG_PM_RUNTIME |
+					DL_FLAG_RPM_ACTIVE);
+			if (!pd->links[i])
+				pr_err("Links for %d not created\n", i);
+		}
+
+		if (corner == 0)
+			device_link_del(pd->links[i]);
+	}
+
+	pd->cur_corner = corner;
+
+	return 0;
+}
+
+/* call from prepare & set rate */
+int clk_power_domain_vote_rate(struct clk_regmap *rclk,
+				unsigned long rate)
+{
+	int corner;
+
+	if (!rclk->pd)
+		return 0;
+
+	corner = find_rate_to_corner(rclk, rate);
+	if (corner < 0)
+		return corner;
+
+	mutex_lock(&rclk->pd->lock);
+
+	rclk->pd->corner_votes[corner]++;
+
+	/* update the corner to power domain */
+	if (pd_update_corner_state(rclk->pd) < 0)
+		rclk->pd->corner_votes[corner]--;
+
+	pr_debug("pd(%s) prepare corner_votes_count %d, corner %d\n",
+		 rclk->pd->pd_name, rclk->pd->corner_votes[corner],
+		 corner);
+
+	mutex_unlock(&rclk->pd->lock);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(clk_power_domain_vote_rate);
+
+/* call from unprepare & set rate */
+void clk_power_domain_unvote_rate(struct clk_regmap *rclk,
+				   unsigned long rate)
+{
+	int corner;
+
+	if (!rclk->pd)
+		return;
+
+	corner = find_rate_to_corner(rclk, rate);
+	if (corner < 0)
+		return;
+
+	if (WARN(!rclk->pd->corner_votes[corner],
+		 "Reference counts are incorrect for %s corner %d\n",
+		 rclk->pd->pd_name, corner))
+		return;
+
+	mutex_lock(&rclk->pd->lock);
+
+	rclk->pd->corner_votes[corner]--;
+
+	if (pd_update_corner_state(rclk->pd) < 0)
+		rclk->pd->corner_votes[corner]++;
+
+	pr_debug("pd(%s) unprepare corner_votes_count %d, corner %d\n",
+		 rclk->pd->pd_name, rclk->pd->corner_votes[corner],
+		 corner);
+
+	mutex_unlock(&rclk->pd->lock);
+}
+EXPORT_SYMBOL_GPL(clk_power_domain_unvote_rate);
+
+int clk_power_domain_class_init(struct device *dev,
+				struct clk_powerdomain_class *pd)
+{
+	struct clk_powerdomain *pwrd;
+	int i, num_domains;
+
+	if (!pd) {
+		pr_debug("pd not defined\n");
+		return 0;
+	}
+
+	/* Deal only with devices using multiple PM domains. */
+	num_domains = of_count_phandle_with_args(dev->of_node, "power-domains",
+						 "#power-domain-cells");
+
+	list_for_each_entry(pwrd, &clk_pd_list, list) {
+		if (pwrd->pd == pd)
+			return 0;
+	}
+
+	pr_debug("Voting for pd_class %s\n", pd->pd_name);
+
+	if (num_domains == 1) {
+		pd->powerdomain_dev[0] = dev;
+	} else {
+		for (i = 0; i < pd->num_pd; i++) {
+			int index = pd->pd_index[i];
+
+			pd->powerdomain_dev[i] = genpd_dev_pm_attach_by_id(dev,
+									index);
+		}
+	}
+
+	pwrd = kmalloc(sizeof(*pwrd), GFP_KERNEL);
+	if (!pwrd)
+		return -ENOMEM;
+
+	pwrd->pd = pd;
+	list_add_tail(&pwrd->list, &clk_pd_list);
+
+	pd->dev = dev;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(clk_power_domain_class_init);
+
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/clk/qcom/clk-pd.h b/drivers/clk/qcom/clk-pd.h
new file mode 100644
index 0000000..addde4f
--- /dev/null
+++ b/drivers/clk/qcom/clk-pd.h
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved. */
+
+struct clk_regmap;
+
+/**
+ * struct clk_powerdomain_class - Power Domain scaling class
+ * @pd_name:		name of the power domain class
+ * @dev:		clock controller device
+ * @powerdomain_dev:	array of power domain devices
+ * @links:		array of power domain devices linked
+ * @num_pd:		size of power domain devices array
+ * @pd_index:		array of power domain index which would
+ *			be used to attach the power domain using
+ *			genpd_dev_pm_attach_by_id(dev, index)
+ * @corner:		sorted 2D array of legal corner settings,
+			indexed by the corner
+ * @corner_votes:	array of votes for each corner
+ * @num_corner:		specifies the size of corner_votes array
+ * @cur_corner:		current set power domain corner
+ * @lock:		lock to protect this struct
+ */
+struct clk_powerdomain_class {
+	const char *pd_name;
+	struct device *dev;
+	struct device **powerdomain_dev;
+	struct device_link **links;
+	int num_pd;
+	int *pd_index;
+	int *corner;
+	int *corner_votes;
+	int num_corners;
+	unsigned int cur_corner;
+	/* Protect this struct */
+	struct mutex lock;
+};
+
+#define CLK_POWERDOMAIN_INIT(_name, _num_corners, _num_pd, _corner)	\
+	struct clk_powerdomain_class _name = {				\
+		.pd_name = #_name,					\
+		.powerdomain_dev = (struct device *([_num_pd])) {},	\
+		.links = (struct device_link *([_num_pd])) {},		\
+		.num_pd = _num_pd,					\
+		.pd_index = (int [_num_pd]) {},				\
+		.corner_votes = (int [_num_corners]) {},		\
+		.num_corners = _num_corners,				\
+		.corner = _corner,					\
+		.cur_corner = _num_corners,				\
+		.lock = __MUTEX_INITIALIZER(_name.lock)			\
+	}
+
+int clk_power_domain_class_init(struct device *dev,
+				struct clk_powerdomain_class *pd);
+int clk_power_domain_vote_rate(struct clk_regmap *rclk, unsigned long rate);
+void clk_power_domain_unvote_rate(struct clk_regmap *rclk, unsigned long rate);
-- 
Qualcomm INDIA, on behalf of Qualcomm Innovation Center, Inc.is a member
of the Code Aurora Forum, hosted by the  Linux Foundation.


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [RFC PATCH 2/4] clk: qcom: Initialize the power domain class for each clock
  2018-07-21 18:14 [RFC PATCH 0/4] clk: qcom: Add support to vote to genpd Taniya Das
  2018-07-21 18:14 ` [RFC PATCH 1/4] clk: qcom: Add support to request power domain state Taniya Das
@ 2018-07-21 18:15 ` Taniya Das
  2018-07-21 18:15 ` [RFC PATCH 3/4] clk: qcom: Add prepare/unprepare clock ops for PLL/RCG Taniya Das
  2018-07-21 18:15 ` [RFC PATCH 4/4] clk: qcom: sdm845: Add Power Domain to RCGs and PLL Taniya Das
  3 siblings, 0 replies; 5+ messages in thread
From: Taniya Das @ 2018-07-21 18:15 UTC (permalink / raw)
  To: Stephen Boyd, Michael Turquette
  Cc: Andy Gross, David Brown, Rajendra Nayak, Amit Nischal,
	linux-arm-msm, linux-soc, linux-clk, linux-kernel, grahamr,
	Taniya Das

The power domain class is being initialized for clocks which has
an associated power domains before registering the clocks with
the clock framework.

Signed-off-by: Taniya Das <tdas@codeaurora.org>
---
 drivers/clk/qcom/clk-regmap.h |  5 +++++
 drivers/clk/qcom/common.c     | 17 +++++++----------
 2 files changed, 12 insertions(+), 10 deletions(-)

diff --git a/drivers/clk/qcom/clk-regmap.h b/drivers/clk/qcom/clk-regmap.h
index 90d95cd..6265d91 100644
--- a/drivers/clk/qcom/clk-regmap.h
+++ b/drivers/clk/qcom/clk-regmap.h
@@ -17,22 +17,27 @@
 #include <linux/clk-provider.h>
 
 struct regmap;
+struct clk_powerdomain_class;
 
 /**
  * struct clk_regmap - regmap supporting clock
  * @hw:		handle between common and hardware-specific interfaces
  * @regmap:	regmap to use for regmap helpers and/or by providers
+ * @pd:		power domain scaling requirement class
  * @enable_reg: register when using regmap enable/disable ops
  * @enable_mask: mask when using regmap enable/disable ops
  * @enable_is_inverted: flag to indicate set enable_mask bits to disable
  *                      when using clock_enable_regmap and friends APIs.
+ * @rate_max:	maximum clock rate in Hz supported at each power domain.
  */
 struct clk_regmap {
 	struct clk_hw hw;
 	struct regmap *regmap;
+	struct clk_powerdomain_class *pd;
 	unsigned int enable_reg;
 	unsigned int enable_mask;
 	bool enable_is_inverted;
+	unsigned long *rate_max;
 };
 #define to_clk_regmap(_hw) container_of(_hw, struct clk_regmap, hw)
 
diff --git a/drivers/clk/qcom/common.c b/drivers/clk/qcom/common.c
index 39ce64c..b0684bf 100644
--- a/drivers/clk/qcom/common.c
+++ b/drivers/clk/qcom/common.c
@@ -1,14 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
- * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
+ * Copyright (c) 2013-2014, 2018, The Linux Foundation. All rights reserved.
  */
 
 #include <linux/export.h>
@@ -20,6 +12,7 @@
 #include <linux/of.h>
 
 #include "common.h"
+#include "clk-pd.h"
 #include "clk-rcg.h"
 #include "clk-regmap.h"
 #include "reset.h"
@@ -263,6 +256,10 @@ int qcom_cc_really_probe(struct platform_device *pdev,
 		if (!rclks[i])
 			continue;
 
+		ret = clk_power_domain_class_init(dev, rclks[i]->pd);
+		if (ret)
+			return ret;
+
 		ret = devm_clk_register_regmap(dev, rclks[i]);
 		if (ret)
 			return ret;
-- 
Qualcomm INDIA, on behalf of Qualcomm Innovation Center, Inc.is a member
of the Code Aurora Forum, hosted by the  Linux Foundation.


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [RFC PATCH 3/4] clk: qcom: Add prepare/unprepare clock ops for PLL/RCG
  2018-07-21 18:14 [RFC PATCH 0/4] clk: qcom: Add support to vote to genpd Taniya Das
  2018-07-21 18:14 ` [RFC PATCH 1/4] clk: qcom: Add support to request power domain state Taniya Das
  2018-07-21 18:15 ` [RFC PATCH 2/4] clk: qcom: Initialize the power domain class for each clock Taniya Das
@ 2018-07-21 18:15 ` Taniya Das
  2018-07-21 18:15 ` [RFC PATCH 4/4] clk: qcom: sdm845: Add Power Domain to RCGs and PLL Taniya Das
  3 siblings, 0 replies; 5+ messages in thread
From: Taniya Das @ 2018-07-21 18:15 UTC (permalink / raw)
  To: Stephen Boyd, Michael Turquette
  Cc: Andy Gross, David Brown, Rajendra Nayak, Amit Nischal,
	linux-arm-msm, linux-soc, linux-clk, linux-kernel, grahamr,
	Taniya Das

To put across power domain votes associated with a PLL or RCG, add the
prepare/unprepare clock ops which would map the corresponding performance
state corners for a clock frequency when the clk_prepare/clk_unprepare is
being invoked.
Also update the set_rate clock ops to send across the performance state to
genpd framework when a new frequency is being requested.

Signed-off-by: Taniya Das <tdas@codeaurora.org>
---
 drivers/clk/qcom/clk-alpha-pll.c | 52 +++++++++++++++++++++++++++-------
 drivers/clk/qcom/clk-rcg2.c      | 61 +++++++++++++++++++++++++++++++++++-----
 2 files changed, 96 insertions(+), 17 deletions(-)

diff --git a/drivers/clk/qcom/clk-alpha-pll.c b/drivers/clk/qcom/clk-alpha-pll.c
index 3c49a60..c03b6e4 100644
--- a/drivers/clk/qcom/clk-alpha-pll.c
+++ b/drivers/clk/qcom/clk-alpha-pll.c
@@ -1,14 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  * Copyright (c) 2015, 2018, The Linux Foundation. All rights reserved.
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
  */
 
 #include <linux/kernel.h>
@@ -18,6 +10,7 @@
 #include <linux/delay.h>
 
 #include "clk-alpha-pll.h"
+#include "clk-pd.h"
 #include "common.h"
 
 #define PLL_MODE(p)		((p)->offset + 0x0)
@@ -522,7 +515,9 @@ static int __clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate,
 	struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
 	const struct pll_vco *vco;
 	u32 l, alpha_width = pll_alpha_width(pll);
+	unsigned long old_rate = clk_hw_get_rate(hw);
 	u64 a;
+	int ret;
 
 	rate = alpha_pll_round_rate(rate, prate, &l, &a, alpha_width);
 	vco = alpha_pll_find_vco(pll, rate);
@@ -531,6 +526,15 @@ static int __clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate,
 		return -EINVAL;
 	}
 
+	if (clk_hw_is_prepared(hw)) {
+		/* Enforce power domain requirement for new frequency */
+		ret = clk_power_domain_vote_rate(&pll->clkr, rate);
+		if (ret) {
+			pr_err("Failed to vote/set new rate %lu\n", rate);
+			return ret;
+		}
+	}
+
 	regmap_write(pll->clkr.regmap, PLL_L_VAL(pll), l);
 
 	if (alpha_width > ALPHA_BITWIDTH)
@@ -550,7 +554,15 @@ static int __clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate,
 	regmap_update_bits(pll->clkr.regmap, PLL_USER_CTL(pll),
 			   PLL_ALPHA_EN, PLL_ALPHA_EN);
 
-	return clk_alpha_pll_update_latch(pll, is_enabled);
+	ret = clk_alpha_pll_update_latch(pll, is_enabled);
+	if (ret)
+		old_rate = rate;
+
+	if (clk_hw_is_prepared(hw))
+		/* Release the power domain requirement for old frequency */
+		clk_power_domain_unvote_rate(&pll->clkr, old_rate);
+
+	return ret;
 }
 
 static int clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -1017,7 +1029,25 @@ static int alpha_pll_fabia_set_rate(struct clk_hw *hw, unsigned long rate,
 	return __clk_alpha_pll_update_latch(pll);
 }
 
+static int clk_alpha_pll_prepare(struct clk_hw *hw)
+{
+	struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
+	unsigned long rate = clk_hw_get_rate(hw);
+
+	return clk_power_domain_vote_rate(&pll->clkr, rate);
+}
+
+static void clk_alpha_pll_unprepare(struct clk_hw *hw)
+{
+	struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
+	unsigned long rate = clk_hw_get_rate(hw);
+
+	clk_power_domain_unvote_rate(&pll->clkr, rate);
+}
+
 const struct clk_ops clk_alpha_pll_fabia_ops = {
+	.prepare = clk_alpha_pll_prepare,
+	.unprepare = clk_alpha_pll_unprepare,
 	.enable = alpha_pll_fabia_enable,
 	.disable = alpha_pll_fabia_disable,
 	.is_enabled = clk_alpha_pll_is_enabled,
@@ -1028,6 +1058,8 @@ static int alpha_pll_fabia_set_rate(struct clk_hw *hw, unsigned long rate,
 EXPORT_SYMBOL_GPL(clk_alpha_pll_fabia_ops);
 
 const struct clk_ops clk_alpha_pll_fixed_fabia_ops = {
+	.prepare = clk_alpha_pll_prepare,
+	.unprepare = clk_alpha_pll_unprepare,
 	.enable = alpha_pll_fabia_enable,
 	.disable = alpha_pll_fabia_disable,
 	.is_enabled = clk_alpha_pll_is_enabled,
diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c
index 52208d4..dc17dbf 100644
--- a/drivers/clk/qcom/clk-rcg2.c
+++ b/drivers/clk/qcom/clk-rcg2.c
@@ -15,6 +15,7 @@
 
 #include <asm/div64.h>
 
+#include "clk-pd.h"
 #include "clk-rcg.h"
 #include "common.h"
 
@@ -247,26 +248,46 @@ static int __clk_rcg2_configure(struct clk_rcg2 *rcg, const struct freq_tbl *f)
 	u32 cfg, mask;
 	struct clk_hw *hw = &rcg->clkr.hw;
 	int ret, index = qcom_find_src_index(hw, rcg->parent_map, f->src);
+	unsigned long old_rate = clk_hw_get_rate(hw);
 
 	if (index < 0)
 		return index;
 
+	/* Enforce power domain requirement for new frequency */
+	if (clk_hw_is_prepared(hw)) {
+		ret = clk_power_domain_vote_rate(&rcg->clkr, f->freq);
+		if (ret) {
+			pr_err("Failed to vote & set new rate %lu\n", f->freq);
+			return ret;
+		}
+	}
+
 	if (rcg->mnd_width && f->n) {
 		mask = BIT(rcg->mnd_width) - 1;
 		ret = regmap_update_bits(rcg->clkr.regmap,
 				rcg->cmd_rcgr + M_REG, mask, f->m);
-		if (ret)
-			return ret;
+		if (ret) {
+			/*
+			 * Release the power domain requirement for
+			 * new frequency
+			 */
+			old_rate = f->freq;
+			goto out;
+		}
 
 		ret = regmap_update_bits(rcg->clkr.regmap,
 				rcg->cmd_rcgr + N_REG, mask, ~(f->n - f->m));
-		if (ret)
-			return ret;
+		if (ret) {
+			old_rate = f->freq;
+			goto out;
+		}
 
 		ret = regmap_update_bits(rcg->clkr.regmap,
 				rcg->cmd_rcgr + D_REG, mask, ~f->n);
-		if (ret)
-			return ret;
+		if (ret) {
+			old_rate = f->freq;
+			goto out;
+		}
 	}
 
 	mask = BIT(rcg->hid_width) - 1;
@@ -276,8 +297,14 @@ static int __clk_rcg2_configure(struct clk_rcg2 *rcg, const struct freq_tbl *f)
 	if (rcg->mnd_width && f->n && (f->m != f->n))
 		cfg |= CFG_MODE_DUAL_EDGE;
 
-	return regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG,
+	ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG,
 					mask, cfg);
+out:
+	if (clk_hw_is_prepared(hw))
+		/* Release the power domain requirement for old/new frequency */
+		clk_power_domain_unvote_rate(&rcg->clkr, old_rate);
+
+	return ret;
 }
 
 static int clk_rcg2_configure(struct clk_rcg2 *rcg, const struct freq_tbl *f)
@@ -338,7 +365,25 @@ static int clk_rcg2_set_floor_rate_and_parent(struct clk_hw *hw,
 	return __clk_rcg2_set_rate(hw, rate, FLOOR);
 }
 
+static int clk_rcg2_prepare(struct clk_hw *hw)
+{
+	struct clk_rcg2 *rcg = to_clk_rcg2(hw);
+	unsigned long rate = clk_hw_get_rate(hw);
+
+	return clk_power_domain_vote_rate(&rcg->clkr, rate);
+}
+
+static void clk_rcg2_unprepare(struct clk_hw *hw)
+{
+	struct clk_rcg2 *rcg = to_clk_rcg2(hw);
+	unsigned long rate = clk_hw_get_rate(hw);
+
+	clk_power_domain_unvote_rate(&rcg->clkr, rate);
+}
+
 const struct clk_ops clk_rcg2_ops = {
+	.prepare = clk_rcg2_prepare,
+	.unprepare = clk_rcg2_unprepare,
 	.is_enabled = clk_rcg2_is_enabled,
 	.get_parent = clk_rcg2_get_parent,
 	.set_parent = clk_rcg2_set_parent,
@@ -919,6 +964,8 @@ static void clk_rcg2_shared_disable(struct clk_hw *hw)
 }
 
 const struct clk_ops clk_rcg2_shared_ops = {
+	.prepare = clk_rcg2_prepare,
+	.unprepare = clk_rcg2_unprepare,
 	.enable = clk_rcg2_shared_enable,
 	.disable = clk_rcg2_shared_disable,
 	.get_parent = clk_rcg2_get_parent,
-- 
Qualcomm INDIA, on behalf of Qualcomm Innovation Center, Inc.is a member
of the Code Aurora Forum, hosted by the  Linux Foundation.


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [RFC PATCH 4/4] clk: qcom: sdm845: Add Power Domain to RCGs and PLL
  2018-07-21 18:14 [RFC PATCH 0/4] clk: qcom: Add support to vote to genpd Taniya Das
                   ` (2 preceding siblings ...)
  2018-07-21 18:15 ` [RFC PATCH 3/4] clk: qcom: Add prepare/unprepare clock ops for PLL/RCG Taniya Das
@ 2018-07-21 18:15 ` Taniya Das
  3 siblings, 0 replies; 5+ messages in thread
From: Taniya Das @ 2018-07-21 18:15 UTC (permalink / raw)
  To: Stephen Boyd, Michael Turquette
  Cc: Andy Gross, David Brown, Rajendra Nayak, Amit Nischal,
	linux-arm-msm, linux-soc, linux-clk, linux-kernel, grahamr,
	Taniya Das

Test code for GCC Power Domain Voting for root clocks/plls.

Signed-off-by: Taniya Das <tdas@codeaurora.org>
---
 arch/arm64/boot/dts/qcom/sdm845.dtsi |  2 +
 drivers/clk/qcom/gcc-sdm845.c        | 83 +++++++++++++++++++++++++++++-------
 drivers/clk/qcom/vdd-level.h         | 31 ++++++++++++++
 3 files changed, 101 insertions(+), 15 deletions(-)
 create mode 100644 drivers/clk/qcom/vdd-level.h

diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi
index 00722b5..742f72a 100644
--- a/arch/arm64/boot/dts/qcom/sdm845.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi
@@ -247,6 +247,8 @@
 			#clock-cells = <1>;
 			#reset-cells = <1>;
 			#power-domain-cells = <1>;
+			power-domains = <&rpmhpd SDM845_CX>,
+					<&rpmhpd SDM845_CX_AO>;
 		};

 		qupv3_id_0: geniqup@8c0000 {
diff --git a/drivers/clk/qcom/gcc-sdm845.c b/drivers/clk/qcom/gcc-sdm845.c
index 0f694ed..60225c1 100644
--- a/drivers/clk/qcom/gcc-sdm845.c
+++ b/drivers/clk/qcom/gcc-sdm845.c
@@ -24,6 +24,11 @@
 #include "clk-alpha-pll.h"
 #include "gdsc.h"
 #include "reset.h"
+#include "clk-pd.h"
+#include "vdd-level.h"
+
+#include <linux/delay.h>
+#include <linux/clk.h>

 #define F(f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n) }

@@ -162,12 +167,22 @@ enum {
 	"core_bi_pll_test_se",
 };

+static CLK_POWERDOMAIN_INIT(vdd_cx, VDD_NUM, 1, vdd_corner);
+static CLK_POWERDOMAIN_INIT(vdd_cx_ao, VDD_NUM, 1, vdd_corner);
+
 static struct clk_alpha_pll gpll0 = {
 	.offset = 0x0,
 	.regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_FABIA],
 	.clkr = {
 		.enable_reg = 0x52000,
 		.enable_mask = BIT(0),
+		.pd = &vdd_cx,
+		.rate_max = (unsigned long[VDD_NUM]) {
+			[VDD_MIN] = 615000000,
+			[VDD_LOW] = 1066000000,
+			[VDD_LOW_L1] = 1600000000,
+			[VDD_NOMINAL] = 2000000000,
+		},
 		.hw.init = &(struct clk_init_data){
 			.name = "gpll0",
 			.parent_names = (const char *[]){ "bi_tcxo" },
@@ -183,6 +198,13 @@ enum {
 	.clkr = {
 		.enable_reg = 0x52000,
 		.enable_mask = BIT(4),
+		.pd = &vdd_cx,
+		.rate_max = (unsigned long[VDD_NUM]) {
+			[VDD_MIN] = 615000000,
+			[VDD_LOW] = 1066000000,
+			[VDD_LOW_L1] = 1600000000,
+			[VDD_NOMINAL] = 2000000000,
+		},
 		.hw.init = &(struct clk_init_data){
 			.name = "gpll4",
 			.parent_names = (const char *[]){ "bi_tcxo" },
@@ -226,11 +248,20 @@ enum {
 	.hid_width = 5,
 	.parent_map = gcc_parent_map_0,
 	.freq_tbl = ftbl_gcc_cpuss_ahb_clk_src,
-	.clkr.hw.init = &(struct clk_init_data){
-		.name = "gcc_cpuss_ahb_clk_src",
-		.parent_names = gcc_parent_names_7,
-		.num_parents = 4,
-		.ops = &clk_rcg2_ops,
+	.clkr = {
+	/*	.pd = &vdd_cx_ao,  Remove this for testing */
+		.pd = &vdd_cx,
+		.rate_max = (unsigned long[VDD_NUM]) {
+			[VDD_MIN] = 19200000,
+			[VDD_LOW] = 50000000,
+			[VDD_NOMINAL] = 100000000,
+		},
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_cpuss_ahb_clk_src",
+			.parent_names = gcc_parent_names_7,
+			.num_parents = 4,
+			.ops = &clk_rcg2_ops,
+		},
 	},
 };

@@ -268,11 +299,20 @@ enum {
 	.hid_width = 5,
 	.parent_map = gcc_parent_map_1,
 	.freq_tbl = ftbl_gcc_gp1_clk_src,
-	.clkr.hw.init = &(struct clk_init_data){
-		.name = "gcc_gp1_clk_src",
-		.parent_names = gcc_parent_names_1,
-		.num_parents = 5,
-		.ops = &clk_rcg2_ops,
+	.clkr = {
+		.pd = &vdd_cx_ao,
+		.rate_max = (unsigned long[VDD_NUM]) {
+			[VDD_MIN] = 19200000,
+			[VDD_LOWER] = 50000000,
+			[VDD_LOW] = 100000000,
+			[VDD_NOMINAL] = 200000000,
+		},
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_gp1_clk_src",
+			.parent_names = gcc_parent_names_1,
+			.num_parents = 5,
+			.ops = &clk_rcg2_ops,
+		},
 	},
 };

@@ -282,11 +322,20 @@ enum {
 	.hid_width = 5,
 	.parent_map = gcc_parent_map_1,
 	.freq_tbl = ftbl_gcc_gp1_clk_src,
-	.clkr.hw.init = &(struct clk_init_data){
-		.name = "gcc_gp2_clk_src",
-		.parent_names = gcc_parent_names_1,
-		.num_parents = 5,
-		.ops = &clk_rcg2_ops,
+	.clkr = {
+		.pd = &vdd_cx,
+		.rate_max = (unsigned long[VDD_NUM]) {
+			[VDD_MIN] = 19200000,
+			[VDD_LOWER] = 50000000,
+			[VDD_LOW] = 100000000,
+			[VDD_NOMINAL] = 200000000,
+		},
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_gp2_clk_src",
+			.parent_names = gcc_parent_names_1,
+			.num_parents = 5,
+			.ops = &clk_rcg2_ops,
+		},
 	},
 };

@@ -3472,6 +3521,10 @@ static int gcc_sdm845_probe(struct platform_device *pdev)
 	regmap_update_bits(regmap, 0x09ffc, 0x3, 0x3);
 	regmap_update_bits(regmap, 0x71028, 0x3, 0x3);

+	/* Indexes of the power domain */
+	vdd_cx.pd_index[0] = 0;
+	vdd_cx_ao.pd_index[0] = 1;
+
 	return qcom_cc_really_probe(pdev, &gcc_sdm845_desc, regmap);
 }

diff --git a/drivers/clk/qcom/vdd-level.h b/drivers/clk/qcom/vdd-level.h
new file mode 100644
index 0000000..7de65142
--- /dev/null
+++ b/drivers/clk/qcom/vdd-level.h
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved. */
+
+#ifndef __DRIVERS_CLK_QCOM_VDD_LEVEL_H
+#define __DRIVERS_CLK_QCOM_VDD_LEVEL_H
+
+#include <dt-bindings/power/qcom-rpmpd.h>
+
+enum vdd_levels {
+	VDD_NONE,
+	VDD_MIN,	/* MIN SVS */
+	VDD_LOWER,	/* SVS2 */
+	VDD_LOW,	/* SVS */
+	VDD_LOW_L1,	/* SVSL1 */
+	VDD_NOMINAL,	/* NOM */
+	VDD_HIGH,	/* TURBO */
+	VDD_NUM,
+};
+
+static int vdd_corner[] = {
+	RPMH_REGULATOR_LEVEL_RETENTION,	/* VDD_NONE */
+	RPMH_REGULATOR_LEVEL_MIN_SVS,	/* VDD_MIN */
+	RPMH_REGULATOR_LEVEL_LOW_SVS,	/* VDD_LOWER */
+	RPMH_REGULATOR_LEVEL_SVS,	/* VDD_LOW */
+	RPMH_REGULATOR_LEVEL_SVS_L1,	/* VDD_LOW_L1 */
+	RPMH_REGULATOR_LEVEL_NOM,	/* VDD_NOMINAL */
+	RPMH_REGULATOR_LEVEL_TURBO,	/* VDD_HIGH */
+};
+
+#endif
+
--
Qualcomm INDIA, on behalf of Qualcomm Innovation Center, Inc.is a member
of the Code Aurora Forum, hosted by the  Linux Foundation.


^ permalink raw reply related	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2018-07-21 18:15 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-07-21 18:14 [RFC PATCH 0/4] clk: qcom: Add support to vote to genpd Taniya Das
2018-07-21 18:14 ` [RFC PATCH 1/4] clk: qcom: Add support to request power domain state Taniya Das
2018-07-21 18:15 ` [RFC PATCH 2/4] clk: qcom: Initialize the power domain class for each clock Taniya Das
2018-07-21 18:15 ` [RFC PATCH 3/4] clk: qcom: Add prepare/unprepare clock ops for PLL/RCG Taniya Das
2018-07-21 18:15 ` [RFC PATCH 4/4] clk: qcom: sdm845: Add Power Domain to RCGs and PLL Taniya Das

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).