net: 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> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
1f3645bb41
commit
a81497bee7
@ -865,9 +865,15 @@ void phy_start(struct phy_device *phydev)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL(phy_start);
|
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);
|
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 the link is down, give up on negotiation for now */
|
||||||
if (!phydev->link) {
|
if (!phydev->link) {
|
||||||
phydev->state = PHY_NOLINK;
|
phydev->state = PHY_NOLINK;
|
||||||
netif_carrier_off(phydev->attached_dev);
|
phy_link_down(phydev, true);
|
||||||
phy_adjust_link(phydev);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -925,9 +930,7 @@ void phy_state_machine(struct work_struct *work)
|
|||||||
/* If AN is done, we're running */
|
/* If AN is done, we're running */
|
||||||
if (err > 0) {
|
if (err > 0) {
|
||||||
phydev->state = PHY_RUNNING;
|
phydev->state = PHY_RUNNING;
|
||||||
netif_carrier_on(phydev->attached_dev);
|
phy_link_up(phydev);
|
||||||
phy_adjust_link(phydev);
|
|
||||||
|
|
||||||
} else if (0 == phydev->link_timeout--)
|
} else if (0 == phydev->link_timeout--)
|
||||||
needs_aneg = true;
|
needs_aneg = true;
|
||||||
break;
|
break;
|
||||||
@ -952,8 +955,7 @@ void phy_state_machine(struct work_struct *work)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
phydev->state = PHY_RUNNING;
|
phydev->state = PHY_RUNNING;
|
||||||
netif_carrier_on(phydev->attached_dev);
|
phy_link_up(phydev);
|
||||||
phy_adjust_link(phydev);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case PHY_FORCING:
|
case PHY_FORCING:
|
||||||
@ -963,13 +965,12 @@ void phy_state_machine(struct work_struct *work)
|
|||||||
|
|
||||||
if (phydev->link) {
|
if (phydev->link) {
|
||||||
phydev->state = PHY_RUNNING;
|
phydev->state = PHY_RUNNING;
|
||||||
netif_carrier_on(phydev->attached_dev);
|
phy_link_up(phydev);
|
||||||
} else {
|
} else {
|
||||||
if (0 == phydev->link_timeout--)
|
if (0 == phydev->link_timeout--)
|
||||||
needs_aneg = true;
|
needs_aneg = true;
|
||||||
|
phy_link_down(phydev, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
phy_adjust_link(phydev);
|
|
||||||
break;
|
break;
|
||||||
case PHY_RUNNING:
|
case PHY_RUNNING:
|
||||||
/* Only register a CHANGE if we are polling and link changed
|
/* 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) {
|
if (phydev->link) {
|
||||||
phydev->state = PHY_RUNNING;
|
phydev->state = PHY_RUNNING;
|
||||||
netif_carrier_on(phydev->attached_dev);
|
phy_link_up(phydev);
|
||||||
} else {
|
} else {
|
||||||
phydev->state = PHY_NOLINK;
|
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))
|
if (phy_interrupt_is_valid(phydev))
|
||||||
err = phy_config_interrupt(phydev,
|
err = phy_config_interrupt(phydev,
|
||||||
PHY_INTERRUPT_ENABLED);
|
PHY_INTERRUPT_ENABLED);
|
||||||
@ -1016,8 +1015,7 @@ void phy_state_machine(struct work_struct *work)
|
|||||||
case PHY_HALTED:
|
case PHY_HALTED:
|
||||||
if (phydev->link) {
|
if (phydev->link) {
|
||||||
phydev->link = 0;
|
phydev->link = 0;
|
||||||
netif_carrier_off(phydev->attached_dev);
|
phy_link_down(phydev, true);
|
||||||
phy_adjust_link(phydev);
|
|
||||||
do_suspend = true;
|
do_suspend = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -1037,11 +1035,11 @@ void phy_state_machine(struct work_struct *work)
|
|||||||
|
|
||||||
if (phydev->link) {
|
if (phydev->link) {
|
||||||
phydev->state = PHY_RUNNING;
|
phydev->state = PHY_RUNNING;
|
||||||
netif_carrier_on(phydev->attached_dev);
|
phy_link_up(phydev);
|
||||||
} else {
|
} else {
|
||||||
phydev->state = PHY_NOLINK;
|
phydev->state = PHY_NOLINK;
|
||||||
|
phy_link_down(phydev, false);
|
||||||
}
|
}
|
||||||
phy_adjust_link(phydev);
|
|
||||||
} else {
|
} else {
|
||||||
phydev->state = PHY_AN;
|
phydev->state = PHY_AN;
|
||||||
phydev->link_timeout = PHY_AN_TIMEOUT;
|
phydev->link_timeout = PHY_AN_TIMEOUT;
|
||||||
@ -1053,11 +1051,11 @@ void phy_state_machine(struct work_struct *work)
|
|||||||
|
|
||||||
if (phydev->link) {
|
if (phydev->link) {
|
||||||
phydev->state = PHY_RUNNING;
|
phydev->state = PHY_RUNNING;
|
||||||
netif_carrier_on(phydev->attached_dev);
|
phy_link_up(phydev);
|
||||||
} else {
|
} else {
|
||||||
phydev->state = PHY_NOLINK;
|
phydev->state = PHY_NOLINK;
|
||||||
|
phy_link_down(phydev, false);
|
||||||
}
|
}
|
||||||
phy_adjust_link(phydev);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -688,6 +688,19 @@ struct phy_device *phy_find_first(struct mii_bus *bus)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL(phy_find_first);
|
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
|
* phy_prepare_link - prepares the PHY layer to monitor link status
|
||||||
* @phydev: target phy_device struct
|
* @phydev: target phy_device struct
|
||||||
@ -951,6 +964,7 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
phydev->phy_link_change = phy_link_change;
|
||||||
phydev->attached_dev = dev;
|
phydev->attached_dev = dev;
|
||||||
dev->phydev = phydev;
|
dev->phydev = phydev;
|
||||||
|
|
||||||
|
@ -474,6 +474,7 @@ struct phy_device {
|
|||||||
u8 mdix;
|
u8 mdix;
|
||||||
u8 mdix_ctrl;
|
u8 mdix_ctrl;
|
||||||
|
|
||||||
|
void (*phy_link_change)(struct phy_device *, bool up, bool do_carrier);
|
||||||
void (*adjust_link)(struct net_device *dev);
|
void (*adjust_link)(struct net_device *dev);
|
||||||
};
|
};
|
||||||
#define to_phy_device(d) container_of(to_mdio_device(d), \
|
#define to_phy_device(d) container_of(to_mdio_device(d), \
|
||||||
|
Loading…
Reference in New Issue
Block a user