2017-04-17 06:43:54 +00:00
|
|
|
From d8b4e728f598d3c8a9b219d4679d5de350caa082 Mon Sep 17 00:00:00 2001
|
|
|
|
From: Russell King <rmk+kernel@arm.linux.org.uk>
|
|
|
|
Date: Fri, 18 Sep 2015 14:42:16 +0100
|
|
|
|
Subject: [PATCH 714/744] phy: provide a hook for link up/link down events
|
|
|
|
|
|
|
|
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>
|
|
|
|
---
|
|
|
|
drivers/net/phy/phy.c | 42 ++++++++++++++++++++++--------------------
|
|
|
|
drivers/net/phy/phy_device.c | 14 ++++++++++++++
|
|
|
|
include/linux/phy.h | 1 +
|
|
|
|
3 files changed, 37 insertions(+), 20 deletions(-)
|
|
|
|
|
|
|
|
--- a/drivers/net/phy/phy.c
|
|
|
|
+++ b/drivers/net/phy/phy.c
|
2017-09-28 09:39:05 +00:00
|
|
|
@@ -849,6 +849,16 @@ void phy_start(struct phy_device *phydev
|
2017-04-17 06:43:54 +00:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(phy_start);
|
|
|
|
|
|
|
|
+static void phy_link_up(struct phy_device *phydev)
|
|
|
|
+{
|
|
|
|
+ phydev->phy_link_change(phydev, true, true);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void phy_link_down(struct phy_device *phydev, bool do_carrier)
|
|
|
|
+{
|
|
|
|
+ phydev->phy_link_change(phydev, false, do_carrier);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
/**
|
|
|
|
* phy_state_machine - Handle the state machine
|
|
|
|
* @work: work_struct that describes the work to be done
|
2017-09-28 09:39:05 +00:00
|
|
|
@@ -890,8 +900,7 @@ void phy_state_machine(struct work_struc
|
2017-04-17 06:43:54 +00:00
|
|
|
/* If the link is down, give up on negotiation for now */
|
|
|
|
if (!phydev->link) {
|
|
|
|
phydev->state = PHY_NOLINK;
|
|
|
|
- netif_carrier_off(phydev->attached_dev);
|
|
|
|
- phydev->adjust_link(phydev->attached_dev);
|
|
|
|
+ phy_link_down(phydev, true);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2017-09-28 09:39:05 +00:00
|
|
|
@@ -903,9 +912,7 @@ void phy_state_machine(struct work_struc
|
2017-04-17 06:43:54 +00:00
|
|
|
/* If AN is done, we're running */
|
|
|
|
if (err > 0) {
|
|
|
|
phydev->state = PHY_RUNNING;
|
|
|
|
- netif_carrier_on(phydev->attached_dev);
|
|
|
|
- phydev->adjust_link(phydev->attached_dev);
|
|
|
|
-
|
|
|
|
+ phy_link_up(phydev);
|
|
|
|
} else if (0 == phydev->link_timeout--)
|
|
|
|
needs_aneg = true;
|
|
|
|
break;
|
2017-09-28 09:39:05 +00:00
|
|
|
@@ -930,8 +937,7 @@ void phy_state_machine(struct work_struc
|
2017-04-17 06:43:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
phydev->state = PHY_RUNNING;
|
|
|
|
- netif_carrier_on(phydev->attached_dev);
|
|
|
|
- phydev->adjust_link(phydev->attached_dev);
|
|
|
|
+ phy_link_up(phydev);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case PHY_FORCING:
|
2017-09-28 09:39:05 +00:00
|
|
|
@@ -941,13 +947,12 @@ void phy_state_machine(struct work_struc
|
2017-04-17 06:43:54 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
-
|
|
|
|
- phydev->adjust_link(phydev->attached_dev);
|
|
|
|
break;
|
|
|
|
case PHY_RUNNING:
|
|
|
|
/* Only register a CHANGE if we are polling or ignoring
|
2017-09-28 09:39:05 +00:00
|
|
|
@@ -979,14 +984,12 @@ void phy_state_machine(struct work_struc
|
2017-04-17 06:43:54 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
- phydev->adjust_link(phydev->attached_dev);
|
|
|
|
-
|
|
|
|
if (phy_interrupt_is_valid(phydev))
|
|
|
|
err = phy_config_interrupt(phydev,
|
|
|
|
PHY_INTERRUPT_ENABLED);
|
2017-09-28 09:39:05 +00:00
|
|
|
@@ -994,8 +997,7 @@ void phy_state_machine(struct work_struc
|
2017-04-17 06:43:54 +00:00
|
|
|
case PHY_HALTED:
|
|
|
|
if (phydev->link) {
|
|
|
|
phydev->link = 0;
|
|
|
|
- netif_carrier_off(phydev->attached_dev);
|
|
|
|
- phydev->adjust_link(phydev->attached_dev);
|
|
|
|
+ phy_link_down(phydev, true);
|
|
|
|
do_suspend = true;
|
|
|
|
}
|
|
|
|
break;
|
2017-09-28 09:39:05 +00:00
|
|
|
@@ -1015,11 +1017,11 @@ void phy_state_machine(struct work_struc
|
2017-04-17 06:43:54 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
- phydev->adjust_link(phydev->attached_dev);
|
|
|
|
} else {
|
|
|
|
phydev->state = PHY_AN;
|
|
|
|
phydev->link_timeout = PHY_AN_TIMEOUT;
|
2017-09-28 09:39:05 +00:00
|
|
|
@@ -1031,11 +1033,11 @@ void phy_state_machine(struct work_struc
|
2017-04-17 06:43:54 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
- phydev->adjust_link(phydev->attached_dev);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
--- a/drivers/net/phy/phy_device.c
|
|
|
|
+++ b/drivers/net/phy/phy_device.c
|
|
|
|
@@ -441,6 +441,19 @@ struct phy_device *phy_find_first(struct
|
|
|
|
}
|
|
|
|
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
|
|
|
|
@@ -659,6 +672,7 @@ int phy_attach_direct(struct net_device
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
+ phydev->phy_link_change = phy_link_change;
|
|
|
|
phydev->attached_dev = dev;
|
|
|
|
dev->phydev = phydev;
|
|
|
|
|
|
|
|
--- a/include/linux/phy.h
|
|
|
|
+++ b/include/linux/phy.h
|
2017-11-23 21:42:13 +00:00
|
|
|
@@ -429,6 +429,7 @@ struct phy_device {
|
2017-04-17 06:43:54 +00:00
|
|
|
|
|
|
|
u8 mdix;
|
|
|
|
|
|
|
|
+ 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(d, struct phy_device, dev)
|