Exemple #1
0
/* The interrupt handler does all of the Rx thread work and cleans up
   after the Tx thread. */
static void intr_handler(int irq, void *dev_instance, struct pt_regs *rgs)
{
	struct net_device *dev = (struct net_device *)dev_instance;
	struct netdev_private *np = (struct netdev_private *)dev->priv;
	long ioaddr = dev->base_addr;
	int work_limit = max_interrupt_work;

	spin_lock(&np->lock);

	do {
		u32 intr_status = readl(ioaddr + IntrStatus);

		/* Acknowledge all of the current interrupt sources ASAP. */
		writel(intr_status & 0x001ffff, ioaddr + IntrStatus);

		if (debug > 4)
			printk(KERN_DEBUG "%s: Interrupt, status %4.4x.\n",
				   dev->name, intr_status);

		if ((intr_status & (NormalIntr|AbnormalIntr)) == 0)
			break;

		if (intr_status & (IntrRxDone | RxNoBuf))
			netdev_rx(dev);

		for (; np->cur_tx - np->dirty_tx > 0; np->dirty_tx++) {
			int entry = np->dirty_tx % TX_RING_SIZE;
			int tx_status = le32_to_cpu(np->tx_ring[entry].status);

			if (tx_status < 0)
				break;
			if (tx_status & 0x8000) { 		/* There was an error, log it. */
#ifndef final_version
				if (debug > 1)
					printk(KERN_DEBUG "%s: Transmit error, Tx status %8.8x.\n",
						   dev->name, tx_status);
#endif
				np->stats.tx_errors++;
				if (tx_status & 0x0104) np->stats.tx_aborted_errors++;
				if (tx_status & 0x0C80) np->stats.tx_carrier_errors++;
				if (tx_status & 0x0200) np->stats.tx_window_errors++;
				if (tx_status & 0x0002) np->stats.tx_fifo_errors++;
				if ((tx_status & 0x0080) && np->full_duplex == 0)
					np->stats.tx_heartbeat_errors++;
#ifdef ETHER_STATS
				if (tx_status & 0x0100) np->stats.collisions16++;
#endif
			} else {
#ifdef ETHER_STATS
				if (tx_status & 0x0001) np->stats.tx_deferred++;
#endif
				np->stats.tx_bytes += np->tx_skbuff[entry]->len;
				np->stats.collisions += (tx_status >> 3) & 15;
				np->stats.tx_packets++;
			}
			/* Free the original skb. */
			pci_unmap_single(np->pdev,np->tx_addr[entry],
						np->tx_skbuff[entry]->len,
						PCI_DMA_TODEVICE);
			np->tx_q_bytes -= np->tx_skbuff[entry]->len;
			dev_kfree_skb_irq(np->tx_skbuff[entry]);
			np->tx_skbuff[entry] = 0;
		}
		if (np->tx_full &&
			np->cur_tx - np->dirty_tx < TX_QUEUE_LEN - 4
			&&  np->tx_q_bytes < TX_BUG_FIFO_LIMIT) {
			/* The ring is no longer full, clear tbusy. */
			np->tx_full = 0;
			netif_wake_queue(dev);
		}

		/* Abnormal error summary/uncommon events handlers. */
		if (intr_status & (AbnormalIntr | TxFIFOUnderflow | IntrPCIErr |
						   TimerInt | IntrTxStopped))
			netdev_error(dev, intr_status);

		if (--work_limit < 0) {
			printk(KERN_WARNING "%s: Too much work at interrupt, "
				   "status=0x%4.4x.\n", dev->name, intr_status);
			/* Set the timer to re-enable the other interrupts after
			   10*82usec ticks. */
			writel(AbnormalIntr | TimerInt, ioaddr + IntrEnable);
			writel(10, ioaddr + GPTimer);
			break;
		}
	} while (1);

	if (debug > 3)
		printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n",
			   dev->name, (int)readl(ioaddr + IntrStatus));

	spin_unlock(&np->lock);
}
Exemple #2
0
/* The interrupt handler does all of the Rx thread work and cleans up
   after the Tx thread. */
