Changeset View
Changeset View
Standalone View
Standalone View
head/sys/dev/axgbe/xgbe-mdio.c
/* | /* | ||||
* AMD 10Gb Ethernet driver | * AMD 10Gb Ethernet driver | ||||
* | * | ||||
* Copyright (c) 2014-2016,2020 Advanced Micro Devices, Inc. | |||||
* | |||||
* This file is available to you under your choice of the following two | * This file is available to you under your choice of the following two | ||||
* licenses: | * licenses: | ||||
* | * | ||||
* License 1: GPLv2 | * License 1: GPLv2 | ||||
* | * | ||||
* Copyright (c) 2014-2016 Advanced Micro Devices, Inc. | |||||
* | |||||
* This file is free software; you may copy, redistribute and/or modify | * This file is free software; you may copy, redistribute and/or modify | ||||
* it under the terms of the GNU General Public License as published by | * it under the terms of the GNU General Public License as published by | ||||
* the Free Software Foundation, either version 2 of the License, or (at | * the Free Software Foundation, either version 2 of the License, or (at | ||||
* your option) any later version. | * your option) any later version. | ||||
* | * | ||||
* This file is distributed in the hope that it will be useful, but | * This file is distributed in the hope that it will be useful, but | ||||
* WITHOUT ANY WARRANTY; without even the implied warranty of | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||||
Show All 32 Lines | |||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | ||||
* THE POSSIBILITY OF SUCH DAMAGE. | * THE POSSIBILITY OF SUCH DAMAGE. | ||||
* | * | ||||
* | * | ||||
* License 2: Modified BSD | * License 2: Modified BSD | ||||
* | * | ||||
* Copyright (c) 2014-2016 Advanced Micro Devices, Inc. | |||||
* All rights reserved. | |||||
* | |||||
* Redistribution and use in source and binary forms, with or without | * Redistribution and use in source and binary forms, with or without | ||||
* modification, are permitted provided that the following conditions are met: | * modification, are permitted provided that the following conditions are met: | ||||
* * Redistributions of source code must retain the above copyright | * * Redistributions of source code must retain the above copyright | ||||
* notice, this list of conditions and the following disclaimer. | * notice, this list of conditions and the following disclaimer. | ||||
* * Redistributions in binary form must reproduce the above copyright | * * Redistributions in binary form must reproduce the above copyright | ||||
* notice, this list of conditions and the following disclaimer in the | * notice, this list of conditions and the following disclaimer in the | ||||
* documentation and/or other materials provided with the distribution. | * documentation and/or other materials provided with the distribution. | ||||
* * Neither the name of Advanced Micro Devices, Inc. nor the | * * Neither the name of Advanced Micro Devices, Inc. nor the | ||||
▲ Show 20 Lines • Show All 42 Lines • ▼ Show 20 Lines | |||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | ||||
* THE POSSIBILITY OF SUCH DAMAGE. | * THE POSSIBILITY OF SUCH DAMAGE. | ||||
*/ | */ | ||||
#include <sys/cdefs.h> | #include <sys/cdefs.h> | ||||
__FBSDID("$FreeBSD$"); | __FBSDID("$FreeBSD$"); | ||||
#include <sys/param.h> | |||||
#include <sys/kernel.h> | |||||
#include "xgbe.h" | #include "xgbe.h" | ||||
#include "xgbe-common.h" | #include "xgbe-common.h" | ||||
static void xgbe_an_state_machine(struct xgbe_prv_data *pdata); | static void xgbe_an_state_machine(struct xgbe_prv_data *pdata); | ||||
static void xgbe_an_enable_kr_training(struct xgbe_prv_data *pdata) | static void | ||||
xgbe_an37_clear_interrupts(struct xgbe_prv_data *pdata) | |||||
{ | { | ||||
unsigned int reg; | int reg; | ||||
reg = XMDIO_READ(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL); | reg = XMDIO_READ(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_STAT); | ||||
reg &= ~XGBE_AN_CL37_INT_MASK; | |||||
reg |= XGBE_KR_TRAINING_ENABLE; | XMDIO_WRITE(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_STAT, reg); | ||||
XMDIO_WRITE(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL, reg); | |||||
} | } | ||||
static void xgbe_an_disable_kr_training(struct xgbe_prv_data *pdata) | static void | ||||
xgbe_an37_disable_interrupts(struct xgbe_prv_data *pdata) | |||||
{ | { | ||||
unsigned int reg; | int reg; | ||||
reg = XMDIO_READ(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL); | reg = XMDIO_READ(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_CTRL); | ||||
reg &= ~XGBE_AN_CL37_INT_MASK; | |||||
XMDIO_WRITE(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_CTRL, reg); | |||||
reg &= ~XGBE_KR_TRAINING_ENABLE; | reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_PCS_DIG_CTRL); | ||||
XMDIO_WRITE(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL, reg); | reg &= ~XGBE_PCS_CL37_BP; | ||||
XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_PCS_DIG_CTRL, reg); | |||||
} | } | ||||
static void xgbe_pcs_power_cycle(struct xgbe_prv_data *pdata) | static void | ||||
xgbe_an37_enable_interrupts(struct xgbe_prv_data *pdata) | |||||
{ | { | ||||
unsigned int reg; | int reg; | ||||
reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1); | reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_PCS_DIG_CTRL); | ||||
reg |= XGBE_PCS_CL37_BP; | |||||
XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_PCS_DIG_CTRL, reg); | |||||
reg |= MDIO_CTRL1_LPOWER; | reg = XMDIO_READ(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_CTRL); | ||||
XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, reg); | reg |= XGBE_AN_CL37_INT_MASK; | ||||
XMDIO_WRITE(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_CTRL, reg); | |||||
DELAY(75); | |||||
reg &= ~MDIO_CTRL1_LPOWER; | |||||
XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, reg); | |||||
} | } | ||||
static void xgbe_serdes_start_ratechange(struct xgbe_prv_data *pdata) | static void | ||||
xgbe_an73_clear_interrupts(struct xgbe_prv_data *pdata) | |||||
{ | { | ||||
/* Assert Rx and Tx ratechange */ | XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INT, 0); | ||||
XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, RATECHANGE, 1); | |||||
} | } | ||||
static void xgbe_serdes_complete_ratechange(struct xgbe_prv_data *pdata) | static void | ||||
xgbe_an73_disable_interrupts(struct xgbe_prv_data *pdata) | |||||
{ | { | ||||
unsigned int wait; | XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INTMASK, 0); | ||||
u16 status; | } | ||||
/* Release Rx and Tx ratechange */ | static void | ||||
XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, RATECHANGE, 0); | xgbe_an73_enable_interrupts(struct xgbe_prv_data *pdata) | ||||
{ | |||||
/* Wait for Rx and Tx ready */ | XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INTMASK, XGBE_AN_CL73_INT_MASK); | ||||
wait = XGBE_RATECHANGE_COUNT; | |||||
while (wait--) { | |||||
DELAY(50); | |||||
status = XSIR0_IOREAD(pdata, SIR0_STATUS); | |||||
if (XSIR_GET_BITS(status, SIR0_STATUS, RX_READY) && | |||||
XSIR_GET_BITS(status, SIR0_STATUS, TX_READY)) | |||||
goto rx_reset; | |||||
} | } | ||||
rx_reset: | static void | ||||
/* Perform Rx reset for the DFE changes */ | xgbe_an_enable_interrupts(struct xgbe_prv_data *pdata) | ||||
XRXTX_IOWRITE_BITS(pdata, RXTX_REG6, RESETB_RXD, 0); | { | ||||
XRXTX_IOWRITE_BITS(pdata, RXTX_REG6, RESETB_RXD, 1); | switch (pdata->an_mode) { | ||||
case XGBE_AN_MODE_CL73: | |||||
case XGBE_AN_MODE_CL73_REDRV: | |||||
xgbe_an73_enable_interrupts(pdata); | |||||
break; | |||||
case XGBE_AN_MODE_CL37: | |||||
case XGBE_AN_MODE_CL37_SGMII: | |||||
xgbe_an37_enable_interrupts(pdata); | |||||
break; | |||||
default: | |||||
break; | |||||
} | } | ||||
} | |||||
static void xgbe_xgmii_mode(struct xgbe_prv_data *pdata) | static void | ||||
xgbe_an_clear_interrupts_all(struct xgbe_prv_data *pdata) | |||||
{ | { | ||||
unsigned int reg; | xgbe_an73_clear_interrupts(pdata); | ||||
xgbe_an37_clear_interrupts(pdata); | |||||
} | |||||
/* Enable KR training */ | static void | ||||
xgbe_an_enable_kr_training(pdata); | xgbe_kr_mode(struct xgbe_prv_data *pdata) | ||||
{ | |||||
/* Set MAC to 10G speed */ | /* Set MAC to 10G speed */ | ||||
pdata->hw_if.set_xgmii_speed(pdata); | pdata->hw_if.set_speed(pdata, SPEED_10000); | ||||
/* Set PCS to KR/10G speed */ | /* Call PHY implementation support to complete rate change */ | ||||
reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL2); | pdata->phy_if.phy_impl.set_mode(pdata, XGBE_MODE_KR); | ||||
reg &= ~MDIO_PCS_CTRL2_TYPE; | |||||
reg |= MDIO_PCS_CTRL2_10GBR; | |||||
XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL2, reg); | |||||
reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1); | |||||
reg &= ~MDIO_CTRL1_SPEEDSEL; | |||||
reg |= MDIO_CTRL1_SPEED10G; | |||||
XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, reg); | |||||
xgbe_pcs_power_cycle(pdata); | |||||
/* Set SerDes to 10G speed */ | |||||
xgbe_serdes_start_ratechange(pdata); | |||||
XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, DATARATE, XGBE_SPEED_10000_RATE); | |||||
XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, WORDMODE, XGBE_SPEED_10000_WORD); | |||||
XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, PLLSEL, XGBE_SPEED_10000_PLL); | |||||
XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, CDR_RATE, | |||||
pdata->serdes_cdr_rate[XGBE_SPEED_10000]); | |||||
XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, TXAMP, | |||||
pdata->serdes_tx_amp[XGBE_SPEED_10000]); | |||||
XRXTX_IOWRITE_BITS(pdata, RXTX_REG20, BLWC_ENA, | |||||
pdata->serdes_blwc[XGBE_SPEED_10000]); | |||||
XRXTX_IOWRITE_BITS(pdata, RXTX_REG114, PQ_REG, | |||||
pdata->serdes_pq_skew[XGBE_SPEED_10000]); | |||||
XRXTX_IOWRITE_BITS(pdata, RXTX_REG129, RXDFE_CONFIG, | |||||
pdata->serdes_dfe_tap_cfg[XGBE_SPEED_10000]); | |||||
XRXTX_IOWRITE(pdata, RXTX_REG22, | |||||
pdata->serdes_dfe_tap_ena[XGBE_SPEED_10000]); | |||||
xgbe_serdes_complete_ratechange(pdata); | |||||
} | } | ||||
static void xgbe_gmii_2500_mode(struct xgbe_prv_data *pdata) | static void | ||||
xgbe_kx_2500_mode(struct xgbe_prv_data *pdata) | |||||
{ | { | ||||
unsigned int reg; | |||||
/* Disable KR training */ | |||||
xgbe_an_disable_kr_training(pdata); | |||||
/* Set MAC to 2.5G speed */ | /* Set MAC to 2.5G speed */ | ||||
pdata->hw_if.set_gmii_2500_speed(pdata); | pdata->hw_if.set_speed(pdata, SPEED_2500); | ||||
/* Set PCS to KX/1G speed */ | /* Call PHY implementation support to complete rate change */ | ||||
reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL2); | pdata->phy_if.phy_impl.set_mode(pdata, XGBE_MODE_KX_2500); | ||||
reg &= ~MDIO_PCS_CTRL2_TYPE; | } | ||||
reg |= MDIO_PCS_CTRL2_10GBX; | |||||
XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL2, reg); | |||||
reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1); | static void | ||||
reg &= ~MDIO_CTRL1_SPEEDSEL; | xgbe_kx_1000_mode(struct xgbe_prv_data *pdata) | ||||
reg |= MDIO_CTRL1_SPEED1G; | { | ||||
XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, reg); | /* Set MAC to 1G speed */ | ||||
pdata->hw_if.set_speed(pdata, SPEED_1000); | |||||
xgbe_pcs_power_cycle(pdata); | /* Call PHY implementation support to complete rate change */ | ||||
pdata->phy_if.phy_impl.set_mode(pdata, XGBE_MODE_KX_1000); | |||||
} | |||||
/* Set SerDes to 2.5G speed */ | static void | ||||
xgbe_serdes_start_ratechange(pdata); | xgbe_sfi_mode(struct xgbe_prv_data *pdata) | ||||
{ | |||||
/* If a KR re-driver is present, change to KR mode instead */ | |||||
if (pdata->kr_redrv) | |||||
return (xgbe_kr_mode(pdata)); | |||||
XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, DATARATE, XGBE_SPEED_2500_RATE); | /* Set MAC to 10G speed */ | ||||
XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, WORDMODE, XGBE_SPEED_2500_WORD); | pdata->hw_if.set_speed(pdata, SPEED_10000); | ||||
XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, PLLSEL, XGBE_SPEED_2500_PLL); | |||||
XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, CDR_RATE, | /* Call PHY implementation support to complete rate change */ | ||||
pdata->serdes_cdr_rate[XGBE_SPEED_2500]); | pdata->phy_if.phy_impl.set_mode(pdata, XGBE_MODE_SFI); | ||||
XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, TXAMP, | |||||
pdata->serdes_tx_amp[XGBE_SPEED_2500]); | |||||
XRXTX_IOWRITE_BITS(pdata, RXTX_REG20, BLWC_ENA, | |||||
pdata->serdes_blwc[XGBE_SPEED_2500]); | |||||
XRXTX_IOWRITE_BITS(pdata, RXTX_REG114, PQ_REG, | |||||
pdata->serdes_pq_skew[XGBE_SPEED_2500]); | |||||
XRXTX_IOWRITE_BITS(pdata, RXTX_REG129, RXDFE_CONFIG, | |||||
pdata->serdes_dfe_tap_cfg[XGBE_SPEED_2500]); | |||||
XRXTX_IOWRITE(pdata, RXTX_REG22, | |||||
pdata->serdes_dfe_tap_ena[XGBE_SPEED_2500]); | |||||
xgbe_serdes_complete_ratechange(pdata); | |||||
} | } | ||||
static void xgbe_gmii_mode(struct xgbe_prv_data *pdata) | static void | ||||
xgbe_x_mode(struct xgbe_prv_data *pdata) | |||||
{ | { | ||||
unsigned int reg; | /* Set MAC to 1G speed */ | ||||
pdata->hw_if.set_speed(pdata, SPEED_1000); | |||||
/* Disable KR training */ | /* Call PHY implementation support to complete rate change */ | ||||
xgbe_an_disable_kr_training(pdata); | pdata->phy_if.phy_impl.set_mode(pdata, XGBE_MODE_X); | ||||
} | |||||
static void | |||||
xgbe_sgmii_1000_mode(struct xgbe_prv_data *pdata) | |||||
{ | |||||
/* Set MAC to 1G speed */ | /* Set MAC to 1G speed */ | ||||
pdata->hw_if.set_gmii_speed(pdata); | pdata->hw_if.set_speed(pdata, SPEED_1000); | ||||
/* Set PCS to KX/1G speed */ | /* Call PHY implementation support to complete rate change */ | ||||
reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL2); | pdata->phy_if.phy_impl.set_mode(pdata, XGBE_MODE_SGMII_1000); | ||||
reg &= ~MDIO_PCS_CTRL2_TYPE; | |||||
reg |= MDIO_PCS_CTRL2_10GBX; | |||||
XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL2, reg); | |||||
reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1); | |||||
reg &= ~MDIO_CTRL1_SPEEDSEL; | |||||
reg |= MDIO_CTRL1_SPEED1G; | |||||
XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, reg); | |||||
xgbe_pcs_power_cycle(pdata); | |||||
/* Set SerDes to 1G speed */ | |||||
xgbe_serdes_start_ratechange(pdata); | |||||
XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, DATARATE, XGBE_SPEED_1000_RATE); | |||||
XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, WORDMODE, XGBE_SPEED_1000_WORD); | |||||
XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, PLLSEL, XGBE_SPEED_1000_PLL); | |||||
XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, CDR_RATE, | |||||
pdata->serdes_cdr_rate[XGBE_SPEED_1000]); | |||||
XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, TXAMP, | |||||
pdata->serdes_tx_amp[XGBE_SPEED_1000]); | |||||
XRXTX_IOWRITE_BITS(pdata, RXTX_REG20, BLWC_ENA, | |||||
pdata->serdes_blwc[XGBE_SPEED_1000]); | |||||
XRXTX_IOWRITE_BITS(pdata, RXTX_REG114, PQ_REG, | |||||
pdata->serdes_pq_skew[XGBE_SPEED_1000]); | |||||
XRXTX_IOWRITE_BITS(pdata, RXTX_REG129, RXDFE_CONFIG, | |||||
pdata->serdes_dfe_tap_cfg[XGBE_SPEED_1000]); | |||||
XRXTX_IOWRITE(pdata, RXTX_REG22, | |||||
pdata->serdes_dfe_tap_ena[XGBE_SPEED_1000]); | |||||
xgbe_serdes_complete_ratechange(pdata); | |||||
} | } | ||||
static void xgbe_cur_mode(struct xgbe_prv_data *pdata, | static void | ||||
enum xgbe_mode *mode) | xgbe_sgmii_100_mode(struct xgbe_prv_data *pdata) | ||||
{ | { | ||||
unsigned int reg; | /* Set MAC to 1G speed */ | ||||
pdata->hw_if.set_speed(pdata, SPEED_1000); | |||||
reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL2); | /* Call PHY implementation support to complete rate change */ | ||||
if ((reg & MDIO_PCS_CTRL2_TYPE) == MDIO_PCS_CTRL2_10GBR) | pdata->phy_if.phy_impl.set_mode(pdata, XGBE_MODE_SGMII_100); | ||||
*mode = XGBE_MODE_KR; | |||||
else | |||||
*mode = XGBE_MODE_KX; | |||||
} | } | ||||
static bool xgbe_in_kr_mode(struct xgbe_prv_data *pdata) | static enum xgbe_mode | ||||
xgbe_cur_mode(struct xgbe_prv_data *pdata) | |||||
{ | { | ||||
enum xgbe_mode mode; | return (pdata->phy_if.phy_impl.cur_mode(pdata)); | ||||
} | |||||
xgbe_cur_mode(pdata, &mode); | static bool | ||||
xgbe_in_kr_mode(struct xgbe_prv_data *pdata) | |||||
return (mode == XGBE_MODE_KR); | { | ||||
return (xgbe_cur_mode(pdata) == XGBE_MODE_KR); | |||||
} | } | ||||
static void xgbe_switch_mode(struct xgbe_prv_data *pdata) | static void | ||||
xgbe_change_mode(struct xgbe_prv_data *pdata, enum xgbe_mode mode) | |||||
{ | { | ||||
/* If we are in KR switch to KX, and vice-versa */ | switch (mode) { | ||||
if (xgbe_in_kr_mode(pdata)) { | case XGBE_MODE_KX_1000: | ||||
if (pdata->speed_set == XGBE_SPEEDSET_1000_10000) | xgbe_kx_1000_mode(pdata); | ||||
xgbe_gmii_mode(pdata); | break; | ||||
else | case XGBE_MODE_KX_2500: | ||||
xgbe_gmii_2500_mode(pdata); | xgbe_kx_2500_mode(pdata); | ||||
} else { | break; | ||||
xgbe_xgmii_mode(pdata); | case XGBE_MODE_KR: | ||||
xgbe_kr_mode(pdata); | |||||
break; | |||||
case XGBE_MODE_SGMII_100: | |||||
xgbe_sgmii_100_mode(pdata); | |||||
break; | |||||
case XGBE_MODE_SGMII_1000: | |||||
xgbe_sgmii_1000_mode(pdata); | |||||
break; | |||||
case XGBE_MODE_X: | |||||
xgbe_x_mode(pdata); | |||||
break; | |||||
case XGBE_MODE_SFI: | |||||
xgbe_sfi_mode(pdata); | |||||
break; | |||||
case XGBE_MODE_UNKNOWN: | |||||
break; | |||||
default: | |||||
axgbe_error("invalid operation mode requested (%u)\n", mode); | |||||
} | } | ||||
} | } | ||||
static void xgbe_set_mode(struct xgbe_prv_data *pdata, | static void | ||||
enum xgbe_mode mode) | xgbe_switch_mode(struct xgbe_prv_data *pdata) | ||||
{ | { | ||||
enum xgbe_mode cur_mode; | xgbe_change_mode(pdata, pdata->phy_if.phy_impl.switch_mode(pdata)); | ||||
xgbe_cur_mode(pdata, &cur_mode); | |||||
if (mode != cur_mode) | |||||
xgbe_switch_mode(pdata); | |||||
} | } | ||||
static bool xgbe_use_xgmii_mode(struct xgbe_prv_data *pdata) | static bool | ||||
xgbe_set_mode(struct xgbe_prv_data *pdata, enum xgbe_mode mode) | |||||
{ | { | ||||
if (pdata->phy.autoneg == AUTONEG_ENABLE) { | if (mode == xgbe_cur_mode(pdata)) | ||||
if (pdata->phy.advertising & ADVERTISED_10000baseKR_Full) | return (false); | ||||
return true; | |||||
} else { | |||||
if (pdata->phy.speed == SPEED_10000) | |||||
return true; | |||||
} | |||||
return false; | xgbe_change_mode(pdata, mode); | ||||
return (true); | |||||
} | } | ||||
static bool xgbe_use_gmii_2500_mode(struct xgbe_prv_data *pdata) | static bool | ||||
xgbe_use_mode(struct xgbe_prv_data *pdata, enum xgbe_mode mode) | |||||
{ | { | ||||
if (pdata->phy.autoneg == AUTONEG_ENABLE) { | return (pdata->phy_if.phy_impl.use_mode(pdata, mode)); | ||||
if (pdata->phy.advertising & ADVERTISED_2500baseX_Full) | |||||
return true; | |||||
} else { | |||||
if (pdata->phy.speed == SPEED_2500) | |||||
return true; | |||||
} | } | ||||
return false; | static void | ||||
xgbe_an37_set(struct xgbe_prv_data *pdata, bool enable, bool restart) | |||||
{ | |||||
unsigned int reg; | |||||
reg = XMDIO_READ(pdata, MDIO_MMD_VEND2, MDIO_CTRL1); | |||||
reg &= ~MDIO_VEND2_CTRL1_AN_ENABLE; | |||||
if (enable) | |||||
reg |= MDIO_VEND2_CTRL1_AN_ENABLE; | |||||
if (restart) | |||||
reg |= MDIO_VEND2_CTRL1_AN_RESTART; | |||||
XMDIO_WRITE(pdata, MDIO_MMD_VEND2, MDIO_CTRL1, reg); | |||||
} | } | ||||
static bool xgbe_use_gmii_mode(struct xgbe_prv_data *pdata) | static void | ||||
xgbe_an37_restart(struct xgbe_prv_data *pdata) | |||||
{ | { | ||||
if (pdata->phy.autoneg == AUTONEG_ENABLE) { | xgbe_an37_enable_interrupts(pdata); | ||||
if (pdata->phy.advertising & ADVERTISED_1000baseKX_Full) | xgbe_an37_set(pdata, true, true); | ||||
return true; | |||||
} else { | |||||
if (pdata->phy.speed == SPEED_1000) | |||||
return true; | |||||
} | } | ||||
return false; | static void | ||||
xgbe_an37_disable(struct xgbe_prv_data *pdata) | |||||
{ | |||||
xgbe_an37_set(pdata, false, false); | |||||
xgbe_an37_disable_interrupts(pdata); | |||||
} | } | ||||
static void xgbe_set_an(struct xgbe_prv_data *pdata, bool enable, bool restart) | static void | ||||
xgbe_an73_set(struct xgbe_prv_data *pdata, bool enable, bool restart) | |||||
{ | { | ||||
unsigned int reg; | unsigned int reg; | ||||
/* Disable KR training for now */ | |||||
reg = XMDIO_READ(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL); | |||||
reg &= ~XGBE_KR_TRAINING_ENABLE; | |||||
XMDIO_WRITE(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL, reg); | |||||
/* Update AN settings */ | |||||
reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_CTRL1); | reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_CTRL1); | ||||
reg &= ~MDIO_AN_CTRL1_ENABLE; | reg &= ~MDIO_AN_CTRL1_ENABLE; | ||||
if (enable) | if (enable) | ||||
reg |= MDIO_AN_CTRL1_ENABLE; | reg |= MDIO_AN_CTRL1_ENABLE; | ||||
if (restart) | if (restart) | ||||
reg |= MDIO_AN_CTRL1_RESTART; | reg |= MDIO_AN_CTRL1_RESTART; | ||||
XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_CTRL1, reg); | XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_CTRL1, reg); | ||||
} | } | ||||
static void xgbe_restart_an(struct xgbe_prv_data *pdata) | static void | ||||
xgbe_an73_restart(struct xgbe_prv_data *pdata) | |||||
{ | { | ||||
xgbe_set_an(pdata, true, true); | xgbe_an73_enable_interrupts(pdata); | ||||
xgbe_an73_set(pdata, true, true); | |||||
} | } | ||||
static void xgbe_disable_an(struct xgbe_prv_data *pdata) | static void | ||||
xgbe_an73_disable(struct xgbe_prv_data *pdata) | |||||
{ | { | ||||
xgbe_set_an(pdata, false, false); | xgbe_an73_set(pdata, false, false); | ||||
xgbe_an73_disable_interrupts(pdata); | |||||
pdata->an_start = 0; | |||||
} | } | ||||
static enum xgbe_an xgbe_an_tx_training(struct xgbe_prv_data *pdata, | static void | ||||
enum xgbe_rx *state) | xgbe_an_restart(struct xgbe_prv_data *pdata) | ||||
{ | { | ||||
if (pdata->phy_if.phy_impl.an_pre) | |||||
pdata->phy_if.phy_impl.an_pre(pdata); | |||||
switch (pdata->an_mode) { | |||||
case XGBE_AN_MODE_CL73: | |||||
case XGBE_AN_MODE_CL73_REDRV: | |||||
xgbe_an73_restart(pdata); | |||||
break; | |||||
case XGBE_AN_MODE_CL37: | |||||
case XGBE_AN_MODE_CL37_SGMII: | |||||
xgbe_an37_restart(pdata); | |||||
break; | |||||
default: | |||||
break; | |||||
} | |||||
} | |||||
static void | |||||
xgbe_an_disable(struct xgbe_prv_data *pdata) | |||||
{ | |||||
if (pdata->phy_if.phy_impl.an_post) | |||||
pdata->phy_if.phy_impl.an_post(pdata); | |||||
switch (pdata->an_mode) { | |||||
case XGBE_AN_MODE_CL73: | |||||
case XGBE_AN_MODE_CL73_REDRV: | |||||
xgbe_an73_disable(pdata); | |||||
break; | |||||
case XGBE_AN_MODE_CL37: | |||||
case XGBE_AN_MODE_CL37_SGMII: | |||||
xgbe_an37_disable(pdata); | |||||
break; | |||||
default: | |||||
break; | |||||
} | |||||
} | |||||
static void | |||||
xgbe_an_disable_all(struct xgbe_prv_data *pdata) | |||||
{ | |||||
xgbe_an73_disable(pdata); | |||||
xgbe_an37_disable(pdata); | |||||
} | |||||
static enum xgbe_an | |||||
xgbe_an73_tx_training(struct xgbe_prv_data *pdata, enum xgbe_rx *state) | |||||
{ | |||||
unsigned int ad_reg, lp_reg, reg; | unsigned int ad_reg, lp_reg, reg; | ||||
*state = XGBE_RX_COMPLETE; | *state = XGBE_RX_COMPLETE; | ||||
/* If we're not in KR mode then we're done */ | /* If we're not in KR mode then we're done */ | ||||
if (!xgbe_in_kr_mode(pdata)) | if (!xgbe_in_kr_mode(pdata)) | ||||
return XGBE_AN_PAGE_RECEIVED; | return (XGBE_AN_PAGE_RECEIVED); | ||||
/* Enable/Disable FEC */ | /* Enable/Disable FEC */ | ||||
ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2); | ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2); | ||||
lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA + 2); | lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA + 2); | ||||
reg = XMDIO_READ(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_FECCTRL); | reg = XMDIO_READ(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_FECCTRL); | ||||
reg &= ~(MDIO_PMA_10GBR_FECABLE_ABLE | MDIO_PMA_10GBR_FECABLE_ERRABLE); | reg &= ~(MDIO_PMA_10GBR_FECABLE_ABLE | MDIO_PMA_10GBR_FECABLE_ERRABLE); | ||||
if ((ad_reg & 0xc000) && (lp_reg & 0xc000)) | if ((ad_reg & 0xc000) && (lp_reg & 0xc000)) | ||||
reg |= pdata->fec_ability; | reg |= pdata->fec_ability; | ||||
XMDIO_WRITE(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_FECCTRL, reg); | XMDIO_WRITE(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_FECCTRL, reg); | ||||
/* Start KR training */ | /* Start KR training */ | ||||
reg = XMDIO_READ(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL); | if (pdata->phy_if.phy_impl.kr_training_pre) | ||||
if (reg & XGBE_KR_TRAINING_ENABLE) { | pdata->phy_if.phy_impl.kr_training_pre(pdata); | ||||
XSIR0_IOWRITE_BITS(pdata, SIR0_KR_RT_1, RESET, 1); | |||||
/* Start KR training */ | |||||
reg = XMDIO_READ(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL); | |||||
reg |= XGBE_KR_TRAINING_ENABLE; | |||||
reg |= XGBE_KR_TRAINING_START; | reg |= XGBE_KR_TRAINING_START; | ||||
XMDIO_WRITE(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL, | XMDIO_WRITE(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL, reg); | ||||
reg); | |||||
XSIR0_IOWRITE_BITS(pdata, SIR0_KR_RT_1, RESET, 0); | if (pdata->phy_if.phy_impl.kr_training_post) | ||||
} | pdata->phy_if.phy_impl.kr_training_post(pdata); | ||||
return XGBE_AN_PAGE_RECEIVED; | return (XGBE_AN_PAGE_RECEIVED); | ||||
} | } | ||||
static enum xgbe_an xgbe_an_tx_xnp(struct xgbe_prv_data *pdata, | static enum xgbe_an | ||||
enum xgbe_rx *state) | xgbe_an73_tx_xnp(struct xgbe_prv_data *pdata, enum xgbe_rx *state) | ||||
{ | { | ||||
u16 msg; | uint16_t msg; | ||||
*state = XGBE_RX_XNP; | *state = XGBE_RX_XNP; | ||||
msg = XGBE_XNP_MCF_NULL_MESSAGE; | msg = XGBE_XNP_MCF_NULL_MESSAGE; | ||||
msg |= XGBE_XNP_MP_FORMATTED; | msg |= XGBE_XNP_MP_FORMATTED; | ||||
XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_XNP + 2, 0); | XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_XNP + 2, 0); | ||||
XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_XNP + 1, 0); | XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_XNP + 1, 0); | ||||
XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_XNP, msg); | XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_XNP, msg); | ||||
return XGBE_AN_PAGE_RECEIVED; | return (XGBE_AN_PAGE_RECEIVED); | ||||
} | } | ||||
static enum xgbe_an xgbe_an_rx_bpa(struct xgbe_prv_data *pdata, | static enum xgbe_an | ||||
enum xgbe_rx *state) | xgbe_an73_rx_bpa(struct xgbe_prv_data *pdata, enum xgbe_rx *state) | ||||
{ | { | ||||
unsigned int link_support; | unsigned int link_support; | ||||
unsigned int reg, ad_reg, lp_reg; | unsigned int reg, ad_reg, lp_reg; | ||||
/* Read Base Ability register 2 first */ | /* Read Base Ability register 2 first */ | ||||
reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA + 1); | reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA + 1); | ||||
/* Check for a supported mode, otherwise restart in a different one */ | /* Check for a supported mode, otherwise restart in a different one */ | ||||
link_support = xgbe_in_kr_mode(pdata) ? 0x80 : 0x20; | link_support = xgbe_in_kr_mode(pdata) ? 0x80 : 0x20; | ||||
if (!(reg & link_support)) | if (!(reg & link_support)) | ||||
return XGBE_AN_INCOMPAT_LINK; | return (XGBE_AN_INCOMPAT_LINK); | ||||
/* Check Extended Next Page support */ | /* Check Extended Next Page support */ | ||||
ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE); | ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE); | ||||
lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA); | lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA); | ||||
return ((ad_reg & XGBE_XNP_NP_EXCHANGE) || | return (((ad_reg & XGBE_XNP_NP_EXCHANGE) || | ||||
(lp_reg & XGBE_XNP_NP_EXCHANGE)) | (lp_reg & XGBE_XNP_NP_EXCHANGE)) | ||||
? xgbe_an_tx_xnp(pdata, state) | ? xgbe_an73_tx_xnp(pdata, state) | ||||
: xgbe_an_tx_training(pdata, state); | : xgbe_an73_tx_training(pdata, state)); | ||||
} | } | ||||
static enum xgbe_an xgbe_an_rx_xnp(struct xgbe_prv_data *pdata, | static enum xgbe_an | ||||
enum xgbe_rx *state) | xgbe_an73_rx_xnp(struct xgbe_prv_data *pdata, enum xgbe_rx *state) | ||||
{ | { | ||||
unsigned int ad_reg, lp_reg; | unsigned int ad_reg, lp_reg; | ||||
/* Check Extended Next Page support */ | /* Check Extended Next Page support */ | ||||
ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_XNP); | ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_XNP); | ||||
lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPX); | lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPX); | ||||
return ((ad_reg & XGBE_XNP_NP_EXCHANGE) || | return (((ad_reg & XGBE_XNP_NP_EXCHANGE) || | ||||
(lp_reg & XGBE_XNP_NP_EXCHANGE)) | (lp_reg & XGBE_XNP_NP_EXCHANGE)) | ||||
? xgbe_an_tx_xnp(pdata, state) | ? xgbe_an73_tx_xnp(pdata, state) | ||||
: xgbe_an_tx_training(pdata, state); | : xgbe_an73_tx_training(pdata, state)); | ||||
} | } | ||||
static enum xgbe_an xgbe_an_page_received(struct xgbe_prv_data *pdata) | static enum xgbe_an | ||||
xgbe_an73_page_received(struct xgbe_prv_data *pdata) | |||||
{ | { | ||||
enum xgbe_rx *state; | enum xgbe_rx *state; | ||||
unsigned long an_timeout; | unsigned long an_timeout; | ||||
enum xgbe_an ret; | enum xgbe_an ret; | ||||
if (!pdata->an_start) { | if (!pdata->an_start) { | ||||
pdata->an_start = ticks; | pdata->an_start = ticks; | ||||
} else { | } else { | ||||
an_timeout = pdata->an_start + | an_timeout = pdata->an_start + | ||||
((uint64_t)XGBE_AN_MS_TIMEOUT * (uint64_t)hz) / 1000ull; | ((uint64_t)XGBE_AN_MS_TIMEOUT * (uint64_t)hz) / 1000ull; | ||||
if ((int)(ticks - an_timeout) > 0) { | if ((int)(ticks - an_timeout) > 0) { | ||||
/* Auto-negotiation timed out, reset state */ | /* Auto-negotiation timed out, reset state */ | ||||
pdata->kr_state = XGBE_RX_BPA; | pdata->kr_state = XGBE_RX_BPA; | ||||
pdata->kx_state = XGBE_RX_BPA; | pdata->kx_state = XGBE_RX_BPA; | ||||
pdata->an_start = ticks; | pdata->an_start = ticks; | ||||
axgbe_printf(2, "CL73 AN timed out, resetting state\n"); | |||||
} | } | ||||
} | } | ||||
state = xgbe_in_kr_mode(pdata) ? &pdata->kr_state | state = xgbe_in_kr_mode(pdata) ? &pdata->kr_state : &pdata->kx_state; | ||||
: &pdata->kx_state; | |||||
switch (*state) { | switch (*state) { | ||||
case XGBE_RX_BPA: | case XGBE_RX_BPA: | ||||
ret = xgbe_an_rx_bpa(pdata, state); | ret = xgbe_an73_rx_bpa(pdata, state); | ||||
break; | break; | ||||
case XGBE_RX_XNP: | case XGBE_RX_XNP: | ||||
ret = xgbe_an_rx_xnp(pdata, state); | ret = xgbe_an73_rx_xnp(pdata, state); | ||||
break; | break; | ||||
default: | default: | ||||
ret = XGBE_AN_ERROR; | ret = XGBE_AN_ERROR; | ||||
} | } | ||||
return ret; | return (ret); | ||||
} | } | ||||
static enum xgbe_an xgbe_an_incompat_link(struct xgbe_prv_data *pdata) | static enum xgbe_an | ||||
xgbe_an73_incompat_link(struct xgbe_prv_data *pdata) | |||||
{ | { | ||||
/* Be sure we aren't looping trying to negotiate */ | /* Be sure we aren't looping trying to negotiate */ | ||||
if (xgbe_in_kr_mode(pdata)) { | if (xgbe_in_kr_mode(pdata)) { | ||||
pdata->kr_state = XGBE_RX_ERROR; | pdata->kr_state = XGBE_RX_ERROR; | ||||
if (!(pdata->phy.advertising & ADVERTISED_1000baseKX_Full) && | if (!(XGBE_ADV(&pdata->phy, 1000baseKX_Full)) && | ||||
!(pdata->phy.advertising & ADVERTISED_2500baseX_Full)) | !(XGBE_ADV(&pdata->phy, 2500baseX_Full))) | ||||
return XGBE_AN_NO_LINK; | return (XGBE_AN_NO_LINK); | ||||
if (pdata->kx_state != XGBE_RX_BPA) | if (pdata->kx_state != XGBE_RX_BPA) | ||||
return XGBE_AN_NO_LINK; | return (XGBE_AN_NO_LINK); | ||||
} else { | } else { | ||||
pdata->kx_state = XGBE_RX_ERROR; | pdata->kx_state = XGBE_RX_ERROR; | ||||
if (!(pdata->phy.advertising & ADVERTISED_10000baseKR_Full)) | if (!(XGBE_ADV(&pdata->phy, 10000baseKR_Full))) | ||||
return XGBE_AN_NO_LINK; | return (XGBE_AN_NO_LINK); | ||||
if (pdata->kr_state != XGBE_RX_BPA) | if (pdata->kr_state != XGBE_RX_BPA) | ||||
return XGBE_AN_NO_LINK; | return (XGBE_AN_NO_LINK); | ||||
} | } | ||||
xgbe_disable_an(pdata); | xgbe_an_disable(pdata); | ||||
xgbe_switch_mode(pdata); | xgbe_switch_mode(pdata); | ||||
xgbe_restart_an(pdata); | xgbe_an_restart(pdata); | ||||
return XGBE_AN_INCOMPAT_LINK; | return (XGBE_AN_INCOMPAT_LINK); | ||||
} | } | ||||
static void xgbe_an_isr(void *data) | static void | ||||
xgbe_an37_isr(struct xgbe_prv_data *pdata) | |||||
{ | { | ||||
struct xgbe_prv_data *pdata = (struct xgbe_prv_data *)data; | unsigned int reg; | ||||
/* Disable AN interrupts */ | /* Disable AN interrupts */ | ||||
XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INTMASK, 0); | xgbe_an37_disable_interrupts(pdata); | ||||
/* Save the interrupt(s) that fired */ | /* Save the interrupt(s) that fired */ | ||||
reg = XMDIO_READ(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_STAT); | |||||
pdata->an_int = reg & XGBE_AN_CL37_INT_MASK; | |||||
pdata->an_status = reg & ~XGBE_AN_CL37_INT_MASK; | |||||
if (pdata->an_int) { | |||||
/* Clear the interrupt(s) that fired and process them */ | |||||
reg &= ~XGBE_AN_CL37_INT_MASK; | |||||
XMDIO_WRITE(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_STAT, reg); | |||||
xgbe_an_state_machine(pdata); | |||||
} else { | |||||
/* Enable AN interrupts */ | |||||
xgbe_an37_enable_interrupts(pdata); | |||||
/* Reissue interrupt if status is not clear */ | |||||
if (pdata->vdata->irq_reissue_support) | |||||
XP_IOWRITE(pdata, XP_INT_REISSUE_EN, 1 << 3); | |||||
} | |||||
} | |||||
static void | |||||
xgbe_an73_isr(struct xgbe_prv_data *pdata) | |||||
{ | |||||
/* Disable AN interrupts */ | |||||
xgbe_an73_disable_interrupts(pdata); | |||||
/* Save the interrupt(s) that fired */ | |||||
pdata->an_int = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_INT); | pdata->an_int = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_INT); | ||||
if (pdata->an_int) { | if (pdata->an_int) { | ||||
/* Clear the interrupt(s) that fired and process them */ | /* Clear the interrupt(s) that fired and process them */ | ||||
XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INT, ~pdata->an_int); | XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INT, ~pdata->an_int); | ||||
xgbe_an_state_machine(pdata); | xgbe_an_state_machine(pdata); | ||||
} else { | } else { | ||||
/* Enable AN interrupts */ | /* Enable AN interrupts */ | ||||
XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INTMASK, | xgbe_an73_enable_interrupts(pdata); | ||||
XGBE_AN_INT_MASK); | |||||
/* Reissue interrupt if status is not clear */ | |||||
if (pdata->vdata->irq_reissue_support) | |||||
XP_IOWRITE(pdata, XP_INT_REISSUE_EN, 1 << 3); | |||||
} | } | ||||
} | } | ||||
static void xgbe_an_state_machine(struct xgbe_prv_data *pdata) | static void | ||||
xgbe_an_isr_task(unsigned long data) | |||||
{ | { | ||||
struct xgbe_prv_data *pdata = (struct xgbe_prv_data *)data; | |||||
axgbe_printf(2, "AN interrupt received\n"); | |||||
switch (pdata->an_mode) { | |||||
case XGBE_AN_MODE_CL73: | |||||
case XGBE_AN_MODE_CL73_REDRV: | |||||
xgbe_an73_isr(pdata); | |||||
break; | |||||
case XGBE_AN_MODE_CL37: | |||||
case XGBE_AN_MODE_CL37_SGMII: | |||||
xgbe_an37_isr(pdata); | |||||
break; | |||||
default: | |||||
break; | |||||
} | |||||
} | |||||
static void | |||||
xgbe_an_combined_isr(struct xgbe_prv_data *pdata) | |||||
{ | |||||
xgbe_an_isr_task((unsigned long)pdata); | |||||
} | |||||
static const char * | |||||
xgbe_state_as_string(enum xgbe_an state) | |||||
{ | |||||
switch (state) { | |||||
case XGBE_AN_READY: | |||||
return ("Ready"); | |||||
case XGBE_AN_PAGE_RECEIVED: | |||||
return ("Page-Received"); | |||||
case XGBE_AN_INCOMPAT_LINK: | |||||
return ("Incompatible-Link"); | |||||
case XGBE_AN_COMPLETE: | |||||
return ("Complete"); | |||||
case XGBE_AN_NO_LINK: | |||||
return ("No-Link"); | |||||
case XGBE_AN_ERROR: | |||||
return ("Error"); | |||||
default: | |||||
return ("Undefined"); | |||||
} | |||||
} | |||||
static void | |||||
xgbe_an37_state_machine(struct xgbe_prv_data *pdata) | |||||
{ | |||||
enum xgbe_an cur_state = pdata->an_state; | enum xgbe_an cur_state = pdata->an_state; | ||||
sx_xlock(&pdata->an_mutex); | if (!pdata->an_int) | ||||
return; | |||||
if (pdata->an_int & XGBE_AN_CL37_INT_CMPLT) { | |||||
pdata->an_state = XGBE_AN_COMPLETE; | |||||
pdata->an_int &= ~XGBE_AN_CL37_INT_CMPLT; | |||||
/* If SGMII is enabled, check the link status */ | |||||
if ((pdata->an_mode == XGBE_AN_MODE_CL37_SGMII) && | |||||
!(pdata->an_status & XGBE_SGMII_AN_LINK_STATUS)) | |||||
pdata->an_state = XGBE_AN_NO_LINK; | |||||
} | |||||
axgbe_printf(2, "%s: CL37 AN %s\n", __func__, | |||||
xgbe_state_as_string(pdata->an_state)); | |||||
cur_state = pdata->an_state; | |||||
switch (pdata->an_state) { | |||||
case XGBE_AN_READY: | |||||
break; | |||||
case XGBE_AN_COMPLETE: | |||||
axgbe_printf(2, "Auto negotiation successful\n"); | |||||
break; | |||||
case XGBE_AN_NO_LINK: | |||||
break; | |||||
default: | |||||
pdata->an_state = XGBE_AN_ERROR; | |||||
} | |||||
if (pdata->an_state == XGBE_AN_ERROR) { | |||||
axgbe_printf(2, "error during auto-negotiation, state=%u\n", | |||||
cur_state); | |||||
pdata->an_int = 0; | |||||
xgbe_an37_clear_interrupts(pdata); | |||||
} | |||||
if (pdata->an_state >= XGBE_AN_COMPLETE) { | |||||
pdata->an_result = pdata->an_state; | |||||
pdata->an_state = XGBE_AN_READY; | |||||
if (pdata->phy_if.phy_impl.an_post) | |||||
pdata->phy_if.phy_impl.an_post(pdata); | |||||
axgbe_printf(2, "CL37 AN result: %s\n", | |||||
xgbe_state_as_string(pdata->an_result)); | |||||
} | |||||
axgbe_printf(2, "%s: an_state %d an_int %d an_mode %d an_status %d\n", | |||||
__func__, pdata->an_state, pdata->an_int, pdata->an_mode, | |||||
pdata->an_status); | |||||
xgbe_an37_enable_interrupts(pdata); | |||||
} | |||||
static void | |||||
xgbe_an73_state_machine(struct xgbe_prv_data *pdata) | |||||
{ | |||||
enum xgbe_an cur_state = pdata->an_state; | |||||
if (!pdata->an_int) | if (!pdata->an_int) | ||||
goto out; | goto out; | ||||
next_int: | next_int: | ||||
if (pdata->an_int & XGBE_AN_PG_RCV) { | if (pdata->an_int & XGBE_AN_CL73_PG_RCV) { | ||||
pdata->an_state = XGBE_AN_PAGE_RECEIVED; | pdata->an_state = XGBE_AN_PAGE_RECEIVED; | ||||
pdata->an_int &= ~XGBE_AN_PG_RCV; | pdata->an_int &= ~XGBE_AN_CL73_PG_RCV; | ||||
} else if (pdata->an_int & XGBE_AN_INC_LINK) { | } else if (pdata->an_int & XGBE_AN_CL73_INC_LINK) { | ||||
pdata->an_state = XGBE_AN_INCOMPAT_LINK; | pdata->an_state = XGBE_AN_INCOMPAT_LINK; | ||||
pdata->an_int &= ~XGBE_AN_INC_LINK; | pdata->an_int &= ~XGBE_AN_CL73_INC_LINK; | ||||
} else if (pdata->an_int & XGBE_AN_INT_CMPLT) { | } else if (pdata->an_int & XGBE_AN_CL73_INT_CMPLT) { | ||||
pdata->an_state = XGBE_AN_COMPLETE; | pdata->an_state = XGBE_AN_COMPLETE; | ||||
pdata->an_int &= ~XGBE_AN_INT_CMPLT; | pdata->an_int &= ~XGBE_AN_CL73_INT_CMPLT; | ||||
} else { | } else { | ||||
pdata->an_state = XGBE_AN_ERROR; | pdata->an_state = XGBE_AN_ERROR; | ||||
} | } | ||||
pdata->an_result = pdata->an_state; | |||||
again: | again: | ||||
axgbe_printf(2, "CL73 AN %s\n", | |||||
xgbe_state_as_string(pdata->an_state)); | |||||
cur_state = pdata->an_state; | cur_state = pdata->an_state; | ||||
switch (pdata->an_state) { | switch (pdata->an_state) { | ||||
case XGBE_AN_READY: | case XGBE_AN_READY: | ||||
pdata->an_supported = 0; | pdata->an_supported = 0; | ||||
break; | break; | ||||
case XGBE_AN_PAGE_RECEIVED: | case XGBE_AN_PAGE_RECEIVED: | ||||
pdata->an_state = xgbe_an_page_received(pdata); | pdata->an_state = xgbe_an73_page_received(pdata); | ||||
pdata->an_supported++; | pdata->an_supported++; | ||||
break; | break; | ||||
case XGBE_AN_INCOMPAT_LINK: | case XGBE_AN_INCOMPAT_LINK: | ||||
pdata->an_supported = 0; | pdata->an_supported = 0; | ||||
pdata->parallel_detect = 0; | pdata->parallel_detect = 0; | ||||
pdata->an_state = xgbe_an_incompat_link(pdata); | pdata->an_state = xgbe_an73_incompat_link(pdata); | ||||
break; | break; | ||||
case XGBE_AN_COMPLETE: | case XGBE_AN_COMPLETE: | ||||
pdata->parallel_detect = pdata->an_supported ? 0 : 1; | pdata->parallel_detect = pdata->an_supported ? 0 : 1; | ||||
axgbe_printf(2, "%s successful\n", | |||||
pdata->an_supported ? "Auto negotiation" | |||||
: "Parallel detection"); | |||||
break; | break; | ||||
case XGBE_AN_NO_LINK: | case XGBE_AN_NO_LINK: | ||||
break; | break; | ||||
default: | default: | ||||
pdata->an_state = XGBE_AN_ERROR; | pdata->an_state = XGBE_AN_ERROR; | ||||
} | } | ||||
if (pdata->an_state == XGBE_AN_NO_LINK) { | if (pdata->an_state == XGBE_AN_NO_LINK) { | ||||
pdata->an_int = 0; | pdata->an_int = 0; | ||||
XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INT, 0); | xgbe_an73_clear_interrupts(pdata); | ||||
} else if (pdata->an_state == XGBE_AN_ERROR) { | } else if (pdata->an_state == XGBE_AN_ERROR) { | ||||
axgbe_printf(2, | |||||
"error during auto-negotiation, state=%u\n", | |||||
cur_state); | |||||
pdata->an_int = 0; | pdata->an_int = 0; | ||||
XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INT, 0); | xgbe_an73_clear_interrupts(pdata); | ||||
} | } | ||||
if (pdata->an_state >= XGBE_AN_COMPLETE) { | if (pdata->an_state >= XGBE_AN_COMPLETE) { | ||||
pdata->an_result = pdata->an_state; | pdata->an_result = pdata->an_state; | ||||
pdata->an_state = XGBE_AN_READY; | pdata->an_state = XGBE_AN_READY; | ||||
pdata->kr_state = XGBE_RX_BPA; | pdata->kr_state = XGBE_RX_BPA; | ||||
pdata->kx_state = XGBE_RX_BPA; | pdata->kx_state = XGBE_RX_BPA; | ||||
pdata->an_start = 0; | pdata->an_start = 0; | ||||
if (pdata->phy_if.phy_impl.an_post) | |||||
pdata->phy_if.phy_impl.an_post(pdata); | |||||
axgbe_printf(2, "CL73 AN result: %s\n", | |||||
xgbe_state_as_string(pdata->an_result)); | |||||
} | } | ||||
if (cur_state != pdata->an_state) | if (cur_state != pdata->an_state) | ||||
goto again; | goto again; | ||||
if (pdata->an_int) | if (pdata->an_int) | ||||
goto next_int; | goto next_int; | ||||
out: | out: | ||||
/* Enable AN interrupts on the way out */ | /* Enable AN interrupts on the way out */ | ||||
XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INTMASK, XGBE_AN_INT_MASK); | xgbe_an73_enable_interrupts(pdata); | ||||
} | |||||
static void | |||||
xgbe_an_state_machine(struct xgbe_prv_data *pdata) | |||||
{ | |||||
sx_xlock(&pdata->an_mutex); | |||||
switch (pdata->an_mode) { | |||||
case XGBE_AN_MODE_CL73: | |||||
case XGBE_AN_MODE_CL73_REDRV: | |||||
xgbe_an73_state_machine(pdata); | |||||
break; | |||||
case XGBE_AN_MODE_CL37: | |||||
case XGBE_AN_MODE_CL37_SGMII: | |||||
xgbe_an37_state_machine(pdata); | |||||
break; | |||||
default: | |||||
break; | |||||
} | |||||
/* Reissue interrupt if status is not clear */ | |||||
if (pdata->vdata->irq_reissue_support) | |||||
XP_IOWRITE(pdata, XP_INT_REISSUE_EN, 1 << 3); | |||||
sx_xunlock(&pdata->an_mutex); | sx_xunlock(&pdata->an_mutex); | ||||
} | } | ||||
static void xgbe_an_init(struct xgbe_prv_data *pdata) | static void | ||||
xgbe_an37_init(struct xgbe_prv_data *pdata) | |||||
{ | { | ||||
struct xgbe_phy local_phy; | |||||
unsigned int reg; | unsigned int reg; | ||||
pdata->phy_if.phy_impl.an_advertising(pdata, &local_phy); | |||||
axgbe_printf(2, "%s: advertising 0x%x\n", __func__, local_phy.advertising); | |||||
/* Set up Advertisement register */ | |||||
reg = XMDIO_READ(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_ADVERTISE); | |||||
if (XGBE_ADV(&local_phy, Pause)) | |||||
reg |= 0x100; | |||||
else | |||||
reg &= ~0x100; | |||||
if (XGBE_ADV(&local_phy, Asym_Pause)) | |||||
reg |= 0x80; | |||||
else | |||||
reg &= ~0x80; | |||||
/* Full duplex, but not half */ | |||||
reg |= XGBE_AN_CL37_FD_MASK; | |||||
reg &= ~XGBE_AN_CL37_HD_MASK; | |||||
axgbe_printf(2, "%s: Writing reg: 0x%x\n", __func__, reg); | |||||
XMDIO_WRITE(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_ADVERTISE, reg); | |||||
/* Set up the Control register */ | |||||
reg = XMDIO_READ(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_CTRL); | |||||
axgbe_printf(2, "%s: AN_ADVERTISE reg 0x%x an_mode %d\n", __func__, | |||||
reg, pdata->an_mode); | |||||
reg &= ~XGBE_AN_CL37_TX_CONFIG_MASK; | |||||
reg &= ~XGBE_AN_CL37_PCS_MODE_MASK; | |||||
switch (pdata->an_mode) { | |||||
case XGBE_AN_MODE_CL37: | |||||
reg |= XGBE_AN_CL37_PCS_MODE_BASEX; | |||||
break; | |||||
case XGBE_AN_MODE_CL37_SGMII: | |||||
reg |= XGBE_AN_CL37_PCS_MODE_SGMII; | |||||
break; | |||||
default: | |||||
break; | |||||
} | |||||
reg |= XGBE_AN_CL37_MII_CTRL_8BIT; | |||||
axgbe_printf(2, "%s: Writing reg: 0x%x\n", __func__, reg); | |||||
XMDIO_WRITE(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_CTRL, reg); | |||||
axgbe_printf(2, "CL37 AN (%s) initialized\n", | |||||
(pdata->an_mode == XGBE_AN_MODE_CL37) ? "BaseX" : "SGMII"); | |||||
} | |||||
static void | |||||
xgbe_an73_init(struct xgbe_prv_data *pdata) | |||||
{ | |||||
/* | |||||
* This local_phy is needed because phy-v2 alters the | |||||
* advertising flag variable. so phy-v1 an_advertising is just copying | |||||
*/ | |||||
struct xgbe_phy local_phy; | |||||
unsigned int reg; | |||||
pdata->phy_if.phy_impl.an_advertising(pdata, &local_phy); | |||||
/* Set up Advertisement register 3 first */ | /* Set up Advertisement register 3 first */ | ||||
reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2); | reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2); | ||||
if (XGBE_ADV(&local_phy, 10000baseR_FEC)) | |||||
reg |= 0xc000; | |||||
else | |||||
reg &= ~0xc000; | reg &= ~0xc000; | ||||
XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2, reg); | XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2, reg); | ||||
/* Set up Advertisement register 2 next */ | /* Set up Advertisement register 2 next */ | ||||
reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1); | reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1); | ||||
if (pdata->phy.advertising & ADVERTISED_10000baseKR_Full) | if (XGBE_ADV(&local_phy, 10000baseKR_Full)) | ||||
reg |= 0x80; | reg |= 0x80; | ||||
else | else | ||||
reg &= ~0x80; | reg &= ~0x80; | ||||
if ((pdata->phy.advertising & ADVERTISED_1000baseKX_Full) || | if (XGBE_ADV(&local_phy, 1000baseKX_Full) || | ||||
(pdata->phy.advertising & ADVERTISED_2500baseX_Full)) | XGBE_ADV(&local_phy, 2500baseX_Full)) | ||||
reg |= 0x20; | reg |= 0x20; | ||||
else | else | ||||
reg &= ~0x20; | reg &= ~0x20; | ||||
XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1, reg); | XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1, reg); | ||||
/* Set up Advertisement register 1 last */ | /* Set up Advertisement register 1 last */ | ||||
reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE); | reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE); | ||||
if (pdata->phy.advertising & ADVERTISED_Pause) | if (XGBE_ADV(&local_phy, Pause)) | ||||
reg |= 0x400; | reg |= 0x400; | ||||
else | else | ||||
reg &= ~0x400; | reg &= ~0x400; | ||||
if (pdata->phy.advertising & ADVERTISED_Asym_Pause) | if (XGBE_ADV(&local_phy, Asym_Pause)) | ||||
reg |= 0x800; | reg |= 0x800; | ||||
else | else | ||||
reg &= ~0x800; | reg &= ~0x800; | ||||
/* We don't intend to perform XNP */ | /* We don't intend to perform XNP */ | ||||
reg &= ~XGBE_XNP_NP_EXCHANGE; | reg &= ~XGBE_XNP_NP_EXCHANGE; | ||||
XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE, reg); | XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE, reg); | ||||
axgbe_printf(2, "CL73 AN initialized\n"); | |||||
} | } | ||||
static void xgbe_phy_adjust_link(struct xgbe_prv_data *pdata) | static void | ||||
xgbe_an_init(struct xgbe_prv_data *pdata) | |||||
{ | { | ||||
/* Set up advertisement registers based on current settings */ | |||||
pdata->an_mode = pdata->phy_if.phy_impl.an_mode(pdata); | |||||
axgbe_printf(2, "%s: setting up an_mode %d\n", __func__, pdata->an_mode); | |||||
switch (pdata->an_mode) { | |||||
case XGBE_AN_MODE_CL73: | |||||
case XGBE_AN_MODE_CL73_REDRV: | |||||
xgbe_an73_init(pdata); | |||||
break; | |||||
case XGBE_AN_MODE_CL37: | |||||
case XGBE_AN_MODE_CL37_SGMII: | |||||
xgbe_an37_init(pdata); | |||||
break; | |||||
default: | |||||
break; | |||||
} | |||||
} | |||||
static const char * | |||||
xgbe_phy_fc_string(struct xgbe_prv_data *pdata) | |||||
{ | |||||
if (pdata->tx_pause && pdata->rx_pause) | |||||
return ("rx/tx"); | |||||
else if (pdata->rx_pause) | |||||
return ("rx"); | |||||
else if (pdata->tx_pause) | |||||
return ("tx"); | |||||
else | |||||
return ("off"); | |||||
} | |||||
static const char * | |||||
xgbe_phy_speed_string(int speed) | |||||
{ | |||||
switch (speed) { | |||||
case SPEED_100: | |||||
return ("100Mbps"); | |||||
case SPEED_1000: | |||||
return ("1Gbps"); | |||||
case SPEED_2500: | |||||
return ("2.5Gbps"); | |||||
case SPEED_10000: | |||||
return ("10Gbps"); | |||||
case SPEED_UNKNOWN: | |||||
return ("Unknown"); | |||||
default: | |||||
return ("Unsupported"); | |||||
} | |||||
} | |||||
static void | |||||
xgbe_phy_print_status(struct xgbe_prv_data *pdata) | |||||
{ | |||||
if (pdata->phy.link) | |||||
axgbe_printf(0, | |||||
"Link is UP - %s/%s - flow control %s\n", | |||||
xgbe_phy_speed_string(pdata->phy.speed), | |||||
pdata->phy.duplex == DUPLEX_FULL ? "Full" : "Half", | |||||
xgbe_phy_fc_string(pdata)); | |||||
else | |||||
axgbe_printf(0, "Link is DOWN\n"); | |||||
} | |||||
static void | |||||
xgbe_phy_adjust_link(struct xgbe_prv_data *pdata) | |||||
{ | |||||
int new_state = 0; | |||||
axgbe_printf(1, "link %d/%d tx %d/%d rx %d/%d speed %d/%d autoneg %d/%d\n", | |||||
pdata->phy_link, pdata->phy.link, | |||||
pdata->tx_pause, pdata->phy.tx_pause, | |||||
pdata->rx_pause, pdata->phy.rx_pause, | |||||
pdata->phy_speed, pdata->phy.speed, | |||||
pdata->pause_autoneg, pdata->phy.pause_autoneg); | |||||
if (pdata->phy.link) { | if (pdata->phy.link) { | ||||
/* Flow control support */ | /* Flow control support */ | ||||
pdata->pause_autoneg = pdata->phy.pause_autoneg; | pdata->pause_autoneg = pdata->phy.pause_autoneg; | ||||
if (pdata->tx_pause != pdata->phy.tx_pause) { | if (pdata->tx_pause != pdata->phy.tx_pause) { | ||||
pdata->hw_if.config_tx_flow_control(pdata); | new_state = 1; | ||||
axgbe_printf(2, "tx pause %d/%d\n", pdata->tx_pause, | |||||
pdata->phy.tx_pause); | |||||
pdata->tx_pause = pdata->phy.tx_pause; | pdata->tx_pause = pdata->phy.tx_pause; | ||||
pdata->hw_if.config_tx_flow_control(pdata); | |||||
} | } | ||||
if (pdata->rx_pause != pdata->phy.rx_pause) { | if (pdata->rx_pause != pdata->phy.rx_pause) { | ||||
pdata->hw_if.config_rx_flow_control(pdata); | new_state = 1; | ||||
axgbe_printf(2, "rx pause %d/%d\n", pdata->rx_pause, | |||||
pdata->phy.rx_pause); | |||||
pdata->rx_pause = pdata->phy.rx_pause; | pdata->rx_pause = pdata->phy.rx_pause; | ||||
pdata->hw_if.config_rx_flow_control(pdata); | |||||
} | } | ||||
/* Speed support */ | /* Speed support */ | ||||
if (pdata->phy_speed != pdata->phy.speed) { | if (pdata->phy_speed != pdata->phy.speed) { | ||||
new_state = 1; | |||||
pdata->phy_speed = pdata->phy.speed; | pdata->phy_speed = pdata->phy.speed; | ||||
} | } | ||||
if (pdata->phy_link != pdata->phy.link) { | if (pdata->phy_link != pdata->phy.link) { | ||||
new_state = 1; | |||||
pdata->phy_link = pdata->phy.link; | pdata->phy_link = pdata->phy.link; | ||||
} | } | ||||
} else if (pdata->phy_link) { | } else if (pdata->phy_link) { | ||||
new_state = 1; | |||||
pdata->phy_link = 0; | pdata->phy_link = 0; | ||||
pdata->phy_speed = SPEED_UNKNOWN; | pdata->phy_speed = SPEED_UNKNOWN; | ||||
} | } | ||||
axgbe_printf(2, "phy_link %d Link %d new_state %d\n", pdata->phy_link, | |||||
pdata->phy.link, new_state); | |||||
if (new_state) | |||||
xgbe_phy_print_status(pdata); | |||||
} | } | ||||
static int xgbe_phy_config_fixed(struct xgbe_prv_data *pdata) | static bool | ||||
xgbe_phy_valid_speed(struct xgbe_prv_data *pdata, int speed) | |||||
{ | { | ||||
return (pdata->phy_if.phy_impl.valid_speed(pdata, speed)); | |||||
} | |||||
/* Disable auto-negotiation */ | static int | ||||
xgbe_disable_an(pdata); | xgbe_phy_config_fixed(struct xgbe_prv_data *pdata) | ||||
{ | |||||
enum xgbe_mode mode; | |||||
/* Validate/Set specified speed */ | axgbe_printf(2, "fixed PHY configuration\n"); | ||||
switch (pdata->phy.speed) { | |||||
case SPEED_10000: | |||||
xgbe_set_mode(pdata, XGBE_MODE_KR); | |||||
break; | |||||
case SPEED_2500: | /* Disable auto-negotiation */ | ||||
case SPEED_1000: | xgbe_an_disable(pdata); | ||||
xgbe_set_mode(pdata, XGBE_MODE_KX); | |||||
break; | |||||
/* Set specified mode for specified speed */ | |||||
mode = pdata->phy_if.phy_impl.get_mode(pdata, pdata->phy.speed); | |||||
switch (mode) { | |||||
case XGBE_MODE_KX_1000: | |||||
case XGBE_MODE_KX_2500: | |||||
case XGBE_MODE_KR: | |||||
case XGBE_MODE_SGMII_100: | |||||
case XGBE_MODE_SGMII_1000: | |||||
case XGBE_MODE_X: | |||||
case XGBE_MODE_SFI: | |||||
break; | |||||
case XGBE_MODE_UNKNOWN: | |||||
default: | default: | ||||
return -EINVAL; | return (-EINVAL); | ||||
} | } | ||||
/* Validate duplex mode */ | /* Validate duplex mode */ | ||||
if (pdata->phy.duplex != DUPLEX_FULL) | if (pdata->phy.duplex != DUPLEX_FULL) | ||||
return -EINVAL; | return (-EINVAL); | ||||
return 0; | xgbe_set_mode(pdata, mode); | ||||
return (0); | |||||
} | } | ||||
static int __xgbe_phy_config_aneg(struct xgbe_prv_data *pdata) | static int | ||||
__xgbe_phy_config_aneg(struct xgbe_prv_data *pdata, bool set_mode) | |||||
{ | { | ||||
int ret; | |||||
unsigned int reg; | |||||
sx_xlock(&pdata->an_mutex); | |||||
set_bit(XGBE_LINK_INIT, &pdata->dev_state); | set_bit(XGBE_LINK_INIT, &pdata->dev_state); | ||||
pdata->link_check = ticks; | pdata->link_check = ticks; | ||||
if (pdata->phy.autoneg != AUTONEG_ENABLE) | ret = pdata->phy_if.phy_impl.an_config(pdata); | ||||
return xgbe_phy_config_fixed(pdata); | if (ret) { | ||||
axgbe_error("%s: an_config fail %d\n", __func__, ret); | |||||
goto out; | |||||
} | |||||
if (pdata->phy.autoneg != AUTONEG_ENABLE) { | |||||
ret = xgbe_phy_config_fixed(pdata); | |||||
if (ret || !pdata->kr_redrv) { | |||||
if (ret) | |||||
axgbe_error("%s: fix conf fail %d\n", __func__, ret); | |||||
goto out; | |||||
} | |||||
axgbe_printf(2, "AN redriver support\n"); | |||||
} else | |||||
axgbe_printf(2, "AN PHY configuration\n"); | |||||
/* Disable auto-negotiation interrupt */ | /* Disable auto-negotiation interrupt */ | ||||
XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INTMASK, 0); | XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INTMASK, 0); | ||||
reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_INTMASK); | |||||
axgbe_printf(2, "%s: set_mode %d AN int reg value 0x%x\n", __func__, | |||||
set_mode, reg); | |||||
/* Clear any auto-negotitation interrupts */ | /* Clear any auto-negotitation interrupts */ | ||||
XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INT, 0); | XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INT, 0); | ||||
/* Start auto-negotiation in a supported mode */ | /* Start auto-negotiation in a supported mode */ | ||||
if (pdata->phy.advertising & ADVERTISED_10000baseKR_Full) { | if (set_mode) { | ||||
/* Start auto-negotiation in a supported mode */ | |||||
if (xgbe_use_mode(pdata, XGBE_MODE_KR)) { | |||||
xgbe_set_mode(pdata, XGBE_MODE_KR); | xgbe_set_mode(pdata, XGBE_MODE_KR); | ||||
} else if ((pdata->phy.advertising & ADVERTISED_1000baseKX_Full) || | } else if (xgbe_use_mode(pdata, XGBE_MODE_KX_2500)) { | ||||
(pdata->phy.advertising & ADVERTISED_2500baseX_Full)) { | xgbe_set_mode(pdata, XGBE_MODE_KX_2500); | ||||
xgbe_set_mode(pdata, XGBE_MODE_KX); | } else if (xgbe_use_mode(pdata, XGBE_MODE_KX_1000)) { | ||||
xgbe_set_mode(pdata, XGBE_MODE_KX_1000); | |||||
} else if (xgbe_use_mode(pdata, XGBE_MODE_SFI)) { | |||||
xgbe_set_mode(pdata, XGBE_MODE_SFI); | |||||
} else if (xgbe_use_mode(pdata, XGBE_MODE_X)) { | |||||
xgbe_set_mode(pdata, XGBE_MODE_X); | |||||
} else if (xgbe_use_mode(pdata, XGBE_MODE_SGMII_1000)) { | |||||
xgbe_set_mode(pdata, XGBE_MODE_SGMII_1000); | |||||
} else if (xgbe_use_mode(pdata, XGBE_MODE_SGMII_100)) { | |||||
xgbe_set_mode(pdata, XGBE_MODE_SGMII_100); | |||||
} else { | } else { | ||||
XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INTMASK, 0x07); | XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INTMASK, 0x07); | ||||
return -EINVAL; | ret = -EINVAL; | ||||
goto out; | |||||
} | } | ||||
} | |||||
/* Disable and stop any in progress auto-negotiation */ | /* Disable and stop any in progress auto-negotiation */ | ||||
xgbe_disable_an(pdata); | xgbe_an_disable_all(pdata); | ||||
/* Clear any auto-negotitation interrupts */ | /* Clear any auto-negotitation interrupts */ | ||||
XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INT, 0); | xgbe_an_clear_interrupts_all(pdata); | ||||
pdata->an_result = XGBE_AN_READY; | pdata->an_result = XGBE_AN_READY; | ||||
pdata->an_state = XGBE_AN_READY; | pdata->an_state = XGBE_AN_READY; | ||||
pdata->kr_state = XGBE_RX_BPA; | pdata->kr_state = XGBE_RX_BPA; | ||||
pdata->kx_state = XGBE_RX_BPA; | pdata->kx_state = XGBE_RX_BPA; | ||||
/* Re-enable auto-negotiation interrupt */ | /* Re-enable auto-negotiation interrupt */ | ||||
XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INTMASK, 0x07); | XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INTMASK, 0x07); | ||||
reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_INTMASK); | |||||
/* Set up advertisement registers based on current settings */ | /* Set up advertisement registers based on current settings */ | ||||
xgbe_an_init(pdata); | xgbe_an_init(pdata); | ||||
/* Enable and start auto-negotiation */ | /* Enable and start auto-negotiation */ | ||||
xgbe_restart_an(pdata); | xgbe_an_restart(pdata); | ||||
return 0; | out: | ||||
} | if (ret) { | ||||
axgbe_printf(0, "%s: set_mode %d AN int reg value 0x%x ret value %d\n", | |||||
static int xgbe_phy_config_aneg(struct xgbe_prv_data *pdata) | __func__, set_mode, reg, ret); | ||||
{ | |||||
int ret; | |||||
sx_xlock(&pdata->an_mutex); | |||||
ret = __xgbe_phy_config_aneg(pdata); | |||||
if (ret) | |||||
set_bit(XGBE_LINK_ERR, &pdata->dev_state); | set_bit(XGBE_LINK_ERR, &pdata->dev_state); | ||||
else | } else | ||||
clear_bit(XGBE_LINK_ERR, &pdata->dev_state); | clear_bit(XGBE_LINK_ERR, &pdata->dev_state); | ||||
sx_unlock(&pdata->an_mutex); | sx_unlock(&pdata->an_mutex); | ||||
return ret; | return (ret); | ||||
} | } | ||||
static bool xgbe_phy_aneg_done(struct xgbe_prv_data *pdata) | static int | ||||
xgbe_phy_config_aneg(struct xgbe_prv_data *pdata) | |||||
{ | { | ||||
return (__xgbe_phy_config_aneg(pdata, true)); | |||||
} | |||||
static int | |||||
xgbe_phy_reconfig_aneg(struct xgbe_prv_data *pdata) | |||||
{ | |||||
return (__xgbe_phy_config_aneg(pdata, false)); | |||||
} | |||||
static bool | |||||
xgbe_phy_aneg_done(struct xgbe_prv_data *pdata) | |||||
{ | |||||
return (pdata->an_result == XGBE_AN_COMPLETE); | return (pdata->an_result == XGBE_AN_COMPLETE); | ||||
} | } | ||||
static void xgbe_check_link_timeout(struct xgbe_prv_data *pdata) | static void | ||||
xgbe_check_link_timeout(struct xgbe_prv_data *pdata) | |||||
{ | { | ||||
unsigned long link_timeout; | unsigned long link_timeout; | ||||
link_timeout = pdata->link_check + (XGBE_LINK_TIMEOUT * hz); | link_timeout = pdata->link_check + (XGBE_LINK_TIMEOUT * hz); | ||||
if ((int)(ticks - link_timeout) >= 0) { | if ((int)(ticks - link_timeout) > 0) { | ||||
axgbe_printf(2, "AN link timeout\n"); | |||||
xgbe_phy_config_aneg(pdata); | xgbe_phy_config_aneg(pdata); | ||||
} | } | ||||
} | } | ||||
static void xgbe_phy_status_force(struct xgbe_prv_data *pdata) | static enum xgbe_mode | ||||
xgbe_phy_status_aneg(struct xgbe_prv_data *pdata) | |||||
{ | { | ||||
if (xgbe_in_kr_mode(pdata)) { | return (pdata->phy_if.phy_impl.an_outcome(pdata)); | ||||
pdata->phy.speed = SPEED_10000; | |||||
} else { | |||||
switch (pdata->speed_set) { | |||||
case XGBE_SPEEDSET_1000_10000: | |||||
pdata->phy.speed = SPEED_1000; | |||||
break; | |||||
case XGBE_SPEEDSET_2500_10000: | |||||
pdata->phy.speed = SPEED_2500; | |||||
break; | |||||
} | } | ||||
} | |||||
pdata->phy.duplex = DUPLEX_FULL; | |||||
} | |||||
static void xgbe_phy_status_aneg(struct xgbe_prv_data *pdata) | static void | ||||
xgbe_phy_status_result(struct xgbe_prv_data *pdata) | |||||
{ | { | ||||
unsigned int ad_reg, lp_reg; | enum xgbe_mode mode; | ||||
pdata->phy.lp_advertising = 0; | XGBE_ZERO_LP_ADV(&pdata->phy); | ||||
if ((pdata->phy.autoneg != AUTONEG_ENABLE) || pdata->parallel_detect) | if ((pdata->phy.autoneg != AUTONEG_ENABLE) || pdata->parallel_detect) | ||||
return xgbe_phy_status_force(pdata); | mode = xgbe_cur_mode(pdata); | ||||
else | |||||
mode = xgbe_phy_status_aneg(pdata); | |||||
pdata->phy.lp_advertising |= ADVERTISED_Autoneg; | axgbe_printf(3, "%s: xgbe mode %d\n", __func__, mode); | ||||
pdata->phy.lp_advertising |= ADVERTISED_Backplane; | switch (mode) { | ||||
case XGBE_MODE_SGMII_100: | |||||
/* Compare Advertisement and Link Partner register 1 */ | pdata->phy.speed = SPEED_100; | ||||
ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE); | |||||
lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA); | |||||
if (lp_reg & 0x400) | |||||
pdata->phy.lp_advertising |= ADVERTISED_Pause; | |||||
if (lp_reg & 0x800) | |||||
pdata->phy.lp_advertising |= ADVERTISED_Asym_Pause; | |||||
if (pdata->phy.pause_autoneg) { | |||||
/* Set flow control based on auto-negotiation result */ | |||||
pdata->phy.tx_pause = 0; | |||||
pdata->phy.rx_pause = 0; | |||||
if (ad_reg & lp_reg & 0x400) { | |||||
pdata->phy.tx_pause = 1; | |||||
pdata->phy.rx_pause = 1; | |||||
} else if (ad_reg & lp_reg & 0x800) { | |||||
if (ad_reg & 0x400) | |||||
pdata->phy.rx_pause = 1; | |||||
else if (lp_reg & 0x400) | |||||
pdata->phy.tx_pause = 1; | |||||
} | |||||
} | |||||
/* Compare Advertisement and Link Partner register 2 */ | |||||
ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1); | |||||
lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA + 1); | |||||
if (lp_reg & 0x80) | |||||
pdata->phy.lp_advertising |= ADVERTISED_10000baseKR_Full; | |||||
if (lp_reg & 0x20) { | |||||
switch (pdata->speed_set) { | |||||
case XGBE_SPEEDSET_1000_10000: | |||||
pdata->phy.lp_advertising |= ADVERTISED_1000baseKX_Full; | |||||
break; | break; | ||||
case XGBE_SPEEDSET_2500_10000: | case XGBE_MODE_X: | ||||
pdata->phy.lp_advertising |= ADVERTISED_2500baseX_Full; | case XGBE_MODE_KX_1000: | ||||
break; | case XGBE_MODE_SGMII_1000: | ||||
} | |||||
} | |||||
ad_reg &= lp_reg; | |||||
if (ad_reg & 0x80) { | |||||
pdata->phy.speed = SPEED_10000; | |||||
xgbe_set_mode(pdata, XGBE_MODE_KR); | |||||
} else if (ad_reg & 0x20) { | |||||
switch (pdata->speed_set) { | |||||
case XGBE_SPEEDSET_1000_10000: | |||||
pdata->phy.speed = SPEED_1000; | pdata->phy.speed = SPEED_1000; | ||||
break; | break; | ||||
case XGBE_MODE_KX_2500: | |||||
case XGBE_SPEEDSET_2500_10000: | |||||
pdata->phy.speed = SPEED_2500; | pdata->phy.speed = SPEED_2500; | ||||
break; | break; | ||||
} | case XGBE_MODE_KR: | ||||
case XGBE_MODE_SFI: | |||||
xgbe_set_mode(pdata, XGBE_MODE_KX); | pdata->phy.speed = SPEED_10000; | ||||
} else { | break; | ||||
case XGBE_MODE_UNKNOWN: | |||||
default: | |||||
axgbe_printf(1, "%s: unknown mode\n", __func__); | |||||
pdata->phy.speed = SPEED_UNKNOWN; | pdata->phy.speed = SPEED_UNKNOWN; | ||||
} | } | ||||
/* Compare Advertisement and Link Partner register 3 */ | pdata->phy.duplex = DUPLEX_FULL; | ||||
ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2); | axgbe_printf(2, "%s: speed %d duplex %d\n", __func__, pdata->phy.speed, | ||||
lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA + 2); | pdata->phy.duplex); | ||||
if (xgbe_set_mode(pdata, mode) && pdata->an_again) | |||||
xgbe_phy_reconfig_aneg(pdata); | |||||
} | } | ||||
static void xgbe_phy_status(struct xgbe_prv_data *pdata) | static void | ||||
xgbe_phy_status(struct xgbe_prv_data *pdata) | |||||
{ | { | ||||
unsigned int reg, link_aneg; | bool link_aneg; | ||||
int an_restart; | |||||
if (test_bit(XGBE_LINK_ERR, &pdata->dev_state)) { | if (test_bit(XGBE_LINK_ERR, &pdata->dev_state)) { | ||||
axgbe_error("%s: LINK_ERR\n", __func__); | |||||
pdata->phy.link = 0; | pdata->phy.link = 0; | ||||
goto adjust_link; | goto adjust_link; | ||||
} | } | ||||
link_aneg = (pdata->phy.autoneg == AUTONEG_ENABLE); | link_aneg = (pdata->phy.autoneg == AUTONEG_ENABLE); | ||||
axgbe_printf(3, "link_aneg - %d\n", link_aneg); | |||||
/* Get the link status. Link status is latched low, so read | /* Get the link status. Link status is latched low, so read | ||||
* once to clear and then read again to get current state | * once to clear and then read again to get current state | ||||
*/ | */ | ||||
reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_STAT1); | pdata->phy.link = pdata->phy_if.phy_impl.link_status(pdata, | ||||
reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_STAT1); | &an_restart); | ||||
pdata->phy.link = (reg & MDIO_STAT1_LSTATUS) ? 1 : 0; | |||||
axgbe_printf(1, "link_status returned Link:%d an_restart:%d aneg:%d\n", | |||||
pdata->phy.link, an_restart, link_aneg); | |||||
if (an_restart) { | |||||
xgbe_phy_config_aneg(pdata); | |||||
return; | |||||
} | |||||
if (pdata->phy.link) { | if (pdata->phy.link) { | ||||
axgbe_printf(2, "Link Active\n"); | |||||
if (link_aneg && !xgbe_phy_aneg_done(pdata)) { | if (link_aneg && !xgbe_phy_aneg_done(pdata)) { | ||||
axgbe_printf(1, "phy_link set check timeout\n"); | |||||
xgbe_check_link_timeout(pdata); | xgbe_check_link_timeout(pdata); | ||||
return; | return; | ||||
} | } | ||||
xgbe_phy_status_aneg(pdata); | axgbe_printf(2, "%s: Link write phy_status result\n", __func__); | ||||
xgbe_phy_status_result(pdata); | |||||
if (test_bit(XGBE_LINK_INIT, &pdata->dev_state)) | if (test_bit(XGBE_LINK_INIT, &pdata->dev_state)) | ||||
clear_bit(XGBE_LINK_INIT, &pdata->dev_state); | clear_bit(XGBE_LINK_INIT, &pdata->dev_state); | ||||
} else { | } else { | ||||
axgbe_printf(2, "Link Deactive\n"); | |||||
if (test_bit(XGBE_LINK_INIT, &pdata->dev_state)) { | if (test_bit(XGBE_LINK_INIT, &pdata->dev_state)) { | ||||
axgbe_printf(1, "phy_link not set check timeout\n"); | |||||
xgbe_check_link_timeout(pdata); | xgbe_check_link_timeout(pdata); | ||||
if (link_aneg) | if (link_aneg) { | ||||
axgbe_printf(2, "link_aneg case\n"); | |||||
return; | return; | ||||
} | } | ||||
} | |||||
xgbe_phy_status_aneg(pdata); | xgbe_phy_status_result(pdata); | ||||
} | } | ||||
adjust_link: | adjust_link: | ||||
axgbe_printf(2, "%s: Link %d\n", __func__, pdata->phy.link); | |||||
xgbe_phy_adjust_link(pdata); | xgbe_phy_adjust_link(pdata); | ||||
} | } | ||||
static void xgbe_phy_stop(struct xgbe_prv_data *pdata) | static void | ||||
xgbe_phy_stop(struct xgbe_prv_data *pdata) | |||||
{ | { | ||||
axgbe_printf(2, "stopping PHY\n"); | |||||
/* Disable auto-negotiation */ | if (!pdata->phy_started) | ||||
xgbe_disable_an(pdata); | return; | ||||
/* Disable auto-negotiation interrupts */ | /* Indicate the PHY is down */ | ||||
XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INTMASK, 0); | pdata->phy_started = 0; | ||||
bus_teardown_intr(pdata->dev, pdata->an_irq_res, pdata->an_irq_tag); | /* Disable auto-negotiation */ | ||||
xgbe_an_disable_all(pdata); | |||||
pdata->phy_if.phy_impl.stop(pdata); | |||||
pdata->phy.link = 0; | pdata->phy.link = 0; | ||||
xgbe_phy_adjust_link(pdata); | xgbe_phy_adjust_link(pdata); | ||||
} | } | ||||
static int xgbe_phy_start(struct xgbe_prv_data *pdata) | static int | ||||
xgbe_phy_start(struct xgbe_prv_data *pdata) | |||||
{ | { | ||||
int ret; | int ret; | ||||
ret = bus_setup_intr(pdata->dev, pdata->an_irq_res, | DBGPR("-->xgbe_phy_start\n"); | ||||
INTR_MPSAFE | INTR_TYPE_NET, NULL, xgbe_an_isr, pdata, | |||||
&pdata->an_irq_tag); | ret = pdata->phy_if.phy_impl.start(pdata); | ||||
if (ret) { | if (ret) { | ||||
return -ret; | axgbe_error("%s: impl start ret %d\n", __func__, ret); | ||||
return (ret); | |||||
} | } | ||||
/* Set initial mode - call the mode setting routines | /* Set initial mode - call the mode setting routines | ||||
* directly to insure we are properly configured | * directly to insure we are properly configured | ||||
*/ | */ | ||||
if (xgbe_use_xgmii_mode(pdata)) { | if (xgbe_use_mode(pdata, XGBE_MODE_KR)) { | ||||
xgbe_xgmii_mode(pdata); | axgbe_printf(2, "%s: KR\n", __func__); | ||||
} else if (xgbe_use_gmii_mode(pdata)) { | xgbe_kr_mode(pdata); | ||||
xgbe_gmii_mode(pdata); | } else if (xgbe_use_mode(pdata, XGBE_MODE_KX_2500)) { | ||||
} else if (xgbe_use_gmii_2500_mode(pdata)) { | axgbe_printf(2, "%s: KX 2500\n", __func__); | ||||
xgbe_gmii_2500_mode(pdata); | xgbe_kx_2500_mode(pdata); | ||||
} else if (xgbe_use_mode(pdata, XGBE_MODE_KX_1000)) { | |||||
axgbe_printf(2, "%s: KX 1000\n", __func__); | |||||
xgbe_kx_1000_mode(pdata); | |||||
} else if (xgbe_use_mode(pdata, XGBE_MODE_SFI)) { | |||||
axgbe_printf(2, "%s: SFI\n", __func__); | |||||
xgbe_sfi_mode(pdata); | |||||
} else if (xgbe_use_mode(pdata, XGBE_MODE_X)) { | |||||
axgbe_printf(2, "%s: X\n", __func__); | |||||
xgbe_x_mode(pdata); | |||||
} else if (xgbe_use_mode(pdata, XGBE_MODE_SGMII_1000)) { | |||||
axgbe_printf(2, "%s: SGMII 1000\n", __func__); | |||||
xgbe_sgmii_1000_mode(pdata); | |||||
} else if (xgbe_use_mode(pdata, XGBE_MODE_SGMII_100)) { | |||||
axgbe_printf(2, "%s: SGMII 100\n", __func__); | |||||
xgbe_sgmii_100_mode(pdata); | |||||
} else { | } else { | ||||
axgbe_error("%s: invalid mode\n", __func__); | |||||
ret = -EINVAL; | ret = -EINVAL; | ||||
goto err_irq; | goto err_stop; | ||||
} | } | ||||
/* Indicate the PHY is up and running */ | |||||
pdata->phy_started = 1; | |||||
/* Set up advertisement registers based on current settings */ | /* Set up advertisement registers based on current settings */ | ||||
xgbe_an_init(pdata); | xgbe_an_init(pdata); | ||||
/* Enable auto-negotiation interrupts */ | /* Enable auto-negotiation interrupts */ | ||||
XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INTMASK, 0x07); | xgbe_an_enable_interrupts(pdata); | ||||
return xgbe_phy_config_aneg(pdata); | ret = xgbe_phy_config_aneg(pdata); | ||||
if (ret) | |||||
axgbe_error("%s: phy_config_aneg %d\n", __func__, ret); | |||||
err_irq: | return (ret); | ||||
bus_teardown_intr(pdata->dev, pdata->an_irq_res, pdata->an_irq_tag); | |||||
return ret; | err_stop: | ||||
pdata->phy_if.phy_impl.stop(pdata); | |||||
return (ret); | |||||
} | } | ||||
static int xgbe_phy_reset(struct xgbe_prv_data *pdata) | static int | ||||
xgbe_phy_reset(struct xgbe_prv_data *pdata) | |||||
{ | { | ||||
unsigned int count, reg; | int ret; | ||||
reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1); | ret = pdata->phy_if.phy_impl.reset(pdata); | ||||
reg |= MDIO_CTRL1_RESET; | if (ret) { | ||||
XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, reg); | axgbe_error("%s: impl phy reset %d\n", __func__, ret); | ||||
return (ret); | |||||
} | |||||
count = 50; | |||||
do { | |||||
DELAY(20); | |||||
reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1); | |||||
} while ((reg & MDIO_CTRL1_RESET) && --count); | |||||
if (reg & MDIO_CTRL1_RESET) | |||||
return -ETIMEDOUT; | |||||
/* Disable auto-negotiation for now */ | /* Disable auto-negotiation for now */ | ||||
xgbe_disable_an(pdata); | xgbe_an_disable_all(pdata); | ||||
/* Clear auto-negotiation interrupts */ | /* Clear auto-negotiation interrupts */ | ||||
XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INT, 0); | xgbe_an_clear_interrupts_all(pdata); | ||||
return 0; | return (0); | ||||
} | } | ||||
static void xgbe_phy_init(struct xgbe_prv_data *pdata) | static int | ||||
xgbe_phy_best_advertised_speed(struct xgbe_prv_data *pdata) | |||||
{ | { | ||||
if (XGBE_ADV(&pdata->phy, 10000baseKR_Full)) | |||||
return (SPEED_10000); | |||||
else if (XGBE_ADV(&pdata->phy, 10000baseT_Full)) | |||||
return (SPEED_10000); | |||||
else if (XGBE_ADV(&pdata->phy, 2500baseX_Full)) | |||||
return (SPEED_2500); | |||||
else if (XGBE_ADV(&pdata->phy, 2500baseT_Full)) | |||||
return (SPEED_2500); | |||||
else if (XGBE_ADV(&pdata->phy, 1000baseKX_Full)) | |||||
return (SPEED_1000); | |||||
else if (XGBE_ADV(&pdata->phy, 1000baseT_Full)) | |||||
return (SPEED_1000); | |||||
else if (XGBE_ADV(&pdata->phy, 100baseT_Full)) | |||||
return (SPEED_100); | |||||
return (SPEED_UNKNOWN); | |||||
} | |||||
static void | |||||
xgbe_phy_exit(struct xgbe_prv_data *pdata) | |||||
{ | |||||
pdata->phy_if.phy_impl.exit(pdata); | |||||
} | |||||
static int | |||||
xgbe_phy_init(struct xgbe_prv_data *pdata) | |||||
{ | |||||
int ret = 0; | |||||
DBGPR("-->xgbe_phy_init\n"); | |||||
sx_init(&pdata->an_mutex, "axgbe AN lock"); | sx_init(&pdata->an_mutex, "axgbe AN lock"); | ||||
pdata->mdio_mmd = MDIO_MMD_PCS; | pdata->mdio_mmd = MDIO_MMD_PCS; | ||||
/* Initialize supported features */ | /* Initialize supported features */ | ||||
pdata->phy.supported = SUPPORTED_Autoneg; | |||||
pdata->phy.supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause; | |||||
pdata->phy.supported |= SUPPORTED_Backplane; | |||||
pdata->phy.supported |= SUPPORTED_10000baseKR_Full; | |||||
switch (pdata->speed_set) { | |||||
case XGBE_SPEEDSET_1000_10000: | |||||
pdata->phy.supported |= SUPPORTED_1000baseKX_Full; | |||||
break; | |||||
case XGBE_SPEEDSET_2500_10000: | |||||
pdata->phy.supported |= SUPPORTED_2500baseX_Full; | |||||
break; | |||||
} | |||||
pdata->fec_ability = XMDIO_READ(pdata, MDIO_MMD_PMAPMD, | pdata->fec_ability = XMDIO_READ(pdata, MDIO_MMD_PMAPMD, | ||||
MDIO_PMA_10GBR_FECABLE); | MDIO_PMA_10GBR_FECABLE); | ||||
pdata->fec_ability &= (MDIO_PMA_10GBR_FECABLE_ABLE | | pdata->fec_ability &= (MDIO_PMA_10GBR_FECABLE_ABLE | | ||||
MDIO_PMA_10GBR_FECABLE_ERRABLE); | MDIO_PMA_10GBR_FECABLE_ERRABLE); | ||||
if (pdata->fec_ability & MDIO_PMA_10GBR_FECABLE_ABLE) | |||||
pdata->phy.supported |= SUPPORTED_10000baseR_FEC; | |||||
pdata->phy.advertising = pdata->phy.supported; | /* Setup the phy (including supported features) */ | ||||
ret = pdata->phy_if.phy_impl.init(pdata); | |||||
if (ret) | |||||
return (ret); | |||||
/* Copy supported link modes to advertising link modes */ | |||||
XGBE_LM_COPY(&pdata->phy, advertising, &pdata->phy, supported); | |||||
pdata->phy.address = 0; | pdata->phy.address = 0; | ||||
if (XGBE_ADV(&pdata->phy, Autoneg)) { | |||||
pdata->phy.autoneg = AUTONEG_ENABLE; | pdata->phy.autoneg = AUTONEG_ENABLE; | ||||
pdata->phy.speed = SPEED_UNKNOWN; | pdata->phy.speed = SPEED_UNKNOWN; | ||||
pdata->phy.duplex = DUPLEX_UNKNOWN; | pdata->phy.duplex = DUPLEX_UNKNOWN; | ||||
} else { | |||||
pdata->phy.autoneg = AUTONEG_DISABLE; | |||||
pdata->phy.speed = xgbe_phy_best_advertised_speed(pdata); | |||||
pdata->phy.duplex = DUPLEX_FULL; | |||||
} | |||||
pdata->phy.link = 0; | pdata->phy.link = 0; | ||||
pdata->phy.pause_autoneg = pdata->pause_autoneg; | pdata->phy.pause_autoneg = pdata->pause_autoneg; | ||||
pdata->phy.tx_pause = pdata->tx_pause; | pdata->phy.tx_pause = pdata->tx_pause; | ||||
pdata->phy.rx_pause = pdata->rx_pause; | pdata->phy.rx_pause = pdata->rx_pause; | ||||
/* Fix up Flow Control advertising */ | /* Fix up Flow Control advertising */ | ||||
pdata->phy.advertising &= ~ADVERTISED_Pause; | XGBE_CLR_ADV(&pdata->phy, Pause); | ||||
pdata->phy.advertising &= ~ADVERTISED_Asym_Pause; | XGBE_CLR_ADV(&pdata->phy, Asym_Pause); | ||||
if (pdata->rx_pause) { | if (pdata->rx_pause) { | ||||
pdata->phy.advertising |= ADVERTISED_Pause; | XGBE_SET_ADV(&pdata->phy, Pause); | ||||
pdata->phy.advertising |= ADVERTISED_Asym_Pause; | XGBE_SET_ADV(&pdata->phy, Asym_Pause); | ||||
} | } | ||||
if (pdata->tx_pause) | if (pdata->tx_pause) { | ||||
pdata->phy.advertising ^= ADVERTISED_Asym_Pause; | if (XGBE_ADV(&pdata->phy, Asym_Pause)) | ||||
XGBE_CLR_ADV(&pdata->phy, Asym_Pause); | |||||
else | |||||
XGBE_SET_ADV(&pdata->phy, Asym_Pause); | |||||
} | } | ||||
void xgbe_init_function_ptrs_phy(struct xgbe_phy_if *phy_if) | return (0); | ||||
} | |||||
void | |||||
xgbe_init_function_ptrs_phy(struct xgbe_phy_if *phy_if) | |||||
{ | { | ||||
phy_if->phy_init = xgbe_phy_init; | phy_if->phy_init = xgbe_phy_init; | ||||
phy_if->phy_exit = xgbe_phy_exit; | |||||
phy_if->phy_reset = xgbe_phy_reset; | phy_if->phy_reset = xgbe_phy_reset; | ||||
phy_if->phy_start = xgbe_phy_start; | phy_if->phy_start = xgbe_phy_start; | ||||
phy_if->phy_stop = xgbe_phy_stop; | phy_if->phy_stop = xgbe_phy_stop; | ||||
phy_if->phy_status = xgbe_phy_status; | phy_if->phy_status = xgbe_phy_status; | ||||
phy_if->phy_config_aneg = xgbe_phy_config_aneg; | phy_if->phy_config_aneg = xgbe_phy_config_aneg; | ||||
phy_if->phy_valid_speed = xgbe_phy_valid_speed; | |||||
phy_if->an_isr = xgbe_an_combined_isr; | |||||
} | } |