summaryrefslogtreecommitdiff
path: root/drivers/clk
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2021-07-01 15:57:23 +0300
committerTom Rini <trini@konsulko.com>2021-07-01 15:57:23 +0300
commit6b69f15fd6386770b6fe782a4a8b4ce9243e2327 (patch)
treead79b9e1ef596dd0f0c2b0b878179535545c8d97 /drivers/clk
parent90c2fd2af8189e2e2682c90cd72a48b65191b467 (diff)
parent45576273e9209309238f332c85a6fef955c49b59 (diff)
downloadu-boot-6b69f15fd6386770b6fe782a4a8b4ce9243e2327.tar.xz
Merge tag 'xilinx-for-v2021.10' of https://source.denx.de/u-boot/custodians/u-boot-microblaze into next
Xilinx changes for v2021.10 clk: - Add driver for Xilinx Clocking Wizard IP fdt: - Also record architecture in /fit-images net: - Fix plat/priv data handling in axi emac - Add support for 10G/25G speeds pca953x: - Add missing dependency on i2c serial: - Fix dependencies for DEBUG uart for pl010/pl011 - Add setconfig option for cadence serial driver watchdog: - Add cadence wdt expire now function zynq: - Update DT bindings to reflect the latest state and descriptions zynqmp: - Update DT bindings to reflect the latest state and descriptions - SPL: Add support for ECC DRAM initialization - Fix R5 core 1 handling logic - Enable firmware driver for mini configurations - Enable secure boot, regulators, wdt - Add support xck devices and 67dr - Add psu init for sm/smk-k26 SOMs - Add handling for MMC seq number via mmc_get_env_dev() - Handle reserved memory locations - Add support for u-boot.itb generation for secure OS - Handle BL32 handoffs for secure OS - Add support for 64bit addresses for u-boot.its generation - Change eeprom handling via nvmem aliases
Diffstat (limited to 'drivers/clk')
-rw-r--r--drivers/clk/Kconfig11
-rw-r--r--drivers/clk/Makefile1
-rw-r--r--drivers/clk/clk-xlnx-clock-wizard.c186
3 files changed, 198 insertions, 0 deletions
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 4bc6680121..e07c6dd78a 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -128,6 +128,17 @@ config CLK_ZYNQ
This clock driver adds support for clock related settings for
Zynq platform.
+config CLK_XLNX_CLKWZRD
+ bool "Xilinx Clocking Wizard"
+ depends on CLK
+ help
+ Support for the Xilinx Clocking Wizard IP core clock generator.
+ The wizard support for dynamically reconfiguring the clocking
+ primitives for Multiply, Divide, Phase Shift/Offset, or Duty
+ Cycle. Limited by U-Boot clk uclass without set_phase API and
+ set_duty_cycle API, this driver only supports set_rate to modify
+ the frequency.
+
config CLK_ZYNQMP
bool "Enable clock driver support for ZynqMP"
depends on ARCH_ZYNQMP
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index f06164bb49..6e9c2d5485 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -43,6 +43,7 @@ obj-$(CONFIG_CLK_UNIPHIER) += uniphier/
obj-$(CONFIG_CLK_VEXPRESS_OSC) += clk_vexpress_osc.o
obj-$(CONFIG_CLK_ZYNQ) += clk_zynq.o
obj-$(CONFIG_CLK_ZYNQMP) += clk_zynqmp.o
+obj-$(CONFIG_CLK_XLNX_CLKWZRD) += clk-xlnx-clock-wizard.o
obj-$(CONFIG_ICS8N3QV01) += ics8n3qv01.o
obj-$(CONFIG_MACH_PIC32) += clk_pic32.o
obj-$(CONFIG_SANDBOX) += clk_sandbox.o
diff --git a/drivers/clk/clk-xlnx-clock-wizard.c b/drivers/clk/clk-xlnx-clock-wizard.c
new file mode 100644
index 0000000000..70ee3af107
--- /dev/null
+++ b/drivers/clk/clk-xlnx-clock-wizard.c
@@ -0,0 +1,186 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Xilinx 'Clocking Wizard' driver
+ *
+ * Copyright (c) 2021 Macronix Inc.
+ *
+ * Author: Zhengxun Li <zhengxunli@mxic.com.tw>
+ */
+
+#include <common.h>
+#include <clk-uclass.h>
+#include <dm.h>
+#include <div64.h>
+#include <dm/device_compat.h>
+#include <linux/iopoll.h>
+
+#include <linux/bitfield.h>
+
+#define SRR 0x0
+
+#define SR 0x4
+#define SR_LOCKED BIT(0)
+
+#define CCR(x) (0x200 + ((x) * 4))
+
+#define FBOUT_CFG CCR(0)
+#define FBOUT_DIV(x) (x)
+#define FBOUT_DIV_MASK GENMASK(7, 0)
+#define FBOUT_GET_DIV(x) FIELD_GET(FBOUT_DIV_MASK, x)
+#define FBOUT_MUL(x) ((x) << 8)
+#define FBOUT_MUL_MASK GENMASK(15, 8)
+#define FBOUT_GET_MUL(x) FIELD_GET(FBOUT_MUL_MASK, x)
+#define FBOUT_FRAC(x) ((x) << 16)
+#define FBOUT_FRAC_MASK GENMASK(25, 16)
+#define FBOUT_GET_FRAC(x) FIELD_GET(FBOUT_FRAC_MASK, x)
+#define FBOUT_FRAC_EN BIT(26)
+
+#define FBOUT_PHASE CCR(1)
+
+#define OUT_CFG(x) CCR(2 + ((x) * 3))
+#define OUT_DIV(x) (x)
+#define OUT_DIV_MASK GENMASK(7, 0)
+#define OUT_GET_DIV(x) FIELD_GET(OUT_DIV_MASK, x)
+#define OUT_FRAC(x) ((x) << 8)
+#define OUT_GET_MASK GENMASK(17, 8)
+#define OUT_GET_FRAC(x) FIELD_GET(OUT_GET_MASK, x)
+#define OUT_FRAC_EN BIT(18)
+
+#define OUT_PHASE(x) CCR(3 + ((x) * 3))
+#define OUT_DUTY(x) CCR(4 + ((x) * 3))
+
+#define CTRL CCR(23)
+#define CTRL_SEN BIT(2)
+#define CTRL_SADDR BIT(1)
+#define CTRL_LOAD BIT(0)
+
+/**
+ * struct clkwzrd - Clock wizard private data structure
+ *
+ * @base: memory base
+ * @vco_clk: voltage-controlled oscillator frequency
+ *
+ */
+struct clkwzd {
+ void *base;
+ u64 vco_clk;
+};
+
+struct clkwzd_plat {
+ fdt_addr_t addr;
+};
+
+static int clk_wzrd_enable(struct clk *clk)
+{
+ struct clkwzd *priv = dev_get_priv(clk->dev);
+ int ret;
+ u32 val;
+
+ ret = readl_poll_sleep_timeout(priv->base + SR, val, val & SR_LOCKED,
+ 1, 100);
+ if (!ret) {
+ writel(CTRL_SEN | CTRL_SADDR | CTRL_LOAD, priv->base + CTRL);
+ writel(CTRL_SADDR, priv->base + CTRL);
+ ret = readl_poll_sleep_timeout(priv->base + SR, val,
+ val & SR_LOCKED, 1, 100);
+ }
+
+ return ret;
+}
+
+static unsigned long clk_wzrd_set_rate(struct clk *clk, ulong rate)
+{
+ struct clkwzd *priv = dev_get_priv(clk->dev);
+ u64 div;
+ u32 cfg;
+
+ /* Get output clock divide value */
+ div = DIV_ROUND_DOWN_ULL(priv->vco_clk * 1000, rate);
+ if (div < 1000 || div > 255999)
+ return -EINVAL;
+
+ cfg = OUT_DIV((u32)div / 1000);
+
+ writel(cfg, priv->base + OUT_CFG(clk->id));
+
+ return 0;
+}
+
+static struct clk_ops clk_wzrd_ops = {
+ .enable = clk_wzrd_enable,
+ .set_rate = clk_wzrd_set_rate,
+};
+
+static int clk_wzrd_probe(struct udevice *dev)
+{
+ struct clkwzd_plat *plat = dev_get_plat(dev);
+ struct clkwzd *priv = dev_get_priv(dev);
+ struct clk clk_in1;
+ u64 clock, vco_clk;
+ u32 cfg;
+ int ret;
+
+ priv->base = (void *)plat->addr;
+
+ ret = clk_get_by_name(dev, "clk_in1", &clk_in1);
+ if (ret < 0) {
+ dev_err(dev, "failed to get clock\n");
+ return ret;
+ }
+
+ clock = clk_get_rate(&clk_in1);
+ if (IS_ERR_VALUE(clock)) {
+ dev_err(dev, "failed to get rate\n");
+ return clock;
+ }
+
+ ret = clk_enable(&clk_in1);
+ if (ret) {
+ dev_err(dev, "failed to enable clock\n");
+ clk_free(&clk_in1);
+ return ret;
+ }
+
+ /* Read clock configuration registers */
+ cfg = readl(priv->base + FBOUT_CFG);
+
+ /* Recalculate VCO rate */
+ if (cfg & FBOUT_FRAC_EN)
+ vco_clk = DIV_ROUND_DOWN_ULL(clock *
+ ((FBOUT_GET_MUL(cfg) * 1000) +
+ FBOUT_GET_FRAC(cfg)),
+ 1000);
+ else
+ vco_clk = clock * FBOUT_GET_MUL(cfg);
+
+ priv->vco_clk = DIV_ROUND_DOWN_ULL(vco_clk, FBOUT_GET_DIV(cfg));
+
+ return 0;
+}
+
+static int clk_wzrd_of_to_plat(struct udevice *dev)
+{
+ struct clkwzd_plat *plat = dev_get_plat(dev);
+
+ plat->addr = dev_read_addr(dev);
+ if (plat->addr == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ return 0;
+}
+
+static const struct udevice_id clk_wzrd_ids[] = {
+ { .compatible = "xlnx,clocking-wizard" },
+ { /* sentinel */ }
+};
+
+U_BOOT_DRIVER(clk_wzrd) = {
+ .name = "zynq-clk-wizard",
+ .id = UCLASS_CLK,
+ .of_match = clk_wzrd_ids,
+ .ops = &clk_wzrd_ops,
+ .probe = clk_wzrd_probe,
+ .of_to_plat = clk_wzrd_of_to_plat,
+ .priv_auto = sizeof(struct clkwzd),
+ .plat_auto = sizeof(struct clkwzd_plat),
+};