From 0abd7c201594540b1b01c70ae45a4223bf0ea3db Mon Sep 17 00:00:00 2001
From: Igor Pecovnik <igor.pecovnik@gmail.com>
Date: Sat, 8 Oct 2016 21:07:57 +0200
Subject: [PATCH] Marvell NEXT patch adjustments

---
 ...ell-88E1512-add-flow-control-support.patch |    4 +-
 ...nk-based-SFP-module-support - Kopija.patch | 1400 +++++++++++++++++
 ...clearfog-remove-duplicate-mdio-entry.patch |   43 -
 3 files changed, 1402 insertions(+), 45 deletions(-)
 create mode 100644 patch/kernel/marvell-next/0014-sfp-add-phylink-based-SFP-module-support - Kopija.patch
 delete mode 100644 patch/kernel/marvell-next/0052-ARM-dts-armada-388-clearfog-remove-duplicate-mdio-entry.patch

diff --git a/patch/kernel/marvell-next/0008-phy-marvell-88E1512-add-flow-control-support.patch b/patch/kernel/marvell-next/0008-phy-marvell-88E1512-add-flow-control-support.patch
index 9af3fa4f0..f503576d3 100644
--- a/patch/kernel/marvell-next/0008-phy-marvell-88E1512-add-flow-control-support.patch
+++ b/patch/kernel/marvell-next/0008-phy-marvell-88E1512-add-flow-control-support.patch
@@ -22,8 +22,8 @@ index 5de8d5827536..9a5329bfd0fd 100644
  		.phy_id = MARVELL_PHY_ID_88E1510,
  		.phy_id_mask = MARVELL_PHY_ID_MASK,
  		.name = "Marvell 88E1510",
--		.features = PHY_GBIT_FEATURES,
-+		.features = PHY_GBIT_FEATURES | SUPPORTED_Pause,
+-		.features = PHY_GBIT_FEATURES | SUPPORTED_FIBRE,
++		.features = PHY_GBIT_FEATURES | SUPPORTED_FIBRE | SUPPORTED_Pause,
  		.flags = PHY_HAS_INTERRUPT,
  		.config_aneg = &m88e1510_config_aneg,
  		.read_status = &marvell_read_status,
