• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**************************************************************************
2 ETHERBOOT -  BOOTP/TFTP Bootstrap Program
3 
4 Author: 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 
19 3c509 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 
35 static unsigned short	eth_nic_base;
36 static 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  */
46 struct el3_mca_adapters_struct {
47         const char *name;
48         int id;
49 };
50 static 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 /**************************************************************************
61 ETH_RESET - Reset adapter
62 ***************************************************************************/
t509_reset(struct nic * nic)63 static 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 /**************************************************************************
149 ETH_TRANSMIT - Transmit a frame
150 ***************************************************************************/
151 static char padmap[] = {
152 	0, 3, 2, 1};
153 
t509_transmit(struct nic * nic,const char * d,unsigned int t,unsigned int s,const char * p)154 static void t509_transmit(
155 struct nic *nic,
156 const char *d,			/* Destination */
157 unsigned int t,			/* Type */
158 unsigned int s,			/* size */
159 const 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 /**************************************************************************
216 ETH_POLL - Wait for a frame
217 ***************************************************************************/
t509_poll(struct nic * nic)218 static 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 
306 static int
eeprom_rdy(void)307 eeprom_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  */
324 static int
get_e(int offset)325 get_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 
335 static int
send_ID_sequence(int port)336 send_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  */
362 static int
get_eeprom_data(int id_port,int offset)363 get_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 
t509_disable(struct nic * nic)374 static void t509_disable(struct nic *nic)
375 {
376 	outb(0xc0, EP_ID_PORT);
377 }
378 
379 /**************************************************************************
380 ETH_PROBE - Look for an adapter
381 ***************************************************************************/
382 #ifdef	INCLUDE_3C529
t529_probe(struct nic * nic,unsigned short * probe_addrs)383 struct nic *t529_probe(struct nic *nic, unsigned short *probe_addrs)
384 #else
385 struct 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;
610 no3c509:
611 	printf("(probe fail)");
612 	}
613 	return 0;
614 }
615 
616 /*
617  * Local variables:
618  *  c-basic-offset: 8
619  * End:
620  */
621