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

amd-xgbe: Add support for a KR redriver

This patch provides support for the presence of a KR redriver chip in
between the device PCS and an external PHY.  When a redriver chip is
present the device must perform clause 73 auto-negotiation in order to
set the redriver chip for the downstream connection.
Signed-off-by: default avatarTom Lendacky <thomas.lendacky@amd.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 732f2ab7
...@@ -1062,6 +1062,16 @@ ...@@ -1062,6 +1062,16 @@
#define XP_PROP_4_MUX_ADDR_LO_WIDTH 3 #define XP_PROP_4_MUX_ADDR_LO_WIDTH 3
#define XP_PROP_4_MUX_CHAN_INDEX 4 #define XP_PROP_4_MUX_CHAN_INDEX 4
#define XP_PROP_4_MUX_CHAN_WIDTH 3 #define XP_PROP_4_MUX_CHAN_WIDTH 3
#define XP_PROP_4_REDRV_ADDR_INDEX 16
#define XP_PROP_4_REDRV_ADDR_WIDTH 7
#define XP_PROP_4_REDRV_IF_INDEX 23
#define XP_PROP_4_REDRV_IF_WIDTH 1
#define XP_PROP_4_REDRV_LANE_INDEX 24
#define XP_PROP_4_REDRV_LANE_WIDTH 3
#define XP_PROP_4_REDRV_MODEL_INDEX 28
#define XP_PROP_4_REDRV_MODEL_WIDTH 3
#define XP_PROP_4_REDRV_PRESENT_INDEX 31
#define XP_PROP_4_REDRV_PRESENT_WIDTH 1
/* I2C Control register offsets */ /* I2C Control register offsets */
#define IC_CON 0x0000 #define IC_CON 0x0000
......
...@@ -179,6 +179,7 @@ static void xgbe_an_enable_interrupts(struct xgbe_prv_data *pdata) ...@@ -179,6 +179,7 @@ static void xgbe_an_enable_interrupts(struct xgbe_prv_data *pdata)
{ {
switch (pdata->an_mode) { switch (pdata->an_mode) {
case XGBE_AN_MODE_CL73: case XGBE_AN_MODE_CL73:
case XGBE_AN_MODE_CL73_REDRV:
xgbe_an73_enable_interrupts(pdata); xgbe_an73_enable_interrupts(pdata);
break; break;
case XGBE_AN_MODE_CL37: case XGBE_AN_MODE_CL37:
...@@ -254,6 +255,10 @@ static void xgbe_kx_1000_mode(struct xgbe_prv_data *pdata) ...@@ -254,6 +255,10 @@ static void xgbe_kx_1000_mode(struct xgbe_prv_data *pdata)
static void xgbe_sfi_mode(struct xgbe_prv_data *pdata) static void 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);
/* Disable KR training */ /* Disable KR training */
xgbe_an73_disable_kr_training(pdata); xgbe_an73_disable_kr_training(pdata);
...@@ -433,6 +438,7 @@ static void xgbe_an_restart(struct xgbe_prv_data *pdata) ...@@ -433,6 +438,7 @@ static void xgbe_an_restart(struct xgbe_prv_data *pdata)
{ {
switch (pdata->an_mode) { switch (pdata->an_mode) {
case XGBE_AN_MODE_CL73: case XGBE_AN_MODE_CL73:
case XGBE_AN_MODE_CL73_REDRV:
xgbe_an73_restart(pdata); xgbe_an73_restart(pdata);
break; break;
case XGBE_AN_MODE_CL37: case XGBE_AN_MODE_CL37:
...@@ -448,6 +454,7 @@ static void xgbe_an_disable(struct xgbe_prv_data *pdata) ...@@ -448,6 +454,7 @@ static void xgbe_an_disable(struct xgbe_prv_data *pdata)
{ {
switch (pdata->an_mode) { switch (pdata->an_mode) {
case XGBE_AN_MODE_CL73: case XGBE_AN_MODE_CL73:
case XGBE_AN_MODE_CL73_REDRV:
xgbe_an73_disable(pdata); xgbe_an73_disable(pdata);
break; break;
case XGBE_AN_MODE_CL37: case XGBE_AN_MODE_CL37:
...@@ -687,6 +694,7 @@ static irqreturn_t xgbe_an_isr(int irq, void *data) ...@@ -687,6 +694,7 @@ static irqreturn_t xgbe_an_isr(int irq, void *data)
switch (pdata->an_mode) { switch (pdata->an_mode) {
case XGBE_AN_MODE_CL73: case XGBE_AN_MODE_CL73:
case XGBE_AN_MODE_CL73_REDRV:
xgbe_an73_isr(pdata); xgbe_an73_isr(pdata);
break; break;
case XGBE_AN_MODE_CL37: case XGBE_AN_MODE_CL37:
...@@ -895,6 +903,7 @@ static void xgbe_an_state_machine(struct work_struct *work) ...@@ -895,6 +903,7 @@ static void xgbe_an_state_machine(struct work_struct *work)
switch (pdata->an_mode) { switch (pdata->an_mode) {
case XGBE_AN_MODE_CL73: case XGBE_AN_MODE_CL73:
case XGBE_AN_MODE_CL73_REDRV:
xgbe_an73_state_machine(pdata); xgbe_an73_state_machine(pdata);
break; break;
case XGBE_AN_MODE_CL37: case XGBE_AN_MODE_CL37:
...@@ -910,16 +919,18 @@ static void xgbe_an_state_machine(struct work_struct *work) ...@@ -910,16 +919,18 @@ static void xgbe_an_state_machine(struct work_struct *work)
static void xgbe_an37_init(struct xgbe_prv_data *pdata) static void xgbe_an37_init(struct xgbe_prv_data *pdata)
{ {
unsigned int reg; unsigned int advertising, reg;
advertising = pdata->phy_if.phy_impl.an_advertising(pdata);
/* Set up Advertisement register */ /* Set up Advertisement register */
reg = XMDIO_READ(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_ADVERTISE); reg = XMDIO_READ(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_ADVERTISE);
if (pdata->phy.advertising & ADVERTISED_Pause) if (advertising & ADVERTISED_Pause)
reg |= 0x100; reg |= 0x100;
else else
reg &= ~0x100; reg &= ~0x100;
if (pdata->phy.advertising & ADVERTISED_Asym_Pause) if (advertising & ADVERTISED_Asym_Pause)
reg |= 0x80; reg |= 0x80;
else else
reg &= ~0x80; reg &= ~0x80;
...@@ -954,11 +965,13 @@ static void xgbe_an37_init(struct xgbe_prv_data *pdata) ...@@ -954,11 +965,13 @@ static void xgbe_an37_init(struct xgbe_prv_data *pdata)
static void xgbe_an73_init(struct xgbe_prv_data *pdata) static void xgbe_an73_init(struct xgbe_prv_data *pdata)
{ {
unsigned int reg; unsigned int advertising, reg;
advertising = pdata->phy_if.phy_impl.an_advertising(pdata);
/* 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 (pdata->phy.advertising & ADVERTISED_10000baseR_FEC) if (advertising & ADVERTISED_10000baseR_FEC)
reg |= 0xc000; reg |= 0xc000;
else else
reg &= ~0xc000; reg &= ~0xc000;
...@@ -967,13 +980,13 @@ static void xgbe_an73_init(struct xgbe_prv_data *pdata) ...@@ -967,13 +980,13 @@ static void xgbe_an73_init(struct xgbe_prv_data *pdata)
/* 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 (advertising & ADVERTISED_10000baseKR_Full)
reg |= 0x80; reg |= 0x80;
else else
reg &= ~0x80; reg &= ~0x80;
if ((pdata->phy.advertising & ADVERTISED_1000baseKX_Full) || if ((advertising & ADVERTISED_1000baseKX_Full) ||
(pdata->phy.advertising & ADVERTISED_2500baseX_Full)) (advertising & ADVERTISED_2500baseX_Full))
reg |= 0x20; reg |= 0x20;
else else
reg &= ~0x20; reg &= ~0x20;
...@@ -982,12 +995,12 @@ static void xgbe_an73_init(struct xgbe_prv_data *pdata) ...@@ -982,12 +995,12 @@ static void xgbe_an73_init(struct xgbe_prv_data *pdata)
/* 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 (advertising & ADVERTISED_Pause)
reg |= 0x400; reg |= 0x400;
else else
reg &= ~0x400; reg &= ~0x400;
if (pdata->phy.advertising & ADVERTISED_Asym_Pause) if (advertising & ADVERTISED_Asym_Pause)
reg |= 0x800; reg |= 0x800;
else else
reg &= ~0x800; reg &= ~0x800;
...@@ -1006,6 +1019,7 @@ static void xgbe_an_init(struct xgbe_prv_data *pdata) ...@@ -1006,6 +1019,7 @@ static void xgbe_an_init(struct xgbe_prv_data *pdata)
pdata->an_mode = pdata->phy_if.phy_impl.an_mode(pdata); pdata->an_mode = pdata->phy_if.phy_impl.an_mode(pdata);
switch (pdata->an_mode) { switch (pdata->an_mode) {
case XGBE_AN_MODE_CL73: case XGBE_AN_MODE_CL73:
case XGBE_AN_MODE_CL73_REDRV:
xgbe_an73_init(pdata); xgbe_an73_init(pdata);
break; break;
case XGBE_AN_MODE_CL37: case XGBE_AN_MODE_CL37:
...@@ -1149,10 +1163,15 @@ static int __xgbe_phy_config_aneg(struct xgbe_prv_data *pdata) ...@@ -1149,10 +1163,15 @@ static int __xgbe_phy_config_aneg(struct xgbe_prv_data *pdata)
if (ret) if (ret)
return ret; return ret;
if (pdata->phy.autoneg != AUTONEG_ENABLE) if (pdata->phy.autoneg != AUTONEG_ENABLE) {
return xgbe_phy_config_fixed(pdata); ret = xgbe_phy_config_fixed(pdata);
if (ret || !pdata->kr_redrv)
return ret;
netif_dbg(pdata, link, pdata->netdev, "AN PHY configuration\n"); netif_dbg(pdata, link, pdata->netdev, "AN redriver support\n");
} else {
netif_dbg(pdata, link, pdata->netdev, "AN PHY configuration\n");
}
/* Disable auto-negotiation interrupt */ /* Disable auto-negotiation interrupt */
disable_irq(pdata->an_irq); disable_irq(pdata->an_irq);
......
...@@ -295,6 +295,11 @@ static enum xgbe_mode xgbe_phy_an_outcome(struct xgbe_prv_data *pdata) ...@@ -295,6 +295,11 @@ static enum xgbe_mode xgbe_phy_an_outcome(struct xgbe_prv_data *pdata)
return mode; return mode;
} }
static unsigned int xgbe_phy_an_advertising(struct xgbe_prv_data *pdata)
{
return pdata->phy.advertising;
}
static int xgbe_phy_an_config(struct xgbe_prv_data *pdata) static int xgbe_phy_an_config(struct xgbe_prv_data *pdata)
{ {
/* Nothing uniquely required for an configuration */ /* Nothing uniquely required for an configuration */
...@@ -831,6 +836,8 @@ void xgbe_init_function_ptrs_phy_v1(struct xgbe_phy_if *phy_if) ...@@ -831,6 +836,8 @@ void xgbe_init_function_ptrs_phy_v1(struct xgbe_phy_if *phy_if)
phy_impl->an_config = xgbe_phy_an_config; phy_impl->an_config = xgbe_phy_an_config;
phy_impl->an_advertising = xgbe_phy_an_advertising;
phy_impl->an_outcome = xgbe_phy_an_outcome; phy_impl->an_outcome = xgbe_phy_an_outcome;
phy_impl->kr_training_pre = xgbe_phy_kr_training_pre; phy_impl->kr_training_pre = xgbe_phy_kr_training_pre;
......
...@@ -277,6 +277,26 @@ enum xgbe_mdio_reset { ...@@ -277,6 +277,26 @@ enum xgbe_mdio_reset {
XGBE_MDIO_RESET_MAX, XGBE_MDIO_RESET_MAX,
}; };
/* Re-driver related definitions */
enum xgbe_phy_redrv_if {
XGBE_PHY_REDRV_IF_MDIO = 0,
XGBE_PHY_REDRV_IF_I2C,
XGBE_PHY_REDRV_IF_MAX,
};
enum xgbe_phy_redrv_model {
XGBE_PHY_REDRV_MODEL_4223 = 0,
XGBE_PHY_REDRV_MODEL_4227,
XGBE_PHY_REDRV_MODEL_MAX,
};
enum xgbe_phy_redrv_mode {
XGBE_PHY_REDRV_MODE_CX = 5,
XGBE_PHY_REDRV_MODE_SR = 9,
};
#define XGBE_PHY_REDRV_MODE_REG 0x12b0
/* PHY related configuration information */ /* PHY related configuration information */
struct xgbe_phy_data { struct xgbe_phy_data {
enum xgbe_port_mode port_mode; enum xgbe_port_mode port_mode;
...@@ -327,6 +347,13 @@ struct xgbe_phy_data { ...@@ -327,6 +347,13 @@ struct xgbe_phy_data {
enum xgbe_mdio_reset mdio_reset; enum xgbe_mdio_reset mdio_reset;
unsigned int mdio_reset_addr; unsigned int mdio_reset_addr;
unsigned int mdio_reset_gpio; unsigned int mdio_reset_gpio;
/* Re-driver support */
unsigned int redrv;
unsigned int redrv_if;
unsigned int redrv_addr;
unsigned int redrv_lane;
unsigned int redrv_model;
}; };
/* I2C, MDIO and GPIO lines are muxed, so only one device at a time */ /* I2C, MDIO and GPIO lines are muxed, so only one device at a time */
...@@ -346,6 +373,68 @@ static int xgbe_phy_i2c_xfer(struct xgbe_prv_data *pdata, ...@@ -346,6 +373,68 @@ static int xgbe_phy_i2c_xfer(struct xgbe_prv_data *pdata,
return pdata->i2c_if.i2c_xfer(pdata, i2c_op); return pdata->i2c_if.i2c_xfer(pdata, i2c_op);
} }
static int xgbe_phy_redrv_write(struct xgbe_prv_data *pdata, unsigned int reg,
unsigned int val)
{
struct xgbe_phy_data *phy_data = pdata->phy_data;
struct xgbe_i2c_op i2c_op;
__be16 *redrv_val;
u8 redrv_data[5], csum;
unsigned int i, retry;
int ret;
/* High byte of register contains read/write indicator */
redrv_data[0] = ((reg >> 8) & 0xff) << 1;
redrv_data[1] = reg & 0xff;
redrv_val = (__be16 *)&redrv_data[2];
*redrv_val = cpu_to_be16(val);
/* Calculate 1 byte checksum */
csum = 0;
for (i = 0; i < 4; i++) {
csum += redrv_data[i];
if (redrv_data[i] > csum)
csum++;
}
redrv_data[4] = ~csum;
retry = 1;
again1:
i2c_op.cmd = XGBE_I2C_CMD_WRITE;
i2c_op.target = phy_data->redrv_addr;
i2c_op.len = sizeof(redrv_data);
i2c_op.buf = redrv_data;
ret = xgbe_phy_i2c_xfer(pdata, &i2c_op);
if (ret) {
if ((ret == -EAGAIN) && retry--)
goto again1;
return ret;
}
retry = 1;
again2:
i2c_op.cmd = XGBE_I2C_CMD_READ;
i2c_op.target = phy_data->redrv_addr;
i2c_op.len = 1;
i2c_op.buf = redrv_data;
ret = xgbe_phy_i2c_xfer(pdata, &i2c_op);
if (ret) {
if ((ret == -EAGAIN) && retry--)
goto again2;
return ret;
}
if (redrv_data[0] != 0xff) {
netif_dbg(pdata, drv, pdata->netdev,
"Redriver write checksum error\n");
ret = -EIO;
}
return ret;
}
static int xgbe_phy_i2c_write(struct xgbe_prv_data *pdata, unsigned int target, static int xgbe_phy_i2c_write(struct xgbe_prv_data *pdata, unsigned int target,
void *val, unsigned int val_len) void *val, unsigned int val_len)
{ {
...@@ -1144,38 +1233,49 @@ static void xgbe_phy_sfp_detect(struct xgbe_prv_data *pdata) ...@@ -1144,38 +1233,49 @@ static void xgbe_phy_sfp_detect(struct xgbe_prv_data *pdata)
xgbe_phy_put_comm_ownership(pdata); xgbe_phy_put_comm_ownership(pdata);
} }
static enum xgbe_mode xgbe_phy_an37_sgmii_outcome(struct xgbe_prv_data *pdata) static void xgbe_phy_phydev_flowctrl(struct xgbe_prv_data *pdata)
{ {
struct xgbe_phy_data *phy_data = pdata->phy_data; struct xgbe_phy_data *phy_data = pdata->phy_data;
enum xgbe_mode mode; u16 lcl_adv = 0, rmt_adv = 0;
u8 fc;
pdata->phy.lp_advertising |= ADVERTISED_Autoneg; pdata->phy.tx_pause = 0;
pdata->phy.lp_advertising |= ADVERTISED_TP; pdata->phy.rx_pause = 0;
if (pdata->phy.pause_autoneg && phy_data->phydev) { if (!phy_data->phydev)
/* Flow control is obtained from the attached PHY */ return;
u16 lcl_adv = 0, rmt_adv = 0;
u8 fc;
pdata->phy.tx_pause = 0; if (phy_data->phydev->advertising & ADVERTISED_Pause)
pdata->phy.rx_pause = 0; lcl_adv |= ADVERTISE_PAUSE_CAP;
if (phy_data->phydev->advertising & ADVERTISED_Asym_Pause)
lcl_adv |= ADVERTISE_PAUSE_ASYM;
if (phy_data->phydev->advertising & ADVERTISED_Pause) if (phy_data->phydev->pause) {
lcl_adv |= ADVERTISE_PAUSE_CAP; pdata->phy.lp_advertising |= ADVERTISED_Pause;
if (phy_data->phydev->advertising & ADVERTISED_Asym_Pause) rmt_adv |= LPA_PAUSE_CAP;
lcl_adv |= ADVERTISE_PAUSE_ASYM; }
if (phy_data->phydev->asym_pause) {
pdata->phy.lp_advertising |= ADVERTISED_Asym_Pause;
rmt_adv |= LPA_PAUSE_ASYM;
}
if (phy_data->phydev->pause) fc = mii_resolve_flowctrl_fdx(lcl_adv, rmt_adv);
rmt_adv |= LPA_PAUSE_CAP; if (fc & FLOW_CTRL_TX)
if (phy_data->phydev->asym_pause) pdata->phy.tx_pause = 1;
rmt_adv |= LPA_PAUSE_ASYM; if (fc & FLOW_CTRL_RX)
pdata->phy.rx_pause = 1;
}
fc = mii_resolve_flowctrl_fdx(lcl_adv, rmt_adv); static enum xgbe_mode xgbe_phy_an37_sgmii_outcome(struct xgbe_prv_data *pdata)
if (fc & FLOW_CTRL_TX) {
pdata->phy.tx_pause = 1; enum xgbe_mode mode;
if (fc & FLOW_CTRL_RX)
pdata->phy.rx_pause = 1; pdata->phy.lp_advertising |= ADVERTISED_Autoneg;
} pdata->phy.lp_advertising |= ADVERTISED_TP;
/* Use external PHY to determine flow control */
if (pdata->phy.pause_autoneg)
xgbe_phy_phydev_flowctrl(pdata);
switch (pdata->an_status & XGBE_SGMII_AN_LINK_SPEED) { switch (pdata->an_status & XGBE_SGMII_AN_LINK_SPEED) {
case XGBE_SGMII_AN_LINK_SPEED_100: case XGBE_SGMII_AN_LINK_SPEED_100:
...@@ -1249,6 +1349,83 @@ static enum xgbe_mode xgbe_phy_an37_outcome(struct xgbe_prv_data *pdata) ...@@ -1249,6 +1349,83 @@ static enum xgbe_mode xgbe_phy_an37_outcome(struct xgbe_prv_data *pdata)
return mode; return mode;
} }
static enum xgbe_mode xgbe_phy_an73_redrv_outcome(struct xgbe_prv_data *pdata)
{
struct xgbe_phy_data *phy_data = pdata->phy_data;
enum xgbe_mode mode;
unsigned int ad_reg, lp_reg;
pdata->phy.lp_advertising |= ADVERTISED_Autoneg;
pdata->phy.lp_advertising |= ADVERTISED_Backplane;
/* Use external PHY to determine flow control */
if (pdata->phy.pause_autoneg)
xgbe_phy_phydev_flowctrl(pdata);
/* 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)
pdata->phy.lp_advertising |= ADVERTISED_1000baseKX_Full;
ad_reg &= lp_reg;
if (ad_reg & 0x80) {
switch (phy_data->port_mode) {
case XGBE_PORT_MODE_BACKPLANE:
mode = XGBE_MODE_KR;
break;
default:
mode = XGBE_MODE_SFI;
break;
}
} else if (ad_reg & 0x20) {
switch (phy_data->port_mode) {
case XGBE_PORT_MODE_BACKPLANE:
mode = XGBE_MODE_KX_1000;
break;
case XGBE_PORT_MODE_1000BASE_X:
mode = XGBE_MODE_X;
break;
case XGBE_PORT_MODE_SFP:
switch (phy_data->sfp_base) {
case XGBE_SFP_BASE_1000_T:
if (phy_data->phydev &&
(phy_data->phydev->speed == SPEED_100))
mode = XGBE_MODE_SGMII_100;
else
mode = XGBE_MODE_SGMII_1000;
break;
case XGBE_SFP_BASE_1000_SX:
case XGBE_SFP_BASE_1000_LX:
case XGBE_SFP_BASE_1000_CX:
default:
mode = XGBE_MODE_X;
break;
}
break;
default:
if (phy_data->phydev &&
(phy_data->phydev->speed == SPEED_100))
mode = XGBE_MODE_SGMII_100;
else
mode = XGBE_MODE_SGMII_1000;
break;
}
} else {
mode = XGBE_MODE_UNKNOWN;
}
/* Compare Advertisement and Link Partner register 3 */
ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2);
lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA + 2);
if (lp_reg & 0xc000)
pdata->phy.lp_advertising |= ADVERTISED_10000baseR_FEC;
return mode;
}
static enum xgbe_mode xgbe_phy_an73_outcome(struct xgbe_prv_data *pdata) static enum xgbe_mode xgbe_phy_an73_outcome(struct xgbe_prv_data *pdata)
{ {
enum xgbe_mode mode; enum xgbe_mode mode;
...@@ -1311,6 +1488,8 @@ static enum xgbe_mode xgbe_phy_an_outcome(struct xgbe_prv_data *pdata) ...@@ -1311,6 +1488,8 @@ static enum xgbe_mode xgbe_phy_an_outcome(struct xgbe_prv_data *pdata)
switch (pdata->an_mode) { switch (pdata->an_mode) {
case XGBE_AN_MODE_CL73: case XGBE_AN_MODE_CL73:
return xgbe_phy_an73_outcome(pdata); return xgbe_phy_an73_outcome(pdata);
case XGBE_AN_MODE_CL73_REDRV:
return xgbe_phy_an73_redrv_outcome(pdata);
case XGBE_AN_MODE_CL37: case XGBE_AN_MODE_CL37:
return xgbe_phy_an37_outcome(pdata); return xgbe_phy_an37_outcome(pdata);
case XGBE_AN_MODE_CL37_SGMII: case XGBE_AN_MODE_CL37_SGMII:
...@@ -1320,6 +1499,63 @@ static enum xgbe_mode xgbe_phy_an_outcome(struct xgbe_prv_data *pdata) ...@@ -1320,6 +1499,63 @@ static enum xgbe_mode xgbe_phy_an_outcome(struct xgbe_prv_data *pdata)
} }
} }
static unsigned int xgbe_phy_an_advertising(struct xgbe_prv_data *pdata)
{
struct xgbe_phy_data *phy_data = pdata->phy_data;
unsigned int advertising;
/* Without a re-driver, just return current advertising */
if (!phy_data->redrv)
return pdata->phy.advertising;
/* With the KR re-driver we need to advertise a single speed */
advertising = pdata->phy.advertising;
advertising &= ~ADVERTISED_1000baseKX_Full;
advertising &= ~ADVERTISED_10000baseKR_Full;
switch (phy_data->port_mode) {
case XGBE_PORT_MODE_BACKPLANE:
advertising |= ADVERTISED_10000baseKR_Full;
break;
case XGBE_PORT_MODE_BACKPLANE_2500:
advertising |= ADVERTISED_1000baseKX_Full;
break;
case XGBE_PORT_MODE_1000BASE_T:
case XGBE_PORT_MODE_1000BASE_X:
case XGBE_PORT_MODE_NBASE_T:
advertising |= ADVERTISED_1000baseKX_Full;
break;
case XGBE_PORT_MODE_10GBASE_T:
if (phy_data->phydev &&
(phy_data->phydev->speed == SPEED_10000))
advertising |= ADVERTISED_10000baseKR_Full;
else
advertising |= ADVERTISED_1000baseKX_Full;
break;
case XGBE_PORT_MODE_10GBASE_R:
advertising |= ADVERTISED_10000baseKR_Full;
break;
case XGBE_PORT_MODE_SFP:
switch (phy_data->sfp_base) {
case XGBE_SFP_BASE_1000_T:
case XGBE_SFP_BASE_1000_SX:
case XGBE_SFP_BASE_1000_LX:
case XGBE_SFP_BASE_1000_CX:
advertising |= ADVERTISED_1000baseKX_Full;
break;
default:
advertising |= ADVERTISED_10000baseKR_Full;
break;
}
break;
default:
advertising |= ADVERTISED_10000baseKR_Full;
break;
}
return advertising;
}
static int xgbe_phy_an_config(struct xgbe_prv_data *pdata) static int xgbe_phy_an_config(struct xgbe_prv_data *pdata)
{ {
struct xgbe_phy_data *phy_data = pdata->phy_data; struct xgbe_phy_data *phy_data = pdata->phy_data;
...@@ -1364,6 +1600,10 @@ static enum xgbe_an_mode xgbe_phy_an_mode(struct xgbe_prv_data *pdata) ...@@ -1364,6 +1600,10 @@ static enum xgbe_an_mode xgbe_phy_an_mode(struct xgbe_prv_data *pdata)
{ {
struct xgbe_phy_data *phy_data = pdata->phy_data; struct xgbe_phy_data *phy_data = pdata->phy_data;
/* A KR re-driver will always require CL73 AN */
if (phy_data->redrv)
return XGBE_AN_MODE_CL73_REDRV;
switch (phy_data->port_mode) { switch (phy_data->port_mode) {
case XGBE_PORT_MODE_BACKPLANE: case XGBE_PORT_MODE_BACKPLANE:
return XGBE_AN_MODE_CL73; return XGBE_AN_MODE_CL73;
...@@ -1386,6 +1626,61 @@ static enum xgbe_an_mode xgbe_phy_an_mode(struct xgbe_prv_data *pdata) ...@@ -1386,6 +1626,61 @@ static enum xgbe_an_mode xgbe_phy_an_mode(struct xgbe_prv_data *pdata)
} }
} }
static int xgbe_phy_set_redrv_mode_mdio(struct xgbe_prv_data *pdata,
enum xgbe_phy_redrv_mode mode)
{
struct xgbe_phy_data *phy_data = pdata->phy_data;
u16 redrv_reg, redrv_val;
redrv_reg = XGBE_PHY_REDRV_MODE_REG + (phy_data->redrv_lane * 0x1000);
redrv_val = (u16)mode;
return pdata->hw_if.write_ext_mii_regs(pdata, phy_data->redrv_addr,
redrv_reg, redrv_val);
}
static int xgbe_phy_set_redrv_mode_i2c(struct xgbe_prv_data *pdata,
enum xgbe_phy_redrv_mode mode)
{
struct xgbe_phy_data *phy_data = pdata->phy_data;
unsigned int redrv_reg;
int ret;
/* Calculate the register to write */
redrv_reg = XGBE_PHY_REDRV_MODE_REG + (phy_data->redrv_lane * 0x1000);
ret = xgbe_phy_redrv_write(pdata, redrv_reg, mode);
return ret;
}
static void xgbe_phy_set_redrv_mode(struct xgbe_prv_data *pdata)
{
struct xgbe_phy_data *phy_data = pdata->phy_data;
enum xgbe_phy_redrv_mode mode;
int ret;
if (!phy_data->redrv)
return;
mode = XGBE_PHY_REDRV_MODE_CX;
if ((phy_data->port_mode == XGBE_PORT_MODE_SFP) &&
(phy_data->sfp_base != XGBE_SFP_BASE_1000_CX) &&
(phy_data->sfp_base != XGBE_SFP_BASE_10000_CR))
mode = XGBE_PHY_REDRV_MODE_SR;
ret = xgbe_phy_get_comm_ownership(pdata);
if (ret)
return;
if (phy_data->redrv_if)
xgbe_phy_set_redrv_mode_i2c(pdata, mode);
else
xgbe_phy_set_redrv_mode_mdio(pdata, mode);
xgbe_phy_put_comm_ownership(pdata);
}
static void xgbe_phy_start_ratechange(struct xgbe_prv_data *pdata) static void xgbe_phy_start_ratechange(struct xgbe_prv_data *pdata)
{ {
if (!XP_IOREAD_BITS(pdata, XP_DRIVER_INT_RO, STATUS)) if (!XP_IOREAD_BITS(pdata, XP_DRIVER_INT_RO, STATUS))
...@@ -1457,6 +1752,8 @@ static void xgbe_phy_sfi_mode(struct xgbe_prv_data *pdata) ...@@ -1457,6 +1752,8 @@ static void xgbe_phy_sfi_mode(struct xgbe_prv_data *pdata)
struct xgbe_phy_data *phy_data = pdata->phy_data; struct xgbe_phy_data *phy_data = pdata->phy_data;
unsigned int s0; unsigned int s0;
xgbe_phy_set_redrv_mode(pdata);
xgbe_phy_start_ratechange(pdata); xgbe_phy_start_ratechange(pdata);
/* 10G/SFI */ /* 10G/SFI */
...@@ -1492,6 +1789,8 @@ static void xgbe_phy_x_mode(struct xgbe_prv_data *pdata) ...@@ -1492,6 +1789,8 @@ static void xgbe_phy_x_mode(struct xgbe_prv_data *pdata)
struct xgbe_phy_data *phy_data = pdata->phy_data; struct xgbe_phy_data *phy_data = pdata->phy_data;
unsigned int s0; unsigned int s0;
xgbe_phy_set_redrv_mode(pdata);
xgbe_phy_start_ratechange(pdata); xgbe_phy_start_ratechange(pdata);
/* 1G/X */ /* 1G/X */
...@@ -1516,6 +1815,8 @@ static void xgbe_phy_sgmii_1000_mode(struct xgbe_prv_data *pdata) ...@@ -1516,6 +1815,8 @@ static void xgbe_phy_sgmii_1000_mode(struct xgbe_prv_data *pdata)
struct xgbe_phy_data *phy_data = pdata->phy_data; struct xgbe_phy_data *phy_data = pdata->phy_data;
unsigned int s0; unsigned int s0;
xgbe_phy_set_redrv_mode(pdata);
xgbe_phy_start_ratechange(pdata); xgbe_phy_start_ratechange(pdata);
/* 1G/SGMII */ /* 1G/SGMII */
...@@ -1540,6 +1841,8 @@ static void xgbe_phy_sgmii_100_mode(struct xgbe_prv_data *pdata) ...@@ -1540,6 +1841,8 @@ static void xgbe_phy_sgmii_100_mode(struct xgbe_prv_data *pdata)
struct xgbe_phy_data *phy_data = pdata->phy_data; struct xgbe_phy_data *phy_data = pdata->phy_data;
unsigned int s0; unsigned int s0;
xgbe_phy_set_redrv_mode(pdata);
xgbe_phy_start_ratechange(pdata); xgbe_phy_start_ratechange(pdata);
/* 1G/SGMII */ /* 1G/SGMII */
...@@ -1564,6 +1867,8 @@ static void xgbe_phy_kr_mode(struct xgbe_prv_data *pdata) ...@@ -1564,6 +1867,8 @@ static void xgbe_phy_kr_mode(struct xgbe_prv_data *pdata)
struct xgbe_phy_data *phy_data = pdata->phy_data; struct xgbe_phy_data *phy_data = pdata->phy_data;
unsigned int s0; unsigned int s0;
xgbe_phy_set_redrv_mode(pdata);
xgbe_phy_start_ratechange(pdata); xgbe_phy_start_ratechange(pdata);
/* 10G/KR */ /* 10G/KR */
...@@ -1588,6 +1893,8 @@ static void xgbe_phy_kx_2500_mode(struct xgbe_prv_data *pdata) ...@@ -1588,6 +1893,8 @@ static void xgbe_phy_kx_2500_mode(struct xgbe_prv_data *pdata)
struct xgbe_phy_data *phy_data = pdata->phy_data; struct xgbe_phy_data *phy_data = pdata->phy_data;
unsigned int s0; unsigned int s0;
xgbe_phy_set_redrv_mode(pdata);
xgbe_phy_start_ratechange(pdata); xgbe_phy_start_ratechange(pdata);
/* 2.5G/KX */ /* 2.5G/KX */
...@@ -1612,6 +1919,8 @@ static void xgbe_phy_kx_1000_mode(struct xgbe_prv_data *pdata) ...@@ -1612,6 +1919,8 @@ static void xgbe_phy_kx_1000_mode(struct xgbe_prv_data *pdata)
struct xgbe_phy_data *phy_data = pdata->phy_data; struct xgbe_phy_data *phy_data = pdata->phy_data;
unsigned int s0; unsigned int s0;
xgbe_phy_set_redrv_mode(pdata);
xgbe_phy_start_ratechange(pdata); xgbe_phy_start_ratechange(pdata);
/* 1G/KX */ /* 1G/KX */
...@@ -2232,6 +2541,30 @@ static int xgbe_phy_mdio_reset(struct xgbe_prv_data *pdata) ...@@ -2232,6 +2541,30 @@ static int xgbe_phy_mdio_reset(struct xgbe_prv_data *pdata)
return ret; return ret;
} }
static bool xgbe_phy_redrv_error(struct xgbe_phy_data *phy_data)
{
if (!phy_data->redrv)
return false;
if (phy_data->redrv_if >= XGBE_PHY_REDRV_IF_MAX)
return true;
switch (phy_data->redrv_model) {
case XGBE_PHY_REDRV_MODEL_4223:
if (phy_data->redrv_lane > 3)
return true;
break;
case XGBE_PHY_REDRV_MODEL_4227:
if (phy_data->redrv_lane > 1)
return true;
break;
default:
return true;
}
return false;
}
static int xgbe_phy_mdio_reset_setup(struct xgbe_prv_data *pdata) static int xgbe_phy_mdio_reset_setup(struct xgbe_prv_data *pdata)
{ {
struct xgbe_phy_data *phy_data = pdata->phy_data; struct xgbe_phy_data *phy_data = pdata->phy_data;
...@@ -2481,6 +2814,20 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata) ...@@ -2481,6 +2814,20 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata)
dev_dbg(pdata->dev, "mdio addr=%u\n", phy_data->mdio_addr); dev_dbg(pdata->dev, "mdio addr=%u\n", phy_data->mdio_addr);
} }
reg = XP_IOREAD(pdata, XP_PROP_4);
phy_data->redrv = XP_GET_BITS(reg, XP_PROP_4, REDRV_PRESENT);
phy_data->redrv_if = XP_GET_BITS(reg, XP_PROP_4, REDRV_IF);
phy_data->redrv_addr = XP_GET_BITS(reg, XP_PROP_4, REDRV_ADDR);
phy_data->redrv_lane = XP_GET_BITS(reg, XP_PROP_4, REDRV_LANE);
phy_data->redrv_model = XP_GET_BITS(reg, XP_PROP_4, REDRV_MODEL);
if (phy_data->redrv && netif_msg_probe(pdata)) {
dev_dbg(pdata->dev, "redrv present\n");
dev_dbg(pdata->dev, "redrv i/f=%u\n", phy_data->redrv_if);
dev_dbg(pdata->dev, "redrv addr=%#x\n", phy_data->redrv_addr);
dev_dbg(pdata->dev, "redrv lane=%u\n", phy_data->redrv_lane);
dev_dbg(pdata->dev, "redrv model=%u\n", phy_data->redrv_model);
}
/* Validate the connection requested */ /* Validate the connection requested */
if (xgbe_phy_conn_type_mismatch(pdata)) { if (xgbe_phy_conn_type_mismatch(pdata)) {
dev_err(pdata->dev, "phy mode/connection mismatch (%#x/%#x)\n", dev_err(pdata->dev, "phy mode/connection mismatch (%#x/%#x)\n",
...@@ -2499,6 +2846,13 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata) ...@@ -2499,6 +2846,13 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata)
if (ret) if (ret)
return ret; return ret;
/* Validate the re-driver information */
if (xgbe_phy_redrv_error(phy_data)) {
dev_err(pdata->dev, "phy re-driver settings error\n");
return -EINVAL;
}
pdata->kr_redrv = phy_data->redrv;
/* Indicate current mode is unknown */ /* Indicate current mode is unknown */
phy_data->cur_mode = XGBE_MODE_UNKNOWN; phy_data->cur_mode = XGBE_MODE_UNKNOWN;
...@@ -2651,6 +3005,29 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata) ...@@ -2651,6 +3005,29 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata)
dev_dbg(pdata->dev, "phy supported=%#x\n", dev_dbg(pdata->dev, "phy supported=%#x\n",
pdata->phy.supported); pdata->phy.supported);
if ((phy_data->conn_type & XGBE_CONN_TYPE_MDIO) &&
(phy_data->phydev_mode != XGBE_MDIO_MODE_NONE)) {
ret = pdata->hw_if.set_ext_mii_mode(pdata, phy_data->mdio_addr,
phy_data->phydev_mode);
if (ret) {
dev_err(pdata->dev,
"mdio port/clause not compatible (%d/%u)\n",
phy_data->mdio_addr, phy_data->phydev_mode);
return -EINVAL;
}
}
if (phy_data->redrv && !phy_data->redrv_if) {
ret = pdata->hw_if.set_ext_mii_mode(pdata, phy_data->redrv_addr,
XGBE_MDIO_MODE_CL22);
if (ret) {
dev_err(pdata->dev,
"redriver mdio port not compatible (%u)\n",
phy_data->redrv_addr);
return -EINVAL;
}
}
/* Register for driving external PHYs */ /* Register for driving external PHYs */
mii = devm_mdiobus_alloc(pdata->dev); mii = devm_mdiobus_alloc(pdata->dev);
if (!mii) { if (!mii) {
...@@ -2700,5 +3077,7 @@ void xgbe_init_function_ptrs_phy_v2(struct xgbe_phy_if *phy_if) ...@@ -2700,5 +3077,7 @@ void xgbe_init_function_ptrs_phy_v2(struct xgbe_phy_if *phy_if)
phy_impl->an_config = xgbe_phy_an_config; phy_impl->an_config = xgbe_phy_an_config;
phy_impl->an_advertising = xgbe_phy_an_advertising;
phy_impl->an_outcome = xgbe_phy_an_outcome; phy_impl->an_outcome = xgbe_phy_an_outcome;
} }
...@@ -508,6 +508,7 @@ enum xgbe_xpcs_access { ...@@ -508,6 +508,7 @@ enum xgbe_xpcs_access {
enum xgbe_an_mode { enum xgbe_an_mode {
XGBE_AN_MODE_CL73 = 0, XGBE_AN_MODE_CL73 = 0,
XGBE_AN_MODE_CL73_REDRV,
XGBE_AN_MODE_CL37, XGBE_AN_MODE_CL37,
XGBE_AN_MODE_CL37_SGMII, XGBE_AN_MODE_CL37_SGMII,
XGBE_AN_MODE_NONE, XGBE_AN_MODE_NONE,
...@@ -807,6 +808,9 @@ struct xgbe_phy_impl_if { ...@@ -807,6 +808,9 @@ struct xgbe_phy_impl_if {
/* Configure auto-negotiation settings */ /* Configure auto-negotiation settings */
int (*an_config)(struct xgbe_prv_data *); int (*an_config)(struct xgbe_prv_data *);
/* Set/override auto-negotiation advertisement settings */
unsigned int (*an_advertising)(struct xgbe_prv_data *);
/* Process results of auto-negotiation */ /* Process results of auto-negotiation */
enum xgbe_mode (*an_outcome)(struct xgbe_prv_data *); enum xgbe_mode (*an_outcome)(struct xgbe_prv_data *);
...@@ -1124,6 +1128,8 @@ struct xgbe_prv_data { ...@@ -1124,6 +1128,8 @@ struct xgbe_prv_data {
unsigned long link_check; unsigned long link_check;
struct completion mdio_complete; struct completion mdio_complete;
unsigned int kr_redrv;
char an_name[IFNAMSIZ + 32]; char an_name[IFNAMSIZ + 32];
struct workqueue_struct *an_workqueue; struct workqueue_struct *an_workqueue;
......
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