Skip to content
Snippets Groups Projects
8139too.c 69.7 KiB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
		 */
		if (unlikely((rx_size > (MAX_ETH_FRAME_SIZE+4)) ||
			     (rx_size < 8) ||
			     (!(rx_status & RxStatusOK)))) {
			rtl8139_rx_err (rx_status, dev, tp, ioaddr);
			received = -1;
			goto out;
		}

		/* Malloc up new buffer, compatible with net-2e. */
		/* Omit the four octet CRC from the length. */

		skb = dev_alloc_skb (pkt_size + 2);
		if (likely(skb)) {
			skb->dev = dev;
			skb_reserve (skb, 2);	/* 16 byte align the IP fields. */
#if RX_BUF_IDX == 3
			wrap_copy(skb, rx_ring, ring_offset+4, pkt_size);
#else
			eth_copy_and_sum (skb, &rx_ring[ring_offset + 4], pkt_size, 0);
#endif
			skb_put (skb, pkt_size);

			skb->protocol = eth_type_trans (skb, dev);

			dev->last_rx = jiffies;
			tp->stats.rx_bytes += pkt_size;
			tp->stats.rx_packets++;

			netif_receive_skb (skb);
		} else {
			if (net_ratelimit()) 
				printk (KERN_WARNING
					"%s: Memory squeeze, dropping packet.\n",
					dev->name);
			tp->stats.rx_dropped++;
		}
		received++;

		cur_rx = (cur_rx + rx_size + 4 + 3) & ~3;
		RTL_W16 (RxBufPtr, (u16) (cur_rx - 16));

		rtl8139_isr_ack(tp);
	}

	if (unlikely(!received || rx_size == 0xfff0))
		rtl8139_isr_ack(tp);

#if RTL8139_DEBUG > 1
	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));
#endif

	tp->cur_rx = cur_rx;

	/*
	 * The receive buffer should be mostly empty.
	 * Tell NAPI to reenable the Rx irq.
	 */
	if (tp->fifo_copy_timeout)
		received = budget;

out:
	return received;
}


static void rtl8139_weird_interrupt (struct net_device *dev,
				     struct rtl8139_private *tp,
				     void __iomem *ioaddr,
Linus Torvalds's avatar
Linus Torvalds committed
				     int status, int link_changed)
{
	DPRINTK ("%s: Abnormal interrupt, status %8.8x.\n",
		 dev->name, status);

	assert (dev != NULL);
	assert (tp != NULL);
	assert (ioaddr != NULL);

	/* Update the error count. */
	tp->stats.rx_missed_errors += RTL_R32 (RxMissed);
	RTL_W32 (RxMissed, 0);

	if ((status & RxUnderrun) && link_changed &&
	    (tp->drv_flags & HAS_LNK_CHNG)) {
		rtl_check_media(dev, 0);
		status &= ~RxUnderrun;
	}

	if (status & (RxUnderrun | RxErr))
		tp->stats.rx_errors++;

	if (status & PCSTimeout)
		tp->stats.rx_length_errors++;
	if (status & RxUnderrun)
		tp->stats.rx_fifo_errors++;
	if (status & PCIErr) {
		u16 pci_cmd_status;
		pci_read_config_word (tp->pci_dev, PCI_STATUS, &pci_cmd_status);
		pci_write_config_word (tp->pci_dev, PCI_STATUS, pci_cmd_status);

		printk (KERN_ERR "%s: PCI Bus error %4.4x.\n",
			dev->name, pci_cmd_status);
	}
}

static int rtl8139_poll(struct net_device *dev, int *budget)
{
	struct rtl8139_private *tp = netdev_priv(dev);
	void __iomem *ioaddr = tp->mmio_addr;
Linus Torvalds's avatar
Linus Torvalds committed
	int orig_budget = min(*budget, dev->quota);
	int done = 1;

	spin_lock(&tp->rx_lock);
	if (likely(RTL_R16(IntrStatus) & RxAckBits)) {
		int work_done;

		work_done = rtl8139_rx(dev, tp, orig_budget);
		if (likely(work_done > 0)) {
			*budget -= work_done;
			dev->quota -= work_done;
			done = (work_done < orig_budget);
		}
	}

	if (done) {
		/*
		 * Order is important since data can get interrupted
		 * again when we think we are done.
		 */
		local_irq_disable();
		RTL_W16_F(IntrMask, rtl8139_intr_mask);
		__netif_rx_complete(dev);
		local_irq_enable();
	}
	spin_unlock(&tp->rx_lock);

	return !done;
}

