diff mbox series

[net,6/6] net: dsa: mv88e6xxx: Link in pcs_get_state() if AN is bypassed

Message ID 20211129195823.11766-7-kabel@kernel.org (mailing list archive)
State Superseded
Delegated to: Netdev Maintainers
Headers show
Series mv88e6xxx fixes (mainly 88E6393X family) | expand

Checks

Context Check Description
netdev/tree_selection success Clearly marked for net
netdev/fixes_present success Fixes tag present in non-next series
netdev/subject_prefix success Link
netdev/cover_letter success Series has a cover letter
netdev/patch_count success Link
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 0 this patch: 0
netdev/cc_maintainers fail 1 blamed authors not CCed: linux@armlinux.org.uk; 4 maintainers not CCed: linux@armlinux.org.uk olteanv@gmail.com f.fainelli@gmail.com vivien.didelot@gmail.com
netdev/build_clang success Errors and warnings before: 0 this patch: 0
netdev/module_param success Was 0 now: 0
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/verify_fixes success Fixes tag looks correct
netdev/build_allmodconfig_warn success Errors and warnings before: 0 this patch: 0
netdev/checkpatch success total: 0 errors, 0 warnings, 0 checks, 81 lines checked
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

Marek Behún Nov. 29, 2021, 7:58 p.m. UTC
Function mv88e6xxx_serdes_pcs_get_state() currently does not report link
up if AN is enabled, Link bit is set, but Speed and Duplex Resolved bit
is not set, which testing shows is the case for when auto-negotiation
was bypassed (we have enabled AN but link partner does not).

An example of such link partner is Marvell 88X3310 PHY, when put into
the mode where host interface changes between 10gbase-r, 5gbase-r,
2500base-x and sgmii according to copper speed. The 88X3310 does not
enable AN in 2500base-x, and so SerDes on mv88e6xxx currently does not
link with it.

Fix this.

Fixes: a5a6858b793f ("net: dsa: mv88e6xxx: extend phylink to Serdes PHYs")
Signed-off-by: Marek Behún <kabel@kernel.org>
---
 drivers/net/dsa/mv88e6xxx/serdes.c | 38 +++++++++++++++++++++++++-----
 1 file changed, 32 insertions(+), 6 deletions(-)

Comments

