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 ******************************/