* [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).