1/*	$NetBSD: imx6_platform.c,v 1.9 2023/05/24 16:43:40 bouyer Exp $	*/
2
3/*-
4 * Copyright (c) 2019 Genetec Corporation.  All rights reserved.
5 * Written by Hashimoto Kenichi for Genetec Corporation.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include <sys/cdefs.h>
30__KERNEL_RCSID(0, "$NetBSD: imx6_platform.c,v 1.9 2023/05/24 16:43:40 bouyer Exp $");
31
32#include "arml2cc.h"
33#include "opt_console.h"
34#include "opt_fdt.h"
35#include "opt_multiprocessor.h"
36#include "opt_soc.h"
37
38#include <sys/param.h>
39#include <sys/bus.h>
40#include <sys/cpu.h>
41#include <sys/device.h>
42#include <sys/termios.h>
43
44#include <dev/fdt/fdtvar.h>
45
46#include <arm/fdt/arm_fdtvar.h>
47
48#include <uvm/uvm_extern.h>
49
50#include <arm/arm32/machdep.h>
51
52#include <machine/bootconfig.h>
53#include <arm/cpufunc.h>
54
55#include <arm/cortex/a9tmr_var.h>
56#include <arm/cortex/scu_reg.h>
57#include <arm/cortex/gic_reg.h>
58#include <arm/cortex/pl310_var.h>
59
60#include <arm/nxp/imx6_reg.h>
61#include <arm/nxp/imx6_srcreg.h>
62#include <arm/imx/imxuartreg.h>
63#include <arm/imx/imxwdogreg.h>
64
65#include <arm/nxp/imx6_platform.h>
66
67#include <libfdt.h>
68
69#define	IMX_REF_FREQ	80000000
70#define	IMX6SX_REF_FREQ	24000000
71
72#ifdef VERBOSE_INIT_ARM
73#define VPRINTF(...)	printf(__VA_ARGS__)
74#else
75#define VPRINTF(...)	__nothing
76#endif
77
78extern struct bus_space armv7_generic_bs_tag;
79extern struct arm32_bus_dma_tag arm_generic_dma_tag;
80
81static const struct pmap_devmap *
82imx_platform_devmap(void)
83{
84	static const struct pmap_devmap devmap[] = {
85		DEVMAP_ENTRY(KERNEL_IO_IOREG_VBASE, IMX6_IOREG_PBASE, IMX6_IOREG_SIZE),
86		DEVMAP_ENTRY(KERNEL_IO_ARMCORE_VBASE, IMX6_ARMCORE_PBASE, IMX6_ARMCORE_SIZE),
87		DEVMAP_ENTRY_END
88	};
89
90	return devmap;
91}
92
93static const struct pmap_devmap *
94imx6sx_platform_devmap(void)
95{
96	static const struct pmap_devmap devmap[] = {
97		DEVMAP_ENTRY(KERNEL_IO_IOREG_VBASE, IMX6_IOREG_PBASE, IMX6SX_IOREG_SIZE),
98		DEVMAP_ENTRY(KERNEL_IO_ARMCORE_VBASE, IMX6_ARMCORE_PBASE, IMX6_ARMCORE_SIZE),
99		DEVMAP_ENTRY_END
100	};
101
102	return devmap;
103}
104
105static void
106imx_platform_init_attach_args(struct fdt_attach_args *faa)
107{
108	faa->faa_bst = &armv7_generic_bs_tag;
109	faa->faa_dmat = &arm_generic_dma_tag;
110}
111
112void imx_platform_early_putchar(char);
113
114void __noasan
115imx_platform_early_putchar(char c)
116{
117#ifdef CONSADDR
118#define CONSADDR_VA	((CONSADDR - IMX6_IOREG_PBASE) + KERNEL_IO_IOREG_VBASE)
119
120	volatile uint32_t *uartaddr = cpu_earlydevice_va_p() ?
121	    (volatile uint32_t *)CONSADDR_VA :
122	    (volatile uint32_t *)CONSADDR;
123
124	while ((le32toh(uartaddr[(IMX_USR2/4)]) & IMX_USR2_TXDC) == 0)
125		;
126
127	uartaddr[(IMX_UTXD/4)] = htole32(c);
128#endif
129}
130
131static void
132imx_platform_device_register(device_t self, void *aux)
133{
134	prop_dictionary_t prop = device_properties(self);
135
136	if (device_is_a(self, "atphy")) {
137		static const struct device_compatible_entry compat_data[] = {
138			{ .compat = "fsl,imx6dl-sabresd" },
139			{ .compat = "fsl,imx6q-sabresd" },
140			{ .compat = "fsl,imx6qp-sabresd" },
141			{ .compat = "solidrun,hummingboard2/q" },
142			{ .compat = "solidrun,hummingboard2/dl" },
143			DEVICE_COMPAT_EOL
144		};
145		if (of_compatible_match(OF_finddevice("/"), compat_data))
146			prop_dictionary_set_uint32(prop, "clk_25m", 125000000);
147	}
148}
149
150static u_int
151imx_platform_uart_freq(void)
152{
153	return IMX_REF_FREQ;
154}
155
156static u_int
157imx6sx_platform_uart_freq(void)
158{
159	return IMX6SX_REF_FREQ;
160}
161
162
163static void
164imx_platform_bootstrap(void)
165{
166#if NARML2CC > 0
167	bus_space_tag_t bst = &armv7_generic_bs_tag;
168	bus_space_handle_t bsh;
169	if (bus_space_map(bst, IMX6_ARMCORE_PBASE, IMX6_ARMCORE_SIZE, 0, &bsh))
170		panic("couldn't map armcore registers");
171	arml2cc_init(bst, bsh, ARMCORE_L2C_BASE);
172	bus_space_unmap(bst, bsh, IMX6_ARMCORE_SIZE);
173#endif
174
175	arm_fdt_cpu_bootstrap();
176}
177
178static void
179imx6sx_platform_bootstrap(void)
180{
181	void *fdt_data;
182	int ofw_root;
183	int soc_node, timer_node, intc_node, clks_node;
184	int ret;
185	fdt32_t intval[3];
186	fdt32_t clkval[2];
187	u_int val32;
188
189	imx_platform_bootstrap();
190
191	/*
192	 * if there's no entry for the TWD timer in the provided DTB, fake one.
193	 * we can't boot witthout it.
194	 * The upstream imx6sx.dtsi is missing the entry
195	 */
196
197	fdt_data = __UNCONST(fdtbus_get_data());
198	KASSERT(fdt_data != NULL);
199	ofw_root = OF_peer(0);
200	if (of_find_bycompat(ofw_root, "arm,cortex-a9-twd-timer") > 0) {
201		/* already there */
202		VPRINTF("timer already present\n");
203		return;
204	}
205	VPRINTF("creating timer fdt@%p", fdt_data);
206	soc_node = fdt_path_offset(fdt_data, "/soc");
207	VPRINTF(" soc_node %d", soc_node);
208	KASSERT(soc_node >= 0);
209
210	timer_node = fdt_add_subnode(fdt_data, soc_node, "timer@a00600");
211	VPRINTF(" timer_node %d\n", timer_node);
212	KASSERT(timer_node >= 0);
213
214	ret = fdt_setprop_string(fdt_data, timer_node, "compatible",
215	    "arm,cortex-a9-twd-timer");
216	KASSERTMSG(ret == 0, "fdt_setprop(compatible) returns %d", ret);
217
218	ret = fdt_appendprop_addrrange(fdt_data, soc_node, timer_node,
219	    "reg", 0x00a00600, 0x20);
220	KASSERTMSG(ret == 0, "fdt_appendprop_addrrange returns %d", ret);
221
222	intval[0] = cpu_to_fdt32(1);
223	intval[1] = cpu_to_fdt32(13);
224	intval[2] = cpu_to_fdt32(0xf01);
225	ret = fdt_setprop(fdt_data, timer_node, "interrupts",
226	    intval, sizeof(intval));
227	KASSERTMSG(ret == 0, "fdt_setprop(interrupts) returns %d", ret);
228
229	intc_node = of_find_bycompat(ofw_root, "arm,cortex-a9-gic");
230	KASSERT(intc_node >= 0);
231	val32 = 0;
232	of_getprop_uint32(intc_node, "phandle", &val32);
233	ret = fdt_setprop_u32(fdt_data, timer_node, "interrupt-parent",
234	    val32);
235	KASSERTMSG(ret == 0, "fdt_setprop(interrupt-parent) returns %d", ret);
236
237	val32 = 0;
238	clks_node = of_find_bycompat(ofw_root, "fsl,imx6sx-ccm");
239	KASSERT(clks_node >= 0);
240	of_getprop_uint32(clks_node, "phandle", &val32);
241	clkval[0] = cpu_to_fdt32(val32);
242	clkval[1] = cpu_to_fdt32(30); /* IMX6SXCLK_TWD */
243	ret = fdt_setprop(fdt_data, timer_node, "clocks",
244	    clkval, sizeof(clkval));
245	KASSERTMSG(ret == 0, "fdt_setprop(clocks) returns %d", ret);
246}
247
248static int
249imx_platform_mpstart(void)
250{
251#if defined(MULTIPROCESSOR)
252	bus_space_tag_t bst = &armv7_generic_bs_tag;
253	bus_space_handle_t bsh;
254
255	if (bus_space_map(bst, IMX6_ARMCORE_PBASE, IMX6_ARMCORE_SIZE, 0, &bsh) != 0)
256		panic("couldn't map armcore registers");
257
258	/* Enable Snoop Control Unit */
259	bus_space_write_4(bst, bsh, SCU_INV_ALL_REG, 0xff);
260	bus_space_write_4(bst, bsh, SCU_CTL,
261	    bus_space_read_4(bst, bsh, SCU_CTL) | SCU_CTL_SCU_ENA);
262
263	bus_space_unmap(bst, bsh, AIPS1_SRC_SIZE);
264
265	if (bus_space_map(bst, IMX6_AIPS1_BASE + AIPS1_SRC_BASE, AIPS1_SRC_SIZE, 0, &bsh) != 0)
266		panic("couldn't map SRC");
267
268	uint32_t srcctl = bus_space_read_4(bst, bsh, SRC_SCR);
269	const paddr_t mpstart = KERN_VTOPHYS((vaddr_t)cpu_mpstart);
270
271	srcctl &= ~(SRC_SCR_CORE1_ENABLE | SRC_SCR_CORE2_ENABLE	 |
272	    SRC_SCR_CORE3_ENABLE);
273	bus_space_write_4(bst, bsh, SRC_SCR, srcctl);
274
275	for (int i = 1; i < arm_cpu_max; i++) {
276		bus_space_write_4(bst, bsh, SRC_GPRN_ENTRY(i), mpstart);
277		srcctl |= SRC_SCR_COREN_RST(i);
278		srcctl |= SRC_SCR_COREN_ENABLE(i);
279	}
280	bus_space_write_4(bst, bsh, SRC_SCR, srcctl);
281
282	bus_space_unmap(bst, bsh, AIPS1_SRC_SIZE);
283
284	return arm_fdt_cpu_mpstart();
285#else
286	return 0;
287#endif
288}
289
290static void
291imx6_platform_reset(void)
292{
293	bus_space_tag_t bst = &armv7_generic_bs_tag;
294	bus_space_handle_t bsh;
295
296	if (bus_space_map(bst, IMX6_AIPS1_BASE + AIPS1_WDOG1_BASE, AIPS1_WDOG_SIZE, 0, &bsh))
297		panic("couldn't map wdog1 registers");
298
299	delay(1000);	/* wait for flushing FIFO of serial console */
300
301	cpsid(I32_bit|F32_bit);
302
303	/* software reset signal on wdog */
304	bus_space_write_2(bst, bsh, IMX_WDOG_WCR, WCR_WDE);
305
306	/*
307	 * write twice due to errata.
308	 * Reference: ERR004346: IMX6DQCE Chip Errata for the i.MX 6Dual/6Quad
309	 */
310	bus_space_write_2(bst, bsh, IMX_WDOG_WCR, WCR_WDE);
311
312	for (;;)
313		__asm("wfi");
314}
315
316static const struct fdt_platform imx6_platform = {
317	.fp_devmap = imx_platform_devmap,
318	.fp_bootstrap = imx_platform_bootstrap,
319	.fp_init_attach_args = imx_platform_init_attach_args,
320	.fp_device_register = imx_platform_device_register,
321	.fp_reset = imx6_platform_reset,
322	.fp_delay = a9ptmr_delay,
323	.fp_uart_freq = imx_platform_uart_freq,
324	.fp_mpstart = imx_platform_mpstart,
325};
326
327static const struct fdt_platform imx6sx_platform = {
328	.fp_devmap = imx6sx_platform_devmap,
329	.fp_bootstrap = imx6sx_platform_bootstrap,
330	.fp_init_attach_args = imx_platform_init_attach_args,
331	.fp_device_register = imx_platform_device_register,
332	.fp_reset = imx6_platform_reset,
333	.fp_delay = a9ptmr_delay,
334	.fp_uart_freq = imx6sx_platform_uart_freq,
335	.fp_mpstart = imx_platform_mpstart,
336};
337
338FDT_PLATFORM(imx6dl, "fsl,imx6dl", &imx6_platform);
339FDT_PLATFORM(imx6sx, "fsl,imx6sx", &imx6sx_platform);
340FDT_PLATFORM(imx6q, "fsl,imx6q", &imx6_platform);
341FDT_PLATFORM(imx6qp, "fsl,imx6qp", &imx6_platform);
342