mvneta: SGMII fixed-link not so fixed

Russell King - ARM Linux linux at arm.linux.org.uk
Fri Sep 18 06:12:57 PDT 2015


On Fri, Sep 18, 2015 at 03:43:54PM +0300, Stas Sergeev wrote:
> 18.09.2015 15:13, Russell King - ARM Linux пишет:
> > On Fri, Sep 18, 2015 at 02:29:34PM +0300, Stas Sergeev wrote:
> >> 18.09.2015 02:14, Russell King - ARM Linux пишет:
> >>>  _But_ using the in-band status
> >>>    property fundamentally requires this for mvneta to behave correctly:
> >>>
> >>> 		phy-mode = "sgmii";
> >>> 		managed = "in-band-status";
> >>> 		fixed-link {
> >>> 		};
> >>>
> >>>    with _no_ phy node.
> >> I don't understand this one.
> >> At least for me it works without empty fixed-link.
> >> There may be some bug.
> > 
> >         if (cause_rx_tx & MVNETA_MISCINTR_INTR_MASK) {
> >                 u32 cause_misc = mvreg_read(pp, MVNETA_INTR_MISC_CAUSE);
> > 
> >                 mvreg_write(pp, MVNETA_INTR_MISC_CAUSE, 0);
> >                 if (pp->use_inband_status && (cause_misc &
> >                                 (MVNETA_CAUSE_PHY_STATUS_CHANGE |
> >                                  MVNETA_CAUSE_LINK_CHANGE |
> >                                  MVNETA_CAUSE_PSC_SYNC_CHANGE))) {
> >                         mvneta_fixed_link_update(pp, pp->phy_dev);
> >                 }
> > 
> > pp->use_inband_status is set when managed = "in-band-status" is set.
> > We detect changes in the in-band status, and call mvneta_fixed_link_update():
> > 
> > mvneta_fixed_link_update() reads the status, and communicates that into
> > the fixed-link phy:
> > 
> >         u32 gmac_stat = mvreg_read(pp, MVNETA_GMAC_STATUS);
> > 
> > 	... code setting status.* values from gmac_stat ...
> >         changed.link = 1;
> >         changed.speed = 1;
> >         changed.duplex = 1;
> > 	fixed_phy_update_state(phy, &status, &changed);
> > 
> > fixed_phy_update_state() then looks up the phy in its list, comparing only
> > the address:
> > 
> >         if (!phydev || !phydev->bus)
> >                 return -EINVAL;
> > 
> >         list_for_each_entry(fp, &fmb->phys, node) {
> >                 if (fp->addr == phydev->addr) {
> > 
> > updating fp->* with the new state, calling fixed_phy_update_regs().  This
> > updates the fixed-link phy emulated registers, and phylib then notices
> > the change in link status, and notifies the netdevice attached to the
> > PHY it found of the change.
> > 
> > Now, one of two things happens as a result of this:
> > 
> > 1. If pp->phy_dev is a fixed-link phy, this finds the correct fixed-link
> >    phy to update its "fixed-link" properties, and the "not so fixed" phy
> >    changes its parameters according to the new status.
> > 
> > 2. If pp->phy_dev is a MDIO phy which matches the address of a fixed-link
> >    phy,
> Doesn't the above loop iterates only "fixed_mdio_bus platform_fmb"?
> I don't think MDIO PHYs can appear there. If they can - the bug is
> very nasty. Have you seen exactly when/why that happens?

I think I explained it fully - please follow the code paths I've detailed
above.

Specifically, look at this code:

         if (!phydev || !phydev->bus)
                 return -EINVAL;

         list_for_each_entry(fp, &fmb->phys, node) {
                 if (fp->addr == phydev->addr) {

Consider what the effect is if you have a MDIO phy at address 0 on eth0
which has in-band-status enabled.

Now if you have a fixed-link phy on the fixed-link bus at address 0
connected to eth1.

Bring eth1 up, everything works as one would expect, it gains the fixed
link settings.

Now bring eth0 up.  Because there's no distinction in mvneta between a
fixed-link phy and a MDIO phy, it ends up calling fixed_phy_update_state()
with the MDIO phy, which has phydev->addr = 0.

The fixed-link code scans every phy on the fixed-link bus, looking for
one with address 0, and it finds that, and modifies the state of that
phy.

eth1 now gains the settings that eth0 communicated into the fixed-link
phy layer.

> > Now, a fixed-link phy is only created in mvneta when there is no MDIO phy
> > specified, but when there is a fixed-link specification in DT:
> > 
> >         phy_node = of_parse_phandle(dn, "phy", 0);
> >         if (!phy_node) {
> >                 if (!of_phy_is_fixed_link(dn)) {
> >                         dev_err(&pdev->dev, "no PHY specified\n");
> >                         err = -ENODEV;
> >                         goto err_free_irq;
> >                 }
> > 
> >                 err = of_phy_register_fixed_link(dn);
> >                 if (err < 0) {
> >                         dev_err(&pdev->dev, "cannot register fixed PHY\n");
> >                         goto err_free_irq;
> >                 }
> > 
> > If there's neither a MDIO PHY nor a fixed-link, then the network driver
> > fails to initialise the device.
> But it does.

Please, look again at the code I quoted above.

> In fact, of_mdio.c has this code:
> 
>         err = of_property_read_string(np, "managed", &managed);
>         if (err == 0) {
>                 if (strcmp(managed, "in-band-status") == 0) {
>                         /* status is zeroed, namely its .link member */
>                         phy = fixed_phy_register(PHY_POLL, &status, np);
>                         return IS_ERR(phy) ? PTR_ERR(phy) : 0;
>                 }
>         }
> 
> Which is exactly for the case you describe.

That code is in of_phy_register_fixed_link().  That code will _NOT_ be
reached if a MDIO phy is specified.  Again, please read the code.

> >>> What I don't know is how many generations of the mvneta hardware have
> >>> support for both serdes modes, but what I'm basically saying is that
> >>> the solution we now have seems to be somewhat lacking - maybe it should
> >>> have been "auto", "in-band-sgmii" and "in-band-1000base-x" with the
> >>> ability to add additional modes later.
> >>
> >> Well, you need to be quick with such a change, esp now when the patch
> >> was queued to -stable.
> >> What alternatives do we have here?
> >> Will the following work too?
> >>  		phy-mode = "1000base-x";
> >>  		managed = "in-band-status";
> > 
> > There's no chance of being "quick" here - the issues are complex and not
> > trivial to solve in a day - I've already spent all week thinking about
> > the issues here, and I'm only _just_ starting to come up with a potential
> > solution this morning, though I haven't yet assessed whether it'll be
> > feasible.
> > 
> > The problem I have with the above is that it fixes the phy mode to either
> > SGMII or 1000base-X, whereas what we need for the SFP case is to have the
> > down-link object tell the MAC driver whether it wants SGMII or 1000base-X
> > mode.
> Not that I understand that SFP business at all.
> Maybe if some downlink tells the MAC what autoneg protocol will
> be used, you can have:
>   		phy-mode = "1000base-x" | "sgmii" | "serdes-auto";
>   		managed = "in-band-status";
> 
> and "serdes-auto" will use either "1000base-x" or "sgmii", depending
> on what the downlink says?

Maybe, but rather than guessing and getting it wrong, let's wait until
we know what kind of a solution is necessary here.  Rushing this will
only create another design mistake and an even larger can of worms.

-- 
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.



More information about the linux-arm-kernel mailing list