mail archive of the barebox mailing list
 help / color / mirror / Atom feed
* [PATCH 0/6] clk: imx6: work around LDB hang caused by ERR009219
@ 2019-04-01 20:12 Ahmad Fatoum
  2019-04-01 20:12 ` [PATCH 1/6] clk: imx6: provide helper to check if video PLL post dividers work Ahmad Fatoum
                   ` (6 more replies)
  0 siblings, 7 replies; 9+ messages in thread
From: Ahmad Fatoum @ 2019-04-01 20:12 UTC (permalink / raw)
  To: barebox; +Cc: pza, lst, sha

Due to incorrect placement of the clock gate cell in the ldb_di[x]_clk
tree, the glitchy parent mux of ldb_di[x]_clk can cause a glitch to
enter the ldb_di_ipu_div divider[1]. If the divider gets locked up, no
ldb_di[x]_clk is generated, and the LVDS display will hang when the
ipu_di_clk is sourced from ldb_di_clk.

The glitch can happen if an assigned-clock-parents property sets
ldb_di[x]_clk's parent or in Linux versions prior to v4.10,
out-of-the-box because then Linux used to hardcode a
clk_set_parent(ldb_di[x]_sel, pll5_video_div) in clk-imx6.c.
The upstream kernel commit that added this reparenting
32f3b8da22 ("ARM i.MX6q: set the LDB serial clock parent to the video PLL")
made its way into barebox as well.

In barebox, this reparenting occurs whenever CONFIG_DRIVER_VIDEO_IMX_IPUV3
is defined and the CPU was either exactly a DualLite or of a revision
bigger than 1.0. The list of CPUs where the reparenting should happen
has not been updated as clk-imx6.c gained support for newer i.MX6 variants,
with the effect that the reparenting nowadays happens on the odd set of:
  - (Quad or Dual) and rev >1.0
  - DualLite
  - Solo and rev >1.0
  - (QuadPlus or DualPlus) rev >1.0

The erratum already has a Linux workaround. On barebox, it can currently
happen if there's an appropriate assigned-clock-parents property or the
reparenting happens (i.e. CONFIG_DRIVER_VIDEO_IMX_IPUV3 is selected on a
board with the aforementioned CPU/Revision pairs). Linux has removed the
reparenting along with the bug fix. If barebox did this, boards that
depended on the reparenting may be broken.

For this reason, the first four patches work around the erratum in a way
that the high level reparenting behavior remains as-is. The optional fifth
patch then drops the reparenting, same as Linux does, simplifying the code
and eventually improving the newly broken boards device tree as well (they
would need to spell out what clock parents they expect for their LDB
muxes instead of depending on a barebox quirk).

This series applies on top of current upstream/next, particularly
e0e87be220fa ("clk: mux: Support CLK_SET_RATE_NO_REPARENT flag").


Rundown
-------

PATCH 1/6 and 4/6 are cosmetic. The former is an exercise in De Morgan's
Laws and rewrites the condition to spell out exactly when the reparenting
to video PLL happens. The latter replaces some hardcoded constants from the
kernel patch with symbolic names.

PATCH 2/6 and 3/6 import the kernel patches that worked around the
erratum by:
  1) replacing the hardcoded clk_set_parent in a glitch-free manner
  2) parsing assigned-clock-parents of ldb_di_sel clocks specially and
     applying the reparenting using the glitch-free method above

PATCH 5/6 makes the ldb_di_sel clocks read-only to prevent
the generic clock code from trying to reconfigure the affected muxes
in a glitchy manner when it parses assigned-clock-parents.

These patches have been reordered from their upstream order to avoid
breaking boards which had the implicit assumption that the LDB clocks
have the video PLL as a parent.

Now that utmost care was invested into not breaking these boards,
RFC PATCH 6/6 breaks them by getting rid of the reparenting along with
its funny conditionals.

The line of thinking is:
If you explicitly use assigned-clock-parents to affect the LDB clocks,
this patch series will apply these without the lock up the erratum may
cause. Everything else is unchanged.

If you don't have that property, but CONFIG_DRIVER_IMX_IPUV3 is
selected, your LDB clocks would've locked up every hundred (?) or so
boots anyway, so you would've noticed, right?

Well, you didn't notice, so it should be Okay to just remove that
reparenting quirk. This simplifies the code and if the default of
mmdc_ch1_axi being the parent of the LDB clocks indeed breaks
you, you will bisect, compare /sys/kernel/debug/clk/clk_summary
and find PATCH 5/6 that tells you the exact device tree snippet you need
to copy into your device tree to restore the old behavior!

To save possibly affected mainline board authors the bisection effort,
they are CC'd in the last patch, so they can take a look. The
relevant boards are:
  - imx6qdl-zii-rdu2.dtsi
  - imx6qdl-udoo.dtsi
  - imx6qdl-mba6x.dtsi
  - imx6q-var-custom.dts
  - imx6q-guf-santaro.dts
  - imx6q-embedsky-e9.dtsi

[1] The ERR009219 erratum is explained in detail in EB821 ("LDB Clock
    Switch Procedure & i.MX6 Asynchronous Clock Switching Guidelines"):
    http://www.nxp.com/files/32bit/doc/eng_bulletin/EB821.pdf

Ahmad Fatoum (3):
  clk: imx6: provide helper to check if video PLL post dividers work
  clk: imx6: define an enum for ldb mux inputs
  clk: imx6: remove quirky clk_set_parent(LDB_diN_sel, pll5_video_div)

Fabio Estevam (1):
  clk: imx6: Fix procedure to switch the parent of LDB_DI_CLK

Philipp Zabel (2):
  clk: imx6: Mask mmdc_ch1 handshake for periph2_sel and
    mmdc_ch1_axi_podf
  clk: imx6: Make the LDB_DI0 and LDB_DI1 clocks read-only

 drivers/clk/imx/clk-imx6.c | 323 +++++++++++++++++++++++++++++++++++--
 drivers/clk/imx/clk.h      |   8 +
 2 files changed, 315 insertions(+), 16 deletions(-)

-- 
2.20.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* [PATCH 1/6] clk: imx6: provide helper to check if video PLL post dividers work
  2019-04-01 20:12 [PATCH 0/6] clk: imx6: work around LDB hang caused by ERR009219 Ahmad Fatoum
@ 2019-04-01 20:12 ` Ahmad Fatoum
  2019-04-01 20:12 ` [PATCH 2/6] clk: imx6: Mask mmdc_ch1 handshake for periph2_sel and mmdc_ch1_axi_podf Ahmad Fatoum
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 9+ messages in thread
From: Ahmad Fatoum @ 2019-04-01 20:12 UTC (permalink / raw)
  To: barebox; +Cc: pza, lst, sha

Audio/Video PLL post dividers don't work on i.MX6q revision 1.0.
This helper can be reused in the upcoming workaround patch for
erratum ERR009219.

The check in the helper has been inverted to make it clearer which
platforms are affected. Old call site was adjusted to spell
out what is really happening nowadays (with clk-imx6.c covering
more variants than just i.MX6Quad and DualLite):

   !rev1.0 || mx6dl
== !rev1.0 || !!mx6dl
== !(rev1.0 && !mx6dl)
== !(rev1.0 && (mx6s || mx6q || mx6qp || mx6d || mx6dp))
== !(rev1.0 && ((mx6q || mx6d) || (mx6s || mx6qp || mx6dp)))
== !((rev1.0 && (mx6q || mx6d)) || (rev1.0 && (mx6s || mx6qp || mx6dp)))
== (!(rev1.0 && (mx6q || mx6d)) && !(rev1.0 && (mx6s || mx6qp || mx6dp)))
== cpu_has_working_video_pll_post_div() && !(rev1.0 && (mx6s || mx6qp || mx6dp))

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/clk/imx/clk-imx6.c | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/drivers/clk/imx/clk-imx6.c b/drivers/clk/imx/clk-imx6.c
index 35b995dae24e..88c4bcde1cb3 100644
--- a/drivers/clk/imx/clk-imx6.c
+++ b/drivers/clk/imx/clk-imx6.c
@@ -64,6 +64,12 @@ static inline int cpu_is_plus(void)
 	return cpu_is_mx6qp() || cpu_is_mx6dp();
 }
 
