/*
* 3c90x.c -- This file implements the 3c90x driver for etherboot. Written
* by Greg Beeley, Greg.Beeley@LightSys.org. Modified by Steve Smith,
* Steve.Smith@Juno.Com. Alignment bug fix Neil Newell (nn@icenoir.net).
*
* This program Copyright (C) 1999 LightSys Technology Services, Inc.
* Portions Copyright (C) 1999 Steve Smith
*
* This program may be re-distributed in source or binary form, modified,
* sold, or copied for any purpose, provided that the above copyright message
* and this text are included with all source copies or derivative works, and
* provided that the above copyright message and this text are included in the
* documentation of any binary-only distributions. This program is distributed
* WITHOUT ANY WARRANTY, without even the warranty of FITNESS FOR A PARTICULAR
* PURPOSE or MERCHANTABILITY. Please read the associated documentation
* "3c90x.txt" before compiling and using this driver.
*
* --------
*
* Program written with the assistance of the 3com documentation for
* the 3c905B-TX card, as well as with some assistance from the 3c59x
* driver Donald Becker wrote for the Linux kernel, and with some assistance
* from the remainder of the Etherboot distribution.
*
* REVISION HISTORY:
*
* v0.10 1-26-1998 GRB Initial implementation.
* v0.90 1-27-1998 GRB System works.
* v1.00pre1 2-11-1998 GRB Got prom boot issue fixed.
* v2.0 9-24-1999 SCS Modified for 3c905 (from 3c905b code)
* Re-wrote poll and transmit for
* better error recovery and heavy
* network traffic operation
* v2.01 5-26-2003 NN Fixed driver alignment issue which
* caused system lockups if driver structures
* not 8-byte aligned.
*
*/
#include "etherboot.h"
#include "nic.h"
#include "pci.h"
#include "timer.h"
/** any single transmission fails after 16 collisions or other errors
** this is the number of times to retry the transmission -- this should
** be plenty
**/
/*** Register definitions for the 3c905 ***/
enum Registers
{
/** **/
};
/** following are windowed registers **/
enum Registers7
{
};
enum Registers6
{
};
enum Registers5
{
};
enum Registers4
{
};
enum Registers3
{
/** Reset Options on Non-B Revision **/
/** definitions, pg 59 **/
};
enum Registers2
{
};
enum Registers1
{
};
enum Registers0
{
};
/*** The names for the eight register windows ***/
enum Windows
{
};
/*** Command definitions for the 3c90X ***/
enum Commands
{
};
/*** Values for int status register bitmask **/
/*** TX descriptor ***/
typedef struct
{
unsigned int DnNextPtr;
unsigned int FrameStartHeader;
unsigned int HdrAddr;
unsigned int HdrLength;
unsigned int DataAddr;
unsigned int DataLength;
}
/*** RX descriptor ***/
typedef struct
{
unsigned int UpNextPtr;
unsigned int UpPktStatus;
unsigned int DataAddr;
unsigned int DataLength;
}
/*** Global variables ***/
static struct
{
unsigned char isBrev;
unsigned char CurrentWindow;
unsigned int IOAddr;
}
/*** a3c90x_internal_IssueCommand: sends a command to the 3c90x card
***/
static int
{
unsigned int val;
/** Build the cmd. **/
val <<= 11;
/** Send the cmd to the cmd register **/
/** Wait for the cmd to complete, if necessary **/
return 0;
}
/*** a3c90x_internal_SetWindow: selects a register window set.
***/
static int
{
/** Window already as set? **/
/** Issue the window command. **/
return 0;
}
/*** a3c90x_internal_ReadEeprom - read data from the serial eeprom.
***/
static unsigned short
{
unsigned short val;
/** Select correct window **/
/** Make sure the eeprom isn't busy **/
/** Read the value. **/
return val;
}
#if 0
/*** a3c90x_internal_WriteEepromWord - write a physical word of
*** data to the onboard serial eeprom (not the BIOS prom, but the
*** nvram in the card that stores, among other things, the MAC
*** address).
***/
static int
{
/** Select register window **/
/** Verify Eeprom not busy **/
/** Issue WriteEnable, and wait for completion. **/
/** Issue EraseRegister, and wait for completion. **/
/** Send the new data to the eeprom, and wait for completion. **/
/** Burn the new data into the eeprom, and wait for completion. **/
return 0;
}
#endif
#if 0
/*** a3c90x_internal_WriteEeprom - write data to the serial eeprom,
*** and re-compute the eeprom checksum.
***/
static int
{
int cksum = 0,v;
int i;
int maxAddress, cksumAddress;
{
maxAddress=0x1f;
cksumAddress=0x20;
}
else
{
maxAddress=0x16;
cksumAddress=0x17;
}
/** Write the value. **/
return -1;
/** Recompute the checksum. **/
for(i=0;i<=maxAddress;i++)
{
v = a3c90x_internal_ReadEeprom(ioaddr, i);
cksum ^= (v & 0xFF);
}
/** Write the checksum to the location in the eeprom **/
return -1;
return 0;
}
#endif
/*** a3c90x_reset: exported function that resets the card to its default
*** state. This is so the Linux driver can re-set the card up the way
*** it wants to. If CFG_3C90X_PRESERVE_XCVR is defined, then the reset will
*** not alter the selected transceiver that we used to download the boot
*** image.
***/
static void a3c90x_reset(void)
{
#ifdef CFG_3C90X_PRESERVE_XCVR
int cfg;
/** Read the current InternalConfig value. **/
#endif
/** Send the reset command to the card **/
printf("Issuing RESET:\n");
/** wait for reset command to complete **/
/** global reset command resets station mask, non-B revision cards
** require explicit reset of values
**/
#ifdef CFG_3C90X_PRESERVE_XCVR
/** Re-set the original InternalConfig value from before reset **/
/** enable DC converter for 10-Base-T **/
{
}
#endif
/** Issue transmit reset, wait for command completion **/
;
/**
** reset of the receiver on B-revision cards re-negotiates the link
** takes several seconds (a computer eternity)
**/
else
;
/** enable rxComplete and txComplete **/
cmdSetIndicationEnable, 0x0014);
/** acknowledge any pending status flags **/
cmdAcknowledgeInterrupt, 0x661);
return;
}
/*** a3c90x_transmit: exported function that transmits a packet. Does not
*** return any particular status. Parameters are:
*** d[6] - destination address, ethernet;
*** t - protocol type (ARP, IP, etc);
*** s - size of the non-header part of the packet that needs transmitted;
*** p - the pointer to the packet data itself.
***/
static void
unsigned int s, const char *p)
{
struct eth_hdr
{
unsigned short type;
} hdr;
unsigned char status;
unsigned i, retries;
{
/** Stall the download engine **/
/** Make sure the card is not waiting on us **/
;
/** Set the ethernet packet type **/
/** Copy the destination address **/
/** Copy our MAC address **/
/** Setup the DPD (download descriptor) **/
/** set notification for transmission completion (bit 15) **/
/** Send the packet **/
/** End Stall and Wait for upload to complete. **/
;
/** Wait for NIC Transmit to Complete **/
;
{
printf("3C90X: Tx Timeout\n");
continue;
}
/** acknowledge transmit interrupt by writing status **/
/** successful completion (sans "interrupt Requested" bit) **/
return;
/** check error codes **/
if (status & 0x02)
{
a3c90x_reset();
}
else if (status & 0x04)
{
for (i=0; i<32; i++)
/** must re-enable after max collisions before re-issuing tx **/
}
else if (status & 0x08)
{
/** must re-enable after max collisions before re-issuing tx **/
}
else if (status & 0x10)
{
a3c90x_reset();
}
else if (status & 0x20)
{
a3c90x_reset();
}
{
printf("3C90X: Internal Error - Incomplete Transmission (%hhX)\n",
status);
a3c90x_reset();
}
}
/** failed after RETRY attempts **/
return;
}
/*** a3c90x_poll: exported routine that waits for a certain length of time
*** for a packet, and if it sees none, returns 0. This routine should
*** copy the packet to nic->packet if it gets a packet and set the size
*** in nic->packetlen. Return 1 if a packet was found.
***/
static int
{
int i, errcode;
{
return 0;
}
if ( ! retrieve ) return 1;
/** we don't need to acknowledge rxComplete -- the upload engine
** does it for us.
**/
/** Build the up-load descriptor **/
/** Submit the upload descriptor to the NIC **/
/** Wait for upload completion (upComplete(15) or upError (14)) **/
for(i=0;i<40000;i++);
for(i=0;i<40000;i++);
/** Check for Error (else we have good packet) **/
{
else
return 0;
}
/** Ok, got packet. Set length in nic->packetlen. **/
return 1;
}
/*** a3c90x_disable: exported routine to disable the card. What's this for?
*** the eepro100.c driver didn't have one, so I just left this one empty too.
*** Ideas anyone?
*** Must turn off receiver at least so stray packets will not corrupt memory
*** [Ken]
***/
static void
{
/* reset and disable merge */
a3c90x_reset();
/* Disable the receiver and transmitter. */
}
{
switch ( action ) {
case DISABLE :
break;
case ENABLE :
break;
case FORCE :
break;
}
}
/*** a3c90x_probe: exported routine to probe for the 3c905 card and perform
*** initialization. If this routine is called, the pci functions did find the
*** card. We just have to init it here.
***/
{
int i, c;
unsigned int cfg;
unsigned int mopt;
unsigned int mstat;
unsigned short linktype;
return 0;
{
case 0x9000: /** 10 Base TPO **/
case 0x9001: /** 10/100 T4 **/
case 0x9050: /** 10/100 TPO **/
case 0x9051: /** 10 Base Combo **/
break;
case 0x9004: /** 10 Base TPO **/
case 0x9005: /** 10 Base Combo **/
case 0x9006: /** 10 Base TPO and Base2 **/
case 0x900A: /** 10 Base FL **/
case 0x9055: /** 10/100 TPO **/
case 0x9056: /** 10/100 T4 **/
case 0x905A: /** 10 Base FX **/
default:
break;
}
/** Load the EEPROM contents **/
{
for(i=0;i<=0x20;i++)
{
}
#ifdef CFG_3C90X_BOOTROM_FIX
/** Set xcvrSelect in InternalConfig in eeprom. **/
/* only necessary for 3c905b revision cards with boot PROM bug!!! */
#endif
#ifdef CFG_3C90X_XCVR
if (CFG_3C90X_XCVR == 255)
{
/** Clear the LanWorks register **/
}
else
{
/** Set the selected permanent-xcvrSelect in the
** LanWorks register
**/
}
#endif
}
else
{
for(i=0;i<=0x17;i++)
{
}
}
/** Print identification message **/
printf("\n\n3C90X Driver 2.00 "
"Copyright 1999 LightSys Technology Services, Inc.\n"
"Portions Copyright 1999 Steve Smith\n");
printf("Provided with ABSOLUTELY NO WARRANTY.\n");
#ifdef CFG_3C90X_BOOTROM_FIX
{
printf("NOTE: 3c905b bootrom fix enabled; has side "
"effects. See 3c90x.txt for info.\n");
}
#endif
printf("-------------------------------------------------------"
"------------------------\n");
/** Retrieve the Hardware address and print it on the screen. **/
/* Test if the link is good, if not continue */
printf("Valid link not established\n");
return 0;
}
/** Program the MAC address into the station address registers **/
/** Fill in our entry in the etherboot arp table **/
for(i=0;i<ETH_ALEN;i++)
/** Read the media options register, print a message and set default
** xcvr.
**
** Uses Media Option command on B revision, Reset Option on non-B
** revision cards -- same register address
**/
/** mask out VCO bit that is defined as 10baseFL bit on B-rev cards **/
{
mopt &= 0x7F;
}
printf("Connectors present: ");
c = 0;
linktype = 0x0008;
if (mopt & 0x01)
{
linktype = 0x0006;
}
if (mopt & 0x04)
{
linktype = 0x0005;
}
if (mopt & 0x10)
{
linktype = 0x0003;
}
if (mopt & 0x20)
{
linktype = 0x0001;
}
if (mopt & 0x40)
{
linktype = 0x0006;
}
{
linktype = 0x0008;
}
{
linktype = 0x0008;
}
{
linktype = 0x0008;
}
printf(".\n");
/** Determine transceiver type to use, depending on value stored in
** eeprom 0x16
**/
{
{
/** User-defined **/
}
}
else
{
#ifdef CFG_3C90X_XCVR
if (CFG_3C90X_XCVR != 255)
#endif /* CFG_3C90X_XCVR */
/** I don't know what MII MAC only mode is!!! **/
if (linktype == 0x0009)
{
printf("WARNING: MII External MAC Mode only supported on B-revision "
"cards!!!!\nFalling Back to MII Mode\n");
linktype = 0x0006;
}
}
/** enable DC converter for 10-Base-T **/
if (linktype == 0x0003)
{
}
/** Set the link to the type we just determined. **/
/** Now that we set the xcvr type, reset the Tx and Rx, re-enable. **/
;
/**
** reset of the receiver on B-revision cards re-negotiates the link
** takes several seconds (a computer eternity)
**/
else
;
/** Set the RX filter = receive only individual pkts & multicast & bcast. **/
/**
** set Indication and Interrupt flags , acknowledge any IRQ's
**/
cmdSetIndicationEnable, 0x0014);
cmdAcknowledgeInterrupt, 0x661);
/** Set our exported functions **/
return 1;
}
/* Original 90x revisions: */
/* Newer 90xB revisions: */
/* Newer 90xC revision: */
};
.type = NIC_DRIVER,
.name = "3C90X",
.probe = a3c90x_probe,
.ids = a3c90x_nics,
.class = 0,
};