This is the mail archive of the ecos-patches@sources.redhat.com mailing list for the eCos project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

patch: at91 serial drivers with configurable PDC


This essentially is the same patch as before, except I added an option
to enable/disable the PDC code.

The PDC code is enabled by default. The most likely reason to disable it
is extremely tight ram footprints and if the code is broken. 

I have not added any other configuration options because I believe there
shouldn't be any:

- Enabling/disabling the PDC code provides the right level of
configurability. 
- The "PDC buffer" isn't the serial buffer. It is more akin to a FIFO
and as such the length should be decided by interrupt latency issues.
I've set it to 128(down from 1024 bytes), though I suspect 16 bytes FIFO
is probably more than enough(if 16x5x drivers are anything to go by).
The serial buffers exist as seperately configurable options already.
- The "PDC read timeout" isn't really a timeout, but is intimately
linked with how the "PDC FIFO" works. There is a correct value, not
something to be configured. It is more akin to "FIFO threshold" value
that exists in tht 16x5x UART.





Ãyvind

Index: ecos/packages/devs/serial/arm/at91/current/ChangeLog
===================================================================
RCS file: /cvs/ecos/ecos/packages/devs/serial/arm/at91/current/ChangeLog,v
retrieving revision 1.7
diff -a -w -u -r1.7 ChangeLog
--- ecos/packages/devs/serial/arm/at91/current/ChangeLog	24 Feb 2003 14:12:18 -0000	1.7
+++ ecos/packages/devs/serial/arm/at91/current/ChangeLog	23 Oct 2003 13:02:50 -0000
@@ -1,3 +1,10 @@
+2003-10-21  Øyvind Harboe <oyvind.harboe@zylin.com>
+	* added support for hardware send/receive buffers to avoid
+	problems with real-time response in DSR routine. This helps
+	compatibility with e.g. 16x5x serial drivers that are also
+	spending time in DSR routines. This increases the amount of time
+	spent in the ISR by a relatively modest amount.
+
 2003-02-24  Jonathan Larmour  <jifl@eCosCentric.com>
 
 	* cdl/ser_arm_at91.cdl: Remove irrelevant doc link.
Index: ecos/packages/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 -a -w -u -r1.5 ser_arm_at91.cdl
--- ecos/packages/devs/serial/arm/at91/current/cdl/ser_arm_at91.cdl	24 Feb 2003 14:12:18 -0000	1.5
+++ ecos/packages/devs/serial/arm/at91/current/cdl/ser_arm_at91.cdl	23 Oct 2003 13:02:50 -0000
@@ -71,6 +71,18 @@
         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_option CYGDAT_IO_SERIAL_ARM_AT91_SERIAL_PDC {
+    display       "Enable UART PDC"
+    flavor        bool
+    default_value 1
+    description   "Enable PDC for increased throughput and reduced interrupt latency problems. Slightly increases codesize and ram usage."
+    }
+
+
 cdl_component CYGPKG_IO_SERIAL_ARM_AT91_SERIAL0 {
     display       "Atmel AT91 serial port 0 driver"
     flavor        bool
Index: ecos/packages/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 -a -w -u -r1.6 at91_serial.c
--- ecos/packages/devs/serial/arm/at91/current/src/at91_serial.c	16 Oct 2003 18:02:55 -0000	1.6
+++ ecos/packages/devs/serial/arm/at91/current/src/at91_serial.c	23 Oct 2003 13:02:50 -0000
@@ -62,15 +62,50 @@
 #include <cyg/io/serial.h>
 #include <cyg/infra/diag.h>
 
+#include <string.h>
+
 #ifdef CYGPKG_IO_SERIAL_ARM_AT91
 
+#ifdef CYGDAT_IO_SERIAL_ARM_AT91_SERIAL_PDC
+/* This PDC buffer size is not configurable, because this isn't really
+   the buffer for the serial port. It is more akin to a "FIFO" which
+   is fed to the serial buffers. */
+#define AT91_UART_BUF_SIZE 128
+#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;		\
+    }
+#endif
+
 #include "at91_serial.h"
 
+
+#ifdef CYGDAT_IO_SERIAL_ARM_AT91_SERIAL_PDC
+FLIP_BUFFER_DEF(AT91_UART_BUF_SIZE);
+#endif
+
 typedef struct at91_serial_info {
     CYG_ADDRWORD   base;
     CYG_WORD       int_num;
     cyg_interrupt  serial_interrupt;
     cyg_handle_t   serial_interrupt_handle;
+#ifdef CYGDAT_IO_SERIAL_ARM_AT91_SERIAL_PDC
+    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;
+#endif
 } at91_serial_info;
 
 static bool at91_serial_init(struct cyg_devtab_entry *tab);
@@ -185,12 +220,31 @@
     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];
 
+#ifdef CYGDAT_IO_SERIAL_ARM_AT91_SERIAL_PDC
+    at91_chan->flip=&at91_chan->recv;
+#endif
+
     if ((word_length == 0xFF) ||
         (parity == 0xFF) ||
         (stop_bits == 0xFF)) {
         return false;  // Unsupported configuration
     }
 
