1
2
3
4
5
6#include <common.h>
7#include <malloc.h>
8#include <asm/io.h>
9#include <clk-uclass.h>
10#include <dm.h>
11#include <dm/device_compat.h>
12#include <dm/devres.h>
13#include <dm/lists.h>
14#include <dm/util.h>
15#include <linux/bitops.h>
16#include <asm/global_data.h>
17
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
28struct socfpga_a10_clk_plat {
29 enum socfpga_a10_clk_type type;
30 struct clk_bulk clks;
31 u32 regs;
32
33 u16 fix_div;
34
35 u16 ctl_reg;
36
37 u16 div_reg;
38 u8 div_len;
39 u8 div_off;
40
41 u16 gate_reg;
42 u8 gate_bit;
43};
44
45static int socfpga_a10_clk_get_upstream(struct clk *clk, struct clk **upclk)
46{
47 struct socfpga_a10_clk_plat *plat = dev_get_plat(clk->dev);
48 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
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{
88 struct socfpga_a10_clk_plat *plat = dev_get_plat(clk->dev);
89 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{
124 struct socfpga_a10_clk_plat *plat = dev_get_plat(clk->dev);
125 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);
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);
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
182
183
184
185
186
187
188
189
190
191
192static void socfpga_a10_handoff_workaround(struct udevice *dev)
193{
194 struct socfpga_a10_clk_plat *plat = dev_get_plat(dev);
195 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
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
262 if (pre_reloc_only &&
263 !ofnode_pre_reloc(offset_to_ofnode(offset)))
264 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{
278 struct socfpga_a10_clk_plat *plat = dev_get_plat(dev);
279 struct socfpga_a10_clk_plat *pplat;
280 struct udevice *pdev;
281 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
288 if (!fdt_node_check_compatible(fdt, offset, "altr,clk-mgr")) {
289 plat->regs = dev_read_addr(dev);
290 } else {
291 pdev = dev_get_parent(dev);
292 if (!pdev)
293 return -ENODEV;
294
295 pplat = dev_get_plat(pdev);
296 if (!pplat)
297 return -EINVAL;
298
299 plat->ctl_reg = dev_read_u32_default(dev, "reg", 0x0);
300 plat->regs = pplat->regs;
301 }
302
303 if (!fdt_node_check_compatible(fdt, offset,
304 "altr,socfpga-a10-pll-clock")) {
305
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
323static int socfpga_a10_of_to_plat(struct udevice *dev)
324{
325 struct socfpga_a10_clk_plat *plat = dev_get_plat(dev);
326 unsigned int divreg[3], gatereg[2];
327 int ret;
328
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,
357 .of_match = socfpga_a10_clk_match,
358 .ops = &socfpga_a10_clk_ops,
359 .bind = socfpga_a10_clk_bind,
360 .probe = socfpga_a10_clk_probe,
361 .of_to_plat = socfpga_a10_of_to_plat,
362
363 .plat_auto = sizeof(struct socfpga_a10_clk_plat),
364};
365