
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@nxp.com Cc: Stefano Babic sbabic@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(®->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, +};