mvneta: SGMII fixed-link not so fixed

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


On Fri, Sep 18, 2015 at 04:43:59PM +0300, Stas Sergeev wrote:
> 18.09.2015 16:12, Russell King - ARM Linux пишет:
> > 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.
> I try. What I don't understand is why both PHYs get the
> same address on the "Fixed MDIO bus".

They aren't both on the fixed MDIO bus - that's the whole point I'm
making.  They get the same phydev->addr but on _different_ buses.

> > Specifically, look at this code:
> > 
> >          if (!phydev || !phydev->bus)
> >                  return -EINVAL;
> > 
> >          list_for_each_entry(fp, &fmb->phys, node) {
> >                  if (fp->addr == phydev->addr) {

Look at this closely - at what point is there any validation that "phydev"
is actually a fixed-link phy?  There is no validation done.  The only
criteria there are:
- phydev is not NULL
- phydev->bus is not NULL (which is true of any registered phy)
- phydev->addr matches _any_ fixed-link phy.

I've already sent a patch earlier today to address this issue.

If you place a WARN_ON(fp->phydev != phydev) then that'll show you
when it incorrectly matches.

> > Consider what the effect is if you have a MDIO phy at address 0 on eth0
> > which has in-band-status enabled.
> So as I understand, you have MDIO phy with DT looking like this:
> ethernet at 70000 {
>   status = "okay";
>   phy-mode = "sgmii";
>   managed = "in-band-status";
> }
> W/O either "phy" of "fixed-link" nodes. Correct?
> mvneta calls of_phy_register_fixed_link(dn) on it after not
> finding the "phy" node. And it will do the same with the second
> non-MDIO phy. What I don't see is how do they get the same addr
> on the same bus, could you please clarify that a bit?

		mdio at 72004 {
			phy_dedicated: ethernet-phy at 0 {
				reg = <0>;
			};
		};

		eth1: ethernet at 30000 {
			phy = <&phy_dedicated>;
			phy-mode = "sgmii";
			managed = "in-band-status";
		};

		eth0: ethernet at 70000 {
			phy-mode = "sgmii";
			fixed-link {
				speed = 1000;
				full-duplex;
			};
		};

Bring eth0 up first, everything works.  Then, bring eth1 up, and eth0
goes down.

This happens because when eth1 is brought up, eth1's mvneta calls into
fixed_phy_update_state() with a pointer to the "phy_dedicated" PHY at
address 0.  fixed_phy_update_state() scans the fixed-link phys for one
at address 0, and finds the fixed-link phy associated with eth0.
This causes the fixed link code to change the settings for eth0.

As I have already shown in my previous setup diagrams, it is _entirely_
reasonable to use in-band status with SGMII with a phy attached.

-- 
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