This is the mail archive of the ecos-patches@sourceware.org mailing list for the eCos project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

ColdFire serial driver


This patch contributes eCosCentric's serial device driver for the
Freescale MCFxxxx ColdFire family.

Bart

Index: ChangeLog
===================================================================
RCS file: ChangeLog
diff -N ChangeLog
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ChangeLog	20 Nov 2008 21:53:44 -0000
@@ -0,0 +1,123 @@
+2008-11-17  Bart Veer  <bartv@ecoscentric.com>
+
+	* cdl/ser_mcf52xx.cdl, doc/mcf52xx_ser.sgml, src/ser_mcf52xx.c:
+	minor clean-ups.
+
+2008-05-26  Bart Veer  <bartv@ecoscentric.com>
+
+	* src/ser_mcf52xx.c: provide more flow control capability
+	information to higher levels.
+
+2006-09-25  Bart Veer  <bartv@ecoscentric.com>
+
+	* doc/mcf52xx_ser.sgml: update docs to reflect changes in the
+	hal/m68k hierarchy.
+
+	* src/ser_mcf52xx.c: Motorola -> Freescale.
+
+2006-09-08  Bart Veer  <bartv@ecoscentric.com>
+
+	* src/ser_mcf52xx.c: update to use new var_io.h definitions.
+
+2006-09-05  Bart Veer  <bartv@ecoscentric.com>
+
+	* src/ser_mcf52xx.c, cdl/ser_mcf52xx.cdl:
+	Trigger off options/components in the processor HAL rather than
+	on interfaces in the device driver. The latter were essentially
+	duplicates of the former so no longer served a purpose.
+
+2006-07-10  Bart Veer  <bartv@ecoscentric.com>
+
+	* cdl/ser_mcf52xx.cdl, src/ser_mcf52xx.c: separate out RTS/CTS
+	handling. Cope with various changes in the mcf52xx variant HAL for
+	better support of the ColdFire range of processors.
+
+2006-03-10  John Dallaway  <jld@ecoscentric.com>
+
+        * cdl/ser_mcf52xx.cdl: Add reference to package documentation.
+
+2004-08-03  John Dallaway  <jld@ecoscentric.com>
+
+        * cdl/ser_mcf52xx.cdl: Use any port for serial testing.
+	
+2004-08-02  Bart Veer  <bartv@ecoscentric.com>
+
+	* cdl/ser_mcf52xx.cdl, src/ser_mcf52xx.c, doc/mcf52xx_ser.sgml:
+	Make it easier for platform HALs to control what functionality is
+	enabled, e.g. whether or not RTS/CTS are connected.
+
+2004-03-17  Bart Veer  <bartv@ecoscentric.com>
+
+	* doc/mcf52xx_ser.sgml: update following port to mcf5282
+
+2004-03-08  Bart Veer  <bartv@ecoscentric.com>
+
+	* cdl/ser_mcf52xx.cdl: look for default ISR priorities supplied by
+	the HAL.
+
+2004-02-11  Bart Veer  <bartv@ecoscentric.com>
+
+	* src/ser_mcf52xx.c, cdl/ser_mcf52xx.cdl: add support for an
+	optional third UART.
+
+2003-08-01  Bart Veer  <bartv@ecoscentric.com>
+
+	* doc/mcf52xx_ser.sgml: Document use of rx fifo
+
+	* src/ser_mcf52xx.c:
+	Use rx fifo where available. Add some statistics gathering.
+
+2003-07-22  Bart Veer  <bartv@ecoscentric.com>
+
+	* doc/mcf52xx_ser.sgml: fix various typos etc.
+
+2003-07-18  Bart Veer  <bartv@ecoscentric.com>
+
+	* doc/mcf52xx_ser.sgml: Add documentation.
+
+2003-07-10  Bart Veer  <bartv@ecoscentric.com>
+
+	* src/ser_mcf52xx.c (mcf52xx_serial_set_config):
+	Reject DSR/DTR flow control more gracefully.
+
+2003-07-08  Bart Veer  <bartv@ecoscentric.com>
+
+	* cdl/ser_mcf52xx.cdl, src/ser_mcf52xx.c:
+	Add support for hardware handshaking.
+	Make better use of the fifos to reduce interrupts.
+
+2003-06-04  Bart Veer  <bartv@ecoscentric.com>
+
+	* New version of the M68K support
+
+//===========================================================================
+//####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 2003, 2004, 2006 eCosCentric Limited
+//
+// 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.,
+// 59 Temple Place, Suite 330, Boston, MA 02111-1307 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.
+//
+// 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####
+//===========================================================================
Index: cdl/ser_mcf52xx.cdl
===================================================================
RCS file: cdl/ser_mcf52xx.cdl
diff -N cdl/ser_mcf52xx.cdl
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ cdl/ser_mcf52xx.cdl	20 Nov 2008 21:53:44 -0000
@@ -0,0 +1,184 @@
+# ====================================================================
+#
+#      ser_mcfxxxx.cdl
+#
+#      Serial driver for mcfxxxx 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.,
+# 59 Temple Place, Suite 330, Boston, MA 02111-1307 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.
+#
+# 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:
+# Date:           2003-06-4
+#
+#####DESCRIPTIONEND####
+# ====================================================================
+
+cdl_package CYGPKG_DEVS_SERIAL_MCFxxxx {
+    display       "Serial driver for the coldfire mcfxxxx family"
+    doc           ref/devs-ser-m68k-mcfxxxx-part.html
+
+    parent        CYGPKG_IO_SERIAL_DEVICES
+    active_if     CYGPKG_IO_SERIAL
+    active_if     CYGPKG_HAL_M68K_MCFxxxx
+
+    requires      CYGPKG_ERROR
+
+    description   "
+           This package provides a serial device driver for the on-chip
+           UART's in MCFxxxx ColdFire processors."
+    compile       -library=libextras.a   ser_mcf52xx.c
+    define_proc {
+        puts $::cdl_system_header "/***** serial driver proc output start *****/"
+        puts $::cdl_system_header "#define CYGDAT_IO_SERIAL_DEVICE_HEADER <pkgconf/devs_serial_mcfxxxx.h>"
+        puts $::cdl_system_header "/*****  serial driver proc output end  *****/"
+    }
+
+    # Support up to three on-chip UART's. The number varies between
+    # processor variants, and on some platforms some of the UART's
+    # may not be connected to save board space or to obtain more
+    # GPIO lines. Also h/w handshake lines may or may not be connected.
+    for { set ::uart 0 } { $::uart < 3 } { incr ::uart } {
+        
+        cdl_component CYGPKG_DEVS_SERIAL_MCFxxxx_SERIAL[set ::uart] {
+            display		    "Allow access to the on-chip uart[set ::uart] via a serial driver"
+            flavor		    bool
+            active_if		CYGHWR_HAL_M68K_MCFxxxx_UART[set ::uart]
+            default_value	1
+            implements		CYGINT_IO_SERIAL_LINE_STATUS_HW
+            
+            description "
+                If the application needs to access the on-chip uart[set ::uart]
+                via an eCos serial driver then this option should be enabled."
+
+            cdl_option CYGDAT_DEVS_SERIAL_MCFxxxx_SERIAL[set ::uart]_NAME {
+                display		"Device name for uart [set ::uart]"
+                flavor 		data
+                default_value	[format {"\"/dev/ser%d\""} $::uart]
+                description	"
+                    This option controls the name that an eCos application
+                    should use to access this device via cyg_io_lookup(),
+                    open(), or similar calls."
+            }
+
+            cdl_option CYGNUM_DEVS_SERIAL_MCFxxxx_SERIAL[set ::uart]_ISR_PRIORITY {
+                display		"Interrupt priority for this device"
+                flavor		data
+                default_value	is_loaded(CYGNUM_HAL_M68K_MCFxxxx_ISR_DEFAULT_PRIORITY_UART[set ::uart]) ? \
+                    CYGNUM_HAL_M68K_MCFxxxx_ISR_DEFAULT_PRIORITY_UART[set ::uart] : \
+                    CYGNUM_HAL_M68K_MCFxxxx_ISR_PRIORITY_MIN
+                legal_values	CYGNUM_HAL_M68K_MCFxxxx_ISR_PRIORITY_MIN to CYGNUM_HAL_M68K_MCFxxxx_ISR_PRIORITY_MAX
+                description "
+                    By default uart [set ::uart] is given an interrupt priority of 1,
+                    in other words it will interrupt at IPL level 1. The device can
+                    be made to interrupt at a higher priority but this is rarely
+                    useful since nearly all processing happens at DSR level rather
+                    than ISR level."
+            }
+            
+            cdl_option CYGNUM_DEVS_SERIAL_MCFxxxx_SERIAL[set ::uart]_BAUD {
+                display		"Default baud rate for uart [set ::uart]"
+                flavor		data
+                default_value	38400
+                legal_values    { 50 75 110 "134_5" 150 200 300 600 1200 1800 2400 3600
+                    4800 7200 9600 14400 19200 38400 57600 115200 230400
+                }
+                description "This option determines the initial baud rate for uart [set ::uart]"
+            }
+
+            cdl_option CYGNUM_DEVS_SERIAL_MCFxxxx_SERIAL[set ::uart]_BUFSIZE {
+                display		"Buffer size for the uart [set ::uart] serial driver"
+                flavor		booldata
+                default_value	128
+                legal_values	16 to 8192
+                description "
+                    Typically the device driver will run in interrupt mode and will
+                    perform some buffering of both incoming and outgoing data. This
+                    option controls the size of both input and output buffer. If
+                    the device will be used only in polled mode then this option
+                    can be disabled."
+            }
+        }
+    }
+
+    cdl_component CYGPKG_DEVS_SERIAL_MCFxxxx_OPTIONS {
+        display "Serial device driver build options"
+        flavor  none
+        description   "
+	    Package specific build options including control over
+	    compiler flags used only in building this package,
+	    and details of which tests are built."
+
+
+        cdl_option CYGPKG_DEVS_SERIAL_MCFxxxx_CFLAGS_ADD {
+            display "Additional compiler flags"
+            flavor  data
+            no_define
+            default_value { "" }
+            description   "
+                This option modifies the set of compiler flags for
+                building these serial device drivers. These flags are
+                used in addition to the set of global flags."
+        }
+
+        cdl_option CYGPKG_DEVS_SERIAL_MCFxxxx_CFLAGS_REMOVE {
+            display "Suppressed compiler flags"
+            flavor  data
+            no_define
+            default_value { "" }
+            description   "
+                This option modifies the set of compiler flags for
+                building these serial device drivers. These flags are
+                removed from the set of global flags if present."
+        }
+    }
+
+    cdl_component CYGPKG_DEVS_SERIAL_MCFxxxx_TESTING {
+        display    "Testing parameters"
+        flavor     none
+        define_proc {
+            puts $::cdl_header "#define CYGPRI_SER_TEST_CRASH_ID \"mcfxxxx\""
+        }
+
+        cdl_option CYGPRI_SER_TEST_SER_DEV {
+            display       "Serial device used for testing"
+            flavor        data
+            default_value { "\"/dev/ser0\"" }
+        }
+        cdl_option CYGPRI_SER_TEST_TTY_DEV {
+            display       "TTY device used for testing"
+            flavor        data
+            default_value { "\"/dev/tty0\"" }
+        }
+    }
+}
Index: doc/mcf52xx_ser.sgml
===================================================================
RCS file: doc/mcf52xx_ser.sgml
diff -N doc/mcf52xx_ser.sgml
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ doc/mcf52xx_ser.sgml	20 Nov 2008 21:53:45 -0000
@@ -0,0 +1,209 @@
+<!-- DOCTYPE refentry  PUBLIC "-//OASIS//DTD DocBook V3.1//EN" -->
+
+<!-- {{{ Banner                         -->
+
+<!-- =============================================================== -->
+<!--                                                                 -->
+<!--     mcfxxxx_ser.sgml                                            -->
+<!--                                                                 -->
+<!--     mcfxxxx serial driver documentation.                        -->
+<!--                                                                 -->
+<!-- =============================================================== -->
+<!-- ####COPYRIGHTBEGIN####                                          -->
+<!--                                                                 -->
+<!-- Copyright (C) 2003,2004,2006,2008                               -->
+<!--   Free Software Foundation, Inc.                                -->
+<!-- This material may be distributed only subject to the terms      -->
+<!-- and conditions set forth in the Open Publication License, v1.0  -->
+<!-- or later (the latest version is presently available at          -->
+<!-- http://www.opencontent.org/openpub/)                            -->
+<!-- Distribution of the work or derivative of the work in any       -->
+<!-- standard (paper) book form is prohibited unless prior           -->
+<!-- permission obtained from the copyright holder                   -->
+<!--                                                                 -->      
+<!-- ####COPYRIGHTEND####                                            -->
+<!-- =============================================================== -->
+<!-- #####DESCRIPTIONBEGIN####                                       -->
+<!--                                                                 -->
+<!-- Author(s):   bartv                                              -->
+<!-- Contact(s):  bartv                                              -->
+<!-- Date:        2003/07/15                                         -->
+<!-- Version:     0.01                                               -->
+<!--                                                                 -->
+<!-- ####DESCRIPTIONEND####                                          -->
+<!-- =============================================================== -->
+
+<!-- }}} -->
+
+<part id="devs-ser-m68k-mcfxxxx-part"><title>Freescale MCFxxxx Serial Driver</title>
+
+<refentry id="devs-ser-m68k-mcfxxxx">
+  <refmeta>
+    <refentrytitle>MCFxxxx Serial Driver</refentrytitle>
+  </refmeta>
+  <refnamediv>
+    <refname><varname>CYGPKG_DEVS_SERIAL_MCFxxxx</varname></refname>
+    <refpurpose>eCos Support for the MCFxxxx On-chip Serial Devices</refpurpose>
+  </refnamediv>
+
+  <refsect1 id="devs-ser-m68k-mcfxxxx-description"><title>Description</title>
+    <para>
+All members of the Freescale MCFxxxx ColdFire family of processors
+contain a number of on-chip UARTs for serial communication. They all
+use very similar hardware. There are some variations such as different
+fifo sizes, and some processors contain extra functionality such as
+autobaud detection, but a single eCos device driver can cope with most
+of these differences. The
+<varname>CYGPKG_DEVS_SERIAL_MCFxxxx</varname> package provides this
+driver. It will use definitions provided by the variant HAL
+<varname>CYGPKG_HAL_M68K_MCFxxxx</varname>, the processor HAL and the
+platform HAL.
+    </para>
+    <para>
+The driver provides partial support for hardware flow control and for
+serial line status. Only CTS/RTS hardware flow control is supported
+since the UART does not provide DTR/DSR lines. Similarly only line
+breaks, and certain communication errors are supported for line status
+since the UART does not provide other lines such as DCD or RI. On some
+platforms it should be possible to emulate these lines using GPIO
+pins, but currently there is no support for this.
+    </para>
+    <para>
+Once application code accesses a UART through the serial driver, for
+example by opening a device <literal>/dev/ser0</literal>, the driver
+assumes that it has sole access to the hardware. This means that the
+UART should not be used for any other purpose, for example HAL
+diagnostics or gdb debug traffic. Instead such traffic has to go via
+another communication channel such as ethernet.
+    </para>
+  </refsect1>
+
+  <refsect1 id="devs-ser-m68k-mcfxxxx-config"><title>Configuration Options</title>
+    <para>
+    The MCFxxxx serial driver should be loaded automatically when
+selecting a platform containing a suitable processor, and it should
+never be necessary to load it explicitly. The driver as a whole is
+inactive unless the generic serial support,
+<varname>CYGPKG_IO_SERIAL_DEVICES</varname>, is enabled. Exactly which
+UART or UARTs are accessible on a given platform is determined by the
+platform because even if the processor contains a UART the platform
+may not provide a connector. Support for a given UART, say uart0, is
+controlled by a configuration option
+<varname>CYGPKG_DEVS_SERIAL_MCFxxxx_SERIAL0</varname>. The device
+driver configuration option in turn depends on a HAL configuration
+option <varname>CYGHWR_HAL_M68K_MCFxxxx_UART0</varname> to indicate
+that the UART is actually present and connected on the target
+hardware. If a given UART is of no interest to an application
+developer then it is possible to save some memory by disabling this
+option.
+    </para>
+    <para>
+For every enabled UART there are a further four configuration options:
+    </para>
+    <variablelist>
+      <varlistentry>
+        <term><varname>CYGDAT_DEVS_SERIAL_MCFxxxx_SERIAL0_NAME</varname></term>
+        <listitem><para>
+Each serial device should have a unique name so that application code
+can open it. The default device names are <literal>/dev/ser0</literal>,
+<literal>/dev/ser1</literal>, and so on. It is only necessary to change
+these if the platform contains additional off-chip UARTs with clashing
+names.
+        </para></listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>CYGNUM_DEVS_SERIAL_MCFxxxx_SERIAL0_ISR_PRIORITY</varname></term>
+        <listitem><para>
+By default the driver arranges for the UARTs to interrupt at a low
+interrupt priority. Usually there will be no need to change this
+because the driver does not actually do very much processing at ISR
+level, and anyway UARTs are not especially fast devices so do not
+require immediate attention. On some Coldfires with MCF5282-compatible
+interrupt controllers care has to be taken that all interrupt
+priorities are unique.
+        </para></listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>CYGNUM_DEVS_SERIAL_MCFxxxx_SERIAL0_BAUD</varname></term>
+        <listitem><para>
+Each UART will be initialized to a given baud rate. The default baud
+rate is 38400 because in most scenarios this is fast enough yet
+does not suffer from excess data corruption. Lower baud rates can be
+used if the application will operate in an electrically noisy
+environment, or higher baud rates up to 230400 can be used if
+38400 does not provide sufficient throughput. 
+        </para></listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>CYGNUM_DEVS_SERIAL_MCFxxxx_SERIAL0_BUFSIZE</varname></term>
+        <listitem><para>
+The serial driver will maintain software buffers for incoming and
+outgoing data. The former allows data to continue to arrive even if
+the application is still busy processing the previous transfer, and
+thus potentially improves throughput. The latter allows the
+application to transmit data without immediately blocking until the
+transfer is complete, often eliminating the need for a separate
+thread. The size of these buffers can be controlled via this
+configuration option, or alternatively these buffers can be disabled
+completely to save memory.
+        </para></listitem>
+      </varlistentry>
+    </variablelist>
+    <para>
+There are additional options in the generic serial I/O package
+<varname>CYGPKG_IO_SERIAL</varname> which will affect this driver. For
+example <varname>CYGPKG_IO_SERIAL_FLOW_CONTROL</varname> and its
+sub-options determine what flow control mechanism (if any) should be
+used.
+    </para>
+    <para>
+This package also defines some configuration options related to
+testing. Usually these options are of no interest to application
+developers and can be ignored.
+    </para>
+  </refsect1>
+
+  <refsect1 id="devs-ser-m68k-mcfxxxx-porting"><title>Porting</title>
+    <para>
+The generic driver needs some information from other packages about
+the exact hardware, for example how many UARTs are available and where
+in memory they can be accessed.
+    </para>
+    <orderedlist>
+      <listitem><para>
+Another package, usually the processor HAL, should provide one or more
+options <varname>CYGHWR_HAL_M68K_MCFxxxx_UART0</varname>,
+<varname>CYGHWR_HAL_M68K_MCFxxxx_UART1</varname> or
+<varname>CYGHWR_HAL_M68K_MCFxxxx_UART2</varname>. These may be
+calculated or user-configurable depending on the processor.
+      </para></listitem>
+      <listitem><para>
+The device driver will also look for symbol definitions
+<varname>CYGHWR_HAL_M68K_MCFxxxx_UART0_RTS</varname> and
+<varname>CYGHWR_HAL_M68K_MCFxxxx_UART0_CTS</varname>, and the
+equivalents for the other UARTs, to determine whether or not these
+handshake lines are connected. These may be configuration options or
+they may be statically defined in a HAL I/O header file. The platform
+HAL should also implement the generic serial package's interface
+<varname>CYGINT_IO_SERIAL_FLOW_CONTROL_HW</varname> if appropriate.
+      </para></listitem>
+      <listitem><para>
+If RTS is connected then the driver will also look for a symbol
+<varname>CYGHWR_HAL_M68K_MCFxxxx_UART0_RS485_RTS</varname>. This
+enables partial support for RS485 communication in that the device
+driver will arrange for the RTS line to be asserted during a transmit.
+The driver has no support for more advanced RS485 functionality such
+as multidrop.
+      </para></listitem>
+    </orderedlist>
+    <para>
+In addition the driver assumes the standard MCFxxxx HAL macros are
+defined for the UART base addresses and the registers. The driver
+primarily targets MCF5282-compatible UARTs but there is also some
+support for functionality available on other members of the Coldfire
+range, for example the MCF5272's fractional baud rate support.
+    </para>
+  </refsect1>
+
+</refentry>
+</part>
Index: src/ser_mcf52xx.c
===================================================================
RCS file: src/ser_mcf52xx.c
diff -N src/ser_mcf52xx.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ src/ser_mcf52xx.c	20 Nov 2008 21:53:46 -0000
@@ -0,0 +1,847 @@
+//==========================================================================
+//
+//      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.,
+// 59 Temple Place, Suite 330, Boston, MA 02111-1307 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.
+//
+// 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);
+}


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]