Subject: Re: Micrel Phy - Is there a way to configure the Phy not to do 802.3x flow control?

From: Murali Karicheri <[email-masked]>
Date: Wed, 16 Mar 2016 11:16:25 -0400

On 03/16/2016 11:08 AM, Murali Karicheri wrote:
> On 03/11/2016 02:51 PM, Florian Fainelli wrote:
>> On 11/03/16 10:31, Murali Karicheri wrote:
>>> On 03/10/2016 02:38 PM, Murali Karicheri wrote:
>>>> On 03/10/2016 01:05 PM, Florian Fainelli wrote:
>>>>> On 10/03/16 08:48, Murali Karicheri wrote:
>>>>>> On 03/03/2016 07:16 PM, Florian Fainelli wrote:
>>>>>>> On 03/03/16 14:18, Murali Karicheri wrote:
>>>>>>>> Hi,
>>>>>>>>
>>>>>>>> We are using Micrel Phy in one of our board and wondering if we can force the
>>>>>>>> Phy to disable flow control at start. I have a 1G ethernet switch connected
>>>>>>>> to Phy and the phy always enable flow control. I would like to configure the
>>>>>>>> phy not to flow control. Is that possible and if yes, what should I do in the
>>>>>>>> my Ethernet driver to tell the Phy not to enable flow control?
>>>>>>>
>>>>>>> The PHY is not doing flow control per-se, your pseudo Ethernet MAC in
>>>>>>> the switch is doing, along with the link partner advertising support for
>>>>>>> it. You would want to make sure that your PHY device interface (provided
>>>>>>> that you are using the PHY library) is not starting with Pause
>>>>>>> advertised, but it could be supported.
>>>>>>
>>>>>> Understood that Phy is just advertise FC. The Micrel phy for 9031 advertise
>>>>>> by default FC supported. After negotiation, I see that Phylib provide the 
>>>>>> link status with parameter pause = 1, asym_pause = 1. How do I tell the Phy not
>>>>>> to advertise?
>>>>>>
>>>>>> I call following sequence in the Ethernet driver.
>>>>>>
>>>>>> of_phy_connect(x,y,hndlr,a,z);
>>>>>
>>>>> Here you should be able to change phydev->advertising and
>>>>> phydev->supported to mask the ADVERTISED_Pause | ADVERTISED_AsymPause
>>>>> bits and have phy_start() restart with that which should disable pause
>>>>> and asym_pause as seen by your adjust_link handler.
>>>>>
>>>> Ok. Good point. I will try this. Thanks for your suggestion.
>>>>
>>> I had to make following changes to the phy_device.c to allow the phy device
>>> report maximum common flow control capability to Ethernet driver through
>>> handler. My driver code looks like this.
>>>
>>>                 slave->phy = of_phy_connect(gbe_intf->ndev,
>>>                                             slave->phy_node,
>>>                                             hndlr, 0,
>>>                                             phy_mode);
>>>                 if (!slave->phy) {
>>>                         dev_err(priv->dev, "phy not found on slave %d\n",
>>>                                 slave->slave_num);
>>>                         return -ENODEV;
>>>                 }
>>>                 dev_dbg(priv->dev, "phy found: id is: 0x%s\n",
>>>                         dev_name(&slave->phy->dev));
>>>
>>>                 slave->phy->supported &=
>>>                                 ~(SUPPORTED_Pause | SUPPORTED_Asym_Pause);
>>>                 slave->phy->advertising = slave->phy->supported;
>>>                 phy_start(slave->phy);
>>>                 phy_read_status(slave->phy);
>>>
>>> And then in the phy_device.c, I did to get flow control off reported in
>>> handler for link status update.
>>
>>
>>
>>>
>>> diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
>>> index d551df6..55412ad 100644
>>> --- a/drivers/net/phy/phy_device.c
>>> +++ b/drivers/net/phy/phy_device.c
>>> @@ -1021,8 +1021,8 @@ int genphy_read_status(struct phy_device *phydev)
>>>                                 phydev->duplex = DUPLEX_FULL;
>>>  
>>>                 if (phydev->duplex == DUPLEX_FULL) {
>>> -                       phydev->pause = lpa & LPA_PAUSE_CAP ? 1 : 0;
>>> -                       phydev->asym_pause = lpa & LPA_PAUSE_ASYM ? 1 : 0;
>>> +                       phydev->pause = adv & lpa & LPA_PAUSE_CAP ? 1 : 0;
>>> +                       phydev->asym_pause = adv & lpa & LPA_PAUSE_ASYM ? 1 : 0;
>>
>> What it means before your patch is that flow control is reported to the
>> PHY device if the link partner advertises that, with your patch applied,
>> it is reported only if the link partner and yourself advertise flow control.
>>
>> You seem to be willing to have phydev->pause and phydev->asym_pause
>> reflect the resolved pause capability, as opposed to the link partner's
>> pause capability, which I am not convinced is correct here, because we
>> need to take into account the user-configured pause configuration as
>> well. Your adjust_link function should be the one deciding whether pause
>> frame advertising and enabling is appropriate based on: locally
>> configured pause settings (enabled, disabled, autoneg) and the link
>> partner's pause capability.
>>
>> I do agree that the two fields are confusing and poorly documented, and
>> we should probably be consolidating the pause frame behavior in PHYLIB
>> as opposed to letting drivers deal with like e.g: gianfar, bcm63xx_enet,
>> tg3 etc.
>>
>>>  
>>> Could you explain, why the common maximum capability is not reported to the
>>> driver as per standard?? Or Am I understood it wrong?
>>
>> I do not understand the question, what is "maximum capability" in that
>> context and what standard are you refering to?
>>
> I assume the Phylib is responsible for deciding what is the flow
> control pause and asym pause status to the driver through adjust_link.
> When driver starts the phy, it also tells its capabilities (for example
> it can reset some of the capabilities available in the Phy driver. In this
> particular case, Pause is a feature supported by Micrel phy. I have reset
> this feature in my driver by resetting this feature bit in the phy_device
> as suggested earlier in this discussion). So phylib has all knowledge
> available to disable flow control in this scenario even if LP is capable
> of flow control. Wondering why every driver has to take this decision
> again to enable or disable flow control instead of telling the driver
> what to do. 
What I meant is if driver tells phy not advertise, then adjust_link should
reflect the same. i.e both pause and asym_pause should be off.

Murali
> 
> Looks like Marvel driver (reproduced below) does this logic. I think the
> 802.3x flow control states, but I don't have access to the standard documentation.
> However I find the documentation at 
> http://www.studioreti.it/slide/08_SwFlowContr_E_A.pdf.
> 
> Page 17 states the behavior based on Local device & Link partner's 
> capabilities. Probably adjust_link() should tell the outcome in pause
> and asym_pause so that driver can enable/disable fc. Also user's action
> to be taken into account as well so that fc can be disabled if desired.
> 
> Code from drivers/net/phy/marvel.c
> 
> static int marvell_read_status(struct phy_device *phydev)
> {
> 
>         int adv;
>         int err;
>         int lpa;
>         int lpagb;
>         int status = 0;
> 
>         /* Update the link, but return if there
>          * was an error */
>         err = genphy_update_link(phydev);
>         if (err)
>                 return err;
> 
>         if (AUTONEG_ENABLE == phydev->autoneg) {
>                 status = phy_read(phydev, MII_M1011_PHY_STATUS);
>                 if (status < 0)
>                         return status;
> 
>                 lpa = phy_read(phydev, MII_LPA);
>                 if (lpa < 0)
>                         return lpa;
> 
>                 lpagb = phy_read(phydev, MII_STAT1000);
>                 if (lpagb < 0)
>                         return lpagb;
> 
>                 adv = phy_read(phydev, MII_ADVERTISE);
>                 if (adv < 0)
>                         return adv;
> 
>                 phydev->lp_advertising = mii_stat1000_to_ethtool_lpa_t(lpagb) |
>                                          mii_lpa_to_ethtool_lpa_t(lpa);
> 
>                 lpa &= adv;
> 
>                 if (status & MII_M1011_PHY_STATUS_FULLDUPLEX        int adv;
>         int err;
>         int lpa;
>         int lpagb;
>         int status = 0;
> 
>         /* Update the link, but return if there
>          * was an error */
>         err = genphy_update_link(phydev);
>         if (err)
>                 return err;
> )
>                         phydev->duplex = DUPLEX_FULL;
>                 else
>                         phydev->duplex = DUPLEX_HALF;
> 
>                 status = status & MII_M1011_PHY_STATUS_SPD_MASK;
>                 phydev->pause = phydev->asym_pause = 0;
> 
>                 switch (status) {
>                 case MII_M1011_PHY_STATUS_1000:
>                         phydev->speed = SPEED_1000;
>                         break;
> 
>                 case MII_M1011_PHY_STATUS_100:
>                         phydev->speed = SPEED_100;
>                         break;
> 
>                 default:
>                         phydev->speed = SPEED_10;
> 
>                         break;
>                 }
> 
>                 if (phydev->duplex == DUPLEX_FULL) {
>                         phydev->pause = lpa & LPA_PAUSE_CAP ? 1 : 0;
>                         phydev->asym_pause = lpa & LPA_PAUSE_ASYM ? 1 : 0;
>                 }
>         } else {
>                 int bmcr = phy_read(phydev, MII_BMCR);
> 
>                 if (bmcr < 0)
>                         return bmcr;
> 
>                 if (bmcr & BMCR_FULLDPLX)
>                         phydev->duplex = DUPLEX_FULL;
>                 else
>                         phydev->duplex = DUPLEX_HALF;
> 
>                 if (bmcr & BMCR_SPEED1000)
>                         phydev->speed = SPEED_1000;
>                 else if (bmcr & BMCR_SPEED100)
>                         phydev->speed = SPEED_100;
>                 else
>                         phydev->speed = SPEED_10;
> 
>                 phydev->pause = phydev->asym_pause = 0;
>                 phydev->lp_advertising = 0;
>         }
> 
>         return 0;
> }
> 
> 

-- 
Murali Karicheri
Linux Kernel, Keystone