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 #define UART_LSR_OFFSET 5 17 #define UART_LSR_THRE 0x20 18 19 /* 20 * Use this guess for the uart base in order to make an attempt at 21 * having earlier printf support. We'll overwrite it with the real 22 * base address that we read from the device tree later. This is 23 * the address we expect the virtual machine manager to put in 24 * its generated device tree. 25 */ 26 #define UART_EARLY_BASE ((u8 *)(unsigned long)CONFIG_UART_EARLY_BASE) 27 static volatile u8 *uart0_base = UART_EARLY_BASE; 28 static u32 uart0_reg_width = 1; 29 static u32 uart0_reg_shift; 30 static struct spinlock uart_lock; 31 32 static u32 uart0_read(u32 num) 33 { 34 u32 offset = num << uart0_reg_shift; 35 36 if (uart0_reg_width == 1) 37 return readb(uart0_base + offset); 38 else if (uart0_reg_width == 2) 39 return readw(uart0_base + offset); 40 else 41 return readl(uart0_base + offset); 42 } 43 44 static void uart0_write(u32 num, u32 val) 45 { 46 u32 offset = num << uart0_reg_shift; 47 48 if (uart0_reg_width == 1) 49 writeb(val, uart0_base + offset); 50 else if (uart0_reg_width == 2) 51 writew(val, uart0_base + offset); 52 else 53 writel(val, uart0_base + offset); 54 } 55 56 static void uart0_init_fdt(void) 57 { 58 const char *compatible[] = {"ns16550a"}; 59 struct dt_pbus_reg base; 60 int i, ret; 61 62 ret = dt_get_default_console_node(); 63 assert(ret >= 0 || ret == -FDT_ERR_NOTFOUND); 64 65 if (ret == -FDT_ERR_NOTFOUND) { 66 for (i = 0; i < ARRAY_SIZE(compatible); i++) { 67 ret = dt_pbus_get_base_compatible(compatible[i], &base); 68 assert(ret == 0 || ret == -FDT_ERR_NOTFOUND); 69 if (ret == 0) 70 break; 71 } 72 73 if (ret) { 74 printf("%s: Compatible uart not found in the device tree, aborting...\n", 75 __func__); 76 abort(); 77 } 78 } else { 79 const fdt32_t *val; 80 int len; 81 82 val = fdt_getprop(dt_fdt(), ret, "reg-shift", &len); 83 if (len == sizeof(*val)) 84 uart0_reg_shift = fdt32_to_cpu(*val); 85 86 val = fdt_getprop(dt_fdt(), ret, "reg-io-width", &len); 87 if (len == sizeof(*val)) 88 uart0_reg_width = fdt32_to_cpu(*val); 89 90 ret = dt_pbus_translate_node(ret, 0, &base); 91 assert(ret == 0); 92 } 93 94 uart0_base = ioremap(base.addr, base.size); 95 } 96 97 static void uart0_init_acpi(void) 98 { 99 assert_msg(false, "ACPI not available"); 100 } 101 102 void io_init(void) 103 { 104 if (dt_available()) 105 uart0_init_fdt(); 106 else 107 uart0_init_acpi(); 108 109 if (uart0_base != UART_EARLY_BASE) { 110 printf("WARNING: early print support may not work. " 111 "Found uart at %p, but early base is %p.\n", 112 uart0_base, UART_EARLY_BASE); 113 } 114 } 115 116 void puts(const char *s) 117 { 118 spin_lock(&uart_lock); 119 while (*s) { 120 while (!(uart0_read(UART_LSR_OFFSET) & UART_LSR_THRE)) 121 ; 122 uart0_write(0, *s++); 123 } 124 spin_unlock(&uart_lock); 125 } 126 127 /* 128 * Defining halt to take 'code' as an argument guarantees that it will 129 * be in a0 when we halt. That gives us a final chance to see the exit 130 * status while inspecting the halted unit test state. 131 */ 132 void halt(int code); 133 134 void exit(int code) 135 { 136 printf("\nEXIT: STATUS=%d\n", ((code) << 1) | 1); 137 sbi_shutdown(); 138 halt(code); 139 __builtin_unreachable(); 140 } 141