kernel: bump 4.14 to 4.14.120
[oweals/openwrt.git] / target / linux / sunxi / patches-4.14 / 002-net-stmmac-dwmac-sun8i-Handle-integrated-external-MD.patch
1 From 634db83b82658f4641d8026e340c6027cf74a6bb Mon Sep 17 00:00:00 2001
2 From: Corentin Labbe <clabbe.montjoie@gmail.com>
3 Date: Tue, 24 Oct 2017 19:57:13 +0200
4 Subject: [PATCH] net: stmmac: dwmac-sun8i: Handle integrated/external MDIOs
5
6 The Allwinner H3 SoC have two distinct MDIO bus, only one could be
7 active at the same time.
8 The selection of the active MDIO bus are done via some bits in the EMAC
9 register of the system controller.
10
11 This patch implement this MDIO switch via a custom MDIO-mux.
12
13 Signed-off-by: Corentin Labbe <clabbe.montjoie@gmail.com>
14 Reviewed-by: Andrew Lunn <andrew@lunn.ch>
15 Signed-off-by: David S. Miller <davem@davemloft.net>
16 ---
17  drivers/net/ethernet/stmicro/stmmac/Kconfig       |   1 +
18  drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c | 353 ++++++++++++++--------
19  2 files changed, 224 insertions(+), 130 deletions(-)
20
21 --- a/drivers/net/ethernet/stmicro/stmmac/Kconfig
22 +++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig
23 @@ -159,6 +159,7 @@ config DWMAC_SUN8I
24         tristate "Allwinner sun8i GMAC support"
25         default ARCH_SUNXI
26         depends on OF && (ARCH_SUNXI || COMPILE_TEST)
27 +       select MDIO_BUS_MUX
28         ---help---
29           Support for Allwinner H3 A83T A64 EMAC ethernet controllers.
30  
31 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
32 +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
33 @@ -17,6 +17,7 @@
34  #include <linux/clk.h>
35  #include <linux/io.h>
36  #include <linux/iopoll.h>
37 +#include <linux/mdio-mux.h>
38  #include <linux/mfd/syscon.h>
39  #include <linux/module.h>
40  #include <linux/of_device.h>
41 @@ -41,14 +42,14 @@
42   *                             This value is used for disabling properly EMAC
43   *                             and used as a good starting value in case of the
44   *                             boot process(uboot) leave some stuff.
45 - * @internal_phy:              Does the MAC embed an internal PHY
46 + * @soc_has_internal_phy:      Does the MAC embed an internal PHY
47   * @support_mii:               Does the MAC handle MII
48   * @support_rmii:              Does the MAC handle RMII
49   * @support_rgmii:             Does the MAC handle RGMII
50   */
51  struct emac_variant {
52         u32 default_syscon_value;
53 -       int internal_phy;
54 +       bool soc_has_internal_phy;
55         bool support_mii;
56         bool support_rmii;
57         bool support_rgmii;
58 @@ -61,7 +62,8 @@ struct emac_variant {
59   * @rst_ephy:  reference to the optional EPHY reset for the internal PHY
60   * @variant:   reference to the current board variant
61   * @regmap:    regmap for using the syscon
62 - * @use_internal_phy: Does the current PHY choice imply using the internal PHY
63 + * @internal_phy_powered: Does the internal PHY is enabled
64 + * @mux_handle:        Internal pointer used by mdio-mux lib
65   */
66  struct sunxi_priv_data {
67         struct clk *tx_clk;
68 @@ -70,12 +72,13 @@ struct sunxi_priv_data {
69         struct reset_control *rst_ephy;
70         const struct emac_variant *variant;
71         struct regmap *regmap;
72 -       bool use_internal_phy;
73 +       bool internal_phy_powered;
74 +       void *mux_handle;
75  };
76  
77  static const struct emac_variant emac_variant_h3 = {
78         .default_syscon_value = 0x58000,
79 -       .internal_phy = PHY_INTERFACE_MODE_MII,
80 +       .soc_has_internal_phy = true,
81         .support_mii = true,
82         .support_rmii = true,
83         .support_rgmii = true
84 @@ -83,20 +86,20 @@ static const struct emac_variant emac_va
85  
86  static const struct emac_variant emac_variant_v3s = {
87         .default_syscon_value = 0x38000,
88 -       .internal_phy = PHY_INTERFACE_MODE_MII,
89 +       .soc_has_internal_phy = true,
90         .support_mii = true
91  };
92  
93  static const struct emac_variant emac_variant_a83t = {
94         .default_syscon_value = 0,
95 -       .internal_phy = 0,
96 +       .soc_has_internal_phy = false,
97         .support_mii = true,
98         .support_rgmii = true
99  };
100  
101  static const struct emac_variant emac_variant_a64 = {
102         .default_syscon_value = 0,
103 -       .internal_phy = 0,
104 +       .soc_has_internal_phy = false,
105         .support_mii = true,
106         .support_rmii = true,
107         .support_rgmii = true
108 @@ -195,6 +198,9 @@ static const struct emac_variant emac_va
109  #define H3_EPHY_LED_POL                BIT(17) /* 1: active low, 0: active high */
110  #define H3_EPHY_SHUTDOWN       BIT(16) /* 1: shutdown, 0: power up */
111  #define H3_EPHY_SELECT         BIT(15) /* 1: internal PHY, 0: external PHY */
112 +#define H3_EPHY_MUX_MASK       (H3_EPHY_SHUTDOWN | H3_EPHY_SELECT)
113 +#define DWMAC_SUN8I_MDIO_MUX_INTERNAL_ID       1
114 +#define DWMAC_SUN8I_MDIO_MUX_EXTERNAL_ID       2
115  
116  /* H3/A64 specific bits */
117  #define SYSCON_RMII_EN         BIT(13) /* 1: enable RMII (overrides EPIT) */
118 @@ -635,6 +641,159 @@ static int sun8i_dwmac_reset(struct stmm
119         return 0;
120  }
121  
122 +/* Search in mdio-mux node for internal PHY node and get its clk/reset */
123 +static int get_ephy_nodes(struct stmmac_priv *priv)
124 +{
125 +       struct sunxi_priv_data *gmac = priv->plat->bsp_priv;
126 +       struct device_node *mdio_mux, *iphynode;
127 +       struct device_node *mdio_internal;
128 +       int ret;
129 +
130 +       mdio_mux = of_get_child_by_name(priv->device->of_node, "mdio-mux");
131 +       if (!mdio_mux) {
132 +               dev_err(priv->device, "Cannot get mdio-mux node\n");
133 +               return -ENODEV;
134 +       }
135 +
136 +       mdio_internal = of_find_compatible_node(mdio_mux, NULL,
137 +                                               "allwinner,sun8i-h3-mdio-internal");
138 +       if (!mdio_internal) {
139 +               dev_err(priv->device, "Cannot get internal_mdio node\n");
140 +               return -ENODEV;
141 +       }
142 +
143 +       /* Seek for internal PHY */
144 +       for_each_child_of_node(mdio_internal, iphynode) {
145 +               gmac->ephy_clk = of_clk_get(iphynode, 0);
146 +               if (IS_ERR(gmac->ephy_clk))
147 +                       continue;
148 +               gmac->rst_ephy = of_reset_control_get_exclusive(iphynode, NULL);
149 +               if (IS_ERR(gmac->rst_ephy)) {
150 +                       ret = PTR_ERR(gmac->rst_ephy);
151 +                       if (ret == -EPROBE_DEFER)
152 +                               return ret;
153 +                       continue;
154 +               }
155 +               dev_info(priv->device, "Found internal PHY node\n");
156 +               return 0;
157 +       }
158 +       return -ENODEV;
159 +}
160 +
161 +static int sun8i_dwmac_power_internal_phy(struct stmmac_priv *priv)
162 +{
163 +       struct sunxi_priv_data *gmac = priv->plat->bsp_priv;
164 +       int ret;
165 +
166 +       if (gmac->internal_phy_powered) {
167 +               dev_warn(priv->device, "Internal PHY already powered\n");
168 +               return 0;
169 +       }
170 +
171 +       dev_info(priv->device, "Powering internal PHY\n");
172 +       ret = clk_prepare_enable(gmac->ephy_clk);
173 +       if (ret) {
174 +               dev_err(priv->device, "Cannot enable internal PHY\n");
175 +               return ret;
176 +       }
177 +
178 +       /* Make sure the EPHY is properly reseted, as U-Boot may leave
179 +        * it at deasserted state, and thus it may fail to reset EMAC.
180 +        */
181 +       reset_control_assert(gmac->rst_ephy);
182 +
183 +       ret = reset_control_deassert(gmac->rst_ephy);
184 +       if (ret) {
185 +               dev_err(priv->device, "Cannot deassert internal phy\n");
186 +               clk_disable_unprepare(gmac->ephy_clk);
187 +               return ret;
188 +       }
189 +
190 +       gmac->internal_phy_powered = true;
191 +
192 +       return 0;
193 +}
194 +
195 +static int sun8i_dwmac_unpower_internal_phy(struct sunxi_priv_data *gmac)
196 +{
197 +       if (!gmac->internal_phy_powered)
198 +               return 0;
199 +
200 +       clk_disable_unprepare(gmac->ephy_clk);
201 +       reset_control_assert(gmac->rst_ephy);
202 +       gmac->internal_phy_powered = false;
203 +       return 0;
204 +}
205 +
206 +/* MDIO multiplexing switch function
207 + * This function is called by the mdio-mux layer when it thinks the mdio bus
208 + * multiplexer needs to switch.
209 + * 'current_child' is the current value of the mux register
210 + * 'desired_child' is the value of the 'reg' property of the target child MDIO
211 + * node.
212 + * The first time this function is called, current_child == -1.
213 + * If current_child == desired_child, then the mux is already set to the
214 + * correct bus.
215 + */
216 +static int mdio_mux_syscon_switch_fn(int current_child, int desired_child,
217 +                                    void *data)
218 +{
219 +       struct stmmac_priv *priv = data;
220 +       struct sunxi_priv_data *gmac = priv->plat->bsp_priv;
221 +       u32 reg, val;
222 +       int ret = 0;
223 +       bool need_power_ephy = false;
224 +
225 +       if (current_child ^ desired_child) {
226 +               regmap_read(gmac->regmap, SYSCON_EMAC_REG, &reg);
227 +               switch (desired_child) {
228 +               case DWMAC_SUN8I_MDIO_MUX_INTERNAL_ID:
229 +                       dev_info(priv->device, "Switch mux to internal PHY");
230 +                       val = (reg & ~H3_EPHY_MUX_MASK) | H3_EPHY_SELECT;
231 +
232 +                       need_power_ephy = true;
233 +                       break;
234 +               case DWMAC_SUN8I_MDIO_MUX_EXTERNAL_ID:
235 +                       dev_info(priv->device, "Switch mux to external PHY");
236 +                       val = (reg & ~H3_EPHY_MUX_MASK) | H3_EPHY_SHUTDOWN;
237 +                       need_power_ephy = false;
238 +                       break;
239 +               default:
240 +                       dev_err(priv->device, "Invalid child ID %x\n",
241 +                               desired_child);
242 +                       return -EINVAL;
243 +               }
244 +               regmap_write(gmac->regmap, SYSCON_EMAC_REG, val);
245 +               if (need_power_ephy) {
246 +                       ret = sun8i_dwmac_power_internal_phy(priv);
247 +                       if (ret)
248 +                               return ret;
249 +               } else {
250 +                       sun8i_dwmac_unpower_internal_phy(gmac);
251 +               }
252 +               /* After changing syscon value, the MAC need reset or it will
253 +                * use the last value (and so the last PHY set).
254 +                */
255 +               ret = sun8i_dwmac_reset(priv);
256 +       }
257 +       return ret;
258 +}
259 +
260 +static int sun8i_dwmac_register_mdio_mux(struct stmmac_priv *priv)
261 +{
262 +       int ret;
263 +       struct device_node *mdio_mux;
264 +       struct sunxi_priv_data *gmac = priv->plat->bsp_priv;
265 +
266 +       mdio_mux = of_get_child_by_name(priv->device->of_node, "mdio-mux");
267 +       if (!mdio_mux)
268 +               return -ENODEV;
269 +
270 +       ret = mdio_mux_init(priv->device, mdio_mux, mdio_mux_syscon_switch_fn,
271 +                           &gmac->mux_handle, priv, priv->mii);
272 +       return ret;
273 +}
274 +
275  static int sun8i_dwmac_set_syscon(struct stmmac_priv *priv)
276  {
277         struct sunxi_priv_data *gmac = priv->plat->bsp_priv;
278 @@ -649,35 +808,25 @@ static int sun8i_dwmac_set_syscon(struct
279                          "Current syscon value is not the default %x (expect %x)\n",
280                          val, reg);
281  
282 -       if (gmac->variant->internal_phy) {
283 -               if (!gmac->use_internal_phy) {
284 -                       /* switch to external PHY interface */
285 -                       reg &= ~H3_EPHY_SELECT;
286 -               } else {
287 -                       reg |= H3_EPHY_SELECT;
288 -                       reg &= ~H3_EPHY_SHUTDOWN;
289 -                       dev_dbg(priv->device, "Select internal_phy %x\n", reg);
290 -
291 -                       if (of_property_read_bool(priv->plat->phy_node,
292 -                                                 "allwinner,leds-active-low"))
293 -                               reg |= H3_EPHY_LED_POL;
294 -                       else
295 -                               reg &= ~H3_EPHY_LED_POL;
296 -
297 -                       /* Force EPHY xtal frequency to 24MHz. */
298 -                       reg |= H3_EPHY_CLK_SEL;
299 -
300 -                       ret = of_mdio_parse_addr(priv->device,
301 -                                                priv->plat->phy_node);
302 -                       if (ret < 0) {
303 -                               dev_err(priv->device, "Could not parse MDIO addr\n");
304 -                               return ret;
305 -                       }
306 -                       /* of_mdio_parse_addr returns a valid (0 ~ 31) PHY
307 -                        * address. No need to mask it again.
308 -                        */
309 -                       reg |= ret << H3_EPHY_ADDR_SHIFT;
310 +       if (gmac->variant->soc_has_internal_phy) {
311 +               if (of_property_read_bool(priv->plat->phy_node,
312 +                                         "allwinner,leds-active-low"))
313 +                       reg |= H3_EPHY_LED_POL;
314 +               else
315 +                       reg &= ~H3_EPHY_LED_POL;
316 +
317 +               /* Force EPHY xtal frequency to 24MHz. */
318 +               reg |= H3_EPHY_CLK_SEL;
319 +
320 +               ret = of_mdio_parse_addr(priv->device, priv->plat->phy_node);
321 +               if (ret < 0) {
322 +                       dev_err(priv->device, "Could not parse MDIO addr\n");
323 +                       return ret;
324                 }
325 +               /* of_mdio_parse_addr returns a valid (0 ~ 31) PHY
326 +                * address. No need to mask it again.
327 +                */
328 +               reg |= 1 << H3_EPHY_ADDR_SHIFT;
329         }
330  
331         if (!of_property_read_u32(node, "allwinner,tx-delay-ps", &val)) {
332 @@ -747,81 +896,21 @@ static void sun8i_dwmac_unset_syscon(str
333         regmap_write(gmac->regmap, SYSCON_EMAC_REG, reg);
334  }
335  
336 -static int sun8i_dwmac_power_internal_phy(struct stmmac_priv *priv)
337 +static void sun8i_dwmac_exit(struct platform_device *pdev, void *priv)
338  {
339 -       struct sunxi_priv_data *gmac = priv->plat->bsp_priv;
340 -       int ret;
341 -
342 -       if (!gmac->use_internal_phy)
343 -               return 0;
344 +       struct sunxi_priv_data *gmac = priv;
345  
346 -       ret = clk_prepare_enable(gmac->ephy_clk);
347 -       if (ret) {
348 -               dev_err(priv->device, "Cannot enable ephy\n");
349 -               return ret;
350 +       if (gmac->variant->soc_has_internal_phy) {
351 +               /* sun8i_dwmac_exit could be called with mdiomux uninit */
352 +               if (gmac->mux_handle)
353 +                       mdio_mux_uninit(gmac->mux_handle);
354 +               if (gmac->internal_phy_powered)
355 +                       sun8i_dwmac_unpower_internal_phy(gmac);
356         }
357  
358 -       /* Make sure the EPHY is properly reseted, as U-Boot may leave
359 -        * it at deasserted state, and thus it may fail to reset EMAC.
360 -        */
361 -       reset_control_assert(gmac->rst_ephy);
362 -
363 -       ret = reset_control_deassert(gmac->rst_ephy);
364 -       if (ret) {
365 -               dev_err(priv->device, "Cannot deassert ephy\n");
366 -               clk_disable_unprepare(gmac->ephy_clk);
367 -               return ret;
368 -       }
369 -
370 -       return 0;
371 -}
372 -
373 -static int sun8i_dwmac_unpower_internal_phy(struct sunxi_priv_data *gmac)
374 -{
375 -       if (!gmac->use_internal_phy)
376 -               return 0;
377 -
378 -       clk_disable_unprepare(gmac->ephy_clk);
379 -       reset_control_assert(gmac->rst_ephy);
380 -       return 0;
381 -}
382 -
383 -/* sun8i_power_phy() - Activate the PHY:
384 - * In case of error, no need to call sun8i_unpower_phy(),
385 - * it will be called anyway by sun8i_dwmac_exit()
386 - */
387 -static int sun8i_power_phy(struct stmmac_priv *priv)
388 -{
389 -       int ret;
390 -
391 -       ret = sun8i_dwmac_power_internal_phy(priv);
392 -       if (ret)
393 -               return ret;
394 -
395 -       ret = sun8i_dwmac_set_syscon(priv);
396 -       if (ret)
397 -               return ret;
398 -
399 -       /* After changing syscon value, the MAC need reset or it will use
400 -        * the last value (and so the last PHY set.
401 -        */
402 -       ret = sun8i_dwmac_reset(priv);
403 -       if (ret)
404 -               return ret;
405 -       return 0;
406 -}
407 -
408 -static void sun8i_unpower_phy(struct sunxi_priv_data *gmac)
409 -{
410         sun8i_dwmac_unset_syscon(gmac);
411 -       sun8i_dwmac_unpower_internal_phy(gmac);
412 -}
413 -
414 -static void sun8i_dwmac_exit(struct platform_device *pdev, void *priv)
415 -{
416 -       struct sunxi_priv_data *gmac = priv;
417  
418 -       sun8i_unpower_phy(gmac);
419 +       reset_control_put(gmac->rst_ephy);
420  
421         clk_disable_unprepare(gmac->tx_clk);
422  
423 @@ -850,7 +939,7 @@ static struct mac_device_info *sun8i_dwm
424         if (!mac)
425                 return NULL;
426  
427 -       ret = sun8i_power_phy(priv);
428 +       ret = sun8i_dwmac_set_syscon(priv);
429         if (ret)
430                 return NULL;
431  
432 @@ -892,6 +981,8 @@ static int sun8i_dwmac_probe(struct plat
433         struct sunxi_priv_data *gmac;
434         struct device *dev = &pdev->dev;
435         int ret;
436 +       struct stmmac_priv *priv;
437 +       struct net_device *ndev;
438  
439         ret = stmmac_get_platform_resources(pdev, &stmmac_res);
440         if (ret)
441 @@ -935,29 +1026,6 @@ static int sun8i_dwmac_probe(struct plat
442         }
443  
444         plat_dat->interface = of_get_phy_mode(dev->of_node);
445 -       if (plat_dat->interface == gmac->variant->internal_phy) {
446 -               dev_info(&pdev->dev, "Will use internal PHY\n");
447 -               gmac->use_internal_phy = true;
448 -               gmac->ephy_clk = of_clk_get(plat_dat->phy_node, 0);
449 -               if (IS_ERR(gmac->ephy_clk)) {
450 -                       ret = PTR_ERR(gmac->ephy_clk);
451 -                       dev_err(&pdev->dev, "Cannot get EPHY clock: %d\n", ret);
452 -                       return -EINVAL;
453 -               }
454 -
455 -               gmac->rst_ephy = of_reset_control_get(plat_dat->phy_node, NULL);
456 -               if (IS_ERR(gmac->rst_ephy)) {
457 -                       ret = PTR_ERR(gmac->rst_ephy);
458 -                       if (ret == -EPROBE_DEFER)
459 -                               return ret;
460 -                       dev_err(&pdev->dev, "No EPHY reset control found %d\n",
461 -                               ret);
462 -                       return -EINVAL;
463 -               }
464 -       } else {
465 -               dev_info(&pdev->dev, "Will use external PHY\n");
466 -               gmac->use_internal_phy = false;
467 -       }
468  
469         /* platform data specifying hardware features and callbacks.
470          * hardware features were copied from Allwinner drivers.
471 @@ -976,9 +1044,34 @@ static int sun8i_dwmac_probe(struct plat
472  
473         ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
474         if (ret)
475 -               sun8i_dwmac_exit(pdev, plat_dat->bsp_priv);
476 +               goto dwmac_exit;
477 +
478 +       ndev = dev_get_drvdata(&pdev->dev);
479 +       priv = netdev_priv(ndev);
480 +       /* The mux must be registered after parent MDIO
481 +        * so after stmmac_dvr_probe()
482 +        */
483 +       if (gmac->variant->soc_has_internal_phy) {
484 +               ret = get_ephy_nodes(priv);
485 +               if (ret)
486 +                       goto dwmac_exit;
487 +               ret = sun8i_dwmac_register_mdio_mux(priv);
488 +               if (ret) {
489 +                       dev_err(&pdev->dev, "Failed to register mux\n");
490 +                       goto dwmac_mux;
491 +               }
492 +       } else {
493 +               ret = sun8i_dwmac_reset(priv);
494 +               if (ret)
495 +                       goto dwmac_exit;
496 +       }
497  
498         return ret;
499 +dwmac_mux:
500 +       sun8i_dwmac_unset_syscon(gmac);
501 +dwmac_exit:
502 +       sun8i_dwmac_exit(pdev, plat_dat->bsp_priv);
503 +return ret;
504  }
505  
506  static const struct of_device_id sun8i_dwmac_match[] = {