/*##############################################################################
Copyright 2006,2007,2008 Sony Corporation. 

This program is based on LAN9118_samplecode.
Please refer to the following original comment for details. 

<NAME>
lan9118_drv.c

<DESCRIPTION>
Ethernet Driver for LAN9118

<NOTES>

<BUGS>

<Revision info>
Rev |    Date    | Description
------------------------------------------------------------------------------
 00 | 2006.02.10 | New making
 01 | 2006.04.12 | DMA disable (temporary) 
 02 | 2006.05.23 | MAC Addr reading from NAND(0x00000030:Boot ram)
 03 | 2007.05.15 | For Linux-2.6.21
 04 | 2007.11.06 | Support MB8AC102 and revive peripheral_init()
 05 | 2007.11.20 | Support Linux-2.6.11 again
 
##############################################################################*/

/* ****** original comment is here ********************************************/
/***************************************************************************
 *
 * Copyright (C) 2004-2005  SMSC
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 ****************************************************************************/
/* ****** original comment is here ********************************************/


/*------------------------------------------------------------------------------
  Driver Parameters
  ------------------------------------------------------------------------------

lan_base (It appropriately sets it. MUST specify)
    specifies the physical base location in memory where the LAN9111 can be 
    accessed. If a LAN9111 can not be found at the location specified then
    driver initialization will fail.
    
    * This address is Logical_address(Kernel adress) defined by 
      arch/arm/mach-cxd4105/mm.c
     It doesn't understand how to become final though I was defining while 
     developing this Driver as 0xEF000000. 


irq (It appropriately sets it. MUST specify)
    specifies the irq number to use. If this number is incorrect, then driver
    initialization will fail.

link_speed 
    specifies the link_speed. It uses it by default usually. 
    
    10            : Manual Link Mode,link_speed = 10Mbps
    100           : Manual Link Mode,link_speed = 100Mbps
    other(etc.0)  : Auto-Negotiation mode (10 or 100Mbps) (Default)


duplex_mode
    specifies the duplex_mode. It uses it by default usually. 
    * When link_speed is auto-negotiation, this setting doesn't have the meaning. 

    0             : HalfDuplex
    1             : FullDuplex  (Default)

dma_mode
    specifies the dma_mode. 
    0             :PIO Mode (Default)
    1             :DMA/PIO Mode(If DMA is busy then use PIO) 
    2             :DMA Only(If DMA is busy then wait for DMA to be free)


tx_dma_ch
    specifies the TX DMA Channel

rx_dma_ch
    specifies the TX DMA Channel

peri_init
    When this value is set to '1', the initial setting to EMC & GPIO is done. 
   
tx_queue_len
    Number of maximum frames that can be put in transmission waiting row of device.
    Default value is 1000(Equal in ether_setup to default value)

debug_mode
    specifies the debug mode
        0x00,Don't display messages (Default)
        0x01,display trace messages
        0x02,display warning messages
    Trace messages will only display if the driver was compiled
        with USE_TRACE defined.
    Warning message will only display if the driver was compiled
        with USE_WARNING defined.


mac_addr_hi16
    Specifies the high word of the mac address.

mac_addr_lo32
    Specifies the low dword of the mac address. 
    
phy_addr
    The default value of 0xFFFFFFFF tells the driver to use the internal phy.
    A value less than or equal to 31 tells the driver to use the external phy
    at that address. If the external phy is not found the internal phy
    will be used.
    Any other value tells the driver to search for the external phy on all
    addresses from 0 to 31. If the external phy is not found the internal 
    phy will be used.
    Default value is 0xFFFFFFFF(Use internal PHY)

  ----------------------------------------------------------------------------*/

/*------------------------------------------------------------------------------
  Compile Parameter
  ------------------------------------------------------------------------------
USE_PHY_WORK_AROUND
  This should not usually define it though this is bug of the LAN9118 chip
  correspondence. 
  Please do effectively and test this definition when the chip operation is doubtful. 

USE_DEBUG
  It is necessary to specify debug_mode to output the debugging message though 
  it is a definition necessary to output the debugging message. 

USE_LED1_WORK_AROUND
  This should not usually define it though this is bug of the LAN9118 chip
  correspondence. 
  Because link speed display LED is not installed in the Ether substrate of cxd4108, 
  this definition need not be made effective though this is used to evade the bug
  of LAN9118 that falls link speed display LED at Link_off. 

Rev01 add from
DMA_DRIVER_OFF
  Correspondence that can be compiled even if there is no DMA Driver
Rev01 add to

  ----------------------------------------------------------------------------*/

/*------------------------------------------------------------------------------
  Change part from original source
  ------------------------------------------------------------------------------
  Refer to the added comment. (description "By Sony")
  * The comment, the header, and the file composition of the source appropriately
    add and have changed. 
  * It is likely not to correct it in the part where it doesn't follow the coding
  rule of Sony either to keep the reliability of the source code that becomes an origin. 
  ----------------------------------------------------------------------------*/

#include "lan9118_drv.h"

MODULE_LICENSE("GPL");


/*------------------------------------------------------------------------------
  Module Parameter & Global variable to preserve parameter
  ----------------------------------------------------------------------------*/
/* ----- MAX_INSTANCES : H/W target connection number of LANC ------- 
It comes up and down by this definition the number of LANC that 
can be connected. 
However, when the definitions other than eight are done, it is
necessary to change other parts of the source code. 
------------------------------------------------------------------ */
#define MAX_INSTANCES 1

static DWORD lan_base[MAX_INSTANCES];
static uint num_lan_base = 0;
module_param_array(lan_base, ulong, &num_lan_base, 0);
MODULE_PARM_DESC(lan_base,"Base Address of LAN911x");

static DWORD irq[MAX_INSTANCES];
static uint num_irq = 0;
module_param_array(irq, ulong, &num_irq, 0);
MODULE_PARM_DESC(irq,"Force use of specific IRQ");


/* ----- An initial value of mac_addr_hi16 &mac_addr_lo32 changed by Sony --
It changed from initial value 0xFFFFFFFFUL to 0x00UL. 
When 0xFFFFFFFFUL is set to this parameter, this Driver acquires the 
MAC address from EEPROM.
However, cxd4108 cannot installing use EEPROM.
(The MAC address is used as 0 of the defaults. )

* When this Driver is used with other systems by setting 0xFFFFFFFFUL
 by the parameter at insmod, the MAC address can be read from EEPROM.
------------------------------------------------------------------- */

static DWORD mac_addr_hi16[MAX_INSTANCES] = {0xFFFFFFFFUL,};
static uint num_mac_addr_hi16 = 0;
module_param_array(mac_addr_hi16, ulong, &num_mac_addr_hi16, 0);
MODULE_PARM_DESC(mac_addr_hi16,"Specifies the high 16 bits of the mac address");

//DWORD mac_addr_lo32[MAX_INSTANCES];
static DWORD mac_addr_lo32[MAX_INSTANCES] = {0x004A0100UL,};// Rev02 add Sony MAC ID
static uint num_mac_addr_lo32 = 0;
module_param_array(mac_addr_lo32, ulong, &num_mac_addr_lo32, 0);
MODULE_PARM_DESC(mac_addr_lo32,"Specifies the low 32 bits of the mac address");
// Rev02 del to


static DWORD debug_mode=0x0UL;
module_param(debug_mode, ulong, 0);
MODULE_PARM_DESC(debug_mode,"bit 0 enables trace points, bit 1 enables warning points");

static DWORD phy_addr[MAX_INSTANCES]=
{0xFFFFFFFFUL,};
static uint num_phy_addr = 0;
module_param_array(phy_addr, ulong, &num_phy_addr, 0);
MODULE_PARM_DESC(phy_addr,"phy_Addr,0xFFFFFFFF=use internal phy,0-31=use external phy with specified address, else autodetect external phy addr");


/* ----- Addition of parameter(link_speed,duplex_mode,dma_mode,tx_dma_ch,
                        rx_dma_ch,peripheral_init) by Sony -- */
static DWORD link_speed = 0;
module_param(link_speed, ulong, 0);
MODULE_PARM_DESC(link_speed,"link_speed,10 or 100. Auto-Negotiation-Mode in case of numbers except 10 or 100");

static DWORD duplex_mode = 1;
module_param(duplex_mode, ulong, 0);
MODULE_PARM_DESC(duplex_mode,"duplex_mode,1:FullDuplex 0:HalfDuplex. Ignored if link_speed is Auto-Negotiation-Mode");

static DWORD dma_mode = 0;
module_param(dma_mode, ulong, 0);
MODULE_PARM_DESC(dma_mode,"0:PIO Mode(Default) 1:DMA/PIO Mode(If DMA Busy then use PIO) 2:DMA Only");

static DWORD tx_dma_ch = 7;
module_param(tx_dma_ch, ulong, 0);
MODULE_PARM_DESC(tx_dma_ch,"TX DMA Channel (Default 7)");

static DWORD rx_dma_ch = 7;
module_param(rx_dma_ch, ulong, 0);
MODULE_PARM_DESC(rx_dma_ch,"RX DMA Channel (Default 7)");

static DWORD peri_init = 0;
module_param(peri_init, ulong, 0);
MODULE_PARM_DESC(peri_init,"Initializing GPIO & EMC by this driver");

static DWORD tx_queue_len = 1000;
module_param(tx_queue_len, ulong, 0);
MODULE_PARM_DESC(tx_queue_len,"Number of maximum frames that can be put in transmission waiting row of device.");

/*------------------------------------------------------------------------------
  General global variable
  ----------------------------------------------------------------------------*/

/* ----- global variable for USE_LED1_WORK_AROUND ----- */
#ifdef USE_LED1_WORK_AROUND
static volatile DWORD g_GpioSetting=0x00000000UL;
static volatile DWORD g_GpioSettingOriginal=0x00000000UL;
#endif

/* ----- this array is used to keep track of which
   instances were successfully registered.
   That way they can be unregistered properly. ----- */
static DWORD registered[MAX_INSTANCES];

/* ---- global variable for Rx Tasklet ----- */
static unsigned long Rx_TaskletParameter[MAX_INSTANCES]=
{0UL,};  /* Tasklet Parameter struct pointer table */
static DECLARE_TASKLET(Rx_Tasklet0,Rx_ProcessPacketsTasklet,0);
#if 0
DECLARE_TASKLET(Rx_Tasklet1,Rx_ProcessPacketsTasklet,1);
DECLARE_TASKLET(Rx_Tasklet2,Rx_ProcessPacketsTasklet,2);
DECLARE_TASKLET(Rx_Tasklet3,Rx_ProcessPacketsTasklet,3);
DECLARE_TASKLET(Rx_Tasklet4,Rx_ProcessPacketsTasklet,4);
DECLARE_TASKLET(Rx_Tasklet5,Rx_ProcessPacketsTasklet,5);
DECLARE_TASKLET(Rx_Tasklet6,Rx_ProcessPacketsTasklet,6);
DECLARE_TASKLET(Rx_Tasklet7,Rx_ProcessPacketsTasklet,7);
#endif

/* ----- gRegistrationIndex ----- 
   gRegistrationIndex is used to pass the current
   instance index from Simp911x_init_module
   through register_netdev, to Simp911x_init ----- */
static DWORD gRegistrationIndex=0;

/* ----- net_device struct ----- */
static struct net_device SIMP911X[MAX_INSTANCES];

/* Rev01 del from */
//@@@
//static int test =0;
/* Rev01 del to */

#if defined(CONFIG_PM)
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14)
static int Simp911x_suspend(struct device *device, u32 state, u32 level);
static int Simp911x_resume(struct device *device, u32 level);
#else
static int Simp911x_suspend(struct platform_device *device, pm_message_t state);
static int Simp911x_resume(struct platform_device *device);
#endif

# if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)
static struct device_driver Simp911x_driver = {
  .name           = CARDNAME,
  .bus            = &platform_bus_type,
  //.probe          = Simp911x_probe,
  //.remove         = Simp911x_remove,
  .suspend        = Simp911x_suspend,
  .resume         = Simp911x_resume
};
#else
static struct platform_driver Simp911x_driver = {
  //.probe          = Simp911x_probe,
  //.remove         = Simp911x_remove,
  .suspend        = Simp911x_suspend,
  .resume         = Simp911x_resume,
  .driver	  = {
  	.name           = CARDNAME,
  	//.bus            = &platform_bus_type,
	.owner	= THIS_MODULE,
  },
};
#endif
#endif /* CONFIG_PM */

/*------------------------------------------------------------------------------
  API
  ----------------------------------------------------------------------------*/

/*==============================================================================
  <NAME>
  Simp911x_init_module

  <SYNOPSIS>
  int Simp911x_init_module(void)

  <OPERANDS>
  Parameter:
  
  Return value:
  0         : No Error
  -ENODEV   : Error 
             It fails in the registration of the device driver. 
             The lan_base parameter was not specified or the driver registration to
             the kernel failed. 
  
  <DESCRIPITON>
  entry point for loading the module (It is executed at insmod. )
   
  <NOTES>
  
  <BUGS>
  
  ============================================================================*/
