1 /* 2 * 3c90x.c -- This file implements the 3c90x driver for etherboot. Written 3 * by Greg Beeley, Greg.Beeley@LightSys.org. Modified by Steve Smith, 4 * Steve.Smith@Juno.Com. Alignment bug fix Neil Newell (nn@icenoir.net). 5 * 6 * This program Copyright (C) 1999 LightSys Technology Services, Inc. 7 * Portions Copyright (C) 1999 Steve Smith 8 * 9 * This program may be re-distributed in source or binary form, modified, 10 * sold, or copied for any purpose, provided that the above copyright message 11 * and this text are included with all source copies or derivative works, and 12 * provided that the above copyright message and this text are included in the 13 * documentation of any binary-only distributions. This program is distributed 14 * WITHOUT ANY WARRANTY, without even the warranty of FITNESS FOR A PARTICULAR 15 * PURPOSE or MERCHANTABILITY. Please read the associated documentation 16 * "3c90x.txt" before compiling and using this driver. 17 * 18 * -------- 19 * 20 * Program written with the assistance of the 3com documentation for 21 * the 3c905B-TX card, as well as with some assistance from the 3c59x 22 * driver Donald Becker wrote for the Linux kernel, and with some assistance 23 * from the remainder of the Etherboot distribution. 24 * 25 * REVISION HISTORY: 26 * 27 * v0.10 1-26-1998 GRB Initial implementation. 28 * v0.90 1-27-1998 GRB System works. 29 * v1.00pre1 2-11-1998 GRB Got prom boot issue fixed. 30 * v2.0 9-24-1999 SCS Modified for 3c905 (from 3c905b code) 31 * Re-wrote poll and transmit for 32 * better error recovery and heavy 33 * network traffic operation 34 * v2.01 5-26-2003 NN Fixed driver alignment issue which 35 * caused system lockups if driver structures 36 * not 8-byte aligned. 37 * 38 */ 39 40 #include "etherboot.h" 41 #include "nic.h" 42 #include "pci.h" 43 #include "timer.h" 44 45 #define XCVR_MAGIC (0x5A00) 46 /** any single transmission fails after 16 collisions or other errors 47 ** this is the number of times to retry the transmission -- this should 48 ** be plenty 49 **/ 50 #define XMIT_RETRIES 250 51 52 /*** Register definitions for the 3c905 ***/ 53 enum Registers 54 { 55 regPowerMgmtCtrl_w = 0x7c, /** 905B Revision Only **/ 56 regUpMaxBurst_w = 0x7a, /** 905B Revision Only **/ 57 regDnMaxBurst_w = 0x78, /** 905B Revision Only **/ 58 regDebugControl_w = 0x74, /** 905B Revision Only **/ 59 regDebugData_l = 0x70, /** 905B Revision Only **/ 60 regRealTimeCnt_l = 0x40, /** Universal **/ 61 regUpBurstThresh_b = 0x3e, /** 905B Revision Only **/ 62 regUpPoll_b = 0x3d, /** 905B Revision Only **/ 63 regUpPriorityThresh_b = 0x3c, /** 905B Revision Only **/ 64 regUpListPtr_l = 0x38, /** Universal **/ 65 regCountdown_w = 0x36, /** Universal **/ 66 regFreeTimer_w = 0x34, /** Universal **/ 67 regUpPktStatus_l = 0x30, /** Universal with Exception, pg 130 **/ 68 regTxFreeThresh_b = 0x2f, /** 90X Revision Only **/ 69 regDnPoll_b = 0x2d, /** 905B Revision Only **/ 70 regDnPriorityThresh_b = 0x2c, /** 905B Revision Only **/ 71 regDnBurstThresh_b = 0x2a, /** 905B Revision Only **/ 72 regDnListPtr_l = 0x24, /** Universal with Exception, pg 107 **/ 73 regDmaCtrl_l = 0x20, /** Universal with Exception, pg 106 **/ 74 /** **/ 75 regIntStatusAuto_w = 0x1e, /** 905B Revision Only **/ 76 regTxStatus_b = 0x1b, /** Universal with Exception, pg 113 **/ 77 regTimer_b = 0x1a, /** Universal **/ 78 regTxPktId_b = 0x18, /** 905B Revision Only **/ 79 regCommandIntStatus_w = 0x0e, /** Universal (Command Variations) **/ 80 }; 81 82 /** following are windowed registers **/ 83 enum Registers7 84 { 85 regPowerMgmtEvent_7_w = 0x0c, /** 905B Revision Only **/ 86 regVlanEtherType_7_w = 0x04, /** 905B Revision Only **/ 87 regVlanMask_7_w = 0x00, /** 905B Revision Only **/ 88 }; 89 90 enum Registers6 91 { 92 regBytesXmittedOk_6_w = 0x0c, /** Universal **/ 93 regBytesRcvdOk_6_w = 0x0a, /** Universal **/ 94 regUpperFramesOk_6_b = 0x09, /** Universal **/ 95 regFramesDeferred_6_b = 0x08, /** Universal **/ 96 regFramesRecdOk_6_b = 0x07, /** Universal with Exceptions, pg 142 **/ 97 regFramesXmittedOk_6_b = 0x06, /** Universal **/ 98 regRxOverruns_6_b = 0x05, /** Universal **/ 99 regLateCollisions_6_b = 0x04, /** Universal **/ 100 regSingleCollisions_6_b = 0x03, /** Universal **/ 101 regMultipleCollisions_6_b = 0x02, /** Universal **/ 102 regSqeErrors_6_b = 0x01, /** Universal **/ 103 regCarrierLost_6_b = 0x00, /** Universal **/ 104 }; 105 106 enum Registers5 107 { 108 regIndicationEnable_5_w = 0x0c, /** Universal **/ 109 regInterruptEnable_5_w = 0x0a, /** Universal **/ 110 regTxReclaimThresh_5_b = 0x09, /** 905B Revision Only **/ 111 regRxFilter_5_b = 0x08, /** Universal **/ 112 regRxEarlyThresh_5_w = 0x06, /** Universal **/ 113 regTxStartThresh_5_w = 0x00, /** Universal **/ 114 }; 115 116 enum Registers4 117 { 118 regUpperBytesOk_4_b = 0x0d, /** Universal **/ 119 regBadSSD_4_b = 0x0c, /** Universal **/ 120 regMediaStatus_4_w = 0x0a, /** Universal with Exceptions, pg 201 **/ 121 regPhysicalMgmt_4_w = 0x08, /** Universal **/ 122 regNetworkDiagnostic_4_w = 0x06, /** Universal with Exceptions, pg 203 **/ 123 regFifoDiagnostic_4_w = 0x04, /** Universal with Exceptions, pg 196 **/ 124 regVcoDiagnostic_4_w = 0x02, /** Undocumented? **/ 125 }; 126 127 enum Registers3 128 { 129 regTxFree_3_w = 0x0c, /** Universal **/ 130 regRxFree_3_w = 0x0a, /** Universal with Exceptions, pg 125 **/ 131 regResetMediaOptions_3_w = 0x08, /** Media Options on B Revision, **/ 132 /** Reset Options on Non-B Revision **/ 133 regMacControl_3_w = 0x06, /** Universal with Exceptions, pg 199 **/ 134 regMaxPktSize_3_w = 0x04, /** 905B Revision Only **/ 135 regInternalConfig_3_l = 0x00, /** Universal, different bit **/ 136 /** definitions, pg 59 **/ 137 }; 138 139 enum Registers2 140 { 141 regResetOptions_2_w = 0x0c, /** 905B Revision Only **/ 142 regStationMask_2_3w = 0x06, /** Universal with Exceptions, pg 127 **/ 143 regStationAddress_2_3w = 0x00, /** Universal with Exceptions, pg 127 **/ 144 }; 145 146 enum Registers1 147 { 148 regRxStatus_1_w = 0x0a, /** 90X Revision Only, Pg 126 **/ 149 }; 150 151 enum Registers0 152 { 153 regEepromData_0_w = 0x0c, /** Universal **/ 154 regEepromCommand_0_w = 0x0a, /** Universal **/ 155 regBiosRomData_0_b = 0x08, /** 905B Revision Only **/ 156 regBiosRomAddr_0_l = 0x04, /** 905B Revision Only **/ 157 }; 158 159 160 /*** The names for the eight register windows ***/ 161 enum Windows 162 { 163 winPowerVlan7 = 0x07, 164 winStatistics6 = 0x06, 165 winTxRxControl5 = 0x05, 166 winDiagnostics4 = 0x04, 167 winTxRxOptions3 = 0x03, 168 winAddressing2 = 0x02, 169 winUnused1 = 0x01, 170 winEepromBios0 = 0x00, 171 }; 172 173 174 /*** Command definitions for the 3c90X ***/ 175 enum Commands 176 { 177 cmdGlobalReset = 0x00, /** Universal with Exceptions, pg 151 **/ 178 cmdSelectRegisterWindow = 0x01, /** Universal **/ 179 cmdEnableDcConverter = 0x02, /** **/ 180 cmdRxDisable = 0x03, /** **/ 181 cmdRxEnable = 0x04, /** Universal **/ 182 cmdRxReset = 0x05, /** Universal **/ 183 cmdStallCtl = 0x06, /** Universal **/ 184 cmdTxEnable = 0x09, /** Universal **/ 185 cmdTxDisable = 0x0A, /** **/ 186 cmdTxReset = 0x0B, /** Universal **/ 187 cmdRequestInterrupt = 0x0C, /** **/ 188 cmdAcknowledgeInterrupt = 0x0D, /** Universal **/ 189 cmdSetInterruptEnable = 0x0E, /** Universal **/ 190 cmdSetIndicationEnable = 0x0F, /** Universal **/ 191 cmdSetRxFilter = 0x10, /** Universal **/ 192 cmdSetRxEarlyThresh = 0x11, /** **/ 193 cmdSetTxStartThresh = 0x13, /** **/ 194 cmdStatisticsEnable = 0x15, /** **/ 195 cmdStatisticsDisable = 0x16, /** **/ 196 cmdDisableDcConverter = 0x17, /** **/ 197 cmdSetTxReclaimThresh = 0x18, /** **/ 198 cmdSetHashFilterBit = 0x19, /** **/ 199 }; 200 201 202 /*** Values for int status register bitmask **/ 203 #define INT_INTERRUPTLATCH (1<<0) 204 #define INT_HOSTERROR (1<<1) 205 #define INT_TXCOMPLETE (1<<2) 206 #define INT_RXCOMPLETE (1<<4) 207 #define INT_RXEARLY (1<<5) 208 #define INT_INTREQUESTED (1<<6) 209 #define INT_UPDATESTATS (1<<7) 210 #define INT_LINKEVENT (1<<8) 211 #define INT_DNCOMPLETE (1<<9) 212 #define INT_UPCOMPLETE (1<<10) 213 #define INT_CMDINPROGRESS (1<<12) 214 #define INT_WINDOWNUMBER (7<<13) 215 216 217 /*** TX descriptor ***/ 218 typedef struct 219 { 220 unsigned int DnNextPtr; 221 unsigned int FrameStartHeader; 222 unsigned int HdrAddr; 223 unsigned int HdrLength; 224 unsigned int DataAddr; 225 unsigned int DataLength; 226 } 227 TXD __attribute__ ((aligned(8))); /* 64-bit aligned for bus mastering */ 228 229 /*** RX descriptor ***/ 230 typedef struct 231 { 232 unsigned int UpNextPtr; 233 unsigned int UpPktStatus; 234 unsigned int DataAddr; 235 unsigned int DataLength; 236 } 237 RXD __attribute__ ((aligned(8))); /* 64-bit aligned for bus mastering */ 238 239 /*** Global variables ***/ 240 static struct 241 { 242 unsigned char isBrev; 243 unsigned char CurrentWindow; 244 unsigned int IOAddr; 245 unsigned char HWAddr[ETH_ALEN]; 246 TXD TransmitDPD; 247 RXD ReceiveUPD; 248 } 249 INF_3C90X; 250 251 252 /*** a3c90x_internal_IssueCommand: sends a command to the 3c90x card 253 ***/ 254 static int 255 a3c90x_internal_IssueCommand(int ioaddr, int cmd, int param) 256 { 257 unsigned int val; 258 259 /** Build the cmd. **/ 260 val = cmd; 261 val <<= 11; 262 val |= param; 263 264 /** Send the cmd to the cmd register **/ 265 outw(val, ioaddr + regCommandIntStatus_w); 266 267 /** Wait for the cmd to complete, if necessary **/ 268 while (inw(ioaddr + regCommandIntStatus_w) & INT_CMDINPROGRESS); 269 270 return 0; 271 } 272 273 274 /*** a3c90x_internal_SetWindow: selects a register window set. 275 ***/ 276 static int 277 a3c90x_internal_SetWindow(int ioaddr, int window) 278 { 279 280 /** Window already as set? **/ 281 if (INF_3C90X.CurrentWindow == window) return 0; 282 283 /** Issue the window command. **/ 284 a3c90x_internal_IssueCommand(ioaddr, cmdSelectRegisterWindow, window); 285 INF_3C90X.CurrentWindow = window; 286 287 return 0; 288 } 289 290 291 /*** a3c90x_internal_ReadEeprom - read data from the serial eeprom. 292 ***/ 293 static unsigned short 294 a3c90x_internal_ReadEeprom(int ioaddr, int address) 295 { 296 unsigned short val; 297 298 /** Select correct window **/ 299 a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winEepromBios0); 300 301 /** Make sure the eeprom isn't busy **/ 302 while((1<<15) & inw(ioaddr + regEepromCommand_0_w)); 303 304 /** Read the value. **/ 305 outw(address + ((0x02)<<6), ioaddr + regEepromCommand_0_w); 306 while((1<<15) & inw(ioaddr + regEepromCommand_0_w)); 307 val = inw(ioaddr + regEepromData_0_w); 308 309 return val; 310 } 311 312 313 #if 0 314 /*** a3c90x_internal_WriteEepromWord - write a physical word of 315 *** data to the onboard serial eeprom (not the BIOS prom, but the 316 *** nvram in the card that stores, among other things, the MAC 317 *** address). 318 ***/ 319 static int 320 a3c90x_internal_WriteEepromWord(int ioaddr, int address, unsigned short value) 321 { 322 /** Select register window **/ 323 a3c90x_internal_SetWindow(ioaddr, winEepromBios0); 324 325 /** Verify Eeprom not busy **/ 326 while((1<<15) & inw(ioaddr + regEepromCommand_0_w)); 327 328 /** Issue WriteEnable, and wait for completion. **/ 329 outw(0x30, ioaddr + regEepromCommand_0_w); 330 while((1<<15) & inw(ioaddr + regEepromCommand_0_w)); 331 332 /** Issue EraseRegister, and wait for completion. **/ 333 outw(address + ((0x03)<<6), ioaddr + regEepromCommand_0_w); 334 while((1<<15) & inw(ioaddr + regEepromCommand_0_w)); 335 336 /** Send the new data to the eeprom, and wait for completion. **/ 337 outw(value, ioaddr + regEepromData_0_w); 338 outw(0x30, ioaddr + regEepromCommand_0_w); 339 while((1<<15) & inw(ioaddr + regEepromCommand_0_w)); 340 341 /** Burn the new data into the eeprom, and wait for completion. **/ 342 outw(address + ((0x01)<<6), ioaddr + regEepromCommand_0_w); 343 while((1<<15) & inw(ioaddr + regEepromCommand_0_w)); 344 345 return 0; 346 } 347 #endif 348 349 #if 0 350 /*** a3c90x_internal_WriteEeprom - write data to the serial eeprom, 351 *** and re-compute the eeprom checksum. 352 ***/ 353 static int 354 a3c90x_internal_WriteEeprom(int ioaddr, int address, unsigned short value) 355 { 356 int cksum = 0,v; 357 int i; 358 int maxAddress, cksumAddress; 359 360 if (INF_3C90X.isBrev) 361 { 362 maxAddress=0x1f; 363 cksumAddress=0x20; 364 } 365 else 366 { 367 maxAddress=0x16; 368 cksumAddress=0x17; 369 } 370 371 /** Write the value. **/ 372 if (a3c90x_internal_WriteEepromWord(ioaddr, address, value) == -1) 373 return -1; 374 375 /** Recompute the checksum. **/ 376 for(i=0;i<=maxAddress;i++) 377 { 378 v = a3c90x_internal_ReadEeprom(ioaddr, i); 379 cksum ^= (v & 0xFF); 380 cksum ^= ((v>>8) & 0xFF); 381 } 382 /** Write the checksum to the location in the eeprom **/ 383 if (a3c90x_internal_WriteEepromWord(ioaddr, cksumAddress, cksum) == -1) 384 return -1; 385 386 return 0; 387 } 388 #endif 389 390 /*** a3c90x_reset: exported function that resets the card to its default 391 *** state. This is so the Linux driver can re-set the card up the way 392 *** it wants to. If CFG_3C90X_PRESERVE_XCVR is defined, then the reset will 393 *** not alter the selected transceiver that we used to download the boot 394 *** image. 395 ***/ 396 static void a3c90x_reset(void) 397 { 398 #ifdef CFG_3C90X_PRESERVE_XCVR 399 int cfg; 400 /** Read the current InternalConfig value. **/ 401 a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winTxRxOptions3); 402 cfg = inl(INF_3C90X.IOAddr + regInternalConfig_3_l); 403 #endif 404 405 /** Send the reset command to the card **/ 406 printf("Issuing RESET:\n"); 407 a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdGlobalReset, 0); 408 409 /** wait for reset command to complete **/ 410 while (inw(INF_3C90X.IOAddr + regCommandIntStatus_w) & INT_CMDINPROGRESS); 411 412 /** global reset command resets station mask, non-B revision cards 413 ** require explicit reset of values 414 **/ 415 a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winAddressing2); 416 outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+0); 417 outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+2); 418 outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+4); 419 420 #ifdef CFG_3C90X_PRESERVE_XCVR 421 /** Re-set the original InternalConfig value from before reset **/ 422 a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winTxRxOptions3); 423 outl(cfg, INF_3C90X.IOAddr + regInternalConfig_3_l); 424 425 /** enable DC converter for 10-Base-T **/ 426 if ((cfg&0x0300) == 0x0300) 427 { 428 a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdEnableDcConverter, 0); 429 } 430 #endif 431 432 /** Issue transmit reset, wait for command completion **/ 433 a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxReset, 0); 434 while (inw(INF_3C90X.IOAddr + regCommandIntStatus_w) & INT_CMDINPROGRESS) 435 ; 436 if (! INF_3C90X.isBrev) 437 outb(0x01, INF_3C90X.IOAddr + regTxFreeThresh_b); 438 a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxEnable, 0); 439 440 /** 441 ** reset of the receiver on B-revision cards re-negotiates the link 442 ** takes several seconds (a computer eternity) 443 **/ 444 if (INF_3C90X.isBrev) 445 a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxReset, 0x04); 446 else 447 a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxReset, 0x00); 448 while (inw(INF_3C90X.IOAddr + regCommandIntStatus_w) & INT_CMDINPROGRESS); 449 ; 450 a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxEnable, 0); 451 452 a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, 453 cmdSetInterruptEnable, 0); 454 /** enable rxComplete and txComplete **/ 455 a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, 456 cmdSetIndicationEnable, 0x0014); 457 /** acknowledge any pending status flags **/ 458 a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, 459 cmdAcknowledgeInterrupt, 0x661); 460 461 return; 462 } 463 464 465 466 /*** a3c90x_transmit: exported function that transmits a packet. Does not 467 *** return any particular status. Parameters are: 468 *** d[6] - destination address, ethernet; 469 *** t - protocol type (ARP, IP, etc); 470 *** s - size of the non-header part of the packet that needs transmitted; 471 *** p - the pointer to the packet data itself. 472 ***/ 473 static void 474 a3c90x_transmit(struct nic *nic __unused, const char *d, unsigned int t, 475 unsigned int s, const char *p) 476 { 477 478 struct eth_hdr 479 { 480 unsigned char dst_addr[ETH_ALEN]; 481 unsigned char src_addr[ETH_ALEN]; 482 unsigned short type; 483 } hdr; 484 485 unsigned char status; 486 unsigned i, retries; 487 488 for (retries=0; retries < XMIT_RETRIES ; retries++) 489 { 490 /** Stall the download engine **/ 491 a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdStallCtl, 2); 492 493 /** Make sure the card is not waiting on us **/ 494 inw(INF_3C90X.IOAddr + regCommandIntStatus_w); 495 inw(INF_3C90X.IOAddr + regCommandIntStatus_w); 496 497 while (inw(INF_3C90X.IOAddr+regCommandIntStatus_w) & 498 INT_CMDINPROGRESS) 499 ; 500 501 /** Set the ethernet packet type **/ 502 hdr.type = htons(t); 503 504 /** Copy the destination address **/ 505 memcpy(hdr.dst_addr, d, ETH_ALEN); 506 507 /** Copy our MAC address **/ 508 memcpy(hdr.src_addr, INF_3C90X.HWAddr, ETH_ALEN); 509 510 /** Setup the DPD (download descriptor) **/ 511 INF_3C90X.TransmitDPD.DnNextPtr = 0; 512 /** set notification for transmission completion (bit 15) **/ 513 INF_3C90X.TransmitDPD.FrameStartHeader = (s + sizeof(hdr)) | 0x8000; 514 INF_3C90X.TransmitDPD.HdrAddr = virt_to_bus(&hdr); 515 INF_3C90X.TransmitDPD.HdrLength = sizeof(hdr); 516 INF_3C90X.TransmitDPD.DataAddr = virt_to_bus(p); 517 INF_3C90X.TransmitDPD.DataLength = s + (1<<31); 518 519 /** Send the packet **/ 520 outl(virt_to_bus(&(INF_3C90X.TransmitDPD)), 521 INF_3C90X.IOAddr + regDnListPtr_l); 522 523 /** End Stall and Wait for upload to complete. **/ 524 a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdStallCtl, 3); 525 while(inl(INF_3C90X.IOAddr + regDnListPtr_l) != 0) 526 ; 527 528 /** Wait for NIC Transmit to Complete **/ 529 load_timer2(10*TICKS_PER_MS); /* Give it 10 ms */ 530 while (!(inw(INF_3C90X.IOAddr + regCommandIntStatus_w)&0x0004) && 531 timer2_running()) 532 ; 533 534 if (!(inw(INF_3C90X.IOAddr + regCommandIntStatus_w)&0x0004)) 535 { 536 printf("3C90X: Tx Timeout\n"); 537 continue; 538 } 539 540 status = inb(INF_3C90X.IOAddr + regTxStatus_b); 541 542 /** acknowledge transmit interrupt by writing status **/ 543 outb(0x00, INF_3C90X.IOAddr + regTxStatus_b); 544 545 /** successful completion (sans "interrupt Requested" bit) **/ 546 if ((status & 0xbf) == 0x80) 547 return; 548 549 printf("3C90X: Status (%hhX)\n", status); 550 /** check error codes **/ 551 if (status & 0x02) 552 { 553 printf("3C90X: Tx Reclaim Error (%hhX)\n", status); 554 a3c90x_reset(); 555 } 556 else if (status & 0x04) 557 { 558 printf("3C90X: Tx Status Overflow (%hhX)\n", status); 559 for (i=0; i<32; i++) 560 outb(0x00, INF_3C90X.IOAddr + regTxStatus_b); 561 /** must re-enable after max collisions before re-issuing tx **/ 562 a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxEnable, 0); 563 } 564 else if (status & 0x08) 565 { 566 printf("3C90X: Tx Max Collisions (%hhX)\n", status); 567 /** must re-enable after max collisions before re-issuing tx **/ 568 a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxEnable, 0); 569 } 570 else if (status & 0x10) 571 { 572 printf("3C90X: Tx Underrun (%hhX)\n", status); 573 a3c90x_reset(); 574 } 575 else if (status & 0x20) 576 { 577 printf("3C90X: Tx Jabber (%hhX)\n", status); 578 a3c90x_reset(); 579 } 580 else if ((status & 0x80) != 0x80) 581 { 582 printf("3C90X: Internal Error - Incomplete Transmission (%hhX)\n", 583 status); 584 a3c90x_reset(); 585 } 586 } 587 588 /** failed after RETRY attempts **/ 589 printf("Failed to send after %d retries\n", retries); 590 return; 591 592 } 593 594 595 596 /*** a3c90x_poll: exported routine that waits for a certain length of time 597 *** for a packet, and if it sees none, returns 0. This routine should 598 *** copy the packet to nic->packet if it gets a packet and set the size 599 *** in nic->packetlen. Return 1 if a packet was found. 600 ***/ 601 static int 602 a3c90x_poll(struct nic *nic, int retrieve) 603 { 604 int i, errcode; 605 606 if (!(inw(INF_3C90X.IOAddr + regCommandIntStatus_w)&0x0010)) 607 { 608 return 0; 609 } 610 611 if ( ! retrieve ) return 1; 612 613 /** we don't need to acknowledge rxComplete -- the upload engine 614 ** does it for us. 615 **/ 616 617 /** Build the up-load descriptor **/ 618 INF_3C90X.ReceiveUPD.UpNextPtr = 0; 619 INF_3C90X.ReceiveUPD.UpPktStatus = 0; 620 INF_3C90X.ReceiveUPD.DataAddr = virt_to_bus(nic->packet); 621 INF_3C90X.ReceiveUPD.DataLength = 1536 + (1<<31); 622 623 /** Submit the upload descriptor to the NIC **/ 624 outl(virt_to_bus(&(INF_3C90X.ReceiveUPD)), 625 INF_3C90X.IOAddr + regUpListPtr_l); 626 627 /** Wait for upload completion (upComplete(15) or upError (14)) **/ 628 for(i=0;i<40000;i++); 629 while((INF_3C90X.ReceiveUPD.UpPktStatus & ((1<<14) | (1<<15))) == 0) 630 for(i=0;i<40000;i++); 631 632 /** Check for Error (else we have good packet) **/ 633 if (INF_3C90X.ReceiveUPD.UpPktStatus & (1<<14)) 634 { 635 errcode = INF_3C90X.ReceiveUPD.UpPktStatus; 636 if (errcode & (1<<16)) 637 printf("3C90X: Rx Overrun (%hX)\n",errcode>>16); 638 else if (errcode & (1<<17)) 639 printf("3C90X: Runt Frame (%hX)\n",errcode>>16); 640 else if (errcode & (1<<18)) 641 printf("3C90X: Alignment Error (%hX)\n",errcode>>16); 642 else if (errcode & (1<<19)) 643 printf("3C90X: CRC Error (%hX)\n",errcode>>16); 644 else if (errcode & (1<<20)) 645 printf("3C90X: Oversized Frame (%hX)\n",errcode>>16); 646 else 647 printf("3C90X: Packet error (%hX)\n",errcode>>16); 648 return 0; 649 } 650 651 /** Ok, got packet. Set length in nic->packetlen. **/ 652 nic->packetlen = (INF_3C90X.ReceiveUPD.UpPktStatus & 0x1FFF); 653 654 return 1; 655 } 656 657 658 659 /*** a3c90x_disable: exported routine to disable the card. What's this for? 660 *** the eepro100.c driver didn't have one, so I just left this one empty too. 661 *** Ideas anyone? 662 *** Must turn off receiver at least so stray packets will not corrupt memory 663 *** [Ken] 664 ***/ 665 static void 666 a3c90x_disable(struct dev *dev __unused) 667 { 668 /* reset and disable merge */ 669 a3c90x_reset(); 670 /* Disable the receiver and transmitter. */ 671 outw(cmdRxDisable, INF_3C90X.IOAddr + regCommandIntStatus_w); 672 outw(cmdTxDisable, INF_3C90X.IOAddr + regCommandIntStatus_w); 673 } 674 675 static void a3c90x_irq(struct nic *nic __unused, irq_action_t action __unused) 676 { 677 switch ( action ) { 678 case DISABLE : 679 break; 680 case ENABLE : 681 break; 682 case FORCE : 683 break; 684 } 685 } 686 687 /*** a3c90x_probe: exported routine to probe for the 3c905 card and perform 688 *** initialization. If this routine is called, the pci functions did find the 689 *** card. We just have to init it here. 690 ***/ 691 static int a3c90x_probe(struct dev *dev, struct pci_device *pci) 692 { 693 struct nic *nic = (struct nic *)dev; 694 int i, c; 695 unsigned short eeprom[0x21]; 696 unsigned int cfg; 697 unsigned int mopt; 698 unsigned int mstat; 699 unsigned short linktype; 700 #define HWADDR_OFFSET 10 701 702 if (pci->ioaddr == 0) 703 return 0; 704 705 adjust_pci_device(pci); 706 707 nic->ioaddr = pci->ioaddr & ~3; 708 nic->irqno = 0; 709 710 INF_3C90X.IOAddr = pci->ioaddr & ~3; 711 INF_3C90X.CurrentWindow = 255; 712 switch (a3c90x_internal_ReadEeprom(INF_3C90X.IOAddr, 0x03)) 713 { 714 case 0x9000: /** 10 Base TPO **/ 715 case 0x9001: /** 10/100 T4 **/ 716 case 0x9050: /** 10/100 TPO **/ 717 case 0x9051: /** 10 Base Combo **/ 718 INF_3C90X.isBrev = 0; 719 break; 720 721 case 0x9004: /** 10 Base TPO **/ 722 case 0x9005: /** 10 Base Combo **/ 723 case 0x9006: /** 10 Base TPO and Base2 **/ 724 case 0x900A: /** 10 Base FL **/ 725 case 0x9055: /** 10/100 TPO **/ 726 case 0x9056: /** 10/100 T4 **/ 727 case 0x905A: /** 10 Base FX **/ 728 default: 729 INF_3C90X.isBrev = 1; 730 break; 731 } 732 733 /** Load the EEPROM contents **/ 734 if (INF_3C90X.isBrev) 735 { 736 for(i=0;i<=0x20;i++) 737 { 738 eeprom[i] = a3c90x_internal_ReadEeprom(INF_3C90X.IOAddr, i); 739 } 740 741 #ifdef CFG_3C90X_BOOTROM_FIX 742 /** Set xcvrSelect in InternalConfig in eeprom. **/ 743 /* only necessary for 3c905b revision cards with boot PROM bug!!! */ 744 a3c90x_internal_WriteEeprom(INF_3C90X.IOAddr, 0x13, 0x0160); 745 #endif 746 747 #ifdef CFG_3C90X_XCVR 748 if (CFG_3C90X_XCVR == 255) 749 { 750 /** Clear the LanWorks register **/ 751 a3c90x_internal_WriteEeprom(INF_3C90X.IOAddr, 0x16, 0); 752 } 753 else 754 { 755 /** Set the selected permanent-xcvrSelect in the 756 ** LanWorks register 757 **/ 758 a3c90x_internal_WriteEeprom(INF_3C90X.IOAddr, 0x16, 759 XCVR_MAGIC + ((CFG_3C90X_XCVR) & 0x000F)); 760 } 761 #endif 762 } 763 else 764 { 765 for(i=0;i<=0x17;i++) 766 { 767 eeprom[i] = a3c90x_internal_ReadEeprom(INF_3C90X.IOAddr, i); 768 } 769 } 770 771 /** Print identification message **/ 772 printf("\n\n3C90X Driver 2.00 " 773 "Copyright 1999 LightSys Technology Services, Inc.\n" 774 "Portions Copyright 1999 Steve Smith\n"); 775 printf("Provided with ABSOLUTELY NO WARRANTY.\n"); 776 #ifdef CFG_3C90X_BOOTROM_FIX 777 if (INF_3C90X.isBrev) 778 { 779 printf("NOTE: 3c905b bootrom fix enabled; has side " 780 "effects. See 3c90x.txt for info.\n"); 781 } 782 #endif 783 printf("-------------------------------------------------------" 784 "------------------------\n"); 785 786 /** Retrieve the Hardware address and print it on the screen. **/ 787 INF_3C90X.HWAddr[0] = eeprom[HWADDR_OFFSET + 0]>>8; 788 INF_3C90X.HWAddr[1] = eeprom[HWADDR_OFFSET + 0]&0xFF; 789 INF_3C90X.HWAddr[2] = eeprom[HWADDR_OFFSET + 1]>>8; 790 INF_3C90X.HWAddr[3] = eeprom[HWADDR_OFFSET + 1]&0xFF; 791 INF_3C90X.HWAddr[4] = eeprom[HWADDR_OFFSET + 2]>>8; 792 INF_3C90X.HWAddr[5] = eeprom[HWADDR_OFFSET + 2]&0xFF; 793 printf("MAC Address = %!\n", INF_3C90X.HWAddr); 794 795 /* Test if the link is good, if not continue */ 796 a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winDiagnostics4); 797 mstat = inw(INF_3C90X.IOAddr + regMediaStatus_4_w); 798 if((mstat & (1<<11)) == 0) { 799 printf("Valid link not established\n"); 800 return 0; 801 } 802 803 /** Program the MAC address into the station address registers **/ 804 a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winAddressing2); 805 outw(htons(eeprom[HWADDR_OFFSET + 0]), INF_3C90X.IOAddr + regStationAddress_2_3w); 806 outw(htons(eeprom[HWADDR_OFFSET + 1]), INF_3C90X.IOAddr + regStationAddress_2_3w+2); 807 outw(htons(eeprom[HWADDR_OFFSET + 2]), INF_3C90X.IOAddr + regStationAddress_2_3w+4); 808 outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+0); 809 outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+2); 810 outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+4); 811 812 /** Fill in our entry in the etherboot arp table **/ 813 for(i=0;i<ETH_ALEN;i++) 814 nic->node_addr[i] = (eeprom[HWADDR_OFFSET + i/2] >> (8*((i&1)^1))) & 0xff; 815 816 /** Read the media options register, print a message and set default 817 ** xcvr. 818 ** 819 ** Uses Media Option command on B revision, Reset Option on non-B 820 ** revision cards -- same register address 821 **/ 822 a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winTxRxOptions3); 823 mopt = inw(INF_3C90X.IOAddr + regResetMediaOptions_3_w); 824 825 /** mask out VCO bit that is defined as 10baseFL bit on B-rev cards **/ 826 if (! INF_3C90X.isBrev) 827 { 828 mopt &= 0x7F; 829 } 830 831 printf("Connectors present: "); 832 c = 0; 833 linktype = 0x0008; 834 if (mopt & 0x01) 835 { 836 printf("%s100Base-T4",(c++)?", ":""); 837 linktype = 0x0006; 838 } 839 if (mopt & 0x04) 840 { 841 printf("%s100Base-FX",(c++)?", ":""); 842 linktype = 0x0005; 843 } 844 if (mopt & 0x10) 845 { 846 printf("%s10Base-2",(c++)?", ":""); 847 linktype = 0x0003; 848 } 849 if (mopt & 0x20) 850 { 851 printf("%sAUI",(c++)?", ":""); 852 linktype = 0x0001; 853 } 854 if (mopt & 0x40) 855 { 856 printf("%sMII",(c++)?", ":""); 857 linktype = 0x0006; 858 } 859 if ((mopt & 0xA) == 0xA) 860 { 861 printf("%s10Base-T / 100Base-TX",(c++)?", ":""); 862 linktype = 0x0008; 863 } 864 else if ((mopt & 0xA) == 0x2) 865 { 866 printf("%s100Base-TX",(c++)?", ":""); 867 linktype = 0x0008; 868 } 869 else if ((mopt & 0xA) == 0x8) 870 { 871 printf("%s10Base-T",(c++)?", ":""); 872 linktype = 0x0008; 873 } 874 printf(".\n"); 875 876 /** Determine transceiver type to use, depending on value stored in 877 ** eeprom 0x16 878 **/ 879 if (INF_3C90X.isBrev) 880 { 881 if ((eeprom[0x16] & 0xFF00) == XCVR_MAGIC) 882 { 883 /** User-defined **/ 884 linktype = eeprom[0x16] & 0x000F; 885 } 886 } 887 else 888 { 889 #ifdef CFG_3C90X_XCVR 890 if (CFG_3C90X_XCVR != 255) 891 linktype = CFG_3C90X_XCVR; 892 #endif /* CFG_3C90X_XCVR */ 893 894 /** I don't know what MII MAC only mode is!!! **/ 895 if (linktype == 0x0009) 896 { 897 if (INF_3C90X.isBrev) 898 printf("WARNING: MII External MAC Mode only supported on B-revision " 899 "cards!!!!\nFalling Back to MII Mode\n"); 900 linktype = 0x0006; 901 } 902 } 903 904 /** enable DC converter for 10-Base-T **/ 905 if (linktype == 0x0003) 906 { 907 a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdEnableDcConverter, 0); 908 } 909 910 /** Set the link to the type we just determined. **/ 911 a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winTxRxOptions3); 912 cfg = inl(INF_3C90X.IOAddr + regInternalConfig_3_l); 913 cfg &= ~(0xF<<20); 914 cfg |= (linktype<<20); 915 outl(cfg, INF_3C90X.IOAddr + regInternalConfig_3_l); 916 917 /** Now that we set the xcvr type, reset the Tx and Rx, re-enable. **/ 918 a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxReset, 0x00); 919 while (inw(INF_3C90X.IOAddr + regCommandIntStatus_w) & INT_CMDINPROGRESS) 920 ; 921 922 if (!INF_3C90X.isBrev) 923 outb(0x01, INF_3C90X.IOAddr + regTxFreeThresh_b); 924 925 a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxEnable, 0); 926 927 /** 928 ** reset of the receiver on B-revision cards re-negotiates the link 929 ** takes several seconds (a computer eternity) 930 **/ 931 if (INF_3C90X.isBrev) 932 a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxReset, 0x04); 933 else 934 a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxReset, 0x00); 935 while (inw(INF_3C90X.IOAddr + regCommandIntStatus_w) & INT_CMDINPROGRESS) 936 ; 937 938 /** Set the RX filter = receive only individual pkts & multicast & bcast. **/ 939 a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdSetRxFilter, 0x01 + 0x02 + 0x04); 940 a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxEnable, 0); 941 942 943 /** 944 ** set Indication and Interrupt flags , acknowledge any IRQ's 945 **/ 946 a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdSetInterruptEnable, 0); 947 a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, 948 cmdSetIndicationEnable, 0x0014); 949 a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, 950 cmdAcknowledgeInterrupt, 0x661); 951 952 /** Set our exported functions **/ 953 dev->disable = a3c90x_disable; 954 nic->poll = a3c90x_poll; 955 nic->transmit = a3c90x_transmit; 956 nic->irq = a3c90x_irq; 957 958 return 1; 959 } 960 961 962 static struct pci_id a3c90x_nics[] = { 963 /* Original 90x revisions: */ 964 PCI_ROM(0x10b7, 0x9000, "3c905-tpo", "3Com900-TPO"), /* 10 Base TPO */ 965 PCI_ROM(0x10b7, 0x9001, "3c905-t4", "3Com900-Combo"), /* 10/100 T4 */ 966 PCI_ROM(0x10b7, 0x9050, "3c905-tpo100", "3Com905-TX"), /* 100 Base TX / 10/100 TPO */ 967 PCI_ROM(0x10b7, 0x9051, "3c905-combo", "3Com905-T4"), /* 100 Base T4 / 10 Base Combo */ 968 /* Newer 90xB revisions: */ 969 PCI_ROM(0x10b7, 0x9004, "3c905b-tpo", "3Com900B-TPO"), /* 10 Base TPO */ 970 PCI_ROM(0x10b7, 0x9005, "3c905b-combo", "3Com900B-Combo"), /* 10 Base Combo */ 971 PCI_ROM(0x10b7, 0x9006, "3c905b-tpb2", "3Com900B-2/T"), /* 10 Base TP and Base2 */ 972 PCI_ROM(0x10b7, 0x900a, "3c905b-fl", "3Com900B-FL"), /* 10 Base FL */ 973 PCI_ROM(0x10b7, 0x9055, "3c905b-tpo100", "3Com905B-TX"), /* 10/100 TPO */ 974 PCI_ROM(0x10b7, 0x9056, "3c905b-t4", "3Com905B-T4"), /* 10/100 T4 */ 975 PCI_ROM(0x10b7, 0x9058, "3c905b-9058", "3Com905B-9058"), /* Cyclone 10/100/BNC */ 976 PCI_ROM(0x10b7, 0x905a, "3c905b-fx", "3Com905B-FL"), /* 100 Base FX / 10 Base FX */ 977 /* Newer 90xC revision: */ 978 PCI_ROM(0x10b7, 0x9200, "3c905c-tpo", "3Com905C-TXM"), /* 10/100 TPO (3C905C-TXM) */ 979 PCI_ROM(0x10b7, 0x9210, "3c920b-emb-wnm","3Com20B-EMB WNM"), 980 PCI_ROM(0x10b7, 0x9800, "3c980", "3Com980-Cyclone"), /* Cyclone */ 981 PCI_ROM(0x10b7, 0x9805, "3c9805", "3Com9805"), /* Dual Port Server Cyclone */ 982 PCI_ROM(0x10b7, 0x7646, "3csoho100-tx", "3CSOHO100-TX"), /* Hurricane */ 983 PCI_ROM(0x10b7, 0x4500, "3c450", "3Com450 HomePNA Tornado"), 984 PCI_ROM(0x10b7, 0x1201, "3c982a", "3Com982A"), 985 PCI_ROM(0x10b7, 0x1202, "3c982b", "3Com982B"), 986 }; 987 988 struct pci_driver a3c90x_driver = { 989 .type = NIC_DRIVER, 990 .name = "3C90X", 991 .probe = a3c90x_probe, 992 .ids = a3c90x_nics, 993 .id_count = sizeof(a3c90x_nics)/sizeof(a3c90x_nics[0]), 994 .class = 0, 995 }; 996