Commit 75223d81 authored by David S. Miller's avatar David S. Miller

Merge branch 'phylib-hard-resetting-devices'

Geert Uytterhoeven says:

====================
Teach phylib hard-resetting devices

This patch series adds optional PHY reset support to phylib.

The first two patches are destined for David's net-next tree. They add
core PHY reset code, and update a driver that currently uses its own
reset code.

The last two patches are destined for Simon's renesas tree.  They add
properties to describe the EthernetAVB PHY reset topology to the common
Salvator-X/XS and ULCB DTS files, which solves two issues:
  1. On Salvator-XS, the enable pin of the regulator providing PHY power
     is connected to PRESETn, and PSCI powers down the SoC during system
     suspend.  Hence a PHY reset is needed to restore network
     functionality after system resume.
  2. Linux should not rely on the boot loader having reset the PHY, but
     should reset the PHY during driver probe.

Changes compared to v3:
  - Remove Florian's Acked-by,
  - Add missing #include <linux/gpio/consumer.h>,
  - Re-add the gpiod check, as the dummy gpiod_set_value() for !GPIOLIB
    does not ignore NULL, and calls WARN_ON(1),
  - Do not reassert the reset signal if {mdio,phy}_probe() or
    phy_device_register() succeeded, as that may destroy initial setup,
  - Do not deassert the reset signal in {mdio,phy}_remove(), as it
    should already be deasserted,
  - Bring the PHY back into reset state in phy_device_remove(),
  - Move/consolidate GPIO descriptor acquiring code from
    of_mdiobus_register_phy() and of_mdiobus_register_device() to
    mdiobus_register_device().
    Note that this changes behavior slightly, in that the reset signal
    is now also asserted when called from of_mdiobus_register_device().
  - Add Reviewed-by,

Changes compared to v2, as sent by Sergei Shtylyov:
  - Fix fwnode_get_named_gpiod() call due to added parameters (which
    allowed to eliminate the gpiod_direction_output() call),
  - Rebased, refreshed, reworded,
  - Take over from Sergei,
  - Add Acked-by,
  - Remove unneeded gpiod check, as gpiod_set_value() handles NULL fine,
  - Handle fwnode_get_named_gpiod() errors correctly:
      - -ENOENT is ignored (the GPIO is optional), and turned into NULL,
	which allowed to remove all later !IS_ERR() checks,
      - Other errors (incl. -EPROBE_DEFER) are propagated,
  - Extract DTS patches from series "[PATCH 0/4] ravb: Add PHY reset
    support" (https://www.spinics.net/lists/netdev/msg457308.html), and
    incorporate in this series, after moving reset-gpios from the
    ethernet to the ethernet-phy node.

