Skip to content
Snippets Groups Projects
8139too.c 69.3 KiB
Newer Older
			dev->stats.rx_bytes += pkt_size;
			dev->stats.rx_packets++;
Linus Torvalds's avatar
Linus Torvalds committed

			netif_receive_skb (skb);
		} else {
				netdev_warn(dev, "Memory squeeze, dropping packet\n");
			dev->stats.rx_dropped++;
Linus Torvalds's avatar
Linus Torvalds committed
		}
		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);

	netdev_dbg(dev, "Done %s(), current %04x BufAddr %04x, free to %04x, Cmd %02x\n",
		   __func__, cur_rx,
		   RTL_R16(RxBufAddr), RTL_R16(RxBufPtr), RTL_R8(ChipCmd));
Linus Torvalds's avatar
Linus Torvalds committed

	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)
{
	netdev_dbg(dev, "Abnormal interrupt, status %08x\n", status);
Linus Torvalds's avatar
Linus Torvalds committed

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

	/* Update the error count. */
	dev->stats.rx_missed_errors += RTL_R32 (RxMissed);
Linus Torvalds's avatar
Linus Torvalds committed
	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))
		dev->stats.rx_errors++;
Linus Torvalds's avatar
Linus Torvalds committed

	if (status & PCSTimeout)
		dev->stats.rx_length_errors++;
Linus Torvalds's avatar
Linus Torvalds committed
	if (status & RxUnderrun)
		dev->stats.rx_fifo_errors++;
Linus Torvalds's avatar
Linus Torvalds committed
	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);

		netdev_err(dev, "PCI Bus error %04x\n", pci_cmd_status);
static int rtl8139_poll(struct napi_struct *napi, int budget)
Linus Torvalds's avatar
Linus Torvalds committed
{
	struct rtl8139_private *tp = container_of(napi, struct rtl8139_private, napi);
	struct net_device *dev = tp->dev;
	void __iomem *ioaddr = tp->mmio_addr;
Linus Torvalds's avatar
Linus Torvalds committed

	spin_lock(&tp->rx_lock);
	work_done = 0;
	if (likely(RTL_R16(IntrStatus) & RxAckBits))
		work_done += rtl8139_rx(dev, tp, budget);
Linus Torvalds's avatar
Linus Torvalds committed

		unsigned long flags;
Linus Torvalds's avatar
Linus Torvalds committed
		/*
		 * Order is important since data can get interrupted
		 * again when we think we are done.
		 */
		spin_lock_irqsave(&tp->lock, flags);
		__napi_complete(napi);
		RTL_W16_F(IntrMask, rtl8139_intr_mask);
		spin_unlock_irqrestore(&tp->lock, flags);
Linus Torvalds's avatar
Linus Torvalds committed
	}
	spin_unlock(&tp->rx_lock);

Linus Torvalds's avatar
Linus Torvalds committed
}

/* 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)
Linus Torvalds's avatar
Linus Torvalds committed
{
	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))
Linus Torvalds's avatar
Linus Torvalds committed
		goto out;

	handled = 1;

	/* h/w no longer present (hotplug?) or major error, bail */
Linus Torvalds's avatar
Linus Torvalds committed
		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 (napi_schedule_prep(&tp->napi)) {
Linus Torvalds's avatar
Linus Torvalds committed
			RTL_W16_F (IntrMask, rtl8139_norx_intr_mask);
			__napi_schedule(&tp->napi);
Linus Torvalds's avatar
Linus Torvalds committed
		}
	}

	/* 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);

	netdev_dbg(dev, "exiting interrupt, intr_status=%#4.4x\n",
		   RTL_R16(IntrStatus));
Linus Torvalds's avatar
Linus Torvalds committed
	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);
Linus Torvalds's avatar
Linus Torvalds committed
	enable_irq(dev->irq);
}
#endif

static int rtl8139_set_mac_address(struct net_device *dev, void *p)
{
	struct rtl8139_private *tp = netdev_priv(dev);
	void __iomem *ioaddr = tp->mmio_addr;
	struct sockaddr *addr = p;

	if (!is_valid_ether_addr(addr->sa_data))
		return -EADDRNOTAVAIL;

	memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);

	spin_lock_irq(&tp->lock);

	RTL_W8_F(Cfg9346, Cfg9346_Unlock);
	RTL_W32_F(MAC0 + 0, cpu_to_le32 (*(u32 *) (dev->dev_addr + 0)));
	RTL_W32_F(MAC0 + 4, cpu_to_le32 (*(u32 *) (dev->dev_addr + 4)));
	RTL_W8_F(Cfg9346, Cfg9346_Lock);

	spin_unlock_irq(&tp->lock);

	return 0;
}

Linus Torvalds's avatar
Linus Torvalds committed
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
	unsigned long flags;

	netif_stop_queue(dev);
	napi_disable(&tp->napi);
Linus Torvalds's avatar
Linus Torvalds committed

	netif_dbg(tp, ifdown, dev, "Shutting down ethercard, status was 0x%04x\n",
		  RTL_R16(IntrStatus));
Linus Torvalds's avatar
Linus Torvalds committed

	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. */
	dev->stats.rx_missed_errors += RTL_R32 (RxMissed);
