/* This function is provided to cope with the possible failures of this phy * during aneg process. When aneg fails, the PHY reports that aneg is done * but the value found in MII_LPA is wrong: * - Early failures: MII_LPA is just 0x0001. if MII_EXPANSION reports that * the link partner (LP) supports aneg but the LP never acked our base * code word, it is likely that we never sent it to begin with. * - Late failures: MII_LPA is filled with a value which seems to make sense * but it actually is not what the LP is advertising. It seems that we * can detect this using a magic bit in the WOL bank (reg 12 - bit 12). * If this particular bit is not set when aneg is reported being done, * it means MII_LPA is likely to be wrong. * * In both case, forcing a restart of the aneg process solve the problem. * When this failure happens, the first retry is usually successful but, * in some cases, it may take up to 6 retries to get a decent result */ static int meson_gxl_read_status(struct phy_device *phydev) { int ret, wol, lpa, exp; if (phydev->autoneg == AUTONEG_ENABLE) { ret = genphy_aneg_done(phydev); if (ret < 0) return ret; else if (!ret) goto read_status_continue; /* Aneg is done, let's check everything is fine */ wol = meson_gxl_read_reg(phydev, BANK_WOL, LPI_STATUS); if (wol < 0) return wol; lpa = phy_read(phydev, MII_LPA); if (lpa < 0) return lpa; exp = phy_read(phydev, MII_EXPANSION); if (exp < 0) return exp; if (!(wol & LPI_STATUS_RSV12) || ((exp & EXPANSION_NWAY) && !(lpa & LPA_LPACK))) { /* Looks like aneg failed after all */ phydev_dbg(phydev, "LPA corruption - aneg restart\n"); return genphy_restart_aneg(phydev); } } read_status_continue: return genphy_read_status(phydev); }
static void at803x_link_change_notify(struct phy_device *phydev) { struct at803x_priv *priv = phydev->priv; /* * Conduct a hardware reset for AT8030 every time a link loss is * signalled. This is necessary to circumvent a hardware bug that * occurs when the cable is unplugged while TX packets are pending * in the FIFO. In such cases, the FIFO enters an error mode it * cannot recover from by software. */ if (phydev->drv->phy_id == ATH8030_PHY_ID) { if (phydev->state == PHY_NOLINK) { if (priv->gpiod_reset && !priv->phy_reset) { struct at803x_context context; at803x_context_save(phydev, &context); gpiod_set_value(priv->gpiod_reset, 0); msleep(1); gpiod_set_value(priv->gpiod_reset, 1); msleep(1); at803x_context_restore(phydev, &context); phydev_dbg(phydev, "%s(): phy was reset\n", __func__); priv->phy_reset = true; } } else { priv->phy_reset = false; } } }
static void aqr107_link_change_notify(struct phy_device *phydev) { u8 fw_major, fw_minor; bool downshift, short_reach, afr; int mode, val; if (phydev->state != PHY_RUNNING || phydev->autoneg == AUTONEG_DISABLE) return; val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_RX_LP_STAT1); /* call failed or link partner is no Aquantia PHY */ if (val < 0 || !(val & MDIO_AN_RX_LP_STAT1_AQ_PHY)) return; short_reach = val & MDIO_AN_RX_LP_STAT1_SHORT_REACH; downshift = val & MDIO_AN_RX_LP_STAT1_AQRATE_DOWNSHIFT; val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_RX_LP_STAT4); if (val < 0) return; fw_major = FIELD_GET(MDIO_AN_RX_LP_STAT4_FW_MAJOR, val); fw_minor = FIELD_GET(MDIO_AN_RX_LP_STAT4_FW_MINOR, val); val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_RX_VEND_STAT3); if (val < 0) return; afr = val & MDIO_AN_RX_VEND_STAT3_AFR; phydev_dbg(phydev, "Link partner is Aquantia PHY, FW %u.%u%s%s%s\n", fw_major, fw_minor, short_reach ? ", short reach mode" : "", downshift ? ", fast-retrain downshift advertised" : "", afr ? ", fast reframe advertised" : ""); val = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_RSVD_STAT9); if (val < 0) return; mode = FIELD_GET(VEND1_GLOBAL_RSVD_STAT9_MODE, val); if (mode == VEND1_GLOBAL_RSVD_STAT9_1000BT2) phydev_info(phydev, "Aquantia 1000Base-T2 mode active\n"); }
static void aqr107_chip_info(struct phy_device *phydev) { u8 fw_major, fw_minor, build_id, prov_id; int val; val = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_FW_ID); if (val < 0) return; fw_major = FIELD_GET(VEND1_GLOBAL_FW_ID_MAJOR, val); fw_minor = FIELD_GET(VEND1_GLOBAL_FW_ID_MINOR, val); val = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_RSVD_STAT1); if (val < 0) return; build_id = FIELD_GET(VEND1_GLOBAL_RSVD_STAT1_FW_BUILD_ID, val); prov_id = FIELD_GET(VEND1_GLOBAL_RSVD_STAT1_PROV_ID, val); phydev_dbg(phydev, "FW %u.%u, Build %u, Provisioning %u\n", fw_major, fw_minor, build_id, prov_id); }