summaryrefslogtreecommitdiffstats
path: root/bsps/shared/dev/serial/ns16550.c
diff options
context:
space:
mode:
authorSebastian Huber <sebastian.huber@embedded-brains.de>2018-04-03 07:20:11 +0200
committerSebastian Huber <sebastian.huber@embedded-brains.de>2018-04-04 10:13:28 +0200
commit27de4e1fb8bcdbdd8cb882fc0d7a2c152b4e027a (patch)
treedef0664dcddc53fd5d599b455c64f76ca2293606 /bsps/shared/dev/serial/ns16550.c
parentbsps: Move config macros to RTEMS_BSP_CONFIGURE (diff)
downloadrtems-27de4e1fb8bcdbdd8cb882fc0d7a2c152b4e027a.tar.bz2
bsps: Move libchip to bsps
This patch is a part of the BSP source reorganization. Update #3285.
Diffstat (limited to 'bsps/shared/dev/serial/ns16550.c')
-rw-r--r--bsps/shared/dev/serial/ns16550.c875
1 files changed, 875 insertions, 0 deletions
diff --git a/bsps/shared/dev/serial/ns16550.c b/bsps/shared/dev/serial/ns16550.c
new file mode 100644
index 0000000000..b1e5892c15
--- /dev/null
+++ b/bsps/shared/dev/serial/ns16550.c
@@ -0,0 +1,875 @@
+/**
+ * @file
+ *
+ * This file contains the TTY driver for the National Semiconductor NS16550.
+ *
+ * This part is widely cloned and second sourced. It is found in a number
+ * of "Super IO" controllers.
+ *
+ * This driver uses the termios pseudo driver.
+ */
+
+/*
+ * COPYRIGHT (c) 1998 by Radstone Technology
+ *
+ * THIS FILE IS PROVIDED TO YOU, THE USER, "AS IS", WITHOUT WARRANTY OF ANY
+ * KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTY OF FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK
+ * AS TO THE QUALITY AND PERFORMANCE OF ALL CODE IN THIS FILE IS WITH YOU.
+ *
+ * You are hereby granted permission to use, copy, modify, and distribute
+ * this file, provided that this notice, plus the above copyright notice
+ * and disclaimer, appears in all copies. Radstone Technology will provide
+ * no support for this code.
+ *
+ * COPYRIGHT (c) 1989-2012.
+ * On-Line Applications Research Corporation (OAR).
+ *
+ * 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 <stdlib.h>
+
+#include <rtems.h>
+#include <rtems/libio.h>
+#include <rtems/ringbuf.h>
+#include <rtems/bspIo.h>
+#include <rtems/termiostypes.h>
+
+#include <libchip/serial.h>
+#include <libchip/sersupp.h>
+
+#include <bsp.h>
+
+#include <libchip/ns16550_p.h>
+#include <libchip/ns16550.h>
+
+#if defined(BSP_FEATURE_IRQ_EXTENSION)
+ #include <bsp/irq.h>
+#elif defined(BSP_FEATURE_IRQ_LEGACY)
+ #include <bsp/irq.h>
+#elif defined(__PPC__) || defined(__i386__)
+ #include <bsp/irq.h>
+ #define BSP_FEATURE_IRQ_LEGACY
+ #ifdef BSP_SHARED_HANDLER_SUPPORT
+ #define BSP_FEATURE_IRQ_LEGACY_SHARED_HANDLER_SUPPORT
+ #endif
+#endif
+
+typedef struct {
+ uint8_t ucModemCtrl;
+ int transmitFifoChars;
+} NS16550Context;
+
+/*
+ * Driver functions
+ */
+
+NS16550_STATIC void ns16550_init(int minor);
+
+NS16550_STATIC int ns16550_open(
+ int major,
+ int minor,
+ void * arg
+);
+
+NS16550_STATIC int ns16550_close(
+ int major,
+ int minor,
+ void * arg
+);
+
+NS16550_STATIC void ns16550_write_polled(
+ int minor,
+ char cChar
+);
+
+NS16550_STATIC int ns16550_assert_RTS(
+ int minor
+);
+
+NS16550_STATIC int ns16550_negate_RTS(
+ int minor
+);
+
+NS16550_STATIC int ns16550_assert_DTR(
+ int minor
+);
+
+NS16550_STATIC int ns16550_negate_DTR(
+ int minor
+);
+
+NS16550_STATIC void ns16550_initialize_interrupts(int minor);
+
+NS16550_STATIC void ns16550_cleanup_interrupts(int minor);
+
+NS16550_STATIC ssize_t ns16550_write_support_int(
+ int minor,
+ const char *buf,
+ size_t len
+);
+
+NS16550_STATIC ssize_t ns16550_write_support_polled(
+ int minor,
+ const char *buf,
+ size_t len
+ );
+
+int ns16550_inbyte_nonblocking_polled(
+ int minor
+);
+
+NS16550_STATIC void ns16550_enable_interrupts(
+ console_tbl *c,
+ int mask
+);
+
+NS16550_STATIC int ns16550_set_attributes(
+ int minor,
+ const struct termios *t
+);
+
+#if defined(BSP_FEATURE_IRQ_EXTENSION) || defined(BSP_FEATURE_IRQ_LEGACY)
+ NS16550_STATIC void ns16550_isr(void *arg);
+#endif
+
+RTEMS_INTERRUPT_LOCK_DEFINE(static, ns16550_lock, "NS16550")
+
+/*
+ * Flow control is only supported when using interrupts
+ */
+
+const console_flow ns16550_flow_RTSCTS = {
+ ns16550_negate_RTS, /* deviceStopRemoteTx */
+ ns16550_assert_RTS /* deviceStartRemoteTx */
+};
+
+const console_flow ns16550_flow_DTRCTS = {
+ ns16550_negate_DTR, /* deviceStopRemoteTx */
+ ns16550_assert_DTR /* deviceStartRemoteTx */
+};
+
+const console_fns ns16550_fns = {
+ libchip_serial_default_probe, /* deviceProbe */
+ ns16550_open, /* deviceFirstOpen */
+ ns16550_close, /* deviceLastClose */
+ NULL, /* deviceRead */
+ ns16550_write_support_int, /* deviceWrite */
+ ns16550_init, /* deviceInitialize */
+ ns16550_write_polled, /* deviceWritePolled */
+ ns16550_set_attributes, /* deviceSetAttributes */
+ true /* deviceOutputUsesInterrupts */
+};
+
+const console_fns ns16550_fns_polled = {
+ libchip_serial_default_probe, /* deviceProbe */
+ ns16550_open, /* deviceFirstOpen */
+ ns16550_close, /* deviceLastClose */
+ ns16550_inbyte_nonblocking_polled, /* deviceRead */
+ ns16550_write_support_polled, /* deviceWrite */
+ ns16550_init, /* deviceInitialize */
+ ns16550_write_polled, /* deviceWritePolled */
+ ns16550_set_attributes, /* deviceSetAttributes */
+ false /* deviceOutputUsesInterrupts */
+};
+
+static uint32_t NS16550_GetBaudDivisor(const console_tbl *c, uint32_t baud)
+{
+ uint32_t clock = c->ulClock;
+ uint32_t baudDivisor = (clock != 0 ? clock : 115200) / (baud * 16);
+
+ if (c->deviceType == SERIAL_NS16550_WITH_FDR) {
+ uint32_t fractionalDivider = 0x10;
+ uint32_t err = baud;
+ uint32_t mulVal;
+ uint32_t divAddVal;
+
+ clock /= 16 * baudDivisor;
+ for (mulVal = 1; mulVal < 16; ++mulVal) {
+ for (divAddVal = 0; divAddVal < mulVal; ++divAddVal) {
+ uint32_t actual = (mulVal * clock) / (mulVal + divAddVal);
+ uint32_t newErr = actual > baud ? actual - baud : baud - actual;
+
+ if (newErr < err) {
+ err = newErr;
+ fractionalDivider = (mulVal << 4) | divAddVal;
+ }
+ }
+ }
+
+ (*c->setRegister)(
+ c->ulCtrlPort1,
+ NS16550_FRACTIONAL_DIVIDER,
+ fractionalDivider
+ );
+ }
+
+ return baudDivisor;
+}
+
+/*
+ * ns16550_init
+ */
+
+void ns16550_init(int minor)
+{
+ uintptr_t pNS16550;
+ uint8_t ucDataByte;
+ uint32_t ulBaudDivisor;
+ NS16550Context *pns16550Context;
+ setRegister_f setReg;
+ getRegister_f getReg;
+ console_tbl *c = Console_Port_Tbl [minor];
+
+ pns16550Context=(NS16550Context *)malloc(sizeof(NS16550Context));
+
+ if (pns16550Context == NULL) {
+ printk( "%s: Error: Not enough memory\n", __func__);
+ rtems_fatal_error_occurred( 0xdeadbeef);
+ }
+
+ Console_Port_Data[minor].pDeviceContext=(void *)pns16550Context;
+ pns16550Context->ucModemCtrl=SP_MODEM_IRQ;
+
+ pNS16550 = c->ulCtrlPort1;
+ setReg = c->setRegister;
+ getReg = c->getRegister;
+
+ /* Clear the divisor latch, clear all interrupt enables,
+ * and reset and
+ * disable the FIFO's.
+ */
+
+ (*setReg)(pNS16550, NS16550_LINE_CONTROL, 0x0);
+ ns16550_enable_interrupts( c, NS16550_DISABLE_ALL_INTR );
+
+ /* Set the divisor latch and set the baud rate. */
+
+ ulBaudDivisor = NS16550_GetBaudDivisor(c, (uintptr_t) c->pDeviceParams);
+ ucDataByte = SP_LINE_DLAB;
+ (*setReg)(pNS16550, NS16550_LINE_CONTROL, ucDataByte);
+
+ /* XXX */
+ (*setReg)(pNS16550,NS16550_TRANSMIT_BUFFER,(uint8_t)(ulBaudDivisor & 0xffU));
+ (*setReg)(
+ pNS16550,NS16550_INTERRUPT_ENABLE,
+ (uint8_t)(( ulBaudDivisor >> 8 ) & 0xffU )
+ );
+
+ /* Clear the divisor latch and set the character size to eight bits */
+ /* with one stop bit and no parity checking. */
+ ucDataByte = EIGHT_BITS;
+ (*setReg)(pNS16550, NS16550_LINE_CONTROL, ucDataByte);
+
+ /* Enable and reset transmit and receive FIFOs. TJA */
+ ucDataByte = SP_FIFO_ENABLE;
+ (*setReg)(pNS16550, NS16550_FIFO_CONTROL, ucDataByte);
+
+ ucDataByte = SP_FIFO_ENABLE | SP_FIFO_RXRST | SP_FIFO_TXRST;
+ (*setReg)(pNS16550, NS16550_FIFO_CONTROL, ucDataByte);
+
+ ns16550_enable_interrupts(c, NS16550_DISABLE_ALL_INTR);
+
+ /* Set data terminal ready. */
+ /* And open interrupt tristate line */
+ (*setReg)(pNS16550, NS16550_MODEM_CONTROL,pns16550Context->ucModemCtrl);
+
+ (*getReg)(pNS16550, NS16550_LINE_STATUS );
+ (*getReg)(pNS16550, NS16550_RECEIVE_BUFFER );
+}
+
+/*
+ * ns16550_open
+ */
+
+int ns16550_open(
+ int major,
+ int minor,
+ void *arg
+)
+{
+ rtems_libio_open_close_args_t *oc = (rtems_libio_open_close_args_t *) arg;
+ struct rtems_termios_tty *tty = (struct rtems_termios_tty *) oc->iop->data1;
+ console_tbl *c = Console_Port_Tbl [minor];
+ console_data *d = &Console_Port_Data [minor];
+
+ d->termios_data = tty;
+
+ /* Assert DTR */
+ if (c->pDeviceFlow != &ns16550_flow_DTRCTS) {
+ ns16550_assert_DTR( minor);
+ }
+
+ /* Set initial baud */
+ rtems_termios_set_initial_baud( tty, (intptr_t) c->pDeviceParams);
+
+ if (c->pDeviceFns->deviceOutputUsesInterrupts) {
+ ns16550_initialize_interrupts( minor);
+ ns16550_enable_interrupts( c, NS16550_ENABLE_ALL_INTR_EXCEPT_TX);
+ }
+
+ return RTEMS_SUCCESSFUL;
+}
+
+/*
+ * ns16550_close
+ */
+
+int ns16550_close(
+ int major,
+ int minor,
+ void * arg
+)
+{
+ console_tbl *c = Console_Port_Tbl [minor];
+
+ /*
+ * Negate DTR
+ */
+ if (c->pDeviceFlow != &ns16550_flow_DTRCTS) {
+ ns16550_negate_DTR(minor);
+ }
+
+ ns16550_enable_interrupts(c, NS16550_DISABLE_ALL_INTR);
+
+ if (c->pDeviceFns->deviceOutputUsesInterrupts) {
+ ns16550_cleanup_interrupts(minor);
+ }
+
+ return(RTEMS_SUCCESSFUL);
+}
+
+/**
+ * @brief Polled write for NS16550.
+ */
+void ns16550_outch_polled(console_tbl *c, char out)
+{
+ uintptr_t port = c->ulCtrlPort1;
+ getRegister_f get = c->getRegister;
+ setRegister_f set = c->setRegister;
+ uint32_t status = 0;
+ rtems_interrupt_lock_context lock_context;
+
+ /* Save port interrupt mask */
+ uint32_t interrupt_mask = get( port, NS16550_INTERRUPT_ENABLE);
+
+ /* Disable port interrupts */
+ ns16550_enable_interrupts( c, NS16550_DISABLE_ALL_INTR);
+
+ while (true) {
+ /* Try to transmit the character in a critical section */
+ rtems_interrupt_lock_acquire(&ns16550_lock, &lock_context);
+
+ /* Read the transmitter holding register and check it */
+ status = get( port, NS16550_LINE_STATUS);
+ if ((status & SP_LSR_THOLD) != 0) {
+ /* Transmit character */
+ set( port, NS16550_TRANSMIT_BUFFER, out);
+
+ /* Finished */
+ rtems_interrupt_lock_release(&ns16550_lock, &lock_context);
+ break;
+ } else {
+ rtems_interrupt_lock_release(&ns16550_lock, &lock_context);
+ }
+
+ /* Wait for transmitter holding register to be empty */
+ do {
+ status = get( port, NS16550_LINE_STATUS);
+ } while ((status & SP_LSR_THOLD) == 0);
+ }
+
+ /* Restore port interrupt mask */
+ set( port, NS16550_INTERRUPT_ENABLE, interrupt_mask);
+}
+
+void ns16550_write_polled(int minor, char out)
+{
+ console_tbl *c = Console_Port_Tbl [minor];
+
+ ns16550_outch_polled( c, out );
+}
+
+/*
+ * These routines provide control of the RTS and DTR lines
+ */
+
+/*
+ * ns16550_assert_RTS
+ */
+
+NS16550_STATIC int ns16550_assert_RTS(int minor)
+{
+ uint32_t pNS16550;
+ rtems_interrupt_lock_context lock_context;
+ NS16550Context *pns16550Context;
+ setRegister_f setReg;
+
+ pns16550Context=(NS16550Context *) Console_Port_Data[minor].pDeviceContext;
+
+ pNS16550 = Console_Port_Tbl[minor]->ulCtrlPort1;
+ setReg = Console_Port_Tbl[minor]->setRegister;
+
+ /*
+ * Assert RTS
+ */
+ rtems_interrupt_lock_acquire(&ns16550_lock, &lock_context);
+ pns16550Context->ucModemCtrl|=SP_MODEM_RTS;
+ (*setReg)(pNS16550, NS16550_MODEM_CONTROL, pns16550Context->ucModemCtrl);
+ rtems_interrupt_lock_release(&ns16550_lock, &lock_context);
+ return 0;
+}
+
+/*
+ * ns16550_negate_RTS
+ */
+
+NS16550_STATIC int ns16550_negate_RTS(int minor)
+{
+ uint32_t pNS16550;
+ rtems_interrupt_lock_context lock_context;
+ NS16550Context *pns16550Context;
+ setRegister_f setReg;
+
+ pns16550Context=(NS16550Context *) Console_Port_Data[minor].pDeviceContext;
+
+ pNS16550 = Console_Port_Tbl[minor]->ulCtrlPort1;
+ setReg = Console_Port_Tbl[minor]->setRegister;
+
+ /*
+ * Negate RTS
+ */
+ rtems_interrupt_lock_acquire(&ns16550_lock, &lock_context);
+ pns16550Context->ucModemCtrl&=~SP_MODEM_RTS;
+ (*setReg)(pNS16550, NS16550_MODEM_CONTROL, pns16550Context->ucModemCtrl);
+ rtems_interrupt_lock_release(&ns16550_lock, &lock_context);
+ return 0;
+}
+
+/*
+ * These flow control routines utilise a connection from the local DTR
+ * line to the remote CTS line
+ */
+
+/*
+ * ns16550_assert_DTR
+ */
+
+NS16550_STATIC int ns16550_assert_DTR(int minor)
+{
+ uint32_t pNS16550;
+ rtems_interrupt_lock_context lock_context;
+ NS16550Context *pns16550Context;
+ setRegister_f setReg;
+
+ pns16550Context=(NS16550Context *) Console_Port_Data[minor].pDeviceContext;
+
+ pNS16550 = Console_Port_Tbl[minor]->ulCtrlPort1;
+ setReg = Console_Port_Tbl[minor]->setRegister;
+
+ /*
+ * Assert DTR
+ */
+ rtems_interrupt_lock_acquire(&ns16550_lock, &lock_context);
+ pns16550Context->ucModemCtrl|=SP_MODEM_DTR;
+ (*setReg)(pNS16550, NS16550_MODEM_CONTROL, pns16550Context->ucModemCtrl);
+ rtems_interrupt_lock_release(&ns16550_lock, &lock_context);
+ return 0;
+}
+
+/*
+ * ns16550_negate_DTR
+ */
+
+NS16550_STATIC int ns16550_negate_DTR(int minor)
+{
+ uint32_t pNS16550;
+ rtems_interrupt_lock_context lock_context;
+ NS16550Context *pns16550Context;
+ setRegister_f setReg;
+
+ pns16550Context=(NS16550Context *) Console_Port_Data[minor].pDeviceContext;
+
+ pNS16550 = Console_Port_Tbl[minor]->ulCtrlPort1;
+ setReg = Console_Port_Tbl[minor]->setRegister;
+
+ /*
+ * Negate DTR
+ */
+ rtems_interrupt_lock_acquire(&ns16550_lock, &lock_context);
+ pns16550Context->ucModemCtrl&=~SP_MODEM_DTR;
+ (*setReg)(pNS16550, NS16550_MODEM_CONTROL,pns16550Context->ucModemCtrl);
+ rtems_interrupt_lock_release(&ns16550_lock, &lock_context);
+ return 0;
+}
+
+/*
+ * ns16550_set_attributes
+ *
+ * This function sets the channel to reflect the requested termios
+ * port settings.
+ */
+
+int ns16550_set_attributes(
+ int minor,
+ const struct termios *t
+)
+{
+ uint32_t pNS16550;
+ uint32_t ulBaudDivisor;
+ uint8_t ucLineControl;
+ uint32_t baud_requested;
+ setRegister_f setReg;
+ rtems_interrupt_lock_context lock_context;
+ const console_tbl *c = Console_Port_Tbl [minor];
+
+ pNS16550 = c->ulCtrlPort1;
+ setReg = c->setRegister;
+
+ /*
+ * Calculate the baud rate divisor
+ *
+ * Assert ensures there is no division by 0.
+ */
+
+ baud_requested = rtems_termios_baud_to_number(t->c_ospeed);
+ _Assert( baud_requested != 0 );
+ ulBaudDivisor = NS16550_GetBaudDivisor(c, baud_requested);
+
+ ucLineControl = 0;
+
+ /*
+ * Parity
+ */
+
+ if (t->c_cflag & PARENB) {
+ ucLineControl |= SP_LINE_PAR;
+ if (!(t->c_cflag & PARODD))
+ ucLineControl |= SP_LINE_ODD;
+ }
+
+ /*
+ * Character Size
+ */
+
+ if (t->c_cflag & CSIZE) {
+ switch (t->c_cflag & CSIZE) {
+ case CS5: ucLineControl |= FIVE_BITS; break;
+ case CS6: ucLineControl |= SIX_BITS; break;
+ case CS7: ucLineControl |= SEVEN_BITS; break;
+ case CS8: ucLineControl |= EIGHT_BITS; break;
+ }
+ } else {
+ ucLineControl |= EIGHT_BITS; /* default to 9600,8,N,1 */
+ }
+
+ /*
+ * Stop Bits
+ */
+
+ if (t->c_cflag & CSTOPB) {
+ ucLineControl |= SP_LINE_STOP; /* 2 stop bits */
+ } else {
+ ; /* 1 stop bit */
+ }
+
+ /*
+ * Now actually set the chip
+ */
+
+ rtems_interrupt_lock_acquire(&ns16550_lock, &lock_context);
+
+ /*
+ * Set the baud rate
+ *
+ * NOTE: When the Divisor Latch Access Bit (DLAB) is set to 1,
+ * the transmit buffer and interrupt enable registers
+ * turn into the LSB and MSB divisor latch registers.
+ */
+
+ (*setReg)(pNS16550, NS16550_LINE_CONTROL, SP_LINE_DLAB);
+ (*setReg)(pNS16550, NS16550_TRANSMIT_BUFFER, ulBaudDivisor&0xff);
+ (*setReg)(pNS16550, NS16550_INTERRUPT_ENABLE, (ulBaudDivisor>>8)&0xff);
+
+ /*
+ * Now write the line control
+ */
+ (*setReg)(pNS16550, NS16550_LINE_CONTROL, ucLineControl );
+
+ rtems_interrupt_lock_release(&ns16550_lock, &lock_context);
+
+ return 0;
+}
+
+#if defined(BSP_FEATURE_IRQ_EXTENSION) || defined(BSP_FEATURE_IRQ_LEGACY)
+
+/**
+ * @brief Process interrupt.
+ */
+NS16550_STATIC void ns16550_process( int minor)
+{
+ console_tbl *c = Console_Port_Tbl [minor];
+ console_data *d = &Console_Port_Data [minor];
+ NS16550Context *ctx = d->pDeviceContext;
+ uint32_t port = c->ulCtrlPort1;
+ getRegister_f get = c->getRegister;
+ int i;
+ char buf [SP_FIFO_SIZE];
+
+ /* Iterate until no more interrupts are pending */
+ do {
+ /* Fetch received characters */
+ i = 0;
+ while ((get(port, NS16550_LINE_STATUS) & SP_LSR_RDY) != 0) {
+ buf[i++] = (char) get(port, NS16550_RECEIVE_BUFFER);
+ if (i == SP_FIFO_SIZE) {
+ /* Enqueue fetched characters */
+ rtems_termios_enqueue_raw_characters( d->termios_data, buf, i);
+ i = 0;
+ }
+ }
+
+ if (i > 0)
+ rtems_termios_enqueue_raw_characters( d->termios_data, buf, i);
+
+ /* Check if we can dequeue transmitted characters */
+ if (ctx->transmitFifoChars > 0
+ && (get( port, NS16550_LINE_STATUS) & SP_LSR_THOLD) != 0) {
+ /* Dequeue transmitted characters */
+ rtems_termios_dequeue_characters(
+ d->termios_data,
+ ctx->transmitFifoChars
+ );
+ }
+ } while ((get( port, NS16550_INTERRUPT_ID) & SP_IID_0) == 0);
+}
+#endif
+
+/**
+ * @brief Transmits up to @a len characters from @a buf.
+ *
+ * This routine is invoked either from task context with disabled interrupts to
+ * start a new transmission process with exactly one character in case of an
+ * idle output state or from the interrupt handler to refill the transmitter.
+ *
+ * Returns always zero.
+ */
+ssize_t ns16550_write_support_int(
+ int minor,
+ const char *buf,
+ size_t len
+)
+{
+ console_tbl *c = Console_Port_Tbl [minor];
+ console_data *d = &Console_Port_Data [minor];
+ NS16550Context *ctx = d->pDeviceContext;
+ uint32_t port = c->ulCtrlPort1;
+ setRegister_f set = c->setRegister;
+ int i = 0;
+ int out = len > SP_FIFO_SIZE ? SP_FIFO_SIZE : len;
+
+ for (i = 0; i < out; ++i) {
+ set( port, NS16550_TRANSMIT_BUFFER, buf [i]);
+ }
+
+ ctx->transmitFifoChars = out;
+
+ if (out > 0) {
+ ns16550_enable_interrupts( c, NS16550_ENABLE_ALL_INTR);
+ } else {
+ ns16550_enable_interrupts( c, NS16550_ENABLE_ALL_INTR_EXCEPT_TX);
+ }
+
+ return 0;
+}
+
+/*
+ * ns16550_enable_interrupts
+ *
+ * This routine initializes the port to have the specified interrupts masked.
+ */
+NS16550_STATIC void ns16550_enable_interrupts(
+ console_tbl *c,
+ int mask
+)
+{
+ uint32_t pNS16550;
+ setRegister_f setReg;
+
+ pNS16550 = c->ulCtrlPort1;
+ setReg = c->setRegister;
+
+ (*setReg)(pNS16550, NS16550_INTERRUPT_ENABLE, mask);
+}
+
+#if defined(BSP_FEATURE_IRQ_EXTENSION) || defined(BSP_FEATURE_IRQ_LEGACY)
+ void ns16550_isr(void *arg)
+ {
+ int minor = (intptr_t) arg;
+
+ ns16550_process( minor);
+ }
+#endif
+
+/*
+ * ns16550_initialize_interrupts
+ *
+ * This routine initializes the port to operate in interrupt driver mode.
+ */
+NS16550_STATIC void ns16550_initialize_interrupts( int minor)
+{
+#if defined(BSP_FEATURE_IRQ_EXTENSION) || defined(BSP_FEATURE_IRQ_LEGACY)
+ console_tbl *c = Console_Port_Tbl [minor];
+#endif
+
+ #ifdef BSP_FEATURE_IRQ_EXTENSION
+ {
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ sc = rtems_interrupt_handler_install(
+ c->ulIntVector,
+ "NS16550",
+ RTEMS_INTERRUPT_SHARED,
+ ns16550_isr,
+ (void *) (intptr_t) minor
+ );
+ if (sc != RTEMS_SUCCESSFUL) {
+ /* FIXME */
+ printk( "%s: Error: Install interrupt handler\n", __func__);
+ rtems_fatal_error_occurred( 0xdeadbeef);
+ }
+ }
+ #elif defined(BSP_FEATURE_IRQ_LEGACY)
+ {
+ int rv = 0;
+ #ifdef BSP_FEATURE_IRQ_LEGACY_SHARED_HANDLER_SUPPORT
+ rtems_irq_connect_data cd = {
+ c->ulIntVector,
+ ns16550_isr,
+ (void *) minor,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+ };
+ rv = BSP_install_rtems_shared_irq_handler( &cd);
+ #else
+ rtems_irq_connect_data cd = {
+ c->ulIntVector,
+ ns16550_isr,
+ (void *) minor,
+ NULL,
+ NULL,
+ NULL
+ };
+ rv = BSP_install_rtems_irq_handler( &cd);
+ #endif
+ if (rv == 0) {
+ /* FIXME */
+ printk( "%s: Error: Install interrupt handler\n", __func__);
+ rtems_fatal_error_occurred( 0xdeadbeef);
+ }
+ }
+ #endif
+}
+
+NS16550_STATIC void ns16550_cleanup_interrupts(int minor)
+{
+ #if defined(BSP_FEATURE_IRQ_EXTENSION)
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ console_tbl *c = Console_Port_Tbl [minor];
+ sc = rtems_interrupt_handler_remove(
+ c->ulIntVector,
+ ns16550_isr,
+ (void *) (intptr_t) minor
+ );
+ if (sc != RTEMS_SUCCESSFUL) {
+ /* FIXME */
+ printk("%s: Error: Remove interrupt handler\n", __func__);
+ rtems_fatal_error_occurred(0xdeadbeef);
+ }
+ #elif defined(BSP_FEATURE_IRQ_LEGACY)
+ int rv = 0;
+ console_tbl *c = Console_Port_Tbl [minor];
+ rtems_irq_connect_data cd = {
+ .name = c->ulIntVector,
+ .hdl = ns16550_isr,
+ .handle = (void *) minor
+ };
+ rv = BSP_remove_rtems_irq_handler(&cd);
+ if (rv == 0) {
+ /* FIXME */
+ printk("%s: Error: Remove interrupt handler\n", __func__);
+ rtems_fatal_error_occurred(0xdeadbeef);
+ }
+ #endif
+}
+
+/*
+ * ns16550_write_support_polled
+ *
+ * Console Termios output entry point.
+ *
+ */
+
+ssize_t ns16550_write_support_polled(
+ int minor,
+ const char *buf,
+ size_t len
+)
+{
+ int nwrite = 0;
+
+ /*
+ * poll each byte in the string out of the port.
+ */
+ while (nwrite < len) {
+ /*
+ * transmit character
+ */
+ ns16550_write_polled(minor, *buf++);
+ nwrite++;
+ }
+
+ /*
+ * return the number of bytes written.
+ */
+ return nwrite;
+}
+
+/*
+ * Debug gets() support
+ */
+int ns16550_inch_polled(
+ console_tbl *c
+)
+{
+ uint32_t pNS16550;
+ unsigned char ucLineStatus;
+ uint8_t cChar;
+ getRegister_f getReg;
+
+ pNS16550 = c->ulCtrlPort1;
+ getReg = c->getRegister;
+
+ ucLineStatus = (*getReg)(pNS16550, NS16550_LINE_STATUS);
+ if (ucLineStatus & SP_LSR_RDY) {
+ cChar = (*getReg)(pNS16550, NS16550_RECEIVE_BUFFER);
+ return (int)cChar;
+ }
+ return -1;
+}
+
+/*
+ * ns16550_inbyte_nonblocking_polled
+ *
+ * Console Termios polling input entry point.
+ */
+int ns16550_inbyte_nonblocking_polled(int minor)
+{
+ console_tbl *c = Console_Port_Tbl [minor];
+
+ return ns16550_inch_polled( c );
+}