[rtems commit] dev: Add NXP SC16IS752 serial device driver

Sebastian Huber sebh at rtems.org
Fri Dec 16 10:30:00 UTC 2016


Module:    rtems
Branch:    master
Commit:    9edc73013bbf20d5ca7df67df46699ca32c3e371
Changeset: http://git.rtems.org/rtems/commit/?id=9edc73013bbf20d5ca7df67df46699ca32c3e371

Author:    Alexander Krutwig <alexander.krutwig at embedded-brains.de>
Date:      Fri Sep 16 15:21:26 2016 +0200

dev: Add NXP SC16IS752 serial device driver

Update #2841.

---

 cpukit/dev/Makefile.am                    |   7 +
 cpukit/dev/include/dev/serial/sc16is752.h | 262 ++++++++++++++++++++++++
 cpukit/dev/preinstall.am                  |   9 +
 cpukit/dev/serial/sc16is752-regs.h        | 116 +++++++++++
 cpukit/dev/serial/sc16is752-spi.c         | 151 ++++++++++++++
 cpukit/dev/serial/sc16is752.c             | 330 ++++++++++++++++++++++++++++++
 6 files changed, 875 insertions(+)

diff --git a/cpukit/dev/Makefile.am b/cpukit/dev/Makefile.am
index 2b65390..fbd84fd 100644
--- a/cpukit/dev/Makefile.am
+++ b/cpukit/dev/Makefile.am
@@ -15,6 +15,10 @@ include_dev_spidir = $(includedir)/dev/spi
 include_dev_spi_HEADERS =
 include_dev_spi_HEADERS += include/dev/spi/spi.h
 
+include_dev_serialdir = $(includedir)/dev/serial
+include_dev_serial_HEADERS =
+include_dev_serial_HEADERS += include/dev/serial/sc16is752.h
+
 include_linuxdir = $(includedir)/linux
 include_linux_HEADERS =
 include_linux_HEADERS += include/linux/i2c.h
@@ -33,6 +37,9 @@ libdev_a_SOURCES += i2c/i2c-bus.c
 libdev_a_SOURCES += i2c/i2c-dev.c
 libdev_a_SOURCES += i2c/switch-nxp-pca9548a.c
 libdev_a_SOURCES += spi/spi-bus.c
+libdev_a_SOURCES += serial/sc16is752.c
+libdev_a_SOURCES += serial/sc16is752-spi.c
+libdev_a_SOURCES += serial/sc16is752-regs.h
 
 include $(srcdir)/preinstall.am
 include $(top_srcdir)/automake/local.am
