linux-clk.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 3/6] clk: realtek: add common clock support for Realtek SoCs
       [not found] <20191203073540.9321-1-james.tai@realtek.com>
@ 2019-12-03  7:35 ` James Tai
  2019-12-03  7:35 ` [PATCH 4/6] clk: realtek: add reset controller " James Tai
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 6+ 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	[flat|nested] 6+ messages in thread

* [PATCH 4/6] clk: realtek: add reset controller support for Realtek SoCs
       [not found] <20191203073540.9321-1-james.tai@realtek.com>
  2019-12-03  7:35 ` [PATCH 3/6] clk: realtek: add common clock support for Realtek SoCs James Tai
@ 2019-12-03  7:35 ` James Tai
  2019-12-03  7:35 ` [PATCH 5/6] clk: realtek: add rtd1619 controllers James Tai
  2019-12-03  7:35 ` [PATCH 6/6] dt-bindings: clk: realtek: add rtd1619 clock controller bindings James Tai
  3 siblings, 0 replies; 6+ 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 add reset control support for Realtek SoCs.

Signed-off-by: Cheng-Yu Lee <cylee12@realtek.com>
Signed-off-by: James Tai <james.tai@realtek.com>
---
 drivers/clk/realtek/Kconfig  |   1 +
 drivers/clk/realtek/Makefile |   1 +
 drivers/clk/realtek/reset.c  | 107 +++++++++++++++++++++++++++++++++++
 drivers/clk/realtek/reset.h  |  37 ++++++++++++
 4 files changed, 146 insertions(+)
 create mode 100644 drivers/clk/realtek/reset.c
 create mode 100644 drivers/clk/realtek/reset.h

diff --git a/drivers/clk/realtek/Kconfig b/drivers/clk/realtek/Kconfig
index 5bca757dddfa..8e7e7edf64dd 100644
--- a/drivers/clk/realtek/Kconfig
+++ b/drivers/clk/realtek/Kconfig
@@ -1,6 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0-only
 config COMMON_CLK_REALTEK
 	bool "Clock driver for realtek"
+	select RESET_CONTROLLER
 	select MFD_SYSCON
 
 config CLK_PLL_PSAUD
