On 06.04.2021 13:42, Heiner Kallweit wrote: > On 06.04.2021 12:07, Joakim Zhang wrote: >> >>> -----Original Message----- >>> From: Heiner Kallweit <hkallwe...@gmail.com> >>> Sent: 2021年4月6日 14:29 >>> To: Joakim Zhang <qiangqing.zh...@nxp.com>; christian.me...@t2data.com; >>> and...@lunn.ch; li...@armlinux.org.uk; da...@davemloft.net; >>> k...@kernel.org >>> Cc: netdev@vger.kernel.org; linux-ker...@vger.kernel.org; dl-linux-imx >>> <linux-...@nxp.com> >>> Subject: Re: [PATCH] net: phy: fix PHY possibly unwork after MDIO bus resume >>> back >>> >>> On 06.04.2021 04:07, Joakim Zhang wrote: >>>> >>>> Hi Heiner, >>>> >>>>> -----Original Message----- >>>>> From: Heiner Kallweit <hkallwe...@gmail.com> >>>>> Sent: 2021年4月5日 20:10 >>>>> To: christian.me...@t2data.com; Joakim Zhang >>>>> <qiangqing.zh...@nxp.com>; and...@lunn.ch; li...@armlinux.org.uk; >>>>> da...@davemloft.net; k...@kernel.org >>>>> Cc: netdev@vger.kernel.org; linux-ker...@vger.kernel.org; >>>>> dl-linux-imx <linux-...@nxp.com> >>>>> Subject: Re: [PATCH] net: phy: fix PHY possibly unwork after MDIO bus >>>>> resume back >>>>> >>>>> On 05.04.2021 10:43, Christian Melki wrote: >>>>>> On 4/5/21 12:48 AM, Heiner Kallweit wrote: >>>>>>> On 04.04.2021 16:09, Heiner Kallweit wrote: >>>>>>>> On 04.04.2021 12:07, Joakim Zhang wrote: >>>>>>>>> commit 4c0d2e96ba055 ("net: phy: consider that suspend2ram may >>>>>>>>> cut off PHY power") invokes phy_init_hw() when MDIO bus resume, >>>>>>>>> it will soft reset PHY if PHY driver implements soft_reset callback. >>>>>>>>> commit 764d31cacfe4 ("net: phy: micrel: set soft_reset callback >>>>>>>>> to genphy_soft_reset for KSZ8081") adds soft_reset for KSZ8081. >>>>>>>>> After these two patches, I found i.MX6UL 14x14 EVK which >>>>>>>>> connected to KSZ8081RNB doesn't work any more when system >>> resume >>>>>>>>> back, MAC >>>>> driver is fec_main.c. >>>>>>>>> >>>>>>>>> It's obvious that initializing PHY hardware when MDIO bus resume >>>>>>>>> back would introduce some regression when PHY implements >>>>>>>>> soft_reset. When I >>>>>>>> >>>>>>>> Why is this obvious? Please elaborate on why a soft reset should >>>>>>>> break something. >>>>>>>> >>>>>>>>> am debugging, I found PHY works fine if MAC doesn't support >>>>>>>>> suspend/resume or phy_stop()/phy_start() doesn't been called >>>>>>>>> during suspend/resume. This let me realize, PHY state machine >>>>>>>>> phy_state_machine() could do something breaks the PHY. >>>>>>>>> >>>>>>>>> As we known, MAC resume first and then MDIO bus resume when >>>>>>>>> system resume back from suspend. When MAC resume, usually it will >>>>>>>>> invoke >>>>>>>>> phy_start() where to change PHY state to PHY_UP, then trigger the >>>>>>>>> stat> machine to run now. In phy_state_machine(), it will >>>>>>>>> start/config auto-nego, then change PHY state to PHY_NOLINK, what >>>>>>>>> to next is periodically check PHY link status. When MDIO bus >>>>>>>>> resume, it will initialize PHY hardware, including soft_reset, >>>>>>>>> what would soft_reset affect seems various from different PHYs. >>>>>>>>> For KSZ8081RNB, when it in PHY_NOLINK state and then perform a >>>>>>>>> soft reset, >>>>> it will never complete auto-nego. >>>>>>>> >>>>>>>> Why? That would need to be checked in detail. Maybe chip errata >>>>>>>> documentation provides a hint. >>>>>>>> >>>>>>> >>>>>>> The KSZ8081 spec says the following about bit BMCR_PDOWN: >>>>>>> >>>>>>> If software reset (Register 0.15) is used to exit power-down mode >>>>>>> (Register 0.11 = 1), two software reset writes (Register 0.15 = 1) >>>>>>> are required. The first write clears power-down mode; the second >>>>>>> write resets the chip and re-latches the pin strapping pin values. >>>>>>> >>>>>>> Maybe this causes the issue you see and genphy_soft_reset() isn't >>>>>>> appropriate for this PHY. Please re-test with the KSZ8081 soft >>>>>>> reset following the spec comment. >>>>>>> >>>>>> >>>>>> Interesting. Never expected that behavior. >>>>>> Thanks for catching it. Skimmed through the datasheets/erratas. >>>>>> This is what I found (micrel.c): >>>>>> >>>>>> 10/100: >>>>>> 8001 - Unaffected? >>>>>> 8021/8031 - Double reset after PDOWN. >>>>>> 8041 - Errata. PDOWN broken. Recommended do not use. Unclear if >>>>>> reset solves the issue since errata says no error after reset but is >>>>>> also claiming that only toggling PDOWN (may) or power will help. >>>>>> 8051 - Double reset after PDOWN. >>>>>> 8061 - Double reset after PDOWN. >>>>>> 8081 - Double reset after PDOWN. >>>>>> 8091 - Double reset after PDOWN. >>>>>> >>>>>> 10/100/1000: >>>>>> Nothing in gigabit afaics. >>>>>> >>>>>> Switches: >>>>>> 8862 - Not affected? >>>>>> 8863 - Errata. PDOWN broken. Reset will not help. Workaround exists. >>>>>> 8864 - Not affected? >>>>>> 8873 - Errata. PDOWN broken. Reset will not help. Workaround exists. >>>>>> 9477 - Errata. PDOWN broken. Will randomly cause link failure on >>>>>> adjacent links. Do not use. >>>>>> >>>>>> This certainly explains a lot. >>>>>> >>>>>>>>> >>>>>>>>> This patch changes PHY state to PHY_UP when MDIO bus resume back, >>>>>>>>> it should be reasonable after PHY hardware re-initialized. Also >>>>>>>>> give state machine a chance to start/config auto-nego again. >>>>>>>>> >>>>>>>> >>>>>>>> If the MAC driver calls phy_stop() on suspend, then >>>>>>>> phydev->suspended is true and mdio_bus_phy_may_suspend() returns >>>>>>>> false. As a consequence >>>>>>>> phydev->suspended_by_mdio_bus is false and mdio_bus_phy_resume() >>>>>>>> skips the PHY hw initialization. >>>>>>>> Please also note that mdio_bus_phy_suspend() calls >>>>>>>> phy_stop_machine() that sets the state to PHY_UP. >>>>>>>> >>>>>>> >>>>>>> Forgot that MDIO bus suspend is done before MAC driver suspend. >>>>>>> Therefore disregard this part for now. >>>>>>> >>>>>>>> Having said that the current argumentation isn't convincing. I'm >>>>>>>> not aware of such issues on other systems, therefore it's likely >>>>>>>> that something is system-dependent. >>>>>>>> >>>>>>>> Please check the exact call sequence on your system, maybe it >>>>>>>> provides a hint. >>>>>>>> >>>>>>>>> Signed-off-by: Joakim Zhang <qiangqing.zh...@nxp.com> >>>>>>>>> --- >>>>>>>>> drivers/net/phy/phy_device.c | 7 +++++++ >>>>>>>>> 1 file changed, 7 insertions(+) >>>>>>>>> >>>>>>>>> diff --git a/drivers/net/phy/phy_device.c >>>>>>>>> b/drivers/net/phy/phy_device.c index cc38e326405a..312a6f662481 >>>>>>>>> 100644 >>>>>>>>> --- a/drivers/net/phy/phy_device.c >>>>>>>>> +++ b/drivers/net/phy/phy_device.c >>>>>>>>> @@ -306,6 +306,13 @@ static __maybe_unused int >>>>> mdio_bus_phy_resume(struct device *dev) >>>>>>>>> ret = phy_resume(phydev); >>>>>>>>> if (ret < 0) >>>>>>>>> return ret; >>>>>>>>> + >>>>>>>>> + /* PHY state could be changed to PHY_NOLINK from MAC controller >>>>> resume >>>>>>>>> + * rounte with phy_start(), here change to PHY_UP after >>>>> re-initializing >>>>>>>>> + * PHY hardware, let PHY state machine to start/config >>>>>>>>> +auto-nego >>>>> again. >>>>>>>>> + */ >>>>>>>>> + phydev->state = PHY_UP; >>>>>>>>> + >>>>>>>>> no_resume: >>>>>>>>> if (phydev->attached_dev && phydev->adjust_link) >>>>>>>>> phy_start_machine(phydev); >>>>>>>>> >>>>>>>> >>>>>>> >>>>>> >>>>> >>>>> This is a quick draft of the modified soft reset for KSZ8081. >>>>> Some tests would be appreciated. >>>>> >>>> >>>> I test this patch at my side, unfortunately, it still can't work. >>>> >>>> I have not found what soft reset would affect in 8081 spec, is there >>>> ang common Description for different PHYs? >>>> >>> >>> You can check the clause 22 spec: 802.3 22.2.4.1.1 >>> >>> Apart from that you can check BMCR value and which modes your PHY >>> advertises after a soft reset. If the PHY indeed wouldn't restart aneg >>> after a >>> soft reset, then it would be the only one with this behavior I know. And I'd >>> wonder why there aren't more such reports. >> >> Hi Heiner, >> >> We have reasons to believe it is a weird behavior of KSZ8081. I have another >> two PHYs at my side, AR8031 and RTL8211FD, >> they can work fine if soft_reset implemented. >> >> As commit message described, phy_start() would change PHY state to PHY_UP >> finally to PHY_NOLINK when MAC resume. >> After MDIO bus resume back, it will periodically check link status. I did >> below code change to dump all PHY registers. >> >> --- a/drivers/net/phy/phy.c >> +++ b/drivers/net/phy/phy.c >> @@ -35,7 +35,7 @@ >> #include <net/genetlink.h> >> #include <net/sock.h> >> >> -#define PHY_STATE_TIME HZ >> +#define PHY_STATE_TIME (10 * HZ) >> >> #define PHY_STATE_STR(_state) \ >> case PHY_##_state: \ >> @@ -738,6 +738,28 @@ static int phy_check_link_status(struct phy_device >> *phydev) >> if (err) >> return err; >> >> + printk("offset 0x00 data = %0x\n", phy_read(phydev, 0x00)); >> + printk("offset 0x01 data = %0x\n", phy_read(phydev, 0x01)); >> + printk("offset 0x02 data = %0x\n", phy_read(phydev, 0x02)); >> + printk("offset 0x03 data = %0x\n", phy_read(phydev, 0x03)); >> + printk("offset 0x04 data = %0x\n", phy_read(phydev, 0x04)); >> + printk("offset 0x05 data = %0x\n", phy_read(phydev, 0x05)); >> + printk("offset 0x06 data = %0x\n", phy_read(phydev, 0x06)); >> + printk("offset 0x07 data = %0x\n", phy_read(phydev, 0x07)); >> + printk("offset 0x08 data = %0x\n", phy_read(phydev, 0x08)); >> + printk("offset 0x09 data = %0x\n", phy_read(phydev, 0x09)); >> + printk("offset 0x10 data = %0x\n", phy_read(phydev, 0x10)); >> + printk("offset 0x11 data = %0x\n", phy_read(phydev, 0x11)); >> + printk("offset 0x15 data = %0x\n", phy_read(phydev, 0x15)); >> + printk("offset 0x16 data = %0x\n", phy_read(phydev, 0x16)); >> + printk("offset 0x17 data = %0x\n", phy_read(phydev, 0x17)); >> + printk("offset 0x18 data = %0x\n", phy_read(phydev, 0x18)); >> + printk("offset 0x1b data = %0x\n", phy_read(phydev, 0x1b)); >> + printk("offset 0x1c data = %0x\n", phy_read(phydev, 0x1c)); >> + printk("offset 0x1d data = %0x\n", phy_read(phydev, 0x1d)); >> + printk("offset 0x1e data = %0x\n", phy_read(phydev, 0x1e)); >> + printk("offset 0x1f data = %0x\n", phy_read(phydev, 0x1f)); >> + printk("\n\n"); >> if (phydev->link && phydev->state != PHY_RUNNING) { >> phy_check_downshift(phydev); >> phydev->state = PHY_RUNNING; >> >> After MDIO bus resume back, results as below: >> >> [ 119.545383] offset 0x00 data = 1100 >> [ 119.549237] offset 0x01 data = 7849 >> [ 119.563125] offset 0x02 data = 22 >> [ 119.566810] offset 0x03 data = 1560 >> [ 119.570597] offset 0x04 data = 85e1 >> [ 119.588016] offset 0x05 data = 0 >> [ 119.592891] offset 0x06 data = 4 >> [ 119.596452] offset 0x07 data = 2001 >> [ 119.600233] offset 0x08 data = 0 >> [ 119.617864] offset 0x09 data = 0 >> [ 119.625990] offset 0x10 data = 0 >> [ 119.629576] offset 0x11 data = 0 >> [ 119.642735] offset 0x15 data = 0 >> [ 119.646332] offset 0x16 data = 202 >> [ 119.650030] offset 0x17 data = 5c02 >> [ 119.668054] offset 0x18 data = 801 >> [ 119.672997] offset 0x1b data = 0 >> [ 119.676565] offset 0x1c data = 0 >> [ 119.680084] offset 0x1d data = 0 >> [ 119.698031] offset 0x1e data = 20 >> [ 119.706246] offset 0x1f data = 8190 >> [ 119.709984] >> [ 119.709984] >> [ 120.182120] offset 0x00 data = 100 >> [ 120.185894] offset 0x01 data = 784d >> [ 120.189681] offset 0x02 data = 22 >> [ 120.206319] offset 0x03 data = 1560 >> [ 120.210171] offset 0x04 data = 8061 >> [ 120.225353] offset 0x05 data = 0 >> [ 120.228948] offset 0x06 data = 4 >> [ 120.242936] offset 0x07 data = 2001 >> [ 120.246792] offset 0x08 data = 0 >> [ 120.250313] offset 0x09 data = 0 >> [ 120.266748] offset 0x10 data = 0 >> [ 120.270335] offset 0x11 data = 0 >> [ 120.284167] offset 0x15 data = 0 >> [ 120.287760] offset 0x16 data = 202 >> [ 120.301031] offset 0x17 data = 3c02 >> [ 120.313209] offset 0x18 data = 801 >> [ 120.316983] offset 0x1b data = 0 >> [ 120.320513] offset 0x1c data = 1 >> [ 120.336589] offset 0x1d data = 0 >> [ 120.340184] offset 0x1e data = 115 >> [ 120.355357] offset 0x1f data = 8190 >> [ 120.359112] >> [ 120.359112] >> [ 129.785396] offset 0x00 data = 1100 >> [ 129.789252] offset 0x01 data = 7849 >> [ 129.809951] offset 0x02 data = 22 >> [ 129.815018] offset 0x03 data = 1560 >> [ 129.818845] offset 0x04 data = 85e1 >> [ 129.835808] offset 0x05 data = 0 >> [ 129.839398] offset 0x06 data = 4 >> [ 129.854514] offset 0x07 data = 2001 >> [ 129.858371] offset 0x08 data = 0 >> [ 129.873357] offset 0x09 data = 0 >> [ 129.876954] offset 0x10 data = 0 >> [ 129.880472] offset 0x11 data = 0 >> [ 129.896450] offset 0x15 data = 0 >> [ 129.900038] offset 0x16 data = 202 >> [ 129.915041] offset 0x17 data = 5c02 >> [ 129.918889] offset 0x18 data = 801 >> [ 129.932735] offset 0x1b data = 0 >> [ 129.946830] offset 0x1c data = 0 >> [ 129.950424] offset 0x1d data = 0 >> [ 129.964585] offset 0x1e data = 0 >> [ 129.968192] offset 0x1f data = 8190 >> [ 129.972938] >> [ 129.972938] >> [ 130.425125] offset 0x00 data = 100 >> [ 130.428889] offset 0x01 data = 784d >> [ 130.442671] offset 0x02 data = 22 >> [ 130.446360] offset 0x03 data = 1560 >> [ 130.450142] offset 0x04 data = 8061 >> [ 130.467207] offset 0x05 data = 0 >> [ 130.470789] offset 0x06 data = 4 >> [ 130.485071] offset 0x07 data = 2001 >> [ 130.488934] offset 0x08 data = 0 >> [ 130.504320] offset 0x09 data = 0 >> [ 130.507911] offset 0x10 data = 0 >> [ 130.520950] offset 0x11 data = 0 >> [ 130.532865] offset 0x15 data = 0 >> [ 130.536461] offset 0x16 data = 202 >> [ 130.540156] offset 0x17 data = 3c02 >> [ 130.557218] offset 0x18 data = 801 >> [ 130.560987] offset 0x1b data = 0 >> [ 130.575158] offset 0x1c data = 1 >> [ 130.578754] offset 0x1d data = 0 >> [ 130.593543] offset 0x1e data = 115 >> [ 130.597312] offset 0x1f data = 8190 >> >> We can see that BMCR and BMSR changes from 0x1100,0x7849 to 0x100,0x784d >> (BMCR[12] bit and BMSR[2]), and loop it. >> Above process is auto-nego enabled, link is down, auto-nego is disabled, >> link is up. And auto-nego complete bit is always 0. >> >> PHY seems become unstable if soft reset after start/config auto-nego. I also >> want to fix it in micrel driver, but failed. >> > > Waiting for ANEG_COMPLETE to be set wouldn't be a good option. Aneg may never > complete for different reasons, e.g. no physical link. And even if we use a > timeout this may add unwanted delays. > >> Do you have any other insights that can help me further locate the issue? >> Thanks. >> > > I think current MAC/PHY PM handling isn't perfect. Often we have the following > scenario: > > *suspend* > 1. PHY is suspended (mdio_bus_phy_suspend) > 2. MAC suspend callback (typically involving phy_stop()) > > *resume* > 1. MAC resume callback (typically involving phy_start()) > 2. PHY is resumed (mdio_bus_phy_resume), incl. calling phy_init_hw() > > Calling phy_init_hw() after phy_start() doesn't look right. > It seems to work in most cases, but there's a certain risk > that phy_init_hw() overwrites something, e.g. the advertised > modes. > I think we have two valid scenarios: > > 1. phylib PM callbacks are used, then the MAC driver shouldn't > touch the PHY in its PM callbacks, especially not call > phy_stop/phy_start. > > 2. MAC PM callbacks take care also of the PHY. Then I think we would > need a flag at the phy_device telling it to make the PHY PM > callbacks a no-op. > > Andrew, Russell: What do you think? > >> Best Regards, >> Joakim Zhang >> >> [...] >> > Heiner >
Based on scenario 1 you can also try the following. diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index 3db882322..cdf9160fc 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -3805,7 +3805,6 @@ static int __maybe_unused fec_suspend(struct device *dev) if (netif_running(ndev)) { if (fep->wol_flag & FEC_WOL_FLAG_ENABLE) fep->wol_flag |= FEC_WOL_FLAG_SLEEP_ON; - phy_stop(ndev->phydev); napi_disable(&fep->napi); netif_tx_lock_bh(ndev); netif_device_detach(ndev); @@ -3864,7 +3863,6 @@ static int __maybe_unused fec_resume(struct device *dev) netif_device_attach(ndev); netif_tx_unlock_bh(ndev); napi_enable(&fep->napi); - phy_start(ndev->phydev); } rtnl_unlock(); -- 2.31.1