mvebu: add initial support for uDPU board
[oweals/openwrt.git] / target / linux / mvebu / patches-4.19 / 405-net-phy-marvell10g-add-SFP-support.patch
1 From 5f3ac54810055fec0cc667bb04c16f783830abff 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: [PATCH] net: phy: marvell10g: add SFP+ support
5
6 Add support for SFP+ cages to the Marvell 10G PHY driver. This is
7 slightly complicated by the way phylib works in that we need to use
8 a multi-step process to attach the SFP bus, and we also need to track
9 the phylink state machine to know when the module's transmit disable
10 signal should change state.
11
12 With appropriate DT changes, this allows the SFP+ canges on the
13 Macchiatobin platform to be functional.
14
15 Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
16 ---
17  drivers/net/phy/marvell10g.c | 80 ++++++++++++++++++++++++++++++++++++
18  1 file changed, 80 insertions(+)
19
20 --- a/drivers/net/phy/marvell10g.c
21 +++ b/drivers/net/phy/marvell10g.c
22 @@ -25,6 +25,8 @@
23  #include <linux/hwmon.h>
24  #include <linux/marvell_phy.h>
25  #include <linux/phy.h>
26 +#include <linux/property.h>
27 +#include <linux/sfp.h>
28  
29  enum {
30         MV_PMA_BOOT             = 0xc050,
31 @@ -56,6 +58,11 @@ enum {
32  };
33  
34  struct mv3310_priv {
35 +       struct fwnode_handle *sfp_fwnode;
36 +       struct sfp_bus *sfp_bus;
37 +       enum phy_state state;
38 +       bool running;
39 +
40         struct device *hwmon_dev;
41         char *hwmon_name;
42  };
43 @@ -219,6 +226,27 @@ static int mv3310_hwmon_probe(struct phy
44  }
45  #endif
46  
47 +static int mv3310_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
48 +{
49 +       struct phy_device *phydev = upstream;
50 +       struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev);
51 +       __ETHTOOL_DECLARE_LINK_MODE_MASK(support) = { 0, };
52 +       phy_interface_t iface;
53 +
54 +       sfp_parse_support(priv->sfp_bus, id, support);
55 +       iface = sfp_select_interface(priv->sfp_bus, id, support);
56 +
57 +       if (iface != PHY_INTERFACE_MODE_10GKR) {
58 +               dev_err(&phydev->mdio.dev, "incompatible SFP module inserted\n");
59 +               return -EINVAL;
60 +       }
61 +       return 0;
62 +}
63 +
64 +static const struct sfp_upstream_ops mv3310_sfp_ops = {
65 +       .module_insert = mv3310_sfp_insert,
66 +};
67 +
68  static int mv3310_probe(struct phy_device *phydev)
69  {
70         struct mv3310_priv *priv;
71 @@ -249,9 +277,30 @@ static int mv3310_probe(struct phy_devic
72         if (ret)
73                 return ret;
74  
75 +       if (phydev->mdio.dev.fwnode) {
76 +               struct fwnode_reference_args ref;
77 +               int ret;
78 +
79 +               ret = fwnode_property_get_reference_args(phydev->mdio.dev.fwnode,
80 +                                                        "sfp", NULL, 0, 0,
81 +                                                        &ref);
82 +               if (ret == 0)
83 +                       priv->sfp_fwnode = ref.fwnode;
84 +       }
85 +
86         return 0;
87  }
88  
89 +static void mv3310_remove(struct phy_device *phydev)
90 +{
91 +       struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev);
92 +
93 +       if (priv->sfp_bus)
94 +               sfp_unregister_upstream(priv->sfp_bus);
95 +
96 +       fwnode_handle_put(priv->sfp_fwnode);
97 +}
98 +
99  static int mv3310_suspend(struct phy_device *phydev)
100  {
101         return 0;
102 @@ -262,8 +311,29 @@ static int mv3310_resume(struct phy_devi
103         return mv3310_hwmon_config(phydev, true);
104  }
105  
106 +static void mv3310_link_change_notify(struct phy_device *phydev)
107 +{
108 +       struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev);
109 +       enum phy_state state = phydev->state;
110 +       bool running;
111 +
112 +       if (priv->sfp_bus && priv->state != state) {
113 +               priv->state = state;
114 +
115 +               running = state >= PHY_UP && state < PHY_HALTED;
116 +               if (priv->running != running) {
117 +                       priv->running = running;
118 +                       if (running)
119 +                               sfp_upstream_start(priv->sfp_bus);
120 +                       else
121 +                               sfp_upstream_stop(priv->sfp_bus);
122 +               }
123 +       }
124 +}
125 +
126  static int mv3310_config_init(struct phy_device *phydev)
127  {
128 +       struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev);
129         __ETHTOOL_DECLARE_LINK_MODE_MASK(supported) = { 0, };
130         u32 mask;
131         int val;
132 @@ -357,6 +427,14 @@ static int mv3310_config_init(struct phy
133         phydev->supported &= mask;
134         phydev->advertising &= phydev->supported;
135  
136 +       /* Would be nice to do this in the probe function, but unfortunately,
137 +        * phylib doesn't have phydev->attached_dev set there.
138 +        */
139 +       if (priv->sfp_fwnode && !priv->sfp_bus)
140 +               priv->sfp_bus = sfp_register_upstream(priv->sfp_fwnode,
141 +                                                     phydev->attached_dev,
142 +                                                     phydev, &mv3310_sfp_ops);
143 +
144         return 0;
145  }
146  
147 @@ -566,6 +644,8 @@ static struct phy_driver mv3310_drivers[
148                 .config_aneg    = mv3310_config_aneg,
149                 .aneg_done      = mv3310_aneg_done,
150                 .read_status    = mv3310_read_status,
151 +               .remove         = mv3310_remove,
152 +               .link_change_notify = mv3310_link_change_notify,
153         },
154  };
155