From nobody Sat Sep 21 23:37:02 2024 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 7DB25C433EF for ; Sun, 12 Jun 2022 13:54:35 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237192AbiFLNyd (ORCPT ); Sun, 12 Jun 2022 09:54:33 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:50364 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S237140AbiFLNyb (ORCPT ); Sun, 12 Jun 2022 09:54:31 -0400 Received: from mailgw01.mediatek.com (unknown [60.244.123.138]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id AD20C56FBA; Sun, 12 Jun 2022 06:54:24 -0700 (PDT) X-UUID: 6a856f5ec905409faa7acdccf8377267-20220612 X-CID-P-RULE: Spam_GS6885AD X-CID-O-INFO: VERSION:1.1.5,REQID:0db3988f-deaa-4abb-899d-59f59000e4a5,OB:0,LO B:0,IP:0,URL:25,TC:0,Content:0,EDM:0,RT:0,SF:100,FILE:0,RULE:Spam_GS6885AD ,ACTION:quarantine,TS:125 X-CID-INFO: VERSION:1.1.5,REQID:0db3988f-deaa-4abb-899d-59f59000e4a5,OB:0,LOB: 0,IP:0,URL:25,TC:0,Content:0,EDM:0,RT:0,SF:100,FILE:0,RULE:Spam_GS981B3D,A CTION:quarantine,TS:125 X-CID-META: VersionHash:2a19b09,CLOUDID:c88f96e7-1f03-4449-90ad-e6cb8f3d1399,C OID:b1b634de6ec6,Recheck:0,SF:28|17|19|48,TC:nil,Content:0,EDM:-3,IP:nil,U RL:1,File:nil,QS:0,BEC:nil X-UUID: 6a856f5ec905409faa7acdccf8377267-20220612 Received: from mtkcas11.mediatek.inc [(172.21.101.40)] by mailgw01.mediatek.com (envelope-from ) (Generic MTA with TLSv1.2 ECDHE-RSA-AES256-SHA384 256/256) with ESMTP id 200731574; Sun, 12 Jun 2022 21:54:17 +0800 Received: from mtkmbs11n1.mediatek.inc (172.21.101.186) by mtkmbs10n1.mediatek.inc (172.21.101.34) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.792.15; Sun, 12 Jun 2022 21:54:16 +0800 Received: from mtksdccf07.mediatek.inc (172.21.84.99) by mtkmbs11n1.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.2.792.3 via Frontend Transport; Sun, 12 Jun 2022 21:54:16 +0800 From: Johnson Wang To: , , , CC: , , , , , , Johnson Wang , Edward-JW Yang Subject: [RFC PATCH 1/2] dt-bindings: arm: mediatek: Add new bindings of MediaTek frequency hopping Date: Sun, 12 Jun 2022 21:54:13 +0800 Message-ID: <20220612135414.3003-2-johnson.wang@mediatek.com> X-Mailer: git-send-email 2.18.0 In-Reply-To: <20220612135414.3003-1-johnson.wang@mediatek.com> References: <20220612135414.3003-1-johnson.wang@mediatek.com> MIME-Version: 1.0 X-MTK: N Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" This patch adds the new binding documentation for frequency hopping and spread spectrum clocking control on MT8186. Signed-off-by: Edward-JW Yang Signed-off-by: Johnson Wang --- .../bindings/arm/mediatek/mediatek,fhctl.yaml | 149 ++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek= ,fhctl.yaml diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,fhctl.= yaml b/Documentation/devicetree/bindings/arm/mediatek/mediatek,fhctl.yaml new file mode 100644 index 000000000000..9cb04bed9e86 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,fhctl.yaml @@ -0,0 +1,149 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/arm/mediatek/mediatek,fhctl.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: MediaTek frequency hopping and spread spectrum clocking control + +maintainers: + - Edward-JW Yang + +description: | + Frequency hopping control (FHCTL) is a piece of hardware that control + some PLLs to adopt "hopping" mechanism to adjust their frequency. + Spread spectrum clocking (SSC) is another function provided by this hard= ware. + +properties: + compatible: + const: mediatek,mt8186-fhctl + + reg: + maxItems: 2 + +patternProperties: + "^map[0-9]$": + type: object + description: + Represents a group of PLLs controlled by the same domain and method. + + properties: + domain: + description: The position of this hardware block in the chip. + $ref: /schemas/types.yaml#/definitions/string-array + + method: + description: Determine which method to control this FHCTL hardware. + $ref: /schemas/types.yaml#/definitions/string-array + + patternProperties: + "^armpll_(ll|bl)|(cci|main|mm|tvd|m|adsp|mfg|nna|nna2|msdc)pll$": + type: object + description: + A subnode represents one PLL clock. The properties it contains a= re + used to determine whether this PLL enable frequency hopping mech= anism and spread spectrum clocking (SSC) control. + + properties: + fh-id: + description: The index of the PLL clocks. + $ref: /schemas/types.yaml#/definitions/uint32 + + perms: + description: + This is a bit field used to manage the permission of frequen= cy + hopping function and spread spectrum clocking control. + $ref: /schemas/types.yaml#/definitions/uint32 + + ssc-rate: + description: The percentage of the spectrum spreading. + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 8 + + required: + - fh-id + - perms + + required: + - domain + - method + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + #include + fhctl: fhctl@1000ce00 { + compatible =3D "mediatek,mt8186-fhctl"; + reg =3D <0x1000ce00 0x200>, + <0x1000c000 0xe00>; + map0 { + domain =3D "top"; + method =3D "fhctl-ap"; + + armpll_ll { + fh-id =3D <0>; + perms =3D <0x10>; + }; + + armpll_bl { + fh-id =3D <1>; + perms =3D <0x10>; + }; + + ccipll { + fh-id =3D <2>; + perms =3D <0x10>; + }; + + mainpll { + fh-id =3D <3>; + perms =3D <0x1c>; + }; + + mmpll { + fh-id =3D <4>; + perms =3D <0x1c>; + }; + + tvdpll { + fh-id =3D <5>; + perms =3D <0x1c>; + }; + + mpll { + fh-id =3D <6>; + perms =3D <0x10>; + }; + + adsppll { + fh-id =3D <7>; + perms =3D <0x1c>; + }; + + mfgpll { + fh-id =3D <8>; + perms =3D <0x1c>; + }; + + nnapll { + fh-id =3D <9>; + perms =3D <0x1c>; + }; + + nna2pll { + fh-id =3D <10>; + perms =3D <0x1c>; + }; + + msdcpll { + fh-id =3D <11>; + perms =3D <0x1c>; + ssc-rate =3D <3>; + }; + }; + }; --=20 2.18.0 From nobody Sat Sep 21 23:37:02 2024 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 A5CEEC433EF for ; Sun, 12 Jun 2022 13:54:42 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237251AbiFLNyk (ORCPT ); Sun, 12 Jun 2022 09:54:40 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:50666 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S237140AbiFLNyd (ORCPT ); Sun, 12 Jun 2022 09:54:33 -0400 Received: from mailgw01.mediatek.com (unknown [60.244.123.138]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id AD53D57113; Sun, 12 Jun 2022 06:54:25 -0700 (PDT) X-UUID: 030e5c685a1846df9a527f0d078927d6-20220612 X-CID-P-RULE: Release_Ham X-CID-O-INFO: VERSION:1.1.5,REQID:3364f3b1-4f30-411f-b598-31703acb6e50,OB:10,L OB:10,IP:0,URL:5,TC:0,Content:-5,EDM:0,RT:0,SF:100,FILE:0,RULE:Release_Ham ,ACTION:release,TS:100 X-CID-INFO: VERSION:1.1.5,REQID:3364f3b1-4f30-411f-b598-31703acb6e50,OB:10,LOB :10,IP:0,URL:5,TC:0,Content:-5,EDM:0,RT:0,SF:100,FILE:0,RULE:Spam_GS981B3D ,ACTION:quarantine,TS:100 X-CID-META: VersionHash:2a19b09,CLOUDID:ca8f96e7-1f03-4449-90ad-e6cb8f3d1399,C OID:b1b634de6ec6,Recheck:0,SF:28|17|19|48,TC:nil,Content:0,EDM:-3,IP:nil,U RL:1,File:nil,QS:0,BEC:nil X-UUID: 030e5c685a1846df9a527f0d078927d6-20220612 Received: from mtkcas11.mediatek.inc [(172.21.101.40)] by mailgw01.mediatek.com (envelope-from ) (Generic MTA with TLSv1.2 ECDHE-RSA-AES256-SHA384 256/256) with ESMTP id 1535220312; Sun, 12 Jun 2022 21:54:18 +0800 Received: from mtkmbs11n1.mediatek.inc (172.21.101.185) by mtkmbs11n1.mediatek.inc (172.21.101.185) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.792.3; Sun, 12 Jun 2022 21:54:16 +0800 Received: from mtksdccf07.mediatek.inc (172.21.84.99) by mtkmbs11n1.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.2.792.3 via Frontend Transport; Sun, 12 Jun 2022 21:54:16 +0800 From: Johnson Wang To: , , , CC: , , , , , , Johnson Wang , Edward-JW Yang Subject: [RFC PATCH 2/2] clk: mediatek: Add frequency hopping support Date: Sun, 12 Jun 2022 21:54:14 +0800 Message-ID: <20220612135414.3003-3-johnson.wang@mediatek.com> X-Mailer: git-send-email 2.18.0 In-Reply-To: <20220612135414.3003-1-johnson.wang@mediatek.com> References: <20220612135414.3003-1-johnson.wang@mediatek.com> MIME-Version: 1.0 X-MTK: N Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add frequency hopping support and spread spectrum clocking control for MT8186. Signed-off-by: Edward-JW Yang Signed-off-by: Johnson Wang --- drivers/clk/mediatek/Kconfig | 8 + drivers/clk/mediatek/Makefile | 2 + drivers/clk/mediatek/clk-fhctl-ap.c | 347 ++++++++++++++++++++++++++ drivers/clk/mediatek/clk-fhctl-pll.c | 209 ++++++++++++++++ drivers/clk/mediatek/clk-fhctl-pll.h | 74 ++++++ drivers/clk/mediatek/clk-fhctl-util.h | 24 ++ drivers/clk/mediatek/clk-fhctl.c | 191 ++++++++++++++ drivers/clk/mediatek/clk-fhctl.h | 45 ++++ drivers/clk/mediatek/clk-pll.c | 5 +- drivers/clk/mediatek/clk-pll.h | 5 + 10 files changed, 909 insertions(+), 1 deletion(-) create mode 100644 drivers/clk/mediatek/clk-fhctl-ap.c create mode 100644 drivers/clk/mediatek/clk-fhctl-pll.c create mode 100644 drivers/clk/mediatek/clk-fhctl-pll.h create mode 100644 drivers/clk/mediatek/clk-fhctl-util.h create mode 100644 drivers/clk/mediatek/clk-fhctl.c create mode 100644 drivers/clk/mediatek/clk-fhctl.h diff --git a/drivers/clk/mediatek/Kconfig b/drivers/clk/mediatek/Kconfig index d5936cfb3bee..fd887c537a91 100644 --- a/drivers/clk/mediatek/Kconfig +++ b/drivers/clk/mediatek/Kconfig @@ -622,4 +622,12 @@ config COMMON_CLK_MT8516_AUDSYS help This driver supports MediaTek MT8516 audsys clocks. =20 +config COMMON_CLK_MTK_FREQ_HOPPING + tristate "MediaTek frequency hopping driver" + depends on ARCH_MEDIATEK || COMPILE_TEST + select COMMON_CLK_MEDIATEK + help + This driver supports frequency hopping and spread spectrum clocking + control for some MediaTek SoCs. + endmenu diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile index caf2ce93d666..3c0e9bd3978b 100644 --- a/drivers/clk/mediatek/Makefile +++ b/drivers/clk/mediatek/Makefile @@ -99,3 +99,5 @@ obj-$(CONFIG_COMMON_CLK_MT8195) +=3D clk-mt8195-apmixedsy= s.o clk-mt8195-topckgen.o clk-mt8195-apusys_pll.o obj-$(CONFIG_COMMON_CLK_MT8516) +=3D clk-mt8516.o obj-$(CONFIG_COMMON_CLK_MT8516_AUDSYS) +=3D clk-mt8516-aud.o +obj-$(CONFIG_COMMON_CLK_MTK_FREQ_HOPPING) +=3D fhctl.o +fhctl-objs +=3D clk-fhctl.o clk-fhctl-ap.o clk-fhctl-pll.o diff --git a/drivers/clk/mediatek/clk-fhctl-ap.c b/drivers/clk/mediatek/clk= -fhctl-ap.c new file mode 100644 index 000000000000..9e3226a9c1ca --- /dev/null +++ b/drivers/clk/mediatek/clk-fhctl-ap.c @@ -0,0 +1,347 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2022 MediaTek Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "clk-fhctl.h" +#include "clk-fhctl-pll.h" +#include "clk-fhctl-util.h" + +#define FHCTL_TARGET FHCTL_AP + +#define PERCENT_TO_DDSLMT(dds, percent_m10) \ + ((((dds) * (percent_m10)) >> 5) / 100) + +struct fh_ap_match { + char *name; + struct fh_hdlr *hdlr; + int (*init)(struct pll_dts *array, struct fh_ap_match *match); +}; + +struct hdlr_data { + struct pll_dts *array; + struct fh_pll_domain *domain; + spinlock_t *lock; +}; + +static int fhctl_set_ssc_regs(struct fh_pll_regs *regs, + struct fh_pll_data *data, + int fh_id, int rate) +{ + unsigned int updnlmt_val; + + if (rate > 0) { + fh_set_field(regs->reg_cfg, data->frddsx_en, 0); + fh_set_field(regs->reg_cfg, data->sfstrx_en, 0); + fh_set_field(regs->reg_cfg, data->fhctlx_en, 0); + + /* Set the relative parameter registers (dt/df/upbnd/downbnd) */ + fh_set_field(regs->reg_cfg, data->msk_frddsx_dys, data->df_val); + fh_set_field(regs->reg_cfg, data->msk_frddsx_dts, data->dt_val); + + writel((readl(regs->reg_con_pcw) & data->dds_mask) | + data->tgl_org, regs->reg_dds); + + /* Calculate UPDNLMT */ + updnlmt_val =3D PERCENT_TO_DDSLMT((readl(regs->reg_dds) & + data->dds_mask), rate) << + data->updnlmt_shft; + + writel(updnlmt_val, regs->reg_updnlmt); + + fh_set_field(regs->reg_hp_en, BIT(fh_id), 1); + + /* Enable SSC */ + fh_set_field(regs->reg_cfg, data->frddsx_en, 1); + /* Enable Hopping control */ + fh_set_field(regs->reg_cfg, data->fhctlx_en, 1); + + } else { + fh_set_field(regs->reg_cfg, data->frddsx_en, 0); + fh_set_field(regs->reg_cfg, data->sfstrx_en, 0); + fh_set_field(regs->reg_cfg, data->fhctlx_en, 0); + + /* Switch to APMIXEDSYS control */ + fh_set_field(regs->reg_hp_en, BIT(fh_id), 0); + + /* Wait for DDS to be stable */ + udelay(30); + } + + return 0; +} + +static int hopping_hw_flow(void *priv_data, char *domain_name, int fh_id, + unsigned int new_dds, int postdiv) +{ + struct fh_pll_domain *domain; + struct fh_pll_regs *regs; + struct fh_pll_data *data; + unsigned int dds_mask; + unsigned int mon_dds =3D 0; + int ret =3D 0; + unsigned int con_pcw_tmp; + struct hdlr_data *d =3D (struct hdlr_data *)priv_data; + struct pll_dts *array =3D d->array; + + domain =3D d->domain; + regs =3D &domain->regs[fh_id]; + data =3D &domain->data[fh_id]; + dds_mask =3D data->dds_mask; + + if (array->ssc_rate) + fhctl_set_ssc_regs(regs, data, fh_id, 0); + + writel((readl(regs->reg_con_pcw) & dds_mask) | + data->tgl_org, regs->reg_dds); + + fh_set_field(regs->reg_cfg, data->sfstrx_en, 1); + fh_set_field(regs->reg_cfg, data->fhctlx_en, 1); + writel(data->slope0_value, regs->reg_slope0); + writel(data->slope1_value, regs->reg_slope1); + + fh_set_field(regs->reg_hp_en, BIT(fh_id), 1); + writel((new_dds) | (data->dvfs_tri), regs->reg_dvfs); + + /* Wait 1000 us until DDS stable */ + ret =3D readl_poll_timeout_atomic(regs->reg_mon, mon_dds, + (mon_dds & dds_mask) =3D=3D new_dds, 10, 1000); + + con_pcw_tmp =3D readl(regs->reg_con_pcw) & (~dds_mask); + con_pcw_tmp =3D (con_pcw_tmp | (readl(regs->reg_mon) & dds_mask) | + data->pcwchg); + + writel(con_pcw_tmp, regs->reg_con_pcw); + + fh_set_field(regs->reg_hp_en, BIT(fh_id), 0); + + if (array->ssc_rate) + fhctl_set_ssc_regs(regs, data, fh_id, array->ssc_rate); + + return ret; +} + +static unsigned int __get_postdiv(struct fh_pll_regs *regs, + struct fh_pll_data *data) +{ + unsigned int regval; + + regval =3D (readl(regs->reg_con_postdiv) & data->postdiv_mask) + >> data->postdiv_offset; + + return data->postdiv_table[regval]; +} + +static void __set_postdiv(struct fh_pll_regs *regs, struct fh_pll_data *da= ta, + int postdiv) +{ + unsigned int regval, temp; + + for (regval =3D 0 ; regval < data->postdiv_table_size ; regval++) { + if (data->postdiv_table[regval] > postdiv) { + regval--; + break; + } + } + + temp =3D (readl(regs->reg_con_postdiv)) & ~(data->postdiv_mask); + temp |=3D regval << data->postdiv_offset; + writel(temp, regs->reg_con_postdiv); +} + +static int fhctl_ap_hopping(void *priv_data, char *domain_name, int fh_id, + unsigned int new_dds, int postdiv) +{ + struct fh_pll_domain *domain; + struct fh_pll_regs *regs; + struct fh_pll_data *data; + int ret =3D 0; + struct hdlr_data *d =3D (struct hdlr_data *)priv_data; + spinlock_t *lock =3D d->lock; + unsigned long flags =3D 0; + unsigned int pll_postdiv; + + domain =3D d->domain; + regs =3D &domain->regs[fh_id]; + data =3D &domain->data[fh_id]; + + if (postdiv > 0) { + pll_postdiv =3D __get_postdiv(regs, data); + + if (postdiv > pll_postdiv) + __set_postdiv(regs, data, postdiv); + } + + spin_lock_irqsave(lock, flags); + + ret =3D hopping_hw_flow(priv_data, domain_name, fh_id, new_dds, postdiv); + + spin_unlock_irqrestore(lock, flags); + + if (postdiv > 0) { + if (postdiv < pll_postdiv) + __set_postdiv(regs, data, postdiv); + } + + return ret; +} + +static int fhctl_ap_ssc_enable(void *priv_data, char *domain_name, + int fh_id, int rate) +{ + struct fh_pll_domain *domain; + struct fh_pll_regs *regs; + struct fh_pll_data *data; + struct hdlr_data *d =3D (struct hdlr_data *)priv_data; + spinlock_t *lock =3D d->lock; + struct pll_dts *array =3D d->array; + unsigned long flags =3D 0; + + spin_lock_irqsave(lock, flags); + + domain =3D d->domain; + regs =3D &domain->regs[fh_id]; + data =3D &domain->data[fh_id]; + + fhctl_set_ssc_regs(regs, data, fh_id, rate); + + array->ssc_rate =3D rate; + + spin_unlock_irqrestore(lock, flags); + + return 0; +} + +static int fhctl_ap_ssc_disable(void *priv_data, char *domain_name, int fh= _id) +{ + struct fh_pll_domain *domain; + struct fh_pll_regs *regs; + struct fh_pll_data *data; + struct hdlr_data *d =3D (struct hdlr_data *)priv_data; + spinlock_t *lock =3D d->lock; + struct pll_dts *array =3D d->array; + unsigned long flags =3D 0; + + spin_lock_irqsave(lock, flags); + + domain =3D d->domain; + regs =3D &domain->regs[fh_id]; + data =3D &domain->data[fh_id]; + + fhctl_set_ssc_regs(regs, data, fh_id, 0); + + array->ssc_rate =3D 0; + + spin_unlock_irqrestore(lock, flags); + + return 0; +} + +static int fhctl_ap_hw_init(struct pll_dts *array, struct fh_ap_match *mat= ch) +{ + static DEFINE_SPINLOCK(lock); + struct hdlr_data *priv_data; + struct fh_hdlr *hdlr; + struct fh_pll_domain *domain; + int fh_id =3D array->fh_id; + struct fh_pll_regs *regs; + struct fh_pll_data *data; + int mask =3D BIT(fh_id); + + priv_data =3D kzalloc(sizeof(*priv_data), GFP_KERNEL); + hdlr =3D kzalloc(sizeof(*hdlr), GFP_KERNEL); + init_fh_domain(array->domain, array->comp, array->fhctl_base, + array->apmixed_base); + + priv_data->array =3D array; + priv_data->lock =3D &lock; + priv_data->domain =3D get_fh_domain(array->domain); + + /* do HW init */ + domain =3D priv_data->domain; + regs =3D &domain->regs[fh_id]; + data =3D &domain->data[fh_id]; + + fh_set_field(regs->reg_clk_con, mask, 1); + fh_set_field(regs->reg_rst_con, mask, 0); + fh_set_field(regs->reg_rst_con, mask, 1); + writel(0x0, regs->reg_cfg); + writel(0x0, regs->reg_updnlmt); + writel(0x0, regs->reg_dds); + + /* hook to array */ + hdlr->data =3D priv_data; + hdlr->ops =3D match->hdlr->ops; + /* hook hdlr to array is the last step */ + mb(); + array->hdlr =3D hdlr; + + /* do SSC */ + if (array->ssc_rate) { + struct fh_hdlr *hdlr =3D array->hdlr; + + hdlr->ops->ssc_enable(hdlr->data, array->domain, array->fh_id, + array->ssc_rate); + } + + return 0; +} + +static struct fh_operation fhctl_ap_ops =3D { + .hopping =3D fhctl_ap_hopping, + .ssc_enable =3D fhctl_ap_ssc_enable, + .ssc_disable =3D fhctl_ap_ssc_disable, +}; + +static struct fh_hdlr mt8186_hdlr =3D { + .ops =3D &fhctl_ap_ops, +}; + +static struct fh_ap_match mt8186_match =3D { + .name =3D "mediatek,mt8186-fhctl", + .hdlr =3D &mt8186_hdlr, + .init =3D &fhctl_ap_hw_init, +}; + +static struct fh_ap_match *matches[] =3D { + &mt8186_match, + NULL, +}; + +int fhctl_ap_init(struct pll_dts *array) +{ + int i; + int num_pll =3D array->num_pll; + struct fh_ap_match **match =3D matches; + + /* find match by compatible */ + for (i =3D 0; i < ARRAY_SIZE(matches); i++) { + char *comp =3D (*match)->name; + char *target =3D array->comp; + + if (!strcmp(comp, target)) + break; + match++; + } + + if (*match =3D=3D NULL) + return -1; + + /* init flow for every pll */ + for (i =3D 0; i < num_pll; i++, array++) { + char *method =3D array->method; + + if (!strcmp(method, FHCTL_TARGET)) + (*match)->init(array, *match); + } + + return 0; +} diff --git a/drivers/clk/mediatek/clk-fhctl-pll.c b/drivers/clk/mediatek/cl= k-fhctl-pll.c new file mode 100644 index 000000000000..b3ccbbd04e1b --- /dev/null +++ b/drivers/clk/mediatek/clk-fhctl-pll.c @@ -0,0 +1,209 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2022 MediaTek Inc. + */ + +#include +#include +#include "clk-fhctl-pll.h" +#include "clk-fhctl-util.h" + +#define REG_ADDR(base, x) ((void __iomem *)((unsigned long)base + (x))) + +struct fh_pll_match { + char *compatible; + struct fh_pll_domain **domain_list; +}; + +static int fhctl_pll_init(struct fh_pll_domain *d, void __iomem *fhctl_bas= e, + void __iomem *apmixed_base) +{ + struct fh_pll_data *data =3D d->data; + struct fh_pll_offset *offset =3D d->offset; + struct fh_pll_regs *regs =3D d->regs; + + if (regs->reg_hp_en) + return 0; + + while (data->dds_mask !=3D 0) { + int regs_offset; + + /* fhctl common part */ + regs->reg_hp_en =3D REG_ADDR(fhctl_base, offset->offset_hp_en); + regs->reg_clk_con =3D REG_ADDR(fhctl_base, + offset->offset_clk_con); + regs->reg_rst_con =3D REG_ADDR(fhctl_base, + offset->offset_rst_con); + regs->reg_slope0 =3D REG_ADDR(fhctl_base, offset->offset_slope0); + regs->reg_slope1 =3D REG_ADDR(fhctl_base, offset->offset_slope1); + + /* fhctl pll part */ + regs_offset =3D offset->offset_fhctl + offset->offset_cfg; + regs->reg_cfg =3D REG_ADDR(fhctl_base, regs_offset); + regs->reg_updnlmt =3D REG_ADDR(regs->reg_cfg, + offset->offset_updnlmt); + regs->reg_dds =3D REG_ADDR(regs->reg_cfg, offset->offset_dds); + regs->reg_dvfs =3D REG_ADDR(regs->reg_cfg, offset->offset_dvfs); + regs->reg_mon =3D REG_ADDR(regs->reg_cfg, offset->offset_mon); + + /* apmixed part */ + regs->reg_con_pcw =3D REG_ADDR(apmixed_base, + offset->offset_con_pcw); + regs->reg_con_postdiv =3D REG_ADDR(apmixed_base, + offset->offset_con_postdiv); + + data++; + offset++; + regs++; + } + + return 0; +} + +static unsigned int __postdiv_pow_tbl[8] =3D {1, 2, 4, 8, 16, 1, 1, 1}; +#define POSTDIV_TABLE_SIZE (sizeof(__postdiv_pow_tbl)\ + /sizeof(unsigned int)) + +#define SIZE_8186_TOP (sizeof(mt8186_top_data)\ + /sizeof(struct fh_pll_data)) +#define DATA_8186_TOP(_name) { \ + .name =3D _name, \ + .dds_mask =3D GENMASK(21, 0), \ + .postdiv_mask =3D GENMASK(26, 24), \ + .postdiv_offset =3D 24, \ + .postdiv_table =3D __postdiv_pow_tbl, \ + .postdiv_table_size =3D POSTDIV_TABLE_SIZE, \ + .slope0_value =3D 0x6003c97, \ + .slope1_value =3D 0x6003c97, \ + .sfstrx_en =3D BIT(2), \ + .frddsx_en =3D BIT(1), \ + .fhctlx_en =3D BIT(0), \ + .tgl_org =3D BIT(31), \ + .dvfs_tri =3D BIT(31), \ + .pcwchg =3D BIT(31), \ + .dt_val =3D 0x0, \ + .df_val =3D 0x9, \ + .updnlmt_shft =3D 16, \ + .msk_frddsx_dys =3D GENMASK(23, 20), \ + .msk_frddsx_dts =3D GENMASK(19, 16), \ + } +#define OFFSET_8186_TOP(_fhctl, _con_pcw) { \ + .offset_fhctl =3D _fhctl, \ + .offset_con_pcw =3D _con_pcw, \ + .offset_con_postdiv =3D _con_pcw, \ + .offset_hp_en =3D 0x0, \ + .offset_clk_con =3D 0x8, \ + .offset_rst_con =3D 0xc, \ + .offset_slope0 =3D 0x10, \ + .offset_slope1 =3D 0x14, \ + .offset_cfg =3D 0x0, \ + .offset_updnlmt =3D 0x4, \ + .offset_dds =3D 0x8, \ + .offset_dvfs =3D 0xc, \ + .offset_mon =3D 0x10, \ + } +static struct fh_pll_data mt8186_top_data[] =3D { + DATA_8186_TOP("armpll_ll"), + DATA_8186_TOP("armpll_bl"), + DATA_8186_TOP("ccipll"), + DATA_8186_TOP("mainpll"), + DATA_8186_TOP("mmpll"), + DATA_8186_TOP("tvdpll"), + DATA_8186_TOP("mpll"), + DATA_8186_TOP("adsppll"), + DATA_8186_TOP("mfgpll"), + DATA_8186_TOP("nnapll"), + DATA_8186_TOP("nna2pll"), + DATA_8186_TOP("msdcpll"), + DATA_8186_TOP("mempll"), + {} +}; +static struct fh_pll_offset mt8186_top_offset[] =3D { + OFFSET_8186_TOP(0x003C, 0x0208), + OFFSET_8186_TOP(0x0050, 0x0218), + OFFSET_8186_TOP(0x0064, 0x0228), + OFFSET_8186_TOP(0x0078, 0x0248), + OFFSET_8186_TOP(0x008C, 0x0258), + OFFSET_8186_TOP(0x00A0, 0x0268), + OFFSET_8186_TOP(0x00B4, 0x0278), + OFFSET_8186_TOP(0x00C8, 0x0308), + OFFSET_8186_TOP(0x00DC, 0x0318), + OFFSET_8186_TOP(0x00F0, 0x0360), + OFFSET_8186_TOP(0x0104, 0x0370), + OFFSET_8186_TOP(0x0118, 0x0390), + OFFSET_8186_TOP(0x012c, 0xdeb1), + {} +}; +static struct fh_pll_regs mt8186_top_regs[SIZE_8186_TOP]; +static struct fh_pll_domain mt8186_top =3D { + .name =3D "top", + .data =3D (struct fh_pll_data *)&mt8186_top_data, + .offset =3D (struct fh_pll_offset *)&mt8186_top_offset, + .regs =3D (struct fh_pll_regs *)&mt8186_top_regs, + .init =3D &fhctl_pll_init, +}; +static struct fh_pll_domain *mt8186_domain[] =3D { + &mt8186_top, + NULL, +}; +static struct fh_pll_match mt8186_match =3D { + .compatible =3D "mediatek,mt8186-fhctl", + .domain_list =3D (struct fh_pll_domain **)mt8186_domain, +}; + +static const struct fh_pll_match *matches[] =3D { + &mt8186_match, + NULL +}; + + +static struct fh_pll_domain **get_list(char *comp) +{ + struct fh_pll_match **match; + static struct fh_pll_domain **list; + int i; + + match =3D (struct fh_pll_match **)matches; + + /* name used only if !list */ + if (!list) { + for (i =3D 0; i < ARRAY_SIZE(matches); i++) { + if (!strcmp(comp, (*match)->compatible)) { + list =3D (*match)->domain_list; + break; + } + match++; + } + } + return list; +} +void init_fh_domain(const char *domain, char *comp, void __iomem *fhctl_ba= se, + void __iomem *apmixed_base) +{ + struct fh_pll_domain **list; + + list =3D get_list(comp); + + while (*list !=3D NULL) { + if (!strcmp(domain, (*list)->name)) { + (*list)->init(*list, fhctl_base, apmixed_base); + return; + } + list++; + } +} + +struct fh_pll_domain *get_fh_domain(const char *domain) +{ + struct fh_pll_domain **list; + + list =3D get_list(NULL); + + /* find instance */ + while (*list !=3D NULL) { + if (!strcmp(domain, (*list)->name)) + return *list; + list++; + } + return NULL; +} diff --git a/drivers/clk/mediatek/clk-fhctl-pll.h b/drivers/clk/mediatek/cl= k-fhctl-pll.h new file mode 100644 index 000000000000..7f0f7577f7a5 --- /dev/null +++ b/drivers/clk/mediatek/clk-fhctl-pll.h @@ -0,0 +1,74 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2022 MediaTek Inc. + */ + +#ifndef __CLK_FHCTL_PLL_H +#define __CLK_FHCTL_PLL_H +#include +#include +#include + +struct fh_pll_data { + char *name; + unsigned int dds_mask; + unsigned int postdiv_mask; + unsigned int postdiv_offset; + unsigned int *postdiv_table; + unsigned int postdiv_table_size; + unsigned int slope0_value; + unsigned int slope1_value; + unsigned int sfstrx_en; + unsigned int frddsx_en; + unsigned int fhctlx_en; + unsigned int tgl_org; + unsigned int dvfs_tri; + unsigned int pcwchg; + unsigned int dt_val; + unsigned int df_val; + unsigned int updnlmt_shft; + unsigned int msk_frddsx_dys; + unsigned int msk_frddsx_dts; +}; +struct fh_pll_offset { + int offset_fhctl; + int offset_con_pcw; + int offset_con_postdiv; + int offset_hp_en; + int offset_clk_con; + int offset_rst_con; + int offset_slope0; + int offset_slope1; + int offset_cfg; + int offset_updnlmt; + int offset_dds; + int offset_dvfs; + int offset_mon; +}; +struct fh_pll_regs { + void __iomem *reg_hp_en; + void __iomem *reg_clk_con; + void __iomem *reg_rst_con; + void __iomem *reg_slope0; + void __iomem *reg_slope1; + void __iomem *reg_cfg; + void __iomem *reg_updnlmt; + void __iomem *reg_dds; + void __iomem *reg_dvfs; + void __iomem *reg_mon; + void __iomem *reg_con_pcw; + void __iomem *reg_con_postdiv; +}; +struct fh_pll_domain { + char *name; + struct fh_pll_data *data; + struct fh_pll_offset *offset; + struct fh_pll_regs *regs; + int (*init)(struct fh_pll_domain *d, void __iomem *fhctl_base, + void __iomem *apmixed_base); +}; +extern struct fh_pll_domain *get_fh_domain(const char *name); +extern void init_fh_domain(const char *domain_name, char *comp_name, + void __iomem *fhctl_base, + void __iomem *apmixed_base); +#endif diff --git a/drivers/clk/mediatek/clk-fhctl-util.h b/drivers/clk/mediatek/c= lk-fhctl-util.h new file mode 100644 index 000000000000..824ed94b9f79 --- /dev/null +++ b/drivers/clk/mediatek/clk-fhctl-util.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2020 MediaTek Inc. + * Author: Yu-Chang Wang + */ + +#ifndef __CLK_FHCTL_UTIL_H +#define __CLK_FHCTL_UTIL_H + +#define fh_set_field(reg, field, val) \ +do { \ + unsigned int tv =3D readl(reg); \ + tv &=3D ~(field); \ + tv |=3D ((val) << (ffs(field) - 1)); \ + writel(tv, reg); \ +} while (0) + +#define fh_get_field(reg, field, val) \ +do { \ + unsigned int tv =3D readl(reg); \ + val =3D ((tv & (field)) >> (ffs(field) - 1)); \ +} while (0) + +#endif diff --git a/drivers/clk/mediatek/clk-fhctl.c b/drivers/clk/mediatek/clk-fh= ctl.c new file mode 100644 index 000000000000..606245f84d71 --- /dev/null +++ b/drivers/clk/mediatek/clk-fhctl.c @@ -0,0 +1,191 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2022 MediaTek Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "clk-fhctl.h" +#include "clk-fhctl-util.h" +#include "clk-mtk.h" + +static struct pll_dts *_array; + +bool mtk_clk_try_freq_hopping(const char *pll_name, unsigned long dds, + int postdiv) +{ + int i; + struct fh_hdlr *hdlr =3D NULL; + struct pll_dts *array; + int num_pll; + + if (_array =3D=3D NULL) + return false; + + array =3D _array; + num_pll =3D array->num_pll; + + for (i =3D 0; i < num_pll; i++, array++) { + if (!strcmp(pll_name, array->pll_name)) { + hdlr =3D array->hdlr; + break; + } + } + + if (hdlr && (array->perms & PERM_DRV_HOP)) { + hdlr->ops->hopping(hdlr->data, array->domain, array->fh_id, + dds, postdiv); + return true; + } + + return false; +} +EXPORT_SYMBOL_GPL(mtk_clk_try_freq_hopping); + +static struct pll_dts *parse_dt(struct platform_device *pdev) +{ + struct device_node *root, *map, *of_pll; + unsigned int num_pll =3D 0; + int iomap_idx =3D 0; + struct pll_dts *array; + int pll_idx =3D 0; + const struct of_device_id *match; + + root =3D pdev->dev.of_node; + match =3D of_match_node(pdev->dev.driver->of_match_table, root); + + /* iterate dts to get pll count */ + for_each_child_of_node(root, map) { + for_each_child_of_node(map, of_pll) { + num_pll++; + } + } + + array =3D kzalloc(sizeof(*array) * num_pll, GFP_KERNEL); + + for_each_child_of_node(root, map) { + void __iomem *fhctl_base, *apmixed_base; + char *domain, *method; + int num =3D 0; + + fhctl_base =3D of_iomap(root, iomap_idx++); + apmixed_base =3D of_iomap(root, iomap_idx++); + of_property_read_string(map, "domain", (const char **)&domain); + of_property_read_string(map, "method", (const char **)&method); + + for_each_child_of_node(map, of_pll) { + int fh_id, perms, ssc_rate; + + if (pll_idx >=3D num_pll) { + pll_idx++; + continue; + } + + /* default for optional field */ + perms =3D 0xffffffff; + ssc_rate =3D 0; + + of_property_read_u32(of_pll, "fh-id", &fh_id); + of_property_read_u32(of_pll, "perms", &perms); + of_property_read_u32(of_pll, "ssc-rate", &ssc_rate); + array[pll_idx].num_pll =3D num_pll; + array[pll_idx].comp =3D (char *)match->compatible; + array[pll_idx].pll_name =3D (char *)of_pll->name; + array[pll_idx].fh_id =3D fh_id; + array[pll_idx].perms =3D perms; + array[pll_idx].ssc_rate =3D ssc_rate; + array[pll_idx].domain =3D domain; + array[pll_idx].method =3D method; + array[pll_idx].fhctl_base =3D fhctl_base; + array[pll_idx].apmixed_base =3D apmixed_base; + num++; + pll_idx++; + } + } + + return array; +} + +static int fh_plt_drv_probe(struct platform_device *pdev) +{ + int i, ret; + struct pll_dts *array; + + /* convert dt to data */ + array =3D parse_dt(pdev); + + ret =3D fhctl_ap_init(array); + if (ret) + return -1; + + /* make sure array is complete */ + for (i =3D 0; i < array->num_pll; i++) { + struct fh_hdlr *hdlr =3D array[i].hdlr; + + if (!hdlr) { + dev_err(&pdev->dev, "Failed to set %s hdlr\n", + array->pll_name); + return -1; + } + } + + /* make sure init complete */ + mb(); + _array =3D array; + + return 0; +} + +static void fh_plt_drv_shutdown(struct platform_device *pdev) +{ + struct pll_dts *array =3D _array; + int num_pll =3D array->num_pll; + int i; + + for (i =3D 0; i < num_pll; i++, array++) { + struct fh_hdlr *hdlr =3D array->hdlr; + + if (array->ssc_rate) + hdlr->ops->ssc_disable(hdlr->data, array->domain, + array->fh_id); + } +} + +static const struct of_device_id fh_of_match[] =3D { + { .compatible =3D "mediatek,mt8186-fhctl"}, + {} +}; + +static struct platform_driver fhctl_driver =3D { + .probe =3D fh_plt_drv_probe, + .shutdown =3D fh_plt_drv_shutdown, + .driver =3D { + .name =3D "mtk-fhctl", + .owner =3D THIS_MODULE, + .of_match_table =3D fh_of_match, + }, +}; + +static int __init fhctl_driver_init(void) +{ + return platform_driver_register(&fhctl_driver); +} +device_initcall_sync(fhctl_driver_init); + +static void __exit fhctl_driver_exit(void) +{ + platform_driver_unregister(&fhctl_driver); +} +module_exit(fhctl_driver_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("MediaTek FHCTL Driver"); +MODULE_AUTHOR("Kuan-Hsin Lee "); diff --git a/drivers/clk/mediatek/clk-fhctl.h b/drivers/clk/mediatek/clk-fh= ctl.h new file mode 100644 index 000000000000..b53a99d6cac7 --- /dev/null +++ b/drivers/clk/mediatek/clk-fhctl.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2022 MediaTek Inc. + */ + +#ifndef __CLK_FHCTL_H +#define __CLK_FHCTL_H + +struct fh_operation { + int (*hopping)(void *data, char *domain, int fh_id, + unsigned int new_dds, int postdiv); + int (*ssc_enable)(void *data, char *domain, int fh_id, int rate); + int (*ssc_disable)(void *data, char *domain, int fh_id); +}; + +struct fh_hdlr { + void *data; + struct fh_operation *ops; +}; + +struct pll_dts { + char *comp; + int num_pll; + char *domain; + char *method; + char *pll_name; + int fh_id; + int perms; + int ssc_rate; + void __iomem *fhctl_base; + void __iomem *apmixed_base; + struct fh_hdlr *hdlr; +}; + +#define PERM_DRV_HOP BIT(0) +#define PERM_DRV_SSC BIT(1) +#define PERM_DBG_HOP BIT(2) +#define PERM_DBG_SSC BIT(3) +#define PERM_DBG_DUMP BIT(4) + +#define FHCTL_AP "fhctl-ap" + +extern int fhctl_ap_init(struct pll_dts *array); + +#endif diff --git a/drivers/clk/mediatek/clk-pll.c b/drivers/clk/mediatek/clk-pll.c index 54e6cfd29dfc..1acd21ca4b93 100644 --- a/drivers/clk/mediatek/clk-pll.c +++ b/drivers/clk/mediatek/clk-pll.c @@ -206,7 +206,10 @@ static int mtk_pll_set_rate(struct clk_hw *hw, unsigne= d long rate, u32 postdiv; =20 mtk_pll_calc_values(pll, &pcw, &postdiv, rate, parent_rate); - mtk_pll_set_rate_regs(pll, pcw, postdiv); +#ifdef CONFIG_COMMON_CLK_MTK_FREQ_HOPPING + if (!mtk_clk_try_freq_hopping(pll->data->name, pcw, postdiv)) +#endif + mtk_pll_set_rate_regs(pll, pcw, postdiv); =20 return 0; } diff --git a/drivers/clk/mediatek/clk-pll.h b/drivers/clk/mediatek/clk-pll.h index fe3199715688..e95f5f48f308 100644 --- a/drivers/clk/mediatek/clk-pll.h +++ b/drivers/clk/mediatek/clk-pll.h @@ -54,4 +54,9 @@ int mtk_clk_register_plls(struct device_node *node, void mtk_clk_unregister_plls(const struct mtk_pll_data *plls, int num_plls, struct clk_hw_onecell_data *clk_data); =20 +#ifdef CONFIG_COMMON_CLK_MTK_FREQ_HOPPING +extern bool mtk_clk_try_freq_hopping(const char *pll_name, unsigned long d= ds, + int postdiv); +#endif + #endif /* __DRV_CLK_MTK_PLL_H */ --=20 2.18.0