summaryrefslogtreecommitdiff
path: root/ecos/packages/devs/serial/m68k/mcf52xx/current/src/ser_mcf52xx.c
diff options
context:
space:
mode:
authorMichael Gielda <mgielda@antmicro.com>2014-04-03 14:53:04 +0200
committerMichael Gielda <mgielda@antmicro.com>2014-04-03 14:53:04 +0200
commitae1e4e08a1005a0c487f03ba189d7536e7fdcba6 (patch)
treef1c296f8a966a9a39876b0e98e16d9c5da1776dd /ecos/packages/devs/serial/m68k/mcf52xx/current/src/ser_mcf52xx.c
parentf157da5337118d3c5cd464266796de4262ac9dbd (diff)
Added the OS files
Diffstat (limited to 'ecos/packages/devs/serial/m68k/mcf52xx/current/src/ser_mcf52xx.c')
-rw-r--r--ecos/packages/devs/serial/m68k/mcf52xx/current/src/ser_mcf52xx.c850
1 files changed, 850 insertions, 0 deletions
diff --git a/ecos/packages/devs/serial/m68k/mcf52xx/current/src/ser_mcf52xx.c b/ecos/packages/devs/serial/m68k/mcf52xx/current/src/ser_mcf52xx.c
new file mode 100644
index 0000000..2a0e7e3
--- /dev/null
+++ b/ecos/packages/devs/serial/m68k/mcf52xx/current/src/ser_mcf52xx.c
@@ -0,0 +1,850 @@
+//==========================================================================
+//
+// ser_mcfxxxx.c
+//
+// Serial driver for Freescale coldfire processors
+//
+//==========================================================================
+// ####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 2003, 2004, 2006, 2008 Free Software Foundation, Inc.
+//
+// eCos is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 2 or (at your option) any later
+// version.
+//
+// eCos is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with eCos; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+// As a special exception, if other files instantiate templates or use
+// macros or inline functions from this file, or you compile this file
+// and link it with other works to produce a work based on this file,
+// this file does not by itself cause the resulting work to be covered by
+// the GNU General Public License. However the source code for this file
+// must still be made available in accordance with section (3) of the GNU
+// General Public License v2.
+//
+// This exception does not invalidate any other reasons why a work based
+// on this file might be covered by the GNU General Public License.
+// -------------------------------------------
+// ####ECOSGPLCOPYRIGHTEND####
+//==========================================================================
+//#####DESCRIPTIONBEGIN####
+//
+// Author(s): bartv
+// Contributors: bartv
+// Date: 2003-06-04
+// Purpose: support coldfire on-chip uart's
+// Description: The various coldfire mcfxxxx processors all use the same
+// basic UART. There are some variations, e.g. different
+// fifo sizes, autobaud capability, and calculating baud
+// rates requires platform-specific knowledge such as the
+// cpu speed. Also there is no standardization of base
+// addresses or interrupt vectors. Never the less a single
+// driver should be able to support most devices, with
+// various processor-specific or platform-specific #define's
+// and other support.
+//
+//####DESCRIPTIONEND####
+//==========================================================================
+
+// NOTE: some platforms may use GPIO pins for other modem lines such as
+// ring and DSR/DTR/DCD. This code could check for #ifdef HAL_MCF52xx_UART_SET_DCD()
+// and incorporate support from the platform HAL.
+
+#include <pkgconf/system.h>
+#include <pkgconf/io_serial.h>
+#include CYGBLD_HAL_VARIANT_H
+#include CYGBLD_HAL_PROC_H
+#include CYGBLD_HAL_PLATFORM_H
+#include <pkgconf/devs_serial_mcfxxxx.h>
+
+#include <cyg/io/io.h>
+#include <cyg/io/devtab.h>
+#include <cyg/io/serial.h>
+
+#include <cyg/hal/hal_arch.h>
+#include <cyg/hal/hal_intr.h>
+#include <cyg/hal/hal_io.h>
+
+//#define MCFxxxx_SERIAL_STATS 1
+#undef MCFxxxx_SERIAL_STATS
+
+#ifdef MCFxxxx_SERIAL_STATS
+# define INCR_STAT(_info_, _field_, _amount_) \
+ CYG_MACRO_START \
+ (_info_)->_field_ += _amount_; \
+ CYG_MACRO_END
+#else
+# define INCR_STAT(_info_, _field_, _amount_) \
+ CYG_MACRO_START \
+ CYG_MACRO_END
+#endif
+
+// ----------------------------------------------------------------------------
+// devtab entries for the supported devices.
+
+static bool mcfxxxx_serial_init(struct cyg_devtab_entry*);
+static Cyg_ErrNo mcfxxxx_serial_lookup(struct cyg_devtab_entry**, struct cyg_devtab_entry*, const char*);
+static Cyg_ErrNo mcfxxxx_serial_set_config(serial_channel*, cyg_uint32, const void*, cyg_uint32*);
+static bool mcfxxxx_serial_putc(serial_channel*, unsigned char);
+static unsigned char mcfxxxx_serial_getc(serial_channel*);
+static void mcfxxxx_serial_start_xmit(serial_channel*);
+static void mcfxxxx_serial_stop_xmit(serial_channel*);
+static cyg_uint32 mcfxxxx_serial_isr(cyg_vector_t, cyg_addrword_t);
+static void mcfxxxx_serial_dsr(cyg_vector_t, cyg_ucount32, cyg_addrword_t);
+
+typedef struct mcfxxxx_serial_info {
+ cyg_uint8* base;
+ cyg_vector_t isr_vec;
+ int isr_priority;
+ cyg_uint8 uimr_shadow;
+ cyg_uint8 umr1_shadow;
+ cyg_uint8 umr2_shadow;
+ cyg_uint8 flags;
+ cyg_interrupt serial_interrupt;
+ cyg_handle_t serial_interrupt_handle;
+#ifdef MCFxxxx_SERIAL_STATS
+ cyg_uint32 isr_count;
+ cyg_uint32 dsr_count;
+ cyg_uint32 rx_bytes;
+ cyg_uint32 tx_bytes;
+ cyg_uint32 rx_errors;
+#endif
+} mcfxxxx_serial_info;
+
+#define MCFxxxx_SERIAL_RTS (0x01 << 0)
+#define MCFxxxx_SERIAL_CTS (0x01 << 1)
+#define MCFxxxx_SERIAL_RS485_RTS (0x01 << 2)
+
+static SERIAL_FUNS(mcfxxxx_serial_funs,
+ mcfxxxx_serial_putc,
+ mcfxxxx_serial_getc,
+ mcfxxxx_serial_set_config,
+ mcfxxxx_serial_start_xmit,
+ mcfxxxx_serial_stop_xmit
+ );
+
+
+#ifdef CYGPKG_DEVS_SERIAL_MCFxxxx_SERIAL0
+static mcfxxxx_serial_info mcfxxxx_serial0_info = {
+ base: (cyg_uint8*)HAL_MCFxxxx_UART0_BASE,
+ isr_vec: CYGNUM_HAL_ISR_UART0,
+ isr_priority: CYGNUM_DEVS_SERIAL_MCFxxxx_SERIAL0_ISR_PRIORITY,
+#ifdef MCFxxxx_SERIAL_STATS
+ isr_count: 0,
+ dsr_count: 0,
+ rx_bytes: 0,
+ tx_bytes: 0,
+ rx_errors: 0,
+#endif
+ flags:
+#if defined(CYGHWR_HAL_M68K_MCFxxxx_UART0_RS485_RTS)
+ MCFxxxx_SERIAL_RS485_RTS |
+#elif defined(CYGHWR_HAL_M68K_MCFxxxx_UART0_RTS)
+ MCFxxxx_SERIAL_RTS |
+#endif
+#if defined(CYGHWR_HAL_M68K_MCFxxxx_UART0_CTS)
+ MCFxxxx_SERIAL_CTS |
+#endif
+ 0x00
+};
+
+# ifdef CYGNUM_DEVS_SERIAL_MCFxxxx_SERIAL0_BUFSIZE
+static unsigned char mcfxxxx_serial0_tx_buf[CYGNUM_DEVS_SERIAL_MCFxxxx_SERIAL0_BUFSIZE];
+static unsigned char mcfxxxx_serial0_rx_buf[CYGNUM_DEVS_SERIAL_MCFxxxx_SERIAL0_BUFSIZE];
+
+static SERIAL_CHANNEL_USING_INTERRUPTS(mcfxxxx_serial0_chan,
+ mcfxxxx_serial_funs,
+ mcfxxxx_serial0_info,
+ CYG_SERIAL_BAUD_RATE(CYGNUM_DEVS_SERIAL_MCFxxxx_SERIAL0_BAUD),
+ CYG_SERIAL_STOP_DEFAULT,
+ CYG_SERIAL_PARITY_DEFAULT,
+ CYG_SERIAL_WORD_LENGTH_DEFAULT,
+ CYG_SERIAL_FLAGS_DEFAULT,
+ mcfxxxx_serial0_tx_buf, CYGNUM_DEVS_SERIAL_MCFxxxx_SERIAL0_BUFSIZE,
+ mcfxxxx_serial0_rx_buf, CYGNUM_DEVS_SERIAL_MCFxxxx_SERIAL0_BUFSIZE
+ );
+#else
+static SERIAL_CHANNEL(mcfxxxx_serial0_chan,
+ mcfxxxx_serial_funs,
+ mcfxxxx_serial0_info,
+ CYG_SERIAL_BAUD_RATE(CYGNUM_DEVS_SERIAL_MCFxxxx_SERIAL0_BAUD),
+ CYG_SERIAL_STOP_DEFAULT,
+ CYG_SERIAL_PARITY_DEFAULT,
+ CYG_SERIAL_WORD_LENGTH_DEFAULT,
+ CYG_SERIAL_FLAGS_DEFAULT
+ );
+# endif
+
+DEVTAB_ENTRY(mcfxxxx_serial0_devtab,
+ CYGDAT_DEVS_SERIAL_MCFxxxx_SERIAL0_NAME,
+ 0, // Does not depend on a lower level interface
+ &cyg_io_serial_devio,
+ mcfxxxx_serial_init,
+ mcfxxxx_serial_lookup, // Serial driver may need initializing
+ &mcfxxxx_serial0_chan
+ );
+#endif
+
+#ifdef CYGPKG_DEVS_SERIAL_MCFxxxx_SERIAL1
+static mcfxxxx_serial_info mcfxxxx_serial1_info = {
+ base: (cyg_uint8*)HAL_MCFxxxx_UART1_BASE,
+ isr_vec: CYGNUM_HAL_ISR_UART1,
+ isr_priority: CYGNUM_DEVS_SERIAL_MCFxxxx_SERIAL1_ISR_PRIORITY,
+#ifdef MCFxxxx_SERIAL_STATS
+ isr_count: 0,
+ dsr_count: 0,
+ rx_bytes: 0,
+ tx_bytes: 0,
+ rx_errors: 0,
+#endif
+ flags:
+#if defined(CYGHWR_HAL_M68K_MCFxxxx_UART1_RS485_RTS)
+ MCFxxxx_SERIAL_RS485_RTS |
+#elif defined(CYGHWR_HAL_M68K_MCFxxxx_UART1_RTS)
+ MCFxxxx_SERIAL_RTS |
+#endif
+#if defined(CYGHWR_HAL_M68K_MCFxxxx_UART1_CTS)
+ MCFxxxx_SERIAL_CTS |
+#endif
+ 0x00
+};
+
+# ifdef CYGNUM_DEVS_SERIAL_MCFxxxx_SERIAL1_BUFSIZE
+static unsigned char mcfxxxx_serial1_tx_buf[CYGNUM_DEVS_SERIAL_MCFxxxx_SERIAL1_BUFSIZE];
+static unsigned char mcfxxxx_serial1_rx_buf[CYGNUM_DEVS_SERIAL_MCFxxxx_SERIAL1_BUFSIZE];
+
+static SERIAL_CHANNEL_USING_INTERRUPTS(mcfxxxx_serial1_chan,
+ mcfxxxx_serial_funs,
+ mcfxxxx_serial1_info,
+ CYG_SERIAL_BAUD_RATE(CYGNUM_DEVS_SERIAL_MCFxxxx_SERIAL1_BAUD),
+ CYG_SERIAL_STOP_DEFAULT,
+ CYG_SERIAL_PARITY_DEFAULT,
+ CYG_SERIAL_WORD_LENGTH_DEFAULT,
+ CYG_SERIAL_FLAGS_DEFAULT,
+ mcfxxxx_serial1_tx_buf, CYGNUM_DEVS_SERIAL_MCFxxxx_SERIAL1_BUFSIZE,
+ mcfxxxx_serial1_rx_buf, CYGNUM_DEVS_SERIAL_MCFxxxx_SERIAL1_BUFSIZE
+ );
+#else
+static SERIAL_CHANNEL(mcfxxxx_serial1_chan,
+ mcfxxxx_serial_funs,
+ mcfxxxx_serial1_info,
+ CYG_SERIAL_BAUD_RATE(CYGNUM_DEVS_SERIAL_MCFxxxx_SERIAL1_BAUD),
+ CYG_SERIAL_STOP_DEFAULT,
+ CYG_SERIAL_PARITY_DEFAULT,
+ CYG_SERIAL_WORD_LENGTH_DEFAULT,
+ CYG_SERIAL_FLAGS_DEFAULT
+ );
+# endif
+
+DEVTAB_ENTRY(mcfxxxx_serial1_devtab,
+ CYGDAT_DEVS_SERIAL_MCFxxxx_SERIAL1_NAME,
+ 0, // Does not depend on a lower level interface
+ &cyg_io_serial_devio,
+ mcfxxxx_serial_init,
+ mcfxxxx_serial_lookup, // Serial driver may need initializing
+ &mcfxxxx_serial1_chan
+ );
+#endif
+
+#ifdef CYGPKG_DEVS_SERIAL_MCFxxxx_SERIAL2
+static mcfxxxx_serial_info mcfxxxx_serial2_info = {
+ base: (cyg_uint8*)HAL_MCFxxxx_UART2_BASE,
+ isr_vec: CYGNUM_HAL_ISR_UART2,
+ isr_priority: CYGNUM_DEVS_SERIAL_MCFxxxx_SERIAL2_ISR_PRIORITY,
+#ifdef MCFxxxx_SERIAL_STATS
+ isr_count: 0,
+ dsr_count: 0,
+ rx_bytes: 0,
+ tx_bytes: 0,
+ rx_errors: 0,
+#endif
+ flags:
+#if defined(CYGHWR_HAL_M68K_MCFxxxx_UART2_RS485_RTS)
+ MCFxxxx_SERIAL_RS485_RTS |
+#elif defined(CYGHWR_HAL_M68K_MCFxxxx_UART2_RTS)
+ MCFxxxx_SERIAL_RTS |
+#endif
+#if defined(CYGHWR_HAL_M68K_MCFxxxx_UART2_CTS)
+ MCFxxxx_SERIAL_CTS |
+#endif
+ 0x00
+};
+
+# ifdef CYGNUM_DEVS_SERIAL_MCFxxxx_SERIAL2_BUFSIZE
+static unsigned char mcfxxxx_serial2_tx_buf[CYGNUM_DEVS_SERIAL_MCFxxxx_SERIAL2_BUFSIZE];
+static unsigned char mcfxxxx_serial2_rx_buf[CYGNUM_DEVS_SERIAL_MCFxxxx_SERIAL2_BUFSIZE];
+
+static SERIAL_CHANNEL_USING_INTERRUPTS(mcfxxxx_serial2_chan,
+ mcfxxxx_serial_funs,
+ mcfxxxx_serial2_info,
+ CYG_SERIAL_BAUD_RATE(CYGNUM_DEVS_SERIAL_MCFxxxx_SERIAL2_BAUD),
+ CYG_SERIAL_STOP_DEFAULT,
+ CYG_SERIAL_PARITY_DEFAULT,
+ CYG_SERIAL_WORD_LENGTH_DEFAULT,
+ CYG_SERIAL_FLAGS_DEFAULT,
+ mcfxxxx_serial2_tx_buf, CYGNUM_DEVS_SERIAL_MCFxxxx_SERIAL2_BUFSIZE,
+ mcfxxxx_serial2_rx_buf, CYGNUM_DEVS_SERIAL_MCFxxxx_SERIAL2_BUFSIZE
+ );
+#else
+static SERIAL_CHANNEL(mcfxxxx_serial2_chan,
+ mcfxxxx_serial_funs,
+ mcfxxxx_serial2_info,
+ CYG_SERIAL_BAUD_RATE(CYGNUM_DEVS_SERIAL_MCFxxxx_SERIAL2_BAUD),
+ CYG_SERIAL_STOP_DEFAULT,
+ CYG_SERIAL_PARITY_DEFAULT,
+ CYG_SERIAL_WORD_LENGTH_DEFAULT,
+ CYG_SERIAL_FLAGS_DEFAULT
+ );
+# endif
+
+DEVTAB_ENTRY(mcfxxxx_serial2_devtab,
+ CYGDAT_DEVS_SERIAL_MCFxxxx_SERIAL2_NAME,
+ 0, // Does not depend on a lower level interface
+ &cyg_io_serial_devio,
+ mcfxxxx_serial_init,
+ mcfxxxx_serial_lookup, // Serial driver may need initializing
+ &mcfxxxx_serial2_chan
+ );
+#endif
+
+// ----------------------------------------------------------------------------
+
+static cyg_uint32 mcfxxxx_baud_rates[] = {
+ 0, // Unused
+ 50, // 50
+ 75, // 75
+ 110, // 110
+ 134, // 134.5
+ 150, // 150
+ 200, // 200
+ 300, // 300
+ 600, // 600
+ 1200, // 1200
+ 1800, // 1800
+ 2400, // 2400
+ 3600, // 3600
+ 4800, // 4800
+ 7200, // 7200
+ 9600, // 9600
+ 14400, // 14400
+ 19200, // 19200
+ 38400, // 38400
+ 57600, // 57600
+ 115200, // 115200
+ 230400 // 230400
+};
+
+static bool
+mcfxxxx_serial_config(serial_channel* chan, cyg_serial_info_t* config, cyg_bool init)
+{
+ mcfxxxx_serial_info* info = (mcfxxxx_serial_info*) chan->dev_priv;
+ cyg_uint8* base = info->base;
+
+ if (init) {
+#if defined(CYGPKG_DEVS_SERIAL_MCFxxxx_SERIAL0) && defined(HAL_MCFxxxx_UART0_PROC_INIT)
+ if (info == &mcfxxxx_serial0_info) {
+ HAL_MCFxxxx_UART0_PROC_INIT();
+ }
+#endif
+#if defined(CYGPKG_DEVS_SERIAL_MCFxxxx_SERIAL1) && defined(HAL_MCFxxxx_UART1_PROC_INIT)
+ if (info == &mcfxxxx_serial1_info) {
+ HAL_MCFxxxx_UART1_PROC_INIT();
+ }
+#endif
+#if defined(CYGPKG_DEVS_SERIAL_MCFxxxx_SERIAL2) && defined(HAL_MCFxxxx_UART2_PROC_INIT)
+ if (info == &mcfxxxx_serial2_info) {
+ HAL_MCFxxxx_UART2_PROC_INIT();
+ }
+#endif
+ // Various resets to get the UART in a known good state
+ HAL_WRITE_UINT8(&(base[HAL_MCFxxxx_UARTx_UCR]), HAL_MCFxxxx_UARTx_UCR_MISC_RR);
+ HAL_WRITE_UINT8(&(base[HAL_MCFxxxx_UARTx_UCR]), HAL_MCFxxxx_UARTx_UCR_MISC_RT);
+ HAL_WRITE_UINT8(&(base[HAL_MCFxxxx_UARTx_UCR]), HAL_MCFxxxx_UARTx_UCR_MISC_RES);
+ HAL_WRITE_UINT8(&(base[HAL_MCFxxxx_UARTx_UCR]), HAL_MCFxxxx_UARTx_UCR_MISC_RBCI);
+
+ // Initialize the interrupt mask register. We want to trigger on rxrdy() and
+ // optionally on breaks. Tx interrupts are not enabled by default, only
+ // when a transmit is in progress.
+ //
+ // Some processors may define HAL_MCFxxxx_UARTx_UIMR_RXFTO which can be
+ // used instead of RXRDY, getting an interrupt only when the fifo is full
+ // or when 64 bit times have elapsed without new data. This reduces the
+ // number of rx interrupts by e.g. a factor of 12. It is not without
+ // penalty: if higher-level code could start processing data before the
+ // fifo has filled up then the latency is increased significantly; even
+ // if a whole packet needs to be received first, unless the packet size
+ // maps cleanly on to fifo boundaries the latency is increased by the
+ // timeout; if software flow control is in use then this side may not
+ // respond to XON/XOFF bytes for a while. For now rx fifos are used
+ // by default if available, although this should probably be made configurable.
+ info->uimr_shadow = 0;
+ if (chan->out_cbuf.len != 0) {
+# if defined(HAL_MCFxxxx_UARTx_UIMR_RXFIFO) && defined(HAL_MCFxxxx_UARTx_UIMR_RXFTO) && defined(HAL_MCFxxxx_UARTx_URF)
+ info->uimr_shadow = HAL_MCFxxxx_UARTx_UIMR_RXFTO | HAL_MCFxxxx_UARTx_UIMR_RXFIFO;
+# else
+ info->uimr_shadow = HAL_MCFxxxx_UARTx_UIMR_RXRDY;
+#endif
+ }
+#ifdef CYGOPT_IO_SERIAL_SUPPORT_LINE_STATUS
+ info->uimr_shadow |= HAL_MCFxxxx_UARTx_UIMR_DB;
+#endif
+ HAL_WRITE_UINT8(&(base[HAL_MCFxxxx_UARTx_UIMR]), info->uimr_shadow);
+
+ // If the hardware supports tx fifo control, set it up so that
+ // interrupts only occur when the fifo is more than 75% empty.
+ // That cuts down on the number of interrupts without
+ // affecting performance. The processor should service the interrupt
+ // and replenish the fifo before the remaining bytes go out.
+#ifdef HAL_MCFxxxx_UARTx_UTF
+ HAL_WRITE_UINT8(&(base[HAL_MCFxxxx_UARTx_UTF]), HAL_MCFxxxx_UARTx_UTF_TXS_75);
+#endif
+ // Ditto for rx fifo, but trigger on 50%. That is a compromise between
+ // latency and efficiency.
+#ifdef HAL_MCFxxxx_UARTx_URF
+ HAL_WRITE_UINT8(&(base[HAL_MCFxxxx_UARTx_URF]), HAL_MCFxxxx_UARTx_URF_RXS_50);
+#endif
+ // Always use the internal prescaled CLKIN.
+ HAL_WRITE_UINT8(&(base[HAL_MCFxxxx_UARTx_UCSR]), HAL_MCFxxxx_UARTx_UCSR_RCS_CLKIN | HAL_MCFxxxx_UARTx_UCSR_TCS_CLKIN);
+
+ // Hardware flow control.
+ //
+ // Default: no TXRTS, no TXCTS, no RXRTS, no configurable RTS fifo level
+ info->umr1_shadow = 0x00;
+ info->umr2_shadow = 0x00;
+ HAL_WRITE_UINT8(&(base[HAL_MCFxxxx_UARTx_UACR]), 0x00);
+
+ // CTS, used to throttle the transmitter automatically. This involves
+ // setting the TXCTS bit. However it is not the default, h/w flow control
+ // has to be explicitly enabled by a set_config() call.
+
+ // RTS. This may not be connected at all, or it may be used
+ // for h/w control of an RS485 transceiver, or it may be used
+ // for RS232 handshaking. If the latter then the uart provides
+ // automatic support for throttling the other side when the
+ // fifo starts filling up.
+ if (info->flags & MCFxxxx_SERIAL_RS485_RTS) {
+ info->umr2_shadow = HAL_MCFxxxx_UARTx_UMR2_TXRTS;
+ } else if (info->flags & MCFxxxx_SERIAL_RTS) {
+ // RS232 h/w flow control.
+ // See if the processor supports configurable RTS levels.
+# ifdef HAL_MCFxxxx_UARTx_UACR_RTSL_25
+ // Set up RTS to change when the fifo is 25% full. This means the
+ // processor can accept another 18 bytes, more than the 16-byte
+ // transmit fifo in a typical PC uart. Increasing the RTS level to
+ // any more than this may cause overruns.
+ HAL_WRITE_UINT8(&(base[HAL_MCFxxxx_UARTx_UACR]), HAL_MCFxxxx_UARTx_UACR_RTSL_25);
+# else
+ // Only RxRTS mode is supported, so use it.
+ info->umr1_shadow = HAL_MCFxxxx_UARTx_UMR1_RXRTS;
+# endif
+ // If RTS is connected assert it here, allowing the other side to transmit
+ // data. This may be too early since the h/w is not fully set up yet, but
+ // we only want to do this during init.
+ HAL_WRITE_UINT8(&(base[HAL_MCFxxxx_UARTx_UOP1]), HAL_MCFxxxx_UARTx_UOP_RTS);
+ } else {
+ // RTS is not connected at all.
+ }
+
+ // Enable both RX and TX
+ HAL_WRITE_UINT8(&(base[HAL_MCFxxxx_UARTx_UCR]), HAL_MCFxxxx_UARTx_UCR_TC_TE | HAL_MCFxxxx_UARTx_UCR_RC_RE);
+ }
+
+ info->umr1_shadow &= ~(HAL_MCFxxxx_UARTx_UMR1_BC_MASK | HAL_MCFxxxx_UARTx_UMR1_PM_MASK);
+ switch (config->word_length) {
+ case CYGNUM_SERIAL_WORD_LENGTH_5:
+ info->umr1_shadow |= HAL_MCFxxxx_UARTx_UMR1_BC_5;
+ break;
+ case CYGNUM_SERIAL_WORD_LENGTH_6:
+ info->umr1_shadow |= HAL_MCFxxxx_UARTx_UMR1_BC_6;
+ break;
+ case CYGNUM_SERIAL_WORD_LENGTH_7:
+ info->umr1_shadow |= HAL_MCFxxxx_UARTx_UMR1_BC_7;
+ break;
+ case CYGNUM_SERIAL_WORD_LENGTH_8:
+ default:
+ info->umr1_shadow |= HAL_MCFxxxx_UARTx_UMR1_BC_8;
+ break;
+ }
+ switch (config->parity) {
+ case CYGNUM_SERIAL_PARITY_EVEN:
+ info->umr1_shadow |= HAL_MCFxxxx_UARTx_UMR1_PM_WITH;
+ break;
+ case CYGNUM_SERIAL_PARITY_ODD:
+ info->umr1_shadow |= HAL_MCFxxxx_UARTx_UMR1_PM_WITH | HAL_MCFxxxx_UARTx_UMR1_PT;
+ break;
+ case CYGNUM_SERIAL_PARITY_MARK:
+ info->umr1_shadow |= HAL_MCFxxxx_UARTx_UMR1_PM_FORCE | HAL_MCFxxxx_UARTx_UMR1_PT;
+ break;
+ case CYGNUM_SERIAL_PARITY_SPACE:
+ info->umr1_shadow |= HAL_MCFxxxx_UARTx_UMR1_PM_FORCE;
+ break;
+ case CYGNUM_SERIAL_PARITY_NONE:
+ default:
+ info->umr1_shadow |= HAL_MCFxxxx_UARTx_UMR1_PM_NO;
+ break;
+ }
+ info->umr2_shadow &= ~HAL_MCFxxxx_UARTx_UMR2_SB_MASK;
+ switch (config->stop) {
+ case CYGNUM_SERIAL_STOP_2:
+ info->umr2_shadow |= HAL_MCFxxxx_UARTx_UMR2_SB_2;
+ break;
+ case CYGNUM_SERIAL_STOP_1_5:
+ info->umr2_shadow |= (CYGNUM_SERIAL_WORD_LENGTH_5 == config->word_length) ? 0x07 : 0x08;
+ break;
+ case CYGNUM_SERIAL_STOP_1:
+ default:
+ info->umr2_shadow |= (CYGNUM_SERIAL_WORD_LENGTH_5 == config->word_length) ? 0x00 : HAL_MCFxxxx_UARTx_UMR2_SB_1;
+ break;
+ }
+
+ HAL_WRITE_UINT8(&(base[HAL_MCFxxxx_UARTx_UCR]), HAL_MCFxxxx_UARTx_UCR_MISC_RMRP);
+ HAL_WRITE_UINT8(&(base[HAL_MCFxxxx_UARTx_UMR]), info->umr1_shadow);
+ HAL_WRITE_UINT8(&(base[HAL_MCFxxxx_UARTx_UMR]), info->umr2_shadow);
+
+ // Set the baud rate, using a processor or platform macro. That way the
+ // calculation can depend on the clock speed.
+ HAL_MCFxxxx_UARTx_SET_BAUD(base, mcfxxxx_baud_rates[config->baud]);
+
+ if (config != &chan->config) {
+ chan->config = *config;
+ }
+
+ return true;
+}
+
+// ----------------------------------------------------------------------------
+static bool
+mcfxxxx_serial_init(struct cyg_devtab_entry* devtab_entry)
+{
+ serial_channel* chan = (serial_channel*) devtab_entry->priv;
+ mcfxxxx_serial_info* info = (mcfxxxx_serial_info*) chan->dev_priv;
+
+ mcfxxxx_serial_config(chan, &(chan->config), true);
+
+ if (0 != chan->out_cbuf.len) {
+ cyg_drv_interrupt_create(info->isr_vec,
+ info->isr_priority,
+ (cyg_addrword_t) chan,
+ &mcfxxxx_serial_isr,
+ &mcfxxxx_serial_dsr,
+ &(info->serial_interrupt_handle),
+ &(info->serial_interrupt));
+ cyg_drv_interrupt_attach(info->serial_interrupt_handle);
+ cyg_drv_interrupt_unmask(info->isr_vec);
+ }
+ return true;
+}
+
+// ----------------------------------------------------------------------------
+static Cyg_ErrNo
+mcfxxxx_serial_lookup(struct cyg_devtab_entry** tab, struct cyg_devtab_entry* sub_tab, const char* name)
+{
+ serial_channel* chan = (serial_channel*) (*tab)->priv;
+ (chan->callbacks->serial_init)(chan);
+ return ENOERR;
+}
+
+// ----------------------------------------------------------------------------
+static Cyg_ErrNo
+mcfxxxx_serial_set_config(serial_channel* chan, cyg_uint32 key, const void* buf, cyg_uint32* len)
+{
+ Cyg_ErrNo result = ENOERR;
+
+ switch(key) {
+ case CYG_IO_SET_CONFIG_SERIAL_INFO:
+ {
+ mcfxxxx_serial_info* info = (mcfxxxx_serial_info*) chan->dev_priv;
+ cyg_serial_info_t* config = (cyg_serial_info_t*) buf;
+ if (*len < sizeof(cyg_serial_info_t)) {
+ return -EINVAL;
+ }
+ *len = sizeof(cyg_serial_info_t);
+ // DSR/DTR is never supported.
+ if (config->flags & (CYGNUM_SERIAL_FLOW_DSRDTR_RX | CYGNUM_SERIAL_FLOW_DSRDTR_TX)) {
+ result = -ENOSUPP;
+ config->flags &= ~(CYGNUM_SERIAL_FLOW_DSRDTR_RX | CYGNUM_SERIAL_FLOW_DSRDTR_TX);
+ }
+ // RTS/CTS may be supported, if the appropriate pins are connected.
+ if ((config->flags & CYGNUM_SERIAL_FLOW_RTSCTS_RX) && !(info->flags & MCFxxxx_SERIAL_RTS)) {
+ result = -ENOSUPP;
+ config->flags &= ~CYGNUM_SERIAL_FLOW_RTSCTS_RX;
+ }
+ if ((config->flags & CYGNUM_SERIAL_FLOW_RTSCTS_TX) && !(info->flags & MCFxxxx_SERIAL_CTS)) {
+ result = -ENOSUPP;
+ config->flags &= ~CYGNUM_SERIAL_FLOW_RTSCTS_TX;
+ }
+ if (ENOERR == result) {
+ if (! mcfxxxx_serial_config(chan, config, false)) {
+ result = -EINVAL;
+ }
+ }
+ break;
+ }
+#ifdef CYGOPT_IO_SERIAL_FLOW_CONTROL_HW
+ case CYG_IO_SET_CONFIG_SERIAL_HW_RX_FLOW_THROTTLE:
+ {
+ // RX flow control involves just the RTS line. Most of the
+ // work is done by the hardware depending on the state of
+ // the fifo. This option serves mainly to drop RTS if
+ // higher-level code is running out of buffer space, even
+ // if the fifo is not yet full.
+ mcfxxxx_serial_info* info = (mcfxxxx_serial_info*) chan->dev_priv;
+ cyg_uint32* flag = (cyg_uint32*) buf;
+ if (! (info->flags & MCFxxxx_SERIAL_RTS)) {
+ return -ENOSUPP;
+ }
+ if (*flag) {
+ HAL_WRITE_UINT8(info->base + HAL_MCFxxxx_UARTx_UOP0, HAL_MCFxxxx_UARTx_UOP_RTS);
+ } else {
+ HAL_WRITE_UINT8(info->base + HAL_MCFxxxx_UARTx_UOP1, HAL_MCFxxxx_UARTx_UOP_RTS);
+ }
+ }
+ break;
+
+ case CYG_IO_SET_CONFIG_SERIAL_HW_FLOW_CONFIG:
+ {
+ mcfxxxx_serial_info* info = (mcfxxxx_serial_info*) chan->dev_priv;
+
+ // DSR/DTR is never supported.
+ if (chan->config.flags & (CYGNUM_SERIAL_FLOW_DSRDTR_RX | CYGNUM_SERIAL_FLOW_DSRDTR_TX)) {
+ result = -ENOSUPP;
+ chan->config.flags &= ~(CYGNUM_SERIAL_FLOW_DSRDTR_RX | CYGNUM_SERIAL_FLOW_DSRDTR_TX);
+ }
+ // RTS/CTS may be supported, if the appropriate pins are connected.
+ if ((chan->config.flags & CYGNUM_SERIAL_FLOW_RTSCTS_RX) && !(info->flags & MCFxxxx_SERIAL_RTS)) {
+ result = -ENOSUPP;
+ chan->config.flags &= ~CYGNUM_SERIAL_FLOW_RTSCTS_RX;
+ }
+ if ((chan->config.flags & CYGNUM_SERIAL_FLOW_RTSCTS_TX) && !(info->flags & MCFxxxx_SERIAL_CTS)) {
+ result = -ENOSUPP;
+ chan->config.flags &= ~CYGNUM_SERIAL_FLOW_RTSCTS_TX;
+ }
+
+ // RTS flow control for RX. Either UMR1 RxRTS or a UACR RTS trigger
+ // level has been set during initialization. There is little point
+ // changing either of these. If h/w flow control is being disabled
+ // then the other side should start ignoring the RTS signal, even
+ // if this side still thinks it is a good idea to change it depending
+ // on the fifo level.
+
+ // CTS flow control for TX just involves the UMR2 TxCTS bit.
+ if (0 != (chan->config.flags & CYGNUM_SERIAL_FLOW_RTSCTS_TX)) {
+ info->umr2_shadow |= HAL_MCFxxxx_UARTx_UMR2_TXCTS;
+ } else {
+ info->umr2_shadow &= ~HAL_MCFxxxx_UARTx_UMR2_TXCTS;
+ }
+ HAL_WRITE_UINT8(info->base + HAL_MCFxxxx_UARTx_UCR, HAL_MCFxxxx_UARTx_UCR_MISC_RMRP);
+ HAL_WRITE_UINT8(info->base + HAL_MCFxxxx_UARTx_UMR, info->umr1_shadow);
+ HAL_WRITE_UINT8(info->base + HAL_MCFxxxx_UARTx_UMR, info->umr2_shadow);
+ }
+ break;
+#endif
+ default:
+ return -EINVAL;
+ }
+
+ return result;
+}
+
+// ----------------------------------------------------------------------------
+// Non-blocking send, returning true if the character was consumed. This can
+// be called in both interrupt and polled mode.
+
+static bool
+mcfxxxx_serial_putc(serial_channel* chan, unsigned char ch)
+{
+ mcfxxxx_serial_info* info = (mcfxxxx_serial_info*) chan->dev_priv;
+ cyg_uint8 usr;
+
+ HAL_READ_UINT8(info->base + HAL_MCFxxxx_UARTx_USR, usr);
+ if (usr & HAL_MCFxxxx_UARTx_USR_TXRDY) {
+ HAL_WRITE_UINT8(info->base + HAL_MCFxxxx_UARTx_UTB, ch);
+ INCR_STAT(info, tx_bytes, 1);
+ return true;
+ }
+ return false;
+}
+
+// Blocking receive, only called in polled mode.
+
+static unsigned char
+mcfxxxx_serial_getc(serial_channel* chan)
+{
+ mcfxxxx_serial_info* info = (mcfxxxx_serial_info*) chan->dev_priv;
+ cyg_uint8 usr, data;
+
+ do {
+ HAL_READ_UINT8(info->base + HAL_MCFxxxx_UARTx_USR, usr);
+ } while (! (usr & HAL_MCFxxxx_UARTx_USR_RXRDY));
+ HAL_READ_UINT8(info->base + HAL_MCFxxxx_UARTx_URB, data);
+ INCR_STAT(info, rx_bytes, 1);
+ return data;
+}
+
+// Start transmitting, only called in interrupt mode. This just requires
+// unmasking tx interrupts, with the interrupt handling code doing the
+// rest. The UIMR register is write-only so this has to go via a shadow
+// copy.
+//
+// If the processor supports interrupting on TXFIFO then that is used
+// instead, raising interrupts only if the fifo >= 75% empty.
+//
+// In RS485 mode it is necessary to enable RTS here so that the transceiver
+// is no longer tristated. RTS will be dropped automatically at the end of the
+// transmit. It is assumed that the fifo will be refilled quickly enough
+// that RTS does not get dropped too soon. Arguably RTS should be raised
+// in the fifo fill code, but that would introduce problems if another node
+// has decided a timeout has occurred and it should start transmitting now.
+
+static void
+mcfxxxx_serial_start_xmit(serial_channel* chan)
+{
+ mcfxxxx_serial_info* info = (mcfxxxx_serial_info*) chan->dev_priv;
+ CYG_INTERRUPT_STATE saved_state;
+
+ if (info->flags & MCFxxxx_SERIAL_RS485_RTS) {
+ HAL_WRITE_UINT8(info->base + HAL_MCFxxxx_UARTx_UOP1, HAL_MCFxxxx_UARTx_UOP_RTS);
+ }
+
+ HAL_DISABLE_INTERRUPTS(saved_state);
+#ifdef HAL_MCFxxxx_UARTx_UIMR_TXFIFO
+ info->uimr_shadow |= HAL_MCFxxxx_UARTx_UIMR_TXFIFO;
+#else
+ info->uimr_shadow |= HAL_MCFxxxx_UARTx_UIMR_TXRDY;
+#endif
+ HAL_WRITE_UINT8(info->base + HAL_MCFxxxx_UARTx_UIMR, info->uimr_shadow);
+ HAL_RESTORE_INTERRUPTS(saved_state);
+}
+
+// Stop transmitting, only called in interrupt mode.
+static void
+mcfxxxx_serial_stop_xmit(serial_channel* chan)
+{
+ mcfxxxx_serial_info* info = (mcfxxxx_serial_info*) chan->dev_priv;
+ CYG_INTERRUPT_STATE saved_state;
+
+ HAL_DISABLE_INTERRUPTS(saved_state);
+#ifdef HAL_MCFxxxx_UARTx_UIMR_TXFIFO
+ info->uimr_shadow &= ~HAL_MCFxxxx_UARTx_UIMR_TXFIFO;
+#else
+ info->uimr_shadow &= ~HAL_MCFxxxx_UARTx_UIMR_TXRDY;
+#endif
+ HAL_WRITE_UINT8(info->base + HAL_MCFxxxx_UARTx_UIMR, info->uimr_shadow);
+ HAL_RESTORE_INTERRUPTS(saved_state);
+}
+
+// ----------------------------------------------------------------------------
+// The main serial I/O callbacks expect to be called in DSR context, not
+// ISR context, so it is not possible to do much processing in the ISR.
+// Instead everything is deferred to the DSR.
+
+static cyg_uint32
+mcfxxxx_serial_isr(cyg_vector_t vec, cyg_addrword_t data)
+{
+ serial_channel* chan = (serial_channel*) data;
+ mcfxxxx_serial_info* info = (mcfxxxx_serial_info*) chan->dev_priv;
+ HAL_WRITE_UINT8(info->base + HAL_MCFxxxx_UARTx_UIMR, 0);
+
+ INCR_STAT(info, isr_count, 1);
+
+ return CYG_ISR_CALL_DSR;
+}
+
+// ----------------------------------------------------------------------------
+static void
+mcfxxxx_serial_dsr(cyg_vector_t vec, cyg_ucount32 count, cyg_addrword_t data)
+{
+ serial_channel* chan = (serial_channel*) data;
+ mcfxxxx_serial_info* info = (mcfxxxx_serial_info*) chan->dev_priv;
+ cyg_uint8 uisr;
+
+ INCR_STAT(info, dsr_count, 1);
+
+ HAL_READ_UINT8(info->base + HAL_MCFxxxx_UARTx_UISR, uisr);
+
+#ifdef CYGOPT_IO_SERIAL_SUPPORT_LINE_STATUS
+ // This is not quite right, it will report a break event instead of a delta-break,
+ // so higher-level code will see two breaks instead of start-break and end-break.
+ // In practice that should be good enough.
+ //
+ // There is also a received-break bit in the usr register, indicating that a
+ // break occurred in the middle of a character.
+ if (uisr & HAL_MCFxxxx_UARTx_UISR_DB) {
+ cyg_serial_line_status_t stat;
+ HAL_WRITE_UINT8(info->base + HAL_MCFxxxx_UARTx_UCR, HAL_MCFxxxx_UARTx_UCR_MISC_RBCI);
+ stat.value = 1;
+ stat.which = CYGNUM_SERIAL_STATUS_BREAK;
+ (chan->callbacks->indicate_status)(chan, &stat);
+ }
+#endif
+
+ // Do not report CTS changes to higher-level code. There is no point since flow
+ // control should be handled by the hardware.
+
+ if (uisr & HAL_MCFxxxx_UARTx_UISR_RXRDY) {
+ cyg_uint8 usr, data;
+ while (1) {
+ HAL_READ_UINT8(info->base + HAL_MCFxxxx_UARTx_USR, usr);
+
+ if (! (usr & HAL_MCFxxxx_UARTx_USR_RXRDY)) {
+#ifdef CYGOPT_IO_SERIAL_SUPPORT_LINE_STATUS
+ // Now check for an overrun, so that the error is
+ // reported in approximately the right place in the
+ // data stream. It is possible that an extra byte
+ // or so has come in after the overrun, but that
+ // cannot be detected.
+ if (usr & HAL_MCFxxxx_UARTx_USR_OE) {
+ cyg_serial_line_status_t stat;
+ HAL_WRITE_UINT8(info->base + HAL_MCFxxxx_UARTx_UCR, HAL_MCFxxxx_UARTx_UCR_MISC_RES);
+ stat.value = 1;
+ stat.which = CYGNUM_SERIAL_STATUS_OVERRUNERR;
+ (chan->callbacks->indicate_status)(chan, &stat);
+ INCR_STAT(info, rx_errors, 1);
+ }
+#endif
+ // There is no more data in the fifo, so look for transmits.
+ break;
+ }
+
+ // RXRDY is set, so we have either a valid or a corrupted byte
+ // in the current fifo position. First pass the byte up the stack,
+ // then report the error.
+ HAL_READ_UINT8(info->base + HAL_MCFxxxx_UARTx_URB, data);
+ (chan->callbacks->rcv_char)(chan, data);
+ INCR_STAT(info, rx_bytes, 1);
+#ifdef CYGOPT_IO_SERIAL_SUPPORT_LINE_STATUS
+ if (usr & HAL_MCFxxxx_UARTx_USR_FE) {
+ cyg_serial_line_status_t stat;
+ stat.value = 1;
+ stat.which = CYGNUM_SERIAL_STATUS_FRAMEERR;
+ (chan->callbacks->indicate_status)(chan, &stat);
+ INCR_STAT(info, rx_errors, 1);
+ }
+ if (usr & HAL_MCFxxxx_UARTx_USR_PE) {
+ cyg_serial_line_status_t stat;
+ stat.value = 1;
+ stat.which = CYGNUM_SERIAL_STATUS_PARITYERR;
+ (chan->callbacks->indicate_status)(chan, &stat);
+ INCR_STAT(info, rx_errors, 1);
+ }
+#endif
+ }
+ }
+
+ if (uisr & HAL_MCFxxxx_UARTx_UISR_TXRDY) {
+ (chan->callbacks->xmt_char)(chan);
+ }
+
+ // Re-enable UART interrupts
+ HAL_WRITE_UINT8(info->base + HAL_MCFxxxx_UARTx_UIMR, info->uimr_shadow);
+}