static void intr_handler(int irq, void *dev_instance, struct pt_regs *rgs)
{
	struct net_device *dev = (struct net_device *)dev_instance;
	struct netdev_private *np;
	long ioaddr;
	int boguscnt = max_interrupt_work;

	ioaddr = dev->base_addr;
	np = dev->priv;
	spin_lock(&np->lock);

	do {
		int intr_status = readw(ioaddr + IntrStatus);
		writew(intr_status & (IntrRxDone | IntrRxDMADone | IntrPCIErr |
			IntrDrvRqst | IntrTxDone | IntrTxDMADone | StatsMax | 
			LinkChange), ioaddr + IntrStatus);

		if (debug > 4)
			printk(KERN_DEBUG "%s: Interrupt, status %4.4x.\n",
				   dev->name, intr_status);

		if (intr_status == 0)
			break;

		if (intr_status & (IntrRxDone|IntrRxDMADone))
			netdev_rx(dev);

		if (intr_status & IntrTxDone) {
			int boguscnt = 32;
			int tx_status = readw(ioaddr + TxStatus);
			while (tx_status & 0x80) {
				if (debug > 4)
					printk("%s: Transmit status is %2.2x.\n",
						   dev->name, tx_status);
				if (tx_status & 0x1e) {
					np->stats.tx_errors++;
					if (tx_status & 0x10)  np->stats.tx_fifo_errors++;
#ifdef ETHER_STATS
					if (tx_status & 0x08)  np->stats.collisions16++;
#else
					if (tx_status & 0x08)  np->stats.collisions++;
#endif
					if (tx_status & 0x04)  np->stats.tx_fifo_errors++;
					if (tx_status & 0x02)  np->stats.tx_window_errors++;
					/* This reset has not been verified!. */
					if (tx_status & 0x10) {			/* Reset the Tx. */
						writew(0x001c, ioaddr + ASICCtrl + 2);
#if 0					/* Do we need to reset the Tx pointer here? */
						writel(np->tx_ring_dma
							+ np->dirty_tx*sizeof(*np->tx_ring),
							dev->base_addr + TxListPtr);
#endif
					}
					if (tx_status & 0x1e) 		/* Restart the Tx. */
						writew(TxEnable, ioaddr + MACCtrl1);
				}
				/* Yup, this is a documentation bug.  It cost me *hours*. */
				writew(0, ioaddr + TxStatus);
				tx_status = readb(ioaddr + TxStatus);
				if (--boguscnt < 0)
					break;
			}
		}
		for (; np->cur_tx - np->dirty_tx > 0; np->dirty_tx++) {
			int entry = np->dirty_tx % TX_RING_SIZE;
			struct sk_buff *skb;

			if ( ! (np->tx_ring[entry].status & 0x00010000))
				break;
			skb = np->tx_skbuff[entry];
			/* Free the original skb. */
			pci_unmap_single(np->pci_dev, 
				np->tx_ring[entry].frag[0].addr, 
				skb->len, PCI_DMA_TODEVICE);
			dev_kfree_skb_irq(skb);
			np->tx_skbuff[entry] = 0;
		}
		if (np->tx_full
			&& np->cur_tx - np->dirty_tx < TX_QUEUE_LEN - 4) {
			/* The ring is no longer full, clear tbusy. */
			np->tx_full = 0;
			netif_wake_queue(dev);
		}

		/* Abnormal error summary/uncommon events handlers. */
		if (intr_status & (IntrDrvRqst | IntrPCIErr | LinkChange | StatsMax))
			netdev_error(dev, intr_status);
		if (--boguscnt < 0) {
			get_stats(dev);
			if (debug > 1) 
				printk(KERN_WARNING "%s: Too much work at interrupt, "
				   "status=0x%4.4x / 0x%4.4x.\n",
				   dev->name, intr_status, readw(ioaddr + IntrClear));
			/* Re-enable us in 3.2msec. */
			writew(0, ioaddr + IntrEnable);
			writew(1000, ioaddr + DownCounter);
			writew(IntrDrvRqst, ioaddr + IntrEnable);
			break;
		}
	} while (1);

	if (debug > 3)
		printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n",
			   dev->name, readw(ioaddr + IntrStatus));

	spin_unlock(&np->lock);
}
/* The interrupt handler does all of the Rx thread work and cleans up
   after the Tx thread. */
