Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 4 Apr 2013 10:46:34 +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: r249093 - in head/sys/mips: atheros conf
Message-ID:  <201304041046.r34AkYBS018356@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: adrian
Date: Thu Apr  4 10:46:33 2013
New Revision: 249093
URL: http://svnweb.freebsd.org/changeset/base/249093

Log:
  Implement AR933x polled IO uart bus code.
  
  This implements the bus transmit/receive/sigchg/ipend methods with
  a polled interrupt handler (ipend) rather than enabling hardware
  interrupts.
  
  The FIFO is faked at 16 bytes deep for now, just so the transmit
  IO side doesn't suck too bad (the callout frequency limits how quickly
  IO is flushed to the sender, rather than scheduling the callout more
  frequently whilst there's active TX.  But I digress.)
  
  Tested:
  
  * Atheros AP121 (AR9330) reference board, booting to multi-user interactive
    mode.

Modified:
  head/sys/mips/atheros/uart_dev_ar933x.c
  head/sys/mips/conf/AR933X_BASE.hints

Modified: head/sys/mips/atheros/uart_dev_ar933x.c
==============================================================================
--- head/sys/mips/atheros/uart_dev_ar933x.c	Thu Apr  4 10:26:09 2013	(r249092)
+++ head/sys/mips/atheros/uart_dev_ar933x.c	Thu Apr  4 10:46:33 2013	(r249093)
@@ -80,72 +80,54 @@ ar933x_clrint(struct uart_bas *bas)
 }
 #endif
 
