From 8477a566b36aaae77e53a9949f963ce6ebad55fe Mon Sep 17 00:00:00 2001
From: Ondrej Jirman <megous@megous.com>
Date: Thu, 12 Jan 2017 16:34:57 +0100
Subject: [PATCH] clk: sunxi-ng: Set maximum M = 1 for H3 pll-cpux clock

When using M factor greater than 1 system is experiencing
occasional lockups.

This change was verified to fix lockups with PLL stress
tester available at https://github.com/megous/h3-firmware.

Note that M factor must not be used outside the kernel
either, so for example u-boot needs a similar patch.
---
 drivers/clk/sunxi-ng/ccu-sun8i-h3.c | 24 +++++++++++++++---------
 1 file changed, 15 insertions(+), 9 deletions(-)

diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-h3.c b/drivers/clk/sunxi-ng/ccu-sun8i-h3.c
index 77ed0b0ba6819..8d47742def49d 100644
--- a/drivers/clk/sunxi-ng/ccu-sun8i-h3.c
+++ b/drivers/clk/sunxi-ng/ccu-sun8i-h3.c
@@ -30,15 +30,21 @@
 
 #include "ccu-sun8i-h3.h"
 
-static SUNXI_CCU_NKMP_WITH_GATE_LOCK(pll_cpux_clk, "pll-cpux",
-				     "osc24M", 0x000,
-				     8, 5,	/* N */
-				     4, 2,	/* K */
-				     0, 2,	/* M */
-				     16, 2,	/* P */
-				     BIT(31),	/* gate */
-				     BIT(28),	/* lock */
-				     CLK_SET_RATE_UNGATE);
+static struct ccu_nkmp pll_cpux_clk = {
+	.enable		= BIT(31),
+	.lock		= BIT(28),
+	.n		= _SUNXI_CCU_MULT(8, 5),
+	.k		= _SUNXI_CCU_MULT(4, 2),
+	.m		= _SUNXI_CCU_DIV_MAX(0, 2, 1),
+	.p		= _SUNXI_CCU_DIV(16, 2),
+	.common		= {
+		.reg		= 0x000,
+		.hw.init	= CLK_HW_INIT("pll-cpux",
+					      "osc24M",
+					      &ccu_nkmp_ops,
+					      CLK_SET_RATE_UNGATE),
+	},
+};
 
 /*
  * The Audio PLL is supposed to have 4 outputs: 3 fixed factors from
From c313a0ac340bf1131d475527171764dc49901895 Mon Sep 17 00:00:00 2001
From: Ondrej Jirman <megous@megous.com>
Date: Wed, 5 Apr 2017 15:43:48 +0200
Subject: [PATCH] clk: sunxi-ng: Limit pll_cpux P factor for rates > 288MHz on
 H3

Datasheet for H3 mandates that CPUX PLL must not use postdivider
(P factor must be 1) for clock rates above 288MHz.

Signed-off-by: Ondrej Jirman <megous@megous.com>
---
 drivers/clk/sunxi-ng/ccu-sun8i-h3.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-h3.c b/drivers/clk/sunxi-ng/ccu-sun8i-h3.c
index 8d47742def49d..7cc9467f373f2 100644
--- a/drivers/clk/sunxi-ng/ccu-sun8i-h3.c
+++ b/drivers/clk/sunxi-ng/ccu-sun8i-h3.c
@@ -37,6 +37,7 @@ static struct ccu_nkmp pll_cpux_clk = {
 	.k		= _SUNXI_CCU_MULT(4, 2),
 	.m		= _SUNXI_CCU_DIV_MAX(0, 2, 1),
 	.p		= _SUNXI_CCU_DIV(16, 2),
+	.max_rate_for_p	= 288000000,
 	.common		= {
 		.reg		= 0x000,
 		.hw.init	= CLK_HW_INIT("pll-cpux",
From f07a20e4ce93e7e0f333f9f6e57f14fd4ab66abd Mon Sep 17 00:00:00 2001
From: Ondrej Jirman <megous@megous.com>
Date: Thu, 12 Jan 2017 16:37:24 +0100
Subject: [PATCH] clk: sunxi-ng: Allow to limit the use of NKMP clock's P
 factor

Some SoCs mandate the maximum clock rate for which the use
of postdivider P factor is allowed. Allow to configure maximum
clock rate.

Signed-off-by: Ondrej Jirman <megous@megous.com>
---
 drivers/clk/sunxi-ng/ccu_nkmp.c | 13 ++++++++-----
 drivers/clk/sunxi-ng/ccu_nkmp.h |  1 +
 2 files changed, 9 insertions(+), 5 deletions(-)

diff --git a/drivers/clk/sunxi-ng/ccu_nkmp.c b/drivers/clk/sunxi-ng/ccu_nkmp.c
index ebd9436d2c7cd..96dbc543b2cbc 100644
--- a/drivers/clk/sunxi-ng/ccu_nkmp.c
+++ b/drivers/clk/sunxi-ng/ccu_nkmp.c
@@ -33,16 +33,19 @@ static unsigned long ccu_nkmp_calc_rate(unsigned long parent,
 }
 
 static void ccu_nkmp_find_best(unsigned long parent, unsigned long rate,
-			       struct _ccu_nkmp *nkmp)
+			       struct _ccu_nkmp *nkmp, struct ccu_nkmp *_nkmp)
 {
 	unsigned long best_rate = 0;
 	unsigned long best_n = 0, best_k = 0, best_m = 0, best_p = 0;
-	unsigned long _n, _k, _m, _p;
+	unsigned long _n, _k, _m, _p, _max_p;
+
+	_max_p = (_nkmp->max_rate_for_p == 0 || rate <= _nkmp->max_rate_for_p) ?
+		nkmp->max_p : nkmp->min_p;
 
 	for (_k = nkmp->min_k; _k <= nkmp->max_k; _k++) {
 		for (_n = nkmp->min_n; _n <= nkmp->max_n; _n++) {
 			for (_m = nkmp->min_m; _m <= nkmp->max_m; _m++) {
-				for (_p = nkmp->min_p; _p <= nkmp->max_p; _p <<= 1) {
+				for (_p = nkmp->min_p; _p <= _max_p; _p <<= 1) {
 					unsigned long tmp_rate;
 
 					tmp_rate = ccu_nkmp_calc_rate(parent,
@@ -146,7 +149,7 @@ static long ccu_nkmp_round_rate(struct clk_hw *hw, unsigned long rate,
 	_nkmp.min_p = 1;
 	_nkmp.max_p = nkmp->p.max ?: 1 << ((1 << nkmp->p.width) - 1);
 
-	ccu_nkmp_find_best(*parent_rate, rate, &_nkmp);
+	ccu_nkmp_find_best(*parent_rate, rate, &_nkmp, nkmp);
 
 	rate = ccu_nkmp_calc_rate(*parent_rate, _nkmp.n, _nkmp.k,
 				  _nkmp.m, _nkmp.p);
@@ -177,7 +180,7 @@ static int ccu_nkmp_set_rate(struct clk_hw *hw, unsigned long rate,
 	_nkmp.min_p = 1;
 	_nkmp.max_p = nkmp->p.max ?: 1 << ((1 << nkmp->p.width) - 1);
 
-	ccu_nkmp_find_best(parent_rate, rate, &_nkmp);
+	ccu_nkmp_find_best(parent_rate, rate, &_nkmp, nkmp);
 
 	n_mask = GENMASK(nkmp->n.width + nkmp->n.shift - 1, nkmp->n.shift);
 	k_mask = GENMASK(nkmp->k.width + nkmp->k.shift - 1, nkmp->k.shift);
diff --git a/drivers/clk/sunxi-ng/ccu_nkmp.h b/drivers/clk/sunxi-ng/ccu_nkmp.h
index 6940503e7fc46..bbea3e5ed6fba 100644
--- a/drivers/clk/sunxi-ng/ccu_nkmp.h
+++ b/drivers/clk/sunxi-ng/ccu_nkmp.h
@@ -33,6 +33,7 @@ struct ccu_nkmp {
 	struct ccu_mult_internal	k;
 	struct ccu_div_internal		m;
 	struct ccu_div_internal		p;
+	unsigned long max_rate_for_p;
 
 	unsigned int		fixed_post_div;
 