static
int Simp911x_init_module(void)
{
  int result=0;
  int device_present=0;
  DWORD index=0;
  SMSC_TRACE("--> init_module()");
#if defined(CONFIG_ARCH_CXD4105BASED) || defined(CONFIG_ARCH_CXD4115BASED)
  lan_base[0] = CXD41XX_ETHER_BASE;
  irq[0] = SMC911X_IRQ;
  peri_init = 1;
  debug_mode = 3;
  //link_speed = 10;
  //tx_queue_len = 100;
#endif
#ifndef CONFIG_SMC9118_EEPROM
  mac_addr_hi16[0] = 0x0UL;
#endif /* !CONFIG_SMC9118_EEPROM */
#ifdef USE_PHY_WORK_AROUND
  SMSC_TRACE("Simple Driver Version = %lX.%02lX, with phy work around",
    (DRIVER_VERSION>>8),(DRIVER_VERSION&0xFFUL));
#else
  SMSC_TRACE("Simple Driver Version = %lX.%02lX",
    (DRIVER_VERSION>>8),(DRIVER_VERSION&0xFFUL));
#endif
  SMSC_TRACE("Compiled: %s, %s",__DATE__,__TIME__);

#if defined(CONFIG_PM)
# if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)
  if (driver_register(&Simp911x_driver) != 0){
#else
  if (platform_driver_register(&Simp911x_driver) != 0){
#endif
    SMSC_WARNING("driver_register failed.\n");
    goto out;
  }
#endif /* CONFIG_PM */

  if(lan_base[0]!=0) {
    for(index=0;index<MAX_INSTANCES;index++) {
      SIMP911X[index].priv=NULL;
      SIMP911X[index].init=Simp911x_init;
      if(lan_base[index]!=0) {
        strcpy(SIMP911X[index].name,"eth%d");
        gRegistrationIndex=index;
        result=register_netdev(&(SIMP911X[index]));
        if(result) {
          SMSC_WARNING("error %i registering device",result);
          registered[index]=0;
        } else {
          device_present=1;
          registered[index]=1;
          SMSC_TRACE("  Interface Name = \"%s\"",SIMP911X[index].name);
        }
      } else {
        registered[index]=0;
      }
    }
  } else {
    SMSC_WARNING("Must supply lan_base and irq.");
  }
#if defined(CONFIG_PM)
out:
#endif /* CONFIG_PM */
  SMSC_TRACE("<-- init_module()");
  return device_present ? 0 : -ENODEV;
}


/*==============================================================================
  <NAME>
  Simp911x_cleanup_module

  <SYNOPSIS>
  void Simp911x_cleanup_module(void)

  <OPERANDS>
  Parameter:
  
  Return value:
  
  <DESCRIPITON>
  entry point for unloading the module (It is executed at rmmod. )
   
  <NOTES>
  
  <BUGS>
  
  ============================================================================*/
static
void Simp911x_cleanup_module(void)
{
  DWORD index=0;
  SMSC_TRACE("--> cleanup_module()");
  for(index=0;index<MAX_INSTANCES;index++) {
    if(SIMP911X[index].priv!=NULL) {
      kfree(SIMP911X[index].priv);
    }
    if(registered[index]) {
      unregister_netdev(&(SIMP911X[index]));
    }
  }
  SMSC_TRACE("<-- cleanup_module()");
}

/*==============================================================================
  <NAME>
  Simp911x_init

  <SYNOPSIS>
  int Simp911x_init(struct net_device *dev)

  <OPERANDS>
  Parameter:
  struct net_device *dev
  
  Return value:
  0         : No Error
  -EFAULT   : Given *dev is NULL. 
  -ENODEV   : This is either of following reason. 
              -Specified lan_base is 0. 
              -The ID_REV register of LAN9118 was not able to be done and to have
               done Read correctly. 

  -ENOMEM   : This is either of following reason. 
              -Unable to allocate PRIVATE_DATA(Kernel Memory Empty)
              -Specified lan_base cannot be used. 

  <DESCRIPITON>
  entry point for initializing the driver
  (It is executed as part of the insmod processing afterwards if the driver registration 
   succeeded in insmod. )
   
  <NOTES>
  
  <BUGS>
  
  ============================================================================*/
static
int Simp911x_init(struct net_device *dev)
{
  DWORD dwIdRev=0UL;
  PPRIVATE_DATA privateData=NULL;
  int result=-ENODEV;
  int auto_nego = 0; // added by Sony

#ifndef CONFIG_SMC9118_EEPROM
  void __iomem *one_nand_boot_ram; // Rev02 Add
  unsigned long macaddr;
#endif /* !CONFIG_SMC9118_EEPROM */
  
  SMSC_TRACE("-->Simp911x_init(dev=0x%08lX)",(DWORD)dev);

  if(dev==NULL) {
    SMSC_WARNING("Simp911x_init(dev==NULL)");
    result=-EFAULT;
    goto DONE;
  }

  SMSC_TRACE("Device Instance Index = %ld",gRegistrationIndex);
  SMSC_TRACE("Driver Parameters");
  SMSC_TRACE("  lan_base         = 0x%08lX",lan_base[gRegistrationIndex]);
  SMSC_TRACE(    "  irq              = %ld",irq[gRegistrationIndex]);
  
  
  // peri_init add by Sony
  if(peri_init){
    peripheral_init();
  }

#ifndef CONFIG_SMC9118_EEPROM
  // Rev02 Add from
  one_nand_boot_ram = ioremap_nocache(0x0,SZ_1K); // Mapping(OneNAND Boot RAM)
  macaddr = readl(one_nand_boot_ram + 0x30);      // offset 0x00000030
  // Read & Set MAC addr(Low24bit)
  mac_addr_lo32[gRegistrationIndex] |= ((0x00ff0000 & macaddr) << 8);
  mac_addr_hi16[gRegistrationIndex] |= ((0x0000ff00 & macaddr) >> 8);
  mac_addr_hi16[gRegistrationIndex] |= ((0x000000ff & macaddr) << 8);
  iounmap(one_nand_boot_ram);
  // Rev02 Add to
  
  if(mac_addr_hi16[gRegistrationIndex]==0xFFFFFFFFUL) {
    SMSC_TRACE("  mac_addr_hi16    = 0x%08lX, will attempt to read from LAN911x",
      mac_addr_hi16[gRegistrationIndex]);
    SMSC_TRACE("  mac_addr_lo32    = 0x%08lX, will attempt to read from LAN911x",
      mac_addr_lo32[gRegistrationIndex]);
  } else {
    if(mac_addr_hi16[gRegistrationIndex]&0xFFFF0000UL) {
      //The high word is reserved
      SMSC_WARNING("  mac_addr_hi16 = 0x%08lX, reserved bits are high.",
        mac_addr_hi16[gRegistrationIndex]);
      mac_addr_hi16[gRegistrationIndex]&=0x0000FFFFUL;
      SMSC_WARNING("    reseting to mac_addr_hi16 = 0x%08lX",
        mac_addr_hi16[gRegistrationIndex]);
    }
    if(mac_addr_lo32[gRegistrationIndex]&0x00000001UL) {
      //bit 0 is the I/G bit
      SMSC_WARNING("  mac_addr_lo32 = 0x%08lX, I/G bit is set.",
        mac_addr_lo32[gRegistrationIndex]);
      mac_addr_lo32[gRegistrationIndex]&=0xFFFFFFFEUL;
      SMSC_WARNING("    reseting to mac_addr_lo32 = 0x%08lX",
        mac_addr_lo32[gRegistrationIndex]);     
    }
    SMSC_TRACE("  mac_addr_hi16    = 0x%08lX",
      mac_addr_hi16[gRegistrationIndex]);
    SMSC_TRACE("  mac_addr_lo32    = 0x%08lX",
      mac_addr_lo32[gRegistrationIndex]);
  }
#endif /* !CONFIG_SMC9118_EEPROM */
  SMSC_TRACE(    "  debug_mode       = 0x%08lX",debug_mode);
  

  /* ----- Message display for additional parameter ----- */
  // added by Sony: from
  if (link_speed == 10){
    SMSC_TRACE(    "  link_speed       = 10Mbps");
  }else if(link_speed == 100){
    SMSC_TRACE(    "  link_speed       = 100Mbps");
  }else{
    SMSC_TRACE(    "  link_speed       = AutoNegotiation");
    auto_nego = 1;
  }

  if(auto_nego){
    SMSC_TRACE(    "  duplex_mode      = AutoNegotiation");
  }else{
    if (duplex_mode)
    {
      SMSC_TRACE(    "  duplex_mode      = FullDuplex");
    }else{
      SMSC_TRACE(    "  duplex_mode      = HalfDuplex");
    }
  }

#ifndef DMA_DRIVER_OFF /* Rev01 add */
  if(dma_mode == DMA_PIO_MODE){
    SMSC_TRACE(    "  dma_mode      = DMA/PIO Mode(If DMA Busy then use PIO)");
  }else if(dma_mode == DMA_ONLY_MODE){
    SMSC_TRACE(    "  dma_mode      = DMA Only");
  }else{
#endif // DMA_DRIVER_OFF /* Rev01 add */    
    SMSC_TRACE(    "  dma_mode      = PIO Mode");
#ifndef DMA_DRIVER_OFF /* Rev01 add */
  }
#endif // DMA_DRIVER_OFF /* Rev01 add */

  if(dma_mode != PIO_MODE){
    SMSC_TRACE(    "  tx_dma_ch     = 0x%08lX",tx_dma_ch);
    SMSC_TRACE(    "  rx_dma_ch     = 0x%08lX",rx_dma_ch);
  }

#ifdef DMA_DRIVER_OFF  /* Rev01 add */
  SMSC_TRACE(    "  dma_mode is not available");
#endif // DMA_DRIVER_OFF /* Rev01 add */

  if(peri_init){
    SMSC_TRACE(    "  peri_init     = ON");
  }
  // added by Sony :to

  
  if(phy_addr[gRegistrationIndex]==0xFFFFFFFFUL) {
    SMSC_TRACE("  phy_addr         = 0xFFFFFFFF, Use internal phy");
  } else if(phy_addr[gRegistrationIndex]<=31) {
    SMSC_TRACE("  phy_addr         = 0x%08lX, use this address for external phy",
      phy_addr[gRegistrationIndex]);
  } else {
    SMSC_TRACE("  phy_addr         = 0x%08lX, auto detect external phy",
      phy_addr[gRegistrationIndex]);
  }
  if(dev->priv!=NULL) {
    SMSC_WARNING("dev->priv!=NULL, going to overwrite pointer");
  }
  dev->priv=kmalloc(sizeof(PRIVATE_DATA),GFP_KERNEL);
  if(dev->priv==NULL) {
    SMSC_WARNING("Unable to allocate PRIVATE_DATA");
    result=-ENOMEM;
    goto DONE;
  }
  memset(dev->priv,0,sizeof(PRIVATE_DATA));
  privateData=(PPRIVATE_DATA)(dev->priv);

  if(lan_base[gRegistrationIndex]==0UL) {
    SMSC_WARNING("lan_base==0x00000000");
    result=-ENODEV;
    goto DONE;
  }

  if(check_mem_region(lan_base[gRegistrationIndex],LAN_REGISTER_EXTENT)!=0) {
    SMSC_WARNING("  Memory Region specified (0x%08lX to 0x%08lX) is not available.",
      lan_base[gRegistrationIndex],lan_base[gRegistrationIndex]+LAN_REGISTER_EXTENT-1);
    result=-ENOMEM;
    goto DONE;
  }
  
  privateData->dwLanBase=(DWORD)ioremap_nocache(lan_base[gRegistrationIndex], LAN_REGISTER_EXTENT);
  privateData->dwInstanceIndex=gRegistrationIndex;
  dwIdRev=Lan_GetRegDW(ID_REV);
  if(HIWORD(dwIdRev)==LOWORD(dwIdRev)) {
    //this may mean the chip is set for 32 bit 
    //  while the bus is reading as 16 bit
UNKNOWN_CHIP:
    SMSC_WARNING("LAN911x NOT Identified, dwIdRev==0x%08lX",dwIdRev);
    result=-ENODEV;
    goto DONE;
  }
  switch(dwIdRev&0xFFFF0000UL) {
  case 0x01180000UL:
    SMSC_TRACE("LAN9118 identified, dwIdRev==0x%08lX",dwIdRev);break;
  case 0x01170000UL:
    SMSC_TRACE("LAN9117 identified, dwIdRev==0x%08lX",dwIdRev);break;
  case 0x01160000UL:
    SMSC_TRACE("LAN9116 identified, dwIdRev==0x%08lX",dwIdRev);break;
  case 0x01150000UL:
    SMSC_TRACE("LAN9115 identified, dwIdRev==0x%08lX",dwIdRev);break;
  case 0x01120000UL:
    SMSC_TRACE("LAN9112 identified, dwIdRev==0x%08lX",dwIdRev);break;
  default:
    goto UNKNOWN_CHIP;
  }
  privateData->dwIdRev=dwIdRev;

  if((dwIdRev&0x0000FFFFUL)==0UL) {
    SMSC_WARNING("This driver is not intended for this chip revision");
  }

  ether_setup(dev);
  dev->open=        Simp911x_open;
  dev->stop=        Simp911x_stop;
  dev->hard_start_xmit= Simp911x_hard_start_xmit;
  dev->get_stats=     Simp911x_get_stats;
  dev->tx_queue_len = tx_queue_len; // added by Sony
//  dev->set_multicast_list=Simp911x_set_multicast_list; // deleted by Sony(The multicast is not supported. ) 
//  dev->flags|=IFF_MULTICAST;                           // deleted by Sony(The multicast is not supported. ) 

  SET_MODULE_OWNER(dev);

  privateData->dev=dev;

  result=0;
    
DONE:
  if(result!=0) {
    //something went wrong, perform clean up
    if(dev!=NULL) {
      if(dev->priv!=NULL) {
        kfree(dev->priv);
        dev->priv=NULL;
      }
    }
  }
  SMSC_TRACE("<--Simp911x_init(), result=%d",result);
  return result;
}


/*==============================================================================
  <NAME>
  Simp911x_open

  <SYNOPSIS>
  int Simp911x_open(struct net_device *dev)

  <OPERANDS>
  Parameter:
  struct net_device *dev
  
  Return value:
  0         : No Error
  -EFAULT   : This is either of following reason. 
              -Given *dev is NULL. 
              -privateData(dev->priv)==NULL
  -ENODEV   : This is either of following reason. 
              -Specified irq cannot be used. 
              -PHY initialization error

  -ENOMEM   : This is either of following reason. 
              -Specified lan_base cannot be used. 

  <DESCRIPITON>
  entry point for starting/opening the interface
  (It is executed at OPEN. )
  
  <NOTES>
  
  <BUGS>
  
  ============================================================================*/
static
int Simp911x_open(struct net_device *dev)
{
  int result=-ENODEV;
  PPRIVATE_DATA privateData=NULL;
  BOOLEAN acquired_mem_region=FALSE;
  BOOLEAN acquired_isr=FALSE;
  SMSC_TRACE("-->Simp911x_open(dev=0x%08lX)",(DWORD)dev);
  if(dev==NULL) {
    SMSC_WARNING("Simp911x_open(dev==NULL)");
    result=-EFAULT;
    goto DONE;
  }
  privateData=(PPRIVATE_DATA)(dev->priv);
  if(privateData==NULL) {
    SMSC_WARNING("Simp911x_open(privateData==NULL)");
    result=-EFAULT;
    goto DONE;
  }

  //get memory region
  if(check_mem_region(privateData->dwLanBase,LAN_REGISTER_EXTENT)!=0)
  {
    SMSC_WARNING("Device memory is already in use.");
    result=-ENOMEM;
    goto DONE;
  }

  request_mem_region(lan_base[privateData->dwInstanceIndex],LAN_REGISTER_EXTENT,dev->name);
  acquired_mem_region=TRUE;

  //initialize the LAN911x
  {
    const DWORD dwIntCfg=IRQ_DEASSERTION_TIME;
    //dwIntCfg|=INT_CFG_IRQ_POL_;  //use this line to set IRQ_POL bit
    //dwIntCfg|=INT_CFG_IRQ_TYPE_; //use this line to set IRQ_TYPE bit
    if(!Lan_Initialize(privateData,dwIntCfg)) {
      goto DONE;
    }
  }

  if(request_irq(
    irq[privateData->dwInstanceIndex],
    Simp911x_ISR,
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
    SA_INTERRUPT,
#else
    IRQF_DISABLED,
#endif
    dev->name,
    privateData)!=0)
  {
    result=-ENODEV;
    goto DONE;
  }
  acquired_isr=TRUE;

  //must now test the IRQ connection to the ISR
  SMSC_TRACE("Testing ISR using IRQ %ld",irq[privateData->dwInstanceIndex]);
  {
    DWORD dwTimeOut=1000;
    privateData->RequestIrqDisable=FALSE;
    privateData->SoftwareInterruptSignal=FALSE;
    Lan_SetBitsDW(INT_EN,INT_EN_SW_INT_EN_);
    do {
      udelay(10);
      dwTimeOut--;
    } while((dwTimeOut)&&(!(privateData->SoftwareInterruptSignal)));
    if(!(privateData->SoftwareInterruptSignal)) {
      SMSC_WARNING("ISR failed signaling test");
      result=-ENODEV;
      goto DONE;
    }
  }
  SMSC_TRACE("ISR passed test using IRQ %ld",irq[privateData->dwInstanceIndex]);

  Mac_Initialize(privateData);
  {//get mac address
    DWORD dwHigh16=0;
    DWORD dwLow32=0;
    //Because this is part of the single threaded initialization
    //  path there is no need to acquire the MacPhyAccessLock
    if(mac_addr_hi16[privateData->dwInstanceIndex]==0xFFFFFFFF) {
      dwHigh16=Mac_GetRegDW(privateData,ADDRH);
      dwLow32=Mac_GetRegDW(privateData,ADDRL);
      if((dwHigh16==0x0000FFFFUL)&&(dwLow32==0xFFFFFFFF))
      {
        dwHigh16=0x00000070UL;
        dwLow32=0x110F8000UL;
        Mac_SetRegDW(privateData,ADDRH,dwHigh16);
        Mac_SetRegDW(privateData,ADDRL,dwLow32);
        SMSC_TRACE("Mac Address is set by default to 0x%04lX%08lX",
          dwHigh16,dwLow32);
      } else {
        SMSC_TRACE("Mac Address is read from LAN911x as 0x%04lX%08lX",
          dwHigh16,dwLow32);
      }
    } else {
      dwHigh16=mac_addr_hi16[privateData->dwInstanceIndex];
      dwLow32=mac_addr_lo32[privateData->dwInstanceIndex];
      Mac_SetRegDW(privateData,ADDRH,dwHigh16);
      Mac_SetRegDW(privateData,ADDRL,dwLow32);
      SMSC_TRACE("Mac Address is set by parameter to 0x%04lX%08lX",
        dwHigh16,dwLow32);
    }
    dev->dev_addr[0]=LOBYTE(LOWORD(dwLow32));
    dev->dev_addr[1]=HIBYTE(LOWORD(dwLow32));
    dev->dev_addr[2]=LOBYTE(HIWORD(dwLow32));
    dev->dev_addr[3]=HIBYTE(HIWORD(dwLow32));
    dev->dev_addr[4]=LOBYTE(LOWORD(dwHigh16));
    dev->dev_addr[5]=HIBYTE(LOWORD(dwHigh16));
  }

  netif_carrier_off(dev);
  if(!Phy_Initialize(
    privateData,
    phy_addr[privateData->dwInstanceIndex]))
  {
    SMSC_WARNING("Failed to initialize Phy");
    result=-ENODEV;
    goto DONE;
  }

  Tx_Initialize(privateData);
  Rx_Initialize(privateData);

#ifndef LINUX_2_6_OR_NEWER
  MOD_INC_USE_COUNT;
#endif
  netif_start_queue(dev);


  result=0;

DONE:
  if(result!=0) {
#ifndef LINUX_2_6_OR_NEWER
    MOD_DEC_USE_COUNT;
#endif
    if((acquired_isr)&&(privateData!=NULL)) {
      free_irq(irq[privateData->dwInstanceIndex],privateData);
    }
    if((acquired_mem_region)&&(privateData!=NULL)) {
      release_mem_region(
        lan_base[privateData->dwInstanceIndex],
        LAN_REGISTER_EXTENT);
    }
  }
  SMSC_TRACE("<--Simp911x_open, result=%d",result);
  return result;
}


/*==============================================================================
  <NAME>
  Simp911x_stop

  <SYNOPSIS>
  int Simp911x_stop(struct net_device *dev)

  <OPERANDS>
  Parameter:
  struct net_device *dev
  
  Return value:
  0         : No Error
  -EFAULT   : This is either of following reason. 
              -Given *dev is NULL. 
              -privateData(dev->priv)==NULL

  <DESCRIPITON>
  point for stopping the interface
  (It is executed at STOP. )
  
  <NOTES>
  
  <BUGS>
  
  ============================================================================*/
static
int Simp911x_stop(struct net_device *dev)
{
  int result=0;
  PPRIVATE_DATA privateData=NULL;
  SMSC_TRACE("-->Simp911x_stop(dev=0x%08lX)",(DWORD)dev);
  if(dev==NULL) {
    SMSC_WARNING("Simp911x_stop(dev==NULL)");
    result=-EFAULT;
    goto DONE;
  }
  privateData=(PPRIVATE_DATA)(dev->priv);
  if(privateData==NULL) {
    SMSC_WARNING("Simp911x_stop(privateData==NULL)");
    result=-EFAULT;
    goto DONE;
  }

  privateData->StopLinkPolling=TRUE;
  del_timer_sync(&(privateData->LinkPollingTimer));

  {//Disable IRQ safely, by using the software interrupt
    // to do it in the ISR itself.
    DWORD dwTimeOut=1000;
    privateData->RequestIrqDisable=TRUE;
    privateData->SoftwareInterruptSignal=FALSE;
    Lan_SetBitsDW(INT_EN,INT_EN_SW_INT_EN_);
    do {
      udelay(10);
      dwTimeOut--;
    } while((dwTimeOut)&&(!(privateData->SoftwareInterruptSignal)));
    if(!(privateData->SoftwareInterruptSignal)) {
      SMSC_WARNING("ISR failed signal test");
      //disable IRQ anyway
      Lan_ClrBitsDW(INT_CFG,INT_CFG_IRQ_EN_);
    }
  }

  //spin_lock(&dev->xmit_lock);
  netif_stop_queue(privateData->dev);
  //spin_unlock(&dev->xmit_lock);

  //at this point all Rx and Tx activity is stopped

  privateData->stats.rx_dropped+=Lan_GetRegDW(RX_DROP);
  Tx_UpdateTxCounters(privateData);

#ifndef LINUX_2_6_OR_NEWER
  MOD_DEC_USE_COUNT;
#endif

  free_irq(irq[privateData->dwInstanceIndex],privateData);
  release_mem_region(lan_base[privateData->dwInstanceIndex],LAN_REGISTER_EXTENT);

  {//preserve important items, clear the rest
    DWORD dwLanBase=privateData->dwLanBase;
    DWORD dwIdRev=privateData->dwIdRev;
    DWORD dwInstanceIndex=privateData->dwInstanceIndex;
    struct net_device *tempDev=privateData->dev;
    struct net_device_stats tempStats;
    memcpy(&tempStats,&(privateData->stats),sizeof(struct net_device_stats));

    memset(privateData,0,sizeof(PRIVATE_DATA));

    memcpy(&(privateData->stats),&tempStats,sizeof(struct net_device_stats));
    privateData->dwLanBase=dwLanBase;
    privateData->dwInstanceIndex=dwInstanceIndex;
    privateData->dev=tempDev;
    privateData->dwIdRev=dwIdRev;
  }

DONE:
  SMSC_TRACE("<--Simp911x_stop, result=%d",result);
  return result;
}


/*==============================================================================
  <NAME>
  Simp911x_hard_start_xmit

  <SYNOPSIS>
  int Simp911x_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)

  <OPERANDS>
  Parameter:
  struct sk_buff *skb
  struct net_device *dev
  
  Return value:
  0         : No Error
  -EFAULT   : This is either of following reason. 
              -Given *dev is NULL. 
              -privateData(dev->priv)==NULL
              -Given *skb is NULL.

  <DESCRIPITON>
  entry point for transmitting a packet
  
  <NOTES>
  
  <BUGS>
  
  ============================================================================*/
static
int Simp911x_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
  int result=0;
  PPRIVATE_DATA privateData=NULL;
  if(skb==NULL) {
    SMSC_WARNING("Simp911x_hard_start_xmit(skb==NULL)");
    result=-EFAULT;
    goto DONE;
  }
  if(dev==NULL) {
    SMSC_WARNING("Simp911x_hard_start_xmit(dev==NULL)");
    result=-EFAULT;
    goto DONE;
  }
  if(dev->priv==NULL) {
    SMSC_WARNING("Simp911x_hard_start_xmit(dev->priv==NULL)");
    result=-EFAULT;
    goto DONE;
  }
  privateData=(PPRIVATE_DATA)(dev->priv);

  Tx_SendSkb(privateData,skb);

DONE:
  return result;
}


