1 /* $NetBSD: sunxi_platform.c,v 1.43 2021/04/24 23:36:28 thorpej Exp $ */ 2 3 /*- 4 * Copyright (c) 2017 Jared McNeill <jmcneill@invisible.ca> 5 * All rights reserved. 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 "opt_soc.h" 30 #include "opt_multiprocessor.h" 31 #include "opt_console.h" 32 33 #include <sys/cdefs.h> 34 __KERNEL_RCSID(0, "$NetBSD: sunxi_platform.c,v 1.43 2021/04/24 23:36:28 thorpej Exp $"); 35 36 #include <sys/param.h> 37 #include <sys/bus.h> 38 #include <sys/cpu.h> 39 #include <sys/device.h> 40 #include <sys/termios.h> 41 42 #include <dev/fdt/fdtvar.h> 43 #include <arm/fdt/arm_fdtvar.h> 44 45 #include <uvm/uvm_extern.h> 46 47 #include <machine/bootconfig.h> 48 #include <arm/cpufunc.h> 49 50 #include <arm/cortex/gtmr_var.h> 51 #include <arm/cortex/gic_reg.h> 52 53 #include <dev/ic/ns16550reg.h> 54 #include <dev/ic/comreg.h> 55 56 #include <arm/arm/psci.h> 57 #include <arm/fdt/psci_fdtvar.h> 58 59 #include <arm/sunxi/sunxi_platform.h> 60 61 #if defined(SOC_SUNXI_MC) 62 #include <arm/sunxi/sunxi_mc_smp.h> 63 #endif 64 65 #include <libfdt.h> 66 67 #define SUNXI_REF_FREQ 24000000 68 69 #define SUN4I_TIMER_BASE 0x01c20c00 70 #define SUN4I_TIMER_SIZE 0x90 71 #define SUN4I_TIMER_1_CTRL 0x20 72 #define SUN4I_TIMER_1_CTRL_CLK_SRC __BITS(3,2) 73 #define SUN4I_TIMER_1_CTRL_CLK_SRC_OSC24M 1 74 #define SUN4I_TIMER_1_CTRL_RELOAD __BIT(1) 75 #define SUN4I_TIMER_1_CTRL_EN __BIT(0) 76 #define SUN4I_TIMER_1_INTV_VALUE 0x24 77 #define SUN4I_TIMER_1_VAL 0x28 78 79 #define SUN4I_WDT_BASE 0x01c20c90 80 #define SUN4I_WDT_SIZE 0x10 81 #define SUN4I_WDT_CTRL 0x00 82 #define SUN4I_WDT_CTRL_KEY (0x333 << 1) 83 #define SUN4I_WDT_CTRL_RESTART __BIT(0) 84 #define SUN4I_WDT_MODE 0x04 85 #define SUN4I_WDT_MODE_RST_EN __BIT(1) 86 #define SUN4I_WDT_MODE_EN __BIT(0) 87 88 #define SUN6I_WDT_BASE 0x01c20ca0 89 #define SUN6I_WDT_SIZE 0x20 90 #define SUN6I_WDT_CFG 0x14 91 #define SUN6I_WDT_CFG_SYS __BIT(0) 92 #define SUN6I_WDT_MODE 0x18 93 #define SUN6I_WDT_MODE_EN __BIT(0) 94 95 #define SUN9I_WDT_BASE 0x06000ca0 96 #define SUN9I_WDT_SIZE 0x20 97 #define SUN9I_WDT_CFG 0x14 98 #define SUN9I_WDT_CFG_SYS __BIT(0) 99 #define SUN9I_WDT_MODE 0x18 100 #define SUN9I_WDT_MODE_EN __BIT(0) 101 102 #define SUN50I_H6_WDT_BASE 0x01c20ca0 103 #define SUN50I_H6_WDT_SIZE 0x20 104 #define SUN50I_H6_WDT_CFG 0x14 105 #define SUN50I_H6_WDT_CFG_SYS __BIT(0) 106 #define SUN50I_H6_WDT_MODE 0x18 107 #define SUN50I_H6_WDT_MODE_EN __BIT(0) 108 109 extern struct arm32_bus_dma_tag arm_generic_dma_tag; 110 extern struct bus_space arm_generic_bs_tag; 111 112 #define sunxi_dma_tag arm_generic_dma_tag 113 #define sunxi_bs_tag arm_generic_bs_tag 114 115 static bus_space_handle_t reset_bsh; 116 117 static const struct pmap_devmap * 118 sunxi_platform_devmap(void) 119 { 120 static const struct pmap_devmap devmap[] = { 121 DEVMAP_ENTRY(SUNXI_CORE_VBASE, 122 SUNXI_CORE_PBASE, 123 SUNXI_CORE_SIZE), 124 DEVMAP_ENTRY_END 125 }; 126 127 return devmap; 128 } 129 130 #define SUNXI_MC_CPU_VBASE (SUNXI_CORE_VBASE + SUNXI_CORE_SIZE) 131 #define SUNXI_MC_CPU_PBASE 0x01700000 132 #define SUNXI_MC_CPU_SIZE 0x00100000 133 134 static const struct pmap_devmap * 135 sun8i_a83t_platform_devmap(void) 136 { 137 static const struct pmap_devmap devmap[] = { 138 DEVMAP_ENTRY(SUNXI_CORE_VBASE, 139 SUNXI_CORE_PBASE, 140 SUNXI_CORE_SIZE), 141 DEVMAP_ENTRY(SUNXI_MC_CPU_VBASE, 142 SUNXI_MC_CPU_PBASE, 143 SUNXI_MC_CPU_SIZE), 144 DEVMAP_ENTRY_END 145 }; 146 147 return devmap; 148 } 149 150 #define SUN9I_A80_PRCM_VBASE (SUNXI_MC_CPU_VBASE + SUNXI_MC_CPU_PBASE) 151 #define SUN9I_A80_PRCM_PBASE 0x08000000 152 #define SUN9I_A80_PRCM_SIZE 0x00100000 153 154 static const struct pmap_devmap * 155 sun9i_a80_platform_devmap(void) 156 { 157 static const struct pmap_devmap devmap[] = { 158 DEVMAP_ENTRY(SUNXI_CORE_VBASE, 159 SUNXI_CORE_PBASE, 160 SUNXI_CORE_SIZE), 161 DEVMAP_ENTRY(SUNXI_MC_CPU_VBASE, 162 SUNXI_MC_CPU_PBASE, 163 SUNXI_MC_CPU_SIZE), 164 DEVMAP_ENTRY(SUN9I_A80_PRCM_VBASE, 165 SUN9I_A80_PRCM_PBASE, 166 SUN9I_A80_PRCM_SIZE), 167 DEVMAP_ENTRY_END 168 }; 169 170 return devmap; 171 } 172 173 174 static void 175 sunxi_platform_init_attach_args(struct fdt_attach_args *faa) 176 { 177 faa->faa_bst = &sunxi_bs_tag; 178 faa->faa_dmat = &sunxi_dma_tag; 179 } 180 181 void sunxi_platform_early_putchar(char); 182 183 void __noasan 184 sunxi_platform_early_putchar(char c) 185 { 186 #ifdef CONSADDR 187 #define CONSADDR_VA ((CONSADDR - SUNXI_CORE_PBASE) + SUNXI_CORE_VBASE) 188 volatile uint32_t *uartaddr = cpu_earlydevice_va_p() ? 189 (volatile uint32_t *)CONSADDR_VA : 190 (volatile uint32_t *)CONSADDR; 191 192 while ((le32toh(uartaddr[com_lsr]) & LSR_TXRDY) == 0) 193 ; 194 195 uartaddr[com_data] = htole32(c); 196 #endif 197 } 198 199 static void 200 sunxi_platform_device_register(device_t self, void *aux) 201 { 202 prop_dictionary_t prop = device_properties(self); 203 int val; 204 205 if (device_is_a(self, "rgephy")) { 206 /* Pine64+ and NanoPi NEO Plus2 gigabit ethernet workaround */ 207 static const struct device_compatible_entry compat_data[] = { 208 { .compat = "pine64,pine64-plus" }, 209 { .compat = "friendlyarm,nanopi-neo-plus2" }, 210 DEVICE_COMPAT_EOL 211 }; 212 if (of_compatible_match(OF_finddevice("/"), compat_data)) { 213 prop_dictionary_set_bool(prop, "no-rx-delay", true); 214 } 215 } 216 217 if (device_is_a(self, "armgtmr")) { 218 /* Allwinner A64 has an unstable architectural timer */ 219 static const struct device_compatible_entry compat_data[] = { 220 { .compat = "allwinner,sun50i-a64" }, 221 /* Cubietruck Plus triggers this problem as well. */ 222 { .compat = "allwinner,sun8i-a83t" }, 223 DEVICE_COMPAT_EOL 224 }; 225 if (of_compatible_match(OF_finddevice("/"), compat_data)) { 226 prop_dictionary_set_bool(prop, "sun50i-a64-unstable-timer", true); 227 } 228 } 229 230 if (device_is_a(self, "sunxidrm") || device_is_a(self, "dwhdmi")) { 231 if (get_bootconf_option(boot_args, "nomodeset", BOOTOPT_TYPE_BOOLEAN, &val)) 232 if (val) 233 prop_dictionary_set_bool(prop, "disabled", true); 234 } 235 236 if (device_is_a(self, "sun50ia64ccu0")) { 237 if (get_bootconf_option(boot_args, "nomodeset", BOOTOPT_TYPE_BOOLEAN, &val)) 238 if (val) 239 prop_dictionary_set_bool(prop, "nomodeset", true); 240 } 241 } 242 243 static u_int 244 sunxi_platform_uart_freq(void) 245 { 246 return SUNXI_REF_FREQ; 247 } 248 249 static void 250 sunxi_platform_bootstrap(void) 251 { 252 arm_fdt_cpu_bootstrap(); 253 254 void *fdt_data = __UNCONST(fdtbus_get_data()); 255 const int chosen_off = fdt_path_offset(fdt_data, "/chosen"); 256 if (chosen_off < 0) 257 return; 258 259 if (match_bootconf_option(boot_args, "console", "fb")) { 260 const int framebuffer_off = 261 fdt_path_offset(fdt_data, "/chosen/framebuffer"); 262 if (framebuffer_off >= 0) { 263 const char *status = fdt_getprop(fdt_data, 264 framebuffer_off, "status", NULL); 265 if (status == NULL || strncmp(status, "ok", 2) == 0) { 266 fdt_setprop_string(fdt_data, chosen_off, 267 "stdout-path", "/chosen/framebuffer"); 268 } 269 } 270 } else if (match_bootconf_option(boot_args, "console", "serial")) { 271 fdt_setprop_string(fdt_data, chosen_off, 272 "stdout-path", "serial0:115200n8"); 273 } 274 } 275 276 static void 277 sun4i_platform_bootstrap(void) 278 { 279 bus_space_tag_t bst = &sunxi_bs_tag; 280 281 sunxi_platform_bootstrap(); 282 bus_space_map(bst, SUN4I_WDT_BASE, SUN4I_WDT_SIZE, 0, &reset_bsh); 283 } 284 285 static void 286 sun6i_platform_bootstrap(void) 287 { 288 bus_space_tag_t bst = &sunxi_bs_tag; 289 290 sunxi_platform_bootstrap(); 291 bus_space_map(bst, SUN6I_WDT_BASE, SUN6I_WDT_SIZE, 0, &reset_bsh); 292 } 293 294 static void 295 sun9i_platform_bootstrap(void) 296 { 297 bus_space_tag_t bst = &sunxi_bs_tag; 298 299 sunxi_platform_bootstrap(); 300 bus_space_map(bst, SUN9I_WDT_BASE, SUN9I_WDT_SIZE, 0, &reset_bsh); 301 } 302 303 static void 304 sun50i_h6_platform_bootstrap(void) 305 { 306 bus_space_tag_t bst = &sunxi_bs_tag; 307 308 sunxi_platform_bootstrap(); 309 bus_space_map(bst, SUN50I_H6_WDT_BASE, SUN50I_H6_WDT_SIZE, 0, &reset_bsh); 310 } 311 312 #if defined(SOC_SUNXI_MC) 313 static int 314 cpu_enable_sun8i_a83t(int phandle) 315 { 316 uint64_t mpidr; 317 318 fdtbus_get_reg64(phandle, 0, &mpidr, NULL); 319 320 return sun8i_a83t_smp_enable(mpidr); 321 } 322 ARM_CPU_METHOD(sun8i_a83t, "allwinner,sun8i-a83t-smp", cpu_enable_sun8i_a83t); 323 324 static int 325 cpu_enable_sun9i_a80(int phandle) 326 { 327 uint64_t mpidr; 328 329 fdtbus_get_reg64(phandle, 0, &mpidr, NULL); 330 331 return sun9i_a80_smp_enable(mpidr); 332 } 333 ARM_CPU_METHOD(sun9i_a80, "allwinner,sun9i-a80-smp", cpu_enable_sun9i_a80); 334 #endif 335 336 static void 337 sun4i_platform_reset(void) 338 { 339 bus_space_tag_t bst = &sunxi_bs_tag; 340 341 bus_space_write_4(bst, reset_bsh, SUN4I_WDT_CTRL, 342 SUN4I_WDT_CTRL_KEY | SUN4I_WDT_CTRL_RESTART); 343 for (;;) { 344 bus_space_write_4(bst, reset_bsh, SUN4I_WDT_MODE, 345 SUN4I_WDT_MODE_EN | SUN4I_WDT_MODE_RST_EN); 346 } 347 } 348 349 static void 350 sun4i_platform_delay(u_int n) 351 { 352 static bus_space_tag_t bst = &sunxi_bs_tag; 353 static bus_space_handle_t bsh = 0; 354 const long incs_per_us = SUNXI_REF_FREQ / 1000000; 355 long ticks = n * incs_per_us; 356 uint32_t cur, prev; 357 358 if (bsh == 0) { 359 bus_space_map(bst, SUN4I_TIMER_BASE, SUN4I_TIMER_SIZE, 0, &bsh); 360 361 /* Enable Timer 1 */ 362 bus_space_write_4(bst, bsh, SUN4I_TIMER_1_INTV_VALUE, ~0U); 363 bus_space_write_4(bst, bsh, SUN4I_TIMER_1_CTRL, 364 SUN4I_TIMER_1_CTRL_EN | 365 SUN4I_TIMER_1_CTRL_RELOAD | 366 __SHIFTIN(SUN4I_TIMER_1_CTRL_CLK_SRC_OSC24M, 367 SUN4I_TIMER_1_CTRL_CLK_SRC)); 368 } 369 370 prev = ~bus_space_read_4(bst, bsh, SUN4I_TIMER_1_VAL); 371 while (ticks > 0) { 372 cur = ~bus_space_read_4(bst, bsh, SUN4I_TIMER_1_VAL); 373 if (cur > prev) 374 ticks -= (cur - prev); 375 else 376 ticks -= (~0U - cur + prev); 377 prev = cur; 378 } 379 } 380 381 static void 382 sun6i_platform_reset(void) 383 { 384 bus_space_tag_t bst = &sunxi_bs_tag; 385 386 bus_space_write_4(bst, reset_bsh, SUN6I_WDT_CFG, SUN6I_WDT_CFG_SYS); 387 bus_space_write_4(bst, reset_bsh, SUN6I_WDT_MODE, SUN6I_WDT_MODE_EN); 388 } 389 390 static void 391 sun9i_platform_reset(void) 392 { 393 bus_space_tag_t bst = &sunxi_bs_tag; 394 395 bus_space_write_4(bst, reset_bsh, SUN9I_WDT_CFG, SUN9I_WDT_CFG_SYS); 396 bus_space_write_4(bst, reset_bsh, SUN9I_WDT_MODE, SUN9I_WDT_MODE_EN); 397 } 398 399 static void 400 sun50i_h6_platform_reset(void) 401 { 402 bus_space_tag_t bst = &sunxi_bs_tag; 403 404 bus_space_write_4(bst, reset_bsh, SUN50I_H6_WDT_CFG, SUN50I_H6_WDT_CFG_SYS); 405 bus_space_write_4(bst, reset_bsh, SUN50I_H6_WDT_MODE, SUN50I_H6_WDT_MODE_EN); 406 } 407 408 static const struct arm_platform sun4i_platform = { 409 .ap_devmap = sunxi_platform_devmap, 410 .ap_bootstrap = sun4i_platform_bootstrap, 411 .ap_init_attach_args = sunxi_platform_init_attach_args, 412 .ap_device_register = sunxi_platform_device_register, 413 .ap_reset = sun4i_platform_reset, 414 .ap_delay = sun4i_platform_delay, 415 .ap_uart_freq = sunxi_platform_uart_freq, 416 }; 417 418 ARM_PLATFORM(sun4i_a10, "allwinner,sun4i-a10", &sun4i_platform); 419 420 static const struct arm_platform sun5i_platform = { 421 .ap_devmap = sunxi_platform_devmap, 422 .ap_bootstrap = sun4i_platform_bootstrap, 423 .ap_init_attach_args = sunxi_platform_init_attach_args, 424 .ap_device_register = sunxi_platform_device_register, 425 .ap_reset = sun4i_platform_reset, 426 .ap_delay = sun4i_platform_delay, 427 .ap_uart_freq = sunxi_platform_uart_freq, 428 }; 429 430 ARM_PLATFORM(sun5i_a13, "allwinner,sun5i-a13", &sun5i_platform); 431 ARM_PLATFORM(sun5i_gr8, "nextthing,gr8", &sun5i_platform); 432 433 static const struct arm_platform sun6i_platform = { 434 .ap_devmap = sunxi_platform_devmap, 435 .ap_bootstrap = sun6i_platform_bootstrap, 436 .ap_init_attach_args = sunxi_platform_init_attach_args, 437 .ap_device_register = sunxi_platform_device_register, 438 .ap_reset = sun6i_platform_reset, 439 .ap_delay = gtmr_delay, 440 .ap_uart_freq = sunxi_platform_uart_freq, 441 .ap_mpstart = arm_fdt_cpu_mpstart, 442 }; 443 444 ARM_PLATFORM(sun6i_a31, "allwinner,sun6i-a31", &sun6i_platform); 445 446 static const struct arm_platform sun7i_platform = { 447 .ap_devmap = sunxi_platform_devmap, 448 .ap_bootstrap = sun4i_platform_bootstrap, 449 .ap_init_attach_args = sunxi_platform_init_attach_args, 450 .ap_device_register = sunxi_platform_device_register, 451 .ap_reset = sun4i_platform_reset, 452 .ap_delay = sun4i_platform_delay, 453 .ap_uart_freq = sunxi_platform_uart_freq, 454 .ap_mpstart = arm_fdt_cpu_mpstart, 455 }; 456 457 ARM_PLATFORM(sun7i_a20, "allwinner,sun7i-a20", &sun7i_platform); 458 459 static const struct arm_platform sun8i_platform = { 460 .ap_devmap = sunxi_platform_devmap, 461 .ap_bootstrap = sun6i_platform_bootstrap, 462 .ap_init_attach_args = sunxi_platform_init_attach_args, 463 .ap_device_register = sunxi_platform_device_register, 464 .ap_reset = sun6i_platform_reset, 465 .ap_delay = gtmr_delay, 466 .ap_uart_freq = sunxi_platform_uart_freq, 467 .ap_mpstart = arm_fdt_cpu_mpstart, 468 }; 469 470 ARM_PLATFORM(sun8i_h2plus, "allwinner,sun8i-h2-plus", &sun8i_platform); 471 ARM_PLATFORM(sun8i_h3, "allwinner,sun8i-h3", &sun8i_platform); 472 473 static const struct arm_platform sun8i_a83t_platform = { 474 .ap_devmap = sun8i_a83t_platform_devmap, 475 .ap_bootstrap = sun6i_platform_bootstrap, 476 .ap_init_attach_args = sunxi_platform_init_attach_args, 477 .ap_device_register = sunxi_platform_device_register, 478 .ap_reset = sun6i_platform_reset, 479 .ap_delay = gtmr_delay, 480 .ap_uart_freq = sunxi_platform_uart_freq, 481 .ap_mpstart = arm_fdt_cpu_mpstart, 482 }; 483 484 ARM_PLATFORM(sun8i_a83t, "allwinner,sun8i-a83t", &sun8i_a83t_platform); 485 486 static const struct arm_platform sun9i_platform = { 487 .ap_devmap = sun9i_a80_platform_devmap, 488 .ap_bootstrap = sun9i_platform_bootstrap, 489 .ap_init_attach_args = sunxi_platform_init_attach_args, 490 .ap_device_register = sunxi_platform_device_register, 491 .ap_reset = sun9i_platform_reset, 492 .ap_delay = gtmr_delay, 493 .ap_uart_freq = sunxi_platform_uart_freq, 494 .ap_mpstart = arm_fdt_cpu_mpstart, 495 }; 496 497 ARM_PLATFORM(sun9i_a80, "allwinner,sun9i-a80", &sun9i_platform); 498 499 static const struct arm_platform sun50i_platform = { 500 .ap_devmap = sunxi_platform_devmap, 501 .ap_bootstrap = sun6i_platform_bootstrap, 502 .ap_init_attach_args = sunxi_platform_init_attach_args, 503 .ap_device_register = sunxi_platform_device_register, 504 .ap_reset = sun6i_platform_reset, 505 .ap_delay = gtmr_delay, 506 .ap_uart_freq = sunxi_platform_uart_freq, 507 .ap_mpstart = arm_fdt_cpu_mpstart, 508 }; 509 510 ARM_PLATFORM(sun50i_a64, "allwinner,sun50i-a64", &sun50i_platform); 511 ARM_PLATFORM(sun50i_h5, "allwinner,sun50i-h5", &sun50i_platform); 512 513 static const struct arm_platform sun50i_h6_platform = { 514 .ap_devmap = sunxi_platform_devmap, 515 .ap_bootstrap = sun50i_h6_platform_bootstrap, 516 .ap_init_attach_args = sunxi_platform_init_attach_args, 517 .ap_device_register = sunxi_platform_device_register, 518 .ap_reset = sun50i_h6_platform_reset, 519 .ap_delay = gtmr_delay, 520 .ap_uart_freq = sunxi_platform_uart_freq, 521 .ap_mpstart = arm_fdt_cpu_mpstart, 522 }; 523 524 ARM_PLATFORM(sun50i_h6, "allwinner,sun50i-h6", &sun50i_h6_platform); 525