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