blob: 4a3f50c638b18e9e208f3cf28a5a63b0027db9c6 [file] [log] [blame]
Zhengxun39df0532021-06-11 15:10:48 +00001// 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
Zhengxun39df0532021-06-11 15:10:48 +000010#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");
Zhengxun39df0532021-06-11 15:10:48 +0000139 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};