Skip to contentSkip to conversation

clk: rockchip: allow adjusting pll rates on init

PATCH Superseded Heiko Stuebner 3 participants 8 messages Nov 20, 2014 – Nov 25, 2014

Subsystems (2)
lore
Share & tools
lore

Heiko Stuebner

author maintainer
#0 lore raw

v2 cover clk: rockchip: allow adjusting pll rates on init

It was found that some firmware versions initialized the pll rates using suboptimal parameters to achieve frequencies the actual rate table contained better parameters for.

Therefore this series first adds the possibility to create pll-specific flags and then adds a flag to adjust the pll rate on init to the parameters from the rate table.

changes since v1:
- move population of pll_mux before registering the core pll clock null pointer exception in the _set_rate callback when called from init

Heiko Stuebner (4):
clk: rockchip: add ability to specify pll-specific flags
clk: rockchip: setup pll_mux data earlier
clk: rockchip: add optional sync to pll rate parameters
clk: rockchip: add ROCKCHIP_PLL_SYNC_RATE flag to some plls

 drivers/clk/rockchip/clk-pll.c    | 81 +++++++++++++++++++++++++++++++--------
 drivers/clk/rockchip/clk-rk3188.c |  8 ++--
 drivers/clk/rockchip/clk-rk3288.c | 10 ++---
 drivers/clk/rockchip/clk.c        |  3 +-
 drivers/clk/rockchip/clk.h        | 13 ++++++-
 5 files changed, 88 insertions(+), 27 deletions(-)
Signature · 1 line
2.1.1
author maintainer
#1 lore raw

PATCH v2 1/4 clk: rockchip: add ability to specify pll-specific flags

This adds a flag parameter to plls that allows us to create special flags to tweak the behaviour of the plls if necessary.

---
 drivers/clk/rockchip/clk-pll.c    |  4 +++-
 drivers/clk/rockchip/clk-rk3188.c |  8 ++++----
 drivers/clk/rockchip/clk-rk3288.c | 10 +++++-----
 drivers/clk/rockchip/clk.c        |  3 ++-
 drivers/clk/rockchip/clk.h        |  7 +++++--
 5 files changed, 19 insertions(+), 13 deletions(-)
drivers/clk/rockchip/clk-pll.c +3 -1
index a3e886a..feb9cad 100644
@@ -39,6 +39,7 @@ struct rockchip_clk_pll {
 	int			lock_offset;
 	unsigned int		lock_shift;
 	enum rockchip_pll_type	type;
+	u8			flags;
 	const struct rockchip_pll_rate_table *rate_table;
 	unsigned int		rate_count;
 	spinlock_t		*lock;
@@ -282,7 +283,7 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type,
 		void __iomem *base, int con_offset, int grf_lock_offset,
 		int lock_shift, int mode_offset, int mode_shift,
 		struct rockchip_pll_rate_table *rate_table,
-		spinlock_t *lock)
+		u8 clk_pll_flags, spinlock_t *lock)
 {
 	const char *pll_parents[3];
 	struct clk_init_data init;
@@ -345,6 +346,7 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type,
 	pll->reg_base = base + con_offset;
 	pll->lock_offset = grf_lock_offset;
 	pll->lock_shift = lock_shift;
+	pll->flags = clk_pll_flags;
 	pll->lock = lock;
 
 	pll_clk = clk_register(NULL, &pll->hw);
drivers/clk/rockchip/clk-rk3188.c +4 -4
index e6cd483..6af5d7f 100644
@@ -212,13 +212,13 @@ PNAME(mux_sclk_macref_p)	= { "mac_src", "ext_rmii" };
 
 static struct rockchip_pll_clock rk3188_pll_clks[] __initdata = {
 	[apll] = PLL(pll_rk3066, PLL_APLL, "apll", mux_pll_p, 0, RK2928_PLL_CON(0),
-		     RK2928_MODE_CON, 0, 6, rk3188_pll_rates),
+		     RK2928_MODE_CON, 0, 6, 0, rk3188_pll_rates),
 	[dpll] = PLL(pll_rk3066, PLL_DPLL, "dpll", mux_pll_p, 0, RK2928_PLL_CON(4),
-		     RK2928_MODE_CON, 4, 5, NULL),
+		     RK2928_MODE_CON, 4, 5, 0, NULL),
 	[cpll] = PLL(pll_rk3066, PLL_CPLL, "cpll", mux_pll_p, 0, RK2928_PLL_CON(8),
-		     RK2928_MODE_CON, 8, 7, rk3188_pll_rates),
+		     RK2928_MODE_CON, 8, 7, 0, rk3188_pll_rates),
 	[gpll] = PLL(pll_rk3066, PLL_GPLL, "gpll", mux_pll_p, 0, RK2928_PLL_CON(12),
-		     RK2928_MODE_CON, 12, 8, rk3188_pll_rates),
+		     RK2928_MODE_CON, 12, 8, 0, rk3188_pll_rates),
 };
 
 #define MFLAGS CLK_MUX_HIWORD_MASK
