162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *    Lance ethernet driver for the MIPS processor based
462306a36Sopenharmony_ci *      DECstation family
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci *      adopted from sunlance.c by Richard van den Berg
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci *      Copyright (C) 2002, 2003, 2005, 2006  Maciej W. Rozycki
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci *      additional sources:
1262306a36Sopenharmony_ci *      - PMAD-AA TURBOchannel Ethernet Module Functional Specification,
1362306a36Sopenharmony_ci *        Revision 1.2
1462306a36Sopenharmony_ci *
1562306a36Sopenharmony_ci *      History:
1662306a36Sopenharmony_ci *
1762306a36Sopenharmony_ci *      v0.001: The kernel accepts the code and it shows the hardware address.
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci *      v0.002: Removed most sparc stuff, left only some module and dma stuff.
2062306a36Sopenharmony_ci *
2162306a36Sopenharmony_ci *      v0.003: Enhanced base address calculation from proposals by
2262306a36Sopenharmony_ci *              Harald Koerfgen and Thomas Riemer.
2362306a36Sopenharmony_ci *
2462306a36Sopenharmony_ci *      v0.004: lance-regs is pointing at the right addresses, added prom
2562306a36Sopenharmony_ci *              check. First start of address mapping and DMA.
2662306a36Sopenharmony_ci *
2762306a36Sopenharmony_ci *      v0.005: started to play around with LANCE-DMA. This driver will not
2862306a36Sopenharmony_ci *              work for non IOASIC lances. HK
2962306a36Sopenharmony_ci *
3062306a36Sopenharmony_ci *      v0.006: added pointer arrays to lance_private and setup routine for
3162306a36Sopenharmony_ci *              them in dec_lance_init. HK
3262306a36Sopenharmony_ci *
3362306a36Sopenharmony_ci *      v0.007: Big shit. The LANCE seems to use a different DMA mechanism to
3462306a36Sopenharmony_ci *              access the init block. This looks like one (short) word at a
3562306a36Sopenharmony_ci *              time, but the smallest amount the IOASIC can transfer is a
3662306a36Sopenharmony_ci *              (long) word. So we have a 2-2 padding here. Changed
3762306a36Sopenharmony_ci *              lance_init_block accordingly. The 16-16 padding for the buffers
3862306a36Sopenharmony_ci *              seems to be correct. HK
3962306a36Sopenharmony_ci *
4062306a36Sopenharmony_ci *      v0.008: mods to make PMAX_LANCE work. 01/09/1999 triemer
4162306a36Sopenharmony_ci *
4262306a36Sopenharmony_ci *      v0.009: Module support fixes, multiple interfaces support, various
4362306a36Sopenharmony_ci *              bits. macro
4462306a36Sopenharmony_ci *
4562306a36Sopenharmony_ci *      v0.010: Fixes for the PMAD mapping of the LANCE buffer and for the
4662306a36Sopenharmony_ci *              PMAX requirement to only use halfword accesses to the
4762306a36Sopenharmony_ci *              buffer. macro
4862306a36Sopenharmony_ci *
4962306a36Sopenharmony_ci *      v0.011: Converted the PMAD to the driver model. macro
5062306a36Sopenharmony_ci */
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci#include <linux/crc32.h>
5362306a36Sopenharmony_ci#include <linux/delay.h>
5462306a36Sopenharmony_ci#include <linux/errno.h>
5562306a36Sopenharmony_ci#include <linux/if_ether.h>
5662306a36Sopenharmony_ci#include <linux/init.h>
5762306a36Sopenharmony_ci#include <linux/kernel.h>
5862306a36Sopenharmony_ci#include <linux/module.h>
5962306a36Sopenharmony_ci#include <linux/netdevice.h>
6062306a36Sopenharmony_ci#include <linux/etherdevice.h>
6162306a36Sopenharmony_ci#include <linux/spinlock.h>
6262306a36Sopenharmony_ci#include <linux/stddef.h>
6362306a36Sopenharmony_ci#include <linux/string.h>
6462306a36Sopenharmony_ci#include <linux/tc.h>
6562306a36Sopenharmony_ci#include <linux/types.h>
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci#include <asm/addrspace.h>
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci#include <asm/dec/interrupts.h>
7062306a36Sopenharmony_ci#include <asm/dec/ioasic.h>
7162306a36Sopenharmony_ci#include <asm/dec/ioasic_addrs.h>
7262306a36Sopenharmony_ci#include <asm/dec/kn01.h>
7362306a36Sopenharmony_ci#include <asm/dec/machtype.h>
7462306a36Sopenharmony_ci#include <asm/dec/system.h>
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic const char version[] =
7762306a36Sopenharmony_ci"declance.c: v0.011 by Linux MIPS DECstation task force\n";
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ciMODULE_AUTHOR("Linux MIPS DECstation task force");
8062306a36Sopenharmony_ciMODULE_DESCRIPTION("DEC LANCE (DECstation onboard, PMAD-xx) driver");
8162306a36Sopenharmony_ciMODULE_LICENSE("GPL");
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci#define __unused __attribute__ ((unused))
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci/*
8662306a36Sopenharmony_ci * card types
8762306a36Sopenharmony_ci */
8862306a36Sopenharmony_ci#define ASIC_LANCE 1
8962306a36Sopenharmony_ci#define PMAD_LANCE 2
9062306a36Sopenharmony_ci#define PMAX_LANCE 3
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci#define LE_CSR0 0
9462306a36Sopenharmony_ci#define LE_CSR1 1
9562306a36Sopenharmony_ci#define LE_CSR2 2
9662306a36Sopenharmony_ci#define LE_CSR3 3
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci#define LE_MO_PROM      0x8000	/* Enable promiscuous mode */
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci#define	LE_C0_ERR	0x8000	/* Error: set if BAB, SQE, MISS or ME is set */
10162306a36Sopenharmony_ci#define	LE_C0_BABL	0x4000	/* BAB:  Babble: tx timeout. */
10262306a36Sopenharmony_ci#define	LE_C0_CERR	0x2000	/* SQE:  Signal quality error */
10362306a36Sopenharmony_ci#define	LE_C0_MISS	0x1000	/* MISS: Missed a packet */
10462306a36Sopenharmony_ci#define	LE_C0_MERR	0x0800	/* ME:   Memory error */
10562306a36Sopenharmony_ci#define	LE_C0_RINT	0x0400	/* Received interrupt */
10662306a36Sopenharmony_ci#define	LE_C0_TINT	0x0200	/* Transmitter Interrupt */
10762306a36Sopenharmony_ci#define	LE_C0_IDON	0x0100	/* IFIN: Init finished. */
10862306a36Sopenharmony_ci#define	LE_C0_INTR	0x0080	/* Interrupt or error */
10962306a36Sopenharmony_ci#define	LE_C0_INEA	0x0040	/* Interrupt enable */
11062306a36Sopenharmony_ci#define	LE_C0_RXON	0x0020	/* Receiver on */
11162306a36Sopenharmony_ci#define	LE_C0_TXON	0x0010	/* Transmitter on */
11262306a36Sopenharmony_ci#define	LE_C0_TDMD	0x0008	/* Transmitter demand */
11362306a36Sopenharmony_ci#define	LE_C0_STOP	0x0004	/* Stop the card */
11462306a36Sopenharmony_ci#define	LE_C0_STRT	0x0002	/* Start the card */
11562306a36Sopenharmony_ci#define	LE_C0_INIT	0x0001	/* Init the card */
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci#define	LE_C3_BSWP	0x4	/* SWAP */
11862306a36Sopenharmony_ci#define	LE_C3_ACON	0x2	/* ALE Control */
11962306a36Sopenharmony_ci#define	LE_C3_BCON	0x1	/* Byte control */
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci/* Receive message descriptor 1 */
12262306a36Sopenharmony_ci#define LE_R1_OWN	0x8000	/* Who owns the entry */
12362306a36Sopenharmony_ci#define LE_R1_ERR	0x4000	/* Error: if FRA, OFL, CRC or BUF is set */
12462306a36Sopenharmony_ci#define LE_R1_FRA	0x2000	/* FRA: Frame error */
12562306a36Sopenharmony_ci#define LE_R1_OFL	0x1000	/* OFL: Frame overflow */
12662306a36Sopenharmony_ci#define LE_R1_CRC	0x0800	/* CRC error */
12762306a36Sopenharmony_ci#define LE_R1_BUF	0x0400	/* BUF: Buffer error */
12862306a36Sopenharmony_ci#define LE_R1_SOP	0x0200	/* Start of packet */
12962306a36Sopenharmony_ci#define LE_R1_EOP	0x0100	/* End of packet */
13062306a36Sopenharmony_ci#define LE_R1_POK	0x0300	/* Packet is complete: SOP + EOP */
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci/* Transmit message descriptor 1 */
13362306a36Sopenharmony_ci#define LE_T1_OWN	0x8000	/* Lance owns the packet */
13462306a36Sopenharmony_ci#define LE_T1_ERR	0x4000	/* Error summary */
13562306a36Sopenharmony_ci#define LE_T1_EMORE	0x1000	/* Error: more than one retry needed */
13662306a36Sopenharmony_ci#define LE_T1_EONE	0x0800	/* Error: one retry needed */
13762306a36Sopenharmony_ci#define LE_T1_EDEF	0x0400	/* Error: deferred */
13862306a36Sopenharmony_ci#define LE_T1_SOP	0x0200	/* Start of packet */
13962306a36Sopenharmony_ci#define LE_T1_EOP	0x0100	/* End of packet */
14062306a36Sopenharmony_ci#define LE_T1_POK	0x0300	/* Packet is complete: SOP + EOP */
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci#define LE_T3_BUF       0x8000	/* Buffer error */
14362306a36Sopenharmony_ci#define LE_T3_UFL       0x4000	/* Error underflow */
14462306a36Sopenharmony_ci#define LE_T3_LCOL      0x1000	/* Error late collision */
14562306a36Sopenharmony_ci#define LE_T3_CLOS      0x0800	/* Error carrier loss */
14662306a36Sopenharmony_ci#define LE_T3_RTY       0x0400	/* Error retry */
14762306a36Sopenharmony_ci#define LE_T3_TDR       0x03ff	/* Time Domain Reflectometry counter */
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci/* Define: 2^4 Tx buffers and 2^4 Rx buffers */
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci#ifndef LANCE_LOG_TX_BUFFERS
15262306a36Sopenharmony_ci#define LANCE_LOG_TX_BUFFERS 4
15362306a36Sopenharmony_ci#define LANCE_LOG_RX_BUFFERS 4
15462306a36Sopenharmony_ci#endif
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci#define TX_RING_SIZE			(1 << (LANCE_LOG_TX_BUFFERS))
15762306a36Sopenharmony_ci#define TX_RING_MOD_MASK		(TX_RING_SIZE - 1)
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci#define RX_RING_SIZE			(1 << (LANCE_LOG_RX_BUFFERS))
16062306a36Sopenharmony_ci#define RX_RING_MOD_MASK		(RX_RING_SIZE - 1)
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci#define PKT_BUF_SZ		1536
16362306a36Sopenharmony_ci#define RX_BUFF_SIZE            PKT_BUF_SZ
16462306a36Sopenharmony_ci#define TX_BUFF_SIZE            PKT_BUF_SZ
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci#undef TEST_HITS
16762306a36Sopenharmony_ci#define ZERO 0
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci/*
17062306a36Sopenharmony_ci * The DS2100/3100 have a linear 64 kB buffer which supports halfword
17162306a36Sopenharmony_ci * accesses only.  Each halfword of the buffer is word-aligned in the
17262306a36Sopenharmony_ci * CPU address space.
17362306a36Sopenharmony_ci *
17462306a36Sopenharmony_ci * The PMAD-AA has a 128 kB buffer on-board.
17562306a36Sopenharmony_ci *
17662306a36Sopenharmony_ci * The IOASIC LANCE devices use a shared memory region.  This region
17762306a36Sopenharmony_ci * as seen from the CPU is (max) 128 kB long and has to be on an 128 kB
17862306a36Sopenharmony_ci * boundary.  The LANCE sees this as a 64 kB long continuous memory
17962306a36Sopenharmony_ci * region.
18062306a36Sopenharmony_ci *
18162306a36Sopenharmony_ci * The LANCE's DMA address is used as an index in this buffer and DMA
18262306a36Sopenharmony_ci * takes place in bursts of eight 16-bit words which are packed into
18362306a36Sopenharmony_ci * four 32-bit words by the IOASIC.  This leads to a strange padding:
18462306a36Sopenharmony_ci * 16 bytes of valid data followed by a 16 byte gap :-(.
18562306a36Sopenharmony_ci */
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_cistruct lance_rx_desc {
18862306a36Sopenharmony_ci	unsigned short rmd0;		/* low address of packet */
18962306a36Sopenharmony_ci	unsigned short rmd1;		/* high address of packet
19062306a36Sopenharmony_ci					   and descriptor bits */
19162306a36Sopenharmony_ci	short length;			/* 2s complement (negative!)
19262306a36Sopenharmony_ci					   of buffer length */
19362306a36Sopenharmony_ci	unsigned short mblength;	/* actual number of bytes received */
19462306a36Sopenharmony_ci};
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_cistruct lance_tx_desc {
19762306a36Sopenharmony_ci	unsigned short tmd0;		/* low address of packet */
19862306a36Sopenharmony_ci	unsigned short tmd1;		/* high address of packet
19962306a36Sopenharmony_ci					   and descriptor bits */
20062306a36Sopenharmony_ci	short length;			/* 2s complement (negative!)
20162306a36Sopenharmony_ci					   of buffer length */
20262306a36Sopenharmony_ci	unsigned short misc;
20362306a36Sopenharmony_ci};
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci/* First part of the LANCE initialization block, described in databook. */
20762306a36Sopenharmony_cistruct lance_init_block {
20862306a36Sopenharmony_ci	unsigned short mode;		/* pre-set mode (reg. 15) */
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	unsigned short phys_addr[3];	/* physical ethernet address */
21162306a36Sopenharmony_ci	unsigned short filter[4];	/* multicast filter */
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	/* Receive and transmit ring base, along with extra bits. */
21462306a36Sopenharmony_ci	unsigned short rx_ptr;		/* receive descriptor addr */
21562306a36Sopenharmony_ci	unsigned short rx_len;		/* receive len and high addr */
21662306a36Sopenharmony_ci	unsigned short tx_ptr;		/* transmit descriptor addr */
21762306a36Sopenharmony_ci	unsigned short tx_len;		/* transmit len and high addr */
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	short gap[4];
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	/* The buffer descriptors */
22262306a36Sopenharmony_ci	struct lance_rx_desc brx_ring[RX_RING_SIZE];
22362306a36Sopenharmony_ci	struct lance_tx_desc btx_ring[TX_RING_SIZE];
22462306a36Sopenharmony_ci};
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci#define BUF_OFFSET_CPU sizeof(struct lance_init_block)
22762306a36Sopenharmony_ci#define BUF_OFFSET_LNC sizeof(struct lance_init_block)
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci#define shift_off(off, type)						\
23062306a36Sopenharmony_ci	(type == ASIC_LANCE || type == PMAX_LANCE ? off << 1 : off)
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci#define lib_off(rt, type)						\
23362306a36Sopenharmony_ci	shift_off(offsetof(struct lance_init_block, rt), type)
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci#define lib_ptr(ib, rt, type) 						\
23662306a36Sopenharmony_ci	((volatile u16 *)((u8 *)(ib) + lib_off(rt, type)))
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci#define rds_off(rt, type)						\
23962306a36Sopenharmony_ci	shift_off(offsetof(struct lance_rx_desc, rt), type)
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci#define rds_ptr(rd, rt, type) 						\
24262306a36Sopenharmony_ci	((volatile u16 *)((u8 *)(rd) + rds_off(rt, type)))
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci#define tds_off(rt, type)						\
24562306a36Sopenharmony_ci	shift_off(offsetof(struct lance_tx_desc, rt), type)
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci#define tds_ptr(td, rt, type) 						\
24862306a36Sopenharmony_ci	((volatile u16 *)((u8 *)(td) + tds_off(rt, type)))
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_cistruct lance_private {
25162306a36Sopenharmony_ci	struct net_device *next;
25262306a36Sopenharmony_ci	int type;
25362306a36Sopenharmony_ci	int dma_irq;
25462306a36Sopenharmony_ci	volatile struct lance_regs *ll;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	spinlock_t	lock;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	int rx_new, tx_new;
25962306a36Sopenharmony_ci	int rx_old, tx_old;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	unsigned short busmaster_regval;
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	struct timer_list       multicast_timer;
26462306a36Sopenharmony_ci	struct net_device	*dev;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	/* Pointers to the ring buffers as seen from the CPU */
26762306a36Sopenharmony_ci	char *rx_buf_ptr_cpu[RX_RING_SIZE];
26862306a36Sopenharmony_ci	char *tx_buf_ptr_cpu[TX_RING_SIZE];
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	/* Pointers to the ring buffers as seen from the LANCE */
27162306a36Sopenharmony_ci	uint rx_buf_ptr_lnc[RX_RING_SIZE];
27262306a36Sopenharmony_ci	uint tx_buf_ptr_lnc[TX_RING_SIZE];
27362306a36Sopenharmony_ci};
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci#define TX_BUFFS_AVAIL ((lp->tx_old<=lp->tx_new)?\
27662306a36Sopenharmony_ci			lp->tx_old+TX_RING_MOD_MASK-lp->tx_new:\
27762306a36Sopenharmony_ci			lp->tx_old - lp->tx_new-1)
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci/* The lance control ports are at an absolute address, machine and tc-slot
28062306a36Sopenharmony_ci * dependent.
28162306a36Sopenharmony_ci * DECstations do only 32-bit access and the LANCE uses 16 bit addresses,
28262306a36Sopenharmony_ci * so we have to give the structure an extra member making rap pointing
28362306a36Sopenharmony_ci * at the right address
28462306a36Sopenharmony_ci */
28562306a36Sopenharmony_cistruct lance_regs {
28662306a36Sopenharmony_ci	volatile unsigned short rdp;	/* register data port */
28762306a36Sopenharmony_ci	unsigned short pad;
28862306a36Sopenharmony_ci	volatile unsigned short rap;	/* register address port */
28962306a36Sopenharmony_ci};
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ciint dec_lance_debug = 2;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_cistatic struct tc_driver dec_lance_tc_driver;
29462306a36Sopenharmony_cistatic struct net_device *root_lance_dev;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_cistatic inline void writereg(volatile unsigned short *regptr, short value)
29762306a36Sopenharmony_ci{
29862306a36Sopenharmony_ci	*regptr = value;
29962306a36Sopenharmony_ci	iob();
30062306a36Sopenharmony_ci}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci/* Load the CSR registers */
30362306a36Sopenharmony_cistatic void load_csrs(struct lance_private *lp)
30462306a36Sopenharmony_ci{
30562306a36Sopenharmony_ci	volatile struct lance_regs *ll = lp->ll;
30662306a36Sopenharmony_ci	uint leptr;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	/* The address space as seen from the LANCE
30962306a36Sopenharmony_ci	 * begins at address 0. HK
31062306a36Sopenharmony_ci	 */
31162306a36Sopenharmony_ci	leptr = 0;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	writereg(&ll->rap, LE_CSR1);
31462306a36Sopenharmony_ci	writereg(&ll->rdp, (leptr & 0xFFFF));
31562306a36Sopenharmony_ci	writereg(&ll->rap, LE_CSR2);
31662306a36Sopenharmony_ci	writereg(&ll->rdp, leptr >> 16);
31762306a36Sopenharmony_ci	writereg(&ll->rap, LE_CSR3);
31862306a36Sopenharmony_ci	writereg(&ll->rdp, lp->busmaster_regval);
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	/* Point back to csr0 */
32162306a36Sopenharmony_ci	writereg(&ll->rap, LE_CSR0);
32262306a36Sopenharmony_ci}
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci/*
32562306a36Sopenharmony_ci * Our specialized copy routines
32662306a36Sopenharmony_ci *
32762306a36Sopenharmony_ci */
32862306a36Sopenharmony_cistatic void cp_to_buf(const int type, void *to, const void *from, int len)
32962306a36Sopenharmony_ci{
33062306a36Sopenharmony_ci	unsigned short *tp;
33162306a36Sopenharmony_ci	const unsigned short *fp;
33262306a36Sopenharmony_ci	unsigned short clen;
33362306a36Sopenharmony_ci	unsigned char *rtp;
33462306a36Sopenharmony_ci	const unsigned char *rfp;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	if (type == PMAD_LANCE) {
33762306a36Sopenharmony_ci		memcpy(to, from, len);
33862306a36Sopenharmony_ci	} else if (type == PMAX_LANCE) {
33962306a36Sopenharmony_ci		clen = len >> 1;
34062306a36Sopenharmony_ci		tp = to;
34162306a36Sopenharmony_ci		fp = from;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci		while (clen--) {
34462306a36Sopenharmony_ci			*tp++ = *fp++;
34562306a36Sopenharmony_ci			tp++;
34662306a36Sopenharmony_ci		}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci		clen = len & 1;
34962306a36Sopenharmony_ci		rtp = (unsigned char *)tp;
35062306a36Sopenharmony_ci		rfp = (const unsigned char *)fp;
35162306a36Sopenharmony_ci		while (clen--) {
35262306a36Sopenharmony_ci			*rtp++ = *rfp++;
35362306a36Sopenharmony_ci		}
35462306a36Sopenharmony_ci	} else {
35562306a36Sopenharmony_ci		/*
35662306a36Sopenharmony_ci		 * copy 16 Byte chunks
35762306a36Sopenharmony_ci		 */
35862306a36Sopenharmony_ci		clen = len >> 4;
35962306a36Sopenharmony_ci		tp = to;
36062306a36Sopenharmony_ci		fp = from;
36162306a36Sopenharmony_ci		while (clen--) {
36262306a36Sopenharmony_ci			*tp++ = *fp++;
36362306a36Sopenharmony_ci			*tp++ = *fp++;
36462306a36Sopenharmony_ci			*tp++ = *fp++;
36562306a36Sopenharmony_ci			*tp++ = *fp++;
36662306a36Sopenharmony_ci			*tp++ = *fp++;
36762306a36Sopenharmony_ci			*tp++ = *fp++;
36862306a36Sopenharmony_ci			*tp++ = *fp++;
36962306a36Sopenharmony_ci			*tp++ = *fp++;
37062306a36Sopenharmony_ci			tp += 8;
37162306a36Sopenharmony_ci		}
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci		/*
37462306a36Sopenharmony_ci		 * do the rest, if any.
37562306a36Sopenharmony_ci		 */
37662306a36Sopenharmony_ci		clen = len & 15;
37762306a36Sopenharmony_ci		rtp = (unsigned char *)tp;
37862306a36Sopenharmony_ci		rfp = (const unsigned char *)fp;
37962306a36Sopenharmony_ci		while (clen--) {
38062306a36Sopenharmony_ci			*rtp++ = *rfp++;
38162306a36Sopenharmony_ci		}
38262306a36Sopenharmony_ci	}
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	iob();
38562306a36Sopenharmony_ci}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_cistatic void cp_from_buf(const int type, void *to, const void *from, int len)
38862306a36Sopenharmony_ci{
38962306a36Sopenharmony_ci	unsigned short *tp;
39062306a36Sopenharmony_ci	const unsigned short *fp;
39162306a36Sopenharmony_ci	unsigned short clen;
39262306a36Sopenharmony_ci	unsigned char *rtp;
39362306a36Sopenharmony_ci	const unsigned char *rfp;
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	if (type == PMAD_LANCE) {
39662306a36Sopenharmony_ci		memcpy(to, from, len);
39762306a36Sopenharmony_ci	} else if (type == PMAX_LANCE) {
39862306a36Sopenharmony_ci		clen = len >> 1;
39962306a36Sopenharmony_ci		tp = to;
40062306a36Sopenharmony_ci		fp = from;
40162306a36Sopenharmony_ci		while (clen--) {
40262306a36Sopenharmony_ci			*tp++ = *fp++;
40362306a36Sopenharmony_ci			fp++;
40462306a36Sopenharmony_ci		}
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci		clen = len & 1;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci		rtp = (unsigned char *)tp;
40962306a36Sopenharmony_ci		rfp = (const unsigned char *)fp;
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci		while (clen--) {
41262306a36Sopenharmony_ci			*rtp++ = *rfp++;
41362306a36Sopenharmony_ci		}
41462306a36Sopenharmony_ci	} else {
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci		/*
41762306a36Sopenharmony_ci		 * copy 16 Byte chunks
41862306a36Sopenharmony_ci		 */
41962306a36Sopenharmony_ci		clen = len >> 4;
42062306a36Sopenharmony_ci		tp = to;
42162306a36Sopenharmony_ci		fp = from;
42262306a36Sopenharmony_ci		while (clen--) {
42362306a36Sopenharmony_ci			*tp++ = *fp++;
42462306a36Sopenharmony_ci			*tp++ = *fp++;
42562306a36Sopenharmony_ci			*tp++ = *fp++;
42662306a36Sopenharmony_ci			*tp++ = *fp++;
42762306a36Sopenharmony_ci			*tp++ = *fp++;
42862306a36Sopenharmony_ci			*tp++ = *fp++;
42962306a36Sopenharmony_ci			*tp++ = *fp++;
43062306a36Sopenharmony_ci			*tp++ = *fp++;
43162306a36Sopenharmony_ci			fp += 8;
43262306a36Sopenharmony_ci		}
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci		/*
43562306a36Sopenharmony_ci		 * do the rest, if any.
43662306a36Sopenharmony_ci		 */
43762306a36Sopenharmony_ci		clen = len & 15;
43862306a36Sopenharmony_ci		rtp = (unsigned char *)tp;
43962306a36Sopenharmony_ci		rfp = (const unsigned char *)fp;
44062306a36Sopenharmony_ci		while (clen--) {
44162306a36Sopenharmony_ci			*rtp++ = *rfp++;
44262306a36Sopenharmony_ci		}
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	}
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci}
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci/* Setup the Lance Rx and Tx rings */
45062306a36Sopenharmony_cistatic void lance_init_ring(struct net_device *dev)
45162306a36Sopenharmony_ci{
45262306a36Sopenharmony_ci	struct lance_private *lp = netdev_priv(dev);
45362306a36Sopenharmony_ci	volatile u16 *ib = (volatile u16 *)dev->mem_start;
45462306a36Sopenharmony_ci	uint leptr;
45562306a36Sopenharmony_ci	int i;
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	/* Lock out other processes while setting up hardware */
45862306a36Sopenharmony_ci	netif_stop_queue(dev);
45962306a36Sopenharmony_ci	lp->rx_new = lp->tx_new = 0;
46062306a36Sopenharmony_ci	lp->rx_old = lp->tx_old = 0;
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	/* Copy the ethernet address to the lance init block.
46362306a36Sopenharmony_ci	 * XXX bit 0 of the physical address registers has to be zero
46462306a36Sopenharmony_ci	 */
46562306a36Sopenharmony_ci	*lib_ptr(ib, phys_addr[0], lp->type) = (dev->dev_addr[1] << 8) |
46662306a36Sopenharmony_ci				     dev->dev_addr[0];
46762306a36Sopenharmony_ci	*lib_ptr(ib, phys_addr[1], lp->type) = (dev->dev_addr[3] << 8) |
46862306a36Sopenharmony_ci				     dev->dev_addr[2];
46962306a36Sopenharmony_ci	*lib_ptr(ib, phys_addr[2], lp->type) = (dev->dev_addr[5] << 8) |
47062306a36Sopenharmony_ci				     dev->dev_addr[4];
47162306a36Sopenharmony_ci	/* Setup the initialization block */
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	/* Setup rx descriptor pointer */
47462306a36Sopenharmony_ci	leptr = offsetof(struct lance_init_block, brx_ring);
47562306a36Sopenharmony_ci	*lib_ptr(ib, rx_len, lp->type) = (LANCE_LOG_RX_BUFFERS << 13) |
47662306a36Sopenharmony_ci					 (leptr >> 16);
47762306a36Sopenharmony_ci	*lib_ptr(ib, rx_ptr, lp->type) = leptr;
47862306a36Sopenharmony_ci	if (ZERO)
47962306a36Sopenharmony_ci		printk("RX ptr: %8.8x(%8.8x)\n",
48062306a36Sopenharmony_ci		       leptr, (uint)lib_off(brx_ring, lp->type));
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	/* Setup tx descriptor pointer */
48362306a36Sopenharmony_ci	leptr = offsetof(struct lance_init_block, btx_ring);
48462306a36Sopenharmony_ci	*lib_ptr(ib, tx_len, lp->type) = (LANCE_LOG_TX_BUFFERS << 13) |
48562306a36Sopenharmony_ci					 (leptr >> 16);
48662306a36Sopenharmony_ci	*lib_ptr(ib, tx_ptr, lp->type) = leptr;
48762306a36Sopenharmony_ci	if (ZERO)
48862306a36Sopenharmony_ci		printk("TX ptr: %8.8x(%8.8x)\n",
48962306a36Sopenharmony_ci		       leptr, (uint)lib_off(btx_ring, lp->type));
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	if (ZERO)
49262306a36Sopenharmony_ci		printk("TX rings:\n");
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	/* Setup the Tx ring entries */
49562306a36Sopenharmony_ci	for (i = 0; i < TX_RING_SIZE; i++) {
49662306a36Sopenharmony_ci		leptr = lp->tx_buf_ptr_lnc[i];
49762306a36Sopenharmony_ci		*lib_ptr(ib, btx_ring[i].tmd0, lp->type) = leptr;
49862306a36Sopenharmony_ci		*lib_ptr(ib, btx_ring[i].tmd1, lp->type) = (leptr >> 16) &
49962306a36Sopenharmony_ci							   0xff;
50062306a36Sopenharmony_ci		*lib_ptr(ib, btx_ring[i].length, lp->type) = 0xf000;
50162306a36Sopenharmony_ci						/* The ones required by tmd2 */
50262306a36Sopenharmony_ci		*lib_ptr(ib, btx_ring[i].misc, lp->type) = 0;
50362306a36Sopenharmony_ci		if (i < 3 && ZERO)
50462306a36Sopenharmony_ci			printk("%d: %8.8x(%p)\n",
50562306a36Sopenharmony_ci			       i, leptr, lp->tx_buf_ptr_cpu[i]);
50662306a36Sopenharmony_ci	}
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	/* Setup the Rx ring entries */
50962306a36Sopenharmony_ci	if (ZERO)
51062306a36Sopenharmony_ci		printk("RX rings:\n");
51162306a36Sopenharmony_ci	for (i = 0; i < RX_RING_SIZE; i++) {
51262306a36Sopenharmony_ci		leptr = lp->rx_buf_ptr_lnc[i];
51362306a36Sopenharmony_ci		*lib_ptr(ib, brx_ring[i].rmd0, lp->type) = leptr;
51462306a36Sopenharmony_ci		*lib_ptr(ib, brx_ring[i].rmd1, lp->type) = ((leptr >> 16) &
51562306a36Sopenharmony_ci							    0xff) |
51662306a36Sopenharmony_ci							   LE_R1_OWN;
51762306a36Sopenharmony_ci		*lib_ptr(ib, brx_ring[i].length, lp->type) = -RX_BUFF_SIZE |
51862306a36Sopenharmony_ci							     0xf000;
51962306a36Sopenharmony_ci		*lib_ptr(ib, brx_ring[i].mblength, lp->type) = 0;
52062306a36Sopenharmony_ci		if (i < 3 && ZERO)
52162306a36Sopenharmony_ci			printk("%d: %8.8x(%p)\n",
52262306a36Sopenharmony_ci			       i, leptr, lp->rx_buf_ptr_cpu[i]);
52362306a36Sopenharmony_ci	}
52462306a36Sopenharmony_ci	iob();
52562306a36Sopenharmony_ci}
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_cistatic int init_restart_lance(struct lance_private *lp)
52862306a36Sopenharmony_ci{
52962306a36Sopenharmony_ci	volatile struct lance_regs *ll = lp->ll;
53062306a36Sopenharmony_ci	int i;
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	writereg(&ll->rap, LE_CSR0);
53362306a36Sopenharmony_ci	writereg(&ll->rdp, LE_C0_INIT);
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	/* Wait for the lance to complete initialization */
53662306a36Sopenharmony_ci	for (i = 0; (i < 100) && !(ll->rdp & LE_C0_IDON); i++) {
53762306a36Sopenharmony_ci		udelay(10);
53862306a36Sopenharmony_ci	}
53962306a36Sopenharmony_ci	if ((i == 100) || (ll->rdp & LE_C0_ERR)) {
54062306a36Sopenharmony_ci		printk("LANCE unopened after %d ticks, csr0=%4.4x.\n",
54162306a36Sopenharmony_ci		       i, ll->rdp);
54262306a36Sopenharmony_ci		return -1;
54362306a36Sopenharmony_ci	}
54462306a36Sopenharmony_ci	if ((ll->rdp & LE_C0_ERR)) {
54562306a36Sopenharmony_ci		printk("LANCE unopened after %d ticks, csr0=%4.4x.\n",
54662306a36Sopenharmony_ci		       i, ll->rdp);
54762306a36Sopenharmony_ci		return -1;
54862306a36Sopenharmony_ci	}
54962306a36Sopenharmony_ci	writereg(&ll->rdp, LE_C0_IDON);
55062306a36Sopenharmony_ci	writereg(&ll->rdp, LE_C0_STRT);
55162306a36Sopenharmony_ci	writereg(&ll->rdp, LE_C0_INEA);
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	return 0;
55462306a36Sopenharmony_ci}
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_cistatic int lance_rx(struct net_device *dev)
55762306a36Sopenharmony_ci{
55862306a36Sopenharmony_ci	struct lance_private *lp = netdev_priv(dev);
55962306a36Sopenharmony_ci	volatile u16 *ib = (volatile u16 *)dev->mem_start;
56062306a36Sopenharmony_ci	volatile u16 *rd;
56162306a36Sopenharmony_ci	unsigned short bits;
56262306a36Sopenharmony_ci	int entry, len;
56362306a36Sopenharmony_ci	struct sk_buff *skb;
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci#ifdef TEST_HITS
56662306a36Sopenharmony_ci	{
56762306a36Sopenharmony_ci		int i;
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci		printk("[");
57062306a36Sopenharmony_ci		for (i = 0; i < RX_RING_SIZE; i++) {
57162306a36Sopenharmony_ci			if (i == lp->rx_new)
57262306a36Sopenharmony_ci				printk("%s", *lib_ptr(ib, brx_ring[i].rmd1,
57362306a36Sopenharmony_ci						      lp->type) &
57462306a36Sopenharmony_ci					     LE_R1_OWN ? "_" : "X");
57562306a36Sopenharmony_ci			else
57662306a36Sopenharmony_ci				printk("%s", *lib_ptr(ib, brx_ring[i].rmd1,
57762306a36Sopenharmony_ci						      lp->type) &
57862306a36Sopenharmony_ci					     LE_R1_OWN ? "." : "1");
57962306a36Sopenharmony_ci		}
58062306a36Sopenharmony_ci		printk("]");
58162306a36Sopenharmony_ci	}
58262306a36Sopenharmony_ci#endif
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	for (rd = lib_ptr(ib, brx_ring[lp->rx_new], lp->type);
58562306a36Sopenharmony_ci	     !((bits = *rds_ptr(rd, rmd1, lp->type)) & LE_R1_OWN);
58662306a36Sopenharmony_ci	     rd = lib_ptr(ib, brx_ring[lp->rx_new], lp->type)) {
58762306a36Sopenharmony_ci		entry = lp->rx_new;
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci		/* We got an incomplete frame? */
59062306a36Sopenharmony_ci		if ((bits & LE_R1_POK) != LE_R1_POK) {
59162306a36Sopenharmony_ci			dev->stats.rx_over_errors++;
59262306a36Sopenharmony_ci			dev->stats.rx_errors++;
59362306a36Sopenharmony_ci		} else if (bits & LE_R1_ERR) {
59462306a36Sopenharmony_ci			/* Count only the end frame as a rx error,
59562306a36Sopenharmony_ci			 * not the beginning
59662306a36Sopenharmony_ci			 */
59762306a36Sopenharmony_ci			if (bits & LE_R1_BUF)
59862306a36Sopenharmony_ci				dev->stats.rx_fifo_errors++;
59962306a36Sopenharmony_ci			if (bits & LE_R1_CRC)
60062306a36Sopenharmony_ci				dev->stats.rx_crc_errors++;
60162306a36Sopenharmony_ci			if (bits & LE_R1_OFL)
60262306a36Sopenharmony_ci				dev->stats.rx_over_errors++;
60362306a36Sopenharmony_ci			if (bits & LE_R1_FRA)
60462306a36Sopenharmony_ci				dev->stats.rx_frame_errors++;
60562306a36Sopenharmony_ci			if (bits & LE_R1_EOP)
60662306a36Sopenharmony_ci				dev->stats.rx_errors++;
60762306a36Sopenharmony_ci		} else {
60862306a36Sopenharmony_ci			len = (*rds_ptr(rd, mblength, lp->type) & 0xfff) - 4;
60962306a36Sopenharmony_ci			skb = netdev_alloc_skb(dev, len + 2);
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci			if (!skb) {
61262306a36Sopenharmony_ci				dev->stats.rx_dropped++;
61362306a36Sopenharmony_ci				*rds_ptr(rd, mblength, lp->type) = 0;
61462306a36Sopenharmony_ci				*rds_ptr(rd, rmd1, lp->type) =
61562306a36Sopenharmony_ci					((lp->rx_buf_ptr_lnc[entry] >> 16) &
61662306a36Sopenharmony_ci					 0xff) | LE_R1_OWN;
61762306a36Sopenharmony_ci				lp->rx_new = (entry + 1) & RX_RING_MOD_MASK;
61862306a36Sopenharmony_ci				return 0;
61962306a36Sopenharmony_ci			}
62062306a36Sopenharmony_ci			dev->stats.rx_bytes += len;
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci			skb_reserve(skb, 2);	/* 16 byte align */
62362306a36Sopenharmony_ci			skb_put(skb, len);	/* make room */
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci			cp_from_buf(lp->type, skb->data,
62662306a36Sopenharmony_ci				    lp->rx_buf_ptr_cpu[entry], len);
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci			skb->protocol = eth_type_trans(skb, dev);
62962306a36Sopenharmony_ci			netif_rx(skb);
63062306a36Sopenharmony_ci			dev->stats.rx_packets++;
63162306a36Sopenharmony_ci		}
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci		/* Return the packet to the pool */
63462306a36Sopenharmony_ci		*rds_ptr(rd, mblength, lp->type) = 0;
63562306a36Sopenharmony_ci		*rds_ptr(rd, length, lp->type) = -RX_BUFF_SIZE | 0xf000;
63662306a36Sopenharmony_ci		*rds_ptr(rd, rmd1, lp->type) =
63762306a36Sopenharmony_ci			((lp->rx_buf_ptr_lnc[entry] >> 16) & 0xff) | LE_R1_OWN;
63862306a36Sopenharmony_ci		lp->rx_new = (entry + 1) & RX_RING_MOD_MASK;
63962306a36Sopenharmony_ci	}
64062306a36Sopenharmony_ci	return 0;
64162306a36Sopenharmony_ci}
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_cistatic void lance_tx(struct net_device *dev)
64462306a36Sopenharmony_ci{
64562306a36Sopenharmony_ci	struct lance_private *lp = netdev_priv(dev);
64662306a36Sopenharmony_ci	volatile u16 *ib = (volatile u16 *)dev->mem_start;
64762306a36Sopenharmony_ci	volatile struct lance_regs *ll = lp->ll;
64862306a36Sopenharmony_ci	volatile u16 *td;
64962306a36Sopenharmony_ci	int i, j;
65062306a36Sopenharmony_ci	int status;
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	j = lp->tx_old;
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	spin_lock(&lp->lock);
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	for (i = j; i != lp->tx_new; i = j) {
65762306a36Sopenharmony_ci		td = lib_ptr(ib, btx_ring[i], lp->type);
65862306a36Sopenharmony_ci		/* If we hit a packet not owned by us, stop */
65962306a36Sopenharmony_ci		if (*tds_ptr(td, tmd1, lp->type) & LE_T1_OWN)
66062306a36Sopenharmony_ci			break;
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci		if (*tds_ptr(td, tmd1, lp->type) & LE_T1_ERR) {
66362306a36Sopenharmony_ci			status = *tds_ptr(td, misc, lp->type);
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci			dev->stats.tx_errors++;
66662306a36Sopenharmony_ci			if (status & LE_T3_RTY)
66762306a36Sopenharmony_ci				dev->stats.tx_aborted_errors++;
66862306a36Sopenharmony_ci			if (status & LE_T3_LCOL)
66962306a36Sopenharmony_ci				dev->stats.tx_window_errors++;
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci			if (status & LE_T3_CLOS) {
67262306a36Sopenharmony_ci				dev->stats.tx_carrier_errors++;
67362306a36Sopenharmony_ci				printk("%s: Carrier Lost\n", dev->name);
67462306a36Sopenharmony_ci				/* Stop the lance */
67562306a36Sopenharmony_ci				writereg(&ll->rap, LE_CSR0);
67662306a36Sopenharmony_ci				writereg(&ll->rdp, LE_C0_STOP);
67762306a36Sopenharmony_ci				lance_init_ring(dev);
67862306a36Sopenharmony_ci				load_csrs(lp);
67962306a36Sopenharmony_ci				init_restart_lance(lp);
68062306a36Sopenharmony_ci				goto out;
68162306a36Sopenharmony_ci			}
68262306a36Sopenharmony_ci			/* Buffer errors and underflows turn off the
68362306a36Sopenharmony_ci			 * transmitter, restart the adapter.
68462306a36Sopenharmony_ci			 */
68562306a36Sopenharmony_ci			if (status & (LE_T3_BUF | LE_T3_UFL)) {
68662306a36Sopenharmony_ci				dev->stats.tx_fifo_errors++;
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci				printk("%s: Tx: ERR_BUF|ERR_UFL, restarting\n",
68962306a36Sopenharmony_ci				       dev->name);
69062306a36Sopenharmony_ci				/* Stop the lance */
69162306a36Sopenharmony_ci				writereg(&ll->rap, LE_CSR0);
69262306a36Sopenharmony_ci				writereg(&ll->rdp, LE_C0_STOP);
69362306a36Sopenharmony_ci				lance_init_ring(dev);
69462306a36Sopenharmony_ci				load_csrs(lp);
69562306a36Sopenharmony_ci				init_restart_lance(lp);
69662306a36Sopenharmony_ci				goto out;
69762306a36Sopenharmony_ci			}
69862306a36Sopenharmony_ci		} else if ((*tds_ptr(td, tmd1, lp->type) & LE_T1_POK) ==
69962306a36Sopenharmony_ci			   LE_T1_POK) {
70062306a36Sopenharmony_ci			/*
70162306a36Sopenharmony_ci			 * So we don't count the packet more than once.
70262306a36Sopenharmony_ci			 */
70362306a36Sopenharmony_ci			*tds_ptr(td, tmd1, lp->type) &= ~(LE_T1_POK);
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci			/* One collision before packet was sent. */
70662306a36Sopenharmony_ci			if (*tds_ptr(td, tmd1, lp->type) & LE_T1_EONE)
70762306a36Sopenharmony_ci				dev->stats.collisions++;
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci			/* More than one collision, be optimistic. */
71062306a36Sopenharmony_ci			if (*tds_ptr(td, tmd1, lp->type) & LE_T1_EMORE)
71162306a36Sopenharmony_ci				dev->stats.collisions += 2;
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci			dev->stats.tx_packets++;
71462306a36Sopenharmony_ci		}
71562306a36Sopenharmony_ci		j = (j + 1) & TX_RING_MOD_MASK;
71662306a36Sopenharmony_ci	}
71762306a36Sopenharmony_ci	lp->tx_old = j;
71862306a36Sopenharmony_ciout:
71962306a36Sopenharmony_ci	if (netif_queue_stopped(dev) &&
72062306a36Sopenharmony_ci	    TX_BUFFS_AVAIL > 0)
72162306a36Sopenharmony_ci		netif_wake_queue(dev);
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	spin_unlock(&lp->lock);
72462306a36Sopenharmony_ci}
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_cistatic irqreturn_t lance_dma_merr_int(int irq, void *dev_id)
72762306a36Sopenharmony_ci{
72862306a36Sopenharmony_ci	struct net_device *dev = dev_id;
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	printk(KERN_ERR "%s: DMA error\n", dev->name);
73162306a36Sopenharmony_ci	return IRQ_HANDLED;
73262306a36Sopenharmony_ci}
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_cistatic irqreturn_t lance_interrupt(int irq, void *dev_id)
73562306a36Sopenharmony_ci{
73662306a36Sopenharmony_ci	struct net_device *dev = dev_id;
73762306a36Sopenharmony_ci	struct lance_private *lp = netdev_priv(dev);
73862306a36Sopenharmony_ci	volatile struct lance_regs *ll = lp->ll;
73962306a36Sopenharmony_ci	int csr0;
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	writereg(&ll->rap, LE_CSR0);
74262306a36Sopenharmony_ci	csr0 = ll->rdp;
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	/* Acknowledge all the interrupt sources ASAP */
74562306a36Sopenharmony_ci	writereg(&ll->rdp, csr0 & (LE_C0_INTR | LE_C0_TINT | LE_C0_RINT));
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	if ((csr0 & LE_C0_ERR)) {
74862306a36Sopenharmony_ci		/* Clear the error condition */
74962306a36Sopenharmony_ci		writereg(&ll->rdp, LE_C0_BABL | LE_C0_ERR | LE_C0_MISS |
75062306a36Sopenharmony_ci			 LE_C0_CERR | LE_C0_MERR);
75162306a36Sopenharmony_ci	}
75262306a36Sopenharmony_ci	if (csr0 & LE_C0_RINT)
75362306a36Sopenharmony_ci		lance_rx(dev);
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	if (csr0 & LE_C0_TINT)
75662306a36Sopenharmony_ci		lance_tx(dev);
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	if (csr0 & LE_C0_BABL)
75962306a36Sopenharmony_ci		dev->stats.tx_errors++;
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	if (csr0 & LE_C0_MISS)
76262306a36Sopenharmony_ci		dev->stats.rx_errors++;
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci	if (csr0 & LE_C0_MERR) {
76562306a36Sopenharmony_ci		printk("%s: Memory error, status %04x\n", dev->name, csr0);
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci		writereg(&ll->rdp, LE_C0_STOP);
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci		lance_init_ring(dev);
77062306a36Sopenharmony_ci		load_csrs(lp);
77162306a36Sopenharmony_ci		init_restart_lance(lp);
77262306a36Sopenharmony_ci		netif_wake_queue(dev);
77362306a36Sopenharmony_ci	}
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	writereg(&ll->rdp, LE_C0_INEA);
77662306a36Sopenharmony_ci	writereg(&ll->rdp, LE_C0_INEA);
77762306a36Sopenharmony_ci	return IRQ_HANDLED;
77862306a36Sopenharmony_ci}
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_cistatic int lance_open(struct net_device *dev)
78162306a36Sopenharmony_ci{
78262306a36Sopenharmony_ci	volatile u16 *ib = (volatile u16 *)dev->mem_start;
78362306a36Sopenharmony_ci	struct lance_private *lp = netdev_priv(dev);
78462306a36Sopenharmony_ci	volatile struct lance_regs *ll = lp->ll;
78562306a36Sopenharmony_ci	int status = 0;
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	/* Stop the Lance */
78862306a36Sopenharmony_ci	writereg(&ll->rap, LE_CSR0);
78962306a36Sopenharmony_ci	writereg(&ll->rdp, LE_C0_STOP);
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	/* Set mode and clear multicast filter only at device open,
79262306a36Sopenharmony_ci	 * so that lance_init_ring() called at any error will not
79362306a36Sopenharmony_ci	 * forget multicast filters.
79462306a36Sopenharmony_ci	 *
79562306a36Sopenharmony_ci	 * BTW it is common bug in all lance drivers! --ANK
79662306a36Sopenharmony_ci	 */
79762306a36Sopenharmony_ci	*lib_ptr(ib, mode, lp->type) = 0;
79862306a36Sopenharmony_ci	*lib_ptr(ib, filter[0], lp->type) = 0;
79962306a36Sopenharmony_ci	*lib_ptr(ib, filter[1], lp->type) = 0;
80062306a36Sopenharmony_ci	*lib_ptr(ib, filter[2], lp->type) = 0;
80162306a36Sopenharmony_ci	*lib_ptr(ib, filter[3], lp->type) = 0;
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci	lance_init_ring(dev);
80462306a36Sopenharmony_ci	load_csrs(lp);
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci	netif_start_queue(dev);
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci	/* Associate IRQ with lance_interrupt */
80962306a36Sopenharmony_ci	if (request_irq(dev->irq, lance_interrupt, 0, "lance", dev)) {
81062306a36Sopenharmony_ci		printk("%s: Can't get IRQ %d\n", dev->name, dev->irq);
81162306a36Sopenharmony_ci		return -EAGAIN;
81262306a36Sopenharmony_ci	}
81362306a36Sopenharmony_ci	if (lp->dma_irq >= 0) {
81462306a36Sopenharmony_ci		unsigned long flags;
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci		if (request_irq(lp->dma_irq, lance_dma_merr_int, IRQF_ONESHOT,
81762306a36Sopenharmony_ci				"lance error", dev)) {
81862306a36Sopenharmony_ci			free_irq(dev->irq, dev);
81962306a36Sopenharmony_ci			printk("%s: Can't get DMA IRQ %d\n", dev->name,
82062306a36Sopenharmony_ci				lp->dma_irq);
82162306a36Sopenharmony_ci			return -EAGAIN;
82262306a36Sopenharmony_ci		}
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci		spin_lock_irqsave(&ioasic_ssr_lock, flags);
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci		fast_mb();
82762306a36Sopenharmony_ci		/* Enable I/O ASIC LANCE DMA.  */
82862306a36Sopenharmony_ci		ioasic_write(IO_REG_SSR,
82962306a36Sopenharmony_ci			     ioasic_read(IO_REG_SSR) | IO_SSR_LANCE_DMA_EN);
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci		fast_mb();
83262306a36Sopenharmony_ci		spin_unlock_irqrestore(&ioasic_ssr_lock, flags);
83362306a36Sopenharmony_ci	}
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci	status = init_restart_lance(lp);
83662306a36Sopenharmony_ci	return status;
83762306a36Sopenharmony_ci}
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_cistatic int lance_close(struct net_device *dev)
84062306a36Sopenharmony_ci{
84162306a36Sopenharmony_ci	struct lance_private *lp = netdev_priv(dev);
84262306a36Sopenharmony_ci	volatile struct lance_regs *ll = lp->ll;
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	netif_stop_queue(dev);
84562306a36Sopenharmony_ci	del_timer_sync(&lp->multicast_timer);
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci	/* Stop the card */
84862306a36Sopenharmony_ci	writereg(&ll->rap, LE_CSR0);
84962306a36Sopenharmony_ci	writereg(&ll->rdp, LE_C0_STOP);
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci	if (lp->dma_irq >= 0) {
85262306a36Sopenharmony_ci		unsigned long flags;
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci		spin_lock_irqsave(&ioasic_ssr_lock, flags);
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci		fast_mb();
85762306a36Sopenharmony_ci		/* Disable I/O ASIC LANCE DMA.  */
85862306a36Sopenharmony_ci		ioasic_write(IO_REG_SSR,
85962306a36Sopenharmony_ci			     ioasic_read(IO_REG_SSR) & ~IO_SSR_LANCE_DMA_EN);
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci		fast_iob();
86262306a36Sopenharmony_ci		spin_unlock_irqrestore(&ioasic_ssr_lock, flags);
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci		free_irq(lp->dma_irq, dev);
86562306a36Sopenharmony_ci	}
86662306a36Sopenharmony_ci	free_irq(dev->irq, dev);
86762306a36Sopenharmony_ci	return 0;
86862306a36Sopenharmony_ci}
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_cistatic inline int lance_reset(struct net_device *dev)
87162306a36Sopenharmony_ci{
87262306a36Sopenharmony_ci	struct lance_private *lp = netdev_priv(dev);
87362306a36Sopenharmony_ci	volatile struct lance_regs *ll = lp->ll;
87462306a36Sopenharmony_ci	int status;
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci	/* Stop the lance */
87762306a36Sopenharmony_ci	writereg(&ll->rap, LE_CSR0);
87862306a36Sopenharmony_ci	writereg(&ll->rdp, LE_C0_STOP);
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci	lance_init_ring(dev);
88162306a36Sopenharmony_ci	load_csrs(lp);
88262306a36Sopenharmony_ci	netif_trans_update(dev); /* prevent tx timeout */
88362306a36Sopenharmony_ci	status = init_restart_lance(lp);
88462306a36Sopenharmony_ci	return status;
88562306a36Sopenharmony_ci}
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_cistatic void lance_tx_timeout(struct net_device *dev, unsigned int txqueue)
88862306a36Sopenharmony_ci{
88962306a36Sopenharmony_ci	struct lance_private *lp = netdev_priv(dev);
89062306a36Sopenharmony_ci	volatile struct lance_regs *ll = lp->ll;
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	printk(KERN_ERR "%s: transmit timed out, status %04x, reset\n",
89362306a36Sopenharmony_ci		dev->name, ll->rdp);
89462306a36Sopenharmony_ci	lance_reset(dev);
89562306a36Sopenharmony_ci	netif_wake_queue(dev);
89662306a36Sopenharmony_ci}
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_cistatic netdev_tx_t lance_start_xmit(struct sk_buff *skb, struct net_device *dev)
89962306a36Sopenharmony_ci{
90062306a36Sopenharmony_ci	struct lance_private *lp = netdev_priv(dev);
90162306a36Sopenharmony_ci	volatile struct lance_regs *ll = lp->ll;
90262306a36Sopenharmony_ci	volatile u16 *ib = (volatile u16 *)dev->mem_start;
90362306a36Sopenharmony_ci	unsigned long flags;
90462306a36Sopenharmony_ci	int entry, len;
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci	len = skb->len;
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci	if (len < ETH_ZLEN) {
90962306a36Sopenharmony_ci		if (skb_padto(skb, ETH_ZLEN))
91062306a36Sopenharmony_ci			return NETDEV_TX_OK;
91162306a36Sopenharmony_ci		len = ETH_ZLEN;
91262306a36Sopenharmony_ci	}
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	dev->stats.tx_bytes += len;
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci	spin_lock_irqsave(&lp->lock, flags);
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	entry = lp->tx_new;
91962306a36Sopenharmony_ci	*lib_ptr(ib, btx_ring[entry].length, lp->type) = (-len);
92062306a36Sopenharmony_ci	*lib_ptr(ib, btx_ring[entry].misc, lp->type) = 0;
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci	cp_to_buf(lp->type, lp->tx_buf_ptr_cpu[entry], skb->data, len);
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci	/* Now, give the packet to the lance */
92562306a36Sopenharmony_ci	*lib_ptr(ib, btx_ring[entry].tmd1, lp->type) =
92662306a36Sopenharmony_ci		((lp->tx_buf_ptr_lnc[entry] >> 16) & 0xff) |
92762306a36Sopenharmony_ci		(LE_T1_POK | LE_T1_OWN);
92862306a36Sopenharmony_ci	lp->tx_new = (entry + 1) & TX_RING_MOD_MASK;
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	if (TX_BUFFS_AVAIL <= 0)
93162306a36Sopenharmony_ci		netif_stop_queue(dev);
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	/* Kick the lance: transmit now */
93462306a36Sopenharmony_ci	writereg(&ll->rdp, LE_C0_INEA | LE_C0_TDMD);
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci	spin_unlock_irqrestore(&lp->lock, flags);
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci	dev_kfree_skb(skb);
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	return NETDEV_TX_OK;
94162306a36Sopenharmony_ci}
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_cistatic void lance_load_multicast(struct net_device *dev)
94462306a36Sopenharmony_ci{
94562306a36Sopenharmony_ci	struct lance_private *lp = netdev_priv(dev);
94662306a36Sopenharmony_ci	volatile u16 *ib = (volatile u16 *)dev->mem_start;
94762306a36Sopenharmony_ci	struct netdev_hw_addr *ha;
94862306a36Sopenharmony_ci	u32 crc;
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci	/* set all multicast bits */
95162306a36Sopenharmony_ci	if (dev->flags & IFF_ALLMULTI) {
95262306a36Sopenharmony_ci		*lib_ptr(ib, filter[0], lp->type) = 0xffff;
95362306a36Sopenharmony_ci		*lib_ptr(ib, filter[1], lp->type) = 0xffff;
95462306a36Sopenharmony_ci		*lib_ptr(ib, filter[2], lp->type) = 0xffff;
95562306a36Sopenharmony_ci		*lib_ptr(ib, filter[3], lp->type) = 0xffff;
95662306a36Sopenharmony_ci		return;
95762306a36Sopenharmony_ci	}
95862306a36Sopenharmony_ci	/* clear the multicast filter */
95962306a36Sopenharmony_ci	*lib_ptr(ib, filter[0], lp->type) = 0;
96062306a36Sopenharmony_ci	*lib_ptr(ib, filter[1], lp->type) = 0;
96162306a36Sopenharmony_ci	*lib_ptr(ib, filter[2], lp->type) = 0;
96262306a36Sopenharmony_ci	*lib_ptr(ib, filter[3], lp->type) = 0;
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	/* Add addresses */
96562306a36Sopenharmony_ci	netdev_for_each_mc_addr(ha, dev) {
96662306a36Sopenharmony_ci		crc = ether_crc_le(ETH_ALEN, ha->addr);
96762306a36Sopenharmony_ci		crc = crc >> 26;
96862306a36Sopenharmony_ci		*lib_ptr(ib, filter[crc >> 4], lp->type) |= 1 << (crc & 0xf);
96962306a36Sopenharmony_ci	}
97062306a36Sopenharmony_ci}
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_cistatic void lance_set_multicast(struct net_device *dev)
97362306a36Sopenharmony_ci{
97462306a36Sopenharmony_ci	struct lance_private *lp = netdev_priv(dev);
97562306a36Sopenharmony_ci	volatile u16 *ib = (volatile u16 *)dev->mem_start;
97662306a36Sopenharmony_ci	volatile struct lance_regs *ll = lp->ll;
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci	if (!netif_running(dev))
97962306a36Sopenharmony_ci		return;
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_ci	if (lp->tx_old != lp->tx_new) {
98262306a36Sopenharmony_ci		mod_timer(&lp->multicast_timer, jiffies + 4 * HZ/100);
98362306a36Sopenharmony_ci		netif_wake_queue(dev);
98462306a36Sopenharmony_ci		return;
98562306a36Sopenharmony_ci	}
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci	netif_stop_queue(dev);
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci	writereg(&ll->rap, LE_CSR0);
99062306a36Sopenharmony_ci	writereg(&ll->rdp, LE_C0_STOP);
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci	lance_init_ring(dev);
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci	if (dev->flags & IFF_PROMISC) {
99562306a36Sopenharmony_ci		*lib_ptr(ib, mode, lp->type) |= LE_MO_PROM;
99662306a36Sopenharmony_ci	} else {
99762306a36Sopenharmony_ci		*lib_ptr(ib, mode, lp->type) &= ~LE_MO_PROM;
99862306a36Sopenharmony_ci		lance_load_multicast(dev);
99962306a36Sopenharmony_ci	}
100062306a36Sopenharmony_ci	load_csrs(lp);
100162306a36Sopenharmony_ci	init_restart_lance(lp);
100262306a36Sopenharmony_ci	netif_wake_queue(dev);
100362306a36Sopenharmony_ci}
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_cistatic void lance_set_multicast_retry(struct timer_list *t)
100662306a36Sopenharmony_ci{
100762306a36Sopenharmony_ci	struct lance_private *lp = from_timer(lp, t, multicast_timer);
100862306a36Sopenharmony_ci	struct net_device *dev = lp->dev;
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci	lance_set_multicast(dev);
101162306a36Sopenharmony_ci}
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_cistatic const struct net_device_ops lance_netdev_ops = {
101462306a36Sopenharmony_ci	.ndo_open		= lance_open,
101562306a36Sopenharmony_ci	.ndo_stop		= lance_close,
101662306a36Sopenharmony_ci	.ndo_start_xmit		= lance_start_xmit,
101762306a36Sopenharmony_ci	.ndo_tx_timeout		= lance_tx_timeout,
101862306a36Sopenharmony_ci	.ndo_set_rx_mode	= lance_set_multicast,
101962306a36Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
102062306a36Sopenharmony_ci	.ndo_set_mac_address	= eth_mac_addr,
102162306a36Sopenharmony_ci};
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_cistatic int dec_lance_probe(struct device *bdev, const int type)
102462306a36Sopenharmony_ci{
102562306a36Sopenharmony_ci	static unsigned version_printed;
102662306a36Sopenharmony_ci	static const char fmt[] = "declance%d";
102762306a36Sopenharmony_ci	char name[10];
102862306a36Sopenharmony_ci	struct net_device *dev;
102962306a36Sopenharmony_ci	struct lance_private *lp;
103062306a36Sopenharmony_ci	volatile struct lance_regs *ll;
103162306a36Sopenharmony_ci	resource_size_t start = 0, len = 0;
103262306a36Sopenharmony_ci	int i, ret;
103362306a36Sopenharmony_ci	unsigned long esar_base;
103462306a36Sopenharmony_ci	unsigned char *esar;
103562306a36Sopenharmony_ci	u8 addr[ETH_ALEN];
103662306a36Sopenharmony_ci	const char *desc;
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci	if (dec_lance_debug && version_printed++ == 0)
103962306a36Sopenharmony_ci		printk(version);
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci	if (bdev)
104262306a36Sopenharmony_ci		snprintf(name, sizeof(name), "%s", dev_name(bdev));
104362306a36Sopenharmony_ci	else {
104462306a36Sopenharmony_ci		i = 0;
104562306a36Sopenharmony_ci		dev = root_lance_dev;
104662306a36Sopenharmony_ci		while (dev) {
104762306a36Sopenharmony_ci			i++;
104862306a36Sopenharmony_ci			lp = netdev_priv(dev);
104962306a36Sopenharmony_ci			dev = lp->next;
105062306a36Sopenharmony_ci		}
105162306a36Sopenharmony_ci		snprintf(name, sizeof(name), fmt, i);
105262306a36Sopenharmony_ci	}
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci	dev = alloc_etherdev(sizeof(struct lance_private));
105562306a36Sopenharmony_ci	if (!dev) {
105662306a36Sopenharmony_ci		ret = -ENOMEM;
105762306a36Sopenharmony_ci		goto err_out;
105862306a36Sopenharmony_ci	}
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci	/*
106162306a36Sopenharmony_ci	 * alloc_etherdev ensures the data structures used by the LANCE
106262306a36Sopenharmony_ci	 * are aligned.
106362306a36Sopenharmony_ci	 */
106462306a36Sopenharmony_ci	lp = netdev_priv(dev);
106562306a36Sopenharmony_ci	spin_lock_init(&lp->lock);
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ci	lp->type = type;
106862306a36Sopenharmony_ci	switch (type) {
106962306a36Sopenharmony_ci	case ASIC_LANCE:
107062306a36Sopenharmony_ci		dev->base_addr = CKSEG1ADDR(dec_kn_slot_base + IOASIC_LANCE);
107162306a36Sopenharmony_ci
107262306a36Sopenharmony_ci		/* buffer space for the on-board LANCE shared memory */
107362306a36Sopenharmony_ci		/*
107462306a36Sopenharmony_ci		 * FIXME: ugly hack!
107562306a36Sopenharmony_ci		 */
107662306a36Sopenharmony_ci		dev->mem_start = CKSEG1ADDR(0x00020000);
107762306a36Sopenharmony_ci		dev->mem_end = dev->mem_start + 0x00020000;
107862306a36Sopenharmony_ci		dev->irq = dec_interrupt[DEC_IRQ_LANCE];
107962306a36Sopenharmony_ci		esar_base = CKSEG1ADDR(dec_kn_slot_base + IOASIC_ESAR);
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci		/* Workaround crash with booting KN04 2.1k from Disk */
108262306a36Sopenharmony_ci		memset((void *)dev->mem_start, 0,
108362306a36Sopenharmony_ci		       dev->mem_end - dev->mem_start);
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci		/*
108662306a36Sopenharmony_ci		 * setup the pointer arrays, this sucks [tm] :-(
108762306a36Sopenharmony_ci		 */
108862306a36Sopenharmony_ci		for (i = 0; i < RX_RING_SIZE; i++) {
108962306a36Sopenharmony_ci			lp->rx_buf_ptr_cpu[i] =
109062306a36Sopenharmony_ci				(char *)(dev->mem_start + 2 * BUF_OFFSET_CPU +
109162306a36Sopenharmony_ci					 2 * i * RX_BUFF_SIZE);
109262306a36Sopenharmony_ci			lp->rx_buf_ptr_lnc[i] =
109362306a36Sopenharmony_ci				(BUF_OFFSET_LNC + i * RX_BUFF_SIZE);
109462306a36Sopenharmony_ci		}
109562306a36Sopenharmony_ci		for (i = 0; i < TX_RING_SIZE; i++) {
109662306a36Sopenharmony_ci			lp->tx_buf_ptr_cpu[i] =
109762306a36Sopenharmony_ci				(char *)(dev->mem_start + 2 * BUF_OFFSET_CPU +
109862306a36Sopenharmony_ci					 2 * RX_RING_SIZE * RX_BUFF_SIZE +
109962306a36Sopenharmony_ci					 2 * i * TX_BUFF_SIZE);
110062306a36Sopenharmony_ci			lp->tx_buf_ptr_lnc[i] =
110162306a36Sopenharmony_ci				(BUF_OFFSET_LNC +
110262306a36Sopenharmony_ci				 RX_RING_SIZE * RX_BUFF_SIZE +
110362306a36Sopenharmony_ci				 i * TX_BUFF_SIZE);
110462306a36Sopenharmony_ci		}
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ci		/* Setup I/O ASIC LANCE DMA.  */
110762306a36Sopenharmony_ci		lp->dma_irq = dec_interrupt[DEC_IRQ_LANCE_MERR];
110862306a36Sopenharmony_ci		ioasic_write(IO_REG_LANCE_DMA_P,
110962306a36Sopenharmony_ci			     CPHYSADDR(dev->mem_start) << 3);
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci		break;
111262306a36Sopenharmony_ci#ifdef CONFIG_TC
111362306a36Sopenharmony_ci	case PMAD_LANCE:
111462306a36Sopenharmony_ci		dev_set_drvdata(bdev, dev);
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci		start = to_tc_dev(bdev)->resource.start;
111762306a36Sopenharmony_ci		len = to_tc_dev(bdev)->resource.end - start + 1;
111862306a36Sopenharmony_ci		if (!request_mem_region(start, len, dev_name(bdev))) {
111962306a36Sopenharmony_ci			printk(KERN_ERR
112062306a36Sopenharmony_ci			       "%s: Unable to reserve MMIO resource\n",
112162306a36Sopenharmony_ci			       dev_name(bdev));
112262306a36Sopenharmony_ci			ret = -EBUSY;
112362306a36Sopenharmony_ci			goto err_out_dev;
112462306a36Sopenharmony_ci		}
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci		dev->mem_start = CKSEG1ADDR(start);
112762306a36Sopenharmony_ci		dev->mem_end = dev->mem_start + 0x100000;
112862306a36Sopenharmony_ci		dev->base_addr = dev->mem_start + 0x100000;
112962306a36Sopenharmony_ci		dev->irq = to_tc_dev(bdev)->interrupt;
113062306a36Sopenharmony_ci		esar_base = dev->mem_start + 0x1c0002;
113162306a36Sopenharmony_ci		lp->dma_irq = -1;
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci		for (i = 0; i < RX_RING_SIZE; i++) {
113462306a36Sopenharmony_ci			lp->rx_buf_ptr_cpu[i] =
113562306a36Sopenharmony_ci				(char *)(dev->mem_start + BUF_OFFSET_CPU +
113662306a36Sopenharmony_ci					 i * RX_BUFF_SIZE);
113762306a36Sopenharmony_ci			lp->rx_buf_ptr_lnc[i] =
113862306a36Sopenharmony_ci				(BUF_OFFSET_LNC + i * RX_BUFF_SIZE);
113962306a36Sopenharmony_ci		}
114062306a36Sopenharmony_ci		for (i = 0; i < TX_RING_SIZE; i++) {
114162306a36Sopenharmony_ci			lp->tx_buf_ptr_cpu[i] =
114262306a36Sopenharmony_ci				(char *)(dev->mem_start + BUF_OFFSET_CPU +
114362306a36Sopenharmony_ci					 RX_RING_SIZE * RX_BUFF_SIZE +
114462306a36Sopenharmony_ci					 i * TX_BUFF_SIZE);
114562306a36Sopenharmony_ci			lp->tx_buf_ptr_lnc[i] =
114662306a36Sopenharmony_ci				(BUF_OFFSET_LNC +
114762306a36Sopenharmony_ci				 RX_RING_SIZE * RX_BUFF_SIZE +
114862306a36Sopenharmony_ci				 i * TX_BUFF_SIZE);
114962306a36Sopenharmony_ci		}
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci		break;
115262306a36Sopenharmony_ci#endif
115362306a36Sopenharmony_ci	case PMAX_LANCE:
115462306a36Sopenharmony_ci		dev->irq = dec_interrupt[DEC_IRQ_LANCE];
115562306a36Sopenharmony_ci		dev->base_addr = CKSEG1ADDR(KN01_SLOT_BASE + KN01_LANCE);
115662306a36Sopenharmony_ci		dev->mem_start = CKSEG1ADDR(KN01_SLOT_BASE + KN01_LANCE_MEM);
115762306a36Sopenharmony_ci		dev->mem_end = dev->mem_start + KN01_SLOT_SIZE;
115862306a36Sopenharmony_ci		esar_base = CKSEG1ADDR(KN01_SLOT_BASE + KN01_ESAR + 1);
115962306a36Sopenharmony_ci		lp->dma_irq = -1;
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_ci		/*
116262306a36Sopenharmony_ci		 * setup the pointer arrays, this sucks [tm] :-(
116362306a36Sopenharmony_ci		 */
116462306a36Sopenharmony_ci		for (i = 0; i < RX_RING_SIZE; i++) {
116562306a36Sopenharmony_ci			lp->rx_buf_ptr_cpu[i] =
116662306a36Sopenharmony_ci				(char *)(dev->mem_start + 2 * BUF_OFFSET_CPU +
116762306a36Sopenharmony_ci					 2 * i * RX_BUFF_SIZE);
116862306a36Sopenharmony_ci			lp->rx_buf_ptr_lnc[i] =
116962306a36Sopenharmony_ci				(BUF_OFFSET_LNC + i * RX_BUFF_SIZE);
117062306a36Sopenharmony_ci		}
117162306a36Sopenharmony_ci		for (i = 0; i < TX_RING_SIZE; i++) {
117262306a36Sopenharmony_ci			lp->tx_buf_ptr_cpu[i] =
117362306a36Sopenharmony_ci				(char *)(dev->mem_start + 2 * BUF_OFFSET_CPU +
117462306a36Sopenharmony_ci					 2 * RX_RING_SIZE * RX_BUFF_SIZE +
117562306a36Sopenharmony_ci					 2 * i * TX_BUFF_SIZE);
117662306a36Sopenharmony_ci			lp->tx_buf_ptr_lnc[i] =
117762306a36Sopenharmony_ci				(BUF_OFFSET_LNC +
117862306a36Sopenharmony_ci				 RX_RING_SIZE * RX_BUFF_SIZE +
117962306a36Sopenharmony_ci				 i * TX_BUFF_SIZE);
118062306a36Sopenharmony_ci		}
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_ci		break;
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci	default:
118562306a36Sopenharmony_ci		printk(KERN_ERR "%s: declance_init called with unknown type\n",
118662306a36Sopenharmony_ci			name);
118762306a36Sopenharmony_ci		ret = -ENODEV;
118862306a36Sopenharmony_ci		goto err_out_dev;
118962306a36Sopenharmony_ci	}
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_ci	ll = (struct lance_regs *) dev->base_addr;
119262306a36Sopenharmony_ci	esar = (unsigned char *) esar_base;
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_ci	/* prom checks */
119562306a36Sopenharmony_ci	/* First, check for test pattern */
119662306a36Sopenharmony_ci	if (esar[0x60] != 0xff && esar[0x64] != 0x00 &&
119762306a36Sopenharmony_ci	    esar[0x68] != 0x55 && esar[0x6c] != 0xaa) {
119862306a36Sopenharmony_ci		printk(KERN_ERR
119962306a36Sopenharmony_ci			"%s: Ethernet station address prom not found!\n",
120062306a36Sopenharmony_ci			name);
120162306a36Sopenharmony_ci		ret = -ENODEV;
120262306a36Sopenharmony_ci		goto err_out_resource;
120362306a36Sopenharmony_ci	}
120462306a36Sopenharmony_ci	/* Check the prom contents */
120562306a36Sopenharmony_ci	for (i = 0; i < 8; i++) {
120662306a36Sopenharmony_ci		if (esar[i * 4] != esar[0x3c - i * 4] &&
120762306a36Sopenharmony_ci		    esar[i * 4] != esar[0x40 + i * 4] &&
120862306a36Sopenharmony_ci		    esar[0x3c - i * 4] != esar[0x40 + i * 4]) {
120962306a36Sopenharmony_ci			printk(KERN_ERR "%s: Something is wrong with the "
121062306a36Sopenharmony_ci				"ethernet station address prom!\n", name);
121162306a36Sopenharmony_ci			ret = -ENODEV;
121262306a36Sopenharmony_ci			goto err_out_resource;
121362306a36Sopenharmony_ci		}
121462306a36Sopenharmony_ci	}
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_ci	/* Copy the ethernet address to the device structure, later to the
121762306a36Sopenharmony_ci	 * lance initialization block so the lance gets it every time it's
121862306a36Sopenharmony_ci	 * (re)initialized.
121962306a36Sopenharmony_ci	 */
122062306a36Sopenharmony_ci	switch (type) {
122162306a36Sopenharmony_ci	case ASIC_LANCE:
122262306a36Sopenharmony_ci		desc = "IOASIC onboard LANCE";
122362306a36Sopenharmony_ci		break;
122462306a36Sopenharmony_ci	case PMAD_LANCE:
122562306a36Sopenharmony_ci		desc = "PMAD-AA";
122662306a36Sopenharmony_ci		break;
122762306a36Sopenharmony_ci	case PMAX_LANCE:
122862306a36Sopenharmony_ci		desc = "PMAX onboard LANCE";
122962306a36Sopenharmony_ci		break;
123062306a36Sopenharmony_ci	}
123162306a36Sopenharmony_ci	for (i = 0; i < 6; i++)
123262306a36Sopenharmony_ci		addr[i] = esar[i * 4];
123362306a36Sopenharmony_ci	eth_hw_addr_set(dev, addr);
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_ci	printk("%s: %s, addr = %pM, irq = %d\n",
123662306a36Sopenharmony_ci	       name, desc, dev->dev_addr, dev->irq);
123762306a36Sopenharmony_ci
123862306a36Sopenharmony_ci	dev->netdev_ops = &lance_netdev_ops;
123962306a36Sopenharmony_ci	dev->watchdog_timeo = 5*HZ;
124062306a36Sopenharmony_ci
124162306a36Sopenharmony_ci	/* lp->ll is the location of the registers for lance card */
124262306a36Sopenharmony_ci	lp->ll = ll;
124362306a36Sopenharmony_ci
124462306a36Sopenharmony_ci	/* busmaster_regval (CSR3) should be zero according to the PMAD-AA
124562306a36Sopenharmony_ci	 * specification.
124662306a36Sopenharmony_ci	 */
124762306a36Sopenharmony_ci	lp->busmaster_regval = 0;
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_ci	dev->dma = 0;
125062306a36Sopenharmony_ci
125162306a36Sopenharmony_ci	/* We cannot sleep if the chip is busy during a
125262306a36Sopenharmony_ci	 * multicast list update event, because such events
125362306a36Sopenharmony_ci	 * can occur from interrupts (ex. IPv6).  So we
125462306a36Sopenharmony_ci	 * use a timer to try again later when necessary. -DaveM
125562306a36Sopenharmony_ci	 */
125662306a36Sopenharmony_ci	lp->dev = dev;
125762306a36Sopenharmony_ci	timer_setup(&lp->multicast_timer, lance_set_multicast_retry, 0);
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_ci	ret = register_netdev(dev);
126162306a36Sopenharmony_ci	if (ret) {
126262306a36Sopenharmony_ci		printk(KERN_ERR
126362306a36Sopenharmony_ci			"%s: Unable to register netdev, aborting.\n", name);
126462306a36Sopenharmony_ci		goto err_out_resource;
126562306a36Sopenharmony_ci	}
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_ci	if (!bdev) {
126862306a36Sopenharmony_ci		lp->next = root_lance_dev;
126962306a36Sopenharmony_ci		root_lance_dev = dev;
127062306a36Sopenharmony_ci	}
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_ci	printk("%s: registered as %s.\n", name, dev->name);
127362306a36Sopenharmony_ci	return 0;
127462306a36Sopenharmony_ci
127562306a36Sopenharmony_cierr_out_resource:
127662306a36Sopenharmony_ci	if (bdev)
127762306a36Sopenharmony_ci		release_mem_region(start, len);
127862306a36Sopenharmony_ci
127962306a36Sopenharmony_cierr_out_dev:
128062306a36Sopenharmony_ci	free_netdev(dev);
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_cierr_out:
128362306a36Sopenharmony_ci	return ret;
128462306a36Sopenharmony_ci}
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_ci/* Find all the lance cards on the system and initialize them */
128762306a36Sopenharmony_cistatic int __init dec_lance_platform_probe(void)
128862306a36Sopenharmony_ci{
128962306a36Sopenharmony_ci	int count = 0;
129062306a36Sopenharmony_ci
129162306a36Sopenharmony_ci	if (dec_interrupt[DEC_IRQ_LANCE] >= 0) {
129262306a36Sopenharmony_ci		if (dec_interrupt[DEC_IRQ_LANCE_MERR] >= 0) {
129362306a36Sopenharmony_ci			if (dec_lance_probe(NULL, ASIC_LANCE) >= 0)
129462306a36Sopenharmony_ci				count++;
129562306a36Sopenharmony_ci		} else if (!TURBOCHANNEL) {
129662306a36Sopenharmony_ci			if (dec_lance_probe(NULL, PMAX_LANCE) >= 0)
129762306a36Sopenharmony_ci				count++;
129862306a36Sopenharmony_ci		}
129962306a36Sopenharmony_ci	}
130062306a36Sopenharmony_ci
130162306a36Sopenharmony_ci	return (count > 0) ? 0 : -ENODEV;
130262306a36Sopenharmony_ci}
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_cistatic void __exit dec_lance_platform_remove(void)
130562306a36Sopenharmony_ci{
130662306a36Sopenharmony_ci	while (root_lance_dev) {
130762306a36Sopenharmony_ci		struct net_device *dev = root_lance_dev;
130862306a36Sopenharmony_ci		struct lance_private *lp = netdev_priv(dev);
130962306a36Sopenharmony_ci
131062306a36Sopenharmony_ci		unregister_netdev(dev);
131162306a36Sopenharmony_ci		root_lance_dev = lp->next;
131262306a36Sopenharmony_ci		free_netdev(dev);
131362306a36Sopenharmony_ci	}
131462306a36Sopenharmony_ci}
131562306a36Sopenharmony_ci
131662306a36Sopenharmony_ci#ifdef CONFIG_TC
131762306a36Sopenharmony_cistatic int dec_lance_tc_probe(struct device *dev);
131862306a36Sopenharmony_cistatic int dec_lance_tc_remove(struct device *dev);
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_cistatic const struct tc_device_id dec_lance_tc_table[] = {
132162306a36Sopenharmony_ci	{ "DEC     ", "PMAD-AA " },
132262306a36Sopenharmony_ci	{ }
132362306a36Sopenharmony_ci};
132462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(tc, dec_lance_tc_table);
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_cistatic struct tc_driver dec_lance_tc_driver = {
132762306a36Sopenharmony_ci	.id_table	= dec_lance_tc_table,
132862306a36Sopenharmony_ci	.driver		= {
132962306a36Sopenharmony_ci		.name	= "declance",
133062306a36Sopenharmony_ci		.bus	= &tc_bus_type,
133162306a36Sopenharmony_ci		.probe	= dec_lance_tc_probe,
133262306a36Sopenharmony_ci		.remove	= dec_lance_tc_remove,
133362306a36Sopenharmony_ci	},
133462306a36Sopenharmony_ci};
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_cistatic int dec_lance_tc_probe(struct device *dev)
133762306a36Sopenharmony_ci{
133862306a36Sopenharmony_ci        int status = dec_lance_probe(dev, PMAD_LANCE);
133962306a36Sopenharmony_ci        if (!status)
134062306a36Sopenharmony_ci                get_device(dev);
134162306a36Sopenharmony_ci        return status;
134262306a36Sopenharmony_ci}
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_cistatic void dec_lance_remove(struct device *bdev)
134562306a36Sopenharmony_ci{
134662306a36Sopenharmony_ci	struct net_device *dev = dev_get_drvdata(bdev);
134762306a36Sopenharmony_ci	resource_size_t start, len;
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_ci	unregister_netdev(dev);
135062306a36Sopenharmony_ci	start = to_tc_dev(bdev)->resource.start;
135162306a36Sopenharmony_ci	len = to_tc_dev(bdev)->resource.end - start + 1;
135262306a36Sopenharmony_ci	release_mem_region(start, len);
135362306a36Sopenharmony_ci	free_netdev(dev);
135462306a36Sopenharmony_ci}
135562306a36Sopenharmony_ci
135662306a36Sopenharmony_cistatic int dec_lance_tc_remove(struct device *dev)
135762306a36Sopenharmony_ci{
135862306a36Sopenharmony_ci        put_device(dev);
135962306a36Sopenharmony_ci        dec_lance_remove(dev);
136062306a36Sopenharmony_ci        return 0;
136162306a36Sopenharmony_ci}
136262306a36Sopenharmony_ci#endif
136362306a36Sopenharmony_ci
136462306a36Sopenharmony_cistatic int __init dec_lance_init(void)
136562306a36Sopenharmony_ci{
136662306a36Sopenharmony_ci	int status;
136762306a36Sopenharmony_ci
136862306a36Sopenharmony_ci	status = tc_register_driver(&dec_lance_tc_driver);
136962306a36Sopenharmony_ci	if (!status)
137062306a36Sopenharmony_ci		dec_lance_platform_probe();
137162306a36Sopenharmony_ci	return status;
137262306a36Sopenharmony_ci}
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_cistatic void __exit dec_lance_exit(void)
137562306a36Sopenharmony_ci{
137662306a36Sopenharmony_ci	dec_lance_platform_remove();
137762306a36Sopenharmony_ci	tc_unregister_driver(&dec_lance_tc_driver);
137862306a36Sopenharmony_ci}
137962306a36Sopenharmony_ci
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_cimodule_init(dec_lance_init);
138262306a36Sopenharmony_cimodule_exit(dec_lance_exit);
1383