summaryrefslogtreecommitdiff
path: root/drivers/clk/sunxi-ng/ccu_mux.c
diff options
context:
space:
mode:
authorStephen Boyd <sboyd@codeaurora.org>2017-06-17 00:45:27 +0300
committerStephen Boyd <sboyd@codeaurora.org>2017-06-17 00:45:27 +0300
commit8a02fcf8a0ad4009c27c047deda7eca25430f963 (patch)
treec7f321c5d00fb3b38dd0833acbf768ac99603bd1 /drivers/clk/sunxi-ng/ccu_mux.c
parent9c861f33284cd77be3077cacc4c04303b3a60ebe (diff)
parent06e226c7fb233f676b01b144d0b321ebe510fdcd (diff)
downloadlinux-8a02fcf8a0ad4009c27c047deda7eca25430f963.tar.xz
Merge tag 'sunxi-clk-for-4.13' of https://git.kernel.org/pub/scm/linux/kernel/git/sunxi/linux into clk-next
Pull Allwinner clock patches from Maxime Ripard: Some new clock units are supported, for the display clocks unsed in the newer SoCs, and the A83T PRCM. There is also a bunch of minor fixes for clocks that are not used by anyone, and reworks needed by drivers that will land in 4.13. * tag 'sunxi-clk-for-4.13' of https://git.kernel.org/pub/scm/linux/kernel/git/sunxi/linux: (21 commits) clk: sunxi-ng: Move all clock types to a library clk: sunxi-ng: a83t: Add support for A83T's PRCM dt-bindings: clock: sunxi-ccu: Add compatible string for A83T PRCM clk: sunxi-ng: select SUNXI_CCU_MULT for sun8i-a83t clk: sunxi-ng: a83t: Fix audio PLL divider offset clk: sunxi-ng: a83t: Fix PLL lock status register offset clk: sunxi-ng: Add driver for A83T CCU clk: sunxi-ng: Support multiple variable pre-dividers dt-bindings: clock: sunxi-ccu: Add compatible string for A83T CCU clk: sunxi-ng: de2: fix wrong pointer passed to PTR_ERR() clk: sunxi-ng: sun5i: Export video PLLs clk: sunxi-ng: mux: Re-adjust parent rate clk: sunxi-ng: mux: Change pre-divider application function prototype clk: sunxi-ng: mux: split out the pre-divider computation code clk: sunxi-ng: mux: Don't just rely on the parent for CLK_SET_RATE_PARENT clk: sunxi-ng: div: Switch to divider_round_rate clk: sunxi-ng: Pass the parent and a pointer to the clocks round rate clk: divider: Make divider_round_rate take the parent clock clk: sunxi-ng: explicitly include linux/spinlock.h clk: sunxi-ng: add support for DE2 CCU ...
Diffstat (limited to 'drivers/clk/sunxi-ng/ccu_mux.c')
-rw-r--r--drivers/clk/sunxi-ng/ccu_mux.c108
1 files changed, 64 insertions, 44 deletions
diff --git a/drivers/clk/sunxi-ng/ccu_mux.c b/drivers/clk/sunxi-ng/ccu_mux.c
index c6bb1f523232..cfe4538304fb 100644
--- a/drivers/clk/sunxi-ng/ccu_mux.c
+++ b/drivers/clk/sunxi-ng/ccu_mux.c
@@ -15,24 +15,20 @@
#include "ccu_gate.h"
#include "ccu_mux.h"
-void ccu_mux_helper_adjust_parent_for_prediv(struct ccu_common *common,
- struct ccu_mux_internal *cm,
- int parent_index,
- unsigned long *parent_rate)
+static u16 ccu_mux_get_prediv(struct ccu_common *common,
+ struct ccu_mux_internal *cm,
+ int parent_index)
{
u16 prediv = 1;
u32 reg;
- int i;
if (!((common->features & CCU_FEATURE_FIXED_PREDIV) ||
(common->features & CCU_FEATURE_VARIABLE_PREDIV) ||
(common->features & CCU_FEATURE_ALL_PREDIV)))
- return;
+ return 1;
- if (common->features & CCU_FEATURE_ALL_PREDIV) {
- *parent_rate = *parent_rate / common->prediv;
- return;
- }
+ if (common->features & CCU_FEATURE_ALL_PREDIV)
+ return common->prediv;
reg = readl(common->base + common->reg);
if (parent_index < 0) {
@@ -40,28 +36,52 @@ void ccu_mux_helper_adjust_parent_for_prediv(struct ccu_common *common,
parent_index &= (1 << cm->width) - 1;
}
- if (common->features & CCU_FEATURE_FIXED_PREDIV)
+ if (common->features & CCU_FEATURE_FIXED_PREDIV) {
+ int i;
+
for (i = 0; i < cm->n_predivs; i++)
if (parent_index == cm->fixed_predivs[i].index)
prediv = cm->fixed_predivs[i].div;
+ }
- if (common->features & CCU_FEATURE_VARIABLE_PREDIV)
- if (parent_index == cm->variable_prediv.index) {
- u8 div;
+ if (common->features & CCU_FEATURE_VARIABLE_PREDIV) {
+ int i;
- div = reg >> cm->variable_prediv.shift;
- div &= (1 << cm->variable_prediv.width) - 1;
- prediv = div + 1;
- }
+ for (i = 0; i < cm->n_var_predivs; i++)
+ if (parent_index == cm->var_predivs[i].index) {
+ u8 div;
+
+ div = reg >> cm->var_predivs[i].shift;
+ div &= (1 << cm->var_predivs[i].width) - 1;
+ prediv = div + 1;
+ }
+ }
+
+ return prediv;
+}
- *parent_rate = *parent_rate / prediv;
+unsigned long ccu_mux_helper_apply_prediv(struct ccu_common *common,
+ struct ccu_mux_internal *cm,
+ int parent_index,
+ unsigned long parent_rate)
+{
+ return parent_rate / ccu_mux_get_prediv(common, cm, parent_index);
+}
+
+unsigned long ccu_mux_helper_unapply_prediv(struct ccu_common *common,
+ struct ccu_mux_internal *cm,
+ int parent_index,
+ unsigned long parent_rate)
+{
+ return parent_rate * ccu_mux_get_prediv(common, cm, parent_index);
}
int ccu_mux_helper_determine_rate(struct ccu_common *common,
struct ccu_mux_internal *cm,
struct clk_rate_request *req,
unsigned long (*round)(struct ccu_mux_internal *,
- unsigned long,
+ struct clk_hw *,
+ unsigned long *,
unsigned long,
void *),
void *data)
@@ -75,41 +95,43 @@ int ccu_mux_helper_determine_rate(struct ccu_common *common,
best_parent = clk_hw_get_parent(hw);
best_parent_rate = clk_hw_get_rate(best_parent);
+ adj_parent_rate = ccu_mux_helper_apply_prediv(common, cm, -1,
+ best_parent_rate);
- adj_parent_rate = best_parent_rate;
- ccu_mux_helper_adjust_parent_for_prediv(common, cm, -1,
- &adj_parent_rate);
+ best_rate = round(cm, best_parent, &adj_parent_rate,
+ req->rate, data);
- best_rate = round(cm, adj_parent_rate, req->rate, data);
+ /*
+ * adj_parent_rate might have been modified by our clock.
+ * Unapply the pre-divider if there's one, and give
+ * the actual frequency the parent needs to run at.
+ */
+ best_parent_rate = ccu_mux_helper_unapply_prediv(common, cm, -1,
+ adj_parent_rate);
goto out;
}
for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
- unsigned long tmp_rate, parent_rate, adj_parent_rate;
+ unsigned long tmp_rate, parent_rate;
struct clk_hw *parent;
parent = clk_hw_get_parent_by_index(hw, i);
if (!parent)
continue;
- if (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) {
- struct clk_rate_request parent_req = *req;
- int ret = __clk_determine_rate(parent, &parent_req);
-
- if (ret)
- continue;
+ parent_rate = ccu_mux_helper_apply_prediv(common, cm, i,
+ clk_hw_get_rate(parent));
- parent_rate = parent_req.rate;
- } else {
- parent_rate = clk_hw_get_rate(parent);
- }
+ tmp_rate = round(cm, parent, &parent_rate, req->rate, data);
- adj_parent_rate = parent_rate;
- ccu_mux_helper_adjust_parent_for_prediv(common, cm, i,
- &adj_parent_rate);
-
- tmp_rate = round(cm, adj_parent_rate, req->rate, data);
+ /*
+ * parent_rate might have been modified by our clock.
+ * Unapply the pre-divider if there's one, and give
+ * the actual frequency the parent needs to run at.
+ */
+ parent_rate = ccu_mux_helper_unapply_prediv(common, cm, i,
+ parent_rate);
if (tmp_rate == req->rate) {
best_parent = parent;
best_parent_rate = parent_rate;
@@ -217,10 +239,8 @@ static unsigned long ccu_mux_recalc_rate(struct clk_hw *hw,
{
struct ccu_mux *cm = hw_to_ccu_mux(hw);
- ccu_mux_helper_adjust_parent_for_prediv(&cm->common, &cm->mux, -1,
- &parent_rate);
-
- return parent_rate;
+ return ccu_mux_helper_apply_prediv(&cm->common, &cm->mux, -1,
+ parent_rate);
}
const struct clk_ops ccu_mux_ops = {