Skip to content

Commit bafbdd5

Browse files
Sergei Shtylyovdavem330
authored andcommitted
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 <[email protected]> Acked-by: Rob Herring <[email protected]> [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 <[email protected]> Tested-by: Richard Leitner <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 62b3237 commit bafbdd5

File tree

7 files changed

+87
-19
lines changed

7 files changed

+87
-19
lines changed

Documentation/devicetree/bindings/net/phy.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ Optional Properties:
5353
to ensure the integrated PHY is used. The absence of this property indicates
5454
the muxers should be configured so that the external PHY is used.
5555

56+
- reset-gpios: The GPIO phandle and specifier for the PHY reset signal.
57+
5658
Example:
5759

5860
ethernet-phy@0 {

drivers/net/phy/at803x.c

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@ MODULE_LICENSE("GPL");
7171

7272
struct at803x_priv {
7373
bool phy_reset:1;
74-
struct gpio_desc *gpiod_reset;
7574
};
7675

7776
struct at803x_context {
@@ -254,22 +253,11 @@ static int at803x_probe(struct phy_device *phydev)
254253
{
255254
struct device *dev = &phydev->mdio.dev;
256255
struct at803x_priv *priv;
257-
struct gpio_desc *gpiod_reset;
258256

259257
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
260258
if (!priv)
261259
return -ENOMEM;
262260

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:
273261
phydev->priv = priv;
274262

275263
return 0;
@@ -343,14 +331,14 @@ static void at803x_link_change_notify(struct phy_device *phydev)
343331
* cannot recover from by software.
344332
*/
345333
if (phydev->state == PHY_NOLINK) {
346-
if (priv->gpiod_reset && !priv->phy_reset) {
334+
if (phydev->mdio.reset && !priv->phy_reset) {
347335
struct at803x_context context;
348336

349337
at803x_context_save(phydev, &context);
350338

351-
gpiod_set_value(priv->gpiod_reset, 1);
339+
phy_device_reset(phydev, 1);
352340
msleep(1);
353-
gpiod_set_value(priv->gpiod_reset, 0);
341+
phy_device_reset(phydev, 0);
354342
msleep(1);
355343

356344
at803x_context_restore(phydev, &context);

drivers/net/phy/mdio_bus.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include <linux/phy.h>
3939
#include <linux/io.h>
4040
#include <linux/uaccess.h>
41+
#include <linux/gpio/consumer.h>
4142

4243
#include <asm/irq.h>
4344

@@ -48,9 +49,26 @@
4849

4950
int mdiobus_register_device(struct mdio_device *mdiodev)
5051
{
52+
struct gpio_desc *gpiod = NULL;
53+
5154
if (mdiodev->bus->mdio_map[mdiodev->addr])
5255
return -EBUSY;
5356

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+
5472
mdiodev->bus->mdio_map[mdiodev->addr] = mdiodev;
5573

5674
return 0;
@@ -420,6 +438,9 @@ void mdiobus_unregister(struct mii_bus *bus)
420438
if (!mdiodev)
421439
continue;
422440

441+
if (mdiodev->reset)
442+
gpiod_put(mdiodev->reset);
443+
423444
mdiodev->device_remove(mdiodev);
424445
mdiodev->device_free(mdiodev);
425446
}

drivers/net/phy/mdio_device.c

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1313

1414
#include <linux/errno.h>
15+
#include <linux/gpio.h>
16+
#include <linux/gpio/consumer.h>
1517
#include <linux/init.h>
1618
#include <linux/interrupt.h>
1719
#include <linux/kernel.h>
@@ -114,6 +116,13 @@ void mdio_device_remove(struct mdio_device *mdiodev)
114116
}
115117
EXPORT_SYMBOL(mdio_device_remove);
116118

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+
117126
/**
118127
* mdio_probe - probe an MDIO device
119128
* @dev: device to probe
@@ -128,8 +137,16 @@ static int mdio_probe(struct device *dev)
128137
struct mdio_driver *mdiodrv = to_mdio_driver(drv);
129138
int err = 0;
130139

131-
if (mdiodrv->probe)
140+
if (mdiodrv->probe) {
141+
/* Deassert the reset signal */
142+
mdio_device_reset(mdiodev, 0);
143+
132144
err = mdiodrv->probe(mdiodev);
145+
if (err) {
146+
/* Assert the reset signal */
147+
mdio_device_reset(mdiodev, 1);
148+
}
149+
}
133150

134151
return err;
135152
}
@@ -140,9 +157,13 @@ static int mdio_remove(struct device *dev)
140157
struct device_driver *drv = mdiodev->dev.driver;
141158
struct mdio_driver *mdiodrv = to_mdio_driver(drv);
142159

143-
if (mdiodrv->remove)
160+
if (mdiodrv->remove) {
144161
mdiodrv->remove(mdiodev);
145162

163+
/* Assert the reset signal */
164+
mdio_device_reset(mdiodev, 1);
165+
}
166+
146167
return 0;
147168
}
148169

drivers/net/phy/phy_device.c

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -632,6 +632,9 @@ int phy_device_register(struct phy_device *phydev)
632632
if (err)
633633
return err;
634634

635+
/* Deassert the reset signal */
636+
phy_device_reset(phydev, 0);
637+
635638
/* Run all of the fixups for this PHY */
636639
err = phy_scan_fixups(phydev);
637640
if (err) {
@@ -650,6 +653,9 @@ int phy_device_register(struct phy_device *phydev)
650653
return 0;
651654

652655
out:
656+
/* Assert the reset signal */
657+
phy_device_reset(phydev, 1);
658+
653659
mdiobus_unregister_device(&phydev->mdio);
654660
return err;
655661
}
@@ -666,6 +672,10 @@ EXPORT_SYMBOL(phy_device_register);
666672
void phy_device_remove(struct phy_device *phydev)
667673
{
668674
device_del(&phydev->mdio.dev);
675+
676+
/* Assert the reset signal */
677+
phy_device_reset(phydev, 1);
678+
669679
mdiobus_unregister_device(&phydev->mdio);
670680
}
671681
EXPORT_SYMBOL(phy_device_remove);
@@ -849,6 +859,9 @@ int phy_init_hw(struct phy_device *phydev)
849859
{
850860
int ret = 0;
851861

862+
/* Deassert the reset signal */
863+
phy_device_reset(phydev, 0);
864+
852865
if (!phydev->drv || !phydev->drv->config_init)
853866
return 0;
854867

@@ -1126,6 +1139,9 @@ void phy_detach(struct phy_device *phydev)
11261139
put_device(&phydev->mdio.dev);
11271140
if (ndev_owner != bus->owner)
11281141
module_put(bus->owner);
1142+
1143+
/* Assert the reset signal */
1144+
phy_device_reset(phydev, 1);
11291145
}
11301146
EXPORT_SYMBOL(phy_detach);
11311147

@@ -1811,8 +1827,16 @@ static int phy_probe(struct device *dev)
18111827
/* Set the state to READY by default */
18121828
phydev->state = PHY_READY;
18131829

1814-
if (phydev->drv->probe)
1830+
if (phydev->drv->probe) {
1831+
/* Deassert the reset signal */
1832+
phy_device_reset(phydev, 0);
1833+
18151834
err = phydev->drv->probe(phydev);
1835+
if (err) {
1836+
/* Assert the reset signal */
1837+
phy_device_reset(phydev, 1);
1838+
}
1839+
}
18161840

18171841
mutex_unlock(&phydev->lock);
18181842

@@ -1829,8 +1853,12 @@ static int phy_remove(struct device *dev)
18291853
phydev->state = PHY_DOWN;
18301854
mutex_unlock(&phydev->lock);
18311855

1832-
if (phydev->drv && phydev->drv->remove)
1856+
if (phydev->drv && phydev->drv->remove) {
18331857
phydev->drv->remove(phydev);
1858+
1859+
/* Assert the reset signal */
1860+
phy_device_reset(phydev, 1);
1861+
}
18341862
phydev->drv = NULL;
18351863

18361864
return 0;

include/linux/mdio.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <uapi/linux/mdio.h>
1313
#include <linux/mod_devicetable.h>
1414

15+
struct gpio_desc;
1516
struct mii_bus;
1617

1718
/* Multiple levels of nesting are possible. However typically this is
@@ -39,6 +40,7 @@ struct mdio_device {
3940
/* Bus address of the MDIO device (0-31) */
4041
int addr;
4142
int flags;
43+
struct gpio_desc *reset;
4244
};
4345
#define to_mdio_device(d) container_of(d, struct mdio_device, dev)
4446

@@ -71,6 +73,7 @@ void mdio_device_free(struct mdio_device *mdiodev);
7173
struct mdio_device *mdio_device_create(struct mii_bus *bus, int addr);
7274
int mdio_device_register(struct mdio_device *mdiodev);
7375
void mdio_device_remove(struct mdio_device *mdiodev);
76+
void mdio_device_reset(struct mdio_device *mdiodev, int value);
7477
int mdio_driver_register(struct mdio_driver *drv);
7578
void mdio_driver_unregister(struct mdio_driver *drv);
7679
int mdio_device_bus_match(struct device *dev, struct device_driver *drv);

include/linux/phy.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -854,6 +854,11 @@ int phy_aneg_done(struct phy_device *phydev);
854854
int phy_stop_interrupts(struct phy_device *phydev);
855855
int phy_restart_aneg(struct phy_device *phydev);
856856

857+
static inline void phy_device_reset(struct phy_device *phydev, int value)
858+
{
859+
mdio_device_reset(&phydev->mdio, value);
860+
}
861+
857862
#define phydev_err(_phydev, format, args...) \
858863
dev_err(&_phydev->mdio.dev, format, ##args)
859864

0 commit comments

Comments
 (0)