source: trunk/minix/drivers/dpeth/3c509.c@ 10

Last change on this file since 10 was 9, checked in by Mattia Monga, 14 years ago

Minix 3.1.2a

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