
Add Nuvoton BMC NPCM7xx/NPCM8xx uart driver
Signed-off-by: Stanley Chu yschu@nuvoton.com --- arch/arm/include/asm/arch-npcm8xx/uart.h | 82 ++++++++++++++ drivers/serial/Kconfig | 7 ++ drivers/serial/Makefile | 1 + drivers/serial/serial_npcm.c | 137 +++++++++++++++++++++++ 4 files changed, 227 insertions(+) create mode 100644 arch/arm/include/asm/arch-npcm8xx/uart.h create mode 100644 drivers/serial/serial_npcm.c
diff --git a/arch/arm/include/asm/arch-npcm8xx/uart.h b/arch/arm/include/asm/arch-npcm8xx/uart.h new file mode 100644 index 0000000000..e3c86849f3 --- /dev/null +++ b/arch/arm/include/asm/arch-npcm8xx/uart.h @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ + +#ifndef _NPCM_UART_H_ +#define _NPCM_UART_H_ + +struct npcm_uart { + union { + unsigned int rbr; + unsigned int thr; + unsigned int dll; + }; + union { + unsigned int ier; + unsigned int dlm; + }; + union { + unsigned int iir; + unsigned int fcr; + }; + unsigned int lcr; + unsigned int mcr; + unsigned int lsr; + unsigned int msr; + unsigned int tor; +}; + +#define IER_DBGACK BIT(4) +#define IER_MSIE BIT(3) +#define IER_RLSE BIT(2) +#define IER_THREIE BIT(1) +#define IER_RDAIE BIT(0) + +#define IIR_FMES BIT(7) +#define IIR_RFTLS BIT(5) +#define IIR_DMS BIT(4) +#define IIR_IID BIT(1) +#define IIR_NIP BIT(0) + +#define FCR_RFITL_1B (0 << 4) +#define FCR_RFITL_4B (4 << 4) +#define FCR_RFITL_8B (8 << 4) +#define FCR_RFITL_14B (12 << 4) +#define FCR_DMS BIT(3) +#define FCR_TFR BIT(2) +#define FCR_RFR BIT(1) +#define FCR_FME BIT(0) + +#define LCR_DLAB BIT(7) +#define LCR_BCB BIT(6) +#define LCR_SPE BIT(5) +#define LCR_EPS BIT(4) +#define LCR_PBE BIT(3) +#define LCR_NSB BIT(2) +#define LCR_WLS_8b 3 +#define LCR_WLS_7b 2 +#define LCR_WLS_6b 1 +#define LCR_WLS_5b 0 + +#define MCR_LBME BIT(4) +#define MCR_OUT2 BIT(3) +#define MCR_RTS BIT(1) +#define MCR_DTR BIT(0) + +#define LSR_ERR_RX BIT(7) +#define LSR_TE BIT(6) +#define LSR_THRE BIT(5) +#define LSR_BII BIT(4) +#define LSR_FEI BIT(3) +#define LSR_PEI BIT(2) +#define LSR_OEI BIT(1) +#define LSR_RFDR BIT(0) + +#define MSR_DCD BIT(7) +#define MSR_RI BIT(6) +#define MSR_DSR BIT(5) +#define MSR_CTS BIT(4) +#define MSR_DDCD BIT(3) +#define MSR_DRI BIT(2) +#define MSR_DDSR BIT(1) +#define MSR_DCTS BIT(0) + +#endif diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 6c8fdda9a0..3982bc9426 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -975,4 +975,11 @@ config SYS_SDMR depends on MPC8XX_CONS default 0
+config NPCM_SERIAL + bool "Nuvoton NPCM UART driver" + depends on DM_SERIAL + help + Select this to enable UART support for Nuvoton BMCs + (NPCM7xx and NPCM8xx) + endif diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index 8168af640f..1b86acf5cf 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -75,6 +75,7 @@ obj-$(CONFIG_MTK_SERIAL) += serial_mtk.o obj-$(CONFIG_MT7620_SERIAL) += serial_mt7620.o obj-$(CONFIG_SIFIVE_SERIAL) += serial_sifive.o obj-$(CONFIG_XEN_SERIAL) += serial_xen.o +obj-$(CONFIG_NPCM_SERIAL) += serial_npcm.o
ifndef CONFIG_SPL_BUILD obj-$(CONFIG_USB_TTY) += usbtty.o diff --git a/drivers/serial/serial_npcm.c b/drivers/serial/serial_npcm.c new file mode 100644 index 0000000000..330c544e5a --- /dev/null +++ b/drivers/serial/serial_npcm.c @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2021 Nuvoton Technology Corp. + */ + +#include <common.h> +#include <dm.h> +#include <serial.h> +#include <clk.h> +#include <asm/arch/uart.h> + +struct npcm_serial_plat { + struct npcm_uart *reg; + u32 uart_clk; +}; + +static int npcm_serial_init(struct npcm_uart *uart) +{ + u8 val; + + /* Disable all UART interrupt */ + writeb(0, &uart->ier); + + /* Set port for 8 bit, 1 stop, no parity */ + val = LCR_WLS_8b; + writeb(val, &uart->lcr); + + /* Set the RX FIFO trigger level, reset RX, TX FIFO */ + val = FCR_FME | FCR_RFR | FCR_TFR | FCR_RFITL_4B; + writeb(val, &uart->fcr); + + return 0; +} + +static int npcm_serial_pending(struct udevice *dev, bool input) +{ + struct npcm_serial_plat *plat = dev_get_plat(dev); + struct npcm_uart *const uart = plat->reg; + + if (input) + return (readb(&uart->lsr) & LSR_RFDR); + else + return !(readb(&uart->lsr) & LSR_THRE); + + return 0; +} + +static int npcm_serial_putc(struct udevice *dev, const char ch) +{ + struct npcm_serial_plat *plat = dev_get_plat(dev); + struct npcm_uart *const uart = plat->reg; + + while (!(readl(&uart->lsr) & LSR_THRE)) + ; + + writeb(ch, &uart->thr); + + return 0; +} + +static int npcm_serial_getc(struct udevice *dev) +{ + struct npcm_serial_plat *plat = dev_get_plat(dev); + struct npcm_uart *const uart = plat->reg; + + while (!(readl(&uart->lsr) & LSR_RFDR)) + ; + + return (int)(readb(&uart->rbr) & 0xff); +} + +static int npcm_serial_setbrg(struct udevice *dev, int baudrate) +{ + struct npcm_serial_plat *plat = dev_get_plat(dev); + struct npcm_uart *const uart = plat->reg; + int ret = 0; + u32 divisor; + + /* BaudOut = UART Clock / (16 * [Divisor + 2]) */ + divisor = DIV_ROUND_CLOSEST(plat->uart_clk, 16 * baudrate + 2) - 2; + + writeb(readb(&uart->lcr) | LCR_DLAB, &uart->lcr); + writeb(divisor & 0xff, &uart->dll); + writeb(divisor >> 8, &uart->dlm); + writeb(readb(&uart->lcr) & (~LCR_DLAB), &uart->lcr); + + return ret; +} + +static int npcm_serial_probe(struct udevice *dev) +{ + struct npcm_serial_plat *plat = dev_get_plat(dev); + struct npcm_uart *const uart = plat->reg; + struct clk clk; + u32 freq; + int ret; + + plat->reg = (struct npcm_uart *)dev_read_addr_ptr(dev); + freq = dev_read_u32_default(dev, "clock-frequency", 0); + + ret = clk_get_by_index(dev, 0, &clk); + if (ret < 0) { + printf("Cannot get clk for uart\n"); + return ret; + } + ret = clk_set_rate(&clk, freq); + if (ret < 0) + return ret; + plat->uart_clk = ret; + + npcm_serial_init(uart); + + return 0; +} + +static const struct dm_serial_ops npcm_serial_ops = { + .getc = npcm_serial_getc, + .setbrg = npcm_serial_setbrg, + .putc = npcm_serial_putc, + .pending = npcm_serial_pending, +}; + +static const struct udevice_id npcm_serial_ids[] = { + { .compatible = "nuvoton,npcm750-uart" }, + { .compatible = "nuvoton,npcm845-uart" }, + { } +}; + +U_BOOT_DRIVER(serial_npcm) = { + .name = "serial_npcm", + .id = UCLASS_SERIAL, + .of_match = npcm_serial_ids, + .plat_auto = sizeof(struct npcm_serial_plat), + .probe = npcm_serial_probe, + .ops = &npcm_serial_ops, + .flags = DM_FLAG_PRE_RELOC, +};