[RFC 3/4] clk: Add Realtek RTD1295

From: Andreas FÃrber
Date: Thu Aug 17 2017 - 07:20:55 EST


Add two clock controller drivers for RTD1295.

Clock names are taken from vendor DT and clk_summary where possible.
Clock rate calculations are guesses derived from Android register values and
resulting rates in clk_summary.
Clock gate parents are mostly unknown - osc27M is chosen for UART baudrate.

Signed-off-by: Andreas FÃrber <afaerber@xxxxxxx>
---
drivers/clk/Kconfig | 7 +
drivers/clk/Makefile | 1 +
drivers/clk/clk-rtd1295.c | 385 ++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 393 insertions(+)
create mode 100644 drivers/clk/clk-rtd1295.c

diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index a874b72612d0..8e4534912f51 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -209,6 +209,13 @@ config COMMON_CLK_OXNAS
---help---
Support for the OXNAS SoC Family clocks.

+config CLK_RTD129X
+ bool "Clock driver for the Realtek RTD129x SoC family"
+ default ARCH_REALTEK && ARM64
+ depends on (ARCH_REALTEK && ARM64) || COMPILE_TEST
+ ---help---
+ Support for the Realtek RTD1295 SoC.
+
config COMMON_CLK_VC5
tristate "Clock driver for IDT VersaClock 5,6 devices"
depends on I2C
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index cd376b3fb47a..466965452d4f 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -37,6 +37,7 @@ obj-$(CONFIG_COMMON_CLK_PALMAS) += clk-palmas.o
obj-$(CONFIG_COMMON_CLK_PWM) += clk-pwm.o
obj-$(CONFIG_CLK_QORIQ) += clk-qoriq.o
obj-$(CONFIG_COMMON_CLK_RK808) += clk-rk808.o
+obj-$(CONFIG_CLK_RTD129X) += clk-rtd1295.o
obj-$(CONFIG_COMMON_CLK_HI655X) += clk-hi655x.o
obj-$(CONFIG_COMMON_CLK_S2MPS11) += clk-s2mps11.o
obj-$(CONFIG_COMMON_CLK_SCPI) += clk-scpi.o
diff --git a/drivers/clk/clk-rtd1295.c b/drivers/clk/clk-rtd1295.c
new file mode 100644
index 000000000000..a20e71460dac
--- /dev/null
+++ b/drivers/clk/clk-rtd1295.c
@@ -0,0 +1,385 @@
+/*
+ * Realtek RTD1295
+ *
+ * Copyright (c) 2017 Andreas FÃrber
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <dt-bindings/clock/realtek,rtd1295.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
+
+struct rtd_pll_clk {
+ struct clk_hw hw;
+ void __iomem *base;
+ bool gpu;
+};
+
+#define to_pll_clk(_hw) container_of(_hw, struct rtd_pll_clk, hw)
+
+static unsigned long rtd_scpu_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
+{
+ struct rtd_pll_clk *pll = to_pll_clk(hw);
+ u32 reg1, reg2, reg3, reg4;
+ unsigned f1, f2, f3, f4, f5;
+ unsigned long rate, frac;
+
+ reg1 = readl(pll->base + 0x4);
+ reg2 = readl(pll->base - (0x500 - 0x30));
+ reg3 = readl(pll->base + 0x0);
+ reg4 = readl(pll->base + 0x1c);
+ f1 = (reg1 >> 11) & 0xff;
+ f2 = (reg1 >> 0) & 0x7ff;
+ f3 = (reg2 >> 7) & 0x3;
+ f4 = (reg3 >> 0) & 0x1;
+ f5 = (reg4 >> 20) & 0x1;
+
+ rate = parent_rate * (f1 + 3) / f3;
+ frac = parent_rate / 2048 * f2 / BIT(f4);
+ rate += frac;
+
+ pr_info("%s 0x%08x n=%u f=%u 0x%08x x=%u 0x%08x y=%u 0x%08x z=%u rate=%lu\n",
+ __clk_get_name(hw->clk),
+ reg1, f1, f2,
+ reg2, f3,
+ reg3, f4,
+ reg4, f5, rate);
+ return rate;
+}
+
+static const struct clk_ops rtd_scpu_ops = {
+ .recalc_rate = rtd_scpu_recalc_rate,
+};
+
+static struct clk *rtd_scpu(void __iomem *base, const char *name, struct clk *parent)
+{
+ struct rtd_pll_clk *pll;
+ struct clk_init_data init;
+ struct clk *clk;
+ const char *parents[1];
+
+ pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+ if (!pll)
+ return ERR_PTR(-ENOMEM);
+
+ if (parent)
+ parents[0] = __clk_get_name(parent);
+ init.name = name;
+ init.ops = &rtd_scpu_ops;
+ init.parent_names = parent ? parents : NULL;
+ init.num_parents = parent ? 1 : 0;
+ init.flags = CLK_IGNORE_UNUSED;
+
+ pll->hw.init = &init;
+ pll->base = base;
+
+ clk = clk_register(NULL, &pll->hw);
+ if (IS_ERR(clk)) {
+ pr_err("%s: error registering clk", name);
+ kfree(pll);
+ }
+ return clk;
+}
+
+static unsigned long rtd_nf_ssc_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
+{
+ struct rtd_pll_clk *pll = to_pll_clk(hw);
+ u32 reg1, reg2, reg3;
+ unsigned f1, f2, f3, f4;
+ unsigned long rate, frac;
+
+ reg1 = readl(pll->base + 0x4);
+ reg2 = readl(pll->base + 0x0);
+ reg3 = readl(pll->base + 0x1c);
+ f1 = (reg1 >> 11) & 0xff;
+ f2 = (reg1 >> 0) & 0x7ff;
+ f3 = (reg2 >> 0) & 0xf;
+ f4 = (reg3 >> 20) & 0x1;
+
+ rate = parent_rate * (f1 + 3);
+ if (pll->gpu) {
+ rate /= 2;
+ }
+ frac = parent_rate * 4 * f2 / BIT(f3);
+ if (pll->gpu) {
+ frac /= 2;
+ }
+ rate += frac;
+
+ pr_info("%s 0x%08x n=%u f=%u 0x%08x d=%u 0x%08x x=%u rate=%lu\n", __clk_get_name(hw->clk),
+ reg1, f1, f2,
+ reg2, f3,
+ reg3, f4, rate);
+ return rate;
+}
+
+static const struct clk_ops rtd_nf_ssc_ops = {
+ .recalc_rate = rtd_nf_ssc_recalc_rate,
+};
+
+static struct clk *rtd_nf_ssc(void __iomem *base, const char *name, struct clk *parent)
+{
+ struct rtd_pll_clk *pll;
+ struct clk_init_data init;
+ struct clk *clk;
+ const char *parents[1];
+
+ pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+ if (!pll)
+ return ERR_PTR(-ENOMEM);
+
+ if (parent)
+ parents[0] = __clk_get_name(parent);
+ init.name = name;
+ init.ops = &rtd_nf_ssc_ops;
+ init.parent_names = parent ? parents : NULL;
+ init.num_parents = parent ? 1 : 0;
+ init.flags = CLK_IGNORE_UNUSED;
+
+ pll->hw.init = &init;
+ pll->base = base;
+ pll->gpu = (strcmp(name, "pll_gpu") == 0);
+
+ clk = clk_register(NULL, &pll->hw);
+ if (IS_ERR(clk)) {
+ pr_err("%s: error registering clk", name);
+ kfree(pll);
+ }
+ return clk;
+}
+
+static unsigned long rtd_mno_ctrl_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
+{
+ struct rtd_pll_clk *pll = to_pll_clk(hw);
+ u32 reg1, reg2;
+ unsigned f1, f2, f3;
+ unsigned long rate;
+
+ reg1 = readl(pll->base + 0x0);
+ reg2 = readl(pll->base + 0x4);
+ f1 = (reg1 >> 4) & 0xff;
+ f2 = (reg1 >> 12) & 0x3;
+ f3 = (reg1 >> 17) & 0x3;
+
+ rate = parent_rate * (f1 + 2) / (f2 +1) / (f3 + 1);
+
+ pr_info("%s 0x%08x m=%u n=%u o=%u 0x%08x rate=%lu\n", __clk_get_name(hw->clk),
+ reg1, f1, f2, f3,
+ reg2, rate);
+ return rate;
+}
+
+static const struct clk_ops rtd_mno_ctrl_ops = {
+ .recalc_rate = rtd_mno_ctrl_recalc_rate,
+};
+
+static struct clk *rtd_mno_ctrl(void __iomem *base, const char *name, struct clk *parent)
+{
+ struct rtd_pll_clk *pll;
+ struct clk_init_data init;
+ struct clk *clk;
+ const char *parents[1];
+
+ pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+ if (!pll)
+ return ERR_PTR(-ENOMEM);
+
+ if (parent)
+ parents[0] = __clk_get_name(parent);
+ init.name = name;
+ init.ops = &rtd_mno_ctrl_ops;
+ init.parent_names = parent ? parents : NULL;
+ init.num_parents = parent ? 1 : 0;
+ init.flags = CLK_IGNORE_UNUSED;
+
+ pll->hw.init = &init;
+ pll->base = base;
+
+ clk = clk_register(NULL, &pll->hw);
+ if (IS_ERR(clk)) {
+ pr_err("%s: error registering clk", name);
+ kfree(pll);
+ }
+ return clk;
+}
+
+static const char * const rtd1295_gates1[32] = {
+ [ 0] = "clk_en_misc",
+ [ 1] = "clk_en_pcie0",
+ [ 2] = "clk_en_sata_0",
+ [ 3] = "clk_en_gspi",
+ [ 4] = "clk_en_usb",
+ [ 5] = "clk_en_pcr",
+ [ 6] = "clk_en_iso_misc",
+ [ 7] = "clk_en_sata_alive_0",
+ [ 8] = "clk_en_hdmi",
+ [ 9] = "clk_en_etn",
+ [10] = "clk_en_aio",
+ /* "*clk_en_gpu", */
+ /* "*clk_en_ve1", */
+ /* "*clk_en_ve2", */
+ [14] = "clk_en_tve",
+ /* "*clk_en_vo", */
+ [16] = "clk_en_lvds",
+ [17] = "clk_en_se",
+ [18] = "clk_en_dcu",
+ [19] = "clk_en_cp",
+ [20] = "clk_en_md",
+ [21] = "clk_en_tp",
+ [22] = "clk_en_rsa",
+ [23] = "clk_en_nf",
+ [24] = "clk_en_emmc",
+ [25] = "clk_en_cr",
+ [26] = "clk_en_sdio_ip",
+ [27] = "clk_en_mipi",
+ [28] = "clk_en_emmc_ip",
+ /* "*clk_en_ve3", */
+ [30] = "clk_en_sdio",
+ [31] = "clk_en_sd_ip",
+};
+
+static const char * const rtd1295_gates2[32] = {
+ [ 0] = "clk_en_nat",
+ [ 1] = "clk_en_misc_i2c_5",
+ /* "*clk_en_scpu", */
+ [ 3] = "clk_en_jpeg",
+ /* "*clk_en_apu", */
+ [ 5] = "clk_en_pcie1",
+ [ 6] = "clk_en_misc_sc",
+ [ 7] = "clk_en_cbus_tx",
+ /* "*rvd", */
+ /* "*rvd", */
+ [10] = "clk_en_misc_rtc",
+ /* "*rvd", */
+ /* "*rvd", */
+ [13] = "clk_en_misc_i2c_4",
+ [14] = "clk_en_misc_i2c_3",
+ [15] = "clk_en_misc_i2c_2",
+ [16] = "clk_en_misc_i2c_1",
+ [17] = "clk_en_aio_au_codec",
+ [18] = "clk_en_aio_mod",
+ [19] = "clk_en_aio_da",
+ [20] = "clk_en_aio_hdmi",
+ [21] = "clk_en_aio_spdif",
+ [22] = "clk_en_aio_i2s",
+ [23] = "clk_en_aio_mclk",
+ [24] = "clk_en_hdmirx",
+ [25] = "clk_en_sata_1",
+ [26] = "clk_en_sata_alive_1",
+ [27] = "clk_en_ur2",
+ [28] = "clk_en_ur1",
+ [29] = "clk_en_fan",
+ [30] = "clk_en_dcphy_0",
+ [31] = "clk_en_dcphy_1",
+};
+
+static struct clk *clks[16 + 2 * 32] = {};
+
+static struct clk_onecell_data rtd_clks = {
+ .clks = clks,
+ .clk_num = ARRAY_SIZE(clks),
+};
+
+static void __init rtd1295_clk_init(struct device_node *node)
+{
+ void __iomem *base;
+ struct clk *osc;
+ int i;
+ static const char *clk_sys_parents[2] = { "pll_bus", "pll_bus_div2" };
+ static const char *clk_ve_parents[4] = { "clk_sysh", "pll_ve1", "pll_ve2", "pll_ve2" };
+
+ base = of_iomap(node, 0);
+
+ osc = of_clk_get(node, 0);
+
+ clks[RTD1295_CLK_PLL_SCPU] = rtd_scpu(base + 0x500, "pll_scpu", osc);
+ clks[RTD1295_CLK_PLL_BUS] = rtd_nf_ssc(base + 0x520, "pll_bus", osc);
+ clks[RTD1295_CLK_PLL_BUS_DIV2] = clk_register_fixed_factor(NULL, "pll_bus_div2", "pll_bus", 0, 1, 2);
+ clks[RTD1295_CLK_SYS] = clk_register_mux(NULL, "clk_sys", clk_sys_parents, 2, 0, base + 0x30, 0, 1, CLK_MUX_READ_ONLY, NULL);
+ clks[RTD1295_CLK_PLL_BUS_H] = rtd_nf_ssc(base + 0x540, "pll_bus_h", osc);
+ clks[RTD1295_CLK_SYSH] = clk_register_fixed_factor(NULL, "clk_sysh", "pll_bus_h", 0, 1, 1);
+ clks[RTD1295_CLK_PLL_DDSA] = rtd_nf_ssc(base + 0x560, "pll_ddsa", osc);
+ clks[RTD1295_CLK_PLL_DDSB] = rtd_nf_ssc(base + 0x580, "pll_ddsb", osc);
+ clks[RTD1295_CLK_PLL_VODMA] = rtd_mno_ctrl(base + 0x260, "pll_vodma", osc);
+ clk_register_fixed_factor(NULL, "clk_vodma", "pll_vodma", 0, 1, 1);
+ clks[RTD1295_CLK_EN_VO] = clk_register_gate(NULL, "clk_en_vo", "clk_vodma", CLK_IGNORE_UNUSED, base + 0xc, 15, 0, NULL);
+ clks[RTD1295_CLK_PLL_VE1] = rtd_mno_ctrl(base + 0x114, "pll_ve1", osc);
+ clks[RTD1295_CLK_PLL_VE2] = rtd_mno_ctrl(base + 0x1d0, "pll_ve2", osc);
+ clk_register_mux(NULL, "clk_ve1", clk_ve_parents, 4, 0, base + 0x4c, 0, 2, CLK_MUX_READ_ONLY, NULL);
+ clks[RTD1295_CLK_EN_VE1] = clk_register_gate(NULL, "clk_en_ve1", "clk_ve1", CLK_IGNORE_UNUSED, base + 0xc, 12, 0, NULL);
+ clk_register_mux(NULL, "clk_ve2", clk_ve_parents, 4, 0, base + 0x4c, 2, 2, CLK_MUX_READ_ONLY, NULL);
+ clks[RTD1295_CLK_EN_VE2] = clk_register_gate(NULL, "clk_en_ve2", "clk_ve2", CLK_IGNORE_UNUSED, base + 0xc, 13, 0, NULL);
+ clk_register_mux(NULL, "clk_ve3", clk_ve_parents, 4, 0, base + 0x4c, 4, 2, CLK_MUX_READ_ONLY, NULL);
+ clks[RTD1295_CLK_EN_VE3] = clk_register_gate(NULL, "clk_en_ve3", "clk_ve3", CLK_IGNORE_UNUSED, base + 0xc, 29, 0, NULL);
+ clks[RTD1295_CLK_PLL_GPU] = rtd_nf_ssc(base + 0x5a0, "pll_gpu", osc);
+ clk_register_fixed_factor(NULL, "clk_gpu", "pll_gpu", 0, 1, 1);
+ clks[RTD1295_CLK_EN_GPU] = clk_register_gate(NULL, "clk_en_gpu", "clk_gpu", CLK_IGNORE_UNUSED, base + 0xc, 11, 0, NULL);
+ clks[RTD1295_CLK_PLL_ACPU] = rtd_nf_ssc(base + 0x5c0, "pll_acpu", osc);
+
+ for (i = 0; i < ARRAY_SIZE(rtd1295_gates1); i++) {
+ if (!rtd1295_gates1[i])
+ continue;
+ clks[RTD1295_CLK_EN_BASE + i] = clk_register_gate(NULL, rtd1295_gates1[i], NULL, CLK_IGNORE_UNUSED, base + 0xc, i, 0, NULL);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(rtd1295_gates2); i++) {
+ if (!rtd1295_gates2[i])
+ continue;
+ clks[RTD1295_CLK_EN_BASE2 + i] = clk_register_gate(NULL, rtd1295_gates2[i], __clk_get_name(osc), CLK_IGNORE_UNUSED, base + 0x10, i, 0, NULL);
+ }
+
+ clk_put(osc);
+
+ of_clk_add_provider(node, of_clk_src_onecell_get, &rtd_clks);
+}
+CLK_OF_DECLARE(rtd1295, "realtek,rtd1295-clk", rtd1295_clk_init);
+
+static const char * const rtd1295_iso_gates[13] = {
+ /* "*unused", */
+ /* "*rvd", */
+ [ 2] = "clk_en_misc_cec0",
+ [ 3] = "clk_en_cbusrx_sys",
+ [ 4] = "clk_en_cbustx_sys",
+ [ 5] = "clk_en_cbus_sys",
+ [ 6] = "clk_en_cbus_osc",
+ [ 7] = "clk_en_misc_ir",
+ [ 8] = "clk_en_misc_ur0",
+ [ 9] = "clk_en_i2c0",
+ [10] = "clk_en_i2c1",
+ [11] = "clk_en_etn_250m",
+ [12] = "clk_en_etn_sys",
+};
+
+static struct clk *iso_clks[13] = {};
+
+static struct clk_onecell_data rtd_iso_clks = {
+ .clks = iso_clks,
+ .clk_num = ARRAY_SIZE(iso_clks),
+};
+
+static void __init rtd1295_iso_clk_init(struct device_node *node)
+{
+ void __iomem *base;
+ struct clk *osc;
+ int i;
+
+ base = of_iomap(node, 0);
+
+ osc = of_clk_get(node, 0);
+
+ for (i = 0; i < ARRAY_SIZE(rtd1295_iso_gates); i++) {
+ if (!rtd1295_iso_gates[i])
+ continue;
+ iso_clks[i] = clk_register_gate(NULL, rtd1295_iso_gates[i], __clk_get_name(osc), CLK_IGNORE_UNUSED, base + 0x8c, i, 0, NULL);
+ }
+
+ clk_put(osc);
+
+ of_clk_add_provider(node, of_clk_src_onecell_get, &rtd_iso_clks);
+}
+CLK_OF_DECLARE(rtd1295_iso, "realtek,rtd1295-iso-clk", rtd1295_iso_clk_init);
--
2.12.3