diff --git a/netboot/3c90x.c b/netboot/3c90x.c new file mode 100644 index 000000000..b2fde66f2 --- /dev/null +++ b/netboot/3c90x.c @@ -0,0 +1,922 @@ +/* + * 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 + * + * 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 + * + */ + +#include "etherboot.h" +#include "nic.h" +#ifndef __FreeBSD__ +#include +#endif + +#define TIME_OUT 60000 +#define XCVR_MAGIC (0x5A00) +/** any single transmission fails after 16 collisions or other errors + ** this is the number of times to retry the transmission -- this should + ** be plenty + **/ +#define XMIT_RETRIES 250 + +#undef virt_to_bus +#define virt_to_bus(x) ((unsigned long)x) + +/*** Register definitions for the 3c905 ***/ +enum Registers + { + regPowerMgmtCtrl_w = 0x7c, /** 905B Revision Only **/ + regUpMaxBurst_w = 0x7a, /** 905B Revision Only **/ + regDnMaxBurst_w = 0x78, /** 905B Revision Only **/ + regDebugControl_w = 0x74, /** 905B Revision Only **/ + regDebugData_l = 0x70, /** 905B Revision Only **/ + regRealTimeCnt_l = 0x40, /** Universal **/ + regUpBurstThresh_b = 0x3e, /** 905B Revision Only **/ + regUpPoll_b = 0x3d, /** 905B Revision Only **/ + regUpPriorityThresh_b = 0x3c, /** 905B Revision Only **/ + regUpListPtr_l = 0x38, /** Universal **/ + regCountdown_w = 0x36, /** Universal **/ + regFreeTimer_w = 0x34, /** Universal **/ + regUpPktStatus_l = 0x30, /** Universal with Exception, pg 130 **/ + regTxFreeThresh_b = 0x2f, /** 90X Revision Only **/ + regDnPoll_b = 0x2d, /** 905B Revision Only **/ + regDnPriorityThresh_b = 0x2c, /** 905B Revision Only **/ + regDnBurstThresh_b = 0x2a, /** 905B Revision Only **/ + regDnListPtr_l = 0x24, /** Universal with Exception, pg 107 **/ + regDmaCtrl_l = 0x20, /** Universal with Exception, pg 106 **/ + /** **/ + regIntStatusAuto_w = 0x1e, /** 905B Revision Only **/ + regTxStatus_b = 0x1b, /** Universal with Exception, pg 113 **/ + regTimer_b = 0x1a, /** Universal **/ + regTxPktId_b = 0x18, /** 905B Revision Only **/ + regCommandIntStatus_w = 0x0e, /** Universal (Command Variations) **/ + }; + +/** following are windowed registers **/ +enum Registers7 + { + regPowerMgmtEvent_7_w = 0x0c, /** 905B Revision Only **/ + regVlanEtherType_7_w = 0x04, /** 905B Revision Only **/ + regVlanMask_7_w = 0x00, /** 905B Revision Only **/ + }; + +enum Registers6 + { + regBytesXmittedOk_6_w = 0x0c, /** Universal **/ + regBytesRcvdOk_6_w = 0x0a, /** Universal **/ + regUpperFramesOk_6_b = 0x09, /** Universal **/ + regFramesDeferred_6_b = 0x08, /** Universal **/ + regFramesRecdOk_6_b = 0x07, /** Universal with Exceptions, pg 142 **/ + regFramesXmittedOk_6_b = 0x06, /** Universal **/ + regRxOverruns_6_b = 0x05, /** Universal **/ + regLateCollisions_6_b = 0x04, /** Universal **/ + regSingleCollisions_6_b = 0x03, /** Universal **/ + regMultipleCollisions_6_b = 0x02, /** Universal **/ + regSqeErrors_6_b = 0x01, /** Universal **/ + regCarrierLost_6_b = 0x00, /** Universal **/ + }; + +enum Registers5 + { + regIndicationEnable_5_w = 0x0c, /** Universal **/ + regInterruptEnable_5_w = 0x0a, /** Universal **/ + regTxReclaimThresh_5_b = 0x09, /** 905B Revision Only **/ + regRxFilter_5_b = 0x08, /** Universal **/ + regRxEarlyThresh_5_w = 0x06, /** Universal **/ + regTxStartThresh_5_w = 0x00, /** Universal **/ + }; + +enum Registers4 + { + regUpperBytesOk_4_b = 0x0d, /** Universal **/ + regBadSSD_4_b = 0x0c, /** Universal **/ + regMediaStatus_4_w = 0x0a, /** Universal with Exceptions, pg 201 **/ + regPhysicalMgmt_4_w = 0x08, /** Universal **/ + regNetworkDiagnostic_4_w = 0x06, /** Universal with Exceptions, pg 203 **/ + regFifoDiagnostic_4_w = 0x04, /** Universal with Exceptions, pg 196 **/ + regVcoDiagnostic_4_w = 0x02, /** Undocumented? **/ + }; + +enum Registers3 + { + regTxFree_3_w = 0x0c, /** Universal **/ + regRxFree_3_w = 0x0a, /** Universal with Exceptions, pg 125 **/ + regResetMediaOptions_3_w = 0x08, /** Media Options on B Revision, **/ + /** Reset Options on Non-B Revision **/ + regMacControl_3_w = 0x06, /** Universal with Exceptions, pg 199 **/ + regMaxPktSize_3_w = 0x04, /** 905B Revision Only **/ + regInternalConfig_3_l = 0x00, /** Universal, different bit **/ + /** definitions, pg 59 **/ + }; + +enum Registers2 + { + regResetOptions_2_w = 0x0c, /** 905B Revision Only **/ + regStationMask_2_3w = 0x06, /** Universal with Exceptions, pg 127 **/ + regStationAddress_2_3w = 0x00, /** Universal with Exceptions, pg 127 **/ + }; + +enum Registers1 + { + regRxStatus_1_w = 0x0a, /** 90X Revision Only, Pg 126 **/ + }; + +enum Registers0 + { + regEepromData_0_w = 0x0c, /** Universal **/ + regEepromCommand_0_w = 0x0a, /** Universal **/ + regBiosRomData_0_b = 0x08, /** 905B Revision Only **/ + regBiosRomAddr_0_l = 0x04, /** 905B Revision Only **/ + }; + + +/*** The names for the eight register windows ***/ +enum Windows + { + winPowerVlan7 = 0x07, + winStatistics6 = 0x06, + winTxRxControl5 = 0x05, + winDiagnostics4 = 0x04, + winTxRxOptions3 = 0x03, + winAddressing2 = 0x02, + winUnused1 = 0x01, + winEepromBios0 = 0x00, + }; + + +/*** Command definitions for the 3c90X ***/ +enum Commands + { + cmdGlobalReset = 0x00, /** Universal with Exceptions, pg 151 **/ + cmdSelectRegisterWindow = 0x01, /** Universal **/ + cmdEnableDcConverter = 0x02, /** **/ + cmdRxDisable = 0x03, /** **/ + cmdRxEnable = 0x04, /** Universal **/ + cmdRxReset = 0x05, /** Universal **/ + cmdStallCtl = 0x06, /** Universal **/ + cmdTxEnable = 0x09, /** Universal **/ + cmdTxDisable = 0x0A, /** **/ + cmdTxReset = 0x0B, /** Universal **/ + cmdRequestInterrupt = 0x0C, /** **/ + cmdAcknowledgeInterrupt = 0x0D, /** Universal **/ + cmdSetInterruptEnable = 0x0E, /** Universal **/ + cmdSetIndicationEnable = 0x0F, /** Universal **/ + cmdSetRxFilter = 0x10, /** Universal **/ + cmdSetRxEarlyThresh = 0x11, /** **/ + cmdSetTxStartThresh = 0x13, /** **/ + cmdStatisticsEnable = 0x15, /** **/ + cmdStatisticsDisable = 0x16, /** **/ + cmdDisableDcConverter = 0x17, /** **/ + cmdSetTxReclaimThresh = 0x18, /** **/ + cmdSetHashFilterBit = 0x19, /** **/ + }; + + +/*** Values for int status register bitmask **/ +#define INT_INTERRUPTLATCH (1<<0) +#define INT_HOSTERROR (1<<1) +#define INT_TXCOMPLETE (1<<2) +#define INT_RXCOMPLETE (1<<4) +#define INT_RXEARLY (1<<5) +#define INT_INTREQUESTED (1<<6) +#define INT_UPDATESTATS (1<<7) +#define INT_LINKEVENT (1<<8) +#define INT_DNCOMPLETE (1<<9) +#define INT_UPCOMPLETE (1<<10) +#define INT_CMDINPROGRESS (1<<12) +#define INT_WINDOWNUMBER (7<<13) + + +/*** TX descriptor ***/ +typedef struct + { + unsigned int DnNextPtr; + unsigned int FrameStartHeader; + unsigned int HdrAddr; + unsigned int HdrLength; + unsigned int DataAddr; + unsigned int DataLength; + } + TXD; + +/*** RX descriptor ***/ +typedef struct + { + unsigned int UpNextPtr; + unsigned int UpPktStatus; + unsigned int DataAddr; + unsigned int DataLength; + } + RXD; + +/*** Global variables ***/ +static struct + { + unsigned char isBrev; + unsigned char CurrentWindow; + unsigned int IOAddr; + unsigned char HWAddr[6]; + TXD TransmitDPD; + RXD ReceiveUPD; + } + INF_3C90X; + + +/*** a3c90x_internal_IssueCommand: sends a command to the 3c90x card + ***/ +static int +a3c90x_internal_IssueCommand(int ioaddr, int cmd, int param) + { + unsigned int val; + + /** Build the cmd. **/ + val = cmd; + val <<= 11; + val |= param; + + /** Send the cmd to the cmd register **/ + outw(val, ioaddr + regCommandIntStatus_w); + + /** Wait for the cmd to complete, if necessary **/ + while (inw(ioaddr + regCommandIntStatus_w) & INT_CMDINPROGRESS); + + return 0; + } + + +/*** a3c90x_internal_SetWindow: selects a register window set. + ***/ +static int +a3c90x_internal_SetWindow(int ioaddr, int window) + { + + /** Window already as set? **/ + if (INF_3C90X.CurrentWindow == window) return 0; + + /** Issue the window command. **/ + a3c90x_internal_IssueCommand(ioaddr, cmdSelectRegisterWindow, window); + INF_3C90X.CurrentWindow = window; + + return 0; + } + + +/*** a3c90x_internal_ReadEeprom - read data from the serial eeprom. + ***/ +static unsigned short +a3c90x_internal_ReadEeprom(int ioaddr, int address) + { + unsigned short val; + + /** Select correct window **/ + a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winEepromBios0); + + /** Make sure the eeprom isn't busy **/ + while((1<<15) & inw(ioaddr + regEepromCommand_0_w)); + + /** Read the value. **/ + outw(address + ((0x02)<<6), ioaddr + regEepromCommand_0_w); + while((1<<15) & inw(ioaddr + regEepromCommand_0_w)); + val = inw(ioaddr + regEepromData_0_w); + + return val; + } + + +/*** 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 +a3c90x_internal_WriteEepromWord(int ioaddr, int address, unsigned short value) + { + /** Select register window **/ + a3c90x_internal_SetWindow(ioaddr, winEepromBios0); + + /** Verify Eeprom not busy **/ + while((1<<15) & inw(ioaddr + regEepromCommand_0_w)); + + /** Issue WriteEnable, and wait for completion. **/ + outw(0x30, ioaddr + regEepromCommand_0_w); + while((1<<15) & inw(ioaddr + regEepromCommand_0_w)); + + /** Issue EraseRegister, and wait for completion. **/ + outw(address + ((0x03)<<6), ioaddr + regEepromCommand_0_w); + while((1<<15) & inw(ioaddr + regEepromCommand_0_w)); + + /** Send the new data to the eeprom, and wait for completion. **/ + outw(value, ioaddr + regEepromData_0_w); + outw(0x30, ioaddr + regEepromCommand_0_w); + while((1<<15) & inw(ioaddr + regEepromCommand_0_w)); + + /** Burn the new data into the eeprom, and wait for completion. **/ + outw(address + ((0x01)<<6), ioaddr + regEepromCommand_0_w); + while((1<<15) & inw(ioaddr + regEepromCommand_0_w)); + + return 0; + } + + +/*** a3c90x_internal_WriteEeprom - write data to the serial eeprom, + *** and re-compute the eeprom checksum. + ***/ +static int +a3c90x_internal_WriteEeprom(int ioaddr, int address, unsigned short value) + { + int cksum = 0,v; + int i; + int maxAddress, cksumAddress; + + if (INF_3C90X.isBrev) + { + maxAddress=0x1f; + cksumAddress=0x20; + } + else + { + maxAddress=0x16; + cksumAddress=0x17; + } + + /** Write the value. **/ + if (a3c90x_internal_WriteEepromWord(ioaddr, address, value) == -1) + return -1; + + /** Recompute the checksum. **/ + for(i=0;i<=maxAddress;i++) + { + v = a3c90x_internal_ReadEeprom(ioaddr, i); + cksum ^= (v & 0xFF); + cksum ^= ((v>>8) & 0xFF); + } + /** Write the checksum to the location in the eeprom **/ + if (a3c90x_internal_WriteEepromWord(ioaddr, cksumAddress, cksum) == -1) + return -1; + + return 0; + } + + + +/*** 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(struct nic *nic) + { + int cfg; + +#ifdef CFG_3C90X_PRESERVE_XCVR + /** Read the current InternalConfig value. **/ + a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winTxRxOptions3); + cfg = inl(INF_3C90X.IOAddr + regInternalConfig_3_l); +#endif + + /** Send the reset command to the card **/ + printf("Issuing RESET:\n"); + a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdGlobalReset, 0); + + /** wait for reset command to complete **/ + while (inw(INF_3C90X.IOAddr + regCommandIntStatus_w) & INT_CMDINPROGRESS); + + /** global reset command resets station mask, non-B revision cards + ** require explicit reset of values + **/ + a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winAddressing2); + outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+0); + outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+2); + outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+4); + +#ifdef CFG_3C90X_PRESERVE_XCVR + /** Re-set the original InternalConfig value from before reset **/ + a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winTxRxOptions3); + outl(cfg, INF_3C90X.IOAddr + regInternalConfig_3_l); + + /** enable DC converter for 10-Base-T **/ + if ((cfg&0x0300) == 0x0300) + { + a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdEnableDcConverter, 0); + } +#endif + + /** Issue transmit reset, wait for command completion **/ + a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxReset, 0); + while (inw(INF_3C90X.IOAddr + regCommandIntStatus_w) & INT_CMDINPROGRESS) + ; + if (! INF_3C90X.isBrev) + outb(0x01, INF_3C90X.IOAddr + regTxFreeThresh_b); + a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxEnable, 0); + + /** + ** reset of the receiver on B-revision cards re-negotiates the link + ** takes several seconds (a computer eternity) + **/ + if (INF_3C90X.isBrev) + a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxReset, 0x04); + else + a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxReset, 0x00); + while (inw(INF_3C90X.IOAddr + regCommandIntStatus_w) & INT_CMDINPROGRESS); + ; + a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxEnable, 0); + + a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, + cmdSetInterruptEnable, 0); + /** enable rxComplete and txComplete **/ + a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, + cmdSetIndicationEnable, 0x0014); + /** acknowledge any pending status flags **/ + a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, + 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 +a3c90x_transmit(struct nic *nic, char *d, unsigned int t, + unsigned int s, char *p) + { + + struct eth_hdr + { + unsigned char dst_addr[6]; + unsigned char src_addr[6]; + unsigned short type; + } hdr; + + unsigned char status; + unsigned timeout, i, retries; + + for (retries=0; retries < XMIT_RETRIES ; retries++) + { + /** Stall the download engine **/ + a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdStallCtl, 2); + + /** Make sure the card is not waiting on us **/ + inw(INF_3C90X.IOAddr + regCommandIntStatus_w); + inw(INF_3C90X.IOAddr + regCommandIntStatus_w); + + while (inw(INF_3C90X.IOAddr+regCommandIntStatus_w) & + INT_CMDINPROGRESS) + ; + + /** Set the ethernet packet type **/ + hdr.type = htons(t); + + /** Copy the destination address **/ + memcpy(hdr.dst_addr, d, 6); + + /** Copy our MAC address **/ + memcpy(hdr.src_addr, INF_3C90X.HWAddr, 6); + + /** Setup the DPD (download descriptor) **/ + INF_3C90X.TransmitDPD.DnNextPtr = 0; + /** set notification for transmission completion (bit 15) **/ + INF_3C90X.TransmitDPD.FrameStartHeader = (s + sizeof(hdr)) | 0x8000; + INF_3C90X.TransmitDPD.HdrAddr = virt_to_bus(&hdr); + INF_3C90X.TransmitDPD.HdrLength = sizeof(hdr); + INF_3C90X.TransmitDPD.DataAddr = virt_to_bus(p); + INF_3C90X.TransmitDPD.DataLength = s + (1<<31); + + /** Send the packet **/ + outl(virt_to_bus(&(INF_3C90X.TransmitDPD)), + INF_3C90X.IOAddr + regDnListPtr_l); + + /** End Stall and Wait for upload to complete. **/ + a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdStallCtl, 3); + while(inl(INF_3C90X.IOAddr + regDnListPtr_l) != 0) + ; + + /** Wait for NIC Transmit to Complete **/ + timeout=TIME_OUT; + while (!(inw(INF_3C90X.IOAddr + regCommandIntStatus_w)&0x0004) && + --timeout) + for(i=0;ipacket if it gets a packet and set the size + *** in nic->packetlen. Return 1 if a packet was found. + ***/ +static int +a3c90x_poll(struct nic *nic) + { + int i, errcode; + + if (!(inw(INF_3C90X.IOAddr + regCommandIntStatus_w)&0x0010)) + { + return 0; + } + + /** we don't need to acknowledge rxComplete -- the upload engine + ** does it for us. + **/ + + /** Build the up-load descriptor **/ + INF_3C90X.ReceiveUPD.UpNextPtr = 0; + INF_3C90X.ReceiveUPD.UpPktStatus = 0; + INF_3C90X.ReceiveUPD.DataAddr = virt_to_bus(nic->packet); + INF_3C90X.ReceiveUPD.DataLength = 1536 + (1<<31); + + /** Submit the upload descriptor to the NIC **/ + outl(virt_to_bus(&(INF_3C90X.ReceiveUPD)), + INF_3C90X.IOAddr + regUpListPtr_l); + + /** Wait for upload completion (upComplete(15) or upError (14)) **/ + for(i=0;i<40000;i++); + while((INF_3C90X.ReceiveUPD.UpPktStatus & ((1<<14) | (1<<15))) == 0) + for(i=0;i<40000;i++); + + /** Check for Error (else we have good packet) **/ + if (INF_3C90X.ReceiveUPD.UpPktStatus & (1<<14)) + { + errcode = INF_3C90X.ReceiveUPD.UpPktStatus; + if (errcode & (1<<16)) + printf("3C90X: Receiver Overrun (%x)\n",errcode>>16); + else if (errcode & (1<<17)) + printf("3C90X: Runt Frame (%x)\n",errcode>>16); + else if (errcode & (1<<18)) + printf("3C90X: Alignment Error (%x)\n",errcode>>16); + else if (errcode & (1<<19)) + printf("3C90X: CRC Error (%x)\n",errcode>>16); + else if (errcode & (1<<20)) + printf("3C90X: Oversized Frame (%x)\n",errcode>>16); + else + printf("3C90X: Packet error (%x)\n",errcode>>16); + return 0; + } + + /** Ok, got packet. Set length in nic->packetlen. **/ + nic->packetlen = (INF_3C90X.ReceiveUPD.UpPktStatus & 0x1FFF); + + 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? + ***/ +static void +a3c90x_disable(struct nic *nic) + { + } + + + +/*** 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. + ***/ +struct nic* +a3c90x_probe(struct nic *nic, unsigned short *probeaddrs) + { + int i, j, to, v, c; + unsigned short eeprom[0x21]; + unsigned int cfg; + unsigned int mopt; + unsigned short linktype; + + if (probeaddrs == 0 || probeaddrs[0] == 0) + return 0; + + INF_3C90X.IOAddr = probeaddrs[0] & ~3; + INF_3C90X.CurrentWindow = 255; + switch (a3c90x_internal_ReadEeprom(INF_3C90X.IOAddr, 0x03)) + { + case 0x9000: /** 10 Base TPO **/ + case 0x9001: /** 10/100 T4 **/ + case 0x9050: /** 10/100 TPO **/ + case 0x9051: /** 10 Base Combo **/ + INF_3C90X.isBrev = 0; + 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: + INF_3C90X.isBrev = 1; + break; + } + + /** Load the EEPROM contents **/ + if (INF_3C90X.isBrev) + { + for(i=0;i<=0x20;i++) + { + eeprom[i] = a3c90x_internal_ReadEeprom(INF_3C90X.IOAddr, i); + } + + /** Set xcvrSelect in InternalConfig in eeprom. **/ + /* only necessary for 3c905b revision cards with boot PROM bug!!! */ + a3c90x_internal_WriteEeprom(INF_3C90X.IOAddr, 0x13, 0x0160); + +#ifdef CFG_3C90X_XCVR + if (CFG_3C90X_XCVR == 255) + { + /** Clear the LanWorks register **/ + a3c90x_internal_WriteEeprom(INF_3C90X.IOAddr, 0x16, 0); + } + else + { + /** Set the selected permanent-xcvrSelect in the + ** LanWorks register + **/ + a3c90x_internal_WriteEeprom(INF_3C90X.IOAddr, 0x16, + XCVR_MAGIC + ((CFG_3C90X_XCVR) & 0x000F)); + } +#endif + } + else + { + for(i=0;i<=0x17;i++) + { + eeprom[i] = a3c90x_internal_ReadEeprom(INF_3C90X.IOAddr, 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"); + printf("-------------------------------------------------------" + "------------------------\n"); + + /** Retrieve the Hardware address and print it on the screen. **/ + INF_3C90X.HWAddr[0] = eeprom[0]>>8; + INF_3C90X.HWAddr[1] = eeprom[0]&0xFF; + INF_3C90X.HWAddr[2] = eeprom[1]>>8; + INF_3C90X.HWAddr[3] = eeprom[1]&0xFF; + INF_3C90X.HWAddr[4] = eeprom[2]>>8; + INF_3C90X.HWAddr[5] = eeprom[2]&0xFF; + printf("MAC Address = %b:%b:%b:%b:%b:%b\n", + INF_3C90X.HWAddr[0],INF_3C90X.HWAddr[1],INF_3C90X.HWAddr[2], + INF_3C90X.HWAddr[3],INF_3C90X.HWAddr[4],INF_3C90X.HWAddr[5]); + + /** Program the MAC address into the station address registers **/ + a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winAddressing2); + outw(htons(eeprom[0]), INF_3C90X.IOAddr + regStationAddress_2_3w); + outw(htons(eeprom[1]), INF_3C90X.IOAddr + regStationAddress_2_3w+2); + outw(htons(eeprom[2]), INF_3C90X.IOAddr + regStationAddress_2_3w+4); + outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+0); + outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+2); + outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+4); + + /** Fill in our entry in the etherboot arp table **/ + for(i=0;i<6;i++) nic->node_addr[i] = (eeprom[i/2] >> (8*((i&1)^1))) & 0xff; + + /** 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 + **/ + a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winTxRxOptions3); + mopt = inw(INF_3C90X.IOAddr + regResetMediaOptions_3_w); + + /** mask out VCO bit that is defined as 10baseFL bit on B-rev cards **/ + if (! INF_3C90X.isBrev) + { + mopt &= 0x7F; + } + + printf("Connectors present: "); + c = 0; + linktype = 0x0008; + if (mopt & 0x01) + { + printf("%s100Base-T4",(c++)?", ":""); + linktype = 0x0006; + } + if (mopt & 0x04) + { + printf("%s100Base-FX",(c++)?", ":""); + linktype = 0x0005; + } + if (mopt & 0x10) + { + printf("%s10Base-2",(c++)?", ":""); + linktype = 0x0003; + } + if (mopt & 0x20) + { + printf("%sAUI",(c++)?", ":""); + linktype = 0x0001; + } + if (mopt & 0x40) + { + printf("%sMII",(c++)?", ":""); + linktype = 0x0006; + } + if ((mopt & 0xA) == 0xA) + { + printf("%s10Base-T / 100Base-TX",(c++)?", ":""); + linktype = 0x0008; + } + else if ((mopt & 0xA) == 0x2) + { + printf("%s100Base-TX",(c++)?", ":""); + linktype = 0x0008; + } + else if ((mopt & 0xA) == 0x8) + { + printf("%s10Base-T",(c++)?", ":""); + linktype = 0x0008; + } + printf(".\n"); + + /** Determine transceiver type to use, depending on value stored in + ** eeprom 0x16 + **/ + if (INF_3C90X.isBrev) + { + if ((eeprom[0x16] & 0xFF00) == XCVR_MAGIC) + { + /** User-defined **/ + linktype = eeprom[0x16] & 0x000F; + } + } + else + { +#ifdef CFG_3C90X_XCVR + if (CFG_3C90X_XCVR != 255) + linktype = CFG_3C90X_XCVR; +#endif // CFG_3C90X_XCVR + + /** I don't know what MII MAC only mode is!!! **/ + if (linktype == 0x0009) + { + if (INF_3C90X.isBrev) + 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) + { + a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdEnableDcConverter, 0); + } + + /** Set the link to the type we just determined. **/ + a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winTxRxOptions3); + cfg = inl(INF_3C90X.IOAddr + regInternalConfig_3_l); + cfg &= ~(0xF<<20); + cfg |= (linktype<<20); + outl(cfg, INF_3C90X.IOAddr + regInternalConfig_3_l); + + /** Now that we set the xcvr type, reset the Tx and Rx, re-enable. **/ + a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxReset, 0x00); + while (inw(INF_3C90X.IOAddr + regCommandIntStatus_w) & INT_CMDINPROGRESS) + ; + + if (!INF_3C90X.isBrev) + outb(0x01, INF_3C90X.IOAddr + regTxFreeThresh_b); + + a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxEnable, 0); + + /** + ** reset of the receiver on B-revision cards re-negotiates the link + ** takes several seconds (a computer eternity) + **/ + if (INF_3C90X.isBrev) + a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxReset, 0x04); + else + a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxReset, 0x00); + while (inw(INF_3C90X.IOAddr + regCommandIntStatus_w) & INT_CMDINPROGRESS) + ; + + /** Set the RX filter = receive only individual pkts & bcast. **/ + a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdSetRxFilter, 0x01 + 0x04); + a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxEnable, 0); + + + /** + ** set Indication and Interrupt flags , acknowledge any IRQ's + **/ + a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdSetInterruptEnable, 0); + a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, + cmdSetIndicationEnable, 0x0014); + a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, + cmdAcknowledgeInterrupt, 0x661); + + /** Set our exported functions **/ + nic->reset = a3c90x_reset; + nic->poll = a3c90x_poll; + nic->transmit = a3c90x_transmit; + nic->disable = a3c90x_disable; + + return nic; + } + + diff --git a/netboot/3c90x.txt b/netboot/3c90x.txt new file mode 100644 index 000000000..d965e23e2 --- /dev/null +++ b/netboot/3c90x.txt @@ -0,0 +1,268 @@ + + Instructions for use of the 3C90X driver for EtherBoot + + Original 3C905B support by: + Greg Beeley (Greg.Beeley@LightSys.org), + LightSys Technology Services, Inc. + February 11, 1999 + + Updates for 3C90X family by: + Steve Smith (steve.smith@juno.com) + October 1, 1999 + +------------------------------------------------------------------------------- + +I OVERVIEW + + The 3c90X series ethernet cards are a group of high-performance busmaster + DMA cards from 3Com. This particular driver supports both the 3c90x and + the 3c90xB revision cards. 3C90xC family support is not yet available. + + Here's the licensing information: + + 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. + + +II FLASH PROMS + + The 3c90xB cards, according to the 3Com documentation, only accept the + following flash memory chips: + + Atmel AT29C512 (64 kilobyte) + Atmel AT29C010 (128 kilobyte) + + The 3c90x cards, according to the 3Com documentation, accept the + following flash memory chips capacities: + + 64 kb (8 kB) + 128 kb (16 kB) + 256 kb (32 kB) and + 512 kb (64 kB) + + Atmel AT29C512 (64 kilobyte) chips are specifically listed for both + adapters, but flashing on the 3c905b cards would only be supported + through the Atmel parts. Any device, of the supported size, should + be supported when programmed by a dedicated PROM programmer (e.g. + not the card). + + To use this driver in such a PROM, visit Atmel's web site and download their + .PDF file containing a list of their distributors. Contact the distributors + for pricing information. The prices are quite reasonable (about $3 US each + for the 64 kB part), and are comparable to what one would expect for + similarly sized standard EPROMs. And, the flash chips are much easier to + work with, as they don't need to be UV-erased to be reprogrammed. The + 3C905B card actually provides a method to program the flash memory while + it is resident on board the card itself; if someone would like to write a + small DOS program to do the programming, I can provide the information + about the registers and so forth. + + Better yet, for those using Linux it might be even better if the programming + could be done while Linux is running, either via a modification to the 3c59x + driver or via some other program/driver. That way, a system administrator + on a private network could update all the NC flash images from a centrally + managed location. + + +III GENERAL USE + + Normally, the basic procedure for using this driver is as follows: + + 1. Run the 3c90xcfg program on the driver diskette to enable the + boot PROM and set it to 64k or 128k, as appropriate. + 2. Edit Config (in the src-32 directory) and define the + 90X_SIGNATURE for your particular adapter (model numbers and + corresponding values are listed in the file) + 3. Build the 3c90x.fd0 floppy image with possibly the value + CFG_3C90X_XCVR defined to the transceiver type that you want + to use (i.e., 10/100 rj45, AUI, coax, MII, etc.) + 4. Run the floppy image on the PC to be network booted, to get + it configured, and to verify that it will boot properly. + 5. Build the 3c90x.rom or 3c90x.lzrom PROM image and program + it into the flash memory chip. + 6. Put the PROM in the ethernet card, boot and enable 'boot from + network first' in the system BIOS, save and reboot. + + Here are some issues to be aware of: + + 1. A picky BIOS that may work better with NO_PNP_PCI_HEADER + defined to boot the prom image. + 2. The possible need to manually set the CFG_3C90X_XCVR value to + configure the transceiver type. Values are listed below. + 3. The possible need to define CFG_3C90X_PRESERVE_XCVR for use in + operating systems that don't intelligently determine the + transceiver type. + + Some things that are on the 'To-Do' list, perhaps for me, but perhaps + for any other volunteers out there: + + 1. Write a DOS- or Linux- based flash programmer for the 3c905b + card, so PROM users don't have to buy a $200 programmer.... + 2. Extend the driver to fully implement the auto-select + algorithm if the card has multiple media ports. + 3. Fix any bugs in the code .... + 4. Extend the driver to support the 3c905c revision cards + + Now for the details.... + + This driver has been tested on roughly 300 systems. The main two + configuration issues to contend with are: + 1. Ensure that PCI Busmastering is enabled for the adapter + (configured in the CMOS setup) + 2. Some systems don't work properly with the adapter when + plug and play OS is enabled; I always set it to "No" or + "Disabled" -- this makes it easier and really doesn't + adversely affect anything. + Roughly 95% of the systems worked when configured properly. A few + have issues with booting locally once the boot PROM has been installed + (this number has been less than 2%). Other configuration issues that + to check: + 1. Newer BIOS's actually work correctly with the network boot + order. Set the network adapter first. Most older BIOS's + automatically go to the network boot PROM first. + 2. For systems where the adapter was already installed and is + just having the PROM installed, try setting the "reset + configuration data" to yes in the CMOS setup if the BIOS isn't + seen at first. If your BIOS doesn't have this option, remove + the card, start the system, shut down, install the card and + restart (or switch to a different PCI slot). + 3. Make sure the CMOS security settings aren't preventing a boot. + + The 3c905B cards have a significant 'bug' that relates to the flash prom: + unless the card is set internally to the MII transceiver, it will only + read the first 8k of the PROM image. Don't ask why -- it seems really + obscure, but it has to do with the way they mux'd the address lines + from the PCI bus to the ROM. Unfortunately, most of us are not using + MII transceivers, and even the .lzrom image ends up being just a little + bit larger than 8k. + + So, the solution that I've used is to internally set the card's nvram + configuration to use MII when it boots. The 3c905b driver does this + automatically. This way, the 16k prom image can be loaded into memory, + and then the 3c905b driver can set the temporary configuration of the + card to an appropriate value, either configurable by the user or chosen + by the driver. + + The driver should choose an appropriate transceiver on the card. However, + if it doesn't on your card or if you need to, for instance, set your + card to 10mbps when connected to an unmanaged 10/100 hub, you can specify + which transceiver you want to use. To do this, build the 3c905b.fd0 + image with -DCFG_3C905B_XCVR=x, where 'x' is one of the following + values: + + 0 10Base-T + 1 10mbps AUI + 3 10Base-2 (thinnet/coax) + 4 100Base-TX + 5 100Base-FX + 6 MII + 8 Auto-negotiation 10Base-T / 100Base-TX (usually the default) + 9 MII External MAC Mode + 255 Allow driver to choose an 'appropriate' media port. + + Then proceed from step 2 in the above 'general use' instructions. The + .rom image can be built with CFG_3C90X_XCVR set to a value, but you + normally don't want to do this, since it is easier to change the + transceiver type by rebuilding a new floppy, changing the BIOS to floppy + boot, booting, and then changing the BIOS back to network boot. If + CFG_3C90X_XCVR is not set in a particular build, it just uses the + current configuration (either its 'best guess' or whatever the stored + CFG_3C90X_XCVR value was from the last time it was set). + + [[ Note for the more technically inclined: The CFG_3C90X_XCVR value is + programmed into a register in the card's NVRAM that was reserved for + LanWorks PROM images to use. When the driver boots, the card comes + up in MII mode, and the driver checks the LanWorks register to find + out if the user specified a transceiver type. If it finds that + information, it uses that, otherwise it picks a transceiver that the + card has based on the 3c905b's MediaOptions register. This driver isn't + quite smart enough to always determine which media port is actually + _connected_; maybe someone else would like to take on that task (it + actually involves sending a self-directed packet and seeing if it + comes back. IF it does, that port is connected). ]] + + Another issue to keep in mind is that it is possible that some OS'es + might not be happy with the way I've handled the PROM-image hack with + setting MII mode on bootup. Linux 2.0.35 does not have this problem. + I haven't tested anything else. The 3com documentation specifically + says that, at least with the card that I have, the device driver in the + OS should auto-select the media port, so other drivers should work fine + with this 'hack'. However, if yours doesn't seem to, you can try defining + CFG_3C90X_PRESERVE_XCVR when building to cause Etherboot to keep the + working setting (that allowed the bootp/tftp process) across the eth_reset + operation. + + +IV FOR DEVELOPERS.... + + If you would like to fix/extend/etc. this driver, feel free to do so; just + be sure you can test the modified version on the 3c905B-TX cards that the + driver was originally designed for. This section of this document gives + some information that might be relevant to a programmer. + + A. Main Entry Point + + a3c90x_probe is the main entry point for this driver. It is referred + to in an array in 'config.c'. + + B. Other Exported Functions + + The functions a3c90x_transmit, a3c90x_poll, a3c90x_reset, and + a3c90x_disable are exported functions that EtherBoot finds out about + as a result of a3c90x_probe setting entries in the nic structure + for them. The EtherBoot framework does not use interrupts. It is + polled. All transmit and receive operations are initiated by the + etherboot framework, not by an interrupt or by the driver. + + C. Internal Functions + + The following functions are internal to the driver: + + a3c90x_internal_IssueCommand - sends a command to the 3c905b card. + a3c90x_internal_SetWindow - shifts between one of eight register + windows onboard the 3c90x. The bottom 16 bytes of the card's + I/O space are multiplexed among 128 bytes, only 16 of which are + visible at any one time. This SetWindow function selects one of + the eight sets. + a3c90x_internal_ReadEeprom - reads a word (16 bits) from the + card's onboard nvram. This is NOT the BIOS boot rom. This is + where the card stores such things as its hardware address. + a3c90x_internal_WriteEeprom - writes a word (16 bits) to the + card's nvram, and recomputes the eeprom checksum. + a3c90x_internal_WriteEepromWord - writes a word (16 bits) to the + card's nvram. Used by the above routine. + a3c90x_internal_WriteEepromWord - writes a word (16 bits) to the + card's nvram. Used by the above routine. + + D. Globals + + All global variables are inside a global structure named INF_3C90X. + So, wherever you see that structure referenced, you know the variable + is a global. Just keeps things a little neater. + + E. Enumerations + + There are quite a few enumerated type definitions for registers and + so forth, many for registers that I didn't even touch in the driver. + Register types start with 'reg', window numbers (for SetWindow) + start with 'win', and commands (for IssueCommand) start with 'cmd'. + Register offsets also include an indication in the name as to the + size of the register (_b = byte, _w = word, _l = long), and which + window the register is in, if it is windowed (0-7). + + F. Why the 'a3c90x' name? + + I had to come up with a letter at the beginning of all of the + identifiers, since 3com so conveniently had their name start with a + number. Another driver used 't' (for 'three'?); I chose 'a' for + no reason at all. + diff --git a/netboot/README.netboot b/netboot/README.netboot new file mode 100644 index 000000000..9740b46af --- /dev/null +++ b/netboot/README.netboot @@ -0,0 +1,128 @@ +You can use the netboot support to download OS images from a network. +Nearly all the device drivers are coming from the network-based boot +loader, Etherboot. Please visit its web page. They have rich +documentations so you will be able to get useful information from there. +The URL is . + +These below are common options for configure. Perhaps you may not need +to specify them. + +--enable-packet-retransmission + Turns on packet retransmission. Use it on a congested network, where + the normal operation can't boot the image. + +--enable-pci-direct + Define this for PCI BIOSes that do not implement BIOS32 or not + correctly. + +Here is the information about the device drivers. They are all disabled +by default, so you must specify configure options to enable drivers you +want to use. Some drivers have extra per-driver options, so the extra +options are also described below. + +Caution: You should enable them as you need. Don't enable any +unnecessary driver, because GRUB might crash if you include too many +drivers at the same time. + +3Com503, aka Etherlink II, also /16 model + --enable-3c503 + --enable-3c503-shmem + Use 3c503 shared memory mode. + --enable-3c503-aui + Use AUI by default on 3c503 cards. + +3Com507 + --enable-3c507 + +3Com509, ISA/EISA + --enable-3c509 + --enable-3c509-hack + Send two bootp packets before waiting for a reply to the first. + Makes a 3c509 do bootp quicker. + +3Com529 == MCA 3c509 + --enable-3c529 + +3Com90x + --enable-3c90x + +Crystal Semiconductor CS89x0 + --enable-cs89x0 + --enable-cs-scan=LIST + Probe for CS89x0 base address using LIST of comma separated hex + addresses; increasing the address by one (0x300 -> 0x301) will force + a more aggressive probing algorithm. This might be neccessary after + a soft-reset of the NIC. + +Intel Etherexpress Pro/100 + --enable-eepro100 + +SMC 83c170 EPIC/100 + --enable-epic100 + +EXOS205 + --enable-exos205 + +Lance PCI PCNet/32 + --enable-lancepci + +Linksys LNE100TX and other NICs using this Tulip clone chip +Netgear FA310TX and other NICs using this Tulip clone chip +Tulip clones based on the Macronix 987x5 + --enable-ntulip + +NE1000/2000 and clones (ISA) + --enable-ne + --enable-ne-scan=LIST (0x280,0x300,0x320,0x340) + Probe for NE base address using LIST of comma separated hex + addresses. + +Novell NE2100 (Lance based, also works on NE1500) + --enable-ne2100 + +NE2000 PCI clone (RTL8029) +Winbond 86C940 +Compex RL2000 +KTI ET32P2 +NetVin 5000SC + --enable-nepci + --enable-compex-rl2000-fix + If you have a Compex RL2000 PCI 32-bit (11F6:1401), and the probe + hangs in "Probing...[NE*000/PCI]", try enabling this fix... it + worked for me :). + +Racal-Interlan NI5210 + --enable-ni5210 + +Racal-Interlan NI6510 + --enable-ni6510 + +Realtek 8139 + --enable-rtl8139 + +Schneider and Koch G16 + --enable-sk-g16 + +SMC9000 + --enable-smc9000 + --enable-smc9000-scan=LIST + List of I/O addresses to probe. + +Tiara, Fujitsu Lancard + --enable-tiara + +Old base driver for Tulip clones + --enable-tulip + +Rhine-I, e.g. D-Link DFE-530TX +Rhine-II + --enable-via-rhine + +WD8003/8013, SMC8216/8416 + --enable-wd + --enable-wd-default-mem=MEM (0xCC000) + Default memory location for WD/SMC cards. + + +The description about how to use the support can be found in the GRUB +manual. Run "info grub" in the shell prompt. diff --git a/netboot/epic100.c b/netboot/epic100.c new file mode 100644 index 000000000..dbd87f8c0 --- /dev/null +++ b/netboot/epic100.c @@ -0,0 +1,507 @@ +/* epic100.c: A SMC 83c170 EPIC/100 fast ethernet driver for EtherBoot 4.0 */ + +#define LINUX_OUT_MACROS + +#include "etherboot.h" +#include "nic.h" +/*#include */ +#include "epic100.h" + +#undef virt_to_bus +#define virt_to_bus(x) ((unsigned long)x) + +#define TX_RING_SIZE 1 /* enough for now */ +#define RX_RING_SIZE 1 + +#define PKT_BUF_SZ 1536 /* Size of each temporary Tx/Rx buffer.*/ + +#define TIME_OUT 1000000 + +/* +#define DEBUG_RX +#define DEBUG_TX +#define DEBUG_EEPROM +*/ + +static int epic_debug = 0; /* debug level */ + +/* The EPIC100 Rx and Tx buffer descriptors. */ +struct epic_rx_desc { + unsigned short status; + unsigned short rxlength; + unsigned long bufaddr; + unsigned short buflength; + unsigned short control; + unsigned long next; +}; + +/* description of the tx descriptors control bits commonly used */ +#define TD_STDFLAGS TD_LASTDESC + +struct epic_tx_desc { + unsigned short status; + unsigned short txlength; + unsigned long bufaddr; + unsigned short buflength; + unsigned short control; + unsigned long next; +}; + +#define delay(nanosec) do { int _i = 3; while (--_i > 0) \ + { __SLOW_DOWN_IO; }} while (0) + +static void epic100_open(); +static void epic100_init_ring(); +static void epic100_disable(struct nic *nic); +static int epic100_poll(struct nic *nic); +static void epic100_transmit(struct nic *nic, char *destaddr, + unsigned int type, unsigned int len, char *data); +static int read_eeprom(int location); +static int mii_read(int phy_id, int location); + +static int ioaddr; + +static int command; +static int intstat; +static int intmask; +static int genctl ; +static int eectl ; +static int test ; +static int mmctl ; +static int mmdata ; +static int lan0 ; +static int rxcon ; +static int txcon ; +static int prcdar ; +static int ptcdar ; +static int eththr ; + +static unsigned int cur_rx, cur_tx; /* The next free ring entry */ +#ifdef DEBUG_EEPROM +static unsigned short eeprom[64]; +#endif +signed char phys[4]; /* MII device addresses. */ +struct epic_rx_desc rx_ring[RX_RING_SIZE]; +struct epic_tx_desc tx_ring[TX_RING_SIZE]; +static char rx_packet[PKT_BUF_SZ * RX_RING_SIZE]; +static char tx_packet[PKT_BUF_SZ * TX_RING_SIZE]; + +/***********************************************************************/ +/* Externally visible functions */ +/***********************************************************************/ + + static void +epic100_reset(struct nic *nic) +{ + /* Soft reset the chip. */ + outl(GC_SOFT_RESET, genctl); +} + + struct nic* +epic100_probe(struct nic *nic, unsigned short *probeaddrs) +{ + unsigned short sum = 0; + unsigned short value; + int i, j; + unsigned short* ap; + int phy, phy_idx; + + if (probeaddrs == 0 || probeaddrs[0] == 0) + return 0; + + /* Ideally we would detect all network cards in slot order. That would + be best done a central PCI probe dispatch, which wouldn't work + well with the current structure. So instead we detect just the + Epic cards in slot order. */ + + ioaddr = probeaddrs[0] & ~3; /* Mask the bit that says "this is an io addr" */ + + /* compute all used static epic100 registers address */ + command = ioaddr + COMMAND; /* Control Register */ + intstat = ioaddr + INTSTAT; /* Interrupt Status */ + intmask = ioaddr + INTMASK; /* Interrupt Mask */ + genctl = ioaddr + GENCTL; /* General Control */ + eectl = ioaddr + EECTL; /* EEPROM Control */ + test = ioaddr + TEST; /* Test register (clocks) */ + mmctl = ioaddr + MMCTL; /* MII Management Interface Control */ + mmdata = ioaddr + MMDATA; /* MII Management Interface Data */ + lan0 = ioaddr + LAN0; /* MAC address. (0x40-0x48) */ + rxcon = ioaddr + RXCON; /* Receive Control */ + txcon = ioaddr + TXCON; /* Transmit Control */ + prcdar = ioaddr + PRCDAR; /* PCI Receive Current Descr Address */ + ptcdar = ioaddr + PTCDAR; /* PCI Transmit Current Descr Address */ + eththr = ioaddr + ETHTHR; /* Early Transmit Threshold */ + + /* Reset the chip & bring it out of low-power mode. */ + outl(GC_SOFT_RESET, genctl); + + /* Disable ALL interrupts by setting the interrupt mask. */ + outl(INTR_DISABLE, intmask); + + /* + * set the internal clocks: + * Application Note 7.15 says: + * In order to set the CLOCK TEST bit in the TEST register, + * perform the following: + * + * Write 0x0008 to the test register at least sixteen + * consecutive times. + * + * The CLOCK TEST bit is Write-Only. Writing it several times + * consecutively insures a successful write to the bit... + */ + + for (i = 0; i < 16; i++) { + outl(0x00000008, test); + } + +#ifdef DEBUG_EEPROM + for (i = 0; i < 64; i++) { + value = read_eeprom(i); + eeprom[i] = value; + sum += value; + } + + if (epic_debug > 1) { + printf("EEPROM contents\n"); + for (i = 0; i < 64; i++) { + printf(" %02x%s", eeprom[i], i % 16 == 15 ? "\n" : ""); + } + } +#endif + + /* This could also be read from the EEPROM. */ + ap = (unsigned short*)nic->node_addr; + for (i = 0; i < 3; i++) + *ap++ = inw(lan0 + i*4); + + printf(" I/O %x ", ioaddr); + + for (i = 0; i < 6; i++) + printf ("%b%c", nic->node_addr[i] , i < 5 ?':':' '); + + /* Find the connected MII xcvrs. */ + for (phy = 0, phy_idx = 0; phy < 32 && phy_idx < sizeof(phys); phy++) { + int mii_status = mii_read(phy, 0); + + if (mii_status != 0xffff && mii_status != 0x0000) { + phys[phy_idx++] = phy; + if (epic_debug > 1) { + printf("MII transceiver found at address %d.\n", phy); + } + } + } + if (phy_idx == 0) { + if (epic_debug > 1) { + printf("***WARNING***: No MII transceiver found!\n"); + } + /* Use the known PHY address of the EPII. */ + phys[0] = 3; + } + + epic100_open(); + + nic->reset = epic100_reset; + nic->poll = epic100_poll; + nic->transmit = epic100_transmit; + nic->disable = epic100_disable; + + return nic; +} + + static void +epic100_open() +{ + int i; + int mii_reg5; + int full_duplex = 0; + unsigned long tmp; + + epic100_init_ring(); + + /* Pull the chip out of low-power mode, and set for PCI read multiple. */ + outl(GC_RX_FIFO_THR_64 | GC_MRC_READ_MULT | GC_ONE_COPY, genctl); + + outl(TX_FIFO_THRESH, eththr); + + tmp = TC_EARLY_TX_ENABLE | TX_SLOT_TIME; + + mii_reg5 = mii_read(phys[0], 5); + if (mii_reg5 != 0xffff && (mii_reg5 & 0x0100)) { + full_duplex = 1; + printf(" full-duplex mode"); + tmp |= TC_LM_FULL_DPX; + } else + tmp |= TC_LM_NORMAL; + + outl(tmp, txcon); + + /* Give adress of RX and TX ring to the chip */ + outl(virt_to_bus(&rx_ring), prcdar); + outl(virt_to_bus(&tx_ring), ptcdar); + + /* Start the chip's Rx process: Don't receive broadcast */ + outl(0, rxcon); + outl(CR_START_RX | CR_QUEUE_RX, command); + + putchar('\n'); +} + +/* Initialize the Rx and Tx rings. */ + static void +epic100_init_ring() +{ + int i; + char* p; + + cur_rx = cur_tx = 0; + + p = &rx_packet[0]; + for (i = 0; i < RX_RING_SIZE; i++) { + rx_ring[i].status = RRING_OWN; /* Owned by Epic chip */ + rx_ring[i].buflength = PKT_BUF_SZ; + rx_ring[i].bufaddr = virt_to_bus(p + (PKT_BUF_SZ * i)); + rx_ring[i].control = 0; + rx_ring[i].next = virt_to_bus(&(rx_ring[i + 1]) ); + } + /* Mark the last entry as wrapping the ring. */ + rx_ring[i-1].next = virt_to_bus(&rx_ring[0]); + + /* + *The Tx buffer descriptor is filled in as needed, + * but we do need to clear the ownership bit. + */ + p = &tx_packet[0]; + + for (i = 0; i < TX_RING_SIZE; i++) { + tx_ring[i].status = 0; /* Owned by CPU */ + tx_ring[i].bufaddr = virt_to_bus(p + (PKT_BUF_SZ * i)); + tx_ring[i].control = TD_STDFLAGS; + tx_ring[i].next = virt_to_bus(&(tx_ring[i + 1]) ); + } + tx_ring[i-1].next = virt_to_bus(&tx_ring[0]); +} + +/* function: epic100_transmit / eth_transmit + * This transmits a packet. + * + * Arguments: char d[6]: destination ethernet address. + * unsigned short t: ethernet protocol type. + * unsigned short s: size of the data-part of the packet. + * char *p: the data for the packet. + * returns: void. + */ + static void +epic100_transmit(struct nic *nic, char *destaddr, unsigned int type, + unsigned int len, char *data) +{ + unsigned short nstype; + unsigned short status; + char* txp; + int to; + int entry; + + /* Calculate the next Tx descriptor entry. */ + entry = cur_tx % TX_RING_SIZE; + + if ((tx_ring[entry].status & TRING_OWN) == TRING_OWN) { + printf("eth_transmit: Unable to transmit. status=%x. Resetting...\n", + tx_ring[entry].status); + + epic100_open(); + return; + } + + txp = (char*)tx_ring[entry].bufaddr; + + memcpy(txp, destaddr, ETHER_ADDR_SIZE); + memcpy(txp + ETHER_ADDR_SIZE, nic->node_addr, ETHER_ADDR_SIZE); + nstype = htons(type); + memcpy(txp + 12, (char*)&nstype, 2); + memcpy(txp + ETHER_HDR_SIZE, data, len); + + len += ETHER_HDR_SIZE; + + /* + * Caution: the write order is important here, + * set the base address with the "ownership" + * bits last. + */ + tx_ring[entry].txlength = (len >= 60 ? len : 60); + tx_ring[entry].buflength = len; + tx_ring[entry].status = TRING_OWN; /* Pass ownership to the chip. */ + + cur_tx++; + + /* Trigger an immediate transmit demand. */ + outl(CR_QUEUE_TX, command); + + to = TIME_OUT; + status = tx_ring[entry].status; + + while ( (status & TRING_OWN) && --to) { + status = tx_ring[entry].status; + } + + if ((status & TRING_OWN) == 0) { +#ifdef DEBUG_TX + printf("tx done after %d loop(s), status %x\n", + TIME_OUT-to, tx_ring[entry].status ); +#endif + return; + } + + if (to == 0) { + printf("OOPS, Something wrong with transmitter. status=%x\n", + tx_ring[entry].status); + } +} + +/* function: epic100_poll / eth_poll + * This receives a packet from the network. + * + * Arguments: none + * + * returns: 1 if a packet was received. + * 0 if no pacet was received. + * side effects: + * returns the packet in the array nic->packet. + * returns the length of the packet in nic->packetlen. + */ + + static int +epic100_poll(struct nic *nic) +{ + int to; + int entry; + int status; + int retcode; + + entry = cur_rx % RX_RING_SIZE; + cur_rx++; + to = TIME_OUT; + + status = rx_ring[entry].status; + while ( (status & RRING_OWN) == RRING_OWN && --to) { + status = rx_ring[entry].status; + } + + if (to == 0) { +#ifdef DEBUG_RX + printf("epic_poll: time out! status %x\n", status); +#endif + /* Restart Receiver */ + outl(CR_START_RX | CR_QUEUE_RX, command); + return 0; + } + + /* We own the next entry, it's a new packet. Send it up. */ + + if (epic_debug > 4) + printf("epic_poll: entry %d status %8x\n", entry, status); + + if (status & 0x2000) { + printf("epic_poll: Giant packet\n"); + retcode = 0; + } else if (status & 0x0006) { + /* Rx Frame errors are counted in hardware. */ + printf("epic_poll: Frame received with errors\n"); + retcode = 0; + } else { + /* Omit the four octet CRC from the length. */ + nic->packetlen = rx_ring[entry].rxlength - 4; + memcpy(nic->packet, (char*)rx_ring[entry].bufaddr, nic->packetlen); + retcode = 1; + } + + /* Clear all error sources. */ + outl(status & INTR_CLEARERRS, intstat); + + /* Give the descriptor back to the chip */ + rx_ring[entry].status = RRING_OWN; + + /* Restart Receiver */ + outl(CR_START_RX | CR_QUEUE_RX, command); + + return retcode; +} + + + static void +epic100_disable(struct nic *nic) +{ +} + + +#ifdef DEBUG_EEPROM +/* Serial EEPROM section. */ + +/* EEPROM_Ctrl bits. */ +#define EE_SHIFT_CLK 0x04 /* EEPROM shift clock. */ +#define EE_CS 0x02 /* EEPROM chip select. */ +#define EE_DATA_WRITE 0x08 /* EEPROM chip data in. */ +#define EE_WRITE_0 0x01 +#define EE_WRITE_1 0x09 +#define EE_DATA_READ 0x10 /* EEPROM chip data out. */ +#define EE_ENB (0x0001 | EE_CS) + +/* The EEPROM commands include the alway-set leading bit. */ +#define EE_WRITE_CMD (5 << 6) +#define EE_READ_CMD (6 << 6) +#define EE_ERASE_CMD (7 << 6) + +#define eeprom_delay(n) delay(n) + + static int +read_eeprom(int location) +{ + int i; + int retval = 0; + int read_cmd = location | EE_READ_CMD; + + outl(EE_ENB & ~EE_CS, eectl); + outl(EE_ENB, eectl); + + /* Shift the read command bits out. */ + for (i = 10; i >= 0; i--) { + short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0; + outl(EE_ENB | dataval, eectl); + eeprom_delay(100); + outl(EE_ENB | dataval | EE_SHIFT_CLK, eectl); + eeprom_delay(150); + outl(EE_ENB | dataval, eectl); /* Finish EEPROM a clock tick. */ + eeprom_delay(250); + } + outl(EE_ENB, eectl); + + for (i = 16; i > 0; i--) { + outl(EE_ENB | EE_SHIFT_CLK, eectl); + eeprom_delay(100); + retval = (retval << 1) | ((inl(eectl) & EE_DATA_READ) ? 1 : 0); + outl(EE_ENB, eectl); + eeprom_delay(100); + } + + /* Terminate the EEPROM access. */ + outl(EE_ENB & ~EE_CS, eectl); + return retval; +} +#endif + + +#define MII_READOP 1 +#define MII_WRITEOP 2 + + static int +mii_read(int phy_id, int location) +{ + int i; + + outl((phy_id << 9) | (location << 4) | MII_READOP, mmctl); + /* Typical operation takes < 50 ticks. */ + + for (i = 4000; i > 0; i--) + if ((inl(mmctl) & MII_READOP) == 0) + break; + return inw(mmdata); +} diff --git a/netboot/epic100.h b/netboot/epic100.h new file mode 100644 index 000000000..8ab0d7edb --- /dev/null +++ b/netboot/epic100.h @@ -0,0 +1,188 @@ +#ifndef _EPIC100_H_ +# define _EPIC100_H_ + +#ifndef PCI_VENDOR_SMC +# define PCI_VENDOR_SMC 0x10B8 +#endif + +#ifndef PCI_DEVICE_SMC_EPIC100 +# define PCI_DEVICE_SMC_EPIC100 0x0005 +#endif + +#define PCI_DEVICE_ID_NONE 0xFFFF + +/* Offsets to registers (using SMC names). */ +enum epic100_registers { + COMMAND= 0, /* Control Register */ + INTSTAT= 4, /* Interrupt Status */ + INTMASK= 8, /* Interrupt Mask */ + GENCTL = 0x0C, /* General Control */ + NVCTL = 0x10, /* Non Volatile Control */ + EECTL = 0x14, /* EEPROM Control */ + TEST = 0x1C, /* Test register: marked as reserved (see in source code) */ + CRCCNT = 0x20, /* CRC Error Counter */ + ALICNT = 0x24, /* Frame Alignment Error Counter */ + MPCNT = 0x28, /* Missed Packet Counter */ + MMCTL = 0x30, /* MII Management Interface Control */ + MMDATA = 0x34, /* MII Management Interface Data */ + MIICFG = 0x38, /* MII Configuration */ + IPG = 0x3C, /* InterPacket Gap */ + LAN0 = 0x40, /* MAC address. (0x40-0x48) */ + IDCHK = 0x4C, /* BoardID/ Checksum */ + MC0 = 0x50, /* Multicast filter table. (0x50-0x5c) */ + RXCON = 0x60, /* Receive Control */ + TXCON = 0x70, /* Transmit Control */ + TXSTAT = 0x74, /* Transmit Status */ + PRCDAR = 0x84, /* PCI Receive Current Descriptor Address */ + PRSTAT = 0xA4, /* PCI Receive DMA Status */ + PRCPTHR= 0xB0, /* PCI Receive Copy Threshold */ + PTCDAR = 0xC4, /* PCI Transmit Current Descriptor Address */ + ETHTHR = 0xDC /* Early Transmit Threshold */ +}; + +/* Command register (CR_) bits */ +#define CR_STOP_RX (0x00000001) +#define CR_START_RX (0x00000002) +#define CR_QUEUE_TX (0x00000004) +#define CR_QUEUE_RX (0x00000008) +#define CR_NEXTFRAME (0x00000010) +#define CR_STOP_TX_DMA (0x00000020) +#define CR_STOP_RX_DMA (0x00000040) +#define CR_TX_UGO (0x00000080) + +/* Interrupt register bits. NI means No Interrupt generated */ + +#define INTR_RX_THR_STA (0x00400000) /* rx copy threshold status NI */ +#define INTR_RX_BUFF_EMPTY (0x00200000) /* rx buffers empty. NI */ +#define INTR_TX_IN_PROG (0x00100000) /* tx copy in progess. NI */ +#define INTR_RX_IN_PROG (0x00080000) /* rx copy in progress. NI */ +#define INTR_TXIDLE (0x00040000) /* tx idle. NI */ +#define INTR_RXIDLE (0x00020000) /* rx idle. NI */ +#define INTR_INTR_ACTIVE (0x00010000) /* Interrupt active. NI */ +#define INTR_RX_STATUS_OK (0x00008000) /* rx status valid. NI */ +#define INTR_PCI_TGT_ABT (0x00004000) /* PCI Target abort */ +#define INTR_PCI_MASTER_ABT (0x00002000) /* PCI Master abort */ +#define INTR_PCI_PARITY_ERR (0x00001000) /* PCI adress parity error */ +#define INTR_PCI_DATA_ERR (0x00000800) /* PCI data parity error */ +#define INTR_RX_THR_CROSSED (0x00000400) /* rx copy threshold crossed */ +#define INTR_CNTFULL (0x00000200) /* Counter overflow */ +#define INTR_TXUNDERRUN (0x00000100) /* tx underrun. */ +#define INTR_TXEMPTY (0x00000080) /* tx queue empty */ +#define INTR_TX_CH_COMPLETE (0x00000040) /* tx chain complete */ +#define INTR_TXDONE (0x00000020) /* tx complete (w or w/o err) */ +#define INTR_RXERROR (0x00000010) /* rx error (CRC) */ +#define INTR_RXOVERFLOW (0x00000008) /* rx buffer overflow */ +#define INTR_RX_QUEUE_EMPTY (0x00000004) /* rx queue empty. */ +#define INTR_RXHEADER (0x00000002) /* header copy complete */ +#define INTR_RXDONE (0x00000001) /* Receive copy complete */ + +#define INTR_CLEARINTR (0x00007FFF) +#define INTR_VALIDBITS (0x007FFFFF) +#define INTR_DISABLE (0x00000000) +#define INTR_CLEARERRS (0x00007F18) +#define INTR_ABNINTR (INTR_CNTFULL | INTR_TXUNDERRUN | INTR_RXOVERFLOW) + +/* General Control (GC_) bits */ + +#define GC_SOFT_RESET (0x00000001) +#define GC_INTR_ENABLE (0x00000002) +#define GC_SOFT_INTR (0x00000004) +#define GC_POWER_DOWN (0x00000008) +#define GC_ONE_COPY (0x00000010) +#define GC_BIG_ENDIAN (0x00000020) +#define GC_RX_PREEMPT_TX (0x00000040) +#define GC_TX_PREEMPT_RX (0x00000080) + +/* + * Receive FIFO Threshold values + * Control the level at which the PCI burst state machine + * begins to empty the receive FIFO. Possible values: 0-3 + * + * 0 => 32, 1 => 64, 2 => 96 3 => 128 bytes. + */ +#define GC_RX_FIFO_THR_32 (0x00000000) +#define GC_RX_FIFO_THR_64 (0x00000100) +#define GC_RX_FIFO_THR_96 (0x00000200) +#define GC_RX_FIFO_THR_128 (0x00000300) + +/* Memory Read Control (MRC_) values */ +#define GC_MRC_MEM_READ (0x00000000) +#define GC_MRC_READ_MULT (0x00000400) +#define GC_MRC_READ_LINE (0x00000800) + +#define GC_SOFTBIT0 (0x00001000) +#define GC_SOFTBIT1 (0x00002000) +#define GC_RESET_PHY (0x00004000) + +/* Definitions of the Receive Control (RC_) register bits */ + +#define RC_SAVE_ERRORED_PKT (0x00000001) +#define RC_SAVE_RUNT_FRAMES (0x00000002) +#define RC_RCV_BROADCAST (0x00000004) +#define RC_RCV_MULTICAST (0x00000008) +#define RC_RCV_INVERSE_PKT (0x00000010) +#define RC_PROMISCUOUS_MODE (0x00000020) +#define RC_MONITOR_MODE (0x00000040) +#define RC_EARLY_RCV_ENABLE (0x00000080) + +/* description of the rx descriptors control bits */ +#define RD_FRAGLIST (0x0001) /* Desc points to a fragment list */ +#define RD_LLFORM (0x0002) /* Frag list format */ +#define RD_HDR_CPY (0x0004) /* Desc used for header copy */ + +/* Definition of the Transmit CONTROL (TC) register bits */ + +#define TC_EARLY_TX_ENABLE (0x00000001) + +/* Loopback Mode (LM_) Select valuesbits */ +#define TC_LM_NORMAL (0x00000000) +#define TC_LM_INTERNAL (0x00000002) +#define TC_LM_EXTERNAL (0x00000004) +#define TC_LM_FULL_DPX (0x00000006) + +#define TX_SLOT_TIME (0x00000078) + +/* Bytes transferred to chip before transmission starts. */ +#define TX_FIFO_THRESH 128 /* Rounded down to 4 byte units. */ + +/* description of rx descriptors status bits */ +#define RRING_PKT_INTACT (0x0001) +#define RRING_ALIGN_ERR (0x0002) +#define RRING_CRC_ERR (0x0004) +#define RRING_MISSED_PKT (0x0008) +#define RRING_MULTICAST (0x0010) +#define RRING_BROADCAST (0x0020) +#define RRING_RECEIVER_DISABLE (0x0040) +#define RRING_STATUS_VALID (0x1000) +#define RRING_FRAGLIST_ERR (0x2000) +#define RRING_HDR_COPIED (0x4000) +#define RRING_OWN (0x8000) + +/* error summary */ +#define RRING_ERROR (RRING_ALIGN_ERR|RRING_CRC_ERR) + +/* description of tx descriptors status bits */ +#define TRING_PKT_INTACT (0x0001) /* pkt transmitted. */ +#define TRING_PKT_NONDEFER (0x0002) /* pkt xmitted w/o deferring */ +#define TRING_COLL (0x0004) /* pkt xmitted w collisions */ +#define TRING_CARR (0x0008) /* carrier sense lost */ +#define TRING_UNDERRUN (0x0010) /* DMA underrun */ +#define TRING_HB_COLL (0x0020) /* Collision detect Heartbeat */ +#define TRING_WIN_COLL (0x0040) /* out of window collision */ +#define TRING_DEFERRED (0x0080) /* Deferring */ +#define TRING_COLL_COUNT (0x0F00) /* collision counter (mask) */ +#define TRING_COLL_EXCESS (0x1000) /* tx aborted: excessive colls */ +#define TRING_OWN (0x8000) /* desc ownership bit */ + +/* error summary */ +#define TRING_ABORT (TRING_COLL_EXCESS|TRING_WIN_COLL|TRING_UNDERRUN) +#define TRING_ERROR (TRING_DEFERRED|TRING_WIN_COLL|TRING_UNDERRUN|TRING_CARR/*|TRING_COLL*/ ) + +/* description of the tx descriptors control bits */ +#define TD_FRAGLIST (0x0001) /* Desc points to a fragment list */ +#define TD_LLFORM (0x0002) /* Frag list format */ +#define TD_IAF (0x0004) /* Generate Interrupt after tx */ +#define TD_NOCRC (0x0008) /* No CRC generated */ +#define TD_LASTDESC (0x0010) /* Last desc for this frame */ + +#endif /* _EPIC100_H_ */ diff --git a/netboot/i82586.c b/netboot/i82586.c new file mode 100644 index 000000000..c7cb41579 --- /dev/null +++ b/netboot/i82586.c @@ -0,0 +1,963 @@ +/************************************************************************** +Etherboot - BOOTP/TFTP Bootstrap Program +i82586 NIC driver for Etherboot +Ken Yap, January 1998 +***************************************************************************/ + +/* to get some global routines like printf */ +#include "etherboot.h" +/* to get the interface to the body of the program */ +#include "nic.h" + +/* Sources of information: + + Donald Becker's excellent 3c507 driver in Linux + Intel 82596 data sheet (yes, 82596; it has a 586 compatibility mode) +*/ + +/* Code below mostly stolen wholesale from 3c507.c driver in Linux */ + +/* + Details of the i82586. + + You'll really need the databook to understand the details of this part, + but the outline is that the i82586 has two separate processing units. + Both are started from a list of three configuration tables, of which only + the last, the System Control Block (SCB), is used after reset-time. The SCB + has the following fields: + Status word + Command word + Tx/Command block addr. + Rx block addr. + The command word accepts the following controls for the Tx and Rx units: + */ + +#define CUC_START 0x0100 +#define CUC_RESUME 0x0200 +#define CUC_SUSPEND 0x0300 +#define RX_START 0x0010 +#define RX_RESUME 0x0020 +#define RX_SUSPEND 0x0030 + +/* The Rx unit uses a list of frame descriptors and a list of data buffer + descriptors. We use full-sized (1518 byte) data buffers, so there is + a one-to-one pairing of frame descriptors to buffer descriptors. + + The Tx ("command") unit executes a list of commands that look like: + Status word Written by the 82586 when the command is done. + Command word Command in lower 3 bits, post-command action in upper 3 + Link word The address of the next command. + Parameters (as needed). + + Some definitions related to the Command Word are: + */ +#define CMD_EOL 0x8000 /* The last command of the list, stop. */ +#define CMD_SUSP 0x4000 /* Suspend after doing cmd. */ +#define CMD_INTR 0x2000 /* Interrupt after doing cmd. */ + +enum commands { + CmdNOp = 0, CmdSASetup = 1, CmdConfigure = 2, CmdMulticastList = 3, + CmdTx = 4, CmdTDR = 5, CmdDump = 6, CmdDiagnose = 7}; + +/* + Details of the EtherLink16 Implementation + + The 3c507 and NI5210 are generic shared-memory i82586 implementations. + 3c507: The host can map 16K, 32K, 48K, or 64K of the 64K memory into + 0x0[CD][08]0000, or all 64K into 0xF[02468]0000. + NI5210: The host can map 8k or 16k at 0x[CDE][048C]000 but we + assume 8k because to have 16k you cannot put a ROM on the NIC. + */ + +/* Offsets from the base I/O address. */ + +#ifdef INCLUDE_3C507 + +#define SA_DATA 0 /* Station address data, or 3Com signature. */ +#define MISC_CTRL 6 /* Switch the SA_DATA banks, and bus config bits. */ +#define RESET_IRQ 10 /* Reset the latched IRQ line. */ +#define I82586_ATTN 11 /* Frob the 82586 Channel Attention line. */ +#define ROM_CONFIG 13 +#define MEM_CONFIG 14 +#define IRQ_CONFIG 15 +#define EL16_IO_EXTENT 16 + +/* The ID port is used at boot-time to locate the ethercard. */ +#define ID_PORT 0x100 + +#endif + +#ifdef INCLUDE_NI5210 + +#define NI52_RESET 0 /* writing to this address, resets the i82586 */ +#define I82586_ATTN 1 /* channel attention, kick the 586 */ + +#endif + +#ifdef INCLUDE_EXOS205 + +#define EXOS205_RESET 0 /* writing to this address, resets the i82586 */ +#define I82586_ATTN 1 /* channel attention, kick the 586 */ + +#endif + +/* Offsets to registers in the mailbox (SCB). */ +#define iSCB_STATUS 0x8 +#define iSCB_CMD 0xA +#define iSCB_CBL 0xC /* Command BLock offset. */ +#define iSCB_RFA 0xE /* Rx Frame Area offset. */ + +/* Since the 3c507 maps the shared memory window so that the last byte is +at 82586 address FFFF, the first byte is at 82586 address 0, 16K, 32K, or +48K corresponding to window sizes of 64K, 48K, 32K and 16K respectively. +We can account for this be setting the 'SBC Base' entry in the ISCP table +below for all the 16 bit offset addresses, and also adding the 'SCB Base' +value to all 24 bit physical addresses (in the SCP table and the TX and RX +Buffer Descriptors). + -Mark +*/ + +/* + What follows in 'init_words[]' is the "program" that is downloaded to the + 82586 memory. It's mostly tables and command blocks, and starts at the + reset address 0xfffff6. This is designed to be similar to the EtherExpress, + thus the unusual location of the SCB at 0x0008. + + Even with the additional "don't care" values, doing it this way takes less + program space than initializing the individual tables, and I feel it's much + cleaner. + + The databook is particularly useless for the first two structures, I had + to use the Crynwr driver as an example. + + The memory setup is as follows: +*/ + +#define CONFIG_CMD 0x18 +#define SET_SA_CMD 0x24 +#define SA_OFFSET 0x2A +#define IDLELOOP 0x30 +#define TDR_CMD 0x38 +#define TDR_TIME 0x3C +#define DUMP_CMD 0x40 +#define DIAG_CMD 0x48 +#define SET_MC_CMD 0x4E +#define DUMP_DATA 0x56 /* A 170 byte buffer for dump and Set-MC into. */ + +#define TX_BUF_START 0x0100 +#define TX_BUF_SIZE (1518+14+20+16) /* packet+header+TBD */ + +#define RX_BUF_START 0x1000 +#define RX_BUF_SIZE (1518+14+18) /* packet+header+RBD */ +#define RX_BUF_END (mem_end - mem_start - 20) + +/* + That's it: only 86 bytes to set up the beast, including every extra + command available. The 170 byte buffer at DUMP_DATA is shared between the + Dump command (called only by the diagnostic program) and the SetMulticastList + command. + + To complete the memory setup you only have to write the station address at + SA_OFFSET and create the Tx & Rx buffer lists. + + The Tx command chain and buffer list is setup as follows: + A Tx command table, with the data buffer pointing to... + A Tx data buffer descriptor. The packet is in a single buffer, rather than + chaining together several smaller buffers. + A NoOp command, which initially points to itself, + And the packet data. + + A transmit is done by filling in the Tx command table and data buffer, + re-writing the NoOp command, and finally changing the offset of the last + command to point to the current Tx command. When the Tx command is finished, + it jumps to the NoOp, when it loops until the next Tx command changes the + "link offset" in the NoOp. This way the 82586 never has to go through the + slow restart sequence. + + The Rx buffer list is set up in the obvious ring structure. We have enough + memory (and low enough interrupt latency) that we can avoid the complicated + Rx buffer linked lists by alway associating a full-size Rx data buffer with + each Rx data frame. + + I currently use one transmit buffer starting at TX_BUF_START (0x0100), and + use the rest of memory, from RX_BUF_START to RX_BUF_END, for Rx buffers. + + */ + +static unsigned short init_words[] = { + /* System Configuration Pointer (SCP). */ +#if defined(INCLUDE_3C507) + 0x0000, /* Set bus size to 16 bits. */ +#else + 0x0001, /* Set bus size to 8 bits */ +#endif + 0,0, /* pad words. */ + 0x0000,0x0000, /* ISCP phys addr, set in init_82586_mem(). */ + + /* Intermediate System Configuration Pointer (ISCP). */ + 0x0001, /* Status word that's cleared when init is done. */ + 0x0008,0,0, /* SCB offset, (skip, skip) */ + + /* System Control Block (SCB). */ + 0,0xf000|RX_START|CUC_START, /* SCB status and cmd. */ + CONFIG_CMD, /* Command list pointer, points to Configure. */ + RX_BUF_START, /* Rx block list. */ + 0,0,0,0, /* Error count: CRC, align, buffer, overrun. */ + + /* 0x0018: Configure command. Change to put MAC data with packet. */ + 0, CmdConfigure, /* Status, command. */ + SET_SA_CMD, /* Next command is Set Station Addr. */ + 0x0804, /* "4" bytes of config data, 8 byte FIFO. */ + 0x2e40, /* Magic values, including MAC data location. */ + 0, /* Unused pad word. */ + + /* 0x0024: Setup station address command. */ + 0, CmdSASetup, + SET_MC_CMD, /* Next command. */ + 0xaa00,0xb000,0x0bad, /* Station address (to be filled in) */ + + /* 0x0030: NOP, looping back to itself. Point to first Tx buffer to Tx. */ + 0, CmdNOp, IDLELOOP, 0 /* pad */, + + /* 0x0038: A unused Time-Domain Reflectometer command. */ + 0, CmdTDR, IDLELOOP, 0, + + /* 0x0040: An unused Dump State command. */ + 0, CmdDump, IDLELOOP, DUMP_DATA, + + /* 0x0048: An unused Diagnose command. */ + 0, CmdDiagnose, IDLELOOP, + + /* 0x004E: An empty set-multicast-list command. */ + 0, CmdMulticastList, IDLELOOP, 0, +}; + +/* NIC specific static variables go here */ + +static unsigned short ioaddr, irq, scb_base; +static Address mem_start, mem_end; +static char if_port; +static unsigned short rx_head, rx_tail; + +#define read_mem(m,s) fmemcpy((char *)s, m, sizeof(s)) + +static void setup_rx_buffers(struct nic *nic) +{ + Address write_ptr; + unsigned short cur_rx_buf; + static unsigned short rx_cmd[16] = { + 0x0000, /* Rx status */ + 0x0000, /* Rx command, only and last */ + RX_BUF_START, /* Link (will be adjusted) */ + RX_BUF_START + 22, /* Buffer offset (will be adjusted) */ + 0x0000, 0x0000, 0x0000, /* Pad for dest addr */ + 0x0000, 0x0000, 0x0000, /* Pad for source addr */ + 0x0000, /* Pad for protocol */ + 0x0000, /* Buffer: Actual count */ + -1, /* Buffer: Next (none) */ + RX_BUF_START + 0x20, /* Buffer: Address low (+ scb_base) (will be adjusted) */ + 0x0000, /* Buffer: Address high */ + 0x8000 | (RX_BUF_SIZE - 0x20) + }; + + cur_rx_buf = rx_head = RX_BUF_START; + do { /* While there is room for one more buffer */ + write_ptr = mem_start + cur_rx_buf; + /* adjust some contents */ + rx_cmd[1] = 0x0000; + rx_cmd[2] = cur_rx_buf + RX_BUF_SIZE; + rx_cmd[3] = cur_rx_buf + 22; + rx_cmd[13] = cur_rx_buf + 0x20 + scb_base; +#ifdef ETHERBOOT32 + memcpy((char *)write_ptr, (char *)rx_cmd, sizeof(rx_cmd)); +#endif +#ifdef ETHERBOOT16 + memcpyf(write_ptr, (char *)rx_cmd, sizeof(rx_cmd)); +#endif + rx_tail = cur_rx_buf; + cur_rx_buf += RX_BUF_SIZE; + } while (cur_rx_buf <= RX_BUF_END - RX_BUF_SIZE); + /* Terminate the list by setting the EOL bit and wrap ther pointer + to make the list a ring. */ + write_ptr = mem_start + rx_tail; + rx_cmd[1] = 0xC000; + rx_cmd[2] = rx_head; +#ifdef ETHERBOOT32 + memcpy((char *)write_ptr, (char *)rx_cmd, sizeof(unsigned short) * 3); +#endif +#ifdef ETHERBOOT16 + memcpyf(write_ptr, (char *)rx_cmd, sizeof(unsigned short) * 3); +#endif +} + +static void ack_status(void) +{ + unsigned short cmd, status; +#ifdef ETHERBOOT32 + unsigned short *shmem = (short *)mem_start; +#endif +#ifdef ETHERBOOT16 + unsigned short shmem[CONFIG_CMD>>1]; +#endif + +#ifdef ETHERBOOT16 + read_mem(mem_start, shmem); +#endif + cmd = (status = shmem[iSCB_STATUS>>1]) & 0xf000; + if (status & 0x100) /* CU suspended? */ + cmd |= CUC_RESUME; + if ((status & 0x200) == 0) /* CU not active? */ + cmd |= CUC_START; + if (status & 0x010) /* RU suspended? */ + cmd |= RX_RESUME; + else if ((status & 0x040) == 0) /* RU not active? */ + cmd |= RX_START; + if (cmd == 0) /* Nothing to do */ + return; + shmem[iSCB_CMD>>1] = cmd; +#ifdef ETHERBOOT16 + memcpyf(mem_start, shmem, sizeof(shmem)); +#endif +#if defined(DEBUG) + printf("Status %x Command %x\n", status, cmd); +#endif + outb(0, ioaddr + I82586_ATTN); +} + +/************************************************************************** +RESET - Reset adapter +***************************************************************************/ + +static void i82586_reset(struct nic *nic) +{ + int boguscnt; +#ifdef ETHERBOOT32 + unsigned short *shmem = (short *)mem_start; +#endif +#ifdef ETHERBOOT16 + unsigned short shmem[CONFIG_CMD>>1]; +#endif + + /* put the card in its initial state */ + +#ifdef INCLUDE_3C507 + /* Enable loopback to protect the wire while starting up, + and hold the 586 in reset during the memory initialisation. */ + outb(0x20, ioaddr + MISC_CTRL); +#endif + + /* Fix the ISCP address and base. */ + init_words[3] = scb_base; + init_words[7] = scb_base; + + /* Write the words at 0xfff6. */ + /* Write the words at 0x0000. */ + /* Fill in the station address. */ +#ifdef ETHERBOOT32 + memcpy((char *)(mem_end - 10), (char *)init_words, 10); + memcpy((char *)mem_start, (char *)&init_words[5], sizeof(init_words) - 10); + memcpy((char *)mem_start + SA_OFFSET, nic->node_addr, ETHER_ADDR_SIZE); +#endif +#ifdef ETHERBOOT16 + memcpyf(mem_end - 10, (char *)init_words, 10); + memcpyf(mem_start, (char *)&init_words[5], sizeof(init_words) - 10); + memcpyf(mem_start + SA_OFFSET, nic->node_addr, ETHER_ADDR_SIZE); +#endif + setup_rx_buffers(nic); + +#ifdef INCLUDE_3C507 + /* Start the 586 by releasing the reset line, but leave loopback. */ + outb(0xA0, ioaddr + MISC_CTRL); +#endif + + /* This was time consuming to track down; you need to give two channel + attention signals to reliably start up the i82586. */ + outb(0, ioaddr + I82586_ATTN); + boguscnt = 10000; + while ( +#ifdef ETHERBOOT16 + read_mem(mem_start, shmem), +#endif + shmem[iSCB_STATUS>>1] == 0) + { + if (--boguscnt == 0) + { + printf("i82586 initialisation timed out with status %x, cmd %x\n", + shmem[iSCB_STATUS>>1], shmem[iSCB_CMD>>1]); + break; + } + } + /* Issue channel-attn -- the 82586 won't start. */ + outb(0, ioaddr + I82586_ATTN); + +#ifdef INCLUDE_3C507 + /* Disable loopback. */ + outb(0x80, ioaddr + MISC_CTRL); +#endif +#if defined(DEBUG) +#ifdef ETHERBOOT16 + read_mem(mem_start, shmem); +#endif + printf("i82586 status %x, cmd %x\n", + shmem[iSCB_STATUS>>1], shmem[iSCB_CMD>>1]); +#endif +} + +/************************************************************************** +POLL - Wait for a frame +***************************************************************************/ +static int i82586_poll(struct nic *nic) +{ + int status; + unsigned short rfd_cmd, next_rx_frame, data_buffer_addr, + frame_status, pkt_len; +#ifdef ETHERBOOT32 + unsigned short *shmem = (short *)mem_start + rx_head; +#endif +#ifdef ETHERBOOT16 + unsigned short shmem[16]; +#endif + + /* return true if there's an ethernet packet ready to read */ + if ( +#ifdef ETHERBOOT16 + read_mem(mem_start + rx_head, shmem), +#endif + ((frame_status = shmem[0]) & 0x8000) == 0) + return (0); /* nope */ + rfd_cmd = shmem[1]; + next_rx_frame = shmem[2]; + data_buffer_addr = shmem[3]; + pkt_len = shmem[11]; + status = 0; + if (rfd_cmd != 0 || data_buffer_addr != rx_head + 22 + || (pkt_len & 0xC000) != 0xC000) + printf("\nRx frame corrupt, discarded"); + else if ((frame_status & 0x2000) == 0) + printf("\nRx frame had error"); + else + { + /* We have a frame, copy it to our buffer */ + pkt_len &= 0x3FFF; +#ifdef ETHERBOOT32 + memcpy(nic->packet, (char *)mem_start + rx_head + 0x20, pkt_len); +#endif +#ifdef ETHERBOOT16 + fmemcpy(nic->packet, mem_start + rx_head + 0x20, pkt_len); +#endif + /* Only packets not from ourself */ + if (memcmp(nic->packet + ETHER_ADDR_SIZE, nic->node_addr, ETHER_ADDR_SIZE) != 0) + { + nic->packetlen = pkt_len; + status = 1; + } + } + /* Clear the status word and set EOL on Rx frame */ + shmem[0] = 0; + shmem[1] = 0xC000; +#ifdef ETHERBOOT16 + memcpyf(mem_start + rx_head, shmem, sizeof(unsigned short) * 2); + /* Clear the EOL on previous RFD */ + memcpyf(mem_start + rx_tail + 2, shmem, sizeof(unsigned short)); +#endif +#ifdef ETHERBOOT32 + *(short *)(mem_start + rx_tail + 2) = 0; +#endif + rx_tail = rx_head; + rx_head = next_rx_frame; + ack_status(); + return (status); +} + +/************************************************************************** +TRANSMIT - Transmit a frame +***************************************************************************/ +static void i82586_transmit( +struct nic *nic, +char *d, /* Destination */ +unsigned int t, /* Type */ +unsigned int s, /* size */ +char *p) /* Packet */ +{ + Address bptr; + unsigned short type, z; + static unsigned short tx_cmd[11] = { + 0x0, /* Tx status */ + CmdTx, /* Tx command */ + TX_BUF_START+16, /* Next command is a NoOp */ + TX_BUF_START+8, /* Data Buffer offset */ + 0x8000, /* | with size */ + 0xffff, /* No next data buffer */ + TX_BUF_START+22, /* + scb_base */ + 0x0, /* Buffer address high bits (always zero) */ + 0x0, /* Nop status */ + CmdNOp, /* Nop command */ + TX_BUF_START+16 /* Next is myself */ + }; +#ifdef ETHERBOOT32 + unsigned short *shmem = (short *)mem_start + TX_BUF_START; +#endif +#ifdef ETHERBOOT16 + unsigned short shmem[11]; +#endif + + /* send the packet to destination */ + /* adjust some contents */ + type = htons(t); + if (s < ETH_MIN_PACKET) + s = ETH_MIN_PACKET; + tx_cmd[4] = (s + ETHER_HDR_SIZE) | 0x8000; + tx_cmd[6] = TX_BUF_START + 22 + scb_base; + bptr = mem_start + TX_BUF_START; +#ifdef ETHERBOOT32 + memcpy((char *)bptr, (char *)tx_cmd, sizeof(tx_cmd)); + bptr += sizeof(tx_cmd); + memcpy((char *)bptr, d, ETHER_ADDR_SIZE); + bptr += ETHER_ADDR_SIZE; + memcpy((char *)bptr, nic->node_addr, ETHER_ADDR_SIZE); + bptr += ETHER_ADDR_SIZE; + memcpy((char *)bptr, (char *)&type, sizeof(type)); + bptr += sizeof(type); + memcpy((char *)bptr, p, s); + /* Change the offset in the IDLELOOP */ + *(unsigned short *)(mem_start + IDLELOOP + 4) = TX_BUF_START; +#endif +#ifdef ETHERBOOT16 + memcpyf(bptr, (char *)tx_cmd, sizeof(tx_cmd)); + bptr += sizeof(tx_cmd); + memcpyf(bptr, d, ETHER_ADDR_SIZE); + bptr += ETHER_ADDR_SIZE; + memcpyf(bptr, nic->node_addr, ETHER_ADDR_SIZE); + bptr += ETHER_ADDR_SIZE; + memcpyf(bptr, (char *)&type, sizeof(type)); + bptr += sizeof(type); + memcpyf(bptr, p, s); + /* Change the offset in the IDLELOOP */ + z = TX_BUF_START; + memcpyf(mem_start + IDLELOOP + 4, (char *)&z, sizeof(z)); +#endif + /* Wait for transmit completion */ + while ( +#ifdef ETHERBOOT16 + read_mem(mem_start + TX_BUF_START, shmem), +#endif + (shmem[0] & 0x2000) == 0) + ; + /* Change the offset in the IDLELOOP back and + change the final loop to point here */ +#ifdef ETHERBOOT32 + *(unsigned short *)(mem_start + IDLELOOP + 4) = IDLELOOP; + *(unsigned short *)(mem_start + TX_BUF_START + 20) = IDLELOOP; +#endif +#ifdef ETHERBOOT16 + z = IDLELOOP; + memcpyf(mem_start + IDLELOOP + 4, (char *)&z, sizeof(z)); + memcpyf(mem_start + TX_BUF_START + 20, (char *)&z, sizeof(z)); +#endif + ack_status(); +} + +/************************************************************************** +DISABLE - Turn off ethernet interface +***************************************************************************/ +static void i82586_disable(struct nic *nic) +{ +#ifdef ETHERBOOT32 + unsigned short *shmem = (short *)mem_start; +#endif +#ifdef ETHERBOOT16 + unsigned short shmem[CONFIG_CMD>>1]; +#endif + +#if 0 + /* Flush the Tx and disable Rx. */ +#ifdef ETHERBOOT16 + read_mem(mem_start, shmem); +#endif + shmem[iSCB_CMD>>1] = RX_SUSPEND | CUC_SUSPEND; +#ifdef ETHERBOOT16 + memcpyf(mem_start, shmem, sizeof(shmem)); +#endif + outb(0, ioaddr + I82586_ATTN); +#ifdef INCLUDE_NI5210 + outb(0, ioaddr + NI52_RESET); +#endif +#endif /* 0 */ +} + +#ifdef INCLUDE_3C507 + +static int t507_probe1(struct nic *nic, unsigned short ioaddr) +{ + int i; + Address size; + char mem_config; + + if (inb(ioaddr) != '*' || inb(ioaddr+1) != '3' + || inb(ioaddr+2) != 'C' || inb(ioaddr+3) != 'O') + return (0); + irq = inb(ioaddr + IRQ_CONFIG) & 0x0f; + mem_config = inb(ioaddr + MEM_CONFIG); + if (mem_config & 0x20) + { + size = 65536L; + mem_start = 0xf00000L + (mem_config & 0x08 ? 0x080000L + : (((Address)mem_config & 0x3) << 17)); + } + else + { + size = ((((Address)mem_config & 0x3) + 1) << 14) & 0xffffL; + mem_start = 0x0c0000L + (((Address)mem_config & 0x18) << 12); + } + mem_end = mem_start + size; + scb_base = 65536L - size; + if_port = inb(ioaddr + ROM_CONFIG) & 0x80; + printf("\n3c507 ioaddr 0x%x, IRQ %d, mem [0x%X-0x%X], %sternal xcvr, addr ", + ioaddr, irq, mem_start, mem_end, if_port ? "in" : "ex"); + /* Get station address */ + outb(0x01, ioaddr + MISC_CTRL); + for (i = 0; i < ETHER_ADDR_SIZE; ++i) + { + printf("%b", nic->node_addr[i] = inb(ioaddr+i)); + if (i < ETHER_ADDR_SIZE -1) + printf(":"); + } + putchar('\n'); + return (1); +} + +/************************************************************************** +PROBE - Look for an adapter, this routine's visible to the outside +***************************************************************************/ + +struct nic *t507_probe(struct nic *nic, unsigned short *probe_addrs) +{ + static unsigned char init_ID_done = 0; + unsigned short lrs_state = 0xff; + static unsigned short io_addrs[] = { 0x300, 0x320, 0x340, 0x280, 0 }; + unsigned short *p; + int i; + + if (init_ID_done == 0) + { + /* Send the ID sequence to the ID_PORT to enable the board */ + outb(0x00, ID_PORT); + for (i = 0; i < 255; ++i) + { + outb(lrs_state, ID_PORT); + lrs_state <<= 1; + if (lrs_state & 0x100) + lrs_state ^= 0xe7; + } + outb(0x00, ID_PORT); + init_ID_done = 1; + } + /* if probe_addrs is 0, then routine can use a hardwired default */ + if (probe_addrs == 0) + probe_addrs = io_addrs; + for (p = probe_addrs; (ioaddr = *p) != 0; ++p) + if (t507_probe1(nic, ioaddr)) + break; + if (ioaddr != 0) + { + /* point to NIC specific routines */ + i82586_reset(nic); + nic->reset = i82586_reset; + nic->poll = i82586_poll; + nic->transmit = i82586_transmit; + nic->disable = i82586_disable; + return nic; + } + /* else */ + { + return 0; + } +} + +#endif + +#ifdef INCLUDE_NI5210 + +static int ni5210_probe2(void) +{ + unsigned short i; + unsigned short shmem[10]; + + /* Fix the ISCP address and base. */ + init_words[3] = scb_base; + init_words[7] = scb_base; + + /* Write the words at 0xfff6. */ + /* Write the words at 0x0000. */ +#ifdef ETHERBOOT32 + memcpy((char *)(mem_end - 10), (char *)init_words, 10); + memcpy((char *)mem_start, (char *)&init_words[5], sizeof(init_words) - 10); + if (*(unsigned short *)mem_start != 1) + return (0); +#endif +#ifdef ETHERBOOT16 + memcpyf(mem_end - 10, (char *)init_words, 10); + memcpyf(mem_start, (char *)&init_words[5], sizeof(init_words) - 10); + fmemcpy((char *)&i, mem_start, sizeof(unsigned short)); + if (i != 1) + return (0); +#endif + outb(0, ioaddr + NI52_RESET); + outb(0, ioaddr + I82586_ATTN); + i = 50; + while ( +#ifdef ETHERBOOT16 + read_mem(mem_start, shmem), +#endif + shmem[iSCB_STATUS>>1] == 0) + { + if (--i == 0) + { + printf("i82586 initialisation timed out with status %x, cmd %x\n", + shmem[iSCB_STATUS>>1], shmem[iSCB_CMD>>1]); + break; + } + } + /* Issue channel-attn -- the 82586 won't start. */ + outb(0, ioaddr + I82586_ATTN); +#ifdef ETHERBOOT32 + if (*(unsigned short *)mem_start != 0) + return (0); +#endif +#ifdef ETHERBOOT16 + fmemcpy((char *)&i, mem_start, sizeof(unsigned short)); + if (i != 0) + return (0); +#endif + return (1); +} + +static int ni5210_probe1(struct nic *nic) +{ + int i; + static Address mem_addrs[] = { + 0xc0000, 0xc4000, 0xc8000, 0xcc000, + 0xd0000, 0xd4000, 0xd8000, 0xdc000, + 0xe0000, 0xe4000, 0xe8000, 0xec000, + 0 }; + Address *p; + + if (inb(ioaddr + 6) != 0x0 || inb(ioaddr + 7) != 0x55) + return (0); + scb_base = -8192; /* assume 8k memory */ + for (p = mem_addrs; (mem_start = *p) != 0; ++p) + if (mem_end = mem_start + 8192, ni5210_probe2()) + break; + if (mem_start == 0) + return (0); + printf("\nNI5210 ioaddr 0x%x, mem [0x%X-0x%X], addr ", + ioaddr, mem_start, mem_end); + /* Get station address */ + for (i = 0; i < ETHER_ADDR_SIZE; ++i) + { + printf("%b", nic->node_addr[i] = inb(ioaddr+i)); + if (i < ETHER_ADDR_SIZE -1) + printf(":"); + } + putchar('\n'); + return (1); +} + +struct nic *ni5210_probe(struct nic *nic, unsigned short *probe_addrs) +{ + /* missing entries are addresses usually already used */ + static unsigned short io_addrs[] = { + 0x200, 0x208, 0x210, 0x218, 0x220, 0x228, 0x230, 0x238, + 0x240, 0x248, 0x250, 0x258, 0x260, 0x268, 0x270, /*Par*/ + 0x280, 0x288, 0x290, 0x298, 0x2A0, 0x2A8, 0x2B0, 0x2B8, + 0x2C0, 0x2C8, 0x2D0, 0x2D8, 0x2E0, 0x2E8, 0x2F0, /*Ser*/ + 0x300, 0x308, 0x310, 0x318, 0x320, 0x328, 0x330, 0x338, + 0x340, 0x348, 0x350, 0x358, 0x360, 0x368, 0x370, /*Par*/ + 0x380, 0x388, 0x390, 0x398, 0x3A0, 0x3A8, /*Vid,Par*/ + 0x3C0, 0x3C8, 0x3D0, 0x3D8, 0x3E0, 0x3E8, /*Ser*/ + 0x0 + }; + unsigned short *p; + int i; + + /* if probe_addrs is 0, then routine can use a hardwired default */ + if (probe_addrs == 0) + probe_addrs = io_addrs; + for (p = probe_addrs; (ioaddr = *p) != 0; ++p) + if (ni5210_probe1(nic)) + break; + if (ioaddr != 0) + { + /* point to NIC specific routines */ + i82586_reset(nic); + nic->reset = i82586_reset; + nic->poll = i82586_poll; + nic->transmit = i82586_transmit; + nic->disable = i82586_disable; + return nic; + } + /* else */ + { + return 0; + } +} +#endif + +#ifdef INCLUDE_EXOS205 + +/* + * Code to download to I186 in EXOS205 + */ + +static unsigned char exos_i186_init[] = +{ +0x08,0x00,0x14,0x00,0x00,0x00,0xaa,0xfa,0x33,0xc0,0xba,0xfe,0xff,0xef,0xb8,0xf8, +0xff,0xe7,0xa0,0xb8,0x7c,0x00,0xe7,0xa4,0xb8,0xbc,0x80,0xe7,0xa8,0x8c,0xc8,0x8e, +0xd8,0xbb,0x2f,0x0e,0xc6,0x07,0xa5,0x33,0xc9,0xeb,0x00,0xeb,0x00,0xeb,0x00,0xe2, +0xf8,0xbe,0x2c,0x0e,0xba,0x02,0x05,0x33,0xdb,0xb9,0x03,0x00,0xec,0x24,0x0f,0x8a, +0xe0,0x02,0xd8,0x42,0x42,0xec,0x02,0xd8,0xd0,0xe0,0xd0,0xe0,0xd0,0xe0,0xd0,0xe0, +0x0a,0xc4,0x88,0x04,0x42,0x42,0x46,0xe2,0xe3,0x8a,0xe3,0xd0,0xec,0xd0,0xec,0xd0, +0xec,0xd0,0xec,0x80,0xe3,0x0f,0x02,0xe3,0x80,0xf4,0x05,0xec,0x3a,0xe0,0x74,0x05, +0xc6,0x04,0x5a,0xeb,0xfe,0xc6,0x04,0x55,0x33,0xc0,0x8e,0xd8,0xbe,0x38,0x00,0xc7, +0x04,0xce,0x0e,0x46,0x46,0xc7,0x04,0x00,0xff,0xfb,0xba,0x3c,0x00,0xb8,0x03,0x00, +0xef,0x33,0xdb,0x33,0xc9,0xbd,0x04,0x0f,0x90,0x90,0x90,0x90,0xe2,0xfa,0x43,0x2e, +0x89,0x5e,0x00,0xeb,0xf3,0x52,0xba,0x00,0x06,0xef,0x50,0x53,0x55,0xbd,0xf8,0x0e, +0x2e,0x8b,0x5e,0x00,0x43,0x2e,0x89,0x5e,0x00,0xba,0x22,0x00,0xb8,0x00,0x80,0xef, +0x5d,0x5b,0x58,0x5a,0xcf,0x49,0x4e,0x54,0x52,0x20,0x63,0x6e,0x74,0x2d,0x3e,0x00, +0x00,0x4c,0x4f,0x4f,0x50,0x20,0x63,0x6e,0x74,0x2d,0x3e,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xea,0x30,0x0e,0x00,0xff,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,00 +}; + +/* These offsets are from the end of the i186 download code */ + +#define OFFSET_SEMA 0x1D1 +#define OFFSET_ADDR 0x1D7 + +static int exos205_probe2(void) +{ + unsigned short i; + unsigned short shmem[10]; + + /* Fix the ISCP address and base. */ + init_words[3] = scb_base; + init_words[7] = scb_base; + + /* Write the words at 0xfff6. */ + /* Write the words at 0x0000. */ +#ifdef ETHERBOOT32 + memcpy((char *)(mem_end - 10), (char *)init_words, 10); + memcpy((char *)mem_start, (char *)&init_words[5], sizeof(init_words) - 10); + if (*(unsigned short *)mem_start != 1) + return (0); +#endif +#ifdef ETHERBOOT16 + memcpyf(mem_end - 10, (char *)init_words, 10); + memcpyf(mem_start, (char *)&init_words[5], sizeof(init_words) - 10); + fmemcpy((char *)&i, mem_start, sizeof(unsigned short)); + if (i != 1) + return (0); +#endif + outb(0, ioaddr + EXOS205_RESET); + outb(0, ioaddr + I82586_ATTN); + i = 50; + while ( +#ifdef ETHERBOOT16 + read_mem(mem_start, shmem), +#endif + shmem[iSCB_STATUS>>1] == 0) + { + if (--i == 0) + { + printf("i82586 initialisation timed out with status %x, cmd %x\n", + shmem[iSCB_STATUS>>1], shmem[iSCB_CMD>>1]); + break; + } + } + /* Issue channel-attn -- the 82586 won't start. */ + outb(0, ioaddr + I82586_ATTN); +#ifdef ETHERBOOT32 + if (*(unsigned short *)mem_start != 0) + return (0); +#endif +#ifdef ETHERBOOT16 + fmemcpy((char *)&i, mem_start, sizeof(unsigned short)); + if (i != 0) + return (0); +#endif + return (1); +} + +static int exos205_probe1(struct nic *nic) +{ + int i; + /* If you know the other addresses please let me know */ + static Address mem_addrs[] = { + 0xcc000, 0 }; + Address *p; + + scb_base = -16384; /* assume 8k memory */ + for (p = mem_addrs; (mem_start = *p) != 0; ++p) + if (mem_end = mem_start + 16384, exos205_probe2()) + break; + if (mem_start == 0) + return (0); + printf("\nEXOS205 ioaddr 0x%x, mem [0x%X-0x%X], addr ", + ioaddr, mem_start, mem_end); + /* Get station address */ + for (i = 0; i < ETHER_ADDR_SIZE; ++i) + { + printf("%b", nic->node_addr[i] = inb(ioaddr+i)); + if (i < ETHER_ADDR_SIZE -1) + printf(":"); + } + putchar('\n'); + return (1); +} + +struct nic *exos205_probe(struct nic *nic, unsigned short *probe_addrs) +{ + /* If you know the other addresses, please let me know */ + static unsigned short io_addrs[] = { + 0x310, 0x0 + }; + unsigned short *p; + int i; + + /* if probe_addrs is 0, then routine can use a hardwired default */ + if (probe_addrs == 0) + probe_addrs = io_addrs; + for (p = probe_addrs; (ioaddr = *p) != 0; ++p) + if (exos205_probe1(nic)) + break; + if (ioaddr != 0) + { + /* point to NIC specific routines */ + i82586_reset(nic); + nic->reset = i82586_reset; + nic->poll = i82586_poll; + nic->transmit = i82586_transmit; + nic->disable = i82586_disable; + return nic; + } + /* else */ + { + return 0; + } +} + +#endif diff --git a/netboot/ntulip.c b/netboot/ntulip.c new file mode 100644 index 000000000..cecbe065c --- /dev/null +++ b/netboot/ntulip.c @@ -0,0 +1,590 @@ +/* + Tulip and clone Etherboot Driver + By Marty Connor (mdc@thinguin.org) + This software may be used and distributed according to the terms + of the GNU Public License, incorporated herein by reference. + + Based on Ken Yap's Tulip Etherboot Driver and Donald Becker's + Linux Tulip Driver. Supports N-Way speed auto-configuration on + MX98715, MX98715A and MX98725. Support inexpensive PCI 10/100 cards + based on the Macronix MX987x5 chip, such as the SOHOware Fast + model SFA110A, and the LinkSYS model LNE100TX. The NetGear + model FA310X, based on the LC82C168 chip is also supported. + + Documentation and source code used: + Source for Etherboot driver at + http://www.slug.org.au/etherboot/ + MX98715A Data Sheet and MX98715A Application Note + on http://www.macronix.com/ (PDF format files) + Source for Linux tulip driver at + http://cesdis.gsfc.nasa.gov/linux/drivers/tulip.html + + Adapted by Ken Yap from + FreeBSD netboot DEC 21143 driver + Author: David Sharp + date: Nov/98 + + Some code fragments were taken from verious places, Ken Yap's + etherboot, FreeBSD's if_de.c, and various Linux related files. + DEC's manuals for the 21143 and SROM format were very helpful. + The Linux de driver development page has a number of links to + useful related information. Have a look at: + ftp://cesdis.gsfc.nasa.gov/pub/linux/drivers/tulip-devel.html +*/ + +/*********************************************************************/ +/* Revision History */ +/*********************************************************************/ + +/* + 11 Jan 2000 mdc 0.75b4 + Added support for NetGear FA310TX card based on the LC82C168 + chip. This should also support Lite-On LC82C168 boards. + Added simple MII support. Re-arranged code to better modularize + initializations. + 04 Dec 1999 mdc 0.75b3 + Added preliminary support for LNE100TX PCI cards. Should work for + PNIC2 cards. No MII support, but single interface (RJ45) tulip + cards seem to not care. + 03 Dec 1999 mdc 0.75b2 + Renamed from mx987x5 to ntulip, merged in original tulip init code + from tulip.c to support other tulip compatible cards. + 02 Dec 1999 mdc 0.75b1 + Released Beta MX987x5 Driver for code review and testing to netboot + and thinguin mailing lists. +*/ + + +#include "etherboot.h" +#include "nic.h" +#include "pci.h" + +/*********************************************************************/ +/* Declarations */ +/*********************************************************************/ + +typedef unsigned char u8; +typedef signed char s8; +typedef unsigned short u16; +typedef signed short s16; +typedef unsigned int u32; +typedef signed int s32; + +/* Register offsets for tulip device */ +enum ntulip_offsets { + CSR0=0, CSR1=0x08, CSR2=0x10, CSR3=0x18, CSR4=0x20, CSR5=0x28, + CSR6=0x30, CSR7=0x38, CSR8=0x40, CSR9=0x48, CSR10=0x50, CSR11=0x58, + CSR12=0x60, CSR13=0x68, CSR14=0x70, CSR15=0x78, CSR16=0x80, CSR20=0xA0 +}; + +/* EEPROM Address width definitions */ +#define EEPROM_ADDRLEN 6 +#define EEPROM_SIZE 128 /* 2 << EEPROM_ADDRLEN */ + +/* Data Read from the EEPROM */ +static unsigned char ee_data[EEPROM_SIZE]; + +/* The EEPROM commands include the alway-set leading bit. */ +#define EE_WRITE_CMD (5 << addr_len) +#define EE_READ_CMD (6 << addr_len) +#define EE_ERASE_CMD (7 << addr_len) + +/* EEPROM_Ctrl bits. */ +#define EE_SHIFT_CLK 0x02 /* EEPROM shift clock. */ +#define EE_CS 0x01 /* EEPROM chip select. */ +#define EE_DATA_WRITE 0x04 /* EEPROM chip data in. */ +#define EE_WRITE_0 0x01 +#define EE_WRITE_1 0x05 +#define EE_DATA_READ 0x08 /* EEPROM chip data out. */ +#define EE_ENB (0x4800 | EE_CS) + +/* Delay between EEPROM clock transitions. Even at 33Mhz current PCI + implementations don't overrun the EEPROM clock. We add a bus + turn-around to insure that this remains true. */ +#define eeprom_delay() inl(ee_addr) + +/* helpful macro if on a big_endian machine for changing byte order. + not strictly needed on Intel */ +#define le16_to_cpu(val) (val) + +/* Calibration constant for udelay loop. Very approximate */ +#define UADJUST 870 + +/* transmit and receive descriptor format */ +struct txrxdesc { + unsigned long status; /* owner, status */ + unsigned long buf1sz:11, /* size of buffer 1 */ + buf2sz:11, /* size of buffer 2 */ + control:10; /* control bits */ + unsigned char *buf1addr; /* buffer 1 address */ + unsigned char *buf2addr; /* buffer 2 address */ +}; + +/* Size of transmit and receive buffers */ +#define BUFLEN 1600 + +/* Note: transmit and receive buffers must be longword aligned and + longword divisable */ + +/* transmit descriptor and buffer */ +static struct txrxdesc txd; +static unsigned char txb[BUFLEN]; + +/* receive descriptor(s) and buffer(s) */ +#define NRXD 4 +static struct txrxdesc rxd[NRXD]; +static int rxd_tail = 0; +static unsigned char rxb[NRXD][BUFLEN]; + +/* PCI Bus parameters */ +static unsigned short vendor, dev_id; +static unsigned long ioaddr; +static unsigned long devfn; + +/* buffer for ethernet header */ +static unsigned char ehdr[ETHER_HDR_SIZE]; + +/* MIIs found */ +static int mii_cnt; + +/* Temporary CSR6 storage */ +static int csr6; + + +/*********************************************************************/ +/* Utility Routines */ +/*********************************************************************/ + +#undef NTULIP_DEBUG + +#undef NTULIP_DEBUG_WHERE +static void inline whereami (char *str) +{ +#ifdef NTULIP_DEBUG_WHERE + printf("%s\n", str); + // sleep(2); +#endif +} + +/*********************************************************************/ +/* Delay Code */ +/*********************************************************************/ +/* Assumes 33MHz PCI bus. This is not very accurate and should be used + only with gross over estimations of required delay times unless + UADJUST is tuned to your specific processor and I/O subsystem. */ +static void udelay(unsigned long usec) +{ + unsigned long i; + for (i=((usec*UADJUST)/33)+1; i>0; i--) + (void) inl(ioaddr + CSR0); +} + + +/*********************************************************************/ +/* Media Descriptor Code */ +/*********************************************************************/ +static int lc82c168_mdio_read(int phy_id, int location) +{ + int i = 1000; + int read_cmd = (0xf6 << 10) | (phy_id << 5) | location; + int retval = 0; + + whereami("mdio_read\n"); + + outl(0x60020000 | (phy_id<<23) | (location<<18), ioaddr + 0xA0); + inl(ioaddr + 0xA0); + inl(ioaddr + 0xA0); + while (--i > 0) + if ( ! ((retval = inl(ioaddr + 0xA0)) & 0x80000000)) + return retval & 0xffff; + if (i == 0) printf("mdio read timeout!\n"); + return 0xffff; +} + +static void lc82c168_mdio_write(int phy_id, int location, int value) +{ + int i = 1000; + int cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value; + + whereami("mdio_write\n"); + + outl(cmd, ioaddr + 0xA0); + do + if ( ! (inl(ioaddr + 0xA0) & 0x80000000)) + break; + while (--i > 0); + if (i == 0) printf("mdio write timeout!\n"); + return; +} + +static void lc82c168_do_mii() +{ + int phy, phy_idx; + + whereami("do_mii\n"); + + mii_cnt = 0; + for (phy = 0, phy_idx = 0; phy < 32 && phy_idx < 4; phy++) { + + int mii_status = lc82c168_mdio_read(phy, 1); + + if ((mii_status & 0x8301) == 0x8001 || + ((mii_status & 0x8000) == 0 && (mii_status & 0x7800) != 0)) { + + int mii_reg0 = lc82c168_mdio_read(phy, 0); + int mii_advert = lc82c168_mdio_read(phy, 4); + int mii_reg4 = ((mii_status >> 6) & 0x01E1) | 1; + + phy_idx++; + printf("%s: MII trcvr #%d " + "config %x status %x advertising %x reg4 %x.\n", + "LC82C168", phy, mii_reg0, mii_status, mii_advert, mii_reg4); + + lc82c168_mdio_write(phy, 0, mii_reg0 | 0x1000); + if (mii_advert != mii_reg4) + lc82c168_mdio_write(phy, 4, mii_reg4); + } + } + mii_cnt = phy_idx; + +#ifdef NTULIP_DEBUG + printf("mii_cnt = %d\n", mii_cnt); +#endif + +} + + +/*********************************************************************/ +/* EEPROM Reading Code */ +/*********************************************************************/ +/* EEPROM routines adapted from the Linux Tulip Code */ +/* Reading a serial EEPROM is a "bit" grungy, but we work our way + through:->. +*/ +static int read_eeprom(unsigned long ioaddr, int location, int addr_len) +{ + int i; + unsigned short retval = 0; + long ee_addr = ioaddr + CSR9; + int read_cmd = location | EE_READ_CMD; + + whereami("read_eeprom\n"); + + outl(EE_ENB & ~EE_CS, ee_addr); + outl(EE_ENB, ee_addr); + + /* Shift the read command bits out. */ + for (i = 4 + addr_len; i >= 0; i--) { + short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0; + outl(EE_ENB | dataval, ee_addr); + eeprom_delay(); + outl(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr); + eeprom_delay(); + } + outl(EE_ENB, ee_addr); + + for (i = 16; i > 0; i--) { + outl(EE_ENB | EE_SHIFT_CLK, ee_addr); + eeprom_delay(); + retval = (retval << 1) | ((inl(ee_addr) & EE_DATA_READ) ? 1 : 0); + outl(EE_ENB, ee_addr); + eeprom_delay(); + } + + /* Terminate the EEPROM access. */ + outl(EE_ENB & ~EE_CS, ee_addr); + return retval; +} + +/*********************************************************************/ +/* ntulip_init_ring - setup the tx and rx descriptors */ +/*********************************************************************/ +static void ntulip_init_ring(struct nic *nic) +{ + int i; + + /* setup the transmit descriptor */ + memset(&txd, 0, sizeof(struct txrxdesc)); + txd.buf1addr = &txb[0]; + txd.buf2addr = &txb[0]; /* just in case */ + txd.buf1sz = 192; /* setup packet must be 192 bytes */ + txd.buf2sz = 0; + txd.control = 0x020; /* setup packet */ + txd.status = 0x80000000; /* give ownership to device */ + + /* construct perfect filter frame with mac address as first match + and broadcast address for all others */ + for (i=0; i<192; i++) txb[i] = 0xff; + txb[0] = nic->node_addr[0]; + txb[1] = nic->node_addr[1]; + txb[4] = nic->node_addr[2]; + txb[5] = nic->node_addr[3]; + txb[8] = nic->node_addr[4]; + txb[9] = nic->node_addr[5]; + + /* setup receive descriptor */ + for (i=0; inode_addr, ETHER_ADDR_SIZE); + ehdr[ETHER_ADDR_SIZE*2] = (t >> 8) & 0xff; + ehdr[ETHER_ADDR_SIZE*2+1] = t & 0xff; + + /* setup the transmit descriptor */ + memset(&txd, 0, sizeof(struct txrxdesc)); + txd.buf1addr = &ehdr[0]; /* ethernet header */ + txd.buf1sz = ETHER_HDR_SIZE; + txd.buf2addr = p; /* packet to transmit */ + txd.buf2sz = s; + txd.control = 0x00000188; /* LS+FS+TER */ + txd.status = 0x80000000; /* give it the device */ + + /* Point to transmit descriptor */ + outl((unsigned long)&txd, ioaddr + CSR4); + + /* Start Tx */ + outl(inl(ioaddr + CSR6) | 0x00002000, ioaddr + CSR6); + udelay(300); +} + +/*********************************************************************/ +/* eth_poll - Wait for a frame */ +/*********************************************************************/ +static int ntulip_poll(struct nic *nic) +{ + int x; + + whereami("ntulip_poll\n"); + + if (rxd[rxd_tail].status & 0x80000000) + return 0; + + whereami("ntulip_poll got one\n"); + + nic->packetlen = (rxd[rxd_tail].status & 0x3FFF0000) >> 16; + + /* copy packet to working buffer */ + /* XXX - this copy could be avoided with a little more work + but for now we are content with it because the optimised + memcpy is quite fast */ + + memcpy(nic->packet, &rxb[rxd_tail][0], nic->packetlen); + + /* return the descriptor and buffer to receive ring */ + rxd[rxd_tail].status = 0x80000000; + rxd_tail++; + if (rxd_tail == NRXD) rxd_tail = 0; + + return 1; +} + +/*********************************************************************/ +/* eth_disable - Disable the interface */ +/*********************************************************************/ +static void ntulip_disable(struct nic *nic) +{ + whereami("ntulip_disable\n"); + + /* disable interrupts */ + outl(0x00000000, ioaddr + CSR7); + + /* Stop the chip's Tx and Rx processes. */ + outl(inl(ioaddr + CSR6) & ~0x00002002, ioaddr + CSR6); + + /* Clear the missed-packet counter. */ + (volatile unsigned long)inl(ioaddr + CSR8); +} + +/*********************************************************************/ +/* eth_probe - Look for an adapter */ +/*********************************************************************/ +struct nic *ntulip_probe(struct nic *nic, unsigned short *io_addrs, + struct pci_device *pci) +{ + int i; + + whereami("ntulip_probe\n"); + + if (io_addrs == 0 || *io_addrs == 0) + return 0; + + vendor = pci->vendor; + dev_id = pci->dev_id; + ioaddr = *io_addrs; + + /* wakeup chip */ + pcibios_write_config_dword(0, pci->devfn, 0x40, 0x00000000); + + /* Stop the chip's Tx and Rx processes. */ + outl(inl(ioaddr + CSR6) & ~0x00002002, ioaddr + CSR6); + + /* Clear the missed-packet counter. */ + (volatile unsigned long)inl(ioaddr + CSR8); + + /* Get MAC Address */ + + /* Hardware Address retrieval method for LC82C168 */ + if (vendor == PCI_VENDOR_ID_LINKSYS && dev_id == PCI_DEVICE_ID_DEC_TULIP) { + for (i = 0; i < 3; i++) { + int value, boguscnt = 100000; + outl(0x600 | i, ioaddr + 0x98); + do + value = inl(ioaddr + CSR9); + while (value < 0 && --boguscnt > 0); + nic->node_addr[i*2] = (u8)((value >> 8) & 0xff); + nic->node_addr[i*2 + 1] = (u8)( value & 0xff); + } + printf("NTulip %b:%b:%b:%b:%b:%b at ioaddr 0x%x\n", + nic->node_addr[0],nic->node_addr[1],nic->node_addr[2],nic->node_addr[3], + nic->node_addr[4],nic->node_addr[5],ioaddr); + + } else { + + /* read EEPROM data */ + for (i = 0; i < sizeof(ee_data)/2; i++) + ((unsigned short *)ee_data)[i] = + le16_to_cpu(read_eeprom(ioaddr, i, EEPROM_ADDRLEN)); + + /* extract MAC address from EEPROM buffer */ + for (i=0; i<6; i++) + nic->node_addr[i] = ee_data[20+i]; + + printf("NTulip %b:%b:%b:%b:%b:%b at ioaddr 0x%x\n", + nic->node_addr[0],nic->node_addr[1],nic->node_addr[2],nic->node_addr[3], + nic->node_addr[4],nic->node_addr[5],ioaddr); + } + + /* initialize device */ + ntulip_reset(nic); + + nic->reset = ntulip_reset; + nic->poll = ntulip_poll; + nic->transmit = ntulip_transmit; + nic->disable = ntulip_disable; + + return nic; +} diff --git a/netboot/ntulip.txt b/netboot/ntulip.txt new file mode 100644 index 000000000..cb192fd20 --- /dev/null +++ b/netboot/ntulip.txt @@ -0,0 +1,48 @@ +This software may be used and distributed according to the terms of +the GNU Public License, incorporated herein by reference. + +Of course you have to have all the usual Etherboot environment +(bootp/dhcp/NFS) set up, and you need a Linux kernel with v0.91g +(7.16.99) or later of the tulip.c driver compiled in to support some +MX98715 based cards. That file is available at: + + http://cesdis.gsfc.nasa.gov/linux/drivers/test/tulip.c + +See the comments at the beginning of ntulip.c for more information +on the code. + +NOTES + +I've tested this driver with a SOHOware Fast 10/100 Model SDA110A, +a Linksys LNE100TX v2.0, and a Netgear FA310TX card, and it worked at +both 10 and 100 mbits. Other cards based on the tulip family may work as +well. + +These cards are about 20$US, are supported by Linux and now Etherboot, +and being PCI, they auto-configure IRQ and IOADDR and auto-negotiate +10/100 half/full duplex. It seems like a pretty good value compared to +some of the pricier cards, and can lower the cost of building/adapting +thin client workstations substantially while giving a considerable +performance increase. + +On some PCI tulip clone chipsets (MX987x5, LC82C115, LC82C168) this driver +lets the card choose the fastest speed it can negotiate with the peer +device. On other cards, it chooses 10mbit half-duplex. + +I burned an AM27C256 (32KByte) EPROM with mx987x5.lzrom and it worked. +According to the data sheet the MX98715A supports up to 64K (27C512) +EPROMs, + +I've liberally commented the code and header files in the hope that it +will help the next person who hacks the code or needs to support some +tulip clone card, or wishes to add functionality. + +Anyway, please test this if you can on your tulip based card, and let +me (mdc@thinguin.org) and the netboot list (netboot@baghira.han.de) +know how things go. I also would appreciate code review by people who +program. I'm a strong believer in "another set of eyes". + +Regards, + +Marty Connor +mdc@thinguin.org diff --git a/netboot/rtl8139.c b/netboot/rtl8139.c new file mode 100644 index 000000000..26e133d14 --- /dev/null +++ b/netboot/rtl8139.c @@ -0,0 +1,429 @@ +/* rtl8139.c - etherboot driver for the Realtek 8139 chipset + + ported from the linux driver written by Donald Becker + by Rainer Bawidamann (Rainer.Bawidamann@informatik.uni-ulm.de) 1999 + + This software may be used and distributed according to the terms + of the GNU Public License, incorporated herein by reference. + + changes to the original driver: + - removed support for interrupts, switchung to polling mode (yuck!) + - removed support for the 8129 chip (external MII) + and the 8139 with ids 0x1113/0x1211 (should be easy to add) + +*/ + +/*********************************************************************/ +/* Revision History */ +/*********************************************************************/ + +/* + + 18 Jan 2000 mdc@thinguin.org (Marty Connor) + Drastically simplified error handling. Basically, if any error + in transmission or reception occurs, the card is reset. + Also, pointed all transmit descriptors to the same buffer to + save buffer space. This should decrease driver size and avoid + corruption because of exceeding 32K during runtime. + + 28 Jul 1999 (Matthias Meixner - meixner@rbg.informatik.tu-darmstadt.de) + rtl_poll was quite broken: it used the RxOK interrupt flag instead + of the RxBufferEmpty flag which often resulted in very bad + transmission performace - below 1kBytes/s. + +*/ + +/* to get some global routines like printf */ +#include "etherboot.h" +/* to get the interface to the body of the program */ +#include "nic.h" +/* we have a PIC card */ +#include "pci.h" + +/* make things easier: the "linux kernel types" */ +#define u16 unsigned short +#define u32 unsigned long +#define u8 unsigned char + +/* PCI Tuning Parameters + Threshold is bytes transferred to chip before transmission starts. */ +#define TX_FIFO_THRESH 256 /* In bytes, rounded down to 32 byte units. */ +#define RX_FIFO_THRESH 4 /* Rx buffer level before first PCI xfer. */ +#define RX_DMA_BURST 4 /* Maximum PCI burst, '4' is 256 bytes */ +#define TX_DMA_BURST 4 /* Calculate as 16<vendor == PCI_VENDOR_ID_REALTEK) + && (p->dev_id == PCI_DEVICE_ID_REALTEK_8139) ) + || ( (p->vendor == PCI_VENDOR_ID_SMC_1211) + && (p->dev_id == PCI_DEVICE_ID_SMC_1211) ) ) { + probeaddrs[0] = p->ioaddr; + printf("rtl8139: probing %x (membase would be %x)\n", + p->ioaddr, p->membase); + } + } + return 0; + } + + /* Mask the bit that says "this is an io addr" */ + ioaddr = probeaddrs[0] & ~3; + + /* Bring the chip out of low-power mode. */ + outb(0x00, ioaddr + Config1); + + if (read_eeprom(ioaddr, 0) != 0xffff) { + unsigned short *ap = (unsigned short*)nic->node_addr; + for (i = 0; i < 3; i++) + *ap++ = read_eeprom(ioaddr, i + 7); + } else { + unsigned char *ap = (unsigned char*)nic->node_addr; + for (i = 0; i < 6; i++) + *ap++ = inb(ioaddr + MAC0 + i); + } + + printf(" I/O %x ", ioaddr); + + for (i = 0; i < 6; i++) + printf ("%b%c", nic->node_addr[i] , i < 5 ?':':' '); + + rtl_reset(nic); + + nic->reset = rtl_reset; + nic->poll = rtl_poll; + nic->transmit = rtl_transmit; + nic->disable = rtl_disable; + + return nic; +} + +/* Serial EEPROM section. */ + +/* EEPROM_Ctrl bits. */ +#define EE_SHIFT_CLK 0x04 /* EEPROM shift clock. */ +#define EE_CS 0x08 /* EEPROM chip select. */ +#define EE_DATA_WRITE 0x02 /* EEPROM chip data in. */ +#define EE_WRITE_0 0x00 +#define EE_WRITE_1 0x02 +#define EE_DATA_READ 0x01 /* EEPROM chip data out. */ +#define EE_ENB (0x80 | EE_CS) + +/* + Delay between EEPROM clock transitions. + No extra delay is needed with 33Mhz PCI, but 66Mhz may change this. +*/ + +#define eeprom_delay() inl(ee_addr) + +/* The EEPROM commands include the alway-set leading bit. */ +#define EE_WRITE_CMD (5 << 6) +#define EE_READ_CMD (6 << 6) +#define EE_ERASE_CMD (7 << 6) + +static int read_eeprom(long ioaddr, int location) +{ + int i; + unsigned retval = 0; + long ee_addr = ioaddr + Cfg9346; + int read_cmd = location | EE_READ_CMD; + + outb(EE_ENB & ~EE_CS, ee_addr); + outb(EE_ENB, ee_addr); + + /* Shift the read command bits out. */ + for (i = 10; i >= 0; i--) { + int dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0; + outb(EE_ENB | dataval, ee_addr); + eeprom_delay(); + outb(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr); + eeprom_delay(); + } + outb(EE_ENB, ee_addr); + eeprom_delay(); + + for (i = 16; i > 0; i--) { + outb(EE_ENB | EE_SHIFT_CLK, ee_addr); + eeprom_delay(); + retval = (retval << 1) | ((inb(ee_addr) & EE_DATA_READ) ? 1 : 0); + outb(EE_ENB, ee_addr); + eeprom_delay(); + } + + /* Terminate the EEPROM access. */ + outb(~EE_CS, ee_addr); + return retval; +} + +static void rtl_open(struct nic* nic) +{ + int i; + + outb(CmdReset, ioaddr + ChipCmd); + + rtl8129_init_ring(); + + /* Check that the chip has finished the reset. */ + for (i = 1000; i > 0; i--) + if ((inb(ioaddr + ChipCmd) & CmdReset) == 0) + break; + + for (i = 0; i < 6; i++) + outb(nic->node_addr[i], ioaddr + MAC0 + i); + + /* Must enable Tx/Rx before setting transfer thresholds! */ + outb(CmdRxEnb | CmdTxEnb, ioaddr + ChipCmd); + outl((RX_FIFO_THRESH << 13) | (RX_BUF_LEN_IDX << 11) | (RX_DMA_BURST<<8), + ioaddr + RxConfig); + outl((TX_DMA_BURST<<8)|0x03000000, ioaddr + TxConfig); + tx_flag = (TX_FIFO_THRESH<<11) & 0x003f0000; + + outb(0xC0, ioaddr + Cfg9346); + outb(0x20, ioaddr + Config1); /* half-duplex */ + outb(0x00, ioaddr + Cfg9346); + +#ifdef DEBUG_RX + printf("rx start address is %X\n",(unsigned long) my.rx_ring); +#endif + outl((unsigned long)my.rx_ring, ioaddr + RxBuf); + + /* Start the chip's Tx and Rx process. */ + outl(0, ioaddr + RxMissed); + /* set_rx_mode */ + outb(AcceptBroadcast|AcceptMulticast|AcceptMyPhys, ioaddr + RxConfig); + outl(0xFFFFFFFF, ioaddr + MAR0 + 0); + outl(0xFFFFFFFF, ioaddr + MAR0 + 4); + outb(CmdRxEnb | CmdTxEnb, ioaddr + ChipCmd); + + /* Disable all known interrupts by setting the interrupt mask. */ + outw(0, ioaddr + IntrMask); +} + +/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ +static void rtl8129_init_ring() +{ + int i; + + cur_rx = 0; + cur_tx = 0; + + for (i = 0; i < NUM_TX_DESC; i++) { + tx_buf[i] = my.tx_bufs; + } +} + +static void rtl_transmit(struct nic *nic, char *destaddr, + unsigned int type, unsigned int len, char *data) +{ + unsigned int i, status=0, to, nstype; + unsigned long txstatus; + unsigned char *txp = tx_buf[cur_tx]; + + memcpy(txp, destaddr, ETHER_ADDR_SIZE); + memcpy(txp + ETHER_ADDR_SIZE, nic->node_addr, ETHER_ADDR_SIZE); + nstype = htons(type); + memcpy(txp + 2 * ETHER_ADDR_SIZE, (char*)&nstype, 2); + memcpy(txp + ETHER_HDR_SIZE, data, len); + + len += ETHER_HDR_SIZE; +#ifdef DEBUG_TX + printf("sending %d bytes ethtype %x\n", len, type); +#endif + outl((unsigned long) txp, ioaddr + TxAddr0 + cur_tx*4); + /* Note: the chip doesn't have auto-pad! */ + /* 60 = ETH_ZLEN */ + outl(tx_flag | (len >= 60 ? len : 60), + ioaddr + TxStatus0 + cur_tx*4); + + to = TIME_OUT; + + do { + status = inw(ioaddr + IntrStatus); + outw(status, ioaddr + IntrStatus); + if ((status & (TxOK | TxErr | PCIErr)) != 0) break; + } while (--to); + + txstatus = inl(ioaddr+ TxStatus0 + cur_tx*4); + + if (status & TxOK) { + cur_tx = ++cur_tx % NUM_TX_DESC; +#ifdef DEBUG_TX + printf("tx done (%d loops), status %x txstatus %X\n", + TIME_OUT-to, status, txstatus); +#endif + return; + + } else { +#ifdef DEBUG_TX + printf("tx ERROR (%d loops), status %x txstatus %X\n", + TIME_OUT-to, status, txstatus); + printf("tx TIME-OUT, status %x\n", status); +#endif + + rtl_reset(nic); + return; + + } +} + +static int rtl_poll(struct nic *nic) +{ + unsigned int status=0; + unsigned int ring_offset, rx_size, rx_status; + + status = inw(ioaddr + IntrStatus); + outw(status, ioaddr + IntrStatus); + + if((inb(ioaddr + ChipCmd) & RxBufEmpty) && !(status & RxErr)) + return 0; + + if( status & RxErr ) { + rtl_reset(nic); + return 0; + } + + ring_offset = cur_rx % RX_BUF_LEN; + rx_status = *(unsigned int*)(my.rx_ring + ring_offset); + rx_size = rx_status >> 16; + + if (rx_status & RxStatusOK) { + nic->packetlen = rx_size; + if (ring_offset+rx_size+4 > RX_BUF_LEN) { + int semi_count = RX_BUF_LEN - ring_offset - 4; + memcpy(nic->packet, (char*)&my.rx_ring[ring_offset+4], semi_count); + memcpy(nic->packet+semi_count, my.rx_ring, rx_size-semi_count); +#ifdef DEBUG_RX + printf("rtl_poll: rx packet %d + %d bytes\n", semi_count,rx_size-semi_count); +#endif + } else { + memcpy(nic->packet, (char*) &my.rx_ring[ring_offset+4], nic->packetlen); +#ifdef DEBUG_RX + printf("rtl_poll: rx packet %d bytes at %X type %b%b status %x rxstatus %X\n", + rx_size, (unsigned long) (my.rx_ring+ring_offset+4), + my.rx_ring[ring_offset+4+12], my.rx_ring[ring_offset+4+13], + status, rx_status); +#endif + } + cur_rx = (cur_rx + rx_size + 4 + 3) & ~3; + outw(cur_rx - 16, ioaddr + RxBufPtr); + return 1; + + } else { + + printf("rtl_poll: rx error status %x\n",status); + rtl_reset(nic); + return 0; + + } +} + +static void rtl_disable(struct nic *nic) +{ +} diff --git a/netboot/sk_g16.c b/netboot/sk_g16.c new file mode 100644 index 000000000..42f5c36fd --- /dev/null +++ b/netboot/sk_g16.c @@ -0,0 +1,1167 @@ +/************************************************************************** +Etherboot - BOOTP/TFTP Bootstrap Program +Schneider & Koch G16 NIC driver for Etherboot +heavily based on SK G16 driver from Linux 2.0.36 +Changes to make it work with Etherboot by Georg Baum +***************************************************************************/ + +/*- + * Copyright (C) 1994 by PJD Weichmann & SWS Bern, Switzerland + * + * This software may be used and distributed according to the terms + * of the GNU Public License, incorporated herein by reference. + * + * Module : sk_g16.c + * + * Version : $Revision$ + * + * Author : Patrick J.D. Weichmann + * + * Date Created : 94/05/26 + * Last Updated : $Date$ + * + * Description : Schneider & Koch G16 Ethernet Device Driver for + * Linux Kernel >= 1.1.22 + * Update History : + * +-*/ + +/* + * The Schneider & Koch (SK) G16 Network device driver is based + * on the 'ni6510' driver from Michael Hipp which can be found at + * ftp://sunsite.unc.edu/pub/Linux/system/Network/drivers/nidrivers.tar.gz + * + * Sources: 1) ni6510.c by M. Hipp + * 2) depca.c by D.C. Davies + * 3) skeleton.c by D. Becker + * 4) Am7990 Local Area Network Controller for Ethernet (LANCE), + * AMD, Pub. #05698, June 1989 + * + * Many Thanks for helping me to get things working to: + * + * A. Cox (A.Cox@swansea.ac.uk) + * M. Hipp (mhipp@student.uni-tuebingen.de) + * R. Bolz (Schneider & Koch, Germany) + * + * See README.sk_g16 for details about limitations and bugs for the + * current version. + * + * To Do: + * - Support of SK_G8 and other SK Network Cards. + * - Autoset memory mapped RAM. Check for free memory and then + * configure RAM correctly. + * - SK_close should really set card in to initial state. + * - Test if IRQ 3 is not switched off. Use autoirq() functionality. + * (as in /drivers/net/skeleton.c) + * - Implement Multicast addressing. At minimum something like + * in depca.c. + * - Redo the statistics part. + * - Try to find out if the board is in 8 Bit or 16 Bit slot. + * If in 8 Bit mode don't use IRQ 11. + * - (Try to make it slightly faster.) + */ + +/* to get some global routines like printf */ +#include "etherboot.h" +/* to get the interface to the body of the program */ +#include "nic.h" + +/* From linux/if_ether.h: */ +#define ETH_ZLEN 60 /* Min. octets in frame sans FCS */ + +#include "sk_g16.h" + +/* + * Schneider & Koch Card Definitions + * ================================= + */ + +#define SK_NAME "SK_G16" + +/* + * SK_G16 Configuration + * -------------------- + */ + +/* + * Abbreviations + * ------------- + * + * RAM - used for the 16KB shared memory + * Boot_ROM, ROM - are used for referencing the BootEPROM + * + * SK_ADDR is a symbolic constant used to configure + * the behaviour of the driver and the SK_G16. + * + * SK_ADDR defines the address where the RAM will be mapped into the real + * host memory. + * valid addresses are from 0xa0000 to 0xfc000 in 16Kbyte steps. + */ + +#define SK_ADDR 0xcc000 + +/* + * In POS3 are bits A14-A19 of the address bus. These bits can be set + * to choose the RAM address. That's why we only can choose the RAM address + * in 16KB steps. + */ + +#define POS_ADDR (rom_addr>>14) /* Do not change this line */ + +/* + * SK_G16 I/O PORT's + IRQ's + Boot_ROM locations + * ---------------------------------------------- + */ + +/* + * As nearly every card has also SK_G16 a specified I/O Port region and + * only a few possible IRQ's. + * In the Installation Guide from Schneider & Koch is listed a possible + * Interrupt IRQ2. IRQ2 is always IRQ9 in boards with two cascaded interrupt + * controllers. So we use in SK_IRQS IRQ9. + */ + +/* Don't touch any of the following #defines. */ + +#define SK_IO_PORTS { 0x100, 0x180, 0x208, 0x220, 0x288, 0x320, 0x328, 0x390, 0 } + +/* + * SK_G16 POS REGISTERS + * -------------------- + */ + +/* + * SK_G16 has a Programmable Option Select (POS) Register. + * The POS is composed of 8 separate registers (POS0-7) which + * are I/O mapped on an address set by the W1 switch. + * + */ + +#define SK_POS_SIZE 8 /* 8 I/O Ports are used by SK_G16 */ + +#define SK_POS0 ioaddr /* Card-ID Low (R) */ +#define SK_POS1 ioaddr+1 /* Card-ID High (R) */ +#define SK_POS2 ioaddr+2 /* Card-Enable, Boot-ROM Disable (RW) */ +#define SK_POS3 ioaddr+3 /* Base address of RAM */ +#define SK_POS4 ioaddr+4 /* IRQ */ + +/* POS5 - POS7 are unused */ + +/* + * SK_G16 MAC PREFIX + * ----------------- + */ + +/* + * Scheider & Koch manufacturer code (00:00:a5). + * This must be checked, that we are sure it is a SK card. + */ + +#define SK_MAC0 0x00 +#define SK_MAC1 0x00 +#define SK_MAC2 0x5a + +/* + * SK_G16 ID + * --------- + */ + +/* + * If POS0,POS1 contain the following ID, then we know + * at which I/O Port Address we are. + */ + +#define SK_IDLOW 0xfd +#define SK_IDHIGH 0x6a + + +/* + * LANCE POS Bit definitions + * ------------------------- + */ + +#define SK_ROM_RAM_ON (POS2_CARD) +#define SK_ROM_RAM_OFF (POS2_EPROM) +#define SK_ROM_ON (inb(SK_POS2) & POS2_CARD) +#define SK_ROM_OFF (inb(SK_POS2) | POS2_EPROM) +#define SK_RAM_ON (inb(SK_POS2) | POS2_CARD) +#define SK_RAM_OFF (inb(SK_POS2) & POS2_EPROM) + +#define POS2_CARD 0x0001 /* 1 = SK_G16 on 0 = off */ +#define POS2_EPROM 0x0002 /* 1 = Boot EPROM off 0 = on */ + +/* + * SK_G16 Memory mapped Registers + * ------------------------------ + * + */ + +#define SK_IOREG (board->ioreg) /* LANCE data registers. */ +#define SK_PORT (board->port) /* Control, Status register */ +#define SK_IOCOM (board->iocom) /* I/O Command */ + +/* + * SK_G16 Status/Control Register bits + * ----------------------------------- + * + * (C) Controlreg (S) Statusreg + */ + +/* + * Register transfer: 0 = no transfer + * 1 = transferring data between LANCE and I/O reg + */ +#define SK_IORUN 0x20 + +/* + * LANCE interrupt: 0 = LANCE interrupt occurred + * 1 = no LANCE interrupt occurred + */ +#define SK_IRQ 0x10 + +#define SK_RESET 0x08 /* Reset SK_CARD: 0 = RESET 1 = normal */ +#define SK_RW 0x02 /* 0 = write to 1 = read from */ +#define SK_ADR 0x01 /* 0 = REG DataPort 1 = RAP Reg addr port */ + + +#define SK_RREG SK_RW /* Transferdirection to read from lance */ +#define SK_WREG 0 /* Transferdirection to write to lance */ +#define SK_RAP SK_ADR /* Destination Register RAP */ +#define SK_RDATA 0 /* Destination Register REG DataPort */ + +/* + * SK_G16 I/O Command + * ------------------ + */ + +/* + * Any bitcombination sets the internal I/O bit (transfer will start) + * when written to I/O Command + */ + +#define SK_DOIO 0x80 /* Do Transfer */ + +/* + * LANCE RAP (Register Address Port). + * --------------------------------- + */ + +/* + * The LANCE internal registers are selected through the RAP. + * The Registers are: + * + * CSR0 - Status and Control flags + * CSR1 - Low order bits of initialize block (bits 15:00) + * CSR2 - High order bits of initialize block (bits 07:00, 15:08 are reserved) + * CSR3 - Allows redefinition of the Bus Master Interface. + * This register must be set to 0x0002, which means BSWAP = 0, + * ACON = 1, BCON = 0; + * + */ + +#define CSR0 0x00 +#define CSR1 0x01 +#define CSR2 0x02 +#define CSR3 0x03 + +/* + * General Definitions + * =================== + */ + +/* + * Set the number of Tx and Rx buffers, using Log_2(# buffers). + * We have 16KB RAM which can be accessed by the LANCE. In the + * memory are not only the buffers but also the ring descriptors and + * the initialize block. + * Don't change anything unless you really know what you do. + */ + +#define LC_LOG_TX_BUFFERS 1 /* (2 == 2^^1) 2 Transmit buffers */ +#define LC_LOG_RX_BUFFERS 2 /* (8 == 2^^3) 8 Receive buffers */ + +/* Descriptor ring sizes */ + +#define TMDNUM (1 << (LC_LOG_TX_BUFFERS)) /* 2 Transmit descriptor rings */ +#define RMDNUM (1 << (LC_LOG_RX_BUFFERS)) /* 8 Receive Buffers */ + +/* Define Mask for setting RMD, TMD length in the LANCE init_block */ + +#define TMDNUMMASK (LC_LOG_TX_BUFFERS << 29) +#define RMDNUMMASK (LC_LOG_RX_BUFFERS << 29) + +/* + * Data Buffer size is set to maximum packet length. + */ + +#define PKT_BUF_SZ 1518 + +/* + * The number of low I/O ports used by the ethercard. + */ + +#define ETHERCARD_TOTAL_SIZE SK_POS_SIZE + +/* + * Portreserve is there to mark the Card I/O Port region as used. + * Check_region is to check if the region at ioaddr with the size "size" + * is free or not. + * Snarf_region allocates the I/O Port region. + */ + +#ifndef HAVE_PORTRESERVE + +#define check_region(ioaddr1, size) 0 +#define request_region(ioaddr1, size,name) do ; while (0) + +#endif + +/* + * SK_DEBUG + * + * Here you can choose what level of debugging wanted. + * + * If SK_DEBUG and SK_DEBUG2 are undefined, then only the + * necessary messages will be printed. + * + * If SK_DEBUG is defined, there will be many debugging prints + * which can help to find some mistakes in configuration or even + * in the driver code. + * + * If SK_DEBUG2 is defined, many many messages will be printed + * which normally you don't need. I used this to check the interrupt + * routine. + * + * (If you define only SK_DEBUG2 then only the messages for + * checking interrupts will be printed!) + * + * Normal way of live is: + * + * For the whole thing get going let both symbolic constants + * undefined. If you face any problems and you know what's going + * on (you know something about the card and you can interpret some + * hex LANCE register output) then define SK_DEBUG + * + */ + +#undef SK_DEBUG /* debugging */ +#undef SK_DEBUG2 /* debugging with more verbose report */ + +#ifdef SK_DEBUG +#define PRINTF(x) printf x +#else +#define PRINTF(x) /**/ +#endif + +#ifdef SK_DEBUG2 +#define PRINTF2(x) printf x +#else +#define PRINTF2(x) /**/ +#endif + +/* + * SK_G16 RAM + * + * The components are memory mapped and can be set in a region from + * 0x00000 through 0xfc000 in 16KB steps. + * + * The Network components are: dual ported RAM, Prom, I/O Reg, Status-, + * Controlregister and I/O Command. + * + * dual ported RAM: This is the only memory region which the LANCE chip + * has access to. From the Lance it is addressed from 0x0000 to + * 0x3fbf. The host accesses it normally. + * + * PROM: The PROM obtains the ETHERNET-MAC-Address. It is realised as a + * 8-Bit PROM, this means only the 16 even addresses are used of the + * 32 Byte Address region. Access to a odd address results in invalid + * data. + * + * LANCE I/O Reg: The I/O Reg is build of 4 single Registers, Low-Byte Write, + * Hi-Byte Write, Low-Byte Read, Hi-Byte Read. + * Transfer from or to the LANCE is always in 16Bit so Low and High + * registers are always relevant. + * + * The Data from the Readregister is not the data in the Writeregister!! + * + * Port: Status- and Controlregister. + * Two different registers which share the same address, Status is + * read-only, Control is write-only. + * + * I/O Command: + * Any bitcombination written in here starts the transmission between + * Host and LANCE. + */ + +typedef struct +{ + unsigned char ram[0x3fc0]; /* 16KB dual ported ram */ + unsigned char rom[0x0020]; /* 32Byte PROM containing 6Byte MAC */ + unsigned char res1[0x0010]; /* reserved */ + unsigned volatile short ioreg;/* LANCE I/O Register */ + unsigned volatile char port; /* Statusregister and Controlregister */ + unsigned char iocom; /* I/O Command Register */ +} SK_RAM; + +/* struct */ + +/* + * This is the structure for the dual ported ram. We + * have exactly 16 320 Bytes. In here there must be: + * + * - Initialize Block (starting at a word boundary) + * - Receive and Transmit Descriptor Rings (quadword boundary) + * - Data Buffers (arbitrary boundary) + * + * This is because LANCE has on SK_G16 only access to the dual ported + * RAM and nowhere else. + */ + +struct SK_ram +{ + struct init_block ib; + struct tmd tmde[TMDNUM]; + struct rmd rmde[RMDNUM]; + char tmdbuf[TMDNUM][PKT_BUF_SZ]; + char rmdbuf[RMDNUM][PKT_BUF_SZ]; +}; + +/* + * Structure where all necessary information is for ring buffer + * management and statistics. + */ + +struct priv +{ + struct SK_ram *ram; /* dual ported ram structure */ + struct rmd *rmdhead; /* start of receive ring descriptors */ + struct tmd *tmdhead; /* start of transmit ring descriptors */ + int rmdnum; /* actual used ring descriptor */ + int tmdnum; /* actual transmit descriptor for transmitting data */ + int tmdlast; /* last sent descriptor used for error handling, etc */ + void *rmdbufs[RMDNUM]; /* pointer to the receive buffers */ + void *tmdbufs[TMDNUM]; /* pointer to the transmit buffers */ +}; + +/* global variable declaration */ + +/* static variables */ + +static SK_RAM *board; /* pointer to our memory mapped board components */ +static unsigned short ioaddr; /* base io address */ +static struct priv p_data; + +/* Macros */ + + +/* Function Prototypes */ + +/* + * Device Driver functions + * ----------------------- + * See for short explanation of each function its definitions header. + */ + +static int SK_probe1(struct nic *nic, short ioaddr1); + +static void SK_reset(struct nic *nic); +static int SK_poll(struct nic *nic); +static void SK_transmit( +struct nic *nic, +char *d, /* Destination */ +unsigned int t, /* Type */ +unsigned int s, /* size */ +char *p); /* Packet */ +static void SK_disable(struct nic *nic); +struct nic *SK_probe(struct nic *nic, unsigned short *probe_addrs); + +/* + * LANCE Functions + * --------------- + */ + +static int SK_lance_init(struct nic *nic, unsigned short mode); +void SK_reset_board(void); +void SK_set_RAP(int reg_number); +int SK_read_reg(int reg_number); +int SK_rread_reg(void); +void SK_write_reg(int reg_number, int value); + +/* + * Debugging functions + * ------------------- + */ + +void SK_print_pos(struct nic *nic, char *text); +void SK_print_ram(struct nic *nic); + + +/************************************************************************** +RESET - Reset adapter +***************************************************************************/ +static void SK_reset(struct nic *nic) +{ + /* put the card in its initial state */ + SK_lance_init(nic, MODE_NORMAL); +} + +/************************************************************************** +POLL - Wait for a frame +***************************************************************************/ +static int SK_poll(struct nic *nic) +{ + /* return true if there's an ethernet packet ready to read */ + struct priv *p; /* SK_G16 private structure */ + struct rmd *rmdp; + int csr0, rmdstat, packet_there; + PRINTF2(("## %s: At beginning of SK_poll(). CSR0: 0x%X\n", + SK_NAME, SK_read_reg(CSR0))); + + p = nic->priv_data; + csr0 = SK_read_reg(CSR0); /* store register for checking */ + + /* + * Acknowledge all of the current interrupt sources, disable + * Interrupts (INEA = 0) + */ + + SK_write_reg(CSR0, csr0 & CSR0_CLRALL); + + if (csr0 & CSR0_ERR) /* LANCE Error */ + { + printf("%s: error: 0x%x", SK_NAME, csr0); + + if (csr0 & CSR0_MISS) /* No place to store packet ? */ + { + printf(", Packet dropped."); + } + putchar('\n'); + } + + rmdp = p->rmdhead + p->rmdnum; + packet_there = 0; + /* As long as we own the next entry, check status and send + * it up to higher layer + */ + + while (!( (rmdstat = rmdp->u.s.status) & RX_OWN)) + { + /* + * Start and end of packet must be set, because we use + * the ethernet maximum packet length (1518) as buffer size. + * + * Because our buffers are at maximum OFLO and BUFF errors are + * not to be concerned (see Data sheet) + */ + + if ((rmdstat & (RX_STP | RX_ENP)) != (RX_STP | RX_ENP)) + { + /* Start of a frame > 1518 Bytes ? */ + + if (rmdstat & RX_STP) + { + printf("%s: packet too long\n", SK_NAME); + } + + /* + * All other packets will be ignored until a new frame with + * start (RX_STP) set follows. + * + * What we do is just give descriptor free for new incoming + * packets. + */ + + rmdp->u.s.status = RX_OWN; /* Relinquish ownership to LANCE */ + + } + else if (rmdstat & RX_ERR) /* Receive Error ? */ + { + printf("%s: RX error: 0x%x\n", SK_NAME, (int) rmdstat); + rmdp->u.s.status = RX_OWN; /* Relinquish ownership to LANCE */ + } + else /* We have a packet which can be queued for the upper layers */ + { + + int len = (rmdp->mlen & 0x0fff); /* extract message length from receive buffer */ + + /* + * Copy data out of our receive descriptor into nic->packet. + * + * (rmdp->u.buffer & 0x00ffffff) -> get address of buffer and + * ignore status fields) + */ + + memcpy(nic->packet, (unsigned char *) (rmdp->u.buffer & 0x00ffffff), nic->packetlen = len); + packet_there = 1; + + + /* + * Packet is queued and marked for processing so we + * free our descriptor + */ + + rmdp->u.s.status = RX_OWN; + + p->rmdnum++; + p->rmdnum %= RMDNUM; + + rmdp = p->rmdhead + p->rmdnum; + } + } + SK_write_reg(CSR0, CSR0_INEA); /* Enable Interrupts */ + return (packet_there); +} + +/************************************************************************** +TRANSMIT - Transmit a frame +***************************************************************************/ +static void SK_transmit( +struct nic *nic, +char *d, /* Destination */ +unsigned int t, /* Type */ +unsigned int s, /* size */ +char *pack) /* Packet */ +{ + /* send the packet to destination */ + struct priv *p; /* SK_G16 private structure */ + struct tmd *tmdp; + short len; + int csr0, i, tmdstat; + + PRINTF2(("## %s: At beginning of SK_transmit(). CSR0: 0x%X\n", + SK_NAME, SK_read_reg(CSR0))); + p = nic->priv_data; + tmdp = p->tmdhead + p->tmdnum; /* Which descriptor for transmitting */ + + /* Copy data into dual ported ram */ + + memcpy(&p->ram->tmdbuf[p->tmdnum][0], d, ETHER_ADDR_SIZE); /* dst */ + memcpy(&p->ram->tmdbuf[p->tmdnum][ETHER_ADDR_SIZE], nic->node_addr, ETHER_ADDR_SIZE); /* src */ + p->ram->tmdbuf[p->tmdnum][ETHER_ADDR_SIZE + ETHER_ADDR_SIZE] = t >> 8; /* type */ + p->ram->tmdbuf[p->tmdnum][ETHER_ADDR_SIZE + ETHER_ADDR_SIZE + 1] = t; /* type */ + memcpy(&p->ram->tmdbuf[p->tmdnum][ETHER_HDR_SIZE], pack, s); + s += ETHER_HDR_SIZE; + while (s < ETH_MIN_PACKET) /* pad to min length */ + p->ram->tmdbuf[p->tmdnum][s++] = 0; + p->ram->tmde[p->tmdnum].status2 = 0x0; + + /* Evaluate Packet length */ + len = ETH_ZLEN < s ? s : ETH_ZLEN; + + /* Fill in Transmit Message Descriptor */ + + tmdp->blen = -len; /* set length to transmit */ + + /* + * Packet start and end is always set because we use the maximum + * packet length as buffer length. + * Relinquish ownership to LANCE + */ + + tmdp->u.s.status = TX_OWN | TX_STP | TX_ENP; + + /* Start Demand Transmission */ + SK_write_reg(CSR0, CSR0_TDMD | CSR0_INEA); + + csr0 = SK_read_reg(CSR0); /* store register for checking */ + + /* + * Acknowledge all of the current interrupt sources, disable + * Interrupts (INEA = 0) + */ + + SK_write_reg(CSR0, csr0 & CSR0_CLRALL); + + if (csr0 & CSR0_ERR) /* LANCE Error */ + { + printf("%s: error: 0x%x", SK_NAME, csr0); + + if (csr0 & CSR0_MISS) /* No place to store packet ? */ + { + printf(", Packet dropped."); + } + putchar('\n'); + } + + + /* Set next buffer */ + p->tmdlast++; + p->tmdlast &= TMDNUM-1; + + tmdstat = tmdp->u.s.status & 0xff00; /* filter out status bits 15:08 */ + + /* + * We check status of transmitted packet. + * see LANCE data-sheet for error explanation + */ + if (tmdstat & TX_ERR) /* Error occurred */ + { + printf("%s: TX error: 0x%x 0x%x\n", SK_NAME, (int) tmdstat, + (int) tmdp->status2); + + if (tmdp->status2 & TX_TDR) /* TDR problems? */ + { + printf("%s: tdr-problems \n", SK_NAME); + } + + if (tmdp->status2 & TX_UFLO) /* Underflow error ? */ + { + /* + * If UFLO error occurs it will turn transmitter of. + * So we must reinit LANCE + */ + + SK_lance_init(nic, MODE_NORMAL); + } + + tmdp->status2 = 0; /* Clear error flags */ + } + + SK_write_reg(CSR0, CSR0_INEA); /* Enable Interrupts */ + + /* Set pointer to next transmit buffer */ + p->tmdnum++; + p->tmdnum &= TMDNUM-1; + +} + +/************************************************************************** +DISABLE - Turn off ethernet interface +***************************************************************************/ +static void SK_disable(struct nic *nic) +{ + PRINTF(("## %s: At beginning of SK_disable(). CSR0: 0x%X\n", + SK_NAME, SK_read_reg(CSR0))); + PRINTF(("%s: Shutting %s down CSR0 0x%X\n", SK_NAME, SK_NAME, + (int) SK_read_reg(CSR0))); + + SK_write_reg(CSR0, CSR0_STOP); /* STOP the LANCE */ +} + +/************************************************************************** +PROBE - Look for an adapter, this routine's visible to the outside +***************************************************************************/ +struct nic *SK_probe(struct nic *nic, unsigned short *probe_addrs) +{ + unsigned short *p; + static unsigned short io_addrs[] = SK_IO_PORTS; + /* if probe_addrs is 0, then routine can use a hardwired default */ + putchar('\n'); + nic->priv_data = &p_data; + if (probe_addrs == 0) + probe_addrs = io_addrs; + for (p = probe_addrs; (ioaddr = *p) != 0; ++p) + { + long offset1, offset0 = inb(ioaddr); + if ((offset0 == SK_IDLOW) && + ((offset1 = inb(ioaddr + 1)) == SK_IDHIGH)) + if (SK_probe1(nic, ioaddr) >= 0) + break; + } + /* if board found */ + if (ioaddr != 0) + { + /* point to NIC specific routines */ + nic->reset = SK_reset; + nic->poll = SK_poll; + nic->transmit = SK_transmit; + nic->disable = SK_disable; + return nic; + } + /* else */ + { + return 0; + } +} + +int SK_probe1(struct nic *nic, short ioaddr1) +{ + int i,j; /* Counters */ + int sk_addr_flag = 0; /* SK ADDR correct? 1 - no, 0 - yes */ + unsigned int rom_addr; /* used to store RAM address used for POS_ADDR */ + + struct priv *p; /* SK_G16 private structure */ + + if (SK_ADDR & 0x3fff || SK_ADDR < 0xa0000) + { + /* + * Now here we could use a routine which searches for a free + * place in the ram and set SK_ADDR if found. TODO. + */ + printf("%s: SK_ADDR 0x%X is not valid. Check configuration.\n", + SK_NAME, SK_ADDR); + return -1; + } + + rom_addr = SK_ADDR; + + outb(SK_ROM_RAM_OFF, SK_POS2); /* Boot_ROM + RAM off */ + outb(POS_ADDR, SK_POS3); /* Set RAM address */ + outb(SK_ROM_RAM_ON, SK_POS2); /* RAM on, BOOT_ROM on */ +#ifdef SK_DEBUG + SK_print_pos(nic, "POS registers after ROM, RAM config"); +#endif + + board = (SK_RAM *) rom_addr; + PRINTF(("adr[0]: %x, adr[1]: %x, adr[2]: %x\n", + board->rom[0], board->rom[2], board->rom[4])); + + /* Read in station address */ + for (i = 0, j = 0; i < ETHER_ADDR_SIZE; i++, j+=2) + { + *(nic->node_addr+i) = board->rom[j]; + } + + /* Check for manufacturer code */ +#ifdef SK_DEBUG + if (!(*(nic->node_addr+0) == SK_MAC0 && + *(nic->node_addr+1) == SK_MAC1 && + *(nic->node_addr+2) == SK_MAC2) ) + { + PRINTF(("## %s: We did not find SK_G16 at RAM location.\n", + SK_NAME)); + return -1; /* NO SK_G16 found */ + } +#endif + + p = nic->priv_data; + + /* Initialize private structure */ + + p->ram = (struct SK_ram *) rom_addr; /* Set dual ported RAM addr */ + p->tmdhead = &(p->ram)->tmde[0]; /* Set TMD head */ + p->rmdhead = &(p->ram)->rmde[0]; /* Set RMD head */ + + printf("Schneider & Koch G16 at 0x%x, mem at 0x%X, HW addr: %b:%b:%b:%b:%b:%b\n", + (unsigned int) ioaddr, + (unsigned int) p->ram, + *(nic->node_addr+0), + *(nic->node_addr+1), + *(nic->node_addr+2), + *(nic->node_addr+3), + *(nic->node_addr+4), + *(nic->node_addr+5)); + + /* Initialize buffer pointers */ + + for (i = 0; i < TMDNUM; i++) + { + p->tmdbufs[i] = p->ram->tmdbuf[i]; + } + + for (i = 0; i < RMDNUM; i++) + { + p->rmdbufs[i] = p->ram->rmdbuf[i]; + } + i = 0; + + if (!(i = SK_lance_init(nic, MODE_NORMAL))) /* LANCE init OK? */ + { + +#ifdef SK_DEBUG + /* + * This debug block tries to stop LANCE, + * reinit LANCE with transmitter and receiver disabled, + * then stop again and reinit with NORMAL_MODE + */ + + printf("## %s: After lance init. CSR0: 0x%X\n", + SK_NAME, SK_read_reg(CSR0)); + SK_write_reg(CSR0, CSR0_STOP); + printf("## %s: LANCE stopped. CSR0: 0x%X\n", + SK_NAME, SK_read_reg(CSR0)); + SK_lance_init(nic, MODE_DTX | MODE_DRX); + printf("## %s: Reinit with DTX + DRX off. CSR0: 0x%X\n", + SK_NAME, SK_read_reg(CSR0)); + SK_write_reg(CSR0, CSR0_STOP); + printf("## %s: LANCE stopped. CSR0: 0x%X\n", + SK_NAME, SK_read_reg(CSR0)); + SK_lance_init(nic, MODE_NORMAL); + printf("## %s: LANCE back to normal mode. CSR0: 0x%X\n", + SK_NAME, SK_read_reg(CSR0)); + SK_print_pos(nic, "POS regs before returning OK"); + +#endif /* SK_DEBUG */ + + } + else /* LANCE init failed */ + { + + PRINTF(("## %s: LANCE init failed: CSR0: 0x%X\n", + SK_NAME, SK_read_reg(CSR0))); + return -1; + } + +#ifdef SK_DEBUG + SK_print_pos(nic, "End of SK_probe1"); + SK_print_ram(nic); +#endif + + return 0; /* Initialization done */ + +} /* End of SK_probe1() */ + +static int SK_lance_init(struct nic *nic, unsigned short mode) +{ + int i; + struct priv *p = (struct priv *) nic->priv_data; + struct tmd *tmdp; + struct rmd *rmdp; + + PRINTF(("## %s: At beginning of LANCE init. CSR0: 0x%X\n", + SK_NAME, SK_read_reg(CSR0))); + + /* Reset LANCE */ + SK_reset_board(); + + /* Initialize TMD's with start values */ + p->tmdnum = 0; /* First descriptor for transmitting */ + p->tmdlast = 0; /* First descriptor for reading stats */ + + for (i = 0; i < TMDNUM; i++) /* Init all TMD's */ + { + tmdp = p->tmdhead + i; + + tmdp->u.buffer = (unsigned long) p->tmdbufs[i]; /* assign buffer */ + + /* Mark TMD as start and end of packet */ + tmdp->u.s.status = TX_STP | TX_ENP; + } + + + /* Initialize RMD's with start values */ + + p->rmdnum = 0; /* First RMD which will be used */ + + for (i = 0; i < RMDNUM; i++) /* Init all RMD's */ + { + rmdp = p->rmdhead + i; + + + rmdp->u.buffer = (unsigned long) p->rmdbufs[i]; /* assign buffer */ + + /* + * LANCE must be owner at beginning so that he can fill in + * receiving packets, set status and release RMD + */ + + rmdp->u.s.status = RX_OWN; + + rmdp->blen = -PKT_BUF_SZ; /* Buffer Size in a two's complement */ + + rmdp->mlen = 0; /* init message length */ + + } + + /* Fill LANCE Initialize Block */ + + (p->ram)->ib.mode = mode; /* Set operation mode */ + + for (i = 0; i < ETHER_ADDR_SIZE; i++) /* Set physical address */ + { + (p->ram)->ib.paddr[i] = *(nic->node_addr+i); + } + + for (i = 0; i < 8; i++) /* Set multicast, logical address */ + { + (p->ram)->ib.laddr[i] = 0; /* We do not use logical addressing */ + } + + /* Set ring descriptor pointers and set number of descriptors */ + + (p->ram)->ib.rdrp = (int) p->rmdhead | RMDNUMMASK; + (p->ram)->ib.tdrp = (int) p->tmdhead | TMDNUMMASK; + + /* Prepare LANCE Control and Status Registers */ + + SK_write_reg(CSR3, CSR3_ACON); /* Ale Control !!!THIS MUST BE SET!!!! */ + + /* + * LANCE addresses the RAM from 0x0000 to 0x3fbf and has no access to + * PC Memory locations. + * + * In structure SK_ram is defined that the first thing in ram + * is the initialization block. So his address is for LANCE always + * 0x0000 + * + * CSR1 contains low order bits 15:0 of initialization block address + * CSR2 is built of: + * 7:0 High order bits 23:16 of initialization block address + * 15:8 reserved, must be 0 + */ + + /* Set initialization block address (must be on word boundary) */ + SK_write_reg(CSR1, 0); /* Set low order bits 15:0 */ + SK_write_reg(CSR2, 0); /* Set high order bits 23:16 */ + + + PRINTF(("## %s: After setting CSR1-3. CSR0: 0x%X\n", + SK_NAME, SK_read_reg(CSR0))); + + /* Initialize LANCE */ + + /* + * INIT = Initialize, when set, causes the LANCE to begin the + * initialization procedure and access the Init Block. + */ + + SK_write_reg(CSR0, CSR0_INIT); + + /* Wait until LANCE finished initialization */ + + SK_set_RAP(CSR0); /* Register Address Pointer to CSR0 */ + + for (i = 0; (i < 100) && !(SK_rread_reg() & CSR0_IDON); i++) + ; /* Wait until init done or go ahead if problems (i>=100) */ + + if (i >= 100) /* Something is wrong ! */ + { + printf("%s: can't init am7990, status: 0x%x " + "init_block: 0x%X\n", + SK_NAME, (int) SK_read_reg(CSR0), + (unsigned int) &(p->ram)->ib); + +#ifdef SK_DEBUG + SK_print_pos(nic, "LANCE INIT failed"); +#endif + + return -1; /* LANCE init failed */ + } + + PRINTF(("## %s: init done after %d ticks\n", SK_NAME, i)); + + /* Clear Initialize done, enable Interrupts, start LANCE */ + + SK_write_reg(CSR0, CSR0_IDON | CSR0_INEA | CSR0_STRT); + + PRINTF(("## %s: LANCE started. CSR0: 0x%X\n", SK_NAME, + SK_read_reg(CSR0))); + + return 0; /* LANCE is up and running */ + +} /* End of SK_lance_init() */ + +/* LANCE access functions + * + * ! CSR1-3 can only be accessed when in CSR0 the STOP bit is set ! + */ + +void SK_reset_board(void) +{ + int i; + + PRINTF(("## %s: At beginning of SK_reset_board.\n", SK_NAME)); + SK_PORT = 0x00; /* Reset active */ + for (i = 0; i < 10 ; i++) /* Delay min 5ms */ + ; + SK_PORT = SK_RESET; /* Set back to normal operation */ + +} /* End of SK_reset_board() */ + +void SK_set_RAP(int reg_number) +{ + SK_IOREG = reg_number; + SK_PORT = SK_RESET | SK_RAP | SK_WREG; + SK_IOCOM = SK_DOIO; + + while (SK_PORT & SK_IORUN) + ; +} /* End of SK_set_RAP() */ + +int SK_read_reg(int reg_number) +{ + SK_set_RAP(reg_number); + + SK_PORT = SK_RESET | SK_RDATA | SK_RREG; + SK_IOCOM = SK_DOIO; + + while (SK_PORT & SK_IORUN) + ; + return (SK_IOREG); + +} /* End of SK_read_reg() */ + +int SK_rread_reg(void) +{ + SK_PORT = SK_RESET | SK_RDATA | SK_RREG; + + SK_IOCOM = SK_DOIO; + + while (SK_PORT & SK_IORUN) + ; + return (SK_IOREG); + +} /* End of SK_rread_reg() */ + +void SK_write_reg(int reg_number, int value) +{ + SK_set_RAP(reg_number); + + SK_IOREG = value; + SK_PORT = SK_RESET | SK_RDATA | SK_WREG; + SK_IOCOM = SK_DOIO; + + while (SK_PORT & SK_IORUN) + ; +} /* End of SK_write_reg */ + +/* + * Debugging functions + * ------------------- + */ + +#ifdef SK_DEBUG +void SK_print_pos(struct nic *nic, char *text) +{ + + unsigned char pos0 = inb(SK_POS0), + pos1 = inb(SK_POS1), + pos2 = inb(SK_POS2), + pos3 = inb(SK_POS3), + pos4 = inb(SK_POS4); + + + printf("## %s: %s.\n" + "## pos0=0x%x pos1=0x%x pos2=0x%x pos3=0x%X pos4=0x%x\n", + SK_NAME, text, pos0, pos1, pos2, (pos3<<14), pos4); + +} /* End of SK_print_pos() */ + +void SK_print_ram(struct nic *nic) +{ + + int i; + struct priv *p = (struct priv *) nic->priv_data; + + printf("## %s: RAM Details.\n" + "## RAM at 0x%X tmdhead: 0x%X rmdhead: 0x%X initblock: 0x%X\n", + SK_NAME, + (unsigned int) p->ram, + (unsigned int) p->tmdhead, + (unsigned int) p->rmdhead, + (unsigned int) &(p->ram)->ib); + + printf("## "); + + for(i = 0; i < TMDNUM; i++) + { + if (!(i % 3)) /* Every third line do a newline */ + { + printf("\n## "); + } + printf("tmdbufs%d: 0x%X ", (i+1), (int) p->tmdbufs[i]); + } + printf("## "); + + for(i = 0; i < RMDNUM; i++) + { + if (!(i % 3)) /* Every third line do a newline */ + { + printf("\n## "); + } + printf("rmdbufs%d: 0x%X ", (i+1), (int) p->rmdbufs[i]); + } + putchar('\n'); + +} /* End of SK_print_ram() */ +#endif diff --git a/netboot/sk_g16.h b/netboot/sk_g16.h new file mode 100644 index 000000000..ea4aca7a8 --- /dev/null +++ b/netboot/sk_g16.h @@ -0,0 +1,168 @@ +/*- + * + * This software may be used and distributed according to the terms + * of the GNU Public License, incorporated herein by reference. + * + * Module : sk_g16.h + * Version : $Revision$ + * + * Author : M.Hipp (mhipp@student.uni-tuebingen.de) + * changes by : Patrick J.D. Weichmann + * + * Date Created : 94/05/25 + * + * Description : In here are all necessary definitions of + * the am7990 (LANCE) chip used for writing a + * network device driver which uses this chip + * + * $Log$ + * Revision 1.1 2000-02-07 06:30:07 okuji + * add missing files. + * +-*/ + +#ifndef SK_G16_H + +#define SK_G16_H + + +/* + * Control and Status Register 0 (CSR0) bit definitions + * + * (R=Readable) (W=Writeable) (S=Set on write) (C-Clear on write) + * + */ + +#define CSR0_ERR 0x8000 /* Error summary (R) */ +#define CSR0_BABL 0x4000 /* Babble transmitter timeout error (RC) */ +#define CSR0_CERR 0x2000 /* Collision Error (RC) */ +#define CSR0_MISS 0x1000 /* Missed packet (RC) */ +#define CSR0_MERR 0x0800 /* Memory Error (RC) */ +#define CSR0_RINT 0x0400 /* Receiver Interrupt (RC) */ +#define CSR0_TINT 0x0200 /* Transmit Interrupt (RC) */ +#define CSR0_IDON 0x0100 /* Initialization Done (RC) */ +#define CSR0_INTR 0x0080 /* Interrupt Flag (R) */ +#define CSR0_INEA 0x0040 /* Interrupt Enable (RW) */ +#define CSR0_RXON 0x0020 /* Receiver on (R) */ +#define CSR0_TXON 0x0010 /* Transmitter on (R) */ +#define CSR0_TDMD 0x0008 /* Transmit Demand (RS) */ +#define CSR0_STOP 0x0004 /* Stop (RS) */ +#define CSR0_STRT 0x0002 /* Start (RS) */ +#define CSR0_INIT 0x0001 /* Initialize (RS) */ + +#define CSR0_CLRALL 0x7f00 /* mask for all clearable bits */ + +/* + * Control and Status Register 3 (CSR3) bit definitions + * + */ + +#define CSR3_BSWAP 0x0004 /* Byte Swap (RW) */ +#define CSR3_ACON 0x0002 /* ALE Control (RW) */ +#define CSR3_BCON 0x0001 /* Byte Control (RW) */ + +/* + * Initialization Block Mode operation Bit Definitions. + */ + +#define MODE_PROM 0x8000 /* Promiscuous Mode */ +#define MODE_INTL 0x0040 /* Internal Loopback */ +#define MODE_DRTY 0x0020 /* Disable Retry */ +#define MODE_COLL 0x0010 /* Force Collision */ +#define MODE_DTCR 0x0008 /* Disable Transmit CRC) */ +#define MODE_LOOP 0x0004 /* Loopback */ +#define MODE_DTX 0x0002 /* Disable the Transmitter */ +#define MODE_DRX 0x0001 /* Disable the Receiver */ + +#define MODE_NORMAL 0x0000 /* Normal operation mode */ + +/* + * Receive message descriptor status bit definitions. + */ + +#define RX_OWN 0x80 /* Owner bit 0 = host, 1 = lance */ +#define RX_ERR 0x40 /* Error Summary */ +#define RX_FRAM 0x20 /* Framing Error */ +#define RX_OFLO 0x10 /* Overflow Error */ +#define RX_CRC 0x08 /* CRC Error */ +#define RX_BUFF 0x04 /* Buffer Error */ +#define RX_STP 0x02 /* Start of Packet */ +#define RX_ENP 0x01 /* End of Packet */ + + +/* + * Transmit message descriptor status bit definitions. + */ + +#define TX_OWN 0x80 /* Owner bit 0 = host, 1 = lance */ +#define TX_ERR 0x40 /* Error Summary */ +#define TX_MORE 0x10 /* More the 1 retry needed to Xmit */ +#define TX_ONE 0x08 /* One retry needed to Xmit */ +#define TX_DEF 0x04 /* Deferred */ +#define TX_STP 0x02 /* Start of Packet */ +#define TX_ENP 0x01 /* End of Packet */ + +/* + * Transmit status (2) (valid if TX_ERR == 1) + */ + +#define TX_BUFF 0x8000 /* Buffering error (no ENP) */ +#define TX_UFLO 0x4000 /* Underflow (late memory) */ +#define TX_LCOL 0x1000 /* Late collision */ +#define TX_LCAR 0x0400 /* Loss of Carrier */ +#define TX_RTRY 0x0200 /* Failed after 16 retransmissions */ +#define TX_TDR 0x003f /* Time-domain-reflectometer-value */ + + +/* + * Structures used for Communication with the LANCE + */ + +/* LANCE Initialize Block */ + +struct init_block +{ + unsigned short mode; /* Mode Register */ + unsigned char paddr[6]; /* Physical Address (MAC) */ + unsigned char laddr[8]; /* Logical Filter Address (not used) */ + unsigned int rdrp; /* Receive Descriptor Ring pointer */ + unsigned int tdrp; /* Transmit Descriptor Ring pointer */ +}; + + +/* Receive Message Descriptor Entry */ + +struct rmd +{ + union rmd_u + { + unsigned long buffer; /* Address of buffer */ + struct rmd_s + { + unsigned char unused[3]; + unsigned volatile char status; /* Status Bits */ + } s; + } u; + volatile short blen; /* Buffer Length (two's complement) */ + unsigned short mlen; /* Message Byte Count */ +}; + + +/* Transmit Message Descriptor Entry */ + +struct tmd +{ + union tmd_u + { + unsigned long buffer; /* Address of buffer */ + struct tmd_s + { + unsigned char unused[3]; + unsigned volatile char status; /* Status Bits */ + } s; + } u; + unsigned short blen; /* Buffer Length (two's complement) */ + unsigned volatile short status2; /* Error Status Bits */ +}; + +#endif /* End of SK_G16_H */ diff --git a/netboot/smc9000.c b/netboot/smc9000.c new file mode 100644 index 000000000..385c7f7b3 --- /dev/null +++ b/netboot/smc9000.c @@ -0,0 +1,527 @@ + /*------------------------------------------------------------------------ + * smc9000.c + * This is a Etherboot driver for SMC's 9000 series of Ethernet cards. + * + * Copyright (C) 1998 Daniel Engström + * Based on the Linux SMC9000 driver, smc9194.c by Eric Stahlman + * Copyright (C) 1996 by Erik Stahlman + * + * This software may be used and distributed according to the terms + * of the GNU Public License, incorporated herein by reference. + * + * "Features" of the SMC chip: + * 4608 byte packet memory. ( for the 91C92/4. Others have more ) + * EEPROM for configuration + * AUI/TP selection + * + * Authors + * Erik Stahlman + * Daniel Engström + * + * History + * 98-09-25 Daniel Engström Etherboot driver crated from Eric's + * Linux driver. + * + *---------------------------------------------------------------------------*/ +#define LINUX_OUT_MACROS 1 +#define SMC9000_VERBOSE 1 +#define SMC9000_DEBUG 0 + +#include "etherboot.h" +#include "nic.h" +#include "smc9000.h" + +# define _outb outb +# define _outw outw + +static const char smc9000_version[] = "Version 0.99 98-09-30"; +static unsigned int smc9000_base=0; +static const char *interfaces[ 2 ] = { "TP", "AUI" }; +static const char *chip_ids[ 15 ] = { + NULL, NULL, NULL, + /* 3 */ "SMC91C90/91C92", + /* 4 */ "SMC91C94", + /* 5 */ "SMC91C95", + NULL, + /* 7 */ "SMC91C100", + /* 8 */ "SMC91C100FD", + NULL, NULL, NULL, + NULL, NULL, NULL +}; +static const char smc91c96_id[] = "SMC91C96"; + +/* + * Function: smc_reset( int ioaddr ) + * Purpose: + * This sets the SMC91xx chip to its normal state, hopefully from whatever + * mess that any other DOS driver has put it in. + * + * Maybe I should reset more registers to defaults in here? SOFTRESET should + * do that for me. + * + * Method: + * 1. send a SOFT RESET + * 2. wait for it to finish + * 3. reset the memory management unit + * 4. clear all interrupts + * +*/ +static void smc_reset(int ioaddr) +{ + /* This resets the registers mostly to defaults, but doesn't + * affect EEPROM. That seems unnecessary */ + SMC_SELECT_BANK(ioaddr, 0); + _outw( RCR_SOFTRESET, ioaddr + RCR ); + + /* this should pause enough for the chip to be happy */ + SMC_DELAY(ioaddr); + + /* Set the transmit and receive configuration registers to + * default values */ + _outw(RCR_CLEAR, ioaddr + RCR); + _outw(TCR_CLEAR, ioaddr + TCR); + + /* Reset the MMU */ + SMC_SELECT_BANK(ioaddr, 2); + _outw( MC_RESET, ioaddr + MMU_CMD ); + + /* Note: It doesn't seem that waiting for the MMU busy is needed here, + * but this is a place where future chipsets _COULD_ break. Be wary + * of issuing another MMU command right after this */ + _outb(0, ioaddr + INT_MASK); +} + + +/*---------------------------------------------------------------------- + * Function: smc_probe( int ioaddr ) + * + * Purpose: + * Tests to see if a given ioaddr points to an SMC9xxx chip. + * Returns a 0 on success + * + * Algorithm: + * (1) see if the high byte of BANK_SELECT is 0x33 + * (2) compare the ioaddr with the base register's address + * (3) see if I recognize the chip ID in the appropriate register + * + * --------------------------------------------------------------------- + */ +static int smc_probe( int ioaddr ) +{ + word bank; + word revision_register; + word base_address_register; + + /* First, see if the high byte is 0x33 */ + bank = inw(ioaddr + BANK_SELECT); + if ((bank & 0xFF00) != 0x3300) { + return -1; + } + /* The above MIGHT indicate a device, but I need to write to further + * test this. */ + _outw(0x0, ioaddr + BANK_SELECT); + bank = inw(ioaddr + BANK_SELECT); + if ((bank & 0xFF00) != 0x3300) { + return -1; + } + + /* well, we've already written once, so hopefully another time won't + * hurt. This time, I need to switch the bank register to bank 1, + * so I can access the base address register */ + SMC_SELECT_BANK(ioaddr, 1); + base_address_register = inw(ioaddr + BASE); + + if (ioaddr != (base_address_register >> 3 & 0x3E0)) { +#ifdef SMC9000_VERBOSE + printf("SMC9000: IOADDR %x doesn't match configuration (%x)." + "Probably not a SMC chip\n", + ioaddr, base_address_register >> 3 & 0x3E0); +#endif + /* well, the base address register didn't match. Must not have + * been a SMC chip after all. */ + return -1; + } + + + /* check if the revision register is something that I recognize. + * These might need to be added to later, as future revisions + * could be added. */ + SMC_SELECT_BANK(ioaddr, 3); + revision_register = inw(ioaddr + REVISION); + if (!chip_ids[(revision_register >> 4) & 0xF]) { + /* I don't recognize this chip, so... */ +#ifdef SMC9000_VERBOSE + printf("SMC9000: IO %x: Unrecognized revision register:" + " %x, Contact author.\n", ioaddr, revision_register); +#endif + return -1; + } + + /* at this point I'll assume that the chip is an SMC9xxx. + * It might be prudent to check a listing of MAC addresses + * against the hardware address, or do some other tests. */ + return 0; +} + + +/************************************************************************** + * ETH_RESET - Reset adapter + ***************************************************************************/ + +static void smc9000_reset(struct nic *nic) +{ + smc_reset(smc9000_base); +} + +/************************************************************************** + * ETH_TRANSMIT - Transmit a frame + ***************************************************************************/ +static void smc9000_transmit( + struct nic *nic, + char *d, /* Destination */ + unsigned int t, /* Type */ + unsigned int s, /* size */ + char *p) /* Packet */ +{ + word length; /* real, length incl. header */ + word numPages; + long time_out; + byte packet_no; + word status; + int i; + + /* We dont pad here since we can have the hardware doing it for us */ + length = (s + ETHER_HDR_SIZE + 1)&~1; + + /* convert to MMU pages */ + numPages = length / 256; + + if (numPages > 7 ) { +#ifdef SMC9000_VERBOSE + printf("SMC9000: Far too big packet error. \n"); +#endif + return; + } + + /* dont try more than, say 30 times */ + for (i=0;i<30;i++) { + /* now, try to allocate the memory */ + SMC_SELECT_BANK(smc9000_base, 2); + _outw(MC_ALLOC | numPages, smc9000_base + MMU_CMD); + + status = 0; + /* wait for the memory allocation to finnish */ + for (time_out = currticks() + 5*TICKS_PER_SEC; (currticks() - time_out) < 0; ) { + status = inb(smc9000_base + INTERRUPT); + if ( status & IM_ALLOC_INT ) { + /* acknowledge the interrupt */ + _outb(IM_ALLOC_INT, smc9000_base + INTERRUPT); + break; + } + } + + if ((status & IM_ALLOC_INT) != 0 ) { + /* We've got the memory */ + break; + } else { + printf("SMC9000: Memory allocation timed out, resetting MMU.\n"); + _outw(MC_RESET, smc9000_base + MMU_CMD); + } + } + + /* If I get here, I _know_ there is a packet slot waiting for me */ + packet_no = inb(smc9000_base + PNR_ARR + 1); + if (packet_no & 0x80) { + /* or isn't there? BAD CHIP! */ + printf("SMC9000: Memory allocation failed. \n"); + return; + } + + /* we have a packet address, so tell the card to use it */ + _outb(packet_no, smc9000_base + PNR_ARR); + + /* point to the beginning of the packet */ + _outw(PTR_AUTOINC, smc9000_base + POINTER); + +#if SMC9000_DEBUG > 2 + printf("Trying to xmit packet of length %x\n", length ); +#endif + + /* send the packet length ( +6 for status, length and ctl byte ) + * and the status word ( set to zeros ) */ + _outw(0, smc9000_base + DATA_1 ); + + /* send the packet length ( +6 for status words, length, and ctl) */ + _outb((length+6) & 0xFF, smc9000_base + DATA_1); + _outb((length+6) >> 8 , smc9000_base + DATA_1); + + /* Write the contents of the packet */ + + /* The ethernet header first... */ + outsw(smc9000_base + DATA_1, d, ETHER_ADDR_SIZE >> 1); + outsw(smc9000_base + DATA_1, nic->node_addr, ETHER_ADDR_SIZE >> 1); + _outw(htons(t), smc9000_base + DATA_1); + + /* ... the data ... */ + outsw(smc9000_base + DATA_1 , p, s >> 1); + + /* ... and the last byte, if there is one. */ + if ((s & 1) == 0) { + _outw(0, smc9000_base + DATA_1); + } else { + _outb(p[s-1], smc9000_base + DATA_1); + _outb(0x20, smc9000_base + DATA_1); + } + + /* and let the chipset deal with it */ + _outw(MC_ENQUEUE , smc9000_base + MMU_CMD); + + status = 0; time_out = currticks() + 5*TICKS_PER_SEC; + do { + status = inb(smc9000_base + INTERRUPT); + + if ((status & IM_TX_INT ) != 0) { + word tx_status; + + /* ack interrupt */ + _outb(IM_TX_INT, smc9000_base + INTERRUPT); + + packet_no = inw(smc9000_base + FIFO_PORTS); + packet_no &= 0x7F; + + /* select this as the packet to read from */ + _outb( packet_no, smc9000_base + PNR_ARR ); + + /* read the first word from this packet */ + _outw( PTR_AUTOINC | PTR_READ, smc9000_base + POINTER ); + + tx_status = inw( smc9000_base + DATA_1 ); + + if (0 == (tx_status & TS_SUCCESS)) { +#ifdef SMC9000_VERBOSE + printf("SMC9000: TX FAIL STATUS: %x \n", tx_status); +#endif + /* re-enable transmit */ + SMC_SELECT_BANK(smc9000_base, 0); + _outw(inw(smc9000_base + TCR ) | TCR_ENABLE, smc9000_base + TCR ); + } + + /* kill the packet */ + SMC_SELECT_BANK(smc9000_base, 2); + _outw(MC_FREEPKT, smc9000_base + MMU_CMD); + + return; + } + }while(currticks() - time_out < 0); + + printf("SMC9000: Waring TX timed out, resetting board\n"); + smc_reset(smc9000_base); + return; +} + +/************************************************************************** + * ETH_POLL - Wait for a frame + ***************************************************************************/ +static int smc9000_poll(struct nic *nic) +{ + word status; + + if(!smc9000_base) + return 0; + + SMC_SELECT_BANK(smc9000_base, 2); + if (inw(smc9000_base + FIFO_PORTS) & FP_RXEMPTY) + return 0; + + /* start reading from the start of the packet */ + _outw(PTR_READ | PTR_RCV | PTR_AUTOINC, smc9000_base + POINTER); + + /* First read the status and check that we're ok */ + if (!(inw(smc9000_base + DATA_1) & RS_ERRORS)) { + /* Next: read the packet length and mask off the top bits */ + nic->packetlen = (inw(smc9000_base + DATA_1) & 0x07ff); + + /* the packet length includes the 3 extra words */ + nic->packetlen -= 6; +#if SMC9000_DEBUG > 2 + printf(" Reading %d words (and %d byte(s))\n", + (nic->packetlen >> 1), nic->packetlen & 1); +#endif + /* read the packet (and the last "extra" word) */ + insw(smc9000_base + DATA_1, nic->packet, (nic->packetlen+2) >> 1); + /* is there an odd last byte ? */ + if (nic->packet[nic->packetlen+1] & 0x20) + nic->packetlen++; + + /* error or good, tell the card to get rid of this packet */ + _outw(MC_RELEASE, smc9000_base + MMU_CMD); + return 1; + } + + printf("SMC9000: RX error\n"); + /* error or good, tell the card to get rid of this packet */ + _outw(MC_RELEASE, smc9000_base + MMU_CMD); + return 0; +} + +static void smc9000_disable(struct nic *nic) +{ + if(!smc9000_base) + return; + + /* no more interrupts for me */ + SMC_SELECT_BANK(smc9000_base, 2); + _outb( 0, smc9000_base + INT_MASK); + + /* and tell the card to stay away from that nasty outside world */ + SMC_SELECT_BANK(smc9000_base, 0); + _outb( RCR_CLEAR, smc9000_base + RCR ); + _outb( TCR_CLEAR, smc9000_base + TCR ); +} + +/************************************************************************** + * ETH_PROBE - Look for an adapter + ***************************************************************************/ + +struct nic *smc9000_probe(struct nic *nic, unsigned short *probe_addrs) +{ + unsigned short revision; + int memory; + int media; + const char * version_string; + const char * if_string; + int irqval; + int i; + + /* + * the SMC9000 can be at any of the following port addresses. To change, + * for a slightly different card, you can add it to the array. Keep in + * mind that the array must end in zero. + */ + static unsigned short portlist[] = { +#ifdef SMC9000_SCAN + SMC9000_SCAN, +#else + 0x200, 0x220, 0x240, 0x260, 0x280, 0x2A0, 0x2C0, 0x2E0, + 0x300, 0x320, 0x340, 0x360, 0x380, 0x3A0, 0x3C0, 0x3E0, +#endif + 0 }; + + printf("\nSMC9000 %s\n", smc9000_version); +#ifdef SMC9000_VERBOSE + printf("Copyright (C) 1998 Daniel Engstr\x94m\n"); + printf("Copyright (C) 1996 Eric Stahlman\n"); +#endif + /* if no addresses supplied, fall back on defaults */ + if (probe_addrs == 0 || probe_addrs[0] == 0) + probe_addrs = portlist; + + /* check every ethernet address */ + for (i = 0; probe_addrs[i]; i++) { + /* check this specific address */ + if (smc_probe(probe_addrs[i]) == 0) + smc9000_base = probe_addrs[i]; + } + + /* couldn't find anything */ + if(0 == smc9000_base) + goto out; + + /* + * Get the MAC address ( bank 1, regs 4 - 9 ) + */ + SMC_SELECT_BANK(smc9000_base, 1); + for ( i = 0; i < 6; i += 2 ) { + word address; + + address = inw(smc9000_base + ADDR0 + i); + nic->node_addr[i+1] = address >> 8; + nic->node_addr[i] = address & 0xFF; + } + + + /* get the memory information */ + SMC_SELECT_BANK(smc9000_base, 0); + memory = ( inw(smc9000_base + MCR) >> 9 ) & 0x7; /* multiplier */ + memory *= 256 * (inw(smc9000_base + MIR) & 0xFF); + + /* + * Now, I want to find out more about the chip. This is sort of + * redundant, but it's cleaner to have it in both, rather than having + * one VERY long probe procedure. + */ + SMC_SELECT_BANK(smc9000_base, 3); + revision = inw(smc9000_base + REVISION); + version_string = chip_ids[(revision >> 4) & 0xF]; + + if (((revision & 0xF0) >> 4 == CHIP_9196) && + ((revision & 0x0F) >= REV_9196)) { + /* This is a 91c96. 'c96 has the same chip id as 'c94 (4) but + * a revision starting at 6 */ + version_string = smc91c96_id; + } + + if ( !version_string ) { + /* I shouldn't get here because this call was done before.... */ + goto out; + } + + /* is it using AUI or 10BaseT ? */ + SMC_SELECT_BANK(smc9000_base, 1); + if (inw(smc9000_base + CONFIG) & CFG_AUI_SELECT) + media = 2; + else + media = 1; + + if_string = interfaces[media - 1]; + + /* now, reset the chip, and put it into a known state */ + smc_reset(smc9000_base); + + printf("%s rev:%d I/O port:%x Interface:%s RAM:%d bytes \n", + version_string, revision & 0xF, + smc9000_base, if_string, memory ); + /* + * Print the Ethernet address + */ + printf("Ethernet MAC address: "); + for (i = 0; i < 5; i++) + printf("%b:", nic->node_addr[i]); + printf("%b\n", nic->node_addr[5]); + + SMC_SELECT_BANK(smc9000_base, 0); + + /* see the header file for options in TCR/RCR NORMAL*/ + _outw(TCR_NORMAL, smc9000_base + TCR); + _outw(RCR_NORMAL, smc9000_base + RCR); + + /* Select which interface to use */ + SMC_SELECT_BANK(smc9000_base, 1); + if ( media == 1 ) { + _outw( inw( smc9000_base + CONFIG ) & ~CFG_AUI_SELECT, + smc9000_base + CONFIG ); + } + else if ( media == 2 ) { + _outw( inw( smc9000_base + CONFIG ) | CFG_AUI_SELECT, + smc9000_base + CONFIG ); + } + + nic->reset = smc9000_reset; + nic->poll = smc9000_poll; + nic->transmit = smc9000_transmit; + nic->disable = smc9000_disable; + + + return nic; + +out: +#ifdef SMC9000_VERBOSE + printf("No SMC9000 adapters found\n"); +#endif + smc9000_base = 0; + + return (0); +} + + + diff --git a/netboot/smc9000.h b/netboot/smc9000.h new file mode 100644 index 000000000..b1bb35698 --- /dev/null +++ b/netboot/smc9000.h @@ -0,0 +1,205 @@ +/*------------------------------------------------------------------------ + * smc9000.h + * + * Copyright (C) 1998 by Daniel Engström + * Copyright (C) 1996 by Erik Stahlman + * + * This software may be used and distributed according to the terms + * of the GNU Public License, incorporated herein by reference. + * + * This file contains register information and access macros for + * the SMC91xxx chipset. + * + * Information contained in this file was obtained from the SMC91C94 + * manual from SMC. To get a copy, if you really want one, you can find + * information under www.smsc.com in the components division. + * ( this thanks to advice from Donald Becker ). + * + * Authors + * Daniel Engström + * Erik Stahlman + * + * History + * 96-01-06 Erik Stahlman moved definitions here from main .c + * file + * 96-01-19 Erik Stahlman polished this up some, and added + * better error handling + * 98-09-25 Daniel Engström adjusted for Etherboot + * 98-09-27 Daniel Engström moved some static strings back to the + * main .c file + * --------------------------------------------------------------------------*/ +#ifndef _SMC9000_H_ +# define _SMC9000_H_ + +/* I want some simple types */ +typedef unsigned char byte; +typedef unsigned short word; +typedef unsigned long int dword; + +/*--------------------------------------------------------------- + * + * A description of the SMC registers is probably in order here, + * although for details, the SMC datasheet is invaluable. + * + * Basically, the chip has 4 banks of registers ( 0 to 3 ), which + * are accessed by writing a number into the BANK_SELECT register + * ( I also use a SMC_SELECT_BANK macro for this ). + * + * The banks are configured so that for most purposes, bank 2 is all + * that is needed for simple run time tasks. + * ----------------------------------------------------------------------*/ + +/* + * Bank Select Register: + * + * yyyy yyyy 0000 00xx + * xx = bank number + * yyyy yyyy = 0x33, for identification purposes. + */ +#define BANK_SELECT 14 + +/* BANK 0 */ + +#define TCR 0 /* transmit control register */ +#define TCR_ENABLE 0x0001 /* if this is 1, we can transmit */ +#define TCR_FDUPLX 0x0800 /* receive packets sent out */ +#define TCR_STP_SQET 0x1000 /* stop transmitting if Signal quality error */ +#define TCR_MON_CNS 0x0400 /* monitors the carrier status */ +#define TCR_PAD_ENABLE 0x0080 /* pads short packets to 64 bytes */ + +#define TCR_CLEAR 0 /* do NOTHING */ +/* the normal settings for the TCR register : */ +#define TCR_NORMAL (TCR_ENABLE | TCR_PAD_ENABLE) + + +#define EPH_STATUS 2 +#define ES_LINK_OK 0x4000 /* is the link integrity ok ? */ + +#define RCR 4 +#define RCR_SOFTRESET 0x8000 /* resets the chip */ +#define RCR_STRIP_CRC 0x200 /* strips CRC */ +#define RCR_ENABLE 0x100 /* IFF this is set, we can receive packets */ +#define RCR_ALMUL 0x4 /* receive all multicast packets */ +#define RCR_PROMISC 0x2 /* enable promiscuous mode */ + +/* the normal settings for the RCR register : */ +#define RCR_NORMAL (RCR_STRIP_CRC | RCR_ENABLE) +#define RCR_CLEAR 0x0 /* set it to a base state */ + +#define COUNTER 6 +#define MIR 8 +#define MCR 10 +/* 12 is reserved */ + +/* BANK 1 */ +#define CONFIG 0 +#define CFG_AUI_SELECT 0x100 +#define BASE 2 +#define ADDR0 4 +#define ADDR1 6 +#define ADDR2 8 +#define GENERAL 10 +#define CONTROL 12 +#define CTL_POWERDOWN 0x2000 +#define CTL_LE_ENABLE 0x80 +#define CTL_CR_ENABLE 0x40 +#define CTL_TE_ENABLE 0x0020 +#define CTL_AUTO_RELEASE 0x0800 +#define CTL_EPROM_ACCESS 0x0003 /* high if Eprom is being read */ + +/* BANK 2 */ +#define MMU_CMD 0 +#define MC_BUSY 1 /* only readable bit in the register */ +#define MC_NOP 0 +#define MC_ALLOC 0x20 /* or with number of 256 byte packets */ +#define MC_RESET 0x40 +#define MC_REMOVE 0x60 /* remove the current rx packet */ +#define MC_RELEASE 0x80 /* remove and release the current rx packet */ +#define MC_FREEPKT 0xA0 /* Release packet in PNR register */ +#define MC_ENQUEUE 0xC0 /* Enqueue the packet for transmit */ + +#define PNR_ARR 2 +#define FIFO_PORTS 4 + +#define FP_RXEMPTY 0x8000 +#define FP_TXEMPTY 0x80 + +#define POINTER 6 +#define PTR_READ 0x2000 +#define PTR_RCV 0x8000 +#define PTR_AUTOINC 0x4000 +#define PTR_AUTO_INC 0x0040 + +#define DATA_1 8 +#define DATA_2 10 +#define INTERRUPT 12 + +#define INT_MASK 13 +#define IM_RCV_INT 0x1 +#define IM_TX_INT 0x2 +#define IM_TX_EMPTY_INT 0x4 +#define IM_ALLOC_INT 0x8 +#define IM_RX_OVRN_INT 0x10 +#define IM_EPH_INT 0x20 +#define IM_ERCV_INT 0x40 /* not on SMC9192 */ + +/* BANK 3 */ +#define MULTICAST1 0 +#define MULTICAST2 2 +#define MULTICAST3 4 +#define MULTICAST4 6 +#define MGMT 8 +#define REVISION 10 /* ( hi: chip id low: rev # ) */ + + +/* this is NOT on SMC9192 */ +#define ERCV 12 + +/* Note that 9194 and 9196 have the smame chip id, + * the 9196 will have revisions starting at 6 */ +#define CHIP_9190 3 +#define CHIP_9194 4 +#define CHIP_9195 5 +#define CHIP_9196 4 +#define CHIP_91100 7 +#define CHIP_91100FD 8 + +#define REV_9196 6 + +/* + * Transmit status bits + */ +#define TS_SUCCESS 0x0001 +#define TS_LOSTCAR 0x0400 +#define TS_LATCOL 0x0200 +#define TS_16COL 0x0010 + +/* + * Receive status bits + */ +#define RS_ALGNERR 0x8000 +#define RS_BADCRC 0x2000 +#define RS_ODDFRAME 0x1000 +#define RS_TOOLONG 0x0800 +#define RS_TOOSHORT 0x0400 +#define RS_MULTICAST 0x0001 +#define RS_ERRORS (RS_ALGNERR | RS_BADCRC | RS_TOOLONG | RS_TOOSHORT) + + +/*------------------------------------------------------------------------- + * I define some macros to make it easier to do somewhat common + * or slightly complicated, repeated tasks. + --------------------------------------------------------------------------*/ + +/* select a register bank, 0 to 3 */ + +#define SMC_SELECT_BANK(x, y) { _outw( y, x + BANK_SELECT ); } + +/* define a small delay for the reset */ +#define SMC_DELAY(x) { inw( x + RCR );\ + inw( x + RCR );\ + inw( x + RCR ); } + + +#endif /* _SMC_9000_H_ */ + diff --git a/netboot/tiara.c b/netboot/tiara.c new file mode 100644 index 000000000..5b4b73597 --- /dev/null +++ b/netboot/tiara.c @@ -0,0 +1,254 @@ +/************************************************************************** +Etherboot - BOOTP/TFTP Bootstrap Program + +TIARA (Fujitsu Etherstar) NIC driver for Etherboot +Copyright (c) Ken Yap 1998 + +Information gleaned from: + +TIARA.ASM Packet driver by Brian Fisher, Queens U, Kingston, Ontario +Fujitsu MB86960 spec sheet (different chip but same family) +***************************************************************************/ + +/* to get some global routines like printf */ +#include "etherboot.h" +/* to get the interface to the body of the program */ +#include "nic.h" + +/* + EtherStar I/O Register offsets +*/ + +/* Offsets of registers */ +#define DLCR_XMIT_STAT 0x00 +#define DLCR_XMIT_MASK 0x01 +#define DLCR_RECV_STAT 0x02 +#define DLCR_RECV_MASK 0x03 +#define DLCR_XMIT_MODE 0x04 +#define DLCR_RECV_MODE 0x05 +#define DLCR_ENABLE 0x06 +#define DLCR_TDR_LOW 0x07 +#define DLCR_NODE_ID 0x08 +#define DLCR_TDR_HIGH 0x0F +#define BMPR_MEM_PORT 0x10 +#define BMPR_PKT_LEN 0x12 +#define BMPR_DMA_ENABLE 0x14 +#define PROM_ID 0x18 + +#define TMST 0x80 +#define TMT_OK 0x80 +#define TMT_16COLL 0x02 +#define BUF_EMPTY 0x40 + +#define CARD_DISABLE 0x80 /* written to DLCR_ENABLE to disable card */ +#define CARD_ENABLE 0 /* written to DLCR_ENABLE to enable card */ + +#define CLEAR_STATUS 0x0F /* used to clear status info */ +/* + 00001111B + !!!!!!!!-------- + !!!!!!!+--------CLEAR BUS WRITE ERROR + !!!!!!+---------CLEAR 16 COLLISION + !!!!!+----------CLEAR COLLISION + !!!!+-----------CLEAR UNDERFLOW + !!!+------------NC + !!+-------------NC + !+--------------NC + +---------------NC +*/ + +#define NO_TX_IRQS 0 /* written to clear transmit IRQs */ + +#define CLR_RCV_STATUS 0xCF /* clears receive status */ + +#define EN_RCV_IRQS 0x80 /* enable receive interrupts */ +/* + 10000000B + !!!!!!!!-------- + !!!!!!!+--------ENABLE OVERFLOW + !!!!!!+---------ENABLE CRC + !!!!!+----------ENABLE ALIGN + !!!!+-----------ENABLE SHORT PKT + !!!+------------DISABLE REMOTE RESET + !!+-------------RESERVED + !+--------------RESERVED + +---------------ENABLE PKT READY +*/ + +#define XMIT_MODE 0x02 +/* + 00000010B + !!!!!!!!---------ENABLE CARRIER DETECT + !!!!!!!+---------DISABLE LOOPBACK +*/ + +#define RECV_MODE 0x02 +/* + 00000010B + !!!!!!!!---------ACCEPT ALL PACKETS + !!!!!!!+---------ACCEPT PHYSICAL, MULTICAST, AND + !!!!!!+----------BROADCAST PACKETS + !!!!!+-----------DISABLE REMOTE RESET + !!!!+------------DISABLE SHORT PACKETS + !!!+-------------USE 6 BYTE ADDRESS + !!+--------------NC + !+---------------NC + +----------------DISABLE CRC TEST MODE +*/ + +/* NIC specific static variables go here */ + +static unsigned short ioaddr; + +/************************************************************************** +RESET - Reset adapter +***************************************************************************/ +static void tiara_reset(struct nic *nic) +{ + int i; + + outb(CARD_DISABLE, ioaddr + DLCR_ENABLE); + outb(CLEAR_STATUS, ioaddr + DLCR_XMIT_STAT); + outb(NO_TX_IRQS, ioaddr + DLCR_XMIT_MASK); + outb(CLR_RCV_STATUS, ioaddr + DLCR_RECV_STAT); + outb(XMIT_MODE, ioaddr + DLCR_XMIT_MODE); + outb(RECV_MODE, ioaddr + DLCR_RECV_MODE); + /* Vacuum recv buffer */ + while ((inb(ioaddr + DLCR_RECV_MODE) & BUF_EMPTY) == 0) + inb(ioaddr + BMPR_MEM_PORT); + /* Set node address */ + for (i = 0; i < ETHER_ADDR_SIZE; ++i) + outb(nic->node_addr[i], ioaddr + DLCR_NODE_ID + i); + outb(CLR_RCV_STATUS, ioaddr + DLCR_RECV_STAT); + outb(CARD_ENABLE, ioaddr + DLCR_ENABLE); +} + +/************************************************************************** +POLL - Wait for a frame +***************************************************************************/ +static int tiara_poll(struct nic *nic) +{ + unsigned int len; + + if (inb(ioaddr + DLCR_RECV_MODE) & BUF_EMPTY) + return (0); + /* Ack packet */ + outw(CLR_RCV_STATUS, ioaddr + DLCR_RECV_STAT); + len = inw(ioaddr + BMPR_MEM_PORT); /* throw away status */ + len = inw(ioaddr + BMPR_MEM_PORT); + /* Drop overlength packets */ + if (len > ETH_MAX_PACKET) + return (0); /* should we drain the buffer? */ + insw(ioaddr + BMPR_MEM_PORT, nic->packet, len / 2); + /* If it's our own, drop it */ + if (memcmp(nic->packet + ETHER_ADDR_SIZE, nic->node_addr, ETHER_ADDR_SIZE) == 0) + return (0); + nic->packetlen = len; + return (1); +} + +/************************************************************************** +TRANSMIT - Transmit a frame +***************************************************************************/ +static void tiara_transmit( +struct nic *nic, +char *d, /* Destination */ +unsigned int t, /* Type */ +unsigned int s, /* size */ +char *p) /* Packet */ +{ + unsigned int len; + unsigned long time; + + len = s + ETHER_HDR_SIZE; + if (len < ETH_MIN_PACKET) + len = ETH_MIN_PACKET; + t = htons(t); + outsw(ioaddr + BMPR_MEM_PORT, d, ETHER_ADDR_SIZE / 2); + outsw(ioaddr + BMPR_MEM_PORT, nic->node_addr, ETHER_ADDR_SIZE / 2); + outw(t, ioaddr + BMPR_MEM_PORT); + outsw(ioaddr + BMPR_MEM_PORT, p, s / 2); + if (s & 1) /* last byte */ + outb(p[s-1], ioaddr + BMPR_MEM_PORT); + while (s++ < ETH_MIN_PACKET - ETHER_HDR_SIZE) /* pad */ + outb(0, ioaddr + BMPR_MEM_PORT); + outw(len | (TMST << 8), ioaddr + BMPR_PKT_LEN); + /* wait for transmit complete */ + time = currticks() + TICKS_PER_SEC; /* wait one second */ + while (currticks() < time && (inb(ioaddr) & (TMT_OK|TMT_16COLL)) == 0) + ; + if ((inb(ioaddr) & (TMT_OK|TMT_16COLL)) == 0) + printf("Tiara timed out on transmit\n"); + /* Do we need to ack the transmit? */ +} + +/************************************************************************** +DISABLE - Turn off ethernet interface +***************************************************************************/ +static void tiara_disable(struct nic *nic) +{ + /* Apparently only a power down can do this properly */ + outb(CARD_DISABLE, ioaddr + DLCR_ENABLE); +} + +static int tiara_probe1(struct nic *nic) +{ + /* Hope all the Tiara cards have this vendor prefix */ + static char vendor_prefix[] = { 0x08, 0x00, 0x1A }; + static char all_ones[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + int i, b; + + for (i = 0; i < ETHER_ADDR_SIZE; ++i) + nic->node_addr[i] = inb(ioaddr + PROM_ID + i); + if (memcmp(nic->node_addr, vendor_prefix, sizeof(vendor_prefix)) != 0) + return (0); + if (memcmp(nic->node_addr, all_ones, sizeof(all_ones)) == 0) + return (0); + printf("\nTiara ioaddr 0x%x, addr ", ioaddr); + for (i = 0; i < ETHER_ADDR_SIZE; ++i) + { + printf("%b", nic->node_addr[i]); + if (i < ETHER_ADDR_SIZE - 1) + printf(":"); + } + putchar('\n'); + return (1); +} + +/************************************************************************** +PROBE - Look for an adapter, this routine's visible to the outside +***************************************************************************/ +struct nic *tiara_probe(struct nic *nic, unsigned short *probe_addrs) +{ + /* missing entries are addresses usually already used */ + static unsigned short io_addrs[] = { + 0x100, 0x120, 0x140, 0x160, + 0x180, 0x1A0, 0x1C0, 0x1E0, + 0x200, 0x220, 0x240, /*Par*/ + 0x280, 0x2A0, 0x2C0, /*Ser*/ + 0x300, 0x320, 0x340, /*Par*/ + 0x380, /*Vid,Par*/ 0x3C0, /*Ser*/ + 0x0 + }; + unsigned short *p; + + /* if probe_addrs is 0, then routine can use a hardwired default */ + if (probe_addrs == 0) + probe_addrs = io_addrs; + for (p = probe_addrs; (ioaddr = *p) != 0; ++p) + if (tiara_probe1(nic)) + break; + /* if board found */ + if (ioaddr != 0) + { + tiara_reset(nic); + /* point to NIC specific routines */ + nic->reset = tiara_reset; + nic->poll = tiara_poll; + nic->transmit = tiara_transmit; + nic->disable = tiara_disable; + return nic; + } + else + return (0); +} diff --git a/netboot/tulip.c b/netboot/tulip.c new file mode 100644 index 000000000..c03bec866 --- /dev/null +++ b/netboot/tulip.c @@ -0,0 +1,380 @@ +/* + Etherboot DEC Tulip driver + adapted by Ken Yap from + + FreeBSD netboot DEC 21143 driver + + Author: David Sharp + date: Nov/98 + + Known to work on DEC DE500 using 21143-PC chipset. + Even on cards with the same chipset there can be + incompatablity problems with the way media selection + and status LED settings are done. See comments below. + + Some code fragments were taken from verious places, + Ken Yap's etherboot, FreeBSD's if_de.c, and various + Linux related files. DEC's manuals for the 21143 and + SROM format were very helpful. The Linux de driver + development page has a number of links to useful + related information. Have a look at: + ftp://cesdis.gsfc.nasa.gov/pub/linux/drivers/tulip-devel.html + +*/ + +#include "etherboot.h" +#include "nic.h" +#include "pci.h" +#include "tulip.h" + +static unsigned short vendor, dev_id; +static unsigned short ioaddr; +static unsigned int *membase; +static unsigned long devfn; +static unsigned char srom[1024]; + +#define BUFLEN 1600 /* must be longword divisable */ + /* buffers must be longword aligned */ + +/* transmit descriptor and buffer */ +static struct txrxdesc txd; +static unsigned char txb[BUFLEN]; + +/* receive descriptor(s) and buffer(s) */ +#define NRXD 4 +static struct txrxdesc rxd[NRXD]; +static int rxd_tail = 0; +static unsigned char rxb[NRXD][BUFLEN]; + +static unsigned char ehdr[ETHER_HDR_SIZE]; /* buffer for ethernet header */ + +enum tulip_offsets { + CSR0=0, CSR1=0x08, CSR2=0x10, CSR3=0x18, CSR4=0x20, CSR5=0x28, + CSR6=0x30, CSR7=0x38, CSR8=0x40, CSR9=0x48, CSR10=0x50, CSR11=0x58, + CSR12=0x60, CSR13=0x68, CSR14=0x70, CSR15=0x78 }; + + +/***************************************************************************/ +/* 21143 specific stuff */ +/***************************************************************************/ + +/* XXX assume 33MHz PCI bus, this is not very accurate and should be + used only with gross over estimations of required delay times unless + you tune UADJUST to your specific processor and I/O subsystem */ + +#define UADJUST 870 +static void udelay(unsigned long usec) { + unsigned long i; + for (i=((usec*UADJUST)/33)+1; i>0; i--) (void) TULIP_CSR_READ(csr_0); +} + +/* The following srom related code was taken from FreeBSD's if_de.c */ +/* with minor alterations to make it work here. the Linux code is */ +/* better but this was easier to use */ + +static void delay_300ns() +{ + int idx; + for (idx = (300 / 33) + 1; idx > 0; idx--) + (void) TULIP_CSR_READ(csr_busmode); +} + +#define EMIT do { TULIP_CSR_WRITE(csr_srom_mii, csr); delay_300ns(); } while (0) + +static void srom_idle() +{ + unsigned bit, csr; + + csr = SROMSEL ; EMIT; + csr = SROMSEL | SROMRD; EMIT; + csr ^= SROMCS; EMIT; + csr ^= SROMCLKON; EMIT; + /* + * Write 25 cycles of 0 which will force the SROM to be idle. + */ + for (bit = 3 + SROM_BITWIDTH + 16; bit > 0; bit--) { + csr ^= SROMCLKOFF; EMIT; /* clock low; data not valid */ + csr ^= SROMCLKON; EMIT; /* clock high; data valid */ + } + csr ^= SROMCLKOFF; EMIT; + csr ^= SROMCS; EMIT; + csr = 0; EMIT; +} + +static void srom_read() +{ + unsigned idx; + const unsigned bitwidth = SROM_BITWIDTH; + const unsigned cmdmask = (SROMCMD_RD << bitwidth); + const unsigned msb = 1 << (bitwidth + 3 - 1); + unsigned lastidx = (1 << bitwidth) - 1; + + srom_idle(); + + for (idx = 0; idx <= lastidx; idx++) { + unsigned lastbit, data, bits, bit, csr; + csr = SROMSEL ; EMIT; + csr = SROMSEL | SROMRD; EMIT; + csr ^= SROMCSON; EMIT; + csr ^= SROMCLKON; EMIT; + + lastbit = 0; + for (bits = idx|cmdmask, bit = bitwidth + 3; bit > 0; bit--, bits <<= 1) + { + const unsigned thisbit = bits & msb; + csr ^= SROMCLKOFF; EMIT; /* clock low; data not valid */ + if (thisbit != lastbit) { + csr ^= SROMDOUT; EMIT; /* clock low; invert data */ + } else { + EMIT; + } + csr ^= SROMCLKON; EMIT; /* clock high; data valid */ + lastbit = thisbit; + } + csr ^= SROMCLKOFF; EMIT; + + for (data = 0, bits = 0; bits < 16; bits++) { + data <<= 1; + csr ^= SROMCLKON; EMIT; /* clock high; data valid */ + data |= TULIP_CSR_READ(csr_srom_mii) & SROMDIN ? 1 : 0; + csr ^= SROMCLKOFF; EMIT; /* clock low; data not valid */ + } + srom[idx*2] = data & 0xFF; + srom[idx*2+1] = data >> 8; + csr = SROMSEL | SROMRD; EMIT; + csr = 0; EMIT; + } + srom_idle(); +} + +/************************************************************************** +ETH_RESET - Reset adapter +***************************************************************************/ +static void tulip_reset(struct nic *nic) +{ + int x,cnt=2; + unsigned long csr6,csr15; + + outl(0x00000001, ioaddr + CSR0); + udelay(1000); + /* turn off reset and set cache align=16lword, burst=unlimit */ + outl(0x01A08000, ioaddr + CSR0); + + /* for some reason the media selection does not take + the first time se it is repeated. */ + + while(cnt--) { + /* stop TX,RX processes */ + if (cnt == 1) + outl(0x32404000, ioaddr + CSR6); + else + outl(0x32000040, ioaddr + CSR6); + + /* XXX - media selection is vendor specific and hard coded right + here. This should be fixed to use the hints in the SROM and + allow media selection by the user at runtime. MII support + should also be added. Support for chips other than the + 21143 should be added here as well */ + + /* start set to 10Mbps half-duplex */ + + /* setup SIA */ + outl(0x0, ioaddr + CSR13); /* reset SIA */ + outl(0x7f3f, ioaddr + CSR14); + outl(0x8000008, ioaddr + CSR15); + outl(0x0, ioaddr + CSR13); + outl(0x1, ioaddr + CSR13); + outl(0x2404000, ioaddr + CSR6); + + /* initalize GP */ + outl(0x8af0008, ioaddr + CSR15); + outl(0x50008, ioaddr + CSR15); + + /* end set to 10Mbps half-duplex */ + + if (vendor == PCI_VENDOR_ID_MACRONIX && dev_id == PCI_DEVICE_ID_MX987x5) { + /* do stuff for MX98715 */ + outl(0x01a80000, ioaddr + CSR6); + outl(0xFFFFFFFF, ioaddr + CSR14); + outl(0x00001000, ioaddr + CSR12); + } + + outl(0x0, ioaddr + CSR7); /* disable interrupts */ + + /* construct setup packet which is used by the 21143 to + program its CAM to recognize interesting MAC addresses */ + + memset(&txd, 0, sizeof(struct txrxdesc)); + txd.buf1addr = &txb[0]; + txd.buf2addr = &txb[0]; /* just in case */ + txd.buf1sz = 192; /* setup packet must be 192 bytes */ + txd.buf2sz = 0; + txd.control = 0x020; /* setup packet */ + txd.status = 0x80000000; /* give ownership to 21143 */ + + /* construct perfect filter frame */ + /* with mac address as first match */ + /* and broadcast address for all others */ + + for(x=0;x<192;x++) txb[x] = 0xff; + txb[0] = nic->node_addr[0]; + txb[1] = nic->node_addr[1]; + txb[4] = nic->node_addr[2]; + txb[5] = nic->node_addr[3]; + txb[8] = nic->node_addr[4]; + txb[9] = nic->node_addr[5]; + outl((unsigned long)&txd, ioaddr + CSR4); /* set xmit buf */ + outl(0x2406000, ioaddr + CSR6); /* start transmiter */ + + udelay(50000); /* wait for the setup packet to be processed */ + + } + + /* setup receive descriptor */ + { + int x; + for(x=0;xnode_addr, ETHER_ADDR_SIZE); + ehdr[ETHER_ADDR_SIZE*2] = (t >> 8) & 0xff; + ehdr[ETHER_ADDR_SIZE*2+1] = t & 0xff; + + /* setup the transmit descriptor */ + + memset(&txd, 0, sizeof(struct txrxdesc)); + + txd.buf1addr = &ehdr[0]; /* ethernet header */ + txd.buf1sz = ETHER_HDR_SIZE; + + txd.buf2addr = p; /* packet to transmit */ + txd.buf2sz = s; + + txd.control = 0x188; /* LS+FS+TER */ + + txd.status = 0x80000000; /* give it to 21143 */ + + outl(inl(ioaddr + CSR6) & ~0x00004000, ioaddr + CSR6); + outl((unsigned long)&txd, ioaddr + CSR4); + outl(inl(ioaddr + CSR6) | 0x00004000, ioaddr + CSR6); + +/* Wait for transmit to complete before returning. not well tested. + + time = currticks(); + while(txd.status & 0x80000000) { + if (currticks() - time > 20) { + printf("transmit timeout.\n"); + break; + } + } +*/ + +} + +/************************************************************************** +ETH_POLL - Wait for a frame +***************************************************************************/ +static int tulip_poll(struct nic *nic) +{ + /* common variables */ + unsigned short type = 0; + int x; + + if (rxd[rxd_tail].status & 0x80000000) return 0; + + nic->packetlen = (rxd[rxd_tail].status & 0x3FFF0000) >> 16; + + /* copy packet to working buffer */ + /* XXX - this copy could be avoided with a little more work + but for now we are content with it because the optimised + memcpy(, , ) is quite fast */ + + memcpy(nic->packet, &rxb[rxd_tail][0], nic->packetlen); + + /* return the descriptor and buffer to recieve ring */ + rxd[rxd_tail].status = 0x80000000; + rxd_tail++; + if (rxd_tail == NRXD) rxd_tail = 0; + + return 1; +} + +static void tulip_disable(struct nic *nic) +{ + /* nothing for the moment */ +} + +/************************************************************************** +ETH_PROBE - Look for an adapter +***************************************************************************/ +struct nic *tulip_probe(struct nic *nic, unsigned short *io_addrs, struct pci_device *pci) +{ + int i; + + if (io_addrs == 0 || *io_addrs == 0) + return (0); + vendor = pci->vendor; + dev_id = pci->dev_id; + ioaddr = *io_addrs; + membase = (unsigned int *)pci->membase; + + /* wakeup chip */ + pcibios_write_config_dword(0,pci->devfn,0x40,0x00000000); + + /* Stop the chip's Tx and Rx processes. */ + /* outl(inl(ioaddr + CSR6) & ~0x2002, ioaddr + CSR6); */ + /* Clear the missed-packet counter. */ + /* (volatile int)inl(ioaddr + CSR8); */ + + srom_read(); + + for (i=0; i < 6; i++) + nic->node_addr[i] = srom[20+i]; + + printf("Tulip %b:%b:%b:%b:%b:%b at ioaddr 0x%x\n", + srom[20],srom[21],srom[22],srom[23],srom[24],srom[25], + ioaddr); + + tulip_reset(nic); + + nic->reset = tulip_reset; + nic->poll = tulip_poll; + nic->transmit = tulip_transmit; + nic->disable = tulip_disable; + return nic; +} diff --git a/netboot/tulip.h b/netboot/tulip.h new file mode 100644 index 000000000..d37ae62da --- /dev/null +++ b/netboot/tulip.h @@ -0,0 +1,67 @@ +/* mostly stolen from FreeBSD if_de.c, if_devar.h */ + +#define TULIP_CSR_READ(csr) (membase[csr*2]) +#define CSR_READ(csr) (membase[csr*2]) +#define TULIP_CSR_WRITE(csr, val) (membase[csr*2] = val) +#define CSR_WRITE(csr, val) (membase[csr*2] = val) + +#define csr_0 0 +#define csr_1 1 +#define csr_2 2 +#define csr_3 3 +#define csr_4 4 +#define csr_5 5 +#define csr_6 6 +#define csr_7 7 +#define csr_8 8 +#define csr_9 9 +#define csr_10 10 +#define csr_11 11 +#define csr_12 12 +#define csr_13 13 +#define csr_14 14 +#define csr_15 15 + +#define csr_busmode csr_0 +#define csr_txpoll csr_1 +#define csr_rxpoll csr_2 +#define csr_rxlist csr_3 +#define csr_txlist csr_4 +#define csr_status csr_5 +#define csr_command csr_6 +#define csr_intr csr_7 +#define csr_missed_frames csr_8 +#define csr_enetrom csr_9 /* 21040 */ +#define csr_reserved csr_10 /* 21040 */ +#define csr_full_duplex csr_11 /* 21040 */ +#define csr_bootrom csr_10 /* 21041/21140A/?? */ +#define csr_gp csr_12 /* 21140* */ +#define csr_watchdog csr_15 /* 21140* */ +#define csr_gp_timer csr_11 /* 21041/21140* */ +#define csr_srom_mii csr_9 /* 21041/21140* */ +#define csr_sia_status csr_12 /* 2104x */ +#define csr_sia_connectivity csr_13 /* 2104x */ +#define csr_sia_tx_rx csr_14 /* 2104x */ +#define csr_sia_general csr_15 /* 2104x */ + +#define SROMSEL 0x0800 +#define SROMCS 0x0001 +#define SROMCLKON 0x0002 +#define SROMCLKOFF 0x0002 +#define SROMRD 0x4000 +#define SROMWR 0x2000 +#define SROM_BITWIDTH 6 +#define SROMCMD_RD 6 +#define SROMCSON 0x0001 +#define SROMDOUT 0x0004 +#define SROMDIN 0x0008 + + +struct txrxdesc { + unsigned long status; /* owner, status */ + unsigned long buf1sz:11, /* size of buffer 1 */ + buf2sz:11, /* size of buffer 2 */ + control:10; /* control bits */ + unsigned char *buf1addr; /* buffer 1 address */ + unsigned char *buf2addr; /* buffer 2 address */ +}; diff --git a/netboot/via-rhine.c b/netboot/via-rhine.c new file mode 100644 index 000000000..ace6a5c6f --- /dev/null +++ b/netboot/via-rhine.c @@ -0,0 +1,1219 @@ +/* rhine.c:Fast Ethernet driver for Linux. */ +/* + Adapted 09-jan-2000 by Paolo Marini (paolom@prisma-eng.it) + + originally written by Donald Becker. + + This software may be used and distributed according to the terms + of the GNU Public License (GPL), incorporated herein by reference. + Drivers derived from this code also fall under the GPL and must retain + this authorship and copyright notice. + + Under no circumstances are the authors responsible for + the proper functioning of this software, nor do the authors assume any + responsibility for damages incurred with its use. + + This driver is designed for the VIA VT86C100A Rhine-II PCI Fast Ethernet + controller. + + TODO: Some block comments are C++ style. Can get away with it + with gcc, but not nice. +*/ + +static const char *version = "rhine.c v1.0.0 2000-01-07\n"; + +/* A few user-configurable values. */ + +/* Size of the in-memory receive ring. */ +#define RX_BUF_LEN_IDX 3 /* 0==8K, 1==16K, 2==32K, 3==64K */ +#define RX_BUF_LEN (8192 << RX_BUF_LEN_IDX) + +/* Size of the Tx bounce buffers -- must be at least (dev->mtu+14+4). */ +#define TX_BUF_SIZE 1536 +#define RX_BUF_SIZE 1536 + +/* PCI Tuning Parameters + Threshold is bytes transferred to chip before transmission starts. */ +#define TX_FIFO_THRESH 256 /* In bytes, rounded down to 32 byte units. */ + +/* The following settings are log_2(bytes)-4: 0 == 16 bytes .. 6==1024. */ +#define RX_FIFO_THRESH 4 /* Rx buffer level before first PCI xfer. */ +#define RX_DMA_BURST 4 /* Maximum PCI burst, '4' is 256 bytes */ +#define TX_DMA_BURST 4 + +/* Operational parameters that usually are not changed. */ +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT ((2000*HZ)/1000) + +#include "etherboot.h" +#include "nic.h" +#include "pci.h" + +/* define all ioaddr */ + +#define byPAR0 ioaddr +#define byRCR ioaddr + 6 +#define byTCR ioaddr + 7 +#define byCR0 ioaddr + 8 +#define byCR1 ioaddr + 9 +#define byISR0 ioaddr + 0x0c +#define byISR1 ioaddr + 0x0d +#define byIMR0 ioaddr + 0x0e +#define byIMR1 ioaddr + 0x0f +#define byMAR0 ioaddr + 0x10 +#define byMAR1 ioaddr + 0x11 +#define byMAR2 ioaddr + 0x12 +#define byMAR3 ioaddr + 0x13 +#define byMAR4 ioaddr + 0x14 +#define byMAR5 ioaddr + 0x15 +#define byMAR6 ioaddr + 0x16 +#define byMAR7 ioaddr + 0x17 +#define dwCurrentRxDescAddr ioaddr + 0x18 +#define dwCurrentTxDescAddr ioaddr + 0x1c +#define dwCurrentRDSE0 ioaddr + 0x20 +#define dwCurrentRDSE1 ioaddr + 0x24 +#define dwCurrentRDSE2 ioaddr + 0x28 +#define dwCurrentRDSE3 ioaddr + 0x2c +#define dwNextRDSE0 ioaddr + 0x30 +#define dwNextRDSE1 ioaddr + 0x34 +#define dwNextRDSE2 ioaddr + 0x38 +#define dwNextRDSE3 ioaddr + 0x3c +#define dwCurrentTDSE0 ioaddr + 0x40 +#define dwCurrentTDSE1 ioaddr + 0x44 +#define dwCurrentTDSE2 ioaddr + 0x48 +#define dwCurrentTDSE3 ioaddr + 0x4c +#define dwNextTDSE0 ioaddr + 0x50 +#define dwNextTDSE1 ioaddr + 0x54 +#define dwNextTDSE2 ioaddr + 0x58 +#define dwNextTDSE3 ioaddr + 0x5c +#define dwCurrRxDMAPtr ioaddr + 0x60 +#define dwCurrTxDMAPtr ioaddr + 0x64 +#define byMPHY ioaddr + 0x6c +#define byMIISR ioaddr + 0x6d +#define byBCR0 ioaddr + 0x6e +#define byBCR1 ioaddr + 0x6f +#define byMIICR ioaddr + 0x70 +#define byMIIAD ioaddr + 0x71 +#define wMIIDATA ioaddr + 0x72 +#define byEECSR ioaddr + 0x74 +#define byTEST ioaddr + 0x75 +#define byGPIO ioaddr + 0x76 +#define byCFGA ioaddr + 0x78 +#define byCFGB ioaddr + 0x79 +#define byCFGC ioaddr + 0x7a +#define byCFGD ioaddr + 0x7b +#define wTallyCntMPA ioaddr + 0x7c +#define wTallyCntCRC ioaddr + 0x7d +/*--------------------- Exioaddr Definitions -------------------------*/ + +// +// Bits in the RCR register +// + +#define RCR_RRFT2 0x80 // +#define RCR_RRFT1 0x40 // +#define RCR_RRFT0 0x20 // +#define RCR_PROM 0x10 // +#define RCR_AB 0x08 // +#define RCR_AM 0x04 // +#define RCR_AR 0x02 // +#define RCR_SEP 0x01 // + +// +// Bits in the TCR register +// + +#define TCR_RTSF 0x80 // +#define TCR_RTFT1 0x40 // +#define TCR_RTFT0 0x20 // +#define TCR_OFSET 0x08 // +#define TCR_LB1 0x04 // loopback[1] +#define TCR_LB0 0x02 // loopback[0] + +// +// Bits in the CR0 register +// + +#define CR0_RDMD 0x40 // rx descriptor polling demand +#define CR0_TDMD 0x20 // tx descriptor polling demand +#define CR0_TXON 0x10 // +#define CR0_RXON 0x08 // +#define CR0_STOP 0x04 // stop NIC, default = 1 +#define CR0_STRT 0x02 // start NIC +#define CR0_INIT 0x01 // start init process + + +// +// Bits in the CR1 register +// + +#define CR1_SFRST 0x80 // software reset +#define CR1_RDMD1 0x40 // RDMD1 +#define CR1_TDMD1 0x20 // TDMD1 +#define CR1_KEYPAG 0x10 // turn on par/key +#define CR1_DPOLL 0x08 // disable rx/tx auto polling +#define CR1_FDX 0x04 // full duplex mode +#define CR1_ETEN 0x02 // early tx mode +#define CR1_EREN 0x01 // early rx mode + +// +// Bits in the CR register +// + +#define CR_RDMD 0x0040 // rx descriptor polling demand +#define CR_TDMD 0x0020 // tx descriptor polling demand +#define CR_TXON 0x0010 // +#define CR_RXON 0x0008 // +#define CR_STOP 0x0004 // stop NIC, default = 1 +#define CR_STRT 0x0002 // start NIC +#define CR_INIT 0x0001 // start init process +#define CR_SFRST 0x8000 // software reset +#define CR_RDMD1 0x4000 // RDMD1 +#define CR_TDMD1 0x2000 // TDMD1 +#define CR_KEYPAG 0x1000 // turn on par/key +#define CR_DPOLL 0x0800 // disable rx/tx auto polling +#define CR_FDX 0x0400 // full duplex mode +#define CR_ETEN 0x0200 // early tx mode +#define CR_EREN 0x0100 // early rx mode + +// +// Bits in the IMR0 register +// + +#define IMR0_CNTM 0x80 // +#define IMR0_BEM 0x40 // +#define IMR0_RUM 0x20 // +#define IMR0_TUM 0x10 // +#define IMR0_TXEM 0x08 // +#define IMR0_RXEM 0x04 // +#define IMR0_PTXM 0x02 // +#define IMR0_PRXM 0x01 // + +// define imrshadow + +#define IMRShadow 0x5AFF + +// +// Bits in the IMR1 register +// + +#define IMR1_INITM 0x80 // +#define IMR1_SRCM 0x40 // +#define IMR1_NBFM 0x10 // +#define IMR1_PRAIM 0x08 // +#define IMR1_RES0M 0x04 // +#define IMR1_ETM 0x02 // +#define IMR1_ERM 0x01 // + +// +// Bits in the ISR register +// + +#define ISR_INITI 0x8000 // +#define ISR_SRCI 0x4000 // +#define ISR_ABTI 0x2000 // +#define ISR_NORBF 0x1000 // +#define ISR_PKTRA 0x0800 // +#define ISR_RES0 0x0400 // +#define ISR_ETI 0x0200 // +#define ISR_ERI 0x0100 // +#define ISR_CNT 0x0080 // +#define ISR_BE 0x0040 // +#define ISR_RU 0x0020 // +#define ISR_TU 0x0010 // +#define ISR_TXE 0x0008 // +#define ISR_RXE 0x0004 // +#define ISR_PTX 0x0002 // +#define ISR_PRX 0x0001 // + +// +// Bits in the ISR0 register +// + +#define ISR0_CNT 0x80 // +#define ISR0_BE 0x40 // +#define ISR0_RU 0x20 // +#define ISR0_TU 0x10 // +#define ISR0_TXE 0x08 // +#define ISR0_RXE 0x04 // +#define ISR0_PTX 0x02 // +#define ISR0_PRX 0x01 // + +// +// Bits in the ISR1 register +// + +#define ISR1_INITI 0x80 // +#define ISR1_SRCI 0x40 // +#define ISR1_NORBF 0x10 // +#define ISR1_PKTRA 0x08 // +#define ISR1_ETI 0x02 // +#define ISR1_ERI 0x01 // + +// ISR ABNORMAL CONDITION + +#define ISR_ABNORMAL ISR_BE+ISR_RU+ISR_TU+ISR_CNT+ISR_NORBF+ISR_PKTRA + +// +// Bits in the MIISR register +// + +#define MIISR_MIIERR 0x08 // +#define MIISR_MRERR 0x04 // +#define MIISR_LNKFL 0x02 // +#define MIISR_SPEED 0x01 // + +// +// Bits in the MIICR register +// + +#define MIICR_MAUTO 0x80 // +#define MIICR_RCMD 0x40 // +#define MIICR_WCMD 0x20 // +#define MIICR_MDPM 0x10 // +#define MIICR_MOUT 0x08 // +#define MIICR_MDO 0x04 // +#define MIICR_MDI 0x02 // +#define MIICR_MDC 0x01 // + +// +// Bits in the EECSR register +// + +#define EECSR_EEPR 0x80 // eeprom programed status, 73h means programed +#define EECSR_EMBP 0x40 // eeprom embeded programming +#define EECSR_AUTOLD 0x20 // eeprom content reload +#define EECSR_DPM 0x10 // eeprom direct programming +#define EECSR_CS 0x08 // eeprom CS pin +#define EECSR_SK 0x04 // eeprom SK pin +#define EECSR_DI 0x02 // eeprom DI pin +#define EECSR_DO 0x01 // eeprom DO pin + +// +// Bits in the BCR0 register +// + +#define BCR0_CRFT2 0x20 // +#define BCR0_CRFT1 0x10 // +#define BCR0_CRFT0 0x08 // +#define BCR0_DMAL2 0x04 // +#define BCR0_DMAL1 0x02 // +#define BCR0_DMAL0 0x01 // + +// +// Bits in the BCR1 register +// + +#define BCR1_CTSF 0x20 // +#define BCR1_CTFT1 0x10 // +#define BCR1_CTFT0 0x08 // +#define BCR1_POT2 0x04 // +#define BCR1_POT1 0x02 // +#define BCR1_POT0 0x01 // + +// +// Bits in the CFGA register +// + +#define CFGA_EELOAD 0x80 // enable eeprom embeded and direct programming +#define CFGA_JUMPER 0x40 // +#define CFGA_MTGPIO 0x08 // +#define CFGA_T10EN 0x02 // +#define CFGA_AUTO 0x01 // + +// +// Bits in the CFGB register +// + +#define CFGB_PD 0x80 // +#define CFGB_POLEN 0x02 // +#define CFGB_LNKEN 0x01 // + +// +// Bits in the CFGC register +// + +#define CFGC_M10TIO 0x80 // +#define CFGC_M10POL 0x40 // +#define CFGC_PHY1 0x20 // +#define CFGC_PHY0 0x10 // +#define CFGC_BTSEL 0x08 // +#define CFGC_BPS2 0x04 // bootrom select[2] +#define CFGC_BPS1 0x02 // bootrom select[1] +#define CFGC_BPS0 0x01 // bootrom select[0] + +// +// Bits in the CFGD register +// + +#define CFGD_GPIOEN 0x80 // +#define CFGD_DIAG 0x40 // +#define CFGD_MAGIC 0x10 // +#define CFGD_CFDX 0x04 // +#define CFGD_CEREN 0x02 // +#define CFGD_CETEN 0x01 // + +// Bits in RSR +#define RSR_RERR 0x00000001 +#define RSR_CRC 0x00000002 +#define RSR_FAE 0x00000004 +#define RSR_FOV 0x00000008 +#define RSR_LONG 0x00000010 +#define RSR_RUNT 0x00000020 +#define RSR_SERR 0x00000040 +#define RSR_BUFF 0x00000080 +#define RSR_EDP 0x00000100 +#define RSR_STP 0x00000200 +#define RSR_CHN 0x00000400 +#define RSR_PHY 0x00000800 +#define RSR_BAR 0x00001000 +#define RSR_MAR 0x00002000 +#define RSR_RXOK 0x00008000 +#define RSR_ABNORMAL RSR_RERR+RSR_LONG+RSR_RUNT + +//Bits in TSR +#define TSR_NCR0 0x00000001 +#define TSR_NCR1 0x00000002 +#define TSR_NCR2 0x00000004 +#define TSR_NCR3 0x00000008 +#define TSR_COLS 0x00000010 +#define TSR_CDH 0x00000080 +#define TSR_ABT 0x00000100 +#define TSR_OWC 0x00000200 +#define TSR_CRS 0x00000400 +#define TSR_UDF 0x00000800 +#define TSR_TBUFF 0x00001000 +#define TSR_SERR 0x00002000 +#define TSR_JAB 0x00004000 +#define TSR_TERR 0x00008000 +#define TSR_ABNORMAL TSR_TERR+TSR_OWC+TSR_ABT+TSR_JAB+TSR_CRS +#define TSR_OWN_BIT 0x80000000 + +#define CB_DELAY_LOOP_WAIT (10) // 10ms +// enabled mask value of irq + +#define W_IMR_MASK_VALUE 0x1BFF // initial value of IMR + +// Ethernet address filter type +#define PKT_TYPE_DIRECTED 0x0001 // obselete, directed address is always accepted +#define PKT_TYPE_MULTICAST 0x0002 +#define PKT_TYPE_ALL_MULTICAST 0x0004 +#define PKT_TYPE_BROADCAST 0x0008 +#define PKT_TYPE_PROMISCUOUS 0x0020 +//#define PKT_TYPE_LONG 0x2000 +#define PKT_TYPE_RUNT 0x4000 +#define PKT_TYPE_ERROR 0x8000 // accept error packets, e.g. CRC error + +// Loopback mode + +#define NIC_LB_NONE 0x00 // +#define NIC_LB_INTERNAL 0x01 // +#define NIC_LB_PHY 0x02 // MII or Internal-10BaseT loopback + +#define TX_RING_SIZE 2 +#define RX_RING_SIZE 2 +#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer. */ + +/* Transmit and receive descriptors definition */ + +struct rhine_tx_desc +{ + union VTC_tx_status_tag + { + struct + { + unsigned long ncro:1; + unsigned long ncr1:1; + unsigned long ncr2:1; + unsigned long ncr3:1; + unsigned long cols:1; + unsigned long reserve_1:2; + unsigned long cdh:1; + unsigned long abt:1; + unsigned long owc:1; + unsigned long crs:1; + unsigned long udf:1; + unsigned long tbuff:1; + unsigned long serr:1; + unsigned long jab:1; + unsigned long terr:1; + unsigned long reserve_2:15; + unsigned long own_bit:1; + } + bits; + unsigned long lw; + } + tx_status; + + union VTC_tx_ctrl_tag + { + struct + { + unsigned long tx_buf_size:11; + unsigned long extend_tx_buf_size:4; + unsigned long chn:1; + unsigned long crc:1; + unsigned long reserve_1:4; + unsigned long stp:1; + unsigned long edp:1; + unsigned long ic:1; + unsigned long reserve_2:8; + } + bits; + unsigned long lw; + } + tx_ctrl; + + unsigned long buf_addr_1:32; + unsigned long buf_addr_2:32; + +}; + +struct rhine_rx_desc +{ + union VTC_rx_status_tag + { + struct + { + unsigned long rerr:1; + unsigned long crc_error:1; + unsigned long fae:1; + unsigned long fov:1; + unsigned long toolong:1; + unsigned long runt:1; + unsigned long serr:1; + unsigned long buff:1; + unsigned long edp:1; + unsigned long stp:1; + unsigned long chn:1; + unsigned long phy:1; + unsigned long bar:1; + unsigned long mar:1; + unsigned long reserve_1:1; + unsigned long rxok:1; + unsigned long frame_length:11; + unsigned long reverve_2:4; + unsigned long own_bit:1; + } + bits; + unsigned long lw; + } + rx_status; + + union VTC_rx_ctrl_tag + { + struct + { + unsigned long rx_buf_size:11; + unsigned long extend_rx_buf_size:4; + unsigned long reserved_1:17; + } + bits; + unsigned long lw; + } + rx_ctrl; + + unsigned long buf_addr_1:32; + unsigned long buf_addr_2:32; + +}; + + +/* The I/O extent. */ +#define rhine_TOTAL_SIZE 0x80 + +#ifdef HAVE_DEVLIST +struct netdev_entry rhine_drv = + { "rhine", rhine_probe, rhine_TOTAL_SIZE, NULL }; +#endif + +static int rhine_debug = 1; + +/* + Theory of Operation + +I. Board Compatibility + +This driver is designed for the VIA 86c100A Rhine-II PCI Fast Ethernet +controller. + +II. Board-specific settings + +Boards with this chip are functional only in a bus-master PCI slot. + +Many operational settings are loaded from the EEPROM to the Config word at +offset 0x78. This driver assumes that they are correct. +If this driver is compiled to use PCI memory space operations the EEPROM +must be configured to enable memory ops. + +III. Driver operation + +IIIa. Ring buffers + +This driver uses two statically allocated fixed-size descriptor lists +formed into rings by a branch from the final descriptor to the beginning of +the list. The ring sizes are set at compile time by RX/TX_RING_SIZE. + +IIIb/c. Transmit/Receive Structure + +This driver attempts to use a zero-copy receive and transmit scheme. + +Alas, all data buffers are required to start on a 32 bit boundary, so +the driver must often copy transmit packets into bounce buffers. + +The driver allocates full frame size skbuffs for the Rx ring buffers at +open() time and passes the skb->data field to the chip as receive data +buffers. When an incoming frame is less than RX_COPYBREAK bytes long, +a fresh skbuff is allocated and the frame is copied to the new skbuff. +When the incoming frame is larger, the skbuff is passed directly up the +protocol stack. Buffers consumed this way are replaced by newly allocated +skbuffs in the last phase of netdev_rx(). + +The RX_COPYBREAK value is chosen to trade-off the memory wasted by +using a full-sized skbuff for small frames vs. the copying costs of larger +frames. New boards are typically used in generously configured machines +and the underfilled buffers have negligible impact compared to the benefit of +a single allocation size, so the default value of zero results in never +copying packets. When copying is done, the cost is usually mitigated by using +a combined copy/checksum routine. Copying also preloads the cache, which is +most useful with small frames. + +Since the VIA chips are only able to transfer data to buffers on 32 bit +boundaries, the the IP header at offset 14 in an ethernet frame isn't +longword aligned for further processing. Copying these unaligned buffers +has the beneficial effect of 16-byte aligning the IP header. + +IIId. Synchronization + +The driver runs as two independent, single-threaded flows of control. One +is the send-packet routine, which enforces single-threaded use by the +dev->tbusy flag. The other thread is the interrupt handler, which is single +threaded by the hardware and interrupt handling software. + +The send packet thread has partial control over the Tx ring and 'dev->tbusy' +flag. It sets the tbusy flag whenever it's queuing a Tx packet. If the next +queue slot is empty, it clears the tbusy flag when finished otherwise it sets +the 'lp->tx_full' flag. + +The interrupt handler has exclusive control over the Rx ring and records stats +from the Tx ring. After reaping the stats, it marks the Tx queue entry as +empty by incrementing the dirty_tx mark. Iff the 'lp->tx_full' flag is set, it +clears both the tx_full and tbusy flags. + +IV. Notes + +IVb. References + +Preliminary VT86C100A manual from http://www.via.com.tw/ +http://cesdis.gsfc.nasa.gov/linux/misc/100mbps.html +http://cesdis.gsfc.nasa.gov/linux/misc/NWay.html + +IVc. Errata + +The VT86C100A manual is not reliable information. +The chip does not handle unaligned transmit or receive buffers, resulting +in significant performance degradation for bounce buffer copies on transmit +and unaligned IP headers on receive. +The chip does not pad to minimum transmit length. + +*/ + +#define PCI_VENDOR_ID_FET 0x1106 +#define PCI_DEVICE_ID_FET_3043 0x3043 + +/* The rest of these values should never change. */ +#define NUM_TX_DESC 2 /* Number of Tx descriptor registers. */ + +static struct rhine_private +{ + char devname[8]; /* Used only for kernel debugging. */ + const char *product_name; + struct rhine_rx_desc *rx_ring; + struct rhine_tx_desc *tx_ring; + char *rx_buffs[RX_RING_SIZE]; + char *tx_buffs[TX_RING_SIZE]; + + /* temporary Rx buffers. */ + + int chip_id; + int chip_revision; + unsigned short ioaddr; + unsigned int cur_rx, cur_tx; /* The next free and used entries */ + unsigned int dirty_rx, dirty_tx; + /* The saved address of a sent-in-place packet/buffer, for skfree(). */ + struct sk_buff *tx_skbuff[TX_RING_SIZE]; + unsigned char mc_filter[8]; /* Current multicast filter. */ + char phys[4]; /* MII device addresses. */ + unsigned int tx_full:1; /* The Tx queue is full. */ + unsigned int full_duplex:1; /* Full-duplex operation requested. */ + unsigned int default_port:4; /* Last dev->if_port value. */ + unsigned int media2:4; /* Secondary monitored media port. */ + unsigned int medialock:1; /* Don't sense media type. */ + unsigned int mediasense:1; /* Media sensing in progress. */ +} +rhine; + +static struct nic *rhine_probe1 (struct nic *dev, int ioaddr, + int chip_id, int options); +static int QueryAuto (int); +static int ReadMII (int byMIIIndex, int); +static void WriteMII (char, char, char, int); +static void MIIDelay (void); +static void post_reset_delay (void); +static unsigned int read_tick_counter (void); +static void rhine_init_ring (struct nic *dev); +static void rhine_disable (struct nic *nic); +static void rhine_reset (struct nic *nic); +static int rhine_poll (struct nic *nic); +static void rhine_transmit (struct nic *nic, char *d, unsigned int t, + unsigned int s, char *p); + +/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ +static void +rhine_init_ring (struct nic *nic) +{ + struct rhine_private *tp = (struct rhine_private *) nic->priv_data; + int i; + + tp->tx_full = 0; + tp->cur_rx = tp->cur_tx = 0; + tp->dirty_rx = tp->dirty_tx = 0; + + for (i = 0; i < RX_RING_SIZE; i++) + { + + tp->rx_ring[i].rx_status.bits.own_bit = 1; + tp->rx_ring[i].rx_ctrl.bits.rx_buf_size = 1536; + + tp->rx_ring[i].buf_addr_1 = virt_to_bus (tp->rx_buffs[i]); + tp->rx_ring[i].buf_addr_2 = virt_to_bus (&tp->rx_ring[i + 1]); + // printf("[%d]buf1=%x,buf2=%x",i,tp->rx_ring[i].buf_addr_1,tp->rx_ring[i].buf_addr_2); + } + /* Mark the last entry as wrapping the ring. */ + // tp->rx_ring[i-1].rx_ctrl.bits.rx_buf_size =1518; + tp->rx_ring[i - 1].buf_addr_2 = virt_to_bus (&tp->rx_ring[0]); + //printf("[%d]buf1=%x,buf2=%x",i-1,tp->rx_ring[i-1].buf_addr_1,tp->rx_ring[i-1].buf_addr_2); + + /* The Tx buffer descriptor is filled in as needed, but we + do need to clear the ownership bit. */ + + for (i = 0; i < TX_RING_SIZE; i++) + { + + tp->tx_ring[i].tx_status.lw = 0; + tp->tx_ring[i].tx_ctrl.lw = 0x00e08000; + tp->tx_ring[i].buf_addr_1 = virt_to_bus (tp->tx_buffs[i]); + tp->tx_ring[i].buf_addr_2 = virt_to_bus (&tp->tx_ring[i + 1]); + // printf("[%d]buf1=%x,buf2=%x",i,tp->tx_ring[i].buf_addr_1,tp->tx_ring[i].buf_addr_2); + } + + tp->tx_ring[i - 1].buf_addr_2 = virt_to_bus (&tp->tx_ring[0]); + // printf("[%d]buf1=%x,buf2=%x",i,tp->tx_ring[i-1].buf_addr_1,tp->tx_ring[i-1].buf_addr_2); +} + +int +QueryAuto (int ioaddr) +{ + int byMIIIndex; + int MIIReturn; + int FDXFlag; + byMIIIndex = 0x06; + MIIReturn = ReadMII (byMIIIndex, ioaddr); + if ((MIIReturn & 0x01) == 0) + { + FDXFlag = 0; + return FDXFlag; + } + + byMIIIndex = 0x05; + MIIReturn = ReadMII (byMIIIndex, ioaddr); + if ((MIIReturn & 0x0140) == 0) + { + FDXFlag = 0; + return FDXFlag; + } + + byMIIIndex = 0x04; + MIIReturn = ReadMII (byMIIIndex, ioaddr); + if ((MIIReturn & 0x0140) == 0) + { + FDXFlag = 0; + return FDXFlag; + } + + FDXFlag = 1; + return FDXFlag; + +} + +int +ReadMII (int byMIIIndex, int ioaddr) +{ + int ReturnMII; + char byMIIAdrbak; + char byMIICRbak; + char byMIItemp; + + byMIIAdrbak = inb (byMIIAD); + byMIICRbak = inb (byMIICR); + outb (byMIICRbak & 0x7f, byMIICR); + MIIDelay (); + + outb (byMIIIndex, byMIIAD); + MIIDelay (); + + outb (inb (byMIICR) | 0x40, byMIICR); + + byMIItemp = inb (byMIICR); + byMIItemp = byMIItemp & 0x40; + + while (byMIItemp != 0) + { + byMIItemp = inb (byMIICR); + byMIItemp = byMIItemp & 0x40; + } + MIIDelay (); + + ReturnMII = inw (wMIIDATA); + + outb (byMIIAdrbak, byMIIAD); + outb (byMIICRbak, byMIICR); + MIIDelay (); + + return (ReturnMII); + +} + +void +WriteMII (char byMIISetByte, char byMIISetBit, char byMIIOP, int ioaddr) +{ + int ReadMIItmp; + int MIIMask; + char byMIIAdrbak; + char byMIICRbak; + char byMIItemp; + + + byMIIAdrbak = inb (byMIIAD); + + byMIICRbak = inb (byMIICR); + outb (byMIICRbak & 0x7f, byMIICR); + MIIDelay (); + outb (byMIISetByte, byMIIAD); + MIIDelay (); + + outb (inb (byMIICR) | 0x40, byMIICR); + + byMIItemp = inb (byMIICR); + byMIItemp = byMIItemp & 0x40; + + while (byMIItemp != 0) + { + byMIItemp = inb (byMIICR); + byMIItemp = byMIItemp & 0x40; + } + MIIDelay (); + + ReadMIItmp = inw (wMIIDATA); + MIIMask = 0x0001; + MIIMask = MIIMask << byMIISetBit; + + + if (byMIIOP == 0) + { + MIIMask = ~MIIMask; + ReadMIItmp = ReadMIItmp & MIIMask; + } + else + { + ReadMIItmp = ReadMIItmp | MIIMask; + + } + outw (ReadMIItmp, wMIIDATA); + MIIDelay (); + + outb (inb (byMIICR) | 0x20, byMIICR); + byMIItemp = inb (byMIICR); + byMIItemp = byMIItemp & 0x20; + + while (byMIItemp != 0) + { + byMIItemp = inb (byMIICR); + byMIItemp = byMIItemp & 0x20; + } + MIIDelay (); + + outb (byMIIAdrbak & 0x7f, byMIIAD); + outb (byMIICRbak, byMIICR); + MIIDelay (); + +} + +void +MIIDelay (void) +{ + int i; + for (i = 0; i < 0x7fff; i++) + { + inb (0x61); + inb (0x61); + inb (0x61); + inb (0x61); + } +} + +static unsigned int +read_tick_counter (void) +{ + unsigned int current_time = 0; + + /* Program 8254 to latch T0's count */ + outb (0x43, 0x6); + /* Read the counter */ + current_time = inb (0x40); + current_time = current_time | (inb (0x40) << 8); + return current_time; + +} + +static void +post_reset_delay (void) +{ + + unsigned int initial_time, current_time; + + /* need to wait 10 mini seconds */ + + initial_time = read_tick_counter (); + + /* 8254 time tick gives 419 nsec per count. We have to delay a delta + count of 10,000,000ns/419ns = 23866. We will round it up to 23866 + */ + + while (1) + { + current_time = read_tick_counter (); + if ((initial_time - current_time) >= 23866) + { /* 10 mini second */ + return; + } + } +} + +struct nic * +rhine_probe (struct nic *nic, unsigned short *probeaddrs, + struct pci_device *pci) +{ + unsigned char pci_latency; + unsigned short pci_command; + + if (!pci->ioaddr) + return NULL; + nic = rhine_probe1 (nic, pci->ioaddr, 0, -1); + + if (nic) + { + /* Get and check the bus-master and latency values. */ + pcibios_read_config_word (0, pci->devfn, PCI_COMMAND, &pci_command); + if (!(pci_command & PCI_COMMAND_MASTER)) + { + printf (" PCI Master Bit has not been set! Setting...\n"); + pci_command |= PCI_COMMAND_MASTER; + pcibios_write_config_word (0, pci->devfn, PCI_COMMAND, + pci_command); + } + pcibios_read_config_byte (0, pci->devfn, PCI_LATENCY_TIMER, + &pci_latency); + if (pci_latency < 10) + { + printf (" PCI latency timer (CFLT) is unreasonably low " + "at %d. Setting to 64 clocks.\n", pci_latency); + pcibios_write_config_byte (0, pci->devfn, PCI_LATENCY_TIMER, 64); + } + else if (rhine_debug > 1) + printf (" PCI latency timer (CFLT) is %#x.\n", pci_latency); + } + nic->poll = rhine_poll; + nic->transmit = rhine_transmit; + nic->reset = rhine_reset; + nic->disable = rhine_disable; + rhine_reset (nic); + + return nic; +} + +static struct nic * +rhine_probe1 (struct nic *nic, int ioaddr, int chip_id, int options) +{ + struct rhine_private *tp; + static int did_version = 0; /* Already printed version info. */ + int i, x; + int FDXFlag; + int byMIIvalue, LineSpeed, MIICRbak; + + if (rhine_debug > 0 && did_version++ == 0) + printf (version); + printf ("IO adress %x ", ioaddr); + /* Perhaps this should be read from the EEPROM? */ + for (i = 0; i < 6; i++) + nic->node_addr[i] = inb (byPAR0 + i); + printf ("Ethernet Address: "); + for (i = 0; i < 5; i++) + printf ("0x%b:", nic->node_addr[i]); + printf ("0x%b\n", nic->node_addr[i]); + + /* restart MII auto-negotiation */ + WriteMII (0, 9, 1, ioaddr); + printf ("Analyzing Media type,this will take several seconds........"); + for (i = 0; i < 5; i++) + { + for (x = 0; x < 100; x++) + post_reset_delay (); + if (ReadMII (1, ioaddr) & 0x0020) + break; + } + printf ("OK\n"); + /* query MII to know LineSpeed,duplex mode */ + byMIIvalue = inb (ioaddr + 0x6d); + LineSpeed = byMIIvalue & MIISR_SPEED; + if (LineSpeed == 1) + { + printf ("Linespeed=10Mbs"); + } + else + { + printf ("Linespeed=100Mbs"); + } + FDXFlag = QueryAuto (ioaddr); + if (FDXFlag == 1) + { + printf (" Fullduplex\n"); + outw (CR_FDX, byCR0); + } + else + { + printf (" Halfduplex\n"); + } + + /* set MII 10 FULL ON */ + WriteMII (17, 1, 1, ioaddr); + + /* turn on MII link change */ + MIICRbak = inb (byMIICR); + outb (MIICRbak & 0x7F, byMIICR); + MIIDelay (); + outb (0x41, byMIIAD); + MIIDelay (); + + // while((inb(byMIIAD)&0x20)==0) + // ; + outb (MIICRbak | 0x80, byMIICR); + + nic->priv_data = &rhine; + tp = &rhine; + tp->chip_id = chip_id; + tp->ioaddr = ioaddr; + tp->phys[0] = -1; + + /* The lower four bits are the media type. */ + if (options > 0) + { + tp->full_duplex = (options & 16) ? 1 : 0; + tp->default_port = options & 15; + if (tp->default_port) + tp->medialock = 1; + } + return nic; +} + +static void +rhine_disable (struct nic *nic) +{ + printf ("rhine disable\n"); + /* nothing for the moment */ +} + +/************************************************************************** +ETH_RESET - Reset adapter +***************************************************************************/ +static void +rhine_reset (struct nic *nic) +{ + struct rhine_private *tp = (struct rhine_private *) nic->priv_data; + int ioaddr = tp->ioaddr; + int i, j; + int FDXFlag, CRbak; + int rx_ring_tmp, rx_ring_tmp1; + int tx_ring_tmp, tx_ring_tmp1; + int rx_bufs_tmp, rx_bufs_tmp1; + int tx_bufs_tmp, tx_bufs_tmp1; + + static char buf1[RX_RING_SIZE * PKT_BUF_SZ + 32]; + static char buf2[RX_RING_SIZE * PKT_BUF_SZ + 32]; + static char desc1[TX_RING_SIZE * sizeof (struct rhine_tx_desc) + 32]; + static char desc2[TX_RING_SIZE * sizeof (struct rhine_tx_desc) + 32]; + + // printf ("rhine_reset\n"); + /* Soft reset the chip. */ + //outb(CmdReset, ioaddr + ChipCmd); + + tx_bufs_tmp = (int) buf1; + tx_ring_tmp = (int) desc1; + rx_bufs_tmp = (int) buf2; + rx_ring_tmp = (int) desc2; + + /* tune RD TD 32 byte alignment */ + rx_ring_tmp1 = (int) virt_to_bus ((char *) rx_ring_tmp); + j = (rx_ring_tmp1 + 32) & (~0x1f); + // printf ("txring[%d]", j); + tp->rx_ring = (struct rhine_rx_desc *) bus_to_virt (j); + + tx_ring_tmp1 = (int) virt_to_bus ((char *) tx_ring_tmp); + j = (tx_ring_tmp1 + 32) & (~0x1f); + tp->tx_ring = (struct rhine_tx_desc *) bus_to_virt (j); + // printf ("rxring[%x]", j); + + + tx_bufs_tmp1 = (int) virt_to_bus ((char *) tx_bufs_tmp); + j = (int) (tx_bufs_tmp1 + 32) & (~0x1f); + tx_bufs_tmp = (int) bus_to_virt (j); + // printf ("txb[%x]", j); + + rx_bufs_tmp1 = (int) virt_to_bus ((char *) rx_bufs_tmp); + j = (int) (rx_bufs_tmp1 + 32) & (~0x1f); + rx_bufs_tmp = (int) bus_to_virt (j); + // printf ("rxb[%x][%x]", rx_bufs_tmp1, j); + + for (i = 0; i < RX_RING_SIZE; i++) + { + tp->rx_buffs[i] = (char *) rx_bufs_tmp; + // printf("r[%x]",tp->rx_buffs[i]); + rx_bufs_tmp += 1536; + } + + for (i = 0; i < TX_RING_SIZE; i++) + { + tp->tx_buffs[i] = (char *) tx_bufs_tmp; + // printf("t[%x]",tp->tx_buffs[i]); + tx_bufs_tmp += 1536; + } + + // printf ("init ring"); + rhine_init_ring (nic); + /*write TD RD Descriptor to MAC */ + outl (virt_to_bus (tp->rx_ring), dwCurrentRxDescAddr); + outl (virt_to_bus (tp->tx_ring), dwCurrentTxDescAddr); + + /* close IMR */ + outw (0x0000, byIMR0); + + /* software reset */ + outb (CR1_SFRST, byCR1); + MIIDelay (); + /* set TCR RCR threshold */ + outb (0x06, byBCR0); + outb (0x00, byBCR1); + outb (0x2c, byRCR); + outb (0x60, byTCR); + /* Set Fulldupex */ + FDXFlag = QueryAuto (ioaddr); + if (FDXFlag == 1) + { + outb (CFGD_CFDX, byCFGD); + outw (CR_FDX, byCR0); + } + + /*KICK NIC to WORK */ + CRbak = inw (byCR0); + CRbak = CRbak & 0xFFFB; /*not CR_STOP */ + outw ((CRbak | CR_STRT | CR_TXON | CR_RXON | CR_DPOLL), byCR0); + + /*set IMR to work */ + outw (IMRShadow, byIMR0); +} + +static int +rhine_poll (struct nic *nic) +{ + struct rhine_private *tp = (struct rhine_private *) nic->priv_data; + int ioaddr = tp->ioaddr; + int rxstatus, good = 0;; + + if (tp->rx_ring[tp->cur_rx].rx_status.bits.own_bit == 0) + { + rxstatus = tp->rx_ring[tp->cur_rx].rx_status.lw; + if ((rxstatus & 0x0300) != 0x0300) + { + printf("rhine_poll: bad status\n"); + } + else if (rxstatus & (RSR_ABNORMAL)) + { + printf ("rxerr[%x]\n", rxstatus); + } + else + good = 1; + + if (good) + { + nic->packetlen = tp->rx_ring[tp->cur_rx].rx_status.bits.frame_length; + memcpy (nic->packet, tp->rx_buffs[tp->cur_rx], nic->packetlen); + // printf ("Packet RXed\n"); + } + tp->rx_ring[tp->cur_rx].rx_status.bits.own_bit = 1; + tp->cur_rx++; + tp->cur_rx = tp->cur_rx % RX_RING_SIZE; + } + return good; +} + +static void +rhine_transmit (struct nic *nic, + char *d, unsigned int t, unsigned int s, char *p) +{ + struct rhine_private *tp = (struct rhine_private *) nic->priv_data; + int ioaddr = tp->ioaddr; + int entry; + unsigned char CR1bak; + + //printf ("rhine_transmit\n"); + /* setup ethernet header */ + + + /* Calculate the next Tx descriptor entry. */ + entry = tp->cur_tx % TX_RING_SIZE; + + memcpy ((void *) tp->tx_buffs[entry], d, ETHER_ADDR_SIZE); /* dst */ + memcpy ((void *) tp->tx_buffs[entry] + ETHER_ADDR_SIZE, nic->node_addr, ETHER_ADDR_SIZE); /* src */ + *((char *) tp->tx_buffs[entry] + 12) = t >> 8; /* type */ + *((char *) tp->tx_buffs[entry] + 13) = t; + memcpy ((void *) tp->tx_buffs[entry] + ETHER_HDR_SIZE, p, s); + s += ETHER_HDR_SIZE; + while (s < ETH_MIN_PACKET) + *((char *) tp->tx_buffs[entry] + ETHER_HDR_SIZE + (s++)) = 0; + + tp->tx_ring[entry].tx_ctrl.bits.tx_buf_size = ETHER_HDR_SIZE + s; + + tp->tx_ring[entry].tx_status.bits.own_bit = 1; + + + CR1bak = inb (byCR1); + + CR1bak = CR1bak | CR1_TDMD1; + //printf("tdsw=[%x]",tp->tx_ring[entry].tx_status.lw); + //printf("tdcw=[%x]",tp->tx_ring[entry].tx_ctrl.lw); + //printf("tdbuf1=[%x]",tp->tx_ring[entry].buf_addr_1); + //printf("tdbuf2=[%x]",tp->tx_ring[entry].buf_addr_2); + //printf("td1=[%x]",inl(dwCurrentTDSE0)); + //printf("td2=[%x]",inl(dwCurrentTDSE1)); + //printf("td3=[%x]",inl(dwCurrentTDSE2)); + //printf("td4=[%x]",inl(dwCurrentTDSE3)); + + outb (CR1bak, byCR1); + tp->cur_tx++; + + //outw(IMRShadow,byIMR0); + //dev_kfree_skb(tp->tx_skbuff[entry], FREE_WRITE); + //tp->tx_skbuff[entry] = 0; +} + +// EOF via-rhine.c