You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
131 lines
3.8 KiB
131 lines
3.8 KiB
From 4a4aca08b11501cb1b2c509113bbb65eb66a1f45 Mon Sep 17 00:00:00 2001
|
|
From: Russell King <rmk+kernel@armlinux.org.uk>
|
|
Date: Fri, 14 Apr 2017 14:21:25 +0100
|
|
Subject: 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>
|
|
---
|
|
drivers/net/phy/marvell10g.c | 54 +++++++++++++++++++++++++++++++++++++++++++-
|
|
1 file changed, 53 insertions(+), 1 deletion(-)
|
|
|
|
--- a/drivers/net/phy/marvell10g.c
|
|
+++ b/drivers/net/phy/marvell10g.c
|
|
@@ -15,8 +15,10 @@
|
|
* 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/marvell_phy.h>
|
|
+#include <linux/sfp.h>
|
|
|
|
enum {
|
|
MV_PCS_BASE_T = 0x0000,
|
|
@@ -38,6 +40,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)
|
|
{
|
|
@@ -56,17 +63,52 @@ static int mv3310_modify(struct phy_devi
|
|
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).
|
|
@@ -78,6 +120,7 @@ static int mv3310_soft_reset(struct phy_
|
|
|
|
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;
|
|
@@ -166,6 +209,14 @@ static int mv3310_config_init(struct phy
|
|
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;
|
|
}
|
|
|
|
@@ -349,12 +400,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,
|
|
},
|
|
};
|
|
|
|
|