123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186 |
- // 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),
- };
|