-#if 0
 static int
 ar933x_drain(struct uart_bas *bas, int what)
 {
-	int delay, limit;
-
-	delay = ar933x_delay(bas);
+	int limit;
 
 	if (what & UART_DRAIN_TRANSMITTER) {
-		/*
-		 * Pick an arbitrary high limit to avoid getting stuck in
-		 * an infinite loop when the hardware is broken. Make the
-		 * limit high enough to handle large FIFOs.
-		 */
 		limit = 10*1024;
-		while ((uart_getreg(bas, REG_LSR) & LSR_TEMT) == 0 && --limit)
-			DELAY(delay);
+
+		/* Loop over until the TX FIFO shows entirely clear */
+		while (--limit) {
+			if ((ar933x_getreg(bas, AR933X_UART_CS_REG)
+			    & AR933X_UART_CS_TX_BUSY) == 0)
+				break;
+		}
 		if (limit == 0) {
-			/* printf("ns8250: transmitter appears stuck... "); */
 			return (EIO);
 		}
 	}
 
 	if (what & UART_DRAIN_RECEIVER) {
-		/*
-		 * Pick an arbitrary high limit to avoid getting stuck in
-		 * an infinite loop when the hardware is broken. Make the
-		 * limit high enough to handle large FIFOs and integrated
-		 * UARTs. The HP rx2600 for example has 3 UARTs on the
-		 * management board that tend to get a lot of data send
-		 * to it when the UART is first activated.
-		 */
 		limit=10*4096;
-		while ((uart_getreg(bas, REG_LSR) & LSR_RXRDY) && --limit) {
-			(void)uart_getreg(bas, REG_DATA);
+		while (--limit) {
+
+			/* XXX duplicated from ar933x_getc() */
+			/* XXX TODO: refactor! */
+
+			/* If there's nothing to read, stop! */
+			if ((ar933x_getreg(bas, AR933X_UART_DATA_REG) &
+			    AR933X_UART_DATA_RX_CSR) == 0) {
+				break;
+			}
+
+			/* Read the top of the RX FIFO */
+			(void) ar933x_getreg(bas, AR933X_UART_DATA_REG);
+
+			/* Remove that entry from said RX FIFO */
+			ar933x_setreg(bas, AR933X_UART_DATA_REG,
+			    AR933X_UART_DATA_RX_CSR);
+
 			uart_barrier(bas);
-			DELAY(delay << 2);
+			DELAY(2);
 		}
 		if (limit == 0) {
-			/* printf("ns8250: receiver appears broken... "); */
 			return (EIO);
 		}
 	}
 	return (0);
 }
-#endif
-
-#if 0
-/*
- * We can only flush UARTs with FIFOs. UARTs without FIFOs should be
- * drained. WARNING: this function clobbers the FIFO setting!
- */
-static void
-ar933x_flush(struct uart_bas *bas, int what)
-{
-	uint8_t fcr;
-
-	fcr = FCR_ENABLE;
-	if (what & UART_FLUSH_TRANSMITTER)
-		fcr |= FCR_XMT_RST;
-	if (what & UART_FLUSH_RECEIVER)
-		fcr |= FCR_RCV_RST;
-	uart_setreg(bas, REG_FCR, fcr);
-	uart_barrier(bas);
-}
-#endif
 
 /*
  * Calculate the baud from the given chip configuration parameters.
@@ -404,56 +386,12 @@ struct uart_class uart_ar933x_class = {
 static int
 ar933x_bus_attach(struct uart_softc *sc)
 {
-#if 0
-	struct ar933x_softc *ns8250 = (struct ar933x_softc*)sc;
-	struct uart_bas *bas;
-	unsigned int ivar;
+	/* XXX TODO: flush transmitter */
 
-	bas = &sc->sc_bas;
+	/* XXX TODO: enable RX interrupts to kick-start things */
 
-	ns8250->mcr = uart_getreg(bas, REG_MCR);
-	ns8250->fcr = FCR_ENABLE;
-	if (!resource_int_value("uart", device_get_unit(sc->sc_dev), "flags",
-	    &ivar)) {
-		if (UART_FLAGS_FCR_RX_LOW(ivar)) 
-			ns8250->fcr |= FCR_RX_LOW;
-		else if (UART_FLAGS_FCR_RX_MEDL(ivar)) 
-			ns8250->fcr |= FCR_RX_MEDL;
-		else if (UART_FLAGS_FCR_RX_HIGH(ivar)) 
-			ns8250->fcr |= FCR_RX_HIGH;
-		else
-			ns8250->fcr |= FCR_RX_MEDH;
-	} else 
-		ns8250->fcr |= FCR_RX_MEDH;
-	
-	/* Get IER mask */
-	ivar = 0xf0;
-	resource_int_value("uart", device_get_unit(sc->sc_dev), "ier_mask",
-	    &ivar);
-	ns8250->ier_mask = (uint8_t)(ivar & 0xff);
-	
-	/* Get IER RX interrupt bits */
-	ivar = IER_EMSC | IER_ERLS | IER_ERXRDY;
-	resource_int_value("uart", device_get_unit(sc->sc_dev), "ier_rxbits",
-	    &ivar);
-	ns8250->ier_rxbits = (uint8_t)(ivar & 0xff);
-	
-	uart_setreg(bas, REG_FCR, ns8250->fcr);
-	uart_barrier(bas);
-	ar933x_bus_flush(sc, UART_FLUSH_RECEIVER|UART_FLUSH_TRANSMITTER);
+	/* XXX TODO: enable the host interrupt now */
 
-	if (ns8250->mcr & MCR_DTR)
-		sc->sc_hwsig |= SER_DTR;
-	if (ns8250->mcr & MCR_RTS)
-		sc->sc_hwsig |= SER_RTS;
-	ar933x_bus_getsig(sc);
-
-	ar933x_clrint(bas);
-	ns8250->ier = uart_getreg(bas, REG_IER) & ns8250->ier_mask;
-	ns8250->ier |= ns8250->ier_rxbits;
-	uart_setreg(bas, REG_IER, ns8250->ier);
-	uart_barrier(bas);
-#endif
 	return (0);
 }
 
@@ -472,54 +410,44 @@ ar933x_bus_detach(struct uart_softc *sc)
 	uart_barrier(bas);
 	ar933x_clrint(bas);
 #endif
