From: Sean Anderson <sean.anderson@seco.com>
To: Zhengxun <zhengxunli.mxic@gmail.com>,
u-boot@lists.denx.de, michal.simek@xilinx.com, lukma@denx.de
Cc: zhengxunli@mxic.com.tw
Subject: Re: [PATCH v6 1/2] clk: zynq: Add clock wizard driver
Date: Fri, 11 Jun 2021 11:09:49 -0400 [thread overview]
Message-ID: <77911b67-1abb-099a-04fe-449d027a6b18@seco.com> (raw)
In-Reply-To: <20210611151049.13475-2-zhengxunli.mxic@gmail.com>
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>
next prev parent reply other threads:[~2021-06-11 15:10 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-06-11 15:10 [PATCH v6 0/2] Add Xilinx clock wizard driver support Zhengxun
2021-06-11 15:10 ` [PATCH v6 1/2] clk: zynq: Add clock wizard driver Zhengxun
2021-06-11 15:09 ` Sean Anderson [this message]
2021-06-16 10:01 ` Michal Simek
2021-06-11 15:10 ` [PATCH v6 2/2] dt-bindings: add xilinx clocking wizard bindings Zhengxun
2021-06-11 15:07 ` Sean Anderson
2021-06-15 17:21 ` Zhengxun Li
2021-06-15 14:52 ` Sean Anderson
2021-06-16 15:26 ` Zhengxun Li
2021-06-16 10:04 ` Michal Simek
2021-06-17 11:12 ` Zhengxun Li
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=77911b67-1abb-099a-04fe-449d027a6b18@seco.com \
--to=sean.anderson@seco.com \
--cc=lukma@denx.de \
--cc=michal.simek@xilinx.com \
--cc=u-boot@lists.denx.de \
--cc=zhengxunli.mxic@gmail.com \
--cc=zhengxunli@mxic.com.tw \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).