Linus Torvalds's avatar
Linus Torvalds committed
	RTL_W32 (RxMissed, 0);

	spin_unlock_irqrestore (&tp->lock, flags);

	free_irq (dev->irq, dev);

	rtl8139_tx_clear (tp);

	dma_free_coherent(&tp->pci_dev->dev, RX_BUF_TOT_LEN,
			  tp->rx_ring, tp->rx_ring_dma);
	dma_free_coherent(&tp->pci_dev->dev, TX_BUF_TOT_LEN,
			  tp->tx_bufs, tp->tx_bufs_dma);
Linus Torvalds's avatar
Linus Torvalds committed
	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 *tp = netdev_priv(dev);
	void __iomem *ioaddr = tp->mmio_addr;
Linus Torvalds's avatar
Linus Torvalds committed

	spin_lock_irq(&tp->lock);
	if (rtl_chip_info[tp->chipset].flags & HasLWake) {
Linus Torvalds's avatar
Linus Torvalds committed
		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(&tp->lock);
Linus Torvalds's avatar
Linus Torvalds committed
}


/* 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 *tp = netdev_priv(dev);
	void __iomem *ioaddr = tp->mmio_addr;
Linus Torvalds's avatar
Linus Torvalds committed
	u32 support;
	u8 cfg3, cfg5;

	support = ((rtl_chip_info[tp->chipset].flags & HasLWake)
Linus Torvalds's avatar
Linus Torvalds committed
		   ? (WAKE_PHY | WAKE_MAGIC
		      | WAKE_UCAST | WAKE_MCAST | WAKE_BCAST)
		   : 0);
	if (wol->wolopts & ~support)
		return -EINVAL;

	spin_lock_irq(&tp->lock);
Linus Torvalds's avatar
Linus Torvalds committed
	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(&tp->lock);
Linus Torvalds's avatar
Linus Torvalds committed

	return 0;
}

static void rtl8139_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
{
	struct rtl8139_private *tp = netdev_priv(dev);
Linus Torvalds's avatar
Linus Torvalds committed
	strcpy(info->driver, DRV_NAME);
	strcpy(info->version, DRV_VERSION);
	strcpy(info->bus_info, pci_name(tp->pci_dev));
	info->regdump_len = tp->regs_len;
Linus Torvalds's avatar
Linus Torvalds committed
}

static int rtl8139_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
	struct rtl8139_private *tp = netdev_priv(dev);
	spin_lock_irq(&tp->lock);
	mii_ethtool_gset(&tp->mii, cmd);
	spin_unlock_irq(&tp->lock);
Linus Torvalds's avatar
Linus Torvalds committed
	return 0;
}

static int rtl8139_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
	struct rtl8139_private *tp = netdev_priv(dev);
Linus Torvalds's avatar
Linus Torvalds committed
	int rc;
	spin_lock_irq(&tp->lock);
	rc = mii_ethtool_sset(&tp->mii, cmd);
	spin_unlock_irq(&tp->lock);
Linus Torvalds's avatar
Linus Torvalds committed
	return rc;
}

static int rtl8139_nway_reset(struct net_device *dev)
{
	struct rtl8139_private *tp = netdev_priv(dev);
	return mii_nway_restart(&tp->mii);
Linus Torvalds's avatar
Linus Torvalds committed
}

static u32 rtl8139_get_link(struct net_device *dev)
{
	struct rtl8139_private *tp = netdev_priv(dev);
	return mii_link_ok(&tp->mii);
Linus Torvalds's avatar
Linus Torvalds committed
}

static u32 rtl8139_get_msglevel(struct net_device *dev)
{
	struct rtl8139_private *tp = netdev_priv(dev);
	return tp->msg_enable;
Linus Torvalds's avatar
Linus Torvalds committed
}

static void rtl8139_set_msglevel(struct net_device *dev, u32 datum)
{
	struct rtl8139_private *tp = netdev_priv(dev);
	tp->msg_enable = datum;
Linus Torvalds's avatar
Linus Torvalds committed
}

static int rtl8139_get_regs_len(struct net_device *dev)
{
	struct rtl8139_private *tp;
	/* TODO: we are too slack to do reg dumping for pio, for now */
	if (use_io)
		return 0;
	tp = netdev_priv(dev);
	return tp->regs_len;
