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