Commit e3eec4e7 authored by Lendacky, Thomas's avatar Lendacky, Thomas Committed by David S. Miller

amd-xgbe-phy: Check device for current speed mode (KR/KX)

Since device resets can change the current mode it's possible to think
the device is in a different mode than it actually is.  Rather than
trying to determine every place that is needed to set/save the current
mode, be safe and check the devices actual mode when needed rather than
trying to track it.
Signed-off-by: default avatarTom Lendacky <thomas.lendacky@amd.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent e4cf0b75
...@@ -331,7 +331,6 @@ struct amd_xgbe_phy_priv { ...@@ -331,7 +331,6 @@ struct amd_xgbe_phy_priv {
/* Maintain link status for re-starting auto-negotiation */ /* Maintain link status for re-starting auto-negotiation */
unsigned int link; unsigned int link;
enum amd_xgbe_phy_mode mode;
unsigned int speed_set; unsigned int speed_set;
/* Auto-negotiation state machine support */ /* Auto-negotiation state machine support */
...@@ -468,8 +467,6 @@ static int amd_xgbe_phy_xgmii_mode(struct phy_device *phydev) ...@@ -468,8 +467,6 @@ static int amd_xgbe_phy_xgmii_mode(struct phy_device *phydev)
amd_xgbe_phy_serdes_complete_ratechange(phydev); amd_xgbe_phy_serdes_complete_ratechange(phydev);
priv->mode = AMD_XGBE_MODE_KR;
return 0; return 0;
} }
...@@ -518,8 +515,6 @@ static int amd_xgbe_phy_gmii_2500_mode(struct phy_device *phydev) ...@@ -518,8 +515,6 @@ static int amd_xgbe_phy_gmii_2500_mode(struct phy_device *phydev)
amd_xgbe_phy_serdes_complete_ratechange(phydev); amd_xgbe_phy_serdes_complete_ratechange(phydev);
priv->mode = AMD_XGBE_MODE_KX;
return 0; return 0;
} }
...@@ -568,18 +563,43 @@ static int amd_xgbe_phy_gmii_mode(struct phy_device *phydev) ...@@ -568,18 +563,43 @@ static int amd_xgbe_phy_gmii_mode(struct phy_device *phydev)
amd_xgbe_phy_serdes_complete_ratechange(phydev); amd_xgbe_phy_serdes_complete_ratechange(phydev);
priv->mode = AMD_XGBE_MODE_KX; return 0;
}
static int amd_xgbe_phy_cur_mode(struct phy_device *phydev,
enum amd_xgbe_phy_mode *mode)
{
int ret;
ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL2);
if (ret < 0)
return ret;
if ((ret & MDIO_PCS_CTRL2_TYPE) == MDIO_PCS_CTRL2_10GBR)
*mode = AMD_XGBE_MODE_KR;
else
*mode = AMD_XGBE_MODE_KX;
return 0; return 0;
} }
static bool amd_xgbe_phy_in_kr_mode(struct phy_device *phydev)
{
enum amd_xgbe_phy_mode mode;
if (amd_xgbe_phy_cur_mode(phydev, &mode))
return false;
return (mode == AMD_XGBE_MODE_KR);
}
static int amd_xgbe_phy_switch_mode(struct phy_device *phydev) static int amd_xgbe_phy_switch_mode(struct phy_device *phydev)
{ {
struct amd_xgbe_phy_priv *priv = phydev->priv; struct amd_xgbe_phy_priv *priv = phydev->priv;
int ret; int ret;
/* If we are in KR switch to KX, and vice-versa */ /* If we are in KR switch to KX, and vice-versa */
if (priv->mode == AMD_XGBE_MODE_KR) { if (amd_xgbe_phy_in_kr_mode(phydev)) {
if (priv->speed_set == AMD_XGBE_PHY_SPEEDSET_1000_10000) if (priv->speed_set == AMD_XGBE_PHY_SPEEDSET_1000_10000)
ret = amd_xgbe_phy_gmii_mode(phydev); ret = amd_xgbe_phy_gmii_mode(phydev);
else else
...@@ -591,27 +611,31 @@ static int amd_xgbe_phy_switch_mode(struct phy_device *phydev) ...@@ -591,27 +611,31 @@ static int amd_xgbe_phy_switch_mode(struct phy_device *phydev)
return ret; return ret;
} }
static enum amd_xgbe_phy_an amd_xgbe_an_switch_mode(struct phy_device *phydev) static int amd_xgbe_phy_set_mode(struct phy_device *phydev,
enum amd_xgbe_phy_mode mode)
{ {
enum amd_xgbe_phy_mode cur_mode;
int ret; int ret;
ret = amd_xgbe_phy_cur_mode(phydev, &cur_mode);
if (ret)
return ret;
if (mode != cur_mode)
ret = amd_xgbe_phy_switch_mode(phydev); ret = amd_xgbe_phy_switch_mode(phydev);
if (ret < 0)
return AMD_XGBE_AN_ERROR;
return AMD_XGBE_AN_START; return ret;
} }
static enum amd_xgbe_phy_an amd_xgbe_an_tx_training(struct phy_device *phydev, static enum amd_xgbe_phy_an amd_xgbe_an_tx_training(struct phy_device *phydev,
enum amd_xgbe_phy_rx *state) enum amd_xgbe_phy_rx *state)
{ {
struct amd_xgbe_phy_priv *priv = phydev->priv;
int ad_reg, lp_reg, ret; int ad_reg, lp_reg, ret;
*state = AMD_XGBE_RX_COMPLETE; *state = AMD_XGBE_RX_COMPLETE;
/* If we're in KX mode then we're done */ /* If we're not in KR mode then we're done */
if (priv->mode == AMD_XGBE_MODE_KX) if (!amd_xgbe_phy_in_kr_mode(phydev))
return AMD_XGBE_AN_EVENT; return AMD_XGBE_AN_EVENT;
/* Enable/Disable FEC */ /* Enable/Disable FEC */
...@@ -669,7 +693,6 @@ static enum amd_xgbe_phy_an amd_xgbe_an_tx_xnp(struct phy_device *phydev, ...@@ -669,7 +693,6 @@ static enum amd_xgbe_phy_an amd_xgbe_an_tx_xnp(struct phy_device *phydev,
static enum amd_xgbe_phy_an amd_xgbe_an_rx_bpa(struct phy_device *phydev, static enum amd_xgbe_phy_an amd_xgbe_an_rx_bpa(struct phy_device *phydev,
enum amd_xgbe_phy_rx *state) enum amd_xgbe_phy_rx *state)
{ {
struct amd_xgbe_phy_priv *priv = phydev->priv;
unsigned int link_support; unsigned int link_support;
int ret, ad_reg, lp_reg; int ret, ad_reg, lp_reg;
...@@ -679,9 +702,9 @@ static enum amd_xgbe_phy_an amd_xgbe_an_rx_bpa(struct phy_device *phydev, ...@@ -679,9 +702,9 @@ static enum amd_xgbe_phy_an amd_xgbe_an_rx_bpa(struct phy_device *phydev,
return AMD_XGBE_AN_ERROR; return AMD_XGBE_AN_ERROR;
/* Check for a supported mode, otherwise restart in a different one */ /* Check for a supported mode, otherwise restart in a different one */
link_support = (priv->mode == AMD_XGBE_MODE_KR) ? 0x80 : 0x20; link_support = amd_xgbe_phy_in_kr_mode(phydev) ? 0x80 : 0x20;
if (!(ret & link_support)) if (!(ret & link_support))
return amd_xgbe_an_switch_mode(phydev); return AMD_XGBE_AN_INCOMPAT_LINK;
/* Check Extended Next Page support */ /* Check Extended Next Page support */
ad_reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE); ad_reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE);
...@@ -722,7 +745,7 @@ static enum amd_xgbe_phy_an amd_xgbe_an_start(struct phy_device *phydev) ...@@ -722,7 +745,7 @@ static enum amd_xgbe_phy_an amd_xgbe_an_start(struct phy_device *phydev)
int ret; int ret;
/* Be sure we aren't looping trying to negotiate */ /* Be sure we aren't looping trying to negotiate */
if (priv->mode == AMD_XGBE_MODE_KR) { if (amd_xgbe_phy_in_kr_mode(phydev)) {
if (priv->kr_state != AMD_XGBE_RX_READY) if (priv->kr_state != AMD_XGBE_RX_READY)
return AMD_XGBE_AN_NO_LINK; return AMD_XGBE_AN_NO_LINK;
priv->kr_state = AMD_XGBE_RX_BPA; priv->kr_state = AMD_XGBE_RX_BPA;
...@@ -825,7 +848,7 @@ static enum amd_xgbe_phy_an amd_xgbe_an_page_received(struct phy_device *phydev) ...@@ -825,7 +848,7 @@ static enum amd_xgbe_phy_an amd_xgbe_an_page_received(struct phy_device *phydev)
enum amd_xgbe_phy_rx *state; enum amd_xgbe_phy_rx *state;
int ret; int ret;
state = (priv->mode == AMD_XGBE_MODE_KR) ? &priv->kr_state state = amd_xgbe_phy_in_kr_mode(phydev) ? &priv->kr_state
: &priv->kx_state; : &priv->kx_state;
switch (*state) { switch (*state) {
...@@ -846,7 +869,13 @@ static enum amd_xgbe_phy_an amd_xgbe_an_page_received(struct phy_device *phydev) ...@@ -846,7 +869,13 @@ static enum amd_xgbe_phy_an amd_xgbe_an_page_received(struct phy_device *phydev)
static enum amd_xgbe_phy_an amd_xgbe_an_incompat_link(struct phy_device *phydev) static enum amd_xgbe_phy_an amd_xgbe_an_incompat_link(struct phy_device *phydev)
{ {
return amd_xgbe_an_switch_mode(phydev); int ret;
ret = amd_xgbe_phy_switch_mode(phydev);
if (ret)
return AMD_XGBE_AN_ERROR;
return AMD_XGBE_AN_START;
} }
static void amd_xgbe_an_state_machine(struct work_struct *work) static void amd_xgbe_an_state_machine(struct work_struct *work)
...@@ -1018,7 +1047,6 @@ static int amd_xgbe_phy_config_aneg(struct phy_device *phydev) ...@@ -1018,7 +1047,6 @@ static int amd_xgbe_phy_config_aneg(struct phy_device *phydev)
{ {
struct amd_xgbe_phy_priv *priv = phydev->priv; struct amd_xgbe_phy_priv *priv = phydev->priv;
u32 mmd_mask = phydev->c45_ids.devices_in_package; u32 mmd_mask = phydev->c45_ids.devices_in_package;
int ret;
if (phydev->autoneg != AUTONEG_ENABLE) if (phydev->autoneg != AUTONEG_ENABLE)
return amd_xgbe_phy_setup_forced(phydev); return amd_xgbe_phy_setup_forced(phydev);
...@@ -1027,11 +1055,6 @@ static int amd_xgbe_phy_config_aneg(struct phy_device *phydev) ...@@ -1027,11 +1055,6 @@ static int amd_xgbe_phy_config_aneg(struct phy_device *phydev)
if (!(mmd_mask & MDIO_DEVS_AN)) if (!(mmd_mask & MDIO_DEVS_AN))
return -EINVAL; return -EINVAL;
/* Get the current speed mode */
ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL2);
if (ret < 0)
return ret;
/* Start/Restart the auto-negotiation state machine */ /* Start/Restart the auto-negotiation state machine */
mutex_lock(&priv->an_mutex); mutex_lock(&priv->an_mutex);
priv->an_result = AMD_XGBE_AN_READY; priv->an_result = AMD_XGBE_AN_READY;
...@@ -1121,17 +1144,12 @@ static int amd_xgbe_phy_read_status(struct phy_device *phydev) ...@@ -1121,17 +1144,12 @@ static int amd_xgbe_phy_read_status(struct phy_device *phydev)
{ {
struct amd_xgbe_phy_priv *priv = phydev->priv; struct amd_xgbe_phy_priv *priv = phydev->priv;
u32 mmd_mask = phydev->c45_ids.devices_in_package; u32 mmd_mask = phydev->c45_ids.devices_in_package;
int ret, mode, ad_ret, lp_ret; int ret, ad_ret, lp_ret;
ret = amd_xgbe_phy_update_link(phydev); ret = amd_xgbe_phy_update_link(phydev);
if (ret) if (ret)
return ret; return ret;
mode = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL2);
if (mode < 0)
return mode;
mode &= MDIO_PCS_CTRL2_TYPE;
if (phydev->autoneg == AUTONEG_ENABLE) { if (phydev->autoneg == AUTONEG_ENABLE) {
if (!(mmd_mask & MDIO_DEVS_AN)) if (!(mmd_mask & MDIO_DEVS_AN))
return -EINVAL; return -EINVAL;
...@@ -1163,40 +1181,39 @@ static int amd_xgbe_phy_read_status(struct phy_device *phydev) ...@@ -1163,40 +1181,39 @@ static int amd_xgbe_phy_read_status(struct phy_device *phydev)
ad_ret &= lp_ret; ad_ret &= lp_ret;
if (ad_ret & 0x80) { if (ad_ret & 0x80) {
phydev->speed = SPEED_10000; phydev->speed = SPEED_10000;
if (mode != MDIO_PCS_CTRL2_10GBR) { ret = amd_xgbe_phy_set_mode(phydev, AMD_XGBE_MODE_KR);
ret = amd_xgbe_phy_xgmii_mode(phydev); if (ret)
if (ret < 0)
return ret; return ret;
}
} else { } else {
int (*mode_fcn)(struct phy_device *); switch (priv->speed_set) {
case AMD_XGBE_PHY_SPEEDSET_1000_10000:
if (priv->speed_set ==
AMD_XGBE_PHY_SPEEDSET_1000_10000) {
phydev->speed = SPEED_1000; phydev->speed = SPEED_1000;
mode_fcn = amd_xgbe_phy_gmii_mode; break;
} else {
case AMD_XGBE_PHY_SPEEDSET_2500_10000:
phydev->speed = SPEED_2500; phydev->speed = SPEED_2500;
mode_fcn = amd_xgbe_phy_gmii_2500_mode; break;
} }
if (mode == MDIO_PCS_CTRL2_10GBR) { ret = amd_xgbe_phy_set_mode(phydev, AMD_XGBE_MODE_KX);
ret = mode_fcn(phydev); if (ret)
if (ret < 0)
return ret; return ret;
} }
}
phydev->duplex = DUPLEX_FULL; phydev->duplex = DUPLEX_FULL;
} else { } else {
if (mode == MDIO_PCS_CTRL2_10GBR) { if (amd_xgbe_phy_in_kr_mode(phydev)) {
phydev->speed = SPEED_10000; phydev->speed = SPEED_10000;
} else { } else {
if (priv->speed_set == switch (priv->speed_set) {
AMD_XGBE_PHY_SPEEDSET_1000_10000) case AMD_XGBE_PHY_SPEEDSET_1000_10000:
phydev->speed = SPEED_1000; phydev->speed = SPEED_1000;
else break;
case AMD_XGBE_PHY_SPEEDSET_2500_10000:
phydev->speed = SPEED_2500; phydev->speed = SPEED_2500;
break;
}
} }
phydev->duplex = DUPLEX_FULL; phydev->duplex = DUPLEX_FULL;
phydev->pause = 0; phydev->pause = 0;
...@@ -1329,14 +1346,6 @@ static int amd_xgbe_phy_probe(struct phy_device *phydev) ...@@ -1329,14 +1346,6 @@ static int amd_xgbe_phy_probe(struct phy_device *phydev)
priv->link = 1; priv->link = 1;
ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL2);
if (ret < 0)
goto err_sir1;
if ((ret & MDIO_PCS_CTRL2_TYPE) == MDIO_PCS_CTRL2_10GBR)
priv->mode = AMD_XGBE_MODE_KR;
else
priv->mode = AMD_XGBE_MODE_KX;
mutex_init(&priv->an_mutex); mutex_init(&priv->an_mutex);
INIT_WORK(&priv->an_work, amd_xgbe_an_state_machine); INIT_WORK(&priv->an_work, amd_xgbe_an_state_machine);
priv->an_workqueue = create_singlethread_workqueue(wq_name); priv->an_workqueue = create_singlethread_workqueue(wq_name);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment