M2S010-MKR-KIT Ethernet Application VSC8541


#1

Has anyone successfully created a design that uses the VSC8541 Ethernet PHY? I can can configure they chip and it looks like its attempting to auto negotiate the link (put a logic analyzer on the RJ45 terminals that would be pre-magnetics). This is a fairly new kit so I am beginning to wonder if the magnetics are configured incorrectly, I also haven’t found any indication that anyone has successfully completed a design with the VSC8541.

I have the kit connected to a 10/100/1000 Ethernet switch. Both products support auto MDIX detection and cat5e cables are being used. I have also tried disabling auto negotiation and using forced 100Mbps with no luck.

Tom


#2

@thomasb1936 I did speak to my engineer who created the SmartFusion 2 Maker-Board Wiki and unfortunately he did not do any work with ethernet on his project. I have forwarded your post to my product manager to see if he has any insight or further information from MicroSemi.


#3

I solved it and someone may want to add the details to the wiki.

The board correctly configures the two clock selection inputs for a 50 MHz external clock (I checked this many times). Which is REFCLK_SEL0 low and REFCLK_SEL1 high and it does this with 10K pull ups/downs. What I discovered in the fine print of the data sheet is internally they are both pulled up with roughly a 30k so the input that is pulled low is hovering about 0.6V above ground. Which counts as a high. The chip thinks it has a 125MHz clock instead of the 50MHz clock and it can’t auto negotiate a link. Fixed it by actively driving both clock select inputs to the correct level with the FPGA.

To get the chip up you cant count on the board pull up or use an internal FPGA pull up. The inputs must be driven. Maybe on future revisions of the board change the pull down to 1K or omit it all together.


#4

Hi Thomas,

Maybe you can describe little what software you are running, I cant find any tutorial for this PHY, the microsemi
Firmware catalog ethernet project dont have support for this phy,

Any help would be most appreciated

Thanks
Konrad


#5

Hey Konrad,

I exported the mss_ethernet_mac firmware for m88e1340 phy. Then using the VSC8541 datasheet I modified the m88e1340_phy.c where I needed too.

If you are using the maker board the attached file is not a bad start, note that there still is a lot of function in the file that wont work for the VSC8541. But the functions that the mss_ethernet_mac.c calls to configure a GMII interface I have modified so they will work. I have added a few of my own as well.

A few other hints is because of how the board is set up you have to us the VSC8541 in unmanaged mode, at least initially, (see datasheet) so set your pull up and pull downs accordingly to get the mode you need. It is safer to drive this config signals with the FPGA during the reset pulse sequence.

I have a function you’ll see in the attached file that forces 100mbps link speed. This is required because even though the phy can negotiate a 1000mbps link I am not convinced the board is set up such that it can reliably transfer data at the 125MHz required for 1000mbps. I’d like to get a scope on the lines to confirm this but for now I just moved on.

Hope that gets you going.

Tom

#include "phy.h"
#include <stdio.h>
#include "mss_ethernet_mac_types.h"
#include "mss_ethernet_mac.h"
#include "../../CMSIS/m2sxxx.h"
#include "../../CMSIS/mss_assert.h"
#include "../../CMSIS/system_m2sxxx.h"
#include "mss_ethernet_mac_regs.h"

