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