Linus Torvalds's avatar
Linus Torvalds committed
}

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

	/* TODO: we are too slack to do reg dumping for pio, for now */
	if (use_io)
		return;
	tp = netdev_priv(dev);
Linus Torvalds's avatar
Linus Torvalds committed

	regs->version = RTL_REGS_VER;

	spin_lock_irq(&tp->lock);
	memcpy_fromio(regbuf, tp->mmio_addr, regs->len);
	spin_unlock_irq(&tp->lock);
static int rtl8139_get_sset_count(struct net_device *dev, int sset)
Linus Torvalds's avatar
Linus Torvalds committed
{
	switch (sset) {
	case ETH_SS_STATS:
		return RTL_NUM_STATS;
	default:
		return -EOPNOTSUPP;
	}
Linus Torvalds's avatar
Linus Torvalds committed
}

static void rtl8139_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *stats, u64 *data)
{
	struct rtl8139_private *tp = netdev_priv(dev);
Linus Torvalds's avatar
Linus Torvalds committed

	data[0] = tp->xstats.early_rx;
	data[1] = tp->xstats.tx_buf_mapped;
	data[2] = tp->xstats.tx_timeouts;
	data[3] = tp->xstats.rx_lost_in_ring;
Linus Torvalds's avatar
Linus Torvalds committed
}

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

static const struct ethtool_ops rtl8139_ethtool_ops = {
Linus Torvalds's avatar
Linus Torvalds committed
	.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_sset_count		= rtl8139_get_sset_count,
Linus Torvalds's avatar
Linus Torvalds committed
	.get_ethtool_stats	= rtl8139_get_ethtool_stats,
};

static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
	struct rtl8139_private *tp = netdev_priv(dev);
Linus Torvalds's avatar
Linus Torvalds committed
	int rc;

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

	spin_lock_irq(&tp->lock);
	rc = generic_mii_ioctl(&tp->mii, if_mii(rq), cmd, NULL);
	spin_unlock_irq(&tp->lock);
Linus Torvalds's avatar
Linus Torvalds committed

	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);
		dev->stats.rx_missed_errors += RTL_R32 (RxMissed);
Linus Torvalds's avatar
Linus Torvalds committed
		RTL_W32 (RxMissed, 0);
		spin_unlock_irqrestore (&tp->lock, flags);
	}

	return &dev->stats;
Linus Torvalds's avatar
Linus Torvalds committed
}

/* 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 rx_mode;
Linus Torvalds's avatar
Linus Torvalds committed
	u32 tmp;

	netdev_dbg(dev, "rtl8139_set_rx_mode(%04x) done -- Rx config %08x\n",
		   dev->flags, RTL_R32(RxConfig));
Linus Torvalds's avatar
Linus Torvalds committed

	/* Note: do not reorder, GCC is clever about common statements. */
	if (dev->flags & IFF_PROMISC) {
		rx_mode =
		    AcceptBroadcast | AcceptMulticast | AcceptMyPhys |
		    AcceptAllPhys;
		mc_filter[1] = mc_filter[0] = 0xffffffff;
	} else if ((netdev_mc_count(dev) > multicast_filter_limit) ||
		   (dev->flags & IFF_ALLMULTI)) {
Linus Torvalds's avatar
Linus Torvalds committed
		/* Too many to filter perfectly -- accept all multicasts. */
		rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys;
		mc_filter[1] = mc_filter[0] = 0xffffffff;
	} else {
		struct netdev_hw_addr *ha;
Linus Torvalds's avatar
Linus Torvalds committed
		rx_mode = AcceptBroadcast | AcceptMyPhys;
		mc_filter[1] = mc_filter[0] = 0;
		netdev_for_each_mc_addr(ha, dev) {
			int bit_nr = ether_crc(ETH_ALEN, ha->addr) >> 26;
Linus Torvalds's avatar
Linus Torvalds committed

			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. */
	dev->stats.rx_missed_errors += RTL_R32 (RxMissed);
Linus Torvalds's avatar
Linus Torvalds committed
	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
	pr_info(RTL8139_DRIVER_NAME "\n");
Linus Torvalds's avatar
Linus Torvalds committed
#endif

	return pci_register_driver(&rtl8139_pci_driver);
Linus Torvalds's avatar
Linus Torvalds committed
}


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


module_init(rtl8139_init_module);
module_exit(rtl8139_cleanup_module);