diff --git a/cpukit/dev/include/dev/serial/sc16is752.h b/cpukit/dev/include/dev/serial/sc16is752.h
new file mode 100644
index 0000000..7e5b47a
--- /dev/null
+++ b/cpukit/dev/include/dev/serial/sc16is752.h
@@ -0,0 +1,262 @@
+/*
+ * Copyright (c) 2016 embedded brains GmbH.  All rights reserved.
+ *
+ *  embedded brains GmbH
+ *  Dornierstr. 4
+ *  82178 Puchheim
+ *  Germany
+ *  <info at embedded-brains.de>
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#ifndef _DEV_SERIAL_SC16IS752_H
+#define _DEV_SERIAL_SC16IS752_H
+
+#include <sys/ioccom.h>
+
+#include <rtems/termiostypes.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/**
+ * @defgroup SC16IS752 SC16IS752 Serial Device Driver
+ *
+ * @ingroup TermiostypesSupport
+ */
+
+typedef enum {
+  SC16IS752_MODE_RS232,
+  SC16IS752_MODE_RS485
+} sc16is752_mode;
+
+typedef struct sc16is752_context sc16is752_context;
+
+/**
+ * @brief SC16IS752 device context.
+ */
+struct sc16is752_context {
+  rtems_termios_device_context base;
+
+  /**
+   * @brief Writes a register.
+   *
+   * Internal handler.
+   */
+  int (*write_reg)(
+    sc16is752_context *ctx,
+    uint8_t addr,
+    const uint8_t *data,
+    size_t len
+  );
+
+  /**
+   * @brief Reads a register.
+   *
+   * Internal handler.
+   */
+  int (*read_reg)(
+    sc16is752_context *ctx,
+    uint8_t addr,
+    uint8_t *data,
+    size_t len
+  );
+
+  /**
+   * @brief Reads two registers.
+   *
+   * Internal handler.
+   */
+  int (*read_2_reg)(
+    sc16is752_context *ctx,
+    uint8_t addr_0,
+    uint8_t addr_1,
+    uint8_t data[2]
+  );
+
+  /**
+   * @brief First open.
+   *
+   * Internal handler.
+   */
+  bool (*first_open)(sc16is752_context *ctx);
+
+  /**
+   * @brief Last close.
+   *
+   * Internal handler.
+   */
+  void (*last_close)(sc16is752_context *ctx);
+
+  /**
+   * @brief Shall install the interrupt handler.
+   *
+   * Must be initialized by the user before the device creation.
+   */
+  bool (*install_irq)(sc16is752_context *ctx);
+
+  /**
+   * @brief Shall remove the interrupt handler.
+   *
+   * Must be initialized by the user before the device creation.
+   */
+  void (*remove_irq)(sc16is752_context *ctx);
+
+  /**
+   * @brief Device mode.
+   *
+   * Must be initialized by the user before the device creation.
+   */
+  sc16is752_mode mode;
+
+  /**
+   * @brief Input frequency in Hertz (dependent on crystal, see XTAL1 and XTAL2
+   * pins).
+   *
+   * Must be initialized by the user before the device creation.
+   */
+  uint32_t input_frequency;
+
+  /**
+   * @brief Corresponding Termios structure.
+   *
+   * Internal variable.
+   */
+  rtems_termios_tty *tty;
+
+  /**
+   * @brief Shadow Interrupt Enable Register (IER).
+   *
+   * Internal variable.
+   */
+  uint8_t ier;
+
+  /**
+   * @brief Characters placed into transmit FIFO.
+   *
+   * Internal variable.
+   */
+  uint8_t tx_in_progress;
+
+  /**
+   * @brief Count of free characters in the transmit FIFO.
+   *
+   * Internal variable.
+   */
+  uint8_t tx_fifo_free;
+
+  /**
+   * @brief Shadow Line Control Register (LCR).
+   *
+   * Internal variable.
+   */
+  uint8_t lcr;
+
+  /**
+   * @brief Shadow Extra Features Control Register (EFCR).
+   *
+   * Internal variable.
+   */
+  uint8_t efcr;
+};
+
+/**
+ * @brief SC16IS752 SPI context.
+ */
+typedef struct {
+  sc16is752_context base;
+
+  /**
+   * @brief The SPI bus device file descriptor.
+   *
+   * Internal variable.
+   */
+  int fd;
+
+  /**
+   * @brief The SPI device chip select.
+   *
+   * Must be initialized by the user before the call to sc16is752_spi_create().
+   */
+  uint8_t cs;
+
+  /**
+   * @brief The SPI bus speed in Hertz.
+   *
+   * Must be initialized by the user before the call to sc16is752_spi_create().
+   */
+  uint32_t speed_hz;
+
+  /**
+   * @brief The SPI bus device path.
+   *
+   * Must be initialized by the user before the call to sc16is752_spi_create().
+   */
+  const char *spi_path;
+} sc16is752_spi_context;
+
+/**
+ * @brief SC16IS752 I2C context.
+ */
+typedef struct {
+  sc16is752_context base;
+
+  /**
+   * @brief The I2C bus device file descriptor.
+   *
+   * Internal variable.
+   */
+  int fd;
+
+  /**
+   * @brief The I2C bus device path.
+   *
+   * Must be initialized before the call to sc16is752_i2c_create().
+   */
+  const char *bus_path;
+} sc16is752_i2c_context;
+
+const rtems_termios_device_handler sc16is752_termios_handler;
+
+/**
+ * @brief The interrupt handler for receive and transmit operations.
+ *
+ * @param[in] arg The device context.
+ */
+void sc16is752_interrupt_handler(void *arg);
+
+/**
+ * @brief Creates an SPI connected SC16IS752 device.
+ *
+ * @param[in] ctx The SPI SC16IS752 device context.
+ * @param[in] device_path The device file path for the new device.
+ *
+ * @retval RTEMS_SUCCESSFUL Successful operation.
+ * @retval other See rtems_termios_device_install().
+ */
+rtems_status_code sc16is752_spi_create(
+  sc16is752_spi_context *ctx,
+  const char            *device_path
+);
+
+/**
+ * @brief Enables the sleep mode if non-zero, otherwise disables it.
+ *
+ * The sleep mode is disabled by default.
+ */
+#define SC16IS752_SET_SLEEP_MODE _IOW('d', 0, int)
+
+/**
+ * @brief Returns non-zero in case the sleep mode is enabled, otherwise zero.
+ */
+#define SC16IS752_GET_SLEEP_MODE _IOR('d', 0, int)
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* _DEV_SERIAL_SC16IS752_H */
diff --git a/cpukit/dev/preinstall.am b/cpukit/dev/preinstall.am
index d2e63ba..307d0a4 100644
--- a/cpukit/dev/preinstall.am
+++ b/cpukit/dev/preinstall.am
@@ -48,6 +48,15 @@ $(PROJECT_INCLUDE)/dev/spi/spi.h: include/dev/spi/spi.h $(PROJECT_INCLUDE)/dev/s
 	$(INSTALL_DATA) $< $(PROJECT_INCLUDE)/dev/spi/spi.h
 PREINSTALL_FILES += $(PROJECT_INCLUDE)/dev/spi/spi.h
 
