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