162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-1.0+
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci/* 8390.c: A general NS8390 ethernet driver core for linux. */
462306a36Sopenharmony_ci/*
562306a36Sopenharmony_ci	Written 1992-94 by Donald Becker.
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci	Copyright 1993 United States Government as represented by the
862306a36Sopenharmony_ci	Director, National Security Agency.
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci	The author may be reached as becker@scyld.com, or C/O
1162306a36Sopenharmony_ci	Scyld Computing Corporation
1262306a36Sopenharmony_ci	410 Severn Ave., Suite 210
1362306a36Sopenharmony_ci	Annapolis MD 21403
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci  This is the chip-specific code for many 8390-based ethernet adaptors.
1762306a36Sopenharmony_ci  This is not a complete driver, it must be combined with board-specific
1862306a36Sopenharmony_ci  code such as ne.c, wd.c, 3c503.c, etc.
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci  Seeing how at least eight drivers use this code, (not counting the
2162306a36Sopenharmony_ci  PCMCIA ones either) it is easy to break some card by what seems like
2262306a36Sopenharmony_ci  a simple innocent change. Please contact me or Donald if you think
2362306a36Sopenharmony_ci  you have found something that needs changing. -- PG
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci  Changelog:
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci  Paul Gortmaker	: remove set_bit lock, other cleanups.
2962306a36Sopenharmony_ci  Paul Gortmaker	: add ei_get_8390_hdr() so we can pass skb's to
3062306a36Sopenharmony_ci			  ei_block_input() for eth_io_copy_and_sum().
3162306a36Sopenharmony_ci  Paul Gortmaker	: exchange static int ei_pingpong for a #define,
3262306a36Sopenharmony_ci			  also add better Tx error handling.
3362306a36Sopenharmony_ci  Paul Gortmaker	: rewrite Rx overrun handling as per NS specs.
3462306a36Sopenharmony_ci  Alexey Kuznetsov	: use the 8390's six bit hash multicast filter.
3562306a36Sopenharmony_ci  Paul Gortmaker	: tweak ANK's above multicast changes a bit.
3662306a36Sopenharmony_ci  Paul Gortmaker	: update packet statistics for v2.1.x
3762306a36Sopenharmony_ci  Alan Cox		: support arbitrary stupid port mappings on the
3862306a36Sopenharmony_ci			  68K Macintosh. Support >16bit I/O spaces
3962306a36Sopenharmony_ci  Paul Gortmaker	: add kmod support for auto-loading of the 8390
4062306a36Sopenharmony_ci			  module by all drivers that require it.
4162306a36Sopenharmony_ci  Alan Cox		: Spinlocking work, added 'BUG_83C690'
4262306a36Sopenharmony_ci  Paul Gortmaker	: Separate out Tx timeout code from Tx path.
4362306a36Sopenharmony_ci  Paul Gortmaker	: Remove old unused single Tx buffer code.
4462306a36Sopenharmony_ci  Hayato Fujiwara	: Add m32r support.
4562306a36Sopenharmony_ci  Paul Gortmaker	: use skb_padto() instead of stack scratch area
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci  Sources:
4862306a36Sopenharmony_ci  The National Semiconductor LAN Databook, and the 3Com 3c503 databook.
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci  */
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci#include <linux/build_bug.h>
5362306a36Sopenharmony_ci#include <linux/module.h>
5462306a36Sopenharmony_ci#include <linux/kernel.h>
5562306a36Sopenharmony_ci#include <linux/jiffies.h>
5662306a36Sopenharmony_ci#include <linux/fs.h>
5762306a36Sopenharmony_ci#include <linux/types.h>
5862306a36Sopenharmony_ci#include <linux/string.h>
5962306a36Sopenharmony_ci#include <linux/bitops.h>
6062306a36Sopenharmony_ci#include <linux/uaccess.h>
6162306a36Sopenharmony_ci#include <linux/io.h>
6262306a36Sopenharmony_ci#include <asm/irq.h>
6362306a36Sopenharmony_ci#include <linux/delay.h>
6462306a36Sopenharmony_ci#include <linux/errno.h>
6562306a36Sopenharmony_ci#include <linux/fcntl.h>
6662306a36Sopenharmony_ci#include <linux/in.h>
6762306a36Sopenharmony_ci#include <linux/interrupt.h>
6862306a36Sopenharmony_ci#include <linux/init.h>
6962306a36Sopenharmony_ci#include <linux/crc32.h>
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci#include <linux/netdevice.h>
7262306a36Sopenharmony_ci#include <linux/etherdevice.h>
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci#define NS8390_CORE
7562306a36Sopenharmony_ci#include "8390.h"
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci#define BUG_83C690
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci/* These are the operational function interfaces to board-specific
8062306a36Sopenharmony_ci   routines.
8162306a36Sopenharmony_ci	void reset_8390(struct net_device *dev)
8262306a36Sopenharmony_ci		Resets the board associated with DEV, including a hardware reset of
8362306a36Sopenharmony_ci		the 8390.  This is only called when there is a transmit timeout, and
8462306a36Sopenharmony_ci		it is always followed by 8390_init().
8562306a36Sopenharmony_ci	void block_output(struct net_device *dev, int count, const unsigned char *buf,
8662306a36Sopenharmony_ci					  int start_page)
8762306a36Sopenharmony_ci		Write the COUNT bytes of BUF to the packet buffer at START_PAGE.  The
8862306a36Sopenharmony_ci		"page" value uses the 8390's 256-byte pages.
8962306a36Sopenharmony_ci	void get_8390_hdr(struct net_device *dev, struct e8390_hdr *hdr, int ring_page)
9062306a36Sopenharmony_ci		Read the 4 byte, page aligned 8390 header. *If* there is a
9162306a36Sopenharmony_ci		subsequent read, it will be of the rest of the packet.
9262306a36Sopenharmony_ci	void block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
9362306a36Sopenharmony_ci		Read COUNT bytes from the packet buffer into the skb data area. Start
9462306a36Sopenharmony_ci		reading from RING_OFFSET, the address as the 8390 sees it.  This will always
9562306a36Sopenharmony_ci		follow the read of the 8390 header.
9662306a36Sopenharmony_ci*/
9762306a36Sopenharmony_ci#define ei_reset_8390 (ei_local->reset_8390)
9862306a36Sopenharmony_ci#define ei_block_output (ei_local->block_output)
9962306a36Sopenharmony_ci#define ei_block_input (ei_local->block_input)
10062306a36Sopenharmony_ci#define ei_get_8390_hdr (ei_local->get_8390_hdr)
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci/* Index to functions. */
10362306a36Sopenharmony_cistatic void ei_tx_intr(struct net_device *dev);
10462306a36Sopenharmony_cistatic void ei_tx_err(struct net_device *dev);
10562306a36Sopenharmony_cistatic void ei_receive(struct net_device *dev);
10662306a36Sopenharmony_cistatic void ei_rx_overrun(struct net_device *dev);
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci/* Routines generic to NS8390-based boards. */
10962306a36Sopenharmony_cistatic void NS8390_trigger_send(struct net_device *dev, unsigned int length,
11062306a36Sopenharmony_ci								int start_page);
11162306a36Sopenharmony_cistatic void do_set_multicast_list(struct net_device *dev);
11262306a36Sopenharmony_cistatic void __NS8390_init(struct net_device *dev, int startp);
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_cistatic unsigned version_printed;
11562306a36Sopenharmony_cistatic int msg_enable;
11662306a36Sopenharmony_cistatic const int default_msg_level = (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_RX_ERR |
11762306a36Sopenharmony_ci				     NETIF_MSG_TX_ERR);
11862306a36Sopenharmony_cimodule_param(msg_enable, int, 0444);
11962306a36Sopenharmony_ciMODULE_PARM_DESC(msg_enable, "Debug message level (see linux/netdevice.h for bitmap)");
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci/*
12262306a36Sopenharmony_ci *	SMP and the 8390 setup.
12362306a36Sopenharmony_ci *
12462306a36Sopenharmony_ci *	The 8390 isn't exactly designed to be multithreaded on RX/TX. There is
12562306a36Sopenharmony_ci *	a page register that controls bank and packet buffer access. We guard
12662306a36Sopenharmony_ci *	this with ei_local->page_lock. Nobody should assume or set the page other
12762306a36Sopenharmony_ci *	than zero when the lock is not held. Lock holders must restore page 0
12862306a36Sopenharmony_ci *	before unlocking. Even pure readers must take the lock to protect in
12962306a36Sopenharmony_ci *	page 0.
13062306a36Sopenharmony_ci *
13162306a36Sopenharmony_ci *	To make life difficult the chip can also be very slow. We therefore can't
13262306a36Sopenharmony_ci *	just use spinlocks. For the longer lockups we disable the irq the device
13362306a36Sopenharmony_ci *	sits on and hold the lock. We must hold the lock because there is a dual
13462306a36Sopenharmony_ci *	processor case other than interrupts (get stats/set multicast list in
13562306a36Sopenharmony_ci *	parallel with each other and transmit).
13662306a36Sopenharmony_ci *
13762306a36Sopenharmony_ci *	Note: in theory we can just disable the irq on the card _but_ there is
13862306a36Sopenharmony_ci *	a latency on SMP irq delivery. So we can easily go "disable irq" "sync irqs"
13962306a36Sopenharmony_ci *	enter lock, take the queued irq. So we waddle instead of flying.
14062306a36Sopenharmony_ci *
14162306a36Sopenharmony_ci *	Finally by special arrangement for the purpose of being generally
14262306a36Sopenharmony_ci *	annoying the transmit function is called bh atomic. That places
14362306a36Sopenharmony_ci *	restrictions on the user context callers as disable_irq won't save
14462306a36Sopenharmony_ci *	them.
14562306a36Sopenharmony_ci *
14662306a36Sopenharmony_ci *	Additional explanation of problems with locking by Alan Cox:
14762306a36Sopenharmony_ci *
14862306a36Sopenharmony_ci *	"The author (me) didn't use spin_lock_irqsave because the slowness of the
14962306a36Sopenharmony_ci *	card means that approach caused horrible problems like losing serial data
15062306a36Sopenharmony_ci *	at 38400 baud on some chips. Remember many 8390 nics on PCI were ISA
15162306a36Sopenharmony_ci *	chips with FPGA front ends.
15262306a36Sopenharmony_ci *
15362306a36Sopenharmony_ci *	Ok the logic behind the 8390 is very simple:
15462306a36Sopenharmony_ci *
15562306a36Sopenharmony_ci *	Things to know
15662306a36Sopenharmony_ci *		- IRQ delivery is asynchronous to the PCI bus
15762306a36Sopenharmony_ci *		- Blocking the local CPU IRQ via spin locks was too slow
15862306a36Sopenharmony_ci *		- The chip has register windows needing locking work
15962306a36Sopenharmony_ci *
16062306a36Sopenharmony_ci *	So the path was once (I say once as people appear to have changed it
16162306a36Sopenharmony_ci *	in the mean time and it now looks rather bogus if the changes to use
16262306a36Sopenharmony_ci *	disable_irq_nosync_irqsave are disabling the local IRQ)
16362306a36Sopenharmony_ci *
16462306a36Sopenharmony_ci *
16562306a36Sopenharmony_ci *		Take the page lock
16662306a36Sopenharmony_ci *		Mask the IRQ on chip
16762306a36Sopenharmony_ci *		Disable the IRQ (but not mask locally- someone seems to have
16862306a36Sopenharmony_ci *			broken this with the lock validator stuff)
16962306a36Sopenharmony_ci *			[This must be _nosync as the page lock may otherwise
17062306a36Sopenharmony_ci *				deadlock us]
17162306a36Sopenharmony_ci *		Drop the page lock and turn IRQs back on
17262306a36Sopenharmony_ci *
17362306a36Sopenharmony_ci *		At this point an existing IRQ may still be running but we can't
17462306a36Sopenharmony_ci *		get a new one
17562306a36Sopenharmony_ci *
17662306a36Sopenharmony_ci *		Take the lock (so we know the IRQ has terminated) but don't mask
17762306a36Sopenharmony_ci *	the IRQs on the processor
17862306a36Sopenharmony_ci *		Set irqlock [for debug]
17962306a36Sopenharmony_ci *
18062306a36Sopenharmony_ci *		Transmit (slow as ****)
18162306a36Sopenharmony_ci *
18262306a36Sopenharmony_ci *		re-enable the IRQ
18362306a36Sopenharmony_ci *
18462306a36Sopenharmony_ci *
18562306a36Sopenharmony_ci *	We have to use disable_irq because otherwise you will get delayed
18662306a36Sopenharmony_ci *	interrupts on the APIC bus deadlocking the transmit path.
18762306a36Sopenharmony_ci *
18862306a36Sopenharmony_ci *	Quite hairy but the chip simply wasn't designed for SMP and you can't
18962306a36Sopenharmony_ci *	even ACK an interrupt without risking corrupting other parallel
19062306a36Sopenharmony_ci *	activities on the chip." [lkml, 25 Jul 2007]
19162306a36Sopenharmony_ci */
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci/**
19662306a36Sopenharmony_ci * ei_open - Open/initialize the board.
19762306a36Sopenharmony_ci * @dev: network device to initialize
19862306a36Sopenharmony_ci *
19962306a36Sopenharmony_ci * This routine goes all-out, setting everything
20062306a36Sopenharmony_ci * up anew at each open, even though many of these registers should only
20162306a36Sopenharmony_ci * need to be set once at boot.
20262306a36Sopenharmony_ci */
20362306a36Sopenharmony_cistatic int __ei_open(struct net_device *dev)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	unsigned long flags;
20662306a36Sopenharmony_ci	struct ei_device *ei_local = netdev_priv(dev);
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	if (dev->watchdog_timeo <= 0)
20962306a36Sopenharmony_ci		dev->watchdog_timeo = TX_TIMEOUT;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	/*
21262306a36Sopenharmony_ci	 *	Grab the page lock so we own the register set, then call
21362306a36Sopenharmony_ci	 *	the init function.
21462306a36Sopenharmony_ci	 */
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	spin_lock_irqsave(&ei_local->page_lock, flags);
21762306a36Sopenharmony_ci	__NS8390_init(dev, 1);
21862306a36Sopenharmony_ci	/* Set the flag before we drop the lock, That way the IRQ arrives
21962306a36Sopenharmony_ci	   after its set and we get no silly warnings */
22062306a36Sopenharmony_ci	netif_start_queue(dev);
22162306a36Sopenharmony_ci	spin_unlock_irqrestore(&ei_local->page_lock, flags);
22262306a36Sopenharmony_ci	ei_local->irqlock = 0;
22362306a36Sopenharmony_ci	return 0;
22462306a36Sopenharmony_ci}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci/**
22762306a36Sopenharmony_ci * ei_close - shut down network device
22862306a36Sopenharmony_ci * @dev: network device to close
22962306a36Sopenharmony_ci *
23062306a36Sopenharmony_ci * Opposite of ei_open(). Only used when "ifconfig <devname> down" is done.
23162306a36Sopenharmony_ci */
23262306a36Sopenharmony_cistatic int __ei_close(struct net_device *dev)
23362306a36Sopenharmony_ci{
23462306a36Sopenharmony_ci	struct ei_device *ei_local = netdev_priv(dev);
23562306a36Sopenharmony_ci	unsigned long flags;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	/*
23862306a36Sopenharmony_ci	 *	Hold the page lock during close
23962306a36Sopenharmony_ci	 */
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	spin_lock_irqsave(&ei_local->page_lock, flags);
24262306a36Sopenharmony_ci	__NS8390_init(dev, 0);
24362306a36Sopenharmony_ci	spin_unlock_irqrestore(&ei_local->page_lock, flags);
24462306a36Sopenharmony_ci	netif_stop_queue(dev);
24562306a36Sopenharmony_ci	return 0;
24662306a36Sopenharmony_ci}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci/**
24962306a36Sopenharmony_ci * ei_tx_timeout - handle transmit time out condition
25062306a36Sopenharmony_ci * @dev: network device which has apparently fallen asleep
25162306a36Sopenharmony_ci *
25262306a36Sopenharmony_ci * Called by kernel when device never acknowledges a transmit has
25362306a36Sopenharmony_ci * completed (or failed) - i.e. never posted a Tx related interrupt.
25462306a36Sopenharmony_ci */
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_cistatic void __ei_tx_timeout(struct net_device *dev, unsigned int txqueue)
25762306a36Sopenharmony_ci{
25862306a36Sopenharmony_ci	unsigned long e8390_base = dev->base_addr;
25962306a36Sopenharmony_ci	struct ei_device *ei_local = netdev_priv(dev);
26062306a36Sopenharmony_ci	int txsr, isr, tickssofar = jiffies - dev_trans_start(dev);
26162306a36Sopenharmony_ci	unsigned long flags;
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	dev->stats.tx_errors++;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	spin_lock_irqsave(&ei_local->page_lock, flags);
26662306a36Sopenharmony_ci	txsr = ei_inb(e8390_base+EN0_TSR);
26762306a36Sopenharmony_ci	isr = ei_inb(e8390_base+EN0_ISR);
26862306a36Sopenharmony_ci	spin_unlock_irqrestore(&ei_local->page_lock, flags);
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	netdev_dbg(dev, "Tx timed out, %s TSR=%#2x, ISR=%#2x, t=%d\n",
27162306a36Sopenharmony_ci		   (txsr & ENTSR_ABT) ? "excess collisions." :
27262306a36Sopenharmony_ci		   (isr) ? "lost interrupt?" : "cable problem?",
27362306a36Sopenharmony_ci		   txsr, isr, tickssofar);
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	if (!isr && !dev->stats.tx_packets) {
27662306a36Sopenharmony_ci		/* The 8390 probably hasn't gotten on the cable yet. */
27762306a36Sopenharmony_ci		ei_local->interface_num ^= 1;   /* Try a different xcvr.  */
27862306a36Sopenharmony_ci	}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	/* Ugly but a reset can be slow, yet must be protected */
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	disable_irq_nosync_lockdep(dev->irq);
28362306a36Sopenharmony_ci	spin_lock(&ei_local->page_lock);
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	/* Try to restart the card.  Perhaps the user has fixed something. */
28662306a36Sopenharmony_ci	ei_reset_8390(dev);
28762306a36Sopenharmony_ci	__NS8390_init(dev, 1);
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	spin_unlock(&ei_local->page_lock);
29062306a36Sopenharmony_ci	enable_irq_lockdep(dev->irq);
29162306a36Sopenharmony_ci	netif_wake_queue(dev);
29262306a36Sopenharmony_ci}
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci/**
29562306a36Sopenharmony_ci * ei_start_xmit - begin packet transmission
29662306a36Sopenharmony_ci * @skb: packet to be sent
29762306a36Sopenharmony_ci * @dev: network device to which packet is sent
29862306a36Sopenharmony_ci *
29962306a36Sopenharmony_ci * Sends a packet to an 8390 network device.
30062306a36Sopenharmony_ci */
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_cistatic netdev_tx_t __ei_start_xmit(struct sk_buff *skb,
30362306a36Sopenharmony_ci				   struct net_device *dev)
30462306a36Sopenharmony_ci{
30562306a36Sopenharmony_ci	unsigned long e8390_base = dev->base_addr;
30662306a36Sopenharmony_ci	struct ei_device *ei_local = netdev_priv(dev);
30762306a36Sopenharmony_ci	int send_length = skb->len, output_page;
30862306a36Sopenharmony_ci	unsigned long flags;
30962306a36Sopenharmony_ci	char buf[ETH_ZLEN];
31062306a36Sopenharmony_ci	char *data = skb->data;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	if (skb->len < ETH_ZLEN) {
31362306a36Sopenharmony_ci		memset(buf, 0, ETH_ZLEN);	/* more efficient than doing just the needed bits */
31462306a36Sopenharmony_ci		memcpy(buf, data, skb->len);
31562306a36Sopenharmony_ci		send_length = ETH_ZLEN;
31662306a36Sopenharmony_ci		data = buf;
31762306a36Sopenharmony_ci	}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	/* Mask interrupts from the ethercard.
32062306a36Sopenharmony_ci	   SMP: We have to grab the lock here otherwise the IRQ handler
32162306a36Sopenharmony_ci	   on another CPU can flip window and race the IRQ mask set. We end
32262306a36Sopenharmony_ci	   up trashing the mcast filter not disabling irqs if we don't lock */
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	spin_lock_irqsave(&ei_local->page_lock, flags);
32562306a36Sopenharmony_ci	ei_outb_p(0x00, e8390_base + EN0_IMR);
32662306a36Sopenharmony_ci	spin_unlock_irqrestore(&ei_local->page_lock, flags);
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	/*
33062306a36Sopenharmony_ci	 *	Slow phase with lock held.
33162306a36Sopenharmony_ci	 */
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	disable_irq_nosync_lockdep_irqsave(dev->irq, &flags);
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	spin_lock(&ei_local->page_lock);
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	ei_local->irqlock = 1;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	/*
34062306a36Sopenharmony_ci	 * We have two Tx slots available for use. Find the first free
34162306a36Sopenharmony_ci	 * slot, and then perform some sanity checks. With two Tx bufs,
34262306a36Sopenharmony_ci	 * you get very close to transmitting back-to-back packets. With
34362306a36Sopenharmony_ci	 * only one Tx buf, the transmitter sits idle while you reload the
34462306a36Sopenharmony_ci	 * card, leaving a substantial gap between each transmitted packet.
34562306a36Sopenharmony_ci	 */
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	if (ei_local->tx1 == 0) {
34862306a36Sopenharmony_ci		output_page = ei_local->tx_start_page;
34962306a36Sopenharmony_ci		ei_local->tx1 = send_length;
35062306a36Sopenharmony_ci		if ((netif_msg_tx_queued(ei_local)) &&
35162306a36Sopenharmony_ci		    ei_local->tx2 > 0)
35262306a36Sopenharmony_ci			netdev_dbg(dev,
35362306a36Sopenharmony_ci				   "idle transmitter tx2=%d, lasttx=%d, txing=%d\n",
35462306a36Sopenharmony_ci				   ei_local->tx2, ei_local->lasttx, ei_local->txing);
35562306a36Sopenharmony_ci	} else if (ei_local->tx2 == 0) {
35662306a36Sopenharmony_ci		output_page = ei_local->tx_start_page + TX_PAGES/2;
35762306a36Sopenharmony_ci		ei_local->tx2 = send_length;
35862306a36Sopenharmony_ci		if ((netif_msg_tx_queued(ei_local)) &&
35962306a36Sopenharmony_ci		    ei_local->tx1 > 0)
36062306a36Sopenharmony_ci			netdev_dbg(dev,
36162306a36Sopenharmony_ci				   "idle transmitter, tx1=%d, lasttx=%d, txing=%d\n",
36262306a36Sopenharmony_ci				   ei_local->tx1, ei_local->lasttx, ei_local->txing);
36362306a36Sopenharmony_ci	} else {			/* We should never get here. */
36462306a36Sopenharmony_ci		netif_dbg(ei_local, tx_err, dev,
36562306a36Sopenharmony_ci			  "No Tx buffers free! tx1=%d tx2=%d last=%d\n",
36662306a36Sopenharmony_ci			  ei_local->tx1, ei_local->tx2, ei_local->lasttx);
36762306a36Sopenharmony_ci		ei_local->irqlock = 0;
36862306a36Sopenharmony_ci		netif_stop_queue(dev);
36962306a36Sopenharmony_ci		ei_outb_p(ENISR_ALL, e8390_base + EN0_IMR);
37062306a36Sopenharmony_ci		spin_unlock(&ei_local->page_lock);
37162306a36Sopenharmony_ci		enable_irq_lockdep_irqrestore(dev->irq, &flags);
37262306a36Sopenharmony_ci		dev->stats.tx_errors++;
37362306a36Sopenharmony_ci		return NETDEV_TX_BUSY;
37462306a36Sopenharmony_ci	}
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	/*
37762306a36Sopenharmony_ci	 * Okay, now upload the packet and trigger a send if the transmitter
37862306a36Sopenharmony_ci	 * isn't already sending. If it is busy, the interrupt handler will
37962306a36Sopenharmony_ci	 * trigger the send later, upon receiving a Tx done interrupt.
38062306a36Sopenharmony_ci	 */
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	ei_block_output(dev, send_length, data, output_page);
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	if (!ei_local->txing) {
38562306a36Sopenharmony_ci		ei_local->txing = 1;
38662306a36Sopenharmony_ci		NS8390_trigger_send(dev, send_length, output_page);
38762306a36Sopenharmony_ci		if (output_page == ei_local->tx_start_page) {
38862306a36Sopenharmony_ci			ei_local->tx1 = -1;
38962306a36Sopenharmony_ci			ei_local->lasttx = -1;
39062306a36Sopenharmony_ci		} else {
39162306a36Sopenharmony_ci			ei_local->tx2 = -1;
39262306a36Sopenharmony_ci			ei_local->lasttx = -2;
39362306a36Sopenharmony_ci		}
39462306a36Sopenharmony_ci	} else
39562306a36Sopenharmony_ci		ei_local->txqueue++;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	if (ei_local->tx1 && ei_local->tx2)
39862306a36Sopenharmony_ci		netif_stop_queue(dev);
39962306a36Sopenharmony_ci	else
40062306a36Sopenharmony_ci		netif_start_queue(dev);
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	/* Turn 8390 interrupts back on. */
40362306a36Sopenharmony_ci	ei_local->irqlock = 0;
40462306a36Sopenharmony_ci	ei_outb_p(ENISR_ALL, e8390_base + EN0_IMR);
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	spin_unlock(&ei_local->page_lock);
40762306a36Sopenharmony_ci	enable_irq_lockdep_irqrestore(dev->irq, &flags);
40862306a36Sopenharmony_ci	skb_tx_timestamp(skb);
40962306a36Sopenharmony_ci	dev_consume_skb_any(skb);
41062306a36Sopenharmony_ci	dev->stats.tx_bytes += send_length;
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	return NETDEV_TX_OK;
41362306a36Sopenharmony_ci}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci/**
41662306a36Sopenharmony_ci * ei_interrupt - handle the interrupts from an 8390
41762306a36Sopenharmony_ci * @irq: interrupt number
41862306a36Sopenharmony_ci * @dev_id: a pointer to the net_device
41962306a36Sopenharmony_ci *
42062306a36Sopenharmony_ci * Handle the ether interface interrupts. We pull packets from
42162306a36Sopenharmony_ci * the 8390 via the card specific functions and fire them at the networking
42262306a36Sopenharmony_ci * stack. We also handle transmit completions and wake the transmit path if
42362306a36Sopenharmony_ci * necessary. We also update the counters and do other housekeeping as
42462306a36Sopenharmony_ci * needed.
42562306a36Sopenharmony_ci */
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_cistatic irqreturn_t __ei_interrupt(int irq, void *dev_id)
42862306a36Sopenharmony_ci{
42962306a36Sopenharmony_ci	struct net_device *dev = dev_id;
43062306a36Sopenharmony_ci	unsigned long e8390_base = dev->base_addr;
43162306a36Sopenharmony_ci	int interrupts, nr_serviced = 0;
43262306a36Sopenharmony_ci	struct ei_device *ei_local = netdev_priv(dev);
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	/*
43562306a36Sopenharmony_ci	 *	Protect the irq test too.
43662306a36Sopenharmony_ci	 */
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	spin_lock(&ei_local->page_lock);
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	if (ei_local->irqlock) {
44162306a36Sopenharmony_ci		/*
44262306a36Sopenharmony_ci		 * This might just be an interrupt for a PCI device sharing
44362306a36Sopenharmony_ci		 * this line
44462306a36Sopenharmony_ci		 */
44562306a36Sopenharmony_ci		netdev_err(dev, "Interrupted while interrupts are masked! isr=%#2x imr=%#2x\n",
44662306a36Sopenharmony_ci			   ei_inb_p(e8390_base + EN0_ISR),
44762306a36Sopenharmony_ci			   ei_inb_p(e8390_base + EN0_IMR));
44862306a36Sopenharmony_ci		spin_unlock(&ei_local->page_lock);
44962306a36Sopenharmony_ci		return IRQ_NONE;
45062306a36Sopenharmony_ci	}
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	/* Change to page 0 and read the intr status reg. */
45362306a36Sopenharmony_ci	ei_outb_p(E8390_NODMA+E8390_PAGE0, e8390_base + E8390_CMD);
45462306a36Sopenharmony_ci	netif_dbg(ei_local, intr, dev, "interrupt(isr=%#2.2x)\n",
45562306a36Sopenharmony_ci		  ei_inb_p(e8390_base + EN0_ISR));
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	/* !!Assumption!! -- we stay in page 0.	 Don't break this. */
45862306a36Sopenharmony_ci	while ((interrupts = ei_inb_p(e8390_base + EN0_ISR)) != 0 &&
45962306a36Sopenharmony_ci	       ++nr_serviced < MAX_SERVICE) {
46062306a36Sopenharmony_ci		if (!netif_running(dev)) {
46162306a36Sopenharmony_ci			netdev_warn(dev, "interrupt from stopped card\n");
46262306a36Sopenharmony_ci			/* rmk - acknowledge the interrupts */
46362306a36Sopenharmony_ci			ei_outb_p(interrupts, e8390_base + EN0_ISR);
46462306a36Sopenharmony_ci			interrupts = 0;
46562306a36Sopenharmony_ci			break;
46662306a36Sopenharmony_ci		}
46762306a36Sopenharmony_ci		if (interrupts & ENISR_OVER)
46862306a36Sopenharmony_ci			ei_rx_overrun(dev);
46962306a36Sopenharmony_ci		else if (interrupts & (ENISR_RX+ENISR_RX_ERR)) {
47062306a36Sopenharmony_ci			/* Got a good (?) packet. */
47162306a36Sopenharmony_ci			ei_receive(dev);
47262306a36Sopenharmony_ci		}
47362306a36Sopenharmony_ci		/* Push the next to-transmit packet through. */
47462306a36Sopenharmony_ci		if (interrupts & ENISR_TX)
47562306a36Sopenharmony_ci			ei_tx_intr(dev);
47662306a36Sopenharmony_ci		else if (interrupts & ENISR_TX_ERR)
47762306a36Sopenharmony_ci			ei_tx_err(dev);
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci		if (interrupts & ENISR_COUNTERS) {
48062306a36Sopenharmony_ci			dev->stats.rx_frame_errors += ei_inb_p(e8390_base + EN0_COUNTER0);
48162306a36Sopenharmony_ci			dev->stats.rx_crc_errors   += ei_inb_p(e8390_base + EN0_COUNTER1);
48262306a36Sopenharmony_ci			dev->stats.rx_missed_errors += ei_inb_p(e8390_base + EN0_COUNTER2);
48362306a36Sopenharmony_ci			ei_outb_p(ENISR_COUNTERS, e8390_base + EN0_ISR); /* Ack intr. */
48462306a36Sopenharmony_ci		}
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci		/* Ignore any RDC interrupts that make it back to here. */
48762306a36Sopenharmony_ci		if (interrupts & ENISR_RDC)
48862306a36Sopenharmony_ci			ei_outb_p(ENISR_RDC, e8390_base + EN0_ISR);
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci		ei_outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base + E8390_CMD);
49162306a36Sopenharmony_ci	}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	if (interrupts && (netif_msg_intr(ei_local))) {
49462306a36Sopenharmony_ci		ei_outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base + E8390_CMD);
49562306a36Sopenharmony_ci		if (nr_serviced >= MAX_SERVICE) {
49662306a36Sopenharmony_ci			/* 0xFF is valid for a card removal */
49762306a36Sopenharmony_ci			if (interrupts != 0xFF)
49862306a36Sopenharmony_ci				netdev_warn(dev, "Too much work at interrupt, status %#2.2x\n",
49962306a36Sopenharmony_ci					    interrupts);
50062306a36Sopenharmony_ci			ei_outb_p(ENISR_ALL, e8390_base + EN0_ISR); /* Ack. most intrs. */
50162306a36Sopenharmony_ci		} else {
50262306a36Sopenharmony_ci			netdev_warn(dev, "unknown interrupt %#2x\n", interrupts);
50362306a36Sopenharmony_ci			ei_outb_p(0xff, e8390_base + EN0_ISR); /* Ack. all intrs. */
50462306a36Sopenharmony_ci		}
50562306a36Sopenharmony_ci	}
50662306a36Sopenharmony_ci	spin_unlock(&ei_local->page_lock);
50762306a36Sopenharmony_ci	return IRQ_RETVAL(nr_serviced > 0);
50862306a36Sopenharmony_ci}
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
51162306a36Sopenharmony_cistatic void __ei_poll(struct net_device *dev)
51262306a36Sopenharmony_ci{
51362306a36Sopenharmony_ci	disable_irq(dev->irq);
51462306a36Sopenharmony_ci	__ei_interrupt(dev->irq, dev);
51562306a36Sopenharmony_ci	enable_irq(dev->irq);
51662306a36Sopenharmony_ci}
51762306a36Sopenharmony_ci#endif
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci/**
52062306a36Sopenharmony_ci * ei_tx_err - handle transmitter error
52162306a36Sopenharmony_ci * @dev: network device which threw the exception
52262306a36Sopenharmony_ci *
52362306a36Sopenharmony_ci * A transmitter error has happened. Most likely excess collisions (which
52462306a36Sopenharmony_ci * is a fairly normal condition). If the error is one where the Tx will
52562306a36Sopenharmony_ci * have been aborted, we try and send another one right away, instead of
52662306a36Sopenharmony_ci * letting the failed packet sit and collect dust in the Tx buffer. This
52762306a36Sopenharmony_ci * is a much better solution as it avoids kernel based Tx timeouts, and
52862306a36Sopenharmony_ci * an unnecessary card reset.
52962306a36Sopenharmony_ci *
53062306a36Sopenharmony_ci * Called with lock held.
53162306a36Sopenharmony_ci */
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_cistatic void ei_tx_err(struct net_device *dev)
53462306a36Sopenharmony_ci{
53562306a36Sopenharmony_ci	unsigned long e8390_base = dev->base_addr;
53662306a36Sopenharmony_ci	/* ei_local is used on some platforms via the EI_SHIFT macro */
53762306a36Sopenharmony_ci	struct ei_device *ei_local __maybe_unused = netdev_priv(dev);
53862306a36Sopenharmony_ci	unsigned char txsr = ei_inb_p(e8390_base+EN0_TSR);
53962306a36Sopenharmony_ci	unsigned char tx_was_aborted = txsr & (ENTSR_ABT+ENTSR_FU);
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci#ifdef VERBOSE_ERROR_DUMP
54262306a36Sopenharmony_ci	netdev_dbg(dev, "transmitter error (%#2x):", txsr);
54362306a36Sopenharmony_ci	if (txsr & ENTSR_ABT)
54462306a36Sopenharmony_ci		pr_cont(" excess-collisions ");
54562306a36Sopenharmony_ci	if (txsr & ENTSR_ND)
54662306a36Sopenharmony_ci		pr_cont(" non-deferral ");
54762306a36Sopenharmony_ci	if (txsr & ENTSR_CRS)
54862306a36Sopenharmony_ci		pr_cont(" lost-carrier ");
54962306a36Sopenharmony_ci	if (txsr & ENTSR_FU)
55062306a36Sopenharmony_ci		pr_cont(" FIFO-underrun ");
55162306a36Sopenharmony_ci	if (txsr & ENTSR_CDH)
55262306a36Sopenharmony_ci		pr_cont(" lost-heartbeat ");
55362306a36Sopenharmony_ci	pr_cont("\n");
55462306a36Sopenharmony_ci#endif
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	ei_outb_p(ENISR_TX_ERR, e8390_base + EN0_ISR); /* Ack intr. */
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	if (tx_was_aborted)
55962306a36Sopenharmony_ci		ei_tx_intr(dev);
56062306a36Sopenharmony_ci	else {
56162306a36Sopenharmony_ci		dev->stats.tx_errors++;
56262306a36Sopenharmony_ci		if (txsr & ENTSR_CRS)
56362306a36Sopenharmony_ci			dev->stats.tx_carrier_errors++;
56462306a36Sopenharmony_ci		if (txsr & ENTSR_CDH)
56562306a36Sopenharmony_ci			dev->stats.tx_heartbeat_errors++;
56662306a36Sopenharmony_ci		if (txsr & ENTSR_OWC)
56762306a36Sopenharmony_ci			dev->stats.tx_window_errors++;
56862306a36Sopenharmony_ci	}
56962306a36Sopenharmony_ci}
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci/**
57262306a36Sopenharmony_ci * ei_tx_intr - transmit interrupt handler
57362306a36Sopenharmony_ci * @dev: network device for which tx intr is handled
57462306a36Sopenharmony_ci *
57562306a36Sopenharmony_ci * We have finished a transmit: check for errors and then trigger the next
57662306a36Sopenharmony_ci * packet to be sent. Called with lock held.
57762306a36Sopenharmony_ci */
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_cistatic void ei_tx_intr(struct net_device *dev)
58062306a36Sopenharmony_ci{
58162306a36Sopenharmony_ci	unsigned long e8390_base = dev->base_addr;
58262306a36Sopenharmony_ci	struct ei_device *ei_local = netdev_priv(dev);
58362306a36Sopenharmony_ci	int status = ei_inb(e8390_base + EN0_TSR);
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	ei_outb_p(ENISR_TX, e8390_base + EN0_ISR); /* Ack intr. */
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	/*
58862306a36Sopenharmony_ci	 * There are two Tx buffers, see which one finished, and trigger
58962306a36Sopenharmony_ci	 * the send of another one if it exists.
59062306a36Sopenharmony_ci	 */
59162306a36Sopenharmony_ci	ei_local->txqueue--;
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	if (ei_local->tx1 < 0) {
59462306a36Sopenharmony_ci		if (ei_local->lasttx != 1 && ei_local->lasttx != -1)
59562306a36Sopenharmony_ci			pr_err("%s: bogus last_tx_buffer %d, tx1=%d\n",
59662306a36Sopenharmony_ci			       ei_local->name, ei_local->lasttx, ei_local->tx1);
59762306a36Sopenharmony_ci		ei_local->tx1 = 0;
59862306a36Sopenharmony_ci		if (ei_local->tx2 > 0) {
59962306a36Sopenharmony_ci			ei_local->txing = 1;
60062306a36Sopenharmony_ci			NS8390_trigger_send(dev, ei_local->tx2, ei_local->tx_start_page + 6);
60162306a36Sopenharmony_ci			netif_trans_update(dev);
60262306a36Sopenharmony_ci			ei_local->tx2 = -1;
60362306a36Sopenharmony_ci			ei_local->lasttx = 2;
60462306a36Sopenharmony_ci		} else {
60562306a36Sopenharmony_ci			ei_local->lasttx = 20;
60662306a36Sopenharmony_ci			ei_local->txing = 0;
60762306a36Sopenharmony_ci		}
60862306a36Sopenharmony_ci	} else if (ei_local->tx2 < 0) {
60962306a36Sopenharmony_ci		if (ei_local->lasttx != 2  &&  ei_local->lasttx != -2)
61062306a36Sopenharmony_ci			pr_err("%s: bogus last_tx_buffer %d, tx2=%d\n",
61162306a36Sopenharmony_ci			       ei_local->name, ei_local->lasttx, ei_local->tx2);
61262306a36Sopenharmony_ci		ei_local->tx2 = 0;
61362306a36Sopenharmony_ci		if (ei_local->tx1 > 0) {
61462306a36Sopenharmony_ci			ei_local->txing = 1;
61562306a36Sopenharmony_ci			NS8390_trigger_send(dev, ei_local->tx1, ei_local->tx_start_page);
61662306a36Sopenharmony_ci			netif_trans_update(dev);
61762306a36Sopenharmony_ci			ei_local->tx1 = -1;
61862306a36Sopenharmony_ci			ei_local->lasttx = 1;
61962306a36Sopenharmony_ci		} else {
62062306a36Sopenharmony_ci			ei_local->lasttx = 10;
62162306a36Sopenharmony_ci			ei_local->txing = 0;
62262306a36Sopenharmony_ci		}
62362306a36Sopenharmony_ci	} /* else
62462306a36Sopenharmony_ci		netdev_warn(dev, "unexpected TX-done interrupt, lasttx=%d\n",
62562306a36Sopenharmony_ci			    ei_local->lasttx);
62662306a36Sopenharmony_ci*/
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	/* Minimize Tx latency: update the statistics after we restart TXing. */
62962306a36Sopenharmony_ci	if (status & ENTSR_COL)
63062306a36Sopenharmony_ci		dev->stats.collisions++;
63162306a36Sopenharmony_ci	if (status & ENTSR_PTX)
63262306a36Sopenharmony_ci		dev->stats.tx_packets++;
63362306a36Sopenharmony_ci	else {
63462306a36Sopenharmony_ci		dev->stats.tx_errors++;
63562306a36Sopenharmony_ci		if (status & ENTSR_ABT) {
63662306a36Sopenharmony_ci			dev->stats.tx_aborted_errors++;
63762306a36Sopenharmony_ci			dev->stats.collisions += 16;
63862306a36Sopenharmony_ci		}
63962306a36Sopenharmony_ci		if (status & ENTSR_CRS)
64062306a36Sopenharmony_ci			dev->stats.tx_carrier_errors++;
64162306a36Sopenharmony_ci		if (status & ENTSR_FU)
64262306a36Sopenharmony_ci			dev->stats.tx_fifo_errors++;
64362306a36Sopenharmony_ci		if (status & ENTSR_CDH)
64462306a36Sopenharmony_ci			dev->stats.tx_heartbeat_errors++;
64562306a36Sopenharmony_ci		if (status & ENTSR_OWC)
64662306a36Sopenharmony_ci			dev->stats.tx_window_errors++;
64762306a36Sopenharmony_ci	}
64862306a36Sopenharmony_ci	netif_wake_queue(dev);
64962306a36Sopenharmony_ci}
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci/**
65262306a36Sopenharmony_ci * ei_receive - receive some packets
65362306a36Sopenharmony_ci * @dev: network device with which receive will be run
65462306a36Sopenharmony_ci *
65562306a36Sopenharmony_ci * We have a good packet(s), get it/them out of the buffers.
65662306a36Sopenharmony_ci * Called with lock held.
65762306a36Sopenharmony_ci */
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_cistatic void ei_receive(struct net_device *dev)
66062306a36Sopenharmony_ci{
66162306a36Sopenharmony_ci	unsigned long e8390_base = dev->base_addr;
66262306a36Sopenharmony_ci	struct ei_device *ei_local = netdev_priv(dev);
66362306a36Sopenharmony_ci	unsigned char rxing_page, this_frame, next_frame;
66462306a36Sopenharmony_ci	unsigned short current_offset;
66562306a36Sopenharmony_ci	int rx_pkt_count = 0;
66662306a36Sopenharmony_ci	struct e8390_pkt_hdr rx_frame;
66762306a36Sopenharmony_ci	int num_rx_pages = ei_local->stop_page-ei_local->rx_start_page;
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	while (++rx_pkt_count < 10) {
67062306a36Sopenharmony_ci		int pkt_len, pkt_stat;
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci		/* Get the rx page (incoming packet pointer). */
67362306a36Sopenharmony_ci		ei_outb_p(E8390_NODMA+E8390_PAGE1, e8390_base + E8390_CMD);
67462306a36Sopenharmony_ci		rxing_page = ei_inb_p(e8390_base + EN1_CURPAG);
67562306a36Sopenharmony_ci		ei_outb_p(E8390_NODMA+E8390_PAGE0, e8390_base + E8390_CMD);
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci		/* Remove one frame from the ring.  Boundary is always a page behind. */
67862306a36Sopenharmony_ci		this_frame = ei_inb_p(e8390_base + EN0_BOUNDARY) + 1;
67962306a36Sopenharmony_ci		if (this_frame >= ei_local->stop_page)
68062306a36Sopenharmony_ci			this_frame = ei_local->rx_start_page;
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci		/* Someday we'll omit the previous, iff we never get this message.
68362306a36Sopenharmony_ci		   (There is at least one clone claimed to have a problem.)
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci		   Keep quiet if it looks like a card removal. One problem here
68662306a36Sopenharmony_ci		   is that some clones crash in roughly the same way.
68762306a36Sopenharmony_ci		 */
68862306a36Sopenharmony_ci		if ((netif_msg_rx_status(ei_local)) &&
68962306a36Sopenharmony_ci		    this_frame != ei_local->current_page &&
69062306a36Sopenharmony_ci		    (this_frame != 0x0 || rxing_page != 0xFF))
69162306a36Sopenharmony_ci			netdev_err(dev,
69262306a36Sopenharmony_ci				   "mismatched read page pointers %2x vs %2x\n",
69362306a36Sopenharmony_ci				   this_frame, ei_local->current_page);
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci		if (this_frame == rxing_page)	/* Read all the frames? */
69662306a36Sopenharmony_ci			break;				/* Done for now */
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci		current_offset = this_frame << 8;
69962306a36Sopenharmony_ci		ei_get_8390_hdr(dev, &rx_frame, this_frame);
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci		pkt_len = rx_frame.count - sizeof(struct e8390_pkt_hdr);
70262306a36Sopenharmony_ci		pkt_stat = rx_frame.status;
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci		next_frame = this_frame + 1 + ((pkt_len+4)>>8);
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci		/* Check for bogosity warned by 3c503 book: the status byte is never
70762306a36Sopenharmony_ci		   written.  This happened a lot during testing! This code should be
70862306a36Sopenharmony_ci		   cleaned up someday. */
70962306a36Sopenharmony_ci		if (rx_frame.next != next_frame &&
71062306a36Sopenharmony_ci		    rx_frame.next != next_frame + 1 &&
71162306a36Sopenharmony_ci		    rx_frame.next != next_frame - num_rx_pages &&
71262306a36Sopenharmony_ci		    rx_frame.next != next_frame + 1 - num_rx_pages) {
71362306a36Sopenharmony_ci			ei_local->current_page = rxing_page;
71462306a36Sopenharmony_ci			ei_outb(ei_local->current_page-1, e8390_base+EN0_BOUNDARY);
71562306a36Sopenharmony_ci			dev->stats.rx_errors++;
71662306a36Sopenharmony_ci			continue;
71762306a36Sopenharmony_ci		}
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci		if (pkt_len < 60  ||  pkt_len > 1518) {
72062306a36Sopenharmony_ci			netif_dbg(ei_local, rx_status, dev,
72162306a36Sopenharmony_ci				  "bogus packet size: %d, status=%#2x nxpg=%#2x\n",
72262306a36Sopenharmony_ci				  rx_frame.count, rx_frame.status,
72362306a36Sopenharmony_ci				  rx_frame.next);
72462306a36Sopenharmony_ci			dev->stats.rx_errors++;
72562306a36Sopenharmony_ci			dev->stats.rx_length_errors++;
72662306a36Sopenharmony_ci		} else if ((pkt_stat & 0x0F) == ENRSR_RXOK) {
72762306a36Sopenharmony_ci			struct sk_buff *skb;
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci			skb = netdev_alloc_skb(dev, pkt_len + 2);
73062306a36Sopenharmony_ci			if (skb == NULL) {
73162306a36Sopenharmony_ci				netif_err(ei_local, rx_err, dev,
73262306a36Sopenharmony_ci					  "Couldn't allocate a sk_buff of size %d\n",
73362306a36Sopenharmony_ci					  pkt_len);
73462306a36Sopenharmony_ci				dev->stats.rx_dropped++;
73562306a36Sopenharmony_ci				break;
73662306a36Sopenharmony_ci			} else {
73762306a36Sopenharmony_ci				skb_reserve(skb, 2);	/* IP headers on 16 byte boundaries */
73862306a36Sopenharmony_ci				skb_put(skb, pkt_len);	/* Make room */
73962306a36Sopenharmony_ci				ei_block_input(dev, pkt_len, skb, current_offset + sizeof(rx_frame));
74062306a36Sopenharmony_ci				skb->protocol = eth_type_trans(skb, dev);
74162306a36Sopenharmony_ci				if (!skb_defer_rx_timestamp(skb))
74262306a36Sopenharmony_ci					netif_rx(skb);
74362306a36Sopenharmony_ci				dev->stats.rx_packets++;
74462306a36Sopenharmony_ci				dev->stats.rx_bytes += pkt_len;
74562306a36Sopenharmony_ci				if (pkt_stat & ENRSR_PHY)
74662306a36Sopenharmony_ci					dev->stats.multicast++;
74762306a36Sopenharmony_ci			}
74862306a36Sopenharmony_ci		} else {
74962306a36Sopenharmony_ci			netif_err(ei_local, rx_err, dev,
75062306a36Sopenharmony_ci				  "bogus packet: status=%#2x nxpg=%#2x size=%d\n",
75162306a36Sopenharmony_ci				  rx_frame.status, rx_frame.next,
75262306a36Sopenharmony_ci				  rx_frame.count);
75362306a36Sopenharmony_ci			dev->stats.rx_errors++;
75462306a36Sopenharmony_ci			/* NB: The NIC counts CRC, frame and missed errors. */
75562306a36Sopenharmony_ci			if (pkt_stat & ENRSR_FO)
75662306a36Sopenharmony_ci				dev->stats.rx_fifo_errors++;
75762306a36Sopenharmony_ci		}
75862306a36Sopenharmony_ci		next_frame = rx_frame.next;
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci		/* This _should_ never happen: it's here for avoiding bad clones. */
76162306a36Sopenharmony_ci		if (next_frame >= ei_local->stop_page) {
76262306a36Sopenharmony_ci			netdev_notice(dev, "next frame inconsistency, %#2x\n",
76362306a36Sopenharmony_ci				      next_frame);
76462306a36Sopenharmony_ci			next_frame = ei_local->rx_start_page;
76562306a36Sopenharmony_ci		}
76662306a36Sopenharmony_ci		ei_local->current_page = next_frame;
76762306a36Sopenharmony_ci		ei_outb_p(next_frame-1, e8390_base+EN0_BOUNDARY);
76862306a36Sopenharmony_ci	}
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci	/* We used to also ack ENISR_OVER here, but that would sometimes mask
77162306a36Sopenharmony_ci	   a real overrun, leaving the 8390 in a stopped state with rec'vr off. */
77262306a36Sopenharmony_ci	ei_outb_p(ENISR_RX+ENISR_RX_ERR, e8390_base+EN0_ISR);
77362306a36Sopenharmony_ci}
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci/**
77662306a36Sopenharmony_ci * ei_rx_overrun - handle receiver overrun
77762306a36Sopenharmony_ci * @dev: network device which threw exception
77862306a36Sopenharmony_ci *
77962306a36Sopenharmony_ci * We have a receiver overrun: we have to kick the 8390 to get it started
78062306a36Sopenharmony_ci * again. Problem is that you have to kick it exactly as NS prescribes in
78162306a36Sopenharmony_ci * the updated datasheets, or "the NIC may act in an unpredictable manner."
78262306a36Sopenharmony_ci * This includes causing "the NIC to defer indefinitely when it is stopped
78362306a36Sopenharmony_ci * on a busy network."  Ugh.
78462306a36Sopenharmony_ci * Called with lock held. Don't call this with the interrupts off or your
78562306a36Sopenharmony_ci * computer will hate you - it takes 10ms or so.
78662306a36Sopenharmony_ci */
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_cistatic void ei_rx_overrun(struct net_device *dev)
78962306a36Sopenharmony_ci{
79062306a36Sopenharmony_ci	unsigned long e8390_base = dev->base_addr;
79162306a36Sopenharmony_ci	unsigned char was_txing, must_resend = 0;
79262306a36Sopenharmony_ci	/* ei_local is used on some platforms via the EI_SHIFT macro */
79362306a36Sopenharmony_ci	struct ei_device *ei_local __maybe_unused = netdev_priv(dev);
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci	/*
79662306a36Sopenharmony_ci	 * Record whether a Tx was in progress and then issue the
79762306a36Sopenharmony_ci	 * stop command.
79862306a36Sopenharmony_ci	 */
79962306a36Sopenharmony_ci	was_txing = ei_inb_p(e8390_base+E8390_CMD) & E8390_TRANS;
80062306a36Sopenharmony_ci	ei_outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base+E8390_CMD);
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	netif_dbg(ei_local, rx_err, dev, "Receiver overrun\n");
80362306a36Sopenharmony_ci	dev->stats.rx_over_errors++;
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci	/*
80662306a36Sopenharmony_ci	 * Wait a full Tx time (1.2ms) + some guard time, NS says 1.6ms total.
80762306a36Sopenharmony_ci	 * Early datasheets said to poll the reset bit, but now they say that
80862306a36Sopenharmony_ci	 * it "is not a reliable indicator and subsequently should be ignored."
80962306a36Sopenharmony_ci	 * We wait at least 10ms.
81062306a36Sopenharmony_ci	 */
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci	mdelay(10);
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci	/*
81562306a36Sopenharmony_ci	 * Reset RBCR[01] back to zero as per magic incantation.
81662306a36Sopenharmony_ci	 */
81762306a36Sopenharmony_ci	ei_outb_p(0x00, e8390_base+EN0_RCNTLO);
81862306a36Sopenharmony_ci	ei_outb_p(0x00, e8390_base+EN0_RCNTHI);
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	/*
82162306a36Sopenharmony_ci	 * See if any Tx was interrupted or not. According to NS, this
82262306a36Sopenharmony_ci	 * step is vital, and skipping it will cause no end of havoc.
82362306a36Sopenharmony_ci	 */
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	if (was_txing) {
82662306a36Sopenharmony_ci		unsigned char tx_completed = ei_inb_p(e8390_base+EN0_ISR) & (ENISR_TX+ENISR_TX_ERR);
82762306a36Sopenharmony_ci		if (!tx_completed)
82862306a36Sopenharmony_ci			must_resend = 1;
82962306a36Sopenharmony_ci	}
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	/*
83262306a36Sopenharmony_ci	 * Have to enter loopback mode and then restart the NIC before
83362306a36Sopenharmony_ci	 * you are allowed to slurp packets up off the ring.
83462306a36Sopenharmony_ci	 */
83562306a36Sopenharmony_ci	ei_outb_p(E8390_TXOFF, e8390_base + EN0_TXCR);
83662306a36Sopenharmony_ci	ei_outb_p(E8390_NODMA + E8390_PAGE0 + E8390_START, e8390_base + E8390_CMD);
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	/*
83962306a36Sopenharmony_ci	 * Clear the Rx ring of all the debris, and ack the interrupt.
84062306a36Sopenharmony_ci	 */
84162306a36Sopenharmony_ci	ei_receive(dev);
84262306a36Sopenharmony_ci	ei_outb_p(ENISR_OVER, e8390_base+EN0_ISR);
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	/*
84562306a36Sopenharmony_ci	 * Leave loopback mode, and resend any packet that got stopped.
84662306a36Sopenharmony_ci	 */
84762306a36Sopenharmony_ci	ei_outb_p(E8390_TXCONFIG, e8390_base + EN0_TXCR);
84862306a36Sopenharmony_ci	if (must_resend)
84962306a36Sopenharmony_ci		ei_outb_p(E8390_NODMA + E8390_PAGE0 + E8390_START + E8390_TRANS, e8390_base + E8390_CMD);
85062306a36Sopenharmony_ci}
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci/*
85362306a36Sopenharmony_ci *	Collect the stats. This is called unlocked and from several contexts.
85462306a36Sopenharmony_ci */
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_cistatic struct net_device_stats *__ei_get_stats(struct net_device *dev)
85762306a36Sopenharmony_ci{
85862306a36Sopenharmony_ci	unsigned long ioaddr = dev->base_addr;
85962306a36Sopenharmony_ci	struct ei_device *ei_local = netdev_priv(dev);
86062306a36Sopenharmony_ci	unsigned long flags;
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci	/* If the card is stopped, just return the present stats. */
86362306a36Sopenharmony_ci	if (!netif_running(dev))
86462306a36Sopenharmony_ci		return &dev->stats;
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci	spin_lock_irqsave(&ei_local->page_lock, flags);
86762306a36Sopenharmony_ci	/* Read the counter registers, assuming we are in page 0. */
86862306a36Sopenharmony_ci	dev->stats.rx_frame_errors  += ei_inb_p(ioaddr + EN0_COUNTER0);
86962306a36Sopenharmony_ci	dev->stats.rx_crc_errors    += ei_inb_p(ioaddr + EN0_COUNTER1);
87062306a36Sopenharmony_ci	dev->stats.rx_missed_errors += ei_inb_p(ioaddr + EN0_COUNTER2);
87162306a36Sopenharmony_ci	spin_unlock_irqrestore(&ei_local->page_lock, flags);
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci	return &dev->stats;
87462306a36Sopenharmony_ci}
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci/*
87762306a36Sopenharmony_ci * Form the 64 bit 8390 multicast table from the linked list of addresses
87862306a36Sopenharmony_ci * associated with this dev structure.
87962306a36Sopenharmony_ci */
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_cistatic inline void make_mc_bits(u8 *bits, struct net_device *dev)
88262306a36Sopenharmony_ci{
88362306a36Sopenharmony_ci	struct netdev_hw_addr *ha;
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci	netdev_for_each_mc_addr(ha, dev) {
88662306a36Sopenharmony_ci		u32 crc = ether_crc(ETH_ALEN, ha->addr);
88762306a36Sopenharmony_ci		/*
88862306a36Sopenharmony_ci		 * The 8390 uses the 6 most significant bits of the
88962306a36Sopenharmony_ci		 * CRC to index the multicast table.
89062306a36Sopenharmony_ci		 */
89162306a36Sopenharmony_ci		bits[crc>>29] |= (1<<((crc>>26)&7));
89262306a36Sopenharmony_ci	}
89362306a36Sopenharmony_ci}
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci/**
89662306a36Sopenharmony_ci * do_set_multicast_list - set/clear multicast filter
89762306a36Sopenharmony_ci * @dev: net device for which multicast filter is adjusted
89862306a36Sopenharmony_ci *
89962306a36Sopenharmony_ci *	Set or clear the multicast filter for this adaptor. May be called
90062306a36Sopenharmony_ci *	from a BH in 2.1.x. Must be called with lock held.
90162306a36Sopenharmony_ci */
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_cistatic void do_set_multicast_list(struct net_device *dev)
90462306a36Sopenharmony_ci{
90562306a36Sopenharmony_ci	unsigned long e8390_base = dev->base_addr;
90662306a36Sopenharmony_ci	int i;
90762306a36Sopenharmony_ci	struct ei_device *ei_local = netdev_priv(dev);
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci	if (!(dev->flags&(IFF_PROMISC|IFF_ALLMULTI))) {
91062306a36Sopenharmony_ci		memset(ei_local->mcfilter, 0, 8);
91162306a36Sopenharmony_ci		if (!netdev_mc_empty(dev))
91262306a36Sopenharmony_ci			make_mc_bits(ei_local->mcfilter, dev);
91362306a36Sopenharmony_ci	} else
91462306a36Sopenharmony_ci		memset(ei_local->mcfilter, 0xFF, 8);	/* mcast set to accept-all */
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci	/*
91762306a36Sopenharmony_ci	 * DP8390 manuals don't specify any magic sequence for altering
91862306a36Sopenharmony_ci	 * the multicast regs on an already running card. To be safe, we
91962306a36Sopenharmony_ci	 * ensure multicast mode is off prior to loading up the new hash
92062306a36Sopenharmony_ci	 * table. If this proves to be not enough, we can always resort
92162306a36Sopenharmony_ci	 * to stopping the NIC, loading the table and then restarting.
92262306a36Sopenharmony_ci	 *
92362306a36Sopenharmony_ci	 * Bug Alert!  The MC regs on the SMC 83C690 (SMC Elite and SMC
92462306a36Sopenharmony_ci	 * Elite16) appear to be write-only. The NS 8390 data sheet lists
92562306a36Sopenharmony_ci	 * them as r/w so this is a bug.  The SMC 83C790 (SMC Ultra and
92662306a36Sopenharmony_ci	 * Ultra32 EISA) appears to have this bug fixed.
92762306a36Sopenharmony_ci	 */
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci	if (netif_running(dev))
93062306a36Sopenharmony_ci		ei_outb_p(E8390_RXCONFIG, e8390_base + EN0_RXCR);
93162306a36Sopenharmony_ci	ei_outb_p(E8390_NODMA + E8390_PAGE1, e8390_base + E8390_CMD);
93262306a36Sopenharmony_ci	for (i = 0; i < 8; i++) {
93362306a36Sopenharmony_ci		ei_outb_p(ei_local->mcfilter[i], e8390_base + EN1_MULT_SHIFT(i));
93462306a36Sopenharmony_ci#ifndef BUG_83C690
93562306a36Sopenharmony_ci		if (ei_inb_p(e8390_base + EN1_MULT_SHIFT(i)) != ei_local->mcfilter[i])
93662306a36Sopenharmony_ci			netdev_err(dev, "Multicast filter read/write mismap %d\n",
93762306a36Sopenharmony_ci				   i);
93862306a36Sopenharmony_ci#endif
93962306a36Sopenharmony_ci	}
94062306a36Sopenharmony_ci	ei_outb_p(E8390_NODMA + E8390_PAGE0, e8390_base + E8390_CMD);
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci	if (dev->flags&IFF_PROMISC)
94362306a36Sopenharmony_ci		ei_outb_p(E8390_RXCONFIG | 0x18, e8390_base + EN0_RXCR);
94462306a36Sopenharmony_ci	else if (dev->flags & IFF_ALLMULTI || !netdev_mc_empty(dev))
94562306a36Sopenharmony_ci		ei_outb_p(E8390_RXCONFIG | 0x08, e8390_base + EN0_RXCR);
94662306a36Sopenharmony_ci	else
94762306a36Sopenharmony_ci		ei_outb_p(E8390_RXCONFIG, e8390_base + EN0_RXCR);
94862306a36Sopenharmony_ci}
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci/*
95162306a36Sopenharmony_ci *	Called without lock held. This is invoked from user context and may
95262306a36Sopenharmony_ci *	be parallel to just about everything else. Its also fairly quick and
95362306a36Sopenharmony_ci *	not called too often. Must protect against both bh and irq users
95462306a36Sopenharmony_ci */
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_cistatic void __ei_set_multicast_list(struct net_device *dev)
95762306a36Sopenharmony_ci{
95862306a36Sopenharmony_ci	unsigned long flags;
95962306a36Sopenharmony_ci	struct ei_device *ei_local = netdev_priv(dev);
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_ci	spin_lock_irqsave(&ei_local->page_lock, flags);
96262306a36Sopenharmony_ci	do_set_multicast_list(dev);
96362306a36Sopenharmony_ci	spin_unlock_irqrestore(&ei_local->page_lock, flags);
96462306a36Sopenharmony_ci}
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci/**
96762306a36Sopenharmony_ci * ethdev_setup - init rest of 8390 device struct
96862306a36Sopenharmony_ci * @dev: network device structure to init
96962306a36Sopenharmony_ci *
97062306a36Sopenharmony_ci * Initialize the rest of the 8390 device structure.  Do NOT __init
97162306a36Sopenharmony_ci * this, as it is used by 8390 based modular drivers too.
97262306a36Sopenharmony_ci */
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_cistatic void ethdev_setup(struct net_device *dev)
97562306a36Sopenharmony_ci{
97662306a36Sopenharmony_ci	struct ei_device *ei_local = netdev_priv(dev);
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci	ether_setup(dev);
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci	spin_lock_init(&ei_local->page_lock);
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci	ei_local->msg_enable = netif_msg_init(msg_enable, default_msg_level);
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	if (netif_msg_drv(ei_local) && (version_printed++ == 0))
98562306a36Sopenharmony_ci		pr_info("%s", version);
98662306a36Sopenharmony_ci}
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci/**
98962306a36Sopenharmony_ci * alloc_ei_netdev - alloc_etherdev counterpart for 8390
99062306a36Sopenharmony_ci * @size: extra bytes to allocate
99162306a36Sopenharmony_ci *
99262306a36Sopenharmony_ci * Allocate 8390-specific net_device.
99362306a36Sopenharmony_ci */
99462306a36Sopenharmony_cistatic struct net_device *____alloc_ei_netdev(int size)
99562306a36Sopenharmony_ci{
99662306a36Sopenharmony_ci	return alloc_netdev(sizeof(struct ei_device) + size, "eth%d",
99762306a36Sopenharmony_ci			    NET_NAME_UNKNOWN, ethdev_setup);
99862306a36Sopenharmony_ci}
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci/* This page of functions should be 8390 generic */
100462306a36Sopenharmony_ci/* Follow National Semi's recommendations for initializing the "NIC". */
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci/**
100762306a36Sopenharmony_ci * NS8390_init - initialize 8390 hardware
100862306a36Sopenharmony_ci * @dev: network device to initialize
100962306a36Sopenharmony_ci * @startp: boolean.  non-zero value to initiate chip processing
101062306a36Sopenharmony_ci *
101162306a36Sopenharmony_ci *	Must be called with lock held.
101262306a36Sopenharmony_ci */
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_cistatic void __NS8390_init(struct net_device *dev, int startp)
101562306a36Sopenharmony_ci{
101662306a36Sopenharmony_ci	unsigned long e8390_base = dev->base_addr;
101762306a36Sopenharmony_ci	struct ei_device *ei_local = netdev_priv(dev);
101862306a36Sopenharmony_ci	int i;
101962306a36Sopenharmony_ci	int endcfg = ei_local->word16
102062306a36Sopenharmony_ci	    ? (0x48 | ENDCFG_WTS | (ei_local->bigendian ? ENDCFG_BOS : 0))
102162306a36Sopenharmony_ci	    : 0x48;
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	BUILD_BUG_ON(sizeof(struct e8390_pkt_hdr) != 4);
102462306a36Sopenharmony_ci	/* Follow National Semi's recommendations for initing the DP83902. */
102562306a36Sopenharmony_ci	ei_outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base+E8390_CMD); /* 0x21 */
102662306a36Sopenharmony_ci	ei_outb_p(endcfg, e8390_base + EN0_DCFG);	/* 0x48 or 0x49 */
102762306a36Sopenharmony_ci	/* Clear the remote byte count registers. */
102862306a36Sopenharmony_ci	ei_outb_p(0x00,  e8390_base + EN0_RCNTLO);
102962306a36Sopenharmony_ci	ei_outb_p(0x00,  e8390_base + EN0_RCNTHI);
103062306a36Sopenharmony_ci	/* Set to monitor and loopback mode -- this is vital!. */
103162306a36Sopenharmony_ci	ei_outb_p(E8390_RXOFF, e8390_base + EN0_RXCR); /* 0x20 */
103262306a36Sopenharmony_ci	ei_outb_p(E8390_TXOFF, e8390_base + EN0_TXCR); /* 0x02 */
103362306a36Sopenharmony_ci	/* Set the transmit page and receive ring. */
103462306a36Sopenharmony_ci	ei_outb_p(ei_local->tx_start_page, e8390_base + EN0_TPSR);
103562306a36Sopenharmony_ci	ei_local->tx1 = ei_local->tx2 = 0;
103662306a36Sopenharmony_ci	ei_outb_p(ei_local->rx_start_page, e8390_base + EN0_STARTPG);
103762306a36Sopenharmony_ci	ei_outb_p(ei_local->stop_page-1, e8390_base + EN0_BOUNDARY);	/* 3c503 says 0x3f,NS0x26*/
103862306a36Sopenharmony_ci	ei_local->current_page = ei_local->rx_start_page;		/* assert boundary+1 */
103962306a36Sopenharmony_ci	ei_outb_p(ei_local->stop_page, e8390_base + EN0_STOPPG);
104062306a36Sopenharmony_ci	/* Clear the pending interrupts and mask. */
104162306a36Sopenharmony_ci	ei_outb_p(0xFF, e8390_base + EN0_ISR);
104262306a36Sopenharmony_ci	ei_outb_p(0x00,  e8390_base + EN0_IMR);
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci	/* Copy the station address into the DS8390 registers. */
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci	ei_outb_p(E8390_NODMA + E8390_PAGE1 + E8390_STOP, e8390_base+E8390_CMD); /* 0x61 */
104762306a36Sopenharmony_ci	for (i = 0; i < 6; i++) {
104862306a36Sopenharmony_ci		ei_outb_p(dev->dev_addr[i], e8390_base + EN1_PHYS_SHIFT(i));
104962306a36Sopenharmony_ci		if ((netif_msg_probe(ei_local)) &&
105062306a36Sopenharmony_ci		    ei_inb_p(e8390_base + EN1_PHYS_SHIFT(i)) != dev->dev_addr[i])
105162306a36Sopenharmony_ci			netdev_err(dev,
105262306a36Sopenharmony_ci				   "Hw. address read/write mismap %d\n", i);
105362306a36Sopenharmony_ci	}
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_ci	ei_outb_p(ei_local->rx_start_page, e8390_base + EN1_CURPAG);
105662306a36Sopenharmony_ci	ei_outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base+E8390_CMD);
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_ci	ei_local->tx1 = ei_local->tx2 = 0;
105962306a36Sopenharmony_ci	ei_local->txing = 0;
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_ci	if (startp) {
106262306a36Sopenharmony_ci		ei_outb_p(0xff,  e8390_base + EN0_ISR);
106362306a36Sopenharmony_ci		ei_outb_p(ENISR_ALL,  e8390_base + EN0_IMR);
106462306a36Sopenharmony_ci		ei_outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base+E8390_CMD);
106562306a36Sopenharmony_ci		ei_outb_p(E8390_TXCONFIG, e8390_base + EN0_TXCR); /* xmit on. */
106662306a36Sopenharmony_ci		/* 3c503 TechMan says rxconfig only after the NIC is started. */
106762306a36Sopenharmony_ci		ei_outb_p(E8390_RXCONFIG, e8390_base + EN0_RXCR); /* rx on,  */
106862306a36Sopenharmony_ci		do_set_multicast_list(dev);	/* (re)load the mcast table */
106962306a36Sopenharmony_ci	}
107062306a36Sopenharmony_ci}
107162306a36Sopenharmony_ci
107262306a36Sopenharmony_ci/* Trigger a transmit start, assuming the length is valid.
107362306a36Sopenharmony_ci   Always called with the page lock held */
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_cistatic void NS8390_trigger_send(struct net_device *dev, unsigned int length,
107662306a36Sopenharmony_ci								int start_page)
107762306a36Sopenharmony_ci{
107862306a36Sopenharmony_ci	unsigned long e8390_base = dev->base_addr;
107962306a36Sopenharmony_ci	struct ei_device *ei_local __attribute((unused)) = netdev_priv(dev);
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci	ei_outb_p(E8390_NODMA+E8390_PAGE0, e8390_base+E8390_CMD);
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_ci	if (ei_inb_p(e8390_base + E8390_CMD) & E8390_TRANS) {
108462306a36Sopenharmony_ci		netdev_warn(dev, "trigger_send() called with the transmitter busy\n");
108562306a36Sopenharmony_ci		return;
108662306a36Sopenharmony_ci	}
108762306a36Sopenharmony_ci	ei_outb_p(length & 0xff, e8390_base + EN0_TCNTLO);
108862306a36Sopenharmony_ci	ei_outb_p(length >> 8, e8390_base + EN0_TCNTHI);
108962306a36Sopenharmony_ci	ei_outb_p(start_page, e8390_base + EN0_TPSR);
109062306a36Sopenharmony_ci	ei_outb_p(E8390_NODMA+E8390_TRANS+E8390_START, e8390_base+E8390_CMD);
109162306a36Sopenharmony_ci}
1092