1 /* $NetBSD: sunxi_platform.c,v 1.42 2021/02/04 22:36:53 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.42 2021/02/04 22:36:53 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 fdtbus_device_register(self, aux); 206 207 if (device_is_a(self, "rgephy")) { 208 /* Pine64+ and NanoPi NEO Plus2 gigabit ethernet workaround */ 209 static const struct device_compatible_entry compat_data[] = { 210 { .compat = "pine64,pine64-plus" }, 211 { .compat = "friendlyarm,nanopi-neo-plus2" }, 212 DEVICE_COMPAT_EOL 213 }; 214 if (of_compatible_match(OF_finddevice("/"), compat_data)) { 215 prop_dictionary_set_bool(prop, "no-rx-delay", true); 216 } 217 } 218 219 if (device_is_a(self, "armgtmr")) { 220 /* Allwinner A64 has an unstable architectural timer */ 221 static const struct device_compatible_entry compat_data[] = { 222 { .compat = "allwinner,sun50i-a64" }, 223 /* Cubietruck Plus triggers this problem as well. */ 224 { .compat = "allwinner,sun8i-a83t" }, 225 DEVICE_COMPAT_EOL 226 }; 227 if (of_compatible_match(OF_finddevice("/"), compat_data)) { 228 prop_dictionary_set_bool(prop, "sun50i-a64-unstable-timer", true); 229 } 230 } 231 232 if (device_is_a(self, "sunxidrm") || device_is_a(self, "dwhdmi")) { 233 if (get_bootconf_option(boot_args, "nomodeset", BOOTOPT_TYPE_BOOLEAN, &val)) 234 if (val) 235 prop_dictionary_set_bool(prop, "disabled", true); 236 } 237 238 if (device_is_a(self, "sun50ia64ccu0")) { 239 if (get_bootconf_option(boot_args, "nomodeset", BOOTOPT_TYPE_BOOLEAN, &val)) 240 if (val) 241 prop_dictionary_set_bool(prop, "nomodeset", true); 242 } 243 } 244 245 static u_int 246 sunxi_platform_uart_freq(void) 247 { 248 return SUNXI_REF_FREQ; 249 } 250 251 static void 252 sunxi_platform_bootstrap(void) 253 { 254 arm_fdt_cpu_bootstrap(); 255 256 void *fdt_data = __UNCONST(fdtbus_get_data()); 257 const int chosen_off = fdt_path_offset(fdt_data, "/chosen"); 258 if (chosen_off < 0) 259 return; 260 261 if (match_bootconf_option(boot_args, "console", "fb")) { 262 const int framebuffer_off = 263 fdt_path_offset(fdt_data, "/chosen/framebuffer"); 264 if (framebuffer_off >= 0) { 265 const char *status = fdt_getprop(fdt_data, 266 framebuffer_off, "status", NULL); 267 if (status == NULL || strncmp(status, "ok", 2) == 0) { 268 fdt_setprop_string(fdt_data, chosen_off, 269 "stdout-path", "/chosen/framebuffer"); 270 } 271 } 272 } else if (match_bootconf_option(boot_args, "console", "serial")) { 273 fdt_setprop_string(fdt_data, chosen_off, 274 "stdout-path", "serial0:115200n8"); 275 } 276 } 277 278 static void 279 sun4i_platform_bootstrap(void) 280 { 281 bus_space_tag_t bst = &sunxi_bs_tag; 282 283 sunxi_platform_bootstrap(); 284 bus_space_map(bst, SUN4I_WDT_BASE, SUN4I_WDT_SIZE, 0, &reset_bsh); 285 } 286 287 static void 288 sun6i_platform_bootstrap(void) 289 { 290 bus_space_tag_t bst = &sunxi_bs_tag; 291 292 sunxi_platform_bootstrap(); 293 bus_space_map(bst, SUN6I_WDT_BASE, SUN6I_WDT_SIZE, 0, &reset_bsh); 294 } 295 296 static void 297 sun9i_platform_bootstrap(void) 298 { 299 bus_space_tag_t bst = &sunxi_bs_tag; 300 301 sunxi_platform_bootstrap(); 302 bus_space_map(bst, SUN9I_WDT_BASE, SUN9I_WDT_SIZE, 0, &reset_bsh); 303 } 304 305 static void 306 sun50i_h6_platform_bootstrap(void) 307 { 308 bus_space_tag_t bst = &sunxi_bs_tag; 309 310 sunxi_platform_bootstrap(); 311 bus_space_map(bst, SUN50I_H6_WDT_BASE, SUN50I_H6_WDT_SIZE, 0, &reset_bsh); 312 } 313 314 #if defined(SOC_SUNXI_MC) 315 static int 316 cpu_enable_sun8i_a83t(int phandle) 317 { 318 uint64_t mpidr; 319 320 fdtbus_get_reg64(phandle, 0, &mpidr, NULL); 321 322 return sun8i_a83t_smp_enable(mpidr); 323 } 324 ARM_CPU_METHOD(sun8i_a83t, "allwinner,sun8i-a83t-smp", cpu_enable_sun8i_a83t); 325 326 static int 327 cpu_enable_sun9i_a80(int phandle) 328 { 329 uint64_t mpidr; 330 331 fdtbus_get_reg64(phandle, 0, &mpidr, NULL); 332 333 return sun9i_a80_smp_enable(mpidr); 334 } 335 ARM_CPU_METHOD(sun9i_a80, "allwinner,sun9i-a80-smp", cpu_enable_sun9i_a80); 336 #endif 337 338 static void 339 sun4i_platform_reset(void) 340 { 341 bus_space_tag_t bst = &sunxi_bs_tag; 342 343 bus_space_write_4(bst, reset_bsh, SUN4I_WDT_CTRL, 344 SUN4I_WDT_CTRL_KEY | SUN4I_WDT_CTRL_RESTART); 345 for (;;) { 346 bus_space_write_4(bst, reset_bsh, SUN4I_WDT_MODE, 347 SUN4I_WDT_MODE_EN | SUN4I_WDT_MODE_RST_EN); 348 } 349 } 350 351 static void 352 sun4i_platform_delay(u_int n) 353 { 354 static bus_space_tag_t bst = &sunxi_bs_tag; 355 static bus_space_handle_t bsh = 0; 356 const long incs_per_us = SUNXI_REF_FREQ / 1000000; 357 long ticks = n * incs_per_us; 358 uint32_t cur, prev; 359 360 if (bsh == 0) { 361 bus_space_map(bst, SUN4I_TIMER_BASE, SUN4I_TIMER_SIZE, 0, &bsh); 362 363 /* Enable Timer 1 */ 364 bus_space_write_4(bst, bsh, SUN4I_TIMER_1_INTV_VALUE, ~0U); 365 bus_space_write_4(bst, bsh, SUN4I_TIMER_1_CTRL, 366 SUN4I_TIMER_1_CTRL_EN | 367 SUN4I_TIMER_1_CTRL_RELOAD | 368 __SHIFTIN(SUN4I_TIMER_1_CTRL_CLK_SRC_OSC24M, 369 SUN4I_TIMER_1_CTRL_CLK_SRC)); 370 } 371 372 prev = ~bus_space_read_4(bst, bsh, SUN4I_TIMER_1_VAL); 373 while (ticks > 0) { 374 cur = ~bus_space_read_4(bst, bsh, SUN4I_TIMER_1_VAL); 375 if (cur > prev) 376 ticks -= (cur - prev); 377 else 378 ticks -= (~0U - cur + prev); 379 prev = cur; 380 } 381 } 382 383 static void 384 sun6i_platform_reset(void) 385 { 386 bus_space_tag_t bst = &sunxi_bs_tag; 387 388 bus_space_write_4(bst, reset_bsh, SUN6I_WDT_CFG, SUN6I_WDT_CFG_SYS); 389 bus_space_write_4(bst, reset_bsh, SUN6I_WDT_MODE, SUN6I_WDT_MODE_EN); 390 } 391 392 static void 393 sun9i_platform_reset(void) 394 { 395 bus_space_tag_t bst = &sunxi_bs_tag; 396 397 bus_space_write_4(bst, reset_bsh, SUN9I_WDT_CFG, SUN9I_WDT_CFG_SYS); 398 bus_space_write_4(bst, reset_bsh, SUN9I_WDT_MODE, SUN9I_WDT_MODE_EN); 399 } 400 401 static void 402 sun50i_h6_platform_reset(void) 403 { 404 bus_space_tag_t bst = &sunxi_bs_tag; 405 406 bus_space_write_4(bst, reset_bsh, SUN50I_H6_WDT_CFG, SUN50I_H6_WDT_CFG_SYS); 407 bus_space_write_4(bst, reset_bsh, SUN50I_H6_WDT_MODE, SUN50I_H6_WDT_MODE_EN); 408 } 409 410 static const struct arm_platform sun4i_platform = { 411 .ap_devmap = sunxi_platform_devmap, 412 .ap_bootstrap = sun4i_platform_bootstrap, 413 .ap_init_attach_args = sunxi_platform_init_attach_args, 414 .ap_device_register = sunxi_platform_device_register, 415 .ap_reset = sun4i_platform_reset, 416 .ap_delay = sun4i_platform_delay, 417 .ap_uart_freq = sunxi_platform_uart_freq, 418 }; 419 420 ARM_PLATFORM(sun4i_a10, "allwinner,sun4i-a10", &sun4i_platform); 421 422 static const struct arm_platform sun5i_platform = { 423 .ap_devmap = sunxi_platform_devmap, 424 .ap_bootstrap = sun4i_platform_bootstrap, 425 .ap_init_attach_args = sunxi_platform_init_attach_args, 426 .ap_device_register = sunxi_platform_device_register, 427 .ap_reset = sun4i_platform_reset, 428 .ap_delay = sun4i_platform_delay, 429 .ap_uart_freq = sunxi_platform_uart_freq, 430 }; 431 432 ARM_PLATFORM(sun5i_a13, "allwinner,sun5i-a13", &sun5i_platform); 433 ARM_PLATFORM(sun5i_gr8, "nextthing,gr8", &sun5i_platform); 434 435 static const struct arm_platform sun6i_platform = { 436 .ap_devmap = sunxi_platform_devmap, 437 .ap_bootstrap = sun6i_platform_bootstrap, 438 .ap_init_attach_args = sunxi_platform_init_attach_args, 439 .ap_device_register = sunxi_platform_device_register, 440 .ap_reset = sun6i_platform_reset, 441 .ap_delay = gtmr_delay, 442 .ap_uart_freq = sunxi_platform_uart_freq, 443 .ap_mpstart = arm_fdt_cpu_mpstart, 444 }; 445 446 ARM_PLATFORM(sun6i_a31, "allwinner,sun6i-a31", &sun6i_platform); 447 448 static const struct arm_platform sun7i_platform = { 449 .ap_devmap = sunxi_platform_devmap, 450 .ap_bootstrap = sun4i_platform_bootstrap, 451 .ap_init_attach_args = sunxi_platform_init_attach_args, 452 .ap_device_register = sunxi_platform_device_register, 453 .ap_reset = sun4i_platform_reset, 454 .ap_delay = sun4i_platform_delay, 455 .ap_uart_freq = sunxi_platform_uart_freq, 456 .ap_mpstart = arm_fdt_cpu_mpstart, 457 }; 458 459 ARM_PLATFORM(sun7i_a20, "allwinner,sun7i-a20", &sun7i_platform); 460 461 static const struct arm_platform sun8i_platform = { 462 .ap_devmap = sunxi_platform_devmap, 463 .ap_bootstrap = sun6i_platform_bootstrap, 464 .ap_init_attach_args = sunxi_platform_init_attach_args, 465 .ap_device_register = sunxi_platform_device_register, 466 .ap_reset = sun6i_platform_reset, 467 .ap_delay = gtmr_delay, 468 .ap_uart_freq = sunxi_platform_uart_freq, 469 .ap_mpstart = arm_fdt_cpu_mpstart, 470 }; 471 472 ARM_PLATFORM(sun8i_h2plus, "allwinner,sun8i-h2-plus", &sun8i_platform); 473 ARM_PLATFORM(sun8i_h3, "allwinner,sun8i-h3", &sun8i_platform); 474 475 static const struct arm_platform sun8i_a83t_platform = { 476 .ap_devmap = sun8i_a83t_platform_devmap, 477 .ap_bootstrap = sun6i_platform_bootstrap, 478 .ap_init_attach_args = sunxi_platform_init_attach_args, 479 .ap_device_register = sunxi_platform_device_register, 480 .ap_reset = sun6i_platform_reset, 481 .ap_delay = gtmr_delay, 482 .ap_uart_freq = sunxi_platform_uart_freq, 483 .ap_mpstart = arm_fdt_cpu_mpstart, 484 }; 485 486 ARM_PLATFORM(sun8i_a83t, "allwinner,sun8i-a83t", &sun8i_a83t_platform); 487 488 static const struct arm_platform sun9i_platform = { 489 .ap_devmap = sun9i_a80_platform_devmap, 490 .ap_bootstrap = sun9i_platform_bootstrap, 491 .ap_init_attach_args = sunxi_platform_init_attach_args, 492 .ap_device_register = sunxi_platform_device_register, 493 .ap_reset = sun9i_platform_reset, 494 .ap_delay = gtmr_delay, 495 .ap_uart_freq = sunxi_platform_uart_freq, 496 .ap_mpstart = arm_fdt_cpu_mpstart, 497 }; 498 499 ARM_PLATFORM(sun9i_a80, "allwinner,sun9i-a80", &sun9i_platform); 500 501 static const struct arm_platform sun50i_platform = { 502 .ap_devmap = sunxi_platform_devmap, 503 .ap_bootstrap = sun6i_platform_bootstrap, 504 .ap_init_attach_args = sunxi_platform_init_attach_args, 505 .ap_device_register = sunxi_platform_device_register, 506 .ap_reset = sun6i_platform_reset, 507 .ap_delay = gtmr_delay, 508 .ap_uart_freq = sunxi_platform_uart_freq, 509 .ap_mpstart = arm_fdt_cpu_mpstart, 510 }; 511 512 ARM_PLATFORM(sun50i_a64, "allwinner,sun50i-a64", &sun50i_platform); 513 ARM_PLATFORM(sun50i_h5, "allwinner,sun50i-h5", &sun50i_platform); 514 515 static const struct arm_platform sun50i_h6_platform = { 516 .ap_devmap = sunxi_platform_devmap, 517 .ap_bootstrap = sun50i_h6_platform_bootstrap, 518 .ap_init_attach_args = sunxi_platform_init_attach_args, 519 .ap_device_register = sunxi_platform_device_register, 520 .ap_reset = sun50i_h6_platform_reset, 521 .ap_delay = gtmr_delay, 522 .ap_uart_freq = sunxi_platform_uart_freq, 523 .ap_mpstart = arm_fdt_cpu_mpstart, 524 }; 525 526 ARM_PLATFORM(sun50i_h6, "allwinner,sun50i-h6", &sun50i_h6_platform); 527