Subject: Re: smc91cxx send routine
To: Antti Kantee <pooka@cs.hut.fi>
From: Nathan J. Williams <nathanw@wasabisystems.com>
List: tech-kern
Date: 06/05/2005 16:28:32
Antti Kantee <pooka@cs.hut.fi> writes:

> Is that patch ok to commit, or was there a reason for doing two writes
> in the first place?

I did something similar for an evb with a 16-bit bus (plus a 32-bit
kluge) to one of these chips. I had trouble with byte-wite interrupt
registers, too. Try this:

--- smc91cxx.c	22 Aug 2004 20:43:09 -0000	1.1.1.7
+++ smc91cxx.c	21 Sep 2004 23:51:30 -0000	1.1.1.7.4.1
@@ -540,8 +540,10 @@
 
 	/*
 	 * Disable all interrupts.
+	 * Writes to both the acknowledge register and the mask register;
+	 * writing zero to the acknowledge register is a no-op.
 	 */
-	bus_space_write_1(bst, bsh, INTR_MASK_REG_B, 0);
+	bus_space_write_2(bst, bsh, INTR_STAT_REG_W, 0);
 
 	/*
 	 * On the 91c111, enable auto-negotiation, and set the LED
@@ -598,12 +600,13 @@
 	SMC_SELECT_BANK(sc, 2);
 
 	if (sc->sc_chipid == CHIP_91C111 && sc->sc_internal_phy) {
-		bus_space_write_1(bst, bsh, INTR_MASK_REG_B,
-		    IM_EPH_INT | IM_RX_OVRN_INT |
-		    IM_RCV_INT | IM_TX_INT | IM_MD_INT);
+		bus_space_write_2(bst, bsh, INTR_STAT_REG_W, 
+		    (IM_EPH_INT | IM_RX_OVRN_INT |
+		    IM_RCV_INT | IM_TX_INT | IM_MD_INT) << 8);
 	} else {
-		bus_space_write_1(bst, bsh, INTR_MASK_REG_B,
-		    IM_EPH_INT | IM_RX_OVRN_INT | IM_RCV_INT | IM_TX_INT);
+		bus_space_write_2(bst, bsh, INTR_STAT_REG_W,
+		    (IM_EPH_INT | IM_RX_OVRN_INT | IM_RCV_INT | IM_TX_INT)
+		    << 8);
 	}
 
 	/* Interface is now running, with no output active. */
@@ -636,7 +639,7 @@
 	bus_space_handle_t bsh = sc->sc_bsh;
 	u_int len;
 	struct mbuf *m;
-	u_int16_t length, npages;
+	u_int16_t length, npages, isr;
 	u_int8_t packetno;
 	int timo, pad;
 
@@ -697,7 +700,7 @@
 
 	timo = MEMORY_WAIT_TIME;
 	do {
-		if (bus_space_read_1(bst, bsh, INTR_STAT_REG_B) & IM_ALLOC_INT)
+		if (bus_space_read_2(bst, bsh, INTR_STAT_REG_W) & IM_ALLOC_INT)
 			break;
 		delay(1);
 	} while (--timo);
@@ -713,8 +716,9 @@
 		 * no one else attempts to transmit while we're allocating
 		 * memory.
 		 */
-		bus_space_write_1(bst, bsh, INTR_MASK_REG_B,
-		    bus_space_read_1(bst, bsh, INTR_MASK_REG_B) | IM_ALLOC_INT);
+		isr = bus_space_read_2(bst, bsh, INTR_STAT_REG_W) >> 8;
+		isr |= IM_ALLOC_INT;
+		bus_space_write_2(bst, bsh, INTR_STAT_REG_W, isr << 8);
 
 		ifp->if_timer = 5;
 		ifp->if_flags |= IFF_OACTIVE;
@@ -737,8 +741,7 @@
 	 * and the status word (set to zeros).
 	 */
 	bus_space_write_2(bst, bsh, DATA_REG_W, 0);
-	bus_space_write_1(bst, bsh, DATA_REG_B, (length + 6) & 0xff);
-	bus_space_write_1(bst, bsh, DATA_REG_B, ((length + 6) >> 8) & 0xff);
+	bus_space_write_2(bst, bsh, DATA_REG_W, (length + 6) & 0x7ff);
 
 	/*
 	 * Get the packet from the kernel.  This will include the Ethernet
@@ -774,9 +777,9 @@
 	 * Enable transmit interrupts and let the chip go.  Set a watchdog
 	 * in case we miss the interrupt.
 	 */
