forked from ROMEO/obsw
835 lines
27 KiB
C
835 lines
27 KiB
C
/*
|
|
* Copyright (C) 2010 - 2022 Xilinx, Inc.
|
|
* Copyright (C) 2022 - 2024 Advanced Micro Devices, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without modification,
|
|
* are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
* 3. The name of the author may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
|
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
|
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
|
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
|
* OF SUCH DAMAGE.
|
|
*
|
|
* This file is part of the lwIP TCP/IP stack.
|
|
*
|
|
*/
|
|
|
|
#include "netif/xaxiemacif.h"
|
|
#include "lwipopts.h"
|
|
#include "sleep.h"
|
|
#include "xemac_ieee_reg.h"
|
|
|
|
#define PHY_R0_ISOLATE 0x0400
|
|
#define PHY_DETECT_REG 1
|
|
#define PHY_IDENTIFIER_1_REG 2
|
|
#define PHY_IDENTIFIER_2_REG 3
|
|
#define PHY_DETECT_MASK 0x1808
|
|
#define PHY_MARVELL_IDENTIFIER 0x0141
|
|
#define PHY_TI_IDENTIFIER 0x2000
|
|
|
|
/* Marvel PHY flags */
|
|
#define MARVEL_PHY_IDENTIFIER 0x141
|
|
#define MARVEL_PHY_MODEL_NUM_MASK 0x3F0
|
|
#define MARVEL_PHY_88E1111_MODEL 0xC0
|
|
#define MARVEL_PHY_88E1116R_MODEL 0x240
|
|
#define PHY_88E1111_RGMII_RX_CLOCK_DELAYED_MASK 0x0080
|
|
|
|
/* TI PHY Flags */
|
|
#define TI_PHY_DETECT_MASK 0x796D
|
|
#define TI_PHY_IDENTIFIER 0x2000
|
|
#define TI_PHY_DP83867_MODEL 0xA231
|
|
#define DP83867_RGMII_CLOCK_DELAY_CTRL_MASK 0x0003
|
|
#define DP83867_RGMII_TX_CLOCK_DELAY_MASK 0x0030
|
|
#define DP83867_RGMII_RX_CLOCK_DELAY_MASK 0x0003
|
|
|
|
/* TI DP83867 PHY Registers */
|
|
#define DP83867_R32_RGMIICTL1 0x32
|
|
#define DP83867_R86_RGMIIDCTL 0x86
|
|
|
|
#define TI_PHY_REGCR 0xD
|
|
#define TI_PHY_ADDDR 0xE
|
|
#define TI_PHY_PHYCTRL 0x10
|
|
#define TI_PHY_CFGR2 0x14
|
|
#define TI_PHY_SGMIITYPE 0xD3
|
|
#define TI_PHY_CFGR2_SGMII_AUTONEG_EN 0x0080
|
|
#define TI_PHY_SGMIICLK_EN 0x4000
|
|
#define TI_PHY_REGCR_DEVAD_EN 0x001F
|
|
#define TI_PHY_REGCR_DEVAD_DATAEN 0x4000
|
|
#define TI_PHY_CFGR2_MASK 0x003F
|
|
#define TI_PHY_REGCFG4 0x31
|
|
#define TI_PHY_REGCR_DATA 0x401F
|
|
#define TI_PHY_CFG4RESVDBIT7 0x80
|
|
#define TI_PHY_CFG4RESVDBIT8 0x100
|
|
#define TI_PHY_CFG4_AUTONEG_TIMER 0x60
|
|
|
|
#define TI_PHY_CFG2_SPEEDOPT_10EN 0x0040
|
|
#define TI_PHY_CFG2_SGMII_AUTONEGEN 0x0080
|
|
#define TI_PHY_CFG2_SPEEDOPT_ENH 0x0100
|
|
#define TI_PHY_CFG2_SPEEDOPT_CNT 0x0800
|
|
#define TI_PHY_CFG2_SPEEDOPT_INTLOW 0x2000
|
|
|
|
#define TI_PHY_CR_SGMII_EN 0x0800
|
|
|
|
/* Loop counters to check for reset done
|
|
*/
|
|
#define RESET_TIMEOUT 0xFFFF
|
|
#define AUTO_NEG_TIMEOUT 0x00FFFFFF
|
|
|
|
#define IEEE_CTRL_RESET 0x9140
|
|
#define IEEE_CTRL_ISOLATE_DISABLE 0xFBFF
|
|
|
|
#define PHY_XILINX_PCS_PMA_ID1 0x0174
|
|
#define PHY_XILINX_PCS_PMA_ID2 0x0C00
|
|
|
|
#ifdef SDT
|
|
#define XPAR_AXIETHERNET_0_PHYADDR XPAR_XAXIETHERNET_0_PHYADDR
|
|
#define XPAR_AXIETHERNET_0_BASEADDR XPAR_XAXIETHERNET_0_BASEADDR
|
|
#endif
|
|
|
|
extern u32_t phyaddrforemac;
|
|
|
|
static void __attribute__ ((noinline)) AxiEthernetUtilPhyDelay(unsigned int Seconds);
|
|
|
|
static int detect_phy(XAxiEthernet *xaxiemacp)
|
|
{
|
|
u16 phy_reg;
|
|
u16 phy_id;
|
|
u32 phy_addr;
|
|
|
|
for (phy_addr = 31; phy_addr > 0; phy_addr--) {
|
|
XAxiEthernet_PhyRead(xaxiemacp, phy_addr, PHY_DETECT_REG,
|
|
&phy_reg);
|
|
|
|
if ((phy_reg != 0xFFFF) &&
|
|
((phy_reg & PHY_DETECT_MASK) == PHY_DETECT_MASK)) {
|
|
/* Found a valid PHY address */
|
|
LWIP_DEBUGF(NETIF_DEBUG, ("XAxiEthernet detect_phy: PHY detected at address %d.\r\n", phy_addr));
|
|
LWIP_DEBUGF(NETIF_DEBUG, ("XAxiEthernet detect_phy: PHY detected.\r\n"));
|
|
XAxiEthernet_PhyRead(xaxiemacp, phy_addr, PHY_IDENTIFIER_1_REG,
|
|
&phy_reg);
|
|
if ((phy_reg != PHY_MARVELL_IDENTIFIER) &&
|
|
(phy_reg != TI_PHY_IDENTIFIER)){
|
|
xil_printf("WARNING: Not a Marvell or TI Ethernet PHY. Please verify the initialization sequence\r\n");
|
|
}
|
|
phyaddrforemac = phy_addr;
|
|
return phy_addr;
|
|
}
|
|
|
|
XAxiEthernet_PhyRead(xaxiemacp, phy_addr, PHY_IDENTIFIER_1_REG,
|
|
&phy_id);
|
|
|
|
if (phy_id == PHY_XILINX_PCS_PMA_ID1) {
|
|
XAxiEthernet_PhyRead(xaxiemacp, phy_addr, PHY_IDENTIFIER_2_REG,
|
|
&phy_id);
|
|
if (phy_id == PHY_XILINX_PCS_PMA_ID2) {
|
|
/* Found a valid PHY address */
|
|
LWIP_DEBUGF(NETIF_DEBUG, ("XAxiEthernet detect_phy: PHY detected at address %d.\r\n",
|
|
phy_addr));
|
|
phyaddrforemac = phy_addr;
|
|
return phy_addr;
|
|
}
|
|
}
|
|
}
|
|
|
|
LWIP_DEBUGF(NETIF_DEBUG, ("XAxiEthernet detect_phy: No PHY detected. Assuming a PHY at address 0\r\n"));
|
|
|
|
/* default to zero */
|
|
return 0;
|
|
}
|
|
|
|
static int isphy_pcspma(XAxiEthernet *xaxiemacp, u32 phy_addr)
|
|
{
|
|
u16 phy_id;
|
|
|
|
XAxiEthernet_PhyRead(xaxiemacp, phy_addr, PHY_IDENTIFIER_1_REG,
|
|
&phy_id);
|
|
if (phy_id == PHY_XILINX_PCS_PMA_ID1) {
|
|
XAxiEthernet_PhyRead(xaxiemacp, phy_addr, PHY_IDENTIFIER_2_REG,
|
|
&phy_id);
|
|
if (phy_id == PHY_XILINX_PCS_PMA_ID2) {
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void XAxiEthernet_PhyReadExtended(XAxiEthernet *InstancePtr, u32 PhyAddress,
|
|
u32 RegisterNum, u16 *PhyDataPtr)
|
|
{
|
|
XAxiEthernet_PhyWrite(InstancePtr, PhyAddress,
|
|
IEEE_MMD_ACCESS_CONTROL_REG, IEEE_MMD_ACCESS_CTRL_DEVAD_MASK);
|
|
|
|
XAxiEthernet_PhyWrite(InstancePtr, PhyAddress,
|
|
IEEE_MMD_ACCESS_ADDRESS_DATA_REG, RegisterNum);
|
|
|
|
XAxiEthernet_PhyWrite(InstancePtr, PhyAddress,
|
|
IEEE_MMD_ACCESS_CONTROL_REG, IEEE_MMD_ACCESS_CTRL_NOPIDEVAD_MASK);
|
|
|
|
XAxiEthernet_PhyRead(InstancePtr, PhyAddress,
|
|
IEEE_MMD_ACCESS_ADDRESS_DATA_REG, PhyDataPtr);
|
|
|
|
}
|
|
|
|
void XAxiEthernet_PhyWriteExtended(XAxiEthernet *InstancePtr, u32 PhyAddress,
|
|
u32 RegisterNum, u16 PhyDataPtr)
|
|
{
|
|
XAxiEthernet_PhyWrite(InstancePtr, PhyAddress,
|
|
IEEE_MMD_ACCESS_CONTROL_REG, IEEE_MMD_ACCESS_CTRL_DEVAD_MASK);
|
|
|
|
XAxiEthernet_PhyWrite(InstancePtr, PhyAddress,
|
|
IEEE_MMD_ACCESS_ADDRESS_DATA_REG, RegisterNum);
|
|
|
|
XAxiEthernet_PhyWrite(InstancePtr, PhyAddress,
|
|
IEEE_MMD_ACCESS_CONTROL_REG, IEEE_MMD_ACCESS_CTRL_NOPIDEVAD_MASK);
|
|
|
|
XAxiEthernet_PhyWrite(InstancePtr, PhyAddress,
|
|
IEEE_MMD_ACCESS_ADDRESS_DATA_REG, PhyDataPtr);
|
|
|
|
}
|
|
|
|
unsigned int get_phy_negotiated_speed (XAxiEthernet *xaxiemacp, u32 phy_addr)
|
|
{
|
|
u16 control;
|
|
u16 status;
|
|
u16 partner_capabilities;
|
|
u16 partner_capabilities_1000;
|
|
u16 phylinkspeed;
|
|
u16 temp;
|
|
|
|
|
|
phy_addr = detect_phy(xaxiemacp);
|
|
|
|
xil_printf("Start PHY autonegotiation \r\n");
|
|
XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_CONTROL_REG_OFFSET,
|
|
&control);
|
|
|
|
control |= IEEE_CTRL_AUTONEGOTIATE_ENABLE;
|
|
control |= IEEE_STAT_AUTONEGOTIATE_RESTART;
|
|
|
|
if (isphy_pcspma(xaxiemacp, phy_addr)) {
|
|
control &= IEEE_CTRL_ISOLATE_DISABLE;
|
|
}
|
|
|
|
XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_CONTROL_REG_OFFSET,
|
|
control);
|
|
if (isphy_pcspma(xaxiemacp, phy_addr)) {
|
|
XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_STATUS_REG_OFFSET, &status);
|
|
xil_printf("Waiting for PHY to complete autonegotiation \r\n");
|
|
while ( !(status & IEEE_STAT_AUTONEGOTIATE_COMPLETE) ) {
|
|
AxiEthernetUtilPhyDelay(1);
|
|
XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_STATUS_REG_OFFSET,
|
|
&status);
|
|
|
|
}
|
|
|
|
xil_printf("Autonegotiation complete \r\n");
|
|
|
|
if (xaxiemacp->Config.Speed == XAE_SPEED_2500_MBPS)
|
|
return XAE_SPEED_2500_MBPS;
|
|
|
|
#ifndef SDT
|
|
if (XAxiEthernet_GetPhysicalInterface(xaxiemacp) == XAE_PHY_TYPE_1000BASE_X) {
|
|
#else
|
|
if (XAxiEthernet_Get_Phy_Interface(xaxiemacp) == XAE_PHY_TYPE_1000BASE_X) {
|
|
#endif
|
|
XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_PAGE_ADDRESS_REGISTER, 1);
|
|
XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_PARTNER_ABILITIES_1_REG_OFFSET, &temp);
|
|
if ((temp & 0x0020) == 0x0020) {
|
|
XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_PAGE_ADDRESS_REGISTER, 0);
|
|
return 1000;
|
|
}
|
|
else {
|
|
XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_PAGE_ADDRESS_REGISTER, 0);
|
|
xil_printf("Link error, temp = %x\r\n", temp);
|
|
return 0;
|
|
}
|
|
#ifndef SDT
|
|
} else if(XAxiEthernet_GetPhysicalInterface(xaxiemacp) == XAE_PHY_TYPE_SGMII) {
|
|
#else
|
|
} else if(XAxiEthernet_Get_Phy_Interface(xaxiemacp) == XAE_PHY_TYPE_SGMII) {
|
|
|
|
#endif
|
|
xil_printf("Waiting for Link to be up; Polling for SGMII core Reg \r\n");
|
|
XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_PARTNER_ABILITIES_1_REG_OFFSET, &temp);
|
|
while(!(temp & 0x8000)) {
|
|
XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_PARTNER_ABILITIES_1_REG_OFFSET, &temp);
|
|
}
|
|
if((temp & 0x0C00) == 0x0800) {
|
|
return 1000;
|
|
}
|
|
else if((temp & 0x0C00) == 0x0400) {
|
|
return 100;
|
|
}
|
|
else if((temp & 0x0C00) == 0x0000) {
|
|
return 10;
|
|
} else {
|
|
xil_printf("get_IEEE_phy_speed(): Invalid speed bit value, Defaulting to Speed = 10 Mbps\r\n");
|
|
XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_CONTROL_REG_OFFSET, &temp);
|
|
XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_CONTROL_REG_OFFSET, 0x0100);
|
|
return 10;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Read PHY control and status registers is successful. */
|
|
XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_CONTROL_REG_OFFSET,
|
|
&control);
|
|
XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_STATUS_REG_OFFSET,
|
|
&status);
|
|
if ((control & IEEE_CTRL_AUTONEGOTIATE_ENABLE) && (status &
|
|
IEEE_STAT_AUTONEGOTIATE_CAPABLE)) {
|
|
xil_printf("Waiting for PHY to complete autonegotiation.\r\n");
|
|
while ( !(status & IEEE_STAT_AUTONEGOTIATE_COMPLETE) ) {
|
|
XAxiEthernet_PhyRead(xaxiemacp, phy_addr,
|
|
IEEE_STATUS_REG_OFFSET,
|
|
&status);
|
|
}
|
|
|
|
xil_printf("autonegotiation complete \r\n");
|
|
|
|
XAxiEthernet_PhyRead(xaxiemacp, phy_addr,
|
|
IEEE_PARTNER_ABILITIES_1_REG_OFFSET,
|
|
&partner_capabilities);
|
|
if (status & IEEE_STAT_1GBPS_EXTENSIONS) {
|
|
XAxiEthernet_PhyRead(xaxiemacp, phy_addr,
|
|
IEEE_PARTNER_ABILITIES_3_REG_OFFSET,
|
|
&partner_capabilities_1000);
|
|
if (partner_capabilities_1000 &
|
|
IEEE_AN3_ABILITY_MASK_1GBPS)
|
|
return 1000;
|
|
}
|
|
|
|
if (partner_capabilities & IEEE_AN1_ABILITY_MASK_100MBPS)
|
|
return 100;
|
|
if (partner_capabilities & IEEE_AN1_ABILITY_MASK_10MBPS)
|
|
return 10;
|
|
|
|
xil_printf("%s: unknown PHY link speed, setting TEMAC speed to be 10 Mbps\r\n",
|
|
__FUNCTION__);
|
|
return 10;
|
|
} else {
|
|
/* Update TEMAC speed accordingly */
|
|
if (status & IEEE_STAT_1GBPS_EXTENSIONS) {
|
|
|
|
/* Get commanded link speed */
|
|
phylinkspeed = control &
|
|
IEEE_CTRL_1GBPS_LINKSPEED_MASK;
|
|
|
|
switch (phylinkspeed) {
|
|
case (IEEE_CTRL_LINKSPEED_1000M):
|
|
return 1000;
|
|
case (IEEE_CTRL_LINKSPEED_100M):
|
|
return 100;
|
|
case (IEEE_CTRL_LINKSPEED_10M):
|
|
return 10;
|
|
default:
|
|
xil_printf("%s: unknown PHY link speed (%d), setting TEMAC speed to be 10 Mbps\r\n",
|
|
__FUNCTION__, phylinkspeed);
|
|
return 10;
|
|
}
|
|
} else {
|
|
return (control & IEEE_CTRL_LINKSPEED_MASK) ? 100 : 10;
|
|
}
|
|
}
|
|
}
|
|
|
|
unsigned int get_phy_speed_TI_DP83867(XAxiEthernet *xaxiemacp, u32 phy_addr)
|
|
{
|
|
u16 phy_val;
|
|
u16 control;
|
|
|
|
xil_printf("Start PHY autonegotiation \r\n");
|
|
|
|
/* Changing the PHY RX and TX DELAY settings. */
|
|
XAxiEthernet_PhyReadExtended(xaxiemacp, phy_addr, DP83867_R32_RGMIICTL1, &phy_val);
|
|
phy_val |= DP83867_RGMII_CLOCK_DELAY_CTRL_MASK;
|
|
XAxiEthernet_PhyWriteExtended(xaxiemacp, phy_addr, DP83867_R32_RGMIICTL1, phy_val);
|
|
|
|
XAxiEthernet_PhyReadExtended(xaxiemacp, phy_addr, DP83867_R86_RGMIIDCTL, &phy_val);
|
|
phy_val &= 0xFF00;
|
|
phy_val |= DP83867_RGMII_TX_CLOCK_DELAY_MASK;
|
|
phy_val |= DP83867_RGMII_RX_CLOCK_DELAY_MASK;
|
|
XAxiEthernet_PhyWriteExtended(xaxiemacp, phy_addr, DP83867_R86_RGMIIDCTL, phy_val);
|
|
|
|
/* Set advertised speeds for 10/100/1000Mbps modes. */
|
|
XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, &control);
|
|
control |= IEEE_ASYMMETRIC_PAUSE_MASK;
|
|
control |= IEEE_PAUSE_MASK;
|
|
control |= ADVERTISE_100;
|
|
control |= ADVERTISE_10;
|
|
XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, control);
|
|
|
|
XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_1000_ADVERTISE_REG_OFFSET, &control);
|
|
control |= ADVERTISE_1000;
|
|
XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_1000_ADVERTISE_REG_OFFSET, control);
|
|
|
|
return get_phy_negotiated_speed(xaxiemacp, phy_addr);
|
|
}
|
|
|
|
unsigned int get_phy_speed_TI_DP83867_SGMII(XAxiEthernet *xaxiemacp, u32 phy_addr)
|
|
{
|
|
u16 control;
|
|
u16 temp;
|
|
u16 phyregtemp;
|
|
|
|
xil_printf("Start TI PHY autonegotiation \r\n");
|
|
|
|
/* Enable SGMII Clock */
|
|
XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, TI_PHY_REGCR,
|
|
TI_PHY_REGCR_DEVAD_EN);
|
|
XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, TI_PHY_ADDDR,
|
|
TI_PHY_SGMIITYPE);
|
|
XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, TI_PHY_REGCR,
|
|
TI_PHY_REGCR_DEVAD_EN | TI_PHY_REGCR_DEVAD_DATAEN);
|
|
XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, TI_PHY_ADDDR,
|
|
TI_PHY_SGMIICLK_EN);
|
|
|
|
XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_CONTROL_REG_OFFSET,
|
|
&control);
|
|
control |= (IEEE_CTRL_AUTONEGOTIATE_ENABLE | IEEE_CTRL_LINKSPEED_1000M |
|
|
IEEE_CTRL_FULL_DUPLEX);
|
|
XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_CONTROL_REG_OFFSET,
|
|
control);
|
|
|
|
XAxiEthernet_PhyRead(xaxiemacp, phy_addr, TI_PHY_CFGR2, &control);
|
|
control &= TI_PHY_CFGR2_MASK;
|
|
control |= (TI_PHY_CFG2_SPEEDOPT_10EN |
|
|
TI_PHY_CFG2_SGMII_AUTONEGEN |
|
|
TI_PHY_CFG2_SPEEDOPT_ENH |
|
|
TI_PHY_CFG2_SPEEDOPT_CNT |
|
|
TI_PHY_CFG2_SPEEDOPT_INTLOW);
|
|
XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, TI_PHY_CFGR2, control);
|
|
|
|
/* Disable RGMII */
|
|
XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, TI_PHY_REGCR,
|
|
TI_PHY_REGCR_DEVAD_EN);
|
|
XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, TI_PHY_ADDDR,
|
|
DP83867_R32_RGMIICTL1);
|
|
XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, TI_PHY_REGCR,
|
|
TI_PHY_REGCR_DEVAD_EN | TI_PHY_REGCR_DEVAD_DATAEN);
|
|
XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, TI_PHY_ADDDR, 0);
|
|
|
|
XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, TI_PHY_PHYCTRL,
|
|
TI_PHY_CR_SGMII_EN);
|
|
|
|
xil_printf("Waiting for Link to be up \r\n");
|
|
XAxiEthernet_PhyRead(xaxiemacp, phy_addr,
|
|
IEEE_PARTNER_ABILITIES_1_REG_OFFSET, &temp);
|
|
while(!(temp & 0x4000)) {
|
|
XAxiEthernet_PhyRead(xaxiemacp, phy_addr,
|
|
IEEE_PARTNER_ABILITIES_1_REG_OFFSET, &temp);
|
|
}
|
|
xil_printf("Auto negotiation completed for TI PHY\n\r");
|
|
|
|
/* SW workaround for unstable link when RX_CTRL is not STRAP MODE 3 or 4 */
|
|
XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, TI_PHY_REGCR, TI_PHY_REGCR_DEVAD_EN);
|
|
XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, TI_PHY_ADDDR, TI_PHY_REGCFG4);
|
|
XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, TI_PHY_REGCR, TI_PHY_REGCR_DATA);
|
|
XAxiEthernet_PhyRead(xaxiemacp, phy_addr, TI_PHY_ADDDR, (u16_t *)&phyregtemp);
|
|
phyregtemp &= ~(TI_PHY_CFG4RESVDBIT7);
|
|
phyregtemp |= TI_PHY_CFG4RESVDBIT8;
|
|
phyregtemp &= ~(TI_PHY_CFG4_AUTONEG_TIMER);
|
|
phyregtemp |= TI_PHY_CFG4_AUTONEG_TIMER;
|
|
XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, TI_PHY_REGCR, TI_PHY_REGCR_DEVAD_EN);
|
|
XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, TI_PHY_ADDDR, TI_PHY_REGCFG4);
|
|
XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, TI_PHY_REGCR, TI_PHY_REGCR_DATA);
|
|
XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, TI_PHY_ADDDR, phyregtemp);
|
|
|
|
return get_phy_negotiated_speed(xaxiemacp, phy_addr);
|
|
}
|
|
|
|
unsigned int get_phy_speed_88E1116R(XAxiEthernet *xaxiemacp, u32 phy_addr)
|
|
{
|
|
u16 phy_val;
|
|
u16 control;
|
|
u16 status;
|
|
u16 partner_capabilities;
|
|
|
|
xil_printf("Start PHY autonegotiation \r\n");
|
|
|
|
XAxiEthernet_PhyWrite(xaxiemacp,phy_addr, IEEE_PAGE_ADDRESS_REGISTER, 2);
|
|
XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_CONTROL_REG_MAC, &control);
|
|
control |= IEEE_RGMII_TXRX_CLOCK_DELAYED_MASK;
|
|
XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_CONTROL_REG_MAC, control);
|
|
|
|
XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_PAGE_ADDRESS_REGISTER, 0);
|
|
|
|
XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, &control);
|
|
control |= IEEE_ASYMMETRIC_PAUSE_MASK;
|
|
control |= IEEE_PAUSE_MASK;
|
|
control |= ADVERTISE_100;
|
|
control |= ADVERTISE_10;
|
|
XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, control);
|
|
|
|
XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_1000_ADVERTISE_REG_OFFSET,
|
|
&control);
|
|
control |= ADVERTISE_1000;
|
|
XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_1000_ADVERTISE_REG_OFFSET,
|
|
control);
|
|
|
|
XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_PAGE_ADDRESS_REGISTER, 0);
|
|
XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_COPPER_SPECIFIC_CONTROL_REG,
|
|
&control);
|
|
control |= (7 << 12); /* max number of gigabit atphy_valts */
|
|
control |= (1 << 11); /* enable downshift */
|
|
XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_COPPER_SPECIFIC_CONTROL_REG,
|
|
control);
|
|
|
|
XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_CONTROL_REG_OFFSET, &control);
|
|
control |= IEEE_CTRL_AUTONEGOTIATE_ENABLE;
|
|
control |= IEEE_STAT_AUTONEGOTIATE_RESTART;
|
|
XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_CONTROL_REG_OFFSET, control);
|
|
|
|
XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_CONTROL_REG_OFFSET, &control);
|
|
control |= IEEE_CTRL_RESET_MASK;
|
|
XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_CONTROL_REG_OFFSET, control);
|
|
while (1) {
|
|
XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_CONTROL_REG_OFFSET, &control);
|
|
if (control & IEEE_CTRL_RESET_MASK)
|
|
continue;
|
|
else
|
|
break;
|
|
}
|
|
|
|
xil_printf("Waiting for PHY to complete autonegotiation.\r\n");
|
|
|
|
XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_STATUS_REG_OFFSET, &status);
|
|
while ( !(status & IEEE_STAT_AUTONEGOTIATE_COMPLETE) ) {
|
|
AxiEthernetUtilPhyDelay(1);
|
|
XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_COPPER_SPECIFIC_STATUS_REG_2,
|
|
&phy_val);
|
|
if (phy_val & IEEE_AUTONEG_ERROR_MASK) {
|
|
xil_printf("Auto negotiation error \r\n");
|
|
}
|
|
XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_STATUS_REG_OFFSET,
|
|
&status);
|
|
}
|
|
|
|
xil_printf("autonegotiation complete \r\n");
|
|
|
|
XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_SPECIFIC_STATUS_REG,
|
|
&partner_capabilities);
|
|
if ( ((partner_capabilities >> 14) & 3) == 2)/* 1000Mbps */
|
|
return 1000;
|
|
else if ( ((partner_capabilities >> 14) & 3) == 1)/* 100Mbps */
|
|
return 100;
|
|
else /* 10Mbps */
|
|
return 10;
|
|
}
|
|
|
|
|
|
unsigned int get_phy_speed_88E1111 (XAxiEthernet *xaxiemacp, u32 phy_addr)
|
|
{
|
|
u16 control;
|
|
int TimeOut;
|
|
u16 phy_val;
|
|
|
|
#ifndef SDT
|
|
if (XAxiEthernet_GetPhysicalInterface(xaxiemacp) ==
|
|
#else
|
|
if (XAxiEthernet_Get_Phy_Interface(xaxiemacp) ==
|
|
#endif
|
|
XAE_PHY_TYPE_RGMII_2_0) {
|
|
XAxiEthernet_PhyRead(xaxiemacp, phy_addr,
|
|
IEEE_EXT_PHY_SPECIFIC_CONTROL_REG, &phy_val);
|
|
phy_val |= PHY_88E1111_RGMII_RX_CLOCK_DELAYED_MASK;
|
|
XAxiEthernet_PhyWrite(xaxiemacp, phy_addr,
|
|
IEEE_EXT_PHY_SPECIFIC_CONTROL_REG, phy_val);
|
|
|
|
XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_CONTROL_REG_OFFSET,
|
|
&control);
|
|
XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_CONTROL_REG_OFFSET,
|
|
control | IEEE_CTRL_RESET_MASK);
|
|
|
|
TimeOut = RESET_TIMEOUT;
|
|
while (TimeOut) {
|
|
XAxiEthernet_PhyRead(xaxiemacp, phy_addr,
|
|
IEEE_CONTROL_REG_OFFSET, &control);
|
|
if (!(control & IEEE_CTRL_RESET_MASK))
|
|
break;
|
|
TimeOut -= 1;
|
|
}
|
|
|
|
if (!TimeOut) {
|
|
xil_printf("%s: Phy Reset failed\n\r", __FUNCTION__);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_1000_ADVERTISE_REG_OFFSET,
|
|
ADVERTISE_1000);
|
|
XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG,
|
|
ADVERTISE_100_AND_10);
|
|
|
|
return get_phy_negotiated_speed(xaxiemacp, phy_addr);
|
|
}
|
|
|
|
unsigned get_IEEE_phy_speed(XAxiEthernet *xaxiemacp)
|
|
{
|
|
u16 phy_identifier;
|
|
u16 phy_model;
|
|
u8 phytype;
|
|
|
|
#ifdef XPAR_AXIETHERNET_0_BASEADDR
|
|
u32 phy_addr = detect_phy(xaxiemacp);
|
|
|
|
/* Get the PHY Identifier and Model number */
|
|
XAxiEthernet_PhyRead(xaxiemacp, phy_addr, PHY_IDENTIFIER_1_REG, &phy_identifier);
|
|
XAxiEthernet_PhyRead(xaxiemacp, phy_addr, PHY_IDENTIFIER_2_REG, &phy_model);
|
|
|
|
/* Depending upon what manufacturer PHY is connected, a different mask is
|
|
* needed to determine the specific model number of the PHY. */
|
|
if (phy_identifier == MARVEL_PHY_IDENTIFIER) {
|
|
phy_model = phy_model & MARVEL_PHY_MODEL_NUM_MASK;
|
|
|
|
if (phy_model == MARVEL_PHY_88E1116R_MODEL) {
|
|
return get_phy_speed_88E1116R(xaxiemacp, phy_addr);
|
|
} else if (phy_model == MARVEL_PHY_88E1111_MODEL) {
|
|
return get_phy_speed_88E1111(xaxiemacp, phy_addr);
|
|
}
|
|
} else if (phy_identifier == TI_PHY_IDENTIFIER) {
|
|
phy_model = phy_model & TI_PHY_DP83867_MODEL;
|
|
#ifndef SDT
|
|
phytype = XAxiEthernet_GetPhysicalInterface(xaxiemacp);
|
|
#else
|
|
phytype = XAxiEthernet_Get_Phy_Interface(xaxiemacp);
|
|
#endif
|
|
if (phy_model == TI_PHY_DP83867_MODEL && phytype == XAE_PHY_TYPE_SGMII) {
|
|
return get_phy_speed_TI_DP83867_SGMII(xaxiemacp, phy_addr);
|
|
}
|
|
|
|
if (phy_model == TI_PHY_DP83867_MODEL) {
|
|
return get_phy_speed_TI_DP83867(xaxiemacp, phy_addr);
|
|
}
|
|
}
|
|
else {
|
|
LWIP_DEBUGF(NETIF_DEBUG, ("XAxiEthernet get_IEEE_phy_speed: Detected PHY with unknown identifier/model.\r\n"));
|
|
}
|
|
#endif
|
|
if (isphy_pcspma(xaxiemacp, phy_addr)) {
|
|
return get_phy_negotiated_speed(xaxiemacp, phy_addr);
|
|
}
|
|
}
|
|
|
|
unsigned configure_IEEE_phy_speed(XAxiEthernet *xaxiemacp, unsigned speed)
|
|
{
|
|
u16 control;
|
|
u32 phy_addr = detect_phy(xaxiemacp);
|
|
u16 phy_val;
|
|
|
|
#ifndef SDT
|
|
if (XAxiEthernet_GetPhysicalInterface(xaxiemacp) ==
|
|
#else
|
|
if (XAxiEthernet_Get_Phy_Interface(xaxiemacp) ==
|
|
#endif
|
|
XAE_PHY_TYPE_RGMII_2_0) {
|
|
/* Setting Tx and Rx Delays for RGMII mode */
|
|
XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_PAGE_ADDRESS_REGISTER, 0x2);
|
|
|
|
XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_CONTROL_REG_MAC, &phy_val);
|
|
phy_val |= IEEE_RGMII_TXRX_CLOCK_DELAYED_MASK;
|
|
XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_CONTROL_REG_MAC, phy_val);
|
|
|
|
XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_PAGE_ADDRESS_REGISTER, 0x0);
|
|
}
|
|
|
|
XAxiEthernet_PhyRead(xaxiemacp, phy_addr,
|
|
IEEE_CONTROL_REG_OFFSET,
|
|
&control);
|
|
control &= ~IEEE_CTRL_LINKSPEED_1000M;
|
|
control &= ~IEEE_CTRL_LINKSPEED_100M;
|
|
control &= ~IEEE_CTRL_LINKSPEED_10M;
|
|
|
|
if (speed == 1000) {
|
|
control |= IEEE_CTRL_LINKSPEED_1000M;
|
|
}
|
|
|
|
else if (speed == 100) {
|
|
control |= IEEE_CTRL_LINKSPEED_100M;
|
|
/* Don't advertise PHY speed of 1000 Mbps */
|
|
XAxiEthernet_PhyWrite(xaxiemacp, phy_addr,
|
|
IEEE_1000_ADVERTISE_REG_OFFSET,
|
|
0);
|
|
/* Don't advertise PHY speed of 10 Mbps */
|
|
XAxiEthernet_PhyWrite(xaxiemacp, phy_addr,
|
|
IEEE_AUTONEGO_ADVERTISE_REG,
|
|
ADVERTISE_100);
|
|
|
|
}
|
|
else if (speed == 10) {
|
|
control |= IEEE_CTRL_LINKSPEED_10M;
|
|
/* Don't advertise PHY speed of 1000 Mbps */
|
|
XAxiEthernet_PhyWrite(xaxiemacp, phy_addr,
|
|
IEEE_1000_ADVERTISE_REG_OFFSET,
|
|
0);
|
|
/* Don't advertise PHY speed of 100 Mbps */
|
|
XAxiEthernet_PhyWrite(xaxiemacp, phy_addr,
|
|
IEEE_AUTONEGO_ADVERTISE_REG,
|
|
ADVERTISE_10);
|
|
}
|
|
|
|
XAxiEthernet_PhyWrite(xaxiemacp, phy_addr,
|
|
IEEE_CONTROL_REG_OFFSET,
|
|
control | IEEE_CTRL_RESET_MASK);
|
|
|
|
#ifndef SDT
|
|
if (XAxiEthernet_GetPhysicalInterface(xaxiemacp) ==
|
|
#else
|
|
if (XAxiEthernet_Get_Phy_Interface(xaxiemacp) ==
|
|
#endif
|
|
XAE_PHY_TYPE_SGMII) {
|
|
control &= (~PHY_R0_ISOLATE);
|
|
XAxiEthernet_PhyWrite(xaxiemacp,
|
|
XPAR_AXIETHERNET_0_PHYADDR,
|
|
IEEE_CONTROL_REG_OFFSET,
|
|
control | IEEE_CTRL_AUTONEGOTIATE_ENABLE);
|
|
}
|
|
|
|
{
|
|
volatile int wait;
|
|
for (wait=0; wait < 100000; wait++);
|
|
for (wait=0; wait < 100000; wait++);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
unsigned phy_setup_axiemac (XAxiEthernet *xaxiemacp)
|
|
{
|
|
unsigned link_speed = 1000;
|
|
|
|
#ifndef SDT
|
|
if (XAxiEthernet_GetPhysicalInterface(xaxiemacp) ==
|
|
#else
|
|
if (XAxiEthernet_Get_Phy_Interface(xaxiemacp) ==
|
|
#endif
|
|
XAE_PHY_TYPE_RGMII_1_3) {
|
|
; /* Add PHY initialization code for RGMII 1.3 */
|
|
#ifndef SDT
|
|
} else if (XAxiEthernet_GetPhysicalInterface(xaxiemacp) ==
|
|
#else
|
|
} else if (XAxiEthernet_Get_Phy_Interface(xaxiemacp) ==
|
|
#endif
|
|
XAE_PHY_TYPE_RGMII_2_0) {
|
|
; /* Add PHY initialization code for RGMII 2.0 */
|
|
#ifndef SDT
|
|
} else if (XAxiEthernet_GetPhysicalInterface(xaxiemacp) ==
|
|
#else
|
|
} else if (XAxiEthernet_Get_Phy_Interface(xaxiemacp) ==
|
|
#endif
|
|
XAE_PHY_TYPE_SGMII) {
|
|
#ifdef CONFIG_LINKSPEED_AUTODETECT
|
|
u32 phy_wr_data = IEEE_CTRL_AUTONEGOTIATE_ENABLE |
|
|
IEEE_CTRL_LINKSPEED_1000M;
|
|
phy_wr_data &= (~PHY_R0_ISOLATE);
|
|
|
|
XAxiEthernet_PhyWrite(xaxiemacp,
|
|
XPAR_AXIETHERNET_0_PHYADDR,
|
|
IEEE_CONTROL_REG_OFFSET,
|
|
phy_wr_data);
|
|
#endif
|
|
#ifndef SDT
|
|
} else if (XAxiEthernet_GetPhysicalInterface(xaxiemacp) ==
|
|
#else
|
|
} else if (XAxiEthernet_Get_Phy_Interface(xaxiemacp) ==
|
|
#endif
|
|
XAE_PHY_TYPE_1000BASE_X) {
|
|
; /* Add PHY initialization code for 1000 Base-X */
|
|
}
|
|
/* set PHY <--> MAC data clock */
|
|
#ifdef CONFIG_LINKSPEED_AUTODETECT
|
|
link_speed = get_IEEE_phy_speed(xaxiemacp);
|
|
xil_printf("auto-negotiated link speed: %d\r\n", link_speed);
|
|
#elif defined(CONFIG_LINKSPEED1000)
|
|
link_speed = 1000;
|
|
configure_IEEE_phy_speed(xaxiemacp, link_speed);
|
|
xil_printf("link speed: %d\r\n", link_speed);
|
|
#elif defined(CONFIG_LINKSPEED100)
|
|
link_speed = 100;
|
|
configure_IEEE_phy_speed(xaxiemacp, link_speed);
|
|
xil_printf("link speed: %d\r\n", link_speed);
|
|
#elif defined(CONFIG_LINKSPEED10)
|
|
link_speed = 10;
|
|
configure_IEEE_phy_speed(xaxiemacp, link_speed);
|
|
xil_printf("link speed: %d\r\n", link_speed);
|
|
#endif
|
|
return link_speed;
|
|
}
|
|
|
|
static void __attribute__ ((noinline)) AxiEthernetUtilPhyDelay(unsigned int Seconds)
|
|
{
|
|
#if defined (__MICROBLAZE__)
|
|
static int WarningFlag = 0;
|
|
|
|
/* If MB caches are disabled or do not exist, this delay loop could
|
|
* take minutes instead of seconds (e.g., 30x longer). Print a warning
|
|
* message for the user (once). If only MB had a built-in timer!
|
|
*/
|
|
if (((mfmsr() & 0x20) == 0) && (!WarningFlag)) {
|
|
WarningFlag = 1;
|
|
}
|
|
|
|
#define ITERS_PER_SEC (XPAR_CPU_CORE_CLOCK_FREQ_HZ / 6)
|
|
__asm volatile ("\n"
|
|
"1: \n\t"
|
|
"addik r7, r0, %0 \n\t"
|
|
"2: \n\t"
|
|
"addik r7, r7, -1 \n\t"
|
|
"bneid r7, 2b \n\t"
|
|
"or r0, r0, r0 \n\t"
|
|
"bneid %1, 1b \n\t"
|
|
"addik %1, %1, -1 \n\t"
|
|
:: "i"(ITERS_PER_SEC), "d" (Seconds));
|
|
#else
|
|
sleep(Seconds);
|
|
#endif
|
|
}
|
|
|
|
void enable_sgmii_clock(XAxiEthernet *xaxiemacp)
|
|
{
|
|
u16 phy_identifier;
|
|
u16 phy_model;
|
|
u8 phytype;
|
|
|
|
XAxiEthernet_PhySetMdioDivisor(xaxiemacp, XAE_MDIO_DIV_DFT);
|
|
u32 phy_addr = detect_phy(xaxiemacp);
|
|
/* Get the PHY Identifier and Model number */
|
|
XAxiEthernet_PhyRead(xaxiemacp, phy_addr, PHY_IDENTIFIER_1_REG, &phy_identifier);
|
|
XAxiEthernet_PhyRead(xaxiemacp, phy_addr, PHY_IDENTIFIER_2_REG, &phy_model);
|
|
|
|
if (phy_identifier == TI_PHY_IDENTIFIER) {
|
|
phy_model = phy_model & TI_PHY_DP83867_MODEL;
|
|
#ifndef SDT
|
|
phytype = XAxiEthernet_GetPhysicalInterface(xaxiemacp);
|
|
#else
|
|
phytype = XAxiEthernet_Get_Phy_Interface(xaxiemacp);
|
|
#endif
|
|
if (phy_model == TI_PHY_DP83867_MODEL && phytype == XAE_PHY_TYPE_SGMII) {
|
|
/* Enable SGMII Clock by switching to 6-wire mode */
|
|
XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, TI_PHY_REGCR,
|
|
TI_PHY_REGCR_DEVAD_EN);
|
|
XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, TI_PHY_ADDDR,
|
|
TI_PHY_SGMIITYPE);
|
|
XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, TI_PHY_REGCR,
|
|
TI_PHY_REGCR_DEVAD_EN | TI_PHY_REGCR_DEVAD_DATAEN);
|
|
XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, TI_PHY_ADDDR,
|
|
TI_PHY_SGMIICLK_EN);
|
|
}
|
|
}
|
|
}
|