Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 5 Apr 2013 00:26:06 +0000 (UTC)
From:      Adrian Chadd <adrian@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r249120 - head/sys/mips/atheros
Message-ID:  <201304050026.r350Q6lo060474@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
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.



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201304050026.r350Q6lo060474>