This is the mail archive of the
ecos-patches@sourceware.org
mailing list for the eCos project.
ColdFire serial driver
- From: Bart Veer <bartv at ecoscentric dot com>
- To: ecos-patches at sourceware dot org
- Date: Thu, 20 Nov 2008 22:03:01 +0000
- Subject: 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);
+}