blob: 732ed4d79b9f25677eb6add4e7af0eecfd66d96b [file] [log] [blame]
Marek Vasut3f9d7352018-07-31 17:58:07 +02001// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) 2018 Marek Vasut <marex@denx.de>
4 */
5
6#include <common.h>
Simon Glass9bc15642020-02-03 07:36:16 -07007#include <malloc.h>
Marek Vasut3f9d7352018-07-31 17:58:07 +02008#include <asm/io.h>
9#include <clk-uclass.h>
10#include <dm.h>
Simon Glass9bc15642020-02-03 07:36:16 -070011#include <dm/device_compat.h>
Simon Glassd66c5f72020-02-03 07:36:15 -070012#include <dm/devres.h>
Marek Vasut3f9d7352018-07-31 17:58:07 +020013#include <dm/lists.h>
14#include <dm/util.h>
Simon Glass4dcacfc2020-05-10 11:40:13 -060015#include <linux/bitops.h>
Marek Vasut3f9d7352018-07-31 17:58:07 +020016
17#include <asm/arch/clock_manager.h>
18
19enum socfpga_a10_clk_type {
20 SOCFPGA_A10_CLK_MAIN_PLL,
21 SOCFPGA_A10_CLK_PER_PLL,
22 SOCFPGA_A10_CLK_PERIP_CLK,
23 SOCFPGA_A10_CLK_GATE_CLK,
24 SOCFPGA_A10_CLK_UNKNOWN_CLK,
25};
26
27struct socfpga_a10_clk_platdata {
28 enum socfpga_a10_clk_type type;
29 struct clk_bulk clks;
30 u32 regs;
31 /* Fixed divider */
32 u16 fix_div;
33 /* Control register */
34 u16 ctl_reg;
35 /* Divider register */
36 u16 div_reg;
37 u8 div_len;
38 u8 div_off;
39 /* Clock gating register */
40 u16 gate_reg;
41 u8 gate_bit;
42};
43
44static int socfpga_a10_clk_get_upstream(struct clk *clk, struct clk **upclk)
45{
46 struct socfpga_a10_clk_platdata *plat = dev_get_platdata(clk->dev);
47 u32 reg, maxval;
48
49 if (plat->clks.count == 0)
50 return 0;
51
52 if (plat->clks.count == 1) {
53 *upclk = &plat->clks.clks[0];
54 return 0;
55 }
56
57 if (!plat->ctl_reg) {
58 dev_err(clk->dev, "Invalid control register\n");
59 return -EINVAL;
60 }
61
62 reg = readl(plat->regs + plat->ctl_reg);
63
64 /* Assume PLLs are ON for now */
65 if (plat->type == SOCFPGA_A10_CLK_MAIN_PLL) {
66 reg = (reg >> 8) & 0x3;
67 maxval = 2;
68 } else if (plat->type == SOCFPGA_A10_CLK_PER_PLL) {
69 reg = (reg >> 8) & 0x3;
70 maxval = 3;
71 } else {
72 reg = (reg >> 16) & 0x7;
73 maxval = 4;
74 }
75
76 if (reg > maxval) {
77 dev_err(clk->dev, "Invalid clock source\n");
78 return -EINVAL;
79 }
80
81 *upclk = &plat->clks.clks[reg];
82 return 0;
83}
84
85static int socfpga_a10_clk_endisable(struct clk *clk, bool enable)
86{
87 struct socfpga_a10_clk_platdata *plat = dev_get_platdata(clk->dev);
88 struct clk *upclk = NULL;
89 int ret;
90
91 if (!enable && plat->gate_reg)
92 clrbits_le32(plat->regs + plat->gate_reg, BIT(plat->gate_bit));
93
94 ret = socfpga_a10_clk_get_upstream(clk, &upclk);
95 if (ret)
96 return ret;
97
98 if (upclk) {
99 if (enable)
100 clk_enable(upclk);
101 else
102 clk_disable(upclk);
103 }
104
105 if (enable && plat->gate_reg)
106 setbits_le32(plat->regs + plat->gate_reg, BIT(plat->gate_bit));
107
108 return 0;
109}
110
111static int socfpga_a10_clk_enable(struct clk *clk)
112{
113 return socfpga_a10_clk_endisable(clk, true);
114}
115
116static int socfpga_a10_clk_disable(struct clk *clk)
117{
118 return socfpga_a10_clk_endisable(clk, false);
119}
120
121static ulong socfpga_a10_clk_get_rate(struct clk *clk)
122{
123 struct socfpga_a10_clk_platdata *plat = dev_get_platdata(clk->dev);
124 struct clk *upclk = NULL;
125 ulong rate = 0, reg, numer, denom;
126 int ret;
127
128 ret = socfpga_a10_clk_get_upstream(clk, &upclk);
129 if (ret || !upclk)
130 return 0;
131
132 rate = clk_get_rate(upclk);
133
134 if (plat->type == SOCFPGA_A10_CLK_MAIN_PLL) {
135 reg = readl(plat->regs + plat->ctl_reg + 4); /* VCO1 */
136 numer = reg & CLKMGR_MAINPLL_VCO1_NUMER_MSK;
137 denom = (reg >> CLKMGR_MAINPLL_VCO1_DENOM_LSB) &
138 CLKMGR_MAINPLL_VCO1_DENOM_MSK;
139
140 rate /= denom + 1;
141 rate *= numer + 1;
142 } else if (plat->type == SOCFPGA_A10_CLK_PER_PLL) {
143 reg = readl(plat->regs + plat->ctl_reg + 4); /* VCO1 */
144 numer = reg & CLKMGR_PERPLL_VCO1_NUMER_MSK;
145 denom = (reg >> CLKMGR_PERPLL_VCO1_DENOM_LSB) &
146 CLKMGR_PERPLL_VCO1_DENOM_MSK;
147
148 rate /= denom + 1;
149 rate *= numer + 1;
150 } else {
151 rate /= plat->fix_div;
152
153 if (plat->fix_div == 1 && plat->ctl_reg) {
154 reg = readl(plat->regs + plat->ctl_reg);
155 reg &= 0x7ff;
156 rate /= reg + 1;
157 }
158
159 if (plat->div_reg) {
160 reg = readl(plat->regs + plat->div_reg);
161 reg >>= plat->div_off;
162 reg &= (1 << plat->div_len) - 1;
163 if (plat->type == SOCFPGA_A10_CLK_PERIP_CLK)
164 rate /= reg + 1;
165 if (plat->type == SOCFPGA_A10_CLK_GATE_CLK)
166 rate /= 1 << reg;
167 }
168 }
169
170 return rate;
171}
172
173static struct clk_ops socfpga_a10_clk_ops = {
174 .enable = socfpga_a10_clk_enable,
175 .disable = socfpga_a10_clk_disable,
176 .get_rate = socfpga_a10_clk_get_rate,
177};
178
179/*
180 * This workaround tries to fix the massively broken generated "handoff" DT,
181 * which contains duplicate clock nodes without any connection to the clock
182 * manager DT node. Yet, those "handoff" DT nodes contain configuration of
183 * the fixed input clock of the Arria10 which are missing from the base DT
184 * for Arria10.
185 *
186 * This workaround sets up upstream clock for the fixed input clocks of the
187 * A10 described in the base DT such that they map to the fixed clock from
188 * the "handoff" DT. This does not fully match how the clock look on the
189 * A10, but it is the least intrusive way to fix this mess.
190 */
191static void socfpga_a10_handoff_workaround(struct udevice *dev)
192{
193 struct socfpga_a10_clk_platdata *plat = dev_get_platdata(dev);
194 const void *fdt = gd->fdt_blob;
195 struct clk_bulk *bulk = &plat->clks;
196 int i, ret, offset = dev_of_offset(dev);
197 static const char * const socfpga_a10_fixedclk_map[] = {
198 "osc1", "altera_arria10_hps_eosc1",
199 "cb_intosc_ls_clk", "altera_arria10_hps_cb_intosc_ls",
200 "f2s_free_clk", "altera_arria10_hps_f2h_free",
201 };
202
203 if (fdt_node_check_compatible(fdt, offset, "fixed-clock"))
204 return;
205
206 for (i = 0; i < ARRAY_SIZE(socfpga_a10_fixedclk_map); i += 2)
207 if (!strcmp(dev->name, socfpga_a10_fixedclk_map[i]))
208 break;
209
210 if (i == ARRAY_SIZE(socfpga_a10_fixedclk_map))
211 return;
212
213 ret = uclass_get_device_by_name(UCLASS_CLK,
214 socfpga_a10_fixedclk_map[i + 1], &dev);
215 if (ret)
216 return;
217
218 bulk->count = 1;
219 bulk->clks = devm_kcalloc(dev, bulk->count,
220 sizeof(struct clk), GFP_KERNEL);
221 if (!bulk->clks)
222 return;
223
224 ret = clk_request(dev, &bulk->clks[0]);
225 if (ret)
226 free(bulk->clks);
227}
228
229static int socfpga_a10_clk_bind(struct udevice *dev)
230{
231 const void *fdt = gd->fdt_blob;
232 int offset = dev_of_offset(dev);
233 bool pre_reloc_only = !(gd->flags & GD_FLG_RELOC);
234 const char *name;
235 int ret;
236
237 for (offset = fdt_first_subnode(fdt, offset);
238 offset > 0;
239 offset = fdt_next_subnode(fdt, offset)) {
240 name = fdt_get_name(fdt, offset, NULL);
241 if (!name)
242 return -EINVAL;
243
244 if (!strcmp(name, "clocks")) {
245 offset = fdt_first_subnode(fdt, offset);
246 name = fdt_get_name(fdt, offset, NULL);
247 if (!name)
248 return -EINVAL;
249 }
250
251 /* Filter out supported sub-clock */
252 if (fdt_node_check_compatible(fdt, offset,
253 "altr,socfpga-a10-pll-clock") &&
254 fdt_node_check_compatible(fdt, offset,
255 "altr,socfpga-a10-perip-clk") &&
256 fdt_node_check_compatible(fdt, offset,
257 "altr,socfpga-a10-gate-clk") &&
258 fdt_node_check_compatible(fdt, offset, "fixed-clock"))
259 continue;
260
Patrick Delaunayacdb7af2019-03-20 18:21:25 +0100261 if (pre_reloc_only &&
Patrick Delaunay9d0731d2020-04-03 11:39:18 +0200262 !ofnode_pre_reloc(offset_to_ofnode(offset)))
Marek Vasut3f9d7352018-07-31 17:58:07 +0200263 continue;
264
265 ret = device_bind_driver_to_node(dev, "clk-a10", name,
266 offset_to_ofnode(offset),
267 NULL);
268 if (ret)
269 return ret;
270 }
271
272 return 0;
273}
274
275static int socfpga_a10_clk_probe(struct udevice *dev)
276{
277 struct socfpga_a10_clk_platdata *plat = dev_get_platdata(dev);
Chee Hong Angf54b71e2020-03-09 01:21:59 -0700278 struct socfpga_a10_clk_platdata *pplat;
279 struct udevice *pdev;
Marek Vasut3f9d7352018-07-31 17:58:07 +0200280 const void *fdt = gd->fdt_blob;
281 int offset = dev_of_offset(dev);
282
283 clk_get_bulk(dev, &plat->clks);
284
285 socfpga_a10_handoff_workaround(dev);
286
Chee Hong Angf54b71e2020-03-09 01:21:59 -0700287 if (!fdt_node_check_compatible(fdt, offset, "altr,clk-mgr")) {
Masahiro Yamadaa89b4de2020-07-17 14:36:48 +0900288 plat->regs = dev_read_addr(dev);
Chee Hong Angf54b71e2020-03-09 01:21:59 -0700289 } else {
290 pdev = dev_get_parent(dev);
291 if (!pdev)
292 return -ENODEV;
293
294 pplat = dev_get_platdata(pdev);
295 if (!pplat)
296 return -EINVAL;
297
298 plat->ctl_reg = dev_read_u32_default(dev, "reg", 0x0);
299 plat->regs = pplat->regs;
300 }
301
Marek Vasut3f9d7352018-07-31 17:58:07 +0200302 if (!fdt_node_check_compatible(fdt, offset,
303 "altr,socfpga-a10-pll-clock")) {
304 /* Main PLL has 3 upstream clock */
305 if (plat->clks.count == 3)
306 plat->type = SOCFPGA_A10_CLK_MAIN_PLL;
307 else
308 plat->type = SOCFPGA_A10_CLK_PER_PLL;
309 } else if (!fdt_node_check_compatible(fdt, offset,
310 "altr,socfpga-a10-perip-clk")) {
311 plat->type = SOCFPGA_A10_CLK_PERIP_CLK;
312 } else if (!fdt_node_check_compatible(fdt, offset,
313 "altr,socfpga-a10-gate-clk")) {
314 plat->type = SOCFPGA_A10_CLK_GATE_CLK;
315 } else {
316 plat->type = SOCFPGA_A10_CLK_UNKNOWN_CLK;
317 }
318
319 return 0;
320}
321
322static int socfpga_a10_ofdata_to_platdata(struct udevice *dev)
323{
324 struct socfpga_a10_clk_platdata *plat = dev_get_platdata(dev);
Marek Vasut3f9d7352018-07-31 17:58:07 +0200325 unsigned int divreg[3], gatereg[2];
Chee Hong Angf54b71e2020-03-09 01:21:59 -0700326 int ret;
Marek Vasut3f9d7352018-07-31 17:58:07 +0200327
328 plat->type = SOCFPGA_A10_CLK_UNKNOWN_CLK;
329
330 plat->fix_div = dev_read_u32_default(dev, "fixed-divider", 1);
331
332 ret = dev_read_u32_array(dev, "div-reg", divreg, ARRAY_SIZE(divreg));
333 if (!ret) {
334 plat->div_reg = divreg[0];
335 plat->div_len = divreg[2];
336 plat->div_off = divreg[1];
337 }
338
339 ret = dev_read_u32_array(dev, "clk-gate", gatereg, ARRAY_SIZE(gatereg));
340 if (!ret) {
341 plat->gate_reg = gatereg[0];
342 plat->gate_bit = gatereg[1];
343 }
344
345 return 0;
346}
347
348static const struct udevice_id socfpga_a10_clk_match[] = {
349 { .compatible = "altr,clk-mgr" },
350 {}
351};
352
353U_BOOT_DRIVER(socfpga_a10_clk) = {
354 .name = "clk-a10",
355 .id = UCLASS_CLK,
Marek Vasut3f9d7352018-07-31 17:58:07 +0200356 .of_match = socfpga_a10_clk_match,
357 .ops = &socfpga_a10_clk_ops,
358 .bind = socfpga_a10_clk_bind,
359 .probe = socfpga_a10_clk_probe,
360 .ofdata_to_platdata = socfpga_a10_ofdata_to_platdata,
361
362 .platdata_auto_alloc_size = sizeof(struct socfpga_a10_clk_platdata),
363};