On Fri, Apr 29, 2016 at 01:12:54AM +0300, Sergei Shtylyov wrote: > 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 as it made use of the reset GPIO for its own purposes... > > Signed-off-by: Sergei Shtylyov <sergei.shtyl...@cogentembedded.com> > > --- > Changes in version 2: > - reformatted the changelog; > - resolved rejects, refreshed the patch. > > Documentation/devicetree/bindings/net/phy.txt | 2 + > Documentation/devicetree/bindings/net/phy.txt | 2 +
Acked-by: Rob Herring <r...@kernel.org> > drivers/net/phy/at803x.c | 19 ++------------ > drivers/net/phy/mdio_bus.c | 4 +++ > drivers/net/phy/mdio_device.c | 27 +++++++++++++++++++-- > drivers/net/phy/phy_device.c | 33 > ++++++++++++++++++++++++-- > drivers/of/of_mdio.c | 16 ++++++++++++ > include/linux/mdio.h | 3 ++ > include/linux/phy.h | 5 +++ > 8 files changed, 89 insertions(+), 20 deletions(-) > > Index: net-next/Documentation/devicetree/bindings/net/phy.txt > =================================================================== > --- net-next.orig/Documentation/devicetree/bindings/net/phy.txt > +++ net-next/Documentation/devicetree/bindings/net/phy.txt > @@ -35,6 +35,8 @@ Optional Properties: > - broken-turn-around: If set, indicates the PHY device does not correctly > release the turn around line low at the end of a MDIO transaction. > > +- reset-gpios: The GPIO phandle and specifier for the PHY reset signal. > + > Example: > > ethernet-phy@0 { > Index: net-next/drivers/net/phy/at803x.c > =================================================================== > --- net-next.orig/drivers/net/phy/at803x.c > +++ net-next/drivers/net/phy/at803x.c > @@ -65,7 +65,6 @@ MODULE_LICENSE("GPL"); > > struct at803x_priv { > bool phy_reset:1; > - struct gpio_desc *gpiod_reset; > }; > > struct at803x_context { > @@ -271,22 +270,10 @@ static int at803x_probe(struct phy_devic > { > struct device *dev = &phydev->mdio.dev; > struct at803x_priv *priv; > - struct gpio_desc *gpiod_reset; > > priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); > if (!priv) > 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; > > return 0; > @@ -361,14 +348,14 @@ static void at803x_link_change_notify(st > */ > if (phydev->drv->phy_id == ATH8030_PHY_ID) { > if (phydev->state == PHY_NOLINK) { > - if (priv->gpiod_reset && !priv->phy_reset) { > + if (phydev->mdio.reset && !priv->phy_reset) { > struct at803x_context context; > > at803x_context_save(phydev, &context); > > - gpiod_set_value(priv->gpiod_reset, 1); > + phy_device_reset(phydev, 1); > msleep(1); > - gpiod_set_value(priv->gpiod_reset, 0); > + phy_device_reset(phydev, 0); > msleep(1); > > at803x_context_restore(phydev, &context); > Index: net-next/drivers/net/phy/mdio_bus.c > =================================================================== > --- net-next.orig/drivers/net/phy/mdio_bus.c > +++ net-next/drivers/net/phy/mdio_bus.c > @@ -35,6 +35,7 @@ > #include <linux/phy.h> > #include <linux/io.h> > #include <linux/uaccess.h> > +#include <linux/gpio/consumer.h> > > #include <asm/irq.h> > > @@ -371,6 +372,9 @@ void mdiobus_unregister(struct mii_bus * > if (!mdiodev) > continue; > > + if (mdiodev->reset) > + gpiod_put(mdiodev->reset); > + > mdiodev->device_remove(mdiodev); > mdiodev->device_free(mdiodev); > } > Index: net-next/drivers/net/phy/mdio_device.c > =================================================================== > --- net-next.orig/drivers/net/phy/mdio_device.c > +++ net-next/drivers/net/phy/mdio_device.c > @@ -12,6 +12,8 @@ > #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt > > #include <linux/errno.h> > +#include <linux/gpio.h> > +#include <linux/gpio/consumer.h> > #include <linux/init.h> > #include <linux/interrupt.h> > #include <linux/kernel.h> > @@ -103,6 +105,13 @@ void mdio_device_remove(struct mdio_devi > } > 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 > * @dev: device to probe > @@ -117,9 +126,16 @@ static int mdio_probe(struct device *dev > struct mdio_driver *mdiodrv = to_mdio_driver(drv); > int err = 0; > > - if (mdiodrv->probe) > + if (mdiodrv->probe) { > + /* Deassert the reset signal */ > + mdio_device_reset(mdiodev, 0); > + > err = mdiodrv->probe(mdiodev); > > + /* Assert the reset signal */ > + mdio_device_reset(mdiodev, 1); > + } > + > return err; > } > > @@ -129,9 +145,16 @@ static int mdio_remove(struct device *de > struct device_driver *drv = mdiodev->dev.driver; > struct mdio_driver *mdiodrv = to_mdio_driver(drv); > > - if (mdiodrv->remove) > + if (mdiodrv->remove) { > + /* Deassert the reset signal */ > + mdio_device_reset(mdiodev, 0); > + > mdiodrv->remove(mdiodev); > > + /* Assert the reset signal */ > + mdio_device_reset(mdiodev, 1); > + } > + > return 0; > } > > Index: net-next/drivers/net/phy/phy_device.c > =================================================================== > --- net-next.orig/drivers/net/phy/phy_device.c > +++ net-next/drivers/net/phy/phy_device.c > @@ -589,6 +589,9 @@ int phy_device_register(struct phy_devic > if (err) > return err; > > + /* Deassert the reset signal */ > + phy_device_reset(phydev, 0); > + > /* Run all of the fixups for this PHY */ > err = phy_scan_fixups(phydev); > if (err) { > @@ -604,9 +607,15 @@ int phy_device_register(struct phy_devic > goto out; > } > > + /* Assert the reset signal */ > + phy_device_reset(phydev, 1); > + > return 0; > > out: > + /* Assert the reset signal */ > + phy_device_reset(phydev, 1); > + > mdiobus_unregister_device(&phydev->mdio); > return err; > } > @@ -792,6 +801,9 @@ int phy_init_hw(struct phy_device *phyde > { > int ret = 0; > > + /* Deassert the reset signal */ > + phy_device_reset(phydev, 0); > + > if (!phydev->drv || !phydev->drv->config_init) > return 0; > > @@ -997,6 +1009,9 @@ void phy_detach(struct phy_device *phyde > > put_device(&phydev->mdio.dev); > module_put(bus->owner); > + > + /* Assert the reset signal */ > + phy_device_reset(phydev, 1); > } > EXPORT_SYMBOL(phy_detach); > > @@ -1596,9 +1611,16 @@ static int phy_probe(struct device *dev) > /* Set the state to READY by default */ > 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); > > + /* Assert the reset signal */ > + phy_device_reset(phydev, 1); > + } > + > mutex_unlock(&phydev->lock); > > return err; > @@ -1612,8 +1634,15 @@ static int phy_remove(struct device *dev > phydev->state = PHY_DOWN; > mutex_unlock(&phydev->lock); > > - if (phydev->drv->remove) > + if (phydev->drv->remove) { > + /* Deassert the reset signal */ > + phy_device_reset(phydev, 0); > + > phydev->drv->remove(phydev); > + > + /* Assert the reset signal */ > + phy_device_reset(phydev, 1); > + } > phydev->drv = NULL; > > return 0; > Index: net-next/drivers/of/of_mdio.c > =================================================================== > --- net-next.orig/drivers/of/of_mdio.c > +++ net-next/drivers/of/of_mdio.c > @@ -44,6 +44,7 @@ static int of_get_phy_id(struct device_n > static void of_mdiobus_register_phy(struct mii_bus *mdio, > struct device_node *child, u32 addr) > { > + struct gpio_desc *gpiod; > struct phy_device *phy; > bool is_c45; > int rc; > @@ -52,10 +53,17 @@ static void of_mdiobus_register_phy(stru > is_c45 = of_device_is_compatible(child, > "ethernet-phy-ieee802.3-c45"); > > + gpiod = fwnode_get_named_gpiod(&child->fwnode, "reset-gpios"); > + /* Deassert the reset signal */ > + if (!IS_ERR(gpiod)) > + gpiod_direction_output(gpiod, 0); > if (!is_c45 && !of_get_phy_id(child, &phy_id)) > phy = phy_device_create(mdio, addr, phy_id, 0, NULL); > else > phy = get_phy_device(mdio, addr, is_c45); > + /* Assert the reset signal again */ > + if (!IS_ERR(gpiod)) > + gpiod_set_value(gpiod, 1); > if (IS_ERR(phy)) > return; > > @@ -75,6 +83,9 @@ static void of_mdiobus_register_phy(stru > of_node_get(child); > phy->mdio.dev.of_node = child; > > + if (!IS_ERR(gpiod)) > + phy->mdio.reset = gpiod; > + > /* All data is now stored in the phy struct; > * register it */ > rc = phy_device_register(phy); > @@ -92,6 +103,7 @@ static void of_mdiobus_register_device(s > struct device_node *child, u32 addr) > { > struct mdio_device *mdiodev; > + struct gpio_desc *gpiod; > int rc; > > mdiodev = mdio_device_create(mdio, addr); > @@ -104,6 +116,10 @@ static void of_mdiobus_register_device(s > of_node_get(child); > mdiodev->dev.of_node = child; > > + gpiod = fwnode_get_named_gpiod(&child->fwnode, "reset-gpios"); > + if (!IS_ERR(gpiod)) > + mdiodev->reset = gpiod; > + > /* All data is now stored in the mdiodev struct; register it. */ > rc = mdio_device_register(mdiodev); > if (rc) { > Index: net-next/include/linux/mdio.h > =================================================================== > --- net-next.orig/include/linux/mdio.h > +++ net-next/include/linux/mdio.h > @@ -11,6 +11,7 @@ > > #include <uapi/linux/mdio.h> > > +struct gpio_desc; > struct mii_bus; > > /* Multiple levels of nesting are possible. However typically this is > @@ -37,6 +38,7 @@ struct mdio_device { > /* Bus address of the MDIO device (0-31) */ > int addr; > int flags; > + struct gpio_desc *reset; > }; > #define to_mdio_device(d) container_of(d, struct mdio_device, dev) > > @@ -69,6 +71,7 @@ void mdio_device_free(struct mdio_device > struct mdio_device *mdio_device_create(struct mii_bus *bus, int addr); > int mdio_device_register(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); > void mdio_driver_unregister(struct mdio_driver *drv); > > Index: net-next/include/linux/phy.h > =================================================================== > --- net-next.orig/include/linux/phy.h > +++ net-next/include/linux/phy.h > @@ -769,6 +769,11 @@ static inline int phy_read_status(struct > return phydev->drv->read_status(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...) \ > dev_err(&_phydev->mdio.dev, format, ##args) > >