dec21140A ethernet driver for virtualpc, contributed by nicolas tittley.
[minix.git] / drivers / dpeth / 3c509.c
blobc170403ea0e3ede40d7b8b96ded1c80cf837fd39
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
11 ** $Id$
14 #include "drivers.h"
15 #include <minix/com.h>
16 #include <net/hton.h>
17 #include <net/gen/ether.h>
18 #include <net/gen/eth_io.h>
20 #include "dp.h"
22 #if (ENABLE_3C509 == 1)
24 #include "3c509.h"
26 static const char *const IfNamesMsg[] = {
27 "10BaseT", "AUI", "unknown", "BNC",
31 ** Name: void el3_update_stats(dpeth_t *dep)
32 ** Function: Reads statistic counters from board
33 ** and updates local counters.
35 static void el3_update_stats(dpeth_t * dep)
38 /* Disables statistics while reading and switches to the correct window */
39 outw_el3(dep, REG_CmdStatus, CMD_StatsDisable);
40 SetWindow(WNO_Statistics);
42 /* Reads everything, adding values to the local counters */
43 dep->de_stat.ets_sendErr += inb_el3(dep, REG_TxCarrierLost); /* Reg. 00 */
44 dep->de_stat.ets_sendErr += inb_el3(dep, REG_TxNoCD); /* Reg. 01 */
45 dep->de_stat.ets_collision += inb_el3(dep, REG_TxMultColl); /* Reg. 02 */
46 dep->de_stat.ets_collision += inb_el3(dep, REG_TxSingleColl); /* Reg. 03 */
47 dep->de_stat.ets_collision += inb_el3(dep, REG_TxLate); /* Reg. 04 */
48 dep->de_stat.ets_recvErr += inb_el3(dep, REG_RxDiscarded); /* Reg. 05 */
49 dep->de_stat.ets_packetT += inb_el3(dep, REG_TxFrames); /* Reg. 06 */
50 dep->de_stat.ets_packetR += inb_el3(dep, REG_RxFrames); /* Reg. 07 */
51 dep->de_stat.ets_transDef += inb_el3(dep, REG_TxDefer); /* Reg. 08 */
52 dep->bytes_Rx += (unsigned) inw_el3(dep, REG_RxBytes); /* Reg. 10 */
53 dep->bytes_Tx += (unsigned) inw_el3(dep, REG_TxBytes); /* Reg. 12 */
55 /* Goes back to operating window and enables statistics */
56 SetWindow(WNO_Operating);
57 outw_el3(dep, REG_CmdStatus, CMD_StatsEnable);
59 return;
63 ** Name: void el3_getstats(dpeth_t *dep)
64 ** Function: Reads statistics counters from board.
66 static void el3_getstats(dpeth_t * dep)
69 lock();
70 el3_update_stats(dep);
71 unlock();
72 return;
76 ** Name: void el3_dodump(dpeth_t *dep)
77 ** Function: Dumps counter on screen (support for console display).
79 static void el3_dodump(dpeth_t * dep)
82 el3_getstats(dep);
83 return;
87 ** Name: void el3_rx_mode(dpeth_t *dep)
88 ** Function: Initializes receiver mode
90 static void el3_rx_mode(dpeth_t * dep)
93 dep->de_recv_mode = FilterIndividual;
94 if (dep->de_flags & DEF_BROAD) dep->de_recv_mode |= FilterBroadcast;
95 if (dep->de_flags & DEF_MULTI) dep->de_recv_mode |= FilterMulticast;
96 if (dep->de_flags & DEF_PROMISC) dep->de_recv_mode |= FilterPromiscuous;
98 outw_el3(dep, REG_CmdStatus, CMD_RxReset);
99 outw_el3(dep, REG_CmdStatus, CMD_SetRxFilter | dep->de_recv_mode);
100 outw_el3(dep, REG_CmdStatus, CMD_RxEnable);
102 return;
106 ** Name: void el3_reset(dpeth_t *dep)
107 ** Function: Reset function specific for Etherlink hardware.
109 static void el3_reset(dpeth_t * dep)
112 return; /* Done */
116 ** Name: void el3_write_fifo(dpeth_t * dep, int pktsize);
117 ** Function: Writes a packet from user area to board.
118 ** Remark: Writing a word/dword at a time may result faster
119 ** but is a lot more complicated. Let's go simpler way.
121 static void el3_write_fifo(dpeth_t * dep, int pktsize)
123 int bytes, ix = 0;
124 iovec_dat_s_t *iovp = &dep->de_write_iovec;
125 int r, padding = pktsize;
127 do { /* Writes chuncks of packet from user buffers */
129 bytes = iovp->iod_iovec[ix].iov_size; /* Size of buffer */
130 if (bytes > pktsize) bytes = pktsize;
131 /* Writes from user buffer to Tx FIFO */
132 r= sys_safe_outsb(dep->de_data_port, iovp->iod_proc_nr,
133 iovp->iod_iovec[ix].iov_grant, 0, bytes);
134 if (r != OK)
135 panic(__FILE__, "el3_write_fifo: sys_safe_outsb failed", r);
137 if (++ix >= IOVEC_NR) { /* Next buffer of IO vector */
138 dp_next_iovec(iovp);
139 ix = 0;
141 /* Till packet done */
142 } while ((pktsize -= bytes) > 0);
143 while ((padding++ % sizeof(long)) != 0) outb(dep->de_data_port, 0x00);
144 return;
148 ** Name: void el3_recv(dpeth_t *dep, int fromint, int size)
149 ** Function: Receive function. Called from interrupt handler or
150 ** from main to unload recv. buffer (packet to client)
152 static void el3_recv(dpeth_t *dep, int fromint, int size)
154 buff_t *rxptr;
156 while ((dep->de_flags & DEF_READING) && (rxptr = dep->de_recvq_head)) {
158 lock(); /* Remove buffer from queue */
159 if (dep->de_recvq_tail == dep->de_recvq_head)
160 dep->de_recvq_head = dep->de_recvq_tail = NULL;
161 else
162 dep->de_recvq_head = rxptr->next;
163 unlock();
165 /* Copy buffer to user area and free it */
166 mem2user(dep, rxptr);
168 dep->de_read_s = rxptr->size;
169 dep->de_flags |= DEF_ACK_RECV;
170 dep->de_flags &= NOT(DEF_READING);
172 /* Return buffer to the idle pool */
173 free_buff(dep, rxptr);
175 return;
179 ** Name: void el3_rx_complete(dpeth_t * dep);
180 ** Function: Upon receiving a packet, provides status checks
181 ** and if packet is OK copies it to local buffer.
183 static void el3_rx_complete(dpeth_t * dep)
185 short int RxStatus;
186 int pktsize;
187 buff_t *rxptr;
189 RxStatus = inw_el3(dep, REG_RxStatus);
190 pktsize = RxStatus & RXS_Length; /* Mask off packet length */
192 if (RxStatus & RXS_Error) {
194 /* First checks for receiving errors */
195 RxStatus &= RXS_ErrType;
196 switch (RxStatus) { /* Bad packet (see error type) */
197 case RXS_Dribble:
198 case RXS_Oversize:
199 case RXS_Runt: dep->de_stat.ets_recvErr += 1; break;
200 case RXS_Overrun: dep->de_stat.ets_OVW += 1; break;
201 case RXS_Framing: dep->de_stat.ets_frameAll += 1; break;
202 case RXS_CRC: dep->de_stat.ets_CRCerr += 1; break;
205 } else if ((rxptr = alloc_buff(dep, pktsize + sizeof(buff_t))) == NULL) {
206 /* Memory not available. Drop packet */
207 dep->de_stat.ets_fifoOver += 1;
209 } else {
210 /* Good packet. Read it from FIFO */
211 insb(dep->de_data_port, SELF, rxptr->buffer, pktsize);
212 rxptr->next = NULL;
213 rxptr->size = pktsize;
215 lock(); /* Queue packet to receive queue */
216 if (dep->de_recvq_head == NULL)
217 dep->de_recvq_head = rxptr;
218 else
219 dep->de_recvq_tail->next = rxptr;
220 dep->de_recvq_tail = rxptr;
221 unlock();
223 /* Reply to pending Receive requests, if any */
224 el3_recv(dep, TRUE, pktsize);
227 /* Discard top packet from queue */
228 outw_el3(dep, REG_CmdStatus, CMD_RxDiscard);
230 return;
234 ** Name: void el3_send(dpeth_t *dep, int count)
235 ** Function: Send function. Called from main to transit a packet or
236 ** from interrupt handler when Tx FIFO gets available.
238 static void el3_send(dpeth_t * dep, int from_int, int count)
240 clock_t now;
241 int ix;
242 short int TxStatus;
244 getuptime(&now);
245 if ((dep->de_flags & DEF_XMIT_BUSY) &&
246 (now - dep->de_xmit_start) > 4) {
248 DEBUG(printf("3c509: Transmitter timed out. Resetting ....\n");)
249 dep->de_stat.ets_sendErr += 1;
250 /* Resets and restars the transmitter */
251 outw_el3(dep, REG_CmdStatus, CMD_TxReset);
252 outw_el3(dep, REG_CmdStatus, CMD_TxEnable);
253 dep->de_flags &= NOT(DEF_XMIT_BUSY);
255 if (!(dep->de_flags & DEF_XMIT_BUSY)) {
257 /* Writes Transmitter preamble 1st Word (packet len, no ints) */
258 outw_el3(dep, REG_TxFIFO, count);
259 /* Writes Transmitter preamble 2nd Word (all zero) */
260 outw_el3(dep, REG_TxFIFO, 0);
261 /* Writes packet */
262 el3_write_fifo(dep, count);
264 getuptime(&dep->de_xmit_start);
265 dep->de_flags |= (DEF_XMIT_BUSY | DEF_ACK_SEND);
266 if (inw_el3(dep, REG_TxFree) > ETH_MAX_PACK_SIZE) {
267 /* Tx has enough room for a packet of maximum size */
268 dep->de_flags &= NOT(DEF_XMIT_BUSY | DEF_SENDING);
269 } else {
270 /* Interrupt driver when enough room is available */
271 outw_el3(dep, REG_CmdStatus, CMD_SetTxAvailable | ETH_MAX_PACK_SIZE);
272 dep->de_flags &= NOT(DEF_SENDING);
275 /* Pops Tx status stack */
276 for (ix = 4; --ix && (TxStatus = inb_el3(dep, REG_TxStatus)) > 0;) {
277 if (TxStatus & 0x38) dep->de_stat.ets_sendErr += 1;
278 if (TxStatus & 0x30)
279 outw_el3(dep, REG_CmdStatus, CMD_TxReset);
280 if (TxStatus & 0x3C)
281 outw_el3(dep, REG_CmdStatus, CMD_TxEnable);
282 outb_el3(dep, REG_TxStatus, 0);
285 return;
289 ** Name: void el3_close(dpeth_t *dep)
290 ** Function: Stops board and makes it ready to shut down.
292 static void el3_close(dpeth_t * dep)
295 /* Disables statistics, Receiver and Transmitter */
296 outw_el3(dep, REG_CmdStatus, CMD_StatsDisable);
297 outw_el3(dep, REG_CmdStatus, CMD_RxDisable);
298 outw_el3(dep, REG_CmdStatus, CMD_TxDisable);
300 if (dep->de_if_port == BNC_XCVR) {
301 outw_el3(dep, REG_CmdStatus, CMD_StopIntXcvr);
302 /* milli_delay(5); */
304 } else if (dep->de_if_port == TP_XCVR) {
305 SetWindow(WNO_Diagnostics);
306 outw_el3(dep, REG_MediaStatus, inw_el3(dep, REG_MediaStatus) &
307 NOT((MediaLBeatEnable | MediaJabberEnable)));
308 /* milli_delay(5); */
310 DEBUG(printf("%s: stopping Etherlink ... \n", dep->de_name));
311 /* Issues a global reset
312 outw_el3(dep, REG_CmdStatus, CMD_GlobalReset); */
313 sys_irqdisable(&dep->de_hook); /* Disable interrupt */
315 return;
319 ** Name: void el3_interrupt(dpeth_t *dep)
320 ** Function: Interrupt handler. Acknwledges transmit interrupts
321 ** or unloads receive buffer to memory queue.
323 static void el3_interrupt(dpeth_t * dep)
325 int loop;
326 unsigned short isr;
328 for (loop = 5; loop > 0 && ((isr = inw_el3(dep, REG_CmdStatus)) &
329 (INT_Latch | INT_RxComplete | INT_UpdateStats)); loop -= 1) {
331 if (isr & INT_RxComplete) /* Got a new packet */
332 el3_rx_complete(dep);
334 if (isr & INT_TxAvailable) { /* Tx has room for big packets */
335 DEBUG(printf("3c509: got Tx interrupt, Status=0x%04x\n", isr);)
336 dep->de_flags &= NOT(DEF_XMIT_BUSY);
337 outw_el3(dep, REG_CmdStatus, CMD_Acknowledge | INT_TxAvailable);
338 if (dep->de_flags & DEF_SENDING) /* Send pending */
339 el3_send(dep, TRUE, dep->de_send_s);
341 if (isr & (INT_AdapterFail | INT_RxEarly | INT_UpdateStats)) {
343 if (isr & INT_UpdateStats) /* Empties statistics */
344 el3_getstats(dep);
346 if (isr & INT_RxEarly) /* Not really used. Do nothing */
347 outw_el3(dep, REG_CmdStatus, CMD_Acknowledge | (INT_RxEarly));
349 if (isr & INT_AdapterFail) {
350 /* Adapter error. Reset and re-enable receiver */
351 DEBUG(printf("3c509: got Rx fail interrupt, Status=0x%04x\n", isr);)
352 el3_rx_mode(dep);
353 outw_el3(dep, REG_CmdStatus, CMD_Acknowledge | INT_AdapterFail);
357 /* Acknowledge interrupt */
358 outw_el3(dep, REG_CmdStatus, CMD_Acknowledge | (INT_Latch | INT_Requested));
360 return;
364 ** Name: unsigned el3_read_eeprom(port_t port, unsigned address);
365 ** Function: Reads the EEPROM at specified address
367 static unsigned el3_read_eeprom(port_t port, unsigned address)
369 unsigned int result;
370 int bit;
372 address |= EL3_READ_EEPROM;
373 outb(port, address);
374 milli_delay(5); /* Allows EEPROM reads */
375 for (result = 0, bit = 16; bit > 0; bit -= 1) {
376 result = (result << 1) | (inb(port) & 0x0001);
378 return result;
382 ** Name: void el3_read_StationAddress(dpeth_t *dep)
383 ** Function: Reads station address from board
385 static void el3_read_StationAddress(dpeth_t * dep)
387 unsigned int ix, rc;
389 for (ix = EE_3COM_NODE_ADDR; ix < SA_ADDR_LEN;) {
390 /* Accesses with word No. */
391 rc = el3_read_eeprom(dep->de_id_port, ix / 2);
392 /* Swaps bytes of word */
393 dep->de_address.ea_addr[ix++] = (rc >> 8) & 0xFF;
394 dep->de_address.ea_addr[ix++] = rc & 0xFF;
396 return;
400 ** Name: void el3_open(dpeth_t *dep)
401 ** Function: Initalizes board hardware and driver data structures.
403 static void el3_open(dpeth_t * dep)
405 unsigned int AddrCfgReg, ResCfgReg;
406 unsigned int ix;
408 el3_read_StationAddress(dep); /* Get ethernet address */
410 /* Get address and resource configurations */
411 AddrCfgReg = el3_read_eeprom(dep->de_id_port, EE_ADDR_CFG);
412 ResCfgReg = el3_read_eeprom(dep->de_id_port, EE_RESOURCE_CFG);
413 outb(dep->de_id_port, EL3_ACTIVATE); /* Activate the board */
415 /* Gets xcvr configuration */
416 dep->de_if_port = AddrCfgReg & EL3_CONFIG_XCVR_MASK;
418 AddrCfgReg = ((AddrCfgReg & EL3_CONFIG_IOBASE_MASK) << 4) + EL3_IO_BASE_ADDR;
419 if (AddrCfgReg != dep->de_base_port)
420 panic(dep->de_name, "Bad I/O port for Etherlink board", NO_NUM);
422 ResCfgReg >>= 12;
423 dep->de_irq &= NOT(DEI_DEFAULT); /* Strips the default flag */
424 if (ResCfgReg != dep->de_irq) panic(dep->de_name, "Bad IRQ for Etherlink board", NO_NUM);
426 SetWindow(WNO_Setup);
428 /* Reset transmitter and receiver */
429 outw_el3(dep, REG_CmdStatus, CMD_TxReset);
430 outw_el3(dep, REG_CmdStatus, CMD_RxReset);
432 /* Enable the adapter */
433 outb_el3(dep, REG_CfgControl, EL3_EnableAdapter);
434 /* Disable Status bits */
435 outw_el3(dep, REG_CmdStatus, CMD_SetStatusEnab + 0x00);
437 /* Set "my own" address */
438 SetWindow(WNO_StationAddress);
439 for (ix = 0; ix < 6; ix += 1)
440 outb_el3(dep, REG_SA0_1 + ix, dep->de_address.ea_addr[ix]);
442 /* Start Transceivers as required */
443 if (dep->de_if_port == BNC_XCVR) {
444 /* Start internal transceiver for Coaxial cable */
445 outw_el3(dep, REG_CmdStatus, CMD_StartIntXcvr);
446 milli_delay(5);
448 } else if (dep->de_if_port == TP_XCVR) {
449 /* Start internal transceiver for Twisted pair cable */
450 SetWindow(WNO_Diagnostics);
451 outw_el3(dep, REG_MediaStatus,
452 inw_el3(dep, REG_MediaStatus) | (MediaLBeatEnable | MediaJabberEnable));
455 /* Switch to the statistic window, and clear counts (by reading) */
456 SetWindow(WNO_Statistics);
457 for (ix = REG_TxCarrierLost; ix <= REG_TxDefer; ix += 1) inb_el3(dep, ix);
458 inw_el3(dep, REG_RxBytes);
459 inw_el3(dep, REG_TxBytes);
461 /* Switch to operating window for normal use */
462 SetWindow(WNO_Operating);
464 /* Receive individual address & broadcast. (Mofified later by rx_mode) */
465 outw_el3(dep, REG_CmdStatus, CMD_SetRxFilter |
466 (FilterIndividual | FilterBroadcast));
468 /* Turn on statistics */
469 outw_el3(dep, REG_CmdStatus, CMD_StatsEnable);
471 /* Enable transmitter and receiver */
472 outw_el3(dep, REG_CmdStatus, CMD_TxEnable);
473 outw_el3(dep, REG_CmdStatus, CMD_RxEnable);
475 /* Enable all the status bits */
476 outw_el3(dep, REG_CmdStatus, CMD_SetStatusEnab | 0xFF);
478 /* Acknowledge all interrupts to clear adapter. Enable interrupts */
479 outw_el3(dep, REG_CmdStatus, CMD_Acknowledge | 0xFF);
480 outw_el3(dep, REG_CmdStatus, CMD_SetIntMask |
481 (INT_Latch | INT_TxAvailable | INT_RxComplete | INT_UpdateStats));
483 /* Ready to operate, sets the environment for eth_task */
484 dep->de_data_port = dep->de_base_port;
485 /* Allocates Rx/Tx buffers */
486 init_buff(dep, NULL);
488 /* Device specific functions */
489 dep->de_recvf = el3_recv;
490 dep->de_sendf = el3_send;
491 dep->de_flagsf = el3_rx_mode;
492 dep->de_resetf = el3_reset;
493 dep->de_getstatsf = el3_getstats;
494 dep->de_dumpstatsf = el3_dodump;
495 dep->de_interruptf = el3_interrupt;
497 printf("%s: Etherlink III (%s) at %X:%d, %s port - ",
498 dep->de_name, "3c509", dep->de_base_port, dep->de_irq,
499 IfNamesMsg[dep->de_if_port >> 14]);
500 for (ix = 0; ix < SA_ADDR_LEN; ix += 1)
501 printf("%02X%c", dep->de_address.ea_addr[ix],
502 ix < SA_ADDR_LEN - 1 ? ':' : '\n');
504 return; /* Done */
508 ** Name: unsigned int el3_checksum(port_t port);
509 ** Function: Reads EEPROM and computes checksum.
511 static unsigned short el3_checksum(port_t port)
513 unsigned short rc, checksum, address;
514 unsigned char lo, hi;
516 for (checksum = address = 0; address < 15; address += 1) {
517 rc = el3_read_eeprom(port, address);
518 lo = rc & 0xFF;
519 hi = (rc >> 8) & 0xFF;
520 if ((address == EE_PROD_ID && (rc & EE_PROD_ID_MASK) != EL3_PRODUCT_ID) ||
521 (address == EE_3COM_CODE && rc != EL3_3COM_CODE))
522 return address;
523 if (address == EE_ADDR_CFG ||
524 address == EE_RESOURCE_CFG ||
525 address == EE_SW_CONFIG_INFO) {
526 lo ^= hi;
527 hi = 0;
528 } else {
529 hi ^= lo;
530 lo = 0;
532 rc = ((unsigned) hi << 8) + lo;
533 checksum ^= rc;
535 rc = el3_read_eeprom(port, address);
536 return(checksum ^= rc); /* If OK checksum is 0 */
540 ** Name: void el3_write_id(port_t port);
541 ** Function: Writes the ID sequence to the board.
543 static void el3_write_id(port_t port)
545 int ix, pattern;
547 outb(port, 0); /* Selects the ID port */
548 outb(port, 0); /* Resets hardware pattern generator */
549 for (pattern = ix = 0x00FF; ix > 0; ix -= 1) {
550 outb(port, pattern);
551 pattern <<= 1;
552 pattern = (pattern & 0x0100) ? pattern ^ 0xCF : pattern;
554 return;
558 ** Name: int el3_probe(dpeth_t *dep)
559 ** Function: Checks for presence of the board.
561 PUBLIC int el3_probe(dpeth_t * dep)
563 port_t id_port;
565 /* Don't ask me what is this for !! */
566 outb(0x0279, 0x02); /* Select PnP config control register. */
567 outb(0x0A79, 0x02); /* Return to WaitForKey state. */
568 /* Tests I/O ports in the 0x1xF range for a valid ID port */
569 for (id_port = 0x110; id_port < 0x200; id_port += 0x10) {
570 outb(id_port, 0x00);
571 outb(id_port, 0xFF);
572 if (inb(id_port) & 0x01) break;
574 if (id_port == 0x200) return 0; /* No board responding */
576 el3_write_id(id_port);
577 outb(id_port, EL3_ID_GLOBAL_RESET); /* Reset the board */
578 milli_delay(5); /* Technical reference says 162 micro sec. */
579 el3_write_id(id_port);
580 outb(id_port, EL3_SET_TAG_REGISTER);
581 milli_delay(5);
583 dep->de_id_port = id_port; /* Stores ID port No. */
584 dep->de_ramsize = /* RAM size is meaningless */
585 dep->de_offset_page = 0;
586 dep->de_linmem = 0L; /* Access is via I/O port */
588 /* Device specific functions */
589 dep->de_initf = el3_open;
590 dep->de_stopf = el3_close;
592 return(el3_checksum(id_port) == 0); /* Etherlink board found/not found */
595 #endif /* ENABLE_3C509 */
597 /** 3c509.c **/