aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSergei Shtylyov <sergei.shtylyov@cogentembedded.com>2017-12-04 07:35:05 -0500
committerDavid S. Miller <davem@davemloft.net>2017-12-05 12:51:19 -0500
commitbafbdd527d569c8200521f2f7579f65a044271be (patch)
tree51db9962c079c2ca76a5e9f6d3cb2dc222999c37
parent62b32379fd124fea521484ba7e220d8a449f0b59 (diff)
phylib: Add device reset GPIO support
The PHY devices sometimes do have their reset signal (maybe even power supply?) tied to some GPIO and sometimes it also does happen that a boot loader does not leave it deasserted. So far this issue has been attacked from (as I believe) a wrong angle: by teaching the MAC driver to manipulate the GPIO in question; that solution, when applied to the device trees, led to adding the PHY reset GPIO properties to the MAC device node, with one exception: Cadence MACB driver which could handle the "reset-gpios" prop in a PHY device subnode. I believe that the correct approach is to teach the 'phylib' to get the MDIO device reset GPIO from the device tree node corresponding to this device -- which this patch is doing... Note that I had to modify the AT803x PHY driver as it would stop working otherwise -- it made use of the reset GPIO for its own purposes... Signed-off-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com> Acked-by: Rob Herring <robh@kernel.org> [geert: Propagate actual errors from fwnode_get_named_gpiod()] [geert: Avoid destroying initial setup] [geert: Consolidate GPIO descriptor acquiring code] Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be> Tested-by: Richard Leitner <richard.leitner@skidata.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--Documentation/devicetree/bindings/net/phy.txt2
-rw-r--r--drivers/net/phy/at803x.c18
-rw-r--r--drivers/net/phy/mdio_bus.c21
-rw-r--r--drivers/net/phy/mdio_device.c25
-rw-r--r--drivers/net/phy/phy_device.c32
-rw-r--r--include/linux/mdio.h3
-rw-r--r--include/linux/phy.h5
7 files changed, 87 insertions, 19 deletions
diff --git a/Documentation/devicetree/bindings/net/phy.txt b/Documentation/devicetree/bindings/net/phy.txt
index 77d0b2a61ffa..c05479f5ac7c 100644
--- a/Documentation/devicetree/bindings/net/phy.txt
+++ b/Documentation/devicetree/bindings/net/phy.txt
@@ -53,6 +53,8 @@ Optional Properties:
53 to ensure the integrated PHY is used. The absence of this property indicates 53 to ensure the integrated PHY is used. The absence of this property indicates
54 the muxers should be configured so that the external PHY is used. 54 the muxers should be configured so that the external PHY is used.
55 55
56- reset-gpios: The GPIO phandle and specifier for the PHY reset signal.
57
56Example: 58Example:
57 59
58ethernet-phy@0 { 60ethernet-phy@0 {
diff --git a/drivers/net/phy/at803x.c b/drivers/net/phy/at803x.c
index de7dd6566df7..29da7a3c7a37 100644
--- a/drivers/net/phy/at803x.c
+++ b/drivers/net/phy/at803x.c
@@ -71,7 +71,6 @@ MODULE_LICENSE("GPL");
71 71
72struct at803x_priv { 72struct at803x_priv {
73 bool phy_reset:1; 73 bool phy_reset:1;
74 struct gpio_desc *gpiod_reset;
75}; 74};
76 75
77struct at803x_context { 76struct at803x_context {
@@ -254,22 +253,11 @@ static int at803x_probe(struct phy_device *phydev)
254{ 253{
255 struct device *dev = &phydev->mdio.dev; 254 struct device *dev = &phydev->mdio.dev;
256 struct at803x_priv *priv; 255 struct at803x_priv *priv;
257 struct gpio_desc *gpiod_reset;
258 256
259 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 257 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
260 if (!priv) 258 if (!priv)
261 return -ENOMEM; 259 return -ENOMEM;
262 260
263 if (phydev->drv->phy_id != ATH8030_PHY_ID)
264 goto does_not_require_reset_workaround;
265
266 gpiod_reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
267 if (IS_ERR(gpiod_reset))
268 return PTR_ERR(gpiod_reset);
269
270 priv->gpiod_reset = gpiod_reset;
271
272does_not_require_reset_workaround:
273 phydev->priv = priv; 261 phydev->priv = priv;
274 262
275 return 0; 263 return 0;
@@ -343,14 +331,14 @@ static void at803x_link_change_notify(struct phy_device *phydev)
343 * cannot recover from by software. 331 * cannot recover from by software.
344 */ 332 */
345 if (phydev->state == PHY_NOLINK) { 333 if (phydev->state == PHY_NOLINK) {
346 if (priv->gpiod_reset && !priv->phy_reset) { 334 if (phydev->mdio.reset && !priv->phy_reset) {
347 struct at803x_context context; 335 struct at803x_context context;
348 336
349 at803x_context_save(phydev, &context); 337 at803x_context_save(phydev, &context);
350 338
351 gpiod_set_value(priv->gpiod_reset, 1); 339 phy_device_reset(phydev, 1);
352 msleep(1); 340 msleep(1);
353 gpiod_set_value(priv->gpiod_reset, 0); 341 phy_device_reset(phydev, 0);
354 msleep(1); 342 msleep(1);
355 343
356 at803x_context_restore(phydev, &context); 344 at803x_context_restore(phydev, &context);
diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c
index 2df7b62c1a36..8f8b7747c54b 100644
--- a/drivers/net/phy/mdio_bus.c
+++ b/drivers/net/phy/mdio_bus.c
@@ -38,6 +38,7 @@
38#include <linux/phy.h> 38#include <linux/phy.h>
39#include <linux/io.h> 39#include <linux/io.h>
40#include <linux/uaccess.h> 40#include <linux/uaccess.h>
41#include <linux/gpio/consumer.h>
41 42
42#include <asm/irq.h> 43#include <asm/irq.h>
43 44
@@ -48,9 +49,26 @@
48 49
49int mdiobus_register_device(struct mdio_device *mdiodev) 50int mdiobus_register_device(struct mdio_device *mdiodev)
50{ 51{
52 struct gpio_desc *gpiod = NULL;
53
51 if (mdiodev->bus->mdio_map[mdiodev->addr]) 54 if (mdiodev->bus->mdio_map[mdiodev->addr])
52 return -EBUSY; 55 return -EBUSY;
53 56
57 /* Deassert the optional reset signal */
58 if (mdiodev->dev.of_node)
59 gpiod = fwnode_get_named_gpiod(&mdiodev->dev.of_node->fwnode,
60 "reset-gpios", 0, GPIOD_OUT_LOW,
61 "PHY reset");
62 if (PTR_ERR(gpiod) == -ENOENT)
63 gpiod = NULL;
64 else if (IS_ERR(gpiod))
65 return PTR_ERR(gpiod);
66
67 mdiodev->reset = gpiod;
68
69 /* Assert the reset signal again */
70 mdio_device_reset(mdiodev, 1);
71
54 mdiodev->bus->mdio_map[mdiodev->addr] = mdiodev; 72 mdiodev->bus->mdio_map[mdiodev->addr] = mdiodev;
55 73
56 return 0; 74 return 0;
@@ -420,6 +438,9 @@ void mdiobus_unregister(struct mii_bus *bus)
420 if (!mdiodev) 438 if (!mdiodev)
421 continue; 439 continue;
422 440
441 if (mdiodev->reset)
442 gpiod_put(mdiodev->reset);
443
423 mdiodev->device_remove(mdiodev); 444 mdiodev->device_remove(mdiodev);
424 mdiodev->device_free(mdiodev); 445 mdiodev->device_free(mdiodev);
425 } 446 }
diff --git a/drivers/net/phy/mdio_device.c b/drivers/net/phy/mdio_device.c
index e24f28924af8..75d97dd9fb28 100644
--- a/drivers/net/phy/mdio_device.c
+++ b/drivers/net/phy/mdio_device.c
@@ -12,6 +12,8 @@
12#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 12#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
13 13
14#include <linux/errno.h> 14#include <linux/errno.h>
15#include <linux/gpio.h>
16#include <linux/gpio/consumer.h>
15#include <linux/init.h> 17#include <linux/init.h>
16#include <linux/interrupt.h> 18#include <linux/interrupt.h>
17#include <linux/kernel.h> 19#include <linux/kernel.h>
@@ -114,6 +116,13 @@ void mdio_device_remove(struct mdio_device *mdiodev)
114} 116}
115EXPORT_SYMBOL(mdio_device_remove); 117EXPORT_SYMBOL(mdio_device_remove);
116 118
119void mdio_device_reset(struct mdio_device *mdiodev, int value)
120{
121 if (mdiodev->reset)
122 gpiod_set_value(mdiodev->reset, value);
123}
124EXPORT_SYMBOL(mdio_device_reset);
125
117/** 126/**
118 * mdio_probe - probe an MDIO device 127 * mdio_probe - probe an MDIO device
119 * @dev: device to probe 128 * @dev: device to probe
@@ -128,8 +137,16 @@ static int mdio_probe(struct device *dev)
128 struct mdio_driver *mdiodrv = to_mdio_driver(drv); 137 struct mdio_driver *mdiodrv = to_mdio_driver(drv);
129 int err = 0; 138 int err = 0;
130 139
131 if (mdiodrv->probe) 140 if (mdiodrv->probe) {
141 /* Deassert the reset signal */
142 mdio_device_reset(mdiodev, 0);
143
132 err = mdiodrv->probe(mdiodev); 144 err = mdiodrv->probe(mdiodev);
145 if (err) {
146 /* Assert the reset signal */
147 mdio_device_reset(mdiodev, 1);
148 }
149 }
133 150
134 return err; 151 return err;
135} 152}
@@ -140,9 +157,13 @@ static int mdio_remove(struct device *dev)
140 struct device_driver *drv = mdiodev->dev.driver; 157 struct device_driver *drv = mdiodev->dev.driver;
141 struct mdio_driver *mdiodrv = to_mdio_driver(drv); 158 struct mdio_driver *mdiodrv = to_mdio_driver(drv);
142 159
143 if (mdiodrv->remove) 160 if (mdiodrv->remove) {
144 mdiodrv->remove(mdiodev); 161 mdiodrv->remove(mdiodev);
145 162
163 /* Assert the reset signal */
164 mdio_device_reset(mdiodev, 1);
165 }
166
146 return 0; 167 return 0;
147} 168}
148 169
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 8154fb706751..1de5e242b8b4 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -632,6 +632,9 @@ int phy_device_register(struct phy_device *phydev)
632 if (err) 632 if (err)
633 return err; 633 return err;
634 634
635 /* Deassert the reset signal */
636 phy_device_reset(phydev, 0);
637
635 /* Run all of the fixups for this PHY */ 638 /* Run all of the fixups for this PHY */
636 err = phy_scan_fixups(phydev); 639 err = phy_scan_fixups(phydev);
637 if (err) { 640 if (err) {
@@ -650,6 +653,9 @@ int phy_device_register(struct phy_device *phydev)
650 return 0; 653 return 0;
651 654
652 out: 655 out:
656 /* Assert the reset signal */
657 phy_device_reset(phydev, 1);
658
653 mdiobus_unregister_device(&phydev->mdio); 659 mdiobus_unregister_device(&phydev->mdio);
654 return err; 660 return err;
655} 661}
@@ -666,6 +672,10 @@ EXPORT_SYMBOL(phy_device_register);
666void phy_device_remove(struct phy_device *phydev) 672void phy_device_remove(struct phy_device *phydev)
667{ 673{
668 device_del(&phydev->mdio.dev); 674 device_del(&phydev->mdio.dev);
675
676 /* Assert the reset signal */
677 phy_device_reset(phydev, 1);
678
669 mdiobus_unregister_device(&phydev->mdio); 679 mdiobus_unregister_device(&phydev->mdio);
670} 680}
671EXPORT_SYMBOL(phy_device_remove); 681EXPORT_SYMBOL(phy_device_remove);
@@ -849,6 +859,9 @@ int phy_init_hw(struct phy_device *phydev)
849{ 859{
850 int ret = 0; 860 int ret = 0;
851 861
862 /* Deassert the reset signal */
863 phy_device_reset(phydev, 0);
864
852 if (!phydev->drv || !phydev->drv->config_init) 865 if (!phydev->drv || !phydev->drv->config_init)
853 return 0; 866 return 0;
854 867
@@ -1126,6 +1139,9 @@ void phy_detach(struct phy_device *phydev)
1126 put_device(&phydev->mdio.dev); 1139 put_device(&phydev->mdio.dev);
1127 if (ndev_owner != bus->owner) 1140 if (ndev_owner != bus->owner)
1128 module_put(bus->owner); 1141 module_put(bus->owner);
1142
1143 /* Assert the reset signal */
1144 phy_device_reset(phydev, 1);
1129} 1145}
1130EXPORT_SYMBOL(phy_detach); 1146EXPORT_SYMBOL(phy_detach);
1131 1147
@@ -1811,8 +1827,16 @@ static int phy_probe(struct device *dev)
1811 /* Set the state to READY by default */ 1827 /* Set the state to READY by default */
1812 phydev->state = PHY_READY; 1828 phydev->state = PHY_READY;
1813 1829
1814 if (phydev->drv->probe) 1830 if (phydev->drv->probe) {
1831 /* Deassert the reset signal */
1832 phy_device_reset(phydev, 0);
1833
1815 err = phydev->drv->probe(phydev); 1834 err = phydev->drv->probe(phydev);
1835 if (err) {
1836 /* Assert the reset signal */
1837 phy_device_reset(phydev, 1);
1838 }
1839 }
1816 1840
1817 mutex_unlock(&phydev->lock); 1841 mutex_unlock(&phydev->lock);
1818 1842
@@ -1829,8 +1853,12 @@ static int phy_remove(struct device *dev)
1829 phydev->state = PHY_DOWN; 1853 phydev->state = PHY_DOWN;
1830 mutex_unlock(&phydev->lock); 1854 mutex_unlock(&phydev->lock);
1831 1855
1832 if (phydev->drv && phydev->drv->remove) 1856 if (phydev->drv && phydev->drv->remove) {
1833 phydev->drv->remove(phydev); 1857 phydev->drv->remove(phydev);
1858
1859 /* Assert the reset signal */
1860 phy_device_reset(phydev, 1);
1861 }
1834 phydev->drv = NULL; 1862 phydev->drv = NULL;
1835 1863
1836 return 0; 1864 return 0;
diff --git a/include/linux/mdio.h b/include/linux/mdio.h
index ca08ab16ecdc..92d4e55ffe67 100644
--- a/include/linux/mdio.h
+++ b/include/linux/mdio.h
@@ -12,6 +12,7 @@
12#include <uapi/linux/mdio.h> 12#include <uapi/linux/mdio.h>
13#include <linux/mod_devicetable.h> 13#include <linux/mod_devicetable.h>
14 14
15struct gpio_desc;
15struct mii_bus; 16struct mii_bus;
16 17
17/* Multiple levels of nesting are possible. However typically this is 18/* Multiple levels of nesting are possible. However typically this is
@@ -39,6 +40,7 @@ struct mdio_device {
39 /* Bus address of the MDIO device (0-31) */ 40 /* Bus address of the MDIO device (0-31) */
40 int addr; 41 int addr;
41 int flags; 42 int flags;
43 struct gpio_desc *reset;
42}; 44};
43#define to_mdio_device(d) container_of(d, struct mdio_device, dev) 45#define to_mdio_device(d) container_of(d, struct mdio_device, dev)
44 46
@@ -71,6 +73,7 @@ void mdio_device_free(struct mdio_device *mdiodev);
71struct mdio_device *mdio_device_create(struct mii_bus *bus, int addr); 73struct mdio_device *mdio_device_create(struct mii_bus *bus, int addr);
72int mdio_device_register(struct mdio_device *mdiodev); 74int mdio_device_register(struct mdio_device *mdiodev);
73void mdio_device_remove(struct mdio_device *mdiodev); 75void mdio_device_remove(struct mdio_device *mdiodev);
76void mdio_device_reset(struct mdio_device *mdiodev, int value);
74int mdio_driver_register(struct mdio_driver *drv); 77int mdio_driver_register(struct mdio_driver *drv);
75void mdio_driver_unregister(struct mdio_driver *drv); 78void mdio_driver_unregister(struct mdio_driver *drv);
76int mdio_device_bus_match(struct device *dev, struct device_driver *drv); 79int mdio_device_bus_match(struct device *dev, struct device_driver *drv);
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 7570cb838410..d3037e2ffbc4 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -854,6 +854,11 @@ int phy_aneg_done(struct phy_device *phydev);
854int phy_stop_interrupts(struct phy_device *phydev); 854int phy_stop_interrupts(struct phy_device *phydev);
855int phy_restart_aneg(struct phy_device *phydev); 855int phy_restart_aneg(struct phy_device *phydev);
856 856
857static inline void phy_device_reset(struct phy_device *phydev, int value)
858{
859 mdio_device_reset(&phydev->mdio, value);
860}
861
857#define phydev_err(_phydev, format, args...) \ 862#define phydev_err(_phydev, format, args...) \
858 dev_err(&_phydev->mdio.dev, format, ##args) 863 dev_err(&_phydev->mdio.dev, format, ##args)
859 864