/*********************************************************************** * s3c.c : Samsung S3C4530A ethernet driver for linux S3C4530A Communication Processor Copyright (c) Copyright 2001-2002 Arcturus Networks Inc. by MaTed mated@arcturusnetworks.com based on isa-skeleton: A network driver outline for linux. Written 1993-94 by Donald Becker. (c) Copyright 2001 Arcturus Networks Inc. Copyright 1993 United States Government as represented by the Director, National Security Agency. This software may be used and distributed according to the terms of the GNU Public License, incorporated herein by reference. The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O Center of Excellence in Space Data and Information Sciences Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771 This file is the first go at creating a driver for the ethernet driver used by the S3C SOC. The core is the 10/100 MB Sierra Research core for which there doesn't appear to be a driver for the Linux operating system. Known Limitations: - Because the S3c controller is so "intelligent", it manages the buffer list on it's own. Therefore you create the Buffer List for the controller without knowing the length required. A maximum MTU buffer is pre-allocated regardless of the actual requirements. Either the controller firmware can be changed to pick buffers whose size is closer to the actual size required, or we can memcpy (ugh!!!) to a smaller buffer. - module support is either non-existant or not tested (it was a SOC afterall) *************************************************************************/ /************************************************************************ * modification history * -------------------- * * 08/16/02 Michael Durrant * 10/29/01 MaTed (Ted Ma) First go * - PLEASE set TABS==4 ************************************************************************/ /* Sources: - source is available from Arcturus Networks Inc. www.arcturusnetworks.com Finally, keep in mind that the Linux kernel is has an API, not ABI. Proprietary object-code-only distributions are not permitted under the GPL. */ #include "s3c4530.h" #ifdef CONFIG_ETH_S3C4530 /* skip the entire file if not configured */ static const char *version = "s3c4530.c: Network driver by Arcturus Networks Inc. \n"; /************* DEBUG **********/ #define VERBOSE y #ifdef VERBOSE UINT verbose = 5; #endif #define DEBUG_PHY_DUMP 0 #define PHY_DEBUG 0 #define DEBUG_RX_DUMP 0 // for dumping Rx Structures // #define DumpJiffies(x) printk("----s3c:%s Jiffies=[%x]\n",x, jiffies); #define DEBUG_SINT 0 // debugging interrupts (s3c ones) #if DEBUG_SINT #define SINT(x, y) printk("S3INT %s=%0x\n", x, y); #else #define SINT(x, y) #endif #define DEBUG_INCTXPTR 0 // debugging incrementing txPtr #if DEBUG_INCTXPTR S3C_MAC * PrivPtr; // for debugging when dev / priv structs NA #define CHECK_ITXPTR(instr) \ if (((int) ReadCSR( TxPtr)) != (int) PrivPtr->qTx.first) { \ printk( "DEBUG_INCTXPTR: %s:read %x ", \ (char *) #instr, (int) ReadCSR( TxPtr)); \ if (0 == (0x3 & ((int) ReadCSR(TxPtr)))) \ printk( "DataPtr=0x%x\n", \ (int) (((TxFD *) (ReadCSR( TxPtr)))->FDDataPtr)); \ else \ printk( "DataPtr is not word aligned !! \n"); \ printk( " ***: setting to %x\n", \ (int) WriteCSR(TxPtr, PrivPtr->qTx.first)); } #else #define CHECK_ITXPTR(x) #endif #define MATED_DEBUG 0 /* Tracking down bad "grow" ? Mostly verify it is grow that kills things ...MaTed---*/ #if MATED_DEBUG #define SMEM_CHECK( index) \ if (((*((int *)(0x7cfea8))) & 3) != 0) { \ printk("\n @@@@* * MEMORY STOMPED!\n"); \ } \ printk(" * * Memory check! %s\n ---- 0x%x-0x7cfea8 = 0x%x\n", \ #index, (int) ((int *)(0x7cfea8)), (*((int *)(0x7cfea8)))); \ printk(" FILE [%s] FUNCTION [%s] - LINE #%d\n", \ __FILE__, __FUNCTION__, __LINE__); \ (*((int *)(0x7cfea8))) = (*((int*)(0x7cfea8))) & ~3; \ printk(" @* Memory set to 0x%x\n", (*((int *)(0x7cfea8))) ); #define SMEM_CHECKQ( index) \ if (((*((int *)(0x7cfea8))) & 3) != 0) { \ printk("\n @@@@* * MEMORY STOMPED!\n"); \ printk(" * * Memory check! %s 0x%x-0x7cfea8 = 0x%x\n", \ #index, (int) ((int *)(0x7cfea8)), (*((int *)(0x7cfea8)))); \ printk(" FILE [%s] FUNCTION [%s] - LINE #%d\n", \ __FILE__, __FUNCTION__, __LINE__); \ (*((int *)(0x7cfea8))) = (*((int*)(0x7cfea8))) & ~3; \ printk(" @* Memory set to 0x%x\n", (*((int *)(0x7cfea8))) ); \ } #else #define SMEM_CHECK( index) #endif /*********** end debug *******/ /* A bit of an introduction for the algorithms used herein - implemented with zero copy. - receive - skb's are preallocated with a maximum sized buffer and the data is dma'd into the preallocated buffer. We may add a section to copy the buffer down so we don't use as much memory resources - but that's still to be implemented - Has the disadvantage of potentially thrashing memory by alloc / dealloc SKB's. - transmit - skb's are attached to the ring buffer. When the MAC either transmits the packet or discards it (we don't guarentee delivery at this level), the skb is freed - all rx buffers are pre-allocated when the MAC is opened - we will use one interrupt per packet sent or received *****FIXME mated - we cold drop the TX interrupt - just remember to enable if backpressure stops tx_send_packet */ /* * The name of the card. Is used for messages and in the requests for * io regions, irqs and dma channels */ static const char* cardname = "s3c"; /* * Setup a base address where offset < 4024 (2**12 - max * immediate offset for ARM) */ static CSR * const baseAddr = ((CSR *) ((BDMA_BASE) | 0x04000000)); // uncached /********************************************** Compile time directives ***********************************************/ #define NO_INCLUDE_MAC_RX_INT 1 // do not include (or set up) the // MAC receive interrupt #define ENABLE_DESTRUCTOR 0 // use destructor to set /************************************************************************ "Knobs" that adjust features and parameters. - Set the copy breakpoint for the copy-only-tiny-frames scheme. - Setting to > 1512 effectively disables this feature. *** NOT implemented *** FixMe ...MaTed *******************************************************************/ static const int rx_copybreak = 1536; /* Allow setting MTU to a larger size, bypassing the normal ethernet setup. */ /**** Currently unused ...MaTed--- ****/ static const int mtu = 1500; /* Maximum events (Rx packets, etc.) to handle at each interrupt. */ // We might need this back - right now handle as many as buffers allow // *** Hard coded in s3c_tx and s3c_rx ...MaTed--- //static int max_interrupt_work = 32; static UINT net_debug = NET_DEBUG; /****************************************************** * Internal S3C data structures ******************************************************/ UINT32 numS3cMacs = 0; // number of macs char * MacName[] // = {"ethaddr0", "ethaddr1", "ethaddr2", "unknown"}; = {"HWADDR0", "HWADDR1", "HWADDR2", "unknown"}; /************************************************************************ * Internal Function Declarations - all static ************************************************************************/ static _INLINE_ int PHYWait( void); static int WritePhy( UINT32 addr, UINT32 reg, UINT32 data); static int ReadPhy( UINT32 addr, UINT32 reg, UINT32 * data); static _INLINE_ int txFull( ND dev); static void SetupPHY( UINT32 mode); static ULONG PHYCheckCable( void); static int PhyInit( ND dev); static ULONG SetupTxFDs( ND dev ); static ULONG SetupRxFDs( ND dev); static _INLINE_ void EnableInt( void ); static _INLINE_ void DisableInt( void); static _INLINE_ void EnableTxRx( void ); static _INLINE_ void DisableTxRx( void ); static int Reset( void); static void SetupRegistersTx( void); static void SetupRegistersRx( void); static _INLINE_ void SetUpTxFrmPtrRegister( ND dev); static _INLINE_ ULONG TxRxInit( ND dev); static int SetUpARC( ND dev, void * addr); #if ENABLE_IOCTL static _INLINE_ void SetSpeed100( void); static _INLINE_ void SetSpeed10( void); #endif // ENABLE_IOCTL // ***** FIXME ** InvalidateCacheLIne should move to other file ...MaTed--- _INLINE_ void InvalidateCacheLine( UINT32 bufaddr); _INLINE_ void InvalidateBuffer( void * bufaddr, UINT length); _INLINE_ void FlushCache(void); /* Index to functions, as function prototypes. */ int __init s3c_probe( ND dev); static int s3c_InitVars( ND dev); static _INLINE_ int s3c_request_irq_mem( ND dev); static int s3c_probe1( ND dev); static _INLINE_ int s3cLogTxMACError( ND dev, int status); static _INLINE_ int s3cLogRxBdmaError( ND dev, int status); static void s3c_tx_timeout( ND dev); static _INLINE_ int s3cInitPriv( ND dev ); static int s3c_open( ND dev); static int s3c_send_packet( struct sk_buff *skb, ND dev); /* four interrupt routines - with one helper */ static void s3c_MacTx( int irq, void *dev_id, struct pt_regs * regs); static _INLINE_ void TxPktCompletion( ND dev); static void s3c_BdmaTx( int irq, void *dev_id, struct pt_regs * regs); #if ! NO_INCLUDE_MAC_RX_INT static void s3c_MacRx( int irq, void *dev_id, struct pt_regs * regs); #endif static void s3c_BdmaRx( int irq, void *dev_id, struct pt_regs * regs); static void s3cAddRxSKBs( ND dev, UINT32 MaxIn, UINT32 MinIn ); #if DEBUG_RX_DUMP static void dumpRx( S3C_MAC * priv, char * where); static void RXFDS_Dump( RxFDS * fds, char * str); #endif #if ENABLE_DESTRUCTOR static void s3cDestructor( struct sk_buff *skb); #endif static int s3c_close( ND dev); static struct net_device_stats *s3c_get_stats( ND dev); static void set_multicast_list( ND dev); static void HardwareSetFilter( ND dev); #if DEBUG_PHY_DUMP static void DumpPhyReg( void); #endif /************************************ * Temporary routines ************************************/ #define S3C_TEMP_ROUTINES 0 #if S3C_TEMP_ROUTINES static int get_ethernet_addr(char * MatchName, char * addr); #endif /************************************************************************ * Utility routines ************************************************************************/ // NOTE: assumes base is already KSEG1 ???? #if 0 #define WriteCSR(base, reg, value) \ ((volatile UINT32)(((CSR *)( base))->reg) = (value)) #define ReadCSR(base, reg) ((volatile UINT32)(((CSR *) (base))->reg)) #else #define WriteCSR(reg, value) (baseAddr->reg = (UINT32) (value)) #define ReadCSR( reg) (baseAddr->reg) #endif #if 0 // ...MaTed--- don't need this here /*************************************************************************** * Interface to boot environment variables ***************************************************************************/ int errno; // the macros want this - not friendly ...MaTed--- // _bsc1(char *, readbenv, int, a) //_bsc1(char *, getbenv, char *, a) // _bsc1(int, setbenv, char *, a) #endif /*************************************************************************** * Function Name: txFull * * Purpose: Returns 0 if tx Queue in not full, !=0 if full * * Inputs: ND dev ***************************************************************************/ static _INLINE_ int txFull( ND dev) { return ((TX_NUM_FD - 1) <= ((S3C_MAC *)dev->priv)->qTx.num); } /************************************************************************* * PHYWait - Waits for Station Management to become non-busy * * This inline routine is a helper to wait for Station Management to * become non-busy - ie for the command to complete * * returns S3c_SUCCESS if succesfully written * other returns indicate timeout or other error *************************************************************************/ static _INLINE_ int PHYWait( void) { UINT32 i = 0; while (( ReadCSR( SmCtl)) & MStBusy) { if ( i++ >= PHY_BUSYLOOP_COUNT) { #ifdef VERBOSE if ( verbose) { printk( "S3C4530-PHY: *** BUSY Timeout! ***\n"); } #endif return( S3C_TIMEOUT); } mdelay( 10); // 10 millisecond delay per loop } return( S3C_SUCCESS); } /************************************************************************* * WritePHY - writes data to phy972 * * This routine is a helper to write Phy data * * data is written to reg of the PHY at addr * returns S3c_SUCCESS if succesfully written * other returns indicate timeout or other error *************************************************************************/ static int WritePhy( UINT32 addr, UINT32 reg, UINT32 data) { int rc; if ( S3C_SUCCESS != ( rc= PHYWait())) return( rc); WriteCSR( SmData, data); /* set data */ WriteCSR( SmCtl, MStWr | MStBusy | reg | addr << 5); // send WRITE cmd #if PHY_DEBUG printk( "S3C4530-PHY: wrote 0x%08x to PHY%d register 0x%x\n", (int) data, (int) addr, (int) reg); #endif return( PHYWait()); } /************************************************************************* * ReadPHY - reads data from phy972 * * This routine is a helper to read Phy data * * data is read from reg of the PHY at addr * returns S3C_SUCCESS if succesfully read * other returns indicate timeout or other error *************************************************************************/ static int ReadPhy( UINT32 addr, UINT32 reg, UINT32 * data) { int rc; // wait for any previous command to complete if ( S3C_SUCCESS != ( rc= PHYWait())) return( rc); WriteCSR( SmCtl, MStBusy | reg | addr << 5); // send READ cmd // wait for read command to complete if ( S3C_SUCCESS != ( rc= PHYWait())) return( rc); *data = ReadCSR( SmData); /* get data */ #if PHY_DEBUG printk( "S3C4530-PHY: read 0x%08x from PHY%d register 0x%x\n", (int) *data, (int) addr, (int) reg); #endif return (rc); /* return success */ } /************************************************************************* * SetupPHY - initialize and configure the PHY device. * * This routine initializes and configures the PHY device * ***** NOTE: the LED values used for initialization are overwritten * * LevelOne LXT972 * Loopback=disabled * Speed = 100/10 * Auto-Negotiation Restart = mode enabled --- FIXME NOTE: Assumption that * -- FIXME Negotiation had not * - FIXME previously run ??? * Power Down = 0 normal * Isolate = 0 normal * Restart Auto-Negotiation = 0 * Duplex = 1 Full / 0 Half * Collision Test = 0 Disabled * **** parm mode values ***** * For Full Dup 10Mbps: 0x0100 * For Half Dup 10Mbps: 0x0000 * For Full Dup 100Mbps: 0x2100 * For Half Dup 100Mbps: 0x2000 * For Auto Negotiate: 0x1000 - half duplex 10 advertised * For Auto Negotiate: 0x1100 - full/half duplex 10 advertised * For Auto Negotiate: 0x3000 - half duplex 10/100 advertised * For Auto Negotiate: 0x3100 - full/half duplex 10/100 advertised * For Restart AutoNeg: 0x0200 - current values retained * *************************************************************************/ static void SetupPHY( UINT32 mode) { UINT32 j; UINT32 data; /* First reset the PHY */ WritePhy( PHYAddr, PHYCtl, PHYCtlReset); /* * Read the Reset bit(0.15) of the PHY to see if the reset is completed. */ for ( j = 0; j < PHY_RESETLOOP_COUNT; j++) { if (S3C_SUCCESS != ReadPhy( PHYAddr, PHYCtl, &data)) { #ifdef VERBOSE printk( "S3C4530-PHY: RESET command not accepted! ***\n"); #endif return; } if ( (data & PHY_RESET) == 0) { #ifdef VERBOSE if ( verbose) { printk( "S3C-PHY reset\n"); } #endif break; } mdelay( 10); // 10 mS } mdelay( 10); // just in case // change the LED to right lin/activity LEFT->speed WritePhy( PHYAddr, PHYLedCfg, PHYLEDCFG); #if PHY_DEBUG printk( "S3C4530-PHY: setting up mode-%08x\n", (int) mode); #endif if (S3C_SUCCESS != ReadPhy( PHYAddr, PHYCtl, &data)) { #ifdef VERBOSE printk( "S3C4530-PHY: Read mode command not accepted! ***\n"); #endif return; } #if PHY_DEBUG printk( "S3C4530-PHY: Original mode-%08x\n", (int) data); #endif /* * Rules for mode * if b12 is set - autonegotiation is enabled -> for the other bits set * - advertizing will match * if b12 is reset - no autonegotian, adverting is not changed * b13 set to 100 MbS (includes 10 if autoneg) * b13 reset -> 10 Mbs only * b8 set FULL DUPLEX (autonegiate -> HALF Duplex included) * b8 reset Half Duplex */ if (0 == (mode & PHY_CONTROL_AUTONEG)) { // No autonegotiate - just write mode and done if (S3C_SUCCESS != WritePhy( PHYAddr, PHYCtl, mode)) { #ifdef VERBOSE printk( "S3C4530-PHY: Mode change failed for Control\n"); #endif } return; // no autonegotiation - no advertising } #if 1 // Advertise to the world what we will negotiate if (S3C_SUCCESS != ReadPhy( PHYAddr, PHYANegAd, &data)) { #ifdef VERBOSE printk( "S3C4530-PHY: Read Negotiate Advertize not accepted! ***\n"); #endif return; } data &= ~PHYANegAdMask; switch (mode & (PHY_CONTROL_SELECT_SPEED | PHY_CONTROL_DUPLEXMODE)) { case PHY_CONTROL_SELECT_SPEED | PHY_CONTROL_DUPLEXMODE: data |= PHYB8 | PHYB7 | PHYB6 | PHYB5; break; case PHY_CONTROL_SELECT_SPEED: data |= PHYB7 | PHYB5; break; case PHY_CONTROL_DUPLEXMODE: data |= PHYB6 | PHYB5; break; case 0: data |= PHYB5; break; } #else // put in everything data = PHYLEDANEG; #endif if (S3C_SUCCESS != WritePhy( PHYAddr, PHYANegAd, data)) { #ifdef VERBOSE printk( "S3C4530-PHY: Mode change failed for Advertise\n"); #endif return; } /* since we are auto negotiating, force a restart */ mode |= PHY_CONTROL_RESTART_AUTONEG; // just in case if (S3C_SUCCESS != WritePhy( PHYAddr, PHYCtl, mode)) { #ifdef VERBOSE printk( "S3C4530-PHY: Autonegotiate failed to restart\n"); #endif return; } #if 1 // let the next routine check for the end of the negotiation j = 0; ReadPhy( PHYAddr, PHYCtl, &data); while ( data & PHY_CONTROL_RESTART_AUTONEG) { if ( j++ >= PHY_NEGLOOP_COUNT) { #ifdef VERBOSE if ( verbose) { printk( "S3C4530-PHY: * Restart AutoNegotiate did not complete "); printk( " Will check again later ***\n"); } #endif return; } mdelay( 10); // 10 millisecond delay per loop ReadPhy( PHYAddr, PHYCtl, &data); } #endif return; } /************************************************************************* * PHYCheckCable - Check for Cable connection to the PHY 972. * * * RETURNS: S3C_SUCCESS, or S3C_FAIL * Needs to be modified according to the PHY, and * needs to be modified according to the SPEC of Level1 972 **************************************************************************/ static UINT32 PHYCheckCable( void) { UINT32 data; if (S3C_SUCCESS != ReadPhy( PHYAddr, PHYStat2, &data)) { #ifdef VERBOSE printk( "S3C-CheckCable: Read status 2 command not accepted! ***\n"); #endif return (S3C_FAIL); } #if PHY_DEBUG printk("S3C-CheckCable: Status 2 = 0x%x\n", (int) data); #endif if (!(data & PHY_LINK_UP)) return( S3C_FAIL); #ifdef VERBOSE else { if ( verbose) { if (data & PHYMODE_100) printk( "S3C4530-PHY: running at 100 Mbps, "); else printk( "S3C4530-PHY: set for 10 Mbps, "); if(data & PHYFULL_DUPLEX) printk( "FULL Duplex Mode.\n"); else printk( "HALF Duplex Mode. \n"); } } #endif return S3C_SUCCESS; } /************************************************************************* * PhyInit - initialize and configure the PHY device. * * This routine initialize and configure the PHY device * * RETURNS: S3C_SUCCESS, or S3C_FAIL * Needs to be modified according to the PHY, and * needs to be modified according to the SPEC of Level1 972. **************************************************************************/ static int PhyInit( ND dev) { S3C_MAC * np = (S3C_MAC *) dev->priv; UINT32 status = S3C_SUCCESS; UINT32 i; static const UINT32 modeList[] = { PHY_CONTROL_AUTONEG_HALF_10, PHY_CONTROL_SPEED_10_HALF, PHY_CONTROL_SPEED_100_HALF, PHY_CONTROL_AUTONEG_HALF_100, PHY_CONTROL_AUTONEG_FULL_10, PHY_CONTROL_SPEED_10_FULL, PHY_CONTROL_SPEED_100_FULL, PHY_CONTROL_AUTONEG_FULL_100 }; // Setup duplex and speed to pass on // automatic get 0 // setup Station Management data -- NOTE: Incomplete Data #define MATED_PHYTEST 0 #if MATED_PHYTEST // SetupPHY( PHY_CONTROL_AUTONEG_FULL_10); SetupPHY( PHY_CONTROL_SPEED_100_FULL); #else SetupPHY( modeList[ np->phySpeed + (np->phyDpx << 2)]); #endif for ( i = 0; i < PHY_BUSYLOOP_COUNT; i++) { if ( S3C_SUCCESS == (status = PHYCheckCable( ))) { #ifdef VERBOSE if ( verbose) { printk( "S3C4530-PHY: Cable detected\n"); } #endif break; } mdelay( 10); // 10 mS } //#ifdef VERBOSE if ( status != S3C_SUCCESS) { printk( "S3C4530-PHY: **** NO Cable is connected to MAC %s %08lx\n", dev->name, (UINT32) baseAddr); } //#endif #if DEBUG_PHY_DUMP DumpPhyReg(); #endif //DEBUG_PHY_DUMP #if 0 // no failure if no cable return ( status); #else return ( S3C_SUCCESS); #endif } /********************************************************************* * Function Name: SetupTxFDs * * Purpose: For transmit frame and buffer descriptors * - set up initial values for these. * - setup queue pointers * - FDS setup as a ring * * ** Assumes called just after dev set up, and priv defined * * Inputs: Pointer to dev structure * * Return Value: * S3C_SUCCESS * S3C_FAIL *********************************************************************/ static ULONG SetupTxFDs( ND dev ) { int i; TxFDS * tptr; // temporary pointer TxFDS * lastPtr; // keep last pointer to update S3C_MAC * priv; //pointer to private area if (0 == (priv = dev->priv)) { return ( S3C_FAIL); } /* Setup the Queue pointers */ tptr // pointer to first in ring = priv->qTx.first = priv->qTx.last = &priv->txFd[ 0]; priv->qTx.num = 0; // no buffers in use lastPtr = &priv->txFd[ TX_NUM_FD - 1]; /* for all the tx frames */ for(i = 0; i < TX_NUM_FD; i++) { /* CPU Ownership and Uncached and index and crash ( not word aligned) */ tptr->fd.FDDataPtr = (UCHAR *) (0x4000000 | (i * 4 + 1)); tptr->fd.FDStat = 0x00000000; tptr->fd.FDLength = 0x0000; // nothing there yet // tptr->fd.FDModes = TxWidSetup; // set widget bits (pg 7-18) tptr->fd.FDModes = 0; // Do setup only for packet being sent/ tptr->skb = (struct sk_buff *) NULL; lastPtr->fd.FDNext = (TxFD *) tptr; // Cast just for warning // link is for top "half" lastPtr = tptr++; } #ifdef VERBOSE if ( verbose) { printk( KERN_INFO "SetupTxFDs-for %s, First buff %x, Last %x\n", dev->name, (int) &priv->txFd[ 0 ], (int) &priv->txFd[ TX_NUM_FD - 1]); } #endif InvalidateBuffer( &priv->txFd, TX_NUM_FD * sizeof( TxFDS)); /* now tell the MAC about the beginning of the TX buffers */ /*** NOPE gets done in TxRxInit */ // WriteCSR( TxPtr, priv->qTx.first); return ( S3C_SUCCESS); } /*********************************************************************** * Function Name: SetupRxFDs * * Purpose: Prepare memory for receive buffers and frame descriptors * * ** Assumes called just after dev set up, and priv defined * * Inputs: Pointer to dev structure * * Return Value: * S3C_SUCCESS * S3C_FAIL ***********************************************************************/ static ULONG SetupRxFDs( ND dev) { int i; S3C_MAC * priv; // pointer to the private s3c area RxFDS * ftPtr; // temp pointer for FDA (rx fram descriptor) RxFDS * fLastPtr; // last pointer for FDA RxFDS * saveLastPtr; // last pointer for FDA struct sk_buff * skb; if ( 0 == (priv = (S3C_MAC *) dev->priv)) { return ( S3C_FAIL); } /* * Setup the Queue pointers */ /** First the Fd **/ ftPtr // pointer to first in ring = priv->qRx.first = &priv->rxFd[ 0]; priv->qRx.num = RX_NUM_FD; // buffers in use priv->qRx.last = fLastPtr = saveLastPtr = (RxFDS *) &priv->rxFd[ RX_NUM_FD - 1]; // last one /* * The Wait list only keeps track of FDs which don't have SKB's * - filled and put back on end of Rx queue later */ priv->qRxWait.first = priv->qRxWait.last = NULL; // point to nothing priv->qRxWait.num = 0; // all given to MAC /* * Initialize the FD Area. Give the ownership of each 16 * byte block to the controller - ready to RX. (The patterns of * 0xBBB etc is for debugging only!) ** NOTE: There is no EOL bit (original code had) */ #if MATED_DEBUG SMEM_CHECK("****** S3C: SetupRxFDs before loop before delay ***:"); { int iMaTed; for (iMaTed=0; iMaTed<500; iMaTed++) printk(" %c",8); } #endif SMEM_CHECK("****** S3C: SetupRxFDs before loop after delay ***:"); for ( i = 0; i < RX_NUM_FD; i++) { /* * Each FD points to a large enough buffer so that for each * incoming packet will fit in one buffer. * */ // we preallocate SKBs skb = dev_alloc_skb( PKT_BUF_SZ); if (skb == NULL) { printk( KERN_NOTICE "%s: Unable to allocate buffers.\n", dev->name); } // SMEM_CHECK("S3C: RxFDs- in skb Allocate loop after alloc"); ASSERT( 0 == (OWNERSHIP & (UINT32) skb->data)); // address space limited to // lower 2GB //get data offset // We invalidate cache after mac dma's data (might be shorter) // InvalidateBuffer( skb->data, PKT_BUF_SZ); // don't want old contents // But we do have to make sure we don't have outstanding writes to // the buffer FlushCache(); ftPtr->fd.FDDataPtr = (UCHAR *) ((UINT32)skb->data | OWNERSHIP); ftPtr->skb = skb; // save for later InvalidateBuffer( (void *) ftPtr->fd.FDDataPtr, PKT_BUF_SZ); skb->dev = dev; #if ENABLE_DESTRUCTOR skb->destructor = priv->destructor; #endif skb_reserve( skb, 2); // 16 byte alignment - for TCP ftPtr->fd.FDLength = PKT_BUF_SZ; ftPtr->fd.FDStat = 0; // Mac wil fill this in // Now do the links fLastPtr->fd.FDNext = (RxFD *) ftPtr; fLastPtr = ftPtr++; SMEM_CHECK("S3C: RxFDs- in skb Allocate loop at end"); } SMEM_CHECK("****** S3C: SetupRxFDs after setup loop ***:"); #if 1 // we aren't going to stop the BDAM engine from getting to the // last one, It stops itself // Need to put in the EOL // We should just fix the above loop not to set the OWNERSHIP bit on the // last one, but it's only initialization ...MaTed--- saveLastPtr->fd.FDDataPtr = (UCHAR *) (~OWNERSHIP & (UINT) saveLastPtr->fd.FDDataPtr); #endif priv->rxFd[ RX_NUM_FD - 1].fd.FDNext = (RxFD *) NULL; // last one #ifdef VERBOSE if ( verbose) { printk( "SetupRxFDs-for %s\n", dev->name); } #endif // This invalidates the header structures InvalidateBuffer( &priv->rxFd, RX_NUM_FD * sizeof( RxFDS)); /* * Now tell the MAC about the buffers */ WriteCSR( RxPtr, priv->qRx.first); return ( S3C_SUCCESS); } /********************************************************************** * Function Name: EnableInt * * Purpose: Enables the Interrupt for the Dev * - Interrupt enables are in 5 different registers * * Parameters: Device ***********************************************************************/ static _INLINE_ void EnableInt( void) { // temp disable (is this needed??) // disable all temporarily WriteCSR( TxCtl, ReadCSR( TxCtl ) & ~ BTxINTMask ); WriteCSR( RxCtl, ReadCSR( RxCtl ) & ~ BRxINTMask ); WriteCSR( MacCtl, ReadCSR( MacCtl ) & ~ MCtlINTMask ); WriteCSR( MacTxCtl, ReadCSR( MacTxCtl ) & ~ (MTxCtlINTMask | MTxCtlEnComp )); WriteCSR( MacRxCtl, ReadCSR( MacRxCtl ) & ~ MRxCtlINTMask ); /* Enable Interrupts */ /* first logic level */ #if !NO_INCLUDE_MAC_RX_INT enable_irq( MAC_RX_IRQ); #endif enable_irq( MAC_TX_IRQ); enable_irq( BDMA_RX_IRQ); enable_irq( BDMA_TX_IRQ); /* then from the hardware */ #if !NO_INCLUDE_MAC_RX_INT s3c4530_unmask_irq( MAC_RX_IRQ); #endif s3c4530_unmask_irq( MAC_TX_IRQ); s3c4530_unmask_irq( BDMA_RX_IRQ); s3c4530_unmask_irq( BDMA_TX_IRQ); /* Enable MAC and BDMA interrupts - everything for now */ WriteCSR( TxCtl, ReadCSR( TxCtl ) | BTxINTMask ); WriteCSR( RxCtl, ReadCSR( RxCtl ) | BRxINTMask ); // Right Now I don't even know what vector this goes to // It only contains a error counter rolls over I don't // have a reason for it. FIXME later ...MaTed--- // WriteCSR( MacCtl, ReadCSR( MacCtl ) | MXtlINTMask ); WriteCSR( MacTxCtl, ReadCSR( MacTxCtl ) | MTxCtlINTMask ); WriteCSR( MacRxCtl, ReadCSR( MacRxCtl ) | MRxCtlINTMask ); CHECK_ITXPTR("EnableInt") } /********************************************************************** * Function Name: DisableInt * * Purpose: Disables the Interrupt for the specified Dev * * Parameters: Device ***********************************************************************/ static _INLINE_ void DisableInt( void ) { // disable all temporarily WriteCSR( TxCtl, ReadCSR( TxCtl ) & ~ BTxINTMask ); WriteCSR( RxCtl, ReadCSR( RxCtl ) & ~ BRxINTMask ); // See above routine for comments // WriteCSR( MacCtl, ReadCSR( MacCtl ) & ~ MXtlINTMask ); WriteCSR( MacTxCtl, ReadCSR( MacTxCtl ) & ~ (MTxCtlINTMask | MTxCtlEnComp)); WriteCSR( MacRxCtl, ReadCSR( MacRxCtl ) & ~ MRxCtlINTMask ); /* Disable Interrupt */ /* first the H/W */ #if !NO_INCLUDE_MAC_RX_INT s3c4530_mask_irq( MAC_RX_IRQ); #endif s3c4530_mask_irq( MAC_TX_IRQ); s3c4530_mask_irq( BDMA_RX_IRQ); s3c4530_mask_irq( BDMA_TX_IRQ); /* then the logical */ #if !NO_INCLUDE_MAC_RX_INT disable_irq( MAC_RX_IRQ); #endif disable_irq( MAC_TX_IRQ); disable_irq( BDMA_RX_IRQ); disable_irq( BDMA_TX_IRQ); CHECK_ITXPTR("DisableInt") } /********************************************************************** * Function Name: EnableTxRx * * Purpose: Starts transmission and reception. * * Parameters: MAC basePort ***********************************************************************/ static _INLINE_ void EnableTxRx( void ) { /* Enable Transmission */ // -- Only enable the Tx when there is something to send moved // to s3c_send_packet // WriteCSR( TxCtl, (( ReadCSR( TxCtl)) | BTxEN)); // tx Enable AND not Tx Halt WriteCSR( MacTxCtl, (( ReadCSR( MacTxCtl)) | MTxCtlTxEn) & ~MTxCtlTxHalt); /* Enable Reception */ WriteCSR( RxCtl, (( ReadCSR( RxCtl)) | BRxEN)); // rx Enable AND not Rx Halt WriteCSR( MacRxCtl, (( ReadCSR( MacRxCtl)) | MRxCtlRxEn) & ~MRxCtlRxHalt); CHECK_ITXPTR("EnableTxRx") } /********************************************************************** * Function Name: DisableTxRx * * Purpose: Stops transmission and reception. * * Parameters: MAC basePort. ***********************************************************************/ static _INLINE_ void DisableTxRx( void) { /* Cancel Transmission */ WriteCSR( TxCtl, (( ReadCSR( TxCtl)) & ~ BTxEN)); // disable Tx and cause immediate halt WriteCSR( MacTxCtl, (( ReadCSR( MacTxCtl)) & ~ MTxCtlTxEn) | MTxCtlTxHalt); /* Cancel Reception */ WriteCSR( RxCtl, (( ReadCSR( RxCtl)) & ~BRxEN)); // rx disable AND immediate Rx Halt WriteCSR( MacRxCtl, (( ReadCSR( MacRxCtl)) & ~ MRxCtlRxEn) | MRxCtlRxHalt); CHECK_ITXPTR("DisableTxRx") } /************************************************************************* * Function Name: Reset * * Purpose: To reset the MAC. * * Return Value: S3C_FAIL on failure * S3C_SUCCESS on success **************************************************************************/ static int Reset( void) { UINT32 i = 0; // Stop all tx / rx WriteCSR( MacCtl, (( ReadCSR( MacCtl)) | MCtlHaltImm)); mdelay( 10); // 10 mS // reset the BDMA TX block WriteCSR( TxCtl, BTxRS); // reset the BDMA RX block WriteCSR( RxCtl, BRxRS); // Software reset of all MAC control and status register // and MAC state machine WriteCSR( MacCtl, MCtlReset); while (MCtlReset & ReadCSR( MacCtl)) { if (i++ > MCtlReset_BUSY_LOOP) break; mdelay( 10); // 10 mS } #if NET_DEBUG > 1 if (i >= MCtlReset_BUSY_LOOP) printk( "SC3: MAC did not come out of RESET :-( \n"); #endif // setup the BDMA rx Word Alignment (must be done directly after reset WriteCSR( RxCtl, BRxSetup_RESET); CHECK_ITXPTR("Reset") return ( S3C_SUCCESS); } /************************************************************************* * Function Name: SetupRegistersTX * * Purpose: To setup the transmit register values * NOTE: We assume that only the BRxCtl register has any which needs * to be preserved. **************************************************************************/ //static _INLINE_ void static void SetupRegistersTx( void) { WriteCSR( TxCtl, BTxSetup); // Buffered DMA transmit control reg // setup in TxRxInit // WriteCSR( TxPtr, priv->qTx.first); // Tx frame descriptor start addr WriteCSR( MacTxCtl, MTxCtlSetup); // Transmit Control CHECK_ITXPTR("SetupRegistersTx") return; } /************************************************************************* * Function Name: SetupRegistersRX * * Purpose: To setup the receive register values * NOTE: if a register has both RX and TX it's in here **************************************************************************/ //static _INLINE_ void static void SetupRegistersRx( void) { UINT32 i; // BDMA rx setup WriteCSR( RxCtl,BRxSetup); // Buffered DMA receive control reg // Setup in SetupTxFDs // WriteCSR( RxPtr; /* 0x0c Rx frame descriptor start addr */ WriteCSR( RxLSz, PKT_BUF_SZ); // Receive Frame maximum size for (i = 0; i < 32; i++) { WriteCSR( Cam[i], 0xffffffff); // CAM Content, set for broadcast } WriteCSR( CamCtl, CAMCtlSETUP); // CAM Control - Accept anything WriteCSR( MacRxCtl, MRxCtlSETUP); // Rx Control WriteCSR( MacCtl, MCtlSETUP); // Full Duplex 100 WriteCSR( CamEn, 0x04); // CAM Enable, third entry CHECK_ITXPTR("SetupRegisterRx") } /************************************************************************* * Function Name: SetUpTxFrmPtrRegister * * Purpose: tell the mac where the Tx Frames are **************************************************************************/ static _INLINE_ void SetUpTxFrmPtrRegister( ND dev) { S3C_MAC * priv = dev->priv; // NOTE: TxPtr doesn't care about uncached / cached (only bits 0:25) WriteCSR( TxPtr, priv->qTx.first); } /************************************************************************* * Function Name: TxRxInit * * Purpose: Set up the Ethernet to receive and transmit data. * * NOTE: Check the order that things happen * It needs to be checked (too many routines) FIXME ...MaTed--- * * Return Value: S3C_FAIL on failure * S3C_SUCCESS on success * - Always Succeeds **************************************************************************/ static _INLINE_ ULONG TxRxInit( ND dev) { // Reset( dev); SetUpTxFrmPtrRegister( dev); // do this before setting BDMA registers SetupRegistersTx( ); SetupRegistersRx( ); DisableTxRx( ); DisableInt( ); // ...MaTed--- changed to always bring up the interface PhyInit( dev); CHECK_ITXPTR("TxRxInit") return (S3C_SUCCESS); } /************************************************************************* * Function Name: SetUpARC * * Purpose: Setup the ARC memory MAC HW address... * - NOTE Only sets up one entry - 3rd entry * - 0 and 1 and 18 are reserved for MAC Control packets * * Parameters: basePort - base address of the MAC register set * addr * - pointer to array of 6 bytes for the MAC HW * address **************************************************************************/ static int SetUpARC( ND dev, void * addrin) { UINT32 save; UCHAR * addr = (UCHAR *) addrin; /* NOTE: This routine assumes little endian setup of UINT32 */ /* don't update while active */ save = ReadCSR( CamEn); // we leave other bits alone (later) WriteCSR( CamEn, 0x00000000); // don't use any ARC entries /* Enter Address at third entry - hard code */ WriteCSR( Cam[3], addr[0] | addr[1] << 8 | addr[2] << 16 | addr[3] << 24 ); WriteCSR( Cam[4], (ReadCSR( Cam[4]) & 0xffff0000) | addr[4] | addr[5] << 8); #if 0 // ** NOTE you could speed this up by using a UINT32 and // a UINT16 = statement (regardless of endian), but this is clearer // ...MaTed--- WriteCSR( Cam [2 * 6 + 0 ], addr [0]); WriteCSR( Cam [2 * 6 + 1 ], addr [1]); WriteCSR( Cam [2 * 6 + 2 ], addr [2]); WriteCSR( Cam [2 * 6 + 3 ], addr [3]); WriteCSR( Cam [2 * 6 + 4 ], addr [4]); WriteCSR( Cam [2 * 6 + 5 ], addr [5]); #endif /* Enable Third Address */ save |= 0x8; WriteCSR( CamEn, save); /* Enable ARC Compare +BCAST */ // WriteCSR( basePort, ARCCtl, (ARC_CompEn | ARC_BroadAcc | ARC_StationAcc)); // Make it accept anything within dest WriteCSR( CamCtl, CompEn | BroadAcc | GroupAcc | StationAcc); return ( 0); } #ifdef ENABLE_IOCTL /************************************************************************* * Function Name: SetSpeed100 * * Purpose: To Set the MAC in 100 mode. * NOTE: The actual speed is determined by the PHY * that is connected to this MAC. * NOTE: this routine is not used - for later implemtation of IOCTL * ** routine needs check and FIXME ** ...MaTed--- * ** needs to talk to the PHY **************************************************************************/ static _INLINE_ void SetSpeed100( void) { WriteCSR( MacCtl, (( ReadCSR(MacCtl)) & MCtlLoop10) ); } /************************************************************************* * Function Name: SetSpeed10 * * Purpose: To Set the MAC in 10 MBPS mode. * NOTE: The speed of the PHY should match this speed. * NOTE: this routine is not used - for later implemtation of IOCTL * ** routine needs check and FIXME ** ...MaTed--- * ** needs to talk to the PHY **************************************************************************/ static _INLINE_ void SetSpeed10( void) { WriteCSR( MacCtl, (( ReadCSR( MacCtl)) & ~MCtlLoop10 ); } #endif // ENABLE_IOCTL /************************************************************************ * Function Name: InvalidateCacheLine * Purpose: Invalidate cache which contains the address * - we reduce to start on 16 byte boundary (should own to * lower 16 byte boundary * - should "own" to next higher 16 byte boundary * ** Using two different algorithms - one just uses the tagline * ** flushes the cache lines regardless of the tags * ** The other checks the tags and only does something if it * ** matches the buffer address / ***********************************************************************/ _INLINE_ void InvalidateCacheLine( UINT32 bufaddr) { UINT32 * TagRamPtr = (UINT32 *) CACHE_TAG_RAM; #if 1 // fastest way - intrusive TagRamPtr[ 0xff & (bufaddr >> L1_CACHE_SHIFT)] = 0; #else #if 1 // preserve tags !?? FOR WHY TagRamPtr[ 0xff & (bufaddr >> L1_CACHE_SHIFT)] &= 0X3FFFFFF; #else // only zero if 14 bit tag matches { UINT32 Tag; UINT32 tagline Tag &= 0x3fff000; tagline = 0xff & (bufaddr >> L1_CACHE_SHIFT); if ( (Tag >> (16 - L1_CACHE_SHIFT) == (0x3fff & TagRamPtr[ tagline ])) { TagRomPtr[ tagline } &= 0xbfffffff; } else { // checking against Set 1 tag / / / / NOTE: the following not fixed for L1_CACHE_SHIFT / / ...MaTed--- if ( (Tag << 3) == (0x1fff8000 & TagRamPtr[ tagline ])) { TagRomPtr[ tagline } &= 0x7fffffff; // top bit } } #endif #endif return; } /************************************************************************ * Function Name: InvalidateBuffer * Purpose: Invalidate buffer cache * - must start on 16 byte boundary * - must "own" to next higher 16 byte boundary from end of buffer * NOTE: can be 32 bytes in cache line / ***********************************************************************/ _INLINE_ void InvalidateBuffer( void * bufaddr, UINT length) { // by computed goto would be optimized - for now call routine UINT32 end; end = ((ULONG) bufaddr + length + L1_CACHE_BYTES) & ~(L1_CACHE_BYTES -1); bufaddr = (void *) ((UINT) bufaddr & ~(L1_CACHE_BYTES - 1)); while ( (ULONG) bufaddr < end ) { InvalidateCacheLine( (UINT32) bufaddr ); bufaddr += L1_CACHE_BYTES; } return; } /************************************************************************ * Function Name: FlushCache * * Purpose: Ensure no outstanding writes * - we are using write through - I'm not even sure this is * required, but ...MaTed--- ***********************************************************************/ //static _INLINE_ void void FlushCache(void) { #define TEST_WRITE_FLUSH 0 #if TEST_WRITE_FLUSH unsigned long xflags; UINT32 * ad1, * ad2, * ad3, * ad4; // address to mod / check /* NOTE: original test had 4096 as array sizes - kills kernel, but is the proper test ...MaTed */ UINT32 val1[100], val2[100], val3[100], val4[100]; UINT32 sav1, sav2, sav3, sav4; // NOTE: this test cannot execute from inside interrupt routine local_irq_save(xflags); ad1 = val1; ad2 = val2; ad3 = val3; ad4 = val4; sav1 = *ad1; sav2 = *ad2; sav3 = *ad3; sav4 = *ad4; *((UINT32 *) (SYSCFG)) &= ~WriteBufferEn; // bit off ASSERT( sav1 == *ad1); ASSERT( sav2 == *ad2); ASSERT( sav3 == *ad3); ASSERT( sav4 == *ad4); *((UINT32 *) (SYSCFG)) |= WriteBufferEn; // bit on ASSERT( sav1 == *ad1); ASSERT( sav2 == *ad2); ASSERT( sav3 == *ad3); ASSERT( sav4 == *ad4); local_irq_restore(xflags); #else *((UINT32 *) (SYSCFG)) &= ~WriteBufferEn; // bit off *((UINT32 *) (SYSCFG)) |= WriteBufferEn; // bit on #endif } /************************************************************************ * The following routines do the work ************************************************************************/ /************************************************************************ * Check for a network adaptor of this type, and return '0' iff one exists. * - we just allocate them the number of times I'm called * NOTE: we ignore the following values for base_addr ...MaTed * If dev->base_addr == 0, probe all likely locations. * If dev->base_addr == 1, always return failure. * If dev->base_addr == 2, allocate space for the device and return success * (detachable devices only). ************************************************************************/ int __init s3c_probe( ND dev) { SMEM_CHECK("****** S3C probe ***:"); // check if all allocated if (numS3cMacs >= ETH_MAXUNITS) return (-ENODEV); // SET_MODULE_OWNER( dev); dev->irq = BDMA_RX_IRQ; // just put in 1 of four dev->base_addr = (UINT32) baseAddr; // just put start base addr if ( check_mem_region( dev->base_addr, MAC_IO_EXTENT)) return (-ENODEV); if ( s3c_probe1( dev) == S3C_SUCCESS) { numS3cMacs++; // increment the number of s3cs found SMEM_CHECK("S3C Leave probe:"); return S3C_SUCCESS; } return (-ENODEV); } /************************************************************************ * This routine initializes all the vars in dev and private areas ************************************************************************/ static int s3c_InitVars( ND dev) { S3C_MAC * priv = dev->priv; // initializtion will be overwritten 1st time // SMEM_CHECK("****** S3C Entering InitVars ***:"); /* Initialize the device structure. */ if ( dev->priv == NULL) { dev->priv = kmalloc( sizeof( S3C_MAC), GFP_KERNEL); if ( dev->priv == NULL) return ( -ENOMEM); #if DEBUG_INCTXPTR PrivPtr = (S3C_MAC *) dev->priv; #endif s3cInitPriv( dev); // initialize the Mac Private structure priv = (S3C_MAC *) dev->priv; spin_lock_init( &priv->lock); } dev->open = &s3c_open; dev->stop = &s3c_close; dev->hard_start_xmit = &s3c_send_packet; dev->get_stats = &s3c_get_stats; dev->set_multicast_list = &set_multicast_list; dev->tx_timeout = &s3c_tx_timeout; dev->watchdog_timeo = MY_TX_TIMEOUT; dev->set_mac_address = &SetUpARC; /* * Fill in the rest of the fields of the device structure * with ethernet values. */ #if ENABLE_DESTRUCTOR priv->destructor = &s3cDestructor; #endif /* * The following can be implemented but hasn't been */ // dev->do_ioctl = &s3c_do_ioctl; SMEM_CHECK("****** S3C Exiting InitVars ***:"); return ( S3C_SUCCESS); } /************************************************************************ * This is the real probe routine. Linux has a history of friendly device * probes on the ISA bus. A good device probes avoids doing writes, and * verifies that the correct device exists and functions. * -- This version is hard codedto just find the one hard coded address * It will only be called once. * NOTE: now gets HW address from external routine. If there is no * bootloader, then this routine must still return a HW address ...MaTed --- ************************************************************************/ static int __init s3c_probe1( ND dev) { int i; static unsigned version_printed = 0; char * ptr; int rc; //return code S3C_MAC * priv; if (net_debug && version_printed++ == 0) printk( KERN_INFO "%s", version); printk( KERN_INFO "\n%s: %s found at %08x, ", dev->name, cardname, (UINT) baseAddr); /* Fill in the 'dev' fields. */ ptr = get_MAC_address("dev0"); memcpy( dev->dev_addr, ptr, NUM_HW_ADDR); for ( i = 0; i < NUM_HW_ADDR; i++) { printk("%02x.", dev->dev_addr[ i]); } printk( "\n"); dev->addr_len = NUM_HW_ADDR; if ( s3c_request_irq_mem( dev)) { return ( -EAGAIN); } Reset( ); // Force initial conditions DisableInt( ); ether_setup( dev); DisableInt( ); SetupRegistersTx( ); SMEM_CHECK("****** S3C-between setupTx and Rx Reg ***:"); SetupRegistersRx( ); SMEM_CHECK("****** S3C-after Setup Rx registers ***:"); DisableTxRx( ); DisableInt( ); // return( s3c_InitVars( dev)); if (S3C_SUCCESS != (rc = s3c_InitVars( dev))) return( rc); // might be a bit redundant with the initial probe -- But ... // Zero out the stats priv = dev->priv; // wasn't init'd before this memset( &priv->stats, 0, sizeof( struct net_device_stats )); return( S3C_SUCCESS ); } /******************************************* * Blocks the multiple requests to set IRQ *******************************************/ static int run_request = 0; /********************************************************************** * s3c_request_irq_mem - routine to set the interrupt vectors and mem **********************************************************************/ static _INLINE_ int s3c_request_irq_mem( ND dev) { int rc; if (run_request) return (S3C_SUCCESS); /* * The ethernet MACS have fixed interrupts, allocate the interrupt * vector now. There is no point in waiting since no other device * can use the interrupt, and this marks the irq as busy. Jumpered * interrupts are typically not reported by the boards. MAC 0 is * fixed at IRQ 10, MAC 1 is fixed IRQ11 * * The S3C4530A allocates 4 interrupts to the MAC, do all four */ #if !NO_INCLUDE_MAC_RX_INT // skip this one - useless ...MaTed--- if ( (rc = request_irq( MAC_RX_IRQ, &s3c_MacRx, 0, cardname, dev))) { printk( KERN_WARNING "%s: unable to get IRQ %d (irqval=%d).\n", dev->name, MAC_RX_IRQ, rc); return ( -EAGAIN); } #endif if ( (rc = request_irq( MAC_TX_IRQ, &s3c_MacTx, 0, cardname, dev))) { printk( KERN_WARNING "%s: unable to get IRQ %d (irqval=%d).\n", dev->name, MAC_TX_IRQ, rc); #if !NO_INCLUDE_MAC_RX_INT // skip this one - useless ...MaTed--- free_irq( MAC_RX_IRQ, dev); #endif return ( -EAGAIN); } if ( (rc = request_irq( BDMA_RX_IRQ, &s3c_BdmaRx, 0, cardname, dev))) { printk( KERN_WARNING "%s: unable to get IRQ %d (irqval=%d).\n", dev->name, BDMA_RX_IRQ, rc); #if !NO_INCLUDE_MAC_RX_INT // skip this one - useless ...MaTed--- free_irq( MAC_RX_IRQ, dev); #endif free_irq( MAC_TX_IRQ, dev); return ( -EAGAIN); } if ( (rc = request_irq( BDMA_TX_IRQ, &s3c_BdmaTx, 0, cardname, dev))) { printk( KERN_WARNING "%s: unable to get IRQ %d (irqval=%d).\n", dev->name, BDMA_TX_IRQ, rc); #if !NO_INCLUDE_MAC_RX_INT // skip this one - useless ...MaTed--- free_irq( MAC_RX_IRQ, dev); #endif free_irq( MAC_TX_IRQ, dev); free_irq( BDMA_RX_IRQ, dev); return ( -EAGAIN); } /* Grab the region so that no one else tries to probe our ioports. */ if (! request_mem_region( (UINT32) baseAddr, sizeof(CSR), cardname)) { printk( KERN_WARNING "%s: unable to get memory/io address region %x\n", dev->name, (UINT) baseAddr); #if !NO_INCLUDE_MAC_RX_INT // skip this one - useless ...MaTed--- free_irq( MAC_RX_IRQ, dev); #endif free_irq( MAC_TX_IRQ, dev); free_irq( BDMA_RX_IRQ, dev); free_irq( BDMA_TX_IRQ, dev); return (-EAGAIN); } run_request = 1; return (rc); } /********************************************************************** * s3c_release_irq_mem - routine to release the interrupt vectors and mem **********************************************************************/ static _INLINE_ int s3c_release_irq_mem( ND dev) { if (run_request == 0) /* did we allocate them yet */ return( S3C_SUCCESS); /* release all the interrupts */ #if !NO_INCLUDE_MAC_RX_INT free_irq( MAC_RX_IRQ, dev); #endif free_irq( MAC_TX_IRQ, dev); free_irq( BDMA_RX_IRQ, dev); free_irq( BDMA_TX_IRQ, dev); /* Release the region so that some else (including ourselves) can have it */ release_mem_region( (UINT32) baseAddr, sizeof(CSR) ); run_request = 0; /* someone else can use it now */ return ( S3C_SUCCESS); } /*********************************************************************** * log transmit MAC errors - status register is the same as the * status in the FD block - juse the FD.status (multiple packets) * returns S3C_FAIL if error * S3C_SUCCESS if status did not show error ***********************************************************************/ static _INLINE_ int s3cLogTxMACError( ND dev, int status) { S3C_MAC * priv = dev->priv; priv->stats.collisions += status & TxCollCnt_mask; if (0 == (status & (TxExColl + TxLateColl + TxDefer + TxPaused + TxMaxDefer + TxHalted + TxNCarr + TxPar + TxUnder + TxSQErr))) { return ( S3C_SUCCESS); } priv->stats.tx_errors++; // we have an error // now log specific error if ( status & (TxExColl)) priv->stats.collisions += 16; // at least 16 collisions occurred if ( status & (TxMaxDefer + TxPaused + TxHalted + TxExColl + TxHalted)) priv->stats.tx_aborted_errors++; if ( status & (TxLateColl)) priv->stats.tx_window_errors++; if ( status & TxNCarr) priv->stats.tx_carrier_errors++; if ( status & (TxPar + TxUnder)) // Underrun is here?? priv->stats.tx_fifo_errors++; if ( status & (TxSQErr + TxDefer)) priv->stats.tx_heartbeat_errors++; #if 0 // there are circumstances when the transmitter has to be restarted if ( status & (TX_PARITY + ???)) // restart transmitter #endif return ( S3C_FAIL); } /*********************************************************************** * log receive BDMA errors * returns S3C_FAIL if error * S3C_SUCCESS if status did not show error * - status passed should be from the F Descriptor ***********************************************************************/ static _INLINE_ int s3cLogRxBdmaError( ND dev, int status) { S3C_MAC * priv = dev->priv; #if 0 if ( 0 != status & RxSGood) #else // Alternately we could check the errors if (0 == (status & (RxS_OvMax + RxS_AlignErr + RxS_CrcErr + RxS_Overflow + RxS_LongErr + RxS_RxPar + RXS_RxHalted ))) #endif { return ( S3C_SUCCESS); } priv->stats.rx_errors++; // now log specific errors if ( status & ( RxS_OvMax + RxS_LongErr )) priv->stats.rx_length_errors++; #if 0 // NOTE: this is an error which should get updated in the RX BDMA interrupt if ( status & ()) priv->stats.rx_over_errors++; #endif if ( status & ( RxS_CrcErr )) priv->stats.rx_crc_errors++; if ( status & ( RxS_AlignErr )) priv->stats.rx_frame_errors++; if ( status & ( RxS_Overflow + RxS_RxPar )) priv->stats.rx_fifo_errors++; if ( status & ( RXS_RxHalted )) // may be other updaters to this priv->stats.rx_missed_errors++; return ( S3C_FAIL); } /*********************************************************************** * Transmit timeout * Kernel calls this routine when a transmit has timed out ***********************************************************************/ static void s3c_tx_timeout( ND dev) { S3C_MAC * priv = dev->priv; // UINT32 basePort = dev->base_addr; UINT32 MacStatus = ReadCSR( MacTxStat); #if (NET_DEBUG > 3) UINT32 BDMAStatus = ReadCSR( BdmaStat); printk( KERN_ERR "%s: transmit timed out: NUM= %ld, MacTXStatus=0x%08lX ", dev->name, priv->qTx.num, MacStatus ); printk( KERN_ERR "MacBDMAStatus=0x%08lX ", BDMAStatus ); #endif /******************************************************** NOTE: This section needs a lot more work ...MaTed--- FIXME ********************************************************/ // We are going to reset the MAC // - all packets - both rx and tx may be discarded // - have to change logic to allow errors to stay around priv->stats.tx_errors++; (void) s3cLogTxMACError( dev, MacStatus); // (void) s3cLogTxBDMAError( dev, BDMAStatus); // FIXME ...MaTed--- the close / open has not been checked s3c_close( dev); s3c_open( dev); // Kick the MAC to check if there is something to send // - this is unnecessary, unless we add code to only do the // close/open every 2nd or third time ...MaTed //WriteCSR( DMACtl, ReadCSR( DMACtl) | DMA_CTL_TxWakeUp); /* If we have space available to accept new transmit * requests, wake up the queueing layer. This would * be the case if the chipset_init() call above just * flushes out the tx queue and empties it. * * If instead, the tx queue is retained then the * netif_wake_queue() call should be placed in the * TX completion interrupt handler of the driver instead * of here. */ if (!txFull( dev)) netif_wake_queue( dev); dev->trans_start = jiffies; // reset the start time return; } /************************************************************************ * initialize all the data in private area ************************************************************************/ static _INLINE_ int s3cInitPriv( ND dev ) { S3C_MAC * priv = dev->priv; RET_CODE rc; memset( priv, 0, sizeof( S3C_MAC)); /* setup the MAC HW address */ SetUpARC( dev, dev->dev_addr); // **Enhancement FIXME-pickup duplex and speed from commandline ...MaTed--- // - if you want to force, userland force later // priv->phySpeed = 2; // Force 100 // priv->phySpeed = 0; // automatic MII mode force speed = 10 priv->phySpeed = 3; // automatic MII mode // priv->phySpeed = 1; // Force 10 Mb/s endec WriteCSR( MacCtl, (ReadCSR( MacCtl) & ~(MCtlMII_Off + MCtlLoop10)) | (priv->phySpeed << MCtl10Shift1) | (priv->phySpeed << MCtl10Shift2)); /* Mac CNTL Full Duplex */ priv->phyDpx = 1; /* Full Duplex */ //priv->phyDpx = 0; /* Half Duplex, if MII_Conn is High?? */ WriteCSR( MacCtl, (ReadCSR( MacCtl) & ~ MCtlFullDup ) | priv->phyDpx << MCtlFullDupShift); /* * NOW set up the S3c MAC struct */ if ((rc = SetupTxFDs( dev))) return (rc); SMEM_CHECK("****** S3C Between SetupTxFDs and RxFDs ***:"); if ((rc = SetupRxFDs( dev))) return (rc); // no error return ( S3C_SUCCESS); } /************************************************************************ * Open/initialize the board. This is called (in the current kernel) * sometime after booting when the 'ifconfig' program is run. * * This routine should set everything up anew at each open, even * registers that "should" only need to be set once at boot, so that * there is non-reboot way to recover if something goes wrong. ***********************************************************************/ static int s3c_open( ND dev) { // S3C_MAC *priv = (S3C_MAC *) dev->priv; // UINT basePort = dev->base_addr; RET_CODE rc; #if 0 // why doesn't this get called during ifconfig ....MaTed--- printk( KERN_INFO "\n******** s3c_open called ********** \n"); #endif #if 0 // moved this section from the probe routine Reset( ); // Force initial conditions DisableInt( ); ether_setup( dev); DisableInt( ); SetupRegistersTx( ); SMEM_CHECK("****** S3C-between setupTx and Rx Reg ***:"); SetupRegistersRx( ); SMEM_CHECK("****** S3C-after Setup Rx registers ***:"); DisableTxRx( ); DisableInt( ); s3c_InitVars( dev); #endif // SetupRegistersTx( ); //SetupRegistersRx( ); //DisableTxRx( ); //DisableInt( ); /* * This is used if the interrupt line can turned off (shared). * - Can we share? - might as well register */ // if ( s3c_request_irq_mem( dev)) rc = s3c_request_irq_mem( dev); TDEBUG("S3C_OPEN: request_irq_mem rc=%d\n", rc); if (rc) { printk( KERN_ERR "S3C4530: Cannot get irq/mem rc=%d\n", rc); return -EAGAIN; } /* * Always allocate the DMA channel after the IRQ, * and clean up on failure. NO DMA used */ // Reset( dev); // reset the MAC // We are leaving priv intact // if (S3C_SUCCESS != (rc = s3c_InitVars( dev))) // return (rc); /* Reset the hardware here. Don't forget to set the station address. */ if (S3C_SUCCESS != (rc = TxRxInit( dev))) { printk( KERN_ERR "S3C4530: Phys init failed \n"); return (rc); } // The following is done in TXRXInit // SetupRegistersTx( dev); // SetupRegistersRx( dev); // DisableTxRx( dev->base_addr); // SetUpTxFrmPtrRegister( dev); // PhyInit( dev); // let the controller go again EnableTxRx( ); EnableInt( ); // priv->open_time = jiffies; /* We are now ready to accept transmit requeusts from * the queueing layer of the networking. */ netif_start_queue( dev); return S3C_SUCCESS; } /************************************************************************ This will only be invoked if your driver is _not_ in XOFF state. * What this means is that you need not check it, and that this * invariant will hold if you make sure that the netif_*_queue() * calls are done at the proper times. ************************************************************************/ static int s3c_send_packet( struct sk_buff *skb, ND dev) { S3C_MAC * priv = dev->priv; // int basePort = dev->base_addr; // short length; UCHAR * buf = skb->data; BPTR * bptr = &priv->qTx; TxFDS * fdbdPtr; #if (NET_DEBUG > 4) TxFDS * tempPtr; #endif UINT32 temp; // make minimum length for TX // Don't need this, the controller does it for me //length = ETH_MIN_LEN < skb->len ? skb->len : ETH_MIN_LEN; /* If some error occurs while trying to transmit this * packet, you should return '1' from this function. * In such a case you _may not_ do anything to the * SKB, it is still owned by the network queueing * layer when an error is returned. This means you * may not modify any SKB fields, you may not free * the SKB, etc. */ #if (NET_DEBUG > 4) /* Paranoid check to see if there is room - by pointers */ /* ?? I don't need this inside */ if (bptr->first == ((TxFDS *) (bptr->last))->fd.FDNext) { printk( KERN_ERR "TX overflow on %s\n", dev->name); // MAKE Sure transmitter was enabled -- maybe that will clear it up if (! ((temp = ReadCSR( TxCtl)) & BTxEN)) WriteCSR( TxCtl, temp | BTxEN); WriteCSR( MacTxCtl, ReadCSR( MacTxCtl) | MTxCtlTxEn); return ( -1); } #endif #if (NET_DEBUG > 4) /* another paranoid check (next should be owned by system */ tempPtr = (TxFDS *) (((TxFDS *) bptr->last)->fd.FDNext); if (((UINT32) ((TxFDS *) UnCache((UINT32)tempPtr))->fd.FDNext) & OWNERSHIP) { printk( KERN_ERR "TX ownership problem on %s\n", dev->name); return (-1); } #endif #if (NET_DEBUG > 5) printk( KERN_ERR "S3C_SEND: 0%08X Skb length %d\n", (unsigned int) dev->base_addr, (unsigned int) skb->len); // { // int i; // printk( KERN_ERR "S3C_SEND: Skb length %d\n - data=", skb->len); // for ( i = 0; i < skb->len; i++) // { // printk( KERN_ERR "[%02x] ", skb->data[ i]); // } // printk( KERN_ERR "\n"); // } #endif #if (NET_DEBUG > 3) ASSERT( atomic_read( &skb->users) == 1); #endif #if 0 //we don't invalidate the cache for writes only for reads InvalidateBuffer( buf, skb->len); #endif /* This is the most common case for modern hardware. * The spinlock protects this code from the TX complete * hardware interrupt handler. Queue flow control is * thus managed under this lock as well. */ spin_lock_irq( &priv->lock); fdbdPtr = (TxFDS *) bptr->last; // move to next buffer in list bptr->last = (void *) ((UINT32)(fdbdPtr->fd.FDNext)); fdbdPtr->fd.FDLength = skb->len; fdbdPtr->fd.FDModes = TxWidSetup; // set widget bits (pg 7-18) //fdbdPtr->fd.FDModes = TxWidIE | TxWidLITTLE | TxWidINC; fdbdPtr->skb = skb; #if (NET_DEBUG > 3) ASSERT( atomic_read( &skb->users) == 1); #endif bptr->num++; // added one buffer // Pass Control and buffer address fdbdPtr->fd.FDDataPtr = (UCHAR *) ((((UINT32) buf) - 2 )| OWNERSHIP); FlushCache(); // just in case // Let the MAC know there is something to send (in case it stopped ) // ...Note the check is from example code (why?), extra comp is for compliler if (! ((temp = ReadCSR( TxCtl)) & BTxEN)) WriteCSR( TxCtl, temp | BTxEN); WriteCSR( MacTxCtl, ReadCSR( MacTxCtl) | MTxCtlTxEn); dev->trans_start = jiffies; TxPktCompletion( dev ); /* If we just used up the very last entry in the * TX ring on this device, tell the queueing * layer to send no more. */ if (txFull( dev)) { netif_stop_queue( dev); // turn on tx completion interrupt WriteCSR( MacTxCtl, ReadCSR( MacTxCtl ) | MTxCtlEnComp ); } /* When the TX completion hw interrupt arrives, this * is when the transmit statistics are updated. */ spin_unlock_irq( &priv->lock); return 0; } /************************************************************************ * This handles TX complete events posted by the MAC portion of the * device via interrupts. ************************************************************************/ void s3c_MacTx( int irq, void *dev_id, struct pt_regs * regs) { ND dev = dev_id; S3C_MAC * priv = dev->priv; /* This protects us from concurrent execution of * our dev->hard_start_xmit function above. */ spin_lock( &priv->lock); TxPktCompletion( dev ); /* If we had stopped the queue due to a "tx full" * condition, and space has now been made available, * wake up the queue. **** Since we alway do a netif_wake_queue in the interrupt routine **** whats this for??? ...MaTed--- (it's in the skeleton) */ #if 1 // since we always call netif_wake_queue WHY? if ( netif_queue_stopped( dev) && ! txFull( dev)) { netif_wake_queue( dev); // turn off tx completion interrupt WriteCSR( MacTxCtl, ReadCSR( MacTxCtl ) & ~MTxCtlEnComp ); } #endif spin_unlock( &priv->lock); FlushCache(); } /************************************************************************ * Helper routine to collect stats on tx completion and release skbs * MUST be called with spin_lock already invoked ************************************************************************/ static _INLINE_ void TxPktCompletion( ND dev) { S3C_MAC * priv = dev->priv; BPTR * txPtr; TxFDS * UtxPtrFirst; UINT32 status; struct sk_buff * skb; UINT32 S3cTxPtr; UINT16 MissedErrorCnt; txPtr = &priv->qTx; UtxPtrFirst = (TxFDS *) (UnCache (txPtr->first)); // set the point where the Mac transmit Pointer is at - don't try to // catch the next one "going" to happen S3cTxPtr = UnCache( ReadCSR( TxPtr)); SINT(" MTx:stat", ((int) UtxPtrFirst->fd.FDStat)); // SINT(" Mac Tx interrupt: TxPtr", ((int) S3cTxPtr )); while ((UINT32) UtxPtrFirst != S3cTxPtr) // It's possible that there // is nothing to send { /*** Only in here if MAC processed at least one packet */ // checks to see if there was an error as well status = (UINT32) UtxPtrFirst->fd.FDStat; if (S3C_SUCCESS == s3cLogTxMACError( dev, status)) { skb = ((TxFDS*)(txPtr->first))->skb; priv->stats.tx_bytes += skb->len; // total bytes transmitted // NOTE: only good packets were transmitted (whereas rx can get bad) priv->stats.tx_packets++; // total packets transmitted } else { // a tx error of some kind occurred priv->stats.tx_errors++; // could/should move this into routine // clear EMISSCNT register , by read (example code) MissedErrorCnt = ReadCSR( MissCnt); SetupRegistersTx( ); // force Tx registers if error } /* * The following stuff we have to do regardless if the packet * transmission had errors or not */ // update the number of collisions priv->stats.collisions += ReadCSR( MacTxStat) & MTxStTxColl; // Clearing parts of the FD structure (probably not needed ??? ) UtxPtrFirst->fd.FDModes = 0x0; // turn off the Mode bits UtxPtrFirst->fd.FDLength = 0x0; // turn off the Length UtxPtrFirst->fd.FDStat = 0x0; // turn off the Status bits // get pointer to skb where we saved it skb = UtxPtrFirst->skb; #if (NET_DEBUG > 4) ASSERT( atomic_read( &skb->users) == 1); #endif #if (NET_DEBUG > 5) if (atomic_read( &skb->users) != 1) { printk("SKB:%08x, txPtrFirst:%08x, users:%x\n", (UINT32) skb, (UINT32) UtxPtrFirst, (UINT32) skb->users); } #endif dev_kfree_skb_irq (skb); // release it regardless // Move on to the next in the list txPtr->first = (void *) ((TxFD *)(txPtr->first))->FDNext; FlushCache(); UtxPtrFirst = (TxFDS *) (UNCACHE_ADDR | (UINT32) (txPtr->first)); txPtr->num--; } } /************************************************************************ * This handles TX complete events posted by the BDMA portion of the * device via interrupts. * - includes the following * - Sent all frames (stopped by owner bit) * - it would be nice to use this interrupt rather than the MAC "Send" * per packet, but error status and busy cause probs * - the interrupt should be disabled serves no purpose * - two parts to this, we disable the interrupt (see EnableInt ) * but enable the stop (no skip frame) * - Interrupt on control packet after MAC sends * - we don't currently send control packets, so this int is never * envoked. * - we will leave it enabled though * - Null List Interrupt * - this is bad - should never happen * - enabled as debug tool * - Tx Buffer Empty * - leaving this off too ************************************************************************/ void s3c_BdmaTx( int irq, void *dev_id, struct pt_regs * regs) { ND dev = dev_id; S3C_MAC * priv = dev->priv; UINT32 status; SINT("BTx:Stat", ((int) ReadCSR(BdmaStat)) ); CHECK_ITXPTR("EnableInt"); /* This protects us from concurrent execution of * our dev->hard_start_xmit function above. */ spin_lock( &priv->lock); // checks to see if there was an error as well status = ReadCSR( BdmaStat); WriteCSR( BdmaStat, status & 0xFFFF0000); // From example code (clear all) if ( status & BTxEmpty ) { printk( "%s: Invalid interrupt! Transmit Buffer Empty \n", dev->name); } else if (status & BTxNO ) { printk( "%s: Invalid interrupt! No owner for TX buffer [0x%08x]\n", dev->name, ((int) ReadCSR( TxPtr))); } else if (status & BTxNL ) { printk( "%s: Invalid interrupt! Transmit Null List \n", dev->name); } else if (status & BTxCCP ) { printk( "%s: Invalid interrupt! Control Packet Sent \n", dev->name); } spin_unlock( &priv->lock); } /************************************************************************ * We have a good packet(s), get it/them out of the buffers. * - we take both BL and FDA from the MAC.first * - move them to the front.last * - buffer is stripped off, added to skb head, skb address stored in ?? * - continue while MAC.first != MAC.last * - other things to check / do * - ensure all four entries are set to SYS_OWNS_xx ************************************************************************/ #if !NO_INCLUDE_MAC_RX_INT static void s3c_MacRx( int irq, void *dev_id, struct pt_regs * regs) { ND dev = dev_id; UINT status; status = ReadCSR( MacRxStat); SINT("MRx: Stat", ((int) status) ); if (status & MRxStGood) return; if ( status & (MRxStAlignErr | MRxStCRCErr | MRxStOverflow | MRxStLongErr | MRxStRxPar | MRxStRxHalted)) { #if NET_DEBUG printk( "%s: MAC Rx Error: 0x%x\n", dev->name, status); #endif } return; } #endif /************************************************************************ * BDMA receive interrupt - special conditions ************************************************************************/ static void s3c_BdmaRx( int irq, void *dev_id, struct pt_regs * regs) { ND dev = dev_id; S3C_MAC * priv = (S3C_MAC *)dev->priv; UINT32 status; UINT32 bdmaStatus; UINT boguscount = RX_NUM_FD / 4; // set to one quarter // of buffers - was 10 BPTR * Rx = &priv->qRx; // is & extraneous? BPTR * RxWait = &priv->qRxWait; struct sk_buff * skb = NULL; // initialize just to get rid // of warning UINT len; UINT32 MacRxPtr; RxFD * nextFirstPtr; UINT32 temp; // Rx First #define FdFirst ((RxFDS *) ( Rx->first)) // Rx Last #define FdLast ((RxFDS *) ( Rx->last)) // Wait first - when skb cannot be filled now #define FdWaitFirst ((RxFDS *) ( RxWait->first)) // Wait last #define FdWaitLast ((RxFDS *) ( RxWait->last)) // UnCached versions // Rx First #define UFdFirst ((RxFDS *) ( UnCache( (UINT32) Rx->first))) // Rx Last #define UFdLast ((RxFDS *) ( UnCache( (UINT32) Rx->last))) // Wait first - when skb cannot be filled now #define UFdWaitFirst ((RxFDS *) ( UnCache( (UINT32) RxWait->first))) // Wait last #define UFdWaitLast ((RxFDS *) ( UnCache( (UINT32) RxWait->last))) SINT("BRx: Stat", ((int) ReadCSR( BdmaStat)) ); /* This protects us from concurrent execution of * our dev->hard_start_xmit function above. */ spin_lock( &priv->lock); MacRxPtr = UnCache( ReadCSR( RxPtr)); bdmaStatus = ReadCSR( BdmaStat); // SINT(" Mac Rx interrupt: First Stat", ((int) UFdFirst->fd.FDStat)); while (MacRxPtr != (UINT32) UFdFirst) { // checks to see if there was an error as well if ( bdmaStatus & BRxNL ) { printk( "%s: Invalid interrupt! Rx dma Pointer is NULL\n", dev->name); WriteCSR( BdmaStat, bdmaStatus | BRxNL); // clear status register // Serious error -- needs a few things reset // -- Is this too much though ....MaTed--- TxRxInit( dev); EnableTxRx( ); EnableInt( ); break; } else if (bdmaStatus & BRxNO ) { printk( "%s: Invalid Interrupt! Not owner of RX buffer \n", dev->name); WriteCSR( BdmaStat, bdmaStatus | BRxNO); // clear status register SetupRegistersRx( ); // Is this enough to recover ...MaTed--- break; } else if (bdmaStatus & BRxMSO ) { printk( "%s: Invalid interrupt! Rx frame bigger than buffer\n", dev->name); WriteCSR( BdmaStat, bdmaStatus | BRxMSO); // clear status register // still have to do something to the data structures // I will assume that some other error in the FD status will flag #if 0 } else if (bdmaStatus & BRxStEmpty ) { printk( "%s: Invalid interrupt! Rx Buffer Empty\n", dev->name); #endif } // NOW we deal with the FD stuff status = UFdFirst->fd.FDStat; len = UFdFirst->fd.FDLength; if (len == 0) { // This should not happen #if (NET_DEBUG >3) printk( "%s: rx'd ZERO length packet... Skipped\n", dev->name); #endif status |= RxS_LongErr; // forces length stat to increment // No break - treat as error in packet //break; } #if 1 // Sanity check -- can probably turn this off ...MaTed--- if ( ((UINT32) (UFdFirst->fd.FDDataPtr)) & OWNERSHIP) { // This should not happen #if (NET_DEBUG >3) printk( "%s: Encountered rx packet not owned\n", dev->name); #endif break; } #endif // 1 - Sanity check nextFirstPtr = (RxFD *) (FdFirst->fd.FDNext); // save the new First if (S3C_SUCCESS == s3cLogRxBdmaError( dev, status)) { skb = UFdFirst->skb; priv->stats.rx_bytes += len; priv->stats.rx_packets++; // update lengths skb->tail += len; skb->len += len; InvalidateBuffer( skb->data, len);// want the real data skb->protocol = eth_type_trans( skb, dev); netif_rx( skb); // give the SKB to the kernel // Now move Frame Descriptor to the front of the WAIT list // This does the first half of pulling it out of the circular queue // by putting the buffer on the wait list (RxFDS *) (FdFirst)->fd.FDNext = FdWaitFirst; FdWaitFirst = FdFirst; RxWait->num ++; // keep track that we added it to the wait list Rx->num --; // will be incremented in destructor // FlushCache(); // Force out to memory } else { // On error we'll stay with the original skb // we had an error in the packet, we're keeping the skb FdFirst->fd.FDLength = PKT_BUF_SZ; // reset in case // Want to put this one at the end of the list (obtuse reasons) FdFirst->fd.FDNext = NULL; // last one can has zero for next // to stop BDMA engine FdFirst->fd.FDDataPtr = (UCHAR*) ((UINT32) (FdLast->fd.FDDataPtr) & ~OWNERSHIP); // Link first to the one last one - link was already to here (should // have been to here anyway (mind skb Memory Squeeze) // Give the FDA back to the MAC // First check if the last one in the list was still ours ASSERT( 0 == (OWNERSHIP & (UINT32) (FdLast->fd.FDDataPtr))); // Last thing to do (just in case) is to link to the end ((RxFD *) FdLast)->FDNext = (RxFD *) (FdFirst); // Give ownership to MAC / BDMA of the FD FdLast->fd.FDDataPtr = (UCHAR*) ((UINT32) (FdLast->fd.FDDataPtr) |= OWNERSHIP); FdLast = FdFirst; // moved first to back } // advance the first FdFirst = nextFirstPtr; // save the new First // have to clear to get next receive ??required?? ...MaTed--- WriteCSR( BdmaStat, ReadCSR( BdmaStat) & ~BRxRDF); FlushCache(); // if there are no buffers left, we're done anyways (last one is marker) if (FdFirst->fd.FDDataPtr == NULL) break; if (!boguscount--) break; // don't want to spend too much time in ISR } #if !ENABLE_DESTRUCTOR // Note try playing with the MinIn parm (1 is for debugging) s3cAddRxSKBs( dev, RX_NUM_FD / 4, 1); // put back SKBs into rx Q #endif // have to clear to get next receive WriteCSR( BdmaStat, ReadCSR( BdmaStat) | bdmaStatus); if ( !( BRxEN & (temp = ReadCSR( RxCtl)))) WriteCSR( RxCtl, temp | BRxEN); if ( !( MRxCtlRxEn & (temp = ReadCSR( MacRxCtl)))) WriteCSR( MacRxCtl, temp | MRxCtlRxEn); spin_unlock( &priv->lock); // Again do I need this ??? return; } /************************************************************************ * This routine adds as many SKBs as the Rx Wait queue requires IF they are * available. * The number of SKBs added depends on the qRxWait.num - number of entries * in the wait queue. Wait queue is filled with skbs, setup and then * reattached to the Rx Queue. We assign a maximum of MaxIn skb's * MinIn defines how many have to be on the Wait Queue before we cycle * through to fill them * * NOTE: special handling required if all Rx queue has been depleted * * NOTE: If called in an interrupt context, the caller must own the * priv->lock. If NOT an interrupt, the priv-> must not be owned ...MaTed--- ************************************************************************/ static void s3cAddRxSKBs( ND dev, UINT32 MaxIn, UINT32 MinIn ) { S3C_MAC * priv = (S3C_MAC *)dev->priv; BPTR * Rx = &priv->qRx; BPTR * RxWait = &priv->qRxWait; // UINT32 SaveNum = Rx->num; UINT32 numXfer = 0; RxFDS * tempPtr; RxFDS * nextTempPtr = NULL; struct sk_buff * skb; // Rx First #define First ((RxFDS *) ( Rx->first)) // Rx Last #define Last ((RxFDS *) ( Rx->last)) // After Last #define After ((RxFDS *) (Last->fd.FDNext)) // Wait first - when skb cannot be filled now #define FdWaitFirst ((RxFDS *) ( RxWait->first)) // Wait last #define FdWaitLast ((RxFDS *) ( RxWait->last)) if (RxWait->num < MinIn) return; // Move spinlock outside of the loop. Needs algorithm change and // much care to put it back inside the loop - may require mutiple // lock / unlock sequences ...MaTed--- if ( !in_irq()) spin_lock_irq( &priv->lock); // Lock it down tempPtr = FdWaitFirst; // keep track of the Head of the list while (RxWait->num && MaxIn --) { // get a newr skb to fill waiting head of wait list /* Malloc up new skb header. Let's leave the headroom */ skb = dev_alloc_skb( PKT_BUF_SZ); if (skb == NULL) { printk(KERN_NOTICE "%s: Memory squeeze.\n", dev->name); break; // add it next time } InvalidateBuffer( (void *) skb->data, PKT_BUF_SZ); skb->dev = dev; #if ENABLE_DESTRUCTOR skb->destructor = priv->destructor; #endif // NOTE: Assumption made that b31 of skb->data is 0 ********* FdWaitFirst->fd.FDDataPtr = (UCHAR *) ((UINT32) (skb->data) | OWNERSHIP); // BDMA owns // reserve after saving the buffer address // NOTE Should combine this reserve with the dev_alloc_skb ->alloc_skb // - for efficiency only, but skb->data would have to be corrected skb_reserve( skb, 2); // 16 byte align FdWaitFirst->skb = skb; // save for later reception FdWaitFirst->fd.FDLength = PKT_BUF_SZ; nextTempPtr = FdWaitFirst; FdWaitFirst = FdWaitFirst->fd.FDNext; RxWait->num --; numXfer ++; } // the Wait list first has advanced over the ones we moved // Last one attached to have a NULL Next Frame Descriptor and no Ownership if (nextTempPtr == NULL) // Nothing to do if nothing was done { return; } nextTempPtr->fd.FDDataPtr = (UCHAR *) (~OWNERSHIP & (UINT32) (nextTempPtr->fd.FDDataPtr)); nextTempPtr->fd.FDNext = NULL; // WaitFirst is pointing to the beginning of the wait queue // we move from nextTempPtr to tempPtr back to end of main queue // *** I'm not sure what the order should be for the next two instructions // ** or if they should be uncached operations Last->fd.FDNext = (RxFD *) tempPtr; Last->fd.FDDataPtr =(UCHAR *) ((UINT32) ( Last->fd.FDDataPtr) | OWNERSHIP); Last = nextTempPtr; // move the last pointer Rx->num += numXfer; // number that we put back FlushCache(); // Force out to memory // Moved the spin lock outside the loop - need to change things to move it // back in ...MaTed--- if ( !in_irq()) spin_unlock_irq( &priv->lock); // unlock it return; } #if DEBUG_RX_DUMP /************************************************************************ * Dump Rx data structures * * * * ************************************************************************/ static void dumpRx( S3C_MAC * priv, char * where ) { BPTR * Rx = &priv->qRx; BPTR * RxWait = &priv->qRxWait; printk( KERN_INFO "\n ***** %s *****\n", where); printk( KERN_INFO "** Rx first-[%p] last-[%p] num-[%ld]\n", Rx->first, Rx->last, Rx->num); RXFDS_Dump( (RxFDS *) (Rx->first), "First"); if (Rx->first != NULL) RXFDS_Dump( (RxFDS *) (((RxFDS *) (Rx->first))->fd.FDNext), "First->Next"); RXFDS_Dump( (RxFDS *) (Rx->last), "Last"); if (Rx->last != NULL) RXFDS_Dump( (RxFDS *) (((RxFDS *) (Rx->last))->fd.FDNext), "Last->Next"); printk( KERN_INFO "** Wait first-[%p] last-[%p] num-[%ld]\n", RxWait->first, RxWait->last, RxWait->num); RXFDS_Dump( (RxFDS *) (RxWait->first), "Wait First"); if (RxWait->first != NULL) RXFDS_Dump( (RxFDS *) (((RxFDS *) (RxWait->first))->fd.FDNext), "Wait First->Next"); RXFDS_Dump( (RxFDS *) (RxWait->last), "Wait Last"); if (RxWait->last !=NULL) RXFDS_Dump( (RxFDS *) (((RxFDS *) (RxWait->last))->fd.FDNext), "Wait Last->Next"); return; } /*********************************************************************** * Dump RxFDS Struct ***********************************************************************/ static void RXFDS_Dump( RxFDS * fds, char * str) { if (fds == NULL) { printk( KERN_INFO " ---- %s NULL Address ----\n", str); return; } printk( KERN_INFO " ---- %s Ptr-[%p] Next-[%p] Data-[%p] Len-[0x%x] Skb-[%p]\n", str, fds, fds->fd.FDNext, fds->fd.FDDataPtr, fds->fd.FDLength, fds->skb); // printk( KERN_INFO " ---- SKB HeadNext-[%p] Data-[%p] Len-[0x%x] Skb-[%p]\n", return; } #endif #if ENABLE_DESTRUCTOR /************************************************************************ * The destructor routine called when SKB is finally dead * - put the buffer back onto the correct free list and * set the skb->head == 0 * - called indirectly through s3cDestructor0 / 1 ****NOTE: this routine cannot be called in an interrupt context ************************************************************************/ static void s3cDestructor( struct sk_buff *skb) { ND dev = skb->dev; // S3C_MAC * priv = dev->priv; SINT("Destruct: SKB", ((int) skb)); if ( ! dev ) // check for NULL dev - why does this happen return; // The MinIn parm is just a guess - needs to be tuned ...MaTed--- s3cAddRxSKBs( dev, RX_NUM_FD, RX_NUM_FD/16 ); } #endif #if 0 // old copy delete later - might want the code still /************************************************************************ * The destructor routine called when SKB is finally dead * - put the buffer back onto the correct free list and * set the skb->head == 0 * - called indirectly through s3cDestructor0 / 1 ****NOTE: this routine cannot be called in an interrupt context ************************************************************************/ static void s3cDestructor( struct sk_buff *skb) { ND dev = skb->dev; S3C_MAC * priv = dev->priv; BPTR * Rx = &priv->qRx; BPTR * RxWait = &priv->qRxWait; UINT boguscount = RX_NUM_FD; // set to # of buffers struct sk_buff * newSkb; SINT("Destruct: SKB", ((int) skb)); // Rx First #define FdFirst ((RxFDS *) ( Rx->first)) // Rx Last #define FdLast ((RxFDS *) ( Rx->last)) // Wait first - when skb cannot be filled now #define FdWaitFirst ((RxFDS *) ( RxWait->first)) // Wait last #define FdWaitLast ((RxFDS *) ( RxWait->last)) // UnCached versions // Rx First #define UFdFirst ((RxFDS *) ( UnCache( (UINT32) Rx->first))) // Rx Last #define UFdLast ((RxFDS *) ( UnCache( (UINT32) Rx->last))) // Wait first - when skb cannot be filled now #define UFdWaitFirst ((RxFDS *) ( UnCache( (UINT32) RxWait->first))) // Wait last #define UFdWaitLast ((RxFDS *) ( UnCache( (UINT32) RxWait->last))) // Clean up the old skb so we never see it again // - Already done by skb_headerinit //skb->dev = 0; //skb->destructor = NULL; #if 0 printk("Destruct:dev=%x\n",(int) dev); #endif if ( (! dev) || in_irq() ) // check for NULL dev - why does this happen return; // The front buffers may need skb attached back - then attached onto the // active buffers in the MAC list while( RxWait->num) // put all the buffers back in that we need // if we can get them { // first get another skb to replace the one we gave away /* Malloc up new skb header. */ newSkb = dev_alloc_skb( PKT_BUF_SZ); if (newSkb == NULL) { // No buffers, let's get out of here break; } spin_lock_irq( &priv->lock); // Again do I need this - I think so??? newSkb->dev = dev; newSkb->destructor = priv->destructor; // NOTE: Assumption made that b31 of skb->data is 0 ********* FdWaitFirst->fd.FDDataPtr = newSkb->data; // reserve after saving the buffer address skb_reserve( newSkb, 2); // 16 byte align FdWaitFirst->skb = newSkb; // save for later reception FdWaitFirst->fd.FDLength = PKT_BUF_SZ; // Put the buffer at the end of the normal list /****************************************************************** * NOTE: Algorithm change - There is no EOL BIT, so we have to be * more careful. The BMDA engine will stop itself, we just have to * make sure we don't give it back before everything is ready (last) * - Make corresponding change in setup routine *******************************************************************/ // Link first wait to the one last normal active FdLast->fd.FDNext = (RxFD *) FdWaitFirst; // Give the FDA back to the MAC FdLast = FdWaitFirst; // moved first wait to back FdWaitFirst = FdWaitFirst->fd.FDNext; // FdWaitLast->fd.FDNext = FdWaitFirst; // circular back to front FdLast->fd.FDNext = (RxFD *) FdFirst; // make it circular RxWait->num--; // keep track that we took it off // the wait list Rx->num++; // and put it on ready list FdLast->fd.FDDataPtr = (UCHAR *) (((UINT32) (FdLast->fd.FDDataPtr)) |= OWNERSHIP); // give back to MAC // have to clear to get next receive - just in case WriteCSR( BdmaStat, ReadCSR( BdmaStat) & ~BRxRDF); FlushCache(); if (!boguscount--) break; // don't want to spend too much time in ISR spin_unlock_irq( &priv->lock); // Again do I need this ??? } if ( netif_queue_stopped( dev) && ! txFull( dev)) netif_wake_queue( dev); return; } #endif /************************************************************************ * The inverse routine to net_open(). ************************************************************************/ static int s3c_close( ND dev) { S3C_MAC * priv = dev->priv; // int basePort = dev->base_addr; // BPTR * TxPtr = &priv->qTx; // BPTR * RxPtr = &priv->qRx; // TxFDS * txPtrFirst; // struct sk_buff *skb; netif_stop_queue( dev); /* Flush the Tx and disable Rx here. */ DisableTxRx(); DisableInt(); // Reset(); // Shut it down // Release all buffers we own - please no memory leaks // * first the TX buffers spin_lock_irq( &priv->lock); #if 0 // ...MaTed--- need later for insmod, but not yet // We really should release the skb's -- when we get open to be called while (TxPtr->num != 0) // It's possible that there is nothing { txPtrFirst = (TxFDS *) (TxPtr->first); // get pointer to skb where we saved it skb = txPtrFirst->skb; ASSERT( atomic_read( &skb->users) == 1); dev_kfree_skb (skb); // release it // move to next buffer TxPtr->first = (void *) (((TxFDS *)(TxPtr->first))->fd.FDNext); TxPtr->num--; } // release all the rx skb's while (RxPtr->num != 0) // It's possible that there is nothing { // release it dev_kfree_skb ( ((RxFDS *) (RxPtr->first))->skb); RxPtr->first = (void *) (((RxFDS *) (RxPtr->first))->fd.FDNext); RxPtr->num--; } #endif // NOTE: do not have any skb's on the WAIT list with current algorithm #if 0 // ...MaTed--- need later for insmod, but not yet /* Release the region */ s3c_release_irq_mem( dev ); #endif spin_unlock_irq( &priv->lock); #if 0 // ...MaTed--- need later for insmod, but not yet /* release the priv area with the buffers and all the strcutures */ kfree( priv); #endif return 0; } /************************************************************************ * Get the current statistics. * This may be called with the card or closed. ************************************************************************/ static struct net_device_stats *s3c_get_stats( ND dev) { S3C_MAC *priv = (S3C_MAC *)dev->priv; return (&priv->stats); } /************************************************************************ * Set or clear the multicast filter for this adaptor. * num_addrs == -1 Promiscuous mode, receive all packets * num_addrs == 0 Normal mode, clear multicast list * num_addrs > 0 Multicast mode, receive normal and MC packets, * and do best-effort filtering. ************************************************************************/ static void set_multicast_list( ND dev) { // int ioaddr = dev->base_addr; WriteCSR( CamEn, 0); // turn off ARC usage for now if (dev->flags & IFF_PROMISC) { /* Enable promiscuous mode */ WriteCSR( CamCtl, ReadCSR( CamCtl) | NegCAM); } else if ((dev->flags & IFF_ALLMULTI) || dev->mc_count > HW_MAX_ADDRS) { WriteCSR( CamCtl, CompEn | BroadAcc | GroupAcc); } else if (dev->mc_count) { /* Walk the address list, and load the filter */ HardwareSetFilter( dev); WriteCSR( CamCtl, ReadCSR( CamCtl) | BroadAcc); } else // setup for normal { /* setup the MAC HW address */ SetUpARC( dev, dev->dev_addr); WriteCSR( CamEn, 1 << 2); // Enable only the third entry WriteCSR( CamCtl, (CompEn | GroupAcc | BroadAcc | StationAcc)); } } /************************************************************************ * HardwareSetFilter ************************************************************************/ static void HardwareSetFilter( ND dev ) { UINT i = 0; // UINT32 port = dev->base_addr; struct dev_mc_list * ptr = dev->mc_list; // UINT32 temp; // UINT16 * addrHalfPtr; UINT32 enableMask = 0; /* * NOTE: this section has never been tested ...MaTed--- */ while (ptr && i < HW_MAX_ADDRS - 1) { if ( i & 1) { // odd entry starting at the third location WriteCSR( Cam[ 3 + (i * 3) / 2], ((0x0000ffff & ReadCSR( Cam[ 3 + (i * 3) / 2] )) | (ptr->dmi_addr[ 1 ] << 24 ) | (ptr->dmi_addr[ 0 ] << 16 ))); WriteCSR( Cam[4 + (i * 3) / 2], (ptr->dmi_addr[ 5 ] << 24) | (ptr->dmi_addr[ 4 ] << 16) | (ptr->dmi_addr[ 3 ] << 8) | (ptr->dmi_addr[ 2 ])); } else { WriteCSR( Cam[3 + (i * 3) / 2], ((ptr->dmi_addr[ 3 ] << 24) | (ptr->dmi_addr[ 2 ] << 16) | (ptr->dmi_addr[ 1 ] << 8) | (ptr->dmi_addr[ 0 ]))); WriteCSR( Cam[4 + (i * 3) / 2], (0xffff0000 & ReadCSR( Cam[4 + (i * 3) / 2] )) | (ptr->dmi_addr[ 4 ] ) | (ptr->dmi_addr[ 5 ] << 8)); } enableMask |= (1 << ( i + 2 )); i++; ptr = ptr->next; } WriteCSR( CamEn, enableMask); // Enable all the entries if (i != dev->mc_count) { printk("S3C4530: %s - MultiCast Count (%d) != pointers (%d)\n", dev->name, dev->mc_count, i); } return; } /********************************************* * Module support *********************************************/ #ifdef MODULE // ***** NOTE: support not yet completed ...MaTed -- static struct net_device this_device; static int io = 0x300; static int irq; static int dma; static int mem; int init_module(void) { int result; if (io == 0) printk(KERN_WARNING "%s: You shouldn't use auto-probing with insmod!\n", cardname); /* Copy the parameters from insmod into the device structure. */ this_device.base_addr = io; this_device.irq = irq; this_device.dma = dma; this_device.mem_start = mem; this_device.init = netcard_probe; if ((result = register_netdev(&this_device)) != 0) return result; return 0; } void cleanup_module(void) { /* No need to check MOD_IN_USE, as sys_delete_module() checks. */ unregister_netdev(&this_device); /* * If we don't do this, we can't re-insmod it later. * Release irq/dma here, when you have jumpered versions and * allocate them in net_probe1(). */ /* free_irq(this_device.irq, dev); free_dma(this_device.dma); */ release_mem_region(this_device.base_addr, NETCARD_IO_EXTENT); if (this_device.priv) kfree(this_device.priv); } #endif /* MODULE */ #if DEBUG_PHY_DUMP /********************************************* * Dump all the phy registers *********************************************/ void DumpPhyReg(void) { int i; UINT32 Array[4]; printk( "**** Phy register dump \n"); for (i=0; i<=32; i+=4) { (void) ReadPhy( PHYAddr, i+0, &Array[0]); (void) ReadPhy( PHYAddr, i+1, &Array[1]); (void) ReadPhy( PHYAddr, i+2, &Array[2]); (void) ReadPhy( PHYAddr, i+3, &Array[3]); printk(" Addr[%d], 0x%08x - 0x%08x - 0x%08x - 0x%08x\n", i, (int) Array[0], (int) Array[1], (int) Array[2], (int) Array[3]); } } #endif //DEBUG_PHY_DUMP /* * Local variables: * compile-command: * gcc -D__KERNEL__ -Wall -Wstrict-prototypes -Wwrite-strings * -Wredundant-decls -O2 -m486 -c s3c.c.c * version-control: t * kept-new-versions: 5 * tab-width: 4 * c-indent-level: 2 * End: */ #endif // CONFIG_SAMSUNG_S3C #if S3C_TEMP_ROUTINES static int get_ethernet_addr(char * MatchName, char * addr) { #if 1 // pull the hw eternet address from the boot loader char *p; if ((p = getbenv (ETH1_ENV_NAME)) == ((char *) 0)) { // printf ("Error: No MAC for S3C4530!\n"); return -ENODEV; } // printf ("S3C4530 MAC=[%s]\n", p); while (*p != 0) *addr++ = *p++; return (0); // returns success #else /* this dummy routine always returns a fixed IP in addr */ addr[0] = 192; addr[1] = 168; addr[2] = 15; addr[3] = 2; return (0); // returns success #endif } #endif