[PATCH] lib: utils/serial: Add Bouffalo Lab BL808 UART driver

Kwanghoon Son asdfkwang at gmail.com
Wed Oct 21 04:50:17 PDT 2026


Add a polling UART driver for the Bouffalo Lab BL808 SoC and its
FDT frontend, matching bflb,bl808-uart. Read the register base, input
clock and baud rate from the device tree and register the SBI console.

The preceding firmware must enable the UART input clock and configure
the pinmux before entering OpenSBI. The driver only configures the UART
peripheral; it does not change SoC clocks or board pin routing.

Signed-off-by: Kwanghoon Son <asdfkwang at gmail.com>
---
The BL808 is also used on the PINE64 Ox64 [1]. Zephyr already provides
a Bouffalo Lab UART driver using the "bflb,uart" compatible [2], and
supports the M1S Dock's E907 core [3].

This patch provides a polling UART console driver for OpenSBI.
Testing was performed on the Sipeed M1s Dock's C906 D0 core using
UART3 at 2 Mbaud, with the input clock and pinmux configured by
the preceding firmware.

The FDT frontend currently requires reg, clock-frequency and
current-speed. This patch does not add board initialization or
T-Head PMU support.

Validation:
- RV32 and RV64 cross-builds passed with the driver enabled.
- UART output through the OpenSBI banner and handoff to S-mode
  U-Boot was verified on the UART-only implementation.
- The board-tested revision was 6b317b61. The submitted revision
  differs only by leaving the driver disabled in generic defconfig;
  the UART driver sources are unchanged.
- UART RX, operation on the Ox64, and repeated cold boots remain
  untested.

[1] https://pine64.org/documentation/Ox64/
[2] https://docs.zephyrproject.org/latest/build/dts/api/bindings/serial/bflb,uart.html
[3] https://docs.zephyrproject.org/latest/boards/sipeed/m1s_dock/doc/index.html

 include/sbi_utils/serial/bl808-uart.h |  11 +++
 lib/utils/serial/Kconfig              |   9 +++
 lib/utils/serial/bl808-uart.c         | 107 ++++++++++++++++++++++++++
 lib/utils/serial/fdt_serial_bl808.c   |  44 +++++++++++
 lib/utils/serial/objects.mk           |   4 +
 5 files changed, 175 insertions(+)
 create mode 100644 include/sbi_utils/serial/bl808-uart.h
 create mode 100644 lib/utils/serial/bl808-uart.c
 create mode 100644 lib/utils/serial/fdt_serial_bl808.c

diff --git a/include/sbi_utils/serial/bl808-uart.h b/include/sbi_utils/serial/bl808-uart.h
new file mode 100644
index 00000000..aa6281db
--- /dev/null
+++ b/include/sbi_utils/serial/bl808-uart.h
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+
+#ifndef __SERIAL_BL808_UART_H__
+#define __SERIAL_BL808_UART_H__
+
+#include <sbi/sbi_types.h>
+
+/* The caller must enable the input clock and configure the pinmux. */
+int bl808_uart_init(unsigned long base, u32 in_freq, u32 baudrate);
+
+#endif
diff --git a/lib/utils/serial/Kconfig b/lib/utils/serial/Kconfig
index 21619d0d..15ede441 100644
--- a/lib/utils/serial/Kconfig
+++ b/lib/utils/serial/Kconfig
@@ -14,6 +14,11 @@ config FDT_SERIAL_ALTR_JUART
 	select SERIAL_ALTR_JUART
 	default n
 
+config FDT_SERIAL_BL808
+	bool "Bouffalo Lab BL808 UART FDT driver"
+	select SERIAL_BL808
+	default n
+
 config FDT_SERIAL_CADENCE
 	bool "Cadence UART FDT driver"
 	select SERIAL_CADENCE
@@ -65,6 +70,10 @@ config SERIAL_ALTR_JUART
 	bool "Altera JTAG UART support"
 	default n
 
+config SERIAL_BL808
+	bool "Bouffalo Lab BL808 UART support"
+	default n
+
 config SERIAL_CADENCE
 	bool "Cadence UART support"
 	default n
