6ca43043fa60dd00a90dce748521ec462c27916f
[openwrt/openwrt.git] / target / linux / mvebu / patches-4.14 / 410-sfp-hack-allow-marvell-10G-phy-support-to-use-SFP.patch
1 From 4a4aca08b11501cb1b2c509113bbb65eb66a1f45 Mon Sep 17 00:00:00 2001
2 From: Russell King <rmk+kernel@armlinux.org.uk>
3 Date: Fri, 14 Apr 2017 14:21:25 +0100
4 Subject: sfp: hack: allow marvell 10G phy support to use SFP
5
6 Allow the Marvell 10G PHY to register with the SFP bus, so that SFP+
7 cages can work. This bypasses phylink, meaning that socket status
8 is not taken into account for the link state. Also, the tx-disable
9 signal must be commented out in DT for this to work...
10
11 Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
12 ---
13 drivers/net/phy/marvell10g.c | 54 +++++++++++++++++++++++++++++++++++++++++++-
14 1 file changed, 53 insertions(+), 1 deletion(-)
15
16 --- a/drivers/net/phy/marvell10g.c
17 +++ b/drivers/net/phy/marvell10g.c
18 @@ -15,8 +15,10 @@
19 * If both the fiber and copper ports are connected, the first to gain
20 * link takes priority and the other port is completely locked out.
21 */
22 +#include <linux/of.h>
23 #include <linux/phy.h>
24 #include <linux/marvell_phy.h>
25 +#include <linux/sfp.h>
26
27 enum {
28 MV_PCS_BASE_T = 0x0000,
29 @@ -38,6 +40,11 @@ enum {
30 MV_AN_RESULT_SPD_10000 = BIT(15),
31 };
32
33 +struct mv3310_priv {
34 + struct device_node *sfp_node;
35 + struct sfp_bus *sfp_bus;
36 +};
37 +
38 static int mv3310_modify(struct phy_device *phydev, int devad, u16 reg,
39 u16 mask, u16 bits)
40 {
41 @@ -56,17 +63,52 @@ static int mv3310_modify(struct phy_devi
42 return ret < 0 ? ret : 1;
43 }
44
45 +static int mv3310_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
46 +{
47 + struct phy_device *phydev = upstream;
48 + struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev);
49 +
50 + if (sfp_parse_interface(priv->sfp_bus, id) != PHY_INTERFACE_MODE_10GKR) {
51 + dev_err(&phydev->mdio.dev, "incompatible SFP module inserted\n");
52 + return -EINVAL;
53 + }
54 + return 0;
55 +}
56 +
57 +static const struct sfp_upstream_ops mv3310_sfp_ops = {
58 + .module_insert = mv3310_sfp_insert,
59 +};
60 +
61 static int mv3310_probe(struct phy_device *phydev)
62 {
63 + struct mv3310_priv *priv;
64 u32 mmd_mask = MDIO_DEVS_PMAPMD | MDIO_DEVS_AN;
65
66 if (!phydev->is_c45 ||
67 (phydev->c45_ids.devices_in_package & mmd_mask) != mmd_mask)
68 return -ENODEV;
69
70 + priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
71 + if (!priv)
72 + return -ENOMEM;
73 +
74 + dev_set_drvdata(&phydev->mdio.dev, priv);
75 +
76 + if (phydev->mdio.dev.of_node)
77 + priv->sfp_node = of_parse_phandle(phydev->mdio.dev.of_node,
78 + "sfp", 0);
79 +
80 return 0;
81 }
82
83 +static void mv3310_remove(struct phy_device *phydev)
84 +{
85 + struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev);
86 +
87 + if (priv->sfp_bus)
88 + sfp_unregister_upstream(priv->sfp_bus);
89 +}
90 +
91 /*
92 * Resetting the MV88X3310 causes it to become non-responsive. Avoid
93 * setting the reset bit(s).
94 @@ -78,6 +120,7 @@ static int mv3310_soft_reset(struct phy_
95
96 static int mv3310_config_init(struct phy_device *phydev)
97 {
98 + struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev);
99 __ETHTOOL_DECLARE_LINK_MODE_MASK(supported) = { 0, };
100 u32 mask;
101 int val;
102 @@ -166,6 +209,14 @@ static int mv3310_config_init(struct phy
103 phydev->supported &= mask;
104 phydev->advertising &= phydev->supported;
105
106 + /* Would be nice to do this in the probe function, but unfortunately,
107 + * phylib doesn't have phydev->attached_dev set there.
108 + */
109 + if (priv->sfp_node && !priv->sfp_bus)
110 + priv->sfp_bus = sfp_register_upstream(priv->sfp_node,
111 + phydev->attached_dev,
112 + phydev, &mv3310_sfp_ops);
113 +
114 return 0;
115 }
116
117 @@ -349,12 +400,13 @@ static struct phy_driver mv3310_drivers[
118 SUPPORTED_FIBRE |
119 SUPPORTED_10000baseT_Full |
120 SUPPORTED_Backplane,
121 - .probe = mv3310_probe,
122 .soft_reset = mv3310_soft_reset,
123 .config_init = mv3310_config_init,
124 + .probe = mv3310_probe,
125 .config_aneg = mv3310_config_aneg,
126 .aneg_done = mv3310_aneg_done,
127 .read_status = mv3310_read_status,
128 + .remove = mv3310_remove,
129 },
130 };
131