[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