From: Wig Cheng <onlywig@gmail.com>
To: xen-devel@lists.xenproject.org
Cc: Stefano Stabellini <sstabellini@kernel.org>,
Julien Grall <julien@xen.org>,
Bertrand Marquis <bertrand.marquis@arm.com>,
Michal Orzel <michal.orzel@amd.com>,
Volodymyr Babchuk <Volodymyr_Babchuk@epam.com>,
John Ernberg <john.ernberg@actia.se>, Peng Fan <peng.fan@nxp.com>,
Wig Cheng <onlywig@gmail.com>
Subject: [PATCH 1/3] xen/char: add classic i.MX UART driver
Date: Sat, 15 Aug 2026 00:25:33 +0800 [thread overview]
Message-ID: <20260814162535.331459-2-onlywig@gmail.com> (raw)
In-Reply-To: <20260814162535.331459-1-onlywig@gmail.com>
Add a console driver for the classic i.MX UART IP ("fsl,imx6q-uart"
compatible), as found on the i.MX6/7/8M families. Baudrate and pin
configuration are inherited from the bootloader; the driver only
enables the transmitter/receiver and wires up the RX/TX interrupts,
mirroring the existing imx-lpuart driver.
This is needed for the i.MX8M family, whose UART IP differs from the
LPUART used on i.MX8QM/8QXP.
Signed-off-by: Wig Cheng <onlywig@gmail.com>
---
xen/arch/arm/include/asm/imx-uart.h | 62 ++++++++
xen/drivers/char/Kconfig | 8 +
xen/drivers/char/Makefile | 1 +
xen/drivers/char/imx-uart.c | 227 ++++++++++++++++++++++++++++
4 files changed, 298 insertions(+)
create mode 100644 xen/arch/arm/include/asm/imx-uart.h
create mode 100644 xen/drivers/char/imx-uart.c
diff --git a/xen/arch/arm/include/asm/imx-uart.h b/xen/arch/arm/include/asm/imx-uart.h
new file mode 100644
index 0000000000..ad4b0b06ff
--- /dev/null
+++ b/xen/arch/arm/include/asm/imx-uart.h
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * xen/arch/arm/include/asm/imx-uart.h
+ *
+ * Register definitions for the classic i.MX UART IP
+ * ("fsl,imx6q-uart" compatible, used on i.MX6/7/8M families).
+ *
+ * Register layout taken from Linux drivers/tty/serial/imx.c.
+ *
+ * Copyright 2026 Open-EP (E-Paper) Community
+ */
+
+#ifndef __ASM_ARM_IMX_UART_H__
+#define __ASM_ARM_IMX_UART_H__
+
+#define URXD0 0x00 /* Receiver Register */
+#define URTX0 0x40 /* Transmitter Register */
+#define UCR1 0x80 /* Control Register 1 */
+#define UCR2 0x84 /* Control Register 2 */
+#define UCR3 0x88 /* Control Register 3 */
+#define UCR4 0x8c /* Control Register 4 */
+#define UFCR 0x90 /* FIFO Control Register */
+#define USR1 0x94 /* Status Register 1 */
+#define USR2 0x98 /* Status Register 2 */
+#define UTS 0xb4 /* Test Register */
+
+#define URXD_CHARRDY (1U << 15)
+#define URXD_RX_DATA 0xff
+
+#define UCR1_UARTEN (1U << 0)
+#define UCR1_RRDYEN (1U << 9) /* Receiver ready interrupt enable */
+#define UCR1_TRDYEN (1U << 13) /* Transmitter ready interrupt enable */
+#define UCR1_RXDMAEN (1U << 8)
+#define UCR1_TXDMAEN (1U << 3)
+#define UCR1_ATDMAEN (1U << 2)
+
+#define UCR2_SRST (1U << 0) /* 0 = issue software reset */
+#define UCR2_RXEN (1U << 1)
+#define UCR2_TXEN (1U << 2)
+
+#define USR1_RRDY (1U << 9) /* Receiver ready */
+#define USR1_TRDY (1U << 13) /* Transmitter ready */
+
+#define USR2_RDR (1U << 0) /* Receive data ready */
+#define USR2_ORE (1U << 1) /* Overrun error */
+#define USR2_TXDC (1U << 3) /* Transmission complete */
+#define USR2_TXFE (1U << 14) /* Transmit FIFO empty */
+
+#define UTS_TXFULL (1U << 4)
+#define UTS_RXEMPTY (1U << 5)
+#define UTS_TXEMPTY (1U << 6)
+
+#endif /* __ASM_ARM_IMX_UART_H__ */
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/xen/drivers/char/Kconfig b/xen/drivers/char/Kconfig
index 8e49a52c73..f237c0220d 100644
--- a/xen/drivers/char/Kconfig
+++ b/xen/drivers/char/Kconfig
@@ -30,6 +30,14 @@ config HAS_IMX_LPUART
help
This selects the i.MX LPUART. If you have i.MX8QM based board, say Y.
+config HAS_IMX_UART
+ bool "i.MX UART driver"
+ default y
+ depends on ARM_64
+ help
+ This selects the classic i.MX UART. If you have an i.MX8M family
+ based board, say Y.
+
config HAS_MVEBU
bool "Marvell MVEBU UART driver"
default y
diff --git a/xen/drivers/char/Makefile b/xen/drivers/char/Makefile
index 8cbbffdca8..039f566926 100644
--- a/xen/drivers/char/Makefile
+++ b/xen/drivers/char/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_HAS_SCIF) += scif-uart.o
obj-$(CONFIG_HAS_EHCI) += ehci-dbgp.o
obj-$(CONFIG_XHCI) += xhci-dbc.o
obj-$(CONFIG_HAS_IMX_LPUART) += imx-lpuart.o
+obj-$(CONFIG_HAS_IMX_UART) += imx-uart.o
obj-$(CONFIG_HAS_LINFLEX) += linflex-uart.o
obj-$(CONFIG_GENERIC_UART_INIT) += uart-init.o
obj-y += serial.o
diff --git a/xen/drivers/char/imx-uart.c b/xen/drivers/char/imx-uart.c
new file mode 100644
index 0000000000..5ae8c13c40
--- /dev/null
+++ b/xen/drivers/char/imx-uart.c
@@ -0,0 +1,227 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * xen/drivers/char/imx-uart.c
+ *
+ * Driver for the classic i.MX UART IP ("fsl,imx6q-uart"), as found on
+ * the i.MX6/7/8M families (e.g. i.MX8MP).
+ *
+ * Baudrate and pin configuration are inherited from the bootloader.
+ *
+ * Copyright 2026 Open-EP (E-Paper) Community
+ */
+
+#include <xen/errno.h>
+#include <xen/init.h>
+#include <xen/irq.h>
+#include <xen/mm.h>
+#include <xen/serial.h>
+#include <asm/device.h>
+#include <asm/imx-uart.h>
+#include <asm/io.h>
+
+#define imx_uart_read(uart, off) readl((uart)->regs + (off))
+#define imx_uart_write(uart, off, val) writel((val), (uart)->regs + (off))
+
+static struct imx_uart {
+ uint32_t irq;
+ char __iomem *regs;
+ struct irqaction irqaction;
+ struct vuart_info vuart;
+} imx8m_com;
+
+static void imx_uart_interrupt(int irq, void *data)
+{
+ struct serial_port *port = data;
+ struct imx_uart *uart = port->uart;
+
+ if ( imx_uart_read(uart, USR2) & USR2_RDR )
+ serial_rx_interrupt(port);
+
+ if ( imx_uart_read(uart, USR1) & USR1_TRDY )
+ serial_tx_interrupt(port);
+}
+
+static void __init imx_uart_init_preirq(struct serial_port *port)
+{
+ struct imx_uart *uart = port->uart;
+ uint32_t ucr1, ucr2;
+
+ /*
+ * Reuse the bootloader baudrate/format settings: only make sure the
+ * UART and both directions are enabled, DMA and interrupts are off.
+ */
+ ucr1 = imx_uart_read(uart, UCR1);
+ ucr1 &= ~(UCR1_RRDYEN | UCR1_TRDYEN | UCR1_RXDMAEN | UCR1_TXDMAEN |
+ UCR1_ATDMAEN);
+ ucr1 |= UCR1_UARTEN;
+ imx_uart_write(uart, UCR1, ucr1);
+
+ ucr2 = imx_uart_read(uart, UCR2);
+ ucr2 |= UCR2_SRST | UCR2_RXEN | UCR2_TXEN;
+ imx_uart_write(uart, UCR2, ucr2);
+}
+
+static void __init imx_uart_init_postirq(struct serial_port *port)
+{
+ struct imx_uart *uart = port->uart;
+ uint32_t ucr1;
+
+ uart->irqaction.handler = imx_uart_interrupt;
+ uart->irqaction.name = "imx_uart";
+ uart->irqaction.dev_id = port;
+
+ if ( setup_irq(uart->irq, 0, &uart->irqaction) != 0 )
+ {
+ dprintk(XENLOG_ERR, "Failed to allocate imx_uart IRQ %d\n", uart->irq);
+ return;
+ }
+
+ /* Enable the receiver ready interrupt */
+ ucr1 = imx_uart_read(uart, UCR1);
+ ucr1 |= UCR1_RRDYEN;
+ imx_uart_write(uart, UCR1, ucr1);
+}
+
+static int imx_uart_tx_ready(struct serial_port *port)
+{
+ struct imx_uart *uart = port->uart;
+
+ return !(imx_uart_read(uart, UTS) & UTS_TXFULL);
+}
+
+static void imx_uart_putc(struct serial_port *port, char c)
+{
+ struct imx_uart *uart = port->uart;
+
+ while ( imx_uart_read(uart, UTS) & UTS_TXFULL )
+ cpu_relax();
+
+ imx_uart_write(uart, URTX0, c);
+}
+
+static int imx_uart_getc(struct serial_port *port, char *pc)
+{
+ struct imx_uart *uart = port->uart;
+
+ if ( !(imx_uart_read(uart, USR2) & USR2_RDR) )
+ return 0;
+
+ *pc = imx_uart_read(uart, URXD0) & URXD_RX_DATA;
+
+ if ( imx_uart_read(uart, USR2) & USR2_ORE )
+ imx_uart_write(uart, USR2, USR2_ORE);
+
+ return 1;
+}
+
+static int __init imx_uart_irq(struct serial_port *port)
+{
+ struct imx_uart *uart = port->uart;
+
+ return ((uart->irq > 0) ? uart->irq : -1);
+}
+
+static const struct vuart_info *imx_uart_vuart_info(struct serial_port *port)
+{
+ struct imx_uart *uart = port->uart;
+
+ return &uart->vuart;
+}
+
+static void imx_uart_start_tx(struct serial_port *port)
+{
+ struct imx_uart *uart = port->uart;
+ uint32_t ucr1;
+
+ ucr1 = imx_uart_read(uart, UCR1);
+ imx_uart_write(uart, UCR1, ucr1 | UCR1_TRDYEN);
+}
+
+static void imx_uart_stop_tx(struct serial_port *port)
+{
+ struct imx_uart *uart = port->uart;
+ uint32_t ucr1;
+
+ ucr1 = imx_uart_read(uart, UCR1);
+ imx_uart_write(uart, UCR1, ucr1 & ~UCR1_TRDYEN);
+}
+
+static struct uart_driver __read_mostly imx_uart_driver = {
+ .init_preirq = imx_uart_init_preirq,
+ .init_postirq = imx_uart_init_postirq,
+ .tx_ready = imx_uart_tx_ready,
+ .putc = imx_uart_putc,
+ .getc = imx_uart_getc,
+ .irq = imx_uart_irq,
+ .start_tx = imx_uart_start_tx,
+ .stop_tx = imx_uart_stop_tx,
+ .vuart_info = imx_uart_vuart_info,
+};
+
+static int __init imx_uart_init(struct dt_device_node *dev, const void *data)
+{
+ const char *config = data;
+ struct imx_uart *uart;
+ int res;
+ paddr_t addr, size;
+
+ if ( strcmp(config, "") )
+ printk("WARNING: UART configuration is not supported\n");
+
+ uart = &imx8m_com;
+
+ res = dt_device_get_paddr(dev, 0, &addr, &size);
+ if ( res )
+ {
+ printk("imx-uart: Unable to retrieve the base address of the UART\n");
+ return res;
+ }
+
+ res = platform_get_irq(dev, 0);
+ if ( res < 0 )
+ {
+ printk("imx-uart: Unable to retrieve the IRQ\n");
+ return -EINVAL;
+ }
+ uart->irq = res;
+
+ uart->regs = ioremap_nocache(addr, size);
+ if ( !uart->regs )
+ {
+ printk("imx-uart: Unable to map the UART memory\n");
+ return -ENOMEM;
+ }
+
+ uart->vuart.base_addr = addr;
+ uart->vuart.size = size;
+ uart->vuart.data_off = URTX0;
+ uart->vuart.status_off = UTS;
+ uart->vuart.status = UTS_TXEMPTY | UTS_RXEMPTY;
+
+ /* Register with generic serial driver */
+ serial_register_uart(SERHND_DTUART, &imx_uart_driver, uart);
+
+ dt_device_set_used_by(dev, DOMID_XEN);
+
+ return 0;
+}
+
+static const struct dt_device_match imx_uart_dt_compat[] __initconst =
+{
+ DT_MATCH_COMPATIBLE("fsl,imx6q-uart"),
+ { /* sentinel */ },
+};
+
+DT_DEVICE_START(imx_uart, "i.MX UART", DEVICE_SERIAL)
+ .dt_match = imx_uart_dt_compat,
+ .init = imx_uart_init,
+DT_DEVICE_END
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--
2.43.0
next prev parent reply other threads:[~2026-08-14 17:04 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-14 16:25 [PATCH 0/3] xen/arm: add i.MX8M platform and UART support Wig Cheng
2026-08-14 16:25 ` Wig Cheng [this message]
2026-08-14 16:25 ` [PATCH 2/3] xen/arm64: add early printk for the classic i.MX UART Wig Cheng
2026-08-14 16:25 ` [PATCH 3/3] xen/arm: add i.MX8M platform support Wig Cheng
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260814162535.331459-2-onlywig@gmail.com \
--to=onlywig@gmail.com \
--cc=Volodymyr_Babchuk@epam.com \
--cc=bertrand.marquis@arm.com \
--cc=john.ernberg@actia.se \
--cc=julien@xen.org \
--cc=michal.orzel@amd.com \
--cc=peng.fan@nxp.com \
--cc=sstabellini@kernel.org \
--cc=xen-devel@lists.xenproject.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.