From: Nathan Barrett-Morrison <nathan.morri...@timesys.com>

Co-developed-by: Greg Malysa <greg.mal...@timesys.com>
Signed-off-by: Greg Malysa <greg.mal...@timesys.com>
Co-developed-by: Ian Roberts <ian.robe...@timesys.com>
Signed-off-by: Ian Roberts <ian.robe...@timesys.com>
Signed-off-by: Vasileios Bimpikas <vasileios.bimpi...@analog.com>
Signed-off-by: Utsav Agarwal <utsav.agar...@analog.com>
Signed-off-by: Arturs Artamonovs <arturs.artamon...@analog.com>
Signed-off-by: Nathan Barrett-Morrison <nathan.morri...@timesys.com>
---

(no changes since v1)

 MAINTAINERS                       |   1 +
 drivers/serial/Makefile           |   1 +
 drivers/serial/serial_adi_uart4.c | 225 ++++++++++++++++++++++++++++++
 3 files changed, 227 insertions(+)
 create mode 100644 drivers/serial/serial_adi_uart4.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 78e3d59f96..74310c8f9d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -610,6 +610,7 @@ T:  git https://github.com/analogdevicesinc/lnxdsp-u-boot
 F:     arch/arm/include/asm/arch-adi/
 F:     arch/arm/mach-sc5xx/
 F:     drivers/clk/adi/
+F:     drivers/serial/serial_adi_uart4.c
 F:     include/env/adi/
 
 ARM SNAPDRAGON
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile
index 403ab1ded6..dbe598b740 100644
--- a/drivers/serial/Makefile
+++ b/drivers/serial/Makefile
@@ -65,3 +65,4 @@ obj-$(CONFIG_S5P4418_PL011_SERIAL) += serial_s5p4418_pl011.o
 ifndef CONFIG_SPL_BUILD
 obj-$(CONFIG_USB_TTY) += usbtty.o
 endif
