/*****************************************************************************
* Copyright (c) 2006 Digi International Inc., All Rights Reserved
* 
* This software contains proprietary and confidential information of Digi
* International Inc.  By accepting transfer of this copy, Recipient agrees
* to retain this software in confidence, to prevent disclosure to others,
* and to make no use of this software other than that for which it was
* delivered.  This is an unpublished copyrighted work of Digi International
* Inc.  Except as permitted by federal law, 17 USC 117, copying is strictly
* prohibited.
* 
* Restricted Rights Legend
*
* Use, duplication, or disclosure by the Government is subject to
* restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in
* Technical Data and Computer Software clause at DFARS 252.227-7031 or
* subparagraphs (c)(1) and (2) of the Commercial Computer Software -
* Restricted Rights at 48 CFR 52.227-19, as applicable.
*
* Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343
*
*****************************************************************************/
#include <stdio.h>
#include "Npttypes.h"
#include "nawait.h"
#include "regs.h"

#include "gpiomux_def.h"
#ifdef BSP_ARM9
#include "gpio.h"
#include "powersave.h"
#endif
#include "eth_netos.h"

#include "bsp_sys.h"
#include "bsp_net.h"
#include "MII.h"

/*
 * These #defines are only for the NS9360 development board.
 */
    #define PHY_RESET_PIN   58
    #define PHY_RESET       0
    #define PHY_ENABLE      1
/* Must be 1st function called */
static void mii_init(void)
{
    static BOOLEAN initialized = FALSE;

    if (initialized == FALSE)
    {
        eth_level1_reset();
        eth_init_clk();

        initialized = TRUE;
    }
}


void customizeMiiInit(void)
{
    mii_init();
}

#if MII_PHY == 0
#include "no_phy.c"
#else

/*ad*
 * PHY Identifiers 
 *
 * @external
 * @category BSP:Device_Drivers:MII
 * @since 6.0
 * @name phy_ids "PHY Identifiers"
 *
 * @param ENABLE_PHY_ID     0x0043
 * @param INTEL_PHY_ID      0x0013
 * @param ICS_PHY_ID        0x0015
 * @param TDK_PHY_ID        0x000e
 *
*ad*/
#define     ENABLE_PHY_ID	    0x0043	    /* Enable Semi LU3X31FT chip. */
#define     INTEL_PHY_ID	    0x0013	    /* Intel LXT971,LXT972, LXT973 chips. */
#define     ICS_PHY_ID          0x0015      /* ICS 1893Bx Phy chip */
#define     TDK_PHY_ID          0x000e      /* TDK 78Q2123 microPHY */

/* Unsupported PHY identifiers.  
 * ALTIMA PHY has the same identifyer as AMD PHY. ALTIMA PHY is at address 0x400.
 * MICREL PHY has the same identifier as AMD PHY. MICREL PHY identifier 2 is 0x1400.
 */
#define     AMD_PHY_ID	        0x0022	    /* Amd Am79C874, Am79C875 chips. */
#define     LEVEL1_PHY_ID	    0x7810	    /* TODO: Intel LXT970.  Is this the right part? */

#define     ALTIMA_PHY_ADDR     0x400       /* ID is same as AMD_PHY_ID */

/* MII command and bit definitions.  IEEE 802.3 defined registers: 0-7 */
/* See IEEE Std 802.3u-1995 specification supplement, chapters 22 and 28. */
#define MII_CONTROL	                0
#define	    MII_CONTROL_RESET	        0x8000	/* Reset PHY. */
#define	    MII_CONTROL_LOOPBACK        0x4000	/* Loopback all frames. */
#define	    MII_CONTROL_SPEED	        0x2000	/* Low bit of speed selection */
#define		MII_CONTROL_SPEED_RSHIFT 13
#define	    MII_CONTROL_AUTONEG		    0x1000	/* Enable AutoNegotiation. */
#define     MII_CONTROL_LOW_POWER       0x0800  /* Low power mode when set */
#define	    MII_CONTROL_RESTART_AUTONEG	0x0200	/* Start AutoNegotiation. */
#define	    MII_CONTROL_DUPLEX		    0x0100	/* 1 = full duplex, 0 = half. */
#define	    MII_CONTROL_HIGHSPEED	    0x0040	/* High bit of speed selection */
#define MII_STATUS	                1
#define	    MII_STATUS_AUTONEG		    0x0020	/* AutoNegotiation complete. */
#define	    MII_STATUS_REMOTEFAULT	    0x0010	/* Remote fault. (Latching High) */
#define	    MII_STATUS_AUTONEG_ABLE	    0x0008	/* AutoNegotiation capable. */
#define	    MII_STATUS_LINK		        0x0004	/* Link is up. (Latching Low) */
#define	    MII_STATUS_JABBER		    0x0002	/* Jabber detected. (Latching High) */
#define	    MII_STATUS_EXTENDED		    0x0001	/* Extended Capability Register Set. */
#define MII_PHY_IDENT1              2
#define MII_PHY_IDENT2	            3
#define     MII_IDENT2_INTEL_LXT971     0x78E0  /* LXT971A or 972A PHY, last nibble is rev*/
#define     MII_IDENT2_INTEL_LXT973     0x7A10  /* Id2 of LXT973 PHY, last nibble is revision */
#define     MII_IDENT2_MICREL           0x1400
#define MII_ANADVERT	            4
#define	    MII_ANADVERT_100BASETFD	    0x0100	/* 100bt full duplex capable */
#define	    MII_ANADVERT_100BASET	    0x0080	/* 100bt capable */
#define	    MII_ANADVERT_10BASETFD	    0x0040	/* 10bt full duplex capable */
#define	    MII_ANADVERT_10BASET	    0x0020	/* 10bt capable */
#define     MII_ANADVERT_ALL_CAPS       0x01e0  /* 100bt_fd + 100bt + 10bt_fd + 10bt */
#define	    MII_ANADVERT_IEEE_802_3	    0x0001	/* IEEE 802.3 auto negotiation */
#define MII_LINK_PARTNER_ABILITY    5
#define MII_ANEXPANSION	            6
#define	    MII_ANEXPANSION_LINK_PARTNER_ANABLE	0x0001	/* Partner can autoneg. */

/* Vendor specific registers: 16-31 */

#define LEVEL1_MII_INTERRUPT        17
#define	    LEVEL1_MII_INTERRUPT_MINT_ENABLE    0x02
#define LEVEL1_MII_CHIP_STATUS      20
#define	    LEVEL1_MII_CHIP_STATUS_AUTONEG		0x0200
#define	    LEVEL1_MII_CHIP_STATUS_SPEED		0x0800
#define	    LEVEL1_MII_CHIP_STATUS_DUPLEX		0x1000
#define	    LEVEL1_MII_CHIP_STATUS_LINK			0x2000

#define ENABLE_MII_UNKNOWN_REG_23   0x17
#define	    ENABLE_MII_UNK_23_SPEED		        0x0200
#define	    ENABLE_MII_UNK_23_DUPLEX		    0x0100
#define ENABLE_MII_ISR              0x1E

