amd-xgbe-phy: Provide support for auto-negotiation timeout
authorLendacky, Thomas <Thomas.Lendacky@amd.com>
Fri, 20 Mar 2015 16:50:04 +0000 (11:50 -0500)
committerDavid S. Miller <davem@davemloft.net>
Fri, 20 Mar 2015 20:33:56 +0000 (16:33 -0400)
Currently, there is no interrupt code that indicates auto-negotiation
has timed out. If the auto-negotiation has timed out then the start of
a new auto-negotiation will begin again with a new base page being
received. The state machine could be in a state that is not expecting
this interrupt code which results in an error during auto-negotiation.

Update the code to timestamp when the auto-negotiation starts.  Should
another page received interrupt code occur before auto-negotiation has
completed but after the auto-negotiation timeout, then reset the state
machine to allow the auto-negotiation to continue.

Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/phy/amd-xgbe-phy.c

index 4b95caf08aa9a3ad7a43ba8eff390d3e1dc727d8..fb276f64cd6400cc7617c2586582c378eb2e9c53 100644 (file)
@@ -78,6 +78,7 @@
 #include <linux/bitops.h>
 #include <linux/property.h>
 #include <linux/acpi.h>
+#include <linux/jiffies.h>
 
 MODULE_AUTHOR("Tom Lendacky <thomas.lendacky@amd.com>");
 MODULE_LICENSE("Dual BSD/GPL");
@@ -100,6 +101,8 @@ MODULE_DESCRIPTION("AMD 10GbE (amd-xgbe) PHY driver");
 #define XGBE_PHY_SPEED_2500            1
 #define XGBE_PHY_SPEED_10000           2
 
+#define XGBE_AN_MS_TIMEOUT             500
+
 #define XGBE_AN_INT_CMPLT              0x01
 #define XGBE_AN_INC_LINK               0x02
 #define XGBE_AN_PG_RCV                 0x04
@@ -434,6 +437,7 @@ struct amd_xgbe_phy_priv {
        unsigned int an_supported;
        unsigned int parallel_detect;
        unsigned int fec_ability;
+       unsigned long an_start;
 
        unsigned int lpm_ctrl;          /* CTRL1 for resume */
 };
@@ -902,8 +906,23 @@ static enum amd_xgbe_phy_an amd_xgbe_an_page_received(struct phy_device *phydev)
 {
        struct amd_xgbe_phy_priv *priv = phydev->priv;
        enum amd_xgbe_phy_rx *state;
+       unsigned long an_timeout;
        int ret;
 
+       if (!priv->an_start) {
+               priv->an_start = jiffies;
+       } else {
+               an_timeout = priv->an_start +
+                            msecs_to_jiffies(XGBE_AN_MS_TIMEOUT);
+               if (time_after(jiffies, an_timeout)) {
+                       /* Auto-negotiation timed out, reset state */
+                       priv->kr_state = AMD_XGBE_RX_BPA;
+                       priv->kx_state = AMD_XGBE_RX_BPA;
+
+                       priv->an_start = jiffies;
+               }
+       }
+
        state = amd_xgbe_phy_in_kr_mode(phydev) ? &priv->kr_state
                                                : &priv->kx_state;
 
@@ -1078,6 +1097,7 @@ again:
                priv->an_state = AMD_XGBE_AN_READY;
                priv->kr_state = AMD_XGBE_RX_BPA;
                priv->kx_state = AMD_XGBE_RX_BPA;
+               priv->an_start = 0;
        }
 
        if (cur_state != priv->an_state)