--- /dev/null
+From d585c55b9f70cf9e8c66820d7efe7130c683f19e Mon Sep 17 00:00:00 2001
+From: Antoine Tenart <antoine.tenart@bootlin.com>
+Date: Fri, 21 Feb 2020 11:51:27 +0100
+Subject: [PATCH 2/3] net: phy: add an MDIO SMBus library
+
+Signed-off-by: Antoine Tenart <antoine.tenart@bootlin.com>
+---
+ drivers/net/mdio/Kconfig | 11 +++++++
+ drivers/net/mdio/Makefile | 1 +
+ drivers/net/mdio/mdio-smbus.c | 62 +++++++++++++++++++++++++++++++++++
+ drivers/net/phy/Kconfig | 1 +
+ include/linux/mdio/mdio-i2c.h | 16 +++++++++
+ 5 files changed, 91 insertions(+)
+ create mode 100644 drivers/net/mdio/mdio-smbus.c
+
+--- a/drivers/net/mdio/Kconfig
++++ b/drivers/net/mdio/Kconfig
+@@ -40,6 +40,17 @@ config MDIO_SUN4I
+ interface units of the Allwinner SoC that have an EMAC (A10,
+ A12, A10s, etc.)
+
++config MDIO_SMBUS
++ tristate
++ depends on I2C_SMBUS
++ help
++ Support SMBus based PHYs. This provides a MDIO bus bridged
++ to SMBus to allow PHYs connected in SMBus mode to be accessed
++ using the existing infrastructure.
++
++ This is library mode.
++
++
+ config MDIO_XGENE
+ tristate "APM X-Gene SoC MDIO bus controller"
+ depends on ARCH_XGENE || COMPILE_TEST
+--- a/drivers/net/mdio/Makefile
++++ b/drivers/net/mdio/Makefile
+@@ -17,6 +17,7 @@ obj-$(CONFIG_MDIO_MOXART) += mdio-moxar
+ obj-$(CONFIG_MDIO_MSCC_MIIM) += mdio-mscc-miim.o
+ obj-$(CONFIG_MDIO_MVUSB) += mdio-mvusb.o
+ obj-$(CONFIG_MDIO_OCTEON) += mdio-octeon.o
++obj-$(CONFIG_MDIO_SMBUS) += mdio-smbus.o
+ obj-$(CONFIG_MDIO_SUN4I) += mdio-sun4i.o
+ obj-$(CONFIG_MDIO_THUNDER) += mdio-thunder.o
+ obj-$(CONFIG_MDIO_XGENE) += mdio-xgene.o
+--- /dev/null
++++ b/drivers/net/mdio/mdio-smbus.c
+@@ -0,0 +1,62 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * MDIO SMBus bridge
++ *
++ * Copyright (C) 2020 Antoine Tenart
++ *
++ * Network PHYs can appear on SMBus when they are part of SFP modules.
++ */
++#include <linux/i2c.h>
++#include <linux/phy.h>
++#include <linux/mdio/mdio-i2c.h>
++
++static int smbus_mii_read(struct mii_bus *mii, int phy_id, int reg)
++{
++ struct i2c_adapter *i2c = mii->priv;
++ union i2c_smbus_data data;
++ int ret;
++
++ ret = i2c_smbus_xfer(i2c, i2c_mii_phy_addr(phy_id), 0, I2C_SMBUS_READ,
++ reg, I2C_SMBUS_BYTE_DATA, &data);
++ if (ret < 0)
++ return 0xff;
++
++ return data.byte;
++}
++
++static int smbus_mii_write(struct mii_bus *mii, int phy_id, int reg, u16 val)
++{
++ struct i2c_adapter *i2c = mii->priv;
++ union i2c_smbus_data data;
++ int ret;
++
++ data.byte = val;
++
++ ret = i2c_smbus_xfer(i2c, i2c_mii_phy_addr(phy_id), 0, I2C_SMBUS_WRITE,
++ reg, I2C_SMBUS_BYTE_DATA, &data);
++ return ret < 0 ? ret : 0;
++}
++
++struct mii_bus *mdio_smbus_alloc(struct device *parent, struct i2c_adapter *i2c)
++{
++ struct mii_bus *mii;
++
++ if (!i2c_check_functionality(i2c, I2C_FUNC_SMBUS_BYTE_DATA))
++ return ERR_PTR(-EINVAL);
++
++ mii = mdiobus_alloc();
++ if (!mii)
++ return ERR_PTR(-ENOMEM);
++
++ snprintf(mii->id, MII_BUS_ID_SIZE, "smbus:%s", dev_name(parent));
++ mii->parent = parent;
++ mii->read = smbus_mii_read;
++ mii->write = smbus_mii_write;
++ mii->priv = i2c;
++
++ return mii;
++}
++
++MODULE_AUTHOR("Antoine Tenart");
++MODULE_DESCRIPTION("MDIO SMBus bridge library");
++MODULE_LICENSE("GPL");
+--- a/drivers/net/phy/Kconfig
++++ b/drivers/net/phy/Kconfig
+@@ -60,6 +60,7 @@ config SFP
+ depends on I2C && PHYLINK
+ depends on HWMON || HWMON=n
+ select MDIO_I2C
++ select MDIO_SMBUS
+
+ comment "Switch configuration API + drivers"
+
+--- a/include/linux/mdio/mdio-i2c.h
++++ b/include/linux/mdio/mdio-i2c.h
+@@ -12,5 +12,21 @@ struct i2c_adapter;
+ struct mii_bus;
+
+ struct mii_bus *mdio_i2c_alloc(struct device *parent, struct i2c_adapter *i2c);
++struct mii_bus *mdio_smbus_alloc(struct device *parent, struct i2c_adapter *i2c);
++
++/*
++ * I2C bus addresses 0x50 and 0x51 are normally an EEPROM, which is
++ * specified to be present in SFP modules. These correspond with PHY
++ * addresses 16 and 17. Disallow access to these "phy" addresses.
++ */
++static bool i2c_mii_valid_phy_id(int phy_id)
++{
++ return phy_id != 0x10 && phy_id != 0x11;
++}
++
++static unsigned int i2c_mii_phy_addr(int phy_id)
++{
++ return phy_id + 0x40;
++}
+
+ #endif
+--- a/drivers/net/mdio/mdio-i2c.c
++++ b/drivers/net/mdio/mdio-i2c.c
+@@ -13,21 +13,6 @@
+ #include <linux/mdio/mdio-i2c.h>
+ #include <linux/phy.h>
+
+-/*
+- * I2C bus addresses 0x50 and 0x51 are normally an EEPROM, which is
+- * specified to be present in SFP modules. These correspond with PHY
+- * addresses 16 and 17. Disallow access to these "phy" addresses.
+- */
+-static bool i2c_mii_valid_phy_id(int phy_id)
+-{
+- return phy_id != 0x10 && phy_id != 0x11;
+-}
+-
+-static unsigned int i2c_mii_phy_addr(int phy_id)
+-{
+- return phy_id + 0x40;
+-}
+-
+ static int i2c_mii_read(struct mii_bus *bus, int phy_id, int reg)
+ {
+ struct i2c_adapter *i2c = bus->priv;
--- /dev/null
+From 3cb0bde365d913c484d20224367a54a0eac780a7 Mon Sep 17 00:00:00 2001
+From: Antoine Tenart <antoine.tenart@bootlin.com>
+Date: Fri, 21 Feb 2020 11:55:29 +0100
+Subject: [PATCH 3/3] net: phy: sfp: add support for SMBus
+
+Signed-off-by: Antoine Tenart <antoine.tenart@bootlin.com>
+---
+ drivers/net/phy/sfp.c | 68 ++++++++++++++++++++++++++++++++++---------
+ 1 file changed, 54 insertions(+), 14 deletions(-)
+
+--- a/drivers/net/phy/sfp.c
++++ b/drivers/net/phy/sfp.c
+@@ -412,32 +412,72 @@ static int sfp_i2c_write(struct sfp *sfp
+ 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)
++{
++ u8 bus_addr = a2 ? 0x51 : 0x50, *val = buf;
++
++ bus_addr -= 0x40;
++
++ while (len > 0) {
++ *val = sfp->i2c_mii->read(sfp->i2c_mii, bus_addr, dev_addr);
++
++ 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)
++{
++ u8 bus_addr = a2 ? 0x51 : 0x50;
++ u16 val;
++
++ memcpy(&val, buf, len);
++
++ return sfp->i2c_mii->write(sfp->i2c_mii, bus_addr, dev_addr, val);
++}
++
+ static int sfp_i2c_configure(struct sfp *sfp, struct i2c_adapter *i2c)
+ {
+- struct mii_bus *i2c_mii;
++ struct mii_bus *mii;
+ int ret;
+
+- if (!i2c_check_functionality(i2c, I2C_FUNC_I2C))
+- 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);
++ if (i2c_check_functionality(i2c, I2C_FUNC_I2C)) {
++ sfp->read = sfp_i2c_read;
++ sfp->write = sfp_i2c_write;
++
++ mii = mdio_i2c_alloc(sfp->dev, i2c);
++ if (IS_ERR(mii))
++ return PTR_ERR(mii);
++
++ 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;
++
++ mii = mdio_smbus_alloc(sfp->dev, i2c);
++ if (IS_ERR(mii))
++ return PTR_ERR(mii);
+
+- i2c_mii->name = "SFP I2C Bus";
+- i2c_mii->phy_mask = ~0;
++ mii->name = "SFP SMBus";
++ } else {
++ return -EINVAL;
++ }
+
+- ret = mdiobus_register(i2c_mii);
++ mii->phy_mask = ~0;
++ ret = mdiobus_register(mii);
+ if (ret < 0) {
+- mdiobus_free(i2c_mii);
++ mdiobus_free(mii);
+ return ret;
+ }
+
+- sfp->i2c_mii = i2c_mii;
++ sfp->i2c_mii = mii;
+
+ return 0;
+ }