mvebu: Add basic support for WRT1900AC (v1) and Turris Omnia (pre 2019)
[librecmc/librecmc.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_PMA_BOOT             = 0xc050,
29 @@ -41,6 +43,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 @@ -59,8 +66,25 @@ 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         int ret;
66  
67 @@ -78,9 +102,27 @@ static int mv3310_probe(struct phy_devic
68                 return -ENODEV;
69         }
70  
71 +       priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
72 +       if (!priv)
73 +               return -ENOMEM;
74 +
75 +       dev_set_drvdata(&phydev->mdio.dev, priv);
76 +
77 +       if (phydev->mdio.dev.of_node)
78 +               priv->sfp_node = of_parse_phandle(phydev->mdio.dev.of_node,
79 +                                                 "sfp", 0);
80 +
81         return 0;
82  }
83  
84 +static void mv3310_remove(struct phy_device *phydev)
85 +{
86 +       struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev);
87 +
88 +       if (priv->sfp_bus)
89 +               sfp_unregister_upstream(priv->sfp_bus);
90 +}
91 +
92  /*
93   * Resetting the MV88X3310 causes it to become non-responsive.  Avoid
94   * setting the reset bit(s).
95 @@ -92,6 +134,7 @@ static int mv3310_soft_reset(struct phy_
96  
97  static int mv3310_config_init(struct phy_device *phydev)
98  {
99 +       struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev);
100         __ETHTOOL_DECLARE_LINK_MODE_MASK(supported) = { 0, };
101         u32 mask;
102         int val;
103 @@ -180,6 +223,14 @@ static int mv3310_config_init(struct phy
104         phydev->supported &= mask;
105         phydev->advertising &= phydev->supported;
106  
107 +       /* Would be nice to do this in the probe function, but unfortunately,
108 +        * phylib doesn't have phydev->attached_dev set there.
109 +        */
110 +       if (priv->sfp_node && !priv->sfp_bus)
111 +               priv->sfp_bus = sfp_register_upstream(priv->sfp_node,
112 +                                                     phydev->attached_dev,
113 +                                                     phydev, &mv3310_sfp_ops);
114 +
115         return 0;
116  }
117  
118 @@ -363,12 +414,13 @@ static struct phy_driver mv3310_drivers[
119                                   SUPPORTED_FIBRE |
120                                   SUPPORTED_10000baseT_Full |
121                                   SUPPORTED_Backplane,
122 -               .probe          = mv3310_probe,
123                 .soft_reset     = mv3310_soft_reset,
124                 .config_init    = mv3310_config_init,
125 +               .probe          = mv3310_probe,
126                 .config_aneg    = mv3310_config_aneg,
127                 .aneg_done      = mv3310_aneg_done,
128                 .read_status    = mv3310_read_status,
129 +               .remove         = mv3310_remove,
130         },
131  };
132