/*==============================================================================
  <NAME>
  Simp911x_get_stats

  <SYNOPSIS>
  struct net_device_stats * Simp911x_get_stats(struct net_device *dev)

  <OPERANDS>
  Parameter:
  struct sk_buff *skb
  struct net_device *dev
  
  Return value:
  0         : No Error
  -EFAULT   : This is either of following reason. 
              -Given *dev is NULL. 
              -privateData(dev->priv)==NULL

  <DESCRIPITON>
  entry point for getting status counters
  
  <NOTES>
  
  <BUGS>
  
  ============================================================================*/
static
struct net_device_stats * Simp911x_get_stats(struct net_device *dev)
{
  PPRIVATE_DATA privateData=NULL;
  if(dev==NULL) {
    SMSC_WARNING("Simp911x_get_stats(dev==NULL)");
    return NULL;
  }
  if(dev->priv==NULL) {
    SMSC_WARNING("Simp911x_get_stats(dev->priv==NULL)");
    return NULL;
  }
  privateData=(PPRIVATE_DATA)(dev->priv);
  return &(privateData->stats);
}


#if 0
/*==============================================================================
  <NAME>
  Simp911x_set_multicast_list

  <SYNOPSIS>
  void Simp911x_set_multicast_list(struct net_device *dev)

  <OPERANDS>
  Parameter:
  struct net_device *dev
  
  Return value:

  <DESCRIPITON>
  entry point for setting addressing modes
  
  <NOTES>
  
  <BUGS>
  
  ============================================================================*/
static
void Simp911x_set_multicast_list(struct net_device *dev)
{
  SMSC_ASSERT(dev!=NULL);
  Rx_SetMulticastList(dev);
}
#endif


/*==============================================================================
  <NAME>
  Simp911x_set_multicast_list

  <SYNOPSIS>
  void Simp911x_set_multicast_list(struct net_device *dev)

  <OPERANDS>
  Parameter:
  int Irq
  void *dev_id
  struct pt_regs *regs
  
  Return value:
  irqreturn_t
  
  <DESCRIPITON>
  primary entry point for ISR
  *Top Half
  
  <NOTES>
  
  <BUGS>
  
  ============================================================================*/
static
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
irqreturn_t Simp911x_ISR(int Irq, void *dev_id, struct pt_regs *regs)
#else
irqreturn_t Simp911x_ISR(int Irq, void *dev_id)
#endif
{
  DWORD dwIntCfg=0;
  DWORD dwIntSts=0;
  PPRIVATE_DATA privateData=(PPRIVATE_DATA)dev_id;
  BOOLEAN serviced=FALSE;

  if(privateData==NULL) {
    SMSC_WARNING("Simp911x_ISR(privateData==NULL)");
    goto DONE;
  }
  if(privateData->dwLanBase==0) {
    SMSC_WARNING("Simp911x_ISR(dwLanBase==0)");
    goto DONE;
  }
  dwIntCfg=Lan_GetRegDW(INT_CFG);
  if((dwIntCfg&0x00001100)!=0x00001100) {
    SMSC_TRACE("In ISR, not my interrupt, dwIntCfg=0x%08lX",
      dwIntCfg);
    goto DONE;
  }
  {
    DWORD reservedBits=0x00FFCEEEUL;
    if(dwIntCfg&reservedBits) {
      SMSC_WARNING("In ISR, reserved bits are high.");
      //this could mean surprise removal
      goto DONE;
    }
  }
  dwIntSts=Lan_GetRegDW(INT_STS);
  if(dwIntSts&INT_STS_SW_INT_) {
    Lan_ClrBitsDW(INT_EN,INT_EN_SW_INT_EN_);
    Lan_SetRegDW(INT_STS,INT_STS_SW_INT_);
    privateData->SoftwareInterruptSignal=TRUE;
    serviced=TRUE;
    if(privateData->RequestIrqDisable) {
      Lan_ClrBitsDW(INT_CFG,INT_CFG_IRQ_EN_);
      dwIntSts=0;//prevent any pending interrupts from being handled.
    }
  }
  if(Tx_HandleInterrupt(privateData,dwIntSts)) {
    serviced=TRUE;
  }
  if(Rx_HandleInterrupt(privateData,dwIntSts)) {
    serviced=TRUE;
  }
  if(!serviced) {
    SMSC_WARNING("unserviced interrupt dwIntCfg=0x%08lX,dwIntSts=0x%08lX,INT_EN=0x%08lX",
      dwIntCfg,dwIntSts,Lan_GetRegDW(INT_EN));
  }
DONE:
  return IRQ_RETVAL(serviced);
}



/*------------------------------------------------------------------------------
  Sub function
  ----------------------------------------------------------------------------*/

#ifdef USE_PHY_WORK_AROUND
/*==============================================================================
  <NAME>
  Phy_Reset

  <SYNOPSIS>
  BOOLEAN Phy_Reset(PPRIVATE_DATA privateData)

  <OPERANDS>
  Parameter:
  Return value:

  <DESCRIPITON>
  
  <NOTES>
  
  <BUGS>
  
  ============================================================================*/
static
BOOLEAN Phy_Reset(PPRIVATE_DATA privateData)
{
  BOOLEAN result=FALSE;
  WORD wTemp=0;
  DWORD dwLoopCount=100000;
  SMSC_TRACE("Performing PHY BCR Reset");
  Phy_SetRegW(privateData,PHY_BCR,PHY_BCR_RESET_);
  do {
    udelay(10);
    wTemp=Phy_GetRegW(privateData,PHY_BCR);
    dwLoopCount--;
  } while((dwLoopCount>0)&&(wTemp&PHY_BCR_RESET_));
  if(wTemp&PHY_BCR_RESET_) {
    SMSC_WARNING("Phy Reset failed to complete.");
    goto DONE;
  }
  //extra delay required because the phy may not be completed with its reset
  //  when PHY_BCR_RESET_ is cleared.
  //  They say 256 uS is enough delay but I'm using 500 here to be safe
  udelay(500);
  result=TRUE;
DONE:
  return result;
}


/*==============================================================================
  <NAME>
  Phy_LBT_GetTxStatus

  <SYNOPSIS>
  DWORD Phy_LBT_GetTxStatus(PPRIVATE_DATA privateData)

  <OPERANDS>
  Parameter:
  Return value:

  <DESCRIPITON>
  
  <NOTES>
  
  <BUGS>
  
  ============================================================================*/
static
DWORD Phy_LBT_GetTxStatus(PPRIVATE_DATA privateData)
{
  DWORD result=Lan_GetRegDW(TX_FIFO_INF);
  result&=TX_FIFO_INF_TSUSED_;
  if(result!=0x00000000UL) {
    result=Lan_GetRegDW(TX_STATUS_FIFO);
  } else {
    result=0;
  }
  return result;
}


/*==============================================================================
  <NAME>
  Phy_LBT_GetRxStatus

  <SYNOPSIS>
  DWORD Phy_LBT_GetRxStatus(PPRIVATE_DATA privateData)

  <OPERANDS>
  Parameter:
  Return value:

  <DESCRIPITON>
  
  <NOTES>
  
  <BUGS>
  
  ============================================================================*/
static
DWORD Phy_LBT_GetRxStatus(PPRIVATE_DATA privateData)
{
  DWORD result=Lan_GetRegDW(RX_FIFO_INF);
  if(result&0x00FF0000UL) {
    //Rx status is available, read it
    result=Lan_GetRegDW(RX_STATUS_FIFO);
  } else {
    result=0;
  }
  return result;
}


/*==============================================================================
  <NAME>
  Phy_CheckLoopBackPacket

  <SYNOPSIS>
  BOOLEAN Phy_CheckLoopBackPacket(PPRIVATE_DATA privateData)

  <OPERANDS>
  Parameter:
  Return value:

  <DESCRIPITON>
  
  <NOTES>
  
  <BUGS>
  
  ============================================================================*/
