1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Each architecture must implement puts() and exit() with the I/O 4 * devices exposed from QEMU, e.g. ns16550a. 5 * 6 * Copyright (C) 2023, Ventana Micro Systems Inc., Andrew Jones <ajones@ventanamicro.com> 7 */ 8 #include <libcflat.h> 9 #include <config.h> 10 #include <devicetree.h> 11 #include <asm/io.h> 12 #include <asm/sbi.h> 13 #include <asm/setup.h> 14 #include <asm/spinlock.h> 15 16 /* 17 * Use this guess for the uart base in order to make an attempt at 18 * having earlier printf support. We'll overwrite it with the real 19 * base address that we read from the device tree later. This is 20 * the address we expect the virtual machine manager to put in 21 * its generated device tree. 22 */ 23 #define UART_EARLY_BASE ((u8 *)(unsigned long)CONFIG_UART_EARLY_BASE) 24 static volatile u8 *uart0_base = UART_EARLY_BASE; 25 static struct spinlock uart_lock; 26 27 static void uart0_init_fdt(void) 28 { 29 const char *compatible[] = {"ns16550a"}; 30 struct dt_pbus_reg base; 31 int i, ret; 32 33 ret = dt_get_default_console_node(); 34 assert(ret >= 0 || ret == -FDT_ERR_NOTFOUND); 35 36 if (ret == -FDT_ERR_NOTFOUND) { 37 for (i = 0; i < ARRAY_SIZE(compatible); i++) { 38 ret = dt_pbus_get_base_compatible(compatible[i], &base); 39 assert(ret == 0 || ret == -FDT_ERR_NOTFOUND); 40 if (ret == 0) 41 break; 42 } 43 44 if (ret) { 45 printf("%s: Compatible uart not found in the device tree, aborting...\n", 46 __func__); 47 abort(); 48 } 49 } else { 50 ret = dt_pbus_translate_node(ret, 0, &base); 51 assert(ret == 0); 52 } 53 54 uart0_base = ioremap(base.addr, base.size); 55 } 56 57 static void uart0_init_acpi(void) 58 { 59 assert_msg(false, "ACPI not available"); 60 } 61 62 void io_init(void) 63 { 64 if (dt_available()) 65 uart0_init_fdt(); 66 else 67 uart0_init_acpi(); 68 69 if (uart0_base != UART_EARLY_BASE) { 70 printf("WARNING: early print support may not work. " 71 "Found uart at %p, but early base is %p.\n", 72 uart0_base, UART_EARLY_BASE); 73 } 74 } 75 76 void puts(const char *s) 77 { 78 spin_lock(&uart_lock); 79 while (*s) 80 writeb(*s++, uart0_base); 81 spin_unlock(&uart_lock); 82 } 83 84 /* 85 * Defining halt to take 'code' as an argument guarantees that it will 86 * be in a0 when we halt. That gives us a final chance to see the exit 87 * status while inspecting the halted unit test state. 88 */ 89 void halt(int code); 90 91 void exit(int code) 92 { 93 printf("\nEXIT: STATUS=%d\n", ((code) << 1) | 1); 94 sbi_shutdown(); 95 halt(code); 96 __builtin_unreachable(); 97 } 98