diff options
Diffstat (limited to 'drivers/net/phy/marvell.c')
-rw-r--r-- | drivers/net/phy/marvell.c | 159 |
1 files changed, 130 insertions, 29 deletions
diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index 4fcfca4e1702..2810f4f9da0c 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -189,6 +189,8 @@ #define MII_88E1510_GEN_CTRL_REG_1_MODE_RGMII_SGMII 0x4 #define MII_88E1510_GEN_CTRL_REG_1_RESET 0x8000 /* Soft reset */ +#define MII_88E1510_MSCR_2 0x15 + #define MII_VCT5_TX_RX_MDI0_COUPLING 0x10 #define MII_VCT5_TX_RX_MDI1_COUPLING 0x11 #define MII_VCT5_TX_RX_MDI2_COUPLING 0x12 @@ -551,9 +553,9 @@ static int m88e1121_config_aneg_rgmii_delays(struct phy_device *phydev) else mscr = 0; - return phy_modify_paged(phydev, MII_MARVELL_MSCR_PAGE, - MII_88E1121_PHY_MSCR_REG, - MII_88E1121_PHY_MSCR_DELAY_MASK, mscr); + return phy_modify_paged_changed(phydev, MII_MARVELL_MSCR_PAGE, + MII_88E1121_PHY_MSCR_REG, + MII_88E1121_PHY_MSCR_DELAY_MASK, mscr); } static int m88e1121_config_aneg(struct phy_device *phydev) @@ -567,11 +569,13 @@ static int m88e1121_config_aneg(struct phy_device *phydev) return err; } + changed = err; + err = marvell_set_polarity(phydev, phydev->mdix_ctrl); if (err < 0) return err; - changed = err; + changed |= err; err = genphy_config_aneg(phydev); if (err < 0) @@ -957,7 +961,21 @@ static int m88e1111_config_init(struct phy_device *phydev) if (err < 0) return err; - return genphy_soft_reset(phydev); + err = genphy_soft_reset(phydev); + if (err < 0) + return err; + + if (phydev->interface == PHY_INTERFACE_MODE_SGMII) { + /* If the HWCFG_MODE was changed from another mode (such as + * 1000BaseX) to SGMII, the state of the support bits may have + * also changed now that the PHY has been reset. + * Update the PHY abilities accordingly. + */ + err = genphy_read_abilities(phydev); + linkmode_or(phydev->advertising, phydev->advertising, + phydev->supported); + } + return err; } static int m88e1111_get_downshift(struct phy_device *phydev, u8 *data) @@ -1173,7 +1191,44 @@ static int m88e1318_config_init(struct phy_device *phydev) static int m88e1510_config_init(struct phy_device *phydev) { + static const struct { + u16 reg17, reg16; + } errata_vals[] = { + { 0x214b, 0x2144 }, + { 0x0c28, 0x2146 }, + { 0xb233, 0x214d }, + { 0xcc0c, 0x2159 }, + }; int err; + int i; + + /* As per Marvell Release Notes - Alaska 88E1510/88E1518/88E1512/ + * 88E1514 Rev A0, Errata Section 5.1: + * If EEE is intended to be used, the following register writes + * must be done once after every hardware reset. + */ + err = marvell_set_page(phydev, 0x00FF); + if (err < 0) + return err; + + for (i = 0; i < ARRAY_SIZE(errata_vals); ++i) { + err = phy_write(phydev, 17, errata_vals[i].reg17); + if (err) + return err; + err = phy_write(phydev, 16, errata_vals[i].reg16); + if (err) + return err; + } + + err = marvell_set_page(phydev, 0x00FB); + if (err < 0) + return err; + err = phy_write(phydev, 07, 0xC00D); + if (err < 0) + return err; + err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE); + if (err < 0) + return err; /* SGMII-to-Copper mode initialization */ if (phydev->interface == PHY_INTERFACE_MODE_SGMII) { @@ -1211,42 +1266,41 @@ static int m88e1118_config_aneg(struct phy_device *phydev) { int err; - err = genphy_soft_reset(phydev); + err = marvell_set_polarity(phydev, phydev->mdix_ctrl); if (err < 0) return err; - err = marvell_set_polarity(phydev, phydev->mdix_ctrl); + err = genphy_config_aneg(phydev); if (err < 0) return err; - err = genphy_config_aneg(phydev); - return 0; + return genphy_soft_reset(phydev); } static int m88e1118_config_init(struct phy_device *phydev) { + u16 leds; int err; - /* Change address */ - err = marvell_set_page(phydev, MII_MARVELL_MSCR_PAGE); - if (err < 0) - return err; - /* Enable 1000 Mbit */ - err = phy_write(phydev, 0x15, 0x1070); + err = phy_write_paged(phydev, MII_MARVELL_MSCR_PAGE, + MII_88E1121_PHY_MSCR_REG, 0x1070); if (err < 0) return err; - /* Change address */ - err = marvell_set_page(phydev, MII_MARVELL_LED_PAGE); - if (err < 0) - return err; + if (phy_interface_is_rgmii(phydev)) { + err = m88e1121_config_aneg_rgmii_delays(phydev); + if (err < 0) + return err; + } /* Adjust LED Control */ if (phydev->dev_flags & MARVELL_PHY_M1118_DNS323_LEDS) - err = phy_write(phydev, 0x10, 0x1100); + leds = 0x1100; else - err = phy_write(phydev, 0x10, 0x021e); + leds = 0x021e; + + err = phy_write_paged(phydev, MII_MARVELL_LED_PAGE, 0x10, leds); if (err < 0) return err; @@ -1254,7 +1308,7 @@ static int m88e1118_config_init(struct phy_device *phydev) if (err < 0) return err; - /* Reset address */ + /* Reset page register */ err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE); if (err < 0) return err; @@ -1684,8 +1738,8 @@ static int marvell_suspend(struct phy_device *phydev) int err; /* Suspend the fiber mode first */ - if (!linkmode_test_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, - phydev->supported)) { + if (linkmode_test_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, + phydev->supported)) { err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE); if (err < 0) goto error; @@ -1719,8 +1773,8 @@ static int marvell_resume(struct phy_device *phydev) int err; /* Resume the fiber mode first */ - if (!linkmode_test_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, - phydev->supported)) { + if (linkmode_test_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, + phydev->supported)) { err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE); if (err < 0) goto error; @@ -1898,7 +1952,7 @@ static void marvell_get_strings(struct phy_device *phydev, u8 *data) int i; for (i = 0; i < count; i++) { - strlcpy(data + i * ETH_GSTRING_LEN, + strscpy(data + i * ETH_GSTRING_LEN, marvell_hw_stats[i].string, ETH_GSTRING_LEN); } } @@ -1932,6 +1986,52 @@ static void marvell_get_stats(struct phy_device *phydev, data[i] = marvell_get_stat(phydev, i); } +static int m88e1510_loopback(struct phy_device *phydev, bool enable) +{ + int err; + + if (enable) { + u16 bmcr_ctl, mscr2_ctl = 0; + + bmcr_ctl = mii_bmcr_encode_fixed(phydev->speed, phydev->duplex); + + err = phy_write(phydev, MII_BMCR, bmcr_ctl); + if (err < 0) + return err; + + if (phydev->speed == SPEED_1000) + mscr2_ctl = BMCR_SPEED1000; + else if (phydev->speed == SPEED_100) + mscr2_ctl = BMCR_SPEED100; + + err = phy_modify_paged(phydev, MII_MARVELL_MSCR_PAGE, + MII_88E1510_MSCR_2, BMCR_SPEED1000 | + BMCR_SPEED100, mscr2_ctl); + if (err < 0) + return err; + + /* Need soft reset to have speed configuration takes effect */ + err = genphy_soft_reset(phydev); + if (err < 0) + return err; + + /* FIXME: Based on trial and error test, it seem 1G need to have + * delay between soft reset and loopback enablement. + */ + if (phydev->speed == SPEED_1000) + msleep(1000); + + return phy_modify(phydev, MII_BMCR, BMCR_LOOPBACK, + BMCR_LOOPBACK); + } else { + err = phy_modify(phydev, MII_BMCR, BMCR_LOOPBACK, 0); + if (err < 0) + return err; + + return phy_config_aneg(phydev); + } +} + static int marvell_vct5_wait_complete(struct phy_device *phydev) { int i; @@ -2745,6 +2845,7 @@ static int marvell_probe(struct phy_device *phydev) static int m88e1510_sfp_insert(void *upstream, const struct sfp_eeprom_id *id) { + DECLARE_PHY_INTERFACE_MASK(interfaces); struct phy_device *phydev = upstream; phy_interface_t interface; struct device *dev; @@ -2756,7 +2857,7 @@ static int m88e1510_sfp_insert(void *upstream, const struct sfp_eeprom_id *id) dev = &phydev->mdio.dev; - sfp_parse_support(phydev->sfp_bus, id, supported); + sfp_parse_support(phydev->sfp_bus, id, supported, interfaces); interface = sfp_select_interface(phydev->sfp_bus, supported); dev_info(dev, "%s SFP module inserted\n", phy_modes(interface)); @@ -3078,7 +3179,7 @@ static struct phy_driver marvell_drivers[] = { .get_sset_count = marvell_get_sset_count, .get_strings = marvell_get_strings, .get_stats = marvell_get_stats, - .set_loopback = genphy_loopback, + .set_loopback = m88e1510_loopback, .get_tunable = m88e1011_get_tunable, .set_tunable = m88e1011_set_tunable, .cable_test_start = marvell_vct7_cable_test_start, |