xref: /freebsd/sys/dev/clk/spacemit/k1_pll.c (revision dcb10e3add17259715352885dfc55ca92061d2ed)
1*dcb10e3aSBojan Novković /*
2*dcb10e3aSBojan Novković  * Copyright (c) 2026 Bojan Novković <bnovkov@FreeBSD.org>
3*dcb10e3aSBojan Novković  *
4*dcb10e3aSBojan Novković  * SPDX-License-Identifier: BSD-2-Clause
5*dcb10e3aSBojan Novković  */
6*dcb10e3aSBojan Novković 
7*dcb10e3aSBojan Novković /*
8*dcb10e3aSBojan Novković  * Driver for the SpacemiT K1 SoC PLL clocks.
9*dcb10e3aSBojan Novković  *
10*dcb10e3aSBojan Novković  * Written using SpacemiT's K1 Chip Product Documentation,
11*dcb10e3aSBojan Novković  * Section 9., "Top System (1/2)" and the accompanying clock
12*dcb10e3aSBojan Novković  * tree schematic, available at https://www.spacemit.com/community/document/
13*dcb10e3aSBojan Novković  */
14*dcb10e3aSBojan Novković 
15*dcb10e3aSBojan Novković #include <sys/cdefs.h>
16*dcb10e3aSBojan Novković 
17*dcb10e3aSBojan Novković #include <sys/param.h>
18*dcb10e3aSBojan Novković #include <sys/systm.h>
19*dcb10e3aSBojan Novković #include <sys/bus.h>
20*dcb10e3aSBojan Novković #include <sys/kernel.h>
21*dcb10e3aSBojan Novković #include <sys/module.h>
22*dcb10e3aSBojan Novković #include <sys/mutex.h>
23*dcb10e3aSBojan Novković 
24*dcb10e3aSBojan Novković #include <machine/bus.h>
25*dcb10e3aSBojan Novković #include <sys/rman.h>
26*dcb10e3aSBojan Novković #include <machine/resource.h>
27*dcb10e3aSBojan Novković 
28*dcb10e3aSBojan Novković #include <dev/fdt/simplebus.h>
29*dcb10e3aSBojan Novković #include <dev/ofw/ofw_bus.h>
30*dcb10e3aSBojan Novković #include <dev/ofw/ofw_bus_subr.h>
31*dcb10e3aSBojan Novković 
32*dcb10e3aSBojan Novković #include <dev/clk/clk.h>
33*dcb10e3aSBojan Novković 
34*dcb10e3aSBojan Novković #include <dt-bindings/clock/spacemit,k1-syscon.h>
35*dcb10e3aSBojan Novković 
36*dcb10e3aSBojan Novković #include "clkdev_if.h"
37*dcb10e3aSBojan Novković #include "k1_clk.h"
38*dcb10e3aSBojan Novković 
39*dcb10e3aSBojan Novković #define K1_PLL1_SW2_CTRL 0x110
40*dcb10e3aSBojan Novković #define K1_PLL2_SW2_CTRL 0x11C
41*dcb10e3aSBojan Novković #define K1_PLL3_SW2_CTRL 0x128
42*dcb10e3aSBojan Novković 
43*dcb10e3aSBojan Novković #define K1_PLL_ROOT(_clkname, _id, _rate)	\
44*dcb10e3aSBojan Novković 	{					\
45*dcb10e3aSBojan Novković 	.clkdef = {				\
46*dcb10e3aSBojan Novković 		.id = _id,			\
47*dcb10e3aSBojan Novković 		.name = #_clkname,		\
48*dcb10e3aSBojan Novković 	},					\
49*dcb10e3aSBojan Novković 	.rate = _rate,				\
50*dcb10e3aSBojan Novković }
51*dcb10e3aSBojan Novković 
52*dcb10e3aSBojan Novković #define K1_PLL_DIV(_clkname, _id, _div, ...)				\
53*dcb10e3aSBojan Novković 	{								\
54*dcb10e3aSBojan Novković 	.clkdef = {							\
55*dcb10e3aSBojan Novković 		.id = _id,						\
56*dcb10e3aSBojan Novković 		.name = #_clkname,					\
57*dcb10e3aSBojan Novković 		.parent_names = (const char * []) { __VA_ARGS__ },	\
58*dcb10e3aSBojan Novković 		.parent_cnt = K1_CLK_PARENT_CNT(__VA_ARGS__),		\
59*dcb10e3aSBojan Novković 	},								\
60*dcb10e3aSBojan Novković 	.fixed_div = _div,						\
61*dcb10e3aSBojan Novković }
62*dcb10e3aSBojan Novković 
63*dcb10e3aSBojan Novković #define K1_PLL_DIV_GATE(_clkname, _id, _div, _reg, _gate_bit, ...)	\
64*dcb10e3aSBojan Novković 	{								\
65*dcb10e3aSBojan Novković 	.clkdef = {							\
66*dcb10e3aSBojan Novković 		.id = _id,						\
67*dcb10e3aSBojan Novković 		.name = #_clkname,					\
68*dcb10e3aSBojan Novković 		.parent_names = (const char * []) { __VA_ARGS__ },	\
69*dcb10e3aSBojan Novković 		.parent_cnt = K1_CLK_PARENT_CNT(__VA_ARGS__),		\
70*dcb10e3aSBojan Novković 	},								\
71*dcb10e3aSBojan Novković 	.fixed_div = _div,						\
72*dcb10e3aSBojan Novković 	.reg = _reg,							\
73*dcb10e3aSBojan Novković 	.gate_mask = (1 << _gate_bit),					\
74*dcb10e3aSBojan Novković }
75*dcb10e3aSBojan Novković 
76*dcb10e3aSBojan Novković static struct k1_clk_def k1_plls[] = {
77*dcb10e3aSBojan Novković     K1_PLL_ROOT(pll1, CLK_PLL1, 2457600000UL),
78*dcb10e3aSBojan Novković     K1_PLL_ROOT(pll2, CLK_PLL2, 3000000000UL),
79*dcb10e3aSBojan Novković     K1_PLL_ROOT(pll3, CLK_PLL3, 3200000000UL),
80*dcb10e3aSBojan Novković 
81*dcb10e3aSBojan Novković     K1_PLL_DIV_GATE(pll1_d2, CLK_PLL1_D2, 2, K1_PLL1_SW2_CTRL, 1, "pll1"),
82*dcb10e3aSBojan Novković     K1_PLL_DIV_GATE(pll1_d3, CLK_PLL1_D3, 3, K1_PLL1_SW2_CTRL, 2, "pll1"),
83*dcb10e3aSBojan Novković     K1_PLL_DIV_GATE(pll1_d4, CLK_PLL1_D4, 4, K1_PLL1_SW2_CTRL, 3, "pll1"),
84*dcb10e3aSBojan Novković     K1_PLL_DIV_GATE(pll1_d5, CLK_PLL1_D5, 5, K1_PLL1_SW2_CTRL, 4, "pll1"),
85*dcb10e3aSBojan Novković     K1_PLL_DIV_GATE(pll1_d6, CLK_PLL1_D6, 6, K1_PLL1_SW2_CTRL, 5, "pll1"),
86*dcb10e3aSBojan Novković     K1_PLL_DIV_GATE(pll1_d7, CLK_PLL1_D7, 7, K1_PLL1_SW2_CTRL, 6, "pll1"),
87*dcb10e3aSBojan Novković     K1_PLL_DIV_GATE(pll1_d8, CLK_PLL1_D8, 8, K1_PLL1_SW2_CTRL, 7, "pll1"),
88*dcb10e3aSBojan Novković     K1_PLL_DIV_GATE(pll1_d11, CLK_PLL1_D11, 11, K1_PLL1_SW2_CTRL, 15, "pll1"),
89*dcb10e3aSBojan Novković     K1_PLL_DIV_GATE(pll1_d13, CLK_PLL1_D13, 13, K1_PLL1_SW2_CTRL, 16, "pll1"),
90*dcb10e3aSBojan Novković     K1_PLL_DIV_GATE(pll1_d23, CLK_PLL1_D23, 23, K1_PLL1_SW2_CTRL, 20, "pll1"),
91*dcb10e3aSBojan Novković     K1_PLL_DIV_GATE(pll1_d64, CLK_PLL1_D64, 64, K1_PLL1_SW2_CTRL, 0, "pll1"),
92*dcb10e3aSBojan Novković     K1_PLL_DIV_GATE(pll1_d10_aud, CLK_PLL1_D10_AUD, 10, K1_PLL1_SW2_CTRL, 10, "pll1"),
93*dcb10e3aSBojan Novković     K1_PLL_DIV_GATE(pll1_d100_aud, CLK_PLL1_D100_AUD, 100, K1_PLL1_SW2_CTRL, 11, "pll1"),
94*dcb10e3aSBojan Novković 
95*dcb10e3aSBojan Novković     K1_PLL_DIV_GATE(pll2_d2, CLK_PLL2_D2, 2, K1_PLL2_SW2_CTRL, 1, "pll2"),
96*dcb10e3aSBojan Novković     K1_PLL_DIV_GATE(pll2_d3, CLK_PLL2_D3, 3, K1_PLL2_SW2_CTRL, 2, "pll2"),
97*dcb10e3aSBojan Novković     K1_PLL_DIV_GATE(pll2_d4, CLK_PLL2_D4, 4, K1_PLL2_SW2_CTRL, 3, "pll2"),
98*dcb10e3aSBojan Novković     K1_PLL_DIV_GATE(pll2_d5, CLK_PLL2_D5, 5, K1_PLL2_SW2_CTRL, 4, "pll2"),
99*dcb10e3aSBojan Novković     K1_PLL_DIV_GATE(pll2_d6, CLK_PLL2_D6, 6, K1_PLL2_SW2_CTRL, 5, "pll2"),
100*dcb10e3aSBojan Novković     K1_PLL_DIV_GATE(pll2_d7, CLK_PLL2_D7, 7, K1_PLL2_SW2_CTRL, 6, "pll2"),
101*dcb10e3aSBojan Novković     K1_PLL_DIV_GATE(pll2_d8, CLK_PLL2_D8, 8, K1_PLL2_SW2_CTRL, 7, "pll2"),
102*dcb10e3aSBojan Novković 
103*dcb10e3aSBojan Novković     K1_PLL_DIV_GATE(pll3_d2, CLK_PLL3_D2, 2, K1_PLL3_SW2_CTRL, 1, "pll3"),
104*dcb10e3aSBojan Novković     K1_PLL_DIV_GATE(pll3_d3, CLK_PLL3_D3, 3, K1_PLL3_SW2_CTRL, 2, "pll3"),
105*dcb10e3aSBojan Novković     K1_PLL_DIV_GATE(pll3_d4, CLK_PLL3_D4, 4, K1_PLL3_SW2_CTRL, 3, "pll3"),
106*dcb10e3aSBojan Novković     K1_PLL_DIV_GATE(pll3_d5, CLK_PLL3_D5, 5, K1_PLL3_SW2_CTRL, 4, "pll3"),
107*dcb10e3aSBojan Novković     K1_PLL_DIV_GATE(pll3_d6, CLK_PLL3_D6, 6, K1_PLL3_SW2_CTRL, 5, "pll3"),
108*dcb10e3aSBojan Novković     K1_PLL_DIV_GATE(pll3_d7, CLK_PLL3_D7, 7, K1_PLL3_SW2_CTRL, 6, "pll3"),
109*dcb10e3aSBojan Novković     K1_PLL_DIV_GATE(pll3_d8, CLK_PLL3_D8, 8, K1_PLL3_SW2_CTRL, 7, "pll3"),
110*dcb10e3aSBojan Novković     K1_PLL_DIV(pll3_80, CLK_PLL3_80, 20, "pll3_d8"),
111*dcb10e3aSBojan Novković     K1_PLL_DIV(pll3_40, CLK_PLL3_40, 10, "pll3_d8"),
112*dcb10e3aSBojan Novković     K1_PLL_DIV(pll3_20, CLK_PLL3_20, 5, "pll3_d8"),
113*dcb10e3aSBojan Novković };
114*dcb10e3aSBojan Novković 
115*dcb10e3aSBojan Novković /*
116*dcb10e3aSBojan Novković  * The SpacemiT K1 manual states that "Changes of the run-time frequency
117*dcb10e3aSBojan Novković  * in the PLL{1,2,3} output are only available for debugging purposes and
118*dcb10e3aSBojan Novković  * should not be used in production systems", which is why this driver does
119*dcb10e3aSBojan Novković  * not support changing the top-level PLL frequency and instead treats all
120*dcb10e3aSBojan Novković  * PLLs as gateable fixed clocks.
121*dcb10e3aSBojan Novković  */
122*dcb10e3aSBojan Novković static int
k1_pll_recalc_freq(struct clknode * clk,uint64_t * freq)123*dcb10e3aSBojan Novković k1_pll_recalc_freq(struct clknode *clk, uint64_t *freq)
124*dcb10e3aSBojan Novković {
125*dcb10e3aSBojan Novković 	struct k1_clk_def *sc;
126*dcb10e3aSBojan Novković 
127*dcb10e3aSBojan Novković 	sc = clknode_get_softc(clk);
128*dcb10e3aSBojan Novković 	if (sc->fixed_div != 0)
129*dcb10e3aSBojan Novković 		*freq = *freq / sc->fixed_div;
130*dcb10e3aSBojan Novković 	else
131*dcb10e3aSBojan Novković 		*freq = sc->rate;
132*dcb10e3aSBojan Novković 	return (0);
133*dcb10e3aSBojan Novković }
134*dcb10e3aSBojan Novković 
135*dcb10e3aSBojan Novković static int
k1_pll_set_freq(struct clknode * clk,uint64_t fin,uint64_t * fout,int flags,int * stop)136*dcb10e3aSBojan Novković k1_pll_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout,
137*dcb10e3aSBojan Novković     int flags, int *stop)
138*dcb10e3aSBojan Novković {
139*dcb10e3aSBojan Novković 	struct k1_clk_def *sc;
140*dcb10e3aSBojan Novković 
141*dcb10e3aSBojan Novković 	sc = clknode_get_softc(clk);
142*dcb10e3aSBojan Novković 	if (sc->fixed_div == 0) {
143*dcb10e3aSBojan Novković 		/* Top-level PLL */
144*dcb10e3aSBojan Novković 		*stop = 1;
145*dcb10e3aSBojan Novković 		if (*fout != sc->rate)
146*dcb10e3aSBojan Novković 			return (ERANGE);
147*dcb10e3aSBojan Novković 		return (0);
148*dcb10e3aSBojan Novković 	}
149*dcb10e3aSBojan Novković 	*stop = 0;
150*dcb10e3aSBojan Novković 	*fout = *fout / sc->fixed_div;
151*dcb10e3aSBojan Novković 
152*dcb10e3aSBojan Novković 	return (0);
153*dcb10e3aSBojan Novković }
154*dcb10e3aSBojan Novković 
155*dcb10e3aSBojan Novković static clknode_method_t k1_pll_clknode_methods[] = {
156*dcb10e3aSBojan Novković 	CLKNODEMETHOD(clknode_set_freq,		k1_pll_set_freq),
157*dcb10e3aSBojan Novković 	CLKNODEMETHOD(clknode_recalc_freq,	k1_pll_recalc_freq),
158*dcb10e3aSBojan Novković 	CLKNODEMETHOD_END
159*dcb10e3aSBojan Novković };
160*dcb10e3aSBojan Novković 
161*dcb10e3aSBojan Novković DEFINE_CLASS_1(k1_pll_clknode, k1_pll_clknode_class, k1_pll_clknode_methods,
162*dcb10e3aSBojan Novković     sizeof(struct k1_clk_def), k1_clknode_class);
163*dcb10e3aSBojan Novković 
164*dcb10e3aSBojan Novković static int
k1_pll_probe(device_t dev)165*dcb10e3aSBojan Novković k1_pll_probe(device_t dev)
166*dcb10e3aSBojan Novković {
167*dcb10e3aSBojan Novković 	if (!ofw_bus_status_okay(dev))
168*dcb10e3aSBojan Novković 		return (ENXIO);
169*dcb10e3aSBojan Novković 
170*dcb10e3aSBojan Novković 	if (!ofw_bus_is_compatible(dev, "spacemit,k1-pll"))
171*dcb10e3aSBojan Novković 		return (ENXIO);
172*dcb10e3aSBojan Novković 	device_set_desc(dev, "SpacemiT K1 PLL Clock Control Unit");
173*dcb10e3aSBojan Novković 
174*dcb10e3aSBojan Novković 	return (BUS_PROBE_DEFAULT);
175*dcb10e3aSBojan Novković }
176*dcb10e3aSBojan Novković 
177*dcb10e3aSBojan Novković static int
k1_pll_attach(device_t dev)178*dcb10e3aSBojan Novković k1_pll_attach(device_t dev)
179*dcb10e3aSBojan Novković {
180*dcb10e3aSBojan Novković 	struct k1_clkdev_softc *sc;
181*dcb10e3aSBojan Novković 
182*dcb10e3aSBojan Novković 	sc = device_get_softc(dev);
183*dcb10e3aSBojan Novković 	sc->clks = k1_plls;
184*dcb10e3aSBojan Novković 	sc->nclks = nitems(k1_plls);
185*dcb10e3aSBojan Novković 	sc->clknode_class = &k1_pll_clknode_class;
186*dcb10e3aSBojan Novković 
187*dcb10e3aSBojan Novković 	return (k1_clk_attach(dev));
188*dcb10e3aSBojan Novković }
189*dcb10e3aSBojan Novković 
190*dcb10e3aSBojan Novković static device_method_t k1_pll_methods[] = {
191*dcb10e3aSBojan Novković 	DEVMETHOD(device_probe,		k1_pll_probe),
192*dcb10e3aSBojan Novković 	DEVMETHOD(device_attach,	k1_pll_attach),
193*dcb10e3aSBojan Novković 
194*dcb10e3aSBojan Novković 	DEVMETHOD_END
195*dcb10e3aSBojan Novković };
196*dcb10e3aSBojan Novković 
197*dcb10e3aSBojan Novković DEFINE_CLASS_1(k1_pll, k1_pll_driver, k1_pll_methods,
198*dcb10e3aSBojan Novković     sizeof(struct k1_clkdev_softc), k1_clkdev_driver);
199*dcb10e3aSBojan Novković EARLY_DRIVER_MODULE(k1_pll, simplebus, k1_pll_driver, 0, 0,
200*dcb10e3aSBojan Novković     BUS_PASS_BUS + BUS_PASS_ORDER_EARLY);
201