diff --git a/drivers/clk/realtek/Makefile b/drivers/clk/realtek/Makefile
index 050d450db067..43f8bd71c0c8 100644
--- a/drivers/clk/realtek/Makefile
+++ b/drivers/clk/realtek/Makefile
@@ -7,3 +7,4 @@ 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
+clk-rtk-y += reset.o
diff --git a/drivers/clk/realtek/reset.c b/drivers/clk/realtek/reset.c
new file mode 100644
index 000000000000..3f4d1a723b2a
--- /dev/null
+++ b/drivers/clk/realtek/reset.c
@@ -0,0 +1,107 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2019 Realtek Semiconductor Corporation
+ */
+
+#include <linux/of.h>
+#include <linux/device.h>
+#include <linux/regmap.h>
+#include <linux/reset-controller.h>
+#include <linux/slab.h>
+#include "reset.h"
+
+static int rtk_reset_assert(struct reset_controller_dev *rcdev,
+			    unsigned long idx)
+{
+	struct rtk_reset_data *data = to_rtk_reset_controller(rcdev);
+	struct rtk_reset_bank *bank = &data->banks[idx >> 8];
+	uint32_t id   = idx & 0xff;
+	uint32_t mask = bank->write_en ? (0x3 << id) : BIT(id);
+	uint32_t val  = bank->write_en ? (0x2 << id) : 0;
+
+	return regmap_update_bits(data->regmap, bank->ofs, mask, val);
+}
+
+static int rtk_reset_deassert(struct reset_controller_dev *rcdev,
+			      unsigned long idx)
+{
+	struct rtk_reset_data *data = to_rtk_reset_controller(rcdev);
+	struct rtk_reset_bank *bank = &data->banks[idx >> 8];
+	uint32_t id   = idx & 0xff;
+	uint32_t mask = bank->write_en ? (0x3 << id) : BIT(id);
+	uint32_t val  = mask;
+
+	return regmap_update_bits(data->regmap, bank->ofs, mask, val);
+}
+
+static int rtk_reset_reset(struct reset_controller_dev *rcdev,
+			   unsigned long idx)
+{
+	int ret;
+
+	ret = rtk_reset_assert(rcdev, idx);
+	if (ret)
+		return ret;
+
+	return rtk_reset_deassert(rcdev, idx);
+}
+
+static int rtk_reset_status(struct reset_controller_dev *rcdev,
+			    unsigned long idx)
+{
+	struct rtk_reset_data *data = to_rtk_reset_controller(rcdev);
+	struct rtk_reset_bank *bank = &data->banks[idx >> 8];
+	uint32_t id = idx & 0xff;
+	uint32_t val;
+
+	regmap_read(data->regmap, bank->ofs, &val);
+	return !((val >> id) & 1);
+}
+
+static struct reset_control_ops rtk_reset_ops = {
+	.assert   = rtk_reset_assert,
+	.deassert = rtk_reset_deassert,
+	.reset    = rtk_reset_reset,
+	.status   = rtk_reset_status,
+};
+
+static int rtk_of_reset_xlate(struct reset_controller_dev *rcdev,
+			      const struct of_phandle_args *reset_spec)
+{
+	struct rtk_reset_data *data = to_rtk_reset_controller(rcdev);
+	int val;
+
+	val = reset_spec->args[0];
+	if (val >= rcdev->nr_resets)
+		return -EINVAL;
+
+	if (data->id_xlate)
+		return data->id_xlate(val);
+	return val;
+}
+
+
+int rtk_reset_controller_add(struct device *dev, struct regmap *regmap,
+			     struct rtk_reset_initdata *initdata)
+{
+	struct rtk_reset_data *data;
+	struct device_node *np = dev->of_node;
+
+	data = kzalloc(sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+	data->regmap    = regmap;
+	data->num_banks = initdata->num_banks;
+	data->banks     = initdata->banks;
+	data->id_xlate  = initdata->id_xlate;
+
+	data->rcdev.owner     = THIS_MODULE;
+	data->rcdev.ops       = &rtk_reset_ops;
+	data->rcdev.of_node   = np;
+	data->rcdev.nr_resets = initdata->num_banks * 0x100;
+	data->rcdev.of_xlate  = rtk_of_reset_xlate;
+	data->rcdev.of_reset_n_cells = 1;
+
+	return reset_controller_register(&data->rcdev);
+}
+
diff --git a/drivers/clk/realtek/reset.h b/drivers/clk/realtek/reset.h
new file mode 100644
index 000000000000..f0cc7b1045ee
--- /dev/null
+++ b/drivers/clk/realtek/reset.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2019 Realtek Semiconductor Corporation
+ * Author: Cheng-Yu Lee <cylee12@realtek.com>
+ */
+
+#ifndef __CLK_REALTEK_RESET_H
+#define __CLK_REALTEK_RESET_H
+
+#include <linux/reset-controller.h>
+
+struct rtk_reset_bank {
+	uint32_t ofs;
+	uint32_t write_en;
+};
+
+struct rtk_reset_data {
+	struct reset_controller_dev rcdev;
+	struct rtk_reset_bank *banks;
+	uint32_t num_banks;
+	struct regmap *regmap;
+	unsigned long (*id_xlate)(unsigned long id);
+};
+
+#define to_rtk_reset_controller(r) \
+	container_of(r, struct rtk_reset_data, rcdev)
+
+struct rtk_reset_initdata {
+	struct rtk_reset_bank *banks;
+	uint32_t num_banks;
+	unsigned long (*id_xlate)(unsigned long id);
+};
+
+int rtk_reset_controller_add(struct device *dev, struct regmap *regmap,
+			     struct rtk_reset_initdata *initdata);
+
+#endif /* __CLK_REALTEK_RESET_H */
-- 
2.24.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 5/6] clk: realtek: add rtd1619 controllers
       [not found] <20191203073540.9321-1-james.tai@realtek.com>
  2019-12-03  7:35 ` [PATCH 3/6] clk: realtek: add common clock support for Realtek SoCs James Tai
  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
  3 siblings, 0 replies; 6+ 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	[flat|nested] 6+ messages in thread

* [PATCH 6/6] dt-bindings: clk: realtek: add rtd1619 clock controller bindings
       [not found] <20191203073540.9321-1-james.tai@realtek.com>
                   ` (2 preceding siblings ...)
  2019-12-03  7:35 ` [PATCH 5/6] clk: realtek: add rtd1619 controllers James Tai
@ 2019-12-03  7:35 ` James Tai
  3 siblings, 0 replies; 6+ 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, Rob Herring, Mark Rutland,
	linux-clk, devicetree