-	bus_space_write_1(bst, bsh, INTR_MASK_REG_B,
-	    bus_space_read_1(bst, bsh, INTR_MASK_REG_B) |
-	    IM_TX_INT | IM_TX_EMPTY_INT);
+	isr = bus_space_read_2(bst, bsh, INTR_STAT_REG_W) >> 8;
+	isr |= IM_TX_INT | IM_TX_EMPTY_INT;
+	bus_space_write_2(bst, bsh, INTR_STAT_REG_W, isr << 8);
 
 	bus_space_write_2(bst, bsh, MMU_CMD_REG_W, MMUCR_ENQUEUE);
 
@@ -852,9 +855,12 @@
 				dbuf = *p++;
 				len--;
 				leftover = 1;
-			} else {
+			} else if ((len < 4)
+			    || ((long) p & 2)
+			    || ((sc->sc_flags & SMC_FLAGS_32BIT_DATA) == 0)) 
+				{
 				/*
-				 * Aligned data.  This is the case we like.
+				 * 16-bit aligned data. 
 				 *
 				 * Write-region out as much as we can, then
 				 * buffer the remaining byte (if any).
@@ -868,6 +874,15 @@
 				if (leftover)
 					dbuf = *p++;
 				len = 0;
+			} else {
+				/* 32-bit aligned data */
+				leftover = len & 3;
+				len &= ~3;
+				bus_space_write_multi_stream_4(sc->sc_d32t,
+				    sc->sc_d32h, 0, (u_int32_t *)p, len >> 2);
+				p += len;
+				len = leftover;
+				leftover = 0;
 			}
 		}
 		if (len < 0)
@@ -893,7 +908,7 @@
 	bus_space_tag_t bst = sc->sc_bst;
 	bus_space_handle_t bsh = sc->sc_bsh;
 	u_int8_t mask, interrupts, status;
-	u_int16_t packetno, tx_status, card_stats;
+	u_int16_t packetno, tx_status, card_stats, isr;
 
 	if ((sc->sc_flags & SMC_FLAGS_ENABLED) == 0 ||
 	    (sc->sc_dev.dv_flags & DVF_ACTIVE) == 0)
@@ -902,15 +917,16 @@
 	SMC_SELECT_BANK(sc, 2);
 
 	/*
-	 * Obtain the current interrupt mask.
+	 * Obtain the current interrupt mask and interrupts which occurred.
 	 */
-	mask = bus_space_read_1(bst, bsh, INTR_MASK_REG_B);
+	isr = bus_space_read_2(bst, bsh, INTR_STAT_REG_W);
+	mask = isr >> 8;
 
 	/*
 	 * Get the set of interrupt which occurred and eliminate any
 	 * which are not enabled.
 	 */
-	interrupts = bus_space_read_1(bst, bsh, INTR_STAT_REG_B);
+	interrupts = isr & 0xff;
 	status = interrupts & mask;
 
 	/* Ours? */
@@ -920,13 +936,16 @@
 	/*
 	 * It's ours; disable all interrupts while we process them.
 	 */
-	bus_space_write_1(bst, bsh, INTR_MASK_REG_B, 0);
+	bus_space_write_2(bst, bsh, INTR_STAT_REG_W, 0);
 
 	/*
 	 * Receive overrun interrupts.
 	 */
 	if (status & IM_RX_OVRN_INT) {
-		bus_space_write_1(bst, bsh, INTR_ACK_REG_B, IM_RX_OVRN_INT);
+		isr = bus_space_read_2(bst, bsh, INTR_STAT_REG_W);
+		isr = (isr & 0xff00) | IM_RX_OVRN_INT;
+		/* Acknowledge */
+		bus_space_write_2(bst, bsh, INTR_STAT_REG_W, isr);
 		ifp->if_ierrors++;
 	}
 
@@ -970,7 +989,10 @@
 	 * mode.
 	 */
 	if (status & IM_TX_INT) {
-		bus_space_write_1(bst, bsh, INTR_ACK_REG_B, IM_TX_INT);
+		isr = bus_space_read_2(bst, bsh, INTR_STAT_REG_W);
+		isr = (isr & 0xff00) | IM_TX_INT;
+		/* Acknowledge */
+		bus_space_write_2(bst, bsh, INTR_STAT_REG_W, isr);
 
 		packetno = bus_space_read_2(bst, bsh, FIFO_PORTS_REG_W) &
 		    FIFO_TX_MASK;
@@ -1029,7 +1051,10 @@
 	 * update transmit statistics from the card.
 	 */
 	if (status & IM_TX_EMPTY_INT) {
-		bus_space_write_1(bst, bsh, INTR_ACK_REG_B, IM_TX_EMPTY_INT);
+		isr = bus_space_read_2(bst, bsh, INTR_STAT_REG_W);
+		isr = (isr & 0xff00) | IM_TX_EMPTY_INT;
+		/* Acknowledge */
+		bus_space_write_2(bst, bsh, INTR_STAT_REG_W, isr);
 
 		/* Disable this interrupt. */
 		mask &= ~IM_TX_EMPTY_INT;
@@ -1074,8 +1099,8 @@
 	 * Reenable the interrupts we wish to receive now that processing
 	 * is complete.
 	 */
-	mask |= bus_space_read_1(bst, bsh, INTR_MASK_REG_B);
-	bus_space_write_1(bst, bsh, INTR_MASK_REG_B, mask);
+	mask |= bus_space_read_2(bst, bsh, INTR_STAT_REG_W) >> 8;
+	bus_space_write_2(bst, bsh, INTR_STAT_REG_W, mask << 8);
 
 #if NRND > 0
 	if (status)
@@ -1176,7 +1201,32 @@
 	 * Pull the packet off the interface.  Make sure the payload
 	 * is aligned.
 	 */
-	if ((sc->sc_flags & SMC_FLAGS_32BIT_READ) == 0) {
+	if (sc->sc_flags & SMC_FLAGS_32BIT_DATA) {
+		int len = packetlen;
+		m->m_data = (caddr_t) ALIGN(mtod(m, caddr_t) +
+		    sizeof(struct ether_header)) - sizeof(struct ether_header);
+		eh = mtod(m, struct ether_header *);
+		data = mtod(m, u_int8_t *);
+
+		*(u_int16_t *)data = bus_space_read_2(bst, bsh, DATA_REG_W);
+		data += 2;
+		len -= 2;
+		/* Now 4-byte aligned */
+		
+		if (len > 3) {
+			bus_space_read_multi_stream_4(sc->sc_d32t,
+			    sc->sc_d32h, 0, (u_int32_t *)data, len >> 2);
+			data += len & ~3;
+		}
+		if (len & 2) {
+			*(u_int16_t *)data = 
+			    bus_space_read_2(bst, bsh, DATA_REG_W);
+			data += 2;
+		}
+		if (len & 1) {
+			*data = bus_space_read_1(bst, bsh, DATA_REG_B);
+		}
+	} else if ((sc->sc_flags & SMC_FLAGS_32BIT_READ) == 0) {
 		m->m_data = (caddr_t) ALIGN(mtod(m, caddr_t) +
 		    sizeof(struct ether_header)) - sizeof(struct ether_header);
 
@@ -1190,11 +1240,9 @@
 			*data = bus_space_read_1(bst, bsh, DATA_REG_B);
 		}
 	} else {
-		u_int8_t *dp;
-
 		m->m_data = (caddr_t) ALIGN(mtod(m, caddr_t));
 		eh = mtod(m, struct ether_header *);
-		dp = data = mtod(m, u_int8_t *);
+		data = mtod(m, u_int8_t *);
 		if (packetlen > 3)
 			bus_space_read_multi_stream_4(bst, bsh, DATA_REG_W,
 			    (u_int32_t *)data, packetlen >> 2);
@@ -1425,7 +1473,7 @@
 	 * Clear interrupt mask; disable all interrupts.
 	 */
 	SMC_SELECT_BANK(sc, 2);
-	bus_space_write_1(bst, bsh, INTR_MASK_REG_B, 0);
+	bus_space_write_2(bst, bsh, INTR_STAT_REG_W, 0);
 
 	/*
 	 * Disable transmitter and receiver.