#ifdef __cplusplus
extern "C" {
#endif

#define PAGE_0                              0x00

#define VSC8541_AUX_PHY_STATUS             0x1cu
#define VSC8541_PHY_STATUS_1000            0x0010u
#define VSC8541_PHY_STATUS_100             0x0008u
#define VSC8541_PHY_STATUS_SPD_MASK        0x0018u
#define VSC8541_PHY_STATUS_FULLDUPLEX      0x0020u
#define M88E1340_PHY_STATUS_RESOLVED        0x0800
#define M88E1340_PHY_STATUS_LINK            0x0400

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

/* start of VCS8541 adapation */

#define VCS_8541_MAC_INTF_CFG 				0x23
#define VCS_8541_MAC_INTF_GMII				0x00
 /* Preprocessor Macros */

#define BMSR_AUTO_NEGOTIATION_COMPLETE      0x0020u

#define ANEG_REQUESTED                      0x80000000u
#define FORCED_CFG_REQUESTED                0x40000000u

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

/**
 * Address of the PHY on the MII management interface.
 */
static uint8_t g_phy_addr = 0u;

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

void MSS_MAC_PHY_VeriPHY(uint8_t phy_addr)
{
	uint16_t phy_reg;
	g_phy_addr = phy_addr;

	//change to extended page regs
	MSS_MAC_write_phy_reg(g_phy_addr, 0x1fu, 0x0001u);
	phy_reg = MSS_MAC_read_phy_reg(g_phy_addr, 0x1fu);
	//enable veriphy
	phy_reg = MSS_MAC_read_phy_reg(g_phy_addr, 0x18u);
	phy_reg |= 0x8000u;
	MSS_MAC_write_phy_reg(g_phy_addr, 0x18u, phy_reg);
    phy_reg = MSS_MAC_read_phy_reg(g_phy_addr, 0x18u);
    phy_reg &= 0x4000;

    while( phy_reg == 0x0000u ){
       phy_reg = MSS_MAC_read_phy_reg(g_phy_addr, 0x18u);
       phy_reg &= 0x4000;
    }

    //get the results
    phy_reg = MSS_MAC_read_phy_reg(g_phy_addr, 0x1au);
    phy_reg = MSS_MAC_read_phy_reg(g_phy_addr, 0x19u);
    phy_reg = MSS_MAC_read_phy_reg(g_phy_addr, 0x18u);
    MSS_MAC_write_phy_reg(g_phy_addr, 0x1fu, 0x0000u);

}

/*void MSS_MAC_phy_check_mdix(uint8_t phy_addr)
{

}*/

void MSS_MAC_force_10(uint8_t phy_addr)
{
	uint16_t phy_reg;
	g_phy_addr = phy_addr;
	phy_reg = MSS_MAC_read_phy_reg(g_phy_addr, MII_BMCR);
	phy_reg &= 0x0000u;
	MSS_MAC_write_phy_reg(g_phy_addr, MII_BMCR, phy_reg);
	phy_reg = MSS_MAC_read_phy_reg(g_phy_addr, MII_BMCR);

	//enable crossover detect
	phy_reg = MSS_MAC_read_phy_reg(g_phy_addr, 0x12u);
	phy_reg &= ~0x0080u;
	MSS_MAC_write_phy_reg(g_phy_addr, 0x12u, phy_reg);
	phy_reg = MSS_MAC_read_phy_reg(g_phy_addr, 0x12u);

}

void MSS_MAC_force_100(uint8_t phy_addr)
{
	uint16_t phy_reg;
	g_phy_addr = phy_addr;
	/*
	phy_reg = MSS_MAC_read_phy_reg(g_phy_addr, MII_BMCR);
	phy_reg &= 0xa000u;
	MSS_MAC_write_phy_reg(g_phy_addr, MII_BMCR, phy_reg);
	phy_reg = MSS_MAC_read_phy_reg(g_phy_addr, MII_BMCR);
	phy_reg &= 0x8000u;
	while( phy_reg != 0x0000u ){
		phy_reg = MSS_MAC_read_phy_reg(g_phy_addr, MII_BMCR);
		phy_reg &= 0x8000u;
	}
	*/
    phy_reg = 0x000u;
    MSS_MAC_write_phy_reg(g_phy_addr, MII_CTRL1000, phy_reg);
    MSS_MAC_phy_autonegotiate();

}
void MSS_MAC_set_far_loopback(uint8_t phy_addr)
{
	uint16_t phy_reg;
	g_phy_addr = phy_addr;
	phy_reg = MSS_MAC_read_phy_reg(g_phy_addr, 0x17u);
	phy_reg |= 0x0008u;
	MSS_MAC_write_phy_reg(g_phy_addr, 0x17u, phy_reg);
	phy_reg = MSS_MAC_read_phy_reg(g_phy_addr, 0x17u);
}
void MSS_MAC_phy_init(uint8_t phy_addr)
{

    uint16_t phy_reg;
    
    g_phy_addr = phy_addr;


    //Set MAC interface to GMII
    phy_reg = MSS_MAC_read_phy_reg(g_phy_addr, 0x17u);
    phy_reg &= 0x0000;
    MSS_MAC_write_phy_reg(g_phy_addr, 0x17, phy_reg);
    phy_reg = MSS_MAC_read_phy_reg(g_phy_addr, 0x17);

    /* Reset the PHY. */
    phy_reg = MSS_MAC_read_phy_reg(g_phy_addr, MII_BMCR);
    phy_reg |= BMCR_RESET;
    MSS_MAC_write_phy_reg(g_phy_addr, MII_BMCR, phy_reg);

    while( phy_reg != 0x0000u ){
       phy_reg = MSS_MAC_read_phy_reg(g_phy_addr, MII_BMCR);
       phy_reg &= BMCR_RESET;
    }

    //Disable ActiPHY
    //phy_reg = MSS_MAC_read_phy_reg(g_phy_addr, 0x1cu);
    //phy_reg &= ~0x0040u;
    //MSS_MAC_write_phy_reg(g_phy_addr, 0x1cu, phy_reg);
    //phy_reg = MSS_MAC_read_phy_reg(g_phy_addr, 0x1cu);
}

/***************************************************************************//**
 * 
 */
void MSS_MAC_phy_set_link_speed(uint32_t speed_duplex_select)
{
    uint16_t phy_reg;
    uint32_t inc;
    uint32_t speed_select;
    const uint16_t mii_advertise_bits[4] = {ADVERTISE_10FULL, ADVERTISE_10HALF, ADVERTISE_100FULL, ADVERTISE_100HALF};
    
    /* Set auto-negotiation advertisement. */
    
    /* Set 10Mbps and 100Mbps advertisement. */
    phy_reg = MSS_MAC_read_phy_reg(g_phy_addr, MII_ADVERTISE);
    phy_reg &= ~(ADVERTISE_10HALF | ADVERTISE_10FULL |
                 ADVERTISE_100HALF | ADVERTISE_100FULL);
                 
    speed_select = speed_duplex_select;
    for(inc = 0u; inc < 4u; ++inc)
    {
        uint32_t advertise;
        advertise = speed_select & 0x00000001u;
        if(advertise != 0u)
        {
            phy_reg |= mii_advertise_bits[inc];
        }
        speed_select = speed_select >> 1u;
    }
    
    MSS_MAC_write_phy_reg(g_phy_addr, MII_ADVERTISE, phy_reg);
    
    /* Set 1000Mbps advertisement. */
    phy_reg = MSS_MAC_read_phy_reg(g_phy_addr, MII_CTRL1000);
    phy_reg &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF);
    //phy_reg &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF);
    
    if((speed_duplex_select & MSS_MAC_ANEG_1000M_FD) != 0u)
    {
        phy_reg |= ADVERTISE_1000FULL;
    }
    
    if((speed_duplex_select & MSS_MAC_ANEG_1000M_HD) != 0u)
    {
        phy_reg |= ADVERTISE_1000HALF;
    }
    //phy_reg &= 0x0000u;
    
    MSS_MAC_write_phy_reg(g_phy_addr, MII_CTRL1000, phy_reg);
    phy_reg = MSS_MAC_read_phy_reg(g_phy_addr, MII_CTRL1000);
    phy_reg = MSS_MAC_read_phy_reg(g_phy_addr, MII_BMCR);


}

