p4-projects@freebsd.org
[Top] [All Lists]

PERFORCE change 100188 for review

Subject: PERFORCE change 100188 for review
From: Warner Losh
Date: Wed, 28 Jun 2006 07:08:45 GMT
http://perforce.freebsd.org/chv.cgi?CH=100188

Change 100188 by imp@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);
_______________________________________________
p4-projects@xxxxxxxxxxx mailing list
http://lists.freebsd.org/mailman/listinfo/p4-projects
To unsubscribe, send any mail to "p4-projects-unsubscribe@xxxxxxxxxxx"

<Prev in Thread] Current Thread [Next in Thread>
  • PERFORCE change 100188 for review, Warner Losh <=