diff options
-rw-r--r-- | drivers/clk/sunxi/clk-factors.c | 54 | ||||
-rw-r--r-- | drivers/clk/sunxi/clk-factors.h | 2 |
2 files changed, 55 insertions, 1 deletions
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 { |