amd-xgbe: Add support for SFP+ modules

Add support for recognizing and using SFP+ modules directly. This includes
using the I2C support to read and interpret the information returned from
an SFP+ module and configuring things properly.

Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Lendacky, Thomas 2016-11-10 17:10:58 -06:00 committed by David S. Miller
parent 372788f964
commit abf0a1c2b2
6 changed files with 1642 additions and 8 deletions

View File

@ -178,6 +178,7 @@ config AMD_XGBE
select BITREVERSE
select CRC32
select PTP_1588_CLOCK
select PHYLIB
select AMD_XGBE_HAVE_ECC if X86
---help---
This driver supports the AMD 10GbE Ethernet device found on an

View File

@ -929,6 +929,8 @@
#define XP_DRIVER_SCRATCH_0 0x0068
#define XP_DRIVER_SCRATCH_1 0x006c
#define XP_INT_EN 0x0078
#define XP_I2C_MUTEX 0x0080
#define XP_MDIO_MUTEX 0x0084
/* MAC Control register entry bit positions and sizes */
#define XP_DRIVER_INT_REQ_REQUEST_INDEX 0
@ -975,6 +977,12 @@
#define XP_ECC_ISR_TX_DED_WIDTH 1
#define XP_ECC_ISR_TX_SEC_INDEX 5
#define XP_ECC_ISR_TX_SEC_WIDTH 1
#define XP_I2C_MUTEX_BUSY_INDEX 31
#define XP_I2C_MUTEX_BUSY_WIDTH 1
#define XP_I2C_MUTEX_ID_INDEX 29
#define XP_I2C_MUTEX_ID_WIDTH 2
#define XP_I2C_MUTEX_ACTIVE_INDEX 0
#define XP_I2C_MUTEX_ACTIVE_WIDTH 1
#define XP_MAC_ADDR_HI_VALID_INDEX 31
#define XP_MAC_ADDR_HI_VALID_WIDTH 1
#define XP_PROP_0_CONN_TYPE_INDEX 28
@ -999,6 +1007,24 @@
#define XP_PROP_2_RX_FIFO_SIZE_WIDTH 16
#define XP_PROP_2_TX_FIFO_SIZE_INDEX 0
#define XP_PROP_2_TX_FIFO_SIZE_WIDTH 16
#define XP_PROP_3_GPIO_MASK_INDEX 28
#define XP_PROP_3_GPIO_MASK_WIDTH 4
#define XP_PROP_3_GPIO_MOD_ABS_INDEX 20
#define XP_PROP_3_GPIO_MOD_ABS_WIDTH 4
#define XP_PROP_3_GPIO_RATE_SELECT_INDEX 16
#define XP_PROP_3_GPIO_RATE_SELECT_WIDTH 4
#define XP_PROP_3_GPIO_RX_LOS_INDEX 24
#define XP_PROP_3_GPIO_RX_LOS_WIDTH 4
#define XP_PROP_3_GPIO_TX_FAULT_INDEX 12
#define XP_PROP_3_GPIO_TX_FAULT_WIDTH 4
#define XP_PROP_3_GPIO_ADDR_INDEX 8
#define XP_PROP_3_GPIO_ADDR_WIDTH 3
#define XP_PROP_4_MUX_ADDR_HI_INDEX 8
#define XP_PROP_4_MUX_ADDR_HI_WIDTH 5
#define XP_PROP_4_MUX_ADDR_LO_INDEX 0
#define XP_PROP_4_MUX_ADDR_LO_WIDTH 3
#define XP_PROP_4_MUX_CHAN_INDEX 4
#define XP_PROP_4_MUX_CHAN_WIDTH 3
/* I2C Control register offsets */
#define IC_CON 0x0000
@ -1235,6 +1261,14 @@
#define MDIO_VEND2_CTRL1_AN_RESTART BIT(9)
#endif
#ifndef MDIO_VEND2_CTRL1_SS6
#define MDIO_VEND2_CTRL1_SS6 BIT(6)
#endif
#ifndef MDIO_VEND2_CTRL1_SS13
#define MDIO_VEND2_CTRL1_SS13 BIT(13)
#endif
/* MDIO mask values */
#define XGBE_AN_CL73_INT_CMPLT BIT(0)
#define XGBE_AN_CL73_INC_LINK BIT(1)

View File

