From patchwork Wed May 14 09:36:37 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Wang, Jiada" X-Patchwork-Id: 348674 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 82F3B140088 for ; Wed, 14 May 2014 19:39:51 +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 1WkVcg-0004v0-8d; Wed, 14 May 2014 09:37:14 +0000 Received: from relay1.mentorg.com ([192.94.38.131]) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1WkVcd-0004sU-AV for linux-arm-kernel@lists.infradead.org; Wed, 14 May 2014 09:37:12 +0000 Received: from svr-orw-fem-01.mgc.mentorg.com ([147.34.98.93]) by relay1.mentorg.com with esmtp id 1WkVcC-0001T5-BQ from Jiada_Wang@mentor.com ; Wed, 14 May 2014 02:36:44 -0700 Received: from NA1-MAIL.mgc.mentorg.com ([147.34.98.181]) by svr-orw-fem-01.mgc.mentorg.com with Microsoft SMTPSVC(6.0.3790.4675); Wed, 14 May 2014 02:36:44 -0700 Received: from jiwang-OptiPlex-980.tokyo.mentorg.com ([134.86.192.150]) by NA1-MAIL.mgc.mentorg.com with Microsoft SMTPSVC(6.0.3790.3959); Wed, 14 May 2014 02:36:43 -0700 From: Jiada Wang To: shawn.guo@linaro.org, dirk.behme@de.bosch.com, rmk+kernel@arm.linux.org.uk Subject: [PATCH] ARM: imx: powerdown PLL before changing of its setting Date: Wed, 14 May 2014 18:36:37 +0900 Message-Id: <1400060197-706-1-git-send-email-jiada_wang@mentor.com> X-Mailer: git-send-email 1.9.3 X-OriginalArrivalTime: 14 May 2014 09:36:43.0786 (UTC) FILETIME=[045A36A0:01CF6F58] X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20140514_023711_383693_957F03A0 X-CRM114-Status: GOOD ( 11.14 ) X-Spam-Score: 0.0 (/) X-Spam-Report: SpamAssassin version 3.3.2 on bombadil.infradead.org summary: Content analysis details: (0.0 points) pts rule name description ---- ---------------------- -------------------------------------------------- Cc: 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 According to i.MX6 user manual, "Before changing the PLL setting, power it down. Power up the PLL after the change" But currently PLL setting is being updated without power-down it first. This may end up with improper output/gitches which prevents furture operation. Signed-off-by: Jiada Wang --- arch/arm/mach-imx/clk-pllv3.c | 72 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 66 insertions(+), 6 deletions(-) diff --git a/arch/arm/mach-imx/clk-pllv3.c b/arch/arm/mach-imx/clk-pllv3.c index 6136405..f77edd6 100644 --- a/arch/arm/mach-imx/clk-pllv3.c +++ b/arch/arm/mach-imx/clk-pllv3.c @@ -67,6 +67,42 @@ static int clk_pllv3_wait_lock(struct clk_pllv3 *pll) return readl_relaxed(pll->base) & BM_PLL_LOCK ? 0 : -ETIMEDOUT; } +static void clk_pllv3_powerdown(struct clk_pllv3 *pll) +{ + u32 val; + + val = readl_relaxed(pll->base); + val |= BM_PLL_BYPASS; + writel_relaxed(val, pll->base); + + if (pll->powerup_set) + val &= ~BM_PLL_POWER; + else + val |= BM_PLL_POWER; + writel_relaxed(val, pll->base); +} + +static int clk_pllv3_powerrestore(struct clk_pllv3 *pll, u32 power, u32 bypass) +{ + u32 val; + int ret; + + val = readl_relaxed(pll->base); + val &= ~BM_PLL_POWER; + val |= power; + writel_relaxed(val, pll->base); + + ret = clk_pllv3_wait_lock(pll); + if (ret == 0) { + /* only if it locked can we switch back to the PLL */ + val &= ~BM_PLL_BYPASS; + val |= bypass; + writel_relaxed(val, pll->base); + } + + return ret; +} + static int clk_pllv3_prepare(struct clk_hw *hw) { struct clk_pllv3 *pll = to_clk_pllv3(hw); @@ -149,7 +185,7 @@ static int clk_pllv3_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct clk_pllv3 *pll = to_clk_pllv3(hw); - u32 val, div; + u32 val, powerval, bypassval, div; if (rate == parent_rate * 22) div = 1; @@ -159,11 +195,19 @@ static int clk_pllv3_set_rate(struct clk_hw *hw, unsigned long rate, return -EINVAL; val = readl_relaxed(pll->base); + powerval = val & BM_PLL_POWER; + bypassval = val & BM_PLL_BYPASS; + + /* power down PLL first */ + clk_pllv3_powerdown(pll); + + val = readl_relaxed(pll->base); val &= ~pll->div_mask; val |= div; writel_relaxed(val, pll->base); - return clk_pllv3_wait_lock(pll); + /* restore power & bypass bit */ + return clk_pllv3_powerrestore(pll, powerval, bypassval); } static const struct clk_ops clk_pllv3_ops = { @@ -208,18 +252,26 @@ static int clk_pllv3_sys_set_rate(struct clk_hw *hw, unsigned long rate, struct clk_pllv3 *pll = to_clk_pllv3(hw); unsigned long min_rate = parent_rate * 54 / 2; unsigned long max_rate = parent_rate * 108 / 2; - u32 val, div; + u32 val, powerval, bypassval, div; if (rate < min_rate || rate > max_rate) return -EINVAL; + val = readl_relaxed(pll->base); + powerval = val & BM_PLL_POWER; + bypassval = val & BM_PLL_BYPASS; + + /* power down PLL first */ + clk_pllv3_powerdown(pll); + div = rate * 2 / parent_rate; val = readl_relaxed(pll->base); val &= ~pll->div_mask; val |= div; writel_relaxed(val, pll->base); - return clk_pllv3_wait_lock(pll); + /* restore power & bypass bit */ + return clk_pllv3_powerrestore(pll, powerval, bypassval); } static const struct clk_ops clk_pllv3_sys_ops = { @@ -273,13 +325,20 @@ static int clk_pllv3_av_set_rate(struct clk_hw *hw, unsigned long rate, struct clk_pllv3 *pll = to_clk_pllv3(hw); unsigned long min_rate = parent_rate * 27; unsigned long max_rate = parent_rate * 54; - u32 val, div; + u32 val, powerval, bypassval, div; u32 mfn, mfd = 1000000; s64 temp64; if (rate < min_rate || rate > max_rate) return -EINVAL; + val = readl_relaxed(pll->base); + powerval = val & BM_PLL_POWER; + bypassval = val & BM_PLL_BYPASS; + + /* power down PLL first */ + clk_pllv3_powerdown(pll); + div = rate / parent_rate; temp64 = (u64) (rate - div * parent_rate); temp64 *= mfd; @@ -293,7 +352,8 @@ static int clk_pllv3_av_set_rate(struct clk_hw *hw, unsigned long rate, writel_relaxed(mfn, pll->base + PLL_NUM_OFFSET); writel_relaxed(mfd, pll->base + PLL_DENOM_OFFSET); - return clk_pllv3_wait_lock(pll); + /* restore power & bypass bit */ + return clk_pllv3_powerrestore(pll, powerval, bypassval); } static const struct clk_ops clk_pllv3_av_ops = {