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>
9*56a30199SAndrew Jones #include <bitops.h>
10bd744d46SAndrew Jones #include <config.h>
1122f287f4SAndrew Jones #include <devicetree.h>
12bd744d46SAndrew Jones #include <asm/io.h>
139ccb00e4SAndrew Jones #include <asm/sbi.h>
1422f287f4SAndrew Jones #include <asm/setup.h>
15bd744d46SAndrew Jones #include <asm/spinlock.h>
16bd744d46SAndrew Jones
17521cb2adSSamuel Holland #define UART_LSR_OFFSET 5
18521cb2adSSamuel Holland #define UART_LSR_THRE 0x20
19521cb2adSSamuel Holland
20bd744d46SAndrew Jones /*
21bd744d46SAndrew Jones * Use this guess for the uart base in order to make an attempt at
22bd744d46SAndrew Jones * having earlier printf support. We'll overwrite it with the real
23bd744d46SAndrew Jones * base address that we read from the device tree later. This is
24bd744d46SAndrew Jones * the address we expect the virtual machine manager to put in
25bd744d46SAndrew Jones * its generated device tree.
26bd744d46SAndrew Jones */
27bd744d46SAndrew Jones #define UART_EARLY_BASE ((u8 *)(unsigned long)CONFIG_UART_EARLY_BASE)
28bd744d46SAndrew Jones static volatile u8 *uart0_base = UART_EARLY_BASE;
29fbeeb847SSamuel Holland static u32 uart0_reg_width = 1;
30fbeeb847SSamuel Holland static u32 uart0_reg_shift;
31bd744d46SAndrew Jones static struct spinlock uart_lock;
32bd744d46SAndrew Jones
33*56a30199SAndrew Jones #ifndef CONFIG_SBI_CONSOLE
uart0_read(u32 num)34fbeeb847SSamuel Holland static u32 uart0_read(u32 num)
35fbeeb847SSamuel Holland {
36fbeeb847SSamuel Holland u32 offset = num << uart0_reg_shift;
37fbeeb847SSamuel Holland
38fbeeb847SSamuel Holland if (uart0_reg_width == 1)
39fbeeb847SSamuel Holland return readb(uart0_base + offset);
40fbeeb847SSamuel Holland else if (uart0_reg_width == 2)
41fbeeb847SSamuel Holland return readw(uart0_base + offset);
42fbeeb847SSamuel Holland else
43fbeeb847SSamuel Holland return readl(uart0_base + offset);
44fbeeb847SSamuel Holland }
45fbeeb847SSamuel Holland
uart0_write(u32 num,u32 val)46fbeeb847SSamuel Holland static void uart0_write(u32 num, u32 val)
47fbeeb847SSamuel Holland {
48fbeeb847SSamuel Holland u32 offset = num << uart0_reg_shift;
49fbeeb847SSamuel Holland
50fbeeb847SSamuel Holland if (uart0_reg_width == 1)
51fbeeb847SSamuel Holland writeb(val, uart0_base + offset);
52fbeeb847SSamuel Holland else if (uart0_reg_width == 2)
53fbeeb847SSamuel Holland writew(val, uart0_base + offset);
54fbeeb847SSamuel Holland else
55fbeeb847SSamuel Holland writel(val, uart0_base + offset);
56fbeeb847SSamuel Holland }
57*56a30199SAndrew Jones #endif
58fbeeb847SSamuel Holland
uart0_init_fdt(void)5922f287f4SAndrew Jones static void uart0_init_fdt(void)
6022f287f4SAndrew Jones {
6122f287f4SAndrew Jones const char *compatible[] = {"ns16550a"};
6222f287f4SAndrew Jones struct dt_pbus_reg base;
6322f287f4SAndrew Jones int i, ret;
6422f287f4SAndrew Jones
6522f287f4SAndrew Jones ret = dt_get_default_console_node();
6622f287f4SAndrew Jones assert(ret >= 0 || ret == -FDT_ERR_NOTFOUND);
6722f287f4SAndrew Jones
6822f287f4SAndrew Jones if (ret == -FDT_ERR_NOTFOUND) {
6922f287f4SAndrew Jones for (i = 0; i < ARRAY_SIZE(compatible); i++) {
7022f287f4SAndrew Jones ret = dt_pbus_get_base_compatible(compatible[i], &base);
7122f287f4SAndrew Jones assert(ret == 0 || ret == -FDT_ERR_NOTFOUND);
7222f287f4SAndrew Jones if (ret == 0)
7322f287f4SAndrew Jones break;
7422f287f4SAndrew Jones }
7522f287f4SAndrew Jones
7622f287f4SAndrew Jones if (ret) {
7722f287f4SAndrew Jones printf("%s: Compatible uart not found in the device tree, aborting...\n",
7822f287f4SAndrew Jones __func__);
7922f287f4SAndrew Jones abort();
8022f287f4SAndrew Jones }
8122f287f4SAndrew Jones } else {
82fbeeb847SSamuel Holland const fdt32_t *val;
83fbeeb847SSamuel Holland int len;
84fbeeb847SSamuel Holland
85fbeeb847SSamuel Holland val = fdt_getprop(dt_fdt(), ret, "reg-shift", &len);
86fbeeb847SSamuel Holland if (len == sizeof(*val))
87fbeeb847SSamuel Holland uart0_reg_shift = fdt32_to_cpu(*val);
88fbeeb847SSamuel Holland
89fbeeb847SSamuel Holland val = fdt_getprop(dt_fdt(), ret, "reg-io-width", &len);
90fbeeb847SSamuel Holland if (len == sizeof(*val))
91fbeeb847SSamuel Holland uart0_reg_width = fdt32_to_cpu(*val);
92fbeeb847SSamuel Holland
9322f287f4SAndrew Jones ret = dt_pbus_translate_node(ret, 0, &base);
9422f287f4SAndrew Jones assert(ret == 0);
9522f287f4SAndrew Jones }
9622f287f4SAndrew Jones
9722f287f4SAndrew Jones uart0_base = ioremap(base.addr, base.size);
9822f287f4SAndrew Jones }
9922f287f4SAndrew Jones
uart0_init_acpi(void)10022f287f4SAndrew Jones static void uart0_init_acpi(void)
10122f287f4SAndrew Jones {
10222f287f4SAndrew Jones assert_msg(false, "ACPI not available");
10322f287f4SAndrew Jones }
10422f287f4SAndrew Jones
io_init(void)10522f287f4SAndrew Jones void io_init(void)
10622f287f4SAndrew Jones {
10722f287f4SAndrew Jones if (dt_available())
10822f287f4SAndrew Jones uart0_init_fdt();
10922f287f4SAndrew Jones else
11022f287f4SAndrew Jones uart0_init_acpi();
11122f287f4SAndrew Jones
11222f287f4SAndrew Jones if (uart0_base != UART_EARLY_BASE) {
11322f287f4SAndrew Jones printf("WARNING: early print support may not work. "
11422f287f4SAndrew Jones "Found uart at %p, but early base is %p.\n",
11522f287f4SAndrew Jones uart0_base, UART_EARLY_BASE);
11622f287f4SAndrew Jones }
11722f287f4SAndrew Jones }
11822f287f4SAndrew Jones
119*56a30199SAndrew Jones #ifdef CONFIG_SBI_CONSOLE
puts(const char * s)120*56a30199SAndrew Jones void puts(const char *s)
121*56a30199SAndrew Jones {
122*56a30199SAndrew Jones phys_addr_t addr = virt_to_phys((void *)s);
123*56a30199SAndrew Jones unsigned long hi = upper_32_bits(addr);
124*56a30199SAndrew Jones unsigned long lo = lower_32_bits(addr);
125*56a30199SAndrew Jones
126*56a30199SAndrew Jones spin_lock(&uart_lock);
127*56a30199SAndrew Jones sbi_ecall(SBI_EXT_DBCN, SBI_EXT_DBCN_CONSOLE_WRITE, strlen(s), lo, hi, 0, 0, 0);
128*56a30199SAndrew Jones spin_unlock(&uart_lock);
129*56a30199SAndrew Jones }
130*56a30199SAndrew Jones #else
puts(const char * s)131bd744d46SAndrew Jones void puts(const char *s)
132bd744d46SAndrew Jones {
133bd744d46SAndrew Jones spin_lock(&uart_lock);
134521cb2adSSamuel Holland while (*s) {
135fbeeb847SSamuel Holland while (!(uart0_read(UART_LSR_OFFSET) & UART_LSR_THRE))
136521cb2adSSamuel Holland ;
137fbeeb847SSamuel Holland uart0_write(0, *s++);
138521cb2adSSamuel Holland }
139bd744d46SAndrew Jones spin_unlock(&uart_lock);
140bd744d46SAndrew Jones }
141*56a30199SAndrew Jones #endif
142bd744d46SAndrew Jones
143bd744d46SAndrew Jones /*
144bd744d46SAndrew Jones * Defining halt to take 'code' as an argument guarantees that it will
145bd744d46SAndrew Jones * be in a0 when we halt. That gives us a final chance to see the exit
146bd744d46SAndrew Jones * status while inspecting the halted unit test state.
147bd744d46SAndrew Jones */
148bd744d46SAndrew Jones void halt(int code);
149bd744d46SAndrew Jones
exit(int code)150bd744d46SAndrew Jones void exit(int code)
151bd744d46SAndrew Jones {
152bd744d46SAndrew Jones printf("\nEXIT: STATUS=%d\n", ((code) << 1) | 1);
1539ccb00e4SAndrew Jones sbi_shutdown();
154bd744d46SAndrew Jones halt(code);
155bd744d46SAndrew Jones __builtin_unreachable();
156bd744d46SAndrew Jones }
157