static
BOOLEAN Phy_CheckLoopBackPacket(PPRIVATE_DATA privateData)
{
  BOOLEAN result=FALSE;
  DWORD tryCount=0;
  DWORD dwLoopCount=0;
  for(tryCount=0;tryCount<10;tryCount++)
  {
    DWORD dwTxCmdA=0;
    DWORD dwTxCmdB=0;
    DWORD dwStatus=0;
    DWORD dwPacketLength=0;

    //zero-out Rx Packet memory
    memset(privateData->LoopBackRxPacket,0,MIN_PACKET_SIZE);

    //write Tx Packet to 118
    dwTxCmdA=
      ((((DWORD)(privateData->LoopBackTxPacket))&0x03UL)<<16) | //DWORD alignment adjustment
      TX_CMD_A_FIRST_SEG_ | TX_CMD_A_LAST_SEG_ |
      ((DWORD)(MIN_PACKET_SIZE));
    dwTxCmdB=
      (((DWORD)(MIN_PACKET_SIZE))<<16) |
      ((DWORD)(MIN_PACKET_SIZE));
    Lan_SetRegDW(TX_DATA_FIFO,dwTxCmdA);
    Lan_SetRegDW(TX_DATA_FIFO,dwTxCmdB);
    Tx_WriteFifo(
      privateData->dwLanBase,
      (DWORD *)(((DWORD)(privateData->LoopBackTxPacket))&0xFFFFFFFCUL),
      (((DWORD)(MIN_PACKET_SIZE))+3+
      (((DWORD)(privateData->LoopBackTxPacket))&0x03UL))>>2);

    //wait till transmit is done
    dwLoopCount=60;
    while((dwLoopCount>0)&&((dwStatus=Phy_LBT_GetTxStatus(privateData))==0)) {
      udelay(5);
      dwLoopCount--;
    }
    if(dwStatus==0) {
      SMSC_WARNING("Failed to Transmit during Loop Back Test");
      continue;
    }
    if(dwStatus&0x00008000UL) {
      SMSC_WARNING("Transmit encountered errors during Loop Back Test");
      continue;
    }

    //wait till receive is done
    dwLoopCount=60;
    while((dwLoopCount>0)&&((dwStatus=Phy_LBT_GetRxStatus(privateData))==0))
    {
           udelay(5);
           dwLoopCount--;
    }
    if(dwStatus==0) {
      SMSC_WARNING("Failed to Receive during Loop Back Test");
      continue;
    }
    if(dwStatus&RX_STS_ES_)
    {
      SMSC_WARNING("Receive encountered errors during Loop Back Test");
      continue;
    }

    dwPacketLength=((dwStatus&0x3FFF0000UL)>>16);

    Rx_ReadFifo(
      privateData->dwLanBase,
      ((DWORD *)(privateData->LoopBackRxPacket)),
      (dwPacketLength+3+(((DWORD)(privateData->LoopBackRxPacket))&0x03UL))>>2);

    if(dwPacketLength!=(MIN_PACKET_SIZE+4)) {
      SMSC_WARNING("Unexpected packet size during loop back test, size=%ld, will retry",dwPacketLength);
    } else {
      DWORD byteIndex=0;
      BOOLEAN foundMissMatch=FALSE;
      for(byteIndex=0;byteIndex<MIN_PACKET_SIZE;byteIndex++) {
        if(privateData->LoopBackTxPacket[byteIndex]!=privateData->LoopBackRxPacket[byteIndex])
        {
          foundMissMatch=TRUE;
          break;
        }
      }
      if(!foundMissMatch) {
        SMSC_TRACE("Successfully Verified Loop Back Packet");
        result=TRUE;
        goto DONE;
      } else {
        SMSC_WARNING("Data miss match during loop back test, will retry.");
      }
    }
  }
DONE:
  return result;
}


/*==============================================================================
  <NAME>
  Phy_LoopBackTest

  <SYNOPSIS>
  BOOLEAN Phy_LoopBackTest(PPRIVATE_DATA privateData)

  <OPERANDS>
  Parameter:
  Return value:

  <DESCRIPITON>
  
  <NOTES>
  
  <BUGS>
  
  ============================================================================*/
static
BOOLEAN Phy_LoopBackTest(PPRIVATE_DATA privateData)
{
  BOOLEAN result=FALSE;
  DWORD byteIndex=0;
  DWORD tryCount=0;
  //Initialize Tx Packet
  for(byteIndex=0;byteIndex<6;byteIndex++) {
    //use broadcast destination address
    privateData->LoopBackTxPacket[byteIndex]=(BYTE)0xFF;
  }
  for(byteIndex=6;byteIndex<12;byteIndex++) {
    //use incrementing source address
    privateData->LoopBackTxPacket[byteIndex]=(BYTE)byteIndex;
  }
  //Set length type field
  privateData->LoopBackTxPacket[12]=0x00;
  privateData->LoopBackTxPacket[13]=0x00;
  for(byteIndex=14;byteIndex<MIN_PACKET_SIZE;byteIndex++)
  {
    privateData->LoopBackTxPacket[byteIndex]=(BYTE)byteIndex;
  }
  {
    DWORD dwRegVal=Lan_GetRegDW(HW_CFG);
    dwRegVal&=HW_CFG_TX_FIF_SZ_;
    dwRegVal|=HW_CFG_SF_;
    Lan_SetRegDW(HW_CFG,dwRegVal);
  }
  Lan_SetRegDW(TX_CFG,TX_CFG_TX_ON_);
  Lan_SetRegDW(RX_CFG,(((DWORD)(privateData->LoopBackRxPacket))&0x03)<<8);

  //Set Phy to 10/FD, no ANEG,
  Phy_SetRegW(privateData,PHY_BCR,0x0100);

  //enable MAC Tx/Rx, FD
  Mac_SetRegDW(privateData,MAC_CR,MAC_CR_FDPX_|MAC_CR_TXEN_|MAC_CR_RXEN_);

  //set Phy to loopback mode
  Phy_SetRegW(privateData,PHY_BCR,0x4100);

  for(tryCount=0;tryCount<10;tryCount++) {
    if(Phy_CheckLoopBackPacket(privateData))
    {
      result=TRUE;
      goto DONE;
    }
    privateData->dwResetCount++;
    //disable MAC rx
    Mac_SetRegDW(privateData,MAC_CR,0UL);
    Phy_Reset(privateData);

    //Set Phy to 10/FD, no ANEG, and Loopbackmode
    Phy_SetRegW(privateData,PHY_BCR,0x4100);

    //enable MAC Tx/Rx, FD
    Mac_SetRegDW(privateData,MAC_CR,MAC_CR_FDPX_|MAC_CR_TXEN_|MAC_CR_RXEN_);
  }
DONE:
  //disable MAC
  Mac_SetRegDW(privateData,MAC_CR,0UL);
  //Cancel Phy loopback mode
  Phy_SetRegW(privateData,PHY_BCR,0U);

  Lan_SetRegDW(TX_CFG,0UL);
  Lan_SetRegDW(RX_CFG,0UL);

  return result;
}

#endif //USE_PHY_WORK_AROUND


/*==============================================================================
  <NAME>
  Phy_SetLink

  <SYNOPSIS>
  void Phy_SetLink(PPRIVATE_DATA privateData) 

  <OPERANDS>
  Parameter:
  Return value:

  <DESCRIPITON>
  Sets the link mode
  <NOTES>
  
  <BUGS>
  
  ============================================================================*/
static
void Phy_SetLink(PPRIVATE_DATA privateData) 
{
  WORD wTemp;

  //Because this is part of the single threaded initialization
  //  path there is no need to acquire the MacPhyAccessLock

  wTemp=Phy_GetRegW(privateData,
    PHY_ANEG_ADV);
  //Advertise all speeds and pause capabilities
  wTemp|=(PHY_ANEG_ADV_PAUSE_|PHY_ANEG_ADV_SPEED_);
  Phy_SetRegW(privateData,PHY_ANEG_ADV,wTemp);

  // begin to establish link
  /* ----- Addition of manual link mode by Sony ----- */
  /* link_speed=10Mbps */
  if (link_speed == 10){
    Phy_SetRegW(privateData,PHY_BCR,duplex_mode << 8);
  }
  /* link_speed=100Mbps */
  else if(link_speed == 100){
    Phy_SetRegW(privateData,PHY_BCR,PHY_BCR_SPEED_SELECT_ | (duplex_mode << 8));
  }
  /* link_speed=Auto negotiation */
  else{
    Phy_SetRegW(privateData,
      PHY_BCR,
      PHY_BCR_AUTO_NEG_ENABLE_|
      PHY_BCR_RESTART_AUTO_NEG_);
  }

}


/*==============================================================================
  <NAME>
  Phy_Initialize

  <SYNOPSIS>
  BOOLEAN Phy_Initialize(PPRIVATE_DATA privateData,DWORD dwPhyAddr)

  <OPERANDS>
  Parameter:
  Return value:

  <DESCRIPITON>
  Initializes the phy
  <NOTES>
  
  <BUGS>
  
  ============================================================================*/
static
BOOLEAN Phy_Initialize(PPRIVATE_DATA privateData,DWORD dwPhyAddr)
{
  BOOLEAN result=FALSE;
#ifndef USE_PHY_WORK_AROUND
  WORD wTemp=0;
#endif
  WORD wPhyId1=0;
  WORD wPhyId2=0;
#ifndef USE_PHY_WORK_AROUND
  DWORD dwLoopCount=0;
#endif

  SMSC_TRACE("-->Phy_Initialize");
  SMSC_ASSERT(privateData!=NULL);
  SMSC_ASSERT(privateData->dwLanBase!=0);

  if(dwPhyAddr!=0xFFFFFFFFUL) {
    switch(privateData->dwIdRev&0xFFFF0000) {
    case 0x01170000UL:
    case 0x01150000UL:
      {
        DWORD dwHwCfg=Lan_GetRegDW(HW_CFG);
        if(dwHwCfg&HW_CFG_EXT_PHY_DET_) {
                    //External phy is requested, supported, and detected
          //Attempt to switch
          //NOTE: Assuming Rx and Tx are stopped
          //  because Phy_Initialize is called before 
          //  Rx_Initialize and Tx_Initialize

          //Disable phy clocks to the mac
          dwHwCfg&= (~HW_CFG_PHY_CLK_SEL_);
          dwHwCfg|= HW_CFG_PHY_CLK_SEL_CLK_DIS_;
          Lan_SetRegDW(HW_CFG,dwHwCfg);
          udelay(10);//wait for clocks to acutally stop

          //switch to external phy
          dwHwCfg|=HW_CFG_EXT_PHY_EN_;
          Lan_SetRegDW(HW_CFG,dwHwCfg);

          //Enable phy clocks to the mac
          dwHwCfg&= (~HW_CFG_PHY_CLK_SEL_);
          dwHwCfg|= HW_CFG_PHY_CLK_SEL_EXT_PHY_;
          Lan_SetRegDW(HW_CFG,dwHwCfg);
          udelay(10);//wait for clocks to actually start

          dwHwCfg|=HW_CFG_SMI_SEL_;
          Lan_SetRegDW(HW_CFG,dwHwCfg);

          //Because this is part of the single threaded initialization
          //  path there is no need to acquire the MacPhyAccessLock
          if(dwPhyAddr<=31) {
            //only check the phy address specified
            privateData->dwPhyAddress=dwPhyAddr;
            wPhyId1=Phy_GetRegW(privateData,PHY_ID_1);
            wPhyId2=Phy_GetRegW(privateData,PHY_ID_2);
          } else {
            //auto detect phy
            DWORD address=0;
            for(address=0;address<=31;address++) {
              privateData->dwPhyAddress=address;
              wPhyId1=Phy_GetRegW(privateData,PHY_ID_1);
              wPhyId2=Phy_GetRegW(privateData,PHY_ID_2);
              if((wPhyId1!=0xFFFFU)||(wPhyId2!=0xFFFFU)) {
                SMSC_TRACE("Detected Phy at address = 0x%02lX = %ld",
                  address,address);
                break;
              }
            }
            if(address>=32) {
              SMSC_WARNING("Failed to auto detect external phy");
            }
          }
          if((wPhyId1==0xFFFFU)&&(wPhyId2==0xFFFFU)) {
            SMSC_WARNING("External Phy is not accessable");
            SMSC_WARNING("  using internal phy instead");
            //revert back to interal phy settings.

            //Disable phy clocks to the mac
            dwHwCfg&= (~HW_CFG_PHY_CLK_SEL_);
            dwHwCfg|= HW_CFG_PHY_CLK_SEL_CLK_DIS_;
            Lan_SetRegDW(HW_CFG,dwHwCfg);
            udelay(10);//wait for clocks to actually stop

            //switch to internal phy
            dwHwCfg&=(~HW_CFG_EXT_PHY_EN_);
            Lan_SetRegDW(HW_CFG,dwHwCfg);
  
            //Enable phy clocks to the mac
            dwHwCfg&= (~HW_CFG_PHY_CLK_SEL_);
            dwHwCfg|= HW_CFG_PHY_CLK_SEL_INT_PHY_;
            Lan_SetRegDW(HW_CFG,dwHwCfg);
            udelay(10);//wait for clocks to actually start

            dwHwCfg&=(~HW_CFG_SMI_SEL_);
            Lan_SetRegDW(HW_CFG,dwHwCfg);
            goto USE_INTERNAL_PHY;
          } else {
            SMSC_TRACE("Successfully switched to external phy");
#ifdef USE_LED1_WORK_AROUND
            privateData->NotUsingExtPhy=0;
#endif
          }
        } else {
          SMSC_WARNING("No External Phy Detected");
          SMSC_WARNING("  using internal phy instead");
          goto USE_INTERNAL_PHY;
        }
      };break;
    default:
      SMSC_WARNING("External Phy is not supported");
      SMSC_WARNING("  using internal phy instead");
      goto USE_INTERNAL_PHY;
    }
  } else {
USE_INTERNAL_PHY:
    privateData->dwPhyAddress=1;
#ifdef USE_LED1_WORK_AROUND
    privateData->NotUsingExtPhy=1;
#endif
  }
    
  //Because this is part of the single threaded initialization
  //  path there is no need to acquire the MacPhyAccessLock
  wPhyId1=Phy_GetRegW(privateData,PHY_ID_1);
  wPhyId2=Phy_GetRegW(privateData,PHY_ID_2);
  if((wPhyId1==0xFFFFU)&&(wPhyId2==0xFFFFU)) {
    SMSC_WARNING("Phy Not detected");
    goto DONE;
  }

  privateData->dwLinkSpeed=LINK_OFF;
  privateData->dwLinkSettings=LINK_OFF;
  
  //reset the PHY
#ifdef USE_PHY_WORK_AROUND
  Phy_Reset(privateData);
  if(!Phy_LoopBackTest(privateData)) {
    SMSC_WARNING("Failed Loop Back Test");
    goto DONE;
  } else {
    SMSC_TRACE("Passed Loop Back Test");
  }
#else
  Phy_SetRegW(privateData,PHY_BCR,PHY_BCR_RESET_);
    dwLoopCount=100000;
  do {
    udelay(10);
    wTemp=Phy_GetRegW(privateData,PHY_BCR);
    dwLoopCount--;
  } while((dwLoopCount>0) && (wTemp&PHY_BCR_RESET_));
  if(wTemp&PHY_BCR_RESET_) {
    SMSC_WARNING("PHY reset failed to complete.");
    goto DONE;
  }
#endif //not USE_PHY_WORK_AROUND
  
  Phy_SetLink(privateData);

  init_timer(&(privateData->LinkPollingTimer));
  privateData->LinkPollingTimer.function=Phy_CheckLink;
  privateData->LinkPollingTimer.data=(unsigned long)privateData;
  privateData->LinkPollingTimer.expires=jiffies+HZ;
  add_timer(&(privateData->LinkPollingTimer));

  result=TRUE;
DONE:
  SMSC_TRACE("<--Phy_Initialize, result=%s",result?"TRUE":"FALSE");
  return result;
}


/*==============================================================================
  <NAME>
  Phy_GetRegW

  <SYNOPSIS>
  WORD Phy_GetRegW(PPRIVATE_DATA privateData,DWORD dwRegIndex)

  <OPERANDS>
  Parameter:
  Return value:

  <DESCRIPITON>
  Gets a phy register
  <NOTES>
  
  <BUGS>
  
  ============================================================================*/
static
WORD Phy_GetRegW(PPRIVATE_DATA privateData,DWORD dwRegIndex)
{
  DWORD dwAddr=0;
  int i=0;
  WORD result=0xFFFFU;

  SMSC_ASSERT(privateData!=NULL);

  // confirm MII not busy
  if ((Mac_GetRegDW(privateData, MII_ACC) & MII_ACC_MII_BUSY_) != 0UL)
  {
    SMSC_WARNING("MII is busy in Phy_GetRegW???");
    result=0;
    goto DONE;
  }

  // set the address, index & direction (read from PHY)
  dwAddr = (((privateData->dwPhyAddress) & 0x1FUL)<<11) | ((dwRegIndex & 0x1FUL)<<6);
  Mac_SetRegDW(privateData, MII_ACC, dwAddr);

  // wait for read to complete w/ timeout
  for(i=0;i<100;i++) {
    // see if MII is finished yet
    if ((Mac_GetRegDW(privateData, MII_ACC) & MII_ACC_MII_BUSY_) == 0UL)
    {
      // get the read data from the MAC & return i
      result=((WORD)Mac_GetRegDW(privateData, MII_DATA));
      goto DONE;
    }
  }
  SMSC_WARNING("timeout waiting for MII write to finish");

DONE:
  return result;
}


