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