From: cylee12 <cylee12@realtek.com>

Signed-off-by: Cheng-Yu Lee <cylee12@realtek.com>
Signed-off-by: James Tai <james.tai@realtek.com>
---
 .../bindings/clock/realtek,clocks.txt         | 38 +++++++++++++++++++
 1 file changed, 38 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/clock/realtek,clocks.txt

diff --git a/Documentation/devicetree/bindings/clock/realtek,clocks.txt b/Documentation/devicetree/bindings/clock/realtek,clocks.txt
new file mode 100644
index 000000000000..db101508ac6a
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/realtek,clocks.txt
@@ -0,0 +1,38 @@
+Realtek Clock/Reset Controller
+==============================
+
+Realtek CRT/ISO controller device-tree binding for Realtek Platforms.
+
+This binding uses the common clock binding[1].
+
+The controller node should be the child of a syscon node with the required
+propertise:
+
+- compatible :
+	should contain only one of the following:
+		"realtek,rtd1619-cc" for RTD1619 CRT clock controller,
+		"realtek,rtd1619-ic" for RTD1619 ISO clock controller,
+
+- #clock-cells : should be 1.
+
+- #reset-cells : should be 1.
+
+[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
+
+Example:
+
+	crt@98000000 {
+		compatible = "realtek,rtd1619-crt", "simple-mfd", "syscon";
+		reg = <0x98000000 0x1000>;
+
+		cc: cc@98000000 {
+			compatible = "realtek,rtd1619-cc";
+			#clock-cells = <1>;
+			#reset-cells = <1>;
+		};
+	};
+
+	consumer {
+		clocks = <&cc CC_CKE_GSPI>;
+	};
+
-- 
2.24.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH 4/6] clk: realtek: add reset controller support for Realtek SoCs
  2019-12-03  7:45 ` [PATCH 4/6] clk: realtek: add reset controller support for Realtek SoCs James Tai
@ 2019-12-03 17:46   ` Andreas Färber
  0 siblings, 0 replies; 6+ messages in thread
From: Andreas Färber @ 2019-12-03 17:46 UTC (permalink / raw)
  To: James Tai
  Cc: Paul Walmsley, Matthias Brugger, linux-kernel, linux-riscv,
	linux-arm-kernel, linux-mediatek, linux-realtek-soc, cylee12,
	Michael Turquette, Stephen Boyd, linux-clk, Philipp Zabel

Hi James,

[dropping Palmer, adding Philipp]

$subject: I prefer to have the subject start with uppercase letter, to
clearly distinguish it from the lowercase prefixes (and because that's
how a book or newspaper headline would start, too). Applies also to
other patches in this series, e.g., "reset: Add ..." (cf. below).

Am 03.12.19 um 08:45 schrieb James Tai:
> From: cylee12 <cylee12@realtek.com>

--author.

> 
> This patch add reset control support for Realtek SoCs.

