Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions include/sbi_utils/serial/pl011.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2026 Alibaba Group Holding Limited.
*/

#ifndef __SERIAL_PL011_UART_H__
#define __SERIAL_PL011_UART_H__

#include <sbi/sbi_types.h>

int pl011_uart_init(unsigned long base, u32 in_freq, u32 baudrate);

#endif
9 changes: 9 additions & 0 deletions lib/utils/serial/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ config FDT_SERIAL_XILINX_UARTLITE
select SERIAL_XILINX_UARTLITE
default n

config FDT_SERIAL_PL011
bool "ARM PL011 UART FDT driver"
select SERIAL_PL011
default n

endif

config SERIAL_CADENCE
Expand Down Expand Up @@ -88,6 +93,10 @@ config SERIAL_XILINX_UARTLITE
bool "Xilinx UART Lite support"
default n

config SERIAL_PL011
bool "ARM PL011 UART support"
default n

config SERIAL_SEMIHOSTING
bool "Semihosting support"
default n
Expand Down
33 changes: 33 additions & 0 deletions lib/utils/serial/fdt_serial_pl011.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2026 Alibaba Group Holding Limited.
*/

#include <sbi_utils/fdt/fdt_helper.h>
#include <sbi_utils/serial/fdt_serial.h>
#include <sbi_utils/serial/pl011.h>

static int serial_pl011_init(const void *fdt, int nodeoff,
const struct fdt_match *match)
{
int rc;
struct platform_uart_data uart = { 0 };

rc = fdt_parse_uart_node(fdt, nodeoff, &uart);
if (rc)
return rc;

return pl011_uart_init(uart.addr, uart.freq, uart.baud);
}

static const struct fdt_match serial_pl011_match[] = {
{ .compatible = "arm,pl011" },
{ .compatible = "arm,sbsa-uart" },
{ },
};

const struct fdt_driver fdt_serial_pl011 = {
.match_table = serial_pl011_match,
.init = serial_pl011_init
};
4 changes: 4 additions & 0 deletions lib/utils/serial/objects.mk
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ libsbiutils-objs-$(CONFIG_FDT_SERIAL_UART8250) += serial/fdt_serial_uart8250.o
carray-fdt_serial_drivers-$(CONFIG_FDT_SERIAL_XILINX_UARTLITE) += fdt_serial_xlnx_uartlite
libsbiutils-objs-$(CONFIG_FDT_SERIAL_XILINX_UARTLITE) += serial/fdt_serial_xlnx_uartlite.o

carray-fdt_serial_drivers-$(CONFIG_FDT_SERIAL_PL011) += fdt_serial_pl011
libsbiutils-objs-$(CONFIG_FDT_SERIAL_PL011) += serial/fdt_serial_pl011.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
Expand All @@ -45,4 +48,5 @@ libsbiutils-objs-$(CONFIG_SERIAL_SIFIVE) += serial/sifive-uart.o
libsbiutils-objs-$(CONFIG_SERIAL_LITEX) += serial/litex-uart.o
libsbiutils-objs-$(CONFIG_SERIAL_UART8250) += serial/uart8250.o
libsbiutils-objs-$(CONFIG_SERIAL_XILINX_UARTLITE) += serial/xlnx-uartlite.o
libsbiutils-objs-$(CONFIG_SERIAL_PL011) += serial/pl011.o
libsbiutils-objs-$(CONFIG_SERIAL_SEMIHOSTING) += serial/semihosting.o
108 changes: 108 additions & 0 deletions lib/utils/serial/pl011.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2026 Alibaba Group Holding Limited.
*
* ARM PrimeCell PL011 UART driver.
*/

#include <sbi/sbi_domain.h>
#include <sbi/riscv_io.h>
#include <sbi/sbi_console.h>
#include <sbi_utils/serial/pl011.h>

/* clang-format off */

#define UART_REG_DR 0x00 /* Data register */
#define UART_REG_FR 0x18 /* Flag register */
#define UART_REG_IBRD 0x24 /* Integer baud rate divisor */
#define UART_REG_FBRD 0x28 /* Fractional baud rate divisor */
#define UART_REG_LCR_H 0x2c /* Line control register */
#define UART_REG_CR 0x30 /* Control register */
#define UART_REG_IMSC 0x38 /* Interrupt mask set/clear register */

#define UART_FR_RXFE (1 << 4) /* Receive FIFO empty */
#define UART_FR_TXFF (1 << 5) /* Transmit FIFO full */

#define UART_LCR_H_FEN (1 << 4) /* Enable FIFOs */
#define UART_LCR_H_WLEN_8 (3 << 5) /* 8 bits word length */

#define UART_CR_UARTEN (1 << 0) /* UART enable */
#define UART_CR_TXE (1 << 8) /* Transmit enable */
#define UART_CR_RXE (1 << 9) /* Receive enable */

/* clang-format on */

static volatile void *uart_base;
static u32 uart_in_freq;
static u32 uart_baudrate;

static u32 get_reg(u32 offset)
{
return readl(uart_base + offset);
}

static void set_reg(u32 offset, u32 val)
{
writel(val, uart_base + offset);
}

static void pl011_uart_putc(char ch)
{
while (get_reg(UART_REG_FR) & UART_FR_TXFF)
;

set_reg(UART_REG_DR, ch);
}

static int pl011_uart_getc(void)
{
if (!(get_reg(UART_REG_FR) & UART_FR_RXFE))
return get_reg(UART_REG_DR);

return -1;
}

static struct sbi_console_device pl011_console = {
.name = "pl011",
.console_putc = pl011_uart_putc,
.console_getc = pl011_uart_getc
};

int pl011_uart_init(unsigned long base, u32 in_freq, u32 baudrate)
{
uart_base = (volatile void *)base;
uart_in_freq = in_freq;
uart_baudrate = baudrate;

/* Disable the UART before configuration */
set_reg(UART_REG_CR, 0);

/* Mask all interrupts */
set_reg(UART_REG_IMSC, 0);

/*
* Program the baud rate divisors only when both the input clock
* frequency and the target baud rate are known. On platforms (such
* as QEMU) that do not expose "clock-frequency", in_freq is 0 and
* the existing divisor configuration is left untouched.
*/
if (in_freq && baudrate) {
u32 div = (in_freq * 4) / baudrate;

set_reg(UART_REG_IBRD, div >> 6);
set_reg(UART_REG_FBRD, div & 0x3f);
}

/* 8 bits word length, enable FIFOs, no parity, 1 stop bit */
set_reg(UART_REG_LCR_H, UART_LCR_H_WLEN_8 | UART_LCR_H_FEN);

/* Enable the UART with both transmit and receive paths */
set_reg(UART_REG_CR, UART_CR_UARTEN | UART_CR_TXE | UART_CR_RXE);

sbi_console_set_device(&pl011_console);

return sbi_domain_root_add_memrange(base, PAGE_SIZE, PAGE_SIZE,
(SBI_DOMAIN_MEMREGION_MMIO |
SBI_DOMAIN_MEMREGION_SHARED_SURW_MRW));
}
1 change: 1 addition & 0 deletions platform/generic/configs/defconfig
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ CONFIG_FDT_SERIAL_SIFIVE=y
CONFIG_FDT_SERIAL_LITEX=y
CONFIG_FDT_SERIAL_UART8250=y
CONFIG_FDT_SERIAL_XILINX_UARTLITE=y
CONFIG_FDT_SERIAL_PL011=y
CONFIG_FDT_SUSPEND=y
CONFIG_FDT_SUSPEND_RPMI=y
CONFIG_FDT_TIMER=y
Expand Down
Loading