diff --git a/patch/kernel/marvell-next/0014-sfp-add-phylink-based-SFP-module-support - Kopija.patch b/patch/kernel/marvell-next/0014-sfp-add-phylink-based-SFP-module-support - Kopija.patch
new file mode 100644
index 000000000..6b53d53ad
--- /dev/null
+++ b/patch/kernel/marvell-next/0014-sfp-add-phylink-based-SFP-module-support - Kopija.patch	
@@ -0,0 +1,1400 @@
+From: Russell King <rmk+kernel@arm.linux.org.uk>
+Subject: [PATCH 14/30] sfp: add phylink based SFP module support
+MIME-Version: 1.0
+Content-Disposition: inline
+Content-Transfer-Encoding: 8bit
+Content-Type: text/plain; charset="utf-8"
+
+Add support for SFP hotpluggable modules via phylink.  This supports
+both copper and optical SFP modules, which require different Serdes
+modes in order to properly negotiate the link.
+
+Optical SFP modules typically require the Serdes link to be talking
+1000base-X mode - this is the gigabit ethernet mode defined by the
+802.3 standard.
+
+Copper SFP modules typically integrate a PHY in the module to convert
+from Serdes to copper, and the PHY will be configured by the vendor
+to either present a 1000base-X Serdes link (for fixed 1000base-T) or
+a SGMII Serdes link.  However, this is vendor defined, so we instead
+detect the PHY, switch the link to SGMII mode, and use traditional
+PHY based negotiation.
+
+Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
+---
+ drivers/net/phy/Kconfig  |   5 +
+ drivers/net/phy/Makefile |   1 +
+ drivers/net/phy/sfp.c    | 989 +++++++++++++++++++++++++++++++++++++++++++++++
+ include/linux/sfp.h      | 338 ++++++++++++++++
+ 4 files changed, 1333 insertions(+)
+ create mode 100644 drivers/net/phy/sfp.c
+ create mode 100644 include/linux/sfp.h
+
+diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
+index 5c634b4bc9bd..5bdf5c24c6ef 100644
+--- a/drivers/net/phy/Kconfig
++++ b/drivers/net/phy/Kconfig
+@@ -179,6 +179,11 @@ config FIXED_PHY
+ 
+ 	  Currently tested with mpc866ads and mpc8349e-mitx.
+ 
++config SFP
++	tristate "SFP cage support"
++	depends on I2C && PHYLINK
++	select MDIO_I2C
++
+ config MDIO_BITBANG
+ 	tristate "Support for bitbanged MDIO buses"
+ 	help
+diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
+index bc052bb6cee0..a7be372fb123 100644
+--- a/drivers/net/phy/Makefile
++++ b/drivers/net/phy/Makefile
+@@ -45,4 +45,5 @@ obj-$(CONFIG_MDIO_MOXART)	+= mdio-moxart.o
+ obj-$(CONFIG_MDIO_BCM_UNIMAC)	+= mdio-bcm-unimac.o
+ obj-$(CONFIG_MICROCHIP_PHY)	+= microchip.o
+ obj-$(CONFIG_MDIO_BCM_IPROC)	+= mdio-bcm-iproc.o
++obj-$(CONFIG_SFP)		+= sfp.o
+ obj-$(CONFIG_INTEL_XWAY_PHY)	+= intel-xway.o
+diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c
+new file mode 100644
+index 000000000000..70a375403e55
+--- /dev/null
++++ b/drivers/net/phy/sfp.c
+@@ -0,0 +1,989 @@
++#include <linux/delay.h>
++#include <linux/gpio.h>
++#include <linux/i2c.h>
++#include <linux/interrupt.h>
++#include <linux/jiffies.h>
++#include <linux/module.h>
++#include <linux/mutex.h>
++#include <linux/netdevice.h>
++#include <linux/of.h>
++#include <linux/of_net.h>
++#include <linux/phylink.h>
++#include <linux/platform_device.h>
++#include <linux/sfp.h>
++#include <linux/slab.h>
++#include <linux/workqueue.h>
++
++#include "mdio-i2c.h"
++#include "swphy.h"
++
++enum {
++	GPIO_MODDEF0,
++	GPIO_LOS,
++	GPIO_TX_FAULT,
++	GPIO_TX_DISABLE,
++	GPIO_RATE_SELECT,
++	GPIO_MAX,
++
++	SFP_F_PRESENT = BIT(GPIO_MODDEF0),
++	SFP_F_LOS = BIT(GPIO_LOS),
++	SFP_F_TX_FAULT = BIT(GPIO_TX_FAULT),
++	SFP_F_TX_DISABLE = BIT(GPIO_TX_DISABLE),
++	SFP_F_RATE_SELECT = BIT(GPIO_RATE_SELECT),
++
++	SFP_E_INSERT = 0,
++	SFP_E_REMOVE,
++	SFP_E_DEV_DOWN,
++	SFP_E_DEV_UP,
++	SFP_E_TX_FAULT,
++	SFP_E_TX_CLEAR,
++	SFP_E_LOS_HIGH,
++	SFP_E_LOS_LOW,
++	SFP_E_TIMEOUT,
++
++	SFP_MOD_EMPTY = 0,
++	SFP_MOD_PROBE,
++	SFP_MOD_PRESENT,
++	SFP_MOD_ERROR,
++
++	SFP_DEV_DOWN = 0,
++	SFP_DEV_UP,
++
++	SFP_S_DOWN = 0,
++	SFP_S_INIT,
++	SFP_S_WAIT_LOS,
++	SFP_S_LINK_UP,
++	SFP_S_TX_FAULT,
++	SFP_S_REINIT,
++	SFP_S_TX_DISABLE,
++};
++
++static const char *gpio_of_names[] = {
++	"moddef0",
++	"los",
++	"tx-fault",
++	"tx-disable",
++	"rate-select",
++};
++
++static const enum gpiod_flags gpio_flags[] = {
++	GPIOD_IN,
++	GPIOD_IN,
++	GPIOD_IN,
++	GPIOD_ASIS,
++	GPIOD_ASIS,
++};
++
++#define T_INIT_JIFFIES	msecs_to_jiffies(300)
++#define T_RESET_US	10
++#define T_FAULT_RECOVER	msecs_to_jiffies(1000)
++
++/* SFP 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.  Try to work around this
++ * design bug by waiting 50ms before probing, and then retry every 250ms.
++ */
++#define T_PROBE_INIT	msecs_to_jiffies(50)
++#define T_PROBE_RETRY	msecs_to_jiffies(250)
++
++/*
++ * SFP modules appear to always have their PHY configured for bus address
++ * 0x56 (which with mdio-i2c, translates to a PHY address of 22).
++ */
++#define SFP_PHY_ADDR	22
++
++/*
++ * Give this long for the PHY to reset.
++ */
++#define T_PHY_RESET_MS	50
++
++static DEFINE_MUTEX(sfp_mutex);
++
++struct sfp {
++	struct device *dev;
++	struct i2c_adapter *i2c;
++	struct mii_bus *i2c_mii;
++	struct net_device *ndev;
++	struct phylink *phylink;
++	struct phy_device *mod_phy;
++
++	unsigned int (*get_state)(struct sfp *);
++	void (*set_state)(struct sfp *, unsigned int);
++	int (*read)(struct sfp *, bool, u8, void *, size_t);
++
++	struct gpio_desc *gpio[GPIO_MAX];
++
++	unsigned int state;
++	struct delayed_work poll;
++	struct delayed_work timeout;
++	struct mutex sm_mutex;
++	unsigned char sm_mod_state;
++	unsigned char sm_dev_state;
++	unsigned short sm_state;
++	unsigned int sm_retries;
++
++	struct sfp_eeprom_id id;
++
++	struct notifier_block netdev_nb;
++};
++
++static unsigned long poll_jiffies;
++
++static unsigned int sfp_gpio_get_state(struct sfp *sfp)
++{
++	unsigned int i, state, v;
++
++	for (i = state = 0; i < GPIO_MAX; i++) {
++		if (gpio_flags[i] != GPIOD_IN || !sfp->gpio[i])
++			continue;
++
++		v = gpiod_get_value_cansleep(sfp->gpio[i]);
++		if (v)
++			state |= BIT(i);
++	}
++
++	return state;
++}
++
++static void sfp_gpio_set_state(struct sfp *sfp, unsigned int state)
++{
++	if (state & SFP_F_PRESENT) {
++		/* If the module is present, drive the signals */
++		if (sfp->gpio[GPIO_TX_DISABLE])
++			gpiod_direction_output(sfp->gpio[GPIO_TX_DISABLE],
++						state & SFP_F_TX_DISABLE);
++		if (state & SFP_F_RATE_SELECT)
++			gpiod_direction_output(sfp->gpio[GPIO_RATE_SELECT],
++						state & SFP_F_RATE_SELECT);
++	} else {
++		/* Otherwise, let them float to the pull-ups */
++		if (sfp->gpio[GPIO_TX_DISABLE])
++			gpiod_direction_input(sfp->gpio[GPIO_TX_DISABLE]);
++		if (state & SFP_F_RATE_SELECT)
++			gpiod_direction_input(sfp->gpio[GPIO_RATE_SELECT]);
++	}
++}
++
++static int sfp__i2c_read(struct i2c_adapter *i2c, u8 bus_addr, u8 dev_addr,
++	void *buf, size_t len)
++{
++	struct i2c_msg msgs[2];
++	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;
++
++	ret = i2c_transfer(i2c, msgs, ARRAY_SIZE(msgs));
++	if (ret < 0)
++		return ret;
++
++	return ret == ARRAY_SIZE(msgs) ? len : 0;
++}
++
++static int sfp_i2c_read(struct sfp *sfp, bool a2, u8 addr, void *buf,
++	size_t len)
++{
++	return sfp__i2c_read(sfp->i2c, a2 ? 0x51 : 0x50, addr, buf, len);
++}
++
++static int sfp_i2c_configure(struct sfp *sfp, struct i2c_adapter *i2c)
++{
++	struct mii_bus *i2c_mii;
++	int ret;
++
++	if (!i2c_check_functionality(i2c, I2C_FUNC_I2C))
++		return -EINVAL;
++
++	sfp->i2c = i2c;
++	sfp->read = sfp_i2c_read;
++
++	i2c_mii = mdio_i2c_alloc(sfp->dev, i2c);
++	if (IS_ERR(i2c_mii))
++		return PTR_ERR(i2c_mii);
++
++	i2c_mii->name = "SFP I2C Bus";
++	i2c_mii->phy_mask = ~0;
++
++	ret = mdiobus_register(i2c_mii);
++	if (ret < 0) {
++		mdiobus_free(i2c_mii);
++		return ret;
++	}
++
++	sfp->i2c_mii = i2c_mii;
++
++	return 0;
++}
++
++
++/* Interface */
++static unsigned int sfp_get_state(struct sfp *sfp)
++{
++	return sfp->get_state(sfp);
++}
++
++static void sfp_set_state(struct sfp *sfp, unsigned int state)
++{
++	sfp->set_state(sfp, state);
++}
++
++static int sfp_read(struct sfp *sfp, bool a2, u8 addr, void *buf, size_t len)
++{
++	return sfp->read(sfp, a2, addr, buf, len);
++}
++
++static unsigned int sfp_check(void *buf, size_t len)
++{
++	u8 *p, check;
++
++	for (p = buf, check = 0; len; p++, len--)
++		check += *p;
++
++	return check;
++}
++
++/* Helpers */
++static void sfp_module_tx_disable(struct sfp *sfp)
++{
++	dev_dbg(sfp->dev, "tx disable %u -> %u\n",
++		sfp->state & SFP_F_TX_DISABLE ? 1 : 0, 1);
++	sfp->state |= SFP_F_TX_DISABLE;
++	sfp_set_state(sfp, sfp->state);
++}
++
++static void sfp_module_tx_enable(struct sfp *sfp)
++{
++	dev_dbg(sfp->dev, "tx disable %u -> %u\n",
++		sfp->state & SFP_F_TX_DISABLE ? 1 : 0, 0);
++	sfp->state &= ~SFP_F_TX_DISABLE;
++	sfp_set_state(sfp, sfp->state);
++}
++
++static void sfp_module_tx_fault_reset(struct sfp *sfp)
++{
++	unsigned int state = sfp->state;
++
++	if (state & SFP_F_TX_DISABLE)
++		return;
++
++	sfp_set_state(sfp, state | SFP_F_TX_DISABLE);
++
++	udelay(T_RESET_US);
++
++	sfp_set_state(sfp, state);
++}
++
++/* SFP state machine */
++static void sfp_sm_set_timer(struct sfp *sfp, unsigned int timeout)
++{
++	if (timeout)
++		mod_delayed_work(system_power_efficient_wq, &sfp->timeout,
++				 timeout);
++	else
++		cancel_delayed_work(&sfp->timeout);
++}
++
++static void sfp_sm_next(struct sfp *sfp, unsigned int state,
++			unsigned int timeout)
++{
++	sfp->sm_state = state;
++	sfp_sm_set_timer(sfp, timeout);
++}
++
++static void sfp_sm_ins_next(struct sfp *sfp, unsigned int state, unsigned int timeout)
++{
++	sfp->sm_mod_state = state;
++	sfp_sm_set_timer(sfp, timeout);
++}
++
++static void sfp_sm_phy_detach(struct sfp *sfp)
++{
++	phy_stop(sfp->mod_phy);
++	if (sfp->phylink)
++		phylink_disconnect_phy(sfp->phylink);
++	phy_device_remove(sfp->mod_phy);
++	phy_device_free(sfp->mod_phy);
++	sfp->mod_phy = NULL;
++}
++
++static void sfp_sm_probe_phy(struct sfp *sfp)
++{
++	struct phy_device *phy;
++	int err;
++
++	msleep(T_PHY_RESET_MS);
++
++	phy = mdiobus_scan(sfp->i2c_mii, SFP_PHY_ADDR);
++	if (IS_ERR(phy)) {
++		dev_err(sfp->dev, "mdiobus scan returned %ld\n", PTR_ERR(phy));
++		return;
++	}
++	if (!phy) {
++		dev_info(sfp->dev, "no PHY detected\n");
++		return;
++	}
++
++	err = phylink_connect_phy(sfp->phylink, phy);
++	if (err) {
++		phy_device_remove(phy);
++		phy_device_free(phy);
++		dev_err(sfp->dev, "phylink_connect_phy failed: %d\n", err);
++		return;
++	}
++
++	/* We found a PHY.  Switch the link to PHY mode, so we use phylib
++	 * to feed the negotiation results to the MAC.  This avoids the
++	 * question over which Serdes mode the PHY is operating.
++	 */
++	sfp->mod_phy = phy;
++	phylink_set_link_an_mode(sfp->phylink, MLO_AN_PHY);
++	phy_start(phy);
++}
++
++static void sfp_sm_link_up(struct sfp *sfp)
++{
++	if (sfp->phylink)
++		phylink_enable(sfp->phylink);
++
++	sfp_sm_next(sfp, SFP_S_LINK_UP, 0);
++}
++
++static void sfp_sm_link_down(struct sfp *sfp)
++{
++	if (sfp->phylink)
++		phylink_disable(sfp->phylink);
++}
++
++static void sfp_sm_link_check_los(struct sfp *sfp)
++{
++	unsigned int los = sfp->state & SFP_F_LOS;
++
++	/* FIXME: what if neither SFP_OPTIONS_LOS_INVERTED nor
++	 * SFP_OPTIONS_LOS_NORMAL are set?  For now, we assume
++	 * the same as SFP_OPTIONS_LOS_NORMAL set.
++	 */
++	if (sfp->id.ext.options & SFP_OPTIONS_LOS_INVERTED)
++		los ^= SFP_F_LOS;
++
++	if (los)
++		sfp_sm_next(sfp, SFP_S_WAIT_LOS, 0);
++	else
++		sfp_sm_link_up(sfp);
++}
++
++static void sfp_sm_fault(struct sfp *sfp, bool warn)
++{
++	if (sfp->sm_retries && !--sfp->sm_retries) {
++		dev_err(sfp->dev, "module persistently indicates fault, disabling\n");
++		sfp_sm_next(sfp, SFP_S_TX_DISABLE, 0);
++	} else {
++		if (warn)
++			dev_err(sfp->dev, "module transmit fault indicated\n");
++
++		sfp_sm_next(sfp, SFP_S_TX_FAULT, T_FAULT_RECOVER);
++	}
++}
++
++static void sfp_sm_mod_init(struct sfp *sfp)
++{
++	sfp_module_tx_enable(sfp);
++
++	/* Wait t_init before indicating that the link is up, provided the
++	 * current state indicates no TX_FAULT.  If TX_FAULT clears before
++	 * this time, that's fine too.
++	 */
++	sfp_sm_next(sfp, SFP_S_INIT, T_INIT_JIFFIES);
++	sfp->sm_retries = 5;
++
++	if (sfp->phylink && sfp->id.base.e1000_base_t)
++		sfp_sm_probe_phy(sfp);
++}
++
++static int sfp_sm_mod_probe(struct sfp *sfp)
++{
++	/* SFP module inserted - read I2C data */
++	struct sfp_eeprom_id id;
++	char vendor[17];
++	char part[17];
++	char sn[17];
++	char date[9];
++	char rev[5];
++	u8 check;
++	int err;
++
++	err = sfp_read(sfp, false, 0, &id, sizeof(id));
++	if (err < 0) {
++		dev_err(sfp->dev, "failed to read EEPROM: %d\n", err);
++		return -EAGAIN;
++	}
++
++	/* Validate the checksum over the base structure */
++	check = sfp_check(&id.base, sizeof(id.base) - 1);
++	if (check != id.base.cc_base) {
++		dev_err(sfp->dev,
++			"EEPROM base structure checksum failure: 0x%02x\n",
++			check);
++		return -EINVAL;
++	}
++
++	check = sfp_check(&id.ext, sizeof(id.ext) - 1);
++	if (check != id.ext.cc_ext) {
++		dev_err(sfp->dev,
++			"EEPROM extended structure checksum failure: 0x%02x\n",
++			check);
++		memset(&id.ext, 0, sizeof(id.ext));
++	}
++
++	sfp->id = id;
++
++	memcpy(vendor, sfp->id.base.vendor_name, 16);
++	vendor[16] = '\0';
++	memcpy(part, sfp->id.base.vendor_pn, 16);
++	part[16] = '\0';
++	memcpy(rev, sfp->id.base.vendor_rev, 4);
++	rev[4] = '\0';
++	memcpy(sn, sfp->id.ext.vendor_sn, 16);
++	sn[16] = '\0';
++	memcpy(date, sfp->id.ext.datecode, 8);
++	date[8] = '\0';
++
++	dev_info(sfp->dev, "module %s %s rev %s sn %s dc %s\n", vendor, part, rev, sn, date);
++
++	/* We only support SFP modules, not the legacy GBIC modules. */
++	if (sfp->id.base.phys_id != SFP_PHYS_ID_SFP ||
++	    sfp->id.base.phys_ext_id != SFP_PHYS_EXT_ID_SFP) {
++		dev_err(sfp->dev, "module is not SFP - phys id 0x%02x 0x%02x\n",
++			sfp->id.base.phys_id, sfp->id.base.phys_ext_id);
++		return -EINVAL;
++	}
++
++	/*
++	 * What isn't clear from the SFP documentation is whether this
++	 * specifies the encoding expected on the TD/RD lines, or whether
++	 * the TD/RD lines are always 8b10b encoded, but the transceiver
++	 * converts.  Eg, think of a copper SFP supporting 1G/100M/10M
++	 * ethernet: this requires 8b10b encoding for 1G, 4b5b for 100M,
++	 * and manchester for 10M.
++	 */
++	/* 1Gbit ethernet requires 8b10b encoding */
++	if (sfp->id.base.encoding != SFP_ENCODING_8B10B) {
++		dev_err(sfp->dev, "module does not support 8B10B encoding\n");
++		return -EINVAL;
++	}
++
++	if (sfp->phylink) {
++		u32 support;
++		u8 port;
++
++		if (sfp->id.base.e1000_base_t) {
++			support = SUPPORTED_TP;
++			port = PORT_TP;
++		} else {
++			support = SUPPORTED_FIBRE;
++			port = PORT_FIBRE;
++		}
++		phylink_set_link_port(sfp->phylink, support, port);
++
++		/* Setting the serdes link mode is guesswork: there's no
++		 * field in the EEPROM which indicates what mode should
++		 * be used.
++		 *
++		 * If it's a fiber module, it probably does not have a
++		 * PHY, so switch to 802.3z negotiation mode.
++		 *
++		 * If it's a copper module, try SGMII mode.  If that
++		 * doesn't work, we should try switching to 802.3z mode.
++		 */
++		if (!sfp->id.base.e1000_base_t)
++			phylink_set_link_an_mode(sfp->phylink, MLO_AN_8023Z);
++		else
++			phylink_set_link_an_mode(sfp->phylink, MLO_AN_SGMII);
++	}
++
++	return 0;
++}
++
++static void sfp_sm_mod_remove(struct sfp *sfp)
++{
++	if (sfp->mod_phy)
++		sfp_sm_phy_detach(sfp);
++
++	sfp_module_tx_disable(sfp);
++
++	memset(&sfp->id, 0, sizeof(sfp->id));
++
++	dev_info(sfp->dev, "module removed\n");
++}
++
++static void sfp_sm_event(struct sfp *sfp, unsigned int event)
++{
++	mutex_lock(&sfp->sm_mutex);
++
++	dev_dbg(sfp->dev, "SM: enter %u:%u:%u event %u\n",
++		sfp->sm_mod_state, sfp->sm_dev_state, sfp->sm_state, event);
++
++	/* This state machine tracks the insert/remove state of
++	 * the module, and handles probing the on-board EEPROM.
++	 */
++	switch (sfp->sm_mod_state) {
++	default:
++		if (event == SFP_E_INSERT) {
++			sfp_module_tx_disable(sfp);
++			sfp_sm_ins_next(sfp, SFP_MOD_PROBE, T_PROBE_INIT);
++		}
++		break;
++
++	case SFP_MOD_PROBE:
++		if (event == SFP_E_REMOVE) {
++			sfp_sm_ins_next(sfp, SFP_MOD_EMPTY, 0);
++		} else if (event == SFP_E_TIMEOUT) {
++			int err = sfp_sm_mod_probe(sfp);
++
++			if (err == 0)
++				sfp_sm_ins_next(sfp, SFP_MOD_PRESENT, 0);
++			else if (err == -EAGAIN)
++				sfp_sm_set_timer(sfp, T_PROBE_RETRY);
++			else
++				sfp_sm_ins_next(sfp, SFP_MOD_ERROR, 0);
++		}
++		break;
++
++	case SFP_MOD_PRESENT:
++	case SFP_MOD_ERROR:
++		if (event == SFP_E_REMOVE) {
++			sfp_sm_mod_remove(sfp);
++			sfp_sm_ins_next(sfp, SFP_MOD_EMPTY, 0);
++		}
++		break;
++	}
++
++	/* This state machine tracks the netdev up/down state */
++	switch (sfp->sm_dev_state) {
++	default:
++		if (event == SFP_E_DEV_UP)
++			sfp->sm_dev_state = SFP_DEV_UP;
++		break;
++
++	case SFP_DEV_UP:
++		if (event == SFP_E_DEV_DOWN) {
++			/* If the module has a PHY, avoid raising TX disable
++			 * as this resets the PHY. Otherwise, raise it to
++			 * turn the laser off.
++			 */
++			if (!sfp->mod_phy)
++				sfp_module_tx_disable(sfp);
++			sfp->sm_dev_state = SFP_DEV_DOWN;
++		}
++		break;
++	}
++
++	/* Some events are global */
++	if (sfp->sm_state != SFP_S_DOWN &&
++	    (sfp->sm_mod_state != SFP_MOD_PRESENT ||
++	     sfp->sm_dev_state != SFP_DEV_UP)) {
++		if (sfp->sm_state == SFP_S_LINK_UP &&
++		    sfp->sm_dev_state == SFP_DEV_UP)
++			sfp_sm_link_down(sfp);
++		if (sfp->mod_phy)
++			sfp_sm_phy_detach(sfp);
++		sfp_sm_next(sfp, SFP_S_DOWN, 0);
++		mutex_unlock(&sfp->sm_mutex);
++		return;
++	}
++
++	/* The main state machine */
++	switch (sfp->sm_state) {
++	case SFP_S_DOWN:
++		if (sfp->sm_mod_state == SFP_MOD_PRESENT &&
++		    sfp->sm_dev_state == SFP_DEV_UP)
++			sfp_sm_mod_init(sfp);
++		break;
++
++	case SFP_S_INIT:
++		if (event == SFP_E_TIMEOUT && sfp->state & SFP_F_TX_FAULT)
++			sfp_sm_fault(sfp, true);
++		else if (event == SFP_E_TIMEOUT || event == SFP_E_TX_CLEAR)
++			sfp_sm_link_check_los(sfp);
++		break;
++
++	case SFP_S_WAIT_LOS:
++		if (event == SFP_E_TX_FAULT)
++			sfp_sm_fault(sfp, true);
++		else if (event ==
++			 (sfp->id.ext.options & SFP_OPTIONS_LOS_INVERTED ?
++			  SFP_E_LOS_HIGH : SFP_E_LOS_LOW))
++			sfp_sm_link_up(sfp);
++		break;
++
++	case SFP_S_LINK_UP:
++		if (event == SFP_E_TX_FAULT) {
++			sfp_sm_link_down(sfp);
++			sfp_sm_fault(sfp, true);
++		} else if (event ==
++			   (sfp->id.ext.options & SFP_OPTIONS_LOS_INVERTED ?
++			    SFP_E_LOS_LOW : SFP_E_LOS_HIGH)) {
++			sfp_sm_link_down(sfp);
++			sfp_sm_next(sfp, SFP_S_WAIT_LOS, 0);
++		}
++		break;
++
++	case SFP_S_TX_FAULT:
++		if (event == SFP_E_TIMEOUT) {
++			sfp_module_tx_fault_reset(sfp);
++			sfp_sm_next(sfp, SFP_S_REINIT, T_INIT_JIFFIES);
++		}
++		break;
++
++	case SFP_S_REINIT:
++		if (event == SFP_E_TIMEOUT && sfp->state & SFP_F_TX_FAULT) {
++			sfp_sm_fault(sfp, false);
++		} else if (event == SFP_E_TIMEOUT || event == SFP_E_TX_CLEAR) {
++			dev_info(sfp->dev, "module transmit fault recovered\n");
++			sfp_sm_link_check_los(sfp);
++		}
++		break;
++
++	case SFP_S_TX_DISABLE:
++		break;
++	}
++
++	dev_dbg(sfp->dev, "SM: exit %u:%u:%u\n",
++		sfp->sm_mod_state, sfp->sm_dev_state, sfp->sm_state);
++
++	mutex_unlock(&sfp->sm_mutex);
++}
++
++#if 0
++static int sfp_phy_module_info(struct phy_device *phy,
++			       struct ethtool_modinfo *modinfo)
++{
++	struct sfp *sfp = phy->priv;
++
++	/* locking... and check module is present */
++
++	if (sfp->id.ext.sff8472_compliance) {
++		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 sfp_phy_module_eeprom(struct phy_device *phy,
++	struct ethtool_eeprom *ee, u8 *data)
++{
++	struct sfp *sfp = phy->priv;
++	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 = last;
++		if (len > ETH_MODULE_SFF_8079_LEN)
++			len = ETH_MODULE_SFF_8079_LEN;
++		len -= first;
++
++		ret = sfp->read(sfp, false, first, data, len);
++		if (ret < 0)
++			return ret;
++
++		first += len;
++		data += len;
++	}
++	if (first >= ETH_MODULE_SFF_8079_LEN && last > first) {
++		len = last - first;
++
++		ret = sfp->read(sfp, true, first, data, len);
++		if (ret < 0)
++			return ret;
++	}
++	return 0;
++}
++#endif
++
++static void sfp_timeout(struct work_struct *work)
++{
++	struct sfp *sfp = container_of(work, struct sfp, timeout.work);
++
++	sfp_sm_event(sfp, SFP_E_TIMEOUT);
++}
++
++static void sfp_check_state(struct sfp *sfp)
++{
++	unsigned int state, i, changed;
++
++	state = sfp_get_state(sfp);
++	changed = state ^ sfp->state;
++	changed &= SFP_F_PRESENT | SFP_F_LOS | SFP_F_TX_FAULT;
++
++	for (i = 0; i < GPIO_MAX; i++)
++		if (changed & BIT(i))
++			dev_dbg(sfp->dev, "%s %u -> %u\n", gpio_of_names[i],
++				!!(sfp->state & BIT(i)), !!(state & BIT(i)));
++
++	state |= sfp->state & (SFP_F_TX_DISABLE | SFP_F_RATE_SELECT);
++	sfp->state = state;
++
++	if (changed & SFP_F_PRESENT)
++		sfp_sm_event(sfp, state & SFP_F_PRESENT ?
++				SFP_E_INSERT : SFP_E_REMOVE);
++
++	if (changed & SFP_F_TX_FAULT)
++		sfp_sm_event(sfp, state & SFP_F_TX_FAULT ?
++				SFP_E_TX_FAULT : SFP_E_TX_CLEAR);
++
++	if (changed & SFP_F_LOS)
++		sfp_sm_event(sfp, state & SFP_F_LOS ?
++				SFP_E_LOS_HIGH : SFP_E_LOS_LOW);
++}
++
++static irqreturn_t sfp_irq(int irq, void *data)
++{
++	struct sfp *sfp = data;
++
++	sfp_check_state(sfp);
++
++	return IRQ_HANDLED;
++}
++
++static void sfp_poll(struct work_struct *work)
++{
++	struct sfp *sfp = container_of(work, struct sfp, poll.work);
++
++	sfp_check_state(sfp);
++	mod_delayed_work(system_wq, &sfp->poll, poll_jiffies);
++}
++
++static int sfp_netdev_notify(struct notifier_block *nb, unsigned long act, void *data)
++{
++	struct sfp *sfp = container_of(nb, struct sfp, netdev_nb);
++	struct netdev_notifier_info *info = data;
++	struct net_device *ndev = info->dev;
++
++	if (!sfp->ndev || ndev != sfp->ndev)
++		return NOTIFY_DONE;
++
++	switch (act) {
++	case NETDEV_UP:
++		sfp_sm_event(sfp, SFP_E_DEV_UP);
++		break;
++
++	case NETDEV_GOING_DOWN:
++		sfp_sm_event(sfp, SFP_E_DEV_DOWN);
++		break;
++
++	case NETDEV_UNREGISTER:
++		if (sfp->mod_phy && sfp->phylink)
++			phylink_disconnect_phy(sfp->phylink);
++		sfp->phylink = NULL;
++		dev_put(sfp->ndev);
++		sfp->ndev = NULL;
++		break;
++	}
++	return NOTIFY_OK;
++}
++
++static struct sfp *sfp_alloc(struct device *dev)
++{
++	struct sfp *sfp;
++
++	sfp = kzalloc(sizeof(*sfp), GFP_KERNEL);
++	if (!sfp)
++		return ERR_PTR(-ENOMEM);
++
++	sfp->dev = dev;
++
++	mutex_init(&sfp->sm_mutex);
++	INIT_DELAYED_WORK(&sfp->poll, sfp_poll);
++	INIT_DELAYED_WORK(&sfp->timeout, sfp_timeout);
++
++	sfp->netdev_nb.notifier_call = sfp_netdev_notify;
++
++	return sfp;
++}
++
++static void sfp_destroy(struct sfp *sfp)
++{
++	cancel_delayed_work_sync(&sfp->poll);
++	cancel_delayed_work_sync(&sfp->timeout);
++	if (sfp->i2c_mii) {
++		mdiobus_unregister(sfp->i2c_mii);
++		mdiobus_free(sfp->i2c_mii);
++	}
++	if (sfp->i2c)
++		i2c_put_adapter(sfp->i2c);
++	of_node_put(sfp->dev->of_node);
++	kfree(sfp);
++}
++
++static void sfp_cleanup(void *data)
++{
++	struct sfp *sfp = data;
++
++	sfp_destroy(sfp);
++}
++
++static int sfp_probe(struct platform_device *pdev)
++{
++	struct sfp *sfp;
++	bool poll = false;
++	int irq, err, i;
++
++	sfp = sfp_alloc(&pdev->dev);
++	if (IS_ERR(sfp))
++		return PTR_ERR(sfp);
++
++	platform_set_drvdata(pdev, sfp);
++
++	err = devm_add_action(sfp->dev, sfp_cleanup, sfp);
++	if (err < 0)
++		return err;
++
++	if (pdev->dev.of_node) {
++		struct device_node *node = pdev->dev.of_node;
++		struct device_node *np;
++
++		np = of_parse_phandle(node, "i2c-bus", 0);
++		if (np) {
++			struct i2c_adapter *i2c;
++
++			i2c = of_find_i2c_adapter_by_node(np);
++			of_node_put(np);
++			if (!i2c)
++				return -EPROBE_DEFER;
++
++			err = sfp_i2c_configure(sfp, i2c);
++			if (err < 0) {
++				i2c_put_adapter(i2c);
++				return err;
++			}
++		}
++
++		for (i = 0; i < GPIO_MAX; i++) {
++			sfp->gpio[i] = devm_gpiod_get_optional(sfp->dev,
++					   gpio_of_names[i], gpio_flags[i]);
++			if (IS_ERR(sfp->gpio[i]))
++				return PTR_ERR(sfp->gpio[i]);
++		}
++
++		sfp->get_state = sfp_gpio_get_state;
++		sfp->set_state = sfp_gpio_set_state;
++
++		np = of_parse_phandle(node, "sfp,ethernet", 0);
++		if (!np) {
++			dev_err(sfp->dev, "missing sfp,ethernet property\n");
++			return -EINVAL;
++		}
++
++		sfp->ndev = of_find_net_device_by_node(np);
++		if (!sfp->ndev) {
++			dev_err(sfp->dev, "ethernet device not found\n");
++			return -EPROBE_DEFER;
++		}
++
++		dev_hold(sfp->ndev);
++		put_device(&sfp->ndev->dev);
++
++		sfp->phylink = phylink_lookup_by_netdev(sfp->ndev);
++		if (!sfp->phylink) {
++			dev_err(sfp->dev, "ethernet device not found\n");
++			return -EPROBE_DEFER;
++		}
++
++		phylink_disable(sfp->phylink);
++	}
++
++	sfp->state = sfp_get_state(sfp);
++	if (sfp->gpio[GPIO_TX_DISABLE] &&
++	    gpiod_get_value_cansleep(sfp->gpio[GPIO_TX_DISABLE]))
++		sfp->state |= SFP_F_TX_DISABLE;
++	if (sfp->gpio[GPIO_RATE_SELECT] &&
++	    gpiod_get_value_cansleep(sfp->gpio[GPIO_RATE_SELECT]))
++		sfp->state |= SFP_F_RATE_SELECT;
++	sfp_set_state(sfp, sfp->state);
++	sfp_module_tx_disable(sfp);
++	if (sfp->state & SFP_F_PRESENT)
++		sfp_sm_event(sfp, SFP_E_INSERT);
++
++	for (i = 0; i < GPIO_MAX; i++) {
++		if (gpio_flags[i] != GPIOD_IN || !sfp->gpio[i])
++			continue;
++
++		irq = gpiod_to_irq(sfp->gpio[i]);
++		if (!irq) {
++			poll = true;
++			continue;
++		}
++
++		err = devm_request_threaded_irq(sfp->dev, irq, NULL, sfp_irq,
++						IRQF_ONESHOT |
++						IRQF_TRIGGER_RISING |
++						IRQF_TRIGGER_FALLING,
++						dev_name(sfp->dev), sfp);
++		if (err)
++			poll = true;
++	}
++
++	if (poll)
++		mod_delayed_work(system_wq, &sfp->poll, poll_jiffies);
++
++	register_netdevice_notifier(&sfp->netdev_nb);
++
++	return 0;
++}
++
++static int sfp_remove(struct platform_device *pdev)
++{
++	struct sfp *sfp = platform_get_drvdata(pdev);
++
++	unregister_netdevice_notifier(&sfp->netdev_nb);
++	if (sfp->ndev)
++		dev_put(sfp->ndev);
++
++	return 0;
++}
++
++static const struct of_device_id sfp_of_match[] = {
++	{ .compatible = "sff,sfp", },
++	{ },
++};
++MODULE_DEVICE_TABLE(of, sfp_of_match);
++
++static struct platform_driver sfp_driver = {
++	.probe = sfp_probe,
++	.remove = sfp_remove,
++	.driver = {
++		.name = "sfp",
++		.of_match_table = sfp_of_match,
++	},
++};
++
++static int sfp_init(void)
++{
++	poll_jiffies = msecs_to_jiffies(100);
++
++	return platform_driver_register(&sfp_driver);
++}
++module_init(sfp_init);
++
++static void sfp_exit(void)
++{
++	platform_driver_unregister(&sfp_driver);
++}
++module_exit(sfp_exit);
++
++MODULE_ALIAS("platform:sfp");
++MODULE_AUTHOR("Russell King");
++MODULE_LICENSE("GPL v2");
+diff --git a/include/linux/sfp.h b/include/linux/sfp.h
+new file mode 100644
+index 000000000000..15be090170c0
+--- /dev/null
++++ b/include/linux/sfp.h
+@@ -0,0 +1,338 @@
++#ifndef LINUX_SFP_H
++#define LINUX_SFP_H
++
++struct __packed sfp_eeprom_base {
++	u8 phys_id;
++	u8 phys_ext_id;
++	u8 connector;
++#if defined __BIG_ENDIAN_BITFIELD
++	u8 e10g_base_er:1;
++	u8 e10g_base_lrm:1;
++	u8 e10g_base_lr:1;
++	u8 e10g_base_sr:1;
++	u8 if_1x_sx:1;
++	u8 if_1x_lx:1;
++	u8 if_1x_copper_active:1;
++	u8 if_1x_copper_passive:1;
++
++	u8 escon_mmf_1310_led:1;
++	u8 escon_smf_1310_laser:1;
++	u8 sonet_oc192_short_reach:1;
++	u8 sonet_reach_bit1:1;
++	u8 sonet_reach_bit2:1;
++	u8 sonet_oc48_long_reach:1;
++	u8 sonet_oc48_intermediate_reach:1;
++	u8 sonet_oc48_short_reach:1;
++
++	u8 unallocated_5_7:1;
++	u8 sonet_oc12_smf_long_reach:1;
++	u8 sonet_oc12_smf_intermediate_reach:1;
++	u8 sonet_oc12_short_reach:1;
++	u8 unallocated_5_3:1;
++	u8 sonet_oc3_smf_long_reach:1;
++	u8 sonet_oc3_smf_intermediate_reach:1;
++	u8 sonet_oc3_short_reach:1;
++
++	u8 e_base_px:1;
++	u8 e_base_bx10:1;
++	u8 e100_base_fx:1;
++	u8 e100_base_lx:1;
++	u8 e1000_base_t:1;
++	u8 e1000_base_cx:1;
++	u8 e1000_base_lx:1;
++	u8 e1000_base_sx:1;
++
++	u8 fc_ll_v:1;
++	u8 fc_ll_s:1;
++	u8 fc_ll_i:1;
++	u8 fc_ll_l:1;
++	u8 fc_ll_m:1;
++	u8 fc_tech_sa:1;
++	u8 fc_tech_lc:1;
++	u8 fc_tech_electrical_inter_enclosure:1;
++
++	u8 fc_tech_electrical_intra_enclosure:1;
++	u8 fc_tech_sn:1;
++	u8 fc_tech_sl:1;
++	u8 fc_tech_ll:1;
++	u8 sfp_ct_active:1;
++	u8 sfp_ct_passive:1;
++	u8 unallocated_8_1:1;
++	u8 unallocated_8_0:1;
++
++	u8 fc_media_tw:1;
++	u8 fc_media_tp:1;
++	u8 fc_media_mi:1;
++	u8 fc_media_tv:1;
++	u8 fc_media_m6:1;
++	u8 fc_media_m5:1;
++	u8 unallocated_9_1:1;
++	u8 fc_media_sm:1;
++
++	u8 fc_speed_1200:1;
++	u8 fc_speed_800:1;
++	u8 fc_speed_1600:1;
++	u8 fc_speed_400:1;
++	u8 fc_speed_3200:1;
++	u8 fc_speed_200:1;
++	u8 unallocated_10_1:1;
++	u8 fc_speed_100:1;
++#elif defined __LITTLE_ENDIAN_BITFIELD
++	u8 if_1x_copper_passive:1;
++	u8 if_1x_copper_active:1;
++	u8 if_1x_lx:1;
++	u8 if_1x_sx:1;
++	u8 e10g_base_sr:1;
++	u8 e10g_base_lr:1;
++	u8 e10g_base_lrm:1;
++	u8 e10g_base_er:1;
++
++	u8 sonet_oc3_short_reach:1;
++	u8 sonet_oc3_smf_intermediate_reach:1;
++	u8 sonet_oc3_smf_long_reach:1;
++	u8 unallocated_5_3:1;
++	u8 sonet_oc12_short_reach:1;
++	u8 sonet_oc12_smf_intermediate_reach:1;
++	u8 sonet_oc12_smf_long_reach:1;
++	u8 unallocated_5_7:1;
++
++	u8 sonet_oc48_short_reach:1;
++	u8 sonet_oc48_intermediate_reach:1;
++	u8 sonet_oc48_long_reach:1;
++	u8 sonet_reach_bit2:1;
++	u8 sonet_reach_bit1:1;
++	u8 sonet_oc192_short_reach:1;
++	u8 escon_smf_1310_laser:1;
++	u8 escon_mmf_1310_led:1;
++
++	u8 e1000_base_sx:1;
++	u8 e1000_base_lx:1;
++	u8 e1000_base_cx:1;
++	u8 e1000_base_t:1;
++	u8 e100_base_lx:1;
++	u8 e100_base_fx:1;
++	u8 e_base_bx10:1;
++	u8 e_base_px:1;
++
++	u8 fc_tech_electrical_inter_enclosure:1;
++	u8 fc_tech_lc:1;
++	u8 fc_tech_sa:1;
++	u8 fc_ll_m:1;
++	u8 fc_ll_l:1;
++	u8 fc_ll_i:1;
++	u8 fc_ll_s:1;
++	u8 fc_ll_v:1;
++
++	u8 unallocated_8_0:1;
++	u8 unallocated_8_1:1;
++	u8 sfp_ct_passive:1;
++	u8 sfp_ct_active:1;
++	u8 fc_tech_ll:1;
++	u8 fc_tech_sl:1;
++	u8 fc_tech_sn:1;
++	u8 fc_tech_electrical_intra_enclosure:1;
++
++	u8 fc_media_sm:1;
++	u8 unallocated_9_1:1;
++	u8 fc_media_m5:1;
++	u8 fc_media_m6:1;
++	u8 fc_media_tv:1;
++	u8 fc_media_mi:1;
++	u8 fc_media_tp:1;
++	u8 fc_media_tw:1;
++
++	u8 fc_speed_100:1;
++	u8 unallocated_10_1:1;
++	u8 fc_speed_200:1;
++	u8 fc_speed_3200:1;
++	u8 fc_speed_400:1;
++	u8 fc_speed_1600:1;
++	u8 fc_speed_800:1;
++	u8 fc_speed_1200:1;
++#else
++#error Unknown Endian
++#endif
++	u8 encoding;
++	u8 br_nominal;
++	u8 rate_id;
++	u8 link_len[6];
++	char vendor_name[16];
++	u8 reserved36;
++	char vendor_oui[3];
++	char vendor_pn[16];
++	char vendor_rev[4];
++	union {
++		__be16 optical_wavelength;
++		u8 cable_spec;
++	};
++	u8 reserved62;
++	u8 cc_base;
++};
++
++struct __packed sfp_eeprom_ext {
++	__be16 options;
++	u8 br_max;
++	u8 br_min;
++	char vendor_sn[16];
++	char datecode[8];
++	u8 diagmon;
++	u8 enhopts;
++	u8 sff8472_compliance;
++	u8 cc_ext;
++};
++
++struct __packed sfp_eeprom_id {
++	struct sfp_eeprom_base base;
++	struct sfp_eeprom_ext ext;
++};
++
++/* SFP EEPROM registers */
++enum {
++	SFP_PHYS_ID			= 0x00,
++	SFP_PHYS_EXT_ID			= 0x01,
++	SFP_CONNECTOR			= 0x02,
++	SFP_COMPLIANCE			= 0x03,
++	SFP_ENCODING			= 0x0b,
++	SFP_BR_NOMINAL			= 0x0c,
++	SFP_RATE_ID			= 0x0d,
++	SFP_LINK_LEN_SM_KM		= 0x0e,
++	SFP_LINK_LEN_SM_100M		= 0x0f,
++	SFP_LINK_LEN_50UM_OM2_10M	= 0x10,
++	SFP_LINK_LEN_62_5UM_OM1_10M	= 0x11,
++	SFP_LINK_LEN_COPPER_1M		= 0x12,
++	SFP_LINK_LEN_50UM_OM4_10M	= 0x12,
++	SFP_LINK_LEN_50UM_OM3_10M	= 0x13,
++	SFP_VENDOR_NAME			= 0x14,
++	SFP_VENDOR_OUI			= 0x25,
++	SFP_VENDOR_PN			= 0x28,
++	SFP_VENDOR_REV			= 0x38,
++	SFP_OPTICAL_WAVELENGTH_MSB	= 0x3c,
++	SFP_OPTICAL_WAVELENGTH_LSB	= 0x3d,
++	SFP_CABLE_SPEC			= 0x3c,
++	SFP_CC_BASE			= 0x3f,
++	SFP_OPTIONS			= 0x40,	/* 2 bytes, MSB, LSB */
++	SFP_BR_MAX			= 0x42,
++	SFP_BR_MIN			= 0x43,
++	SFP_VENDOR_SN			= 0x44,
++	SFP_DATECODE			= 0x54,
++	SFP_DIAGMON			= 0x5c,
++	SFP_ENHOPTS			= 0x5d,
++	SFP_SFF8472_COMPLIANCE		= 0x5e,
++	SFP_CC_EXT			= 0x5f,
++
++	SFP_PHYS_ID_SFP			= 0x03,
++	SFP_PHYS_EXT_ID_SFP		= 0x04,
++	SFP_CONNECTOR_UNSPEC		= 0x00,
++	/* codes 01-05 not supportable on SFP */
++	SFP_CONNECTOR_FIBERJACK		= 0x06,
++	SFP_CONNECTOR_LC		= 0x07,
++	SFP_CONNECTOR_MT_RJ		= 0x08,
++	SFP_CONNECTOR_MU		= 0x09,
++	SFP_CONNECTOR_SG		= 0x0a,
++	SFP_CONNECTOR_OPTICAL_PIGTAIL	= 0x0b,
++	SFP_CONNECTOR_HSSDC_II		= 0x20,
++	SFP_CONNECTOR_COPPER_PIGTAIL	= 0x21,
++	SFP_ENCODING_UNSPEC		= 0x00,
++	SFP_ENCODING_8B10B		= 0x01,
++	SFP_ENCODING_4B5B		= 0x02,
++	SFP_ENCODING_NRZ		= 0x03,
++	SFP_ENCODING_MANCHESTER		= 0x04,
++	SFP_OPTIONS_HIGH_POWER_LEVEL	= BIT(13),
++	SFP_OPTIONS_PAGING_A2		= BIT(12),
++	SFP_OPTIONS_RETIMER		= BIT(11),
++	SFP_OPTIONS_COOLED_XCVR		= BIT(10),
++	SFP_OPTIONS_POWER_DECL		= BIT(9),
++	SFP_OPTIONS_RX_LINEAR_OUT	= BIT(8),
++	SFP_OPTIONS_RX_DECISION_THRESH	= BIT(7),
++	SFP_OPTIONS_TUNABLE_TX		= BIT(6),
++	SFP_OPTIONS_RATE_SELECT		= BIT(5),
++	SFP_OPTIONS_TX_DISABLE		= BIT(4),
++	SFP_OPTIONS_TX_FAULT		= BIT(3),
++	SFP_OPTIONS_LOS_INVERTED	= BIT(2),
++	SFP_OPTIONS_LOS_NORMAL		= BIT(1),
++	SFP_DIAGMON_DDM			= BIT(6),
++	SFP_DIAGMON_INT_CAL		= BIT(5),
++	SFP_DIAGMON_EXT_CAL		= BIT(4),
++	SFP_DIAGMON_RXPWR_AVG		= BIT(3),
++	SFP_DIAGMON_ADDRMODE		= BIT(2),
++	SFP_ENHOPTS_ALARMWARN		= BIT(7),
++	SFP_ENHOPTS_SOFT_TX_DISABLE	= BIT(6),
++	SFP_ENHOPTS_SOFT_TX_FAULT	= BIT(5),
++	SFP_ENHOPTS_SOFT_RX_LOS		= BIT(4),
++	SFP_ENHOPTS_SOFT_RATE_SELECT	= BIT(3),
++	SFP_ENHOPTS_APP_SELECT_SFF8079	= BIT(2),
++	SFP_ENHOPTS_SOFT_RATE_SFF8431	= BIT(1),
++	SFP_SFF8472_COMPLIANCE_NONE	= 0x00,
++	SFP_SFF8472_COMPLIANCE_REV9_3	= 0x01,
++	SFP_SFF8472_COMPLIANCE_REV9_5	= 0x02,
++	SFP_SFF8472_COMPLIANCE_REV10_2	= 0x03,
++	SFP_SFF8472_COMPLIANCE_REV10_4	= 0x04,
++	SFP_SFF8472_COMPLIANCE_REV11_0	= 0x05,
++	SFP_SFF8472_COMPLIANCE_REV11_3	= 0x06,
++	SFP_SFF8472_COMPLIANCE_REV11_4	= 0x07,
++	SFP_SFF8472_COMPLIANCE_REV12_0	= 0x08,
++};
++
++/* SFP Diagnostics */
++enum {
++	/* Alarm and warnings stored MSB at lower address then LSB */
++	SFP_TEMP_HIGH_ALARM		= 0x00,
++	SFP_TEMP_LOW_ALARM		= 0x02,
++	SFP_TEMP_HIGH_WARN		= 0x04,
++	SFP_TEMP_LOW_WARN		= 0x06,
++	SFP_VOLT_HIGH_ALARM		= 0x08,
++	SFP_VOLT_LOW_ALARM		= 0x0a,
++	SFP_VOLT_HIGH_WARN		= 0x0c,
++	SFP_VOLT_LOW_WARN		= 0x0e,
++	SFP_BIAS_HIGH_ALARM		= 0x10,
++	SFP_BIAS_LOW_ALARM		= 0x12,
++	SFP_BIAS_HIGH_WARN		= 0x14,
++	SFP_BIAS_LOW_WARN		= 0x16,
++	SFP_TXPWR_HIGH_ALARM		= 0x18,
++	SFP_TXPWR_LOW_ALARM		= 0x1a,
++	SFP_TXPWR_HIGH_WARN		= 0x1c,
++	SFP_TXPWR_LOW_WARN		= 0x1e,
++	SFP_RXPWR_HIGH_ALARM		= 0x20,
++	SFP_RXPWR_LOW_ALARM		= 0x22,
++	SFP_RXPWR_HIGH_WARN		= 0x24,
++	SFP_RXPWR_LOW_WARN		= 0x26,
++	SFP_LASER_TEMP_HIGH_ALARM	= 0x28,
++	SFP_LASER_TEMP_LOW_ALARM	= 0x2a,
++	SFP_LASER_TEMP_HIGH_WARN	= 0x2c,
++	SFP_LASER_TEMP_LOW_WARN		= 0x2e,
++	SFP_TEC_CUR_HIGH_ALARM		= 0x30,
++	SFP_TEC_CUR_LOW_ALARM		= 0x32,
++	SFP_TEC_CUR_HIGH_WARN		= 0x34,
++	SFP_TEC_CUR_LOW_WARN		= 0x36,
++	SFP_CAL_RXPWR4			= 0x38,
++	SFP_CAL_RXPWR3			= 0x3c,
++	SFP_CAL_RXPWR2			= 0x40,
++	SFP_CAL_RXPWR1			= 0x44,
++	SFP_CAL_RXPWR0			= 0x48,
++	SFP_CAL_TXI_SLOPE		= 0x4c,
++	SFP_CAL_TXI_OFFSET		= 0x4e,
++	SFP_CAL_TXPWR_SLOPE		= 0x50,
++	SFP_CAL_TXPWR_OFFSET		= 0x52,
++	SFP_CAL_T_SLOPE			= 0x54,
++	SFP_CAL_T_OFFSET		= 0x56,
++	SFP_CAL_V_SLOPE			= 0x58,
++	SFP_CAL_V_OFFSET		= 0x5a,
++	SFP_CHKSUM			= 0x5f,
++
++	SFP_TEMP			= 0x60,
++	SFP_VCC				= 0x62,
++	SFP_TX_BIAS			= 0x64,
++	SFP_TX_POWER			= 0x66,
++	SFP_RX_POWER			= 0x68,
++	SFP_LASER_TEMP			= 0x6a,
++	SFP_TEC_CUR			= 0x6c,
++
++	SFP_STATUS			= 0x6e,
++	SFP_ALARM			= 0x70,
++
++	SFP_EXT_STATUS			= 0x76,
++	SFP_VSL				= 0x78,
++	SFP_PAGE			= 0x7f,
++};
++
++#endif
+-- 
+2.1.0
+
diff --git a/patch/kernel/marvell-next/0052-ARM-dts-armada-388-clearfog-remove-duplicate-mdio-entry.patch b/patch/kernel/marvell-next/0052-ARM-dts-armada-388-clearfog-remove-duplicate-mdio-entry.patch
deleted file mode 100644
index 8182f8e2f..000000000
--- a/patch/kernel/marvell-next/0052-ARM-dts-armada-388-clearfog-remove-duplicate-mdio-entry.patch
+++ /dev/null
@@ -1,43 +0,0 @@
-From 31c0a49bb770afe520a0a7380da428b2af1f6fb9 Mon Sep 17 00:00:00 2001
-From: Russell King <rmk+kernel@armlinux.org.uk>
-Date: Sun, 10 Jul 2016 16:27:38 +0100
-Subject: [PATCH] ARM: dts: armada-388-clearfog: remove duplicate mdio entry
-
-The clearfog DTS should not be defining the on-board phy, this device
-is located on the microsom.  Remove the duplicated definition.
-
-Reported-by: Jon Nettleton <jon@solid-run.com>
-Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
----
- arch/arm/boot/dts/armada-388-clearfog.dts | 16 ----------------
- 1 file changed, 16 deletions(-)
-
-diff --git a/arch/arm/boot/dts/armada-388-clearfog.dts b/arch/arm/boot/dts/armada-388-clearfog.dts
-index 8a13265..74cc941 100644
---- a/arch/arm/boot/dts/armada-388-clearfog.dts
-+++ b/arch/arm/boot/dts/armada-388-clearfog.dts
-@@ -201,22 +201,6 @@
- 				status = "okay";
- 			};
- 
--			mdio@72004 {
--				pinctrl-0 = <&mdio_pins>;
--				pinctrl-names = "default";
--
--				phy_dedicated: ethernet-phy@0 {
--					/*
--					 * Annoyingly, the marvell phy driver
--					 * configures the LED register, rather
--					 * than preserving reset-loaded setting.
--					 * We undo that rubbish here.
--					 */
--					marvell,reg-init = <3 16 0 0x101e>;
--					reg = <0>;
--				};
--			};
--
- 			pinctrl@18000 {
- 				clearfog_dsa0_clk_pins: clearfog-dsa0-clk-pins {
- 					marvell,pins = "mpp46";