+$(PROJECT_INCLUDE)/dev/serial/$(dirstamp):
+	@$(MKDIR_P) $(PROJECT_INCLUDE)/dev/serial
+	@: > $(PROJECT_INCLUDE)/dev/serial/$(dirstamp)
+PREINSTALL_DIRS += $(PROJECT_INCLUDE)/dev/serial/$(dirstamp)
+
+$(PROJECT_INCLUDE)/dev/serial/sc16is752.h: include/dev/serial/sc16is752.h $(PROJECT_INCLUDE)/dev/serial/$(dirstamp)
+	$(INSTALL_DATA) $< $(PROJECT_INCLUDE)/dev/serial/sc16is752.h
+PREINSTALL_FILES += $(PROJECT_INCLUDE)/dev/serial/sc16is752.h
+
 $(PROJECT_INCLUDE)/linux/$(dirstamp):
 	@$(MKDIR_P) $(PROJECT_INCLUDE)/linux
 	@: > $(PROJECT_INCLUDE)/linux/$(dirstamp)
diff --git a/cpukit/dev/serial/sc16is752-regs.h b/cpukit/dev/serial/sc16is752-regs.h
new file mode 100644
index 0000000..8080074
--- /dev/null
+++ b/cpukit/dev/serial/sc16is752-regs.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2016 embedded brains GmbH.  All rights reserved.
+ *
+ *  embedded brains GmbH
+ *  Dornierstr. 4
+ *  82178 Puchheim
+ *  Germany
+ *  <info at embedded-brains.de>
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#ifndef LIBBSP_ARM_ATSAM_SC16IS752_H
+#define LIBBSP_ARM_ATSAM_SC16IS752_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* General register set */
+#define SC16IS752_RHR 0x0
+#define SC16IS752_THR 0x0
+#define SC16IS752_IER 0x1
+#define SC16IS752_FCR 0x2
+#define SC16IS752_IIR 0x2
+#define SC16IS752_LCR 0x3
+#define SC16IS752_MCR 0x4
+#define SC16IS752_LSR 0x5
+#define SC16IS752_MSR 0x6
+#define SC16IS752_TCR 0x6
+#define SC16IS752_SPR 0x7
+#define SC16IS752_TLR 0x7
+#define SC16IS752_TXLVL 0x8
+#define SC16IS752_RXLVL 0x9
+#define SC16IS752_IODIR 0xA
+#define SC16IS752_IOSTATE 0xB
+#define SC16IS752_IOINTENA 0xC
+#define SC16IS752_IOCONTROL 0xE
+#define SC16IS752_EFCR 0xF
+
+/* Special register set */
+#define SC16IS752_DLL 0x0
+#define SC16IS752_DLH 0x1
+
+/* Enhanced register set */
+#define SC16IS752_EFR 0x2
+#define SC16IS752_XON1 0x4
+#define SC16IS752_XON2 0x5
+#define SC16IS752_XOFF1 0x6
+#define SC16IS752_XOFF2 0x7
+
+/* FCR */
+#define FCR_FIFO_EN        0x01
+#define FCR_RX_FIFO_RST    0x02
+#define FCR_TX_FIFO_RST    0x04
+#define FCR_TX_FIFO_TRG_8  0x00
+#define FCR_TX_FIFO_TRG_16 0x10
+#define FCR_TX_FIFO_TRG_32 0x20
+#define FCR_TX_FIFO_TRG_56 0x30
+#define FCR_RX_FIFO_TRG_8  0x00
+#define FCR_RX_FIFO_TRG_16 0x40
+#define FCR_RX_FIFO_TRG_56 0x80
+#define FCR_RX_FIFO_TRG_60 0xc0
+
+/* EFCR */
+#define EFCR_RS485_ENABLE (1u << 0)
+#define EFCR_RX_DISABLE (1u << 1)
+#define EFCR_TX_DISABLE (1u << 2)
+
+/* IER */
+#define IER_RHR (1u << 0)
+#define IER_THR (1u << 1)
+#define IER_RECEIVE_LINE_STATUS (1u << 2)
+#define IER_MODEM_STATUS (1u << 3)
+#define IER_SLEEP_MODE (1u << 4)
+#define IER_XOFF (1u << 5)
+#define IER_RTS (1u << 6)
+#define IER_CTS (1u << 7)
+
+/* IIR */
+#define IIR_TX_INTERRUPT (1u << 1)
+#define IIR_RX_INTERRUPT (1u << 2)
+
+/* LCR */
+#define LCR_CHRL_5_BIT (0u << 1) | (0u << 0)
+#define LCR_CHRL_6_BIT (0u << 1) | (1u << 0)
+#define LCR_CHRL_7_BIT (1u << 1) | (0u << 0)
+#define LCR_CHRL_8_BIT (1u << 1) | (1u << 0)
+#define LCR_2_STOP_BIT (1u << 2)
+#define LCR_SET_PARITY (1u << 3)
+#define LCR_EVEN_PARITY (1u << 4)
+#define LCR_ENABLE_DIVISOR (1u << 7)
+
+/* LSR */
+#define LSR_TXEMPTY (1u << 5)
+#define LSR_RXRDY (1u << 0)
+#define LSR_ERROR_BITS (7u << 2)
+
+/* MCR */
+#define MCR_PRESCALE_NEEDED (1u << 0)
+
+/* EFR */
+#define EFR_ENHANCED_FUNC_ENABLE (1u << 4)
+#define EFR_SPECIAL_CHAR_DETECT (1u << 5)
+#define EFR_RTS_FLOW_CTRL_EN (1u << 6)
+#define EFR_CTS_FLOW_CTRL_EN (1u << 7)
+
+#define SC16IS752_FIFO_DEPTH 64
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* LIBBSP_ARM_ATSAM_SC16IS752_H */
diff --git a/cpukit/dev/serial/sc16is752-spi.c b/cpukit/dev/serial/sc16is752-spi.c
new file mode 100644
index 0000000..f51a363
--- /dev/null
+++ b/cpukit/dev/serial/sc16is752-spi.c
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2016 embedded brains GmbH.  All rights reserved.
+ *
+ *  embedded brains GmbH
+ *  Dornierstr. 4
+ *  82178 Puchheim
+ *  Germany
+ *  <info at embedded-brains.de>
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#include <dev/serial/sc16is752.h>
+
+#include <assert.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <unistd.h>
+
+#include <dev/spi/spi.h>
+
+#include "sc16is752-regs.h"
+
+static void msg_init(
+  struct spi_ioc_transfer *msg,
+  uint8_t cs,
+  uint32_t speed,
+  size_t len,
+  uint8_t *rx_buf,
+  const uint8_t *tx_buf
+)
+{
+  msg->rx_buf = rx_buf;
+  msg->tx_buf = tx_buf;
+  msg->len = len;
+  msg->speed_hz = speed;
+  msg->delay_usecs = 1;
+  msg->bits_per_word = 8;
+  msg->cs_change = 0;
+  msg->rx_nbits = 0;
+  msg->tx_nbits = 0;
+  msg->mode = 0;
+  msg->cs = cs;
+}
+
+static int sc16is752_spi_write_reg(
+  sc16is752_context *base,
+  uint8_t addr,
+  const uint8_t *data,
+  size_t len
+)
+{
+  sc16is752_spi_context *ctx = (sc16is752_spi_context *) base;
+  spi_ioc_transfer msg[2];
+  uint8_t unused[SC16IS752_FIFO_DEPTH];
+  uint8_t tx_cmd;
+
+  _Assert(len < RTEMS_ARRAY_SIZE(unused));
+
+  msg_init(&msg[0], ctx->cs, ctx->speed_hz, 1, &unused[0], &tx_cmd);
+  msg_init(&msg[1], ctx->cs, ctx->speed_hz, len, &unused[0], &data[0]);
+
+  tx_cmd = addr << 3;
+  msg[1].cs_change = 1;
+
+  return ioctl(ctx->fd, SPI_IOC_MESSAGE(2), &msg[0]);
+}
+
+static int sc16is752_spi_read_reg(
+  sc16is752_context *base,
+  uint8_t addr,
+  uint8_t *data,
+  size_t len
+)
+{
+  sc16is752_spi_context *ctx = (sc16is752_spi_context *) base;
+  spi_ioc_transfer msg[2];
+  uint8_t unused;
+  uint8_t tx_cmd;
+  uint8_t tx_buf[SC16IS752_FIFO_DEPTH];
+
+  _Assert(len < RTEMS_ARRAY_SIZE(tx_buf));
+
+  msg_init(&msg[0], ctx->cs, ctx->speed_hz, 1, &unused, &tx_cmd);
+  msg_init(&msg[1], ctx->cs, ctx->speed_hz, len, &data[0], &tx_buf[0]);
+
+  tx_cmd = 0x80 | (addr << 3);
+  msg[1].cs_change = 1;
+
+  return ioctl(ctx->fd, SPI_IOC_MESSAGE(2), &msg[0]);
+}
+
+static int sc16is752_spi_read_2_reg(
+  sc16is752_context *base,
+  uint8_t addr_0,
+  uint8_t addr_1,
+  uint8_t data[2]
+)
+{
+  sc16is752_spi_context *ctx = (sc16is752_spi_context *) base;
+  spi_ioc_transfer msg[4];
+  uint8_t unused;
+  uint8_t tx_cmd_0;
+  uint8_t tx_cmd_1;
+
+  msg_init(&msg[0], ctx->cs, ctx->speed_hz, 1, &unused, &tx_cmd_0);
+  msg_init(&msg[1], ctx->cs, ctx->speed_hz, 1, &data[0], &unused);
+  msg_init(&msg[2], ctx->cs, ctx->speed_hz, 1, &unused, &tx_cmd_1);
+  msg_init(&msg[3], ctx->cs, ctx->speed_hz, 1, &data[1], &unused);
+
+  tx_cmd_0 = 0x80 | (addr_0 << 3);
+  tx_cmd_1 = 0x80 | (addr_1 << 3);
+  msg[1].cs_change = 1;
+  msg[3].cs_change = 1;
+
+  return ioctl(ctx->fd, SPI_IOC_MESSAGE(4), &msg[0]);
+}
+
+static bool sc16is752_spi_first_open(sc16is752_context *base)
+{
+  sc16is752_spi_context *ctx = (sc16is752_spi_context *) base;
+  ctx->fd = open(ctx->spi_path, O_RDWR);
+  return ctx->fd >= 0;
+}
+
+static void sc16is752_spi_last_close(sc16is752_context *base)
+{
+  sc16is752_spi_context *ctx = (sc16is752_spi_context *) base;
+  close(ctx->fd);
+}
+
+rtems_status_code sc16is752_spi_create(
+  sc16is752_spi_context *ctx,
+  const char *device_path
+)
+{
+  ctx->base.write_reg = sc16is752_spi_write_reg;
+  ctx->base.read_reg = sc16is752_spi_read_reg;
+  ctx->base.read_2_reg = sc16is752_spi_read_2_reg;
+  ctx->base.first_open = sc16is752_spi_first_open;
+  ctx->base.last_close = sc16is752_spi_last_close;
+
+  return rtems_termios_device_install(
+    device_path,
+    &sc16is752_termios_handler,
+    NULL,
+    &ctx->base.base
+  );
+}
diff --git a/cpukit/dev/serial/sc16is752.c b/cpukit/dev/serial/sc16is752.c
new file mode 100644
index 0000000..96fac30
--- /dev/null
+++ b/cpukit/dev/serial/sc16is752.c
@@ -0,0 +1,330 @@
+/*
+ * Copyright (c) 2016 embedded brains GmbH.  All rights reserved.
+ *
+ *  embedded brains GmbH
+ *  Dornierstr. 4
+ *  82178 Puchheim
+ *  Germany
+ *  <rtems at embedded-brains.de>
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+
+#include <dev/serial/sc16is752.h>
+
+#include <sys/param.h>
+
+#include <assert.h>
+#include <stdio.h>
+#include <fcntl.h>
+
+#include <rtems/seterr.h>
+
+#include "sc16is752-regs.h"
+
+static void write_reg(
+  sc16is752_context *ctx,
+  uint8_t addr,
+  const uint8_t *data,
+  size_t len
+)
+{
+  (*ctx->write_reg)(ctx, addr, data, len);
+}
+
+static void read_reg(
+  sc16is752_context *ctx,
+  uint8_t addr,
+  uint8_t *data,
+  size_t len
+)
+{
+  (*ctx->read_reg)(ctx, addr, data, len);
+}
+
+static void read_2_reg(
+  sc16is752_context *ctx,
+  uint8_t addr_0,
+  uint8_t addr_1,
+  uint8_t data[2]
+)
+{
+  (*ctx->read_2_reg)(ctx, addr_0, addr_1, data);
+}
+
+static bool is_sleep_mode_enabled(sc16is752_context *ctx)
+{
+  return (ctx->ier & IER_SLEEP_MODE) != 0;
+}
+
+static void set_sleep_mode(sc16is752_context *ctx, bool enable)
+{
+  if (enable) {
+    ctx->ier |= IER_SLEEP_MODE;
+  } else {
+    ctx->ier &= ~IER_SLEEP_MODE;
+  }
+
+  write_reg(ctx, SC16IS752_IER, &ctx->ier, 1);
+}
+
+static void set_mcr_dll_dlh(
+  sc16is752_context *ctx,
+  uint8_t mcr,
+  uint32_t divisor
+)
+{
+  bool sleep_mode = is_sleep_mode_enabled(ctx);
+  uint8_t dll = (uint8_t)divisor;
+  uint8_t dlh = (uint8_t)(divisor >> 8);
+
+  if (sleep_mode) {
+    set_sleep_mode(ctx, false);
+  }
+
+  ctx->lcr |= LCR_ENABLE_DIVISOR;
+  write_reg(ctx, SC16IS752_LCR, &ctx->lcr, 1);
+
+  write_reg(ctx, SC16IS752_MCR, &mcr, 1);
+  write_reg(ctx, SC16IS752_DLH, &dlh, 1);
+  write_reg(ctx, SC16IS752_DLL, &dll, 1);
+
+  ctx->lcr &= ~LCR_ENABLE_DIVISOR;
+  write_reg(ctx, SC16IS752_LCR, &ctx->lcr, 1);
+
+  if (sleep_mode) {
+    set_sleep_mode(ctx, true);
+  }
+}
+
+static void set_efr(sc16is752_context *ctx, uint8_t efr)
+{
+  uint8_t lcr = ctx->lcr;
+
+  ctx->lcr = 0xbf;
+  write_reg(ctx, SC16IS752_LCR, &ctx->lcr, 1);
+
+  write_reg(ctx, SC16IS752_EFR, &efr, 1);
+
+  ctx->lcr = lcr;
+  write_reg(ctx, SC16IS752_LCR, &ctx->lcr, 1);
+}
+
+static bool set_baud(sc16is752_context *ctx, rtems_termios_baud_t baud)
+{
+  uint32_t freq = ctx->input_frequency;
+  uint8_t mcr;
+  uint32_t divisor;
+
+  read_reg(ctx, SC16IS752_MCR, &mcr, 1);
+
+  divisor = freq / baud / 16;
+  if (divisor > 0xFFFF){
+    divisor = (freq / (4 * baud)) / 16;
+    if (divisor > 0xFFFF){
+      return false;
+    } else {
+      mcr |= MCR_PRESCALE_NEEDED;
+    }
+  } else {
+    mcr &= ~MCR_PRESCALE_NEEDED;
+  }
+
+  set_mcr_dll_dlh(ctx, mcr, divisor);
+  return true;
+}
+
+static bool sc16is752_set_attributes(
+  rtems_termios_device_context *base,
+  const struct termios *term
+)
+{
+  sc16is752_context *ctx = (sc16is752_context *)base;
+  bool baud_successful;
+  rtems_termios_baud_t baud;
+
+  ctx->lcr = 0;
+
+  baud = rtems_termios_baud_to_number(term->c_cflag);
+  baud_successful = set_baud(ctx, baud);
+  if (!baud_successful){
+    return false;
+  }
+
+  if ((term->c_cflag & CREAD) == 0){
+    ctx->efcr |= EFCR_RX_DISABLE;
+  } else {
+    ctx->efcr &= ~EFCR_RX_DISABLE;
+  }
+
+  write_reg(ctx, SC16IS752_EFCR, &ctx->efcr, 1);
+
+  switch (term->c_cflag & CSIZE) {
+    case CS5:
+      ctx->lcr |= LCR_CHRL_5_BIT;
+      break;
+    case CS6:
+      ctx->lcr |= LCR_CHRL_6_BIT;
+      break;
+    case CS7:
+      ctx->lcr |= LCR_CHRL_7_BIT;
+      break;
+    case CS8:
+      ctx->lcr |= LCR_CHRL_8_BIT;
+      break;
+  }
+
+  if ((term->c_cflag & PARENB) != 0){
+    if ((term->c_cflag & PARODD) != 0) {
+      ctx->lcr &= ~LCR_EVEN_PARITY;
+    } else {
+      ctx->lcr |= LCR_EVEN_PARITY;
+    }
+  } else {
+    ctx->lcr &= ~LCR_SET_PARITY;
+  }
+
+  if ((term->c_cflag & CSTOPB) != 0) {
+    ctx->lcr |= LCR_2_STOP_BIT;
+  } else {
+    ctx->lcr &= ~LCR_2_STOP_BIT;
+  }
+
+  write_reg(ctx, SC16IS752_LCR, &ctx->lcr, 1);
+  return true;
+}
+
+static bool sc16is752_first_open(
+  rtems_termios_tty *tty,
+  rtems_termios_device_context *base,
+  struct termios *term,
+  rtems_libio_open_close_args_t *args
+)
+{
+  bool ok;
+  uint8_t fcr;
+
+  (void)args;
+  sc16is752_context *ctx = (sc16is752_context *)base;
+
+  ctx->tty = tty;
+
+  ok = (*ctx->first_open)(ctx);
+  if (!ok) {
+    return ok;
+  }
+
+  if (ctx->mode == SC16IS752_MODE_RS485) {
+    ctx->efcr = EFCR_RS485_ENABLE;
+  } else {
+    ctx->efcr = 0;
+  }
+
+  write_reg(ctx, SC16IS752_FCR, &ctx->efcr, 1);
+
+  fcr = FCR_FIFO_EN | FCR_RX_FIFO_RST | FCR_TX_FIFO_RST
+    | FCR_RX_FIFO_TRG_16 | FCR_TX_FIFO_TRG_32;
+  write_reg(ctx, SC16IS752_FCR, &fcr, 1);
+
+  ctx->ier = IER_RHR;
+  write_reg(ctx, SC16IS752_IER, &ctx->ier, 1);
+  set_efr(ctx, EFR_ENHANCED_FUNC_ENABLE);
+
+  rtems_termios_set_initial_baud(tty, 115200);
+  sc16is752_set_attributes(base, term);
+
+  (*ctx->install_irq)(ctx);
+
+  return true;
+}
+
+static void sc16is752_last_close(
+  rtems_termios_tty *tty,
+  rtems_termios_device_context *base,
+  rtems_libio_open_close_args_t *args
+)
+{
+  sc16is752_context *ctx = (sc16is752_context *)base;
+
+  (void)tty;
+  (void)args;
+  (*ctx->last_close)(ctx);
+}
+
+static void sc16is752_write(
+  rtems_termios_device_context *base,
+  const char *buf,
+  size_t len
+)
+{
+  sc16is752_context *ctx = (sc16is752_context *)base;
+
+  if (len > 0) {
+    ctx->ier |= IER_THR;
+    len = MIN(len, 32);
+    ctx->tx_in_progress = (uint8_t)len;
+    write_reg(ctx, SC16IS752_THR, (const uint8_t *)&buf[0], len);
+    write_reg(ctx, SC16IS752_IER, &ctx->ier, 1);
+  } else {
+    ctx->tx_in_progress = 0;
+    ctx->ier &= ~IER_THR;
+    write_reg(ctx, SC16IS752_IER, &ctx->ier, 1);
+  }
+}
+
+static int sc16is752_ioctl(
+  rtems_termios_device_context *base,
+  ioctl_command_t               request,
+  void                         *buffer
+)
+{
+  sc16is752_context *ctx = (sc16is752_context *)base;
+
+  switch (request) {
+    case SC16IS752_SET_SLEEP_MODE:
+      set_sleep_mode(ctx, *(int *)buffer != 0);
+      break;
+    case SC16IS752_GET_SLEEP_MODE:
+      *(int *)buffer = is_sleep_mode_enabled(ctx);
+      break;
+    default:
+      rtems_set_errno_and_return_minus_one(EINVAL);
+  }
+
+  return 0;
+}
+
+const rtems_termios_device_handler sc16is752_termios_handler = {
+  .first_open = sc16is752_first_open,
+  .last_close = sc16is752_last_close,
+  .write = sc16is752_write,
+  .set_attributes = sc16is752_set_attributes,
+  .ioctl = sc16is752_ioctl,
+  .mode = TERMIOS_IRQ_SERVER_DRIVEN
+};
+
+void sc16is752_interrupt_handler(void *arg)
+{
+  sc16is752_context *ctx = (sc16is752_context *)arg;
+  uint8_t data[2];
+  uint8_t iir;
+
+  read_2_reg(ctx, SC16IS752_IIR, SC16IS752_RXLVL, data);
+  iir = data[0];
+
+  if ((iir & IIR_TX_INTERRUPT) != 0 && ctx->tx_in_progress > 0) {
+    rtems_termios_dequeue_characters(ctx->tty, ctx->tx_in_progress);
+  }
+
+  if ((iir & IIR_RX_INTERRUPT) != 0) {
+    uint8_t buf[SC16IS752_FIFO_DEPTH];
+    uint8_t rxlvl = data[1];
+
+    rxlvl = MIN(rxlvl, SC16IS752_FIFO_DEPTH);
+    read_reg(ctx, SC16IS752_RHR, &buf[0], rxlvl);
+    rtems_termios_enqueue_raw_characters(ctx->tty, (const char *)&buf[0], rxlvl);
+  }
+}




More information about the vc mailing list