Mercurial > ecos
changeset 2647:fd157b9ab53a
Contribute eCosCentric's serial driver for the Freescale MCFxxxx
ColdFire family.
| author | bartv |
|---|---|
| date | Thu, 20 Nov 2008 22:03:59 +0000 |
| parents | 45fd5619b6e0 |
| children | d5ae069b8e8c |
| files | packages/devs/serial/m68k/mcf52xx/current/ChangeLog packages/devs/serial/m68k/mcf52xx/current/cdl/ser_mcf52xx.cdl packages/devs/serial/m68k/mcf52xx/current/doc/mcf52xx_ser.sgml packages/devs/serial/m68k/mcf52xx/current/src/ser_mcf52xx.c |
| diffstat | 4 files changed, 1363 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/packages/devs/serial/m68k/mcf52xx/current/ChangeLog @@ -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#### +//===========================================================================
new file mode 100644 --- /dev/null +++ b/packages/devs/serial/m68k/mcf52xx/current/cdl/ser_mcf52xx.cdl @@ -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\"" } + } + } +}
new file mode 100644 --- /dev/null +++ b/packages/devs/serial/m68k/mcf52xx/current/doc/mcf52xx_ser.sgml @@ -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>
new file mode 100644 --- /dev/null +++ b/packages/devs/serial/m68k/mcf52xx/current/src/ser_mcf52xx.c @@ -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); +}
