1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Xilinx 'Clocking Wizard' driver 4 * 5 * Copyright (c) 2021 Macronix Inc. 6 * 7 * Author: Zhengxun Li <zhengxunli@mxic.com.tw> 8 */ 9 10#include <clk-uclass.h> 11#include <dm.h> 12#include <div64.h> 13#include <dm/device_compat.h> 14#include <linux/iopoll.h> 15 16#include <linux/bitfield.h> 17 18#define SRR 0x0 19 20#define SR 0x4 21#define SR_LOCKED BIT(0) 22 23#define CCR(x) (0x200 + ((x) * 4)) 24 25#define FBOUT_CFG CCR(0) 26#define FBOUT_DIV(x) (x) 27#define FBOUT_DIV_MASK GENMASK(7, 0) 28#define FBOUT_GET_DIV(x) FIELD_GET(FBOUT_DIV_MASK, x) 29#define FBOUT_MUL(x) ((x) << 8) 30#define FBOUT_MUL_MASK GENMASK(15, 8) 31#define FBOUT_GET_MUL(x) FIELD_GET(FBOUT_MUL_MASK, x) 32#define FBOUT_FRAC(x) ((x) << 16) 33#define FBOUT_FRAC_MASK GENMASK(25, 16) 34#define FBOUT_GET_FRAC(x) FIELD_GET(FBOUT_FRAC_MASK, x) 35#define FBOUT_FRAC_EN BIT(26) 36 37#define FBOUT_PHASE CCR(1) 38 39#define OUT_CFG(x) CCR(2 + ((x) * 3)) 40#define OUT_DIV(x) (x) 41#define OUT_DIV_MASK GENMASK(7, 0) 42#define OUT_GET_DIV(x) FIELD_GET(OUT_DIV_MASK, x) 43#define OUT_FRAC(x) ((x) << 8) 44#define OUT_GET_MASK GENMASK(17, 8) 45#define OUT_GET_FRAC(x) FIELD_GET(OUT_GET_MASK, x) 46#define OUT_FRAC_EN BIT(18) 47 48#define OUT_PHASE(x) CCR(3 + ((x) * 3)) 49#define OUT_DUTY(x) CCR(4 + ((x) * 3)) 50 51#define CTRL CCR(23) 52#define CTRL_SEN BIT(2) 53#define CTRL_SADDR BIT(1) 54#define CTRL_LOAD BIT(0) 55 56/** 57 * struct clkwzrd - Clock wizard private data structure 58 * 59 * @base: memory base 60 * @vco_clk: voltage-controlled oscillator frequency 61 * 62 */ 63struct clkwzd { 64 void *base; 65 u64 vco_clk; 66}; 67 68struct clkwzd_plat { 69 fdt_addr_t addr; 70}; 71 72static int clk_wzrd_enable(struct clk *clk) 73{ 74 struct clkwzd *priv = dev_get_priv(clk->dev); 75 int ret; 76 u32 val; 77 78 ret = readl_poll_sleep_timeout(priv->base + SR, val, val & SR_LOCKED, 79 1, 100); 80 if (!ret) { 81 writel(CTRL_SEN | CTRL_SADDR | CTRL_LOAD, priv->base + CTRL); 82 writel(CTRL_SADDR, priv->base + CTRL); 83 ret = readl_poll_sleep_timeout(priv->base + SR, val, 84 val & SR_LOCKED, 1, 100); 85 } 86 87 return ret; 88} 89 90static unsigned long clk_wzrd_set_rate(struct clk *clk, ulong rate) 91{ 92 struct clkwzd *priv = dev_get_priv(clk->dev); 93 u64 div; 94 u32 cfg; 95 96 /* Get output clock divide value */ 97 div = DIV_ROUND_DOWN_ULL(priv->vco_clk * 1000, rate); 98 if (div < 1000 || div > 255999) 99 return -EINVAL; 100 101 cfg = OUT_DIV((u32)div / 1000); 102 103 writel(cfg, priv->base + OUT_CFG(clk->id)); 104 105 return 0; 106} 107 108static struct clk_ops clk_wzrd_ops = { 109 .enable = clk_wzrd_enable, 110 .set_rate = clk_wzrd_set_rate, 111}; 112 113static int clk_wzrd_probe(struct udevice *dev) 114{ 115 struct clkwzd_plat *plat = dev_get_plat(dev); 116 struct clkwzd *priv = dev_get_priv(dev); 117 struct clk clk_in1; 118 u64 clock, vco_clk; 119 u32 cfg; 120 int ret; 121 122 priv->base = (void *)plat->addr; 123 124 ret = clk_get_by_name(dev, "clk_in1", &clk_in1); 125 if (ret < 0) { 126 dev_err(dev, "failed to get clock\n"); 127 return ret; 128 } 129 130 clock = clk_get_rate(&clk_in1); 131 if (IS_ERR_VALUE(clock)) { 132 dev_err(dev, "failed to get rate\n"); 133 return clock; 134 } 135 136 ret = clk_enable(&clk_in1); 137 if (ret) { 138 dev_err(dev, "failed to enable clock\n"); 139 return ret; 140 } 141 142 /* Read clock configuration registers */ 143 cfg = readl(priv->base + FBOUT_CFG); 144 145 /* Recalculate VCO rate */ 146 if (cfg & FBOUT_FRAC_EN) 147 vco_clk = DIV_ROUND_DOWN_ULL(clock * 148 ((FBOUT_GET_MUL(cfg) * 1000) + 149 FBOUT_GET_FRAC(cfg)), 150 1000); 151 else 152 vco_clk = clock * FBOUT_GET_MUL(cfg); 153 154 priv->vco_clk = DIV_ROUND_DOWN_ULL(vco_clk, FBOUT_GET_DIV(cfg)); 155 156 return 0; 157} 158 159static int clk_wzrd_of_to_plat(struct udevice *dev) 160{ 161 struct clkwzd_plat *plat = dev_get_plat(dev); 162 163 plat->addr = dev_read_addr(dev); 164 if (plat->addr == FDT_ADDR_T_NONE) 165 return -EINVAL; 166 167 return 0; 168} 169 170static const struct udevice_id clk_wzrd_ids[] = { 171 { .compatible = "xlnx,clocking-wizard" }, 172 { /* sentinel */ } 173}; 174 175U_BOOT_DRIVER(clk_wzrd) = { 176 .name = "zynq-clk-wizard", 177 .id = UCLASS_CLK, 178 .of_match = clk_wzrd_ids, 179 .ops = &clk_wzrd_ops, 180 .probe = clk_wzrd_probe, 181 .of_to_plat = clk_wzrd_of_to_plat, 182 .priv_auto = sizeof(struct clkwzd), 183 .plat_auto = sizeof(struct clkwzd_plat), 184}; 185