[PATCH 14/25] serial: Add lpuart32 driver

Sascha Hauer s.hauer at pengutronix.de
Fri Nov 10 04:57:49 PST 2023


This adds a lpuart32 driver. This is a variant of the lpuart driver
that is found on i.MX9 and other SoCs.

Signed-off-by: Sascha Hauer <s.hauer at pengutronix.de>
---
 drivers/serial/Kconfig           |   4 +
 drivers/serial/Makefile          |   1 +
 drivers/serial/serial_lpuart32.c | 185 +++++++++++++++++++++++++++++++
 include/serial/lpuart32.h        | 158 ++++++++++++++++++++++++++
 4 files changed, 348 insertions(+)
 create mode 100644 drivers/serial/serial_lpuart32.c
 create mode 100644 include/serial/lpuart32.h

diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index 77c827e436..803f6b6aee 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -122,6 +122,10 @@ config DRIVER_SERIAL_LPUART
 	default y
 	bool "LPUART serial driver"
 
+config DRIVER_SERIAL_LPUART32
+	depends on ARCH_IMX
+	bool "LPUART32 serial driver"
+
 config VIRTIO_CONSOLE
 	tristate "Virtio console"
 	depends on VIRTIO
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile
index bbc517f521..4887e24ee1 100644
--- a/drivers/serial/Makefile
+++ b/drivers/serial/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_DRIVER_SERIAL_CADENCE)		+= serial_cadence.o
 obj-$(CONFIG_DRIVER_SERIAL_EFI_STDIO)		+= efi-stdio.o
 obj-$(CONFIG_DRIVER_SERIAL_DIGIC)		+= serial_digic.o
 obj-$(CONFIG_DRIVER_SERIAL_LPUART)		+= serial_lpuart.o
+obj-$(CONFIG_DRIVER_SERIAL_LPUART32)		+= serial_lpuart32.o
 obj-$(CONFIG_VIRTIO_CONSOLE)			+= virtio_console.o
 obj-$(CONFIG_SERIAL_SIFIVE)			+= serial_sifive.o
 obj-$(CONFIG_SERIAL_SBI)			+= serial_sbi.o
