am18xx_pllc.c revision 1.1 1 /* $NetBSD $ */
2
3 /*-
4 * Copyright (c) 2026 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Yuri Honegger.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 /*
33 * PLL Controller for the TI AM18XX SOC.
34 */
35
36 #include <sys/param.h>
37 #include <sys/bus.h>
38 #include <sys/cdefs.h>
39 #include <sys/device.h>
40
41 #include <dev/clk/clk_backend.h>
42 #include <dev/fdt/fdtvar.h>
43
44 #include <arm/fdt/arm_fdtvar.h>
45
46 struct am18xx_pllc_softc;
47
48 struct am18xx_pllc_clk {
49 struct clk clk_base; /* must be first */
50 u_int (*get_rate)(struct am18xx_pllc_softc *, struct am18xx_pllc_clk *);
51 u_int clk_index;
52 };
53
54 struct am18xx_pllc_config {
55 struct am18xx_pllc_clk *sysclks;
56 struct am18xx_pllc_clk *auxclk;
57 int num_sysclk;
58 };
59
60 struct am18xx_pllc_softc {
61 bus_space_tag_t sc_bst;
62 bus_space_handle_t sc_bsh;
63 int sc_sysclk_phandle;
64 int sc_auxclk_phandle;
65 const struct am18xx_pllc_config *sc_config;
66 struct clk_domain sc_clkdom;
67 struct clk *sc_ref_clk;
68 };
69
70 static int am18xx_pllc_match(device_t, cfdata_t, void *);
71 static void am18xx_pllc_attach(device_t, device_t, void *);
72 static u_int am18xx_pllc_get_sysclk_rate(struct am18xx_pllc_softc *,
73 struct am18xx_pllc_clk *);
74 static u_int am18xx_pllc_get_auxclk_rate(struct am18xx_pllc_softc *,
75 struct am18xx_pllc_clk *);
76 static struct clk * am18xx_pllc_decode(device_t, int, const void *, size_t);
77 static struct clk * am18xx_pllc_clk_get(void *, const char *);
78 static u_int am18xx_pllc_clk_get_rate(void *, struct clk *);
79 static struct clk * am18xx_pllc_clk_get_parent(void *, struct clk *);
80
81 CFATTACH_DECL_NEW(am18xxpllc, sizeof(struct am18xx_pllc_softc),
82 am18xx_pllc_match, am18xx_pllc_attach, NULL, NULL);
83
84 #define AM18XX_PLLC_PLLCTL 0x100
85 #define AM18XX_PLLC_PLLM 0x110
86 #define AM18XX_PLLC_PREDIV 0x114
87 #define AM18XX_PLLC_PLLDIV1 0x118
88 #define AM18XX_PLLC_POSTDIV 0x128
89 #define AM18XX_PLLC_PLLDIV4 0x160
90
91 #define AM18XX_PLLC_PLLCTL_PLLEN __BIT(0)
92 #define AM18XX_PLLC_PLLCTL_EXTCLKSRC __BIT(9)
93 #define AM18XX_PLLC_PLLM_MULTIPLIER __BITS(4,0)
94 #define AM18XX_PLLC_PREDIV_RATIO __BITS(4,0)
95 #define AM18XX_PLLC_POSTDIV_RATIO __BITS(4,0)
96 #define AM18XX_PLLC_PLLDIV_RATIO __BITS(4,0)
97
98 #define PLLC_READ(sc, reg) \
99 bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, reg)
100 #define PLLC_WRITE(sc, reg, val) \
101 bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, reg, val)
102
103 #define PLLC_CLK(_i, _name, _rate) \
104 { \
105 .clk_base.name = (_name), \
106 .clk_base.flags = 0, \
107 .get_rate = (_rate), \
108 .clk_index = (_i), \
109 }
110
111 static struct am18xx_pllc_clk am18xx_pllc_pll0_auxclk =
112 PLLC_CLK(0, "pll0_auxclk", &am18xx_pllc_get_auxclk_rate);
113
114 static struct am18xx_pllc_clk am18xx_pllc_pll0_sysclks[] = {
115 PLLC_CLK(0, "pll0_sysclk1", &am18xx_pllc_get_sysclk_rate),
116 PLLC_CLK(1, "pll0_sysclk2", &am18xx_pllc_get_sysclk_rate),
117 PLLC_CLK(2, "pll0_sysclk3", &am18xx_pllc_get_sysclk_rate),
118 PLLC_CLK(3, "pll0_sysclk4", &am18xx_pllc_get_sysclk_rate),
119 PLLC_CLK(4, "pll0_sysclk5", &am18xx_pllc_get_sysclk_rate),
120 PLLC_CLK(5, "pll0_sysclk6", &am18xx_pllc_get_sysclk_rate),
121 PLLC_CLK(6, "pll0_sysclk7", &am18xx_pllc_get_sysclk_rate),
122 };
123
124 static const struct am18xx_pllc_config am18xx_pllc_pll0_config = {
125 .auxclk = &am18xx_pllc_pll0_auxclk,
126 .sysclks = am18xx_pllc_pll0_sysclks,
127 .num_sysclk = __arraycount(am18xx_pllc_pll0_sysclks),
128 };
129
130 static const struct fdtbus_clock_controller_func am18xx_pllc_clk_fdt_funcs = {
131 .decode = am18xx_pllc_decode,
132 };
133
134 static const struct clk_funcs am18xx_pllc_clk_funcs = {
135 .get = am18xx_pllc_clk_get,
136 .get_rate = am18xx_pllc_clk_get_rate,
137 .get_parent = am18xx_pllc_clk_get_parent,
138 };
139
140 static const struct device_compatible_entry compat_data[] = {
141 { .compat = "ti,da850-pll0", .data = &am18xx_pllc_pll0_config },
142 DEVICE_COMPAT_EOL
143 };
144
145 static struct clk *
146 am18xx_pllc_clk_get(void *priv, const char *name)
147 {
148 struct am18xx_pllc_softc * const sc = priv;
149
150 /* check if it is the auxclk */
151 if (strcmp(sc->sc_config->auxclk->clk_base.name, name) == 0) {
152 return &sc->sc_config->auxclk->clk_base;
153 }
154
155 /* check if it is a sysclk */
156 for (int i = 0; i < sc->sc_config->num_sysclk; i++) {
157 if (strcmp(sc->sc_config->sysclks[i].clk_base.name, name) == 0)
158 return &sc->sc_config->sysclks[i].clk_base;
159 }
160
161 return NULL;
162 }
163
164 static u_int
165 am18xx_pllc_clk_get_rate(void *priv, struct clk *clkp)
166 {
167 struct am18xx_pllc_softc * const sc = priv;
168 struct am18xx_pllc_clk *clk = (struct am18xx_pllc_clk *)clkp;
169
170 return clk->get_rate(sc, clk);
171 }
172
173 static struct clk *
174 am18xx_pllc_clk_get_parent(void *priv, struct clk *clkp)
175 {
176 struct am18xx_pllc_softc * const sc = priv;
177
178 return sc->sc_ref_clk;
179 }
180
181 static u_int
182 am18xx_pllc_get_sysclk_rate(struct am18xx_pllc_softc *sc,
183 struct am18xx_pllc_clk *clk)
184 {
185 uint32_t pllctl_reg = PLLC_READ(sc, AM18XX_PLLC_PLLCTL);
186
187 uint32_t prediv_reg = PLLC_READ(sc, AM18XX_PLLC_PREDIV);
188 uint32_t prediv_ratio = (prediv_reg & AM18XX_PLLC_PREDIV_RATIO) + 1;
189
190 uint32_t pllm_reg = PLLC_READ(sc, AM18XX_PLLC_PLLM);
191 uint32_t pllm_multiplier = (pllm_reg & AM18XX_PLLC_PLLM_MULTIPLIER) + 1;
192
193 uint32_t postdiv_reg = PLLC_READ(sc, AM18XX_PLLC_POSTDIV);
194 uint32_t postdiv_ratio = (postdiv_reg & AM18XX_PLLC_POSTDIV_RATIO) + 1;
195
196 uint32_t plldiv_regaddr;
197 if (clk->clk_index <= 2) {
198 plldiv_regaddr = AM18XX_PLLC_PLLDIV1 + 4 * clk->clk_index;
199 } else {
200 plldiv_regaddr = AM18XX_PLLC_PLLDIV4 + 4 * (clk->clk_index - 3);
201 }
202 uint32_t plldiv_reg = PLLC_READ(sc, plldiv_regaddr);
203 uint32_t plldiv_ratio = (plldiv_reg & AM18XX_PLLC_PLLDIV_RATIO) + 1;
204
205 u_int ref_clk_rate = clk_get_rate(sc->sc_ref_clk);
206
207 if (pllctl_reg & AM18XX_PLLC_PLLCTL_PLLEN) {
208 /* PLL enabled */
209 ref_clk_rate /= prediv_ratio;
210 ref_clk_rate *= pllm_multiplier;
211 ref_clk_rate /= postdiv_ratio;
212 } else {
213 /* bypass mode (ensure we aren't using the other PLL)*/
214 KASSERT((pllctl_reg & AM18XX_PLLC_PLLCTL_EXTCLKSRC) == 0);
215 }
216
217 ref_clk_rate /= plldiv_ratio;
218
219 return ref_clk_rate;
220 }
221
222 static u_int
223 am18xx_pllc_get_auxclk_rate(struct am18xx_pllc_softc *sc,
224 struct am18xx_pllc_clk *clk)
225 {
226 return clk_get_rate(sc->sc_ref_clk);
227 }
228
229 static struct clk *
230 am18xx_pllc_decode(device_t dev, int cc_phandle, const void *data, size_t len)
231 {
232 struct am18xx_pllc_softc * const sc = device_private(dev);
233 const u_int *cells = data;
234
235 if (cc_phandle == sc->sc_sysclk_phandle) {
236 if (len != 4)
237 return NULL;
238 const u_int clock_index = be32toh(cells[0]) - 1;
239 if (clock_index >= sc->sc_config->num_sysclk) {
240 return NULL;
241 }
242
243 return &sc->sc_config->sysclks[clock_index].clk_base;
244 } else if (cc_phandle == sc->sc_auxclk_phandle) {
245 return &sc->sc_config->auxclk->clk_base;
246 }
247
248 return NULL;
249 }
250
251 int
252 am18xx_pllc_match(device_t parent, cfdata_t cf, void *aux)
253 {
254 struct fdt_attach_args * const faa = aux;
255
256 return of_compatible_match(faa->faa_phandle, compat_data);
257 }
258
259 void
260 am18xx_pllc_attach(device_t parent, device_t self, void *aux)
261 {
262 struct am18xx_pllc_softc * const sc = device_private(self);
263 struct fdt_attach_args * const faa = aux;
264 const int phandle = faa->faa_phandle;
265 bus_addr_t addr;
266 bus_size_t size;
267
268 sc->sc_bst = faa->faa_bst;
269 sc->sc_config = of_compatible_lookup(phandle, compat_data)->data;
270
271 /* map PSC control registers */
272 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
273 aprint_error(": couldn't get registers\n");
274 return;
275 }
276 if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) {
277 aprint_error(": couldn't map registers\n");
278 return;
279 }
280
281 /* get parent clock */
282 sc->sc_ref_clk = fdtbus_clock_get_index(phandle, 0);
283 if (sc->sc_ref_clk == NULL) {
284 aprint_error(": couldn't get reference clock\n");
285 return;
286 }
287
288 /* initialize clock domain */
289 sc->sc_clkdom.name = device_xname(self);
290 sc->sc_clkdom.funcs = &am18xx_pllc_clk_funcs;
291 sc->sc_clkdom.priv = sc;
292
293 /* initialize the clocks */
294 sc->sc_config->auxclk->clk_base.domain = &sc->sc_clkdom;
295 clk_attach(&sc->sc_config->auxclk->clk_base);
296 for (int i = 0; i < sc->sc_config->num_sysclk; i++) {
297 sc->sc_config->sysclks[i].clk_base.domain = &sc->sc_clkdom;
298 clk_attach(&sc->sc_config->sysclks[i].clk_base);
299 }
300
301 /* register auxclk fdt controller*/
302 sc->sc_auxclk_phandle = of_find_firstchild_byname(phandle, "auxclk");
303 if (sc->sc_auxclk_phandle < 0) {
304 aprint_error(": couldn't get pll0_auxclk child\n");
305 return;
306 }
307 fdtbus_register_clock_controller(self, sc->sc_auxclk_phandle,
308 &am18xx_pllc_clk_fdt_funcs);
309
310 /* register sysclk fdt controller*/
311 sc->sc_sysclk_phandle = of_find_firstchild_byname(phandle, "sysclk");
312 if (sc->sc_sysclk_phandle < 0) {
313 aprint_error(": couldn't get pll0_sysclk child\n");
314 return;
315 }
316 fdtbus_register_clock_controller(self, sc->sc_sysclk_phandle,
317 &am18xx_pllc_clk_fdt_funcs);
318
319 aprint_normal("\n");
320 }
321
322