diff options
Diffstat (limited to 'ecos/packages/devs/serial/arm/at91/current/src/at91_serial.c')
-rw-r--r-- | ecos/packages/devs/serial/arm/at91/current/src/at91_serial.c | 650 |
1 files changed, 650 insertions, 0 deletions
diff --git a/ecos/packages/devs/serial/arm/at91/current/src/at91_serial.c b/ecos/packages/devs/serial/arm/at91/current/src/at91_serial.c new file mode 100644 index 0000000..26b0d14 --- /dev/null +++ b/ecos/packages/devs/serial/arm/at91/current/src/at91_serial.c @@ -0,0 +1,650 @@ +//========================================================================== +// +// devs/serial/arm/at91/at91_serial.c +// +// Atmel AT91/EB40 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): gthomas +// Contributors: gthomas, tkoeller, sblock +// Date: 2001-07-24 +// Purpose: Atmel AT91/EB40 Serial I/O module (interrupt driven version) +// Description: +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#include <pkgconf/hal.h> +#include <pkgconf/infra.h> +#include <pkgconf/system.h> +#include <pkgconf/io_serial.h> +#include <pkgconf/io.h> +#include <pkgconf/kernel.h> + +#include <cyg/io/io.h> +#include <cyg/hal/hal_io.h> +#include <cyg/hal/hal_intr.h> +#include <cyg/io/devtab.h> +#include <cyg/io/serial.h> +#include <cyg/infra/diag.h> +#include <cyg/infra/cyg_type.h> +#include <cyg/infra/cyg_ass.h> + +externC void * memcpy( void *, const void *, size_t ); + +#ifdef CYGPKG_IO_SERIAL_ARM_AT91 + +#include "at91_serial.h" + +#define RCVBUF_EXTRA 16 +#define RCV_TIMEOUT 10 + +#define SIFLG_NONE 0x00 +#define SIFLG_TX_READY 0x01 +#define SIFLG_XMIT_BUSY 0x02 +#define SIFLG_XMIT_CONTINUE 0x04 + +typedef struct at91_serial_info { + CYG_ADDRWORD base; + CYG_WORD int_num; + CYG_WORD stat; + int transmit_size; + cyg_interrupt serial_interrupt; + cyg_handle_t serial_interrupt_handle; + cyg_uint8 *rcv_buffer[2]; + cyg_uint16 rcv_chunk_size; + cyg_uint8 curbuf; + cyg_uint8 flags; +} at91_serial_info; + +static bool at91_serial_init(struct cyg_devtab_entry *tab); +static bool at91_serial_putc_interrupt(serial_channel *chan, unsigned char c); +#if (defined(CYGPKG_IO_SERIAL_ARM_AT91_SERIAL0) && CYGNUM_IO_SERIAL_ARM_AT91_SERIAL0_BUFSIZE == 0) \ + || (defined(CYGPKG_IO_SERIAL_ARM_AT91_SERIAL1) && CYGNUM_IO_SERIAL_ARM_AT91_SERIAL1_BUFSIZE == 0) \ + || (defined(CYGPKG_IO_SERIAL_ARM_AT91_SERIAL2) && CYGNUM_IO_SERIAL_ARM_AT91_SERIAL2_BUFSIZE == 0) +static bool at91_serial_putc_polled(serial_channel *chan, unsigned char c); +#endif +static Cyg_ErrNo at91_serial_lookup(struct cyg_devtab_entry **tab, + struct cyg_devtab_entry *sub_tab, + const char *name); +static unsigned char at91_serial_getc_interrupt(serial_channel *chan); +#if (defined(CYGPKG_IO_SERIAL_ARM_AT91_SERIAL0) && CYGNUM_IO_SERIAL_ARM_AT91_SERIAL0_BUFSIZE == 0) \ + || (defined(CYGPKG_IO_SERIAL_ARM_AT91_SERIAL1) && CYGNUM_IO_SERIAL_ARM_AT91_SERIAL1_BUFSIZE == 0) \ + || (defined(CYGPKG_IO_SERIAL_ARM_AT91_SERIAL2) && CYGNUM_IO_SERIAL_ARM_AT91_SERIAL2_BUFSIZE == 0) +static unsigned char at91_serial_getc_polled(serial_channel *chan); +#endif +static Cyg_ErrNo at91_serial_set_config(serial_channel *chan, cyg_uint32 key, + const void *xbuf, cyg_uint32 *len); +static void at91_serial_start_xmit(serial_channel *chan); +static void at91_serial_stop_xmit(serial_channel *chan); + +static cyg_uint32 at91_serial_ISR(cyg_vector_t vector, cyg_addrword_t data); +static void at91_serial_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data); + +#if (defined(CYGPKG_IO_SERIAL_ARM_AT91_SERIAL0) && CYGNUM_IO_SERIAL_ARM_AT91_SERIAL0_BUFSIZE > 0) \ + || (defined(CYGPKG_IO_SERIAL_ARM_AT91_SERIAL1) && CYGNUM_IO_SERIAL_ARM_AT91_SERIAL1_BUFSIZE > 0) \ + || (defined(CYGPKG_IO_SERIAL_ARM_AT91_SERIAL2) && CYGNUM_IO_SERIAL_ARM_AT91_SERIAL2_BUFSIZE > 0) +static SERIAL_FUNS(at91_serial_funs_interrupt, + at91_serial_putc_interrupt, + at91_serial_getc_interrupt, + at91_serial_set_config, + at91_serial_start_xmit, + at91_serial_stop_xmit + ); +#endif + +#if (defined(CYGPKG_IO_SERIAL_ARM_AT91_SERIAL0) && CYGNUM_IO_SERIAL_ARM_AT91_SERIAL0_BUFSIZE == 0) \ + || (defined(CYGPKG_IO_SERIAL_ARM_AT91_SERIAL1) && CYGNUM_IO_SERIAL_ARM_AT91_SERIAL1_BUFSIZE == 0) \ + || (defined(CYGPKG_IO_SERIAL_ARM_AT91_SERIAL2) && CYGNUM_IO_SERIAL_ARM_AT91_SERIAL2_BUFSIZE == 0) +static SERIAL_FUNS(at91_serial_funs_polled, + at91_serial_putc_polled, + at91_serial_getc_polled, + at91_serial_set_config, + at91_serial_start_xmit, + at91_serial_stop_xmit + ); +#endif + +#ifdef CYGPKG_IO_SERIAL_ARM_AT91_SERIAL0 +static cyg_uint8 at91_serial_rcv_buffer_0 + [2][CYGNUM_IO_SERIAL_ARM_AT91_SERIAL0_RCV_CHUNK_SIZE + RCVBUF_EXTRA]; +static at91_serial_info at91_serial_info0 = { + base : (CYG_ADDRWORD) AT91_USART0, + int_num : CYGNUM_HAL_INTERRUPT_USART0, + rcv_chunk_size : CYGNUM_IO_SERIAL_ARM_AT91_SERIAL0_RCV_CHUNK_SIZE, + rcv_buffer : {at91_serial_rcv_buffer_0[0], at91_serial_rcv_buffer_0[1]} +}; + +#if CYGNUM_IO_SERIAL_ARM_AT91_SERIAL0_BUFSIZE > 0 +static unsigned char at91_serial_out_buf0[CYGNUM_IO_SERIAL_ARM_AT91_SERIAL0_BUFSIZE]; +static unsigned char at91_serial_in_buf0[CYGNUM_IO_SERIAL_ARM_AT91_SERIAL0_BUFSIZE]; + +static SERIAL_CHANNEL_USING_INTERRUPTS(at91_serial_channel0, + at91_serial_funs_interrupt, + at91_serial_info0, + CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_ARM_AT91_SERIAL0_BAUD), + CYG_SERIAL_STOP_DEFAULT, + CYG_SERIAL_PARITY_DEFAULT, + CYG_SERIAL_WORD_LENGTH_DEFAULT, + CYG_SERIAL_FLAGS_DEFAULT, + &at91_serial_out_buf0[0], sizeof(at91_serial_out_buf0), + &at91_serial_in_buf0[0], sizeof(at91_serial_in_buf0) + ); +#else +static SERIAL_CHANNEL(at91_serial_channel0, + at91_serial_funs_polled, + at91_serial_info0, + CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_ARM_AT91_SERIAL0_BAUD), + CYG_SERIAL_STOP_DEFAULT, + CYG_SERIAL_PARITY_DEFAULT, + CYG_SERIAL_WORD_LENGTH_DEFAULT, + CYG_SERIAL_FLAGS_DEFAULT + ); +#endif + +DEVTAB_ENTRY(at91_serial_io0, + CYGDAT_IO_SERIAL_ARM_AT91_SERIAL0_NAME, + 0, // Does not depend on a lower level interface + &cyg_io_serial_devio, + at91_serial_init, + at91_serial_lookup, // Serial driver may need initializing + &at91_serial_channel0 + ); +#endif // CYGPKG_IO_SERIAL_ARM_AT91_SERIAL1 + +#ifdef CYGPKG_IO_SERIAL_ARM_AT91_SERIAL1 +static cyg_uint8 at91_serial_rcv_buffer_1 + [2][CYGNUM_IO_SERIAL_ARM_AT91_SERIAL1_RCV_CHUNK_SIZE + RCVBUF_EXTRA]; +static at91_serial_info at91_serial_info1 = { + base : (CYG_ADDRWORD) AT91_USART1, + int_num : CYGNUM_HAL_INTERRUPT_USART1, + rcv_chunk_size : CYGNUM_IO_SERIAL_ARM_AT91_SERIAL1_RCV_CHUNK_SIZE, + rcv_buffer : {at91_serial_rcv_buffer_1[0], at91_serial_rcv_buffer_1[1]} +}; +#if CYGNUM_IO_SERIAL_ARM_AT91_SERIAL1_BUFSIZE > 0 +static unsigned char at91_serial_out_buf1[CYGNUM_IO_SERIAL_ARM_AT91_SERIAL1_BUFSIZE]; +static unsigned char at91_serial_in_buf1[CYGNUM_IO_SERIAL_ARM_AT91_SERIAL1_BUFSIZE]; + +static SERIAL_CHANNEL_USING_INTERRUPTS(at91_serial_channel1, + at91_serial_funs_interrupt, + at91_serial_info1, + CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_ARM_AT91_SERIAL1_BAUD), + CYG_SERIAL_STOP_DEFAULT, + CYG_SERIAL_PARITY_DEFAULT, + CYG_SERIAL_WORD_LENGTH_DEFAULT, + CYG_SERIAL_FLAGS_DEFAULT, + &at91_serial_out_buf1[0], sizeof(at91_serial_out_buf1), + &at91_serial_in_buf1[0], sizeof(at91_serial_in_buf1) + ); +#else +static SERIAL_CHANNEL(at91_serial_channel1, + at91_serial_funs_polled, + at91_serial_info1, + CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_ARM_AT91_SERIAL1_BAUD), + CYG_SERIAL_STOP_DEFAULT, + CYG_SERIAL_PARITY_DEFAULT, + CYG_SERIAL_WORD_LENGTH_DEFAULT, + CYG_SERIAL_FLAGS_DEFAULT + ); +#endif + +DEVTAB_ENTRY(at91_serial_io1, + CYGDAT_IO_SERIAL_ARM_AT91_SERIAL1_NAME, + 0, // Does not depend on a lower level interface + &cyg_io_serial_devio, + at91_serial_init, + at91_serial_lookup, // Serial driver may need initializing + &at91_serial_channel1 + ); +#endif // CYGPKG_IO_SERIAL_ARM_AT91_SERIAL1 + + +#ifdef CYGPKG_IO_SERIAL_ARM_AT91_SERIAL2 + +static cyg_uint8 at91_serial_rcv_buffer_2 + [2][CYGNUM_IO_SERIAL_ARM_AT91_SERIAL2_RCV_CHUNK_SIZE + RCVBUF_EXTRA]; +static at91_serial_info at91_serial_info2 = { + base : (CYG_ADDRWORD) AT91_USART2, + int_num : CYGNUM_HAL_INTERRUPT_USART2, + rcv_chunk_size : CYGNUM_IO_SERIAL_ARM_AT91_SERIAL2_RCV_CHUNK_SIZE, + rcv_buffer : {at91_serial_rcv_buffer_2[0], at91_serial_rcv_buffer_2[1]} +}; + +#if CYGNUM_IO_SERIAL_ARM_AT91_SERIAL2_BUFSIZE > 0 +static unsigned char at91_serial_out_buf2[CYGNUM_IO_SERIAL_ARM_AT91_SERIAL2_BUFSIZE]; +static unsigned char at91_serial_in_buf2[CYGNUM_IO_SERIAL_ARM_AT91_SERIAL2_BUFSIZE]; + +static SERIAL_CHANNEL_USING_INTERRUPTS(at91_serial_channel2, + at91_serial_funs_interrupt, + at91_serial_info2, + CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_ARM_AT91_SERIAL2_BAUD), + CYG_SERIAL_STOP_DEFAULT, + CYG_SERIAL_PARITY_DEFAULT, + CYG_SERIAL_WORD_LENGTH_DEFAULT, + CYG_SERIAL_FLAGS_DEFAULT, + &at91_serial_out_buf2[0], sizeof(at91_serial_out_buf2), + &at91_serial_in_buf2[0], sizeof(at91_serial_in_buf2) + ); +#else +static SERIAL_CHANNEL(at91_serial_channel2, + at91_serial_funs_polled, + at91_serial_info2, + CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_ARM_AT91_SERIAL2_BAUD), + CYG_SERIAL_STOP_DEFAULT, + CYG_SERIAL_PARITY_DEFAULT, + CYG_SERIAL_WORD_LENGTH_DEFAULT, + CYG_SERIAL_FLAGS_DEFAULT + ); +#endif + +DEVTAB_ENTRY(at91_serial_io2, + CYGDAT_IO_SERIAL_ARM_AT91_SERIAL2_NAME, + 0, // Does not depend on a lower level interface + &cyg_io_serial_devio, + at91_serial_init, + at91_serial_lookup, // Serial driver may need initializing + &at91_serial_channel2 + ); +#endif // CYGPKG_IO_SERIAL_ARM_AT91_SERIAL2 + + +// Internal function to actually configure the hardware to desired baud rate, etc. +static bool +at91_serial_config_port(serial_channel *chan, cyg_serial_info_t *new_config, bool init) +{ + at91_serial_info * const at91_chan = (at91_serial_info *)chan->dev_priv; + const CYG_ADDRWORD base = at91_chan->base; + cyg_uint32 parity = select_parity[new_config->parity]; + cyg_uint32 word_length = select_word_length[new_config->word_length-CYGNUM_SERIAL_WORD_LENGTH_5]; + cyg_uint32 stop_bits = select_stop_bits[new_config->stop]; + + if ((word_length == 0xFF) || + (parity == 0xFF) || + (stop_bits == 0xFF)) { + return false; // Unsupported configuration + } + + // Reset device + HAL_WRITE_UINT32(base + AT91_US_CR, AT91_US_CR_RxRESET | AT91_US_CR_TxRESET); + + // Configuration + HAL_WRITE_UINT32(base + AT91_US_MR, parity | word_length | stop_bits); + + // Baud rate + HAL_WRITE_UINT32(base + AT91_US_BRG, AT91_US_BAUD(select_baud[new_config->baud])); + + // Disable all interrupts + HAL_WRITE_UINT32(base + AT91_US_IDR, 0xFFFFFFFF); + + // Start receiver + at91_chan->curbuf = 0; + HAL_WRITE_UINT32(base + AT91_US_RPR, (CYG_ADDRESS) at91_chan->rcv_buffer[0]); + HAL_WRITE_UINT32(base + AT91_US_RTO, RCV_TIMEOUT); + HAL_WRITE_UINT32(base + AT91_US_IER, AT91_US_IER_ENDRX | AT91_US_IER_TIMEOUT); + HAL_WRITE_UINT32(base + AT91_US_RCR, at91_chan->rcv_chunk_size); + + // Enable RX and TX + HAL_WRITE_UINT32( + base + AT91_US_CR, + AT91_US_CR_RxENAB | AT91_US_CR_TxENAB | AT91_US_CR_RSTATUS | AT91_US_CR_STTTO + ); + + // Enable the DMA is the control register exists +#ifdef AT91_US_PTCR + HAL_WRITE_UINT32(base + AT91_US_PTCR, + AT91_US_PTCR_RXTEN | + AT91_US_PTCR_TXTEN); +#endif + + if (new_config != &chan->config) { + chan->config = *new_config; + } + + return true; +} + +// Function to initialize the device. Called at bootstrap time. +static bool +at91_serial_init(struct cyg_devtab_entry *tab) +{ + serial_channel * const chan = (serial_channel *) tab->priv; + at91_serial_info * const at91_chan = (at91_serial_info *) chan->dev_priv; + int res; + +#ifdef CYGDBG_IO_INIT + diag_printf("AT91 SERIAL init - dev: %x.%d\n", at91_chan->base, at91_chan->int_num); +#endif + at91_chan->curbuf = 0; + at91_chan->flags = SIFLG_NONE; + at91_chan->stat = 0; + (chan->callbacks->serial_init)(chan); // Really only required for interrupt driven devices + if (chan->out_cbuf.len != 0) { + cyg_drv_interrupt_create(at91_chan->int_num, + 4, // Priority + (cyg_addrword_t)chan, // Data item passed to interrupt handler + at91_serial_ISR, + at91_serial_DSR, + &at91_chan->serial_interrupt_handle, + &at91_chan->serial_interrupt); + cyg_drv_interrupt_attach(at91_chan->serial_interrupt_handle); + cyg_drv_interrupt_unmask(at91_chan->int_num); + } + res = at91_serial_config_port(chan, &chan->config, true); + return res; +} + +// This routine is called when the device is "looked" up (i.e. attached) +static Cyg_ErrNo +at91_serial_lookup(struct cyg_devtab_entry **tab, + struct cyg_devtab_entry *sub_tab, + const char *name) +{ + serial_channel * const chan = (serial_channel *) (*tab)->priv; + +#ifdef AT91_PMC_PCER + // Enable the peripheral clock to the device + at91_serial_info * const at91_chan = (at91_serial_info *)chan->dev_priv; + HAL_WRITE_UINT32(AT91_PMC+AT91_PMC_PCER, + (1 << at91_chan->int_num)); +#endif + (chan->callbacks->serial_init)(chan); // Really only required for interrupt driven devices + return ENOERR; +} + +// Send a character to the device output buffer. +// Return 'true' if character is sent to device +static bool +at91_serial_putc_interrupt(serial_channel *chan, unsigned char c) +{ + at91_serial_info * const at91_chan = (at91_serial_info *) chan->dev_priv; + const bool res = (at91_chan->flags & SIFLG_XMIT_BUSY) == 0; + + if (res) { + const CYG_ADDRWORD base = at91_chan->base; + HAL_WRITE_UINT32(base + AT91_US_THR, c); + at91_chan->flags |= SIFLG_XMIT_BUSY; + } + return res; +} + +#if (defined(CYGPKG_IO_SERIAL_ARM_AT91_SERIAL0) && CYGNUM_IO_SERIAL_ARM_AT91_SERIAL0_BUFSIZE == 0) \ + || (defined(CYGPKG_IO_SERIAL_ARM_AT91_SERIAL1) && CYGNUM_IO_SERIAL_ARM_AT91_SERIAL1_BUFSIZE == 0) \ + || (defined(CYGPKG_IO_SERIAL_ARM_AT91_SERIAL2) && CYGNUM_IO_SERIAL_ARM_AT91_SERIAL2_BUFSIZE == 0) +static bool +at91_serial_putc_polled(serial_channel *chan, unsigned char c) +{ + at91_serial_info * const at91_chan = (at91_serial_info *) chan->dev_priv; + const CYG_ADDRWORD base = at91_chan->base; + CYG_WORD32 w; + + while (HAL_READ_UINT32(base + AT91_US_CSR, w), (w & AT91_US_IER_TxRDY) == 0) + CYG_EMPTY_STATEMENT; + HAL_WRITE_UINT32(base + AT91_US_THR, c); + return true; +} +#endif + +// Fetch a character from the device input buffer, waiting if necessary +static unsigned char +at91_serial_getc_interrupt(serial_channel *chan) +{ + at91_serial_info * const at91_chan = (at91_serial_info *) chan->dev_priv; + const CYG_ADDRWORD base = at91_chan->base; + CYG_WORD32 c; + + // Read data + HAL_READ_UINT32(base + AT91_US_RHR, c); + return (unsigned char) c; +} + +#if (defined(CYGPKG_IO_SERIAL_ARM_AT91_SERIAL0) && CYGNUM_IO_SERIAL_ARM_AT91_SERIAL0_BUFSIZE == 0) \ + || (defined(CYGPKG_IO_SERIAL_ARM_AT91_SERIAL1) && CYGNUM_IO_SERIAL_ARM_AT91_SERIAL1_BUFSIZE == 0) \ + || (defined(CYGPKG_IO_SERIAL_ARM_AT91_SERIAL2) && CYGNUM_IO_SERIAL_ARM_AT91_SERIAL2_BUFSIZE == 0) +static unsigned char +at91_serial_getc_polled(serial_channel *chan) +{ + at91_serial_info * const at91_chan = (at91_serial_info *) chan->dev_priv; + const CYG_ADDRWORD base = at91_chan->base; + CYG_WORD32 c; + + while (HAL_READ_UINT32(base + AT91_US_CSR, c), (c & AT91_US_IER_RxRDY) == 0) + CYG_EMPTY_STATEMENT; + // Read data + HAL_READ_UINT32(base + AT91_US_RHR, c); + return (unsigned char) c; +} +#endif +// Set up the device characteristics; baud rate, etc. +static Cyg_ErrNo +at91_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 != at91_serial_config_port(chan, config, false) ) + return -EINVAL; + } + break; + default: + return -EINVAL; + } + return ENOERR; +} + +// Enable the transmitter on the device +static void +at91_serial_start_xmit(serial_channel *chan) +{ + at91_serial_info * const at91_chan = (at91_serial_info *) chan->dev_priv; + const CYG_ADDRWORD base = at91_chan->base; + unsigned char * chars; + xmt_req_reply_t res; + + cyg_drv_dsr_lock(); + if ((at91_chan->flags & SIFLG_XMIT_CONTINUE) == 0) { + res = (chan->callbacks->data_xmt_req)(chan, 0xffff, &at91_chan->transmit_size, &chars); + switch (res) + { + case CYG_XMT_OK: + HAL_WRITE_UINT32(base + AT91_US_TPR, (CYG_WORD32) chars); + HAL_WRITE_UINT32(base + AT91_US_TCR, at91_chan->transmit_size); + at91_chan->flags |= SIFLG_XMIT_CONTINUE; + HAL_WRITE_UINT32(base + AT91_US_IER, AT91_US_IER_ENDTX); + break; + case CYG_XMT_DISABLED: + (chan->callbacks->xmt_char)(chan); // Kick transmitter + at91_chan->flags |= SIFLG_XMIT_CONTINUE; + HAL_WRITE_UINT32(base + AT91_US_IER, AT91_US_IER_TxRDY); + break; + default: + // No data or unknown error - can't do anything about it + break; + } + } + cyg_drv_dsr_unlock(); +} + +// Disable the transmitter on the device +static void +at91_serial_stop_xmit(serial_channel *chan) +{ + at91_serial_info * const at91_chan = (at91_serial_info *) chan->dev_priv; + const CYG_ADDRWORD base = at91_chan->base; + HAL_WRITE_UINT32(base + AT91_US_IDR, AT91_US_IER_TxRDY | AT91_US_IER_ENDTX); + at91_chan->flags &= ~SIFLG_XMIT_CONTINUE; +} + +// Serial I/O - low level interrupt handler (ISR) +static cyg_uint32 +at91_serial_ISR(cyg_vector_t vector, cyg_addrword_t data) +{ + serial_channel * const chan = (serial_channel *) data; + at91_serial_info * const at91_chan = (at91_serial_info *) chan->dev_priv; + const CYG_ADDRWORD base = at91_chan->base; + CYG_WORD32 stat, mask; + cyg_uint32 retcode = 0; + + HAL_READ_UINT32(base + AT91_US_CSR, stat); + HAL_READ_UINT32(base + AT91_US_IMR, mask); + stat &= mask; + + if (stat & (AT91_US_IER_ENDRX | AT91_US_IER_TIMEOUT)) { + cyg_uint32 x; + HAL_WRITE_UINT32(base + AT91_US_IDR, AT91_US_IER_ENDRX | AT91_US_IER_TIMEOUT); + HAL_WRITE_UINT32(base + AT91_US_RCR, 0); + HAL_WRITE_UINT32(base + AT91_US_RTO, 0); + HAL_READ_UINT32(base + AT91_US_RPR, x); + HAL_WRITE_UINT32( + base + AT91_US_RCR, + (CYG_ADDRESS) at91_chan->rcv_buffer[at91_chan->curbuf] + + at91_chan->rcv_chunk_size + RCVBUF_EXTRA - x + ); + retcode = CYG_ISR_CALL_DSR; + } + + if (stat & (AT91_US_IER_TxRDY | AT91_US_IER_ENDTX)) { + HAL_WRITE_UINT32(base + AT91_US_IDR, AT91_US_IER_TxRDY | AT91_US_IER_ENDTX); + retcode = CYG_ISR_CALL_DSR; + } + at91_chan->stat |= stat; + + cyg_drv_interrupt_acknowledge(vector); + return retcode; +} + +// Serial I/O - high level interrupt handler (DSR) +static void +at91_serial_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data) +{ + serial_channel * const chan = (serial_channel *) data; + at91_serial_info * const at91_chan = (at91_serial_info *) chan->dev_priv; + const CYG_ADDRWORD base = at91_chan->base; + CYG_WORD32 stat; + + cyg_drv_interrupt_mask(vector); + stat = at91_chan->stat; + at91_chan->stat = 0; + cyg_drv_interrupt_unmask(vector); + + if (stat & (AT91_US_IER_ENDRX | AT91_US_IER_TIMEOUT)) { + const cyg_uint8 cb = at91_chan->curbuf, nb = cb ^ 0x01; + const cyg_uint8 * p = at91_chan->rcv_buffer[cb], * end; + cyg_uint32 temp_word; + + at91_chan->curbuf = nb; + HAL_WRITE_UINT32(base + AT91_US_RCR, 0); + HAL_READ_UINT32(base + AT91_US_RPR, temp_word); + end = (const cyg_uint8 *)temp_word; + HAL_WRITE_UINT32(base + AT91_US_RTO, RCV_TIMEOUT); + HAL_WRITE_UINT32(base + AT91_US_CR, AT91_US_CR_RSTATUS | AT91_US_CR_STTTO); + HAL_WRITE_UINT32(base + AT91_US_RPR, (CYG_ADDRESS) at91_chan->rcv_buffer[nb]); + HAL_WRITE_UINT32(base + AT91_US_RCR, at91_chan->rcv_chunk_size); + HAL_WRITE_UINT32( + base + AT91_US_IER, + AT91_US_IER_ENDRX | AT91_US_IER_TIMEOUT + ); + + while (p < end) { + rcv_req_reply_t res; + int space_avail; + unsigned char *space; + + res = (chan->callbacks->data_rcv_req)( + chan, + end - at91_chan->rcv_buffer[cb], + &space_avail, + &space + ); + + switch (res) + { + case CYG_RCV_OK: + memcpy(space, p, space_avail); + (chan->callbacks->data_rcv_done)(chan, space_avail); + p += space_avail; + break; + case CYG_RCV_DISABLED: + (chan->callbacks->rcv_char)(chan, *p++); + break; + default: + // Buffer full or unknown error, can't do anything about it + // Discard data + p = end; + break; + } + } + } + + if (stat & AT91_US_IER_TxRDY) { + at91_chan->flags &= ~SIFLG_XMIT_BUSY; + (chan->callbacks->xmt_char)(chan); + if (at91_chan->flags & SIFLG_XMIT_CONTINUE) + HAL_WRITE_UINT32(base + AT91_US_IER, AT91_US_IER_TxRDY); + } + + if (stat & AT91_US_IER_ENDTX) { + (chan->callbacks->data_xmt_done)(chan, at91_chan->transmit_size); + if (at91_chan->flags & SIFLG_XMIT_CONTINUE) { + unsigned char * chars; + xmt_req_reply_t res; + + res = (chan->callbacks->data_xmt_req)(chan, 0xffff, &at91_chan->transmit_size, &chars); + + switch (res) + { + case CYG_XMT_OK: + HAL_WRITE_UINT32(base + AT91_US_TPR, (CYG_WORD32) chars); + HAL_WRITE_UINT32(base + AT91_US_TCR, at91_chan->transmit_size); + at91_chan->flags |= SIFLG_XMIT_CONTINUE; + HAL_WRITE_UINT32(base + AT91_US_IER, AT91_US_IER_ENDTX); + break; + default: + // No data or unknown error - can't do anything about it + // CYG_XMT_DISABLED should not happen here! + break; + } + } + } +} +#endif |