diff mbox series

[kvm-unit-tests,3/3] riscv: Support UARTs with different I/O widths

Message ID 20241210044442.91736-4-samuel.holland@sifive.com (mailing list archive)
State New
Headers show
Series riscv: Improved bare metal support | expand

Commit Message

Samuel Holland Dec. 10, 2024, 4:44 a.m. UTC
Integration of ns16550-compatible UARTs is often done with 16 or 32-bit wide
registers. Add support for these using the standard DT properties.

Signed-off-by: Samuel Holland <samuel.holland@sifive.com>
---
 lib/riscv/io.c | 41 +++++++++++++++++++++++++++++++++++++++--
 1 file changed, 39 insertions(+), 2 deletions(-)
diff mbox series

Patch

diff --git a/lib/riscv/io.c b/lib/riscv/io.c
index 8d684ccd..011b5b1d 100644
--- a/lib/riscv/io.c
+++ b/lib/riscv/io.c
@@ -25,8 +25,34 @@ 
  */
 #define UART_EARLY_BASE ((u8 *)(unsigned long)CONFIG_UART_EARLY_BASE)
 static volatile u8 *uart0_base = UART_EARLY_BASE;
+static u32 uart0_reg_shift = 1;
+static u32 uart0_reg_width = 1;
 static struct spinlock uart_lock;
 
+static u32 uart0_read(u32 num)
+{
+	u32 offset = num << uart0_reg_shift;
+
+	if (uart0_reg_width == 1)
+		return readb(uart0_base + offset);
+	else if (uart0_reg_width == 2)
+		return readw(uart0_base + offset);
+	else
+		return readl(uart0_base + offset);
+}
+
+static void uart0_write(u32 num, u32 val)
+{
+	u32 offset = num << uart0_reg_shift;
+
+	if (uart0_reg_width == 1)
+		writeb(val, uart0_base + offset);
+	else if (uart0_reg_width == 2)
+		writew(val, uart0_base + offset);
+	else
+		writel(val, uart0_base + offset);
+}
+
 static void uart0_init_fdt(void)
 {
 	const char *compatible[] = {"ns16550a"};
@@ -50,6 +76,17 @@  static void uart0_init_fdt(void)
 			abort();
 		}
 	} else {
+		const fdt32_t *val;
+		int len;
+
+		val = fdt_getprop(dt_fdt(), ret, "reg-shift", &len);
+		if (len == sizeof(*val))
+			uart0_reg_shift = fdt32_to_cpu(*val);
+
+		val = fdt_getprop(dt_fdt(), ret, "reg-io-width", &len);
+		if (len == sizeof(*val))
+			uart0_reg_width = fdt32_to_cpu(*val);
+
 		ret = dt_pbus_translate_node(ret, 0, &base);
 		assert(ret == 0);
 	}
@@ -80,9 +117,9 @@  void puts(const char *s)
 {
 	spin_lock(&uart_lock);
 	while (*s) {
-		while (!(readb(uart0_base + UART_LSR_OFFSET) & UART_LSR_THRE))
+		while (!(uart0_read(UART_LSR_OFFSET) & UART_LSR_THRE))
 			;
-		writeb(*s++, uart0_base);
+		uart0_write(0, *s++);
 	}
 	spin_unlock(&uart_lock);
 }