From: NagarajuDeepakX <deepakx.nagar...@intel.com>

commit b9927cdeeb591cacbd31206530d54a98ce08207f from
https://github.com/altera-opensource/linux-socfpga.git

This patch adds qsfp_phy support to the E-Tile Ethernet controller.

Signed-off-by: NagarajuDeepakX <deepakx.nagar...@intel.com>
Signed-off-by: Wenlin Kang <wenlin.k...@windriver.com>
---
 drivers/net/phy/Kconfig    |    8 +
 drivers/net/phy/Makefile   |    4 +
 drivers/net/phy/qsfp.c     | 1848 ++++++++++++++++++++++++++++++++++++
 drivers/net/phy/qsfp_bus.c |  782 +++++++++++++++
 include/linux/qsfp.h       |  400 ++++++++
 5 files changed, 3042 insertions(+)
 create mode 100644 drivers/net/phy/qsfp.c
 create mode 100644 drivers/net/phy/qsfp_bus.c
 create mode 100644 include/linux/qsfp.h

diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 698bea312adc..4833e817bb98 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -63,6 +63,14 @@ config SFP
 
 comment "MII PHY device drivers"
 
+config QSFP
+       tristate "QSFP cage support"
+       depends on I2C && PHYLINK
+       depends on HWMON || HWMON=n
+       select MDIO_I2C
+
+comment "MII PHY device drivers"
+
 config AMD_PHY
        tristate "AMD PHYs"
        help
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index a13e402074cf..f3dd5cd88451 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -30,6 +30,10 @@ obj-$(CONFIG_SFP)            += sfp.o
 sfp-obj-$(CONFIG_SFP)          += sfp-bus.o
 obj-y                          += $(sfp-obj-y) $(sfp-obj-m)
 
+obj-$(CONFIG_QSFP)             += qsfp.o
+qsfp-obj-$(CONFIG_QSFP)                += qsfp_bus.o
+obj-y                          += $(qsfp-obj-y) $(qsfp-obj-m)
+
 obj-$(CONFIG_ADIN_PHY)         += adin.o
 obj-$(CONFIG_AMD_PHY)          += amd.o
 aquantia-objs                  += aquantia_main.o
