diff options
author | Sergei Shtylyov <sergei.shtylyov@cogentembedded.com> | 2017-12-04 07:35:05 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2017-12-05 12:51:19 -0500 |
commit | bafbdd527d569c8200521f2f7579f65a044271be (patch) | |
tree | 51db9962c079c2ca76a5e9f6d3cb2dc222999c37 | |
parent | 62b32379fd124fea521484ba7e220d8a449f0b59 (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.txt | 2 | ||||
-rw-r--r-- | drivers/net/phy/at803x.c | 18 | ||||
-rw-r--r-- | drivers/net/phy/mdio_bus.c | 21 | ||||
-rw-r--r-- | drivers/net/phy/mdio_device.c | 25 | ||||
-rw-r--r-- | drivers/net/phy/phy_device.c | 32 | ||||
-rw-r--r-- | include/linux/mdio.h | 3 | ||||
-rw-r--r-- | include/linux/phy.h | 5 |
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 | |||
56 | Example: | 58 | Example: |
57 | 59 | ||
58 | ethernet-phy@0 { | 60 | ethernet-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 | ||
72 | struct at803x_priv { | 72 | struct at803x_priv { |
73 | bool phy_reset:1; | 73 | bool phy_reset:1; |
74 | struct gpio_desc *gpiod_reset; | ||
75 | }; | 74 | }; |
76 | 75 | ||
77 | struct at803x_context { | 76 | struct 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 | |||
272 | does_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 | ||
49 | int mdiobus_register_device(struct mdio_device *mdiodev) | 50 | int 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 | } |
115 | EXPORT_SYMBOL(mdio_device_remove); | 117 | EXPORT_SYMBOL(mdio_device_remove); |
116 | 118 | ||
119 | void mdio_device_reset(struct mdio_device *mdiodev, int value) | ||
120 | { | ||
121 | if (mdiodev->reset) | ||
122 | gpiod_set_value(mdiodev->reset, value); | ||
123 | } | ||
124 | EXPORT_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); | |||
666 | void phy_device_remove(struct phy_device *phydev) | 672 | void 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 | } |
671 | EXPORT_SYMBOL(phy_device_remove); | 681 | EXPORT_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 | } |
1130 | EXPORT_SYMBOL(phy_detach); | 1146 | EXPORT_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 | ||
15 | struct gpio_desc; | ||
15 | struct mii_bus; | 16 | struct 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); | |||
71 | struct mdio_device *mdio_device_create(struct mii_bus *bus, int addr); | 73 | struct mdio_device *mdio_device_create(struct mii_bus *bus, int addr); |
72 | int mdio_device_register(struct mdio_device *mdiodev); | 74 | int mdio_device_register(struct mdio_device *mdiodev); |
73 | void mdio_device_remove(struct mdio_device *mdiodev); | 75 | void mdio_device_remove(struct mdio_device *mdiodev); |
76 | void mdio_device_reset(struct mdio_device *mdiodev, int value); | ||
74 | int mdio_driver_register(struct mdio_driver *drv); | 77 | int mdio_driver_register(struct mdio_driver *drv); |
75 | void mdio_driver_unregister(struct mdio_driver *drv); | 78 | void mdio_driver_unregister(struct mdio_driver *drv); |
76 | int mdio_device_bus_match(struct device *dev, struct device_driver *drv); | 79 | int 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); | |||
854 | int phy_stop_interrupts(struct phy_device *phydev); | 854 | int phy_stop_interrupts(struct phy_device *phydev); |
855 | int phy_restart_aneg(struct phy_device *phydev); | 855 | int phy_restart_aneg(struct phy_device *phydev); |
856 | 856 | ||
857 | static 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 | ||