realtek: Add support for SFP EEPROM-access over SMBus
authorBirger Koblitz <git@birger-koblitz.de>
Sun, 16 Jan 2022 10:18:38 +0000 (11:18 +0100)
committerPaul Spooren <mail@aparcar.org>
Wed, 2 Feb 2022 09:32:57 +0000 (10:32 +0100)
The EEPROMs on SFP modules are compatible both to I2C as well
as SMBus. However, the kernel so far only supports I2C
access. We add SMBus access routines, because the I2C driver
for the RTL9300 HW only supports that protocol. At the same
time we disable I2C access to PHYs on SFP modules as otherwise
detection of any SFP module would fail. This is not in any
way problematic at this point in time since the RTL93XX
platform so far does not support PHYs on SFP modules. An
alternative is to implement PHY over SMBus access, see e.g.
the link below.

The patch is inspired by the work here:
https://bootlin.com/blog/sfp-modules-on-a-board-running-linux/

Signed-off-by: Birger Koblitz <git@birger-koblitz.de>
target/linux/realtek/patches-5.10/713-add-smbus-support-for-sfp.patch [new file with mode: 0644]

diff --git a/target/linux/realtek/patches-5.10/713-add-smbus-support-for-sfp.patch b/target/linux/realtek/patches-5.10/713-add-smbus-support-for-sfp.patch
new file mode 100644 (file)
index 0000000..562f1d1
--- /dev/null
@@ -0,0 +1,115 @@
+--- a/drivers/net/phy/sfp.c
++++ b/drivers/net/phy/sfp.c
+@@ -412,23 +412,73 @@
+       return ret == ARRAY_SIZE(msgs) ? len : 0;
+ }
++static int sfp_smbus_read(struct sfp *sfp, bool a2, u8 dev_addr, void *buf,
++                        size_t len)
++{
++      union i2c_smbus_data data;
++      u8 bus_addr = a2 ? 0x51 : 0x50, *val = buf;
++      int ret;
++
++      while (len > 0) {
++              ret = i2c_smbus_xfer(sfp->i2c, bus_addr, 0, I2C_SMBUS_READ,
++                           dev_addr, I2C_SMBUS_BYTE_DATA, &data) < 0;
++              if (ret) 
++                      return -EIO;
++
++              *val = data.byte;
++
++              val++;
++              dev_addr++;
++              len--;
++      }
++
++      return val - (u8 *)buf;
++}
++
++static int sfp_smbus_write(struct sfp *sfp, bool a2, u8 dev_addr, void *buf,
++                         size_t len)
++{
++      union i2c_smbus_data data;
++      u8 bus_addr = a2 ? 0x51 : 0x50;
++      int ret;
++
++      memcpy(&data.byte, buf, len);
++
++      ret = i2c_smbus_xfer(sfp->i2c, bus_addr, 0, I2C_SMBUS_WRITE,
++                           dev_addr, I2C_SMBUS_BYTE_DATA, &data);
++
++      return ret;
++}
++
+ static int sfp_i2c_configure(struct sfp *sfp, struct i2c_adapter *i2c)
+ {
+       struct mii_bus *i2c_mii;
+       int ret;
+-      if (!i2c_check_functionality(i2c, I2C_FUNC_I2C))
++      if (i2c_check_functionality(i2c, I2C_FUNC_I2C)) {
++              sfp->read = sfp_i2c_read;
++              sfp->write = sfp_i2c_write;
++
++              i2c_mii = mdio_i2c_alloc(sfp->dev, i2c);
++              if (IS_ERR(i2c_mii))
++                      return PTR_ERR(i2c_mii);
++
++              i2c_mii->name = "SFP I2C Bus";
++      } else if (i2c_check_functionality(i2c, I2C_FUNC_SMBUS_BYTE_DATA)) {
++              sfp->read = sfp_smbus_read;
++              sfp->write = sfp_smbus_write;
++
++              i2c_mii = mdio_i2c_alloc(sfp->dev, i2c);
++              if (IS_ERR(i2c_mii))
++                      return PTR_ERR(i2c_mii);
++
++              i2c_mii->name = "SFP SMBus";
++      } else {
+               return -EINVAL;
++      }
+       sfp->i2c = i2c;
+-      sfp->read = sfp_i2c_read;
+-      sfp->write = sfp_i2c_write;
+-
+-      i2c_mii = mdio_i2c_alloc(sfp->dev, i2c);
+-      if (IS_ERR(i2c_mii))
+-              return PTR_ERR(i2c_mii);
+-      i2c_mii->name = "SFP I2C Bus";
+       i2c_mii->phy_mask = ~0;
+       ret = mdiobus_register(i2c_mii);
+--- a/drivers/net/mdio/mdio-i2c.c
++++ b/drivers/net/mdio/mdio-i2c.c
+@@ -35,6 +35,8 @@
+       u8 addr[3], data[2], *p;
+       int bus_addr, ret;
++      return 0xffff;
++
+       if (!i2c_mii_valid_phy_id(phy_id))
+               return 0xffff;
+@@ -69,6 +71,8 @@
+       int ret;
+       u8 data[5], *p;
++      return 0;
++
+       if (!i2c_mii_valid_phy_id(phy_id))
+               return 0;
+@@ -95,8 +99,8 @@
+ {
+       struct mii_bus *mii;
+-      if (!i2c_check_functionality(i2c, I2C_FUNC_I2C))
+-              return ERR_PTR(-EINVAL);
++//    if (!i2c_check_functionality(i2c, I2C_FUNC_I2C))
++//            return ERR_PTR(-EINVAL);
+       mii = mdiobus_alloc();
+       if (!mii)