/* The interrupt handler does all of the Rx thread work and cleans up
   after the Tx thread. */
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 = netdev_priv(dev);
	void __iomem *ioaddr = tp->mmio_addr;
Linus Torvalds's avatar
Linus Torvalds committed
	u16 status, ackstat;
	int link_changed = 0; /* avoid bogus "uninit" warning */
	int handled = 0;

	spin_lock (&tp->lock);
	status = RTL_R16 (IntrStatus);

	/* shared irq? */
	if (unlikely((status & rtl8139_intr_mask) == 0)) 
		goto out;

	handled = 1;

	/* h/w no longer present (hotplug?) or major error, bail */
	if (unlikely(status == 0xFFFF)) 
		goto out;

	/* close possible race's with dev_close */
	if (unlikely(!netif_running(dev))) {
		RTL_W16 (IntrMask, 0);
		goto out;
	}

	/* Acknowledge all of the current interrupt sources ASAP, but
	   an first get an additional status bit from CSCR. */
	if (unlikely(status & RxUnderrun))
		link_changed = RTL_R16 (CSCR) & CSCR_LinkChangeBit;

	ackstat = status & ~(RxAckBits | TxErr);
	if (ackstat)
		RTL_W16 (IntrStatus, ackstat);

	/* Receive packets are processed by poll routine.
	   If not running start it now. */
	if (status & RxAckBits){
		if (netif_rx_schedule_prep(dev)) {
			RTL_W16_F (IntrMask, rtl8139_norx_intr_mask);
			__netif_rx_schedule (dev);
		}
	}

	/* Check uncommon events with one test. */
	if (unlikely(status & (PCIErr | PCSTimeout | RxUnderrun | RxErr)))
		rtl8139_weird_interrupt (dev, tp, ioaddr,
					 status, link_changed);

	if (status & (TxOK | TxErr)) {
		rtl8139_tx_interrupt (dev, tp, ioaddr);
		if (status & TxErr)
			RTL_W16 (IntrStatus, TxErr);
	}
 out:
	spin_unlock (&tp->lock);

	DPRINTK ("%s: exiting interrupt, intr_status=%#4.4x.\n",
		 dev->name, RTL_R16 (IntrStatus));
	return IRQ_RETVAL(handled);
}

#ifdef CONFIG_NET_POLL_CONTROLLER
/*
 * Polling receive - used by netconsole and other diagnostic tools
 * to allow network i/o with interrupts disabled.
 */
static void rtl8139_poll_controller(struct net_device *dev)
{
	disable_irq(dev->irq);
	rtl8139_interrupt(dev->irq, dev, NULL);
	enable_irq(dev->irq);
}
#endif

static int rtl8139_close (struct net_device *dev)
{
	struct rtl8139_private *tp = netdev_priv(dev);
	void __iomem *ioaddr = tp->mmio_addr;
Linus Torvalds's avatar
Linus Torvalds committed
	int ret = 0;
	unsigned long flags;

	netif_stop_queue (dev);

	if (tp->thr_pid >= 0) {
		tp->time_to_die = 1;
		wmb();
		ret = kill_proc (tp->thr_pid, SIGTERM, 1);
		if (ret) {
			printk (KERN_ERR "%s: unable to signal thread\n", dev->name);
			return ret;
		}
		wait_for_completion (&tp->thr_exited);
	}
	
	if (netif_msg_ifdown(tp))
		printk(KERN_DEBUG "%s: Shutting down ethercard, status was 0x%4.4x.\n",
			dev->name, RTL_R16 (IntrStatus));

	spin_lock_irqsave (&tp->lock, flags);

	/* Stop the chip's Tx and Rx DMA processes. */
	RTL_W8 (ChipCmd, 0);

	/* Disable interrupts by clearing the interrupt mask. */
	RTL_W16 (IntrMask, 0);

	/* Update the error counts. */
	tp->stats.rx_missed_errors += RTL_R32 (RxMissed);
	RTL_W32 (RxMissed, 0);

	spin_unlock_irqrestore (&tp->lock, flags);

	synchronize_irq (dev->irq);	/* racy, but that's ok here */
	free_irq (dev->irq, dev);

	rtl8139_tx_clear (tp);

	pci_free_consistent(tp->pci_dev, RX_BUF_TOT_LEN,
			    tp->rx_ring, tp->rx_ring_dma);
	pci_free_consistent(tp->pci_dev, TX_BUF_TOT_LEN,
			    tp->tx_bufs, tp->tx_bufs_dma);
	tp->rx_ring = NULL;
	tp->tx_bufs = NULL;

	/* Green! Put the chip in low-power mode. */
	RTL_W8 (Cfg9346, Cfg9346_Unlock);

	if (rtl_chip_info[tp->chipset].flags & HasHltClk)
		RTL_W8 (HltClk, 'H');	/* 'R' would leave the clock running. */

	return 0;
}


