summaryrefslogtreecommitdiffstats
path: root/drivers/net/phy/amd-xgbe-phy.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/phy/amd-xgbe-phy.c')
-rw-r--r--drivers/net/phy/amd-xgbe-phy.c136
1 files changed, 118 insertions, 18 deletions
diff --git a/drivers/net/phy/amd-xgbe-phy.c b/drivers/net/phy/amd-xgbe-phy.c
index b57c224..f3230ee 100644
--- a/drivers/net/phy/amd-xgbe-phy.c
+++ b/drivers/net/phy/amd-xgbe-phy.c
@@ -74,7 +74,6 @@
#include <linux/of_platform.h>
#include <linux/of_device.h>
#include <linux/uaccess.h>
-#include <asm/irq.h>
MODULE_AUTHOR("Tom Lendacky <thomas.lendacky@amd.com>");
@@ -85,6 +84,8 @@ MODULE_DESCRIPTION("AMD 10GbE (amd-xgbe) PHY driver");
#define XGBE_PHY_ID 0x000162d0
#define XGBE_PHY_MASK 0xfffffff0
+#define XGBE_PHY_SPEEDSET_PROPERTY "amd,speed-set"
+
#define XGBE_AN_INT_CMPLT 0x01
#define XGBE_AN_INC_LINK 0x02
#define XGBE_AN_PG_RCV 0x04
@@ -94,6 +95,8 @@ MODULE_DESCRIPTION("AMD 10GbE (amd-xgbe) PHY driver");
#define XNP_MP_FORMATTED (1 << 13)
#define XNP_NP_EXCHANGE (1 << 15)
+#define XGBE_PHY_RATECHANGE_COUNT 500
+
#ifndef MDIO_PMA_10GBR_PMD_CTRL
#define MDIO_PMA_10GBR_PMD_CTRL 0x0096
#endif
@@ -116,10 +119,13 @@ MODULE_DESCRIPTION("AMD 10GbE (amd-xgbe) PHY driver");
#endif
/* SerDes integration register offsets */
+#define SIR0_KR_RT_1 0x002c
#define SIR0_STATUS 0x0040
#define SIR1_SPEED 0x0000
/* SerDes integration register entry bit positions and sizes */
+#define SIR0_KR_RT_1_RESET_INDEX 11
+#define SIR0_KR_RT_1_RESET_WIDTH 1
#define SIR0_STATUS_RX_READY_INDEX 0
#define SIR0_STATUS_RX_READY_WIDTH 1
#define SIR0_STATUS_TX_READY_INDEX 8
@@ -145,7 +151,7 @@ MODULE_DESCRIPTION("AMD 10GbE (amd-xgbe) PHY driver");
#define SPEED_2500_CDR 0x2
#define SPEED_2500_PLL 0x0
-#define SPEED_2500_RATE 0x2
+#define SPEED_2500_RATE 0x1
#define SPEED_2500_TXAMP 0xf
#define SPEED_2500_WORD 0x1
@@ -192,6 +198,16 @@ do { \
(_var) |= (((_val) & ((0x1 << (_width)) - 1)) << (_index)); \
} while (0)
+#define XSIR_GET_BITS(_var, _prefix, _field) \
+ GET_BITS((_var), \
+ _prefix##_##_field##_INDEX, \
+ _prefix##_##_field##_WIDTH)
+
+#define XSIR_SET_BITS(_var, _prefix, _field, _val) \
+ SET_BITS((_var), \
+ _prefix##_##_field##_INDEX, \
+ _prefix##_##_field##_WIDTH, (_val))
+
/* Macros for reading or writing SerDes integration registers
* The ioread macros will get bit fields or full values using the
* register definitions formed using the input names
@@ -292,6 +308,11 @@ enum amd_xgbe_phy_mode {
AMD_XGBE_MODE_KX,
};
+enum amd_xgbe_phy_speedset {
+ AMD_XGBE_PHY_SPEEDSET_1000_10000,
+ AMD_XGBE_PHY_SPEEDSET_2500_10000,
+};
+
struct amd_xgbe_phy_priv {
struct platform_device *pdev;
struct device *dev;
@@ -311,6 +332,7 @@ struct amd_xgbe_phy_priv {
/* Maintain link status for re-starting auto-negotiation */
unsigned int link;
enum amd_xgbe_phy_mode mode;
+ unsigned int speed_set;
/* Auto-negotiation state machine support */
struct mutex an_mutex;
@@ -380,14 +402,25 @@ static void amd_xgbe_phy_serdes_start_ratechange(struct phy_device *phydev)
static void amd_xgbe_phy_serdes_complete_ratechange(struct phy_device *phydev)
{
struct amd_xgbe_phy_priv *priv = phydev->priv;
+ unsigned int wait;
+ u16 status;
/* Release Rx and Tx ratechange */
XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, RATECHANGE, 0);
/* Wait for Rx and Tx ready */
- while (!XSIR0_IOREAD_BITS(priv, SIR0_STATUS, RX_READY) &&
- !XSIR0_IOREAD_BITS(priv, SIR0_STATUS, TX_READY))
- usleep_range(10, 20);
+ wait = XGBE_PHY_RATECHANGE_COUNT;
+ while (wait--) {
+ usleep_range(50, 75);
+
+ status = XSIR0_IOREAD(priv, SIR0_STATUS);
+ if (XSIR_GET_BITS(status, SIR0_STATUS, RX_READY) &&
+ XSIR_GET_BITS(status, SIR0_STATUS, TX_READY))
+ return;
+ }
+
+ netdev_dbg(phydev->attached_dev, "SerDes rx/tx not ready (%#hx)\n",
+ status);
}
static int amd_xgbe_phy_xgmii_mode(struct phy_device *phydev)
@@ -546,10 +579,14 @@ static int amd_xgbe_phy_switch_mode(struct phy_device *phydev)
int ret;
/* If we are in KR switch to KX, and vice-versa */
- if (priv->mode == AMD_XGBE_MODE_KR)
- ret = amd_xgbe_phy_gmii_mode(phydev);
- else
+ if (priv->mode == AMD_XGBE_MODE_KR) {
+ if (priv->speed_set == AMD_XGBE_PHY_SPEEDSET_1000_10000)
+ ret = amd_xgbe_phy_gmii_mode(phydev);
+ else
+ ret = amd_xgbe_phy_gmii_2500_mode(phydev);
+ } else {
ret = amd_xgbe_phy_xgmii_mode(phydev);
+ }
return ret;
}
@@ -602,9 +639,13 @@ static enum amd_xgbe_phy_an amd_xgbe_an_tx_training(struct phy_device *phydev,
if (ret < 0)
return AMD_XGBE_AN_ERROR;
+ XSIR0_IOWRITE_BITS(priv, SIR0_KR_RT_1, RESET, 1);
+
ret |= 0x01;
phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL, ret);
+ XSIR0_IOWRITE_BITS(priv, SIR0_KR_RT_1, RESET, 0);
+
return AMD_XGBE_AN_EVENT;
}
@@ -713,7 +754,8 @@ static enum amd_xgbe_phy_an amd_xgbe_an_start(struct phy_device *phydev)
else
ret &= ~0x80;
- if (phydev->supported & SUPPORTED_1000baseKX_Full)
+ if ((phydev->supported & SUPPORTED_1000baseKX_Full) ||
+ (phydev->supported & SUPPORTED_2500baseX_Full))
ret |= 0x20;
else
ret &= ~0x20;
@@ -815,6 +857,7 @@ static void amd_xgbe_an_state_machine(struct work_struct *work)
struct phy_device *phydev = priv->phydev;
enum amd_xgbe_phy_an cur_state;
int sleep;
+ unsigned int an_supported = 0;
while (1) {
mutex_lock(&priv->an_mutex);
@@ -824,6 +867,7 @@ static void amd_xgbe_an_state_machine(struct work_struct *work)
switch (priv->an_state) {
case AMD_XGBE_AN_START:
priv->an_state = amd_xgbe_an_start(phydev);
+ an_supported = 0;
break;
case AMD_XGBE_AN_EVENT:
@@ -832,6 +876,7 @@ static void amd_xgbe_an_state_machine(struct work_struct *work)
case AMD_XGBE_AN_PAGE_RECEIVED:
priv->an_state = amd_xgbe_an_page_received(phydev);
+ an_supported++;
break;
case AMD_XGBE_AN_INCOMPAT_LINK:
@@ -839,6 +884,11 @@ static void amd_xgbe_an_state_machine(struct work_struct *work)
break;
case AMD_XGBE_AN_COMPLETE:
+ netdev_info(phydev->attached_dev, "%s successful\n",
+ an_supported ? "Auto negotiation"
+ : "Parallel detection");
+ /* fall through */
+
case AMD_XGBE_AN_NO_LINK:
case AMD_XGBE_AN_EXIT:
goto exit_unlock;
@@ -896,14 +946,22 @@ static int amd_xgbe_phy_soft_reset(struct phy_device *phydev)
static int amd_xgbe_phy_config_init(struct phy_device *phydev)
{
+ struct amd_xgbe_phy_priv *priv = phydev->priv;
+
/* Initialize supported features */
phydev->supported = SUPPORTED_Autoneg;
phydev->supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause;
phydev->supported |= SUPPORTED_Backplane;
- phydev->supported |= SUPPORTED_1000baseKX_Full |
- SUPPORTED_2500baseX_Full;
phydev->supported |= SUPPORTED_10000baseKR_Full |
SUPPORTED_10000baseR_FEC;
+ switch (priv->speed_set) {
+ case AMD_XGBE_PHY_SPEEDSET_1000_10000:
+ phydev->supported |= SUPPORTED_1000baseKX_Full;
+ break;
+ case AMD_XGBE_PHY_SPEEDSET_2500_10000:
+ phydev->supported |= SUPPORTED_2500baseX_Full;
+ break;
+ }
phydev->advertising = phydev->supported;
/* Turn off and clear interrupts */
@@ -1020,9 +1078,9 @@ static int amd_xgbe_phy_update_link(struct phy_device *phydev)
* (re-)established (cable connected after the interface is
* up, etc.), the link status may report no link. If there
* is no link, try switching modes and checking the status
- * again.
+ * again if auto negotiation is enabled.
*/
- check_again = 1;
+ check_again = (phydev->autoneg == AUTONEG_ENABLE) ? 1 : 0;
again:
/* Link status is latched low, so read once to clear
* and then read again to get current state
@@ -1038,8 +1096,10 @@ again:
phydev->link = (ret & MDIO_STAT1_LSTATUS) ? 1 : 0;
if (!phydev->link) {
- ret = amd_xgbe_phy_switch_mode(phydev);
if (check_again) {
+ ret = amd_xgbe_phy_switch_mode(phydev);
+ if (ret < 0)
+ return ret;
check_again = 0;
goto again;
}
@@ -1059,6 +1119,7 @@ again:
static int amd_xgbe_phy_read_status(struct phy_device *phydev)
{
+ struct amd_xgbe_phy_priv *priv = phydev->priv;
u32 mmd_mask = phydev->c45_ids.devices_in_package;
int ret, mode, ad_ret, lp_ret;
@@ -1108,9 +1169,19 @@ static int amd_xgbe_phy_read_status(struct phy_device *phydev)
return ret;
}
} else {
- phydev->speed = SPEED_1000;
+ int (*mode_fcn)(struct phy_device *);
+
+ if (priv->speed_set ==
+ AMD_XGBE_PHY_SPEEDSET_1000_10000) {
+ phydev->speed = SPEED_1000;
+ mode_fcn = amd_xgbe_phy_gmii_mode;
+ } else {
+ phydev->speed = SPEED_2500;
+ mode_fcn = amd_xgbe_phy_gmii_2500_mode;
+ }
+
if (mode == MDIO_PCS_CTRL2_10GBR) {
- ret = amd_xgbe_phy_gmii_mode(phydev);
+ ret = mode_fcn(phydev);
if (ret < 0)
return ret;
}
@@ -1118,8 +1189,15 @@ static int amd_xgbe_phy_read_status(struct phy_device *phydev)
phydev->duplex = DUPLEX_FULL;
} else {
- phydev->speed = (mode == MDIO_PCS_CTRL2_10GBR) ? SPEED_10000
- : SPEED_1000;
+ if (mode == MDIO_PCS_CTRL2_10GBR) {
+ phydev->speed = SPEED_10000;
+ } else {
+ if (priv->speed_set ==
+ AMD_XGBE_PHY_SPEEDSET_1000_10000)
+ phydev->speed = SPEED_1000;
+ else
+ phydev->speed = SPEED_2500;
+ }
phydev->duplex = DUPLEX_FULL;
phydev->pause = 0;
phydev->asym_pause = 0;
@@ -1176,6 +1254,8 @@ static int amd_xgbe_phy_probe(struct phy_device *phydev)
struct platform_device *pdev;
struct device *dev;
char *wq_name;
+ const __be32 *property;
+ unsigned int speed_set;
int ret;
if (!phydev->dev.of_node)
@@ -1227,6 +1307,26 @@ static int amd_xgbe_phy_probe(struct phy_device *phydev)
goto err_sir0;
}
+ /* Get the device speed set property */
+ speed_set = 0;
+ property = of_get_property(dev->of_node, XGBE_PHY_SPEEDSET_PROPERTY,
+ NULL);
+ if (property)
+ speed_set = be32_to_cpu(*property);
+
+ switch (speed_set) {
+ case 0:
+ priv->speed_set = AMD_XGBE_PHY_SPEEDSET_1000_10000;
+ break;
+ case 1:
+ priv->speed_set = AMD_XGBE_PHY_SPEEDSET_2500_10000;
+ break;
+ default:
+ dev_err(dev, "invalid amd,speed-set property\n");
+ ret = -EINVAL;
+ goto err_sir1;
+ }
+
priv->link = 1;
ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL2);
OpenPOWER on IntegriCloud