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