Given (1) the new reset-gpios DT property in the PHY node follows
established practises, (2) the DT binding change in the first patch has
been acked by Rob, and (3) the DTS patch does not cause any regressions
if it is applied before the PHY driver patches, the DTS patches can be
applied independently.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 62b32379 096457b5
......@@ -53,6 +53,8 @@ Optional Properties:
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.
- reset-gpios: The GPIO phandle and specifier for the PHY reset signal.
Example:
ethernet-phy@0 {
......
......@@ -1171,7 +1171,6 @@ struct macb {
unsigned int dma_burst_length;
phy_interface_t phy_interface;
struct gpio_desc *reset_gpio;
/* AT91RM9200 transmit */
struct sk_buff *skb; /* holds skb until xmit interrupt completes */
......
......@@ -3803,7 +3803,6 @@ static int macb_probe(struct platform_device *pdev)
= macb_config->clk_init;
int (*init)(struct platform_device *) = macb_config->init;
struct device_node *np = pdev->dev.of_node;
struct device_node *phy_node;
struct clk *pclk, *hclk = NULL, *tx_clk = NULL, *rx_clk = NULL;
unsigned int queue_mask, num_queues;
struct macb_platform_data *pdata;
......@@ -3909,18 +3908,6 @@ static int macb_probe(struct platform_device *pdev)
else
macb_get_hwaddr(bp);
/* Power up the PHY if there is a GPIO reset */
phy_node = of_get_next_available_child(np, NULL);
if (phy_node) {
int gpio = of_get_named_gpio(phy_node, "reset-gpios", 0);
if (gpio_is_valid(gpio)) {
bp->reset_gpio = gpio_to_desc(gpio);
gpiod_direction_output(bp->reset_gpio, 1);
}
}
of_node_put(phy_node);
err = of_get_phy_mode(np);
if (err < 0) {
pdata = dev_get_platdata(&pdev->dev);
......@@ -3967,10 +3954,6 @@ static int macb_probe(struct platform_device *pdev)
of_phy_deregister_fixed_link(np);
mdiobus_free(bp->mii_bus);
/* Shutdown the PHY if there is a GPIO reset */
if (bp->reset_gpio)
gpiod_set_value(bp->reset_gpio, 0);
err_out_free_netdev:
free_netdev(dev);
......@@ -4001,10 +3984,6 @@ static int macb_remove(struct platform_device *pdev)
dev->phydev = NULL;
mdiobus_free(bp->mii_bus);
/* Shutdown the PHY if there is a GPIO reset */
if (bp->reset_gpio)
gpiod_set_value(bp->reset_gpio, 0);
unregister_netdev(dev);
clk_disable_unprepare(bp->tx_clk);
clk_disable_unprepare(bp->hclk);
......
......@@ -71,7 +71,6 @@ MODULE_LICENSE("GPL");
struct at803x_priv {
bool phy_reset:1;
struct gpio_desc *gpiod_reset;
};
struct at803x_context {
......@@ -254,22 +253,11 @@ static int at803x_probe(struct phy_device *phydev)
{
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;
......@@ -343,14 +331,14 @@ static void at803x_link_change_notify(struct phy_device *phydev)
* cannot recover from by software.
*/
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);
......
......@@ -38,6 +38,7 @@
#include <linux/phy.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include <linux/gpio/consumer.h>
#include <asm/irq.h>
......@@ -48,9 +49,26 @@
int mdiobus_register_device(struct mdio_device *mdiodev)
{
struct gpio_desc *gpiod = NULL;
if (mdiodev->bus->mdio_map[mdiodev->addr])
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;
return 0;
......@@ -420,6 +438,9 @@ void mdiobus_unregister(struct mii_bus *bus)
if (!mdiodev)
continue;
if (mdiodev->reset)
gpiod_put(mdiodev->reset);
mdiodev->device_remove(mdiodev);
mdiodev->device_free(mdiodev);
}
......
......@@ -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>
......@@ -114,6 +116,13 @@ void mdio_device_remove(struct mdio_device *mdiodev)
}
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
......@@ -128,8 +137,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);
if (err) {
/* Assert the reset signal */
mdio_device_reset(mdiodev, 1);
}
}
return err;
}
......@@ -140,9 +157,13 @@ static int mdio_remove(struct device *dev)
struct device_driver *drv = mdiodev->dev.driver;
struct mdio_driver *mdiodrv = to_mdio_driver(drv);
if (mdiodrv->remove)
if (mdiodrv->remove) {
mdiodrv->remove(mdiodev);
/* Assert the reset signal */
mdio_device_reset(mdiodev, 1);
}
return 0;
}
......
......@@ -632,6 +632,9 @@ int phy_device_register(struct phy_device *phydev)
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) {
......@@ -650,6 +653,9 @@ int phy_device_register(struct phy_device *phydev)
return 0;
out:
/* Assert the reset signal */
phy_device_reset(phydev, 1);
mdiobus_unregister_device(&phydev->mdio);
return err;
}
......@@ -666,6 +672,10 @@ EXPORT_SYMBOL(phy_device_register);
void phy_device_remove(struct phy_device *phydev)
{
device_del(&phydev->mdio.dev);
/* Assert the reset signal */
phy_device_reset(phydev, 1);
mdiobus_unregister_device(&phydev->mdio);
}
EXPORT_SYMBOL(phy_device_remove);
......@@ -849,6 +859,9 @@ int phy_init_hw(struct phy_device *phydev)
{
int ret = 0;
/* Deassert the reset signal */
phy_device_reset(phydev, 0);
if (!phydev->drv || !phydev->drv->config_init)
return 0;
......@@ -1126,6 +1139,9 @@ void phy_detach(struct phy_device *phydev)
put_device(&phydev->mdio.dev);
if (ndev_owner != bus->owner)
module_put(bus->owner);
/* Assert the reset signal */
phy_device_reset(phydev, 1);
}
EXPORT_SYMBOL(phy_detach);
......@@ -1811,8 +1827,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);
if (err) {
/* Assert the reset signal */
phy_device_reset(phydev, 1);
}
}
mutex_unlock(&phydev->lock);
......@@ -1829,8 +1853,12 @@ static int phy_remove(struct device *dev)
phydev->state = PHY_DOWN;
mutex_unlock(&phydev->lock);
if (phydev->drv && phydev->drv->remove)
if (phydev->drv && phydev->drv->remove) {
phydev->drv->remove(phydev);
/* Assert the reset signal */
phy_device_reset(phydev, 1);
}
phydev->drv = NULL;
return 0;
......
......@@ -12,6 +12,7 @@
#include <uapi/linux/mdio.h>
#include <linux/mod_devicetable.h>
struct gpio_desc;
struct mii_bus;
/* Multiple levels of nesting are possible. However typically this is
......@@ -39,6 +40,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)
......@@ -71,6 +73,7 @@ void mdio_device_free(struct mdio_device *mdiodev);
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);
int mdio_device_bus_match(struct device *dev, struct device_driver *drv);
......
......@@ -854,6 +854,11 @@ int phy_aneg_done(struct phy_device *phydev);
int phy_stop_interrupts(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...) \
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