[U-Boot] [RFC PATCH v2 12/18] arm: socfpga: gen5: add readonly clk driver

Simon Goldschmidt simon.k.r.goldschmidt at gmail.com
Tue Oct 15 20:10:25 UTC 2019


This adds clk-gen5 as a readonly DM_CLK driver that can return clocks for
the peripherals.

Signed-off-by: Simon Goldschmidt <simon.k.r.goldschmidt at gmail.com>
---

Changes in v2: None

 MAINTAINERS                   |   1 +
 drivers/clk/altera/Makefile   |   1 +
 drivers/clk/altera/clk-gen5.c | 338 ++++++++++++++++++++++++++++++++++
 3 files changed, 340 insertions(+)
 create mode 100644 drivers/clk/altera/clk-gen5.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 74a1423f50..be8a38ea07 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -114,6 +114,7 @@ M:	Simon Goldschmidt <simon.k.r.goldschmidt at gmail.com>
 S:	Maintainted
 T:	git https://gitlab.denx.de/u-boot/custodians/u-boot-socfpga.git
 F:	arch/arm/mach-socfpga/
+F:	drivers/clk/altera/clk-gen5.c
 F:	drivers/sysreset/sysreset_socfpga*
 
 ARM AMLOGIC SOC SUPPORT
diff --git a/drivers/clk/altera/Makefile b/drivers/clk/altera/Makefile
index a3ae8b24b0..d0664c6ad5 100644
--- a/drivers/clk/altera/Makefile
+++ b/drivers/clk/altera/Makefile
@@ -4,3 +4,4 @@
 #
 
 obj-$(CONFIG_TARGET_SOCFPGA_ARRIA10) += clk-arria10.o
