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