From patchwork Fri Jun 23 09:00:55 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Quentin Schulz X-Patchwork-Id: 106247 Delivered-To: patch@linaro.org Received: by 10.140.91.2 with SMTP id y2csp104502qgd; Fri, 23 Jun 2017 02:02:52 -0700 (PDT) X-Received: by 10.99.140.79 with SMTP id q15mr6912988pgn.140.1498208572280; Fri, 23 Jun 2017 02:02:52 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1498208572; cv=none; d=google.com; s=arc-20160816; b=cXH6tNB+1GSbkaVXnmWBVc0b6vnxb+/j8ypQeAUIjkI51bWyO6XknR6hRlrRXiabHn Vi2l6qA7V2McxhdT18CgstDiFcl+zQJTKkEV8GX0cHqy7NbTIqQlrvcTn1sncTUZovC0 idBRhQ8OVGq233whT/XNjcwo2JgGzSGxMFA/isFKsqg2UYf5baOGg8uj4a8vXEylIX/+ Z/lkGP88wpSmXoYNa77OSvfsuxjV93b4Ld90GkEFgJX+K95Sl0zRGhGppgmLWBefWcXA L8ySpxMzALdvDUT1n8BtXrLOn+DYa4Dk1S3ELvhN907kKs6CUgd7fapXeSol4FzLhB7h u2Dw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:arc-authentication-results; bh=yibLCj1/EGYXMdfxuWcTc355QSvx6dp2JK72KGL4AN4=; b=PbdhU9ue4Cfogw2rdPbPsSsOWmosINpTSbj1HR05hK1Oa05r7RJGiCPEJxRtoSh2P3 ZjGia/uzeJ9eGqPD27mGaQSf7AvhWVS4wbIZMEK2nDcS1sLzcp6fr81GIhZzfwvIFO/q P7i/v0AQoiG0BsMHFVUHP4eWoCQnSfV/C6mG44g9PIldOlzFbZC5XGEHPzSObTo2F8tf DgMrauRtP9cWN3OaklDZ0WpBp+B22g1ZYtKbivYZlUCStIpt2g4ALFxV6oKVEAcfvStM tHgSfX42Gmpmz+Gex9YdIzZnRp01tWG+5H+AHjI5U85oeSvYz0lxIdYw8eWO3gad1Qgj VcGw== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id b62si2950859pfc.177.2017.06.23.02.02.51; Fri, 23 Jun 2017 02:02:52 -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; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754624AbdFWJCh (ORCPT + 25 others); Fri, 23 Jun 2017 05:02:37 -0400 Received: from mail.free-electrons.com ([62.4.15.54]:36214 "EHLO mail.free-electrons.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753912AbdFWJBU (ORCPT ); Fri, 23 Jun 2017 05:01:20 -0400 Received: by mail.free-electrons.com (Postfix, from userid 110) id 6764F20934; Fri, 23 Jun 2017 11:01:18 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on mail.free-electrons.com X-Spam-Level: X-Spam-Status: No, score=-1.0 required=5.0 tests=ALL_TRUSTED,SHORTCIRCUIT shortcircuit=ham autolearn=disabled version=3.4.0 Received: from localhost.localdomain (LStLambert-657-1-97-87.w90-63.abo.wanadoo.fr [90.63.216.87]) by mail.free-electrons.com (Postfix) with ESMTPSA id EDBF620904; Fri, 23 Jun 2017 11:01:17 +0200 (CEST) From: Quentin Schulz To: mturquette@baylibre.com, sboyd@codeaurora.org, robh+dt@kernel.org, mark.rutland@arm.com, nicolas.ferre@microchip.com, alexandre.belloni@free-electrons.com, linux@armlinux.org.uk, boris.brezillon@free-electrons.com, lgirdwood@gmail.com, broonie@kernel.org, perex@perex.cz, tiwai@suse.com Cc: Nicolas Ferre , linux-clk@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, alsa-devel@alsa-project.org, thomas.petazzoni@free-electrons.com, Quentin Schulz Subject: [PATCH 2/6] clk: at91: add audio pll clock driver Date: Fri, 23 Jun 2017 11:00:55 +0200 Message-Id: <20170623090059.8371-3-quentin.schulz@free-electrons.com> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20170623090059.8371-1-quentin.schulz@free-electrons.com> References: <20170623090059.8371-1-quentin.schulz@free-electrons.com> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Nicolas Ferre This new clock driver set allows to have a fractional divided clock that would generate a precise clock particularly suitable for audio applications. The main audio pll clock has two children clocks: one that is connected to the PMC, the other that can directly drive a pad. As these two routes have different enable bits and different dividers and divider formula, they are handled by two different drivers. Each of them could modify the rate of the main audio pll parent. The main audio pll clock can output 620MHz to 700MHz. Signed-off-by: Nicolas Ferre Signed-off-by: Quentin Schulz --- .../devicetree/bindings/clock/at91-clock.txt | 10 + arch/arm/mach-at91/Kconfig | 4 + drivers/clk/at91/Makefile | 2 + drivers/clk/at91/clk-audio-pll-pad.c | 204 ++++++++++++++++++ drivers/clk/at91/clk-audio-pll-pmc.c | 175 +++++++++++++++ drivers/clk/at91/clk-audio-pll.c | 237 +++++++++++++++++++++ include/linux/clk/at91_pmc.h | 25 +++ sound/soc/atmel/atmel-classd.c | 20 +- 8 files changed, 658 insertions(+), 19 deletions(-) create mode 100644 drivers/clk/at91/clk-audio-pll-pad.c create mode 100644 drivers/clk/at91/clk-audio-pll-pmc.c create mode 100644 drivers/clk/at91/clk-audio-pll.c -- 2.11.0 diff --git a/Documentation/devicetree/bindings/clock/at91-clock.txt b/Documentation/devicetree/bindings/clock/at91-clock.txt index 5f3ad65daf69..51c259a92d02 100644 --- a/Documentation/devicetree/bindings/clock/at91-clock.txt +++ b/Documentation/devicetree/bindings/clock/at91-clock.txt @@ -81,6 +81,16 @@ Required properties: "atmel,sama5d2-clk-generated": at91 generated clock + "atmel,sama5d2-clk-audio-pll-frac": + at91 audio fractional pll + + "atmel,sama5d2-clk-audio-pll-pad": + at91 audio pll CLK_AUDIO output pin + + "atmel,sama5d2-clk-audio-pll-pmc" + at91 audio pll output on AUDIOPLLCLK that feeds the PMC + and can be used by peripheral clock or generic clock + Required properties for SCKC node: - reg : defines the IO memory reserved for the SCKC. - #size-cells : shall be 0 (reg is used to encode clk id). diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig index d735e5fc4772..9ae14d59a9ce 100644 --- a/arch/arm/mach-at91/Kconfig +++ b/arch/arm/mach-at91/Kconfig @@ -26,6 +26,7 @@ config SOC_SAMA5D2 select HAVE_AT91_USB_CLK select HAVE_AT91_H32MX select HAVE_AT91_GENERATED_CLK + select HAVE_AT91_AUDIO_PLL select PINCTRL_AT91PIO4 help Select this if ou are using one of Atmel's SAMA5D2 family SoC. @@ -125,6 +126,9 @@ config HAVE_AT91_H32MX config HAVE_AT91_GENERATED_CLK bool +config HAVE_AT91_AUDIO_PLL + bool + config SOC_SAM_V4_V5 bool diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile index 13e67bd35cff..c9353d17763a 100644 --- a/drivers/clk/at91/Makefile +++ b/drivers/clk/at91/Makefile @@ -6,6 +6,8 @@ obj-y += pmc.o sckc.o obj-y += clk-slow.o clk-main.o clk-pll.o clk-plldiv.o clk-master.o obj-y += clk-system.o clk-peripheral.o clk-programmable.o +obj-$(CONFIG_HAVE_AT91_AUDIO_PLL) += clk-audio-pll.o +obj-$(CONFIG_HAVE_AT91_AUDIO_PLL) += clk-audio-pll-pmc.o clk-audio-pll-pad.o obj-$(CONFIG_HAVE_AT91_UTMI) += clk-utmi.o obj-$(CONFIG_HAVE_AT91_USB_CLK) += clk-usb.o obj-$(CONFIG_HAVE_AT91_SMD) += clk-smd.o diff --git a/drivers/clk/at91/clk-audio-pll-pad.c b/drivers/clk/at91/clk-audio-pll-pad.c new file mode 100644 index 000000000000..a60ce47b82c6 --- /dev/null +++ b/drivers/clk/at91/clk-audio-pll-pad.c @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2016 Atmel Corporation, + * Nicolas Ferre + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ +#include +#include +#include +#include +#include +#include +#include + +#include "pmc.h" + +/* + * DOC: PAD output for fractional PLL clock for audio + * + * Traits of this clock: + * enable - clk_enable writes divisors and enables PAD output + * rate - rate is adjustable. + * clk->rate = parent->rate / (qdaudio * div)) + * parent - fixed parent. No clk_set_parent support + */ + +#define AUDIO_PLL_QDPAD(qd, div) ((AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV(qd) & \ + AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_MASK) | \ + (AT91_PMC_AUDIO_PLL_QDPAD_DIV(div) & \ + AT91_PMC_AUDIO_PLL_QDPAD_DIV_MASK)) + +struct clk_audio_pad { + struct clk_hw hw; + struct regmap *regmap; + u8 qdaudio; + u8 div; +}; + +#define to_clk_audio_pad(hw) container_of(hw, struct clk_audio_pad, hw) + +static int clk_audio_pll_pad_enable(struct clk_hw *hw) +{ + struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw); + + regmap_update_bits(apad_ck->regmap, AT91_PMC_AUDIO_PLL1, + AT91_PMC_AUDIO_PLL_QDPAD_MASK, + AUDIO_PLL_QDPAD(apad_ck->qdaudio, apad_ck->div)); + regmap_update_bits(apad_ck->regmap, AT91_PMC_AUDIO_PLL0, + AT91_PMC_AUDIO_PLL_PADEN, AT91_PMC_AUDIO_PLL_PADEN); + + return 0; +} + +static void clk_audio_pll_pad_disable(struct clk_hw *hw) +{ + struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw); + + regmap_update_bits(apad_ck->regmap, AT91_PMC_AUDIO_PLL0, + AT91_PMC_AUDIO_PLL_PADEN, 0); +} + +static unsigned long clk_audio_pll_pad_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw); + unsigned long apad_rate = 0; + + if (apad_ck->qdaudio && apad_ck->div) + apad_rate = parent_rate / (apad_ck->qdaudio * apad_ck->div); + + pr_debug("A PLL/PAD: %s, apad_rate = %lu (div = %u, qdaudio = %u)\n", + __func__, apad_rate, apad_ck->div, apad_ck->qdaudio); + + return apad_rate; +} + +static long clk_audio_pll_pad_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct clk_hw *pclk = clk_hw_get_parent(hw); + long best_rate = -EINVAL; + unsigned long best_parent_rate; + unsigned long tmp_qd; + u32 div; + long tmp_rate; + int tmp_diff; + int best_diff = -1; + + pr_debug("A PLL/PAD: %s, rate = %lu (parent_rate = %lu)\n", __func__, + rate, *parent_rate); + + /* + * Rate divisor is actually made of two different divisors, multiplied + * between themselves before dividing the rate. + * tmp_qd goes from 1 to 31 and div is either 2 or 3. + * In order to avoid testing twice the rate divisor (e.g. divisor 12 can + * be found with (tmp_qd, div) = (2, 6) or (3, 4)), we remove any loop + * for a rate divisor when div is 2 and tmp_qd is a multiple of 3. + * We cannot inverse it (condition div is 3 and tmp_qd is even) or we + * would miss some rate divisor that aren't reachable with div being 2 + * (e.g. rate divisor 90 is made with div = 3 and tmp_qd = 30, thus + * tmp_qd is even so we skip it because we think div 2 could make this + * rate divisor which isn't possible since tmp_qd has to be <= 31). + */ + for (tmp_qd = 1; tmp_qd < AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_MAX; tmp_qd++) + for (div = 2; div <= 3; div++) { + if (div == 2 && tmp_qd % 3 == 0) + continue; + + best_parent_rate = clk_hw_round_rate(pclk, + rate * tmp_qd * div); + tmp_rate = best_parent_rate / (div * tmp_qd); + tmp_diff = abs(rate - tmp_rate); + + if (best_diff < 0 || best_diff > tmp_diff) { + *parent_rate = best_parent_rate; + best_rate = tmp_rate; + best_diff = tmp_diff; + } + } + + pr_debug("A PLL/PAD: %s, best_rate = %ld, best_parent_rate = %lu\n", + __func__, best_rate, best_parent_rate); + + return best_rate; +} + +static int clk_audio_pll_pad_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw); + u8 tmp_div; + + pr_debug("A PLL/PAD: %s, rate = %lu (parent_rate = %lu)\n", __func__, + rate, parent_rate); + + if (!rate) + return -EINVAL; + + tmp_div = parent_rate / rate; + if (tmp_div % 3 == 0) { + apad_ck->qdaudio = tmp_div / 3; + apad_ck->div = 3; + } else { + apad_ck->qdaudio = tmp_div / 2; + apad_ck->div = 2; + } + + return 0; +} + +static const struct clk_ops audio_pll_pad_ops = { + .enable = clk_audio_pll_pad_enable, + .disable = clk_audio_pll_pad_disable, + .recalc_rate = clk_audio_pll_pad_recalc_rate, + .round_rate = clk_audio_pll_pad_round_rate, + .set_rate = clk_audio_pll_pad_set_rate, +}; + +static void __init of_sama5d2_clk_audio_pll_pad_setup(struct device_node *np) +{ + struct clk_audio_pad *apad_ck; + struct clk_init_data init; + struct regmap *regmap; + const char *parent_name; + const char *name = np->name; + int ret; + + parent_name = of_clk_get_parent_name(np, 0); + + of_property_read_string(np, "clock-output-names", &name); + + regmap = syscon_node_to_regmap(of_get_parent(np)); + if (IS_ERR(regmap)) + return; + + apad_ck = kzalloc(sizeof(*apad_ck), GFP_KERNEL); + if (!apad_ck) + return; + + init.name = name; + init.ops = &audio_pll_pad_ops; + init.parent_names = (parent_name ? &parent_name : NULL); + init.num_parents = 1; + init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE | + CLK_SET_RATE_PARENT; + + apad_ck->hw.init = &init; + apad_ck->regmap = regmap; + + ret = clk_hw_register(NULL, &apad_ck->hw); + if (ret) + kfree(apad_ck); + else + of_clk_add_hw_provider(np, of_clk_hw_simple_get, &apad_ck->hw); +} + +CLK_OF_DECLARE(of_sama5d2_clk_audio_pll_pad_setup, + "atmel,sama5d2-clk-audio-pll-pad", + of_sama5d2_clk_audio_pll_pad_setup); diff --git a/drivers/clk/at91/clk-audio-pll-pmc.c b/drivers/clk/at91/clk-audio-pll-pmc.c new file mode 100644 index 000000000000..208d0b59eb4e --- /dev/null +++ b/drivers/clk/at91/clk-audio-pll-pmc.c @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2016 Atmel Corporation, + * Nicolas Ferre + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ +#include +#include +#include +#include +#include +#include +#include + +#include "pmc.h" + +/* + * DOC: PMC output for fractional PLL clock for audio + * + * Traits of this clock: + * enable - clk_enable writes qdpmc, and enables PMC output + * rate - rate is adjustable. + * clk->rate = parent->rate / (qdpmc + 1) + * parent - fixed parent. No clk_set_parent support + */ + +#define AUDIO_PLL_FOUT_MIN 620000000 +#define AUDIO_PLL_FOUT_MAX 700000000 +#define AUDIO_PLL_REFERENCE_FOUT 660000000 +#define AUDIO_PLL_QDPMC_MAX (AT91_PMC_AUDIO_PLL_QDPMC_MASK >> \ + AT91_PMC_AUDIO_PLL_QDPMC_OFFSET) +struct clk_audio_pmc { + struct clk_hw hw; + struct regmap *regmap; + u8 qdpmc; +}; + +#define to_clk_audio_pmc(hw) container_of(hw, struct clk_audio_pmc, hw) + +static int clk_audio_pll_pmc_enable(struct clk_hw *hw) +{ + struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(hw); + + regmap_update_bits(apmc_ck->regmap, AT91_PMC_AUDIO_PLL0, + AT91_PMC_AUDIO_PLL_PMCEN | + AT91_PMC_AUDIO_PLL_QDPMC_MASK, + AT91_PMC_AUDIO_PLL_PMCEN | + AT91_PMC_AUDIO_PLL_QDPMC(apmc_ck->qdpmc)); + return 0; +} + +static void clk_audio_pll_pmc_disable(struct clk_hw *hw) +{ + struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(hw); + + regmap_update_bits(apmc_ck->regmap, AT91_PMC_AUDIO_PLL0, + AT91_PMC_AUDIO_PLL_PMCEN, 0); +} + +static unsigned long clk_audio_pll_pmc_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(hw); + unsigned long apmc_rate = 0; + + apmc_rate = parent_rate / (apmc_ck->qdpmc + 1); + + pr_debug("A PLL/PMC: %s, apmc_rate = %lu (qdpmc = %u)\n", __func__, + apmc_rate, apmc_ck->qdpmc); + + return apmc_rate; +} + +static long clk_audio_pll_pmc_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct clk_hw *pclk = clk_hw_get_parent(hw); + long best_rate = -EINVAL; + unsigned long best_parent_rate = 0; + u32 tmp_qd = 0, div; + long tmp_rate; + int tmp_diff; + int best_diff = -1; + + pr_debug("A PLL/PMC: %s, rate = %lu (parent_rate = %lu)\n", __func__, + rate, *parent_rate); + + for (div = 1; div <= AUDIO_PLL_QDPMC_MAX; div++) { + best_parent_rate = clk_round_rate(pclk->clk, rate * div); + tmp_rate = best_parent_rate / div; + tmp_diff = abs(rate - tmp_rate); + + if (best_diff < 0 || best_diff > tmp_diff) { + *parent_rate = best_parent_rate; + best_rate = tmp_rate; + best_diff = tmp_diff; + tmp_qd = div; + } + } + + pr_debug("A PLL/PMC: %s, best_rate = %ld, best_parent_rate = %lu (qd = %d)\n", + __func__, best_rate, *parent_rate, tmp_qd - 1); + + return best_rate; +} + +static int clk_audio_pll_pmc_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(hw); + + if (!rate) + return -EINVAL; + + pr_debug("A PLL/PMC: %s, rate = %lu (parent_rate = %lu)\n", __func__, + rate, parent_rate); + + apmc_ck->qdpmc = parent_rate / rate - 1; + + return 0; +} + +static const struct clk_ops audio_pll_pmc_ops = { + .enable = clk_audio_pll_pmc_enable, + .disable = clk_audio_pll_pmc_disable, + .recalc_rate = clk_audio_pll_pmc_recalc_rate, + .round_rate = clk_audio_pll_pmc_round_rate, + .set_rate = clk_audio_pll_pmc_set_rate, +}; + +static void __init of_sama5d2_clk_audio_pll_pmc_setup(struct device_node *np) +{ + struct clk_audio_pmc *apmc_ck; + struct clk_init_data init; + struct regmap *regmap; + const char *parent_name; + const char *name = np->name; + int ret; + + parent_name = of_clk_get_parent_name(np, 0); + + of_property_read_string(np, "clock-output-names", &name); + + regmap = syscon_node_to_regmap(of_get_parent(np)); + if (IS_ERR(regmap)) + return; + + apmc_ck = kzalloc(sizeof(*apmc_ck), GFP_KERNEL); + if (!apmc_ck) + return; + + init.name = name; + init.ops = &audio_pll_pmc_ops; + init.parent_names = (parent_name ? &parent_name : NULL); + init.num_parents = 1; + init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE | + CLK_SET_RATE_PARENT; + + apmc_ck->hw.init = &init; + apmc_ck->regmap = regmap; + + ret = clk_hw_register(NULL, &apmc_ck->hw); + if (ret) + kfree(apmc_ck); + else + of_clk_add_hw_provider(np, of_clk_hw_simple_get, &apmc_ck->hw); +} + +CLK_OF_DECLARE(of_sama5d2_clk_audio_pll_pmc_setup, + "atmel,sama5d2-clk-audio-pll-pmc", + of_sama5d2_clk_audio_pll_pmc_setup); diff --git a/drivers/clk/at91/clk-audio-pll.c b/drivers/clk/at91/clk-audio-pll.c new file mode 100644 index 000000000000..ad8c8827681e --- /dev/null +++ b/drivers/clk/at91/clk-audio-pll.c @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2016 Atmel Corporation, + * Songjun Wu , + * Nicolas Ferre + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ +#include +#include +#include +#include +#include +#include + +#include "pmc.h" + +/* + * DOC: Fractional PLL clock for audio + * + * Traits of this clock: + * prepare - clk_prepare puts audio PLL in reset state + * enable - clk_enable writes nd, fracr parameters and enables PLL + * rate - rate is adjustable. + * clk->rate = parent->rate * ((nd + 1) + (fracr / 2^22)) + * parent - fixed parent. No clk_set_parent support + */ + +#define AUDIO_PLL_DIV_FRAC BIT(22) +#define AUDIO_PLL_ND_MAX (AT91_PMC_AUDIO_PLL_ND_MASK >> \ + AT91_PMC_AUDIO_PLL_ND_OFFSET) + +#define AUDIO_PLL_FOUT_MIN 620000000 +#define AUDIO_PLL_FOUT_MAX 700000000 + +struct clk_audio_frac { + struct clk_hw hw; + struct regmap *regmap; + u32 fracr; + u8 nd; +}; + +#define to_clk_audio_frac(hw) container_of(hw, struct clk_audio_frac, hw) + +static int clk_audio_pll_enable(struct clk_hw *hw) +{ + struct clk_audio_frac *fck = to_clk_audio_frac(hw); + + regmap_update_bits(fck->regmap, AT91_PMC_AUDIO_PLL0, + AT91_PMC_AUDIO_PLL_RESETN, 0); + regmap_update_bits(fck->regmap, AT91_PMC_AUDIO_PLL0, + AT91_PMC_AUDIO_PLL_RESETN, + AT91_PMC_AUDIO_PLL_RESETN); + regmap_update_bits(fck->regmap, AT91_PMC_AUDIO_PLL1, + AT91_PMC_AUDIO_PLL_FRACR_MASK, fck->fracr); + + /* + * reset and enable have to be done in 2 separated writes + * for AT91_PMC_AUDIO_PLL0 + */ + regmap_update_bits(fck->regmap, AT91_PMC_AUDIO_PLL0, + AT91_PMC_AUDIO_PLL_PLLEN | + AT91_PMC_AUDIO_PLL_ND_MASK, + AT91_PMC_AUDIO_PLL_PLLEN | + AT91_PMC_AUDIO_PLL_ND(fck->nd)); + + return 0; +} + +static void clk_audio_pll_disable(struct clk_hw *hw) +{ + struct clk_audio_frac *fck = to_clk_audio_frac(hw); + + regmap_update_bits(fck->regmap, AT91_PMC_AUDIO_PLL0, + AT91_PMC_AUDIO_PLL_PLLEN, 0); + /* do it in 2 separated writes */ + regmap_update_bits(fck->regmap, AT91_PMC_AUDIO_PLL0, + AT91_PMC_AUDIO_PLL_RESETN, 0); +} + +static unsigned long clk_audio_pll_fout(unsigned long parent_rate, + unsigned long nd, unsigned long fracr) +{ + unsigned long long fr = (unsigned long long)parent_rate * + (unsigned long long)fracr; + + pr_debug("A PLL: %s, fr = %llu\n", __func__, fr); + + fr = DIV_ROUND_CLOSEST_ULL(fr, AUDIO_PLL_DIV_FRAC); + + pr_debug("A PLL: %s, fr = %llu\n", __func__, fr); + + return parent_rate * (nd + 1) + fr; +} + +static unsigned long clk_audio_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_audio_frac *fck = to_clk_audio_frac(hw); + unsigned long fout; + + fout = clk_audio_pll_fout(parent_rate, fck->nd, fck->fracr); + + pr_debug("A PLL: %s, fout = %lu (nd = %u, fracr = %lu)\n", __func__, + fout, fck->nd, (unsigned long)fck->fracr); + + return fout; +} + +static int clk_audio_pll_compute_frac(unsigned long rate, + unsigned long parent_rate, + unsigned long *nd, unsigned long *fracr) +{ + unsigned long long tmp, rem; + + if (!rate) + return -EINVAL; + + tmp = rate; + rem = do_div(tmp, parent_rate); + if (!tmp || tmp >= AUDIO_PLL_ND_MAX) + return -EINVAL; + + *nd = tmp - 1; + + tmp = rem * AUDIO_PLL_DIV_FRAC; + tmp = DIV_ROUND_CLOSEST_ULL(tmp, parent_rate); + if (tmp > AT91_PMC_AUDIO_PLL_FRACR_MASK) + return -EINVAL; + + /* we can cast here as we verified the bounds just above */ + *fracr = (unsigned long)tmp; + + return 0; +} + +static long clk_audio_pll_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + long best_rate = -EINVAL; + unsigned long fracr, nd; + int ret; + + pr_debug("A PLL: %s, rate = %lu (parent_rate = %lu)\n", __func__, rate, + *parent_rate); + + if (rate < AUDIO_PLL_FOUT_MIN) + rate = AUDIO_PLL_FOUT_MIN; + else if (rate > AUDIO_PLL_FOUT_MAX) + rate = AUDIO_PLL_FOUT_MAX; + + ret = clk_audio_pll_compute_frac(rate, *parent_rate, &nd, &fracr); + if (ret) + return ret; + + best_rate = clk_audio_pll_fout(*parent_rate, nd, fracr); + + pr_debug("A PLL: %s, best_rate = %lu (nd = %lu, fracr = %lu)\n", + __func__, best_rate, nd, fracr); + + return best_rate; +} + +static int clk_audio_pll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_audio_frac *fck = to_clk_audio_frac(hw); + unsigned long fracr, nd; + int ret; + + pr_debug("A PLL: %s, rate = %lu (parent_rate = %lu)\n", __func__, rate, + parent_rate); + + if (rate < AUDIO_PLL_FOUT_MIN || rate > AUDIO_PLL_FOUT_MAX) + return -EINVAL; + + ret = clk_audio_pll_compute_frac(rate, parent_rate, &nd, &fracr); + if (ret) + return ret; + + fck->nd = nd; + fck->fracr = fracr; + + return 0; +} + +static const struct clk_ops audio_pll_ops = { + .enable = clk_audio_pll_enable, + .disable = clk_audio_pll_disable, + .recalc_rate = clk_audio_pll_recalc_rate, + .round_rate = clk_audio_pll_round_rate, + .set_rate = clk_audio_pll_set_rate, +}; + +static void __init of_sama5d2_clk_audio_pll_setup(struct device_node *np) +{ + struct clk_audio_frac *fck; + struct clk_init_data init; + struct regmap *regmap; + const char *parent_name; + const char *name = np->name; + int ret; + + parent_name = of_clk_get_parent_name(np, 0); + + of_property_read_string(np, "clock-output-names", &name); + + regmap = syscon_node_to_regmap(of_get_parent(np)); + if (IS_ERR(regmap)) + return; + + fck = kzalloc(sizeof(*fck), GFP_KERNEL); + if (!fck) + return; + + init.name = name; + init.ops = &audio_pll_ops; + init.parent_names = (parent_name ? &parent_name : NULL); + init.num_parents = 1; + init.flags = CLK_SET_RATE_GATE; + + fck->hw.init = &init; + fck->regmap = regmap; + + ret = clk_hw_register(NULL, &fck->hw); + if (ret) + kfree(fck); + else + of_clk_add_hw_provider(np, of_clk_hw_simple_get, &fck->hw); +} + +CLK_OF_DECLARE(of_sama5d2_clk_audio_pll_setup, + "atmel,sama5d2-clk-audio-pll-frac", + of_sama5d2_clk_audio_pll_setup); diff --git a/include/linux/clk/at91_pmc.h b/include/linux/clk/at91_pmc.h index 17f413bbbedf..6aca5ce8a99a 100644 --- a/include/linux/clk/at91_pmc.h +++ b/include/linux/clk/at91_pmc.h @@ -185,4 +185,29 @@ #define AT91_PMC_PCR_EN (0x1 << 28) /* Enable */ #define AT91_PMC_PCR_GCKEN (0x1 << 29) /* GCK Enable */ +#define AT91_PMC_AUDIO_PLL0 0x14c +#define AT91_PMC_AUDIO_PLL_PLLEN (1 << 0) +#define AT91_PMC_AUDIO_PLL_PADEN (1 << 1) +#define AT91_PMC_AUDIO_PLL_PMCEN (1 << 2) +#define AT91_PMC_AUDIO_PLL_RESETN (1 << 3) +#define AT91_PMC_AUDIO_PLL_ND_OFFSET 8 +#define AT91_PMC_AUDIO_PLL_ND_MASK (0x7f << AT91_PMC_AUDIO_PLL_ND_OFFSET) +#define AT91_PMC_AUDIO_PLL_ND(n) ((n) << AT91_PMC_AUDIO_PLL_ND_OFFSET) +#define AT91_PMC_AUDIO_PLL_QDPMC_OFFSET 16 +#define AT91_PMC_AUDIO_PLL_QDPMC_MASK (0x7f << AT91_PMC_AUDIO_PLL_QDPMC_OFFSET) +#define AT91_PMC_AUDIO_PLL_QDPMC(n) ((n) << AT91_PMC_AUDIO_PLL_QDPMC_OFFSET) + +#define AT91_PMC_AUDIO_PLL1 0x150 +#define AT91_PMC_AUDIO_PLL_FRACR_MASK 0x3fffff +#define AT91_PMC_AUDIO_PLL_QDPAD_OFFSET 24 +#define AT91_PMC_AUDIO_PLL_QDPAD_MASK (0x7f << AT91_PMC_AUDIO_PLL_QDPAD_OFFSET) +#define AT91_PMC_AUDIO_PLL_QDPAD(n) ((n) << AT91_PMC_AUDIO_PLL_QDPAD_OFFSET) +#define AT91_PMC_AUDIO_PLL_QDPAD_DIV_OFFSET AT91_PMC_AUDIO_PLL_QDPAD_OFFSET +#define AT91_PMC_AUDIO_PLL_QDPAD_DIV_MASK (0x3 << AT91_PMC_AUDIO_PLL_QDPAD_DIV_OFFSET) +#define AT91_PMC_AUDIO_PLL_QDPAD_DIV(n) ((n) << AT91_PMC_AUDIO_PLL_QDPAD_DIV_OFFSET) +#define AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_OFFSET 26 +#define AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_MAX 0x1f +#define AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_MASK (AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_MAX << AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_OFFSET) +#define AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV(n) ((n) << AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_OFFSET) + #endif diff --git a/sound/soc/atmel/atmel-classd.c b/sound/soc/atmel/atmel-classd.c index b7ef8c59b49a..05bf5004fecc 100644 --- a/sound/soc/atmel/atmel-classd.c +++ b/sound/soc/atmel/atmel-classd.c @@ -32,7 +32,6 @@ struct atmel_classd { struct regmap *regmap; struct clk *pclk; struct clk *gclk; - struct clk *aclk; int irq; const struct atmel_classd_pdata *pdata; }; @@ -332,10 +331,6 @@ static int atmel_classd_codec_dai_startup(struct snd_pcm_substream *substream, struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card); int ret; - ret = clk_prepare_enable(dd->aclk); - if (ret) - return ret; - return clk_prepare_enable(dd->gclk); } @@ -414,9 +409,8 @@ atmel_classd_codec_dai_hw_params(struct snd_pcm_substream *substream, sample_rates[best].rate, sample_rates[best].aclk_rate); clk_disable_unprepare(dd->gclk); - clk_disable_unprepare(dd->aclk); - ret = clk_set_rate(dd->aclk, sample_rates[best].aclk_rate); + ret = clk_set_rate(dd->gclk, sample_rates[best].aclk_rate); if (ret) return ret; @@ -426,10 +420,6 @@ atmel_classd_codec_dai_hw_params(struct snd_pcm_substream *substream, snd_soc_update_bits(codec, CLASSD_INTPMR, mask, val); - ret = clk_prepare_enable(dd->aclk); - if (ret) - return ret; - return clk_prepare_enable(dd->gclk); } @@ -441,7 +431,6 @@ atmel_classd_codec_dai_shutdown(struct snd_pcm_substream *substream, struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card); clk_disable_unprepare(dd->gclk); - clk_disable_unprepare(dd->aclk); } static int atmel_classd_codec_dai_prepare(struct snd_pcm_substream *substream, @@ -596,13 +585,6 @@ static int atmel_classd_probe(struct platform_device *pdev) return ret; } - dd->aclk = devm_clk_get(dev, "aclk"); - if (IS_ERR(dd->aclk)) { - ret = PTR_ERR(dd->aclk); - dev_err(dev, "failed to get audio clock: %d\n", ret); - return ret; - } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); io_base = devm_ioremap_resource(dev, res); if (IS_ERR(io_base)) { From patchwork Fri Jun 23 09:00:56 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Quentin Schulz X-Patchwork-Id: 106246 Delivered-To: patch@linaro.org Received: by 10.140.91.2 with SMTP id y2csp104421qgd; Fri, 23 Jun 2017 02:02:39 -0700 (PDT) X-Received: by 10.99.5.82 with SMTP id 79mr6935701pgf.134.1498208559190; Fri, 23 Jun 2017 02:02:39 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1498208559; cv=none; d=google.com; s=arc-20160816; b=mn3LSN5nBseWYir/FOPUMDqjimJhBvbHc5t6gdvGK6mEPi5b0I363vU+VC6qG5R/u2 zfBqawOhqIeww8VcDCkmkuKgB9ohyhxr/zYwmxITzply5eKB+G/iiNSDXE88naqmH8IW Jmyeyvy0frjJGarcDHOri4GTJeawJSLDtEpj6ifoa/i7GSI2Gxxc/dSk07eQc3kCwcY9 W6CbLZGfkG9AuPjIp1lhFykJT4bR/rnUcVeDO28q7CailB9hS3hI7yVyOuZQ4fpjRH2o +UPEwnjWfnb10xbgf+9klX9vQq1aodiPLbp5hY3kKTK1FPTlyHZ28/wdHz37MuFaYQmU OEKw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:arc-authentication-results; bh=YJkSSF7gNuGWldQEfhsTlodcgFM5ECXg5eFiHXNSMRs=; b=00Zw3mwfi9OswOyCp9931TQZyySN8cBgPj2MmvcyR661d+9v81vgNx5CvoyPhVsSxk SMgr4wO6abBTjsSpFIpSDEjE+VgCcE7Ei8RqC71Od2qv/Q0KsnUCfvi5hMUzyEYBQuZb hPCiWUyNLHjRgwjmugwjVK3WkCZJp7WcI2+ZlPS+La9hOH++QEJ2i5Kty2alFyR3DLQv x7FKjSztY2EV5914oaW28jFC0DEQG5sPb8xX0bUcoxfcY4Hszvnqp7Dp8QVsjOOwkKR6 6LHiaWz4F6WLg8SVOa8JFyFtNyMDUqHueqJlfyFTSnJ4vr4CH4nTrjP4ZoSomo2N623B 8DYw== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id 34si3315084pln.382.2017.06.23.02.02.38; Fri, 23 Jun 2017 02:02:39 -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; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754606AbdFWJCf (ORCPT + 25 others); Fri, 23 Jun 2017 05:02:35 -0400 Received: from mail.free-electrons.com ([62.4.15.54]:36229 "EHLO mail.free-electrons.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753920AbdFWJBU (ORCPT ); Fri, 23 Jun 2017 05:01:20 -0400 Received: by mail.free-electrons.com (Postfix, from userid 110) id C230920958; Fri, 23 Jun 2017 11:01:18 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on mail.free-electrons.com X-Spam-Level: X-Spam-Status: No, score=-1.0 required=5.0 tests=ALL_TRUSTED,SHORTCIRCUIT shortcircuit=ham autolearn=disabled version=3.4.0 Received: from localhost.localdomain (LStLambert-657-1-97-87.w90-63.abo.wanadoo.fr [90.63.216.87]) by mail.free-electrons.com (Postfix) with ESMTPSA id 5894020922; Fri, 23 Jun 2017 11:01:18 +0200 (CEST) From: Quentin Schulz To: mturquette@baylibre.com, sboyd@codeaurora.org, robh+dt@kernel.org, mark.rutland@arm.com, nicolas.ferre@microchip.com, alexandre.belloni@free-electrons.com, linux@armlinux.org.uk, boris.brezillon@free-electrons.com, lgirdwood@gmail.com, broonie@kernel.org, perex@perex.cz, tiwai@suse.com Cc: Cyrille Pitchen , linux-clk@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, alsa-devel@alsa-project.org, thomas.petazzoni@free-electrons.com, Nicolas Ferre , Quentin Schulz Subject: [PATCH 3/6] ARM: dts: at91: sama5d2: add classd nodes Date: Fri, 23 Jun 2017 11:00:56 +0200 Message-Id: <20170623090059.8371-4-quentin.schulz@free-electrons.com> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20170623090059.8371-1-quentin.schulz@free-electrons.com> References: <20170623090059.8371-1-quentin.schulz@free-electrons.com> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Cyrille Pitchen This patch adds nodes for the classd device and its generated clock. Signed-off-by: Cyrille Pitchen Signed-off-by: Nicolas Ferre Signed-off-by: Quentin Schulz --- arch/arm/boot/dts/sama5d2.dtsi | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) -- 2.11.0 diff --git a/arch/arm/boot/dts/sama5d2.dtsi b/arch/arm/boot/dts/sama5d2.dtsi index cc06da394366..a564cd1ba327 100644 --- a/arch/arm/boot/dts/sama5d2.dtsi +++ b/arch/arm/boot/dts/sama5d2.dtsi @@ -494,6 +494,24 @@ clocks = <&plla>; }; + audio_pll_frac: audiopll_fracck { + compatible = "atmel,sama5d2-clk-audio-pll-frac"; + #clock-cells = <0>; + clocks = <&main>; + }; + + audio_pll_pad: audiopll_padck { + compatible = "atmel,sama5d2-clk-audio-pll-pad"; + #clock-cells = <0>; + clocks = <&audio_pll_frac>; + }; + + audio_pll_pmc: audiopll_pmcck { + compatible = "atmel,sama5d2-clk-audio-pll-pmc"; + #clock-cells = <0>; + clocks = <&audio_pll_frac>; + }; + utmi: utmick { compatible = "atmel,at91sam9x5-clk-utmi"; #clock-cells = <0>; @@ -895,7 +913,7 @@ #address-cells = <1>; #size-cells = <0>; interrupt-parent = <&pmc>; - clocks = <&clk32k>, <&main>, <&plladiv>, <&utmi>, <&mck>; + clocks = <&clk32k>, <&main>, <&plladiv>, <&utmi>, <&mck>, <&audio_pll_pmc>; sdmmc0_gclk: sdmmc0_gclk { #clock-cells = <0>; @@ -951,6 +969,12 @@ reg = <57>; atmel,clk-output-range = <0 80000000>; }; + + classd_gclk: classd_gclk { + #clock-cells = <0>; + reg = <59>; + atmel,clk-output-range = <0 100000000>; + }; }; }; @@ -1406,6 +1430,19 @@ status = "okay"; }; + classd: classd@fc048000 { + compatible = "atmel,sama5d2-classd"; + reg = <0xfc048000 0x100>; + interrupts = <59 IRQ_TYPE_LEVEL_HIGH 7>; + dmas = <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(47))>; + dma-names = "tx"; + clocks = <&classd_clk>, <&classd_gclk>; + clock-names = "pclk", "gclk"; + status = "disabled"; + }; + can1: can@fc050000 { compatible = "bosch,m_can"; reg = <0xfc050000 0x4000>, <0x210000 0x4000>; From patchwork Fri Jun 23 09:00:58 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Quentin Schulz X-Patchwork-Id: 106243 Delivered-To: patch@linaro.org Received: by 10.140.91.2 with SMTP id y2csp104061qgd; Fri, 23 Jun 2017 02:01:51 -0700 (PDT) X-Received: by 10.84.232.5 with SMTP id h5mr8054780plk.261.1498208511086; Fri, 23 Jun 2017 02:01:51 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1498208511; cv=none; d=google.com; s=arc-20160816; b=UNMuanRGnuEmSSx4mcUpQcX8CR1PKo2Pukd1523kL98KgFnHnr9FFUVFU7XcHW9oCI 8bUopYhiz0Z8qnFyRhyKK5Y4CDMvvfs364KR1s4yxZ4ApBlrTka4LXt7bMJ5ZIgtbtDg pEPjqJntScKy51MHyxoM3T/Kcd4NLnxnakSEcFJCCVnh4mxwonYQwzqaxiSU651+wRmy ppx40oLeVicCQEybGdmUEYl36cbuwpJ3qNbJ6on1P4/lbJ8onntQ70KWTfhAh0sY4RAh 8dOVeRcWY3tR+aGzxwfFR+Vyv5jUTBZju8gjjijwhOWNZXFO17Yh5k1pIiNRSN2dSek3 vyhA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:arc-authentication-results; bh=uApCZBbD30SqDCyR6FdxrXAwl3ilqiJCPwsUcT4s1B8=; b=dnYtoVMeBwGb/zjlcHdJbl5yC/N+JpCTvNOuq7fH52JX3/qe5ar1ppECJoe/tuMD6S UMEwyc5kNTp+lVfZOyeOvFcyWd3G+21N93LuUJwp1qWn7PcoEa2m5+B1wdU8TrXWAWdA XhEICRbCE8r/5lAtTMFNKbivKQKFGMmnH8SaZrE62JpJ+E71zwXzeLycAYQY6QqJL+78 42xU8slsGa+8drnNGilnqEt9/D552rGEBT1Xoilln8E7j4V7wnT6TvI2Wnv+xmFmZCse tGswcDcMmlYF1C2nl9qvfOyyvWlw+j2B0iXVxhhPxjPnIdO01+AFKcvnw4beSZMOU4GH E5xA== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id c206si2933971pfc.424.2017.06.23.02.01.50; Fri, 23 Jun 2017 02:01:51 -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; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754323AbdFWJBc (ORCPT + 25 others); Fri, 23 Jun 2017 05:01:32 -0400 Received: from mail.free-electrons.com ([62.4.15.54]:36261 "EHLO mail.free-electrons.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754140AbdFWJBX (ORCPT ); Fri, 23 Jun 2017 05:01:23 -0400 Received: by mail.free-electrons.com (Postfix, from userid 110) id 7AAA720981; Fri, 23 Jun 2017 11:01:19 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on mail.free-electrons.com X-Spam-Level: X-Spam-Status: No, score=-1.0 required=5.0 tests=ALL_TRUSTED,SHORTCIRCUIT shortcircuit=ham autolearn=disabled version=3.4.0 Received: from localhost.localdomain (LStLambert-657-1-97-87.w90-63.abo.wanadoo.fr [90.63.216.87]) by mail.free-electrons.com (Postfix) with ESMTPSA id 1C8CA20922; Fri, 23 Jun 2017 11:01:19 +0200 (CEST) From: Quentin Schulz To: mturquette@baylibre.com, sboyd@codeaurora.org, robh+dt@kernel.org, mark.rutland@arm.com, nicolas.ferre@microchip.com, alexandre.belloni@free-electrons.com, linux@armlinux.org.uk, boris.brezillon@free-electrons.com, lgirdwood@gmail.com, broonie@kernel.org, perex@perex.cz, tiwai@suse.com Cc: Quentin Schulz , linux-clk@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, alsa-devel@alsa-project.org, thomas.petazzoni@free-electrons.com Subject: [PATCH 5/6] clk: at91: clk-generated: make gclk determine audio_pll rate Date: Fri, 23 Jun 2017 11:00:58 +0200 Message-Id: <20170623090059.8371-6-quentin.schulz@free-electrons.com> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20170623090059.8371-1-quentin.schulz@free-electrons.com> References: <20170623090059.8371-1-quentin.schulz@free-electrons.com> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org This allows gclk to determine audio_pll rate and set the parent rate accordingly. However, there are multiple children clocks that could technically change the rate of audio_pll. Since the clock rate is usually critical for IPs, it isn't a good idea to have multiple clocks changing audio_pll clock rate. There isn't any rate locking system for the moment, thus we enforce that classd_gclk be the only one allowed to change audio_pll rate. To remain consistent, we deny other clocks to be children of audio_pll so classd_gclk altering audio_pll rate does not impact any other device. This needs to be re-worked once we have a rate locking system so two clocks could be children of the same clock and be sure the clock rate isn't changed by one or the other. Signed-off-by: Quentin Schulz --- drivers/clk/at91/clk-generated.c | 41 ++++++++++++++++++++++++++++++++++------ 1 file changed, 35 insertions(+), 6 deletions(-) -- 2.11.0 diff --git a/drivers/clk/at91/clk-generated.c b/drivers/clk/at91/clk-generated.c index 6530a2e7e84d..e66ee735a569 100644 --- a/drivers/clk/at91/clk-generated.c +++ b/drivers/clk/at91/clk-generated.c @@ -26,6 +26,9 @@ #define GENERATED_SOURCE_MAX 6 #define GENERATED_MAX_DIV 255 +#define GCK_ID_CLASSD 59 +#define GCK_INDEX_DT_AUDIO_PLL 5 + struct clk_generated { struct clk_hw hw; struct regmap *regmap; @@ -126,15 +129,14 @@ static int clk_generated_determine_rate(struct clk_hw *hw, { struct clk_generated *gck = to_clk_generated(hw); struct clk_hw *parent = NULL; + struct clk_rate_request req_parent = *req; long best_rate = -EINVAL; - unsigned long min_rate; + unsigned long min_rate, parent_rate; int best_diff = -1; int i; + u32 div; - for (i = 0; i < clk_hw_get_num_parents(hw); i++) { - u32 div; - unsigned long parent_rate; - + for (i = 0; i < clk_hw_get_num_parents(hw) - 1; i++) { parent = clk_hw_get_parent_by_index(hw, i); if (!parent) continue; @@ -150,11 +152,37 @@ static int clk_generated_determine_rate(struct clk_hw *hw, clk_generated_best_diff(req, parent, parent_rate, div, &best_diff, &best_rate); + if (!best_diff) + break; + } + + /* + * The audio_pll rate can be modified unlike the five others clocks that + * should never be altered. + * The audio_pll can technically be used by multiple consumers. However, + * they all are likely to want to modify its rate while the rate is + * critical for each one. Since we do not have a rate locking for the + * moment, let classd_gclk be the only consumer of the audio_pll clock. + */ + + if (gck->id != GCK_ID_CLASSD) + goto end; + + parent = clk_hw_get_parent_by_index(hw, GCK_INDEX_DT_AUDIO_PLL); + if (!parent) + goto end; + + for (div = 1; div < GENERATED_MAX_DIV + 2; div++) { + req_parent.rate = req->rate * div; + __clk_determine_rate(parent, &req_parent); + clk_generated_best_diff(req, parent, req_parent.rate, div, + &best_diff, &best_rate); if (!best_diff) break; } +end: pr_debug("GCLK: %s, best_rate = %ld, parent clk: %s @ %ld\n", __func__, best_rate, __clk_get_name((req->best_parent_hw)->clk), @@ -264,7 +292,8 @@ at91_clk_register_generated(struct regmap *regmap, spinlock_t *lock, init.ops = &generated_ops; init.parent_names = parent_names; init.num_parents = num_parents; - init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE; + init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE | + CLK_SET_RATE_PARENT; gck->id = id; gck->hw.init = &init; From patchwork Fri Jun 23 09:00:59 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Quentin Schulz X-Patchwork-Id: 106242 Delivered-To: patch@linaro.org Received: by 10.140.91.2 with SMTP id y2csp103994qgd; Fri, 23 Jun 2017 02:01:42 -0700 (PDT) X-Received: by 10.84.200.200 with SMTP id u8mr7605532plh.63.1498208502517; Fri, 23 Jun 2017 02:01:42 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1498208502; cv=none; d=google.com; s=arc-20160816; b=GtUU2QCAtn0rO/sQn4wowcajGITv49xaeVFCU0sIC6EoVFRwg8CSDndfAt4q4xBrOW a8DybFXMPk4+TVSkNfiBtFX0Lq7AHLaV3AfvIfy/dblonzTMMX/7tVZLi/u3lagLOwqy LaGH/RUzcSyeN0WY8andrM/1VcAbmLLb8xvhJRmqEFqllzLCtDS1G+ZEnxDdWpfnE71f 0szAbtFBO9mptVLAY/i6pvZt/ajKwsZmDR2X8ZV0c6jVOGHmXacWG1etxoF77JSkZAwm CWnqXNIPU7JdBNfF+uzeNcxKe2CX/CWM7drZae3BXHm/80Ki4Vd7rDReVk5afWobrXlw wgcg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:arc-authentication-results; bh=ofKL4D+hNaequF5Bx9IBipYMfQT8kvXw+KP8O/CfXHM=; b=BVCu6JEkyNjZZYvo+TnShDzwmEldwVbe53qK4O/+BT0ySas+O61NZ6n+hqU6tb9GAX 9o07DTjT+JEGBThY+g6mJKXzH4EqMyq9wkQ50CeDY4KEHMsdUyXxyT2S3Rc850gqgiW6 W8ARZbDXa2AxvQkuvcc6xWPs4vldeCGvigDrS5iqCqfeGQyetAzE1le6YfLL27P2OKyJ vQaVR0uEmHhR1YaUP/Xm3WsL1q5jkCDqHg2bElK1IC/JDNV3lQJgw8Vrrbrz7y72zGyc til4J2+zDEtXr4OeQyUS+DwbVXL9afUv3UVnyOXWcSrFe+GKJbrB/jk66pRVGVsdb9F4 P2kw== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id c206si2933971pfc.424.2017.06.23.02.01.42; Fri, 23 Jun 2017 02:01:42 -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; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754360AbdFWJBe (ORCPT + 25 others); Fri, 23 Jun 2017 05:01:34 -0400 Received: from mail.free-electrons.com ([62.4.15.54]:36262 "EHLO mail.free-electrons.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754123AbdFWJBX (ORCPT ); Fri, 23 Jun 2017 05:01:23 -0400 Received: by mail.free-electrons.com (Postfix, from userid 110) id DA9C320993; Fri, 23 Jun 2017 11:01:19 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on mail.free-electrons.com X-Spam-Level: X-Spam-Status: No, score=-1.0 required=5.0 tests=ALL_TRUSTED,SHORTCIRCUIT shortcircuit=ham autolearn=disabled version=3.4.0 Received: from localhost.localdomain (LStLambert-657-1-97-87.w90-63.abo.wanadoo.fr [90.63.216.87]) by mail.free-electrons.com (Postfix) with ESMTPSA id 71E0E20904; Fri, 23 Jun 2017 11:01:19 +0200 (CEST) From: Quentin Schulz To: mturquette@baylibre.com, sboyd@codeaurora.org, robh+dt@kernel.org, mark.rutland@arm.com, nicolas.ferre@microchip.com, alexandre.belloni@free-electrons.com, linux@armlinux.org.uk, boris.brezillon@free-electrons.com, lgirdwood@gmail.com, broonie@kernel.org, perex@perex.cz, tiwai@suse.com Cc: Cyrille Pitchen , linux-clk@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, alsa-devel@alsa-project.org, thomas.petazzoni@free-electrons.com, Nicolas Ferre , Quentin Schulz Subject: [PATCH 6/6] ARM: dts: at91: sama5d2_xplained: add pin muxing and enable classd Date: Fri, 23 Jun 2017 11:00:59 +0200 Message-Id: <20170623090059.8371-7-quentin.schulz@free-electrons.com> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20170623090059.8371-1-quentin.schulz@free-electrons.com> References: <20170623090059.8371-1-quentin.schulz@free-electrons.com> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Cyrille Pitchen This patch adds the pin muxing for classd and enables it. Signed-off-by: Cyrille Pitchen Signed-off-by: Nicolas Ferre Signed-off-by: Quentin Schulz --- arch/arm/boot/dts/at91-sama5d2_xplained.dts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) -- 2.11.0 diff --git a/arch/arm/boot/dts/at91-sama5d2_xplained.dts b/arch/arm/boot/dts/at91-sama5d2_xplained.dts index 2e2c3d1a1fa2..e92b030ca45a 100644 --- a/arch/arm/boot/dts/at91-sama5d2_xplained.dts +++ b/arch/arm/boot/dts/at91-sama5d2_xplained.dts @@ -355,6 +355,14 @@ bias-pull-up; }; + pinctrl_classd_default: classd_default { + pinmux = , + , + , + ; + bias-pull-up; + }; + pinctrl_flx0_default: flx0_default { pinmux = , ; @@ -488,6 +496,14 @@ }; + classd: classd@fc048000 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_classd_default>; + atmel,pwm-type = "diff"; + atmel,non-overlap-time = <10>; + status = "okay"; + }; + can1: can@fc050000 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_can1_default>;