--- /dev/null
+From 6aae7a6232c6444941de8e5a1e0cc42442ab9bce Mon Sep 17 00:00:00 2001
+From: Russell King <rmk+kernel@armlinux.org.uk>
+Date: Tue, 12 Dec 2017 10:45:36 +0000
+Subject: [PATCH] net: phy: fix resume handling
+
+From: Russell King <rmk+kernel@armlinux.org.uk>
+
+[ Upstream commit f5e64032a799d4f54decc7eb6aafcdffb67f9ad9 ]
+
+When a PHY has the BMCR_PDOWN bit set, it may decide to ignore writes
+to other registers, or reset the registers to power-on defaults.
+Micrel PHYs do this for their interrupt registers.
+
+The current structure of phylib tries to enable interrupts before
+resuming (and releasing) the BMCR_PDOWN bit. This fails, causing
+Micrel PHYs to stop working after a suspend/resume sequence if they
+are using interrupts.
+
+Fix this by ensuring that the PHY driver resume methods do not take
+the phydev->lock mutex themselves, but the callers of phy_resume()
+take that lock. This then allows us to move the call to phy_resume()
+before we enable interrupts in phy_start().
+
+Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Sasha Levin <alexander.levin@verizon.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/net/phy/at803x.c | 4 ----
+ drivers/net/phy/phy.c | 9 +++------
+ drivers/net/phy/phy_device.c | 10 ++++++----
+ 3 files changed, 9 insertions(+), 14 deletions(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -239,14 +239,10 @@ static int at803x_resume(struct phy_devi
+ {
+ int value;
+
+- mutex_lock(&phydev->lock);
+-
+ value = phy_read(phydev, MII_BMCR);
+ value &= ~(BMCR_PDOWN | BMCR_ISOLATE);
+ phy_write(phydev, MII_BMCR, value);
+
+- mutex_unlock(&phydev->lock);
+-
+ return 0;
+ }
+
+--- a/drivers/net/phy/phy.c
++++ b/drivers/net/phy/phy.c
+@@ -828,7 +828,6 @@ EXPORT_SYMBOL(phy_stop);
+ */
+ void phy_start(struct phy_device *phydev)
+ {
+- bool do_resume = false;
+ int err = 0;
+
+ mutex_lock(&phydev->lock);
+@@ -841,6 +840,9 @@ void phy_start(struct phy_device *phydev
+ phydev->state = PHY_UP;
+ break;
+ case PHY_HALTED:
++ /* if phy was suspended, bring the physical link up again */
++ phy_resume(phydev);
++
+ /* make sure interrupts are re-enabled for the PHY */
+ if (phy_interrupt_is_valid(phydev)) {
+ err = phy_enable_interrupts(phydev);
+@@ -849,17 +851,12 @@ void phy_start(struct phy_device *phydev
+ }
+
+ phydev->state = PHY_RESUMING;
+- do_resume = true;
+ break;
+ default:
+ break;
+ }
+ mutex_unlock(&phydev->lock);
+
+- /* if phy was suspended, bring the physical link up again */
+- if (do_resume)
+- phy_resume(phydev);
+-
+ phy_trigger_machine(phydev, true);
+ }
+ EXPORT_SYMBOL(phy_start);
+--- a/drivers/net/phy/phy_device.c
++++ b/drivers/net/phy/phy_device.c
+@@ -135,7 +135,9 @@ static int mdio_bus_phy_resume(struct de
+ if (!mdio_bus_phy_may_suspend(phydev))
+ goto no_resume;
+
++ mutex_lock(&phydev->lock);
+ ret = phy_resume(phydev);
++ mutex_unlock(&phydev->lock);
+ if (ret < 0)
+ return ret;
+
+@@ -1026,7 +1028,9 @@ int phy_attach_direct(struct net_device
+ if (err)
+ goto error;
+
++ mutex_lock(&phydev->lock);
+ phy_resume(phydev);
++ mutex_unlock(&phydev->lock);
+ phy_led_triggers_register(phydev);
+
+ return err;
+@@ -1157,6 +1161,8 @@ int phy_resume(struct phy_device *phydev
+ struct phy_driver *phydrv = to_phy_driver(phydev->mdio.dev.driver);
+ int ret = 0;
+
++ WARN_ON(!mutex_is_locked(&phydev->lock));
++
+ if (phydev->drv && phydrv->resume)
+ ret = phydrv->resume(phydev);
+
+@@ -1639,13 +1645,9 @@ int genphy_resume(struct phy_device *phy
+ {
+ int value;
+
+- mutex_lock(&phydev->lock);
+-
+ value = phy_read(phydev, MII_BMCR);
+ phy_write(phydev, MII_BMCR, value & ~BMCR_PDOWN);
+
+- mutex_unlock(&phydev->lock);
+-
+ return 0;
+ }
+ EXPORT_SYMBOL(genphy_resume);
--- /dev/null
+From 9c2c2e62df3fa30fb13fbeb7512a4eede729383b Mon Sep 17 00:00:00 2001
+From: Andrew Lunn <andrew@lunn.ch>
+Date: Tue, 27 Feb 2018 01:56:06 +0100
+Subject: net: phy: Restore phy_resume() locking assumption
+
+From: Andrew Lunn <andrew@lunn.ch>
+
+commit 9c2c2e62df3fa30fb13fbeb7512a4eede729383b upstream.
+
+commit f5e64032a799 ("net: phy: fix resume handling") changes the
+locking semantics for phy_resume() such that the caller now needs to
+hold the phy mutex. Not all call sites were adopted to this new
+semantic, resulting in warnings from the added
+WARN_ON(!mutex_is_locked(&phydev->lock)). Rather than change the
+semantics, add a __phy_resume() and restore the old behavior of
+phy_resume().
+
+Reported-by: Heiner Kallweit <hkallweit1@gmail.com>
+Fixes: f5e64032a799 ("net: phy: fix resume handling")
+Signed-off-by: Andrew Lunn <andrew@lunn.ch>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/net/phy/phy.c | 2 +-
+ drivers/net/phy/phy_device.c | 18 +++++++++++++-----
+ include/linux/phy.h | 1 +
+ 3 files changed, 15 insertions(+), 6 deletions(-)
+
+--- a/drivers/net/phy/phy.c
++++ b/drivers/net/phy/phy.c
+@@ -841,7 +841,7 @@ void phy_start(struct phy_device *phydev
+ break;
+ case PHY_HALTED:
+ /* if phy was suspended, bring the physical link up again */
+- phy_resume(phydev);
++ __phy_resume(phydev);
+
+ /* make sure interrupts are re-enabled for the PHY */
+ if (phy_interrupt_is_valid(phydev)) {
+--- a/drivers/net/phy/phy_device.c
++++ b/drivers/net/phy/phy_device.c
+@@ -135,9 +135,7 @@ static int mdio_bus_phy_resume(struct de
+ if (!mdio_bus_phy_may_suspend(phydev))
+ goto no_resume;
+
+- mutex_lock(&phydev->lock);
+ ret = phy_resume(phydev);
+- mutex_unlock(&phydev->lock);
+ if (ret < 0)
+ return ret;
+
+@@ -1028,9 +1026,7 @@ int phy_attach_direct(struct net_device
+ if (err)
+ goto error;
+
+- mutex_lock(&phydev->lock);
+ phy_resume(phydev);
+- mutex_unlock(&phydev->lock);
+ phy_led_triggers_register(phydev);
+
+ return err;
+@@ -1156,7 +1152,7 @@ int phy_suspend(struct phy_device *phyde
+ }
+ EXPORT_SYMBOL(phy_suspend);
+
+-int phy_resume(struct phy_device *phydev)
++int __phy_resume(struct phy_device *phydev)
+ {
+ struct phy_driver *phydrv = to_phy_driver(phydev->mdio.dev.driver);
+ int ret = 0;
+@@ -1173,6 +1169,18 @@ int phy_resume(struct phy_device *phydev
+
+ return ret;
+ }
++EXPORT_SYMBOL(__phy_resume);
++
++int phy_resume(struct phy_device *phydev)
++{
++ int ret;
++
++ mutex_lock(&phydev->lock);
++ ret = __phy_resume(phydev);
++ mutex_unlock(&phydev->lock);
++
++ return ret;
++}
+ EXPORT_SYMBOL(phy_resume);
+
+ int phy_loopback(struct phy_device *phydev, bool enable)
+--- a/include/linux/phy.h
++++ b/include/linux/phy.h
+@@ -817,6 +817,7 @@ void phy_device_remove(struct phy_device
+ int phy_init_hw(struct phy_device *phydev);
+ int phy_suspend(struct phy_device *phydev);
+ int phy_resume(struct phy_device *phydev);
++int __phy_resume(struct phy_device *phydev);
+ int phy_loopback(struct phy_device *phydev, bool enable);
+ struct phy_device *phy_attach(struct net_device *dev, const char *bus_id,
+ phy_interface_t interface);