/***************************************************************************//**
 * 
 */
void MSS_MAC_phy_autonegotiate(void)
{
    uint16_t phy_reg = 0x00u;
    uint16_t autoneg_complete;
    volatile uint32_t copper_aneg_timeout = 1000000u;
    
    /* Enable auto-negotiation. */
    //MSS_MAC_write_phy_reg(g_phy_addr, M88E1340_EXT_ADDR_PAGE_CR, PAGE_0);
    
    //MSS_MAC_write_phy_reg(g_phy_addr, 0x31u, 0x0002u);
    //phy_reg = MSS_MAC_read_phy_reg(g_phy_addr, 0x0eu);
    //MSS_MAC_write_phy_reg(g_phy_addr, 0x31u, 0x0000u);

    //phy_reg = MSS_MAC_read_phy_reg(g_phy_addr, 0x1cu);

    //phy_reg = MSS_MAC_read_phy_reg(g_phy_addr, 0x05u);
    //phy_reg = MSS_MAC_read_phy_reg(g_phy_addr, 0x04u);
    //phy_reg = MSS_MAC_read_phy_reg(g_phy_addr, 0x0au);

    phy_reg = MSS_MAC_read_phy_reg(g_phy_addr, MII_BMCR);
    phy_reg |= (BMCR_ANENABLE | BMCR_ANRESTART);
    //phy_reg |= (BMCR_SPEED100 );//| BMCR_SPEED1000);



    //phy_reg |= 0x1920u;
    MSS_MAC_write_phy_reg(g_phy_addr, MII_BMCR, phy_reg);
    
    //phy_reg = MSS_MAC_read_phy_reg(g_phy_addr, MII_BMCR);

    /* Wait for copper auto-negotiation to complete. */
    do {
    	phy_reg = MSS_MAC_read_phy_reg(g_phy_addr, MII_LPA);
    	phy_reg = MSS_MAC_read_phy_reg(g_phy_addr, MII_ADVERTISE);
    	phy_reg = MSS_MAC_read_phy_reg(g_phy_addr, MII_STAT1000);
    	phy_reg = MSS_MAC_read_phy_reg(g_phy_addr, MII_ESTATUS);
    	phy_reg = MSS_MAC_read_phy_reg(g_phy_addr, MII_STAT1000_2);
    	phy_reg = MSS_MAC_read_phy_reg(g_phy_addr, MII_INT);
        phy_reg = MSS_MAC_read_phy_reg(g_phy_addr, MII_BMSR);
        autoneg_complete = phy_reg & BMSR_AUTO_NEGOTIATION_COMPLETE;
        --copper_aneg_timeout;
    } while((!autoneg_complete && (copper_aneg_timeout != 0u)) || (0xFFFF == phy_reg));
}

/***************************************************************************//**
 * 
 */
uint8_t MSS_MAC_phy_get_link_status
(
    mss_mac_speed_t * speed,
    uint8_t *     fullduplex
)
{
    uint16_t phy_reg;
    uint16_t copper_link_up;
    uint8_t link_status;

    /*
     * Find out if link is up between Marvell PHY and remote device.
     */
    phy_reg = MSS_MAC_read_phy_reg(g_phy_addr, MII_BMSR);
    copper_link_up = phy_reg & BMSR_LSTATUS;
    
    if(copper_link_up != MSS_MAC_LINK_DOWN)
    {
        uint16_t duplex;
        uint16_t phy_speed;

        /* Link is up. */
        link_status = MSS_MAC_LINK_UP;
        printf("link is up: ");
    	phy_reg = MSS_MAC_read_phy_reg(g_phy_addr, MII_STAT1000);
        phy_reg = MSS_MAC_read_phy_reg(g_phy_addr, MII_STAT1000_2);
        phy_reg = MSS_MAC_read_phy_reg(g_phy_addr, VSC8541_AUX_PHY_STATUS);
        duplex = phy_reg & VSC8541_PHY_STATUS_FULLDUPLEX;
        phy_speed = phy_reg & VSC8541_PHY_STATUS_SPD_MASK;
        
        if(MSS_MAC_HALF_DUPLEX == duplex)
        {
            *fullduplex = MSS_MAC_HALF_DUPLEX;
        }
        else
        {
            *fullduplex = MSS_MAC_FULL_DUPLEX;
        }
        
        switch(phy_speed) 
        {
            case VSC8541_PHY_STATUS_1000:
                *speed = MSS_MAC_1000MBPS;
                printf("Speed is 1000BASE-T\n\r");
                break;
    
            case VSC8541_PHY_STATUS_100:
                *speed = MSS_MAC_100MBPS;
                printf("Speed is 100BASE-TX\n\r");
                break;
    
            default:
                *speed = MSS_MAC_10MBPS;
                printf("Speed is 10BASE-T\n\r");
                break;
        }
    }
    else
    {
        /* Link is down. */
        link_status = MSS_MAC_LINK_DOWN;
    }
    
    return link_status;
}

#ifdef __cplusplus
}
#endif

/******************************** END OF FILE ******************************/