static void intr_handler(int irq, void *dev_instance, struct pt_regs *rgs)
{
	struct device *dev = (struct device *)dev_instance;
	struct netdev_private *np;
	long ioaddr, boguscnt = max_interrupt_work;

	ioaddr = dev->base_addr;
	np = (struct netdev_private *)dev->priv;
#if defined(__i386__)
	/* A lock to prevent simultaneous entry bug on Intel SMP machines. */
	if (test_and_set_bit(0, (void*)&dev->interrupt)) {
		printk(KERN_ERR"%s: SMP simultaneous entry of an interrupt handler.\n",
			   dev->name);
		dev->interrupt = 0;	/* Avoid halting machine. */
		return;
	}
#else
	if (dev->interrupt) {
		printk(KERN_ERR "%s: Re-entering the interrupt handler.\n", dev->name);
		return;
	}
	dev->interrupt = 1;
#endif

	do {
		u32 intr_status = readw(ioaddr + IntrStatus);

		/* Acknowledge all of the current interrupt sources ASAP. */
		writew(intr_status & 0xffff, ioaddr + IntrStatus);

		if (debug > 4)
			printk(KERN_DEBUG "%s: Interrupt, status %4.4x.\n",
				   dev->name, intr_status);

		if (intr_status == 0)
			break;

		if (intr_status & (IntrRxDone | IntrRxErr | IntrRxDropped |
						   IntrRxWakeUp | IntrRxEmpty | IntrRxNoBuf))
			netdev_rx(dev);

		for (; np->cur_tx - np->dirty_tx > 0; np->dirty_tx++) {
			int entry = np->dirty_tx % TX_RING_SIZE;
			int txstatus;
			if (np->tx_ring[entry].tx_own)
				break;
			txstatus = np->tx_ring[entry].tx_status;
			if (debug > 6)
				printk(KERN_DEBUG " Tx scavenge %d status %4.4x.\n",
					   entry, txstatus);
			if (txstatus & 0x8000) {
				if (debug > 1)
					printk(KERN_DEBUG "%s: Transmit error, Tx status %4.4x.\n",
						   dev->name, txstatus);
				np->stats.tx_errors++;
				if (txstatus & 0x0400) np->stats.tx_carrier_errors++;
				if (txstatus & 0x0200) np->stats.tx_window_errors++;
				if (txstatus & 0x0100) np->stats.tx_aborted_errors++;
				if (txstatus & 0x0080) np->stats.tx_heartbeat_errors++;
				if (txstatus & 0x0002) np->stats.tx_fifo_errors++;
#ifdef ETHER_STATS
				if (txstatus & 0x0100) np->stats.collisions16++;
#endif
				/* Transmitter restarted in 'abnormal' handler. */
			} else {
#ifdef ETHER_STATS
				if (txstatus & 0x0001) np->stats.tx_deferred++;
#endif
				np->stats.collisions += (txstatus >> 3) & 15;
#if defined(NETSTATS_VER2)
				np->stats.tx_bytes += np->tx_ring[entry].desc_length & 0x7ff;
#endif
				np->stats.tx_packets++;
			}
			/* Free the original skb. */
			dev_free_skb(np->tx_skbuff[entry]);
			np->tx_skbuff[entry] = 0;
		}
		if (np->tx_full && dev->tbusy
			&& np->cur_tx - np->dirty_tx < TX_RING_SIZE - 4) {
			/* The ring is no longer full, clear tbusy. */
			np->tx_full = 0;
			clear_bit(0, (void*)&dev->tbusy);
			mark_bh(NET_BH);
		}

		/* Abnormal error summary/uncommon events handlers. */
		if (intr_status & (IntrPCIErr | IntrLinkChange | IntrMIIChange |
						   IntrStatsMax | IntrTxAbort | IntrTxUnderrun))
			netdev_error(dev, intr_status);

		if (--boguscnt < 0) {
			printk(KERN_WARNING "%s: Too much work at interrupt, "
				   "status=0x%4.4x.\n",
				   dev->name, intr_status);
			break;
		}
	} while (1);

	if (debug > 3)
		printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n",
			   dev->name, readw(ioaddr + IntrStatus));

#if defined(__i386__)
	clear_bit(0, (void*)&dev->interrupt);
#else
	dev->interrupt = 0;
#endif
	return;
}