From patchwork Wed Jul 2 22:53:04 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Fabio Estevam X-Patchwork-Id: 366505 Return-Path: X-Original-To: incoming-imx@patchwork.ozlabs.org Delivered-To: patchwork-incoming-imx@bilbo.ozlabs.org Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2001:1868:205::9]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 745ED1400B9 for ; Thu, 3 Jul 2014 08:56:50 +1000 (EST) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1X2TPX-0006V2-Gw; Wed, 02 Jul 2014 22:53:55 +0000 Received: from mail-yh0-x22a.google.com ([2607:f8b0:4002:c01::22a]) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1X2TPT-0006ML-Vp for linux-arm-kernel@lists.infradead.org; Wed, 02 Jul 2014 22:53:52 +0000 Received: by mail-yh0-f42.google.com with SMTP id i57so7301962yha.29 for ; Wed, 02 Jul 2014 15:53:29 -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=g8tUXtQnDQcQzwbTCrqypT9uMuoEI1078Y5NdtNs7no=; b=qpkIVzl90Wc+cjVBA9UmcSJAMuJidId8gOrJ++TJxmsaX4B0O/MB8PlhV9oBfxYOS4 eNQrt6UbDzp3hXltfxDemj3Cu8PAaZTcAeCFhgl/MLGMsBzahVwG08bFj9+VEccnYqc/ tLcyMtLg/Kmi0l63ksX3QenSyLyonlFhCiCy0Lq4Hpqhmrca50OefJXo+CBl21pSvqGc M5GEq8OeLJdmRtXKzVAiOLwkp12Aaq/g3jbjemlTc5bLnF/k1mku1S0fq58yYvNG68p6 Ogvo5BCfShfwnKU/wqqr5FUtNbVD9Ns52GbUuIPbuemT3TJDCr1DI2mtLEaEfggnim1S HUMA== X-Received: by 10.236.8.103 with SMTP id 67mr1218443yhq.29.1404341609866; Wed, 02 Jul 2014 15:53:29 -0700 (PDT) Received: from localhost.localdomain ([177.194.40.193]) by mx.google.com with ESMTPSA id r48sm36610762yho.31.2014.07.02.15.53.26 for (version=TLSv1.1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Wed, 02 Jul 2014 15:53:28 -0700 (PDT) From: Fabio Estevam To: shawn.guo@freescale.com Subject: [PATCH v2] ARM: imx6: Fix procedure to switch the parent of LDB_DI_CLK Date: Wed, 2 Jul 2014 19:53:04 -0300 Message-Id: <1404341584-21668-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-20140702_155352_197633_C932B684 X-CRM114-Status: GOOD ( 21.55 ) X-Spam-Score: -0.8 (/) X-Spam-Report: SpamAssassin version 3.4.0 on bombadil.infradead.org summary: Content analysis details: (-0.8 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.7 RCVD_IN_DNSWL_LOW RBL: Sender listed at http://www.dnswl.org/, low trust [2607:f8b0:4002:c01:0:0:0:22a listed in] [list.dnswl.org] 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 -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain Cc: Ranjani.Vaidyanathan@freescale.com, Fabio Estevam , christian.gmeiner@gmail.com, dirk.behme@de.bosch.com, linux-arm-kernel@lists.infradead.org X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.18-1 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 --- Changes since v1: - Remove cpu_is_imx6dl() from disable_anatop_clocks. - Do not use bit 31 of CCM_ANALOG_PFD_528n 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 f092ee13..2d2179c 100644 --- a/arch/arm/mach-imx/clk-imx6q.c +++ b/arch/arm/mach-imx/clk-imx6q.c @@ -108,6 +108,123 @@ static struct clk_div_table video_div_table[] = { static unsigned int share_count_esai; +static void init_ldb_clks(unsigned int 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 to pll3_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[IMX6QDL_CLK_LDB_DI0_SEL], clk[new_parent]); + clk_set_parent(clk[IMX6QDL_CLK_LDB_DI0_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 gate PFD2 if pll2_pfd2_396m is the parent of MMDC clock */ + if (clk_get_parent(clk[IMX6QDL_CLK_PERIPH_PRE]) == clk[IMX6QDL_CLK_PLL2_PFD2_396M]) + reg |= 0x00008080; + else + reg |= 0x00808080; + 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; @@ -200,6 +317,8 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) clk[IMX6QDL_CLK_PLL5_OST_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[IMX6QDL_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); @@ -407,10 +526,8 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) clk_register_clkdev(clk[IMX6QDL_CLK_ENET_REF], "enet_ref", NULL); if ((imx_get_soc_revision() != IMX_CHIP_REVISION_1_0) || - cpu_is_imx6dl()) { - clk_set_parent(clk[IMX6QDL_CLK_LDB_DI0_SEL], clk[IMX6QDL_CLK_PLL5_VIDEO_DIV]); - clk_set_parent(clk[IMX6QDL_CLK_LDB_DI1_SEL], clk[IMX6QDL_CLK_PLL5_VIDEO_DIV]); - } + cpu_is_imx6dl()) + init_ldb_clks(IMX6QDL_CLK_PLL5_VIDEO_DIV); clk_set_parent(clk[IMX6QDL_CLK_IPU1_DI0_PRE_SEL], clk[IMX6QDL_CLK_PLL5_VIDEO_DIV]); clk_set_parent(clk[IMX6QDL_CLK_IPU1_DI1_PRE_SEL], clk[IMX6QDL_CLK_PLL5_VIDEO_DIV]);