Skip to content

Commit 162bb91

Browse files
committed
evbarm/am18: add psc driver
The PSC is the power and sleep controller.
1 parent 984e62d commit 162bb91

3 files changed

Lines changed: 331 additions & 0 deletions

File tree

sys/arch/arm/ti/am18_psc.c

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

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 am18psc: fdt_clock
75+
attach am18psc at fdt
76+
file arch/arm/ti/am18_psc.c am18psc
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+
am18psc* at fdt? pass 3 # am18 power&sleep controller
7576

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

0 commit comments

Comments
 (0)