patch-2.4.6 linux/drivers/net/8139too.c
Next file: linux/drivers/net/82596.c
Previous file: linux/drivers/net/3c59x.c
Back to the patch index
Back to the overall index
- Lines: 616
- Date:
Mon Jul 2 14:02:53 2001
- Orig file:
v2.4.5/linux/drivers/net/8139too.c
- Orig date:
Mon May 7 14:13:19 2001
diff -u --recursive --new-file v2.4.5/linux/drivers/net/8139too.c linux/drivers/net/8139too.c
@@ -136,6 +136,10 @@
*/
+#define DRV_NAME "8139too"
+#define DRV_VERSION "0.9.18-pre4"
+
+
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
@@ -146,13 +150,14 @@
#include <linux/etherdevice.h>
#include <linux/rtnetlink.h>
#include <linux/delay.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
#include <asm/io.h>
+#include <asm/uaccess.h>
-#define RTL8139_VERSION "0.9.17"
-#define MODNAME "8139too"
-#define RTL8139_DRIVER_NAME MODNAME " Fast Ethernet driver " RTL8139_VERSION
-#define PFX MODNAME ": "
+#define RTL8139_DRIVER_NAME DRV_NAME " Fast Ethernet driver " DRV_VERSION
+#define PFX DRV_NAME ": "
/* enable PIO instead of MMIO, if CONFIG_8139TOO_PIO is selected */
@@ -363,7 +368,10 @@
TxOK = 0x04,
RxErr = 0x02,
RxOK = 0x01,
+
+ RxAckBits = RxFIFOOver | RxOverflow | RxOK,
};
+
enum TxStatusBits {
TxHostOwns = 0x2000,
TxUnderrun = 0x4000,
@@ -542,6 +550,11 @@
};
+struct rtl_extra_stats {
+ unsigned long early_rx;
+ unsigned long tx_buf_mapped;
+ unsigned long tx_timeouts;
+};
struct rtl8139_private {
void *mmio_addr;
@@ -560,7 +573,6 @@
dma_addr_t rx_ring_dma;
dma_addr_t tx_bufs_dma;
signed char phys[4]; /* MII device addresses. */
- u16 advertising; /* NWay media advertisement */
char twistie, twist_row, twist_col; /* Twister tune state. */
unsigned int full_duplex:1; /* Full-duplex operation requested. */
unsigned int duplex_lock:1;
@@ -574,6 +586,7 @@
wait_queue_head_t thr_wait;
struct semaphore thr_exited;
u32 rx_config;
+ struct rtl_extra_stats xstats;
};
MODULE_AUTHOR ("Jeff Garzik <jgarzik@mandrakesoft.com>");
@@ -582,6 +595,10 @@
MODULE_PARM (max_interrupt_work, "i");
MODULE_PARM (media, "1-" __MODULE_STRING(MAX_UNITS) "i");
MODULE_PARM (full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i");
+MODULE_PARM_DESC (multicast_filter_limit, "8139too maximum number of filtered multicast addresses");
+MODULE_PARM_DESC (max_interrupt_work, "8139too maximum events handled per interrupt");
+MODULE_PARM_DESC (media, "8139too: Bits 4+9: force full duplex, bit 5: 100Mbps");
+MODULE_PARM_DESC (full_duplex, "8139too: Force full duplex for board(s) (1)");
static int read_eeprom (void *ioaddr, int location, int addr_len);
static int rtl8139_open (struct net_device *dev);
@@ -596,7 +613,7 @@
static void rtl8139_interrupt (int irq, void *dev_instance,
struct pt_regs *regs);
static int rtl8139_close (struct net_device *dev);
-static int mii_ioctl (struct net_device *dev, struct ifreq *rq, int cmd);
+static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd);
static struct net_device_stats *rtl8139_get_stats (struct net_device *dev);
static inline u32 ether_crc (int length, unsigned char *data);
static void rtl8139_set_rx_mode (struct net_device *dev);
@@ -938,7 +955,7 @@
dev->stop = rtl8139_close;
dev->get_stats = rtl8139_get_stats;
dev->set_multicast_list = rtl8139_set_rx_mode;
- dev->do_ioctl = mii_ioctl;
+ dev->do_ioctl = netdev_ioctl;
dev->tx_timeout = rtl8139_tx_timeout;
dev->watchdog_timeo = TX_TIMEOUT;
@@ -984,11 +1001,11 @@
for (phy = 0; phy < 32 && phy_idx < sizeof(tp->phys); phy++) {
int mii_status = mdio_read(dev, phy, 1);
if (mii_status != 0xffff && mii_status != 0x0000) {
+ u16 advertising = mdio_read(dev, phy, 4);
tp->phys[phy_idx++] = phy;
- tp->advertising = mdio_read(dev, phy, 4);
printk(KERN_INFO "%s: MII transceiver %d status 0x%4.4x "
"advertising %4.4x.\n",
- dev->name, phy, mii_status, tp->advertising);
+ dev->name, phy, mii_status, advertising);
}
}
if (phy_idx == 0) {
@@ -1314,6 +1331,28 @@
}
+static void rtl_check_media (struct net_device *dev)
+{
+ struct rtl8139_private *tp = dev->priv;
+
+ DPRINTK("ENTER\n");
+
+ if (tp->phys[0] >= 0) {
+ u16 mii_reg5 = mdio_read(dev, tp->phys[0], 5);
+ if (mii_reg5 == 0xffff)
+ ; /* Not there */
+ else if ((mii_reg5 & 0x0100) == 0x0100
+ || (mii_reg5 & 0x00C0) == 0x0040)
+ tp->full_duplex = 1;
+
+ printk (KERN_INFO"%s: Setting %s%s-duplex based on"
+ " auto-negotiated partner ability %4.4x.\n",
+ dev->name, mii_reg5 == 0 ? "" :
+ (mii_reg5 & 0x0180) ? "100mbps " : "10mbps ",
+ tp->full_duplex ? "full" : "half", mii_reg5);
+ }
+}
+
/* Start the hardware at open or resume. */
static void rtl8139_hw_start (struct net_device *dev)
{
@@ -1331,50 +1370,25 @@
rtl8139_chip_reset (ioaddr);
/* unlock Config[01234] and BMCR register writes */
- RTL_W8 (Cfg9346, Cfg9346_Unlock);
+ RTL_W8_F (Cfg9346, Cfg9346_Unlock);
/* Restore our idea of the MAC address. */
RTL_W32_F (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 + 4, cpu_to_le32 (*(u32 *) (dev->dev_addr + 4)));
/* Must enable Tx/Rx before setting transfer thresholds! */
RTL_W8 (ChipCmd, CmdRxEnb | CmdTxEnb);
tp->rx_config = rtl8139_rx_config | AcceptBroadcast | AcceptMyPhys;
- RTL_W32 (RxConfig, rtl8139_rx_config);
+ RTL_W32 (RxConfig, tp->rx_config);
/* Check this value: the documentation for IFG contradicts ifself. */
RTL_W32 (TxConfig, (TX_DMA_BURST << TxDMAShift));
tp->cur_rx = 0;
- DPRINTK("check_duplex");
-
- /* This is check_duplex() */
- if (tp->phys[0] >= 0 || (tp->drv_flags & HAS_MII_XCVR)) {
- u16 mii_reg5 = mdio_read(dev, tp->phys[0], 5);
- if (mii_reg5 == 0xffff)
- ; /* Not there */
- else if ((mii_reg5 & 0x0100) == 0x0100
- || (mii_reg5 & 0x00C0) == 0x0040)
- tp->full_duplex = 1;
- if (mii_reg5) {
- printk(KERN_INFO"%s: Setting %s%s-duplex based on"
- " auto-negotiated partner ability %4.4x.\n", dev->name,
- mii_reg5 == 0 ? "" :
- (mii_reg5 & 0x0180) ? "100mbps " : "10mbps ",
- tp->full_duplex ? "full" : "half", mii_reg5);
- } else {
- printk(KERN_INFO"%s: media is unconnected, link down, or incompatible connection\n",
- dev->name);
- }
- }
+ rtl_check_media (dev);
if (tp->chipset >= CH_8139B) {
- tmp = RTL_R8 (Config4) & ~(1<<2);
- /* chip will clear Rx FIFO overflow automatically */
- tmp |= (1<<7);
- RTL_W8 (Config4, tmp);
-
/* disable magic packet scanning, which is enabled
* when PM is enabled in Config1 */
RTL_W8 (Config3, RTL_R8 (Config3) & ~(1<<5));
@@ -1654,6 +1668,8 @@
RTL_R16 (IntrStatus),
RTL_R8 (MediaStatus));
+ tp->xstats.tx_timeouts++;
+
/* disable Tx ASAP, if not already */
tmp8 = RTL_R8 (ChipCmd);
if (tmp8 & CmdTxEnb)
@@ -1683,13 +1699,14 @@
}
-
static int rtl8139_start_xmit (struct sk_buff *skb, struct net_device *dev)
{
struct rtl8139_private *tp = dev->priv;
void *ioaddr = tp->mmio_addr;
unsigned int entry;
- unsigned long flags;
+ u32 dma_addr;
+
+ mb();
/* Calculate the next Tx descriptor entry. */
entry = tp->cur_tx % NUM_TX_DESC;
@@ -1701,29 +1718,29 @@
if ((long) skb->data & 3) { /* Must use alignment buffer. */
/* tp->tx_info[entry].mapping = 0; */
memcpy (tp->tx_buf[entry], skb->data, skb->len);
- RTL_W32 (TxAddr0 + (entry * 4),
- tp->tx_bufs_dma + (tp->tx_buf[entry] - tp->tx_bufs));
+ dma_addr = tp->tx_bufs_dma + (tp->tx_buf[entry] - tp->tx_bufs);
} else {
+ tp->xstats.tx_buf_mapped++;
tp->tx_info[entry].mapping =
pci_map_single (tp->pci_dev, skb->data, skb->len,
PCI_DMA_TODEVICE);
- RTL_W32 (TxAddr0 + (entry * 4), tp->tx_info[entry].mapping);
+ dma_addr = tp->tx_info[entry].mapping;
}
/* Note: the chip doesn't have auto-pad! */
- RTL_W32 (TxStatus0 + (entry * sizeof (u32)),
- tp->tx_flag | (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN));
+ spin_lock_irq(&tp->lock);
+ RTL_W32_F (TxAddr0 + (entry * 4), dma_addr);
+ RTL_W32_F (TxStatus0 + (entry * sizeof (u32)),
+ tp->tx_flag | (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN));
+ spin_unlock_irq(&tp->lock);
dev->trans_start = jiffies;
- spin_lock_irqsave (&tp->lock, flags);
-
tp->cur_tx++;
+ mb();
if ((tp->cur_tx - NUM_TX_DESC) == tp->dirty_tx)
netif_stop_queue (dev);
- spin_unlock_irqrestore (&tp->lock, flags);
-
DPRINTK ("%s: Queued Tx packet at %p size %u to slot %d.\n",
dev->name, skb->data, skb->len, entry);
@@ -1760,7 +1777,7 @@
tp->stats.tx_errors++;
if (txstatus & TxAborted) {
tp->stats.tx_aborted_errors++;
- RTL_W32 (TxConfig, TxClearAbt | (TX_DMA_BURST << TxDMAShift));
+ RTL_W32_F (TxConfig, TxClearAbt | (TX_DMA_BURST << TxDMAShift));
}
if (txstatus & TxCarrierLost)
tp->stats.tx_carrier_errors++;
@@ -1795,7 +1812,6 @@
dirty_tx++;
tx_left--;
- barrier();
}
#ifndef RTL8139_NDEBUG
@@ -1809,6 +1825,7 @@
/* only wake the queue if we did work, and the queue is stopped */
if (tp->dirty_tx != dirty_tx) {
tp->dirty_tx = dirty_tx;
+ mb();
if (netif_queue_stopped (dev))
netif_wake_queue (dev);
}
@@ -1865,11 +1882,10 @@
static void rtl8139_rx_interrupt (struct net_device *dev,
- struct rtl8139_private *tp, void *ioaddr,
- u16 status)
+ struct rtl8139_private *tp, void *ioaddr)
{
unsigned char *rx_ring;
- u16 cur_rx, ackstat;
+ u16 cur_rx;
assert (dev != NULL);
assert (tp != NULL);
@@ -1883,11 +1899,6 @@
RTL_R16 (RxBufAddr),
RTL_R16 (RxBufPtr), RTL_R8 (ChipCmd));
- if (status & RxFIFOOver)
- status = RxOverflow | RxOK;
- else
- status = RxOK;
-
while ((RTL_R8 (ChipCmd) & RxBufEmpty) == 0) {
int ring_offset = cur_rx % RX_BUF_LEN;
u32 rx_status;
@@ -1895,8 +1906,8 @@
unsigned int pkt_size;
struct sk_buff *skb;
- mb();
-
+ rmb();
+
/* read size+status of next frame from DMA ring buffer */
rx_status = le32_to_cpu (*(u32 *) (rx_ring + ring_offset));
rx_size = rx_status >> 16;
@@ -1916,8 +1927,10 @@
}
#endif
- if (rx_size == 0xfff0) /* Early Rx in progress */
+ if (rx_size == 0xfff0) { /* Early Rx in progress */
+ tp->xstats.early_rx++;
break;
+ }
/* If Rx err or invalid rx_size/rx_status received
* (which happens if we get lost in the ring),
@@ -1963,9 +1976,8 @@
cur_rx = (cur_rx + rx_size + 4 + 3) & ~3;
RTL_W16 (RxBufPtr, cur_rx - 16);
- ackstat = RTL_R16 (IntrStatus) & status;
- if (ackstat)
- RTL_W16 (IntrStatus, ackstat);
+ if (RTL_R16 (IntrStatus) & RxAckBits)
+ RTL_W16_F (IntrStatus, RxAckBits);
}
DPRINTK ("%s: Done rtl8139_rx(), current %4.4x BufAddr %4.4x,"
@@ -1975,11 +1987,9 @@
tp->cur_rx = cur_rx;
- if (RTL_R8 (ChipCmd) & RxBufEmpty) {
- ackstat = RTL_R16 (IntrStatus) & status;
- if (ackstat)
- RTL_W16_F (IntrStatus, ackstat);
- }
+ if ((RTL_R8 (ChipCmd) & RxBufEmpty) &&
+ (RTL_R16 (IntrStatus) & RxAckBits))
+ RTL_W16_F (IntrStatus, RxAckBits);
}
@@ -2047,6 +2057,8 @@
int ackstat, status;
int link_changed = 0; /* avoid bogus "uninit" warning */
+ spin_lock (&tp->lock);
+
do {
status = RTL_R16 (IntrStatus);
@@ -2059,26 +2071,10 @@
if (status & RxUnderrun)
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 transferred */
- ackstat = status & ~(RxFIFOOver | RxOverflow | RxOK);
+ /* The chip takes special action when we clear RxAckBits,
+ * so we clear them later in rtl8139_rx_interrupt
+ */
+ ackstat = status & ~RxAckBits;
RTL_W16 (IntrStatus, ackstat);
DPRINTK ("%s: interrupt status=%#4.4x ackstat=%#4.4x new intstat=%#4.4x.\n",
@@ -2089,9 +2085,8 @@
RxFIFOOver | TxErr | TxOK | RxErr | RxOK)) == 0)
break;
- if (netif_running (dev) &&
- status & (RxOK | RxUnderrun | RxOverflow | RxFIFOOver)) /* Rx interrupt */
- rtl8139_rx_interrupt (dev, tp, ioaddr, status);
+ if (netif_running (dev) && (status & RxAckBits))
+ rtl8139_rx_interrupt (dev, tp, ioaddr);
/* Check uncommon events with one test. */
if (status & (PCIErr | PCSTimeout | RxUnderrun | RxOverflow |
@@ -2099,26 +2094,22 @@
rtl8139_weird_interrupt (dev, tp, ioaddr,
status, link_changed);
- if (netif_running (dev) &&
- status & (TxOK | TxErr)) {
- spin_lock (&tp->lock);
+ if (netif_running (dev) && (status & (TxOK | TxErr)))
rtl8139_tx_interrupt (dev, tp, ioaddr);
- spin_unlock (&tp->lock);
- }
boguscnt--;
} while (boguscnt > 0);
if (boguscnt <= 0) {
- printk (KERN_WARNING
- "%s: Too much work at interrupt, "
- "IntrStatus=0x%4.4x.\n", dev->name,
- status);
+ printk (KERN_WARNING "%s: Too much work at interrupt, "
+ "IntrStatus=0x%4.4x.\n", dev->name, status);
/* Clear all interrupt sources. */
RTL_W16 (IntrStatus, 0xffff);
}
+ spin_unlock (&tp->lock);
+
DPRINTK ("%s: exiting interrupt, intr_status=%#4.4x.\n",
dev->name, RTL_R16 (IntrStatus));
}
@@ -2184,42 +2175,79 @@
}
-static int mii_ioctl (struct net_device *dev, struct ifreq *rq, int cmd)
+static int netdev_ethtool_ioctl (struct net_device *dev, void *useraddr)
+{
+ struct rtl8139_private *np = dev->priv;
+ u32 ethcmd;
+
+ if (copy_from_user (ðcmd, useraddr, sizeof (ethcmd)))
+ return -EFAULT;
+
+ switch (ethcmd) {
+ case ETHTOOL_GDRVINFO:
+ {
+ struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO };
+ strcpy (info.driver, DRV_NAME);
+ strcpy (info.version, DRV_VERSION);
+ strcpy (info.bus_info, np->pci_dev->slot_name);
+ if (copy_to_user (useraddr, &info, sizeof (info)))
+ return -EFAULT;
+ return 0;
+ }
+
+ default:
+ break;
+ }
+
+ return -EOPNOTSUPP;
+}
+
+static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd)
{
struct rtl8139_private *tp = dev->priv;
- u16 *data = (u16 *) & rq->ifr_data;
+ struct mii_ioctl_data *data = (struct mii_ioctl_data *)&rq->ifr_data;
int rc = 0;
+ int phy = tp->phys[0] & 0x3f;
DPRINTK ("ENTER\n");
+ data->phy_id &= 0x1f;
+ data->reg_num &= 0x1f;
+
switch (cmd) {
- case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */
- data[0] = tp->phys[0] & 0x3f;
+ case SIOCETHTOOL:
+ return netdev_ethtool_ioctl(dev, (void *) rq->ifr_data);
+
+ case SIOCGMIIPHY: /* Get the address of the PHY in use. */
+ case SIOCDEVPRIVATE: /* binary compat, remove in 2.5 */
+ data->phy_id = phy;
/* Fall Through */
- case SIOCDEVPRIVATE + 1: /* Read the specified MII register. */
- data[3] = mdio_read (dev, data[0], data[1] & 0x1f);
+ case SIOCGMIIREG: /* Read the specified MII register. */
+ case SIOCDEVPRIVATE+1: /* binary compat, remove in 2.5 */
+ data->val_out = mdio_read (dev, data->phy_id, data->reg_num);
break;
- case SIOCDEVPRIVATE + 2: /* Write the specified MII register */
+ case SIOCSMIIREG: /* Write the specified MII register */
+ case SIOCDEVPRIVATE+2: /* binary compat, remove in 2.5 */
if (!capable (CAP_NET_ADMIN)) {
rc = -EPERM;
break;
}
- if (data[0] == tp->phys[0]) {
- u16 value = data[2];
- switch (data[1]) {
+ if (data->phy_id == phy) {
+ u16 value = data->val_in;
+ switch (data->reg_num) {
case 0:
/* Check for autonegotiation on or reset. */
tp->medialock = (value & 0x9000) ? 0 : 1;
if (tp->medialock)
tp->full_duplex = (value & 0x0100) ? 1 : 0;
break;
- case 4: tp->advertising = value; break;
+ case 4: /* tp->advertising = value; */ break;
}
}
- mdio_write(dev, data[0], data[1] & 0x1f, data[2]);
+ mdio_write(dev, data->phy_id, data->reg_num, data->val_in);
break;
default:
@@ -2308,8 +2336,9 @@
mc_filter[1] = mc_filter[0] = 0;
for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
i++, mclist = mclist->next) {
- set_bit (ether_crc (ETH_ALEN, mclist->dmi_addr) >> 26,
- mc_filter);
+ int bit_nr = ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26;
+
+ mc_filter[bit_nr >> 5] |= cpu_to_le32(1 << (bit_nr & 31));
rx_mode |= AcceptMulticast;
}
}
@@ -2323,7 +2352,7 @@
tp->rx_config = tmp;
}
RTL_W32_F (MAR0 + 0, mc_filter[0]);
- RTL_W32 (MAR0 + 4, mc_filter[1]);
+ RTL_W32_F (MAR0 + 4, mc_filter[1]);
spin_unlock_irqrestore (&tp->lock, flags);
@@ -2331,7 +2360,9 @@
}
-static void rtl8139_suspend (struct pci_dev *pdev)
+#ifdef CONFIG_PM
+
+static int rtl8139_suspend (struct pci_dev *pdev, u32 state)
{
struct net_device *dev = pci_get_drvdata (pdev);
struct rtl8139_private *tp = dev->priv;
@@ -2339,7 +2370,7 @@
unsigned long flags;
if (!netif_running (dev))
- return;
+ return 0;
netif_device_detach (dev);
@@ -2354,27 +2385,33 @@
RTL_W32 (RxMissed, 0);
spin_unlock_irqrestore (&tp->lock, flags);
+ return 0;
}
-static void rtl8139_resume (struct pci_dev *pdev)
+static int rtl8139_resume (struct pci_dev *pdev)
{
struct net_device *dev = pci_get_drvdata (pdev);
if (!netif_running (dev))
- return;
+ return 0;
netif_device_attach (dev);
rtl8139_hw_start (dev);
+ return 0;
}
+#endif /* CONFIG_PM */
+
static struct pci_driver rtl8139_pci_driver = {
- name: MODNAME,
+ name: DRV_NAME,
id_table: rtl8139_pci_tbl,
probe: rtl8139_init_one,
remove: rtl8139_remove_one,
+#ifdef CONFIG_PM
suspend: rtl8139_suspend,
resume: rtl8139_resume,
+#endif /* CONFIG_PM */
};
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)