[PATCH v1] serial: npcm: Add support for Nuvoton NPCM SoCs

Add Nuvoton BMC NPCM7xx/NPCM8xx uart driver
Signed-off-by: Stanley Chu yschu@nuvoton.com --- drivers/serial/Kconfig | 7 ++ drivers/serial/Makefile | 1 + drivers/serial/serial_npcm.c | 151 +++++++++++++++++++++++++++++++++++ 3 files changed, 159 insertions(+) create mode 100644 drivers/serial/serial_npcm.c
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 6c8fdda9a0..8daaef61b3 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -920,6 +920,13 @@ config MPC8XX_CONS depends on MPC8xx default y
+config NPCM_SERIAL + bool "UART driver for Nuvoton NPCM BMC" + depends on DM_SERIAL + help + Select this to enable UART support for Nuvoton BMCs + (NPCM7xx and NPCM8xx) + config XEN_SERIAL bool "XEN serial support" depends on XEN diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index 8168af640f..866495e416 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -73,6 +73,7 @@ obj-$(CONFIG_OWL_SERIAL) += serial_owl.o obj-$(CONFIG_OMAP_SERIAL) += serial_omap.o obj-$(CONFIG_MTK_SERIAL) += serial_mtk.o obj-$(CONFIG_MT7620_SERIAL) += serial_mt7620.o +obj-$(CONFIG_NPCM_SERIAL) += serial_npcm.o obj-$(CONFIG_SIFIVE_SERIAL) += serial_sifive.o obj-$(CONFIG_XEN_SERIAL) += serial_xen.o
diff --git a/drivers/serial/serial_npcm.c b/drivers/serial/serial_npcm.c new file mode 100644 index 0000000000..67343e5805 --- /dev/null +++ b/drivers/serial/serial_npcm.c @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2021 Nuvoton Technology Corp. + */ + +#include <common.h> +#include <clk.h> +#include <dm.h> +#include <serial.h> + +struct npcm_uart { + union { + u32 rbr; + u32 thr; + u32 dll; + }; + union { + u32 ier; + u32 dlm; + }; + union { + u32 iir; + u32 fcr; + }; + u32 lcr; + u32 mcr; + u32 lsr; + u32 msr; + u32 tor; +}; + +#define LCR_WLS_8BITS 3 +#define FCR_TFR BIT(2) +#define FCR_RFR BIT(1) +#define FCR_FME BIT(0) +#define LSR_THRE BIT(5) +#define LSR_RFDR BIT(0) +#define LCR_DLAB BIT(7) + +struct npcm_serial_plat { + struct npcm_uart *reg; + u32 uart_clk; +}; + +static int npcm_serial_pending(struct udevice *dev, bool input) +{ + struct npcm_serial_plat *plat = dev_get_plat(dev); + struct npcm_uart *uart = plat->reg; + + if (input) + return (readb(&uart->lsr) & LSR_RFDR); + else + return !(readb(&uart->lsr) & LSR_THRE); +} + +static int npcm_serial_putc(struct udevice *dev, const char ch) +{ + struct npcm_serial_plat *plat = dev_get_plat(dev); + struct npcm_uart *uart = plat->reg; + + while (!(readb(&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 *uart = plat->reg; + + while (!(readb(&uart->lsr) & LSR_RFDR)) + ; + + return readb(&uart->rbr); +} + +static int npcm_serial_setbrg(struct udevice *dev, int baudrate) +{ + struct npcm_serial_plat *plat = dev_get_plat(dev); + struct npcm_uart *uart = plat->reg; + u16 divisor; + + /* BaudOut = UART Clock / (16 * [Divisor + 2]) */ + divisor = DIV_ROUND_CLOSEST(plat->uart_clk, 16 * baudrate + 2) - 2; + + setbits_8(&uart->lcr, LCR_DLAB); + writeb(divisor & 0xff, &uart->dll); + writeb(divisor >> 8, &uart->dlm); + clrbits_8(&uart->lcr, LCR_DLAB); + + return 0; +} + +static int npcm_serial_probe(struct udevice *dev) +{ + struct npcm_serial_plat *plat = dev_get_plat(dev); + struct npcm_uart *uart = plat->reg; + struct clk clk; + u32 freq; + int ret; + + plat->reg = 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; + + /* Disable all interrupt */ + writeb(0, &uart->ier); + + /* Set 8 bit, 1 stop, no parity */ + writeb(LCR_WLS_8BITS, &uart->lcr); + + /* Reset RX/TX FIFO */ + writeb(FCR_FME | FCR_RFR | FCR_TFR, &uart->fcr); + + 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, +};

