Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 35 additions & 1 deletion drivers/net/phy/bcm84881.c
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,24 @@ static int bcm84881_config_init(struct phy_device *phydev)

static int bcm8489x_config_init(struct phy_device *phydev)
{
bcm84881_fill_possible_interfaces(phydev);
__set_bit(PHY_INTERFACE_MODE_USXGMII, phydev->possible_interfaces);

if (phydev->interface != PHY_INTERFACE_MODE_USXGMII)
/* The BCM84891 is found both soldered down and attached over
* USXGMII, and inside SFP+ copper modules (e.g. various
* "OEM SFP-10G-T" RollBall modules), where the host-side
* interface is 10GBASE-R with SGMII/2500BASE-X rate switching,
* as with the BCM84881. Accept both attachments.
*/
switch (phydev->interface) {
case PHY_INTERFACE_MODE_SGMII:
case PHY_INTERFACE_MODE_2500BASEX:
case PHY_INTERFACE_MODE_10GBASER:
case PHY_INTERFACE_MODE_USXGMII:
break;
default:
return -ENODEV;
}

/* MDIO_CTRL1_LPOWER is set at boot on the tested platform. Does not
* recur on ifdown/ifup, cable events, or link-partner advertisement
Expand Down Expand Up @@ -416,12 +430,30 @@ static unsigned int bcm84881_inband_caps(struct phy_device *phydev,
return LINK_INBAND_DISABLE;
}

static int bcm84881_config_inband(struct phy_device *phydev,
unsigned int modes)
{
/* This PHY does not generate inband signalling in any mode (see
* bcm84881_inband_caps()); inband is permanently disabled in
* hardware. A request to disable inband therefore requires no
* action, but must succeed: phylink treats any error from
* phy_config_inband() (including -EOPNOTSUPP from a missing
* config_inband method) as a major configuration failure which
* forces and holds the link down.
*/
if (modes == LINK_INBAND_DISABLE)
return 0;

return -EINVAL;
}

static struct phy_driver bcm84881_drivers[] = {
{
.phy_id = 0xae025150,
.phy_id_mask = 0xfffffff0,
.name = "Broadcom BCM84881",
.inband_caps = bcm84881_inband_caps,
.config_inband = bcm84881_config_inband,
.config_init = bcm84881_config_init,
.probe = bcm84881_probe,
.get_features = bcm84881_get_features,
Expand All @@ -432,6 +464,7 @@ static struct phy_driver bcm84881_drivers[] = {
PHY_ID_MATCH_MODEL(0x35905080),
.name = "Broadcom BCM84891",
.inband_caps = bcm84881_inband_caps,
.config_inband = bcm84881_config_inband,
.config_init = bcm8489x_config_init,
.probe = bcm84881_probe,
.get_features = bcm84881_get_features,
Expand All @@ -446,6 +479,7 @@ static struct phy_driver bcm84881_drivers[] = {
PHY_ID_MATCH_MODEL(0x359050a0),
.name = "Broadcom BCM84892",
.inband_caps = bcm84881_inband_caps,
.config_inband = bcm84881_config_inband,
.config_init = bcm8489x_config_init,
.probe = bcm84881_probe,
.get_features = bcm84881_get_features,
Expand Down
76 changes: 76 additions & 0 deletions drivers/net/phy/sfp.c
Original file line number Diff line number Diff line change
Expand Up @@ -1938,6 +1938,75 @@ static int sfp_sm_probe_phy(struct sfp *sfp, int addr, bool is_c45)
return PTR_ERR(phy);
}

/* Some RollBall modules stop gating PHY register access (reads
* returning 0xffff) slightly before the PHY's identifier registers
* are stable. A probe landing in this window reads a plausible
* devices-in-package value but all-zero device identifiers, which
* no PHY driver can match. sfp_add_phy() then fails hard with
* -EINVAL and the state machine enters SFP_S_FAIL, requiring a
* physical re-plug. Treat all-zero identifiers as "PHY not ready"
* instead, so the existing R_PHY_RETRY/phy_t_retry logic applies.
*/
if (is_c45) {
u32 devs = phy->c45_ids.devices_in_package;
bool ids_valid = false;
int ret;
int i;

/* Absent MMDs leave their identifier zero; a gated or
* timed-out RollBall access reads 0xffff, so an identifier
* with either 16-bit half at 0xffff is corrupt. Require at
* least one fully plausible identifier before accepting the
* PHY.
*/
for (i = 1; i < ARRAY_SIZE(phy->c45_ids.device_ids); i++) {
u32 id = phy->c45_ids.device_ids[i];

if (id && (id & 0xffff) != 0xffff &&
(id >> 16) != 0xffff) {
ids_valid = true;
break;
}
}

/* devices_in_package selects which MMDs are enumerated, so a
* timed-out read leaving either 16-bit half at 0xffff makes
* the map itself untrustworthy -- one clean identifier is not
* enough to accept it. Seen on a BCM84891 that answered PMA
* (0x35905081) while the rest of the mailbox was still gated
* (devs_in_pkg 0xffff009b): the id-only check passed on the
* clean PMA, attached a half-awake PHY, and its reset poll
* then failed -ETIMEDOUT during attach.
*/
if (!ids_valid ||
(devs & 0xffff) == 0xffff || (devs >> 16) == 0xffff) {
dev_info(sfp->dev,
"PHY identifiers unstable (devs_in_pkg 0x%08x), retrying\n",
phy->c45_ids.devices_in_package);
phy_device_free(phy);
return -ENODEV;
}

/* Plausible identifiers do not guarantee the PMA/PMD control
* register is out of the gated window. phy_init_hw()'s soft
* reset during attach writes MDIO_CTRL1 and polls the reset
* bit to self-clear; a gated read returns 0xffff so the bit
* never clears and sfp_add_phy() fails -ETIMEDOUT (which has
* been seen to later fault the PHY state machine on interface
* teardown). Require that same register to read back live
* before attaching, so the retry logic waits out the wake
* window and the PHY is attached exactly once, cleanly.
*/
ret = phy_read_mmd(phy, MDIO_MMD_PMAPMD, MDIO_CTRL1);
if (ret < 0 || ret == 0xffff || (ret & MDIO_CTRL1_RESET)) {
dev_info(sfp->dev,
"PHY control register not ready (0x%04x), retrying\n",
ret);
phy_device_free(phy);
return -ENODEV;
}
}

/* Mark this PHY as being on a SFP module */
phy->is_on_sfp_module = true;

Expand All @@ -1951,6 +2020,13 @@ static int sfp_sm_probe_phy(struct sfp *sfp, int addr, bool is_c45)

err = sfp_add_phy(sfp->sfp_bus, phy);
if (err) {
if (is_c45)
dev_err(sfp->dev,
"PHY c45 ids: PMA 0x%08x PCS 0x%08x AN 0x%08x devs_in_pkg 0x%08x\n",
phy->c45_ids.device_ids[MDIO_MMD_PMAPMD],
phy->c45_ids.device_ids[MDIO_MMD_PCS],
phy->c45_ids.device_ids[MDIO_MMD_AN],
phy->c45_ids.devices_in_package);
phy_device_remove(phy);
phy_device_free(phy);
dev_err(sfp->dev, "sfp_add_phy failed: %pe\n", ERR_PTR(err));
Expand Down