1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2024 Jari Sihvola <jsihv@gmx.com>
5 * Copyright (c) 2024 The FreeBSD Foundation
6 *
7 * Portions of this software were developed by Mitchell Horne
8 * <mhorne@FreeBSD.org> under sponsorship from the FreeBSD Foundation.
9 */
10
11 #include <sys/param.h>
12 #include <sys/systm.h>
13 #include <sys/bus.h>
14 #include <sys/kernel.h>
15 #include <sys/module.h>
16 #include <sys/mutex.h>
17
18 #include <machine/bus.h>
19
20 #include <dev/fdt/simplebus.h>
21 #include <dev/ofw/ofw_bus.h>
22 #include <dev/ofw/ofw_bus_subr.h>
23
24 #include <dev/clk/clk.h>
25 #include <dev/clk/starfive/jh7110_clk.h>
26 #include <dev/clk/starfive/jh7110_clk_pll.h>
27 #include <dev/syscon/syscon.h>
28
29 #include <dt-bindings/clock/starfive,jh7110-crg.h>
30
31 #include "clkdev_if.h"
32 #include "syscon_if.h"
33
34 #define JH7110_SYS_SYSCON_SYSCFG24 0x18
35 #define JH7110_SYS_SYSCON_SYSCFG28 0x1c
36 #define JH7110_SYS_SYSCON_SYSCFG32 0x20
37 #define JH7110_SYS_SYSCON_SYSCFG36 0x24
38 #define JH7110_SYS_SYSCON_SYSCFG40 0x28
39 #define JH7110_SYS_SYSCON_SYSCFG44 0x2c
40 #define JH7110_SYS_SYSCON_SYSCFG48 0x30
41 #define JH7110_SYS_SYSCON_SYSCFG52 0x34
42
43 #define DEVICE_LOCK(_clk) \
44 CLKDEV_DEVICE_LOCK(clknode_get_device(_clk))
45 #define DEVICE_UNLOCK(_clk) \
46 CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk))
47
48 #define PLL_MASK_FILL(sc, id) \
49 do { \
50 sc->dacpd_mask = PLL## id ##_DACPD_MASK; \
51 sc->dsmpd_mask = PLL## id ##_DSMPD_MASK; \
52 sc->fbdiv_mask = PLL## id ##_FBDIV_MASK; \
53 sc->frac_mask = PLL## id ##_FRAC_MASK; \
54 sc->prediv_mask = PLL## id ##_PREDIV_MASK; \
55 sc->postdiv1_mask = PLL## id ##_POSTDIV1_MASK; \
56 } while (0)
57
58 #define PLL_SHIFT_FILL(sc, id) \
59 do { \
60 sc->dacpd_shift = PLL## id ##_DACPD_SHIFT; \
61 sc->dsmpd_shift = PLL## id ##_DSMPD_SHIFT; \
62 sc->fbdiv_shift = PLL## id ##_FBDIV_SHIFT; \
63 sc->frac_shift = PLL## id ##_FRAC_SHIFT; \
64 sc->prediv_shift = PLL## id ##_PREDIV_SHIFT; \
65 sc->postdiv1_shift = PLL## id ##_POSTDIV1_SHIFT; \
66 } while (0)
67
68 struct jh7110_clk_pll_softc {
69 struct mtx mtx;
70 struct clkdom *clkdom;
71 struct syscon *syscon;
72 };
73
74 struct jh7110_pll_clknode_softc {
75 uint32_t dacpd_offset;
76 uint32_t dsmpd_offset;
77 uint32_t fbdiv_offset;
78 uint32_t frac_offset;
79 uint32_t prediv_offset;
80 uint32_t postdiv1_offset;
81
82 uint32_t dacpd_mask;
83 uint32_t dsmpd_mask;
84 uint32_t fbdiv_mask;
85 uint32_t frac_mask;
86 uint32_t prediv_mask;
87 uint32_t postdiv1_mask;
88
89 uint32_t dacpd_shift;
90 uint32_t dsmpd_shift;
91 uint32_t fbdiv_shift;
92 uint32_t frac_shift;
93 uint32_t prediv_shift;
94 uint32_t postdiv1_shift;
95
96 const struct jh7110_pll_syscon_value *syscon_arr;
97 int syscon_nitems;
98 };
99
100 static const char *pll_parents[] = { "osc" };
101
102 static struct jh7110_clk_def pll_out_clks[] = {
103 {
104 .clkdef.id = JH7110_PLLCLK_PLL0_OUT,
105 .clkdef.name = "pll0_out",
106 .clkdef.parent_names = pll_parents,
107 .clkdef.parent_cnt = nitems(pll_parents),
108 .clkdef.flags = CLK_NODE_STATIC_STRINGS,
109 },
110 {
111 .clkdef.id = JH7110_PLLCLK_PLL1_OUT,
112 .clkdef.name = "pll1_out",
113 .clkdef.parent_names = pll_parents,
114 .clkdef.parent_cnt = nitems(pll_parents),
115 .clkdef.flags = CLK_NODE_STATIC_STRINGS,
116 },
117 {
118 .clkdef.id = JH7110_PLLCLK_PLL2_OUT,
119 .clkdef.name = "pll2_out",
120 .clkdef.parent_names = pll_parents,
121 .clkdef.parent_cnt = nitems(pll_parents),
122 .clkdef.flags = CLK_NODE_STATIC_STRINGS,
123 },
124 };
125
126 static int jh7110_clk_pll_register(struct clkdom *clkdom,
127 struct jh7110_clk_def *clkdef);
128
129 static int
jh7110_clk_pll_recalc_freq(struct clknode * clk,uint64_t * freq)130 jh7110_clk_pll_recalc_freq(struct clknode *clk, uint64_t *freq)
131 {
132 struct jh7110_clk_pll_softc *sc;
133 struct jh7110_pll_clknode_softc *clk_sc;
134 uint32_t dacpd, dsmpd, fbdiv, prediv, postdiv1;
135 uint64_t frac, fcal = 0;
136
137 sc = device_get_softc(clknode_get_device(clk));
138 clk_sc = clknode_get_softc(clk);
139
140 DEVICE_LOCK(clk);
141
142 dacpd = (SYSCON_READ_4(sc->syscon, clk_sc->dacpd_offset) & clk_sc->dacpd_mask) >>
143 clk_sc->dacpd_shift;
144 dsmpd = (SYSCON_READ_4(sc->syscon, clk_sc->dsmpd_offset) & clk_sc->dsmpd_mask) >>
145 clk_sc->dsmpd_shift;
146 fbdiv = (SYSCON_READ_4(sc->syscon, clk_sc->fbdiv_offset) & clk_sc->fbdiv_mask) >>
147 clk_sc->fbdiv_shift;
148 prediv = (SYSCON_READ_4(sc->syscon, clk_sc->prediv_offset) & clk_sc->prediv_mask) >>
149 clk_sc->prediv_shift;
150 postdiv1 = (SYSCON_READ_4(sc->syscon, clk_sc->postdiv1_offset) &
151 clk_sc->postdiv1_mask) >> clk_sc->postdiv1_shift;
152 frac = (SYSCON_READ_4(sc->syscon, clk_sc->frac_offset) & clk_sc->frac_mask) >>
153 clk_sc->frac_shift;
154
155 DEVICE_UNLOCK(clk);
156
157 /* dacpd and dsmpd both being 0 entails Fraction Multiple Mode */
158 if (dacpd == 0 && dsmpd == 0)
159 fcal = frac * FRAC_PATR_SIZE / (1 << 24);
160
161 *freq = *freq / FRAC_PATR_SIZE * (fbdiv * FRAC_PATR_SIZE + fcal) /
162 prediv / (1 << postdiv1);
163
164 return (0);
165 }
166
167 static int
jh7110_clk_pll_set_freq(struct clknode * clk,uint64_t fin,uint64_t * fout,int flags,int * done)168 jh7110_clk_pll_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout,
169 int flags, int *done)
170 {
171 struct jh7110_clk_pll_softc *sc;
172 struct jh7110_pll_clknode_softc *clk_sc;
173 const struct jh7110_pll_syscon_value *syscon_val = NULL;
174
175 sc = device_get_softc(clknode_get_device(clk));
176 clk_sc = clknode_get_softc(clk);
177
178 for (int i = 0; i != clk_sc->syscon_nitems; i++) {
179 if (*fout == clk_sc->syscon_arr[i].freq) {
180 syscon_val = &clk_sc->syscon_arr[i];
181 }
182 }
183
184 if (syscon_val == NULL) {
185 printf("%s: tried to set an unknown frequency %ju for %s\n",
186 __func__, *fout, clknode_get_name(clk));
187 return (EINVAL);
188 }
189
190 if ((flags & CLK_SET_DRYRUN) != 0) {
191 *done = 1;
192 return (0);
193 }
194
195 DEVICE_LOCK(clk);
196
197 SYSCON_MODIFY_4(sc->syscon, clk_sc->dacpd_offset, clk_sc->dacpd_mask,
198 syscon_val->dacpd << clk_sc->dacpd_shift & clk_sc->dacpd_mask);
199 SYSCON_MODIFY_4(sc->syscon, clk_sc->dsmpd_offset, clk_sc->dsmpd_mask,
200 syscon_val->dsmpd << clk_sc->dsmpd_shift & clk_sc->dsmpd_mask);
201 SYSCON_MODIFY_4(sc->syscon, clk_sc->prediv_offset, clk_sc->prediv_mask,
202 syscon_val->prediv << clk_sc->prediv_shift & clk_sc->prediv_mask);
203 SYSCON_MODIFY_4(sc->syscon, clk_sc->fbdiv_offset, clk_sc->fbdiv_mask,
204 syscon_val->fbdiv << clk_sc->fbdiv_shift & clk_sc->fbdiv_mask);
205 SYSCON_MODIFY_4(sc->syscon, clk_sc->postdiv1_offset,
206 clk_sc->postdiv1_mask, (syscon_val->postdiv1 >> 1) <<
207 clk_sc->postdiv1_shift & clk_sc->postdiv1_mask);
208
209 if (!syscon_val->dacpd && !syscon_val->dsmpd) {
210 SYSCON_MODIFY_4(sc->syscon, clk_sc->frac_offset, clk_sc->frac_mask,
211 syscon_val->frac << clk_sc->frac_shift & clk_sc->frac_mask);
212 }
213
214 DEVICE_UNLOCK(clk);
215
216 *done = 1;
217 return (0);
218 }
219
220 static int
jh7110_clk_pll_init(struct clknode * clk,device_t dev)221 jh7110_clk_pll_init(struct clknode *clk, device_t dev)
222 {
223 clknode_init_parent_idx(clk, 0);
224
225 return (0);
226 }
227
228 static int
jh7110_clk_pll_probe(device_t dev)229 jh7110_clk_pll_probe(device_t dev)
230 {
231 if (!ofw_bus_status_okay(dev))
232 return (ENXIO);
233
234 if (!ofw_bus_is_compatible(dev, "starfive,jh7110-pll"))
235 return (ENXIO);
236
237 device_set_desc(dev, "StarFive JH7110 PLL clock generator");
238
239 return (BUS_PROBE_DEFAULT);
240 }
241
242 static int
jh7110_clk_pll_attach(device_t dev)243 jh7110_clk_pll_attach(device_t dev)
244 {
245 struct jh7110_clk_pll_softc *sc;
246 int error;
247
248 sc = device_get_softc(dev);
249
250 mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF);
251
252 sc->clkdom = clkdom_create(dev);
253 if (sc->clkdom == NULL) {
254 device_printf(dev, "Couldn't create clkdom\n");
255 return (ENXIO);
256 }
257
258 error = syscon_get_by_ofw_node(dev, OF_parent(ofw_bus_get_node(dev)),
259 &sc->syscon);
260 if (error != 0) {
261 device_printf(dev, "Couldn't get syscon handle of parent\n");
262 return (error);
263 }
264
265 for (int i = 0; i < nitems(pll_out_clks); i++) {
266 error = jh7110_clk_pll_register(sc->clkdom, &pll_out_clks[i]);
267 if (error != 0)
268 device_printf(dev, "Couldn't register clock %s: %d\n",
269 pll_out_clks[i].clkdef.name, error);
270 }
271
272 error = clkdom_finit(sc->clkdom);
273 if (error != 0) {
274 device_printf(dev, "clkdom_finit() returned %d\n", error);
275 }
276
277 if (bootverbose)
278 clkdom_dump(sc->clkdom);
279
280 return (0);
281 }
282
283 static void
jh7110_clk_pll_device_lock(device_t dev)284 jh7110_clk_pll_device_lock(device_t dev)
285 {
286 struct jh7110_clk_pll_softc *sc;
287
288 sc = device_get_softc(dev);
289 mtx_lock(&sc->mtx);
290 }
291
292 static void
jh7110_clk_pll_device_unlock(device_t dev)293 jh7110_clk_pll_device_unlock(device_t dev)
294 {
295 struct jh7110_clk_pll_softc *sc;
296
297 sc = device_get_softc(dev);
298 mtx_unlock(&sc->mtx);
299 }
300
301 static clknode_method_t jh7110_pllnode_methods[] = {
302 /* Device interface */
303 CLKNODEMETHOD(clknode_init, jh7110_clk_pll_init),
304 CLKNODEMETHOD(clknode_recalc_freq, jh7110_clk_pll_recalc_freq),
305 CLKNODEMETHOD(clknode_set_freq, jh7110_clk_pll_set_freq),
306
307 CLKNODEMETHOD_END
308 };
309
310 static device_method_t jh7110_clk_pll_methods[] = {
311 /* Device interface */
312 DEVMETHOD(device_probe, jh7110_clk_pll_probe),
313 DEVMETHOD(device_attach, jh7110_clk_pll_attach),
314
315 /* clkdev interface */
316 DEVMETHOD(clkdev_device_lock, jh7110_clk_pll_device_lock),
317 DEVMETHOD(clkdev_device_unlock, jh7110_clk_pll_device_unlock),
318
319 DEVMETHOD_END
320 };
321
322 DEFINE_CLASS_1(jh7110_pllnode, jh7110_pllnode_class, jh7110_pllnode_methods,
323 sizeof(struct jh7110_pll_clknode_softc), clknode_class);
324 DEFINE_CLASS_0(jh7110_clk_pll, jh7110_clk_pll_driver, jh7110_clk_pll_methods,
325 sizeof(struct jh7110_clk_pll_softc));
326 EARLY_DRIVER_MODULE(jh7110_clk_pll, simplebus, jh7110_clk_pll_driver, 0, 0,
327 BUS_PASS_BUS + BUS_PASS_ORDER_EARLY);
328 MODULE_VERSION(jh7110_clk_pll, 1);
329
330 int
jh7110_clk_pll_register(struct clkdom * clkdom,struct jh7110_clk_def * clkdef)331 jh7110_clk_pll_register(struct clkdom *clkdom, struct jh7110_clk_def *clkdef)
332 {
333 struct clknode *clk = NULL;
334 struct jh7110_pll_clknode_softc *sc;
335
336 clk = clknode_create(clkdom, &jh7110_pllnode_class, &clkdef->clkdef);
337 if (clk == NULL)
338 return (1);
339
340 sc = clknode_get_softc(clk);
341
342 switch (clkdef->clkdef.id) {
343 case JH7110_PLLCLK_PLL0_OUT:
344 sc->syscon_arr = jh7110_pll0_syscon_freq;
345 sc->syscon_nitems = nitems(jh7110_pll0_syscon_freq);
346 PLL_MASK_FILL(sc, 0);
347 PLL_SHIFT_FILL(sc, 0);
348 sc->dacpd_offset = JH7110_SYS_SYSCON_SYSCFG24;
349 sc->dsmpd_offset = JH7110_SYS_SYSCON_SYSCFG24;
350 sc->fbdiv_offset = JH7110_SYS_SYSCON_SYSCFG28;
351 sc->frac_offset = JH7110_SYS_SYSCON_SYSCFG32;
352 sc->prediv_offset = JH7110_SYS_SYSCON_SYSCFG36;
353 sc->postdiv1_offset = JH7110_SYS_SYSCON_SYSCFG32;
354 break;
355 case JH7110_PLLCLK_PLL1_OUT:
356 sc->syscon_arr = jh7110_pll1_syscon_freq;
357 sc->syscon_nitems = nitems(jh7110_pll1_syscon_freq);
358 PLL_MASK_FILL(sc, 1);
359 PLL_SHIFT_FILL(sc, 1);
360 sc->dacpd_offset = JH7110_SYS_SYSCON_SYSCFG36;
361 sc->dsmpd_offset = JH7110_SYS_SYSCON_SYSCFG36;
362 sc->fbdiv_offset = JH7110_SYS_SYSCON_SYSCFG36;
363 sc->frac_offset = JH7110_SYS_SYSCON_SYSCFG40;
364 sc->prediv_offset = JH7110_SYS_SYSCON_SYSCFG44;
365 sc->postdiv1_offset = JH7110_SYS_SYSCON_SYSCFG40;
366 break;
367 case JH7110_PLLCLK_PLL2_OUT:
368 sc->syscon_arr = jh7110_pll2_syscon_freq;
369 sc->syscon_nitems = nitems(jh7110_pll2_syscon_freq);
370 PLL_MASK_FILL(sc, 2);
371 PLL_SHIFT_FILL(sc, 2);
372 sc->dacpd_offset = JH7110_SYS_SYSCON_SYSCFG44;
373 sc->dsmpd_offset = JH7110_SYS_SYSCON_SYSCFG44;
374 sc->fbdiv_offset = JH7110_SYS_SYSCON_SYSCFG44;
375 sc->frac_offset = JH7110_SYS_SYSCON_SYSCFG48;
376 sc->prediv_offset = JH7110_SYS_SYSCON_SYSCFG52;
377 sc->postdiv1_offset = JH7110_SYS_SYSCON_SYSCFG48;
378 break;
379 default:
380 return (EINVAL);
381 }
382
383 clknode_register(clkdom, clk);
384
385 return (0);
386 }
387