* [PATCH 3/6] clk: realtek: add common clock support for Realtek SoCs
2019-12-03 7:35 [PATCH 0/6] arm64: Realtek RTD1619 clock and reset controllers James Tai
2019-12-03 7:35 ` [PATCH 1/6] dt-bindings: clock: add bindings for RTD1619 clocks James Tai
2019-12-03 7:35 ` [PATCH 2/6] dt-bindings: reset: add bindings for rtd1619 reset controls James Tai
@ 2019-12-03 7:35 ` James Tai
2019-12-03 7:35 ` [PATCH 4/6] clk: realtek: add reset controller " James Tai
` (3 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: James Tai @ 2019-12-03 7:35 UTC (permalink / raw)
To: Andreas Färber
Cc: Palmer Dabbelt, Paul Walmsley, Matthias Brugger, linux-kernel,
linux-riscv, linux-arm-kernel, linux-mediatek, cylee12,
Michael Turquette, Stephen Boyd, linux-clk
From: cylee12 <cylee12@realtek.com>
This patch adds common clock support for Realtek SoCs, including PLLs,
Mux clocks and Gate clocks.
Signed-off-by: Cheng-Yu Lee <cylee12@realtek.com>
Signed-off-by: James Tai <james.tai@realtek.com>
---
drivers/clk/Kconfig | 1 +
drivers/clk/Makefile | 1 +
drivers/clk/realtek/Kconfig | 10 +
drivers/clk/realtek/Makefile | 9 +
drivers/clk/realtek/clk-pll-dif.c | 81 ++++++
drivers/clk/realtek/clk-pll-psaud.c | 120 ++++++++
drivers/clk/realtek/clk-pll.c | 400 ++++++++++++++++++++++++++
drivers/clk/realtek/clk-pll.h | 151 ++++++++++
drivers/clk/realtek/clk-regmap-gate.c | 89 ++++++
drivers/clk/realtek/clk-regmap-gate.h | 26 ++
drivers/clk/realtek/clk-regmap-mux.c | 63 ++++
drivers/clk/realtek/clk-regmap-mux.h | 26 ++
drivers/clk/realtek/common.c | 320 +++++++++++++++++++++
drivers/clk/realtek/common.h | 123 ++++++++
14 files changed, 1420 insertions(+)
create mode 100644 drivers/clk/realtek/Kconfig
create mode 100644 drivers/clk/realtek/Makefile
create mode 100644 drivers/clk/realtek/clk-pll-dif.c
create mode 100644 drivers/clk/realtek/clk-pll-psaud.c
create mode 100644 drivers/clk/realtek/clk-pll.c
create mode 100644 drivers/clk/realtek/clk-pll.h
create mode 100644 drivers/clk/realtek/clk-regmap-gate.c
create mode 100644 drivers/clk/realtek/clk-regmap-gate.h
create mode 100644 drivers/clk/realtek/clk-regmap-mux.c
create mode 100644 drivers/clk/realtek/clk-regmap-mux.h
create mode 100644 drivers/clk/realtek/common.c
create mode 100644 drivers/clk/realtek/common.h
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index c44247d0b83e..8e06487440ce 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -317,6 +317,7 @@ source "drivers/clk/mediatek/Kconfig"
source "drivers/clk/meson/Kconfig"
source "drivers/clk/mvebu/Kconfig"
source "drivers/clk/qcom/Kconfig"
+source "drivers/clk/realtek/Kconfig"
source "drivers/clk/renesas/Kconfig"
source "drivers/clk/samsung/Kconfig"
source "drivers/clk/sifive/Kconfig"
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 0138fb14e6f8..71ea17f97f7d 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -95,6 +95,7 @@ obj-$(CONFIG_COMMON_CLK_NXP) += nxp/
obj-$(CONFIG_MACH_PISTACHIO) += pistachio/
obj-$(CONFIG_COMMON_CLK_PXA) += pxa/
obj-$(CONFIG_COMMON_CLK_QCOM) += qcom/
+obj-$(CONFIG_COMMON_CLK_REALTEK) += realtek/
obj-y += renesas/
obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/
obj-$(CONFIG_COMMON_CLK_SAMSUNG) += samsung/
diff --git a/drivers/clk/realtek/Kconfig b/drivers/clk/realtek/Kconfig
new file mode 100644
index 000000000000..5bca757dddfa
--- /dev/null
+++ b/drivers/clk/realtek/Kconfig
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config COMMON_CLK_REALTEK
+ bool "Clock driver for realtek"
+ select MFD_SYSCON
+
+config CLK_PLL_PSAUD
+ bool
+
+config CLK_PLL_DIF
+ bool
diff --git a/drivers/clk/realtek/Makefile b/drivers/clk/realtek/Makefile
new file mode 100644
index 000000000000..050d450db067
--- /dev/null
+++ b/drivers/clk/realtek/Makefile
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_COMMON_CLK_REALTEK) += clk-rtk.o
+
+clk-rtk-y += common.o
+clk-rtk-y += clk-regmap-mux.o
+clk-rtk-y += clk-regmap-gate.o
+clk-rtk-y += clk-pll.o
+clk-rtk-$(CONFIG_CLK_PLL_PSAUD) += clk-pll-psaud.o
+clk-rtk-$(CONFIG_CLK_PLL_DIF) += clk-pll-dif.o
diff --git a/drivers/clk/realtek/clk-pll-dif.c b/drivers/clk/realtek/clk-pll-dif.c
new file mode 100644
index 000000000000..d19efef2626e
--- /dev/null
+++ b/drivers/clk/realtek/clk-pll-dif.c
@@ -0,0 +1,81 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2019 Realtek Semiconductor Corporation
+ * Author: Cheng-Yu Lee <cylee12@realtek.com>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clk.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include "common.h"
+#include "clk-pll.h"
+
+static int clk_pll_dif_enable(struct clk_hw *hw)
+{
+ struct clk_pll_dif *pll = to_clk_pll_dif(hw);
+
+ pr_debug("%pC: %s\n", hw->clk, __func__);
+
+ clk_regmap_write(&pll->clkr, pll->pll_ofs + 0x0C, 0x00000048);
+ clk_regmap_write(&pll->clkr, pll->pll_ofs + 0x08, 0x00020c00);
+ clk_regmap_write(&pll->clkr, pll->pll_ofs + 0x04, 0x204004ca);
+ clk_regmap_write(&pll->clkr, pll->pll_ofs + 0x00, 0x8000a000);
+ udelay(100);
+
+ clk_regmap_write(&pll->clkr, pll->pll_ofs + 0x08, 0x00420c00);
+ udelay(50);
+
+ clk_regmap_write(&pll->clkr, pll->pll_ofs + 0x08, 0x00420c03);
+ udelay(200);
+
+ clk_regmap_write(&pll->clkr, pll->pll_ofs + 0x0C, 0x00000078);
+ udelay(100);
+
+ clk_regmap_write(&pll->clkr, pll->pll_ofs + 0x04, 0x204084ca);
+
+ /* ssc control */
+ clk_regmap_write(&pll->clkr, pll->ssc_ofs + 0x00, 0x00000004);
+ clk_regmap_write(&pll->clkr, pll->ssc_ofs + 0x04, 0x00006800);
+ clk_regmap_write(&pll->clkr, pll->ssc_ofs + 0x0C, 0x00000000);
+ clk_regmap_write(&pll->clkr, pll->ssc_ofs + 0x10, 0x00000000);
+ clk_regmap_write(&pll->clkr, pll->ssc_ofs + 0x08, 0x001e1f98);
+ clk_regmap_write(&pll->clkr, pll->ssc_ofs + 0x00, 0x00000005);
+ pll->status = 1;
+
+ return 0;
+}
+
+static void clk_pll_dif_disable(struct clk_hw *hw)
+{
+ struct clk_pll_dif *pll = to_clk_pll_dif(hw);
+
+ pr_debug("%pC: %s\n", hw->clk, __func__);
+ clk_regmap_update(&pll->clkr, pll->pll_ofs + 0x04, 0x00080000, 0x0);
+ clk_regmap_update(&pll->clkr, pll->pll_ofs + 0x08, 0x00400C03, 0x0);
+ clk_regmap_update(&pll->clkr, pll->pll_ofs + 0x0C, 0x00000038, 0x0);
+
+ clk_regmap_write(&pll->clkr, pll->ssc_ofs + 0x00, 0x00000004);
+ pll->status = 0;
+}
+
+static int clk_pll_dif_is_enabled(struct clk_hw *hw)
+{
+ struct clk_pll_dif *pll = to_clk_pll_dif(hw);
+
+ return pll->status;
+}
+
+static void clk_pll_dif_disable_unused(struct clk_hw *hw)
+{
+ pr_info("%pC: %s\n", hw->clk, __func__);
+ clk_pll_dif_disable(hw);
+}
+
+const struct clk_ops clk_pll_dif_ops = {
+ .enable = clk_pll_dif_enable,
+ .disable = clk_pll_dif_disable,
+ .disable_unused = clk_pll_dif_disable_unused,
+ .is_enabled = clk_pll_dif_is_enabled,
+};
+
diff --git a/drivers/clk/realtek/clk-pll-psaud.c b/drivers/clk/realtek/clk-pll-psaud.c
new file mode 100644
index 000000000000..757a3320ebb7
--- /dev/null
+++ b/drivers/clk/realtek/clk-pll-psaud.c
@@ -0,0 +1,120 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * clk-pll-psaud.c - PLL_PSAUDXA
+ *
+ * Copyright (c) 2019 Realtek Semiconductor Corporation
+ * Author: Cheng-Yu Lee <cylee12@realtek.com>
+ */
+
+#include <linux/clk-provider.h>
+#include "common.h"
+#include "clk-pll.h"
+
+static int clk_pll_psaud_enable(struct clk_hw *hw)
+{
+ struct clk_pll_psaud *pll = to_clk_pll_psaud(hw);
+ u32 mask = 0, val = 0;
+
+ if (pll->id == CLK_PLL_PSAUD1A) {
+ mask = 0x3;
+ val = 0x1;
+ } else {
+ mask = 0xc;
+ val = 0x4;
+ }
+ clk_regmap_update(&pll->clkr, pll->reg + 4, mask, val);
+ return 0;
+}
+
+static void clk_pll_psaud_disable(struct clk_hw *hw)
+{
+ struct clk_pll_psaud *pll = to_clk_pll_psaud(hw);
+ u32 mask = 0, val = 0;
+
+ if (pll->id == CLK_PLL_PSAUD1A) {
+ mask = 0x3;
+ val = 0x3;
+ } else {
+ mask = 0xc;
+ val = 0xc;
+ }
+ clk_regmap_update(&pll->clkr, pll->reg + 4, mask, val);
+}
+
+static void clk_pll_psaud_disable_unused(struct clk_hw *hw)
+{
+ pr_info("%pC: %s\n", hw->clk, __func__);
+ clk_pll_psaud_disable(hw);
+}
+
+static int clk_pll_psaud_is_enabled(struct clk_hw *hw)
+{
+ struct clk_pll_psaud *pll = to_clk_pll_psaud(hw);
+ u32 val;
+
+ val = clk_regmap_read(&pll->clkr, pll->reg + 4);
+ if (pll->id == CLK_PLL_PSAUD1A)
+ val &= 0x3;
+ else
+ val >>= 2;
+ return val == 0x1;
+}
+
+static long clk_pll_psaud_round_rate(struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long *parent_rate)
+{
+ return 49192000;
+}
+
+static int clk_pll_psaud_set_rate(struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_pll_psaud *pll = to_clk_pll_psaud(hw);
+ u32 rsel = 0;
+ u32 mask = 0, val = 0;
+
+ if (WARN_ON_ONCE(rate != 45158400 && rate != 49192000))
+ return -EINVAL;
+
+ if (rate == 45158400)
+ rsel = 1;
+ if (pll->id == CLK_PLL_PSAUD1A) {
+ val = 0x6a0 | (rsel << 8);
+ mask = 0x7e0;
+ } else {
+ val = 0x19 | (rsel << 2);
+ mask = 0x1f;
+ }
+ clk_regmap_update(&pll->clkr, pll->reg, mask, val);
+ return 0;
+}
+
+static unsigned long clk_pll_psaud_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_pll_psaud *pll = to_clk_pll_psaud(hw);
+ u32 val;
+ u32 rsel = 0;
+
+ val = clk_regmap_read(&pll->clkr, pll->reg);
+
+ if (pll->id == CLK_PLL_PSAUD1A)
+ rsel = !!(val & BIT(8));
+ else
+ rsel = !!(val & BIT(2));
+
+ return rsel ? 45158400 : 49192000;
+}
+
+const struct clk_ops clk_pll_psaud_ops = {
+ .enable = clk_pll_psaud_enable,
+ .disable = clk_pll_psaud_disable,
+ .disable_unused = clk_pll_psaud_disable_unused,
+ .is_enabled = clk_pll_psaud_is_enabled,
+ .set_rate = clk_pll_psaud_set_rate,
+ .round_rate = clk_pll_psaud_round_rate,
+ .recalc_rate = clk_pll_psaud_recalc_rate,
+};
+
diff --git a/drivers/clk/realtek/clk-pll.c b/drivers/clk/realtek/clk-pll.c
new file mode 100644
index 000000000000..dd90bfc011ec
--- /dev/null
+++ b/drivers/clk/realtek/clk-pll.c
@@ -0,0 +1,400 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2017-2018 Realtek Semiconductor Corporation
+ * Author: Cheng-Yu Lee <cylee12@realtek.com>
+ */
+
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/clk-provider.h>
+#include <linux/clk.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/debugfs.h>
+#include "common.h"
+#include "clk-pll.h"
+
+static int clk_pll_debug_rate_u64_set(void *data, u64 val)
+{
+ struct clk_hw *hw = data;
+
+ clk_set_rate(hw->clk, (unsigned long)(val));
+ return 0;
+}
+
+static int clk_pll_debug_rate_u64_get(void *data, u64 *val)
+{
+ struct clk_hw *hw = data;
+
+ *val = (u64)clk_get_rate(hw->clk);
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(clk_pll_debut_rate_op, clk_pll_debug_rate_u64_get,
+ clk_pll_debug_rate_u64_set, "%llu\n");
+
+static void clk_pll_debug_init(struct clk_hw *hw, struct dentry *d)
+{
+ debugfs_create_file("debug_rate", 0644, d, hw, &clk_pll_debut_rate_op);
+}
+
+static const struct freq_table *ftbl_find_by_rate(const struct freq_table *ftbl,
+ unsigned long rate)
+{
+ unsigned long best_rate = 0;
+ const struct freq_table *best = NULL;
+
+ for ( ; !IS_FREQ_TABLE_END(ftbl); ftbl++) {
+ if (ftbl->rate == rate)
+ return ftbl;
+
+ if (ftbl->rate > rate)
+ continue;
+
+ if ((rate - best_rate) > (rate - ftbl->rate)) {
+ best_rate = ftbl->rate;
+ best = ftbl;
+ }
+ }
+
+ return best;
+}
+
+static const struct freq_table *ftbl_find_by_val(const struct freq_table *ftbl,
+ uint32_t value)
+{
+ while (!IS_FREQ_TABLE_END(ftbl)) {
+ if (ftbl->val == (value))
+ return ftbl;
+ ftbl++;
+ }
+ return NULL;
+};
+
+static const struct div_table *dtbl_find_by_rate(const struct div_table *dtbl,
+ unsigned long rate)
+{
+ while (!IS_DIV_TABLE_END(dtbl)) {
+ if (rate >= dtbl->rate)
+ return dtbl;
+ dtbl++;
+ }
+ return NULL;
+}
+
+static const struct div_table *dtbl_find_by_val(const struct div_table *dtbl,
+ uint32_t val)
+{
+ while (!IS_DIV_TABLE_END(dtbl)) {
+ if (val == dtbl->val)
+ return dtbl;
+ dtbl++;
+ }
+ return NULL;
+}
+
+static void __clk_pll_set_pow_reg(struct clk_pll *clkp, int on)
+{
+ uint32_t pow = (clkp->pow_loc == CLK_PLL_CONF_POW_LOC_CTL3) ? 0x8 : 0x4;
+
+ if (on) {
+ clk_regmap_update(&clkp->clkr, clkp->pll_ofs + pow, 0x7, 0x3);
+ if (clkp->freq_loc == CLK_PLL_CONF_FREQ_LOC_SSC1) {
+ uint32_t val;
+
+ val = clk_regmap_read(&clkp->clkr, clkp->ssc_ofs + 0x0);
+
+ /*
+ * For those PLL with SCC used only the default
+ * freq, the oc_en would nerver to be set.
+ * Help to set it here.
+ */
+ if ((val & 0x7) != 0x5)
+ clk_regmap_update(&clkp->clkr,
+ clkp->ssc_ofs + 0x0, 0x7, 0x5);
+ }
+ udelay(200);
+ } else {
+ clk_regmap_update(&clkp->clkr, clkp->pll_ofs + pow, 0x7, 0x4);
+ }
+}
+
+static int clk_pll_enable(struct clk_hw *hw)
+{
+ struct clk_pll *clkp = to_clk_pll(hw);
+
+ if (clk_pll_has_pow(clkp))
+ __clk_pll_set_pow_reg(clkp, 1);
+ return 0;
+}
+
+static void clk_pll_disable(struct clk_hw *hw)
+{
+ struct clk_pll *clkp = to_clk_pll(hw);
+
+ if (clk_pll_has_pow(clkp))
+ __clk_pll_set_pow_reg(clkp, 0);
+}
+
+static void clk_pll_disable_unused(struct clk_hw *hw)
+{
+ pr_info("%pC: %s\n", hw->clk, __func__);
+ clk_pll_disable(hw);
+}
+
+static int clk_pll_is_enabled(struct clk_hw *hw)
+{
+ struct clk_pll *clkp = to_clk_pll(hw);
+ uint32_t pow;
+ uint32_t val;
+
+ if (!clk_pll_has_pow(clkp))
+ return -EINVAL;
+
+ pow = (clkp->pow_loc == CLK_PLL_CONF_POW_LOC_CTL3) ? 0x8 : 0x4;
+ val = clk_regmap_read(&clkp->clkr, clkp->pll_ofs + pow);
+ return !!(val & 0x1);
+}
+
+static long clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ struct clk_pll *clkp = to_clk_pll(hw);
+ const struct freq_table *ftblv = NULL;
+
+ ftblv = ftbl_find_by_rate(clkp->freq_tbl, rate);
+ return ftblv ? ftblv->rate : 0;
+}
+
+static uint32_t __clk_pll_freq_get(struct clk_pll *clkp)
+{
+ uint32_t val = 0;
+
+ switch (clkp->freq_loc) {
+ case CLK_PLL_CONF_FREQ_LOC_CTL1:
+ val = clk_regmap_read(&clkp->clkr, clkp->pll_ofs + 0x0);
+ break;
+
+ case CLK_PLL_CONF_FREQ_LOC_CTL2:
+ val = clk_regmap_read(&clkp->clkr, clkp->pll_ofs + 0x4);
+ break;
+
+ case CLK_PLL_CONF_FREQ_LOC_SSC1:
+ val = clk_regmap_read(&clkp->clkr, clkp->ssc_ofs + 0x4);
+ break;
+
+ default:
+ break;
+ }
+ return val & clkp->freq_mask;
+}
+
+static unsigned long clk_pll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_pll *clkp = to_clk_pll(hw);
+ unsigned long flags = 0;
+ const struct freq_table *fv;
+ uint32_t val;
+
+ flags = clk_pll_lock(clkp);
+ val = __clk_pll_freq_get(clkp);
+ clk_pll_unlock(clkp, flags);
+
+ fv = ftbl_find_by_val(clkp->freq_tbl, val);
+ return fv ? fv->rate : 0;
+}
+
+static inline int __clk_pll_freq_set(struct clk_pll *clkp, uint32_t val)
+{
+ struct clk_hw *hw = &clkp->clkr.hw;
+ int ret = 0;
+ uint32_t mask = clkp->freq_mask;
+ uint32_t pollval;
+
+ switch (clkp->freq_loc) {
+ case CLK_PLL_CONF_FREQ_LOC_CTL1:
+ clk_regmap_update(&clkp->clkr, clkp->pll_ofs, mask, val);
+ break;
+
+ case CLK_PLL_CONF_FREQ_LOC_CTL2:
+ clk_regmap_update(&clkp->clkr, clkp->pll_ofs + 0x4, mask, val);
+ clk_regmap_update(&clkp->clkr, clkp->pll_ofs + 0x8, 0x1, 0x0);
+ clk_regmap_update(&clkp->clkr, clkp->pll_ofs + 0x8, 0x1, 0x1);
+ break;
+
+ case CLK_PLL_CONF_FREQ_LOC_SSC1:
+ clk_regmap_update(&clkp->clkr, clkp->ssc_ofs + 0x0, 0x7, 0x4);
+ clk_regmap_update(&clkp->clkr, clkp->ssc_ofs + 0x4, mask, val);
+ clk_regmap_update(&clkp->clkr, clkp->ssc_ofs + 0x0, 0x7, 0x5);
+
+ if (clk_pll_has_pow(clkp) && !clk_pll_is_enabled(hw))
+ return 0;
+
+ ret = regmap_read_poll_timeout(clkp->clkr.regmap,
+ clkp->ssc_ofs + 0x1c,
+ pollval, pollval & BIT(20),
+ 0, 2000);
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_pll *clkp = to_clk_pll(hw);
+ unsigned long flags = 0;
+ const struct freq_table *fv;
+ int ret = 0;
+
+ fv = ftbl_find_by_rate(clkp->freq_tbl, rate);
+ if (!fv)
+ return -EINVAL;
+
+ pr_debug("%pC: %s: rate=%ld, val=0x%08x\n", hw->clk, __func__,
+ fv->rate, fv->val);
+
+ flags = clk_pll_lock(clkp);
+ ret = __clk_pll_freq_set(clkp, fv->val);
+ clk_pll_unlock(clkp, flags);
+ if (ret)
+ pr_warn("%pC %s: failed to set freq: %d\n", hw->clk, __func__,
+ ret);
+ return ret;
+}
+
+static void __clk_pll_div_set(struct clk_pll_div *clkpd, uint32_t val)
+{
+ uint32_t m = (BIT(clkpd->div_width) - 1) << clkpd->div_shift;
+ uint32_t s = clkpd->div_shift;
+
+ clk_regmap_update(&clkpd->clkp.clkr, clkpd->div_ofs, m, val << s);
+}
+
+static uint32_t __clk_pll_div_get(struct clk_pll_div *clkpd)
+{
+ uint32_t m = (BIT(clkpd->div_width) - 1) << clkpd->div_shift;
+ uint32_t s = clkpd->div_shift;
+
+ return (clk_regmap_read(&clkpd->clkp.clkr, clkpd->div_ofs) & m) >> s;
+}
+
+static long clk_pll_div_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ struct clk_pll_div *clkpd = to_clk_pll_div(hw);
+ const struct div_table *dv;
+
+ /* lookup div in dtbl */
+ dv = dtbl_find_by_rate(clkpd->div_tbl, rate);
+ if (!dv)
+ return 0;
+
+ rate *= dv->div;
+ rate = clk_pll_round_rate(hw, rate, parent_rate);
+ return rate / dv->div;
+}
+
+static unsigned long clk_pll_div_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_pll_div *clkpd = to_clk_pll_div(hw);
+ unsigned long rate;
+ const struct div_table *dv;
+ uint32_t val;
+
+ rate = clk_pll_recalc_rate(hw, parent_rate);
+
+ val = __clk_pll_div_get(clkpd);
+ dv = dtbl_find_by_val(clkpd->div_tbl, val);
+ if (!dv)
+ return 0;
+
+ rate /= dv->div;
+ pr_debug("%pC: %s: current rate=%lu, div=%d, reg_val=0x%x\n",
+ hw->clk, __func__, rate, dv->div, val);
+
+ return rate;
+}
+
+static int clk_pll_div_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk *clk = hw->clk;
+ struct clk_pll_div *clkpd = to_clk_pll_div(hw);
+ unsigned long flags;
+ const struct div_table *ndv, *cdv;
+ unsigned long target;
+ uint32_t cur_d;
+ int ret;
+
+ /* find next in the dtbl */
+ ndv = dtbl_find_by_rate(clkpd->div_tbl, rate);
+ if (!ndv)
+ return -EINVAL;
+
+ target = rate * ndv->div;
+
+ /* find current in the dtbl */
+ cur_d = __clk_pll_div_get(clkpd);
+ cdv = dtbl_find_by_val(clkpd->div_tbl, cur_d);
+ if (!cdv)
+ return -EINVAL;
+
+ pr_debug("%pC: rate=%lu, cdv={%d,0x%x}, ndv={%d,0x%x}\n",
+ clk, rate, cdv->div, cdv->val, ndv->div, ndv->val);
+
+ flags = clk_pll_div_lock(clkpd);
+
+ /* workaround to prevent glitch */
+#ifdef CONFIG_COMMON_CLK_RTD129X
+ if ((&clkpd->clkp.flags & CLK_PLL_DIV_WORKAROUND) &&
+ ndv->val != cdv->val && (ndv->val == 1 || cdv->val == 1)) {
+
+ pr_debug("%pC: apply rate=%u\n", clk, 1000000000);
+ clk_pll_set_rate(hw, 1000000000, parent_rate);
+
+ pr_debug("%pC: apply dv={%d, 0x%x}\n", clk, ndv->div, ndv->val);
+ __clk_pll_div_set(clkpd, ndv->val);
+ cdv = ndv;
+ }
+#endif
+
+ if (ndv->div > cdv->div)
+ __clk_pll_div_set(clkpd, ndv->val);
+ ret = clk_pll_set_rate(hw, target, parent_rate);
+ if (ndv->div < cdv->div)
+ __clk_pll_div_set(clkpd, ndv->val);
+
+ clk_pll_div_unlock(clkpd, flags);
+
+ return ret;
+}
+
+const struct clk_ops clk_pll_ops = {
+ .debug_init = clk_pll_debug_init,
+ .round_rate = clk_pll_round_rate,
+ .recalc_rate = clk_pll_recalc_rate,
+ .set_rate = clk_pll_set_rate,
+ .enable = clk_pll_enable,
+ .disable = clk_pll_disable,
+ .disable_unused = clk_pll_disable_unused,
+ .is_enabled = clk_pll_is_enabled,
+};
+
+const struct clk_ops clk_pll_div_ops = {
+ .debug_init = clk_pll_debug_init,
+ .round_rate = clk_pll_div_round_rate,
+ .recalc_rate = clk_pll_div_recalc_rate,
+ .set_rate = clk_pll_div_set_rate,
+ .enable = clk_pll_enable,
+ .disable = clk_pll_disable,
+ .disable_unused = clk_pll_disable_unused,
+ .is_enabled = clk_pll_is_enabled,
+};
+
diff --git a/drivers/clk/realtek/clk-pll.h b/drivers/clk/realtek/clk-pll.h
new file mode 100644
index 000000000000..03b4d0391c31
--- /dev/null
+++ b/drivers/clk/realtek/clk-pll.h
@@ -0,0 +1,151 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2017-2019 Realtek Semiconductor Corporation
+ * Author: Cheng-Yu Lee <cylee12@realtek.com>
+ */
+
+#ifndef __CLK_REALTEK_CLK_PLL_H
+#define __CLK_REALTEK_CLK_PLL_H
+
+#include "common.h"
+
+struct freq_table {
+ uint32_t val;
+ unsigned long rate;
+};
+
+#define FREQ_TABLE_END { .rate = 0 }
+#define IS_FREQ_TABLE_END(_f) ((_f)->rate == 0)
+
+struct div_table {
+ unsigned long rate;
+ uint32_t div;
+ uint32_t val;
+};
+
+#define DIV_TABLE_END { .rate = 0 }
+#define IS_DIV_TABLE_END(_d) ((_d)->rate == 0)
+
+struct clk_pll {
+ struct clk_regmap clkr;
+
+
+ int pll_ofs;
+ int ssc_ofs;
+
+ const struct freq_table *freq_tbl;
+ uint32_t freq_mask;
+ uint32_t freq_loc;
+#define CLK_PLL_CONF_FREQ_LOC_CTL1 1
+#define CLK_PLL_CONF_FREQ_LOC_CTL2 2
+#define CLK_PLL_CONF_FREQ_LOC_SSC1 3
+
+ uint32_t pow_loc;
+#define CLK_PLL_CONF_NO_POW 0
+#define CLK_PLL_CONF_POW_LOC_CTL2 1
+#define CLK_PLL_CONF_POW_LOC_CTL3 2
+
+ spinlock_t *lock;
+
+ uint32_t flags;
+};
+
+#define to_clk_pll(_hw) container_of(to_clk_regmap(_hw), struct clk_pll, clkr)
+#define __clk_pll_hw(_ptr) __clk_regmap_hw(&(_ptr)->clkr)
+
+/* clk_pll flags */
+#define CLK_PLL_DIV_WORKAROUND BIT(2)
+
+
+static inline bool clk_pll_has_pow(struct clk_pll *pll)
+{
+ if (pll->pow_loc != CLK_PLL_CONF_NO_POW)
+ return true;
+ return false;
+}
+
+static inline unsigned long clk_pll_lock(struct clk_pll *pll)
+{
+ unsigned long flags = 0;
+
+ if (pll->lock)
+ spin_lock_irqsave(pll->lock, flags);
+ return flags;
+}
+
+static inline void clk_pll_unlock(struct clk_pll *pll, unsigned long flags)
+{
+ if (pll->lock)
+ spin_unlock_irqrestore(pll->lock, flags);
+}
+
+struct clk_pll_div {
+ struct clk_pll clkp;
+ int div_ofs;
+ int div_shift;
+ int div_width;
+ const struct div_table *div_tbl;
+ spinlock_t *lock;
+};
+
+#define to_clk_pll_div(_hw) \
+ container_of(to_clk_pll(_hw), struct clk_pll_div, clkp)
+#define __clk_pll_div_hw(_ptr) __clk_pll_hw(&(_ptr)->clkp)
+
+/* clk_pll_div helper functions */
+static inline unsigned long clk_pll_div_lock(struct clk_pll_div *plld)
+{
+ unsigned long flags = 0;
+
+ if (plld->lock)
+ spin_lock_irqsave(plld->lock, flags);
+ return flags;
+}
+
+static inline void clk_pll_div_unlock(struct clk_pll_div *plld,
+ unsigned long flags)
+{
+ if (plld->lock)
+ spin_unlock_irqrestore(plld->lock, flags);
+}
+
+extern const struct clk_ops clk_pll_ops;
+extern const struct clk_ops clk_pll_div_ops;
+
+#ifdef CONFIG_CLK_PLL_PSAUD
+
+struct clk_pll_psaud {
+ struct clk_regmap clkr;
+ int id;
+ int reg;
+ spinlock_t *lock;
+};
+
+#define to_clk_pll_psaud(_hw) \
+ container_of(to_clk_regmap(_hw), struct clk_pll_psaud, clkr)
+#define __clk_pll_psaud_hw(_ptr) __clk_regmap_hw(&(_ptr)->clkr)
+extern const struct clk_ops clk_pll_psaud_ops;
+
+#define CLK_PLL_PSAUD1A (0x1)
+#define CLK_PLL_PSAUD2A (0x2)
+
+#endif /* CONFIG_CLK_PLL_PSAUD */
+
+#ifdef CONFIG_CLK_PLL_DIF
+
+struct clk_pll_dif {
+ struct clk_regmap clkr;
+ int pll_ofs;
+ int ssc_ofs;
+ uint32_t status;
+ spinlock_t *lock;
+};
+#define to_clk_pll_dif(_hw) \
+ container_of(to_clk_regmap(_hw), struct clk_pll_dif, clkr)
+#define __clk_pll_dif_hw(_ptr) __clk_regmap_hw(&(_ptr)->clkr)
+
+extern const struct clk_ops clk_pll_dif_ops;
+#endif /* CONFIG_CLK_PLL_DIF */
+
+
+#endif /* __CLK_REALTEK_CLK_PLL_H */
diff --git a/drivers/clk/realtek/clk-regmap-gate.c b/drivers/clk/realtek/clk-regmap-gate.c
new file mode 100644
index 000000000000..5f4a6a98da94
--- /dev/null
+++ b/drivers/clk/realtek/clk-regmap-gate.c
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2017 Realtek Semiconductor Corporation
+ * Author: Cheng-Yu Lee <cylee12@realtek.com>
+ */
+
+#include <linux/slab.h>
+#include <linux/clk-provider.h>
+#include "clk-regmap-gate.h"
+
+static int clk_regmap_gate_enable(struct clk_hw *hw)
+{
+ struct clk_regmap_gate *clkg = to_clk_regmap_gate(hw);
+ unsigned long flags = 0;
+ unsigned int mask;
+ unsigned int val;
+
+ if (clkg->lock)
+ spin_lock_irqsave(clkg->lock, flags);
+
+ mask = BIT(clkg->bit_idx);
+ val = BIT(clkg->bit_idx);
+
+ if (clkg->write_en) {
+ mask |= BIT(clkg->bit_idx + 1);
+ val |= BIT(clkg->bit_idx + 1);
+ }
+
+ clk_regmap_update(&clkg->clkr, clkg->gate_ofs, mask, val);
+
+ if (clkg->lock)
+ spin_unlock_irqrestore(clkg->lock, flags);
+
+ return 0;
+}
+
+static void clk_regmap_gate_disable(struct clk_hw *hw)
+{
+ struct clk_regmap_gate *clkg = to_clk_regmap_gate(hw);
+ unsigned long flags = 0;
+ unsigned int mask;
+ unsigned int val;
+
+ if (clkg->lock)
+ spin_lock_irqsave(clkg->lock, flags);
+
+ mask = BIT(clkg->bit_idx);
+ val = 0;
+
+ if (clkg->write_en) {
+ mask |= BIT(clkg->bit_idx + 1);
+ val |= BIT(clkg->bit_idx + 1);
+ }
+
+ clk_regmap_update(&clkg->clkr, clkg->gate_ofs, mask, val);
+
+ if (clkg->lock)
+ spin_unlock_irqrestore(clkg->lock, flags);
+}
+
+static void clk_regmap_gate_disable_unused(struct clk_hw *hw)
+{
+ pr_info("%pC: %s\n", hw->clk, __func__);
+ clk_regmap_gate_disable(hw);
+}
+
+static int clk_regmap_gate_is_enabled(struct clk_hw *hw)
+{
+ struct clk_regmap_gate *clkg = to_clk_regmap_gate(hw);
+ int ret;
+ unsigned long flags = 0;
+
+ if (clkg->lock)
+ spin_lock_irqsave(clkg->lock, flags);
+
+ ret = clk_regmap_read(&clkg->clkr, clkg->gate_ofs) & BIT(clkg->bit_idx);
+
+ if (clkg->lock)
+ spin_unlock_irqrestore(clkg->lock, flags);
+
+ return !!ret;
+}
+
+const struct clk_ops clk_regmap_gate_ops = {
+ .enable = clk_regmap_gate_enable,
+ .disable = clk_regmap_gate_disable,
+ .disable_unused = clk_regmap_gate_disable_unused,
+ .is_enabled = clk_regmap_gate_is_enabled,
+};
diff --git a/drivers/clk/realtek/clk-regmap-gate.h b/drivers/clk/realtek/clk-regmap-gate.h
new file mode 100644
index 000000000000..7643e0193177
--- /dev/null
+++ b/drivers/clk/realtek/clk-regmap-gate.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2017 Realtek Semiconductor Corporation
+ * Author: Cheng-Yu Lee <cylee12@realtek.com>
+ */
+
+#ifndef __CLK_REALTEK_CLK_MMIO_GATE_H
+#define __CLK_REALTEK_CLK_MMIO_GATE_H
+
+#include "common.h"
+
+struct clk_regmap_gate {
+ struct clk_regmap clkr;
+ int gate_ofs;
+ uint8_t bit_idx;
+ spinlock_t *lock;
+ int write_en:1;
+};
+
+#define to_clk_regmap_gate(_hw) \
+ container_of(to_clk_regmap(_hw), struct clk_regmap_gate, clkr)
+#define __clk_regmap_gate_hw(_p) __clk_regmap_hw(&(_p)->clkr)
+
+extern const struct clk_ops clk_regmap_gate_ops;
+
+#endif
diff --git a/drivers/clk/realtek/clk-regmap-mux.c b/drivers/clk/realtek/clk-regmap-mux.c
new file mode 100644
index 000000000000..bd7eb706e12a
--- /dev/null
+++ b/drivers/clk/realtek/clk-regmap-mux.c
@@ -0,0 +1,63 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2017 Realtek Semiconductor Corporation
+ * Author: Cheng-Yu Lee <cylee12@realtek.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include "clk-regmap-mux.h"
+
+static u8 clk_regmap_mux_get_parent(struct clk_hw *hw)
+{
+ struct clk_regmap_mux *clkm = to_clk_regmap_mux(hw);
+ int num_parents = clk_hw_get_num_parents(hw);
+ u32 val;
+ unsigned long flags = 0;
+
+ if (clkm->lock)
+ spin_lock_irqsave(clkm->lock, flags);
+
+ val = clk_regmap_read(&clkm->clkr, clkm->mux_ofs) >> clkm->shift;
+ if (clkm->lock)
+ spin_unlock_irqrestore(clkm->lock, flags);
+
+ val &= clkm->mask;
+ if (val >= num_parents)
+ return -EINVAL;
+
+ return val;
+}
+
+static int clk_regmap_mux_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct clk_regmap_mux *clkm = to_clk_regmap_mux(hw);
+ unsigned long flags = 0;
+
+ if (clkm->lock)
+ spin_lock_irqsave(clkm->lock, flags);
+
+ clk_regmap_update(&clkm->clkr, clkm->mux_ofs, clkm->mask << clkm->shift,
+ index << clkm->shift);
+
+ if (clkm->lock)
+ spin_unlock_irqrestore(clkm->lock, flags);
+ return 0;
+}
+
+const struct clk_ops clk_regmap_mux_ops = {
+ .get_parent = clk_regmap_mux_get_parent,
+ .set_parent = clk_regmap_mux_set_parent,
+ .determine_rate = __clk_mux_determine_rate,
+};
+EXPORT_SYMBOL_GPL(clk_regmap_mux_ops);
+
+const struct clk_ops clk_regmap_mux_ro_ops = {
+ .get_parent = clk_regmap_mux_get_parent,
+};
+EXPORT_SYMBOL_GPL(clk_regmap_mux_ro_ops);
+
diff --git a/drivers/clk/realtek/clk-regmap-mux.h b/drivers/clk/realtek/clk-regmap-mux.h
new file mode 100644
index 000000000000..36895f03c0f4
--- /dev/null
+++ b/drivers/clk/realtek/clk-regmap-mux.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2017 Realtek Semiconductor Corporation
+ * Author: Cheng-Yu Lee <cylee12@realtek.com>
+ */
+
+#ifndef __CLK_REALTEK_CLK_MMIO_MUX_H
+#define __CLK_REALTEK_CLK_MMIO_MUX_H
+
+#include "common.h"
+
+struct clk_regmap_mux {
+ struct clk_regmap clkr;
+ int mux_ofs;
+ unsigned int mask;
+ unsigned int shift;
+ spinlock_t *lock;
+};
+
+#define to_clk_regmap_mux(_hw) \
+ container_of(to_clk_regmap(_hw), struct clk_regmap_mux, clkr)
+#define __clk_regmap_mux_hw(_p) __clk_regmap_hw(&(_p)->clkr)
+
+extern const struct clk_ops clk_regmap_mux_ops;
+
+#endif
diff --git a/drivers/clk/realtek/common.c b/drivers/clk/realtek/common.c
new file mode 100644
index 000000000000..0c5dc5ce4682
--- /dev/null
+++ b/drivers/clk/realtek/common.c
@@ -0,0 +1,320 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2019 Realtek Semiconductor Corporation
+ * Author: Cheng-Yu Lee <cylee12@realtek.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/pm.h>
+#include "common.h"
+#include "clk-pll.h"
+#include "clk-regmap-gate.h"
+#include "clk-regmap-mux.h"
+
+static int rtk_clk_suspend(struct device *dev)
+{
+ struct rtk_clk_data *data = dev_get_drvdata(dev);
+ int i;
+
+ for (i = 0; i < data->pm_data_num; i++) {
+ struct clk_pm_data *pm_data = &data->pm_data[i];
+
+ regmap_read(data->regmap, pm_data->ofs, &pm_data->val);
+ }
+ return 0;
+}
+
+static int rtk_clk_resume(struct device *dev)
+{
+ struct rtk_clk_data *data = dev_get_drvdata(dev);
+ int i;
+
+ for (i = data->pm_data_num - 1; i >= 0; i--) {
+ struct clk_pm_data *pm_data = &data->pm_data[i];
+ uint32_t val = pm_data->val;
+ uint32_t mask = ~0x0;
+
+ if (pm_data->write_en_bits)
+ val |= pm_data->write_en_bits;
+ if (pm_data->ignore_bits)
+ mask &= ~pm_data->ignore_bits;
+ dev_info(dev, "resuming: ofs=%03x, vs=%08x vr=%08x m=%08x\n",
+ pm_data->ofs, pm_data->val, val, mask);
+ regmap_update_bits(data->regmap, pm_data->ofs, mask, val);
+ }
+ return 0;
+}
+
+const struct dev_pm_ops rtk_clk_pm_ops = {
+ .suspend = rtk_clk_suspend,
+ .resume = rtk_clk_resume,
+};
+
+struct rtk_clk_data *alloc_rtk_clk_data(int clk_num)
+{
+ struct rtk_clk_data *data;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return NULL;
+
+ data->clk_num = clk_num;
+ data->clk_data.clk_num = clk_num;
+ data->clk_data.clks = kcalloc(clk_num, sizeof(*data->clk_data.clks),
+ GFP_KERNEL);
+ if (!data->clk_data.clks)
+ goto free_data;
+ return data;
+
+free_data:
+ kfree(data->clk_data.clks);
+ kfree(data);
+ return NULL;
+}
+
+void free_rtk_clk_data(struct rtk_clk_data *data)
+{
+ kfree(data->clk_data.clks);
+ kfree(data);
+}
+
+static inline
+int __cell_clk_add(struct clk_onecell_data *clk_data, int i, struct clk *clk)
+{
+ if (clk_data->clks[i]) {
+ pr_err("%s: failed to add %pC, cell%d is used by %pC\n",
+ __func__, clk, i, clk_data->clks[i]);
+ return -EINVAL;
+ }
+ clk_data->clks[i] = clk;
+ return 0;
+}
+
+#define CLK_TYPE_DEFAULT (0x0)
+#define CLK_TYPE_REGMAP (0x8)
+#define CLK_TYPE_REGMAP_PLL (0x1 | CLK_TYPE_REGMAP)
+#define CLK_TYPE_REGMAP_MUX (0x2 | CLK_TYPE_REGMAP)
+#define CLK_TYPE_REGMAP_GATE (0x3 | CLK_TYPE_REGMAP)
+
+static inline int __hw_to_type(struct clk_hw *hw)
+{
+ const struct clk_ops *ops = hw->init->ops;
+
+ if (ops == &clk_pll_ops || ops == &clk_pll_div_ops)
+ return CLK_TYPE_REGMAP_PLL;
+ if (ops == &clk_regmap_mux_ops)
+ return CLK_TYPE_REGMAP_MUX;
+ if (ops == &clk_regmap_gate_ops)
+ return CLK_TYPE_REGMAP_GATE;
+#ifdef CONFIG_CLK_PLL_DIF
+ /*
+ * clk_pll_dif is not based struct clk_pll, so
+ * return as CLK_TYPE_REGMAP to setup internal
+ * clk_reg
+ */
+ if (ops == &clk_pll_dif_ops)
+ return CLK_TYPE_REGMAP;
+#endif
+#ifdef CONFIG_CLK_PLL_PSAUD
+ /*
+ * clk_pll_psaud is not based struct clk_pll, so
+ * return as CLK_TYPE_REGMAP to setup internal
+ * clk_reg
+ */
+ if (ops == &clk_pll_psaud_ops)
+ return CLK_TYPE_REGMAP;
+#endif
+
+ return CLK_TYPE_DEFAULT;
+}
+
+static
+struct clk *rtk_clk_register_hw(struct device *dev, struct regmap *regmap,
+ struct clk_hw *hw)
+{
+ int type;
+
+ type = __hw_to_type(hw);
+ if (type & CLK_TYPE_REGMAP) {
+ struct clk_regmap *clkr = to_clk_regmap(hw);
+
+ clkr->regmap = regmap;
+ }
+
+ return clk_register(dev, hw);
+}
+
+int rtk_clk_add_hws(struct device *dev, struct rtk_clk_data *data,
+ struct clk_hw **hws, int num)
+{
+ struct clk_onecell_data *clk_data = &data->clk_data;
+ struct regmap *regmap = data->regmap;
+ int i;
+
+ for (i = 0; i < num; i++) {
+ struct clk_hw *hw = hws[i];
+ const char *name;
+ struct clk *clk;
+
+ if (IS_ERR(hw))
+ __cell_clk_add(clk_data, i, ERR_CAST(hw));
+ if (IS_ERR_OR_NULL(hw))
+ continue;
+
+ name = hw->init->name;
+ clk = rtk_clk_register_hw(dev, regmap, hw);
+ if (IS_ERR(clk)) {
+ pr_err("%s: failed to add hw%d(%s): %ld\n", __func__,
+ i, name, PTR_ERR(clk));
+ continue;
+ }
+
+ clk_register_clkdev(clk, name, NULL);
+ __cell_clk_add(clk_data, i, clk);
+ }
+ return 0;
+}
+
+static
+struct clk *rtk_clk_register_composite(struct device *dev,
+ struct regmap *regmap,
+ struct clk_composite_data *comp)
+{
+ struct clk_regmap_mux *clkm = NULL;
+ const struct clk_ops *mux_op = NULL;
+ struct clk_hw *mux_hw = NULL;
+ struct clk_regmap_gate *clkg = NULL;
+ const struct clk_ops *gate_op = NULL;
+ struct clk_hw *gate_hw = NULL;
+ struct clk *clk;
+
+ if (comp->mux_ofs != CLK_OFS_INVALID) {
+ clkm = kzalloc(sizeof(*clkm), GFP_KERNEL);
+ if (!clkm) {
+ clk = ERR_PTR(-ENOMEM);
+ goto check_err;
+ }
+
+ clkm->mux_ofs = comp->mux_ofs;
+ clkm->mask = BIT(comp->mux_width) - 1;
+ clkm->shift = comp->mux_shift;
+ clkm->clkr.regmap = regmap;
+
+ mux_op = &clk_regmap_mux_ops;
+ mux_hw = &__clk_regmap_mux_hw(clkm);
+ }
+
+ if (comp->gate_ofs != CLK_OFS_INVALID) {
+ clkg = kzalloc(sizeof(*clkg), GFP_KERNEL);
+ if (!clkg) {
+ clk = ERR_PTR(-ENOMEM);
+ goto check_err;
+ }
+
+ clkg->gate_ofs = comp->gate_ofs;
+ clkg->bit_idx = comp->gate_shift;
+ clkg->write_en = comp->gate_write_en;
+ clkg->clkr.regmap = regmap;
+
+ gate_op = &clk_regmap_gate_ops;
+ gate_hw = &__clk_regmap_gate_hw(clkg);
+ }
+
+ clk = clk_register_composite(NULL, comp->name, comp->parent_names,
+ comp->num_parents, mux_hw, mux_op,
+ NULL, NULL, gate_hw, gate_op, comp->flags);
+check_err:
+ if (IS_ERR(clk)) {
+ kfree(clkm);
+ kfree(clkg);
+ }
+ return clk;
+}
+
+int rtk_clk_add_composites(struct device *dev, struct rtk_clk_data *data,
+ struct clk_composite_data *comps, int num)
+{
+ struct clk_onecell_data *clk_data = &data->clk_data;
+ struct regmap *regmap = data->regmap;
+ int i;
+
+ for (i = 0; i < num; i++) {
+ struct clk_composite_data *comp = &comps[i];
+ const char *name = comp->name;
+ struct clk *clk;
+
+ clk = rtk_clk_register_composite(dev, regmap, comp);
+ if (IS_ERR(clk)) {
+ pr_err("%s: failed to add composite%d(%s): %ld\n",
+ __func__, i, name, PTR_ERR(clk));
+ continue;
+ }
+
+ clk_register_clkdev(clk, name, NULL);
+ __cell_clk_add(clk_data, comp->id, clk);
+ }
+
+ return 0;
+}
+
+static
+struct clk *rtk_clk_register_gate(struct device *dev, struct regmap *regmap,
+ struct clk_gate_data *gate)
+{
+ struct clk_regmap_gate *clkg;
+ struct clk_init_data init = { 0 };
+ struct clk_hw *hw;
+
+ clkg = kzalloc(sizeof(*clkg), GFP_KERNEL);
+ if (!clkg)
+ return ERR_PTR(-ENOMEM);
+
+ clkg->gate_ofs = gate->gate_ofs;
+ clkg->bit_idx = gate->gate_shift;
+ clkg->write_en = gate->gate_write_en;
+ clkg->clkr.regmap = regmap;
+
+ init.name = gate->name;
+ init.ops = &clk_regmap_gate_ops;
+ init.flags = gate->flags;
+ if (gate->parent) {
+ init.parent_names = &gate->parent;
+ init.num_parents = 1;
+ }
+
+ hw = &__clk_regmap_gate_hw(clkg);
+ hw->init = &init;
+ return clk_register(dev, hw);
+}
+
+int rtk_clk_add_gates(struct device *dev, struct rtk_clk_data *data,
+ struct clk_gate_data *gates, int num)
+{
+ struct clk_onecell_data *clk_data = &data->clk_data;
+ struct regmap *regmap = data->regmap;
+ int i;
+
+ for (i = 0; i < num; i++) {
+ struct clk_gate_data *gate = &gates[i];
+ const char *name = gate->name;
+ struct clk *clk;
+
+ clk = rtk_clk_register_gate(dev, regmap, gate);
+ if (IS_ERR(clk)) {
+ pr_err("%s: failed to add gate%d(%s): %ld\n", __func__,
+ i, name, PTR_ERR(clk));
+ continue;
+ }
+
+ clk_register_clkdev(clk, name, NULL);
+ __cell_clk_add(clk_data, gate->id, clk);
+ }
+
+ return 0;
+}
+
diff --git a/drivers/clk/realtek/common.h b/drivers/clk/realtek/common.h
new file mode 100644
index 000000000000..c736b254852f
--- /dev/null
+++ b/drivers/clk/realtek/common.h
@@ -0,0 +1,123 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2016-2019 Realtek Semiconductor Corporation
+ * Author: Cheng-Yu Lee <cylee12@realtek.com>
+ */
+
+#ifndef __CLK_REALTEK_COMMON_H
+#define __CLK_REALTEK_COMMON_H
+
+#include <linux/bitops.h>
+#include <linux/clk-provider.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+
+struct device;
+struct platform_device;
+
+struct clk_regmap {
+ struct clk_hw hw;
+ struct regmap *regmap;
+};
+
+#define to_clk_regmap(_hw) container_of(_hw, struct clk_regmap, hw)
+#define __clk_regmap_hw(_p) ((_p)->hw)
+
+static inline
+void clk_regmap_write(struct clk_regmap *clkr, uint32_t ofs, uint32_t val)
+{
+ pr_debug("%s: ofs=%03x, val=%08x\n", __func__, ofs, val);
+ regmap_write(clkr->regmap, ofs, val);
+}
+
+static inline
+uint32_t clk_regmap_read(struct clk_regmap *clkr, uint32_t ofs)
+{
+ uint32_t val = 0;
+
+ regmap_read(clkr->regmap, ofs, &val);
+ pr_debug("%s: ofs=%03x, val=%08x\n", __func__, ofs, val);
+ return val;
+}
+
+static inline void clk_regmap_update(struct clk_regmap *clkr, uint32_t ofs,
+ uint32_t mask, uint32_t val)
+{
+ pr_debug("%s: ofs=%03x, mask=%08x, val=%08x\n", __func__, ofs,
+ mask, val);
+ regmap_update_bits(clkr->regmap, ofs, mask, val);
+}
+
+/* ofs check */
+#define CLK_OFS_INVALID (-1)
+#define CLK_OFS_IS_VALID(_ofs) ((_ofs) != CLK_OFS_INVALID)
+
+struct clk_composite_data {
+ int id;
+ const char *name;
+ unsigned long flags;
+ struct clk *clk;
+
+ int gate_ofs;
+ int gate_shift;
+ int gate_write_en;
+
+ int mux_ofs;
+ int mux_width;
+ int mux_shift;
+ const char * const *parent_names;
+ int num_parents;
+};
+
+struct clk_gate_data {
+ int id;
+ const char *name;
+ const char *parent;
+ unsigned long flags;
+ struct clk *clk;
+
+ int gate_ofs;
+ int gate_shift;
+ int gate_write_en;
+};
+
+#define CLK_GATE_DATA(_id, _name, _parent, _flags, _ofs, _shift, _write_en) \
+{ \
+ .id = _id, \
+ .name = _name, \
+ .parent = _parent, \
+ .flags = _flags, \
+ .gate_ofs = _ofs, \
+ .gate_shift = _shift, \
+ .gate_write_en = _write_en, \
+}
+
+struct clk_pm_data {
+ int ofs;
+ uint32_t ignore_bits;
+ uint32_t write_en_bits;
+ uint32_t val;
+};
+
+struct rtk_clk_data {
+ int clk_num;
+ struct regmap *regmap;
+ struct clk_onecell_data clk_data;
+ struct clk_pm_data *pm_data;
+ int pm_data_num;
+};
+
+struct rtk_clk_data *alloc_rtk_clk_data(int clk_num);
+void free_rtk_clk_data(struct rtk_clk_data *data);
+int rtk_clk_add_hws(struct device *dev, struct rtk_clk_data *data,
+ struct clk_hw **hws, int num);
+int rtk_clk_add_composites(struct device *dev, struct rtk_clk_data *data,
+ struct clk_composite_data *comps, int num);
+int rtk_clk_add_gates(struct device *dev, struct rtk_clk_data *data,
+ struct clk_gate_data *gates, int num);
+extern const struct dev_pm_ops rtk_clk_pm_ops;
+
+#endif /* __CLK_REALTEK_COMMON_H */
+
--
2.24.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 5/6] clk: realtek: add rtd1619 controllers
2019-12-03 7:35 [PATCH 0/6] arm64: Realtek RTD1619 clock and reset controllers James Tai
` (3 preceding siblings ...)
2019-12-03 7:35 ` [PATCH 4/6] clk: realtek: add reset controller " James Tai
@ 2019-12-03 7:35 ` James Tai
2019-12-03 7:35 ` [PATCH 6/6] dt-bindings: clk: realtek: add rtd1619 clock controller bindings James Tai
2019-12-03 8:15 ` [PATCH 0/6] arm64: Realtek RTD1619 clock and reset controllers Andreas Färber
6 siblings, 0 replies; 9+ messages in thread
From: James Tai @ 2019-12-03 7:35 UTC (permalink / raw)
To: Andreas Färber
Cc: Palmer Dabbelt, Paul Walmsley, Matthias Brugger, linux-kernel,
linux-riscv, linux-arm-kernel, linux-mediatek, cylee12,
Michael Turquette, Stephen Boyd, linux-clk
From: cylee12 <cylee12@realtek.com>
This patch adds CRT controller and ISO controller for RTD1619 SoC.
Signed-off-by: Cheng-Yu Lee <cylee12@realtek.com>
Signed-off-by: James Tai <james.tai@realtek.com>
---
drivers/clk/realtek/Kconfig | 10 +
drivers/clk/realtek/Makefile | 2 +
drivers/clk/realtek/clk-rtd1619-cc.c | 553 +++++++++++++++++++++++++++
drivers/clk/realtek/clk-rtd1619-ic.c | 112 ++++++
4 files changed, 677 insertions(+)
create mode 100644 drivers/clk/realtek/clk-rtd1619-cc.c
create mode 100644 drivers/clk/realtek/clk-rtd1619-ic.c
diff --git a/drivers/clk/realtek/Kconfig b/drivers/clk/realtek/Kconfig
index 8e7e7edf64dd..43c55be25eba 100644
--- a/drivers/clk/realtek/Kconfig
+++ b/drivers/clk/realtek/Kconfig
@@ -9,3 +9,13 @@ config CLK_PLL_PSAUD
config CLK_PLL_DIF
bool
+
+config COMMON_CLK_RTD1619
+ bool "RTD1619 Clock Controller"
+ depends on ARCH_REALTEK || COMPILE_TEST
+ select COMMON_CLK_REALTEK
+ select CLK_PLL_PSAUD
+ select CLK_PLL_DIF
+ default ARCH_REALTEK
+ ---help---
+ Support for the clock controller on RTD1619
diff --git a/drivers/clk/realtek/Makefile b/drivers/clk/realtek/Makefile
index 43f8bd71c0c8..24af3dbe2006 100644
--- a/drivers/clk/realtek/Makefile
+++ b/drivers/clk/realtek/Makefile
@@ -8,3 +8,5 @@ clk-rtk-y += clk-pll.o
clk-rtk-$(CONFIG_CLK_PLL_PSAUD) += clk-pll-psaud.o
clk-rtk-$(CONFIG_CLK_PLL_DIF) += clk-pll-dif.o
clk-rtk-y += reset.o
+clk-rtk-$(CONFIG_COMMON_CLK_RTD1619) += clk-rtd1619-cc.o
+clk-rtk-$(CONFIG_COMMON_CLK_RTD1619) += clk-rtd1619-ic.o
diff --git a/drivers/clk/realtek/clk-rtd1619-cc.c b/drivers/clk/realtek/clk-rtd1619-cc.c
new file mode 100644
index 000000000000..c799ffd4cc7a
--- /dev/null
+++ b/drivers/clk/realtek/clk-rtd1619-cc.c
@@ -0,0 +1,553 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2018-2019 Realtek Semiconductor Corporation
+ * Author: Cheng-Yu Lee <cylee12@realtek.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/bitops.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/syscon.h>
+#include "common.h"
+#include "clk-pll.h"
+#include "clk-regmap-gate.h"
+#include "clk-regmap-mux.h"
+#include "reset.h"
+#include <dt-bindings/clock/rtk,clock-rtd1619.h>
+#include <dt-bindings/reset/rtk,reset-rtd1619.h>
+
+#define DIV_DV(_r, _d, _v) { .rate = _r, .div = _d, .val = _v, }
+#define FREQ_NF_MASK (0x7FFFF)
+#define FREQ_NF(_r, _n, _f) { .rate = _r, .val = ((_n) << 11) | (_f), }
+#define FREQ_MNO_MASK (0x63FF0)
+#define FREQ_MNO(_r, _m, _n, _o) \
+ { .rate = _r, .val = ((_m) << 4) | ((_n) << 12) | ((_o) << 17), }
+
+static const char * const default_parent[] = { "osc27m" };
+
+static const struct div_table scpu_div_tbl[] = {
+ DIV_DV(1000000000, 1, 0),
+ DIV_DV(500000000, 2, 0x88),
+ DIV_DV(350000000, 3, 0x8C),
+ DIV_DV(250000000, 4, 0x90),
+ DIV_DV(200000000, 8, 0xA0),
+ DIV_DV(100000000, 10, 0xA8),
+ DIV_TABLE_END
+};
+
+static const struct freq_table scpu_tbl[] = {
+ FREQ_NF(1000000000, 34, 75),
+ FREQ_NF(1100000000, 37, 1517),
+ FREQ_NF(1200000000, 41, 910),
+ FREQ_NF(1300000000, 45, 303),
+ FREQ_NF(1400000000, 48, 1745),
+ FREQ_NF(1500000000, 52, 1137),
+ FREQ_NF(1600000000, 56, 530),
+ FREQ_NF(1700000000, 59, 1972),
+ FREQ_NF(1800000000, 63, 1365),
+ FREQ_NF(1900000000, 67, 758),
+ FREQ_NF(2000000000, 71, 151),
+ /* init-value mapping */
+ FREQ_NF(1000000000, 35, 0),
+ FREQ_NF(1200000000, 41, 0),
+ FREQ_NF(1800000000, 65, 0),
+ FREQ_NF(1800000000, 64, 0),
+ FREQ_TABLE_END
+};
+
+static struct clk_pll_div pll_scpu = {
+ .div_ofs = 0x030,
+ .div_shift = 6,
+ .div_width = 8,
+ .div_tbl = scpu_div_tbl,
+ .clkp = {
+ .ssc_ofs = 0x500,
+ .pll_ofs = CLK_OFS_INVALID,
+ .freq_loc = CLK_PLL_CONF_FREQ_LOC_SSC1,
+ .freq_tbl = scpu_tbl,
+ .freq_mask = FREQ_NF_MASK,
+ .clkr.hw.init = &(struct clk_init_data) {
+ .name = "pll_scpu",
+ .ops = &clk_pll_div_ops,
+ .parent_names = default_parent,
+ .num_parents = 1,
+ .flags = CLK_IGNORE_UNUSED |
+ CLK_GET_RATE_NOCACHE,
+ },
+ },
+};
+
+static const struct div_table bus_div_tbl[] = {
+ DIV_DV(257000000, 1, 0),
+ DIV_DV(129000000, 2, 2),
+ DIV_DV(65000000, 4, 3),
+ DIV_TABLE_END
+};
+
+static const struct freq_table bus_tbl[] = {
+ FREQ_NF(513000000, 35, 0),
+ FREQ_NF(400000000, 26, 1289),
+ FREQ_TABLE_END
+};
+
+static struct clk_pll_div pll_bus = {
+ .div_ofs = 0x030,
+ .div_shift = 0,
+ .div_width = 2,
+ .div_tbl = bus_div_tbl,
+ .clkp = {
+ .ssc_ofs = 0x520,
+ .pll_ofs = CLK_OFS_INVALID,
+ .freq_loc = CLK_PLL_CONF_FREQ_LOC_SSC1,
+ .freq_tbl = bus_tbl,
+ .freq_mask = FREQ_NF_MASK,
+ .clkr.hw.init = &(struct clk_init_data) {
+ .name = "pll_bus",
+ .ops = &clk_pll_div_ops,
+ .parent_names = default_parent,
+ .num_parents = 1,
+ .flags = CLK_IGNORE_UNUSED |
+ CLK_GET_RATE_NOCACHE,
+ },
+ },
+};
+
+static struct clk_fixed_factor clk_sys = {
+ .div = 1,
+ .mult = 1,
+ .hw.init = &(struct clk_init_data) {
+ .name = "clk_sys",
+ .ops = &clk_fixed_factor_ops,
+ .parent_names = (const char *[]){ "pll_bus" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static const struct div_table dcsb_div_tbl[] = {
+ DIV_DV(550000000, 1, 0),
+ DIV_DV(275000000, 2, 2),
+ DIV_DV(1, 4, 3),
+ DIV_TABLE_END
+};
+
+static const struct freq_table dcsb_tbl[] = {
+ FREQ_NF(550000000, 38, 0),
+ FREQ_NF(550000000, 37, 1517),
+ FREQ_TABLE_END
+};
+
+static struct clk_pll_div pll_dcsb = {
+ .div_ofs = 0x030,
+ .div_shift = 2,
+ .div_width = 2,
+ .div_tbl = dcsb_div_tbl,
+ .clkp = {
+ .ssc_ofs = 0x540,
+ .pll_ofs = CLK_OFS_INVALID,
+ .freq_loc = CLK_PLL_CONF_FREQ_LOC_SSC1,
+ .freq_tbl = dcsb_tbl,
+ .freq_mask = FREQ_NF_MASK,
+ .clkr.hw.init = &(struct clk_init_data) {
+ .name = "pll_dcsb",
+ .ops = &clk_pll_div_ops,
+ .parent_names = default_parent,
+ .num_parents = 1,
+ .flags = CLK_IGNORE_UNUSED |
+ CLK_GET_RATE_NOCACHE,
+ },
+ },
+};
+
+static struct clk_fixed_factor clk_sysh = {
+ .div = 1,
+ .mult = 1,
+ .hw.init = &(struct clk_init_data) {
+ .name = "clk_sysh",
+ .ops = &clk_fixed_factor_ops,
+ .parent_names = (const char *[]){ "pll_dcsb" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static const struct freq_table ddsx_tbl[] = {
+ FREQ_NF(432000000, 13, 0),
+ FREQ_TABLE_END
+};
+
+static struct clk_pll pll_ddsa = {
+ .ssc_ofs = 0x560,
+ .pll_ofs = 0x120,
+ .pow_loc = CLK_PLL_CONF_POW_LOC_CTL3,
+ .freq_loc = CLK_PLL_CONF_FREQ_LOC_SSC1,
+ .freq_tbl = ddsx_tbl,
+ .freq_mask = FREQ_NF_MASK,
+ .clkr.hw.init = &(struct clk_init_data) {
+ .name = "pll_ddsa",
+ .ops = &clk_pll_ops,
+ .parent_names = default_parent,
+ .num_parents = 1,
+ .flags = CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE,
+ },
+};
+
+static const struct freq_table gpu_tbl[] = {
+ FREQ_NF(300000000, 19, 455),
+ FREQ_NF(400000000, 26, 1289),
+ FREQ_NF(500000000, 34, 75),
+ FREQ_NF(600000000, 41, 910),
+ FREQ_NF(650000000, 45, 303),
+ FREQ_NF(700000000, 48, 1745),
+ FREQ_NF(750000000, 52, 1137),
+ FREQ_NF(800000000, 56, 530),
+ FREQ_NF(850000000, 59, 1971),
+ FREQ_TABLE_END
+};
+
+static struct clk_pll pll_gpu = {
+ .ssc_ofs = 0x5A0,
+ .pll_ofs = 0x1C0,
+ .pow_loc = CLK_PLL_CONF_POW_LOC_CTL2,
+ .freq_loc = CLK_PLL_CONF_FREQ_LOC_SSC1,
+ .freq_tbl = gpu_tbl,
+ .freq_mask = FREQ_NF_MASK,
+ .clkr.hw.init = &(struct clk_init_data) {
+ .name = "pll_gpu",
+ .ops = &clk_pll_ops,
+ .parent_names = default_parent,
+ .num_parents = 1,
+ .flags = CLK_GET_RATE_NOCACHE,
+ },
+};
+
+static const struct freq_table ve_tbl[] = {
+ FREQ_MNO(189000000, 12, 0, 1),
+ FREQ_MNO(270000000, 18, 0, 1),
+ FREQ_MNO(405000000, 13, 0, 0),
+ FREQ_MNO(432000000, 14, 0, 0),
+ FREQ_MNO(459000000, 15, 0, 0),
+ FREQ_MNO(486000000, 16, 0, 0),
+ FREQ_MNO(513000000, 17, 0, 0),
+ FREQ_MNO(540000000, 18, 0, 0),
+ FREQ_MNO(550000000, 59, 2, 0),
+ FREQ_MNO(567000000, 19, 0, 0),
+ FREQ_MNO(594000000, 20, 0, 0),
+ FREQ_MNO(648000000, 22, 0, 0),
+ FREQ_MNO(675000000, 23, 0, 0),
+ FREQ_MNO(702000000, 24, 0, 0),
+ FREQ_MNO(715000000, 51, 1, 0),
+ FREQ_TABLE_END
+};
+
+static struct clk_pll pll_ve1 = {
+ .ssc_ofs = CLK_OFS_INVALID,
+ .pll_ofs = 0x114,
+ .pow_loc = CLK_PLL_CONF_POW_LOC_CTL2,
+ .freq_loc = CLK_PLL_CONF_FREQ_LOC_CTL1,
+ .freq_tbl = ve_tbl,
+ .freq_mask = FREQ_MNO_MASK,
+ .clkr.hw.init = &(struct clk_init_data) {
+ .name = "pll_ve1",
+ .ops = &clk_pll_ops,
+ .parent_names = default_parent,
+ .num_parents = 1,
+ .flags = CLK_GET_RATE_NOCACHE,
+ },
+};
+
+static struct clk_pll pll_ve2 = {
+ .ssc_ofs = CLK_OFS_INVALID,
+ .pll_ofs = 0x1D0,
+ .pow_loc = CLK_PLL_CONF_POW_LOC_CTL2,
+ .freq_loc = CLK_PLL_CONF_FREQ_LOC_CTL1,
+ .freq_tbl = ve_tbl,
+ .freq_mask = FREQ_MNO_MASK,
+ .clkr.hw.init = &(struct clk_init_data) {
+ .name = "pll_ve2",
+ .ops = &clk_pll_ops,
+ .parent_names = default_parent,
+ .num_parents = 1,
+ .flags = CLK_GET_RATE_NOCACHE,
+ },
+};
+
+static struct clk_pll_dif pll_dif = {
+ .ssc_ofs = 0x634,
+ .pll_ofs = 0x624,
+ .clkr.hw.init = &(struct clk_init_data) {
+ .name = "pll_dif",
+ .ops = &clk_pll_dif_ops,
+ .parent_names = default_parent,
+ .num_parents = 1,
+ .flags = CLK_GET_RATE_NOCACHE,
+ },
+};
+
+static struct clk_pll_psaud pll_psaud1a = {
+ .reg = 0x130,
+ .id = CLK_PLL_PSAUD1A,
+ .clkr.hw.init = &(struct clk_init_data) {
+ .name = "pll_psaud1a",
+ .ops = &clk_pll_psaud_ops,
+ .parent_names = default_parent,
+ .num_parents = 1,
+ .flags = CLK_IGNORE_UNUSED | CLK_SET_RATE_UNGATE,
+ },
+};
+
+static struct clk_pll_psaud pll_psaud2a = {
+ .reg = 0x130,
+ .id = CLK_PLL_PSAUD2A,
+ .clkr.hw.init = &(struct clk_init_data) {
+ .name = "pll_psaud2a",
+ .ops = &clk_pll_psaud_ops,
+ .parent_names = default_parent,
+ .num_parents = 1,
+ .flags = CLK_IGNORE_UNUSED | CLK_SET_RATE_UNGATE,
+ },
+};
+
+static struct clk_hw *cc_hws[] = {
+ [CC_PLL_SCPU] = &__clk_pll_div_hw(&pll_scpu),
+ [CC_PLL_BUS] = &__clk_pll_div_hw(&pll_bus),
+ [CC_PLL_DCSB] = &__clk_pll_div_hw(&pll_dcsb),
+ [CC_PLL_DDSA] = &__clk_pll_hw(&pll_ddsa),
+ [CC_PLL_GPU] = &__clk_pll_hw(&pll_gpu),
+ [CC_PLL_VE1] = &__clk_pll_hw(&pll_ve1),
+ [CC_PLL_VE2] = &__clk_pll_hw(&pll_ve2),
+ [CC_PLL_DIF] = &__clk_pll_dif_hw(&pll_dif),
+ [CC_CLK_SYS] = &clk_sys.hw,
+ [CC_CLK_SYSH] = &clk_sysh.hw,
+ [CC_PLL_PSAUD1A] = &__clk_pll_psaud_hw(&pll_psaud1a),
+ [CC_PLL_PSAUD2A] = &__clk_pll_psaud_hw(&pll_psaud2a),
+};
+
+static const char * const ve_parents[] = {
+ "clk_sys",
+ "clk_sysh",
+ "pll_ve1",
+ "pll_ve2",
+};
+
+static struct clk_composite_data cc_composites[] = {
+ {
+ .id = CC_CLK_GPU,
+ .mux_ofs = CLK_OFS_INVALID,
+ .gate_ofs = 0x050,
+ .gate_shift = 18,
+ .gate_write_en = 1,
+ .parent_names = (const char *[]){ "pll_gpu" },
+ .num_parents = 1,
+ .name = "clk_gpu",
+ .flags = CLK_SET_RATE_PARENT,
+ },
+ {
+ .id = CC_CLK_VE1,
+ .gate_ofs = 0x050,
+ .gate_shift = 20,
+ .gate_write_en = 1,
+ .mux_ofs = 0x04C,
+ .mux_width = 3,
+ .mux_shift = 0,
+ .parent_names = ve_parents,
+ .num_parents = ARRAY_SIZE(ve_parents),
+ .name = "clk_ve1",
+ .flags = CLK_SET_RATE_PARENT |
+ CLK_SET_RATE_NO_REPARENT,
+ },
+ {
+ .id = CC_CLK_VE2,
+ .gate_ofs = 0x050,
+ .gate_shift = 22,
+ .gate_write_en = 1,
+ .mux_ofs = 0x04C,
+ .mux_width = 3,
+ .mux_shift = 3,
+ .parent_names = ve_parents,
+ .num_parents = ARRAY_SIZE(ve_parents),
+ .name = "clk_ve2",
+ .flags = CLK_SET_RATE_PARENT |
+ CLK_SET_RATE_NO_REPARENT,
+ },
+ {
+ .id = CC_CLK_VE3,
+ .gate_ofs = 0x05C,
+ .gate_shift = 26,
+ .gate_write_en = 1,
+ .mux_ofs = 0x04C,
+ .mux_width = 3,
+ .mux_shift = 6,
+ .parent_names = ve_parents,
+ .num_parents = ARRAY_SIZE(ve_parents),
+ .name = "clk_ve3",
+ .flags = CLK_SET_RATE_PARENT |
+ CLK_SET_RATE_NO_REPARENT,
+ },
+ {
+ .id = CC_CLK_VE2_BPU,
+ .gate_ofs = CLK_OFS_INVALID,
+ .mux_ofs = 0x04C,
+ .mux_width = 3,
+ .mux_shift = 9,
+ .parent_names = ve_parents,
+ .num_parents = ARRAY_SIZE(ve_parents),
+ .name = "clk_ve2_bpu",
+ .flags = CLK_SET_RATE_PARENT |
+ CLK_SET_RATE_NO_REPARENT,
+ },
+};
+
+#define GATE(_id, _name, _parent, _ofs, _shift) \
+ CLK_GATE_DATA(_id, _name, _parent, 0, _ofs, _shift, 1)
+#define GATE_IGNORE(_id, _name, _parent, _ofs, _shift) \
+ CLK_GATE_DATA(_id, _name, _parent, CLK_IGNORE_UNUSED, _ofs, _shift, 1)
+
+static struct clk_gate_data cc_gates[] = {
+ GATE_IGNORE(CC_CKE_MISC, "misc", NULL, 0x50, 0),
+ GATE(CC_CKE_PCIE0, "pcie0", NULL, 0x50, 2),
+ GATE(CC_CKE_GSPI, "gspi", "misc", 0x50, 6),
+ GATE(CC_CKE_SDS, "sds", NULL, 0x50, 12),
+ GATE_IGNORE(CC_CKE_HDMI, "hdmi", NULL, 0x50, 14),
+ GATE_IGNORE(CC_CKE_TVE, "tve", NULL, 0x50, 24),
+ GATE_IGNORE(CC_CKE_VO, "vo", NULL, 0x50, 26),
+ GATE_IGNORE(CC_CKE_LSADC, "lsadc", NULL, 0x50, 28),
+ GATE(CC_CKE_SE, "se", NULL, 0x50, 30),
+ GATE_IGNORE(CC_CKE_CP, "cp", NULL, 0x54, 2),
+ GATE_IGNORE(CC_CKE_MD, "md", NULL, 0x54, 4),
+ GATE_IGNORE(CC_CKE_TP, "tp", NULL, 0x54, 6),
+ GATE(CC_CKE_RSA, "rsa", NULL, 0x54, 8),
+ GATE(CC_CKE_NF, "nf", NULL, 0x54, 10),
+ GATE(CC_CKE_EMMC, "emmc", NULL, 0x54, 12),
+ GATE(CC_CKE_SD, "sd", NULL, 0x54, 14),
+ GATE(CC_CKE_SDIO_IP, "sdio_ip", NULL, 0x54, 16),
+ GATE(CC_CKE_MIPI, "mipi", NULL, 0x54, 18),
+ GATE(CC_CKE_EMMC_IP, "emmc_ip", NULL, 0x54, 20),
+ GATE(CC_CKE_SDIO, "sdio", NULL, 0x54, 22),
+ GATE(CC_CKE_SD_IP, "sd_ip", NULL, 0x54, 24),
+ GATE(CC_CKE_CABLERX, "cablerx", NULL, 0x54, 26),
+ GATE(CC_CKE_TPB, "tpb", NULL, 0x54, 28),
+ GATE(CC_CKE_SC1, "sc1", "misc", 0x54, 30),
+ GATE(CC_CKE_I2C3, "i2c3", "misc", 0x58, 0),
+ GATE(CC_CKE_JPEG, "jpeg", NULL, 0x58, 4),
+ GATE(CC_CKE_SC0, "sc0", "misc", 0x58, 10),
+ GATE(CC_CKE_HDMIRX, "hdmirx", NULL, 0x58, 26),
+ GATE(CC_CKE_HSE, "hse", NULL, 0x58, 28),
+ GATE(CC_CKE_UR2, "ur2", "misc", 0x58, 30),
+ GATE(CC_CKE_UR1, "ur1", "misc", 0x5C, 0),
+ GATE(CC_CKE_FAN, "fan", "misc", 0x5C, 2),
+ GATE(CC_CKE_SATA_WRAP_SYS, "sata_wrap_sys", NULL, 0x5C, 8),
+ GATE(CC_CKE_SATA_WRAP_SYSH, "sata_wrap_sysh", NULL, 0x5C, 10),
+ GATE(CC_CKE_SATA_MAC_SYSH, "sata_mac_sysh", NULL, 0x5C, 12),
+ GATE(CC_CKE_R2RDSC, "r2rdsc", NULL, 0x5C, 14),
+ GATE(CC_CKE_PCIE1, "pcie1", NULL, 0x5C, 18),
+ GATE(CC_CKE_I2C4, "i2c4", "misc", 0x5C, 20),
+ GATE(CC_CKE_I2C5, "i2c5", "misc", 0x5C, 22),
+ GATE(CC_CKE_EDP, "edp", NULL, 0x5C, 28),
+ GATE_IGNORE(CC_CKE_TSIO_TRX, "tsio_trx", NULL, 0x5C, 30),
+};
+
+static struct rtk_reset_bank cc_reset_banks[] = {
+ { .ofs = 0x00, .write_en = 1, },
+ { .ofs = 0x04, .write_en = 1, },
+ { .ofs = 0x08, .write_en = 1, },
+ { .ofs = 0x0c, .write_en = 1, },
+ { .ofs = 0x14, .write_en = 1, },
+ { .ofs = 0x68, .write_en = 1, },
+};
+
+static struct rtk_reset_initdata cc_reset_initdata = {
+ .banks = cc_reset_banks,
+ .num_banks = ARRAY_SIZE(cc_reset_banks),
+};
+
+static struct clk_pm_data cc_pm_data[] = {
+ /* SOFT_RESET */
+ { .ofs = 0x00, .write_en_bits = 0xAAAAAAAA, },
+ { .ofs = 0x04, .write_en_bits = 0xAAAAAAAA, },
+ { .ofs = 0x08, .write_en_bits = 0x0A80AAAA, },
+ { .ofs = 0x0C, .write_en_bits = 0x2AAAAAAA, },
+ { .ofs = 0x14, .write_en_bits = 0xAA82AA82, },
+ { .ofs = 0x68, .write_en_bits = 0xAAAAAAAA, },
+ /* CLK_EN */
+ { .ofs = 0x50, .write_en_bits = 0xA0A8288A, },
+ { .ofs = 0x54, .write_en_bits = 0xAAAAAAA8, },
+ { .ofs = 0x58, .write_en_bits = 0xA800082A, },
+ { .ofs = 0x5C, .write_en_bits = 0xAAAAAA0A, },
+ /* PLL_GPU */
+ { .ofs = 0x1C4, },
+ { .ofs = 0x5A4, .ignore_bits = ~(0x7FFFF), },
+ /* PLL_VE1 */
+ { .ofs = 0x118, },
+ { .ofs = 0x114, .ignore_bits = ~(0x63FF0), },
+ /* PLL_VE2 */
+ { .ofs = 0x1D4, },
+ { .ofs = 0x1D0, .ignore_bits = ~(0x63FF0), },
+};
+
+static int rtd1619_cc_probe(struct platform_device *pdev)
+{
+ struct device *parent;
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct rtk_clk_data *data;
+ struct regmap *regmap;
+ int ret;
+
+ parent = dev->parent;
+ if (!parent) {
+ dev_err(dev, "no parent crt controller\n");
+ return -ENODEV;
+ }
+
+ regmap = syscon_node_to_regmap(parent->of_node);
+ if (IS_ERR(regmap)) {
+ ret = PTR_ERR(regmap);
+ dev_err(dev, "failed to get regmap form %s: %d\n", np->name,
+ ret);
+ return ret;
+ }
+
+ data = alloc_rtk_clk_data(CC_CLK_MAX);
+ if (!data)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, data);
+ data->regmap = regmap;
+ data->pm_data = cc_pm_data;
+ data->pm_data_num = ARRAY_SIZE(cc_pm_data);
+
+ rtk_clk_add_hws(dev, data, cc_hws, ARRAY_SIZE(cc_hws));
+ rtk_clk_add_composites(dev, data, cc_composites,
+ ARRAY_SIZE(cc_composites));
+ rtk_clk_add_gates(dev, data, cc_gates, ARRAY_SIZE(cc_gates));
+
+ ret = of_clk_add_provider(np, of_clk_src_onecell_get, &data->clk_data);
+ if (ret)
+ dev_err(dev, "failed to add clk provider: %d\n", ret);
+
+ rtk_reset_controller_add(dev, regmap, &cc_reset_initdata);
+
+ return 0;
+}
+
+static const struct of_device_id rtd1619_cc_match[] = {
+ { .compatible = "realtek,rtd1619-cc", },
+ { /* sentinel */ }
+};
+
+static struct platform_driver rtd1619_cc_driver = {
+ .probe = rtd1619_cc_probe,
+ .driver = {
+ .name = "rtk-rtd1619-cc",
+ .of_match_table = rtd1619_cc_match,
+ .pm = &rtk_clk_pm_ops,
+ },
+};
+
+static int __init rtd1619_cc_init(void)
+{
+ return platform_driver_register(&rtd1619_cc_driver);
+}
+core_initcall(rtd1619_cc_init);
diff --git a/drivers/clk/realtek/clk-rtd1619-ic.c b/drivers/clk/realtek/clk-rtd1619-ic.c
new file mode 100644
index 000000000000..9651c9aca26d
--- /dev/null
+++ b/drivers/clk/realtek/clk-rtd1619-ic.c
@@ -0,0 +1,112 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2019 Realtek Semiconductor Corporation
+ * Author: Cheng-Yu Lee <cylee12@realtek.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/bitops.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/syscon.h>
+#include "common.h"
+#include "reset.h"
+#include <dt-bindings/clock/rtk,clock-rtd1619.h>
+
+#define GATE(_id, _name, _parent, _ofs, _shift) \
+ CLK_GATE_DATA(_id, _name, _parent, 0, _ofs, _shift, 0)
+
+static struct clk_gate_data ic_gates[] = {
+ GATE(IC_CKE_CEC0, "cec0", NULL, 0x8c, 2),
+ GATE(IC_CKE_CBUSRX_SYS, "cbusrx_sys", NULL, 0x8c, 3),
+ GATE(IC_CKE_CBUSTX_SYS, "cbustx_sys", NULL, 0x8c, 4),
+ GATE(IC_CKE_CBUS_SYS, "cbus_sys", NULL, 0x8c, 5),
+ GATE(IC_CKE_CBUS_OSC, "cbus_osc", NULL, 0x8c, 6),
+ GATE(IC_CKE_IR, "ir", NULL, 0x8c, 7),
+ GATE(IC_CKE_UR0, "ur0", NULL, 0x8c, 8),
+ GATE(IC_CKE_I2C0, "i2c0", NULL, 0x8c, 9),
+ GATE(IC_CKE_I2C1, "i2c1", NULL, 0x8c, 10),
+ GATE(IC_CKE_ETN_250M, "etn_250m", NULL, 0x8c, 11),
+ GATE(IC_CKE_ETN_SYS, "etn_sys", NULL, 0x8c, 12),
+ GATE(IC_CKE_USB_DRD, "usb_drd", NULL, 0x8c, 13),
+ GATE(IC_CKE_USB_HOST, "usb_host", NULL, 0x8c, 14),
+ GATE(IC_CKE_USB_U3_HOST, "usb_u3_host", NULL, 0x8c, 15),
+ GATE(IC_CKE_USB, "usb", NULL, 0x8c, 16),
+};
+
+static struct rtk_reset_bank ic_reset_banks[] = {
+ { .ofs = 0x88, },
+};
+
+static struct rtk_reset_initdata ic_reset_initdata = {
+ .banks = ic_reset_banks,
+ .num_banks = ARRAY_SIZE(ic_reset_banks),
+};
+
+static struct clk_pm_data ic_pm_data[] = {
+ { .ofs = 0x88, },
+ { .ofs = 0x8C, },
+};
+
+static int rtd1619_ic_probe(struct platform_device *pdev)
+{
+ struct device *parent;
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct rtk_clk_data *data;
+ struct regmap *regmap;
+ int ret;
+
+ parent = dev->parent;
+ if (!parent) {
+ dev_err(dev, "no parent crt controller\n");
+ return -ENODEV;
+ }
+
+ regmap = syscon_node_to_regmap(parent->of_node);
+ if (IS_ERR(regmap)) {
+ ret = PTR_ERR(regmap);
+ dev_err(dev, "failed to get regmap form %s: %d\n", np->name,
+ ret);
+ return ret;
+ }
+
+ data = alloc_rtk_clk_data(IC_CLK_MAX);
+ if (!data)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, data);
+ data->regmap = regmap;
+ data->pm_data = ic_pm_data;
+ data->pm_data_num = ARRAY_SIZE(ic_pm_data);
+
+ rtk_clk_add_gates(dev, data, ic_gates, ARRAY_SIZE(ic_gates));
+
+ ret = of_clk_add_provider(np, of_clk_src_onecell_get, &data->clk_data);
+ if (ret)
+ dev_err(dev, "failed to add clk provider: %d\n", ret);
+
+ rtk_reset_controller_add(dev, regmap, &ic_reset_initdata);
+
+ return 0;
+}
+
+static const struct of_device_id rtd1619_ic_match[] = {
+ { .compatible = "realtek,rtd1619-ic", },
+ { /* sentinel */ }
+};
+
+static struct platform_driver rtd1619_ic_driver = {
+ .probe = rtd1619_ic_probe,
+ .driver = {
+ .name = "rtk-rtd1619-ic",
+ .of_match_table = rtd1619_ic_match,
+ .pm = &rtk_clk_pm_ops,
+ },
+};
+
+static int __init rtd1619_ic_init(void)
+{
+ return platform_driver_register(&rtd1619_ic_driver);
+}
+core_initcall(rtd1619_ic_init);
--
2.24.0
^ permalink raw reply related [flat|nested] 9+ messages in thread