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