From mboxrd@z Thu Jan 1 00:00:00 1970 From: Lubomir Rintel Date: Tue, 17 Nov 2020 22:00:04 +0100 Subject: [PATCH RFC 06/20] clk: Add driver for Ingenic JZ4730 CGU In-Reply-To: <20201117210018.751469-1-lkundrak@v3.sk> References: <20201117210018.751469-1-lkundrak@v3.sk> Message-ID: <20201117210018.751469-7-lkundrak@v3.sk> List-Id: MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit To: u-boot@lists.denx.de A rather minimal driver for that reads back configured clock rates for hardware we support. Signed-off-by: Lubomir Rintel --- 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 + */ + +#include +#include +#include +#include +#include +#include +#include + +#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