--- cpukit/dev/Makefile.am | 7 + cpukit/dev/include/dev/serial/nxp-sc16is752.h | 68 ++++++ cpukit/dev/preinstall.am | 9 + cpukit/dev/serial/nxp-sc16is752-regs.h | 122 ++++++++++ cpukit/dev/serial/nxp-sc16is752-spi.c | 90 +++++++ cpukit/dev/serial/nxp-sc16is752.c | 324 ++++++++++++++++++++++++++ 6 files changed, 620 insertions(+) create mode 100644 cpukit/dev/include/dev/serial/nxp-sc16is752.h create mode 100644 cpukit/dev/serial/nxp-sc16is752-regs.h create mode 100644 cpukit/dev/serial/nxp-sc16is752-spi.c create mode 100644 cpukit/dev/serial/nxp-sc16is752.c
diff --git a/cpukit/dev/Makefile.am b/cpukit/dev/Makefile.am index 2b65390..cdc3fe8 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/nxp-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/nxp-sc16is752.c +libdev_a_SOURCES += serial/nxp-sc16is752-spi.c +libdev_a_SOURCES += serial/nxp-sc16is752-regs.h include $(srcdir)/preinstall.am include $(top_srcdir)/automake/local.am diff --git a/cpukit/dev/include/dev/serial/nxp-sc16is752.h b/cpukit/dev/include/dev/serial/nxp-sc16is752.h new file mode 100644 index 0000000..2566ebe --- /dev/null +++ b/cpukit/dev/include/dev/serial/nxp-sc16is752.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2016 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Dornierstr. 4 + * 82178 Puchheim + * Germany + * <i...@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 <rtems/termiostypes.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* General device paths */ +#define SC16IS752_PATH "/dev/ttyUARTSC16IS752" + +typedef struct { + const char *spi_nxp_path; + uint8_t cs; + uint32_t speed_hz; +} sc16is752_reg_ctx; + +typedef struct { + rtems_termios_device_context base; + sc16is752_reg_ctx *reg_ctx; + int (*write_reg)(int fd, + sc16is752_reg_ctx *reg_ctx, + uint8_t addr, + uint8_t data + ); + int (*read_reg)(int fd, + sc16is752_reg_ctx *reg_ctx, + uint8_t addr, + uint8_t *data + ); + uint32_t input_frequency; + rtems_vector_number irq; + int fd; + bool rs485_wanted; +#ifdef SC16IS752_USE_INTERRUPTS + bool transmitting; +#endif +} sc16is752_uart_ctx; + +rtems_status_code sc16is752_initialize( + sc16is752_uart_ctx *uart_ctx, + sc16is752_reg_ctx *reg_ctx, + uint32_t input_frequency, + rtems_vector_number irq, + bool rs485_wanted, + void *arg +); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _DEV_SERIAL_SC16IS752_H */ diff --git a/cpukit/dev/preinstall.am b/cpukit/dev/preinstall.am index d2e63ba..801bdb5 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/nxp-sc16is752.h: include/dev/serial/nxp-sc16is752.h $(PROJECT_INCLUDE)/dev/serial/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/dev/serial/nxp-sc16is752.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/dev/serial/nxp-sc16is752.h + $(PROJECT_INCLUDE)/linux/$(dirstamp): @$(MKDIR_P) $(PROJECT_INCLUDE)/linux @: > $(PROJECT_INCLUDE)/linux/$(dirstamp) diff --git a/cpukit/dev/serial/nxp-sc16is752-regs.h b/cpukit/dev/serial/nxp-sc16is752-regs.h new file mode 100644 index 0000000..0b1c024 --- /dev/null +++ b/cpukit/dev/serial/nxp-sc16is752-regs.h @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2016 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Dornierstr. 4 + * 82178 Puchheim + * Germany + * <i...@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 + +#include <dev/serial/nxp-sc16is752.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* General device paths */ +#define SC16IS752_PATH "/dev/ttyUARTSC16IS752" + +/* 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 + +/* EFCR Bit masks */ +#define EFCR_RS485ENABLE (1 << 0) +#define EFCR_RXENABLE (0 << 1) +#define EFCR_TXENABLE (0 << 2) + +/* IER Bit masks */ +#define IER_ENABLE_RHR_INTERRUPT (1u << 0) +#define IER_ENABLE_THR_INTERRUPT (1u << 1) + +/* LCR Bit masks */ +#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) +#define LCR_ENABLE_DIVISOR (1u << 7) + +/* LSR Bit masks */ +#define LSR_TXEMPTY (1u << 5) +#define LSR_RXRDY (1u << 0) +#define LSR_ERROR_BITS (7u << 2) + +/* MCR Bit masks */ +#define MCR_PRESCALE_NEEDED (1u << 0) + +/** + * @brief Writes a value to a register specified by an offset. + * + * @param[in] reg_addr The register address expressed as an offset. + * @param[in] reg_val The register value that shall be written to the specified + * address. + * @param[in] msg The message that shall be sent. + */ +int sc16is752_write_register ( + int fd, + sc16is752_reg_ctx *ctx, + uint8_t reg_addr, + const uint8_t reg_val +); + +/** + * @brief Reads a value from a register specified by an offset. + * + * @param[in] reg_addr The register address expressed as an offset. + * @param[out] reg_val The register value that shall be read from the specified + * address. + * @param[in] msg The message that shall be sent. + */ +int sc16is752_read_register ( + int fd, + sc16is752_reg_ctx *ctx, + uint8_t reg_addr, + uint8_t *reg_val +); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* LIBBSP_ARM_ATSAM_SC16IS752_H */ diff --git a/cpukit/dev/serial/nxp-sc16is752-spi.c b/cpukit/dev/serial/nxp-sc16is752-spi.c new file mode 100644 index 0000000..65f7070 --- /dev/null +++ b/cpukit/dev/serial/nxp-sc16is752-spi.c @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2016 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Dornierstr. 4 + * 82178 Puchheim + * Germany + * <i...@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 <assert.h> +#include <fcntl.h> +#include <inttypes.h> +#include <unistd.h> + +#include <bsp/spi.h> +#include <bsp/atsam-spi.h> +#include "nxp-sc16is752-regs.h" + +static void set_default_msg( + struct spi_ioc_transfer *msg, + uint8_t cs, + uint32_t speed, + uint8_t *rx_buf, + uint8_t *tx_buf +) +{ + msg->rx_buf = rx_buf; + msg->tx_buf = tx_buf; + msg->len = REGISTER_COMMAND_LENGTH; + msg->speed_hz = speed; + msg->delay_usecs = 1; + msg->bits_per_word = 8; + msg->cs_change = 1; + msg->rx_nbits = 0; + msg->tx_nbits = 0; + msg->mode = 0; + msg->cs = cs; +} + + +int sc16is752_write_register( + int fd, + sc16is752_reg_ctx *ctx, + uint8_t reg_addr, + const uint8_t reg_val +) +{ + int rv; + spi_ioc_transfer msg; + uint8_t tx_buf[REGISTER_COMMAND_LENGTH]; + uint8_t rx_buf[REGISTER_COMMAND_LENGTH]; + + tx_buf[0] = reg_addr << 3; + tx_buf[1] = reg_val; + + set_default_msg(&msg, ctx->cs, ctx->speed_hz, &rx_buf[0], &tx_buf[0]); + + rv = ioctl(fd, SPI_IOC_MESSAGE(1), &msg); + + return rv; +} + +int sc16is752_read_register( + int fd, + sc16is752_reg_ctx *ctx, + uint8_t reg_addr, + uint8_t *reg_val +) +{ + int rv; + spi_ioc_transfer msg; + uint8_t tx_buf[REGISTER_COMMAND_LENGTH]; + uint8_t rx_buf[REGISTER_COMMAND_LENGTH]; + + tx_buf[0] = 0x80 | reg_addr << 3; + tx_buf[1] = 0x00; + + set_default_msg(&msg, ctx->cs, ctx->speed_hz, &rx_buf[0], &tx_buf[0]); + + rv = ioctl(fd, SPI_IOC_MESSAGE(1), &msg); + *reg_val = rx_buf[1]; + + return rv; +} + diff --git a/cpukit/dev/serial/nxp-sc16is752.c b/cpukit/dev/serial/nxp-sc16is752.c new file mode 100644 index 0000000..f6435e8 --- /dev/null +++ b/cpukit/dev/serial/nxp-sc16is752.c @@ -0,0 +1,324 @@ +/* + * Copyright (c) 2016 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Dornierstr. 4 + * 82178 Puchheim + * Germany + * <rt...@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 <fcntl.h> + +#include <bsp/atsam-spi.h> +#include <bsp/spi.h> +#include <dev/serial/nxp-sc16is752.h> +#include "nxp-sc16is752-regs.h" + +#include <rtems/termiostypes.h> + +static void sc16is752_uart_set_baud_rate( + sc16is752_uart_ctx *ctx, + uint32_t value_mcr, + uint32_t divisor +) +{ + uint8_t value_lcr; + + ctx->read_reg(ctx->fd, ctx->reg_ctx, SC16IS752_LCR, &value_lcr); + value_lcr |= LCR_ENABLE_DIVISOR; + + ctx->write_reg(ctx->fd, ctx->reg_ctx, SC16IS752_LCR, value_lcr); + ctx->write_reg(ctx->fd, ctx->reg_ctx, SC16IS752_MCR, value_mcr); + ctx->write_reg(ctx->fd, ctx->reg_ctx, SC16IS752_DLH, divisor >> 8); + ctx->write_reg(ctx->fd, ctx->reg_ctx, SC16IS752_DLL, divisor); + value_lcr &= ~LCR_ENABLE_DIVISOR; + ctx->write_reg(ctx->fd, ctx->reg_ctx, SC16IS752_LCR, value_lcr); +} + +static bool sc16is752_uart_prepare_baud_rate( + sc16is752_uart_ctx *ctx, + rtems_termios_baud_t baud +) +{ + uint32_t freq = ctx->input_frequency; + uint8_t value_mcr; + uint32_t divisor; + + ctx->read_reg(ctx->fd, ctx->reg_ctx, SC16IS752_MCR, &value_mcr); + + divisor = freq / baud / 16; + if (divisor > 0xFFFF){ + divisor = (freq / (4 * baud)) / 16; + if (divisor > 0xFFFF){ + return false; + } else { + value_mcr |= MCR_PRESCALE_NEEDED; + sc16is752_uart_set_baud_rate(ctx, value_mcr, divisor); + } + } else { + value_mcr &= ~MCR_PRESCALE_NEEDED; + sc16is752_uart_set_baud_rate(ctx, value_mcr, divisor); + } + return true; +} + +#ifdef SC16IS752_USE_INTERRUPTS +static void sc16is752_uart_interrupt(void *arg) +{ + rtems_termios_tty *tty = arg; + sc16is752_uart_ctx *ctx = rtems_termios_get_device_context(tty); + uint8_t value_lsr; + uint8_t value_rhr; + + ctx->read_reg(ctx->fd, ctx->reg_ctx, SC16IS752_LSR, &value_lsr); + + while ((value & LSR_RXRDY) != 0) { + char c; + ctx->read_reg(ctx->fd, ctx->reg_ctx, SC16IS752_RHR, &value_rhr); + c = (char) value_rhr; + + rtems_termios_enqueue_raw_characters(tty, &c, 1); + + ctx->read_reg(ctx->fd, ctx->reg_ctx, SC16IS752_LSR, &value_lsr); + } + + if (ctx->transmitting && (value_lsr & LSR_TXEMPTY) != 0) { + rtems_termios_dequeue_characters(tty, 1); + } +} +#endif + +static bool sc16is752_uart_set_attributes( + rtems_termios_device_context *base, + const struct termios *term +) +{ + sc16is752_uart_ctx *ctx = (sc16is752_uart_ctx *) base; + uint8_t lcr_value = 0; + uint8_t efcr_value = 0; + bool baud_successful; + rtems_termios_baud_t baud; + + baud = rtems_termios_baud_to_number(term->c_cflag); + baud_successful = sc16is752_uart_prepare_baud_rate(ctx, baud); + if (!baud_successful){ + return false; + } + + if (ctx->rs485_wanted){ + efcr_value = EFCR_RS485ENABLE; + } + + if ((term->c_cflag & CREAD) != 0){ + efcr_value |= EFCR_RXENABLE | EFCR_TXENABLE; + } else { + efcr_value |= EFCR_TXENABLE; + } + ctx->write_reg(ctx->fd, ctx->reg_ctx, SC16IS752_EFCR, efcr_value); + + switch (term->c_cflag & CSIZE) { + case CS5: + lcr_value |= LCR_CHRL_5_BIT; + break; + case CS6: + lcr_value |= LCR_CHRL_6_BIT; + break; + case CS7: + lcr_value |= LCR_CHRL_7_BIT; + break; + case CS8: + lcr_value |= LCR_CHRL_8_BIT; + break; + } + + if ((term->c_cflag & PARENB) != 0){ + if ((term->c_cflag & PARODD) != 0) { + lcr_value &= ~LCR_EVEN_PARITY; + } else { + lcr_value |= LCR_EVEN_PARITY; + } + } else { + lcr_value &= ~LCR_SET_PARITY; + } + + if ((term->c_cflag & CSTOPB) != 0) { + lcr_value |= LCR_2_STOP_BIT; + } else { + lcr_value &= ~LCR_2_STOP_BIT; + } + + ctx->write_reg(ctx->fd, ctx->reg_ctx, SC16IS752_LCR, lcr_value); + + return true; +} + +static bool sc16is752_uart_first_open( + rtems_termios_tty *tty, + rtems_termios_device_context *base, + struct termios *term, + rtems_libio_open_close_args_t *args +) +{ + (void)args; + sc16is752_uart_ctx *ctx = (sc16is752_uart_ctx *) base; +#ifdef SC16IS752_USE_INTERRUPTS + rtems_status_code sc; +#endif + ctx->fd = open(ATSAM_SPI_0_BUS_PATH, O_RDWR); + if (ctx->fd < 0) { + return false; + } + + ctx->write_reg(ctx->fd, ctx->reg_ctx, SC16IS752_FCR, 0x7); + ctx->write_reg(ctx->fd, ctx->reg_ctx, SC16IS752_IER, 0x0); + + rtems_termios_set_initial_baud(tty, ATSAM_CONSOLE_BAUD); + sc16is752_uart_set_attributes(base, term); + +#ifdef SC16IS752_USE_INTERRUPTS + ctx->read_reg(ctx->fd, ctx->reg_ctx, SC16IS752_IER, &value); + value |= IER_ENABLE_RHR_INTERRUPT; + ctx->write_reg(ctx->fd, ctx->reg_ctx, SC16IS752_IER, value); + sc = rtems_interrupt_handler_install( + ctx->irq, + "SC16IS752", + RTEMS_INTERRUPT_SHARED, + sc16is752_uart_interrupt, + tty + ); + if (sc != RTEMS_SUCCESSFUL) { + return false; + } +#endif + + return true; +} + +static void sc16is752_uart_last_close( + rtems_termios_tty *tty, + rtems_termios_device_context *base, + rtems_libio_open_close_args_t *args +) +{ + (void) tty; + (void) args; + sc16is752_uart_ctx *ctx = (sc16is752_uart_ctx *) base; +#ifdef SC16IS752_USE_INTERRUPTS + rtems_interrupt_handler_remove(ctx->irq, sc16is752_uart_interrupt, tty); +#endif + close(ctx->fd); +} + +static void sc16is752_uart_write( + rtems_termios_device_context *base, + const char *buf, + size_t len +) +{ + uint8_t value; + + sc16is752_uart_ctx *ctx = (sc16is752_uart_ctx *) base; + +#ifdef SC16IS752_USE_INTERRUPTS + if (len > 0) { + ctx->transmitting = true; + ctx->write_reg(ctx->fd, ctx->reg_ctx, SC16IS752_THR, buf[0]); + ctx->write_reg( + ctx->fd, + ctx->reg_ctx, + SC16IS752_IER, + IER_ENABLE_THR_INTERRUPT + ); + } else { + ctx->transmitting = false; + ctx->write_reg( + ctx->fd, + ctx->reg_ctx, + SC16IS752_IER, + IER_DISABLE_THR_INTERRUPT + ); + } +#else + size_t i; + + for (i = 0; i < len; i++) { + do{ + ctx->read_reg(ctx->fd, ctx->reg_ctx, SC16IS752_LSR, &value); + } while ((value & LSR_TXEMPTY) == 0); + + ctx->write_reg(ctx->fd, ctx->reg_ctx, SC16IS752_THR, buf[i]); + } +#endif +} + +#ifndef SC16IS752_USE_INTERRUPTS +static int sc16is752_uart_read(rtems_termios_device_context *base) +{ + uint8_t status; + uint8_t rhr; + uint8_t rx_amount; + + sc16is752_uart_ctx *ctx = (sc16is752_uart_ctx *) base; + + ctx->read_reg(ctx->fd, ctx->reg_ctx, SC16IS752_RXLVL, &rx_amount); + + ctx->read_reg(ctx->fd, ctx->reg_ctx, SC16IS752_LSR, &status); + if (((status & LSR_RXRDY) != 0) && ((status & LSR_ERROR_BITS) == 0)) { + ctx->read_reg(ctx->fd, ctx->reg_ctx, SC16IS752_RHR, &rhr); + return (char) rhr; + } else { + return -1; + } +} +#endif + +static const rtems_termios_device_handler sc16is752_uart_handler = { + .first_open = sc16is752_uart_first_open, + .last_close = sc16is752_uart_last_close, + .write = sc16is752_uart_write, + .set_attributes = sc16is752_uart_set_attributes, +#ifdef SC16IS752_USE_INTERRUPTS + .mode = TERMIOS_IRQ_DRIVEN +#else + .poll_read = sc16is752_uart_read, + .mode = TERMIOS_POLLED +#endif +}; + +rtems_status_code sc16is752_initialize( + sc16is752_uart_ctx *uart_ctx, + sc16is752_reg_ctx *reg_ctx, + uint32_t input_frequency, + rtems_vector_number irq, + bool rs485_wanted, + void *arg +) +{ + (void)arg; + + rtems_termios_initialize(); + + uart_ctx->reg_ctx = reg_ctx; + uart_ctx->write_reg = sc16is752_write_register; + uart_ctx->read_reg = sc16is752_read_register; + uart_ctx->input_frequency = input_frequency; + uart_ctx->irq = irq; + + char uart_sc16is752[] = "/dev/ttyUARTSC16IS752"; + + rtems_termios_device_install( + &uart_sc16is752[0], + &sc16is752_uart_handler, + NULL, + &uart_ctx->base + ); + + uart_ctx->rs485_wanted = rs485_wanted; + + return RTEMS_SUCCESSFUL; +} -- 1.8.4.5 _______________________________________________ devel mailing list devel@rtems.org http://lists.rtems.org/mailman/listinfo/devel