162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci	drivers/net/ethernet/dec/tulip/interrupt.c
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci	Copyright 2000,2001  The Linux Kernel Team
562306a36Sopenharmony_ci	Written/copyright 1994-2001 by Donald Becker.
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci	This software may be used and distributed according to the terms
862306a36Sopenharmony_ci	of the GNU General Public License, incorporated herein by reference.
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci        Please submit bugs to http://bugzilla.kernel.org/ .
1162306a36Sopenharmony_ci*/
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <linux/pci.h>
1462306a36Sopenharmony_ci#include "tulip.h"
1562306a36Sopenharmony_ci#include <linux/etherdevice.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ciint tulip_rx_copybreak;
1862306a36Sopenharmony_ciunsigned int tulip_max_interrupt_work;
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#ifdef CONFIG_TULIP_NAPI_HW_MITIGATION
2162306a36Sopenharmony_ci#define MIT_SIZE 15
2262306a36Sopenharmony_ci#define MIT_TABLE 15 /* We use 0 or max */
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistatic unsigned int mit_table[MIT_SIZE+1] =
2562306a36Sopenharmony_ci{
2662306a36Sopenharmony_ci        /*  CRS11 21143 hardware Mitigation Control Interrupt
2762306a36Sopenharmony_ci            We use only RX mitigation we other techniques for
2862306a36Sopenharmony_ci            TX intr. mitigation.
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci           31    Cycle Size (timer control)
3162306a36Sopenharmony_ci           30:27 TX timer in 16 * Cycle size
3262306a36Sopenharmony_ci           26:24 TX No pkts before Int.
3362306a36Sopenharmony_ci           23:20 RX timer in Cycle size
3462306a36Sopenharmony_ci           19:17 RX No pkts before Int.
3562306a36Sopenharmony_ci           16       Continues Mode (CM)
3662306a36Sopenharmony_ci        */
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci        0x0,             /* IM disabled */
3962306a36Sopenharmony_ci        0x80150000,      /* RX time = 1, RX pkts = 2, CM = 1 */
4062306a36Sopenharmony_ci        0x80150000,
4162306a36Sopenharmony_ci        0x80270000,
4262306a36Sopenharmony_ci        0x80370000,
4362306a36Sopenharmony_ci        0x80490000,
4462306a36Sopenharmony_ci        0x80590000,
4562306a36Sopenharmony_ci        0x80690000,
4662306a36Sopenharmony_ci        0x807B0000,
4762306a36Sopenharmony_ci        0x808B0000,
4862306a36Sopenharmony_ci        0x809D0000,
4962306a36Sopenharmony_ci        0x80AD0000,
5062306a36Sopenharmony_ci        0x80BD0000,
5162306a36Sopenharmony_ci        0x80CF0000,
5262306a36Sopenharmony_ci        0x80DF0000,
5362306a36Sopenharmony_ci//       0x80FF0000      /* RX time = 16, RX pkts = 7, CM = 1 */
5462306a36Sopenharmony_ci        0x80F10000      /* RX time = 16, RX pkts = 0, CM = 1 */
5562306a36Sopenharmony_ci};
5662306a36Sopenharmony_ci#endif
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ciint tulip_refill_rx(struct net_device *dev)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci	struct tulip_private *tp = netdev_priv(dev);
6262306a36Sopenharmony_ci	int entry;
6362306a36Sopenharmony_ci	int refilled = 0;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	/* Refill the Rx ring buffers. */
6662306a36Sopenharmony_ci	for (; tp->cur_rx - tp->dirty_rx > 0; tp->dirty_rx++) {
6762306a36Sopenharmony_ci		entry = tp->dirty_rx % RX_RING_SIZE;
6862306a36Sopenharmony_ci		if (tp->rx_buffers[entry].skb == NULL) {
6962306a36Sopenharmony_ci			struct sk_buff *skb;
7062306a36Sopenharmony_ci			dma_addr_t mapping;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci			skb = tp->rx_buffers[entry].skb =
7362306a36Sopenharmony_ci				netdev_alloc_skb(dev, PKT_BUF_SZ);
7462306a36Sopenharmony_ci			if (skb == NULL)
7562306a36Sopenharmony_ci				break;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci			mapping = dma_map_single(&tp->pdev->dev, skb->data,
7862306a36Sopenharmony_ci						 PKT_BUF_SZ, DMA_FROM_DEVICE);
7962306a36Sopenharmony_ci			if (dma_mapping_error(&tp->pdev->dev, mapping)) {
8062306a36Sopenharmony_ci				dev_kfree_skb(skb);
8162306a36Sopenharmony_ci				tp->rx_buffers[entry].skb = NULL;
8262306a36Sopenharmony_ci				break;
8362306a36Sopenharmony_ci			}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci			tp->rx_buffers[entry].mapping = mapping;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci			tp->rx_ring[entry].buffer1 = cpu_to_le32(mapping);
8862306a36Sopenharmony_ci			refilled++;
8962306a36Sopenharmony_ci		}
9062306a36Sopenharmony_ci		tp->rx_ring[entry].status = cpu_to_le32(DescOwned);
9162306a36Sopenharmony_ci	}
9262306a36Sopenharmony_ci	if(tp->chip_id == LC82C168) {
9362306a36Sopenharmony_ci		if(((ioread32(tp->base_addr + CSR5)>>17)&0x07) == 4) {
9462306a36Sopenharmony_ci			/* Rx stopped due to out of buffers,
9562306a36Sopenharmony_ci			 * restart it
9662306a36Sopenharmony_ci			 */
9762306a36Sopenharmony_ci			iowrite32(0x01, tp->base_addr + CSR2);
9862306a36Sopenharmony_ci		}
9962306a36Sopenharmony_ci	}
10062306a36Sopenharmony_ci	return refilled;
10162306a36Sopenharmony_ci}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci#ifdef CONFIG_TULIP_NAPI
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_civoid oom_timer(struct timer_list *t)
10662306a36Sopenharmony_ci{
10762306a36Sopenharmony_ci	struct tulip_private *tp = from_timer(tp, t, oom_timer);
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	napi_schedule(&tp->napi);
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ciint tulip_poll(struct napi_struct *napi, int budget)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	struct tulip_private *tp = container_of(napi, struct tulip_private, napi);
11562306a36Sopenharmony_ci	struct net_device *dev = tp->dev;
11662306a36Sopenharmony_ci	int entry = tp->cur_rx % RX_RING_SIZE;
11762306a36Sopenharmony_ci	int work_done = 0;
11862306a36Sopenharmony_ci#ifdef CONFIG_TULIP_NAPI_HW_MITIGATION
11962306a36Sopenharmony_ci	int received = 0;
12062306a36Sopenharmony_ci#endif
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci#ifdef CONFIG_TULIP_NAPI_HW_MITIGATION
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci/* that one buffer is needed for mit activation; or might be a
12562306a36Sopenharmony_ci   bug in the ring buffer code; check later -- JHS*/
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci        if (budget >=RX_RING_SIZE) budget--;
12862306a36Sopenharmony_ci#endif
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	if (tulip_debug > 4)
13162306a36Sopenharmony_ci		netdev_dbg(dev, " In tulip_rx(), entry %d %08x\n",
13262306a36Sopenharmony_ci			   entry, tp->rx_ring[entry].status);
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci       do {
13562306a36Sopenharmony_ci		if (ioread32(tp->base_addr + CSR5) == 0xffffffff) {
13662306a36Sopenharmony_ci			netdev_dbg(dev, " In tulip_poll(), hardware disappeared\n");
13762306a36Sopenharmony_ci			break;
13862306a36Sopenharmony_ci		}
13962306a36Sopenharmony_ci               /* Acknowledge current RX interrupt sources. */
14062306a36Sopenharmony_ci               iowrite32((RxIntr | RxNoBuf), tp->base_addr + CSR5);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci               /* If we own the next entry, it is a new packet. Send it up. */
14462306a36Sopenharmony_ci               while ( ! (tp->rx_ring[entry].status & cpu_to_le32(DescOwned))) {
14562306a36Sopenharmony_ci                       s32 status = le32_to_cpu(tp->rx_ring[entry].status);
14662306a36Sopenharmony_ci		       short pkt_len;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci                       if (tp->dirty_rx + RX_RING_SIZE == tp->cur_rx)
14962306a36Sopenharmony_ci                               break;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci		       if (tulip_debug > 5)
15262306a36Sopenharmony_ci				netdev_dbg(dev, "In tulip_rx(), entry %d %08x\n",
15362306a36Sopenharmony_ci					   entry, status);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci		       if (++work_done >= budget)
15662306a36Sopenharmony_ci                               goto not_done;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci		       /*
15962306a36Sopenharmony_ci			* Omit the four octet CRC from the length.
16062306a36Sopenharmony_ci			* (May not be considered valid until we have
16162306a36Sopenharmony_ci			* checked status for RxLengthOver2047 bits)
16262306a36Sopenharmony_ci			*/
16362306a36Sopenharmony_ci		       pkt_len = ((status >> 16) & 0x7ff) - 4;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci		       /*
16662306a36Sopenharmony_ci			* Maximum pkt_len is 1518 (1514 + vlan header)
16762306a36Sopenharmony_ci			* Anything higher than this is always invalid
16862306a36Sopenharmony_ci			* regardless of RxLengthOver2047 bits
16962306a36Sopenharmony_ci			*/
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci		       if ((status & (RxLengthOver2047 |
17262306a36Sopenharmony_ci				      RxDescCRCError |
17362306a36Sopenharmony_ci				      RxDescCollisionSeen |
17462306a36Sopenharmony_ci				      RxDescRunt |
17562306a36Sopenharmony_ci				      RxDescDescErr |
17662306a36Sopenharmony_ci				      RxWholePkt)) != RxWholePkt ||
17762306a36Sopenharmony_ci			   pkt_len > 1518) {
17862306a36Sopenharmony_ci			       if ((status & (RxLengthOver2047 |
17962306a36Sopenharmony_ci					      RxWholePkt)) != RxWholePkt) {
18062306a36Sopenharmony_ci                                /* Ingore earlier buffers. */
18162306a36Sopenharmony_ci                                       if ((status & 0xffff) != 0x7fff) {
18262306a36Sopenharmony_ci                                               if (tulip_debug > 1)
18362306a36Sopenharmony_ci                                                       dev_warn(&dev->dev,
18462306a36Sopenharmony_ci								"Oversized Ethernet frame spanned multiple buffers, status %08x!\n",
18562306a36Sopenharmony_ci								status);
18662306a36Sopenharmony_ci						dev->stats.rx_length_errors++;
18762306a36Sopenharmony_ci					}
18862306a36Sopenharmony_ci			       } else {
18962306a36Sopenharmony_ci                                /* There was a fatal error. */
19062306a36Sopenharmony_ci				       if (tulip_debug > 2)
19162306a36Sopenharmony_ci						netdev_dbg(dev, "Receive error, Rx status %08x\n",
19262306a36Sopenharmony_ci							   status);
19362306a36Sopenharmony_ci					dev->stats.rx_errors++; /* end of a packet.*/
19462306a36Sopenharmony_ci					if (pkt_len > 1518 ||
19562306a36Sopenharmony_ci					    (status & RxDescRunt))
19662306a36Sopenharmony_ci						dev->stats.rx_length_errors++;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci					if (status & 0x0004)
19962306a36Sopenharmony_ci						dev->stats.rx_frame_errors++;
20062306a36Sopenharmony_ci					if (status & 0x0002)
20162306a36Sopenharmony_ci						dev->stats.rx_crc_errors++;
20262306a36Sopenharmony_ci					if (status & 0x0001)
20362306a36Sopenharmony_ci						dev->stats.rx_fifo_errors++;
20462306a36Sopenharmony_ci                               }
20562306a36Sopenharmony_ci                       } else {
20662306a36Sopenharmony_ci                               struct sk_buff *skb;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci                               /* Check if the packet is long enough to accept without copying
20962306a36Sopenharmony_ci                                  to a minimally-sized skbuff. */
21062306a36Sopenharmony_ci                               if (pkt_len < tulip_rx_copybreak &&
21162306a36Sopenharmony_ci                                   (skb = netdev_alloc_skb(dev, pkt_len + 2)) != NULL) {
21262306a36Sopenharmony_ci                                       skb_reserve(skb, 2);    /* 16 byte align the IP header */
21362306a36Sopenharmony_ci					dma_sync_single_for_cpu(&tp->pdev->dev,
21462306a36Sopenharmony_ci								tp->rx_buffers[entry].mapping,
21562306a36Sopenharmony_ci								pkt_len,
21662306a36Sopenharmony_ci								DMA_FROM_DEVICE);
21762306a36Sopenharmony_ci#if ! defined(__alpha__)
21862306a36Sopenharmony_ci                                       skb_copy_to_linear_data(skb, tp->rx_buffers[entry].skb->data,
21962306a36Sopenharmony_ci                                                        pkt_len);
22062306a36Sopenharmony_ci                                       skb_put(skb, pkt_len);
22162306a36Sopenharmony_ci#else
22262306a36Sopenharmony_ci                                       skb_put_data(skb,
22362306a36Sopenharmony_ci                                                    tp->rx_buffers[entry].skb->data,
22462306a36Sopenharmony_ci                                                    pkt_len);
22562306a36Sopenharmony_ci#endif
22662306a36Sopenharmony_ci					dma_sync_single_for_device(&tp->pdev->dev,
22762306a36Sopenharmony_ci								   tp->rx_buffers[entry].mapping,
22862306a36Sopenharmony_ci								   pkt_len,
22962306a36Sopenharmony_ci								   DMA_FROM_DEVICE);
23062306a36Sopenharmony_ci                               } else {        /* Pass up the skb already on the Rx ring. */
23162306a36Sopenharmony_ci                                       char *temp = skb_put(skb = tp->rx_buffers[entry].skb,
23262306a36Sopenharmony_ci                                                            pkt_len);
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci#ifndef final_version
23562306a36Sopenharmony_ci                                       if (tp->rx_buffers[entry].mapping !=
23662306a36Sopenharmony_ci                                           le32_to_cpu(tp->rx_ring[entry].buffer1)) {
23762306a36Sopenharmony_ci                                               dev_err(&dev->dev,
23862306a36Sopenharmony_ci						       "Internal fault: The skbuff addresses do not match in tulip_rx: %08x vs. %08llx %p / %p\n",
23962306a36Sopenharmony_ci						       le32_to_cpu(tp->rx_ring[entry].buffer1),
24062306a36Sopenharmony_ci						       (unsigned long long)tp->rx_buffers[entry].mapping,
24162306a36Sopenharmony_ci						       skb->head, temp);
24262306a36Sopenharmony_ci                                       }
24362306a36Sopenharmony_ci#endif
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci					dma_unmap_single(&tp->pdev->dev,
24662306a36Sopenharmony_ci							 tp->rx_buffers[entry].mapping,
24762306a36Sopenharmony_ci							 PKT_BUF_SZ,
24862306a36Sopenharmony_ci							 DMA_FROM_DEVICE);
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci                                       tp->rx_buffers[entry].skb = NULL;
25162306a36Sopenharmony_ci                                       tp->rx_buffers[entry].mapping = 0;
25262306a36Sopenharmony_ci                               }
25362306a36Sopenharmony_ci                               skb->protocol = eth_type_trans(skb, dev);
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci                               netif_receive_skb(skb);
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci				dev->stats.rx_packets++;
25862306a36Sopenharmony_ci				dev->stats.rx_bytes += pkt_len;
25962306a36Sopenharmony_ci                       }
26062306a36Sopenharmony_ci#ifdef CONFIG_TULIP_NAPI_HW_MITIGATION
26162306a36Sopenharmony_ci		       received++;
26262306a36Sopenharmony_ci#endif
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci                       entry = (++tp->cur_rx) % RX_RING_SIZE;
26562306a36Sopenharmony_ci                       if (tp->cur_rx - tp->dirty_rx > RX_RING_SIZE/4)
26662306a36Sopenharmony_ci                               tulip_refill_rx(dev);
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci                }
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci               /* New ack strategy... irq does not ack Rx any longer
27162306a36Sopenharmony_ci                  hopefully this helps */
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci               /* Really bad things can happen here... If new packet arrives
27462306a36Sopenharmony_ci                * and an irq arrives (tx or just due to occasionally unset
27562306a36Sopenharmony_ci                * mask), it will be acked by irq handler, but new thread
27662306a36Sopenharmony_ci                * is not scheduled. It is major hole in design.
27762306a36Sopenharmony_ci                * No idea how to fix this if "playing with fire" will fail
27862306a36Sopenharmony_ci                * tomorrow (night 011029). If it will not fail, we won
27962306a36Sopenharmony_ci                * finally: amount of IO did not increase at all. */
28062306a36Sopenharmony_ci       } while ((ioread32(tp->base_addr + CSR5) & RxIntr));
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci #ifdef CONFIG_TULIP_NAPI_HW_MITIGATION
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci          /* We use this simplistic scheme for IM. It's proven by
28562306a36Sopenharmony_ci             real life installations. We can have IM enabled
28662306a36Sopenharmony_ci            continuesly but this would cause unnecessary latency.
28762306a36Sopenharmony_ci            Unfortunely we can't use all the NET_RX_* feedback here.
28862306a36Sopenharmony_ci            This would turn on IM for devices that is not contributing
28962306a36Sopenharmony_ci            to backlog congestion with unnecessary latency.
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci             We monitor the device RX-ring and have:
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci             HW Interrupt Mitigation either ON or OFF.
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci            ON:  More then 1 pkt received (per intr.) OR we are dropping
29662306a36Sopenharmony_ci             OFF: Only 1 pkt received
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci             Note. We only use min and max (0, 15) settings from mit_table */
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci          if( tp->flags &  HAS_INTR_MITIGATION) {
30262306a36Sopenharmony_ci                 if( received > 1 ) {
30362306a36Sopenharmony_ci                         if( ! tp->mit_on ) {
30462306a36Sopenharmony_ci                                 tp->mit_on = 1;
30562306a36Sopenharmony_ci                                 iowrite32(mit_table[MIT_TABLE], tp->base_addr + CSR11);
30662306a36Sopenharmony_ci                         }
30762306a36Sopenharmony_ci                  }
30862306a36Sopenharmony_ci                 else {
30962306a36Sopenharmony_ci                         if( tp->mit_on ) {
31062306a36Sopenharmony_ci                                 tp->mit_on = 0;
31162306a36Sopenharmony_ci                                 iowrite32(0, tp->base_addr + CSR11);
31262306a36Sopenharmony_ci                         }
31362306a36Sopenharmony_ci                  }
31462306a36Sopenharmony_ci          }
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci#endif /* CONFIG_TULIP_NAPI_HW_MITIGATION */
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci         tulip_refill_rx(dev);
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci         /* If RX ring is not full we are out of memory. */
32162306a36Sopenharmony_ci         if (tp->rx_buffers[tp->dirty_rx % RX_RING_SIZE].skb == NULL)
32262306a36Sopenharmony_ci		 goto oom;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci         /* Remove us from polling list and enable RX intr. */
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	napi_complete_done(napi, work_done);
32762306a36Sopenharmony_ci	iowrite32(tulip_tbl[tp->chip_id].valid_intrs, tp->base_addr+CSR7);
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci         /* The last op happens after poll completion. Which means the following:
33062306a36Sopenharmony_ci          * 1. it can race with disabling irqs in irq handler
33162306a36Sopenharmony_ci          * 2. it can race with dise/enabling irqs in other poll threads
33262306a36Sopenharmony_ci          * 3. if an irq raised after beginning loop, it will be immediately
33362306a36Sopenharmony_ci          *    triggered here.
33462306a36Sopenharmony_ci          *
33562306a36Sopenharmony_ci          * Summarizing: the logic results in some redundant irqs both
33662306a36Sopenharmony_ci          * due to races in masking and due to too late acking of already
33762306a36Sopenharmony_ci          * processed irqs. But it must not result in losing events.
33862306a36Sopenharmony_ci          */
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci         return work_done;
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci not_done:
34362306a36Sopenharmony_ci         if (tp->cur_rx - tp->dirty_rx > RX_RING_SIZE/2 ||
34462306a36Sopenharmony_ci             tp->rx_buffers[tp->dirty_rx % RX_RING_SIZE].skb == NULL)
34562306a36Sopenharmony_ci                 tulip_refill_rx(dev);
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci         if (tp->rx_buffers[tp->dirty_rx % RX_RING_SIZE].skb == NULL)
34862306a36Sopenharmony_ci		 goto oom;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci         return work_done;
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci oom:    /* Executed with RX ints disabled */
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci         /* Start timer, stop polling, but do not enable rx interrupts. */
35562306a36Sopenharmony_ci         mod_timer(&tp->oom_timer, jiffies+1);
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci         /* Think: timer_pending() was an explicit signature of bug.
35862306a36Sopenharmony_ci          * Timer can be pending now but fired and completed
35962306a36Sopenharmony_ci          * before we did napi_complete(). See? We would lose it. */
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci         /* remove ourselves from the polling list */
36262306a36Sopenharmony_ci         napi_complete_done(napi, work_done);
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci         return work_done;
36562306a36Sopenharmony_ci}
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci#else /* CONFIG_TULIP_NAPI */
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_cistatic int tulip_rx(struct net_device *dev)
37062306a36Sopenharmony_ci{
37162306a36Sopenharmony_ci	struct tulip_private *tp = netdev_priv(dev);
37262306a36Sopenharmony_ci	int entry = tp->cur_rx % RX_RING_SIZE;
37362306a36Sopenharmony_ci	int rx_work_limit = tp->dirty_rx + RX_RING_SIZE - tp->cur_rx;
37462306a36Sopenharmony_ci	int received = 0;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	if (tulip_debug > 4)
37762306a36Sopenharmony_ci		netdev_dbg(dev, "In tulip_rx(), entry %d %08x\n",
37862306a36Sopenharmony_ci			   entry, tp->rx_ring[entry].status);
37962306a36Sopenharmony_ci	/* If we own the next entry, it is a new packet. Send it up. */
38062306a36Sopenharmony_ci	while ( ! (tp->rx_ring[entry].status & cpu_to_le32(DescOwned))) {
38162306a36Sopenharmony_ci		s32 status = le32_to_cpu(tp->rx_ring[entry].status);
38262306a36Sopenharmony_ci		short pkt_len;
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci		if (tulip_debug > 5)
38562306a36Sopenharmony_ci			netdev_dbg(dev, "In tulip_rx(), entry %d %08x\n",
38662306a36Sopenharmony_ci				   entry, status);
38762306a36Sopenharmony_ci		if (--rx_work_limit < 0)
38862306a36Sopenharmony_ci			break;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci		/*
39162306a36Sopenharmony_ci		  Omit the four octet CRC from the length.
39262306a36Sopenharmony_ci		  (May not be considered valid until we have
39362306a36Sopenharmony_ci		  checked status for RxLengthOver2047 bits)
39462306a36Sopenharmony_ci		*/
39562306a36Sopenharmony_ci		pkt_len = ((status >> 16) & 0x7ff) - 4;
39662306a36Sopenharmony_ci		/*
39762306a36Sopenharmony_ci		  Maximum pkt_len is 1518 (1514 + vlan header)
39862306a36Sopenharmony_ci		  Anything higher than this is always invalid
39962306a36Sopenharmony_ci		  regardless of RxLengthOver2047 bits
40062306a36Sopenharmony_ci		*/
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci		if ((status & (RxLengthOver2047 |
40362306a36Sopenharmony_ci			       RxDescCRCError |
40462306a36Sopenharmony_ci			       RxDescCollisionSeen |
40562306a36Sopenharmony_ci			       RxDescRunt |
40662306a36Sopenharmony_ci			       RxDescDescErr |
40762306a36Sopenharmony_ci			       RxWholePkt))        != RxWholePkt ||
40862306a36Sopenharmony_ci		    pkt_len > 1518) {
40962306a36Sopenharmony_ci			if ((status & (RxLengthOver2047 |
41062306a36Sopenharmony_ci			     RxWholePkt))         != RxWholePkt) {
41162306a36Sopenharmony_ci				/* Ingore earlier buffers. */
41262306a36Sopenharmony_ci				if ((status & 0xffff) != 0x7fff) {
41362306a36Sopenharmony_ci					if (tulip_debug > 1)
41462306a36Sopenharmony_ci						netdev_warn(dev,
41562306a36Sopenharmony_ci							    "Oversized Ethernet frame spanned multiple buffers, status %08x!\n",
41662306a36Sopenharmony_ci							    status);
41762306a36Sopenharmony_ci					dev->stats.rx_length_errors++;
41862306a36Sopenharmony_ci				}
41962306a36Sopenharmony_ci			} else {
42062306a36Sopenharmony_ci				/* There was a fatal error. */
42162306a36Sopenharmony_ci				if (tulip_debug > 2)
42262306a36Sopenharmony_ci					netdev_dbg(dev, "Receive error, Rx status %08x\n",
42362306a36Sopenharmony_ci						   status);
42462306a36Sopenharmony_ci				dev->stats.rx_errors++; /* end of a packet.*/
42562306a36Sopenharmony_ci				if (pkt_len > 1518 ||
42662306a36Sopenharmony_ci				    (status & RxDescRunt))
42762306a36Sopenharmony_ci					dev->stats.rx_length_errors++;
42862306a36Sopenharmony_ci				if (status & 0x0004)
42962306a36Sopenharmony_ci					dev->stats.rx_frame_errors++;
43062306a36Sopenharmony_ci				if (status & 0x0002)
43162306a36Sopenharmony_ci					dev->stats.rx_crc_errors++;
43262306a36Sopenharmony_ci				if (status & 0x0001)
43362306a36Sopenharmony_ci					dev->stats.rx_fifo_errors++;
43462306a36Sopenharmony_ci			}
43562306a36Sopenharmony_ci		} else {
43662306a36Sopenharmony_ci			struct sk_buff *skb;
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci			/* Check if the packet is long enough to accept without copying
43962306a36Sopenharmony_ci			   to a minimally-sized skbuff. */
44062306a36Sopenharmony_ci			if (pkt_len < tulip_rx_copybreak &&
44162306a36Sopenharmony_ci			    (skb = netdev_alloc_skb(dev, pkt_len + 2)) != NULL) {
44262306a36Sopenharmony_ci				skb_reserve(skb, 2);	/* 16 byte align the IP header */
44362306a36Sopenharmony_ci				dma_sync_single_for_cpu(&tp->pdev->dev,
44462306a36Sopenharmony_ci							tp->rx_buffers[entry].mapping,
44562306a36Sopenharmony_ci							pkt_len,
44662306a36Sopenharmony_ci							DMA_FROM_DEVICE);
44762306a36Sopenharmony_ci#if ! defined(__alpha__)
44862306a36Sopenharmony_ci				skb_copy_to_linear_data(skb, tp->rx_buffers[entry].skb->data,
44962306a36Sopenharmony_ci						 pkt_len);
45062306a36Sopenharmony_ci				skb_put(skb, pkt_len);
45162306a36Sopenharmony_ci#else
45262306a36Sopenharmony_ci				skb_put_data(skb,
45362306a36Sopenharmony_ci					     tp->rx_buffers[entry].skb->data,
45462306a36Sopenharmony_ci					     pkt_len);
45562306a36Sopenharmony_ci#endif
45662306a36Sopenharmony_ci				dma_sync_single_for_device(&tp->pdev->dev,
45762306a36Sopenharmony_ci							   tp->rx_buffers[entry].mapping,
45862306a36Sopenharmony_ci							   pkt_len,
45962306a36Sopenharmony_ci							   DMA_FROM_DEVICE);
46062306a36Sopenharmony_ci			} else { 	/* Pass up the skb already on the Rx ring. */
46162306a36Sopenharmony_ci				char *temp = skb_put(skb = tp->rx_buffers[entry].skb,
46262306a36Sopenharmony_ci						     pkt_len);
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci#ifndef final_version
46562306a36Sopenharmony_ci				if (tp->rx_buffers[entry].mapping !=
46662306a36Sopenharmony_ci				    le32_to_cpu(tp->rx_ring[entry].buffer1)) {
46762306a36Sopenharmony_ci					dev_err(&dev->dev,
46862306a36Sopenharmony_ci						"Internal fault: The skbuff addresses do not match in tulip_rx: %08x vs. %Lx %p / %p\n",
46962306a36Sopenharmony_ci						le32_to_cpu(tp->rx_ring[entry].buffer1),
47062306a36Sopenharmony_ci						(long long)tp->rx_buffers[entry].mapping,
47162306a36Sopenharmony_ci						skb->head, temp);
47262306a36Sopenharmony_ci				}
47362306a36Sopenharmony_ci#endif
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci				dma_unmap_single(&tp->pdev->dev,
47662306a36Sopenharmony_ci						 tp->rx_buffers[entry].mapping,
47762306a36Sopenharmony_ci						 PKT_BUF_SZ, DMA_FROM_DEVICE);
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci				tp->rx_buffers[entry].skb = NULL;
48062306a36Sopenharmony_ci				tp->rx_buffers[entry].mapping = 0;
48162306a36Sopenharmony_ci			}
48262306a36Sopenharmony_ci			skb->protocol = eth_type_trans(skb, dev);
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci			netif_rx(skb);
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci			dev->stats.rx_packets++;
48762306a36Sopenharmony_ci			dev->stats.rx_bytes += pkt_len;
48862306a36Sopenharmony_ci		}
48962306a36Sopenharmony_ci		received++;
49062306a36Sopenharmony_ci		entry = (++tp->cur_rx) % RX_RING_SIZE;
49162306a36Sopenharmony_ci	}
49262306a36Sopenharmony_ci	return received;
49362306a36Sopenharmony_ci}
49462306a36Sopenharmony_ci#endif  /* CONFIG_TULIP_NAPI */
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_cistatic inline unsigned int phy_interrupt (struct net_device *dev)
49762306a36Sopenharmony_ci{
49862306a36Sopenharmony_ci#ifdef __hppa__
49962306a36Sopenharmony_ci	struct tulip_private *tp = netdev_priv(dev);
50062306a36Sopenharmony_ci	int csr12 = ioread32(tp->base_addr + CSR12) & 0xff;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	if (csr12 != tp->csr12_shadow) {
50362306a36Sopenharmony_ci		/* ack interrupt */
50462306a36Sopenharmony_ci		iowrite32(csr12 | 0x02, tp->base_addr + CSR12);
50562306a36Sopenharmony_ci		tp->csr12_shadow = csr12;
50662306a36Sopenharmony_ci		/* do link change stuff */
50762306a36Sopenharmony_ci		spin_lock(&tp->lock);
50862306a36Sopenharmony_ci		tulip_check_duplex(dev);
50962306a36Sopenharmony_ci		spin_unlock(&tp->lock);
51062306a36Sopenharmony_ci		/* clear irq ack bit */
51162306a36Sopenharmony_ci		iowrite32(csr12 & ~0x02, tp->base_addr + CSR12);
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci		return 1;
51462306a36Sopenharmony_ci	}
51562306a36Sopenharmony_ci#endif
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	return 0;
51862306a36Sopenharmony_ci}
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci/* The interrupt handler does all of the Rx thread work and cleans up
52162306a36Sopenharmony_ci   after the Tx thread. */
52262306a36Sopenharmony_ciirqreturn_t tulip_interrupt(int irq, void *dev_instance)
52362306a36Sopenharmony_ci{
52462306a36Sopenharmony_ci	struct net_device *dev = (struct net_device *)dev_instance;
52562306a36Sopenharmony_ci	struct tulip_private *tp = netdev_priv(dev);
52662306a36Sopenharmony_ci	void __iomem *ioaddr = tp->base_addr;
52762306a36Sopenharmony_ci	int csr5;
52862306a36Sopenharmony_ci	int missed;
52962306a36Sopenharmony_ci	int rx = 0;
53062306a36Sopenharmony_ci	int tx = 0;
53162306a36Sopenharmony_ci	int oi = 0;
53262306a36Sopenharmony_ci	int maxrx = RX_RING_SIZE;
53362306a36Sopenharmony_ci	int maxtx = TX_RING_SIZE;
53462306a36Sopenharmony_ci	int maxoi = TX_RING_SIZE;
53562306a36Sopenharmony_ci#ifdef CONFIG_TULIP_NAPI
53662306a36Sopenharmony_ci	int rxd = 0;
53762306a36Sopenharmony_ci#else
53862306a36Sopenharmony_ci	int entry;
53962306a36Sopenharmony_ci#endif
54062306a36Sopenharmony_ci	unsigned int work_count = tulip_max_interrupt_work;
54162306a36Sopenharmony_ci	unsigned int handled = 0;
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	/* Let's see whether the interrupt really is for us */
54462306a36Sopenharmony_ci	csr5 = ioread32(ioaddr + CSR5);
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci        if (tp->flags & HAS_PHY_IRQ)
54762306a36Sopenharmony_ci	        handled = phy_interrupt (dev);
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	if ((csr5 & (NormalIntr|AbnormalIntr)) == 0)
55062306a36Sopenharmony_ci		return IRQ_RETVAL(handled);
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	tp->nir++;
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	do {
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci#ifdef CONFIG_TULIP_NAPI
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci		if (!rxd && (csr5 & (RxIntr | RxNoBuf))) {
55962306a36Sopenharmony_ci			rxd++;
56062306a36Sopenharmony_ci			/* Mask RX intrs and add the device to poll list. */
56162306a36Sopenharmony_ci			iowrite32(tulip_tbl[tp->chip_id].valid_intrs&~RxPollInt, ioaddr + CSR7);
56262306a36Sopenharmony_ci			napi_schedule(&tp->napi);
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci			if (!(csr5&~(AbnormalIntr|NormalIntr|RxPollInt|TPLnkPass)))
56562306a36Sopenharmony_ci                               break;
56662306a36Sopenharmony_ci		}
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci               /* Acknowledge the interrupt sources we handle here ASAP
56962306a36Sopenharmony_ci                  the poll function does Rx and RxNoBuf acking */
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci		iowrite32(csr5 & 0x0001ff3f, ioaddr + CSR5);
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci#else
57462306a36Sopenharmony_ci		/* Acknowledge all of the current interrupt sources ASAP. */
57562306a36Sopenharmony_ci		iowrite32(csr5 & 0x0001ffff, ioaddr + CSR5);
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci		if (csr5 & (RxIntr | RxNoBuf)) {
57962306a36Sopenharmony_ci				rx += tulip_rx(dev);
58062306a36Sopenharmony_ci			tulip_refill_rx(dev);
58162306a36Sopenharmony_ci		}
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci#endif /*  CONFIG_TULIP_NAPI */
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci		if (tulip_debug > 4)
58662306a36Sopenharmony_ci			netdev_dbg(dev, "interrupt  csr5=%#8.8x new csr5=%#8.8x\n",
58762306a36Sopenharmony_ci				   csr5, ioread32(ioaddr + CSR5));
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci		if (csr5 & (TxNoBuf | TxDied | TxIntr | TimerInt)) {
59162306a36Sopenharmony_ci			unsigned int dirty_tx;
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci			spin_lock(&tp->lock);
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci			for (dirty_tx = tp->dirty_tx; tp->cur_tx - dirty_tx > 0;
59662306a36Sopenharmony_ci				 dirty_tx++) {
59762306a36Sopenharmony_ci				int entry = dirty_tx % TX_RING_SIZE;
59862306a36Sopenharmony_ci				int status = le32_to_cpu(tp->tx_ring[entry].status);
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci				if (status < 0)
60162306a36Sopenharmony_ci					break;			/* It still has not been Txed */
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci				/* Check for Rx filter setup frames. */
60462306a36Sopenharmony_ci				if (tp->tx_buffers[entry].skb == NULL) {
60562306a36Sopenharmony_ci					/* test because dummy frames not mapped */
60662306a36Sopenharmony_ci					if (tp->tx_buffers[entry].mapping)
60762306a36Sopenharmony_ci						dma_unmap_single(&tp->pdev->dev,
60862306a36Sopenharmony_ci								 tp->tx_buffers[entry].mapping,
60962306a36Sopenharmony_ci								 sizeof(tp->setup_frame),
61062306a36Sopenharmony_ci								 DMA_TO_DEVICE);
61162306a36Sopenharmony_ci					continue;
61262306a36Sopenharmony_ci				}
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci				if (status & 0x8000) {
61562306a36Sopenharmony_ci					/* There was an major error, log it. */
61662306a36Sopenharmony_ci#ifndef final_version
61762306a36Sopenharmony_ci					if (tulip_debug > 1)
61862306a36Sopenharmony_ci						netdev_dbg(dev, "Transmit error, Tx status %08x\n",
61962306a36Sopenharmony_ci							   status);
62062306a36Sopenharmony_ci#endif
62162306a36Sopenharmony_ci					dev->stats.tx_errors++;
62262306a36Sopenharmony_ci					if (status & 0x4104)
62362306a36Sopenharmony_ci						dev->stats.tx_aborted_errors++;
62462306a36Sopenharmony_ci					if (status & 0x0C00)
62562306a36Sopenharmony_ci						dev->stats.tx_carrier_errors++;
62662306a36Sopenharmony_ci					if (status & 0x0200)
62762306a36Sopenharmony_ci						dev->stats.tx_window_errors++;
62862306a36Sopenharmony_ci					if (status & 0x0002)
62962306a36Sopenharmony_ci						dev->stats.tx_fifo_errors++;
63062306a36Sopenharmony_ci					if ((status & 0x0080) && tp->full_duplex == 0)
63162306a36Sopenharmony_ci						dev->stats.tx_heartbeat_errors++;
63262306a36Sopenharmony_ci				} else {
63362306a36Sopenharmony_ci					dev->stats.tx_bytes +=
63462306a36Sopenharmony_ci						tp->tx_buffers[entry].skb->len;
63562306a36Sopenharmony_ci					dev->stats.collisions += (status >> 3) & 15;
63662306a36Sopenharmony_ci					dev->stats.tx_packets++;
63762306a36Sopenharmony_ci				}
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci				dma_unmap_single(&tp->pdev->dev,
64062306a36Sopenharmony_ci						 tp->tx_buffers[entry].mapping,
64162306a36Sopenharmony_ci						 tp->tx_buffers[entry].skb->len,
64262306a36Sopenharmony_ci						 DMA_TO_DEVICE);
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci				/* Free the original skb. */
64562306a36Sopenharmony_ci				dev_kfree_skb_irq(tp->tx_buffers[entry].skb);
64662306a36Sopenharmony_ci				tp->tx_buffers[entry].skb = NULL;
64762306a36Sopenharmony_ci				tp->tx_buffers[entry].mapping = 0;
64862306a36Sopenharmony_ci				tx++;
64962306a36Sopenharmony_ci			}
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci#ifndef final_version
65262306a36Sopenharmony_ci			if (tp->cur_tx - dirty_tx > TX_RING_SIZE) {
65362306a36Sopenharmony_ci				dev_err(&dev->dev,
65462306a36Sopenharmony_ci					"Out-of-sync dirty pointer, %d vs. %d\n",
65562306a36Sopenharmony_ci					dirty_tx, tp->cur_tx);
65662306a36Sopenharmony_ci				dirty_tx += TX_RING_SIZE;
65762306a36Sopenharmony_ci			}
65862306a36Sopenharmony_ci#endif
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci			if (tp->cur_tx - dirty_tx < TX_RING_SIZE - 2)
66162306a36Sopenharmony_ci				netif_wake_queue(dev);
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci			tp->dirty_tx = dirty_tx;
66462306a36Sopenharmony_ci			if (csr5 & TxDied) {
66562306a36Sopenharmony_ci				if (tulip_debug > 2)
66662306a36Sopenharmony_ci					dev_warn(&dev->dev,
66762306a36Sopenharmony_ci						 "The transmitter stopped.  CSR5 is %x, CSR6 %x, new CSR6 %x\n",
66862306a36Sopenharmony_ci						 csr5, ioread32(ioaddr + CSR6),
66962306a36Sopenharmony_ci						 tp->csr6);
67062306a36Sopenharmony_ci				tulip_restart_rxtx(tp);
67162306a36Sopenharmony_ci			}
67262306a36Sopenharmony_ci			spin_unlock(&tp->lock);
67362306a36Sopenharmony_ci		}
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci		/* Log errors. */
67662306a36Sopenharmony_ci		if (csr5 & AbnormalIntr) {	/* Abnormal error summary bit. */
67762306a36Sopenharmony_ci			if (csr5 == 0xffffffff)
67862306a36Sopenharmony_ci				break;
67962306a36Sopenharmony_ci			if (csr5 & TxJabber)
68062306a36Sopenharmony_ci				dev->stats.tx_errors++;
68162306a36Sopenharmony_ci			if (csr5 & TxFIFOUnderflow) {
68262306a36Sopenharmony_ci				if ((tp->csr6 & 0xC000) != 0xC000)
68362306a36Sopenharmony_ci					tp->csr6 += 0x4000;	/* Bump up the Tx threshold */
68462306a36Sopenharmony_ci				else
68562306a36Sopenharmony_ci					tp->csr6 |= 0x00200000;  /* Store-n-forward. */
68662306a36Sopenharmony_ci				/* Restart the transmit process. */
68762306a36Sopenharmony_ci				tulip_restart_rxtx(tp);
68862306a36Sopenharmony_ci				iowrite32(0, ioaddr + CSR1);
68962306a36Sopenharmony_ci			}
69062306a36Sopenharmony_ci			if (csr5 & (RxDied | RxNoBuf)) {
69162306a36Sopenharmony_ci				if (tp->flags & COMET_MAC_ADDR) {
69262306a36Sopenharmony_ci					iowrite32(tp->mc_filter[0], ioaddr + 0xAC);
69362306a36Sopenharmony_ci					iowrite32(tp->mc_filter[1], ioaddr + 0xB0);
69462306a36Sopenharmony_ci				}
69562306a36Sopenharmony_ci			}
69662306a36Sopenharmony_ci			if (csr5 & RxDied) {		/* Missed a Rx frame. */
69762306a36Sopenharmony_ci				dev->stats.rx_missed_errors += ioread32(ioaddr + CSR8) & 0xffff;
69862306a36Sopenharmony_ci				dev->stats.rx_errors++;
69962306a36Sopenharmony_ci				tulip_start_rxtx(tp);
70062306a36Sopenharmony_ci			}
70162306a36Sopenharmony_ci			/*
70262306a36Sopenharmony_ci			 * NB: t21142_lnk_change() does a del_timer_sync(), so be careful if this
70362306a36Sopenharmony_ci			 * call is ever done under the spinlock
70462306a36Sopenharmony_ci			 */
70562306a36Sopenharmony_ci			if (csr5 & (TPLnkPass | TPLnkFail | 0x08000000)) {
70662306a36Sopenharmony_ci				if (tp->link_change)
70762306a36Sopenharmony_ci					(tp->link_change)(dev, csr5);
70862306a36Sopenharmony_ci			}
70962306a36Sopenharmony_ci			if (csr5 & SystemError) {
71062306a36Sopenharmony_ci				int error = (csr5 >> 23) & 7;
71162306a36Sopenharmony_ci				/* oops, we hit a PCI error.  The code produced corresponds
71262306a36Sopenharmony_ci				 * to the reason:
71362306a36Sopenharmony_ci				 *  0 - parity error
71462306a36Sopenharmony_ci				 *  1 - master abort
71562306a36Sopenharmony_ci				 *  2 - target abort
71662306a36Sopenharmony_ci				 * Note that on parity error, we should do a software reset
71762306a36Sopenharmony_ci				 * of the chip to get it back into a sane state (according
71862306a36Sopenharmony_ci				 * to the 21142/3 docs that is).
71962306a36Sopenharmony_ci				 *   -- rmk
72062306a36Sopenharmony_ci				 */
72162306a36Sopenharmony_ci				dev_err(&dev->dev,
72262306a36Sopenharmony_ci					"(%lu) System Error occurred (%d)\n",
72362306a36Sopenharmony_ci					tp->nir, error);
72462306a36Sopenharmony_ci			}
72562306a36Sopenharmony_ci			/* Clear all error sources, included undocumented ones! */
72662306a36Sopenharmony_ci			iowrite32(0x0800f7ba, ioaddr + CSR5);
72762306a36Sopenharmony_ci			oi++;
72862306a36Sopenharmony_ci		}
72962306a36Sopenharmony_ci		if (csr5 & TimerInt) {
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci			if (tulip_debug > 2)
73262306a36Sopenharmony_ci				dev_err(&dev->dev,
73362306a36Sopenharmony_ci					"Re-enabling interrupts, %08x\n",
73462306a36Sopenharmony_ci					csr5);
73562306a36Sopenharmony_ci			iowrite32(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR7);
73662306a36Sopenharmony_ci			tp->ttimer = 0;
73762306a36Sopenharmony_ci			oi++;
73862306a36Sopenharmony_ci		}
73962306a36Sopenharmony_ci		if (tx > maxtx || rx > maxrx || oi > maxoi) {
74062306a36Sopenharmony_ci			if (tulip_debug > 1)
74162306a36Sopenharmony_ci				dev_warn(&dev->dev, "Too much work during an interrupt, csr5=0x%08x. (%lu) (%d,%d,%d)\n",
74262306a36Sopenharmony_ci					 csr5, tp->nir, tx, rx, oi);
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci                       /* Acknowledge all interrupt sources. */
74562306a36Sopenharmony_ci                        iowrite32(0x8001ffff, ioaddr + CSR5);
74662306a36Sopenharmony_ci                        if (tp->flags & HAS_INTR_MITIGATION) {
74762306a36Sopenharmony_ci                     /* Josip Loncaric at ICASE did extensive experimentation
74862306a36Sopenharmony_ci			to develop a good interrupt mitigation setting.*/
74962306a36Sopenharmony_ci                                iowrite32(0x8b240000, ioaddr + CSR11);
75062306a36Sopenharmony_ci                        } else if (tp->chip_id == LC82C168) {
75162306a36Sopenharmony_ci				/* the LC82C168 doesn't have a hw timer.*/
75262306a36Sopenharmony_ci				iowrite32(0x00, ioaddr + CSR7);
75362306a36Sopenharmony_ci				mod_timer(&tp->timer, RUN_AT(HZ/50));
75462306a36Sopenharmony_ci			} else {
75562306a36Sopenharmony_ci                          /* Mask all interrupting sources, set timer to
75662306a36Sopenharmony_ci				re-enable. */
75762306a36Sopenharmony_ci                                iowrite32(((~csr5) & 0x0001ebef) | AbnormalIntr | TimerInt, ioaddr + CSR7);
75862306a36Sopenharmony_ci                                iowrite32(0x0012, ioaddr + CSR11);
75962306a36Sopenharmony_ci                        }
76062306a36Sopenharmony_ci			break;
76162306a36Sopenharmony_ci		}
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci		work_count--;
76462306a36Sopenharmony_ci		if (work_count == 0)
76562306a36Sopenharmony_ci			break;
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci		csr5 = ioread32(ioaddr + CSR5);
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci#ifdef CONFIG_TULIP_NAPI
77062306a36Sopenharmony_ci		if (rxd)
77162306a36Sopenharmony_ci			csr5 &= ~RxPollInt;
77262306a36Sopenharmony_ci	} while ((csr5 & (TxNoBuf |
77362306a36Sopenharmony_ci			  TxDied |
77462306a36Sopenharmony_ci			  TxIntr |
77562306a36Sopenharmony_ci			  TimerInt |
77662306a36Sopenharmony_ci			  /* Abnormal intr. */
77762306a36Sopenharmony_ci			  RxDied |
77862306a36Sopenharmony_ci			  TxFIFOUnderflow |
77962306a36Sopenharmony_ci			  TxJabber |
78062306a36Sopenharmony_ci			  TPLnkFail |
78162306a36Sopenharmony_ci			  SystemError )) != 0);
78262306a36Sopenharmony_ci#else
78362306a36Sopenharmony_ci	} while ((csr5 & (NormalIntr|AbnormalIntr)) != 0);
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	tulip_refill_rx(dev);
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	/* check if the card is in suspend mode */
78862306a36Sopenharmony_ci	entry = tp->dirty_rx % RX_RING_SIZE;
78962306a36Sopenharmony_ci	if (tp->rx_buffers[entry].skb == NULL) {
79062306a36Sopenharmony_ci		if (tulip_debug > 1)
79162306a36Sopenharmony_ci			dev_warn(&dev->dev,
79262306a36Sopenharmony_ci				 "in rx suspend mode: (%lu) (tp->cur_rx = %u, ttimer = %d, rx = %d) go/stay in suspend mode\n",
79362306a36Sopenharmony_ci				 tp->nir, tp->cur_rx, tp->ttimer, rx);
79462306a36Sopenharmony_ci		if (tp->chip_id == LC82C168) {
79562306a36Sopenharmony_ci			iowrite32(0x00, ioaddr + CSR7);
79662306a36Sopenharmony_ci			mod_timer(&tp->timer, RUN_AT(HZ/50));
79762306a36Sopenharmony_ci		} else {
79862306a36Sopenharmony_ci			if (tp->ttimer == 0 || (ioread32(ioaddr + CSR11) & 0xffff) == 0) {
79962306a36Sopenharmony_ci				if (tulip_debug > 1)
80062306a36Sopenharmony_ci					dev_warn(&dev->dev,
80162306a36Sopenharmony_ci						 "in rx suspend mode: (%lu) set timer\n",
80262306a36Sopenharmony_ci						 tp->nir);
80362306a36Sopenharmony_ci				iowrite32(tulip_tbl[tp->chip_id].valid_intrs | TimerInt,
80462306a36Sopenharmony_ci					ioaddr + CSR7);
80562306a36Sopenharmony_ci				iowrite32(TimerInt, ioaddr + CSR5);
80662306a36Sopenharmony_ci				iowrite32(12, ioaddr + CSR11);
80762306a36Sopenharmony_ci				tp->ttimer = 1;
80862306a36Sopenharmony_ci			}
80962306a36Sopenharmony_ci		}
81062306a36Sopenharmony_ci	}
81162306a36Sopenharmony_ci#endif /* CONFIG_TULIP_NAPI */
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	if ((missed = ioread32(ioaddr + CSR8) & 0x1ffff)) {
81462306a36Sopenharmony_ci		dev->stats.rx_dropped += missed & 0x10000 ? 0x10000 : missed;
81562306a36Sopenharmony_ci	}
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	if (tulip_debug > 4)
81862306a36Sopenharmony_ci		netdev_dbg(dev, "exiting interrupt, csr5=%#04x\n",
81962306a36Sopenharmony_ci			   ioread32(ioaddr + CSR5));
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci	return IRQ_HANDLED;
82262306a36Sopenharmony_ci}
823