/* Get the ethtool Wake-on-LAN settings.  Assumes that wol points to
   kernel memory, *wol has been initialized as {ETHTOOL_GWOL}, and
   other threads or interrupts aren't messing with the 8139.  */
static void rtl8139_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
{
	struct rtl8139_private *np = netdev_priv(dev);
	void __iomem *ioaddr = np->mmio_addr;
Linus Torvalds's avatar
Linus Torvalds committed

	spin_lock_irq(&np->lock);
	if (rtl_chip_info[np->chipset].flags & HasLWake) {
		u8 cfg3 = RTL_R8 (Config3);
		u8 cfg5 = RTL_R8 (Config5);

		wol->supported = WAKE_PHY | WAKE_MAGIC
			| WAKE_UCAST | WAKE_MCAST | WAKE_BCAST;

		wol->wolopts = 0;
		if (cfg3 & Cfg3_LinkUp)
			wol->wolopts |= WAKE_PHY;
		if (cfg3 & Cfg3_Magic)
			wol->wolopts |= WAKE_MAGIC;
		/* (KON)FIXME: See how netdev_set_wol() handles the
		   following constants.  */
		if (cfg5 & Cfg5_UWF)
			wol->wolopts |= WAKE_UCAST;
		if (cfg5 & Cfg5_MWF)
			wol->wolopts |= WAKE_MCAST;
		if (cfg5 & Cfg5_BWF)
			wol->wolopts |= WAKE_BCAST;
	}
	spin_unlock_irq(&np->lock);
}


/* Set the ethtool Wake-on-LAN settings.  Return 0 or -errno.  Assumes
   that wol points to kernel memory and other threads or interrupts
   aren't messing with the 8139.  */
static int rtl8139_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
{
	struct rtl8139_private *np = netdev_priv(dev);
	void __iomem *ioaddr = np->mmio_addr;
Linus Torvalds's avatar
Linus Torvalds committed
	u32 support;
	u8 cfg3, cfg5;

	support = ((rtl_chip_info[np->chipset].flags & HasLWake)
		   ? (WAKE_PHY | WAKE_MAGIC
		      | WAKE_UCAST | WAKE_MCAST | WAKE_BCAST)
		   : 0);
	if (wol->wolopts & ~support)
		return -EINVAL;

	spin_lock_irq(&np->lock);
	cfg3 = RTL_R8 (Config3) & ~(Cfg3_LinkUp | Cfg3_Magic);
	if (wol->wolopts & WAKE_PHY)
		cfg3 |= Cfg3_LinkUp;
	if (wol->wolopts & WAKE_MAGIC)
		cfg3 |= Cfg3_Magic;
	RTL_W8 (Cfg9346, Cfg9346_Unlock);
	RTL_W8 (Config3, cfg3);
	RTL_W8 (Cfg9346, Cfg9346_Lock);

	cfg5 = RTL_R8 (Config5) & ~(Cfg5_UWF | Cfg5_MWF | Cfg5_BWF);
	/* (KON)FIXME: These are untested.  We may have to set the
	   CRC0, Wakeup0 and LSBCRC0 registers too, but I have no
	   documentation.  */
	if (wol->wolopts & WAKE_UCAST)
		cfg5 |= Cfg5_UWF;
	if (wol->wolopts & WAKE_MCAST)
		cfg5 |= Cfg5_MWF;
	if (wol->wolopts & WAKE_BCAST)
		cfg5 |= Cfg5_BWF;
	RTL_W8 (Config5, cfg5);	/* need not unlock via Cfg9346 */
	spin_unlock_irq(&np->lock);

	return 0;
}

static void rtl8139_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
{
	struct rtl8139_private *np = netdev_priv(dev);
	strcpy(info->driver, DRV_NAME);
	strcpy(info->version, DRV_VERSION);
	strcpy(info->bus_info, pci_name(np->pci_dev));
	info->regdump_len = np->regs_len;
}

static int rtl8139_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
	struct rtl8139_private *np = netdev_priv(dev);
	spin_lock_irq(&np->lock);
	mii_ethtool_gset(&np->mii, cmd);
	spin_unlock_irq(&np->lock);
	return 0;
}

