From patchwork Thu Jul 9 11:57:40 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Gabriel Fernandez X-Patchwork-Id: 6755331 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id E5E669F380 for ; Thu, 9 Jul 2015 12:00:27 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 32E0F2078A for ; Thu, 9 Jul 2015 12:00:26 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 195FA20606 for ; Thu, 9 Jul 2015 12:00:24 +0000 (UTC) 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 1ZDATS-0004Qk-NL; Thu, 09 Jul 2015 11:58:42 +0000 Received: from mail-wi0-f173.google.com ([209.85.212.173]) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1ZDAT9-000466-5N for linux-arm-kernel@lists.infradead.org; Thu, 09 Jul 2015 11:58:25 +0000 Received: by wiga1 with SMTP id a1so310807244wig.0 for ; Thu, 09 Jul 2015 04:58:01 -0700 (PDT) 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:in-reply-to :references; bh=ah0xaCPeItNTBoiUEqzdlxgMF0YbyeOw7dy8hOyd6v4=; b=VyZd0fkx2r86fZ+9lcp/7kcD1GnFYiBCFKHsqIAkjCsidzUsSjOldR7CjDRpEHPlh9 /3+RySK4j3R8vNWGHgCMDSYl7syHQbB6mA7DWzqm0FdHAcE5ijQ7QbCjtbAbVIYttKXz HTbhdaO/AuZb0Dxrj72K5gTOd3vRO+peo6NquiXEDIjosjam0e03p9CAbealGwuwgIby fx4JKQoGzx3BXSV9akiMdhGw74sbf8+Nf23BDo9JqwRFWpurMy2EH0aUER9BxYWSqH8d nIwPNrFT8vaeiQsi+WnFKf/2GXRrWzghZ+1r7DCSEhTjyPYQ1H9CXELWgeeqZJgsoJ+t w2GQ== X-Gm-Message-State: ALoCoQkUqEN8U9ewiZv2Du9BLKDeYCrVZIpxHBkzAiEEujYaGA0kvq2t8RyFmTjzkQuneBWSdIAb X-Received: by 10.181.12.20 with SMTP id em20mr35358056wid.28.1436443081521; Thu, 09 Jul 2015 04:58:01 -0700 (PDT) Received: from lmenx315.st.com. ([80.12.39.69]) by smtp.gmail.com with ESMTPSA id lz10sm8436503wjb.48.2015.07.09.04.57.57 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 09 Jul 2015 04:58:00 -0700 (PDT) From: Gabriel Fernandez To: Rob Herring , Pawel Moll , Mark Rutland , Ian Campbell , Kumar Gala , Srinivas Kandagatla , Maxime Coquelin , Patrice Chotard , Russell King , Michael Turquette , Stephen Boyd , Gabriel Fernandez , Peter Griffin , Pankaj Dev , Olivier Bideau , Geert Uytterhoeven , Fabian Frederick Subject: [PATCH 3/3] drivers: clk: st: Correct the pll-type for A9 for stih418 Date: Thu, 9 Jul 2015 13:57:40 +0200 Message-Id: <1436443060-12866-4-git-send-email-gabriel.fernandez@linaro.org> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1436443060-12866-1-git-send-email-gabriel.fernandez@linaro.org> References: <1436443060-12866-1-git-send-email-gabriel.fernandez@linaro.org> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20150709_045823_371998_259A4ED3 X-CRM114-Status: GOOD ( 20.09 ) X-Spam-Score: -2.6 (--) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: devicetree@vger.kernel.org, linux-clk@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, kernel@stlinux.com MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-4.5 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Add support for new PLL-type for stih418 A9-PLL. Currently the 407_A9_PLL type being used, it is corrected with this patch 4600c28 PLL allows to reach higher frequencies so its programming algorithm is extended. Signed-off-by: Pankaj Dev Signed-off-by: Gabriel Fernandez --- .../devicetree/bindings/clock/st/st,clkgen-pll.txt | 1 + drivers/clk/st/clkgen-pll.c | 195 +++++++++++++++++++++ 2 files changed, 196 insertions(+) diff --git a/Documentation/devicetree/bindings/clock/st/st,clkgen-pll.txt b/Documentation/devicetree/bindings/clock/st/st,clkgen-pll.txt index efb51cf..19dc0ca 100644 --- a/Documentation/devicetree/bindings/clock/st/st,clkgen-pll.txt +++ b/Documentation/devicetree/bindings/clock/st/st,clkgen-pll.txt @@ -23,6 +23,7 @@ Required properties: "st,stih407-plls-c32-a9", "st,clkgen-plls-c32" "st,stih407-plls-c32-c0_0", "st,clkgen-plls-c32" "st,stih407-plls-c32-c0_1", "st,clkgen-plls-c32" + "st,stih418-plls-c28-a9", "st,clkgen-plls-c32" "st,stih415-gpu-pll-c32", "st,clkgengpu-pll-c32" "st,stih416-gpu-pll-c32", "st,clkgengpu-pll-c32" diff --git a/drivers/clk/st/clkgen-pll.c b/drivers/clk/st/clkgen-pll.c index 04d66ee1d..7062943 100644 --- a/drivers/clk/st/clkgen-pll.c +++ b/drivers/clk/st/clkgen-pll.c @@ -42,6 +42,13 @@ DEFINE_SPINLOCK(clkgen_a9_lock); #define C32_MAX_ODFS (4) +/* + * PLL configuration register bits for PLL4600 C28 + */ +#define C28_NDIV_MASK (0xff) +#define C28_IDF_MASK (0x7) +#define C28_ODF_MASK (0x3f) + struct clkgen_pll_data { struct clkgen_field pdn_status; struct clkgen_field pdn_ctrl; @@ -66,6 +73,7 @@ static const struct clk_ops st_pll800c65_ops; static const struct clk_ops stm_pll3200c32_ops; static const struct clk_ops stm_pll3200c32_a9_ops; static const struct clk_ops st_pll1200c32_ops; +static const struct clk_ops stm_pll4600c28_ops; static const struct clkgen_pll_data st_pll1600c65_ax = { .pdn_status = CLKGEN_FIELD(0x0, 0x1, 19), @@ -254,6 +262,22 @@ static const struct clkgen_pll_data st_pll3200c32_407_a9 = { .ops = &stm_pll3200c32_a9_ops, }; +static struct clkgen_pll_data st_pll4600c28_418_a9 = { + /* 418 A9 */ + .pdn_status = CLKGEN_FIELD(0x1a8, 0x1, 0), + .pdn_ctrl = CLKGEN_FIELD(0x1a8, 0x1, 0), + .locked_status = CLKGEN_FIELD(0x87c, 0x1, 0), + .ndiv = CLKGEN_FIELD(0x1b0, C28_NDIV_MASK, 0), + .idf = CLKGEN_FIELD(0x1a8, C28_IDF_MASK, 25), + .num_odfs = 1, + .odf = { CLKGEN_FIELD(0x1b0, C28_ODF_MASK, 8) }, + .odf_gate = { CLKGEN_FIELD(0x1ac, 0x1, 28) }, + .switch2pll_en = true, + .switch2pll = CLKGEN_FIELD(0x1a4, 0x1, 1), + .lock = &clkgen_a9_lock, + .ops = &stm_pll4600c28_ops, +}; + /** * DOC: Clock Generated by PLL, rate set and enabled by bootloader * @@ -610,6 +634,164 @@ static unsigned long recalc_stm_pll1200c32(struct clk_hw *hw, return rate; } +/* PLL output structure + * FVCO >> /2 >> FVCOBY2 (no output) + * |> Divider (ODF) >> PHI + * + * FVCOby2 output = (input * 2 * NDIV) / IDF (assuming FRAC_CONTROL==L) + * + * Rules: + * 4Mhz <= INFF input <= 350Mhz + * 4Mhz <= INFIN (INFF / IDF) <= 50Mhz + * 19.05Mhz <= FVCOby2 output (PHI w ODF=1) <= 3000Mhz + * 1 <= i (register/dec value for IDF) <= 7 + * 8 <= n (register/dec value for NDIV) <= 246 + */ + +static int clk_pll4600c28_get_params(unsigned long input, unsigned long output, + struct stm_pll *pll) +{ + + unsigned long i, infin, n; + unsigned long deviation = ~0; + unsigned long new_freq, new_deviation; + + /* Output clock range: 19Mhz to 3000Mhz */ + if (output < 19000000 || output > 3000000000u) + return -EINVAL; + + /* For better jitter, IDF should be smallest + and NDIV must be maximum */ + for (i = 1; i <= 7 && deviation; i++) { + /* INFIN checks */ + infin = input / i; + if (infin < 4000000 || infin > 50000000) + continue; /* Invalid case */ + + n = output / (infin * 2); + if (n < 8 || n > 246) + continue; /* Invalid case */ + if (n < 246) + n++; /* To work around 'y' when n=x.y */ + + for (; n >= 8 && deviation; n--) { + new_freq = infin * 2 * n; + if (new_freq < output) + break; /* Optimization: shorting loop */ + + new_deviation = new_freq - output; + if (!new_deviation || new_deviation < deviation) { + pll->idf = i; + pll->ndiv = n; + deviation = new_deviation; + } + } + } + + if (deviation == ~0) /* No solution found */ + return -EINVAL; + + return 0; +} + +static int clk_pll4600c28_get_rate(unsigned long input, struct stm_pll *pll, + unsigned long *rate) +{ + if (!pll->idf) + pll->idf = 1; + + *rate = (input / pll->idf) * 2 * pll->ndiv; + + return 0; +} + +static unsigned long recalc_stm_pll4600c28(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clkgen_pll *pll = to_clkgen_pll(hw); + struct stm_pll params; + unsigned long rate; + + if (!clkgen_pll_is_enabled(hw) || !clkgen_pll_is_locked(hw)) + return 0; + + params.ndiv = CLKGEN_READ(pll, ndiv); + params.idf = CLKGEN_READ(pll, idf); + + clk_pll4600c28_get_rate(parent_rate, ¶ms, &rate); + + pr_debug("%s:%s rate %lu\n", __clk_get_name(hw->clk), __func__, rate); + + return rate; +} + +static long round_rate_stm_pll4600c28(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct stm_pll params; + + if (!clk_pll4600c28_get_params(*prate, rate, ¶ms)) { + clk_pll4600c28_get_rate(*prate, ¶ms, &rate); + } else { + pr_debug("%s: %s rate %ld Invalid\n", __func__, + __clk_get_name(hw->clk), rate); + return 0; + } + + pr_debug("%s: %s new rate %ld [ndiv=%u] [idf=%u]\n", + __func__, __clk_get_name(hw->clk), + rate, (unsigned int)params.ndiv, + (unsigned int)params.idf); + + return rate; +} + +static int set_rate_stm_pll4600c28(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clkgen_pll *pll = to_clkgen_pll(hw); + struct stm_pll params; + long hwrate; + unsigned long flags = 0; + + if (!rate || !parent_rate) + return -EINVAL; + + if (!clk_pll4600c28_get_params(parent_rate, rate, ¶ms)) { + clk_pll4600c28_get_rate(parent_rate, ¶ms, &hwrate); + } else { + pr_debug("%s: %s rate %ld Invalid\n", __func__, + __clk_get_name(hw->clk), rate); + return -EINVAL; + } + + pr_debug("%s: %s new rate %ld [ndiv=0x%x] [idf=0x%x]\n", + __func__, __clk_get_name(hw->clk), + hwrate, (unsigned int)params.ndiv, + (unsigned int)params.idf); + + if (!hwrate) + return -EINVAL; + + pll->ndiv = params.ndiv; + pll->idf = params.idf; + + clkgen_pll_disable(hw); + + if (pll->lock) + spin_lock_irqsave(pll->lock, flags); + + CLKGEN_WRITE(pll, ndiv, pll->ndiv); + CLKGEN_WRITE(pll, idf, pll->idf); + + if (pll->lock) + spin_unlock_irqrestore(pll->lock, flags); + + clkgen_pll_enable(hw); + + return 0; +} + static const struct clk_ops st_pll1600c65_ops = { .enable = clkgen_pll_enable, .disable = clkgen_pll_disable, @@ -647,6 +829,15 @@ static const struct clk_ops st_pll1200c32_ops = { .recalc_rate = recalc_stm_pll1200c32, }; +static const struct clk_ops stm_pll4600c28_ops = { + .enable = clkgen_pll_enable, + .disable = clkgen_pll_disable, + .is_enabled = clkgen_pll_is_enabled, + .recalc_rate = recalc_stm_pll4600c28, + .round_rate = round_rate_stm_pll4600c28, + .set_rate = set_rate_stm_pll4600c28, +}; + static struct clk * __init clkgen_pll_register(const char *parent_name, struct clkgen_pll_data *pll_data, void __iomem *reg, @@ -892,6 +1083,10 @@ static const struct of_device_id c32_pll_of_match[] = { .compatible = "st,stih407-plls-c32-a9", .data = &st_pll3200c32_407_a9, }, + { + .compatible = "st,stih418-plls-c28-a9", + .data = &st_pll4600c28_418_a9, + }, {} };