From patchwork Fri Dec 9 13:00:08 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nikita Yushchenko X-Patchwork-Id: 9468151 X-Patchwork-Delegate: sboyd@codeaurora.org Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 7F8B660231 for ; Fri, 9 Dec 2016 13:00:49 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 71F332863C for ; Fri, 9 Dec 2016 13:00:49 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 669A92864B; Fri, 9 Dec 2016 13:00:49 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.8 required=2.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_HI,T_DKIM_INVALID autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id A929728645 for ; Fri, 9 Dec 2016 13:00:48 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932610AbcLINAp (ORCPT ); Fri, 9 Dec 2016 08:00:45 -0500 Received: from mail-wm0-f45.google.com ([74.125.82.45]:37823 "EHLO mail-wm0-f45.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932414AbcLINAo (ORCPT ); Fri, 9 Dec 2016 08:00:44 -0500 Received: by mail-wm0-f45.google.com with SMTP id t79so26223341wmt.0 for ; Fri, 09 Dec 2016 05:00:43 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cogentembedded-com.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id; bh=tWZBKjTkcczZmsiZ5ZhcEztMpxa98L/0ZjMLnG7y9g0=; b=pi+2t2xJMHYVVXwblqjZU4tNXLt6eGdASeHtLYISg0XuHtFiBpT+ryUAoDhlV+0VaX QrMUoVDyJeNg9+YO8af6/cM4J5RIK7kB4+lZr3+g2N0UckQ66Sv76FfDhDE9PZ00QUdW DdBJQH5rYu3vUYkiKGGeuPOEXUM3AggDO7YjnhJZizD+8s/qPDyBKkvt1TIpxeIALJST b6paZ2vOQBNtfw3mCQf4E2fuwK9NNhCi1svX7wS/rsilF3IbtQbaJjblVX/pOddLS52O 0dZW67zJ4xNRcQHuuL4d9ICryaw6ptNx7Zo1i29WlLFLnnRnKFbc5BfCZ1oOc/QB4QKk TghA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=tWZBKjTkcczZmsiZ5ZhcEztMpxa98L/0ZjMLnG7y9g0=; b=Jstn6LW6EFygqZewjabUjTfcLmmB0lEu8QiNJCmQArAUhrs27nz8EDh9kZ01csJzYm SmXxQSMrnkMas5K0T/yLZM3Mhdb8kEXJCUQM9dXXlesBIvufYGLx3QpMMcVSF7PG+0vl fA+gWnR51lLkH0GAgnVlZGYraqoCBKnZJPVnAnbroHAMXgi8ropaBOw+Q3bCC8pS52dm YIbniSWgwdpn1f8V3b9YIJqFTJZ44rkHa/rCSNx2KSeZ8fyAIDqEYcpVv4K73d90hnU6 0yOy2nlZ/bjOPrPPCvMF5B7WCvTMKRBEt3TB0oSSCEkw0mBq6YcLQ2QRYcdreLT/f5H8 JNrA== X-Gm-Message-State: AKaTC01rrxv0L53VvWATVA0qwKWasjwZZvYp9QCfZcpqbaBEkV9bEtfx/0fB9DQegCAspQ== X-Received: by 10.46.5.215 with SMTP id 206mr35171880ljf.17.1481288441656; Fri, 09 Dec 2016 05:00:41 -0800 (PST) Received: from hugenb.home (nikaet.starlink.ru. [94.141.168.29]) by smtp.gmail.com with ESMTPSA id d79sm6555440lfd.46.2016.12.09.05.00.40 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Fri, 09 Dec 2016 05:00:40 -0800 (PST) From: Nikita Yushchenko To: Shawn Guo , Sascha Hauer , Fabio Estevam , Michael Turquette , Stephen Boyd , linux-clk@vger.kernel.org Cc: Chris Healy , linux-kernel@vger.kernel.org, Andrey Smirnov , Nikita Yushchenko Subject: [PATCH] clk: imx: pllv3: support fractional multiplier on vf610 PLL1/PLL2 Date: Fri, 9 Dec 2016 16:00:08 +0300 Message-Id: <1481288408-16035-1-git-send-email-nikita.yoush@cogentembedded.com> X-Mailer: git-send-email 2.1.4 Sender: linux-clk-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-clk@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP On vf610, PLL1 and PLL2 have registers to configure fractional part of frequency multiplier. This patch adds support for these registers. This fixes "fast system clock" issue on boards where bootloader sets fractional multiplier for PLL1. Suggested-by: Andrey Smirnov CC: Chris Healy Signed-off-by: Nikita Yushchenko --- drivers/clk/imx/clk-pllv3.c | 87 +++++++++++++++++++++++++++++++++++++++++++++ drivers/clk/imx/clk-vf610.c | 4 +-- drivers/clk/imx/clk.h | 1 + 3 files changed, 90 insertions(+), 2 deletions(-) diff --git a/drivers/clk/imx/clk-pllv3.c b/drivers/clk/imx/clk-pllv3.c index 19f9b622981a..24a9e914e0d5 100644 --- a/drivers/clk/imx/clk-pllv3.c +++ b/drivers/clk/imx/clk-pllv3.c @@ -21,6 +21,9 @@ #define PLL_NUM_OFFSET 0x10 #define PLL_DENOM_OFFSET 0x20 +#define PLL_VF610_NUM_OFFSET 0x20 +#define PLL_VF610_DENOM_OFFSET 0x30 + #define BM_PLL_POWER (0x1 << 12) #define BM_PLL_LOCK (0x1 << 31) #define IMX7_ENET_PLL_POWER (0x1 << 5) @@ -288,6 +291,87 @@ static const struct clk_ops clk_pllv3_av_ops = { .set_rate = clk_pllv3_av_set_rate, }; +static unsigned long clk_pllv3_vf610_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_pllv3 *pll = to_clk_pllv3(hw); + u32 mfn = readl_relaxed(pll->base + PLL_VF610_NUM_OFFSET); + u32 mfd = readl_relaxed(pll->base + PLL_VF610_DENOM_OFFSET); + u32 div = (readl_relaxed(pll->base) & pll->div_mask) ? 22 : 20; + u64 temp64 = (u64)parent_rate; + + temp64 *= mfn; + do_div(temp64, mfd); + + return (parent_rate * div) + (u32)temp64; +} + +static long clk_pllv3_vf610_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + unsigned long parent_rate = *prate; + unsigned int mfi = (rate >= 22 * parent_rate) ? 22 : 20; + u32 mfn, mfd = 0x3fffffff; + u64 temp64; + + if (rate <= parent_rate * mfi) + mfn = 0; + else if (rate >= parent_rate * (mfi + 1)) + mfn = mfd - 1; + else { + /* rate = parent_rate * (mfi + mfn/mfd) */ + temp64 = rate - parent_rate * mfi; + temp64 *= mfd; + do_div(temp64, parent_rate); + mfn = temp64; + } + + temp64 = ((u64)mfd * mfi + mfn) * parent_rate; + do_div(temp64, mfd); + return (u32)temp64; +} + +static int clk_pllv3_vf610_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_pllv3 *pll = to_clk_pllv3(hw); + unsigned int mfi = (rate >= 22 * parent_rate) ? 22 : 20; + u32 val, mfn, mfd = 0x3fffffff; + u64 temp64; + + if (rate <= parent_rate * mfi) + mfn = 0; + else if (rate >= parent_rate * (mfi + 1)) + mfn = mfd - 1; + else { + /* rate = parent_rate * (mfi + mfn/mfd) */ + temp64 = rate - parent_rate * mfi; + temp64 *= mfd; + do_div(temp64, parent_rate); + mfn = temp64; + } + + val = readl_relaxed(pll->base); + if (mfi == 20) + val &= ~pll->div_mask; + else + val |= pll->div_mask; + writel_relaxed(val, pll->base); + writel_relaxed(mfn, pll->base + PLL_VF610_NUM_OFFSET); + writel_relaxed(mfd, pll->base + PLL_VF610_DENOM_OFFSET); + + return clk_pllv3_wait_lock(pll); +} + +static const struct clk_ops clk_pllv3_vf610_ops = { + .prepare = clk_pllv3_prepare, + .unprepare = clk_pllv3_unprepare, + .is_prepared = clk_pllv3_is_prepared, + .recalc_rate = clk_pllv3_vf610_recalc_rate, + .round_rate = clk_pllv3_vf610_round_rate, + .set_rate = clk_pllv3_vf610_set_rate, +}; + static unsigned long clk_pllv3_enet_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { @@ -322,6 +406,9 @@ struct clk *imx_clk_pllv3(enum imx_pllv3_type type, const char *name, case IMX_PLLV3_SYS: ops = &clk_pllv3_sys_ops; break; + case IMX_PLLV3_SYS_VF610: + ops = &clk_pllv3_vf610_ops; + break; case IMX_PLLV3_USB_VF610: pll->div_shift = 1; case IMX_PLLV3_USB: diff --git a/drivers/clk/imx/clk-vf610.c b/drivers/clk/imx/clk-vf610.c index 0476353ab423..59b1863deb88 100644 --- a/drivers/clk/imx/clk-vf610.c +++ b/drivers/clk/imx/clk-vf610.c @@ -219,8 +219,8 @@ static void __init vf610_clocks_init(struct device_node *ccm_node) clk[VF610_CLK_PLL6_BYPASS_SRC] = imx_clk_mux("pll6_bypass_src", PLL6_CTRL, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); clk[VF610_CLK_PLL7_BYPASS_SRC] = imx_clk_mux("pll7_bypass_src", PLL7_CTRL, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); - clk[VF610_CLK_PLL1] = imx_clk_pllv3(IMX_PLLV3_GENERIC, "pll1", "pll1_bypass_src", PLL1_CTRL, 0x1); - clk[VF610_CLK_PLL2] = imx_clk_pllv3(IMX_PLLV3_GENERIC, "pll2", "pll2_bypass_src", PLL2_CTRL, 0x1); + clk[VF610_CLK_PLL1] = imx_clk_pllv3(IMX_PLLV3_SYS_VF610, "pll1", "pll1_bypass_src", PLL1_CTRL, 0x1); + clk[VF610_CLK_PLL2] = imx_clk_pllv3(IMX_PLLV3_SYS_VF610, "pll2", "pll2_bypass_src", PLL2_CTRL, 0x1); clk[VF610_CLK_PLL3] = imx_clk_pllv3(IMX_PLLV3_USB_VF610, "pll3", "pll3_bypass_src", PLL3_CTRL, 0x2); clk[VF610_CLK_PLL4] = imx_clk_pllv3(IMX_PLLV3_AV, "pll4", "pll4_bypass_src", PLL4_CTRL, 0x7f); clk[VF610_CLK_PLL5] = imx_clk_pllv3(IMX_PLLV3_ENET, "pll5", "pll5_bypass_src", PLL5_CTRL, 0x3); diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h index 3799ff82a9b4..99a617897831 100644 --- a/drivers/clk/imx/clk.h +++ b/drivers/clk/imx/clk.h @@ -34,6 +34,7 @@ enum imx_pllv3_type { IMX_PLLV3_AV, IMX_PLLV3_ENET, IMX_PLLV3_ENET_IMX7, + IMX_PLLV3_SYS_VF610, }; struct clk *imx_clk_pllv3(enum imx_pllv3_type type, const char *name,