net: phy: provide a hook for link up/link down events
authorRussell King <rmk+kernel@arm.linux.org.uk>
Tue, 25 Jul 2017 14:02:58 +0000 (15:02 +0100)
committerDavid S. Miller <davem@davemloft.net>
Mon, 7 Aug 2017 03:55:28 +0000 (20:55 -0700)
Sometimes, we need to do additional work between the PHY coming up and
marking the carrier present - for example, we may need to wait for the
PHY to MAC link to finish negotiation.  This changes phylib to provide
a notification function pointer which avoids the built-in
netif_carrier_on() and netif_carrier_off() functions.

Standard ->adjust_link functionality is provided by hooking a helper
into the new ->phy_link_change method.

Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/phy/phy.c
drivers/net/phy/phy_device.c
include/linux/phy.h

index 71c64a77485678ae3093811bb41aa05802a72bae..d5f2af2c5ddb76747ccfa5276b0a247a2c40fd45 100644 (file)
@@ -865,9 +865,15 @@ void phy_start(struct phy_device *phydev)
 }
 EXPORT_SYMBOL(phy_start);
 
-static void phy_adjust_link(struct phy_device *phydev)
+static void phy_link_up(struct phy_device *phydev)
 {
-       phydev->adjust_link(phydev->attached_dev);
+       phydev->phy_link_change(phydev, true, true);
+       phy_led_trigger_change_speed(phydev);
+}
+
+static void phy_link_down(struct phy_device *phydev, bool do_carrier)
+{
+       phydev->phy_link_change(phydev, false, do_carrier);
        phy_led_trigger_change_speed(phydev);
 }
 
@@ -912,8 +918,7 @@ void phy_state_machine(struct work_struct *work)
                /* If the link is down, give up on negotiation for now */
                if (!phydev->link) {
                        phydev->state = PHY_NOLINK;
-                       netif_carrier_off(phydev->attached_dev);
-                       phy_adjust_link(phydev);
+                       phy_link_down(phydev, true);
                        break;
                }
 
@@ -925,9 +930,7 @@ void phy_state_machine(struct work_struct *work)
                /* If AN is done, we're running */
                if (err > 0) {
                        phydev->state = PHY_RUNNING;
-                       netif_carrier_on(phydev->attached_dev);
-                       phy_adjust_link(phydev);
-
+                       phy_link_up(phydev);
                } else if (0 == phydev->link_timeout--)
                        needs_aneg = true;
                break;
@@ -952,8 +955,7 @@ void phy_state_machine(struct work_struct *work)
                                }
                        }
                        phydev->state = PHY_RUNNING;
-                       netif_carrier_on(phydev->attached_dev);
-                       phy_adjust_link(phydev);
+                       phy_link_up(phydev);
                }
                break;
        case PHY_FORCING:
@@ -963,13 +965,12 @@ void phy_state_machine(struct work_struct *work)
 
                if (phydev->link) {
                        phydev->state = PHY_RUNNING;
-                       netif_carrier_on(phydev->attached_dev);
+                       phy_link_up(phydev);
                } else {
                        if (0 == phydev->link_timeout--)
                                needs_aneg = true;
+                       phy_link_down(phydev, false);
                }
-
-               phy_adjust_link(phydev);
                break;
        case PHY_RUNNING:
                /* Only register a CHANGE if we are polling and link changed
@@ -1001,14 +1002,12 @@ void phy_state_machine(struct work_struct *work)
 
                if (phydev->link) {
                        phydev->state = PHY_RUNNING;
-                       netif_carrier_on(phydev->attached_dev);
+                       phy_link_up(phydev);
                } else {
                        phydev->state = PHY_NOLINK;
-                       netif_carrier_off(phydev->attached_dev);
+                       phy_link_down(phydev, true);
                }
 
-               phy_adjust_link(phydev);
-
                if (phy_interrupt_is_valid(phydev))
                        err = phy_config_interrupt(phydev,
                                                   PHY_INTERRUPT_ENABLED);
@@ -1016,8 +1015,7 @@ void phy_state_machine(struct work_struct *work)
        case PHY_HALTED:
                if (phydev->link) {
                        phydev->link = 0;
-                       netif_carrier_off(phydev->attached_dev);
-                       phy_adjust_link(phydev);
+                       phy_link_down(phydev, true);
                        do_suspend = true;
                }
                break;
@@ -1037,11 +1035,11 @@ void phy_state_machine(struct work_struct *work)
 
                                if (phydev->link) {
                                        phydev->state = PHY_RUNNING;
-                                       netif_carrier_on(phydev->attached_dev);
+                                       phy_link_up(phydev);
                                } else  {
                                        phydev->state = PHY_NOLINK;
+                                       phy_link_down(phydev, false);
                                }
-                               phy_adjust_link(phydev);
                        } else {
                                phydev->state = PHY_AN;
                                phydev->link_timeout = PHY_AN_TIMEOUT;
@@ -1053,11 +1051,11 @@ void phy_state_machine(struct work_struct *work)
 
                        if (phydev->link) {
                                phydev->state = PHY_RUNNING;
-                               netif_carrier_on(phydev->attached_dev);
+                               phy_link_up(phydev);
                        } else  {
                                phydev->state = PHY_NOLINK;
+                               phy_link_down(phydev, false);
                        }
-                       phy_adjust_link(phydev);
                }
                break;
        }
index 1790f7fec12573fe21d088f9865126784efc0c08..d536a9a7cd2bb8152051f41fb3a1242c292da69f 100644 (file)
@@ -688,6 +688,19 @@ struct phy_device *phy_find_first(struct mii_bus *bus)
 }
 EXPORT_SYMBOL(phy_find_first);
 
+static void phy_link_change(struct phy_device *phydev, bool up, bool do_carrier)
+{
+       struct net_device *netdev = phydev->attached_dev;
+
+       if (do_carrier) {
+               if (up)
+                       netif_carrier_on(netdev);
+               else
+                       netif_carrier_off(netdev);
+       }
+       phydev->adjust_link(netdev);
+}
+
 /**
  * phy_prepare_link - prepares the PHY layer to monitor link status
  * @phydev: target phy_device struct
@@ -951,6 +964,7 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
                goto error;
        }
 
+       phydev->phy_link_change = phy_link_change;
        phydev->attached_dev = dev;
        dev->phydev = phydev;
 
index 8a280257778cf6f16b4de7e8084a9232be1caaf3..0a5e8e62c9e067664769a521f7cfc01aa1f38010 100644 (file)
@@ -474,6 +474,7 @@ struct phy_device {
        u8 mdix;
        u8 mdix_ctrl;
 
+       void (*phy_link_change)(struct phy_device *, bool up, bool do_carrier);
        void (*adjust_link)(struct net_device *dev);
 };
 #define to_phy_device(d) container_of(to_mdio_device(d), \