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