1/**************************************************************************
2ETHERBOOT -  BOOTP/TFTP Bootstrap Program
3
4Author: Martin Renters
5  Date: May/94
6
7 This code is based heavily on David Greenman's if_ed.c driver
8
9 Copyright (C) 1993-1994, David Greenman, Martin Renters.
10  This software may be used, modified, copied, distributed, and sold, in
11  both source and binary form provided that the above copyright and these
12  terms are retained. Under no circumstances are the authors responsible for
13  the proper functioning of this software, nor do the authors assume any
14  responsibility for damages incurred with its use.
15
163c503 support added by Bill Paul (wpaul@ctr.columbia.edu) on 11/15/94
17SMC8416 support added by Bill Paul (wpaul@ctr.columbia.edu) on 12/25/94
183c503 PIO support added by Jim Hague (jim.hague@acm.org) on 2/17/98
19RX overrun by Klaus Espenlaub (espenlaub@informatik.uni-ulm.de) on 3/10/99
20  parts taken from the Linux 8390 driver (by Donald Becker and Paul Gortmaker)
21
22**************************************************************************/
23
24#include "etherboot.h"
25#include "nic.h"
26#include "ns8390.h"
27#ifdef	INCLUDE_NS8390
28#include "pci.h"
29#endif
30#include "cards.h"
31
32static unsigned char	eth_vendor, eth_flags, eth_laar;
33static unsigned short	eth_nic_base, eth_asic_base;
34static unsigned char	eth_memsize, eth_rx_start, eth_tx_start;
35static Address		eth_bmem, eth_rmem;
36static unsigned char	eth_drain_receiver;
37
38#ifdef	INCLUDE_WD
39static struct wd_board {
40	const char *name;
41	char id;
42	char flags;
43	char memsize;
44} wd_boards[] = {
45	{"WD8003S",	TYPE_WD8003S,	0,			MEM_8192},
46	{"WD8003E",	TYPE_WD8003E,	0,			MEM_8192},
47	{"WD8013EBT",	TYPE_WD8013EBT,	FLAG_16BIT,		MEM_16384},
48	{"WD8003W",	TYPE_WD8003W,	0,			MEM_8192},
49	{"WD8003EB",	TYPE_WD8003EB,	0,			MEM_8192},
50	{"WD8013W",	TYPE_WD8013W,	FLAG_16BIT,		MEM_16384},
51	{"WD8003EP/WD8013EP",
52			TYPE_WD8013EP,	0,			MEM_8192},
53	{"WD8013WC",	TYPE_WD8013WC,	FLAG_16BIT,		MEM_16384},
54	{"WD8013EPC",	TYPE_WD8013EPC,	FLAG_16BIT,		MEM_16384},
55	{"SMC8216T",	TYPE_SMC8216T,	FLAG_16BIT | FLAG_790,	MEM_16384},
56	{"SMC8216C",	TYPE_SMC8216C,	FLAG_16BIT | FLAG_790,	MEM_16384},
57	{"SMC8416T",	TYPE_SMC8416T,	FLAG_16BIT | FLAG_790,	MEM_8192},
58	{"SMC8416C/BT",	TYPE_SMC8416C,	FLAG_16BIT | FLAG_790,	MEM_8192},
59	{"SMC8013EBP",	TYPE_SMC8013EBP,FLAG_16BIT,		MEM_16384},
60	{NULL,		0,		0,			0}
61};
62#endif
63
64#ifdef	INCLUDE_3C503
65static unsigned char	t503_output;	/* AUI or internal xcvr (Thinnet) */
66#endif
67
68#if	defined(INCLUDE_WD)
69#define	eth_probe	wd_probe
70#if	defined(INCLUDE_3C503) || defined(INCLUDE_NE) || defined(INCLUDE_NS8390)
71Error you must only define one of INCLUDE_WD, INCLUDE_3C503, INCLUDE_NE, INCLUDE_NS8390
72#endif
73#endif
74
75#if	defined(INCLUDE_3C503)
76#define	eth_probe	t503_probe
77#if	defined(INCLUDE_NE) || defined(INCLUDE_NS8390) || defined(INCLUDE_WD)
78Error you must only define one of INCLUDE_WD, INCLUDE_3C503, INCLUDE_NE, INCLUDE_NS8390
79#endif
80#endif
81
82#if	defined(INCLUDE_NE)
83#define	eth_probe	ne_probe
84#if	defined(INCLUDE_NS8390) || defined(INCLUDE_3C503) || defined(INCLUDE_WD)
85Error you must only define one of INCLUDE_WD, INCLUDE_3C503, INCLUDE_NE, INCLUDE_NS8390
86#endif
87#endif
88
89#if	defined(INCLUDE_NS8390)
90#define	eth_probe	nepci_probe
91#if	defined(INCLUDE_NE) || defined(INCLUDE_3C503) || defined(INCLUDE_WD)
92Error you must only define one of INCLUDE_WD, INCLUDE_3C503, INCLUDE_NE, INCLUDE_NS8390
93#endif
94#endif
95
96#if	defined(INCLUDE_3C503)
97#define	ASIC_PIO	_3COM_RFMSB
98#else
99#if	defined(INCLUDE_NE) || defined(INCLUDE_NS8390)
100#define	ASIC_PIO	NE_DATA
101#endif
102#endif
103
104#if	defined(INCLUDE_NE) || defined(INCLUDE_NS8390) || (defined(INCLUDE_3C503) && !defined(T503_SHMEM))
105/**************************************************************************
106ETH_PIO_READ - Read a frame via Programmed I/O
107**************************************************************************/
108static void eth_pio_read(unsigned int src, unsigned char *dst, unsigned int cnt)
109{
110	if (eth_flags & FLAG_16BIT) { ++cnt; cnt &= ~1; }
111	outb(D8390_COMMAND_RD2 |
112		D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
113	outb(cnt, eth_nic_base + D8390_P0_RBCR0);
114	outb(cnt>>8, eth_nic_base + D8390_P0_RBCR1);
115	outb(src, eth_nic_base + D8390_P0_RSAR0);
116	outb(src>>8, eth_nic_base + D8390_P0_RSAR1);
117	outb(D8390_COMMAND_RD0 |
118		D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
119
120#ifdef	INCLUDE_3C503
121	outb(src & 0xff, eth_asic_base + _3COM_DALSB);
122	outb(src >> 8, eth_asic_base + _3COM_DAMSB);
123	outb(t503_output | _3COM_CR_START, eth_asic_base + _3COM_CR);
124#endif
125
126	if (eth_flags & FLAG_16BIT)
127		cnt >>= 1;
128
129	while(cnt--) {
130#ifdef	INCLUDE_3C503
131		while((inb(eth_asic_base + _3COM_STREG) & _3COM_STREG_DPRDY) == 0)
132			;
133#endif
134
135		if (eth_flags & FLAG_16BIT) {
136			*((unsigned short *)dst) = inw(eth_asic_base + ASIC_PIO);
137			dst += 2;
138		}
139		else
140			*(dst++) = inb(eth_asic_base + ASIC_PIO);
141	}
142
143#ifdef	INCLUDE_3C503
144	outb(t503_output, eth_asic_base + _3COM_CR);
145#endif
146}
147
148/**************************************************************************
149ETH_PIO_WRITE - Write a frame via Programmed I/O
150**************************************************************************/
151static void eth_pio_write(const unsigned char *src, unsigned int dst, unsigned int cnt)
152{
153#ifdef	COMPEX_RL2000_FIX
154	unsigned int x;
155#endif	/* COMPEX_RL2000_FIX */
156	if (eth_flags & FLAG_16BIT) { ++cnt; cnt &= ~1; }
157	outb(D8390_COMMAND_RD2 |
158		D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
159	outb(D8390_ISR_RDC, eth_nic_base + D8390_P0_ISR);
160	outb(cnt, eth_nic_base + D8390_P0_RBCR0);
161	outb(cnt>>8, eth_nic_base + D8390_P0_RBCR1);
162	outb(dst, eth_nic_base + D8390_P0_RSAR0);
163	outb(dst>>8, eth_nic_base + D8390_P0_RSAR1);
164	outb(D8390_COMMAND_RD1 |
165		D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
166
167#ifdef	INCLUDE_3C503
168	outb(dst & 0xff, eth_asic_base + _3COM_DALSB);
169	outb(dst >> 8, eth_asic_base + _3COM_DAMSB);
170
171	outb(t503_output | _3COM_CR_DDIR | _3COM_CR_START, eth_asic_base + _3COM_CR);
172#endif
173
174	if (eth_flags & FLAG_16BIT)
175		cnt >>= 1;
176
177	while(cnt--)
178	{
179#ifdef	INCLUDE_3C503
180		while((inb(eth_asic_base + _3COM_STREG) & _3COM_STREG_DPRDY) == 0)
181			;
182#endif
183
184		if (eth_flags & FLAG_16BIT) {
185			outw(*((unsigned short *)src), eth_asic_base + ASIC_PIO);
186			src += 2;
187		}
188		else
189			outb(*(src++), eth_asic_base + ASIC_PIO);
190	}
191
192#ifdef	INCLUDE_3C503
193	outb(t503_output, eth_asic_base + _3COM_CR);
194#else
195#ifdef	COMPEX_RL2000_FIX
196	for (x = 0;
197		x < COMPEX_RL2000_TRIES &&
198		(inb(eth_nic_base + D8390_P0_ISR) & D8390_ISR_RDC)
199		!= D8390_ISR_RDC;
200		++x);
201	if (x >= COMPEX_RL2000_TRIES)
202		printf("Warning: Compex RL2000 aborted wait!\n");
203#endif	/* COMPEX_RL2000_FIX */
204	while((inb(eth_nic_base + D8390_P0_ISR) & D8390_ISR_RDC)
205		!= D8390_ISR_RDC);
206#endif
207}
208#else
209/**************************************************************************
210ETH_PIO_READ - Dummy routine when NE2000 not compiled in
211**************************************************************************/
212static void eth_pio_read(unsigned int src, unsigned char *dst, unsigned int cnt) {}
213#endif
214
215/**************************************************************************
216NS8390_RESET - Reset adapter
217**************************************************************************/
218static void ns8390_reset(struct nic *nic)
219{
220	int i;
221
222	eth_drain_receiver = 0;
223#ifdef	INCLUDE_WD
224	if (eth_flags & FLAG_790)
225		outb(D8390_COMMAND_PS0 | D8390_COMMAND_STP, eth_nic_base+D8390_P0_COMMAND);
226	else
227#endif
228		outb(D8390_COMMAND_PS0 | D8390_COMMAND_RD2 |
229			D8390_COMMAND_STP, eth_nic_base+D8390_P0_COMMAND);
230	if (eth_flags & FLAG_16BIT)
231		outb(0x49, eth_nic_base+D8390_P0_DCR);
232	else
233		outb(0x48, eth_nic_base+D8390_P0_DCR);
234	outb(0, eth_nic_base+D8390_P0_RBCR0);
235	outb(0, eth_nic_base+D8390_P0_RBCR1);
236	outb(0x20, eth_nic_base+D8390_P0_RCR);	/* monitor mode */
237	outb(2, eth_nic_base+D8390_P0_TCR);
238	outb(eth_tx_start, eth_nic_base+D8390_P0_TPSR);
239	outb(eth_rx_start, eth_nic_base+D8390_P0_PSTART);
240#ifdef	INCLUDE_WD
241	if (eth_flags & FLAG_790) outb(0, eth_nic_base + 0x09);
242#endif
243	outb(eth_memsize, eth_nic_base+D8390_P0_PSTOP);
244	outb(eth_memsize - 1, eth_nic_base+D8390_P0_BOUND);
245	outb(0xFF, eth_nic_base+D8390_P0_ISR);
246	outb(0, eth_nic_base+D8390_P0_IMR);
247#ifdef	INCLUDE_WD
248	if (eth_flags & FLAG_790)
249		outb(D8390_COMMAND_PS1 |
250			D8390_COMMAND_STP, eth_nic_base+D8390_P0_COMMAND);
251	else
252#endif
253		outb(D8390_COMMAND_PS1 |
254			D8390_COMMAND_RD2 | D8390_COMMAND_STP, eth_nic_base+D8390_P0_COMMAND);
255	for (i=0; i<ETH_ALEN; i++)
256		outb(nic->node_addr[i], eth_nic_base+D8390_P1_PAR0+i);
257	for (i=0; i<ETH_ALEN; i++)
258		outb(0xFF, eth_nic_base+D8390_P1_MAR0+i);
259	outb(eth_rx_start, eth_nic_base+D8390_P1_CURR);
260#ifdef	INCLUDE_WD
261	if (eth_flags & FLAG_790)
262		outb(D8390_COMMAND_PS0 |
263			D8390_COMMAND_STA, eth_nic_base+D8390_P0_COMMAND);
264	else
265#endif
266		outb(D8390_COMMAND_PS0 |
267			D8390_COMMAND_RD2 | D8390_COMMAND_STA, eth_nic_base+D8390_P0_COMMAND);
268	outb(0xFF, eth_nic_base+D8390_P0_ISR);
269	outb(0, eth_nic_base+D8390_P0_TCR);
270	outb(4, eth_nic_base+D8390_P0_RCR);	/* allow broadcast frames */
271
272#ifdef	INCLUDE_3C503
273        /*
274         * No way to tell whether or not we're supposed to use
275         * the 3Com's transceiver unless the user tells us.
276         * 'flags' should have some compile time default value
277         * which can be changed from the command menu.
278         */
279	t503_output = (nic->flags) ? 0 : _3COM_CR_XSEL;
280	outb(t503_output, eth_asic_base + _3COM_CR);
281#endif
282}
283
284static int ns8390_poll(struct nic *nic);
285
286#ifndef	INCLUDE_3C503
287/**************************************************************************
288ETH_RX_OVERRUN - Bring adapter back to work after an RX overrun
289**************************************************************************/
290static void eth_rx_overrun(struct nic *nic)
291{
292	int start_time;
293
294#ifdef	INCLUDE_WD
295	if (eth_flags & FLAG_790)
296		outb(D8390_COMMAND_PS0 | D8390_COMMAND_STP, eth_nic_base+D8390_P0_COMMAND);
297	else
298#endif
299		outb(D8390_COMMAND_PS0 | D8390_COMMAND_RD2 |
300			D8390_COMMAND_STP, eth_nic_base+D8390_P0_COMMAND);
301
302	/* wait for at least 1.6ms - we wait one timer tick */
303	start_time = currticks();
304	while (currticks() - start_time <= 1)
305		/* Nothing */;
306
307	outb(0, eth_nic_base+D8390_P0_RBCR0);	/* reset byte counter */
308	outb(0, eth_nic_base+D8390_P0_RBCR1);
309
310	/*
311	 * Linux driver checks for interrupted TX here. This is not necessary,
312	 * because the transmit routine waits until the frame is sent.
313	 */
314
315	/* enter loopback mode and restart NIC */
316	outb(2, eth_nic_base+D8390_P0_TCR);
317#ifdef	INCLUDE_WD
318	if (eth_flags & FLAG_790)
319		outb(D8390_COMMAND_PS0 | D8390_COMMAND_STA, eth_nic_base+D8390_P0_COMMAND);
320	else
321#endif
322		outb(D8390_COMMAND_PS0 | D8390_COMMAND_RD2 |
323			D8390_COMMAND_STA, eth_nic_base+D8390_P0_COMMAND);
324
325	/* clear the RX ring, acknowledge overrun interrupt */
326	eth_drain_receiver = 1;
327	while (ns8390_poll(nic))
328		/* Nothing */;
329	eth_drain_receiver = 0;
330	outb(D8390_ISR_OVW, eth_nic_base+D8390_P0_ISR);
331
332	/* leave loopback mode - no packets to be resent (see Linux driver) */
333	outb(0, eth_nic_base+D8390_P0_TCR);
334}
335#endif	/* INCLUDE_3C503 */
336
337/**************************************************************************
338NS8390_TRANSMIT - Transmit a frame
339**************************************************************************/
340static void ns8390_transmit(
341	struct nic *nic,
342	const char *d,			/* Destination */
343	unsigned int t,			/* Type */
344	unsigned int s,			/* size */
345	const char *p)			/* Packet */
346{
347#ifdef	INCLUDE_3C503
348        if (!(eth_flags & FLAG_PIO)) {
349                memcpy((char *)eth_bmem, d, ETH_ALEN);	/* dst */
350                memcpy((char *)eth_bmem+ETH_ALEN, nic->node_addr, ETH_ALEN); /* src */
351                *((char *)eth_bmem+12) = t>>8;		/* type */
352                *((char *)eth_bmem+13) = t;
353                memcpy((char *)eth_bmem+ETH_HLEN, p, s);
354                s += ETH_HLEN;
355                while (s < ETH_ZLEN) *((char *)eth_bmem+(s++)) = 0;
356        }
357#endif
358
359#ifdef	INCLUDE_WD
360	/* Memory interface */
361	if (eth_flags & FLAG_16BIT) {
362		outb(eth_laar | WD_LAAR_M16EN, eth_asic_base + WD_LAAR);
363		inb(0x84);
364	}
365	if (eth_flags & FLAG_790) {
366		outb(WD_MSR_MENB, eth_asic_base + WD_MSR);
367		inb(0x84);
368	}
369	inb(0x84);
370	memcpy((char *)eth_bmem, d, ETH_ALEN);	/* dst */
371	memcpy((char *)eth_bmem+ETH_ALEN, nic->node_addr, ETH_ALEN); /* src */
372	*((char *)eth_bmem+12) = t>>8;		/* type */
373	*((char *)eth_bmem+13) = t;
374	memcpy((char *)eth_bmem+ETH_HLEN, p, s);
375	s += ETH_HLEN;
376	while (s < ETH_ZLEN) *((char *)eth_bmem+(s++)) = 0;
377	if (eth_flags & FLAG_790) {
378		outb(0, eth_asic_base + WD_MSR);
379		inb(0x84);
380	}
381	if (eth_flags & FLAG_16BIT) {
382		outb(eth_laar & ~WD_LAAR_M16EN, eth_asic_base + WD_LAAR);
383		inb(0x84);
384	}
385#endif
386
387#if	defined(INCLUDE_3C503)
388	if (eth_flags & FLAG_PIO) {
389#endif
390#if	defined(INCLUDE_NE) || defined(INCLUDE_NS8390) || (defined(INCLUDE_3C503) && !defined(T503_SHMEM))
391		/* Programmed I/O */
392		unsigned short type;
393		type = (t >> 8) | (t << 8);
394		eth_pio_write(d, eth_tx_start<<8, ETH_ALEN);
395		eth_pio_write(nic->node_addr, (eth_tx_start<<8)+ETH_ALEN, ETH_ALEN);
396		/* bcc generates worse code without (const+const) below */
397		eth_pio_write((unsigned char *)&type, (eth_tx_start<<8)+(ETH_ALEN+ETH_ALEN), 2);
398		eth_pio_write(p, (eth_tx_start<<8)+ETH_HLEN, s);
399		s += ETH_HLEN;
400		if (s < ETH_ZLEN) s = ETH_ZLEN;
401#endif
402#if	defined(INCLUDE_3C503)
403	}
404#endif
405
406#ifdef	INCLUDE_WD
407	if (eth_flags & FLAG_790)
408		outb(D8390_COMMAND_PS0 |
409			D8390_COMMAND_STA, eth_nic_base+D8390_P0_COMMAND);
410	else
411#endif
412		outb(D8390_COMMAND_PS0 |
413			D8390_COMMAND_RD2 | D8390_COMMAND_STA, eth_nic_base+D8390_P0_COMMAND);
414	outb(eth_tx_start, eth_nic_base+D8390_P0_TPSR);
415	outb(s, eth_nic_base+D8390_P0_TBCR0);
416	outb(s>>8, eth_nic_base+D8390_P0_TBCR1);
417#ifdef	INCLUDE_WD
418	if (eth_flags & FLAG_790)
419		outb(D8390_COMMAND_PS0 |
420			D8390_COMMAND_TXP | D8390_COMMAND_STA, eth_nic_base+D8390_P0_COMMAND);
421	else
422#endif
423		outb(D8390_COMMAND_PS0 |
424			D8390_COMMAND_TXP | D8390_COMMAND_RD2 |
425			D8390_COMMAND_STA, eth_nic_base+D8390_P0_COMMAND);
426}
427
428/**************************************************************************
429NS8390_POLL - Wait for a frame
430**************************************************************************/
431static int ns8390_poll(struct nic *nic)
432{
433	int ret = 0;
434	unsigned char rstat, curr, next;
435	unsigned short len, frag;
436	unsigned short pktoff;
437	unsigned char *p;
438	struct ringbuffer pkthdr;
439
440#ifndef	INCLUDE_3C503
441	/* avoid infinite recursion: see eth_rx_overrun() */
442	if (!eth_drain_receiver && (inb(eth_nic_base+D8390_P0_ISR) & D8390_ISR_OVW)) {
443		eth_rx_overrun(nic);
444		return(0);
445	}
446#endif	/* INCLUDE_3C503 */
447	rstat = inb(eth_nic_base+D8390_P0_RSR);
448	if (!(rstat & D8390_RSTAT_PRX)) return(0);
449	next = inb(eth_nic_base+D8390_P0_BOUND)+1;
450	if (next >= eth_memsize) next = eth_rx_start;
451	outb(D8390_COMMAND_PS1, eth_nic_base+D8390_P0_COMMAND);
452	curr = inb(eth_nic_base+D8390_P1_CURR);
453	outb(D8390_COMMAND_PS0, eth_nic_base+D8390_P0_COMMAND);
454	if (curr >= eth_memsize) curr=eth_rx_start;
455	if (curr == next) return(0);
456#ifdef	INCLUDE_WD
457	if (eth_flags & FLAG_16BIT) {
458		outb(eth_laar | WD_LAAR_M16EN, eth_asic_base + WD_LAAR);
459		inb(0x84);
460	}
461	if (eth_flags & FLAG_790) {
462		outb(WD_MSR_MENB, eth_asic_base + WD_MSR);
463		inb(0x84);
464	}
465	inb(0x84);
466#endif
467	pktoff = next << 8;
468	if (eth_flags & FLAG_PIO)
469		eth_pio_read(pktoff, (char *)&pkthdr, 4);
470	else
471		memcpy(&pkthdr, (char *)eth_rmem + pktoff, 4);
472	pktoff += sizeof(pkthdr);
473	/* incoming length includes FCS so must sub 4 */
474	len = pkthdr.len - 4;
475	if ((pkthdr.status & D8390_RSTAT_PRX) == 0 || len < ETH_ZLEN
476		|| len > ETH_FRAME_LEN) {
477		printf("Bogus packet, ignoring\n");
478		return (0);
479	}
480	else {
481		p = nic->packet;
482		nic->packetlen = len;		/* available to caller */
483		frag = (eth_memsize << 8) - pktoff;
484		if (len > frag) {		/* We have a wrap-around */
485			/* read first part */
486			if (eth_flags & FLAG_PIO)
487				eth_pio_read(pktoff, p, frag);
488			else
489				memcpy(p, (char *)eth_rmem + pktoff, frag);
490			pktoff = eth_rx_start << 8;
491			p += frag;
492			len -= frag;
493		}
494		/* read second part */
495		if (eth_flags & FLAG_PIO)
496			eth_pio_read(pktoff, p, len);
497		else
498			memcpy(p, (char *)eth_rmem + pktoff, len);
499		ret = 1;
500	}
501#ifdef	INCLUDE_WD
502	if (eth_flags & FLAG_790) {
503		outb(0, eth_asic_base + WD_MSR);
504		inb(0x84);
505	}
506	if (eth_flags & FLAG_16BIT) {
507		outb(eth_laar & ~WD_LAAR_M16EN, eth_asic_base + WD_LAAR);
508		inb(0x84);
509	}
510	inb(0x84);
511#endif
512	next = pkthdr.next;		/* frame number of next packet */
513	if (next == eth_rx_start)
514		next = eth_memsize;
515	outb(next-1, eth_nic_base+D8390_P0_BOUND);
516	return(ret);
517}
518
519/**************************************************************************
520NS8390_DISABLE - Turn off adapter
521**************************************************************************/
522static void ns8390_disable(struct nic *nic)
523{
524}
525
526/**************************************************************************
527ETH_PROBE - Look for an adapter
528**************************************************************************/
529#ifdef	INCLUDE_NS8390
530struct nic *eth_probe(struct nic *nic, unsigned short *probe_addrs,
531		      struct pci_device *pci)
532#else
533struct nic *eth_probe(struct nic *nic, unsigned short *probe_addrs)
534#endif
535{
536	int i;
537	struct wd_board *brd;
538	unsigned short chksum;
539	unsigned char c;
540	eth_vendor = VENDOR_NONE;
541	eth_drain_receiver = 0;
542
543#ifdef	INCLUDE_WD
544	/******************************************************************
545	Search for WD/SMC cards
546	******************************************************************/
547	for (eth_asic_base = WD_LOW_BASE; eth_asic_base <= WD_HIGH_BASE;
548		eth_asic_base += 0x20) {
549		chksum = 0;
550		for (i=8; i<16; i++)
551			chksum += inb(eth_asic_base+i);
552		/* Extra checks to avoid soundcard */
553		if ((chksum & 0xFF) == 0xFF &&
554			inb(eth_asic_base+8) != 0xFF &&
555			inb(eth_asic_base+9) != 0xFF)
556			break;
557	}
558	if (eth_asic_base > WD_HIGH_BASE)
559		return (0);
560	/* We've found a board */
561	eth_vendor = VENDOR_WD;
562	eth_nic_base = eth_asic_base + WD_NIC_ADDR;
563	c = inb(eth_asic_base+WD_BID);	/* Get board id */
564	for (brd = wd_boards; brd->name; brd++)
565		if (brd->id == c) break;
566	if (!brd->name) {
567		printf("Unknown WD/SMC NIC type %hhX\n", c);
568		return (0);	/* Unknown type */
569	}
570	eth_flags = brd->flags;
571	eth_memsize = brd->memsize;
572	eth_tx_start = 0;
573	eth_rx_start = D8390_TXBUF_SIZE;
574	if ((c == TYPE_WD8013EP) &&
575		(inb(eth_asic_base + WD_ICR) & WD_ICR_16BIT)) {
576			eth_flags = FLAG_16BIT;
577			eth_memsize = MEM_16384;
578	}
579	if ((c & WD_SOFTCONFIG) && (!(eth_flags & FLAG_790))) {
580		eth_bmem = (0x80000 |
581		 ((inb(eth_asic_base + WD_MSR) & 0x3F) << 13));
582	} else
583		eth_bmem = WD_DEFAULT_MEM;
584	if (brd->id == TYPE_SMC8216T || brd->id == TYPE_SMC8216C) {
585		*((unsigned int *)(eth_bmem + 8192)) = (unsigned int)0;
586		if (*((unsigned int *)(eth_bmem + 8192))) {
587			brd += 2;
588			eth_memsize = brd->memsize;
589		}
590	}
591	outb(0x80, eth_asic_base + WD_MSR);	/* Reset */
592	for (i=0; i<ETH_ALEN; i++) {
593		nic->node_addr[i] = inb(i+eth_asic_base+WD_LAR);
594	}
595	printf("\n%s base %#hx, memory %#hx, addr %!\n",
596		brd->name, eth_asic_base, eth_bmem, nic->node_addr);
597	if (eth_flags & FLAG_790) {
598		outb(WD_MSR_MENB, eth_asic_base+WD_MSR);
599		outb((inb(eth_asic_base+0x04) |
600			0x80), eth_asic_base+0x04);
601		outb((((unsigned)eth_bmem >> 13) & 0x0F) |
602			(((unsigned)eth_bmem >> 11) & 0x40) |
603			(inb(eth_asic_base+0x0B) & 0xB0), eth_asic_base+0x0B);
604		outb((inb(eth_asic_base+0x04) &
605			~0x80), eth_asic_base+0x04);
606	} else {
607		outb((((unsigned)eth_bmem >> 13) & 0x3F) | 0x40, eth_asic_base+WD_MSR);
608	}
609	if (eth_flags & FLAG_16BIT) {
610		if (eth_flags & FLAG_790) {
611			eth_laar = inb(eth_asic_base + WD_LAAR);
612			outb(WD_LAAR_M16EN, eth_asic_base + WD_LAAR);
613		} else {
614			outb((eth_laar =
615				WD_LAAR_L16EN | 1), eth_asic_base + WD_LAAR);
616/*
617	The previous line used to be
618				WD_LAAR_M16EN | WD_LAAR_L16EN | 1));
619	jluke@deakin.edu.au reported that removing WD_LAAR_M16EN made
620	it work for WD8013s.  This seems to work for my 8013 boards. I
621	don't know what is really happening.  I wish I had data sheets
622	or more time to decode the Linux driver. - Ken
623*/
624		}
625		inb(0x84);
626	}
627#endif
628#ifdef	INCLUDE_3C503
629        /******************************************************************
630        Search for 3Com 3c503 if no WD/SMC cards
631        ******************************************************************/
632	if (eth_vendor == VENDOR_NONE) {
633		int	idx;
634		int	iobase_reg, membase_reg;
635		static unsigned short	base[] = {
636			0x300, 0x310, 0x330, 0x350,
637			0x250, 0x280, 0x2A0, 0x2E0, 0 };
638
639		/* Loop through possible addresses checking each one */
640
641		for (idx = 0; (eth_nic_base = base[idx]) != 0; ++idx) {
642
643			eth_asic_base = eth_nic_base + _3COM_ASIC_OFFSET;
644/*
645 * Note that we use the same settings for both 8 and 16 bit cards:
646 * both have an 8K bank of memory at page 1 while only the 16 bit
647 * cards have a bank at page 0.
648 */
649			eth_memsize = MEM_16384;
650			eth_tx_start = 32;
651			eth_rx_start = 32 + D8390_TXBUF_SIZE;
652
653		/* Check our base address. iobase and membase should */
654		/* both have a maximum of 1 bit set or be 0. */
655
656			iobase_reg = inb(eth_asic_base + _3COM_BCFR);
657			membase_reg = inb(eth_asic_base + _3COM_PCFR);
658
659			if ((iobase_reg & (iobase_reg - 1)) ||
660				(membase_reg & (membase_reg - 1)))
661				continue;		/* nope */
662
663		/* Now get the shared memory address */
664
665			eth_flags = 0;
666
667			switch (membase_reg) {
668				case _3COM_PCFR_DC000:
669					eth_bmem = 0xdc000;
670					break;
671				case _3COM_PCFR_D8000:
672					eth_bmem = 0xd8000;
673					break;
674				case _3COM_PCFR_CC000:
675					eth_bmem = 0xcc000;
676					break;
677				case _3COM_PCFR_C8000:
678					eth_bmem = 0xc8000;
679					break;
680				case _3COM_PCFR_PIO:
681					eth_flags |= FLAG_PIO;
682					eth_bmem = 0;
683					break;
684				default:
685					continue;	/* nope */
686				}
687			break;
688		}
689
690		if (base[idx] == 0)		/* not found */
691			return (0);
692#ifndef	T503_SHMEM
693		eth_flags |= FLAG_PIO;		/* force PIO mode */
694		eth_bmem = 0;
695#endif
696		eth_vendor = VENDOR_3COM;
697
698
699        /* Need this to make ns8390_poll() happy. */
700
701                eth_rmem = eth_bmem - 0x2000;
702
703        /* Reset NIC and ASIC */
704
705                outb(_3COM_CR_RST | _3COM_CR_XSEL, eth_asic_base + _3COM_CR );
706                outb(_3COM_CR_XSEL, eth_asic_base + _3COM_CR );
707
708        /* Get our ethernet address */
709
710                outb(_3COM_CR_EALO | _3COM_CR_XSEL, eth_asic_base + _3COM_CR);
711                printf("\n3Com 3c503 base %#hx, ", eth_nic_base);
712                if (eth_flags & FLAG_PIO)
713			printf("PIO mode");
714                else
715			printf("memory %#hx", eth_bmem);
716                for (i=0; i<ETH_ALEN; i++) {
717                        nic->node_addr[i] = inb(eth_nic_base+i);
718                }
719                printf(", %s, addr %!\n", nic->flags ? "AUI" : "internal xcvr",
720			nic->node_addr);
721                outb(_3COM_CR_XSEL, eth_asic_base + _3COM_CR);
722        /*
723         * Initialize GA configuration register. Set bank and enable shared
724         * mem. We always use bank 1. Disable interrupts.
725         */
726                outb(_3COM_GACFR_RSEL |
727			_3COM_GACFR_MBS0 | _3COM_GACFR_TCM | _3COM_GACFR_NIM, eth_asic_base + _3COM_GACFR);
728
729                outb(0xff, eth_asic_base + _3COM_VPTR2);
730                outb(0xff, eth_asic_base + _3COM_VPTR1);
731                outb(0x00, eth_asic_base + _3COM_VPTR0);
732        /*
733         * Clear memory and verify that it worked (we use only 8K)
734         */
735
736		if (!(eth_flags & FLAG_PIO)) {
737			memset((char *)eth_bmem, 0, 0x2000);
738			for(i = 0; i < 0x2000; ++i)
739				if (*(((char *)eth_bmem)+i)) {
740					printf ("Failed to clear 3c503 shared mem.\n");
741					return (0);
742				}
743		}
744        /*
745         * Initialize GA page/start/stop registers.
746         */
747                outb(eth_tx_start, eth_asic_base + _3COM_PSTR);
748                outb(eth_memsize, eth_asic_base + _3COM_PSPR);
749        }
750#endif
751#if	defined(INCLUDE_NE) || defined(INCLUDE_NS8390)
752	/******************************************************************
753	Search for NE1000/2000 if no WD/SMC or 3com cards
754	******************************************************************/
755	if (eth_vendor == VENDOR_NONE) {
756		char romdata[16], testbuf[32];
757		int idx;
758		static char test[] = "NE*000 memory";
759		static unsigned short base[] = {
760#ifdef	NE_SCAN
761			NE_SCAN,
762#endif
763			0 };
764		/* if no addresses supplied, fall back on defaults */
765		if (probe_addrs == 0 || probe_addrs[0] == 0)
766			probe_addrs = base;
767		eth_bmem = 0;		/* No shared memory */
768		for (idx = 0; (eth_nic_base = probe_addrs[idx]) != 0; ++idx) {
769			eth_flags = FLAG_PIO;
770			eth_asic_base = eth_nic_base + NE_ASIC_OFFSET;
771			eth_memsize = MEM_16384;
772			eth_tx_start = 32;
773			eth_rx_start = 32 + D8390_TXBUF_SIZE;
774			c = inb(eth_asic_base + NE_RESET);
775			outb(c, eth_asic_base + NE_RESET);
776			inb(0x84);
777			outb(D8390_COMMAND_STP |
778				D8390_COMMAND_RD2, eth_nic_base + D8390_P0_COMMAND);
779			outb(D8390_RCR_MON, eth_nic_base + D8390_P0_RCR);
780			outb(D8390_DCR_FT1 | D8390_DCR_LS, eth_nic_base + D8390_P0_DCR);
781			outb(MEM_8192, eth_nic_base + D8390_P0_PSTART);
782			outb(MEM_16384, eth_nic_base + D8390_P0_PSTOP);
783#ifdef	NS8390_FORCE_16BIT
784			eth_flags |= FLAG_16BIT;	/* force 16-bit mode */
785#endif
786
787			eth_pio_write(test, 8192, sizeof(test));
788			eth_pio_read(8192, testbuf, sizeof(test));
789			if (!memcmp(test, testbuf, sizeof(test)))
790				break;
791			eth_flags |= FLAG_16BIT;
792			eth_memsize = MEM_32768;
793			eth_tx_start = 64;
794			eth_rx_start = 64 + D8390_TXBUF_SIZE;
795			outb(D8390_DCR_WTS |
796				D8390_DCR_FT1 | D8390_DCR_LS, eth_nic_base + D8390_P0_DCR);
797			outb(MEM_16384, eth_nic_base + D8390_P0_PSTART);
798			outb(MEM_32768, eth_nic_base + D8390_P0_PSTOP);
799			eth_pio_write(test, 16384, sizeof(test));
800			eth_pio_read(16384, testbuf, sizeof(test));
801			if (!memcmp(testbuf, test, sizeof(test)))
802				break;
803		}
804		if (eth_nic_base == 0)
805			return (0);
806		if (eth_nic_base > ISA_MAX_ADDR)	/* PCI probably */
807			eth_flags |= FLAG_16BIT;
808		eth_vendor = VENDOR_NOVELL;
809		eth_pio_read(0, romdata, sizeof(romdata));
810		for (i=0; i<ETH_ALEN; i++) {
811			nic->node_addr[i] = romdata[i + ((eth_flags & FLAG_16BIT) ? i : 0)];
812		}
813		printf("\nNE%c000 base %#hx, addr %!\n",
814			(eth_flags & FLAG_16BIT) ? '2' : '1', eth_nic_base,
815			nic->node_addr);
816	}
817#endif
818	if (eth_vendor == VENDOR_NONE)
819		return(0);
820        if (eth_vendor != VENDOR_3COM)
821		eth_rmem = eth_bmem;
822	ns8390_reset(nic);
823	nic->reset = ns8390_reset;
824	nic->poll = ns8390_poll;
825	nic->transmit = ns8390_transmit;
826	nic->disable = ns8390_disable;
827	return(nic);
828}
829
830/*
831 * Local variables:
832 *  c-basic-offset: 8
833 * End:
834 */
835
836