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