[PATCH] DRIVERS: xlnx-uartlite: Add support for Xilinx AXI UART Lite

33 views
Skip to first unread message

Xu, Zefan

unread,
May 11, 2024, 1:51:15 AM5/11/24
to xvisor...@googlegroups.com, Xu, Zefan
This patch add support for Xilinx AXI UART Lite, which is widely used by soft risc-v cores on FPGA.
---
arch/common/generic_defterm.c | 52 +++++
arch/common/generic_defterm_early.c | 15 ++
arch/common/openconf.cfg | 5 +
drivers/include/drv/serial/xlnx-uartlite.h | 58 ++++++
drivers/serial/objects.mk | 1 +
drivers/serial/openconf.cfg | 7 +
drivers/serial/xlnx-uartlite.c | 227 +++++++++++++++++++++
7 files changed, 365 insertions(+)
create mode 100644 drivers/include/drv/serial/xlnx-uartlite.h
create mode 100644 drivers/serial/xlnx-uartlite.c

diff --git a/arch/common/generic_defterm.c b/arch/common/generic_defterm.c
index 38b50d0b..606b34d1 100644
--- a/arch/common/generic_defterm.c
+++ b/arch/common/generic_defterm.c
@@ -628,6 +628,56 @@ static struct defterm_ops zynq_uart_ops = {

#endif

+#if defined(CONFIG_SERIAL_XLNX_UARTLITE)
+
+#include <drv/serial/xlnx-uartlite.h>
+
+struct xlnx_uartlite_priv uart_port;
+
+static int xlnx_uartlite_defterm_getc(u8 *ch)
+{
+ if (!xlnx_uartlite_lowlevel_can_getc(uart_port.regs)) {
+ return VMM_EFAIL;
+ }
+ *ch = xlnx_uartlite_lowlevel_getc(uart_port.regs);
+ return VMM_OK;
+}
+
+static int xlnx_uartlite_defterm_putc(u8 ch)
+{
+ if (!xlnx_uartlite_lowlevel_can_putc(uart_port.regs)) {
+ return VMM_EFAIL;
+ }
+
+ xlnx_uartlite_lowlevel_putc(uart_port.regs, ch);
+ return VMM_OK;
+}
+
+static int __init xlnx_uartlite_defterm_init(struct vmm_devtree_node *node)
+{
+ int rc;
+
+ rc = vmm_devtree_regmap(node, (virtual_addr_t*)&uart_port.regs, 0);
+ if (rc) {
+ return rc;
+ }
+
+ xlnx_uartlite_lowlevel_init(&uart_port);
+
+ return VMM_OK;
+}
+
+static struct defterm_ops xlnx_uartlite_ops = {
+ .putc = xlnx_uartlite_defterm_putc,
+ .getc = xlnx_uartlite_defterm_getc,
+ .init = xlnx_uartlite_defterm_init
+};
+#else
+
+#define xlnx_uartlite_ops unknown_ops
+
+#endif
+
static struct vmm_devtree_nodeid defterm_devid_table[] = {
{ .compatible = "arm,pl011", .data = &pl011_ops },
{ .compatible = "ns8250", .data = &uart8250_ops },
@@ -649,6 +699,8 @@ static struct vmm_devtree_nodeid defterm_devid_table[] = {
{ .compatible = "brcm,bcm283x-mu", .data = &bcm283x_mu_ops },
{ .compatible = "cdns,uart-r1p12", .data = &zynq_uart_ops },
{ .compatible = "xlnx,xuartps", .data = &zynq_uart_ops },
+ { .compatible = "xlnx,opb-uartlite-1.00.b", .data = &xlnx_uartlite_ops },
+ { .compatible = "xlnx,xps-uartlite-1.00.a", .data = &xlnx_uartlite_ops },
{ /* end of list */ },
};

diff --git a/arch/common/generic_defterm_early.c b/arch/common/generic_defterm_early.c
index e201db12..4417bf7b 100644
--- a/arch/common/generic_defterm_early.c
+++ b/arch/common/generic_defterm_early.c
@@ -177,6 +177,21 @@ void __init arch_defterm_early_putc(u8 ch)
vmm_writeb(ch, (void *)&reg->tx_rx_fifo);
}

+#elif defined(CONFIG_ARCH_GENERIC_DEFTERM_EARLY_XLNX_UARTLITE)
+
+#include <drv/serial/xlnx-uartlite.h>
+
+void __init arch_defterm_early_putc(u8 ch)
+{
+ struct xlnx_uartlite *reg = (struct xlnx_uartlite *)early_base;
+
+ /* Wait until FIFO is not full */
+ while (vmm_readl(&reg->stat_reg) & UARTLITE_STAT_TX_FIFO_FULL) ;
+
+ /* Send the character */
+ vmm_writel(ch, (void *)&reg->tx_fifo);
+}
+
#else

void __init arch_defterm_early_putc(u8 ch)
diff --git a/arch/common/openconf.cfg b/arch/common/openconf.cfg
index 39fe0065..2b7f7be8 100644
--- a/arch/common/openconf.cfg
+++ b/arch/common/openconf.cfg
@@ -100,6 +100,11 @@ choice
bool "zynq-uart"
help
Zynq uart based early prints
+
+ config CONFIG_ARCH_GENERIC_DEFTERM_EARLY_XLNX_UARTLITE
+ bool "xlnx-uartlite"
+ help
+ Xlinx uartlite based early prints
endchoice

config CONFIG_ARCH_GENERIC_DEFTERM_EARLY_BASE_PA
diff --git a/drivers/include/drv/serial/xlnx-uartlite.h b/drivers/include/drv/serial/xlnx-uartlite.h
new file mode 100644
index 00000000..96558965
--- /dev/null
+++ b/drivers/include/drv/serial/xlnx-uartlite.h
@@ -0,0 +1,58 @@
+/**
+ * Copyright (c) 2024 Xu, Zefan.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * @file xlnx-uartlite.h
+ * @author Xu, Zefan (ceba_...@outlook.com)
+ * @brief Header file for xlinx uartlite driver.
+ */
+#ifndef __XLNX_UARTLITE_H__
+#define __XLNX_UARTLITE_H__
+
+#include <vmm_types.h>
+
+#define UARTLITE_STAT_INTR_ENABLED (1 << 4) /* interrupts is enabled */
+#define UARTLITE_STAT_TX_FIFO_FULL (1 << 3) /* transmit FIFO is full */
+#define UARTLITE_STAT_TX_FIFO_EMPTY (1 << 2) /* transmit FIFO is empty */
+#define UARTLITE_STAT_RX_FIFO_FULL (1 << 1) /* receive FIFO is full */
+#define UARTLITE_STAT_RX_FIFO_VALID_DATA (1 << 0) /* receive FIFO has data */
+
+#define UARTLITE_CTRL_ENABLE_INTR (1 << 4) /* Enable interrupt */
+#define UARTLITE_CTRL_RST_RX_FIFO (1 << 1) /* Reset/clear the receive FIFO */
+#define UARTLITE_CTRL_RST_TX_FIFO (1 << 0) /* Reset/clear the transmit FIFO */
+
+struct xlnx_uartlite {
+ u32 rx_fifo;
+ u32 tx_fifo;
+ u32 stat_reg;
+ u32 ctrl_reg;
+};
+
+struct xlnx_uartlite_priv {
+ struct serial *p;
+ struct xlnx_uartlite *regs;
+ u32 input_clock;
+ u32 irq;
+};
+
+bool xlnx_uartlite_lowlevel_can_getc(struct xlnx_uartlite *regs);
+u8 xlnx_uartlite_lowlevel_getc(struct xlnx_uartlite *regs);
+bool xlnx_uartlite_lowlevel_can_putc(struct xlnx_uartlite *reg);
+void xlnx_uartlite_lowlevel_putc(struct xlnx_uartlite *reg, u8 ch);
+void xlnx_uartlite_lowlevel_init(struct xlnx_uartlite_priv *port);
+
+#endif /* __XLNX_UARTLITE_H__ */
diff --git a/drivers/serial/objects.mk b/drivers/serial/objects.mk
index f1e8cd94..6fcc6c2b 100644
--- a/drivers/serial/objects.mk
+++ b/drivers/serial/objects.mk
@@ -30,3 +30,4 @@ drivers-objs-$(CONFIG_SERIAL_IMX)+= serial/imx-uart.o
drivers-objs-$(CONFIG_SERIAL_SCIF)+= serial/scif.o
drivers-objs-$(CONFIG_SERIAL_BCM283X_MU)+= serial/bcm283x_mu.o
drivers-objs-$(CONFIG_SERIAL_ZYNQ_UART)+= serial/zynq-uart.o
+drivers-objs-$(CONFIG_SERIAL_XLNX_UARTLITE)+= serial/xlnx-uartlite.o
diff --git a/drivers/serial/openconf.cfg b/drivers/serial/openconf.cfg
index 9b586c83..bbd77376 100644
--- a/drivers/serial/openconf.cfg
+++ b/drivers/serial/openconf.cfg
@@ -86,4 +86,11 @@ config CONFIG_SERIAL_ZYNQ_UART
help
Zynq/ZynqMP Serial Port.

+config CONFIG_SERIAL_XLNX_UARTLITE
+ bool "Xilinx UART Lite"
+ depends on CONFIG_SERIAL
+ default n
+ help
+ Xilinx AXI UART Lite Serial Port.
+
endmenu
diff --git a/drivers/serial/xlnx-uartlite.c b/drivers/serial/xlnx-uartlite.c
new file mode 100644
index 00000000..bc17a72e
--- /dev/null
+++ b/drivers/serial/xlnx-uartlite.c
@@ -0,0 +1,227 @@
+/**
+ * Copyright (c) 2024 Xu, Zefan.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * @file xlnx-uartlite.c
+ * @author Xu, Zefan (ceba_...@outlook.com)
+ * @brief source file for xlinx uartlite driver.
+ */
+
+#include <vmm_error.h>
+#include <vmm_heap.h>
+#include <vmm_host_io.h>
+#include <vmm_host_irq.h>
+#include <vmm_modules.h>
+#include <vmm_devtree.h>
+#include <vmm_devdrv.h>
+#include <libs/stringlib.h>
+#include <libs/mathlib.h>
+#include <drv/serial.h>
+#include <drv/serial/xlnx-uartlite.h>
+
+#define MODULE_DESC "Xlinx uartlite driver"
+#define MODULE_AUTHOR "Xu, Zefan"
+#define MODULE_LICENSE "GPL"
+#define MODULE_IPRIORITY (SERIAL_IPRIORITY+1)
+#define MODULE_INIT xlnx_uartlite_driver_init
+#define MODULE_EXIT xlnx_uartlite_driver_exit
+
+bool xlnx_uartlite_lowlevel_can_getc(struct xlnx_uartlite *regs)
+{
+ if (vmm_readl((void*)&regs->stat_reg) & UARTLITE_STAT_RX_FIFO_VALID_DATA)
+ return TRUE;
+
+ return FALSE;
+}
+
+u8 xlnx_uartlite_lowlevel_getc(struct xlnx_uartlite *regs)
+{
+ /* Wait until there is data in the FIFO */
+ while (!xlnx_uartlite_lowlevel_can_getc(regs)) ;
+
+ /* Read IO register */
+ return (char)vmm_readl((void *)(&regs->rx_fifo));
+}
+
+bool xlnx_uartlite_lowlevel_can_putc(struct xlnx_uartlite *reg)
+{
+ if (vmm_readl(&reg->stat_reg) & UARTLITE_STAT_TX_FIFO_FULL)
+ return FALSE;
+
+ return TRUE;
+}
+
+void xlnx_uartlite_lowlevel_putc(struct xlnx_uartlite *reg, u8 ch)
+{
+ /* Wait until tx FIFO is not full */
+ while (!xlnx_uartlite_lowlevel_can_putc(reg)) ;
+
+ /* Send the character */
+ vmm_writel(ch, (void *)&reg->tx_fifo);
+}
+
+static u32 xlnx_uartlite_tx(struct serial *p, u8 *src, size_t len)
+{
+ u32 i;
+ struct xlnx_uartlite_priv *port = serial_tx_priv(p);
+
+ for (i = 0; i < len; i++) {
+ if (!xlnx_uartlite_lowlevel_can_putc(port->regs)) {
+ break;
+ }
+ xlnx_uartlite_lowlevel_putc(port->regs, src[i]);
+ }
+
+ return i;
+}
+
+void xlnx_uartlite_lowlevel_init(struct xlnx_uartlite_priv *port)
+{
+ struct xlnx_uartlite *regs = port->regs;
+
+ /* RX/TX reset, disable interrupt */
+ vmm_writel(
+ UARTLITE_CTRL_RST_RX_FIFO | \
+ UARTLITE_CTRL_RST_TX_FIFO,
+ &regs->ctrl_reg
+ );
+}
+
+static vmm_irq_return_t xlnx_uartlite_irq_handler(int irq_no, void *pdev)
+{
+ u8 ch;
+ struct xlnx_uartlite_priv *port = (struct xlnx_uartlite_priv *)pdev;
+ struct xlnx_uartlite *regs = port->regs;
+
+ /* Handle RX interrupt */
+ while (xlnx_uartlite_lowlevel_can_getc(regs)) {
+ ch = xlnx_uartlite_lowlevel_getc(regs);
+ serial_rx(port->p, &ch, 1);
+ }
+
+ return VMM_IRQ_HANDLED;
+}
+
+static int xlnx_uartlite_driver_probe(struct vmm_device *dev)
+{
+ int rc;
+ struct xlnx_uartlite_priv *port;
+
+ port = vmm_zalloc(sizeof(struct xlnx_uartlite_priv));
+ if (!port) {
+ rc = VMM_ENOMEM;
+ goto free_nothing;
+ }
+
+ rc = vmm_devtree_request_regmap(dev->of_node,
+ (virtual_addr_t*)&port->regs,
+ 0, "Xilinx uartlite");
+ if (rc) {
+ goto free_port;
+ }
+
+ port->irq = vmm_devtree_irq_parse_map(dev->of_node, 0);
+ if (!port->irq) {
+ rc = VMM_ENODEV;
+ goto free_reg;
+ }
+ if ((rc = vmm_host_irq_register(port->irq, dev->name,
+ xlnx_uartlite_irq_handler, port))) {
+ goto free_reg;
+ }
+
+ /* Call low-level init function */
+ xlnx_uartlite_lowlevel_init(port);
+
+ /* Create Serial Port */
+ port->p = serial_create(dev, 256, xlnx_uartlite_tx, port);
+ if (VMM_IS_ERR_OR_NULL(port->p)) {
+ rc = VMM_PTR_ERR(port->p);
+ goto free_irq;
+ }
+
+ /* Save port pointer */
+ dev->priv = port;
+
+ /* Enable Interrupt */
+ // vmm_writel(UARTLITE_CTRL_ENABLE_INTR, &port->regs->ctrl_reg);
+
+ return VMM_OK;
+
+free_irq:
+ vmm_host_irq_unregister(port->irq, port);
+free_reg:
+ vmm_devtree_regunmap_release(dev->of_node,
+ (virtual_addr_t)port->regs, 0);
+free_port:
+ vmm_free(port);
+free_nothing:
+ return rc;
+}
+
+static int xlnx_uartlite_driver_remove(struct vmm_device *dev)
+{
+ struct xlnx_uartlite_priv *port = dev->priv;
+
+ if (!port) {
+ return VMM_OK;
+ }
+
+ /* Disable interrupts */
+ // vmm_writel(0, &port->regs->ctrl_reg);
+
+ /* Free-up resources */
+ serial_destroy(port->p);
+ vmm_host_irq_unregister(port->irq, port);
+ vmm_devtree_regunmap_release(dev->of_node,
+ (virtual_addr_t)port->regs, 0);
+ vmm_free(port);
+ dev->priv = NULL;
+
+ return VMM_OK;
+}
+
+static struct vmm_devtree_nodeid xlnx_uartlite_devid_table[] = {
+ { .compatible = "xilinx,uartlite" },
+ { .compatible = "xlnx,opb-uartlite-1.00.b" },
+ { .compatible = "xlnx,xps-uartlite-1.00.a" },
+ { /* end of list */ },
+};
+
+static struct vmm_driver xlnx_uartlite_driver = {
+ .name = "xlnx_uartlite",
+ .match_table = xlnx_uartlite_devid_table,
+ .probe = xlnx_uartlite_driver_probe,
+ .remove = xlnx_uartlite_driver_remove,
+};
+
+static int __init xlnx_uartlite_driver_init(void)
+{
+ return vmm_devdrv_register_driver(&xlnx_uartlite_driver);
+}
+
+static void __exit xlnx_uartlite_driver_exit(void)
+{
+ vmm_devdrv_unregister_driver(&xlnx_uartlite_driver);
+}
+
+VMM_DECLARE_MODULE(MODULE_DESC,
+ MODULE_AUTHOR,
+ MODULE_LICENSE,
+ MODULE_IPRIORITY,
+ MODULE_INIT,
+ MODULE_EXIT);
--
2.43.0

Xu, Zefan

unread,
May 11, 2024, 1:51:15 AM5/11/24
to xvisor...@googlegroups.com, Xu, Zefan
From: "Xu, Zefan" <ceba_...@outlook.com>

Anup Patel

unread,
May 13, 2024, 1:10:44 AM5/13/24
to xvisor...@googlegroups.com, Xu, Zefan
On Sat, May 11, 2024 at 11:21 AM Xu, Zefan <ceba_...@outlook.com> wrote:
>
> This patch add support for Xilinx AXI UART Lite, which is widely used by soft risc-v cores on FPGA.

Reviewed-by: Anup Patel <an...@brainfault.org>

Applied this patch to the xvisor-next repo.

Thanks,
Anup
> --
> You received this message because you are subscribed to the Google Groups "Xvisor Development" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to xvisor-devel...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/xvisor-devel/SY8P282MB47328152C1D3C3F7BA9F6463E2E02%40SY8P282MB4732.AUSP282.PROD.OUTLOOK.COM.
Reply all
Reply to author
Forward
0 new messages