dev: Add NXP SC16IS752 serial device driver

Update #2841.
This commit is contained in:
Alexander Krutwig
2016-09-16 15:21:26 +02:00
committed by Sebastian Huber
parent e34fe384cb
commit 9edc73013b
6 changed files with 875 additions and 0 deletions

View File

@@ -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

View File

@@ -0,0 +1,262 @@
/*
* Copyright (c) 2016 embedded brains GmbH. All rights reserved.
*
* embedded brains GmbH
* Dornierstr. 4
* 82178 Puchheim
* Germany
* <info@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 */

View File

@@ -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)

View File

@@ -0,0 +1,116 @@
/*
* Copyright (c) 2016 embedded brains GmbH. All rights reserved.
*
* embedded brains GmbH
* Dornierstr. 4
* 82178 Puchheim
* Germany
* <info@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 */

View File

@@ -0,0 +1,151 @@
/*
* Copyright (c) 2016 embedded brains GmbH. All rights reserved.
*
* embedded brains GmbH
* Dornierstr. 4
* 82178 Puchheim
* Germany
* <info@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
);
}

View File

@@ -0,0 +1,330 @@
/*
* Copyright (c) 2016 embedded brains GmbH. All rights reserved.
*
* embedded brains GmbH
* Dornierstr. 4
* 82178 Puchheim
* Germany
* <rtems@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);
}
}