1/* $NetBSD: omap4_prcm.c,v 1.1 2025/12/16 12:20:22 skrll Exp $ */
2
3/*-
4 * Copyright (c) 2025 Rui-Xiang Guo
5 * Copyright (c) 2017 Jared McNeill <jmcneill@invisible.ca>
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#include <sys/cdefs.h>
31
32__KERNEL_RCSID(1, "$NetBSD: omap4_prcm.c,v 1.1 2025/12/16 12:20:22 skrll Exp $");
33
34#include <sys/param.h>
35#include <sys/bus.h>
36#include <sys/device.h>
37#include <sys/systm.h>
38
39#include <dev/fdt/fdtvar.h>
40
41#define	TI_PRCM_PRIVATE
42#include <arm/ti/ti_prcm.h>
43
44#define CM1_ABE		0x0500
45
46#define CM2_CORE	0x0700
47#define CM2_L3INIT	0x1300
48#define CM2_L4PER	0x1400
49
50#define CM_WKUP		0x1800
51
52#define	CLKCTRL_MODULEMODE		__BITS(1,0)
53#define	CLKCTRL_MODULEMODE_DISABLE	0x0
54#define	CLKCTRL_MODULEMODE_AUTO		0x1
55#define	CLKCTRL_MODULEMODE_ENABLE	0x2
56
57#define	CLKCTRL_IDLEST			__BITS(17,16)
58#define CLKCTRL_IDLEST_ENABLE		0x0
59#define CLKCTRL_IDLEST_BUSY		0x1
60#define CLKCTRL_IDLEST_IDLE		0x2
61#define CLKCTRL_IDLEST_DISABLE		0x3
62
63enum omap4_prcm_inst {
64	PRCM_CM1 = 1,
65	PRCM_CM2,
66	PRCM_PRM,
67};
68
69struct omap4_prcm_softc {
70	struct ti_prcm_softc	sc_prcm;	/* must be first */
71	bus_addr_t		sc_regbase;
72	enum omap4_prcm_inst	sc_inst;
73};
74
75static int omap4_prcm_match(device_t, cfdata_t, void *);
76static void omap4_prcm_attach(device_t, device_t, void *);
77
78static int
79omap4_prcm_hwmod_enable(struct ti_prcm_softc *sc, struct ti_prcm_clk *tc, int enable)
80{
81	uint32_t val;
82	int retry;
83
84	val = PRCM_READ(sc, tc->u.hwmod.reg);
85	val &= ~CLKCTRL_MODULEMODE;
86	if (enable) {
87		val |= __SHIFTIN(CLKCTRL_MODULEMODE_ENABLE,
88					 CLKCTRL_MODULEMODE);
89	} else
90		val |= __SHIFTIN(CLKCTRL_MODULEMODE_DISABLE,
91					 CLKCTRL_MODULEMODE);
92	PRCM_WRITE(sc, tc->u.hwmod.reg, val);
93
94	for (retry = 100; retry > 0; retry--) {
95		val = PRCM_READ(sc, tc->u.hwmod.reg);
96		if ((val & CLKCTRL_IDLEST) == CLKCTRL_IDLEST_ENABLE)
97			break;
98		delay(10);
99	}
100
101	return (retry >= 0) ? 0 : ETIMEDOUT;
102}
103
104static int
105omap4_prcm_hwmod_enable_auto(struct ti_prcm_softc *sc, struct ti_prcm_clk *tc, int enable)
106{
107	uint32_t val;
108	int retry;
109
110	val = PRCM_READ(sc, tc->u.hwmod.reg);
111	val &= ~CLKCTRL_MODULEMODE;
112	if (enable) {
113		val |= __SHIFTIN(CLKCTRL_MODULEMODE_AUTO,
114					 CLKCTRL_MODULEMODE);
115	} else
116		val |= __SHIFTIN(CLKCTRL_MODULEMODE_DISABLE,
117					 CLKCTRL_MODULEMODE);
118	PRCM_WRITE(sc, tc->u.hwmod.reg, val);
119
120	for (retry = 100; retry > 0; retry--) {
121		val = PRCM_READ(sc, tc->u.hwmod.reg);
122		if ((val & CLKCTRL_IDLEST) == CLKCTRL_IDLEST_ENABLE)
123			break;
124		delay(10);
125	}
126
127	return (retry >= 0) ? 0 : ETIMEDOUT;
128}
129
130#define	OMAP4_PRCM_HWMOD_CM1_ABE(_name, _reg, _parent)	\
131	TI_PRCM_HWMOD((_name), CM1_ABE + (_reg), (_parent), omap4_prcm_hwmod_enable)
132#define	OMAP4_PRCM_HWMOD_CM2_L3INIT(_name, _reg, _parent)	\
133	TI_PRCM_HWMOD((_name), CM2_L3INIT + (_reg), (_parent), omap4_prcm_hwmod_enable)
134#define	OMAP4_PRCM_HWMOD_CM2_L3INIT_AUTO(_name, _reg, _parent)	\
135	TI_PRCM_HWMOD((_name), CM2_L3INIT + (_reg), (_parent), omap4_prcm_hwmod_enable_auto)
136#define	OMAP4_PRCM_HWMOD_CM2_L4PER(_name, _reg, _parent)	\
137	TI_PRCM_HWMOD((_name), CM2_L4PER + (_reg), (_parent), omap4_prcm_hwmod_enable)
138#define	OMAP4_PRCM_HWMOD_CM2_L4PER_AUTO(_name, _reg, _parent)	\
139	TI_PRCM_HWMOD((_name), CM2_L4PER + (_reg), (_parent), omap4_prcm_hwmod_enable_auto)
140#define	OMAP4_PRCM_HWMOD_CM_WKUP(_name, _reg, _parent)	\
141	TI_PRCM_HWMOD((_name), CM_WKUP + (_reg), (_parent), omap4_prcm_hwmod_enable)
142#define	OMAP4_PRCM_HWMOD_CM_WKUP_AUTO(_name, _reg, _parent)	\
143	TI_PRCM_HWMOD((_name), CM_WKUP + (_reg), (_parent), omap4_prcm_hwmod_enable_auto)
144
145static const struct device_compatible_entry compat_data[] = {
146	{ .compat = "ti,omap4-cm1",	.value = PRCM_CM1 },
147	{ .compat = "ti,omap4-cm2",	.value = PRCM_CM2 },
148	{ .compat = "ti,omap4-prm",	.value = PRCM_PRM },
149	{ .compat = "ti,omap4-scrm",	.value = PRCM_PRM },
150	DEVICE_COMPAT_EOL
151};
152
153static const struct device_compatible_entry cm_compat_data[] = {
154	{ .compat = "ti,omap4-cm" },
155	DEVICE_COMPAT_EOL
156};
157
158static const struct device_compatible_entry clkctrl_compat_data[] = {
159	{ .compat = "ti,clkctrl" },
160	DEVICE_COMPAT_EOL
161};
162
163CFATTACH_DECL_NEW(omap4_prcm, sizeof(struct omap4_prcm_softc),
164	omap4_prcm_match, omap4_prcm_attach, NULL, NULL);
165
166static struct ti_prcm_clk omap4_cm1_clks[] = {
167	/* XXX until we get a proper clock tree */
168	TI_PRCM_FIXED("FIXED_32K", 32768),
169	TI_PRCM_FIXED("FIXED_24MHZ", 24000000),
170
171	OMAP4_PRCM_HWMOD_CM1_ABE("timer5", 0x68, "FIXED_24MHZ"),
172	OMAP4_PRCM_HWMOD_CM1_ABE("timer6", 0x70, "FIXED_24MHZ"),
173	OMAP4_PRCM_HWMOD_CM1_ABE("timer7", 0x78, "FIXED_24MHZ"),
174	OMAP4_PRCM_HWMOD_CM1_ABE("timer8", 0x80, "FIXED_24MHZ"),
175//	OMAP4_PRCM_HWMOD_CM1_ABE("wd_timer3", 0x88, "FIXED_32K"),
176};
177
178static struct ti_prcm_clk omap4_cm2_clks[] = {
179	/* XXX until we get a proper clock tree */
180	TI_PRCM_FIXED("FIXED_32K", 32768),
181	TI_PRCM_FIXED("FIXED_24MHZ", 24000000),
182	TI_PRCM_FIXED("FIXED_48MHZ", 48000000),
183	TI_PRCM_FIXED("FIXED_96MHZ", 96000000),
184	TI_PRCM_FIXED_FACTOR("PERIPH_CLK", 1, 1, "FIXED_48MHZ"),
185	TI_PRCM_FIXED_FACTOR("MMC_CLK", 1, 1, "FIXED_96MHZ"),
186
187	OMAP4_PRCM_HWMOD_CM2_L4PER("uart1", 0x140, "PERIPH_CLK"),
188	OMAP4_PRCM_HWMOD_CM2_L4PER("uart2", 0x148, "PERIPH_CLK"),
189	OMAP4_PRCM_HWMOD_CM2_L4PER("uart3", 0x150, "PERIPH_CLK"),
190	OMAP4_PRCM_HWMOD_CM2_L4PER("uart4", 0x158, "PERIPH_CLK"),
191
192	OMAP4_PRCM_HWMOD_CM2_L4PER("i2c1", 0xa0, "PERIPH_CLK"),
193	OMAP4_PRCM_HWMOD_CM2_L4PER("i2c2", 0xa8, "PERIPH_CLK"),
194	OMAP4_PRCM_HWMOD_CM2_L4PER("i2c3", 0xb0, "PERIPH_CLK"),
195	OMAP4_PRCM_HWMOD_CM2_L4PER("i2c4", 0xb8, "PERIPH_CLK"),
196
197	OMAP4_PRCM_HWMOD_CM2_L4PER_AUTO("gpio2", 0x60, "PERIPH_CLK"),
198	OMAP4_PRCM_HWMOD_CM2_L4PER_AUTO("gpio3", 0x68, "PERIPH_CLK"),
199	OMAP4_PRCM_HWMOD_CM2_L4PER_AUTO("gpio4", 0x70, "PERIPH_CLK"),
200	OMAP4_PRCM_HWMOD_CM2_L4PER_AUTO("gpio5", 0x78, "PERIPH_CLK"),
201	OMAP4_PRCM_HWMOD_CM2_L4PER_AUTO("gpio6", 0x80, "PERIPH_CLK"),
202
203	OMAP4_PRCM_HWMOD_CM2_L4PER("timer2", 0x38, "FIXED_24MHZ"),
204	OMAP4_PRCM_HWMOD_CM2_L4PER("timer3", 0x40, "FIXED_24MHZ"),
205	OMAP4_PRCM_HWMOD_CM2_L4PER("timer4", 0x48, "FIXED_24MHZ"),
206	OMAP4_PRCM_HWMOD_CM2_L4PER("timer9", 0x50, "FIXED_24MHZ"),
207	OMAP4_PRCM_HWMOD_CM2_L4PER("timer10", 0x28, "FIXED_24MHZ"),
208	OMAP4_PRCM_HWMOD_CM2_L4PER("timer11", 0x30, "FIXED_24MHZ"),
209
210	OMAP4_PRCM_HWMOD_CM2_L3INIT("mmc1", 0x28, "MMC_CLK"),
211	OMAP4_PRCM_HWMOD_CM2_L3INIT("mmc2", 0x30, "MMC_CLK"),
212	OMAP4_PRCM_HWMOD_CM2_L4PER("mmc3", 0x120, "PERIPH_CLK"),
213	OMAP4_PRCM_HWMOD_CM2_L4PER("mmc4", 0x128, "PERIPH_CLK"),
214	OMAP4_PRCM_HWMOD_CM2_L4PER("mmc5", 0x160, "PERIPH_CLK"),
215
216	OMAP4_PRCM_HWMOD_CM2_L3INIT("usb_host_hs", 0x58, "PERIPH_CLK"),
217	OMAP4_PRCM_HWMOD_CM2_L3INIT_AUTO("usb_otg_hs", 0x60, "PERIPH_CLK"),
218	OMAP4_PRCM_HWMOD_CM2_L3INIT_AUTO("usb_tll_hs", 0x68, "PERIPH_CLK"),
219
220//	OMAP4_PRCM_HWMOD_CM2_L4PER_AUTO("rng", 0x1c0, "PERIPH_CLK"),
221};
222
223static struct ti_prcm_clk omap4_prm_clks[] = {
224	/* XXX until we get a proper clock tree */
225	TI_PRCM_FIXED("FIXED_32K", 32768),
226	TI_PRCM_FIXED("FIXED_48MHZ", 48000000),
227	TI_PRCM_FIXED_FACTOR("PERIPH_CLK", 1, 1, "FIXED_48MHZ"),
228
229	OMAP4_PRCM_HWMOD_CM_WKUP_AUTO("gpio1", 0x38, "PERIPH_CLK"),
230	OMAP4_PRCM_HWMOD_CM_WKUP("timer1", 0x40, "FIXED_32K"),
231	OMAP4_PRCM_HWMOD_CM_WKUP("wd_timer2", 0x30, "FIXED_32K"),
232};
233
234static struct clk *
235omap4_prcm_clock_decode(device_t dev, int cc_phandle, const void *data, size_t len)
236{
237	struct omap4_prcm_softc * const sc = device_private(dev);
238	const u_int *cells = data;
239	bus_addr_t regbase;
240	u_int n;
241
242	if (len != 8)
243		return NULL;
244
245	bus_size_t regoff = be32toh(cells[0]);
246	const u_int clock_index = be32toh(cells[1]);
247
248	/* XXX not sure how to handle this yet */
249	if (clock_index != 0)
250		return NULL;
251
252	/*
253	 * Register offset in specifier is relative to base address of the
254	 * clock node. Translate this to an address relative to the start
255	 * of PRCM space.
256	 */
257	if (fdtbus_get_reg(cc_phandle, 0, &regbase, NULL) != 0)
258		return NULL;
259	regoff += (regbase - sc->sc_regbase);
260
261	/*
262	 * Look for a matching hwmod.
263	 */
264	for (n = 0; n < sc->sc_prcm.sc_nclks; n++) {
265		struct ti_prcm_clk *tclk = &sc->sc_prcm.sc_clks[n];
266		if (tclk->type != TI_PRCM_HWMOD)
267			continue;
268
269		if (tclk->u.hwmod.reg == regoff)
270			return &tclk->base;
271	}
272
273	/* Not found */
274	return NULL;
275}
276
277static const struct fdtbus_clock_controller_func omap4_prcm_clock_fdt_funcs = {
278	.decode = omap4_prcm_clock_decode
279};
280
281static int
282omap4_prcm_match(device_t parent, cfdata_t cf, void *aux)
283{
284	struct fdt_attach_args * const faa = aux;
285
286	return of_compatible_match(faa->faa_phandle, compat_data);
287}
288
289static void
290omap4_prcm_attach(device_t parent, device_t self, void *aux)
291{
292	struct omap4_prcm_softc * const sc = device_private(self);
293	struct fdt_attach_args * const faa = aux;
294	const int phandle = faa->faa_phandle;
295	int clocks, child, cm_child;
296	char iname[5];
297
298	if (fdtbus_get_reg(phandle, 0, &sc->sc_regbase, NULL) != 0) {
299		aprint_error(": couldn't get registers\n");
300		return;
301	}
302
303	sc->sc_prcm.sc_dev = self;
304	sc->sc_prcm.sc_phandle = phandle;
305	sc->sc_prcm.sc_bst = faa->faa_bst;
306	sc->sc_inst = of_compatible_lookup(phandle, compat_data)->value;
307
308	switch (sc->sc_inst) {
309	case PRCM_CM1:
310		sc->sc_prcm.sc_clks = omap4_cm1_clks;
311		sc->sc_prcm.sc_nclks = __arraycount(omap4_cm1_clks);
312		strcpy(iname, "CM1");
313		break;
314	case PRCM_CM2:
315		sc->sc_prcm.sc_clks = omap4_cm2_clks;
316		sc->sc_prcm.sc_nclks = __arraycount(omap4_cm2_clks);
317		strcpy(iname, "CM2");
318		break;
319	case PRCM_PRM:
320		sc->sc_prcm.sc_clks = omap4_prm_clks;
321		sc->sc_prcm.sc_nclks = __arraycount(omap4_prm_clks);
322		strcpy(iname, "PRM");
323		break;
324	default:
325		aprint_error(": unsupported instance\n");
326		return;
327	}
328
329	if (ti_prcm_attach(&sc->sc_prcm) != 0)
330		return;
331
332	aprint_naive("\n");
333	aprint_normal(": OMAP44xx PRCM (%s)\n", iname);
334
335	for (child = OF_child(phandle); child; child = OF_peer(child)) {
336		if (of_compatible_match(child, cm_compat_data) == 0)
337			continue;
338
339		for (cm_child =	OF_child(child); cm_child;
340		     cm_child = OF_peer(cm_child)) {
341			if (of_compatible_match(cm_child,
342						 clkctrl_compat_data) == 0)
343				continue;
344
345			aprint_debug_dev(self, "clkctrl: %s\n", fdtbus_get_string(cm_child, "name"));
346			fdtbus_register_clock_controller(self, cm_child,
347			    &omap4_prcm_clock_fdt_funcs);
348		}
349	}
350
351	clocks = of_find_firstchild_byname(phandle, "clocks");
352	if (clocks > 0)
353		fdt_add_bus(self, clocks, faa);
354}
355