[U-Boot] [PATCH 14/18] serial: Add support for i.MX7ULP

Peng Fan peng.fan at nxp.com
Mon Dec 26 08:00:13 CET 2016


Add lpuart driver to support i.MX7ULP.
Need to enable CONFIG_IMX_LPUART for the using.

Also add the lpuart_fsl register structure and registers bits definitions
in registers header file.

Signed-off-by: Peng Fan <peng.fan at nxp.com>
Cc: Stefano Babic <sbabic at denx.de>
---
 arch/arm/include/asm/arch-mx7ulp/imx-regs.h |  41 +++++
 drivers/serial/Kconfig                      |   5 +
 drivers/serial/Makefile                     |   1 +
 drivers/serial/serial_imxlpuart.c           | 261 ++++++++++++++++++++++++++++
 4 files changed, 308 insertions(+)
 create mode 100644 drivers/serial/serial_imxlpuart.c

diff --git a/arch/arm/include/asm/arch-mx7ulp/imx-regs.h b/arch/arm/include/asm/arch-mx7ulp/imx-regs.h
index 30d0ba4..2b53435 100644
--- a/arch/arm/include/asm/arch-mx7ulp/imx-regs.h
+++ b/arch/arm/include/asm/arch-mx7ulp/imx-regs.h
@@ -1116,6 +1116,47 @@ struct usbphy_regs {
 	u32	usb1_pfda_ctrl1_tog;		/* 0x14c */
 };
 
+struct lpuart_fsl {
+	u32 verid;
+	u32 param;
+	u32 global;
+	u32 pincfg;
+	u32 baud;
+	u32 stat;
+	u32 ctrl;
+	u32 data;
+	u32 match;
+	u32 modir;
+	u32 fifo;
+	u32 water;
+};
+
+#define LPUART_BAUD_SBR_MASK                     (0x1FFFU)
+#define LPUART_BAUD_SBR_SHIFT                    (0U)
+#define LPUART_BAUD_SBR(x)                       (((uint32_t)(((uint32_t)(x)) << LPUART_BAUD_SBR_SHIFT)) & LPUART_BAUD_SBR_MASK)
+#define LPUART_BAUD_SBNS_MASK                    (0x2000U)
+#define LPUART_BAUD_SBNS_SHIFT                   (13U)
+#define LPUART_BAUD_SBNS(x)                      (((uint32_t)(((uint32_t)(x)) << LPUART_BAUD_SBNS_SHIFT)) & LPUART_BAUD_SBNS_MASK)
+#define LPUART_BAUD_BOTHEDGE_MASK                (0x20000U)
+#define LPUART_BAUD_BOTHEDGE_SHIFT               (17U)
+#define LPUART_BAUD_BOTHEDGE(x)                  (((uint32_t)(((uint32_t)(x)) << LPUART_BAUD_BOTHEDGE_SHIFT)) & LPUART_BAUD_BOTHEDGE_MASK)
+#define LPUART_BAUD_OSR_MASK                     (0x1F000000U)
+#define LPUART_BAUD_OSR_SHIFT                    (24U)
+#define LPUART_BAUD_OSR(x)                       (((uint32_t)(((uint32_t)(x)) << LPUART_BAUD_OSR_SHIFT)) & LPUART_BAUD_OSR_MASK)
+#define LPUART_BAUD_M10_MASK                     (0x20000000U)
+#define LPUART_BAUD_M10_SHIFT                    (29U)
+#define LPUART_BAUD_M10(x)                       (((uint32_t)(((uint32_t)(x)) << LPUART_BAUD_M10_SHIFT)) & LPUART_BAUD_M10_MASK)
+
+/*! @name CTRL - LPUART Control Register */
+#define LPUART_CTRL_PT_MASK                      (0x1U)
+#define LPUART_CTRL_PT_SHIFT                     (0U)
+#define LPUART_CTRL_PT(x)                        (((uint32_t)(((uint32_t)(x)) << LPUART_CTRL_PT_SHIFT)) & LPUART_CTRL_PT_MASK)
+#define LPUART_CTRL_PE_MASK                      (0x2U)
+#define LPUART_CTRL_PE_SHIFT                     (1U)
+#define LPUART_CTRL_PE(x)                        (((uint32_t)(((uint32_t)(x)) << LPUART_CTRL_PE_SHIFT)) & LPUART_CTRL_PE_MASK)
+#define LPUART_CTRL_M_MASK                       (0x10U)
+#define LPUART_CTRL_M_SHIFT                      (4U)
+#define LPUART_CTRL_M(x)                         (((uint32_t)(((uint32_t)(x)) << LPUART_CTRL_M_SHIFT)) & LPUART_CTRL_M_MASK)
 
 #define	is_boot_from_usb(void)		(!(readl(USB_PHY0_BASE_ADDR) & (1<<20)))
 #define	disconnect_from_pc(void)	writel(0x0, USBOTG0_RBASE + 0x140)
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index b26ada3..38880bb 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -316,6 +316,11 @@ config FSL_LPUART
 	  Select this to enable a Low Power UART for Freescale VF610 and
 	  QorIQ Layerscape devices.
 
