public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/5] clk: ingenic: Add support for divider tables
@ 2019-05-02 21:24 Paul Cercueil
  2019-05-02 21:24 ` [PATCH 2/5] clk: ingenic/jz4740: Fix incorrect dividers for main clocks Paul Cercueil
                   ` (4 more replies)
  0 siblings, 5 replies; 11+ messages in thread
From: Paul Cercueil @ 2019-05-02 21:24 UTC (permalink / raw)
  To: Michael Turquette, Stephen Boyd
  Cc: linux-clk, linux-kernel, od, Paul Cercueil

Some clocks provided on Ingenic SoCs have dividers, whose hardware value
as written in the register cannot be expressed as an affine function
to the actual divider value.

For instance, for the CPU clock on the JZ4770, the dividers are coded as
follows:

    ------------------
    | Bits     | Div |
    ------------------
    | 0  0  0  |  1  |
    | 0  0  1  |  2  |
    | 0  1  0  |  3  |
    | 0  1  1  |  4  |
    | 1  0  0  |  6  |
    | 1  0  1  |  8  |
    | 1  1  0  | 12  |
    ------------------

To support this setup, we introduce a new field in the
ingenic_cgu_div_info structure that allows to specify the divider table.

Signed-off-by: Paul Cercueil <paul@crapouillou.net>
---
 drivers/clk/ingenic/cgu.c | 41 +++++++++++++++++++++++++++++++++------
 drivers/clk/ingenic/cgu.h |  3 +++
 2 files changed, 38 insertions(+), 6 deletions(-)

diff --git a/drivers/clk/ingenic/cgu.c b/drivers/clk/ingenic/cgu.c
index 510b685212d3..6a8c4fb0f6d5 100644
--- a/drivers/clk/ingenic/cgu.c
+++ b/drivers/clk/ingenic/cgu.c
@@ -383,8 +383,11 @@ ingenic_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
 		div_reg = readl(cgu->base + clk_info->div.reg);
 		div = (div_reg >> clk_info->div.shift) &
 		      GENMASK(clk_info->div.bits - 1, 0);
-		div += 1;
-		div *= clk_info->div.div;
+
+		if (clk_info->div.div_table)
+			div = clk_info->div.div_table[div];
+		else
+			div = (div + 1) * clk_info->div.div;
 
 		rate /= div;
 	} else if (clk_info->type & CGU_CLK_FIXDIV) {
@@ -394,16 +397,37 @@ ingenic_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
 	return rate;
 }
 
+static unsigned int
+ingenic_clk_calc_hw_div(const struct ingenic_cgu_clk_info *clk_info,
+			unsigned int div)
+{
+	unsigned int i;
+
+	for (i = 0; i < (1 << clk_info->div.bits)
+				&& clk_info->div.div_table[i]; i++) {
+		if (clk_info->div.div_table[i] >= div)
+			return i;
+	}
+
+	return i - 1;
+}
+
 static unsigned
 ingenic_clk_calc_div(const struct ingenic_cgu_clk_info *clk_info,
 		     unsigned long parent_rate, unsigned long req_rate)
 {
-	unsigned div;
+	unsigned int div, hw_div;
 
 	/* calculate the divide */
 	div = DIV_ROUND_UP(parent_rate, req_rate);
 
-	/* and impose hardware constraints */
+	if (clk_info->div.div_table) {
+		hw_div = ingenic_clk_calc_hw_div(clk_info, div);
+
+		return clk_info->div.div_table[hw_div];
+	}
+
+	/* Impose hardware constraints */
 	div = min_t(unsigned, div, 1 << clk_info->div.bits);
 	div = max_t(unsigned, div, 1);
 
@@ -446,7 +470,7 @@ ingenic_clk_set_rate(struct clk_hw *hw, unsigned long req_rate,
 	const struct ingenic_cgu_clk_info *clk_info;
 	const unsigned timeout = 100;
 	unsigned long rate, flags;
-	unsigned div, i;
+	unsigned int hw_div, div, i;
 	u32 reg, mask;
 	int ret = 0;
 
@@ -459,13 +483,18 @@ ingenic_clk_set_rate(struct clk_hw *hw, unsigned long req_rate,
 		if (rate != req_rate)
 			return -EINVAL;
 
+		if (clk_info->div.div_table)
+			hw_div = ingenic_clk_calc_hw_div(clk_info, div);
+		else
+			hw_div = ((div / clk_info->div.div) - 1);
+
 		spin_lock_irqsave(&cgu->lock, flags);
 		reg = readl(cgu->base + clk_info->div.reg);
 
 		/* update the divide */
 		mask = GENMASK(clk_info->div.bits - 1, 0);
 		reg &= ~(mask << clk_info->div.shift);
-		reg |= ((div / clk_info->div.div) - 1) << clk_info->div.shift;
+		reg |= hw_div << clk_info->div.shift;
 
 		/* clear the stop bit */
 		if (clk_info->div.stop_bit != -1)
diff --git a/drivers/clk/ingenic/cgu.h b/drivers/clk/ingenic/cgu.h
index e12716d8ce3c..8dcd83aeab84 100644
--- a/drivers/clk/ingenic/cgu.h
+++ b/drivers/clk/ingenic/cgu.h
@@ -88,6 +88,8 @@ struct ingenic_cgu_mux_info {
  *          isn't one
  * @busy_bit: the index of the busy bit within reg, or -1 if there isn't one
  * @stop_bit: the index of the stop bit within reg, or -1 if there isn't one
+ * @div_table: optional table to map the value read from the register to the
+ *             actual divider value
  */
 struct ingenic_cgu_div_info {
 	unsigned reg;
@@ -97,6 +99,7 @@ struct ingenic_cgu_div_info {
 	s8 ce_bit;
 	s8 busy_bit;
 	s8 stop_bit;
+	const u8 *div_table;
 };
 
 /**
-- 
2.21.0.593.g511ec345e18


^ permalink raw reply related	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2019-06-08 10:18 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2019-05-02 21:24 [PATCH 1/5] clk: ingenic: Add support for divider tables Paul Cercueil
2019-05-02 21:24 ` [PATCH 2/5] clk: ingenic/jz4740: Fix incorrect dividers for main clocks Paul Cercueil
2019-06-07 21:03   ` Stephen Boyd
2019-06-08 10:18     ` Paul Cercueil
2019-05-02 21:25 ` [PATCH 3/5] clk: ingenic/jz4770: " Paul Cercueil
2019-06-07 21:03   ` Stephen Boyd
2019-05-02 21:25 ` [PATCH 4/5] clk: ingenic/jz4725b: " Paul Cercueil
2019-06-07 21:03   ` Stephen Boyd
2019-05-02 21:25 ` [PATCH 5/5] clk: ingenic/jz4725b: Fix "pll half" divider not read/written properly Paul Cercueil
2019-06-07 21:03   ` Stephen Boyd
2019-06-07 21:03 ` [PATCH 1/5] clk: ingenic: Add support for divider tables Stephen Boyd

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox