|
@@ -493,15 +493,122 @@ static int m88e1318_config_aneg(struct phy_device *phydev)
|
|
return m88e1121_config_aneg(phydev);
|
|
return m88e1121_config_aneg(phydev);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/**
|
|
|
|
+ * ethtool_adv_to_fiber_adv_t
|
|
|
|
+ * @ethadv: the ethtool advertisement settings
|
|
|
|
+ *
|
|
|
|
+ * A small helper function that translates ethtool advertisement
|
|
|
|
+ * settings to phy autonegotiation advertisements for the
|
|
|
|
+ * MII_ADV register for fiber link.
|
|
|
|
+ */
|
|
|
|
+static inline u32 ethtool_adv_to_fiber_adv_t(u32 ethadv)
|
|
|
|
+{
|
|
|
|
+ u32 result = 0;
|
|
|
|
+
|
|
|
|
+ if (ethadv & ADVERTISED_1000baseT_Half)
|
|
|
|
+ result |= ADVERTISE_FIBER_1000HALF;
|
|
|
|
+ if (ethadv & ADVERTISED_1000baseT_Full)
|
|
|
|
+ result |= ADVERTISE_FIBER_1000FULL;
|
|
|
|
+
|
|
|
|
+ if ((ethadv & ADVERTISE_PAUSE_ASYM) && (ethadv & ADVERTISE_PAUSE_CAP))
|
|
|
|
+ result |= LPA_PAUSE_ASYM_FIBER;
|
|
|
|
+ else if (ethadv & ADVERTISE_PAUSE_CAP)
|
|
|
|
+ result |= (ADVERTISE_PAUSE_FIBER
|
|
|
|
+ & (~ADVERTISE_PAUSE_ASYM_FIBER));
|
|
|
|
+
|
|
|
|
+ return result;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * marvell_config_aneg_fiber - restart auto-negotiation or write BMCR
|
|
|
|
+ * @phydev: target phy_device struct
|
|
|
|
+ *
|
|
|
|
+ * Description: If auto-negotiation is enabled, we configure the
|
|
|
|
+ * advertising, and then restart auto-negotiation. If it is not
|
|
|
|
+ * enabled, then we write the BMCR. Adapted for fiber link in
|
|
|
|
+ * some Marvell's devices.
|
|
|
|
+ */
|
|
|
|
+static int marvell_config_aneg_fiber(struct phy_device *phydev)
|
|
|
|
+{
|
|
|
|
+ int changed = 0;
|
|
|
|
+ int err;
|
|
|
|
+ int adv, oldadv;
|
|
|
|
+ u32 advertise;
|
|
|
|
+
|
|
|
|
+ if (phydev->autoneg != AUTONEG_ENABLE)
|
|
|
|
+ return genphy_setup_forced(phydev);
|
|
|
|
+
|
|
|
|
+ /* Only allow advertising what this PHY supports */
|
|
|
|
+ phydev->advertising &= phydev->supported;
|
|
|
|
+ advertise = phydev->advertising;
|
|
|
|
+
|
|
|
|
+ /* Setup fiber advertisement */
|
|
|
|
+ adv = phy_read(phydev, MII_ADVERTISE);
|
|
|
|
+ if (adv < 0)
|
|
|
|
+ return adv;
|
|
|
|
+
|
|
|
|
+ oldadv = adv;
|
|
|
|
+ adv &= ~(ADVERTISE_FIBER_1000HALF | ADVERTISE_FIBER_1000FULL
|
|
|
|
+ | LPA_PAUSE_FIBER);
|
|
|
|
+ adv |= ethtool_adv_to_fiber_adv_t(advertise);
|
|
|
|
+
|
|
|
|
+ if (adv != oldadv) {
|
|
|
|
+ err = phy_write(phydev, MII_ADVERTISE, adv);
|
|
|
|
+ if (err < 0)
|
|
|
|
+ return err;
|
|
|
|
+
|
|
|
|
+ changed = 1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (changed == 0) {
|
|
|
|
+ /* Advertisement hasn't changed, but maybe aneg was never on to
|
|
|
|
+ * begin with? Or maybe phy was isolated?
|
|
|
|
+ */
|
|
|
|
+ int ctl = phy_read(phydev, MII_BMCR);
|
|
|
|
+
|
|
|
|
+ if (ctl < 0)
|
|
|
|
+ return ctl;
|
|
|
|
+
|
|
|
|
+ if (!(ctl & BMCR_ANENABLE) || (ctl & BMCR_ISOLATE))
|
|
|
|
+ changed = 1; /* do restart aneg */
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Only restart aneg if we are advertising something different
|
|
|
|
+ * than we were before.
|
|
|
|
+ */
|
|
|
|
+ if (changed > 0)
|
|
|
|
+ changed = genphy_restart_aneg(phydev);
|
|
|
|
+
|
|
|
|
+ return changed;
|
|
|
|
+}
|
|
|
|
+
|
|
static int m88e1510_config_aneg(struct phy_device *phydev)
|
|
static int m88e1510_config_aneg(struct phy_device *phydev)
|
|
{
|
|
{
|
|
int err;
|
|
int err;
|
|
|
|
|
|
|
|
+ err = phy_write(phydev, MII_MARVELL_PHY_PAGE, MII_M1111_COPPER);
|
|
|
|
+ if (err < 0)
|
|
|
|
+ goto error;
|
|
|
|
+
|
|
|
|
+ /* Configure the copper link first */
|
|
err = m88e1318_config_aneg(phydev);
|
|
err = m88e1318_config_aneg(phydev);
|
|
if (err < 0)
|
|
if (err < 0)
|
|
- return err;
|
|
|
|
|
|
+ goto error;
|
|
|
|
|
|
- return 0;
|
|
|
|
|
|
+ /* Then the fiber link */
|
|
|
|
+ err = phy_write(phydev, MII_MARVELL_PHY_PAGE, MII_M1111_FIBER);
|
|
|
|
+ if (err < 0)
|
|
|
|
+ goto error;
|
|
|
|
+
|
|
|
|
+ err = marvell_config_aneg_fiber(phydev);
|
|
|
|
+ if (err < 0)
|
|
|
|
+ goto error;
|
|
|
|
+
|
|
|
|
+ return phy_write(phydev, MII_MARVELL_PHY_PAGE, MII_M1111_COPPER);
|
|
|
|
+
|
|
|
|
+error:
|
|
|
|
+ phy_write(phydev, MII_MARVELL_PHY_PAGE, MII_M1111_COPPER);
|
|
|
|
+ return err;
|
|
}
|
|
}
|
|
|
|
|
|
static int marvell_config_init(struct phy_device *phydev)
|
|
static int marvell_config_init(struct phy_device *phydev)
|