All of lore.kernel.org
 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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.