@ -252,6 +252,54 @@ static void xgbe_kx_1000_mode(struct xgbe_prv_data *pdata)
pdata->phy_if.phy_impl.set_mode(pdata, XGBE_MODE_KX_1000);
}
static void xgbe_sfi_mode(struct xgbe_prv_data *pdata)
{
/* Disable KR training */
xgbe_an73_disable_kr_training(pdata);
/* Set MAC to 10G speed */
pdata->hw_if.set_speed(pdata, SPEED_10000);
/* Call PHY implementation support to complete rate change */
pdata->phy_if.phy_impl.set_mode(pdata, XGBE_MODE_SFI);
}
static void xgbe_x_mode(struct xgbe_prv_data *pdata)
{
/* Disable KR training */
xgbe_an73_disable_kr_training(pdata);
/* Set MAC to 1G speed */
pdata->hw_if.set_speed(pdata, SPEED_1000);
/* Call PHY implementation support to complete rate change */
pdata->phy_if.phy_impl.set_mode(pdata, XGBE_MODE_X);
}
static void xgbe_sgmii_1000_mode(struct xgbe_prv_data *pdata)
{
/* Disable KR training */
xgbe_an73_disable_kr_training(pdata);
/* Set MAC to 1G speed */
pdata->hw_if.set_speed(pdata, SPEED_1000);
/* Call PHY implementation support to complete rate change */
pdata->phy_if.phy_impl.set_mode(pdata, XGBE_MODE_SGMII_1000);
}
static void xgbe_sgmii_100_mode(struct xgbe_prv_data *pdata)
{
/* Disable KR training */
xgbe_an73_disable_kr_training(pdata);
/* Set MAC to 1G speed */
pdata->hw_if.set_speed(pdata, SPEED_1000);
/* Call PHY implementation support to complete rate change */
pdata->phy_if.phy_impl.set_mode(pdata, XGBE_MODE_SGMII_100);
}
static enum xgbe_mode xgbe_cur_mode(struct xgbe_prv_data *pdata)
{
return pdata->phy_if.phy_impl.cur_mode(pdata);
@ -275,6 +323,18 @@ static void xgbe_change_mode(struct xgbe_prv_data *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:
@ -972,6 +1032,8 @@ static const char *xgbe_phy_fc_string(struct xgbe_prv_data *pdata)
static const char *xgbe_phy_speed_string(int speed)
{
switch (speed) {
case SPEED_100:
return "100Mbps";
case SPEED_1000:
return "1Gbps";
case SPEED_2500:
@ -1057,6 +1119,10 @@ static int xgbe_phy_config_fixed(struct xgbe_prv_data *pdata)
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:
@ -1074,9 +1140,15 @@ static int xgbe_phy_config_fixed(struct xgbe_prv_data *pdata)
static int __xgbe_phy_config_aneg(struct xgbe_prv_data *pdata)
{
int ret;
set_bit(XGBE_LINK_INIT, &pdata->dev_state);
pdata->link_check = jiffies;
ret = pdata->phy_if.phy_impl.an_config(pdata);
if (ret)
return ret;
if (pdata->phy.autoneg != AUTONEG_ENABLE)
return xgbe_phy_config_fixed(pdata);
@ -1092,6 +1164,14 @@ static int __xgbe_phy_config_aneg(struct xgbe_prv_data *pdata)
xgbe_set_mode(pdata, XGBE_MODE_KX_2500);
} 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 {
enable_irq(pdata->an_irq);
return -EINVAL;
@ -1167,13 +1247,19 @@ static void xgbe_phy_status_result(struct xgbe_prv_data *pdata)
mode = xgbe_phy_status_aneg(pdata);
switch (mode) {
case XGBE_MODE_SGMII_100:
pdata->phy.speed = SPEED_100;
break;
case XGBE_MODE_X:
case XGBE_MODE_KX_1000:
case XGBE_MODE_SGMII_1000:
pdata->phy.speed = SPEED_1000;
break;
case XGBE_MODE_KX_2500:
pdata->phy.speed = SPEED_2500;
break;
case XGBE_MODE_KR:
case XGBE_MODE_SFI:
pdata->phy.speed = SPEED_10000;
break;
case XGBE_MODE_UNKNOWN:
@ -1189,6 +1275,7 @@ static void xgbe_phy_status_result(struct xgbe_prv_data *pdata)
static void xgbe_phy_status(struct xgbe_prv_data *pdata)
{
unsigned int link_aneg;
int an_restart;
if (test_bit(XGBE_LINK_ERR, &pdata->dev_state)) {
netif_carrier_off(pdata->netdev);
@ -1199,7 +1286,13 @@ static void xgbe_phy_status(struct xgbe_prv_data *pdata)
link_aneg = (pdata->phy.autoneg == AUTONEG_ENABLE);
pdata->phy.link = pdata->phy_if.phy_impl.link_status(pdata);
pdata->phy.link = pdata->phy_if.phy_impl.link_status(pdata,
&an_restart);
if (an_restart) {
xgbe_phy_config_aneg(pdata);
return;
}
if (pdata->phy.link) {
if (link_aneg && !xgbe_phy_aneg_done(pdata)) {
xgbe_check_link_timeout(pdata);
@ -1284,6 +1377,14 @@ static int xgbe_phy_start(struct xgbe_prv_data *pdata)
xgbe_kx_2500_mode(pdata);
} else if (xgbe_use_mode(pdata, XGBE_MODE_KX_1000)) {
xgbe_kx_1000_mode(pdata);
} else if (xgbe_use_mode(pdata, XGBE_MODE_SFI)) {
xgbe_sfi_mode(pdata);
} else if (xgbe_use_mode(pdata, XGBE_MODE_X)) {
xgbe_x_mode(pdata);
} else if (xgbe_use_mode(pdata, XGBE_MODE_SGMII_1000)) {
xgbe_sgmii_1000_mode(pdata);
} else if (xgbe_use_mode(pdata, XGBE_MODE_SGMII_100)) {
xgbe_sgmii_100_mode(pdata);
} else {
ret = -EINVAL;
goto err_irq;
@ -1367,10 +1468,16 @@ static int xgbe_phy_best_advertised_speed(struct xgbe_prv_data *pdata)
{
if (pdata->phy.advertising & ADVERTISED_10000baseKR_Full)
return SPEED_10000;
else if (pdata->phy.advertising & ADVERTISED_10000baseT_Full)
return SPEED_10000;
else if (pdata->phy.advertising & ADVERTISED_2500baseX_Full)
return SPEED_2500;
else if (pdata->phy.advertising & ADVERTISED_1000baseKX_Full)
return SPEED_1000;
else if (pdata->phy.advertising & ADVERTISED_1000baseT_Full)
return SPEED_1000;
else if (pdata->phy.advertising & ADVERTISED_100baseT_Full)
return SPEED_100;
return SPEED_UNKNOWN;
}

View File

@ -295,6 +295,12 @@ static enum xgbe_mode xgbe_phy_an_outcome(struct xgbe_prv_data *pdata)
return mode;
}
static int xgbe_phy_an_config(struct xgbe_prv_data *pdata)
{
/* Nothing uniquely required for an configuration */
return 0;
}
static enum xgbe_an_mode xgbe_phy_an_mode(struct xgbe_prv_data *pdata)
{
return XGBE_AN_MODE_CL73;
@ -607,10 +613,12 @@ static bool xgbe_phy_valid_speed(struct xgbe_prv_data *pdata, int speed)
}
}
static int xgbe_phy_link_status(struct xgbe_prv_data *pdata)
static int xgbe_phy_link_status(struct xgbe_prv_data *pdata, int *an_restart)
{
unsigned int reg;
*an_restart = 0;
/* Link status is latched low, so read once to clear
* and then read again to get current state
*/
@ -821,6 +829,8 @@ void xgbe_init_function_ptrs_phy_v1(struct xgbe_phy_if *phy_if)
phy_impl->an_mode = xgbe_phy_an_mode;
phy_impl->an_config = xgbe_phy_an_config;
phy_impl->an_outcome = xgbe_phy_an_outcome;
phy_impl->kr_training_pre = xgbe_phy_kr_training_pre;

File diff suppressed because it is too large Load Diff

View File

@ -530,6 +530,10 @@ enum xgbe_mode {
XGBE_MODE_KX_1000 = 0,
XGBE_MODE_KX_2500,
XGBE_MODE_KR,
XGBE_MODE_X,
XGBE_MODE_SGMII_100,
XGBE_MODE_SGMII_1000,
XGBE_MODE_SFI,
XGBE_MODE_UNKNOWN,
};
@ -538,6 +542,12 @@ enum xgbe_speedset {
XGBE_SPEEDSET_2500_10000,
};
enum xgbe_mdio_mode {
XGBE_MDIO_MODE_NONE = 0,
XGBE_MDIO_MODE_CL22,
XGBE_MDIO_MODE_CL45,
};
struct xgbe_phy {
u32 supported;
u32 advertising;
@ -764,7 +774,7 @@ struct xgbe_phy_impl_if {
void (*stop)(struct xgbe_prv_data *);
/* Return the link status */
int (*link_status)(struct xgbe_prv_data *);
int (*link_status)(struct xgbe_prv_data *, int *);
/* Indicate if a particular speed is valid */
bool (*valid_speed)(struct xgbe_prv_data *, int);
@ -783,6 +793,9 @@ struct xgbe_phy_impl_if {
/* Retrieve current auto-negotiation mode */
enum xgbe_an_mode (*an_mode)(struct xgbe_prv_data *);
/* Configure auto-negotiation settings */
int (*an_config)(struct xgbe_prv_data *);
/* Process results of auto-negotiation */
enum xgbe_mode (*an_outcome)(struct xgbe_prv_data *);