From: Matthias Schiffer matthias.schiffer@ew.tq-group.com
stable inclusion from stable-v6.6.33 commit a47027919d69e71a5cbcf752cb2d7b02576bfd21 category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/IAD6H2
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=...
--------------------------------
[ Upstream commit e44894e2aa4eb311ceda134de8b6f51ff979211b ]
88E6250-family switches have the quirk that the EEPROM Running flag can get stuck at 1 when no EEPROM is connected, causing mv88e6xxx_g2_eeprom_wait() to time out. We still want to wait for the EEPROM however, to avoid interrupting a transfer and leaving the EEPROM in an invalid state.
The condition to wait for recommended by the hardware spec is the EEInt flag, however this flag is cleared on read, so before the hardware reset, is may have been cleared already even though the EEPROM has been read successfully.
For this reason, we revive the mv88e6xxx_g1_wait_eeprom_done() function that was removed in commit 6ccf50d4d474 ("net: dsa: mv88e6xxx: Avoid EEPROM timeout when EEPROM is absent") in a slightly refactored form, and introduce a new mv88e6xxx_g1_wait_eeprom_done_prereset() that additionally handles this case by triggering another EEPROM reload that can be waited on.
On other switch models without this quirk, mv88e6xxx_g2_eeprom_wait() is kept, as it avoids the additional reload.
Fixes: 6ccf50d4d474 ("net: dsa: mv88e6xxx: Avoid EEPROM timeout when EEPROM is absent") Signed-off-by: Matthias Schiffer matthias.schiffer@ew.tq-group.com Reviewed-by: Andrew Lunn andrew@lunn.ch Signed-off-by: David S. Miller davem@davemloft.net Signed-off-by: Sasha Levin sashal@kernel.org Signed-off-by: Wang Hai wanghai38@huawei.com --- drivers/net/dsa/mv88e6xxx/chip.c | 4 +- drivers/net/dsa/mv88e6xxx/global1.c | 89 +++++++++++++++++++++++++++++ drivers/net/dsa/mv88e6xxx/global1.h | 2 + 3 files changed, 93 insertions(+), 2 deletions(-)
diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index a2aec16abb8f..9571e1b1e59e 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -4900,8 +4900,8 @@ static const struct mv88e6xxx_ops mv88e6250_ops = { .watchdog_ops = &mv88e6250_watchdog_ops, .mgmt_rsvd2cpu = mv88e6352_g2_mgmt_rsvd2cpu, .pot_clear = mv88e6xxx_g2_pot_clear, - .hardware_reset_pre = mv88e6xxx_g2_eeprom_wait, - .hardware_reset_post = mv88e6xxx_g2_eeprom_wait, + .hardware_reset_pre = mv88e6250_g1_wait_eeprom_done_prereset, + .hardware_reset_post = mv88e6xxx_g1_wait_eeprom_done, .reset = mv88e6250_g1_reset, .vtu_getnext = mv88e6185_g1_vtu_getnext, .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge, diff --git a/drivers/net/dsa/mv88e6xxx/global1.c b/drivers/net/dsa/mv88e6xxx/global1.c index 174c773b38c2..7ef0f4426ad7 100644 --- a/drivers/net/dsa/mv88e6xxx/global1.c +++ b/drivers/net/dsa/mv88e6xxx/global1.c @@ -75,6 +75,95 @@ static int mv88e6xxx_g1_wait_init_ready(struct mv88e6xxx_chip *chip) return mv88e6xxx_g1_wait_bit(chip, MV88E6XXX_G1_STS, bit, 1); }
+static int mv88e6250_g1_eeprom_reload(struct mv88e6xxx_chip *chip) +{ + /* MV88E6185_G1_CTL1_RELOAD_EEPROM is also valid for 88E6250 */ + int bit = __bf_shf(MV88E6185_G1_CTL1_RELOAD_EEPROM); + u16 val; + int err; + + err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_CTL1, &val); + if (err) + return err; + + val |= MV88E6185_G1_CTL1_RELOAD_EEPROM; + + err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_CTL1, val); + if (err) + return err; + + return mv88e6xxx_g1_wait_bit(chip, MV88E6XXX_G1_CTL1, bit, 0); +} + +/* Returns 0 when done, -EBUSY when waiting, other negative codes on error */ +static int mv88e6xxx_g1_is_eeprom_done(struct mv88e6xxx_chip *chip) +{ + u16 val; + int err; + + err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_STS, &val); + if (err < 0) { + dev_err(chip->dev, "Error reading status"); + return err; + } + + /* If the switch is still resetting, it may not + * respond on the bus, and so MDIO read returns + * 0xffff. Differentiate between that, and waiting for + * the EEPROM to be done by bit 0 being set. + */ + if (val == 0xffff || !(val & BIT(MV88E6XXX_G1_STS_IRQ_EEPROM_DONE))) + return -EBUSY; + + return 0; +} + +/* As the EEInt (EEPROM done) flag clears on read if the status register, this + * function must be called directly after a hard reset or EEPROM ReLoad request, + * or the done condition may have been missed + */ +int mv88e6xxx_g1_wait_eeprom_done(struct mv88e6xxx_chip *chip) +{ + const unsigned long timeout = jiffies + 1 * HZ; + int ret; + + /* Wait up to 1 second for the switch to finish reading the + * EEPROM. + */ + while (time_before(jiffies, timeout)) { + ret = mv88e6xxx_g1_is_eeprom_done(chip); + if (ret != -EBUSY) + return ret; + } + + dev_err(chip->dev, "Timeout waiting for EEPROM done"); + return -ETIMEDOUT; +} + +int mv88e6250_g1_wait_eeprom_done_prereset(struct mv88e6xxx_chip *chip) +{ + int ret; + + ret = mv88e6xxx_g1_is_eeprom_done(chip); + if (ret != -EBUSY) + return ret; + + /* Pre-reset, we don't know the state of the switch - when + * mv88e6xxx_g1_is_eeprom_done() returns -EBUSY, that may be because + * the switch is actually busy reading the EEPROM, or because + * MV88E6XXX_G1_STS_IRQ_EEPROM_DONE has been cleared by an unrelated + * status register read already. + * + * To account for the latter case, trigger another EEPROM reload for + * another chance at seeing the done flag. + */ + ret = mv88e6250_g1_eeprom_reload(chip); + if (ret) + return ret; + + return mv88e6xxx_g1_wait_eeprom_done(chip); +} + /* Offset 0x01: Switch MAC Address Register Bytes 0 & 1 * Offset 0x02: Switch MAC Address Register Bytes 2 & 3 * Offset 0x03: Switch MAC Address Register Bytes 4 & 5 diff --git a/drivers/net/dsa/mv88e6xxx/global1.h b/drivers/net/dsa/mv88e6xxx/global1.h index 1095261f5b49..3dbb7a1b8fe1 100644 --- a/drivers/net/dsa/mv88e6xxx/global1.h +++ b/drivers/net/dsa/mv88e6xxx/global1.h @@ -282,6 +282,8 @@ int mv88e6xxx_g1_set_switch_mac(struct mv88e6xxx_chip *chip, u8 *addr); int mv88e6185_g1_reset(struct mv88e6xxx_chip *chip); int mv88e6352_g1_reset(struct mv88e6xxx_chip *chip); int mv88e6250_g1_reset(struct mv88e6xxx_chip *chip); +int mv88e6xxx_g1_wait_eeprom_done(struct mv88e6xxx_chip *chip); +int mv88e6250_g1_wait_eeprom_done_prereset(struct mv88e6xxx_chip *chip);
int mv88e6185_g1_ppu_enable(struct mv88e6xxx_chip *chip); int mv88e6185_g1_ppu_disable(struct mv88e6xxx_chip *chip);