From patchwork Wed Apr 9 11:55:38 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Fabio Estevam X-Patchwork-Id: 337830 Return-Path: X-Original-To: incoming-imx@patchwork.ozlabs.org Delivered-To: patchwork-incoming-imx@bilbo.ozlabs.org Received: from casper.infradead.org (casper.infradead.org [85.118.1.10]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 02844140156 for ; Wed, 9 Apr 2014 21:57:06 +1000 (EST) Received: from merlin.infradead.org ([2001:4978:20e::2]) by casper.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1WXr7b-0006Gz-NI; Wed, 09 Apr 2014 11:56:51 +0000 Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1WXr7Z-0007wX-IK; Wed, 09 Apr 2014 11:56:49 +0000 Received: from mail-yh0-x22d.google.com ([2607:f8b0:4002:c01::22d]) by merlin.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1WXr7V-0007vY-5a for linux-arm-kernel@lists.infradead.org; Wed, 09 Apr 2014 11:56:46 +0000 Received: by mail-yh0-f45.google.com with SMTP id a41so2191115yho.32 for ; Wed, 09 Apr 2014 04:56:22 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id; bh=0iNgL1alWpkjpIQuLsfHx3dpTTn4BvlNmNqlFuat4Hg=; b=AwRJyf1i1I2K2AX+y74dDOcYlK/pjXGsJKuRNln3Frdz2C/Tn0T3MxHdbAXTS6m5dP JUmDE8w9vQGKbgLDBzBACFwr0w5sgkeha2kauhX+QuR3wvH3fYQaSADP9vB7YN/1ftyf KfOywoD1wFGYBkEKXGijve/CIMBcu3l3/Lwl6vwXj/jRa3ACQTAcmPDqQ1MeO3QLTQPz ufV0iKS0/EZsWd4WEUExc/p7xbjU/Fh3AlnjKV7WywfZeMEmf2H9y8TikAznu6eglqkc 144ECwZtN+vbRLMymlIJdWVe824U46mPEd7EfOdFs/tr1WcPZYukWd5LIeznaZcIAc3R DDMw== X-Received: by 10.236.20.194 with SMTP id p42mr13689666yhp.56.1397044582745; Wed, 09 Apr 2014 04:56:22 -0700 (PDT) Received: from localhost.localdomain ([201.82.195.226]) by mx.google.com with ESMTPSA id m69sm1209003yhn.16.2014.04.09.04.56.20 for (version=TLSv1.1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Wed, 09 Apr 2014 04:56:21 -0700 (PDT) From: Fabio Estevam To: shawn.guo@freescale.com Subject: [PATCH] ARM: imx6: Fix procedure to switch the parent of LDB_DI_CLK Date: Wed, 9 Apr 2014 08:55:38 -0300 Message-Id: <1397044538-12676-1-git-send-email-festevam@gmail.com> X-Mailer: git-send-email 1.8.3.2 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20140409_075646_062888_7340FA0A X-CRM114-Status: GOOD ( 19.67 ) X-Spam-Score: -2.0 (--) X-Spam-Report: SpamAssassin version 3.3.2 on merlin.infradead.org summary: Content analysis details: (-2.0 points) pts rule name description ---- ---------------------- -------------------------------------------------- 0.0 FREEMAIL_FROM Sender email is commonly abused enduser mail provider (festevam[at]gmail.com) -0.0 SPF_PASS SPF: sender matches SPF record -1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1% [score: 0.0000] -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature Cc: Ranjani.Vaidyanathan@freescale.com, Fabio Estevam , christian.gmeiner@gmail.com, linux-arm-kernel@lists.infradead.org X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.15 Precedence: list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+incoming-imx=patchwork.ozlabs.org@lists.infradead.org List-Id: linux-imx-kernel.lists.patchwork.ozlabs.org From: Fabio Estevam 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. Signed-off-by: Ranjani Vaidyanathan Signed-off-by: Fabio Estevam Tested-by: Christian Gmeiner --- arch/arm/mach-imx/clk-imx6q.c | 125 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 121 insertions(+), 4 deletions(-) diff --git a/arch/arm/mach-imx/clk-imx6q.c b/arch/arm/mach-imx/clk-imx6q.c index 20ad0d1..3ee45f4 100644 --- a/arch/arm/mach-imx/clk-imx6q.c +++ b/arch/arm/mach-imx/clk-imx6q.c @@ -140,6 +140,123 @@ static struct clk_div_table video_div_table[] = { { /* sentinel */ } }; +static void init_ldb_clks(enum mx6q_clks new_parent) +{ + struct device_node *np; + static void __iomem *ccm_base; + unsigned int reg; + + np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-ccm"); + ccm_base = of_iomap(np, 0); + WARN_ON(!ccm_base); + + /* + * Need to follow a strict procedure when changing the LDB + * clock, else we can introduce a glitch. Things to keep in + * mind: + * 1. The current and new parent clocks must be disabled. + * 2. The default clock for ldb_dio_clk is mmdc_ch1 which has + * no CG bit. + * 3. In the RTL implementation of the LDB_DI_CLK_SEL mux + * the top four options are in one mux and the PLL3 option along + * with another option is in the second mux. There is third mux + * used to decide between the first and second mux. + * The code below switches the parent to the bottom mux first + * and then manipulates the top mux. This ensures that no glitch + * will enter the divider. + * + * Need to disable MMDC_CH1 clock manually as there is no CG bit + * for this clock. The only way to disable this clock is to move + * it topll3_sw_clk and then to disable pll3_sw_clk + * Make sure periph2_clk2_sel is set to pll3_sw_clk + */ + reg = readl_relaxed(ccm_base + 0x18); + reg &= ~(1 << 20); + writel_relaxed(reg, ccm_base + 0x18); + + /* Set MMDC_CH1 mask bit */ + reg = readl_relaxed(ccm_base + 0x4); + reg |= 1 << 16; + writel_relaxed(reg, ccm_base + 0x4); + + /* + * Set the periph2_clk_sel to the top mux so that + * mmdc_ch1 is from pll3_sw_clk. + */ + reg = readl_relaxed(ccm_base + 0x14); + reg |= 1 << 26; + writel_relaxed(reg, ccm_base + 0x14); + + /* Wait for the clock switch */ + while (readl_relaxed(ccm_base + 0x48)) + ; + + /* Disable pll3_sw_clk by selecting the bypass clock source */ + reg = readl_relaxed(ccm_base + 0xc); + reg |= 1 << 0; + writel_relaxed(reg, ccm_base + 0xc); + + /* Set the ldb_di0_clk and ldb_di1_clk to 111b */ + reg = readl_relaxed(ccm_base + 0x2c); + reg |= ((7 << 9) | (7 << 12)); + writel_relaxed(reg, ccm_base + 0x2c); + + /* Set the ldb_di0_clk and ldb_di1_clk to 100b */ + reg = readl_relaxed(ccm_base + 0x2c); + reg &= ~((7 << 9) | (7 << 12)); + reg |= ((4 << 9) | (4 << 12)); + writel_relaxed(reg, ccm_base + 0x2c); + + /* Perform the LDB parent clock switch */ + clk_set_parent(clk[ldb_di0_sel], clk[new_parent]); + clk_set_parent(clk[ldb_di1_sel], clk[new_parent]); + + /* Unbypass pll3_sw_clk */ + reg = readl_relaxed(ccm_base + 0xc); + reg &= ~(1 << 0); + writel_relaxed(reg, ccm_base + 0xc); + + /* + * Set the periph2_clk_sel back to the bottom mux so that + * mmdc_ch1 is from its original parent. + */ + reg = readl_relaxed(ccm_base + 0x14); + reg &= ~(1 << 26); + writel_relaxed(reg, ccm_base + 0x14); + + /* Wait for the clock switch */ + while (readl_relaxed(ccm_base + 0x48)) + ; + + /* Clear MMDC_CH1 mask bit */ + reg = readl_relaxed(ccm_base + 0x4); + reg &= ~(1 << 16); + writel_relaxed(reg, ccm_base + 0x4); +} + +static void disable_anatop_clocks(void __iomem *anatop_base) +{ + unsigned int reg; + + /* Make sure PFDs are disabled at boot. */ + reg = readl_relaxed(anatop_base + 0x100); + /* Cannot disable pll2_pfd2_396M, as it is the MMDC clock in iMX6DL */ + if (cpu_is_imx6dl()) + reg |= 0x80008080; + else + reg |= 0x80808080; + writel_relaxed(reg, anatop_base + 0x100); + + reg = readl_relaxed(anatop_base + 0xf0); + reg |= 0x80808080; + writel_relaxed(reg, anatop_base + 0xf0); + + /* Make sure PLLs is disabled */ + reg = readl_relaxed(anatop_base + 0xa0); + reg &= ~(1 << 13); + writel_relaxed(reg, anatop_base + 0xa0); +} + static void __init imx6q_clocks_init(struct device_node *ccm_node) { struct device_node *np; @@ -232,6 +349,8 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) clk[pll5_post_div] = clk_register_divider_table(NULL, "pll5_post_div", "pll5_video", CLK_SET_RATE_PARENT, base + 0xa0, 19, 2, 0, post_div_table, &imx_ccm_lock); clk[pll5_video_div] = clk_register_divider_table(NULL, "pll5_video_div", "pll5_post_div", CLK_SET_RATE_PARENT, base + 0x170, 30, 2, 0, video_div_table, &imx_ccm_lock); + disable_anatop_clocks(base); + np = ccm_node; base = of_iomap(np, 0); WARN_ON(!base); @@ -440,10 +559,8 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) clk_register_clkdev(clk[enet_ref], "enet_ref", NULL); if ((imx_get_soc_revision() != IMX_CHIP_REVISION_1_0) || - cpu_is_imx6dl()) { - clk_set_parent(clk[ldb_di0_sel], clk[pll5_video_div]); - clk_set_parent(clk[ldb_di1_sel], clk[pll5_video_div]); - } + cpu_is_imx6dl()) + init_ldb_clks(pll5_video_div); /* * The gpmi needs 100MHz frequency in the EDO/Sync mode,