1/**************************************************************************
2ETHERBOOT -  BOOTP/TFTP Bootstrap Program
3
4Author: Martin Renters.
5  Date: Mar 22 1995
6
7 This code is based heavily on David Greenman's if_ed.c driver and
8  Andres Vega Garcia's if_ep.c driver.
9
10 Copyright (C) 1993-1994, David Greenman, Martin Renters.
11 Copyright (C) 1993-1995, Andres Vega Garcia.
12 Copyright (C) 1995, Serge Babkin.
13  This software may be used, modified, copied, distributed, and sold, in
14  both source and binary form provided that the above copyright and these
15  terms are retained. Under no circumstances are the authors responsible for
16  the proper functioning of this software, nor do the authors assume any
17  responsibility for damages incurred with its use.
18
193c509 support added by Serge Babkin (babkin@hq.icb.chel.su)
20
21$Id: 3c509.c,v 1.4 2002/01/02 21:56:40 okuji Exp $
22
23***************************************************************************/
24
25/* #define EDEBUG */
26
27#include "etherboot.h"
28#include "nic.h"
29#include "cards.h"
30#include "timer.h"
31#include "3c509.h"
32
33#define	udelay(n)	waiton_timer2(((n)*TICKS_PER_MS)/1000)
34
35static unsigned short	eth_nic_base;
36static enum { none, bnc, utp } connector = none;	/* for 3C509 */
37
38#ifdef	INCLUDE_3C529
39/*
40 * This table and several other pieces of the MCA support
41 * code were shamelessly borrowed from the Linux kernel source.
42 *
43 * MCA support added by Adam Fritzler (mid@auk.cx)
44 *
45 */
46struct el3_mca_adapters_struct {
47        const char *name;
48        int id;
49};
50static struct el3_mca_adapters_struct el3_mca_adapters[] = {
51        { "3Com 3c529 EtherLink III (10base2)", 0x627c },
52        { "3Com 3c529 EtherLink III (10baseT)", 0x627d },
53        { "3Com 3c529 EtherLink III (test mode)", 0x62db },
54        { "3Com 3c529 EtherLink III (TP or coax)", 0x62f6 },
55        { "3Com 3c529 EtherLink III (TP)", 0x62f7 },
56        { NULL, 0 },
57};
58#endif
59
60/**************************************************************************
61ETH_RESET - Reset adapter
62***************************************************************************/
63static void t509_reset(struct nic *nic)
64{
65	int i;
66
67	/***********************************************************
68			Reset 3Com 509 card
69	*************************************************************/
70
71	/* stop card */
72	outw(RX_DISABLE, BASE + EP_COMMAND);
73	outw(RX_DISCARD_TOP_PACK, BASE + EP_COMMAND);
74	while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS)
75		;
76	outw(TX_DISABLE, BASE + EP_COMMAND);
77	outw(STOP_TRANSCEIVER, BASE + EP_COMMAND);
78	udelay(1000);
79	outw(RX_RESET, BASE + EP_COMMAND);
80	outw(TX_RESET, BASE + EP_COMMAND);
81	outw(C_INTR_LATCH, BASE + EP_COMMAND);
82	outw(SET_RD_0_MASK, BASE + EP_COMMAND);
83	outw(SET_INTR_MASK, BASE + EP_COMMAND);
84	outw(SET_RX_FILTER, BASE + EP_COMMAND);
85
86	/*
87	* initialize card
88	*/
89	while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS)
90		;
91
92	GO_WINDOW(0);
93
94	/* Disable the card */
95	outw(0, BASE + EP_W0_CONFIG_CTRL);
96
97	/* Configure IRQ to none */
98	outw(SET_IRQ(0), BASE + EP_W0_RESOURCE_CFG);
99
100	/* Enable the card */
101	outw(ENABLE_DRQ_IRQ, BASE + EP_W0_CONFIG_CTRL);
102
103	GO_WINDOW(2);
104
105	/* Reload the ether_addr. */
106	for (i = 0; i < ETH_ALEN; i++)
107		outb(nic->node_addr[i], BASE + EP_W2_ADDR_0 + i);
108
109	outw(RX_RESET, BASE + EP_COMMAND);
110	outw(TX_RESET, BASE + EP_COMMAND);
111
112	/* Window 1 is operating window */
113	GO_WINDOW(1);
114	for (i = 0; i < 31; i++)
115		inb(BASE + EP_W1_TX_STATUS);
116
117	/* get rid of stray intr's */
118	outw(ACK_INTR | 0xff, BASE + EP_COMMAND);
119
120	outw(SET_RD_0_MASK | S_5_INTS, BASE + EP_COMMAND);
121
122	outw(SET_INTR_MASK, BASE + EP_COMMAND);
123
124	outw(SET_RX_FILTER | FIL_INDIVIDUAL | FIL_BRDCST, BASE + EP_COMMAND);
125
126	/* configure BNC */
127	if (connector == bnc) {
128		outw(START_TRANSCEIVER, BASE + EP_COMMAND);
129		udelay(1000);
130	}
131	/* configure UTP */
132	else if (connector == utp) {
133		GO_WINDOW(4);
134		outw(ENABLE_UTP, BASE + EP_W4_MEDIA_TYPE);
135		sleep(2);	/* Give time for media to negotiate */
136		GO_WINDOW(1);
137	}
138
139	/* start transceiver and receiver */
140	outw(RX_ENABLE, BASE + EP_COMMAND);
141	outw(TX_ENABLE, BASE + EP_COMMAND);
142
143	/* set early threshold for minimal packet length */
144	outw(SET_RX_EARLY_THRESH | ETH_ZLEN, BASE + EP_COMMAND);
145	outw(SET_TX_START_THRESH | 16, BASE + EP_COMMAND);
146}
147
148/**************************************************************************
149ETH_TRANSMIT - Transmit a frame
150***************************************************************************/
151static char padmap[] = {
152	0, 3, 2, 1};
153
154static void t509_transmit(
155struct nic *nic,
156const char *d,			/* Destination */
157unsigned int t,			/* Type */
158unsigned int s,			/* size */
159const char *p)			/* Packet */
160{
161	register unsigned int len;
162	int pad;
163	int status;
164
165#ifdef	EDEBUG
166	printf("{l=%d,t=%hX}",s+ETH_HLEN,t);
167#endif
168
169	/* swap bytes of type */
170	t= htons(t);
171
172	len=s+ETH_HLEN; /* actual length of packet */
173	pad = padmap[len & 3];
174
175	/*
176	* The 3c509 automatically pads short packets to minimum ethernet length,
177	* but we drop packets that are too large. Perhaps we should truncate
178	* them instead?
179	*/
180	if (len + pad > ETH_FRAME_LEN) {
181		return;
182	}
183
184	/* drop acknowledgements */
185	while ((status=inb(BASE + EP_W1_TX_STATUS)) & TXS_COMPLETE ) {
186		if (status & (TXS_UNDERRUN|TXS_MAX_COLLISION|TXS_STATUS_OVERFLOW)) {
187			outw(TX_RESET, BASE + EP_COMMAND);
188			outw(TX_ENABLE, BASE + EP_COMMAND);
189		}
190		outb(0x0, BASE + EP_W1_TX_STATUS);
191	}
192
193	while (inw(BASE + EP_W1_FREE_TX) < (unsigned short)len + pad + 4)
194		; /* no room in FIFO */
195
196	outw(len, BASE + EP_W1_TX_PIO_WR_1);
197	outw(0x0, BASE + EP_W1_TX_PIO_WR_1);	/* Second dword meaningless */
198
199	/* write packet */
200	outsw(BASE + EP_W1_TX_PIO_WR_1, d, ETH_ALEN/2);
201	outsw(BASE + EP_W1_TX_PIO_WR_1, nic->node_addr, ETH_ALEN/2);
202	outw(t, BASE + EP_W1_TX_PIO_WR_1);
203	outsw(BASE + EP_W1_TX_PIO_WR_1, p, s / 2);
204	if (s & 1)
205		outb(*(p+s - 1), BASE + EP_W1_TX_PIO_WR_1);
206
207	while (pad--)
208		outb(0, BASE + EP_W1_TX_PIO_WR_1);	/* Padding */
209
210	/* wait for Tx complete */
211	while((inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS) != 0)
212		;
213}
214
215/**************************************************************************
216ETH_POLL - Wait for a frame
217***************************************************************************/
218static int t509_poll(struct nic *nic)
219{
220	/* common variables */
221	unsigned short type = 0;	/* used by EDEBUG */
222	/* variables for 3C509 */
223	short status, cst;
224	register short rx_fifo;
225
226	cst=inw(BASE + EP_STATUS);
227
228#ifdef	EDEBUG
229	if(cst & 0x1FFF)
230		printf("-%hX-",cst);
231#endif
232
233	if( (cst & S_RX_COMPLETE)==0 ) {
234		/* acknowledge  everything */
235		outw(ACK_INTR| (cst & S_5_INTS), BASE + EP_COMMAND);
236		outw(C_INTR_LATCH, BASE + EP_COMMAND);
237
238		return 0;
239	}
240
241	status = inw(BASE + EP_W1_RX_STATUS);
242#ifdef	EDEBUG
243	printf("*%hX*",status);
244#endif
245
246	if (status & ERR_RX) {
247		outw(RX_DISCARD_TOP_PACK, BASE + EP_COMMAND);
248		return 0;
249	}
250
251	rx_fifo = status & RX_BYTES_MASK;
252	if (rx_fifo==0)
253		return 0;
254
255		/* read packet */
256#ifdef	EDEBUG
257	printf("[l=%d",rx_fifo);
258#endif
259	insw(BASE + EP_W1_RX_PIO_RD_1, nic->packet, rx_fifo / 2);
260	if(rx_fifo & 1)
261		nic->packet[rx_fifo-1]=inb(BASE + EP_W1_RX_PIO_RD_1);
262	nic->packetlen=rx_fifo;
263
264	while(1) {
265		status = inw(BASE + EP_W1_RX_STATUS);
266#ifdef	EDEBUG
267		printf("*%hX*",status);
268#endif
269		rx_fifo = status & RX_BYTES_MASK;
270		if(rx_fifo>0) {
271			insw(BASE + EP_W1_RX_PIO_RD_1, nic->packet+nic->packetlen, rx_fifo / 2);
272			if(rx_fifo & 1)
273				nic->packet[nic->packetlen+rx_fifo-1]=inb(BASE + EP_W1_RX_PIO_RD_1);
274			nic->packetlen+=rx_fifo;
275#ifdef	EDEBUG
276			printf("+%d",rx_fifo);
277#endif
278		}
279		if(( status & RX_INCOMPLETE )==0) {
280#ifdef	EDEBUG
281			printf("=%d",nic->packetlen);
282#endif
283			break;
284		}
285		udelay(1000);	/* if incomplete wait 1 ms */
286	}
287	/* acknowledge reception of packet */
288	outw(RX_DISCARD_TOP_PACK, BASE + EP_COMMAND);
289	while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS)
290		;
291#ifdef	EDEBUG
292	type = (nic->packet[12]<<8) | nic->packet[13];
293	if(nic->packet[0]+nic->packet[1]+nic->packet[2]+nic->packet[3]+nic->packet[4]+
294	    nic->packet[5] == 0xFF*ETH_ALEN)
295		printf(",t=%hX,b]",type);
296	else
297		printf(",t=%hX]",type);
298#endif
299	return (1);
300}
301
302/*************************************************************************
303	3Com 509 - specific routines
304**************************************************************************/
305
306static int
307eeprom_rdy(void)
308{
309	int i;
310
311	for (i = 0; is_eeprom_busy(IS_BASE) && i < MAX_EEPROMBUSY; i++);
312	if (i >= MAX_EEPROMBUSY) {
313		/* printf("3c509: eeprom failed to come ready.\n"); */
314		printf("3c509: eeprom busy.\n"); /* memory in EPROM is tight */
315		return (0);
316	}
317	return (1);
318}
319
320/*
321 * get_e: gets a 16 bits word from the EEPROM. we must have set the window
322 * before
323 */
324static int
325get_e(int offset)
326{
327	if (!eeprom_rdy())
328		return (0xffff);
329	outw(EEPROM_CMD_RD | offset, IS_BASE + EP_W0_EEPROM_COMMAND);
330	if (!eeprom_rdy())
331		return (0xffff);
332	return (inw(IS_BASE + EP_W0_EEPROM_DATA));
333}
334
335static int
336send_ID_sequence(int port)
337{
338	int cx, al;
339
340	for (al = 0xff, cx = 0; cx < 255; cx++) {
341		outb(al, port);
342		al <<= 1;
343		if (al & 0x100)
344			al ^= 0xcf;
345	}
346	return (1);
347}
348
349
350/*
351 * We get eeprom data from the id_port given an offset into the eeprom.
352 * Basically; after the ID_sequence is sent to all of the cards; they enter
353 * the ID_CMD state where they will accept command requests. 0x80-0xbf loads
354 * the eeprom data.  We then read the port 16 times and with every read; the
355 * cards check for contention (ie: if one card writes a 0 bit and another
356 * writes a 1 bit then the host sees a 0. At the end of the cycle; each card
357 * compares the data on the bus; if there is a difference then that card goes
358 * into ID_WAIT state again). In the meantime; one bit of data is returned in
359 * the AX register which is conveniently returned to us by inb().  Hence; we
360 * read 16 times getting one bit of data with each read.
361 */
362static int
363get_eeprom_data(int id_port, int offset)
364{
365	int i, data = 0;
366	outb(0x80 + offset, id_port);
367	/* Do we really need this wait? Won't be noticeable anyway */
368	udelay(10000);
369	for (i = 0; i < 16; i++)
370		data = (data << 1) | (inw(id_port) & 1);
371	return (data);
372}
373
374static void t509_disable(struct nic *nic)
375{
376	outb(0xc0, EP_ID_PORT);
377}
378
379/**************************************************************************
380ETH_PROBE - Look for an adapter
381***************************************************************************/
382#ifdef	INCLUDE_3C529
383struct nic *t529_probe(struct nic *nic, unsigned short *probe_addrs)
384#else
385struct nic *t509_probe(struct nic *nic, unsigned short *probe_addrs)
386#endif
387{
388	/* common variables */
389	int i;
390	int failcount;
391
392#ifdef	INCLUDE_3C529
393	struct el3_mca_adapters_struct *mcafound = NULL;
394	int mca_pos4 = 0, mca_pos5 = 0, mca_irq = 0;
395#endif
396
397	t509_disable(nic);		/* in case board was active */
398					/* note that nic is not used */
399	for (failcount = 0; failcount < 4000; failcount++) {
400		int data, j, io_base, id_port;
401		unsigned short k;
402		int ep_current_tag;
403		short *p;
404#ifdef	INCLUDE_3C529
405		int curboard;
406#endif
407
408		id_port = EP_ID_PORT;
409		ep_current_tag = EP_LAST_TAG + 1;
410
411	/*********************************************************
412			Search for 3Com 509 card
413	***********************************************************/
414#ifdef	INCLUDE_3C529
415		/*
416		 * XXX: We should really check to make sure we have an MCA
417		 * bus controller before going ahead with this...
418		 *
419		 * For now, we avoid any hassle by making it a compile
420		 * time option.
421		 *
422		 */
423		printf("\nWarning: Assuming presence of MCA bus\n");
424
425                /* Make sure motherboard setup is off */
426                outb_p(0xff, MCA_MOTHERBOARD_SETUP_REG);
427
428		/* Cycle through slots */
429		for(curboard=0; curboard<MCA_MAX_SLOT_NR; curboard++) {
430			int boardid;
431			int curcard;
432
433			outb_p(0x8|(curboard&0xf), MCA_ADAPTER_SETUP_REG);
434
435			boardid = inb_p(MCA_POS_REG(0));
436			boardid += inb_p(MCA_POS_REG(1)) << 8;
437
438			curcard = 0;
439			while (el3_mca_adapters[curcard].name) {
440				if (el3_mca_adapters[curcard].id == boardid) {
441					mcafound = &el3_mca_adapters[curcard];
442
443					mca_pos4 = inb_p(MCA_POS_REG(4));
444					mca_pos5 = inb_p(MCA_POS_REG(5));
445
446					goto donewithdetect;
447				}
448				else
449					curcard++;
450			}
451
452		}
453	donewithdetect:
454		/* Kill all setup modes */
455		outb_p(0, MCA_ADAPTER_SETUP_REG);
456
457		if (mcafound) {
458			eth_nic_base = ((short)((mca_pos4&0xfc)|0x02)) << 8;
459			mca_irq = mca_pos5 & 0x0f;
460			ep_current_tag--;
461		}
462		else
463			printf("MCA Card not found\n");
464#endif
465	/* Look for the EISA boards, leave them activated */
466	/* search for the first card, ignore all others */
467	for(j = 1; j < 16; j++) {
468		io_base = (j * EP_EISA_START) | EP_EISA_W0;
469		if (inw(io_base + EP_W0_MFG_ID) != MFG_ID)
470			continue;
471
472		/* we must have found 0x1f if the board is EISA configurated */
473		if ((inw(io_base + EP_W0_ADDRESS_CFG) & 0x1f) != 0x1f)
474			continue;
475
476		/* Reset and Enable the card */
477		outb(W0_P4_CMD_RESET_ADAPTER, io_base + EP_W0_CONFIG_CTRL);
478		udelay(1000); /* Must wait 800 �s, be conservative */
479		outb(W0_P4_CMD_ENABLE_ADAPTER, io_base + EP_W0_CONFIG_CTRL);
480
481		/*
482		 * Once activated, all the registers are mapped in the range
483		 * x000 - x00F, where x is the slot number.
484		 */
485		eth_nic_base = j * EP_EISA_START;
486		break;
487	}
488	ep_current_tag--;
489
490	/* Look for the ISA boards. Init and leave them actived */
491	/* search for the first card, ignore all others */
492	outb(0xc0, id_port);	/* Global reset */
493	udelay(1000);		/* wait 1 ms */
494	for (i = 0; i < EP_MAX_BOARDS; i++) {
495		outb(0, id_port);
496		outb(0, id_port);
497		send_ID_sequence(id_port);
498
499		data = get_eeprom_data(id_port, EEPROM_MFG_ID);
500		if (data != MFG_ID)
501			break;
502
503		/* resolve contention using the Ethernet address */
504		for (j = 0; j < 3; j++)
505			data = get_eeprom_data(id_port, j);
506
507		eth_nic_base =
508		    (get_eeprom_data(id_port, EEPROM_ADDR_CFG) & 0x1f) * 0x10 + 0x200;
509		outb(ep_current_tag, id_port);	/* tags board */
510		outb(ACTIVATE_ADAPTER_TO_CONFIG, id_port);
511		ep_current_tag--;
512		break;
513	}
514
515	if (i >= EP_MAX_BOARDS)
516		goto no3c509;
517
518	/*
519	* The iobase was found and MFG_ID was 0x6d50. PROD_ID should be
520	* 0x9[0-f]50
521	*/
522	GO_WINDOW(0);
523	k = get_e(EEPROM_PROD_ID);
524#ifdef	INCLUDE_3C529
525	/*
526	 * On MCA, the PROD_ID matches the MCA card ID (POS0+POS1)
527	 */
528	if (mcafound) {
529		if (mcafound->id != k) {
530			printf("MCA: PROD_ID in EEPROM does not match MCA card ID! (%hX != %hX)\n", k, mcafound->id);
531			goto no3c509;
532		}
533	} else { /* for ISA/EISA */
534		if ((k & 0xf0ff) != (PROD_ID & 0xf0ff))
535			goto no3c509;
536	}
537#else
538	if ((k & 0xf0ff) != (PROD_ID & 0xf0ff))
539		goto no3c509;
540#endif
541
542#ifdef	INCLUDE_3C529
543	if (mcafound) {
544		printf("%s board found on MCA at %#hx IRQ %d -",
545		       mcafound->name, eth_nic_base, mca_irq);
546	} else {
547#endif
548		if(eth_nic_base >= EP_EISA_START)
549			printf("3C5x9 board on EISA at %#hx - ",eth_nic_base);
550		else
551			printf("3C5x9 board on ISA at %#hx - ",eth_nic_base);
552#ifdef	INCLUDE_3C529
553	}
554#endif
555
556	/* test for presence of connectors */
557	i = inw(IS_BASE + EP_W0_CONFIG_CTRL);
558	j = (inw(IS_BASE + EP_W0_ADDRESS_CFG) >> 14) & 0x3;
559
560	switch(j) {
561		case 0:
562			if (i & IS_UTP) {
563				printf("10baseT\n");
564				connector = utp;
565				}
566			else {
567				printf("10baseT not present\n");
568				goto no3c509;
569				}
570			break;
571		case 1:
572			if (i & IS_AUI)
573				printf("10base5\n");
574			else {
575				printf("10base5 not present\n");
576				goto no3c509;
577				}
578			break;
579		case 3:
580			if (i & IS_BNC) {
581				printf("10base2\n");
582				connector = bnc;
583				}
584			else {
585				printf("10base2 not present\n");
586				goto no3c509;
587				}
588			break;
589		default:
590			printf("unknown connector\n");
591			goto no3c509;
592		}
593	/*
594	* Read the station address from the eeprom
595	*/
596	p = (unsigned short *) nic->node_addr;
597	for (i = 0; i < ETH_ALEN / 2; i++) {
598		GO_WINDOW(0);
599		p[i] = htons(get_e(i));
600		GO_WINDOW(2);
601		outw(ntohs(p[i]), BASE + EP_W2_ADDR_0 + (i * 2));
602	}
603	printf("Ethernet address: %!\n", nic->node_addr);
604	t509_reset(nic);
605	nic->reset = t509_reset;
606	nic->poll = t509_poll;
607	nic->transmit = t509_transmit;
608	nic->disable = t509_disable;
609	return nic;
610no3c509:
611	printf("(probe fail)");
612	}
613	return 0;
614}
615
616/*
617 * Local variables:
618 *  c-basic-offset: 8
619 * End:
620 */
621