patch-2.3.46 linux/drivers/net/8139too.c

Next file: linux/drivers/net/eepro100.c
Previous file: linux/drivers/net/3c505.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.45/linux/drivers/net/8139too.c linux/drivers/net/8139too.c
@@ -19,6 +19,11 @@
 		
 		David S. Miller - PCI DMA and softnet updates
 
+		Ernst Gill - fixes ported from BSD driver
+
+		Daniel Kobras - identified specific locations of
+			posted MMIO write bugginess
+
 -----------------------------------------------------------------------------
 
 				Theory of Operation
@@ -82,7 +87,7 @@
 #include <asm/io.h>
 
 
-#define RTL8139_VERSION "0.9.0"
+#define RTL8139_VERSION "0.9.2"
 #define RTL8139_MODULE_NAME "8139too"
 #define RTL8139_DRIVER_NAME   RTL8139_MODULE_NAME " Fast Ethernet driver " RTL8139_VERSION
 #define PFX RTL8139_MODULE_NAME ": "
@@ -154,15 +159,17 @@
 #define RTL8139_CAPS  HAS_CHIP_XCVR|HAS_LNK_CHNG
 
 typedef enum {
-	UNKNOWN = 0,
 	RTL8139,
 	RTL8139_CB,
 	SMC1211TX,
-	MPX5030,
+	/*MPX5030,*/
 	SIS900,
 	SIS7016,
+	DELTA8139,
+	ADDTRON8139,
 } chip_t;
 
+
 static struct {
 	chip_t chip;
 	const char *name;
@@ -170,19 +177,24 @@
 	{ RTL8139, "RealTek RTL8139 Fast Ethernet"},
 	{ RTL8139_CB, "RealTek RTL8139B PCI/CardBus"},
 	{ SMC1211TX, "SMC1211TX EZCard 10/100 (RealTek RTL8139)"},
-	{ MPX5030, "Accton MPX5030 (RealTek RTL8139)"},
+/*	{ MPX5030, "Accton MPX5030 (RealTek RTL8139)"},*/
 	{ SIS900, "SiS 900 (RealTek RTL8139) Fast Ethernet"},
 	{ SIS7016, "SiS 7016 (RealTek RTL8139) Fast Ethernet"},
+	{ DELTA8139, "Delta Electronics 8139 10/100BaseTX"},
+	{ ADDTRON8139, "Addtron Technolgy 8139 10/100BaseTX"},
 	{0,},
 };
 
