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