summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/clk/sunxi/clk-factors.c54
-rw-r--r--drivers/clk/sunxi/clk-factors.h2
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 {