Home | History | Annotate | Download | only in netboot
      1 /*
      2  * 3c90x.c -- This file implements the 3c90x driver for etherboot.  Written
      3  * by Greg Beeley, Greg.Beeley (at) LightSys.org.  Modified by Steve Smith,
      4  * Steve.Smith (at) Juno.Com
      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  *
     35  */
     36 
     37 #include "etherboot.h"
     38 #include "nic.h"
     39 #include "pci.h"
     40 #include "cards.h"
     41 #include "timer.h"
     42 
     43 #define	XCVR_MAGIC	(0x5A00)
     44 /** any single transmission fails after 16 collisions or other errors
     45  ** this is the number of times to retry the transmission -- this should
     46  ** be plenty
     47  **/
     48 #define	XMIT_RETRIES	250
     49 
     50 #undef	virt_to_bus
     51 #define	virt_to_bus(x)	((unsigned long)x)
     52 
     53 /*** Register definitions for the 3c905 ***/
     54 enum Registers
     55     {
     56     regPowerMgmtCtrl_w = 0x7c,        /** 905B Revision Only                 **/
     57     regUpMaxBurst_w = 0x7a,           /** 905B Revision Only                 **/
     58     regDnMaxBurst_w = 0x78,           /** 905B Revision Only                 **/
     59     regDebugControl_w = 0x74,         /** 905B Revision Only                 **/
     60     regDebugData_l = 0x70,            /** 905B Revision Only                 **/
     61     regRealTimeCnt_l = 0x40,          /** Universal                          **/
     62     regUpBurstThresh_b = 0x3e,        /** 905B Revision Only                 **/
     63     regUpPoll_b = 0x3d,               /** 905B Revision Only                 **/
     64     regUpPriorityThresh_b = 0x3c,     /** 905B Revision Only                 **/
     65     regUpListPtr_l = 0x38,            /** Universal                          **/
     66     regCountdown_w = 0x36,            /** Universal                          **/
     67     regFreeTimer_w = 0x34,            /** Universal                          **/
     68     regUpPktStatus_l = 0x30,          /** Universal with Exception, pg 130   **/
     69     regTxFreeThresh_b = 0x2f,         /** 90X Revision Only                  **/
     70     regDnPoll_b = 0x2d,               /** 905B Revision Only                 **/
     71     regDnPriorityThresh_b = 0x2c,     /** 905B Revision Only                 **/
     72     regDnBurstThresh_b = 0x2a,        /** 905B Revision Only                 **/
     73     regDnListPtr_l = 0x24,            /** Universal with Exception, pg 107   **/
     74     regDmaCtrl_l = 0x20,              /** Universal with Exception, pg 106   **/
     75                                       /**                                    **/
     76     regIntStatusAuto_w = 0x1e,        /** 905B Revision Only                 **/
     77     regTxStatus_b = 0x1b,             /** Universal with Exception, pg 113   **/
     78     regTimer_b = 0x1a,                /** Universal                          **/
     79     regTxPktId_b = 0x18,              /** 905B Revision Only                 **/
     80     regCommandIntStatus_w = 0x0e,     /** Universal (Command Variations)     **/
     81     };
     82 
     83 /** following are windowed registers **/
     84 enum Registers7
     85     {
     86     regPowerMgmtEvent_7_w = 0x0c,     /** 905B Revision Only                 **/
     87     regVlanEtherType_7_w = 0x04,      /** 905B Revision Only                 **/
     88     regVlanMask_7_w = 0x00,           /** 905B Revision Only                 **/
     89     };
     90 
     91 enum Registers6
     92     {
     93     regBytesXmittedOk_6_w = 0x0c,     /** Universal                          **/
     94     regBytesRcvdOk_6_w = 0x0a,        /** Universal                          **/
     95     regUpperFramesOk_6_b = 0x09,      /** Universal                          **/
     96     regFramesDeferred_6_b = 0x08,     /** Universal                          **/
     97     regFramesRecdOk_6_b = 0x07,       /** Universal with Exceptions, pg 142  **/
     98     regFramesXmittedOk_6_b = 0x06,    /** Universal                          **/
     99     regRxOverruns_6_b = 0x05,         /** Universal                          **/
    100     regLateCollisions_6_b = 0x04,     /** Universal                          **/
    101     regSingleCollisions_6_b = 0x03,   /** Universal                          **/
    102     regMultipleCollisions_6_b = 0x02, /** Universal                          **/
    103     regSqeErrors_6_b = 0x01,          /** Universal                          **/
    104     regCarrierLost_6_b = 0x00,        /** Universal                          **/
    105     };
    106 
    107 enum Registers5
    108     {
    109     regIndicationEnable_5_w = 0x0c,   /** Universal                          **/
    110     regInterruptEnable_5_w = 0x0a,    /** Universal                          **/
    111     regTxReclaimThresh_5_b = 0x09,    /** 905B Revision Only                 **/
    112     regRxFilter_5_b = 0x08,           /** Universal                          **/
    113     regRxEarlyThresh_5_w = 0x06,      /** Universal                          **/
    114     regTxStartThresh_5_w = 0x00,      /** Universal                          **/
    115     };
    116 
    117 enum Registers4
    118     {
    119     regUpperBytesOk_4_b = 0x0d,       /** Universal                          **/
    120     regBadSSD_4_b = 0x0c,             /** Universal                          **/
    121     regMediaStatus_4_w = 0x0a,        /** Universal with Exceptions, pg 201  **/
    122     regPhysicalMgmt_4_w = 0x08,       /** Universal                          **/
    123     regNetworkDiagnostic_4_w = 0x06,  /** Universal with Exceptions, pg 203  **/
    124     regFifoDiagnostic_4_w = 0x04,     /** Universal with Exceptions, pg 196  **/
    125     regVcoDiagnostic_4_w = 0x02,      /** Undocumented?                      **/
    126     };
    127 
    128 enum Registers3
    129     {
    130     regTxFree_3_w = 0x0c,             /** Universal                          **/
    131     regRxFree_3_w = 0x0a,             /** Universal with Exceptions, pg 125  **/
    132     regResetMediaOptions_3_w = 0x08,  /** Media Options on B Revision,       **/
    133                                       /** Reset Options on Non-B Revision    **/
    134     regMacControl_3_w = 0x06,         /** Universal with Exceptions, pg 199  **/
    135     regMaxPktSize_3_w = 0x04,         /** 905B Revision Only                 **/
    136     regInternalConfig_3_l = 0x00,     /** Universal, different bit           **/
    137                                       /** definitions, pg 59                 **/
    138     };
    139 
    140 enum Registers2
    141     {
    142     regResetOptions_2_w = 0x0c,       /** 905B Revision Only                 **/
    143     regStationMask_2_3w = 0x06,       /** Universal with Exceptions, pg 127  **/
    144     regStationAddress_2_3w = 0x00,    /** Universal with Exceptions, pg 127  **/
    145     };
    146 
    147 enum Registers1
    148     {
    149     regRxStatus_1_w = 0x0a,           /** 90X Revision Only, Pg 126          **/
    150     };
    151 
    152 enum Registers0
    153     {
    154     regEepromData_0_w = 0x0c,         /** Universal                          **/
    155     regEepromCommand_0_w = 0x0a,      /** Universal                          **/
    156     regBiosRomData_0_b = 0x08,        /** 905B Revision Only                 **/
    157     regBiosRomAddr_0_l = 0x04,        /** 905B Revision Only                 **/
    158     };
    159 
    160 
    161 /*** The names for the eight register windows ***/
    162 enum Windows
    163     {
    164     winPowerVlan7 = 0x07,
    165     winStatistics6 = 0x06,
    166     winTxRxControl5 = 0x05,
    167     winDiagnostics4 = 0x04,
    168     winTxRxOptions3 = 0x03,
    169     winAddressing2 = 0x02,
    170     winUnused1 = 0x01,
    171     winEepromBios0 = 0x00,
    172     };
    173 
    174 
    175 /*** Command definitions for the 3c90X ***/
    176 enum Commands
    177     {
    178     cmdGlobalReset = 0x00,             /** Universal with Exceptions, pg 151 **/
    179     cmdSelectRegisterWindow = 0x01,    /** Universal                         **/
    180     cmdEnableDcConverter = 0x02,       /**                                   **/
    181     cmdRxDisable = 0x03,               /**                                   **/
    182     cmdRxEnable = 0x04,                /** Universal                         **/
    183     cmdRxReset = 0x05,                 /** Universal                         **/
    184     cmdStallCtl = 0x06,                /** Universal                         **/
    185     cmdTxEnable = 0x09,                /** Universal                         **/
    186     cmdTxDisable = 0x0A,               /**                                   **/
    187     cmdTxReset = 0x0B,                 /** Universal                         **/
    188     cmdRequestInterrupt = 0x0C,        /**                                   **/
    189     cmdAcknowledgeInterrupt = 0x0D,    /** Universal                         **/
    190     cmdSetInterruptEnable = 0x0E,      /** Universal                         **/
    191     cmdSetIndicationEnable = 0x0F,     /** Universal                         **/
    192     cmdSetRxFilter = 0x10,             /** Universal                         **/
    193     cmdSetRxEarlyThresh = 0x11,        /**                                   **/
    194     cmdSetTxStartThresh = 0x13,        /**                                   **/
    195     cmdStatisticsEnable = 0x15,        /**                                   **/
    196     cmdStatisticsDisable = 0x16,       /**                                   **/
    197     cmdDisableDcConverter = 0x17,      /**                                   **/
    198     cmdSetTxReclaimThresh = 0x18,      /**                                   **/
    199     cmdSetHashFilterBit = 0x19,        /**                                   **/
    200     };
    201 
    202 
    203 /*** Values for int status register bitmask **/
    204 #define	INT_INTERRUPTLATCH	(1<<0)
    205 #define INT_HOSTERROR		(1<<1)
    206 #define INT_TXCOMPLETE		(1<<2)
    207 #define INT_RXCOMPLETE		(1<<4)
    208 #define INT_RXEARLY		(1<<5)
    209 #define INT_INTREQUESTED	(1<<6)
    210 #define INT_UPDATESTATS		(1<<7)
    211 #define INT_LINKEVENT		(1<<8)
    212 #define INT_DNCOMPLETE		(1<<9)
    213 #define INT_UPCOMPLETE		(1<<10)
    214 #define INT_CMDINPROGRESS	(1<<12)
    215 #define INT_WINDOWNUMBER	(7<<13)
    216 
    217 
    218 /*** TX descriptor ***/
    219 typedef struct
    220     {
    221     unsigned int	DnNextPtr;
    222     unsigned int	FrameStartHeader;
    223     unsigned int	HdrAddr;
    224     unsigned int	HdrLength;
    225     unsigned int	DataAddr;
    226     unsigned int	DataLength;
    227     }
    228     TXD;
    229 
    230 /*** RX descriptor ***/
    231 typedef struct
    232     {
    233     unsigned int	UpNextPtr;
    234     unsigned int	UpPktStatus;
    235     unsigned int	DataAddr;
    236     unsigned int	DataLength;
    237     }
    238     RXD;
    239 
    240 /*** Global variables ***/
    241 static struct
    242     {
    243     unsigned char	isBrev;
    244     unsigned char	CurrentWindow;
    245     unsigned int	IOAddr;
    246     unsigned char	HWAddr[ETH_ALEN];
    247     TXD			TransmitDPD;
    248     RXD			ReceiveUPD;
    249     }
    250     INF_3C90X;
    251 
    252 
    253 /*** a3c90x_internal_IssueCommand: sends a command to the 3c90x card
    254  ***/
    255 static int
    256 a3c90x_internal_IssueCommand(int ioaddr, int cmd, int param)
    257     {
    258     unsigned int val;
    259 
    260 	/** Build the cmd. **/
    261 	val = cmd;
    262 	val <<= 11;
    263 	val |= param;
    264 
    265 	/** Send the cmd to the cmd register **/
    266 	outw(val, ioaddr + regCommandIntStatus_w);
    267 
    268 	/** Wait for the cmd to complete, if necessary **/
    269 	while (inw(ioaddr + regCommandIntStatus_w) & INT_CMDINPROGRESS);
    270 
    271     return 0;
    272     }
    273 
    274 
    275 /*** a3c90x_internal_SetWindow: selects a register window set.
    276  ***/
    277 static int
    278 a3c90x_internal_SetWindow(int ioaddr, int window)
    279     {
    280 
    281 	/** Window already as set? **/
    282 	if (INF_3C90X.CurrentWindow == window) return 0;
    283 
    284 	/** Issue the window command. **/
    285 	a3c90x_internal_IssueCommand(ioaddr, cmdSelectRegisterWindow, window);
    286 	INF_3C90X.CurrentWindow = window;
    287 
    288     return 0;
    289     }
    290 
    291 
    292 /*** a3c90x_internal_ReadEeprom - read data from the serial eeprom.
    293  ***/
    294 static unsigned short
    295 a3c90x_internal_ReadEeprom(int ioaddr, int address)
    296     {
    297     unsigned short val;
    298 
    299 	/** Select correct window **/
    300         a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winEepromBios0);
    301 
    302 	/** Make sure the eeprom isn't busy **/
    303 	while((1<<15) & inw(ioaddr + regEepromCommand_0_w));
    304 
    305 	/** Read the value. **/
    306 	outw(address + ((0x02)<<6), ioaddr + regEepromCommand_0_w);
    307 	while((1<<15) & inw(ioaddr + regEepromCommand_0_w));
    308 	val = inw(ioaddr + regEepromData_0_w);
    309 
    310     return val;
    311     }
    312 
    313 
    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 
    348 
    349 /*** a3c90x_internal_WriteEeprom - write data to the serial eeprom,
    350  *** and re-compute the eeprom checksum.
    351  ***/
    352 static int
    353 a3c90x_internal_WriteEeprom(int ioaddr, int address, unsigned short value)
    354     {
    355     int cksum = 0,v;
    356     int i;
    357     int maxAddress, cksumAddress;
    358 
    359 	if (INF_3C90X.isBrev)
    360 	    {
    361 	    maxAddress=0x1f;
    362 	    cksumAddress=0x20;
    363 	    }
    364 	else
    365 	    {
    366 	    maxAddress=0x16;
    367 	    cksumAddress=0x17;
    368 	    }
    369 
    370 	/** Write the value. **/
    371 	if (a3c90x_internal_WriteEepromWord(ioaddr, address, value) == -1)
    372 	    return -1;
    373 
    374 	/** Recompute the checksum. **/
    375 	for(i=0;i<=maxAddress;i++)
    376 	    {
    377 	    v = a3c90x_internal_ReadEeprom(ioaddr, i);
    378 	    cksum ^= (v & 0xFF);
    379 	    cksum ^= ((v>>8) & 0xFF);
    380 	    }
    381 	/** Write the checksum to the location in the eeprom **/
    382 	if (a3c90x_internal_WriteEepromWord(ioaddr, cksumAddress, cksum) == -1)
    383 	    return -1;
    384 
    385     return 0;
    386     }
    387 
    388 
    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
    397 a3c90x_reset(struct nic *nic)
    398     {
    399     int cfg;
    400 
    401 #ifdef	CFG_3C90X_PRESERVE_XCVR
    402     /** Read the current InternalConfig value. **/
    403     a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winTxRxOptions3);
    404     cfg = inl(INF_3C90X.IOAddr + regInternalConfig_3_l);
    405 #endif
    406 
    407     /** Send the reset command to the card **/
    408     printf("Issuing RESET:\n");
    409     a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdGlobalReset, 0);
    410 
    411     /** wait for reset command to complete **/
    412     while (inw(INF_3C90X.IOAddr + regCommandIntStatus_w) & INT_CMDINPROGRESS);
    413 
    414     /** global reset command resets station mask, non-B revision cards
    415      ** require explicit reset of values
    416      **/
    417     a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winAddressing2);
    418     outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+0);
    419     outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+2);
    420     outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+4);
    421 
    422 #ifdef	CFG_3C90X_PRESERVE_XCVR
    423     /** Re-set the original InternalConfig value from before reset **/
    424     a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winTxRxOptions3);
    425     outl(cfg, INF_3C90X.IOAddr + regInternalConfig_3_l);
    426 
    427     /** enable DC converter for 10-Base-T **/
    428     if ((cfg&0x0300) == 0x0300)
    429 	{
    430 	a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdEnableDcConverter, 0);
    431 	}
    432 #endif
    433 
    434     /** Issue transmit reset, wait for command completion **/
    435     a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxReset, 0);
    436     while (inw(INF_3C90X.IOAddr + regCommandIntStatus_w) & INT_CMDINPROGRESS)
    437 	;
    438     if (! INF_3C90X.isBrev)
    439 	outb(0x01, INF_3C90X.IOAddr + regTxFreeThresh_b);
    440     a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxEnable, 0);
    441 
    442     /**
    443      ** reset of the receiver on B-revision cards re-negotiates the link
    444      ** takes several seconds (a computer eternity)
    445      **/
    446     if (INF_3C90X.isBrev)
    447 	a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxReset, 0x04);
    448     else
    449 	a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxReset, 0x00);
    450     while (inw(INF_3C90X.IOAddr + regCommandIntStatus_w) & INT_CMDINPROGRESS);
    451 	;
    452     a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxEnable, 0);
    453 
    454     a3c90x_internal_IssueCommand(INF_3C90X.IOAddr,
    455                                  cmdSetInterruptEnable, 0);
    456     /** enable rxComplete and txComplete **/
    457     a3c90x_internal_IssueCommand(INF_3C90X.IOAddr,
    458                                  cmdSetIndicationEnable, 0x0014);
    459     /** acknowledge any pending status flags **/
    460     a3c90x_internal_IssueCommand(INF_3C90X.IOAddr,
    461                                  cmdAcknowledgeInterrupt, 0x661);
    462 
    463     return;
    464     }
    465 
    466 
    467 
    468 /*** a3c90x_transmit: exported function that transmits a packet.  Does not
    469  *** return any particular status.  Parameters are:
    470  *** d[6] - destination address, ethernet;
    471  *** t - protocol type (ARP, IP, etc);
    472  *** s - size of the non-header part of the packet that needs transmitted;
    473  *** p - the pointer to the packet data itself.
    474  ***/
    475 static void
    476 a3c90x_transmit(struct nic *nic, const char *d, unsigned int t,
    477                 unsigned int s, const char *p)
    478     {
    479 
    480     struct eth_hdr
    481 	{
    482 	unsigned char dst_addr[ETH_ALEN];
    483 	unsigned char src_addr[ETH_ALEN];
    484 	unsigned short type;
    485 	} hdr;
    486 
    487     unsigned char status;
    488     unsigned i, retries;
    489 
    490     for (retries=0; retries < XMIT_RETRIES ; retries++)
    491 	{
    492 	/** Stall the download engine **/
    493 	a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdStallCtl, 2);
    494 
    495 	/** Make sure the card is not waiting on us **/
    496 	inw(INF_3C90X.IOAddr + regCommandIntStatus_w);
    497 	inw(INF_3C90X.IOAddr + regCommandIntStatus_w);
    498 
    499 	while (inw(INF_3C90X.IOAddr+regCommandIntStatus_w) &
    500 	       INT_CMDINPROGRESS)
    501 	    ;
    502 
    503 	/** Set the ethernet packet type **/
    504 	hdr.type = htons(t);
    505 
    506 	/** Copy the destination address **/
    507 	memcpy(hdr.dst_addr, d, ETH_ALEN);
    508 
    509 	/** Copy our MAC address **/
    510 	memcpy(hdr.src_addr, INF_3C90X.HWAddr, ETH_ALEN);
    511 
    512 	/** Setup the DPD (download descriptor) **/
    513 	INF_3C90X.TransmitDPD.DnNextPtr = 0;
    514 	/** set notification for transmission completion (bit 15) **/
    515 	INF_3C90X.TransmitDPD.FrameStartHeader = (s + sizeof(hdr)) | 0x8000;
    516 	INF_3C90X.TransmitDPD.HdrAddr = virt_to_bus(&hdr);
    517 	INF_3C90X.TransmitDPD.HdrLength = sizeof(hdr);
    518 	INF_3C90X.TransmitDPD.DataAddr = virt_to_bus(p);
    519 	INF_3C90X.TransmitDPD.DataLength = s + (1<<31);
    520 
    521 	/** Send the packet **/
    522 	outl(virt_to_bus(&(INF_3C90X.TransmitDPD)),
    523 	     INF_3C90X.IOAddr + regDnListPtr_l);
    524 
    525 	/** End Stall and Wait for upload to complete. **/
    526 	a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdStallCtl, 3);
    527 	while(inl(INF_3C90X.IOAddr + regDnListPtr_l) != 0)
    528 	    ;
    529 
    530 	/** Wait for NIC Transmit to Complete **/
    531 	load_timer2(10*TICKS_PER_MS);	/* Give it 10 ms */
    532 	while (!(inw(INF_3C90X.IOAddr + regCommandIntStatus_w)&0x0004) &&
    533 		timer2_running())
    534 		;
    535 
    536 	if (!(inw(INF_3C90X.IOAddr + regCommandIntStatus_w)&0x0004))
    537 	    {
    538 	    printf("3C90X: Tx Timeout\n");
    539 	    continue;
    540 	    }
    541 
    542 	status = inb(INF_3C90X.IOAddr + regTxStatus_b);
    543 
    544 	/** acknowledge transmit interrupt by writing status **/
    545 	outb(0x00, INF_3C90X.IOAddr + regTxStatus_b);
    546 
    547 	/** successful completion (sans "interrupt Requested" bit) **/
    548 	if ((status & 0xbf) == 0x80)
    549 	    return;
    550 
    551 	   printf("3C90X: Status (%hhX)\n", status);
    552 	/** check error codes **/
    553 	if (status & 0x02)
    554 	    {
    555 	    printf("3C90X: Tx Reclaim Error (%hhX)\n", status);
    556 	    a3c90x_reset(NULL);
    557 	    }
    558 	else if (status & 0x04)
    559 	    {
    560 	    printf("3C90X: Tx Status Overflow (%hhX)\n", status);
    561 	    for (i=0; i<32; i++)
    562 		outb(0x00, INF_3C90X.IOAddr + regTxStatus_b);
    563 	    /** must re-enable after max collisions before re-issuing tx **/
    564 	    a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxEnable, 0);
    565 	    }
    566 	else if (status & 0x08)
    567 	    {
    568 	    printf("3C90X: Tx Max Collisions (%hhX)\n", status);
    569 	    /** must re-enable after max collisions before re-issuing tx **/
    570 	    a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxEnable, 0);
    571 	    }
    572 	else if (status & 0x10)
    573 	    {
    574 	    printf("3C90X: Tx Underrun (%hhX)\n", status);
    575 	    a3c90x_reset(NULL);
    576 	    }
    577 	else if (status & 0x20)
    578 	    {
    579 	    printf("3C90X: Tx Jabber (%hhX)\n", status);
    580 	    a3c90x_reset(NULL);
    581 	    }
    582 	else if ((status & 0x80) != 0x80)
    583 	    {
    584 	    printf("3C90X: Internal Error - Incomplete Transmission (%hhX)\n",
    585 	           status);
    586 	    a3c90x_reset(NULL);
    587 	    }
    588 	}
    589 
    590     /** failed after RETRY attempts **/
    591     printf("Failed to send after %d retries\n", retries);
    592     return;
    593 
    594     }
    595 
    596 
    597 
    598 /*** a3c90x_poll: exported routine that waits for a certain length of time
    599  *** for a packet, and if it sees none, returns 0.  This routine should
    600  *** copy the packet to nic->packet if it gets a packet and set the size
    601  *** in nic->packetlen.  Return 1 if a packet was found.
    602  ***/
    603 static int
    604 a3c90x_poll(struct nic *nic)
    605     {
    606     int i, errcode;
    607 
    608     if (!(inw(INF_3C90X.IOAddr + regCommandIntStatus_w)&0x0010))
    609 	{
    610 	return 0;
    611 	}
    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 nic *nic)
    667     {
    668 	/* Disable the receiver and transmitter. */
    669 	outw(cmdRxDisable, INF_3C90X.IOAddr + regCommandIntStatus_w);
    670 	outw(cmdTxDisable, INF_3C90X.IOAddr + regCommandIntStatus_w);
    671     }
    672 
    673 
    674 
    675 /*** a3c90x_probe: exported routine to probe for the 3c905 card and perform
    676  *** initialization.  If this routine is called, the pci functions did find the
    677  *** card.  We just have to init it here.
    678  ***/
    679 struct nic*
    680 a3c90x_probe(struct nic *nic, unsigned short *probeaddrs, struct pci_device *pci)
    681     {
    682     int i, c;
    683     unsigned short eeprom[0x21];
    684     unsigned int cfg;
    685     unsigned int mopt;
    686     unsigned short linktype;
    687 
    688     if (probeaddrs == 0 || probeaddrs[0] == 0)
    689           return 0;
    690 
    691     adjust_pci_device(pci);
    692 
    693     INF_3C90X.IOAddr = probeaddrs[0] & ~3;
    694     INF_3C90X.CurrentWindow = 255;
    695     switch (a3c90x_internal_ReadEeprom(INF_3C90X.IOAddr, 0x03))
    696 	{
    697 	case 0x9000: /** 10 Base TPO             **/
    698 	case 0x9001: /** 10/100 T4               **/
    699 	case 0x9050: /** 10/100 TPO              **/
    700 	case 0x9051: /** 10 Base Combo           **/
    701 		INF_3C90X.isBrev = 0;
    702 		break;
    703 
    704 	case 0x9004: /** 10 Base TPO             **/
    705 	case 0x9005: /** 10 Base Combo           **/
    706 	case 0x9006: /** 10 Base TPO and Base2   **/
    707 	case 0x900A: /** 10 Base FL              **/
    708 	case 0x9055: /** 10/100 TPO              **/
    709 	case 0x9056: /** 10/100 T4               **/
    710 	case 0x905A: /** 10 Base FX              **/
    711 	default:
    712 		INF_3C90X.isBrev = 1;
    713 		break;
    714 	}
    715 
    716     /** Load the EEPROM contents **/
    717     if (INF_3C90X.isBrev)
    718 	{
    719 	for(i=0;i<=0x20;i++)
    720 	    {
    721 	    eeprom[i] = a3c90x_internal_ReadEeprom(INF_3C90X.IOAddr, i);
    722 	    }
    723 
    724 #ifdef	CFG_3C90X_BOOTROM_FIX
    725 	/** Set xcvrSelect in InternalConfig in eeprom. **/
    726 	/* only necessary for 3c905b revision cards with boot PROM bug!!! */
    727 	a3c90x_internal_WriteEeprom(INF_3C90X.IOAddr, 0x13, 0x0160);
    728 #endif
    729 
    730 #ifdef	CFG_3C90X_XCVR
    731 	if (CFG_3C90X_XCVR == 255)
    732 	    {
    733 	    /** Clear the LanWorks register **/
    734 	    a3c90x_internal_WriteEeprom(INF_3C90X.IOAddr, 0x16, 0);
    735 	    }
    736 	else
    737 	    {
    738 	    /** Set the selected permanent-xcvrSelect in the
    739 	     ** LanWorks register
    740 	     **/
    741 	    a3c90x_internal_WriteEeprom(INF_3C90X.IOAddr, 0x16,
    742 	                    XCVR_MAGIC + ((CFG_3C90X_XCVR) & 0x000F));
    743 	    }
    744 #endif
    745 	}
    746     else
    747 	{
    748 	for(i=0;i<=0x17;i++)
    749 	    {
    750 	    eeprom[i] = a3c90x_internal_ReadEeprom(INF_3C90X.IOAddr, i);
    751 	    }
    752 	}
    753 
    754     /** Print identification message **/
    755     printf("\n\n3C90X Driver 2.00 "
    756            "Copyright 1999 LightSys Technology Services, Inc.\n"
    757            "Portions Copyright 1999 Steve Smith\n");
    758     printf("Provided with ABSOLUTELY NO WARRANTY.\n");
    759     printf("-------------------------------------------------------"
    760            "------------------------\n");
    761 
    762     /** Retrieve the Hardware address and print it on the screen. **/
    763     INF_3C90X.HWAddr[0] = eeprom[0]>>8;
    764     INF_3C90X.HWAddr[1] = eeprom[0]&0xFF;
    765     INF_3C90X.HWAddr[2] = eeprom[1]>>8;
    766     INF_3C90X.HWAddr[3] = eeprom[1]&0xFF;
    767     INF_3C90X.HWAddr[4] = eeprom[2]>>8;
    768     INF_3C90X.HWAddr[5] = eeprom[2]&0xFF;
    769     printf("MAC Address = %!\n", INF_3C90X.HWAddr);
    770 
    771     /** Program the MAC address into the station address registers **/
    772     a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winAddressing2);
    773     outw(htons(eeprom[0]), INF_3C90X.IOAddr + regStationAddress_2_3w);
    774     outw(htons(eeprom[1]), INF_3C90X.IOAddr + regStationAddress_2_3w+2);
    775     outw(htons(eeprom[2]), INF_3C90X.IOAddr + regStationAddress_2_3w+4);
    776     outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+0);
    777     outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+2);
    778     outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+4);
    779 
    780     /** Fill in our entry in the etherboot arp table **/
    781     for(i=0;i<ETH_ALEN;i++)
    782 	nic->node_addr[i] = (eeprom[i/2] >> (8*((i&1)^1))) & 0xff;
    783 
    784     /** Read the media options register, print a message and set default
    785      ** xcvr.
    786      **
    787      ** Uses Media Option command on B revision, Reset Option on non-B
    788      ** revision cards -- same register address
    789      **/
    790     a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winTxRxOptions3);
    791     mopt = inw(INF_3C90X.IOAddr + regResetMediaOptions_3_w);
    792 
    793     /** mask out VCO bit that is defined as 10baseFL bit on B-rev cards **/
    794     if (! INF_3C90X.isBrev)
    795 	{
    796 	mopt &= 0x7F;
    797 	}
    798 
    799     printf("Connectors present: ");
    800     c = 0;
    801     linktype = 0x0008;
    802     if (mopt & 0x01)
    803 	{
    804 	printf("%s100Base-T4",(c++)?", ":"");
    805 	linktype = 0x0006;
    806 	}
    807     if (mopt & 0x04)
    808 	{
    809 	printf("%s100Base-FX",(c++)?", ":"");
    810 	linktype = 0x0005;
    811 	}
    812     if (mopt & 0x10)
    813 	{
    814 	printf("%s10Base-2",(c++)?", ":"");
    815 	linktype = 0x0003;
    816 	}
    817     if (mopt & 0x20)
    818 	{
    819 	printf("%sAUI",(c++)?", ":"");
    820 	linktype = 0x0001;
    821 	}
    822     if (mopt & 0x40)
    823 	{
    824 	printf("%sMII",(c++)?", ":"");
    825 	linktype = 0x0006;
    826 	}
    827     if ((mopt & 0xA) == 0xA)
    828 	{
    829 	printf("%s10Base-T / 100Base-TX",(c++)?", ":"");
    830 	linktype = 0x0008;
    831 	}
    832     else if ((mopt & 0xA) == 0x2)
    833 	{
    834 	printf("%s100Base-TX",(c++)?", ":"");
    835 	linktype = 0x0008;
    836 	}
    837     else if ((mopt & 0xA) == 0x8)
    838 	{
    839 	printf("%s10Base-T",(c++)?", ":"");
    840 	linktype = 0x0008;
    841 	}
    842     printf(".\n");
    843 
    844     /** Determine transceiver type to use, depending on value stored in
    845      ** eeprom 0x16
    846      **/
    847     if (INF_3C90X.isBrev)
    848 	{
    849 	if ((eeprom[0x16] & 0xFF00) == XCVR_MAGIC)
    850 	    {
    851 	    /** User-defined **/
    852 	    linktype = eeprom[0x16] & 0x000F;
    853 	    }
    854 	}
    855     else
    856 	{
    857 #ifdef	CFG_3C90X_XCVR
    858 	    if (CFG_3C90X_XCVR != 255)
    859 		linktype = CFG_3C90X_XCVR;
    860 #endif	/* CFG_3C90X_XCVR */
    861 
    862 	    /** I don't know what MII MAC only mode is!!! **/
    863 	    if (linktype == 0x0009)
    864 		{
    865 		if (INF_3C90X.isBrev)
    866 			printf("WARNING: MII External MAC Mode only supported on B-revision "
    867 			       "cards!!!!\nFalling Back to MII Mode\n");
    868 		linktype = 0x0006;
    869 		}
    870 	}
    871 
    872     /** enable DC converter for 10-Base-T **/
    873     if (linktype == 0x0003)
    874 	{
    875 	a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdEnableDcConverter, 0);
    876 	}
    877 
    878     /** Set the link to the type we just determined. **/
    879     a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winTxRxOptions3);
    880     cfg = inl(INF_3C90X.IOAddr + regInternalConfig_3_l);
    881     cfg &= ~(0xF<<20);
    882     cfg |= (linktype<<20);
    883     outl(cfg, INF_3C90X.IOAddr + regInternalConfig_3_l);
    884 
    885     /** Now that we set the xcvr type, reset the Tx and Rx, re-enable. **/
    886     a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxReset, 0x00);
    887     while (inw(INF_3C90X.IOAddr + regCommandIntStatus_w) & INT_CMDINPROGRESS)
    888 	;
    889 
    890     if (!INF_3C90X.isBrev)
    891 	outb(0x01, INF_3C90X.IOAddr + regTxFreeThresh_b);
    892 
    893     a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxEnable, 0);
    894 
    895     /**
    896      ** reset of the receiver on B-revision cards re-negotiates the link
    897      ** takes several seconds (a computer eternity)
    898      **/
    899     if (INF_3C90X.isBrev)
    900 	a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxReset, 0x04);
    901     else
    902 	a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxReset, 0x00);
    903     while (inw(INF_3C90X.IOAddr + regCommandIntStatus_w) & INT_CMDINPROGRESS)
    904 	;
    905 
    906     /** Set the RX filter = receive only individual pkts & bcast. **/
    907     a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdSetRxFilter, 0x01 + 0x04);
    908     a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxEnable, 0);
    909 
    910 
    911     /**
    912      ** set Indication and Interrupt flags , acknowledge any IRQ's
    913      **/
    914     a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdSetInterruptEnable, 0);
    915     a3c90x_internal_IssueCommand(INF_3C90X.IOAddr,
    916                                  cmdSetIndicationEnable, 0x0014);
    917     a3c90x_internal_IssueCommand(INF_3C90X.IOAddr,
    918                                  cmdAcknowledgeInterrupt, 0x661);
    919 
    920     /** Set our exported functions **/
    921     nic->reset    = a3c90x_reset;
    922     nic->poll     = a3c90x_poll;
    923     nic->transmit = a3c90x_transmit;
    924     nic->disable  = a3c90x_disable;
    925 
    926     return nic;
    927     }
    928 
    929 
    930