+#ifdef CYGDAT_IO_SERIAL_ARM_AT91_SERIAL_PDC
+    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) {
+#endif
     // Reset device
     HAL_WRITE_UINT32(base+AT91_US_CR, AT91_US_CR_RxRESET | AT91_US_CR_TxRESET);
 
@@ -200,15 +254,39 @@
     // Baud rate
     HAL_WRITE_UINT32(base+AT91_US_BRG, AT91_US_BAUD(select_baud[new_config->baud]));
 
+#ifdef CYGDAT_IO_SERIAL_ARM_AT91_SERIAL_PDC
+	// 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) {
+#endif
     // Disable all interrupts
     HAL_WRITE_UINT32(base+AT91_US_IDR, 0xFFFFFFFF);
 
+
+#ifdef CYGDAT_IO_SERIAL_ARM_AT91_SERIAL_PDC
+	// 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);
+    }
+
+    // Start timeout
+    HAL_WRITE_UINT32(base+AT91_US_RTO, RECV_UART_CHAR_TIMEOUT);
+    HAL_WRITE_UINT32(base+AT91_US_CR, AT91_US_CR_STTTO);
+#else
     // Enable Rx interrupts
     HAL_WRITE_UINT32(base+AT91_US_IER, AT91_US_IER_RxRDY);
 
     // Enable RX and TX
     HAL_WRITE_UINT32(base+AT91_US_CR, AT91_US_CR_RxENAB | AT91_US_CR_TxENAB);
-
+#endif
     if (new_config != &chan->config) {
         chan->config = *new_config;
     }
@@ -255,12 +333,44 @@
     return ENOERR;
 }
 
+#ifdef CYGDAT_IO_SERIAL_ARM_AT91_SERIAL_PDC
+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 ();
+}
+#endif
+
 // 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;
+#ifndef CYGDAT_IO_SERIAL_ARM_AT91_SERIAL_PDC
     CYG_ADDRWORD base = at91_chan->base;
     cyg_uint32 stat;
 
@@ -274,12 +384,32 @@
     } else {
         return false;  // Couldn't send, tx was busy
     }
+#else
+    int other;
+
+    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);
+
+    return true;
+#endif
 }
 
 // Fetch a character from the device input buffer, waiting if necessary
 static unsigned char 
 at91_serial_getc(serial_channel *chan)
 {
+#ifdef CYGDAT_IO_SERIAL_ARM_AT91_SERIAL_PDC
+  // we don't support this - its only for bufferless operation: see serial.c
+    return 0;
+#else
     at91_serial_info *at91_chan = (at91_serial_info *)chan->dev_priv;
     CYG_ADDRWORD base = at91_chan->base;
     cyg_uint32 c;
@@ -287,6 +417,7 @@
     // Read data
     HAL_READ_UINT32(base+AT91_US_RHR, c);
     return c;
+#endif
 }
 
 // Set up the device characteristics; baud rate, etc.
@@ -318,9 +449,23 @@
 {
     at91_serial_info *at91_chan = (at91_serial_info *)chan->dev_priv;
     CYG_ADDRWORD base = at91_chan->base;
+#ifdef CYGDAT_IO_SERIAL_ARM_AT91_SERIAL_PDC
+    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);
+#else
     (chan->callbacks->xmt_char)(chan);  // Kick transmitter (if necessary)
     HAL_WRITE_UINT32(base+AT91_US_IER, AT91_US_IER_TxRDY);
+#endif
 }
 
 // Disable the transmitter on the device
@@ -328,17 +473,51 @@
 at91_serial_stop_xmit(serial_channel *chan)
 {
     at91_serial_info *at91_chan = (at91_serial_info *)chan->dev_priv;
+#ifdef CYGDAT_IO_SERIAL_ARM_AT91_SERIAL_PDC
+    at91_chan->tx_enabled = 0;
+#else
     CYG_ADDRWORD base = at91_chan->base;
 
     HAL_WRITE_UINT32(base+AT91_US_IDR, AT91_US_IER_TxRDY);
+#endif
 }
 
 // Serial I/O - low level interrupt handler (ISR)
 static cyg_uint32 
 at91_serial_ISR(cyg_vector_t vector, cyg_addrword_t data)
 {
+#ifdef CYGDAT_IO_SERIAL_ARM_AT91_SERIAL_PDC
+    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;
+#endif
+    // 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);
+#ifdef CYGDAT_IO_SERIAL_ARM_AT91_SERIAL_PDC
+    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);
+    }
+#endif
     return (CYG_ISR_CALL_DSR|CYG_ISR_HANDLED);  // Cause DSR to be run
 }
 
@@ -349,6 +528,67 @@
     serial_channel *chan = (serial_channel *)data;
     at91_serial_info *at91_chan = (at91_serial_info *)chan->dev_priv;
     CYG_ADDRWORD base = at91_chan->base;
+#ifdef CYGDAT_IO_SERIAL_ARM_AT91_SERIAL_PDC
+    cyg_uint32 stat, mask;
+
+    HAL_READ_UINT32 (base + AT91_US_CSR, stat);
+    HAL_READ_UINT32 (base + AT91_US_IMR, mask);
+
+    // 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);
+    }
+	} 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);
+            }
+        }
+#else
     cyg_uint32 stat, c;
 
     // Check status
@@ -367,6 +607,7 @@
             (chan->callbacks->rcv_char)(chan, c);
         }
     }
+#endif
     cyg_drv_interrupt_unmask(vector);
 }
 #endif

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]