From patchwork Wed Oct 7 09:08:57 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Gabriel Fernandez X-Patchwork-Id: 54571 Return-Path: X-Original-To: linaro@patches.linaro.org Delivered-To: linaro@patches.linaro.org Received: from mail-la0-f69.google.com (mail-la0-f69.google.com [209.85.215.69]) by patches.linaro.org (Postfix) with ESMTPS id 59A4D22FF8 for ; Wed, 7 Oct 2015 09:10:18 +0000 (UTC) Received: by lafn2 with SMTP id n2sf2891266laf.3 for ; Wed, 07 Oct 2015 02:10:17 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:delivered-to:from:to:cc:subject :date:message-id:in-reply-to:references:sender:precedence:list-id :x-original-sender:x-original-authentication-results:mailing-list :list-post:list-help:list-archive:list-unsubscribe; bh=CBz2ADJQ1agHooZaAALdEPXy4d3jZfjbvhmEsLzkTzM=; b=dduve+BKUISyKKRpWbjpfV3rj78RH/z/T0+RUIb/5BCIZEeuJrEMMjGIzbSBP3GCRz pf9zrw2vU3ASoWDPx1Mh1QqS1k4tX96dwFPHeUVovCrwZBQjQNknoaamB5eADiTt0itu Ra1zfmW2MdHSJaue7dY6h7dO0hGAk44zW2xsNgWNqWxfwUHgJMV+YBYn4EyTphT3CPaz eCGz1sC3xk3kRMihshscAPeGjH36CyNKzNQPcdb7BzXabX+xWWzlFbgpleo3W1H2XOAm 3nEBofQkV1o1A3dEEXt0772RVpTXVWn/PTfZVJr+EL8jGFiS8XYRen7OMT2uHPUO0Bmt iQsA== X-Gm-Message-State: ALoCoQk2RmIAgt0X7uNye4ELwVj8FtjiAzx68eoND9X2W3r0UBBgXwetIzNTK0PLUFm++SL+ZUrl X-Received: by 10.112.138.4 with SMTP id qm4mr6138453lbb.6.1444209017429; Wed, 07 Oct 2015 02:10:17 -0700 (PDT) MIME-Version: 1.0 X-BeenThere: patchwork-forward@linaro.org Received: by 10.25.17.70 with SMTP id g67ls40774lfi.13.gmail; Wed, 07 Oct 2015 02:10:17 -0700 (PDT) X-Received: by 10.25.23.40 with SMTP id n40mr11154494lfi.52.1444209017115; Wed, 07 Oct 2015 02:10:17 -0700 (PDT) Received: from mail-la0-f44.google.com (mail-la0-f44.google.com. [209.85.215.44]) by mx.google.com with ESMTPS id t2si24370822lbo.80.2015.10.07.02.10.17 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Wed, 07 Oct 2015 02:10:17 -0700 (PDT) Received-SPF: pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.215.44 as permitted sender) client-ip=209.85.215.44; Received: by labzv5 with SMTP id zv5so6249145lab.1 for ; Wed, 07 Oct 2015 02:10:17 -0700 (PDT) X-Received: by 10.112.163.131 with SMTP id yi3mr16926649lbb.36.1444209016956; Wed, 07 Oct 2015 02:10:16 -0700 (PDT) X-Forwarded-To: patchwork-forward@linaro.org X-Forwarded-For: patch@linaro.org patchwork-forward@linaro.org Delivered-To: patch@linaro.org Received: by 10.112.59.35 with SMTP id w3csp2319023lbq; Wed, 7 Oct 2015 02:10:14 -0700 (PDT) X-Received: by 10.66.146.197 with SMTP id te5mr48043075pab.35.1444209014694; Wed, 07 Oct 2015 02:10:14 -0700 (PDT) Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id rp16si56227797pab.8.2015.10.07.02.10.14; Wed, 07 Oct 2015 02:10:14 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752617AbbJGJKI (ORCPT + 30 others); Wed, 7 Oct 2015 05:10:08 -0400 Received: from mail-wi0-f179.google.com ([209.85.212.179]:38564 "EHLO mail-wi0-f179.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752138AbbJGJKB (ORCPT ); Wed, 7 Oct 2015 05:10:01 -0400 Received: by wiclk2 with SMTP id lk2so18497136wic.1 for ; Wed, 07 Oct 2015 02:10:00 -0700 (PDT) X-Received: by 10.194.202.137 with SMTP id ki9mr38916785wjc.16.1444209000079; Wed, 07 Oct 2015 02:10:00 -0700 (PDT) Received: from lmenx315.lme.st.com (LPuteaux-656-1-48-212.w82-127.abo.wanadoo.fr. [82.127.83.212]) by smtp.gmail.com with ESMTPSA id fx2sm1637358wib.24.2015.10.07.02.09.57 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Wed, 07 Oct 2015 02:09:59 -0700 (PDT) From: Gabriel Fernandez To: Maxime Coquelin , Michael Turquette , Stephen Boyd , Gabriel Fernandez Cc: Peter Griffin , Pankaj Dev , Olivier Bideau , Geert Uytterhoeven , Fabian Frederick , Rob Herring , Pawel Moll , Mark Rutland , Ian Campbell , Kumar Gala , Srinivas Kandagatla , devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, kernel@stlinux.com, linux-clk@vger.kernel.org Subject: [PATCH v4 2/4] drivers: clk: st: PLL rate change implementation for DVFS Date: Wed, 7 Oct 2015 11:08:57 +0200 Message-Id: <1444208939-10233-3-git-send-email-gabriel.fernandez@linaro.org> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1444208939-10233-1-git-send-email-gabriel.fernandez@linaro.org> References: <1444208939-10233-1-git-send-email-gabriel.fernandez@linaro.org> Sender: linux-kernel-owner@vger.kernel.org Precedence: list List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-Removed-Original-Auth: Dkim didn't pass. X-Original-Sender: gabriel.fernandez@linaro.org X-Original-Authentication-Results: mx.google.com; spf=pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.215.44 as permitted sender) smtp.mailfrom=patch+caf_=patchwork-forward=linaro.org@linaro.org Mailing-list: list patchwork-forward@linaro.org; contact patchwork-forward+owners@linaro.org X-Google-Group-Id: 836684582541 List-Post: , List-Help: , List-Archive: List-Unsubscribe: , Change A9 PLL rate, as per requirement from the cpufreq framework, for DVFS. For rate change, the A9 clock needs to be temporarily sourced from PLL external to A9 and then sourced back to A9-PLL Signed-off-by: Pankaj Dev Signed-off-by: Gabriel Fernandez --- drivers/clk/st/clkgen-mux.c | 3 + drivers/clk/st/clkgen-pll.c | 221 ++++++++++++++++++++++++++++++++++++++++++-- drivers/clk/st/clkgen.h | 2 + 3 files changed, 216 insertions(+), 10 deletions(-) diff --git a/drivers/clk/st/clkgen-mux.c b/drivers/clk/st/clkgen-mux.c index 4f7f6c0..5dc5ce2 100644 --- a/drivers/clk/st/clkgen-mux.c +++ b/drivers/clk/st/clkgen-mux.c @@ -17,6 +17,7 @@ #include #include #include +#include "clkgen.h" static DEFINE_SPINLOCK(clkgena_divmux_lock); static DEFINE_SPINLOCK(clkgenf_lock); @@ -576,6 +577,7 @@ static struct clkgen_mux_data stih415_a9_mux_data = { .offset = 0, .shift = 1, .width = 2, + .lock = &clkgen_a9_lock, }; static struct clkgen_mux_data stih416_a9_mux_data = { .offset = 0, @@ -586,6 +588,7 @@ static struct clkgen_mux_data stih407_a9_mux_data = { .offset = 0x1a4, .shift = 0, .width = 2, + .lock = &clkgen_a9_lock, }; static const struct of_device_id mux_of_match[] = { diff --git a/drivers/clk/st/clkgen-pll.c b/drivers/clk/st/clkgen-pll.c index 092f82c..494848e 100644 --- a/drivers/clk/st/clkgen-pll.c +++ b/drivers/clk/st/clkgen-pll.c @@ -23,6 +23,7 @@ #include "clkgen.h" static DEFINE_SPINLOCK(clkgena_c32_odf_lock); +DEFINE_SPINLOCK(clkgen_a9_lock); /* * Common PLL configuration register bits for PLL800 and PLL1600 C65 @@ -39,6 +40,7 @@ static DEFINE_SPINLOCK(clkgena_c32_odf_lock); #define C32_IDF_MASK (0x7) #define C32_ODF_MASK (0x3f) #define C32_LDF_MASK (0x7f) +#define C32_CP_MASK (0x1f) #define C32_MAX_ODFS (4) @@ -51,15 +53,20 @@ struct clkgen_pll_data { struct clkgen_field pdiv; struct clkgen_field idf; struct clkgen_field ldf; + struct clkgen_field cp; unsigned int num_odfs; struct clkgen_field odf[C32_MAX_ODFS]; struct clkgen_field odf_gate[C32_MAX_ODFS]; + bool switch2pll_en; + struct clkgen_field switch2pll; + spinlock_t *lock; const struct clk_ops *ops; }; static const struct clk_ops st_pll1600c65_ops; 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 clkgen_pll_data st_pll1600c65_ax = { @@ -242,7 +249,11 @@ static const struct clkgen_pll_data st_pll3200c32_407_a9 = { .num_odfs = 1, .odf = { CLKGEN_FIELD(0x1b0, C32_ODF_MASK, 8) }, .odf_gate = { CLKGEN_FIELD(0x1ac, 0x1, 28) }, - .ops = &stm_pll3200c32_ops, + .switch2pll_en = true, + .cp = CLKGEN_FIELD(0x1a8, C32_CP_MASK, 1), + .switch2pll = CLKGEN_FIELD(0x1a4, 0x1, 1), + .lock = &clkgen_a9_lock, + .ops = &stm_pll3200c32_a9_ops, }; /** @@ -268,10 +279,26 @@ struct clkgen_pll { struct clk_hw hw; struct clkgen_pll_data *data; void __iomem *regs_base; + spinlock_t *lock; + + u32 ndiv; + u32 idf; + u32 odf; + u32 cp; }; #define to_clkgen_pll(_hw) container_of(_hw, struct clkgen_pll, hw) +struct stm_pll { + unsigned long mdiv; + unsigned long ndiv; + unsigned long pdiv; + unsigned long odf; + unsigned long idf; + unsigned long ldf; + unsigned long cp; +}; + static int clkgen_pll_is_locked(struct clk_hw *hw) { struct clkgen_pll *pll = to_clkgen_pll(hw); @@ -287,7 +314,7 @@ static int clkgen_pll_is_enabled(struct clk_hw *hw) return !poweroff; } -static int clkgen_pll_enable(struct clk_hw *hw) +static int __clkgen_pll_enable(struct clk_hw *hw) { struct clkgen_pll *pll = to_clkgen_pll(hw); void __iomem *base = pll->regs_base; @@ -303,24 +330,62 @@ static int clkgen_pll_enable(struct clk_hw *hw) ret = readl_relaxed_poll_timeout(base + field->offset, reg, !!((reg >> field->shift) & field->mask), 0, 10000); - if (!ret) + if (!ret) { + if (pll->data->switch2pll_en) + CLKGEN_WRITE(pll, switch2pll, 0); + pr_debug("%s:%s enabled\n", __clk_get_name(hw->clk), __func__); + } return ret; } -static void clkgen_pll_disable(struct clk_hw *hw) +static int clkgen_pll_enable(struct clk_hw *hw) +{ + struct clkgen_pll *pll = to_clkgen_pll(hw); + unsigned long flags = 0; + int ret = 0; + + if (pll->lock) + spin_lock_irqsave(pll->lock, flags); + + ret = __clkgen_pll_enable(hw); + + if (pll->lock) + spin_unlock_irqrestore(pll->lock, flags); + + return ret; +} + +static void __clkgen_pll_disable(struct clk_hw *hw) { struct clkgen_pll *pll = to_clkgen_pll(hw); if (!clkgen_pll_is_enabled(hw)) return; + if (pll->data->switch2pll_en) + CLKGEN_WRITE(pll, switch2pll, 1); + CLKGEN_WRITE(pll, pdn_ctrl, 1); pr_debug("%s:%s disabled\n", __clk_get_name(hw->clk), __func__); } +static void clkgen_pll_disable(struct clk_hw *hw) +{ + struct clkgen_pll *pll = to_clkgen_pll(hw); + unsigned long flags = 0; + + if (pll->lock) + spin_lock_irqsave(pll->lock, flags); + + __clkgen_pll_disable(hw); + + if (pll->lock) + spin_unlock_irqrestore(pll->lock, flags); +} + static unsigned long recalc_stm_pll800c65(struct clk_hw *hw, unsigned long parent_rate) { @@ -372,6 +437,67 @@ static unsigned long recalc_stm_pll1600c65(struct clk_hw *hw, return rate; } +static int clk_pll3200c32_get_params(unsigned long input, unsigned long output, + struct stm_pll *pll) +{ + unsigned long i, n; + unsigned long deviation = ~0; + unsigned long new_freq; + long new_deviation; + /* Charge pump table: highest ndiv value for cp=6 to 25 */ + static const unsigned char cp_table[] = { + 48, 56, 64, 72, 80, 88, 96, 104, 112, 120, + 128, 136, 144, 152, 160, 168, 176, 184, 192 + }; + + /* Output clock range: 800Mhz to 1600Mhz */ + if (output < 800000000 || output > 1600000000) + return -EINVAL; + + input /= 1000; + output /= 1000; + + for (i = 1; i <= 7 && deviation; i++) { + n = i * output / (2 * input); + + /* Checks */ + if (n < 8) + continue; + if (n > 200) + break; + + new_freq = (input * 2 * n) / i; + + new_deviation = abs(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; + + /* Computing recommended charge pump value */ + for (pll->cp = 6; pll->ndiv > cp_table[pll->cp-6]; (pll->cp)++) + ; + + return 0; +} + +static int clk_pll3200c32_get_rate(unsigned long input, struct stm_pll *pll, + unsigned long *rate) +{ + if (!pll->idf) + pll->idf = 1; + + *rate = ((2 * (input / 1000) * pll->ndiv) / pll->idf) * 1000; + + return 0; +} + static unsigned long recalc_stm_pll3200c32(struct clk_hw *hw, unsigned long parent_rate) { @@ -394,6 +520,70 @@ static unsigned long recalc_stm_pll3200c32(struct clk_hw *hw, return rate; } +static long round_rate_stm_pll3200c32(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct stm_pll params; + + if (!clk_pll3200c32_get_params(*prate, rate, ¶ms)) + clk_pll3200c32_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_pll3200c32(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 = 0; + unsigned long flags = 0; + + if (!rate || !parent_rate) + return -EINVAL; + + if (!clk_pll3200c32_get_params(parent_rate, rate, ¶ms)) + clk_pll3200c32_get_rate(parent_rate, ¶ms, &hwrate); + + 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; + pll->cp = params.cp; + + __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); + CLKGEN_WRITE(pll, cp, pll->cp); + + if (pll->lock) + spin_unlock_irqrestore(pll->lock, flags); + + __clkgen_pll_enable(hw); + + return 0; +} + static unsigned long recalc_stm_pll1200c32(struct clk_hw *hw, unsigned long parent_rate) { @@ -442,6 +632,15 @@ static const struct clk_ops stm_pll3200c32_ops = { .recalc_rate = recalc_stm_pll3200c32, }; +static const struct clk_ops stm_pll3200c32_a9_ops = { + .enable = clkgen_pll_enable, + .disable = clkgen_pll_disable, + .is_enabled = clkgen_pll_is_enabled, + .recalc_rate = recalc_stm_pll3200c32, + .round_rate = round_rate_stm_pll3200c32, + .set_rate = set_rate_stm_pll3200c32, +}; + static const struct clk_ops st_pll1200c32_ops = { .enable = clkgen_pll_enable, .disable = clkgen_pll_disable, @@ -452,7 +651,7 @@ static const struct clk_ops st_pll1200c32_ops = { static struct clk * __init clkgen_pll_register(const char *parent_name, struct clkgen_pll_data *pll_data, void __iomem *reg, - const char *clk_name) + const char *clk_name, spinlock_t *lock) { struct clkgen_pll *pll; struct clk *clk; @@ -472,6 +671,7 @@ static struct clk * __init clkgen_pll_register(const char *parent_name, pll->data = pll_data; pll->regs_base = reg; pll->hw.init = &init; + pll->lock = lock; clk = clk_register(NULL, &pll->hw); if (IS_ERR(clk)) { @@ -558,7 +758,7 @@ static void __init clkgena_c65_pll_setup(struct device_node *np) */ clk_data->clks[0] = clkgen_pll_register(parent_name, (struct clkgen_pll_data *) &st_pll1600c65_ax, - reg + CLKGENAx_PLL0_OFFSET, clk_name); + reg + CLKGENAx_PLL0_OFFSET, clk_name, NULL); if (IS_ERR(clk_data->clks[0])) goto err; @@ -587,7 +787,7 @@ static void __init clkgena_c65_pll_setup(struct device_node *np) */ clk_data->clks[2] = clkgen_pll_register(parent_name, (struct clkgen_pll_data *) &st_pll800c65_ax, - reg + CLKGENAx_PLL1_OFFSET, clk_name); + reg + CLKGENAx_PLL1_OFFSET, clk_name, NULL); if (IS_ERR(clk_data->clks[2])) goto err; @@ -614,7 +814,7 @@ static struct clk * __init clkgen_odf_register(const char *parent_name, struct clk_gate *gate; struct clk_divider *div; - flags = CLK_GET_RATE_NOCACHE | CLK_SET_RATE_GATE; + flags = CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT; gate = kzalloc(sizeof(*gate), GFP_KERNEL); if (!gate) @@ -722,7 +922,8 @@ static void __init clkgen_c32_pll_setup(struct device_node *np) if (!pll_base) return; - clk = clkgen_pll_register(parent_name, data, pll_base, np->name); + clk = clkgen_pll_register(parent_name, data, pll_base, np->name, + data->lock); if (IS_ERR(clk)) return; @@ -811,7 +1012,7 @@ static void __init clkgengpu_c32_pll_setup(struct device_node *np) /* * PLL 1200MHz output */ - clk = clkgen_pll_register(parent_name, data, reg, clk_name); + clk = clkgen_pll_register(parent_name, data, reg, clk_name, data->lock); if (!IS_ERR(clk)) of_clk_add_provider(np, of_clk_src_simple_get, clk); diff --git a/drivers/clk/st/clkgen.h b/drivers/clk/st/clkgen.h index 35c8632..f7ec2d9 100644 --- a/drivers/clk/st/clkgen.h +++ b/drivers/clk/st/clkgen.h @@ -9,6 +9,8 @@ Copyright (C) 2014 STMicroelectronics #ifndef __CLKGEN_INFO_H #define __CLKGEN_INFO_H +extern spinlock_t clkgen_a9_lock; + struct clkgen_field { unsigned int offset; unsigned int mask;