From: Bartosz Golaszewski <bgolaszewski@xxxxxxxxxxxx> Currently many MAC drivers control the regulator supplying the PHY but this is conceptually wrong. The regulator should be defined as a property of the PHY node on the MDIO bus and controlled by the MDIO sub-system. Add support for an optional PHY regulator which will be enabled before optional deasserting of the reset signal. Signed-off-by: Bartosz Golaszewski <bgolaszewski@xxxxxxxxxxxx> --- drivers/net/phy/mdio_bus.c | 21 ++++++++++++++++++++ drivers/net/phy/mdio_device.c | 36 +++++++++++++++++++++++++++++++++++ include/linux/mdio.h | 3 +++ 3 files changed, 60 insertions(+) diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 53e2fb0be7b9..19f0b9664fe3 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -27,6 +27,7 @@ #include <linux/of_gpio.h> #include <linux/of_mdio.h> #include <linux/phy.h> +#include <linux/regulator/consumer.h> #include <linux/reset.h> #include <linux/skbuff.h> #include <linux/slab.h> @@ -67,6 +68,22 @@ static int mdiobus_register_reset(struct mdio_device *mdiodev) return 0; } +static int mdiobus_register_regulator(struct mdio_device *mdiodev) +{ + struct regulator *phy_supply; + + phy_supply = regulator_get_optional(&mdiodev->dev, "phy"); + if (IS_ERR(phy_supply)) { + if (PTR_ERR(phy_supply) == -EPROBE_DEFER) + return -EPROBE_DEFER; + phy_supply = NULL; + } + + mdiodev->phy_supply = phy_supply; + + return 0; +} + int mdiobus_register_device(struct mdio_device *mdiodev) { int err; @@ -83,6 +100,10 @@ int mdiobus_register_device(struct mdio_device *mdiodev) if (err) return err; + err = mdiobus_register_regulator(mdiodev); + if (err) + return err; + /* Assert the reset signal */ mdio_device_reset(mdiodev, 1); } diff --git a/drivers/net/phy/mdio_device.c b/drivers/net/phy/mdio_device.c index be615504b829..0f698d7a770b 100644 --- a/drivers/net/phy/mdio_device.c +++ b/drivers/net/phy/mdio_device.c @@ -17,6 +17,7 @@ #include <linux/mii.h> #include <linux/module.h> #include <linux/phy.h> +#include <linux/regulator/consumer.h> #include <linux/reset.h> #include <linux/slab.h> #include <linux/string.h> @@ -136,6 +137,32 @@ void mdio_device_reset(struct mdio_device *mdiodev, int value) } EXPORT_SYMBOL(mdio_device_reset); +int mdio_device_power_on(struct mdio_device *mdiodev) +{ + int ret = 0; + + if (mdiodev->phy_supply) + /* TODO We may need a delay here just like reset_assert_delay + * but since no user currently needs it, I'm not adding it just + * yet. + */ + ret = regulator_enable(mdiodev->phy_supply); + + return ret; +} +EXPORT_SYMBOL(mdio_device_power_on); + +int mdio_device_power_off(struct mdio_device *mdiodev) +{ + int ret = 0; + + if (mdiodev->phy_supply) + ret = regulator_disable(mdiodev->phy_supply); + + return ret; +} +EXPORT_SYMBOL(mdio_device_power_off); + /** * mdio_probe - probe an MDIO device * @dev: device to probe @@ -150,6 +177,11 @@ static int mdio_probe(struct device *dev) struct mdio_driver *mdiodrv = to_mdio_driver(drv); int err = 0; + /* Enable the power supply */ + err = mdio_device_power_on(mdiodev); + if (err) + return err; + /* Deassert the reset signal */ mdio_device_reset(mdiodev, 0); @@ -158,6 +190,8 @@ static int mdio_probe(struct device *dev) if (err) { /* Assert the reset signal */ mdio_device_reset(mdiodev, 1); + /* Disable the power supply */ + mdio_device_power_off(mdiodev); } } @@ -175,6 +209,8 @@ static int mdio_remove(struct device *dev) /* Assert the reset signal */ mdio_device_reset(mdiodev, 1); + /* Disable the power supply */ + mdio_device_power_off(mdiodev); return 0; } diff --git a/include/linux/mdio.h b/include/linux/mdio.h index 9ac5e7ff6156..0ae07365a6ca 100644 --- a/include/linux/mdio.h +++ b/include/linux/mdio.h @@ -46,6 +46,7 @@ struct mdio_device { int flags; struct gpio_desc *reset_gpio; struct reset_control *reset_ctrl; + struct regulator *phy_supply; unsigned int reset_assert_delay; unsigned int reset_deassert_delay; }; @@ -92,6 +93,8 @@ 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_device_power_on(struct mdio_device *mdiodev); +int mdio_device_power_off(struct mdio_device *mdiodev); int mdio_driver_register(struct mdio_driver *drv); void mdio_driver_unregister(struct mdio_driver *drv); int mdio_device_bus_match(struct device *dev, struct device_driver *drv); -- 2.26.1