/*==============================================================================
  <NAME>
  Phy_SetRegW

  <SYNOPSIS>
  void Phy_SetRegW(PPRIVATE_DATA privateData,DWORD dwRegIndex,WORD wVal)

  <OPERANDS>
  Parameter:
  Return value:

  <DESCRIPITON>
  Sets a phy register
  <NOTES>
  
  <BUGS>
  
  ============================================================================*/
static
void Phy_SetRegW(PPRIVATE_DATA privateData,DWORD dwRegIndex,WORD wVal)
{
  DWORD dwAddr=0;
  int i=0;

  SMSC_ASSERT(privateData!=NULL);

  // confirm MII not busy
  if ((Mac_GetRegDW(privateData, MII_ACC) & MII_ACC_MII_BUSY_) != 0UL)
  {
    SMSC_WARNING("MII is busy in Phy_SetRegW???");
    goto DONE;
  }

  // put the data to write in the MAC
  Mac_SetRegDW(privateData, MII_DATA, (DWORD)wVal);

  // set the address, index & direction (write to PHY)
  dwAddr = (((privateData->dwPhyAddress) & 0x1FUL)<<11) | ((dwRegIndex & 0x1FUL)<<6) | MII_ACC_MII_WRITE_;
  Mac_SetRegDW(privateData, MII_ACC, dwAddr);

  // wait for write to complete w/ timeout
  for(i=0;i<100;i++) {
    // see if MII is finished yet
    if ((Mac_GetRegDW(privateData, MII_ACC) & MII_ACC_MII_BUSY_) == 0UL)
    {
      goto DONE;
    }
  }
  SMSC_WARNING("timeout waiting for MII write to finish");
DONE:
  return;
}


/*==============================================================================
  <NAME>
  Phy_UpdateLinkMode

  <SYNOPSIS>
  void Phy_UpdateLinkMode(PPRIVATE_DATA privateData)

  <OPERANDS>
  Parameter:
  Return value:

  <DESCRIPITON>
  Update link mode if any thing has changed
  <NOTES>
  
  <BUGS>
  
  ============================================================================*/
static
void Phy_UpdateLinkMode(PPRIVATE_DATA privateData)
{
  DWORD dwOldLinkSpeed=privateData->dwLinkSpeed;

  spin_lock(&(privateData->MacPhyAccessLock));

  Phy_GetLinkMode(privateData);

  if(dwOldLinkSpeed!=(privateData->dwLinkSpeed)) {
    if(privateData->dwLinkSpeed!=LINK_OFF) {
      DWORD dwRegVal=0;
      switch(privateData->dwLinkSpeed) {
      case LINK_SPEED_10HD:
        SMSC_TRACE("Link is now UP at 10Mbps HD");
        break;
      case LINK_SPEED_10FD:
        SMSC_TRACE("Link is now UP at 10Mbps FD");
        break;
      case LINK_SPEED_100HD:
        SMSC_TRACE("Link is now UP at 100Mbps HD");
        break;
      case LINK_SPEED_100FD:
        SMSC_TRACE("Link is now UP at 100Mbps FD");
        break;
      default:
        SMSC_WARNING("Link is now UP at Unknown Link Speed, dwLinkSpeed=0x%08lX",
          privateData->dwLinkSpeed);
        break;
      }
    
      dwRegVal=Mac_GetRegDW(privateData,MAC_CR);
      dwRegVal&=~(MAC_CR_FDPX_|MAC_CR_RCVOWN_);
      switch(privateData->dwLinkSpeed) {
      case LINK_SPEED_10HD:
      case LINK_SPEED_100HD:
        dwRegVal|=MAC_CR_RCVOWN_;
        break;
      case LINK_SPEED_10FD:
      case LINK_SPEED_100FD:
        dwRegVal|=MAC_CR_FDPX_;
        break;
      default:
        SMSC_WARNING("Unknown Link Speed, dwLinkSpeed=0x%08lX",
          privateData->dwLinkSpeed);
        break;
      }

      Mac_SetRegDW(privateData,
        MAC_CR,dwRegVal);

      if(privateData->dwLinkSettings&LINK_AUTO_NEGOTIATE) {
        WORD linkPartner=0;
        WORD localLink=0;
        localLink=Phy_GetRegW(privateData,4);
        linkPartner=Phy_GetRegW(privateData,5);
        switch(privateData->dwLinkSpeed) {
        case LINK_SPEED_10FD:
        case LINK_SPEED_100FD:
          if(((localLink&linkPartner)&((WORD)0x0400U)) != ((WORD)0U)) {
            //Enable PAUSE receive and transmit
            Mac_SetRegDW(privateData,FLOW,0xFFFF0002UL);
            Lan_SetBitsDW(AFC_CFG,0x0000000FUL);
          } else if(((localLink&((WORD)0x0C00U))==((WORD)0x0C00U)) &&
              ((linkPartner&((WORD)0x0C00U))==((WORD)0x0800U)))
          {
            //Enable PAUSE receive, disable PAUSE transmit
            Mac_SetRegDW(privateData,FLOW,0xFFFF0002UL);
            Lan_ClrBitsDW(AFC_CFG,0x0000000FUL);
          } else {
            //Disable PAUSE receive and transmit
            Mac_SetRegDW(privateData,FLOW,0UL);
            Lan_ClrBitsDW(AFC_CFG,0x0000000FUL);
          };break;
        case LINK_SPEED_10HD:
        case LINK_SPEED_100HD:
          Mac_SetRegDW(privateData,FLOW,0UL);
          Lan_SetBitsDW(AFC_CFG,0x0000000FUL);
          break;
        default:
          SMSC_WARNING("Unknown Link Speed, dwLinkSpeed=0x%08lX\n",privateData->dwLinkSpeed);
          break;
        }
        SMSC_TRACE("LAN911x: %s,%s,%s,%s,%s,%s",
          (localLink&PHY_ANEG_ADV_ASYMP_)?"ASYMP":"     ",
          (localLink&PHY_ANEG_ADV_SYMP_)?"SYMP ":"     ",
          (localLink&PHY_ANEG_ADV_100F_)?"100FD":"     ",
          (localLink&PHY_ANEG_ADV_100H_)?"100HD":"     ",
          (localLink&PHY_ANEG_ADV_10F_)?"10FD ":"     ",
          (localLink&PHY_ANEG_ADV_10H_)?"10HD ":"     ");
  
        SMSC_TRACE("Partner: %s,%s,%s,%s,%s,%s",
          (linkPartner&PHY_ANEG_LPA_ASYMP_)?"ASYMP":"     ",
          (linkPartner&PHY_ANEG_LPA_SYMP_)?"SYMP ":"     ",
          (linkPartner&PHY_ANEG_LPA_100FDX_)?"100FD":"     ",
          (linkPartner&PHY_ANEG_LPA_100HDX_)?"100HD":"     ",
          (linkPartner&PHY_ANEG_LPA_10FDX_)?"10FD ":"     ",
          (linkPartner&PHY_ANEG_LPA_10HDX_)?"10HD ":"     ");
      } else {
        switch(privateData->dwLinkSpeed) {
        case LINK_SPEED_10HD:
        case LINK_SPEED_100HD:
          Mac_SetRegDW(privateData,FLOW,0x0UL);
          Lan_SetBitsDW(AFC_CFG,0x0000000FUL);
          break;
        default:
          Mac_SetRegDW(privateData,FLOW,0x0UL);
          Lan_ClrBitsDW(AFC_CFG,0x0000000FUL);
          break;
        }
      }
      netif_carrier_on(privateData->dev);
#ifdef USE_LED1_WORK_AROUND
      if ((g_GpioSettingOriginal & GPIO_CFG_LED1_EN_) &&
        privateData->NotUsingExtPhy)
      {
        // Restore orginal GPIO configuration
        g_GpioSetting = g_GpioSettingOriginal;
        Lan_SetRegDW(GPIO_CFG,g_GpioSetting);
      }
#endif // USE_LED1_WORK_AROUND
    } else {
      SMSC_TRACE("Link is now DOWN");
      netif_carrier_off(privateData->dev);
      Mac_SetRegDW(privateData,FLOW,0UL);
      Lan_ClrBitsDW(AFC_CFG,0x0000000FUL);
#ifdef USE_LED1_WORK_AROUND
      // Check global setting that LED1 usage is 10/100 indicator
      g_GpioSetting = Lan_GetRegDW(GPIO_CFG);
      if ((g_GpioSetting & GPIO_CFG_LED1_EN_) &&
        privateData->9118testNotUsingExtPhy)
      {
        //Force 10/100 LED off, after saving orginal GPIO configuration
        g_GpioSettingOriginal = g_GpioSetting;

        g_GpioSetting &= ~GPIO_CFG_LED1_EN_;
        g_GpioSetting |=
          (GPIO_CFG_GPIOBUF0_|GPIO_CFG_GPIODIR0_|GPIO_CFG_GPIOD0_);
        Lan_SetRegDW(GPIO_CFG,g_GpioSetting);
      }
#endif // USE_LED1_WORK_AROUND
    }
  }
  spin_unlock(&(privateData->MacPhyAccessLock));
}


/*==============================================================================
  <NAME>
  Phy_CheckLink

  <SYNOPSIS>
  void Phy_CheckLink(unsigned long ptr)

  <OPERANDS>
  Parameter:
  Return value:

  <DESCRIPITON>
  entry point for the link poller
  <NOTES>
  
  <BUGS>
  
  ============================================================================*/
static
void Phy_CheckLink(unsigned long ptr)
{
  PPRIVATE_DATA privateData=(PPRIVATE_DATA)ptr;
  if(privateData==NULL) {
    SMSC_WARNING("Phy_CheckLink(ptr==0)");
    return;
  }

  //must call this twice
  Phy_UpdateLinkMode(privateData);
  Phy_UpdateLinkMode(privateData);

  if(!(privateData->StopLinkPolling)) {
    privateData->LinkPollingTimer.expires=jiffies+HZ;
    add_timer(&(privateData->LinkPollingTimer));
  }
}


/*==============================================================================
  <NAME>
  Phy_GetLinkMode

  <SYNOPSIS>
  void Phy_GetLinkMode(PPRIVATE_DATA privateData)

  <OPERANDS>
  Parameter:
  Return value:

  <DESCRIPITON>
  gets the current link mode
  <NOTES>
  
  <BUGS>
  
  ============================================================================*/
static
void Phy_GetLinkMode(PPRIVATE_DATA privateData)
{
  DWORD result=LINK_OFF;
  WORD wRegVal=0;
  WORD wRegBSR=0;

  //Assuming MacPhyAccessLock has already been acquired

  wRegBSR=Phy_GetRegW(
    privateData,
    PHY_BSR);
    
  privateData->dwLinkSettings=LINK_OFF;
  if(wRegBSR&PHY_BSR_LINK_STATUS_) {
    wRegVal=Phy_GetRegW(
      privateData,
      PHY_BCR);

    if(wRegVal&PHY_BCR_AUTO_NEG_ENABLE_) {
      DWORD linkSettings=LINK_AUTO_NEGOTIATE;
      WORD wRegADV=Phy_GetRegW(
        privateData,
        PHY_ANEG_ADV);

      WORD wRegLPA=Phy_GetRegW(
        privateData,
        PHY_ANEG_LPA);

      if(wRegADV&PHY_ANEG_ADV_ASYMP_) {
        linkSettings|=LINK_ASYMMETRIC_PAUSE;
      }
      if(wRegADV&PHY_ANEG_ADV_SYMP_) {
        linkSettings|=LINK_SYMMETRIC_PAUSE;
      }
      if(wRegADV&PHY_ANEG_LPA_100FDX_) {
        linkSettings|=LINK_SPEED_100FD;
      }
      if(wRegADV&PHY_ANEG_LPA_100HDX_) {
        linkSettings|=LINK_SPEED_100HD;
      }
      if(wRegADV&PHY_ANEG_LPA_10FDX_) {
        linkSettings|=LINK_SPEED_10FD;
      }
      if(wRegADV&PHY_ANEG_LPA_10HDX_) {
        linkSettings|=LINK_SPEED_10HD;
      }
      privateData->dwLinkSettings=linkSettings;
      wRegLPA&=wRegADV;
      if(wRegLPA&PHY_ANEG_LPA_100FDX_) {
        result=LINK_SPEED_100FD;
      } else if(wRegLPA&PHY_ANEG_LPA_100HDX_) {
        result=LINK_SPEED_100HD;
      } else if(wRegLPA&PHY_ANEG_LPA_10FDX_) {
        result=LINK_SPEED_10FD;
      } else if(wRegLPA&PHY_ANEG_LPA_10HDX_) {
        result=LINK_SPEED_10HD;
      }
    } else {
      if(wRegVal&PHY_BCR_SPEED_SELECT_) {
        if(wRegVal&PHY_BCR_DUPLEX_MODE_) {
          privateData->dwLinkSettings=result=LINK_SPEED_100FD;
        } else {
          privateData->dwLinkSettings=result=LINK_SPEED_100HD;
        }
      } else {
        if(wRegVal&PHY_BCR_DUPLEX_MODE_) {
          privateData->dwLinkSettings=result=LINK_SPEED_10FD;
        } else {
          privateData->dwLinkSettings=result=LINK_SPEED_10HD;
        }
      }
    }
  }
  privateData->dwLinkSpeed=result;
}


/*==============================================================================
  <NAME>
  Mac_Initialize

  <SYNOPSIS>
  void Mac_Initialize(PPRIVATE_DATA privateData)

  <OPERANDS>
  Parameter:
  Return value:

  <DESCRIPITON>
  Initalize the Mac data elements
  <NOTES>
  
  <BUGS>
  
  ============================================================================*/
static
void Mac_Initialize(PPRIVATE_DATA privateData)
{
  SMSC_ASSERT(privateData!=NULL);

  //nothing to do here in the simple driver
  //  but this function is kept as a place holder.

}


/*==============================================================================
  <NAME>
  MacNotBusy

  <SYNOPSIS>
  static BOOLEAN MacNotBusy(PPRIVATE_DATA privateData)

  <OPERANDS>
  Parameter:
  Return value:

  <DESCRIPITON>
  Check for not busy
  <NOTES>
  
  <BUGS>
  
  ============================================================================*/
static BOOLEAN MacNotBusy(PPRIVATE_DATA privateData)
{
  int i=0;

  //Assuming MacPhyAccessLock has already been acquired

  // wait for MAC not busy, w/ timeout
  for(i=0;i<40;i++)
  {
    if((Lan_GetRegDW(MAC_CSR_CMD) & MAC_CSR_CMD_CSR_BUSY_)==(0UL)) {
      return TRUE;
    }
  }
  SMSC_WARNING("timeout waiting for MAC not BUSY. MAC_CSR_CMD = 0x%08lX",
    Lan_GetRegDW(MAC_CSR_CMD));
  return FALSE;
}


/*==============================================================================
  <NAME>
  Mac_GetRegDW

  <SYNOPSIS>
  DWORD Mac_GetRegDW(PPRIVATE_DATA privateData,DWORD dwRegOffset)

  <OPERANDS>
  Parameter:
  Return value:

  <DESCRIPITON>
  Gets a mac register value
  <NOTES>
  
  <BUGS>
  
  ============================================================================*/