diff --git a/drivers/serial/serial_lpuart32.c b/drivers/serial/serial_lpuart32.c
new file mode 100644
index 0000000000..0f3e7c7a04
--- /dev/null
+++ b/drivers/serial/serial_lpuart32.c
@@ -0,0 +1,185 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2023 Pengutronix
+ */
+
+#include <common.h>
+#include <driver.h>
+#include <init.h>
+#include <malloc.h>
+#include <notifier.h>
+#include <io.h>
+#include <of.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <serial/lpuart32.h>
+
+struct lpuart32_devtype_data {
+	unsigned int reg_offs;
+};
+
+struct lpuart32 {
+	struct console_device cdev;
+	int baudrate;
+	int dte_mode;
+	struct notifier_block notify;
+	struct resource *io;
+	void __iomem *base;
+	struct clk *clk;
+};
+
+static struct lpuart32 *cdev_to_lpuart32(struct console_device *cdev)
+{
+	return container_of(cdev, struct lpuart32, cdev);
+}
+
+static void lpuart32_enable(struct lpuart32 *lpuart32)
+{
+	writel(LPUART32_UARTCTRL_TE | LPUART32_UARTCTRL_RE,
+	       lpuart32->base + LPUART32_UARTCTRL);
+}
+
+static void lpuart32_disable(struct lpuart32 *lpuart32)
+{
+	writel(0, lpuart32->base + LPUART32_UARTCTRL);
+}
+
+static int lpuart32_serial_setbaudrate(struct console_device *cdev,
+				     int baudrate)
+{
+	struct lpuart32 *lpuart32 = cdev_to_lpuart32(cdev);
+
+	lpuart32_disable(lpuart32);
+
+	/*
+	 * We treat baudrate of 0 as a request to disable UART
+	 */
+	if (baudrate) {
+		lpuart32_setbrg(lpuart32->base, clk_get_rate(lpuart32->clk),
+			      baudrate);
+		lpuart32_enable(lpuart32);
+	}
+
+	lpuart32->baudrate = baudrate;
+
+	return 0;
+}
+
+static int lpuart32_serial_getc(struct console_device *cdev)
+{
+	struct lpuart32 *lpuart32 = cdev_to_lpuart32(cdev);
+
+	while (!(readl(lpuart32->base + LPUART32_UARTSTAT) & LPUART32_UARTSTAT_RDRF));
+
+	return readl(lpuart32->base + LPUART32_UARTDATA) & 0xff;
+}
+
+static void lpuart32_serial_putc(struct console_device *cdev, char c)
+{
+	struct lpuart32 *lpuart32 = cdev_to_lpuart32(cdev);
+
+	lpuart32_putc(lpuart32->base, c);
+}
+
+/* Test whether a character is in the RX buffer */
+static int lpuart32_serial_tstc(struct console_device *cdev)
+{
+	struct lpuart32 *lpuart32 = cdev_to_lpuart32(cdev);
+
+	return readl(lpuart32->base + LPUART32_UARTSTAT) & LPUART32_UARTSTAT_RDRF;
+}
+
+static void lpuart32_serial_flush(struct console_device *cdev)
+{
+}
+
+static int lpuart32_serial_probe(struct device *dev)
+{
+	int ret;
+	struct console_device *cdev;
+	struct lpuart32 *lpuart32;
+	const char *devname;
+	struct lpuart32_devtype_data *devtype;
+
+	ret = dev_get_drvdata(dev, (const void **)&devtype);
+	if (ret)
+		return ret;
+
+	lpuart32 = xzalloc(sizeof(*lpuart32));
+	cdev = &lpuart32->cdev;
+	dev->priv = lpuart32;
+
+	lpuart32->io = dev_request_mem_resource(dev, 0);
+	if (IS_ERR(lpuart32->io)) {
+		ret = PTR_ERR(lpuart32->io);
+		goto err_free;
+	}
+	lpuart32->base = IOMEM(lpuart32->io->start) + devtype->reg_offs;
+
+	lpuart32->clk = clk_get(dev, NULL);
+	if (IS_ERR(lpuart32->clk)) {
+		ret = PTR_ERR(lpuart32->clk);
+		dev_err(dev, "Failed to get UART clock %d\n", ret);
+		goto io_release;
+	}
+
+	ret = clk_enable(lpuart32->clk);
+	if (ret) {
+		dev_err(dev, "Failed to enable UART clock %d\n", ret);
+		goto io_release;
+	}
+
+	cdev->dev    = dev;
+	cdev->tstc   = lpuart32_serial_tstc;
+	cdev->putc   = lpuart32_serial_putc;
+	cdev->getc   = lpuart32_serial_getc;
+	cdev->flush  = lpuart32_serial_flush;
+	cdev->setbrg = lpuart32_serial_setbaudrate;
+
+	if (dev->of_node) {
+		devname = of_alias_get(dev->of_node);
+		if (devname) {
+			cdev->devname = xstrdup(devname);
+			cdev->devid   = DEVICE_ID_SINGLE;
+		}
+	}
+
+	cdev->linux_console_name = "ttyLP";
+	cdev->linux_earlycon_name = "lpuart";
+	cdev->phys_base = lpuart32->base;
+
+	lpuart32_setup(lpuart32->base, clk_get_rate(lpuart32->clk));
+
+	ret = console_register(cdev);
+	if (!ret)
+		return 0;
+
+	clk_put(lpuart32->clk);
+io_release:
+	release_region(lpuart32->io);
+err_free:
+	free(lpuart32);
+
+	return ret;
+}
+
+static struct lpuart32_devtype_data imx7ulp_data = {
+	.reg_offs = 0x10,
+};
+
+static struct of_device_id lpuart32_serial_dt_ids[] = {
+	{
+		.compatible = "fsl,imx7ulp-lpuart",
+		.data = &imx7ulp_data,
+	}, {
+		/* sentinel */
+	}
+};
+MODULE_DEVICE_TABLE(of, lpuart32_serial_dt_ids);
+
+static struct driver lpuart32_serial_driver = {
+	.name   = "lpuart32-serial",
+	.probe  = lpuart32_serial_probe,
+	.of_compatible = DRV_OF_COMPAT(lpuart32_serial_dt_ids),
+};
+console_platform_driver(lpuart32_serial_driver);
diff --git a/include/serial/lpuart32.h b/include/serial/lpuart32.h
new file mode 100644
index 0000000000..bcfd067113
--- /dev/null
+++ b/include/serial/lpuart32.h
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2016 Zodiac Inflight Innovation
+ * Author: Andrey Smirnov <andrew.smirnov at gmail.com>
+ *
+ * Based on code found in Linux kernel and U-Boot.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __lpuart32_H__
+#define __lpuart32_H__
+
+
+/* 32-bit register definition */
+#define LPUART32_UARTBAUD		0x00
+#define LPUART32_UARTSTAT		0x04
+#define LPUART32_UARTCTRL		0x08
+#define LPUART32_UARTDATA		0x0C
+#define LPUART32_UARTMATCH		0x10
+#define LPUART32_UARTMODIR		0x14
+#define LPUART32_UARTFIFO		0x18
+#define LPUART32_UARTWATER		0x1c
+
+#define LPUART32_UARTBAUD_MAEN1		0x80000000
+#define LPUART32_UARTBAUD_MAEN2		0x40000000
+#define LPUART32_UARTBAUD_M10		0x20000000
+#define LPUART32_UARTBAUD_TDMAE		0x00800000
+#define LPUART32_UARTBAUD_RDMAE		0x00200000
+#define LPUART32_UARTBAUD_MATCFG	0x00400000
+#define LPUART32_UARTBAUD_BOTHEDGE	0x00020000
+#define LPUART32_UARTBAUD_RESYNCDIS	0x00010000
+#define LPUART32_UARTBAUD_LBKDIE	0x00008000
+#define LPUART32_UARTBAUD_RXEDGIE	0x00004000
+#define LPUART32_UARTBAUD_SBNS		0x00002000
+#define LPUART32_UARTBAUD_SBR		0x00000000
+#define LPUART32_UARTBAUD_SBR_MASK	0x1fff
+#define LPUART32_UARTBAUD_OSR_MASK	0x1f
+#define LPUART32_UARTBAUD_OSR_SHIFT	24
+
+#define LPUART32_UARTSTAT_LBKDIF	0x80000000
+#define LPUART32_UARTSTAT_RXEDGIF	0x40000000
+#define LPUART32_UARTSTAT_MSBF		0x20000000
+#define LPUART32_UARTSTAT_RXINV		0x10000000
+#define LPUART32_UARTSTAT_RWUID		0x08000000
+#define LPUART32_UARTSTAT_BRK13		0x04000000
+#define LPUART32_UARTSTAT_LBKDE		0x02000000
+#define LPUART32_UARTSTAT_RAF		0x01000000
+#define LPUART32_UARTSTAT_TDRE		0x00800000
+#define LPUART32_UARTSTAT_TC		0x00400000
+#define LPUART32_UARTSTAT_RDRF		0x00200000
+#define LPUART32_UARTSTAT_IDLE		0x00100000
+#define LPUART32_UARTSTAT_OR		0x00080000
+#define LPUART32_UARTSTAT_NF		0x00040000
+#define LPUART32_UARTSTAT_FE		0x00020000
+#define LPUART32_UARTSTAT_PE		0x00010000
+#define LPUART32_UARTSTAT_MA1F		0x00008000
+#define LPUART32_UARTSTAT_M21F		0x00004000
+
+#define LPUART32_UARTCTRL_R8T9		0x80000000
+#define LPUART32_UARTCTRL_R9T8		0x40000000
+#define LPUART32_UARTCTRL_TXDIR		0x20000000
+#define LPUART32_UARTCTRL_TXINV		0x10000000
+#define LPUART32_UARTCTRL_ORIE		0x08000000
+#define LPUART32_UARTCTRL_NEIE		0x04000000
+#define LPUART32_UARTCTRL_FEIE		0x02000000
+#define LPUART32_UARTCTRL_PEIE		0x01000000
+#define LPUART32_UARTCTRL_TIE		0x00800000
+#define LPUART32_UARTCTRL_TCIE		0x00400000
+#define LPUART32_UARTCTRL_RIE		0x00200000
+#define LPUART32_UARTCTRL_ILIE		0x00100000
+#define LPUART32_UARTCTRL_TE		0x00080000
+#define LPUART32_UARTCTRL_RE		0x00040000
+#define LPUART32_UARTCTRL_RWU		0x00020000
+#define LPUART32_UARTCTRL_SBK		0x00010000
+#define LPUART32_UARTCTRL_MA1IE		0x00008000
+#define LPUART32_UARTCTRL_MA2IE		0x00004000
+#define LPUART32_UARTCTRL_IDLECFG	GENMASK(10, 8)
+#define LPUART32_UARTCTRL_LOOPS		0x00000080
+#define LPUART32_UARTCTRL_DOZEEN	0x00000040
+#define LPUART32_UARTCTRL_RSRC		0x00000020
+#define LPUART32_UARTCTRL_M		0x00000010
+#define LPUART32_UARTCTRL_WAKE		0x00000008
+#define LPUART32_UARTCTRL_ILT		0x00000004
+#define LPUART32_UARTCTRL_PE		0x00000002
+#define LPUART32_UARTCTRL_PT		0x00000001
+
+#define LPUART32_UARTDATA_NOISY		0x00008000
+#define LPUART32_UARTDATA_PARITYE	0x00004000
+#define LPUART32_UARTDATA_FRETSC	0x00002000
+#define LPUART32_UARTDATA_RXEMPT	0x00001000
+#define LPUART32_UARTDATA_IDLINE	0x00000800
+#define LPUART32_UARTDATA_MASK		0x3ff
+
+#define LPUART32_UARTMODIR_IREN		0x00020000
+#define LPUART32_UARTMODIR_RTSWATER	GENMASK(10, 8)
+#define LPUART32_UARTMODIR_TXCTSSRC	0x00000020
+#define LPUART32_UARTMODIR_TXCTSC	0x00000010
+#define LPUART32_UARTMODIR_RXRTSE	0x00000008
+#define LPUART32_UARTMODIR_TXRTSPOL	0x00000004
+#define LPUART32_UARTMODIR_TXRTSE	0x00000002
+#define LPUART32_UARTMODIR_TXCTSE	0x00000001
+
+#define LPUART32_UARTFIFO_TXEMPT	0x00800000
+#define LPUART32_UARTFIFO_RXEMPT	0x00400000
+#define LPUART32_UARTFIFO_TXOF		0x00020000
+#define LPUART32_UARTFIFO_RXUF		0x00010000
+#define LPUART32_UARTFIFO_TXFLUSH	0x00008000
+#define LPUART32_UARTFIFO_RXFLUSH	0x00004000
+#define LPUART32_UARTFIFO_RXIDEN	GENMASK(12, 10)
+#define LPUART32_UARTFIFO_TXOFE		0x00000200
+#define LPUART32_UARTFIFO_RXUFE		0x00000100
+#define LPUART32_UARTFIFO_TXFE		0x00000080
+#define LPUART32_UARTFIFO_FIFOSIZE_MASK	0x7
+#define LPUART32_UARTFIFO_TXSIZE_OFF	4
+#define LPUART32_UARTFIFO_RXFE		0x00000008
+#define LPUART32_UARTFIFO_RXSIZE_OFF	0
+#define LPUART32_UARTFIFO_DEPTH(x)	(0x1 << ((x) ? ((x) + 1) : 0))
+
+#define LPUART32_UARTWATER_COUNT_MASK	0xff
+#define LPUART32_UARTWATER_TXCNT_OFF	8
+#define LPUART32_UARTWATER_RXCNT_OFF	24
+#define LPUART32_UARTWATER_WATER_MASK	0xff
+#define LPUART32_UARTWATER_TXWATER_OFF	0
+#define LPUART32_UARTWATER_RXWATER_OFF	16
+
+static inline void lpuart32_setbrg(void __iomem *base,
+				 unsigned int refclock,
+				 unsigned int baudrate)
+{
+	u32 sbr;
+
+	sbr = (refclock / (16 * baudrate));
+	writel(sbr, base + LPUART32_UARTBAUD);
+}
+
+static inline void lpuart32_setup(void __iomem *base,
+				unsigned int refclock)
+{
+	lpuart32_setbrg(base, refclock, CONFIG_BAUDRATE);
+	writel(LPUART32_UARTCTRL_TE | LPUART32_UARTCTRL_RE, base + LPUART32_UARTCTRL);
+}
+
+static inline void lpuart32_putc(void __iomem *base, int c)
+{
+	while (!(readl(base + LPUART32_UARTSTAT) & LPUART32_UARTSTAT_TDRE));
+
+	writel(c, base + LPUART32_UARTDATA);
+}
+
+#endif
-- 
2.39.2




More information about the barebox mailing list