diff options
author | Michael Gielda <mgielda@antmicro.com> | 2014-04-03 14:53:04 +0200 |
---|---|---|
committer | Michael Gielda <mgielda@antmicro.com> | 2014-04-03 14:53:04 +0200 |
commit | ae1e4e08a1005a0c487f03ba189d7536e7fdcba6 (patch) | |
tree | f1c296f8a966a9a39876b0e98e16d9c5da1776dd /ecos/packages/devs/serial/powerpc/mpc555 | |
parent | f157da5337118d3c5cd464266796de4262ac9dbd (diff) |
Added the OS files
Diffstat (limited to 'ecos/packages/devs/serial/powerpc/mpc555')
4 files changed, 1701 insertions, 0 deletions
diff --git a/ecos/packages/devs/serial/powerpc/mpc555/current/ChangeLog b/ecos/packages/devs/serial/powerpc/mpc555/current/ChangeLog new file mode 100644 index 0000000..750cd75 --- /dev/null +++ b/ecos/packages/devs/serial/powerpc/mpc555/current/ChangeLog @@ -0,0 +1,59 @@ +2008-12-23 Steven Clugston <steven.clugston@ncl.ac.uk> + + * cdl/ser_powerpc_mpc555.cdl: Add HW queue option + * src/mpc555_serial_with_ints.c: + To help resolve an issue of characters being lost a software buffer + has been added between the Rx ISR and DSR when no hardware queue is + being used. + A cdl option to enable support the hardware queue on the first serial + port has been added. This enables 16 character hardware Tx and Rx + buffers to be used which allows continuous transmission and reception + of serial data and significantly improves performance. + +2008-05-13 Steven Clugston <steven.clugston@ncl.ac.uk> + + * cdl/ser_powerpc_mpc555.cdl: Add line status + * src/mpc555_serial_with_ints.c: + Fixed exception caused by attempt to clear scsr bits. + Add line status callbacks + +2008-04-06 Steven Clugston <steven.clugston@ncl.ac.uk> + + * Refactored package to more generic mpc555 + +2003-02-24 Jonathan Larmour <jifl@eCosCentric.com> + + * cdl/ser_powerpc_cme555.cdl: Remove irrelevant doc link. + +2002-11-11 Bob Koninckx <bob.koninckx@mech.kuleuven.ac.be> + + * src/cme555_serial_with_ints.c: + interrupt arbiter slightly modified to make GDB CTRL-C work + +2002-04-24 Bob Koninckx <bob.koninckx@mech.kuleuven.ac.be> + + * New package. + +//=========================================================================== +// ####GPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. +// +// This program 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. +// +// This program 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 this program; if not, write to the +// Free Software Foundation, Inc., 51 Franklin Street, +// Fifth Floor, Boston, MA 02110-1301, USA. +// ------------------------------------------- +// ####GPLCOPYRIGHTEND#### +//=========================================================================== diff --git a/ecos/packages/devs/serial/powerpc/mpc555/current/cdl/ser_powerpc_mpc555.cdl b/ecos/packages/devs/serial/powerpc/mpc555/current/cdl/ser_powerpc_mpc555.cdl new file mode 100644 index 0000000..f25a447 --- /dev/null +++ b/ecos/packages/devs/serial/powerpc/mpc555/current/cdl/ser_powerpc_mpc555.cdl @@ -0,0 +1,196 @@ +# ==================================================================== +# +# ser_powerpc_mpc555.cdl +# +# eCos serial PowerPC/mpc555 configuration data +# +# ==================================================================== +## ####ECOSGPLCOPYRIGHTBEGIN#### +## ------------------------------------------- +## This file is part of eCos, the Embedded Configurable Operating System. +## Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. +## +## eCos is free software; you can redistribute it and/or modify it under +## the terms of the GNU General Public License as published by the Free +## Software Foundation; either version 2 or (at your option) any later +## version. +## +## eCos is distributed in the hope that it will be useful, but WITHOUT +## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +## for more details. +## +## You should have received a copy of the GNU General Public License +## along with eCos; if not, write to the Free Software Foundation, Inc., +## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +## +## As a special exception, if other files instantiate templates or use +## macros or inline functions from this file, or you compile this file +## and link it with other works to produce a work based on this file, +## this file does not by itself cause the resulting work to be covered by +## the GNU General Public License. However the source code for this file +## must still be made available in accordance with section (3) of the GNU +## General Public License v2. +## +## This exception does not invalidate any other reasons why a work based +## on this file might be covered by the GNU General Public License. +## ------------------------------------------- +## ####ECOSGPLCOPYRIGHTEND#### +# ==================================================================== +######DESCRIPTIONBEGIN#### +# +# Author(s): Bob Koninckx +# Original data: +# Contributors: +# Date: 1999-07-14 +# +#####DESCRIPTIONEND#### +# +# ==================================================================== + +cdl_package CYGPKG_IO_SERIAL_POWERPC_MPC555 { + display "mpc555 PowerPC serial device drivers" + + parent CYGPKG_IO_SERIAL_DEVICES + active_if CYGPKG_IO_SERIAL + active_if CYGPKG_HAL_POWERPC_MPC555 + + requires CYGPKG_ERROR + include_dir cyg/io + include_files ; # none _exported_ whatsoever + description " + This option enables the serial device drivers for the + mpc555 mpc555 development board." + + compile -library=libextras.a mpc555_serial_with_ints.c + + define_proc { + puts $::cdl_system_header "/***** serial driver proc output start *****/" + puts $::cdl_system_header "#define CYGDAT_IO_SERIAL_DEVICE_HEADER <pkgconf/io_serial_powerpc_mpc555.h>" + puts $::cdl_system_header "/***** serial driver proc output end *****/" + } + +cdl_component CYGPKG_IO_SERIAL_POWERPC_MPC555_SERIAL_A { + display "mpc555 PowerPC serial port A driver" + flavor bool + default_value 0 + implements CYGINT_IO_SERIAL_LINE_STATUS_HW + implements CYGINT_IO_SERIAL_BLOCK_TRANSFER + description " + This option includes the serial device driver for the mpc555 + PowerPC port A." + + cdl_option CYGDAT_IO_SERIAL_POWERPC_MPC555_SERIAL_A_NAME { + display "Device name for mpc555 PowerPC serial port A" + flavor data + default_value {"\"/dev/ser1\""} + description " + This option specifies the device name for the mpc555 PowerPC + port A." + } + + cdl_option CYGNUM_IO_SERIAL_POWERPC_MPC555_SERIAL_A_BAUD { + display "Baud rate for the mpc555 PowerPC serial port A driver" + flavor data + legal_values { 300 600 1200 2400 4800 9600 14400 19200 38400 57600 115200 } + default_value 38400 + description " + This option specifies the default baud rate (speed) for the + mpc555 PowerPC port A." + } + + cdl_option CYGNUM_IO_SERIAL_POWERPC_MPC555_SERIAL_A_BUFSIZE { + display "Buffer size for the mpc555 PowerPC serial port A driver" + flavor data + legal_values 0 to 8192 + default_value 128 + description " + This option specifies the size of the internal buffers used for + the mpc555 PowerPC port A." + } + cdl_option CYGDAT_IO_SERIAL_POWERPC_MPC555_SERIAL_A_USE_HWARE_QUEUE { + display "Use hardware queue for mpc555 PowerPC serial port A" + flavor bool + default_value 1 + description " + This option specifies if the QSCI 16-byte hardware queue + is used for the mpc555 PowerPC port A. Using the queue + makes block transfers possible. Select this option + if you need to support continuous transmission and reception + without buffer overruns occurring." + } +} + +cdl_component CYGPKG_IO_SERIAL_POWERPC_MPC555_SERIAL_B { + display "mpc555 PowerPC serial port B driver" + flavor bool + default_value 1 + implements CYGINT_IO_SERIAL_LINE_STATUS_HW + description " + This option includes the serial device driver for the mpc555 + PowerPC port B." + + cdl_option CYGDAT_IO_SERIAL_POWERPC_MPC555_SERIAL_B_NAME { + display "Device name for mpc555 PowerPC serial port B" + flavor data + default_value {"\"/dev/ser2\""} + description " + This option specifies the device name for the mpc555 PowerPC + port B." + } + + cdl_option CYGNUM_IO_SERIAL_POWERPC_MPC555_SERIAL_B_BAUD { + display "Baud rate for the mpc555 PowerPC serial port B driver" + flavor data + legal_values { 300 600 1200 2400 4800 9600 14400 19200 38400 57600 115200 } + default_value 38400 + description " + This option specifies the default baud rate (speed) for the + mpc555 PowerPC port B." + } + + cdl_option CYGNUM_IO_SERIAL_POWERPC_MPC555_SERIAL_B_BUFSIZE { + display "Buffer size for the mpc555 PowerPC serial port B driver" + flavor data + legal_values 0 to 8192 + default_value 128 + description " + This option specifies the size of the internal buffers used + for the mpc555 PowerPC port B." + } +} + + cdl_component CYGPKG_IO_SERIAL_POWERPC_MPC555_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_IO_SERIAL_POWERPC_MPC555_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_IO_SERIAL_POWERPC_MPC555_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." + } + } +} + +# EOF ser_powerpc_mpc555.cdl diff --git a/ecos/packages/devs/serial/powerpc/mpc555/current/src/mpc555_serial.h b/ecos/packages/devs/serial/powerpc/mpc555/current/src/mpc555_serial.h new file mode 100644 index 0000000..8c0044b --- /dev/null +++ b/ecos/packages/devs/serial/powerpc/mpc555/current/src/mpc555_serial.h @@ -0,0 +1,156 @@ +#ifndef CYGONCE_DEVS_SERIAL_POWERPC_MPC555_SERIAL_H +#define CYGONCE_DEVS_SERIAL_POWERPC_MPC555_SERIAL_H +//========================================================================== +// +// mpc555_serial.h +// +// PowerPC 5xx MPC555 Serial I/O definitions. +// +//========================================================================== +// ####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later +// version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License +// along with eCos; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// As a special exception, if other files instantiate templates or use +// macros or inline functions from this file, or you compile this file +// and link it with other works to produce a work based on this file, +// this file does not by itself cause the resulting work to be covered by +// the GNU General Public License. However the source code for this file +// must still be made available in accordance with section (3) of the GNU +// General Public License v2. +// +// This exception does not invalidate any other reasons why a work based +// on this file might be covered by the GNU General Public License. +// ------------------------------------------- +// ####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): Bob Koninckx +// Contributors: +// Date: 2002-04-25 +// Purpose: MPC555 Serial I/O definitions. +// Description: +// +// +//####DESCRIPTIONEND#### +//========================================================================== + +//---------------------------------- +// Includes and forward declarations +//---------------------------------- + +//---------------------- +// Constants definitions +//---------------------- +// Base addresses for the two serial ports +#define MPC555_SERIAL_BASE_A 0x305008 +#define MPC555_SERIAL_BASE_B 0x305020 + +// The offset from the base for all serial registers +#define MPC555_SERIAL_SCCxR0 0 +#define MPC555_SERIAL_SCCxR1 2 +#define MPC555_SERIAL_SCxSR 4 +#define MPC555_SERIAL_SCxDR 6 + +// The bits in the serial registers +#define MPC555_SERIAL_SCCxR0_OTHR 0x8000 +#define MPC555_SERIAL_SCCxR0_LINKBD 0x4000 +#define MPC555_SERIAL_SCCxR0_SCxBR 0x1fff + +#define MPC555_SERIAL_SCCxR1_LOOPS 0x4000 +#define MPC555_SERIAL_SCCxR1_WOMS 0x2000 +#define MPC555_SERIAL_SCCxR1_ILT 0x1000 +#define MPC555_SERIAL_SCCxR1_PT 0x0800 +#define MPC555_SERIAL_SCCxR1_PE 0x0400 +#define MPC555_SERIAL_SCCxR1_M 0x0200 +#define MPC555_SERIAL_SCCxR1_WAKE 0x0100 +#define MPC555_SERIAL_SCCxR1_TIE 0x0080 +#define MPC555_SERIAL_SCCxR1_TCIE 0x0040 +#define MPC555_SERIAL_SCCxR1_RIE 0x0020 +#define MPC555_SERIAL_SCCxR1_ILIE 0x0010 +#define MPC555_SERIAL_SCCxR1_TE 0x0008 +#define MPC555_SERIAL_SCCxR1_RE 0x0004 +#define MPC555_SERIAL_SCCxR1_RWU 0x0002 +#define MPC555_SERIAL_SCCxR1_SBK 0x0001 + +#define MPC555_SERIAL_SCxSR_TDRE 0x0100 +#define MPC555_SERIAL_SCxSR_TC 0x0080 +#define MPC555_SERIAL_SCxSR_RDRF 0x0040 +#define MPC555_SERIAL_SCxSR_RAF 0x0020 +#define MPC555_SERIAL_SCxSR_IDLE 0x0010 +#define MPC555_SERIAL_SCxSR_OR 0x0008 +#define MPC555_SERIAL_SCxSR_NF 0x0004 +#define MPC555_SERIAL_SCxSR_FE 0x0002 +#define MPC555_SERIAL_SCxSR_PF 0x0001 + +// The available baud rates +// These are calculated for a busclock of 40 MHz +// It is not necessary to let the compiler calculate these +// values, we did not provide clockfrequency as a configuarion +// option anyway. +static unsigned short select_baud[] = { + 0, // Unused + 0, // 50 bps unsupported + 0, // 75 bps unsupported + 0, // 110 bps unsupported + 0, // 134_5 bps unsupported + 0, // 150 bps unsupported + 0, // 200 bps unsupported + 4167, // 300 bps + 2083, // 600 bps + 1042, // 1200 bps + 0, // 1800 bps unsupported + 521, // 2400 bps + 0, // 3600 bps unsupported + 260, // 4800 bps + 0, // 7200 bps unsupported + 130, // 9600 bps + 87, // 14400 bps + 65, // 19200 bps + 33, // 38400 bps + 22, // 57600 bps + 11, // 115200 bps + 0 // 230400 bps unsupported +}; + +static unsigned char select_word_length[] = { + 0, // 5 bits / word (char) not supported + 0, // 6 bits / word (char) not supported + 7, // 7 bits / word (char) ->> 7 bits per frame + 8 // 8 bits / word (char) ->> 8 bits per frame +}; + +static unsigned char select_stop_bits[] = { + 0, + 1, // 1 stop bit ->> 1 bit per frame + 0, // 1.5 stop bit not supported + 2 // 2 stop bits ->> 2 bits per frame +}; + +static unsigned char select_parity[] = { + 0, // No parity ->> 0 bits per frame + 1, // Even parity ->> 1 bit per frame + 1, // Odd parityv ->> 1 bit per frame + 0, // Mark parity not supported + 0, // Space parity not supported +}; + +#endif // CYGONCE_DEVS_SERIAL_POWERPC_MPC555_SERIAL_H + +// EOF mpc555_serial.h diff --git a/ecos/packages/devs/serial/powerpc/mpc555/current/src/mpc555_serial_with_ints.c b/ecos/packages/devs/serial/powerpc/mpc555/current/src/mpc555_serial_with_ints.c new file mode 100644 index 0000000..6571356 --- /dev/null +++ b/ecos/packages/devs/serial/powerpc/mpc555/current/src/mpc555_serial_with_ints.c @@ -0,0 +1,1290 @@ +//========================================================================== +// +// mpc555_serial_with_ints.c +// +// PowerPC 5xx MPC555 Serial I/O Interface Module (interrupt driven) +// +//========================================================================== +// ####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later +// version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License +// along with eCos; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// As a special exception, if other files instantiate templates or use +// macros or inline functions from this file, or you compile this file +// and link it with other works to produce a work based on this file, +// this file does not by itself cause the resulting work to be covered by +// the GNU General Public License. However the source code for this file +// must still be made available in accordance with section (3) of the GNU +// General Public License v2. +// +// This exception does not invalidate any other reasons why a work based +// on this file might be covered by the GNU General Public License. +// ------------------------------------------- +// ####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): Bob Koninckx +// Contributors: +// Date: 2002-04-25 +// Purpose: MPC555 Serial I/O module (interrupt driven version) +// Description: +// +// +//####DESCRIPTIONEND#### +//========================================================================== +//---------------------------------- +// Includes and forward declarations +//---------------------------------- +#include <pkgconf/io_serial.h> +#include <pkgconf/io.h> + +#include <cyg/io/io.h> +#include <cyg/hal/hal_intr.h> +#include <cyg/hal/hal_arbiter.h> +#include <cyg/io/devtab.h> +#include <cyg/infra/diag.h> +#include <cyg/io/serial.h> + +// Only build this driver for the MPC555 based boards +#if defined (CYGPKG_IO_SERIAL_POWERPC_MPC555) && \ + (defined (CYGPKG_IO_SERIAL_POWERPC_MPC555_SERIAL_A) || \ + defined (CYGPKG_IO_SERIAL_POWERPC_MPC555_SERIAL_B)) + +#include "mpc555_serial.h" + +//--------------------------------------------------------------------------- +// Type definitions +//--------------------------------------------------------------------------- +#define MPC555_SCI_RX_BUFF_SIZE 256 +typedef struct st_sci_circbuf { + cyg_uint8 buf[MPC555_SCI_RX_BUFF_SIZE]; + cyg_uint16 scsr[MPC555_SCI_RX_BUFF_SIZE]; + cyg_uint8 fill_pos; + cyg_uint8 read_pos; +} mpc555_sci_circbuf_t; + +typedef struct mpc555_serial_info { + CYG_ADDRWORD base; // The base address of the serial port + CYG_WORD tx_interrupt_num; // trivial + CYG_WORD rx_interrupt_num; // trivial + cyg_priority_t tx_interrupt_priority;// trivial + cyg_priority_t rx_interrupt_priority;// trivial + bool tx_interrupt_enable; // can the tx interrupt be re-enabled? + mpc555_sci_circbuf_t* rx_circbuf; // rx buff for ISR to DSR data exchange + bool use_queue; // Use the queue when available? + CYG_WORD rx_last_queue_pointer;// Keep track where queue read is upto + CYG_WORD rx_interrupt_idle_line_num; // trivial + CYG_WORD tx_interrupt_queue_top_empty_num; // trivial + CYG_WORD tx_interrupt_queue_bot_empty_num; // trivial + CYG_WORD rx_interrupt_queue_top_full_num; // trivial + CYG_WORD rx_interrupt_queue_bot_full_num; // trivial + cyg_priority_t rx_interrupt_idle_line_priority; // trivial + cyg_priority_t tx_interrupt_queue_top_empty_priority; // trivial + cyg_priority_t tx_interrupt_queue_bot_empty_priority; // trivial + cyg_priority_t rx_interrupt_queue_top_full_priority; // trivial + cyg_priority_t rx_interrupt_queue_bot_full_priority; // trivial + cyg_interrupt tx_interrupt; // the tx interrupt object + cyg_handle_t tx_interrupt_handle; // the tx interrupt handle + cyg_interrupt rx_interrupt; // the rx interrupt object + cyg_handle_t rx_interrupt_handle; // the rx interrupt handle + cyg_interrupt rx_idle_interrupt; // the rx idle line isr object + cyg_handle_t rx_idle_interrupt_handle; // the rx idle line isr handle + cyg_interrupt tx_queue_top_interrupt; // the tx interrupt object + cyg_handle_t tx_queue_top_interrupt_handle;// the tx interrupt handle + cyg_interrupt tx_queue_bot_interrupt; // the tx interrupt object + cyg_handle_t tx_queue_bot_interrupt_handle;// the tx interrupt handle + cyg_interrupt rx_queue_top_interrupt; // the tx interrupt object + cyg_handle_t rx_queue_top_interrupt_handle;// the tx interrupt handle + cyg_interrupt rx_queue_bot_interrupt; // the tx interrupt object + cyg_handle_t rx_queue_bot_interrupt_handle;// the tx interrupt handle +} mpc555_serial_info; + +//-------------------- +// Function prototypes +//-------------------- +static bool mpc555_serial_putc(serial_channel * chan, unsigned char c); +static unsigned char mpc555_serial_getc(serial_channel *chan); +static Cyg_ErrNo mpc555_serial_set_config(serial_channel *chan, cyg_uint32 key, + const void *xbuf, cyg_uint32 *len); +static void mpc555_serial_start_xmit(serial_channel *chan); +static void mpc555_serial_stop_xmit(serial_channel *chan); +static Cyg_ErrNo mpc555_serial_lookup(struct cyg_devtab_entry ** tab, + struct cyg_devtab_entry * sub_tab, + const char * name); +static bool mpc555_serial_init(struct cyg_devtab_entry * tab); + +// The interrupt servers +static cyg_uint32 mpc555_serial_tx_ISR(cyg_vector_t vector, cyg_addrword_t data); +static cyg_uint32 mpc555_serial_rx_ISR(cyg_vector_t vector, cyg_addrword_t data); +static void mpc555_serial_tx_DSR(cyg_vector_t vector, + cyg_ucount32 count, + cyg_addrword_t data); +static void mpc555_serial_rx_DSR(cyg_vector_t vector, + cyg_ucount32 count, + cyg_addrword_t data); + +#ifdef CYGPKG_IO_SERIAL_POWERPC_MPC555_SERIAL_A +static cyg_uint32 mpc555_serial_tx_queue_top_ISR(cyg_vector_t vector, + cyg_addrword_t data); +static cyg_uint32 mpc555_serial_tx_queue_bot_ISR(cyg_vector_t vector, + cyg_addrword_t data); +static cyg_uint32 mpc555_serial_rx_queue_top_ISR(cyg_vector_t vector, + cyg_addrword_t data); +static cyg_uint32 mpc555_serial_rx_queue_bot_ISR(cyg_vector_t vector, + cyg_addrword_t data); +static cyg_uint32 mpc555_serial_rx_idle_line_ISR(cyg_vector_t vector, + cyg_addrword_t data); + +static void mpc555_serial_tx_queue_DSR(cyg_vector_t vector, + cyg_ucount32 count, + cyg_addrword_t data); +static void mpc555_serial_rx_queue_DSR(cyg_vector_t vector, + cyg_ucount32 count, + cyg_addrword_t data); + +static int mpc555_serial_read_queue(serial_channel* chan, int start, int end); +#endif + +//------------------------------------------------------------------------------ +// Register the device driver with the kernel +//------------------------------------------------------------------------------ +static SERIAL_FUNS(mpc555_serial_funs, + mpc555_serial_putc, + mpc555_serial_getc, + mpc555_serial_set_config, + mpc555_serial_start_xmit, + mpc555_serial_stop_xmit); + +//------------------------------------------------------------------------------ +// Device driver data +//------------------------------------------------------------------------------ +#ifdef CYGPKG_IO_SERIAL_POWERPC_MPC555_SERIAL_A +#ifdef CYGDAT_IO_SERIAL_POWERPC_MPC555_SERIAL_A_USE_HWARE_QUEUE +//static mpc555_sci_circbuf_t mpc555_serial_isr_to_dsr_buf0; + +static mpc555_serial_info mpc555_serial_info0 = { + MPC555_SERIAL_BASE_A, + CYGNUM_HAL_INTERRUPT_IMB3_SCI1_TX, + CYGNUM_HAL_INTERRUPT_IMB3_SCI1_RX, + CYGNUM_HAL_INTERRUPT_IMB3_SCI1_TX_PRIORITY, + CYGNUM_HAL_INTERRUPT_IMB3_SCI1_RX_PRIORITY, + false, + NULL, // Don't need software buffer + true, // Use queue + 0, // init queue pointer + CYGNUM_HAL_INTERRUPT_IMB3_SCI1_IDLE, + CYGNUM_HAL_INTERRUPT_IMB3_SCI1_TXQTHE, + CYGNUM_HAL_INTERRUPT_IMB3_SCI1_TXQBHE, + CYGNUM_HAL_INTERRUPT_IMB3_SCI1_RXQTHF, + CYGNUM_HAL_INTERRUPT_IMB3_SCI1_RXQBHF, + CYGNUM_HAL_INTERRUPT_IMB3_SCI1_IDLE_PRIORITY, + CYGNUM_HAL_INTERRUPT_IMB3_SCI1_TXQTHE_PRIORITY, + CYGNUM_HAL_INTERRUPT_IMB3_SCI1_TXQBHE_PRIORITY, + CYGNUM_HAL_INTERRUPT_IMB3_SCI1_RXQTHF_PRIORITY, + CYGNUM_HAL_INTERRUPT_IMB3_SCI1_RXQBHF_PRIORITY}; + +static unsigned char mpc555_serial_out_buf0[CYGNUM_IO_SERIAL_POWERPC_MPC555_SERIAL_A_BUFSIZE]; +static unsigned char mpc555_serial_in_buf0[CYGNUM_IO_SERIAL_POWERPC_MPC555_SERIAL_A_BUFSIZE]; + +static SERIAL_CHANNEL_USING_INTERRUPTS( + mpc555_serial_channel0, + mpc555_serial_funs, + mpc555_serial_info0, + CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_POWERPC_MPC555_SERIAL_A_BAUD), + CYG_SERIAL_STOP_DEFAULT, + CYG_SERIAL_PARITY_DEFAULT, + CYG_SERIAL_WORD_LENGTH_DEFAULT, + CYG_SERIAL_FLAGS_DEFAULT, + &mpc555_serial_out_buf0[0], + sizeof(mpc555_serial_out_buf0), + &mpc555_serial_in_buf0[0], + sizeof(mpc555_serial_in_buf0)); + +#elif CYGNUM_IO_SERIAL_POWERPC_MPC555_SERIAL_A_BUFSIZE > 0 +static mpc555_sci_circbuf_t mpc555_serial_isr_to_dsr_buf0; + +static mpc555_serial_info mpc555_serial_info0 = { + MPC555_SERIAL_BASE_A, + CYGNUM_HAL_INTERRUPT_IMB3_SCI1_TX, + CYGNUM_HAL_INTERRUPT_IMB3_SCI1_RX, + CYGNUM_HAL_INTERRUPT_IMB3_SCI1_TX_PRIORITY, + CYGNUM_HAL_INTERRUPT_IMB3_SCI1_RX_PRIORITY, + false, + &mpc555_serial_isr_to_dsr_buf0, + false}; + +static unsigned char mpc555_serial_out_buf0[CYGNUM_IO_SERIAL_POWERPC_MPC555_SERIAL_A_BUFSIZE]; +static unsigned char mpc555_serial_in_buf0[CYGNUM_IO_SERIAL_POWERPC_MPC555_SERIAL_A_BUFSIZE]; + +static SERIAL_CHANNEL_USING_INTERRUPTS( + mpc555_serial_channel0, + mpc555_serial_funs, + mpc555_serial_info0, + CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_POWERPC_MPC555_SERIAL_A_BAUD), + CYG_SERIAL_STOP_DEFAULT, + CYG_SERIAL_PARITY_DEFAULT, + CYG_SERIAL_WORD_LENGTH_DEFAULT, + CYG_SERIAL_FLAGS_DEFAULT, + &mpc555_serial_out_buf0[0], + sizeof(mpc555_serial_out_buf0), + &mpc555_serial_in_buf0[0], + sizeof(mpc555_serial_in_buf0)); +#else +static mpc555_serial_info mpc555_serial_info0 = { + MPC555_SERIAL_BASE_A, + CYGNUM_HAL_INTERRUPT_IMB3_SCI1_TX, + CYGNUM_HAL_INTERRUPT_IMB3_SCI1_RX, + CYGNUM_HAL_INTERRUPT_IMB3_SCI1_TX_PRIORITY, + CYGNUM_HAL_INTERRUPT_IMB3_SCI1_RX_PRIORITY, + false, + NULL, + false}; + +static SERIAL_CHANNEL( + mpc555_serial_channel0, + mpc555_serial_funs, + mpc555_serial_info0, + CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_POWERPC_MPC555_SERIAL_A_BAUD), + CYG_SERIAL_STOP_DEFAULT, + CYG_SERIAL_PARITY_DEFAULT, + CYG_SERIAL_WORD_LENGTH_DEFAULT, + CYG_SERIAL_FLAGS_DEFAULT); +#endif +DEVTAB_ENTRY(mpc555_serial_io0, + CYGDAT_IO_SERIAL_POWERPC_MPC555_SERIAL_A_NAME, + 0, // does not depend on a lower level device driver + &cyg_io_serial_devio, + mpc555_serial_init, + mpc555_serial_lookup, + &mpc555_serial_channel0); +#endif // ifdef CYGPKG_IO_SERIAL_POWERPC_MPC555_SERIAL_A + +#ifdef CYGPKG_IO_SERIAL_POWERPC_MPC555_SERIAL_B + +#if CYGNUM_IO_SERIAL_POWERPC_MPC555_SERIAL_B_BUFSIZE > 0 +static mpc555_sci_circbuf_t mpc555_serial_isr_to_dsr_buf1; + +static mpc555_serial_info mpc555_serial_info1 = { + MPC555_SERIAL_BASE_B, + CYGNUM_HAL_INTERRUPT_IMB3_SCI2_TX, + CYGNUM_HAL_INTERRUPT_IMB3_SCI2_RX, + CYGNUM_HAL_INTERRUPT_IMB3_SCI2_TX_PRIORITY, + CYGNUM_HAL_INTERRUPT_IMB3_SCI2_RX_PRIORITY, + false, + &mpc555_serial_isr_to_dsr_buf1, + false}; + +static unsigned char mpc555_serial_out_buf1[CYGNUM_IO_SERIAL_POWERPC_MPC555_SERIAL_B_BUFSIZE]; +static unsigned char mpc555_serial_in_buf1[CYGNUM_IO_SERIAL_POWERPC_MPC555_SERIAL_B_BUFSIZE]; + +static SERIAL_CHANNEL_USING_INTERRUPTS( + mpc555_serial_channel1, + mpc555_serial_funs, + mpc555_serial_info1, + CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_POWERPC_MPC555_SERIAL_B_BAUD), + CYG_SERIAL_STOP_DEFAULT, + CYG_SERIAL_PARITY_DEFAULT, + CYG_SERIAL_WORD_LENGTH_DEFAULT, + CYG_SERIAL_FLAGS_DEFAULT, + &mpc555_serial_out_buf1[0], + sizeof(mpc555_serial_out_buf1), + &mpc555_serial_in_buf1[0], + sizeof(mpc555_serial_in_buf1)); +#else +static mpc555_serial_info mpc555_serial_info1 = { + MPC555_SERIAL_BASE_B, + CYGNUM_HAL_INTERRUPT_IMB3_SCI1_TX, + CYGNUM_HAL_INTERRUPT_IMB3_SCI1_RX, + CYGNUM_HAL_INTERRUPT_IMB3_SCI1_TX_PRIORITY, + CYGNUM_HAL_INTERRUPT_IMB3_SCI1_RX_PRIORITY, + false, + NULL, + false}; +static SERIAL_CHANNEL( + mpc555_serial_channel1, + mpc555_serial_funs, + mpc555_serial_info1, + CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_POWERPC_MPC555_SERIAL_B_BAUD), + CYG_SERIAL_STOP_DEFAULT, + CYG_SERIAL_PARITY_DEFAULT, + CYG_SERIAL_WORD_LENGTH_DEFAULT, + CYG_SERIAL_FLAGS_DEFAULT); +#endif +DEVTAB_ENTRY(mpc555_serial_io1, + CYGDAT_IO_SERIAL_POWERPC_MPC555_SERIAL_B_NAME, + 0, // does not depend on a lower level device driver + &cyg_io_serial_devio, + mpc555_serial_init, + mpc555_serial_lookup, + &mpc555_serial_channel1); +#endif // ifdef CYGPKG_IO_SERIAL_POWERPC_MPC555_SERIAL_B + +//------------------------------------------------------------------------------ +// Device driver implementation +//------------------------------------------------------------------------------ + +// The arbitration isr. +// I think this is the best place to implement it. +// The device driver is the only place in the code where the knowledge is +// present about how the hardware is used. +// +// Always check receive interrupts. +// Some rom monitor might be waiting for CTRL-C +static cyg_uint32 hal_arbitration_isr_qsci(CYG_ADDRWORD a_vector, + CYG_ADDRWORD a_data) +{ + cyg_uint16 status; + cyg_uint16 control; + + HAL_READ_UINT16(CYGARC_REG_IMM_SC1SR, status); + HAL_READ_UINT16(CYGARC_REG_IMM_SCC1R1, control); + if((status & CYGARC_REG_IMM_SCxSR_RDRF) && + (control & CYGARC_REG_IMM_SCCxR1_RIE)) + return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_SCI1_RX); +// Do not waist time on unused hardware +#ifdef CYGPKG_IO_SERIAL_POWERPC_MPC555_SERIAL_A +#ifdef CYGDAT_IO_SERIAL_POWERPC_MPC555_SERIAL_A_USE_HWARE_QUEUE + // Only one port supports queue mode + if((status & CYGARC_REG_IMM_SCxSR_IDLE) && + (control & CYGARC_REG_IMM_SCCxR1_ILIE)) + return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_SCI1_IDLE); + HAL_READ_UINT16(CYGARC_REG_IMM_QSCI1SR, status); + HAL_READ_UINT16(CYGARC_REG_IMM_QSCI1CR, control); + if((status & CYGARC_REG_IMM_QSCI1SR_QTHF) && + (control & CYGARC_REG_IMM_QSCI1CR_QTHFI)) + return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_SCI1_RXQTHF); + if((status & CYGARC_REG_IMM_QSCI1SR_QBHF) && + (control & CYGARC_REG_IMM_QSCI1CR_QBHFI)) + return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_SCI1_RXQBHF); + if((status & CYGARC_REG_IMM_QSCI1SR_QTHE) && + (control & CYGARC_REG_IMM_QSCI1CR_QTHEI)) + return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_SCI1_TXQTHE); + if((status & CYGARC_REG_IMM_QSCI1SR_QBHE) && + (control & CYGARC_REG_IMM_QSCI1CR_QBHEI)) + return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_SCI1_TXQBHE); +// Only for SPI, leave fo future reference +#if 0 + HAL_READ_UINT16(CYGARC_REG_IMM_SPSR, status); + HAL_READ_UINT16(CYGARC_REG_IMM_SPCR2, control); + if((status & CYGARC_REG_IMM_SPSR_SPIF) && + (control & CYGARC_REG_IMM_SPCR2_SPIFIE)) + return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_SPI_FI); + + HAL_READ_UINT16(CYGARC_REG_IMM_SPCR3, control); + if((status & CYGARC_REG_IMM_SPSR_MODF) && + (control & CYGARC_REG_IMM_SPCR3_HMIE)) + return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_SPI_MODF); + + if((status & CYGARC_REG_IMM_SPSR_HALTA) && + (control & CYGARC_REG_IMM_SPCR3_HMIE)) + return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_SPI_HALTA); +#endif +#else //No HW Queue + if((status & CYGARC_REG_IMM_SCxSR_TDRE) && + (control & CYGARC_REG_IMM_SCCxR1_TIE)) + return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_SCI1_TX); +// Don't waist time on unused interrupts +// Transmit complete interrupt enabled (not used) +// if((status & CYGARC_REG_IMM_SCxSR_TC) && (control & CYGARC_REG_IMM_SCCxR1_TCIE)) +// return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_SCI1_TXC); +// Don't waist time on unused interrupts +// Idle-line interrupt enabled (not used) +// if((status & CYGARC_REG_IMM_SCxSR_IDLE) && (control & CYGARC_REG_IMM_SCCxR1_ILIE)) +// return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_SCI1_IDLE); +#endif // HW_QUEUE +#endif // SERIAL_A + + HAL_READ_UINT16(CYGARC_REG_IMM_SC2SR, status); + HAL_READ_UINT16(CYGARC_REG_IMM_SCC2R1, control); + if((status & CYGARC_REG_IMM_SCxSR_RDRF) && + (control & CYGARC_REG_IMM_SCCxR1_RIE)) + return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_SCI2_RX); +// Do not waist time on unused hardware +#ifdef CYGPKG_IO_SERIAL_POWERPC_MPC555_SERIAL_B + if((status & CYGARC_REG_IMM_SCxSR_TDRE) && + (control & CYGARC_REG_IMM_SCCxR1_TIE)) + return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_SCI2_TX); +// Don't waist time on unused interrupts +// Transmit complete interrupt enabled (not used) +// if((status & CYGARC_REG_IMM_SCxSR_TC) && (control & CYGARC_REG_IMM_SCCxR1_TCIE)) +// return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_SCI2_TXC); +// Don't waist time on unused interrupts +// Idle-line interrupt enabled (not used) +// if((status & CYGARC_REG_IMM_SCxSR_IDLE) && (control & CYGARC_REG_IMM_SCCxR1_ILIE)) +// return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_SCI2_IDLE); +#endif + + return 0; +} + +//------------------------------------------------------------------------------ +// Internal function to configure the hardware to desired baud rate, etc. +//------------------------------------------------------------------------------ +static bool mpc555_serial_config_port(serial_channel * chan, + cyg_serial_info_t * new_config, + bool init) +{ + mpc555_serial_info * mpc555_chan = (mpc555_serial_info *)(chan->dev_priv); + + cyg_addrword_t port = mpc555_chan->base; + cyg_uint16 baud_rate = select_baud[new_config->baud]; + unsigned char frame_length = 1; // The start bit + + cyg_uint16 old_isrstate; + cyg_uint16 sccxr; + + if(!baud_rate) + return false; // Invalid baud rate selected + + if((new_config->word_length != CYGNUM_SERIAL_WORD_LENGTH_7) && + (new_config->word_length != CYGNUM_SERIAL_WORD_LENGTH_8)) + return false; // Invalid word length selected + + if((new_config->parity != CYGNUM_SERIAL_PARITY_NONE) && + (new_config->parity != CYGNUM_SERIAL_PARITY_EVEN) && + (new_config->parity != CYGNUM_SERIAL_PARITY_ODD)) + return false; // Invalid parity selected + + if((new_config->stop != CYGNUM_SERIAL_STOP_1) && + (new_config->stop != CYGNUM_SERIAL_STOP_2)) + return false; // Invalid stop bits selected + + frame_length += select_word_length[new_config->word_length - + CYGNUM_SERIAL_WORD_LENGTH_5]; + frame_length += select_stop_bits[new_config->stop]; + frame_length += select_parity[new_config->parity]; + + if((frame_length != 10) && (frame_length != 11)) + return false; // Invalid frame format selected + + // Disable port interrupts while changing hardware + HAL_READ_UINT16(port + MPC555_SERIAL_SCCxR1, sccxr); + old_isrstate = sccxr; + old_isrstate &= ~((cyg_uint16)MPC555_SERIAL_SCCxR1_LOOPS); + old_isrstate &= ~((cyg_uint16)MPC555_SERIAL_SCCxR1_WOMS); + old_isrstate &= ~((cyg_uint16)MPC555_SERIAL_SCCxR1_ILT); + old_isrstate &= ~((cyg_uint16)MPC555_SERIAL_SCCxR1_PT); + old_isrstate &= ~((cyg_uint16)MPC555_SERIAL_SCCxR1_PE); + old_isrstate &= ~((cyg_uint16)MPC555_SERIAL_SCCxR1_M); + old_isrstate &= ~((cyg_uint16)MPC555_SERIAL_SCCxR1_WAKE); + old_isrstate &= ~((cyg_uint16)MPC555_SERIAL_SCCxR1_TE); + old_isrstate &= ~((cyg_uint16)MPC555_SERIAL_SCCxR1_RE); + old_isrstate &= ~((cyg_uint16)MPC555_SERIAL_SCCxR1_RWU); + old_isrstate &= ~((cyg_uint16)MPC555_SERIAL_SCCxR1_SBK); + sccxr &= ~((cyg_uint16)MPC555_SERIAL_SCCxR1_TIE); + sccxr &= ~((cyg_uint16)MPC555_SERIAL_SCCxR1_TCIE); + sccxr &= ~((cyg_uint16)MPC555_SERIAL_SCCxR1_RIE); + sccxr &= ~((cyg_uint16)MPC555_SERIAL_SCCxR1_ILIE); + HAL_WRITE_UINT16(port + MPC555_SERIAL_SCCxR1, sccxr); + +#ifdef CYGDAT_IO_SERIAL_POWERPC_MPC555_SERIAL_A_USE_HWARE_QUEUE + cyg_uint16 qsci1cr = 0; + if(mpc555_chan->use_queue){ + HAL_READ_UINT16( CYGARC_REG_IMM_QSCI1SR, qsci1cr); + // disable queue + qsci1cr &= ~((cyg_uint16)CYGARC_REG_IMM_QSCI1CR_QTE); + qsci1cr &= ~((cyg_uint16)CYGARC_REG_IMM_QSCI1CR_QRE); + // disable queue interrupts + qsci1cr &= ~((cyg_uint16)CYGARC_REG_IMM_QSCI1CR_QTHFI); + qsci1cr &= ~((cyg_uint16)CYGARC_REG_IMM_QSCI1CR_QBHFI); + qsci1cr &= ~((cyg_uint16)CYGARC_REG_IMM_QSCI1CR_QTHEI); + qsci1cr &= ~((cyg_uint16)CYGARC_REG_IMM_QSCI1CR_QBHEI); + HAL_WRITE_UINT16( CYGARC_REG_IMM_QSCI1SR, qsci1cr); + } +#endif + // Set databits, stopbits and parity. + HAL_READ_UINT16(port + MPC555_SERIAL_SCCxR1, sccxr); + + if(frame_length == 11) + sccxr |= (cyg_uint16)MPC555_SERIAL_SCCxR1_M; + else + sccxr &= ~((cyg_uint16)MPC555_SERIAL_SCCxR1_M); + + switch(new_config->parity){ + case CYGNUM_SERIAL_PARITY_NONE: + sccxr &= ~((cyg_uint16)MPC555_SERIAL_SCCxR1_PE); + break; + case CYGNUM_SERIAL_PARITY_EVEN: + sccxr |= (cyg_uint16)MPC555_SERIAL_SCCxR1_PE; + sccxr &= ~((cyg_uint16)MPC555_SERIAL_SCCxR1_PT); + break; + case CYGNUM_SERIAL_PARITY_ODD: + sccxr |= (cyg_uint16)MPC555_SERIAL_SCCxR1_PE; + sccxr |= (cyg_uint16)MPC555_SERIAL_SCCxR1_PT; + break; + default: + break; + } + HAL_WRITE_UINT16(port + MPC555_SERIAL_SCCxR1, sccxr); + + // Set baud rate. + baud_rate &= ~((cyg_uint16)MPC555_SERIAL_SCCxR0_OTHR); + baud_rate &= ~((cyg_uint16)MPC555_SERIAL_SCCxR0_LINKBD); + HAL_READ_UINT16(port + MPC555_SERIAL_SCCxR0, sccxr); + sccxr &= ~(MPC555_SERIAL_SCCxR0_SCxBR); + sccxr |= baud_rate; + HAL_WRITE_UINT16(port + MPC555_SERIAL_SCCxR0, sccxr); + + // Enable the device + HAL_READ_UINT16(port + MPC555_SERIAL_SCCxR1, sccxr); + sccxr |= MPC555_SERIAL_SCCxR1_TE; + sccxr |= MPC555_SERIAL_SCCxR1_RE; + HAL_WRITE_UINT16(port + MPC555_SERIAL_SCCxR1, sccxr); + + if(init){ +#ifdef CYGDAT_IO_SERIAL_POWERPC_MPC555_SERIAL_A_USE_HWARE_QUEUE + HAL_READ_UINT16(port + MPC555_SERIAL_SCCxR1, sccxr); + if(mpc555_chan->use_queue){ + cyg_uint16 qsci1sr; + // enable read queue + qsci1cr |= ((cyg_uint16)CYGARC_REG_IMM_QSCI1CR_QRE); + // enable receive queue interrupts + qsci1cr |= ((cyg_uint16)CYGARC_REG_IMM_QSCI1CR_QTHFI); + qsci1cr |= ((cyg_uint16)CYGARC_REG_IMM_QSCI1CR_QBHFI); + HAL_WRITE_UINT16( CYGARC_REG_IMM_QSCI1CR, qsci1cr); + // also enable idle line detect interrupt + sccxr |= MPC555_SERIAL_SCxSR_IDLE; + HAL_READ_UINT16( CYGARC_REG_IMM_QSCI1SR, qsci1sr); + qsci1sr &= ~((cyg_uint16)CYGARC_REG_IMM_QSCI1SR_QBHF); + qsci1sr &= ~((cyg_uint16)CYGARC_REG_IMM_QSCI1SR_QTHF); + HAL_WRITE_UINT16(CYGARC_REG_IMM_QSCI1SR, qsci1sr); + } + else { + // enable the receiver interrupt + sccxr |= MPC555_SERIAL_SCCxR1_RIE; + } + HAL_WRITE_UINT16(port + MPC555_SERIAL_SCCxR1, sccxr); +#else + // enable the receiver interrupt + HAL_READ_UINT16(port + MPC555_SERIAL_SCCxR1, sccxr); + sccxr |= MPC555_SERIAL_SCCxR1_RIE; + HAL_WRITE_UINT16(port + MPC555_SERIAL_SCCxR1, sccxr); +#endif + } + else {// Restore the old interrupt state + + HAL_READ_UINT16(port + MPC555_SERIAL_SCCxR1, sccxr); + sccxr |= old_isrstate; + HAL_WRITE_UINT16(port + MPC555_SERIAL_SCCxR1, sccxr); + } + + if(new_config != &chan->config) + chan->config = *new_config; + + return true; +} + +//------------------------------------------------------------------------------ +// Function to initialize the device. Called at bootstrap time. +//------------------------------------------------------------------------------ +static hal_mpc5xx_arbitration_data arbiter; +static bool mpc555_serial_init(struct cyg_devtab_entry * tab){ + serial_channel * chan = (serial_channel *)tab->priv; + mpc555_serial_info * mpc555_chan = (mpc555_serial_info *)chan->dev_priv; + + if(!mpc555_serial_config_port(chan, &chan->config, true)) + return false; + + // Really only required for interrupt driven devices + (chan->callbacks->serial_init)(chan); + if(chan->out_cbuf.len != 0){ + arbiter.priority = CYGNUM_HAL_ISR_SOURCE_PRIORITY_QSCI; + arbiter.data = 0; + arbiter.arbiter = hal_arbitration_isr_qsci; + + // Install the arbitration isr, Make sure that is is not installed twice + hal_mpc5xx_remove_arbitration_isr(CYGNUM_HAL_ISR_SOURCE_PRIORITY_QSCI); + hal_mpc5xx_install_arbitration_isr(&arbiter); + + // if !(Chan_B && using queue) + if(!mpc555_chan->use_queue){ + mpc555_chan->rx_circbuf->fill_pos = 0; + mpc555_chan->rx_circbuf->read_pos = 0; + + // Create the Tx interrupt, do not enable it yet + cyg_drv_interrupt_create(mpc555_chan->tx_interrupt_num, + mpc555_chan->tx_interrupt_priority, + (cyg_addrword_t)chan,//Data item passed to isr + mpc555_serial_tx_ISR, + mpc555_serial_tx_DSR, + &mpc555_chan->tx_interrupt_handle, + &mpc555_chan->tx_interrupt); + cyg_drv_interrupt_attach(mpc555_chan->tx_interrupt_handle); + + // Create the Rx interrupt, this can be safely unmasked now + cyg_drv_interrupt_create(mpc555_chan->rx_interrupt_num, + mpc555_chan->rx_interrupt_priority, + (cyg_addrword_t)chan, + mpc555_serial_rx_ISR, + mpc555_serial_rx_DSR, + &mpc555_chan->rx_interrupt_handle, + &mpc555_chan->rx_interrupt); + cyg_drv_interrupt_attach(mpc555_chan->rx_interrupt_handle); + cyg_drv_interrupt_unmask(mpc555_chan->rx_interrupt_num); + } +#ifdef CYGDAT_IO_SERIAL_POWERPC_MPC555_SERIAL_A_USE_HWARE_QUEUE + else {// Use HW queue + // Create the Tx interrupt, do not enable it yet + cyg_drv_interrupt_create(mpc555_chan->tx_interrupt_queue_top_empty_num, + mpc555_chan->tx_interrupt_queue_top_empty_priority, + (cyg_addrword_t)chan,//Data item passed to isr + mpc555_serial_tx_queue_top_ISR, + mpc555_serial_tx_queue_DSR, + &mpc555_chan->tx_queue_top_interrupt_handle, + &mpc555_chan->tx_queue_top_interrupt); + cyg_drv_interrupt_attach(mpc555_chan->tx_queue_top_interrupt_handle); + + + cyg_drv_interrupt_create(mpc555_chan->tx_interrupt_queue_bot_empty_num, + mpc555_chan->tx_interrupt_queue_bot_empty_priority, + (cyg_addrword_t)chan,//Data passed to isr + mpc555_serial_tx_queue_bot_ISR, + mpc555_serial_tx_queue_DSR, + &mpc555_chan->tx_queue_bot_interrupt_handle, + &mpc555_chan->tx_queue_bot_interrupt); + cyg_drv_interrupt_attach(mpc555_chan->tx_queue_bot_interrupt_handle); + + // Rx queue interrupts + cyg_drv_interrupt_create(mpc555_chan->rx_interrupt_queue_top_full_num, + mpc555_chan->rx_interrupt_queue_top_full_priority, + (cyg_addrword_t)chan,//Data item passed to isr + mpc555_serial_rx_queue_top_ISR, + mpc555_serial_rx_queue_DSR, + &mpc555_chan->rx_queue_top_interrupt_handle, + &mpc555_chan->rx_queue_top_interrupt); + cyg_drv_interrupt_attach(mpc555_chan->rx_queue_top_interrupt_handle); + + cyg_drv_interrupt_create(mpc555_chan->rx_interrupt_queue_bot_full_num, + mpc555_chan->rx_interrupt_queue_bot_full_priority, + (cyg_addrword_t)chan,//Data item passed to isr + mpc555_serial_rx_queue_bot_ISR, + mpc555_serial_rx_queue_DSR, + &mpc555_chan->rx_queue_bot_interrupt_handle, + &mpc555_chan->rx_queue_bot_interrupt); + cyg_drv_interrupt_attach(mpc555_chan->rx_queue_bot_interrupt_handle); + + cyg_drv_interrupt_create(mpc555_chan->rx_interrupt_idle_line_num, + mpc555_chan->rx_interrupt_idle_line_priority, + (cyg_addrword_t)chan,//Data item passed to isr + mpc555_serial_rx_idle_line_ISR, + mpc555_serial_rx_queue_DSR, + &mpc555_chan->rx_idle_interrupt_handle, + &mpc555_chan->rx_idle_interrupt); + cyg_drv_interrupt_attach(mpc555_chan->rx_idle_interrupt_handle); + } +#endif // use queue + } + return true; +} + +//---------------------------------------------------------------------------- +// This routine is called when the device is "looked" up (i.e. attached) +//---------------------------------------------------------------------------- +static Cyg_ErrNo mpc555_serial_lookup(struct cyg_devtab_entry ** tab, + struct cyg_devtab_entry * sub_tab, + const char * name) +{ + serial_channel * chan = (serial_channel *)(*tab)->priv; + //Really only required for interrupt driven devices + (chan->callbacks->serial_init)(chan); + + return ENOERR; +} + +//---------------------------------------------------------------------------- +// Send a character to the device output buffer. +// Return 'true' if character is sent to device +//---------------------------------------------------------------------------- +static bool mpc555_serial_putc(serial_channel * chan, unsigned char c){ + mpc555_serial_info * mpc555_chan = (mpc555_serial_info *)chan->dev_priv; + cyg_addrword_t port = mpc555_chan->base; + + cyg_uint16 scsr; + cyg_uint16 scdr; + + HAL_READ_UINT16(port + MPC555_SERIAL_SCxSR, scsr); + if(scsr & MPC555_SERIAL_SCxSR_TDRE){ + // Ok, we have space, write the character and return success + scdr = (cyg_uint16)c; + HAL_WRITE_UINT16(port + MPC555_SERIAL_SCxDR, scdr); + return true; + } + else + // We cannot write to the transmitter, return failure + return false; +} + +//---------------------------------------------------------------------------- +// Fetch a character from the device input buffer, waiting if necessary +//---------------------------------------------------------------------------- +static unsigned char mpc555_serial_getc(serial_channel * chan){ + unsigned char c; + mpc555_serial_info * mpc555_chan = (mpc555_serial_info *)chan->dev_priv; + cyg_addrword_t port = mpc555_chan->base; + + cyg_uint16 scsr; + cyg_uint16 scdr; + + do { + HAL_READ_UINT16(port + MPC555_SERIAL_SCxSR, scsr); + } while(!(scsr & MPC555_SERIAL_SCxSR_RDRF)); + + // Ok, data is received, read it out and return + HAL_READ_UINT16(port + MPC555_SERIAL_SCxDR, scdr); + c = (unsigned char)scdr; + + return c; +} + +//---------------------------------------------------------------------------- +// Set up the device characteristics; baud rate, etc. +//---------------------------------------------------------------------------- +static bool mpc555_serial_set_config(serial_channel * chan, cyg_uint32 key, + const void *xbuf, cyg_uint32 * len) +{ + switch(key){ + case CYG_IO_SET_CONFIG_SERIAL_INFO:{ + cyg_serial_info_t *config = (cyg_serial_info_t *)xbuf; + if(*len < sizeof(cyg_serial_info_t)){ + return -EINVAL; + } + *len = sizeof(cyg_serial_info_t); + if(true != mpc555_serial_config_port(chan, config, false)) + return -EINVAL; + } + break; + default: + return -EINVAL; + } + return ENOERR; +} + +//------------------------------------------------------------------------------ +// Enable the transmitter on the device +//------------------------------------------------------------------------------ +static void mpc555_serial_start_xmit(serial_channel * chan) +{ + mpc555_serial_info * mpc555_chan = (mpc555_serial_info *)chan->dev_priv; +#ifdef CYGDAT_IO_SERIAL_POWERPC_MPC555_SERIAL_A_USE_HWARE_QUEUE + cyg_addrword_t port = mpc555_chan->base; + if(mpc555_chan->use_queue){ + cyg_uint16 qscicr; + cyg_uint16 qscisr; + cyg_uint16 scsr; + + int chars_avail; + unsigned char* chars; + int block_index = 0; + cyg_addrword_t i; + cyg_uint16 queue_transfer; + + if(!(mpc555_chan->tx_interrupt_enable) && + (chan->callbacks->data_xmt_req)(chan, 32, &chars_avail, &chars) + == CYG_XMT_OK){ + queue_transfer = (chars_avail > 16) ? 16 : chars_avail; + + HAL_READ_UINT16( CYGARC_REG_IMM_QSCI1CR, qscicr); + // Write QTSZ for first pass through the queue + qscicr &= ~(CYGARC_REG_IMM_QSCI1CR_QTSZ); + qscicr |= (CYGARC_REG_IMM_QSCI1CR_QTSZ & (queue_transfer - 1)); + HAL_WRITE_UINT16( CYGARC_REG_IMM_QSCI1CR, qscicr); + // Read SC1SR to clear TC bit when followed by a write of sctq + HAL_READ_UINT16(port + MPC555_SERIAL_SCxSR, scsr); + + for(i=0; i < queue_transfer; i++){ + HAL_WRITE_UINT16(CYGARC_REG_IMM_SCTQ + (i * 2), chars[block_index]); + ++block_index; + } + chan->callbacks->data_xmt_done(chan, queue_transfer); + + // clear QTHE and QBHE + HAL_READ_UINT16(CYGARC_REG_IMM_QSCI1SR, qscisr); + + qscisr &= ~(CYGARC_REG_IMM_QSCI1SR_QTHE); + HAL_WRITE_UINT16(CYGARC_REG_IMM_QSCI1SR, qscisr); + if(queue_transfer > 8){ + qscisr &= ~(CYGARC_REG_IMM_QSCI1SR_QBHE); + HAL_WRITE_UINT16(CYGARC_REG_IMM_QSCI1SR, qscisr); + } + + mpc555_chan->tx_interrupt_enable = true; + + cyg_drv_interrupt_unmask(mpc555_chan->tx_interrupt_queue_top_empty_num); + if(queue_transfer > 8){ + cyg_drv_interrupt_unmask(mpc555_chan->tx_interrupt_queue_bot_empty_num); + } + + HAL_READ_UINT16( CYGARC_REG_IMM_QSCI1CR, qscicr); + qscicr |= ((cyg_uint16)CYGARC_REG_IMM_QSCI1CR_QTE); + HAL_WRITE_UINT16( CYGARC_REG_IMM_QSCI1CR, qscicr); + } + } + else { // no queue + mpc555_chan->tx_interrupt_enable = true; + cyg_drv_interrupt_unmask(mpc555_chan->tx_interrupt_num); +// No need to call xmt_char, this will generate an interrupt immediately. + } +#else // No queue + mpc555_chan->tx_interrupt_enable = true; + cyg_drv_interrupt_unmask(mpc555_chan->tx_interrupt_num); +// No need to call xmt_char, this will generate an interrupt immediately. +#endif +} + +//---------------------------------------------------------------------------- +// Disable the transmitter on the device +//---------------------------------------------------------------------------- +static void mpc555_serial_stop_xmit(serial_channel * chan){ + mpc555_serial_info * mpc555_chan = (mpc555_serial_info *)chan->dev_priv; + + if(!mpc555_chan->use_queue){ + cyg_drv_dsr_lock(); + mpc555_chan->tx_interrupt_enable = false; + cyg_drv_interrupt_mask(mpc555_chan->tx_interrupt_num); + cyg_drv_dsr_unlock(); + } +} + +//---------------------------------------------------------------------------- +// The low level transmit interrupt handler +//---------------------------------------------------------------------------- +static cyg_uint32 mpc555_serial_tx_ISR(cyg_vector_t vector, + cyg_addrword_t data){ + serial_channel * chan = (serial_channel *)data; + mpc555_serial_info * mpc555_chan = (mpc555_serial_info *)chan->dev_priv; + + cyg_drv_interrupt_mask(mpc555_chan->tx_interrupt_num); + cyg_drv_interrupt_acknowledge(mpc555_chan->tx_interrupt_num); + + return CYG_ISR_CALL_DSR; // cause the DSR to run +} + +//---------------------------------------------------------------------------- +// The low level receive interrupt handler +//---------------------------------------------------------------------------- +static cyg_uint32 mpc555_serial_rx_ISR(cyg_vector_t vector, + cyg_addrword_t data){ + serial_channel * chan = (serial_channel *)data; + mpc555_serial_info * mpc555_chan = (mpc555_serial_info *)chan->dev_priv; + + cyg_drv_interrupt_mask(mpc555_chan->rx_interrupt_num); + cyg_drv_interrupt_acknowledge(mpc555_chan->rx_interrupt_num); + + cyg_addrword_t port = mpc555_chan->base; + cyg_uint16 scdr; + cyg_uint16 scsr; + + HAL_READ_UINT16(port + MPC555_SERIAL_SCxSR, scsr); + // Always read out the received character, in order to clear receiver flags + HAL_READ_UINT16(port + MPC555_SERIAL_SCxDR, scdr); + + mpc555_chan->rx_circbuf->scsr[mpc555_chan->rx_circbuf->fill_pos] = scsr; + mpc555_chan->rx_circbuf->buf[mpc555_chan->rx_circbuf->fill_pos] = (cyg_uint8)scdr; + + if(mpc555_chan->rx_circbuf->fill_pos < MPC555_SCI_RX_BUFF_SIZE - 1){ + mpc555_chan->rx_circbuf->fill_pos = mpc555_chan->rx_circbuf->fill_pos + 1; + } + else { + mpc555_chan->rx_circbuf->fill_pos = 0; + } + cyg_drv_interrupt_unmask(mpc555_chan->rx_interrupt_num); + return CYG_ISR_CALL_DSR; // cause the DSR to run +} + +#ifdef CYGPKG_IO_SERIAL_POWERPC_MPC555_SERIAL_A +//---------------------------------------------------------------------------- +// The low level queued receive interrupt handlers +//---------------------------------------------------------------------------- +static cyg_uint32 mpc555_serial_rx_queue_top_ISR(cyg_vector_t vector, + cyg_addrword_t data){ + serial_channel * chan = (serial_channel *)data; + mpc555_serial_info * mpc555_chan = (mpc555_serial_info *)chan->dev_priv; + + cyg_drv_interrupt_mask(mpc555_chan->rx_interrupt_queue_top_full_num); + cyg_drv_interrupt_acknowledge(mpc555_chan->rx_interrupt_queue_top_full_num); + + return CYG_ISR_CALL_DSR; // cause the DSR to run +} + +static cyg_uint32 mpc555_serial_rx_queue_bot_ISR(cyg_vector_t vector, + cyg_addrword_t data){ + serial_channel* chan = (serial_channel *)data; + mpc555_serial_info* mpc555_chan = (mpc555_serial_info *)chan->dev_priv; + + cyg_drv_interrupt_mask(mpc555_chan->rx_interrupt_queue_bot_full_num); + cyg_drv_interrupt_acknowledge(mpc555_chan->rx_interrupt_queue_bot_full_num); + + return CYG_ISR_CALL_DSR; // cause the DSR to run +} + +// This is used to flush the queue when the line falls idle +static cyg_uint32 mpc555_serial_rx_idle_line_ISR(cyg_vector_t vector, + cyg_addrword_t data){ + serial_channel* chan = (serial_channel *)data; + mpc555_serial_info* mpc555_chan = (mpc555_serial_info *)chan->dev_priv; + + cyg_drv_interrupt_mask(mpc555_chan->rx_interrupt_idle_line_num); + cyg_drv_interrupt_acknowledge(mpc555_chan->rx_interrupt_idle_line_num); + + return CYG_ISR_CALL_DSR; // cause the DSR to run +} + +//---------------------------------------------------------------------------- +// The low level queued transmit interrupt handlers +//---------------------------------------------------------------------------- +static cyg_uint32 mpc555_serial_tx_queue_top_ISR(cyg_vector_t vector, + cyg_addrword_t data){ + serial_channel * chan = (serial_channel *)data; + mpc555_serial_info * mpc555_chan = (mpc555_serial_info *)chan->dev_priv; + + cyg_drv_interrupt_mask(mpc555_chan->tx_interrupt_queue_top_empty_num); + cyg_drv_interrupt_acknowledge(mpc555_chan->tx_interrupt_queue_top_empty_num); + + return CYG_ISR_CALL_DSR; // cause the DSR to run +} + +static cyg_uint32 mpc555_serial_tx_queue_bot_ISR(cyg_vector_t vector, + cyg_addrword_t data){ + serial_channel * chan = (serial_channel *)data; + mpc555_serial_info * mpc555_chan = (mpc555_serial_info *)chan->dev_priv; + + cyg_drv_interrupt_mask(mpc555_chan->tx_interrupt_queue_bot_empty_num); + cyg_drv_interrupt_acknowledge(mpc555_chan->tx_interrupt_queue_bot_empty_num); + + return CYG_ISR_CALL_DSR; // cause the DSR to run +} +#endif // SERIAL_A + +//---------------------------------------------------------------------------- +// The high level transmit interrupt handler +//---------------------------------------------------------------------------- +static void mpc555_serial_tx_DSR(cyg_vector_t vector, cyg_ucount32 count, + cyg_addrword_t data){ + serial_channel * chan = (serial_channel *)data; + mpc555_serial_info * mpc555_chan = (mpc555_serial_info *)chan->dev_priv; + + (chan->callbacks->xmt_char)(chan); + if(mpc555_chan->tx_interrupt_enable) + cyg_drv_interrupt_unmask(mpc555_chan->tx_interrupt_num); +} + +//---------------------------------------------------------------------------- +// The high level receive interrupt handler +//---------------------------------------------------------------------------- +#define MPC555_SERIAL_SCxSR_ERRORS (MPC555_SERIAL_SCxSR_OR | \ + MPC555_SERIAL_SCxSR_NF | \ + MPC555_SERIAL_SCxSR_FE | \ + MPC555_SERIAL_SCxSR_PF) + +static void mpc555_serial_rx_DSR(cyg_vector_t vector, cyg_ucount32 count, + cyg_addrword_t data){ + serial_channel * chan = (serial_channel *)data; + mpc555_serial_info * mpc555_chan = (mpc555_serial_info *)chan->dev_priv; +// cyg_addrword_t port = mpc555_chan->base; +// cyg_uint16 scdr; + cyg_uint16 scsr; + +#ifdef CYGOPT_IO_SERIAL_SUPPORT_LINE_STATUS + cyg_serial_line_status_t stat; +#endif + + + int i = mpc555_chan->rx_circbuf->read_pos; + while (i < mpc555_chan->rx_circbuf->fill_pos){ + scsr = mpc555_chan->rx_circbuf->scsr[i]; + if(scsr & (cyg_uint16)MPC555_SERIAL_SCxSR_ERRORS){ +#ifdef CYGOPT_IO_SERIAL_SUPPORT_LINE_STATUS + if(scsr & MPC555_SERIAL_SCxSR_OR){ + stat.which = CYGNUM_SERIAL_STATUS_OVERRUNERR; + (chan->callbacks->indicate_status)(chan, &stat); + // The current byte is still valid when OR is set + (chan->callbacks->rcv_char)(chan, mpc555_chan->rx_circbuf->buf[i]); + } + else { // OR is never set with any other error bits + if(scsr & MPC555_SERIAL_SCxSR_NF){ + stat.which = CYGNUM_SERIAL_STATUS_NOISEERR; + (chan->callbacks->indicate_status)(chan, &stat); + } + if(scsr & MPC555_SERIAL_SCxSR_FE){ + stat.which = CYGNUM_SERIAL_STATUS_FRAMEERR; + (chan->callbacks->indicate_status)(chan, &stat); + } + if(scsr & MPC555_SERIAL_SCxSR_PF){ + stat.which = CYGNUM_SERIAL_STATUS_PARITYERR; + (chan->callbacks->indicate_status)(chan, &stat); + } + } +#endif + } + else { + (chan->callbacks->rcv_char)(chan, mpc555_chan->rx_circbuf->buf[i]); + } + ++i; + } + + cyg_drv_isr_lock(); + mpc555_chan->rx_circbuf->fill_pos = 0; + mpc555_chan->rx_circbuf->read_pos = 0; + cyg_drv_isr_unlock(); +} + +#ifdef CYGPKG_IO_SERIAL_POWERPC_MPC555_SERIAL_A +//---------------------------------------------------------------------------- +// The high level queued transmit interrupt handler +//---------------------------------------------------------------------------- +static void mpc555_serial_tx_queue_DSR(cyg_vector_t vector, cyg_ucount32 count, + cyg_addrword_t data){ + serial_channel * chan = (serial_channel *)data; + mpc555_serial_info * mpc555_chan = (mpc555_serial_info *)chan->dev_priv; + bool QTHE = false; + bool QBHE = false; + cyg_uint16 qscisr; + cyg_uint16 qscicr; + HAL_READ_UINT16(CYGARC_REG_IMM_QSCI1SR, qscisr); + QTHE = (qscisr & CYGARC_REG_IMM_QSCI1SR_QTHE) ? true : false; + QBHE = (qscisr & CYGARC_REG_IMM_QSCI1SR_QBHE) ? true : false; + + CYG_ASSERT(QTHE || QBHE,"In tx queue DSR for no reason"); + + HAL_READ_UINT16(CYGARC_REG_IMM_QSCI1CR, qscicr); + int chars_avail; + unsigned char* chars; + int block_index = 0; + cyg_addrword_t i; + cyg_uint16 queue_transfer; + xmt_req_reply_t result = (chan->callbacks->data_xmt_req)(chan, 24, &chars_avail, &chars); + if(CYG_XMT_OK == result){ + queue_transfer = (chars_avail > 8) ? 8 : chars_avail; + if(QTHE){ + for(i=0; i < queue_transfer; i++){ + HAL_WRITE_UINT16(CYGARC_REG_IMM_SCTQ + (i * 2), chars[block_index]); + ++block_index; + } + chan->callbacks->data_xmt_done(chan, queue_transfer); + // Clear QTHE + qscisr &= ~(CYGARC_REG_IMM_QSCI1SR_QTHE); + HAL_WRITE_UINT16(CYGARC_REG_IMM_QSCI1SR, qscisr); + + // Re-enable wrap QTWE + HAL_READ_UINT16(CYGARC_REG_IMM_QSCI1CR, qscicr); + qscicr |= ((cyg_uint16)CYGARC_REG_IMM_QSCI1CR_QTWE); + HAL_WRITE_UINT16(CYGARC_REG_IMM_QSCI1CR, qscicr); + HAL_READ_UINT16(CYGARC_REG_IMM_QSCI1CR, qscicr); + // load QTSZ with how many chars *after* the next wrap + cyg_uint16 next_time = (chars_avail) > 16 ? 15 : chars_avail -1; + qscicr &= ~(CYGARC_REG_IMM_QSCI1CR_QTSZ); + qscicr |= (CYGARC_REG_IMM_QSCI1CR_QTSZ & next_time); + HAL_WRITE_UINT16(CYGARC_REG_IMM_QSCI1CR, qscicr); + cyg_drv_interrupt_unmask(mpc555_chan->tx_interrupt_queue_top_empty_num); + } + else if(QBHE){ + for(i=8; i < queue_transfer + 8; i++){ + HAL_WRITE_UINT16(CYGARC_REG_IMM_SCTQ + (i * 2), chars[block_index]); + ++block_index; + } + chan->callbacks->data_xmt_done(chan, queue_transfer); + // Clear QBHE + qscisr &= ~(CYGARC_REG_IMM_QSCI1SR_QBHE); + HAL_WRITE_UINT16(CYGARC_REG_IMM_QSCI1SR, qscisr); + cyg_drv_interrupt_unmask(mpc555_chan->tx_interrupt_queue_bot_empty_num); + } + + } + else if(CYG_XMT_EMPTY== result){ + // No more data + cyg_drv_interrupt_mask(mpc555_chan->tx_interrupt_queue_top_empty_num); + cyg_drv_interrupt_mask(mpc555_chan->tx_interrupt_queue_bot_empty_num); + mpc555_chan->tx_interrupt_enable = false; + + // Clear QTHE + HAL_READ_UINT16(CYGARC_REG_IMM_QSCI1SR, qscisr); + qscisr &= ~(CYGARC_REG_IMM_QSCI1SR_QTHE); + HAL_WRITE_UINT16(CYGARC_REG_IMM_QSCI1SR, qscisr); + } +} + +//---------------------------------------------------------------------------- +// The high level queued receive interrupt handler +//---------------------------------------------------------------------------- +static void mpc555_serial_rx_queue_DSR(cyg_vector_t vector, + cyg_ucount32 count, cyg_addrword_t data){ + serial_channel * chan = (serial_channel *)data; + mpc555_serial_info * mpc555_chan = (mpc555_serial_info *)chan->dev_priv; + cyg_addrword_t port = mpc555_chan->base; + cyg_uint16 scrq; + cyg_uint16 qscisr; + cyg_uint16 scsr; + cyg_uint16 scdr; + bool QTHF = false; + bool QBHF = false; + bool idle = false; + // Read status reg before reading any data otherwise NE flag will be lost + HAL_READ_UINT16(port + MPC555_SERIAL_SCxSR, scsr); + HAL_READ_UINT16(CYGARC_REG_IMM_QSCI1SR, qscisr); + QTHF = (qscisr & CYGARC_REG_IMM_QSCI1SR_QTHF) ? true : false; + QBHF = (qscisr & CYGARC_REG_IMM_QSCI1SR_QBHF) ? true : false; + idle = (scsr & CYGARC_REG_IMM_SCxSR_IDLE)? true : false; + // The queue pointer is the next place to be filled by incomming data + cyg_uint16 queue_pointer = (qscisr & CYGARC_REG_IMM_QSCI1SR_QRPNT) >> 4; + + int start; + int space_req = 0; + // Idle needs to be handled first as the IDLE bit will be cleared by a read of + // scsr followed by a read of scrq[0:16] + + if(queue_pointer > mpc555_chan->rx_last_queue_pointer){ + start = mpc555_chan->rx_last_queue_pointer; + space_req = mpc555_serial_read_queue(chan, start, queue_pointer - 1); + } + else {// Its wrapped around + if(mpc555_chan->rx_last_queue_pointer > queue_pointer){ + space_req = mpc555_serial_read_queue(chan, mpc555_chan->rx_last_queue_pointer,15); + if(queue_pointer != 0){ + mpc555_serial_read_queue(chan, 0,queue_pointer -1); + } + } + else // No new data to read, do nothing here + { + } + } + + mpc555_chan->rx_last_queue_pointer = queue_pointer; + + if(CYGARC_REG_IMM_QSCI1SR_QOR & qscisr){ + // Need to re-enable the queue + cyg_uint16 qscicr; + HAL_READ_UINT16( CYGARC_REG_IMM_QSCI1CR, qscicr); + qscicr |= ((cyg_uint16)CYGARC_REG_IMM_QSCI1CR_QRE); + HAL_WRITE_UINT16( CYGARC_REG_IMM_QSCI1CR, qscicr); + // Queue has overrun but data might not have been lost yet + if(scsr & MPC555_SERIAL_SCxSR_OR){ +#ifdef CYGOPT_IO_SERIAL_SUPPORT_LINE_STATUS + cyg_serial_line_status_t stat; + stat.which = CYGNUM_SERIAL_STATUS_OVERRUNERR; + (chan->callbacks->indicate_status)(chan, &stat); +#endif + } + } + + if(scsr & (cyg_uint16)MPC555_SERIAL_SCxSR_ERRORS){ + // Special case for queue overrun handled above. + // Only data without FE or PF is allowed into the queue. + // Data with NE is allowed into the queue. + // If FE or PF have occured then the queue is disabled + // until they are cleared (by reading scsr then scdr). + +#ifdef CYGOPT_IO_SERIAL_SUPPORT_LINE_STATUS + cyg_serial_line_status_t stat; + if(scsr & MPC555_SERIAL_SCxSR_NF){ + // Note if there is more than one frame in the queue + // it is not possible to tell which frame + // in the queue caused the noise error. + // The error has already been cleared by reading + // srsr then scrq[n], so no action is required here. + stat.which = CYGNUM_SERIAL_STATUS_NOISEERR; + (chan->callbacks->indicate_status)(chan, &stat); + } +#endif + if(scsr & (MPC555_SERIAL_SCxSR_FE | MPC555_SERIAL_SCxSR_PF)){ + // This action needs to be taken clear the status bits so that + // the queue can be re-enabled. + HAL_READ_UINT16(port + MPC555_SERIAL_SCxDR, scdr); + // Need to re-enable the queue + cyg_uint16 qscicr; + HAL_READ_UINT16(CYGARC_REG_IMM_QSCI1CR, qscicr); + qscicr |= ((cyg_uint16)CYGARC_REG_IMM_QSCI1CR_QRE); + HAL_WRITE_UINT16(CYGARC_REG_IMM_QSCI1CR, qscicr); + +#ifdef CYGOPT_IO_SERIAL_SUPPORT_LINE_STATUS + if(scsr & MPC555_SERIAL_SCxSR_FE){ + stat.which = CYGNUM_SERIAL_STATUS_FRAMEERR; + (chan->callbacks->indicate_status)(chan, &stat); + } + if(scsr & MPC555_SERIAL_SCxSR_PF){ + stat.which = CYGNUM_SERIAL_STATUS_PARITYERR; + (chan->callbacks->indicate_status)(chan, &stat); + } +#endif + } + } + if(QTHF){ + qscisr &= ~((cyg_uint16)CYGARC_REG_IMM_QSCI1SR_QTHF); + HAL_WRITE_UINT16(CYGARC_REG_IMM_QSCI1SR, qscisr); + //cyg_drv_interrupt_unmask(mpc555_chan->rx_interrupt_queue_top_full_num); + } + if(QBHF){ + qscisr &= ~((cyg_uint16)CYGARC_REG_IMM_QSCI1SR_QBHF); + HAL_WRITE_UINT16(CYGARC_REG_IMM_QSCI1SR, qscisr); + //cyg_drv_interrupt_unmask(mpc555_chan->rx_interrupt_queue_bot_full_num); + } + if(idle){ + if(idle && !space_req){ + // The IDLE flag can be set sometimes when RE is set + // so a read of scrq is needed to clear it. + // If this occurs there should be no new data yet otherwise the + // condition is impossible to detect + HAL_READ_UINT16(CYGARC_REG_IMM_SCRQ, scrq); + } + HAL_READ_UINT16(CYGARC_REG_IMM_SCRQ, scrq); + //cyg_drv_interrupt_unmask(mpc555_chan->rx_interrupt_idle_line_num); + } + // A bit lasy, but we don't know or care what the original ISR source + // was so to cover all bases re-enble them all + cyg_drv_interrupt_unmask(mpc555_chan->rx_interrupt_queue_top_full_num); + cyg_drv_interrupt_unmask(mpc555_chan->rx_interrupt_queue_bot_full_num); + cyg_drv_interrupt_unmask(mpc555_chan->rx_interrupt_idle_line_num); +} + +static int mpc555_serial_read_queue(serial_channel* chan, int start, int end) +{ + int block_index = 0; + cyg_uint16 scrq; + cyg_addrword_t i; + unsigned char* space; + int space_avail = 0; + int space_req = end - start + 1; + if((space_req > 0) && + ((chan->callbacks->data_rcv_req) + (chan, space_req, &space_avail, &space) == CYG_RCV_OK)) { + CYG_ASSERT((start >= 0) && (start < 16),"rx queue read start point out of range"); + CYG_ASSERT(start <= end,"rx queue read start and end points reversed"); + for(i=start ;i < (start + space_avail); i++){ + CYG_ASSERT((i >= 0) && (i < 16),"rx queue read out of range"); + HAL_READ_UINT16(CYGARC_REG_IMM_SCRQ + (i * 2), scrq); + space[block_index] = scrq; + ++block_index; + } + (chan->callbacks->data_rcv_done)(chan,space_avail); +#ifdef CYGOPT_IO_SERIAL_SUPPORT_LINE_STATUS +// If there's not enough room data will be lost. +// There's no point calling rcv_char because the reader is blocked by this DSR. + if(space_avail < space_req){ + cyg_serial_line_status_t stat; + stat.which = CYGNUM_SERIAL_STATUS_OVERRUNERR; + (chan->callbacks->indicate_status)(chan, &stat); + } +#endif + } + return space_req; +} +#endif // SERIAL_A +#endif // CYGPKG_IO_SERIAL_POWERPC_MPC555 + +// EOF mpc555_serial_with_ints.c + |