diff options
author | Tim Beale <tim.beale@alliedtelesis.co.nz> | 2015-05-17 23:38:38 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2015-05-20 12:22:08 -0400 |
commit | c15e10e71ce3b4ee78d85d80102a9621cde1edbd (patch) | |
tree | 51f55dc4cd4bd6e6f082f36ec6b9d081deb4ecad | |
parent | 7764b9dd38f31ce58e6f764e2d7d8399d9b62486 (diff) |
net: phy: Make sure phy_start() always re-enables the phy interrupts
This is an alternative way of fixing:
commit db9683fb412d ("net: phy: Make sure PHY_RESUMING state change
is always processed")
When the PHY state transitions from PHY_HALTED to PHY_RESUMING, there are
two things we need to do:
1). Re-enable interrupts (and power up the physical link, if powered down)
2). Update the PHY state and net-device based on the link status.
There's no strict reason why #1 has to be done from within the main
phy_state_machine() function. There is a risk that other changes to the
PHY (e.g. setting speed/duplex, which calls phy_start_aneg()) could cause
a subsequent state transition before phy_state_machine() has processed
the PHY_RESUMING state change. This would leave the PHY with interrupts
disabled and/or still in the BMCR_PDOWN/low-power mode.
Moving enabling the interrupts and phy_resume() into phy_start() will
guarantee this work always gets done. As the PHY is already in the HALTED
state and interrupts are disabled, it shouldn't conflict with any work
being done in phy_state_machine(). The downside of this change is that if
the PHY_RESUMING state is ever entered from anywhere else, it'll also have
to repeat this work.
Signed-off-by: Tim Beale <tim.beale@alliedtelesis.co.nz>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/phy/phy.c | 29 |
1 files changed, 16 insertions, 13 deletions
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 710696d1af97..47cd578052fc 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c | |||
@@ -465,7 +465,7 @@ int phy_start_aneg(struct phy_device *phydev) | |||
465 | if (err < 0) | 465 | if (err < 0) |
466 | goto out_unlock; | 466 | goto out_unlock; |
467 | 467 | ||
468 | if (phydev->state != PHY_HALTED && phydev->state != PHY_RESUMING) { | 468 | if (phydev->state != PHY_HALTED) { |
469 | if (AUTONEG_ENABLE == phydev->autoneg) { | 469 | if (AUTONEG_ENABLE == phydev->autoneg) { |
470 | phydev->state = PHY_AN; | 470 | phydev->state = PHY_AN; |
471 | phydev->link_timeout = PHY_AN_TIMEOUT; | 471 | phydev->link_timeout = PHY_AN_TIMEOUT; |
@@ -742,6 +742,9 @@ EXPORT_SYMBOL(phy_stop); | |||
742 | */ | 742 | */ |
743 | void phy_start(struct phy_device *phydev) | 743 | void phy_start(struct phy_device *phydev) |
744 | { | 744 | { |
745 | bool do_resume = false; | ||
746 | int err = 0; | ||
747 | |||
745 | mutex_lock(&phydev->lock); | 748 | mutex_lock(&phydev->lock); |
746 | 749 | ||
747 | switch (phydev->state) { | 750 | switch (phydev->state) { |
@@ -752,11 +755,22 @@ void phy_start(struct phy_device *phydev) | |||
752 | phydev->state = PHY_UP; | 755 | phydev->state = PHY_UP; |
753 | break; | 756 | break; |
754 | case PHY_HALTED: | 757 | case PHY_HALTED: |
758 | /* make sure interrupts are re-enabled for the PHY */ | ||
759 | err = phy_enable_interrupts(phydev); | ||
760 | if (err < 0) | ||
761 | break; | ||
762 | |||
755 | phydev->state = PHY_RESUMING; | 763 | phydev->state = PHY_RESUMING; |
764 | do_resume = true; | ||
765 | break; | ||
756 | default: | 766 | default: |
757 | break; | 767 | break; |
758 | } | 768 | } |
759 | mutex_unlock(&phydev->lock); | 769 | mutex_unlock(&phydev->lock); |
770 | |||
771 | /* if phy was suspended, bring the physical link up again */ | ||
772 | if (do_resume) | ||
773 | phy_resume(phydev); | ||
760 | } | 774 | } |
761 | EXPORT_SYMBOL(phy_start); | 775 | EXPORT_SYMBOL(phy_start); |
762 | 776 | ||
@@ -769,7 +783,7 @@ void phy_state_machine(struct work_struct *work) | |||
769 | struct delayed_work *dwork = to_delayed_work(work); | 783 | struct delayed_work *dwork = to_delayed_work(work); |
770 | struct phy_device *phydev = | 784 | struct phy_device *phydev = |
771 | container_of(dwork, struct phy_device, state_queue); | 785 | container_of(dwork, struct phy_device, state_queue); |
772 | bool needs_aneg = false, do_suspend = false, do_resume = false; | 786 | bool needs_aneg = false, do_suspend = false; |
773 | int err = 0; | 787 | int err = 0; |
774 | 788 | ||
775 | mutex_lock(&phydev->lock); | 789 | mutex_lock(&phydev->lock); |
@@ -888,14 +902,6 @@ void phy_state_machine(struct work_struct *work) | |||
888 | } | 902 | } |
889 | break; | 903 | break; |
890 | case PHY_RESUMING: | 904 | case PHY_RESUMING: |
891 | err = phy_clear_interrupt(phydev); | ||
892 | if (err) | ||
893 | break; | ||
894 | |||
895 | err = phy_config_interrupt(phydev, PHY_INTERRUPT_ENABLED); | ||
896 | if (err) | ||
897 | break; | ||
898 | |||
899 | if (AUTONEG_ENABLE == phydev->autoneg) { | 905 | if (AUTONEG_ENABLE == phydev->autoneg) { |
900 | err = phy_aneg_done(phydev); | 906 | err = phy_aneg_done(phydev); |
901 | if (err < 0) | 907 | if (err < 0) |
@@ -933,7 +939,6 @@ void phy_state_machine(struct work_struct *work) | |||
933 | } | 939 | } |
934 | phydev->adjust_link(phydev->attached_dev); | 940 | phydev->adjust_link(phydev->attached_dev); |
935 | } | 941 | } |
936 | do_resume = true; | ||
937 | break; | 942 | break; |
938 | } | 943 | } |
939 | 944 | ||
@@ -943,8 +948,6 @@ void phy_state_machine(struct work_struct *work) | |||
943 | err = phy_start_aneg(phydev); | 948 | err = phy_start_aneg(phydev); |
944 | else if (do_suspend) | 949 | else if (do_suspend) |
945 | phy_suspend(phydev); | 950 | phy_suspend(phydev); |
946 | else if (do_resume) | ||
947 | phy_resume(phydev); | ||
948 | 951 | ||
949 | if (err < 0) | 952 | if (err < 0) |
950 | phy_error(phydev); | 953 | phy_error(phydev); |