Please don't write "This patch" in a Git commit message. You can
elegantly avoid the patch vs. commit topic by using "This adds" (and
note the grammar for third-person presence vs. imperative in subject).

All SoCs since the beginning of time or just RTD1619 and later?
As you know, we're already using reset-simple driver for RTD1295.

> 
> Signed-off-by: Cheng-Yu Lee <cylee12@realtek.com>
> Signed-off-by: James Tai <james.tai@realtek.com>
> ---
>  drivers/clk/realtek/Kconfig  |   1 +
>  drivers/clk/realtek/Makefile |   1 +
>  drivers/clk/realtek/reset.c  | 107 +++++++++++++++++++++++++++++++++++
>  drivers/clk/realtek/reset.h  |  37 ++++++++++++

Why in the world under clk? I just introduced syscon nodes to cleanly
allow accessing CRT and Iso registers from multiple independent drivers,
and I have patches doing exactly that for RTD1295 and RTD1195. Please
move it into drivers/reset/, also to make sure that for v2 the reset
maintainers actually get CC'ed for review.

>  4 files changed, 146 insertions(+)
>  create mode 100644 drivers/clk/realtek/reset.c
>  create mode 100644 drivers/clk/realtek/reset.h
> 
> diff --git a/drivers/clk/realtek/Kconfig b/drivers/clk/realtek/Kconfig
> index 5bca757dddfa..8e7e7edf64dd 100644
> --- a/drivers/clk/realtek/Kconfig
> +++ b/drivers/clk/realtek/Kconfig
> @@ -1,6 +1,7 @@
>  # SPDX-License-Identifier: GPL-2.0-only
>  config COMMON_CLK_REALTEK
>  	bool "Clock driver for realtek"
> +	select RESET_CONTROLLER

No!

BTW if you would make this Kconfig option "depends on ARCH_REALTEK" then
it would be selected already. However, ARCH_REALTEK || COMPILE_TEST
would be preferable for noticing errors early. It doesn't make sense to
allow selecting this driver for non-Realtek systems unless COMPILE_TEST
asks for it.

>  	select MFD_SYSCON
>  
>  config CLK_PLL_PSAUD
> diff --git a/drivers/clk/realtek/Makefile b/drivers/clk/realtek/Makefile
> index 050d450db067..43f8bd71c0c8 100644
> --- a/drivers/clk/realtek/Makefile
> +++ b/drivers/clk/realtek/Makefile
> @@ -7,3 +7,4 @@ 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
> +clk-rtk-y += reset.o
> diff --git a/drivers/clk/realtek/reset.c b/drivers/clk/realtek/reset.c
> new file mode 100644
> index 000000000000..3f4d1a723b2a
> --- /dev/null
> +++ b/drivers/clk/realtek/reset.c
> @@ -0,0 +1,107 @@
> +// SPDX-License-Identifier: GPL-2.0-only

Is GPL-2.0-or-later possible?

> +/*
> + * Copyright (C) 2019 Realtek Semiconductor Corporation
> + */
> +
> +#include <linux/of.h>
> +#include <linux/device.h>

Sort order.

> +#include <linux/regmap.h>
> +#include <linux/reset-controller.h>
> +#include <linux/slab.h>

White line please ...

> +#include "reset.h"

... or better try to avoid this #include by driver design.

