Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: sfp: VSOL V2801F / CarlitoxxPro CPGOS03-0490 v2.0 workaround

Add a workaround for the detection of VSOL V2801F / CarlitoxxPro
CPGOS03-0490 v2.0 GPON module which CarlitoxxPro states needs single
byte I2C reads to the EEPROM.

Pali Rohár reports that he also has a CarlitoxxPro-based V2801F module,
which reports a manufacturer of "OEM". This manufacturer can't be
matched as it appears in many different modules, so also match the part
number too.

Reported-by: Thomas Schreiber <tschreibe@gmail.com>
Reported-by: Pali Rohár <pali@kernel.org>
Tested-by: Pali Rohár <pali@kernel.org>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Russell King and committed by
David S. Miller
0d035bed 6b21c0bb

+58 -5
+58 -5
drivers/net/phy/sfp.c
··· 219 219 struct sfp_bus *sfp_bus; 220 220 struct phy_device *mod_phy; 221 221 const struct sff_data *type; 222 + size_t i2c_block_size; 222 223 u32 max_power_mW; 223 224 224 225 unsigned int (*get_state)(struct sfp *); ··· 336 335 size_t len) 337 336 { 338 337 struct i2c_msg msgs[2]; 339 - u8 bus_addr = a2 ? 0x51 : 0x50; 338 + size_t block_size; 340 339 size_t this_len; 340 + u8 bus_addr; 341 341 int ret; 342 + 343 + if (a2) { 344 + block_size = 16; 345 + bus_addr = 0x51; 346 + } else { 347 + block_size = sfp->i2c_block_size; 348 + bus_addr = 0x50; 349 + } 342 350 343 351 msgs[0].addr = bus_addr; 344 352 msgs[0].flags = 0; ··· 360 350 361 351 while (len) { 362 352 this_len = len; 363 - if (this_len > 16) 364 - this_len = 16; 353 + if (this_len > block_size) 354 + this_len = block_size; 365 355 366 356 msgs[1].len = this_len; 367 357 ··· 1642 1632 return 0; 1643 1633 } 1644 1634 1635 + /* Some modules (Nokia 3FE46541AA) lock up if byte 0x51 is read as a 1636 + * single read. Switch back to reading 16 byte blocks unless we have 1637 + * a CarlitoxxPro module (rebranded VSOL V2801F). Even more annoyingly, 1638 + * some VSOL V2801F have the vendor name changed to OEM. 1639 + */ 1640 + static int sfp_quirk_i2c_block_size(const struct sfp_eeprom_base *base) 1641 + { 1642 + if (!memcmp(base->vendor_name, "VSOL ", 16)) 1643 + return 1; 1644 + if (!memcmp(base->vendor_name, "OEM ", 16) && 1645 + !memcmp(base->vendor_pn, "V2801F ", 16)) 1646 + return 1; 1647 + 1648 + /* Some modules can't cope with long reads */ 1649 + return 16; 1650 + } 1651 + 1652 + static void sfp_quirks_base(struct sfp *sfp, const struct sfp_eeprom_base *base) 1653 + { 1654 + sfp->i2c_block_size = sfp_quirk_i2c_block_size(base); 1655 + } 1656 + 1645 1657 static int sfp_cotsworks_fixup_check(struct sfp *sfp, struct sfp_eeprom_id *id) 1646 1658 { 1647 1659 u8 check; ··· 1705 1673 u8 check; 1706 1674 int ret; 1707 1675 1708 - ret = sfp_read(sfp, false, 0, &id, sizeof(id)); 1676 + /* Some modules (CarlitoxxPro CPGOS03-0490) do not support multibyte 1677 + * reads from the EEPROM, so start by reading the base identifying 1678 + * information one byte at a time. 1679 + */ 1680 + sfp->i2c_block_size = 1; 1681 + 1682 + ret = sfp_read(sfp, false, 0, &id.base, sizeof(id.base)); 1709 1683 if (ret < 0) { 1710 1684 if (report) 1711 1685 dev_err(sfp->dev, "failed to read EEPROM: %d\n", ret); 1712 1686 return -EAGAIN; 1713 1687 } 1714 1688 1715 - if (ret != sizeof(id)) { 1689 + if (ret != sizeof(id.base)) { 1716 1690 dev_err(sfp->dev, "EEPROM short read: %d\n", ret); 1717 1691 return -EAGAIN; 1718 1692 } ··· 1755 1717 16, 1, &id, sizeof(id), true); 1756 1718 return -EINVAL; 1757 1719 } 1720 + } 1721 + 1722 + /* Apply any early module-specific quirks */ 1723 + sfp_quirks_base(sfp, &id.base); 1724 + 1725 + ret = sfp_read(sfp, false, SFP_CC_BASE + 1, &id.ext, sizeof(id.ext)); 1726 + if (ret < 0) { 1727 + if (report) 1728 + dev_err(sfp->dev, "failed to read EEPROM: %d\n", ret); 1729 + return -EAGAIN; 1730 + } 1731 + 1732 + if (ret != sizeof(id.ext)) { 1733 + dev_err(sfp->dev, "EEPROM short read: %d\n", ret); 1734 + return -EAGAIN; 1758 1735 } 1759 1736 1760 1737 check = sfp_check(&id.ext, sizeof(id.ext) - 1);