From nobody Tue Jun 30 09:15:59 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 881AFC4167B for ; Fri, 21 Jan 2022 07:45:24 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1379123AbiAUHpV (ORCPT ); Fri, 21 Jan 2022 02:45:21 -0500 Received: from mail-sz.amlogic.com ([211.162.65.117]:14996 "EHLO mail-sz.amlogic.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1348992AbiAUHpP (ORCPT ); Fri, 21 Jan 2022 02:45:15 -0500 Received: from droid11-sz.amlogic.com (10.28.8.21) by mail-sz.amlogic.com (10.28.11.5) with Microsoft SMTP Server id 15.1.2176.2; Fri, 21 Jan 2022 15:45:14 +0800 From: Liang Yang To: Neil Armstrong , Jerome Brunet , Kevin Hilman , Michael Turquette , Stephen Boyd , Rob Herring , CC: Liang Yang , Martin Blumenstingl , Jianxin Pan , Victor Wan , XianWei Zhao , Kelvin Zhang , BiChao Zheng , YongHui Yu , , , , Subject: [PATCH v10 1/4] clk: meson: add one based divider support for sclk Date: Fri, 21 Jan 2022 15:45:05 +0800 Message-ID: <20220121074508.42168-2-liang.yang@amlogic.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220121074508.42168-1-liang.yang@amlogic.com> References: <20220121074508.42168-1-liang.yang@amlogic.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Originating-IP: [10.28.8.21] Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" When MESON_SCLK_ONE_BASED flag is set, the sclk divider will be: one based divider (div =3D val), and zero value gates the clock Signed-off-by: Liang Yang --- drivers/clk/meson/sclk-div.c | 59 ++++++++++++++++++++++-------------- drivers/clk/meson/sclk-div.h | 3 ++ 2 files changed, 40 insertions(+), 22 deletions(-) diff --git a/drivers/clk/meson/sclk-div.c b/drivers/clk/meson/sclk-div.c index 76d31c0a3342..4ddc1763a12d 100644 --- a/drivers/clk/meson/sclk-div.c +++ b/drivers/clk/meson/sclk-div.c @@ -4,16 +4,17 @@ * Author: Jerome Brunet * * Sample clock generator divider: - * This HW divider gates with value 0 but is otherwise a zero based divide= r: + * This HW divider gates with value 0: * * val >=3D 1 - * divider =3D val + 1 + * divider =3D val + 1 if ONE_BASED is not set, otherwise divider =3D val. * * The duty cycle may also be set for the LR clock variant. The duty cycle * ratio is: * * hi =3D [0 - val] - * duty_cycle =3D (1 + hi) / (1 + val) + * duty_cycle =3D (1 + hi) / (1 + val) if ONE_BASED is not set, otherwise: + * duty_cycle =3D hi / (1 + val) */ =20 #include @@ -28,22 +29,37 @@ meson_sclk_div_data(struct clk_regmap *clk) return (struct meson_sclk_div_data *)clk->data; } =20 -static int sclk_div_maxval(struct meson_sclk_div_data *sclk) +static inline int sclk_get_reg(int val, unsigned char flag) { - return (1 << sclk->div.width) - 1; + if ((flag & MESON_SCLK_ONE_BASED) || !val) + return val; + return val - 1; +} + +static inline int sclk_get_divider(int reg, unsigned char flag) +{ + if (flag & MESON_SCLK_ONE_BASED) + return reg; + return reg + 1; } =20 static int sclk_div_maxdiv(struct meson_sclk_div_data *sclk) { - return sclk_div_maxval(sclk) + 1; + unsigned int reg =3D (1 << sclk->div.width) - 1; + + return sclk_get_divider(reg, sclk->flags); } =20 static int sclk_div_getdiv(struct clk_hw *hw, unsigned long rate, - unsigned long prate, int maxdiv) + unsigned long prate) { int div =3D DIV_ROUND_CLOSEST_ULL((u64)prate, rate); + struct clk_regmap *clk =3D to_clk_regmap(hw); + struct meson_sclk_div_data *sclk =3D meson_sclk_div_data(clk); + int mindiv =3D sclk_get_divider(1, sclk->flags); + int maxdiv =3D sclk_div_maxdiv(sclk); =20 - return clamp(div, 2, maxdiv); + return clamp(div, mindiv, maxdiv); } =20 static int sclk_div_bestdiv(struct clk_hw *hw, unsigned long rate, @@ -51,25 +67,25 @@ static int sclk_div_bestdiv(struct clk_hw *hw, unsigned= long rate, struct meson_sclk_div_data *sclk) { struct clk_hw *parent =3D clk_hw_get_parent(hw); - int bestdiv =3D 0, i; + int bestdiv =3D 0, i, mindiv; unsigned long maxdiv, now, parent_now; unsigned long best =3D 0, best_parent =3D 0; =20 if (!rate) rate =3D 1; =20 - maxdiv =3D sclk_div_maxdiv(sclk); - if (!(clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT)) - return sclk_div_getdiv(hw, rate, *prate, maxdiv); + return sclk_div_getdiv(hw, rate, *prate); =20 /* * The maximum divider we can use without overflowing * unsigned long in rate * i below */ + maxdiv =3D sclk_div_maxdiv(sclk); maxdiv =3D min(ULONG_MAX / rate, maxdiv); + mindiv =3D sclk_get_divider(1, sclk->flags); =20 - for (i =3D 2; i <=3D maxdiv; i++) { + for (i =3D mindiv; i <=3D maxdiv; i++) { /* * It's the most ideal case if the requested rate can be * divided from parent clock without needing to change @@ -115,10 +131,7 @@ static void sclk_apply_ratio(struct clk_regmap *clk, sclk->cached_duty.num, sclk->cached_duty.den); =20 - if (hi) - hi -=3D 1; - - meson_parm_write(clk->map, &sclk->hi, hi); + meson_parm_write(clk->map, &sclk->hi, sclk_get_reg(hi, sclk->flags)); } =20 static int sclk_div_set_duty_cycle(struct clk_hw *hw, @@ -149,7 +162,7 @@ static int sclk_div_get_duty_cycle(struct clk_hw *hw, } =20 hi =3D meson_parm_read(clk->map, &sclk->hi); - duty->num =3D hi + 1; + duty->num =3D sclk_get_divider(hi, sclk->flags); duty->den =3D sclk->cached_div; return 0; } @@ -157,10 +170,13 @@ static int sclk_div_get_duty_cycle(struct clk_hw *hw, static void sclk_apply_divider(struct clk_regmap *clk, struct meson_sclk_div_data *sclk) { + unsigned int div; + if (MESON_PARM_APPLICABLE(&sclk->hi)) sclk_apply_ratio(clk, sclk); =20 - meson_parm_write(clk->map, &sclk->div, sclk->cached_div - 1); + div =3D sclk_get_reg(sclk->cached_div, sclk->flags); + meson_parm_write(clk->map, &sclk->div, div); } =20 static int sclk_div_set_rate(struct clk_hw *hw, unsigned long rate, @@ -168,9 +184,8 @@ static int sclk_div_set_rate(struct clk_hw *hw, unsigne= d long rate, { struct clk_regmap *clk =3D to_clk_regmap(hw); struct meson_sclk_div_data *sclk =3D meson_sclk_div_data(clk); - unsigned long maxdiv =3D sclk_div_maxdiv(sclk); =20 - sclk->cached_div =3D sclk_div_getdiv(hw, rate, prate, maxdiv); + sclk->cached_div =3D sclk_div_getdiv(hw, rate, prate); =20 if (clk_hw_is_enabled(hw)) sclk_apply_divider(clk, sclk); @@ -228,7 +243,7 @@ static int sclk_div_init(struct clk_hw *hw) if (!val) sclk->cached_div =3D sclk_div_maxdiv(sclk); else - sclk->cached_div =3D val + 1; + sclk->cached_div =3D sclk_get_divider(val, sclk->flags); =20 sclk_div_get_duty_cycle(hw, &sclk->cached_duty); =20 diff --git a/drivers/clk/meson/sclk-div.h b/drivers/clk/meson/sclk-div.h index b64b2a32005f..944dab5ec0cf 100644 --- a/drivers/clk/meson/sclk-div.h +++ b/drivers/clk/meson/sclk-div.h @@ -10,11 +10,14 @@ #include #include "parm.h" =20 +#define MESON_SCLK_ONE_BASED BIT(0) + struct meson_sclk_div_data { struct parm div; struct parm hi; unsigned int cached_div; struct clk_duty cached_duty; + u8 flags; }; =20 extern const struct clk_ops meson_sclk_div_ops; --=20 2.34.1 From nobody Tue Jun 30 09:15:59 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 7AB4CC4332F for ; Fri, 21 Jan 2022 07:45:30 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1379134AbiAUHp1 (ORCPT ); Fri, 21 Jan 2022 02:45:27 -0500 Received: from mail-sz.amlogic.com ([211.162.65.117]:15116 "EHLO mail-sz.amlogic.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1379093AbiAUHpT (ORCPT ); Fri, 21 Jan 2022 02:45:19 -0500 Received: from droid11-sz.amlogic.com (10.28.8.21) by mail-sz.amlogic.com (10.28.11.5) with Microsoft SMTP Server id 15.1.2176.2; Fri, 21 Jan 2022 15:45:17 +0800 From: Liang Yang To: Neil Armstrong , Jerome Brunet , Kevin Hilman , Michael Turquette , Stephen Boyd , Rob Herring , CC: Liang Yang , Martin Blumenstingl , Jianxin Pan , Victor Wan , XianWei Zhao , Kelvin Zhang , BiChao Zheng , YongHui Yu , , , Subject: [PATCH v10 2/4] clk: meson: add emmc sub clock phase delay driver Date: Fri, 21 Jan 2022 15:45:06 +0800 Message-ID: <20220121074508.42168-3-liang.yang@amlogic.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220121074508.42168-1-liang.yang@amlogic.com> References: <20220121074508.42168-1-liang.yang@amlogic.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Originating-IP: [10.28.8.21] Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" Export the emmc sub clock phase delay ops which will be used by the emmc sub clock driver itself. Signed-off-by: Liang Yang --- drivers/clk/meson/Kconfig | 4 ++ drivers/clk/meson/Makefile | 1 + drivers/clk/meson/clk-phase-delay.c | 69 +++++++++++++++++++++++++++++ drivers/clk/meson/clk-phase-delay.h | 20 +++++++++ 4 files changed, 94 insertions(+) create mode 100644 drivers/clk/meson/clk-phase-delay.c create mode 100644 drivers/clk/meson/clk-phase-delay.h diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig index 3014e2f1fbb4..bb0f59eea366 100644 --- a/drivers/clk/meson/Kconfig +++ b/drivers/clk/meson/Kconfig @@ -18,6 +18,10 @@ config COMMON_CLK_MESON_PHASE tristate select COMMON_CLK_MESON_REGMAP =20 +config COMMON_CLK_MESON_PHASE_DELAY + tristate + select COMMON_CLK_MESON_REGMAP + config COMMON_CLK_MESON_PLL tristate select COMMON_CLK_MESON_REGMAP diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile index b3ef5f67820f..99fe4eeed000 100644 --- a/drivers/clk/meson/Makefile +++ b/drivers/clk/meson/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_COMMON_CLK_MESON_MPLL) +=3D clk-mpll.o obj-$(CONFIG_COMMON_CLK_MESON_PHASE) +=3D clk-phase.o obj-$(CONFIG_COMMON_CLK_MESON_PLL) +=3D clk-pll.o obj-$(CONFIG_COMMON_CLK_MESON_REGMAP) +=3D clk-regmap.o +obj-$(CONFIG_COMMON_CLK_MESON_PHASE_DELAY) +=3D clk-phase-delay.o obj-$(CONFIG_COMMON_CLK_MESON_SCLK_DIV) +=3D sclk-div.o obj-$(CONFIG_COMMON_CLK_MESON_VID_PLL_DIV) +=3D vid-pll-div.o =20 diff --git a/drivers/clk/meson/clk-phase-delay.c b/drivers/clk/meson/clk-ph= ase-delay.c new file mode 100644 index 000000000000..3c1ae0ee2a24 --- /dev/null +++ b/drivers/clk/meson/clk-phase-delay.c @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2019 Amlogic, Inc. All rights reserved. + */ + +#include +#include + +#include "clk-regmap.h" +#include "clk-phase-delay.h" + +static inline struct meson_clk_phase_delay_data * +meson_clk_get_phase_delay_data(struct clk_regmap *clk) +{ + return clk->data; +} + +static int meson_clk_phase_delay_get_phase(struct clk_hw *hw) +{ + struct clk_regmap *clk =3D to_clk_regmap(hw); + struct meson_clk_phase_delay_data *ph; + unsigned long period_ps, p, d; + int degrees; + + ph =3D meson_clk_get_phase_delay_data(clk); + p =3D meson_parm_read(clk->map, &ph->phase); + degrees =3D p * 360 / (1 << (ph->phase.width)); + + period_ps =3D DIV_ROUND_UP_ULL(NSEC_PER_SEC * 1000ull, + clk_hw_get_rate(hw)); + + d =3D meson_parm_read(clk->map, &ph->delay); + degrees +=3D d * ph->delay_step_ps * 360 / period_ps; + degrees %=3D 360; + + return degrees; +} + +static int meson_clk_phase_delay_set_phase(struct clk_hw *hw, int degrees) +{ + struct clk_regmap *clk =3D to_clk_regmap(hw); + struct meson_clk_phase_delay_data *ph; + unsigned long period_ps, d =3D 0; + unsigned int p; + + ph =3D meson_clk_get_phase_delay_data(clk); + period_ps =3D DIV_ROUND_UP_ULL(NSEC_PER_SEC * 1000ull, + clk_hw_get_rate(hw)); + + /* + * First compute the phase index (p), the remainder (r) is the + * part we'll try to acheive using the delays (d). + */ + p =3D 360 / 1 << (ph->phase.width); + degrees =3D degrees / p; + d =3D DIV_ROUND_CLOSEST((degrees % p) * period_ps, + 360 * ph->delay_step_ps); + d =3D min(d, PMASK(ph->delay.width)); + + meson_parm_write(clk->map, &ph->phase, degrees); + meson_parm_write(clk->map, &ph->delay, d); + return 0; +} + +const struct clk_ops meson_clk_phase_delay_ops =3D { + .get_phase =3D meson_clk_phase_delay_get_phase, + .set_phase =3D meson_clk_phase_delay_set_phase, +}; +EXPORT_SYMBOL_GPL(meson_clk_phase_delay_ops); diff --git a/drivers/clk/meson/clk-phase-delay.h b/drivers/clk/meson/clk-ph= ase-delay.h new file mode 100644 index 000000000000..b4f211d02c84 --- /dev/null +++ b/drivers/clk/meson/clk-phase-delay.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */ +/* + * Copyright (c) 2019 Amlogic, Inc. All rights reserved. + */ + +#ifndef __MESON_CLK_PHASE_DELAY_H +#define __MESON_CLK_PHASE_DELAY_H + +#include +#include "parm.h" + +struct meson_clk_phase_delay_data { + struct parm phase; + struct parm delay; + unsigned int delay_step_ps; +}; + +extern const struct clk_ops meson_clk_phase_delay_ops; + +#endif /* __MESON_CLK_PHASE_DELAY_H */ --=20 2.34.1 From nobody Tue Jun 30 09:15:59 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 89BABC4332F for ; Fri, 21 Jan 2022 07:45:36 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1379179AbiAUHpb (ORCPT ); Fri, 21 Jan 2022 02:45:31 -0500 Received: from mail-sz.amlogic.com ([211.162.65.117]:15143 "EHLO mail-sz.amlogic.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1379114AbiAUHpY (ORCPT ); Fri, 21 Jan 2022 02:45:24 -0500 Received: from droid11-sz.amlogic.com (10.28.8.21) by mail-sz.amlogic.com (10.28.11.5) with Microsoft SMTP Server id 15.1.2176.2; Fri, 21 Jan 2022 15:45:20 +0800 From: Liang Yang To: Neil Armstrong , Jerome Brunet , Kevin Hilman , Michael Turquette , Stephen Boyd , Rob Herring , CC: Liang Yang , Martin Blumenstingl , Jianxin Pan , Victor Wan , XianWei Zhao , Kelvin Zhang , BiChao Zheng , YongHui Yu , , , Subject: [PATCH v10 3/4] clk: meson: add DT documentation for emmc clock controller Date: Fri, 21 Jan 2022 15:45:07 +0800 Message-ID: <20220121074508.42168-4-liang.yang@amlogic.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220121074508.42168-1-liang.yang@amlogic.com> References: <20220121074508.42168-1-liang.yang@amlogic.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Originating-IP: [10.28.8.21] Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" Document the MMC sub clock controller driver, the potential consumer of this driver is MMC or NAND. Also add four clock bindings IDs which provided by this driver. Signed-off-by: Liang Yang --- .../bindings/clock/amlogic,mmc-clkc.yaml | 64 +++++++++++++++++++ include/dt-bindings/clock/amlogic,mmc-clkc.h | 14 ++++ 2 files changed, 78 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/amlogic,mmc-clk= c.yaml create mode 100644 include/dt-bindings/clock/amlogic,mmc-clkc.h diff --git a/Documentation/devicetree/bindings/clock/amlogic,mmc-clkc.yaml = b/Documentation/devicetree/bindings/clock/amlogic,mmc-clkc.yaml new file mode 100644 index 000000000000..0fe2e33c2082 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/amlogic,mmc-clkc.yaml @@ -0,0 +1,64 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/amlogic,mmc-clkc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Amlogic MMC Sub Clock Controller Driver Device Tree Bindings + +maintainers: + - jianxin.pan@amlogic.com + - liang.yang@amlogic.com + +properties: + compatible: + enum: + - "amlogic,axg-mmc-clkc", "syscon" + - "amlogic,gx-mmc-clkc", "syscon" + + reg: + maxItems: 1 + + clocks: + maxItems: 2 + + clock-names: + items: + - const: "clkin0", "clkin1" + + "#clock-cells": + const: 1 + +required: + - compatible + - reg + - clocks + - clock-names + - "#clock-cells" + +additionalProperties: false + +examples: + - | + sd_mmc_c_clkc: clock-controller@7000 { + compatible =3D "amlogic,axg-mmc-clkc", "syscon"; + reg =3D <0x0 0x7000 0x0 0x4>; + #clock-cells =3D <1>; + + clock-names =3D "clkin0", "clkin1"; + clocks =3D <&clkc CLKID_SD_EMMC_C_CLK0>, + <&clkc CLKID_FCLK_DIV2>; + }; + + - | + sd_emmc_b_clkc: clock-controller@5000 { + compatible =3D "amlogic,axg-mmc-clkc", "syscon"; + reg =3D <0x0 0x5000 0x0 0x4>; + + #clock-cells =3D <1>; + clock-names =3D "clkin0", "clkin1"; + clocks =3D <&clkc CLKID_SD_EMMC_B_CLK0>, + <&clkc CLKID_FCLK_DIV2>; + }; + +... \ No newline at end of file diff --git a/include/dt-bindings/clock/amlogic,mmc-clkc.h b/include/dt-bind= ings/clock/amlogic,mmc-clkc.h new file mode 100644 index 000000000000..71301517b183 --- /dev/null +++ b/include/dt-bindings/clock/amlogic,mmc-clkc.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */ +/* + * Copyright (c) 2019 Amlogic, Inc. All rights reserved. + */ + +#ifndef __MMC_CLKC_H +#define __MMC_CLKC_H + +#define CLKID_MMC_DIV 0 +#define CLKID_MMC_PHASE_CORE 1 +#define CLKID_MMC_PHASE_TX 2 +#define CLKID_MMC_PHASE_RX 3 + +#endif --=20 2.34.1 From nobody Tue Jun 30 09:15:59 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 09DC9C433F5 for ; Fri, 21 Jan 2022 07:46:36 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1379209AbiAUHpe (ORCPT ); Fri, 21 Jan 2022 02:45:34 -0500 Received: from mail-sz.amlogic.com ([211.162.65.117]:15143 "EHLO mail-sz.amlogic.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1379136AbiAUHp2 (ORCPT ); Fri, 21 Jan 2022 02:45:28 -0500 Received: from droid11-sz.amlogic.com (10.28.8.21) by mail-sz.amlogic.com (10.28.11.5) with Microsoft SMTP Server id 15.1.2176.2; Fri, 21 Jan 2022 15:45:22 +0800 From: Liang Yang To: Neil Armstrong , Jerome Brunet , Kevin Hilman , Michael Turquette , Stephen Boyd , Rob Herring , CC: Liang Yang , Martin Blumenstingl , Jianxin Pan , Victor Wan , XianWei Zhao , Kelvin Zhang , BiChao Zheng , YongHui Yu , , , Subject: [PATCH v10 4/4] clk: meson: add sub MMC clock controller driver Date: Fri, 21 Jan 2022 15:45:08 +0800 Message-ID: <20220121074508.42168-5-liang.yang@amlogic.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220121074508.42168-1-liang.yang@amlogic.com> References: <20220121074508.42168-1-liang.yang@amlogic.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Originating-IP: [10.28.8.21] Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" The patch will add a MMC clock controller driver which used by MMC or NAND, It provide a mux and divider clock, and three phase clocks - core, tx, tx. Two clocks are provided as the parent of MMC clock controller from upper layer clock controller - eg "amlogic,axg-clkc" in AXG platform. To specify which clock the MMC or NAND driver may consume, the preprocessor macros in the dt-bindings/clock/amlogic,mmc-clkc.h header can be used in the device tree sources. Signed-off-by: Liang Yang --- drivers/clk/meson/Kconfig | 14 ++ drivers/clk/meson/Makefile | 1 + drivers/clk/meson/mmc-clkc.c | 302 +++++++++++++++++++++++++++++++++++ 3 files changed, 317 insertions(+) create mode 100644 drivers/clk/meson/mmc-clkc.c diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig index bb0f59eea366..5f344a0892cb 100644 --- a/drivers/clk/meson/Kconfig +++ b/drivers/clk/meson/Kconfig @@ -39,6 +39,20 @@ config COMMON_CLK_MESON_AO_CLKC select COMMON_CLK_MESON_REGMAP select RESET_CONTROLLER =20 +config COMMON_CLK_MMC_MESON + tristate "Meson MMC Sub Clock Controller Driver" + depends on ARCH_MESON || COMPILE_TEST + select MFD_SYSCON + select COMMON_CLK_AMLOGIC + select COMMON_CLK_MESON_PHASE + select COMMON_CLK_MESON_PHASE_DELAY + select COMMON_CLK_MESON_SCLK_DIV + help + Support for the MMC sub clock controller on + Amlogic Meson Platform, which includes S905 (GXBB, GXL), + A113D/X (AXG) devices. Say Y if you want this + clock enabled. + config COMMON_CLK_MESON_EE_CLKC tristate select COMMON_CLK_MESON_REGMAP diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile index 99fe4eeed000..7a7ffb37a726 100644 --- a/drivers/clk/meson/Makefile +++ b/drivers/clk/meson/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_COMMON_CLK_MESON_VID_PLL_DIV) +=3D vid-pll-d= iv.o =20 obj-$(CONFIG_COMMON_CLK_AXG) +=3D axg.o axg-aoclk.o obj-$(CONFIG_COMMON_CLK_AXG_AUDIO) +=3D axg-audio.o +obj-$(CONFIG_COMMON_CLK_MMC_MESON) +=3D mmc-clkc.o obj-$(CONFIG_COMMON_CLK_GXBB) +=3D gxbb.o gxbb-aoclk.o obj-$(CONFIG_COMMON_CLK_G12A) +=3D g12a.o g12a-aoclk.o obj-$(CONFIG_COMMON_CLK_MESON8B) +=3D meson8b.o meson8-ddr.o diff --git a/drivers/clk/meson/mmc-clkc.c b/drivers/clk/meson/mmc-clkc.c new file mode 100644 index 000000000000..e42c4015c7a3 --- /dev/null +++ b/drivers/clk/meson/mmc-clkc.c @@ -0,0 +1,302 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2019 Amlogic, Inc. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sclk-div.h" +#include "clk-phase-delay.h" +#include "clk-regmap.h" +#include "clk-phase.h" + +/* clock ID used by internal driver */ + +#define SD_EMMC_CLOCK 0 +#define CLK_DELAY_STEP_PS_GX 200 +#define CLK_DELAY_STEP_PS_AXG 78 +#define MUX_CLK_NUM_PARENTS 2 +#define MMC_MAX_CLKS 4 + +static struct clk_parent_data mmc_clkc_parent_data[MUX_CLK_NUM_PARENTS]; + +struct mmc_clkc_data { + struct meson_clk_phase_delay_data tx; + struct meson_clk_phase_delay_data rx; +}; + +static struct clk_regmap_mux_data mmc_clkc_mux_data =3D { + .offset =3D SD_EMMC_CLOCK, + .mask =3D 0x3, + .shift =3D 6, +}; + +static const struct meson_sclk_div_data mmc_clkc_div_data =3D { + .div =3D { + .reg_off =3D SD_EMMC_CLOCK, + .width =3D 6, + }, + .flags =3D MESON_SCLK_ONE_BASED, +}; + +static struct meson_clk_phase_data mmc_clkc_core_phase =3D { + .ph =3D { + .reg_off =3D SD_EMMC_CLOCK, + .shift =3D 8, + .width =3D 2, + } +}; + +static const struct mmc_clkc_data mmc_clkc_gx_data =3D { + .tx =3D { + .phase =3D { + .reg_off =3D SD_EMMC_CLOCK, + .shift =3D 10, + .width =3D 2, + }, + .delay =3D { + .reg_off =3D SD_EMMC_CLOCK, + .shift =3D 16, + .width =3D 4, + }, + .delay_step_ps =3D CLK_DELAY_STEP_PS_GX, + }, + .rx =3D { + .phase =3D { + .reg_off =3D SD_EMMC_CLOCK, + .shift =3D 12, + .width =3D 2, + }, + .delay =3D { + .reg_off =3D SD_EMMC_CLOCK, + .shift =3D 20, + .width =3D 4, + }, + .delay_step_ps =3D CLK_DELAY_STEP_PS_GX, + }, +}; + +static const struct mmc_clkc_data mmc_clkc_axg_data =3D { + .tx =3D { + .phase =3D { + .reg_off =3D SD_EMMC_CLOCK, + .shift =3D 10, + .width =3D 2, + }, + .delay =3D { + .reg_off =3D SD_EMMC_CLOCK, + .shift =3D 16, + .width =3D 6, + }, + .delay_step_ps =3D CLK_DELAY_STEP_PS_AXG, + }, + .rx =3D { + .phase =3D { + .reg_off =3D SD_EMMC_CLOCK, + .shift =3D 12, + .width =3D 2, + }, + .delay =3D { + .reg_off =3D SD_EMMC_CLOCK, + .shift =3D 22, + .width =3D 6, + }, + .delay_step_ps =3D CLK_DELAY_STEP_PS_AXG, + }, +}; + +static const struct of_device_id mmc_clkc_match_table[] =3D { + { + .compatible =3D "amlogic,gx-mmc-clkc", + .data =3D &mmc_clkc_gx_data + }, + { + .compatible =3D "amlogic,axg-mmc-clkc", + .data =3D &mmc_clkc_axg_data + }, + {} +}; +MODULE_DEVICE_TABLE(of, mmc_clkc_match_table); + +static struct clk_regmap * +mmc_clkc_register_clk(struct device *dev, struct regmap *map, + struct clk_init_data *init, + const char *suffix, void *data) +{ + struct clk_regmap *clk; + char *name; + int ret; + + clk =3D devm_kzalloc(dev, sizeof(*clk), GFP_KERNEL); + if (!clk) + return ERR_PTR(-ENOMEM); + + name =3D kasprintf(GFP_KERNEL, "%s#%s", dev_name(dev), suffix); + if (!name) + return ERR_PTR(-ENOMEM); + + init->name =3D name; + clk->map =3D map; + clk->data =3D data; + clk->hw.init =3D init; + ret =3D devm_clk_hw_register(dev, &clk->hw); + if (ret) + clk =3D ERR_PTR(ret); + + kfree(name); + return clk; +} + +static struct clk_regmap *mmc_clkc_register_mux(struct device *dev, + struct regmap *map) +{ + struct clk_init_data init =3D {0}; + struct clk_regmap *mux; + struct clk *clk; + int i; + + for (i =3D 0; i < MUX_CLK_NUM_PARENTS; i++) { + char name[8]; + + snprintf(name, sizeof(name), "clkin%d", i); + clk =3D devm_clk_get(dev, name); + if (IS_ERR(clk)) { + if (clk !=3D ERR_PTR(-EPROBE_DEFER)) + dev_err_probe(dev, PTR_ERR(clk), + "Missing clock\n"); + return ERR_CAST(clk); + } + + mmc_clkc_parent_data[i].fw_name =3D __clk_get_name(clk); + } + + init.ops =3D &clk_regmap_mux_ops; + init.flags =3D CLK_SET_RATE_PARENT; + init.parent_data =3D mmc_clkc_parent_data; + init.num_parents =3D MUX_CLK_NUM_PARENTS; + + mux =3D mmc_clkc_register_clk(dev, map, &init, "mux", &mmc_clkc_mux_data); + if (IS_ERR(mux)) + dev_err(dev, "Mux clock registration failed\n"); + + return mux; +} + +static struct clk_regmap * +mmc_clkc_register_clk_with_parent(struct device *dev, struct regmap *map, + char *suffix, const struct clk_hw *hw, + unsigned long flags, + const struct clk_ops *ops, void *data) +{ + struct clk_init_data init; + struct clk_regmap *clk; + const char *parent_name =3D clk_hw_get_name(hw); + + init.ops =3D ops; + init.flags =3D flags; + init.parent_names =3D &parent_name; + init.num_parents =3D 1; + clk =3D mmc_clkc_register_clk(dev, map, &init, suffix, data); + if (IS_ERR(clk)) + dev_err(dev, "%s clock registration failed\n", suffix); + + return clk; +} + +static int mmc_clkc_probe(struct platform_device *pdev) +{ + struct clk_hw_onecell_data *onecell_data; + struct device *dev =3D &pdev->dev; + const struct mmc_clkc_data *data; + struct regmap *map; + struct clk_regmap *clk, *core; + struct meson_sclk_div_data *div_data, *tx, *rx; + + data =3D of_device_get_match_data(dev); + if (!data) + return -ENODEV; + + /* cast to drop the const */ + tx =3D (struct meson_sclk_div_data *)&data->tx; + rx =3D (struct meson_sclk_div_data *)&data->rx; + + map =3D syscon_node_to_regmap(dev->of_node); + if (IS_ERR(map)) { + dev_err(dev, "could not find mmc clock controller\n"); + return PTR_ERR(map); + } + + onecell_data =3D devm_kzalloc(dev, + struct_size(onecell_data, hws, + MMC_MAX_CLKS), + GFP_KERNEL); + if (!onecell_data) + return -ENOMEM; + + clk =3D mmc_clkc_register_mux(dev, map); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + div_data =3D devm_kzalloc(dev, sizeof(*div_data), GFP_KERNEL); + if (!div_data) + return -ENOMEM; + + memcpy(div_data, &mmc_clkc_div_data, sizeof(*div_data)); + clk =3D mmc_clkc_register_clk_with_parent(dev, map, "div", + &clk->hw, + CLK_SET_RATE_PARENT, + &meson_sclk_div_ops, + div_data); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + onecell_data->hws[CLKID_MMC_DIV] =3D &clk->hw; + core =3D mmc_clkc_register_clk_with_parent(dev, map, "core", + &clk->hw, + CLK_SET_RATE_PARENT, + &meson_clk_phase_ops, + &mmc_clkc_core_phase); + if (IS_ERR(core)) + return PTR_ERR(core); + onecell_data->hws[CLKID_MMC_PHASE_CORE] =3D &core->hw; + clk =3D mmc_clkc_register_clk_with_parent(dev, map, "rx", + &core->hw, 0, + &meson_clk_phase_delay_ops, + rx); + if (IS_ERR(clk)) + return PTR_ERR(clk); + onecell_data->hws[CLKID_MMC_PHASE_RX] =3D &clk->hw; + clk =3D mmc_clkc_register_clk_with_parent(dev, map, "tx", + &core->hw, 0, + &meson_clk_phase_delay_ops, + tx); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + onecell_data->hws[CLKID_MMC_PHASE_TX] =3D &clk->hw; + onecell_data->num =3D MMC_MAX_CLKS; + return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, + onecell_data); +} + +static struct platform_driver mmc_clkc_driver =3D { + .probe =3D mmc_clkc_probe, + .driver =3D { + .name =3D "meson-mmc-clkc", + .of_match_table =3D of_match_ptr(mmc_clkc_match_table), + }, +}; + +module_platform_driver(mmc_clkc_driver); + +MODULE_DESCRIPTION("Amlogic AXG MMC clock driver"); +MODULE_AUTHOR("Jianxin Pan "); +MODULE_LICENSE("GPL v2"); --=20 2.34.1