From patchwork Sun Sep 26 09:58:58 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dario Binacchi X-Patchwork-Id: 1532942 X-Patchwork-Delegate: trini@ti.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=libero.it header.i=@libero.it header.a=rsa-sha256 header.s=s2021 header.b=cExbNagb; dkim-atps=neutral Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de (client-ip=85.214.62.61; helo=phobos.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Received: from phobos.denx.de (phobos.denx.de [85.214.62.61]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4HHLq251mbz9t14 for ; Sun, 26 Sep 2021 19:59:58 +1000 (AEST) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id 5CD81835E4; Sun, 26 Sep 2021 11:59:36 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=quarantine dis=none) header.from=libero.it Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (2048-bit key; unprotected) header.d=libero.it header.i=@libero.it header.b="cExbNagb"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id 7AF08835CA; Sun, 26 Sep 2021 11:59:24 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM,SPF_HELO_PASS, SPF_PASS autolearn=ham autolearn_force=no version=3.4.2 Received: from libero.it (smtp-18.italiaonline.it [213.209.10.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id E6A7D835D5 for ; Sun, 26 Sep 2021 11:59:15 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=quarantine dis=none) header.from=libero.it Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=dariobin@libero.it Received: from passgat-Modern-14-A10M.homenet.telecomitalia.it ([82.56.38.87]) by smtp-18.iol.local with ESMTPA id UQwAmaUTCBqjpUQwFmAPSm; Sun, 26 Sep 2021 11:59:15 +0200 x-libjamoibt: 1601 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=libero.it; s=s2021; t=1632650355; bh=E8aCbix/jb0bd1qyXhXB15YKny+1IvOMCN1mQBJnKgk=; h=From; b=cExbNagb3XBRLD+fEQm5WOPEBBtD2z5zM4iJjLD9gC0trdOK5U314mf/1E3BSiuJp jo6+xVcTa1JiVVOQEMjighFXleqiNrJ+vrHW8IvnyznzJn2hxRbPBuQJKJ++ugfGPm MyXKrDczmoaOQD2ptQ/utVQsERpU4BbEnsvT2IZaB+irMzmUbyNSaiIYwsGrro5aER jWr8aVHQ9b1wmgOtYIQIfd0lH4hCB2XWwAHeKuIc8kxSvGnwN2QZ4XC2pn6OS3TPh7 OSWPSUTl8KuRIYwoN55e9QZtVcBX9NJTTxE8/ckei6ny6jJlJg2wxxFaQW6BP1xYam iRcPZpY8UirSQ== X-CNFS-Analysis: v=2.4 cv=Q4sXX66a c=1 sm=1 tr=0 ts=61504473 cx=a_exe a=DqrqJ578uMgtS2r0uCYQPA==:117 a=DqrqJ578uMgtS2r0uCYQPA==:17 a=VwQbUJbxAAAA:8 a=vVVTR7F-XuZuLTJS3bsA:9 a=5yUOnwQy5QICz8m5uxDm:22 a=AjGcO6oz07-iQ99wixmX:22 From: Dario Binacchi To: u-boot@lists.denx.de Cc: Dario Binacchi , Lukasz Majewski , Tom Rini Subject: [PATCH 3/3] clk: ti: add am33xx/am43xx spread spectrum clock support Date: Sun, 26 Sep 2021 11:58:58 +0200 Message-Id: <20210926095858.31278-4-dariobin@libero.it> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20210926095858.31278-1-dariobin@libero.it> References: <20210926095858.31278-1-dariobin@libero.it> X-CMAE-Envelope: MS4xfDTNIsvbw2yxPk5xxU8JNO9CpfJ61dLMDNLZaMq6iWVEK/yBE5+Lnc/vy2C2fXgvWVE9X6YK+Az9UUalwkkJO3puhWpj3E2vnc8qof9Hzo4VSiUl42p5 vhfUu06dNfIOntCvwyQDQnJUNv4cmIDlyf5u8Pdw4+OQQPq5orYAV3TebqbwdupP6Lqv4xqdyJqVJk64c54xtvyQ/iW3nMt94MDExDckrxI5Vqtu+gqQCn3C tSYVCpRSp4b1kdqKSXosAp6yJDhV3A2Uv17XcdjgG7fJngcDsVaCXGB6olqg9x42to6Qs+8oTQHG1r0ujLA1gg== X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.34 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.103.2 at phobos.denx.de X-Virus-Status: Clean The patch enables spread spectrum clocking (SSC) for MPU and LCD PLLs. As reported by the TI spruh73x/spruhl7x RM, SSC is only supported for the DISP/LCD and MPU PLLs on am33xx/am43xx. SSC is not supported for DDR, PER, and CORE PLLs. Calculating the required values and setting the registers accordingly was taken from the set_mpu_spreadspectrum routine contained in the arch/arm/mach-omap2/am33xx/clock_am33xx.c file of the u-boot project. In locked condition, DPLL output clock = CLKINP *[M/N]. In case of SSC enabled, the reference manual explains that there is a restriction of range of M values. Since the clk_ti_am3_dpll_round_rate() attempts to select the minimum possible N, the value of M obtained is not guaranteed to be within the range required. With the new "ti,min-div" parameter it is possible to increase N and consequently M to satisfy the constraint imposed by SSC. Link: https://lore.kernel.org/r/20210606202253.31649-6-dariobin@libero.it Signed-off-by: Dario Binacchi --- arch/arm/include/asm/arch-am33xx/clock.h | 12 +++ drivers/clk/ti/clk-am3-dpll.c | 131 ++++++++++++++++++++++- 2 files changed, 140 insertions(+), 3 deletions(-) diff --git a/arch/arm/include/asm/arch-am33xx/clock.h b/arch/arm/include/asm/arch-am33xx/clock.h index 5d775902bb..79e3b8c7d9 100644 --- a/arch/arm/include/asm/arch-am33xx/clock.h +++ b/arch/arm/include/asm/arch-am33xx/clock.h @@ -78,6 +78,18 @@ #define CM_CLKSEL_DPLL_N_SHIFT 0 #define CM_CLKSEL_DPLL_N_MASK 0x7F +/* CM_SSC_DELTAM_DPLL */ +#define CM_SSC_DELTAM_DPLL_FRAC_SHIFT 0 +#define CM_SSC_DELTAM_DPLL_FRAC_MASK GENMASK(17, 0) +#define CM_SSC_DELTAM_DPLL_INT_SHIFT 18 +#define CM_SSC_DELTAM_DPLL_INT_MASK GENMASK(19, 18) + +/* CM_SSC_MODFREQ_DPLL */ +#define CM_SSC_MODFREQ_DPLL_MANT_SHIFT 0 +#define CM_SSC_MODFREQ_DPLL_MANT_MASK GENMASK(6, 0) +#define CM_SSC_MODFREQ_DPLL_EXP_SHIFT 7 +#define CM_SSC_MODFREQ_DPLL_EXP_MASK GENMASK(10, 8) + struct dpll_params { u32 m; u32 n; diff --git a/drivers/clk/ti/clk-am3-dpll.c b/drivers/clk/ti/clk-am3-dpll.c index 916d308034..398a011a5c 100644 --- a/drivers/clk/ti/clk-am3-dpll.c +++ b/drivers/clk/ti/clk-am3-dpll.c @@ -27,11 +27,17 @@ struct clk_ti_am3_dpll_priv { struct clk_ti_reg clkmode_reg; struct clk_ti_reg idlest_reg; struct clk_ti_reg clksel_reg; + struct clk_ti_reg ssc_deltam_reg; + struct clk_ti_reg ssc_modfreq_reg; struct clk clk_bypass; struct clk clk_ref; u16 last_rounded_mult; u8 last_rounded_div; + u8 min_div; ulong max_rate; + u32 ssc_modfreq; + u32 ssc_deltam; + bool ssc_downspread; }; static ulong clk_ti_am3_dpll_round_rate(struct clk *clk, ulong rate) @@ -51,7 +57,7 @@ static ulong clk_ti_am3_dpll_round_rate(struct clk *clk, ulong rate) err = rate; err_min = rate; ref_rate = clk_get_rate(&priv->clk_ref); - for (d = 1; err_min && d <= 128; d++) { + for (d = priv->min_div; err_min && d <= 128; d++) { for (m = 2; m <= 2047; m++) { r = (ref_rate * m) / d; err = abs(r - rate); @@ -71,8 +77,8 @@ static ulong clk_ti_am3_dpll_round_rate(struct clk *clk, ulong rate) priv->last_rounded_mult = mult; priv->last_rounded_div = div; - dev_dbg(clk->dev, "rate=%ld, best_rate=%ld, mult=%d, div=%d\n", rate, - ret, mult, div); + dev_dbg(clk->dev, "rate=%ld, min-div: %d, best_rate=%ld, mult=%d, div=%d\n", + rate, priv->min_div, ret, mult, div); return ret; } @@ -107,6 +113,96 @@ static int clk_ti_am3_dpll_state(struct clk *clk, u8 state) return 0; } +/** + * clk_ti_am3_dpll_ssc_program - set spread-spectrum clocking registers + * @clk: struct clk * of DPLL to set + * + * Enable the DPLL spread spectrum clocking if frequency modulation and + * frequency spreading have been set, otherwise disable it. + */ +static void clk_ti_am3_dpll_ssc_program(struct clk *clk) +{ + struct clk_ti_am3_dpll_priv *priv = dev_get_priv(clk->dev); + unsigned long ref_rate; + u32 v, ctrl, mod_freq_divider, exponent, mantissa; + u32 deltam_step, deltam_ceil; + + ctrl = clk_ti_readl(&priv->clkmode_reg); + + if (priv->ssc_modfreq && priv->ssc_deltam) { + ctrl |= CM_CLKMODE_DPLL_SSC_EN_MASK; + + if (priv->ssc_downspread) + ctrl |= CM_CLKMODE_DPLL_SSC_DOWNSPREAD_MASK; + else + ctrl &= ~CM_CLKMODE_DPLL_SSC_DOWNSPREAD_MASK; + + ref_rate = clk_get_rate(&priv->clk_ref); + mod_freq_divider = + (ref_rate / priv->last_rounded_div) / (4 * priv->ssc_modfreq); + if (priv->ssc_modfreq > (ref_rate / 70)) + dev_warn(clk->dev, + "clock: SSC modulation frequency of DPLL %s greater than %ld\n", + clk->dev->name, ref_rate / 70); + + exponent = 0; + mantissa = mod_freq_divider; + while ((mantissa > 127) && (exponent < 7)) { + exponent++; + mantissa /= 2; + } + if (mantissa > 127) + mantissa = 127; + + v = clk_ti_readl(&priv->ssc_modfreq_reg); + v &= ~(CM_SSC_MODFREQ_DPLL_MANT_MASK | CM_SSC_MODFREQ_DPLL_EXP_MASK); + v |= mantissa << __ffs(CM_SSC_MODFREQ_DPLL_MANT_MASK); + v |= exponent << __ffs(CM_SSC_MODFREQ_DPLL_EXP_MASK); + clk_ti_writel(v, &priv->ssc_modfreq_reg); + dev_dbg(clk->dev, + "mod_freq_divider: %u, exponent: %u, mantissa: %u, modfreq_reg: 0x%x\n", + mod_freq_divider, exponent, mantissa, v); + + deltam_step = priv->last_rounded_mult * priv->ssc_deltam; + deltam_step /= 10; + if (priv->ssc_downspread) + deltam_step /= 2; + + deltam_step <<= __ffs(CM_SSC_DELTAM_DPLL_INT_MASK); + deltam_step /= 100; + deltam_step /= mod_freq_divider; + if (deltam_step > 0xFFFFF) + deltam_step = 0xFFFFF; + + deltam_ceil = (deltam_step & CM_SSC_DELTAM_DPLL_INT_MASK) >> + __ffs(CM_SSC_DELTAM_DPLL_INT_MASK); + if (deltam_step & CM_SSC_DELTAM_DPLL_FRAC_MASK) + deltam_ceil++; + + if ((priv->ssc_downspread && + ((priv->last_rounded_mult - (2 * deltam_ceil)) < 20 || + priv->last_rounded_mult > 2045)) || + ((priv->last_rounded_mult - deltam_ceil) < 20 || + (priv->last_rounded_mult + deltam_ceil) > 2045)) + dev_warn(clk->dev, + "clock: SSC multiplier of DPLL %s is out of range\n", + clk->dev->name); + + v = clk_ti_readl(&priv->ssc_deltam_reg); + v &= ~(CM_SSC_DELTAM_DPLL_INT_MASK | CM_SSC_DELTAM_DPLL_FRAC_MASK); + v |= deltam_step << __ffs(CM_SSC_DELTAM_DPLL_INT_MASK | + CM_SSC_DELTAM_DPLL_FRAC_MASK); + clk_ti_writel(v, &priv->ssc_deltam_reg); + dev_dbg(clk->dev, + "deltam_step: %u, deltam_ceil: %u, deltam_reg: 0x%x\n", + deltam_step, deltam_ceil, v); + } else { + ctrl &= ~CM_CLKMODE_DPLL_SSC_EN_MASK; + } + + clk_ti_writel(ctrl, &priv->clkmode_reg); +} + static ulong clk_ti_am3_dpll_set_rate(struct clk *clk, ulong rate) { struct clk_ti_am3_dpll_priv *priv = dev_get_priv(clk->dev); @@ -136,6 +232,8 @@ static ulong clk_ti_am3_dpll_set_rate(struct clk *clk, ulong rate) clk_ti_writel(v, &priv->clksel_reg); + clk_ti_am3_dpll_ssc_program(clk); + /* lock dpll */ clk_ti_am3_dpll_clken(priv, DPLL_EN_LOCK); @@ -229,6 +327,7 @@ static int clk_ti_am3_dpll_of_to_plat(struct udevice *dev) struct clk_ti_am3_dpll_priv *priv = dev_get_priv(dev); struct clk_ti_am3_dpll_drv_data *data = (struct clk_ti_am3_dpll_drv_data *)dev_get_driver_data(dev); + u32 min_div; int err; priv->max_rate = data->max_rate; @@ -251,6 +350,32 @@ static int clk_ti_am3_dpll_of_to_plat(struct udevice *dev) return err; } + err = clk_ti_get_reg_addr(dev, 3, &priv->ssc_deltam_reg); + if (err) { + dev_err(dev, "failed to get SSC deltam register\n"); + return err; + } + + err = clk_ti_get_reg_addr(dev, 4, &priv->ssc_modfreq_reg); + if (err) { + dev_err(dev, "failed to get SSC modfreq register\n"); + return err; + } + + if (dev_read_u32(dev, "ti,ssc-modfreq-hz", &priv->ssc_modfreq)) + priv->ssc_modfreq = 0; + + if (dev_read_u32(dev, "ti,ssc-deltam", &priv->ssc_deltam)) + priv->ssc_deltam = 0; + + priv->ssc_downspread = dev_read_bool(dev, "ti,ssc-downspread"); + + if (dev_read_u32(dev, "ti,min-div", &min_div) || min_div == 0 || + min_div > 128) + priv->min_div = 1; + else + priv->min_div = min_div; + return 0; }