* Re: mpc555 serial driver hardware queue support
2008-09-02 17:30 mpc555 serial driver hardware queue support Steven Clugston
@ 2008-12-23 17:08 ` Andrew Lunn
0 siblings, 0 replies; 3+ messages in thread
From: Andrew Lunn @ 2008-12-23 17:08 UTC (permalink / raw)
To: Steven Clugston; +Cc: ecos-patches
[-- Attachment #1: Type: text/plain, Size: 2736 bytes --]
On Tue, Sep 02, 2008 at 06:25:54PM +0100, Steven Clugston wrote:
> Hi
>
> There is a problem/shortcoming of the MPC555 serial driver that I've
> discussed previously on the list that in its current implementation it
> cannot cope with continuous concurrent reception and transmission of
> characters as with the serial_echo.c test program. If a continuous
> stream of characters are sent from a PC with something like a 16x5x chip
> or superIO chip, then characters are almost immediately lost as the Rx
> DSR is not run in time. I have added a sort of software circular buffer
> between the ISR and DSR to allow a nominal number (say 256) of
> characters to be buffered. This fixes or reduces the problem for
> applications which require a serial packet of less than the buffer size
> to be received, provided there is a rest period between packets.
>
> For a continuous data stream, this buffer will eventually overflow no
> matter how big it is made, so I have also separately implemented support
> for the hardware serial queue facility on the mpc555. Unfortunately this
> is only available on the first serial port (SCI1) on this chip (newer
> 5xx chips have it on both). It is available as a cdl option so that it
> can be disabled and the old behaviour is restored. There are two
> 16-character buffers or queues, one for Rx and one for Tx. There are
> half empty/full interrupts and full/empty interrupts as well as an Rx
> line idle interrupt to flush the buffer/queue when there is a gap or
> absence of incoming data.
>
> I tried various methods for implementing this including separate DSRs
> for the interrupt sources, a combined DSR that deals with each interrupt
> source, but only reliable way I could get it to work is by using a
> combined DSR which doesn't care what the actual ISR source was. The
> trouble is that the status information is often lost by the time the DSR
> is reached as reading from certain registers clears the contents. Also
> DSRs seem to be run in the opposite order to which they are posted which
> makes it even harder to work out what part of the queue to read/write.
> The final implementation is perhaps the least elegant but nonetheless
> works. It just reads/writes whatever data is available at the time of
> the DSR running and subsequent DSR instances that have been posted just
> drop through if there is no data left to read by the time they run.
>
> I've tested this extensively but perhaps not exhaustively (all of the
> type of parity/noise errors etc not fully tested as its perhaps
> difficult to contrive these).
This patch has been going back and forth for a while, and has now
reached the state it can be committed. Attached is what has been
committed.
Andrew
[-- Attachment #2: devs_serial_mpc555.diff --]
[-- Type: text/x-diff, Size: 65815 bytes --]
Index: devs/serial/powerpc/mpc555/current/ChangeLog
===================================================================
RCS file: /cvs/ecos/ecos/packages/devs/serial/powerpc/mpc555/current/ChangeLog,v
retrieving revision 1.2
diff -u -r1.2 ChangeLog
--- devs/serial/powerpc/mpc555/current/ChangeLog 2 Sep 2008 05:56:02 -0000 1.2
+++ devs/serial/powerpc/mpc555/current/ChangeLog 23 Dec 2008 16:58:04 -0000
@@ -1,3 +1,15 @@
+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
@@ -7,7 +19,7 @@
2008-04-06 Steven Clugston <steven.clugston@ncl.ac.uk>
- * Refactored cme555 package to more generic mpc555
+ * Refactored package to more generic mpc555
2003-02-24 Jonathan Larmour <jifl@eCosCentric.com>
Index: devs/serial/powerpc/mpc555/current/cdl/ser_powerpc_mpc555.cdl
===================================================================
RCS file: /cvs/ecos/ecos/packages/devs/serial/powerpc/mpc555/current/cdl/ser_powerpc_mpc555.cdl,v
retrieving revision 1.2
diff -u -r1.2 ser_powerpc_mpc555.cdl
--- devs/serial/powerpc/mpc555/current/cdl/ser_powerpc_mpc555.cdl 2 Sep 2008 05:56:02 -0000 1.2
+++ devs/serial/powerpc/mpc555/current/cdl/ser_powerpc_mpc555.cdl 23 Dec 2008 16:58:04 -0000
@@ -73,6 +73,7 @@
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."
@@ -105,6 +106,17 @@
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 {
@@ -177,7 +189,6 @@
the set of global flags if present."
}
}
-
}
# EOF ser_powerpc_mpc555.cdl
Index: devs/serial/powerpc/mpc555/current/src/mpc555_serial_with_ints.c
===================================================================
RCS file: /cvs/ecos/ecos/packages/devs/serial/powerpc/mpc555/current/src/mpc555_serial_with_ints.c,v
retrieving revision 1.2
diff -u -r1.2 mpc555_serial_with_ints.c
--- devs/serial/powerpc/mpc555/current/src/mpc555_serial_with_ints.c 2 Sep 2008 05:56:02 -0000 1.2
+++ devs/serial/powerpc/mpc555/current/src/mpc555_serial_with_ints.c 23 Dec 2008 16:58:05 -0000
@@ -60,49 +60,108 @@
#include <cyg/io/serial.h>
// Only build this driver for the MPC555 based boards
-#ifdef CYGPKG_IO_SERIAL_POWERPC_MPC555
+#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; // tells if the transmit interrupt may be re-enabled
- 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_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_init(struct cyg_devtab_entry * tab);
static bool mpc555_serial_putc(serial_channel * chan, unsigned char c);
-static Cyg_ErrNo mpc555_serial_lookup(struct cyg_devtab_entry ** tab,
- struct cyg_devtab_entry * sub_tab,
- const char * name);
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);
+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,
@@ -110,41 +169,100 @@
mpc555_serial_start_xmit,
mpc555_serial_stop_xmit);
-//-------------------
+//------------------------------------------------------------------------------
// Device driver data
-//-------------------
+//------------------------------------------------------------------------------
#ifdef CYGPKG_IO_SERIAL_POWERPC_MPC555_SERIAL_A
-static mpc555_serial_info mpc555_serial_info0 = {MPC555_SERIAL_BASE_A,
- CYGNUM_HAL_INTERRUPT_IMB3_SCI0_TX,
- CYGNUM_HAL_INTERRUPT_IMB3_SCI0_RX,
- CYGNUM_HAL_INTERRUPT_IMB3_SCI0_TX_PRIORITY,
- CYGNUM_HAL_INTERRUPT_IMB3_SCI0_RX_PRIORITY,
- false};
-#if CYGNUM_IO_SERIAL_POWERPC_MPC555_SERIAL_A_BUFSIZE > 0
+#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));
-#else
-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);
+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,
@@ -156,37 +274,55 @@
#endif // ifdef CYGPKG_IO_SERIAL_POWERPC_MPC555_SERIAL_A
#ifdef CYGPKG_IO_SERIAL_POWERPC_MPC555_SERIAL_B
-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};
+
#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));
+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 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);
+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,
@@ -197,90 +333,110 @@
&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
+// 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)
+// 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_SCI0_RX);
-#ifdef CYGPKG_IO_SERIAL_POWERPC_MPC555_SERIAL_A // Do not waist time on unused hardware
- if((status & CYGARC_REG_IMM_SCxSR_TDRE) && (control & CYGARC_REG_IMM_SCCxR1_TIE))
- return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_SCI0_TX);
-// Don't waist time on unused interrupts
-// if((status & CYGARC_REG_IMM_SCxSR_TC) && (control & CYGARC_REG_IMM_SCCxR1_TCIE))
-// return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_SCI0_TXC);
-// Don't waist time on unused interrupts
-// if((status & CYGARC_REG_IMM_SCxSR_IDLE) && (control & CYGARC_REG_IMM_SCCxR1_ILIE))
-// return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_SCI0_IDLE);
-#endif
-
- 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))
+ if((status & CYGARC_REG_IMM_SCxSR_RDRF) &&
+ (control & CYGARC_REG_IMM_SCCxR1_RIE))
return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_SCI1_RX);
-#ifdef CYGPKG_IO_SERIAL_POWERPC_MPC555_SERIAL_B // Do not waist time on unused hardware
- 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
-// 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
-// if((status & CYGARC_REG_IMM_SCxSR_IDLE) && (control & CYGARC_REG_IMM_SCCxR1_ILIE))
-// return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_SCI1_IDLE);
-
-#if 0
- // The driver doesn't use the queue operation of the hardware (It would need different code for serial 1 and 2
- // since oly one port supports queue mode). So the following is not needed.
- // Leave it there. It is easyer for later implementations to remove the comments than finding
- // out how the hardware works again.
+// 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))
+ 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))
+ 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))
+ 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))
+ if((status & CYGARC_REG_IMM_QSCI1SR_QBHE) &&
+ (control & CYGARC_REG_IMM_QSCI1CR_QBHEI))
return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_SCI1_TXQBHE);
-
- cyg_uint16 status;
- cyg_uint16 control;
-
+// 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))
+ 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))
+ 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))
+ 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 actually 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)
+//------------------------------------------------------------------------------
+// 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);
@@ -307,7 +463,8 @@
(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_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];
@@ -334,6 +491,21 @@
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);
@@ -342,8 +514,7 @@
else
sccxr &= ~((cyg_uint16)MPC555_SERIAL_SCCxR1_M);
- switch(new_config->parity)
- {
+ switch(new_config->parity){
case CYGNUM_SERIAL_PARITY_NONE:
sccxr &= ~((cyg_uint16)MPC555_SERIAL_SCCxR1_PE);
break;
@@ -374,14 +545,38 @@
sccxr |= MPC555_SERIAL_SCCxR1_RE;
HAL_WRITE_UINT16(port + MPC555_SERIAL_SCCxR1, sccxr);
- if(init)
- { // enable the receiver interrupt
+ 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
- {
+ 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);
@@ -393,22 +588,20 @@
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)
-{
+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;
- (chan->callbacks->serial_init)(chan); // Really only required for interrupt driven devices
- if(chan->out_cbuf.len != 0)
- {
+ // 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;
@@ -417,50 +610,106 @@
hal_mpc5xx_remove_arbitration_isr(CYGNUM_HAL_ISR_SOURCE_PRIORITY_QSCI);
hal_mpc5xx_install_arbitration_isr(&arbiter);
- // 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 interrupt handler
- 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);
- }
-
+ // 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;
- (chan->callbacks->serial_init)(chan); // Really only required for interrupt driven devices
+ //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)
-{
+//----------------------------------------------------------------------------
+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;
@@ -468,8 +717,8 @@
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
+ 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;
@@ -479,11 +728,10 @@
return false;
}
-//---------------------------------------------------------------------
+//----------------------------------------------------------------------------
// Fetch a character from the device input buffer, waiting if necessary
-//---------------------------------------------------------------------
-static unsigned char mpc555_serial_getc(serial_channel * chan)
-{
+//----------------------------------------------------------------------------
+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;
@@ -502,23 +750,22 @@
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:
- {
+ 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;
+ 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;
- }
+ return -EINVAL;
+ }
break;
default:
return -EINVAL;
@@ -526,37 +773,97 @@
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_num);
+ mpc555_chan->tx_interrupt_enable = true;
- // No need to call xmt_char, this will generate an interrupt immediately.
+ 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)
-{
+//----------------------------------------------------------------------------
+static void mpc555_serial_stop_xmit(serial_channel * chan){
mpc555_serial_info * mpc555_chan = (mpc555_serial_info *)chan->dev_priv;
- cyg_drv_dsr_lock();
- mpc555_chan->tx_interrupt_enable = false;
- cyg_drv_interrupt_mask(mpc555_chan->tx_interrupt_num);
- cyg_drv_dsr_unlock();
+ 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)
-{
+//----------------------------------------------------------------------------
+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;
@@ -566,25 +873,107 @@
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)
-{
+//----------------------------------------------------------------------------
+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)
-{
+//----------------------------------------------------------------------------
+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;
@@ -593,63 +982,307 @@
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)
-{
+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_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
- 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);
-
- if(scsr & (cyg_uint16)MPC555_SERIAL_SCxSR_ERRORS)
- {
+
+ 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)
+ 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);
- }
- if(scsr & MPC555_SERIAL_SCxSR_NF)
- {
+ (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);
}
- 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(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
+ }
}
- else
- {
- (chan->callbacks->rcv_char)(chan, (cyg_uint8)scdr);
+ 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);
}
-
- cyg_drv_interrupt_unmask(mpc555_chan->rx_interrupt_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
+
^ permalink raw reply [flat|nested] 3+ messages in thread