static int rtl8139_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
	struct rtl8139_private *np = netdev_priv(dev);
	int rc;
	spin_lock_irq(&np->lock);
	rc = mii_ethtool_sset(&np->mii, cmd);
	spin_unlock_irq(&np->lock);
	return rc;
}

static int rtl8139_nway_reset(struct net_device *dev)
{
	struct rtl8139_private *np = netdev_priv(dev);
	return mii_nway_restart(&np->mii);
}

static u32 rtl8139_get_link(struct net_device *dev)
{
	struct rtl8139_private *np = netdev_priv(dev);
	return mii_link_ok(&np->mii);
}

static u32 rtl8139_get_msglevel(struct net_device *dev)
{
	struct rtl8139_private *np = netdev_priv(dev);
	return np->msg_enable;
}

static void rtl8139_set_msglevel(struct net_device *dev, u32 datum)
{
	struct rtl8139_private *np = netdev_priv(dev);
	np->msg_enable = datum;
}

/* TODO: we are too slack to do reg dumping for pio, for now */
#ifdef CONFIG_8139TOO_PIO
#define rtl8139_get_regs_len	NULL
#define rtl8139_get_regs	NULL
#else
static int rtl8139_get_regs_len(struct net_device *dev)
{
	struct rtl8139_private *np = netdev_priv(dev);
	return np->regs_len;
}

static void rtl8139_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *regbuf)
{
	struct rtl8139_private *np = netdev_priv(dev);

	regs->version = RTL_REGS_VER;

	spin_lock_irq(&np->lock);
	memcpy_fromio(regbuf, np->mmio_addr, regs->len);
	spin_unlock_irq(&np->lock);
}
#endif /* CONFIG_8139TOO_MMIO */

static int rtl8139_get_stats_count(struct net_device *dev)
{
	return RTL_NUM_STATS;
}

static void rtl8139_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *stats, u64 *data)
{
	struct rtl8139_private *np = netdev_priv(dev);

	data[0] = np->xstats.early_rx;
	data[1] = np->xstats.tx_buf_mapped;
	data[2] = np->xstats.tx_timeouts;
	data[3] = np->xstats.rx_lost_in_ring;
}

static void rtl8139_get_strings(struct net_device *dev, u32 stringset, u8 *data)
{
	memcpy(data, ethtool_stats_keys, sizeof(ethtool_stats_keys));
}

static struct ethtool_ops rtl8139_ethtool_ops = {
	.get_drvinfo		= rtl8139_get_drvinfo,
	.get_settings		= rtl8139_get_settings,
	.set_settings		= rtl8139_set_settings,
	.get_regs_len		= rtl8139_get_regs_len,
	.get_regs		= rtl8139_get_regs,
	.nway_reset		= rtl8139_nway_reset,
	.get_link		= rtl8139_get_link,
	.get_msglevel		= rtl8139_get_msglevel,
	.set_msglevel		= rtl8139_set_msglevel,
	.get_wol		= rtl8139_get_wol,
	.set_wol		= rtl8139_set_wol,
	.get_strings		= rtl8139_get_strings,
	.get_stats_count	= rtl8139_get_stats_count,
	.get_ethtool_stats	= rtl8139_get_ethtool_stats,
	.get_perm_addr		= ethtool_op_get_perm_addr,
Linus Torvalds's avatar
Linus Torvalds committed
};

static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
	struct rtl8139_private *np = netdev_priv(dev);
	int rc;

	if (!netif_running(dev))
		return -EINVAL;

	spin_lock_irq(&np->lock);
	rc = generic_mii_ioctl(&np->mii, if_mii(rq), cmd, NULL);
	spin_unlock_irq(&np->lock);

	return rc;
}


static struct net_device_stats *rtl8139_get_stats (struct net_device *dev)
{
	struct rtl8139_private *tp = netdev_priv(dev);
	void __iomem *ioaddr = tp->mmio_addr;
Linus Torvalds's avatar
Linus Torvalds committed
	unsigned long flags;

	if (netif_running(dev)) {
		spin_lock_irqsave (&tp->lock, flags);
		tp->stats.rx_missed_errors += RTL_R32 (RxMissed);
		RTL_W32 (RxMissed, 0);
		spin_unlock_irqrestore (&tp->lock, flags);
	}

	return &tp->stats;
}

/* Set or clear the multicast filter for this adaptor.
   This routine is not state sensitive and need not be SMP locked. */

