summaryrefslogtreecommitdiff
path: root/drivers/net/dsa
diff options
context:
space:
mode:
authorMarek BehĂșn <kabel@kernel.org>2021-11-30 18:01:50 +0100
committerDavid S. Miller <davem@davemloft.net>2021-12-01 14:39:10 +0000
commit163000dbc772c1eae9bdfe7c8fe30155db1efd74 (patch)
tree10c9bddbd3700e4b2e9b4f4dbf8f6f715eaefb57 /drivers/net/dsa
parent93fd8207bed80ce19aaf59932cbe1c03d418a37d (diff)
net: dsa: mv88e6xxx: Fix inband AN for 2500base-x on 88E6393X family
Inband AN is broken on Amethyst in 2500base-x mode when set by standard mechanism (via cmode). (There probably is some weird setting done by default in the switch for this mode that make it cycle in some state or something, because when the peer is the mvneta controller, it receives link change interrupts every ~0.3ms, but the link is always down.) Get around this by configuring the PCS mode to 1000base-x (where inband AN works), and then changing the SerDes frequency while SerDes transmitter and receiver are disabled, before enabling SerDes PHY. After disabling SerDes PHY, change the PCS mode back to 2500base-x, to avoid confusing the device (if we leave it at 1000base-x PCS mode but with different frequency, and then change cmode to sgmii, the device won't change the frequency because it thinks it already has the correct one). The register which changes the frequency is undocumented. I discovered it by going through all registers in the ranges 4.f000-4.f100 and 1e.8000-1e.8200 for all SerDes cmodes (sgmii, 1000base-x, 2500base-x, 5gbase-r, 10gbase-r, usxgmii) and filtering out registers that didn't make sense (the value was the same for modes which have different frequency). The result of this was: reg sgmii 1000base-x 2500base-x 5gbase-r 10gbase-r usxgmii 04.f002 005b 0058 0059 005c 005d 005f 04.f076 3000 0000 1000 4000 5000 7000 04.f07c 0950 0950 1850 0550 0150 0150 1e.8000 0059 0059 0058 0055 0051 0051 1e.8140 0e20 0e20 0e28 0e21 0e42 0e42 Register 04.f002 is the documented Port Operational Confiuration register, it's last 3 bits select PCS type, so changing this register also changes the frequency to the appropriate value. Registers 04.f076 and 04.f07c are not writable. Undocumented register 1e.8000 was the one: changing bits 3:0 from 9 to 8 changed SerDes frequency to 3.125 GHz, while leaving the value of PCS mode in register 04.f002.2:0 at 1000base-x. Inband autonegotiation started working correctly. (I didn't try anything with register 1e.8140 since 1e.8000 solved the problem.) Since I don't have documentation for this register 1e.8000.3:0, I am using the constants without names, but my hypothesis is that this register selects PHY frequency. If in the future I have access to an oscilloscope able to handle these frequencies, I will try to test this hypothesis. Fixes: de776d0d316f ("net: dsa: mv88e6xxx: add support for mv88e6393x family") Signed-off-by: Marek BehĂșn <kabel@kernel.org> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/dsa')
-rw-r--r--drivers/net/dsa/mv88e6xxx/serdes.c61
-rw-r--r--drivers/net/dsa/mv88e6xxx/serdes.h1
2 files changed, 61 insertions, 1 deletions
diff --git a/drivers/net/dsa/mv88e6xxx/serdes.c b/drivers/net/dsa/mv88e6xxx/serdes.c
index 9e4f18a4adc2..6f60376b932c 100644
--- a/drivers/net/dsa/mv88e6xxx/serdes.c
+++ b/drivers/net/dsa/mv88e6xxx/serdes.c
@@ -1419,6 +1419,54 @@ static int mv88e6393x_serdes_erratum_5_2(struct mv88e6xxx_chip *chip, int lane,
return 0;
}
+static int mv88e6393x_serdes_fix_2500basex_an(struct mv88e6xxx_chip *chip,
+ int lane, u8 cmode, bool on)
+{
+ u16 reg;
+ int err;
+
+ if (cmode != MV88E6XXX_PORT_STS_CMODE_2500BASEX)
+ return 0;
+
+ /* Inband AN is broken on Amethyst in 2500base-x mode when set by
+ * standard mechanism (via cmode).
+ * We can get around this by configuring the PCS mode to 1000base-x
+ * and then writing value 0x58 to register 1e.8000. (This must be done
+ * while SerDes receiver and transmitter are disabled, which is, when
+ * this function is called.)
+ * It seem that when we do this configuration to 2500base-x mode (by
+ * changing PCS mode to 1000base-x and frequency to 3.125 GHz from
+ * 1.25 GHz) and then configure to sgmii or 1000base-x, the device
+ * thinks that it already has SerDes at 1.25 GHz and does not change
+ * the 1e.8000 register, leaving SerDes at 3.125 GHz.
+ * To avoid this, change PCS mode back to 2500base-x when disabling
+ * SerDes from 2500base-x mode.
+ */
+ err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
+ MV88E6393X_SERDES_POC, &reg);
+ if (err)
+ return err;
+
+ reg &= ~(MV88E6393X_SERDES_POC_PCS_MASK | MV88E6393X_SERDES_POC_AN);
+ if (on)
+ reg |= MV88E6393X_SERDES_POC_PCS_1000BASEX |
+ MV88E6393X_SERDES_POC_AN;
+ else
+ reg |= MV88E6393X_SERDES_POC_PCS_2500BASEX;
+ reg |= MV88E6393X_SERDES_POC_RESET;
+
+ err = mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
+ MV88E6393X_SERDES_POC, reg);
+ if (err)
+ return err;
+
+ err = mv88e6390_serdes_write(chip, lane, MDIO_MMD_VEND1, 0x8000, 0x58);
+ if (err)
+ return err;
+
+ return 0;
+}
+
int mv88e6393x_serdes_power(struct mv88e6xxx_chip *chip, int port, int lane,
bool on)
{
@@ -1437,6 +1485,11 @@ int mv88e6393x_serdes_power(struct mv88e6xxx_chip *chip, int port, int lane,
if (err)
return err;
+ err = mv88e6393x_serdes_fix_2500basex_an(chip, lane, cmode,
+ true);
+ if (err)
+ return err;
+
err = mv88e6393x_serdes_power_lane(chip, lane, true);
if (err)
return err;
@@ -1457,8 +1510,14 @@ int mv88e6393x_serdes_power(struct mv88e6xxx_chip *chip, int port, int lane,
if (err)
return err;
- if (!on)
+ if (!on) {
err = mv88e6393x_serdes_power_lane(chip, lane, false);
+ if (err)
+ return err;
+
+ err = mv88e6393x_serdes_fix_2500basex_an(chip, lane, cmode,
+ false);
+ }
return err;
}
diff --git a/drivers/net/dsa/mv88e6xxx/serdes.h b/drivers/net/dsa/mv88e6xxx/serdes.h
index e9292c8beee4..8dd8ed225b45 100644
--- a/drivers/net/dsa/mv88e6xxx/serdes.h
+++ b/drivers/net/dsa/mv88e6xxx/serdes.h
@@ -93,6 +93,7 @@
#define MV88E6393X_SERDES_POC_PCS_MASK 0x0007
#define MV88E6393X_SERDES_POC_RESET BIT(15)
#define MV88E6393X_SERDES_POC_PDOWN BIT(5)
+#define MV88E6393X_SERDES_POC_AN BIT(3)
#define MV88E6393X_SERDES_CTRL1 0xf003
#define MV88E6393X_SERDES_CTRL1_TX_PDOWN BIT(9)
#define MV88E6393X_SERDES_CTRL1_RX_PDOWN BIT(8)