[PATCH 02/14] clk: sunxi: factors: Implement clock min and max frequencies

Maxime Ripard maxime.ripard at free-electrons.com
Thu Jul 17 02:08:42 PDT 2014


In the A13, the out of reset frequency for the PLL6 is too high to be actually
working.

Hence, we need to be able to lower down its frequency whenever we need to use
the clock to some acceptable frequency.

This patch adds two new properties in the clock-nodes, clk-min-frequency and
clk-max-frequency, to specify acceptable boundaries for proper clock
operations.

It also adds supports in the sunxi factor clocks driver to use these
boundaries, enforce them at prepare time to make sure that the drivers will
have a decent frequency, even though it never called set_rate, but also make
sure we never cross these boundaries.

Signed-off-by: Maxime Ripard <maxime.ripard at free-electrons.com>
---
 drivers/clk/sunxi/clk-factors.c | 28 ++++++++++++++++++++++++++++
 drivers/clk/sunxi/clk-factors.h |  2 ++
 2 files changed, 30 insertions(+)

diff --git a/drivers/clk/sunxi/clk-factors.c b/drivers/clk/sunxi/clk-factors.c
index 2057c8ac648f..f8f45a7ffcd7 100644
--- a/drivers/clk/sunxi/clk-factors.c
+++ b/drivers/clk/sunxi/clk-factors.c
@@ -39,6 +39,25 @@
 #define FACTOR_SET(bit, len, reg, val) \
 	(((reg) & CLRMASK(len, bit)) | (val << (bit)))
 
+static int clk_factors_prepare(struct clk_hw *hw)
+{
+	struct clk_factors *factors = to_clk_factors(hw);
+	unsigned long rate = __clk_get_rate(hw->clk);
+	unsigned long new_rate;
+
+	if (factors->min_rate && (rate < factors->min_rate))
+		new_rate = factors->min_rate;
+	else if (factors->max_rate && (rate > factors->max_rate))
+		new_rate = factors->max_rate;
+	else
+		return 0;
+
+	pr_info("Initial rate of %s out of boundaries.. Changing rate from %lu to %lu\n",
+		__clk_get_name(hw->clk), rate, new_rate);
+
+	return clk_set_rate(hw->clk, rate);
+}
+
 static unsigned long clk_factors_recalc_rate(struct clk_hw *hw,
 					     unsigned long parent_rate)
 {
@@ -74,6 +93,11 @@ static long clk_factors_round_rate(struct clk_hw *hw, unsigned long rate,
 	factors->get_factors((u32 *)&rate, (u32)*parent_rate,
 			     NULL, NULL, NULL, NULL);
 
+	if (factors->max_rate && (rate > factors->max_rate))
+		rate = factors->max_rate;
+	else if (factors->min_rate && (rate < factors->min_rate))
+		rate = factors->min_rate;
+
 	return rate;
 }
 
@@ -123,6 +147,9 @@ static int clk_factors_set_rate(struct clk_hw *hw, unsigned long rate,
 
 	factors->get_factors((u32 *)&rate, (u32)parent_rate, &n, &k, &m, &p);
 
+	if ((rate > factors->max_rate) || (rate < factors->min_rate))
+		return -EINVAL;
+
 	if (factors->lock)
 		spin_lock_irqsave(factors->lock, flags);
 
@@ -149,6 +176,7 @@ static int clk_factors_set_rate(struct clk_hw *hw, unsigned long rate,
 
 const struct clk_ops clk_factors_ops = {
 	.determine_rate = clk_factors_determine_rate,
+	.prepare = clk_factors_prepare,
 	.recalc_rate = clk_factors_recalc_rate,
 	.round_rate = clk_factors_round_rate,
 	.set_rate = clk_factors_set_rate,
diff --git a/drivers/clk/sunxi/clk-factors.h b/drivers/clk/sunxi/clk-factors.h
index d2d0efa39379..2b8579a24039 100644
--- a/drivers/clk/sunxi/clk-factors.h
+++ b/drivers/clk/sunxi/clk-factors.h
@@ -24,6 +24,8 @@ struct clk_factors {
 	struct clk_factors_config *config;
 	void (*get_factors) (u32 *rate, u32 parent, u8 *n, u8 *k, u8 *m, u8 *p);
 	spinlock_t *lock;
+	unsigned long max_rate;
+	unsigned long min_rate;
 };
 
 extern const struct clk_ops clk_factors_ops;
-- 
2.0.1




More information about the linux-arm-kernel mailing list