am18xx_psc.c revision 1.1 1 1.1 yurix /* $NetBSD $ */
2 1.1 yurix
3 1.1 yurix /*-
4 1.1 yurix * Copyright (c) 2025 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 * Power&Sleep 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/cdefs.h>
38 1.1 yurix #include <sys/device.h>
39 1.1 yurix
40 1.1 yurix #include <dev/clk/clk_backend.h>
41 1.1 yurix #include <dev/fdt/fdtvar.h>
42 1.1 yurix
43 1.1 yurix #include <arm/fdt/arm_fdtvar.h>
44 1.1 yurix
45 1.1 yurix #define MAX_PARENT_CLOCKS 5
46 1.1 yurix
47 1.1 yurix struct am18xx_psc_clk {
48 1.1 yurix struct clk clk_base;
49 1.1 yurix int clk_index;
50 1.1 yurix union {
51 1.1 yurix const char *clk_parent_name;
52 1.1 yurix struct clk *clk_parent;
53 1.1 yurix } u;
54 1.1 yurix };
55 1.1 yurix
56 1.1 yurix struct am18xx_psc_config {
57 1.1 yurix struct am18xx_psc_clk *clks;
58 1.1 yurix int clknum;
59 1.1 yurix };
60 1.1 yurix
61 1.1 yurix struct am18xx_psc_softc {
62 1.1 yurix bus_space_tag_t sc_bst;
63 1.1 yurix bus_space_handle_t sc_bsh;
64 1.1 yurix struct clk_domain sc_clkdom;
65 1.1 yurix struct clk parent_clocks[MAX_PARENT_CLOCKS];
66 1.1 yurix struct am18xx_psc_clk *sc_clks;
67 1.1 yurix int sc_clknum;
68 1.1 yurix };
69 1.1 yurix
70 1.1 yurix static int am18xx_psc_match(device_t, cfdata_t, void *);
71 1.1 yurix static void am18xx_psc_attach(device_t, device_t, void *);
72 1.1 yurix static struct clk * am18xx_psc_decode(device_t, int, const void *, size_t);
73 1.1 yurix static struct clk * am18xx_psc_clk_get(void *, const char *);
74 1.1 yurix static u_int am18xx_psc_clk_get_rate(void *, struct clk *);
75 1.1 yurix static int am18xx_psc_clk_enable(void *, struct clk *);
76 1.1 yurix static int am18xx_psc_clk_disable(void *, struct clk *);
77 1.1 yurix static struct clk * am18xx_psc_clk_get_parent(void *, struct clk *);
78 1.1 yurix static void am18xx_psc_clk_transition(struct am18xx_psc_softc *,
79 1.1 yurix struct am18xx_psc_clk *,
80 1.1 yurix uint32_t);
81 1.1 yurix
82 1.1 yurix CFATTACH_DECL_NEW(am18xxpsc, sizeof(struct am18xx_psc_softc),
83 1.1 yurix am18xx_psc_match, am18xx_psc_attach, NULL, NULL);
84 1.1 yurix
85 1.1 yurix #define AM18XX_PSC_PTCMD 0x120
86 1.1 yurix #define AM18XX_PSC_PTSTAT 0x128
87 1.1 yurix #define AM18XX_PSC_MDCTL0 0xA00
88 1.1 yurix
89 1.1 yurix #define AM18XX_PSC_PTCMD_GO_ALL 0x3
90 1.1 yurix #define AM18XX_PSC_PTSTAT_MASK 0x3
91 1.1 yurix #define AM18XX_PSC_MDCTL_DISABLE 0x2
92 1.1 yurix #define AM18XX_PSC_MDCTL_ENABLE 0x3
93 1.1 yurix
94 1.1 yurix #define PSC_READ(sc, reg) \
95 1.1 yurix bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, reg)
96 1.1 yurix #define PSC_WRITE(sc, reg, val) \
97 1.1 yurix bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, reg, val)
98 1.1 yurix
99 1.1 yurix #define PSC_CLK(_i, _name, _p) \
100 1.1 yurix { \
101 1.1 yurix .clk_index = (_i), \
102 1.1 yurix .u.clk_parent_name = (_p), \
103 1.1 yurix .clk_base.name = (_name), \
104 1.1 yurix .clk_base.flags = 0, \
105 1.1 yurix }
106 1.1 yurix
107 1.1 yurix static struct am18xx_psc_clk am18xx_psc0_clks[] = {
108 1.1 yurix PSC_CLK(0, "edma0_cc0", "pll0_sysclk2"),
109 1.1 yurix PSC_CLK(1, "edma0_tc0", "pll0_sysclk2"),
110 1.1 yurix PSC_CLK(2, "edma0_tc1", "pll0_sysclk2"),
111 1.1 yurix PSC_CLK(3, "emifa", "async1"),
112 1.1 yurix PSC_CLK(4, "spi0", "pll0_sysclk2"),
113 1.1 yurix PSC_CLK(5, "sdmmc0", "pll0_sysclk2"),
114 1.1 yurix PSC_CLK(6, "aintc", "pll0_sysclk4"),
115 1.1 yurix PSC_CLK(7, "imem", "pll0_sysclk2"),
116 1.1 yurix PSC_CLK(8, NULL, NULL), /* unused */
117 1.1 yurix PSC_CLK(9, "uart0", "pll0_sysclk2"),
118 1.1 yurix PSC_CLK(10, NULL, NULL), /* scr0; purpose and parent clk unclear */
119 1.1 yurix PSC_CLK(11, NULL, NULL), /* scr1; purpose and parent clk unclear */
120 1.1 yurix PSC_CLK(12, NULL, NULL), /* scr2; purpose and parent clk unclear */
121 1.1 yurix PSC_CLK(13, "pru", "pll0_sysclk2"),
122 1.1 yurix PSC_CLK(14, "arm", "pll0_sysclk6"),
123 1.1 yurix };
124 1.1 yurix
125 1.1 yurix static struct am18xx_psc_clk am18xx_psc1_clks[] = {
126 1.1 yurix PSC_CLK(0, "edma1_cc0", "pll0_sysclk2"),
127 1.1 yurix PSC_CLK(1, "usb2_0", "pll0_sysclk2"),
128 1.1 yurix PSC_CLK(2, "usb1_1", "pll0_sysclk4"),
129 1.1 yurix PSC_CLK(3, "gpio", "pll0_sysclk4"),
130 1.1 yurix PSC_CLK(4, "hpi", "pll0_sysclk2"),
131 1.1 yurix PSC_CLK(5, "emac", "pll0_sysclk4"),
132 1.1 yurix PSC_CLK(6, "ddr", "pll0_sysclk2"),
133 1.1 yurix PSC_CLK(7, "mcasp0", "async3"),
134 1.1 yurix PSC_CLK(8, "sata", "pll0_sysclk2"),
135 1.1 yurix PSC_CLK(9, "vpif", "pll0_sysclk2"),
136 1.1 yurix PSC_CLK(10, "spi1", "async3"),
137 1.1 yurix PSC_CLK(11, "i2c1", "pll0_sysclk4"),
138 1.1 yurix PSC_CLK(12, "uart1", "async3"),
139 1.1 yurix PSC_CLK(13, "uart2", "async3"),
140 1.1 yurix PSC_CLK(14, "mcbsp0", "async3"),
141 1.1 yurix PSC_CLK(15, "mcbsp1", "async3"),
142 1.1 yurix PSC_CLK(16, "lcdc", "pll0_sysclk2"),
143 1.1 yurix PSC_CLK(17, "ehrpwm", "async3"),
144 1.1 yurix PSC_CLK(18, "sdmmc1", "pll0_sysclk2"),
145 1.1 yurix PSC_CLK(19, "upp", "pll0_sysclk2"),
146 1.1 yurix PSC_CLK(20, "ecap", "async3"),
147 1.1 yurix PSC_CLK(21, "edma1_tc0", "pll0_sysclk2"),
148 1.1 yurix };
149 1.1 yurix
150 1.1 yurix static const struct am18xx_psc_config am18xx_psc0_config = {
151 1.1 yurix .clks = am18xx_psc0_clks,
152 1.1 yurix .clknum = __arraycount(am18xx_psc0_clks)
153 1.1 yurix };
154 1.1 yurix
155 1.1 yurix static const struct am18xx_psc_config am18xx_psc1_config = {
156 1.1 yurix .clks = am18xx_psc1_clks,
157 1.1 yurix .clknum = __arraycount(am18xx_psc1_clks)
158 1.1 yurix };
159 1.1 yurix
160 1.1 yurix static const struct device_compatible_entry compat_data[] = {
161 1.1 yurix { .compat = "ti,da850-psc0", .data = &am18xx_psc0_config },
162 1.1 yurix { .compat = "ti,da850-psc1", .data = &am18xx_psc1_config },
163 1.1 yurix DEVICE_COMPAT_EOL
164 1.1 yurix };
165 1.1 yurix
166 1.1 yurix static const struct fdtbus_clock_controller_func am18xx_psc_clk_fdt_funcs = {
167 1.1 yurix .decode = am18xx_psc_decode,
168 1.1 yurix };
169 1.1 yurix
170 1.1 yurix static const struct clk_funcs am18xx_psc_clk_funcs = {
171 1.1 yurix .get = am18xx_psc_clk_get,
172 1.1 yurix .get_rate = am18xx_psc_clk_get_rate,
173 1.1 yurix .enable = am18xx_psc_clk_enable,
174 1.1 yurix .disable = am18xx_psc_clk_disable,
175 1.1 yurix .get_parent = am18xx_psc_clk_get_parent
176 1.1 yurix };
177 1.1 yurix
178 1.1 yurix static void
179 1.1 yurix am18xx_psc_clk_transition(struct am18xx_psc_softc *sc,
180 1.1 yurix struct am18xx_psc_clk *clk, uint32_t state)
181 1.1 yurix {
182 1.1 yurix /* update clock gate state */
183 1.1 yurix uint32_t mdctl_reg = AM18XX_PSC_MDCTL0 + 4*clk->clk_index;
184 1.1 yurix PSC_WRITE(sc, mdctl_reg, state);
185 1.1 yurix PSC_WRITE(sc, AM18XX_PSC_PTCMD, AM18XX_PSC_PTCMD_GO_ALL);
186 1.1 yurix
187 1.1 yurix /* wait for clock state transition to finish */
188 1.1 yurix while (PSC_READ(sc, AM18XX_PSC_PTSTAT) & AM18XX_PSC_PTSTAT_MASK)
189 1.1 yurix continue;
190 1.1 yurix }
191 1.1 yurix
192 1.1 yurix static struct clk *
193 1.1 yurix am18xx_psc_clk_get(void *priv, const char *name)
194 1.1 yurix {
195 1.1 yurix struct am18xx_psc_softc * const sc = priv;
196 1.1 yurix
197 1.1 yurix for (int i = 0; i < sc->sc_clknum; i++) {
198 1.1 yurix if (strcmp(sc->sc_clks[i].clk_base.name, name) == 0)
199 1.1 yurix return &sc->sc_clks[i].clk_base;
200 1.1 yurix }
201 1.1 yurix
202 1.1 yurix return NULL;
203 1.1 yurix }
204 1.1 yurix
205 1.1 yurix static u_int
206 1.1 yurix am18xx_psc_clk_get_rate(void *priv, struct clk *clkp)
207 1.1 yurix {
208 1.1 yurix struct am18xx_psc_clk *clk = (struct am18xx_psc_clk *)clkp;
209 1.1 yurix
210 1.1 yurix /* The PSC is only for clock gates, rates are passed through */
211 1.1 yurix return clk_get_rate(clk->u.clk_parent);
212 1.1 yurix }
213 1.1 yurix
214 1.1 yurix static int
215 1.1 yurix am18xx_psc_clk_enable(void *priv, struct clk *clkp)
216 1.1 yurix {
217 1.1 yurix struct am18xx_psc_softc * const sc = priv;
218 1.1 yurix struct am18xx_psc_clk *clk = (struct am18xx_psc_clk *)clkp;
219 1.1 yurix
220 1.1 yurix am18xx_psc_clk_transition(sc, clk, AM18XX_PSC_MDCTL_ENABLE);
221 1.1 yurix
222 1.1 yurix return 0;
223 1.1 yurix }
224 1.1 yurix
225 1.1 yurix static int
226 1.1 yurix am18xx_psc_clk_disable(void *priv, struct clk *clkp)
227 1.1 yurix {
228 1.1 yurix struct am18xx_psc_softc * const sc = priv;
229 1.1 yurix struct am18xx_psc_clk *clk = (struct am18xx_psc_clk *)clkp;
230 1.1 yurix
231 1.1 yurix am18xx_psc_clk_transition(sc, clk, AM18XX_PSC_MDCTL_DISABLE);
232 1.1 yurix
233 1.1 yurix return 0;
234 1.1 yurix }
235 1.1 yurix
236 1.1 yurix static struct clk *
237 1.1 yurix am18xx_psc_clk_get_parent(void *priv, struct clk *clkp)
238 1.1 yurix {
239 1.1 yurix struct am18xx_psc_clk *clk = (struct am18xx_psc_clk *)clkp;
240 1.1 yurix
241 1.1 yurix return clk->u.clk_parent;
242 1.1 yurix }
243 1.1 yurix
244 1.1 yurix static struct clk *
245 1.1 yurix am18xx_psc_decode(device_t dev, int cc_phandle, const void *data, size_t len)
246 1.1 yurix {
247 1.1 yurix struct am18xx_psc_softc * const sc = device_private(dev);
248 1.1 yurix const u_int *cells = data;
249 1.1 yurix
250 1.1 yurix if (len != 4)
251 1.1 yurix return NULL;
252 1.1 yurix const u_int clock_index = be32toh(cells[0]);
253 1.1 yurix
254 1.1 yurix if (clock_index >= sc->sc_clknum)
255 1.1 yurix return NULL;
256 1.1 yurix
257 1.1 yurix struct am18xx_psc_clk *clk = &sc->sc_clks[clock_index];
258 1.1 yurix
259 1.1 yurix return &clk->clk_base;
260 1.1 yurix }
261 1.1 yurix
262 1.1 yurix int
263 1.1 yurix am18xx_psc_match(device_t parent, cfdata_t cf, void *aux)
264 1.1 yurix {
265 1.1 yurix struct fdt_attach_args * const faa = aux;
266 1.1 yurix
267 1.1 yurix return of_compatible_match(faa->faa_phandle, compat_data);
268 1.1 yurix }
269 1.1 yurix
270 1.1 yurix void
271 1.1 yurix am18xx_psc_attach(device_t parent, device_t self, void *aux)
272 1.1 yurix {
273 1.1 yurix struct am18xx_psc_softc * const sc = device_private(self);
274 1.1 yurix struct fdt_attach_args * const faa = aux;
275 1.1 yurix const int phandle = faa->faa_phandle;
276 1.1 yurix bus_addr_t addr;
277 1.1 yurix bus_size_t size;
278 1.1 yurix const struct am18xx_psc_config *config;
279 1.1 yurix
280 1.1 yurix sc->sc_bst = faa->faa_bst;
281 1.1 yurix
282 1.1 yurix /* map PSC control registers */
283 1.1 yurix if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
284 1.1 yurix aprint_error(": couldn't get registers\n");
285 1.1 yurix return;
286 1.1 yurix }
287 1.1 yurix if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) {
288 1.1 yurix aprint_error(": couldn't map registers\n");
289 1.1 yurix return;
290 1.1 yurix }
291 1.1 yurix
292 1.1 yurix /* initialize clock domain */
293 1.1 yurix sc->sc_clkdom.name = device_xname(self);
294 1.1 yurix sc->sc_clkdom.funcs = &am18xx_psc_clk_funcs;
295 1.1 yurix sc->sc_clkdom.priv = sc;
296 1.1 yurix
297 1.1 yurix /* wire up different clock tables depending on if it is psc0 or psc1 */
298 1.1 yurix config = of_compatible_lookup(phandle, compat_data)->data;
299 1.1 yurix sc->sc_clks = config->clks;
300 1.1 yurix sc->sc_clknum = config->clknum;
301 1.1 yurix
302 1.1 yurix /* get parent clock, then attach the clk gate */
303 1.1 yurix for (int i = 0; i < sc->sc_clknum; i++) {
304 1.1 yurix /* skip unused clock gates */
305 1.1 yurix if (sc->sc_clks[i].clk_base.name == NULL)
306 1.1 yurix continue;
307 1.1 yurix
308 1.1 yurix struct clk *parent_clk =
309 1.1 yurix fdtbus_clock_get(phandle, sc->sc_clks[i].u.clk_parent_name);
310 1.1 yurix if (parent_clk == NULL) {
311 1.1 yurix aprint_error(": couldn't get clock parent for %s\n",
312 1.1 yurix sc->sc_clks[i].clk_base.name);
313 1.1 yurix return;
314 1.1 yurix }
315 1.1 yurix sc->sc_clks[i].u.clk_parent = parent_clk;
316 1.1 yurix sc->sc_clks[i].clk_base.domain = &sc->sc_clkdom;
317 1.1 yurix
318 1.1 yurix clk_attach(&sc->sc_clks[i].clk_base);
319 1.1 yurix }
320 1.1 yurix
321 1.1 yurix /* register it int he fdt subsystem */
322 1.1 yurix fdtbus_register_clock_controller(self, phandle,
323 1.1 yurix &am18xx_psc_clk_fdt_funcs);
324 1.1 yurix
325 1.1 yurix aprint_normal("\n");
326 1.1 yurix }
327 1.1 yurix
328