Commit bafbdd52 authored by Sergei Shtylyov's avatar Sergei Shtylyov Committed by David S. Miller

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: default avatarSergei Shtylyov <sergei.shtylyov@cogentembedded.com>
Acked-by: default avatarRob 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: default avatarGeert Uytterhoeven <geert+renesas@glider.be>
Tested-by: default avatarRichard Leitner <richard.leitner@skidata.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 62b32379
...@@ -53,6 +53,8 @@ Optional Properties: ...@@ -53,6 +53,8 @@ Optional Properties:
to ensure the integrated PHY is used. The absence of this property indicates to ensure the integrated PHY is used. The absence of this property indicates
the muxers should be configured so that the external PHY is used. the muxers should be configured so that the external PHY is used.
- reset-gpios: The GPIO phandle and specifier for the PHY reset signal.
Example: Example:
ethernet-phy@0 { ethernet-phy@0 {
......
...@@ -71,7 +71,6 @@ MODULE_LICENSE("GPL"); ...@@ -71,7 +71,6 @@ MODULE_LICENSE("GPL");
struct at803x_priv { struct at803x_priv {
bool phy_reset:1; bool phy_reset:1;
struct gpio_desc *gpiod_reset;
}; };
struct at803x_context { struct at803x_context {
...@@ -254,22 +253,11 @@ static int at803x_probe(struct phy_device *phydev) ...@@ -254,22 +253,11 @@ static int at803x_probe(struct phy_device *phydev)
{ {
struct device *dev = &phydev->mdio.dev; struct device *dev = &phydev->mdio.dev;
struct at803x_priv *priv; struct at803x_priv *priv;
struct gpio_desc *gpiod_reset;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv) if (!priv)
return -ENOMEM; return -ENOMEM;
if (phydev->drv->phy_id != ATH8030_PHY_ID)
goto does_not_require_reset_workaround;
gpiod_reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(gpiod_reset))
return PTR_ERR(gpiod_reset);
priv->gpiod_reset = gpiod_reset;
does_not_require_reset_workaround:
phydev->priv = priv; phydev->priv = priv;
return 0; return 0;
...@@ -343,14 +331,14 @@ static void at803x_link_change_notify(struct phy_device *phydev) ...@@ -343,14 +331,14 @@ static void at803x_link_change_notify(struct phy_device *phydev)
* cannot recover from by software. * cannot recover from by software.
*/ */
if (phydev->state == PHY_NOLINK) { if (phydev->state == PHY_NOLINK) {
if (priv->gpiod_reset && !priv->phy_reset) { if (phydev->mdio.reset && !priv->phy_reset) {
struct at803x_context context; struct at803x_context context;
at803x_context_save(phydev, &context); at803x_context_save(phydev, &context);
gpiod_set_value(priv->gpiod_reset, 1); phy_device_reset(phydev, 1);
msleep(1); msleep(1);
gpiod_set_value(priv->gpiod_reset, 0); phy_device_reset(phydev, 0);
msleep(1); msleep(1);
at803x_context_restore(phydev, &context); at803x_context_restore(phydev, &context);
......
...@@ -38,6 +38,7 @@ ...@@ -38,6 +38,7 @@
#include <linux/phy.h> #include <linux/phy.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/gpio/consumer.h>
#include <asm/irq.h> #include <asm/irq.h>
...@@ -48,9 +49,26 @@ ...@@ -48,9 +49,26 @@
int mdiobus_register_device(struct mdio_device *mdiodev) int mdiobus_register_device(struct mdio_device *mdiodev)
{ {
struct gpio_desc *gpiod = NULL;
if (mdiodev->bus->mdio_map[mdiodev->addr]) if (mdiodev->bus->mdio_map[mdiodev->addr])
return -EBUSY; return -EBUSY;
/* Deassert the optional reset signal */
if (mdiodev->dev.of_node)
gpiod = fwnode_get_named_gpiod(&mdiodev->dev.of_node->fwnode,
"reset-gpios", 0, GPIOD_OUT_LOW,
"PHY reset");
if (PTR_ERR(gpiod) == -ENOENT)
gpiod = NULL;
else if (IS_ERR(gpiod))
return PTR_ERR(gpiod);
mdiodev->reset = gpiod;
/* Assert the reset signal again */
mdio_device_reset(mdiodev, 1);
mdiodev->bus->mdio_map[mdiodev->addr] = mdiodev; mdiodev->bus->mdio_map[mdiodev->addr] = mdiodev;
return 0; return 0;
...@@ -420,6 +438,9 @@ void mdiobus_unregister(struct mii_bus *bus) ...@@ -420,6 +438,9 @@ void mdiobus_unregister(struct mii_bus *bus)
if (!mdiodev) if (!mdiodev)
continue; continue;
if (mdiodev->reset)
gpiod_put(mdiodev->reset);
mdiodev->device_remove(mdiodev); mdiodev->device_remove(mdiodev);
mdiodev->device_free(mdiodev); mdiodev->device_free(mdiodev);
} }
......
...@@ -12,6 +12,8 @@ ...@@ -12,6 +12,8 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/kernel.h> #include <linux/kernel.h>
...@@ -114,6 +116,13 @@ void mdio_device_remove(struct mdio_device *mdiodev) ...@@ -114,6 +116,13 @@ void mdio_device_remove(struct mdio_device *mdiodev)
} }
EXPORT_SYMBOL(mdio_device_remove); EXPORT_SYMBOL(mdio_device_remove);
void mdio_device_reset(struct mdio_device *mdiodev, int value)
{
if (mdiodev->reset)
gpiod_set_value(mdiodev->reset, value);
}
EXPORT_SYMBOL(mdio_device_reset);
/** /**
* mdio_probe - probe an MDIO device * mdio_probe - probe an MDIO device
* @dev: device to probe * @dev: device to probe
...@@ -128,8 +137,16 @@ static int mdio_probe(struct device *dev) ...@@ -128,8 +137,16 @@ static int mdio_probe(struct device *dev)
struct mdio_driver *mdiodrv = to_mdio_driver(drv); struct mdio_driver *mdiodrv = to_mdio_driver(drv);
int err = 0; int err = 0;
if (mdiodrv->probe) if (mdiodrv->probe) {
/* Deassert the reset signal */
mdio_device_reset(mdiodev, 0);
err = mdiodrv->probe(mdiodev); err = mdiodrv->probe(mdiodev);
if (err) {
/* Assert the reset signal */
mdio_device_reset(mdiodev, 1);
}
}
return err; return err;
} }
...@@ -140,9 +157,13 @@ static int mdio_remove(struct device *dev) ...@@ -140,9 +157,13 @@ static int mdio_remove(struct device *dev)
struct device_driver *drv = mdiodev->dev.driver; struct device_driver *drv = mdiodev->dev.driver;
struct mdio_driver *mdiodrv = to_mdio_driver(drv); struct mdio_driver *mdiodrv = to_mdio_driver(drv);
if (mdiodrv->remove) if (mdiodrv->remove) {
mdiodrv->remove(mdiodev); mdiodrv->remove(mdiodev);
/* Assert the reset signal */
mdio_device_reset(mdiodev, 1);
}
return 0; return 0;
} }
......
...@@ -632,6 +632,9 @@ int phy_device_register(struct phy_device *phydev) ...@@ -632,6 +632,9 @@ int phy_device_register(struct phy_device *phydev)
if (err) if (err)
return err; return err;
/* Deassert the reset signal */
phy_device_reset(phydev, 0);
/* Run all of the fixups for this PHY */ /* Run all of the fixups for this PHY */
err = phy_scan_fixups(phydev); err = phy_scan_fixups(phydev);
if (err) { if (err) {
...@@ -650,6 +653,9 @@ int phy_device_register(struct phy_device *phydev) ...@@ -650,6 +653,9 @@ int phy_device_register(struct phy_device *phydev)
return 0; return 0;
out: out:
/* Assert the reset signal */
phy_device_reset(phydev, 1);
mdiobus_unregister_device(&phydev->mdio); mdiobus_unregister_device(&phydev->mdio);
return err; return err;
} }
...@@ -666,6 +672,10 @@ EXPORT_SYMBOL(phy_device_register); ...@@ -666,6 +672,10 @@ EXPORT_SYMBOL(phy_device_register);
void phy_device_remove(struct phy_device *phydev) void phy_device_remove(struct phy_device *phydev)
{ {
device_del(&phydev->mdio.dev); device_del(&phydev->mdio.dev);
/* Assert the reset signal */
phy_device_reset(phydev, 1);
mdiobus_unregister_device(&phydev->mdio); mdiobus_unregister_device(&phydev->mdio);
} }
EXPORT_SYMBOL(phy_device_remove); EXPORT_SYMBOL(phy_device_remove);
...@@ -849,6 +859,9 @@ int phy_init_hw(struct phy_device *phydev) ...@@ -849,6 +859,9 @@ int phy_init_hw(struct phy_device *phydev)
{ {
int ret = 0; int ret = 0;
/* Deassert the reset signal */
phy_device_reset(phydev, 0);
if (!phydev->drv || !phydev->drv->config_init) if (!phydev->drv || !phydev->drv->config_init)
return 0; return 0;
...@@ -1126,6 +1139,9 @@ void phy_detach(struct phy_device *phydev) ...@@ -1126,6 +1139,9 @@ void phy_detach(struct phy_device *phydev)
put_device(&phydev->mdio.dev); put_device(&phydev->mdio.dev);
if (ndev_owner != bus->owner) if (ndev_owner != bus->owner)
module_put(bus->owner); module_put(bus->owner);
/* Assert the reset signal */
phy_device_reset(phydev, 1);
} }
EXPORT_SYMBOL(phy_detach); EXPORT_SYMBOL(phy_detach);
...@@ -1811,8 +1827,16 @@ static int phy_probe(struct device *dev) ...@@ -1811,8 +1827,16 @@ static int phy_probe(struct device *dev)
/* Set the state to READY by default */ /* Set the state to READY by default */
phydev->state = PHY_READY; phydev->state = PHY_READY;
if (phydev->drv->probe) if (phydev->drv->probe) {
/* Deassert the reset signal */
phy_device_reset(phydev, 0);
err = phydev->drv->probe(phydev); err = phydev->drv->probe(phydev);
if (err) {
/* Assert the reset signal */
phy_device_reset(phydev, 1);
}
}
mutex_unlock(&phydev->lock); mutex_unlock(&phydev->lock);
...@@ -1829,8 +1853,12 @@ static int phy_remove(struct device *dev) ...@@ -1829,8 +1853,12 @@ static int phy_remove(struct device *dev)
phydev->state = PHY_DOWN; phydev->state = PHY_DOWN;
mutex_unlock(&phydev->lock); mutex_unlock(&phydev->lock);
if (phydev->drv && phydev->drv->remove) if (phydev->drv && phydev->drv->remove) {
phydev->drv->remove(phydev); phydev->drv->remove(phydev);
/* Assert the reset signal */
phy_device_reset(phydev, 1);
}
phydev->drv = NULL; phydev->drv = NULL;
return 0; return 0;
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <uapi/linux/mdio.h> #include <uapi/linux/mdio.h>
#include <linux/mod_devicetable.h> #include <linux/mod_devicetable.h>
struct gpio_desc;
struct mii_bus; struct mii_bus;
/* Multiple levels of nesting are possible. However typically this is /* Multiple levels of nesting are possible. However typically this is
...@@ -39,6 +40,7 @@ struct mdio_device { ...@@ -39,6 +40,7 @@ struct mdio_device {
/* Bus address of the MDIO device (0-31) */ /* Bus address of the MDIO device (0-31) */
int addr; int addr;
int flags; int flags;
struct gpio_desc *reset;
}; };
#define to_mdio_device(d) container_of(d, struct mdio_device, dev) #define to_mdio_device(d) container_of(d, struct mdio_device, dev)
...@@ -71,6 +73,7 @@ void mdio_device_free(struct mdio_device *mdiodev); ...@@ -71,6 +73,7 @@ void mdio_device_free(struct mdio_device *mdiodev);
struct mdio_device *mdio_device_create(struct mii_bus *bus, int addr); struct mdio_device *mdio_device_create(struct mii_bus *bus, int addr);
int mdio_device_register(struct mdio_device *mdiodev); int mdio_device_register(struct mdio_device *mdiodev);
void mdio_device_remove(struct mdio_device *mdiodev); void mdio_device_remove(struct mdio_device *mdiodev);
void mdio_device_reset(struct mdio_device *mdiodev, int value);
int mdio_driver_register(struct mdio_driver *drv); int mdio_driver_register(struct mdio_driver *drv);
void mdio_driver_unregister(struct mdio_driver *drv); void mdio_driver_unregister(struct mdio_driver *drv);
int mdio_device_bus_match(struct device *dev, struct device_driver *drv); int mdio_device_bus_match(struct device *dev, struct device_driver *drv);
......
...@@ -854,6 +854,11 @@ int phy_aneg_done(struct phy_device *phydev); ...@@ -854,6 +854,11 @@ int phy_aneg_done(struct phy_device *phydev);
int phy_stop_interrupts(struct phy_device *phydev); int phy_stop_interrupts(struct phy_device *phydev);
int phy_restart_aneg(struct phy_device *phydev); int phy_restart_aneg(struct phy_device *phydev);
static inline void phy_device_reset(struct phy_device *phydev, int value)
{
mdio_device_reset(&phydev->mdio, value);
}
#define phydev_err(_phydev, format, args...) \ #define phydev_err(_phydev, format, args...) \
dev_err(&_phydev->mdio.dev, format, ##args) dev_err(&_phydev->mdio.dev, format, ##args)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment