Module Name:    src
Committed By:   chs
Date:           Sat Sep  7 18:55:29 UTC 2013

Modified Files:
        src/sys/dev/ic: smc91cxx.c

Log Message:
apply changes from Robert Sprowson in PR 47765 and PR 47788:
 - make sure we wait long enough after resetting the chip.
 - add the necessary delay after changing the FIFO pointer.
 - add a missing interrupt ACK.
 - fix padding of short and odd-length packets.

and a few more changes from me:
 - do 2-byte writes in most places even if SMC91CXX_NO_BYTE_WRITE
   is not defined.  the only ones still conditionalized are
   writing to the interrupt ack/mask registers.
 - the only big-endian front-end of this driver (on mac68k) uses
   a bus_space that does all the byte-swapping in that layer,
   so remove the explicit byte-swapping in the MI part.

tested on mac68k.


To generate a diff of this commit:
cvs rdiff -u -r1.84 -r1.85 src/sys/dev/ic/smc91cxx.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/dev/ic/smc91cxx.c
diff -u src/sys/dev/ic/smc91cxx.c:1.84 src/sys/dev/ic/smc91cxx.c:1.85
--- src/sys/dev/ic/smc91cxx.c:1.84	Tue Jun 11 16:57:05 2013
+++ src/sys/dev/ic/smc91cxx.c	Sat Sep  7 18:55:29 2013
@@ -1,4 +1,4 @@
-/*	$NetBSD: smc91cxx.c,v 1.84 2013/06/11 16:57:05 msaitoh Exp $	*/
+/*	$NetBSD: smc91cxx.c,v 1.85 2013/09/07 18:55:29 chs Exp $	*/
 
 /*-
  * Copyright (c) 1997 The NetBSD Foundation, Inc.
@@ -71,7 +71,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: smc91cxx.c,v 1.84 2013/06/11 16:57:05 msaitoh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: smc91cxx.c,v 1.85 2013/09/07 18:55:29 chs Exp $");
 
 #include "opt_inet.h"
 
@@ -126,7 +126,7 @@ __KERNEL_RCSID(0, "$NetBSD: smc91cxx.c,v
 /* XXX Hardware padding doesn't work yet(?) */
 #define	SMC91CXX_SW_PAD
 