static void __set_rx_mode (struct net_device *dev)
{
	struct rtl8139_private *tp = netdev_priv(dev);
	void __iomem *ioaddr = tp->mmio_addr;
Linus Torvalds's avatar
Linus Torvalds committed
	u32 mc_filter[2];	/* Multicast hash filter */
	int i, rx_mode;
	u32 tmp;

	DPRINTK ("%s:   rtl8139_set_rx_mode(%4.4x) done -- Rx config %8.8lx.\n",
			dev->name, dev->flags, RTL_R32 (RxConfig));

	/* Note: do not reorder, GCC is clever about common statements. */
	if (dev->flags & IFF_PROMISC) {
		/* Unconditionally log net taps. */
		printk (KERN_NOTICE "%s: Promiscuous mode enabled.\n",
			dev->name);
		rx_mode =
		    AcceptBroadcast | AcceptMulticast | AcceptMyPhys |
		    AcceptAllPhys;
		mc_filter[1] = mc_filter[0] = 0xffffffff;
	} else if ((dev->mc_count > multicast_filter_limit)
		   || (dev->flags & IFF_ALLMULTI)) {
		/* Too many to filter perfectly -- accept all multicasts. */
		rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys;
		mc_filter[1] = mc_filter[0] = 0xffffffff;
	} else {
		struct dev_mc_list *mclist;
		rx_mode = AcceptBroadcast | AcceptMyPhys;
		mc_filter[1] = mc_filter[0] = 0;
		for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
		     i++, mclist = mclist->next) {
			int bit_nr = ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26;

			mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31);
			rx_mode |= AcceptMulticast;
		}
	}

	/* We can safely update without stopping the chip. */
	tmp = rtl8139_rx_config | rx_mode;
	if (tp->rx_config != tmp) {
		RTL_W32_F (RxConfig, tmp);
		tp->rx_config = tmp;
	}
	RTL_W32_F (MAR0 + 0, mc_filter[0]);
	RTL_W32_F (MAR0 + 4, mc_filter[1]);
}

static void rtl8139_set_rx_mode (struct net_device *dev)
{
	unsigned long flags;
	struct rtl8139_private *tp = netdev_priv(dev);

	spin_lock_irqsave (&tp->lock, flags);
	__set_rx_mode(dev);
	spin_unlock_irqrestore (&tp->lock, flags);
}

#ifdef CONFIG_PM

static int rtl8139_suspend (struct pci_dev *pdev, pm_message_t state)
{
	struct net_device *dev = pci_get_drvdata (pdev);
	struct rtl8139_private *tp = netdev_priv(dev);
	void __iomem *ioaddr = tp->mmio_addr;
Linus Torvalds's avatar
Linus Torvalds committed
	unsigned long flags;

	pci_save_state (pdev);

	if (!netif_running (dev))
		return 0;

	netif_device_detach (dev);

	spin_lock_irqsave (&tp->lock, flags);

	/* Disable interrupts, stop Tx and Rx. */
	RTL_W16 (IntrMask, 0);
	RTL_W8 (ChipCmd, 0);

	/* Update the error counts. */
	tp->stats.rx_missed_errors += RTL_R32 (RxMissed);
	RTL_W32 (RxMissed, 0);

	spin_unlock_irqrestore (&tp->lock, flags);

	pci_set_power_state (pdev, PCI_D3hot);

	return 0;
}


static int rtl8139_resume (struct pci_dev *pdev)
{
	struct net_device *dev = pci_get_drvdata (pdev);

	pci_restore_state (pdev);
	if (!netif_running (dev))
		return 0;
	pci_set_power_state (pdev, PCI_D0);
	rtl8139_init_ring (dev);
	rtl8139_hw_start (dev);
	netif_device_attach (dev);
	return 0;
}

#endif /* CONFIG_PM */


static struct pci_driver rtl8139_pci_driver = {
	.name		= DRV_NAME,
	.id_table	= rtl8139_pci_tbl,
	.probe		= rtl8139_init_one,
	.remove		= __devexit_p(rtl8139_remove_one),
#ifdef CONFIG_PM
	.suspend	= rtl8139_suspend,
	.resume		= rtl8139_resume,
#endif /* CONFIG_PM */
};


static int __init rtl8139_init_module (void)
{
	/* when we're a module, we always print a version message,
	 * even if no 8139 board is found.
	 */
#ifdef MODULE
	printk (KERN_INFO RTL8139_DRIVER_NAME "\n");
#endif

	return pci_module_init (&rtl8139_pci_driver);
}


static void __exit rtl8139_cleanup_module (void)
{
	pci_unregister_driver (&rtl8139_pci_driver);
}


module_init(rtl8139_init_module);
module_exit(rtl8139_cleanup_module);