+config IMX_LPUART
+	bool "Freescale i.MX7ULP LPUART support"
+	help
+	  Select this to enable a Low Power UART for Freescale i.MX7ULP
+
 config MVEBU_A3700_UART
 	bool "UART support for Armada 3700"
 	default n
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile
index 8430668..34a5c51 100644
--- a/drivers/serial/Makefile
+++ b/drivers/serial/Makefile
@@ -37,6 +37,7 @@ obj-$(CONFIG_SCIF_CONSOLE) += serial_sh.o
 obj-$(CONFIG_ZYNQ_SERIAL) += serial_zynq.o
 obj-$(CONFIG_BFIN_SERIAL) += serial_bfin.o
 obj-$(CONFIG_FSL_LPUART) += serial_lpuart.o
+obj-$(CONFIG_IMX_LPUART) += serial_imxlpuart.o
 obj-$(CONFIG_FSL_LINFLEXUART) += serial_linflexuart.o
 obj-$(CONFIG_ARC_SERIAL) += serial_arc.o
 obj-$(CONFIG_UNIPHIER_SERIAL) += serial_uniphier.o
diff --git a/drivers/serial/serial_imxlpuart.c b/drivers/serial/serial_imxlpuart.c
new file mode 100644
index 0000000..078cc84
--- /dev/null
+++ b/drivers/serial/serial_imxlpuart.c
@@ -0,0 +1,261 @@
+/*
+ * Copyright 2016 Freescale Semiconductor, Inc.
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <watchdog.h>
+#include <asm/io.h>
+#include <serial.h>
+#include <linux/compiler.h>
+#include <asm/arch/imx-regs.h>
+#include <asm/arch/clock.h>
+
+#define US1_TDRE	(1 << 7)
+#define US1_RDRF	(1 << 5)
+#define US1_OR		(1 << 3)
+#define UC2_TE		(1 << 3)
+#define UC2_RE		(1 << 2)
+#define CFIFO_TXFLUSH	(1 << 7)
+#define CFIFO_RXFLUSH	(1 << 6)
+#define SFIFO_RXOF	(1 << 2)
+#define SFIFO_RXUF	(1 << 0)
+
+#define STAT_LBKDIF	(1 << 31)
+#define STAT_RXEDGIF	(1 << 30)
+#define STAT_TDRE	(1 << 23)
+#define STAT_RDRF	(1 << 21)
+#define STAT_IDLE	(1 << 20)
+#define STAT_OR		(1 << 19)
+#define STAT_NF		(1 << 18)
+#define STAT_FE		(1 << 17)
+#define STAT_PF		(1 << 16)
+#define STAT_MA1F	(1 << 15)
+#define STAT_MA2F	(1 << 14)
+#define STAT_FLAGS	(STAT_LBKDIF | STAT_RXEDGIF | STAT_IDLE | STAT_OR | \
+			 STAT_NF | STAT_FE | STAT_PF | STAT_MA1F | STAT_MA2F)
+
+#define CTRL_TE		(1 << 19)
+#define CTRL_RE		(1 << 18)
+
+#define FIFO_TXFE		0x80
+#define FIFO_RXFE		0x40
+
+#define WATER_TXWATER_OFF	1
+#define WATER_RXWATER_OFF	16
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct lpuart_serial_platdata {
+	struct lpuart_fsl *reg;
+};
+
+static void _lpuart32_serial_setbrg(struct lpuart_fsl *base, int baudrate)
+{
+	u32 sbr, osr, baud_diff, tmp_osr, tmp_sbr, tmp_diff, tmp;
+	u32 clk = get_lpuart_clk();
+
+	baud_diff = baudrate;
+	osr = 0;
+	sbr = 0;
+
+	for (tmp_osr = 4; tmp_osr <= 32; tmp_osr++) {
+		tmp_sbr = (clk / (baudrate * tmp_osr));
+
+		if (tmp_sbr == 0)
+			tmp_sbr = 1;
+
+		/*calculate difference in actual buad w/ current values */
+		tmp_diff = (clk / (tmp_osr * tmp_sbr));
+		tmp_diff = tmp_diff - baudrate;
+
+		/* select best values between sbr and sbr+1 */
+		if (tmp_diff > (baudrate - (clk / (tmp_osr * (tmp_sbr + 1))))) {
+			tmp_diff = baudrate - (clk / (tmp_osr * (tmp_sbr + 1)));
+			tmp_sbr++;
+		}
+
+		if (tmp_diff <= baud_diff) {
+			baud_diff = tmp_diff;
+			osr = tmp_osr;
+			sbr = tmp_sbr;
+		}
+	}
+
+	/*
+	 * TODO: handle buadrate outside acceptable rate
+	 * if (baudDiff > ((config->baudRate_Bps / 100) * 3))
+	 * {
+	 *   Unacceptable baud rate difference of more than 3%
+	 *   return kStatus_LPUART_BaudrateNotSupport;
+	 * }
+	 */ 
+	tmp = in_le32(&base->baud);
+
+	if ((osr > 3) && (osr < 8))
+		tmp |= LPUART_BAUD_BOTHEDGE_MASK;
+
+	tmp &= ~LPUART_BAUD_OSR_MASK;
+	tmp |= LPUART_BAUD_OSR(osr-1);
+
+	tmp &= ~LPUART_BAUD_SBR_MASK;
+	tmp |= LPUART_BAUD_SBR(sbr);
+
+	/* explicitly disable 10 bit mode & set 1 stop bit */
+	tmp &= ~(LPUART_BAUD_M10_MASK | LPUART_BAUD_SBNS_MASK);
+
+	out_le32(&base->baud, tmp);
+}
+
+static int _lpuart32_serial_getc(struct lpuart_fsl *base)
+{
+	int ch;
+
+	while (!(in_le32(&base->stat) & STAT_RDRF))
+		WATCHDOG_RESET();
+
+	ch = in_le32(&base->data) & 0x3ff;
+	if (in_le32(&base->stat) & STAT_OR)
+		out_le32(&base->stat, STAT_OR);
+
+	return ch;
+}
+
+static void _lpuart32_serial_putc(struct lpuart_fsl *base, const char c)
+{
+	if (c == '\n')
+		serial_putc('\r');
+
+	while (!(in_le32(&base->stat) & STAT_TDRE))
+		WATCHDOG_RESET();
+
+	out_le32(&base->data, c);
+}
+
+/*
+ * Test whether a character is in the RX buffer
+ */
+static int _lpuart32_serial_tstc(struct lpuart_fsl *base)
+{
+	if ((in_le32(&base->water) >> 24) == 0)
+		return 0;
+
+	return 1;
+}
+
+/*
+ * Initialise the serial port with the given baudrate. The settings
+ * are always 8 data bits, no parity, 1 stop bit, no start bits.
+ */
+static int _lpuart32_serial_init(struct lpuart_fsl *base)
+{
+	u32 tmp;
+
+	/*disable TX & RX before enabling clocks */
+	tmp = in_le32(&base->ctrl);
+	tmp &= ~(CTRL_TE | CTRL_RE);
+	out_le32(&base->ctrl, tmp);
+
+	out_le32(&base->modir, 0);
+	out_le32(&base->fifo, ~(FIFO_TXFE | FIFO_RXFE));
+
+	out_le32(&base->match, 0);
+
+	/* provide data bits, parity, stop bit, etc */
+	_lpuart32_serial_setbrg(base, gd->baudrate);
+
+	/* eight data bits no parity bit */
+	tmp = in_le32(&base->ctrl);
+	tmp &= ~(LPUART_CTRL_PE_MASK | LPUART_CTRL_PT_MASK | LPUART_CTRL_M_MASK);
+	out_le32(&base->ctrl, tmp);
+
+	out_le32(&base->ctrl, CTRL_RE | CTRL_TE);
+
+	return 0;
+}
+
+static int lpuart32_serial_setbrg(struct udevice *dev, int baudrate)
+{
+	struct lpuart_serial_platdata *plat = dev->platdata;
+	struct lpuart_fsl *reg = plat->reg;
+
+	_lpuart32_serial_setbrg(reg, baudrate);
+
+	return 0;
+}
+
+static int lpuart32_serial_getc(struct udevice *dev)
+{
+	struct lpuart_serial_platdata *plat = dev->platdata;
+	struct lpuart_fsl *reg = plat->reg;
+
+	return _lpuart32_serial_getc(reg);
+}
+
+static int lpuart32_serial_putc(struct udevice *dev, const char c)
+{
+	struct lpuart_serial_platdata *plat = dev->platdata;
+	struct lpuart_fsl *reg = plat->reg;
+
+	_lpuart32_serial_putc(reg, c);
+
+	return 0;
+}
+
+static int lpuart32_serial_pending(struct udevice *dev, bool input)
+{
+	struct lpuart_serial_platdata *plat = dev->platdata;
+	struct lpuart_fsl *reg = plat->reg;
+
+	if (input)
+		return _lpuart32_serial_tstc(reg);
+	else
+		return in_le32(&reg->stat) & STAT_TDRE ? 0 : 1;
+}
+
+static int lpuart32_serial_probe(struct udevice *dev)
+{
+	struct lpuart_serial_platdata *plat = dev->platdata;
+	struct lpuart_fsl *reg = plat->reg;
+
+	return _lpuart32_serial_init(reg);
+}
+
+static int lpuart_serial_ofdata_to_platdata(struct udevice *dev)
+{
+	struct lpuart_serial_platdata *plat = dev->platdata;
+	fdt_addr_t addr;
+
+	addr = dev_get_addr(dev);
+	if (addr == FDT_ADDR_T_NONE)
+		return -EINVAL;
+
+	plat->reg = (struct lpuart_fsl *)addr;
+
+	return 0;
+}
+
+static const struct dm_serial_ops lpuart32_serial_ops = {
+	.putc = lpuart32_serial_putc,
+	.pending = lpuart32_serial_pending,
+	.getc = lpuart32_serial_getc,
+	.setbrg = lpuart32_serial_setbrg,
+};
+
+static const struct udevice_id lpuart32_serial_ids[] = {
+	{ .compatible = "fsl,imx7ulp-lpuart" },
+	{ }
+};
+
+U_BOOT_DRIVER(serial_lpuart32) = {
+	.name	= "serial_lpuart32",
+	.id	= UCLASS_SERIAL,
+	.of_match = lpuart32_serial_ids,
+	.ofdata_to_platdata = lpuart_serial_ofdata_to_platdata,
+	.platdata_auto_alloc_size = sizeof(struct lpuart_serial_platdata),
+	.probe = lpuart32_serial_probe,
+	.ops	= &lpuart32_serial_ops,
+	.flags = DM_FLAG_PRE_RELOC,
+};
-- 
2.6.2



More information about the U-Boot mailing list