+
+	/* XXX TODO: Disable all interrupts */
+	/* XXX TODO: Disable the host interrupt */
+
 	return (0);
 }
 
 static int
 ar933x_bus_flush(struct uart_softc *sc, int what)
 {
-#if 0
-	struct ar933x_softc *ns8250 = (struct ar933x_softc*)sc;
 	struct uart_bas *bas;
-	int error;
 
 	bas = &sc->sc_bas;
 	uart_lock(sc->sc_hwmtx);
-	if (sc->sc_rxfifosz > 1) {
-		ar933x_flush(bas, what);
-		uart_setreg(bas, REG_FCR, ns8250->fcr);
-		uart_barrier(bas);
-		error = 0;
-	} else
-		error = ar933x_drain(bas, what);
+	ar933x_drain(bas, what);
 	uart_unlock(sc->sc_hwmtx);
-	return (error);
-#endif
-	return (ENXIO);
+
+	return (0);
 }
 
 static int
 ar933x_bus_getsig(struct uart_softc *sc)
 {
-#if 0
-	uint32_t new, old, sig;
-	uint8_t msr;
+	uint32_t sig = sc->sc_hwsig;
+
+	/*
+	 * For now, let's just return that DSR/DCD/CTS is asserted.
+	 *
+	 * XXX TODO: actually verify whether this is correct!
+	 */
+	SIGCHG(1, sig, SER_DSR, SER_DDSR);
+	SIGCHG(1, sig, SER_CTS, SER_DCTS);
+	SIGCHG(1, sig, SER_DCD, SER_DDCD);
+	SIGCHG(1, sig,  SER_RI,  SER_DRI);
+
+	sc->sc_hwsig = sig & ~SER_MASK_DELTA;
 
-	do {
-		old = sc->sc_hwsig;
-		sig = old;
-		uart_lock(sc->sc_hwmtx);
-		msr = uart_getreg(&sc->sc_bas, REG_MSR);
-		uart_unlock(sc->sc_hwmtx);
-		SIGCHG(msr & MSR_DSR, sig, SER_DSR, SER_DDSR);
-		SIGCHG(msr & MSR_CTS, sig, SER_CTS, SER_DCTS);
-		SIGCHG(msr & MSR_DCD, sig, SER_DCD, SER_DDCD);
-		SIGCHG(msr & MSR_RI,  sig, SER_RI,  SER_DRI);
-		new = sig & ~SER_MASK_DELTA;
-	} while (!atomic_cmpset_32(&sc->sc_hwsig, old, new));
 	return (sig);
-#endif
-	return (0);
 }
 
 static int
@@ -598,52 +526,47 @@ ar933x_bus_ioctl(struct uart_softc *sc, 
 	return (ENXIO);
 }
 