> +
> +static int rtk_reset_assert(struct reset_controller_dev *rcdev,

rtd1619_?

> +			    unsigned long idx)
> +{
> +	struct rtk_reset_data *data = to_rtk_reset_controller(rcdev);

_controller vs. _data looks weird.

> +	struct rtk_reset_bank *bank = &data->banks[idx >> 8];
> +	uint32_t id   = idx & 0xff;
> +	uint32_t mask = bank->write_en ? (0x3 << id) : BIT(id);
> +	uint32_t val  = bank->write_en ? (0x2 << id) : 0;

Peeking into patch 5/6, write_en seems always 1 for CRT and 0 for a
single ISO bank.

Wouldn't it be easier to just keep two different drivers, one for
RTD1619-and-later with the new semantics, and one for the older simpler
semantics? I was really just waiting for you to post a trivial patch
that with an ack from Rob Philipp and I could apply right away. This
here is huge and I don't think you realize yet how convoluted this is
for trying to merge this into mainline within one merge window...

> +
> +	return regmap_update_bits(data->regmap, bank->ofs, mask, val);
> +}
> +
> +static int rtk_reset_deassert(struct reset_controller_dev *rcdev,
> +			      unsigned long idx)
> +{
> +	struct rtk_reset_data *data = to_rtk_reset_controller(rcdev);
> +	struct rtk_reset_bank *bank = &data->banks[idx >> 8];
> +	uint32_t id   = idx & 0xff;
> +	uint32_t mask = bank->write_en ? (0x3 << id) : BIT(id);
> +	uint32_t val  = mask;
> +
> +	return regmap_update_bits(data->regmap, bank->ofs, mask, val);
> +}

The way I first read this code, there's one bit BIT(1) for writing the
new status and one bit BIT(0) for checking current status? Shouldn't
enable/disable then poll for the change to go into effect before
returning? Otherwise the caller may make wrong assumptions.

Or is this like the write_data bit for the irq mux ISR but in bit 1?
Then I am having trouble following this code and wonder why you use
regmap_update_bits() in the first place, if only some bits get written
anyway - use plain regmap_write() then. If I am puzzled, having seen the
other code, then consider it badly maintainable for the community...

You need to make the code self-documenting by using symbolic constants
instead of magic numbers, or add an inline helper. Maybe add comments.

> +
> +static int rtk_reset_reset(struct reset_controller_dev *rcdev,
> +			   unsigned long idx)
> +{
> +	int ret;
> +
> +	ret = rtk_reset_assert(rcdev, idx);
> +	if (ret)
> +		return ret;
> +
> +	return rtk_reset_deassert(rcdev, idx);
> +}