#define AMD_MII_DIAGNOSTIC	        18
#define	    AMD_MII_DIAGNOSTIC_SPEED		    0x0400  /* Bit 10 gives data rate */
#define	    AMD_MII_DIAGNOSTIC_DUPLEX		    0x0800  /* Bit 11 gives duplex */
#define AMD_MII_INTERRUPT           0x11                /* AMD ISR is in reg 17 (0x11)*/
#define     AMD_ENABLE_ALL_INTERRUPTS           0xff00
#define     AMD_DISABLE_ALL_INTERRUPTS          0

#define INTEL_MII_STATUS2	            17
#define	    INTEL_MII_STATUS2_SPEED		        0x4000	/* 1=100MBit, 0=10MBit. */
#define	    INTEL_MII_STATUS2_LINK		        0x0400	/* 1=link, 0=no link. */
#define	    INTEL_MII_STATUS2_DUPLEX		    0x0200	    /* 1=full duplex, 0=half. */
#define	INTEL_MII_LEDCONFIG	            20
#define	    INTEL_MII_LEDCONFIG_LED1_TX_STRETCH	0x1000
#define	    INTEL_MII_LEDCONFIG_LED2_LINK	    0x0400
#define	    INTEL_MII_LEDCONFIG_LED3_RX		    0x0020
#define	    INTEL_MII_LEDCONFIG_STRETCH_ALL	    0x0002
#define INTEL_MII_SFR                   27              /* Intel LXT973 Special Function Register */
#define     INTEL_MII_SFR_MDIX_EN               0x0200  /* 1=enable, 0=disable */
#define     INTEL_MII_SFR_MDIX                  0x0100  /* 1=cross-cable, 0=straight */
#define     INTEL_ENABLE_ALL_INTERRUPTS         0x00f2
#define     INTEL_DISABLE_LINK_INTERRUPT        0x0010
#define INTEL_MII_ISR                       0x13    /* INTEL ISR is in reg 19 (0x13)*/
#define INTEL_MII_ICR                       0x12    /* INTEL ICR is in reg 18 (0x12)*/
#define     INTEL_ENABLE_LINK_INTERRUPT     0x0012
#define INTEL_MII_DCR                   26              /* INTEL Digital Control Reg on 26 (0x1a)*/
#define     INTEL_MII_DRIVE_STRENGTH            0x800   /* 1 = Increased MII drive strength */

#ifndef BSP_INCREASED_MII_DRIVE_STRENGTH
#define BSP_INCREASED_MII_DRIVE_STRENGTH FALSE
#endif

#define ICS_MII_EXCONTROL               16              /* ICS Extended Control reg */
#define ICS_MII_STATUS2			        17              /* ICS Quick Poll Status */
#define	    ICS_MII_STATUS2_SPEED		        0x8000	/* 1=100MBit, 0=10MBit. */
#define	    ICS_MII_STATUS2_DUPLEX		        0x4000	/* 1=full duplex, 0=half. */
#define	    ICS_MII_STATUS2_AUTONEG_COMPLETE	0x0010	/* 1=full duplex, 0=half. */
#define     ICS_MII_STATUS2_LINK                1       /* link status */
#define ICS_MII_10BT_OPS                18              /* 10Base-T Operations register */
#define ICS_MII_EXCONTROL2              19              /* ICS Extended Control reg 2 */
#define     ICS_MII_EXCONTROL2_MDIX             0x0100  /* 1=cross-cable, 0=straight */
#define     ICS_MII_EXCONTROL2_AUTO_MDIX        0x0200  /* 1=auto, 0=(MDIX setting) */
#define     ICS_ENABLE_ALL_INTERRUPTS           0x00f2

#define TDK_MII_VSR                     16              /* TDK Vendor Specific Register */
#define     TDK_MII_VSR_AP                      0x0020  /* 1=Auto Palarity enable */
#define     TDK_MII_VSR_100BT_PWR               0x0001  /* 1=power down RX clock when not in use */
#define TDK_MII_STATUS2			        17              /* TDK Intr control/Status register */
#define	    TDK_MII_STATUS2_AUTONEG_COMPLETE	0x0001	/* 1=complete, 0=not. */
#define     TDK_MII_STATUS2_LINK                0x0004  /* 1= fail link status */
#define     TDK_ENABLE_ALL_INTERRUPTS           0xff00
#define TDK_MII_DIAG                    18              /* TDK Diagnostic Register */
#define     TDK_MII_DIAG_DUPLEX                 0x0800  /* 1=full duplex, 0=half duplex */
#define     TDK_MII_DIAG_SPEED                  0x0400  /* autoneg result 1=100MBit, 0=10MBit */
#define TDK_MII_MDIX                    24              /* TDK MDI/MDIX control register */
#define     TDK_MII_MDIX_EN                     0x0040  /* 1=enable auto-switching, default is off */
#define     TDK_MII_MDIX_PD                     0x0080  /* 1=enable parallel detect mode */
#define     TDK_MII_MDIX_MDIX                   0x0020  /* 1=cross-over cable, 0=straight */

#define MICREL_100BASE_TX_PHY_CONTROL   0x1f
#define     MICREL_SPEED_100                    0x0008 /* In MICREL_100BASE_TX_PHY_CONTROL reg */
#define     MICREL_FULL_DUPLEX                  0x0010 /* In MICREL_100BASE_TX_PHY_CONTROL reg */

#define     MII_IDENT2_MASK                     0xfc00


#if (PROCESSOR == ns9750) || (PROCESSOR == ns9360) || (PROCESSOR == ns9210) || (PROCESSOR == ns9215)

#define NARM_EMIC       NARM_MIIMCR  
#define NARM_EMAR       NARM_MIIMAR  
#define NARM_EMRD       NARM_MIIMRDR 
#define NARM_EMWT       NARM_MIIMWDR
#define NARM_EMIR       NARM_MIIMIR  

#endif 

typedef struct 
{
    WORD16      error;
    WORD16      phy_type;
    WORD16      phy_addr;
    BOOLEAN     link_state;
 
} miiDataType;

static miiDataType mii_data = 
{
    FALSE, PHY_ERROR, PHY_ERROR, TRUE
};

#if (PROCESSOR == ns9750) || (PROCESSOR == ns9360) || (PROCESSOR == ns9210) || (PROCESSOR == ns9215)
static BOOLEAN mii_sleep_mode(unsigned speed);
#else
#define mii_sleep_mode(a) FALSE
#endif

/*  mii_poll_busy Wait for the current PHY operation to complete.
 *
 *  Return Value:
 *     1: completed
 *     0: timed-out
 */
static WORD16 mii_poll_busy (void)

{
    WORD32 timeout;
    
    timeout = 10000;
    while (timeout && ReadRegMask(NARM_EMIR, NARM_EMIR_BUSY))
       timeout--;

    if (timeout)
      return (1);
    else
      return (0);

}

/* mii_read sends a command, waits for it to complete and retrieves the data.
 */
WORD16 mii_read(WORD16 addr, WORD16 command)
{
    int status = 0;

    /* Set the addr and register number in the mii address register. */
    WriteRegMaskData(NARM_EMAR, NARM_EMAR_MADR, addr|command);

    /* Set the rstat bit to execute the read. */ 
    WriteRegMaskData(NARM_EMIC, NARM_EMIC_RSTAT, NARM_EMIC_RSTAT);

    /* Wait for the read to complete. */
    status = mii_poll_busy();

    if (status == 1)
	{
        /* NET+20UM does not clear this field  */
	    WriteRegMaskData(NARM_EMIC, NARM_EMIC_RSTAT, 0);
	}

    /* Return the result of the read. */
    return ReadRegMask(NARM_EMRD, NARM_EMRD_MRDD);
}

/* mii_write sends a command and data and waits for it to complete.
 */
static void mii_write(WORD16 addr, WORD16 command, WORD16 write_data)
{
    WriteRegMaskData(NARM_EMAR, NARM_EMAR_MADR, addr|command);

    WriteReg(NARM_EMWT, write_data);
    mii_poll_busy();
}

/* mii_wait_for_link waits for the link status to appear.
Returns:
    TRUE  - link occurred before timeout.
    FALSE - no link before timeout.
 */
static int mii_wait_for_link(WORD16 addr)
{
    WORD32 timeout = 100000;

    while (!(mii_read(addr, MII_STATUS) & MII_STATUS_LINK) && --timeout)
	    ;
    return timeout > 0;
}

static BOOLEAN mii_check_link(WORD16 addr, WORD16 phy_type)
{
    WORD16 status;
    (void) phy_type;

    status = mii_read(addr, MII_STATUS); /* read history first */
    status = mii_read(addr, MII_STATUS); /* latch current state */

    return (status & MII_STATUS_LINK);
}

/* used by mii_get_phy_status */
static WORD32 mii_negotiated_speed_duplex(WORD16 addr, WORD16 phy_type)
{
    WORD16 status;
    WORD32 phy_status = 0;
    int speed = 0, duplex = 0;

    /* Now it's okay to check the normal speed status. */
    switch(phy_type)
    {
    case AMD_PHY:
        status = mii_read(addr, AMD_MII_DIAGNOSTIC);
        speed  = status & AMD_MII_DIAGNOSTIC_SPEED;
        duplex = status & AMD_MII_DIAGNOSTIC_DUPLEX;
        break;

    case LEVEL1_PHY:
	    status = mii_read(addr, LEVEL1_MII_CHIP_STATUS);
        speed  = status & LEVEL1_MII_CHIP_STATUS_SPEED;
        duplex = status & LEVEL1_MII_CHIP_STATUS_DUPLEX;
        break;
	
    case ENABLE_PHY:
	    status = mii_read(addr, ENABLE_MII_UNKNOWN_REG_23);
        speed  = status & ENABLE_MII_UNK_23_SPEED;
        duplex = status & ENABLE_MII_UNK_23_DUPLEX;
        break;

    case INTEL_PHY:
	    status = mii_read(addr, INTEL_MII_STATUS2);
        speed  = status & INTEL_MII_STATUS2_SPEED;
        duplex = status & INTEL_MII_STATUS2_DUPLEX;
        break;

    case TDK_PHY:
	    status = mii_read(addr, TDK_MII_DIAG);
        speed  = status & TDK_MII_DIAG_SPEED;
        duplex = status & TDK_MII_DIAG_DUPLEX;
    	break;

    case ICS_PHY:
	    status = mii_read(addr, ICS_MII_STATUS2);
        speed  = status & ICS_MII_STATUS2_SPEED;
        duplex = status & ICS_MII_STATUS2_DUPLEX;
	break;

    case MICREL_PHY:
        status = mii_read(addr, MICREL_100BASE_TX_PHY_CONTROL);
        speed  = status & MICREL_SPEED_100;   
        duplex = status & MICREL_FULL_DUPLEX; 
    }

    phy_status |= speed  ?  MII_PHY_STATUS_100 : MII_PHY_STATUS_10;
    phy_status |= duplex ?  MII_PHY_STATUS_FD  : MII_PHY_STATUS_HD;

    return phy_status;
}

/* Get full status */
static WORD32 mii_get_phy_status(WORD16 addr, WORD16 phy_type)
{
    WORD32  ctrl_reg, stat_reg, phy_status = 0;

    ctrl_reg = mii_read(addr, MII_CONTROL);
    stat_reg = mii_read(addr, MII_STATUS); /* read history first */
    stat_reg = mii_read(addr, MII_STATUS); /* latch current state */

    if (ctrl_reg & MII_CONTROL_AUTONEG)
    {
        phy_status |= MII_PHY_STATUS_AUTONEG;
        
        /* advetised capabilities  */
        phy_status |= mii_read(addr, MII_ANADVERT) & MII_ANADVERT_ALL_CAPS; 
        
        if (stat_reg & MII_STATUS_LINK)
        {
            if (stat_reg & MII_STATUS_AUTONEG)
                phy_status |= MII_PHY_STATUS_AUTONEG_COMPLETE;

            if (mii_read(addr, MII_ANEXPANSION) & MII_ANEXPANSION_LINK_PARTNER_ANABLE)
                phy_status |= MII_PHY_STATUS_AUTONEG_ABLE;

            phy_status |= mii_negotiated_speed_duplex(addr, phy_type);
        }
    }
    else
    {
        phy_status |= MII_PHY_STATUS_FORCED;

        if (ctrl_reg & MII_CONTROL_SPEED)
            phy_status |= MII_PHY_STATUS_100;
        else
            phy_status |= MII_PHY_STATUS_10;

        if (ctrl_reg & MII_CONTROL_DUPLEX)
            phy_status |= MII_PHY_STATUS_FD;
        else
            phy_status |= MII_PHY_STATUS_HD;
    }
    if (stat_reg & MII_STATUS_LINK)
    {
        phy_status |= MII_PHY_STATUS_LINK;
    }
    if (ctrl_reg & MII_CONTROL_LOOPBACK)
        phy_status |= MII_PHY_STATUS_LOOPBACK;

    return phy_status;
}

static int mii_reset(WORD16 addr, WORD16 phy_type)
{
    WORD32 timeout = 10000;

    mii_write(addr, MII_CONTROL, MII_CONTROL_RESET);

    while ((mii_read(addr, MII_CONTROL) & MII_CONTROL_RESET) && --timeout)
	;

    switch (phy_type)
    {
    case LEVEL1_PHY:
        mii_write(addr, LEVEL1_MII_INTERRUPT, LEVEL1_MII_INTERRUPT_MINT_ENABLE);
        break;

    case INTEL_PHY:
        {
	    /* Find out which PHY is actually on here */
	    WORD16 ident2 = mii_read (addr, MII_PHY_IDENT2);
	    if ((ident2 & 0xFFF0) == MII_IDENT2_INTEL_LXT973)
	        mii_write (addr, INTEL_MII_SFR, INTEL_MII_SFR_MDIX_EN);

    #if BSP_INCREASED_MII_DRIVE_STRENGTH && (PROCESSOR == ns9750)
        /* Set Increased MII Drive Strength for the NS9750 development board
           to drive signals over longer PCB trace lengths, or over high-capacitive loads, 
           through multiple vias. Set bit 11 in the Digital Config Register (26) 
           in the Intel phy */
           mii_write(addr, INTEL_MII_DCR, INTEL_MII_DRIVE_STRENGTH);
    #endif
        }
        break;

    case TDK_PHY:
	    /* set Auto Polarity */
	    mii_write(addr, TDK_MII_VSR, TDK_MII_VSR_AP); 
	    /* set the Auto switching and parallel detect mode */
	    mii_write(addr, TDK_MII_MDIX, TDK_MII_MDIX_EN|TDK_MII_MDIX_PD);
        break;

    case ICS_PHY:
        {
        WORD16 value = mii_read(addr, ICS_MII_EXCONTROL2);

        /* Disable auto-mdix on ICS PHY, because it causes problems with some
           Ethernet hubs/switches.  Also make sure straight cable mode is
           used. */
        value &= ~ (ICS_MII_EXCONTROL2_AUTO_MDIX | ICS_MII_EXCONTROL2_MDIX);   
        mii_write(addr, ICS_MII_EXCONTROL2, value);
        }
        break;
    }
    return 0;
}

/* Negotiate & wait for link */
static int mii_negotiate(WORD16 addr, WORD16 phy_type, WORD16 anadvert)
{
    WORD32 timeout = 100000;
    (void) phy_type;

    anadvert &= MII_ANADVERT_ALL_CAPS;
    if (anadvert == 0)
        return PHY_ERROR;

    if ((mii_read(addr, MII_STATUS) & MII_STATUS_EXTENDED) == 0)
       return PHY_ERROR;

    anadvert |= MII_ANADVERT_IEEE_802_3;   

    mii_write(addr, MII_ANADVERT, anadvert);
    mii_write(addr, MII_CONTROL, MII_CONTROL_AUTONEG|MII_CONTROL_RESTART_AUTONEG);

    do 
    {
        timeout--;
        if ((mii_read(addr, MII_STATUS) & (MII_STATUS_AUTONEG|MII_STATUS_LINK)) 
	                  == (MII_STATUS_AUTONEG|MII_STATUS_LINK))
        {
 			  break;
        }
    } while(timeout);

    return timeout ? 0 : 1;
}

/* Negotiate & NOT wait for link */
static WORD16 mii_restart_autoneg(WORD16 addr, WORD16 phy_type, WORD16 anadvert)
{
    (void) phy_type;

    anadvert &= MII_ANADVERT_ALL_CAPS;
    if (anadvert == 0)
        return PHY_ERROR;

    if ((mii_read(addr, MII_STATUS) & MII_STATUS_EXTENDED) == 0)
       return PHY_ERROR;

    anadvert |= MII_ANADVERT_IEEE_802_3;   

    mii_write(addr, MII_ANADVERT, anadvert);
    mii_write(addr, MII_CONTROL, MII_CONTROL_AUTONEG|MII_CONTROL_RESTART_AUTONEG);

    return 0;
}

/* Force speed & duplex */
WORD16 mii_force_speed_duplex(WORD16 addr, WORD16 phy_type, unsigned speed, unsigned duplex)
{
    WORD16 write_data;

   
    if (mii_read(addr, MII_STATUS) & MII_STATUS_EXTENDED)
    {
        /*
         * For some reason, the Cisco switch requires that we advertise the
         * settings we will force even if the switch has been configured 
         * not to negotiate.  So, this code changes the autonegotiate 
         * advertising settings to what we are about to force.
         *
         * It is not clear if this is a Cisco bug, or should be done with
         * all switches (several other switches work fine without this). 
         * Extreme Summit48 switch although requires this.
         * This may also be a problem with just some Intel PHYs.
         */
        WORD16 anadvert; 

        if (speed == 0 && duplex == 0)
            anadvert = MII_ANADVERT_10BASET;
        else
        if (speed == 0 && duplex != 0)
            anadvert = MII_ANADVERT_10BASETFD;
        else
        if (duplex == 0)
            anadvert = MII_ANADVERT_10BASET | MII_ANADVERT_100BASET;
        else
            anadvert = MII_ANADVERT_10BASETFD | MII_ANADVERT_100BASETFD;

        if (anadvert != (mii_read(addr, MII_ANADVERT) & MII_ANADVERT_ALL_CAPS))
            mii_negotiate(addr, phy_type, anadvert);
    }

    write_data = duplex ? MII_CONTROL_DUPLEX : 0;
    write_data |= speed ? MII_CONTROL_SPEED : 0;

#if 0
    /*
     * A reset is required when forcing connection settings between the Intel
     * PHY and the Cisco switch.  It does not do any harm for other switches.
     */
	mii_write(addr, MII_CONTROL, MII_CONTROL_RESET | write_data);
	
	while (mii_read(addr, MII_CONTROL) & MII_CONTROL_RESET);
#endif 
	mii_write(addr, MII_CONTROL, write_data);

    /* Now wait for link status to return. */
    mii_wait_for_link(addr);

    return 0;
}

static WORD16 mii_set_loopback(WORD16 addr, WORD16 phy_type, BOOLEAN loopback, unsigned speed)
{
    WORD16 write_data;
    (void) phy_type;
    
    write_data  = loopback ? MII_CONTROL_LOOPBACK | MII_CONTROL_DUPLEX : 0;
    write_data |= speed ? MII_CONTROL_SPEED : 0;
	        
    mii_write(addr, MII_CONTROL, write_data);

    /* Now wait for link status to return. */
    mii_wait_for_link(addr);

    return 0;
}

/********************************************************************************/

/* Check if the other side can negotiate: 0 - negotiable, 1 - non-negotiable */
int mii_check_negotiable(void)
{
     return (mii_read(mii_data.phy_addr, MII_ANEXPANSION) & 
      MII_ANEXPANSION_LINK_PARTNER_ANABLE) ? 0 : 1;
}

/*ad*
 * Returns the status of the external PHY device. 
 *
 * The returned value is a combination of ORR-ed PHY status masks and 
 * autonegotiation adverticement masks, if PHY is not in forced mode. 
 *
 * @external
 * @category BSP:Device_Drivers:MII
 * @since 7.0
 *
 *
 * @return  "PHY Status"    Status.
 * @return  PHY_ERROR       Error identifying PHY.
 *
 * @see @link phy_status
 * @see @link phy_anadvert
*ad*/
WORD32 customizeGetPhyStatus(void)
{
    WORD16 phy_type, addr;

    if ((phy_type = customizeMiiIdentifyPhy(&addr)) == PHY_ERROR)
        return MII_PHY_STATUS_ERROR;

    return  mii_get_phy_status(addr, phy_type);
}

/*ad*
 * Checks the presence of the Ethernet link.
 *
 * @external
 * @category BSP:Device_Drivers:MII
 * @since 6.0
 *
 * @return  FALSE   No link
 * @return  TRUE    Link is ok
 *
*ad*/
BOOLEAN customizeMiiCheckLink(void)
{
    WORD16 phy_type, addr;

    if ((phy_type = customizeMiiIdentifyPhy(&addr)) == PHY_ERROR)
        return FALSE;
 

        return mii_check_link(addr, phy_type) ? 1 : 0;
}

/*ad*
 * Checks the operating speed of the Ethernet link. If the link is down, 
 * the result is undefined.
 *
 * @external
 * @category BSP:Device_Drivers:MII
 * @since 6.0
 *
 * @return  0           10Mbit  speed 
 * @return  1           100Mbit speed
 * @return  PHY_ERROR   Error udentifying PHY.
 *
*ad*/
WORD16 customizeMiiCheckSpeed(void)
{
    WORD16 phy_type, addr;

    if ((phy_type = customizeMiiIdentifyPhy(&addr)) == PHY_ERROR)
        return PHY_ERROR;    
    
    return mii_get_phy_status(addr, phy_type) & MII_PHY_STATUS_100 ? 1 : 0;
}

/*ad*
 * Checks the operating duplex of the Ethernet link. If the link is down, 
 * the result is undefined.
 *
 * @external
 * @category BSP:Device_Drivers:MII
 * @since 6.0
 *
 * @return  0           Half Duplex
 * @return  1           Full Duplex
 * @return  PHY_ERROR   Error udentifying PHY.
 *
*ad*/
WORD16 customizeMiiCheckDuplex(void)
{
    WORD16 phy_type, addr;

    if ((phy_type = customizeMiiIdentifyPhy(&addr)) == PHY_ERROR)
        return PHY_ERROR;   
    
    return mii_get_phy_status(addr, phy_type) & MII_PHY_STATUS_FD ? 1 : 0;
}

/*ad*
 * Identifies the PHY type and returns a PHY type code and the device address.
 *
 * @external
 * @category BSP:Device_Drivers:MII
 * @since 6.0
 *
 * @param addr      Points to a variable to be filled with PHY address on output.
 *
 * @return  PHY_ERROR       Error identifying PHY.
 * @return  "PHY type"      Phy type value.
 *
 * @see @link phy_types
*ad*/
WORD16 customizeMiiIdentifyPhy(WORD16 * addr)
{
    WORD16 phy_type = PHY_ERROR;
    WORD16 address, id, id2;

    mii_init();

    if (mii_data.error || (mii_data.phy_type != PHY_ERROR))
    {
	    phy_type = mii_data.phy_type;
        goto ret;
    }

   /* We don't know PHY address yet. Search for our PHY addr and 
      try to identify PHY. We assume there is only one! 
    */
    for (address = 0x000; address <= 0x2000; address += 0x100)
    {
        /* Look for addr i.  PHY_ID_1 identifies the PHY. */
	    id = mii_read(address, MII_PHY_IDENT1);
	    switch (id)
	    {
	        case INTEL_PHY_ID:
                phy_type = INTEL_PHY;
		        break;

	        case AMD_PHY_ID:
                id2 = mii_read(address, MII_PHY_IDENT2);

                if ((id2 & MII_IDENT2_MASK) == MII_IDENT2_MICREL)
                    phy_type = MICREL_PHY;
                else
                if (address == ALTIMA_PHY_ADDR)
                    phy_type = ALTIMA_PHY;
                else
		            phy_type = AMD_PHY;
		        break;

	        case ENABLE_PHY_ID:
                phy_type = ENABLE_PHY;
		        break;

		    case LEVEL1_PHY_ID:
                phy_type = LEVEL1_PHY;
		        break;

		    case ICS_PHY_ID:
		        phy_type = ICS_PHY;
		        break;

            case TDK_PHY_ID:
                phy_type = TDK_PHY;
                break;
	    }
        if (phy_type != PHY_ERROR)
            break;
    }
    if (phy_type != PHY_ERROR)
    {
        mii_data.phy_type = phy_type;
        mii_data.phy_addr = address;
    }
    else
    {
        mii_data.error = TRUE;
        customizeErrorHandler(ERROR_MII, ERROR_PHY_TYPE_NOT_SUPPORTED);
    }

ret:
    *addr = mii_data.phy_addr;
    return phy_type;   
}

/*ad*
 * Resets the external Ethernet PHY device.
 *
 * @external
 * @category BSP:Device_Drivers:MII
 * @since 6.0
 *
 * @return  0           Success.
 * @return  PHY_ERROR   Error identifying PHY.
 *
*ad*/
WORD16 customizeMiiReset(void)
{
    WORD16 phy_type, addr;

    mii_init();

#if PROCESSOR == ns9360
    NAsetGPIOpin(PHY_RESET_PIN, PHY_RESET);
    NAuWait(100);       /* actually only need 500ns, but the wait routine is not accurate so... */
    NAsetGPIOpin(PHY_RESET_PIN, PHY_ENABLE);
#endif
    if ((phy_type = customizeMiiIdentifyPhy(&addr)) == PHY_ERROR)
        return PHY_ERROR;

    return mii_reset(addr, phy_type);
}

/*ad*
 * Restarts the autonegotiation process of the external Ethernet PHY device 
 * and waits for the autonegotiation to complete. 
 *
 * @external
 * @category BSP:Device_Drivers:MII
 * @since 7.0
 *
 * @param anadvert  Autonegotiation advertisement mask - any combination of  
 * <list type="unordered">
 *  <item> MII_AUTONEG_100FD == 0x0100, 100BaseT, full duplex </item>
 *  <item> MII_AUTONEG_100HD == 0x0080, 100BaseT, half duplex  </item>
 *  <item> MII_AUTONEG_10FD  == 0x0040, 10BaseT,  full duplex  </item>
 *  <item> MII_AUTONEG_10HD  == 0x0020, 10BaseT,  half duplex  </item>
 *  <item> MII_ANADVERT_ALL_CAPS == 0x01e0,  All modes</item>
 * </list>
 *
 * @return  0           Autonegotiation completed or timed out.
 * @return  PHY_ERROR   Invalid argument or error identifying PHY.    
 *
 * @see @link phy_anadvert
 *
*ad*/
WORD16 customizeMiiAutonegotiate(WORD16 anadvert)
{
    WORD16 phy_type, addr;

    if ((phy_type = customizeMiiIdentifyPhy(&addr)) == PHY_ERROR)
        return PHY_ERROR;   
  
    return mii_negotiate(addr, phy_type, anadvert);
}

/*ad*
 * Restarts the autonegotiation process of the external Ethernet PHY device. 
 *
 * @external
 * @category BSP:Device_Drivers:MII
 * @since 6.0
 *
 * @param anadvert  Autonegotiation advertisement mask - any combination of  
 * <list type="unordered">
 *  <item> MII_AUTONEG_100FD == 0x0100, 100BaseT, full duplex </item>
 *  <item> MII_AUTONEG_100HD == 0x0080, 100BaseT, half duplex  </item>
 *  <item> MII_AUTONEG_10FD  == 0x0040, 10BaseT,  full duplex  </item>
 *  <item> MII_AUTONEG_10HD  == 0x0020, 10BaseT,  half duplex  </item>
 *  <item> MII_ANADVERT_ALL_CAPS == 0x01e0,  All modes</item>
 * </list>
 *
 * @return  0           Autonegotiation restarted.
 * @return  PHY_ERROR   Invalid argument or error identifying PHY.    
 *
 * @see @link phy_anadvert
 * @see @link customizeMiiAutonegotiationComplete
 *
*ad*/
WORD16 customizeMiiRestartAutoneg(WORD16 anadvert)
{
    WORD16 phy_type, addr;

    if ((phy_type = customizeMiiIdentifyPhy(&addr)) == PHY_ERROR)
        return PHY_ERROR;   
   
    return mii_restart_autoneg(addr, phy_type, anadvert);
}

/*ad*
 * Checks the completion of the autonegotiation process started in the 
 * customizeMiiRestartAutoneg function. 
 *
 * @external
 * @category BSP:Device_Drivers:MII
 * @since 6.0
 *
 * @return  TRUE    Autonegotiation completed successfully.
 * @return  FALSE   Did not complete autonegotiation, or link is down,
 *                  or error identifying PHY.
 *
*ad*/
BOOLEAN customizeMiiAutonegotiationComplete(void)
{
    WORD16 phy_type, addr;
    WORD32 phy_status;

    if ((phy_type = customizeMiiIdentifyPhy(&addr)) == PHY_ERROR)
        return FALSE;   

    phy_status = mii_get_phy_status(addr, phy_type);

    return phy_status & MII_PHY_STATUS_AUTONEG_COMPLETE;
}

/*ad*
 * Puts the external PHY into loopback mode. 
 *
 * @external
 * @category BSP:Device_Drivers:MII
 * @since 6.0
 *
 * @param loopback      FALSE - disable, TRUE - enable.
 * @param speed         0 - 10Mbit,  1 - 100Mbit
 *
 * @return  0           Loopback enabled or disabled.
 * @return  PHY_ERROR   Error identifying PHY.    
 *
*ad*/
WORD16  customizeMiiSetLoopback(BOOLEAN loopback, unsigned speed)
{
    WORD16 phy_type, addr;

    if ((phy_type = customizeMiiIdentifyPhy(&addr)) == PHY_ERROR)
        return PHY_ERROR;   

    return mii_set_loopback(addr, phy_type, loopback, speed);
}

WORD16 anadvert_settings[MII_MAX_SPEED_SETTINGS][MII_MAX_DUPLEX_SETTINGS] = 
{
    {MII_ANADVERT_10BASET,                              /* 10 HD        */
     MII_ANADVERT_10BASETFD | MII_ANADVERT_10BASET,     /* 10 FD        */
     MII_ANADVERT_10BASETFD | MII_ANADVERT_10BASET},    /* 10 AUTO      */

    {MII_ANADVERT_10BASET  | MII_ANADVERT_100BASET,     /* 100 HD       */
     MII_ANADVERT_10BASET  | MII_ANADVERT_10BASETFD |    
     MII_ANADVERT_100BASET | MII_ANADVERT_100BASETFD,   /* 100 FD       */
     MII_ANADVERT_10BASET  | MII_ANADVERT_10BASETFD |    
     MII_ANADVERT_100BASET | MII_ANADVERT_100BASETFD},  /* 100 AUTO     */

    {MII_ANADVERT_10BASET  | MII_ANADVERT_100BASET,     /* AUTO HD      */
     MII_ANADVERT_10BASET  | MII_ANADVERT_10BASETFD |    
     MII_ANADVERT_100BASET | MII_ANADVERT_100BASETFD,   /* AUTO FD      */
     MII_ANADVERT_10BASET  | MII_ANADVERT_10BASETFD |    
     MII_ANADVERT_100BASET | MII_ANADVERT_100BASETFD}   /* AUTO, AUTO   */
};

BOOLEAN customizeMiiNeedReconfigure(unsigned speed, unsigned duplex)
{
    WORD16  phy_type, addr;
    phy_type = customizeMiiIdentifyPhy(&addr);
    
    if (speed >= MII_MAX_SPEED_SETTINGS || duplex >= MII_MAX_DUPLEX_SETTINGS || 
        phy_type == PHY_ERROR)
    {
        return TRUE;
    }
    if ((mii_read(addr, MII_CONTROL) & MII_CONTROL_AUTONEG) == 0 || 
        (mii_read(addr, MII_ANADVERT) & MII_ANADVERT_ALL_CAPS) != 
        anadvert_settings[speed][duplex])
    {
        return TRUE;
    }
    return FALSE;
}

/*ad*
 * Tries to negotiate desired speed and duplex settings. 
 * If this fails, disables autonegotiation and forces speed and duplex.
 *
 * The following process is used:
 *
 * <list type="unordered">
 *  <item> 1. Restart autonegotiation, according to the following rools:
 *      <list type="unordered">
 *        <item> speed  == 0        Only 10 BaseT advertisements are used </item>
 *        <item> speed  == 1 or 2   Both 10 BaseT and 100BaseT advertisements are used </item>
 *        <item> duplex == 0        Only Half Duplex advertisements are used </item> 
 *        <item> duplex == 1 or 2   Both Half and Full Duplex advertisements are used </item> 
 *      </list>
 *  </item>
 * <item> 2. Wait until autonegotiation completes. </item>
 * <item> 3. Force actual established speed and duplex only, if
 *    Full Duplex is desired, link is up, and the link partner does not 
 *    support autonegotiation. </item>
 * </list>
 *
 * @note Speed and duplex are never forced for the requested Half Duplex,
 *       because all known devices default to Half Duplex in a forced mode.
 *       This Function might involve two autonegotiations, because forcing 
 *       Full Duplex requires re-negotiation with the Full Duplex advertisements
 *       only.
 *
 * @external
 * @category BSP:Device_Drivers:MII
 * @since 7.0
 *
 * @param speed     MII_CONFIG_AUTO, MII_CONFIG_100, or MII_CONFIG_10
 * @param duplex    MII_CONFIG_AUTO, MII_CONFIG_HD,  or MII_CONFIG_FD
 *
 * @return  0           Speed and duplex configured.
 * @return  PHY_ERROR   Invalid argument or error identifying PHY.    
 *
 * @see @link customizeMiiForceSpeedDuplex
*ad*/
WORD16 customizeMiiConfigure(unsigned speed, unsigned duplex)
{
    WORD16 phy_type, addr;

    if (speed >= MII_MAX_SPEED_SETTINGS || duplex >= MII_MAX_DUPLEX_SETTINGS || 
        (phy_type = customizeMiiIdentifyPhy(&addr)) == PHY_ERROR)
    {
        return PHY_ERROR;
    }
    /* don't negotiate if coming out of sleep mode */
    if (mii_sleep_mode(speed))
    {
       goto done_negotiate; 
    }

    if (mii_read(addr, MII_STATUS) & MII_STATUS_EXTENDED)
    {
        WORD16 anadvert = anadvert_settings[speed][duplex];

        if (mii_negotiate(addr, phy_type, anadvert) == PHY_ERROR) 
            return PHY_ERROR;

        if (duplex == MII_CONFIG_FD) 
        {
            WORD32  phy_status = mii_get_phy_status(addr, phy_type);

            if ((phy_status & MII_PHY_STATUS_AUTONEG_ABLE) == 0) 
            {
                if (phy_status & MII_PHY_STATUS_LINK)
                {
                    if (phy_status & MII_PHY_STATUS_100) speed = MII_CONFIG_100;
                    else
                    if (phy_status & MII_PHY_STATUS_10)  speed = MII_CONFIG_10;

                    if (speed != MII_CONFIG_AUTO)
                        mii_force_speed_duplex(addr, phy_type, speed, duplex);
                }
            }
        }
    }
    else
    {
        mii_force_speed_duplex(addr, phy_type, speed, duplex);
    }
done_negotiate:
    return 0;
}

