Skip to content

Commit 6500f5b

Browse files
committed
evbarm/am18xx: add pll controller
1 parent 1d64049 commit 6500f5b

3 files changed

Lines changed: 330 additions & 1 deletion

File tree

sys/arch/arm/ti/am18xx_pllc.c

Lines changed: 321 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,321 @@
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+

sys/arch/arm/ti/files.ti

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,10 @@ device am18xxpsc: fdt_clock
7575
attach am18xxpsc at fdt
7676
file arch/arm/ti/am18xx_psc.c am18xxpsc
7777

78+
device am18xxpllc: fdt_clock
79+
attach am18xxpllc at fdt
80+
file arch/arm/ti/am18xx_pllc.c am18xxpllc
81+
7882
# UART
7983
attach com at fdt with ti_com: ti_prcm
8084
file arch/arm/ti/ti_com.c ti_com needs-flag

sys/arch/evbarm/conf/GENERIC_V5

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,11 @@ 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
75+
am18xxpllc* at fdt? pass 2 # TI AM18XX pll controller
76+
am18xxpsc* at fdt? pass 4 # TI AM18XX power&sleep controller
77+
78+
# System Controller
79+
syscon* at fdt? pass 1 # Generic System Controller
7680

7781
# general FDT devices
7882
fclock* at fdt? pass 1

0 commit comments

Comments
 (0)