[PATCH v5 1/2] clk: zynq: Add clock wizard driver

Zhengxun Li zhengxunli.mxic at gmail.com
Thu Jun 10 12:44:03 CEST 2021


Hi,

Sean Anderson <sean.anderson at seco.com> 於 2021年6月4日 週五 下午2:22寫道:
>
>
>
> On 6/4/21 1:05 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 at gmail.com>
> > ---
> >   drivers/clk/Kconfig                 |   9 ++
> >   drivers/clk/Makefile                |   1 +
> >   drivers/clk/clk-xlnx-clock-wizard.c | 186 ++++++++++++++++++++++++++++
> >   3 files changed, 196 insertions(+)
> >   create mode 100644 drivers/clk/clk-xlnx-clock-wizard.c
> >
> > diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
> > index 40a5a5dd88..59922fc88e 100644
> > --- a/drivers/clk/Kconfig
> > +++ b/drivers/clk/Kconfig
> > @@ -128,6 +128,15 @@ 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.
> > +       Adds support for clocking wizard and compatible.
> > +       This driver supports the Xilinx clocking wizard programmable clock
> > +       synthesizer.
>
> Please modify this description in accordance with my comments from last revision.

Oops, I misunderstood your information and thought I needed to add the
dts binding
to tell how to use the clock wizard. So in this state, I need to add a
comment here or
create another comment to describe the clock wizard?

> > +
> >   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 at 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),
> > +};
> >
>
> With that fixed,
>
> Reviewed-by: Sean Anderson <sean.anderson at seco.com>

Thanks,
Zhengxun


More information about the U-Boot mailing list