From owner-svn-src-head@FreeBSD.ORG Fri Apr 5 00:26:06 2013 Return-Path: Delivered-To: svn-src-head@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by hub.freebsd.org (Postfix) with ESMTP id 9E95FA75; Fri, 5 Apr 2013 00:26:06 +0000 (UTC) (envelope-from adrian@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) by mx1.freebsd.org (Postfix) with ESMTP id 812FF6DD; Fri, 5 Apr 2013 00:26:06 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.6/8.14.6) with ESMTP id r350Q6fk060475; Fri, 5 Apr 2013 00:26:06 GMT (envelope-from adrian@svn.freebsd.org) Received: (from adrian@localhost) by svn.freebsd.org (8.14.6/8.14.5/Submit) id r350Q6lo060474; Fri, 5 Apr 2013 00:26:06 GMT (envelope-from adrian@svn.freebsd.org) Message-Id: <201304050026.r350Q6lo060474@svn.freebsd.org> From: Adrian Chadd Date: Fri, 5 Apr 2013 00:26:06 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r249120 - head/sys/mips/atheros X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 05 Apr 2013 00:26:06 -0000 Author: adrian Date: Fri Apr 5 00:26:06 2013 New Revision: 249120 URL: http://svnweb.freebsd.org/changeset/base/249120 Log: Implement the AR933x interrupt driven UART code. * Enable RX and host interrupts during bus probe/attach * Disable all interrupts (+ host ISR) during bus detach * Enable TX DONE interrupt only when we start transmitting; clear it when we're done. * The RX/TX FIFO depth is still conjecture on my part. I'll fix this shortly. * The TX FIFO interrupt isn't an "empty" interrupt, it's an "almost empty" interrupt. Sigh. So.. * .. in ar933x_bus_transmit(), wait for the FIFO to drain before continuing. I dislike having to wait for the FIFO to drain, alas. Tested: * Atheros AP121 board, AR9331 SoC. TODO: * RX/TX overflow, RX error, BREAK support, etc. * Figure out the true RX/TX FIFO depth. Modified: head/sys/mips/atheros/uart_dev_ar933x.c Modified: head/sys/mips/atheros/uart_dev_ar933x.c ============================================================================== --- head/sys/mips/atheros/uart_dev_ar933x.c Fri Apr 5 00:22:53 2013 (r249119) +++ head/sys/mips/atheros/uart_dev_ar933x.c Fri Apr 5 00:26:06 2013 (r249120) @@ -53,32 +53,6 @@ __FBSDID("$FreeBSD$"); bus_space_write_4((bas)->bst, (bas)->bsh, reg, value) -#if 0 -/* - * Clear pending interrupts. THRE is cleared by reading IIR. Data - * that may have been received gets lost here. - */ -static void -ar933x_clrint(struct uart_bas *bas) -{ - uint8_t iir, lsr; - - iir = uart_getreg(bas, REG_IIR); - while ((iir & IIR_NOPEND) == 0) { - iir &= IIR_IMASK; - if (iir == IIR_RLS) { - lsr = uart_getreg(bas, REG_LSR); - if (lsr & (LSR_BI|LSR_FE|LSR_PE)) - (void)uart_getreg(bas, REG_DATA); - } else if (iir == IIR_RXRDY || iir == IIR_RXTOUT) - (void)uart_getreg(bas, REG_DATA); - else if (iir == IIR_MLSC) - (void)uart_getreg(bas, REG_MSR); - uart_barrier(bas); - iir = uart_getreg(bas, REG_IIR); - } -} -#endif static int ar933x_drain(struct uart_bas *bas, int what) @@ -386,11 +360,28 @@ struct uart_class uart_ar933x_class = { static int ar933x_bus_attach(struct uart_softc *sc) { + struct ar933x_softc *u = (struct ar933x_softc *)sc; + struct uart_bas *bas = &sc->sc_bas; + uint32_t reg; + /* XXX TODO: flush transmitter */ - /* XXX TODO: enable RX interrupts to kick-start things */ + /* + * Setup initial interrupt notifications. + * + * XXX for now, just RX FIFO valid. + * Later on (when they're handled), also handle + * RX errors/overflow. + */ + u->u_ier = AR933X_UART_INT_RX_VALID; + + /* Enable RX interrupts to kick-start things */ + ar933x_setreg(bas, AR933X_UART_INT_EN_REG, u->u_ier); - /* XXX TODO: enable the host interrupt now */ + /* Enable the host interrupt now */ + reg = ar933x_getreg(bas, AR933X_UART_CS_REG); + reg |= AR933X_UART_CS_HOST_INT_EN; + ar933x_setreg(bas, AR933X_UART_CS_REG, reg); return (0); } @@ -398,21 +389,17 @@ ar933x_bus_attach(struct uart_softc *sc) static int ar933x_bus_detach(struct uart_softc *sc) { -#if 0 - struct ar933x_softc *ns8250; - struct uart_bas *bas; - u_char ier; + struct uart_bas *bas = &sc->sc_bas; + uint32_t reg; - ns8250 = (struct ar933x_softc *)sc; - bas = &sc->sc_bas; - ier = uart_getreg(bas, REG_IER) & ns8250->ier_mask; - uart_setreg(bas, REG_IER, ier); - uart_barrier(bas); - ar933x_clrint(bas); -#endif + /* Disable all interrupts */ + ar933x_setreg(bas, AR933X_UART_INT_EN_REG, 0x00000000); - /* XXX TODO: Disable all interrupts */ - /* XXX TODO: Disable the host interrupt */ + /* Disable the host interrupt */ + reg = ar933x_getreg(bas, AR933X_UART_CS_REG); + reg &= ~AR933X_UART_CS_HOST_INT_EN; + ar933x_setreg(bas, AR933X_UART_CS_REG, reg); + uart_barrier(bas); return (0); } @@ -536,23 +523,63 @@ ar933x_bus_ioctl(struct uart_softc *sc, static int ar933x_bus_ipend(struct uart_softc *sc) { + struct ar933x_softc *u = (struct ar933x_softc *)sc; struct uart_bas *bas = &sc->sc_bas; int ipend = 0; + uint32_t isr; uart_lock(sc->sc_hwmtx); /* - * Always notify the upper layer if RX is ready. + * Fetch/ACK the ISR status. + */ + isr = ar933x_getreg(bas, AR933X_UART_INT_REG); + ar933x_setreg(bas, AR933X_UART_INT_REG, isr); + uart_barrier(bas); + + /* + * RX ready - notify upper layer. */ - if (ar933x_rxready(bas)) { + if (isr & AR933X_UART_INT_RX_VALID) { ipend |= SER_INT_RXREADY; } + + /* + * If we get this interrupt, we should disable + * it from the interrupt mask and inform the uart + * driver appropriately. + * + * We can't keep setting SER_INT_TXIDLE or SER_INT_SIGCHG + * all the time or IO stops working. So we will always + * clear this interrupt if we get it, then we only signal + * the upper layer if we were doing active TX in the + * first place. + * + * Also, the name is misleading. This actually means + * "the FIFO is almost empty." So if we just write some + * more data to the FIFO without checking whether it can + * take said data, we'll overflow the thing. + * + * Unfortunately the FreeBSD uart device has no concept of + * partial UART writes - it expects that the whole buffer + * is written to the hardware. Thus for now, ar933x_bus_transmit() + * will wait for the FIFO to finish draining before it pushes + * more frames into it. + */ + if (isr & AR933X_UART_INT_TX_EMPTY) { + /* + * Update u_ier to disable TX notifications; update hardware + */ + u->u_ier &= ~AR933X_UART_INT_TX_EMPTY; + ar933x_setreg(bas, AR933X_UART_INT_EN_REG, u->u_ier); + uart_barrier(bas); + } + /* * Only signal TX idle if we're not busy transmitting. */ if (sc->sc_txbusy) { - if ((ar933x_getreg(bas, AR933X_UART_DATA_REG) - & AR933X_UART_DATA_TX_CSR)) { + if (isr & AR933X_UART_INT_TX_EMPTY) { ipend |= SER_INT_TXIDLE; } else { ipend |= SER_INT_SIGCHG; @@ -620,6 +647,7 @@ ar933x_bus_receive(struct uart_softc *sc /* Remove that entry from said RX FIFO */ ar933x_setreg(bas, AR933X_UART_DATA_REG, AR933X_UART_DATA_RX_CSR); + uart_barrier(bas); /* XXX frame, parity error */ uart_rx_put(sc, xc); @@ -669,23 +697,52 @@ ar933x_bus_setsig(struct uart_softc *sc, return (0); } +/* + * Write the current transmit buffer to the TX FIFO. + * + * Unfortunately the FreeBSD uart device has no concept of + * partial UART writes - it expects that the whole buffer + * is written to the hardware. Thus for now, this will wait for + * the FIFO to finish draining before it pushes more frames into it. + * + * If non-blocking operation is truely needed here, either + * the FreeBSD uart device will need to handle partial writes + * in xxx_bus_transmit(), or we'll need to do TX FIFO buffering + * of our own here. + */ static int ar933x_bus_transmit(struct uart_softc *sc) { struct uart_bas *bas = &sc->sc_bas; + struct ar933x_softc *u = (struct ar933x_softc *)sc; int i; uart_lock(sc->sc_hwmtx); - /* XXX wait for FIFO to be ready? */ + /* Wait for the FIFO to be clear - see above */ + while (ar933x_getreg(bas, AR933X_UART_CS_REG) & + AR933X_UART_CS_TX_BUSY) + ; + /* + * Write some data! + */ for (i = 0; i < sc->sc_txdatasz; i++) { /* Write the TX data */ ar933x_setreg(bas, AR933X_UART_DATA_REG, (sc->sc_txbuf[i] & 0xff) | AR933X_UART_DATA_TX_CSR); + uart_barrier(bas); } /* + * Now that we're transmitting, get interrupt notification + * when the FIFO is (almost) empty - see above. + */ + u->u_ier |= AR933X_UART_INT_TX_EMPTY; + ar933x_setreg(bas, AR933X_UART_INT_EN_REG, u->u_ier); + uart_barrier(bas); + + /* * Inform the upper layer that we are presently transmitting * data to the hardware; this will be cleared when the * TXIDLE interrupt occurs.