-const char *smc91cxx_idstrs[] = {
+static const char *smc91cxx_idstrs[] = {
 	NULL,				/* 0 */
 	NULL,				/* 1 */
 	NULL,				/* 2 */
@@ -146,7 +146,7 @@ const char *smc91cxx_idstrs[] = {
 };
 
 /* Supported media types. */
-const int smc91cxx_media[] = {
+static const int smc91cxx_media[] = {
 	IFM_ETHER|IFM_10_T,
 	IFM_ETHER|IFM_10_5,
 };
@@ -158,7 +158,7 @@ const int smc91cxx_media[] = {
 u_int32_t smc91cxx_mii_bitbang_read(device_t);
 void smc91cxx_mii_bitbang_write(device_t, u_int32_t);
 
-const struct mii_bitbang_ops smc91cxx_mii_bitbang_ops = {
+static const struct mii_bitbang_ops smc91cxx_mii_bitbang_ops = {
 	smc91cxx_mii_bitbang_read,
 	smc91cxx_mii_bitbang_write,
 	{
@@ -208,12 +208,8 @@ smc91cxx_intr_mask_write(bus_space_tag_t
 {
 	KDASSERT((mask & IM_ERCV_INT) == 0);
 #ifdef SMC91CXX_NO_BYTE_WRITE
-#if BYTE_ORDER == LITTLE_ENDIAN
 	bus_space_write_2(bst, bsh, INTR_STAT_REG_B, mask << 8);
 #else
-	bus_space_write_2(bst, bsh, INTR_STAT_REG_B, mask);
-#endif
-#else
 	bus_space_write_1(bst, bsh, INTR_MASK_REG_B, mask);
 #endif
 	KDASSERT(!(bus_space_read_1(bst, bsh, INTR_MASK_REG_B) & IM_ERCV_INT));
@@ -221,18 +217,12 @@ smc91cxx_intr_mask_write(bus_space_tag_t
 
 static inline void
 smc91cxx_intr_ack_write(bus_space_tag_t bst, bus_space_handle_t bsh,
-	uint8_t mask)
+	uint8_t ack, uint8_t mask)
 {
 #ifdef SMC91CXX_NO_BYTE_WRITE
-#if BYTE_ORDER == LITTLE_ENDIAN
-	bus_space_write_2(bst, bsh, INTR_ACK_REG_B,
-	    mask | (bus_space_read_2(bst, bsh, INTR_ACK_REG_B) & 0xff00));
+	bus_space_write_2(bst, bsh, INTR_ACK_REG_B, ack | (mask << 8));
 #else
-	bus_space_write_2(bst, bsh, INTR_ACK_REG_B,
-	    (mask << 8) | (bus_space_read_2(bst, bsh, INTR_ACK_REG_B) & 0xff));
-#endif
-#else
-	bus_space_write_1(bst, bsh, INTR_ACK_REG_B, mask);
+	bus_space_write_1(bst, bsh, INTR_ACK_REG_B, ack);
 #endif
 	KDASSERT(!(bus_space_read_1(bst, bsh, INTR_MASK_REG_B) & IM_ERCV_INT));
 }
@@ -254,7 +244,8 @@ smc91cxx_attach(struct smc91cxx_softc *s
 	tmp = bus_space_read_2(bst, bsh, BANK_SELECT_REG_W);
 	/* check magic number */
 	if ((tmp & BSR_DETECT_MASK) != BSR_DETECT_VALUE) {
-		aprint_error_dev(sc->sc_dev, "failed to detect chip, bsr=%04x\n", tmp);
+		aprint_error_dev(sc->sc_dev,
+		     "failed to detect chip, bsr=%04x\n", tmp);
 		return;
 	}
 
@@ -285,7 +276,8 @@ smc91cxx_attach(struct smc91cxx_softc *s
 		scale = MIR_SCALE_91C111;
 	}
 	memsize = bus_space_read_2(bst, bsh, MEM_INFO_REG_W) & MIR_TOTAL_MASK;
-	if (memsize == 255) memsize++;
+	if (memsize == 255)
+		memsize++;
 	memsize *= scale * mult;
 
 	format_bytes(pbuf, sizeof(pbuf), memsize);
@@ -344,7 +336,8 @@ smc91cxx_attach(struct smc91cxx_softc *s
 		if (tmp & CR_MII_SELECT) {
 			aprint_normal("default media MII");
 			if (sc->sc_chipid == CHIP_91C111) {
-				aprint_normal(" (%s PHY)\n", (tmp & CR_AUI_SELECT) ?
+				aprint_normal(" (%s PHY)\n",
+				    (tmp & CR_AUI_SELECT) ?
 				    "external" : "internal");
 				sc->sc_internal_phy = !(tmp & CR_AUI_SELECT);
 			} else
@@ -373,7 +366,8 @@ smc91cxx_attach(struct smc91cxx_softc *s
 		}
 		/*FALLTHROUGH*/
 	default:
-		aprint_normal("default media %s\n", (aui = (tmp & CR_AUI_SELECT)) ?
+		aprint_normal("default media %s\n",
+		    (aui = (tmp & CR_AUI_SELECT)) ?
 		    "AUI" : "UTP");
 		for (i = 0; i < NSMC91CxxMEDIA; i++)
 			ifmedia_add(ifm, smc91cxx_media[i], 0, NULL);
@@ -494,16 +488,14 @@ smc91cxx_init(struct smc91cxx_softc *sc)
 
 	/*
 	 * This resets the registers mostly to defaults, but doesn't
-	 * affect the EEPROM.  After the reset cycle, we pause briefly
-	 * for the chip to recover.
-	 *
-	 * XXX how long are we really supposed to delay?  --thorpej
+	 * affect the EEPROM.  The longest reset recovery time of those devices
+	 * supported is the 91C111. Section 7.8 of its datasheet asks for 50ms.
 	 */
 	SMC_SELECT_BANK(sc, 0);
 	bus_space_write_2(bst, bsh, RECV_CONTROL_REG_W, RCR_SOFTRESET);
-	delay(100);
+	delay(5);
 	bus_space_write_2(bst, bsh, RECV_CONTROL_REG_W, 0);
-	delay(200);
+	delay(50000);
 
 	bus_space_write_2(bst, bsh, TXMIT_CONTROL_REG_W, 0);
 
@@ -654,20 +646,21 @@ smc91cxx_start(struct ifnet *ifp)
 	 */
 	for (len = 0; m != NULL; m = m->m_next)
 		len += m->m_len;
-	pad = (len & 1);
 
 	/*
 	 * We drop packets that are too large.  Perhaps we should
 	 * truncate them instead?
 	 */
-	if ((len + pad) > (ETHER_MAX_LEN - ETHER_CRC_LEN)) {
-		printf("%s: large packet discarded\n", device_xname(sc->sc_dev));
+	if (len > (ETHER_MAX_LEN - ETHER_CRC_LEN)) {
+		printf("%s: large packet discarded\n",
+		    device_xname(sc->sc_dev));
 		ifp->if_oerrors++;
 		IFQ_DEQUEUE(&ifp->if_snd, m);
 		m_freem(m);
 		goto readcheck;
 	}
 
+	pad = 0;
 #ifdef SMC91CXX_SW_PAD
 	/*
 	 * Not using hardware padding; pad to ETHER_MIN_LEN.
@@ -709,7 +702,7 @@ smc91cxx_start(struct ifnet *ifp)
 	if (packetno & ARR_FAILED || timo == 0) {
 		/*
 		 * No transmit memory is available.  Record the number
-		 * of requestd pages and enable the allocation completion
+		 * of requested pages and enable the allocation completion
 		 * interrupt.  Set up the watchdog timer in case we miss
 		 * the interrupt.  Mark the interface as active so that
 		 * no one else attempts to transmit while we're allocating
@@ -726,7 +719,7 @@ smc91cxx_start(struct ifnet *ifp)
 	/*
 	 * We have a packet number - set the data window.
 	 */
-	bus_space_write_1(bst, bsh, PACKET_NUM_REG_B, packetno);
+	bus_space_write_2(bst, bsh, PACKET_NUM_REG_B, packetno);
 
 	/*
 	 * Point to the beginning of the packet.
@@ -747,24 +740,17 @@ smc91cxx_start(struct ifnet *ifp)
 	IFQ_DEQUEUE(&ifp->if_snd, m);
 
 	/*
-	 * Push the packet out to the card.
+	 * Push the packet out to the card.  The copying function only does
+	 * whole words and returns the straggling byte (if any).
 	 */
 	oddbyte = smc91cxx_copy_tx_frame(sc, m);
 
 #ifdef SMC91CXX_SW_PAD
-#ifdef SMC91CXX_NO_BYTE_WRITE
-#if BYTE_ORDER == LITTLE_ENDIAN
 	if (pad > 1 && (pad & 1)) {
-		bus_space_write_2(bst, bsh, DATA_REG_W, oddbyte << 0);
+		bus_space_write_2(bst, bsh, DATA_REG_W, oddbyte);
 		oddbyte = 0;
+		pad -= 1;
 	}
-#else
-	if (pad > 1 && (pad & 1)) {
-		bus_space_write_2(bst, bsh, DATA_REG_W, oddbyte << 8);
-		oddbyte = 0;
-	}
-#endif
-#endif
 
 	/*
 	 * Push out padding.
@@ -775,23 +761,13 @@ smc91cxx_start(struct ifnet *ifp)
 	}
 #endif
 
-#ifdef SMC91CXX_NO_BYTE_WRITE
 	/*
 	 * Push out control byte and unused packet byte.  The control byte
-	 * is 0, meaning the packet is even lengthed and no special
-	 * CRC handling is necessary.
+	 * denotes whether this is an odd or even length packet, and that
+	 * no special CRC handling is necessary.
 	 */
-#if BYTE_ORDER == LITTLE_ENDIAN
-	bus_space_write_2(bst, bsh, DATA_REG_W,
-	    oddbyte | (pad ? (CTLB_ODD << 8) : 0));
-#else
 	bus_space_write_2(bst, bsh, DATA_REG_W,
-	    (oddbyte << 8) | (pad ? CTLB_ODD : 0));
-#endif
-#else
-	if (pad)
-		bus_space_write_1(bst, bsh, DATA_REG_B, 0);
-#endif
+	    oddbyte | ((length & 1) ? (CTLB_ODD << 8) : 0));
 
 	/*
 	 * Enable transmit interrupts and let the chip go.  Set a watchdog
@@ -812,7 +788,7 @@ smc91cxx_start(struct ifnet *ifp)
 
  readcheck:
 	/*
-	 * Check for incoming pcakets.  We don't want to overflow the small
+	 * Check for incoming packets.  We don't want to overflow the small
 	 * RX FIFO.  If nothing has arrived, attempt to queue another
 	 * transmit packet.
 	 */
@@ -894,10 +870,7 @@ smc91cxx_copy_tx_frame(struct smc91cxx_s
 			panic("smc91cxx_copy_tx_frame: p != lim");
 #endif
 	}
-#ifndef SMC91CXX_NO_BYTE_WRITE
-	if (leftover)
-		bus_space_write_1(bst, bsh, DATA_REG_B, dbuf);
-#endif
+
 	return dbuf;
 }
 
@@ -913,9 +886,7 @@ smc91cxx_intr(void *arg)
 	bus_space_handle_t bsh = sc->sc_bsh;
 	u_int8_t mask, interrupts, status;
 	u_int16_t packetno, tx_status, card_stats;
-#ifdef SMC91CXX_NO_BYTE_WRITE
 	u_int16_t v;
-#endif
 
 	if ((sc->sc_flags & SMC_FLAGS_ENABLED) == 0 ||
 	    !device_is_active(sc->sc_dev))
@@ -926,30 +897,15 @@ smc91cxx_intr(void *arg)
 	/*
 	 * Obtain the current interrupt status and mask.
 	 */
-#ifdef SMC91CXX_NO_BYTE_WRITE
 	v = bus_space_read_2(bst, bsh, INTR_STAT_REG_B);
 
 	/*
 	 * Get the set of interrupt which occurred and eliminate any
 	 * which are not enabled.
 	 */
-#if BYTE_ORDER == LITTLE_ENDIAN
 	mask = v >> 8;
 	interrupts = v & 0xff;
-#else
-	interrupts = v >> 8;
-	mask = v & 0xff;
-#endif
 	KDASSERT(mask == sc->sc_intmask);
-#else
-	mask = bus_space_read_1(bst, bsh, INTR_MASK_REG_B);
-
-	/*
-	 * 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);
-#endif
 	status = interrupts & mask;
 
 	/* Ours? */
@@ -965,7 +921,7 @@ smc91cxx_intr(void *arg)
 	 * Receive overrun interrupts.
 	 */
 	if (status & IM_RX_OVRN_INT) {
-		smc91cxx_intr_ack_write(bst, bsh, IM_RX_OVRN_INT);
+		smc91cxx_intr_ack_write(bst, bsh, IM_RX_OVRN_INT, 0);
 		ifp->if_ierrors++;
 	}
 
@@ -973,13 +929,6 @@ smc91cxx_intr(void *arg)
 	 * Receive interrupts.
 	 */
 	if (status & IM_RCV_INT) {
-#if 1 /* DIAGNOSTIC */
-		packetno = bus_space_read_2(bst, bsh, FIFO_PORTS_REG_W);
-		if (packetno & FIFO_REMPTY) {
-			aprint_error_dev(sc->sc_dev, "receive interrupt on empty fifo\n");
-			goto out;
-		} else
-#endif
 		smc91cxx_read(sc);
 	}
 
@@ -1011,7 +960,7 @@ smc91cxx_intr(void *arg)
 	 * mode.
 	 */
 	if (status & IM_TX_INT) {
-		smc91cxx_intr_ack_write(bst, bsh, IM_TX_INT);
+		smc91cxx_intr_ack_write(bst, bsh, IM_TX_INT, 0);
 
 		packetno = bus_space_read_2(bst, bsh, FIFO_PORTS_REG_W) &
 		    FIFO_TX_MASK;
@@ -1019,13 +968,15 @@ smc91cxx_intr(void *arg)
 		/*
 		 * Select this as the packet to read from.
 		 */
-		bus_space_write_1(bst, bsh, PACKET_NUM_REG_B, packetno);
+		bus_space_write_2(bst, bsh, PACKET_NUM_REG_B, packetno);
 
 		/*
-		 * Position the pointer to the beginning of the packet.
+		 * Position the pointer to the beginning of the packet, wait
+		 * for preload.
 		 */
 		bus_space_write_2(bst, bsh, POINTER_REG_W,
 		    PTR_AUTOINC | PTR_READ /* | 0x0000 */);
+		delay(1);
 
 		/*
 		 * Fetch the TX status word.  This will be a copy of
@@ -1077,7 +1028,7 @@ smc91cxx_intr(void *arg)
 	 * update transmit statistics from the card.
 	 */
 	if (status & IM_TX_EMPTY_INT) {
-		smc91cxx_intr_ack_write(bst, bsh, IM_TX_EMPTY_INT);
+		smc91cxx_intr_ack_write(bst, bsh, IM_TX_EMPTY_INT, 0);
 
 		/* Disable this interrupt. */
 		mask &= ~IM_TX_EMPTY_INT;
@@ -1097,11 +1048,16 @@ smc91cxx_intr(void *arg)
 		ifp->if_timer = 0;
 	}
 
+	/*
+	 * Internal PHY status change
+	 */
 	if (sc->sc_chipid == CHIP_91C111 && sc->sc_internal_phy &&
 	    (status & IM_MD_INT)) {
+
 		/*
 		 * Internal PHY status change
 		 */
+		smc91cxx_intr_ack_write(bst, bsh, IM_MD_INT, 0);
 		mii_pollstat(&sc->sc_mii);
 	}
 
@@ -1118,7 +1074,6 @@ smc91cxx_intr(void *arg)
 	 */
 	smc91cxx_start(ifp);
 
-out:
 	/*
 	 * Reenable the interrupts we wish to receive now that processing
 	 * is complete.
@@ -1147,6 +1102,7 @@ smc91cxx_read(struct smc91cxx_softc *sc)
 	u_int16_t status, packetno, packetlen;
 	u_int8_t *data;
 	u_int32_t dr;
+	bool first = true;
 
  again:
 	/*
@@ -1155,28 +1111,25 @@ smc91cxx_read(struct smc91cxx_softc *sc)
 	 * in FIFO_PORTS_REG_W, FIFO_RX_MASK.
 	 */
 	packetno = bus_space_read_2(bst, bsh, FIFO_PORTS_REG_W);
-	if (packetno & FIFO_REMPTY)
+	if (packetno & FIFO_REMPTY) {
+		if (first) {
+			aprint_error_dev(sc->sc_dev,
+			    "receive interrupt on empty fifo\n");
+		}
 		return;
+	}
+	first = false;
 
 	bus_space_write_2(bst, bsh, POINTER_REG_W,
 	    PTR_READ | PTR_RCV | PTR_AUTOINC /* | 0x0000 */);
+	delay(1);
 
 	/*
 	 * First two words are status and packet length.
 	 */
-	if ((sc->sc_flags & SMC_FLAGS_32BIT_READ) == 0) {
-		status = bus_space_read_2(bst, bsh, DATA_REG_W);
-		packetlen = bus_space_read_2(bst, bsh, DATA_REG_W);
-	} else {
-		dr = bus_space_read_4(bst, bsh, DATA_REG_W);
-#if BYTE_ORDER == LITTLE_ENDIAN
-		status = (u_int16_t)dr;
-		packetlen = (u_int16_t)(dr >> 16);
-#else
-		packetlen = (u_int16_t)dr;
-		status = (u_int16_t)(dr >> 16);
-#endif
-	}
+	dr = bus_space_read_4(bst, bsh, DATA_REG_W);
+	status = (u_int16_t)dr;
+	packetlen = (u_int16_t)(dr >> 16);
 
 	packetlen &= RLEN_MASK;
 	if (packetlen < ETHER_MIN_LEN - ETHER_CRC_LEN + 6 || packetlen > 1534) {
@@ -1221,7 +1174,8 @@ smc91cxx_read(struct smc91cxx_softc *sc)
 	if ((m->m_flags & M_EXT) == 0) {
 		m_freem(m);
 		ifp->if_ierrors++;
-		aprint_error_dev(sc->sc_dev, "can't allocate cluster for incoming packet\n");
+		aprint_error_dev(sc->sc_dev,
+		     "can't allocate cluster for incoming packet\n");
 		goto out;
 	}
 
@@ -1235,7 +1189,8 @@ smc91cxx_read(struct smc91cxx_softc *sc)
 
 		eh = mtod(m, struct ether_header *);
 		data = mtod(m, u_int8_t *);
-		KASSERT(trunc_page((uintptr_t)data) == trunc_page((uintptr_t)data + packetlen - 1));
+		KASSERT(trunc_page((uintptr_t)data) ==
+			trunc_page((uintptr_t)data + packetlen - 1));
 		if (packetlen > 1)
 			bus_space_read_multi_stream_2(bst, bsh, DATA_REG_W,
 			    (u_int16_t *)data, packetlen >> 1);
@@ -1244,12 +1199,11 @@ smc91cxx_read(struct smc91cxx_softc *sc)
 			*data = bus_space_read_1(bst, bsh, DATA_REG_B);
 		}
 	} else {
-		u_int8_t *dp;
-
 		m->m_data = (void *) ALIGN(mtod(m, void *));
 		eh = mtod(m, struct ether_header *);
-		dp = data = mtod(m, u_int8_t *);
-		KASSERT(trunc_page((uintptr_t)data) == trunc_page((uintptr_t)data + packetlen - 1));
+		data = mtod(m, u_int8_t *);
+		KASSERT(trunc_page((uintptr_t)data) ==
+			trunc_page((uintptr_t)data + packetlen - 1));
 		if (packetlen > 3)
 			bus_space_read_multi_stream_4(bst, bsh, DATA_REG_W,
 			    (u_int32_t *)data, packetlen >> 2);
@@ -1298,9 +1252,6 @@ smc91cxx_read(struct smc91cxx_softc *sc)
 	/*
 	 * Check for another packet.
 	 */
-	packetno = bus_space_read_2(bst, bsh, FIFO_PORTS_REG_W);
-	if (packetno & FIFO_REMPTY)
-		return;
 	goto again;
 }
 
@@ -1512,7 +1463,6 @@ smc91cxx_detach(device_t self, int flags
 	if ((sc->sc_flags & SMC_FLAGS_ATTACHED) == 0)
 		return (0);
 
-
 	/* smc91cxx_disable() checks SMC_FLAGS_ENABLED */
 	smc91cxx_disable(sc);
 
@@ -1615,4 +1565,3 @@ smc91cxx_tick(void *arg)
 
 	callout_reset(&sc->sc_mii_callout, hz, smc91cxx_tick, sc);
 }
-

Reply via email to