From owner-p4-projects@FreeBSD.ORG Wed Jun 28 07:08:47 2006 Return-Path: X-Original-To: p4-projects@freebsd.org Delivered-To: p4-projects@freebsd.org Received: by hub.freebsd.org (Postfix, from userid 32767) id 4611C16A40F; Wed, 28 Jun 2006 07:08:47 +0000 (UTC) X-Original-To: perforce@freebsd.org Delivered-To: perforce@freebsd.org Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id 20F4316A40B for ; Wed, 28 Jun 2006 07:08:47 +0000 (UTC) (envelope-from imp@freebsd.org) Received: from repoman.freebsd.org (repoman.freebsd.org [216.136.204.115]) by mx1.FreeBSD.org (Postfix) with ESMTP id F208D43D5D for ; Wed, 28 Jun 2006 07:08:45 +0000 (GMT) (envelope-from imp@freebsd.org) Received: from repoman.freebsd.org (localhost [127.0.0.1]) by repoman.freebsd.org (8.13.6/8.13.6) with ESMTP id k5S78j3c064520 for ; Wed, 28 Jun 2006 07:08:45 GMT (envelope-from imp@freebsd.org) Received: (from perforce@localhost) by repoman.freebsd.org (8.13.6/8.13.4/Submit) id k5S78jI8064517 for perforce@freebsd.org; Wed, 28 Jun 2006 07:08:45 GMT (envelope-from imp@freebsd.org) Date: Wed, 28 Jun 2006 07:08:45 GMT Message-Id: <200606280708.k5S78jI8064517@repoman.freebsd.org> X-Authentication-Warning: repoman.freebsd.org: perforce set sender to imp@freebsd.org using -f From: Warner Losh To: Perforce Change Reviews Cc: Subject: PERFORCE change 100188 for review X-BeenThere: p4-projects@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: p4 projects tree changes List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 28 Jun 2006 07:08:47 -0000 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);