ipq806x: update clk and cpufreq drivers
A newer clk and cpufreq drivers for ipq806x platform had been sent upstream. A change that i have noticed is that now it's possible to set min, cur and max frequencies from sysfs (previously it was bugged and caused nothing). Following patches are removed: - 0036-clk-Avoid-sending-high-rates-to-downstream-clocks-du.patch - seems it was dropped from the patchset by current committer. - 0044-clk-qcom-krait-Remove-CLK_IS_ROOT.patch - already applied to the driver itself in the corresponding patch. - 0057-clk-qcom-Add-regmap-mux-div-clocks-support.patch - seem to be irrelevant to ipq806x. Signed-off-by: Pavel Kubelun <be.dissent@gmail.com>v19.07.3_mercusys_ac12_duma
parent
83499bef73
commit
7903a9219c
@ -1,107 +0,0 @@
|
||||
From 88e1d6d9c113fe50810d1b03eb1fdbf015e5d1bd Mon Sep 17 00:00:00 2001
|
||||
From: Stephen Boyd <sboyd@codeaurora.org>
|
||||
Date: Fri, 20 Mar 2015 23:45:22 -0700
|
||||
Subject: [PATCH 36/69] clk: Avoid sending high rates to downstream clocks
|
||||
during set_rate
|
||||
|
||||
If a clock is on and we call clk_set_rate() on it we may get into
|
||||
a situation where the clock temporarily increases in rate
|
||||
dramatically while we walk the tree and call .set_rate() ops. For
|
||||
example, consider a case where a PLL feeds into a divider.
|
||||
Initially the divider is set to divide by 1 and the PLL is
|
||||
running fairly slow (100MHz). The downstream consumer of the
|
||||
divider output can only handle rates =< 400 MHz, but the divider
|
||||
can only choose between divisors of 1 and 4.
|
||||
|
||||
+-----+ +----------------+
|
||||
| PLL |-->| div 1 or div 4 |---> consumer device
|
||||
+-----+ +----------------+
|
||||
|
||||
To achieve a rate of 400MHz on the output of the divider, we
|
||||
would have to set the rate of the PLL to 1.6 GHz and then divide
|
||||
it by 4. The current code would set the PLL to 1.6GHz first while
|
||||
the divider is still set to 1, thus causing the downstream
|
||||
consumer of the clock to receive a few clock cycles of 1.6GHz
|
||||
clock (far beyond it's maximum acceptable rate). We should be
|
||||
changing the divider first before increasing the PLL rate to
|
||||
avoid this problem.
|
||||
|
||||
Therefore, set the rate of any child clocks that are increasing
|
||||
in rate from their current rate so that they can increase their
|
||||
dividers if necessary. We assume that there isn't such a thing as
|
||||
minimum rate requirements.
|
||||
|
||||
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
|
||||
|
||||
Conflicts:
|
||||
drivers/clk/clk.c
|
||||
---
|
||||
drivers/clk/clk.c | 22 +++++++++++++++-------
|
||||
1 file changed, 15 insertions(+), 7 deletions(-)
|
||||
|
||||
--- a/drivers/clk/clk.c
|
||||
+++ b/drivers/clk/clk.c
|
||||
@@ -1466,12 +1466,12 @@ static struct clk_core *clk_propagate_ra
|
||||
* walk down a subtree and set the new rates notifying the rate
|
||||
* change on the way
|
||||
*/
|
||||
-static void clk_change_rate(struct clk_core *core)
|
||||
+static void
|
||||
+clk_change_rate(struct clk_core *core, unsigned long best_parent_rate)
|
||||
{
|
||||
struct clk_core *child;
|
||||
struct hlist_node *tmp;
|
||||
unsigned long old_rate;
|
||||
- unsigned long best_parent_rate = 0;
|
||||
bool skip_set_rate = false;
|
||||
struct clk_core *old_parent;
|
||||
struct clk_core *parent = NULL;
|
||||
@@ -1523,6 +1523,7 @@ static void clk_change_rate(struct clk_c
|
||||
trace_clk_set_rate_complete(core, core->new_rate);
|
||||
|
||||
core->rate = clk_recalc(core, best_parent_rate);
|
||||
+ core->rate = core->new_rate;
|
||||
|
||||
if (core->flags & CLK_SET_RATE_UNGATE) {
|
||||
unsigned long flags;
|
||||
@@ -1550,12 +1551,13 @@ static void clk_change_rate(struct clk_c
|
||||
/* Skip children who will be reparented to another clock */
|
||||
if (child->new_parent && child->new_parent != core)
|
||||
continue;
|
||||
- clk_change_rate(child);
|
||||
+ if (child->new_rate != child->rate)
|
||||
+ clk_change_rate(child, core->new_rate);
|
||||
}
|
||||
|
||||
- /* handle the new child who might not be in core->children yet */
|
||||
- if (core->new_child)
|
||||
- clk_change_rate(core->new_child);
|
||||
+ /* handle the new child who might not be in clk->children yet */
|
||||
+ if (core->new_child && core->new_child->new_rate != core->new_child->rate)
|
||||
+ clk_change_rate(core->new_child, core->new_rate);
|
||||
}
|
||||
|
||||
static int clk_core_set_rate_nolock(struct clk_core *core,
|
||||
@@ -1563,6 +1565,7 @@ static int clk_core_set_rate_nolock(stru
|
||||
{
|
||||
struct clk_core *top, *fail_clk;
|
||||
unsigned long rate = req_rate;
|
||||
+ unsigned long parent_rate;
|
||||
|
||||
if (!core)
|
||||
return 0;
|
||||
@@ -1588,8 +1591,13 @@ static int clk_core_set_rate_nolock(stru
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
+ if (top->parent)
|
||||
+ parent_rate = top->parent->rate;
|
||||
+ else
|
||||
+ parent_rate = 0;
|
||||
+
|
||||
/* change the rates */
|
||||
- clk_change_rate(top);
|
||||
+ clk_change_rate(top, parent_rate);
|
||||
|
||||
core->req_rate = req_rate;
|
||||
|
@ -1,158 +0,0 @@
|
||||
From a1adfb782789ae9b25c928dfe3d639288563a86c Mon Sep 17 00:00:00 2001
|
||||
From: Stephen Boyd <sboyd@codeaurora.org>
|
||||
Date: Fri, 20 Mar 2015 23:45:23 -0700
|
||||
Subject: [PATCH 37/69] clk: Add safe switch hook
|
||||
|
||||
Sometimes clocks can't accept their parent source turning off
|
||||
while the source is reprogrammed to a different rate. Most
|
||||
notably CPU clocks require a way to switch away from the current
|
||||
PLL they're running on, reprogram that PLL to a new rate, and
|
||||
then switch back to the PLL with the new rate once they're done.
|
||||
Add a hook that drivers can implement allowing them to return a
|
||||
'safe parent' and 'safe frequency' that they can switch their
|
||||
parent to while the upstream source is reprogrammed to support
|
||||
this.
|
||||
|
||||
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
|
||||
Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org>
|
||||
---
|
||||
drivers/clk/clk.c | 73 +++++++++++++++++++++++++++++++++++++++++---
|
||||
include/linux/clk-provider.h | 2 ++
|
||||
2 files changed, 70 insertions(+), 5 deletions(-)
|
||||
|
||||
--- a/drivers/clk/clk.c
|
||||
+++ b/drivers/clk/clk.c
|
||||
@@ -51,9 +51,13 @@ struct clk_core {
|
||||
struct clk_core **parents;
|
||||
u8 num_parents;
|
||||
u8 new_parent_index;
|
||||
+ u8 safe_parent_index;
|
||||
unsigned long rate;
|
||||
unsigned long req_rate;
|
||||
+ unsigned long old_rate;
|
||||
unsigned long new_rate;
|
||||
+ unsigned long safe_freq;
|
||||
+ struct clk_core *safe_parent;
|
||||
struct clk_core *new_parent;
|
||||
struct clk_core *new_child;
|
||||
unsigned long flags;
|
||||
@@ -1310,7 +1314,9 @@ out:
|
||||
static void clk_calc_subtree(struct clk_core *core, unsigned long new_rate,
|
||||
struct clk_core *new_parent, u8 p_index)
|
||||
{
|
||||
- struct clk_core *child;
|
||||
+ struct clk_core *child, *parent;
|
||||
+ struct clk_hw *parent_hw;
|
||||
+ unsigned long safe_freq = 0;
|
||||
|
||||
core->new_rate = new_rate;
|
||||
core->new_parent = new_parent;
|
||||
@@ -1320,6 +1326,23 @@ static void clk_calc_subtree(struct clk_
|
||||
if (new_parent && new_parent != core->parent)
|
||||
new_parent->new_child = core;
|
||||
|
||||
+ if (core->ops->get_safe_parent) {
|
||||
+ parent_hw = core->ops->get_safe_parent(core->hw, &safe_freq);
|
||||
+ if (parent_hw) {
|
||||
+ parent = parent_hw->core;
|
||||
+ p_index = clk_fetch_parent_index(core, parent);
|
||||
+ core->safe_parent_index = p_index;
|
||||
+ core->safe_parent = parent;
|
||||
+ if (safe_freq)
|
||||
+ core->safe_freq = safe_freq;
|
||||
+ else
|
||||
+ core->safe_freq = 0;
|
||||
+ }
|
||||
+ } else {
|
||||
+ core->safe_parent = NULL;
|
||||
+ core->safe_freq = 0;
|
||||
+ }
|
||||
+
|
||||
hlist_for_each_entry(child, &core->children, child_node) {
|
||||
child->new_rate = clk_recalc(child, new_rate);
|
||||
clk_calc_subtree(child, child->new_rate, NULL, 0);
|
||||
@@ -1432,14 +1455,51 @@ static struct clk_core *clk_propagate_ra
|
||||
unsigned long event)
|
||||
{
|
||||
struct clk_core *child, *tmp_clk, *fail_clk = NULL;
|
||||
+ struct clk_core *old_parent;
|
||||
int ret = NOTIFY_DONE;
|
||||
|
||||
- if (core->rate == core->new_rate)
|
||||
+ if (core->rate == core->new_rate && event != POST_RATE_CHANGE)
|
||||
return NULL;
|
||||
|
||||
+ switch (event) {
|
||||
+ case PRE_RATE_CHANGE:
|
||||
+ if (core->safe_parent) {
|
||||
+ if (core->safe_freq)
|
||||
+ core->ops->set_rate_and_parent(core->hw,
|
||||
+ core->safe_freq,
|
||||
+ core->safe_parent->rate,
|
||||
+ core->safe_parent_index);
|
||||
+ else
|
||||
+ core->ops->set_parent(core->hw,
|
||||
+ core->safe_parent_index);
|
||||
+ }
|
||||
+ core->old_rate = core->rate;
|
||||
+ break;
|
||||
+ case POST_RATE_CHANGE:
|
||||
+ if (core->safe_parent) {
|
||||
+ old_parent = __clk_set_parent_before(core,
|
||||
+ core->new_parent);
|
||||
+ if (core->ops->set_rate_and_parent) {
|
||||
+ core->ops->set_rate_and_parent(core->hw,
|
||||
+ core->new_rate,
|
||||
+ core->new_parent ?
|
||||
+ core->new_parent->rate : 0,
|
||||
+ core->new_parent_index);
|
||||
+ } else if (core->ops->set_parent) {
|
||||
+ core->ops->set_parent(core->hw,
|
||||
+ core->new_parent_index);
|
||||
+ }
|
||||
+ __clk_set_parent_after(core, core->new_parent,
|
||||
+ old_parent);
|
||||
+ }
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
if (core->notifier_count) {
|
||||
- ret = __clk_notify(core, event, core->rate, core->new_rate);
|
||||
- if (ret & NOTIFY_STOP_MASK)
|
||||
+ if (event != POST_RATE_CHANGE || core->old_rate != core->rate)
|
||||
+ ret = __clk_notify(core, event, core->old_rate,
|
||||
+ core->new_rate);
|
||||
+ if (ret & NOTIFY_STOP_MASK && event != POST_RATE_CHANGE)
|
||||
fail_clk = core;
|
||||
}
|
||||
|
||||
@@ -1495,7 +1555,8 @@ clk_change_rate(struct clk_core *core, u
|
||||
clk_enable_unlock(flags);
|
||||
}
|
||||
|
||||
- if (core->new_parent && core->new_parent != core->parent) {
|
||||
+ if (core->new_parent && core->new_parent != core->parent &&
|
||||
+ !core->safe_parent) {
|
||||
old_parent = __clk_set_parent_before(core, core->new_parent);
|
||||
trace_clk_set_parent(core, core->new_parent);
|
||||
|
||||
@@ -1601,6 +1662,8 @@ static int clk_core_set_rate_nolock(stru
|
||||
|
||||
core->req_rate = req_rate;
|
||||
|
||||
+ clk_propagate_rate_change(top, POST_RATE_CHANGE);
|
||||
+
|
||||
return 0;
|
||||
}
|
||||
|
||||
--- a/include/linux/clk-provider.h
|
||||
+++ b/include/linux/clk-provider.h
|
||||
@@ -206,6 +206,8 @@ struct clk_ops {
|
||||
struct clk_rate_request *req);
|
||||
int (*set_parent)(struct clk_hw *hw, u8 index);
|
||||
u8 (*get_parent)(struct clk_hw *hw);
|
||||
+ struct clk_hw *(*get_safe_parent)(struct clk_hw *hw,
|
||||
+ unsigned long *safe_freq);
|
||||
int (*set_rate)(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate);
|
||||
int (*set_rate_and_parent)(struct clk_hw *hw,
|
@ -0,0 +1,160 @@
|
||||
From patchwork Fri Dec 8 09:42:28 2017
|
||||
Content-Type: text/plain; charset="utf-8"
|
||||
MIME-Version: 1.0
|
||||
Content-Transfer-Encoding: 7bit
|
||||
Subject: [v4,10/12] clk: qcom: Add safe switch hook for krait mux clocks
|
||||
From: Sricharan R <sricharan@codeaurora.org>
|
||||
X-Patchwork-Id: 10102057
|
||||
Message-Id: <1512726150-7204-11-git-send-email-sricharan@codeaurora.org>
|
||||
To: mturquette@baylibre.com, sboyd@codeaurora.org,
|
||||
devicetree@vger.kernel.org, linux-pm@vger.kernel.org,
|
||||
linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org,
|
||||
viresh.kumar@linaro.org, linux-arm-kernel@lists.infradead.org
|
||||
Cc: sricharan@codeaurora.org
|
||||
Date: Fri, 8 Dec 2017 15:12:28 +0530
|
||||
|
||||
When the Hfplls are reprogrammed during the rate change,
|
||||
the primary muxes which are sourced from the same hfpll
|
||||
for higher frequencies, needs to be switched to the 'safe
|
||||
secondary mux' as the parent for that small window. This
|
||||
is done by registering a clk notifier for the muxes and
|
||||
switching to the safe parent in the PRE_RATE_CHANGE notifier
|
||||
and back to the original parent in the POST_RATE_CHANGE notifier.
|
||||
|
||||
Signed-off-by: Sricharan R <sricharan@codeaurora.org>
|
||||
---
|
||||
drivers/clk/qcom/clk-krait.c | 2 ++
|
||||
drivers/clk/qcom/clk-krait.h | 3 +++
|
||||
drivers/clk/qcom/krait-cc.c | 56 ++++++++++++++++++++++++++++++++++++++++++++
|
||||
3 files changed, 61 insertions(+)
|
||||
|
||||
--- a/drivers/clk/qcom/clk-krait.c
|
||||
+++ b/drivers/clk/qcom/clk-krait.c
|
||||
@@ -60,6 +60,8 @@ static int krait_mux_set_parent(struct c
|
||||
if (__clk_is_enabled(hw->clk))
|
||||
__krait_mux_set_sel(mux, sel);
|
||||
|
||||
+ mux->reparent = true;
|
||||
+
|
||||
return 0;
|
||||
}
|
||||
|
||||
--- a/drivers/clk/qcom/clk-krait.h
|
||||
+++ b/drivers/clk/qcom/clk-krait.h
|
||||
@@ -23,6 +23,9 @@ struct krait_mux_clk {
|
||||
u32 shift;
|
||||
u32 en_mask;
|
||||
bool lpl;
|
||||
+ u8 safe_sel;
|
||||
+ u8 old_index;
|
||||
+ bool reparent;
|
||||
|
||||
struct clk_hw hw;
|
||||
struct notifier_block clk_nb;
|
||||
--- a/drivers/clk/qcom/krait-cc.c
|
||||
+++ b/drivers/clk/qcom/krait-cc.c
|
||||
@@ -35,6 +35,49 @@ static unsigned int pri_mux_map[] = {
|
||||
0,
|
||||
};
|
||||
|
||||
+/*
|
||||
+ * Notifier function for switching the muxes to safe parent
|
||||
+ * while the hfpll is getting reprogrammed.
|
||||
+ */
|
||||
+static int krait_notifier_cb(struct notifier_block *nb,
|
||||
+ unsigned long event,
|
||||
+ void *data)
|
||||
+{
|
||||
+ int ret = 0;
|
||||
+ struct krait_mux_clk *mux = container_of(nb, struct krait_mux_clk,
|
||||
+ clk_nb);
|
||||
+ /* Switch to safe parent */
|
||||
+ if (event == PRE_RATE_CHANGE) {
|
||||
+ mux->old_index = krait_mux_clk_ops.get_parent(&mux->hw);
|
||||
+ ret = krait_mux_clk_ops.set_parent(&mux->hw, mux->safe_sel);
|
||||
+ mux->reparent = false;
|
||||
+ /*
|
||||
+ * By the time POST_RATE_CHANGE notifier is called,
|
||||
+ * clk framework itself would have changed the parent for the new rate.
|
||||
+ * Only otherwise, put back to the old parent.
|
||||
+ */
|
||||
+ } else if (event == POST_RATE_CHANGE) {
|
||||
+ if (!mux->reparent)
|
||||
+ ret = krait_mux_clk_ops.set_parent(&mux->hw,
|
||||
+ mux->old_index);
|
||||
+ }
|
||||
+
|
||||
+ return notifier_from_errno(ret);
|
||||
+}
|
||||
+
|
||||
+static int krait_notifier_register(struct device *dev, struct clk *clk,
|
||||
+ struct krait_mux_clk *mux)
|
||||
+{
|
||||
+ int ret = 0;
|
||||
+
|
||||
+ mux->clk_nb.notifier_call = krait_notifier_cb;
|
||||
+ ret = clk_notifier_register(clk, &mux->clk_nb);
|
||||
+ if (ret)
|
||||
+ dev_err(dev, "failed to register clock notifier: %d\n", ret);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
static int
|
||||
krait_add_div(struct device *dev, int id, const char *s, unsigned int offset)
|
||||
{
|
||||
@@ -79,6 +122,7 @@ static int
|
||||
krait_add_sec_mux(struct device *dev, int id, const char *s,
|
||||
unsigned int offset, bool unique_aux)
|
||||
{
|
||||
+ int ret;
|
||||
struct krait_mux_clk *mux;
|
||||
static const char *sec_mux_list[] = {
|
||||
"acpu_aux",
|
||||
@@ -102,6 +146,7 @@ krait_add_sec_mux(struct device *dev, in
|
||||
mux->shift = 2;
|
||||
mux->parent_map = sec_mux_map;
|
||||
mux->hw.init = &init;
|
||||
+ mux->safe_sel = 0;
|
||||
|
||||
init.name = kasprintf(GFP_KERNEL, "krait%s_sec_mux", s);
|
||||
if (!init.name)
|
||||
@@ -117,6 +162,11 @@ krait_add_sec_mux(struct device *dev, in
|
||||
|
||||
clk = devm_clk_register(dev, &mux->hw);
|
||||
|
||||
+ ret = krait_notifier_register(dev, clk, mux);
|
||||
+ if (ret)
|
||||
+ goto unique_aux;
|
||||
+
|
||||
+unique_aux:
|
||||
if (unique_aux)
|
||||
kfree(sec_mux_list[0]);
|
||||
err_aux:
|
||||
@@ -128,6 +178,7 @@ static struct clk *
|
||||
krait_add_pri_mux(struct device *dev, int id, const char *s,
|
||||
unsigned int offset)
|
||||
{
|
||||
+ int ret;
|
||||
struct krait_mux_clk *mux;
|
||||
const char *p_names[3];
|
||||
struct clk_init_data init = {
|
||||
@@ -148,6 +199,7 @@ krait_add_pri_mux(struct device *dev, in
|
||||
mux->lpl = id >= 0;
|
||||
mux->parent_map = pri_mux_map;
|
||||
mux->hw.init = &init;
|
||||
+ mux->safe_sel = 2;
|
||||
|
||||
init.name = kasprintf(GFP_KERNEL, "krait%s_pri_mux", s);
|
||||
if (!init.name)
|
||||
@@ -173,6 +225,10 @@ krait_add_pri_mux(struct device *dev, in
|
||||
|
||||
clk = devm_clk_register(dev, &mux->hw);
|
||||
|
||||
+ ret = krait_notifier_register(dev, clk, mux);
|
||||
+ if (ret)
|
||||
+ goto err_p3;
|
||||
+err_p3:
|
||||
kfree(p_names[2]);
|
||||
err_p2:
|
||||
kfree(p_names[1]);
|
@ -1,23 +0,0 @@
|
||||
From 58f8215f1d9397f9130657cc2c15a956bd99210e Mon Sep 17 00:00:00 2001
|
||||
From: Georgi Djakov <georgi.djakov@linaro.org>
|
||||
Date: Wed, 13 Jul 2016 15:22:25 +0300
|
||||
Subject: [PATCH 44/69] clk: qcom: krait: Remove CLK_IS_ROOT
|
||||
|
||||
The flag CLK_IS_ROOT is no-op now. Remove it.
|
||||
|
||||
Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org>
|
||||
---
|
||||
drivers/clk/qcom/krait-cc.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/clk/qcom/krait-cc.c
|
||||
+++ b/drivers/clk/qcom/krait-cc.c
|
||||
@@ -258,7 +258,7 @@ static int krait_cc_probe(struct platfor
|
||||
return -ENODEV;
|
||||
|
||||
/* Rate is 1 because 0 causes problems for __clk_mux_determine_rate */
|
||||
- clk = clk_register_fixed_rate(dev, "qsb", NULL, CLK_IS_ROOT, 1);
|
||||
+ clk = clk_register_fixed_rate(dev, "qsb", NULL, 0, 1);
|
||||
if (IS_ERR(clk))
|
||||
return PTR_ERR(clk);
|
||||
|
@ -1,372 +0,0 @@
|
||||
From f72c5aa18281c44945fea6181d0d816a7605505c Mon Sep 17 00:00:00 2001
|
||||
From: Georgi Djakov <georgi.djakov@linaro.org>
|
||||
Date: Wed, 18 Mar 2015 17:23:29 +0200
|
||||
Subject: [PATCH 57/69] clk: qcom: Add regmap mux-div clocks support
|
||||
|
||||
Add support for hardware that can switch both parent clocks and divider
|
||||
at the same time. This avoids generating intermediate frequencies from
|
||||
either the old parent clock and new divider or new parent clock and
|
||||
old divider combinations.
|
||||
|
||||
Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org>
|
||||
---
|
||||
drivers/clk/qcom/Makefile | 1 +
|
||||
drivers/clk/qcom/clk-regmap-mux-div.c | 272 ++++++++++++++++++++++++++++++++++
|
||||
drivers/clk/qcom/clk-regmap-mux-div.h | 65 ++++++++
|
||||
3 files changed, 338 insertions(+)
|
||||
create mode 100644 drivers/clk/qcom/clk-regmap-mux-div.c
|
||||
create mode 100644 drivers/clk/qcom/clk-regmap-mux-div.h
|
||||
|
||||
--- a/drivers/clk/qcom/Makefile
|
||||
+++ b/drivers/clk/qcom/Makefile
|
||||
@@ -9,6 +9,7 @@ clk-qcom-y += clk-rcg2.o
|
||||
clk-qcom-y += clk-branch.o
|
||||
clk-qcom-y += clk-regmap-divider.o
|
||||
clk-qcom-y += clk-regmap-mux.o
|
||||
+clk-qcom-y += clk-regmap-mux-div.o
|
||||
clk-qcom-$(CONFIG_KRAIT_CLOCKS) += clk-krait.o
|
||||
clk-qcom-y += clk-hfpll.o
|
||||
clk-qcom-y += reset.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/clk/qcom/clk-regmap-mux-div.c
|
||||
@@ -0,0 +1,272 @@
|
||||
+/*
|
||||
+ * Copyright (c) 2015, Linaro Limited
|
||||
+ * Copyright (c) 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.
|
||||
+ */
|
||||
+
|
||||
+#include <linux/bitops.h>
|
||||
+#include <linux/delay.h>
|
||||
+#include <linux/export.h>
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/regmap.h>
|
||||
+
|
||||
+#include "clk-regmap-mux-div.h"
|
||||
+
|
||||
+#define CMD_RCGR 0x0
|
||||
+#define CMD_RCGR_UPDATE BIT(0)
|
||||
+#define CMD_RCGR_DIRTY_CFG BIT(4)
|
||||
+#define CMD_RCGR_ROOT_OFF BIT(31)
|
||||
+#define CFG_RCGR 0x4
|
||||
+
|
||||
+#define to_clk_regmap_mux_div(_hw) \
|
||||
+ container_of(to_clk_regmap(_hw), struct clk_regmap_mux_div, clkr)
|
||||
+
|
||||
+int __mux_div_set_src_div(struct clk_regmap_mux_div *md, u32 src, u32 div)
|
||||
+{
|
||||
+ int ret, count;
|
||||
+ u32 val, mask;
|
||||
+ const char *name = clk_hw_get_name(&md->clkr.hw);
|
||||
+
|
||||
+ val = (div << md->hid_shift) | (src << md->src_shift);
|
||||
+ mask = ((BIT(md->hid_width) - 1) << md->hid_shift) |
|
||||
+ ((BIT(md->src_width) - 1) << md->src_shift);
|
||||
+
|
||||
+ ret = regmap_update_bits(md->clkr.regmap, CFG_RCGR + md->reg_offset,
|
||||
+ mask, val);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ ret = regmap_update_bits(md->clkr.regmap, CMD_RCGR + md->reg_offset,
|
||||
+ CMD_RCGR_UPDATE, CMD_RCGR_UPDATE);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ /* Wait for update to take effect */
|
||||
+ for (count = 500; count > 0; count--) {
|
||||
+ ret = regmap_read(md->clkr.regmap, CMD_RCGR + md->reg_offset,
|
||||
+ &val);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ if (!(val & CMD_RCGR_UPDATE))
|
||||
+ return 0;
|
||||
+ udelay(1);
|
||||
+ }
|
||||
+
|
||||
+ pr_err("%s: RCG did not update its configuration", name);
|
||||
+ return -EBUSY;
|
||||
+}
|
||||
+
|
||||
+static void __mux_div_get_src_div(struct clk_regmap_mux_div *md, u32 *src,
|
||||
+ u32 *div)
|
||||
+{
|
||||
+ u32 val, __div, __src;
|
||||
+ const char *name = clk_hw_get_name(&md->clkr.hw);
|
||||
+
|
||||
+ regmap_read(md->clkr.regmap, CMD_RCGR + md->reg_offset, &val);
|
||||
+
|
||||
+ if (val & CMD_RCGR_DIRTY_CFG) {
|
||||
+ pr_err("%s: RCG configuration is pending\n", name);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ regmap_read(md->clkr.regmap, CFG_RCGR + md->reg_offset, &val);
|
||||
+ __src = (val >> md->src_shift);
|
||||
+ __src &= BIT(md->src_width) - 1;
|
||||
+ *src = __src;
|
||||
+
|
||||
+ __div = (val >> md->hid_shift);
|
||||
+ __div &= BIT(md->hid_width) - 1;
|
||||
+ *div = __div;
|
||||
+}
|
||||
+
|
||||
+static int mux_div_enable(struct clk_hw *hw)
|
||||
+{
|
||||
+ struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
|
||||
+
|
||||
+ return __mux_div_set_src_div(md, md->src, md->div);
|
||||
+}
|
||||
+
|
||||
+static inline bool is_better_rate(unsigned long req, unsigned long best,
|
||||
+ unsigned long new)
|
||||
+{
|
||||
+ return (req <= new && new < best) || (best < req && best < new);
|
||||
+}
|
||||
+
|
||||
+static int mux_div_determine_rate(struct clk_hw *hw,
|
||||
+ struct clk_rate_request *req)
|
||||
+{
|
||||
+ struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
|
||||
+ unsigned int i, div, max_div;
|
||||
+ unsigned long actual_rate, best_rate = 0;
|
||||
+ unsigned long req_rate = req->rate;
|
||||
+
|
||||
+ for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
|
||||
+ struct clk_hw *parent = clk_hw_get_parent_by_index(hw, i);
|
||||
+ unsigned long parent_rate = clk_hw_get_rate(parent);
|
||||
+
|
||||
+ max_div = BIT(md->hid_width) - 1;
|
||||
+ for (div = 1; div < max_div; div++) {
|
||||
+ parent_rate = mult_frac(req_rate, div, 2);
|
||||
+ parent_rate = clk_hw_round_rate(parent, parent_rate);
|
||||
+ actual_rate = mult_frac(parent_rate, 2, div);
|
||||
+
|
||||
+ if (is_better_rate(req_rate, best_rate, actual_rate)) {
|
||||
+ best_rate = actual_rate;
|
||||
+ req->rate = best_rate;
|
||||
+ req->best_parent_rate = parent_rate;
|
||||
+ req->best_parent_hw = parent;
|
||||
+ }
|
||||
+
|
||||
+ if (actual_rate < req_rate || best_rate <= req_rate)
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (!best_rate)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int __mux_div_set_rate_and_parent(struct clk_hw *hw, unsigned long rate,
|
||||
+ unsigned long prate, u32 src)
|
||||
+{
|
||||
+ struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
|
||||
+ int ret;
|
||||
+ u32 div, max_div, best_src = 0, best_div = 0;
|
||||
+ unsigned int i;
|
||||
+ unsigned long actual_rate, best_rate = 0;
|
||||
+
|
||||
+ for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
|
||||
+ struct clk_hw *parent = clk_hw_get_parent_by_index(hw, i);
|
||||
+ unsigned long parent_rate = clk_hw_get_rate(parent);
|
||||
+
|
||||
+ max_div = BIT(md->hid_width) - 1;
|
||||
+ for (div = 1; div < max_div; div++) {
|
||||
+ parent_rate = mult_frac(rate, div, 2);
|
||||
+ parent_rate = clk_hw_round_rate(parent, parent_rate);
|
||||
+ actual_rate = mult_frac(parent_rate, 2, div);
|
||||
+
|
||||
+ if (is_better_rate(rate, best_rate, actual_rate)) {
|
||||
+ best_rate = actual_rate;
|
||||
+ best_src = md->parent_map[i].cfg;
|
||||
+ best_div = div - 1;
|
||||
+ }
|
||||
+
|
||||
+ if (actual_rate < rate || best_rate <= rate)
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ ret = __mux_div_set_src_div(md, best_src, best_div);
|
||||
+ if (!ret) {
|
||||
+ md->div = best_div;
|
||||
+ md->src = best_src;
|
||||
+ }
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static u8 mux_div_get_parent(struct clk_hw *hw)
|
||||
+{
|
||||
+ struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
|
||||
+ const char *name = clk_hw_get_name(hw);
|
||||
+ u32 i, div, src = 0;
|
||||
+
|
||||
+ __mux_div_get_src_div(md, &src, &div);
|
||||
+
|
||||
+ for (i = 0; i < clk_hw_get_num_parents(hw); i++)
|
||||
+ if (src == md->parent_map[i].cfg)
|
||||
+ return i;
|
||||
+
|
||||
+ pr_err("%s: Can't find parent with src %d\n", name, src);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int mux_div_set_parent(struct clk_hw *hw, u8 index)
|
||||
+{
|
||||
+ struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
|
||||
+
|
||||
+ return __mux_div_set_src_div(md, md->parent_map[index].cfg, md->div);
|
||||
+}
|
||||
+
|
||||
+static int mux_div_set_rate(struct clk_hw *hw,
|
||||
+ unsigned long rate, unsigned long prate)
|
||||
+{
|
||||
+ struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
|
||||
+
|
||||
+ return __mux_div_set_rate_and_parent(hw, rate, prate, md->src);
|
||||
+}
|
||||
+
|
||||
+static int mux_div_set_rate_and_parent(struct clk_hw *hw, unsigned long rate,
|
||||
+ unsigned long prate, u8 index)
|
||||
+{
|
||||
+ struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
|
||||
+
|
||||
+ return __mux_div_set_rate_and_parent(hw, rate, prate,
|
||||
+ md->parent_map[index].cfg);
|
||||
+}
|
||||
+
|
||||
+static unsigned long mux_div_recalc_rate(struct clk_hw *hw, unsigned long prate)
|
||||
+{
|
||||
+ struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
|
||||
+ u32 div, src;
|
||||
+ int i, num_parents = clk_hw_get_num_parents(hw);
|
||||
+ const char *name = clk_hw_get_name(hw);
|
||||
+
|
||||
+ __mux_div_get_src_div(md, &src, &div);
|
||||
+ for (i = 0; i < num_parents; i++)
|
||||
+ if (src == md->parent_map[i].cfg) {
|
||||
+ struct clk_hw *p = clk_hw_get_parent_by_index(hw, i);
|
||||
+ unsigned long parent_rate = clk_hw_get_rate(p);
|
||||
+
|
||||
+ return mult_frac(parent_rate, 2, div + 1);
|
||||
+ }
|
||||
+
|
||||
+ pr_err("%s: Can't find parent %d\n", name, src);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static struct clk_hw *mux_div_get_safe_parent(struct clk_hw *hw,
|
||||
+ unsigned long *safe_freq)
|
||||
+{
|
||||
+ int i;
|
||||
+ struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
|
||||
+
|
||||
+ if (md->safe_freq)
|
||||
+ *safe_freq = md->safe_freq;
|
||||
+
|
||||
+ for (i = 0; i < clk_hw_get_num_parents(hw); i++)
|
||||
+ if (md->safe_src == md->parent_map[i].cfg)
|
||||
+ break;
|
||||
+
|
||||
+ return clk_hw_get_parent_by_index(hw, i);
|
||||
+}
|
||||
+
|
||||
+static void mux_div_disable(struct clk_hw *hw)
|
||||
+{
|
||||
+ struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
|
||||
+
|
||||
+ __mux_div_set_src_div(md, md->safe_src, md->safe_div);
|
||||
+}
|
||||
+
|
||||
+const struct clk_ops clk_regmap_mux_div_ops = {
|
||||
+ .enable = mux_div_enable,
|
||||
+ .disable = mux_div_disable,
|
||||
+ .get_parent = mux_div_get_parent,
|
||||
+ .set_parent = mux_div_set_parent,
|
||||
+ .set_rate = mux_div_set_rate,
|
||||
+ .set_rate_and_parent = mux_div_set_rate_and_parent,
|
||||
+ .determine_rate = mux_div_determine_rate,
|
||||
+ .recalc_rate = mux_div_recalc_rate,
|
||||
+ .get_safe_parent = mux_div_get_safe_parent,
|
||||
+};
|
||||
+EXPORT_SYMBOL_GPL(clk_regmap_mux_div_ops);
|
||||
--- /dev/null
|
||||
+++ b/drivers/clk/qcom/clk-regmap-mux-div.h
|
||||
@@ -0,0 +1,65 @@
|
||||
+/*
|
||||
+ * Copyright (c) 2015, Linaro Limited
|
||||
+ * Copyright (c) 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.
|
||||
+ */
|
||||
+
|
||||
+#ifndef __QCOM_CLK_REGMAP_MUX_DIV_H__
|
||||
+#define __QCOM_CLK_REGMAP_MUX_DIV_H__
|
||||
+
|
||||
+#include <linux/clk-provider.h>
|
||||
+#include "clk-rcg.h"
|
||||
+#include "clk-regmap.h"
|
||||
+
|
||||
+/**
|
||||
+ * struct mux_div_clk - combined mux/divider clock
|
||||
+ * @reg_offset: offset of the mux/divider register
|
||||
+ * @hid_width: number of bits in half integer divider
|
||||
+ * @hid_shift: lowest bit of hid value field
|
||||
+ * @src_width: number of bits in source select
|
||||
+ * @src_shift: lowest bit of source select field
|
||||
+ * @div: the divider raw configuration value
|
||||
+ * @src: the mux index which will be used if the clock is enabled
|
||||
+ * @safe_src: the safe source mux value we switch to, while the main PLL is
|
||||
+ * reconfigured
|
||||
+ * @safe_div: the safe divider value that we set, while the main PLL is
|
||||
+ * reconfigured
|
||||
+ * @safe_freq: When switching rates from A to B, the mux div clock will
|
||||
+ * instead switch from A -> safe_freq -> B. This allows the
|
||||
+ * mux_div clock to change rates while enabled, even if this
|
||||
+ * behavior is not supported by the parent clocks.
|
||||
+ * If changing the rate of parent A also causes the rate of
|
||||
+ * parent B to change, then safe_freq must be defined.
|
||||
+ * safe_freq is expected to have a source clock which is always
|
||||
+ * on and runs at only one rate.
|
||||
+ * @parent_map: pointer to parent_map struct
|
||||
+ * @clkr: handle between common and hardware-specific interfaces
|
||||
+ */
|
||||
+
|
||||
+struct clk_regmap_mux_div {
|
||||
+ u32 reg_offset;
|
||||
+ u32 hid_width;
|
||||
+ u32 hid_shift;
|
||||
+ u32 src_width;
|
||||
+ u32 src_shift;
|
||||
+ u32 div;
|
||||
+ u32 src;
|
||||
+ u32 safe_src;
|
||||
+ u32 safe_div;
|
||||
+ unsigned long safe_freq;
|
||||
+ const struct parent_map *parent_map;
|
||||
+ struct clk_regmap clkr;
|
||||
+};
|
||||
+
|
||||
+extern const struct clk_ops clk_regmap_mux_div_ops;
|
||||
+int __mux_div_set_src_div(struct clk_regmap_mux_div *md, u32 src, u32 div);
|
||||
+
|
||||
+#endif
|
Loading…
Reference in New Issue