summaryrefslogtreecommitdiffstats
path: root/bsps/shared/dev/serial
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
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')
-rw-r--r--bsps/shared/dev/serial/README13
-rw-r--r--bsps/shared/dev/serial/README.mc6868183
-rw-r--r--bsps/shared/dev/serial/README.ns1655082
-rw-r--r--bsps/shared/dev/serial/README.xr886812
-rw-r--r--bsps/shared/dev/serial/README.z85c3074
-rw-r--r--bsps/shared/dev/serial/STATUS48
-rw-r--r--bsps/shared/dev/serial/mc68681.c776
-rw-r--r--bsps/shared/dev/serial/mc68681_baud.c124
-rw-r--r--bsps/shared/dev/serial/mc68681_p.h323
-rw-r--r--bsps/shared/dev/serial/mc68681_reg.c61
-rw-r--r--bsps/shared/dev/serial/mc68681_reg2.c20
-rw-r--r--bsps/shared/dev/serial/mc68681_reg4.c20
-rw-r--r--bsps/shared/dev/serial/mc68681_reg8.c20
-rw-r--r--bsps/shared/dev/serial/ns16550-context.c814
-rw-r--r--bsps/shared/dev/serial/ns16550.c875
-rw-r--r--bsps/shared/dev/serial/serprobe.c13
-rw-r--r--bsps/shared/dev/serial/z85c30.c893
-rw-r--r--bsps/shared/dev/serial/z85c30_p.h420
-rw-r--r--bsps/shared/dev/serial/z85c30_reg.c72
19 files changed, 4733 insertions, 0 deletions
diff --git a/bsps/shared/dev/serial/README b/bsps/shared/dev/serial/README
new file mode 100644
index 0000000000..59bb9e90fa
--- /dev/null
+++ b/bsps/shared/dev/serial/README
@@ -0,0 +1,13 @@
+This is the serial controller portion of the libchip library. This
+directory contains the source code for reusable console driver
+support code. Each individual driver is configured using the
+console_tbl data structure. This structure is defined and explained
+in the console.h file.
+
+The reusable chip drivers do not directly access the serial controller.
+They access the registers on the controller via a set of up to four
+functions which are provided by the BSP. These functins set and get
+general registers and data buffers. Some chips can access the data
+buffers as general registers and thus the driver may not require
+those interface routines.
+
diff --git a/bsps/shared/dev/serial/README.mc68681 b/bsps/shared/dev/serial/README.mc68681
new file mode 100644
index 0000000000..e0966d0e10
--- /dev/null
+++ b/bsps/shared/dev/serial/README.mc68681
@@ -0,0 +1,83 @@
+Configuration Table Use
+=======================
+
+sDeviceName
+
+ The name of this device.
+
+deviceType
+
+ This field must be SERIAL_MC68681.
+
+pDeviceFns
+
+ The device interface control table. This may be:
+ + mc68681_fns for interrupt driven IO
+ + mc68681_fns_polled for polled IO
+
+deviceProbe
+
+ This is the address of the routine which probes to see if the device
+ is present.
+
+pDeviceFlow
+
+ This field is ignored as hardware flow control is not currently supported.
+
+ulMargin
+
+ This is currently unused.
+
+ulHysteresis
+
+ This is currently unused.
+
+pDeviceParams
+
+ This is set to the default settings.
+
+ulCtrlPort1
+
+ This field is the base address of the entire DUART.
+
+ulCtrlPort2
+
+ This field is the base address of the port specific registers.
+
+ulDataPort
+
+ This field is bit mapped as follows:
+ bit 0: baud rate set a or b
+ bit 1-2: BRG selection ("Select Extend bit")
+
+ Note: If both ports on single DUART are not configured for the same
+ baud rate set, then unexpected results will occur.
+
+ Note: On the Exar 88c681, if a standard clock of 3.6864 Mhz is used
+ and the "Select Extend bit" is 0 (disabled), then the default
+ MC68681 baud rate table is selected.
+
+getRegister
+setRegister
+
+ These follow standard conventions.
+
+getData
+setData
+
+ These are unused since the TX and RX data registers can be accessed
+ as regular registers.
+
+ulClock
+
+ This is a pointer to a baud rate mapping table. If set to
+ mc68681_baud_rate_table, then the CSR/ACR/X bit mappings shown
+ in the 68681 and 88681 manuals are used. Otherwise, the board
+ specific baud rate mapping is used.
+
+ NULL is not a valid value.
+
+ulIntVector
+
+ This is the interrupt vector number associated with this chip.
+
diff --git a/bsps/shared/dev/serial/README.ns16550 b/bsps/shared/dev/serial/README.ns16550
new file mode 100644
index 0000000000..a0c31b5506
--- /dev/null
+++ b/bsps/shared/dev/serial/README.ns16550
@@ -0,0 +1,82 @@
+Status
+======
+
+There are no known problems with this driver.
+
+Configuration Table Use
+=======================
+
+sDeviceName
+
+ The name of this device.
+
+deviceType
+
+ This field must be SERIAL_NS16550.
+
+pDeviceFns
+
+ The device interface control table. This may be:
+ + ns16550_fns for interrupt driven IO
+ + ns16550_fns_polled for polled IO
+
+deviceProbe
+
+ This is the address of the routine which probes to see if the device
+ is present.
+
+pDeviceFlow
+
+ This field is ignored as hardware flow control is not currently supported.
+
+ulMargin
+
+ This is currently unused.
+
+ulHysteresis
+
+ This is currently unused.
+
+pDeviceParams
+
+ This is set to the default settings. At this point, it is the default
+ baud rate cast as a (void *).
+
+ulCtrlPort1
+
+ This field is the base address of this port on the UART.
+
+ulCtrlPort2
+
+ This field is unused for the NS16550.
+
+ulDataPort
+
+ This field is the base address of this port on the UART.
+
+getRegister
+setRegister
+
+ These follow standard conventions.
+
+getData
+setData
+
+ These are unused since the TX and RX data registers can be accessed
+ as regular registers.
+
+ulClock
+
+ This is the clock constant which is divided by the desired baud
+ to get the value programmed into the part. The formula for this
+ for 9600 baud is:
+
+ chip_divisor_value = ulClock / 9600.
+
+ NOTE: When ulClock is 0, the correct value for a PC (115,200) is
+ used.
+
+ulIntVector
+
+ This is the interrupt vector number associated with this chip.
+
diff --git a/bsps/shared/dev/serial/README.xr88681 b/bsps/shared/dev/serial/README.xr88681
new file mode 100644
index 0000000000..89b661143f
--- /dev/null
+++ b/bsps/shared/dev/serial/README.xr88681
@@ -0,0 +1,2 @@
+The Exar XR88681 is an enhanced version of the Motorola MC68681 and is
+supported by the mc68681 driver.
diff --git a/bsps/shared/dev/serial/README.z85c30 b/bsps/shared/dev/serial/README.z85c30
new file mode 100644
index 0000000000..f6e0b8cb11
--- /dev/null
+++ b/bsps/shared/dev/serial/README.z85c30
@@ -0,0 +1,74 @@
+Configuration Table Use
+=======================
+
+sDeviceName
+
+ The name of this device.
+
+deviceType
+
+ This field must be SERIAL_Z85C30.
+
+pDeviceFns
+
+ The device interface control table. This may be:
+ + z85c30_fns for interrupt driven IO
+ + z85c30_fns_polled for polled IO
+
+deviceProbe
+
+ This is the address of the routine which probes to see if the device
+ is present.
+
+pDeviceFlow
+
+ This field is set to one of the following values:
+ + NULL for no hardware flow control
+ + z85c30_flow_RTSCTS for RTS/CTS based flow control
+ + z85c30_flow_DTRCTS for DTR/CTS based flow control
+
+ulMargin
+
+ This is currently unused.
+
+ulHysteresis
+
+ This is currently unused.
+
+pDeviceParams
+
+ This is set to the default settings.
+
+ulCtrlPort1
+
+ This field is the address of the control register for this port.
+
+ulCtrlPort2
+
+ This field is the address of the control register for chip.
+
+ulDataPort
+
+ This field is the address of the data register for this port.
+
+getRegister
+setRegister
+
+ These follow standard conventions.
+
+getData
+setData
+
+ These follow standard conventions.
+
+ulClock
+
+ This is the clock speed of the baud rate clock.
+ NULL, then the CSR/ACR/X bit mappings shown in the 68681 and 88681
+ manuals are used. Otherwise, the board specific baud rate mapping
+ is used.
+
+ulIntVector
+
+ This is the interrupt vector number associated with this chip.
+
diff --git a/bsps/shared/dev/serial/STATUS b/bsps/shared/dev/serial/STATUS
new file mode 100644
index 0000000000..243b1a9de5
--- /dev/null
+++ b/bsps/shared/dev/serial/STATUS
@@ -0,0 +1,48 @@
+General
+=======
+
++ Hardware flow control is not currently supported. Some of the chip
+ drivers (in particular the z8530) have support for hardware flow control
+ but this has not been tested in the libchip context. There will need
+ to be a way to totally disabled hardware flow control which is not
+ currently in this.
+
++ "ulClockSpeed" configuration item field to become a pointer to a table
+ of chip specific information. For example, the z8530 should specify
+ clock speed and clock divisor setting.
+
++ A termios structure should be included to specify the initial settings.
+ Right now all drivers default to 9600, 8N1.
+
++ Need to switch to passing pointers rather than a minor number to
+ functions which are strictly internal to each chip driver. This
+ should be a performance win.
+
++ Need a test which prompts you for termios settings and tests them. Until
+ this happens, testing for the variety of settings possible will be limited.
+ This test should be able to test any serial port while prompts come to the
+ console.
+
+MC68681
+=======
+
++ Works interrupt and polled.
+
++ Hardware flow control not included.
+
+NS16650
+=======
+
++ ns16550_set-attributes function is untested.
+
++ Hardware flow control included but is currently disabled in ISR.
+
+Z85C30
+======
+
++ Works polled and interrupt.
+
++ Hardware flow control included but is currently disabled in ISR.
+
++ Needs to support mode where more specific vectors are generated.
+
diff --git a/bsps/shared/dev/serial/mc68681.c b/bsps/shared/dev/serial/mc68681.c
new file mode 100644
index 0000000000..f4ddbd6a50
--- /dev/null
+++ b/bsps/shared/dev/serial/mc68681.c
@@ -0,0 +1,776 @@
+/*
+ * This file contains the termios TTY driver for the Motorola MC68681.
+ *
+ * This part is available from a number of secondary sources.
+ * In particular, we know about the following:
+ *
+ * + Exar 88c681 and 68c681
+ *
+ * COPYRIGHT (c) 1989-1999.
+ * 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 <rtems.h>
+#include <rtems/libio.h>
+#include <rtems/score/sysstate.h>
+#include <stdlib.h>
+
+#include <libchip/serial.h>
+#include <libchip/mc68681.h>
+#include <libchip/sersupp.h>
+#include "mc68681_p.h"
+
+/*
+ * Flow control is only supported when using interrupts
+ */
+
+const console_fns mc68681_fns =
+{
+ libchip_serial_default_probe, /* deviceProbe */
+ mc68681_open, /* deviceFirstOpen */
+ NULL, /* deviceLastClose */
+ NULL, /* deviceRead */
+ mc68681_write_support_int, /* deviceWrite */
+ mc68681_initialize_interrupts, /* deviceInitialize */
+ mc68681_write_polled, /* deviceWritePolled */
+ mc68681_set_attributes, /* deviceSetAttributes */
+ true /* deviceOutputUsesInterrupts */
+};
+
+const console_fns mc68681_fns_polled =
+{
+ libchip_serial_default_probe, /* deviceProbe */
+ mc68681_open, /* deviceFirstOpen */
+ mc68681_close, /* deviceLastClose */
+ mc68681_inbyte_nonblocking_polled, /* deviceRead */
+ mc68681_write_support_polled, /* deviceWrite */
+ mc68681_init, /* deviceInitialize */
+ mc68681_write_polled, /* deviceWritePolled */
+ mc68681_set_attributes, /* deviceSetAttributes */
+ false, /* deviceOutputUsesInterrupts */
+};
+
+
+#if (CPU_SIMPLE_VECTORED_INTERRUPTS == TRUE)
+ extern void set_vector( rtems_isr_entry, rtems_vector_number, int );
+#endif
+
+/*
+ * Console Device Driver Entry Points
+ */
+
+/*
+ * mc68681_baud_rate
+ *
+ * This routine returns the proper ACR bit and baud rate field values
+ * based on the requested baud rate. The baud rate set to be used
+ * must be configured by the user.
+ */
+
+MC68681_STATIC int mc68681_baud_rate(
+ int minor,
+ int baud,
+ unsigned int *baud_mask_p,
+ unsigned int *acr_bit_p,
+ unsigned int *command
+);
+
+/*
+ * mc68681_set_attributes
+ *
+ * This function sets the DUART channel to reflect the requested termios
+ * port settings.
+ */
+
+MC68681_STATIC int mc68681_set_attributes(
+ int minor,
+ const struct termios *t
+)
+{
+ uint32_t pMC68681_port;
+ uint32_t pMC68681;
+ unsigned int mode1;
+ unsigned int mode2;
+ unsigned int baud_mask;
+ unsigned int acr_bit;
+ unsigned int cmd = 0;
+ setRegister_f setReg;
+ rtems_interrupt_level Irql;
+
+ pMC68681 = Console_Port_Tbl[minor]->ulCtrlPort1;
+ pMC68681_port = Console_Port_Tbl[minor]->ulCtrlPort2;
+ setReg = Console_Port_Tbl[minor]->setRegister;
+
+ /*
+ * Set the baud rate
+ */
+
+ if (mc68681_baud_rate( minor, t->c_cflag, &baud_mask, &acr_bit, &cmd ) == -1)
+ return -1;
+
+ baud_mask |= baud_mask << 4;
+ acr_bit <<= 7;
+
+ /*
+ * Parity
+ */
+
+ mode1 = 0;
+ mode2 = 0;
+
+ if (t->c_cflag & PARENB) {
+ if (t->c_cflag & PARODD)
+ mode1 |= 0x04;
+ /* else
+ mode1 |= 0x04; */
+ } else {
+ mode1 |= 0x10;
+ }
+
+ /*
+ * Character Size
+ */
+
+ if (t->c_cflag & CSIZE) {
+ switch (t->c_cflag & CSIZE) {
+ case CS5: break;
+ case CS6: mode1 |= 0x01; break;
+ case CS7: mode1 |= 0x02; break;
+ case CS8: mode1 |= 0x03; break;
+ }
+ } else {
+ mode1 |= 0x03; /* default to 9600,8,N,1 */
+ }
+
+ /*
+ * Stop Bits
+ */
+
+ if (t->c_cflag & CSTOPB) {
+ mode2 |= 0x0F; /* 2 stop bits */
+ } else {
+ if ((t->c_cflag & CSIZE) == CS5) /* CS5 and 1 stop bits not supported */
+ return -1;
+ mode2 |= 0x07; /* 1 stop bit */
+ }
+
+ /*
+ * Hardware Flow Control
+ */
+
+ if(t->c_cflag & CRTSCTS) {
+ mode1 |= 0x80; /* Enable Rx RTS Control */
+ mode2 |= 0x10; /* Enable CTS Enable Tx */
+ }
+
+
+ rtems_interrupt_disable(Irql);
+ (*setReg)( pMC68681, MC68681_AUX_CTRL_REG, acr_bit );
+ (*setReg)( pMC68681_port, MC68681_CLOCK_SELECT, baud_mask );
+ if ( cmd ) {
+ (*setReg)( pMC68681_port, MC68681_COMMAND, cmd ); /* RX */
+ (*setReg)( pMC68681_port, MC68681_COMMAND, cmd | 0x20 ); /* TX */
+ }
+ (*setReg)( pMC68681_port, MC68681_COMMAND, MC68681_MODE_REG_RESET_MR_PTR );
+ (*setReg)( pMC68681_port, MC68681_MODE, mode1 );
+ (*setReg)( pMC68681_port, MC68681_MODE, mode2 );
+ rtems_interrupt_enable(Irql);
+ return 0;
+}
+
+/*
+ * mc68681_initialize_context
+ *
+ * This function sets the default values of the per port context structure.
+ */
+
+MC68681_STATIC void mc68681_initialize_context(
+ int minor,
+ mc68681_context *pmc68681Context
+)
+{
+ int port;
+ unsigned int pMC68681;
+ unsigned int pMC68681_port;
+
+ pMC68681 = Console_Port_Tbl[minor]->ulCtrlPort1;
+ pMC68681_port = Console_Port_Tbl[minor]->ulCtrlPort2;
+
+ pmc68681Context->mate = -1;
+
+ for (port=0 ; port<Console_Port_Count ; port++ ) {
+ if ( Console_Port_Tbl[port]->ulCtrlPort1 == pMC68681 &&
+ Console_Port_Tbl[port]->ulCtrlPort2 != pMC68681_port ) {
+ pmc68681Context->mate = port;
+ pmc68681Context->imr = 0;
+ break;
+ }
+ }
+
+}
+
+/*
+ * mc68681_init
+ *
+ * This function initializes the DUART to a quiecsent state.
+ */
+
+MC68681_STATIC void mc68681_init(int minor)
+{
+ uint32_t pMC68681_port;
+ mc68681_context *pmc68681Context;
+ setRegister_f setReg;
+
+ pmc68681Context = (mc68681_context *) malloc(sizeof(mc68681_context));
+
+ Console_Port_Data[minor].pDeviceContext = (void *)pmc68681Context;
+
+ mc68681_initialize_context( minor, pmc68681Context );
+
+ pMC68681_port = Console_Port_Tbl[minor]->ulCtrlPort2;
+ setReg = Console_Port_Tbl[minor]->setRegister;
+
+ /*
+ * Reset everything and leave this port disabled.
+ */
+
+ (*setReg)( pMC68681_port, MC68681_COMMAND, MC68681_MODE_REG_RESET_RX );
+ (*setReg)( pMC68681_port, MC68681_COMMAND, MC68681_MODE_REG_RESET_TX );
+ (*setReg)( pMC68681_port, MC68681_COMMAND, MC68681_MODE_REG_RESET_ERROR );
+ (*setReg)( pMC68681_port, MC68681_COMMAND, MC68681_MODE_REG_RESET_BREAK );
+ (*setReg)( pMC68681_port, MC68681_COMMAND, MC68681_MODE_REG_STOP_BREAK );
+ (*setReg)( pMC68681_port, MC68681_COMMAND, MC68681_MODE_REG_DISABLE_TX );
+ (*setReg)( pMC68681_port, MC68681_COMMAND, MC68681_MODE_REG_DISABLE_RX );
+
+
+ (*setReg)( pMC68681_port, MC68681_MODE_REG_1A, 0x00 );
+ (*setReg)( pMC68681_port, MC68681_MODE_REG_2A, 0x02 );
+
+ /*
+ * Disable interrupts on RX and TX for this port
+ */
+
+ mc68681_enable_interrupts( minor, MC68681_IMR_DISABLE_ALL );
+}
+
+/*
+ * mc68681_open
+ *
+ * This function opens a port for communication.
+ *
+ * Default state is 9600 baud, 8 bits, No parity, and 1 stop bit.
+ */
+
+MC68681_STATIC int mc68681_open(
+ int major,
+ int minor,
+ void *arg
+)
+{
+ uint32_t pMC68681;
+ uint32_t pMC68681_port;
+ unsigned int baud;
+ unsigned int acr_bit;
+ unsigned int vector;
+ unsigned int command = 0;
+ rtems_interrupt_level Irql;
+ setRegister_f setReg;
+ int status;
+
+
+ pMC68681 = Console_Port_Tbl[minor]->ulCtrlPort1;
+ pMC68681_port = Console_Port_Tbl[minor]->ulCtrlPort2;
+ setReg = Console_Port_Tbl[minor]->setRegister;
+ vector = Console_Port_Tbl[minor]->ulIntVector;
+
+ /* XXX default baud rate should be from configuration table */
+
+ status = mc68681_baud_rate( minor, B9600, &baud, &acr_bit, &command );
+ if (status < 0) rtems_fatal_error_occurred (RTEMS_NOT_DEFINED);
+
+ /*
+ * Set the DUART channel to a default useable state
+ */
+
+ rtems_interrupt_disable(Irql);
+ (*setReg)( pMC68681, MC68681_AUX_CTRL_REG, acr_bit << 7 );
+ (*setReg)( pMC68681_port, MC68681_CLOCK_SELECT, baud );
+ if ( command ) {
+ (*setReg)( pMC68681_port, MC68681_COMMAND, command ); /* RX */
+ (*setReg)( pMC68681_port, MC68681_COMMAND, command | 0x20 ); /* TX */
+ }
+ (*setReg)( pMC68681_port, MC68681_COMMAND, MC68681_MODE_REG_RESET_MR_PTR );
+ (*setReg)( pMC68681_port, MC68681_MODE, 0x13 );
+ (*setReg)( pMC68681_port, MC68681_MODE, 0x07 );
+ rtems_interrupt_enable(Irql);
+
+ (*setReg)( pMC68681_port, MC68681_COMMAND, MC68681_MODE_REG_ENABLE_TX );
+ (*setReg)( pMC68681_port, MC68681_COMMAND, MC68681_MODE_REG_ENABLE_RX );
+
+ (*setReg)( pMC68681, MC68681_INTERRUPT_VECTOR_REG, vector );
+
+ return RTEMS_SUCCESSFUL;
+}
+
+/*
+ * mc68681_close
+ *
+ * This function shuts down the requested port.
+ */
+
+MC68681_STATIC int mc68681_close(
+ int major,
+ int minor,
+ void *arg
+)
+{
+ uint32_t pMC68681_port;
+ setRegister_f setReg;
+
+ pMC68681_port = Console_Port_Tbl[minor]->ulCtrlPort2;
+ setReg = Console_Port_Tbl[minor]->setRegister;
+
+ /*
+ * Disable interrupts from this channel and then disable it totally.
+ */
+ (*setReg)( pMC68681_port, MC68681_COMMAND, MC68681_MODE_REG_DISABLE_TX );
+ (*setReg)( pMC68681_port, MC68681_COMMAND, MC68681_MODE_REG_DISABLE_RX );
+
+ return(RTEMS_SUCCESSFUL);
+}
+
+/*
+ * mc68681_write_polled
+ *
+ * This routine polls out the requested character.
+ */
+
+MC68681_STATIC void mc68681_write_polled(
+ int minor,
+ char cChar
+)
+{
+ uint32_t pMC68681_port;
+ unsigned char ucLineStatus;
+ int iTimeout;
+ getRegister_f getReg;
+ setRegister_f setReg;
+
+ pMC68681_port = Console_Port_Tbl[minor]->ulCtrlPort2;
+ getReg = Console_Port_Tbl[minor]->getRegister;
+ setReg = Console_Port_Tbl[minor]->setRegister;
+
+ /*
+ * wait for transmitter holding register to be empty
+ */
+ iTimeout = 1000;
+ ucLineStatus = (*getReg)(pMC68681_port, MC68681_STATUS);
+ while ((ucLineStatus & (MC68681_TX_READY|MC68681_TX_EMPTY)) == 0) {
+
+ if ((ucLineStatus & 0xF0))
+ (*setReg)( pMC68681_port, MC68681_COMMAND, MC68681_MODE_REG_RESET_ERROR );
+
+ /*
+ * Yield while we wait
+ */
+
+#if 0
+ if(_System_state_Is_up(_System_state_Get())) {
+ rtems_task_wake_after(RTEMS_YIELD_PROCESSOR);
+ }
+#endif
+ ucLineStatus = (*getReg)(pMC68681_port, MC68681_STATUS);
+ if(!--iTimeout) {
+ break;
+ }
+ }
+
+ /*
+ * transmit character
+ */
+
+ (*setReg)(pMC68681_port, MC68681_TX_BUFFER, cChar);
+}
+
+/*
+ * mc68681_isr
+ *
+ * This is the single interrupt entry point which parcels interrupts
+ * out to the various ports.
+ */
+
+MC68681_STATIC rtems_isr mc68681_isr(
+ rtems_vector_number vector
+)
+{
+ int minor;
+
+ for(minor=0 ; minor<Console_Port_Count ; minor++) {
+ if(Console_Port_Tbl[minor]->ulIntVector == vector &&
+ Console_Port_Tbl[minor]->deviceType == SERIAL_MC68681 ) {
+ mc68681_process(minor);
+ }
+ }
+}
+
+/*
+ * mc68681_initialize_interrupts
+ *
+ * This routine initializes the console's receive and transmit
+ * ring buffers and loads the appropriate vectors to handle the interrupts.
+ */
+
+MC68681_STATIC void mc68681_initialize_interrupts(int minor)
+{
+ mc68681_init(minor);
+
+ Console_Port_Data[minor].bActive = FALSE;
+
+#if (CPU_SIMPLE_VECTORED_INTERRUPTS == TRUE)
+ set_vector(mc68681_isr, Console_Port_Tbl[minor]->ulIntVector, 1);
+#endif
+
+ mc68681_enable_interrupts(minor,MC68681_IMR_ENABLE_ALL_EXCEPT_TX);
+}
+
+/*
+ * mc68681_write_support_int
+ *
+ * Console Termios output entry point when using interrupt driven output.
+ */
+
+MC68681_STATIC ssize_t mc68681_write_support_int(
+ int minor,
+ const char *buf,
+ size_t len
+)
+{
+ uint32_t Irql;
+ uint32_t pMC68681_port;
+ setRegister_f setReg;
+
+ pMC68681_port = Console_Port_Tbl[minor]->ulCtrlPort2;
+ setReg = Console_Port_Tbl[minor]->setRegister;
+
+ /*
+ * We are using interrupt driven output and termios only sends us
+ * one character at a time.
+ */
+
+ if ( !len )
+ return 0;
+
+ /*
+ * Put the character out and enable interrupts if necessary.
+ */
+
+ rtems_interrupt_disable(Irql);
+ if ( Console_Port_Data[minor].bActive == FALSE ) {
+ Console_Port_Data[minor].bActive = TRUE;
+ mc68681_enable_interrupts(minor, MC68681_IMR_ENABLE_ALL);
+ }
+ (*setReg)(pMC68681_port, MC68681_TX_BUFFER, *buf);
+ rtems_interrupt_enable(Irql);
+
+ return 0;
+}
+
+/*
+ * mc68681_write_support_polled
+ *
+ * Console Termios output entry point when using polled output.
+ *
+ */
+
+MC68681_STATIC ssize_t mc68681_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
+ */
+ mc68681_write_polled(minor, *buf++);
+ nwrite++;
+ }
+
+ /*
+ * return the number of bytes written.
+ */
+ return nwrite;
+}
+
+/*
+ * mc68681_inbyte_nonblocking_polled
+ *
+ * Console Termios polling input entry point.
+ */
+
+MC68681_STATIC int mc68681_inbyte_nonblocking_polled(
+ int minor
+)
+{
+ uint32_t pMC68681_port;
+ unsigned char ucLineStatus;
+ unsigned char cChar;
+ getRegister_f getReg;
+
+ pMC68681_port = Console_Port_Tbl[minor]->ulCtrlPort2;
+ getReg = Console_Port_Tbl[minor]->getRegister;
+
+ ucLineStatus = (*getReg)(pMC68681_port, MC68681_STATUS);
+ if(ucLineStatus & MC68681_RX_READY) {
+ cChar = (*getReg)(pMC68681_port, MC68681_RX_BUFFER);
+ return (int)cChar;
+ } else {
+ return -1;
+ }
+}
+
+/*
+ * mc68681_baud_rate
+ */
+
+MC68681_STATIC int mc68681_baud_rate(
+ int minor,
+ int baud,
+ unsigned int *baud_mask_p,
+ unsigned int *acr_bit_p,
+ unsigned int *command
+)
+{
+ unsigned int baud_mask;
+ unsigned int acr_bit;
+ int status;
+ int is_extended;
+ int baud_requested;
+ mc68681_baud_table_t *baud_tbl;
+
+ baud_mask = 0;
+ acr_bit = 0;
+ status = 0;
+
+ if (Console_Port_Tbl[minor]->ulDataPort & MC68681_DATA_BAUD_RATE_SET_2)
+ {
+ acr_bit = 1;
+ }
+
+ is_extended = 0;
+
+ switch (Console_Port_Tbl[minor]->ulDataPort & MC68681_XBRG_MASK) {
+ case MC68681_XBRG_IGNORED:
+ *command = 0x00;
+ break;
+ case MC68681_XBRG_ENABLED:
+ *command = 0x80;
+ is_extended = 1;
+ break;
+ case MC68681_XBRG_DISABLED:
+ *command = 0x90;
+ break;
+ }
+
+ baud_requested = baud;
+ if (!baud_requested)
+ baud_requested = B9600; /* default to 9600 baud */
+
+ baud_requested = rtems_termios_baud_to_index( baud_requested );
+ if (baud_requested == -1)
+ return -1;
+
+ baud_tbl = (mc68681_baud_table_t *)
+ ((uintptr_t)Console_Port_Tbl[minor]->ulClock);
+ if (!baud_tbl)
+ rtems_fatal_error_occurred(RTEMS_INVALID_ADDRESS);
+
+ if ( is_extended )
+ baud_mask = (unsigned int)baud_tbl[ acr_bit + 2 ][ baud_requested ];
+ else
+ baud_mask = baud_tbl[ acr_bit ][ baud_requested ];
+
+ if ( baud_mask == MC68681_BAUD_NOT_VALID )
+ status = -1;
+
+ /*
+ * upper nibble is receiver and lower nibble is transmitter
+ */
+
+ *baud_mask_p = (baud_mask << 4) | baud_mask;
+ *acr_bit_p = acr_bit;
+ return status;
+}
+
+/*
+ * mc68681_process
+ *
+ * This routine is the per port console interrupt handler.
+ */
+
+MC68681_STATIC void mc68681_process(
+ int minor
+)
+{
+ uint32_t pMC68681;
+ uint32_t pMC68681_port;
+ volatile uint8_t ucLineStatus;
+ volatile uint8_t ucISRStatus;
+ char cChar;
+ getRegister_f getReg;
+
+ pMC68681 = Console_Port_Tbl[minor]->ulCtrlPort1;
+ pMC68681_port = Console_Port_Tbl[minor]->ulCtrlPort2;
+ getReg = Console_Port_Tbl[minor]->getRegister;
+
+ /* Get ISR at the beginning of the IT routine */
+ ucISRStatus = (*getReg)(pMC68681, MC68681_INTERRUPT_STATUS_REG);
+
+ /* Get good ISR a or b channel */
+ if (pMC68681 != pMC68681_port){
+ ucISRStatus >>= 4;
+ }
+
+ /* See if is usefull to call rtems_termios_dequeue */
+ if(Console_Port_Data[minor].bActive == FALSE) {
+ ucISRStatus = ucISRStatus & ~MC68681_IR_TX_READY;
+ }
+
+ /*
+ * Deal with any received characters
+ */
+ while(true) {
+ ucLineStatus = (*getReg)(pMC68681_port, MC68681_STATUS);
+ if(!(ucLineStatus & MC68681_RX_READY)) {
+ break;
+ }
+ /*
+ * If there is a RX error, then dump all the data.
+ */
+ if ( ucLineStatus & MC68681_RX_ERRORS ) {
+ do {
+ cChar = (*getReg)(pMC68681_port, MC68681_RX_BUFFER);
+ ucLineStatus = (*getReg)(pMC68681_port, MC68681_STATUS);
+ } while ( ucLineStatus & MC68681_RX_READY );
+ continue;
+ }
+ cChar = (*getReg)(pMC68681_port, MC68681_RX_BUFFER);
+ rtems_termios_enqueue_raw_characters(
+ Console_Port_Data[minor].termios_data,
+ &cChar,
+ 1
+ );
+ }
+
+ /*
+ * Deal with the transmitter
+ */
+
+ if (ucISRStatus & MC68681_IR_TX_READY) {
+ if (!rtems_termios_dequeue_characters(
+ Console_Port_Data[minor].termios_data, 1)) {
+ /* If no more char to send, disable TX interrupt */
+ Console_Port_Data[minor].bActive = FALSE;
+ mc68681_enable_interrupts(minor, MC68681_IMR_ENABLE_ALL_EXCEPT_TX);
+ }
+ }
+}
+
+/*
+ * mc68681_build_imr
+ *
+ * This function returns the value for the interrupt mask register for this
+ * DUART. Since this is a shared register, we must look at the other port
+ * on this chip to determine whether or not it is using interrupts.
+ */
+
+MC68681_STATIC unsigned int mc68681_build_imr(
+ int minor,
+ int enable_flag
+)
+{
+ int mate;
+ int is_a;
+ unsigned int mask;
+ unsigned int mate_mask;
+ unsigned int pMC68681;
+ unsigned int pMC68681_port;
+ mc68681_context *pmc68681Context;
+ mc68681_context *mateContext;
+
+ pMC68681 = Console_Port_Tbl[minor]->ulCtrlPort1;
+ pMC68681_port = Console_Port_Tbl[minor]->ulCtrlPort2;
+ pmc68681Context = (mc68681_context *) Console_Port_Data[minor].pDeviceContext;
+ mate = pmc68681Context->mate;
+
+ mask = 0;
+ mate_mask = 0;
+
+ is_a = (pMC68681 == pMC68681_port);
+
+ /*
+ * If there is a mate for this port, get its IMR mask.
+ */
+
+ if ( mate != -1 ) {
+ mateContext = Console_Port_Data[mate].pDeviceContext;
+
+ if (mateContext)
+ mate_mask = mateContext->imr;
+ }
+
+ /*
+ * Calculate this port's IMR mask and save it in the context area.
+ */
+
+ if ( Console_Port_Tbl[minor]->pDeviceFns->deviceOutputUsesInterrupts )
+ mask = enable_flag;
+
+ pmc68681Context->imr = mask;
+
+ /*
+ * Now return the full IMR value
+ */
+
+ if (is_a)
+ return (mate_mask << 4) | mask;
+
+ return (mask << 4) | mate_mask;
+}
+
+/*
+ * mc68681_enable_interrupts
+ *
+ * This function enables specific interrupt sources on the DUART.
+ */
+
+MC68681_STATIC void mc68681_enable_interrupts(
+ int minor,
+ int imr_mask
+)
+{
+ uint32_t pMC68681;
+ setRegister_f setReg;
+
+ pMC68681 = Console_Port_Tbl[minor]->ulCtrlPort1;
+ setReg = Console_Port_Tbl[minor]->setRegister;
+
+ /*
+ * Enable interrupts on RX and TX -- not break
+ */
+
+ (*setReg)(
+ pMC68681,
+ MC68681_INTERRUPT_MASK_REG,
+ mc68681_build_imr(minor, imr_mask)
+ );
+}
diff --git a/bsps/shared/dev/serial/mc68681_baud.c b/bsps/shared/dev/serial/mc68681_baud.c
new file mode 100644
index 0000000000..0f8e87c2c2
--- /dev/null
+++ b/bsps/shared/dev/serial/mc68681_baud.c
@@ -0,0 +1,124 @@
+/*
+ * MC68681 Default Baud Rate Table
+ */
+
+#include <rtems.h>
+#include <libchip/serial.h>
+#include <libchip/mc68681.h>
+
+/* major index of 0 : ACR[7] = 0, X = 0 -- 68c681 only has these */
+/* major index of 1 : ACR[7] = 1, X = 0 -- 68c681 only has these */
+/* major index of 2 : ACR[7] = 0, X = 1 */
+/* major index of 3 : ACR[7] = 1, X = 1 */
+
+/* mc68681_baud_table_t mc68681_baud_rate_table[4] = { */
+mc68681_baud_t mc68681_baud_rate_table[4][RTEMS_TERMIOS_NUMBER_BAUD_RATES] = {
+ { /* ACR[7] = 0, X = 0 */
+ MC68681_BAUD_NOT_VALID, /* B0 */
+ 0x00, /* B50 */
+ MC68681_BAUD_NOT_VALID, /* B75 */
+ 0x01, /* B110 */
+ 0x02, /* B134 */
+ MC68681_BAUD_NOT_VALID, /* B150 */
+ 0x03, /* B200 */
+ 0x04, /* B300 */
+ 0x05, /* B600 */
+ 0x06, /* B1200 */
+ MC68681_BAUD_NOT_VALID, /* B1800 */
+ 0x08, /* B2400 */
+ 0x09, /* B4800 */
+ 0x0B, /* B9600 */
+ MC68681_BAUD_NOT_VALID, /* B19200 */
+ 0x0C, /* B38400 */
+ MC68681_BAUD_NOT_VALID, /* B7200 */
+ MC68681_BAUD_NOT_VALID, /* B14400 */
+ MC68681_BAUD_NOT_VALID, /* B28800 */
+ MC68681_BAUD_NOT_VALID, /* B57600 */
+ MC68681_BAUD_NOT_VALID, /* B76800 */
+ MC68681_BAUD_NOT_VALID, /* B115200 */
+ MC68681_BAUD_NOT_VALID, /* B230400 */
+ MC68681_BAUD_NOT_VALID, /* B460800 */
+ MC68681_BAUD_NOT_VALID /* B921600 */
+ },
+ { /* ACR[7] = 1, X = 0 */
+ MC68681_BAUD_NOT_VALID, /* B0 */
+ MC68681_BAUD_NOT_VALID, /* B50 */
+ 0x00, /* B75 */
+ 0x01, /* B110 */
+ 0x02, /* B134 */
+ 0x03, /* B150 */
+ MC68681_BAUD_NOT_VALID, /* B200 */
+ 0x04, /* B300 */
+ 0x05, /* B600 */
+ 0x06, /* B1200 */
+ 0x0A, /* B1800 */
+ 0x08, /* B2400 */
+ 0x09, /* B4800 */
+ 0x0B, /* B9600 */
+ 0x0C, /* B19200 */
+ MC68681_BAUD_NOT_VALID, /* B38400 */
+ MC68681_BAUD_NOT_VALID, /* B7200 */
+ MC68681_BAUD_NOT_VALID, /* B14400 */
+ MC68681_BAUD_NOT_VALID, /* B28800 */
+ MC68681_BAUD_NOT_VALID, /* B57600 */
+ MC68681_BAUD_NOT_VALID, /* B76800 */
+ MC68681_BAUD_NOT_VALID, /* B115200 */
+ MC68681_BAUD_NOT_VALID, /* B230400 */
+ MC68681_BAUD_NOT_VALID, /* B460800 */
+ MC68681_BAUD_NOT_VALID /* B921600 */
+ },
+ { /* ACR[7] = 0, X = 1 */
+ MC68681_BAUD_NOT_VALID, /* B0 */
+ MC68681_BAUD_NOT_VALID, /* B50 */
+ 0x00, /* B75 */
+ 0x01, /* B110 */
+ 0x02, /* B134 */
+ 0x03, /* B150 */
+ MC68681_BAUD_NOT_VALID, /* B200 */
+ MC68681_BAUD_NOT_VALID, /* B300 */
+ MC68681_BAUD_NOT_VALID, /* B600 */
+ MC68681_BAUD_NOT_VALID, /* B1200 */
+ 0x0A, /* B1800 */
+ MC68681_BAUD_NOT_VALID, /* B2400 */
+ 0x08, /* B4800 */
+ 0x0B, /* B9600 */
+ 0x0C, /* B19200 */
+ MC68681_BAUD_NOT_VALID, /* B38400 */
+ MC68681_BAUD_NOT_VALID, /* B7200 */
+ MC68681_BAUD_NOT_VALID, /* B14400 */
+ MC68681_BAUD_NOT_VALID, /* B28800 */
+ 0x07, /* B57600 */
+ MC68681_BAUD_NOT_VALID, /* B76800 */
+ 0x08, /* B115200 */
+ MC68681_BAUD_NOT_VALID, /* B230400 */
+ MC68681_BAUD_NOT_VALID, /* B460800 */
+ MC68681_BAUD_NOT_VALID /* B921600 */
+ },
+ { /* ACR[7] = 1, X = 1 */
+ MC68681_BAUD_NOT_VALID, /* B0 */
+ 0x00, /* B50 */
+ MC68681_BAUD_NOT_VALID, /* B75 */
+ 0x01, /* B110 */
+ 0x02, /* B134 */
+ MC68681_BAUD_NOT_VALID, /* B150 */
+ 0x03, /* B200 */
+ MC68681_BAUD_NOT_VALID, /* B300 */
+ MC68681_BAUD_NOT_VALID, /* B600 */
+ MC68681_BAUD_NOT_VALID, /* B1200 */
+ MC68681_BAUD_NOT_VALID, /* B1800 */
+ MC68681_BAUD_NOT_VALID, /* B2400 */
+ 0x09, /* B4800 */
+ 0x0B, /* B9600 */
+ MC68681_BAUD_NOT_VALID, /* B19200 */
+ 0x0C, /* B38400 */
+ MC68681_BAUD_NOT_VALID, /* B7200 */
+ MC68681_BAUD_NOT_VALID, /* B14400 */
+ MC68681_BAUD_NOT_VALID, /* B28800 */
+ 0x07, /* B57600 */
+ MC68681_BAUD_NOT_VALID, /* B76800 */
+ 0x08, /* B115200 */
+ MC68681_BAUD_NOT_VALID, /* B230400 */
+ MC68681_BAUD_NOT_VALID, /* B460800 */
+ MC68681_BAUD_NOT_VALID /* B921600 */
+ },
+};
diff --git a/bsps/shared/dev/serial/mc68681_p.h b/bsps/shared/dev/serial/mc68681_p.h
new file mode 100644
index 0000000000..4623276303
--- /dev/null
+++ b/bsps/shared/dev/serial/mc68681_p.h
@@ -0,0 +1,323 @@
+/*
+ *
+ * COPYRIGHT (c) 1989-1999.
+ * 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.
+ */
+
+#ifndef _MC68681_P_H_
+#define _MC68681_P_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Define MC68681_STATIC to nothing while debugging so the entry points
+ * will show up in the symbol table.
+ */
+
+#define MC68681_STATIC
+
+/* #define MC68681_STATIC static */
+
+/*
+ * mc68681 register offsets Read/Write Addresses
+ */
+
+#define MC68681_MODE_REG_1A 0 /* MR1A-MR Prior to Read */
+#define MC68681_MODE_REG_2A 0 /* MR2A-MR After Read */
+
+#define MC68681_COUNT_MODE_CURRENT_MSB 6 /* CTU */
+#define MC68681_COUNTER_TIMER_UPPER_REG 6 /* CTU */
+#define MC68681_COUNT_MODE_CURRENT_LSB 7 /* CTL */
+#define MC68681_COUNTER_TIMER_LOWER_REG 7 /* CTL */
+#define MC68681_INTERRUPT_VECTOR_REG 12 /* IVR */
+
+#define MC68681_MODE_REG_1B 8 /* MR1B-MR Prior to Read */
+#define MC68681_MODE_REG_2B 8 /* MR2BA-MR After Read */
+
+/*
+ * mc68681 register offsets Read Only Addresses
+ */
+
+#define MC68681_STATUS_REG_A 1 /* SRA */
+#define MC68681_MASK_ISR_REG 2 /* MISR */
+#define MC68681_RECEIVE_BUFFER_A 3 /* RHRA */
+#define MC68681_INPUT_PORT_CHANGE_REG 4 /* IPCR */
+#define MC68681_INTERRUPT_STATUS_REG 5 /* ISR */
+#define MC68681_STATUS_REG_B 9 /* SRB */
+#define MC68681_RECEIVE_BUFFER_B 11 /* RHRB */
+#define MC68681_INPUT_PORT 13 /* IP */
+#define MC68681_START_COUNT_CMD 14 /* SCC */
+#define MC68681_STOP_COUNT_CMD 15 /* STC */
+
+/*
+ * mc68681 register offsets Write Only Addresses
+ */
+
+#define MC68681_CLOCK_SELECT_REG_A 1 /* CSRA */
+#define MC68681_COMMAND_REG_A 2 /* CRA */
+#define MC68681_TRANSMIT_BUFFER_A 3 /* THRA */
+#define MC68681_AUX_CTRL_REG 4 /* ACR */
+#define MC68681_INTERRUPT_MASK_REG 5 /* IMR */
+#define MC68681_CLOCK_SELECT_REG_B 9 /* CSRB */
+#define MC68681_COMMAND_REG_B 10 /* CRB */
+#define MC68681_TRANSMIT_BUFFER_B 11 /* THRB */
+#define MC68681_OUTPUT_PORT_CONFIG_REG 13 /* OPCR */
+#define MC68681_OUTPUT_PORT_SET_REG 14 /* SOPBC */
+#define MC68681_OUTPUT_PORT_RESET_BITS 15 /* COPBC */
+
+/*
+ * DUART Command Register Definitions:
+ *
+ * MC68681_COMMAND_REG_A,MC68681_COMMAND_REG_B
+ */
+
+#define MC68681_MODE_REG_ENABLE_RX 0x01
+#define MC68681_MODE_REG_DISABLE_RX 0x02
+#define MC68681_MODE_REG_ENABLE_TX 0x04
+#define MC68681_MODE_REG_DISABLE_TX 0x08
+#define MC68681_MODE_REG_RESET_MR_PTR 0x10
+#define MC68681_MODE_REG_RESET_RX 0x20
+#define MC68681_MODE_REG_RESET_TX 0x30
+#define MC68681_MODE_REG_RESET_ERROR 0x40
+#define MC68681_MODE_REG_RESET_BREAK 0x50
+#define MC68681_MODE_REG_START_BREAK 0x60
+#define MC68681_MODE_REG_STOP_BREAK 0x70
+#define MC68681_MODE_REG_SET_RX_BRG 0x80
+#define MC68681_MODE_REG_CLEAR_RX_BRG 0x90
+#define MC68681_MODE_REG_SET_TX_BRG 0xa0
+#define MC68681_MODE_REG_CLEAR_TX_BRG 0xb0
+#define MC68681_MODE_REG_SET_STANDBY 0xc0
+#define MC68681_MODE_REG_SET_ACTIVE 0xd0
+
+/*
+ * Mode Register Definitions
+ *
+ * MC68681_MODE_REG_1A
+ * MC68681_MODE_REG_1B
+ */
+
+#define MC68681_5BIT_CHARS 0x00
+#define MC68681_6BIT_CHARS 0x01
+#define MC68681_7BIT_CHARS 0x02
+#define MC68681_8BIT_CHARS 0x03
+
+#define MC68681_ODD_PARITY 0x00
+#define MC68681_EVEN_PARITY 0x04
+
+#define MC68681_WITH_PARITY 0x00
+#define MC68681_FORCE_PARITY 0x08
+#define MC68681_NO_PARITY 0x10
+#define MC68681_MULTI_DROP 0x18
+
+#define MC68681_ERR_MODE_CHAR 0x00
+#define MC68681_ERR_MODE_BLOCK 0x20
+
+#define MC68681_RX_INTR_RX_READY 0x00
+#define MC68681_RX_INTR_FFULL 0x40
+
+#define MC68681_NO_RX_RTS_CTL 0x00
+#define MC68681_RX_RTS_CTRL 0x80
+
+/*
+ * Mode Register Definitions
+ *
+ * MC68681_MODE_REG_2A
+ * MC68681_MODE_REG_2B
+ */
+
+#define MC68681_STOP_BIT_LENGTH__563 0x00
+#define MC68681_STOP_BIT_LENGTH__625 0x01
+#define MC68681_STOP_BIT_LENGTH__688 0x02
+#define MC68681_STOP_BIT_LENGTH__75 0x03
+#define MC68681_STOP_BIT_LENGTH__813 0x04
+#define MC68681_STOP_BIT_LENGTH__875 0x05
+#define MC68681_STOP_BIT_LENGTH__938 0x06
+#define MC68681_STOP_BIT_LENGTH_1 0x07
+#define MC68681_STOP_BIT_LENGTH_1_563 0x08
+#define MC68681_STOP_BIT_LENGTH_1_625 0x09
+#define MC68681_STOP_BIT_LENGTH_1_688 0x0a
+#define MC68681_STOP_BIT_LENGTH_1_75 0x0b
+#define MC68681_STOP_BIT_LENGTH_1_813 0x0c
+#define MC68681_STOP_BIT_LENGTH_1_875 0x0d
+#define MC68681_STOP_BIT_LENGTH_1_938 0x0e
+#define MC68681_STOP_BIT_LENGTH_2 0x0f
+
+#define MC68681_CTS_ENABLE_TX 0x10
+#define MC68681_TX_RTS_CTRL 0x20
+
+#define MC68681_CHANNEL_MODE_NORMAL 0x00
+#define MC68681_CHANNEL_MODE_ECHO 0x40
+#define MC68681_CHANNEL_MODE_LOCAL_LOOP 0x80
+#define MC68681_CHANNEL_MODE_REMOTE_LOOP 0xc0
+
+/*
+ * Status Register Definitions
+ *
+ * MC68681_STATUS_REG_A, MC68681_STATUS_REG_B
+ */
+
+#define MC68681_RX_READY 0x01
+#define MC68681_FFULL 0x02
+#define MC68681_TX_READY 0x04
+#define MC68681_TX_EMPTY 0x08
+#define MC68681_OVERRUN_ERROR 0x10
+#define MC68681_PARITY_ERROR 0x20
+#define MC68681_FRAMING_ERROR 0x40
+#define MC68681_RECEIVED_BREAK 0x80
+
+#define MC68681_RX_ERRORS \
+ (MC68681_OVERRUN_ERROR|MC68681_PARITY_ERROR| \
+ MC68681_FRAMING_ERROR|MC68681_RECEIVED_BREAK)
+
+/*
+ * Interupt Status Register Definitions.
+ *
+ * MC68681_INTERRUPT_STATUS_REG
+ */
+
+/*
+ * Interupt Mask Register Definitions
+ *
+ * MC68681_INTERRUPT_MASK_REG
+ */
+
+/* These are passed to mc68681_build_imr */
+#define MC68681_IR_TX_READY 0x01
+#define MC68681_IR_RX_READY 0x02
+#define MC68681_IR_BREAK 0x04
+#define MC68681_IMR_ENABLE_ALL 0x07
+#define MC68681_IMR_DISABLE_ALL 0x00
+#define MC68681_IMR_ENABLE_ALL_EXCEPT_TX 0x06
+
+#define MC68681_IR_TX_READY_A 0x01
+#define MC68681_IR_RX_READY_A 0x02
+#define MC68681_IR_BREAK_A 0x04
+#define MC68681_IR_COUNTER_READY 0x08
+#define MC68681_IR_TX_READY_B 0x10
+#define MC68681_IR_RX_READY_B 0x20
+#define MC68681_IR_BREAK_B 0x40
+#define MC68681_IR_INPUT_PORT_CHANGE 0x80
+
+/*
+ * Status Register Definitions.
+ *
+ * MC68681_STATUS_REG_A,MC68681_STATUS_REG_B
+ */
+
+#define MC68681_STATUS_RXRDY 0x01
+#define MC68681_STATUS_FFULL 0x02
+#define MC68681_STATUS_TXRDY 0x04
+#define MC68681_STATUS_TXEMT 0x08
+#define MC68681_STATUS_OVERRUN_ERROR 0x10
+#define MC68681_STATUS_PARITY_ERROR 0x20
+#define MC68681_STATUS_FRAMING_ERROR 0x40
+#define MC68681_STATUS_RECEIVED_BREAK 0x80
+
+/*
+ * Definitions for the Interrupt Vector Register:
+ *
+ * MC68681_INTERRUPT_VECTOR_REG
+ */
+
+#define MC68681_INTERRUPT_VECTOR_INIT 0x0f
+
+/*
+ * Definitions for the Auxiliary Control Register
+ *
+ * MC68681_AUX_CTRL_REG
+ */
+
+#define MC68681_AUX_BRG_SET1 0x00
+#define MC68681_AUX_BRG_SET2 0x80
+
+/*
+ * Per chip context control
+ */
+
+typedef struct _mc68681_context
+{
+ int mate;
+ unsigned char imr;
+} mc68681_context;
+
+/*
+ * Driver functions
+ */
+MC68681_STATIC void mc68681_initialize_context(
+ int minor,
+ mc68681_context *pmc68681Context
+);
+
+MC68681_STATIC bool mc68681_probe(int minor);
+
+MC68681_STATIC int mc68681_set_attributes(
+ int minor,
+ const struct termios *t
+);
+
+MC68681_STATIC void mc68681_init(int minor);
+
+MC68681_STATIC int mc68681_open(
+ int major,
+ int minor,
+ void * arg
+);
+
+MC68681_STATIC int mc68681_close(
+ int major,
+ int minor,
+ void * arg
+);
+
+MC68681_STATIC void mc68681_write_polled(
+ int minor,
+ char cChar
+);
+
+MC68681_STATIC void mc68681_initialize_interrupts(int minor);
+
+MC68681_STATIC ssize_t mc68681_write_support_int(
+ int minor,
+ const char *buf,
+ size_t len
+);
+
+MC68681_STATIC ssize_t mc68681_write_support_polled(
+ int minor,
+ const char *buf,
+ size_t len
+ );
+
+MC68681_STATIC int mc68681_inbyte_nonblocking_polled(
+ int minor
+);
+
+MC68681_STATIC unsigned int mc68681_build_imr(
+ int minor,
+ int enable_flag
+);
+
+MC68681_STATIC void mc68681_process(
+ int minor
+);
+
+MC68681_STATIC void mc68681_enable_interrupts(
+ int minor,
+ int imr_mask
+);
+
+MC68681_STATIC rtems_isr mc68681_isr(
+ rtems_vector_number vector
+);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MC68681_P_H_ */
diff --git a/bsps/shared/dev/serial/mc68681_reg.c b/bsps/shared/dev/serial/mc68681_reg.c
new file mode 100644
index 0000000000..fb92b8fcd3
--- /dev/null
+++ b/bsps/shared/dev/serial/mc68681_reg.c
@@ -0,0 +1,61 @@
+/*
+ * This file contains a typical set of register access routines which may be
+ * used with the mc68681 chip if accesses to the chip are as follows:
+ *
+ * + registers are accessed as bytes
+ * + registers are only byte-aligned (no address gaps)
+ *
+ * COPYRIGHT (c) 1989-1997.
+ * 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 <rtems.h>
+
+#include <libchip/serial.h>
+#include <libchip/mc68681.h>
+
+#ifndef _MC68681_MULTIPLIER
+#define _MC68681_MULTIPLIER 1
+#define _MC68681_NAME(_X) _X
+#define _MC68681_TYPE uint8_t
+#endif
+
+#define CALCULATE_REGISTER_ADDRESS( _base, _reg ) \
+ (_MC68681_TYPE *)((_base) + ((_reg) * _MC68681_MULTIPLIER ))
+
+/*
+ * MC68681 Get Register Routine
+ */
+
+uint8_t _MC68681_NAME(mc68681_get_register)(
+ uintptr_t ulCtrlPort,
+ uint8_t ucRegNum
+)
+{
+ _MC68681_TYPE *port;
+
+ port = CALCULATE_REGISTER_ADDRESS( ulCtrlPort, ucRegNum );
+
+ return *port;
+}
+
+/*
+ * MC68681 Set Register Routine
+ */
+
+void _MC68681_NAME(mc68681_set_register)(
+ uintptr_t ulCtrlPort,
+ uint8_t ucRegNum,
+ uint8_t ucData
+)
+{
+ _MC68681_TYPE *port;
+
+ port = CALCULATE_REGISTER_ADDRESS( ulCtrlPort, ucRegNum );
+
+ *port = ucData;
+}
diff --git a/bsps/shared/dev/serial/mc68681_reg2.c b/bsps/shared/dev/serial/mc68681_reg2.c
new file mode 100644
index 0000000000..0e0121eb40
--- /dev/null
+++ b/bsps/shared/dev/serial/mc68681_reg2.c
@@ -0,0 +1,20 @@
+/*
+ * This file contains a typical set of register access routines which may be
+ * used with the mc68681 chip if accesses to the chip are as follows:
+ *
+ * + registers are accessed as bytes
+ * + registers are on 16-bit boundaries
+ *
+ * COPYRIGHT (c) 1989-1997.
+ * 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.
+ */
+
+#define _MC68681_MULTIPLIER 2
+#define _MC68681_NAME(_X) _X##_2
+#define _MC68681_TYPE uint8_t
+
+#include "mc68681_reg.c"
diff --git a/bsps/shared/dev/serial/mc68681_reg4.c b/bsps/shared/dev/serial/mc68681_reg4.c
new file mode 100644
index 0000000000..e9dd94ce4b
--- /dev/null
+++ b/bsps/shared/dev/serial/mc68681_reg4.c
@@ -0,0 +1,20 @@
+/*
+ * This file contains a typical set of register access routines which may be
+ * used with the mc68681 chip if accesses to the chip are as follows:
+ *
+ * + registers are accessed as bytes
+ * + registers are on 32-bit boundaries
+ *
+ * COPYRIGHT (c) 1989-1997.
+ * 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.
+ */
+
+#define _MC68681_MULTIPLIER 4
+#define _MC68681_NAME(_X) _X##_4
+#define _MC68681_TYPE uint8_t
+
+#include "mc68681_reg.c"
diff --git a/bsps/shared/dev/serial/mc68681_reg8.c b/bsps/shared/dev/serial/mc68681_reg8.c
new file mode 100644
index 0000000000..402c2ffe1b
--- /dev/null
+++ b/bsps/shared/dev/serial/mc68681_reg8.c
@@ -0,0 +1,20 @@
+/*
+ * This file contains a typical set of register access routines which may be
+ * used with the mc68681 chip if accesses to the chip are as follows:
+ *
+ * + registers are accessed as bytes
+ * + registers are on 64-bit boundaries
+ *
+ * COPYRIGHT (c) 1989-1997.
+ * 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.
+ */
+
+#define _MC68681_MULTIPLIER 8
+#define _MC68681_NAME(_X) _X##_8
+#define _MC68681_TYPE uint8_t
+
+#include "mc68681_reg.c"
diff --git a/bsps/shared/dev/serial/ns16550-context.c b/bsps/shared/dev/serial/ns16550-context.c
new file mode 100644
index 0000000000..b42be96a26
--- /dev/null
+++ b/bsps/shared/dev/serial/ns16550-context.c
@@ -0,0 +1,814 @@
+/**
+ * @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/bspIo.h>
+
+#include <bsp.h>
+
+#include <libchip/ns16550.h>
+#include <libchip/ns16550_p.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
+
+static uint32_t NS16550_GetBaudDivisor(ns16550_context *ctx, uint32_t baud)
+{
+ uint32_t clock = ctx->clock;
+ uint32_t baudDivisor = (clock != 0 ? clock : 115200) / (baud * 16);
+
+ if (ctx->has_fractional_divider_register) {
+ 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;
+ }
+ }
+ }
+
+ (*ctx->set_reg)(
+ ctx->port,
+ NS16550_FRACTIONAL_DIVIDER,
+ fractionalDivider
+ );
+ }
+
+ return baudDivisor;
+}
+
+/*
+ * ns16550_enable_interrupts
+ *
+ * This routine initializes the port to have the specified interrupts masked.
+ */
+static void ns16550_enable_interrupts(
+ ns16550_context *ctx,
+ int mask
+)
+{
+ (*ctx->set_reg)(ctx->port, NS16550_INTERRUPT_ENABLE, mask);
+}
+
+static void ns16550_clear_and_set_interrupts(
+ ns16550_context *ctx,
+ uint8_t clear,
+ uint8_t set
+)
+{
+ rtems_interrupt_lock_context lock_context;
+ ns16550_get_reg get_reg = ctx->get_reg;
+ ns16550_set_reg set_reg = ctx->set_reg;
+ uintptr_t port = ctx->port;
+ uint8_t val;
+
+ rtems_termios_device_lock_acquire(&ctx->base, &lock_context);
+ val = (*get_reg)(port, NS16550_INTERRUPT_ENABLE);
+ val &= ~clear;
+ val |= set;
+ (*set_reg)(port, NS16550_INTERRUPT_ENABLE, val);
+ rtems_termios_device_lock_release(&ctx->base, &lock_context);
+}
+
+/*
+ * ns16550_probe
+ */
+
+bool ns16550_probe(rtems_termios_device_context *base)
+{
+ ns16550_context *ctx = (ns16550_context *) base;
+ uintptr_t pNS16550;
+ uint8_t ucDataByte;
+ uint32_t ulBaudDivisor;
+ ns16550_set_reg setReg;
+ ns16550_get_reg getReg;
+
+ ctx->modem_control = SP_MODEM_IRQ;
+
+ pNS16550 = ctx->port;
+ setReg = ctx->set_reg;
+ getReg = ctx->get_reg;
+
+ /* Clear the divisor latch, clear all interrupt enables,
+ * and reset and
+ * disable the FIFO's.
+ */
+
+ (*setReg)(pNS16550, NS16550_LINE_CONTROL, 0x0);
+ ns16550_enable_interrupts(ctx, NS16550_DISABLE_ALL_INTR );
+
+ /* Set the divisor latch and set the baud rate. */
+
+ ulBaudDivisor = NS16550_GetBaudDivisor(ctx, ctx->initial_baud);
+ ctx->baud_divisor = ulBaudDivisor;
+ 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;
+ ctx->line_control = ucDataByte;
+ (*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(ctx, NS16550_DISABLE_ALL_INTR);
+
+ /* Set data terminal ready. */
+ /* And open interrupt tristate line */
+ (*setReg)(pNS16550, NS16550_MODEM_CONTROL,ctx->modem_control);
+
+ (*getReg)(pNS16550, NS16550_LINE_STATUS );
+ (*getReg)(pNS16550, NS16550_RECEIVE_BUFFER );
+
+ return true;
+}
+
+static size_t ns16550_write_to_fifo(
+ const ns16550_context *ctx,
+ const char *buf,
+ size_t len
+)
+{
+ uintptr_t port = ctx->port;
+ ns16550_set_reg set = ctx->set_reg;
+ size_t out = len > SP_FIFO_SIZE ? SP_FIFO_SIZE : len;
+ size_t i;
+
+ for (i = 0; i < out; ++i) {
+ (*set)(port, NS16550_TRANSMIT_BUFFER, buf[i]);
+ }
+
+ return out;
+}
+
+/**
+ * @brief Process interrupt.
+ */
+static void ns16550_isr(void *arg)
+{
+ rtems_termios_tty *tty = arg;
+ ns16550_context *ctx = rtems_termios_get_device_context(tty);
+ uintptr_t port = ctx->port;
+ ns16550_get_reg get = ctx->get_reg;
+ int i = 0;
+ char buf [SP_FIFO_SIZE];
+
+ /* Iterate until no more interrupts are pending */
+ do {
+ /* Fetch received characters */
+ for (i = 0; i < SP_FIFO_SIZE; ++i) {
+ if ((get( port, NS16550_LINE_STATUS) & SP_LSR_RDY) != 0) {
+ buf [i] = (char) get(port, NS16550_RECEIVE_BUFFER);
+ } else {
+ break;
+ }
+ }
+
+ /* Enqueue fetched characters */
+ rtems_termios_enqueue_raw_characters(tty, buf, i);
+
+ /* Do transmit */
+ if (ctx->out_total > 0
+ && (get(port, NS16550_LINE_STATUS) & SP_LSR_THOLD) != 0) {
+ size_t current = ctx->out_current;
+
+ ctx->out_buf += current;
+ ctx->out_remaining -= current;
+
+ if (ctx->out_remaining > 0) {
+ ctx->out_current =
+ ns16550_write_to_fifo(ctx, ctx->out_buf, ctx->out_remaining);
+ } else {
+ rtems_termios_dequeue_characters(tty, ctx->out_total);
+ }
+ }
+ } while ((get( port, NS16550_INTERRUPT_ID) & SP_IID_0) == 0);
+}
+
+static void ns16550_isr_task(void *arg)
+{
+ rtems_termios_tty *tty = arg;
+ ns16550_context *ctx = rtems_termios_get_device_context(tty);
+ uint8_t status = (*ctx->get_reg)(ctx->port, NS16550_LINE_STATUS);
+
+ if ((status & SP_LSR_RDY) != 0) {
+ ns16550_clear_and_set_interrupts(ctx, SP_INT_RX_ENABLE, 0);
+ rtems_termios_rxirq_occured(tty);
+ }
+
+ if (ctx->out_total > 0 && (status & SP_LSR_THOLD) != 0) {
+ size_t current = ctx->out_current;
+
+ ctx->out_buf += current;
+ ctx->out_remaining -= current;
+
+ if (ctx->out_remaining > 0) {
+ ctx->out_current =
+ ns16550_write_to_fifo(ctx, ctx->out_buf, ctx->out_remaining);
+ } else {
+ size_t done = ctx->out_total;
+
+ ctx->out_total = 0;
+ ns16550_clear_and_set_interrupts(ctx, SP_INT_TX_ENABLE, 0);
+ rtems_termios_dequeue_characters(tty, done);
+ }
+ }
+}
+
+static int ns16550_read_task(rtems_termios_device_context *base)
+{
+ ns16550_context *ctx = (ns16550_context *) base;
+ uintptr_t port = ctx->port;
+ ns16550_get_reg get = ctx->get_reg;
+ char buf[SP_FIFO_SIZE];
+ int i;
+
+ for (i = 0; i < SP_FIFO_SIZE; ++i) {
+ if ((get(port, NS16550_LINE_STATUS) & SP_LSR_RDY) != 0) {
+ buf[i] = (char) get(port, NS16550_RECEIVE_BUFFER);
+ } else {
+ break;
+ }
+ }
+
+ rtems_termios_enqueue_raw_characters(ctx->tty, buf, i);
+ ns16550_clear_and_set_interrupts(ctx, 0, SP_INT_RX_ENABLE);
+
+ return -1;
+}
+
+/*
+ * ns16550_initialize_interrupts
+ *
+ * This routine initializes the port to operate in interrupt driver mode.
+ */
+static void ns16550_initialize_interrupts(
+ struct rtems_termios_tty *tty,
+ ns16550_context *ctx,
+ void (*isr)(void *)
+)
+{
+ #ifdef BSP_FEATURE_IRQ_EXTENSION
+ {
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ sc = rtems_interrupt_handler_install(
+ ctx->irq,
+ "NS16550",
+ RTEMS_INTERRUPT_SHARED,
+ isr,
+ tty
+ );
+ 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 = {
+ ctx->irq,
+ isr,
+ tty,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+ };
+ rv = BSP_install_rtems_shared_irq_handler( &cd);
+ #else
+ rtems_irq_connect_data cd = {
+ ctx->irq,
+ isr,
+ tty,
+ 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_open
+ */
+
+static bool ns16550_open(
+ struct rtems_termios_tty *tty,
+ rtems_termios_device_context *base,
+ struct termios *term,
+ rtems_libio_open_close_args_t *args
+)
+{
+ ns16550_context *ctx = (ns16550_context *) base;
+
+ ctx->tty = tty;
+
+ /* Set initial baud */
+ rtems_termios_set_initial_baud(tty, ctx->initial_baud);
+
+ if (tty->handler.mode == TERMIOS_IRQ_DRIVEN) {
+ ns16550_initialize_interrupts(tty, ctx, ns16550_isr);
+ ns16550_enable_interrupts(ctx, NS16550_ENABLE_ALL_INTR_EXCEPT_TX);
+ } else if (tty->handler.mode == TERMIOS_TASK_DRIVEN) {
+ ns16550_initialize_interrupts(tty, ctx, ns16550_isr_task);
+ ns16550_enable_interrupts(ctx, NS16550_ENABLE_ALL_INTR_EXCEPT_TX);
+ }
+
+ return true;
+}
+
+static void ns16550_cleanup_interrupts(
+ struct rtems_termios_tty *tty,
+ ns16550_context *ctx,
+ void (*isr)(void *)
+)
+{
+ #if defined(BSP_FEATURE_IRQ_EXTENSION)
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ sc = rtems_interrupt_handler_remove(
+ ctx->irq,
+ isr,
+ tty
+ );
+ 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;
+ rtems_irq_connect_data cd = {
+ .name = ctx->irq,
+ .hdl = isr,
+ .handle = tty
+ };
+ 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_close
+ */
+
+static void ns16550_close(
+ struct rtems_termios_tty *tty,
+ rtems_termios_device_context *base,
+ rtems_libio_open_close_args_t *args
+)
+{
+ ns16550_context *ctx = (ns16550_context *) base;
+
+ ns16550_enable_interrupts(ctx, NS16550_DISABLE_ALL_INTR);
+
+ if (tty->handler.mode == TERMIOS_IRQ_DRIVEN) {
+ ns16550_cleanup_interrupts(tty, ctx, ns16550_isr);
+ } else if (tty->handler.mode == TERMIOS_TASK_DRIVEN) {
+ ns16550_cleanup_interrupts(tty, ctx, ns16550_isr_task);
+ }
+}
+
+/**
+ * @brief Polled write for NS16550.
+ */
+void ns16550_polled_putchar(rtems_termios_device_context *base, char out)
+{
+ ns16550_context *ctx = (ns16550_context *) base;
+ uintptr_t port = ctx->port;
+ ns16550_get_reg get = ctx->get_reg;
+ ns16550_set_reg set = ctx->set_reg;
+ 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(ctx, NS16550_DISABLE_ALL_INTR);
+
+ while (true) {
+ /* Try to transmit the character in a critical section */
+ rtems_termios_device_lock_acquire(&ctx->base, &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_termios_device_lock_release(&ctx->base, &lock_context);
+ break;
+ } else {
+ rtems_termios_device_lock_release(&ctx->base, &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);
+}
+
+/*
+ * These routines provide control of the RTS and DTR lines
+ */
+
+/*
+ * ns16550_assert_RTS
+ */
+
+static void ns16550_assert_RTS(rtems_termios_device_context *base)
+{
+ ns16550_context *ctx = (ns16550_context *) base;
+ rtems_interrupt_lock_context lock_context;
+
+ /*
+ * Assert RTS
+ */
+ rtems_termios_device_lock_acquire(base, &lock_context);
+ ctx->modem_control |= SP_MODEM_RTS;
+ (*ctx->set_reg)(ctx->port, NS16550_MODEM_CONTROL, ctx->modem_control);
+ rtems_termios_device_lock_release(base, &lock_context);
+}
+
+/*
+ * ns16550_negate_RTS
+ */
+
+static void ns16550_negate_RTS(rtems_termios_device_context *base)
+{
+ ns16550_context *ctx = (ns16550_context *) base;
+ rtems_interrupt_lock_context lock_context;
+
+ /*
+ * Negate RTS
+ */
+ rtems_termios_device_lock_acquire(base, &lock_context);
+ ctx->modem_control &= ~SP_MODEM_RTS;
+ (*ctx->set_reg)(ctx->port, NS16550_MODEM_CONTROL, ctx->modem_control);
+ rtems_termios_device_lock_release(base, &lock_context);
+}
+
+/*
+ * These flow control routines utilise a connection from the local DTR
+ * line to the remote CTS line
+ */
+
+/*
+ * ns16550_assert_DTR
+ */
+
+static void ns16550_assert_DTR(rtems_termios_device_context *base)
+{
+ ns16550_context *ctx = (ns16550_context *) base;
+ rtems_interrupt_lock_context lock_context;
+
+ /*
+ * Assert DTR
+ */
+ rtems_termios_device_lock_acquire(base, &lock_context);
+ ctx->modem_control |= SP_MODEM_DTR;
+ (*ctx->set_reg)(ctx->port, NS16550_MODEM_CONTROL, ctx->modem_control);
+ rtems_termios_device_lock_release(base, &lock_context);
+}
+
+/*
+ * ns16550_negate_DTR
+ */
+
+static void ns16550_negate_DTR(rtems_termios_device_context *base)
+{
+ ns16550_context *ctx = (ns16550_context *) base;
+ rtems_interrupt_lock_context lock_context;
+
+ /*
+ * Negate DTR
+ */
+ rtems_termios_device_lock_acquire(base, &lock_context);
+ ctx->modem_control &=~SP_MODEM_DTR;
+ (*ctx->set_reg)(ctx->port, NS16550_MODEM_CONTROL,ctx->modem_control);
+ rtems_termios_device_lock_release(base, &lock_context);
+}
+
+/*
+ * ns16550_set_attributes
+ *
+ * This function sets the channel to reflect the requested termios
+ * port settings.
+ */
+
+static bool ns16550_set_attributes(
+ rtems_termios_device_context *base,
+ const struct termios *t
+)
+{
+ ns16550_context *ctx = (ns16550_context *) base;
+ uint32_t pNS16550;
+ uint32_t ulBaudDivisor;
+ uint8_t ucLineControl;
+ uint32_t baud_requested;
+ ns16550_set_reg setReg;
+
+ pNS16550 = ctx->port;
+ setReg = ctx->set_reg;
+
+ /*
+ * 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(ctx, 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
+ */
+
+ if (ulBaudDivisor != ctx->baud_divisor || ucLineControl != ctx->line_control) {
+ rtems_interrupt_lock_context lock_context;
+
+ ctx->baud_divisor = ulBaudDivisor;
+ ctx->line_control = ucLineControl;
+
+ rtems_termios_device_lock_acquire(base, &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_termios_device_lock_release(base, &lock_context);
+ }
+
+ return true;
+}
+
+/**
+ * @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.
+ */
+static void ns16550_write_support_int(
+ rtems_termios_device_context *base,
+ const char *buf,
+ size_t len
+)
+{
+ ns16550_context *ctx = (ns16550_context *) base;
+
+ ctx->out_total = len;
+
+ if (len > 0) {
+ ctx->out_remaining = len;
+ ctx->out_buf = buf;
+ ctx->out_current = ns16550_write_to_fifo(ctx, buf, len);
+
+ ns16550_enable_interrupts(ctx, NS16550_ENABLE_ALL_INTR);
+ } else {
+ ns16550_enable_interrupts(ctx, NS16550_ENABLE_ALL_INTR_EXCEPT_TX);
+ }
+}
+
+static void ns16550_write_support_task(
+ rtems_termios_device_context *base,
+ const char *buf,
+ size_t len
+)
+{
+ ns16550_context *ctx = (ns16550_context *) base;
+
+ ctx->out_total = len;
+
+ if (len > 0) {
+ ctx->out_remaining = len;
+ ctx->out_buf = buf;
+ ctx->out_current = ns16550_write_to_fifo(ctx, buf, len);
+
+ ns16550_clear_and_set_interrupts(ctx, 0, SP_INT_TX_ENABLE);
+ }
+}
+
+/*
+ * ns16550_write_support_polled
+ *
+ * Console Termios output entry point.
+ *
+ */
+
+static void ns16550_write_support_polled(
+ rtems_termios_device_context *base,
+ const char *buf,
+ size_t len
+)
+{
+ size_t nwrite = 0;
+
+ /*
+ * poll each byte in the string out of the port.
+ */
+ while (nwrite < len) {
+ /*
+ * transmit character
+ */
+ ns16550_polled_putchar(base, *buf++);
+ nwrite++;
+ }
+}
+
+/*
+ * Debug gets() support
+ */
+int ns16550_polled_getchar(rtems_termios_device_context *base)
+{
+ ns16550_context *ctx = (ns16550_context *) base;
+ uint32_t pNS16550;
+ unsigned char ucLineStatus;
+ uint8_t cChar;
+ ns16550_get_reg getReg;
+
+ pNS16550 = ctx->port;
+ getReg = ctx->get_reg;
+
+ ucLineStatus = (*getReg)(pNS16550, NS16550_LINE_STATUS);
+ if (ucLineStatus & SP_LSR_RDY) {
+ cChar = (*getReg)(pNS16550, NS16550_RECEIVE_BUFFER);
+ return (int)cChar;
+ }
+ return -1;
+}
+
+/*
+ * Flow control is only supported when using interrupts
+ */
+
+const rtems_termios_device_flow ns16550_flow_rtscts = {
+ .stop_remote_tx = ns16550_negate_RTS,
+ .start_remote_tx = ns16550_assert_RTS
+};
+
+const rtems_termios_device_flow ns16550_flow_dtrcts = {
+ .stop_remote_tx = ns16550_negate_DTR,
+ .start_remote_tx = ns16550_assert_DTR
+};
+
+const rtems_termios_device_handler ns16550_handler_interrupt = {
+ .first_open = ns16550_open,
+ .last_close = ns16550_close,
+ .poll_read = NULL,
+ .write = ns16550_write_support_int,
+ .set_attributes = ns16550_set_attributes,
+ .mode = TERMIOS_IRQ_DRIVEN
+};
+
+const rtems_termios_device_handler ns16550_handler_polled = {
+ .first_open = ns16550_open,
+ .last_close = ns16550_close,
+ .poll_read = ns16550_polled_getchar,
+ .write = ns16550_write_support_polled,
+ .set_attributes = ns16550_set_attributes,
+ .mode = TERMIOS_POLLED
+};
+
+const rtems_termios_device_handler ns16550_handler_task = {
+ .first_open = ns16550_open,
+ .last_close = ns16550_close,
+ .poll_read = ns16550_read_task,
+ .write = ns16550_write_support_task,
+ .set_attributes = ns16550_set_attributes,
+ .mode = TERMIOS_TASK_DRIVEN
+};
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 );
+}
diff --git a/bsps/shared/dev/serial/serprobe.c b/bsps/shared/dev/serial/serprobe.c
new file mode 100644
index 0000000000..7f8d392452
--- /dev/null
+++ b/bsps/shared/dev/serial/serprobe.c
@@ -0,0 +1,13 @@
+#include <rtems.h>
+#include <libchip/serial.h>
+#include <libchip/sersupp.h>
+
+bool libchip_serial_default_probe(int minor)
+{
+ /*
+ * If the configuration dependent probe has located the device then
+ * assume it is there
+ */
+
+ return true;
+}
diff --git a/bsps/shared/dev/serial/z85c30.c b/bsps/shared/dev/serial/z85c30.c
new file mode 100644
index 0000000000..55df9d3451
--- /dev/null
+++ b/bsps/shared/dev/serial/z85c30.c
@@ -0,0 +1,893 @@
+/*
+ * This file contains the console driver chip level routines for the
+ * Zilog z85c30 chip.
+ *
+ * The Zilog Z8530 is also available as:
+ *
+ * + Intel 82530
+ * + AMD ???
+ *
+ * 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-1997.
+ * 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 <rtems.h>
+#include <rtems/libio.h>
+#include <rtems/score/sysstate.h>
+#include <stdlib.h>
+
+#include <libchip/serial.h>
+#include <libchip/sersupp.h>
+#include "z85c30_p.h"
+
+/*
+ * Flow control is only supported when using interrupts
+ */
+
+const console_flow z85c30_flow_RTSCTS = {
+ z85c30_negate_RTS, /* deviceStopRemoteTx */
+ z85c30_assert_RTS /* deviceStartRemoteTx */
+};
+
+const console_flow z85c30_flow_DTRCTS = {
+ z85c30_negate_DTR, /* deviceStopRemoteTx */
+ z85c30_assert_DTR /* deviceStartRemoteTx */
+};
+
+/*
+ * Exported driver function table
+ */
+
+const console_fns z85c30_fns = {
+ libchip_serial_default_probe, /* deviceProbe */
+ z85c30_open, /* deviceFirstOpen */
+ NULL, /* deviceLastClose */
+ NULL, /* deviceRead */
+ z85c30_write_support_int, /* deviceWrite */
+ z85c30_initialize_interrupts, /* deviceInitialize */
+ z85c30_write_polled, /* deviceWritePolled */
+ NULL, /* deviceSetAttributes */
+ true /* deviceOutputUsesInterrupts */
+};
+
+const console_fns z85c30_fns_polled = {
+ libchip_serial_default_probe, /* deviceProbe */
+ z85c30_open, /* deviceFirstOpen */
+ z85c30_close, /* deviceLastClose */
+ z85c30_inbyte_nonblocking_polled, /* deviceRead */
+ z85c30_write_support_polled, /* deviceWrite */
+ z85c30_init, /* deviceInitialize */
+ z85c30_write_polled, /* deviceWritePolled */
+ NULL, /* deviceSetAttributes */
+ false /* deviceOutputUsesInterrupts */
+};
+
+#if (CPU_SIMPLE_VECTORED_INTERRUPTS == TRUE)
+ extern void set_vector( rtems_isr_entry, rtems_vector_number, int );
+#endif
+
+/*
+ * z85c30_initialize_port
+ *
+ * initialize a z85c30 Port
+ */
+
+Z85C30_STATIC void z85c30_initialize_port(
+ int minor
+)
+{
+ uintptr_t ulCtrlPort;
+ uintptr_t ulBaudDivisor;
+ setRegister_f setReg;
+
+ ulCtrlPort = Console_Port_Tbl[minor]->ulCtrlPort1;
+ setReg = Console_Port_Tbl[minor]->setRegister;
+
+ /*
+ * Using register 4
+ * Set up the clock rate is 16 times the data
+ * rate, 8 bit sync char, 1 stop bit, no parity
+ */
+
+ (*setReg)( ulCtrlPort, SCC_WR0_SEL_WR4, SCC_WR4_1_STOP | SCC_WR4_16_CLOCK );
+
+ /*
+ * Set up for 8 bits/character on receive with
+ * receiver disable via register 3
+ */
+ (*setReg)( ulCtrlPort, SCC_WR0_SEL_WR3, SCC_WR3_RX_8_BITS );
+
+ /*
+ * Set up for 8 bits/character on transmit
+ * with transmitter disable via register 5
+ */
+ (*setReg)( ulCtrlPort, SCC_WR0_SEL_WR5, SCC_WR5_TX_8_BITS );
+
+ /*
+ * Clear misc control bits
+ */
+ (*setReg)( ulCtrlPort, SCC_WR0_SEL_WR10, 0x00 );
+
+ /*
+ * Setup the source of the receive and xmit
+ * clock as BRG output and the transmit clock
+ * as the output source for TRxC pin via register 11
+ */
+ (*setReg)(
+ ulCtrlPort,
+ SCC_WR0_SEL_WR11,
+ SCC_WR11_OUT_BR_GEN | SCC_WR11_TRXC_OI |
+ SCC_WR11_TX_BR_GEN | SCC_WR11_RX_BR_GEN
+ );
+
+ ulBaudDivisor = Z85C30_Baud(
+ (uint32_t) Console_Port_Tbl[minor]->ulClock,
+ (uint32_t) ((uintptr_t)Console_Port_Tbl[minor]->pDeviceParams)
+ );
+
+ /*
+ * Setup the lower 8 bits time constants=1E.
+ * If the time constans=1E, then the desire
+ * baud rate will be equilvalent to 9600, via register 12.
+ */
+ (*setReg)( ulCtrlPort, SCC_WR0_SEL_WR12, ulBaudDivisor & 0xff );
+
+ /*
+ * using register 13
+ * Setup the upper 8 bits time constant
+ */
+ (*setReg)( ulCtrlPort, SCC_WR0_SEL_WR13, (ulBaudDivisor>>8) & 0xff );
+
+ /*
+ * Enable the baud rate generator enable with clock from the
+ * SCC's PCLK input via register 14.
+ */
+ (*setReg)(
+ ulCtrlPort,
+ SCC_WR0_SEL_WR14,
+ SCC_WR14_BR_EN | SCC_WR14_BR_SRC | SCC_WR14_NULL
+ );
+
+ /*
+ * We are only interested in CTS state changes
+ */
+ (*setReg)( ulCtrlPort, SCC_WR0_SEL_WR15, SCC_WR15_CTS_IE );
+
+ /*
+ * Reset errors
+ */
+ (*setReg)( ulCtrlPort, SCC_WR0_SEL_WR0, SCC_WR0_RST_INT );
+
+ (*setReg)( ulCtrlPort, SCC_WR0_SEL_WR0, SCC_WR0_ERR_RST );
+
+ /*
+ * Enable the receiver via register 3
+ */
+ (*setReg)( ulCtrlPort, SCC_WR0_SEL_WR3, SCC_WR3_RX_8_BITS | SCC_WR3_RX_EN );
+
+ /*
+ * Enable the transmitter pins set via register 5.
+ */
+ (*setReg)( ulCtrlPort, SCC_WR0_SEL_WR5, SCC_WR5_TX_8_BITS | SCC_WR5_TX_EN );
+
+ /*
+ * Disable interrupts
+ */
+ (*setReg)( ulCtrlPort, SCC_WR0_SEL_WR1, 0 );
+
+ /*
+ * Reset TX CRC
+ */
+ (*setReg)( ulCtrlPort, SCC_WR0_SEL_WR0, SCC_WR0_RST_TX_CRC );
+
+ /*
+ * Reset interrupts
+ */
+ (*setReg)( ulCtrlPort, SCC_WR0_SEL_WR0, SCC_WR0_RST_INT );
+}
+
+/*
+ * z85c30_open
+ */
+
+Z85C30_STATIC int z85c30_open(
+ int major,
+ int minor,
+ void *arg
+)
+{
+
+ z85c30_initialize_port(minor);
+
+ /*
+ * Assert DTR
+ */
+
+ if (Console_Port_Tbl[minor]->pDeviceFlow !=&z85c30_flow_DTRCTS) {
+ z85c30_assert_DTR(minor);
+ }
+
+ return(RTEMS_SUCCESSFUL);
+}
+
+/*
+ * z85c30_close
+ */
+
+Z85C30_STATIC int z85c30_close(
+ int major,
+ int minor,
+ void *arg
+)
+{
+ /*
+ * Negate DTR
+ */
+
+ if (Console_Port_Tbl[minor]->pDeviceFlow !=&z85c30_flow_DTRCTS) {
+ z85c30_negate_DTR(minor);
+ }
+
+ return(RTEMS_SUCCESSFUL);
+}
+
+/*
+ * z85c30_init
+ */
+
+Z85C30_STATIC void z85c30_init(int minor)
+{
+ uintptr_t ulCtrlPort;
+ z85c30_context *pz85c30Context;
+ setRegister_f setReg;
+ getRegister_f getReg;
+
+ ulCtrlPort = Console_Port_Tbl[minor]->ulCtrlPort1;
+ setReg = Console_Port_Tbl[minor]->setRegister;
+ getReg = Console_Port_Tbl[minor]->getRegister;
+
+ pz85c30Context = (z85c30_context *)malloc(sizeof(z85c30_context));
+
+ Console_Port_Data[minor].pDeviceContext = (void *)pz85c30Context;
+
+ pz85c30Context->ucModemCtrl = SCC_WR5_TX_8_BITS | SCC_WR5_TX_EN;
+
+ if ( ulCtrlPort == Console_Port_Tbl[minor]->ulCtrlPort2 ) {
+ /*
+ * This is channel A
+ */
+ /*
+ * Ensure port state machine is reset
+ */
+ (*getReg)(ulCtrlPort, SCC_WR0_SEL_RD0);
+
+ (*setReg)(ulCtrlPort, SCC_WR0_SEL_WR9, SCC_WR9_CH_A_RST);
+
+ } else {
+ /*
+ * This is channel B
+ */
+ /*
+ * Ensure port state machine is reset
+ */
+ (*getReg)(ulCtrlPort, SCC_WR0_SEL_RD0);
+
+ (*setReg)(ulCtrlPort, SCC_WR0_SEL_WR9, SCC_WR9_CH_B_RST);
+ }
+}
+
+/*
+ * These routines provide control of the RTS and DTR lines
+ */
+
+/*
+ * z85c30_assert_RTS
+ */
+
+Z85C30_STATIC int z85c30_assert_RTS(int minor)
+{
+ rtems_interrupt_level Irql;
+ z85c30_context *pz85c30Context;
+ setRegister_f setReg;
+
+ setReg = Console_Port_Tbl[minor]->setRegister;
+
+ pz85c30Context = (z85c30_context *) Console_Port_Data[minor].pDeviceContext;
+
+ /*
+ * Assert RTS
+ */
+
+ rtems_interrupt_disable(Irql);
+ pz85c30Context->ucModemCtrl|=SCC_WR5_RTS;
+ (*setReg)(
+ Console_Port_Tbl[minor]->ulCtrlPort1,
+ SCC_WR0_SEL_WR5,
+ pz85c30Context->ucModemCtrl
+ );
+ rtems_interrupt_enable(Irql);
+ return 0;
+}
+
+/*
+ * z85c30_negate_RTS
+ */
+
+Z85C30_STATIC int z85c30_negate_RTS(int minor)
+{
+ rtems_interrupt_level Irql;
+ z85c30_context *pz85c30Context;
+ setRegister_f setReg;
+
+ setReg = Console_Port_Tbl[minor]->setRegister;
+
+ pz85c30Context = (z85c30_context *) Console_Port_Data[minor].pDeviceContext;
+
+ /*
+ * Negate RTS
+ */
+
+ rtems_interrupt_disable(Irql);
+ pz85c30Context->ucModemCtrl&=~SCC_WR5_RTS;
+ (*setReg)(
+ Console_Port_Tbl[minor]->ulCtrlPort1,
+ SCC_WR0_SEL_WR5,
+ pz85c30Context->ucModemCtrl
+ );
+ rtems_interrupt_enable(Irql);
+ return 0;
+}
+
+/*
+ * These flow control routines utilise a connection from the local DTR
+ * line to the remote CTS line
+ */
+
+/*
+ * z85c30_assert_DTR
+ */
+
+Z85C30_STATIC int z85c30_assert_DTR(int minor)
+{
+ rtems_interrupt_level Irql;
+ z85c30_context *pz85c30Context;
+ setRegister_f setReg;
+
+ setReg = Console_Port_Tbl[minor]->setRegister;
+
+ pz85c30Context = (z85c30_context *) Console_Port_Data[minor].pDeviceContext;
+
+ /*
+ * Assert DTR
+ */
+
+ rtems_interrupt_disable(Irql);
+ pz85c30Context->ucModemCtrl|=SCC_WR5_DTR;
+ (*setReg)(
+ Console_Port_Tbl[minor]->ulCtrlPort1,
+ SCC_WR0_SEL_WR5,
+ pz85c30Context->ucModemCtrl
+ );
+ rtems_interrupt_enable(Irql);
+ return 0;
+}
+
+/*
+ * z85c30_negate_DTR
+ */
+
+Z85C30_STATIC int z85c30_negate_DTR(int minor)
+{
+ rtems_interrupt_level Irql;
+ z85c30_context *pz85c30Context;
+ setRegister_f setReg;
+
+ setReg = Console_Port_Tbl[minor]->setRegister;
+
+ pz85c30Context = (z85c30_context *) Console_Port_Data[minor].pDeviceContext;
+
+ /*
+ * Negate DTR
+ */
+
+ rtems_interrupt_disable(Irql);
+ pz85c30Context->ucModemCtrl&=~SCC_WR5_DTR;
+ (*setReg)(
+ Console_Port_Tbl[minor]->ulCtrlPort1,
+ SCC_WR0_SEL_WR5,
+ pz85c30Context->ucModemCtrl
+ );
+ rtems_interrupt_enable(Irql);
+ return 0;
+}
+
+/*
+ * z85c30_set_attributes
+ *
+ * This function sets the SCC channel to reflect the requested termios
+ * port settings.
+ */
+
+Z85C30_STATIC int z85c30_set_attributes(
+ int minor,
+ const struct termios *t
+)
+{
+ uintptr_t ulCtrlPort;
+ uint32_t ulBaudDivisor;
+ uint32_t wr3;
+ uint32_t wr4;
+ uint32_t wr5;
+ int baud_requested;
+ uint32_t baud_number;
+ setRegister_f setReg;
+ rtems_interrupt_level Irql;
+
+ ulCtrlPort = Console_Port_Tbl[minor]->ulCtrlPort1;
+ setReg = Console_Port_Tbl[minor]->setRegister;
+
+ /*
+ * Calculate the baud rate divisor
+ *
+ * Assert ensures there is no division by 0.
+ */
+
+ baud_requested = t->c_ospeed;
+ if (!baud_requested)
+ baud_requested = B9600; /* default to 9600 baud */
+
+ baud_number = (uint32_t) rtems_termios_baud_to_number( baud_requested );
+ _Assert( baud_number != 0 );
+
+ ulBaudDivisor = Z85C30_Baud(
+ (uint32_t) Console_Port_Tbl[minor]->ulClock,
+ baud_number
+ );
+
+ wr3 = SCC_WR3_RX_EN;
+ wr4 = SCC_WR4_16_CLOCK;
+ wr5 = SCC_WR5_TX_EN;
+
+ /*
+ * Parity
+ */
+
+ if (t->c_cflag & PARENB) {
+ wr4 |= SCC_WR4_PAR_EN;
+ if (!(t->c_cflag & PARODD))
+ wr4 |= SCC_WR4_PAR_EVEN;
+ }
+
+ /*
+ * Character Size
+ */
+
+ if (t->c_cflag & CSIZE) {
+ switch (t->c_cflag & CSIZE) {
+ case CS5: break;
+ case CS6: wr3 |= SCC_WR3_RX_6_BITS; wr5 |= SCC_WR5_TX_6_BITS; break;
+ case CS7: wr3 |= SCC_WR3_RX_7_BITS; wr5 |= SCC_WR5_TX_7_BITS; break;
+ case CS8: wr3 |= SCC_WR3_RX_8_BITS; wr5 |= SCC_WR5_TX_8_BITS; break;
+ }
+ } else {
+ wr3 |= SCC_WR3_RX_8_BITS; /* default to 9600,8,N,1 */
+ wr5 |= SCC_WR5_TX_8_BITS; /* default to 9600,8,N,1 */
+ }
+
+ /*
+ * Stop Bits
+ */
+
+ if (t->c_cflag & CSTOPB) {
+ wr4 |= SCC_WR4_2_STOP; /* 2 stop bits */
+ } else {
+ wr4 |= SCC_WR4_1_STOP; /* 1 stop bits */
+ }
+
+ /*
+ * Now actually set the chip
+ */
+
+ rtems_interrupt_disable(Irql);
+ (*setReg)( ulCtrlPort, SCC_WR0_SEL_WR4, wr4 );
+ (*setReg)( ulCtrlPort, SCC_WR0_SEL_WR3, wr3 );
+ (*setReg)( ulCtrlPort, SCC_WR0_SEL_WR5, wr5 );
+
+ /*
+ * Setup the lower 8 bits time constants=1E.
+ * If the time constans=1E, then the desire
+ * baud rate will be equilvalent to 9600, via register 12.
+ */
+
+ (*setReg)( ulCtrlPort, SCC_WR0_SEL_WR12, ulBaudDivisor & 0xff );
+
+ /*
+ * using register 13
+ * Setup the upper 8 bits time constant
+ */
+
+ (*setReg)( ulCtrlPort, SCC_WR0_SEL_WR13, (ulBaudDivisor>>8) & 0xff );
+
+ rtems_interrupt_enable(Irql);
+
+ return 0;
+}
+
+/*
+ * z85c30_process
+ *
+ * This is the per port ISR handler.
+ */
+
+Z85C30_STATIC void z85c30_process(
+ int minor,
+ uint8_t ucIntPend
+)
+{
+ uint32_t ulCtrlPort;
+ volatile uint8_t z85c30_status;
+ char cChar;
+ setRegister_f setReg;
+ getRegister_f getReg;
+
+ ulCtrlPort = Console_Port_Tbl[minor]->ulCtrlPort1;
+ setReg = Console_Port_Tbl[minor]->setRegister;
+ getReg = Console_Port_Tbl[minor]->getRegister;
+
+ /*
+ * Deal with any received characters
+ */
+
+ while (ucIntPend&SCC_RR3_B_RX_IP)
+ {
+ z85c30_status = (*getReg)(ulCtrlPort, SCC_WR0_SEL_RD0);
+ if (!Z85C30_Status_Is_RX_character_available(z85c30_status)) {
+ break;
+ }
+
+ /*
+ * Return the character read.
+ */
+
+ cChar = (*getReg)(ulCtrlPort, SCC_WR0_SEL_RD8);
+
+ rtems_termios_enqueue_raw_characters(
+ Console_Port_Data[minor].termios_data,
+ &cChar,
+ 1
+ );
+ }
+
+ /*
+ * There could be a race condition here if there is not yet a TX
+ * interrupt pending but the buffer is empty. This condition has
+ * been seen before on other z8530 drivers but has not been seen
+ * with this one. The typical solution is to use "vector includes
+ * status" or to only look at the interrupts actually pending
+ * in RR3.
+ */
+
+ while (true) {
+ z85c30_status = (*getReg)(ulCtrlPort, SCC_WR0_SEL_RD0);
+ if (!Z85C30_Status_Is_TX_buffer_empty(z85c30_status)) {
+ /*
+ * We'll get another interrupt when
+ * the transmitter holding reg. becomes
+ * free again and we are clear to send
+ */
+ break;
+ }
+
+#if 0
+ if (!Z85C30_Status_Is_CTS_asserted(z85c30_status)) {
+ /*
+ * We can't transmit yet
+ */
+ (*setReg)(ulCtrlPort, SCC_WR0_SEL_WR0, SCC_WR0_RST_TX_INT);
+ /*
+ * The next state change of CTS will wake us up
+ */
+ break;
+ }
+#endif
+
+ rtems_termios_dequeue_characters(Console_Port_Data[minor].termios_data, 1);
+ if (rtems_termios_dequeue_characters(
+ Console_Port_Data[minor].termios_data, 1)) {
+ if (Console_Port_Tbl[minor]->pDeviceFlow != &z85c30_flow_RTSCTS) {
+ z85c30_negate_RTS(minor);
+ }
+ Console_Port_Data[minor].bActive = FALSE;
+ z85c30_enable_interrupts(minor, SCC_ENABLE_ALL_INTR_EXCEPT_TX);
+ (*setReg)(ulCtrlPort, SCC_WR0_SEL_WR0, SCC_WR0_RST_TX_INT);
+ break;
+ }
+
+ }
+
+ if (ucIntPend & SCC_RR3_B_EXT_IP) {
+ /*
+ * Clear the external status interrupt
+ */
+ (*setReg)(ulCtrlPort, SCC_WR0_SEL_WR0, SCC_WR0_RST_INT);
+ z85c30_status = (*getReg)(ulCtrlPort, SCC_WR0_SEL_RD0);
+ }
+
+ /*
+ * Reset interrupts
+ */
+ (*setReg)(ulCtrlPort, SCC_WR0_SEL_WR0, SCC_WR0_RST_HI_IUS);
+}
+
+/*
+ * z85c30_isr
+ *
+ * This is the ISR handler for each Z8530.
+ */
+
+Z85C30_STATIC rtems_isr z85c30_isr(
+ rtems_vector_number vector
+)
+{
+ int minor;
+ uint32_t ulCtrlPort;
+ volatile uint8_t ucIntPend;
+ volatile uint8_t ucIntPendPort;
+ getRegister_f getReg;
+
+ for (minor=0;minor<Console_Port_Count;minor++) {
+ if(Console_Port_Tbl[minor]->ulIntVector == vector &&
+ Console_Port_Tbl[minor]->deviceType == SERIAL_Z85C30 ) {
+ ulCtrlPort = Console_Port_Tbl[minor]->ulCtrlPort2;
+ getReg = Console_Port_Tbl[minor]->getRegister;
+ do {
+ ucIntPend = (*getReg)(ulCtrlPort, SCC_WR0_SEL_RD3);
+
+ /*
+ * If this is channel A select channel A status
+ */
+
+ if (ulCtrlPort == Console_Port_Tbl[minor]->ulCtrlPort1) {
+ ucIntPendPort = ucIntPend >> 3;
+ ucIntPendPort &= 7;
+ } else {
+ ucIntPendPort = ucIntPend &= 7;
+ }
+
+ if (ucIntPendPort) {
+ z85c30_process(minor, ucIntPendPort);
+ }
+ } while (ucIntPendPort);
+ }
+ }
+}
+
+/*
+ * z85c30_enable_interrupts
+ *
+ * This routine enables the specified interrupts for this minor.
+ */
+
+Z85C30_STATIC void z85c30_enable_interrupts(
+ int minor,
+ int interrupt_mask
+)
+{
+ uint32_t ulCtrlPort;
+ setRegister_f setReg;
+
+ ulCtrlPort = Console_Port_Tbl[minor]->ulCtrlPort1;
+ setReg = Console_Port_Tbl[minor]->setRegister;
+
+ (*setReg)(ulCtrlPort, SCC_WR0_SEL_WR1, interrupt_mask);
+}
+
+/*
+ * z85c30_initialize_interrupts
+ *
+ * This routine initializes the port to use interrupts.
+ */
+
+Z85C30_STATIC void z85c30_initialize_interrupts(
+ int minor
+)
+{
+ uint32_t ulCtrlPort1;
+ setRegister_f setReg;
+
+ ulCtrlPort1 = Console_Port_Tbl[minor]->ulCtrlPort1;
+ setReg = Console_Port_Tbl[minor]->setRegister;
+
+
+ z85c30_init(minor);
+
+ Console_Port_Data[minor].bActive=FALSE;
+
+ z85c30_initialize_port( minor );
+
+ if (Console_Port_Tbl[minor]->pDeviceFlow != &z85c30_flow_RTSCTS) {
+ z85c30_negate_RTS(minor);
+ }
+
+#if (CPU_SIMPLE_VECTORED_INTERRUPTS == TRUE)
+ set_vector(z85c30_isr, Console_Port_Tbl[minor]->ulIntVector, 1);
+#endif
+
+ z85c30_enable_interrupts(minor, SCC_ENABLE_ALL_INTR_EXCEPT_TX);
+
+ (*setReg)(ulCtrlPort1, SCC_WR0_SEL_WR2, 0); /* XXX vector */
+ (*setReg)(ulCtrlPort1, SCC_WR0_SEL_WR9, SCC_WR9_MIE);
+
+ /*
+ * Reset interrupts
+ */
+
+ (*setReg)(ulCtrlPort1, SCC_WR0_SEL_WR0, SCC_WR0_RST_INT);
+}
+
+/*
+ * z85c30_write_support_int
+ *
+ * Console Termios output entry point.
+ *
+ */
+
+Z85C30_STATIC ssize_t z85c30_write_support_int(
+ int minor,
+ const char *buf,
+ size_t len)
+{
+ uint32_t Irql;
+ uint32_t ulCtrlPort;
+ setRegister_f setReg;
+
+ ulCtrlPort = Console_Port_Tbl[minor]->ulCtrlPort1;
+ setReg = Console_Port_Tbl[minor]->setRegister;
+
+ /*
+ * We are using interrupt driven output and termios only sends us
+ * one character at a time.
+ */
+
+ if ( !len )
+ return 0;
+
+ /*
+ * Put the character out and enable interrupts if necessary.
+ */
+
+ if (Console_Port_Tbl[minor]->pDeviceFlow != &z85c30_flow_RTSCTS) {
+ z85c30_assert_RTS(minor);
+ }
+ rtems_interrupt_disable(Irql);
+ if ( Console_Port_Data[minor].bActive == FALSE) {
+ Console_Port_Data[minor].bActive = TRUE;
+ z85c30_enable_interrupts(minor, SCC_ENABLE_ALL_INTR);
+ }
+ (*setReg)(ulCtrlPort, SCC_WR0_SEL_WR8, *buf);
+ rtems_interrupt_enable(Irql);
+
+ return 0;
+}
+
+/*
+ * z85c30_inbyte_nonblocking_polled
+ *
+ * This routine polls for a character.
+ */
+
+Z85C30_STATIC int z85c30_inbyte_nonblocking_polled(
+ int minor
+)
+{
+ volatile uint8_t z85c30_status;
+ uint32_t ulCtrlPort;
+ getRegister_f getReg;
+
+ ulCtrlPort = Console_Port_Tbl[minor]->ulCtrlPort1;
+ getReg = Console_Port_Tbl[minor]->getRegister;
+
+ /*
+ * return -1 if a character is not available.
+ */
+ z85c30_status = (*getReg)(ulCtrlPort, SCC_WR0_SEL_RD0);
+ if (!Z85C30_Status_Is_RX_character_available(z85c30_status)) {
+ return -1;
+ }
+
+ /*
+ * Return the character read.
+ */
+
+ return (*getReg)(ulCtrlPort, SCC_WR0_SEL_RD8);
+}
+
+/*
+ * z85c30_write_support_polled
+ *
+ * Console Termios output entry point.
+ *
+ */
+
+Z85C30_STATIC ssize_t z85c30_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) {
+ z85c30_write_polled(minor, *buf++);
+ nwrite++;
+ }
+
+ /*
+ * return the number of bytes written.
+ */
+ return nwrite;
+}
+
+/*
+ * z85c30_write_polled
+ *
+ * This routine transmits a character using polling.
+ */
+
+Z85C30_STATIC void z85c30_write_polled(
+ int minor,
+ char cChar
+)
+{
+ volatile uint8_t z85c30_status;
+ uint32_t ulCtrlPort;
+ getRegister_f getReg;
+ setRegister_f setReg;
+
+ ulCtrlPort = Console_Port_Tbl[minor]->ulCtrlPort1;
+ getReg = Console_Port_Tbl[minor]->getRegister;
+ setReg = Console_Port_Tbl[minor]->setRegister;
+
+ /*
+ * Wait for the Transmit buffer to indicate that it is empty.
+ */
+
+ z85c30_status = (*getReg)( ulCtrlPort, SCC_WR0_SEL_RD0 );
+
+ while (!Z85C30_Status_Is_TX_buffer_empty(z85c30_status)) {
+ /*
+ * Yield while we wait
+ */
+#if 0
+ if (_System_state_Is_up(_System_state_Get())) {
+ rtems_task_wake_after(RTEMS_YIELD_PROCESSOR);
+ }
+#endif
+ z85c30_status = (*getReg)(ulCtrlPort, SCC_WR0_SEL_RD0);
+ }
+
+ /*
+ * Write the character.
+ */
+
+ (*setReg)( ulCtrlPort, SCC_WR0_SEL_WR8, cChar );
+}
diff --git a/bsps/shared/dev/serial/z85c30_p.h b/bsps/shared/dev/serial/z85c30_p.h
new file mode 100644
index 0000000000..af2ed6507c
--- /dev/null
+++ b/bsps/shared/dev/serial/z85c30_p.h
@@ -0,0 +1,420 @@
+/*
+ * This include file contains all private driver definitions for the
+ * Zilog z85c30.
+ *
+ * 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-1997.
+ * On-Line Applications Research Corporation (OAR).
+ *
+ * The license and distribution terms for this file may in
+ * the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#ifndef __Z85C30_P_H
+#define __Z85C30_P_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Define Z85C30_STATIC to nothing while debugging so the entry points
+ * will show up in the symbol table.
+ */
+
+#define Z85C30_STATIC
+
+/* #define Z85C30_STATIC static */
+
+/* bit values for write register 0 */
+/* command register */
+
+#define SCC_WR0_SEL_WR0 0x00
+#define SCC_WR0_SEL_WR1 0x01
+#define SCC_WR0_SEL_WR2 0x02
+#define SCC_WR0_SEL_WR3 0x03
+#define SCC_WR0_SEL_WR4 0x04
+#define SCC_WR0_SEL_WR5 0x05
+#define SCC_WR0_SEL_WR6 0x06
+#define SCC_WR0_SEL_WR7 0x07
+#define SCC_WR0_SEL_WR8 0x08
+#define SCC_WR0_SEL_WR9 0x09
+#define SCC_WR0_SEL_WR10 0x0a
+#define SCC_WR0_SEL_WR11 0x0b
+#define SCC_WR0_SEL_WR12 0x0c
+#define SCC_WR0_SEL_WR13 0x0d
+#define SCC_WR0_SEL_WR14 0x0e
+#define SCC_WR0_SEL_WR15 0x0f
+#define SCC_WR0_SEL_RD0 0x00
+#define SCC_WR0_SEL_RD1 0x01
+#define SCC_WR0_SEL_RD2 0x02
+#define SCC_WR0_SEL_RD3 0x03
+#define SCC_WR0_SEL_RD4 0x04
+#define SCC_WR0_SEL_RD5 0x05
+#define SCC_WR0_SEL_RD6 0x06
+#define SCC_WR0_SEL_RD7 0x07
+#define SCC_WR0_SEL_RD8 0x08
+#define SCC_WR0_SEL_RD9 0x09
+#define SCC_WR0_SEL_RD10 0x0a
+#define SCC_WR0_SEL_RD11 0x0b
+#define SCC_WR0_SEL_RD12 0x0c
+#define SCC_WR0_SEL_RD13 0x0d
+#define SCC_WR0_SEL_RD14 0x0e
+#define SCC_WR0_SEL_RD15 0x0f
+#define SCC_WR0_NULL_CODE 0x00
+#define SCC_WR0_RST_INT 0x10
+#define SCC_WR0_SEND_ABORT 0x18
+#define SCC_WR0_EN_INT_RX 0x20
+#define SCC_WR0_RST_TX_INT 0x28
+#define SCC_WR0_ERR_RST 0x30
+#define SCC_WR0_RST_HI_IUS 0x38
+#define SCC_WR0_RST_RX_CRC 0x40
+#define SCC_WR0_RST_TX_CRC 0x80
+#define SCC_WR0_RST_TX_UND 0xc0
+
+/* write register 2 */
+/* interrupt vector */
+
+/* bit values for write register 1 */
+/* tx/rx interrupt and data transfer mode definition */
+
+#define SCC_WR1_EXT_INT_EN 0x01
+#define SCC_WR1_TX_INT_EN 0x02
+#define SCC_WR1_PARITY 0x04
+#define SCC_WR1_RX_INT_DIS 0x00
+#define SCC_WR1_RX_INT_FIR 0x08
+#define SCC_WR1_INT_ALL_RX 0x10
+#define SCC_WR1_RX_INT_SPE 0x18
+#define SCC_WR1_RDMA_RECTR 0x20
+#define SCC_WR1_RDMA_FUNC 0x40
+#define SCC_WR1_RDMA_EN 0x80
+
+#define SCC_ENABLE_ALL_INTR \
+ (SCC_WR1_EXT_INT_EN | SCC_WR1_TX_INT_EN | SCC_WR1_INT_ALL_RX)
+
+#define SCC_DISABLE_ALL_INTR 0x00
+
+#define SCC_ENABLE_ALL_INTR_EXCEPT_TX \
+ (SCC_WR1_EXT_INT_EN | SCC_WR1_INT_ALL_RX)
+
+/* bit values for write register 3 */
+/* receive parameters and control */
+
+#define SCC_WR3_RX_EN 0x01
+#define SCC_WR3_SYNC_CHAR 0x02
+#define SCC_WR3_ADR_SEARCH 0x04
+#define SCC_WR3_RX_CRC_EN 0x08
+#define SCC_WR3_ENTER_HUNT 0x10
+#define SCC_WR3_AUTO_EN 0x20
+#define SCC_WR3_RX_5_BITS 0x00
+#define SCC_WR3_RX_7_BITS 0x40
+#define SCC_WR3_RX_6_BITS 0x80
+#define SCC_WR3_RX_8_BITS 0xc0
+
+/* bit values for write register 4 */
+/* tx/rx misc parameters and modes */
+
+#define SCC_WR4_PAR_EN 0x01
+#define SCC_WR4_PAR_EVEN 0x02
+#define SCC_WR4_SYNC_EN 0x00
+#define SCC_WR4_1_STOP 0x04
+#define SCC_WR4_2_STOP 0x0c
+#define SCC_WR4_8_SYNC 0x00
+#define SCC_WR4_16_SYNC 0x10
+#define SCC_WR4_SDLC 0x20
+#define SCC_WR4_EXT_SYNC 0x30
+#define SCC_WR4_1_CLOCK 0x00
+#define SCC_WR4_16_CLOCK 0x40
+#define SCC_WR4_32_CLOCK 0x80
+#define SCC_WR4_64_CLOCK 0xc0
+
+/* bit values for write register 5 */
+/* transmit parameter and controls */
+
+#define SCC_WR5_TX_CRC_EN 0x01
+#define SCC_WR5_RTS 0x02
+#define SCC_WR5_SDLC 0x04
+#define SCC_WR5_TX_EN 0x08
+#define SCC_WR5_SEND_BRK 0x10
+
+#define SCC_WR5_TX_5_BITS 0x00
+#define SCC_WR5_TX_7_BITS 0x20
+#define SCC_WR5_TX_6_BITS 0x40
+#define SCC_WR5_TX_8_BITS 0x60
+#define SCC_WR5_DTR 0x80
+
+/* write register 6 */
+/* sync chars or sdlc address field */
+
+/* write register 7 */
+/* sync char or sdlc flag */
+
+/* write register 8 */
+/* transmit buffer */
+
+/* bit values for write register 9 */
+/* master interrupt control */
+
+#define SCC_WR9_VIS 0x01
+#define SCC_WR9_NV 0x02
+#define SCC_WR9_DLC 0x04
+#define SCC_WR9_MIE 0x08
+#define SCC_WR9_STATUS_HI 0x10
+#define SCC_WR9_NO_RST 0x00
+#define SCC_WR9_CH_B_RST 0x40
+#define SCC_WR9_CH_A_RST 0x80
+#define SCC_WR9_HDWR_RST 0xc0
+
+/* bit values for write register 10 */
+/* misc tx/rx control bits */
+
+#define SCC_WR10_6_BIT_SYNC 0x01
+#define SCC_WR10_LOOP_MODE 0x02
+#define SCC_WR10_ABORT_UND 0x04
+#define SCC_WR10_MARK_IDLE 0x08
+#define SCC_WR10_ACT_POLL 0x10
+#define SCC_WR10_NRZ 0x00
+#define SCC_WR10_NRZI 0x20
+#define SCC_WR10_FM1 0x40
+#define SCC_WR10_FM0 0x60
+#define SCC_WR10_CRC_PRESET 0x80
+
+/* bit values for write register 11 */
+/* clock mode control */
+
+#define SCC_WR11_OUT_XTAL 0x00
+#define SCC_WR11_OUT_TX_CLK 0x01
+#define SCC_WR11_OUT_BR_GEN 0x02
+#define SCC_WR11_OUT_DPLL 0x03
+#define SCC_WR11_TRXC_OI 0x04
+#define SCC_WR11_TX_RTXC 0x00
+#define SCC_WR11_TX_TRXC 0x08
+#define SCC_WR11_TX_BR_GEN 0x10
+#define SCC_WR11_TX_DPLL 0x18
+#define SCC_WR11_RX_RTXC 0x00
+#define SCC_WR11_RX_TRXC 0x20
+#define SCC_WR11_RX_BR_GEN 0x40
+#define SCC_WR11_RX_DPLL 0x60
+#define SCC_WR11_RTXC_XTAL 0x80
+
+/* write register 12 */
+/* lower byte of baud rate generator time constant */
+
+/* write register 13 */
+/* upper byte of baud rate generator time constant */
+
+/* bit values for write register 14 */
+/* misc control bits */
+
+#define SCC_WR14_BR_EN 0x01
+#define SCC_WR14_BR_SRC 0x02
+#define SCC_WR14_DTR_FUNC 0x04
+#define SCC_WR14_AUTO_ECHO 0x08
+#define SCC_WR14_LCL_LOOP 0x10
+#define SCC_WR14_NULL 0x00
+#define SCC_WR14_SEARCH 0x20
+#define SCC_WR14_RST_CLK 0x40
+#define SCC_WR14_DIS_DPLL 0x60
+#define SCC_WR14_SRC_BR 0x80
+#define SCC_WR14_SRC_RTXC 0xa0
+#define SCC_WR14_FM_MODE 0xc0
+#define SCC_WR14_NRZI 0xe0
+
+/* bit values for write register 15 */
+/* external/status interrupt control */
+
+#define SCC_WR15_ZERO_CNT 0x02
+#define SCC_WR15_CD_IE 0x08
+#define SCC_WR15_SYNC_IE 0x10
+#define SCC_WR15_CTS_IE 0x20
+#define SCC_WR15_TX_UND_IE 0x40
+#define SCC_WR15_BREAK_IE 0x80
+
+/* bit values for read register 0 */
+/* tx/rx buffer status and external status */
+
+#define SCC_RR0_RX_AVAIL 0x01
+#define SCC_RR0_ZERO_CNT 0x02
+#define SCC_RR0_TX_EMPTY 0x04
+#define SCC_RR0_CD 0x08
+#define SCC_RR0_SYNC 0x10
+#define SCC_RR0_CTS 0x20
+#define SCC_RR0_TX_UND 0x40
+#define SCC_RR0_BREAK 0x80
+
+/* bit values for read register 1 */
+
+#define SCC_RR1_ALL_SENT 0x01
+#define SCC_RR1_RES_CD_2 0x02
+#define SCC_RR1_RES_CD_1 0x01
+#define SCC_RR1_RES_CD_0 0x08
+#define SCC_RR1_PAR_ERR 0x10
+#define SCC_RR1_RX_OV_ERR 0x20
+#define SCC_RR1_CRC_ERR 0x40
+#define SCC_RR1_END_FRAME 0x80
+
+/* read register 2 */
+/* interrupt vector */
+
+/* bit values for read register 3 */
+/* interrupt pending register */
+
+#define SCC_RR3_B_EXT_IP 0x01
+#define SCC_RR3_B_TX_IP 0x02
+#define SCC_RR3_B_RX_IP 0x04
+#define SCC_RR3_A_EXT_IP 0x08
+#define SCC_RR3_A_TX_IP 0x10
+#define SCC_RR3_A_RX_IP 0x20
+
+/* read register 8 */
+/* receive data register */
+
+/* bit values for read register 10 */
+/* misc status bits */
+
+#define SCC_RR10_ON_LOOP 0x02
+#define SCC_RR10_LOOP_SEND 0x10
+#define SCC_RR10_2_CLK_MIS 0x40
+#define SCC_RR10_1_CLK_MIS 0x80
+
+/* read register 12 */
+/* lower byte of time constant */
+
+/* read register 13 */
+/* upper byte of time constant */
+
+/* bit values for read register 15 */
+/* external/status ie bits */
+
+#define SCC_RR15_ZERO_CNT 0x02
+#define SCC_RR15_CD_IE 0x08
+#define SCC_RR15_SYNC_IE 0x10
+#define SCC_RR15_CTS_IE 0x20
+#define SCC_RR15_TX_UND_IE 0x40
+#define SCC_RR15_BREAK_IE 0x80
+
+typedef struct _z85c30_context
+{
+ uint8_t ucModemCtrl;
+} z85c30_context;
+
+/*
+ * The following macro calculates the Baud constant. For the Z85C30 chip.
+ *
+ * Note: baud constant = ((clock frequency / Clock_X) / (2 * Baud Rate)) - 2
+ * eg ((10,000,000 / 16) / (2 * Baud Rate)) - 2
+ */
+
+#define Z85C30_Baud( _clock, _baud_rate ) \
+ ( ((_clock) /( 16 * 2 * _baud_rate)) - 2)
+
+#define Z85C30_Status_Is_RX_character_available(_status) \
+ ((_status) & SCC_RR0_RX_AVAIL)
+
+#define Z85C30_Status_Is_TX_buffer_empty(_status) \
+ ((_status) & SCC_RR0_TX_EMPTY)
+
+#define Z85C30_Status_Is_CTS_asserted(_status) \
+ ((_status) & SCC_RR0_CTS)
+
+#define Z85C30_Status_Is_break_abort(_status) \
+ ((_status) & SCC_RR0_BREAK)
+
+/*
+ * Private routines
+ */
+
+Z85C30_STATIC void z85c30_initialize_port(
+ int minor
+);
+
+Z85C30_STATIC void z85c30_init(int minor);
+
+Z85C30_STATIC int z85c30_set_attributes(
+ int minor,
+ const struct termios *t
+);
+
+Z85C30_STATIC int z85c30_open(
+ int major,
+ int minor,
+ void * arg
+);
+
+Z85C30_STATIC int z85c30_close(
+ int major,
+ int minor,
+ void * arg
+);
+
+Z85C30_STATIC void z85c30_write_polled(
+ int minor,
+ char cChar
+);
+
+Z85C30_STATIC int z85c30_assert_RTS(
+ int minor
+);
+
+Z85C30_STATIC int z85c30_negate_RTS(
+ int minor
+);
+
+Z85C30_STATIC int z85c30_assert_DTR(
+ int minor
+);
+
+Z85C30_STATIC int z85c30_negate_DTR(
+ int minor
+);
+
+Z85C30_STATIC void z85c30_initialize_interrupts(int minor);
+
+Z85C30_STATIC ssize_t z85c30_write_support_int(
+ int minor,
+ const char *buf,
+ size_t len
+);
+
+Z85C30_STATIC ssize_t z85c30_write_support_polled(
+ int minor,
+ const char *buf,
+ size_t len
+);
+
+Z85C30_STATIC int z85c30_inbyte_nonblocking_polled(
+ int minor
+);
+
+Z85C30_STATIC void z85c30_enable_interrupts(
+ int minor,
+ int interrupt_mask
+);
+
+Z85C30_STATIC void z85c30_process(
+ int minor,
+ uint8_t ucIntPend
+);
+
+Z85C30_STATIC rtems_isr z85c30_isr(
+ rtems_vector_number vector
+);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/bsps/shared/dev/serial/z85c30_reg.c b/bsps/shared/dev/serial/z85c30_reg.c
new file mode 100644
index 0000000000..6e7b5d3494
--- /dev/null
+++ b/bsps/shared/dev/serial/z85c30_reg.c
@@ -0,0 +1,72 @@
+/*
+ * This file contains a typical set of register access routines which may be
+ * used with the z85c30 chip if accesses to the chip are as follows:
+ *
+ * + registers are accessed as bytes
+ *
+ * COPYRIGHT (c) 1989-1997.
+ * 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 <rtems.h>
+
+#include <libchip/z85c30.h>
+
+#ifndef _Z85C30_MULTIPLIER
+#define _Z85C30_MULTIPLIER 1
+#define _Z85C30_NAME(_X) _X
+#define _Z85C30_TYPE uint8_t
+#endif
+
+/*
+ * Z85C30 Get Register Routine
+ */
+
+uint8_t _Z85C30_NAME(z85c30_get_register)(
+ uintptr_t ulCtrlPort,
+ uint8_t ucRegNum
+)
+{
+ _Z85C30_TYPE *port;
+ uint8_t data;
+ rtems_interrupt_level level;
+
+ port = (_Z85C30_TYPE *)ulCtrlPort;
+
+ rtems_interrupt_disable(level);
+
+ if(ucRegNum) {
+ *port = ucRegNum;
+ }
+ data = *port;
+ rtems_interrupt_enable(level);
+
+ return data;
+}
+
+/*
+ * Z85C30 Set Register Routine
+ */
+
+void _Z85C30_NAME(z85c30_set_register)(
+ uintptr_t ulCtrlPort,
+ uint8_t ucRegNum,
+ uint8_t ucData
+)
+{
+ _Z85C30_TYPE *port;
+ rtems_interrupt_level level;
+
+ port = (_Z85C30_TYPE *)ulCtrlPort;
+
+ rtems_interrupt_disable(level);
+ if(ucRegNum) {
+ *port = ucRegNum;
+ }
+ *port = ucData;
+ rtems_interrupt_enable(level);
+}