+obj-$(CONFIG_TARGET_SOCFPGA_GEN5) += clk-gen5.o
diff --git a/drivers/clk/altera/clk-gen5.c b/drivers/clk/altera/clk-gen5.c
new file mode 100644
index 0000000000..4c197b81b0
--- /dev/null
+++ b/drivers/clk/altera/clk-gen5.c
@@ -0,0 +1,338 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018 Marek Vasut <marex at denx.de>
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <clk-uclass.h>
+#include <dm.h>
+#include <dm/lists.h>
+#include <dm/util.h>
+
+#include <asm/arch/clock_manager.h>
+
+enum socfpga_gen5_clk_type {
+	SOCFPGA_GEN5_CLK_MAIN_PLL,
+	SOCFPGA_GEN5_CLK_PER_PLL,
+	SOCFPGA_GEN5_CLK_PERIP_CLK,
+	SOCFPGA_GEN5_CLK_GATE_CLK,
+	SOCFPGA_GEN5_CLK_UNKNOWN_CLK,
+};
+
+struct socfpga_gen5_clk_platdata {
+	enum socfpga_gen5_clk_type type;
+	struct clk_bulk	clks;
+	u32		regs;
+	/* Fixed divider */
+	u16		fix_div;
+	/* Control register */
+	u16		ctl_reg;
+	/* Divider register */
+	u16		div_reg;
+	u8		div_len;
+	u8		div_off;
+	/* Clock gating register */
+	u16		gate_reg;
+	u8		gate_bit;
+	/* Clock source register */
+	u16		src_reg;
+	u8		src_len;
+	u8		src_off;
+};
+
+static int socfpga_gen5_clk_get_upstream(struct clk *clk, struct clk **upclk)
+{
+	struct socfpga_gen5_clk_platdata *plat = dev_get_platdata(clk->dev);
+	u32 reg, maxval;
+
+	if (plat->clks.count == 0)
+		return 0;
+
+	if (plat->src_reg && plat->src_len) {
+		maxval = 1 << plat->src_len;
+		if (plat->clks.count <= maxval) {
+			reg = readl(plat->regs + plat->src_reg);
+			reg >>= plat->src_off;
+			reg &= (maxval - 1);
+			*upclk = &plat->clks.clks[reg];
+			return 0;
+		}
+	}
+
+	if (plat->clks.count == 1) {
+		*upclk = &plat->clks.clks[0];
+		return 0;
+	}
+
+	if (!plat->ctl_reg)
+		return -EINVAL;
+
+	reg = readl(plat->regs + plat->ctl_reg);
+
+	/* Assume PLLs are ON for now */
+	if (plat->type == SOCFPGA_GEN5_CLK_MAIN_PLL) {
+		reg = (reg >> 8) & 0x3;
+		maxval = 2;
+	} else if (plat->type == SOCFPGA_GEN5_CLK_PER_PLL) {
+		reg = (reg >> 8) & 0x3;
+		maxval = 3;
+	} else {
+		reg = (reg >> 16) & 0x7;
+		maxval = 4;
+	}
+
+	if (reg > maxval) {
+		dev_err(clk->dev, "Invalid clock source\n");
+		return -EINVAL;
+	}
+
+	*upclk = &plat->clks.clks[reg];
+	return 0;
+}
+
+static int socfpga_gen5_clk_endisable(struct clk *clk, bool enable)
+{
+	struct socfpga_gen5_clk_platdata *plat = dev_get_platdata(clk->dev);
+	struct clk *upclk = NULL;
+	int ret;
+
+	if (!enable && plat->gate_reg)
+		clrbits_le32(plat->regs + plat->gate_reg, BIT(plat->gate_bit));
+
+	ret = socfpga_gen5_clk_get_upstream(clk, &upclk);
+	if (ret)
+		return ret;
+
+	if (upclk) {
+		if (enable)
+			clk_enable(upclk);
+		else
+			clk_disable(upclk);
+	}
+
+	if (enable && plat->gate_reg)
+		setbits_le32(plat->regs + plat->gate_reg, BIT(plat->gate_bit));
+
+	return 0;
+}
+
+static int socfpga_gen5_clk_enable(struct clk *clk)
+{
+	return socfpga_gen5_clk_endisable(clk, true);
+}
+
+static int socfpga_gen5_clk_disable(struct clk *clk)
+{
+	return socfpga_gen5_clk_endisable(clk, false);
+}
+
+static ulong socfpga_gen5_clk_get_rate(struct clk *clk)
+{
+	struct socfpga_gen5_clk_platdata *plat = dev_get_platdata(clk->dev);
+	struct clk *upclk = NULL;
+	ulong rate, uprate, reg, numer, denom;
+	int ret;
+
+	ret = socfpga_gen5_clk_get_upstream(clk, &upclk);
+	if (ret || !upclk)
+		return 0;
+
+	uprate = clk_get_rate(upclk);
+	rate = uprate;
+
+	if (plat->type == SOCFPGA_GEN5_CLK_MAIN_PLL) {
+		reg = readl(plat->regs + plat->ctl_reg);	/* VCO */
+		numer = (reg & CLKMGR_MAINPLLGRP_VCO_NUMER_MASK) >>
+			CLKMGR_MAINPLLGRP_VCO_NUMER_OFFSET;
+		denom = (reg & CLKMGR_MAINPLLGRP_VCO_DENOM_MASK) >>
+			CLKMGR_MAINPLLGRP_VCO_DENOM_OFFSET;
+		rate /= denom + 1;
+		rate *= numer + 1;
+	} else if (plat->type == SOCFPGA_GEN5_CLK_PER_PLL) {
+		reg = readl(plat->regs + plat->ctl_reg);	/* VCO */
+		numer = (reg & CLKMGR_PERPLLGRP_VCO_NUMER_MASK) >>
+			CLKMGR_PERPLLGRP_VCO_NUMER_OFFSET;
+		denom = (reg & CLKMGR_PERPLLGRP_VCO_DENOM_MASK) >>
+			CLKMGR_PERPLLGRP_VCO_DENOM_OFFSET;
+		rate /= denom + 1;
+		rate *= numer + 1;
+	} else {
+		rate /= plat->fix_div;
+
+		if (plat->fix_div == 1 && plat->ctl_reg) {
+			reg = readl(plat->regs + plat->ctl_reg);
+			reg &= 0x1ff;
+			rate /= reg + 1;
+		}
+
+		if (plat->div_reg) {
+			reg = readl(plat->regs + plat->div_reg);
+			reg >>= plat->div_off;
+			reg &= (1 << plat->div_len) - 1;
+			if (plat->type == SOCFPGA_GEN5_CLK_PERIP_CLK)
+				rate /= reg + 1;
+			if (plat->type == SOCFPGA_GEN5_CLK_GATE_CLK)
+				rate /= 1 << reg;
+		}
+	}
+
+	return rate;
+}
+
+static const struct clk_ops socfpga_gen5_clk_ops = {
+	.enable		= socfpga_gen5_clk_enable,
+	.disable	= socfpga_gen5_clk_disable,
+	.get_rate	= socfpga_gen5_clk_get_rate,
+};
+
+static int socfpga_gen5_clk_bind(struct udevice *dev)
+{
+	const void *fdt = gd->fdt_blob;
+	int offset = dev_of_offset(dev);
+	bool pre_reloc_only = !(gd->flags & GD_FLG_RELOC);
+	const char *name;
+	int ret;
+	const char *drv_name;
+
+	for (offset = fdt_first_subnode(fdt, offset);
+	     offset > 0;
+	     offset = fdt_next_subnode(fdt, offset)) {
+		name = fdt_get_name(fdt, offset, NULL);
+		if (!name)
+			return -EINVAL;
+
+		if (!strcmp(name, "clocks")) {
+			offset = fdt_first_subnode(fdt, offset);
+			name = fdt_get_name(fdt, offset, NULL);
+			if (!name)
+				return -EINVAL;
+		}
+
+		/* Filter out supported sub-clock */
+		if (fdt_node_check_compatible(fdt, offset,
+					      "altr,socfpga-pll-clock") &&
+		    fdt_node_check_compatible(fdt, offset,
+					      "altr,socfpga-perip-clk") &&
+		    fdt_node_check_compatible(fdt, offset,
+					      "altr,socfpga-gate-clk") &&
+		    fdt_node_check_compatible(fdt, offset, "fixed-clock"))
+			continue;
+
+		if (pre_reloc_only &&
+		    !dm_ofnode_pre_reloc(offset_to_ofnode(offset)))
+			continue;
+
+		if (fdt_node_check_compatible(fdt, offset, "fixed-clock"))
+			drv_name = "clk-gen5";
+		else
+			drv_name = "fixed_rate_clock";
+
+		ret = device_bind_driver_to_node(dev, drv_name, name,
+						 offset_to_ofnode(offset),
+						 NULL);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int socfpga_gen5_clk_probe(struct udevice *dev)
+{
+	struct socfpga_gen5_clk_platdata *plat = dev_get_platdata(dev);
+	const void *fdt = gd->fdt_blob;
+	int offset = dev_of_offset(dev);
+
+	clk_get_bulk(dev, &plat->clks);
+
+	if (!fdt_node_check_compatible(fdt, offset,
+				       "altr,socfpga-pll-clock")) {
+		/* Main PLL has 3 upstream clock */
+		if (plat->clks.count == 1)
+			plat->type = SOCFPGA_GEN5_CLK_MAIN_PLL;
+		else
+			plat->type = SOCFPGA_GEN5_CLK_PER_PLL;
+	} else if (!fdt_node_check_compatible(fdt, offset,
+					      "altr,socfpga-perip-clk")) {
+		plat->type = SOCFPGA_GEN5_CLK_PERIP_CLK;
+	} else if (!fdt_node_check_compatible(fdt, offset,
+					      "altr,socfpga-gate-clk")) {
+		plat->type = SOCFPGA_GEN5_CLK_GATE_CLK;
+	} else {
+		plat->type = SOCFPGA_GEN5_CLK_UNKNOWN_CLK;
+	}
+
+	return 0;
+}
+
+static int socfpga_gen5_ofdata_to_platdata(struct udevice *dev)
+{
+	struct socfpga_gen5_clk_platdata *plat = dev_get_platdata(dev);
+	struct socfpga_gen5_clk_platdata *pplat;
+	struct udevice *pdev;
+	const void *fdt = gd->fdt_blob;
+	unsigned int divreg[3], gatereg[2], srcreg[3];
+	int ret, offset = dev_of_offset(dev);
+	u32 regs;
+
+	regs = dev_read_u32_default(dev, "reg", 0x0);
+
+	if (!fdt_node_check_compatible(fdt, offset, "altr,clk-mgr")) {
+		plat->regs = devfdt_get_addr(dev);
+	} else {
+		pdev = dev_get_parent(dev);
+		if (!pdev)
+			return -ENODEV;
+
+		pplat = dev_get_platdata(pdev);
+		if (!pplat)
+			return -EINVAL;
+
+		plat->ctl_reg = regs;
+		plat->regs = pplat->regs;
+	}
+
+	plat->type = SOCFPGA_GEN5_CLK_UNKNOWN_CLK;
+
+	plat->fix_div = dev_read_u32_default(dev, "fixed-divider", 1);
+
+	ret = dev_read_u32_array(dev, "src-reg", srcreg, ARRAY_SIZE(srcreg));
+	if (!ret) {
+		plat->src_reg = srcreg[0];
+		plat->src_len = srcreg[2];
+		plat->src_off = srcreg[1];
+	}
+
+	ret = dev_read_u32_array(dev, "div-reg", divreg, ARRAY_SIZE(divreg));
+	if (!ret) {
+		plat->div_reg = divreg[0];
+		plat->div_len = divreg[2];
+		plat->div_off = divreg[1];
+	}
+
+	ret = dev_read_u32_array(dev, "clk-gate", gatereg, ARRAY_SIZE(gatereg));
+	if (!ret) {
+		plat->gate_reg = gatereg[0];
+		plat->gate_bit = gatereg[1];
+	}
+
+	return 0;
+}
+
+static const struct udevice_id socfpga_gen5_clk_match[] = {
+	{ .compatible = "altr,clk-mgr" },
+	{}
+};
+
+U_BOOT_DRIVER(socfpga_gen5_clk) = {
+	.name		= "clk-gen5",
+	.id		= UCLASS_CLK,
+	.of_match	= socfpga_gen5_clk_match,
+	.ops		= &socfpga_gen5_clk_ops,
+	.bind		= socfpga_gen5_clk_bind,
+	.probe		= socfpga_gen5_clk_probe,
+	.ofdata_to_platdata = socfpga_gen5_ofdata_to_platdata,
+
+	.platdata_auto_alloc_size = sizeof(struct socfpga_gen5_clk_platdata),
+};
-- 
2.20.1



More information about the U-Boot mailing list