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