drivers/clk/rockchip/clk-rk3288.c +5 -5
index 174589c..2d31a22 100644
@@ -202,15 +202,15 @@ PNAME(mux_hsicphy12m_p)		= { "hsicphy12m_xin12m", "hsicphy12m_usbphy" };
 
 static struct rockchip_pll_clock rk3288_pll_clks[] __initdata = {
 	[apll] = PLL(pll_rk3066, PLL_APLL, "apll", mux_pll_p, 0, RK3288_PLL_CON(0),
-		     RK3288_MODE_CON, 0, 6, rk3288_pll_rates),
+		     RK3288_MODE_CON, 0, 6, 0, rk3288_pll_rates),
 	[dpll] = PLL(pll_rk3066, PLL_DPLL, "dpll", mux_pll_p, 0, RK3288_PLL_CON(4),
-		     RK3288_MODE_CON, 4, 5, NULL),
+		     RK3288_MODE_CON, 4, 5, 0, NULL),
 	[cpll] = PLL(pll_rk3066, PLL_CPLL, "cpll", mux_pll_p, 0, RK3288_PLL_CON(8),
-		     RK3288_MODE_CON, 8, 7, rk3288_pll_rates),
+		     RK3288_MODE_CON, 8, 7, 0, rk3288_pll_rates),
 	[gpll] = PLL(pll_rk3066, PLL_GPLL, "gpll", mux_pll_p, 0, RK3288_PLL_CON(12),
-		     RK3288_MODE_CON, 12, 8, rk3288_pll_rates),
+		     RK3288_MODE_CON, 12, 8, 0, rk3288_pll_rates),
 	[npll] = PLL(pll_rk3066, PLL_NPLL, "npll",  mux_pll_p, 0, RK3288_PLL_CON(16),
-		     RK3288_MODE_CON, 14, 9, rk3288_pll_rates),
+		     RK3288_MODE_CON, 14, 9, 0, rk3288_pll_rates),
 };
 
 static struct clk_div_table div_hclk_cpu_t[] = {
drivers/clk/rockchip/clk.c +2 -1
index dec6f8d6..3b8f26e 100644
@@ -199,7 +199,8 @@ void __init rockchip_clk_register_plls(struct rockchip_pll_clock *list,
 				list->parent_names, list->num_parents,
 				reg_base, list->con_offset, grf_lock_offset,
 				list->lock_shift, list->mode_offset,
-				list->mode_shift, list->rate_table, &clk_lock);
+				list->mode_shift, list->rate_table,
+				list->pll_flags, &clk_lock);
 		if (IS_ERR(clk)) {
 			pr_err("%s: failed to register clock %s\n", __func__,
 				list->name);
drivers/clk/rockchip/clk.h +5 -2
index 6baf665..eefd39a 100644
@@ -90,6 +90,7 @@ struct rockchip_pll_rate_table {
  * @mode_shift: offset inside the mode-register for the mode of this pll.
  * @lock_shift: offset inside the lock register for the lock status.
  * @type: Type of PLL to be registered.
+ * @pll_flags: hardware-specific flags
  * @rate_table: Table of usable pll rates
  */
 struct rockchip_pll_clock {
@@ -103,11 +104,12 @@ struct rockchip_pll_clock {
 	int			mode_shift;
 	int			lock_shift;
 	enum rockchip_pll_type	type;
+	u8			pll_flags;
 	struct rockchip_pll_rate_table *rate_table;
 };
 
 #define PLL(_type, _id, _name, _pnames, _flags, _con, _mode, _mshift,	\
-		_lshift, _rtable)					\
+		_lshift, _pflags, _rtable)				\
 	{								\
 		.id		= _id,					\
 		.type		= _type,				\
@@ -119,6 +121,7 @@ struct rockchip_pll_clock {
 		.mode_offset	= _mode,				\
 		.mode_shift	= _mshift,				\
 		.lock_shift	= _lshift,				\
+		.pll_flags	= _pflags,				\
 		.rate_table	= _rtable,				\
 	}
 
@@ -127,7 +130,7 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type,
 		void __iomem *base, int con_offset, int grf_lock_offset,
 		int lock_shift, int reg_mode, int mode_shift,
 		struct rockchip_pll_rate_table *rate_table,
-		spinlock_t *lock);
+		u8 clk_pll_flags, spinlock_t *lock);
 
 struct rockchip_cpuclk_clksel {
 	int reg;
Signature · 1 line
2.1.1
author maintainer
#2 lore raw

PATCH v2 2/4 clk: rockchip: setup pll_mux data earlier

In reply to Heiko Stuebner

In some cases we might need to access the data of the pll mux before the actual mux gets registered - like in the following patch adding an init-callback.

Therefore populate pll_mux before registering the core pll-clock.

---
 drivers/clk/rockchip/clk-pll.c | 27 +++++++++++++--------------
 1 file changed, 13 insertions(+), 14 deletions(-)
drivers/clk/rockchip/clk-pll.c +13 -14
index feb9cad..1bb6891 100644
@@ -349,6 +349,19 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type,
 	pll->flags = clk_pll_flags;
 	pll->lock = lock;
 
+	/* create the mux on top of the real pll */
+	pll->pll_mux_ops = &clk_mux_ops;
+	pll_mux = &pll->pll_mux;
+	pll_mux->reg = base + mode_offset;
+	pll_mux->shift = mode_shift;
+	pll_mux->mask = PLL_MODE_MASK;
+	pll_mux->flags = 0;
+	pll_mux->lock = lock;
+	pll_mux->hw.init = &init;
+
+	if (pll_type == pll_rk3066)
+		pll_mux->flags |= CLK_MUX_HIWORD_MASK;
+
 	pll_clk = clk_register(NULL, &pll->hw);
 	if (IS_ERR(pll_clk)) {
 		pr_err("%s: failed to register pll clock %s : %ld\n",
@@ -357,10 +370,6 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type,
 		goto err_pll;
 	}
 
-	/* create the mux on top of the real pll */
-	pll->pll_mux_ops = &clk_mux_ops;
-	pll_mux = &pll->pll_mux;
-
 	/* the actual muxing is xin24m, pll-output, xin32k */
 	pll_parents[0] = parent_names[0];
 	pll_parents[1] = pll_name;
@@ -372,16 +381,6 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type,
 	init.parent_names = pll_parents;
 	init.num_parents = ARRAY_SIZE(pll_parents);
 
-	pll_mux->reg = base + mode_offset;
-	pll_mux->shift = mode_shift;
-	pll_mux->mask = PLL_MODE_MASK;
-	pll_mux->flags = 0;
-	pll_mux->lock = lock;
-	pll_mux->hw.init = &init;
-
-	if (pll_type == pll_rk3066)
-		pll_mux->flags |= CLK_MUX_HIWORD_MASK;
-
 	mux_clk = clk_register(NULL, &pll_mux->hw);
 	if (IS_ERR(mux_clk))
 		goto err_mux;
Signature · 1 line
2.1.1
author maintainer
#3 lore raw

PATCH v2 3/4 clk: rockchip: add optional sync to pll rate parameters

In reply to Heiko Stuebner

In some cases firmware brings up plls with different parameters than the ones noted in the rate table for the specific frequency. These firmware-selected parameters are worse than the tested ones in the pll rate tables but cannot be changed by a simple clk_set_rate call when the rate stays the same.

Therefore add a ROCKCHIP_PLL_SYNC_RATE flag and implement an init callback that checks the runtime-parameters against the matching rate table entry and adjusts them to the table-ones if necessary.

If no rate table is set or the current rate does not match any rate-table entry no changes are made.

Being able to limit this adjustment to specific plls is necessary to not touch the ones supplying core components like the apll and dpll supplying the armcores and dram.

---
 drivers/clk/rockchip/clk-pll.c | 50 ++++++++++++++++++++++++++++++++++++++++++
 drivers/clk/rockchip/clk.h     |  6 +++++
 2 files changed, 56 insertions(+)
drivers/clk/rockchip/clk-pll.c +50 -0
index 1bb6891..f8d3baf 100644
@@ -258,6 +258,55 @@ static int rockchip_rk3066_pll_is_enabled(struct clk_hw *hw)
 	return !(pllcon & RK3066_PLLCON3_PWRDOWN);
 }
 
+static void rockchip_rk3066_pll_init(struct clk_hw *hw)
+{
+	struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
+	const struct rockchip_pll_rate_table *rate;
+	unsigned int nf, nr, no, bwadj;
+	unsigned long drate;
+	u32 pllcon;
+
+	if (!(pll->flags & ROCKCHIP_PLL_SYNC_RATE))
+		return;
+
+	drate = __clk_get_rate(hw->clk);
+	rate = rockchip_get_pll_settings(pll, drate);
+
+	/* when no rate setting for the current rate, rely on clk_set_rate */
+	if (!rate)
+		return;
+
+	pllcon = readl_relaxed(pll->reg_base + RK3066_PLLCON(0));
+	nr = ((pllcon >> RK3066_PLLCON0_NR_SHIFT) & RK3066_PLLCON0_NR_MASK) + 1;
+	no = ((pllcon >> RK3066_PLLCON0_OD_SHIFT) & RK3066_PLLCON0_OD_MASK) + 1;
+
+	pllcon = readl_relaxed(pll->reg_base + RK3066_PLLCON(1));
+	nf = ((pllcon >> RK3066_PLLCON1_NF_SHIFT) & RK3066_PLLCON1_NF_MASK) + 1;
+
+	pllcon = readl_relaxed(pll->reg_base + RK3066_PLLCON(2));
+	bwadj = (pllcon >> RK3066_PLLCON2_BWADJ_SHIFT) & RK3066_PLLCON2_BWADJ_MASK;
+
+	pr_debug("%s: pll %s@%lu: nr (%d:%d); no (%d:%d); nf(%d:%d), bwadj(%d:%d)\n",
+		 __func__, __clk_get_name(hw->clk), drate, rate->nr, nr,
+		rate->no, no, rate->nf, nf, rate->bwadj, bwadj);
+	if (rate->nr != nr || rate->no != no || rate->nf != nf
+					     || rate->bwadj != bwadj) {
+		struct clk *parent = __clk_get_parent(hw->clk);
+		unsigned long prate;
+
+		if (!parent) {
+			pr_warn("%s: parent of %s not available\n",
+				__func__, __clk_get_name(hw->clk));
+			return;
+		}
+
+		pr_debug("%s: pll %s: rate params do not match rate table, adjusting\n",
+			 __func__, __clk_get_name(hw->clk));
+		prate = __clk_get_rate(parent);
+		rockchip_rk3066_pll_set_rate(hw, drate, prate);
+	}
+}
+
 static const struct clk_ops rockchip_rk3066_pll_clk_norate_ops = {
 	.recalc_rate = rockchip_rk3066_pll_recalc_rate,
 	.enable = rockchip_rk3066_pll_enable,
@@ -272,6 +321,7 @@ static const struct clk_ops rockchip_rk3066_pll_clk_ops = {
 	.enable = rockchip_rk3066_pll_enable,
 	.disable = rockchip_rk3066_pll_disable,
 	.is_enabled = rockchip_rk3066_pll_is_enabled,
+	.init = rockchip_rk3066_pll_init,
 };
 
 /*
drivers/clk/rockchip/clk.h +6 -0
index eefd39a..dc94681 100644
@@ -92,6 +92,10 @@ struct rockchip_pll_rate_table {
  * @type: Type of PLL to be registered.
  * @pll_flags: hardware-specific flags
  * @rate_table: Table of usable pll rates
+ *
+ * Flags:
+ * ROCKCHIP_PLL_SYNC_RATE - check rate parameters to match against the
+ *	rate_table parameters and ajust them if necessary.
  */
 struct rockchip_pll_clock {
 	unsigned int		id;
@@ -108,6 +112,8 @@ struct rockchip_pll_clock {
 	struct rockchip_pll_rate_table *rate_table;
 };
 
+#define ROCKCHIP_PLL_SYNC_RATE		BIT(0)
+
 #define PLL(_type, _id, _name, _pnames, _flags, _con, _mode, _mshift,	\
 		_lshift, _pflags, _rtable)				\
 	{								\
Signature · 1 line
2.1.1
author maintainer
#4 lore raw

PATCH v2 4/4 clk: rockchip: add ROCKCHIP_PLL_SYNC_RATE flag to some plls

In reply to Heiko Stuebner

Add the new flag to gpll and cpll on rk3188 and similar and to gpll, cpll and npll on rk3288.

---
 drivers/clk/rockchip/clk-rk3188.c | 4 ++--
 drivers/clk/rockchip/clk-rk3288.c | 6 +++---
 2 files changed, 5 insertions(+), 5 deletions(-)
drivers/clk/rockchip/clk-rk3188.c +2 -2
index 6af5d7f..37a1988 100644
@@ -216,9 +216,9 @@ static struct rockchip_pll_clock rk3188_pll_clks[] __initdata = {
 	[dpll] = PLL(pll_rk3066, PLL_DPLL, "dpll", mux_pll_p, 0, RK2928_PLL_CON(4),
 		     RK2928_MODE_CON, 4, 5, 0, NULL),
 	[cpll] = PLL(pll_rk3066, PLL_CPLL, "cpll", mux_pll_p, 0, RK2928_PLL_CON(8),
-		     RK2928_MODE_CON, 8, 7, 0, rk3188_pll_rates),
+		     RK2928_MODE_CON, 8, 7, ROCKCHIP_PLL_SYNC_RATE, rk3188_pll_rates),
 	[gpll] = PLL(pll_rk3066, PLL_GPLL, "gpll", mux_pll_p, 0, RK2928_PLL_CON(12),
-		     RK2928_MODE_CON, 12, 8, 0, rk3188_pll_rates),
+		     RK2928_MODE_CON, 12, 8, ROCKCHIP_PLL_SYNC_RATE, rk3188_pll_rates),
 };
 
 #define MFLAGS CLK_MUX_HIWORD_MASK
drivers/clk/rockchip/clk-rk3288.c +3 -3
index 2d31a22..ad8a27a 100644
@@ -206,11 +206,11 @@ static struct rockchip_pll_clock rk3288_pll_clks[] __initdata = {
 	[dpll] = PLL(pll_rk3066, PLL_DPLL, "dpll", mux_pll_p, 0, RK3288_PLL_CON(4),
 		     RK3288_MODE_CON, 4, 5, 0, NULL),
 	[cpll] = PLL(pll_rk3066, PLL_CPLL, "cpll", mux_pll_p, 0, RK3288_PLL_CON(8),
-		     RK3288_MODE_CON, 8, 7, 0, rk3288_pll_rates),
+		     RK3288_MODE_CON, 8, 7, ROCKCHIP_PLL_SYNC_RATE, rk3288_pll_rates),
 	[gpll] = PLL(pll_rk3066, PLL_GPLL, "gpll", mux_pll_p, 0, RK3288_PLL_CON(12),
-		     RK3288_MODE_CON, 12, 8, 0, rk3288_pll_rates),
+		     RK3288_MODE_CON, 12, 8, ROCKCHIP_PLL_SYNC_RATE, rk3288_pll_rates),
 	[npll] = PLL(pll_rk3066, PLL_NPLL, "npll",  mux_pll_p, 0, RK3288_PLL_CON(16),
-		     RK3288_MODE_CON, 14, 9, 0, rk3288_pll_rates),
+		     RK3288_MODE_CON, 14, 9, ROCKCHIP_PLL_SYNC_RATE, rk3288_pll_rates),
 };
 
 static struct clk_div_table div_hclk_cpu_t[] = {
Signature · 1 line
2.1.1
#5 lore raw

v2 cover clk: rockchip: allow adjusting pll rates on init

In reply to Heiko Stuebner

Hi Heiko,

Quoted 18 lines · Heiko Stuebner

It was found that some firmware versions initialized the pll rates using suboptimal parameters to achieve frequencies the actual rate table contained better parameters for.

Therefore this series first adds the possibility to create pll-specific flags and then adds a flag to adjust the pll rate on init to the parameters from the rate table.

changes since v1:
- move population of pll_mux before registering the core pll clock null pointer exception in the _set_rate callback when called from init

Heiko Stuebner (4):
clk: rockchip: add ability to specify pll-specific flags
clk: rockchip: setup pll_mux data earlier
clk: rockchip: add optional sync to pll rate parameters
clk: rockchip: add ROCKCHIP_PLL_SYNC_RATE flag to some plls

Quoted 7 lines
  drivers/clk/rockchip/clk-pll.c    | 81 +++++++++++++++++++++++++++++++--------
  drivers/clk/rockchip/clk-rk3188.c |  8 ++--
  drivers/clk/rockchip/clk-rk3288.c | 10 ++---
  drivers/clk/rockchip/clk.c        |  3 +-
  drivers/clk/rockchip/clk.h        | 13 ++++++-
  5 files changed, 88 insertions(+), 27 deletions(-)

The PLLs may have different usage, so add the pll-specific flag make sense.

I have test this patch set on both rk3288 evb and rk3288 pinky.

For this series:

#6 lore raw

v2 cover clk: rockchip: allow adjusting pll rates on init

Quoting Kever Yang (2014-11-20 18:56:39)

Quoted 28 lines

Hi Heiko,

Quoted 18 lines · Heiko Stuebner

It was found that some firmware versions initialized the pll rates using suboptimal parameters to achieve frequencies the actual rate table contained better parameters for.

Therefore this series first adds the possibility to create pll-specific flags and then adds a flag to adjust the pll rate on init to the parameters from the rate table.

changes since v1:
- move population of pll_mux before registering the core pll clock null pointer exception in the _set_rate callback when called from init

Heiko Stuebner (4):
clk: rockchip: add ability to specify pll-specific flags
clk: rockchip: setup pll_mux data earlier
clk: rockchip: add optional sync to pll rate parameters
clk: rockchip: add ROCKCHIP_PLL_SYNC_RATE flag to some plls

Quoted 7 lines
  drivers/clk/rockchip/clk-pll.c    | 81 +++++++++++++++++++++++++++++++--------
  drivers/clk/rockchip/clk-rk3188.c |  8 ++--
  drivers/clk/rockchip/clk-rk3288.c | 10 ++---
  drivers/clk/rockchip/clk.c        |  3 +-
  drivers/clk/rockchip/clk.h        | 13 ++++++-
  5 files changed, 88 insertions(+), 27 deletions(-)
Quoted 8 lines

The PLLs may have different usage, so add the pll-specific flag make sense.

I have test this patch set on both rk3288 evb and rk3288 pinky.

For this series:

Looks good to me. I think rk3xxx might be the first user of the .init callback!

Regards,
Mike

author maintainer
#7 lore raw

v2 cover clk: rockchip: allow adjusting pll rates on init

In reply to Heiko Stuebner

Am Donnerstag, 20. November 2014, 20:38:49 schrieb Heiko Stuebner:

Quoted 12 lines

It was found that some firmware versions initialized the pll rates using suboptimal parameters to achieve frequencies the actual rate table contained better parameters for.

Therefore this series first adds the possibility to create pll-specific flags and then adds a flag to adjust the pll rate on init to the parameters from the rate table.

changes since v1:
- move population of pll_mux before registering the core pll clock null pointer exception in the _set_rate callback when called from init

Quoted 5 lines

Heiko Stuebner (4):
clk: rockchip: add ability to specify pll-specific flags
clk: rockchip: setup pll_mux data earlier
clk: rockchip: add optional sync to pll rate parameters
clk: rockchip: add ROCKCHIP_PLL_SYNC_RATE flag to some plls

with Mike's green-light, I've now added these to my clk branch with Kevers tags added.