static
DWORD Mac_GetRegDW(PPRIVATE_DATA privateData,DWORD dwRegOffset)
{
  DWORD result=0xFFFFFFFFUL;
  DWORD dwTemp=0;
  SMSC_ASSERT(privateData!=NULL);
  SMSC_ASSERT(privateData->dwLanBase!=0);
  
  //Assuming MacPhyAccessLock has already been acquired

  // wait until not busy
  if (Lan_GetRegDW(MAC_CSR_CMD) & MAC_CSR_CMD_CSR_BUSY_)
  {
    SMSC_WARNING("Mac_GetRegDW() failed, MAC already busy at entry");
    goto DONE;
  }

  // send the MAC Cmd w/ offset
  Lan_SetRegDW(MAC_CSR_CMD,
    ((dwRegOffset & 0x000000FFUL) | MAC_CSR_CMD_CSR_BUSY_ | MAC_CSR_CMD_R_NOT_W_));
  dwTemp=Lan_GetRegDW(BYTE_TEST);//to flush previous write
  dwTemp=dwTemp;

  // wait for the read to happen, w/ timeout
  if (!MacNotBusy(privateData))
  {
    SMSC_WARNING("Mac_GetRegDW() failed, waiting for MAC not busy after read");
    goto DONE;
  } else {
    // finally, return the read data
    result=Lan_GetRegDW(MAC_CSR_DATA);
  }
DONE:
  return result;
}


/*==============================================================================
  <NAME>
  Mac_SetRegDW

  <SYNOPSIS>
  void Mac_SetRegDW(PPRIVATE_DATA privateData,DWORD dwRegOffset,DWORD dwVal)

  <OPERANDS>
  Parameter:
  Return value:

  <DESCRIPITON>
  Sets a Mac register
  <NOTES>
  
  <BUGS>
  
  ============================================================================*/
static
void Mac_SetRegDW(PPRIVATE_DATA privateData,DWORD dwRegOffset,DWORD dwVal)
{
  DWORD dwTemp=0;
  SMSC_ASSERT(privateData!=NULL);
  SMSC_ASSERT(privateData->dwLanBase!=0);

  //Assuming MacPhyAccessLock has already been acquired

  if (Lan_GetRegDW(MAC_CSR_CMD) & MAC_CSR_CMD_CSR_BUSY_)
  {
    SMSC_WARNING("Mac_SetRegDW() failed, MAC already busy at entry");
    goto DONE;
  }

  // send the data to write
  Lan_SetRegDW(MAC_CSR_DATA,dwVal);

  // do the actual write
  Lan_SetRegDW(MAC_CSR_CMD,((dwRegOffset & 0x000000FFUL) | MAC_CSR_CMD_CSR_BUSY_));
  dwTemp=Lan_GetRegDW(BYTE_TEST);//force flush of previous write
  dwTemp=dwTemp;

  // wait for the write to complete, w/ timeout
  if (!MacNotBusy(privateData))
  {
    SMSC_WARNING("Mac_SetRegDW() failed, waiting for MAC not busy after write");
  }
DONE:
  return;
}


/*==============================================================================
  <NAME>
  Tx_Initialize

  <SYNOPSIS>
  void Tx_Initialize(PPRIVATE_DATA privateData)

  <OPERANDS>
  Parameter:
  Return value:

  <DESCRIPITON>
  initializes the Transmitter
  <NOTES>
  
  <BUGS>
  
  ============================================================================*/
static
void Tx_Initialize(PPRIVATE_DATA privateData)
{
  DWORD dwRegVal=0;
  SMSC_ASSERT(privateData!=NULL);
  SMSC_ASSERT(privateData->dwLanBase!=0);

  dwRegVal=Lan_GetRegDW(HW_CFG);
  dwRegVal&=HW_CFG_TX_FIF_SZ_;
  dwRegVal|=HW_CFG_SF_;
  Lan_SetRegDW(HW_CFG,dwRegVal);

  Lan_SetBitsDW(FIFO_INT,0xFF000000UL);
  Lan_SetBitsDW(INT_EN,INT_EN_TDFA_EN_);

  //Because this is part of the single threaded initialization
  //  path there is no need to acquire the MacPhyAccessLock
  {
    DWORD dwMacCr=Mac_GetRegDW(privateData,MAC_CR);
    dwMacCr|=(MAC_CR_TXEN_|MAC_CR_HBDIS_);
    Mac_SetRegDW(privateData,MAC_CR,dwMacCr);
    Lan_SetRegDW(TX_CFG,TX_CFG_TX_ON_);
  }
}


/*==============================================================================
  <NAME>
  Tx_HandleInterrupt

  <SYNOPSIS>
  BOOLEAN Tx_HandleInterrupt(PPRIVATE_DATA privateData,DWORD dwIntSts)

  <OPERANDS>
  Parameter:
  Return value:

  <DESCRIPITON>
  Reenables the Transmitter
  <NOTES>
  
  <BUGS>
  
  ============================================================================*/
static
BOOLEAN Tx_HandleInterrupt(PPRIVATE_DATA privateData,DWORD dwIntSts)
{
  SMSC_ASSERT(privateData!=NULL);
  if(dwIntSts&INT_STS_TDFA_) 
  {
    Lan_SetBitsDW(FIFO_INT,0xFF000000UL);
    Lan_SetRegDW(INT_STS,INT_STS_TDFA_);
    netif_wake_queue(privateData->dev);
    return TRUE;
  }
  return FALSE;
}


/*==============================================================================
  <NAME>
  Tx_WriteFifo

  <SYNOPSIS>
  static void Tx_WriteFifo(DWORD dwLanBase,DWORD *pdwBuf,DWORD dwDwordCount)

  <OPERANDS>
  Parameter:
  Return value:

  <DESCRIPITON>
  Writes a packet to the TX_DATA_FIFO
  <NOTES>
  
  <BUGS>
  
  ============================================================================*/
static void Tx_WriteFifo(DWORD dwLanBase,DWORD *pdwBuf,DWORD dwDwordCount)
{
  volatile DWORD *pdwReg;
  pdwReg = (volatile DWORD *)(dwLanBase+TX_DATA_FIFO);
  while(dwDwordCount)
  {
    *pdwReg = *pdwBuf++;
    dwDwordCount--;
  }
}


/*==============================================================================
  <NAME>
  Tx_GetTxStatusCount

  <SYNOPSIS>
  static DWORD Tx_GetTxStatusCount( PPRIVATE_DATA privateData)

  <OPERANDS>
  Parameter:
  Return value:

  <DESCRIPITON>
  Gets the number of Tx Statuses in the fifo
  <NOTES>
  
  <BUGS>
  
  ============================================================================*/
static DWORD Tx_GetTxStatusCount( PPRIVATE_DATA privateData)
{
  DWORD result=0;
  SMSC_ASSERT(privateData!=NULL);
  SMSC_ASSERT(privateData->dwLanBase!=0);
  result=Lan_GetRegDW(TX_FIFO_INF);
  result&=TX_FIFO_INF_TSUSED_;
  result>>=16;
  return result;
}


/*==============================================================================
  <NAME>
  Tx_SendSkb

  <SYNOPSIS>
  void Tx_SendSkb(PPRIVATE_DATA privateData,struct sk_buff *skb)

  <OPERANDS>
  Parameter:
  Return value:

  <DESCRIPITON>
  sends a packet
  <NOTES>
  It completely rewrote it by Sony. 
  
  <BUGS>
  
  ============================================================================*/
static
void Tx_SendSkb(PPRIVATE_DATA privateData,struct sk_buff *skb)
{
  SMSC_ASSERT(privateData!=NULL);
  SMSC_ASSERT(privateData->dwLanBase!=0);

#ifndef DMA_DRIVER_OFF /* Rev01 add */
  if(dma_mode == DMA_ONLY_MODE){
    cxd4105_wait4request_dma(tx_dma_ch,NULL,NULL);
    Tx_Packet_by_DMA(privateData,skb);
  }else if(dma_mode == DMA_PIO_MODE){
    if(cxd4105_request_dma(tx_dma_ch,NULL,NULL) == 0){
      Tx_Packet_by_DMA(privateData,skb);
    }else{
      Tx_Packet_by_PIO(privateData,skb);
    }
  }else{
#endif // DMA_DRIVER_OFF /* Rev01 add */
    Tx_Packet_by_PIO(privateData,skb);

#ifndef DMA_DRIVER_OFF /* Rev01 add */
  }
#endif // DMA_DRIVER_OFF /* Rev01 add */

}


/*==============================================================================
  <NAME>
  Tx_CompleteTx

  <SYNOPSIS>
  static DWORD Tx_CompleteTx(PPRIVATE_DATA privateData)

  <OPERANDS>
  Parameter:
  Return value:

  <DESCRIPITON>
  gets a tx status out of the status fifo
  <NOTES>
  
  <BUGS>
  
  ============================================================================*/
static DWORD Tx_CompleteTx(PPRIVATE_DATA privateData)
{
  DWORD result=0;
  SMSC_ASSERT(privateData!=NULL);
  SMSC_ASSERT(privateData->dwLanBase!=0);

  result=Lan_GetRegDW(TX_FIFO_INF);

  result&=TX_FIFO_INF_TSUSED_;
  if(result!=0x00000000UL) {
    result=Lan_GetRegDW(TX_STATUS_FIFO);
  } else {
    result=0;
  }

  return result;
}


/*==============================================================================
  <NAME>
  Tx_UpdateTxCounters

  <SYNOPSIS>
  void Tx_UpdateTxCounters(PPRIVATE_DATA privateData)

  <OPERANDS>
  Parameter:
  Return value:

  <DESCRIPITON>
  reads tx statuses and increments counters where necessary
  <NOTES>
  
  <BUGS>
  
  ============================================================================*/
static
void Tx_UpdateTxCounters(PPRIVATE_DATA privateData)
{
  DWORD dwTxStatus=0;
  SMSC_ASSERT(privateData!=NULL);
  while((dwTxStatus=Tx_CompleteTx(privateData))!=0)
  {
    if(dwTxStatus&0x80000000UL) {
      SMSC_WARNING("Packet tag reserved bit is high");
      //In this driver the packet tag is used as the packet
      //  length. Since a packet length can never reach
      //  the size of 0x8000, I made this bit reserved
      //  so if I ever decided to use packet tracking 
      //  tags then those tracking tags would set the 
      //  reserved bit. And I would use this control path
      //  to look up the packet and perhaps free it.
      //  As you can see I never persued this idea.
      //  because it never provided any benefit in this
      //  linux environment.
      //But it is worth noting that the "reserved bit"
      //  in the warning above does not reference a
      //  hardware defined reserved bit but rather a 
      //  driver defined reserved bit. 
    } else {
      if(dwTxStatus&0x00008000UL) {
        privateData->stats.tx_errors++;
      } else {
        privateData->stats.tx_packets++;
        privateData->stats.tx_bytes+=(dwTxStatus>>16);
      }
      if(dwTxStatus&0x00000100UL) {
        privateData->stats.collisions+=16;
        privateData->stats.tx_aborted_errors+=1;
      } else {
        privateData->stats.collisions+=
          ((dwTxStatus>>3)&0xFUL);
      }
      if(dwTxStatus&0x00000800UL) {
        privateData->stats.tx_carrier_errors+=1;
      }
      if(dwTxStatus&0x00000200UL) {
        privateData->stats.collisions++;
        privateData->stats.tx_aborted_errors++;
      }
    }
  }
}


/*==============================================================================
  <NAME>
  Rx_Initialize

  <SYNOPSIS>
  void Rx_Initialize(PPRIVATE_DATA privateData)

  <OPERANDS>
  Parameter:
  Return value:

  <DESCRIPITON>
  rinitializes the receiver
  <NOTES>
  
  <BUGS>
  
  ============================================================================*/
static
void Rx_Initialize(PPRIVATE_DATA privateData)
{
  DWORD dwMacCr=0;
  SMSC_ASSERT(privateData!=NULL);

  Lan_SetRegDW(RX_CFG,0x00000200UL);
  
  //Because this is part of the single threaded initialization
  //  path there is no need to acquire the MacPhyAccessLock
  dwMacCr=Mac_GetRegDW(privateData,MAC_CR);
  dwMacCr|=MAC_CR_RXEN_;
  Mac_SetRegDW(privateData,MAC_CR,dwMacCr);

  Lan_ClrBitsDW(FIFO_INT,0x000000FFUL);

  Lan_SetBitsDW(INT_EN,INT_EN_RSFL_EN_);
}


/*==============================================================================
  <NAME>
  Rx_ReadFifo

  <SYNOPSIS>
  static void Rx_ReadFifo(DWORD dwLanBase,DWORD *pdwBuf,DWORD dwDwordCount)

  <OPERANDS>
  Parameter:
  Return value:

  <DESCRIPITON>
  reads a packet out of the RX_DATA_FIFO
  <NOTES>
  
  <BUGS>
  
  ============================================================================*/
static void Rx_ReadFifo(DWORD dwLanBase,DWORD *pdwBuf,DWORD dwDwordCount)
{
  const volatile DWORD * const pdwReg = 
    (const volatile DWORD * const)(dwLanBase+RX_DATA_FIFO);
  
  while (dwDwordCount)
  {
    *pdwBuf++ = *pdwReg;
    dwDwordCount--;
  }
}


/*==============================================================================
  <NAME>
  Rx_ReadFifo

  <SYNOPSIS>
  static void Rx_HandOffSkb(PPRIVATE_DATA privateData,struct sk_buff *skb)

  <OPERANDS>
  Parameter:
  Return value:

  <DESCRIPITON>
  passes a packet to linux
  <NOTES>
  
  <BUGS>
  
  ============================================================================*/
static void Rx_HandOffSkb(PPRIVATE_DATA privateData,struct sk_buff *skb)
{
  int result=0;

  skb->dev=privateData->dev;
  skb->protocol= eth_type_trans(skb,privateData->dev);
  skb->ip_summed = CHECKSUM_NONE;

  result=netif_rx(skb);

  switch(result) 
  {
  case NET_RX_SUCCESS:
    break;
  case NET_RX_CN_LOW:
  case NET_RX_CN_MOD:
  case NET_RX_CN_HIGH:
  case NET_RX_DROP:
    privateData->RxCongested=TRUE;
    break;
  default:
    privateData->RxCongested=TRUE;
    SMSC_WARNING("Unknown return value from netif_rx, result=%d",result);
    break;
  }
}


/*==============================================================================
  <NAME>
  Rx_PopRxStatus

  <SYNOPSIS>
  static DWORD Rx_PopRxStatus(PPRIVATE_DATA privateData)

  <OPERANDS>
  Parameter:
  Return value:

  <DESCRIPITON>
  Gets the next rx status
  <NOTES>
  
  <BUGS>
  
  ============================================================================*/
static DWORD Rx_PopRxStatus(PPRIVATE_DATA privateData)
{
  DWORD result=Lan_GetRegDW(RX_FIFO_INF);
  if((privateData->RxCongested==FALSE)||
    ((privateData->RxCongested==TRUE)&&((result&0x00FF0000UL)==0UL)))
  {
    if(result&0x00FF0000UL) {
      //Rx status is available, read it
      result=Lan_GetRegDW(RX_STATUS_FIFO);
    } else {
      result=0;
    }
  } else {
    //  Initiate the interrupt deassertion interval
    Lan_SetBitsDW(INT_CFG,INT_CFG_INT_DEAS_CLR_);
    result=0;
  }
  return result;
}


/*==============================================================================
  <NAME>
  Rx_CountErrors

  <SYNOPSIS>
  void Rx_CountErrors(PPRIVATE_DATA privateData,DWORD dwRxStatus) 

  <OPERANDS>
  Parameter:
  Return value:

  <DESCRIPITON>
  Increments the Rx error counters
  <NOTES>
  
  <BUGS>
  
  ============================================================================*/
static
void Rx_CountErrors(PPRIVATE_DATA privateData,DWORD dwRxStatus) 
{
  BOOLEAN crcError=FALSE;
  if(dwRxStatus&0x00008000UL) {
    privateData->stats.rx_errors++;
    if(dwRxStatus&0x00000002UL) {
      privateData->stats.rx_crc_errors++;
      crcError=TRUE;
    }
  }
  if(!crcError) {
    if(dwRxStatus&0x00001020UL) {
      //Frame type indicates length, and length error is set
      privateData->stats.rx_length_errors++;
    }
    if(dwRxStatus&RX_STS_MCAST_) {
      privateData->stats.multicast++;
    }
  }
}


