diff --git a/drivers/net/phy/bcm84881.c b/drivers/net/phy/bcm84881.c index 2ae70dcf82ec..391f32d25a0b 100644 --- a/drivers/net/phy/bcm84881.c +++ b/drivers/net/phy/bcm84881.c @@ -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 @@ -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, @@ -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, @@ -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, diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index e1358a015442..93bb411240b8 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -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; @@ -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));