diff options
author | Lendacky, Thomas <Thomas.Lendacky@amd.com> | 2016-11-11 00:11:41 +0100 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2016-11-13 06:56:26 +0100 |
commit | d7445d1f0506fe26a877fac8dcdff7f4eff1328e (patch) | |
tree | d3a0d6adaa0cc72c1fc568721df940b032f2551d /drivers/net/ethernet/amd/xgbe | |
parent | amd-xgbe: Add support for MDIO attached PHYs (diff) | |
download | linux-d7445d1f0506fe26a877fac8dcdff7f4eff1328e.tar.xz linux-d7445d1f0506fe26a877fac8dcdff7f4eff1328e.zip |
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: Tom Lendacky <thomas.lendacky@amd.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/ethernet/amd/xgbe')
-rw-r--r-- | drivers/net/ethernet/amd/xgbe/xgbe-common.h | 10 | ||||
-rw-r--r-- | drivers/net/ethernet/amd/xgbe/xgbe-mdio.c | 45 | ||||
-rw-r--r-- | drivers/net/ethernet/amd/xgbe/xgbe-phy-v1.c | 7 | ||||
-rw-r--r-- | drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c | 427 | ||||
-rw-r--r-- | drivers/net/ethernet/amd/xgbe/xgbe.h | 6 |
5 files changed, 458 insertions, 37 deletions
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-common.h b/drivers/net/ethernet/amd/xgbe/xgbe-common.h index ecd4f4dce572..5b7ba25e0065 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-common.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe-common.h @@ -1062,6 +1062,16 @@ #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 +#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 */ #define IC_CON 0x0000 diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c index 622675ae4de6..0ecae7045044 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c @@ -179,6 +179,7 @@ static void xgbe_an_enable_interrupts(struct xgbe_prv_data *pdata) { switch (pdata->an_mode) { case XGBE_AN_MODE_CL73: + case XGBE_AN_MODE_CL73_REDRV: xgbe_an73_enable_interrupts(pdata); break; case XGBE_AN_MODE_CL37: @@ -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) { + /* If a KR re-driver is present, change to KR mode instead */ + if (pdata->kr_redrv) + return xgbe_kr_mode(pdata); + /* Disable KR training */ xgbe_an73_disable_kr_training(pdata); @@ -433,6 +438,7 @@ static void xgbe_an_restart(struct xgbe_prv_data *pdata) { switch (pdata->an_mode) { case XGBE_AN_MODE_CL73: + case XGBE_AN_MODE_CL73_REDRV: xgbe_an73_restart(pdata); break; case XGBE_AN_MODE_CL37: @@ -448,6 +454,7 @@ static void xgbe_an_disable(struct xgbe_prv_data *pdata) { switch (pdata->an_mode) { case XGBE_AN_MODE_CL73: + case XGBE_AN_MODE_CL73_REDRV: xgbe_an73_disable(pdata); break; case XGBE_AN_MODE_CL37: @@ -687,6 +694,7 @@ static irqreturn_t xgbe_an_isr(int irq, void *data) switch (pdata->an_mode) { case XGBE_AN_MODE_CL73: + case XGBE_AN_MODE_CL73_REDRV: xgbe_an73_isr(pdata); break; case XGBE_AN_MODE_CL37: @@ -895,6 +903,7 @@ static void xgbe_an_state_machine(struct work_struct *work) switch (pdata->an_mode) { case XGBE_AN_MODE_CL73: + case XGBE_AN_MODE_CL73_REDRV: xgbe_an73_state_machine(pdata); break; case XGBE_AN_MODE_CL37: @@ -910,16 +919,18 @@ static void xgbe_an_state_machine(struct work_struct *work) 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 */ reg = XMDIO_READ(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_ADVERTISE); - if (pdata->phy.advertising & ADVERTISED_Pause) + if (advertising & ADVERTISED_Pause) reg |= 0x100; else reg &= ~0x100; - if (pdata->phy.advertising & ADVERTISED_Asym_Pause) + if (advertising & ADVERTISED_Asym_Pause) reg |= 0x80; else reg &= ~0x80; @@ -954,11 +965,13 @@ static void xgbe_an37_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 */ 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; else reg &= ~0xc000; @@ -967,13 +980,13 @@ static void xgbe_an73_init(struct xgbe_prv_data *pdata) /* Set up Advertisement register 2 next */ 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; else reg &= ~0x80; - if ((pdata->phy.advertising & ADVERTISED_1000baseKX_Full) || - (pdata->phy.advertising & ADVERTISED_2500baseX_Full)) + if ((advertising & ADVERTISED_1000baseKX_Full) || + (advertising & ADVERTISED_2500baseX_Full)) reg |= 0x20; else reg &= ~0x20; @@ -982,12 +995,12 @@ static void xgbe_an73_init(struct xgbe_prv_data *pdata) /* Set up Advertisement register 1 last */ reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE); - if (pdata->phy.advertising & ADVERTISED_Pause) + if (advertising & ADVERTISED_Pause) reg |= 0x400; else reg &= ~0x400; - if (pdata->phy.advertising & ADVERTISED_Asym_Pause) + if (advertising & ADVERTISED_Asym_Pause) reg |= 0x800; else reg &= ~0x800; @@ -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); switch (pdata->an_mode) { case XGBE_AN_MODE_CL73: + case XGBE_AN_MODE_CL73_REDRV: xgbe_an73_init(pdata); break; case XGBE_AN_MODE_CL37: @@ -1149,10 +1163,15 @@ static int __xgbe_phy_config_aneg(struct xgbe_prv_data *pdata) if (ret) return ret; - if (pdata->phy.autoneg != AUTONEG_ENABLE) - return xgbe_phy_config_fixed(pdata); + if (pdata->phy.autoneg != AUTONEG_ENABLE) { + 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_irq(pdata->an_irq); diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v1.c b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v1.c index 6c64d11d0815..c75edcac5e0a 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v1.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v1.c @@ -295,6 +295,11 @@ static enum xgbe_mode xgbe_phy_an_outcome(struct xgbe_prv_data *pdata) 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) { /* Nothing uniquely required for an configuration */ @@ -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_advertising = xgbe_phy_an_advertising; + phy_impl->an_outcome = xgbe_phy_an_outcome; phy_impl->kr_training_pre = xgbe_phy_kr_training_pre; diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c index 9848f741f44f..4ba43328d99e 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c @@ -277,6 +277,26 @@ enum xgbe_mdio_reset { 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 */ struct xgbe_phy_data { enum xgbe_port_mode port_mode; @@ -327,6 +347,13 @@ struct xgbe_phy_data { enum xgbe_mdio_reset mdio_reset; unsigned int mdio_reset_addr; 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 */ @@ -346,6 +373,68 @@ static int xgbe_phy_i2c_xfer(struct xgbe_prv_data *pdata, 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, void *val, unsigned int val_len) { @@ -1144,38 +1233,49 @@ put: 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; - enum xgbe_mode mode; + u16 lcl_adv = 0, rmt_adv = 0; + u8 fc; - pdata->phy.lp_advertising |= ADVERTISED_Autoneg; - pdata->phy.lp_advertising |= ADVERTISED_TP; + pdata->phy.tx_pause = 0; + pdata->phy.rx_pause = 0; - if (pdata->phy.pause_autoneg && phy_data->phydev) { - /* Flow control is obtained from the attached PHY */ - u16 lcl_adv = 0, rmt_adv = 0; - u8 fc; + if (!phy_data->phydev) + return; - pdata->phy.tx_pause = 0; - pdata->phy.rx_pause = 0; + if (phy_data->phydev->advertising & ADVERTISED_Pause) + 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) - lcl_adv |= ADVERTISE_PAUSE_CAP; - if (phy_data->phydev->advertising & ADVERTISED_Asym_Pause) - lcl_adv |= ADVERTISE_PAUSE_ASYM; + if (phy_data->phydev->pause) { + pdata->phy.lp_advertising |= ADVERTISED_Pause; + rmt_adv |= LPA_PAUSE_CAP; + } + if (phy_data->phydev->asym_pause) { + pdata->phy.lp_advertising |= ADVERTISED_Asym_Pause; + rmt_adv |= LPA_PAUSE_ASYM; + } - if (phy_data->phydev->pause) - rmt_adv |= LPA_PAUSE_CAP; - if (phy_data->phydev->asym_pause) - rmt_adv |= LPA_PAUSE_ASYM; + fc = mii_resolve_flowctrl_fdx(lcl_adv, rmt_adv); + if (fc & FLOW_CTRL_TX) + pdata->phy.tx_pause = 1; + if (fc & FLOW_CTRL_RX) + pdata->phy.rx_pause = 1; +} - fc = mii_resolve_flowctrl_fdx(lcl_adv, rmt_adv); - if (fc & FLOW_CTRL_TX) - pdata->phy.tx_pause = 1; - if (fc & FLOW_CTRL_RX) - pdata->phy.rx_pause = 1; - } +static enum xgbe_mode xgbe_phy_an37_sgmii_outcome(struct xgbe_prv_data *pdata) +{ + enum xgbe_mode mode; + + 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) { case XGBE_SGMII_AN_LINK_SPEED_100: @@ -1249,6 +1349,83 @@ static enum xgbe_mode xgbe_phy_an37_outcome(struct xgbe_prv_data *pdata) 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) { enum xgbe_mode mode; @@ -1311,6 +1488,8 @@ static enum xgbe_mode xgbe_phy_an_outcome(struct xgbe_prv_data *pdata) switch (pdata->an_mode) { case XGBE_AN_MODE_CL73: return xgbe_phy_an73_outcome(pdata); + case XGBE_AN_MODE_CL73_REDRV: + return xgbe_phy_an73_redrv_outcome(pdata); case XGBE_AN_MODE_CL37: return xgbe_phy_an37_outcome(pdata); case XGBE_AN_MODE_CL37_SGMII: @@ -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) { 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) { 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) { case XGBE_PORT_MODE_BACKPLANE: return XGBE_AN_MODE_CL73; @@ -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) { 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) struct xgbe_phy_data *phy_data = pdata->phy_data; unsigned int s0; + xgbe_phy_set_redrv_mode(pdata); + xgbe_phy_start_ratechange(pdata); /* 10G/SFI */ @@ -1492,6 +1789,8 @@ static void xgbe_phy_x_mode(struct xgbe_prv_data *pdata) struct xgbe_phy_data *phy_data = pdata->phy_data; unsigned int s0; + xgbe_phy_set_redrv_mode(pdata); + xgbe_phy_start_ratechange(pdata); /* 1G/X */ @@ -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; unsigned int s0; + xgbe_phy_set_redrv_mode(pdata); + xgbe_phy_start_ratechange(pdata); /* 1G/SGMII */ @@ -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; unsigned int s0; + xgbe_phy_set_redrv_mode(pdata); + xgbe_phy_start_ratechange(pdata); /* 1G/SGMII */ @@ -1564,6 +1867,8 @@ static void xgbe_phy_kr_mode(struct xgbe_prv_data *pdata) struct xgbe_phy_data *phy_data = pdata->phy_data; unsigned int s0; + xgbe_phy_set_redrv_mode(pdata); + xgbe_phy_start_ratechange(pdata); /* 10G/KR */ @@ -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; unsigned int s0; + xgbe_phy_set_redrv_mode(pdata); + xgbe_phy_start_ratechange(pdata); /* 2.5G/KX */ @@ -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; unsigned int s0; + xgbe_phy_set_redrv_mode(pdata); + xgbe_phy_start_ratechange(pdata); /* 1G/KX */ @@ -2232,6 +2541,30 @@ static int xgbe_phy_mdio_reset(struct xgbe_prv_data *pdata) 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) { struct xgbe_phy_data *phy_data = pdata->phy_data; @@ -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); } + 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 */ if (xgbe_phy_conn_type_mismatch(pdata)) { 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) if (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 */ phy_data->cur_mode = XGBE_MODE_UNKNOWN; @@ -2651,6 +3005,29 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata) dev_dbg(pdata->dev, "phy supported=%#x\n", 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 */ mii = devm_mdiobus_alloc(pdata->dev); if (!mii) { @@ -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_advertising = xgbe_phy_an_advertising; + phy_impl->an_outcome = xgbe_phy_an_outcome; } diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h index 34db47094c61..f52a9bd05bac 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe.h @@ -508,6 +508,7 @@ enum xgbe_xpcs_access { enum xgbe_an_mode { XGBE_AN_MODE_CL73 = 0, + XGBE_AN_MODE_CL73_REDRV, XGBE_AN_MODE_CL37, XGBE_AN_MODE_CL37_SGMII, XGBE_AN_MODE_NONE, @@ -807,6 +808,9 @@ struct xgbe_phy_impl_if { /* Configure auto-negotiation settings */ 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 */ enum xgbe_mode (*an_outcome)(struct xgbe_prv_data *); @@ -1124,6 +1128,8 @@ struct xgbe_prv_data { unsigned long link_check; struct completion mdio_complete; + unsigned int kr_redrv; + char an_name[IFNAMSIZ + 32]; struct workqueue_struct *an_workqueue; |