Skip to content

Commit 96e35ea

Browse files
committed
evbarm/am18: add psc driver
The PSC is the power and sleep controller.
1 parent 5ceab1b commit 96e35ea

3 files changed

Lines changed: 332 additions & 0 deletions

File tree

sys/arch/arm/ti/am18xx_psc.c

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

sys/arch/arm/ti/files.ti

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@ device ticompclk
7171
attach ticompclk at fdt with ti_comp_clock
7272
file arch/arm/ti/ti_comp_clock.c ti_comp_clock
7373

74+
device am18xxpsc: fdt_clock
75+
attach am18xxpsc at fdt
76+
file arch/arm/ti/am18xx_psc.c am18xxpsc
77+
7478
# UART
7579
attach com at fdt with ti_com: ti_prcm
7680
file arch/arm/ti/ti_com.c ti_com needs-flag

sys/arch/evbarm/conf/GENERIC_V5

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ imx23apbdma* at fdt? pass 1 # NXP i.MX23 DMA controller
7272

7373
# Clock Controllers
7474
imx23clkctrl* at fdt? pass 1 # i.MX23 clock controller
75+
am18xxpsc* at fdt? pass 3 # TI AM18XX power&sleep controller
7576

7677
# general FDT devices
7778
fclock* at fdt? pass 1

0 commit comments

Comments
 (0)