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