Does the reset core not do this for us? (If not, shouldn't we fix that?)

> +
> +static int rtk_reset_status(struct reset_controller_dev *rcdev,
> +			    unsigned long idx)
> +{
> +	struct rtk_reset_data *data = to_rtk_reset_controller(rcdev);
> +	struct rtk_reset_bank *bank = &data->banks[idx >> 8];
> +	uint32_t id = idx & 0xff;
> +	uint32_t val;
> +
> +	regmap_read(data->regmap, bank->ofs, &val);
> +	return !((val >> id) & 1);

	return !(val & BIT(id)); seems much more readable to me?

Without seeing bit 0 read here, the above enable/disable code would be
even more confusing.

Please share what the register bits are actually called, then we can
discuss what the best way of calling and using them will be.

> +}
> +
> +static struct reset_control_ops rtk_reset_ops = {
> +	.assert   = rtk_reset_assert,
> +	.deassert = rtk_reset_deassert,
> +	.reset    = rtk_reset_reset,
> +	.status   = rtk_reset_status,
> +};
> +
> +static int rtk_of_reset_xlate(struct reset_controller_dev *rcdev,
> +			      const struct of_phandle_args *reset_spec)
> +{
> +	struct rtk_reset_data *data = to_rtk_reset_controller(rcdev);
> +	int val;
> +
> +	val = reset_spec->args[0];
> +	if (val >= rcdev->nr_resets)
> +		return -EINVAL;
> +
> +	if (data->id_xlate)
> +		return data->id_xlate(val);

This does not appear to be used in this series - how do you envision it
to be used? That would need to be covered in the dt-binding then.

> +	return val;
> +}
> +
> +
> +int rtk_reset_controller_add(struct device *dev, struct regmap *regmap,
> +			     struct rtk_reset_initdata *initdata)

Just get that initdata via DT compatible within this reset driver, like
I did for irqchip.

> +{
> +	struct rtk_reset_data *data;
> +	struct device_node *np = dev->of_node;
> +
> +	data = kzalloc(sizeof(*data), GFP_KERNEL);
> +	if (!data)
> +		return -ENOMEM;
> +	data->regmap    = regmap;
> +	data->num_banks = initdata->num_banks;
> +	data->banks     = initdata->banks;
> +	data->id_xlate  = initdata->id_xlate;
> +
> +	data->rcdev.owner     = THIS_MODULE;
> +	data->rcdev.ops       = &rtk_reset_ops;
> +	data->rcdev.of_node   = np;
> +	data->rcdev.nr_resets = initdata->num_banks * 0x100;

This design doesn't make much sense to me? At most a bank is going to
get you 32 resets, 16 in case of the new ones, all much smaller than 256.

The theoretical-here case of mixed sizes you could just handle by
exposing them as individual DT nodes, like we do today, which as a bonus
would make the syscon DT node nicely self-documenting. I.e.,
"realtek,rtd1619-reset" vs. "realtek,rtd1195-reset".

> +	data->rcdev.of_xlate  = rtk_of_reset_xlate;
> +	data->rcdev.of_reset_n_cells = 1;
> +
> +	return reset_controller_register(&data->rcdev);
> +}
> +

Trailing white line.

I would expect a platform_driver here, turning above _add into _probe.

> diff --git a/drivers/clk/realtek/reset.h b/drivers/clk/realtek/reset.h
> new file mode 100644
> index 000000000000..f0cc7b1045ee
> --- /dev/null
> +++ b/drivers/clk/realtek/reset.h
> @@ -0,0 +1,37 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright (C) 2019 Realtek Semiconductor Corporation
> + * Author: Cheng-Yu Lee <cylee12@realtek.com>
> + */
> +
> +#ifndef __CLK_REALTEK_RESET_H
> +#define __CLK_REALTEK_RESET_H
> +
> +#include <linux/reset-controller.h>
> +
> +struct rtk_reset_bank {
> +	uint32_t ofs;
> +	uint32_t write_en;

If we needed this, it might as well be bool with true/false.

> +};
> +
> +struct rtk_reset_data {
> +	struct reset_controller_dev rcdev;
> +	struct rtk_reset_bank *banks;

const?

> +	uint32_t num_banks;
> +	struct regmap *regmap;
> +	unsigned long (*id_xlate)(unsigned long id);

Drop until used?

> +};
> +
> +#define to_rtk_reset_controller(r) \
> +	container_of(r, struct rtk_reset_data, rcdev)
> +
> +struct rtk_reset_initdata {
> +	struct rtk_reset_bank *banks;

const?

> +	uint32_t num_banks;
> +	unsigned long (*id_xlate)(unsigned long id);

Drop until used?

> +};
> +
> +int rtk_reset_controller_add(struct device *dev, struct regmap *regmap,
> +			     struct rtk_reset_initdata *initdata);
> +
> +#endif /* __CLK_REALTEK_RESET_H */

Regards,
Andreas

-- 
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Felix Imendörffer
HRB 36809 (AG Nürnberg)

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 4/6] clk: realtek: add reset controller support for Realtek SoCs
       [not found] <20191203074513.9416-1-james.tai@realtek.com>
@ 2019-12-03  7:45 ` James Tai
  2019-12-03 17:46   ` Andreas Färber
  0 siblings, 1 reply; 6+ messages in thread
From: James Tai @ 2019-12-03  7:45 UTC (permalink / raw)
  To: Andreas Färber
  Cc: Palmer Dabbelt, Paul Walmsley, Matthias Brugger, linux-kernel,
	linux-riscv, linux-arm-kernel, linux-mediatek, linux-realtek-soc,
	cylee12, Michael Turquette, Stephen Boyd, linux-clk

From: cylee12 <cylee12@realtek.com>

