[PATCH RFC 06/20] clk: Add driver for Ingenic JZ4730 CGU

Lubomir Rintel lkundrak at v3.sk
Tue Nov 17 22:00:04 CET 2020


A rather minimal driver for that reads back configured clock rates for
hardware we support.

Signed-off-by: Lubomir Rintel <lkundrak at v3.sk>
---
 drivers/clk/Kconfig      |   8 +++
 drivers/clk/Makefile     |   1 +
 drivers/clk/clk-jz4730.c | 121 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 130 insertions(+)
 create mode 100644 drivers/clk/clk-jz4730.c

diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 4dfbad7986b..a138c6ebcde 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -113,6 +113,14 @@ config CLK_HSDK
 	  Enable this to support the cgu clocks on Synopsys ARC HSDK and
 	  Synopsys ARC HSDK-4xD boards
 
+config CLK_JZ4730
+	bool "Enable clock driver for Ingenic JZ4730 cgu"
+	depends on CLK && SOC_JZ4730
+	default y
+	help
+	  This clock driver adds support for clock generators present on
+	  Ingenic JZ4730 SoC.
+
 config CLK_VERSAL
 	bool "Enable clock driver support for Versal"
 	depends on ARCH_VERSAL
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index d1e295ac7c1..daad6333b36 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -27,6 +27,7 @@ obj-$(CONFIG_CLK_BOSTON) += clk_boston.o
 obj-$(CONFIG_CLK_EXYNOS) += exynos/
 obj-$(CONFIG_$(SPL_TPL_)CLK_INTEL) += intel/
 obj-$(CONFIG_CLK_HSDK) += clk-hsdk-cgu.o
+obj-$(CONFIG_CLK_JZ4730) += clk-jz4730.o
 obj-$(CONFIG_CLK_K210) += kendryte/
 obj-$(CONFIG_CLK_MPC83XX) += mpc83xx_clk.o
 obj-$(CONFIG_CLK_OCTEON) += clk_octeon.o
diff --git a/drivers/clk/clk-jz4730.c b/drivers/clk/clk-jz4730.c
new file mode 100644
index 00000000000..332b1ea82d6
--- /dev/null
+++ b/drivers/clk/clk-jz4730.c
@@ -0,0 +1,121 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * JZ4730 Clock Generation Unit driver.
+ *
+ * Copyright (C) 2020 Lubomir Rintel <lkundrak at v3.sk>
+ */
+
+#include <dt-bindings/clock/jz4730-cgu.h>
+#include <common.h>
+#include <dm.h>
+#include <clk-uclass.h>
+#include <div64.h>
+#include <linux/bitops.h>
+#include <asm/io.h>
+
+#define CPM_CFCR	0x00
+#define CPM_PLCR1	0x10
+#define CPM_OCR		0x1c
+
+#define CPM_OCR_EXT_RTC_CLK	BIT(8)
+
+#define CPM_PLCR1_PLL1EN	BIT(8)
+
+#define CPM_CFCR_PFR_SHIFT	8
+
+#define CPM_PLCR1_PLL1FD_SHIFT	23
+#define CPM_PLCR1_PLL1FD_MASK	(0x1ff << CPM_PLCR1_PLL1FD_SHIFT)
+#define CPM_PLCR1_PLL1RD_SHIFT	18
+#define CPM_PLCR1_PLL1RD_MASK	(0x1f << CPM_PLCR1_PLL1RD_SHIFT)
+#define CPM_PLCR1_PLL1OD_SHIFT	16
+#define CPM_PLCR1_PLL1OD_MASK	(0x03 << CPM_PLCR1_PLL1OD_SHIFT)
+
+struct jz4730_cgu_priv {
+	void __iomem *base;
+	unsigned long ext_rate;
+};
+
+static inline unsigned int pdiv(u32 cfcr, u32 shift)
+{
+	static unsigned int pdiv_table[] = { 1, 2, 3, 4, 6, 8, 12, 16, 24, 32 };
+
+	return pdiv_table[cfcr >> shift & 0xf];
+}
+
+static inline unsigned long pll_rate(u32 plcr1, unsigned long ext_rate)
+{
+	unsigned long long rate;
+
+	rate = ext_rate;
+	rate *= ((plcr1 & CPM_PLCR1_PLL1FD_MASK) >> CPM_PLCR1_PLL1FD_SHIFT) + 2;
+	do_div(rate, ((plcr1 & CPM_PLCR1_PLL1RD_MASK) >> CPM_PLCR1_PLL1RD_SHIFT) + 2);
+
+	return rate;
+}
+
+static ulong jz4730_cgu_clk_get_rate(struct clk *clk)
+{
+	struct jz4730_cgu_priv *priv = dev_get_priv(clk->dev);
+	u32 cfcr, plcr1, ocr;
+
+	switch (clk->id) {
+	case JZ4730_CLK_PCLK:
+		plcr1 = readl(priv->base + CPM_PLCR1);
+		if (plcr1 & CPM_PLCR1_PLL1EN) {
+			cfcr = readl(priv->base + CPM_CFCR);
+			return pll_rate(plcr1, priv->ext_rate) /
+				pdiv(cfcr, CPM_CFCR_PFR_SHIFT);
+		}
+		return priv->ext_rate;
+	case JZ4730_CLK_WDT:
+		ocr = readl(priv->base + CPM_OCR);
+		if (ocr & CPM_OCR_EXT_RTC_CLK)
+			return -EINVAL;
+		return priv->ext_rate / 128;
+	case JZ4730_CLK_UART0:
+	case JZ4730_CLK_UART1:
+	case JZ4730_CLK_UART2:
+	case JZ4730_CLK_UART3:
+	case JZ4730_CLK_I2C:
+	case JZ4730_CLK_TCU:
+		return priv->ext_rate;
+	default:
+		return -EINVAL;
+	}
+}
+
+static const struct clk_ops jz4730_cgu_clk_ops = {
+	.get_rate = jz4730_cgu_clk_get_rate,
+};
+
+static int jz4730_cgu_clk_probe(struct udevice *dev)
+{
+	struct jz4730_cgu_priv *priv = dev_get_priv(dev);
+	struct clk clk;
+	int ret;
+
+	priv->base = dev_remap_addr(dev);
+	if (!priv->base)
+		return -EINVAL;
+
+	ret = clk_get_by_name(dev, "ext", &clk);
+	if (ret)
+		return ret;
+	priv->ext_rate = clk_get_rate(&clk);
+
+	return 0;
+}
+
+static const struct udevice_id jz4730_cgu_clk_of_match[] = {
+	{ .compatible = "ingenic,jz4730-cgu" },
+	{ },
+};
+
+U_BOOT_DRIVER(jz4730_cgu_clk) = {
+	.name = "jz4730-cgu",
+	.id = UCLASS_CLK,
+	.of_match = jz4730_cgu_clk_of_match,
+	.probe = jz4730_cgu_clk_probe,
+	.priv_auto_alloc_size = sizeof(struct jz4730_cgu_priv),
+	.ops = &jz4730_cgu_clk_ops,
+};
-- 
2.28.0



More information about the U-Boot mailing list