diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-04-03 07:20:11 +0200 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-04-04 10:13:28 +0200 |
commit | 27de4e1fb8bcdbdd8cb882fc0d7a2c152b4e027a (patch) | |
tree | def0664dcddc53fd5d599b455c64f76ca2293606 /bsps/shared/dev/serial | |
parent | bsps: Move config macros to RTEMS_BSP_CONFIGURE (diff) | |
download | rtems-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/README | 13 | ||||
-rw-r--r-- | bsps/shared/dev/serial/README.mc68681 | 83 | ||||
-rw-r--r-- | bsps/shared/dev/serial/README.ns16550 | 82 | ||||
-rw-r--r-- | bsps/shared/dev/serial/README.xr88681 | 2 | ||||
-rw-r--r-- | bsps/shared/dev/serial/README.z85c30 | 74 | ||||
-rw-r--r-- | bsps/shared/dev/serial/STATUS | 48 | ||||
-rw-r--r-- | bsps/shared/dev/serial/mc68681.c | 776 | ||||
-rw-r--r-- | bsps/shared/dev/serial/mc68681_baud.c | 124 | ||||
-rw-r--r-- | bsps/shared/dev/serial/mc68681_p.h | 323 | ||||
-rw-r--r-- | bsps/shared/dev/serial/mc68681_reg.c | 61 | ||||
-rw-r--r-- | bsps/shared/dev/serial/mc68681_reg2.c | 20 | ||||
-rw-r--r-- | bsps/shared/dev/serial/mc68681_reg4.c | 20 | ||||
-rw-r--r-- | bsps/shared/dev/serial/mc68681_reg8.c | 20 | ||||
-rw-r--r-- | bsps/shared/dev/serial/ns16550-context.c | 814 | ||||
-rw-r--r-- | bsps/shared/dev/serial/ns16550.c | 875 | ||||
-rw-r--r-- | bsps/shared/dev/serial/serprobe.c | 13 | ||||
-rw-r--r-- | bsps/shared/dev/serial/z85c30.c | 893 | ||||
-rw-r--r-- | bsps/shared/dev/serial/z85c30_p.h | 420 | ||||
-rw-r--r-- | bsps/shared/dev/serial/z85c30_reg.c | 72 |
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); +} |