diff mbox series

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

Message ID 20220117031953.16098-1-yschu@nuvoton.com
State Superseded
Delegated to: Tom Rini
Headers show
Series [v1] serial: npcm: Add support for Nuvoton NPCM SoCs | expand

Commit Message

Stanley Chu Jan. 17, 2022, 3:19 a.m. UTC
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

Comments

Simon Glass Jan. 21, 2022, 3:20 p.m. UTC | #1
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
diff mbox series

Patch

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,
+};