1 /* $NetBSD: imx6_platform.c,v 1.10 2025/09/06 21:02:40 thorpej 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.10 2025/09/06 21:02:40 thorpej 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 #include <dev/fdt/fdt_platform.h> 46 47 #include <arm/fdt/arm_fdtvar.h> 48 49 #include <uvm/uvm_extern.h> 50 51 #include <arm/arm32/machdep.h> 52 53 #include <machine/bootconfig.h> 54 #include <arm/cpufunc.h> 55 56 #include <arm/cortex/a9tmr_var.h> 57 #include <arm/cortex/scu_reg.h> 58 #include <arm/cortex/gic_reg.h> 59 #include <arm/cortex/pl310_var.h> 60 61 #include <arm/nxp/imx6_reg.h> 62 #include <arm/nxp/imx6_srcreg.h> 63 #include <arm/imx/imxuartreg.h> 64 #include <arm/imx/imxwdogreg.h> 65 66 #include <arm/nxp/imx6_platform.h> 67 68 #include <libfdt.h> 69 70 #define IMX_REF_FREQ 80000000 71 #define IMX6SX_REF_FREQ 24000000 72 73 #ifdef VERBOSE_INIT_ARM 74 #define VPRINTF(...) printf(__VA_ARGS__) 75 #else 76 #define VPRINTF(...) __nothing 77 #endif 78 79 extern struct bus_space armv7_generic_bs_tag; 80 extern struct arm32_bus_dma_tag arm_generic_dma_tag; 81 82 static const struct pmap_devmap * 83 imx_platform_devmap(void) 84 { 85 static const struct pmap_devmap devmap[] = { 86 DEVMAP_ENTRY(KERNEL_IO_IOREG_VBASE, IMX6_IOREG_PBASE, IMX6_IOREG_SIZE), 87 DEVMAP_ENTRY(KERNEL_IO_ARMCORE_VBASE, IMX6_ARMCORE_PBASE, IMX6_ARMCORE_SIZE), 88 DEVMAP_ENTRY_END 89 }; 90 91 return devmap; 92 } 93 94 static const struct pmap_devmap * 95 imx6sx_platform_devmap(void) 96 { 97 static const struct pmap_devmap devmap[] = { 98 DEVMAP_ENTRY(KERNEL_IO_IOREG_VBASE, IMX6_IOREG_PBASE, IMX6SX_IOREG_SIZE), 99 DEVMAP_ENTRY(KERNEL_IO_ARMCORE_VBASE, IMX6_ARMCORE_PBASE, IMX6_ARMCORE_SIZE), 100 DEVMAP_ENTRY_END 101 }; 102 103 return devmap; 104 } 105 106 static void 107 imx_platform_init_attach_args(struct fdt_attach_args *faa) 108 { 109 faa->faa_bst = &armv7_generic_bs_tag; 110 faa->faa_dmat = &arm_generic_dma_tag; 111 } 112 113 void imx_platform_early_putchar(char); 114 115 void __noasan 116 imx_platform_early_putchar(char c) 117 { 118 #ifdef CONSADDR 119 #define CONSADDR_VA ((CONSADDR - IMX6_IOREG_PBASE) + KERNEL_IO_IOREG_VBASE) 120 121 volatile uint32_t *uartaddr = cpu_earlydevice_va_p() ? 122 (volatile uint32_t *)CONSADDR_VA : 123 (volatile uint32_t *)CONSADDR; 124 125 while ((le32toh(uartaddr[(IMX_USR2/4)]) & IMX_USR2_TXDC) == 0) 126 ; 127 128 uartaddr[(IMX_UTXD/4)] = htole32(c); 129 #endif 130 } 131 132 static void 133 imx_platform_device_register(device_t self, void *aux) 134 { 135 prop_dictionary_t prop = device_properties(self); 136 137 if (device_is_a(self, "atphy")) { 138 static const struct device_compatible_entry compat_data[] = { 139 { .compat = "fsl,imx6dl-sabresd" }, 140 { .compat = "fsl,imx6q-sabresd" }, 141 { .compat = "fsl,imx6qp-sabresd" }, 142 { .compat = "solidrun,hummingboard2/q" }, 143 { .compat = "solidrun,hummingboard2/dl" }, 144 DEVICE_COMPAT_EOL 145 }; 146 if (of_compatible_match(OF_finddevice("/"), compat_data)) 147 prop_dictionary_set_uint32(prop, "clk_25m", 125000000); 148 } 149 } 150 151 static u_int 152 imx_platform_uart_freq(void) 153 { 154 return IMX_REF_FREQ; 155 } 156 157 static u_int 158 imx6sx_platform_uart_freq(void) 159 { 160 return IMX6SX_REF_FREQ; 161 } 162 163 164 static void 165 imx_platform_bootstrap(void) 166 { 167 #if NARML2CC > 0 168 bus_space_tag_t bst = &armv7_generic_bs_tag; 169 bus_space_handle_t bsh; 170 if (bus_space_map(bst, IMX6_ARMCORE_PBASE, IMX6_ARMCORE_SIZE, 0, &bsh)) 171 panic("couldn't map armcore registers"); 172 arml2cc_init(bst, bsh, ARMCORE_L2C_BASE); 173 bus_space_unmap(bst, bsh, IMX6_ARMCORE_SIZE); 174 #endif 175 176 arm_fdt_cpu_bootstrap(); 177 } 178 179 static void 180 imx6sx_platform_bootstrap(void) 181 { 182 void *fdt_data; 183 int ofw_root; 184 int soc_node, timer_node, intc_node, clks_node; 185 int ret; 186 fdt32_t intval[3]; 187 fdt32_t clkval[2]; 188 u_int val32; 189 190 imx_platform_bootstrap(); 191 192 /* 193 * if there's no entry for the TWD timer in the provided DTB, fake one. 194 * we can't boot witthout it. 195 * The upstream imx6sx.dtsi is missing the entry 196 */ 197 198 fdt_data = __UNCONST(fdtbus_get_data()); 199 KASSERT(fdt_data != NULL); 200 ofw_root = OF_peer(0); 201 if (of_find_bycompat(ofw_root, "arm,cortex-a9-twd-timer") > 0) { 202 /* already there */ 203 VPRINTF("timer already present\n"); 204 return; 205 } 206 VPRINTF("creating timer fdt@%p", fdt_data); 207 soc_node = fdt_path_offset(fdt_data, "/soc"); 208 VPRINTF(" soc_node %d", soc_node); 209 KASSERT(soc_node >= 0); 210 211 timer_node = fdt_add_subnode(fdt_data, soc_node, "timer@a00600"); 212 VPRINTF(" timer_node %d\n", timer_node); 213 KASSERT(timer_node >= 0); 214 215 ret = fdt_setprop_string(fdt_data, timer_node, "compatible", 216 "arm,cortex-a9-twd-timer"); 217 KASSERTMSG(ret == 0, "fdt_setprop(compatible) returns %d", ret); 218 219 ret = fdt_appendprop_addrrange(fdt_data, soc_node, timer_node, 220 "reg", 0x00a00600, 0x20); 221 KASSERTMSG(ret == 0, "fdt_appendprop_addrrange returns %d", ret); 222 223 intval[0] = cpu_to_fdt32(1); 224 intval[1] = cpu_to_fdt32(13); 225 intval[2] = cpu_to_fdt32(0xf01); 226 ret = fdt_setprop(fdt_data, timer_node, "interrupts", 227 intval, sizeof(intval)); 228 KASSERTMSG(ret == 0, "fdt_setprop(interrupts) returns %d", ret); 229 230 intc_node = of_find_bycompat(ofw_root, "arm,cortex-a9-gic"); 231 KASSERT(intc_node >= 0); 232 val32 = 0; 233 of_getprop_uint32(intc_node, "phandle", &val32); 234 ret = fdt_setprop_u32(fdt_data, timer_node, "interrupt-parent", 235 val32); 236 KASSERTMSG(ret == 0, "fdt_setprop(interrupt-parent) returns %d", ret); 237 238 val32 = 0; 239 clks_node = of_find_bycompat(ofw_root, "fsl,imx6sx-ccm"); 240 KASSERT(clks_node >= 0); 241 of_getprop_uint32(clks_node, "phandle", &val32); 242 clkval[0] = cpu_to_fdt32(val32); 243 clkval[1] = cpu_to_fdt32(30); /* IMX6SXCLK_TWD */ 244 ret = fdt_setprop(fdt_data, timer_node, "clocks", 245 clkval, sizeof(clkval)); 246 KASSERTMSG(ret == 0, "fdt_setprop(clocks) returns %d", ret); 247 } 248 249 static int 250 imx_platform_mpstart(void) 251 { 252 #if defined(MULTIPROCESSOR) 253 bus_space_tag_t bst = &armv7_generic_bs_tag; 254 bus_space_handle_t bsh; 255 256 if (bus_space_map(bst, IMX6_ARMCORE_PBASE, IMX6_ARMCORE_SIZE, 0, &bsh) != 0) 257 panic("couldn't map armcore registers"); 258 259 /* Enable Snoop Control Unit */ 260 bus_space_write_4(bst, bsh, SCU_INV_ALL_REG, 0xff); 261 bus_space_write_4(bst, bsh, SCU_CTL, 262 bus_space_read_4(bst, bsh, SCU_CTL) | SCU_CTL_SCU_ENA); 263 264 bus_space_unmap(bst, bsh, AIPS1_SRC_SIZE); 265 266 if (bus_space_map(bst, IMX6_AIPS1_BASE + AIPS1_SRC_BASE, AIPS1_SRC_SIZE, 0, &bsh) != 0) 267 panic("couldn't map SRC"); 268 269 uint32_t srcctl = bus_space_read_4(bst, bsh, SRC_SCR); 270 const paddr_t mpstart = KERN_VTOPHYS((vaddr_t)cpu_mpstart); 271 272 srcctl &= ~(SRC_SCR_CORE1_ENABLE | SRC_SCR_CORE2_ENABLE | 273 SRC_SCR_CORE3_ENABLE); 274 bus_space_write_4(bst, bsh, SRC_SCR, srcctl); 275 276 for (int i = 1; i < arm_cpu_max; i++) { 277 bus_space_write_4(bst, bsh, SRC_GPRN_ENTRY(i), mpstart); 278 srcctl |= SRC_SCR_COREN_RST(i); 279 srcctl |= SRC_SCR_COREN_ENABLE(i); 280 } 281 bus_space_write_4(bst, bsh, SRC_SCR, srcctl); 282 283 bus_space_unmap(bst, bsh, AIPS1_SRC_SIZE); 284 285 return arm_fdt_cpu_mpstart(); 286 #else 287 return 0; 288 #endif 289 } 290 291 static void 292 imx6_platform_reset(void) 293 { 294 bus_space_tag_t bst = &armv7_generic_bs_tag; 295 bus_space_handle_t bsh; 296 297 if (bus_space_map(bst, IMX6_AIPS1_BASE + AIPS1_WDOG1_BASE, AIPS1_WDOG_SIZE, 0, &bsh)) 298 panic("couldn't map wdog1 registers"); 299 300 delay(1000); /* wait for flushing FIFO of serial console */ 301 302 cpsid(I32_bit|F32_bit); 303 304 /* software reset signal on wdog */ 305 bus_space_write_2(bst, bsh, IMX_WDOG_WCR, WCR_WDE); 306 307 /* 308 * write twice due to errata. 309 * Reference: ERR004346: IMX6DQCE Chip Errata for the i.MX 6Dual/6Quad 310 */ 311 bus_space_write_2(bst, bsh, IMX_WDOG_WCR, WCR_WDE); 312 313 for (;;) 314 __asm("wfi"); 315 } 316 317 static const struct fdt_platform imx6_platform = { 318 .fp_devmap = imx_platform_devmap, 319 .fp_bootstrap = imx_platform_bootstrap, 320 .fp_init_attach_args = imx_platform_init_attach_args, 321 .fp_device_register = imx_platform_device_register, 322 .fp_reset = imx6_platform_reset, 323 .fp_delay = a9ptmr_delay, 324 .fp_uart_freq = imx_platform_uart_freq, 325 .fp_mpstart = imx_platform_mpstart, 326 }; 327 328 static const struct fdt_platform imx6sx_platform = { 329 .fp_devmap = imx6sx_platform_devmap, 330 .fp_bootstrap = imx6sx_platform_bootstrap, 331 .fp_init_attach_args = imx_platform_init_attach_args, 332 .fp_device_register = imx_platform_device_register, 333 .fp_reset = imx6_platform_reset, 334 .fp_delay = a9ptmr_delay, 335 .fp_uart_freq = imx6sx_platform_uart_freq, 336 .fp_mpstart = imx_platform_mpstart, 337 }; 338 339 FDT_PLATFORM(imx6dl, "fsl,imx6dl", &imx6_platform); 340 FDT_PLATFORM(imx6sx, "fsl,imx6sx", &imx6sx_platform); 341 FDT_PLATFORM(imx6q, "fsl,imx6q", &imx6_platform); 342 FDT_PLATFORM(imx6qp, "fsl,imx6qp", &imx6_platform); 343