xref: /kvm-unit-tests/lib/riscv/io.c (revision 695740795adee59b48599e2f1a6bf19866a77779)
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