Index: devs/serial/arm/at91//current/cdl/ser_arm_at91.cdl =================================================================== RCS file: /cvs/ecos/ecos/packages/devs/serial/arm/at91/current/cdl/ser_arm_at91.cdl,v retrieving revision 1.5 diff -u -r1.5 ser_arm_at91.cdl --- devs/serial/arm/at91//current/cdl/ser_arm_at91.cdl 24 Feb 2003 14:12:18 -0000 1.5 +++ devs/serial/arm/at91//current/cdl/ser_arm_at91.cdl 21 Oct 2003 16:29:59 -0000 @@ -71,6 +71,9 @@ puts $::cdl_system_header "/***** serial driver proc output end *****/" } + # The driver tries to be effective with FIFO transfers + implements CYGINT_IO_SERIAL_BLOCK_TRANSFER + cdl_component CYGPKG_IO_SERIAL_ARM_AT91_SERIAL0 { display "Atmel AT91 serial port 0 driver" flavor bool Index: devs/serial/arm/at91//current/src/at91_serial.c =================================================================== RCS file: /cvs/ecos/ecos/packages/devs/serial/arm/at91/current/src/at91_serial.c,v retrieving revision 1.6 diff -u -r1.6 at91_serial.c --- devs/serial/arm/at91//current/src/at91_serial.c 16 Oct 2003 18:02:55 -0000 1.6 +++ devs/serial/arm/at91//current/src/at91_serial.c 21 Oct 2003 16:30:01 -0000 @@ -62,15 +62,41 @@ #include #include +#include + #ifdef CYGPKG_IO_SERIAL_ARM_AT91 +#define AT91_UART_BUF_SIZE 1024 +#define RECV_UART_CHAR_TIMEOUT 50 + +#define FLIP_BUFFER_DEF(x) \ + struct flip_buffer { \ + cyg_uint8 buf[2][x]; \ + int buf_which; \ + cyg_uint8 *buf_p; \ + int buf_count; \ + } + + #include "at91_serial.h" +FLIP_BUFFER_DEF(AT91_UART_BUF_SIZE); + typedef struct at91_serial_info { CYG_ADDRWORD base; CYG_WORD int_num; cyg_interrupt serial_interrupt; cyg_handle_t serial_interrupt_handle; + cyg_interrupt line_interrupt; + cyg_handle_t line_interrupt_handle; + struct flip_buffer recv; + cyg_uint8 send_buf[2][AT91_UART_BUF_SIZE]; + int send_buf_which; + int send_buf_count[2]; + int tx_enabled; + cyg_uint32 stat; + cyg_serial_info_t config; + struct flip_buffer *flip; } at91_serial_info; static bool at91_serial_init(struct cyg_devtab_entry *tab); @@ -87,6 +113,7 @@ 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); + static SERIAL_FUNS(at91_serial_funs, at91_serial_putc, at91_serial_getc, @@ -185,12 +212,27 @@ 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]; + at91_chan->flip=&at91_chan->recv; + if ((word_length == 0xFF) || (parity == 0xFF) || (stop_bits == 0xFF)) { return false; // Unsupported configuration } + if (init) { + memset (&at91_chan->config, 0, sizeof (at91_chan->config)); + at91_chan->recv.buf_which = 0; + at91_chan->send_buf_which = 0; + at91_chan->recv.buf_count = 0; + at91_chan->tx_enabled = 0; + at91_chan->send_buf_count[0] = at91_chan->send_buf_count[1] = 0; + } + + if (new_config->word_length != at91_chan->config.word_length || + new_config->parity != at91_chan->config.parity || + new_config->stop != at91_chan->config.stop || + new_config->baud != at91_chan->config.baud) { // Reset device HAL_WRITE_UINT32(base+AT91_US_CR, AT91_US_CR_RxRESET | AT91_US_CR_TxRESET); @@ -200,14 +242,28 @@ // Baud rate HAL_WRITE_UINT32(base+AT91_US_BRG, AT91_US_BAUD(select_baud[new_config->baud])); + // Enable RX and TX + HAL_WRITE_UINT32(base+AT91_US_CR, AT91_US_CR_RxENAB | AT91_US_CR_TxENAB); + at91_chan->config = *new_config; + } + + if (init) { // Disable all interrupts HAL_WRITE_UINT32(base+AT91_US_IDR, 0xFFFFFFFF); - // Enable Rx interrupts - HAL_WRITE_UINT32(base+AT91_US_IER, AT91_US_IER_RxRDY); + // Initialize hardware buffers + HAL_WRITE_UINT32 (base + AT91_US_RPR, (cyg_uint32) at91_chan->recv.buf[at91_chan->recv.buf_which]); + HAL_WRITE_UINT32 (base + AT91_US_RCR, (cyg_uint32) AT91_UART_BUF_SIZE); + HAL_WRITE_UINT32 (base + AT91_US_TCR, (cyg_uint32) 0); + HAL_WRITE_UINT32 (base + AT91_US_TPR, (cyg_uint32) at91_chan->send_buf[at91_chan->send_buf_which]); + + // Enable buffer full and timeout interrupts + HAL_WRITE_UINT32(base+AT91_US_IER, AT91_US_IER_TIMEOUT | AT91_US_IER_ENDRX); + } - // Enable RX and TX - HAL_WRITE_UINT32(base+AT91_US_CR, AT91_US_CR_RxENAB | AT91_US_CR_TxENAB); + // Start timeout + HAL_WRITE_UINT32(base+AT91_US_RTO, RECV_UART_CHAR_TIMEOUT); + HAL_WRITE_UINT32(base+AT91_US_CR, AT91_US_CR_STTTO); if (new_config != &chan->config) { chan->config = *new_config; @@ -224,9 +280,12 @@ at91_serial_info *at91_chan = (at91_serial_info *)chan->dev_priv; int res; + memset(at91_chan->recv.buf, 0x55, sizeof(at91_chan->recv.buf)); + #ifdef CYGDBG_IO_INIT diag_printf("AT91 SERIAL init - dev: %x.%d\n", at91_chan->base, at91_chan->int_num); #endif + (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, @@ -238,6 +297,7 @@ &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; @@ -255,38 +315,62 @@ return ENOERR; } +static void +at91_serial_flip_send_buffer (at91_serial_info * at91_chan) +{ + CYG_ADDRWORD base = at91_chan->base; + cyg_uint32 count; + +// cyg_drv_isr_lock (); + + HAL_READ_UINT32 (base + AT91_US_TCR, count); + if (count > 0) { // transmitter still busy + HAL_WRITE_UINT32 (base + AT91_US_IER, AT91_US_IER_ENDTX); + } else { // transmitter done with old buf +// empty buffer - + at91_chan->send_buf_count[at91_chan->send_buf_which] = 0; +// flip - + at91_chan->send_buf_which = 1 - at91_chan->send_buf_which; + HAL_WRITE_UINT32 (base + AT91_US_TPR, (cyg_uint32) at91_chan->send_buf[at91_chan->send_buf_which]); + HAL_WRITE_UINT32 (base + AT91_US_TCR, + (cyg_uint32) at91_chan->send_buf_count[at91_chan->send_buf_which]); +// enable completion interrupt - + if (at91_chan->send_buf_count[at91_chan->send_buf_which] > 0) + HAL_WRITE_UINT32 (base + AT91_US_IER, AT91_US_IER_ENDTX); + else + HAL_WRITE_UINT32 (base + AT91_US_IDR, AT91_US_IER_ENDTX); + } + +// cyg_drv_isr_unlock (); +} + // Send a character to the device output buffer. // Return 'true' if character is sent to device static bool at91_serial_putc(serial_channel *chan, unsigned char c) { at91_serial_info *at91_chan = (at91_serial_info *)chan->dev_priv; - CYG_ADDRWORD base = at91_chan->base; - cyg_uint32 stat; + int other; - // Check status - HAL_READ_UINT32(base+AT91_US_CSR, stat); + other = 1 - at91_chan->send_buf_which; + if (at91_chan->send_buf_count[other] >= AT91_UART_BUF_SIZE) + return false; + +// append buffer with char + at91_chan->send_buf[other][at91_chan->send_buf_count[other]] = (cyg_uint8) c; + at91_chan->send_buf_count[other]++; + +// flip if needed: + at91_serial_flip_send_buffer (at91_chan); - // Send character if possible - if ((stat & AT91_US_CSR_TxRDY) != 0) { - HAL_WRITE_UINT32(base+AT91_US_THR, c); return true; - } else { - return false; // Couldn't send, tx was busy - } } -// Fetch a character from the device input buffer, waiting if necessary +// we don't support this - its only for bufferless operation: see serial.c static unsigned char at91_serial_getc(serial_channel *chan) { - at91_serial_info *at91_chan = (at91_serial_info *)chan->dev_priv; - CYG_ADDRWORD base = at91_chan->base; - cyg_uint32 c; - - // Read data - HAL_READ_UINT32(base+AT91_US_RHR, c); - return c; + return 0; } // Set up the device characteristics; baud rate, etc. @@ -312,15 +396,27 @@ return ENOERR; } + + + // Enable the transmitter on the device static void at91_serial_start_xmit(serial_channel *chan) { at91_serial_info *at91_chan = (at91_serial_info *)chan->dev_priv; CYG_ADDRWORD base = at91_chan->base; + cyg_uint32 count; + +// cyg_drv_isr_lock (); + + HAL_READ_UINT32 (base + AT91_US_TCR, count); + if (count > 0) + HAL_WRITE_UINT32 (base + AT91_US_IER, AT91_US_IER_ENDTX); + at91_chan->tx_enabled = 1; + +// cyg_drv_isr_unlock (); - (chan->callbacks->xmt_char)(chan); // Kick transmitter (if necessary) - HAL_WRITE_UINT32(base+AT91_US_IER, AT91_US_IER_TxRDY); + (chan->callbacks->xmt_char) (chan); } // Disable the transmitter on the device @@ -328,18 +424,44 @@ at91_serial_stop_xmit(serial_channel *chan) { at91_serial_info *at91_chan = (at91_serial_info *)chan->dev_priv; - CYG_ADDRWORD base = at91_chan->base; - - HAL_WRITE_UINT32(base+AT91_US_IDR, AT91_US_IER_TxRDY); + at91_chan->tx_enabled = 0; } // Serial I/O - low level interrupt handler (ISR) static cyg_uint32 at91_serial_ISR(cyg_vector_t vector, cyg_addrword_t data) { + serial_channel *chan = (serial_channel *) data; + at91_serial_info *at91_chan = (at91_serial_info *) chan->dev_priv; + CYG_ADDRWORD base = at91_chan->base; + cyg_uint32 ipr, imr, count, p; + + // Turn off interrupts and acknowledge the interrupt. This avoids + // lost interrupts and unwanted reentrancy. This problem is not entirely + // theoretical, since transmit and recieve interrupts are asynchronous + // and can happen immediately. cyg_drv_interrupt_mask(vector); cyg_drv_interrupt_acknowledge(vector); - return (CYG_ISR_CALL_DSR|CYG_ISR_HANDLED); // Cause DSR to be run + + HAL_READ_UINT32 (base + AT91_US_CSR, ipr); + HAL_READ_UINT32 (base + AT91_US_IMR, imr); + if ((ipr & imr & (AT91_US_IER_ENDRX | AT91_US_IER_TIMEOUT)) && at91_chan->flip) { + HAL_READ_UINT32 (base + AT91_US_RPR, p); + count = p - (cyg_uint32) at91_chan->flip->buf[at91_chan->flip->buf_which]; + if (count > 0) { // PDC has done work + /* record the location of the bytes read while we were away */ + at91_chan->flip->buf_p = at91_chan->flip->buf[at91_chan->flip->buf_which]; + at91_chan->flip->buf_count = count; + /* flip the buffer */ + at91_chan->flip->buf_which = 1 - at91_chan->flip->buf_which; + HAL_WRITE_UINT32 (base + AT91_US_RPR, (cyg_uint32) at91_chan->flip->buf[at91_chan->flip->buf_which]); + HAL_WRITE_UINT32 (base + AT91_US_RCR, (cyg_uint32) AT91_UART_BUF_SIZE); + } + HAL_WRITE_UINT32 (base + AT91_US_RTO, RECV_UART_CHAR_TIMEOUT); + HAL_WRITE_UINT32 (base + AT91_US_CR, AT91_US_CR_STTTO); + } + + return CYG_ISR_CALL_DSR | CYG_ISR_HANDLED; } // Serial I/O - high level interrupt handler (DSR) @@ -349,24 +471,69 @@ serial_channel *chan = (serial_channel *)data; at91_serial_info *at91_chan = (at91_serial_info *)chan->dev_priv; CYG_ADDRWORD base = at91_chan->base; - cyg_uint32 stat, c; + cyg_uint32 stat, mask; - // Check status - HAL_READ_UINT32(base+AT91_US_IMR, stat); + HAL_READ_UINT32 (base + AT91_US_CSR, stat); + HAL_READ_UINT32 (base + AT91_US_IMR, mask); - if (stat & (AT91_US_IER_TxRDY)) { + // Check status + if ((mask & stat & AT91_US_IER_ENDTX)) { + if (at91_chan->tx_enabled) { + int count, other; + unsigned char *p; + + other = 1 - at91_chan->send_buf_which; + if (0 && CYG_XMT_OK == + (chan->callbacks->data_xmt_req) (chan, AT91_UART_BUF_SIZE - at91_chan->send_buf_count[other], + &count, &p)) { + do { + if (count <= 0) + break; + memcpy (at91_chan->send_buf[other] + at91_chan->send_buf_count[other], p, count); + at91_chan->send_buf_count[other] += count; + + (chan->callbacks->data_xmt_done) (chan, count); + if (!(AT91_UART_BUF_SIZE - at91_chan->send_buf_count[other])) + break; + } while (CYG_RCV_OK == (chan->callbacks->data_xmt_req) (chan, + AT91_UART_BUF_SIZE - + at91_chan->send_buf_count[other], + &count, &p)); + at91_serial_flip_send_buffer (at91_chan); + } else { (chan->callbacks->xmt_char)(chan); + at91_serial_flip_send_buffer (at91_chan); } - if (stat & (AT91_US_IER_RxRDY)) { - while (true) { - HAL_READ_UINT32(base+AT91_US_CSR, stat); - if ((stat & AT91_US_CSR_RxRDY) == 0) { + } else { + at91_serial_flip_send_buffer (at91_chan); + } + } + + if (at91_chan->recv.buf_count > 0) { // chars waiting from ISR + int count; + unsigned char *p; + if (CYG_RCV_OK == (chan->callbacks->data_rcv_req) (chan, at91_chan->recv.buf_count, &count, &p)) { +// block transfer acceptable to upper layer: + do { + memcpy (p, at91_chan->recv.buf_p, count); + at91_chan->recv.buf_p += count; + at91_chan->recv.buf_count -= count; + (chan->callbacks->data_rcv_done) (chan, count); + if (!at91_chan->recv.buf_count) break; + } while (CYG_RCV_OK == + (chan->callbacks->data_rcv_req) (chan, at91_chan->recv.buf_count, &count, &p)); + } else { +// block transfer not acceptable to upper layer: + do { + (chan->callbacks->rcv_char) (chan, (unsigned char) *at91_chan->recv.buf_p++); + } while (--at91_chan->recv.buf_count > 0); } - HAL_READ_UINT32(base+AT91_US_RHR, c); - (chan->callbacks->rcv_char)(chan, c); } - } - cyg_drv_interrupt_unmask(vector); + + cyg_drv_interrupt_unmask (vector); } + + #endif +