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 #ifndef CONFIG_SBI_CONSOLE
uart0_read(u32 num)34 static u32 uart0_read(u32 num)
35 {
36 u32 offset = num << uart0_reg_shift;
37
38 if (uart0_reg_width == 1)
39 return readb(uart0_base + offset);
40 else if (uart0_reg_width == 2)
41 return readw(uart0_base + offset);
42 else
43 return readl(uart0_base + offset);
44 }
45
uart0_write(u32 num,u32 val)46 static void uart0_write(u32 num, u32 val)
47 {
48 u32 offset = num << uart0_reg_shift;
49
50 if (uart0_reg_width == 1)
51 writeb(val, uart0_base + offset);
52 else if (uart0_reg_width == 2)
53 writew(val, uart0_base + offset);
54 else
55 writel(val, uart0_base + offset);
56 }
57 #endif
58
uart0_init_fdt(void)59 static void uart0_init_fdt(void)
60 {
61 const char *compatible[] = {"ns16550a"};
62 struct dt_pbus_reg base;
63 int i, ret;
64
65 ret = dt_get_default_console_node();
66 assert(ret >= 0 || ret == -FDT_ERR_NOTFOUND);
67
68 if (ret == -FDT_ERR_NOTFOUND) {
69 for (i = 0; i < ARRAY_SIZE(compatible); i++) {
70 ret = dt_pbus_get_base_compatible(compatible[i], &base);
71 assert(ret == 0 || ret == -FDT_ERR_NOTFOUND);
72 if (ret == 0)
73 break;
74 }
75
76 if (ret) {
77 printf("%s: Compatible uart not found in the device tree, aborting...\n",
78 __func__);
79 abort();
80 }
81 } else {
82 const fdt32_t *val;
83 int len;
84
85 val = fdt_getprop(dt_fdt(), ret, "reg-shift", &len);
86 if (len == sizeof(*val))
87 uart0_reg_shift = fdt32_to_cpu(*val);
88
89 val = fdt_getprop(dt_fdt(), ret, "reg-io-width", &len);
90 if (len == sizeof(*val))
91 uart0_reg_width = fdt32_to_cpu(*val);
92
93 ret = dt_pbus_translate_node(ret, 0, &base);
94 assert(ret == 0);
95 }
96
97 uart0_base = ioremap(base.addr, base.size);
98 }
99
uart0_init_acpi(void)100 static void uart0_init_acpi(void)
101 {
102 assert_msg(false, "ACPI not available");
103 }
104
io_init(void)105 void io_init(void)
106 {
107 if (dt_available())
108 uart0_init_fdt();
109 else
110 uart0_init_acpi();
111
112 if (uart0_base != UART_EARLY_BASE) {
113 printf("WARNING: early print support may not work. "
114 "Found uart at %p, but early base is %p.\n",
115 uart0_base, UART_EARLY_BASE);
116 }
117 }
118
119 #ifdef CONFIG_SBI_CONSOLE
puts(const char * s)120 void puts(const char *s)
121 {
122 phys_addr_t addr = virt_to_phys((void *)s);
123 unsigned long hi = upper_32_bits(addr);
124 unsigned long lo = lower_32_bits(addr);
125
126 spin_lock(&uart_lock);
127 sbi_ecall(SBI_EXT_DBCN, SBI_EXT_DBCN_CONSOLE_WRITE, strlen(s), lo, hi, 0, 0, 0);
128 spin_unlock(&uart_lock);
129 }
130 #else
puts(const char * s)131 void puts(const char *s)
132 {
133 spin_lock(&uart_lock);
134 while (*s) {
135 while (!(uart0_read(UART_LSR_OFFSET) & UART_LSR_THRE))
136 ;
137 uart0_write(0, *s++);
138 }
139 spin_unlock(&uart_lock);
140 }
141 #endif
142
143 /*
144 * Defining halt to take 'code' as an argument guarantees that it will
145 * be in a0 when we halt. That gives us a final chance to see the exit
146 * status while inspecting the halted unit test state.
147 */
148 void halt(int code);
149
exit(int code)150 void exit(int code)
151 {
152 printf("\nEXIT: STATUS=%d\n", ((code) << 1) | 1);
153 sbi_shutdown();
154 halt(code);
155 __builtin_unreachable();
156 }
157