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