PERFORCE change 100188 for review
Warner Losh
imp at FreeBSD.org
Wed Jun 28 07:08:47 UTC 2006
http://perforce.freebsd.org/chv.cgi?CH=100188
Change 100188 by imp at imp_lighthouse on 2006/06/28 07:08:42
Functionality:
o auto detect if the device has TIMEOUT.
o if it has TIMEOUT, then use ping-pong buffers to keep the interrupt
load down.
o If not, then use single character buffer, but move its harvesting
into the ISR rather than scheduling a task that takes a while to
run (this not losing characters).
Deck chairs:
o move to SET, CLR, ISSET
Affected files ...
.. //depot/projects/arm/src/sys/arm/at91/uart_dev_at91usart.c#28 edit
Differences ...
==== //depot/projects/arm/src/sys/arm/at91/uart_dev_at91usart.c#28 (text+ko) ====
@@ -45,19 +45,34 @@
#include "uart_if.h"
+/* Macros to clear/set/test flags. */
+#define SET(t, f) (t) |= (f)
+#define CLR(t, f) (t) &= ~(f)
+#define ISSET(t, f) ((t) & (f))
+
+#define DEFAULT_RCLK AT91C_MASTER_CLOCK
+#define USART_BUFFER_SIZE 128
+
/*
* High-level UART interface.
*/
+struct at91_usart_rx {
+ bus_addr_t pa;
+ uint8_t buffer[USART_BUFFER_SIZE];
+ bus_dmamap_t map;
+};
+
struct at91_usart_softc {
struct uart_softc base;
bus_dma_tag_t dmatag; /* bus dma tag for mbufs */
bus_dmamap_t tx_map;
- bus_dmamap_t rx_map;
+ uint32_t flags;
+#define HAS_TIMEOUT 1
+ struct at91_usart_rx ping_pong[2];
+ struct at91_usart_rx *ping;
+ struct at91_usart_rx *pong;
};
-#define DEFAULT_RCLK AT91C_MASTER_CLOCK
-#define USART_BUFFER_SIZE 128
-
#define RD4(bas, reg) \
bus_space_read_4((bas)->bst, (bas)->bsh, uart_regofs(bas, reg))
#define WR4(bas, reg, value) \
@@ -213,13 +228,6 @@
WR4(bas, USART_CR, cr);
WR4(bas, USART_CR, USART_CR_RXEN | USART_CR_TXEN);
WR4(bas, USART_IDR, 0xffffffff);
-#if 0
- WR4(bas, USART_IER, USART_CSR_TIMEOUT |
- USART_CSR_TXRDY | USART_CSR_RXRDY |
- USART_CSR_RXBRK | USART_CSR_ENDRX | USART_CSR_ENDTX);
- /* Set the receive timeout to be 1.5 character times. */
- WR4(bas, USART_RTOR, 12);
-#endif
}
/*
@@ -240,7 +248,7 @@
at91_usart_putc(struct uart_bas *bas, int c)
{
- while (!(RD4(bas, USART_CSR) & USART_CSR_TXRDY))
+ while (!(ISSET(RD4(bas, USART_CSR), USART_CSR_TXRDY)))
continue;
WR4(bas, USART_THR, c);
}
@@ -252,7 +260,7 @@
at91_usart_poll(struct uart_bas *bas)
{
- if (!(RD4(bas, USART_CSR) & USART_CSR_RXRDY))
+ if (!ISSET(RD4(bas, USART_CSR), USART_CSR_RXRDY))
return (-1);
return (RD4(bas, USART_RHR) & 0xff);
}
@@ -265,7 +273,7 @@
{
int c;
- while (!(RD4(bas, USART_CSR) & USART_CSR_RXRDY))
+ while (!ISSET(RD4(bas, USART_CSR), USART_CSR_RXRDY))
continue;
c = RD4(bas, USART_RHR);
c &= 0xff;
@@ -304,15 +312,35 @@
return (0);
}
+#ifndef SKYEYE_WORKAROUNDS
+static void
+at91_getaddr(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
+{
+ if (error != 0)
+ return;
+ *(bus_addr_t *)arg = segs[0].ds_addr;
+}
+#endif
+
static int
at91_usart_bus_attach(struct uart_softc *sc)
{
- int err;
+ int err, i;
uint32_t cr;
struct at91_usart_softc *atsc;
atsc = (struct at91_usart_softc *)sc;
+ /*
+ * See if we have a TIMEOUT bit. We disable all interrupts to
+ * minimize interference.
+ */
+ WR4(&sc->sc_bas, USART_IDR, 0xffffffff);
+ WR4(&sc->sc_bas, USART_IER, USART_CSR_TIMEOUT);
+ if (ISSET(RD4(&sc->sc_bas, USART_IMR), USART_CSR_TIMEOUT))
+ SET(atsc->flags, HAS_TIMEOUT);
+ WR4(&sc->sc_bas, USART_IDR, 0xffffffff);
+
sc->sc_txfifosz = USART_BUFFER_SIZE;
sc->sc_rxfifosz = USART_BUFFER_SIZE;
sc->sc_hwiflow = 0;
@@ -328,38 +356,63 @@
err = bus_dmamap_create(atsc->dmatag, 0, &atsc->tx_map);
if (err != 0)
goto errout;
- err = bus_dmamap_create(atsc->dmatag, 0, &atsc->rx_map);
- if (err != 0)
- goto errout;
+ if (ISSET(atsc->flags, HAS_TIMEOUT)) {
+ for (i = 0; i < 2; i++) {
+ err = bus_dmamap_create(atsc->dmatag, 0,
+ &atsc->ping_pong[i].map);
+ if (err != 0)
+ goto errout;
+ err = bus_dmamap_load(atsc->dmatag,
+ atsc->ping_pong[i].map,
+ atsc->ping_pong[i].buffer, sc->sc_rxfifosz,
+ at91_getaddr, &atsc->ping_pong[i].pa, 0);
+ if (err != 0)
+ goto errout;
+ bus_dmamap_sync(atsc->dmatag, atsc->ping_pong[i].map,
+ BUS_DMASYNC_PREREAD);
+ }
+ atsc->ping = &atsc->ping_pong[0];
+ atsc->pong = &atsc->ping_pong[1];
+ }
+
+ /*
+ * Prime the pump with the RX buffer. We use two 64 byte bounce
+ * buffers here to avoid data overflow.
+ */
/* Turn on rx and tx */
cr = USART_CR_RSTSTA | USART_CR_RSTRX | USART_CR_RSTTX;
WR4(&sc->sc_bas, USART_CR, cr);
WR4(&sc->sc_bas, USART_CR, USART_CR_RXEN | USART_CR_TXEN);
- WR4(&sc->sc_bas, USART_IDR, 0xffffffff);
-#if 0
- WR4(&sc->sc_bas, USART_IER, USART_CSR_TIMEOUT |
- USART_CSR_TXRDY | USART_CSR_RXRDY |
- USART_CSR_RXBRK | USART_CSR_ENDRX | USART_CSR_ENDTX);
-#endif
- /* Set the receive timeout to be 1.5 character times. */
- WR4(&sc->sc_bas, USART_RTOR, 12);
+
+ /*
+ * Setup the PDC to receive data. We use the ping-pong buffers
+ * so that we can more easily bounce between the two and so that
+ * we get an interrupt 1/2 way through the software 'fifo' we have
+ * to avoid overruns.
+ */
+ if (ISSET(atsc->flags, HAS_TIMEOUT)) {
+ WR4(&sc->sc_bas, PDC_RPR, atsc->ping->pa);
+ WR4(&sc->sc_bas, PDC_RCR, sc->sc_rxfifosz);
+ WR4(&sc->sc_bas, PDC_RNPR, atsc->pong->pa);
+ WR4(&sc->sc_bas, PDC_RNCR, sc->sc_rxfifosz);
+ WR4(&sc->sc_bas, PDC_PTCR, PDC_PTCR_RXTEN);
+
+ /* Set the receive timeout to be 1.5 character times. */
+ WR4(&sc->sc_bas, USART_RTOR, 12);
+ WR4(&sc->sc_bas, USART_CR, USART_CR_STTTO);
+
+ WR4(&sc->sc_bas, USART_IER, USART_CSR_TIMEOUT |
+ USART_CSR_RXBUFF | USART_CSR_ENDRX);
+ } else {
+ WR4(&sc->sc_bas, USART_IER, USART_CSR_RXRDY);
+ }
+ WR4(&sc->sc_bas, USART_IER, USART_CSR_RXBRK);
errout:;
// XXX bad
return (err);
}
-#ifndef SKYEYE_WORKAROUNDS
-static void
-at91_getaddr(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
-{
- if (error != 0)
- return;
- *(bus_addr_t *)arg = segs[0].ds_addr;
-}
-#endif
-
-
static int
at91_usart_bus_transmit(struct uart_softc *sc)
{
@@ -387,8 +440,6 @@
WR4(&sc->sc_bas, PDC_TCR, sc->sc_txdatasz);
WR4(&sc->sc_bas, PDC_PTCR, PDC_PTCR_TXTEN);
uart_unlock(sc->sc_hwmtx);
- if (device_get_unit(sc->sc_dev))
- device_printf(sc->sc_dev, "transmit %d bytes\n", sc->sc_txdatasz);
#else
for (int i = 0; i < sc->sc_txdatasz; i++)
at91_usart_putc(&sc->sc_bas, sc->sc_txbuf[i]);
@@ -409,24 +460,22 @@
do {
old = sc->sc_hwsig;
new = old;
- if (sig & SER_DDTR)
+ if (ISSET(sig, SER_DDTR))
SIGCHG(sig & SER_DTR, new, SER_DTR, SER_DDTR);
- if (sig & SER_DRTS)
+ if (ISSET(sig, SER_DRTS))
SIGCHG(sig & SER_RTS, new, SER_RTS, SER_DRTS);
} while (!atomic_cmpset_32(&sc->sc_hwsig, old, new));
bas = &sc->sc_bas;
uart_lock(sc->sc_hwmtx);
- cr = RD4(bas, USART_CR);
- cr &= ~(USART_CR_DTREN | USART_CR_DTRDIS | USART_CR_RTSEN |
- USART_CR_RTSDIS);
- if (new & SER_DTR)
- cr |= USART_CR_DTREN;
+ cr = 0;
+ if (ISSET(new, SER_DTR))
+ SET(cr, USART_CR_DTREN);
else
- cr |= USART_CR_DTRDIS;
- if (new & SER_RTS)
- cr |= USART_CR_RTSEN;
+ SET(cr, USART_CR_DTRDIS);
+ if (ISSET(new, SER_RTS))
+ SET(cr, USART_CR_RTSEN);
else
- cr |= USART_CR_RTSDIS;
+ SET(cr, USART_CR_RTSDIS);
WR4(bas, USART_CR, cr);
uart_unlock(sc->sc_hwmtx);
return (0);
@@ -434,16 +483,14 @@
static int
at91_usart_bus_receive(struct uart_softc *sc)
{
-
- uart_lock(sc->sc_hwmtx);
- uart_rx_put(sc, at91_usart_getc(&sc->sc_bas, NULL));
- uart_unlock(sc->sc_hwmtx);
+
return (0);
}
static int
at91_usart_bus_param(struct uart_softc *sc, int baudrate, int databits,
int stopbits, int parity)
{
+
return (at91_usart_param(&sc->sc_bas, baudrate, databits, stopbits,
parity));
}
@@ -451,27 +498,91 @@
at91_usart_bus_ipend(struct uart_softc *sc)
{
int csr = RD4(&sc->sc_bas, USART_CSR);
- int ipend = 0;
+ int ipend = 0, i, len;
struct at91_usart_softc *atsc;
+ struct at91_usart_rx *p;
- if (device_get_unit(sc->sc_dev))
- device_printf(sc->sc_dev, "ipend csr %#x\n", csr);
atsc = (struct at91_usart_softc *)sc;
- if (csr & USART_CSR_ENDTX) {
+ if (ISSET(csr, USART_CSR_ENDTX)) {
bus_dmamap_sync(atsc->dmatag, atsc->tx_map,
BUS_DMASYNC_POSTWRITE);
bus_dmamap_unload(atsc->dmatag, atsc->tx_map);
}
uart_lock(sc->sc_hwmtx);
- if (csr & USART_CSR_TXRDY && sc->sc_txbusy) {
+ if (ISSET(csr, USART_CSR_TXRDY) && sc->sc_txbusy) {
ipend |= SER_INT_TXIDLE;
WR4(&sc->sc_bas, USART_IDR, USART_CSR_TXRDY);
}
- if (csr & USART_CSR_ENDTX && sc->sc_txbusy)
+ if (ISSET(csr, USART_CSR_ENDTX) && sc->sc_txbusy)
ipend |= SER_INT_TXIDLE;
- if (csr & (USART_CSR_RXRDY /* | USART_CSR_ENDRX | USART_CSR_TIMEOUT */))
+ /*
+ * Due to the contraints of the DMA engine present in the
+ * atmel chip, I can't just say I have a rx interrupt pending
+ * and do all the work elsewhere. I need to look at the CSR
+ * bits right now and do things based on them to avoid races.
+ */
+ if (ISSET(atsc->flags, HAS_TIMEOUT) && ISSET(csr, USART_CSR_RXBUFF)) {
+ // Have a buffer overflow. Copy all data from both
+ // ping and pong. Insert overflow character. Reset
+ // ping and pong and re-enable the PDC to receive
+ // characters again.
+ bus_dmamap_sync(atsc->dmatag, atsc->ping->map,
+ BUS_DMASYNC_POSTREAD);
+ bus_dmamap_sync(atsc->dmatag, atsc->pong->map,
+ BUS_DMASYNC_POSTREAD);
+ for (i = 0; i < sc->sc_rxfifosz; i++)
+ uart_rx_put(sc, atsc->ping->buffer[i]);
+ for (i = 0; i < sc->sc_rxfifosz; i++)
+ uart_rx_put(sc, atsc->pong->buffer[i]);
+ uart_rx_put(sc, UART_STAT_OVERRUN);
+ CLR(csr, USART_CSR_ENDRX | USART_CSR_TIMEOUT);
+ WR4(&sc->sc_bas, PDC_RPR, atsc->ping->pa);
+ WR4(&sc->sc_bas, PDC_RCR, sc->sc_rxfifosz);
+ WR4(&sc->sc_bas, PDC_RNPR, atsc->pong->pa);
+ WR4(&sc->sc_bas, PDC_RNCR, sc->sc_rxfifosz);
+ WR4(&sc->sc_bas, PDC_PTCR, PDC_PTCR_RXTEN);
+ ipend |= SER_INT_RXREADY;
+ }
+ if (ISSET(atsc->flags, HAS_TIMEOUT) && ISSET(csr, USART_CSR_ENDRX)) {
+ // Shuffle data from 'ping' of ping pong buffer, but
+ // leave current 'pong' in place, as it has become the
+ // new 'ping'. We need to copy data and setup the old
+ // 'ping' as the new 'pong' when we're done.
+ bus_dmamap_sync(atsc->dmatag, atsc->ping->map,
+ BUS_DMASYNC_POSTREAD);
+ for (i = 0; i < sc->sc_rxfifosz; i++)
+ uart_rx_put(sc, atsc->ping->buffer[i]);
+ p = atsc->ping;
+ atsc->ping = atsc->pong;
+ atsc->pong = p;
+ WR4(&sc->sc_bas, PDC_RNPR, atsc->pong->pa);
+ WR4(&sc->sc_bas, PDC_RNCR, sc->sc_rxfifosz);
+ ipend |= SER_INT_RXREADY;
+ }
+ if (ISSET(atsc->flags, HAS_TIMEOUT) && ISSET(csr, USART_CSR_TIMEOUT)) {
+ // We have one partial buffer. We need to stop the
+ // PDC, get the number of characters left and from
+ // that compute number of valid characters. We then
+ // need to reset ping and pong and reenable the PDC.
+ // Not sure if there's a race here at fast baud rates
+ // we need to worry about.
+ WR4(&sc->sc_bas, PDC_PTCR, PDC_PTCR_RXTDIS);
+ len = sc->sc_rxfifosz - RD4(&sc->sc_bas, PDC_RCR);
+ for (i = 0; i < len; i++)
+ uart_rx_put(sc, atsc->ping->buffer[i]);
+ WR4(&sc->sc_bas, PDC_RPR, atsc->ping->pa);
+ WR4(&sc->sc_bas, PDC_RCR, sc->sc_rxfifosz);
+ WR4(&sc->sc_bas, PDC_PTCR, PDC_PTCR_RXTEN);
+ ipend |= SER_INT_RXREADY;
+ }
+ if (!ISSET(atsc->flags, HAS_TIMEOUT) && ISSET(csr, USART_CSR_RXRDY)) {
+ // We have another charater in a device that doesn't support
+ // timeouts, so we do it one character at a time.
+ uart_rx_put(sc, RD4(&sc->sc_bas, USART_RHR) & 0xff);
ipend |= SER_INT_RXREADY;
- if (csr & USART_CSR_RXBRK) {
+ }
+
+ if (ISSET(csr, USART_CSR_RXBRK)) {
unsigned int cr = USART_CR_RSTSTA;
ipend |= SER_INT_BREAK;
@@ -495,14 +606,14 @@
uart_lock(sc->sc_hwmtx);
csr = RD4(&sc->sc_bas, USART_CSR);
sig = 0;
- if (csr & USART_CSR_CTS)
- sig |= SER_CTS;
- if (csr & USART_CSR_DCD)
- sig |= SER_DCD;
- if (csr & USART_CSR_DSR)
- sig |= SER_DSR;
- if (csr & USART_CSR_RI)
- sig |= SER_RI;
+ if (ISSET(csr, USART_CSR_CTS))
+ SET(sig, SER_CTS);
+ if (ISSET(csr, USART_CSR_DCD))
+ SET(sig, SER_DCD);
+ if (ISSET(csr, USART_CSR_DSR))
+ SET(sig, SER_DSR);
+ if (ISSET(csr, USART_CSR_RI))
+ SET(sig, SER_RI);
new = sig & ~SER_MASK_DELTA;
sc->sc_hwsig = new;
uart_unlock(sc->sc_hwmtx);
More information about the p4-projects
mailing list