sunxi: backport stmmac network patches
[openwrt/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 @@ -634,6 +640,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 @@ -648,35 +807,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 @@ -746,81 +895,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 @@ -849,7 +938,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 @@ -889,6 +978,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 @@ -932,29 +1023,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 @@ -973,9 +1041,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[] = {