Hi Stanley,
On Sun, 16 Jan 2022 at 20:20, Stanley Chu stanley.chuys@gmail.com wrote:
Add Nuvoton BMC NPCM7xx/NPCM8xx uart driver
Signed-off-by: Stanley Chu yschu@nuvoton.com
drivers/serial/Kconfig | 7 ++ drivers/serial/Makefile | 1 + drivers/serial/serial_npcm.c | 151 +++++++++++++++++++++++++++++++++++ 3 files changed, 159 insertions(+) create mode 100644 drivers/serial/serial_npcm.c
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 6c8fdda9a0..8daaef61b3 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -920,6 +920,13 @@ config MPC8XX_CONS depends on MPC8xx default y
+config NPCM_SERIAL
bool "UART driver for Nuvoton NPCM BMC"
depends on DM_SERIAL
help
Select this to enable UART support for Nuvoton BMCs
(NPCM7xx and NPCM8xx)
What features does it support? Please expand this a bit.
config XEN_SERIAL bool "XEN serial support" depends on XEN diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index 8168af640f..866495e416 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -73,6 +73,7 @@ obj-$(CONFIG_OWL_SERIAL) += serial_owl.o obj-$(CONFIG_OMAP_SERIAL) += serial_omap.o obj-$(CONFIG_MTK_SERIAL) += serial_mtk.o obj-$(CONFIG_MT7620_SERIAL) += serial_mt7620.o +obj-$(CONFIG_NPCM_SERIAL) += serial_npcm.o obj-$(CONFIG_SIFIVE_SERIAL) += serial_sifive.o obj-$(CONFIG_XEN_SERIAL) += serial_xen.o
diff --git a/drivers/serial/serial_npcm.c b/drivers/serial/serial_npcm.c new file mode 100644 index 0000000000..67343e5805 --- /dev/null +++ b/drivers/serial/serial_npcm.c @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright (c) 2021 Nuvoton Technology Corp.
- */
+#include <common.h> +#include <clk.h> +#include <dm.h> +#include <serial.h>
+struct npcm_uart {
union {
u32 rbr;
u32 thr;
u32 dll;
};
union {
u32 ier;
u32 dlm;
};
union {
u32 iir;
u32 fcr;
};
u32 lcr;
u32 mcr;
u32 lsr;
u32 msr;
u32 tor;
+};
+#define LCR_WLS_8BITS 3 +#define FCR_TFR BIT(2) +#define FCR_RFR BIT(1) +#define FCR_FME BIT(0) +#define LSR_THRE BIT(5) +#define LSR_RFDR BIT(0) +#define LCR_DLAB BIT(7)
+struct npcm_serial_plat {
struct npcm_uart *reg;
u32 uart_clk;
comment that
+};
+static int npcm_serial_pending(struct udevice *dev, bool input) +{
struct npcm_serial_plat *plat = dev_get_plat(dev);
struct npcm_uart *uart = plat->reg;
if (input)
return (readb(&uart->lsr) & LSR_RFDR);
drop outer brackets
else
return !(readb(&uart->lsr) & LSR_THRE);
and here
+}
+static int npcm_serial_putc(struct udevice *dev, const char ch) +{
struct npcm_serial_plat *plat = dev_get_plat(dev);
struct npcm_uart *uart = plat->reg;
while (!(readb(&uart->lsr) & LSR_THRE))
;
You cannot loop here. Return -EAGAIN instead if you cannot print.
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 *uart = plat->reg;
while (!(readb(&uart->lsr) & LSR_RFDR))
;
You cannot loop here. Return -EAGAIN instead. See __serial_getc()
return readb(&uart->rbr);
+}
+static int npcm_serial_setbrg(struct udevice *dev, int baudrate) +{
struct npcm_serial_plat *plat = dev_get_plat(dev);
struct npcm_uart *uart = plat->reg;
u16 divisor;
/* BaudOut = UART Clock / (16 * [Divisor + 2]) */
divisor = DIV_ROUND_CLOSEST(plat->uart_clk, 16 * baudrate + 2) - 2;
setbits_8(&uart->lcr, LCR_DLAB);
writeb(divisor & 0xff, &uart->dll);
writeb(divisor >> 8, &uart->dlm);
clrbits_8(&uart->lcr, LCR_DLAB);
return 0;
+}
+static int npcm_serial_probe(struct udevice *dev) +{
struct npcm_serial_plat *plat = dev_get_plat(dev);
struct npcm_uart *uart = plat->reg;
struct clk clk;
u32 freq;
int ret;
plat->reg = 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");
log_debug() would be better to avoid increasing code size
return ret;
}
ret = clk_set_rate(&clk, freq);
if (ret < 0)
return ret;
plat->uart_clk = ret;
/* Disable all interrupt */
writeb(0, &uart->ier);
/* Set 8 bit, 1 stop, no parity */
writeb(LCR_WLS_8BITS, &uart->lcr);
/* Reset RX/TX FIFO */
writeb(FCR_FME | FCR_RFR | FCR_TFR, &uart->fcr);
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,
+};
2.17.1
Regards, Simon
participants (2)
-
Simon Glass
-
Stanley Chu