+
 static struct pci_device_id rtl8139_pci_tbl[] __devinitdata = {
 	{0x10ec, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
 	{0x10ec, 0x8138, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139_CB },
 	{0x1113, 0x1211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, SMC1211TX },
-	{0x1113, 0x1211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MPX5030 },
+/*	{0x1113, 0x1211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MPX5030 },*/
 	{0x1039, 0x0900, PCI_ANY_ID, PCI_ANY_ID, 0, 0, SIS900 },
 	{0x1039, 0x7016, PCI_ANY_ID, PCI_ANY_ID, 0, 0, SIS7016 },
+	{0x1500, 0x1360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DELTA8139 },
+	{0x4033, 0x1360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ADDTRON8139 },
 	{0,},
 };
 MODULE_DEVICE_TABLE (pci, rtl8139_pci_tbl);
@@ -304,7 +316,7 @@
 #define PARA78_default	0x78fa8388
 #define PARA7c_default	0xcb38de43	/* param[0][3] */
 #define PARA7c_xxx		0xcb38de43
-unsigned long param[4][4] = {
+static const unsigned long param[4][4] = {
 	{0xcb39de43, 0xcb39ce43, 0xfb38de03, 0xcb38de43},
 	{0xcb39de43, 0xcb39ce43, 0xcb39ce83, 0xcb39ce83},
 	{0xcb39de43, 0xcb39ce43, 0xcb39ce83, 0xcb39ce83},
@@ -328,7 +340,6 @@
 	struct timer_list timer;	/* Media selection timer. */
 	unsigned char *rx_ring;
 	unsigned int cur_rx;	/* Index into the Rx buffer of next Rx pkt. */
-	unsigned int rx_config;
 	unsigned int cur_tx, dirty_tx, tx_flag;
 	/* The saved address of a sent-in-place packet/buffer, for skfree(). */
 	struct ring_info tx_info[NUM_TX_DESC];
@@ -370,13 +381,43 @@
 static inline u32 ether_crc (int length, unsigned char *data);
 static void rtl8139_set_rx_mode (struct net_device *dev);
 
-/* read after write, to avoid rtl8139 bug w/ posted MMIO writes */
-#define RTL_W8(reg, val8)	do { writeb (val8, ioaddr + reg); readb (ioaddr + reg); } while (0)
-#define RTL_W16(reg, val16)	do { writew (val16, ioaddr + reg); readw (ioaddr + reg); } while (0)
-#define RTL_W32(reg, val32)	do { writel (val32, ioaddr + reg); readl (ioaddr + reg); } while (0)
-#define RTL_R8(reg)		readb (ioaddr + reg)
-#define RTL_R16(reg)		readw (ioaddr + reg)
-#define RTL_R32(reg)		readl (ioaddr + reg)
+/* write MMIO register, with flush */
+/* Flush avoids rtl8139 bug w/ posted MMIO writes */
+#define RTL_W8_F(reg, val8)	do { writeb ((val8), ioaddr + (reg)); readb (ioaddr + (reg)); } while (0)
+#define RTL_W16_F(reg, val16)	do { writew ((val16), ioaddr + (reg)); readw (ioaddr + (reg)); } while (0)
+#define RTL_W32_F(reg, val32)	do { writel ((val32), ioaddr + (reg)); readl (ioaddr + (reg)); } while (0)
+
+
+#if MMIO_FLUSH_AUDIT_COMPLETE
+
+/* write MMIO register */
+#define RTL_W8(reg, val8)	writeb ((val8), ioaddr + (reg))
+#define RTL_W16(reg, val16)	writew ((val16), ioaddr + (reg))
+#define RTL_W32(reg, val32)	writel ((val32), ioaddr + (reg))
+
+#else
+
+/* write MMIO register, then flush */
+#define RTL_W8		RTL_W8_F
+#define RTL_W16		RTL_W16_F
+#define RTL_W32		RTL_W32_F
+
+#endif /* MMIO_FLUSH_AUDIT_COMPLETE */
+
+/* read MMIO register */
+#define RTL_R8(reg)		readb (ioaddr + (reg))
+#define RTL_R16(reg)		readw (ioaddr + (reg))
+#define RTL_R32(reg)		readl (ioaddr + (reg))
+
+
+static const u16 rtl8139_intr_mask = 
+	PCIErr | PCSTimeout | RxUnderrun | RxOverflow | RxFIFOOver |
+	TxErr | TxOK | RxErr | RxOK;
+
+static const unsigned int rtl8139_rx_config =
+	    (RX_FIFO_THRESH << 13) |
+	    (RX_BUF_LEN_IDX << 11) |
+	    (RX_DMA_BURST << 8);
 
 
 static const char * __devinit rtl8139_name_from_chip (chip_t chip)
@@ -855,22 +896,18 @@
 	rtl8139_init_ring (dev);
 	tp->full_duplex = tp->duplex_lock;
 	tp->tx_flag = (TX_FIFO_THRESH << 11) & 0x003f0000;
-	tp->rx_config =
-	    (RX_FIFO_THRESH << 13) |
-	    (RX_BUF_LEN_IDX << 11) |
-	    (RX_DMA_BURST << 8);
 
 	/* Check that the chip has finished the reset. */
 	for (i = 1000; i > 0; i--)
 		if ((RTL_R8 (ChipCmd) & CmdReset) == 0)
 			break;
 
-	RTL_W32 (MAC0 + 0, cpu_to_le32 (*(u32 *) (dev->dev_addr + 0)));
-	RTL_W32 (MAC0 + 4, cpu_to_le32 (*(u32 *) (dev->dev_addr + 4)));
+	RTL_W32_F (MAC0 + 0, cpu_to_le32 (*(u32 *) (dev->dev_addr + 0)));
+	RTL_W32_F (MAC0 + 4, cpu_to_le32 (*(u32 *) (dev->dev_addr + 4)));
 
 	/* Must enable Tx/Rx before setting transfer thresholds! */
 	RTL_W8 (ChipCmd, CmdRxEnb | CmdTxEnb);
-	RTL_W32 (RxConfig, tp->rx_config);
+	RTL_W32 (RxConfig, rtl8139_rx_config);
 	RTL_W32 (TxConfig, (TX_DMA_BURST << 8) | 0x00000000);
 
 	/* Reset N-Way to chipset defaults */
@@ -897,9 +934,7 @@
 	RTL_W8 (ChipCmd, CmdRxEnb | CmdTxEnb);
 
 	/* Enable all known interrupts by setting the interrupt mask. */
-	RTL_W16 (IntrMask,
-		 PCIErr | PCSTimeout | RxUnderrun | RxOverflow | RxFIFOOver
-		 | TxErr | TxOK | RxErr | RxOK);
+	RTL_W16 (IntrMask, rtl8139_intr_mask);
 
 	DPRINTK ("%s: rtl8139_open() ioaddr %#lx IRQ %d"
 			" GP Pins %2.2x %s-duplex.\n",
@@ -938,8 +973,8 @@
 			break;
 
 	/* Restore our idea of the MAC address. */
-	RTL_W32 (MAC0 + 0, cpu_to_le32 (*(u32 *) (dev->dev_addr + 0)));
-	RTL_W32 (MAC0 + 4, cpu_to_le32 (*(u32 *) (dev->dev_addr + 4)));
+	RTL_W32_F (MAC0 + 0, cpu_to_le32 (*(u32 *) (dev->dev_addr + 0)));
+	RTL_W32_F (MAC0 + 4, cpu_to_le32 (*(u32 *) (dev->dev_addr + 4)));
 
 	/* Hmmm, do these belong here? */
 	RTL_W8 (Cfg9346, 0x00);
@@ -947,7 +982,7 @@
 
 	/* Must enable Tx/Rx before setting transfer thresholds! */
 	RTL_W8 (ChipCmd, CmdRxEnb | CmdTxEnb);
-	RTL_W32 (RxConfig, tp->rx_config);
+	RTL_W32 (RxConfig, rtl8139_rx_config);
 	/* Check this value: the documentation contradicts ifself.  Is the
 	   IFG correct with bit 28:27 zero, or with |0x03000000 ? */
 	RTL_W32 (TxConfig, (TX_DMA_BURST << 8) | 0x00000000);
@@ -974,9 +1009,7 @@
 	rtl8139_set_rx_mode (dev);
 	RTL_W8 (ChipCmd, CmdRxEnb | CmdTxEnb);
 	/* Enable all known interrupts by setting the interrupt mask. */
-	RTL_W16 (IntrMask,
-		 PCIErr | PCSTimeout | RxUnderrun | RxOverflow | RxFIFOOver
-		 | TxErr | TxOK | RxErr | RxOK);
+	RTL_W16 (IntrMask, rtl8139_intr_mask);
 
 	netif_start_queue (dev);
 
@@ -1136,9 +1169,9 @@
 
 	DPRINTK ("%s: Transmit timeout, status %2.2x %4.4x "
 		 "media %2.2x.\n", dev->name,
-		 readb (ioaddr + ChipCmd),
-		 readw (ioaddr + IntrStatus),
-		 readb (ioaddr + GPPinData));
+		 RTL_R8 (ChipCmd),
+		 RTL_R16 (IntrStatus),
+		 RTL_R8 (GPPinData));
 
 	/* Disable interrupts by clearing the interrupt mask. */
 	RTL_W16 (IntrMask, 0x0000);
@@ -1148,7 +1181,7 @@
 		dev->name, tp->cur_tx, tp->dirty_tx);
 	for (i = 0; i < NUM_TX_DESC; i++)
 		printk (KERN_DEBUG "%s:  Tx descriptor %d is %8.8x.%s\n",
-			dev->name, i, readl (ioaddr + TxStatus0 + i * 4),
+			dev->name, i, RTL_R32 (TxStatus0 + (i * 4)),
 			i ==
 		      tp->dirty_tx % NUM_TX_DESC ? " (queue head)" : "");
 	printk (KERN_DEBUG "%s: MII #%d registers are:", dev->name,
@@ -1263,7 +1296,7 @@
 
 	while (tp->cur_tx - dirty_tx > 0) {
 		int entry = dirty_tx % NUM_TX_DESC;
-		int txstatus = readl (ioaddr + TxStatus0 + entry * 4);
+		int txstatus = RTL_R32 (TxStatus0 + (entry * 4));
 
 		if (!(txstatus & (TxStatOK | TxUnderrun | TxAborted)))
 			break;	/* It still hasn't been Txed */
@@ -1338,11 +1371,11 @@
 
 	DPRINTK ("%s: In rtl8139_rx(), current %4.4x BufAddr %4.4x,"
 			" free to %4.4x, Cmd %2.2x.\n", dev->name, cur_rx,
-			readw (ioaddr + RxBufAddr),
-			readw (ioaddr + RxBufPtr),
-			readb (ioaddr + ChipCmd));
+			RTL_R16 (RxBufAddr),
+			RTL_R16 (RxBufPtr),
+			RTL_R8 (ChipCmd));
 
-	while ((readb (ioaddr + ChipCmd) & RxBufEmpty) == 0) {
+	while ((RTL_R8 (ChipCmd) & RxBufEmpty) == 0) {
 		int ring_offset = cur_rx % RX_BUF_LEN;
 		u32 rx_status =
 		    le32_to_cpu (*(u32 *) (rx_ring + ring_offset));
@@ -1359,6 +1392,18 @@
 		printk (".\n");
 #endif
 
+		/* E. Gill */
+		/* Note from BSD driver:
+		 * Here's a totally undocumented fact for you. When the
+		 * RealTek chip is in the process of copying a packet into
+		 * RAM for you, the length will be 0xfff0. If you spot a
+		 * packet header with this value, you need to stop. The
+		 * datasheet makes absolutely no mention of this and
+		 * RealTek should be shot for this.
+		 */
+		if (rx_size == 0xfff0)
+			break;
+
 		if (rx_status &
 		    (RxBadSymbol | RxRunt | RxTooLong | RxCRCErr |
 		     RxBadAlign)) {
@@ -1437,13 +1482,13 @@
 		}
 
 		cur_rx = (cur_rx + rx_size + 4 + 3) & ~3;
-		RTL_W16 (RxBufPtr, cur_rx - 16);
+		RTL_W16_F (RxBufPtr, cur_rx - 16);
 	}
 	DPRINTK ("%s: Done rtl8139_rx(), current %4.4x BufAddr %4.4x,"
 		" free to %4.4x, Cmd %2.2x.\n", dev->name, cur_rx,
-		readw (ioaddr + RxBufAddr),
-		readw (ioaddr + RxBufPtr),
-		readb (ioaddr + ChipCmd));
+		RTL_R16 (RxBufAddr),
+		RTL_R16 (RxBufPtr),
+		RTL_R8 (ChipCmd));
 	tp->cur_rx = cur_rx;
 }
 
@@ -1468,14 +1513,13 @@
 	}
 
 	/* Update the error count. */
-	tp->stats.rx_missed_errors +=
-	    readl (ioaddr + RxMissed);
+	tp->stats.rx_missed_errors += RTL_R32 (RxMissed);
 	RTL_W32 (RxMissed, 0);
 
 	if ((status & RxUnderrun) && link_changed &&
 	    (tp->drv_flags & HAS_LNK_CHNG)) {
 		/* Really link-change on new chips. */
-		int lpar = readw (ioaddr + NWayLPAR);
+		int lpar = RTL_R16 (NWayLPAR);
 		int duplex = (lpar & 0x0100) || (lpar & 0x01C0) == 0x0040
 				|| tp->duplex_lock;
 		if (tp->full_duplex != duplex) {
@@ -1496,8 +1540,8 @@
 		tp->stats.rx_fifo_errors++;
 	if (status & RxOverflow) {
 		tp->stats.rx_over_errors++;
-		tp->cur_rx = readw (ioaddr + RxBufAddr) % RX_BUF_LEN;
-		RTL_W16 (RxBufPtr, tp->cur_rx - 16);
+		tp->cur_rx = RTL_R16 (RxBufAddr) % RX_BUF_LEN;
+		RTL_W16_F (RxBufPtr, tp->cur_rx - 16);
 	}
 	if (status & PCIErr) {
 		u16 pci_cmd_status;
@@ -1524,17 +1568,40 @@
 
 	spin_lock_irq (&tp->lock);
 
+	/* disable interrupt generation while handling this interrupt */
+	RTL_W16 (IntrMask, 0x0000);
+
 	do {
-		int status = readw (ioaddr + IntrStatus);
+		int status = RTL_R16 (IntrStatus);
 		/* Acknowledge all of the current interrupt sources ASAP, but
 		   an first get an additional status bit from CSCR. */
 		if (status & RxUnderrun)
-			link_changed = readw (ioaddr + CSCR) & CSCR_LinkChangeBit;
-		RTL_W16 (IntrStatus, status);
+			link_changed = RTL_R16 (CSCR) & CSCR_LinkChangeBit;
+
+		/* E. Gill */
+		/* In case of an RxFIFOOver we must also clear the RxOverflow
+		   bit to avoid dropping frames for ever. Believe me, I got a
+		   lot of troubles copying huge data (approximately 2 RxFIFOOver
+		   errors per 1GB data transfer).
+		   The following is written in the 'p-guide.pdf' file (RTL8139(A/B)
+		   Programming guide V0.1, from 1999/1/15) on page 9 from REALTEC.
+		   -----------------------------------------------------------
+		   2. RxFIFOOvw handling:
+		     When RxFIFOOvw occurs, all incoming packets are discarded.
+		     Clear ISR(RxFIFOOvw) doesn't dismiss RxFIFOOvw event. To
+		     dismiss RxFIFOOvw event, the ISR(RxBufOvw) must be written
+		     with a '1'.
+		   -----------------------------------------------------------
+		   Unfortunately I was not able to find any reason for the
+		   RxFIFOOver error (I got the feeling this depends on the
+		   CPU speed, lower CPU speed --> more errors).
+		   After clearing the RxOverflow bit the transfer of the
+		   packet was repeated and all data are error free transfered */
+		RTL_W16 (IntrStatus, (status & RxFIFOOver) ? (status | RxOverflow) : status);
 
 		DPRINTK ("%s: interrupt  status=%#4.4x new intstat=%#4.4x.\n",
 				dev->name, status,
-				readw (ioaddr + IntrStatus));
+				RTL_R16 (IntrStatus));
 
 		if ((status &
 		     (PCIErr | PCSTimeout | RxUnderrun | RxOverflow |
@@ -1565,11 +1632,14 @@
 		}
 	} while (1);
 
-	DPRINTK ("%s: exiting interrupt, intr_status=%#4.4x.\n",
-			dev->name, readw (ioaddr + IntrStatus));
+	/* Enable all known interrupts by setting the interrupt mask. */
+	RTL_W16 (IntrMask, rtl8139_intr_mask);
 
 	spin_unlock_irq (&tp->lock);
-	return;
+
+	DPRINTK ("%s: exiting interrupt, intr_status=%#4.4x.\n",
+			dev->name, RTL_R16 (IntrStatus));
+
 }
 
 static int rtl8139_close (struct net_device *dev)
@@ -1583,7 +1653,7 @@
 	netif_stop_queue (dev);
 
 	DPRINTK ("%s: Shutting down ethercard, status was 0x%4.4x.\n",
-			dev->name, readw (ioaddr + IntrStatus));
+			dev->name, RTL_R16 (IntrStatus));
 
 	/* Disable interrupts by clearing the interrupt mask. */
 	RTL_W16 (IntrMask, 0x0000);
@@ -1710,7 +1780,7 @@
 	DPRINTK ("ENTER\n");
 
 	DPRINTK ("%s:   rtl8139_set_rx_mode(%4.4x) done -- Rx config %8.8x.\n",
-			dev->name, dev->flags, readl (ioaddr + RxConfig));
+			dev->name, dev->flags, RTL_R32 (RxConfig));
 
 	/* Note: do not reorder, GCC is clever about common statements. */
 	if (dev->flags & IFF_PROMISC) {
@@ -1739,9 +1809,9 @@
 					    mc_filter);
 	}
 	/* We can safely update without stopping the chip. */
-	RTL_W32 (RxConfig, tp->rx_config | rx_mode);
-	RTL_W32 (MAR0 + 0, mc_filter[0]);
-	RTL_W32 (MAR0 + 4, mc_filter[1]);
+	RTL_W32 (RxConfig, rtl8139_rx_config | rx_mode);
+	RTL_W32_F (MAR0 + 0, mc_filter[0]);
+	RTL_W32_F (MAR0 + 4, mc_filter[1]);
 
 	DPRINTK ("EXIT\n");
 }

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)