[09/15] arc: libgloss: Add UART 8250 library

Message ID 20240521095658.10311-10-kolerov93@gmail.com
State New
Headers
Series A series of fixes for ARC and libgloss update |

Commit Message

Yuriy Kolerov May 21, 2024, 9:56 a.m. UTC
  From: Yuriy Kolerov <kolerov93@gmail.com>

This library implements libgloss input/output and setup
routines for UART 8250 devices of ARC development boards:

    * EM Starter Kit
    * HS Development Kit
    * EM Software Development Platform
    * IoT Development Kit

_uart_8250_setup function with proper parameteres must be called
to setup UART 8250 device for a particular board (refer to
a board's documentation).

Signed-off-by: Vladimir Isaev <vvisaev@gmail.com>
Signed-off-by: Yuriy Kolerov <kolerov93@gmail.com>
---
 libgloss/arc/uart-8250-stub.c | 116 +++++++++++++
 libgloss/arc/uart-8250.c      | 316 ++++++++++++++++++++++++++++++++++
 libgloss/arc/uart-8250.h      |  25 +++
 3 files changed, 457 insertions(+)
 create mode 100644 libgloss/arc/uart-8250-stub.c
 create mode 100644 libgloss/arc/uart-8250.c
 create mode 100644 libgloss/arc/uart-8250.h
  

Patch

diff --git a/libgloss/arc/uart-8250-stub.c b/libgloss/arc/uart-8250-stub.c
new file mode 100644
index 000000000..ab69d6354
--- /dev/null
+++ b/libgloss/arc/uart-8250-stub.c
@@ -0,0 +1,116 @@ 
+/*
+ * uart-8250-stub.c -- various stubs for 8250 UART libgloss.
+ *
+ * Copyright (c) 2024 Synopsys Inc.
+ *
+ * The authors hereby grant permission to use, copy, modify, distribute,
+ * and license this software and its documentation for any purpose, provided
+ * that existing copyright notices are retained in all copies and that this
+ * notice is included verbatim in any distributions. No written agreement,
+ * license, or royalty fee is required for any of the authorized uses.
+ * Modifications to this software may be copyrighted by their authors
+ * and need not follow the licensing terms described here, provided that
+ * the new terms are clearly indicated on the first page of each file where
+ * they apply.
+ */
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#include "glue.h"
+
+
+/* Always return character device with 1024 bytes block.  */
+int
+_fstat (int fd __attribute__ ((unused)), struct stat *buf)
+{
+  memset (buf, 0, sizeof (*buf));
+  buf->st_mode = S_IFCHR;
+  buf->st_blksize = 1024;
+
+  return 0;
+}
+
+/* ARC UART is actually console so just return 1.  */
+int
+_isatty (int fildes __attribute__ ((unused)))
+{
+  return 1;
+}
+
+/* Should be provided by crt0.S.  */
+extern void __attribute__((noreturn)) _exit_halt (int ret);
+
+void
+__attribute__((noreturn))
+_exit (int ret)
+{
+  _exit_halt (ret);
+}
+
+/* Not implemented.  */
+off_t
+_lseek (int fd __attribute__ ((unused)),
+	off_t offset __attribute__ ((unused)),
+	int whence __attribute__ ((unused)))
+{
+  errno = ENOSYS;
+  return -1;
+}
+
+/* Not implemented.  */
+int
+_close (int fd __attribute__ ((unused)))
+{
+  errno = ENOSYS;
+  return -1;
+}
+
+/* Not implemented.  */
+int
+_open (const char *path __attribute__ ((unused)),
+       int flags __attribute__ ((unused)), ...)
+{
+  errno = ENOSYS;
+  return -1;
+}
+
+/* If PID is equal to __MYPID, exit with sig as retcode.  */
+int
+_kill (int pid, int sig)
+{
+  if (pid == __MYPID)
+    _exit (sig);
+
+  errno = ENOSYS;
+  return -1;
+}
+
+/* Return __MYPID.  */
+int
+_getpid (void)
+{
+  return __MYPID;
+}
+
+/* No arguments.  */
+int
+_argc (void)
+{
+  return 0;
+}
+
+/* No arguments.  */
+uint32_t
+_argvlen (int a __attribute__ ((unused)))
+{
+  return 0;
+}
+
+/* No arguments.  */
+int
+_argv (int a __attribute__ ((unused)), char *arg __attribute__ ((unused)))
+{
+  return -1;
+}
diff --git a/libgloss/arc/uart-8250.c b/libgloss/arc/uart-8250.c
new file mode 100644
index 000000000..6f4e9f3d6
--- /dev/null
+++ b/libgloss/arc/uart-8250.c
@@ -0,0 +1,316 @@ 
+/*
+ * uart-8250.c -- polling driver for 32-bit 8250 UART.
+ * Provides _uart_8250_setup(), _read() and _write(). Please note that
+ * _read() and _write() from this file can only be used for console access.
+ *
+ * Copyright (c) 2024 Synopsys Inc.
+ *
+ * The authors hereby grant permission to use, copy, modify, distribute,
+ * and license this software and its documentation for any purpose, provided
+ * that existing copyright notices are retained in all copies and that this
+ * notice is included verbatim in any distributions. No written agreement,
+ * license, or royalty fee is required for any of the authorized uses.
+ * Modifications to this software may be copyrighted by their authors
+ * and need not follow the licensing terms described here, provided that
+ * the new terms are clearly indicated on the first page of each file where
+ * they apply.
+ */
+
+#include <unistd.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <errno.h>
+
+/*
+ * List of UART 8250 registers with offsets:
+ *
+ *     0x00 - Transmit Holding Register, WO, LCR_DLAB == 0
+ *     0x00 - Receive Buffer Register, RO, LCR_DLAB == 0
+ *     0x00 - Divisor Latch Low, RW, LCR_DLAB == 1
+ *     0x04 - Divisor Latch High, RW, LCR_DLAB == 1
+ *     0x04 - Interrupt Enable Register, RW, LCR_DLAB == 0
+ *     0x08 - FIFO Control Register, WO
+ *     0x08 - Interrupt Identification Register, RO
+ *     0x0C - Line Control Register, RW
+ *     0x10 - Modem Control Register, RW
+ *     0x14 - Line Status Register, RO
+ *     0x18 - Modem Status Register, RO
+ */
+
+#define THR_OFFSET 0x00
+#define RBR_OFFSET 0x00
+#define DLL_OFFSET 0x00
+#define DLH_OFFSET 0x04
+#define IER_OFFSET 0x04
+#define FCR_OFFSET 0x08
+#define IIR_OFFSET 0x08
+#define LCR_OFFSET 0x0C
+#define MCR_OFFSET 0x10
+#define LSR_OFFSET 0x14
+#define MSR_OFFSET 0x18
+
+/*
+ * LCR (Line Control Register) fields:
+ *
+ *       [7] - Divisor Latch Access Bit
+ *       [6] - Set Break
+ *       [5] - Stick Parity
+ *       [4] - Even Parity Select
+ *       [3] - Parity Enable
+ *       [2] - Number of Stop Bits
+ *     [1:0] - Data Length Select. Values:
+ *                 0b11 - 8 data bits per character (default)
+ *                 0b10 - 7 data bits per character
+ *                 0b01 - 6 data bits per character
+ *                 0b00 - 5 data bits per character
+ */
+
+#define LCR_DLAB_SHIFT	7
+#define LCR_SB_SHIFT	6
+#define LCR_SP_SHIFT	5
+#define LCR_EPS_SHIFT	4
+#define LCR_PEN_SHIFT	3
+#define LCR_STB_SHIFT	2
+#define LCR_DLS_SHIFT	0
+/*
+ * MCR (Modem Control Register) fields:
+ *
+ *     [4] - LoopBack Bit
+ *     [3] - Auxiliary Output 2
+ *     [2] - Auxiliary Output 1
+ *     [1] - Request To Send (ON by default)
+ *     [0] - Data Terminal Ready (ON by default)
+ */
+
+#define	MCR_LB_SHIFT	4
+#define	MCR_OUT2_SHIFT	3
+#define	MCR_OUT1_SHIFT	2
+#define	MCR_RTS_SHIFT	1
+#define	MCR_DTR_SHIFT	0
+
+/*
+ * LSR (Line Status Register) fields:
+ *
+ *     [7] - Receiver FIFO Error
+ *     [6] - Transmitter Empty
+ *     [5] - Transmit Holding Register Empty
+ *     [4] - Break Interrupt
+ *     [3] - Framing Error
+ *     [2] - Parity Error
+ *     [1] - Overrun Error
+ *     [0] - Data Ready
+ */
+
+#define LSR_RFE_SHIFT	7
+#define LSR_TEMT_SHIFT	6
+#define LSR_THRE_SHIFT	5
+#define LSR_BI_SHIFT	4
+#define LSR_FE_SHIFT	3
+#define LSR_PE_SHIFT	2
+#define LSR_OE_SHIFT	1
+#define LSR_DR_SHIFT	0
+
+/*
+ * Default initial values for configuration registers.
+ */
+
+#define FCR_DEFAULT	0x0
+#define IER_DEFAULT	0x0
+#define LCR_DLS_DEFAULT	0x3
+#define LCR_DEFAULT	(LCR_DLS_DEFAULT << LCR_DLS_SHIFT)
+#define MCR_RTS_DEFAULT	0x1
+#define MCR_DTR_DEFAULT	0x1
+#define MCR_DEFAULT	((MCR_RTS_DEFAULT << MCR_RTS_SHIFT) | \
+			 (MCR_DTR_DEFAULT << MCR_DTR_SHIFT))
+
+#define LCR_MODE_SETUP	(0x1 << LCR_DLAB_SHIFT)
+#define LSR_MODE_GETC	(0x1 << LSR_DR_SHIFT)
+#define LSR_MODE_PUTC	((0x1 << LSR_TEMT_SHIFT) | (0x1 << LSR_THRE_SHIFT))
+
+/* Main UART control structure.  */
+struct _uart_8250 {
+  volatile char *base;	/* Start of UART registers.  */
+  uint32_t clk;		/* UART clock.  */
+  uint32_t baud;	/* Baud rate.  */
+  int aux_mapped;	/* If UART registers are mapped to AUX or memory.  */
+  int ready;		/* If UART is ready to use or not.  */
+};
+
+static struct _uart_8250 _uart_8250;
+
+/* Write 32-bit value to the UART register.  */
+static inline void
+_uart_8250_write_reg (const struct _uart_8250 *uart, uint32_t reg,
+		      uint32_t value)
+{
+  if (uart->aux_mapped)
+    __builtin_arc_sr (value, (uint32_t) uart->base + reg);
+  else
+    *(volatile uint32_t *)(uart->base + reg) = value;
+}
+
+/* Read 32-bit value from the UART register.  */
+static inline uint32_t
+_uart_8250_read_reg (const struct _uart_8250 *uart, uint32_t reg)
+{
+  if (uart->aux_mapped)
+    return __builtin_arc_lr ((uint32_t) uart->base + reg);
+  else
+    return *(volatile uint32_t *)(uart->base + reg);
+}
+
+/* Wait until all flags are set.  */
+static inline void
+_uart_8250_wait (const struct _uart_8250 *uart, uint32_t reg, uint32_t flags)
+{
+  while (1)
+    {
+      if ((_uart_8250_read_reg (uart, reg) & flags) == flags)
+	break;
+    }
+}
+
+/* Get one character from UART.  CR is converted to NL.  */
+static int
+_uart_8250_getc (const struct _uart_8250 *uart)
+{
+  char c;
+
+  _uart_8250_wait (uart, LSR_OFFSET, LSR_MODE_GETC);
+
+  c = _uart_8250_read_reg (uart, RBR_OFFSET);
+
+  if (c == '\r')
+    c = '\n';
+
+  return c;
+}
+
+/* Put one character to UART. CR is placed before NL.  */
+static void
+_uart_8250_putc (const struct _uart_8250 *uart, char c)
+{
+  if (c == '\n')
+    _uart_8250_putc (uart, '\r');
+
+  _uart_8250_wait (uart, LSR_OFFSET, LSR_MODE_PUTC);
+
+  _uart_8250_write_reg (uart, THR_OFFSET, c);
+}
+
+/*
+ * Setup UART control structure and following parameters:
+ *
+ *     - baudrate if clock and baudrate are passed to function
+ *     - 8n1 (8 data bits, no parity bit, one stop bit)
+ *     - disable interrupts
+ *     - disable FIFO
+ *     - set Request To Send and Data Terminal Ready
+ *
+ * Arguments:
+ *
+ *     - base - start address of UART registers
+ *     - aux_mapped - set if UART registers are mapped to ARC AUX
+ *     - clk - UART clock frequency
+ *     - baud - UART baudrate to setup
+ *
+ * The function returns 0 on success.
+ */
+int
+_uart_8250_setup (void *base, int aux_mapped, uint32_t clk, uint32_t baud)
+{
+  struct _uart_8250 *uart = &_uart_8250;
+
+  uart->base = base;
+  uart->aux_mapped = aux_mapped;
+  uart->clk = clk;
+  uart->baud = baud;
+
+  if (clk && baud)
+    {
+      uint32_t div;
+
+      div = ((clk + 8 * baud) / baud) / 16;
+      _uart_8250_write_reg (uart, LCR_OFFSET, LCR_MODE_SETUP);
+      _uart_8250_write_reg (uart, DLL_OFFSET, div & 0xFF);
+      _uart_8250_write_reg (uart, DLH_OFFSET, div >> 8);
+    }
+
+  _uart_8250_write_reg (uart, FCR_OFFSET, FCR_DEFAULT);
+  _uart_8250_write_reg (uart, IER_OFFSET, IER_DEFAULT);
+  _uart_8250_write_reg (uart, LCR_OFFSET, LCR_DEFAULT);
+  _uart_8250_write_reg (uart, MCR_OFFSET, MCR_DEFAULT);
+
+  uart->ready = 1;
+
+  return 0;
+}
+
+/* _read() is implemented only for stdin. Each read character is echoed.  */
+ssize_t
+_read (int fd, void *buf, size_t count)
+{
+  struct _uart_8250 *uart = &_uart_8250;
+  size_t bytes_read;
+  char *buf_char = buf;
+  int c;
+
+  if (fd != STDIN_FILENO)
+    {
+      errno = ENOSYS;
+      return -1;
+    }
+
+  if (!uart->ready)
+    {
+      errno = EIO;
+      return -1;
+    }
+
+  bytes_read = 0;
+  c = EOF;
+  /* Break on '\n' to simulate readline behavior.  */
+  while (bytes_read != count && c != '\n')
+    {
+      c = _uart_8250_getc (uart);
+      if (c == EOF)
+	break;
+
+      /* Echo character to the console.  */
+      _uart_8250_putc (uart, c);
+
+      buf_char[bytes_read] = c;
+      bytes_read++;
+    }
+
+  return bytes_read;
+}
+
+/* _write() is implemented only for stdout and stderr.  */
+ssize_t
+_write (int fd, const char *buf, size_t nbyte)
+{
+  struct _uart_8250 *uart = &_uart_8250;
+  uint32_t bytes_written;
+
+  if (fd != STDOUT_FILENO && fd != STDERR_FILENO)
+    {
+      errno = ENOSYS;
+      return -1;
+    }
+
+  if (!uart->ready)
+    {
+      errno = EIO;
+      return -1;
+    }
+
+  bytes_written = 0;
+  while (bytes_written != nbyte)
+    {
+      _uart_8250_putc (uart, buf[bytes_written]);
+      bytes_written++;
+    }
+
+  return bytes_written;
+}
diff --git a/libgloss/arc/uart-8250.h b/libgloss/arc/uart-8250.h
new file mode 100644
index 000000000..dc6062334
--- /dev/null
+++ b/libgloss/arc/uart-8250.h
@@ -0,0 +1,25 @@ 
+/*
+ * uart-8250.h -- polling driver for 32-bit 8250 UART.
+ * Header defines _uart_8250_setup().
+ *
+ * Copyright (c) 2024 Synopsys Inc.
+ *
+ * The authors hereby grant permission to use, copy, modify, distribute,
+ * and license this software and its documentation for any purpose, provided
+ * that existing copyright notices are retained in all copies and that this
+ * notice is included verbatim in any distributions. No written agreement,
+ * license, or royalty fee is required for any of the authorized uses.
+ * Modifications to this software may be copyrighted by their authors
+ * and need not follow the licensing terms described here, provided that
+ * the new terms are clearly indicated on the first page of each file where
+ * they apply.
+ */
+
+#ifndef _UART_8250_H
+#define _UART_8250_H
+
+#include <stdint.h>
+
+int _uart_8250_setup (void *base, int aux_mapped, uint32_t clk, uint32_t baud);
+
+#endif /* ! _UART_8250_H */