18c2ecf20Sopenharmony_ci/* sun3lance.c: Ethernet driver for SUN3 Lance chip */
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci
48c2ecf20Sopenharmony_ci  Sun3 Lance ethernet driver, by Sam Creasey (sammy@users.qual.net).
58c2ecf20Sopenharmony_ci  This driver is a part of the linux kernel, and is thus distributed
68c2ecf20Sopenharmony_ci  under the GNU General Public License.
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci  The values used in LANCE_OBIO and LANCE_IRQ seem to be empirically
98c2ecf20Sopenharmony_ci  true for the correct IRQ and address of the lance registers.  They
108c2ecf20Sopenharmony_ci  have not been widely tested, however.  What we probably need is a
118c2ecf20Sopenharmony_ci  "proper" way to search for a device in the sun3's prom, but, alas,
128c2ecf20Sopenharmony_ci  linux has no such thing.
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci  This driver is largely based on atarilance.c, by Roman Hodek.  Other
158c2ecf20Sopenharmony_ci  sources of inspiration were the NetBSD sun3 am7990 driver, and the
168c2ecf20Sopenharmony_ci  linux sparc lance driver (sunlance.c).
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci  There are more assumptions made throughout this driver, it almost
198c2ecf20Sopenharmony_ci  certainly still needs work, but it does work at least for RARP/BOOTP and
208c2ecf20Sopenharmony_ci  mounting the root NFS filesystem.
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci*/
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_cistatic const char version[] =
258c2ecf20Sopenharmony_ci"sun3lance.c: v1.2 1/12/2001  Sam Creasey (sammy@sammy.net)\n";
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#include <linux/module.h>
288c2ecf20Sopenharmony_ci#include <linux/stddef.h>
298c2ecf20Sopenharmony_ci#include <linux/kernel.h>
308c2ecf20Sopenharmony_ci#include <linux/string.h>
318c2ecf20Sopenharmony_ci#include <linux/errno.h>
328c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
338c2ecf20Sopenharmony_ci#include <linux/init.h>
348c2ecf20Sopenharmony_ci#include <linux/ioport.h>
358c2ecf20Sopenharmony_ci#include <linux/delay.h>
368c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
378c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
388c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
398c2ecf20Sopenharmony_ci#include <linux/bitops.h>
408c2ecf20Sopenharmony_ci#include <linux/pgtable.h>
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci#include <asm/cacheflush.h>
438c2ecf20Sopenharmony_ci#include <asm/setup.h>
448c2ecf20Sopenharmony_ci#include <asm/irq.h>
458c2ecf20Sopenharmony_ci#include <asm/io.h>
468c2ecf20Sopenharmony_ci#include <asm/dvma.h>
478c2ecf20Sopenharmony_ci#include <asm/idprom.h>
488c2ecf20Sopenharmony_ci#include <asm/machines.h>
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci#ifdef CONFIG_SUN3
518c2ecf20Sopenharmony_ci#include <asm/sun3mmu.h>
528c2ecf20Sopenharmony_ci#else
538c2ecf20Sopenharmony_ci#include <asm/sun3xprom.h>
548c2ecf20Sopenharmony_ci#endif
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci/* sun3/60 addr/irq for the lance chip.  If your sun is different,
578c2ecf20Sopenharmony_ci   change this. */
588c2ecf20Sopenharmony_ci#define LANCE_OBIO 0x120000
598c2ecf20Sopenharmony_ci#define LANCE_IRQ IRQ_AUTO_3
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci/* Debug level:
628c2ecf20Sopenharmony_ci *  0 = silent, print only serious errors
638c2ecf20Sopenharmony_ci *  1 = normal, print error messages
648c2ecf20Sopenharmony_ci *  2 = debug, print debug infos
658c2ecf20Sopenharmony_ci *  3 = debug, print even more debug infos (packet data)
668c2ecf20Sopenharmony_ci */
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci#define	LANCE_DEBUG	0
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci#ifdef LANCE_DEBUG
718c2ecf20Sopenharmony_cistatic int lance_debug = LANCE_DEBUG;
728c2ecf20Sopenharmony_ci#else
738c2ecf20Sopenharmony_cistatic int lance_debug = 1;
748c2ecf20Sopenharmony_ci#endif
758c2ecf20Sopenharmony_cimodule_param(lance_debug, int, 0);
768c2ecf20Sopenharmony_ciMODULE_PARM_DESC(lance_debug, "SUN3 Lance debug level (0-3)");
778c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci#define	DPRINTK(n,a) \
808c2ecf20Sopenharmony_ci	do {  \
818c2ecf20Sopenharmony_ci		if (lance_debug >= n)  \
828c2ecf20Sopenharmony_ci			printk a; \
838c2ecf20Sopenharmony_ci	} while( 0 )
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci/* we're only using 32k of memory, so we use 4 TX
878c2ecf20Sopenharmony_ci   buffers and 16 RX buffers.  These values are expressed as log2. */
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci#define TX_LOG_RING_SIZE			3
908c2ecf20Sopenharmony_ci#define RX_LOG_RING_SIZE			5
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci/* These are the derived values */
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci#define TX_RING_SIZE			(1 << TX_LOG_RING_SIZE)
958c2ecf20Sopenharmony_ci#define TX_RING_LEN_BITS		(TX_LOG_RING_SIZE << 5)
968c2ecf20Sopenharmony_ci#define	TX_RING_MOD_MASK		(TX_RING_SIZE - 1)
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci#define RX_RING_SIZE			(1 << RX_LOG_RING_SIZE)
998c2ecf20Sopenharmony_ci#define RX_RING_LEN_BITS		(RX_LOG_RING_SIZE << 5)
1008c2ecf20Sopenharmony_ci#define	RX_RING_MOD_MASK		(RX_RING_SIZE - 1)
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci/* Definitions for packet buffer access: */
1038c2ecf20Sopenharmony_ci#define PKT_BUF_SZ		1544
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci/* Get the address of a packet buffer corresponding to a given buffer head */
1068c2ecf20Sopenharmony_ci#define	PKTBUF_ADDR(head)	(void *)((unsigned long)(MEM) | (head)->base)
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci/* The LANCE Rx and Tx ring descriptors. */
1108c2ecf20Sopenharmony_cistruct lance_rx_head {
1118c2ecf20Sopenharmony_ci	unsigned short	base;		/* Low word of base addr */
1128c2ecf20Sopenharmony_ci	volatile unsigned char	flag;
1138c2ecf20Sopenharmony_ci	unsigned char  base_hi;	/* High word of base addr (unused) */
1148c2ecf20Sopenharmony_ci	short buf_length;	/* This length is 2s complement! */
1158c2ecf20Sopenharmony_ci	volatile short msg_length;	/* This length is "normal". */
1168c2ecf20Sopenharmony_ci};
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_cistruct lance_tx_head {
1198c2ecf20Sopenharmony_ci	unsigned short base;		/* Low word of base addr */
1208c2ecf20Sopenharmony_ci	volatile unsigned char	flag;
1218c2ecf20Sopenharmony_ci	unsigned char base_hi;	/* High word of base addr (unused) */
1228c2ecf20Sopenharmony_ci	short length;		/* Length is 2s complement! */
1238c2ecf20Sopenharmony_ci	volatile short misc;
1248c2ecf20Sopenharmony_ci};
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci/* The LANCE initialization block, described in databook. */
1278c2ecf20Sopenharmony_cistruct lance_init_block {
1288c2ecf20Sopenharmony_ci	unsigned short	mode;		/* Pre-set mode */
1298c2ecf20Sopenharmony_ci	unsigned char	hwaddr[6];	/* Physical ethernet address */
1308c2ecf20Sopenharmony_ci	unsigned int    filter[2];	/* Multicast filter (unused). */
1318c2ecf20Sopenharmony_ci	/* Receive and transmit ring base, along with length bits. */
1328c2ecf20Sopenharmony_ci	unsigned short rdra;
1338c2ecf20Sopenharmony_ci	unsigned short rlen;
1348c2ecf20Sopenharmony_ci	unsigned short tdra;
1358c2ecf20Sopenharmony_ci	unsigned short tlen;
1368c2ecf20Sopenharmony_ci	unsigned short pad[4]; /* is thie needed? */
1378c2ecf20Sopenharmony_ci};
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci/* The whole layout of the Lance shared memory */
1408c2ecf20Sopenharmony_cistruct lance_memory {
1418c2ecf20Sopenharmony_ci	struct lance_init_block	init;
1428c2ecf20Sopenharmony_ci	struct lance_tx_head	tx_head[TX_RING_SIZE];
1438c2ecf20Sopenharmony_ci	struct lance_rx_head	rx_head[RX_RING_SIZE];
1448c2ecf20Sopenharmony_ci	char   rx_data[RX_RING_SIZE][PKT_BUF_SZ];
1458c2ecf20Sopenharmony_ci	char   tx_data[TX_RING_SIZE][PKT_BUF_SZ];
1468c2ecf20Sopenharmony_ci};
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci/* The driver's private device structure */
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cistruct lance_private {
1518c2ecf20Sopenharmony_ci	volatile unsigned short	*iobase;
1528c2ecf20Sopenharmony_ci	struct lance_memory	*mem;
1538c2ecf20Sopenharmony_ci     	int new_rx, new_tx;	/* The next free ring entry */
1548c2ecf20Sopenharmony_ci	int old_tx, old_rx;     /* ring entry to be processed */
1558c2ecf20Sopenharmony_ci/* These two must be longs for set_bit() */
1568c2ecf20Sopenharmony_ci	long	    tx_full;
1578c2ecf20Sopenharmony_ci	long	    lock;
1588c2ecf20Sopenharmony_ci};
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci/* I/O register access macros */
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci#define	MEM	lp->mem
1638c2ecf20Sopenharmony_ci#define	DREG	lp->iobase[0]
1648c2ecf20Sopenharmony_ci#define	AREG	lp->iobase[1]
1658c2ecf20Sopenharmony_ci#define	REGA(a)	(*( AREG = (a), &DREG ))
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci/* Definitions for the Lance */
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci/* tx_head flags */
1708c2ecf20Sopenharmony_ci#define TMD1_ENP		0x01	/* end of packet */
1718c2ecf20Sopenharmony_ci#define TMD1_STP		0x02	/* start of packet */
1728c2ecf20Sopenharmony_ci#define TMD1_DEF		0x04	/* deferred */
1738c2ecf20Sopenharmony_ci#define TMD1_ONE		0x08	/* one retry needed */
1748c2ecf20Sopenharmony_ci#define TMD1_MORE		0x10	/* more than one retry needed */
1758c2ecf20Sopenharmony_ci#define TMD1_ERR		0x40	/* error summary */
1768c2ecf20Sopenharmony_ci#define TMD1_OWN 		0x80	/* ownership (set: chip owns) */
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci#define TMD1_OWN_CHIP	TMD1_OWN
1798c2ecf20Sopenharmony_ci#define TMD1_OWN_HOST	0
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci/* tx_head misc field */
1828c2ecf20Sopenharmony_ci#define TMD3_TDR		0x03FF	/* Time Domain Reflectometry counter */
1838c2ecf20Sopenharmony_ci#define TMD3_RTRY		0x0400	/* failed after 16 retries */
1848c2ecf20Sopenharmony_ci#define TMD3_LCAR		0x0800	/* carrier lost */
1858c2ecf20Sopenharmony_ci#define TMD3_LCOL		0x1000	/* late collision */
1868c2ecf20Sopenharmony_ci#define TMD3_UFLO		0x4000	/* underflow (late memory) */
1878c2ecf20Sopenharmony_ci#define TMD3_BUFF		0x8000	/* buffering error (no ENP) */
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci/* rx_head flags */
1908c2ecf20Sopenharmony_ci#define RMD1_ENP		0x01	/* end of packet */
1918c2ecf20Sopenharmony_ci#define RMD1_STP		0x02	/* start of packet */
1928c2ecf20Sopenharmony_ci#define RMD1_BUFF		0x04	/* buffer error */
1938c2ecf20Sopenharmony_ci#define RMD1_CRC		0x08	/* CRC error */
1948c2ecf20Sopenharmony_ci#define RMD1_OFLO		0x10	/* overflow */
1958c2ecf20Sopenharmony_ci#define RMD1_FRAM		0x20	/* framing error */
1968c2ecf20Sopenharmony_ci#define RMD1_ERR		0x40	/* error summary */
1978c2ecf20Sopenharmony_ci#define RMD1_OWN 		0x80	/* ownership (set: ship owns) */
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci#define RMD1_OWN_CHIP	RMD1_OWN
2008c2ecf20Sopenharmony_ci#define RMD1_OWN_HOST	0
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci/* register names */
2038c2ecf20Sopenharmony_ci#define CSR0	0		/* mode/status */
2048c2ecf20Sopenharmony_ci#define CSR1	1		/* init block addr (low) */
2058c2ecf20Sopenharmony_ci#define CSR2	2		/* init block addr (high) */
2068c2ecf20Sopenharmony_ci#define CSR3	3		/* misc */
2078c2ecf20Sopenharmony_ci#define CSR8	8	  	/* address filter */
2088c2ecf20Sopenharmony_ci#define CSR15	15		/* promiscuous mode */
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci/* CSR0 */
2118c2ecf20Sopenharmony_ci/* (R=readable, W=writeable, S=set on write, C=clear on write) */
2128c2ecf20Sopenharmony_ci#define CSR0_INIT	0x0001		/* initialize (RS) */
2138c2ecf20Sopenharmony_ci#define CSR0_STRT	0x0002		/* start (RS) */
2148c2ecf20Sopenharmony_ci#define CSR0_STOP	0x0004		/* stop (RS) */
2158c2ecf20Sopenharmony_ci#define CSR0_TDMD	0x0008		/* transmit demand (RS) */
2168c2ecf20Sopenharmony_ci#define CSR0_TXON	0x0010		/* transmitter on (R) */
2178c2ecf20Sopenharmony_ci#define CSR0_RXON	0x0020		/* receiver on (R) */
2188c2ecf20Sopenharmony_ci#define CSR0_INEA	0x0040		/* interrupt enable (RW) */
2198c2ecf20Sopenharmony_ci#define CSR0_INTR	0x0080		/* interrupt active (R) */
2208c2ecf20Sopenharmony_ci#define CSR0_IDON	0x0100		/* initialization done (RC) */
2218c2ecf20Sopenharmony_ci#define CSR0_TINT	0x0200		/* transmitter interrupt (RC) */
2228c2ecf20Sopenharmony_ci#define CSR0_RINT	0x0400		/* receiver interrupt (RC) */
2238c2ecf20Sopenharmony_ci#define CSR0_MERR	0x0800		/* memory error (RC) */
2248c2ecf20Sopenharmony_ci#define CSR0_MISS	0x1000		/* missed frame (RC) */
2258c2ecf20Sopenharmony_ci#define CSR0_CERR	0x2000		/* carrier error (no heartbeat :-) (RC) */
2268c2ecf20Sopenharmony_ci#define CSR0_BABL	0x4000		/* babble: tx-ed too many bits (RC) */
2278c2ecf20Sopenharmony_ci#define CSR0_ERR	0x8000		/* error (RC) */
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci/* CSR3 */
2308c2ecf20Sopenharmony_ci#define CSR3_BCON	0x0001		/* byte control */
2318c2ecf20Sopenharmony_ci#define CSR3_ACON	0x0002		/* ALE control */
2328c2ecf20Sopenharmony_ci#define CSR3_BSWP	0x0004		/* byte swap (1=big endian) */
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci/***************************** Prototypes *****************************/
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_cistatic int lance_probe( struct net_device *dev);
2378c2ecf20Sopenharmony_cistatic int lance_open( struct net_device *dev );
2388c2ecf20Sopenharmony_cistatic void lance_init_ring( struct net_device *dev );
2398c2ecf20Sopenharmony_cistatic netdev_tx_t lance_start_xmit(struct sk_buff *skb,
2408c2ecf20Sopenharmony_ci				    struct net_device *dev);
2418c2ecf20Sopenharmony_cistatic irqreturn_t lance_interrupt( int irq, void *dev_id);
2428c2ecf20Sopenharmony_cistatic int lance_rx( struct net_device *dev );
2438c2ecf20Sopenharmony_cistatic int lance_close( struct net_device *dev );
2448c2ecf20Sopenharmony_cistatic void set_multicast_list( struct net_device *dev );
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci/************************* End of Prototypes **************************/
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_cistruct net_device * __init sun3lance_probe(int unit)
2498c2ecf20Sopenharmony_ci{
2508c2ecf20Sopenharmony_ci	struct net_device *dev;
2518c2ecf20Sopenharmony_ci	static int found;
2528c2ecf20Sopenharmony_ci	int err = -ENODEV;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	if (!MACH_IS_SUN3 && !MACH_IS_SUN3X)
2558c2ecf20Sopenharmony_ci		return ERR_PTR(-ENODEV);
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	/* check that this machine has an onboard lance */
2588c2ecf20Sopenharmony_ci	switch(idprom->id_machtype) {
2598c2ecf20Sopenharmony_ci	case SM_SUN3|SM_3_50:
2608c2ecf20Sopenharmony_ci	case SM_SUN3|SM_3_60:
2618c2ecf20Sopenharmony_ci	case SM_SUN3X|SM_3_80:
2628c2ecf20Sopenharmony_ci		/* these machines have lance */
2638c2ecf20Sopenharmony_ci		break;
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	default:
2668c2ecf20Sopenharmony_ci		return ERR_PTR(-ENODEV);
2678c2ecf20Sopenharmony_ci	}
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	if (found)
2708c2ecf20Sopenharmony_ci		return ERR_PTR(-ENODEV);
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	dev = alloc_etherdev(sizeof(struct lance_private));
2738c2ecf20Sopenharmony_ci	if (!dev)
2748c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
2758c2ecf20Sopenharmony_ci	if (unit >= 0) {
2768c2ecf20Sopenharmony_ci		sprintf(dev->name, "eth%d", unit);
2778c2ecf20Sopenharmony_ci		netdev_boot_setup_check(dev);
2788c2ecf20Sopenharmony_ci	}
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	if (!lance_probe(dev))
2818c2ecf20Sopenharmony_ci		goto out;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	err = register_netdev(dev);
2848c2ecf20Sopenharmony_ci	if (err)
2858c2ecf20Sopenharmony_ci		goto out1;
2868c2ecf20Sopenharmony_ci	found = 1;
2878c2ecf20Sopenharmony_ci	return dev;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ciout1:
2908c2ecf20Sopenharmony_ci#ifdef CONFIG_SUN3
2918c2ecf20Sopenharmony_ci	iounmap((void __iomem *)dev->base_addr);
2928c2ecf20Sopenharmony_ci#endif
2938c2ecf20Sopenharmony_ciout:
2948c2ecf20Sopenharmony_ci	free_netdev(dev);
2958c2ecf20Sopenharmony_ci	return ERR_PTR(err);
2968c2ecf20Sopenharmony_ci}
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_cistatic const struct net_device_ops lance_netdev_ops = {
2998c2ecf20Sopenharmony_ci	.ndo_open		= lance_open,
3008c2ecf20Sopenharmony_ci	.ndo_stop		= lance_close,
3018c2ecf20Sopenharmony_ci	.ndo_start_xmit		= lance_start_xmit,
3028c2ecf20Sopenharmony_ci	.ndo_set_rx_mode	= set_multicast_list,
3038c2ecf20Sopenharmony_ci	.ndo_set_mac_address	= NULL,
3048c2ecf20Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
3058c2ecf20Sopenharmony_ci};
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_cistatic int __init lance_probe( struct net_device *dev)
3088c2ecf20Sopenharmony_ci{
3098c2ecf20Sopenharmony_ci	unsigned long ioaddr;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	struct lance_private	*lp;
3128c2ecf20Sopenharmony_ci	int 			i;
3138c2ecf20Sopenharmony_ci	static int 		did_version;
3148c2ecf20Sopenharmony_ci	volatile unsigned short *ioaddr_probe;
3158c2ecf20Sopenharmony_ci	unsigned short tmp1, tmp2;
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci#ifdef CONFIG_SUN3
3188c2ecf20Sopenharmony_ci	ioaddr = (unsigned long)ioremap(LANCE_OBIO, PAGE_SIZE);
3198c2ecf20Sopenharmony_ci	if (!ioaddr)
3208c2ecf20Sopenharmony_ci		return 0;
3218c2ecf20Sopenharmony_ci#else
3228c2ecf20Sopenharmony_ci	ioaddr = SUN3X_LANCE;
3238c2ecf20Sopenharmony_ci#endif
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	/* test to see if there's really a lance here */
3268c2ecf20Sopenharmony_ci	/* (CSRO_INIT shouldn't be readable) */
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	ioaddr_probe = (volatile unsigned short *)ioaddr;
3298c2ecf20Sopenharmony_ci	tmp1 = ioaddr_probe[0];
3308c2ecf20Sopenharmony_ci	tmp2 = ioaddr_probe[1];
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	ioaddr_probe[1] = CSR0;
3338c2ecf20Sopenharmony_ci	ioaddr_probe[0] = CSR0_INIT | CSR0_STOP;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	if(ioaddr_probe[0] != CSR0_STOP) {
3368c2ecf20Sopenharmony_ci		ioaddr_probe[0] = tmp1;
3378c2ecf20Sopenharmony_ci		ioaddr_probe[1] = tmp2;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci#ifdef CONFIG_SUN3
3408c2ecf20Sopenharmony_ci		iounmap((void __iomem *)ioaddr);
3418c2ecf20Sopenharmony_ci#endif
3428c2ecf20Sopenharmony_ci		return 0;
3438c2ecf20Sopenharmony_ci	}
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	lp = netdev_priv(dev);
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	/* XXX - leak? */
3488c2ecf20Sopenharmony_ci	MEM = dvma_malloc_align(sizeof(struct lance_memory), 0x10000);
3498c2ecf20Sopenharmony_ci	if (MEM == NULL) {
3508c2ecf20Sopenharmony_ci#ifdef CONFIG_SUN3
3518c2ecf20Sopenharmony_ci		iounmap((void __iomem *)ioaddr);
3528c2ecf20Sopenharmony_ci#endif
3538c2ecf20Sopenharmony_ci		printk(KERN_WARNING "SUN3 Lance couldn't allocate DVMA memory\n");
3548c2ecf20Sopenharmony_ci		return 0;
3558c2ecf20Sopenharmony_ci	}
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	lp->iobase = (volatile unsigned short *)ioaddr;
3588c2ecf20Sopenharmony_ci	dev->base_addr = (unsigned long)ioaddr; /* informational only */
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	REGA(CSR0) = CSR0_STOP;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	if (request_irq(LANCE_IRQ, lance_interrupt, 0, "SUN3 Lance", dev) < 0) {
3638c2ecf20Sopenharmony_ci#ifdef CONFIG_SUN3
3648c2ecf20Sopenharmony_ci		iounmap((void __iomem *)ioaddr);
3658c2ecf20Sopenharmony_ci#endif
3668c2ecf20Sopenharmony_ci		dvma_free((void *)MEM);
3678c2ecf20Sopenharmony_ci		printk(KERN_WARNING "SUN3 Lance unable to allocate IRQ\n");
3688c2ecf20Sopenharmony_ci		return 0;
3698c2ecf20Sopenharmony_ci	}
3708c2ecf20Sopenharmony_ci	dev->irq = (unsigned short)LANCE_IRQ;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	printk("%s: SUN3 Lance at io %#lx, mem %#lx, irq %d, hwaddr ",
3748c2ecf20Sopenharmony_ci		   dev->name,
3758c2ecf20Sopenharmony_ci		   (unsigned long)ioaddr,
3768c2ecf20Sopenharmony_ci		   (unsigned long)MEM,
3778c2ecf20Sopenharmony_ci		   dev->irq);
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	/* copy in the ethernet address from the prom */
3808c2ecf20Sopenharmony_ci	for(i = 0; i < 6 ; i++)
3818c2ecf20Sopenharmony_ci	     dev->dev_addr[i] = idprom->id_ethaddr[i];
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	/* tell the card it's ether address, bytes swapped */
3848c2ecf20Sopenharmony_ci	MEM->init.hwaddr[0] = dev->dev_addr[1];
3858c2ecf20Sopenharmony_ci	MEM->init.hwaddr[1] = dev->dev_addr[0];
3868c2ecf20Sopenharmony_ci	MEM->init.hwaddr[2] = dev->dev_addr[3];
3878c2ecf20Sopenharmony_ci	MEM->init.hwaddr[3] = dev->dev_addr[2];
3888c2ecf20Sopenharmony_ci	MEM->init.hwaddr[4] = dev->dev_addr[5];
3898c2ecf20Sopenharmony_ci	MEM->init.hwaddr[5] = dev->dev_addr[4];
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	printk("%pM\n", dev->dev_addr);
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	MEM->init.mode = 0x0000;
3948c2ecf20Sopenharmony_ci	MEM->init.filter[0] = 0x00000000;
3958c2ecf20Sopenharmony_ci	MEM->init.filter[1] = 0x00000000;
3968c2ecf20Sopenharmony_ci	MEM->init.rdra = dvma_vtob(MEM->rx_head);
3978c2ecf20Sopenharmony_ci	MEM->init.rlen    = (RX_LOG_RING_SIZE << 13) |
3988c2ecf20Sopenharmony_ci		(dvma_vtob(MEM->rx_head) >> 16);
3998c2ecf20Sopenharmony_ci	MEM->init.tdra = dvma_vtob(MEM->tx_head);
4008c2ecf20Sopenharmony_ci	MEM->init.tlen    = (TX_LOG_RING_SIZE << 13) |
4018c2ecf20Sopenharmony_ci		(dvma_vtob(MEM->tx_head) >> 16);
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	DPRINTK(2, ("initaddr: %08lx rx_ring: %08lx tx_ring: %08lx\n",
4048c2ecf20Sopenharmony_ci	       dvma_vtob(&(MEM->init)), dvma_vtob(MEM->rx_head),
4058c2ecf20Sopenharmony_ci	       (dvma_vtob(MEM->tx_head))));
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	if (did_version++ == 0)
4088c2ecf20Sopenharmony_ci		printk( version );
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	dev->netdev_ops = &lance_netdev_ops;
4118c2ecf20Sopenharmony_ci//	KLUDGE -- REMOVE ME
4128c2ecf20Sopenharmony_ci	set_bit(__LINK_STATE_PRESENT, &dev->state);
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	return 1;
4168c2ecf20Sopenharmony_ci}
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_cistatic int lance_open( struct net_device *dev )
4198c2ecf20Sopenharmony_ci{
4208c2ecf20Sopenharmony_ci	struct lance_private *lp = netdev_priv(dev);
4218c2ecf20Sopenharmony_ci	int i;
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	DPRINTK( 2, ( "%s: lance_open()\n", dev->name ));
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	REGA(CSR0) = CSR0_STOP;
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	lance_init_ring(dev);
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	/* From now on, AREG is kept to point to CSR0 */
4308c2ecf20Sopenharmony_ci	REGA(CSR0) = CSR0_INIT;
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	i = 1000000;
4338c2ecf20Sopenharmony_ci	while (--i > 0)
4348c2ecf20Sopenharmony_ci		if (DREG & CSR0_IDON)
4358c2ecf20Sopenharmony_ci			break;
4368c2ecf20Sopenharmony_ci	if (i <= 0 || (DREG & CSR0_ERR)) {
4378c2ecf20Sopenharmony_ci		DPRINTK( 2, ( "lance_open(): opening %s failed, i=%d, csr0=%04x\n",
4388c2ecf20Sopenharmony_ci					  dev->name, i, DREG ));
4398c2ecf20Sopenharmony_ci		DREG = CSR0_STOP;
4408c2ecf20Sopenharmony_ci		return -EIO;
4418c2ecf20Sopenharmony_ci	}
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	DREG = CSR0_IDON | CSR0_STRT | CSR0_INEA;
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	netif_start_queue(dev);
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	DPRINTK( 2, ( "%s: LANCE is open, csr0 %04x\n", dev->name, DREG ));
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	return 0;
4508c2ecf20Sopenharmony_ci}
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci/* Initialize the LANCE Rx and Tx rings. */
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_cistatic void lance_init_ring( struct net_device *dev )
4568c2ecf20Sopenharmony_ci{
4578c2ecf20Sopenharmony_ci	struct lance_private *lp = netdev_priv(dev);
4588c2ecf20Sopenharmony_ci	int i;
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	lp->lock = 0;
4618c2ecf20Sopenharmony_ci	lp->tx_full = 0;
4628c2ecf20Sopenharmony_ci	lp->new_rx = lp->new_tx = 0;
4638c2ecf20Sopenharmony_ci	lp->old_rx = lp->old_tx = 0;
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	for( i = 0; i < TX_RING_SIZE; i++ ) {
4668c2ecf20Sopenharmony_ci		MEM->tx_head[i].base = dvma_vtob(MEM->tx_data[i]);
4678c2ecf20Sopenharmony_ci		MEM->tx_head[i].flag = 0;
4688c2ecf20Sopenharmony_ci 		MEM->tx_head[i].base_hi =
4698c2ecf20Sopenharmony_ci			(dvma_vtob(MEM->tx_data[i])) >>16;
4708c2ecf20Sopenharmony_ci		MEM->tx_head[i].length = 0;
4718c2ecf20Sopenharmony_ci		MEM->tx_head[i].misc = 0;
4728c2ecf20Sopenharmony_ci	}
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	for( i = 0; i < RX_RING_SIZE; i++ ) {
4758c2ecf20Sopenharmony_ci		MEM->rx_head[i].base = dvma_vtob(MEM->rx_data[i]);
4768c2ecf20Sopenharmony_ci		MEM->rx_head[i].flag = RMD1_OWN_CHIP;
4778c2ecf20Sopenharmony_ci		MEM->rx_head[i].base_hi =
4788c2ecf20Sopenharmony_ci			(dvma_vtob(MEM->rx_data[i])) >> 16;
4798c2ecf20Sopenharmony_ci		MEM->rx_head[i].buf_length = -PKT_BUF_SZ | 0xf000;
4808c2ecf20Sopenharmony_ci		MEM->rx_head[i].msg_length = 0;
4818c2ecf20Sopenharmony_ci	}
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	/* tell the card it's ether address, bytes swapped */
4848c2ecf20Sopenharmony_ci	MEM->init.hwaddr[0] = dev->dev_addr[1];
4858c2ecf20Sopenharmony_ci	MEM->init.hwaddr[1] = dev->dev_addr[0];
4868c2ecf20Sopenharmony_ci	MEM->init.hwaddr[2] = dev->dev_addr[3];
4878c2ecf20Sopenharmony_ci	MEM->init.hwaddr[3] = dev->dev_addr[2];
4888c2ecf20Sopenharmony_ci	MEM->init.hwaddr[4] = dev->dev_addr[5];
4898c2ecf20Sopenharmony_ci	MEM->init.hwaddr[5] = dev->dev_addr[4];
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	MEM->init.mode = 0x0000;
4928c2ecf20Sopenharmony_ci	MEM->init.filter[0] = 0x00000000;
4938c2ecf20Sopenharmony_ci	MEM->init.filter[1] = 0x00000000;
4948c2ecf20Sopenharmony_ci	MEM->init.rdra = dvma_vtob(MEM->rx_head);
4958c2ecf20Sopenharmony_ci	MEM->init.rlen    = (RX_LOG_RING_SIZE << 13) |
4968c2ecf20Sopenharmony_ci		(dvma_vtob(MEM->rx_head) >> 16);
4978c2ecf20Sopenharmony_ci	MEM->init.tdra = dvma_vtob(MEM->tx_head);
4988c2ecf20Sopenharmony_ci	MEM->init.tlen    = (TX_LOG_RING_SIZE << 13) |
4998c2ecf20Sopenharmony_ci		(dvma_vtob(MEM->tx_head) >> 16);
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	/* tell the lance the address of its init block */
5038c2ecf20Sopenharmony_ci	REGA(CSR1) = dvma_vtob(&(MEM->init));
5048c2ecf20Sopenharmony_ci	REGA(CSR2) = dvma_vtob(&(MEM->init)) >> 16;
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci#ifdef CONFIG_SUN3X
5078c2ecf20Sopenharmony_ci	REGA(CSR3) = CSR3_BSWP | CSR3_ACON | CSR3_BCON;
5088c2ecf20Sopenharmony_ci#else
5098c2ecf20Sopenharmony_ci	REGA(CSR3) = CSR3_BSWP;
5108c2ecf20Sopenharmony_ci#endif
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci}
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_cistatic netdev_tx_t
5168c2ecf20Sopenharmony_cilance_start_xmit(struct sk_buff *skb, struct net_device *dev)
5178c2ecf20Sopenharmony_ci{
5188c2ecf20Sopenharmony_ci	struct lance_private *lp = netdev_priv(dev);
5198c2ecf20Sopenharmony_ci	int entry, len;
5208c2ecf20Sopenharmony_ci	struct lance_tx_head *head;
5218c2ecf20Sopenharmony_ci	unsigned long flags;
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	DPRINTK( 1, ( "%s: transmit start.\n",
5248c2ecf20Sopenharmony_ci		      dev->name));
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	/* Transmitter timeout, serious problems. */
5278c2ecf20Sopenharmony_ci	if (netif_queue_stopped(dev)) {
5288c2ecf20Sopenharmony_ci		int tickssofar = jiffies - dev_trans_start(dev);
5298c2ecf20Sopenharmony_ci		if (tickssofar < HZ/5)
5308c2ecf20Sopenharmony_ci			return NETDEV_TX_BUSY;
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci		DPRINTK( 1, ( "%s: transmit timed out, status %04x, resetting.\n",
5338c2ecf20Sopenharmony_ci					  dev->name, DREG ));
5348c2ecf20Sopenharmony_ci		DREG = CSR0_STOP;
5358c2ecf20Sopenharmony_ci		/*
5368c2ecf20Sopenharmony_ci		 * Always set BSWP after a STOP as STOP puts it back into
5378c2ecf20Sopenharmony_ci		 * little endian mode.
5388c2ecf20Sopenharmony_ci		 */
5398c2ecf20Sopenharmony_ci		REGA(CSR3) = CSR3_BSWP;
5408c2ecf20Sopenharmony_ci		dev->stats.tx_errors++;
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci		if(lance_debug >= 2) {
5438c2ecf20Sopenharmony_ci			int i;
5448c2ecf20Sopenharmony_ci			printk("Ring data: old_tx %d new_tx %d%s new_rx %d\n",
5458c2ecf20Sopenharmony_ci			       lp->old_tx, lp->new_tx,
5468c2ecf20Sopenharmony_ci			       lp->tx_full ? " (full)" : "",
5478c2ecf20Sopenharmony_ci			       lp->new_rx );
5488c2ecf20Sopenharmony_ci			for( i = 0 ; i < RX_RING_SIZE; i++ )
5498c2ecf20Sopenharmony_ci				printk( "rx #%d: base=%04x blen=%04x mlen=%04x\n",
5508c2ecf20Sopenharmony_ci					i, MEM->rx_head[i].base,
5518c2ecf20Sopenharmony_ci					-MEM->rx_head[i].buf_length,
5528c2ecf20Sopenharmony_ci					MEM->rx_head[i].msg_length);
5538c2ecf20Sopenharmony_ci			for( i = 0 ; i < TX_RING_SIZE; i++ )
5548c2ecf20Sopenharmony_ci				printk("tx #%d: base=%04x len=%04x misc=%04x\n",
5558c2ecf20Sopenharmony_ci				       i, MEM->tx_head[i].base,
5568c2ecf20Sopenharmony_ci				       -MEM->tx_head[i].length,
5578c2ecf20Sopenharmony_ci				       MEM->tx_head[i].misc );
5588c2ecf20Sopenharmony_ci		}
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci		lance_init_ring(dev);
5618c2ecf20Sopenharmony_ci		REGA( CSR0 ) = CSR0_INEA | CSR0_INIT | CSR0_STRT;
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci		netif_start_queue(dev);
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci		return NETDEV_TX_OK;
5668c2ecf20Sopenharmony_ci	}
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci	/* Block a timer-based transmit from overlapping.  This could better be
5708c2ecf20Sopenharmony_ci	   done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci	/* Block a timer-based transmit from overlapping with us by
5738c2ecf20Sopenharmony_ci	   stopping the queue for a bit... */
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	netif_stop_queue(dev);
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci	if (test_and_set_bit( 0, (void*)&lp->lock ) != 0) {
5788c2ecf20Sopenharmony_ci		printk( "%s: tx queue lock!.\n", dev->name);
5798c2ecf20Sopenharmony_ci		/* don't clear dev->tbusy flag. */
5808c2ecf20Sopenharmony_ci		return NETDEV_TX_BUSY;
5818c2ecf20Sopenharmony_ci	}
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	AREG = CSR0;
5848c2ecf20Sopenharmony_ci  	DPRINTK( 2, ( "%s: lance_start_xmit() called, csr0 %4.4x.\n",
5858c2ecf20Sopenharmony_ci  				  dev->name, DREG ));
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci#ifdef CONFIG_SUN3X
5888c2ecf20Sopenharmony_ci	/* this weirdness doesn't appear on sun3... */
5898c2ecf20Sopenharmony_ci	if(!(DREG & CSR0_INIT)) {
5908c2ecf20Sopenharmony_ci		DPRINTK( 1, ("INIT not set, reinitializing...\n"));
5918c2ecf20Sopenharmony_ci		REGA( CSR0 ) = CSR0_STOP;
5928c2ecf20Sopenharmony_ci		lance_init_ring(dev);
5938c2ecf20Sopenharmony_ci		REGA( CSR0 ) = CSR0_INIT | CSR0_STRT;
5948c2ecf20Sopenharmony_ci	}
5958c2ecf20Sopenharmony_ci#endif
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	/* Fill in a Tx ring entry */
5988c2ecf20Sopenharmony_ci#if 0
5998c2ecf20Sopenharmony_ci	if (lance_debug >= 2) {
6008c2ecf20Sopenharmony_ci		printk( "%s: TX pkt %d type 0x%04x"
6018c2ecf20Sopenharmony_ci			" from %s to %s"
6028c2ecf20Sopenharmony_ci			" data at 0x%08x len %d\n",
6038c2ecf20Sopenharmony_ci			dev->name, lp->new_tx, ((u_short *)skb->data)[6],
6048c2ecf20Sopenharmony_ci			DEV_ADDR(&skb->data[6]), DEV_ADDR(skb->data),
6058c2ecf20Sopenharmony_ci			(int)skb->data, (int)skb->len );
6068c2ecf20Sopenharmony_ci	}
6078c2ecf20Sopenharmony_ci#endif
6088c2ecf20Sopenharmony_ci	/* We're not prepared for the int until the last flags are set/reset.
6098c2ecf20Sopenharmony_ci	 * And the int may happen already after setting the OWN_CHIP... */
6108c2ecf20Sopenharmony_ci	local_irq_save(flags);
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci	/* Mask to ring buffer boundary. */
6138c2ecf20Sopenharmony_ci	entry = lp->new_tx;
6148c2ecf20Sopenharmony_ci	head  = &(MEM->tx_head[entry]);
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci	/* Caution: the write order is important here, set the "ownership" bits
6178c2ecf20Sopenharmony_ci	 * last.
6188c2ecf20Sopenharmony_ci	 */
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci	/* the sun3's lance needs it's buffer padded to the minimum
6218c2ecf20Sopenharmony_ci	   size */
6228c2ecf20Sopenharmony_ci	len = (ETH_ZLEN < skb->len) ? skb->len : ETH_ZLEN;
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci//	head->length = -len;
6258c2ecf20Sopenharmony_ci	head->length = (-len) | 0xf000;
6268c2ecf20Sopenharmony_ci	head->misc = 0;
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci	skb_copy_from_linear_data(skb, PKTBUF_ADDR(head), skb->len);
6298c2ecf20Sopenharmony_ci	if (len != skb->len)
6308c2ecf20Sopenharmony_ci		memset(PKTBUF_ADDR(head) + skb->len, 0, len-skb->len);
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci	head->flag = TMD1_OWN_CHIP | TMD1_ENP | TMD1_STP;
6338c2ecf20Sopenharmony_ci	lp->new_tx = (lp->new_tx + 1) & TX_RING_MOD_MASK;
6348c2ecf20Sopenharmony_ci	dev->stats.tx_bytes += skb->len;
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci	/* Trigger an immediate send poll. */
6378c2ecf20Sopenharmony_ci	REGA(CSR0) = CSR0_INEA | CSR0_TDMD | CSR0_STRT;
6388c2ecf20Sopenharmony_ci	AREG = CSR0;
6398c2ecf20Sopenharmony_ci  	DPRINTK( 2, ( "%s: lance_start_xmit() exiting, csr0 %4.4x.\n",
6408c2ecf20Sopenharmony_ci  				  dev->name, DREG ));
6418c2ecf20Sopenharmony_ci	dev_kfree_skb(skb);
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	lp->lock = 0;
6448c2ecf20Sopenharmony_ci	if ((MEM->tx_head[(entry+1) & TX_RING_MOD_MASK].flag & TMD1_OWN) ==
6458c2ecf20Sopenharmony_ci	    TMD1_OWN_HOST)
6468c2ecf20Sopenharmony_ci		netif_start_queue(dev);
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci	local_irq_restore(flags);
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci	return NETDEV_TX_OK;
6518c2ecf20Sopenharmony_ci}
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci/* The LANCE interrupt handler. */
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_cistatic irqreturn_t lance_interrupt( int irq, void *dev_id)
6568c2ecf20Sopenharmony_ci{
6578c2ecf20Sopenharmony_ci	struct net_device *dev = dev_id;
6588c2ecf20Sopenharmony_ci	struct lance_private *lp = netdev_priv(dev);
6598c2ecf20Sopenharmony_ci	int csr0;
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci still_more:
6628c2ecf20Sopenharmony_ci	flush_cache_all();
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci	AREG = CSR0;
6658c2ecf20Sopenharmony_ci	csr0 = DREG;
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci	/* ack interrupts */
6688c2ecf20Sopenharmony_ci	DREG = csr0 & (CSR0_TINT | CSR0_RINT | CSR0_IDON);
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci	/* clear errors */
6718c2ecf20Sopenharmony_ci	if(csr0 & CSR0_ERR)
6728c2ecf20Sopenharmony_ci		DREG = CSR0_BABL | CSR0_MERR | CSR0_CERR | CSR0_MISS;
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci	DPRINTK( 2, ( "%s: interrupt  csr0=%04x new csr=%04x.\n",
6768c2ecf20Sopenharmony_ci		      dev->name, csr0, DREG ));
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci	if (csr0 & CSR0_TINT) {			/* Tx-done interrupt */
6798c2ecf20Sopenharmony_ci		int old_tx = lp->old_tx;
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci//		if(lance_debug >= 3) {
6828c2ecf20Sopenharmony_ci//			int i;
6838c2ecf20Sopenharmony_ci//
6848c2ecf20Sopenharmony_ci//			printk("%s: tx int\n", dev->name);
6858c2ecf20Sopenharmony_ci//
6868c2ecf20Sopenharmony_ci//			for(i = 0; i < TX_RING_SIZE; i++)
6878c2ecf20Sopenharmony_ci//				printk("ring %d flag=%04x\n", i,
6888c2ecf20Sopenharmony_ci//				       MEM->tx_head[i].flag);
6898c2ecf20Sopenharmony_ci//		}
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci		while( old_tx != lp->new_tx) {
6928c2ecf20Sopenharmony_ci			struct lance_tx_head *head = &(MEM->tx_head[old_tx]);
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_ci			DPRINTK(3, ("on tx_ring %d\n", old_tx));
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci			if (head->flag & TMD1_OWN_CHIP)
6978c2ecf20Sopenharmony_ci				break; /* It still hasn't been Txed */
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci			if (head->flag & TMD1_ERR) {
7008c2ecf20Sopenharmony_ci				int status = head->misc;
7018c2ecf20Sopenharmony_ci				dev->stats.tx_errors++;
7028c2ecf20Sopenharmony_ci				if (status & TMD3_RTRY) dev->stats.tx_aborted_errors++;
7038c2ecf20Sopenharmony_ci				if (status & TMD3_LCAR) dev->stats.tx_carrier_errors++;
7048c2ecf20Sopenharmony_ci				if (status & TMD3_LCOL) dev->stats.tx_window_errors++;
7058c2ecf20Sopenharmony_ci				if (status & (TMD3_UFLO | TMD3_BUFF)) {
7068c2ecf20Sopenharmony_ci					dev->stats.tx_fifo_errors++;
7078c2ecf20Sopenharmony_ci					printk("%s: Tx FIFO error\n",
7088c2ecf20Sopenharmony_ci					       dev->name);
7098c2ecf20Sopenharmony_ci					REGA(CSR0) = CSR0_STOP;
7108c2ecf20Sopenharmony_ci					REGA(CSR3) = CSR3_BSWP;
7118c2ecf20Sopenharmony_ci					lance_init_ring(dev);
7128c2ecf20Sopenharmony_ci					REGA(CSR0) = CSR0_STRT | CSR0_INEA;
7138c2ecf20Sopenharmony_ci					return IRQ_HANDLED;
7148c2ecf20Sopenharmony_ci				}
7158c2ecf20Sopenharmony_ci			} else if(head->flag & (TMD1_ENP | TMD1_STP)) {
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci				head->flag &= ~(TMD1_ENP | TMD1_STP);
7188c2ecf20Sopenharmony_ci				if(head->flag & (TMD1_ONE | TMD1_MORE))
7198c2ecf20Sopenharmony_ci					dev->stats.collisions++;
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci				dev->stats.tx_packets++;
7228c2ecf20Sopenharmony_ci				DPRINTK(3, ("cleared tx ring %d\n", old_tx));
7238c2ecf20Sopenharmony_ci			}
7248c2ecf20Sopenharmony_ci			old_tx = (old_tx +1) & TX_RING_MOD_MASK;
7258c2ecf20Sopenharmony_ci		}
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci		lp->old_tx = old_tx;
7288c2ecf20Sopenharmony_ci	}
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci	if (netif_queue_stopped(dev)) {
7328c2ecf20Sopenharmony_ci		/* The ring is no longer full, clear tbusy. */
7338c2ecf20Sopenharmony_ci		netif_start_queue(dev);
7348c2ecf20Sopenharmony_ci		netif_wake_queue(dev);
7358c2ecf20Sopenharmony_ci	}
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci	if (csr0 & CSR0_RINT)			/* Rx interrupt */
7388c2ecf20Sopenharmony_ci		lance_rx( dev );
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci	/* Log misc errors. */
7418c2ecf20Sopenharmony_ci	if (csr0 & CSR0_BABL) dev->stats.tx_errors++; /* Tx babble. */
7428c2ecf20Sopenharmony_ci	if (csr0 & CSR0_MISS) dev->stats.rx_errors++; /* Missed a Rx frame. */
7438c2ecf20Sopenharmony_ci	if (csr0 & CSR0_MERR) {
7448c2ecf20Sopenharmony_ci		DPRINTK( 1, ( "%s: Bus master arbitration failure (?!?), "
7458c2ecf20Sopenharmony_ci			      "status %04x.\n", dev->name, csr0 ));
7468c2ecf20Sopenharmony_ci		/* Restart the chip. */
7478c2ecf20Sopenharmony_ci		REGA(CSR0) = CSR0_STOP;
7488c2ecf20Sopenharmony_ci		REGA(CSR3) = CSR3_BSWP;
7498c2ecf20Sopenharmony_ci		lance_init_ring(dev);
7508c2ecf20Sopenharmony_ci		REGA(CSR0) = CSR0_STRT | CSR0_INEA;
7518c2ecf20Sopenharmony_ci	}
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci    /* Clear any other interrupt, and set interrupt enable. */
7558c2ecf20Sopenharmony_ci//	DREG = CSR0_BABL | CSR0_CERR | CSR0_MISS | CSR0_MERR |
7568c2ecf20Sopenharmony_ci//		   CSR0_IDON | CSR0_INEA;
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci	REGA(CSR0) = CSR0_INEA;
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci	if(DREG & (CSR0_RINT | CSR0_TINT)) {
7618c2ecf20Sopenharmony_ci	     DPRINTK(2, ("restarting interrupt, csr0=%#04x\n", DREG));
7628c2ecf20Sopenharmony_ci	     goto still_more;
7638c2ecf20Sopenharmony_ci	}
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ci	DPRINTK( 2, ( "%s: exiting interrupt, csr0=%#04x.\n",
7668c2ecf20Sopenharmony_ci				  dev->name, DREG ));
7678c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
7688c2ecf20Sopenharmony_ci}
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci/* get packet, toss into skbuff */
7718c2ecf20Sopenharmony_cistatic int lance_rx( struct net_device *dev )
7728c2ecf20Sopenharmony_ci{
7738c2ecf20Sopenharmony_ci	struct lance_private *lp = netdev_priv(dev);
7748c2ecf20Sopenharmony_ci	int entry = lp->new_rx;
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci	/* If we own the next entry, it's a new packet. Send it up. */
7778c2ecf20Sopenharmony_ci	while( (MEM->rx_head[entry].flag & RMD1_OWN) == RMD1_OWN_HOST ) {
7788c2ecf20Sopenharmony_ci		struct lance_rx_head *head = &(MEM->rx_head[entry]);
7798c2ecf20Sopenharmony_ci		int status = head->flag;
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci		if (status != (RMD1_ENP|RMD1_STP)) {  /* There was an error. */
7828c2ecf20Sopenharmony_ci			/* There is a tricky error noted by John Murphy,
7838c2ecf20Sopenharmony_ci			   <murf@perftech.com> to Russ Nelson: Even with
7848c2ecf20Sopenharmony_ci			   full-sized buffers it's possible for a jabber packet to use two
7858c2ecf20Sopenharmony_ci			   buffers, with only the last correctly noting the error. */
7868c2ecf20Sopenharmony_ci			if (status & RMD1_ENP)	/* Only count a general error at the */
7878c2ecf20Sopenharmony_ci				dev->stats.rx_errors++; /* end of a packet.*/
7888c2ecf20Sopenharmony_ci			if (status & RMD1_FRAM) dev->stats.rx_frame_errors++;
7898c2ecf20Sopenharmony_ci			if (status & RMD1_OFLO) dev->stats.rx_over_errors++;
7908c2ecf20Sopenharmony_ci			if (status & RMD1_CRC) dev->stats.rx_crc_errors++;
7918c2ecf20Sopenharmony_ci			if (status & RMD1_BUFF) dev->stats.rx_fifo_errors++;
7928c2ecf20Sopenharmony_ci			head->flag &= (RMD1_ENP|RMD1_STP);
7938c2ecf20Sopenharmony_ci		} else {
7948c2ecf20Sopenharmony_ci			/* Malloc up new buffer, compatible with net-3. */
7958c2ecf20Sopenharmony_ci//			short pkt_len = head->msg_length;// & 0xfff;
7968c2ecf20Sopenharmony_ci			short pkt_len = (head->msg_length & 0xfff) - 4;
7978c2ecf20Sopenharmony_ci			struct sk_buff *skb;
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci			if (pkt_len < 60) {
8008c2ecf20Sopenharmony_ci				printk( "%s: Runt packet!\n", dev->name );
8018c2ecf20Sopenharmony_ci				dev->stats.rx_errors++;
8028c2ecf20Sopenharmony_ci			}
8038c2ecf20Sopenharmony_ci			else {
8048c2ecf20Sopenharmony_ci				skb = netdev_alloc_skb(dev, pkt_len + 2);
8058c2ecf20Sopenharmony_ci				if (skb == NULL) {
8068c2ecf20Sopenharmony_ci					dev->stats.rx_dropped++;
8078c2ecf20Sopenharmony_ci					head->msg_length = 0;
8088c2ecf20Sopenharmony_ci					head->flag |= RMD1_OWN_CHIP;
8098c2ecf20Sopenharmony_ci					lp->new_rx = (lp->new_rx+1) &
8108c2ecf20Sopenharmony_ci					     RX_RING_MOD_MASK;
8118c2ecf20Sopenharmony_ci				}
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci#if 0
8148c2ecf20Sopenharmony_ci				if (lance_debug >= 3) {
8158c2ecf20Sopenharmony_ci					u_char *data = PKTBUF_ADDR(head);
8168c2ecf20Sopenharmony_ci					printk("%s: RX pkt %d type 0x%04x"
8178c2ecf20Sopenharmony_ci					       " from %pM to %pM",
8188c2ecf20Sopenharmony_ci					       dev->name, lp->new_tx, ((u_short *)data)[6],
8198c2ecf20Sopenharmony_ci					       &data[6], data);
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_ci					printk(" data %02x %02x %02x %02x %02x %02x %02x %02x "
8228c2ecf20Sopenharmony_ci					       "len %d at %08x\n",
8238c2ecf20Sopenharmony_ci					       data[15], data[16], data[17], data[18],
8248c2ecf20Sopenharmony_ci					       data[19], data[20], data[21], data[22],
8258c2ecf20Sopenharmony_ci					       pkt_len, data);
8268c2ecf20Sopenharmony_ci				}
8278c2ecf20Sopenharmony_ci#endif
8288c2ecf20Sopenharmony_ci				if (lance_debug >= 3) {
8298c2ecf20Sopenharmony_ci					u_char *data = PKTBUF_ADDR(head);
8308c2ecf20Sopenharmony_ci					printk( "%s: RX pkt %d type 0x%04x len %d\n ", dev->name, entry, ((u_short *)data)[6], pkt_len);
8318c2ecf20Sopenharmony_ci				}
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_ci				skb_reserve( skb, 2 );	/* 16 byte align */
8358c2ecf20Sopenharmony_ci				skb_put( skb, pkt_len );	/* Make room */
8368c2ecf20Sopenharmony_ci				skb_copy_to_linear_data(skb,
8378c2ecf20Sopenharmony_ci						 PKTBUF_ADDR(head),
8388c2ecf20Sopenharmony_ci						 pkt_len);
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci				skb->protocol = eth_type_trans( skb, dev );
8418c2ecf20Sopenharmony_ci				netif_rx( skb );
8428c2ecf20Sopenharmony_ci				dev->stats.rx_packets++;
8438c2ecf20Sopenharmony_ci				dev->stats.rx_bytes += pkt_len;
8448c2ecf20Sopenharmony_ci			}
8458c2ecf20Sopenharmony_ci		}
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_ci//		head->buf_length = -PKT_BUF_SZ | 0xf000;
8488c2ecf20Sopenharmony_ci		head->msg_length = 0;
8498c2ecf20Sopenharmony_ci		head->flag = RMD1_OWN_CHIP;
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci		entry = lp->new_rx = (lp->new_rx +1) & RX_RING_MOD_MASK;
8528c2ecf20Sopenharmony_ci	}
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ci	/* From lance.c (Donald Becker): */
8558c2ecf20Sopenharmony_ci	/* We should check that at least two ring entries are free.
8568c2ecf20Sopenharmony_ci	   If not, we should free one and mark stats->rx_dropped++. */
8578c2ecf20Sopenharmony_ci
8588c2ecf20Sopenharmony_ci	return 0;
8598c2ecf20Sopenharmony_ci}
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_cistatic int lance_close( struct net_device *dev )
8638c2ecf20Sopenharmony_ci{
8648c2ecf20Sopenharmony_ci	struct lance_private *lp = netdev_priv(dev);
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci	netif_stop_queue(dev);
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_ci	AREG = CSR0;
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci	DPRINTK( 2, ( "%s: Shutting down ethercard, status was %2.2x.\n",
8718c2ecf20Sopenharmony_ci				  dev->name, DREG ));
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_ci	/* We stop the LANCE here -- it occasionally polls
8748c2ecf20Sopenharmony_ci	   memory if we don't. */
8758c2ecf20Sopenharmony_ci	DREG = CSR0_STOP;
8768c2ecf20Sopenharmony_ci	return 0;
8778c2ecf20Sopenharmony_ci}
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_ci
8808c2ecf20Sopenharmony_ci/* Set or clear the multicast filter for this adaptor.
8818c2ecf20Sopenharmony_ci   num_addrs == -1		Promiscuous mode, receive all packets
8828c2ecf20Sopenharmony_ci   num_addrs == 0		Normal mode, clear multicast list
8838c2ecf20Sopenharmony_ci   num_addrs > 0		Multicast mode, receive normal and MC packets, and do
8848c2ecf20Sopenharmony_ci						best-effort filtering.
8858c2ecf20Sopenharmony_ci */
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_ci/* completely untested on a sun3 */
8888c2ecf20Sopenharmony_cistatic void set_multicast_list( struct net_device *dev )
8898c2ecf20Sopenharmony_ci{
8908c2ecf20Sopenharmony_ci	struct lance_private *lp = netdev_priv(dev);
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci	if(netif_queue_stopped(dev))
8938c2ecf20Sopenharmony_ci		/* Only possible if board is already started */
8948c2ecf20Sopenharmony_ci		return;
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci	/* We take the simple way out and always enable promiscuous mode. */
8978c2ecf20Sopenharmony_ci	DREG = CSR0_STOP; /* Temporarily stop the lance. */
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_ci	if (dev->flags & IFF_PROMISC) {
9008c2ecf20Sopenharmony_ci		/* Log any net taps. */
9018c2ecf20Sopenharmony_ci		DPRINTK( 3, ( "%s: Promiscuous mode enabled.\n", dev->name ));
9028c2ecf20Sopenharmony_ci		REGA( CSR15 ) = 0x8000; /* Set promiscuous mode */
9038c2ecf20Sopenharmony_ci	} else {
9048c2ecf20Sopenharmony_ci		short multicast_table[4];
9058c2ecf20Sopenharmony_ci		int num_addrs = netdev_mc_count(dev);
9068c2ecf20Sopenharmony_ci		int i;
9078c2ecf20Sopenharmony_ci		/* We don't use the multicast table, but rely on upper-layer
9088c2ecf20Sopenharmony_ci		 * filtering. */
9098c2ecf20Sopenharmony_ci		memset( multicast_table, (num_addrs == 0) ? 0 : -1,
9108c2ecf20Sopenharmony_ci				sizeof(multicast_table) );
9118c2ecf20Sopenharmony_ci		for( i = 0; i < 4; i++ )
9128c2ecf20Sopenharmony_ci			REGA( CSR8+i ) = multicast_table[i];
9138c2ecf20Sopenharmony_ci		REGA( CSR15 ) = 0; /* Unset promiscuous mode */
9148c2ecf20Sopenharmony_ci	}
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_ci	/*
9178c2ecf20Sopenharmony_ci	 * Always set BSWP after a STOP as STOP puts it back into
9188c2ecf20Sopenharmony_ci	 * little endian mode.
9198c2ecf20Sopenharmony_ci	 */
9208c2ecf20Sopenharmony_ci	REGA( CSR3 ) = CSR3_BSWP;
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_ci	/* Resume normal operation and reset AREG to CSR0 */
9238c2ecf20Sopenharmony_ci	REGA( CSR0 ) = CSR0_IDON | CSR0_INEA | CSR0_STRT;
9248c2ecf20Sopenharmony_ci}
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_ci
9278c2ecf20Sopenharmony_ci#ifdef MODULE
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_cistatic struct net_device *sun3lance_dev;
9308c2ecf20Sopenharmony_ci
9318c2ecf20Sopenharmony_ciint __init init_module(void)
9328c2ecf20Sopenharmony_ci{
9338c2ecf20Sopenharmony_ci	sun3lance_dev = sun3lance_probe(-1);
9348c2ecf20Sopenharmony_ci	return PTR_ERR_OR_ZERO(sun3lance_dev);
9358c2ecf20Sopenharmony_ci}
9368c2ecf20Sopenharmony_ci
9378c2ecf20Sopenharmony_civoid __exit cleanup_module(void)
9388c2ecf20Sopenharmony_ci{
9398c2ecf20Sopenharmony_ci	unregister_netdev(sun3lance_dev);
9408c2ecf20Sopenharmony_ci#ifdef CONFIG_SUN3
9418c2ecf20Sopenharmony_ci	iounmap((void __iomem *)sun3lance_dev->base_addr);
9428c2ecf20Sopenharmony_ci#endif
9438c2ecf20Sopenharmony_ci	free_netdev(sun3lance_dev);
9448c2ecf20Sopenharmony_ci}
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_ci#endif /* MODULE */
9478c2ecf20Sopenharmony_ci
948