18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *    Lance ethernet driver for the MIPS processor based
48c2ecf20Sopenharmony_ci *      DECstation family
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci *      adopted from sunlance.c by Richard van den Berg
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci *      Copyright (C) 2002, 2003, 2005, 2006  Maciej W. Rozycki
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci *      additional sources:
128c2ecf20Sopenharmony_ci *      - PMAD-AA TURBOchannel Ethernet Module Functional Specification,
138c2ecf20Sopenharmony_ci *        Revision 1.2
148c2ecf20Sopenharmony_ci *
158c2ecf20Sopenharmony_ci *      History:
168c2ecf20Sopenharmony_ci *
178c2ecf20Sopenharmony_ci *      v0.001: The kernel accepts the code and it shows the hardware address.
188c2ecf20Sopenharmony_ci *
198c2ecf20Sopenharmony_ci *      v0.002: Removed most sparc stuff, left only some module and dma stuff.
208c2ecf20Sopenharmony_ci *
218c2ecf20Sopenharmony_ci *      v0.003: Enhanced base address calculation from proposals by
228c2ecf20Sopenharmony_ci *              Harald Koerfgen and Thomas Riemer.
238c2ecf20Sopenharmony_ci *
248c2ecf20Sopenharmony_ci *      v0.004: lance-regs is pointing at the right addresses, added prom
258c2ecf20Sopenharmony_ci *              check. First start of address mapping and DMA.
268c2ecf20Sopenharmony_ci *
278c2ecf20Sopenharmony_ci *      v0.005: started to play around with LANCE-DMA. This driver will not
288c2ecf20Sopenharmony_ci *              work for non IOASIC lances. HK
298c2ecf20Sopenharmony_ci *
308c2ecf20Sopenharmony_ci *      v0.006: added pointer arrays to lance_private and setup routine for
318c2ecf20Sopenharmony_ci *              them in dec_lance_init. HK
328c2ecf20Sopenharmony_ci *
338c2ecf20Sopenharmony_ci *      v0.007: Big shit. The LANCE seems to use a different DMA mechanism to
348c2ecf20Sopenharmony_ci *              access the init block. This looks like one (short) word at a
358c2ecf20Sopenharmony_ci *              time, but the smallest amount the IOASIC can transfer is a
368c2ecf20Sopenharmony_ci *              (long) word. So we have a 2-2 padding here. Changed
378c2ecf20Sopenharmony_ci *              lance_init_block accordingly. The 16-16 padding for the buffers
388c2ecf20Sopenharmony_ci *              seems to be correct. HK
398c2ecf20Sopenharmony_ci *
408c2ecf20Sopenharmony_ci *      v0.008: mods to make PMAX_LANCE work. 01/09/1999 triemer
418c2ecf20Sopenharmony_ci *
428c2ecf20Sopenharmony_ci *      v0.009: Module support fixes, multiple interfaces support, various
438c2ecf20Sopenharmony_ci *              bits. macro
448c2ecf20Sopenharmony_ci *
458c2ecf20Sopenharmony_ci *      v0.010: Fixes for the PMAD mapping of the LANCE buffer and for the
468c2ecf20Sopenharmony_ci *              PMAX requirement to only use halfword accesses to the
478c2ecf20Sopenharmony_ci *              buffer. macro
488c2ecf20Sopenharmony_ci *
498c2ecf20Sopenharmony_ci *      v0.011: Converted the PMAD to the driver model. macro
508c2ecf20Sopenharmony_ci */
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci#include <linux/crc32.h>
538c2ecf20Sopenharmony_ci#include <linux/delay.h>
548c2ecf20Sopenharmony_ci#include <linux/errno.h>
558c2ecf20Sopenharmony_ci#include <linux/if_ether.h>
568c2ecf20Sopenharmony_ci#include <linux/init.h>
578c2ecf20Sopenharmony_ci#include <linux/kernel.h>
588c2ecf20Sopenharmony_ci#include <linux/module.h>
598c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
608c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
618c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
628c2ecf20Sopenharmony_ci#include <linux/stddef.h>
638c2ecf20Sopenharmony_ci#include <linux/string.h>
648c2ecf20Sopenharmony_ci#include <linux/tc.h>
658c2ecf20Sopenharmony_ci#include <linux/types.h>
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci#include <asm/addrspace.h>
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci#include <asm/dec/interrupts.h>
708c2ecf20Sopenharmony_ci#include <asm/dec/ioasic.h>
718c2ecf20Sopenharmony_ci#include <asm/dec/ioasic_addrs.h>
728c2ecf20Sopenharmony_ci#include <asm/dec/kn01.h>
738c2ecf20Sopenharmony_ci#include <asm/dec/machtype.h>
748c2ecf20Sopenharmony_ci#include <asm/dec/system.h>
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistatic const char version[] =
778c2ecf20Sopenharmony_ci"declance.c: v0.011 by Linux MIPS DECstation task force\n";
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ciMODULE_AUTHOR("Linux MIPS DECstation task force");
808c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("DEC LANCE (DECstation onboard, PMAD-xx) driver");
818c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci#define __unused __attribute__ ((unused))
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci/*
868c2ecf20Sopenharmony_ci * card types
878c2ecf20Sopenharmony_ci */
888c2ecf20Sopenharmony_ci#define ASIC_LANCE 1
898c2ecf20Sopenharmony_ci#define PMAD_LANCE 2
908c2ecf20Sopenharmony_ci#define PMAX_LANCE 3
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci#define LE_CSR0 0
948c2ecf20Sopenharmony_ci#define LE_CSR1 1
958c2ecf20Sopenharmony_ci#define LE_CSR2 2
968c2ecf20Sopenharmony_ci#define LE_CSR3 3
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci#define LE_MO_PROM      0x8000	/* Enable promiscuous mode */
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci#define	LE_C0_ERR	0x8000	/* Error: set if BAB, SQE, MISS or ME is set */
1018c2ecf20Sopenharmony_ci#define	LE_C0_BABL	0x4000	/* BAB:  Babble: tx timeout. */
1028c2ecf20Sopenharmony_ci#define	LE_C0_CERR	0x2000	/* SQE:  Signal quality error */
1038c2ecf20Sopenharmony_ci#define	LE_C0_MISS	0x1000	/* MISS: Missed a packet */
1048c2ecf20Sopenharmony_ci#define	LE_C0_MERR	0x0800	/* ME:   Memory error */
1058c2ecf20Sopenharmony_ci#define	LE_C0_RINT	0x0400	/* Received interrupt */
1068c2ecf20Sopenharmony_ci#define	LE_C0_TINT	0x0200	/* Transmitter Interrupt */
1078c2ecf20Sopenharmony_ci#define	LE_C0_IDON	0x0100	/* IFIN: Init finished. */
1088c2ecf20Sopenharmony_ci#define	LE_C0_INTR	0x0080	/* Interrupt or error */
1098c2ecf20Sopenharmony_ci#define	LE_C0_INEA	0x0040	/* Interrupt enable */
1108c2ecf20Sopenharmony_ci#define	LE_C0_RXON	0x0020	/* Receiver on */
1118c2ecf20Sopenharmony_ci#define	LE_C0_TXON	0x0010	/* Transmitter on */
1128c2ecf20Sopenharmony_ci#define	LE_C0_TDMD	0x0008	/* Transmitter demand */
1138c2ecf20Sopenharmony_ci#define	LE_C0_STOP	0x0004	/* Stop the card */
1148c2ecf20Sopenharmony_ci#define	LE_C0_STRT	0x0002	/* Start the card */
1158c2ecf20Sopenharmony_ci#define	LE_C0_INIT	0x0001	/* Init the card */
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci#define	LE_C3_BSWP	0x4	/* SWAP */
1188c2ecf20Sopenharmony_ci#define	LE_C3_ACON	0x2	/* ALE Control */
1198c2ecf20Sopenharmony_ci#define	LE_C3_BCON	0x1	/* Byte control */
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci/* Receive message descriptor 1 */
1228c2ecf20Sopenharmony_ci#define LE_R1_OWN	0x8000	/* Who owns the entry */
1238c2ecf20Sopenharmony_ci#define LE_R1_ERR	0x4000	/* Error: if FRA, OFL, CRC or BUF is set */
1248c2ecf20Sopenharmony_ci#define LE_R1_FRA	0x2000	/* FRA: Frame error */
1258c2ecf20Sopenharmony_ci#define LE_R1_OFL	0x1000	/* OFL: Frame overflow */
1268c2ecf20Sopenharmony_ci#define LE_R1_CRC	0x0800	/* CRC error */
1278c2ecf20Sopenharmony_ci#define LE_R1_BUF	0x0400	/* BUF: Buffer error */
1288c2ecf20Sopenharmony_ci#define LE_R1_SOP	0x0200	/* Start of packet */
1298c2ecf20Sopenharmony_ci#define LE_R1_EOP	0x0100	/* End of packet */
1308c2ecf20Sopenharmony_ci#define LE_R1_POK	0x0300	/* Packet is complete: SOP + EOP */
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci/* Transmit message descriptor 1 */
1338c2ecf20Sopenharmony_ci#define LE_T1_OWN	0x8000	/* Lance owns the packet */
1348c2ecf20Sopenharmony_ci#define LE_T1_ERR	0x4000	/* Error summary */
1358c2ecf20Sopenharmony_ci#define LE_T1_EMORE	0x1000	/* Error: more than one retry needed */
1368c2ecf20Sopenharmony_ci#define LE_T1_EONE	0x0800	/* Error: one retry needed */
1378c2ecf20Sopenharmony_ci#define LE_T1_EDEF	0x0400	/* Error: deferred */
1388c2ecf20Sopenharmony_ci#define LE_T1_SOP	0x0200	/* Start of packet */
1398c2ecf20Sopenharmony_ci#define LE_T1_EOP	0x0100	/* End of packet */
1408c2ecf20Sopenharmony_ci#define LE_T1_POK	0x0300	/* Packet is complete: SOP + EOP */
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci#define LE_T3_BUF       0x8000	/* Buffer error */
1438c2ecf20Sopenharmony_ci#define LE_T3_UFL       0x4000	/* Error underflow */
1448c2ecf20Sopenharmony_ci#define LE_T3_LCOL      0x1000	/* Error late collision */
1458c2ecf20Sopenharmony_ci#define LE_T3_CLOS      0x0800	/* Error carrier loss */
1468c2ecf20Sopenharmony_ci#define LE_T3_RTY       0x0400	/* Error retry */
1478c2ecf20Sopenharmony_ci#define LE_T3_TDR       0x03ff	/* Time Domain Reflectometry counter */
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci/* Define: 2^4 Tx buffers and 2^4 Rx buffers */
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci#ifndef LANCE_LOG_TX_BUFFERS
1528c2ecf20Sopenharmony_ci#define LANCE_LOG_TX_BUFFERS 4
1538c2ecf20Sopenharmony_ci#define LANCE_LOG_RX_BUFFERS 4
1548c2ecf20Sopenharmony_ci#endif
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci#define TX_RING_SIZE			(1 << (LANCE_LOG_TX_BUFFERS))
1578c2ecf20Sopenharmony_ci#define TX_RING_MOD_MASK		(TX_RING_SIZE - 1)
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci#define RX_RING_SIZE			(1 << (LANCE_LOG_RX_BUFFERS))
1608c2ecf20Sopenharmony_ci#define RX_RING_MOD_MASK		(RX_RING_SIZE - 1)
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci#define PKT_BUF_SZ		1536
1638c2ecf20Sopenharmony_ci#define RX_BUFF_SIZE            PKT_BUF_SZ
1648c2ecf20Sopenharmony_ci#define TX_BUFF_SIZE            PKT_BUF_SZ
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci#undef TEST_HITS
1678c2ecf20Sopenharmony_ci#define ZERO 0
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci/*
1708c2ecf20Sopenharmony_ci * The DS2100/3100 have a linear 64 kB buffer which supports halfword
1718c2ecf20Sopenharmony_ci * accesses only.  Each halfword of the buffer is word-aligned in the
1728c2ecf20Sopenharmony_ci * CPU address space.
1738c2ecf20Sopenharmony_ci *
1748c2ecf20Sopenharmony_ci * The PMAD-AA has a 128 kB buffer on-board.
1758c2ecf20Sopenharmony_ci *
1768c2ecf20Sopenharmony_ci * The IOASIC LANCE devices use a shared memory region.  This region
1778c2ecf20Sopenharmony_ci * as seen from the CPU is (max) 128 kB long and has to be on an 128 kB
1788c2ecf20Sopenharmony_ci * boundary.  The LANCE sees this as a 64 kB long continuous memory
1798c2ecf20Sopenharmony_ci * region.
1808c2ecf20Sopenharmony_ci *
1818c2ecf20Sopenharmony_ci * The LANCE's DMA address is used as an index in this buffer and DMA
1828c2ecf20Sopenharmony_ci * takes place in bursts of eight 16-bit words which are packed into
1838c2ecf20Sopenharmony_ci * four 32-bit words by the IOASIC.  This leads to a strange padding:
1848c2ecf20Sopenharmony_ci * 16 bytes of valid data followed by a 16 byte gap :-(.
1858c2ecf20Sopenharmony_ci */
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_cistruct lance_rx_desc {
1888c2ecf20Sopenharmony_ci	unsigned short rmd0;		/* low address of packet */
1898c2ecf20Sopenharmony_ci	unsigned short rmd1;		/* high address of packet
1908c2ecf20Sopenharmony_ci					   and descriptor bits */
1918c2ecf20Sopenharmony_ci	short length;			/* 2s complement (negative!)
1928c2ecf20Sopenharmony_ci					   of buffer length */
1938c2ecf20Sopenharmony_ci	unsigned short mblength;	/* actual number of bytes received */
1948c2ecf20Sopenharmony_ci};
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_cistruct lance_tx_desc {
1978c2ecf20Sopenharmony_ci	unsigned short tmd0;		/* low address of packet */
1988c2ecf20Sopenharmony_ci	unsigned short tmd1;		/* high address of packet
1998c2ecf20Sopenharmony_ci					   and descriptor bits */
2008c2ecf20Sopenharmony_ci	short length;			/* 2s complement (negative!)
2018c2ecf20Sopenharmony_ci					   of buffer length */
2028c2ecf20Sopenharmony_ci	unsigned short misc;
2038c2ecf20Sopenharmony_ci};
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci/* First part of the LANCE initialization block, described in databook. */
2078c2ecf20Sopenharmony_cistruct lance_init_block {
2088c2ecf20Sopenharmony_ci	unsigned short mode;		/* pre-set mode (reg. 15) */
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	unsigned short phys_addr[3];	/* physical ethernet address */
2118c2ecf20Sopenharmony_ci	unsigned short filter[4];	/* multicast filter */
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	/* Receive and transmit ring base, along with extra bits. */
2148c2ecf20Sopenharmony_ci	unsigned short rx_ptr;		/* receive descriptor addr */
2158c2ecf20Sopenharmony_ci	unsigned short rx_len;		/* receive len and high addr */
2168c2ecf20Sopenharmony_ci	unsigned short tx_ptr;		/* transmit descriptor addr */
2178c2ecf20Sopenharmony_ci	unsigned short tx_len;		/* transmit len and high addr */
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	short gap[4];
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	/* The buffer descriptors */
2228c2ecf20Sopenharmony_ci	struct lance_rx_desc brx_ring[RX_RING_SIZE];
2238c2ecf20Sopenharmony_ci	struct lance_tx_desc btx_ring[TX_RING_SIZE];
2248c2ecf20Sopenharmony_ci};
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci#define BUF_OFFSET_CPU sizeof(struct lance_init_block)
2278c2ecf20Sopenharmony_ci#define BUF_OFFSET_LNC sizeof(struct lance_init_block)
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci#define shift_off(off, type)						\
2308c2ecf20Sopenharmony_ci	(type == ASIC_LANCE || type == PMAX_LANCE ? off << 1 : off)
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci#define lib_off(rt, type)						\
2338c2ecf20Sopenharmony_ci	shift_off(offsetof(struct lance_init_block, rt), type)
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci#define lib_ptr(ib, rt, type) 						\
2368c2ecf20Sopenharmony_ci	((volatile u16 *)((u8 *)(ib) + lib_off(rt, type)))
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci#define rds_off(rt, type)						\
2398c2ecf20Sopenharmony_ci	shift_off(offsetof(struct lance_rx_desc, rt), type)
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci#define rds_ptr(rd, rt, type) 						\
2428c2ecf20Sopenharmony_ci	((volatile u16 *)((u8 *)(rd) + rds_off(rt, type)))
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci#define tds_off(rt, type)						\
2458c2ecf20Sopenharmony_ci	shift_off(offsetof(struct lance_tx_desc, rt), type)
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci#define tds_ptr(td, rt, type) 						\
2488c2ecf20Sopenharmony_ci	((volatile u16 *)((u8 *)(td) + tds_off(rt, type)))
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_cistruct lance_private {
2518c2ecf20Sopenharmony_ci	struct net_device *next;
2528c2ecf20Sopenharmony_ci	int type;
2538c2ecf20Sopenharmony_ci	int dma_irq;
2548c2ecf20Sopenharmony_ci	volatile struct lance_regs *ll;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	spinlock_t	lock;
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	int rx_new, tx_new;
2598c2ecf20Sopenharmony_ci	int rx_old, tx_old;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	unsigned short busmaster_regval;
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	struct timer_list       multicast_timer;
2648c2ecf20Sopenharmony_ci	struct net_device	*dev;
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	/* Pointers to the ring buffers as seen from the CPU */
2678c2ecf20Sopenharmony_ci	char *rx_buf_ptr_cpu[RX_RING_SIZE];
2688c2ecf20Sopenharmony_ci	char *tx_buf_ptr_cpu[TX_RING_SIZE];
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	/* Pointers to the ring buffers as seen from the LANCE */
2718c2ecf20Sopenharmony_ci	uint rx_buf_ptr_lnc[RX_RING_SIZE];
2728c2ecf20Sopenharmony_ci	uint tx_buf_ptr_lnc[TX_RING_SIZE];
2738c2ecf20Sopenharmony_ci};
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci#define TX_BUFFS_AVAIL ((lp->tx_old<=lp->tx_new)?\
2768c2ecf20Sopenharmony_ci			lp->tx_old+TX_RING_MOD_MASK-lp->tx_new:\
2778c2ecf20Sopenharmony_ci			lp->tx_old - lp->tx_new-1)
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci/* The lance control ports are at an absolute address, machine and tc-slot
2808c2ecf20Sopenharmony_ci * dependent.
2818c2ecf20Sopenharmony_ci * DECstations do only 32-bit access and the LANCE uses 16 bit addresses,
2828c2ecf20Sopenharmony_ci * so we have to give the structure an extra member making rap pointing
2838c2ecf20Sopenharmony_ci * at the right address
2848c2ecf20Sopenharmony_ci */
2858c2ecf20Sopenharmony_cistruct lance_regs {
2868c2ecf20Sopenharmony_ci	volatile unsigned short rdp;	/* register data port */
2878c2ecf20Sopenharmony_ci	unsigned short pad;
2888c2ecf20Sopenharmony_ci	volatile unsigned short rap;	/* register address port */
2898c2ecf20Sopenharmony_ci};
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ciint dec_lance_debug = 2;
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_cistatic struct tc_driver dec_lance_tc_driver;
2948c2ecf20Sopenharmony_cistatic struct net_device *root_lance_dev;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_cistatic inline void writereg(volatile unsigned short *regptr, short value)
2978c2ecf20Sopenharmony_ci{
2988c2ecf20Sopenharmony_ci	*regptr = value;
2998c2ecf20Sopenharmony_ci	iob();
3008c2ecf20Sopenharmony_ci}
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci/* Load the CSR registers */
3038c2ecf20Sopenharmony_cistatic void load_csrs(struct lance_private *lp)
3048c2ecf20Sopenharmony_ci{
3058c2ecf20Sopenharmony_ci	volatile struct lance_regs *ll = lp->ll;
3068c2ecf20Sopenharmony_ci	uint leptr;
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	/* The address space as seen from the LANCE
3098c2ecf20Sopenharmony_ci	 * begins at address 0. HK
3108c2ecf20Sopenharmony_ci	 */
3118c2ecf20Sopenharmony_ci	leptr = 0;
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	writereg(&ll->rap, LE_CSR1);
3148c2ecf20Sopenharmony_ci	writereg(&ll->rdp, (leptr & 0xFFFF));
3158c2ecf20Sopenharmony_ci	writereg(&ll->rap, LE_CSR2);
3168c2ecf20Sopenharmony_ci	writereg(&ll->rdp, leptr >> 16);
3178c2ecf20Sopenharmony_ci	writereg(&ll->rap, LE_CSR3);
3188c2ecf20Sopenharmony_ci	writereg(&ll->rdp, lp->busmaster_regval);
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	/* Point back to csr0 */
3218c2ecf20Sopenharmony_ci	writereg(&ll->rap, LE_CSR0);
3228c2ecf20Sopenharmony_ci}
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci/*
3258c2ecf20Sopenharmony_ci * Our specialized copy routines
3268c2ecf20Sopenharmony_ci *
3278c2ecf20Sopenharmony_ci */
3288c2ecf20Sopenharmony_cistatic void cp_to_buf(const int type, void *to, const void *from, int len)
3298c2ecf20Sopenharmony_ci{
3308c2ecf20Sopenharmony_ci	unsigned short *tp;
3318c2ecf20Sopenharmony_ci	const unsigned short *fp;
3328c2ecf20Sopenharmony_ci	unsigned short clen;
3338c2ecf20Sopenharmony_ci	unsigned char *rtp;
3348c2ecf20Sopenharmony_ci	const unsigned char *rfp;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	if (type == PMAD_LANCE) {
3378c2ecf20Sopenharmony_ci		memcpy(to, from, len);
3388c2ecf20Sopenharmony_ci	} else if (type == PMAX_LANCE) {
3398c2ecf20Sopenharmony_ci		clen = len >> 1;
3408c2ecf20Sopenharmony_ci		tp = to;
3418c2ecf20Sopenharmony_ci		fp = from;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci		while (clen--) {
3448c2ecf20Sopenharmony_ci			*tp++ = *fp++;
3458c2ecf20Sopenharmony_ci			tp++;
3468c2ecf20Sopenharmony_ci		}
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci		clen = len & 1;
3498c2ecf20Sopenharmony_ci		rtp = (unsigned char *)tp;
3508c2ecf20Sopenharmony_ci		rfp = (const unsigned char *)fp;
3518c2ecf20Sopenharmony_ci		while (clen--) {
3528c2ecf20Sopenharmony_ci			*rtp++ = *rfp++;
3538c2ecf20Sopenharmony_ci		}
3548c2ecf20Sopenharmony_ci	} else {
3558c2ecf20Sopenharmony_ci		/*
3568c2ecf20Sopenharmony_ci		 * copy 16 Byte chunks
3578c2ecf20Sopenharmony_ci		 */
3588c2ecf20Sopenharmony_ci		clen = len >> 4;
3598c2ecf20Sopenharmony_ci		tp = to;
3608c2ecf20Sopenharmony_ci		fp = from;
3618c2ecf20Sopenharmony_ci		while (clen--) {
3628c2ecf20Sopenharmony_ci			*tp++ = *fp++;
3638c2ecf20Sopenharmony_ci			*tp++ = *fp++;
3648c2ecf20Sopenharmony_ci			*tp++ = *fp++;
3658c2ecf20Sopenharmony_ci			*tp++ = *fp++;
3668c2ecf20Sopenharmony_ci			*tp++ = *fp++;
3678c2ecf20Sopenharmony_ci			*tp++ = *fp++;
3688c2ecf20Sopenharmony_ci			*tp++ = *fp++;
3698c2ecf20Sopenharmony_ci			*tp++ = *fp++;
3708c2ecf20Sopenharmony_ci			tp += 8;
3718c2ecf20Sopenharmony_ci		}
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci		/*
3748c2ecf20Sopenharmony_ci		 * do the rest, if any.
3758c2ecf20Sopenharmony_ci		 */
3768c2ecf20Sopenharmony_ci		clen = len & 15;
3778c2ecf20Sopenharmony_ci		rtp = (unsigned char *)tp;
3788c2ecf20Sopenharmony_ci		rfp = (const unsigned char *)fp;
3798c2ecf20Sopenharmony_ci		while (clen--) {
3808c2ecf20Sopenharmony_ci			*rtp++ = *rfp++;
3818c2ecf20Sopenharmony_ci		}
3828c2ecf20Sopenharmony_ci	}
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	iob();
3858c2ecf20Sopenharmony_ci}
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_cistatic void cp_from_buf(const int type, void *to, const void *from, int len)
3888c2ecf20Sopenharmony_ci{
3898c2ecf20Sopenharmony_ci	unsigned short *tp;
3908c2ecf20Sopenharmony_ci	const unsigned short *fp;
3918c2ecf20Sopenharmony_ci	unsigned short clen;
3928c2ecf20Sopenharmony_ci	unsigned char *rtp;
3938c2ecf20Sopenharmony_ci	const unsigned char *rfp;
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	if (type == PMAD_LANCE) {
3968c2ecf20Sopenharmony_ci		memcpy(to, from, len);
3978c2ecf20Sopenharmony_ci	} else if (type == PMAX_LANCE) {
3988c2ecf20Sopenharmony_ci		clen = len >> 1;
3998c2ecf20Sopenharmony_ci		tp = to;
4008c2ecf20Sopenharmony_ci		fp = from;
4018c2ecf20Sopenharmony_ci		while (clen--) {
4028c2ecf20Sopenharmony_ci			*tp++ = *fp++;
4038c2ecf20Sopenharmony_ci			fp++;
4048c2ecf20Sopenharmony_ci		}
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci		clen = len & 1;
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci		rtp = (unsigned char *)tp;
4098c2ecf20Sopenharmony_ci		rfp = (const unsigned char *)fp;
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci		while (clen--) {
4128c2ecf20Sopenharmony_ci			*rtp++ = *rfp++;
4138c2ecf20Sopenharmony_ci		}
4148c2ecf20Sopenharmony_ci	} else {
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci		/*
4178c2ecf20Sopenharmony_ci		 * copy 16 Byte chunks
4188c2ecf20Sopenharmony_ci		 */
4198c2ecf20Sopenharmony_ci		clen = len >> 4;
4208c2ecf20Sopenharmony_ci		tp = to;
4218c2ecf20Sopenharmony_ci		fp = from;
4228c2ecf20Sopenharmony_ci		while (clen--) {
4238c2ecf20Sopenharmony_ci			*tp++ = *fp++;
4248c2ecf20Sopenharmony_ci			*tp++ = *fp++;
4258c2ecf20Sopenharmony_ci			*tp++ = *fp++;
4268c2ecf20Sopenharmony_ci			*tp++ = *fp++;
4278c2ecf20Sopenharmony_ci			*tp++ = *fp++;
4288c2ecf20Sopenharmony_ci			*tp++ = *fp++;
4298c2ecf20Sopenharmony_ci			*tp++ = *fp++;
4308c2ecf20Sopenharmony_ci			*tp++ = *fp++;
4318c2ecf20Sopenharmony_ci			fp += 8;
4328c2ecf20Sopenharmony_ci		}
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci		/*
4358c2ecf20Sopenharmony_ci		 * do the rest, if any.
4368c2ecf20Sopenharmony_ci		 */
4378c2ecf20Sopenharmony_ci		clen = len & 15;
4388c2ecf20Sopenharmony_ci		rtp = (unsigned char *)tp;
4398c2ecf20Sopenharmony_ci		rfp = (const unsigned char *)fp;
4408c2ecf20Sopenharmony_ci		while (clen--) {
4418c2ecf20Sopenharmony_ci			*rtp++ = *rfp++;
4428c2ecf20Sopenharmony_ci		}
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	}
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci}
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci/* Setup the Lance Rx and Tx rings */
4508c2ecf20Sopenharmony_cistatic void lance_init_ring(struct net_device *dev)
4518c2ecf20Sopenharmony_ci{
4528c2ecf20Sopenharmony_ci	struct lance_private *lp = netdev_priv(dev);
4538c2ecf20Sopenharmony_ci	volatile u16 *ib = (volatile u16 *)dev->mem_start;
4548c2ecf20Sopenharmony_ci	uint leptr;
4558c2ecf20Sopenharmony_ci	int i;
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	/* Lock out other processes while setting up hardware */
4588c2ecf20Sopenharmony_ci	netif_stop_queue(dev);
4598c2ecf20Sopenharmony_ci	lp->rx_new = lp->tx_new = 0;
4608c2ecf20Sopenharmony_ci	lp->rx_old = lp->tx_old = 0;
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	/* Copy the ethernet address to the lance init block.
4638c2ecf20Sopenharmony_ci	 * XXX bit 0 of the physical address registers has to be zero
4648c2ecf20Sopenharmony_ci	 */
4658c2ecf20Sopenharmony_ci	*lib_ptr(ib, phys_addr[0], lp->type) = (dev->dev_addr[1] << 8) |
4668c2ecf20Sopenharmony_ci				     dev->dev_addr[0];
4678c2ecf20Sopenharmony_ci	*lib_ptr(ib, phys_addr[1], lp->type) = (dev->dev_addr[3] << 8) |
4688c2ecf20Sopenharmony_ci				     dev->dev_addr[2];
4698c2ecf20Sopenharmony_ci	*lib_ptr(ib, phys_addr[2], lp->type) = (dev->dev_addr[5] << 8) |
4708c2ecf20Sopenharmony_ci				     dev->dev_addr[4];
4718c2ecf20Sopenharmony_ci	/* Setup the initialization block */
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	/* Setup rx descriptor pointer */
4748c2ecf20Sopenharmony_ci	leptr = offsetof(struct lance_init_block, brx_ring);
4758c2ecf20Sopenharmony_ci	*lib_ptr(ib, rx_len, lp->type) = (LANCE_LOG_RX_BUFFERS << 13) |
4768c2ecf20Sopenharmony_ci					 (leptr >> 16);
4778c2ecf20Sopenharmony_ci	*lib_ptr(ib, rx_ptr, lp->type) = leptr;
4788c2ecf20Sopenharmony_ci	if (ZERO)
4798c2ecf20Sopenharmony_ci		printk("RX ptr: %8.8x(%8.8x)\n",
4808c2ecf20Sopenharmony_ci		       leptr, (uint)lib_off(brx_ring, lp->type));
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	/* Setup tx descriptor pointer */
4838c2ecf20Sopenharmony_ci	leptr = offsetof(struct lance_init_block, btx_ring);
4848c2ecf20Sopenharmony_ci	*lib_ptr(ib, tx_len, lp->type) = (LANCE_LOG_TX_BUFFERS << 13) |
4858c2ecf20Sopenharmony_ci					 (leptr >> 16);
4868c2ecf20Sopenharmony_ci	*lib_ptr(ib, tx_ptr, lp->type) = leptr;
4878c2ecf20Sopenharmony_ci	if (ZERO)
4888c2ecf20Sopenharmony_ci		printk("TX ptr: %8.8x(%8.8x)\n",
4898c2ecf20Sopenharmony_ci		       leptr, (uint)lib_off(btx_ring, lp->type));
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	if (ZERO)
4928c2ecf20Sopenharmony_ci		printk("TX rings:\n");
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	/* Setup the Tx ring entries */
4958c2ecf20Sopenharmony_ci	for (i = 0; i < TX_RING_SIZE; i++) {
4968c2ecf20Sopenharmony_ci		leptr = lp->tx_buf_ptr_lnc[i];
4978c2ecf20Sopenharmony_ci		*lib_ptr(ib, btx_ring[i].tmd0, lp->type) = leptr;
4988c2ecf20Sopenharmony_ci		*lib_ptr(ib, btx_ring[i].tmd1, lp->type) = (leptr >> 16) &
4998c2ecf20Sopenharmony_ci							   0xff;
5008c2ecf20Sopenharmony_ci		*lib_ptr(ib, btx_ring[i].length, lp->type) = 0xf000;
5018c2ecf20Sopenharmony_ci						/* The ones required by tmd2 */
5028c2ecf20Sopenharmony_ci		*lib_ptr(ib, btx_ring[i].misc, lp->type) = 0;
5038c2ecf20Sopenharmony_ci		if (i < 3 && ZERO)
5048c2ecf20Sopenharmony_ci			printk("%d: %8.8x(%p)\n",
5058c2ecf20Sopenharmony_ci			       i, leptr, lp->tx_buf_ptr_cpu[i]);
5068c2ecf20Sopenharmony_ci	}
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	/* Setup the Rx ring entries */
5098c2ecf20Sopenharmony_ci	if (ZERO)
5108c2ecf20Sopenharmony_ci		printk("RX rings:\n");
5118c2ecf20Sopenharmony_ci	for (i = 0; i < RX_RING_SIZE; i++) {
5128c2ecf20Sopenharmony_ci		leptr = lp->rx_buf_ptr_lnc[i];
5138c2ecf20Sopenharmony_ci		*lib_ptr(ib, brx_ring[i].rmd0, lp->type) = leptr;
5148c2ecf20Sopenharmony_ci		*lib_ptr(ib, brx_ring[i].rmd1, lp->type) = ((leptr >> 16) &
5158c2ecf20Sopenharmony_ci							    0xff) |
5168c2ecf20Sopenharmony_ci							   LE_R1_OWN;
5178c2ecf20Sopenharmony_ci		*lib_ptr(ib, brx_ring[i].length, lp->type) = -RX_BUFF_SIZE |
5188c2ecf20Sopenharmony_ci							     0xf000;
5198c2ecf20Sopenharmony_ci		*lib_ptr(ib, brx_ring[i].mblength, lp->type) = 0;
5208c2ecf20Sopenharmony_ci		if (i < 3 && ZERO)
5218c2ecf20Sopenharmony_ci			printk("%d: %8.8x(%p)\n",
5228c2ecf20Sopenharmony_ci			       i, leptr, lp->rx_buf_ptr_cpu[i]);
5238c2ecf20Sopenharmony_ci	}
5248c2ecf20Sopenharmony_ci	iob();
5258c2ecf20Sopenharmony_ci}
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_cistatic int init_restart_lance(struct lance_private *lp)
5288c2ecf20Sopenharmony_ci{
5298c2ecf20Sopenharmony_ci	volatile struct lance_regs *ll = lp->ll;
5308c2ecf20Sopenharmony_ci	int i;
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	writereg(&ll->rap, LE_CSR0);
5338c2ecf20Sopenharmony_ci	writereg(&ll->rdp, LE_C0_INIT);
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	/* Wait for the lance to complete initialization */
5368c2ecf20Sopenharmony_ci	for (i = 0; (i < 100) && !(ll->rdp & LE_C0_IDON); i++) {
5378c2ecf20Sopenharmony_ci		udelay(10);
5388c2ecf20Sopenharmony_ci	}
5398c2ecf20Sopenharmony_ci	if ((i == 100) || (ll->rdp & LE_C0_ERR)) {
5408c2ecf20Sopenharmony_ci		printk("LANCE unopened after %d ticks, csr0=%4.4x.\n",
5418c2ecf20Sopenharmony_ci		       i, ll->rdp);
5428c2ecf20Sopenharmony_ci		return -1;
5438c2ecf20Sopenharmony_ci	}
5448c2ecf20Sopenharmony_ci	if ((ll->rdp & LE_C0_ERR)) {
5458c2ecf20Sopenharmony_ci		printk("LANCE unopened after %d ticks, csr0=%4.4x.\n",
5468c2ecf20Sopenharmony_ci		       i, ll->rdp);
5478c2ecf20Sopenharmony_ci		return -1;
5488c2ecf20Sopenharmony_ci	}
5498c2ecf20Sopenharmony_ci	writereg(&ll->rdp, LE_C0_IDON);
5508c2ecf20Sopenharmony_ci	writereg(&ll->rdp, LE_C0_STRT);
5518c2ecf20Sopenharmony_ci	writereg(&ll->rdp, LE_C0_INEA);
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci	return 0;
5548c2ecf20Sopenharmony_ci}
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_cistatic int lance_rx(struct net_device *dev)
5578c2ecf20Sopenharmony_ci{
5588c2ecf20Sopenharmony_ci	struct lance_private *lp = netdev_priv(dev);
5598c2ecf20Sopenharmony_ci	volatile u16 *ib = (volatile u16 *)dev->mem_start;
5608c2ecf20Sopenharmony_ci	volatile u16 *rd;
5618c2ecf20Sopenharmony_ci	unsigned short bits;
5628c2ecf20Sopenharmony_ci	int entry, len;
5638c2ecf20Sopenharmony_ci	struct sk_buff *skb;
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci#ifdef TEST_HITS
5668c2ecf20Sopenharmony_ci	{
5678c2ecf20Sopenharmony_ci		int i;
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci		printk("[");
5708c2ecf20Sopenharmony_ci		for (i = 0; i < RX_RING_SIZE; i++) {
5718c2ecf20Sopenharmony_ci			if (i == lp->rx_new)
5728c2ecf20Sopenharmony_ci				printk("%s", *lib_ptr(ib, brx_ring[i].rmd1,
5738c2ecf20Sopenharmony_ci						      lp->type) &
5748c2ecf20Sopenharmony_ci					     LE_R1_OWN ? "_" : "X");
5758c2ecf20Sopenharmony_ci			else
5768c2ecf20Sopenharmony_ci				printk("%s", *lib_ptr(ib, brx_ring[i].rmd1,
5778c2ecf20Sopenharmony_ci						      lp->type) &
5788c2ecf20Sopenharmony_ci					     LE_R1_OWN ? "." : "1");
5798c2ecf20Sopenharmony_ci		}
5808c2ecf20Sopenharmony_ci		printk("]");
5818c2ecf20Sopenharmony_ci	}
5828c2ecf20Sopenharmony_ci#endif
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	for (rd = lib_ptr(ib, brx_ring[lp->rx_new], lp->type);
5858c2ecf20Sopenharmony_ci	     !((bits = *rds_ptr(rd, rmd1, lp->type)) & LE_R1_OWN);
5868c2ecf20Sopenharmony_ci	     rd = lib_ptr(ib, brx_ring[lp->rx_new], lp->type)) {
5878c2ecf20Sopenharmony_ci		entry = lp->rx_new;
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci		/* We got an incomplete frame? */
5908c2ecf20Sopenharmony_ci		if ((bits & LE_R1_POK) != LE_R1_POK) {
5918c2ecf20Sopenharmony_ci			dev->stats.rx_over_errors++;
5928c2ecf20Sopenharmony_ci			dev->stats.rx_errors++;
5938c2ecf20Sopenharmony_ci		} else if (bits & LE_R1_ERR) {
5948c2ecf20Sopenharmony_ci			/* Count only the end frame as a rx error,
5958c2ecf20Sopenharmony_ci			 * not the beginning
5968c2ecf20Sopenharmony_ci			 */
5978c2ecf20Sopenharmony_ci			if (bits & LE_R1_BUF)
5988c2ecf20Sopenharmony_ci				dev->stats.rx_fifo_errors++;
5998c2ecf20Sopenharmony_ci			if (bits & LE_R1_CRC)
6008c2ecf20Sopenharmony_ci				dev->stats.rx_crc_errors++;
6018c2ecf20Sopenharmony_ci			if (bits & LE_R1_OFL)
6028c2ecf20Sopenharmony_ci				dev->stats.rx_over_errors++;
6038c2ecf20Sopenharmony_ci			if (bits & LE_R1_FRA)
6048c2ecf20Sopenharmony_ci				dev->stats.rx_frame_errors++;
6058c2ecf20Sopenharmony_ci			if (bits & LE_R1_EOP)
6068c2ecf20Sopenharmony_ci				dev->stats.rx_errors++;
6078c2ecf20Sopenharmony_ci		} else {
6088c2ecf20Sopenharmony_ci			len = (*rds_ptr(rd, mblength, lp->type) & 0xfff) - 4;
6098c2ecf20Sopenharmony_ci			skb = netdev_alloc_skb(dev, len + 2);
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci			if (!skb) {
6128c2ecf20Sopenharmony_ci				dev->stats.rx_dropped++;
6138c2ecf20Sopenharmony_ci				*rds_ptr(rd, mblength, lp->type) = 0;
6148c2ecf20Sopenharmony_ci				*rds_ptr(rd, rmd1, lp->type) =
6158c2ecf20Sopenharmony_ci					((lp->rx_buf_ptr_lnc[entry] >> 16) &
6168c2ecf20Sopenharmony_ci					 0xff) | LE_R1_OWN;
6178c2ecf20Sopenharmony_ci				lp->rx_new = (entry + 1) & RX_RING_MOD_MASK;
6188c2ecf20Sopenharmony_ci				return 0;
6198c2ecf20Sopenharmony_ci			}
6208c2ecf20Sopenharmony_ci			dev->stats.rx_bytes += len;
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci			skb_reserve(skb, 2);	/* 16 byte align */
6238c2ecf20Sopenharmony_ci			skb_put(skb, len);	/* make room */
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci			cp_from_buf(lp->type, skb->data,
6268c2ecf20Sopenharmony_ci				    lp->rx_buf_ptr_cpu[entry], len);
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci			skb->protocol = eth_type_trans(skb, dev);
6298c2ecf20Sopenharmony_ci			netif_rx(skb);
6308c2ecf20Sopenharmony_ci			dev->stats.rx_packets++;
6318c2ecf20Sopenharmony_ci		}
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci		/* Return the packet to the pool */
6348c2ecf20Sopenharmony_ci		*rds_ptr(rd, mblength, lp->type) = 0;
6358c2ecf20Sopenharmony_ci		*rds_ptr(rd, length, lp->type) = -RX_BUFF_SIZE | 0xf000;
6368c2ecf20Sopenharmony_ci		*rds_ptr(rd, rmd1, lp->type) =
6378c2ecf20Sopenharmony_ci			((lp->rx_buf_ptr_lnc[entry] >> 16) & 0xff) | LE_R1_OWN;
6388c2ecf20Sopenharmony_ci		lp->rx_new = (entry + 1) & RX_RING_MOD_MASK;
6398c2ecf20Sopenharmony_ci	}
6408c2ecf20Sopenharmony_ci	return 0;
6418c2ecf20Sopenharmony_ci}
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_cistatic void lance_tx(struct net_device *dev)
6448c2ecf20Sopenharmony_ci{
6458c2ecf20Sopenharmony_ci	struct lance_private *lp = netdev_priv(dev);
6468c2ecf20Sopenharmony_ci	volatile u16 *ib = (volatile u16 *)dev->mem_start;
6478c2ecf20Sopenharmony_ci	volatile struct lance_regs *ll = lp->ll;
6488c2ecf20Sopenharmony_ci	volatile u16 *td;
6498c2ecf20Sopenharmony_ci	int i, j;
6508c2ecf20Sopenharmony_ci	int status;
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci	j = lp->tx_old;
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci	spin_lock(&lp->lock);
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	for (i = j; i != lp->tx_new; i = j) {
6578c2ecf20Sopenharmony_ci		td = lib_ptr(ib, btx_ring[i], lp->type);
6588c2ecf20Sopenharmony_ci		/* If we hit a packet not owned by us, stop */
6598c2ecf20Sopenharmony_ci		if (*tds_ptr(td, tmd1, lp->type) & LE_T1_OWN)
6608c2ecf20Sopenharmony_ci			break;
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci		if (*tds_ptr(td, tmd1, lp->type) & LE_T1_ERR) {
6638c2ecf20Sopenharmony_ci			status = *tds_ptr(td, misc, lp->type);
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci			dev->stats.tx_errors++;
6668c2ecf20Sopenharmony_ci			if (status & LE_T3_RTY)
6678c2ecf20Sopenharmony_ci				dev->stats.tx_aborted_errors++;
6688c2ecf20Sopenharmony_ci			if (status & LE_T3_LCOL)
6698c2ecf20Sopenharmony_ci				dev->stats.tx_window_errors++;
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci			if (status & LE_T3_CLOS) {
6728c2ecf20Sopenharmony_ci				dev->stats.tx_carrier_errors++;
6738c2ecf20Sopenharmony_ci				printk("%s: Carrier Lost\n", dev->name);
6748c2ecf20Sopenharmony_ci				/* Stop the lance */
6758c2ecf20Sopenharmony_ci				writereg(&ll->rap, LE_CSR0);
6768c2ecf20Sopenharmony_ci				writereg(&ll->rdp, LE_C0_STOP);
6778c2ecf20Sopenharmony_ci				lance_init_ring(dev);
6788c2ecf20Sopenharmony_ci				load_csrs(lp);
6798c2ecf20Sopenharmony_ci				init_restart_lance(lp);
6808c2ecf20Sopenharmony_ci				goto out;
6818c2ecf20Sopenharmony_ci			}
6828c2ecf20Sopenharmony_ci			/* Buffer errors and underflows turn off the
6838c2ecf20Sopenharmony_ci			 * transmitter, restart the adapter.
6848c2ecf20Sopenharmony_ci			 */
6858c2ecf20Sopenharmony_ci			if (status & (LE_T3_BUF | LE_T3_UFL)) {
6868c2ecf20Sopenharmony_ci				dev->stats.tx_fifo_errors++;
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci				printk("%s: Tx: ERR_BUF|ERR_UFL, restarting\n",
6898c2ecf20Sopenharmony_ci				       dev->name);
6908c2ecf20Sopenharmony_ci				/* Stop the lance */
6918c2ecf20Sopenharmony_ci				writereg(&ll->rap, LE_CSR0);
6928c2ecf20Sopenharmony_ci				writereg(&ll->rdp, LE_C0_STOP);
6938c2ecf20Sopenharmony_ci				lance_init_ring(dev);
6948c2ecf20Sopenharmony_ci				load_csrs(lp);
6958c2ecf20Sopenharmony_ci				init_restart_lance(lp);
6968c2ecf20Sopenharmony_ci				goto out;
6978c2ecf20Sopenharmony_ci			}
6988c2ecf20Sopenharmony_ci		} else if ((*tds_ptr(td, tmd1, lp->type) & LE_T1_POK) ==
6998c2ecf20Sopenharmony_ci			   LE_T1_POK) {
7008c2ecf20Sopenharmony_ci			/*
7018c2ecf20Sopenharmony_ci			 * So we don't count the packet more than once.
7028c2ecf20Sopenharmony_ci			 */
7038c2ecf20Sopenharmony_ci			*tds_ptr(td, tmd1, lp->type) &= ~(LE_T1_POK);
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci			/* One collision before packet was sent. */
7068c2ecf20Sopenharmony_ci			if (*tds_ptr(td, tmd1, lp->type) & LE_T1_EONE)
7078c2ecf20Sopenharmony_ci				dev->stats.collisions++;
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ci			/* More than one collision, be optimistic. */
7108c2ecf20Sopenharmony_ci			if (*tds_ptr(td, tmd1, lp->type) & LE_T1_EMORE)
7118c2ecf20Sopenharmony_ci				dev->stats.collisions += 2;
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci			dev->stats.tx_packets++;
7148c2ecf20Sopenharmony_ci		}
7158c2ecf20Sopenharmony_ci		j = (j + 1) & TX_RING_MOD_MASK;
7168c2ecf20Sopenharmony_ci	}
7178c2ecf20Sopenharmony_ci	lp->tx_old = j;
7188c2ecf20Sopenharmony_ciout:
7198c2ecf20Sopenharmony_ci	if (netif_queue_stopped(dev) &&
7208c2ecf20Sopenharmony_ci	    TX_BUFFS_AVAIL > 0)
7218c2ecf20Sopenharmony_ci		netif_wake_queue(dev);
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci	spin_unlock(&lp->lock);
7248c2ecf20Sopenharmony_ci}
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_cistatic irqreturn_t lance_dma_merr_int(int irq, void *dev_id)
7278c2ecf20Sopenharmony_ci{
7288c2ecf20Sopenharmony_ci	struct net_device *dev = dev_id;
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci	printk(KERN_ERR "%s: DMA error\n", dev->name);
7318c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
7328c2ecf20Sopenharmony_ci}
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_cistatic irqreturn_t lance_interrupt(int irq, void *dev_id)
7358c2ecf20Sopenharmony_ci{
7368c2ecf20Sopenharmony_ci	struct net_device *dev = dev_id;
7378c2ecf20Sopenharmony_ci	struct lance_private *lp = netdev_priv(dev);
7388c2ecf20Sopenharmony_ci	volatile struct lance_regs *ll = lp->ll;
7398c2ecf20Sopenharmony_ci	int csr0;
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci	writereg(&ll->rap, LE_CSR0);
7428c2ecf20Sopenharmony_ci	csr0 = ll->rdp;
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci	/* Acknowledge all the interrupt sources ASAP */
7458c2ecf20Sopenharmony_ci	writereg(&ll->rdp, csr0 & (LE_C0_INTR | LE_C0_TINT | LE_C0_RINT));
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci	if ((csr0 & LE_C0_ERR)) {
7488c2ecf20Sopenharmony_ci		/* Clear the error condition */
7498c2ecf20Sopenharmony_ci		writereg(&ll->rdp, LE_C0_BABL | LE_C0_ERR | LE_C0_MISS |
7508c2ecf20Sopenharmony_ci			 LE_C0_CERR | LE_C0_MERR);
7518c2ecf20Sopenharmony_ci	}
7528c2ecf20Sopenharmony_ci	if (csr0 & LE_C0_RINT)
7538c2ecf20Sopenharmony_ci		lance_rx(dev);
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci	if (csr0 & LE_C0_TINT)
7568c2ecf20Sopenharmony_ci		lance_tx(dev);
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci	if (csr0 & LE_C0_BABL)
7598c2ecf20Sopenharmony_ci		dev->stats.tx_errors++;
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci	if (csr0 & LE_C0_MISS)
7628c2ecf20Sopenharmony_ci		dev->stats.rx_errors++;
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	if (csr0 & LE_C0_MERR) {
7658c2ecf20Sopenharmony_ci		printk("%s: Memory error, status %04x\n", dev->name, csr0);
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci		writereg(&ll->rdp, LE_C0_STOP);
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci		lance_init_ring(dev);
7708c2ecf20Sopenharmony_ci		load_csrs(lp);
7718c2ecf20Sopenharmony_ci		init_restart_lance(lp);
7728c2ecf20Sopenharmony_ci		netif_wake_queue(dev);
7738c2ecf20Sopenharmony_ci	}
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci	writereg(&ll->rdp, LE_C0_INEA);
7768c2ecf20Sopenharmony_ci	writereg(&ll->rdp, LE_C0_INEA);
7778c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
7788c2ecf20Sopenharmony_ci}
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_cistatic int lance_open(struct net_device *dev)
7818c2ecf20Sopenharmony_ci{
7828c2ecf20Sopenharmony_ci	volatile u16 *ib = (volatile u16 *)dev->mem_start;
7838c2ecf20Sopenharmony_ci	struct lance_private *lp = netdev_priv(dev);
7848c2ecf20Sopenharmony_ci	volatile struct lance_regs *ll = lp->ll;
7858c2ecf20Sopenharmony_ci	int status = 0;
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_ci	/* Stop the Lance */
7888c2ecf20Sopenharmony_ci	writereg(&ll->rap, LE_CSR0);
7898c2ecf20Sopenharmony_ci	writereg(&ll->rdp, LE_C0_STOP);
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci	/* Set mode and clear multicast filter only at device open,
7928c2ecf20Sopenharmony_ci	 * so that lance_init_ring() called at any error will not
7938c2ecf20Sopenharmony_ci	 * forget multicast filters.
7948c2ecf20Sopenharmony_ci	 *
7958c2ecf20Sopenharmony_ci	 * BTW it is common bug in all lance drivers! --ANK
7968c2ecf20Sopenharmony_ci	 */
7978c2ecf20Sopenharmony_ci	*lib_ptr(ib, mode, lp->type) = 0;
7988c2ecf20Sopenharmony_ci	*lib_ptr(ib, filter[0], lp->type) = 0;
7998c2ecf20Sopenharmony_ci	*lib_ptr(ib, filter[1], lp->type) = 0;
8008c2ecf20Sopenharmony_ci	*lib_ptr(ib, filter[2], lp->type) = 0;
8018c2ecf20Sopenharmony_ci	*lib_ptr(ib, filter[3], lp->type) = 0;
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci	lance_init_ring(dev);
8048c2ecf20Sopenharmony_ci	load_csrs(lp);
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_ci	netif_start_queue(dev);
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci	/* Associate IRQ with lance_interrupt */
8098c2ecf20Sopenharmony_ci	if (request_irq(dev->irq, lance_interrupt, 0, "lance", dev)) {
8108c2ecf20Sopenharmony_ci		printk("%s: Can't get IRQ %d\n", dev->name, dev->irq);
8118c2ecf20Sopenharmony_ci		return -EAGAIN;
8128c2ecf20Sopenharmony_ci	}
8138c2ecf20Sopenharmony_ci	if (lp->dma_irq >= 0) {
8148c2ecf20Sopenharmony_ci		unsigned long flags;
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci		if (request_irq(lp->dma_irq, lance_dma_merr_int, IRQF_ONESHOT,
8178c2ecf20Sopenharmony_ci				"lance error", dev)) {
8188c2ecf20Sopenharmony_ci			free_irq(dev->irq, dev);
8198c2ecf20Sopenharmony_ci			printk("%s: Can't get DMA IRQ %d\n", dev->name,
8208c2ecf20Sopenharmony_ci				lp->dma_irq);
8218c2ecf20Sopenharmony_ci			return -EAGAIN;
8228c2ecf20Sopenharmony_ci		}
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_ci		spin_lock_irqsave(&ioasic_ssr_lock, flags);
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_ci		fast_mb();
8278c2ecf20Sopenharmony_ci		/* Enable I/O ASIC LANCE DMA.  */
8288c2ecf20Sopenharmony_ci		ioasic_write(IO_REG_SSR,
8298c2ecf20Sopenharmony_ci			     ioasic_read(IO_REG_SSR) | IO_SSR_LANCE_DMA_EN);
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_ci		fast_mb();
8328c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&ioasic_ssr_lock, flags);
8338c2ecf20Sopenharmony_ci	}
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_ci	status = init_restart_lance(lp);
8368c2ecf20Sopenharmony_ci	return status;
8378c2ecf20Sopenharmony_ci}
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_cistatic int lance_close(struct net_device *dev)
8408c2ecf20Sopenharmony_ci{
8418c2ecf20Sopenharmony_ci	struct lance_private *lp = netdev_priv(dev);
8428c2ecf20Sopenharmony_ci	volatile struct lance_regs *ll = lp->ll;
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci	netif_stop_queue(dev);
8458c2ecf20Sopenharmony_ci	del_timer_sync(&lp->multicast_timer);
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_ci	/* Stop the card */
8488c2ecf20Sopenharmony_ci	writereg(&ll->rap, LE_CSR0);
8498c2ecf20Sopenharmony_ci	writereg(&ll->rdp, LE_C0_STOP);
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci	if (lp->dma_irq >= 0) {
8528c2ecf20Sopenharmony_ci		unsigned long flags;
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ci		spin_lock_irqsave(&ioasic_ssr_lock, flags);
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci		fast_mb();
8578c2ecf20Sopenharmony_ci		/* Disable I/O ASIC LANCE DMA.  */
8588c2ecf20Sopenharmony_ci		ioasic_write(IO_REG_SSR,
8598c2ecf20Sopenharmony_ci			     ioasic_read(IO_REG_SSR) & ~IO_SSR_LANCE_DMA_EN);
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_ci		fast_iob();
8628c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&ioasic_ssr_lock, flags);
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_ci		free_irq(lp->dma_irq, dev);
8658c2ecf20Sopenharmony_ci	}
8668c2ecf20Sopenharmony_ci	free_irq(dev->irq, dev);
8678c2ecf20Sopenharmony_ci	return 0;
8688c2ecf20Sopenharmony_ci}
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_cistatic inline int lance_reset(struct net_device *dev)
8718c2ecf20Sopenharmony_ci{
8728c2ecf20Sopenharmony_ci	struct lance_private *lp = netdev_priv(dev);
8738c2ecf20Sopenharmony_ci	volatile struct lance_regs *ll = lp->ll;
8748c2ecf20Sopenharmony_ci	int status;
8758c2ecf20Sopenharmony_ci
8768c2ecf20Sopenharmony_ci	/* Stop the lance */
8778c2ecf20Sopenharmony_ci	writereg(&ll->rap, LE_CSR0);
8788c2ecf20Sopenharmony_ci	writereg(&ll->rdp, LE_C0_STOP);
8798c2ecf20Sopenharmony_ci
8808c2ecf20Sopenharmony_ci	lance_init_ring(dev);
8818c2ecf20Sopenharmony_ci	load_csrs(lp);
8828c2ecf20Sopenharmony_ci	netif_trans_update(dev); /* prevent tx timeout */
8838c2ecf20Sopenharmony_ci	status = init_restart_lance(lp);
8848c2ecf20Sopenharmony_ci	return status;
8858c2ecf20Sopenharmony_ci}
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_cistatic void lance_tx_timeout(struct net_device *dev, unsigned int txqueue)
8888c2ecf20Sopenharmony_ci{
8898c2ecf20Sopenharmony_ci	struct lance_private *lp = netdev_priv(dev);
8908c2ecf20Sopenharmony_ci	volatile struct lance_regs *ll = lp->ll;
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci	printk(KERN_ERR "%s: transmit timed out, status %04x, reset\n",
8938c2ecf20Sopenharmony_ci		dev->name, ll->rdp);
8948c2ecf20Sopenharmony_ci	lance_reset(dev);
8958c2ecf20Sopenharmony_ci	netif_wake_queue(dev);
8968c2ecf20Sopenharmony_ci}
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_cistatic netdev_tx_t lance_start_xmit(struct sk_buff *skb, struct net_device *dev)
8998c2ecf20Sopenharmony_ci{
9008c2ecf20Sopenharmony_ci	struct lance_private *lp = netdev_priv(dev);
9018c2ecf20Sopenharmony_ci	volatile struct lance_regs *ll = lp->ll;
9028c2ecf20Sopenharmony_ci	volatile u16 *ib = (volatile u16 *)dev->mem_start;
9038c2ecf20Sopenharmony_ci	unsigned long flags;
9048c2ecf20Sopenharmony_ci	int entry, len;
9058c2ecf20Sopenharmony_ci
9068c2ecf20Sopenharmony_ci	len = skb->len;
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ci	if (len < ETH_ZLEN) {
9098c2ecf20Sopenharmony_ci		if (skb_padto(skb, ETH_ZLEN))
9108c2ecf20Sopenharmony_ci			return NETDEV_TX_OK;
9118c2ecf20Sopenharmony_ci		len = ETH_ZLEN;
9128c2ecf20Sopenharmony_ci	}
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_ci	dev->stats.tx_bytes += len;
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_ci	spin_lock_irqsave(&lp->lock, flags);
9178c2ecf20Sopenharmony_ci
9188c2ecf20Sopenharmony_ci	entry = lp->tx_new;
9198c2ecf20Sopenharmony_ci	*lib_ptr(ib, btx_ring[entry].length, lp->type) = (-len);
9208c2ecf20Sopenharmony_ci	*lib_ptr(ib, btx_ring[entry].misc, lp->type) = 0;
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_ci	cp_to_buf(lp->type, lp->tx_buf_ptr_cpu[entry], skb->data, len);
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ci	/* Now, give the packet to the lance */
9258c2ecf20Sopenharmony_ci	*lib_ptr(ib, btx_ring[entry].tmd1, lp->type) =
9268c2ecf20Sopenharmony_ci		((lp->tx_buf_ptr_lnc[entry] >> 16) & 0xff) |
9278c2ecf20Sopenharmony_ci		(LE_T1_POK | LE_T1_OWN);
9288c2ecf20Sopenharmony_ci	lp->tx_new = (entry + 1) & TX_RING_MOD_MASK;
9298c2ecf20Sopenharmony_ci
9308c2ecf20Sopenharmony_ci	if (TX_BUFFS_AVAIL <= 0)
9318c2ecf20Sopenharmony_ci		netif_stop_queue(dev);
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_ci	/* Kick the lance: transmit now */
9348c2ecf20Sopenharmony_ci	writereg(&ll->rdp, LE_C0_INEA | LE_C0_TDMD);
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&lp->lock, flags);
9378c2ecf20Sopenharmony_ci
9388c2ecf20Sopenharmony_ci	dev_kfree_skb(skb);
9398c2ecf20Sopenharmony_ci
9408c2ecf20Sopenharmony_ci 	return NETDEV_TX_OK;
9418c2ecf20Sopenharmony_ci}
9428c2ecf20Sopenharmony_ci
9438c2ecf20Sopenharmony_cistatic void lance_load_multicast(struct net_device *dev)
9448c2ecf20Sopenharmony_ci{
9458c2ecf20Sopenharmony_ci	struct lance_private *lp = netdev_priv(dev);
9468c2ecf20Sopenharmony_ci	volatile u16 *ib = (volatile u16 *)dev->mem_start;
9478c2ecf20Sopenharmony_ci	struct netdev_hw_addr *ha;
9488c2ecf20Sopenharmony_ci	u32 crc;
9498c2ecf20Sopenharmony_ci
9508c2ecf20Sopenharmony_ci	/* set all multicast bits */
9518c2ecf20Sopenharmony_ci	if (dev->flags & IFF_ALLMULTI) {
9528c2ecf20Sopenharmony_ci		*lib_ptr(ib, filter[0], lp->type) = 0xffff;
9538c2ecf20Sopenharmony_ci		*lib_ptr(ib, filter[1], lp->type) = 0xffff;
9548c2ecf20Sopenharmony_ci		*lib_ptr(ib, filter[2], lp->type) = 0xffff;
9558c2ecf20Sopenharmony_ci		*lib_ptr(ib, filter[3], lp->type) = 0xffff;
9568c2ecf20Sopenharmony_ci		return;
9578c2ecf20Sopenharmony_ci	}
9588c2ecf20Sopenharmony_ci	/* clear the multicast filter */
9598c2ecf20Sopenharmony_ci	*lib_ptr(ib, filter[0], lp->type) = 0;
9608c2ecf20Sopenharmony_ci	*lib_ptr(ib, filter[1], lp->type) = 0;
9618c2ecf20Sopenharmony_ci	*lib_ptr(ib, filter[2], lp->type) = 0;
9628c2ecf20Sopenharmony_ci	*lib_ptr(ib, filter[3], lp->type) = 0;
9638c2ecf20Sopenharmony_ci
9648c2ecf20Sopenharmony_ci	/* Add addresses */
9658c2ecf20Sopenharmony_ci	netdev_for_each_mc_addr(ha, dev) {
9668c2ecf20Sopenharmony_ci		crc = ether_crc_le(ETH_ALEN, ha->addr);
9678c2ecf20Sopenharmony_ci		crc = crc >> 26;
9688c2ecf20Sopenharmony_ci		*lib_ptr(ib, filter[crc >> 4], lp->type) |= 1 << (crc & 0xf);
9698c2ecf20Sopenharmony_ci	}
9708c2ecf20Sopenharmony_ci}
9718c2ecf20Sopenharmony_ci
9728c2ecf20Sopenharmony_cistatic void lance_set_multicast(struct net_device *dev)
9738c2ecf20Sopenharmony_ci{
9748c2ecf20Sopenharmony_ci	struct lance_private *lp = netdev_priv(dev);
9758c2ecf20Sopenharmony_ci	volatile u16 *ib = (volatile u16 *)dev->mem_start;
9768c2ecf20Sopenharmony_ci	volatile struct lance_regs *ll = lp->ll;
9778c2ecf20Sopenharmony_ci
9788c2ecf20Sopenharmony_ci	if (!netif_running(dev))
9798c2ecf20Sopenharmony_ci		return;
9808c2ecf20Sopenharmony_ci
9818c2ecf20Sopenharmony_ci	if (lp->tx_old != lp->tx_new) {
9828c2ecf20Sopenharmony_ci		mod_timer(&lp->multicast_timer, jiffies + 4 * HZ/100);
9838c2ecf20Sopenharmony_ci		netif_wake_queue(dev);
9848c2ecf20Sopenharmony_ci		return;
9858c2ecf20Sopenharmony_ci	}
9868c2ecf20Sopenharmony_ci
9878c2ecf20Sopenharmony_ci	netif_stop_queue(dev);
9888c2ecf20Sopenharmony_ci
9898c2ecf20Sopenharmony_ci	writereg(&ll->rap, LE_CSR0);
9908c2ecf20Sopenharmony_ci	writereg(&ll->rdp, LE_C0_STOP);
9918c2ecf20Sopenharmony_ci
9928c2ecf20Sopenharmony_ci	lance_init_ring(dev);
9938c2ecf20Sopenharmony_ci
9948c2ecf20Sopenharmony_ci	if (dev->flags & IFF_PROMISC) {
9958c2ecf20Sopenharmony_ci		*lib_ptr(ib, mode, lp->type) |= LE_MO_PROM;
9968c2ecf20Sopenharmony_ci	} else {
9978c2ecf20Sopenharmony_ci		*lib_ptr(ib, mode, lp->type) &= ~LE_MO_PROM;
9988c2ecf20Sopenharmony_ci		lance_load_multicast(dev);
9998c2ecf20Sopenharmony_ci	}
10008c2ecf20Sopenharmony_ci	load_csrs(lp);
10018c2ecf20Sopenharmony_ci	init_restart_lance(lp);
10028c2ecf20Sopenharmony_ci	netif_wake_queue(dev);
10038c2ecf20Sopenharmony_ci}
10048c2ecf20Sopenharmony_ci
10058c2ecf20Sopenharmony_cistatic void lance_set_multicast_retry(struct timer_list *t)
10068c2ecf20Sopenharmony_ci{
10078c2ecf20Sopenharmony_ci	struct lance_private *lp = from_timer(lp, t, multicast_timer);
10088c2ecf20Sopenharmony_ci	struct net_device *dev = lp->dev;
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_ci	lance_set_multicast(dev);
10118c2ecf20Sopenharmony_ci}
10128c2ecf20Sopenharmony_ci
10138c2ecf20Sopenharmony_cistatic const struct net_device_ops lance_netdev_ops = {
10148c2ecf20Sopenharmony_ci	.ndo_open		= lance_open,
10158c2ecf20Sopenharmony_ci	.ndo_stop		= lance_close,
10168c2ecf20Sopenharmony_ci	.ndo_start_xmit		= lance_start_xmit,
10178c2ecf20Sopenharmony_ci	.ndo_tx_timeout		= lance_tx_timeout,
10188c2ecf20Sopenharmony_ci	.ndo_set_rx_mode	= lance_set_multicast,
10198c2ecf20Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
10208c2ecf20Sopenharmony_ci	.ndo_set_mac_address	= eth_mac_addr,
10218c2ecf20Sopenharmony_ci};
10228c2ecf20Sopenharmony_ci
10238c2ecf20Sopenharmony_cistatic int dec_lance_probe(struct device *bdev, const int type)
10248c2ecf20Sopenharmony_ci{
10258c2ecf20Sopenharmony_ci	static unsigned version_printed;
10268c2ecf20Sopenharmony_ci	static const char fmt[] = "declance%d";
10278c2ecf20Sopenharmony_ci	char name[10];
10288c2ecf20Sopenharmony_ci	struct net_device *dev;
10298c2ecf20Sopenharmony_ci	struct lance_private *lp;
10308c2ecf20Sopenharmony_ci	volatile struct lance_regs *ll;
10318c2ecf20Sopenharmony_ci	resource_size_t start = 0, len = 0;
10328c2ecf20Sopenharmony_ci	int i, ret;
10338c2ecf20Sopenharmony_ci	unsigned long esar_base;
10348c2ecf20Sopenharmony_ci	unsigned char *esar;
10358c2ecf20Sopenharmony_ci	const char *desc;
10368c2ecf20Sopenharmony_ci
10378c2ecf20Sopenharmony_ci	if (dec_lance_debug && version_printed++ == 0)
10388c2ecf20Sopenharmony_ci		printk(version);
10398c2ecf20Sopenharmony_ci
10408c2ecf20Sopenharmony_ci	if (bdev)
10418c2ecf20Sopenharmony_ci		snprintf(name, sizeof(name), "%s", dev_name(bdev));
10428c2ecf20Sopenharmony_ci	else {
10438c2ecf20Sopenharmony_ci		i = 0;
10448c2ecf20Sopenharmony_ci		dev = root_lance_dev;
10458c2ecf20Sopenharmony_ci		while (dev) {
10468c2ecf20Sopenharmony_ci			i++;
10478c2ecf20Sopenharmony_ci			lp = netdev_priv(dev);
10488c2ecf20Sopenharmony_ci			dev = lp->next;
10498c2ecf20Sopenharmony_ci		}
10508c2ecf20Sopenharmony_ci		snprintf(name, sizeof(name), fmt, i);
10518c2ecf20Sopenharmony_ci	}
10528c2ecf20Sopenharmony_ci
10538c2ecf20Sopenharmony_ci	dev = alloc_etherdev(sizeof(struct lance_private));
10548c2ecf20Sopenharmony_ci	if (!dev) {
10558c2ecf20Sopenharmony_ci		ret = -ENOMEM;
10568c2ecf20Sopenharmony_ci		goto err_out;
10578c2ecf20Sopenharmony_ci	}
10588c2ecf20Sopenharmony_ci
10598c2ecf20Sopenharmony_ci	/*
10608c2ecf20Sopenharmony_ci	 * alloc_etherdev ensures the data structures used by the LANCE
10618c2ecf20Sopenharmony_ci	 * are aligned.
10628c2ecf20Sopenharmony_ci	 */
10638c2ecf20Sopenharmony_ci	lp = netdev_priv(dev);
10648c2ecf20Sopenharmony_ci	spin_lock_init(&lp->lock);
10658c2ecf20Sopenharmony_ci
10668c2ecf20Sopenharmony_ci	lp->type = type;
10678c2ecf20Sopenharmony_ci	switch (type) {
10688c2ecf20Sopenharmony_ci	case ASIC_LANCE:
10698c2ecf20Sopenharmony_ci		dev->base_addr = CKSEG1ADDR(dec_kn_slot_base + IOASIC_LANCE);
10708c2ecf20Sopenharmony_ci
10718c2ecf20Sopenharmony_ci		/* buffer space for the on-board LANCE shared memory */
10728c2ecf20Sopenharmony_ci		/*
10738c2ecf20Sopenharmony_ci		 * FIXME: ugly hack!
10748c2ecf20Sopenharmony_ci		 */
10758c2ecf20Sopenharmony_ci		dev->mem_start = CKSEG1ADDR(0x00020000);
10768c2ecf20Sopenharmony_ci		dev->mem_end = dev->mem_start + 0x00020000;
10778c2ecf20Sopenharmony_ci		dev->irq = dec_interrupt[DEC_IRQ_LANCE];
10788c2ecf20Sopenharmony_ci		esar_base = CKSEG1ADDR(dec_kn_slot_base + IOASIC_ESAR);
10798c2ecf20Sopenharmony_ci
10808c2ecf20Sopenharmony_ci		/* Workaround crash with booting KN04 2.1k from Disk */
10818c2ecf20Sopenharmony_ci		memset((void *)dev->mem_start, 0,
10828c2ecf20Sopenharmony_ci		       dev->mem_end - dev->mem_start);
10838c2ecf20Sopenharmony_ci
10848c2ecf20Sopenharmony_ci		/*
10858c2ecf20Sopenharmony_ci		 * setup the pointer arrays, this sucks [tm] :-(
10868c2ecf20Sopenharmony_ci		 */
10878c2ecf20Sopenharmony_ci		for (i = 0; i < RX_RING_SIZE; i++) {
10888c2ecf20Sopenharmony_ci			lp->rx_buf_ptr_cpu[i] =
10898c2ecf20Sopenharmony_ci				(char *)(dev->mem_start + 2 * BUF_OFFSET_CPU +
10908c2ecf20Sopenharmony_ci					 2 * i * RX_BUFF_SIZE);
10918c2ecf20Sopenharmony_ci			lp->rx_buf_ptr_lnc[i] =
10928c2ecf20Sopenharmony_ci				(BUF_OFFSET_LNC + i * RX_BUFF_SIZE);
10938c2ecf20Sopenharmony_ci		}
10948c2ecf20Sopenharmony_ci		for (i = 0; i < TX_RING_SIZE; i++) {
10958c2ecf20Sopenharmony_ci			lp->tx_buf_ptr_cpu[i] =
10968c2ecf20Sopenharmony_ci				(char *)(dev->mem_start + 2 * BUF_OFFSET_CPU +
10978c2ecf20Sopenharmony_ci					 2 * RX_RING_SIZE * RX_BUFF_SIZE +
10988c2ecf20Sopenharmony_ci					 2 * i * TX_BUFF_SIZE);
10998c2ecf20Sopenharmony_ci			lp->tx_buf_ptr_lnc[i] =
11008c2ecf20Sopenharmony_ci				(BUF_OFFSET_LNC +
11018c2ecf20Sopenharmony_ci				 RX_RING_SIZE * RX_BUFF_SIZE +
11028c2ecf20Sopenharmony_ci				 i * TX_BUFF_SIZE);
11038c2ecf20Sopenharmony_ci		}
11048c2ecf20Sopenharmony_ci
11058c2ecf20Sopenharmony_ci		/* Setup I/O ASIC LANCE DMA.  */
11068c2ecf20Sopenharmony_ci		lp->dma_irq = dec_interrupt[DEC_IRQ_LANCE_MERR];
11078c2ecf20Sopenharmony_ci		ioasic_write(IO_REG_LANCE_DMA_P,
11088c2ecf20Sopenharmony_ci			     CPHYSADDR(dev->mem_start) << 3);
11098c2ecf20Sopenharmony_ci
11108c2ecf20Sopenharmony_ci		break;
11118c2ecf20Sopenharmony_ci#ifdef CONFIG_TC
11128c2ecf20Sopenharmony_ci	case PMAD_LANCE:
11138c2ecf20Sopenharmony_ci		dev_set_drvdata(bdev, dev);
11148c2ecf20Sopenharmony_ci
11158c2ecf20Sopenharmony_ci		start = to_tc_dev(bdev)->resource.start;
11168c2ecf20Sopenharmony_ci		len = to_tc_dev(bdev)->resource.end - start + 1;
11178c2ecf20Sopenharmony_ci		if (!request_mem_region(start, len, dev_name(bdev))) {
11188c2ecf20Sopenharmony_ci			printk(KERN_ERR
11198c2ecf20Sopenharmony_ci			       "%s: Unable to reserve MMIO resource\n",
11208c2ecf20Sopenharmony_ci			       dev_name(bdev));
11218c2ecf20Sopenharmony_ci			ret = -EBUSY;
11228c2ecf20Sopenharmony_ci			goto err_out_dev;
11238c2ecf20Sopenharmony_ci		}
11248c2ecf20Sopenharmony_ci
11258c2ecf20Sopenharmony_ci		dev->mem_start = CKSEG1ADDR(start);
11268c2ecf20Sopenharmony_ci		dev->mem_end = dev->mem_start + 0x100000;
11278c2ecf20Sopenharmony_ci		dev->base_addr = dev->mem_start + 0x100000;
11288c2ecf20Sopenharmony_ci		dev->irq = to_tc_dev(bdev)->interrupt;
11298c2ecf20Sopenharmony_ci		esar_base = dev->mem_start + 0x1c0002;
11308c2ecf20Sopenharmony_ci		lp->dma_irq = -1;
11318c2ecf20Sopenharmony_ci
11328c2ecf20Sopenharmony_ci		for (i = 0; i < RX_RING_SIZE; i++) {
11338c2ecf20Sopenharmony_ci			lp->rx_buf_ptr_cpu[i] =
11348c2ecf20Sopenharmony_ci				(char *)(dev->mem_start + BUF_OFFSET_CPU +
11358c2ecf20Sopenharmony_ci					 i * RX_BUFF_SIZE);
11368c2ecf20Sopenharmony_ci			lp->rx_buf_ptr_lnc[i] =
11378c2ecf20Sopenharmony_ci				(BUF_OFFSET_LNC + i * RX_BUFF_SIZE);
11388c2ecf20Sopenharmony_ci		}
11398c2ecf20Sopenharmony_ci		for (i = 0; i < TX_RING_SIZE; i++) {
11408c2ecf20Sopenharmony_ci			lp->tx_buf_ptr_cpu[i] =
11418c2ecf20Sopenharmony_ci				(char *)(dev->mem_start + BUF_OFFSET_CPU +
11428c2ecf20Sopenharmony_ci					 RX_RING_SIZE * RX_BUFF_SIZE +
11438c2ecf20Sopenharmony_ci					 i * TX_BUFF_SIZE);
11448c2ecf20Sopenharmony_ci			lp->tx_buf_ptr_lnc[i] =
11458c2ecf20Sopenharmony_ci				(BUF_OFFSET_LNC +
11468c2ecf20Sopenharmony_ci				 RX_RING_SIZE * RX_BUFF_SIZE +
11478c2ecf20Sopenharmony_ci				 i * TX_BUFF_SIZE);
11488c2ecf20Sopenharmony_ci		}
11498c2ecf20Sopenharmony_ci
11508c2ecf20Sopenharmony_ci		break;
11518c2ecf20Sopenharmony_ci#endif
11528c2ecf20Sopenharmony_ci	case PMAX_LANCE:
11538c2ecf20Sopenharmony_ci		dev->irq = dec_interrupt[DEC_IRQ_LANCE];
11548c2ecf20Sopenharmony_ci		dev->base_addr = CKSEG1ADDR(KN01_SLOT_BASE + KN01_LANCE);
11558c2ecf20Sopenharmony_ci		dev->mem_start = CKSEG1ADDR(KN01_SLOT_BASE + KN01_LANCE_MEM);
11568c2ecf20Sopenharmony_ci		dev->mem_end = dev->mem_start + KN01_SLOT_SIZE;
11578c2ecf20Sopenharmony_ci		esar_base = CKSEG1ADDR(KN01_SLOT_BASE + KN01_ESAR + 1);
11588c2ecf20Sopenharmony_ci		lp->dma_irq = -1;
11598c2ecf20Sopenharmony_ci
11608c2ecf20Sopenharmony_ci		/*
11618c2ecf20Sopenharmony_ci		 * setup the pointer arrays, this sucks [tm] :-(
11628c2ecf20Sopenharmony_ci		 */
11638c2ecf20Sopenharmony_ci		for (i = 0; i < RX_RING_SIZE; i++) {
11648c2ecf20Sopenharmony_ci			lp->rx_buf_ptr_cpu[i] =
11658c2ecf20Sopenharmony_ci				(char *)(dev->mem_start + 2 * BUF_OFFSET_CPU +
11668c2ecf20Sopenharmony_ci					 2 * i * RX_BUFF_SIZE);
11678c2ecf20Sopenharmony_ci			lp->rx_buf_ptr_lnc[i] =
11688c2ecf20Sopenharmony_ci				(BUF_OFFSET_LNC + i * RX_BUFF_SIZE);
11698c2ecf20Sopenharmony_ci		}
11708c2ecf20Sopenharmony_ci		for (i = 0; i < TX_RING_SIZE; i++) {
11718c2ecf20Sopenharmony_ci			lp->tx_buf_ptr_cpu[i] =
11728c2ecf20Sopenharmony_ci				(char *)(dev->mem_start + 2 * BUF_OFFSET_CPU +
11738c2ecf20Sopenharmony_ci					 2 * RX_RING_SIZE * RX_BUFF_SIZE +
11748c2ecf20Sopenharmony_ci					 2 * i * TX_BUFF_SIZE);
11758c2ecf20Sopenharmony_ci			lp->tx_buf_ptr_lnc[i] =
11768c2ecf20Sopenharmony_ci				(BUF_OFFSET_LNC +
11778c2ecf20Sopenharmony_ci				 RX_RING_SIZE * RX_BUFF_SIZE +
11788c2ecf20Sopenharmony_ci				 i * TX_BUFF_SIZE);
11798c2ecf20Sopenharmony_ci		}
11808c2ecf20Sopenharmony_ci
11818c2ecf20Sopenharmony_ci		break;
11828c2ecf20Sopenharmony_ci
11838c2ecf20Sopenharmony_ci	default:
11848c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s: declance_init called with unknown type\n",
11858c2ecf20Sopenharmony_ci			name);
11868c2ecf20Sopenharmony_ci		ret = -ENODEV;
11878c2ecf20Sopenharmony_ci		goto err_out_dev;
11888c2ecf20Sopenharmony_ci	}
11898c2ecf20Sopenharmony_ci
11908c2ecf20Sopenharmony_ci	ll = (struct lance_regs *) dev->base_addr;
11918c2ecf20Sopenharmony_ci	esar = (unsigned char *) esar_base;
11928c2ecf20Sopenharmony_ci
11938c2ecf20Sopenharmony_ci	/* prom checks */
11948c2ecf20Sopenharmony_ci	/* First, check for test pattern */
11958c2ecf20Sopenharmony_ci	if (esar[0x60] != 0xff && esar[0x64] != 0x00 &&
11968c2ecf20Sopenharmony_ci	    esar[0x68] != 0x55 && esar[0x6c] != 0xaa) {
11978c2ecf20Sopenharmony_ci		printk(KERN_ERR
11988c2ecf20Sopenharmony_ci			"%s: Ethernet station address prom not found!\n",
11998c2ecf20Sopenharmony_ci			name);
12008c2ecf20Sopenharmony_ci		ret = -ENODEV;
12018c2ecf20Sopenharmony_ci		goto err_out_resource;
12028c2ecf20Sopenharmony_ci	}
12038c2ecf20Sopenharmony_ci	/* Check the prom contents */
12048c2ecf20Sopenharmony_ci	for (i = 0; i < 8; i++) {
12058c2ecf20Sopenharmony_ci		if (esar[i * 4] != esar[0x3c - i * 4] &&
12068c2ecf20Sopenharmony_ci		    esar[i * 4] != esar[0x40 + i * 4] &&
12078c2ecf20Sopenharmony_ci		    esar[0x3c - i * 4] != esar[0x40 + i * 4]) {
12088c2ecf20Sopenharmony_ci			printk(KERN_ERR "%s: Something is wrong with the "
12098c2ecf20Sopenharmony_ci				"ethernet station address prom!\n", name);
12108c2ecf20Sopenharmony_ci			ret = -ENODEV;
12118c2ecf20Sopenharmony_ci			goto err_out_resource;
12128c2ecf20Sopenharmony_ci		}
12138c2ecf20Sopenharmony_ci	}
12148c2ecf20Sopenharmony_ci
12158c2ecf20Sopenharmony_ci	/* Copy the ethernet address to the device structure, later to the
12168c2ecf20Sopenharmony_ci	 * lance initialization block so the lance gets it every time it's
12178c2ecf20Sopenharmony_ci	 * (re)initialized.
12188c2ecf20Sopenharmony_ci	 */
12198c2ecf20Sopenharmony_ci	switch (type) {
12208c2ecf20Sopenharmony_ci	case ASIC_LANCE:
12218c2ecf20Sopenharmony_ci		desc = "IOASIC onboard LANCE";
12228c2ecf20Sopenharmony_ci		break;
12238c2ecf20Sopenharmony_ci	case PMAD_LANCE:
12248c2ecf20Sopenharmony_ci		desc = "PMAD-AA";
12258c2ecf20Sopenharmony_ci		break;
12268c2ecf20Sopenharmony_ci	case PMAX_LANCE:
12278c2ecf20Sopenharmony_ci		desc = "PMAX onboard LANCE";
12288c2ecf20Sopenharmony_ci		break;
12298c2ecf20Sopenharmony_ci	}
12308c2ecf20Sopenharmony_ci	for (i = 0; i < 6; i++)
12318c2ecf20Sopenharmony_ci		dev->dev_addr[i] = esar[i * 4];
12328c2ecf20Sopenharmony_ci
12338c2ecf20Sopenharmony_ci	printk("%s: %s, addr = %pM, irq = %d\n",
12348c2ecf20Sopenharmony_ci	       name, desc, dev->dev_addr, dev->irq);
12358c2ecf20Sopenharmony_ci
12368c2ecf20Sopenharmony_ci	dev->netdev_ops = &lance_netdev_ops;
12378c2ecf20Sopenharmony_ci	dev->watchdog_timeo = 5*HZ;
12388c2ecf20Sopenharmony_ci
12398c2ecf20Sopenharmony_ci	/* lp->ll is the location of the registers for lance card */
12408c2ecf20Sopenharmony_ci	lp->ll = ll;
12418c2ecf20Sopenharmony_ci
12428c2ecf20Sopenharmony_ci	/* busmaster_regval (CSR3) should be zero according to the PMAD-AA
12438c2ecf20Sopenharmony_ci	 * specification.
12448c2ecf20Sopenharmony_ci	 */
12458c2ecf20Sopenharmony_ci	lp->busmaster_regval = 0;
12468c2ecf20Sopenharmony_ci
12478c2ecf20Sopenharmony_ci	dev->dma = 0;
12488c2ecf20Sopenharmony_ci
12498c2ecf20Sopenharmony_ci	/* We cannot sleep if the chip is busy during a
12508c2ecf20Sopenharmony_ci	 * multicast list update event, because such events
12518c2ecf20Sopenharmony_ci	 * can occur from interrupts (ex. IPv6).  So we
12528c2ecf20Sopenharmony_ci	 * use a timer to try again later when necessary. -DaveM
12538c2ecf20Sopenharmony_ci	 */
12548c2ecf20Sopenharmony_ci	lp->dev = dev;
12558c2ecf20Sopenharmony_ci	timer_setup(&lp->multicast_timer, lance_set_multicast_retry, 0);
12568c2ecf20Sopenharmony_ci
12578c2ecf20Sopenharmony_ci
12588c2ecf20Sopenharmony_ci	ret = register_netdev(dev);
12598c2ecf20Sopenharmony_ci	if (ret) {
12608c2ecf20Sopenharmony_ci		printk(KERN_ERR
12618c2ecf20Sopenharmony_ci			"%s: Unable to register netdev, aborting.\n", name);
12628c2ecf20Sopenharmony_ci		goto err_out_resource;
12638c2ecf20Sopenharmony_ci	}
12648c2ecf20Sopenharmony_ci
12658c2ecf20Sopenharmony_ci	if (!bdev) {
12668c2ecf20Sopenharmony_ci		lp->next = root_lance_dev;
12678c2ecf20Sopenharmony_ci		root_lance_dev = dev;
12688c2ecf20Sopenharmony_ci	}
12698c2ecf20Sopenharmony_ci
12708c2ecf20Sopenharmony_ci	printk("%s: registered as %s.\n", name, dev->name);
12718c2ecf20Sopenharmony_ci	return 0;
12728c2ecf20Sopenharmony_ci
12738c2ecf20Sopenharmony_cierr_out_resource:
12748c2ecf20Sopenharmony_ci	if (bdev)
12758c2ecf20Sopenharmony_ci		release_mem_region(start, len);
12768c2ecf20Sopenharmony_ci
12778c2ecf20Sopenharmony_cierr_out_dev:
12788c2ecf20Sopenharmony_ci	free_netdev(dev);
12798c2ecf20Sopenharmony_ci
12808c2ecf20Sopenharmony_cierr_out:
12818c2ecf20Sopenharmony_ci	return ret;
12828c2ecf20Sopenharmony_ci}
12838c2ecf20Sopenharmony_ci
12848c2ecf20Sopenharmony_ci/* Find all the lance cards on the system and initialize them */
12858c2ecf20Sopenharmony_cistatic int __init dec_lance_platform_probe(void)
12868c2ecf20Sopenharmony_ci{
12878c2ecf20Sopenharmony_ci	int count = 0;
12888c2ecf20Sopenharmony_ci
12898c2ecf20Sopenharmony_ci	if (dec_interrupt[DEC_IRQ_LANCE] >= 0) {
12908c2ecf20Sopenharmony_ci		if (dec_interrupt[DEC_IRQ_LANCE_MERR] >= 0) {
12918c2ecf20Sopenharmony_ci			if (dec_lance_probe(NULL, ASIC_LANCE) >= 0)
12928c2ecf20Sopenharmony_ci				count++;
12938c2ecf20Sopenharmony_ci		} else if (!TURBOCHANNEL) {
12948c2ecf20Sopenharmony_ci			if (dec_lance_probe(NULL, PMAX_LANCE) >= 0)
12958c2ecf20Sopenharmony_ci				count++;
12968c2ecf20Sopenharmony_ci		}
12978c2ecf20Sopenharmony_ci	}
12988c2ecf20Sopenharmony_ci
12998c2ecf20Sopenharmony_ci	return (count > 0) ? 0 : -ENODEV;
13008c2ecf20Sopenharmony_ci}
13018c2ecf20Sopenharmony_ci
13028c2ecf20Sopenharmony_cistatic void __exit dec_lance_platform_remove(void)
13038c2ecf20Sopenharmony_ci{
13048c2ecf20Sopenharmony_ci	while (root_lance_dev) {
13058c2ecf20Sopenharmony_ci		struct net_device *dev = root_lance_dev;
13068c2ecf20Sopenharmony_ci		struct lance_private *lp = netdev_priv(dev);
13078c2ecf20Sopenharmony_ci
13088c2ecf20Sopenharmony_ci		unregister_netdev(dev);
13098c2ecf20Sopenharmony_ci		root_lance_dev = lp->next;
13108c2ecf20Sopenharmony_ci		free_netdev(dev);
13118c2ecf20Sopenharmony_ci	}
13128c2ecf20Sopenharmony_ci}
13138c2ecf20Sopenharmony_ci
13148c2ecf20Sopenharmony_ci#ifdef CONFIG_TC
13158c2ecf20Sopenharmony_cistatic int dec_lance_tc_probe(struct device *dev);
13168c2ecf20Sopenharmony_cistatic int dec_lance_tc_remove(struct device *dev);
13178c2ecf20Sopenharmony_ci
13188c2ecf20Sopenharmony_cistatic const struct tc_device_id dec_lance_tc_table[] = {
13198c2ecf20Sopenharmony_ci	{ "DEC     ", "PMAD-AA " },
13208c2ecf20Sopenharmony_ci	{ }
13218c2ecf20Sopenharmony_ci};
13228c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(tc, dec_lance_tc_table);
13238c2ecf20Sopenharmony_ci
13248c2ecf20Sopenharmony_cistatic struct tc_driver dec_lance_tc_driver = {
13258c2ecf20Sopenharmony_ci	.id_table	= dec_lance_tc_table,
13268c2ecf20Sopenharmony_ci	.driver		= {
13278c2ecf20Sopenharmony_ci		.name	= "declance",
13288c2ecf20Sopenharmony_ci		.bus	= &tc_bus_type,
13298c2ecf20Sopenharmony_ci		.probe	= dec_lance_tc_probe,
13308c2ecf20Sopenharmony_ci		.remove	= dec_lance_tc_remove,
13318c2ecf20Sopenharmony_ci	},
13328c2ecf20Sopenharmony_ci};
13338c2ecf20Sopenharmony_ci
13348c2ecf20Sopenharmony_cistatic int dec_lance_tc_probe(struct device *dev)
13358c2ecf20Sopenharmony_ci{
13368c2ecf20Sopenharmony_ci        int status = dec_lance_probe(dev, PMAD_LANCE);
13378c2ecf20Sopenharmony_ci        if (!status)
13388c2ecf20Sopenharmony_ci                get_device(dev);
13398c2ecf20Sopenharmony_ci        return status;
13408c2ecf20Sopenharmony_ci}
13418c2ecf20Sopenharmony_ci
13428c2ecf20Sopenharmony_cistatic void dec_lance_remove(struct device *bdev)
13438c2ecf20Sopenharmony_ci{
13448c2ecf20Sopenharmony_ci	struct net_device *dev = dev_get_drvdata(bdev);
13458c2ecf20Sopenharmony_ci	resource_size_t start, len;
13468c2ecf20Sopenharmony_ci
13478c2ecf20Sopenharmony_ci	unregister_netdev(dev);
13488c2ecf20Sopenharmony_ci	start = to_tc_dev(bdev)->resource.start;
13498c2ecf20Sopenharmony_ci	len = to_tc_dev(bdev)->resource.end - start + 1;
13508c2ecf20Sopenharmony_ci	release_mem_region(start, len);
13518c2ecf20Sopenharmony_ci	free_netdev(dev);
13528c2ecf20Sopenharmony_ci}
13538c2ecf20Sopenharmony_ci
13548c2ecf20Sopenharmony_cistatic int dec_lance_tc_remove(struct device *dev)
13558c2ecf20Sopenharmony_ci{
13568c2ecf20Sopenharmony_ci        put_device(dev);
13578c2ecf20Sopenharmony_ci        dec_lance_remove(dev);
13588c2ecf20Sopenharmony_ci        return 0;
13598c2ecf20Sopenharmony_ci}
13608c2ecf20Sopenharmony_ci#endif
13618c2ecf20Sopenharmony_ci
13628c2ecf20Sopenharmony_cistatic int __init dec_lance_init(void)
13638c2ecf20Sopenharmony_ci{
13648c2ecf20Sopenharmony_ci	int status;
13658c2ecf20Sopenharmony_ci
13668c2ecf20Sopenharmony_ci	status = tc_register_driver(&dec_lance_tc_driver);
13678c2ecf20Sopenharmony_ci	if (!status)
13688c2ecf20Sopenharmony_ci		dec_lance_platform_probe();
13698c2ecf20Sopenharmony_ci	return status;
13708c2ecf20Sopenharmony_ci}
13718c2ecf20Sopenharmony_ci
13728c2ecf20Sopenharmony_cistatic void __exit dec_lance_exit(void)
13738c2ecf20Sopenharmony_ci{
13748c2ecf20Sopenharmony_ci	dec_lance_platform_remove();
13758c2ecf20Sopenharmony_ci	tc_unregister_driver(&dec_lance_tc_driver);
13768c2ecf20Sopenharmony_ci}
13778c2ecf20Sopenharmony_ci
13788c2ecf20Sopenharmony_ci
13798c2ecf20Sopenharmony_cimodule_init(dec_lance_init);
13808c2ecf20Sopenharmony_cimodule_exit(dec_lance_exit);
1381