xref: /freebsd/sys/dev/clk/starfive/jh7110_clk_pll.c (revision a90b9d0159070121c221b966469c3e36d912bf82)
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
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
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
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
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
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
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
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
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