xref: /kvm-unit-tests/lib/riscv/io.c (revision 9ccb00e4e5514d4dd5b279c8ec6a396875782a0b)
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>
12*9ccb00e4SAndrew Jones #include <asm/sbi.h>
1322f287f4SAndrew Jones #include <asm/setup.h>
14bd744d46SAndrew Jones #include <asm/spinlock.h>
15bd744d46SAndrew Jones 
16bd744d46SAndrew Jones /*
17bd744d46SAndrew Jones  * Use this guess for the uart base in order to make an attempt at
18bd744d46SAndrew Jones  * having earlier printf support. We'll overwrite it with the real
19bd744d46SAndrew Jones  * base address that we read from the device tree later. This is
20bd744d46SAndrew Jones  * the address we expect the virtual machine manager to put in
21bd744d46SAndrew Jones  * its generated device tree.
22bd744d46SAndrew Jones  */
23bd744d46SAndrew Jones #define UART_EARLY_BASE ((u8 *)(unsigned long)CONFIG_UART_EARLY_BASE)
24bd744d46SAndrew Jones static volatile u8 *uart0_base = UART_EARLY_BASE;
25bd744d46SAndrew Jones static struct spinlock uart_lock;
26bd744d46SAndrew Jones 
2722f287f4SAndrew Jones static void uart0_init_fdt(void)
2822f287f4SAndrew Jones {
2922f287f4SAndrew Jones 	const char *compatible[] = {"ns16550a"};
3022f287f4SAndrew Jones 	struct dt_pbus_reg base;
3122f287f4SAndrew Jones 	int i, ret;
3222f287f4SAndrew Jones 
3322f287f4SAndrew Jones 	ret = dt_get_default_console_node();
3422f287f4SAndrew Jones 	assert(ret >= 0 || ret == -FDT_ERR_NOTFOUND);
3522f287f4SAndrew Jones 
3622f287f4SAndrew Jones 	if (ret == -FDT_ERR_NOTFOUND) {
3722f287f4SAndrew Jones 		for (i = 0; i < ARRAY_SIZE(compatible); i++) {
3822f287f4SAndrew Jones 			ret = dt_pbus_get_base_compatible(compatible[i], &base);
3922f287f4SAndrew Jones 			assert(ret == 0 || ret == -FDT_ERR_NOTFOUND);
4022f287f4SAndrew Jones 			if (ret == 0)
4122f287f4SAndrew Jones 				break;
4222f287f4SAndrew Jones 		}
4322f287f4SAndrew Jones 
4422f287f4SAndrew Jones 		if (ret) {
4522f287f4SAndrew Jones 			printf("%s: Compatible uart not found in the device tree, aborting...\n",
4622f287f4SAndrew Jones 			       __func__);
4722f287f4SAndrew Jones 			abort();
4822f287f4SAndrew Jones 		}
4922f287f4SAndrew Jones 	} else {
5022f287f4SAndrew Jones 		ret = dt_pbus_translate_node(ret, 0, &base);
5122f287f4SAndrew Jones 		assert(ret == 0);
5222f287f4SAndrew Jones 	}
5322f287f4SAndrew Jones 
5422f287f4SAndrew Jones 	uart0_base = ioremap(base.addr, base.size);
5522f287f4SAndrew Jones }
5622f287f4SAndrew Jones 
5722f287f4SAndrew Jones static void uart0_init_acpi(void)
5822f287f4SAndrew Jones {
5922f287f4SAndrew Jones 	assert_msg(false, "ACPI not available");
6022f287f4SAndrew Jones }
6122f287f4SAndrew Jones 
6222f287f4SAndrew Jones void io_init(void)
6322f287f4SAndrew Jones {
6422f287f4SAndrew Jones 	if (dt_available())
6522f287f4SAndrew Jones 		uart0_init_fdt();
6622f287f4SAndrew Jones 	else
6722f287f4SAndrew Jones 		uart0_init_acpi();
6822f287f4SAndrew Jones 
6922f287f4SAndrew Jones 	if (uart0_base != UART_EARLY_BASE) {
7022f287f4SAndrew Jones 		printf("WARNING: early print support may not work. "
7122f287f4SAndrew Jones 		       "Found uart at %p, but early base is %p.\n",
7222f287f4SAndrew Jones 		       uart0_base, UART_EARLY_BASE);
7322f287f4SAndrew Jones 	}
7422f287f4SAndrew Jones }
7522f287f4SAndrew Jones 
76bd744d46SAndrew Jones void puts(const char *s)
77bd744d46SAndrew Jones {
78bd744d46SAndrew Jones 	spin_lock(&uart_lock);
79bd744d46SAndrew Jones 	while (*s)
80bd744d46SAndrew Jones 		writeb(*s++, uart0_base);
81bd744d46SAndrew Jones 	spin_unlock(&uart_lock);
82bd744d46SAndrew Jones }
83bd744d46SAndrew Jones 
84bd744d46SAndrew Jones /*
85bd744d46SAndrew Jones  * Defining halt to take 'code' as an argument guarantees that it will
86bd744d46SAndrew Jones  * be in a0 when we halt. That gives us a final chance to see the exit
87bd744d46SAndrew Jones  * status while inspecting the halted unit test state.
88bd744d46SAndrew Jones  */
89bd744d46SAndrew Jones void halt(int code);
90bd744d46SAndrew Jones 
91bd744d46SAndrew Jones void exit(int code)
92bd744d46SAndrew Jones {
93bd744d46SAndrew Jones 	printf("\nEXIT: STATUS=%d\n", ((code) << 1) | 1);
94*9ccb00e4SAndrew Jones 	sbi_shutdown();
95bd744d46SAndrew Jones 	halt(code);
96bd744d46SAndrew Jones 	__builtin_unreachable();
97bd744d46SAndrew Jones }
98