Russell King (Oracle) Nov. 29, 2021, 11:14 p.m. UTC | #1
On Mon, Nov 29, 2021 at 08:58:23PM +0100, Marek Behún wrote:
>  static int mv88e6xxx_serdes_pcs_get_state(struct mv88e6xxx_chip *chip,
> -					  u16 status, u16 lpa,
> +					  u16 ctrl, u16 status, u16 lpa,
>  					  struct phylink_link_state *state)
>  {
> +	state->link = !!(status & MV88E6390_SGMII_PHY_STATUS_LINK);
> +
>  	if (status & MV88E6390_SGMII_PHY_STATUS_SPD_DPL_VALID) {
> -		state->link = !!(status & MV88E6390_SGMII_PHY_STATUS_LINK);
> +		state->an_complete = !!(ctrl & BMCR_ANENABLE);

I think I'd much rather report the value of BMSR_ANEGCAPABLE - since
an_complete controls the BMSR_ANEGCAPABLE bit in the emulated PHY
that userspace sees. Otherwise, an_complete is not used.

state->link is the key that phylink uses to know whether it can
trust the status being reported.
Marek Behún Nov. 30, 2021, 12:18 a.m. UTC | #2
On Mon, 29 Nov 2021 23:14:17 +0000
"Russell King (Oracle)" <linux@armlinux.org.uk> wrote:

> On Mon, Nov 29, 2021 at 08:58:23PM +0100, Marek Behún wrote:
> >  static int mv88e6xxx_serdes_pcs_get_state(struct mv88e6xxx_chip *chip,
> > -					  u16 status, u16 lpa,
> > +					  u16 ctrl, u16 status, u16 lpa,
> >  					  struct phylink_link_state *state)
> >  {
> > +	state->link = !!(status & MV88E6390_SGMII_PHY_STATUS_LINK);
> > +
> >  	if (status & MV88E6390_SGMII_PHY_STATUS_SPD_DPL_VALID) {
> > -		state->link = !!(status & MV88E6390_SGMII_PHY_STATUS_LINK);
> > +		state->an_complete = !!(ctrl & BMCR_ANENABLE);  
> 
> I think I'd much rather report the value of BMSR_ANEGCAPABLE - since
> an_complete controls the BMSR_ANEGCAPABLE bit in the emulated PHY
> that userspace sees. Otherwise, an_complete is not used.
> 
> state->link is the key that phylink uses to know whether it can
> trust the status being reported.

Isn't BMSR_ANEGCAPABLE set to 1 even if aneg is disabled in BMCR?
I will test tomorrow.

The reason why I used BMCR_ANENABLE is that if we don't enable AN, the
PHY will report SPD_DPL_VALID if link is up. But clearly AN is not
completed in that case, because it was never enabled in the first place.

Marek
Marek Behún Nov. 30, 2021, 4:09 p.m. UTC | #3
On Tue, 30 Nov 2021 01:18:12 +0100
Marek Behún <kabel@kernel.org> wrote:

> On Mon, 29 Nov 2021 23:14:17 +0000
> "Russell King (Oracle)" <linux@armlinux.org.uk> wrote:
> 
> > On Mon, Nov 29, 2021 at 08:58:23PM +0100, Marek Behún wrote:  
> > >  static int mv88e6xxx_serdes_pcs_get_state(struct mv88e6xxx_chip *chip,
> > > -					  u16 status, u16 lpa,
> > > +					  u16 ctrl, u16 status, u16 lpa,
> > >  					  struct phylink_link_state *state)
> > >  {
> > > +	state->link = !!(status & MV88E6390_SGMII_PHY_STATUS_LINK);
> > > +
> > >  	if (status & MV88E6390_SGMII_PHY_STATUS_SPD_DPL_VALID) {
> > > -		state->link = !!(status & MV88E6390_SGMII_PHY_STATUS_LINK);
> > > +		state->an_complete = !!(ctrl & BMCR_ANENABLE);    
> > 
> > I think I'd much rather report the value of BMSR_ANEGCAPABLE - since
> > an_complete controls the BMSR_ANEGCAPABLE bit in the emulated PHY
> > that userspace sees. Otherwise, an_complete is not used.
> > 
> > state->link is the key that phylink uses to know whether it can
> > trust the status being reported.  
> 
> Isn't BMSR_ANEGCAPABLE set to 1 even if aneg is disabled in BMCR?
> I will test tomorrow.
> 
> The reason why I used BMCR_ANENABLE is that if we don't enable AN, the
> PHY will report SPD_DPL_VALID if link is up. But clearly AN is not
> completed in that case, because it was never enabled in the first place.

Seems that BMSR_ANEGCAPABLE is set even if AN is disabled in BMCR.

If phylink_autoneg_inband() is not true mv88e6*_serdes_pcs_config(),
then we aren't enabling AN. But in this case SPD_DPL_VALID is always 1,
so if we use, as you say, an_complete=BMSR_ANEGCAPABLE in
mv88e6xxx_serdes_pcs_get_state(), we will always set an_complete=true, even
if AN wasn't enabled.

I was under the impression that
  state->an_complete
should only be set to true if AN is enabled.

Marek
Russell King (Oracle) Nov. 30, 2021, 4:15 p.m. UTC | #4
On Tue, Nov 30, 2021 at 05:09:11PM +0100, Marek Behún wrote:
> Seems that BMSR_ANEGCAPABLE is set even if AN is disabled in BMCR.

Hmm, that behaviour goes against 22.2.4.2.10:

A PHY shall return a value of zero in bit 1.5 if Auto-Negotiation is
disabled by clearing bit 0.12. A PHY shall also return a value of zero
in bit 1.5 if it lacks the ability to perform Auto-Negotiation.

> I was under the impression that
>   state->an_complete
> should only be set to true if AN is enabled.

Yes - however as you've stated, the PHY doesn't follow 802.3 behaviour
so I guess we should make the emulation appear compliant by fixing it
like this.
Marek Behún Nov. 30, 2021, 4:18 p.m. UTC | #5
On Tue, 30 Nov 2021 16:15:50 +0000
"Russell King (Oracle)" <linux@armlinux.org.uk> wrote:

> On Tue, Nov 30, 2021 at 05:09:11PM +0100, Marek Behún wrote:
> > Seems that BMSR_ANEGCAPABLE is set even if AN is disabled in BMCR.  
> 
> Hmm, that behaviour goes against 22.2.4.2.10:
> 
> A PHY shall return a value of zero in bit 1.5 if Auto-Negotiation is
> disabled by clearing bit 0.12. A PHY shall also return a value of zero
> in bit 1.5 if it lacks the ability to perform Auto-Negotiation.
> 
> > I was under the impression that
> >   state->an_complete
> > should only be set to true if AN is enabled.  
> 
> Yes - however as you've stated, the PHY doesn't follow 802.3 behaviour
> so I guess we should make the emulation appear compliant by fixing it
> like this.

OK, I will use BMCR_ANENABLE and add a comment explaining that we can't
use BMSR_ANEGCAPABLE because the PHY violates standard. Would that be
okay?
Russell King (Oracle) Nov. 30, 2021, 4:22 p.m. UTC | #6
On Tue, Nov 30, 2021 at 05:18:59PM +0100, Marek Behún wrote:
> On Tue, 30 Nov 2021 16:15:50 +0000
> "Russell King (Oracle)" <linux@armlinux.org.uk> wrote:
> 
> > On Tue, Nov 30, 2021 at 05:09:11PM +0100, Marek Behún wrote:
> > > Seems that BMSR_ANEGCAPABLE is set even if AN is disabled in BMCR.  
> > 
> > Hmm, that behaviour goes against 22.2.4.2.10:
> > 
> > A PHY shall return a value of zero in bit 1.5 if Auto-Negotiation is
> > disabled by clearing bit 0.12. A PHY shall also return a value of zero
> > in bit 1.5 if it lacks the ability to perform Auto-Negotiation.
> > 
> > > I was under the impression that
> > >   state->an_complete
> > > should only be set to true if AN is enabled.  
> > 
> > Yes - however as you've stated, the PHY doesn't follow 802.3 behaviour
> > so I guess we should make the emulation appear compliant by fixing it
> > like this.
> 
> OK, I will use BMCR_ANENABLE and add a comment explaining that we can't
> use BMSR_ANEGCAPABLE because the PHY violates standard. Would that be
> okay?

Yes, thanks.
diff mbox series

Patch

diff --git a/drivers/net/dsa/mv88e6xxx/serdes.c b/drivers/net/dsa/mv88e6xxx/serdes.c
index d297131afe21..a145598905a5 100644
--- a/drivers/net/dsa/mv88e6xxx/serdes.c
+++ b/drivers/net/dsa/mv88e6xxx/serdes.c
@@ -50,11 +50,13 @@  static int mv88e6390_serdes_write(struct mv88e6xxx_chip *chip,
 }
 
 static int mv88e6xxx_serdes_pcs_get_state(struct mv88e6xxx_chip *chip,
-					  u16 status, u16 lpa,
+					  u16 ctrl, u16 status, u16 lpa,
 					  struct phylink_link_state *state)
 {
+	state->link = !!(status & MV88E6390_SGMII_PHY_STATUS_LINK);
+
 	if (status & MV88E6390_SGMII_PHY_STATUS_SPD_DPL_VALID) {
-		state->link = !!(status & MV88E6390_SGMII_PHY_STATUS_LINK);
+		state->an_complete = !!(ctrl & BMCR_ANENABLE);
 		state->duplex = status &
 				MV88E6390_SGMII_PHY_STATUS_DUPLEX_FULL ?
 			                         DUPLEX_FULL : DUPLEX_HALF;
@@ -81,6 +83,17 @@  static int mv88e6xxx_serdes_pcs_get_state(struct mv88e6xxx_chip *chip,
 			dev_err(chip->dev, "invalid PHY speed\n");
 			return -EINVAL;
 		}
+	} else if (state->link &&
+		   state->interface != PHY_INTERFACE_MODE_SGMII) {
+		/* The Spped and Duplex Resolved register is always 1 when AN is
+		 * disabled. If it is not set, and link is up, it means that the
+		 * SerDes PHY AN bypass feature was invoked.
+		 */
+		state->duplex = DUPLEX_FULL;
+		if (state->interface == PHY_INTERFACE_MODE_2500BASEX)
+			state->speed = SPEED_2500;
+		else
+			state->speed = SPEED_1000;
 	} else {
 		state->link = false;
 	}
@@ -168,9 +181,15 @@  int mv88e6352_serdes_pcs_config(struct mv88e6xxx_chip *chip, int port,
 int mv88e6352_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port,
 				   int lane, struct phylink_link_state *state)
 {
-	u16 lpa, status;
+	u16 lpa, status, ctrl;
 	int err;
 
+	err = mv88e6352_serdes_read(chip, MII_BMCR, &ctrl);
+	if (err) {
+		dev_err(chip->dev, "can't read Serdes PHY control: %d\n", err);
+		return err;
+	}
+
 	err = mv88e6352_serdes_read(chip, 0x11, &status);
 	if (err) {
 		dev_err(chip->dev, "can't read Serdes PHY status: %d\n", err);
@@ -183,7 +202,7 @@  int mv88e6352_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port,
 		return err;
 	}
 
-	return mv88e6xxx_serdes_pcs_get_state(chip, status, lpa, state);
+	return mv88e6xxx_serdes_pcs_get_state(chip, ctrl, status, lpa, state);
 }
 
 int mv88e6352_serdes_pcs_an_restart(struct mv88e6xxx_chip *chip, int port,
@@ -883,9 +902,16 @@  int mv88e6390_serdes_pcs_config(struct mv88e6xxx_chip *chip, int port,
 static int mv88e6390_serdes_pcs_get_state_sgmii(struct mv88e6xxx_chip *chip,
 	int port, int lane, struct phylink_link_state *state)
 {
-	u16 lpa, status;
+	u16 lpa, status, ctrl;
 	int err;
 
+	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
+				    MV88E6390_SGMII_BMCR, &ctrl);
+	if (err) {
+		dev_err(chip->dev, "can't read Serdes PHY control: %d\n", err);
+		return err;
+	}
+
 	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
 				    MV88E6390_SGMII_PHY_STATUS, &status);
 	if (err) {
@@ -900,7 +926,7 @@  static int mv88e6390_serdes_pcs_get_state_sgmii(struct mv88e6xxx_chip *chip,
 		return err;
 	}
 
-	return mv88e6xxx_serdes_pcs_get_state(chip, status, lpa, state);
+	return mv88e6xxx_serdes_pcs_get_state(chip, ctrl, status, lpa, state);
 }
 
 static int mv88e6390_serdes_pcs_get_state_10g(struct mv88e6xxx_chip *chip,