| From foo@baz Thu 09 Apr 2020 04:04:49 PM CEST |
| From: Oleksij Rempel <o.rempel@pengutronix.de> |
| Date: Fri, 3 Apr 2020 09:53:25 +0200 |
| Subject: net: phy: micrel: kszphy_resume(): add delay after genphy_resume() before accessing PHY registers |
| |
| From: Oleksij Rempel <o.rempel@pengutronix.de> |
| |
| [ Upstream commit 6110dff776f7fa65c35850ef65b41d3b39e2fac2 ] |
| |
| After the power-down bit is cleared, the chip internally triggers a |
| global reset. According to the KSZ9031 documentation, we have to wait at |
| least 1ms for the reset to finish. |
| |
| If the chip is accessed during reset, read will return 0xffff, while |
| write will be ignored. Depending on the system performance and MDIO bus |
| speed, we may or may not run in to this issue. |
| |
| This bug was discovered on an iMX6QP system with KSZ9031 PHY and |
| attached PHY interrupt line. If IRQ was used, the link status update was |
| lost. In polling mode, the link status update was always correct. |
| |
| The investigation showed, that during a read-modify-write access, the |
| read returned 0xffff (while the chip was still in reset) and |
| corresponding write hit the chip _after_ reset and triggered (due to the |
| 0xffff) another reset in an undocumented bit (register 0x1f, bit 1), |
| resulting in the next write being lost due to the new reset cycle. |
| |
| This patch fixes the issue by adding a 1...2 ms sleep after the |
| genphy_resume(). |
| |
| Fixes: 836384d2501d ("net: phy: micrel: Add specific suspend") |
| Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de> |
| Reviewed-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/micrel.c | 7 +++++++ |
| 1 file changed, 7 insertions(+) |
| |
| --- a/drivers/net/phy/micrel.c |
| +++ b/drivers/net/phy/micrel.c |
| @@ -28,6 +28,7 @@ |
| #include <linux/micrel_phy.h> |
| #include <linux/of.h> |
| #include <linux/clk.h> |
| +#include <linux/delay.h> |
| |
| /* Operation Mode Strap Override */ |
| #define MII_KSZPHY_OMSO 0x16 |
| @@ -728,6 +729,12 @@ static int kszphy_resume(struct phy_devi |
| { |
| genphy_resume(phydev); |
| |
| + /* After switching from power-down to normal mode, an internal global |
| + * reset is automatically generated. Wait a minimum of 1 ms before |
| + * read/write access to the PHY registers. |
| + */ |
| + usleep_range(1000, 2000); |
| + |
| /* Enable PHY Interrupts */ |
| if (phy_interrupt_is_valid(phydev)) { |
| phydev->interrupts = PHY_INTERRUPT_ENABLED; |