diff --git a/drivers/net/phy/qsfp.c b/drivers/net/phy/qsfp.c
new file mode 100644
index 000000000000..c0f1f8953d6c
--- /dev/null
+++ b/drivers/net/phy/qsfp.c
@@ -0,0 +1,1848 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/acpi.h>
+#include <linux/ctype.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/hwmon.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/mdio/mdio-i2c.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/phy.h>
+#include <linux/platform_device.h>
+#include <linux/rtnetlink.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/qsfp.h>
+
+#include "swphy.h"
+
+static u8 previous_state = 1;
+static struct qsfp *qsfp;
+static void qsfp_sm_event(struct qsfp *qsfp, unsigned int event);
+static void get_module_revision(struct qsfp *qsfp);
+
+#define QSFP_TX_CHANNEL_4 0x4
+#define QSFP_TX_CHANNEL_3 0x3
+#define QSFP_TX_CHANNEL_2 0x2
+#define QSFP_TX_CHANNEL_1 0x1
+#define QSFP_RX_CHANNEL_4 0x4
+#define QSFP_RX_CHANNEL_3 0x3
+#define QSFP_RX_CHANNEL_2 0x2
+#define QSFP_RX_CHANNEL_1 0x1
+
+enum {
+       GPIO_MODULE_PRESENT,
+       GPIO_MODULE_INTERRUPT,
+       GPIO_MODULE_INIT_MODE,
+       GPIO_MODULE_RESET,
+       GPIO_MODULE_SELECT,
+       GPIO_MAX,
+
+       QSFP_F_PRESENT = BIT(GPIO_MODULE_PRESENT),
+       QSFP_INTERRUPT = BIT(GPIO_MODULE_INTERRUPT),
+       QSFP_INIT = BIT(GPIO_MODULE_INIT_MODE),
+       QSFP_RESET = BIT(GPIO_MODULE_RESET),
+       QSFP_SELECT = BIT(GPIO_MODULE_SELECT),
+
+       QSFP_E_INSERT = 0,
+       QSFP_E_REMOVE,
+       QSFP_E_DEV_ATTACH,
+       QSFP_E_DEV_DETACH,
+       QSFP_E_DEV_DOWN,
+       QSFP_E_DEV_UP,
+       QSFP_E_TX_FAULT,
+       QSFP_E_TX_CLEAR,
+       QSFP_E_TX_LOS,
+       QSFP_E_RX_LOS,
+       QSFP_E_TIMEOUT,
+
+       QSFP_MOD_EMPTY = 0,
+       QSFP_MOD_ERROR,
+       QSFP_MOD_PROBE,
+       QSFP_MOD_WAITDEV,
+       QSFP_MOD_HPOWER,
+       QSFP_MOD_WAITPWR,
+       QSFP_MOD_PRESENT,
+
+       QSFP_DEV_DETACHED = 0,
+       QSFP_DEV_DOWN,
+       QSFP_DEV_UP,
+
+       QSFP_S_DOWN = 0,
+       QSFP_S_FAIL,
+       QSFP_S_WAIT,
+       QSFP_S_INIT,
+       QSFP_S_INIT_PHY,
+       QSFP_S_INIT_TX_FAULT,
+       QSFP_S_WAIT_LOS,
+       QSFP_S_LINK_UP,
+       QSFP_S_TX_FAULT,
+       QSFP_S_REINIT,
+       QSFP_S_TX_DISABLE,
+};
+
+static const char *const mod_state_strings[] = {
+       [QSFP_MOD_EMPTY] = "empty",     [QSFP_MOD_ERROR] = "error",
+       [QSFP_MOD_PROBE] = "probe",     [QSFP_MOD_WAITDEV] = "waitdev",
+       [QSFP_MOD_HPOWER] = "hpower",   [QSFP_MOD_WAITPWR] = "waitpwr",
+       [QSFP_MOD_PRESENT] = "present",
+};
+
+static const char *mod_state_to_str(unsigned short mod_state)
+{
+       if (mod_state >= ARRAY_SIZE(mod_state_strings))
+               return "Unknown module state";
+       return mod_state_strings[mod_state];
+}
+
+static const char *const dev_state_strings[] = {
+       [QSFP_DEV_DETACHED] = "detached",
+       [QSFP_DEV_DOWN] = "down",
+       [QSFP_DEV_UP] = "up",
+};
+
+static const char *dev_state_to_str(unsigned short dev_state)
+{
+       if (dev_state >= ARRAY_SIZE(dev_state_strings))
+               return "Unknown device state";
+       return dev_state_strings[dev_state];
+}
+
+static const char *const event_strings[] = {
+       [QSFP_E_INSERT] = "insert",         [QSFP_E_REMOVE] = "remove",
+       [QSFP_E_DEV_ATTACH] = "dev_attach", [QSFP_E_DEV_DETACH] = "dev_detach",
+       [QSFP_E_DEV_DOWN] = "dev_down",     [QSFP_E_DEV_UP] = "dev_up",
+       [QSFP_E_TX_FAULT] = "tx_fault",     [QSFP_E_TX_CLEAR] = "tx_clear",
+       [QSFP_E_TX_LOS] = "tx_los",         [QSFP_E_RX_LOS] = "rx_los",
+       [QSFP_E_TIMEOUT] = "timeout",
+};
+
+static const char *event_to_str(unsigned short event)
+{
+       if (event >= ARRAY_SIZE(event_strings))
+               return "Unknown event";
+       return event_strings[event];
+}
+
+static const char *const sm_state_strings[] = {
+       [QSFP_S_DOWN] = "down",
+       [QSFP_S_FAIL] = "fail",
+       [QSFP_S_WAIT] = "wait",
+       [QSFP_S_INIT] = "init",
+       [QSFP_S_INIT_PHY] = "init_phy",
+       [QSFP_S_INIT_TX_FAULT] = "init_tx_fault",
+       [QSFP_S_WAIT_LOS] = "wait_los",
+       [QSFP_S_LINK_UP] = "link_up",
+       [QSFP_S_TX_FAULT] = "tx_fault",
+       [QSFP_S_REINIT] = "reinit",
+       [QSFP_S_TX_DISABLE] = "rx_disable",
+};
+
+static const char *sm_state_to_str(unsigned short sm_state)
+{
+       if (sm_state >= ARRAY_SIZE(sm_state_strings))
+               return "Unknown state";
+       return sm_state_strings[sm_state];
+}
+
+static const char *const gpio_of_names[] = {
+       "qsfpdd_modprsn", "qsfpdd_intn",    "qsfpdd_initmode",
+       "qsfpdd_resetn",  "qsfpdd_modseln",
+};
+
+static const enum gpiod_flags gpio_flags[] = {
+       GPIOD_IN, GPIOD_IN, GPIOD_ASIS, GPIOD_ASIS, GPIOD_ASIS,
+};
+
+/* t_start_up (SFF-8431) or t_init (SFF-8472) is the time required for a
+ * non-cooled module to initialise its laser safety circuitry. We wait
+ * an initial T_WAIT period before we check the tx fault to give any PHY
+ * on board (for a copper qsfp) time to initialise.
+ */
+#define T_WAIT msecs_to_jiffies(50)
+#define T_START_UP msecs_to_jiffies(300)
+#define T_START_UP_BAD_GPON msecs_to_jiffies(60000)
+
+/* t_reset is the time required to assert the TX_DISABLE signal to reset
+ * an indicated TX_FAULT.
+ */
+#define T_RESET_US 10
+#define T_FAULT_RECOVER msecs_to_jiffies(1000)
+
+/* N_FAULT_INIT is the number of recovery attempts at module initialisation
+ * time. If the TX_FAULT signal is not deasserted after this number of
+ * attempts at clearing it, we decide that the module is faulty.
+ * N_FAULT is the same but after the module has initialised.
+ */
+#define N_FAULT_INIT 5
+#define N_FAULT 5
+
+/* T_PHY_RETRY is the time interval between attempts to probe the PHY.
+ * R_PHY_RETRY is the number of attempts.
+ */
+#define T_PHY_RETRY msecs_to_jiffies(50)
+#define R_PHY_RETRY 12
+
+/* qsfp module presence detection is poor: the three MOD DEF signals are
+ * the same length on the PCB, which means it's possible for MOD DEF 0 to
+ * connect before the I2C bus on MOD DEF 1/2.
+ *
+ * The SFF-8472 specifies t_serial ("Time from power on until module is
+ * ready for data transmission over the two wire serial bus.") as 300ms.
+ */
+#define T_SERIAL msecs_to_jiffies(300)
+#define T_HPOWER_LEVEL msecs_to_jiffies(300)
+#define T_PROBE_RETRY_INIT msecs_to_jiffies(100)
+#define R_PROBE_RETRY_INIT 10
+#define T_PROBE_RETRY_SLOW msecs_to_jiffies(5000)
+#define R_PROBE_RETRY_SLOW 12
+
+/* qsfp modules appear to always have their PHY configured for bus address
+ * 0x56 (which with mdio-i2c, translates to a PHY address of 22).
+ */
+#define QSFP_PHY_ADDR 22
+
+struct sff_data {
+       unsigned int gpios;
+       bool (*module_supported)(const struct qsfp_eeprom_id *id);
+};
+
+struct qsfp {
+       struct device *dev;
+       struct i2c_adapter *i2c;
+       struct mii_bus *i2c_mii;
+       struct qsfp_bus *qsfp_bus;
+       struct phy_device *mod_phy;
+       const struct sff_data *type;
+       size_t i2c_block_size;
+       u32 max_power_mw;
+       unsigned int module_revision;
+       unsigned int module_present;
+       int channel_number;
+       unsigned char vender_name[16];
+       unsigned char sn_number[16];
+       unsigned char part_number[16];
+
+       unsigned int (*get_state)(struct qsfp *qsfp);
+       void (*set_state)(struct qsfp *qsfp, unsigned int state);
+       int (*read)(struct qsfp *qsfp, bool a2, u8 dev_addr, void *buf, size_t 
len);
+       int (*write)(struct qsfp *qsfp, bool a2, u8 dev_addr, void *buf, size_t 
len);
+
+       struct gpio_desc *gpio[GPIO_MAX];
+       int gpio_irq[GPIO_MAX];
+
+       bool need_poll;
+
+       struct mutex st_mutex; /* Protects state */
+       unsigned int state_soft_mask;
+       unsigned int state;
+       struct delayed_work poll;
+       struct delayed_work timeout;
+       struct mutex sm_mutex; /* Protects state machine */
+       unsigned char sm_mod_state;
+       unsigned char sm_mod_tries_init;
+       unsigned char sm_mod_tries;
+       unsigned char sm_dev_state;
+       unsigned short sm_state;
+       unsigned char sm_fault_retries;
+       unsigned char sm_phy_retries;
+
+       struct qsfp_eeprom_id id;
+       unsigned int module_power_mw;
+       unsigned int module_t_start_up;
+};
+
+static bool qsfp_module_supported(const struct qsfp_eeprom_id *id)
+{
+       if (id->base.etile_qsfp_identifier == SFF8024_ID_QSFP_28)
+               return true;
+
+       /* qsfp GPON module Ubiquiti U-Fiber Instant has in its EEPROM stored
+        * phys id SFF instead of qsfp. Therefore mark this module explicitly
+        * as supported based on vendor name and pn match.
+        */
+       if (id->base.etile_qsfp_identifier == SFF8024_ID_QSFP_DD_INF_8628 &&
+           id->base.etile_qsfp_ext_identifier == QSFP_EXT_IDENTIFIER &&
+           !memcmp(id->base.etile_qsfp_vendor_name, "UBNT            ", 16) &&
+           !memcmp(id->base.etile_qsfp_vendor_pn, "UF-INSTANT      ", 16))
+               return true;
+
+       return false;
+}
+
+static const struct sff_data qsfp_data = {
+       .gpios = QSFP_F_PRESENT | QSFP_INTERRUPT | QSFP_INIT | QSFP_RESET |
+                QSFP_SELECT,
+       .module_supported = qsfp_module_supported,
+};
+
+static const struct of_device_id qsfp_of_match[] = {
+       {
+               .compatible = "sff,qsfp",
+               .data = &qsfp_data,
+       },
+       {},
+};
+MODULE_DEVICE_TABLE(of, qsfp_of_match);
+
+static unsigned long poll_jiffies;
+
+static unsigned int qsfp_gpio_get_state(struct qsfp *qsfp)
+{
+       unsigned int i, state, v;
+
+       for (i = state = 0; i < GPIO_MAX; i++) {
+               if (gpio_flags[i] != GPIOD_IN || !qsfp->gpio[i])
+                       continue;
+
+               v = gpiod_get_value_cansleep(qsfp->gpio[i]);
+               if (v)
+                       state |= BIT(i);
+       }
+
+       return state;
+}
+
+static unsigned int sff_gpio_get_state(struct qsfp *qsfp)
+{
+       return qsfp_gpio_get_state(qsfp) | QSFP_F_PRESENT;
+}
+
+static void qsfp_gpio_set_state(struct qsfp *qsfp, unsigned int state)
+{
+       if (state & QSFP_F_PRESENT) {
+               /* If the module is present, drive the signals */
+               gpiod_direction_output(qsfp->gpio[GPIO_MODULE_SELECT],
+                                      state & QSFP_SELECT);
+
+       } else {
+               /* Otherwise, let them float to the pull-ups */
+
+               gpiod_direction_input(qsfp->gpio[GPIO_MODULE_PRESENT]);
+       }
+}
+
+static int qsfp_i2c_read(struct qsfp *qsfp, bool a2, u8 dev_addr, void *buf,
+                        size_t len)
+{
+       struct i2c_msg msgs[2];
+       u8 bus_addr = a2 ? 0x51 : 0x50;
+
+       size_t block_size = qsfp->i2c_block_size;
+       size_t this_len;
+       int ret;
+
+       msgs[0].addr = bus_addr;
+       msgs[0].flags = 0;
+       msgs[0].len = 1;
+       msgs[0].buf = &dev_addr;
+       msgs[1].addr = bus_addr;
+       msgs[1].flags = I2C_M_RD;
+       msgs[1].len = len;
+       msgs[1].buf = buf;
+
+       while (len) {
+               this_len = len;
+               if (this_len > block_size)
+                       this_len = block_size;
+
+               msgs[1].len = this_len;
+
+               ret = i2c_transfer(qsfp->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 qsfp_i2c_write(struct qsfp *qsfp, bool a2, u8 dev_addr, void *buf,
+                         size_t len)
+{
+       struct i2c_msg msgs[1];
+       u8 bus_addr = a2 ? 0x51 : 0x50;
+       int ret;
+
+       msgs[0].addr = bus_addr;
+       msgs[0].flags = 0;
+       msgs[0].len = 1 + len;
+       msgs[0].buf = kmalloc(1 + len, GFP_KERNEL);
+       if (!msgs[0].buf)
+               return -ENOMEM;
+
+       msgs[0].buf[0] = dev_addr;
+       memcpy(&msgs[0].buf[1], buf, len);
+
+       ret = i2c_transfer(qsfp->i2c, msgs, ARRAY_SIZE(msgs));
+
+       kfree(msgs[0].buf);
+
+       if (ret < 0)
+               return ret;
+
+       return ret == ARRAY_SIZE(msgs) ? len : 0;
+}
+
+static int qsfp_i2c_configure(struct qsfp *qsfp, struct i2c_adapter *i2c)
+{
+       struct mii_bus *i2c_mii;
+       int ret;
+
+       if (!i2c_check_functionality(i2c, I2C_FUNC_I2C))
+               return -EINVAL;
+
+       qsfp->i2c = i2c;
+       qsfp->read = qsfp_i2c_read;
+       qsfp->write = qsfp_i2c_write;
+
+       i2c_mii = mdio_i2c_alloc(qsfp->dev, i2c);
+       if (IS_ERR(i2c_mii))
+               return PTR_ERR(i2c_mii);
+       i2c_mii->name = "QSFP I2C Bus";
+       i2c_mii->phy_mask = ~0;
+
+       ret = mdiobus_register(i2c_mii);
+       if (ret < 0) {
+               ;
+               mdiobus_free(i2c_mii);
+               return ret;
+       }
+
+       qsfp->i2c_mii = i2c_mii;
+
+       return 0;
+}
+
+void qsfp_reset(struct qsfp *qsfp, unsigned int value)
+{
+       unsigned int state = qsfp_gpio_get_state(qsfp);
+
+       if (value)
+               qsfp_gpio_set_state(qsfp, state | QSFP_RESET);
+       else
+               qsfp_gpio_set_state(qsfp, state & (~QSFP_RESET));
+}
+EXPORT_SYMBOL(qsfp_reset);
+
+/* Interface */
+static int qsfp_read(struct qsfp *qsfp, bool a2, u8 addr, void *buf, size_t 
len)
+{
+       return qsfp->read(qsfp, a2, addr, buf, len);
+}
+
+static int qsfp_write(struct qsfp *qsfp, bool a2, u8 addr, void *buf,
+                     size_t len)
+{
+       return qsfp->write(qsfp, a2, addr, buf, len);
+}
+
+static void qsfp_soft_start_poll(struct qsfp *qsfp)
+{
+       u8 status;
+       //const struct qsfp_eeprom_id *id = &qsfp->id;
+       qsfp_read(qsfp, false, QSFP_OPTIONS, &status, sizeof(status));
+
+       qsfp->state_soft_mask = 0;
+
+       if (qsfp->id.base.etile_qsfp_options_3 & QSFP_OPTIONS_TX_DISABLE)
+               qsfp->state_soft_mask |= QSFP_OPTIONS_TX_DISABLE;
+       if (qsfp->id.base.etile_qsfp_options_3 & QSFP_OPTIONS_TX_FAULT)
+               qsfp->state_soft_mask |= QSFP_OPTIONS_TX_FAULT;
+       if (qsfp->id.base.etile_qsfp_options_3 & QSFP_OPTIONS_TX_LOSS_SIGNAL)
+               qsfp->state_soft_mask |= QSFP_OPTIONS_TX_LOSS_SIGNAL;
+
+       if (qsfp->state_soft_mask &
+                   (QSFP_OPTIONS_TX_LOSS_SIGNAL | QSFP_OPTIONS_TX_FAULT) &&
+           !qsfp->need_poll)
+
+               mod_delayed_work(system_wq, &qsfp->poll, poll_jiffies);
+}
+
+static void qsfp_soft_stop_poll(struct qsfp *qsfp)
+{
+       qsfp->state_soft_mask = 0;
+}
+
+static unsigned int qsfp_get_state(struct qsfp *qsfp)
+{
+       unsigned int state = qsfp->get_state(qsfp);
+
+       return state;
+}
+
+static void qsfp_set_state(struct qsfp *qsfp, unsigned int state)
+{
+       qsfp->set_state(qsfp, state);
+}
+
+static unsigned int qsfp_check(void *buf, size_t len)
+{
+       u8 *p, check;
+
+       for (p = buf, check = 0; len; p++, len--)
+               check += *p;
+
+       return check;
+}
+
+/* Helpers */
+static void qsfp_module_tx_disable(struct qsfp *qsfp)
+{
+       dev_dbg(qsfp->dev, "tx disable %u -> %u\n",
+               qsfp->id.base.etile_qsfp_options_3 & QSFP_OPTIONS_TX_DISABLE ?
+                       1 :
+                             0,
+               1);
+       qsfp->id.base.etile_qsfp_options_3 |= QSFP_OPTIONS_TX_DISABLE;
+       qsfp_set_state(qsfp, qsfp->state);
+}
+
+static void qsfp_module_tx_enable(struct qsfp *qsfp)
+{
+       dev_dbg(qsfp->dev, "tx disable %u -> %u\n",
+               qsfp->id.base.etile_qsfp_options_3 & QSFP_OPTIONS_TX_DISABLE ?
+                       1 :
+                             0,
+               0);
+       qsfp->id.base.etile_qsfp_options_3 &= ~QSFP_OPTIONS_TX_DISABLE;
+       qsfp_set_state(qsfp, qsfp->state);
+}
+
+static void qsfp_module_tx_fault_reset(struct qsfp *qsfp)
+{
+       unsigned int state = qsfp->id.base.etile_qsfp_options_3;
+
+       int ret;
+
+       ret = qsfp_read(qsfp, false, QSFP_OPTIONS, &state, sizeof(state));
+
+       if (state & QSFP_OPTIONS_TX_DISABLE)
+               return;
+
+       qsfp_set_state(qsfp, state | QSFP_OPTIONS_TX_DISABLE);
+
+       udelay(T_RESET_US);
+
+       qsfp_set_state(qsfp, state);
+}
+
+/* qsfp state machine */
+static void qsfp_sm_set_timer(struct qsfp *qsfp, unsigned int timeout)
+{
+       if (timeout) {
+               mod_delayed_work(system_power_efficient_wq, &qsfp->timeout,
+                                timeout);
+       } else {
+               cancel_delayed_work(&qsfp->timeout);
+       }
+}
+
+static void qsfp_sm_next(struct qsfp *qsfp, unsigned int state,
+                        unsigned int timeout)
+{
+       qsfp->sm_state = state;
+       qsfp_sm_set_timer(qsfp, timeout);
+}
+
+static void qsfp_sm_mod_next(struct qsfp *qsfp, unsigned int state,
+                            unsigned int timeout)
+{
+       qsfp->sm_mod_state = state;
+       qsfp_sm_set_timer(qsfp, timeout);
+}
+
+static void qsfp_sm_phy_detach(struct qsfp *qsfp)
+{
+       qsfp_remove_phy(qsfp->qsfp_bus);
+       phy_device_remove(qsfp->mod_phy);
+       phy_device_free(qsfp->mod_phy);
+       qsfp->mod_phy = NULL;
+}
+
+static int qsfp_sm_probe_phy(struct qsfp *qsfp, bool is_c45)
+{
+       struct phy_device *phy;
+       int err;
+
+       phy = get_phy_device(qsfp->i2c_mii, QSFP_PHY_ADDR, is_c45);
+
+       if (phy == ERR_PTR(-ENODEV))
+               return PTR_ERR(phy);
+
+       if (IS_ERR(phy)) {
+               dev_err(qsfp->dev, "mdiobus scan returned %ld\n", PTR_ERR(phy));
+               return PTR_ERR(phy);
+       }
+
+       err = phy_device_register(phy);
+       if (err) {
+               phy_device_free(phy);
+               dev_err(qsfp->dev, "phy_device_register failed: %d\n", err);
+               return err;
+       }
+
+       err = qsfp_add_phy(qsfp->qsfp_bus, phy);
+       if (err) {
+               phy_device_remove(phy);
+               phy_device_free(phy);
+               dev_err(qsfp->dev, "qsfp_add_phy failed: %d\n", err);
+               return err;
+       }
+
+       qsfp->mod_phy = phy;
+
+       return 0;
+}
+
+static void qsfp_sm_link_up(struct qsfp *qsfp)
+{
+       qsfp_link_up(qsfp->qsfp_bus);
+       qsfp_sm_next(qsfp, QSFP_S_LINK_UP, 0);
+}
+
+static void qsfp_sm_link_down(struct qsfp *qsfp)
+{
+       qsfp_link_down(qsfp->qsfp_bus);
+}
+
+static void qsfp_sm_link_check_los(struct qsfp *qsfp)
+{
+       bool los = false;
+
+       if (los)
+               qsfp_sm_next(qsfp, QSFP_S_WAIT_LOS, 0);
+       else
+
+               qsfp_sm_link_up(qsfp);
+}
+
+static bool qsfp_los_event_active(struct qsfp *qsfp, unsigned int event)
+{
+       int ret;
+       u8 buf[16];
+
+       ret = qsfp_read(qsfp, false, QSFP_PHYS_ID, buf, 1);
+
+       return 0;
+}
+
+static bool qsfp_los_event_inactive(struct qsfp *qsfp, unsigned int event)
+{
+       return 0;
+}
+
+static void qsfp_sm_fault(struct qsfp *qsfp, unsigned int next_state, bool 
warn)
+{
+       if (qsfp->sm_fault_retries && !--qsfp->sm_fault_retries) {
+               dev_err(qsfp->dev,
+                       "module persistently indicates fault, disabling\n");
+               qsfp_sm_next(qsfp, QSFP_S_TX_DISABLE, 0);
+       } else {
+               if (warn)
+                       dev_err(qsfp->dev, "module transmit fault indicated\n");
+
+               qsfp_sm_next(qsfp, next_state, T_FAULT_RECOVER);
+       }
+}
+
+/* Probe a qsfp for a PHY device if the module supports copper - the PHY
+ * normally sits at I2C bus address 0x56, and may either be a clause 22
+ * or clause 45 PHY.
+ *
+ * Clause 22 copper qsfp modules normally operate in Cisco SGMII mode with
+ * negotiation enabled, but some may be in 1000base-X - which is for the
+ * PHY driver to determine.
+ *
+ * Clause 45 copper qsfp+ modules (10G) appear to switch their interface
+ * mode according to the negotiated line speed.
+ */
+static int qsfp_sm_probe_for_phy(struct qsfp *qsfp)
+{
+       int err = 0;
+
+       switch (qsfp->id.base.etile_qsfp_spec_compliance_1[0]) {
+       case SFF8636_QSFP_DD_ECC_LAUI2_C2M:
+       case SFF8636_QSFP_DD_ECC_50GAUI2_C2M:
+       case SFF8636_QSFP_DD_ECC_50GAUI1_C2M:
+       case SFF8636_QSFP_DD_ECC_CDAUI8_C2M:
+               err = qsfp_sm_probe_phy(qsfp, true);
+               break;
+
+       default:
+
+               err = qsfp_sm_probe_phy(qsfp, false);
+               break;
+       }
+       return err;
+}
+
+static int qsfp_module_parse_power(struct qsfp *qsfp)
+{
+       u32 power_mw = 1000;
+
+       if (power_mw > qsfp->max_power_mw) {
+               /* Module power specification exceeds the allowed maximum. */
+               if (qsfp->id.base.etile_qsfp_spec_compliance_1[0] ==
+                           SFF8636_QSFP_DD_ECC_100GBASE_CR4 &&
+                   !(qsfp->id.base.etile_qsfp_diag_monitor &
+                     QSFP_DIAGMON_DDM)) {
+                       /* The module appears not to implement bus address
+                        * 0xa2, so assume that the module powers up in the
+                        * indicated mode.
+                        */
+                       dev_err(qsfp->dev,
+                               "Host does not support %u.%uW modules, module 
left in power mode\n",
+                               power_mw / 1000, (power_mw / 100) % 10);
+                       return -EINVAL;
+               }
+       }
+
+       /* If the module requires a higher power mode, but also requires
+        * an address change sequence, warn the user that the module may
+        * not be functional.
+        */
+       if (qsfp->id.base.etile_qsfp_diag_monitor & QSFP_DIAGMON_ADDRMODE &&
+           power_mw > 1000) {
+               dev_warn(qsfp->dev,
+                        "Address Change Sequence not supported but module 
requires %u.%uW, module may not be functional\n",
+                       power_mw / 1000, (power_mw / 100) % 10);
+               return 0;
+       }
+
+       qsfp->module_power_mw = power_mw;
+
+       return 0;
+}
+
+static int qsfp_sm_mod_hpower(struct qsfp *qsfp, bool enable)
+{
+       u8 val;
+       int err;
+
+       static void *gpio_reg;
+       u32 gpio_reg_val;
+
+       gpio_reg = ioremap(0x82000020, 4);
+       gpio_reg_val = readl(gpio_reg);
+       gpio_reg_val = gpio_reg_val & 0xfffffffe;
+       writel(gpio_reg_val, gpio_reg);
+
+       qsfp_set_state(qsfp, qsfp->state & QSFP_INIT);
+
+       err = qsfp_read(qsfp, false, QSFP_EXT_STATUS, &val, sizeof(val));
+       if (err != sizeof(val)) {
+               dev_err(qsfp->dev, "Failed to read EEPROM: %d\n", err);
+               return -EAGAIN;
+       }
+
+       /* DM7052 reports as a high power module, responds to reads (with
+        * all bytes 0xff) at 0x51 but does not accept writes.  In any case,
+        * if the bit is already set, we're already in high power mode.
+        */
+       if (!!(val & BIT(0)) == enable)
+               return 0;
+
+       if (enable)
+               val |= BIT(0);
+       else
+               val &= ~BIT(0);
+
+       err = qsfp_write(qsfp, false, QSFP_EXT_STATUS, &val, sizeof(val));
+       if (err != sizeof(val)) {
+               dev_err(qsfp->dev, "Failed to write EEPROM: %d\n", err);
+               return -EAGAIN;
+       }
+
+       if (enable)
+               dev_info(qsfp->dev, "Module switched to %u.%uW power level\n",
+                        qsfp->module_power_mw / 1000,
+                        (qsfp->module_power_mw / 100) % 10);
+
+       return 0;
+}
+
+/* GPON modules based on Realtek RTL8672 and RTL9601C chips (e.g. V-SOL
+ * V2801F, CarlitoxxPro CPGOS03-0490, Ubiquiti U-Fiber Instant, ...) do
+ * not support multibyte reads from the EEPROM. Each multi-byte read
+ * operation returns just one byte of EEPROM followed by zeros. There is
+ * no way to identify which modules are using Realtek RTL8672 and RTL9601C
+ * chips. Moreover every OEM of V-SOL V2801F module puts its own vendor
+ * name and vendor id into EEPROM, so there is even no way to detect if
+ * module is V-SOL V2801F. Therefore check for those zeros in the read
+ * data and then based on check switch to reading EEPROM to one byte
+ * at a time.
+ */
+static bool qsfp_id_needs_byte_io(struct qsfp *qsfp, void *buf, size_t len)
+{
+       size_t i, block_size = qsfp->i2c_block_size;
+
+       /* Already using byte IO */
+       if (block_size == 1)
+               return false;
+
+       for (i = 1; i < len; i += block_size) {
+               if (memchr_inv(buf + i, '\0', min(block_size - 1, len - i)))
+                       return false;
+       }
+       return true;
+}
+
+static void get_module_revision(struct qsfp *old)
+{
+       int ret;
+       u8 buf[16];
+       char buf_16[16];
+
+               ret = qsfp_read(qsfp, false, QSFP_VENDOR_NAME, buf_16, 16);
+               buf_16[15] = '\0';
+               strcpy(qsfp->vender_name, buf_16);
+               ret = qsfp_read(qsfp, false, QSFP_VENDOR_PN, buf_16, 16);
+               buf_16[15] = '\0';
+               strcpy(qsfp->part_number, buf_16);
+               ret = qsfp_read(qsfp, false, QSFP_VENDOR_SN, buf_16, 16);
+               buf_16[15] = '\0';
+               strcpy(qsfp->sn_number, buf_16);
+               ret = qsfp_read(qsfp, false, QSFP_STATUS, buf, 1);
+               qsfp->module_revision = buf[0];
+}
+
+int get_cable_attach(struct qsfp *old)
+{
+       return qsfp->module_present;
+}
+EXPORT_SYMBOL(get_cable_attach);
+
+int get_channel_info(struct qsfp *old)
+{
+       return qsfp->channel_number;
+}
+EXPORT_SYMBOL(get_channel_info);
+
+static int qsfp_select_eeprom_page(struct qsfp *qsfp)
+{
+       int err;
+       u8 buf[16];
+       u8 i = 0;
+       int ret;
+
+       err = qsfp_write(qsfp, false, QSFP_PAGE_SELECT_BYTE, &i, 1);
+
+       ret = qsfp_read(qsfp, false, QSFP_PAGE_SELECT_BYTE, buf, 1);
+
+       return 0;
+}
+
+static int qsfp_status_indicators(struct qsfp *qsfp)
+{
+       int ret;
+       u8 buf[16];
+       bool flag = false;
+       static unsigned int prv_buf, prv_buf_rx;
+
+       qsfp->state = qsfp_gpio_get_state(qsfp);
+       if (qsfp->state & QSFP_F_PRESENT) {
+               ret = qsfp_read(qsfp, false, QSFP_STATUS, buf, 1);
+               qsfp->module_revision = buf[0];
+               if (qsfp->module_revision == 0x0) {
+                       qsfp->module_present = TRUE;
+                       qsfp->channel_number = -EOPNOTSUPP;
+               }
+
+               if (qsfp->module_revision == 0x7 || qsfp->module_revision == 
0x8) {
+                       ret = qsfp_read(qsfp, false, QSFP_OPTIONS, buf, 1);
+
+                       if (buf[0] & QSFP_OPTIONS_TX_LOSS_SIGNAL) {
+                               ret = qsfp_read(qsfp, false, QSFP_RX_TX_LOSS, 
buf, 1);
+
+                       if (prv_buf != buf[0]) {
+                               prv_buf = buf[0];
+                               flag = true;
+                       }
+                       if (flag) {
+                               qsfp->module_present = TRUE;
+                               qsfp->channel_number = buf[0];
+                       } else {
+                               qsfp->module_present = TRUE;
+                               qsfp->channel_number = buf[0];
+                       }
+                       } else {
+                               ret = qsfp_read(qsfp, false, QSFP_RX_TX_LOSS, 
buf, 1);
+                               buf[0] = buf[0] & 0xf;
+                       if (prv_buf_rx != buf[0]) {
+                               prv_buf_rx = buf[0];
+                               flag = true;
+                               }
+                       if (flag) {
+                               qsfp->module_present = TRUE;
+                               qsfp->channel_number = buf[0];
+                       } else {
+                               qsfp->module_present = TRUE;
+                               qsfp->channel_number = buf[0];
+                       }
+               }
+       }
+
+       } else {
+               qsfp->module_present = -EINVAL;
+               qsfp->channel_number = -EOPNOTSUPP;
+               }
+
+       return 0;
+}
+
+static int qsfp_state_indicators(struct qsfp *qsfp)
+{
+       int ret;
+       u8 buf[16];
+       bool flag = false;
+
+       qsfp->state = qsfp_gpio_get_state(qsfp);
+       qsfp->state &= QSFP_F_PRESENT;
+       if (previous_state != qsfp->state) {
+               previous_state = (qsfp->state);
+               flag = true;
+       }
+
+       if (flag) {
+               if (qsfp->state & QSFP_F_PRESENT) {
+                       ret = qsfp_read(qsfp, false, QSFP_PHYS_ID, buf, 1);
+
+                       get_module_revision(qsfp);
+                       pr_info("module: %s pn: %s sn: %s rev: %x\n", 
qsfp->vender_name,
+                               qsfp->part_number, qsfp->sn_number, 
qsfp->module_revision);
+
+               } else {
+                       qsfp->module_present = -EINVAL;
+                       qsfp->channel_number = -EOPNOTSUPP;
+               }
+       }
+
+       return 0;
+}
+
+static int qsfp_cotsworks_fixup_check(struct qsfp *qsfp,
+                                     struct qsfp_eeprom_id *id)
+{
+       u8 check;
+       int err;
+
+       err = qsfp_write(qsfp, false, QSFP_PAGE_SELECT_BYTE, (u8 *)0x2, 1);
+
+       if (id->base.etile_qsfp_identifier != SFF8024_ID_QSFP_DD_INF_8628 ||
+           id->base.etile_qsfp_ext_identifier != QSFP_EXT_IDENTIFIER ||
+           id->base.etile_qsfp_connector_type !=
+                   SFF8024_QSFP_DD_CONNECTOR_LC) {
+               dev_warn(qsfp->dev,
+                        "Rewriting fiber module EEPROM with corrected 
values\n");
+               id->base.etile_qsfp_identifier = SFF8024_ID_QSFP_DD_INF_8628;
+               id->base.etile_qsfp_ext_identifier = QSFP_EXT_IDENTIFIER;
+               id->base.etile_qsfp_connector_type =
+                       SFF8024_QSFP_DD_CONNECTOR_LC;
+               err = qsfp_write(qsfp, false, QSFP_PHYS_ID, &id->base, 3);
+               if (err != 3) {
+                       dev_err(qsfp->dev,
+                               "Failed to rewrite module EEPROM: %d\n", err);
+                       return err;
+               }
+
+               /* Cotsworks modules have been found to require a delay between 
write operations. */
+               mdelay(50);
+
+               /* Update base structure checksum */
+               check = qsfp_check(&id->base, sizeof(id->base) - 1);
+               err = qsfp_write(qsfp, false, QSFP_CC_BASE, &check, 1);
+               if (err != 1) {
+                       dev_err(qsfp->dev,
+                               "Failed to update base structure checksum in 
fiber module EEPROM: %d\n",
+                               err);
+                       return err;
+               }
+       }
+       return 0;
+}
+
+static int qsfp_sm_mod_probe(struct qsfp *qsfp, bool report)
+{
+       /* qsfp module inserted - read I2C data */
+       struct qsfp_eeprom_id id;
+       bool cotsworks_sfbg;
+       bool cotsworks;
+
+       int ret;
+
+       /* Some qsfp modules and also some Linux I2C drivers do not like reads
+        * longer than 16 bytes, so read the EEPROM in chunks of 16 bytes at
+        * a time.
+        */
+
+       qsfp->i2c_block_size = 16;
+
+       qsfp_select_eeprom_page(qsfp);
+
+       ret = qsfp_read(qsfp, false, 0x0, &id.base, sizeof(id.base));
+       if (ret < 0) {
+               if (report)
+                       dev_err(qsfp->dev, "failed to read EEPROM: %d\n", ret);
+                               return -EAGAIN;
+       }
+
+       if (ret != sizeof(id.base)) {
+               dev_err(qsfp->dev, "EEPROM short read: %d\n", ret);
+
+               return -EAGAIN;
+       }
+
+       /* Some qsfp modules (e.g. Nokia 3FE46541AA) lock up if read from
+        * address 0x51 is just one byte at a time. Also SFF-8472 requires
+        * that EEPROM supports atomic 16bit read operation for diagnostic
+        * fields, so do not switch to one byte reading at a time unless it
+        * is really required and we have no other option.
+        */
+       if (qsfp_id_needs_byte_io(qsfp, &id.base, sizeof(id.base))) {
+               dev_info(qsfp->dev,
+                        "Detected broken RTL8672/RTL9601C emulated EEPROM\n");
+
+               dev_info(qsfp->dev,
+                        "Switching to reading EEPROM to one byte at a time\n");
+
+               qsfp->i2c_block_size = 1;
+
+               ret = qsfp_read(qsfp, false, 0, &id.base, sizeof(id.base));
+               if (ret < 0) {
+                       if (report) {
+                               dev_err(qsfp->dev,
+                                       "failed to read EEPROM: %d\n", ret);
+                       }
+
+                       return -EAGAIN;
+               }
+
+               if (ret != sizeof(id.base)) {
+                       dev_err(qsfp->dev, "EEPROM short read: %d\n", ret);
+
+                       return -EAGAIN;
+               }
+       }
+
+       /* Cotsworks do not seem to update the checksums when they
+        * do the final programming with the final module part number,
+        * serial number and date code.
+        */
+       cotsworks =
+               !memcmp(id.base.etile_qsfp_vendor_name, "COTSWORKS       ", 16);
+       cotsworks_sfbg = !memcmp(id.base.etile_qsfp_vendor_pn, "SFBG", 4);
+
+       /* Cotsworks SFF module EEPROM do not always have valid 
etile_qsfp_identifier,
+        * etile_qsfp_ext_identifier, and connector bytes.  Rewrite SFF EEPROM 
bytes if
+        * Cotsworks PN matches and bytes are not correct.
+        */
+       if (cotsworks && cotsworks_sfbg) {
+               ret = qsfp_cotsworks_fixup_check(qsfp, &id);
+
+               if (ret < 0)
+                       return ret;
+       }
+
+       qsfp->id = id;
+       dev_info(qsfp->dev, "module %.*s %.*s rev %.*x sn %.*s dc %.*s\n",
+                (int)sizeof(id.base.etile_qsfp_vendor_name),
+                id.base.etile_qsfp_vendor_name,
+                (int)sizeof(id.base.etile_qsfp_vendor_pn),
+                id.base.etile_qsfp_vendor_pn,
+                (int)sizeof(id.base.etile_qsfp_revision),
+                id.base.etile_qsfp_revision,
+                (int)sizeof(id.base.etile_qsfp_vendor_serial_number),
+                id.base.etile_qsfp_vendor_serial_number,
+                (int)sizeof(id.base.etile_qsfp_vendor_date_code),
+                id.base.etile_qsfp_vendor_date_code);
+
+       /* Check whether we support this module */
+       if (!qsfp->type->module_supported(&id)) {
+               dev_err(qsfp->dev,
+                       "module is not supported - phys id 0x%02x 0x%02x\n",
+                       qsfp->id.base.etile_qsfp_identifier_1,
+                       qsfp->id.base.etile_qsfp_ext_identifier);
+               return -EINVAL;
+       }
+
+       /* Parse the module power requirement */
+       ret = qsfp_module_parse_power(qsfp);
+       if (ret < 0)
+               return ret;
+
+       if (!memcmp(id.base.etile_qsfp_vendor_name, "YAMAICHI   ", 16) &&
+           !memcmp(id.base.etile_qsfp_vendor_pn, "3FE46541AA      ", 16))
+               qsfp->module_t_start_up = T_START_UP_BAD_GPON;
+       else
+               qsfp->module_t_start_up = T_START_UP;
+
+       return 0;
+}
+
+static void qsfp_sm_mod_remove(struct qsfp *qsfp)
+{
+       if (qsfp->sm_mod_state > QSFP_MOD_WAITDEV)
+               qsfp_module_remove(qsfp->qsfp_bus);
+
+       memset(&qsfp->id, 0, sizeof(qsfp->id));
+       qsfp->module_power_mw = 0;
+
+       dev_info(qsfp->dev, "module removed\n");
+}
+
+/* This state machine tracks the upstream's state */
+static void qsfp_sm_device(struct qsfp *qsfp, unsigned int event)
+{
+       switch (qsfp->sm_dev_state) {
+       default:
+               if (event == QSFP_E_DEV_ATTACH)
+                       qsfp->sm_dev_state = QSFP_DEV_DOWN;
+               break;
+       case QSFP_DEV_DOWN:
+
+               if (event == QSFP_E_DEV_DETACH)
+                       qsfp->sm_dev_state = QSFP_DEV_DETACHED;
+
+               else if (event == QSFP_E_DEV_UP)
+                       qsfp->sm_dev_state = QSFP_DEV_UP;
+               break;
+
+       case QSFP_DEV_UP:
+
+               if (event == QSFP_E_DEV_DETACH)
+                       qsfp->sm_dev_state = QSFP_DEV_DETACHED;
+               else if (event == QSFP_E_DEV_DOWN)
+                       qsfp->sm_dev_state = QSFP_DEV_DOWN;
+               break;
+       }
+}
+
+/* This state machine tracks the insert/remove state of the module, probes
+ * the on-board EEPROM, and sets up the power level.
+ */
+static void qsfp_sm_module(struct qsfp *qsfp, unsigned int event)
+{
+       int err;
+
+       /* Handle remove event globally, it resets this state machine */
+       if (event == QSFP_E_REMOVE) {
+               if (qsfp->sm_mod_state > QSFP_MOD_PROBE)
+                       qsfp_sm_mod_remove(qsfp);
+               qsfp_sm_mod_next(qsfp, QSFP_MOD_EMPTY, 0);
+               return;
+       }
+
+       /* Handle device detach globally */
+       if (qsfp->sm_dev_state < QSFP_DEV_DOWN &&
+           qsfp->sm_mod_state > QSFP_MOD_WAITDEV) {
+               if (qsfp->module_power_mw > 1000 &&
+                   qsfp->sm_mod_state > QSFP_MOD_HPOWER)
+                       qsfp_sm_mod_hpower(qsfp, false);
+               qsfp_sm_mod_next(qsfp, QSFP_MOD_WAITDEV, 0);
+               return;
+       }
+
+       switch (qsfp->sm_mod_state) {
+       default:
+               if (event == QSFP_E_INSERT) {
+                       qsfp_sm_mod_next(qsfp, QSFP_MOD_PROBE, T_SERIAL);
+                       qsfp->sm_mod_tries_init = R_PROBE_RETRY_INIT;
+                       qsfp->sm_mod_tries = R_PROBE_RETRY_SLOW;
+               }
+               break;
+
+       case QSFP_MOD_PROBE:
+               /* Wait for T_PROBE_INIT to time out */
+               if (event != QSFP_E_TIMEOUT)
+                       break;
+
+               err = qsfp_sm_mod_probe(qsfp, qsfp->sm_mod_tries == 1);
+               if (err == -EAGAIN) {
+                       if (qsfp->sm_mod_tries_init &&
+                           --qsfp->sm_mod_tries_init) {
+                               qsfp_sm_set_timer(qsfp, T_PROBE_RETRY_INIT);
+                               break;
+                       } else if (qsfp->sm_mod_tries && --qsfp->sm_mod_tries) {
+                               if (qsfp->sm_mod_tries == R_PROBE_RETRY_SLOW - 
1)
+                                       dev_warn(qsfp->dev, "please wait, 
module slow to respond\n");
+                               qsfp_sm_set_timer(qsfp, T_PROBE_RETRY_SLOW);
+                               break;
+                       }
+               }
+               if (err < 0) {
+                       qsfp_sm_mod_next(qsfp, QSFP_MOD_ERROR, 0);
+                       break;
+               }
+
+               fallthrough;
+       case QSFP_MOD_WAITDEV:
+
+               /* Ensure that the device is attached before proceeding */
+               if (qsfp->sm_dev_state < QSFP_DEV_DOWN)
+                       break;
+
+               /* Report the module insertion to the upstream device */
+               qsfp->id.base.etile_qsfp_identifier = 0x11; //
+               err = qsfp_module_insert(qsfp->qsfp_bus, &qsfp->id);
+
+               if (err < 0) {
+                       qsfp_sm_mod_next(qsfp, QSFP_MOD_ERROR, 0);
+                       break;
+               }
+
+               /* If this is a power level 1 module, we are done */
+               if (qsfp->module_power_mw <= 1000)
+                       goto insert;
+
+               qsfp_sm_mod_next(qsfp, QSFP_MOD_HPOWER, 0);
+               fallthrough;
+       case QSFP_MOD_HPOWER:
+               /* Enable high power mode */
+               err = qsfp_sm_mod_hpower(qsfp, true);
+               if (err < 0) {
+                       if (err != -EAGAIN) {
+                               qsfp_module_remove(qsfp->qsfp_bus);
+                               qsfp_sm_mod_next(qsfp, QSFP_MOD_ERROR, 0);
+                       } else {
+                               qsfp_sm_set_timer(qsfp, T_PROBE_RETRY_INIT);
+                       }
+                       break;
+               }
+
+               qsfp_sm_mod_next(qsfp, QSFP_MOD_WAITPWR, T_HPOWER_LEVEL);
+               break;
+
+       case QSFP_MOD_WAITPWR:
+
+               /* Wait for T_HPOWER_LEVEL to time out */
+               if (event != QSFP_E_TIMEOUT)
+                       break;
+
+insert:
+
+               qsfp_sm_mod_next(qsfp, QSFP_MOD_PRESENT, 0);
+               break;
+
+       case QSFP_MOD_PRESENT:
+       case QSFP_MOD_ERROR:
+               break;
+       }
+}
+
+static void qsfp_sm_main(struct qsfp *qsfp, unsigned int event)
+{
+       unsigned long timeout;
+       int ret;
+
+       /* Some events are global */
+       if (qsfp->sm_state != QSFP_S_DOWN &&
+           (qsfp->sm_mod_state != QSFP_MOD_PRESENT ||
+            qsfp->sm_dev_state != QSFP_DEV_UP)) {
+               if (qsfp->sm_state == QSFP_S_LINK_UP &&
+                   qsfp->sm_dev_state == QSFP_DEV_UP)
+                       qsfp_sm_link_down(qsfp);
+               if (qsfp->sm_state > QSFP_S_INIT)
+                       qsfp_module_stop(qsfp->qsfp_bus);
+               if (qsfp->mod_phy)
+                       qsfp_sm_phy_detach(qsfp);
+               qsfp_module_tx_disable(qsfp);
+               qsfp_soft_stop_poll(qsfp);
+               qsfp_sm_next(qsfp, QSFP_S_DOWN, 0);
+               return;
+       }
+
+       /* The main state machine */
+       switch (qsfp->sm_state) {
+       case QSFP_S_DOWN:
+
+               if (qsfp->sm_mod_state != QSFP_MOD_PRESENT ||
+                   qsfp->sm_dev_state != QSFP_DEV_UP)
+                       break;
+
+               if (!(qsfp->id.base.etile_qsfp_diag_monitor &
+                     QSFP_DIAGMON_ADDRMODE))
+                       qsfp_soft_start_poll(qsfp);
+
+               qsfp_module_tx_enable(qsfp);
+
+               /* Initialise the fault clearance retries */
+               qsfp->sm_fault_retries = N_FAULT_INIT;
+
+               /* We need to check the TX_FAULT state, which is not defined
+                * while TX_DISABLE is asserted. The earliest we want to do
+                * anything (such as probe for a PHY) is 50ms.
+                */
+               qsfp_sm_next(qsfp, QSFP_S_WAIT, T_WAIT);
+               break;
+
+       case QSFP_S_WAIT:
+
+               if (event != QSFP_E_TIMEOUT)
+
+                       break;
+
+               if (qsfp->id.base.etile_qsfp_options_3 &
+                   QSFP_OPTIONS_TX_FAULT) {
+                       /* Wait up to t_init (SFF-8472) or t_start_up (SFF-8431)
+                        * from the TX_DISABLE deassertion for the module to
+                        * initialise, which is indicated by TX_FAULT
+                        * deasserting.
+                        */
+                       timeout = qsfp->module_t_start_up;
+                       if (timeout > T_WAIT)
+                               timeout -= T_WAIT;
+                       else
+                               timeout = 1;
+
+                       qsfp_sm_next(qsfp, QSFP_S_INIT, timeout);
+               } else {
+                       /* TX_FAULT is not asserted, assume the module has
+                        * finished initialising.
+                        */
+                       goto init_done;
+               }
+               break;
+
+       case QSFP_S_INIT:
+
+               if (event == QSFP_E_TIMEOUT &&
+                   qsfp->id.base.etile_qsfp_options_3 &
+                           QSFP_OPTIONS_TX_FAULT) {
+                       /* TX_FAULT is still asserted after t_init
+                        * or t_start_up, so assume there is a fault.
+                        */
+                       qsfp_sm_fault(qsfp, QSFP_S_INIT_TX_FAULT,
+                                     qsfp->sm_fault_retries == N_FAULT_INIT);
+               } else if (event == QSFP_E_TIMEOUT ||
+                          event == QSFP_E_TX_CLEAR) {
+init_done:
+
+                       qsfp->sm_phy_retries = R_PHY_RETRY;
+                       goto phy_probe;
+               }
+               break;
+
+       case QSFP_S_INIT_PHY:
+
+               if (event != QSFP_E_TIMEOUT)
+                       break;
+phy_probe:
+               /* TX_FAULT deasserted or we timed out with TX_FAULT
+                * clear.  Probe for the PHY and check the LOS state.
+                */
+               ret = qsfp_sm_probe_for_phy(qsfp);
+               if (ret == -ENODEV) {
+                       if (--qsfp->sm_phy_retries) {
+                               qsfp_sm_next(qsfp, QSFP_S_INIT_PHY,
+                                            T_PHY_RETRY);
+                                       dev_info(qsfp->dev, "PHY detected\n");
+                               break;
+                       }
+               } else if (ret) {
+                       qsfp_sm_next(qsfp, QSFP_S_FAIL, 0);
+                       break;
+               }
+               if (qsfp_module_start(qsfp->qsfp_bus)) {
+                       qsfp_sm_next(qsfp, QSFP_S_FAIL, 0);
+                       break;
+               }
+               qsfp_sm_link_check_los(qsfp);
+
+               /* Reset the fault retry count */
+               qsfp->sm_fault_retries = N_FAULT;
+               break;
+
+       case QSFP_S_INIT_TX_FAULT:
+
+               if (event == QSFP_E_TIMEOUT) {
+                       qsfp_module_tx_fault_reset(qsfp);
+                       qsfp_sm_next(qsfp, QSFP_S_INIT,
+                                    qsfp->module_t_start_up);
+               }
+
+               break;
+
+       case QSFP_S_WAIT_LOS:
+
+               if (event == QSFP_E_TX_FAULT)
+                       qsfp_sm_fault(qsfp, QSFP_S_TX_FAULT, true);
+               else if (qsfp_los_event_inactive(qsfp, event))
+                       qsfp_sm_link_up(qsfp);
+
+               break;
+
+       case QSFP_S_LINK_UP:
+
+               if (event == QSFP_E_TX_FAULT) {
+                       qsfp_sm_link_down(qsfp);
+                       qsfp_sm_fault(qsfp, QSFP_S_TX_FAULT, true);
+               } else if (qsfp_los_event_active(qsfp, event)) {
+                       qsfp_sm_link_down(qsfp);
+                       qsfp_sm_next(qsfp, QSFP_S_WAIT_LOS, 0);
+               }
+
+               break;
+
+       case QSFP_S_TX_FAULT:
+
+               if (event == QSFP_E_TIMEOUT) {
+                       qsfp_module_tx_fault_reset(qsfp);
+                       qsfp_sm_next(qsfp, QSFP_S_REINIT,
+                                    qsfp->module_t_start_up);
+               }
+
+               break;
+
+       case QSFP_S_REINIT:
+
+               if (event == QSFP_E_TIMEOUT &&
+                   qsfp->id.base.etile_qsfp_options_3 &
+                           QSFP_OPTIONS_TX_FAULT) {
+                       qsfp_sm_fault(qsfp, QSFP_S_TX_FAULT, false);
+               } else if (event == QSFP_E_TIMEOUT ||
+                          event == QSFP_E_TX_CLEAR) {
+                       dev_info(qsfp->dev,
+                                "module transmit fault recovered\n");
+                       qsfp_sm_link_check_los(qsfp);
+               };
+
+               break;
+
+       case QSFP_S_TX_DISABLE:
+
+               break;
+       }
+}
+
+static void qsfp_sm_event(struct qsfp *qsfp, unsigned int event)
+{
+       mutex_lock(&qsfp->sm_mutex);
+
+       dev_dbg(qsfp->dev, "SM: enter %s:%s:%s event %s\n",
+               mod_state_to_str(qsfp->sm_mod_state),
+               dev_state_to_str(qsfp->sm_dev_state),
+               sm_state_to_str(qsfp->sm_state), event_to_str(event));
+
+       qsfp_sm_device(qsfp, event);
+       qsfp_sm_module(qsfp, event);
+       qsfp_sm_main(qsfp, event);
+
+       dev_dbg(qsfp->dev, "SM: exit %s:%s:%s\n",
+               mod_state_to_str(qsfp->sm_mod_state),
+               dev_state_to_str(qsfp->sm_dev_state),
+               sm_state_to_str(qsfp->sm_state));
+
+       mutex_unlock(&qsfp->sm_mutex);
+}
+
+void qsfp_attach(struct qsfp *old)
+{
+       qsfp_sm_event(qsfp, QSFP_E_DEV_ATTACH);
+}
+EXPORT_SYMBOL(qsfp_attach);
+
+void qsfp_detach(struct qsfp *old)
+{
+       qsfp_sm_event(qsfp, QSFP_E_DEV_DETACH);
+}
+EXPORT_SYMBOL(qsfp_detach);
+
+void qsfp_start(struct qsfp *old)
+{
+       qsfp_sm_event(qsfp, QSFP_E_DEV_UP);
+}
+EXPORT_SYMBOL(qsfp_start);
+
+void qsfp_stop(struct qsfp *old)
+{
+       qsfp_sm_event(qsfp, QSFP_E_DEV_DOWN);
+}
+EXPORT_SYMBOL(qsfp_stop);
+
+static int qsfp_module_info(struct qsfp *qsfp, struct ethtool_modinfo *modinfo)
+{
+       /* locking... and check module is present */
+
+       if (qsfp->id.base.etile_qsfp_spec_compliance_1[0] &&
+           !(qsfp->id.base.etile_qsfp_diag_monitor & QSFP_DIAGMON_ADDRMODE)) {
+               modinfo->type = ETH_MODULE_SFF_8472;
+               modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN;
+       } else {
+               modinfo->type = ETH_MODULE_SFF_8079;
+               modinfo->eeprom_len = ETH_MODULE_SFF_8079_LEN;
+       }
+       return 0;
+}
+
+static int qsfp_module_eeprom(struct qsfp *qsfp, struct ethtool_eeprom *ee,
+                             u8 *data)
+{
+       unsigned int first, last, len;
+       int ret;
+
+       if (ee->len == 0)
+               return -EINVAL;
+
+       first = ee->offset;
+       last = ee->offset + ee->len;
+       if (first < ETH_MODULE_SFF_8079_LEN) {
+               len = min_t(unsigned int, last, ETH_MODULE_SFF_8079_LEN);
+               len -= first;
+
+               ret = qsfp_read(qsfp, false, first, data, len);
+               if (ret < 0)
+                       return ret;
+
+               first += len;
+               data += len;
+       }
+       if (first < ETH_MODULE_SFF_8472_LEN && last > ETH_MODULE_SFF_8079_LEN) {
+               len = min_t(unsigned int, last, ETH_MODULE_SFF_8472_LEN);
+               len -= first;
+               first -= ETH_MODULE_SFF_8079_LEN;
+
+               ret = qsfp_read(qsfp, false, first, data, len);
+               if (ret < 0)
+                       return ret;
+       }
+       return 0;
+}
+
+static const struct qsfp_socket_ops qsfp_module_ops = {
+       .attach = qsfp_attach,
+       .detach = qsfp_detach,
+       .start = qsfp_start,
+       .stop = qsfp_stop,
+       .module_info = qsfp_module_info,
+       .module_eeprom = qsfp_module_eeprom,
+};
+
+static void qsfp_timeout(struct work_struct *work)
+{
+       struct qsfp *qsfp = container_of(work, struct qsfp, timeout.work);
+
+       rtnl_lock();
+       qsfp_sm_event(qsfp, QSFP_E_TIMEOUT);
+       rtnl_unlock();
+}
+
+static void qsfp_check_state(struct qsfp *qsfp)
+{
+       unsigned int state, i, changed;
+
+       mutex_lock(&qsfp->st_mutex);
+       state = qsfp_get_state(qsfp);
+
+       changed = state ^ qsfp->state;
+       changed &= QSFP_F_PRESENT | QSFP_OPTIONS_TX_LOSS_SIGNAL |
+                  QSFP_OPTIONS_TX_FAULT;
+
+       for (i = 0; i < GPIO_MAX; i++)
+               if (changed & BIT(i))
+                       dev_dbg(qsfp->dev, "%s %u -> %u\n", gpio_of_names[i],
+                               !!(qsfp->state & BIT(i)), !!(state & BIT(i)));
+
+       qsfp->state = state;
+
+       rtnl_lock();
+       if (changed & QSFP_F_PRESENT)
+               qsfp_sm_event(qsfp, state & QSFP_F_PRESENT ? QSFP_E_INSERT :
+                                                                  
QSFP_E_REMOVE);
+
+       if (changed & QSFP_OPTIONS_TX_FAULT)
+               qsfp_sm_event(qsfp, state & QSFP_OPTIONS_TX_FAULT ?
+                                           QSFP_E_TX_FAULT :
+                                                 QSFP_E_TX_CLEAR);
+
+       if (changed & QSFP_OPTIONS_TX_LOSS_SIGNAL)
+               qsfp_sm_event(qsfp, state & QSFP_OPTIONS_TX_LOSS_SIGNAL ?
+                                           QSFP_E_TX_LOS :
+                                                 QSFP_E_RX_LOS);
+
+       rtnl_unlock();
+       mutex_unlock(&qsfp->st_mutex);
+}
+
+static irqreturn_t qsfp_irq(int irq, void *data)
+{
+       struct qsfp *qsfp = data;
+
+       qsfp_check_state(qsfp);
+
+       return IRQ_HANDLED;
+}
+
+static void qsfp_poll(struct work_struct *work)
+{
+       struct qsfp *qsfp = container_of(work, struct qsfp, poll.work);
+
+       qsfp_check_state(qsfp);
+
+       qsfp_state_indicators(qsfp);
+       qsfp_status_indicators(qsfp);
+
+       if (qsfp->state_soft_mask &
+                   (QSFP_OPTIONS_TX_LOSS_SIGNAL | QSFP_OPTIONS_TX_FAULT) ||
+           qsfp->need_poll)
+               mod_delayed_work(system_wq, &qsfp->poll, poll_jiffies);
+}
+
+static struct qsfp *qsfp_alloc(struct device *dev)
+{
+       struct qsfp *qsfp;
+
+       qsfp = kzalloc(sizeof(*qsfp), GFP_KERNEL);
+       if (!qsfp)
+               return ERR_PTR(-ENOMEM);
+
+       qsfp->dev = dev;
+
+       mutex_init(&qsfp->sm_mutex);
+       mutex_init(&qsfp->st_mutex);
+
+       INIT_DELAYED_WORK(&qsfp->poll, qsfp_poll);
+
+       INIT_DELAYED_WORK(&qsfp->timeout, qsfp_timeout);
+
+       return qsfp;
+}
+
+static void qsfp_cleanup(void *data)
+{
+       struct qsfp *qsfp = data;
+
+       cancel_delayed_work_sync(&qsfp->poll);
+       cancel_delayed_work_sync(&qsfp->timeout);
+       if (qsfp->i2c_mii) {
+               mdiobus_unregister(qsfp->i2c_mii);
+               mdiobus_free(qsfp->i2c_mii);
+       }
+       if (qsfp->i2c)
+               i2c_put_adapter(qsfp->i2c);
+       kfree(qsfp);
+}
+
+static int qsfp_probe(struct platform_device *pdev)
+{
+       const struct sff_data *sff;
+       struct i2c_adapter *i2c;
+       char *qsfp_irq_name;
+       int err, i;
+       static void *reg_addr;
+       u32 reg_value;
+
+       qsfp = qsfp_alloc(&pdev->dev);
+
+       if (IS_ERR(qsfp))
+               return PTR_ERR(qsfp);
+
+       platform_set_drvdata(pdev, qsfp);
+
+       err = devm_add_action(qsfp->dev, qsfp_cleanup, qsfp);
+       if (err < 0)
+               return err;
+
+       qsfp->type = &qsfp_data;
+       sff = qsfp->type;
+
+       if (pdev->dev.of_node) {
+               struct device_node *node = pdev->dev.of_node;
+               const struct of_device_id *id;
+               struct device_node *np;
+
+               id = of_match_node(qsfp_of_match, node);
+               if (WARN_ON(!id))
+                       return -EINVAL;
+
+               qsfp->type = id->data;
+               sff = qsfp->type;
+
+               reg_addr = ioremap(0xffd11000, 0x40);
+               reg_addr += 0x28;
+               reg_value = readl(reg_addr);
+               //bring out from reset by writing to bit 8//
+               reg_value &= 0xfffffeff;
+               writel(reg_value, reg_addr);
+
+               np = of_parse_phandle(node, "i2c-bus", 0);
+
+               if (!np) {
+                       dev_err(qsfp->dev, "missing 'i2c-bus' property\n");
+                       return -ENODEV;
+               }
+
+               i2c = of_find_i2c_adapter_by_node(np);
+               of_node_put(np);
+       } else if (has_acpi_companion(&pdev->dev)) {
+               struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
+               struct fwnode_handle *fw = acpi_fwnode_handle(adev);
+               struct fwnode_reference_args args;
+               struct acpi_handle *acpi_handle;
+               int ret;
+
+               ret = acpi_node_get_property_reference(fw, "i2c-bus", 0, &args);
+               if (ret || !is_acpi_device_node(args.fwnode)) {
+                       dev_err(&pdev->dev, "missing 'i2c-bus' property\n");
+                       return -ENODEV;
+               }
+
+               acpi_handle = ACPI_HANDLE_FWNODE
+               (args.fwnode);
+               i2c = i2c_acpi_find_adapter_by_handle(acpi_handle);
+
+       } else {
+               return -EINVAL;
+       }
+
+       if (!i2c)
+               return -EPROBE_DEFER;
+
+       err = qsfp_i2c_configure(qsfp, i2c);
+       if (err < 0) {
+               i2c_put_adapter(i2c);
+
+               return err;
+       }
+       qsfp_reset(qsfp, 0);
+
+       for (i = 0; i < GPIO_MAX; i++)
+               if (sff->gpios & BIT(i)) {
+                       qsfp->gpio[i] = devm_gpiod_get_optional
+                       (qsfp->dev, gpio_of_names[i], gpio_flags[i]);
+
+                       if (IS_ERR(qsfp->gpio[i]))
+                               return PTR_ERR(qsfp->gpio[i]);
+               }
+
+       qsfp->get_state = qsfp_gpio_get_state;
+       qsfp->set_state = qsfp_gpio_set_state;
+
+       qsfp_reset(qsfp, 1);
+
+       /* Modules that have no detect signal are always present */
+       if (!(qsfp->gpio[GPIO_MODULE_PRESENT]))
+               qsfp->get_state = sff_gpio_get_state;
+
+       device_property_read_u32(&pdev->dev, "maximum-power-milliwatt",
+                                &qsfp->max_power_mw);
+       if (!qsfp->max_power_mw)
+               qsfp->max_power_mw = 1000;
+
+       qsfp_set_state(qsfp, qsfp->state | QSFP_SELECT | QSFP_F_PRESENT);
+
+       qsfp->state = qsfp_get_state(qsfp) | QSFP_OPTIONS_TX_DISABLE;
+
+       if (qsfp->state & QSFP_F_PRESENT) {
+               qsfp->state |= QSFP_SELECT;
+
+               qsfp->state = qsfp_get_state(qsfp) | QSFP_OPTIONS_TX_DISABLE;
+
+               qsfp_set_state(qsfp, qsfp->state);
+               rtnl_lock();
+               qsfp_sm_event(qsfp, QSFP_E_INSERT);
+
+               rtnl_unlock();
+       } else {
+               pr_info("qsfp is not present\n");
+       }
+
+       qsfp->gpio_irq[GPIO_MODULE_INTERRUPT] =
+               gpiod_to_irq(qsfp->gpio[GPIO_MODULE_INTERRUPT]);
+       if (qsfp->gpio_irq[GPIO_MODULE_INTERRUPT] < 0) {
+               qsfp->gpio_irq[GPIO_MODULE_INTERRUPT] = 0;
+               qsfp->need_poll = true;
+       }
+
+       qsfp_irq_name = devm_kasprintf(qsfp->dev, GFP_KERNEL, "%s-%s",
+                                      dev_name(qsfp->dev),
+                                      gpio_of_names[GPIO_MODULE_INTERRUPT]);
+
+       if (!qsfp_irq_name)
+               return -ENOMEM;
+
+       err = devm_request_threaded_irq(qsfp->dev, 
qsfp->gpio_irq[GPIO_MODULE_INTERRUPT],
+                                       NULL, qsfp_irq,
+                                       IRQF_ONESHOT |
+                                       IRQF_TRIGGER_RISING |
+                                       IRQF_TRIGGER_FALLING,
+                                       qsfp_irq_name, qsfp);
+
+       if (err) {
+               qsfp->gpio_irq[GPIO_MODULE_INTERRUPT] = 0;
+               qsfp->need_poll = true;
+       }
+
+       if (qsfp->need_poll)
+               mod_delayed_work(system_wq, &qsfp->poll, poll_jiffies);
+
+       /* We could have an issue in cases no Tx disable pin is available or
+        * wired as modules using a laser as their light source will continue to
+        * be active when the fiber is removed. This could be a safety issue and
+        * we should at least warn the user about that.
+        */
+       if (qsfp->id.base.etile_qsfp_options_3 & QSFP_OPTIONS_TX_DISABLE)
+               dev_warn(qsfp->dev, "No tx_disable pin: qsfp modules will 
always be emitting.\n");
+
+       qsfp->qsfp_bus =
+               qsfp_register_socket(qsfp->dev, qsfp, &qsfp_module_ops);
+       if (!qsfp->qsfp_bus)
+               return -ENOMEM;
+
+       get_module_revision(qsfp);
+
+       return 0;
+}
+
+static int qsfp_remove(struct platform_device *pdev)
+
+{
+       struct qsfp *qsfp = platform_get_drvdata(pdev);
+
+       qsfp_unregister_socket(qsfp->qsfp_bus);
+
+       rtnl_lock();
+       qsfp_sm_event(qsfp, QSFP_E_REMOVE);
+       rtnl_unlock();
+
+       return 0;
+}
+
+static struct platform_driver qsfp_driver = {
+       .probe = qsfp_probe,
+       .remove = qsfp_remove,
+       .driver = {
+               .name = "sff,qsfp",
+               .owner = THIS_MODULE,
+               .of_match_table = qsfp_of_match,
+       },
+};
+
+static int qsfp_init(void)
+{
+       poll_jiffies = msecs_to_jiffies(1000);
+
+       return platform_driver_register(&qsfp_driver);
+}
+module_init(qsfp_init);
+
+static void qsfp_exit(void)
+{
+       platform_driver_unregister(&qsfp_driver);
+}
+module_exit(qsfp_exit);
+
+MODULE_ALIAS("platform:qsfp");
+MODULE_AUTHOR("Malku Deepak");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/phy/qsfp_bus.c b/drivers/net/phy/qsfp_bus.c
new file mode 100644
index 000000000000..cd46c4f7ba13
--- /dev/null
+++ b/drivers/net/phy/qsfp_bus.c
@@ -0,0 +1,782 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <linux/export.h>
+#include <linux/kref.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/phylink.h>
+#include <linux/property.h>
+#include <linux/rtnetlink.h>
+#include <linux/slab.h>
+#include <linux/qsfp.h>
+
+
+
+struct qsfp_quirk {
+       const char *vendor;
+       const char *part;
+       void (*modes)(const struct qsfp_eeprom_id *id, unsigned long *modes);
+};
+
+/**
+ * struct qsfp_bus - internal representation of a qsfp bus
+ */
+struct qsfp_bus {
+       /* private: */
+       struct kref kref;
+       struct list_head node;
+       struct fwnode_handle *fwnode;
+
+       const struct qsfp_socket_ops *socket_ops;
+       struct device *qsfp_dev;
+       struct qsfp *qsfp;
+       const struct qsfp_quirk *qsfp_quirk;
+
+       const struct qsfp_upstream_ops *upstream_ops;
+       void *upstream;
+       struct phy_device *phydev;
+
+       bool registered;
+       bool started;
+};
+
+static void qsfp_quirk_2500basex(const struct qsfp_eeprom_id *id,
+                                unsigned long *modes)
+{
+       phylink_set(modes, 2500baseX_Full);
+}
+
+static void qsfp_quirk_ubnt_uf_instant(const struct qsfp_eeprom_id *id,
+                                      unsigned long *modes)
+{
+       /* Ubiquiti U-Fiber Instant module claims that support all transceiver
+        * types including 10G Ethernet which is not truth. So clear all claimed
+        * modes and set only one mode which module supports: 1000baseX_Full.
+        */
+       phylink_zero(modes);
+       phylink_set(modes, 1000baseX_Full);
+}
+
+static const struct qsfp_quirk qsfp_quirks[] = {
+       {
+               // Alcatel Lucent G-010S-P can operate at 2500base-X, but
+               // incorrectly report 2500MBd NRZ in their EEPROM
+               .vendor = "ALCATELLUCENT",
+               .part = "G010SP",
+               .modes = qsfp_quirk_2500basex,
+       },
+       {
+               // Alcatel Lucent G-010S-A can operate at 2500base-X, but
+               // report 3.2GBd NRZ in their EEPROM
+               .vendor = "ALCATELLUCENT",
+               .part = "3FE46541AA",
+               .modes = qsfp_quirk_2500basex,
+       },
+       {
+               // Huawei MA5671A can operate at 2500base-X, but report 1.2GBd
+               // NRZ in their EEPROM
+               .vendor = "HUAWEI",
+               .part = "MA5671A",
+               .modes = qsfp_quirk_2500basex,
+       },
+       {
+               .vendor = "UBNT",
+               .part = "UF-INSTANT",
+               .modes = qsfp_quirk_ubnt_uf_instant,
+       },
+};
+
+static size_t qsfp_strlen(const char *str, size_t maxlen)
+{
+       size_t size, i;
+
+       /* Trailing characters should be filled with space chars */
+       for (i = 0, size = 0; i < maxlen; i++)
+               if (str[i] != ' ')
+                       size = i + 1;
+
+       return size;
+}
+
+static bool qsfp_match(const char *qs, const char *str, size_t len)
+{
+       if (!qs)
+               return true;
+       if (strlen(qs) != len)
+               return false;
+       return !strncmp(qs, str, len);
+}
+
+static const struct qsfp_quirk *
+qsfp_lookup_quirk(const struct qsfp_eeprom_id *id)
+{
+       const struct qsfp_quirk *q;
+       unsigned int i;
+       size_t vs, ps;
+
+       vs = qsfp_strlen(id->base.etile_qsfp_vendor_name,
+                        ARRAY_SIZE(id->base.etile_qsfp_vendor_name));
+       ps = qsfp_strlen(id->base.etile_qsfp_vendor_pn,
+                        ARRAY_SIZE(id->base.etile_qsfp_vendor_pn));
+
+       for (i = 0, q = qsfp_quirks; i < ARRAY_SIZE(qsfp_quirks); i++, q++)
+               if (qsfp_match(q->vendor, id->base.etile_qsfp_vendor_name,
+                              vs) &&
+                   qsfp_match(q->part, id->base.etile_qsfp_vendor_pn, ps))
+                       return q;
+
+       return NULL;
+}
+
+/**
+ * qsfp_parse_port() - Parse the EEPROM base ID, setting the port type
+ * @bus: a pointer to the &struct qsfp_bus structure for the qsfp module
+ * @id: a pointer to the module's &struct qsfp_eeprom_id
+ * @support: optional pointer to an array of unsigned long for the
+ *   ethtool support mask
+ *
+ * Parse the EEPROM identification given in @id, and return one of
+ * %PORT_TP, %PORT_FIBRE or %PORT_OTHER. If @support is non-%NULL,
+ * also set the ethtool %ETHTOOL_LINK_MODE_xxx_BIT corresponding with
+ * the connector type.
+ *
+ * If the port type is not known, returns %PORT_OTHER.
+ */
+
+int qsfp_parse_port(struct qsfp_bus *bus, const struct qsfp_eeprom_id *id,
+                   unsigned long *support)
+{
+       int port;
+
+       /* port is the physical connector, set this from the connector field. */
+       switch (id->base.etile_qsfp_connector_type) {
+       case SFF8024_QSFP_DD_CONNECTOR_SC:
+       case SFF8024_QSFP_DD_CONNECTOR_FIBERJACK:
+       case SFF8024_QSFP_DD_CONNECTOR_LC:
+       case SFF8024_QSFP_DD_CONNECTOR_MT_RJ:
+       case SFF8024_QSFP_DD_CONNECTOR_MU:
+       case SFF8024_QSFP_DD_CONNECTOR_OPTICAL_PIGTAIL:
+       case SFF8024_QSFP_DD_CONNECTOR_MPO_1X12:
+       case SFF8024_QSFP_DD_CONNECTOR_MPO_2X16:
+               port = PORT_FIBRE;
+               break;
+
+       case SFF8024_QSFP_DD_CONNECTOR_RJ45:
+               port = PORT_TP;
+               break;
+
+       case SFF8024_QSFP_DD_CONNECTOR_COPPER_PIGTAIL:
+               port = PORT_DA;
+               break;
+
+       case SFF8024_QSFP_DD_CONNECTOR_UNSPEC:
+               {
+                       port = PORT_TP;
+                       break;
+               }
+               fallthrough;
+       case SFF8024_QSFP_DD_CONNECTOR_SG: /* guess */
+       case SFF8024_QSFP_DD_CONNECTOR_HSSDC_II:
+       case SFF8024_QSFP_DD_CONNECTOR_NOSEPARATE:
+       case SFF8024_QSFP_DD_CONNECTOR_MXC_2X16:
+               port = PORT_OTHER;
+               break;
+       default:
+               dev_warn(bus->qsfp_dev, "qsfp: unknown connector id 0x%02x\n",
+                        id->base.etile_qsfp_connector_type);
+               port = PORT_OTHER;
+               break;
+       }
+
+       if (support) {
+               switch (port) {
+               case PORT_FIBRE:
+                       phylink_set(support, FIBRE);
+                       break;
+
+               case PORT_TP:
+                       phylink_set(support, TP);
+                       break;
+               }
+       }
+
+       return port;
+}
+EXPORT_SYMBOL_GPL(qsfp_parse_port);
+
+/**
+ * qsfp_may_have_phy() - indicate whether the module may have a PHY
+ * @bus: a pointer to the &struct qsfp_bus structure for the qsfp module
+ * @id: a pointer to the module's &struct qsfp_eeprom_id
+ *
+ * Parse the EEPROM identification given in @id, and return whether
+ * this module may have a PHY.
+ */
+
+bool qsfp_may_have_phy(struct qsfp_bus *bus, const struct qsfp_eeprom_id *id)
+{
+       if (id->base.etile_qsfp_identifier != SFF8024_ID_QSFP_DD_INF_8628) {
+               switch (id->base.etile_qsfp_spec_compliance_1[0]) {
+               case SFF8636_QSFP_DD_ECC_100GBASE_CR4:
+               case SFF8636_QSFP_DD_ECC_CAUI4:
+
+                       return true;
+               }
+       }
+
+       return false;
+}
+EXPORT_SYMBOL_GPL(qsfp_may_have_phy);
+
+/**
+ * qsfp_parse_support() - Parse the eeprom id for supported link modes
+ * @bus: a pointer to the &struct qsfp_bus structure for the qsfp module
+ * @id: a pointer to the module's &struct qsfp_eeprom_id
+ * @support: pointer to an array of unsigned long for the ethtool support mask
+ *
+ * Parse the EEPROM identification information and derive the supported
+ * ethtool link modes for the module.
+ */
+
+void qsfp_parse_support(struct qsfp_bus *bus, const struct qsfp_eeprom_id *id,
+                       unsigned long *support)
+{
+       unsigned int etile_qsfp_br_nom, etile_qsfp_br_max, etile_qsfp_br_min;
+       __ETHTOOL_DECLARE_LINK_MODE_MASK(modes) = {
+               0,
+       };
+
+       /* Decode the bitrate information to MBd */
+       etile_qsfp_br_min = 0;
+       etile_qsfp_br_nom = 0;
+       etile_qsfp_br_max = 0;
+       if (id->base.etile_qsfp_br_nom) {
+               if (id->base.etile_qsfp_br_nom != 255) {
+                       etile_qsfp_br_nom = id->base.etile_qsfp_br_nom * 100;
+                       etile_qsfp_br_min = etile_qsfp_br_nom -
+                                           id->base.etile_qsfp_br_nom *
+                                                   id->base.etile_qsfp_br_min;
+                       etile_qsfp_br_max = etile_qsfp_br_nom +
+                                           id->base.etile_qsfp_br_nom *
+                                                   id->base.etile_qsfp_br_max;
+               } else if (id->base.etile_qsfp_br_max) {
+                       etile_qsfp_br_nom = 250 * id->base.etile_qsfp_br_max;
+                       etile_qsfp_br_max = etile_qsfp_br_nom +
+                                           etile_qsfp_br_nom *
+                                                   id->base.etile_qsfp_br_min /
+                                                   100;
+                       etile_qsfp_br_min = etile_qsfp_br_nom -
+                                           etile_qsfp_br_nom *
+                                                   id->base.etile_qsfp_br_min /
+                                                   100;
+               }
+
+               /* When using passive cables, in case neither BR,min nor BR,max
+                * are specified, set etile_qsfp_br_min to 0 as the nominal 
value is then
+                * used as the maximum.
+                */
+       }
+
+       /* Set ethtool support from the compliance fields. */
+       if (id->base.etile_qsfp_spec_compliance_1)
+               phylink_set(modes, 10000baseSR_Full);
+       if (id->base.etile_qsfp_spec_compliance_1)
+               phylink_set(modes, 10000baseLR_Full);
+
+       switch (id->base.etile_qsfp_spec_compliance_1[0]) {
+       case SFF8636_QSFP_DD_ECC_100GBASE_CR4:
+               break;
+       case SFF8636_QSFP_DD_ECC_CAUI4:
+               phylink_set(modes, 100000baseSR4_Full);
+               phylink_set(modes, 25000baseSR_Full);
+               break;
+
+       default:
+               dev_warn(bus->qsfp_dev,
+                        "Unknown/unsupported extended compliance code: 
0x%02x\n",
+                        id->base.etile_qsfp_spec_compliance_1[0]);
+               break;
+       }
+
+       /* If we haven't discovered any modes that this module supports, try
+        * the bitrate to determine supported modes. Some BiDi modules (eg,
+        * 1310nm/1550nm) are not 1000BASE-BX compliant due to the differing
+        * wavelengths, so do not set any transceiver bits.
+        */
+       if (bitmap_empty(modes, __ETHTOOL_LINK_MODE_MASK_NBITS)) {
+               /* If the bit rate allows 1000baseX */
+               if (etile_qsfp_br_nom && etile_qsfp_br_min <= 1300 &&
+                   etile_qsfp_br_max >= 1200)
+                       phylink_set(modes, 1000baseX_Full);
+       }
+
+       if (bus->qsfp_quirk)
+               bus->qsfp_quirk->modes(id, modes);
+
+       bitmap_or(support, support, modes, __ETHTOOL_LINK_MODE_MASK_NBITS);
+
+       phylink_set(support, Autoneg);
+       phylink_set(support, Pause);
+       phylink_set(support, Asym_Pause);
+}
+EXPORT_SYMBOL_GPL(qsfp_parse_support);
+
+/**
+ * qsfp_select_interface() - Select appropriate phy_interface_t mode
+ * @bus: a pointer to the &struct qsfp_bus structure for the qsfp module
+ * @link_modes: ethtool link modes mask
+ *
+ * Derive the phy_interface_t mode for the qsfp module from the link
+ * modes mask.
+ */
+phy_interface_t qsfp_select_interface(struct qsfp_bus *bus,
+                                     unsigned long *link_modes)
+{
+       if (phylink_test(link_modes, 10000baseCR_Full) ||
+           phylink_test(link_modes, 10000baseSR_Full) ||
+           phylink_test(link_modes, 10000baseLR_Full) ||
+           phylink_test(link_modes, 10000baseLRM_Full) ||
+           phylink_test(link_modes, 10000baseER_Full) ||
+           phylink_test(link_modes, 10000baseT_Full))
+               return PHY_INTERFACE_MODE_10GBASER;
+
+       if (phylink_test(link_modes, 2500baseX_Full))
+               return PHY_INTERFACE_MODE_2500BASEX;
+
+       if (phylink_test(link_modes, 1000baseT_Half) ||
+           phylink_test(link_modes, 1000baseT_Full))
+               return PHY_INTERFACE_MODE_SGMII;
+
+       if (phylink_test(link_modes, 1000baseX_Full))
+               return PHY_INTERFACE_MODE_1000BASEX;
+
+       dev_warn(bus->qsfp_dev, "Unable to ascertain link mode\n");
+
+       return PHY_INTERFACE_MODE_NA;
+}
+EXPORT_SYMBOL_GPL(qsfp_select_interface);
+
+static LIST_HEAD(qsfp_buses);
+static DEFINE_MUTEX(qsfp_mutex);
+
+static const struct qsfp_upstream_ops *
+qsfp_get_upstream_ops(struct qsfp_bus *bus)
+{
+       return bus->registered ? bus->upstream_ops : NULL;
+}
+
+static struct qsfp_bus *qsfp_bus_get(struct fwnode_handle *fwnode)
+{
+       struct qsfp_bus *qsfp, *new, *found = NULL;
+
+       new = kzalloc(sizeof(*new), GFP_KERNEL);
+
+       mutex_lock(&qsfp_mutex);
+
+       list_for_each_entry(qsfp, &qsfp_buses, node) {
+               if (qsfp->fwnode == fwnode) {
+                       kref_get(&qsfp->kref);
+                       found = qsfp;
+                       break;
+               }
+       }
+
+       if (!found && new) {
+               kref_init(&new->kref);
+               new->fwnode = fwnode;
+               list_add(&new->node, &qsfp_buses);
+               found = new;
+               new = NULL;
+       }
+
+       mutex_unlock(&qsfp_mutex);
+
+       kfree(new);
+
+       return found;
+}
+
+static void qsfp_bus_release(struct kref *kref)
+{
+       struct qsfp_bus *bus = container_of(kref, struct qsfp_bus, kref);
+
+       list_del(&bus->node);
+       mutex_unlock(&qsfp_mutex);
+       kfree(bus);
+}
+
+/**
+ * qsfp_bus_put() - put a reference on the &struct qsfp_bus
+ * @bus: the &struct qsfp_bus found via qsfp_bus_find_fwnode()
+ *
+ * Put a reference on the &struct qsfp_bus and free the underlying structure
+ * if this was the last reference.
+ */
+void qsfp_bus_put(struct qsfp_bus *bus)
+{
+       if (bus)
+               kref_put_mutex(&bus->kref, qsfp_bus_release, &qsfp_mutex);
+}
+EXPORT_SYMBOL_GPL(qsfp_bus_put);
+
+static int qsfp_register_bus(struct qsfp_bus *bus)
+{
+       const struct qsfp_upstream_ops *ops = bus->upstream_ops;
+       int ret;
+
+       pr_info("qsfp register bus\n");
+
+       if (ops) {
+               if (ops->link_down)
+                       ops->link_down(bus->upstream);
+               if (ops->connect_phy /*&& bus->phydev*/) {
+                       ret = ops->connect_phy(bus->upstream, bus->phydev);
+                       if (ret)
+                               return ret;
+               }
+       }
+       bus->registered = true;
+
+       bus->socket_ops->attach(bus->qsfp);
+       if (bus->started)
+               bus->socket_ops->start(bus->qsfp);
+       bus->upstream_ops->attach(bus->upstream, bus);
+       return 0;
+}
+
+static void qsfp_unregister_bus(struct qsfp_bus *bus)
+{
+       const struct qsfp_upstream_ops *ops = bus->upstream_ops;
+
+       if (bus->registered) {
+               bus->upstream_ops->detach(bus->upstream, bus);
+               if (bus->started)
+                       bus->socket_ops->stop(bus->qsfp);
+               bus->socket_ops->detach(bus->qsfp);
+               if (bus->phydev && ops && ops->disconnect_phy)
+                       ops->disconnect_phy(bus->upstream);
+       }
+       bus->registered = false;
+}
+
+/**
+ * qsfp_get_module_info() - Get the ethtool_modinfo for a qsfp module
+ * @bus: a pointer to the &struct qsfp_bus structure for the qsfp module
+ * @modinfo: a &struct ethtool_modinfo
+ *
+ * Fill in the type and eeprom_len parameters in @modinfo for a module on
+ * the qsfp bus specified by @bus.
+ *
+ * Returns 0 on success or a negative errno number.
+ */
+int qsfp_get_module_info(struct qsfp_bus *bus, struct ethtool_modinfo *modinfo)
+{
+       return bus->socket_ops->module_info(bus->qsfp, modinfo);
+}
+EXPORT_SYMBOL_GPL(qsfp_get_module_info);
+
+/**
+ * qsfp_get_module_eeprom() - Read the qsfp module EEPROM
+ * @bus: a pointer to the &struct qsfp_bus structure for the qsfp module
+ * @ee: a &struct ethtool_eeprom
+ * @data: buffer to contain the EEPROM data (must be at least @ee->len bytes)
+ *
+ * Read the EEPROM as specified by the supplied @ee. See the documentation
+ * for &struct ethtool_eeprom for the region to be read.
+ *
+ * Returns 0 on success or a negative errno number.
+ */
+int qsfp_get_module_eeprom(struct qsfp_bus *bus, struct ethtool_eeprom *ee,
+                          u8 *data)
+{
+       return bus->socket_ops->module_eeprom(bus->qsfp, ee, data);
+}
+EXPORT_SYMBOL_GPL(qsfp_get_module_eeprom);
+
+/**
+ * qsfp_upstream_start() - Inform the qsfp that the network device is up
+ * @bus: a pointer to the &struct qsfp_bus structure for the qsfp module
+ *
+ * Inform the qsfp socket that the network device is now up, so that the
+ * module can be enabled by allowing TX_DISABLE to be deasserted. This
+ * should be called from the network device driver's &struct net_device_ops
+ * ndo_open() method.
+ */
+void qsfp_upstream_start(struct qsfp_bus *bus)
+{
+       if (bus->registered)
+               bus->socket_ops->start(bus->qsfp);
+       bus->started = true;
+}
+EXPORT_SYMBOL_GPL(qsfp_upstream_start);
+
+/**
+ * qsfp_upstream_stop() - Inform the qsfp that the network device is down
+ * @bus: a pointer to the &struct qsfp_bus structure for the qsfp module
+ *
+ * Inform the qsfp socket that the network device is now up, so that the
+ * module can be disabled by asserting TX_DISABLE, disabling the laser
+ * in optical modules. This should be called from the network device
+ * driver's &struct net_device_ops ndo_stop() method.
+ */
+void qsfp_upstream_stop(struct qsfp_bus *bus)
+{
+       if (bus->registered)
+               bus->socket_ops->stop(bus->qsfp);
+       bus->started = false;
+}
+EXPORT_SYMBOL_GPL(qsfp_upstream_stop);
+
+static void qsfp_upstream_clear(struct qsfp_bus *bus)
+{
+       bus->upstream_ops = NULL;
+       bus->upstream = NULL;
+}
+
+/**
+ * qsfp_bus_find_fwnode() - parse and locate the qsfp bus from fwnode
+ * @fwnode: firmware node for the parent device (MAC or PHY)
+ *
+ * Parse the parent device's firmware node for a qsfp bus, and locate
+ * the qsfp_bus structure, incrementing its reference count.  This must
+ * be put via qsfp_bus_put() when done.
+ *
+ * Returns:
+ *     - on success, a pointer to the qsfp_bus structure,
+ *     - %NULL if no qsfp is specified,
+ *     - on failure, an error pointer value:
+ *     - corresponding to the errors detailed for
+ *     fwnode_property_get_reference_args().
+ *     - %-ENOMEM if we failed to allocate the bus.
+ *     - an error from the upstream's connect_phy() method.
+ */
+struct qsfp_bus *qsfp_bus_find_fwnode(struct fwnode_handle *fwnode)
+{
+       struct fwnode_reference_args ref;
+       struct qsfp_bus *bus;
+       int ret;
+
+       ret = fwnode_property_get_reference_args(fwnode, "qsfp", NULL, 0, 0,
+                                                &ref);
+       if (ret == -ENOENT)
+               return NULL;
+       else if (ret < 0)
+               return ERR_PTR(ret);
+
+       bus = qsfp_bus_get(ref.fwnode);
+       fwnode_handle_put(ref.fwnode);
+       if (!bus)
+               return ERR_PTR(-ENOMEM);
+
+       return bus;
+}
+EXPORT_SYMBOL_GPL(qsfp_bus_find_fwnode);
+
+/**
+ * qsfp_bus_add_upstream() - parse and register the neighbouring device
+ * @bus: the &struct qsfp_bus found via qsfp_bus_find_fwnode()
+ * @upstream: the upstream private data
+ * @ops: the upstream's &struct qsfp_upstream_ops
+ *
+ * Add upstream driver for the qsfp bus, and if the bus is complete, register
+ * the qsfp bus using qsfp_register_upstream().  This takes a reference on the
+ * bus, so it is safe to put the bus after this call.
+ *
+ * Returns:
+ *     - on success, a pointer to the qsfp_bus structure,
+ *     - %NULL if no qsfp is specified,
+ *     - on failure, an error pointer value:
+ *     - corresponding to the errors detailed for
+ *     fwnode_property_get_reference_args().
+ *     - %-ENOMEM if we failed to allocate the bus.
+ *     - an error from the upstream's connect_phy() method.
+ */
+int qsfp_bus_add_upstream(struct qsfp_bus *bus, void *upstream,
+                         const struct qsfp_upstream_ops *ops)
+{
+       int ret;
+
+       /* If no bus, return success */
+       if (!bus)
+               return 0;
+
+       rtnl_lock();
+       kref_get(&bus->kref);
+       bus->upstream_ops = ops;
+       bus->upstream = upstream;
+
+       if (bus->qsfp) {
+               ret = qsfp_register_bus(bus);
+               if (ret)
+                       qsfp_upstream_clear(bus);
+       } else {
+               ret = 0;
+       }
+       rtnl_unlock();
+
+       if (ret)
+               qsfp_bus_put(bus);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(qsfp_bus_add_upstream);
+
+/**
+ * qsfp_bus_del_upstream() - Delete a qsfp bus
+ * @bus: a pointer to the &struct qsfp_bus structure for the qsfp module
+ *
+ * Delete a previously registered upstream connection for the qsfp
+ * module. @bus should have been added by qsfp_bus_add_upstream().
+ */
+void qsfp_bus_del_upstream(struct qsfp_bus *bus)
+{
+       if (bus) {
+               rtnl_lock();
+               if (bus->qsfp)
+                       qsfp_unregister_bus(bus);
+               qsfp_upstream_clear(bus);
+               rtnl_unlock();
+
+               qsfp_bus_put(bus);
+       }
+}
+EXPORT_SYMBOL_GPL(qsfp_bus_del_upstream);
+
+/* Socket driver entry points */
+int qsfp_add_phy(struct qsfp_bus *bus, struct phy_device *phydev)
+{
+       const struct qsfp_upstream_ops *ops = qsfp_get_upstream_ops(bus);
+       int ret = 0;
+
+       if (ops && ops->connect_phy)
+               ret = ops->connect_phy(bus->upstream, phydev);
+
+       if (ret == 0)
+               bus->phydev = phydev;
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(qsfp_add_phy);
+
+void qsfp_remove_phy(struct qsfp_bus *bus)
+{
+       const struct qsfp_upstream_ops *ops = qsfp_get_upstream_ops(bus);
+
+       if (ops && ops->disconnect_phy)
+               ops->disconnect_phy(bus->upstream);
+       bus->phydev = NULL;
+}
+EXPORT_SYMBOL_GPL(qsfp_remove_phy);
+
+void qsfp_link_up(struct qsfp_bus *bus)
+{
+       const struct qsfp_upstream_ops *ops = qsfp_get_upstream_ops(bus);
+
+       if (ops && ops->link_up)
+               ops->link_up(bus->upstream);
+}
+EXPORT_SYMBOL_GPL(qsfp_link_up);
+
+void qsfp_link_down(struct qsfp_bus *bus)
+{
+       const struct qsfp_upstream_ops *ops = qsfp_get_upstream_ops(bus);
+
+       if (ops && ops->link_down)
+               ops->link_down(bus->upstream);
+}
+EXPORT_SYMBOL_GPL(qsfp_link_down);
+
+int qsfp_module_insert(struct qsfp_bus *bus, const struct qsfp_eeprom_id *id)
+{
+       const struct qsfp_upstream_ops *ops = qsfp_get_upstream_ops(bus);
+       int ret = 0;
+
+       pr_info("Tessolve module insert\n");
+
+       bus->qsfp_quirk = qsfp_lookup_quirk(id);
+
+       if (ops && ops->module_insert)
+               ret = ops->module_insert(bus->upstream, id);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(qsfp_module_insert);
+
+void qsfp_module_remove(struct qsfp_bus *bus)
+{
+       const struct qsfp_upstream_ops *ops = qsfp_get_upstream_ops(bus);
+
+       if (ops && ops->module_remove)
+               ops->module_remove(bus->upstream);
+
+       bus->qsfp_quirk = NULL;
+}
+EXPORT_SYMBOL_GPL(qsfp_module_remove);
+
+int qsfp_module_start(struct qsfp_bus *bus)
+{
+       const struct qsfp_upstream_ops *ops = qsfp_get_upstream_ops(bus);
+       int ret = 0;
+
+       if (ops && ops->module_start)
+               ret = ops->module_start(bus->upstream);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(qsfp_module_start);
+
+void qsfp_module_stop(struct qsfp_bus *bus)
+{
+       const struct qsfp_upstream_ops *ops = qsfp_get_upstream_ops(bus);
+
+       if (ops && ops->module_stop)
+               ops->module_stop(bus->upstream);
+}
+EXPORT_SYMBOL_GPL(qsfp_module_stop);
+
+static void qsfp_socket_clear(struct qsfp_bus *bus)
+{
+       bus->qsfp_dev = NULL;
+       bus->qsfp = NULL;
+       bus->socket_ops = NULL;
+}
+
+struct qsfp_bus *qsfp_register_socket(struct device *dev, struct qsfp *qsfp,
+                                     const struct qsfp_socket_ops *ops)
+{
+       struct qsfp_bus *bus = qsfp_bus_get(dev->fwnode);
+       int ret = 0;
+
+       if (bus) {
+               rtnl_lock();
+               bus->qsfp_dev = dev;
+               bus->qsfp = qsfp;
+               bus->socket_ops = ops;
+
+               if (bus->upstream_ops) {
+                       ret = qsfp_register_bus(bus);
+                       if (ret)
+                               qsfp_socket_clear(bus);
+               }
+               rtnl_unlock();
+       }
+
+       if (ret) {
+               qsfp_bus_put(bus);
+               bus = NULL;
+       }
+
+       return bus;
+}
+EXPORT_SYMBOL_GPL(qsfp_register_socket);
+
+void qsfp_unregister_socket(struct qsfp_bus *bus)
+{
+       rtnl_lock();
+       if (bus->upstream_ops)
+               qsfp_unregister_bus(bus);
+       qsfp_socket_clear(bus);
+       rtnl_unlock();
+
+       qsfp_bus_put(bus);
+}
+EXPORT_SYMBOL_GPL(qsfp_unregister_socket);
diff --git a/include/linux/qsfp.h b/include/linux/qsfp.h
new file mode 100644
index 000000000000..72a819eed32d
--- /dev/null
+++ b/include/linux/qsfp.h
@@ -0,0 +1,400 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Intel QSFP_MODULE_DRIVER
+ * Copyright (C) 2020-2021 Intel Corporation. All rights reserved.
+ *
+ * Contributors:
+ * Malku
+ * Deepak
+ * Original driver contributed by Intel.
+ */
+#ifndef LINUX_QSFP_H
+#define LINUX_QSFP_H
+
+#include <linux/phy.h>
+#include <linux/ethtool.h>
+
+struct qsfp;
+struct qsfp_eeprom_base {
+       u8 etile_qsfp_identifier;                                       //0x00 
00
+       u8 etile_qsfp_revision;                                         //0x01 
01
+       u8 etile_qsfp_status;                                           //0x02 
02
+       u8 etile_qsfp_interrupt_flags[19];                      //0x03 03
+       u8 etile_qsfp_device_monitors[12];                      //0x16 22
+       u8 etile_qsfp_channel_monitors[48];                     //0x22 34
+       u8 RESERVED_0[4];                                                       
//0x52 82
+       u8 etile_qsfp_control[13];                                      //0x56 
86
+       u8 RESERVED_1;                                                          
//0x63 99
+       u8 etile_qsfp_device_channel_masks[5];          //0x64 100
+       u8 etile_qsfp_vendor_specific[2];                       //0x69 105
+       u8 RESERVED_2;                                                          
//0x6b 107
+       u8 etile_device_properties_1[3];                        //0x6c 108
+       u8 etile_pci_express[2];                                        //0x6f 
111
+       u8 etile_device_properties_2[2];                        //0x71 113
+       u8 RESERVED_3[4];                                                       
//0x74 115
+       u8 etile_qsfp_password_change[4];                       //0x77 119
+       u8 etile_qsfp_password_entry_area[4];           //0x7b 123
+       u8 etile_qsfp_page_select_byte;                         //0x7f 127
+       u8 etile_qsfp_identifier_1;                                     //0x80 
128
+       u8 etile_qsfp_ext_identifier;                           //0x81 129
+       u8 etile_qsfp_connector_type;                           //0x82 130
+       u8 etile_qsfp_spec_compliance_1[8];                     //0x83 131
+       u8 etile_qsfp_encoding;                                         //0x8b 
139
+       u8 etile_qsfp_br_nom;                                           //0x8c 
140
+       u8 etile_qsfp_ext_compliance;                           //0x8d 141
+       u8 etile_qsfp_link_lenghth_1;                           //0x8e 142
+       u8 etile_qsfp_link_lenghth_2;                           //0x8f 143
+       u8 etile_qsfp_link_lenghth_3;                           //0x90 144
+       u8 etile_qsfp_link_lenghth_4;                           //0x91 145
+       u8 etile_qsfp_link_lenghth_5;                           //0x92 146
+       u8 etile_qsfp_device_technology;                        //0x93 147
+       char etile_qsfp_vendor_name[16];                        //0x94 148
+       u8 etile_qsfp_extended_module;                          //0xa4 164
+       char etile_qsfp_vendor_oui[3];                          //0xa5 165
+       char etile_qsfp_vendor_pn[16];                          //0xa8 168
+       char etile_qsfp_vendor_rev[2];                          //0xb8 184
+       u8 etile_qsfp_wavelength_copper[2];                     //0xba 186
+       u8 etile_qsfp_wavelength_tolerance[2];          //0xbc 188
+       u8 etile_qsfp_max_case_temp[1];                         //0xbe 190
+       u8 etile_qsfp_cc_base;                                          //0xbf 
191
+       u8 etile_qsfp_ext_spec_compliance;                      //0xc0 192
+       u8 etile_qsfp_options_1;                                        //0xc1 
193
+       u8 etile_qsfp_options_2;                                        //0xc2 
194
+       u8 etile_qsfp_options_3;                                        //0xc3 
195
+       char etile_qsfp_vendor_serial_number[16];       //0xc4 196
+       char etile_qsfp_vendor_date_code[8];            //0xd4 212
+       u8 etile_qsfp_diag_monitor;                                     //0xdc 
220
+       u8 etile_qsfp_enhanced_options;                         //0xdd 221
+       u8 etile_qsfp_br_nom_1;                                         //0xde 
222
+       u8 etile_qsfp_cc_ext;                                           //0xdf 
223
+       u8 etile_qsfp_venodor_specific_id[32];          //0xe0 224
+       u8 etile_qsfp_br_max;                                           //TBD
+       u8 etile_qsfp_br_min;                                           //TBD
+
+} __packed;
+
+
+       /**
+        * struct qsfp_eeprom_id - raw qsfp module identification information
+        * @base: base qsfp module identification structure
+        * @ext: extended qsfp module identification structure
+        *
+        * See the SFF-8472 specification and related documents for the 
definition
+        * of these structure members. This can be obtained from
+        * https://www.snia.org/technology-communities/sff/specifications
+        */
+struct qsfp_eeprom_id {
+       struct qsfp_eeprom_base base;
+
+} __packed;
+
+
+
+enum {
+
+       SFF8024_ID_QSFP_DD_INF_8628     = 0x18,
+       SFF8024_QSFP_DD_ENCODING_UNSPEC = 0x00,
+       SFF8024_QSFP_DD_ENCODING_8B10B  = 0x01,
+       SFF8024_QSFP_DD_ENCODING_4B5B   = 0x02,
+       SFF8024_QSFP_DD_ENCODING_NRZ    = 0x03,
+       SFF8024_QSFP_DD_ENCODING_8436_SONET     = 0x04,
+       SFF8024_QSFP_DD_ENCODING_8436_64B66B    = 0x05,
+       SFF8024_QSFP_DD_ENCODING_8436_MANCHESTER        = 0x06,
+       SFF8024_QSFP_DD_ENCODING_256B257B       = 0x07,
+       SFF8024_QSFP_DD_ENCODING_PAM4   = 0x08,
+       SFF8024_ID_QSFP_28      = 0x11,
+
+       SFF8024_QSFP_DD_CONNECTOR_UNSPEC = 0x00,
+       SFF8024_QSFP_DD_CONNECTOR_SC = 0x01,
+       SFF8024_QSFP_DD_CONNECTOR_FIBRE_CHANNEL_STYLE1 = 0x02,
+       SFF8024_QSFP_DD_CONNECTOR_FIBRE_CHANNEL_STYLE2 = 0x03,
+       SFF8024_QSFP_DD_CONNECTOR_BNC_TNC = 0x04,
+       SFF8024_QSFP_DD_CONNECTOR_FIBRE_CHANNEL_COAX_HEADERS = 0x05,
+       SFF8024_QSFP_DD_CONNECTOR_FIBERJACK = 0x06,
+       SFF8024_QSFP_DD_CONNECTOR_LC = 0x07,
+       SFF8024_QSFP_DD_CONNECTOR_MT_RJ = 0x08,
+       SFF8024_QSFP_DD_CONNECTOR_MU = 0x09,
+       SFF8024_QSFP_DD_CONNECTOR_SG = 0x0a,
+       SFF8024_QSFP_DD_CONNECTOR_OPTICAL_PIGTAIL = 0x0b,
+       SFF8024_QSFP_DD_CONNECTOR_MPO_1X12 = 0x0c,
+       SFF8024_QSFP_DD_CONNECTOR_MPO_2X16 = 0x0d,
+       SFF8024_QSFP_DD_CONNECTOR_HSSDC_II = 0x20,
+       SFF8024_QSFP_DD_CONNECTOR_COPPER_PIGTAIL = 0x21,
+       SFF8024_QSFP_DD_CONNECTOR_RJ45 = 0x22,
+       SFF8024_QSFP_DD_CONNECTOR_NOSEPARATE = 0x23,
+       SFF8024_QSFP_DD_CONNECTOR_MXC_2X16 = 0x24,
+       SFF8024_QSFP_DD_CONNECTOR_CS_OPTICAL_CONNECTOR = 0x25,
+       SFF8024_QSFP_DD_CONNECTOR_SN_OPICAL_CONNECTOR = 0x26,
+       SFF8024_QSFP_DD_CONNECTOR_MPO_2X12 = 0x27,
+       SFF8024_QSFP_DD_CONNECTOR_MXC_1X16 = 0x28,
+
+       SFF8636_QSFP_DD_ECC_100GBASE_CR4 = 1,
+       SFF8636_QSFP_DD_ECC_CAUI4 = 0,
+
+       SFF8636_QSFP_DD_ECC_LAUI2_C2M = 3,
+       SFF8636_QSFP_DD_ECC_50GAUI2_C2M = 2,
+       SFF8636_QSFP_DD_ECC_50GAUI1_C2M = 1,
+       SFF8636_QSFP_DD_ECC_CDAUI8_C2M = 0,
+
+};
+
+/* qsfp EEPROM registers */
+enum {
+       QSFP_PHYS_ID    = 0x00,
+       QSFP_STATUS     = 0x01,
+       QSFP_STATUS_1   = 0x02,
+       QSFP_RX_TX_LOSS = 0x03,
+       QSFP_TX_FAULT   = 0x04,
+       QSFP_DEVICE_MONITORS    = 0x16,
+       QSFP_CHANNEL_MONITORS   = 0x22,
+       QSFP_CONTROL    = 0x56,
+       QSFP_DEVICE_CHANNEL_MASKS       = 0x64,
+       QSFP_VENDOR_SPECIFIC    = 0x69,
+       QSFP_DEVICE_PROPERTIES  = 0x6C,
+       QSFP_PCI_EXPRESS        = 0x6F,
+       QSFP_DEVICE_PROPERTIES_1        = 0x71,
+       QSFP_PASSWORD_CHANGE_AREA       = 0x77,
+       QSFP_PASSWORD_ENTRY_AREA        = 0x7B,
+       QSFP_PAGE_SELECT_BYTE   = 0x7F,
+       QSFP_IDENTIFIER = 0x80,
+       QSFP_EXT_IDENTIFIER     = 0x81,
+       QSFP_CONNECTOR  = 0x82,
+       QSFP_COMPLIANCE = 0x83,
+       QSFP_ENCODING   = 0x8b,
+       QSFP_BR_NOMINAL = 0x8C,
+       QSFP_EXT_RATE_SELECT    = 0x8d,
+       QSFP_LINK_LEN_SMF       = 0x8e,
+       QSFP_LINK_LEN_OM3_50    = 0x8f,
+       QSFP_LINK_LEN_OM2_50    = 0x90,
+       QSFP_LINK_LEN_OM2_62_5  = 0x91,
+       QSFP_LINK_LEN_COPPER_1M = 0x92,
+       QSFP_DEVICE_TECHNOLOGY  = 0x93,
+       QSFP_VENDOR_NAME        = 0x94,
+       QSFP_VENDOR_OUI = 0xa5,
+       QSFP_VENDOR_PN  = 0xa8,
+       QSFP_VENDOR_REV = 0xb8,
+       QSFP_MAX_CASE_TEMP      = 0xbe,
+       QSFP_CC_BASE    = 0xbf,
+       QSFP_OPTIONS    = 0xC3,
+       QSFP_VENDOR_SN  = 0xC4,
+       QSFP_DATECODE   = 0xD4,
+       QSFP_DIAGMON    = 0xDc,
+       QSFP_ENHOPTS    = 0xDD,
+       QSFP_CC_EXT     = 0xDf,
+
+                       QSFP_TX4_LOS_CHANNEL_4 = BIT(7),
+                       QSFP_TX3_LOS_CHANNEL_3 = BIT(6),
+                       QSFP_TX2_LOS_CHANNEL_2 = BIT(5),
+                       QSFP_TX1_LOS_CHANNEL_1 = BIT(4),
+                       QSFP_RX4_LOS_CHANNEL_4 = BIT(3),
+                       QSFP_RX3_LOS_CHANNEL_3 = BIT(2),
+                       QSFP_RX2_LOS_CHANNEL_2 = BIT(1),
+                       QSFP_RX1_LOS_CHANNEL_1 = BIT(0),
+
+       QSFP_OPTIONS_TX_INPUT_ADAPTIVE  = BIT(20),
+       QSFP_OPTIONS_TX_INPUT_EQUALIZATION      = BIT(19),
+       QSFP_OPTIONS_TX_INPUT_EQUALIZATION_FIXED        = BIT(18),
+       QSFP_OPTIONS_RX_OUTPUT_EMPHASIS = BIT(17),
+       QSFP_OPTIONS_RX_OUTPUT_APLITUDE = BIT(16),
+       QSFP_OPTIONS_TX_CDR_CONTROL     = BIT(15),
+       QSFP_OPTIONS_RX_CDR_CONTROL     = BIT(14),
+       QSFP_OPTIONS_TX_CDR_LOSS        = BIT(13),
+       QSFP_OPTIONS_RX_CDR_LOSS        = BIT(12),
+       QSFP_OPTIONS_RX_SQUELCH_DISABLE = BIT(11),
+       QSFP_OPTIONS_RX_OUTPUT_DISBALE  = BIT(10),
+       QSFP_OPTIONS_TX_SQUELCH_DISABLE = BIT(9),
+       QSFP_OPTIONS_TX_OUTPUT_DISBALE  = BIT(8),
+       QSFP_OPTIONS_MEMORY_PAGE_2      = BIT(7),
+       QSFP_OPTIONS_MEMORY_PAGE_1      = BIT(6),
+       QSFP_OPTIONS_RATE_SELECT        = BIT(5),
+       QSFP_OPTIONS_TX_DISABLE = BIT(4),
+       QSFP_OPTIONS_TX_FAULT   = BIT(3),
+       QSFP_OPTIONS_TX_SQUELCH_IMPLEMENTED     = BIT(2),
+       QSFP_OPTIONS_TX_LOSS_SIGNAL     = BIT(1),
+       QSFP_OPTIONS_PAGES      = BIT(0),
+
+
+
+       QSFP_DIAGMON_DDM        = BIT(6),
+       QSFP_DIAGMON_INT_CAL    = BIT(5),
+       QSFP_DIAGMON_EXT_CAL    = BIT(4),
+       QSFP_DIAGMON_RXPWR_AVG  = BIT(3),
+       QSFP_DIAGMON_ADDRMODE   = BIT(2),
+
+       QSFP_DIAGMON_RX_OPTICAL_POWER_MONITOR   = BIT(4),
+       QSFP_DIAGMON_RX_OPTICAL_POWER_MEASUREMENT_TYPE  = BIT(3),
+       QSFP_DIAGMON_TX_OPTICAL_POWER_MONITOR   = BIT(2),
+       QSFP_DIAGMON_TX_BIAS_MONITOR_IMPLEMENTED        = BIT(1),
+       QSFP_DIAGMON_RESERVED   = BIT(0),
+
+       QSFP_ENHOPTS_USER_DEFINED       = BIT(7),
+       QSFP_ENHOPTS_VENDOR_SPECIFIC    = BIT(6),
+       QSFP_ENHOPTS_INTERNAL_3_VOLTS   = BIT(5),
+       QSFP_ENHOPTS_POWER_CHANGE_COMPLETE      = BIT(4),
+       QSFP_ENHOPTS_RX_RATE_SELECT     = BIT(3),
+       QSFP_ENHOPTS_APPLICATION_SELECTED       = BIT(2),
+
+       QSFP_SFF8472_COMPLIANCE_NONE = 0x00,
+       QSFP_SFF8472_COMPLIANCE_REV9_3 = 0x01,
+       QSFP_SFF8472_COMPLIANCE_REV9_5 = 0x02,
+       QSFP_SFF8472_COMPLIANCE_REV10_2 = 0x03,
+       QSFP_SFF8472_COMPLIANCE_REV10_4 = 0x04,
+       QSFP_SFF8472_COMPLIANCE_REV11_0 = 0x05,
+       QSFP_SFF8472_COMPLIANCE_REV11_3 = 0x06,
+       QSFP_SFF8472_COMPLIANCE_REV11_4 = 0x07,
+       QsSFP_SFF8472_COMPLIANCE_REV12_0 = 0x08,
+
+       QSFP_EXT_STATUS = 0x76,
+
+};
+
+struct fwnode_handle;
+struct ethtool_eeprom;
+struct ethtool_modinfo;
+struct qsfp_bus;
+
+       /**
+        * struct qsfp_upstream_ops - upstream operations structure
+        * @attach: called when the qsfp socket driver is bound to the upstream
+        *   (mandatory).
+        * @detach: called when the qsfp socket driver is unbound from the 
upstream
+        *   (mandatory).
+        * @module_insert: called after a module has been detected to determine
+        *   whether the module is supported for the upstream device.
+        * @module_remove: called after the module has been removed.
+        * @module_start: called after the PHY probe step
+        * @module_stop: called before the PHY is removed
+        * @link_down: called when the link is non-operational for whatever
+        *   reason.
+        * @link_up: called when the link is operational.
+        * @connect_phy: called when an I2C accessible PHY has been detected
+        *   on the module.
+        * @disconnect_phy: called when a module with an I2C accessible PHY has
+        *   been removed.
+        */
+struct qsfp_upstream_ops {
+       void (*attach)(void *priv, struct qsfp_bus *bus);
+       void (*detach)(void *priv, struct qsfp_bus *bus);
+       int (*module_insert)(void *priv, const struct qsfp_eeprom_id *id);
+       void (*module_remove)(void *priv);
+       int (*module_start)(void *priv);
+       void (*module_stop)(void *priv);
+       void (*link_down)(void *priv);
+       void (*link_up)(void *priv);
+       int (*connect_phy)(void *priv, struct phy_device *phydev);
+       void (*disconnect_phy)(void *priv);
+};
+
+struct qsfp_socket_ops {
+       void (*attach)(struct qsfp *qsfp);
+       void (*detach)(struct qsfp *qsfp);
+       void (*start)(struct qsfp *qsfp);
+       void (*stop)(struct qsfp *qsfp);
+       int (*module_info)(struct qsfp *qsfp, struct ethtool_modinfo *modinfo);
+       int (*module_eeprom)(struct qsfp *qsfp, struct ethtool_eeprom *ee,
+                            u8 *data);
+};
+
+int qsfp_add_phy(struct qsfp_bus *bus, struct phy_device *phydev);
+void qsfp_remove_phy(struct qsfp_bus *bus);
+void qsfp_link_up(struct qsfp_bus *bus);
+void qsfp_link_down(struct qsfp_bus *bus);
+int qsfp_module_insert(struct qsfp_bus *bus, const struct qsfp_eeprom_id *id);
+void qsfp_module_remove(struct qsfp_bus *bus);
+int qsfp_module_start(struct qsfp_bus *bus);
+void qsfp_module_stop(struct qsfp_bus *bus);
+int qsfp_link_configure(struct qsfp_bus *bus, const struct qsfp_eeprom_id *id);
+struct qsfp_bus *qsfp_register_socket(struct device *dev, struct qsfp *qsfp,
+                                     const struct qsfp_socket_ops *ops);
+void qsfp_unregister_socket(struct qsfp_bus *bus);
+
+int get_cable_attach(struct qsfp *old);
+int get_channel_info(struct qsfp *old);
+
+#if IS_ENABLED(CONFIG_QSFP)
+int qsfp_parse_port(struct qsfp_bus *bus, const struct qsfp_eeprom_id *id,
+                   unsigned long *support);
+bool qsfp_may_have_phy(struct qsfp_bus *bus, const struct qsfp_eeprom_id *id);
+void qsfp_parse_support(struct qsfp_bus *bus, const struct qsfp_eeprom_id *id,
+                       unsigned long *support);
+phy_interface_t qsfp_select_interface(struct qsfp_bus *bus,
+                                     unsigned long *link_modes);
+
+int qsfp_get_module_info(struct qsfp_bus *bus, struct ethtool_modinfo 
*modinfo);
+int qsfp_get_module_eeprom(struct qsfp_bus *bus, struct ethtool_eeprom *ee,
+                          u8 *data);
+void qsfp_upstream_start(struct qsfp_bus *bus);
+void qsfp_upstream_stop(struct qsfp_bus *bus);
+void qsfp_bus_put(struct qsfp_bus *bus);
+struct qsfp_bus *qsfp_bus_find_fwnode(struct fwnode_handle *fwnode);
+int qsfp_bus_add_upstream(struct qsfp_bus *bus, void *upstream,
+                         const struct qsfp_upstream_ops *ops);
+void qsfp_bus_del_upstream(struct qsfp_bus *bus);
+#else
+
+static inline int qsfp_parse_port(struct qsfp_bus *bus,
+                                 const struct qsfp_eeprom_id *id,
+                                 unsigned long *support)
+{
+       return PORT_OTHER;
+}
+
+static inline bool qsfp_may_have_phy(struct qsfp_bus *bus,
+                                    const struct qsfp_eeprom_id *id)
+{
+       return false;
+}
+
+static inline void qsfp_parse_support(struct qsfp_bus *bus,
+                                     const struct qsfp_eeprom_id *id,
+                                     unsigned long *support)
+{
+}
+
+static inline phy_interface_t qsfp_select_interface(struct qsfp_bus *bus,
+                                                   unsigned long *link_modes)
+{
+       return PHY_INTERFACE_MODE_NA;
+}
+
+static inline int qsfp_get_module_info(struct qsfp_bus *bus,
+                                      struct ethtool_modinfo *modinfo)
+{
+       return -EOPNOTSUPP;
+}
+
+static inline int qsfp_get_module_eeprom(struct qsfp_bus *bus,
+                                        struct ethtool_eeprom *ee, u8 *data)
+{
+       return -EOPNOTSUPP;
+}
+
+static inline void qsfp_upstream_start(struct qsfp_bus *bus)
+{
+}
+
+static inline void qsfp_upstream_stop(struct qsfp_bus *bus)
+{
+}
+
+static inline void qsfp_bus_put(struct qsfp_bus *bus)
+{
+}
+
+static inline struct qsfp_bus *
+qsfp_bus_find_fwnode(struct fwnode_handle *fwnode)
+{
+       return NULL;
+}
+
+static inline int qsfp_bus_add_upstream(struct qsfp_bus *bus, void *upstream,
+                                       const struct qsfp_upstream_ops *ops)
+{
+       return 0;
+}
+
+static inline void qsfp_bus_del_upstream(struct qsfp_bus *bus)
+{
+}
+
+#endif
+#endif /* LINUX_QSFP_H */
-- 
2.25.1

-=-=-=-=-=-=-=-=-=-=-=-
Links: You receive all messages sent to this group.
View/Reply Online (#12592): 
https://lists.yoctoproject.org/g/linux-yocto/message/12592
Mute This Topic: https://lists.yoctoproject.org/mt/98921515/21656
Group Owner: linux-yocto+ow...@lists.yoctoproject.org
Unsubscribe: https://lists.yoctoproject.org/g/linux-yocto/unsub 
[arch...@mail-archive.com]
-=-=-=-=-=-=-=-=-=-=-=-

Reply via email to