summaryrefslogtreecommitdiff
path: root/drivers/net/phy
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/phy')
-rw-r--r--drivers/net/phy/fixed_phy.c4
-rw-r--r--drivers/net/phy/mdio_bus.c3
-rw-r--r--drivers/net/phy/phylink.c27
3 files changed, 31 insertions, 3 deletions
diff --git a/drivers/net/phy/fixed_phy.c b/drivers/net/phy/fixed_phy.c
index c65fb5f5d2dc..a0c256bd5441 100644
--- a/drivers/net/phy/fixed_phy.c
+++ b/drivers/net/phy/fixed_phy.c
@@ -239,8 +239,8 @@ static struct phy_device *__fixed_phy_register(unsigned int irq,
/* Check if we have a GPIO associated with this fixed phy */
if (!gpiod) {
gpiod = fixed_phy_get_gpiod(np);
- if (IS_ERR(gpiod))
- return ERR_CAST(gpiod);
+ if (!gpiod)
+ return ERR_PTR(-EINVAL);
}
/* Get the next available PHY address, up to PHY_MAX_ADDR */
diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c
index c204067f1890..c198722e4871 100644
--- a/drivers/net/phy/mdio_bus.c
+++ b/drivers/net/phy/mdio_bus.c
@@ -460,6 +460,9 @@ static void of_mdiobus_link_mdiodev(struct mii_bus *bus,
if (addr == mdiodev->addr) {
device_set_node(dev, of_fwnode_handle(child));
+ /* The refcount on "child" is passed to the mdio
+ * device. Do _not_ use of_node_put(child) here.
+ */
return;
}
}
diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
index 3ad7397b8119..ea82ea5660e7 100644
--- a/drivers/net/phy/phylink.c
+++ b/drivers/net/phy/phylink.c
@@ -710,6 +710,7 @@ static void phylink_resolve(struct work_struct *w)
struct phylink_link_state link_state;
struct net_device *ndev = pl->netdev;
bool mac_config = false;
+ bool retrigger = false;
bool cur_link_state;
mutex_lock(&pl->state_mutex);
@@ -723,6 +724,7 @@ static void phylink_resolve(struct work_struct *w)
link_state.link = false;
} else if (pl->mac_link_dropped) {
link_state.link = false;
+ retrigger = true;
} else {
switch (pl->cur_link_an_mode) {
case MLO_AN_PHY:
@@ -739,6 +741,19 @@ static void phylink_resolve(struct work_struct *w)
case MLO_AN_INBAND:
phylink_mac_pcs_get_state(pl, &link_state);
+ /* The PCS may have a latching link-fail indicator.
+ * If the link was up, bring the link down and
+ * re-trigger the resolve. Otherwise, re-read the
+ * PCS state to get the current status of the link.
+ */
+ if (!link_state.link) {
+ if (cur_link_state)
+ retrigger = true;
+ else
+ phylink_mac_pcs_get_state(pl,
+ &link_state);
+ }
+
/* If we have a phy, the "up" state is the union of
* both the PHY and the MAC
*/
@@ -747,6 +762,15 @@ static void phylink_resolve(struct work_struct *w)
/* Only update if the PHY link is up */
if (pl->phydev && pl->phy_state.link) {
+ /* If the interface has changed, force a
+ * link down event if the link isn't already
+ * down, and re-resolve.
+ */
+ if (link_state.interface !=
+ pl->phy_state.interface) {
+ retrigger = true;
+ link_state.link = false;
+ }
link_state.interface = pl->phy_state.interface;
/* If we have a PHY, we need to update with
@@ -789,7 +813,7 @@ static void phylink_resolve(struct work_struct *w)
else
phylink_link_up(pl, link_state);
}
- if (!link_state.link && pl->mac_link_dropped) {
+ if (!link_state.link && retrigger) {
pl->mac_link_dropped = false;
queue_work(system_power_efficient_wq, &pl->resolve);
}
@@ -1364,6 +1388,7 @@ EXPORT_SYMBOL_GPL(phylink_stop);
* @mac_wol: true if the MAC needs to receive packets for Wake-on-Lan
*
* Handle a network device suspend event. There are several cases:
+ *
* - If Wake-on-Lan is not active, we can bring down the link between
* the MAC and PHY by calling phylink_stop().
* - If Wake-on-Lan is active, and being handled only by the PHY, we