+/* Audio/Video PLL post dividers don't work on i.MX6q revision 1.0 */
+static inline int cpu_has_working_video_pll_post_div(void) {
+	return !((cpu_is_mx6q() || cpu_is_mx6d()) &&
+		 imx_silicon_revision() == IMX_CHIP_REV_1_0);
+}
+
 static const char *step_sels[] = {
 	"osc",
 	"pll2_pfd2_396m",
@@ -341,8 +347,8 @@ static void imx6_add_video_clks(void __iomem *anab, void __iomem *cb)
 	clk_set_parent(clks[IMX6QDL_CLK_IPU2_DI0_PRE_SEL], clks[IMX6QDL_CLK_PLL5_VIDEO_DIV]);
 	clk_set_parent(clks[IMX6QDL_CLK_IPU2_DI1_PRE_SEL], clks[IMX6QDL_CLK_PLL5_VIDEO_DIV]);
 
-	if ((imx_silicon_revision() != IMX_CHIP_REV_1_0) ||
-	    cpu_is_mx6dl()) {
+	if (cpu_has_working_video_pll_post_div() &&
+	    !((cpu_is_plus() || cpu_is_mx6s()) && imx_silicon_revision() == IMX_CHIP_REV_1_0)) {
 		clk_set_parent(clks[IMX6QDL_CLK_LDB_DI0_SEL], clks[IMX6QDL_CLK_PLL5_VIDEO_DIV]);
 		clk_set_parent(clks[IMX6QDL_CLK_LDB_DI1_SEL], clks[IMX6QDL_CLK_PLL5_VIDEO_DIV]);
 	}
-- 
2.20.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* [PATCH 2/6] clk: imx6: Mask mmdc_ch1 handshake for periph2_sel and mmdc_ch1_axi_podf
  2019-04-01 20:12 [PATCH 0/6] clk: imx6: work around LDB hang caused by ERR009219 Ahmad Fatoum
  2019-04-01 20:12 ` [PATCH 1/6] clk: imx6: provide helper to check if video PLL post dividers work Ahmad Fatoum
@ 2019-04-01 20:12 ` Ahmad Fatoum
  2019-04-01 20:12 ` [PATCH 3/6] clk: imx6: Fix procedure to switch the parent of LDB_DI_CLK Ahmad Fatoum
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 9+ messages in thread
From: Ahmad Fatoum @ 2019-04-01 20:12 UTC (permalink / raw)
  To: barebox; +Cc: pza, lst, sha

From: Philipp Zabel <p.zabel@pengutronix.de>

MMDC CH1 is not used on i.MX6Q, so the handshake needed to change the
parent of periph2_sel or the divider of mmdc_ch1_axi_podf will never
succeed.
Disable the handshake mechanism to allow changing the frequency of
mmdc_ch1_axi, allowing to use it as a possible source for the LDB DI
clock.

Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
Signed-off-by: Fabio Estevam <fabio.estevam@nxp.com>
Signed-off-by: Shawn Guo <shawnguo@kernel.org>
[afa: ported to barebox from Linux commit f13abeff2c]
[afa: moved call site to where it would've been moved in following commit]
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/clk/imx/clk-imx6.c | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/drivers/clk/imx/clk-imx6.c b/drivers/clk/imx/clk-imx6.c
index 88c4bcde1cb3..21cbc77c50c5 100644
--- a/drivers/clk/imx/clk-imx6.c
+++ b/drivers/clk/imx/clk-imx6.c
@@ -299,6 +299,19 @@ static struct clk_div_table video_div_table[] = {
 	{ /* sentinel */ }
 };
 
+#define CCM_CCDR		0x04
+
+#define CCDR_MMDC_CH1_MASK	BIT(16)
+
+static void __init imx6q_mmdc_ch1_mask_handshake(void __iomem *ccm_base)
+{
+	unsigned int reg;
+
+	reg = readl(ccm_base + CCM_CCDR);
+	reg |= CCDR_MMDC_CH1_MASK;
+	writel(reg, ccm_base + CCM_CCDR);
+}
+
 static void imx6_add_video_clks(void __iomem *anab, void __iomem *cb)
 {
 	clks[IMX6QDL_CLK_PLL5_POST_DIV] = imx_clk_divider_table("pll5_post_div", "pll5_video", anab + 0xa0, 19, 2, post_div_table);
@@ -306,6 +319,9 @@ static void imx6_add_video_clks(void __iomem *anab, void __iomem *cb)
 
 	clks[IMX6QDL_CLK_IPU1_SEL]         = imx_clk_mux("ipu1_sel",         cb + 0x3c, 9,  2, ipu_sels,          ARRAY_SIZE(ipu_sels));
 	clks[IMX6QDL_CLK_IPU2_SEL]         = imx_clk_mux("ipu2_sel",         cb + 0x3c, 14, 2, ipu_sels,          ARRAY_SIZE(ipu_sels));
+
+	imx6q_mmdc_ch1_mask_handshake(cb);
+
 	clks[IMX6QDL_CLK_LDB_DI0_SEL]      = imx_clk_mux_p("ldb_di0_sel",      cb + 0x2c, 9,  3, ldb_di_sels,       ARRAY_SIZE(ldb_di_sels));
 	clks[IMX6QDL_CLK_LDB_DI1_SEL]      = imx_clk_mux_p("ldb_di1_sel",      cb + 0x2c, 12, 3, ldb_di_sels,       ARRAY_SIZE(ldb_di_sels));
 	clks[IMX6QDL_CLK_IPU1_DI0_PRE_SEL] = imx_clk_mux_p("ipu1_di0_pre_sel", cb + 0x34, 6,  3, ipu_di_pre_sels,   ARRAY_SIZE(ipu_di_pre_sels));
-- 
2.20.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* [PATCH 3/6] clk: imx6: Fix procedure to switch the parent of LDB_DI_CLK
  2019-04-01 20:12 [PATCH 0/6] clk: imx6: work around LDB hang caused by ERR009219 Ahmad Fatoum
  2019-04-01 20:12 ` [PATCH 1/6] clk: imx6: provide helper to check if video PLL post dividers work Ahmad Fatoum
  2019-04-01 20:12 ` [PATCH 2/6] clk: imx6: Mask mmdc_ch1 handshake for periph2_sel and mmdc_ch1_axi_podf Ahmad Fatoum
@ 2019-04-01 20:12 ` Ahmad Fatoum
  2019-04-01 20:12 ` [PATCH 4/6] clk: imx6: define an enum for ldb mux inputs Ahmad Fatoum
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 9+ messages in thread
From: Ahmad Fatoum @ 2019-04-01 20:12 UTC (permalink / raw)
  To: barebox; +Cc: pza, lst, sha

From: Fabio Estevam <fabio.estevam@nxp.com>

Due to incorrect placement of the clock gate cell in the ldb_di[x]_clk
tree, the glitchy parent mux of ldb_di[x]_clk can cause a glitch to
enter the ldb_di_ipu_div divider. If the divider gets locked up, no
ldb_di[x]_clk is generated, and the LVDS display will hang when the
ipu_di_clk is sourced from ldb_di_clk.

To fix the problem, both the new and current parent of the ldb_di_clk
should be disabled before the switch. This patch ensures that correct
steps are followed when ldb_di_clk parent is switched in the beginning
of boot. The glitchy muxes are then registered as read-only. The clock
parent can be selected using the assigned-clocks and
assigned-clock-parents properties of the ccm device tree node:

        &clks {
                assigned-clocks = <&clks IMX6QDL_CLK_LDB_DI0_SEL>,
                                  <&clks IMX6QDL_CLK_LDB_DI1_SEL>;
                assigned-clock-parents = <&clks IMX6QDL_CLK_MMDC_CH1_AXI>,
                                         <&clks IMX6QDL_CLK_PLL5_VIDEO_DIV>;
        };

The issue is explained in detail in EB821 ("LDB Clock Switch Procedure &
i.MX6 Asynchronous Clock Switching Guidelines") [1].

[1] http://www.nxp.com/files/32bit/doc/eng_bulletin/EB821.pdf

Signed-off-by: Ranjani Vaidyanathan <Ranjani.Vaidyanathan@nxp.com>
Signed-off-by: Fabio Estevam <fabio.estevam@nxp.com>
Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
Reviewed-by: Akshay Bhat <akshay.bhat@timesys.com>
Tested-by Joshua Clayton <stillcompiling@gmail.com>
Tested-by: Charles Kang <Charles.Kang@advantech.com.tw>
Signed-off-by: Shawn Guo <shawnguo@kernel.org>
[afa: ported to barebox from Linux commit 5d283b0838]
[afa: maintained reparenting for imx6qp revision >1.0 as before]
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/clk/imx/clk-imx6.c | 292 ++++++++++++++++++++++++++++++++++++-
 1 file changed, 284 insertions(+), 8 deletions(-)

diff --git a/drivers/clk/imx/clk-imx6.c b/drivers/clk/imx/clk-imx6.c
index 21cbc77c50c5..d12b494d578c 100644
--- a/drivers/clk/imx/clk-imx6.c
+++ b/drivers/clk/imx/clk-imx6.c
@@ -70,6 +70,12 @@ static inline int cpu_has_working_video_pll_post_div(void) {
 		 imx_silicon_revision() == IMX_CHIP_REV_1_0);
 }
 
+/* i.MX6 Quad/Dual/DualLite/Solo are all affected */
+static inline int cpu_has_err009219(void) {
+	return cpu_is_mx6d() || cpu_is_mx6q() ||
+		cpu_is_mx6dl() || cpu_is_mx6s();
+}
+
 static const char *step_sels[] = {
 	"osc",
 	"pll2_pfd2_396m",
@@ -299,9 +305,89 @@ static struct clk_div_table video_div_table[] = {
 	{ /* sentinel */ }
 };
 
+static int ldb_di_sel_by_clock_id(int clock_id)
+{
+	switch (clock_id) {
+	case IMX6QDL_CLK_PLL5_VIDEO_DIV:
+		if (!cpu_has_working_video_pll_post_div())
+			return -ENOENT;
+		return 0;
+	case IMX6QDL_CLK_PLL2_PFD0_352M:
+		return 1;
+	case IMX6QDL_CLK_PLL2_PFD2_396M:
+		return 2;
+	case IMX6QDL_CLK_MMDC_CH1_AXI:
+		return 3;
+	case IMX6QDL_CLK_PLL3_USB_OTG:
+		return 4;
+	default:
+		return -ENOENT;
+	}
+}
+
+static void of_assigned_ldb_sels(struct device_node *node,
+				 unsigned int *ldb_di0_sel,
+				 unsigned int *ldb_di1_sel)
+{
+	struct of_phandle_args clkspec;
+	int index, rc, num_parents;
+	int parent, child, sel;
+
+	num_parents = of_count_phandle_with_args(node, "assigned-clock-parents",
+						 "#clock-cells");
+	for (index = 0; index < num_parents; index++) {
+		rc = of_parse_phandle_with_args(node, "assigned-clock-parents",
+					"#clock-cells", index, &clkspec);
+		if (rc < 0) {
+			/* skip empty (null) phandles */
+			if (rc == -ENOENT)
+				continue;
+			else
+				return;
+		}
+		if (clkspec.np != node || clkspec.args[0] >= IMX6QDL_CLK_END) {
+			pr_err("ccm: parent clock %d not in ccm\n", index);
+			return;
+		}
+		parent = clkspec.args[0];
+
+		rc = of_parse_phandle_with_args(node, "assigned-clocks",
+				"#clock-cells", index, &clkspec);
+		if (rc < 0)
+			return;
+		if (clkspec.np != node || clkspec.args[0] >= IMX6QDL_CLK_END) {
+			pr_err("ccm: child clock %d not in ccm\n", index);
+			return;
+		}
+		child = clkspec.args[0];
+
+		if (child != IMX6QDL_CLK_LDB_DI0_SEL &&
+		    child != IMX6QDL_CLK_LDB_DI1_SEL)
+			continue;
+
+		sel = ldb_di_sel_by_clock_id(parent);
+		if (sel < 0) {
+			pr_err("ccm: invalid ldb_di%d parent clock: %d\n",
+			       child == IMX6QDL_CLK_LDB_DI1_SEL, parent);
+			continue;
+		}
+
+		if (child == IMX6QDL_CLK_LDB_DI0_SEL)
+			*ldb_di0_sel = sel;
+		if (child == IMX6QDL_CLK_LDB_DI1_SEL)
+			*ldb_di1_sel = sel;
+	}
+}
+
 #define CCM_CCDR		0x04
+#define CCM_CCSR		0x0c
+#define CCM_CS2CDR		0x2c
 
-#define CCDR_MMDC_CH1_MASK	BIT(16)
+#define CCDR_MMDC_CH1_MASK		BIT(16)
+#define CCSR_PLL3_SW_CLK_SEL		BIT(0)
+
+#define CS2CDR_LDB_DI0_CLK_SEL_SHIFT	9
+#define CS2CDR_LDB_DI1_CLK_SEL_SHIFT	12
 
 static void __init imx6q_mmdc_ch1_mask_handshake(void __iomem *ccm_base)
 {
@@ -312,7 +398,178 @@ static void __init imx6q_mmdc_ch1_mask_handshake(void __iomem *ccm_base)
 	writel(reg, ccm_base + CCM_CCDR);
 }
 
-static void imx6_add_video_clks(void __iomem *anab, void __iomem *cb)
+/*
+ * The only way to disable the MMDC_CH1 clock is to move it to pll3_sw_clk
+ * via periph2_clk2_sel and then to disable pll3_sw_clk by selecting the
+ * bypass clock source, since there is no CG bit for mmdc_ch1.
+ */
+static void mmdc_ch1_disable(void __iomem *ccm_base)
+{
+	unsigned int reg;
+
+	clk_set_parent(clks[IMX6QDL_CLK_PERIPH2_CLK2_SEL],
+		       clks[IMX6QDL_CLK_PLL3_USB_OTG]);
+
+	/*
+	 * Handshake with mmdc_ch1 module must be masked when changing
+	 * periph2_clk_sel.
+	 */
+	clk_set_parent(clks[IMX6QDL_CLK_PERIPH2], clks[IMX6QDL_CLK_PERIPH2_CLK2]);
+
+	/* Disable pll3_sw_clk by selecting the bypass clock source */
+	reg = readl(ccm_base + CCM_CCSR);
+	reg |= CCSR_PLL3_SW_CLK_SEL;
+	writel(reg, ccm_base + CCM_CCSR);
+}
+
+static void mmdc_ch1_reenable(void __iomem *ccm_base)
+{
+	unsigned int reg;
+
+	/* Enable pll3_sw_clk by disabling the bypass */
+	reg = readl(ccm_base + CCM_CCSR);
+	reg &= ~CCSR_PLL3_SW_CLK_SEL;
+	writel(reg, ccm_base + CCM_CCSR);
+
+	clk_set_parent(clks[IMX6QDL_CLK_PERIPH2], clks[IMX6QDL_CLK_PERIPH2_PRE]);
+}
+
+/*
+ * We have to follow a strict procedure when changing the LDB clock source,
+ * otherwise we risk introducing a glitch that can lock up the LDB divider.
+ * Things to keep in mind:
+ *
+ * 1. The current and new parent clock inputs to the mux must be disabled.
+ * 2. The default clock input for ldb_di0/1_clk_sel is mmdc_ch1_axi, which
+ *    has no CG bit.
+ * 3. pll2_pfd2_396m can not be gated if it is used as memory clock.
+ * 4. In the RTL implementation of the LDB_DI_CLK_SEL muxes the top four
+ *    options are in one mux and the PLL3 option along with three unused
+ *    inputs is in a second mux. There is a third mux with two inputs used
+ *    to decide between the first and second 4-port mux:
+ *
+ *    pll5_video_div 0 --|\
+ *    pll2_pfd0_352m 1 --| |_
+ *    pll2_pfd2_396m 2 --| | `-|\
+ *    mmdc_ch1_axi   3 --|/    | |
+ *                             | |--
+ *    pll3_usb_otg   4 --|\    | |
+ *                   5 --| |_,-|/
+ *                   6 --| |
+ *                   7 --|/
+ *
+ * The ldb_di0/1_clk_sel[1:0] bits control both 4-port muxes at the same time.
+ * The ldb_di0/1_clk_sel[2] bit controls the 2-port mux. The code below
+ * switches the parent to the bottom mux first and then manipulates the top
+ * mux to ensure that no glitch will enter the divider.
+ */
+static void init_ldb_clks(struct device_node *np, void __iomem *ccm_base)
+{
+	unsigned int reg;
+	unsigned int sel[2][4];
+	int i;
+
+	reg = readl(ccm_base + CCM_CS2CDR);
+	sel[0][0] = (reg >> CS2CDR_LDB_DI0_CLK_SEL_SHIFT) & 7;
+	sel[1][0] = (reg >> CS2CDR_LDB_DI1_CLK_SEL_SHIFT) & 7;
+
+	sel[0][3] = sel[0][2] = sel[0][1] = sel[0][0];
+	sel[1][3] = sel[1][2] = sel[1][1] = sel[1][0];
+
+	/*
+	 * This is for compatibility with the existing barebox behavior where
+	 * these configurations had their ldb_diN_sel clocks reparented.
+	 */
+	if (!(cpu_is_mx6s() && imx_silicon_revision() == IMX_CHIP_REV_1_0)) {
+		sel[0][3] = sel[1][3] = 0;
+	}
+
+	of_assigned_ldb_sels(np, &sel[0][3], &sel[1][3]);
+
+	for (i = 0; i < 2; i++) {
+		/* Warn if a glitch might have been introduced already */
+		if (sel[i][0] != 3) {
+			pr_warn("ccm: ldb_di%d_sel already changed from reset value: %d\n",
+				i, sel[i][0]);
+		}
+
+		if (sel[i][0] == sel[i][3])
+			continue;
+
+		/* Only switch to or from pll2_pfd2_396m if it is disabled */
+		if ((sel[i][0] == 2 || sel[i][3] == 2) &&
+		    (clk_get_parent(clks[IMX6QDL_CLK_PERIPH_PRE]) ==
+		     clks[IMX6QDL_CLK_PLL2_PFD2_396M])) {
+			pr_err("ccm: ldb_di%d_sel: couldn't disable pll2_pfd2_396m\n",
+			       i);
+			sel[i][3] = sel[i][2] = sel[i][1] = sel[i][0];
+			continue;
+		}
+
+		/* First switch to the bottom mux */
+		sel[i][1] = sel[i][0] | 4;
+
+		/* Then configure the top mux before switching back to it */
+		sel[i][2] = sel[i][3] | 4;
+
+		pr_debug("ccm: switching ldb_di%d_sel: %d->%d->%d->%d\n", i,
+			 sel[i][0], sel[i][1], sel[i][2], sel[i][3]);
+	}
+
+	if (sel[0][0] == sel[0][3] && sel[1][0] == sel[1][3])
+		return;
+
+	mmdc_ch1_disable(ccm_base);
+
+	for (i = 1; i < 4; i++) {
+		reg = readl(ccm_base + CCM_CS2CDR);
+		reg &= ~((7 << CS2CDR_LDB_DI0_CLK_SEL_SHIFT) |
+			 (7 << CS2CDR_LDB_DI1_CLK_SEL_SHIFT));
+		reg |= ((sel[0][i] << CS2CDR_LDB_DI0_CLK_SEL_SHIFT) |
+			(sel[1][i] << CS2CDR_LDB_DI1_CLK_SEL_SHIFT));
+		writel(reg, ccm_base + CCM_CS2CDR);
+	}
+
+	mmdc_ch1_reenable(ccm_base);
+}
+
+#define CCM_ANALOG_PLL_VIDEO	0xa0
+#define CCM_ANALOG_PFD_480	0xf0
+#define CCM_ANALOG_PFD_528	0x100
+
+#define PLL_ENABLE		BIT(13)
+
+#define PFD0_CLKGATE		BIT(7)
+#define PFD1_CLKGATE		BIT(15)
+#define PFD2_CLKGATE		BIT(23)
+#define PFD3_CLKGATE		BIT(31)
+
+static void disable_anatop_clocks(void __iomem *anatop_base)
+{
+	unsigned int reg;
+
+	/* Make sure PLL2 PFDs 0-2 are gated */
+	reg = readl(anatop_base + CCM_ANALOG_PFD_528);
+	/* Cannot gate PFD2 if pll2_pfd2_396m is the parent of MMDC clock */
+	if (clk_get_parent(clks[IMX6QDL_CLK_PERIPH_PRE]) ==
+	    clks[IMX6QDL_CLK_PLL2_PFD2_396M])
+		reg |= PFD0_CLKGATE | PFD1_CLKGATE;
+	else
+		reg |= PFD0_CLKGATE | PFD1_CLKGATE | PFD2_CLKGATE;
+	writel(reg, anatop_base + CCM_ANALOG_PFD_528);
+
+	/* Make sure PLL3 PFDs 0-3 are gated */
+	reg = readl(anatop_base + CCM_ANALOG_PFD_480);
+	reg |= PFD0_CLKGATE | PFD1_CLKGATE | PFD2_CLKGATE | PFD3_CLKGATE;
+	writel(reg, anatop_base + CCM_ANALOG_PFD_480);
+
+	/* Make sure PLL5 is disabled */
+	reg = readl(anatop_base + CCM_ANALOG_PLL_VIDEO);
+	reg &= ~PLL_ENABLE;
+	writel(reg, anatop_base + CCM_ANALOG_PLL_VIDEO);
+}
+
+static void imx6_add_video_clks(void __iomem *anab, void __iomem *cb, struct device_node *ccm_np)
 {
 	clks[IMX6QDL_CLK_PLL5_POST_DIV] = imx_clk_divider_table("pll5_post_div", "pll5_video", anab + 0xa0, 19, 2, post_div_table);
 	clks[IMX6QDL_CLK_PLL5_VIDEO_DIV] = imx_clk_divider_table("pll5_video_div", "pll5_post_div", anab + 0x170, 30, 2, video_div_table);
@@ -320,10 +577,25 @@ static void imx6_add_video_clks(void __iomem *anab, void __iomem *cb)
 	clks[IMX6QDL_CLK_IPU1_SEL]         = imx_clk_mux("ipu1_sel",         cb + 0x3c, 9,  2, ipu_sels,          ARRAY_SIZE(ipu_sels));
 	clks[IMX6QDL_CLK_IPU2_SEL]         = imx_clk_mux("ipu2_sel",         cb + 0x3c, 14, 2, ipu_sels,          ARRAY_SIZE(ipu_sels));
 
+	disable_anatop_clocks(anab);
+
 	imx6q_mmdc_ch1_mask_handshake(cb);
 
-	clks[IMX6QDL_CLK_LDB_DI0_SEL]      = imx_clk_mux_p("ldb_di0_sel",      cb + 0x2c, 9,  3, ldb_di_sels,       ARRAY_SIZE(ldb_di_sels));
-	clks[IMX6QDL_CLK_LDB_DI1_SEL]      = imx_clk_mux_p("ldb_di1_sel",      cb + 0x2c, 12, 3, ldb_di_sels,       ARRAY_SIZE(ldb_di_sels));
+	/*
+	 * skip if QuadPlus, as it doesn't suffer from ERR009219 and is handled
+	 * below
+	 */
+	if (cpu_has_err009219()) {
+		/*
+		 * The LDB_DI0/1_SEL muxes should be read-only due to a hardware
+		 * bug. Set the muxes to the requested values before registering the
+		 * ldb_di_sel clocks.
+		 */
+		init_ldb_clks(ccm_np, cb);
+	}
+
+	clks[IMX6QDL_CLK_LDB_DI0_SEL]       = imx_clk_mux_p("ldb_di0_sel",    cb + 0x2c, 9,  3, ldb_di_sels,      ARRAY_SIZE(ldb_di_sels));
+	clks[IMX6QDL_CLK_LDB_DI1_SEL]      = imx_clk_mux_p("ldb_di1_sel",    cb + 0x2c, 12, 3, ldb_di_sels,       ARRAY_SIZE(ldb_di_sels));
 	clks[IMX6QDL_CLK_IPU1_DI0_PRE_SEL] = imx_clk_mux_p("ipu1_di0_pre_sel", cb + 0x34, 6,  3, ipu_di_pre_sels,   ARRAY_SIZE(ipu_di_pre_sels));
 	clks[IMX6QDL_CLK_IPU1_DI1_PRE_SEL] = imx_clk_mux_p("ipu1_di1_pre_sel", cb + 0x34, 15, 3, ipu_di_pre_sels,   ARRAY_SIZE(ipu_di_pre_sels));
 	clks[IMX6QDL_CLK_IPU2_DI0_PRE_SEL] = imx_clk_mux_p("ipu2_di0_pre_sel", cb + 0x38, 6,  3, ipu_di_pre_sels,   ARRAY_SIZE(ipu_di_pre_sels));
@@ -363,12 +635,16 @@ static void imx6_add_video_clks(void __iomem *anab, void __iomem *cb)
 	clk_set_parent(clks[IMX6QDL_CLK_IPU2_DI0_PRE_SEL], clks[IMX6QDL_CLK_PLL5_VIDEO_DIV]);
 	clk_set_parent(clks[IMX6QDL_CLK_IPU2_DI1_PRE_SEL], clks[IMX6QDL_CLK_PLL5_VIDEO_DIV]);
 
-	if (cpu_has_working_video_pll_post_div() &&
-	    !((cpu_is_plus() || cpu_is_mx6s()) && imx_silicon_revision() == IMX_CHIP_REV_1_0)) {
+	/*
+	 * On SoC affected by ERR009219, it's not safe to call these
+	 * clk_set_parent. Thus we do this via init_ldb_clks if the device tree
+	 * indicates so. QuadPlus doesn't suffer from the erratum, so for now,
+	 * we leave the old behavior as is.
+	 */
+	if (((cpu_is_plus() && imx_silicon_revision() != IMX_CHIP_REV_1_0))) {
 		clk_set_parent(clks[IMX6QDL_CLK_LDB_DI0_SEL], clks[IMX6QDL_CLK_PLL5_VIDEO_DIV]);
 		clk_set_parent(clks[IMX6QDL_CLK_LDB_DI1_SEL], clks[IMX6QDL_CLK_PLL5_VIDEO_DIV]);
 	}
-
 }
 
 static int imx6_ccm_probe(struct device_d *dev)
@@ -528,7 +804,7 @@ static int imx6_ccm_probe(struct device_d *dev)
 	clkdev_add_physbase(clks[IMX6QDL_CLK_IPG], MX6_OCOTP_BASE_ADDR, NULL);
 
 	if (IS_ENABLED(CONFIG_DRIVER_VIDEO_IMX_IPUV3))
-		imx6_add_video_clks(anatop_base, ccm_base);
+		imx6_add_video_clks(anatop_base, ccm_base, dev->device_node);
 
 	writel(0xffffffff, ccm_base + CCGR0);
 	writel(0xf0ffffff, ccm_base + CCGR1); /* gate GPU3D, GPU2D */
-- 
2.20.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* [PATCH 4/6] clk: imx6: define an enum for ldb mux inputs
  2019-04-01 20:12 [PATCH 0/6] clk: imx6: work around LDB hang caused by ERR009219 Ahmad Fatoum
                   ` (2 preceding siblings ...)
  2019-04-01 20:12 ` [PATCH 3/6] clk: imx6: Fix procedure to switch the parent of LDB_DI_CLK Ahmad Fatoum
@ 2019-04-01 20:12 ` Ahmad Fatoum
  2019-04-01 20:12 ` [PATCH 5/6] clk: imx6: Make the LDB_DI0 and LDB_DI1 clocks read-only Ahmad Fatoum
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 9+ messages in thread
From: Ahmad Fatoum @ 2019-04-01 20:12 UTC (permalink / raw)
  To: barebox; +Cc: pza, lst, sha

For better readability should this code be reviewed
in future, replace the hardcoded input numbers
with an enum.

This is just a cosmetic change and was verified
to not affect clk-imx6.o.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/clk/imx/clk-imx6.c | 41 +++++++++++++++++++++++---------------
 1 file changed, 25 insertions(+), 16 deletions(-)

diff --git a/drivers/clk/imx/clk-imx6.c b/drivers/clk/imx/clk-imx6.c
index d12b494d578c..f527d1d5e565 100644
--- a/drivers/clk/imx/clk-imx6.c
+++ b/drivers/clk/imx/clk-imx6.c
@@ -213,12 +213,20 @@ static const char *ipu_sels[] = {
 	"pll3_pfd1_540m",
 };
 
+enum ldb_di_sel { /* for use in init_ldb_clks */
+	LDB_DI_SEL_PLL5_VIDEO_DIV	= 0,
+	LDB_DI_SEL_PLL2_PFD0_352M	= 1,
+	LDB_DI_SEL_PLL2_PFD2_396M	= 2,
+	LDB_DI_SEL_MMDC_CH1_AXI		= 3,
+	LDB_DI_SEL_PLL3_USB_OTG		= 4,
+};
+
 static const char *ldb_di_sels[] = {
-	"pll5_video_div",
-	"pll2_pfd0_352m",
-	"pll2_pfd2_396m",
-	"mmdc_ch1_axi_podf",
-	"pll3_usb_otg",
+	[LDB_DI_SEL_PLL5_VIDEO_DIV]	= "pll5_video_div",
+	[LDB_DI_SEL_PLL2_PFD0_352M]	= "pll2_pfd0_352m",
+	[LDB_DI_SEL_PLL2_PFD2_396M]	= "pll2_pfd2_396m",
+	[LDB_DI_SEL_MMDC_CH1_AXI]	= "mmdc_ch1_axi_podf",
+	[LDB_DI_SEL_PLL3_USB_OTG]	= "pll3_usb_otg",
 };
 
 static const char *ipu_di_pre_sels[] = {
@@ -311,23 +319,23 @@ static int ldb_di_sel_by_clock_id(int clock_id)
 	case IMX6QDL_CLK_PLL5_VIDEO_DIV:
 		if (!cpu_has_working_video_pll_post_div())
 			return -ENOENT;
-		return 0;
+		return LDB_DI_SEL_PLL5_VIDEO_DIV;
 	case IMX6QDL_CLK_PLL2_PFD0_352M:
-		return 1;
+		return LDB_DI_SEL_PLL2_PFD0_352M;
 	case IMX6QDL_CLK_PLL2_PFD2_396M:
-		return 2;
+		return LDB_DI_SEL_PLL2_PFD2_396M;
 	case IMX6QDL_CLK_MMDC_CH1_AXI:
-		return 3;
+		return LDB_DI_SEL_MMDC_CH1_AXI;
 	case IMX6QDL_CLK_PLL3_USB_OTG:
-		return 4;
+		return LDB_DI_SEL_PLL3_USB_OTG;
 	default:
 		return -ENOENT;
 	}
 }
 
 static void of_assigned_ldb_sels(struct device_node *node,
-				 unsigned int *ldb_di0_sel,
-				 unsigned int *ldb_di1_sel)
+				 enum ldb_di_sel *ldb_di0_sel,
+				 enum ldb_di_sel *ldb_di1_sel)
 {
 	struct of_phandle_args clkspec;
 	int index, rc, num_parents;
@@ -466,7 +474,7 @@ static void mmdc_ch1_reenable(void __iomem *ccm_base)
 static void init_ldb_clks(struct device_node *np, void __iomem *ccm_base)
 {
 	unsigned int reg;
-	unsigned int sel[2][4];
+	enum ldb_di_sel sel[2][4];
 	int i;
 
 	reg = readl(ccm_base + CCM_CS2CDR);
@@ -481,14 +489,14 @@ static void init_ldb_clks(struct device_node *np, void __iomem *ccm_base)
 	 * these configurations had their ldb_diN_sel clocks reparented.
 	 */
 	if (!(cpu_is_mx6s() && imx_silicon_revision() == IMX_CHIP_REV_1_0)) {
-		sel[0][3] = sel[1][3] = 0;
+		sel[0][3] = sel[1][3] = LDB_DI_SEL_PLL5_VIDEO_DIV;
 	}
 
 	of_assigned_ldb_sels(np, &sel[0][3], &sel[1][3]);
 
 	for (i = 0; i < 2; i++) {
 		/* Warn if a glitch might have been introduced already */
-		if (sel[i][0] != 3) {
+		if (sel[i][0] != LDB_DI_SEL_MMDC_CH1_AXI) {
 			pr_warn("ccm: ldb_di%d_sel already changed from reset value: %d\n",
 				i, sel[i][0]);
 		}
@@ -497,7 +505,8 @@ static void init_ldb_clks(struct device_node *np, void __iomem *ccm_base)
 			continue;
 
 		/* Only switch to or from pll2_pfd2_396m if it is disabled */
-		if ((sel[i][0] == 2 || sel[i][3] == 2) &&
+		if ((sel[i][0] == LDB_DI_SEL_PLL2_PFD2_396M ||
+		     sel[i][3] == LDB_DI_SEL_PLL2_PFD2_396M) &&
 		    (clk_get_parent(clks[IMX6QDL_CLK_PERIPH_PRE]) ==
 		     clks[IMX6QDL_CLK_PLL2_PFD2_396M])) {
 			pr_err("ccm: ldb_di%d_sel: couldn't disable pll2_pfd2_396m\n",
-- 
2.20.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* [PATCH 5/6] clk: imx6: Make the LDB_DI0 and LDB_DI1 clocks read-only
  2019-04-01 20:12 [PATCH 0/6] clk: imx6: work around LDB hang caused by ERR009219 Ahmad Fatoum
                   ` (3 preceding siblings ...)
  2019-04-01 20:12 ` [PATCH 4/6] clk: imx6: define an enum for ldb mux inputs Ahmad Fatoum
@ 2019-04-01 20:12 ` Ahmad Fatoum
  2019-04-01 20:15 ` [PATCH] clk: imx6: remove quirky clk_set_parent(LDB_diN_sel, pll5_video_div) Ahmad Fatoum
  2019-04-03  9:52 ` [PATCH 0/6] clk: imx6: work around LDB hang caused by ERR009219 Ahmad Fatoum
  6 siblings, 0 replies; 9+ messages in thread
From: Ahmad Fatoum @ 2019-04-01 20:12 UTC (permalink / raw)
  To: barebox; +Cc: pza, lst, sha

From: Philipp Zabel <p.zabel@pengutronix.de>

Due to incorrect placement of the clock gate cell in the ldb_di[x]_clk
tree, the glitchy parent mux of ldb_di[x]_clk can cause a glitch to
enter the ldb_di_ipu_div divider. If the divider gets locked up, no
ldb_di[x]_clk is generated, and the LVDS display will hang when the
ipu_di_clk is sourced from ldb_di_clk.

To fix the problem, both the new and current parent of the ldb_di_clk
should be disabled before the switch. As this can not be guaranteed by
the clock framework during runtime, make the ldb_di[x]_sel muxes read-only.
A workaround to set the muxes once during boot could be added to the
kernel or bootloader.

Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
Signed-off-by: Fabio Estevam <fabio.estevam@nxp.com>
Signed-off-by: Shawn Guo <shawnguo@kernel.org>
[afa: ported from Linux kernel commit 03d576f202]
[afa: added exception for i.MX6QP, see kernel commit f4a0a6c309]
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/clk/imx/clk-imx6.c | 9 ++++++---
 drivers/clk/imx/clk.h      | 8 ++++++++
 2 files changed, 14 insertions(+), 3 deletions(-)

diff --git a/drivers/clk/imx/clk-imx6.c b/drivers/clk/imx/clk-imx6.c
index f527d1d5e565..65d8f4cd89be 100644
--- a/drivers/clk/imx/clk-imx6.c
+++ b/drivers/clk/imx/clk-imx6.c
@@ -601,10 +601,13 @@ static void imx6_add_video_clks(void __iomem *anab, void __iomem *cb, struct dev
 		 * ldb_di_sel clocks.
 		 */
 		init_ldb_clks(ccm_np, cb);
-	}
 
-	clks[IMX6QDL_CLK_LDB_DI0_SEL]       = imx_clk_mux_p("ldb_di0_sel",    cb + 0x2c, 9,  3, ldb_di_sels,      ARRAY_SIZE(ldb_di_sels));
-	clks[IMX6QDL_CLK_LDB_DI1_SEL]      = imx_clk_mux_p("ldb_di1_sel",    cb + 0x2c, 12, 3, ldb_di_sels,       ARRAY_SIZE(ldb_di_sels));
+		clks[IMX6QDL_CLK_LDB_DI0_SEL] = imx_clk_mux_ldb("ldb_di0_sel",    cb + 0x2c, 9,  3, ldb_di_sels,      ARRAY_SIZE(ldb_di_sels));
+		clks[IMX6QDL_CLK_LDB_DI1_SEL] = imx_clk_mux_ldb("ldb_di1_sel",    cb + 0x2c, 12, 3, ldb_di_sels,       ARRAY_SIZE(ldb_di_sels));
+	} else {
+		clks[IMX6QDL_CLK_LDB_DI0_SEL] = imx_clk_mux_p("ldb_di0_sel",    cb + 0x2c, 9,  3, ldb_di_sels,      ARRAY_SIZE(ldb_di_sels));
+		clks[IMX6QDL_CLK_LDB_DI1_SEL] = imx_clk_mux_p("ldb_di1_sel",    cb + 0x2c, 12, 3, ldb_di_sels,       ARRAY_SIZE(ldb_di_sels));
+	}
 	clks[IMX6QDL_CLK_IPU1_DI0_PRE_SEL] = imx_clk_mux_p("ipu1_di0_pre_sel", cb + 0x34, 6,  3, ipu_di_pre_sels,   ARRAY_SIZE(ipu_di_pre_sels));
 	clks[IMX6QDL_CLK_IPU1_DI1_PRE_SEL] = imx_clk_mux_p("ipu1_di1_pre_sel", cb + 0x34, 15, 3, ipu_di_pre_sels,   ARRAY_SIZE(ipu_di_pre_sels));
 	clks[IMX6QDL_CLK_IPU2_DI0_PRE_SEL] = imx_clk_mux_p("ipu2_di0_pre_sel", cb + 0x38, 6,  3, ipu_di_pre_sels,   ARRAY_SIZE(ipu_di_pre_sels));
diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h
index 875c76a8b3e4..04286f03f727 100644
--- a/drivers/clk/imx/clk.h
+++ b/drivers/clk/imx/clk.h
@@ -39,6 +39,14 @@ static inline struct clk *imx_clk_divider_table(const char *name,
 				 width, table, 0);
 }
 
+static inline struct clk *imx_clk_mux_ldb(const char *name, void __iomem *reg,
+		u8 shift, u8 width, const char **parents, int num_parents)
+{
+	return clk_mux(name, CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT, reg,
+		       shift, width, parents, num_parents, CLK_MUX_READ_ONLY);
+}
+
+
 static inline struct clk *imx_clk_fixed_factor(const char *name,
 		const char *parent, unsigned int mult, unsigned int div)
 {
-- 
2.20.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* [PATCH] clk: imx6: remove quirky clk_set_parent(LDB_diN_sel, pll5_video_div)
  2019-04-01 20:12 [PATCH 0/6] clk: imx6: work around LDB hang caused by ERR009219 Ahmad Fatoum
                   ` (4 preceding siblings ...)
  2019-04-01 20:12 ` [PATCH 5/6] clk: imx6: Make the LDB_DI0 and LDB_DI1 clocks read-only Ahmad Fatoum
@ 2019-04-01 20:15 ` Ahmad Fatoum
  2019-04-01 20:16   ` Ahmad Fatoum
  2019-04-03  9:52 ` [PATCH 0/6] clk: imx6: work around LDB hang caused by ERR009219 Ahmad Fatoum
  6 siblings, 1 reply; 9+ messages in thread
From: Ahmad Fatoum @ 2019-04-01 20:15 UTC (permalink / raw)
  To: barebox; +Cc: Andrey Smirnov, Raphael Poggi, lst, pza, sha

barebox has inherited the clk_set_parent(ldb_diN_sel, pll5_video_div)
from upstream kernel commit 32f3b8da22 ("ARM i.MX6q: set the LDB serial
clock parent to the video PLL"), where it was enabled for all i.MX6Q
revisions after 1.0. It was applied whenever CONFIG_DRIVER_VIDEO_IMX_IPUV3
was defined.

The kernel removed this reparenting again as a preventive measure
against ERR009219 in 03d576f202 ("clk: imx6: Make the LDB_DI0 and
LDB_DI1 clocks read-only").

As the kernel used the device tree compatible, which is the same
for e.g. i.MX6Solo and DualLite, but barebox queried ANATOP which
lists different CPU types for i.MX6Solo and DualLite and because
i.MX6QuadPlus wasn't supported at first, barebox grew to do
the reparenting on the odd set of:
  - Quad and Dual rev >1.0
  - DualLite
  - Solo rev >1.0
  - QuadPlus and DualPlus rev >1.0

On all of these, except for the QuadPlus and the DualPlus, this
reparenting may glitch the LDB so that it permanently locks up.

Previous commits work around this glitch and this commit
now removes this unituituve quirk and instead of _sometimes_
breaking the boards in the previous list, _always_ breaks them
if they are strictly dependent on pll5_video_div being the parent
of ldb_diN_sel.

As of v2019.03.0, following mainline boards are potentially broken
by this (i.e. they're supported by barebox, are in the list above,
have a LDB enabled and might be defining CONFIG_DRIVER_VIDEO_IMX_IPUV3):
  - imx6qdl-zii-rdu2.dtsi
  - imx6qdl-udoo.dtsi
  - imx6qdl-mba6x.dtsi
  - imx6q-var-custom.dts
  - imx6q-guf-santaro.dts
  - imx6q-embedsky-e9.dtsi

These and any other affected non-mainline boards can have the old behavior
reinstated by adding the following to their respective device trees, but
this time in a glitch-free manner:

	&clks {
		assigned-clocks = <&clks IMX6QDL_CLK_LDB_DI0_SEL>,
				  <&clks IMX6QDL_CLK_LDB_DI1_SEL>;
		assigned-clock-parents = <&clks IMX6QDL_CLK_PLL5_VIDEO_DIV>,
					 <&clks IMX6QDL_CLK_PLL5_VIDEO_DIV>;
	};

If barebox is configured to show a boot splash screen, this snippet
should exist in the barebox device tree. If barebox acts on it, the
kernel will show following warning:
	ccm: ldb_di0_sel already changed from reset value: 0
	ccm: ldb_di1_sel already changed from reset value: 0

This warning is safe to ignore.

Cc: Andrey Smirnov <andrew.smirnov@gmail.com>
Cc: Raphael Poggi <poggi.raph@gmail.com>
Cc: Sascha Hauer <s.hauer@pengutronix.de>
Cc: Lucas Stach <l.stach@pengutronix.de>
Cc: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/clk/imx/clk-imx6.c | 19 -------------------
 1 file changed, 19 deletions(-)

diff --git a/drivers/clk/imx/clk-imx6.c b/drivers/clk/imx/clk-imx6.c
index 65d8f4cd89be..a595785fed60 100644
--- a/drivers/clk/imx/clk-imx6.c
+++ b/drivers/clk/imx/clk-imx6.c
@@ -484,14 +484,6 @@ static void init_ldb_clks(struct device_node *np, void __iomem *ccm_base)
 	sel[0][3] = sel[0][2] = sel[0][1] = sel[0][0];
 	sel[1][3] = sel[1][2] = sel[1][1] = sel[1][0];
 
-	/*
-	 * This is for compatibility with the existing barebox behavior where
-	 * these configurations had their ldb_diN_sel clocks reparented.
-	 */
-	if (!(cpu_is_mx6s() && imx_silicon_revision() == IMX_CHIP_REV_1_0)) {
-		sel[0][3] = sel[1][3] = LDB_DI_SEL_PLL5_VIDEO_DIV;
-	}
-
 	of_assigned_ldb_sels(np, &sel[0][3], &sel[1][3]);
 
 	for (i = 0; i < 2; i++) {
@@ -646,17 +638,6 @@ static void imx6_add_video_clks(void __iomem *anab, void __iomem *cb, struct dev
 	clk_set_parent(clks[IMX6QDL_CLK_IPU1_DI1_PRE_SEL], clks[IMX6QDL_CLK_PLL5_VIDEO_DIV]);
 	clk_set_parent(clks[IMX6QDL_CLK_IPU2_DI0_PRE_SEL], clks[IMX6QDL_CLK_PLL5_VIDEO_DIV]);
 	clk_set_parent(clks[IMX6QDL_CLK_IPU2_DI1_PRE_SEL], clks[IMX6QDL_CLK_PLL5_VIDEO_DIV]);
-
-	/*
-	 * On SoC affected by ERR009219, it's not safe to call these
-	 * clk_set_parent. Thus we do this via init_ldb_clks if the device tree
-	 * indicates so. QuadPlus doesn't suffer from the erratum, so for now,
-	 * we leave the old behavior as is.
-	 */
-	if (((cpu_is_plus() && imx_silicon_revision() != IMX_CHIP_REV_1_0))) {
-		clk_set_parent(clks[IMX6QDL_CLK_LDB_DI0_SEL], clks[IMX6QDL_CLK_PLL5_VIDEO_DIV]);
-		clk_set_parent(clks[IMX6QDL_CLK_LDB_DI1_SEL], clks[IMX6QDL_CLK_PLL5_VIDEO_DIV]);
-	}
 }
 
 static int imx6_ccm_probe(struct device_d *dev)
-- 
2.20.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* Re: [PATCH] clk: imx6: remove quirky clk_set_parent(LDB_diN_sel, pll5_video_div)
  2019-04-01 20:15 ` [PATCH] clk: imx6: remove quirky clk_set_parent(LDB_diN_sel, pll5_video_div) Ahmad Fatoum
@ 2019-04-01 20:16   ` Ahmad Fatoum
  0 siblings, 0 replies; 9+ messages in thread
From: Ahmad Fatoum @ 2019-04-01 20:16 UTC (permalink / raw)
  To: barebox


On 1/4/19 22:15, Ahmad Fatoum wrote:

Argh. This is RFC PATCH 6/6.. Missed the prefix.

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* Re: [PATCH 0/6] clk: imx6: work around LDB hang caused by ERR009219
  2019-04-01 20:12 [PATCH 0/6] clk: imx6: work around LDB hang caused by ERR009219 Ahmad Fatoum
                   ` (5 preceding siblings ...)
  2019-04-01 20:15 ` [PATCH] clk: imx6: remove quirky clk_set_parent(LDB_diN_sel, pll5_video_div) Ahmad Fatoum
@ 2019-04-03  9:52 ` Ahmad Fatoum
  6 siblings, 0 replies; 9+ messages in thread
From: Ahmad Fatoum @ 2019-04-03  9:52 UTC (permalink / raw)
  To: barebox

On 1/4/19 22:12, Ahmad Fatoum wrote:
> The erratum already has a Linux workaround. On barebox, it can currently
> happen if there's an appropriate assigned-clock-parents property or the
> reparenting happens (i.e. CONFIG_DRIVER_VIDEO_IMX_IPUV3 is selected on a
> board with the aforementioned CPU/Revision pairs). Linux has removed the
> reparenting along with the bug fix. If barebox did this, boards that
> depended on the reparenting may be broken.

After a discussion with Philipp and Sascha, I'll rearrange this patch series:
Patch 6/6 will wander to the start of the series and will remove the quirk of
the ldb _sometimes_ having pll5_video_div as parent and instead never set it
by default, like the kernel does now but potentially breaking boards that
depended on barebox setting this. 

Now with the need for the funny conditionals removed, I can squash 1/6, 3/6, 4/6,
without much loss of comprehensibility (not that it was particularly comprehensible
before..).


-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

end of thread, other threads:[~2019-04-03  9:52 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-04-01 20:12 [PATCH 0/6] clk: imx6: work around LDB hang caused by ERR009219 Ahmad Fatoum
2019-04-01 20:12 ` [PATCH 1/6] clk: imx6: provide helper to check if video PLL post dividers work Ahmad Fatoum
2019-04-01 20:12 ` [PATCH 2/6] clk: imx6: Mask mmdc_ch1 handshake for periph2_sel and mmdc_ch1_axi_podf Ahmad Fatoum
2019-04-01 20:12 ` [PATCH 3/6] clk: imx6: Fix procedure to switch the parent of LDB_DI_CLK Ahmad Fatoum
2019-04-01 20:12 ` [PATCH 4/6] clk: imx6: define an enum for ldb mux inputs Ahmad Fatoum
2019-04-01 20:12 ` [PATCH 5/6] clk: imx6: Make the LDB_DI0 and LDB_DI1 clocks read-only Ahmad Fatoum
2019-04-01 20:15 ` [PATCH] clk: imx6: remove quirky clk_set_parent(LDB_diN_sel, pll5_video_div) Ahmad Fatoum
2019-04-01 20:16   ` Ahmad Fatoum
2019-04-03  9:52 ` [PATCH 0/6] clk: imx6: work around LDB hang caused by ERR009219 Ahmad Fatoum

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