aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/mvebu/patches-4.14/410-sfp-hack-allow-marvell-10G-phy-support-to-use-SFP.patch
blob: 6ca43043fa60dd00a90dce748521ec462c27916f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
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,
 	},
 };