+/*
+ * Bus interrupt handler.
+ *
+ * For now, system interrupts are disabled.
+ * So this is just called from a callout in uart_core.c
+ * to poll various state.
+ */
 static int
 ar933x_bus_ipend(struct uart_softc *sc)
 {
-#if 0
-	struct uart_bas *bas;
-	struct ar933x_softc *ns8250;
-	int ipend;
-	uint8_t iir, lsr;
+	struct uart_bas *bas = &sc->sc_bas;
+	int ipend = 0;
 
-	ns8250 = (struct ar933x_softc *)sc;
-	bas = &sc->sc_bas;
 	uart_lock(sc->sc_hwmtx);
-	iir = uart_getreg(bas, REG_IIR);
-	if (iir & IIR_NOPEND) {
-		uart_unlock(sc->sc_hwmtx);
-		return (0);
-	}
-	ipend = 0;
-	if (iir & IIR_RXRDY) {
-		lsr = uart_getreg(bas, REG_LSR);
-		if (lsr & LSR_OE)
-			ipend |= SER_INT_OVERRUN;
-		if (lsr & LSR_BI)
-			ipend |= SER_INT_BREAK;
-		if (lsr & LSR_RXRDY)
-			ipend |= SER_INT_RXREADY;
-	} else {
-		if (iir & IIR_TXRDY) {
+
+	/*
+	 * Always notify the upper layer if RX is ready.
+	 */
+	if (ar933x_rxready(bas)) {
+		ipend |= SER_INT_RXREADY;
+	}
+	/*
+	 * 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)) {
 			ipend |= SER_INT_TXIDLE;
-			uart_setreg(bas, REG_IER, ns8250->ier);
-		} else
+		} else {
 			ipend |= SER_INT_SIGCHG;
+		}
 	}
-	if (ipend == 0)
-		ar933x_clrint(bas);
+
 	uart_unlock(sc->sc_hwmtx);
 	return (ipend);
-#endif
-	return (0);
 }
 
 static int
 ar933x_bus_param(struct uart_softc *sc, int baudrate, int databits,
     int stopbits, int parity)
 {
-#if 0
 	struct uart_bas *bas;
 	int error;
 
@@ -652,193 +575,63 @@ ar933x_bus_param(struct uart_softc *sc, 
 	error = ar933x_param(bas, baudrate, databits, stopbits, parity);
 	uart_unlock(sc->sc_hwmtx);
 	return (error);
-#endif
-	return (ENXIO);
 }
 
 static int
 ar933x_bus_probe(struct uart_softc *sc)
 {
-#if 0
-	struct ar933x_softc *ns8250;
 	struct uart_bas *bas;
-	int count, delay, error, limit;
-	uint8_t lsr, mcr, ier;
+	int error;
 
-	ns8250 = (struct ar933x_softc *)sc;
 	bas = &sc->sc_bas;
 
 	error = ar933x_probe(bas);
 	if (error)
 		return (error);
 
-	mcr = MCR_IE;
-	if (sc->sc_sysdev == NULL) {
-		/* By using ar933x_init() we also set DTR and RTS. */
-		ar933x_init(bas, 115200, 8, 1, UART_PARITY_NONE);
-	} else
-		mcr |= MCR_DTR | MCR_RTS;
-
-	error = ar933x_drain(bas, UART_DRAIN_TRANSMITTER);
-	if (error)
-		return (error);
-
-	/*
-	 * Set loopback mode. This avoids having garbage on the wire and
-	 * also allows us send and receive data. We set DTR and RTS to
-	 * avoid the possibility that automatic flow-control prevents
-	 * any data from being sent.
-	 */
-	uart_setreg(bas, REG_MCR, MCR_LOOPBACK | MCR_IE | MCR_DTR | MCR_RTS);
-	uart_barrier(bas);
-
-	/*
-	 * Enable FIFOs. And check that the UART has them. If not, we're
-	 * done. Since this is the first time we enable the FIFOs, we reset
-	 * them.
-	 */
-	uart_setreg(bas, REG_FCR, FCR_ENABLE);
-	uart_barrier(bas);
-	if (!(uart_getreg(bas, REG_IIR) & IIR_FIFO_MASK)) {
-		/*
-		 * NS16450 or INS8250. We don't bother to differentiate
-		 * between them. They're too old to be interesting.
-		 */
-		uart_setreg(bas, REG_MCR, mcr);
-		uart_barrier(bas);
-		sc->sc_rxfifosz = sc->sc_txfifosz = 1;
-		device_set_desc(sc->sc_dev, "8250 or 16450 or compatible");
-		return (0);
-	}
-
-	uart_setreg(bas, REG_FCR, FCR_ENABLE | FCR_XMT_RST | FCR_RCV_RST);
-	uart_barrier(bas);
-
-	count = 0;
-	delay = ar933x_delay(bas);
-
-	/* We have FIFOs. Drain the transmitter and receiver. */
-	error = ar933x_drain(bas, UART_DRAIN_RECEIVER|UART_DRAIN_TRANSMITTER);
-	if (error) {
-		uart_setreg(bas, REG_MCR, mcr);
-		uart_setreg(bas, REG_FCR, 0);
-		uart_barrier(bas);
-		goto describe;
-	}
-
-	/*
-	 * We should have a sufficiently clean "pipe" to determine the
-	 * size of the FIFOs. We send as much characters as is reasonable
-	 * and wait for the overflow bit in the LSR register to be
-	 * asserted, counting the characters as we send them. Based on
-	 * that count we know the FIFO size.
-	 */
-	do {
-		uart_setreg(bas, REG_DATA, 0);
-		uart_barrier(bas);
-		count++;
-
-		limit = 30;
-		lsr = 0;
-		/*
-		 * LSR bits are cleared upon read, so we must accumulate
-		 * them to be able to test LSR_OE below.
-		 */
-		while (((lsr |= uart_getreg(bas, REG_LSR)) & LSR_TEMT) == 0 &&
-		    --limit)
-			DELAY(delay);
-		if (limit == 0) {
-			ier = uart_getreg(bas, REG_IER) & ns8250->ier_mask;
-			uart_setreg(bas, REG_IER, ier);
-			uart_setreg(bas, REG_MCR, mcr);
-			uart_setreg(bas, REG_FCR, 0);
-			uart_barrier(bas);
-			count = 0;
-			goto describe;
-		}
-	} while ((lsr & LSR_OE) == 0 && count < 130);
-	count--;
-
-	uart_setreg(bas, REG_MCR, mcr);
-
 	/* Reset FIFOs. */
-	ar933x_flush(bas, UART_FLUSH_RECEIVER|UART_FLUSH_TRANSMITTER);
-
- describe:
-	if (count >= 14 && count <= 16) {
-		sc->sc_rxfifosz = 16;
-		device_set_desc(sc->sc_dev, "16550 or compatible");
-	} else if (count >= 28 && count <= 32) {
-		sc->sc_rxfifosz = 32;
-		device_set_desc(sc->sc_dev, "16650 or compatible");
-	} else if (count >= 56 && count <= 64) {
-		sc->sc_rxfifosz = 64;
-		device_set_desc(sc->sc_dev, "16750 or compatible");
-	} else if (count >= 112 && count <= 128) {
-		sc->sc_rxfifosz = 128;
-		device_set_desc(sc->sc_dev, "16950 or compatible");
-	} else {
-		sc->sc_rxfifosz = 16;
-		device_set_desc(sc->sc_dev,
-		    "Non-standard ns8250 class UART with FIFOs");
-	}
+	ar933x_drain(bas, UART_FLUSH_RECEIVER|UART_FLUSH_TRANSMITTER);
 
-	/*
-	 * Force the Tx FIFO size to 16 bytes for now. We don't program the
-	 * Tx trigger. Also, we assume that all data has been sent when the
-	 * interrupt happens.
-	 */
+	/* XXX TODO: actually find out what the FIFO depth is! */
+	sc->sc_rxfifosz = 16;
 	sc->sc_txfifosz = 16;
 
-#if 0
-	/*
-	 * XXX there are some issues related to hardware flow control and
-	 * it's likely that uart(4) is the cause. This basicly needs more
-	 * investigation, but we avoid using for hardware flow control
-	 * until then.
-	 */
-	/* 16650s or higher have automatic flow control. */
-	if (sc->sc_rxfifosz > 16) {
-		sc->sc_hwiflow = 1;
-		sc->sc_hwoflow = 1;
-	}
-#endif
-#endif
 	return (0);
 }
 
 static int
 ar933x_bus_receive(struct uart_softc *sc)
 {
-#if 0
-	struct uart_bas *bas;
+	struct uart_bas *bas = &sc->sc_bas;
 	int xc;
-	uint8_t lsr;
 
-	bas = &sc->sc_bas;
 	uart_lock(sc->sc_hwmtx);
-	lsr = uart_getreg(bas, REG_LSR);
-	while (lsr & LSR_RXRDY) {
+
+	/* Loop over until we are full, or no data is available */
+	while (ar933x_rxready(bas)) {
 		if (uart_rx_full(sc)) {
 			sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN;
 			break;
 		}
-		xc = uart_getreg(bas, REG_DATA);
-		if (lsr & LSR_FE)
-			xc |= UART_STAT_FRAMERR;
-		if (lsr & LSR_PE)
-			xc |= UART_STAT_PARERR;
+
+		/* Read the top of the RX FIFO */
+		xc = ar933x_getreg(bas, AR933X_UART_DATA_REG) & 0xff;
+
+		/* Remove that entry from said RX FIFO */
+		ar933x_setreg(bas, AR933X_UART_DATA_REG,
+		    AR933X_UART_DATA_RX_CSR);
+
+		/* XXX frame, parity error */
 		uart_rx_put(sc, xc);
-		lsr = uart_getreg(bas, REG_LSR);
-	}
-	/* Discard everything left in the Rx FIFO. */
-	while (lsr & LSR_RXRDY) {
-		(void)uart_getreg(bas, REG_DATA);
-		uart_barrier(bas);
-		lsr = uart_getreg(bas, REG_LSR);
 	}
+
+	/*
+	 * XXX TODO: Discard everything left in the Rx FIFO?
+	 * XXX only if we've hit an overrun condition?
+	 */
+
 	uart_unlock(sc->sc_hwmtx);
-#endif
+
 	return (0);
 }
 
@@ -879,23 +672,26 @@ ar933x_bus_setsig(struct uart_softc *sc,
 static int
 ar933x_bus_transmit(struct uart_softc *sc)
 {
-#if 0
-	struct ar933x_softc *ns8250 = (struct ar933x_softc*)sc;
-	struct uart_bas *bas;
+	struct uart_bas *bas = &sc->sc_bas;
 	int i;
 
-	bas = &sc->sc_bas;
 	uart_lock(sc->sc_hwmtx);
-	while ((uart_getreg(bas, REG_LSR) & LSR_THRE) == 0)
-		;
-	uart_setreg(bas, REG_IER, ns8250->ier | IER_ETXRDY);
-	uart_barrier(bas);
+
+	/* XXX wait for FIFO to be ready? */
+
 	for (i = 0; i < sc->sc_txdatasz; i++) {
-		uart_setreg(bas, REG_DATA, sc->sc_txbuf[i]);
-		uart_barrier(bas);
+		/* Write the TX data */
+		ar933x_setreg(bas, AR933X_UART_DATA_REG,
+		    (sc->sc_txbuf[i] & 0xff) | AR933X_UART_DATA_TX_CSR);
 	}
+
+	/*
+	 * Inform the upper layer that we are presently transmitting
+	 * data to the hardware; this will be cleared when the
+	 * TXIDLE interrupt occurs.
+	 */
 	sc->sc_txbusy = 1;
 	uart_unlock(sc->sc_hwmtx);
-#endif
+
 	return (0);
 }

Modified: head/sys/mips/conf/AR933X_BASE.hints
==============================================================================
--- head/sys/mips/conf/AR933X_BASE.hints	Thu Apr  4 10:26:09 2013	(r249092)
+++ head/sys/mips/conf/AR933X_BASE.hints	Thu Apr  4 10:46:33 2013	(r249093)
@@ -14,7 +14,7 @@ hint.uart.0.at="apb0"
 # NB: This isn't an ns8250 UART
 hint.uart.0.maddr=0x18020000
 hint.uart.0.msize=0x18
-hint.uart.0.irq=3
+# hint.uart.0.irq=3
 
 #ehci - note the 0x100 offset for the AR913x/AR724x
 hint.ehci.0.at="nexus0"



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