diff --git a/lib/utils/serial/bl808-uart.c b/lib/utils/serial/bl808-uart.c
new file mode 100644
index 00000000..e1f1ffbf
--- /dev/null
+++ b/lib/utils/serial/bl808-uart.c
@@ -0,0 +1,107 @@
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <sbi/riscv_io.h>
+#include <sbi/sbi_console.h>
+#include <sbi/sbi_domain.h>
+#include <sbi/sbi_error.h>
+#include <sbi_utils/serial/bl808-uart.h>
+
+#define UART_TX_CONFIG		0x00
+#define UART_RX_CONFIG		0x04
+#define UART_BIT_PERIOD		0x08
+#define UART_DATA_CONFIG	0x0c
+#define UART_SW_MODE		0x1c
+#define UART_INT_MASK		0x24
+#define UART_INT_CLEAR		0x28
+#define UART_INT_ENABLE		0x2c
+#define UART_FIFO_CONFIG0	0x80
+#define UART_FIFO_CONFIG1	0x84
+#define UART_FIFO_WDATA		0x88
+#define UART_FIFO_RDATA		0x8c
+
+#define UART_TX_ENABLE		BIT(0)
+#define UART_RX_ENABLE		BIT(0)
+#define UART_TX_FREE_RUN		BIT(2)
+#define UART_TX_DATA_BITS	(7 << 8)
+#define UART_TX_STOP_BITS	BIT(11)
+#define UART_RX_DATA_BITS	(7 << 8)
+#define UART_INT_MASK_ALL	0xfff
+#define UART_INT_CLEAR_ALL	0xf33
+#define UART_TX_FIFO_CLEAR	BIT(2)
+#define UART_RX_FIFO_CLEAR	BIT(3)
+#define UART_TX_FIFO_COUNT	0x3f
+#define UART_RX_FIFO_COUNT	(0x3f << 8)
+
+static volatile void *uart_base;
+
+static void bl808_uart_putc(char ch)
+{
+	while (!(readl(uart_base + UART_FIFO_CONFIG1) & UART_TX_FIFO_COUNT))
+		;
+
+	writeb(ch, uart_base + UART_FIFO_WDATA);
+}
+
+static int bl808_uart_getc(void)
+{
+	if (!(readl(uart_base + UART_FIFO_CONFIG1) & UART_RX_FIFO_COUNT))
+		return -1;
+
+	return readb(uart_base + UART_FIFO_RDATA);
+}
+
+static struct sbi_console_device bl808_console = {
+	.name = "bl808_uart",
+	.console_putc = bl808_uart_putc,
+	.console_getc = bl808_uart_getc,
+};
+
+int bl808_uart_init(unsigned long base, u32 in_freq, u32 baudrate)
+{
+	u64 divisor;
+	u32 period;
+	int rc;
+
+	if (!base || (base & 3) || base > -1UL - PAGE_SIZE ||
+	    !in_freq || !baudrate)
+		return SBI_EINVAL;
+
+	divisor = ((u64)in_freq + baudrate / 2) / baudrate;
+	if (!divisor || divisor > 0x10000)
+		return SBI_EINVAL;
+
+	rc = sbi_domain_root_add_memrange(base, PAGE_SIZE, PAGE_SIZE,
+					  (SBI_DOMAIN_MEMREGION_MMIO |
+					  SBI_DOMAIN_MEMREGION_SHARED_SURW_MRW));
+	if (rc)
+		return rc;
+
+	uart_base = (void *)base;
+	writel(0, uart_base + UART_TX_CONFIG);
+	writel(0, uart_base + UART_RX_CONFIG);
+
+	/* The TX and RX period fields both encode divisor minus one. */
+	period = divisor - 1;
+	writel(period | (period << 16), uart_base + UART_BIT_PERIOD);
+	writel(UART_TX_FREE_RUN | UART_TX_DATA_BITS | UART_TX_STOP_BITS,
+	       uart_base + UART_TX_CONFIG);
+	writel(UART_RX_DATA_BITS, uart_base + UART_RX_CONFIG);
+	writel(0, uart_base + UART_DATA_CONFIG);
+	writel(0, uart_base + UART_SW_MODE);
+
+	/* Polling only: mask interrupts, disable DMA, and clear both FIFOs. */
+	writel(UART_INT_MASK_ALL, uart_base + UART_INT_MASK);
+	writel(0, uart_base + UART_INT_ENABLE);
+	writel(UART_INT_CLEAR_ALL, uart_base + UART_INT_CLEAR);
+	writel(UART_TX_FIFO_CLEAR | UART_RX_FIFO_CLEAR,
+	       uart_base + UART_FIFO_CONFIG0);
+	writel(0, uart_base + UART_FIFO_CONFIG1);
+
+	writel(UART_TX_FREE_RUN | UART_TX_DATA_BITS | UART_TX_STOP_BITS |
+	       UART_TX_ENABLE, uart_base + UART_TX_CONFIG);
+	writel(UART_RX_DATA_BITS | UART_RX_ENABLE, uart_base + UART_RX_CONFIG);
+
+	sbi_console_set_device(&bl808_console);
+
+	return 0;
+}
diff --git a/lib/utils/serial/fdt_serial_bl808.c b/lib/utils/serial/fdt_serial_bl808.c
new file mode 100644
index 00000000..99e19bae
--- /dev/null
+++ b/lib/utils/serial/fdt_serial_bl808.c
@@ -0,0 +1,44 @@
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <libfdt.h>
+#include <sbi/sbi_error.h>
+#include <sbi_utils/fdt/fdt_helper.h>
+#include <sbi_utils/serial/bl808-uart.h>
+#include <sbi_utils/serial/fdt_serial.h>
+
+static int serial_bl808_init(const void *fdt, int nodeoff,
+			     const struct fdt_match *match)
+{
+	const fdt32_t *val;
+	u64 addr, size;
+	u32 freq, baud;
+	int len, rc;
+
+	rc = fdt_get_node_addr_size(fdt, nodeoff, 0, &addr, &size);
+	if (rc)
+		return rc;
+	if (size < 0x90 || addr != (unsigned long)addr)
+		return SBI_EINVAL;
+
+	val = fdt_getprop(fdt, nodeoff, "clock-frequency", &len);
+	if (!val || len != sizeof(*val))
+		return SBI_EINVAL;
+	freq = fdt32_to_cpu(*val);
+
+	val = fdt_getprop(fdt, nodeoff, "current-speed", &len);
+	if (!val || len != sizeof(*val))
+		return SBI_EINVAL;
+	baud = fdt32_to_cpu(*val);
+
+	return bl808_uart_init(addr, freq, baud);
+}
+
+static const struct fdt_match serial_bl808_match[] = {
+	{ .compatible = "bflb,bl808-uart" },
+	{ },
+};
+
+const struct fdt_driver fdt_serial_bl808 = {
+	.match_table = serial_bl808_match,
+	.init = serial_bl808_init,
+};
diff --git a/lib/utils/serial/objects.mk b/lib/utils/serial/objects.mk
index 94cf3a4b..d8659602 100644
--- a/lib/utils/serial/objects.mk
+++ b/lib/utils/serial/objects.mk
@@ -13,6 +13,9 @@ libsbiutils-objs-$(CONFIG_FDT_SERIAL) += serial/fdt_serial_drivers.carray.o
 carray-fdt_serial_drivers-$(CONFIG_FDT_SERIAL_ALTR_JUART) += fdt_serial_altr_juart
 libsbiutils-objs-$(CONFIG_FDT_SERIAL_ALTR_JUART) += serial/fdt_serial_altr_juart.o
 
