The purpose of this serie is to enhance frequency hopping and spread spectrum
clocking feature for MT8186.
We introduce new PLL register APIs and some helpers for FHCTL hardware control.
For MT8186 PLL driver, we replace mtk_clk_register_plls() with newly added API
to support frequency hopping and SSC function for specific PLLs.
Changes in v3:
- Change binding file name.
- Add some constraints for properties.
- Rename "mediatek,hopping-ssc-percents" to "mediatek,hopping-ssc-percent".
- Add new config symbol.
Changes in v2:
- Use SoC-specific compatible instead of generic one.
- Use standard clocks property and vendor-specific property in dt-binding.
- Remove some unused arguments and fix some coding style.
Johnson Wang (4):
clk: mediatek: Export PLL operations symbols
dt-bindings: arm: mediatek: Add new bindings of MediaTek frequency
hopping
clk: mediatek: Add new clock driver to handle FHCTL hardware
clk: mediatek: Change PLL register API for MT8186
.../arm/mediatek/mediatek,mt8186-fhctl.yaml | 53 ++++
drivers/clk/mediatek/Kconfig | 8 +
drivers/clk/mediatek/Makefile | 1 +
drivers/clk/mediatek/clk-fhctl.c | 244 ++++++++++++++++
drivers/clk/mediatek/clk-fhctl.h | 26 ++
drivers/clk/mediatek/clk-mt8186-apmixedsys.c | 66 ++++-
drivers/clk/mediatek/clk-pll.c | 84 +++---
drivers/clk/mediatek/clk-pll.h | 56 ++++
drivers/clk/mediatek/clk-pllfh.c | 268 ++++++++++++++++++
drivers/clk/mediatek/clk-pllfh.h | 82 ++++++
10 files changed, 835 insertions(+), 53 deletions(-)
create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,mt8186-fhctl.yaml
create mode 100644 drivers/clk/mediatek/clk-fhctl.c
create mode 100644 drivers/clk/mediatek/clk-fhctl.h
create mode 100644 drivers/clk/mediatek/clk-pllfh.c
create mode 100644 drivers/clk/mediatek/clk-pllfh.h
--
2.18.0
Add the new binding documentation for MediaTek frequency hopping
and spread spectrum clocking control.
Co-developed-by: Edward-JW Yang <[email protected]>
Signed-off-by: Edward-JW Yang <[email protected]>
Signed-off-by: Johnson Wang <[email protected]>
---
.../arm/mediatek/mediatek,mt8186-fhctl.yaml | 53 +++++++++++++++++++
1 file changed, 53 insertions(+)
create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,mt8186-fhctl.yaml
diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,mt8186-fhctl.yaml b/Documentation/devicetree/bindings/arm/mediatek/mediatek,mt8186-fhctl.yaml
new file mode 100644
index 000000000000..258dff7ce6bc
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,mt8186-fhctl.yaml
@@ -0,0 +1,53 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/arm/mediatek/mediatek,mt8186-fhctl.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: MediaTek frequency hopping and spread spectrum clocking control
+
+maintainers:
+ - Edward-JW Yang <[email protected]>
+
+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 hardware.
+
+properties:
+ compatible:
+ const: mediatek,mt8186-fhctl
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ description: Phandles of the PLL with FHCTL hardware capability.
+ minItems: 1
+ maxItems: 30
+
+ mediatek,hopping-ssc-percent:
+ description: The percentage of spread spectrum clocking for one PLL.
+ minItems: 1
+ maxItems: 30
+ items:
+ default: 0
+ minimum: 0
+ maximum: 8
+
+required:
+ - compatible
+ - reg
+ - clocks
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/mt8186-clk.h>
+ fhctl: fhctl@1000ce00 {
+ compatible = "mediatek,mt8186-fhctl";
+ reg = <0x1000c000 0xe00>;
+ clocks = <&apmixedsys CLK_APMIXED_MSDCPLL>;
+ mediatek,hopping-ssc-percent = <3>;
+ };
--
2.18.0
To implement frequency hopping and spread spectrum clocking
function, we introduce new clock type and APIs to handle
FHCTL hardware.
Co-developed-by: Edward-JW Yang <[email protected]>
Signed-off-by: Edward-JW Yang <[email protected]>
Signed-off-by: Johnson Wang <[email protected]>
---
drivers/clk/mediatek/Kconfig | 7 +
drivers/clk/mediatek/Makefile | 1 +
drivers/clk/mediatek/clk-fhctl.c | 244 ++++++++++++++++++++++++++++
drivers/clk/mediatek/clk-fhctl.h | 26 +++
drivers/clk/mediatek/clk-pllfh.c | 268 +++++++++++++++++++++++++++++++
drivers/clk/mediatek/clk-pllfh.h | 82 ++++++++++
6 files changed, 628 insertions(+)
create mode 100644 drivers/clk/mediatek/clk-fhctl.c
create mode 100644 drivers/clk/mediatek/clk-fhctl.h
create mode 100644 drivers/clk/mediatek/clk-pllfh.c
create mode 100644 drivers/clk/mediatek/clk-pllfh.h
diff --git a/drivers/clk/mediatek/Kconfig b/drivers/clk/mediatek/Kconfig
index d5936cfb3bee..65b25082daf2 100644
--- a/drivers/clk/mediatek/Kconfig
+++ b/drivers/clk/mediatek/Kconfig
@@ -11,6 +11,13 @@ config COMMON_CLK_MEDIATEK
help
MediaTek SoCs' clock support.
+config COMMON_CLK_MEDIATEK_FHCTL
+ bool "clock driver for MediaTek FHCTL hardware control"
+ select COMMON_CLK_MEDIATEK
+ help
+ This driver supports MediaTek frequency hopping and
+ spread spectrum clocking features.
+
config COMMON_CLK_MT2701
bool "Clock driver for MediaTek MT2701"
depends on (ARCH_MEDIATEK && ARM) || COMPILE_TEST
diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile
index caf2ce93d666..80a9f4a4aa03 100644
--- a/drivers/clk/mediatek/Makefile
+++ b/drivers/clk/mediatek/Makefile
@@ -1,5 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_COMMON_CLK_MEDIATEK) += clk-mtk.o clk-pll.o clk-gate.o clk-apmixed.o clk-cpumux.o reset.o clk-mux.o
+obj-$(CONFIG_COMMON_CLK_MEDIATEK_FHCTL) += clk-fhctl.o clk-pllfh.o
obj-$(CONFIG_COMMON_CLK_MT6765) += clk-mt6765.o
obj-$(CONFIG_COMMON_CLK_MT6765_AUDIOSYS) += clk-mt6765-audio.o
diff --git a/drivers/clk/mediatek/clk-fhctl.c b/drivers/clk/mediatek/clk-fhctl.c
new file mode 100644
index 000000000000..4f271acef5fe
--- /dev/null
+++ b/drivers/clk/mediatek/clk-fhctl.c
@@ -0,0 +1,244 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Edward-JW Yang <[email protected]>
+ */
+
+#include <linux/io.h>
+#include <linux/iopoll.h>
+
+#include "clk-mtk.h"
+#include "clk-pllfh.h"
+#include "clk-fhctl.h"
+
+#define PERCENT_TO_DDSLMT(dds, percent_m10) \
+ ((((dds) * (percent_m10)) >> 5) / 100)
+
+static const struct fhctl_offset fhctl_offset = {
+ .offset_hp_en = 0x0,
+ .offset_clk_con = 0x8,
+ .offset_rst_con = 0xc,
+ .offset_slope0 = 0x10,
+ .offset_slope1 = 0x14,
+ .offset_cfg = 0x0,
+ .offset_updnlmt = 0x4,
+ .offset_dds = 0x8,
+ .offset_dvfs = 0xc,
+ .offset_mon = 0x10,
+};
+
+const struct fhctl_offset *fhctl_get_offset_table(void)
+{
+ return &fhctl_offset;
+}
+
+static void dump_hw(struct mtk_clk_pll *pll, struct fh_pll_regs *regs,
+ const struct fh_pll_data *data)
+{
+ pr_info("hp_en<%x>,clk_con<%x>,slope0<%x>,slope1<%x>\n",
+ readl(regs->reg_hp_en), readl(regs->reg_clk_con),
+ readl(regs->reg_slope0), readl(regs->reg_slope1));
+ pr_info("cfg<%x>,lmt<%x>,dds<%x>,dvfs<%x>,mon<%x>\n",
+ readl(regs->reg_cfg), readl(regs->reg_updnlmt),
+ readl(regs->reg_dds), readl(regs->reg_dvfs),
+ readl(regs->reg_mon));
+ pr_info("pcw<%x>\n", readl(pll->pcw_addr));
+}
+
+static int fhctl_set_ssc_regs(struct mtk_clk_pll *pll, struct fh_pll_regs *regs,
+ const struct fh_pll_data *data, u32 rate)
+{
+ u32 updnlmt_val, r;
+
+ writel((readl(regs->reg_cfg) & ~(data->frddsx_en)), regs->reg_cfg);
+ writel((readl(regs->reg_cfg) & ~(data->sfstrx_en)), regs->reg_cfg);
+ writel((readl(regs->reg_cfg) & ~(data->fhctlx_en)), regs->reg_cfg);
+
+ if (rate > 0) {
+ /* Set the relative parameter registers (dt/df/upbnd/downbnd) */
+ r = readl(regs->reg_cfg);
+ r &= ~(data->msk_frddsx_dys);
+ r |= (data->df_val << (ffs(data->msk_frddsx_dys) - 1));
+ writel(r, regs->reg_cfg);
+
+ r = readl(regs->reg_cfg);
+ r &= ~(data->msk_frddsx_dts);
+ r |= (data->dt_val << (ffs(data->msk_frddsx_dts) - 1));
+ writel(r, regs->reg_cfg);
+
+ writel((readl(pll->pcw_addr) & data->dds_mask) | data->tgl_org,
+ regs->reg_dds);
+
+ /* Calculate UPDNLMT */
+ updnlmt_val = PERCENT_TO_DDSLMT((readl(regs->reg_dds) &
+ data->dds_mask), rate) <<
+ data->updnlmt_shft;
+
+ writel(updnlmt_val, regs->reg_updnlmt);
+ writel(readl(regs->reg_hp_en) | BIT(data->fh_id),
+ regs->reg_hp_en);
+ /* Enable SSC */
+ writel(readl(regs->reg_cfg) | data->frddsx_en, regs->reg_cfg);
+ /* Enable Hopping control */
+ writel(readl(regs->reg_cfg) | data->fhctlx_en, regs->reg_cfg);
+
+ } else {
+ /* Switch to APMIXEDSYS control */
+ writel(readl(regs->reg_hp_en) & ~BIT(data->fh_id),
+ regs->reg_hp_en);
+ /* Wait for DDS to be stable */
+ udelay(30);
+ }
+
+ return 0;
+}
+
+static int hopping_hw_flow(struct mtk_clk_pll *pll, struct fh_pll_regs *regs,
+ const struct fh_pll_data *data,
+ struct fh_pll_state *state, unsigned int new_dds)
+{
+ u32 dds_mask = data->dds_mask;
+ u32 mon_dds = 0;
+ u32 con_pcw_tmp;
+ int ret;
+
+ if (state->ssc_rate)
+ fhctl_set_ssc_regs(pll, regs, data, 0);
+
+ writel((readl(pll->pcw_addr) & dds_mask) | data->tgl_org,
+ regs->reg_dds);
+
+ writel(readl(regs->reg_cfg) | data->sfstrx_en, regs->reg_cfg);
+ writel(readl(regs->reg_cfg) | data->fhctlx_en, regs->reg_cfg);
+ writel(data->slope0_value, regs->reg_slope0);
+ writel(data->slope1_value, regs->reg_slope1);
+
+ writel(readl(regs->reg_hp_en) | BIT(data->fh_id), regs->reg_hp_en);
+ writel((new_dds) | (data->dvfs_tri), regs->reg_dvfs);
+
+ /* Wait 1000 us until DDS stable */
+ ret = readl_poll_timeout_atomic(regs->reg_mon, mon_dds,
+ (mon_dds & dds_mask) == new_dds,
+ 10, 1000);
+ if (ret) {
+ pr_warn("%s: FHCTL hopping timeout\n", pll->data->name);
+ dump_hw(pll, regs, data);
+ }
+
+ con_pcw_tmp = readl(pll->pcw_addr) & (~dds_mask);
+ con_pcw_tmp = (con_pcw_tmp | (readl(regs->reg_mon) & dds_mask) |
+ data->pcwchg);
+
+ writel(con_pcw_tmp, pll->pcw_addr);
+ writel(readl(regs->reg_hp_en) & ~BIT(data->fh_id), regs->reg_hp_en);
+
+ if (state->ssc_rate)
+ fhctl_set_ssc_regs(pll, regs, data, state->ssc_rate);
+
+ return ret;
+}
+
+static unsigned int __get_postdiv(struct mtk_clk_pll *pll)
+{
+ unsigned int regval;
+
+ regval = readl(pll->pd_addr) >> pll->data->pd_shift;
+ regval &= POSTDIV_MASK;
+
+ return BIT(regval);
+}
+
+static void __set_postdiv(struct mtk_clk_pll *pll, unsigned int postdiv)
+{
+ unsigned int regval;
+
+ regval = readl(pll->pd_addr);
+ regval &= ~(POSTDIV_MASK << pll->data->pd_shift);
+ regval |= (ffs(postdiv) - 1) << pll->data->pd_shift;
+ writel(regval, pll->pd_addr);
+}
+
+static int fhctl_hopping(struct mtk_fh *fh, unsigned int new_dds,
+ unsigned int postdiv)
+{
+ const struct fh_pll_data *data = &fh->pllfh_data->data;
+ struct fh_pll_state *state = &fh->pllfh_data->state;
+ struct fh_pll_regs *regs = &fh->regs;
+ struct mtk_clk_pll *pll = &fh->clk_pll;
+ spinlock_t *lock = fh->lock;
+ unsigned int pll_postdiv;
+ unsigned long flags = 0;
+ int ret;
+
+ if (postdiv) {
+ pll_postdiv = __get_postdiv(pll);
+
+ if (postdiv > pll_postdiv)
+ __set_postdiv(pll, postdiv);
+ }
+
+ spin_lock_irqsave(lock, flags);
+
+ ret = hopping_hw_flow(pll, regs, data, state, new_dds);
+
+ spin_unlock_irqrestore(lock, flags);
+
+ if (postdiv && postdiv < pll_postdiv)
+ __set_postdiv(pll, postdiv);
+
+ return ret;
+}
+
+static int fhctl_ssc_enable(struct mtk_fh *fh, u32 rate)
+{
+ const struct fh_pll_data *data = &fh->pllfh_data->data;
+ struct fh_pll_state *state = &fh->pllfh_data->state;
+ struct fh_pll_regs *regs = &fh->regs;
+ struct mtk_clk_pll *pll = &fh->clk_pll;
+ spinlock_t *lock = fh->lock;
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(lock, flags);
+
+ fhctl_set_ssc_regs(pll, regs, data, rate);
+ state->ssc_rate = rate;
+
+ spin_unlock_irqrestore(lock, flags);
+
+ return 0;
+}
+
+static const struct fh_operation fhctl_ops = {
+ .hopping = fhctl_hopping,
+ .ssc_enable = fhctl_ssc_enable,
+};
+
+const struct fh_operation *fhctl_get_ops(void)
+{
+ return &fhctl_ops;
+}
+
+void fhctl_hw_init(struct mtk_fh *fh)
+{
+ const struct fh_pll_data data = fh->pllfh_data->data;
+ struct fh_pll_state state = fh->pllfh_data->state;
+ struct fh_pll_regs regs = fh->regs;
+ u32 val;
+
+ /* initial hw register */
+ val = readl(regs.reg_clk_con) | BIT(data.fh_id);
+ writel(val, regs.reg_clk_con);
+
+ val = readl(regs.reg_rst_con) & ~BIT(data.fh_id);
+ writel(val, regs.reg_rst_con);
+ val = readl(regs.reg_rst_con) | BIT(data.fh_id);
+ writel(val, regs.reg_rst_con);
+
+ writel(0x0, regs.reg_cfg);
+ writel(0x0, regs.reg_updnlmt);
+ writel(0x0, regs.reg_dds);
+
+ /* enable ssc if needed */
+ if (state.ssc_rate)
+ fh->ops->ssc_enable(fh, state.ssc_rate);
+}
diff --git a/drivers/clk/mediatek/clk-fhctl.h b/drivers/clk/mediatek/clk-fhctl.h
new file mode 100644
index 000000000000..51275febf086
--- /dev/null
+++ b/drivers/clk/mediatek/clk-fhctl.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Edward-JW Yang <[email protected]>
+ */
+
+#ifndef __CLK_FHCTL_H
+#define __CLK_FHCTL_H
+
+struct fhctl_offset {
+ u32 offset_hp_en;
+ u32 offset_clk_con;
+ u32 offset_rst_con;
+ u32 offset_slope0;
+ u32 offset_slope1;
+ u32 offset_cfg;
+ u32 offset_updnlmt;
+ u32 offset_dds;
+ u32 offset_dvfs;
+ u32 offset_mon;
+};
+const struct fhctl_offset *fhctl_get_offset_table(void);
+const struct fh_operation *fhctl_get_ops(void);
+void fhctl_hw_init(struct mtk_fh *fh);
+
+#endif
diff --git a/drivers/clk/mediatek/clk-pllfh.c b/drivers/clk/mediatek/clk-pllfh.c
new file mode 100644
index 000000000000..a728ff749db1
--- /dev/null
+++ b/drivers/clk/mediatek/clk-pllfh.c
@@ -0,0 +1,268 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Edward-JW Yang <[email protected]>
+ */
+
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/clkdev.h>
+#include <linux/delay.h>
+
+#include "clk-mtk.h"
+#include "clk-pllfh.h"
+#include "clk-fhctl.h"
+
+static DEFINE_SPINLOCK(pllfh_lock);
+
+inline struct mtk_fh *to_mtk_fh(struct clk_hw *hw)
+{
+ struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
+
+ return container_of(pll, struct mtk_fh, clk_pll);
+}
+
+static int mtk_fhctl_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
+ struct mtk_fh *fh = to_mtk_fh(hw);
+ u32 pcw = 0;
+ u32 postdiv;
+
+ mtk_pll_calc_values(pll, &pcw, &postdiv, rate, parent_rate);
+
+ return fh->ops->hopping(fh, pcw, postdiv);
+}
+
+static const struct clk_ops mtk_pllfh_ops = {
+ .is_prepared = mtk_pll_is_prepared,
+ .prepare = mtk_pll_prepare,
+ .unprepare = mtk_pll_unprepare,
+ .recalc_rate = mtk_pll_recalc_rate,
+ .round_rate = mtk_pll_round_rate,
+ .set_rate = mtk_fhctl_set_rate,
+};
+
+static struct mtk_pllfh_data *get_pllfh_by_id(struct mtk_pllfh_data *pllfhs,
+ int num_fhs, int pll_id)
+{
+ int i;
+
+ for (i = 0; i < num_fhs; i++)
+ if (pllfhs[i].data.pll_id == pll_id)
+ return &pllfhs[i];
+
+ return NULL;
+}
+
+void fhctl_parse_dt(const u8 *compatible_node, struct mtk_pllfh_data *pllfhs,
+ int num_fhs)
+{
+ void __iomem *base;
+ struct device_node *node;
+ u32 num_clocks, pll_id, ssc_rate;
+ int offset, i;
+
+ node = of_find_compatible_node(NULL, NULL, compatible_node);
+ if (!node) {
+ pr_err("cannot find \"%s\"\n", compatible_node);
+ return;
+ }
+
+ base = of_iomap(node, 0);
+ if (!base) {
+ pr_err("%s(): ioremap failed\n", __func__);
+ return;
+ }
+
+ num_clocks = of_clk_get_parent_count(node);
+ if (!num_clocks) {
+ pr_err("%s(): failed to get clocks property\n", __func__);
+ return;
+ }
+
+ for (i = 0; i < num_clocks; i++) {
+ struct mtk_pllfh_data *pllfh;
+
+ offset = i * 2;
+
+ of_property_read_u32_index(node, "clocks", offset + 1, &pll_id);
+ of_property_read_u32_index(node,
+ "mediatek,hopping-ssc-percent",
+ i, &ssc_rate);
+
+ pllfh = get_pllfh_by_id(pllfhs, num_fhs, pll_id);
+ if (!pllfh)
+ continue;
+
+ pllfh->state.fh_enable = 1;
+ pllfh->state.ssc_rate = ssc_rate;
+ pllfh->state.base = base;
+ }
+}
+
+static void pllfh_init(struct mtk_fh *fh, struct mtk_pllfh_data *pllfh_data)
+{
+ struct fh_pll_regs *regs = &fh->regs;
+ const struct fhctl_offset *offset;
+ void __iomem *base = pllfh_data->state.base;
+ void __iomem *fhx_base = base + pllfh_data->data.fhx_offset;
+
+ offset = fhctl_get_offset_table();
+
+ regs->reg_hp_en = base + offset->offset_hp_en;
+ regs->reg_clk_con = base + offset->offset_clk_con;
+ regs->reg_rst_con = base + offset->offset_rst_con;
+ regs->reg_slope0 = base + offset->offset_slope0;
+ regs->reg_slope1 = base + offset->offset_slope1;
+
+ regs->reg_cfg = fhx_base + offset->offset_cfg;
+ regs->reg_updnlmt = fhx_base + offset->offset_updnlmt;
+ regs->reg_dds = fhx_base + offset->offset_dds;
+ regs->reg_dvfs = fhx_base + offset->offset_dvfs;
+ regs->reg_mon = fhx_base + offset->offset_mon;
+
+ fh->pllfh_data = pllfh_data;
+ fh->lock = &pllfh_lock;
+
+ fh->ops = fhctl_get_ops();
+}
+
+static bool fhctl_is_supported_and_enabled(const struct mtk_pllfh_data *pllfh)
+{
+ return pllfh && (pllfh->state.fh_enable == 1);
+}
+
+static struct clk_hw *
+mtk_clk_register_pllfh(const struct mtk_pll_data *pll_data,
+ struct mtk_pllfh_data *pllfh_data, void __iomem *base)
+{
+ struct clk_hw *hw;
+ struct mtk_fh *fh;
+
+ fh = kzalloc(sizeof(*fh), GFP_KERNEL);
+ if (!fh)
+ return ERR_PTR(-ENOMEM);
+
+ pllfh_init(fh, pllfh_data);
+
+ hw = mtk_clk_register_pll_ops(&fh->clk_pll, pll_data, base,
+ &mtk_pllfh_ops);
+
+ if (IS_ERR(hw))
+ kfree(fh);
+ else
+ fhctl_hw_init(fh);
+
+ return hw;
+}
+
+static void mtk_clk_unregister_pllfh(struct clk_hw *hw)
+{
+ struct mtk_fh *fh;
+
+ if (!hw)
+ return;
+
+ fh = to_mtk_fh(hw);
+
+ clk_hw_unregister(hw);
+ kfree(fh);
+}
+
+int mtk_clk_register_pllfhs(struct device_node *node,
+ const struct mtk_pll_data *plls, int num_plls,
+ struct mtk_pllfh_data *pllfhs, int num_fhs,
+ struct clk_hw_onecell_data *clk_data)
+{
+ void __iomem *base;
+ int i;
+ struct clk_hw *hw;
+
+ base = of_iomap(node, 0);
+ if (!base) {
+ pr_err("%s(): ioremap failed\n", __func__);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < num_plls; i++) {
+ const struct mtk_pll_data *pll = &plls[i];
+ struct mtk_pllfh_data *pllfh;
+
+ pllfh = get_pllfh_by_id(pllfhs, num_fhs, pll->id);
+
+ if (fhctl_is_supported_and_enabled(pllfh))
+ hw = mtk_clk_register_pllfh(pll, pllfh, base);
+ else
+ hw = mtk_clk_register_pll(pll, base);
+
+ if (IS_ERR(hw)) {
+ pr_err("Failed to register clk %s: %pe\n", pll->name,
+ hw);
+ goto err;
+ }
+
+ clk_data->hws[pll->id] = hw;
+ }
+
+ return 0;
+
+err:
+ while (--i >= 0) {
+ const struct mtk_pll_data *pll = &plls[i];
+ struct mtk_pllfh_data *pllfh;
+
+ pllfh = get_pllfh_by_id(pllfhs, num_fhs, pll->id);
+
+ if (fhctl_is_supported_and_enabled(pllfh))
+ mtk_clk_unregister_pllfh(clk_data->hws[pll->id]);
+ else
+ mtk_clk_unregister_pll(clk_data->hws[pll->id]);
+
+ clk_data->hws[pll->id] = ERR_PTR(-ENOENT);
+ }
+
+ iounmap(base);
+
+ return PTR_ERR(hw);
+}
+
+void mtk_clk_unregister_pllfhs(const struct mtk_pll_data *plls, int num_plls,
+ struct mtk_pllfh_data *pllfhs, int num_fhs,
+ struct clk_hw_onecell_data *clk_data)
+{
+ void __iomem *base = NULL, *fhctl_base = NULL;
+ int i;
+
+ if (!clk_data)
+ return;
+
+ for (i = num_plls; i > 0; i--) {
+ const struct mtk_pll_data *pll = &plls[i - 1];
+ struct mtk_pllfh_data *pllfh;
+
+ if (IS_ERR_OR_NULL(clk_data->hws[pll->id]))
+ continue;
+
+ pllfh = get_pllfh_by_id(pllfhs, num_fhs, pll->id);
+
+ if (fhctl_is_supported_and_enabled(pllfh)) {
+ fhctl_base = pllfh->state.base;
+ mtk_clk_unregister_pllfh(clk_data->hws[pll->id]);
+ } else {
+ base = mtk_clk_pll_get_base(clk_data->hws[pll->id],
+ pll);
+ mtk_clk_unregister_pll(clk_data->hws[pll->id]);
+ }
+
+ clk_data->hws[pll->id] = ERR_PTR(-ENOENT);
+ }
+
+ if (fhctl_base)
+ iounmap(fhctl_base);
+
+ iounmap(base);
+}
diff --git a/drivers/clk/mediatek/clk-pllfh.h b/drivers/clk/mediatek/clk-pllfh.h
new file mode 100644
index 000000000000..effc7976c496
--- /dev/null
+++ b/drivers/clk/mediatek/clk-pllfh.h
@@ -0,0 +1,82 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Edward-JW Yang <[email protected]>
+ */
+
+#ifndef __DRV_CLKFH_H
+#define __DRV_CLKFH_H
+
+#include "clk-pll.h"
+
+struct fh_pll_state {
+ void __iomem *base;
+ u32 fh_enable;
+ u32 ssc_rate;
+};
+
+struct fh_pll_data {
+ int pll_id;
+ int fh_id;
+ u32 fhx_offset;
+ u32 dds_mask;
+ u32 slope0_value;
+ u32 slope1_value;
+ u32 sfstrx_en;
+ u32 frddsx_en;
+ u32 fhctlx_en;
+ u32 tgl_org;
+ u32 dvfs_tri;
+ u32 pcwchg;
+ u32 dt_val;
+ u32 df_val;
+ u32 updnlmt_shft;
+ u32 msk_frddsx_dys;
+ u32 msk_frddsx_dts;
+};
+
+struct mtk_pllfh_data {
+ struct fh_pll_state state;
+ const struct fh_pll_data data;
+};
+
+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;
+};
+
+struct mtk_fh {
+ struct mtk_clk_pll clk_pll;
+ struct fh_pll_regs regs;
+ struct mtk_pllfh_data *pllfh_data;
+ const struct fh_operation *ops;
+ spinlock_t *lock;
+};
+
+struct fh_operation {
+ int (*hopping)(struct mtk_fh *fh, unsigned int new_dds,
+ unsigned int postdiv);
+ int (*ssc_enable)(struct mtk_fh *fh, u32 rate);
+};
+
+int mtk_clk_register_pllfhs(struct device_node *node,
+ const struct mtk_pll_data *plls, int num_plls,
+ struct mtk_pllfh_data *pllfhs, int num_pllfhs,
+ struct clk_hw_onecell_data *clk_data);
+
+void mtk_clk_unregister_pllfhs(const struct mtk_pll_data *plls, int num_plls,
+ struct mtk_pllfh_data *pllfhs, int num_fhs,
+ struct clk_hw_onecell_data *clk_data);
+
+void fhctl_parse_dt(const u8 *compatible_node, struct mtk_pllfh_data *pllfhs,
+ int num_pllfhs);
+
+#endif /* __DRV_CLKFH_H */
--
2.18.0
Use mtk_clk_register_pllfhs() to enhance frequency hopping and
spread spectrum clocking control for MT8186.
Co-developed-by: Edward-JW Yang <[email protected]>
Signed-off-by: Edward-JW Yang <[email protected]>
Signed-off-by: Johnson Wang <[email protected]>
---
drivers/clk/mediatek/Kconfig | 1 +
drivers/clk/mediatek/clk-mt8186-apmixedsys.c | 66 +++++++++++++++++++-
2 files changed, 64 insertions(+), 3 deletions(-)
diff --git a/drivers/clk/mediatek/Kconfig b/drivers/clk/mediatek/Kconfig
index 65b25082daf2..2de905aa6132 100644
--- a/drivers/clk/mediatek/Kconfig
+++ b/drivers/clk/mediatek/Kconfig
@@ -523,6 +523,7 @@ config COMMON_CLK_MT8186
bool "Clock driver for MediaTek MT8186"
depends on ARM64 || COMPILE_TEST
select COMMON_CLK_MEDIATEK
+ select COMMON_CLK_MEDIATEK_FHCTL
default ARCH_MEDIATEK
help
This driver supports MediaTek MT8186 clocks.
diff --git a/drivers/clk/mediatek/clk-mt8186-apmixedsys.c b/drivers/clk/mediatek/clk-mt8186-apmixedsys.c
index e692a2a67ce1..1d673c6278a9 100644
--- a/drivers/clk/mediatek/clk-mt8186-apmixedsys.c
+++ b/drivers/clk/mediatek/clk-mt8186-apmixedsys.c
@@ -9,6 +9,7 @@
#include "clk-mtk.h"
#include "clk-pll.h"
+#include "clk-pllfh.h"
#define MT8186_PLL_FMAX (3800UL * MHZ)
#define MT8186_PLL_FMIN (1500UL * MHZ)
@@ -76,6 +77,59 @@ static const struct mtk_pll_data plls[] = {
0, 0, 32, 0x034C, 24, 0x0044, 0x000C, 5, 0x0350),
};
+enum fh_pll_id {
+ FH_ARMPLL_LL,
+ FH_ARMPLL_BL,
+ FH_CCIPLL,
+ FH_MAINPLL,
+ FH_MMPLL,
+ FH_TVDPLL,
+ FH_RESERVE6,
+ FH_ADSPPLL,
+ FH_MFGPLL,
+ FH_NNAPLL,
+ FH_NNA2PLL,
+ FH_MSDCPLL,
+ FH_RESERVE12,
+ FH_NR_FH,
+};
+
+#define FH(_pllid, _fhid, _offset) { \
+ .data = { \
+ .pll_id = _pllid, \
+ .fh_id = _fhid, \
+ .fhx_offset = _offset, \
+ .dds_mask = GENMASK(21, 0), \
+ .slope0_value = 0x6003c97, \
+ .slope1_value = 0x6003c97, \
+ .sfstrx_en = BIT(2), \
+ .frddsx_en = BIT(1), \
+ .fhctlx_en = BIT(0), \
+ .tgl_org = BIT(31), \
+ .dvfs_tri = BIT(31), \
+ .pcwchg = BIT(31), \
+ .dt_val = 0x0, \
+ .df_val = 0x9, \
+ .updnlmt_shft = 16, \
+ .msk_frddsx_dys = GENMASK(23, 20), \
+ .msk_frddsx_dts = GENMASK(19, 16), \
+ }, \
+ }
+
+static struct mtk_pllfh_data pllfhs[] = {
+ FH(CLK_APMIXED_ARMPLL_LL, FH_ARMPLL_LL, 0x003C),
+ FH(CLK_APMIXED_ARMPLL_BL, FH_ARMPLL_BL, 0x0050),
+ FH(CLK_APMIXED_CCIPLL, FH_CCIPLL, 0x0064),
+ FH(CLK_APMIXED_MAINPLL, FH_MAINPLL, 0x0078),
+ FH(CLK_APMIXED_MMPLL, FH_MMPLL, 0x008C),
+ FH(CLK_APMIXED_TVDPLL, FH_TVDPLL, 0x00A0),
+ FH(CLK_APMIXED_ADSPPLL, FH_ADSPPLL, 0x00C8),
+ FH(CLK_APMIXED_MFGPLL, FH_MFGPLL, 0x00DC),
+ FH(CLK_APMIXED_NNAPLL, FH_NNAPLL, 0x00F0),
+ FH(CLK_APMIXED_NNA2PLL, FH_NNA2PLL, 0x0104),
+ FH(CLK_APMIXED_MSDCPLL, FH_MSDCPLL, 0x0118),
+};
+
static const struct of_device_id of_match_clk_mt8186_apmixed[] = {
{ .compatible = "mediatek,mt8186-apmixedsys", },
{}
@@ -85,13 +139,17 @@ static int clk_mt8186_apmixed_probe(struct platform_device *pdev)
{
struct clk_hw_onecell_data *clk_data;
struct device_node *node = pdev->dev.of_node;
+ const u8 *fhctl_node = "mediatek,mt8186-fhctl";
int r;
clk_data = mtk_alloc_clk_data(CLK_APMIXED_NR_CLK);
if (!clk_data)
return -ENOMEM;
- r = mtk_clk_register_plls(node, plls, ARRAY_SIZE(plls), clk_data);
+ fhctl_parse_dt(fhctl_node, pllfhs, ARRAY_SIZE(pllfhs));
+
+ r = mtk_clk_register_pllfhs(node, plls, ARRAY_SIZE(plls),
+ pllfhs, ARRAY_SIZE(pllfhs), clk_data);
if (r)
goto free_apmixed_data;
@@ -104,7 +162,8 @@ static int clk_mt8186_apmixed_probe(struct platform_device *pdev)
return r;
unregister_plls:
- mtk_clk_unregister_plls(plls, ARRAY_SIZE(plls), clk_data);
+ mtk_clk_unregister_pllfhs(plls, ARRAY_SIZE(plls), pllfhs,
+ ARRAY_SIZE(pllfhs), clk_data);
free_apmixed_data:
mtk_free_clk_data(clk_data);
return r;
@@ -116,7 +175,8 @@ static int clk_mt8186_apmixed_remove(struct platform_device *pdev)
struct clk_hw_onecell_data *clk_data = platform_get_drvdata(pdev);
of_clk_del_provider(node);
- mtk_clk_unregister_plls(plls, ARRAY_SIZE(plls), clk_data);
+ mtk_clk_unregister_pllfhs(plls, ARRAY_SIZE(plls), pllfhs,
+ ARRAY_SIZE(pllfhs), clk_data);
mtk_free_clk_data(clk_data);
return 0;
--
2.18.0
Export PLL operations and register functions for different type
of clock driver used.
Co-developed-by: Edward-JW Yang <[email protected]>
Signed-off-by: Edward-JW Yang <[email protected]>
Signed-off-by: Johnson Wang <[email protected]>
---
drivers/clk/mediatek/clk-pll.c | 84 ++++++++++++++--------------------
drivers/clk/mediatek/clk-pll.h | 56 +++++++++++++++++++++++
2 files changed, 90 insertions(+), 50 deletions(-)
diff --git a/drivers/clk/mediatek/clk-pll.c b/drivers/clk/mediatek/clk-pll.c
index 54e6cfd29dfc..a4eca5fd539c 100644
--- a/drivers/clk/mediatek/clk-pll.c
+++ b/drivers/clk/mediatek/clk-pll.c
@@ -27,37 +27,10 @@
#define AUDPLL_TUNER_EN BIT(31)
-#define POSTDIV_MASK 0x7
-
/* default 7 bits integer, can be overridden with pcwibits. */
#define INTEGER_BITS 7
-/*
- * MediaTek PLLs are configured through their pcw value. The pcw value describes
- * a divider in the PLL feedback loop which consists of 7 bits for the integer
- * part and the remaining bits (if present) for the fractional part. Also they
- * have a 3 bit power-of-two post divider.
- */
-
-struct mtk_clk_pll {
- struct clk_hw hw;
- void __iomem *base_addr;
- void __iomem *pd_addr;
- void __iomem *pwr_addr;
- void __iomem *tuner_addr;
- void __iomem *tuner_en_addr;
- void __iomem *pcw_addr;
- void __iomem *pcw_chg_addr;
- void __iomem *en_addr;
- const struct mtk_pll_data *data;
-};
-
-static inline struct mtk_clk_pll *to_mtk_clk_pll(struct clk_hw *hw)
-{
- return container_of(hw, struct mtk_clk_pll, hw);
-}
-
-static int mtk_pll_is_prepared(struct clk_hw *hw)
+int mtk_pll_is_prepared(struct clk_hw *hw)
{
struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
@@ -161,8 +134,8 @@ static void mtk_pll_set_rate_regs(struct mtk_clk_pll *pll, u32 pcw,
* @fin: The input frequency
*
*/
-static void mtk_pll_calc_values(struct mtk_clk_pll *pll, u32 *pcw, u32 *postdiv,
- u32 freq, u32 fin)
+void mtk_pll_calc_values(struct mtk_clk_pll *pll, u32 *pcw, u32 *postdiv,
+ u32 freq, u32 fin)
{
unsigned long fmin = pll->data->fmin ? pll->data->fmin : (1000 * MHZ);
const struct mtk_pll_div_table *div_table = pll->data->div_table;
@@ -198,8 +171,8 @@ static void mtk_pll_calc_values(struct mtk_clk_pll *pll, u32 *pcw, u32 *postdiv,
*pcw = (u32)_pcw;
}
-static int mtk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long parent_rate)
+int mtk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
{
struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
u32 pcw = 0;
@@ -211,8 +184,7 @@ static int mtk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
return 0;
}
-static unsigned long mtk_pll_recalc_rate(struct clk_hw *hw,
- unsigned long parent_rate)
+unsigned long mtk_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
{
struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
u32 postdiv;
@@ -227,8 +199,8 @@ static unsigned long mtk_pll_recalc_rate(struct clk_hw *hw,
return __mtk_pll_recalc_rate(pll, parent_rate, pcw, postdiv);
}
-static long mtk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
+long mtk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
{
struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
u32 pcw = 0;
@@ -239,7 +211,7 @@ static long mtk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
return __mtk_pll_recalc_rate(pll, *prate, pcw, postdiv);
}
-static int mtk_pll_prepare(struct clk_hw *hw)
+int mtk_pll_prepare(struct clk_hw *hw)
{
struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
u32 r;
@@ -273,7 +245,7 @@ static int mtk_pll_prepare(struct clk_hw *hw)
return 0;
}
-static void mtk_pll_unprepare(struct clk_hw *hw)
+void mtk_pll_unprepare(struct clk_hw *hw)
{
struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
u32 r;
@@ -301,7 +273,7 @@ static void mtk_pll_unprepare(struct clk_hw *hw)
writel(r, pll->pwr_addr);
}
-static const struct clk_ops mtk_pll_ops = {
+const struct clk_ops mtk_pll_ops = {
.is_prepared = mtk_pll_is_prepared,
.prepare = mtk_pll_prepare,
.unprepare = mtk_pll_unprepare,
@@ -310,18 +282,15 @@ static const struct clk_ops mtk_pll_ops = {
.set_rate = mtk_pll_set_rate,
};
-static struct clk_hw *mtk_clk_register_pll(const struct mtk_pll_data *data,
- void __iomem *base)
+struct clk_hw *mtk_clk_register_pll_ops(struct mtk_clk_pll *pll,
+ const struct mtk_pll_data *data,
+ void __iomem *base,
+ const struct clk_ops *pll_ops)
{
- struct mtk_clk_pll *pll;
struct clk_init_data init = {};
int ret;
const char *parent_name = "clk26m";
- pll = kzalloc(sizeof(*pll), GFP_KERNEL);
- if (!pll)
- return ERR_PTR(-ENOMEM);
-
pll->base_addr = base + data->reg;
pll->pwr_addr = base + data->pwr_reg;
pll->pd_addr = base + data->pd_reg;
@@ -343,7 +312,7 @@ static struct clk_hw *mtk_clk_register_pll(const struct mtk_pll_data *data,
init.name = data->name;
init.flags = (data->flags & PLL_AO) ? CLK_IS_CRITICAL : 0;
- init.ops = &mtk_pll_ops;
+ init.ops = pll_ops;
if (data->parent_name)
init.parent_names = &data->parent_name;
else
@@ -360,7 +329,22 @@ static struct clk_hw *mtk_clk_register_pll(const struct mtk_pll_data *data,
return &pll->hw;
}
-static void mtk_clk_unregister_pll(struct clk_hw *hw)
+struct clk_hw *mtk_clk_register_pll(const struct mtk_pll_data *data,
+ void __iomem *base)
+{
+ struct mtk_clk_pll *pll;
+ struct clk_hw *hw;
+
+ pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+ if (!pll)
+ return ERR_PTR(-ENOMEM);
+
+ hw = mtk_clk_register_pll_ops(pll, data, base, &mtk_pll_ops);
+
+ return hw;
+}
+
+void mtk_clk_unregister_pll(struct clk_hw *hw)
{
struct mtk_clk_pll *pll;
@@ -423,8 +407,8 @@ int mtk_clk_register_plls(struct device_node *node,
}
EXPORT_SYMBOL_GPL(mtk_clk_register_plls);
-static __iomem void *mtk_clk_pll_get_base(struct clk_hw *hw,
- const struct mtk_pll_data *data)
+__iomem void *mtk_clk_pll_get_base(struct clk_hw *hw,
+ const struct mtk_pll_data *data)
{
struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
diff --git a/drivers/clk/mediatek/clk-pll.h b/drivers/clk/mediatek/clk-pll.h
index fe3199715688..e87ab08eea9b 100644
--- a/drivers/clk/mediatek/clk-pll.h
+++ b/drivers/clk/mediatek/clk-pll.h
@@ -7,6 +7,7 @@
#ifndef __DRV_CLK_MTK_PLL_H
#define __DRV_CLK_MTK_PLL_H
+#include <linux/clk-provider.h>
#include <linux/types.h>
struct clk_ops;
@@ -20,6 +21,7 @@ struct mtk_pll_div_table {
#define HAVE_RST_BAR BIT(0)
#define PLL_AO BIT(1)
+#define POSTDIV_MASK 0x7
struct mtk_pll_data {
int id;
@@ -48,10 +50,64 @@ struct mtk_pll_data {
u8 pll_en_bit; /* Assume 0, indicates BIT(0) by default */
};
+/*
+ * MediaTek PLLs are configured through their pcw value. The pcw value describes
+ * a divider in the PLL feedback loop which consists of 7 bits for the integer
+ * part and the remaining bits (if present) for the fractional part. Also they
+ * have a 3 bit power-of-two post divider.
+ */
+
+struct mtk_clk_pll {
+ struct clk_hw hw;
+ void __iomem *base_addr;
+ void __iomem *pd_addr;
+ void __iomem *pwr_addr;
+ void __iomem *tuner_addr;
+ void __iomem *tuner_en_addr;
+ void __iomem *pcw_addr;
+ void __iomem *pcw_chg_addr;
+ void __iomem *en_addr;
+ const struct mtk_pll_data *data;
+};
+
+
int mtk_clk_register_plls(struct device_node *node,
const struct mtk_pll_data *plls, int num_plls,
struct clk_hw_onecell_data *clk_data);
void mtk_clk_unregister_plls(const struct mtk_pll_data *plls, int num_plls,
struct clk_hw_onecell_data *clk_data);
+extern const struct clk_ops mtk_pll_ops;
+
+static inline struct mtk_clk_pll *to_mtk_clk_pll(struct clk_hw *hw)
+{
+ return container_of(hw, struct mtk_clk_pll, hw);
+}
+
+int mtk_pll_is_prepared(struct clk_hw *hw);
+
+int mtk_pll_prepare(struct clk_hw *hw);
+
+void mtk_pll_unprepare(struct clk_hw *hw);
+
+unsigned long mtk_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate);
+
+void mtk_pll_calc_values(struct mtk_clk_pll *pll, u32 *pcw, u32 *postdiv,
+ u32 freq, u32 fin);
+int mtk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate);
+long mtk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate);
+
+struct clk_hw *mtk_clk_register_pll_ops(struct mtk_clk_pll *pll,
+ const struct mtk_pll_data *data,
+ void __iomem *base,
+ const struct clk_ops *pll_ops);
+struct clk_hw *mtk_clk_register_pll(const struct mtk_pll_data *data,
+ void __iomem *base);
+void mtk_clk_unregister_pll(struct clk_hw *hw);
+
+__iomem void *mtk_clk_pll_get_base(struct clk_hw *hw,
+ const struct mtk_pll_data *data);
+
#endif /* __DRV_CLK_MTK_PLL_H */
--
2.18.0
Il 29/09/22 13:46, Johnson Wang ha scritto:
> Export PLL operations and register functions for different type
> of clock driver used.
>
> Co-developed-by: Edward-JW Yang <[email protected]>
> Signed-off-by: Edward-JW Yang <[email protected]>
> Signed-off-by: Johnson Wang <[email protected]>
> ---
> drivers/clk/mediatek/clk-pll.c | 84 ++++++++++++++--------------------
> drivers/clk/mediatek/clk-pll.h | 56 +++++++++++++++++++++++
> 2 files changed, 90 insertions(+), 50 deletions(-)
>
> diff --git a/drivers/clk/mediatek/clk-pll.c b/drivers/clk/mediatek/clk-pll.c
> index 54e6cfd29dfc..a4eca5fd539c 100644
> --- a/drivers/clk/mediatek/clk-pll.c
> +++ b/drivers/clk/mediatek/clk-pll.c
> @@ -27,37 +27,10 @@
>
> #define AUDPLL_TUNER_EN BIT(31)
>
> -#define POSTDIV_MASK 0x7
> -
> /* default 7 bits integer, can be overridden with pcwibits. */
> #define INTEGER_BITS 7
>
> -/*
> - * MediaTek PLLs are configured through their pcw value. The pcw value describes
> - * a divider in the PLL feedback loop which consists of 7 bits for the integer
> - * part and the remaining bits (if present) for the fractional part. Also they
> - * have a 3 bit power-of-two post divider.
> - */
> -
> -struct mtk_clk_pll {
> - struct clk_hw hw;
> - void __iomem *base_addr;
> - void __iomem *pd_addr;
> - void __iomem *pwr_addr;
> - void __iomem *tuner_addr;
> - void __iomem *tuner_en_addr;
> - void __iomem *pcw_addr;
> - void __iomem *pcw_chg_addr;
> - void __iomem *en_addr;
> - const struct mtk_pll_data *data;
> -};
> -
> -static inline struct mtk_clk_pll *to_mtk_clk_pll(struct clk_hw *hw)
> -{
> - return container_of(hw, struct mtk_clk_pll, hw);
> -}
> -
> -static int mtk_pll_is_prepared(struct clk_hw *hw)
> +int mtk_pll_is_prepared(struct clk_hw *hw)
> {
> struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
>
> @@ -161,8 +134,8 @@ static void mtk_pll_set_rate_regs(struct mtk_clk_pll *pll, u32 pcw,
> * @fin: The input frequency
> *
> */
> -static void mtk_pll_calc_values(struct mtk_clk_pll *pll, u32 *pcw, u32 *postdiv,
> - u32 freq, u32 fin)
> +void mtk_pll_calc_values(struct mtk_clk_pll *pll, u32 *pcw, u32 *postdiv,
> + u32 freq, u32 fin)
> {
> unsigned long fmin = pll->data->fmin ? pll->data->fmin : (1000 * MHZ);
> const struct mtk_pll_div_table *div_table = pll->data->div_table;
> @@ -198,8 +171,8 @@ static void mtk_pll_calc_values(struct mtk_clk_pll *pll, u32 *pcw, u32 *postdiv,
> *pcw = (u32)_pcw;
> }
>
> -static int mtk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
> - unsigned long parent_rate)
> +int mtk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
> + unsigned long parent_rate)
> {
> struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
> u32 pcw = 0;
> @@ -211,8 +184,7 @@ static int mtk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
> return 0;
> }
>
> -static unsigned long mtk_pll_recalc_rate(struct clk_hw *hw,
> - unsigned long parent_rate)
> +unsigned long mtk_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
> {
> struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
> u32 postdiv;
> @@ -227,8 +199,8 @@ static unsigned long mtk_pll_recalc_rate(struct clk_hw *hw,
> return __mtk_pll_recalc_rate(pll, parent_rate, pcw, postdiv);
> }
>
> -static long mtk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
> - unsigned long *prate)
> +long mtk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
> + unsigned long *prate)
> {
> struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
> u32 pcw = 0;
> @@ -239,7 +211,7 @@ static long mtk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
> return __mtk_pll_recalc_rate(pll, *prate, pcw, postdiv);
> }
>
> -static int mtk_pll_prepare(struct clk_hw *hw)
> +int mtk_pll_prepare(struct clk_hw *hw)
> {
> struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
> u32 r;
> @@ -273,7 +245,7 @@ static int mtk_pll_prepare(struct clk_hw *hw)
> return 0;
> }
>
> -static void mtk_pll_unprepare(struct clk_hw *hw)
> +void mtk_pll_unprepare(struct clk_hw *hw)
> {
> struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
> u32 r;
> @@ -301,7 +273,7 @@ static void mtk_pll_unprepare(struct clk_hw *hw)
> writel(r, pll->pwr_addr);
> }
>
> -static const struct clk_ops mtk_pll_ops = {
> +const struct clk_ops mtk_pll_ops = {
> .is_prepared = mtk_pll_is_prepared,
> .prepare = mtk_pll_prepare,
> .unprepare = mtk_pll_unprepare,
> @@ -310,18 +282,15 @@ static const struct clk_ops mtk_pll_ops = {
> .set_rate = mtk_pll_set_rate,
> };
>
> -static struct clk_hw *mtk_clk_register_pll(const struct mtk_pll_data *data,
> - void __iomem *base)
> +struct clk_hw *mtk_clk_register_pll_ops(struct mtk_clk_pll *pll,
> + const struct mtk_pll_data *data,
> + void __iomem *base,
> + const struct clk_ops *pll_ops)
> {
> - struct mtk_clk_pll *pll;
> struct clk_init_data init = {};
> int ret;
> const char *parent_name = "clk26m";
>
> - pll = kzalloc(sizeof(*pll), GFP_KERNEL);
> - if (!pll)
> - return ERR_PTR(-ENOMEM);
> -
> pll->base_addr = base + data->reg;
> pll->pwr_addr = base + data->pwr_reg;
> pll->pd_addr = base + data->pd_reg;
> @@ -343,7 +312,7 @@ static struct clk_hw *mtk_clk_register_pll(const struct mtk_pll_data *data,
>
> init.name = data->name;
> init.flags = (data->flags & PLL_AO) ? CLK_IS_CRITICAL : 0;
> - init.ops = &mtk_pll_ops;
> + init.ops = pll_ops;
> if (data->parent_name)
> init.parent_names = &data->parent_name;
> else
> @@ -360,7 +329,22 @@ static struct clk_hw *mtk_clk_register_pll(const struct mtk_pll_data *data,
> return &pll->hw;
> }
>
> -static void mtk_clk_unregister_pll(struct clk_hw *hw)
> +struct clk_hw *mtk_clk_register_pll(const struct mtk_pll_data *data,
> + void __iomem *base)
> +{
> + struct mtk_clk_pll *pll;
> + struct clk_hw *hw;
> +
> + pll = kzalloc(sizeof(*pll), GFP_KERNEL);
> + if (!pll)
> + return ERR_PTR(-ENOMEM);
> +
> + hw = mtk_clk_register_pll_ops(pll, data, base, &mtk_pll_ops);
> +
> + return hw;
> +}
> +
> +void mtk_clk_unregister_pll(struct clk_hw *hw)
> {
> struct mtk_clk_pll *pll;
>
> @@ -423,8 +407,8 @@ int mtk_clk_register_plls(struct device_node *node,
> }
> EXPORT_SYMBOL_GPL(mtk_clk_register_plls);
>
> -static __iomem void *mtk_clk_pll_get_base(struct clk_hw *hw,
> - const struct mtk_pll_data *data)
> +__iomem void *mtk_clk_pll_get_base(struct clk_hw *hw,
> + const struct mtk_pll_data *data)
> {
> struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
>
> diff --git a/drivers/clk/mediatek/clk-pll.h b/drivers/clk/mediatek/clk-pll.h
> index fe3199715688..e87ab08eea9b 100644
> --- a/drivers/clk/mediatek/clk-pll.h
> +++ b/drivers/clk/mediatek/clk-pll.h
> @@ -7,6 +7,7 @@
> #ifndef __DRV_CLK_MTK_PLL_H
> #define __DRV_CLK_MTK_PLL_H
>
> +#include <linux/clk-provider.h>
> #include <linux/types.h>
>
> struct clk_ops;
> @@ -20,6 +21,7 @@ struct mtk_pll_div_table {
>
> #define HAVE_RST_BAR BIT(0)
> #define PLL_AO BIT(1)
> +#define POSTDIV_MASK 0x7
While moving this, can you please also fixup declaring this mask as GENMASK()?
#define POSTDIV_MASK GENMASK(2, 0)
>
> struct mtk_pll_data {
> int id;
> @@ -48,10 +50,64 @@ struct mtk_pll_data {
> u8 pll_en_bit; /* Assume 0, indicates BIT(0) by default */
> };
>
> +/*
> + * MediaTek PLLs are configured through their pcw value. The pcw value describes
> + * a divider in the PLL feedback loop which consists of 7 bits for the integer
> + * part and the remaining bits (if present) for the fractional part. Also they
> + * have a 3 bit power-of-two post divider.
> + */
> +
> +struct mtk_clk_pll {
> + struct clk_hw hw;
> + void __iomem *base_addr;
> + void __iomem *pd_addr;
> + void __iomem *pwr_addr;
> + void __iomem *tuner_addr;
> + void __iomem *tuner_en_addr;
> + void __iomem *pcw_addr;
> + void __iomem *pcw_chg_addr;
> + void __iomem *en_addr;
> + const struct mtk_pll_data *data;
> +};
> +
> +
Please drop this extra newline.
Thanks,
Angelo
Il 29/09/22 13:46, Johnson Wang ha scritto:
> Add the new binding documentation for MediaTek frequency hopping
> and spread spectrum clocking control.
>
> Co-developed-by: Edward-JW Yang <[email protected]>
> Signed-off-by: Edward-JW Yang <[email protected]>
> Signed-off-by: Johnson Wang <[email protected]>
> ---
> .../arm/mediatek/mediatek,mt8186-fhctl.yaml | 53 +++++++++++++++++++
> 1 file changed, 53 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,mt8186-fhctl.yaml
>
> diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,mt8186-fhctl.yaml b/Documentation/devicetree/bindings/arm/mediatek/mediatek,mt8186-fhctl.yaml
> new file mode 100644
> index 000000000000..258dff7ce6bc
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,mt8186-fhctl.yaml
> @@ -0,0 +1,53 @@
> +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/arm/mediatek/mediatek,mt8186-fhctl.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: MediaTek frequency hopping and spread spectrum clocking control
> +
> +maintainers:
> + - Edward-JW Yang <[email protected]>
> +
> +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 hardware.
> +
> +properties:
> + compatible:
> + const: mediatek,mt8186-fhctl
> +
> + reg:
> + maxItems: 1
> +
> + clocks:
> + description: Phandles of the PLL with FHCTL hardware capability.
> + minItems: 1
> + maxItems: 30
> +
> + mediatek,hopping-ssc-percent:
> + description: The percentage of spread spectrum clocking for one PLL.
> + minItems: 1
> + maxItems: 30
> + items:
> + default: 0
> + minimum: 0
> + maximum: 8
> +
> +required:
> + - compatible
> + - reg
> + - clocks
> +
> +additionalProperties: false
> +
> +examples:
> + - |
> + #include <dt-bindings/clock/mt8186-clk.h>
> + fhctl: fhctl@1000ce00 {
> + compatible = "mediatek,mt8186-fhctl";
> + reg = <0x1000c000 0xe00>;
> + clocks = <&apmixedsys CLK_APMIXED_MSDCPLL>;
> + mediatek,hopping-ssc-percent = <3>;
> + };
.... a more realistic example:
#include <dt-bindings/clock/mt8186-clk.h>
soc {
#address-cells = <2>;
#size-cells = <2>;
fhctl: fhctl@1000ce00 {
compatible = "mediatek,mt8186-fhctl";
reg = <0 0x1000c000 0 0xe00>;
clocks = <&apmixedsys CLK_APMIXED_MSDCPLL>;
mediatek,hopping-ssc-percent = <3>;
};
};
After which...
Reviewed-by: AngeloGioacchino Del Regno <[email protected]>
On Thu, Sep 29, 2022 at 03:07:49PM +0200, AngeloGioacchino Del Regno wrote:
> Il 29/09/22 13:46, Johnson Wang ha scritto:
> > Add the new binding documentation for MediaTek frequency hopping
> > and spread spectrum clocking control.
> >
> > Co-developed-by: Edward-JW Yang <[email protected]>
> > Signed-off-by: Edward-JW Yang <[email protected]>
> > Signed-off-by: Johnson Wang <[email protected]>
> > ---
> > .../arm/mediatek/mediatek,mt8186-fhctl.yaml | 53 +++++++++++++++++++
> > 1 file changed, 53 insertions(+)
> > create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,mt8186-fhctl.yaml
> >
> > diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,mt8186-fhctl.yaml b/Documentation/devicetree/bindings/arm/mediatek/mediatek,mt8186-fhctl.yaml
> > new file mode 100644
> > index 000000000000..258dff7ce6bc
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,mt8186-fhctl.yaml
> > @@ -0,0 +1,53 @@
> > +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
> > +%YAML 1.2
> > +---
> > +$id: http://devicetree.org/schemas/arm/mediatek/mediatek,mt8186-fhctl.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +title: MediaTek frequency hopping and spread spectrum clocking control
> > +
> > +maintainers:
> > + - Edward-JW Yang <[email protected]>
> > +
> > +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 hardware.
> > +
> > +properties:
> > + compatible:
> > + const: mediatek,mt8186-fhctl
> > +
> > + reg:
> > + maxItems: 1
> > +
> > + clocks:
> > + description: Phandles of the PLL with FHCTL hardware capability.
> > + minItems: 1
> > + maxItems: 30
> > +
> > + mediatek,hopping-ssc-percent:
> > + description: The percentage of spread spectrum clocking for one PLL.
> > + minItems: 1
> > + maxItems: 30
> > + items:
> > + default: 0
> > + minimum: 0
> > + maximum: 8
> > +
> > +required:
> > + - compatible
> > + - reg
> > + - clocks
> > +
> > +additionalProperties: false
> > +
> > +examples:
> > + - |
> > + #include <dt-bindings/clock/mt8186-clk.h>
> > + fhctl: fhctl@1000ce00 {
> > + compatible = "mediatek,mt8186-fhctl";
> > + reg = <0x1000c000 0xe00>;
> > + clocks = <&apmixedsys CLK_APMIXED_MSDCPLL>;
> > + mediatek,hopping-ssc-percent = <3>;
> > + };
>
>
> .... a more realistic example:
One or 2 cells is really outside the scope of the binding as either is
valid. If the parent bus only has 32-bits of address space, there's
little reason to use 2 cells.
>
> #include <dt-bindings/clock/mt8186-clk.h>
>
> soc {
> #address-cells = <2>;
> #size-cells = <2>;
>
> fhctl: fhctl@1000ce00 {
> compatible = "mediatek,mt8186-fhctl";
> reg = <0 0x1000c000 0 0xe00>;
> clocks = <&apmixedsys CLK_APMIXED_MSDCPLL>;
> mediatek,hopping-ssc-percent = <3>;
> };
> };
>
> After which...
>
> Reviewed-by: AngeloGioacchino Del Regno <[email protected]>
>
On Thu, 29 Sep 2022 19:46:22 +0800, Johnson Wang wrote:
> Add the new binding documentation for MediaTek frequency hopping
> and spread spectrum clocking control.
>
> Co-developed-by: Edward-JW Yang <[email protected]>
> Signed-off-by: Edward-JW Yang <[email protected]>
> Signed-off-by: Johnson Wang <[email protected]>
> ---
> .../arm/mediatek/mediatek,mt8186-fhctl.yaml | 53 +++++++++++++++++++
> 1 file changed, 53 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,mt8186-fhctl.yaml
>
Reviewed-by: Rob Herring <[email protected]>
On Thu, 2022-09-29 at 15:07 +0200, AngeloGioacchino Del Regno wrote:
> Il 29/09/22 13:46, Johnson Wang ha scritto:
> > Export PLL operations and register functions for different type
> > of clock driver used.
> >
> > Co-developed-by: Edward-JW Yang <[email protected]>
> > Signed-off-by: Edward-JW Yang <[email protected]>
> > Signed-off-by: Johnson Wang <[email protected]>
> > ---
> > drivers/clk/mediatek/clk-pll.c | 84 ++++++++++++++---------------
> > -----
> > drivers/clk/mediatek/clk-pll.h | 56 +++++++++++++++++++++++
> > 2 files changed, 90 insertions(+), 50 deletions(-)
> >
> > diff --git a/drivers/clk/mediatek/clk-pll.c
> > b/drivers/clk/mediatek/clk-pll.c
> > index 54e6cfd29dfc..a4eca5fd539c 100644
> > --- a/drivers/clk/mediatek/clk-pll.c
> > +++ b/drivers/clk/mediatek/clk-pll.c
> > @@ -27,37 +27,10 @@
> >
> > #define AUDPLL_TUNER_EN BIT(31)
> >
> > -#define POSTDIV_MASK 0x7
> > -
> > /* default 7 bits integer, can be overridden with pcwibits. */
> > #define INTEGER_BITS 7
> >
> > -/*
> > - * MediaTek PLLs are configured through their pcw value. The pcw
> > value describes
> > - * a divider in the PLL feedback loop which consists of 7 bits for
> > the integer
> > - * part and the remaining bits (if present) for the fractional
> > part. Also they
> > - * have a 3 bit power-of-two post divider.
> > - */
> > -
> > -struct mtk_clk_pll {
> > - struct clk_hw hw;
> > - void __iomem *base_addr;
> > - void __iomem *pd_addr;
> > - void __iomem *pwr_addr;
> > - void __iomem *tuner_addr;
> > - void __iomem *tuner_en_addr;
> > - void __iomem *pcw_addr;
> > - void __iomem *pcw_chg_addr;
> > - void __iomem *en_addr;
> > - const struct mtk_pll_data *data;
> > -};
> > -
> > -static inline struct mtk_clk_pll *to_mtk_clk_pll(struct clk_hw
> > *hw)
> > -{
> > - return container_of(hw, struct mtk_clk_pll, hw);
> > -}
> > -
> > -static int mtk_pll_is_prepared(struct clk_hw *hw)
> > +int mtk_pll_is_prepared(struct clk_hw *hw)
> > {
> > struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
> >
> > @@ -161,8 +134,8 @@ static void mtk_pll_set_rate_regs(struct
> > mtk_clk_pll *pll, u32 pcw,
> > * @fin: The input frequency
> > *
> > */
> > -static void mtk_pll_calc_values(struct mtk_clk_pll *pll, u32 *pcw,
> > u32 *postdiv,
> > - u32 freq, u32 fin)
> > +void mtk_pll_calc_values(struct mtk_clk_pll *pll, u32 *pcw, u32
> > *postdiv,
> > + u32 freq, u32 fin)
> > {
> > unsigned long fmin = pll->data->fmin ? pll->data->fmin : (1000
> > * MHZ);
> > const struct mtk_pll_div_table *div_table = pll->data-
> > >div_table;
> > @@ -198,8 +171,8 @@ static void mtk_pll_calc_values(struct
> > mtk_clk_pll *pll, u32 *pcw, u32 *postdiv,
> > *pcw = (u32)_pcw;
> > }
> >
> > -static int mtk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
> > - unsigned long parent_rate)
> > +int mtk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
> > + unsigned long parent_rate)
> > {
> > struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
> > u32 pcw = 0;
> > @@ -211,8 +184,7 @@ static int mtk_pll_set_rate(struct clk_hw *hw,
> > unsigned long rate,
> > return 0;
> > }
> >
> > -static unsigned long mtk_pll_recalc_rate(struct clk_hw *hw,
> > - unsigned long parent_rate)
> > +unsigned long mtk_pll_recalc_rate(struct clk_hw *hw, unsigned long
> > parent_rate)
> > {
> > struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
> > u32 postdiv;
> > @@ -227,8 +199,8 @@ static unsigned long mtk_pll_recalc_rate(struct
> > clk_hw *hw,
> > return __mtk_pll_recalc_rate(pll, parent_rate, pcw, postdiv);
> > }
> >
> > -static long mtk_pll_round_rate(struct clk_hw *hw, unsigned long
> > rate,
> > - unsigned long *prate)
> > +long mtk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
> > + unsigned long *prate)
> > {
> > struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
> > u32 pcw = 0;
> > @@ -239,7 +211,7 @@ static long mtk_pll_round_rate(struct clk_hw
> > *hw, unsigned long rate,
> > return __mtk_pll_recalc_rate(pll, *prate, pcw, postdiv);
> > }
> >
> > -static int mtk_pll_prepare(struct clk_hw *hw)
> > +int mtk_pll_prepare(struct clk_hw *hw)
> > {
> > struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
> > u32 r;
> > @@ -273,7 +245,7 @@ static int mtk_pll_prepare(struct clk_hw *hw)
> > return 0;
> > }
> >
> > -static void mtk_pll_unprepare(struct clk_hw *hw)
> > +void mtk_pll_unprepare(struct clk_hw *hw)
> > {
> > struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
> > u32 r;
> > @@ -301,7 +273,7 @@ static void mtk_pll_unprepare(struct clk_hw
> > *hw)
> > writel(r, pll->pwr_addr);
> > }
> >
> > -static const struct clk_ops mtk_pll_ops = {
> > +const struct clk_ops mtk_pll_ops = {
> > .is_prepared = mtk_pll_is_prepared,
> > .prepare = mtk_pll_prepare,
> > .unprepare = mtk_pll_unprepare,
> > @@ -310,18 +282,15 @@ static const struct clk_ops mtk_pll_ops = {
> > .set_rate = mtk_pll_set_rate,
> > };
> >
> > -static struct clk_hw *mtk_clk_register_pll(const struct
> > mtk_pll_data *data,
> > - void __iomem *base)
> > +struct clk_hw *mtk_clk_register_pll_ops(struct mtk_clk_pll *pll,
> > + const struct mtk_pll_data
> > *data,
> > + void __iomem *base,
> > + const struct clk_ops *pll_ops)
> > {
> > - struct mtk_clk_pll *pll;
> > struct clk_init_data init = {};
> > int ret;
> > const char *parent_name = "clk26m";
> >
> > - pll = kzalloc(sizeof(*pll), GFP_KERNEL);
> > - if (!pll)
> > - return ERR_PTR(-ENOMEM);
> > -
> > pll->base_addr = base + data->reg;
> > pll->pwr_addr = base + data->pwr_reg;
> > pll->pd_addr = base + data->pd_reg;
> > @@ -343,7 +312,7 @@ static struct clk_hw
> > *mtk_clk_register_pll(const struct mtk_pll_data *data,
> >
> > init.name = data->name;
> > init.flags = (data->flags & PLL_AO) ? CLK_IS_CRITICAL : 0;
> > - init.ops = &mtk_pll_ops;
> > + init.ops = pll_ops;
> > if (data->parent_name)
> > init.parent_names = &data->parent_name;
> > else
> > @@ -360,7 +329,22 @@ static struct clk_hw
> > *mtk_clk_register_pll(const struct mtk_pll_data *data,
> > return &pll->hw;
> > }
> >
> > -static void mtk_clk_unregister_pll(struct clk_hw *hw)
> > +struct clk_hw *mtk_clk_register_pll(const struct mtk_pll_data
> > *data,
> > + void __iomem *base)
> > +{
> > + struct mtk_clk_pll *pll;
> > + struct clk_hw *hw;
> > +
> > + pll = kzalloc(sizeof(*pll), GFP_KERNEL);
> > + if (!pll)
> > + return ERR_PTR(-ENOMEM);
> > +
> > + hw = mtk_clk_register_pll_ops(pll, data, base, &mtk_pll_ops);
> > +
> > + return hw;
> > +}
> > +
> > +void mtk_clk_unregister_pll(struct clk_hw *hw)
> > {
> > struct mtk_clk_pll *pll;
> >
> > @@ -423,8 +407,8 @@ int mtk_clk_register_plls(struct device_node
> > *node,
> > }
> > EXPORT_SYMBOL_GPL(mtk_clk_register_plls);
> >
> > -static __iomem void *mtk_clk_pll_get_base(struct clk_hw *hw,
> > - const struct mtk_pll_data
> > *data)
> > +__iomem void *mtk_clk_pll_get_base(struct clk_hw *hw,
> > + const struct mtk_pll_data *data)
> > {
> > struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
> >
> > diff --git a/drivers/clk/mediatek/clk-pll.h
> > b/drivers/clk/mediatek/clk-pll.h
> > index fe3199715688..e87ab08eea9b 100644
> > --- a/drivers/clk/mediatek/clk-pll.h
> > +++ b/drivers/clk/mediatek/clk-pll.h
> > @@ -7,6 +7,7 @@
> > #ifndef __DRV_CLK_MTK_PLL_H
> > #define __DRV_CLK_MTK_PLL_H
> >
> > +#include <linux/clk-provider.h>
> > #include <linux/types.h>
> >
> > struct clk_ops;
> > @@ -20,6 +21,7 @@ struct mtk_pll_div_table {
> >
> > #define HAVE_RST_BAR BIT(0)
> > #define PLL_AO BIT(1)
> > +#define POSTDIV_MASK 0x7
>
> While moving this, can you please also fixup declaring this mask as
> GENMASK()?
>
> #define POSTDIV_MASK GENMASK(2, 0)
>
> >
> > struct mtk_pll_data {
> > int id;
> > @@ -48,10 +50,64 @@ struct mtk_pll_data {
> > u8 pll_en_bit; /* Assume 0, indicates BIT(0) by default */
> > };
> >
> > +/*
> > + * MediaTek PLLs are configured through their pcw value. The pcw
> > value describes
> > + * a divider in the PLL feedback loop which consists of 7 bits for
> > the integer
> > + * part and the remaining bits (if present) for the fractional
> > part. Also they
> > + * have a 3 bit power-of-two post divider.
> > + */
> > +
> > +struct mtk_clk_pll {
> > + struct clk_hw hw;
> > + void __iomem *base_addr;
> > + void __iomem *pd_addr;
> > + void __iomem *pwr_addr;
> > + void __iomem *tuner_addr;
> > + void __iomem *tuner_en_addr;
> > + void __iomem *pcw_addr;
> > + void __iomem *pcw_chg_addr;
> > + void __iomem *en_addr;
> > + const struct mtk_pll_data *data;
> > +};
> > +
> > +
>
> Please drop this extra newline.
>
> Thanks,
> Angelo
Hi Angelo,
Thanks for your suggestion.
I will improve this in the next version.
BRs,
Johnson Wang
On Fri, 2022-09-30 at 14:07 -0500, Rob Herring wrote:
> On Thu, Sep 29, 2022 at 03:07:49PM +0200, AngeloGioacchino Del Regno
> wrote:
> > Il 29/09/22 13:46, Johnson Wang ha scritto:
> > > Add the new binding documentation for MediaTek frequency hopping
> > > and spread spectrum clocking control.
> > >
> > > Co-developed-by: Edward-JW Yang <[email protected]>
> > > Signed-off-by: Edward-JW Yang <[email protected]>
> > > Signed-off-by: Johnson Wang <[email protected]>
> > > ---
> > > .../arm/mediatek/mediatek,mt8186-fhctl.yaml | 53
> > > +++++++++++++++++++
> > > 1 file changed, 53 insertions(+)
> > > create mode 100644
> > > Documentation/devicetree/bindings/arm/mediatek/mediatek,mt8186-
> > > fhctl.yaml
> > >
> > > diff --git
> > > a/Documentation/devicetree/bindings/arm/mediatek/mediatek,mt8186-
> > > fhctl.yaml
> > > b/Documentation/devicetree/bindings/arm/mediatek/mediatek,mt8186-
> > > fhctl.yaml
> > > new file mode 100644
> > > index 000000000000..258dff7ce6bc
> > > --- /dev/null
> > > +++
> > > b/Documentation/devicetree/bindings/arm/mediatek/mediatek,mt8186-
> > > fhctl.yaml
> > > @@ -0,0 +1,53 @@
> > > +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
> > > +%YAML 1.2
> > > +---
> > > +$id:
> > > https://urldefense.com/v3/__http://devicetree.org/schemas/arm/mediatek/mediatek,mt8186-fhctl.yaml*__;Iw!!CTRNKA9wMg0ARbw!xVt73f8PaQKph8n0ffGHHmQIxdzooUtGr0xDNVqy_6V0vwcs5k-FLrmkB3PZupSu1ciC$
> > >
> > > +$schema:
> > > https://urldefense.com/v3/__http://devicetree.org/meta-schemas/core.yaml*__;Iw!!CTRNKA9wMg0ARbw!xVt73f8PaQKph8n0ffGHHmQIxdzooUtGr0xDNVqy_6V0vwcs5k-FLrmkB3PZulXwBACk$
> > >
> > > +
> > > +title: MediaTek frequency hopping and spread spectrum clocking
> > > control
> > > +
> > > +maintainers:
> > > + - Edward-JW Yang <[email protected]>
> > > +
> > > +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 hardware.
> > > +
> > > +properties:
> > > + compatible:
> > > + const: mediatek,mt8186-fhctl
> > > +
> > > + reg:
> > > + maxItems: 1
> > > +
> > > + clocks:
> > > + description: Phandles of the PLL with FHCTL hardware
> > > capability.
> > > + minItems: 1
> > > + maxItems: 30
> > > +
> > > + mediatek,hopping-ssc-percent:
> > > + description: The percentage of spread spectrum clocking for
> > > one PLL.
> > > + minItems: 1
> > > + maxItems: 30
> > > + items:
> > > + default: 0
> > > + minimum: 0
> > > + maximum: 8
> > > +
> > > +required:
> > > + - compatible
> > > + - reg
> > > + - clocks
> > > +
> > > +additionalProperties: false
> > > +
> > > +examples:
> > > + - |
> > > + #include <dt-bindings/clock/mt8186-clk.h>
> > > + fhctl: fhctl@1000ce00 {
> > > + compatible = "mediatek,mt8186-fhctl";
> > > + reg = <0x1000c000 0xe00>;
> > > + clocks = <&apmixedsys CLK_APMIXED_MSDCPLL>;
> > > + mediatek,hopping-ssc-percent = <3>;
> > > + };
> >
> >
> > .... a more realistic example:
>
> One or 2 cells is really outside the scope of the binding as either
> is
> valid. If the parent bus only has 32-bits of address space, there's
> little reason to use 2 cells.
>
Hi Rob,
Thanks for the explanation.
Then I will keep this example the same in the next version.
BRs,
Johnson Wang
> >
> > #include <dt-bindings/clock/mt8186-clk.h>
> >
> > soc {
> > #address-cells = <2>;
> > #size-cells = <2>;
> >
> > fhctl: fhctl@1000ce00 {
> > compatible = "mediatek,mt8186-fhctl";
> > reg = <0 0x1000c000 0 0xe00>;
> > clocks = <&apmixedsys CLK_APMIXED_MSDCPLL>;
> > mediatek,hopping-ssc-percent = <3>;
> > };
> > };
> >
> > After which...
> >
> > Reviewed-by: AngeloGioacchino Del Regno <
> > [email protected]>
> >
Il 06/10/22 13:23, Johnson Wang (王聖鑫) ha scritto:
> On Fri, 2022-09-30 at 14:07 -0500, Rob Herring wrote:
>> On Thu, Sep 29, 2022 at 03:07:49PM +0200, AngeloGioacchino Del Regno
>> wrote:
>>> Il 29/09/22 13:46, Johnson Wang ha scritto:
>>>> Add the new binding documentation for MediaTek frequency hopping
>>>> and spread spectrum clocking control.
>>>>
>>>> Co-developed-by: Edward-JW Yang <[email protected]>
>>>> Signed-off-by: Edward-JW Yang <[email protected]>
>>>> Signed-off-by: Johnson Wang <[email protected]>
>>>> ---
>>>> .../arm/mediatek/mediatek,mt8186-fhctl.yaml | 53
>>>> +++++++++++++++++++
>>>> 1 file changed, 53 insertions(+)
>>>> create mode 100644
>>>> Documentation/devicetree/bindings/arm/mediatek/mediatek,mt8186-
>>>> fhctl.yaml
>>>>
>>>> diff --git
>>>> a/Documentation/devicetree/bindings/arm/mediatek/mediatek,mt8186-
>>>> fhctl.yaml
>>>> b/Documentation/devicetree/bindings/arm/mediatek/mediatek,mt8186-
>>>> fhctl.yaml
>>>> new file mode 100644
>>>> index 000000000000..258dff7ce6bc
>>>> --- /dev/null
>>>> +++
>>>> b/Documentation/devicetree/bindings/arm/mediatek/mediatek,mt8186-
>>>> fhctl.yaml
>>>> @@ -0,0 +1,53 @@
>>>> +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
>>>> +%YAML 1.2
>>>> +---
>>>> +$id:
>>>> https://urldefense.com/v3/__http://devicetree.org/schemas/arm/mediatek/mediatek,mt8186-fhctl.yaml*__;Iw!!CTRNKA9wMg0ARbw!xVt73f8PaQKph8n0ffGHHmQIxdzooUtGr0xDNVqy_6V0vwcs5k-FLrmkB3PZupSu1ciC$
>>>>
>>>> +$schema:
>>>> https://urldefense.com/v3/__http://devicetree.org/meta-schemas/core.yaml*__;Iw!!CTRNKA9wMg0ARbw!xVt73f8PaQKph8n0ffGHHmQIxdzooUtGr0xDNVqy_6V0vwcs5k-FLrmkB3PZulXwBACk$
>>>>
>>>> +
>>>> +title: MediaTek frequency hopping and spread spectrum clocking
>>>> control
>>>> +
>>>> +maintainers:
>>>> + - Edward-JW Yang <[email protected]>
>>>> +
>>>> +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 hardware.
>>>> +
>>>> +properties:
>>>> + compatible:
>>>> + const: mediatek,mt8186-fhctl
>>>> +
>>>> + reg:
>>>> + maxItems: 1
>>>> +
>>>> + clocks:
>>>> + description: Phandles of the PLL with FHCTL hardware
>>>> capability.
>>>> + minItems: 1
>>>> + maxItems: 30
>>>> +
>>>> + mediatek,hopping-ssc-percent:
>>>> + description: The percentage of spread spectrum clocking for
>>>> one PLL.
>>>> + minItems: 1
>>>> + maxItems: 30
>>>> + items:
>>>> + default: 0
>>>> + minimum: 0
>>>> + maximum: 8
>>>> +
>>>> +required:
>>>> + - compatible
>>>> + - reg
>>>> + - clocks
>>>> +
>>>> +additionalProperties: false
>>>> +
>>>> +examples:
>>>> + - |
>>>> + #include <dt-bindings/clock/mt8186-clk.h>
>>>> + fhctl: fhctl@1000ce00 {
>>>> + compatible = "mediatek,mt8186-fhctl";
>>>> + reg = <0x1000c000 0xe00>;
>>>> + clocks = <&apmixedsys CLK_APMIXED_MSDCPLL>;
>>>> + mediatek,hopping-ssc-percent = <3>;
>>>> + };
>>>
>>>
>>> .... a more realistic example:
>>
>> One or 2 cells is really outside the scope of the binding as either
>> is
>> valid. If the parent bus only has 32-bits of address space, there's
>> little reason to use 2 cells.
>>
>
> Hi Rob,
>
> Thanks for the explanation.
>
> Then I will keep this example the same in the next version.
That was just to have it 1:1 with how it'll be written in the devicetree....
....in any case, Johnson, there's a discrepancy in the iospace:
1000ce00 vs 0c1000c000 - that has to be fixed anyway.
Cheers,
Angelo
>
> BRs,
> Johnson Wang
>>>
>>> #include <dt-bindings/clock/mt8186-clk.h>
>>>
>>> soc {
>>> #address-cells = <2>;
>>> #size-cells = <2>;
>>>
>>> fhctl: fhctl@1000ce00 {
>>> compatible = "mediatek,mt8186-fhctl";
>>> reg = <0 0x1000c000 0 0xe00>;
>>> clocks = <&apmixedsys CLK_APMIXED_MSDCPLL>;
>>> mediatek,hopping-ssc-percent = <3>;
>>> };
>>> };
>>>
>>> After which...
>>>
>>> Reviewed-by: AngeloGioacchino Del Regno <
>>> [email protected]>
>>>
On Thu, 2022-10-06 at 14:33 +0200, AngeloGioacchino Del Regno wrote:
> Il 06/10/22 13:23, Johnson Wang (王聖鑫) ha scritto:
> > On Fri, 2022-09-30 at 14:07 -0500, Rob Herring wrote:
> > > On Thu, Sep 29, 2022 at 03:07:49PM +0200, AngeloGioacchino Del
> > > Regno
> > > wrote:
> > > > Il 29/09/22 13:46, Johnson Wang ha scritto:
> > > > > Add the new binding documentation for MediaTek frequency
> > > > > hopping
> > > > > and spread spectrum clocking control.
> > > > >
> > > > > Co-developed-by: Edward-JW Yang <[email protected]>
> > > > > Signed-off-by: Edward-JW Yang <[email protected]>
> > > > > Signed-off-by: Johnson Wang <[email protected]>
> > > > > ---
> > > > > .../arm/mediatek/mediatek,mt8186-fhctl.yaml | 53
> > > > > +++++++++++++++++++
> > > > > 1 file changed, 53 insertions(+)
> > > > > create mode 100644
> > > > > Documentation/devicetree/bindings/arm/mediatek/mediatek,mt818
> > > > > 6-
> > > > > fhctl.yaml
> > > > >
> > > > > diff --git
> > > > > a/Documentation/devicetree/bindings/arm/mediatek/mediatek,mt8
> > > > > 186-
> > > > > fhctl.yaml
> > > > > b/Documentation/devicetree/bindings/arm/mediatek/mediatek,mt8
> > > > > 186-
> > > > > fhctl.yaml
> > > > > new file mode 100644
> > > > > index 000000000000..258dff7ce6bc
> > > > > --- /dev/null
> > > > > +++
> > > > > b/Documentation/devicetree/bindings/arm/mediatek/mediatek,mt8
> > > > > 186-
> > > > > fhctl.yaml
> > > > > @@ -0,0 +1,53 @@
> > > > > +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
> > > > > +%YAML 1.2
> > > > > +---
> > > > > +$id:
> > > > >
https://urldefense.com/v3/__http://devicetree.org/schemas/arm/mediatek/mediatek,mt8186-fhctl.yaml*__;Iw!!CTRNKA9wMg0ARbw!xVt73f8PaQKph8n0ffGHHmQIxdzooUtGr0xDNVqy_6V0vwcs5k-FLrmkB3PZupSu1ciC$
> > > > >
> > > > > +$schema:
> > > > >
https://urldefense.com/v3/__http://devicetree.org/meta-schemas/core.yaml*__;Iw!!CTRNKA9wMg0ARbw!xVt73f8PaQKph8n0ffGHHmQIxdzooUtGr0xDNVqy_6V0vwcs5k-FLrmkB3PZulXwBACk$
> > > > >
> > > > > +
> > > > > +title: MediaTek frequency hopping and spread spectrum
> > > > > clocking
> > > > > control
> > > > > +
> > > > > +maintainers:
> > > > > + - Edward-JW Yang <[email protected]>
> > > > > +
> > > > > +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 hardware.
> > > > > +
> > > > > +properties:
> > > > > + compatible:
> > > > > + const: mediatek,mt8186-fhctl
> > > > > +
> > > > > + reg:
> > > > > + maxItems: 1
> > > > > +
> > > > > + clocks:
> > > > > + description: Phandles of the PLL with FHCTL hardware
> > > > > capability.
> > > > > + minItems: 1
> > > > > + maxItems: 30
> > > > > +
> > > > > + mediatek,hopping-ssc-percent:
> > > > > + description: The percentage of spread spectrum clocking
> > > > > for
> > > > > one PLL.
> > > > > + minItems: 1
> > > > > + maxItems: 30
> > > > > + items:
> > > > > + default: 0
> > > > > + minimum: 0
> > > > > + maximum: 8
> > > > > +
> > > > > +required:
> > > > > + - compatible
> > > > > + - reg
> > > > > + - clocks
> > > > > +
> > > > > +additionalProperties: false
> > > > > +
> > > > > +examples:
> > > > > + - |
> > > > > + #include <dt-bindings/clock/mt8186-clk.h>
> > > > > + fhctl: fhctl@1000ce00 {
> > > > > + compatible = "mediatek,mt8186-fhctl";
> > > > > + reg = <0x1000c000 0xe00>;
> > > > > + clocks = <&apmixedsys CLK_APMIXED_MSDCPLL>;
> > > > > + mediatek,hopping-ssc-percent = <3>;
> > > > > + };
> > > >
> > > >
> > > > .... a more realistic example:
> > >
> > > One or 2 cells is really outside the scope of the binding as
> > > either
> > > is
> > > valid. If the parent bus only has 32-bits of address space,
> > > there's
> > > little reason to use 2 cells.
> > >
> >
> > Hi Rob,
> >
> > Thanks for the explanation.
> >
> > Then I will keep this example the same in the next version.
>
> That was just to have it 1:1 with how it'll be written in the
> devicetree....
> ....in any case, Johnson, there's a discrepancy in the iospace:
> 1000ce00 vs 0c1000c000 - that has to be fixed anyway.
>
> Cheers,
> Angelo
Hi Angelo,
Thank you for reminding me.
I didn't notice the iospace typo in the example.
I will fix it in the next version.
BRs,
Johnson Wang
>
> >
> > BRs,
> > Johnson Wang
> > > >
> > > > #include <dt-bindings/clock/mt8186-clk.h>
> > > >
> > > > soc {
> > > > #address-cells = <2>;
> > > > #size-cells = <2>;
> > > >
> > > > fhctl: fhctl@1000ce00 {
> > > > compatible = "mediatek,mt8186-fhctl";
> > > > reg = <0 0x1000c000 0 0xe00>;
> > > > clocks = <&apmixedsys CLK_APMIXED_MSDCPLL>;
> > > > mediatek,hopping-ssc-percent = <3>;
> > > > };
> > > > };
> > > >
> > > > After which...
> > > >
> > > > Reviewed-by: AngeloGioacchino Del Regno <
> > > > [email protected]>
> > > >
>
>