From 836ccda884608f91b4ff57afb3ff2359911343dd Mon Sep 17 00:00:00 2001 From: Christoph Muellner Date: Tue, 5 May 2015 23:27:59 +0200 Subject: Clk: Sunxi: Add support for reparenting. This patch adds support for reparenting sunxi factor clocks. The idea is simple: if the DTS provides all possible parents in the correct order, they can be tagged by "clocks-complete" = "ok"; If such a property is found in a sunxi factor clock, then the reparenting uses the indices of the parent list provided in the DTB and the new clock properties parentwidth and parentshift to set the new parent in the clock configuration register. Signed-off-by: Christoph Muellner --- drivers/clk/sunxi/clk-factors.c | 54 ++++++++++++++++++++++++++++++++++++++++- drivers/clk/sunxi/clk-factors.h | 2 ++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/drivers/clk/sunxi/clk-factors.c b/drivers/clk/sunxi/clk-factors.c index 8c20190a3e9f..2b0ef1613c84 100644 --- a/drivers/clk/sunxi/clk-factors.c +++ b/drivers/clk/sunxi/clk-factors.c @@ -151,6 +151,38 @@ static int clk_factors_set_rate(struct clk_hw *hw, unsigned long rate, return 0; } +static u8 clk_factors_get_parent(struct clk_hw *hw) +{ + struct clk_factors *factors = to_clk_factors(hw); + struct clk_factors_config *config = factors->config; + u32 reg; + + reg = readl(factors->reg); + return FACTOR_GET(config->parentshift, config->parentwidth, reg); +} + +static int clk_factors_set_parent(struct clk_hw *hw, u8 index) +{ + struct clk_factors *factors = to_clk_factors(hw); + struct clk_factors_config *config = factors->config; + unsigned long flags = 0; + u32 reg; + + if (factors->lock) + spin_lock_irqsave(factors->lock, flags); + + reg = readl(factors->reg); + + reg = FACTOR_SET(config->parentshift, config->parentwidth, reg, index); + + writel(reg, factors->reg); + + if (factors->lock) + spin_unlock_irqrestore(factors->lock, flags); + + return 0; +} + static const struct clk_ops clk_factors_ops = { .determine_rate = clk_factors_determine_rate, .recalc_rate = clk_factors_recalc_rate, @@ -158,6 +190,15 @@ static const struct clk_ops clk_factors_ops = { .set_rate = clk_factors_set_rate, }; +static const struct clk_ops clk_factors_with_parenting_ops = { + .determine_rate = clk_factors_determine_rate, + .recalc_rate = clk_factors_recalc_rate, + .round_rate = clk_factors_round_rate, + .set_rate = clk_factors_set_rate, + .get_parent = clk_factors_get_parent, + .set_parent = clk_factors_set_parent, +}; + struct clk *sunxi_factors_register(struct device_node *node, const struct factors_data *data, spinlock_t *lock, @@ -171,6 +212,7 @@ struct clk *sunxi_factors_register(struct device_node *node, struct clk_hw *mux_hw = NULL; const char *clk_name = node->name; const char *parents[FACTORS_MAX_PARENTS]; + const struct clk_ops *clk_ops; int i = 0; /* if we have a mux, we will have >1 parents */ @@ -229,10 +271,20 @@ struct clk *sunxi_factors_register(struct device_node *node, mux_hw = &mux->hw; } + /* Some clocks specify all their possible parents + * in correct order in the DTB. + * We accept reparenting on them. + */ + if (of_property_match_string(node, "clocks-complete", "okay") > 0 || + of_property_match_string(node, "clocks-complete", "ok") > 0) + clk_ops = &clk_factors_with_parenting_ops; + else + clk_ops = &clk_factors_ops; + clk = clk_register_composite(NULL, clk_name, parents, i, mux_hw, &clk_mux_ops, - &factors->hw, &clk_factors_ops, + &factors->hw, clk_ops, gate_hw, &clk_gate_ops, 0); if (!IS_ERR(clk)) { diff --git a/drivers/clk/sunxi/clk-factors.h b/drivers/clk/sunxi/clk-factors.h index 171085ab5513..1fee3d096738 100644 --- a/drivers/clk/sunxi/clk-factors.h +++ b/drivers/clk/sunxi/clk-factors.h @@ -17,6 +17,8 @@ struct clk_factors_config { u8 pshift; u8 pwidth; u8 n_start; + u8 parentshift; + u8 parentwidth; }; struct factors_data { -- cgit v1.2.3