2 ** File: 3c509.c Jun. 01, 2000
4 ** Author: Giovanni Falzoni <gfalzoni@inwind.it>
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
15 #include <minix/com.h>
17 #include <net/gen/ether.h>
18 #include <net/gen/eth_io.h>
22 #if (ENABLE_3C509 == 1)
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
);
63 ** Name: void el3_getstats(dpeth_t *dep)
64 ** Function: Reads statistics counters from board.
66 static void el3_getstats(dpeth_t
* dep
)
70 el3_update_stats(dep
);
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
)
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
);
106 ** Name: void el3_reset(dpeth_t *dep)
107 ** Function: Reset function specific for Etherlink hardware.
109 static void el3_reset(dpeth_t
* dep
)
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
)
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
);
135 panic(__FILE__
, "el3_write_fifo: sys_safe_outsb failed", r
);
137 if (++ix
>= IOVEC_NR
) { /* Next buffer of IO vector */
141 /* Till packet done */
142 } while ((pktsize
-= bytes
) > 0);
143 while ((padding
++ % sizeof(long)) != 0) outb(dep
->de_data_port
, 0x00);
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
)
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
;
162 dep
->de_recvq_head
= rxptr
->next
;
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
);
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
)
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) */
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;
210 /* Good packet. Read it from FIFO */
211 insb(dep
->de_data_port
, SELF
, rxptr
->buffer
, pktsize
);
213 rxptr
->size
= pktsize
;
215 lock(); /* Queue packet to receive queue */
216 if (dep
->de_recvq_head
== NULL
)
217 dep
->de_recvq_head
= rxptr
;
219 dep
->de_recvq_tail
->next
= rxptr
;
220 dep
->de_recvq_tail
= rxptr
;
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
);
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
)
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);
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
);
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;
279 outw_el3(dep
, REG_CmdStatus
, CMD_TxReset
);
281 outw_el3(dep
, REG_CmdStatus
, CMD_TxEnable
);
282 outb_el3(dep
, REG_TxStatus
, 0);
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 */
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
)
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 */
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
);)
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
));
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
)
372 address
|= EL3_READ_EEPROM
;
374 milli_delay(5); /* Allows EEPROM reads */
375 for (result
= 0, bit
= 16; bit
> 0; bit
-= 1) {
376 result
= (result
<< 1) | (inb(port
) & 0x0001);
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
)
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;
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
;
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
);
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
);
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');
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
);
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
))
523 if (address
== EE_ADDR_CFG
||
524 address
== EE_RESOURCE_CFG
||
525 address
== EE_SW_CONFIG_INFO
) {
532 rc
= ((unsigned) hi
<< 8) + lo
;
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
)
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) {
552 pattern
= (pattern
& 0x0100) ? pattern
^ 0xCF : pattern
;
558 ** Name: int el3_probe(dpeth_t *dep)
559 ** Function: Checks for presence of the board.
561 PUBLIC
int el3_probe(dpeth_t
* dep
)
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) {
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
);
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 */