summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRussell King <rmk+kernel@armlinux.org.uk>2017-04-14 14:21:25 +0100
committerRussell King <rmk+kernel@armlinux.org.uk>2017-07-07 16:12:14 +0100
commitbcfefaf65045e79f3b37dd09163e5dd44285d3e9 (patch)
tree00fc00208cbc1ddfa7011a8041e98f9febb7276c
parenteebe34fe4bd96fd01c2e1eecb52a1ae8a8bb80bc (diff)
sfp: hack: allow marvell 10G phy support to use SFP
Allow the Marvell 10G PHY to register with the SFP bus, so that SFP+ cages can work. This bypasses phylink, meaning that socket status is not taken into account for the link state. Also, the tx-disable signal must be commented out in DT for this to work... Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
-rw-r--r--drivers/net/phy/marvell10g.c54
1 files changed, 53 insertions, 1 deletions
diff --git a/drivers/net/phy/marvell10g.c b/drivers/net/phy/marvell10g.c
index aebc08beceba..e3e743ab0bb4 100644
--- a/drivers/net/phy/marvell10g.c
+++ b/drivers/net/phy/marvell10g.c
@@ -15,7 +15,9 @@
* If both the fiber and copper ports are connected, the first to gain
* link takes priority and the other port is completely locked out.
*/
+#include <linux/of.h>
#include <linux/phy.h>
+#include <linux/sfp.h>
enum {
MV_PCS_BASE_T = 0x0000,
@@ -37,6 +39,11 @@ enum {
MV_AN_RESULT_SPD_10000 = BIT(15),
};
+struct mv3310_priv {
+ struct device_node *sfp_node;
+ struct sfp_bus *sfp_bus;
+};
+
static int mv3310_modify(struct phy_device *phydev, int devad, u16 reg,
u16 mask, u16 bits)
{
@@ -55,17 +62,52 @@ static int mv3310_modify(struct phy_device *phydev, int devad, u16 reg,
return ret < 0 ? ret : 1;
}
+static int mv3310_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
+{
+ struct phy_device *phydev = upstream;
+ struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev);
+
+ if (sfp_parse_interface(priv->sfp_bus, id) != PHY_INTERFACE_MODE_10GKR) {
+ dev_err(&phydev->mdio.dev, "incompatible SFP module inserted\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static const struct sfp_upstream_ops mv3310_sfp_ops = {
+ .module_insert = mv3310_sfp_insert,
+};
+
static int mv3310_probe(struct phy_device *phydev)
{
+ struct mv3310_priv *priv;
u32 mmd_mask = MDIO_DEVS_PMAPMD | MDIO_DEVS_AN;
if (!phydev->is_c45 ||
(phydev->c45_ids.devices_in_package & mmd_mask) != mmd_mask)
return -ENODEV;
+ priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ dev_set_drvdata(&phydev->mdio.dev, priv);
+
+ if (phydev->mdio.dev.of_node)
+ priv->sfp_node = of_parse_phandle(phydev->mdio.dev.of_node,
+ "sfp", 0);
+
return 0;
}
+static void mv3310_remove(struct phy_device *phydev)
+{
+ struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev);
+
+ if (priv->sfp_bus)
+ sfp_unregister_upstream(priv->sfp_bus);
+}
+
/*
* Resetting the MV88X3310 causes it to become non-responsive. Avoid
* setting the reset bit(s).
@@ -77,6 +119,7 @@ static int mv3310_soft_reset(struct phy_device *phydev)
static int mv3310_config_init(struct phy_device *phydev)
{
+ struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev);
__ETHTOOL_DECLARE_LINK_MODE_MASK(supported) = { 0, };
u32 mask;
int val;
@@ -165,6 +208,14 @@ static int mv3310_config_init(struct phy_device *phydev)
phydev->supported &= mask;
phydev->advertising &= phydev->supported;
+ /* Would be nice to do this in the probe function, but unfortunately,
+ * phylib doesn't have phydev->attached_dev set there.
+ */
+ if (priv->sfp_node && !priv->sfp_bus)
+ priv->sfp_bus = sfp_register_upstream(priv->sfp_node,
+ phydev->attached_dev,
+ phydev, &mv3310_sfp_ops);
+
return 0;
}
@@ -348,12 +399,13 @@ static struct phy_driver mv3310_drivers[] = {
SUPPORTED_FIBRE |
SUPPORTED_10000baseT_Full |
SUPPORTED_Backplane,
- .probe = mv3310_probe,
.soft_reset = mv3310_soft_reset,
.config_init = mv3310_config_init,
+ .probe = mv3310_probe,
.config_aneg = mv3310_config_aneg,
.aneg_done = mv3310_aneg_done,
.read_status = mv3310_read_status,
+ .remove = mv3310_remove,
},
};