The AX88772D, AX88179A and AX88279 are 100MBit, 1GBit and respectively 2.5 GBit PHYs that are found in ASIX USB-Ethernet controllers of the same name. Signed-off-by: Birger Koblitz --- drivers/net/phy/ax88796b.c | 192 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 192 insertions(+) diff --git a/drivers/net/phy/ax88796b.c b/drivers/net/phy/ax88796b.c index f20ddf64914993f7097070d1f56c1103ec1e40e8..686f4e8d08f74da98e453ea50a5347cf3673e869 100644 --- a/drivers/net/phy/ax88796b.c +++ b/drivers/net/phy/ax88796b.c @@ -13,6 +13,19 @@ #define PHY_ID_ASIX_AX88772A 0x003b1861 #define PHY_ID_ASIX_AX88772C 0x003b1881 #define PHY_ID_ASIX_AX88796B 0x003b1841 +#define PHY_ID_ASIX_AX88772D 0x003b772d +#define PHY_ID_ASIX_AX88179A 0x003b179a +#define PHY_ID_ASIX_AX88279 0x003b2790 + +#define AX_ADVERTISE_2500 0x1000 + +/* MII Vendor registers */ +#define AX_CTRL_STATUS 0x1d +#define AX_CTRL_STATUS_SPEED_MASK 0x0c +#define AX_CTRL_STATUS_SPEED_10 0x0 +#define AX_CTRL_STATUS_SPEED_100 0x4 +#define AX_CTRL_STATUS_SPEED_1000 0x8 +#define AX_CTRL_STATUS_SPEED_2500 0xc MODULE_DESCRIPTION("Asix PHY driver"); MODULE_AUTHOR("Michael Schmitz "); @@ -83,6 +96,135 @@ static int asix_ax88772a_read_status(struct phy_device *phydev) return 0; } +static int asix_ax88279_read_status(struct phy_device *phydev) +{ + int ret, val; + + ret = genphy_update_link(phydev); + if (ret) + return ret; + + phydev->master_slave_get = MASTER_SLAVE_CFG_UNSUPPORTED; + phydev->master_slave_state = MASTER_SLAVE_STATE_UNSUPPORTED; + phydev->speed = SPEED_UNKNOWN; + phydev->duplex = DUPLEX_UNKNOWN; + phydev->pause = 0; + phydev->asym_pause = 0; + phydev->rate_matching = RATE_MATCH_PAUSE; + + if (!phydev->link) + return 0; + + ret = genphy_read_master_slave(phydev); + if (ret < 0) + return ret; + + ret = genphy_read_lpa(phydev); + if (ret < 0) + return ret; + + val = phy_read(phydev, MII_ADVERTISE); + if (val < 0) + return val; + + linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, + phydev->advertising, val & AX_ADVERTISE_2500); + + /* Read actual speed from vendor register */ + val = phy_read(phydev, AX_CTRL_STATUS); + switch (val & AX_CTRL_STATUS_SPEED_MASK) { + case AX_CTRL_STATUS_SPEED_2500: + phydev->speed = SPEED_2500; + break; + case AX_CTRL_STATUS_SPEED_1000: + phydev->speed = SPEED_1000; + break; + case AX_CTRL_STATUS_SPEED_100: + phydev->speed = SPEED_100; + break; + case AX_CTRL_STATUS_SPEED_10: + phydev->speed = SPEED_10; + } + + /* MDIO_AN_10GBT_STAT_LP2_5G is broken, but we can deduce that + * the link-partner advertised 2500M if remotely AN succceded + * for link speed > 1000M and we locally have a link speed of + * 2500M + */ + val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_STAT); + if (val >= 0 && val & MDIO_AN_10GBT_STAT_REMOK) { + linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, + phydev->lp_advertising, + phydev->speed == SPEED_2500); + } + /* Only supports full duplex */ + phydev->duplex = DUPLEX_FULL; + + return 0; +} + +static int asix_ax88279_config_aneg(struct phy_device *phydev) +{ + bool changed = false; + int ret; + u32 adv; + + if (phydev->autoneg == AUTONEG_DISABLE) { + phydev_warn(phydev, "Disabling autoneg is not supported\n"); + return -EINVAL; + } + + adv = linkmode_adv_to_mii_10gbt_adv_t(phydev->advertising); + + ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL, + MDIO_AN_10GBT_CTRL_ADV2_5G, adv); + if (ret < 0) + return ret; + if (ret > 0) + changed = true; + + return __genphy_config_aneg(phydev, changed); +} + +static int asix_ax88279_get_features(struct phy_device *phydev) +{ + int ret; + + /* MDIO_DEVS1/2 empty, so set mmds_present bits to allow reading abilities */ + phydev->c45_ids.mmds_present |= MDIO_DEVS_PMAPMD | MDIO_DEVS_AN; + + linkmode_set_bit_array(phy_basic_ports_array, ARRAY_SIZE(phy_basic_ports_array), + phydev->supported); + + ret = genphy_c45_pma_read_abilities(phydev); + if (ret < 0) + return ret; + + /* AX88279 does not support reported 100baseT-half duplex mode */ + linkmode_clear_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, phydev->supported); + + return 0; +} + +/* AX88179A-based PHYs cannot access MMD registers via MII_MMD_CTRL/MII_MMD_DATA + * Make sure to use c45 access instead. + */ +static int asix_read_mmd(struct phy_device *phydev, int devnum, u16 reg) +{ + struct mii_bus *bus = phydev->mdio.bus; + int addr = phydev->mdio.addr; + + return __mdiobus_c45_read(bus, addr, devnum, reg); +} + +static int asix_write_mmd(struct phy_device *phydev, int devnum, u16 reg, u16 val) +{ + struct mii_bus *bus = phydev->mdio.bus; + int addr = phydev->mdio.addr; + + return __mdiobus_c45_write(bus, addr, devnum, reg, val); +} + static void asix_ax88772a_link_change_notify(struct phy_device *phydev) { /* Reset PHY, otherwise MII_LPA will provide outdated information. @@ -94,6 +236,25 @@ static void asix_ax88772a_link_change_notify(struct phy_device *phydev) } } +static int asix_ax88772D_get_features(struct phy_device *phydev) +{ + int ret; + __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = {}; + + /* MDIO_DEVS1/2 empty, so set mmds_present bits to allow reading abilities */ + phydev->c45_ids.mmds_present |= MDIO_DEVS_PMAPMD | MDIO_DEVS_AN; + + ret = genphy_read_abilities(phydev); + if (ret < 0) + return ret; + + /* AX88772D does not support reported 1000baseT mode */ + linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, mask); + linkmode_andnot(phydev->supported, phydev->supported, mask); + + return 0; +} + static struct phy_driver asix_driver[] = { { PHY_ID_MATCH_EXACT(PHY_ID_ASIX_AX88772A), @@ -111,6 +272,34 @@ static struct phy_driver asix_driver[] = { .suspend = genphy_suspend, .resume = genphy_resume, .soft_reset = asix_soft_reset, +}, { + PHY_ID_MATCH_EXACT(PHY_ID_ASIX_AX88772D), + .name = "Asix Electronics AX88772D", + .flags = PHY_IS_INTERNAL, + .get_features = asix_ax88772D_get_features, + .read_mmd = asix_read_mmd, + .write_mmd = asix_write_mmd, + .suspend = genphy_suspend, + .resume = genphy_resume, +}, { + PHY_ID_MATCH_EXACT(PHY_ID_ASIX_AX88179A), + .name = "Asix Electronics AX88179A", + .flags = PHY_IS_INTERNAL, + .suspend = genphy_suspend, + .resume = genphy_resume, + .read_mmd = asix_read_mmd, + .write_mmd = asix_write_mmd, +}, { + PHY_ID_MATCH_EXACT(PHY_ID_ASIX_AX88279), + .name = "Asix Electronics AX88279", + .flags = PHY_IS_INTERNAL, + .get_features = asix_ax88279_get_features, + .read_status = asix_ax88279_read_status, + .config_aneg = asix_ax88279_config_aneg, + .read_mmd = asix_read_mmd, + .write_mmd = asix_write_mmd, + .suspend = genphy_suspend, + .resume = genphy_resume, }, { PHY_ID_MATCH_MODEL(PHY_ID_ASIX_AX88796B), .name = "Asix Electronics AX88796B", @@ -124,6 +313,9 @@ static const struct mdio_device_id __maybe_unused asix_tbl[] = { { PHY_ID_MATCH_EXACT(PHY_ID_ASIX_AX88772A) }, { PHY_ID_MATCH_EXACT(PHY_ID_ASIX_AX88772C) }, { PHY_ID_MATCH_MODEL(PHY_ID_ASIX_AX88796B) }, + { PHY_ID_MATCH_EXACT(PHY_ID_ASIX_AX88772D) }, + { PHY_ID_MATCH_EXACT(PHY_ID_ASIX_AX88179A) }, + { PHY_ID_MATCH_EXACT(PHY_ID_ASIX_AX88279) }, { } }; -- 2.47.3