diff options
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/phy/mdio_bus.c | 47 |
1 files changed, 47 insertions, 0 deletions
diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 5a214f3b8671..a898e5c4ef1b 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -22,8 +22,11 @@ #include <linux/init.h> #include <linux/delay.h> #include <linux/device.h> +#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/of_device.h> #include <linux/of_mdio.h> +#include <linux/of_gpio.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/skbuff.h> @@ -337,6 +340,7 @@ int __mdiobus_register(struct mii_bus *bus, struct module *owner) { struct mdio_device *mdiodev; int i, err; + struct gpio_desc *gpiod; if (NULL == bus || NULL == bus->name || NULL == bus->read || NULL == bus->write) @@ -363,6 +367,35 @@ int __mdiobus_register(struct mii_bus *bus, struct module *owner) if (bus->reset) bus->reset(bus); + /* de-assert bus level PHY GPIO resets */ + if (bus->num_reset_gpios > 0) { + bus->reset_gpiod = devm_kcalloc(&bus->dev, + bus->num_reset_gpios, + sizeof(struct gpio_desc *), + GFP_KERNEL); + if (!bus->reset_gpiod) + return -ENOMEM; + } + + for (i = 0; i < bus->num_reset_gpios; i++) { + gpiod = devm_gpiod_get_index(&bus->dev, "reset", i, + GPIOD_OUT_LOW); + if (IS_ERR(gpiod)) { + err = PTR_ERR(gpiod); + if (err != -ENOENT) { + dev_err(&bus->dev, + "mii_bus %s couldn't get reset GPIO\n", + bus->id); + return err; + } + } else { + bus->reset_gpiod[i] = gpiod; + gpiod_set_value_cansleep(gpiod, 1); + udelay(bus->reset_delay_us); + gpiod_set_value_cansleep(gpiod, 0); + } + } + for (i = 0; i < PHY_MAX_ADDR; i++) { if ((bus->phy_mask & (1 << i)) == 0) { struct phy_device *phydev; @@ -390,6 +423,13 @@ error: mdiodev->device_remove(mdiodev); mdiodev->device_free(mdiodev); } + + /* Put PHYs in RESET to save power */ + for (i = 0; i < bus->num_reset_gpios; i++) { + if (bus->reset_gpiod[i]) + gpiod_set_value_cansleep(bus->reset_gpiod[i], 1); + } + device_del(&bus->dev); return err; } @@ -411,6 +451,13 @@ void mdiobus_unregister(struct mii_bus *bus) mdiodev->device_remove(mdiodev); mdiodev->device_free(mdiodev); } + + /* Put PHYs in RESET to save power */ + for (i = 0; i < bus->num_reset_gpios; i++) { + if (bus->reset_gpiod[i]) + gpiod_set_value_cansleep(bus->reset_gpiod[i], 1); + } + device_del(&bus->dev); } EXPORT_SYMBOL(mdiobus_unregister); |