xref: /illumos-gate/usr/src/grub/grub-0.97/netboot/3c595.c (revision 2bbdd445a21f9d61f4a0ca0faf05d5ceb2bd91f3)
1 /*
2 * 3c595.c -- 3COM 3C595 Fast Etherlink III PCI driver for etherboot
3 *
4 * Copyright (C) 2000 Shusuke Nisiyama <shu@athena.qe.eng.hokudai.ac.jp>
5 * All rights reserved.
6 * Mar. 14, 2000
7 *
8 *  This software may be used, modified, copied, distributed, and sold, in
9 *  both source and binary form provided that the above copyright and these
10 *  terms are retained. Under no circumstances are the authors responsible for
11 *  the proper functioning of this software, nor do the authors assume any
12 *  responsibility for damages incurred with its use.
13 *
14 * This code is based on Martin Renters' etherboot-4.4.3 3c509.c and
15 * Herb Peyerl's FreeBSD 3.4-RELEASE if_vx.c driver.
16 *
17 *  Copyright (C) 1993-1994, David Greenman, Martin Renters.
18 *  Copyright (C) 1993-1995, Andres Vega Garcia.
19 *  Copyright (C) 1995, Serge Babkin.
20 *
21 *  Copyright (c) 1994 Herb Peyerl <hpeyerl@novatel.ca>
22 *
23 * timlegge	08-24-2003	Add Multicast Support
24 */
25 
26 /* #define EDEBUG */
27 
28 #include "etherboot.h"
29 #include "nic.h"
30 #include "pci.h"
31 #include "3c595.h"
32 #include "timer.h"
33 
34 static unsigned short	eth_nic_base;
35 static unsigned short	vx_connector, vx_connectors;
36 
37 static struct connector_entry {
38   int bit;
39   char *name;
40 } conn_tab[VX_CONNECTORS] = {
41 #define CONNECTOR_UTP   0
42   { 0x08, "utp"},
43 #define CONNECTOR_AUI   1
44   { 0x20, "aui"},
45 /* dummy */
46   { 0, "???"},
47 #define CONNECTOR_BNC   3
48   { 0x10, "bnc"},
49 #define CONNECTOR_TX    4
50   { 0x02, "tx"},
51 #define CONNECTOR_FX    5
52   { 0x04, "fx"},
53 #define CONNECTOR_MII   6
54   { 0x40, "mii"},
55   { 0, "???"}
56 };
57 
58 static void vxgetlink(void);
59 static void vxsetlink(void);
60 
61 /**************************************************************************
62 ETH_RESET - Reset adapter
63 ***************************************************************************/
64 static void t595_reset(struct nic *nic)
65 {
66 	int i;
67 
68 	/***********************************************************
69 			Reset 3Com 595 card
70 	*************************************************************/
71 
72 	/* stop card */
73 	outw(RX_DISABLE, BASE + VX_COMMAND);
74 	outw(RX_DISCARD_TOP_PACK, BASE + VX_COMMAND);
75 	VX_BUSY_WAIT;
76 	outw(TX_DISABLE, BASE + VX_COMMAND);
77 	outw(STOP_TRANSCEIVER, BASE + VX_COMMAND);
78 	udelay(8000);
79 	outw(RX_RESET, BASE + VX_COMMAND);
80 	VX_BUSY_WAIT;
81 	outw(TX_RESET, BASE + VX_COMMAND);
82 	VX_BUSY_WAIT;
83 	outw(C_INTR_LATCH, BASE + VX_COMMAND);
84 	outw(SET_RD_0_MASK, BASE + VX_COMMAND);
85 	outw(SET_INTR_MASK, BASE + VX_COMMAND);
86 	outw(SET_RX_FILTER, BASE + VX_COMMAND);
87 
88 	/*
89 	* initialize card
90 	*/
91 	VX_BUSY_WAIT;
92 
93 	GO_WINDOW(0);
94 
95 	/* Disable the card */
96 /*	outw(0, BASE + VX_W0_CONFIG_CTRL); */
97 
98 	/* Configure IRQ to none */
99 /*	outw(SET_IRQ(0), BASE + VX_W0_RESOURCE_CFG); */
100 
101 	/* Enable the card */
102 /*	outw(ENABLE_DRQ_IRQ, BASE + VX_W0_CONFIG_CTRL); */
103 
104 	GO_WINDOW(2);
105 
106 	/* Reload the ether_addr. */
107 	for (i = 0; i < ETH_ALEN; i++)
108 		outb(nic->node_addr[i], BASE + VX_W2_ADDR_0 + i);
109 
110 	outw(RX_RESET, BASE + VX_COMMAND);
111 	VX_BUSY_WAIT;
112 	outw(TX_RESET, BASE + VX_COMMAND);
113 	VX_BUSY_WAIT;
114 
115 	/* Window 1 is operating window */
116 	GO_WINDOW(1);
117 	for (i = 0; i < 31; i++)
118 		inb(BASE + VX_W1_TX_STATUS);
119 
120 	outw(SET_RD_0_MASK | S_CARD_FAILURE | S_RX_COMPLETE |
121 		S_TX_COMPLETE | S_TX_AVAIL, BASE + VX_COMMAND);
122 	outw(SET_INTR_MASK | S_CARD_FAILURE | S_RX_COMPLETE |
123 		S_TX_COMPLETE | S_TX_AVAIL, BASE + VX_COMMAND);
124 
125 /*
126  * Attempt to get rid of any stray interrupts that occured during
127  * configuration.  On the i386 this isn't possible because one may
128  * already be queued.  However, a single stray interrupt is
129  * unimportant.
130  */
131 
132 	outw(ACK_INTR | 0xff, BASE + VX_COMMAND);
133 
134 	outw(SET_RX_FILTER | FIL_INDIVIDUAL |
135 	    FIL_BRDCST|FIL_MULTICAST, BASE + VX_COMMAND);
136 
137 	vxsetlink();
138 /*{
139 	int i,j;
140 	i = CONNECTOR_TX;
141 	GO_WINDOW(3);
142 	j = inl(BASE + VX_W3_INTERNAL_CFG) & ~INTERNAL_CONNECTOR_MASK;
143 	outl(BASE + VX_W3_INTERNAL_CFG, j | (i <<INTERNAL_CONNECTOR_BITS));
144         GO_WINDOW(4);
145         outw(LINKBEAT_ENABLE, BASE + VX_W4_MEDIA_TYPE);
146         GO_WINDOW(1);
147 }*/
148 
149 	/* start tranciever and receiver */
150 	outw(RX_ENABLE, BASE + VX_COMMAND);
151 	outw(TX_ENABLE, BASE + VX_COMMAND);
152 
153 }
154 
155 /**************************************************************************
156 ETH_TRANSMIT - Transmit a frame
157 ***************************************************************************/
158 static char padmap[] = {
159 	0, 3, 2, 1};
160 
161 static void t595_transmit(
162 struct nic *nic,
163 const char *d,			/* Destination */
164 unsigned int t,			/* Type */
165 unsigned int s,			/* size */
166 const char *p)			/* Packet */
167 {
168 	register int len;
169 	int pad;
170 	int status;
171 
172 #ifdef EDEBUG
173 	printf("{l=%d,t=%hX}",s+ETH_HLEN,t);
174 #endif
175 
176 	/* swap bytes of type */
177 	t= htons(t);
178 
179 	len=s+ETH_HLEN; /* actual length of packet */
180 	pad = padmap[len & 3];
181 
182 	/*
183 	* The 3c595 automatically pads short packets to minimum ethernet length,
184 	* but we drop packets that are too large. Perhaps we should truncate
185 	* them instead?
186 	*/
187 	if (len + pad > ETH_FRAME_LEN) {
188 		return;
189 	}
190 
191 	/* drop acknowledgements */
192 	while(( status=inb(BASE + VX_W1_TX_STATUS) )& TXS_COMPLETE ) {
193 		if(status & (TXS_UNDERRUN|TXS_MAX_COLLISION|TXS_STATUS_OVERFLOW)) {
194 			outw(TX_RESET, BASE + VX_COMMAND);
195 			outw(TX_ENABLE, BASE + VX_COMMAND);
196 		}
197 
198 		outb(0x0, BASE + VX_W1_TX_STATUS);
199 	}
200 
201 	while (inw(BASE + VX_W1_FREE_TX) < len + pad + 4) {
202 		/* no room in FIFO */
203 	}
204 
205 	outw(len, BASE + VX_W1_TX_PIO_WR_1);
206 	outw(0x0, BASE + VX_W1_TX_PIO_WR_1);	/* Second dword meaningless */
207 
208 	/* write packet */
209 	outsw(BASE + VX_W1_TX_PIO_WR_1, d, ETH_ALEN/2);
210 	outsw(BASE + VX_W1_TX_PIO_WR_1, nic->node_addr, ETH_ALEN/2);
211 	outw(t, BASE + VX_W1_TX_PIO_WR_1);
212 	outsw(BASE + VX_W1_TX_PIO_WR_1, p, s / 2);
213 	if (s & 1)
214 		outb(*(p+s - 1), BASE + VX_W1_TX_PIO_WR_1);
215 
216 	while (pad--)
217 		outb(0, BASE + VX_W1_TX_PIO_WR_1);	/* Padding */
218 
219         /* wait for Tx complete */
220         while((inw(BASE + VX_STATUS) & S_COMMAND_IN_PROGRESS) != 0)
221                 ;
222 }
223 
224 /**************************************************************************
225 ETH_POLL - Wait for a frame
226 ***************************************************************************/
227 static int t595_poll(struct nic *nic, int retrieve)
228 {
229 	/* common variables */
230 	/* variables for 3C595 */
231 	short status, cst;
232 	register short rx_fifo;
233 
234 	cst=inw(BASE + VX_STATUS);
235 
236 #ifdef EDEBUG
237 	if(cst & 0x1FFF)
238 		printf("-%hX-",cst);
239 #endif
240 
241 	if( (cst & S_RX_COMPLETE)==0 ) {
242 		/* acknowledge  everything */
243 		outw(ACK_INTR | cst, BASE + VX_COMMAND);
244 		outw(C_INTR_LATCH, BASE + VX_COMMAND);
245 
246 		return 0;
247 	}
248 
249 	status = inw(BASE + VX_W1_RX_STATUS);
250 #ifdef EDEBUG
251 	printf("*%hX*",status);
252 #endif
253 
254 	if (status & ERR_RX) {
255 		outw(RX_DISCARD_TOP_PACK, BASE + VX_COMMAND);
256 		return 0;
257 	}
258 
259 	rx_fifo = status & RX_BYTES_MASK;
260 	if (rx_fifo==0)
261 		return 0;
262 
263 	if ( ! retrieve ) return 1;
264 
265 		/* read packet */
266 #ifdef EDEBUG
267 	printf("[l=%d",rx_fifo);
268 #endif
269 	insw(BASE + VX_W1_RX_PIO_RD_1, nic->packet, rx_fifo / 2);
270 	if(rx_fifo & 1)
271 		nic->packet[rx_fifo-1]=inb(BASE + VX_W1_RX_PIO_RD_1);
272 	nic->packetlen=rx_fifo;
273 
274 	while(1) {
275 		status = inw(BASE + VX_W1_RX_STATUS);
276 #ifdef EDEBUG
277 		printf("*%hX*",status);
278 #endif
279 		rx_fifo = status & RX_BYTES_MASK;
280 
281 		if(rx_fifo>0) {
282 			insw(BASE + VX_W1_RX_PIO_RD_1, nic->packet+nic->packetlen, rx_fifo / 2);
283 			if(rx_fifo & 1)
284 				nic->packet[nic->packetlen+rx_fifo-1]=inb(BASE + VX_W1_RX_PIO_RD_1);
285 			nic->packetlen+=rx_fifo;
286 #ifdef EDEBUG
287 			printf("+%d",rx_fifo);
288 #endif
289 		}
290 		if(( status & RX_INCOMPLETE )==0) {
291 #ifdef EDEBUG
292 			printf("=%d",nic->packetlen);
293 #endif
294 			break;
295 		}
296 		udelay(1000);
297 	}
298 
299 	/* acknowledge reception of packet */
300 	outw(RX_DISCARD_TOP_PACK, BASE + VX_COMMAND);
301 	while (inw(BASE + VX_STATUS) & S_COMMAND_IN_PROGRESS);
302 #ifdef EDEBUG
303 {
304 	unsigned short type = 0;	/* used by EDEBUG */
305 	type = (nic->packet[12]<<8) | nic->packet[13];
306 	if(nic->packet[0]+nic->packet[1]+nic->packet[2]+nic->packet[3]+nic->packet[4]+
307 	    nic->packet[5] == 0xFF*ETH_ALEN)
308 		printf(",t=%hX,b]",type);
309 	else
310 		printf(",t=%hX]",type);
311 }
312 #endif
313 	return 1;
314 }
315 
316 
317 /*************************************************************************
318 	3Com 595 - specific routines
319 **************************************************************************/
320 
321 static int
322 eeprom_rdy()
323 {
324 	int i;
325 
326 	for (i = 0; is_eeprom_busy(BASE) && i < MAX_EEPROMBUSY; i++)
327 		udelay(1000);
328 	if (i >= MAX_EEPROMBUSY) {
329 	        /* printf("3c595: eeprom failed to come ready.\n"); */
330 		printf("3c595: eeprom is busy.\n"); /* memory in EPROM is tight */
331 		return (0);
332 	}
333 	return (1);
334 }
335 
336 /*
337  * get_e: gets a 16 bits word from the EEPROM. we must have set the window
338  * before
339  */
340 static int
341 get_e(offset)
342 int offset;
343 {
344 	if (!eeprom_rdy())
345 		return (0xffff);
346 	outw(EEPROM_CMD_RD | offset, BASE + VX_W0_EEPROM_COMMAND);
347 	if (!eeprom_rdy())
348 		return (0xffff);
349 	return (inw(BASE + VX_W0_EEPROM_DATA));
350 }
351 
352 static void
353 vxgetlink(void)
354 {
355     int n, k;
356 
357     GO_WINDOW(3);
358     vx_connectors = inw(BASE + VX_W3_RESET_OPT) & 0x7f;
359     for (n = 0, k = 0; k < VX_CONNECTORS; k++) {
360       if (vx_connectors & conn_tab[k].bit) {
361         if (n > 0) {
362           printf("/");
363 	}
364         printf(conn_tab[k].name);
365         n++;
366       }
367     }
368     if (vx_connectors == 0) {
369         printf("no connectors!");
370         return;
371     }
372     GO_WINDOW(3);
373     vx_connector = (inl(BASE + VX_W3_INTERNAL_CFG)
374                         & INTERNAL_CONNECTOR_MASK)
375                         >> INTERNAL_CONNECTOR_BITS;
376     if (vx_connector & 0x10) {
377         vx_connector &= 0x0f;
378         printf("[*%s*]", conn_tab[vx_connector].name);
379         printf(": disable 'auto select' with DOS util!");
380     } else {
381         printf("[*%s*]", conn_tab[vx_connector].name);
382     }
383 }
384 
385 static void
386 vxsetlink(void)
387 {
388     int i, j;
389     char *reason, *warning;
390     static char prev_conn = -1;
391 
392     if (prev_conn == -1) {
393         prev_conn = vx_connector;
394     }
395 
396     i = vx_connector;       /* default in EEPROM */
397     reason = "default";
398     warning = 0;
399 
400     if ((vx_connectors & conn_tab[vx_connector].bit) == 0) {
401         warning = "strange connector type in EEPROM.";
402         reason = "forced";
403         i = CONNECTOR_UTP;
404     }
405 
406         if (warning != 0) {
407             printf("warning: %s\n", warning);
408         }
409         printf("selected %s. (%s)\n", conn_tab[i].name, reason);
410 
411     /* Set the selected connector. */
412     GO_WINDOW(3);
413     j = inl(BASE + VX_W3_INTERNAL_CFG) & ~INTERNAL_CONNECTOR_MASK;
414     outl(j | (i <<INTERNAL_CONNECTOR_BITS), BASE + VX_W3_INTERNAL_CFG);
415 
416     /* First, disable all. */
417     outw(STOP_TRANSCEIVER, BASE + VX_COMMAND);
418     udelay(8000);
419     GO_WINDOW(4);
420     outw(0, BASE + VX_W4_MEDIA_TYPE);
421 
422     /* Second, enable the selected one. */
423     switch(i) {
424       case CONNECTOR_UTP:
425         GO_WINDOW(4);
426         outw(ENABLE_UTP, BASE + VX_W4_MEDIA_TYPE);
427         break;
428       case CONNECTOR_BNC:
429         outw(START_TRANSCEIVER,BASE + VX_COMMAND);
430         udelay(8000);
431         break;
432       case CONNECTOR_TX:
433       case CONNECTOR_FX:
434         GO_WINDOW(4);
435         outw(LINKBEAT_ENABLE, BASE + VX_W4_MEDIA_TYPE);
436         break;
437       default:  /* AUI and MII fall here */
438         break;
439     }
440     GO_WINDOW(1);
441 }
442 
443 static void t595_disable(struct dev *dev)
444 {
445 	struct nic *nic = (struct nic *)dev;
446 	t595_reset(nic);
447 
448 	outw(STOP_TRANSCEIVER, BASE + VX_COMMAND);
449 	udelay(8000);
450 	GO_WINDOW(4);
451 	outw(0, BASE + VX_W4_MEDIA_TYPE);
452 	GO_WINDOW(1);
453 }
454 
455 static void t595_irq(struct nic *nic __unused, irq_action_t action __unused)
456 {
457   switch ( action ) {
458   case DISABLE :
459     break;
460   case ENABLE :
461     break;
462   case FORCE :
463     break;
464   }
465 }
466 
467 /**************************************************************************
468 ETH_PROBE - Look for an adapter
469 ***************************************************************************/
470 static int t595_probe(struct dev *dev, struct pci_device *pci)
471 {
472 	struct nic *nic = (struct nic *)dev;
473 	int i;
474 	unsigned short *p;
475 
476 	if (pci->ioaddr == 0)
477 		return 0;
478 /*	eth_nic_base = probeaddrs[0] & ~3; */
479 	eth_nic_base = pci->ioaddr;
480 
481 	nic->irqno  = 0;
482 	nic->ioaddr = pci->ioaddr & ~3;
483 
484 	GO_WINDOW(0);
485 	outw(GLOBAL_RESET, BASE + VX_COMMAND);
486 	VX_BUSY_WAIT;
487 
488 	vxgetlink();
489 
490 /*
491 	printf("\nEEPROM:");
492 	for (i = 0; i < (EEPROMSIZE/2); i++) {
493 	  printf("%hX:", get_e(i));
494 	}
495 	printf("\n");
496 */
497 	/*
498 	* Read the station address from the eeprom
499 	*/
500 	p = (unsigned short *) nic->node_addr;
501 	for (i = 0; i < 3; i++) {
502 		GO_WINDOW(0);
503 		p[i] = htons(get_e(EEPROM_OEM_ADDR_0 + i));
504 		GO_WINDOW(2);
505 		outw(ntohs(p[i]), BASE + VX_W2_ADDR_0 + (i * 2));
506 	}
507 
508 	printf("Ethernet address: %!\n", nic->node_addr);
509 
510 	t595_reset(nic);
511 	dev->disable  = t595_disable;
512 	nic->poll     = t595_poll;
513 	nic->transmit = t595_transmit;
514 	nic->irq      = t595_irq;
515 	return 1;
516 
517 }
518 
519 static struct pci_id t595_nics[] = {
520 PCI_ROM(0x10b7, 0x5900, "3c590",           "3Com590"),		/* Vortex 10Mbps */
521 PCI_ROM(0x10b7, 0x5950, "3c595",           "3Com595"),		/* Vortex 100baseTx */
522 PCI_ROM(0x10b7, 0x5951, "3c595-1",         "3Com595"),		/* Vortex 100baseT4 */
523 PCI_ROM(0x10b7, 0x5952, "3c595-2",         "3Com595"),		/* Vortex 100base-MII */
524 PCI_ROM(0x10b7, 0x9000, "3c900-tpo",       "3Com900-TPO"),	/* 10 Base TPO */
525 PCI_ROM(0x10b7, 0x9001, "3c900-t4",        "3Com900-Combo"),	/* 10/100 T4 */
526 PCI_ROM(0x10b7, 0x9004, "3c900b-tpo",      "3Com900B-TPO"),	/* 10 Base TPO */
527 PCI_ROM(0x10b7, 0x9005, "3c900b-combo",    "3Com900B-Combo"),	/* 10 Base Combo */
528 PCI_ROM(0x10b7, 0x9006, "3c900b-tpb2",     "3Com900B-2/T"),	/* 10 Base TP and Base2 */
529 PCI_ROM(0x10b7, 0x900a, "3c900b-fl",       "3Com900B-FL"),	/* 10 Base F */
530 PCI_ROM(0x10b7, 0x9800, "3c980-cyclone-1", "3Com980-Cyclone"),	/* Cyclone */
531 PCI_ROM(0x10b7, 0x9805, "3c9805-1",        "3Com9805"),		/* Dual Port Server Cyclone */
532 PCI_ROM(0x10b7, 0x7646, "3csoho100-tx-1",  "3CSOHO100-TX"),	/* Hurricane */
533 PCI_ROM(0x10b7, 0x4500, "3c450-1",         "3Com450 HomePNA Tornado"),
534 };
535 
536 struct pci_driver t595_driver = {
537 	.type     = NIC_DRIVER,
538 	.name     = "3C595",
539 	.probe    = t595_probe,
540 	.ids      = t595_nics,
541 	.id_count = sizeof(t595_nics)/sizeof(t595_nics[0]),
542 	.class    = 0,
543 };
544 
545 /*
546  * Local variables:
547  *  c-basic-offset: 8
548  * End:
549  */
550 
551