This patch add reset control support for Realtek SoCs.

Signed-off-by: Cheng-Yu Lee <cylee12@realtek.com>
Signed-off-by: James Tai <james.tai@realtek.com>
---
 drivers/clk/realtek/Kconfig  |   1 +
 drivers/clk/realtek/Makefile |   1 +
 drivers/clk/realtek/reset.c  | 107 +++++++++++++++++++++++++++++++++++
 drivers/clk/realtek/reset.h  |  37 ++++++++++++
 4 files changed, 146 insertions(+)
 create mode 100644 drivers/clk/realtek/reset.c
 create mode 100644 drivers/clk/realtek/reset.h

diff --git a/drivers/clk/realtek/Kconfig b/drivers/clk/realtek/Kconfig
index 5bca757dddfa..8e7e7edf64dd 100644
--- a/drivers/clk/realtek/Kconfig
+++ b/drivers/clk/realtek/Kconfig
@@ -1,6 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0-only
 config COMMON_CLK_REALTEK
 	bool "Clock driver for realtek"
+	select RESET_CONTROLLER
 	select MFD_SYSCON
 
 config CLK_PLL_PSAUD
diff --git a/drivers/clk/realtek/Makefile b/drivers/clk/realtek/Makefile
index 050d450db067..43f8bd71c0c8 100644
--- a/drivers/clk/realtek/Makefile
+++ b/drivers/clk/realtek/Makefile
@@ -7,3 +7,4 @@ 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
+clk-rtk-y += reset.o
diff --git a/drivers/clk/realtek/reset.c b/drivers/clk/realtek/reset.c
new file mode 100644
index 000000000000..3f4d1a723b2a
--- /dev/null
+++ b/drivers/clk/realtek/reset.c
@@ -0,0 +1,107 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2019 Realtek Semiconductor Corporation
+ */
+
+#include <linux/of.h>
+#include <linux/device.h>
+#include <linux/regmap.h>
+#include <linux/reset-controller.h>
+#include <linux/slab.h>
+#include "reset.h"
+
+static int rtk_reset_assert(struct reset_controller_dev *rcdev,
+			    unsigned long idx)
+{
+	struct rtk_reset_data *data = to_rtk_reset_controller(rcdev);
+	struct rtk_reset_bank *bank = &data->banks[idx >> 8];
+	uint32_t id   = idx & 0xff;
+	uint32_t mask = bank->write_en ? (0x3 << id) : BIT(id);
+	uint32_t val  = bank->write_en ? (0x2 << id) : 0;
+
+	return regmap_update_bits(data->regmap, bank->ofs, mask, val);
+}
+
+static int rtk_reset_deassert(struct reset_controller_dev *rcdev,
+			      unsigned long idx)
+{
+	struct rtk_reset_data *data = to_rtk_reset_controller(rcdev);
+	struct rtk_reset_bank *bank = &data->banks[idx >> 8];
+	uint32_t id   = idx & 0xff;
+	uint32_t mask = bank->write_en ? (0x3 << id) : BIT(id);
+	uint32_t val  = mask;
+
+	return regmap_update_bits(data->regmap, bank->ofs, mask, val);
+}
+
+static int rtk_reset_reset(struct reset_controller_dev *rcdev,
+			   unsigned long idx)
+{
+	int ret;
+
+	ret = rtk_reset_assert(rcdev, idx);
+	if (ret)
+		return ret;
+
+	return rtk_reset_deassert(rcdev, idx);
+}
+
+static int rtk_reset_status(struct reset_controller_dev *rcdev,
+			    unsigned long idx)
+{
+	struct rtk_reset_data *data = to_rtk_reset_controller(rcdev);
+	struct rtk_reset_bank *bank = &data->banks[idx >> 8];
+	uint32_t id = idx & 0xff;
+	uint32_t val;
+
+	regmap_read(data->regmap, bank->ofs, &val);
+	return !((val >> id) & 1);
+}
+
+static struct reset_control_ops rtk_reset_ops = {
+	.assert   = rtk_reset_assert,
+	.deassert = rtk_reset_deassert,
+	.reset    = rtk_reset_reset,
+	.status   = rtk_reset_status,
+};
+
+static int rtk_of_reset_xlate(struct reset_controller_dev *rcdev,
+			      const struct of_phandle_args *reset_spec)
+{
+	struct rtk_reset_data *data = to_rtk_reset_controller(rcdev);
+	int val;
+
+	val = reset_spec->args[0];
+	if (val >= rcdev->nr_resets)
+		return -EINVAL;
+
+	if (data->id_xlate)
+		return data->id_xlate(val);
+	return val;
+}
+
+
+int rtk_reset_controller_add(struct device *dev, struct regmap *regmap,
+			     struct rtk_reset_initdata *initdata)
+{
+	struct rtk_reset_data *data;
+	struct device_node *np = dev->of_node;
+
+	data = kzalloc(sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+	data->regmap    = regmap;
+	data->num_banks = initdata->num_banks;
+	data->banks     = initdata->banks;
+	data->id_xlate  = initdata->id_xlate;
+
+	data->rcdev.owner     = THIS_MODULE;
+	data->rcdev.ops       = &rtk_reset_ops;
+	data->rcdev.of_node   = np;
+	data->rcdev.nr_resets = initdata->num_banks * 0x100;
+	data->rcdev.of_xlate  = rtk_of_reset_xlate;
+	data->rcdev.of_reset_n_cells = 1;
+
+	return reset_controller_register(&data->rcdev);
+}
+
diff --git a/drivers/clk/realtek/reset.h b/drivers/clk/realtek/reset.h
new file mode 100644
index 000000000000..f0cc7b1045ee
--- /dev/null
+++ b/drivers/clk/realtek/reset.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2019 Realtek Semiconductor Corporation
+ * Author: Cheng-Yu Lee <cylee12@realtek.com>
+ */
+
+#ifndef __CLK_REALTEK_RESET_H
+#define __CLK_REALTEK_RESET_H
+
+#include <linux/reset-controller.h>
+
+struct rtk_reset_bank {
+	uint32_t ofs;
+	uint32_t write_en;
+};
+
+struct rtk_reset_data {
+	struct reset_controller_dev rcdev;
+	struct rtk_reset_bank *banks;
+	uint32_t num_banks;
+	struct regmap *regmap;
+	unsigned long (*id_xlate)(unsigned long id);
+};
+
+#define to_rtk_reset_controller(r) \
+	container_of(r, struct rtk_reset_data, rcdev)
+
+struct rtk_reset_initdata {
+	struct rtk_reset_bank *banks;
+	uint32_t num_banks;
+	unsigned long (*id_xlate)(unsigned long id);
+};
+
+int rtk_reset_controller_add(struct device *dev, struct regmap *regmap,
+			     struct rtk_reset_initdata *initdata);
+
+#endif /* __CLK_REALTEK_RESET_H */
-- 
2.24.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2019-12-03 17:46 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <20191203073540.9321-1-james.tai@realtek.com>
2019-12-03  7:35 ` [PATCH 3/6] clk: realtek: add common clock support for Realtek SoCs James Tai
2019-12-03  7:35 ` [PATCH 4/6] clk: realtek: add reset controller " James Tai
2019-12-03  7:35 ` [PATCH 5/6] clk: realtek: add rtd1619 controllers James Tai
2019-12-03  7:35 ` [PATCH 6/6] dt-bindings: clk: realtek: add rtd1619 clock controller bindings James Tai
     [not found] <20191203074513.9416-1-james.tai@realtek.com>
2019-12-03  7:45 ` [PATCH 4/6] clk: realtek: add reset controller support for Realtek SoCs James Tai
2019-12-03 17:46   ` Andreas Färber

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).