+obj-$(CONFIG_UART4_SERIAL) += serial_adi_uart4.o
diff --git a/drivers/serial/serial_adi_uart4.c 
b/drivers/serial/serial_adi_uart4.c
new file mode 100644
index 0000000000..45f8315d0a
--- /dev/null
+++ b/drivers/serial/serial_adi_uart4.c
@@ -0,0 +1,225 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (C) Copyright 2022 - Analog Devices, Inc.
+ *
+ * Written and/or maintained by Timesys Corporation
+ *
+ * Converted to driver model by Nathan Barrett-Morrison
+ *
+ * Contact: Nathan Barrett-Morrison <nathan.morri...@timesys.com>
+ * Contact: Greg Malysa <greg.mal...@timesys.com>
+ *
+ */
+
+#include <clk.h>
+#include <dm.h>
+#include <serial.h>
+#include <asm/io.h>
+#include <dm/device_compat.h>
+#include <linux/bitops.h>
+
+/*
+ * UART4 Masks
+ */
+
+/* UART_CONTROL */
+#define UEN                    BIT(0)
+#define LOOP_ENA               BIT(1)
+#define UMOD                   (3 << 4)
+#define UMOD_UART              (0 << 4)
+#define UMOD_MDB               BIT(4)
+#define UMOD_IRDA              BIT(4)
+#define WLS                    (3 << 8)
+#define WLS_5                  (0 << 8)
+#define WLS_6                  BIT(8)
+#define WLS_7                  (2 << 8)
+#define WLS_8                  (3 << 8)
+#define STB                    BIT(12)
+#define STBH                   BIT(13)
+#define PEN                    BIT(14)
+#define EPS                    BIT(15)
+#define STP                    BIT(16)
+#define FPE                    BIT(17)
+#define FFE                    BIT(18)
+#define SB                     BIT(19)
+#define FCPOL                  BIT(22)
+#define RPOLC                  BIT(23)
+#define TPOLC                  BIT(24)
+#define MRTS                   BIT(25)
+#define XOFF                   BIT(26)
+#define ARTS                   BIT(27)
+#define ACTS                   BIT(28)
+#define RFIT                   BIT(29)
+#define RFRT                   BIT(30)
+
+/* UART_STATUS */
+#define DR                     BIT(0)
+#define OE                     BIT(1)
+#define PE                     BIT(2)
+#define FE                     BIT(3)
+#define BI                     BIT(4)
+#define THRE                   BIT(5)
+#define TEMT                   BIT(7)
+#define TFI                    BIT(8)
+#define ASTKY                  BIT(9)
+#define ADDR                   BIT(10)
+#define RO                     BIT(11)
+#define SCTS                   BIT(12)
+#define CTS                    BIT(16)
+#define RFCS                   BIT(17)
+
+/* UART_EMASK */
+#define ERBFI                  BIT(0)
+#define ETBEI                  BIT(1)
+#define ELSI                   BIT(2)
+#define EDSSI                  BIT(3)
+#define EDTPTI                 BIT(4)
+#define ETFI                   BIT(5)
+#define ERFCI                  BIT(6)
+#define EAWI                   BIT(7)
+#define ERXS                   BIT(8)
+#define ETXS                   BIT(9)
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct uart4_reg {
+       u32 revid;
+       u32 control;
+       u32 status;
+       u32 scr;
+       u32 clock;
+       u32 emask;
+       u32 emaskst;
+       u32 emaskcl;
+       u32 rbr;
+       u32 thr;
+       u32 taip;
+       u32 tsr;
+       u32 rsr;
+       u32 txdiv_cnt;
+       u32 rxdiv_cnt;
+};
+
+struct adi_uart4_platdata {
+       // Hardware registers
+       struct uart4_reg *regs;
+
+       // Enable divide-by-one baud rate setting
+       bool edbo;
+};
+
+static int adi_uart4_set_brg(struct udevice *dev, int baudrate)
+{
+       struct adi_uart4_platdata *plat = dev_get_plat(dev);
+       struct uart4_reg *regs = plat->regs;
+       u32 divisor, uart_base_clk_rate;
+       struct clk uart_base_clk;
+
+       if (clk_get_by_index(dev, 0, &uart_base_clk)) {
+               dev_err(dev, "Could not get UART base clock\n");
+               return -1;
+       }
+
+       uart_base_clk_rate = clk_get_rate(&uart_base_clk);
+
+       if (plat->edbo) {
+               u16 divisor16 = (uart_base_clk_rate + (baudrate / 2)) / 
baudrate;
+
+               divisor = divisor16 | BIT(31);
+       } else {
+               // Divisor is only 16 bits
+               divisor = 0x0000ffff & ((uart_base_clk_rate + (baudrate * 8)) / 
(baudrate * 16));
+       }
+
+       writel(divisor, &regs->clock);
+       return 0;
+}
+
+static int adi_uart4_pending(struct udevice *dev, bool input)
+{
+       struct adi_uart4_platdata *plat = dev_get_plat(dev);
+       struct uart4_reg *regs = plat->regs;
+
+       if (input)
+               return (readl(&regs->status) & DR) ? 1 : 0;
+       else
+               return (readl(&regs->status) & THRE) ? 0 : 1;
+}
+
+static int adi_uart4_getc(struct udevice *dev)
+{
+       struct adi_uart4_platdata *plat = dev_get_plat(dev);
+       struct uart4_reg *regs = plat->regs;
+       int uart_rbr_val;
+
+       if (!adi_uart4_pending(dev, true))
+               return -EAGAIN;
+
+       uart_rbr_val = readl(&regs->rbr);
+       writel(-1, &regs->status);
+
+       return uart_rbr_val;
+}
+
+static int adi_uart4_putc(struct udevice *dev, const char ch)
+{
+       struct adi_uart4_platdata *plat = dev_get_plat(dev);
+       struct uart4_reg *regs = plat->regs;
+
+       if (adi_uart4_pending(dev, false))
+               return -EAGAIN;
+
+       writel(ch, &regs->thr);
+       return 0;
+}
+
+static const struct dm_serial_ops adi_uart4_serial_ops = {
+       .setbrg = adi_uart4_set_brg,
+       .getc = adi_uart4_getc,
+       .putc = adi_uart4_putc,
+       .pending = adi_uart4_pending,
+};
+
+static int adi_uart4_of_to_plat(struct udevice *dev)
+{
+       struct adi_uart4_platdata *plat = dev_get_plat(dev);
+       fdt_addr_t addr;
+
+       addr = dev_read_addr(dev);
+       if (addr == FDT_ADDR_T_NONE)
+               return -EINVAL;
+
+       plat->regs = (struct uart4_reg *)addr;
+       plat->edbo = dev_read_bool(dev, "adi,enable-edbo");
+
+       return 0;
+}
+
+static int adi_uart4_probe(struct udevice *dev)
+{
+       struct adi_uart4_platdata *plat = dev_get_plat(dev);
+       struct uart4_reg *regs = plat->regs;
+
+       /* always enable UART to 8-bit mode */
+       writel(UEN | UMOD_UART | WLS_8, &regs->control);
+
+       writel(-1, &regs->status);
+
+       return 0;
+}
+
+static const struct udevice_id adi_uart4_serial_ids[] = {
+       { .compatible = "adi,uart4" },
+       { }
+};
+
+U_BOOT_DRIVER(serial_adi_uart4) = {
+       .name = "serial_adi_uart4",
+       .id = UCLASS_SERIAL,
+       .of_match = adi_uart4_serial_ids,
+       .of_to_plat = adi_uart4_of_to_plat,
+       .plat_auto = sizeof(struct adi_uart4_platdata),
+       .probe = adi_uart4_probe,
+       .ops = &adi_uart4_serial_ops,
+       .flags = DM_FLAG_PRE_RELOC,
+};
-- 
2.43.2

Reply via email to