Message ID | 20210611151049.13475-2-zhengxunli.mxic@gmail.com |
---|---|
State | Accepted |
Commit | 2b157d8127804d879d2624e1ed79085950957476 |
Delegated to: | Michal Simek |
Headers | show |
Series | Add Xilinx clock wizard driver support | expand |
On 6/11/21 11:10 AM, Zhengxun wrote: > The Clocking Wizard IP supports clock circuits customized > to your clocking requirements. 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, this > patch only provides set_rate to modify the frequency. > > Signed-off-by: Zhengxun <zhengxunli.mxic@gmail.com> > --- > drivers/clk/Kconfig | 11 ++ > drivers/clk/Makefile | 1 + > drivers/clk/clk-xlnx-clock-wizard.c | 186 ++++++++++++++++++++++++++++ > 3 files changed, 198 insertions(+) > create mode 100644 drivers/clk/clk-xlnx-clock-wizard.c > > diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig > index 40a5a5dd88..a0ac661f28 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 645709b855..4fcc33953a 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), > +}; > Reviewed-by: Sean Anderson <sean.anderson@seco.com>
On 6/11/21 5:10 PM, Zhengxun wrote: > The Clocking Wizard IP supports clock circuits customized > to your clocking requirements. 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, this > patch only provides set_rate to modify the frequency. > > Signed-off-by: Zhengxun <zhengxunli.mxic@gmail.com> > --- > drivers/clk/Kconfig | 11 ++ > drivers/clk/Makefile | 1 + > drivers/clk/clk-xlnx-clock-wizard.c | 186 ++++++++++++++++++++++++++++ > 3 files changed, 198 insertions(+) > create mode 100644 drivers/clk/clk-xlnx-clock-wizard.c > > diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig > index 40a5a5dd88..a0ac661f28 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 645709b855..4fcc33953a 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), > +}; > Applied. M
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 40a5a5dd88..a0ac661f28 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 645709b855..4fcc33953a 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), +};
The Clocking Wizard IP supports clock circuits customized to your clocking requirements. 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, this patch only provides set_rate to modify the frequency. Signed-off-by: Zhengxun <zhengxunli.mxic@gmail.com> --- drivers/clk/Kconfig | 11 ++ drivers/clk/Makefile | 1 + drivers/clk/clk-xlnx-clock-wizard.c | 186 ++++++++++++++++++++++++++++ 3 files changed, 198 insertions(+) create mode 100644 drivers/clk/clk-xlnx-clock-wizard.c