1bd744d46SAndrew Jones // SPDX-License-Identifier: GPL-2.0-only 2bd744d46SAndrew Jones /* 3bd744d46SAndrew Jones * Each architecture must implement puts() and exit() with the I/O 4bd744d46SAndrew Jones * devices exposed from QEMU, e.g. ns16550a. 5bd744d46SAndrew Jones * 6bd744d46SAndrew Jones * Copyright (C) 2023, Ventana Micro Systems Inc., Andrew Jones <ajones@ventanamicro.com> 7bd744d46SAndrew Jones */ 8bd744d46SAndrew Jones #include <libcflat.h> 9bd744d46SAndrew Jones #include <config.h> 1022f287f4SAndrew Jones #include <devicetree.h> 11bd744d46SAndrew Jones #include <asm/io.h> 129ccb00e4SAndrew Jones #include <asm/sbi.h> 1322f287f4SAndrew Jones #include <asm/setup.h> 14bd744d46SAndrew Jones #include <asm/spinlock.h> 15bd744d46SAndrew Jones 16521cb2adSSamuel Holland #define UART_LSR_OFFSET 5 17521cb2adSSamuel Holland #define UART_LSR_THRE 0x20 18521cb2adSSamuel Holland 19bd744d46SAndrew Jones /* 20bd744d46SAndrew Jones * Use this guess for the uart base in order to make an attempt at 21bd744d46SAndrew Jones * having earlier printf support. We'll overwrite it with the real 22bd744d46SAndrew Jones * base address that we read from the device tree later. This is 23bd744d46SAndrew Jones * the address we expect the virtual machine manager to put in 24bd744d46SAndrew Jones * its generated device tree. 25bd744d46SAndrew Jones */ 26bd744d46SAndrew Jones #define UART_EARLY_BASE ((u8 *)(unsigned long)CONFIG_UART_EARLY_BASE) 27bd744d46SAndrew Jones static volatile u8 *uart0_base = UART_EARLY_BASE; 28*fbeeb847SSamuel Holland static u32 uart0_reg_width = 1; 29*fbeeb847SSamuel Holland static u32 uart0_reg_shift; 30bd744d46SAndrew Jones static struct spinlock uart_lock; 31bd744d46SAndrew Jones 32*fbeeb847SSamuel Holland static u32 uart0_read(u32 num) 33*fbeeb847SSamuel Holland { 34*fbeeb847SSamuel Holland u32 offset = num << uart0_reg_shift; 35*fbeeb847SSamuel Holland 36*fbeeb847SSamuel Holland if (uart0_reg_width == 1) 37*fbeeb847SSamuel Holland return readb(uart0_base + offset); 38*fbeeb847SSamuel Holland else if (uart0_reg_width == 2) 39*fbeeb847SSamuel Holland return readw(uart0_base + offset); 40*fbeeb847SSamuel Holland else 41*fbeeb847SSamuel Holland return readl(uart0_base + offset); 42*fbeeb847SSamuel Holland } 43*fbeeb847SSamuel Holland 44*fbeeb847SSamuel Holland static void uart0_write(u32 num, u32 val) 45*fbeeb847SSamuel Holland { 46*fbeeb847SSamuel Holland u32 offset = num << uart0_reg_shift; 47*fbeeb847SSamuel Holland 48*fbeeb847SSamuel Holland if (uart0_reg_width == 1) 49*fbeeb847SSamuel Holland writeb(val, uart0_base + offset); 50*fbeeb847SSamuel Holland else if (uart0_reg_width == 2) 51*fbeeb847SSamuel Holland writew(val, uart0_base + offset); 52*fbeeb847SSamuel Holland else 53*fbeeb847SSamuel Holland writel(val, uart0_base + offset); 54*fbeeb847SSamuel Holland } 55*fbeeb847SSamuel Holland 5622f287f4SAndrew Jones static void uart0_init_fdt(void) 5722f287f4SAndrew Jones { 5822f287f4SAndrew Jones const char *compatible[] = {"ns16550a"}; 5922f287f4SAndrew Jones struct dt_pbus_reg base; 6022f287f4SAndrew Jones int i, ret; 6122f287f4SAndrew Jones 6222f287f4SAndrew Jones ret = dt_get_default_console_node(); 6322f287f4SAndrew Jones assert(ret >= 0 || ret == -FDT_ERR_NOTFOUND); 6422f287f4SAndrew Jones 6522f287f4SAndrew Jones if (ret == -FDT_ERR_NOTFOUND) { 6622f287f4SAndrew Jones for (i = 0; i < ARRAY_SIZE(compatible); i++) { 6722f287f4SAndrew Jones ret = dt_pbus_get_base_compatible(compatible[i], &base); 6822f287f4SAndrew Jones assert(ret == 0 || ret == -FDT_ERR_NOTFOUND); 6922f287f4SAndrew Jones if (ret == 0) 7022f287f4SAndrew Jones break; 7122f287f4SAndrew Jones } 7222f287f4SAndrew Jones 7322f287f4SAndrew Jones if (ret) { 7422f287f4SAndrew Jones printf("%s: Compatible uart not found in the device tree, aborting...\n", 7522f287f4SAndrew Jones __func__); 7622f287f4SAndrew Jones abort(); 7722f287f4SAndrew Jones } 7822f287f4SAndrew Jones } else { 79*fbeeb847SSamuel Holland const fdt32_t *val; 80*fbeeb847SSamuel Holland int len; 81*fbeeb847SSamuel Holland 82*fbeeb847SSamuel Holland val = fdt_getprop(dt_fdt(), ret, "reg-shift", &len); 83*fbeeb847SSamuel Holland if (len == sizeof(*val)) 84*fbeeb847SSamuel Holland uart0_reg_shift = fdt32_to_cpu(*val); 85*fbeeb847SSamuel Holland 86*fbeeb847SSamuel Holland val = fdt_getprop(dt_fdt(), ret, "reg-io-width", &len); 87*fbeeb847SSamuel Holland if (len == sizeof(*val)) 88*fbeeb847SSamuel Holland uart0_reg_width = fdt32_to_cpu(*val); 89*fbeeb847SSamuel Holland 9022f287f4SAndrew Jones ret = dt_pbus_translate_node(ret, 0, &base); 9122f287f4SAndrew Jones assert(ret == 0); 9222f287f4SAndrew Jones } 9322f287f4SAndrew Jones 9422f287f4SAndrew Jones uart0_base = ioremap(base.addr, base.size); 9522f287f4SAndrew Jones } 9622f287f4SAndrew Jones 9722f287f4SAndrew Jones static void uart0_init_acpi(void) 9822f287f4SAndrew Jones { 9922f287f4SAndrew Jones assert_msg(false, "ACPI not available"); 10022f287f4SAndrew Jones } 10122f287f4SAndrew Jones 10222f287f4SAndrew Jones void io_init(void) 10322f287f4SAndrew Jones { 10422f287f4SAndrew Jones if (dt_available()) 10522f287f4SAndrew Jones uart0_init_fdt(); 10622f287f4SAndrew Jones else 10722f287f4SAndrew Jones uart0_init_acpi(); 10822f287f4SAndrew Jones 10922f287f4SAndrew Jones if (uart0_base != UART_EARLY_BASE) { 11022f287f4SAndrew Jones printf("WARNING: early print support may not work. " 11122f287f4SAndrew Jones "Found uart at %p, but early base is %p.\n", 11222f287f4SAndrew Jones uart0_base, UART_EARLY_BASE); 11322f287f4SAndrew Jones } 11422f287f4SAndrew Jones } 11522f287f4SAndrew Jones 116bd744d46SAndrew Jones void puts(const char *s) 117bd744d46SAndrew Jones { 118bd744d46SAndrew Jones spin_lock(&uart_lock); 119521cb2adSSamuel Holland while (*s) { 120*fbeeb847SSamuel Holland while (!(uart0_read(UART_LSR_OFFSET) & UART_LSR_THRE)) 121521cb2adSSamuel Holland ; 122*fbeeb847SSamuel Holland uart0_write(0, *s++); 123521cb2adSSamuel Holland } 124bd744d46SAndrew Jones spin_unlock(&uart_lock); 125bd744d46SAndrew Jones } 126bd744d46SAndrew Jones 127bd744d46SAndrew Jones /* 128bd744d46SAndrew Jones * Defining halt to take 'code' as an argument guarantees that it will 129bd744d46SAndrew Jones * be in a0 when we halt. That gives us a final chance to see the exit 130bd744d46SAndrew Jones * status while inspecting the halted unit test state. 131bd744d46SAndrew Jones */ 132bd744d46SAndrew Jones void halt(int code); 133bd744d46SAndrew Jones 134bd744d46SAndrew Jones void exit(int code) 135bd744d46SAndrew Jones { 136bd744d46SAndrew Jones printf("\nEXIT: STATUS=%d\n", ((code) << 1) | 1); 1379ccb00e4SAndrew Jones sbi_shutdown(); 138bd744d46SAndrew Jones halt(code); 139bd744d46SAndrew Jones __builtin_unreachable(); 140bd744d46SAndrew Jones } 141