NAPI support in various net drivers (backported from Linux 2.6). --- drivers/net/8139too.c | 235 +++++++++++++++++++++ drivers/net/Config.in | 8 drivers/net/Makefile | 13 - drivers/net/bonding/bond_main.c | 2 drivers/net/e100/e100.h | 4 drivers/net/e100/e100_main.c | 178 +++++++++------- drivers/net/eepro100.c | 36 ++- drivers/net/gt64240eth.h | 2 drivers/net/natsemi.c | 147 +++++++++++-- drivers/net/starfire.c | 7 drivers/net/sundance.c | 3 drivers/net/tulip/interrupt.c | 432 ++++++++++++++++++++++++++++------------ drivers/net/tulip/tulip.h | 31 +- drivers/net/tulip/tulip_core.c | 186 +++++++++++++---- 14 files changed, 983 insertions(+), 301 deletions(-) diff --git a/drivers/net/8139too.c b/drivers/net/8139too.c index 81dbd19..f7ef441 100644 --- a/drivers/net/8139too.c +++ b/drivers/net/8139too.c @@ -92,8 +92,11 @@ */ #define DRV_NAME "8139too" +#ifdef CONFIG_8139TOO_NAPI +#define DRV_VERSION "0.9.26-NAPI" +#else #define DRV_VERSION "0.9.26" - +#endif #include #include @@ -163,8 +166,10 @@ static int media[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; +#ifndef CONFIG_8139TOO_NAPI /* Maximum events (Rx packets, etc.) to handle at each interrupt. */ static int max_interrupt_work = 20; +#endif /* Maximum number of multicast addresses to filter (vs. Rx-all-multicast). The RTL chips use a 64 element hash table based on the Ethernet CRC. */ @@ -599,13 +604,17 @@ MODULE_DESCRIPTION ("RealTek RTL-8139 Fast Ethernet driver"); MODULE_LICENSE("GPL"); MODULE_PARM (multicast_filter_limit, "i"); +#ifndef CONFIG_8139TOO_NAPI MODULE_PARM (max_interrupt_work, "i"); +#endif MODULE_PARM (media, "1-" __MODULE_STRING(MAX_UNITS) "i"); MODULE_PARM (full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i"); MODULE_PARM (debug, "i"); MODULE_PARM_DESC (debug, "8139too bitmapped message enable number"); MODULE_PARM_DESC (multicast_filter_limit, "8139too maximum number of filtered multicast addresses"); +#ifndef CONFIG_8139TOO_NAPI MODULE_PARM_DESC (max_interrupt_work, "8139too maximum events handled per interrupt"); +#endif 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)"); @@ -628,6 +637,9 @@ static void rtl8139_set_rx_mode (struct net_device *dev); static void __set_rx_mode (struct net_device *dev); static void rtl8139_hw_start (struct net_device *dev); static struct ethtool_ops rtl8139_ethtool_ops; +#ifdef CONFIG_8139TOO_NAPI +static int rtl8139_poll(struct net_device *dev, int *budget); +#endif #ifdef USE_IO_OPS @@ -971,6 +983,10 @@ static int __devinit rtl8139_init_one (struct pci_dev *pdev, /* The Rtl8139-specific entries in the device structure. */ dev->open = rtl8139_open; dev->hard_start_xmit = rtl8139_start_xmit; +#ifdef CONFIG_8139TOO_NAPI + dev->poll = rtl8139_poll; + dev->weight = 16; +#endif dev->stop = rtl8139_close; dev->get_stats = rtl8139_get_stats; dev->set_multicast_list = rtl8139_set_rx_mode; @@ -1896,7 +1912,150 @@ static void rtl8139_rx_err (u32 rx_status, struct net_device *dev, __set_rx_mode (dev); #endif } +#ifdef CONFIG_8139TOO_NAPI +/** + * NAPI poll routine. + */ +static int rtl8139_poll (struct net_device *dev, int *budget) +{ + struct rtl8139_private *tp; + void *ioaddr; + unsigned char *rx_ring; + u16 cur_rx; + int rx, status; + unsigned long flags; + + assert (dev != NULL); + + tp = dev->priv; + ioaddr = tp->mmio_addr; + + spin_lock_irqsave(&tp->lock, flags); +rescan: + rx_ring = tp->rx_ring; + cur_rx = tp->cur_rx; + rx = 0; + + DPRINTK ("%s: In rtl8139_rx_poll(), current %4.4x BufAddr %4.4x," + " free to %4.4x, Cmd %2.2x.\n", dev->name, cur_rx, + RTL_R16 (RxBufAddr), + RTL_R16 (RxBufPtr), RTL_R8 (ChipCmd)); + + while ((RTL_R8 (ChipCmd) & RxBufEmpty) == 0) { + int ring_offset = cur_rx % RX_BUF_LEN; + u32 rx_status; + unsigned int rx_size; + unsigned int pkt_size; + struct sk_buff *skb; + + 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; + pkt_size = rx_size - 4; + + if (netif_msg_rx_status(tp)) + printk(KERN_DEBUG "%s: rtl8139_rx() status %4.4x, size %4.4x," + " cur %4.4x.\n", dev->name, rx_status, + rx_size, cur_rx); +#if RTL8139_DEBUG > 2 + { + int i; + DPRINTK ("%s: Frame contents ", dev->name); + for (i = 0; i < 70; i++) + printk (" %2.2x", + rx_ring[ring_offset + i]); + printk (".\n"); + } +#endif + + /* Packet copy from FIFO still in progress. + * Theoretically, this should never happen + * since EarlyRx is disabled. + */ + if (rx_size == 0xfff0) { + tp->xstats.early_rx++; + break; + } + + /* If Rx err or invalid rx_size/rx_status received + * (which happens if we get lost in the ring), + * Rx process gets reset, so we abort any further + * Rx processing. + */ + if ((rx_size > (MAX_ETH_FRAME_SIZE+4)) || + (rx_size < 8) || + (!(rx_status & RxStatusOK))) { + rtl8139_rx_err (rx_status, dev, tp, ioaddr); + goto err; + } + + /** + * Drop lock so we can copy data with interrupts enabled. + */ + spin_unlock_irqrestore(&tp->lock, flags); + /* Omit the four octet CRC from the length. */ + skb = dev_alloc_skb (pkt_size + 2); + if (skb) { + skb->dev = dev; + skb_reserve (skb, 2); /* 16 byte align the IP fields. */ + + eth_copy_and_sum (skb, &rx_ring[ring_offset + 4], pkt_size, 0); + skb_put (skb, pkt_size); + + skb->protocol = eth_type_trans (skb, dev); + netif_receive_skb(skb); + dev->last_rx = jiffies; + tp->stats.rx_bytes += pkt_size; + tp->stats.rx_packets++; + } else { + if (net_ratelimit()) { + printk (KERN_WARNING + "%s: Memory squeeze, dropping packet.\n", + dev->name); + } + tp->stats.rx_dropped++; + } + + spin_lock_irqsave(&tp->lock, flags); + cur_rx = (cur_rx + rx_size + 4 + 3) & ~3; + RTL_W16 (RxBufPtr, cur_rx - 16); + + status = RTL_R16 (IntrStatus) & RxAckBits; + + if (status) { + RTL_W16_F (IntrStatus, RxAckBits); + } + if (++rx >= dev->quota) { + break; + } + } + + DPRINTK ("%s: Done rtl8139_rx(), current %4.4x BufAddr %4.4x," + " free to %4.4x, Cmd %2.2x.\n", dev->name, cur_rx, + RTL_R16 (RxBufAddr), + RTL_R16 (RxBufPtr), RTL_R8 (ChipCmd)); + + tp->cur_rx = cur_rx; +err: + *budget -= rx; + if ((dev->quota -= rx) <= 0) { + spin_unlock_irqrestore(&tp->lock, flags); + return 1; + } + /* last gasp check if interrupt still pending */ + if (RTL_R16 (IntrStatus) & RxAckBits) { + DPRINTK("%s: going back for more work\n", dev->name); + goto rescan; + } + netif_rx_complete(dev); + RTL_W16 (IntrMask, rtl8139_intr_mask); + spin_unlock_irqrestore(&tp->lock, flags); + return 0; /* done */ +} +#else static void rtl8139_rx_interrupt (struct net_device *dev, struct rtl8139_private *tp, void *ioaddr) { @@ -1929,9 +2088,8 @@ static void rtl8139_rx_interrupt (struct net_device *dev, rx_size = rx_status >> 16; pkt_size = rx_size - 4; - if (netif_msg_rx_status(tp)) - printk(KERN_DEBUG "%s: rtl8139_rx() status %4.4x, size %4.4x," - " cur %4.4x.\n", dev->name, rx_status, + DPRINTK ("%s: rtl8139_rx() status %4.4x, size %4.4x," + " cur %4.4x.\n", dev->name, rx_status, rx_size, cur_rx); #if RTL8139_DEBUG > 2 { @@ -2008,7 +2166,7 @@ static void rtl8139_rx_interrupt (struct net_device *dev, tp->cur_rx = cur_rx; } - +#endif static void rtl8139_weird_interrupt (struct net_device *dev, struct rtl8139_private *tp, @@ -2033,8 +2191,7 @@ static void rtl8139_weird_interrupt (struct net_device *dev, } /* XXX along with rtl8139_rx_err, are we double-counting errors? */ - if (status & - (RxUnderrun | RxOverflow | RxErr | RxFIFOOver)) + if (status & RxUnderrun) tp->stats.rx_errors++; if (status & PCSTimeout) @@ -2054,6 +2211,68 @@ static void rtl8139_weird_interrupt (struct net_device *dev, /* The interrupt handler does all of the Rx thread work and cleans up after the Tx thread. */ +#ifdef CONFIG_8139TOO_NAPI +static irqreturn_t rtl8139_interrupt (int irq, void *dev_instance, + struct pt_regs *regs) +{ + struct net_device *dev = (struct net_device *) dev_instance; + struct rtl8139_private *tp = dev->priv; + void *ioaddr = tp->mmio_addr; + int ackstat, status; + int link_changed = 0; /* avoid bogus "uninit" warning */ + + spin_lock (&tp->lock); + + status = RTL_R16 (IntrStatus); + + /* h/w no longer present (hotplug?) or major error, bail */ + if (unlikely(status == 0xFFFF)) { + spin_unlock(&tp->lock); + return IRQ_HANDLED; + } + + /* no interrupt source present */ + if (unlikely((status & rtl8139_intr_mask) == 0)) { + spin_unlock(&tp->lock); + return IRQ_NONE; + } + + /* The chip takes special action when we clear RxAckBits, + * so we clear them later in poll + */ + ackstat = status & ~(RxAckBits | TxErr); + RTL_W16 (IntrStatus, ackstat); + + DPRINTK ("%s: interrupt status=%#4.4x ackstat=%#4.4x new intstat=%#4.4x.\n", + dev->name, status, ackstat, RTL_R16 (IntrStatus)); + + /* If received interrupt, then disable furthur interrupts + * and enable NAPI polling. + */ + if (netif_running (dev) && (status & RxAckBits)) { + /* disable more receive interrupts */ + RTL_W16 (IntrMask, rtl8139_intr_mask & ~RxAckBits); + netif_rx_schedule(dev); + } + + /* Check uncommon events with one test. */ + if (unlikely(status & (PCIErr|PCSTimeout|RxUnderrun|RxOverflow))) { + rtl8139_weird_interrupt (dev, tp, ioaddr, + status, link_changed); + } + if (netif_running (dev) && (status & (TxOK | TxErr))) { + rtl8139_tx_interrupt (dev, tp, ioaddr); + if (status & TxErr) + RTL_W16 (IntrStatus, TxErr); + } + + spin_unlock (&tp->lock); + + DPRINTK ("%s: exiting interrupt, intr_status=%#4.4x.\n", + dev->name, RTL_R16 (IntrStatus)); + return IRQ_HANDLED; +} +#else static irqreturn_t rtl8139_interrupt (int irq, void *dev_instance, struct pt_regs *regs) { @@ -2128,7 +2347,7 @@ static irqreturn_t rtl8139_interrupt (int irq, void *dev_instance, dev->name, RTL_R16 (IntrStatus)); return IRQ_RETVAL(handled); } - +#endif static int rtl8139_close (struct net_device *dev) { diff --git a/drivers/net/Config.in b/drivers/net/Config.in index 188fe42..4fa79f6 100644 --- a/drivers/net/Config.in +++ b/drivers/net/Config.in @@ -178,6 +178,7 @@ if [ "$CONFIG_NET_ETHERNET" = "y" ]; then if [ "$CONFIG_TULIP" = "y" -o "$CONFIG_TULIP" = "m" ]; then dep_bool ' New bus configuration (EXPERIMENTAL)' CONFIG_TULIP_MWI $CONFIG_EXPERIMENTAL bool ' Use PCI shared mem for NIC registers' CONFIG_TULIP_MMIO + bool ' Use NAPI' CONFIG_TULIP_NAPI fi if [ "$CONFIG_PCI" = "y" -o "$CONFIG_EISA" = "y" ]; then tristate ' Generic DECchip & DIGITAL EtherWORKS PCI/EISA' CONFIG_DE4X5 @@ -191,9 +192,15 @@ if [ "$CONFIG_NET_ETHERNET" = "y" ]; then dep_mbool ' Use PIO instead of MMIO' CONFIG_EEPRO100_PIO $CONFIG_EEPRO100 fi dep_tristate ' EtherExpressPro/100 support (e100, Alternate Intel driver)' CONFIG_E100 $CONFIG_PCI + if [ "$CONFIG_E100" = "y" -o "$CONFIG_E100" = "m" ]; then + bool ' Use NAPI' CONFIG_E100_NAPI + fi dep_tristate ' Mylex EISA LNE390A/B support (EXPERIMENTAL)' CONFIG_LNE390 $CONFIG_EISA $CONFIG_EXPERIMENTAL dep_tristate ' Myson MTD-8xx PCI Ethernet support' CONFIG_FEALNX $CONFIG_PCI dep_tristate ' National Semiconductor DP8381x series PCI Ethernet support' CONFIG_NATSEMI $CONFIG_PCI + if [ "$CONFIG_NATSEMI" = "y" -o "$CONFIG_NATSEMI" = "m" ]; then + bool ' Use NAPI' CONFIG_NATSEMI_NAPI + fi dep_tristate ' PCI NE2000 and clones support (see help)' CONFIG_NE2K_PCI $CONFIG_PCI dep_tristate ' nForce Ethernet support (EXPERIMENTAL)' CONFIG_FORCEDETH $CONFIG_PCI $CONFIG_EXPERIMENTAL dep_tristate ' Novell/Eagle/Microdyne NE3210 EISA support (EXPERIMENTAL)' CONFIG_NE3210 $CONFIG_EISA $CONFIG_EXPERIMENTAL @@ -201,6 +208,7 @@ if [ "$CONFIG_NET_ETHERNET" = "y" ]; then dep_tristate ' RealTek RTL-8139 C+ PCI Fast Ethernet Adapter support (EXPERIMENTAL)' CONFIG_8139CP $CONFIG_PCI $CONFIG_EXPERIMENTAL dep_tristate ' RealTek RTL-8139 PCI Fast Ethernet Adapter support' CONFIG_8139TOO $CONFIG_PCI dep_mbool ' Use PIO instead of MMIO' CONFIG_8139TOO_PIO $CONFIG_8139TOO + dep_mbool ' Use NAPI' CONFIG_8139TOO_NAPI $CONFIG_8139TOO dep_mbool ' Support for uncommon RTL-8139 rev. K (automatic channel equalization)' CONFIG_8139TOO_TUNE_TWISTER $CONFIG_8139TOO dep_mbool ' Support for older RTL-8129/8130 boards' CONFIG_8139TOO_8129 $CONFIG_8139TOO dep_mbool ' Use older RX-reset method' CONFIG_8139_OLD_RX_RESET $CONFIG_8139TOO diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 82f52fe..fba8c82 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -21,10 +21,6 @@ export-objs := 8390.o arlan.o aironet4500_core.o aironet4500_card.o \ list-multi := rcpci.o rcpci-objs := rcpci45.o rclanmtl.o -ifeq ($(CONFIG_TULIP),y) - obj-y += tulip/tulip.o -endif - obj-$(CONFIG_OCP_NET) += ibm_emac/ocp.o ifeq ($(CONFIG_E1000),y) @@ -108,6 +104,13 @@ ifeq ($(CONFIG_SKFP),y) obj-y += skfp/skfp.o endif +ifeq ($(CONFIG_TULIP),y) + obj-y += tulip/tulip.o +endif + +obj-$(CONFIG_8139CP) += 8139cp.o mii.o +obj-$(CONFIG_8139TOO) += 8139too.o mii.o + obj-$(CONFIG_VIA_RHINE) += via-rhine.o mii.o obj-$(CONFIG_ADAPTEC_STARFIRE) += starfire.o mii.o @@ -192,8 +195,6 @@ obj-$(CONFIG_EL3) += 3c509.o obj-$(CONFIG_3C515) += 3c515.o obj-$(CONFIG_EEXPRESS) += eexpress.o obj-$(CONFIG_EEXPRESS_PRO) += eepro.o -obj-$(CONFIG_8139CP) += 8139cp.o mii.o -obj-$(CONFIG_8139TOO) += 8139too.o mii.o obj-$(CONFIG_WAVELAN) += wavelan.o obj-$(CONFIG_ARLAN) += arlan.o arlan-proc.o obj-$(CONFIG_ZNET) += znet.o diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index c588ea3..086aa94 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -4662,7 +4662,7 @@ static int __init bonding_init(void) goto out_err; } - res = dev_alloc_name(bond_dev, "bond%d"); + res = dev_alloc_name(bond_dev, "bnd%d"); if (res < 0) { free_netdev(bond_dev); goto out_err; diff --git a/drivers/net/e100/e100.h b/drivers/net/e100/e100.h index 7d977da..9916c12 100644 --- a/drivers/net/e100/e100.h +++ b/drivers/net/e100/e100.h @@ -948,10 +948,10 @@ struct e100_private { #ifdef CONFIG_PM u32 pci_state[16]; #endif -#ifdef E100_CU_DEBUG +#ifdef E100_CU_DEBUG u8 last_cmd; u8 last_sub_cmd; -#endif +#endif }; #define E100_AUTONEG 0 diff --git a/drivers/net/e100/e100_main.c b/drivers/net/e100/e100_main.c index c9d801a..03c065b 100644 --- a/drivers/net/e100/e100_main.c +++ b/drivers/net/e100/e100_main.c @@ -135,14 +135,14 @@ static void e100_non_tx_background(unsigned long); static inline void e100_tx_skb_free(struct e100_private *bdp, tcb_t *tcb); /* Global Data structures and variables */ char e100_copyright[] __devinitdata = "Copyright (c) 2004 Intel Corporation"; +#ifdef CONFIG_E100_NAPI +char e100_driver_version[]="2.3.43-k1-NAPI"; +#else char e100_driver_version[]="2.3.43-k1"; +#endif const char *e100_full_driver_name = "Intel(R) PRO/100 Network Driver"; char e100_short_driver_name[] = "e100"; static int e100nics = 0; -static void e100_vlan_rx_register(struct net_device *netdev, struct vlan_group - *grp); -static void e100_vlan_rx_add_vid(struct net_device *netdev, u16 vid); -static void e100_vlan_rx_kill_vid(struct net_device *netdev, u16 vid); #ifdef CONFIG_NET_POLL_CONTROLLER /* for netdump / net console */ @@ -223,7 +223,9 @@ static void e100_set_bool_option(struct e100_private *bdp, int, u32, int, char *); unsigned char e100_wait_exec_cmplx(struct e100_private *, u32, u8, u8); void e100_exec_cmplx(struct e100_private *, u32, u8); - +#ifdef CONFIG_E100_NAPI +static int e100_poll(struct net_device *dev, int *budget); +#endif /** * e100_get_rx_struct - retrieve cell to hold skb buff from the pool * @bdp: atapter's private data struct @@ -343,7 +345,11 @@ e100_alloc_skbs(struct e100_private *bdp) } void e100_tx_srv(struct e100_private *); +#ifdef CONFIG_E100_NAPI +u32 e100_rx_srv(struct e100_private *bdp, int *work_done, int work_to_do); +#else u32 e100_rx_srv(struct e100_private *); +#endif void e100_watchdog(struct net_device *); void e100_refresh_txthld(struct e100_private *); @@ -636,9 +642,6 @@ e100_found1(struct pci_dev *pcid, const struct pci_device_id *ent) bdp->rfd_size = 16; } - dev->vlan_rx_register = e100_vlan_rx_register; - dev->vlan_rx_add_vid = e100_vlan_rx_add_vid; - dev->vlan_rx_kill_vid = e100_vlan_rx_kill_vid; dev->irq = pcid->irq; dev->open = &e100_open; dev->hard_start_xmit = &e100_xmit_frame; @@ -653,10 +656,14 @@ e100_found1(struct pci_dev *pcid, const struct pci_device_id *ent) dev->poll_controller = e100_netpoll; #endif - if (bdp->flags & USE_IPCB) - dev->features = NETIF_F_SG | NETIF_F_IP_CSUM | - NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX; - +#ifdef CONFIG_E100_NAPI + dev->poll = e100_poll; + dev->weight = 16; +#endif + if (bdp->flags & USE_IPCB) { + dev->features = NETIF_F_SG | NETIF_F_IP_CSUM; + } + if ((rc = register_netdev(dev)) != 0) { goto err_dealloc; } @@ -768,20 +775,24 @@ e100_remove1(struct pci_dev *pcid) --e100nics; } +static struct pci_device_id e100_id_table_pre[] __devinitdata = { + {0x8086, 0x2449, PCI_ANY_ID, PCI_ANY_ID, 0, 0, }, + {0x8086, 0x103A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, }, + {0,} +}; + static struct pci_device_id e100_id_table[] = { {0x8086, 0x1229, PCI_ANY_ID, PCI_ANY_ID, 0, 0, }, - {0x8086, 0x2449, PCI_ANY_ID, PCI_ANY_ID, 0, 0, }, {0x8086, 0x1059, PCI_ANY_ID, PCI_ANY_ID, 0, 0, }, {0x8086, 0x1209, PCI_ANY_ID, PCI_ANY_ID, 0, 0, }, - {0x8086, 0x1029, PCI_ANY_ID, PCI_ANY_ID, 0, 0, }, - {0x8086, 0x1030, PCI_ANY_ID, PCI_ANY_ID, 0, 0, }, - {0x8086, 0x1031, PCI_ANY_ID, PCI_ANY_ID, 0, 0, }, + {0x8086, 0x1029, PCI_ANY_ID, PCI_ANY_ID, 0, 0, }, + {0x8086, 0x1030, PCI_ANY_ID, PCI_ANY_ID, 0, 0, }, + {0x8086, 0x1031, PCI_ANY_ID, PCI_ANY_ID, 0, 0, }, {0x8086, 0x1032, PCI_ANY_ID, PCI_ANY_ID, 0, 0, }, - {0x8086, 0x1033, PCI_ANY_ID, PCI_ANY_ID, 0, 0, }, - {0x8086, 0x1034, PCI_ANY_ID, PCI_ANY_ID, 0, 0, }, + {0x8086, 0x1033, PCI_ANY_ID, PCI_ANY_ID, 0, 0, }, + {0x8086, 0x1034, PCI_ANY_ID, PCI_ANY_ID, 0, 0, }, {0x8086, 0x1038, PCI_ANY_ID, PCI_ANY_ID, 0, 0, }, {0x8086, 0x1039, PCI_ANY_ID, PCI_ANY_ID, 0, 0, }, - {0x8086, 0x103A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, }, {0x8086, 0x103B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, }, {0x8086, 0x103C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, }, {0x8086, 0x103D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, }, @@ -816,20 +827,40 @@ static struct pci_driver e100_driver = { .resume = e100_resume, #endif }; +static struct pci_driver e100_driver_pre = { + .name = "e100", + .id_table = e100_id_table_pre, + .probe = e100_found1, + .remove = __devexit_p(e100_remove1), +#ifdef CONFIG_PM + .suspend = e100_suspend, + .resume = e100_resume, +#endif +}; static int __init e100_init_module(void) { - int ret; - ret = pci_module_init(&e100_driver); + int ret1,ret2; + + ret1 = pci_module_init(&e100_driver_pre); + if (!ret1 || ret1 == -ENODEV) { + ret2 = pci_module_init(&e100_driver); + } + else { + return ret1; + } + if (!ret1 && ret2 == -ENODEV) { + ret2 = 0; + } - if(ret >= 0) { + if(ret2 >= 0) { #ifdef CONFIG_PM register_reboot_notifier(&e100_notifier_reboot); -#endif +#endif } - return ret; + return ret2; } static void __exit @@ -1453,7 +1484,6 @@ e100_sw_init(struct e100_private *bdp) spin_lock_init(&(bdp->mdi_access_lock)); /* Initialize configuration data */ e100_config_init(bdp); - return 1; } @@ -2018,20 +2048,23 @@ e100intr(int irq, void *dev_inst, struct pt_regs *regs) return IRQ_NONE; } - /* disable and ack intr */ e100_disable_clear_intr(bdp); - /* the device is closed, don't continue or else bad things may happen. */ if (!netif_running(dev)) { - e100_set_intr_mask(bdp); return IRQ_NONE; } - /* SWI intr (triggered by watchdog) is signal to allocate new skb buffers */ if (intr_status & SCB_STATUS_ACK_SWI) { e100_alloc_skbs(bdp); } - +#ifdef CONFIG_E100_NAPI + if (netif_rx_schedule_prep(dev)) { + __netif_rx_schedule(dev); + } + else { + e100_set_intr_mask(bdp); + } +#else /* do recv work if any */ if (intr_status & (SCB_STATUS_ACK_FR | SCB_STATUS_ACK_RNR | SCB_STATUS_ACK_SWI)) @@ -2042,9 +2075,32 @@ e100intr(int irq, void *dev_inst, struct pt_regs *regs) e100_tx_srv(bdp); e100_set_intr_mask(bdp); +#endif return IRQ_HANDLED; } +#ifdef CONFIG_E100_NAPI +static int +e100_poll(struct net_device *netdev, int *budget) +{ + struct e100_private *bdp = netdev->priv; + int work_to_do = min(*budget, netdev->quota); + int work_done = 0; + + e100_tx_srv(bdp); + e100_rx_srv(bdp, &work_done, work_to_do); + + *budget -= work_done; + netdev->quota -= work_done; + + if(work_done < work_to_do) { + netif_rx_complete(netdev); + e100_set_intr_mask(bdp); + } + return (work_done >= work_to_do); +} +#endif + /** * e100_tx_srv - service TX queues * @bdp: atapter's private data struct @@ -2066,7 +2122,6 @@ e100_tx_srv(struct e100_private *bdp) tcb += bdp->tcb_pool.head; rmb(); - /* if the buffer at 'head' is not complete, break */ if (!(tcb->tcb_hdr.cb_status & __constant_cpu_to_le16(CB_STATUS_COMPLETE))) @@ -2100,7 +2155,11 @@ e100_tx_srv(struct e100_private *bdp) * It returns the number of serviced RFDs. */ u32 +#ifdef CONFIG_E100_NAPI +e100_rx_srv(struct e100_private *bdp, int *work_done, int work_to_do) +#else e100_rx_srv(struct e100_private *bdp) +#endif { rfd_t *rfd; /* new rfd, received rfd */ int i; @@ -2128,7 +2187,11 @@ e100_rx_srv(struct e100_private *bdp) if (list_empty(&(bdp->active_rx_list))) { break; } - +#ifdef CONFIG_E100_NAPI + if(*work_done >= work_to_do) + break; + (*work_done)++; +#endif rx_struct = list_entry(bdp->active_rx_list.next, struct rx_list_elem, list_elem); skb = rx_struct->skb; @@ -2192,13 +2255,13 @@ e100_rx_srv(struct e100_private *bdp) skb->ip_summed = CHECKSUM_NONE; } - if(bdp->vlgrp && (rfd_status & CB_STATUS_VLAN)) { - vlan_hwaccel_rx(skb, bdp->vlgrp, be16_to_cpu(rfd->vlanid)); - } else { - netif_rx(skb); - } +#ifdef CONFIG_E100_NAPI + netif_receive_skb(skb); +#else + netif_rx(skb); +#endif dev->last_rx = jiffies; - + rfd_cnt++; } /* end of rfd loop */ @@ -4242,45 +4305,6 @@ exit: spin_unlock_bh(&(bdp->bd_non_tx_lock)); } -static void -e100_vlan_rx_register(struct net_device *netdev, struct vlan_group *grp) -{ - struct e100_private *bdp = netdev->priv; - - e100_disable_clear_intr(bdp); - bdp->vlgrp = grp; - - if(grp) { - /* enable VLAN tag insert/strip */ - e100_config_vlan_drop(bdp, true); - - } else { - /* disable VLAN tag insert/strip */ - e100_config_vlan_drop(bdp, false); - } - - e100_config(bdp); - e100_set_intr_mask(bdp); -} - -static void -e100_vlan_rx_add_vid(struct net_device *netdev, u16 vid) -{ - /* We don't do Vlan filtering */ - return; -} - -static void -e100_vlan_rx_kill_vid(struct net_device *netdev, u16 vid) -{ - struct e100_private *bdp = netdev->priv; - - if(bdp->vlgrp) - bdp->vlgrp->vlan_devices[vid] = NULL; - /* We don't do Vlan filtering */ - return; -} - #ifdef CONFIG_PM static int e100_notify_reboot(struct notifier_block *nb, unsigned long event, void *p) diff --git a/drivers/net/eepro100.c b/drivers/net/eepro100.c index 0877586..6cd5269 100644 --- a/drivers/net/eepro100.c +++ b/drivers/net/eepro100.c @@ -2369,13 +2369,17 @@ static void __devexit eepro100_remove_one (struct pci_dev *pdev) kfree(dev); } +static struct pci_device_id eepro100_pci_tbl_pre[] __devinitdata = { + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_7, + PCI_ANY_ID, PCI_ANY_ID, }, + { PCI_VENDOR_ID_INTEL, 0x103A, PCI_ANY_ID, PCI_ANY_ID, }, + { 0,} +}; static struct pci_device_id eepro100_pci_tbl[] __devinitdata = { { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82557, PCI_ANY_ID, PCI_ANY_ID, }, { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82559ER, PCI_ANY_ID, PCI_ANY_ID, }, - { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_7, - PCI_ANY_ID, PCI_ANY_ID, }, { PCI_VENDOR_ID_INTEL, 0x1029, PCI_ANY_ID, PCI_ANY_ID, }, { PCI_VENDOR_ID_INTEL, 0x1030, PCI_ANY_ID, PCI_ANY_ID, }, { PCI_VENDOR_ID_INTEL, 0x1031, PCI_ANY_ID, PCI_ANY_ID, }, @@ -2387,7 +2391,6 @@ static struct pci_device_id eepro100_pci_tbl[] __devinitdata = { { PCI_VENDOR_ID_INTEL, 0x1037, PCI_ANY_ID, PCI_ANY_ID, }, { PCI_VENDOR_ID_INTEL, 0x1038, PCI_ANY_ID, PCI_ANY_ID, }, { PCI_VENDOR_ID_INTEL, 0x1039, PCI_ANY_ID, PCI_ANY_ID, }, - { PCI_VENDOR_ID_INTEL, 0x103A, PCI_ANY_ID, PCI_ANY_ID, }, { PCI_VENDOR_ID_INTEL, 0x103B, PCI_ANY_ID, PCI_ANY_ID, }, { PCI_VENDOR_ID_INTEL, 0x103C, PCI_ANY_ID, PCI_ANY_ID, }, { PCI_VENDOR_ID_INTEL, 0x103D, PCI_ANY_ID, PCI_ANY_ID, }, @@ -2405,6 +2408,16 @@ static struct pci_device_id eepro100_pci_tbl[] __devinitdata = { }; MODULE_DEVICE_TABLE(pci, eepro100_pci_tbl); +static struct pci_driver eepro100_driver_pre = { + name: "eepro100", + id_table: eepro100_pci_tbl_pre, + probe: eepro100_init_one, + remove: __devexit_p(eepro100_remove_one), +#ifdef CONFIG_PM + suspend: eepro100_suspend, + resume: eepro100_resume, +#endif /* CONFIG_PM */ +}; static struct pci_driver eepro100_driver = { name: "eepro100", id_table: eepro100_pci_tbl, @@ -2434,15 +2447,30 @@ static int pci_module_init(struct pci_driver *pdev) static int __init eepro100_init_module(void) { + int r1,r2; #ifdef MODULE printk(version); #endif - return pci_module_init(&eepro100_driver); + + r1 = pci_module_init(&eepro100_driver_pre); + if ((r1==0) || (r1==-ENODEV)) { + r2 = pci_module_init(&eepro100_driver); + } + else { + return r1; + } + + if ((r1==0) && (r2==-ENODEV)) { + r2 = 0; + } + + return r2; } static void __exit eepro100_cleanup_module(void) { pci_unregister_driver(&eepro100_driver); + pci_unregister_driver(&eepro100_driver_pre); } module_init(eepro100_init_module); diff --git a/drivers/net/natsemi.c b/drivers/net/natsemi.c index 215473a..d51b50e 100644 --- a/drivers/net/natsemi.c +++ b/drivers/net/natsemi.c @@ -136,7 +136,6 @@ TODO: * big endian support with CFG:BEM instead of cpu_to_le32 * support for an external PHY - * NAPI */ #if !defined(__OPTIMIZE__) @@ -172,7 +171,11 @@ #include #define DRV_NAME "natsemi" +#ifdef CONFIG_NATSEMI_NAPI +#define DRV_VERSION "1.07+LK1.0.17-NAPI" +#else #define DRV_VERSION "1.07+LK1.0.17" +#endif #define DRV_RELDATE "Sep 27, 2002" #define RX_OFFSET 2 @@ -188,9 +191,10 @@ NETIF_MSG_RX_ERR | \ NETIF_MSG_TX_ERR) static int debug = -1; - +#ifndef CONFIG_NATSEMI_NAPI /* Maximum events (Rx packets, etc.) to handle at each interrupt. */ static int max_interrupt_work = 20; +#endif static int mtu; /* Maximum number of multicast addresses to filter (vs. rx-all-multicast). @@ -249,15 +253,16 @@ static char version[] __devinitdata = MODULE_AUTHOR("Donald Becker "); MODULE_DESCRIPTION("National Semiconductor DP8381x series PCI Ethernet driver"); MODULE_LICENSE("GPL"); - +#ifndef CONFIG_NATSEMI_NAPI MODULE_PARM(max_interrupt_work, "i"); +MODULE_PARM_DESC(max_interrupt_work, + "DP8381x maximum events handled per interrupt"); +#endif MODULE_PARM(mtu, "i"); MODULE_PARM(debug, "i"); MODULE_PARM(rx_copybreak, "i"); MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i"); MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i"); -MODULE_PARM_DESC(max_interrupt_work, - "DP8381x maximum events handled per interrupt"); MODULE_PARM_DESC(mtu, "DP8381x MTU (all boards)"); MODULE_PARM_DESC(debug, "DP8381x default debug level"); MODULE_PARM_DESC(rx_copybreak, @@ -674,6 +679,9 @@ struct netdev_private { unsigned int iosize; spinlock_t lock; u32 msg_enable; +#ifdef CONFIG_NATSEMI_NAPI + atomic_t irq_sem; +#endif }; static int eeprom_read(long ioaddr, int location); @@ -699,8 +707,12 @@ static void reinit_ring(struct net_device *dev); static void init_registers(struct net_device *dev); static int start_tx(struct sk_buff *skb, struct net_device *dev); static irqreturn_t intr_handler(int irq, void *dev_instance, struct pt_regs *regs); -static void netdev_error(struct net_device *dev, int intr_status); +#ifdef CONFIG_NATSEMI_NAPI +static void netdev_rx(struct net_device *dev, int *work_done, int work_to_do); +#else static void netdev_rx(struct net_device *dev); +#endif +static void netdev_error(struct net_device *dev, int intr_status); static void netdev_tx_done(struct net_device *dev); static void __set_rx_mode(struct net_device *dev); static void set_rx_mode(struct net_device *dev); @@ -717,7 +729,39 @@ static void enable_wol_mode(struct net_device *dev, int enable_intr); static int netdev_close(struct net_device *dev); static int netdev_get_regs(struct net_device *dev, u8 *buf); static int netdev_get_eeprom(struct net_device *dev, u8 *buf); +#ifdef CONFIG_NATSEMI_NAPI +static int netdev_poll(struct net_device *dev, int *budget); +#endif +static void intr_enable(struct net_device *dev) +{ + struct netdev_private *np = dev->priv; +#ifdef CONFIG_NATSEMI_NAPI + if(atomic_dec_and_test(&np->irq_sem)) { + writel(DEFAULT_INTR, dev->base_addr + IntrMask); + writel(1, dev->base_addr + IntrEnable); + readl(dev->base_addr + IntrEnable); // read safe + mb(); + } +#else + writel(DEFAULT_INTR, dev->base_addr + IntrMask); + writel(1, dev->base_addr + IntrEnable); + readl(dev->base_addr + IntrEnable); // read safe + mb(); +#endif + +} + +static void intr_disable(struct net_device *dev) +{ + struct netdev_private *np = dev->priv; +#ifdef CONFIG_NATSEMI_NAPI + atomic_inc(&np->irq_sem); +#endif + writel(0, dev->base_addr + IntrEnable); + readl(dev->base_addr + IntrEnable); // read safe + mb(); +} static int __devinit natsemi_probe1 (struct pci_dev *pdev, const struct pci_device_id *ent) @@ -793,6 +837,9 @@ static int __devinit natsemi_probe1 (struct pci_dev *pdev, pci_set_drvdata(pdev, dev); np->iosize = iosize; spin_lock_init(&np->lock); +#ifdef CONFIG_NATSEMI_NAPI + atomic_set(&np->irq_sem, 1); +#endif np->msg_enable = (debug >= 0) ? (1<hands_off = 0; @@ -825,6 +872,10 @@ static int __devinit natsemi_probe1 (struct pci_dev *pdev, dev->do_ioctl = &netdev_ioctl; dev->tx_timeout = &tx_timeout; dev->watchdog_timeo = TX_TIMEOUT; +#ifdef CONFIG_NATSEMI_NAPI + dev->poll = netdev_poll; + dev->weight = 16; +#endif if (mtu) dev->mtu = mtu; @@ -1294,7 +1345,7 @@ static void init_registers(struct net_device *dev) /* DRTH 0x10: start copying to memory if 128 bytes are in the fifo * MXDMA 0: up to 256 byte bursts */ - np->rx_config = RxMxdma_256 | 0x20; + np->rx_config = RxMxdma_256 | 0x20 | RxAcceptLong; writel(np->rx_config, ioaddr + RxConfig); /* Disable PME: @@ -1314,8 +1365,10 @@ static void init_registers(struct net_device *dev) __set_rx_mode(dev); /* Enable interrupts by setting the interrupt mask. */ - writel(DEFAULT_INTR, ioaddr + IntrMask); - writel(1, ioaddr + IntrEnable); +/* writel(DEFAULT_INTR, ioaddr + IntrMask); + writel(1, ioaddr + IntrEnable);*/ + printk(KERN_INFO "Set Interrupt enable in init_registers\n"); + intr_enable(dev); writel(RxOn | TxOn, ioaddr + ChipCmd); writel(StatsClear, ioaddr + StatsCtrl); /* Clear Stats */ @@ -1626,7 +1679,7 @@ static int start_tx(struct sk_buff *skb, struct net_device *dev) /* Wake the potentially-idle transmit channel. */ writel(TxOn, dev->base_addr + ChipCmd); } else { - dev_kfree_skb_irq(skb); + dev_kfree_skb_any(skb); np->stats.tx_dropped++; } spin_unlock_irq(&np->lock); @@ -1673,7 +1726,7 @@ static void netdev_tx_done(struct net_device *dev) np->tx_skbuff[entry]->len, PCI_DMA_TODEVICE); /* Free the original skb. */ - dev_kfree_skb_irq(np->tx_skbuff[entry]); + dev_kfree_skb_any(np->tx_skbuff[entry]); np->tx_skbuff[entry] = NULL; } if (netif_queue_stopped(dev) @@ -1689,15 +1742,31 @@ static irqreturn_t intr_handler(int irq, void *dev_instance, struct pt_regs *rgs { struct net_device *dev = dev_instance; struct netdev_private *np = dev->priv; - long ioaddr = dev->base_addr; + u32 intr_status; +#ifndef CONFIG_NATSEMI_NAPI int boguscnt = max_interrupt_work; - unsigned int handled = 0; + long ioaddr = dev->base_addr; +#endif - if (np->hands_off) + if (np->hands_off) { return IRQ_NONE; + } +#ifdef CONFIG_NATSEMI_NAPI + intr_status = readl(dev->base_addr + IntrStatus); + /* Abnormal error summary/uncommon events handlers. */ + if (intr_status & IntrAbnormalSummary) + netdev_error(dev, intr_status); + + if (netif_rx_schedule_prep(dev)) { + intr_disable(dev); + __netif_rx_schedule(dev); + } + return IRQ_HANDLED; +#else + do { /* Reading automatically acknowledges all int sources. */ - u32 intr_status = readl(ioaddr + IntrStatus); + intr_status = readl(ioaddr + IntrStatus); if (netif_msg_intr(np)) printk(KERN_DEBUG @@ -1740,11 +1809,44 @@ static irqreturn_t intr_handler(int irq, void *dev_instance, struct pt_regs *rgs printk(KERN_DEBUG "%s: exiting interrupt.\n", dev->name); return IRQ_RETVAL(handled); +#endif +} + +#ifdef CONFIG_NATSEMI_NAPI +static int netdev_poll(struct net_device *dev, int *budget) +{ + struct netdev_private *np = dev->priv; + int work_to_do = min(*budget, dev->quota); + int work_done = 0; + + spin_lock(&np->lock); + netdev_tx_done(dev); + spin_unlock(&np->lock); + + netdev_rx(dev, &work_done, work_to_do); + + *budget -= work_done; + dev->quota -= work_done; + + if (work_done < work_to_do) { + netif_rx_complete(dev); + /** + * Enable Irq + */ + intr_enable(dev); + } + return (work_done >= work_to_do); } +#endif + /* This routine is logically part of the interrupt handler, but separated for clarity and better register allocation. */ +#ifdef CONFIG_NATSEMI_NAPI +static void netdev_rx(struct net_device *dev, int *work_done, int work_to_do) +#else static void netdev_rx(struct net_device *dev) +#endif { struct netdev_private *np = dev->priv; int entry = np->cur_rx % RX_RING_SIZE; @@ -1760,6 +1862,11 @@ static void netdev_rx(struct net_device *dev) entry, desc_status); if (--boguscnt < 0) break; +#ifdef CONFIG_NATSEMI_NAPI + if (*work_done >= work_to_do) + break; + (*work_done)++; +#endif if ((desc_status&(DescMore|DescPktOK|DescRxLong)) != DescPktOK){ if (desc_status & DescMore) { if (netif_msg_rx_err(np)) @@ -1812,7 +1919,11 @@ static void netdev_rx(struct net_device *dev) np->rx_skbuff[entry] = NULL; } skb->protocol = eth_type_trans(skb, dev); +#ifdef CONFIG_NATSEMI_NAPI + netif_receive_skb(skb); +#else netif_rx(skb); +#endif dev->last_rx = jiffies; np->stats.rx_packets++; np->stats.rx_bytes += pkt_len; @@ -2468,6 +2579,7 @@ static void enable_wol_mode(struct net_device *dev, int enable_intr) /* enable the WOL interrupt. * Could be used to send a netlink message. */ + printk(KERN_INFO "Enable WOL Interrupt\n"); writel(WOLPkt | LinkChange, ioaddr + IntrMask); writel(1, ioaddr + IntrEnable); } @@ -2499,8 +2611,7 @@ static int netdev_close(struct net_device *dev) disable_irq(dev->irq); spin_lock_irq(&np->lock); /* Disable interrupts, and flush posted writes */ - writel(0, ioaddr + IntrEnable); - readl(ioaddr + IntrEnable); + intr_disable(dev); np->hands_off = 1; spin_unlock_irq(&np->lock); enable_irq(dev->irq); @@ -2598,7 +2709,7 @@ static int natsemi_suspend (struct pci_dev *pdev, u32 state) disable_irq(dev->irq); spin_lock_irq(&np->lock); - writel(0, ioaddr + IntrEnable); + intr_disable(dev); np->hands_off = 1; natsemi_stop_rxtx(dev); netif_stop_queue(dev); diff --git a/drivers/net/starfire.c b/drivers/net/starfire.c index 14a25a4..77b6d38 100644 --- a/drivers/net/starfire.c +++ b/drivers/net/starfire.c @@ -171,10 +171,6 @@ TODO: #include "starfire_firmware.h" #endif /* HAS_FIRMWARE */ -#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE) -#define VLAN_SUPPORT -#endif - /* The user-configurable values. These may be modified when a driver module is loaded.*/ @@ -1977,9 +1973,6 @@ static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) struct mii_ioctl_data *data = (struct mii_ioctl_data *) & rq->ifr_data; int rc; - if (!netif_running(dev)) - return -EINVAL; - if (cmd == SIOCETHTOOL) rc = netdev_ethtool_ioctl(dev, (void *) rq->ifr_data); diff --git a/drivers/net/sundance.c b/drivers/net/sundance.c index aba75be..c44f9af 100644 --- a/drivers/net/sundance.c +++ b/drivers/net/sundance.c @@ -1654,9 +1654,6 @@ static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) int i; long ioaddr = dev->base_addr; - if (!netif_running(dev)) - return -EINVAL; - if (cmd == SIOCETHTOOL) rc = netdev_ethtool_ioctl(dev, (void *) rq->ifr_data); diff --git a/drivers/net/tulip/interrupt.c b/drivers/net/tulip/interrupt.c index 802004b..a3003ff 100644 --- a/drivers/net/tulip/interrupt.c +++ b/drivers/net/tulip/interrupt.c @@ -23,9 +23,11 @@ int tulip_rx_copybreak; unsigned int tulip_max_interrupt_work; -#ifdef CONFIG_NET_HW_FLOWCONTROL +#ifdef CONFIG_TULIP_NAPI_HW_MITIGATION #define MIT_SIZE 15 +#define MIT_TABLE 15 /* We use 0 or max */ + unsigned int mit_table[MIT_SIZE+1] = { /* CRS11 21143 hardware Mitigation Control Interrupt @@ -100,16 +102,27 @@ int tulip_refill_rx(struct net_device *dev) } -static int tulip_rx(struct net_device *dev) +#ifdef CONFIG_TULIP_NAPI +void oom_timer(unsigned long data) +{ +struct net_device *dev = (struct net_device *)data; + netif_rx_schedule(dev); +} + +int tulip_poll(struct net_device *dev, int *budget) { struct tulip_private *tp = (struct tulip_private *)dev->priv; int entry = tp->cur_rx % RX_RING_SIZE; - int rx_work_limit = tp->dirty_rx + RX_RING_SIZE - tp->cur_rx; + int rx_work_limit = *budget; int received = 0; -#ifdef CONFIG_NET_HW_FLOWCONTROL - int drop = 0, mit_sel = 0; + if (!netif_running(dev)) + goto done; + + if (rx_work_limit > dev->quota) + rx_work_limit = dev->quota; +#ifdef CONFIG_TULIP_NAPI_HW_MITIGATION /* that one buffer is needed for mit activation; or might be a bug in the ring buffer code; check later -- JHS*/ @@ -119,6 +132,237 @@ static int tulip_rx(struct net_device *dev) if (tulip_debug > 4) printk(KERN_DEBUG " In tulip_rx(), entry %d %8.8x.\n", entry, tp->rx_ring[entry].status); + + do { + if (inl(dev->base_addr + CSR5) == 0xffffffff) { + printk(KERN_DEBUG " In tulip_poll(), hardware disappeared.\n"); + break; + } + /* Acknowledge current RX interrupt sources. */ + outl((RxIntr | RxNoBuf), dev->base_addr + CSR5); + + + /* If we own the next entry, it is a new packet. Send it up. */ + while ( ! (tp->rx_ring[entry].status & cpu_to_le32(DescOwned))) { + s32 status = le32_to_cpu(tp->rx_ring[entry].status); + + + if (tp->dirty_rx + RX_RING_SIZE == tp->cur_rx) + break; + + if (tulip_debug > 5) + printk(KERN_DEBUG "%s: In tulip_rx(), entry %d %8.8x.\n", + dev->name, entry, status); + if (--rx_work_limit < 0) + goto not_done; + + if ((status & (0x38000000 | RxDescFatalErr | RxWholePkt)) != RxWholePkt) { + if ((status & (0x38000000 | RxWholePkt)) != RxWholePkt) { + /* Ingore earlier buffers. */ + if ((status & 0xffff) != 0x7fff) { + if (tulip_debug > 1) + printk(KERN_WARNING "%s: Oversized Ethernet frame " + "spanned multiple buffers, status %8.8x!\n", + dev->name, status); + tp->stats.rx_length_errors++; + } + } + else if (status & RxDescFatalErr) { + /* There was a fatal error. */ + if (tulip_debug > 2) + printk(KERN_DEBUG "%s: Receive error, Rx status %8.8x.\n", + dev->name, status); + tp->stats.rx_errors++; /* end of a packet.*/ + if (status & 0x0890) tp->stats.rx_length_errors++; + if (status & 0x0004) tp->stats.rx_frame_errors++; + if (status & 0x0002) tp->stats.rx_crc_errors++; + if (status & 0x0001) tp->stats.rx_fifo_errors++; + } + } + else { + /* Omit the four octet CRC from the length. */ + short pkt_len = ((status >> 16) & 0x7ff) - 4; + struct sk_buff *skb; + +#ifndef final_version + if (pkt_len > 1522) { + printk(KERN_WARNING "%s: Bogus packet size of %d (%#x).\n", + dev->name, pkt_len, pkt_len); + pkt_len = 1522; + tp->stats.rx_length_errors++; + } +#endif + /* Check if the packet is long enough to accept without copying + to a minimally-sized skbuff. */ + if (pkt_len < tulip_rx_copybreak + && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { + skb->dev = dev; + skb_reserve(skb, 2); /* 16 byte align the IP header */ +#if ! defined(__alpha__) + eth_copy_and_sum(skb, tp->rx_buffers[entry].skb->tail, + pkt_len, 0); + skb_put(skb, pkt_len); +#else + memcpy(skb_put(skb, pkt_len), + tp->rx_buffers[entry].skb->tail, + pkt_len); +#endif + } + else { /* Pass up the skb already on the Rx ring. */ + char *temp = skb_put(skb = tp->rx_buffers[entry].skb, + pkt_len); + +#ifndef final_version + if (tp->rx_buffers[entry].mapping != + le32_to_cpu(tp->rx_ring[entry].buffer1)) { + printk(KERN_ERR "%s: Internal fault: The skbuff addresses " + "do not match in tulip_rx: %08x vs. %08llx %p / %p.\n", + dev->name, + le32_to_cpu(tp->rx_ring[entry].buffer1), + (unsigned long long)tp->rx_buffers[entry].mapping, + skb->head, temp); + } +#endif + + tp->rx_buffers[entry].skb = NULL; + tp->rx_buffers[entry].mapping = 0; + } + skb->protocol = eth_type_trans(skb, dev); + + netif_receive_skb(skb); + + dev->last_rx = jiffies; + tp->stats.rx_packets++; + tp->stats.rx_bytes += pkt_len; + } + received++; + + entry = (++tp->cur_rx) % RX_RING_SIZE; + if (tp->cur_rx - tp->dirty_rx > RX_RING_SIZE/4) + tulip_refill_rx(dev); + + } + + /* New ack strategy... irq does not ack Rx any longer + hopefully this helps */ + + /* Really bad things can happen here... If new packet arrives + * and an irq arrives (tx or just due to occasionally unset + * mask), it will be acked by irq handler, but new thread + * is not scheduled. It is major hole in design. + * No idea how to fix this if "playing with fire" will fail + * tomorrow (night 011029). If it will not fail, we won + * finally: amount of IO did not increase at all. */ + } while ((inl(dev->base_addr + CSR5) & RxIntr)); + +done: + +#ifdef CONFIG_TULIP_NAPI_HW_MITIGATION + + /* We use this simplistic scheme for IM. It's proven by + real life installations. We can have IM enabled + continuesly but this would cause unnecessary latency. + Unfortunely we can't use all the NET_RX_* feedback here. + This would turn on IM for devices that is not contributing + to backlog congestion with unnecessary latency. + + We monitor the the device RX-ring and have: + + HW Interrupt Mitigation either ON or OFF. + + ON: More then 1 pkt received (per intr.) OR we are dropping + OFF: Only 1 pkt received + + Note. We only use min and max (0, 15) settings from mit_table */ + + + if( tp->flags & HAS_INTR_MITIGATION) { + if( received > 1 ) { + if( ! tp->mit_on ) { + tp->mit_on = 1; + outl(mit_table[MIT_TABLE], dev->base_addr + CSR11); + } + } + else { + if( tp->mit_on ) { + tp->mit_on = 0; + outl(0, dev->base_addr + CSR11); + } + } + } + +#endif /* CONFIG_TULIP_NAPI_HW_MITIGATION */ + + dev->quota -= received; + *budget -= received; + + tulip_refill_rx(dev); + + /* If RX ring is not full we are out of memory. */ + if (tp->rx_buffers[tp->dirty_rx % RX_RING_SIZE].skb == NULL) goto oom; + + /* Remove us from polling list and enable RX intr. */ + + netif_rx_complete(dev); + outl(tulip_tbl[tp->chip_id].valid_intrs, dev->base_addr+CSR7); + + /* The last op happens after poll completion. Which means the following: + * 1. it can race with disabling irqs in irq handler + * 2. it can race with dise/enabling irqs in other poll threads + * 3. if an irq raised after beginning loop, it will be immediately + * triggered here. + * + * Summarizing: the logic results in some redundant irqs both + * due to races in masking and due to too late acking of already + * processed irqs. But it must not result in losing events. + */ + + return 0; + +not_done: + if (!received) { + + received = dev->quota; /* Not to happen */ + } + dev->quota -= received; + *budget -= received; + + if (tp->cur_rx - tp->dirty_rx > RX_RING_SIZE/2 || + tp->rx_buffers[tp->dirty_rx % RX_RING_SIZE].skb == NULL) + tulip_refill_rx(dev); + + if (tp->rx_buffers[tp->dirty_rx % RX_RING_SIZE].skb == NULL) goto oom; + + return 1; + + +oom: /* Executed with RX ints disabled */ + + /* Start timer, stop polling, but do not enable rx interrupts. */ + mod_timer(&tp->oom_timer, jiffies+1); + + /* Think: timer_pending() was an explicit signature of bug. + * Timer can be pending now but fired and completed + * before we did netif_rx_complete(). See? We would lose it. */ + + /* remove ourselves from the polling list */ + netif_rx_complete(dev); + + return 0; +} + +#else /* CONFIG_TULIP_NAPI */ + +static int tulip_rx(struct net_device *dev) +{ + struct tulip_private *tp = (struct tulip_private *)dev->priv; + int entry = tp->cur_rx % RX_RING_SIZE; + int rx_work_limit = tp->dirty_rx + RX_RING_SIZE - tp->cur_rx; + int received = 0; + + if (tulip_debug > 4) + printk(KERN_DEBUG " In tulip_rx(), entry %d %8.8x.\n", entry, + tp->rx_ring[entry].status); /* If we own the next entry, it is a new packet. Send it up. */ while ( ! (tp->rx_ring[entry].status & cpu_to_le32(DescOwned))) { s32 status = le32_to_cpu(tp->rx_ring[entry].status); @@ -128,8 +372,8 @@ static int tulip_rx(struct net_device *dev) dev->name, entry, status); if (--rx_work_limit < 0) break; - if ((status & 0x38008300) != 0x0300) { - if ((status & 0x38000300) != 0x0300) { + if ((status & (0x38000000 | RxDescFatalErr | RxWholePkt)) != RxWholePkt) { + if ((status & (0x38000000 | RxWholePkt)) != RxWholePkt) { /* Ingore earlier buffers. */ if ((status & 0xffff) != 0x7fff) { if (tulip_debug > 1) @@ -155,19 +399,13 @@ static int tulip_rx(struct net_device *dev) struct sk_buff *skb; #ifndef final_version - if (pkt_len > 1518) { + if (pkt_len > 1522) { printk(KERN_WARNING "%s: Bogus packet size of %d (%#x).\n", dev->name, pkt_len, pkt_len); - pkt_len = 1518; + pkt_len = 1522; tp->stats.rx_length_errors++; } #endif - -#ifdef CONFIG_NET_HW_FLOWCONTROL - drop = atomic_read(&netdev_dropping); - if (drop) - goto throttle; -#endif /* Check if the packet is long enough to accept without copying to a minimally-sized skbuff. */ if (pkt_len < tulip_rx_copybreak @@ -209,44 +447,7 @@ static int tulip_rx(struct net_device *dev) tp->rx_buffers[entry].mapping = 0; } skb->protocol = eth_type_trans(skb, dev); -#ifdef CONFIG_NET_HW_FLOWCONTROL - mit_sel = -#endif netif_rx(skb); - -#ifdef CONFIG_NET_HW_FLOWCONTROL - switch (mit_sel) { - case NET_RX_SUCCESS: - case NET_RX_CN_LOW: - case NET_RX_CN_MOD: - break; - - case NET_RX_CN_HIGH: - rx_work_limit -= NET_RX_CN_HIGH; /* additional*/ - break; - case NET_RX_DROP: - rx_work_limit = -1; - break; - default: - printk("unknown feedback return code %d\n", mit_sel); - break; - } - - drop = atomic_read(&netdev_dropping); - if (drop) { -throttle: - rx_work_limit = -1; - mit_sel = NET_RX_DROP; - - if (tp->fc_bit) { - long ioaddr = dev->base_addr; - - /* disable Rx & RxNoBuf ints. */ - outl(tulip_tbl[tp->chip_id].valid_intrs&RX_A_NBF_STOP, ioaddr + CSR7); - set_bit(tp->fc_bit, &netdev_fc_xoff); - } - } -#endif dev->last_rx = jiffies; tp->stats.rx_packets++; tp->stats.rx_bytes += pkt_len; @@ -254,44 +455,10 @@ throttle: received++; entry = (++tp->cur_rx) % RX_RING_SIZE; } -#ifdef CONFIG_NET_HW_FLOWCONTROL - - /* We use this simplistic scheme for IM. It's proven by - real life installations. We can have IM enabled - continuesly but this would cause unnecessary latency. - Unfortunely we can't use all the NET_RX_* feedback here. - This would turn on IM for devices that is not contributing - to backlog congestion with unnecessary latency. - - We monitor the device RX-ring and have: - - HW Interrupt Mitigation either ON or OFF. - - ON: More then 1 pkt received (per intr.) OR we are dropping - OFF: Only 1 pkt received - - Note. We only use min and max (0, 15) settings from mit_table */ - - - if( tp->flags & HAS_INTR_MITIGATION) { - if((received > 1 || mit_sel == NET_RX_DROP) - && tp->mit_sel != 15 ) { - tp->mit_sel = 15; - tp->mit_change = 1; /* Force IM change */ - } - if((received <= 1 && mit_sel != NET_RX_DROP) && tp->mit_sel != 0 ) { - tp->mit_sel = 0; - tp->mit_change = 1; /* Force IM change */ - } - } - - return RX_RING_SIZE+1; /* maxrx+1 */ -#else return received; -#endif } - -static inline void phy_interrupt (struct net_device *dev) +#endif /* CONFIG_TULIP_NAPI */ +static inline unsigned int phy_interrupt (struct net_device *dev) { #ifdef __hppa__ int csr12 = inl(dev->base_addr + CSR12) & 0xff; @@ -307,19 +474,20 @@ static inline void phy_interrupt (struct net_device *dev) spin_unlock(&tp->lock); /* clear irq ack bit */ outl(csr12 & ~0x02, dev->base_addr + CSR12); + return 1; } + return 0; #endif } /* The interrupt handler does all of the Rx thread work and cleans up after the Tx thread. */ -void tulip_interrupt(int irq, void *dev_instance, struct pt_regs *regs) +irqreturn_t tulip_interrupt(int irq, void *dev_instance, struct pt_regs *regs) { struct net_device *dev = (struct net_device *)dev_instance; struct tulip_private *tp = (struct tulip_private *)dev->priv; long ioaddr = dev->base_addr; int csr5; - int entry; int missed; int rx = 0; int tx = 0; @@ -327,35 +495,55 @@ void tulip_interrupt(int irq, void *dev_instance, struct pt_regs *regs) int maxrx = RX_RING_SIZE; int maxtx = TX_RING_SIZE; int maxoi = TX_RING_SIZE; +#ifdef CONFIG_TULIP_NAPI + int rxd = 0; +#else + int entry; +#endif unsigned int work_count = tulip_max_interrupt_work; + unsigned int handled = 0; /* Let's see whether the interrupt really is for us */ csr5 = inl(ioaddr + CSR5); if (tp->flags & HAS_PHY_IRQ) - phy_interrupt (dev); - + handled = phy_interrupt (dev); + if ((csr5 & (NormalIntr|AbnormalIntr)) == 0) - return; + return IRQ_RETVAL(handled); + ; tp->nir++; do { + +#ifdef CONFIG_TULIP_NAPI + if (!rxd && (csr5 & (RxIntr | RxNoBuf))) { + rxd++; + /* Mask RX intrs and add the device to poll list. */ + outl(tulip_tbl[tp->chip_id].valid_intrs&~RxPollInt, ioaddr + CSR7); + netif_rx_schedule(dev); + + if (!(csr5&~(AbnormalIntr|NormalIntr|RxPollInt|TPLnkPass))) + break; + } + + /* Acknowledge the interrupt sources we handle here ASAP + the poll function does Rx and RxNoBuf acking */ + + outl(csr5 & 0x0001ff3f, ioaddr + CSR5); +#else /* Acknowledge all of the current interrupt sources ASAP. */ outl(csr5 & 0x0001ffff, ioaddr + CSR5); - if (tulip_debug > 4) - printk(KERN_DEBUG "%s: interrupt csr5=%#8.8x new csr5=%#8.8x.\n", - dev->name, csr5, inl(dev->base_addr + CSR5)); - if (csr5 & (RxIntr | RxNoBuf)) { -#ifdef CONFIG_NET_HW_FLOWCONTROL - if ((!tp->fc_bit) || - (!test_bit(tp->fc_bit, &netdev_fc_xoff))) -#endif rx += tulip_rx(dev); tulip_refill_rx(dev); } +#endif + if (tulip_debug > 4) + printk(KERN_DEBUG "%s: interrupt csr5=%#8.8x new csr5=%#8.8x.\n", + dev->name, csr5, inl(dev->base_addr + CSR5)); if (csr5 & (TxNoBuf | TxDied | TxIntr | TimerInt)) { unsigned int dirty_tx; @@ -457,15 +645,8 @@ void tulip_interrupt(int irq, void *dev_instance, struct pt_regs *regs) } if (csr5 & RxDied) { /* Missed a Rx frame. */ tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff; -#ifdef CONFIG_NET_HW_FLOWCONTROL - if (tp->fc_bit && !test_bit(tp->fc_bit, &netdev_fc_xoff)) { - tp->stats.rx_errors++; - tulip_start_rxtx(tp); - } -#else tp->stats.rx_errors++; tulip_start_rxtx(tp); -#endif } /* * NB: t21142_lnk_change() does a del_timer_sync(), so be careful if this @@ -499,32 +680,21 @@ void tulip_interrupt(int irq, void *dev_instance, struct pt_regs *regs) if (tulip_debug > 2) printk(KERN_ERR "%s: Re-enabling interrupts, %8.8x.\n", dev->name, csr5); -#ifdef CONFIG_NET_HW_FLOWCONTROL - if (tp->fc_bit && (test_bit(tp->fc_bit, &netdev_fc_xoff))) - if (net_ratelimit()) printk("BUG!! enabling interupt when FC off (timerintr.) \n"); -#endif outl(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR7); tp->ttimer = 0; oi++; } if (tx > maxtx || rx > maxrx || oi > maxoi) { - if (tulip_debug > 1) + /* if (tulip_debug > 1)*/ printk(KERN_WARNING "%s: Too much work during an interrupt, " - "csr5=0x%8.8x. (%lu) (%d,%d,%d)\n", dev->name, csr5, tp->nir, tx, rx, oi); + "csr5=0x%8.8x. (%lu) (%d>%d,%d>%d,%d>%d)\n", dev->name, csr5, tp->nir, tx, maxtx,rx,maxrx, oi,maxoi); /* Acknowledge all interrupt sources. */ outl(0x8001ffff, ioaddr + CSR5); if (tp->flags & HAS_INTR_MITIGATION) { -#ifdef CONFIG_NET_HW_FLOWCONTROL - if(tp->mit_change) { - outl(mit_table[tp->mit_sel], ioaddr + CSR11); - tp->mit_change = 0; - } -#else /* Josip Loncaric at ICASE did extensive experimentation to develop a good interrupt mitigation setting.*/ outl(0x8b240000, ioaddr + CSR11); -#endif } else if (tp->chip_id == LC82C168) { /* the LC82C168 doesn't have a hw timer.*/ outl(0x00, ioaddr + CSR7); @@ -532,10 +702,6 @@ void tulip_interrupt(int irq, void *dev_instance, struct pt_regs *regs) } else { /* Mask all interrupting sources, set timer to re-enable. */ -#ifndef CONFIG_NET_HW_FLOWCONTROL - outl(((~csr5) & 0x0001ebef) | AbnormalIntr | TimerInt, ioaddr + CSR7); - outl(0x0012, ioaddr + CSR11); -#endif } break; } @@ -545,6 +711,20 @@ void tulip_interrupt(int irq, void *dev_instance, struct pt_regs *regs) break; csr5 = inl(ioaddr + CSR5); +#ifdef CONFIG_TULIP_NAPI + if (rxd) + csr5 &= ~RxPollInt; + } while ((csr5 & (TxNoBuf | + TxDied | + TxIntr | + TimerInt | + /* Abnormal intr. */ + RxDied | + TxFIFOUnderflow | + TxJabber | + TPLnkFail | + SytemError )) != 0); +#else } while ((csr5 & (NormalIntr|AbnormalIntr)) != 0); tulip_refill_rx(dev); @@ -569,7 +749,7 @@ void tulip_interrupt(int irq, void *dev_instance, struct pt_regs *regs) } } } - +#endif if ((missed = inl(ioaddr + CSR8) & 0x1ffff)) { tp->stats.rx_dropped += missed & 0x10000 ? 0x10000 : missed; } diff --git a/drivers/net/tulip/tulip.h b/drivers/net/tulip/tulip.h index 758ae05..5929fd8 100644 --- a/drivers/net/tulip/tulip.h +++ b/drivers/net/tulip/tulip.h @@ -38,7 +38,8 @@ # define USE_IO_OPS 1 #endif - +//#define CONFIG_TULIP_NAPI_HW_MITIGATION +//#define CONFIG_TULIP_NAPI struct tulip_chip_table { char *chip_name; @@ -127,6 +128,9 @@ enum pci_cfg_driver_reg { }; +#define RxPollInt (RxIntr|RxNoBuf|RxDied|RxJabber) + + /* The bits in the CSR5 status registers, mostly interrupt sources. */ enum status_bits { TimerInt = 0x800, @@ -191,7 +195,7 @@ struct tulip_tx_desc { enum desc_status_bits { DescOwned = 0x80000000, - RxDescFatalErr = 0x8000, + RxDescFatalErr = 0x4842, RxWholePkt = 0x0300, }; @@ -264,12 +268,12 @@ enum t21143_csr6_bits { Making the Tx ring too large decreases the effectiveness of channel bonding and packet priority. There are no ill effects from too-large receive rings. */ -#define TX_RING_SIZE 16 -#define RX_RING_SIZE 32 +#define TX_RING_SIZE 32 +#define RX_RING_SIZE 128 #define MEDIA_MASK 31 -#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer. */ +#define PKT_BUF_SZ 1540 /* Size of each temporary Rx buffer. */ #define TULIP_MIN_CACHE_LINE 8 /* in units of 32-bit words */ @@ -295,7 +299,7 @@ enum t21143_csr6_bits { #define DESC_RING_WRAP 0x02000000 -#define EEPROM_SIZE 128 /* 2 << EEPROM_ADDRLEN */ +#define EEPROM_SIZE 512 /* 2 << EEPROM_ADDRLEN */ #define RUN_AT(x) (jiffies + (x)) @@ -356,17 +360,14 @@ struct tulip_private { int flags; struct net_device_stats stats; struct timer_list timer; /* Media selection timer. */ + struct timer_list oom_timer; /* Out of memory timer. */ u32 mc_filter[2]; spinlock_t lock; spinlock_t mii_lock; unsigned int cur_rx, cur_tx; /* The next free ring entry */ unsigned int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */ - -#ifdef CONFIG_NET_HW_FLOWCONTROL -#define RX_A_NBF_STOP 0xffffff3f /* To disable RX and RX-NOBUF ints. */ - int fc_bit; - int mit_sel; - int mit_change; /* Signal for Interrupt Mitigtion */ +#ifdef CONFIG_TULIP_NAPI_HW_MITIGATION + int mit_on; #endif unsigned int full_duplex:1; /* Full-duplex operation requested. */ unsigned int full_duplex_lock:1; @@ -426,8 +427,11 @@ int tulip_read_eeprom(long ioaddr, int location, int addr_len); /* interrupt.c */ extern unsigned int tulip_max_interrupt_work; extern int tulip_rx_copybreak; -void tulip_interrupt(int irq, void *dev_instance, struct pt_regs *regs); +irqreturn_t tulip_interrupt(int irq, void *dev_instance, struct pt_regs *regs); int tulip_refill_rx(struct net_device *dev); +#ifdef CONFIG_TULIP_NAPI +int tulip_poll(struct net_device *dev, int *budget); +#endif /* media.c */ int tulip_mdio_read(struct net_device *dev, int phy_id, int location); @@ -451,6 +455,7 @@ extern int tulip_debug; extern const char * const medianame[]; extern const char tulip_media_cap[]; extern struct tulip_chip_table tulip_tbl[]; +void oom_timer(unsigned long data); extern u8 t21040_csr13[]; extern u16 t21041_csr13[]; extern u16 t21041_csr14[]; diff --git a/drivers/net/tulip/tulip_core.c b/drivers/net/tulip/tulip_core.c index d63f096..b36a7bd 100644 --- a/drivers/net/tulip/tulip_core.c +++ b/drivers/net/tulip/tulip_core.c @@ -267,8 +267,12 @@ static void tulip_down(struct net_device *dev); static struct net_device_stats *tulip_get_stats(struct net_device *dev); static int private_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); static void set_rx_mode(struct net_device *dev); +static int tulip_get_ecmd(struct net_device *dev, struct ethtool_cmd *ecmd); +static int tulip_set_ecmd(struct net_device *dev, struct ethtool_cmd *ecmd); - +#ifdef CONFIG_NET_POLL_CONTROLLER +static void poll_tulip(struct net_device *dev); +#endif static void tulip_set_power_state (struct tulip_private *tp, int sleep, int snooze) @@ -499,29 +503,17 @@ media_picked: to an alternate media type. */ tp->timer.expires = RUN_AT(next_tick); add_timer(&tp->timer); -} - -#ifdef CONFIG_NET_HW_FLOWCONTROL -/* Enable receiver */ -void tulip_xon(struct net_device *dev) -{ - struct tulip_private *tp = (struct tulip_private *)dev->priv; - - clear_bit(tp->fc_bit, &netdev_fc_xoff); - if (netif_running(dev)){ +#ifdef CONFIG_TULIP_NAPI + init_timer(&tp->oom_timer); + tp->oom_timer.data = (unsigned long)dev; + tp->oom_timer.function = oom_timer; +#endif - tulip_refill_rx(dev); - outl(tulip_tbl[tp->chip_id].valid_intrs, dev->base_addr+CSR7); - } } -#endif static int tulip_open(struct net_device *dev) { -#ifdef CONFIG_NET_HW_FLOWCONTROL - struct tulip_private *tp = (struct tulip_private *)dev->priv; -#endif int retval; MOD_INC_USE_COUNT; @@ -534,10 +526,6 @@ tulip_open(struct net_device *dev) tulip_up (dev); -#ifdef CONFIG_NET_HW_FLOWCONTROL - tp->fc_bit = netdev_register_fc(dev, tulip_xon); -#endif - netif_start_queue (dev); return 0; @@ -645,10 +633,6 @@ static void tulip_tx_timeout(struct net_device *dev) #endif /* Stop and restart the chip's Tx processes . */ -#ifdef CONFIG_NET_HW_FLOWCONTROL - if (tp->fc_bit && test_bit(tp->fc_bit,&netdev_fc_xoff)) - printk("BUG tx_timeout restarting rx when fc on\n"); -#endif tulip_restart_rxtx(tp); /* Trigger an immediate transmit demand. */ outl(0, ioaddr + CSR1); @@ -808,6 +792,10 @@ static void tulip_down (struct net_device *dev) del_timer_sync (&tp->timer); +#ifdef CONFIG_TULIP_NAPI + del_timer_sync (&tp->oom_timer); +#endif + spin_lock_irqsave (&tp->lock, flags); /* Disable interrupts by clearing the interrupt mask. */ @@ -850,13 +838,6 @@ static int tulip_close (struct net_device *dev) netif_stop_queue (dev); -#ifdef CONFIG_NET_HW_FLOWCONTROL - if (tp->fc_bit) { - int bit = tp->fc_bit; - tp->fc_bit = 0; - netdev_unregister_fc(bit); - } -#endif tulip_down (dev); if (tulip_debug > 1) @@ -917,6 +898,111 @@ static struct net_device_stats *tulip_get_stats(struct net_device *dev) return &tp->stats; } +static int tulip_get_ecmd(struct net_device *dev, struct ethtool_cmd *ecmd) +{ + u32 adv, lpa, bmcr; + + ecmd->supported = + (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | + SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII); + + + /* only supports twisted-pair or MII */ + if (tulip_media_cap[dev->if_port] & MediaIsMII) { + ecmd->port = PORT_MII; + } + else { + ecmd->port = PORT_TP; + } + + /* only supports internal transceiver */ + ecmd->transceiver = XCVR_INTERNAL; + + ecmd->advertising = ADVERTISED_TP | ADVERTISED_MII; + adv = tulip_mdio_read(dev, 1, MII_ADVERTISE); + if (adv & ADVERTISE_10HALF) + ecmd->advertising |= ADVERTISED_10baseT_Half; + if (adv & ADVERTISE_10FULL) + ecmd->advertising |= ADVERTISED_10baseT_Full; + if (adv & ADVERTISE_100HALF) + ecmd->advertising |= ADVERTISED_100baseT_Half; + if (adv & ADVERTISE_100FULL) + ecmd->advertising |= ADVERTISED_100baseT_Full; + + + bmcr = tulip_mdio_read(dev, 1, MII_BMCR); + lpa = tulip_mdio_read(dev, 1, MII_LPA); + + if (bmcr & BMCR_ANENABLE) { + ecmd->advertising |= ADVERTISED_Autoneg; + ecmd->autoneg = AUTONEG_ENABLE; + if (lpa & adv & LPA_100FULL) { + ecmd->speed = SPEED_100; + ecmd->duplex = DUPLEX_FULL; + } + else if (lpa & adv & LPA_100HALF) { + ecmd->speed = SPEED_100; + ecmd->duplex = DUPLEX_HALF; + } + else if (lpa & adv & LPA_10FULL) { + ecmd->speed = SPEED_10; + ecmd->duplex = DUPLEX_FULL; + } + else if (lpa & adv & LPA_10HALF) { + ecmd->speed = SPEED_10; + ecmd->duplex = DUPLEX_HALF; + } + } else { + ecmd->autoneg = AUTONEG_DISABLE; + if (bmcr & BMCR_SPEED100) { + ecmd->speed = SPEED_100; + } + else { + ecmd->speed = SPEED_10; + } + if (bmcr & BMCR_FULLDPLX) { + ecmd->duplex = DUPLEX_FULL; + } + else { + ecmd->duplex = DUPLEX_HALF; + } + } + return 0; +} + +static int tulip_set_ecmd(struct net_device *dev, struct ethtool_cmd *ecmd) +{ + struct tulip_private *np = dev->priv; + u32 tmp; + + if (ecmd->speed != SPEED_10 && ecmd->speed != SPEED_100) + return -EINVAL; + if (ecmd->duplex != DUPLEX_HALF && ecmd->duplex != DUPLEX_FULL) + return -EINVAL; + if (ecmd->port != PORT_TP && ecmd->port != PORT_MII) + return -EINVAL; + if (ecmd->transceiver != XCVR_INTERNAL) + return -EINVAL; + if (ecmd->autoneg != AUTONEG_DISABLE && ecmd->autoneg != AUTONEG_ENABLE) + return -EINVAL; + + tmp = tulip_mdio_read(dev, 1, MII_BMCR); + if (ecmd->autoneg == AUTONEG_ENABLE) { + tmp |= BMCR_ANENABLE; + } + else { + tmp &= ~(BMCR_ANENABLE | BMCR_SPEED100 | BMCR_FULLDPLX); + if (ecmd->speed == SPEED_100) + tmp |= BMCR_SPEED100; + if (ecmd->duplex == DUPLEX_FULL) + tmp |= BMCR_FULLDPLX; + else + np->full_duplex = 0; + } + tulip_mdio_write(dev, 1, MII_BMCR, tmp); + return 0; +} static int netdev_ethtool_ioctl(struct net_device *dev, void *useraddr) { @@ -936,7 +1022,34 @@ static int netdev_ethtool_ioctl(struct net_device *dev, void *useraddr) return -EFAULT; return 0; } - + case ETHTOOL_GLINK: { + struct ethtool_value edata = {ETHTOOL_GLINK}; + /* LSTATUS is latched low until a read - so read twice */ + tulip_mdio_read(dev, 1, MII_BMSR); + edata.data = (tulip_mdio_read(dev, 1, MII_BMSR) & BMSR_LSTATUS)? 1:0; + if (copy_to_user(useraddr, &edata, sizeof(edata))) + return -EFAULT; + return 0; + } + case ETHTOOL_GSET: { + struct ethtool_cmd ecmd = { ETHTOOL_GSET }; + spin_lock_irq(&np->lock); + tulip_get_ecmd(dev, &ecmd); + spin_unlock_irq(&np->lock); + if (copy_to_user(useraddr, &ecmd, sizeof(ecmd))) + return -EFAULT; + return 0; + } + case ETHTOOL_SSET: { + struct ethtool_cmd ecmd; + int r; + if (copy_from_user(&ecmd, useraddr, sizeof(ecmd))) + return -EFAULT; + spin_lock_irq(&np->lock); + r = tulip_set_ecmd(dev, &ecmd); + spin_unlock_irq(&np->lock); + return r; + } } return -EOPNOTSUPP; @@ -1729,6 +1842,10 @@ static int __devinit tulip_init_one (struct pci_dev *pdev, dev->get_stats = tulip_get_stats; dev->do_ioctl = private_ioctl; dev->set_multicast_list = set_rx_mode; +#ifdef CONFIG_TULIP_NAPI + dev->poll = tulip_poll; + dev->weight = 16; +#endif if (register_netdev(dev)) goto err_out_free_ring; @@ -1902,7 +2019,6 @@ static void __devexit tulip_remove_one (struct pci_dev *pdev) /* pci_power_off (pdev, -1); */ } - static struct pci_driver tulip_driver = { name: DRV_NAME, id_table: tulip_pci_tbl,