xref: /minix/minix/drivers/net/dpeth/3c509.c (revision 9f988b79)
1 /*
2 **  File:	3c509.c		Jun. 01, 2000
3 **
4 **  Author:	Giovanni Falzoni <gfalzoni@inwind.it>
5 **
6 **  This file contains specific implementation of the ethernet
7 **  device driver for 3Com Etherlink III (3c509) boards.
8 **  NOTE: The board has to be setup to disable PnP and to assign
9 **	  I/O base and IRQ.  The driver is for ISA bus only
10 */
11 
12 #include <minix/drivers.h>
13 #include <minix/netdriver.h>
14 #include <net/gen/ether.h>
15 #include <net/gen/eth_io.h>
16 
17 #include "dp.h"
18 
19 #if (ENABLE_3C509 == 1)
20 
21 #include "3c509.h"
22 
23 static const char *const IfNamesMsg[] = {
24 	"10BaseT", "AUI", "unknown", "BNC",
25 };
26 
27 /*
28 **  Name:	el3_update_stats
29 **  Function:	Reads statistic counters from board
30 **  		and updates local counters.
31 */
32 static void el3_update_stats(dpeth_t * dep)
33 {
34 
35   /* Disables statistics while reading and switches to the correct window */
36   outw_el3(dep, REG_CmdStatus, CMD_StatsDisable);
37   SetWindow(WNO_Statistics);
38 
39   /* Reads everything, adding values to the local counters */
40   dep->de_stat.ets_sendErr += inb_el3(dep, REG_TxCarrierLost);	/* Reg. 00 */
41   dep->de_stat.ets_sendErr += inb_el3(dep, REG_TxNoCD);		/* Reg. 01 */
42   dep->de_stat.ets_collision += inb_el3(dep, REG_TxMultColl);	/* Reg. 02 */
43   dep->de_stat.ets_collision += inb_el3(dep, REG_TxSingleColl);	/* Reg. 03 */
44   dep->de_stat.ets_collision += inb_el3(dep, REG_TxLate);	/* Reg. 04 */
45   dep->de_stat.ets_recvErr += inb_el3(dep, REG_RxDiscarded);	/* Reg. 05 */
46   dep->de_stat.ets_packetT += inb_el3(dep, REG_TxFrames);	/* Reg. 06 */
47   dep->de_stat.ets_packetR += inb_el3(dep, REG_RxFrames);	/* Reg. 07 */
48   dep->de_stat.ets_transDef += inb_el3(dep, REG_TxDefer);	/* Reg. 08 */
49   dep->bytes_Rx += (unsigned) inw_el3(dep, REG_RxBytes);	/* Reg. 10 */
50   dep->bytes_Tx += (unsigned) inw_el3(dep, REG_TxBytes);	/* Reg. 12 */
51 
52   /* Goes back to operating window and enables statistics */
53   SetWindow(WNO_Operating);
54   outw_el3(dep, REG_CmdStatus, CMD_StatsEnable);
55 }
56 
57 /*
58 **  Name:	el3_getstats
59 **  Function:	Reads statistics counters from board.
60 */
61 static void el3_getstats(dpeth_t * dep)
62 {
63 
64   el3_update_stats(dep);
65 }
66 
67 /*
68 **  Name:	el3_dodump
69 **  Function:	Dumps counter on screen (support for console display).
70 */
71 static void el3_dodump(dpeth_t * dep)
72 {
73 
74   el3_getstats(dep);
75 }
76 
77 /*
78 **  Name:	el3_rx_mode
79 **  Function:	Initializes receiver mode
80 */
81 static void el3_rx_mode(dpeth_t * dep)
82 {
83 
84   dep->de_recv_mode = FilterIndividual;
85   if (dep->de_flags & DEF_BROAD) dep->de_recv_mode |= FilterBroadcast;
86   if (dep->de_flags & DEF_MULTI) dep->de_recv_mode |= FilterMulticast;
87   if (dep->de_flags & DEF_PROMISC) dep->de_recv_mode |= FilterPromiscuous;
88 
89   outw_el3(dep, REG_CmdStatus, CMD_RxReset);
90   outw_el3(dep, REG_CmdStatus, CMD_SetRxFilter | dep->de_recv_mode);
91   outw_el3(dep, REG_CmdStatus, CMD_RxEnable);
92 }
93 
94 /*
95 **  Name:	el3_reset
96 **  Function:	Reset function specific for Etherlink hardware.
97 */
98 static void el3_reset(dpeth_t * UNUSED(dep))
99 {
100 
101 }
102 
103 /*
104 **  Name:	el3_recv
105 **  Function:	Receive function.  Called from interrupt handler or
106 **  		from main to unload recv. buffer (packet to client)
107 */
108 static ssize_t el3_recv(dpeth_t *dep, struct netdriver_data *data, size_t max)
109 {
110   buff_t *rxptr;
111   size_t size;
112 
113   if ((rxptr = dep->de_recvq_head) == NULL)
114 	return SUSPEND;
115 
116   /* Remove buffer from queue */
117   if (dep->de_recvq_tail == dep->de_recvq_head)
118 	dep->de_recvq_head = dep->de_recvq_tail = NULL;
119   else
120 	dep->de_recvq_head = rxptr->next;
121 
122   /* Copy buffer to user area and free it */
123   size = MIN(rxptr->size, max);
124 
125   netdriver_copyout(data, 0, rxptr->buffer, size);
126 
127   /* Return buffer to the idle pool */
128   free_buff(dep, rxptr);
129 
130   return size;
131 }
132 
133 /*
134 **  Name:	el3_rx_complete
135 **  Function:	Upon receiving a packet, provides status checks
136 **  		and if packet is OK copies it to local buffer.
137 */
138 static void el3_rx_complete(dpeth_t * dep)
139 {
140   short int RxStatus;
141   int pktsize;
142   buff_t *rxptr;
143 
144   RxStatus = inw_el3(dep, REG_RxStatus);
145   pktsize = RxStatus & RXS_Length;	/* Mask off packet length */
146 
147   if (RxStatus & RXS_Error) {
148 
149 	/* First checks for receiving errors */
150 	RxStatus &= RXS_ErrType;
151 	switch (RxStatus) {	/* Bad packet (see error type) */
152 	    case RXS_Dribble:
153 	    case RXS_Oversize:
154 	    case RXS_Runt:	dep->de_stat.ets_recvErr += 1;	break;
155 	    case RXS_Overrun:	dep->de_stat.ets_OVW += 1;	break;
156 	    case RXS_Framing:	dep->de_stat.ets_frameAll += 1;	break;
157 	    case RXS_CRC:	dep->de_stat.ets_CRCerr += 1;	break;
158 	}
159 
160   } else if ((rxptr = alloc_buff(dep, pktsize + sizeof(buff_t))) == NULL) {
161 	/* Memory not available. Drop packet */
162 	dep->de_stat.ets_fifoOver += 1;
163 
164   } else {
165 	/* Good packet.  Read it from FIFO */
166 	insb(dep->de_data_port, rxptr->buffer, pktsize);
167 	rxptr->next = NULL;
168 	rxptr->size = pktsize;
169 
170 	/* Queue packet to receive queue */
171 	if (dep->de_recvq_head == NULL)
172 		dep->de_recvq_head = rxptr;
173 	else
174 		dep->de_recvq_tail->next = rxptr;
175 	dep->de_recvq_tail = rxptr;
176 
177 	/* Reply to pending Receive requests, if any */
178 	netdriver_recv();
179   }
180 
181   /* Discard top packet from queue */
182   outw_el3(dep, REG_CmdStatus, CMD_RxDiscard);
183 }
184 
185 /*
186 **  Name:	el3_send
187 **  Function:	Send function.  Called from main to transit a packet or
188 **  		from interrupt handler when Tx FIFO gets available.
189 */
190 static int el3_send(dpeth_t *dep, struct netdriver_data *data, size_t size)
191 {
192   clock_t now;
193   int ix;
194   short int TxStatus;
195   size_t padding;
196 
197   now = getticks();
198   if ((dep->de_flags & DEF_XMIT_BUSY) &&
199       (now - dep->de_xmit_start) > 4) {
200 
201 	DEBUG(printf("3c509:  Transmitter timed out. Resetting ....\n");)
202 	dep->de_stat.ets_sendErr += 1;
203 	/* Resets and restars the transmitter */
204 	outw_el3(dep, REG_CmdStatus, CMD_TxReset);
205 	outw_el3(dep, REG_CmdStatus, CMD_TxEnable);
206 	dep->de_flags &= NOT(DEF_XMIT_BUSY);
207   }
208   if (dep->de_flags & DEF_XMIT_BUSY)
209 	return SUSPEND;
210 
211   /* Writes Transmitter preamble 1st Word (packet len, no ints) */
212   outw_el3(dep, REG_TxFIFO, size);
213   /* Writes Transmitter preamble 2nd Word (all zero) */
214   outw_el3(dep, REG_TxFIFO, 0);
215   /* Writes packet */
216   netdriver_portoutb(data, 0, dep->de_data_port, size);
217   padding = size;
218   while ((padding++ % sizeof(long)) != 0) outb(dep->de_data_port, 0x00);
219 
220   dep->de_xmit_start = getticks();
221   dep->de_flags |= DEF_XMIT_BUSY;
222   if (inw_el3(dep, REG_TxFree) > ETH_MAX_PACK_SIZE) {
223 	/* Tx has enough room for a packet of maximum size */
224 	dep->de_flags &= NOT(DEF_XMIT_BUSY);
225   } else {
226 	/* Interrupt driver when enough room is available */
227 	outw_el3(dep, REG_CmdStatus, CMD_SetTxAvailable | ETH_MAX_PACK_SIZE);
228   }
229 
230   /* Pops Tx status stack */
231   for (ix = 4; --ix && (TxStatus = inb_el3(dep, REG_TxStatus)) > 0;) {
232 	if (TxStatus & 0x38) dep->de_stat.ets_sendErr += 1;
233 	if (TxStatus & 0x30)
234 		outw_el3(dep, REG_CmdStatus, CMD_TxReset);
235 	if (TxStatus & 0x3C)
236 		outw_el3(dep, REG_CmdStatus, CMD_TxEnable);
237 	outb_el3(dep, REG_TxStatus, 0);
238   }
239 
240   return OK;
241 }
242 
243 /*
244 **  Name:	el3_close
245 **  Function:	Stops board and makes it ready to shut down.
246 */
247 static void el3_close(dpeth_t * dep)
248 {
249 
250   /* Disables statistics, Receiver and Transmitter */
251   outw_el3(dep, REG_CmdStatus, CMD_StatsDisable);
252   outw_el3(dep, REG_CmdStatus, CMD_RxDisable);
253   outw_el3(dep, REG_CmdStatus, CMD_TxDisable);
254 
255   if (dep->de_if_port == BNC_XCVR) {
256 	outw_el3(dep, REG_CmdStatus, CMD_StopIntXcvr);
257 	/* micro_delay(5000); */
258 
259   } else if (dep->de_if_port == TP_XCVR) {
260 	SetWindow(WNO_Diagnostics);
261 	outw_el3(dep, REG_MediaStatus, inw_el3(dep, REG_MediaStatus) &
262 		 NOT((MediaLBeatEnable | MediaJabberEnable)));
263 	/* micro_delay(5000); */
264   }
265   DEBUG(printf("%s: stopping Etherlink ... \n", dep->de_name));
266   /* Issues a global reset
267   outw_el3(dep, REG_CmdStatus, CMD_GlobalReset); */
268   sys_irqdisable(&dep->de_hook);	/* Disable interrupt */
269 }
270 
271 /*
272 **  Name:	el3_interrupt
273 **  Function:	Interrupt handler.  Acknwledges transmit interrupts
274 **  		or unloads receive buffer to memory queue.
275 */
276 static void el3_interrupt(dpeth_t * dep)
277 {
278   int loop;
279   unsigned short isr;
280 
281   for (loop = 5; loop > 0 && ((isr = inw_el3(dep, REG_CmdStatus)) &
282          (INT_Latch | INT_RxComplete | INT_UpdateStats)); loop -= 1) {
283 
284 	if (isr & INT_RxComplete)	/* Got a new packet */
285 		el3_rx_complete(dep);
286 
287 	if (isr & INT_TxAvailable) {	/* Tx has room for big packets */
288 		DEBUG(printf("3c509: got Tx interrupt, Status=0x%04x\n", isr);)
289 		dep->de_flags &= NOT(DEF_XMIT_BUSY);
290 		outw_el3(dep, REG_CmdStatus, CMD_Acknowledge | INT_TxAvailable);
291 		netdriver_send();
292 	}
293 	if (isr & (INT_AdapterFail | INT_RxEarly | INT_UpdateStats)) {
294 
295 		if (isr & INT_UpdateStats)	/* Empties statistics */
296 			el3_getstats(dep);
297 
298 		if (isr & INT_RxEarly)	/* Not really used. Do nothing */
299 			outw_el3(dep, REG_CmdStatus, CMD_Acknowledge | (INT_RxEarly));
300 
301 		if (isr & INT_AdapterFail) {
302 			/* Adapter error. Reset and re-enable receiver */
303 			DEBUG(printf("3c509: got Rx fail interrupt, Status=0x%04x\n", isr);)
304 			el3_rx_mode(dep);
305 			outw_el3(dep, REG_CmdStatus, CMD_Acknowledge | INT_AdapterFail);
306 		}
307 	}
308 
309 	/* Acknowledge interrupt */
310 	outw_el3(dep, REG_CmdStatus, CMD_Acknowledge | (INT_Latch | INT_Requested));
311   }
312 }
313 
314 /*
315 **  Name:	el3_read_eeprom
316 **  Function:	Reads the EEPROM at specified address
317 */
318 static unsigned el3_read_eeprom(port_t port, unsigned address)
319 {
320   unsigned int result;
321   int bit;
322 
323   address |= EL3_READ_EEPROM;
324   outb(port, address);
325   micro_delay(5000);		/* Allows EEPROM reads */
326   for (result = 0, bit = 16; bit > 0; bit -= 1) {
327 	result = (result << 1) | (inb(port) & 0x0001);
328   }
329   return result;
330 }
331 
332 /*
333 **  Name:	el3_read_StationAddress
334 **  Function:	Reads station address from board
335 */
336 static void el3_read_StationAddress(dpeth_t * dep)
337 {
338   unsigned int ix, rc;
339 
340   for (ix = EE_3COM_NODE_ADDR; ix < SA_ADDR_LEN+EE_3COM_NODE_ADDR;) {
341 	/* Accesses with word No. */
342 	rc = el3_read_eeprom(dep->de_id_port, ix / 2);
343 	/* Swaps bytes of word */
344 	dep->de_address.ea_addr[ix++] = (rc >> 8) & 0xFF;
345 	dep->de_address.ea_addr[ix++] = rc & 0xFF;
346   }
347 }
348 
349 /*
350 **  Name:	el3_open
351 **  Function:	Initalizes board hardware and driver data structures.
352 */
353 static void el3_open(dpeth_t * dep)
354 {
355   unsigned int AddrCfgReg, ResCfgReg;
356   unsigned int ix;
357 
358   el3_read_StationAddress(dep);	/* Get ethernet address */
359 
360   /* Get address and resource configurations */
361   AddrCfgReg = el3_read_eeprom(dep->de_id_port, EE_ADDR_CFG);
362   ResCfgReg = el3_read_eeprom(dep->de_id_port, EE_RESOURCE_CFG);
363   outb(dep->de_id_port, EL3_ACTIVATE);	/* Activate the board */
364 
365   /* Gets xcvr configuration */
366   dep->de_if_port = AddrCfgReg & EL3_CONFIG_XCVR_MASK;
367 
368   AddrCfgReg = ((AddrCfgReg & EL3_CONFIG_IOBASE_MASK) << 4) + EL3_IO_BASE_ADDR;
369   if (AddrCfgReg != dep->de_base_port)
370 	panic("Bad I/O port for Etherlink board");
371 
372   ResCfgReg >>= 12;
373   dep->de_irq &= NOT(DEI_DEFAULT);	/* Strips the default flag */
374   if (ResCfgReg != dep->de_irq) panic("Bad IRQ for Etherlink board");
375 
376   SetWindow(WNO_Setup);
377 
378   /* Reset transmitter and receiver */
379   outw_el3(dep, REG_CmdStatus, CMD_TxReset);
380   outw_el3(dep, REG_CmdStatus, CMD_RxReset);
381 
382   /* Enable the adapter */
383   outb_el3(dep, REG_CfgControl, EL3_EnableAdapter);
384   /* Disable Status bits */
385   outw_el3(dep, REG_CmdStatus, CMD_SetStatusEnab + 0x00);
386 
387   /* Set "my own" address */
388   SetWindow(WNO_StationAddress);
389   for (ix = 0; ix < 6; ix += 1)
390 	outb_el3(dep, REG_SA0_1 + ix, dep->de_address.ea_addr[ix]);
391 
392   /* Start Transceivers as required */
393   if (dep->de_if_port == BNC_XCVR) {
394 	/* Start internal transceiver for Coaxial cable */
395 	outw_el3(dep, REG_CmdStatus, CMD_StartIntXcvr);
396 	micro_delay(5000);
397 
398   } else if (dep->de_if_port == TP_XCVR) {
399 	/* Start internal transceiver for Twisted pair cable */
400 	SetWindow(WNO_Diagnostics);
401 	outw_el3(dep, REG_MediaStatus,
402 		 inw_el3(dep, REG_MediaStatus) | (MediaLBeatEnable | MediaJabberEnable));
403   }
404 
405   /* Switch to the statistic window, and clear counts (by reading) */
406   SetWindow(WNO_Statistics);
407   for (ix = REG_TxCarrierLost; ix <= REG_TxDefer; ix += 1) inb_el3(dep, ix);
408   inw_el3(dep, REG_RxBytes);
409   inw_el3(dep, REG_TxBytes);
410 
411   /* Switch to operating window for normal use */
412   SetWindow(WNO_Operating);
413 
414   /* Receive individual address & broadcast. (Mofified later by rx_mode) */
415   outw_el3(dep, REG_CmdStatus, CMD_SetRxFilter |
416 	 (FilterIndividual | FilterBroadcast));
417 
418   /* Turn on statistics */
419   outw_el3(dep, REG_CmdStatus, CMD_StatsEnable);
420 
421   /* Enable transmitter and receiver */
422   outw_el3(dep, REG_CmdStatus, CMD_TxEnable);
423   outw_el3(dep, REG_CmdStatus, CMD_RxEnable);
424 
425   /* Enable all the status bits */
426   outw_el3(dep, REG_CmdStatus, CMD_SetStatusEnab | 0xFF);
427 
428   /* Acknowledge all interrupts to clear adapter. Enable interrupts */
429   outw_el3(dep, REG_CmdStatus, CMD_Acknowledge | 0xFF);
430   outw_el3(dep, REG_CmdStatus, CMD_SetIntMask |
431     (INT_Latch | INT_TxAvailable | INT_RxComplete | INT_UpdateStats));
432 
433   /* Ready to operate, sets the environment for eth_task */
434   dep->de_data_port = dep->de_base_port;
435   /* Allocates Rx/Tx buffers */
436   init_buff(dep, NULL);
437 
438   /* Device specific functions */
439   dep->de_recvf = el3_recv;
440   dep->de_sendf = el3_send;
441   dep->de_flagsf = el3_rx_mode;
442   dep->de_resetf = el3_reset;
443   dep->de_getstatsf = el3_getstats;
444   dep->de_dumpstatsf = el3_dodump;
445   dep->de_interruptf = el3_interrupt;
446 
447   printf("%s: Etherlink III (%s) at %X:%d, %s port - ",
448          dep->de_name, "3c509", dep->de_base_port, dep->de_irq,
449          IfNamesMsg[dep->de_if_port >> 14]);
450   for (ix = 0; ix < SA_ADDR_LEN; ix += 1)
451 	printf("%02X%c", dep->de_address.ea_addr[ix],
452 	       ix < SA_ADDR_LEN - 1 ? ':' : '\n');
453 }
454 
455 /*
456 **  Name:	int el3_checksum
457 **  Function:	Reads EEPROM and computes checksum.
458 */
459 static unsigned short el3_checksum(port_t port)
460 {
461   unsigned short rc, checksum, address;
462   unsigned char lo, hi;
463 
464   for (checksum = address = 0; address < 15; address += 1) {
465 	rc = el3_read_eeprom(port, address);
466 	lo = rc & 0xFF;
467 	hi = (rc >> 8) & 0xFF;
468 	if ((address == EE_PROD_ID && (rc & EE_PROD_ID_MASK) != EL3_PRODUCT_ID) ||
469 	    (address == EE_3COM_CODE && rc != EL3_3COM_CODE))
470 		return address;
471 	if (address == EE_ADDR_CFG ||
472 	    address == EE_RESOURCE_CFG ||
473 	    address == EE_SW_CONFIG_INFO) {
474 		lo ^= hi;
475 		hi = 0;
476 	} else {
477 		hi ^= lo;
478 		lo = 0;
479 	}
480 	rc = ((unsigned) hi << 8) + lo;
481 	checksum ^= rc;
482   }
483   rc = el3_read_eeprom(port, address);
484   return(checksum ^= rc);	/* If OK checksum is 0 */
485 }
486 
487 /*
488 **  Name:	el3_write_id
489 **  Function:	Writes the ID sequence to the board.
490 */
491 static void el3_write_id(port_t port)
492 {
493   int ix, pattern;
494 
495   outb(port, 0);		/* Selects the ID port */
496   outb(port, 0);		/* Resets hardware pattern generator */
497   for (pattern = ix = 0x00FF; ix > 0; ix -= 1) {
498 	outb(port, pattern);
499 	pattern <<= 1;
500 	pattern = (pattern & 0x0100) ? pattern ^ 0xCF : pattern;
501   }
502 }
503 
504 /*
505 **  Name:	el3_probe
506 **  Function:	Checks for presence of the board.
507 */
508 int el3_probe(dpeth_t * dep)
509 {
510   port_t id_port;
511 
512   /* Don't ask me what is this for !! */
513   outb(0x0279, 0x02);	/* Select PnP config control register. */
514   outb(0x0A79, 0x02);	/* Return to WaitForKey state. */
515   /* Tests I/O ports in the 0x1xF range for a valid ID port */
516   for (id_port = 0x110; id_port < 0x200; id_port += 0x10) {
517 	outb(id_port, 0x00);
518 	outb(id_port, 0xFF);
519 	if (inb(id_port) & 0x01) break;
520   }
521   if (id_port == 0x200) return 0;	/* No board responding */
522 
523   el3_write_id(id_port);
524   outb(id_port, EL3_ID_GLOBAL_RESET);	/* Reset the board */
525   micro_delay(5000);		/* Technical reference says 162 micro sec. */
526   el3_write_id(id_port);
527   outb(id_port, EL3_SET_TAG_REGISTER);
528   micro_delay(5000);
529 
530   dep->de_id_port = id_port;	/* Stores ID port No. */
531   dep->de_ramsize =		/* RAM size is meaningless */
532 	dep->de_offset_page = 0;
533   dep->de_linmem = 0L;		/* Access is via I/O port  */
534 
535   /* Device specific functions */
536   dep->de_initf = el3_open;
537   dep->de_stopf = el3_close;
538 
539   return(el3_checksum(id_port) == 0);	/* Etherlink board found/not found */
540 }
541 
542 #endif				/* ENABLE_3C509 */
543 
544 /** 3c509.c **/
545