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