From 28e74a7cfd6403f0d1c0f8b10b45d6fae37b227e Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 2 Jun 2019 15:13:00 +0100 Subject: net: sfp: read eeprom in maximum 16 byte increments Some SFP modules do not like reads longer than 16 bytes, so read the EEPROM in chunks of 16 bytes at a time. This behaviour is not specified in the SFP MSAs, which specifies: "The serial interface uses the 2-wire serial CMOS E2PROM protocol defined for the ATMEL AT24C01A/02/04 family of components." and "As long as the SFP+ receives an acknowledge, it shall serially clock out sequential data words. The sequence is terminated when the host responds with a NACK and a STOP instead of an acknowledge." We must avoid breaking a read across a 16-bit quantity in the diagnostic page, thankfully all 16-bit quantities in that page are naturally aligned. Signed-off-by: Russell King Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/phy/sfp.c | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) (limited to 'drivers/net/phy') diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index d4635c2178d1..71812be0ac64 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -281,6 +281,7 @@ static int sfp_i2c_read(struct sfp *sfp, bool a2, u8 dev_addr, void *buf, { struct i2c_msg msgs[2]; u8 bus_addr = a2 ? 0x51 : 0x50; + size_t this_len; int ret; msgs[0].addr = bus_addr; @@ -292,11 +293,26 @@ static int sfp_i2c_read(struct sfp *sfp, bool a2, u8 dev_addr, void *buf, msgs[1].len = len; msgs[1].buf = buf; - ret = i2c_transfer(sfp->i2c, msgs, ARRAY_SIZE(msgs)); - if (ret < 0) - return ret; + while (len) { + this_len = len; + if (this_len > 16) + this_len = 16; - return ret == ARRAY_SIZE(msgs) ? len : 0; + msgs[1].len = this_len; + + ret = i2c_transfer(sfp->i2c, msgs, ARRAY_SIZE(msgs)); + if (ret < 0) + return ret; + + if (ret != ARRAY_SIZE(msgs)) + break; + + msgs[1].buf += this_len; + dev_addr += this_len; + len -= this_len; + } + + return msgs[1].buf - (u8 *)buf; } static int sfp_i2c_write(struct sfp *sfp, bool a2, u8 dev_addr, void *buf, -- cgit v1.2.3-59-g8ed1b From 77316763321ee4050f0576ffd472183aa90dcb30 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 2 Jun 2019 15:12:54 +0100 Subject: net: phylink: avoid reducing support mask Avoid reducing the support mask as a result of the interface type selected for SFP modules, or when setting the link settings through ethtool - this should only change when the supported link modes of the hardware combination change. Signed-off-by: Russell King Signed-off-by: David S. Miller --- drivers/net/phy/phylink.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'drivers/net/phy') diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index 9044b95d2afe..4c0616ba314d 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -1073,6 +1073,7 @@ EXPORT_SYMBOL_GPL(phylink_ethtool_ksettings_get); int phylink_ethtool_ksettings_set(struct phylink *pl, const struct ethtool_link_ksettings *kset) { + __ETHTOOL_DECLARE_LINK_MODE_MASK(support); struct ethtool_link_ksettings our_kset; struct phylink_link_state config; int ret; @@ -1083,11 +1084,12 @@ int phylink_ethtool_ksettings_set(struct phylink *pl, kset->base.autoneg != AUTONEG_ENABLE) return -EINVAL; + linkmode_copy(support, pl->supported); config = pl->link_config; /* Mask out unsupported advertisements */ linkmode_and(config.advertising, kset->link_modes.advertising, - pl->supported); + support); /* FIXME: should we reject autoneg if phy/mac does not support it? */ if (kset->base.autoneg == AUTONEG_DISABLE) { @@ -1097,7 +1099,7 @@ int phylink_ethtool_ksettings_set(struct phylink *pl, * duplex. */ s = phy_lookup_setting(kset->base.speed, kset->base.duplex, - pl->supported, false); + support, false); if (!s) return -EINVAL; @@ -1126,7 +1128,7 @@ int phylink_ethtool_ksettings_set(struct phylink *pl, __set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, config.advertising); } - if (phylink_validate(pl, pl->supported, &config)) + if (phylink_validate(pl, support, &config)) return -EINVAL; /* If autonegotiation is enabled, we must have an advertisement */ @@ -1576,6 +1578,7 @@ static int phylink_sfp_module_insert(void *upstream, { struct phylink *pl = upstream; __ETHTOOL_DECLARE_LINK_MODE_MASK(support) = { 0, }; + __ETHTOOL_DECLARE_LINK_MODE_MASK(support1); struct phylink_link_state config; phy_interface_t iface; int ret = 0; @@ -1603,6 +1606,8 @@ static int phylink_sfp_module_insert(void *upstream, return ret; } + linkmode_copy(support1, support); + iface = sfp_select_interface(pl->sfp_bus, id, config.advertising); if (iface == PHY_INTERFACE_MODE_NA) { netdev_err(pl->netdev, @@ -1612,7 +1617,7 @@ static int phylink_sfp_module_insert(void *upstream, } config.interface = iface; - ret = phylink_validate(pl, support, &config); + ret = phylink_validate(pl, support1, &config); if (ret) { netdev_err(pl->netdev, "validation of %s/%s with support %*pb failed: %d\n", phylink_an_mode_str(MLO_AN_INBAND), -- cgit v1.2.3-59-g8ed1b