/*==============================================================================
  <NAME>
  Rx_FastForward

  <SYNOPSIS>
  void Rx_FastForward(PPRIVATE_DATA privateData,DWORD dwDwordCount)

  <OPERANDS>
  Parameter:
  Return value:

  <DESCRIPITON>
  This function is used to quickly dump bad packets
  <NOTES>
  
  <BUGS>
  
  ============================================================================*/
static
void Rx_FastForward(PPRIVATE_DATA privateData,DWORD dwDwordCount)
{
  if(dwDwordCount>=4) 
  {
    DWORD dwTimeOut=500;
    Lan_SetRegDW(RX_DP_CTRL,RX_DP_CTRL_RX_FFWD_);
    while((dwTimeOut)&&(Lan_GetRegDW(RX_DP_CTRL)&RX_DP_CTRL_RX_FFWD_))
    {
      udelay(1);
      dwTimeOut--;
    }
    if(dwTimeOut==0) {
      SMSC_WARNING("timed out waiting for RX FFWD to finish, RX_DP_CTRL=0x%08lX",
        Lan_GetRegDW(RX_DP_CTRL));
    }
  } else {
    while(dwDwordCount) {
      DWORD dwTemp=Lan_GetRegDW(RX_DATA_FIFO);
      dwTemp=dwTemp;
      dwDwordCount--;
    }
  }
}


/*==============================================================================
  <NAME>
  Rx_ProcessPackets

  <SYNOPSIS>
  void Rx_ProcessPackets(PPRIVATE_DATA privateData)

  <OPERANDS>
  Parameter:
  Return value:

  <DESCRIPITON>
  This is the main function for reading packets out of the LAN911x
  
  <NOTES>
  It completely rewrote it by Sony. 

  <BUGS>
  
  ============================================================================*/
static
void Rx_ProcessPackets(PPRIVATE_DATA privateData)
{
  privateData->RxCongested=FALSE;

#ifndef DMA_DRIVER_OFF /* Rev01 add */
  if(dma_mode == DMA_ONLY_MODE){
    cxd4105_wait4request_dma(rx_dma_ch,NULL,NULL);
    Rx_Packet_by_DMA(privateData);
  }else if(dma_mode == DMA_PIO_MODE){
    if(cxd4105_request_dma(rx_dma_ch,NULL,NULL) == 0){
      Rx_Packet_by_DMA(privateData);
    }else{
      Rx_Packet_by_PIO(privateData);
    }
  }else{
#endif // DMA_DRIVER_OFF /* Rev01 add */
    Rx_Packet_by_PIO(privateData);
#ifndef DMA_DRIVER_OFF /* Rev01 add */
  }
#endif // DMA_DRIVER_OFF /* Rev01 add */

  privateData->stats.rx_dropped+=Lan_GetRegDW(RX_DROP);
  Lan_SetRegDW(INT_STS,INT_STS_RSFL_);
}


/*==============================================================================
  <NAME>
  Rx_ProcessPacketsTasklet

  <SYNOPSIS>
  void Rx_ProcessPacketsTasklet(unsigned long data) 

  <OPERANDS>
  Parameter:
  Return value:

  <DESCRIPITON>
  This is the tasklet entry point
  <NOTES>
  
  <BUGS>
  
  ============================================================================*/
static
void Rx_ProcessPacketsTasklet(unsigned long data) 
{
    PPRIVATE_DATA privateData=(PPRIVATE_DATA)(Rx_TaskletParameter[data]);
  if(privateData==NULL) {
    SMSC_WARNING("Rx_ProcessPacketsTasklet(privateData==NULL)");
    return;
  }
  Rx_ProcessPackets(privateData);
  Lan_SetBitsDW(INT_CFG,INT_CFG_IRQ_EN_);
}


/*==============================================================================
  <NAME>
  Rx_HandleInterrupt

  <SYNOPSIS>
  BOOLEAN Rx_HandleInterrupt(PPRIVATE_DATA privateData,DWORD dwIntSts)

  <OPERANDS>
  Parameter:
  Return value:

  <DESCRIPITON>
  Handles receiver interrupts
  <NOTES>
  
  <BUGS>
  
  ============================================================================*/
static
BOOLEAN Rx_HandleInterrupt(PPRIVATE_DATA privateData,DWORD dwIntSts)
{
  BOOLEAN result=FALSE;
  SMSC_ASSERT(privateData!=NULL);

  if(dwIntSts&INT_STS_RXE_) {
    Lan_SetRegDW(INT_STS,INT_STS_RXE_);
    result=TRUE;
  }
  
  if(!(dwIntSts&INT_STS_RSFL_)) {
    return result;
  }
  result=TRUE;

  Lan_ClrBitsDW(INT_CFG,INT_CFG_IRQ_EN_);
  Rx_TaskletParameter[privateData->dwInstanceIndex]=
    (unsigned long)privateData;
  switch(privateData->dwInstanceIndex) {
  case 0:tasklet_schedule(&Rx_Tasklet0);break;
#if 0
  case 1:tasklet_schedule(&Rx_Tasklet1);break;
  case 2:tasklet_schedule(&Rx_Tasklet2);break;
  case 3:tasklet_schedule(&Rx_Tasklet3);break;
  case 4:tasklet_schedule(&Rx_Tasklet4);break;
  case 5:tasklet_schedule(&Rx_Tasklet5);break;
  case 6:tasklet_schedule(&Rx_Tasklet6);break;
  case 7:tasklet_schedule(&Rx_Tasklet7);break;
#endif
  default:break;
  }

  return result;
}


#if 0
/*==============================================================================
  <NAME>
  Hash

  <SYNOPSIS>
  static DWORD Hash(BYTE addr[6])

  <OPERANDS>
  Parameter:
  Return value:

  <DESCRIPITON>
  returns hash bit number for given MAC address
  example:
     01 00 5E 00 00 01 -> returns bit number 31
  <NOTES>
  
  <BUGS>
  
  ============================================================================*/
static DWORD Hash(BYTE addr[6])
{
  int i;
  DWORD crc=0xFFFFFFFFUL;
  DWORD poly=0xEDB88320UL;
  DWORD result=0;
  for(i=0;i<6;i++) 
  {
    int bit;
    DWORD data=((DWORD)addr[i]);
    for(bit=0;bit<8;bit++) 
    {
      DWORD p = (crc^((DWORD)data))&1UL;
      crc >>= 1;
      if(p!=0) crc ^= poly;
      data >>=1;
    }
  }
  result=((crc&0x01UL)<<5)|
    ((crc&0x02UL)<<3)|
    ((crc&0x04UL)<<1)|
    ((crc&0x08UL)>>1)|
    ((crc&0x10UL)>>3)|
    ((crc&0x20UL)>>5);
  return result;
}


/*==============================================================================
  <NAME>
  Rx_SetMulticastList

  <SYNOPSIS>
  void Rx_SetMulticastList(struct net_device *dev)

  <OPERANDS>
  Parameter:
  Return value:

  <DESCRIPITON>
  Sets addressing modes
  <NOTES>
  
  <BUGS>
  
  ============================================================================*/
static
void Rx_SetMulticastList(struct net_device *dev)
{
  PPRIVATE_DATA privateData=NULL;
  SMSC_ASSERT(dev!=NULL);

  privateData=((PPRIVATE_DATA)(dev->priv));
    SMSC_ASSERT(privateData!=NULL);    

  spin_lock(&(privateData->MacPhyAccessLock));

  if(dev->flags & IFF_PROMISC) {
    //enabling promiscuous mode
    DWORD dwMacCr=0;
    dwMacCr=Mac_GetRegDW(privateData,MAC_CR);
    dwMacCr|=MAC_CR_PRMS_;
    dwMacCr&=(~MAC_CR_MCPAS_);
    dwMacCr&=(~MAC_CR_HPFILT_);
    Mac_SetRegDW(privateData,MAC_CR,dwMacCr);
    goto end;
  }

  if(dev->flags & IFF_ALLMULTI) {
    //enabling all multicast mode
    DWORD dwMacCr=0;
    dwMacCr=Mac_GetRegDW(privateData,MAC_CR);
    dwMacCr&=(~MAC_CR_PRMS_);
    dwMacCr|=MAC_CR_MCPAS_;
    dwMacCr&=(~MAC_CR_HPFILT_);
    Mac_SetRegDW(privateData,MAC_CR,dwMacCr);
    goto end;
  }

  if(dev->mc_count>0) {
    //enabling specific multicast addresses
    DWORD dwHashH=0;
    DWORD dwHashL=0;
    DWORD dwCount=0;
    struct dev_mc_list *mc_list=dev->mc_list;
    DWORD dwMacCr=0;
    dwMacCr=Mac_GetRegDW(privateData,MAC_CR);
    dwMacCr&=(~MAC_CR_PRMS_);
    dwMacCr&=(~MAC_CR_MCPAS_);
    dwMacCr|=MAC_CR_HPFILT_;
    while(mc_list!=NULL) {
      dwCount++;
      if((mc_list->dmi_addrlen)==6) {
        DWORD dwMask=0x01UL;
        DWORD dwBitNum=Hash(mc_list->dmi_addr);
        dwMask<<=(dwBitNum&0x1FUL);
        if(dwBitNum&0x20UL) {
          dwHashH|=dwMask;
        } else {
          dwHashL|=dwMask;
        }
      } else {
        SMSC_WARNING("dmi_addrlen!=6");
      }
      mc_list=mc_list->next;
    }
    if(dwCount!=((DWORD)dev->mc_count)) {
      SMSC_WARNING("dwCount!=dev->mc_count");
    }
    Mac_SetRegDW(privateData,HASHH,dwHashH);
    Mac_SetRegDW(privateData,HASHL,dwHashL);
    Mac_SetRegDW(privateData,MAC_CR,dwMacCr);
  } 
  else 
  {
    //enabling local MAC address only
    DWORD dwMacCr=0;
    dwMacCr=Mac_GetRegDW(privateData,MAC_CR);
    dwMacCr&=(~MAC_CR_PRMS_);
    dwMacCr&=(~MAC_CR_MCPAS_);
    dwMacCr&=(~MAC_CR_HPFILT_);
    Mac_SetRegDW(privateData,HASHH,0);
    Mac_SetRegDW(privateData,HASHL,0);
    Mac_SetRegDW(privateData,MAC_CR,dwMacCr);
  }
end:
  spin_unlock(&(privateData->MacPhyAccessLock));
}
#endif


/*==============================================================================
  <NAME>
  Lan_Initialize

  <SYNOPSIS>
  BOOLEAN Lan_Initialize( PPRIVATE_DATA privateData,DWORD dwIntCfg)

  <OPERANDS>
  Parameter:
  Return value:

  <DESCRIPITON>
  <NOTES>
  
  <BUGS>
  
  ============================================================================*/
static
BOOLEAN Lan_Initialize( PPRIVATE_DATA privateData,DWORD dwIntCfg)
{
  BOOLEAN result=FALSE;
  DWORD dwTimeOut=0;
  DWORD dwTemp=0;

    SMSC_TRACE("-->Lan_Initialize(dwIntCfg=0x%08lX)",dwIntCfg);
  SMSC_ASSERT(privateData!=NULL);

  spin_lock_init(&(privateData->MacPhyAccessLock));

  //Reset the LAN911x
  Lan_SetRegDW(HW_CFG,HW_CFG_SRST_);
  dwTimeOut=10;
  do {
    udelay(10);
    dwTemp=Lan_GetRegDW(HW_CFG);
    dwTimeOut--;
  } while((dwTimeOut>0)&&(dwTemp&HW_CFG_SRST_));
  if(dwTemp&HW_CFG_SRST_) {
    SMSC_WARNING("  Failed to complete reset.");
    goto DONE;
  }

  Lan_SetRegDW(HW_CFG,HW_CFG_INITIAL_VALUE);    // changed by Sony
  Lan_SetRegDW(AFC_CFG,AFC_CFG_INITIAL_VALUE);  // changed by Sony

  //make sure EEPROM has finished loading before setting GPIO_CFG
  dwTimeOut=50;
  while((dwTimeOut>0)&&(Lan_GetRegDW(E2P_CMD)&E2P_CMD_EPC_BUSY_)) {
    udelay(10);
    dwTimeOut--;
  }
  if(dwTimeOut==0) {
    SMSC_WARNING("Lan_Initialize: Timed out waiting for EEPROM busy bit to clear\n");
  }

  if(debug_mode&0x04UL) {
    Lan_SetRegDW(GPIO_CFG,0x00670700UL);
  } else {
    Lan_SetRegDW(GPIO_CFG,GPIO_CFG_INITIAL_VALUE); // changed by Sony
  }

  //initialize interrupts
  Lan_SetRegDW(INT_EN,0);
  Lan_SetRegDW(INT_STS,0xFFFFFFFFUL);
  dwIntCfg|=INT_CFG_IRQ_EN_;
  Lan_SetRegDW(INT_CFG,dwIntCfg);

  result=TRUE;

DONE:
  SMSC_TRACE("<--Lan_Initialize");
  return result;
}


/*==============================================================================
  <NAME>
  peripheral_init

  <SYNOPSIS>
  void peripheral_init(void)

  <OPERANDS>
  Parameter:
  
  Return value:
  
  <DESCRIPITON>
  EMC and GPIO necessary to use LAN9118 are set. 
   
  <NOTES>
  This function was added by Sony. 
  
  <BUGS>
  
  ============================================================================*/
static
void peripheral_init(void)
{
	unsigned long flags;
#if defined(CONFIG_ARCH_CXD4115BASED)
	u32 dat;
#endif

	local_irq_save(flags);

#if defined(CONFIG_ARCH_MB8AC102)
	/* INT pin setup */
	gpio_clr(GPIOSYS_NR, GPIO_INTLS, SMC911X_INT);   /* edge */
	gpio_clr(GPIOSYS_NR, GPIO_INTHE, SMC911X_INT);   /* not rise edge */
	gpio_set(GPIOSYS_NR, GPIO_INTLE, SMC911X_INT);   /* fall edge */
	gpio_clr(GPIOSYS_NR, GPIO_DIR,   SMC911X_INT);   /* dir=input */
	gpio_set(GPIOSYS_NR, GPIO_PUPD,  SMC911X_INT);   /* Pull up */
	gpio_set(GPIOSYS_NR, GPIO_RESEN, SMC911X_INT);   /* Pu/Pd enable */
	gpio_set(GPIOSYS_NR, GPIO_INPUTEN, SMC911X_INT); /* input enable */
	writel(1<<SMC911X_INT, VA_GPIOSYS+GPIO_INTCL);   /* clear intr */
	gpio_set(GPIOSYS_NR, GPIO_INTEN, SMC911X_INT);   /* intr enable */
#elif defined(CONFIG_ARCH_CXD4108)
	/* INT pin setup */
	writel(1<<SMC911X_INT, VA_OCTRL(1)+OCTRL_PUPDSET);  /* pullup */
	writel(1<<SMC911X_INT, VA_OCTRL(1)+OCTRL_RESENCLR); /* Pu/Pd enable */
	gpio_clr(GPIOSYS_NR, GPIO_INTLS, SMC911X_INT);   /* edge */
	gpio_clr(GPIOSYS_NR, GPIO_INTHE, SMC911X_INT);   /* not rise edge */
	gpio_set(GPIOSYS_NR, GPIO_INTLE, SMC911X_INT);   /* fall edge */
	gpio_clr(GPIOSYS_NR, GPIO_DIR,   SMC911X_INT);   /* dir=input */
	gpio_set(GPIOSYS_NR, GPIO_INPUTEN, SMC911X_INT); /* input enable */
	writel(1<<SMC911X_INT, VA_GPIOSYS+GPIO_INTCL);   /* clear intr */
	gpio_set(GPIOSYS_NR, GPIO_INTEN, SMC911X_INT);   /* intr enable */
#elif defined(CONFIG_ARCH_CXD4115BASED)
	/* INT pin setup */
#if defined(CONFIG_ARCH_CXD4120)
	writel(1<<SMC911X_INT, VA_OCTRL_SET(4)); /* P1=1 */
	writel(1<<SMC911X_INT, VA_OCTRL_CLR(5)); /* P2=0 */
#else
	    /* Pullup
             *   (note) It is OK to set OCTRL regs for both 
             *          CXD4115 and CXD9938. No side effects.
             *          
             */
		/* for CXD4115 */
	writel(1<<SMC911X_INT, VA_OCTRL_SET(23)); /* pullup */
	writel(1<<SMC911X_INT, VA_OCTRL_CLR(22)); /* Pu/Pd enable */
		/* for CXD9938 */
	writel(1<<SMC911X_INT, VA_OCTRL_SET(535)); /* P1=1 */
	writel(1<<SMC911X_INT, VA_OCTRL_CLR(534)); /* P2=0 */
#endif /* CONFIG_ARCH_CXD4120 */

	dat = readl(VA_GPIO(0) + GPIO_INEN);
	dat |= 1 << SMC911X_INT;
	writel(dat, VA_GPIO(0) + GPIO_INEN); /* input enable */
#endif
	local_irq_restore(flags);
}