/*ad*
 * Disables autonegotiations and forces the speed and duplex of the 
 * external PHY. 
 *
 * @note Forcing Full Duplex might require re-negotiation.
 *
 * @external
 * @category BSP:Device_Drivers:MII
 * @since 6.0
 *
 * @param speed   1 - 100Mbit,     0 - 10Mbit.
 * @param duplex  1 - full duplex, 0 - half duplex.
 *
 * @return  0           Speed and duplex forced.
 * @return  PHY_ERROR   Error identifying PHY.    
 *
*ad*/
WORD16 customizeMiiForceSpeedDuplex(unsigned speed, unsigned duplex)
{
    WORD16 phy_type, addr;
    
    if ((phy_type = customizeMiiIdentifyPhy(&addr)) == PHY_ERROR)
        return PHY_ERROR;

    return mii_force_speed_duplex(addr, phy_type, (int)speed, (int)duplex);
}

#if BSP_USE_PHY_INTERRUPT

#ifdef  ETH_ENABLE_LINK_MONITOR
static naEthSetLinkEventFnType linkEventFn = NULL;

void customizeMiiSetLinkEventFn(const naEthSetLinkEventFnType linkFn)
{
    linkEventFn = linkFn;
}
#endif

BOOLEAN customizeEnableMiiInterrupt(void)
{
    WORD16  phy_type, addr;
    BOOLEAN result = FALSE; 
 
    phy_type = customizeMiiIdentifyPhy(&addr);

    if (phy_type != PHY_ERROR)
    {
        result = TRUE;
       /*
        * Enable mii interrupts
        */
        switch (phy_type)
        {
        case ALTIMA_PHY:
        case AMD_PHY:
           /* first lets clear all pending PHY interrupts by reading ISR
            * read form ISR register to acknoledge interrupt 
            */
            mii_read(addr, AMD_MII_INTERRUPT); /* AMD & Altima's ISR is in reg 17 (0x11) */
	    
           /* Now enable Interrupts by setting all bits in the ICR */
            mii_write(addr, AMD_MII_INTERRUPT, AMD_ENABLE_ALL_INTERRUPTS);
            break;

        case INTEL_PHY:
            /* first lets clear all pending PHY interrupts by reading ISR
             * read form ISR register to acknoledge interrupt
             */
            mii_read(addr, INTEL_MII_ISR); /* INTEL ISR is in reg 19 (0x13) */
	    
            /* Now enable Interrupts by setting all bits in the ICR */
            mii_write(addr, INTEL_MII_ICR, INTEL_ENABLE_LINK_INTERRUPT); /* INTEL ICR is in reg 18 (0x12) */
            break;

        case ICS_PHY:
            customizeErrorHandler(ERROR_MII, ERROR_MII_INTERRUPT_NOT_SUPPORTED);
            result = FALSE; 
            break;
	default:
            /* Enable/Lucent/Level1 PHYs have interrupts enabled by default */
            break;
        }
    }
        
    return result;
}

BOOLEAN customizeDisableMiiInterrupt(void)
{
    WORD16  phy_type, addr;
    BOOLEAN result = FALSE; 
 
    phy_type = customizeMiiIdentifyPhy(&addr);

    if (phy_type != PHY_ERROR)
    {
        result = TRUE;

        switch (phy_type)
        {
        case ALTIMA_PHY:
        case AMD_PHY:
           /* Now enable Interrupts by setting all bits in the ICR */
            mii_write(addr, AMD_MII_INTERRUPT, AMD_DISABLE_ALL_INTERRUPTS);
            break;

        case INTEL_PHY:
            /* Now enable Interrupts by setting all bits in the ICR */
            mii_write(addr, INTEL_MII_ICR, INTEL_DISABLE_LINK_INTERRUPT); 
            break;
        }
    }
    return result;
}


void customizeLinkChangeIsr(void)
{
    if (mii_data.phy_type == PHY_ERROR && mii_data.phy_addr == PHY_ERROR)
        return;
    
    /* acknowldge interrupt on Phy 
     * read form ISR register to acknoledge interrupt
     */
    switch(mii_data.phy_type)
    {
    case ALTIMA_PHY:
    case AMD_PHY:
        mii_read(mii_data.phy_addr, AMD_MII_INTERRUPT); /* AMD & Altima's ISR is in reg 17 (0x11) */
        break;

    case INTEL_PHY:
        mii_read(mii_data.phy_addr, INTEL_MII_ISR);     /* INTEL ISR is in reg 19 (0x13) */
        break;

    case ENABLE_PHY:
    case LEVEL1_PHY:
        mii_read(mii_data.phy_addr, ENABLE_MII_ISR);    /* INTEL ISR is in reg 0x1E) */
        break;
    }
#ifdef  ETH_ENABLE_LINK_MONITOR
    if (linkEventFn)
        linkEventFn();
#endif
}

BOOLEAN customizeIsMiiInterruptActiveLow(void)
{
    BOOLEAN result = FALSE;

    switch (mii_data.phy_type)
    {
    case AMD_PHY:
        /* This PHYs has normally active-high interrupt, 
           The interrupt level can be programmed on register 16 pin 14, 
           this requires an external resistor.
         */
       result = FALSE;
       break;

    case ALTIMA_PHY:
        /* This PHYs has programmable interrupt level on register 16 pin 14.
           The default is active-low. 
         */
        result = TRUE;
        break;

    case ENABLE_PHY:
    case LEVEL1_PHY:
    case INTEL_PHY:
    default:
        /* This PHYs have active-low interrupt */
        result = TRUE;
        break;
    }
    return result;
}
#endif

