diff options
Diffstat (limited to 'drivers/net/8139too.c')
-rw-r--r-- | drivers/net/8139too.c | 230 |
1 files changed, 127 insertions, 103 deletions
diff --git a/drivers/net/8139too.c b/drivers/net/8139too.c index b26b06302..b66edcc44 100644 --- a/drivers/net/8139too.c +++ b/drivers/net/8139too.c @@ -134,6 +134,7 @@ an MMIO register read. */ +#include <linux/config.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/pci.h> @@ -141,18 +142,21 @@ an MMIO register read. #include <linux/ioport.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> +#include <linux/rtnetlink.h> #include <linux/delay.h> #include <asm/io.h> -#define RTL8139_VERSION "0.9.11" -#define RTL8139_MODULE_NAME "8139too" -#define RTL8139_DRIVER_NAME RTL8139_MODULE_NAME " Fast Ethernet driver " RTL8139_VERSION -#define PFX RTL8139_MODULE_NAME ": " +#define RTL8139_VERSION "0.9.12" +#define MODNAME "8139too" +#define RTL8139_DRIVER_NAME MODNAME " Fast Ethernet driver " RTL8139_VERSION +#define PFX MODNAME ": " -/* define to 1 to enable PIO instead of MMIO */ -#undef USE_IO_OPS +/* enable PIO instead of MMIO, if CONFIG_8139TOO_PIO is selected */ +#ifdef CONFIG_8139TOO_PIO +#define USE_IO_OPS 1 +#endif /* define to 1 to enable copious debugging info */ #undef RTL8139_DEBUG @@ -502,12 +506,11 @@ struct rtl8139_private { int drv_flags; struct pci_dev *pci_dev; struct net_device_stats stats; - 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 tx_flag; - atomic_t cur_tx; - atomic_t dirty_tx; + unsigned int cur_tx; + unsigned int dirty_tx; /* The saved address of a sent-in-place packet/buffer, for skfree(). */ struct ring_info tx_info[NUM_TX_DESC]; unsigned char *tx_buf[NUM_TX_DESC]; /* Tx bounce buffers */ @@ -524,6 +527,9 @@ struct rtl8139_private { unsigned int mediasense:1; /* Media sensing in progress. */ spinlock_t lock; chip_t chipset; + pid_t thr_pid; + wait_queue_head_t thr_wait; + struct semaphore thr_exited; }; MODULE_AUTHOR ("Jeff Garzik <jgarzik@mandrakesoft.com>"); @@ -538,7 +544,7 @@ static int rtl8139_open (struct net_device *dev); static int mdio_read (struct net_device *dev, int phy_id, int location); static void mdio_write (struct net_device *dev, int phy_id, int location, int val); -static void rtl8139_timer (unsigned long data); +static int rtl8139_thread (void *data); static void rtl8139_tx_timeout (struct net_device *dev); static void rtl8139_init_ring (struct net_device *dev); static int rtl8139_start_xmit (struct sk_buff *skb, @@ -552,7 +558,6 @@ static inline u32 ether_crc (int length, unsigned char *data); static void rtl8139_set_rx_mode (struct net_device *dev); static void rtl8139_hw_start (struct net_device *dev); - #ifdef USE_IO_OPS #define RTL_R8(reg) inb (((unsigned long)ioaddr) + (reg)) @@ -648,6 +653,7 @@ static int __devinit rtl8139_init_board (struct pci_dev *pdev, DPRINTK ("EXIT, returning -ENOMEM\n"); return -ENOMEM; } + SET_MODULE_OWNER(dev); tp = dev->priv; pio_start = pci_resource_start (pdev, 0); @@ -867,7 +873,9 @@ static int __devinit rtl8139_init_one (struct pci_dev *pdev, tp->pci_dev = pdev; tp->board = ent->driver_data; tp->mmio_addr = ioaddr; - tp->lock = SPIN_LOCK_UNLOCKED; + spin_lock_init (&tp->lock); + init_waitqueue_head (&tp->thr_wait); + init_MUTEX_LOCKED (&tp->thr_exited); pdev->driver_data = dev; @@ -897,7 +905,7 @@ static int __devinit rtl8139_init_one (struct pci_dev *pdev, RTL_W8_F (HltClk, 'H'); /* 'R' would leave the clock running. */ /* The lower four bits are the media type. */ - option = (board_idx > 7) ? 0 : media[board_idx]; + option = (board_idx >= ARRAY_SIZE(media)) ? 0 : media[board_idx]; if (option > 0) { tp->full_duplex = (option & 0x200) ? 1 : 0; tp->default_port = option & 15; @@ -1068,7 +1076,7 @@ static void mdio_sync (void *mdio_addr) static int mdio_read (struct net_device *dev, int phy_id, int location) { - struct rtl8139_private *tp = (struct rtl8139_private *) dev->priv; + struct rtl8139_private *tp = dev->priv; void *mdio_addr = tp->mmio_addr + Config4; int mii_cmd = (0xf6 << 10) | (phy_id << 5) | location; int retval = 0; @@ -1111,7 +1119,7 @@ static int mdio_read (struct net_device *dev, int phy_id, int location) static void mdio_write (struct net_device *dev, int phy_id, int location, int value) { - struct rtl8139_private *tp = (struct rtl8139_private *) dev->priv; + struct rtl8139_private *tp = dev->priv; void *mdio_addr = tp->mmio_addr + Config4; int mii_cmd = (0x5002 << 16) | (phy_id << 23) | (location << 18) | value; @@ -1154,7 +1162,7 @@ static void mdio_write (struct net_device *dev, int phy_id, int location, static int rtl8139_open (struct net_device *dev) { - struct rtl8139_private *tp = (struct rtl8139_private *) dev->priv; + struct rtl8139_private *tp = dev->priv; int retval; #ifdef RTL8139_DEBUG void *ioaddr = tp->mmio_addr; @@ -1162,12 +1170,9 @@ static int rtl8139_open (struct net_device *dev) DPRINTK ("ENTER\n"); - MOD_INC_USE_COUNT; - retval = request_irq (dev->irq, rtl8139_interrupt, SA_SHIRQ, dev->name, dev); if (retval) { DPRINTK ("EXIT, returning %d\n", retval); - MOD_DEC_USE_COUNT; return retval; } @@ -1186,13 +1191,13 @@ static int rtl8139_open (struct net_device *dev) tp->rx_ring, tp->rx_ring_dma); DPRINTK ("EXIT, returning -ENOMEM\n"); - MOD_DEC_USE_COUNT; return -ENOMEM; } tp->full_duplex = tp->duplex_lock; tp->tx_flag = (TX_FIFO_THRESH << 11) & 0x003f0000; + tp->twistie = 1; rtl8139_init_ring (dev); rtl8139_hw_start (dev); @@ -1203,13 +1208,10 @@ static int rtl8139_open (struct net_device *dev) dev->irq, RTL_R8 (MediaStatus), tp->full_duplex ? "full" : "half"); - /* Set the timer to switch to check for link beat and perhaps switch - to an alternate media type. */ - init_timer (&tp->timer); - tp->timer.expires = jiffies + 3 * HZ; - tp->timer.data = (unsigned long) dev; - tp->timer.function = &rtl8139_timer; - add_timer (&tp->timer); + tp->thr_pid = kernel_thread (rtl8139_thread, dev, CLONE_FS | CLONE_FILES); + if (tp->thr_pid < 0) + printk (KERN_WARNING "%s: unable to start kernel thread\n", + dev->name); DPRINTK ("EXIT, returning 0\n"); return 0; @@ -1219,7 +1221,7 @@ static int rtl8139_open (struct net_device *dev) /* Start the hardware at open or resume. */ static void rtl8139_hw_start (struct net_device *dev) { - struct rtl8139_private *tp = (struct rtl8139_private *) dev->priv; + struct rtl8139_private *tp = dev->priv; void *ioaddr = tp->mmio_addr; u32 i; u8 tmp; @@ -1311,14 +1313,14 @@ static void rtl8139_hw_start (struct net_device *dev) /* Initialize the Rx and Tx rings, along with various 'dev' bits. */ static void rtl8139_init_ring (struct net_device *dev) { - struct rtl8139_private *tp = (struct rtl8139_private *) dev->priv; + struct rtl8139_private *tp = dev->priv; int i; DPRINTK ("ENTER\n"); tp->cur_rx = 0; - atomic_set (&tp->cur_tx, 0); - atomic_set (&tp->dirty_tx, 0); + tp->cur_tx = 0; + tp->dirty_tx = 0; for (i = 0; i < NUM_TX_DESC; i++) { tp->tx_info[i].skb = NULL; @@ -1330,7 +1332,10 @@ static void rtl8139_init_ring (struct net_device *dev) } -#ifndef RTL_TUNE_TWISTER +/* This must be global for CONFIG_8139TOO_TUNE_TWISTER case */ +static int next_tick = 3 * HZ; + +#ifndef CONFIG_8139TOO_TUNE_TWISTER static inline void rtl8139_tune_twister (struct net_device *dev, struct rtl8139_private *tp) {} #else @@ -1338,6 +1343,7 @@ static void rtl8139_tune_twister (struct net_device *dev, struct rtl8139_private *tp) { int linkcase; + void *ioaddr = tp->mmio_addr; DPRINTK ("ENTER\n"); @@ -1421,15 +1427,13 @@ static void rtl8139_tune_twister (struct net_device *dev, DPRINTK ("EXIT\n"); } -#endif /* RTL_TUNE_TWISTER */ +#endif /* CONFIG_8139TOO_TUNE_TWISTER */ -static void rtl8139_timer (unsigned long data) +static inline void rtl8139_thread_iter (struct net_device *dev, + struct rtl8139_private *tp, + void *ioaddr) { - struct net_device *dev = (struct net_device *) data; - struct rtl8139_private *tp = (struct rtl8139_private *) dev->priv; - void *ioaddr = tp->mmio_addr; - int next_tick = 60 * HZ; int mii_reg5; mii_reg5 = mdio_read (dev, tp->phys[0], 5); @@ -1450,6 +1454,8 @@ static void rtl8139_timer (unsigned long data) } } + next_tick = HZ * 60; + rtl8139_tune_twister (dev, tp); DPRINTK ("%s: Media selection tick, Link partner %4.4x.\n", @@ -1462,9 +1468,33 @@ static void rtl8139_timer (unsigned long data) DPRINTK ("%s: Chip config %2.2x %2.2x.\n", dev->name, RTL_R8 (Config0), RTL_R8 (Config1)); +} + + +static int rtl8139_thread (void *data) +{ + struct net_device *dev = data; + struct rtl8139_private *tp = dev->priv; + unsigned long timeout; - tp->timer.expires = jiffies + next_tick; - add_timer (&tp->timer); + daemonize (); + sprintf (current->comm, "k8139d-%s", dev->name); + + while (1) { + timeout = next_tick; + do { + timeout = interruptible_sleep_on_timeout (&tp->thr_wait, timeout); + } while (!signal_pending (current) && (timeout > 0)); + + if (signal_pending (current)) + break; + + rtnl_lock (); + rtl8139_thread_iter (dev, tp, tp->mmio_addr); + rtnl_unlock (); + } + + up_and_exit (&tp->thr_exited, 0); } @@ -1472,8 +1502,8 @@ static void rtl8139_tx_clear (struct rtl8139_private *tp) { int i; - atomic_set (&tp->cur_tx, 0); - atomic_set (&tp->dirty_tx, 0); + tp->cur_tx = 0; + tp->dirty_tx = 0; /* Dump the unsent Tx packets. */ for (i = 0; i < NUM_TX_DESC; i++) { @@ -1494,11 +1524,10 @@ static void rtl8139_tx_clear (struct rtl8139_private *tp) static void rtl8139_tx_timeout (struct net_device *dev) { - struct rtl8139_private *tp = (struct rtl8139_private *) dev->priv; + struct rtl8139_private *tp = dev->priv; void *ioaddr = tp->mmio_addr; int i; u8 tmp8; - unsigned long flags; DPRINTK ("%s: Transmit timeout, status %2.2x %4.4x " "media %2.2x.\n", dev->name, @@ -1516,20 +1545,17 @@ static void rtl8139_tx_timeout (struct net_device *dev) /* Emit info to figure out what went wrong. */ printk (KERN_DEBUG "%s: Tx queue start entry %d dirty entry %d.\n", - dev->name, atomic_read (&tp->cur_tx), - atomic_read (&tp->dirty_tx)); + 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.8lx.%s\n", dev->name, i, RTL_R32 (TxStatus0 + (i * 4)), - i == atomic_read (&tp->dirty_tx) % NUM_TX_DESC ? + i == tp->dirty_tx % NUM_TX_DESC ? " (queue head)" : ""); /* Stop a shared interrupt from scavenging while we are. */ - spin_lock_irqsave (&tp->lock, flags); - + spin_lock_irq (&tp->lock); rtl8139_tx_clear (tp); - - spin_unlock_irqrestore (&tp->lock, flags); + spin_unlock_irq (&tp->lock); /* ...and finally, reset everything */ rtl8139_hw_start (dev); @@ -1539,27 +1565,37 @@ static void rtl8139_tx_timeout (struct net_device *dev) static int rtl8139_start_xmit (struct sk_buff *skb, struct net_device *dev) { - struct rtl8139_private *tp = (struct rtl8139_private *) dev->priv; + struct rtl8139_private *tp = dev->priv; void *ioaddr = tp->mmio_addr; int entry; /* Calculate the next Tx descriptor entry. */ - entry = atomic_read (&tp->cur_tx) % NUM_TX_DESC; + entry = tp->cur_tx % NUM_TX_DESC; assert (tp->tx_info[entry].skb == NULL); assert (tp->tx_info[entry].mapping == 0); tp->tx_info[entry].skb = skb; - /* tp->tx_info[entry].mapping = 0; */ - memcpy (tp->tx_buf[entry], skb->data, skb->len); + 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)); + } else { + 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); + } /* Note: the chip doesn't have auto-pad! */ - RTL_W32 (TxStatus0 + (entry * sizeof(u32)), + RTL_W32 (TxStatus0 + (entry * sizeof (u32)), tp->tx_flag | (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN)); dev->trans_start = jiffies; - atomic_inc (&tp->cur_tx); - if ((atomic_read (&tp->cur_tx) - atomic_read (&tp->dirty_tx)) >= NUM_TX_DESC) + tp->cur_tx++; + mb(); + if ((tp->cur_tx - NUM_TX_DESC) == tp->dirty_tx) netif_stop_queue (dev); DPRINTK ("%s: Queued Tx packet at %p size %u to slot %d.\n", @@ -1573,16 +1609,14 @@ static void rtl8139_tx_interrupt (struct net_device *dev, struct rtl8139_private *tp, void *ioaddr) { - int cur_tx, dirty_tx, tx_left; + unsigned int dirty_tx, tx_left; assert (dev != NULL); assert (tp != NULL); assert (ioaddr != NULL); - dirty_tx = atomic_read (&tp->dirty_tx); - - cur_tx = atomic_read (&tp->cur_tx); - tx_left = cur_tx - dirty_tx; + dirty_tx = tp->dirty_tx; + tx_left = tp->cur_tx - dirty_tx; while (tx_left > 0) { int entry = dirty_tx % NUM_TX_DESC; int txstatus; @@ -1632,29 +1666,27 @@ static void rtl8139_tx_interrupt (struct net_device *dev, } dev_kfree_skb_irq (tp->tx_info[entry].skb); tp->tx_info[entry].skb = NULL; - dirty_tx++; - if (dirty_tx < 0) { /* handle signed int overflow */ - atomic_sub (cur_tx, &tp->cur_tx); /* XXX racy? */ - dirty_tx = cur_tx - tx_left + 1; - } - if (netif_queue_stopped (dev)) - netif_wake_queue (dev); - - cur_tx = atomic_read (&tp->cur_tx); - tx_left = cur_tx - dirty_tx; + dirty_tx++; + tx_left--; } #ifndef RTL8139_NDEBUG - if (atomic_read (&tp->cur_tx) - dirty_tx > NUM_TX_DESC) { + if (tp->cur_tx - dirty_tx > NUM_TX_DESC) { printk (KERN_ERR "%s: Out-of-sync dirty pointer, %d vs. %d.\n", - dev->name, dirty_tx, atomic_read (&tp->cur_tx)); + dev->name, dirty_tx, tp->cur_tx); dirty_tx += NUM_TX_DESC; } #endif /* RTL8139_NDEBUG */ - atomic_set (&tp->dirty_tx, dirty_tx); + /* 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); + } } @@ -1879,7 +1911,7 @@ static void rtl8139_interrupt (int irq, void *dev_instance, struct pt_regs *regs) { struct net_device *dev = (struct net_device *) dev_instance; - struct rtl8139_private *tp = (struct rtl8139_private *) dev->priv; + struct rtl8139_private *tp = dev->priv; int boguscnt = max_interrupt_work; void *ioaddr = tp->mmio_addr; int status = 0, link_changed = 0; /* avoid bogus "uninit" warning */ @@ -1962,20 +1994,27 @@ static void rtl8139_interrupt (int irq, void *dev_instance, static int rtl8139_close (struct net_device *dev) { - struct rtl8139_private *tp = (struct rtl8139_private *) dev->priv; + struct rtl8139_private *tp = dev->priv; void *ioaddr = tp->mmio_addr; - unsigned long flags; + int ret = 0; DPRINTK ("ENTER\n"); netif_stop_queue (dev); + if (tp->thr_pid >= 0) { + ret = kill_proc (tp->thr_pid, SIGTERM, 1); + if (ret) { + printk (KERN_ERR "%s: unable to signal thread\n", dev->name); + return ret; + } + down (&tp->thr_exited); + } + DPRINTK ("%s: Shutting down ethercard, status was 0x%4.4x.\n", dev->name, RTL_R16 (IntrStatus)); - del_timer_sync (&tp->timer); - - spin_lock_irqsave (&tp->lock, flags); + spin_lock_irq (&tp->lock); /* Stop the chip's Tx and Rx DMA processes. */ RTL_W8 (ChipCmd, (RTL_R8 (ChipCmd) & ChipCmdClear)); @@ -1987,7 +2026,7 @@ static int rtl8139_close (struct net_device *dev) tp->stats.rx_missed_errors += RTL_R32 (RxMissed); RTL_W32 (RxMissed, 0); - spin_unlock_irqrestore (&tp->lock, flags); + spin_unlock_irq (&tp->lock); synchronize_irq (); free_irq (dev->irq, dev); @@ -2006,8 +2045,6 @@ static int rtl8139_close (struct net_device *dev) RTL_W8 (Config1, 0x03); RTL_W8 (HltClk, 'H'); /* 'R' would leave the clock running. */ - MOD_DEC_USE_COUNT; - DPRINTK ("EXIT\n"); return 0; } @@ -2015,9 +2052,8 @@ static int rtl8139_close (struct net_device *dev) static int mii_ioctl (struct net_device *dev, struct ifreq *rq, int cmd) { - struct rtl8139_private *tp = (struct rtl8139_private *) dev->priv; + struct rtl8139_private *tp = dev->priv; u16 *data = (u16 *) & rq->ifr_data; - unsigned long flags; int rc = 0; DPRINTK ("ENTER\n"); @@ -2028,9 +2064,7 @@ static int mii_ioctl (struct net_device *dev, struct ifreq *rq, int cmd) /* Fall Through */ case SIOCDEVPRIVATE + 1: /* Read the specified MII register. */ - spin_lock_irqsave (&tp->lock, flags); data[3] = mdio_read (dev, data[0], data[1] & 0x1f); - spin_unlock_irqrestore (&tp->lock, flags); break; case SIOCDEVPRIVATE + 2: /* Write the specified MII register */ @@ -2039,9 +2073,7 @@ static int mii_ioctl (struct net_device *dev, struct ifreq *rq, int cmd) break; } - spin_lock_irqsave (&tp->lock, flags); mdio_write (dev, data[0], data[1] & 0x1f, data[2]); - spin_unlock_irqrestore (&tp->lock, flags); break; default: @@ -2056,22 +2088,14 @@ static int mii_ioctl (struct net_device *dev, struct ifreq *rq, int cmd) static struct net_device_stats *rtl8139_get_stats (struct net_device *dev) { - struct rtl8139_private *tp = (struct rtl8139_private *) dev->priv; + struct rtl8139_private *tp = dev->priv; void *ioaddr = tp->mmio_addr; DPRINTK ("ENTER\n"); - assert (tp != NULL); - if (netif_running(dev)) { - unsigned long flags; - - spin_lock_irqsave (&tp->lock, flags); - tp->stats.rx_missed_errors += RTL_R32 (RxMissed); RTL_W32 (RxMissed, 0); - - spin_unlock_irqrestore (&tp->lock, flags); } DPRINTK ("EXIT\n"); @@ -2104,7 +2128,7 @@ static inline u32 ether_crc (int length, unsigned char *data) static void rtl8139_set_rx_mode (struct net_device *dev) { - struct rtl8139_private *tp = (struct rtl8139_private *) dev->priv; + struct rtl8139_private *tp = dev->priv; void *ioaddr = tp->mmio_addr; u32 mc_filter[2]; /* Multicast hash filter */ int i, rx_mode; @@ -2160,7 +2184,7 @@ static void rtl8139_set_rx_mode (struct net_device *dev) static void rtl8139_suspend (struct pci_dev *pdev) { struct net_device *dev = pdev->driver_data; - struct rtl8139_private *tp = (struct rtl8139_private *) dev->priv; + struct rtl8139_private *tp = dev->priv; void *ioaddr = tp->mmio_addr; unsigned long flags; @@ -2190,7 +2214,7 @@ static void rtl8139_resume (struct pci_dev *pdev) static struct pci_driver rtl8139_pci_driver = { - name: RTL8139_MODULE_NAME, + name: MODNAME, id_table: rtl8139_pci_tbl, probe: rtl8139_init_one, remove: rtl8139_remove_one, |