+carray-fdt_serial_drivers-$(CONFIG_FDT_SERIAL_BL808) += fdt_serial_bl808
+libsbiutils-objs-$(CONFIG_FDT_SERIAL_BL808) += serial/fdt_serial_bl808.o
+
 carray-fdt_serial_drivers-$(CONFIG_FDT_SERIAL_CADENCE) += fdt_serial_cadence
 libsbiutils-objs-$(CONFIG_FDT_SERIAL_CADENCE) += serial/fdt_serial_cadence.o
 
@@ -41,6 +44,7 @@ carray-fdt_serial_drivers-$(CONFIG_FDT_SERIAL_XILINX_UARTLITE) += fdt_serial_xln
 libsbiutils-objs-$(CONFIG_FDT_SERIAL_XILINX_UARTLITE) += serial/fdt_serial_xlnx_uartlite.o
 
 libsbiutils-objs-$(CONFIG_SERIAL_ALTR_JUART) += serial/altr-juart.o
+libsbiutils-objs-$(CONFIG_SERIAL_BL808) += serial/bl808-uart.o
 libsbiutils-objs-$(CONFIG_SERIAL_CADENCE) += serial/cadence-uart.o
 libsbiutils-objs-$(CONFIG_SERIAL_GAISLER) += serial/gaisler-uart.o
 libsbiutils-objs-$(CONFIG_SERIAL_RENESAS_SCIF) += serial/renesas_scif.o

base-commit: 6ad246a1494807ee91e36ffcf60e4c30bb309d45
-- 
2.47.3




More information about the opensbi mailing list