/*==============================================================================
  <NAME>
  Rx_Packet_by_PIO

  <SYNOPSIS>
  void Rx_Packet_by_PIO(PPRIVATE_DATA privateData)

  <OPERANDS>
  Parameter:
  
  Return value:
  
  <DESCRIPITON>
  Receive data is forwarded from FIFO of LAN9118 to the main memory by PIO
  (program access). 
   
  <NOTES>
  This function was added by Sony. 
  
  <BUGS>
  
  ============================================================================*/
static
void Rx_Packet_by_PIO(PPRIVATE_DATA privateData)
{
  DWORD dwRxStatus=0;

  Lan_SetRegDW(RX_CFG,0x00000200UL);
  while((dwRxStatus=Rx_PopRxStatus(privateData))!=0)
  {
    DWORD dwPacketLength=((dwRxStatus&0x3FFF0000UL)>>16);
    Rx_CountErrors(privateData,dwRxStatus);
    if((dwRxStatus&RX_STS_ES_)==0) {
      struct sk_buff *skb=NULL;
      skb=dev_alloc_skb(dwPacketLength+2);
      if(skb!=NULL) {
        skb->data=skb->head;
        skb->tail=skb->head;
        skb_reserve(skb,2); // align IP on 16B boundary
        skb_put(skb,dwPacketLength-4UL);
          //update counters
        privateData->stats.rx_packets++;
        privateData->stats.rx_bytes+=(dwPacketLength-4);  
          Rx_ReadFifo(
          privateData->dwLanBase,
          ((DWORD *)(skb->head)),
          (dwPacketLength+2+3)>>2);

        Rx_HandOffSkb(privateData,skb);
        continue;
      } else {
        SMSC_WARNING("Unable to allocate sk_buff for RX Packet, in PIO path");
        privateData->stats.rx_dropped++;
      }
    }
    //if we get here then the packet is to be read
    //  out of the fifo and discarded
    dwPacketLength+=(2+3);
    dwPacketLength>>=2;
    Rx_FastForward(privateData,dwPacketLength);
  }
}


#ifndef DMA_DRIVER_OFF /* Rev01 add */
/*==============================================================================
  <NAME>
  Rx_Packet_by_DMA

  <SYNOPSIS>
  void Rx_Packet_by_DMA(PPRIVATE_DATA privateData)

  <OPERANDS>
  Parameter:
  
  Return value:
  
  <DESCRIPITON>
  Receive data is forwarded from FIFO of LAN9118 to the main memory by DMA. 
   
  <NOTES>
  This function was added by Sony. 
  It is necessary to do DMA before this function is used and to do reserve. 
  <BUGS>
  mak
  ============================================================================*/
static
void Rx_Packet_by_DMA(PPRIVATE_DATA privateData)
{
  DWORD dwRxStatus=0;
  
  struct  linkedlist sglist;  // DMA lls
  int     time_count = 0;     // DMA Time out count
  DWORD int_cfg;

  while((dwRxStatus=Rx_PopRxStatus(privateData))!=0)
  {
    DWORD dwPacketLength=((dwRxStatus&0x3FFF0000UL)>>16);
    DWORD dwDwordCount;
    Rx_CountErrors(privateData,dwRxStatus);
    if((dwRxStatus&RX_STS_ES_)==0) {

      struct sk_buff *skb=dev_alloc_skb(dwPacketLength+2*PLATFORM_CACHE_LINE_BYTES);
      if(skb!=NULL) {
        skb->data=skb->head;
        skb->tail=skb->head;
        skb_reserve(skb,PLATFORM_CACHE_LINE_BYTES-14);//align IP on cache line boundary
        skb_put(skb,dwPacketLength-4UL);

        //update counters
        privateData->stats.rx_packets++;
        privateData->stats.rx_bytes+=(dwPacketLength-4);  

        // Transmit Size
        dwDwordCount=((dwPacketLength+(PLATFORM_CACHE_LINE_BYTES-14)+
            PLATFORM_CACHE_LINE_BYTES-1)& (~(PLATFORM_CACHE_LINE_BYTES-1)))>>2;

        switch(PLATFORM_CACHE_LINE_BYTES) {
          case 16:Lan_SetRegDW(RX_CFG,0x40000200UL);
            break;
          case 32:Lan_SetRegDW(RX_CFG,0x80001200UL);
            break;
          default:SMSC_ASSERT(FALSE);
        }
        
        // Source address(physical)
        sglist.src_address = (void *)(lan_base[privateData->dwInstanceIndex] + FIFO_SEL_ADR + RX_DATA_FIFO);
        // Destination address(physical)
        sglist.dest_address = (void *)virt_to_bus((void *)skb->head);
        sglist.next_linkedlist = 0;
        sglist.control = 0x8C489000 | dwDwordCount;
        
        
        cxd4105_set_dma_sg(rx_dma_ch,&sglist); // set linked list item

        cxd4105_set_dma_mode(rx_dma_ch, 0x01000000);// set Memburst

        cxd4105_enable_dma(rx_dma_ch); // start dma

        while(cxd4105_dma_channel_active(rx_dma_ch)){
          udelay(1);
          time_count++;
          if(time_count > 1000000) break;
        }
        

        // Cache Invalitate
        consistent_sync((void *)skb->head,dwDwordCount * 4,DMA_FROM_DEVICE);
          
        Rx_HandOffSkb(privateData,skb);
        continue;
      } else {
        SMSC_WARNING("Unable to allocate sk_buff for RX Packet, in PIO path");
        privateData->stats.rx_dropped++;
      }
    }
    //if we get here then the packet is to be read
    //  out of the fifo and discarded
    dwPacketLength+=(2+3);
    dwPacketLength>>=2;
    Lan_SetRegDW(RX_CFG,0x00000200UL);//4 byte end alignment
    Rx_FastForward(privateData,dwPacketLength);
  }

  cxd4105_free_dma(rx_dma_ch); // free dma
}

#endif // DMA_DRIVER_OFF /* Rev01 add */


/*==============================================================================
  <NAME>
  Tx_Packet_by_PIO

  <SYNOPSIS>
  void Tx_Packet_by_PIO(PPRIVATE_DATA privateData,struct sk_buff *skb)

  <OPERANDS>
  Parameter:
  
  Return value:
  
  <DESCRIPITON>
  The transmission data is transmitted from the main memory to FIFO of LAN9118 
  by PIO. (program access)
   
  <NOTES>
  This function was added by Sony. 
  
  <BUGS>
  
  ============================================================================*/
static
void Tx_Packet_by_PIO(PPRIVATE_DATA privateData,struct sk_buff *skb)
{
  DWORD dwFreeSpace=0;
  DWORD dwTxCmdA=0;
  DWORD dwTxCmdB=0;
  
  dwFreeSpace=Lan_GetRegDW(TX_FIFO_INF);
  dwFreeSpace&=TX_FIFO_INF_TDFREE_;
  if(dwFreeSpace<TX_FIFO_LOW_THRESHOLD) {
    SMSC_WARNING("Tx Data Fifo Low, space available = %ld",dwFreeSpace);
  }
  dwTxCmdA=
    ((((DWORD)(skb->data))&0x03UL)<<16) | //DWORD alignment adjustment
    TX_CMD_A_FIRST_SEG_ | TX_CMD_A_LAST_SEG_ | 
    ((DWORD)(skb->len));
  dwTxCmdB=
    (((DWORD)(skb->len))<<16) |
    ((DWORD)(skb->len));
  Lan_SetRegDW(TX_DATA_FIFO,dwTxCmdA);
  Lan_SetRegDW(TX_DATA_FIFO,dwTxCmdB);
  Tx_WriteFifo(
    privateData->dwLanBase,
    (DWORD *)(((DWORD)(skb->data))&0xFFFFFFFCUL),
    (((DWORD)(skb->len))+3+
    (((DWORD)(skb->data))&0x03UL))>>2);
  dwFreeSpace-=(skb->len+32);
  dev_kfree_skb(skb);
  
  if(Tx_GetTxStatusCount(privateData)>=30)
  {
    Tx_UpdateTxCounters(privateData);
  }
  if(dwFreeSpace<TX_FIFO_LOW_THRESHOLD) {
    netif_stop_queue(privateData->dev);
    {
      DWORD temp=Lan_GetRegDW(FIFO_INT);
      temp&=0x00FFFFFFUL;
      temp|=0x32000000UL;
      Lan_SetRegDW(FIFO_INT,temp);
    }
  }
}

#ifndef DMA_DRIVER_OFF /* Rev01 add */
/*==============================================================================
  <NAME>
  Tx_Packet_by_DMA

  <SYNOPSIS>
  void Tx_Packet_by_DMA(PPRIVATE_DATA privateData,struct sk_buff *skb)

  <OPERANDS>
  Parameter:
  
  Return value:
  
  <DESCRIPITON>
  The transmission data is transmitted from the main memory to FIFO of LAN9118 
  by DMA.
   
  <NOTES>
  This function was added by Sony. 
  
  <BUGS>
  
  ============================================================================*/
static
void Tx_Packet_by_DMA(PPRIVATE_DATA privateData,struct sk_buff *skb)
{
  DWORD dwFreeSpace=0;
  DWORD dwTxCmdA=0;
  DWORD dwTxCmdB=0;
  struct  linkedlist sglist;  // DMA lls
  int     time_count = 0;     // DMA Time out count
  DWORD *pdwBuf;
  DWORD dwDwCnt;
  DWORD int_cfg;
  
  dwFreeSpace=Lan_GetRegDW(TX_FIFO_INF);
  dwFreeSpace&=TX_FIFO_INF_TDFREE_;
  if(dwFreeSpace<TX_FIFO_LOW_THRESHOLD) {
    SMSC_WARNING("Tx Data Fifo Low, space available = %ld",dwFreeSpace);
  }
  
  //prepare for 16 byte alignment
  dwTxCmdA=
#if (PLATFORM_CACHE_LINE_BYTES == 16)
      (0x01UL<<24)|//16 byte end alignment
#endif
#if (PLATFORM_CACHE_LINE_BYTES == 32)
      (0x02UL<<24)|//32 byte end alignment
#endif
      ((((DWORD)(skb->data))&(PLATFORM_CACHE_LINE_BYTES-1))<<16) |//16 Byte start alignment
      TX_CMD_A_FIRST_SEG_ |
      TX_CMD_A_LAST_SEG_ |
      ((DWORD)(skb->len));//buffer length
  dwTxCmdB=
    (((DWORD)(skb->len))<<16)|
    ((DWORD)(skb->len)&0x7FFUL);
  Lan_SetRegDW(TX_DATA_FIFO,dwTxCmdA);
  Lan_SetRegDW(TX_DATA_FIFO,dwTxCmdB);
  
  pdwBuf= (DWORD *)(((DWORD)(skb->data))& (~(PLATFORM_CACHE_LINE_BYTES-1)));
  dwDwCnt=  (
              ( ((DWORD)(skb->len))+
                (PLATFORM_CACHE_LINE_BYTES-1)+
                ( ((DWORD)(skb->data))&(PLATFORM_CACHE_LINE_BYTES-1))
              ) & (~(PLATFORM_CACHE_LINE_BYTES-1))
            )>>2;
  
  // Cache Write Back
  consistent_sync((void *)pdwBuf,dwDwCnt * 4,DMA_TO_DEVICE);
  // Source address(physical)
  sglist.src_address = (void *)virt_to_bus((void *)pdwBuf);
  // Destination address(physical)
  sglist.dest_address = (void *)(lan_base[privateData->dwInstanceIndex] + FIFO_SEL_ADR + TX_DATA_FIFO);
  sglist.next_linkedlist = 0;
  sglist.control = 0x8C489000 | dwDwCnt;


  cxd4105_set_dma_sg(tx_dma_ch,&sglist); // set linked list item
  cxd4105_set_dma_mode(tx_dma_ch, 0x01000000);// set Memburst
  cxd4105_enable_dma(tx_dma_ch); // start dma

  while(cxd4105_dma_channel_active(tx_dma_ch)){
    udelay(1);
    time_count++;
    if(time_count > 1000000) break;
  }


  dwFreeSpace-=(skb->len+32);
  dev_kfree_skb(skb);

  cxd4105_free_dma(tx_dma_ch); // free dma

  if(Tx_GetTxStatusCount(privateData)>=30)
  {
    Tx_UpdateTxCounters(privateData);
  }
  if(dwFreeSpace<TX_FIFO_LOW_THRESHOLD) {
    netif_stop_queue(privateData->dev);
    {
      DWORD temp=Lan_GetRegDW(FIFO_INT);
      temp&=0x00FFFFFFUL;
      temp|=0x32000000UL;
      Lan_SetRegDW(FIFO_INT,temp);
    }
  }

}
#endif // DMA_DRIVER_OFF /* Rev01 add */

#if defined(CONFIG_PM)
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14)
static int Simp911x_suspend(struct device *dev, u32 state, u32 level)
#else
static int Simp911x_suspend(struct platform_device *dev, pm_message_t state)
#endif
{
  //struct net_device *ndev = dev_get_drvdata(dev);
  struct net_device *ndev = &SIMP911X[0]; /* first instance only! */

  //printk("Simp911x_suspend %d %x\n", level, ndev);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14)
  if (ndev && level == SUSPEND_DISABLE) {
#else
  if (ndev) {
#endif
    if (netif_running(ndev)) {
      netif_device_detach(ndev);
      Simp911x_stop(ndev);
    }
  }
  return 0;
}

static void Simp911x_wait_for_ready(void)
{
	SMSC_TRACE("wait for ready");
	msleep(100);
}

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14)
static int Simp911x_resume(struct device *dev, u32 level)
#else
static int Simp911x_resume(struct platform_device *dev)
#endif
{
  //struct net_device *ndev = dev_get_drvdata(dev);
  struct net_device *ndev = &SIMP911X[0]; /* first instance only! */

  //printk("Simp911x_resume %d %x\n",level, ndev);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14)
  if (ndev && level == RESUME_ENABLE) {
#else
  if (ndev) {
#endif
    if (netif_running(ndev)) {
      if (peri_init) {
        peripheral_init();
      }
      Simp911x_wait_for_ready();
      Simp911x_open(ndev);
      netif_device_attach(ndev);
    } 
  }
  return 0;
}
#endif /* CONFIG_PM */

module_init(Simp911x_init_module);
module_exit(Simp911x_cleanup_module);