void customizeMiiPhySleep(void)
{
    WORD16  phy_type, addr;
    WORD16  value; 
 
    phy_type = customizeMiiIdentifyPhy(&addr);

    if (phy_type != PHY_ERROR)
    {
        value = mii_read(addr, MII_CONTROL);
        value &= ~MII_CONTROL_RESET;
        mii_write(addr, MII_CONTROL, value | MII_CONTROL_LOW_POWER);    
    }
}

void customizeMiiPhyWakeup(void)
{
    WORD16  phy_type, addr;
    WORD16  value; 
 
    phy_type = customizeMiiIdentifyPhy(&addr);

    if (phy_type != PHY_ERROR)
    {
        value = mii_read(addr, MII_CONTROL);
        value &= ~(MII_CONTROL_RESET | MII_CONTROL_LOW_POWER);
        mii_write(addr, MII_CONTROL, value);    
    }
}

#define MII_LED_CONFIG      20

#define MII_LED_1_MASK      0xf000
#define MII_LED_2_NASK      0x0f00
#define MII_LED_3_MASK      0x00f0

int mii_get_LED (WORD16 *value)
{
    /* check the existing PHY type. */
	*value = mii_read(mii_data.phy_addr, MII_LED_CONFIG);
    return (0);
}

int mii_set_LED (WORD16 id, WORD16 option)
{
    WORD16  value;
    WORD16  mask;
    WORD16  ind;

    mii_get_LED (&value);

    ind = (id * 4);
	mask = 0x000f << ind;
    value = (value & ~mask) | (option << ind);

	mii_write(mii_data.phy_addr, MII_LED_CONFIG, value);
    return (0);
}

#ifdef NETOS_DEBUG
void print_phy_registers(void)
{
    int ind;
    unsigned value;
    WORD16 phyType, phyAddress;
    
    phyType = customizeMiiIdentifyPhy(&phyAddress);
    printf ("phyType = %d, phyAddress = 0x%4.4X\n", phyType, phyAddress);
    
    for (ind = 0; ind < 20; ind++)
    {
        value = mii_read(phyAddress, ind);
        printf (" reg(%d) = 0x%8.8X\n", ind, value);
    }
}
#endif

#if (PROCESSOR == ns9750) || (PROCESSOR == ns9360) || (PROCESSOR == ns9210) || (PROCESSOR == ns9215)
static BOOLEAN mii_sleep_mode(unsigned speed)
{
    BOOLEAN result = FALSE;
    (void) speed;

#if BSP_WAKEUP_DISABLE_AUTO_NEGOTIATION
    volatile naPowerSaveData_t *p_data = (naPowerSaveData_t *)NA_SLEEP_DATA_ADDRESS;

    result = NAPDIsSleepWakeup();
    if (result && speed == MII_CONFIG_FD && p_data->eth_negotiable != 0)
    {
       customizeMiiForceSpeedDuplex(customizeMiiCheckSpeed(), speed);
    }
#endif

    return result;
}
#endif

/* Deprecated Functions */

/**
 * Tries to negotiate desired speed and duplex settings. 
 * If this fails, disables autonegotiation and forces speed and duplex.
 *
 * @external
 * @category BSP:Device_Drivers:MII
 * @since 6.0
 * @deprecated  7.0
 *
 * @param anadvert  Autonegotiation advertisement mask - a combination of  
 * <list type="unordered">
 *  <item> MII_AUTONEG_100FD == 0x0100, 100BaseT, full duplex </item>
 *  <item> MII_AUTONEG_100HD == 0x0080, 100BaseT, half duplex  </item>
 *  <item> MII_AUTONEG_10FD  == 0x0040, 10BaseT,  full duplex  </item>
 *  <item> MII_AUTONEG_10HD  == 0x0020, 10BaseT,  half duplex  </item>
 *  <item> MII_ANADVERT_ALL_CAPS == 0x01e0,  All modes</item>
 * </list>
 *
 * @return  0           Speed and duplex configured.
 * @return  PHY_ERROR   Invalid argument or error identifying PHY.    
 *
 * @see @link customizeMiiConfigure
**/
WORD16 customizeMiiSetAuto(WORD16 anadvert)
{
    unsigned speed = 0, duplex = 0;

    anadvert &= MII_ANADVERT_ALL_CAPS;
    if (anadvert == 0)
        return PHY_ERROR;

    switch (anadvert)
    {
    case MII_ANADVERT_10BASET:
        break;

    case MII_ANADVERT_10BASETFD:
        duplex = 1;
        break;

    case MII_ANADVERT_100BASET:
        speed  = 1;
        break;

    case MII_ANADVERT_100BASETFD:
        speed  = 1;
        duplex = 1;
        break;

    case (MII_ANADVERT_10BASET | MII_ANADVERT_10BASETFD):
        duplex = MII_CONFIG_AUTO;
        break;

   case (MII_ANADVERT_100BASET | MII_ANADVERT_100BASETFD):
        speed  = 1;
        duplex = MII_CONFIG_AUTO;
        break;

    case (MII_ANADVERT_10BASET | MII_ANADVERT_100BASET):
        speed = MII_CONFIG_AUTO;
        break;
    
    case (MII_ANADVERT_10BASETFD | MII_ANADVERT_100BASETFD):
        speed = MII_CONFIG_AUTO;
	duplex = 1;
        break;
    	
    default:
        speed  = MII_CONFIG_AUTO;
        duplex = MII_CONFIG_AUTO;
        break;
    }
    return customizeMiiConfigure(speed, duplex);
}

/**
 * Calls customizeMiiAutonegotiate with the 
 * MII_ANADVERT_ALL_CAPS agrument.
 *
 * @external
 * @category BSP:Device_Drivers:MII
 * @since 6.0
 * @deprecated  7.0
 *
 * @return  0           Autonegotiation completed or timed out.
 * @return  PHY_ERROR   Invalid argument or error identifying PHY.    
 *
 * @see @link phy_anadvert
 *
**/
WORD16 customizeMiiNegotiate(void)
{
    return customizeMiiAutonegotiate(MII_ANADVERT_ALL_CAPS);
}

/**
 * Calls customizeMiiRestartAutoneg with the 
 * MII_ANADVERT_ALL_CAPS agrument.
 *
 * @external
 * @category BSP:Device_Drivers:MII
 * @since 6.0
 * @deprecated  7.0
 *
 * @return  0           Autonegotiation restarted.
 * @return  PHY_ERROR   Invalid argument or error identifying PHY.    
 *
 * @see @link phy_anadvert
 *
**/
WORD16 customizeMiiRestartAutonegotiation(void)
{
    return customizeMiiRestartAutoneg(MII_ANADVERT_ALL_CAPS);
}

#endif

