All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Andreas Färber" <afaerber@suse.de>
To: Michael Turquette <mturquette@baylibre.com>,
	Stephen Boyd <sboyd@codeaurora.org>,
	linux-clk@vger.kernel.org, linux-arm-kernel@lists.infradead.org
Cc: linux-kernel@vger.kernel.org, "Roc He" <hepeng@zidoo.tv>,
	蒋丽琴 <jiang.liqin@geniatech.com>,
	"Andreas Färber" <afaerber@suse.de>
Subject: [RFC 3/4] clk: Add Realtek RTD1295
Date: Thu, 17 Aug 2017 13:20:24 +0200	[thread overview]
Message-ID: <20170817112026.24062-4-afaerber@suse.de> (raw)
In-Reply-To: <20170817112026.24062-1-afaerber@suse.de>

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@suse.de>
---
 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

WARNING: multiple messages have this Message-ID (diff)
From: afaerber@suse.de (Andreas Färber)
To: linux-arm-kernel@lists.infradead.org
Subject: [RFC 3/4] clk: Add Realtek RTD1295
Date: Thu, 17 Aug 2017 13:20:24 +0200	[thread overview]
Message-ID: <20170817112026.24062-4-afaerber@suse.de> (raw)
In-Reply-To: <20170817112026.24062-1-afaerber@suse.de>

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@suse.de>
---
 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

  parent reply	other threads:[~2017-08-17 11:20 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-08-17 11:20 [RFC 0/4] arm64: Realtek RTD1295 clocks Andreas Färber
2017-08-17 11:20 ` Andreas Färber
2017-08-17 11:20 ` Andreas Färber
2017-08-17 11:20 ` [RFC 1/4] dt-bindings: clock: Add Realtek RTD1295 Andreas Färber
2017-08-17 11:20   ` Andreas Färber
2017-08-22  2:21   ` Rob Herring
2017-08-22  2:21     ` Rob Herring
2017-08-17 11:20 ` [RFC 2/4] arm64: dts: realtek: Add clock nodes for RTD1295 Andreas Färber
2017-08-17 11:20   ` Andreas Färber
2017-08-17 11:20 ` Andreas Färber [this message]
2017-08-17 11:20   ` [RFC 3/4] clk: Add Realtek RTD1295 Andreas Färber
2017-08-17 11:20 ` [RFC 4/4] arm64: dts: realtek: Update RTD1295 UART nodes with clocks Andreas Färber
2017-08-17 11:20   ` Andreas Färber
2017-08-17 11:20   ` Andreas Färber

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20170817112026.24062-4-afaerber@suse.de \
    --to=afaerber@suse.de \
    --cc=hepeng@zidoo.tv \
    --cc=jiang.liqin@geniatech.com \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-clk@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mturquette@baylibre.com \
    --cc=sboyd@codeaurora.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.