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