16e3bac3eSIvan Bornyakov // SPDX-License-Identifier: GPL-2.0+ 26e3bac3eSIvan Bornyakov /* 36e3bac3eSIvan Bornyakov * Marvell 88x2222 dual-port multi-speed ethernet transceiver. 46e3bac3eSIvan Bornyakov * 56e3bac3eSIvan Bornyakov * Supports: 66e3bac3eSIvan Bornyakov * XAUI on the host side. 76e3bac3eSIvan Bornyakov * 1000Base-X or 10GBase-R on the line side. 86e3bac3eSIvan Bornyakov * SGMII over 1000Base-X. 96e3bac3eSIvan Bornyakov */ 106e3bac3eSIvan Bornyakov #include <linux/module.h> 116e3bac3eSIvan Bornyakov #include <linux/phy.h> 126e3bac3eSIvan Bornyakov #include <linux/gpio.h> 136e3bac3eSIvan Bornyakov #include <linux/delay.h> 146e3bac3eSIvan Bornyakov #include <linux/mdio.h> 156e3bac3eSIvan Bornyakov #include <linux/marvell_phy.h> 166e3bac3eSIvan Bornyakov #include <linux/of.h> 176e3bac3eSIvan Bornyakov #include <linux/of_device.h> 186e3bac3eSIvan Bornyakov #include <linux/of_gpio.h> 196e3bac3eSIvan Bornyakov #include <linux/sfp.h> 206e3bac3eSIvan Bornyakov #include <linux/netdevice.h> 216e3bac3eSIvan Bornyakov 226e3bac3eSIvan Bornyakov /* Port PCS Configuration */ 236e3bac3eSIvan Bornyakov #define MV_PCS_CONFIG 0xF002 246e3bac3eSIvan Bornyakov #define MV_PCS_HOST_XAUI 0x73 256e3bac3eSIvan Bornyakov #define MV_PCS_LINE_10GBR (0x71 << 8) 266e3bac3eSIvan Bornyakov #define MV_PCS_LINE_1GBX_AN (0x7B << 8) 276e3bac3eSIvan Bornyakov #define MV_PCS_LINE_SGMII_AN (0x7F << 8) 286e3bac3eSIvan Bornyakov 296e3bac3eSIvan Bornyakov /* Port Reset and Power Down */ 306e3bac3eSIvan Bornyakov #define MV_PORT_RST 0xF003 316e3bac3eSIvan Bornyakov #define MV_LINE_RST_SW BIT(15) 326e3bac3eSIvan Bornyakov #define MV_HOST_RST_SW BIT(7) 336e3bac3eSIvan Bornyakov #define MV_PORT_RST_SW (MV_LINE_RST_SW | MV_HOST_RST_SW) 346e3bac3eSIvan Bornyakov 35*58581478SIvan Bornyakov /* PMD Receive Signal Detect */ 36*58581478SIvan Bornyakov #define MV_RX_SIGNAL_DETECT 0x000A 37*58581478SIvan Bornyakov #define MV_RX_SIGNAL_DETECT_GLOBAL BIT(0) 38*58581478SIvan Bornyakov 396e3bac3eSIvan Bornyakov /* 1000Base-X/SGMII Control Register */ 406e3bac3eSIvan Bornyakov #define MV_1GBX_CTRL (0x2000 + MII_BMCR) 416e3bac3eSIvan Bornyakov 426e3bac3eSIvan Bornyakov /* 1000BASE-X/SGMII Status Register */ 436e3bac3eSIvan Bornyakov #define MV_1GBX_STAT (0x2000 + MII_BMSR) 446e3bac3eSIvan Bornyakov 456e3bac3eSIvan Bornyakov /* 1000Base-X Auto-Negotiation Advertisement Register */ 466e3bac3eSIvan Bornyakov #define MV_1GBX_ADVERTISE (0x2000 + MII_ADVERTISE) 476e3bac3eSIvan Bornyakov 486e3bac3eSIvan Bornyakov /* 1000Base-X PHY Specific Status Register */ 496e3bac3eSIvan Bornyakov #define MV_1GBX_PHY_STAT 0xA003 506e3bac3eSIvan Bornyakov #define MV_1GBX_PHY_STAT_AN_RESOLVED BIT(11) 516e3bac3eSIvan Bornyakov #define MV_1GBX_PHY_STAT_DUPLEX BIT(13) 526e3bac3eSIvan Bornyakov #define MV_1GBX_PHY_STAT_SPEED100 BIT(14) 536e3bac3eSIvan Bornyakov #define MV_1GBX_PHY_STAT_SPEED1000 BIT(15) 546e3bac3eSIvan Bornyakov 556e3bac3eSIvan Bornyakov struct mv2222_data { 566e3bac3eSIvan Bornyakov phy_interface_t line_interface; 576e3bac3eSIvan Bornyakov __ETHTOOL_DECLARE_LINK_MODE_MASK(supported); 58*58581478SIvan Bornyakov bool sfp_link; 596e3bac3eSIvan Bornyakov }; 606e3bac3eSIvan Bornyakov 616e3bac3eSIvan Bornyakov /* SFI PMA transmit enable */ 626e3bac3eSIvan Bornyakov static int mv2222_tx_enable(struct phy_device *phydev) 636e3bac3eSIvan Bornyakov { 646e3bac3eSIvan Bornyakov return phy_clear_bits_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_TXDIS, 656e3bac3eSIvan Bornyakov MDIO_PMD_TXDIS_GLOBAL); 666e3bac3eSIvan Bornyakov } 676e3bac3eSIvan Bornyakov 686e3bac3eSIvan Bornyakov /* SFI PMA transmit disable */ 696e3bac3eSIvan Bornyakov static int mv2222_tx_disable(struct phy_device *phydev) 706e3bac3eSIvan Bornyakov { 716e3bac3eSIvan Bornyakov return phy_set_bits_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_TXDIS, 726e3bac3eSIvan Bornyakov MDIO_PMD_TXDIS_GLOBAL); 736e3bac3eSIvan Bornyakov } 746e3bac3eSIvan Bornyakov 756e3bac3eSIvan Bornyakov static int mv2222_soft_reset(struct phy_device *phydev) 766e3bac3eSIvan Bornyakov { 776e3bac3eSIvan Bornyakov int val, ret; 786e3bac3eSIvan Bornyakov 796e3bac3eSIvan Bornyakov ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, MV_PORT_RST, 806e3bac3eSIvan Bornyakov MV_PORT_RST_SW); 816e3bac3eSIvan Bornyakov if (ret < 0) 826e3bac3eSIvan Bornyakov return ret; 836e3bac3eSIvan Bornyakov 846e3bac3eSIvan Bornyakov return phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND2, MV_PORT_RST, 856e3bac3eSIvan Bornyakov val, !(val & MV_PORT_RST_SW), 866e3bac3eSIvan Bornyakov 5000, 1000000, true); 876e3bac3eSIvan Bornyakov } 886e3bac3eSIvan Bornyakov 896e3bac3eSIvan Bornyakov /* Returns negative on error, 0 if link is down, 1 if link is up */ 906e3bac3eSIvan Bornyakov static int mv2222_read_status_10g(struct phy_device *phydev) 916e3bac3eSIvan Bornyakov { 926e3bac3eSIvan Bornyakov int val, link = 0; 936e3bac3eSIvan Bornyakov 946e3bac3eSIvan Bornyakov val = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_STAT1); 956e3bac3eSIvan Bornyakov if (val < 0) 966e3bac3eSIvan Bornyakov return val; 976e3bac3eSIvan Bornyakov 986e3bac3eSIvan Bornyakov if (val & MDIO_STAT1_LSTATUS) { 996e3bac3eSIvan Bornyakov link = 1; 1006e3bac3eSIvan Bornyakov 1016e3bac3eSIvan Bornyakov /* 10GBASE-R do not support auto-negotiation */ 1026e3bac3eSIvan Bornyakov phydev->autoneg = AUTONEG_DISABLE; 1036e3bac3eSIvan Bornyakov phydev->speed = SPEED_10000; 1046e3bac3eSIvan Bornyakov phydev->duplex = DUPLEX_FULL; 1056e3bac3eSIvan Bornyakov } 1066e3bac3eSIvan Bornyakov 1076e3bac3eSIvan Bornyakov return link; 1086e3bac3eSIvan Bornyakov } 1096e3bac3eSIvan Bornyakov 1106e3bac3eSIvan Bornyakov /* Returns negative on error, 0 if link is down, 1 if link is up */ 1116e3bac3eSIvan Bornyakov static int mv2222_read_status_1g(struct phy_device *phydev) 1126e3bac3eSIvan Bornyakov { 1136e3bac3eSIvan Bornyakov int val, link = 0; 1146e3bac3eSIvan Bornyakov 1156e3bac3eSIvan Bornyakov val = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_1GBX_STAT); 1166e3bac3eSIvan Bornyakov if (val < 0) 1176e3bac3eSIvan Bornyakov return val; 1186e3bac3eSIvan Bornyakov 1196e3bac3eSIvan Bornyakov if (!(val & BMSR_LSTATUS) || 1206e3bac3eSIvan Bornyakov (phydev->autoneg == AUTONEG_ENABLE && 1216e3bac3eSIvan Bornyakov !(val & BMSR_ANEGCOMPLETE))) 1226e3bac3eSIvan Bornyakov return 0; 1236e3bac3eSIvan Bornyakov 1246e3bac3eSIvan Bornyakov link = 1; 1256e3bac3eSIvan Bornyakov 1266e3bac3eSIvan Bornyakov val = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_1GBX_PHY_STAT); 1276e3bac3eSIvan Bornyakov if (val < 0) 1286e3bac3eSIvan Bornyakov return val; 1296e3bac3eSIvan Bornyakov 1306e3bac3eSIvan Bornyakov if (val & MV_1GBX_PHY_STAT_AN_RESOLVED) { 1316e3bac3eSIvan Bornyakov if (val & MV_1GBX_PHY_STAT_DUPLEX) 1326e3bac3eSIvan Bornyakov phydev->duplex = DUPLEX_FULL; 1336e3bac3eSIvan Bornyakov else 1346e3bac3eSIvan Bornyakov phydev->duplex = DUPLEX_HALF; 1356e3bac3eSIvan Bornyakov 1366e3bac3eSIvan Bornyakov if (val & MV_1GBX_PHY_STAT_SPEED1000) 1376e3bac3eSIvan Bornyakov phydev->speed = SPEED_1000; 1386e3bac3eSIvan Bornyakov else if (val & MV_1GBX_PHY_STAT_SPEED100) 1396e3bac3eSIvan Bornyakov phydev->speed = SPEED_100; 1406e3bac3eSIvan Bornyakov else 1416e3bac3eSIvan Bornyakov phydev->speed = SPEED_10; 1426e3bac3eSIvan Bornyakov } 1436e3bac3eSIvan Bornyakov 1446e3bac3eSIvan Bornyakov return link; 1456e3bac3eSIvan Bornyakov } 1466e3bac3eSIvan Bornyakov 147*58581478SIvan Bornyakov static bool mv2222_link_is_operational(struct phy_device *phydev) 148*58581478SIvan Bornyakov { 149*58581478SIvan Bornyakov struct mv2222_data *priv = phydev->priv; 150*58581478SIvan Bornyakov int val; 151*58581478SIvan Bornyakov 152*58581478SIvan Bornyakov val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MV_RX_SIGNAL_DETECT); 153*58581478SIvan Bornyakov if (val < 0 || !(val & MV_RX_SIGNAL_DETECT_GLOBAL)) 154*58581478SIvan Bornyakov return false; 155*58581478SIvan Bornyakov 156*58581478SIvan Bornyakov if (phydev->sfp_bus && !priv->sfp_link) 157*58581478SIvan Bornyakov return false; 158*58581478SIvan Bornyakov 159*58581478SIvan Bornyakov return true; 160*58581478SIvan Bornyakov } 161*58581478SIvan Bornyakov 1626e3bac3eSIvan Bornyakov static int mv2222_read_status(struct phy_device *phydev) 1636e3bac3eSIvan Bornyakov { 1646e3bac3eSIvan Bornyakov struct mv2222_data *priv = phydev->priv; 1656e3bac3eSIvan Bornyakov int link; 1666e3bac3eSIvan Bornyakov 1676e3bac3eSIvan Bornyakov phydev->link = 0; 1686e3bac3eSIvan Bornyakov phydev->speed = SPEED_UNKNOWN; 1696e3bac3eSIvan Bornyakov phydev->duplex = DUPLEX_UNKNOWN; 1706e3bac3eSIvan Bornyakov 171*58581478SIvan Bornyakov if (!mv2222_link_is_operational(phydev)) 172*58581478SIvan Bornyakov return 0; 173*58581478SIvan Bornyakov 1746e3bac3eSIvan Bornyakov if (priv->line_interface == PHY_INTERFACE_MODE_10GBASER) 1756e3bac3eSIvan Bornyakov link = mv2222_read_status_10g(phydev); 1766e3bac3eSIvan Bornyakov else 1776e3bac3eSIvan Bornyakov link = mv2222_read_status_1g(phydev); 1786e3bac3eSIvan Bornyakov 1796e3bac3eSIvan Bornyakov if (link < 0) 1806e3bac3eSIvan Bornyakov return link; 1816e3bac3eSIvan Bornyakov 1826e3bac3eSIvan Bornyakov phydev->link = link; 1836e3bac3eSIvan Bornyakov 1846e3bac3eSIvan Bornyakov return 0; 1856e3bac3eSIvan Bornyakov } 1866e3bac3eSIvan Bornyakov 1876e3bac3eSIvan Bornyakov static int mv2222_disable_aneg(struct phy_device *phydev) 1886e3bac3eSIvan Bornyakov { 1896e3bac3eSIvan Bornyakov int ret = phy_clear_bits_mmd(phydev, MDIO_MMD_PCS, MV_1GBX_CTRL, 1906e3bac3eSIvan Bornyakov BMCR_ANENABLE | BMCR_ANRESTART); 1916e3bac3eSIvan Bornyakov if (ret < 0) 1926e3bac3eSIvan Bornyakov return ret; 1936e3bac3eSIvan Bornyakov 1946e3bac3eSIvan Bornyakov return mv2222_soft_reset(phydev); 1956e3bac3eSIvan Bornyakov } 1966e3bac3eSIvan Bornyakov 1976e3bac3eSIvan Bornyakov static int mv2222_enable_aneg(struct phy_device *phydev) 1986e3bac3eSIvan Bornyakov { 1996e3bac3eSIvan Bornyakov int ret = phy_set_bits_mmd(phydev, MDIO_MMD_PCS, MV_1GBX_CTRL, 2006e3bac3eSIvan Bornyakov BMCR_ANENABLE | BMCR_RESET); 2016e3bac3eSIvan Bornyakov if (ret < 0) 2026e3bac3eSIvan Bornyakov return ret; 2036e3bac3eSIvan Bornyakov 2046e3bac3eSIvan Bornyakov return mv2222_soft_reset(phydev); 2056e3bac3eSIvan Bornyakov } 2066e3bac3eSIvan Bornyakov 2076e3bac3eSIvan Bornyakov static int mv2222_set_sgmii_speed(struct phy_device *phydev) 2086e3bac3eSIvan Bornyakov { 2096e3bac3eSIvan Bornyakov struct mv2222_data *priv = phydev->priv; 2106e3bac3eSIvan Bornyakov 2116e3bac3eSIvan Bornyakov switch (phydev->speed) { 2126e3bac3eSIvan Bornyakov default: 2136e3bac3eSIvan Bornyakov case SPEED_1000: 2146e3bac3eSIvan Bornyakov if ((linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, 2156e3bac3eSIvan Bornyakov priv->supported) || 2166e3bac3eSIvan Bornyakov linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, 2176e3bac3eSIvan Bornyakov priv->supported))) 2186e3bac3eSIvan Bornyakov return phy_modify_mmd(phydev, MDIO_MMD_PCS, 2196e3bac3eSIvan Bornyakov MV_1GBX_CTRL, 2206e3bac3eSIvan Bornyakov BMCR_SPEED1000 | BMCR_SPEED100, 2216e3bac3eSIvan Bornyakov BMCR_SPEED1000); 2226e3bac3eSIvan Bornyakov 2236e3bac3eSIvan Bornyakov fallthrough; 2246e3bac3eSIvan Bornyakov case SPEED_100: 2256e3bac3eSIvan Bornyakov if ((linkmode_test_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, 2266e3bac3eSIvan Bornyakov priv->supported) || 2276e3bac3eSIvan Bornyakov linkmode_test_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, 2286e3bac3eSIvan Bornyakov priv->supported))) 2296e3bac3eSIvan Bornyakov return phy_modify_mmd(phydev, MDIO_MMD_PCS, 2306e3bac3eSIvan Bornyakov MV_1GBX_CTRL, 2316e3bac3eSIvan Bornyakov BMCR_SPEED1000 | BMCR_SPEED100, 2326e3bac3eSIvan Bornyakov BMCR_SPEED100); 2336e3bac3eSIvan Bornyakov fallthrough; 2346e3bac3eSIvan Bornyakov case SPEED_10: 2356e3bac3eSIvan Bornyakov if ((linkmode_test_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, 2366e3bac3eSIvan Bornyakov priv->supported) || 2376e3bac3eSIvan Bornyakov linkmode_test_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, 2386e3bac3eSIvan Bornyakov priv->supported))) 2396e3bac3eSIvan Bornyakov return phy_modify_mmd(phydev, MDIO_MMD_PCS, 2406e3bac3eSIvan Bornyakov MV_1GBX_CTRL, 2416e3bac3eSIvan Bornyakov BMCR_SPEED1000 | BMCR_SPEED100, 2426e3bac3eSIvan Bornyakov BMCR_SPEED10); 2436e3bac3eSIvan Bornyakov 2446e3bac3eSIvan Bornyakov return -EINVAL; 2456e3bac3eSIvan Bornyakov } 2466e3bac3eSIvan Bornyakov } 2476e3bac3eSIvan Bornyakov 2486e3bac3eSIvan Bornyakov static bool mv2222_is_10g_capable(struct phy_device *phydev) 2496e3bac3eSIvan Bornyakov { 2506e3bac3eSIvan Bornyakov struct mv2222_data *priv = phydev->priv; 2516e3bac3eSIvan Bornyakov 2526e3bac3eSIvan Bornyakov return (linkmode_test_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, 2536e3bac3eSIvan Bornyakov priv->supported) || 2546e3bac3eSIvan Bornyakov linkmode_test_bit(ETHTOOL_LINK_MODE_10000baseCR_Full_BIT, 2556e3bac3eSIvan Bornyakov priv->supported) || 2566e3bac3eSIvan Bornyakov linkmode_test_bit(ETHTOOL_LINK_MODE_10000baseSR_Full_BIT, 2576e3bac3eSIvan Bornyakov priv->supported) || 2586e3bac3eSIvan Bornyakov linkmode_test_bit(ETHTOOL_LINK_MODE_10000baseLR_Full_BIT, 2596e3bac3eSIvan Bornyakov priv->supported) || 2606e3bac3eSIvan Bornyakov linkmode_test_bit(ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT, 2616e3bac3eSIvan Bornyakov priv->supported) || 2626e3bac3eSIvan Bornyakov linkmode_test_bit(ETHTOOL_LINK_MODE_10000baseER_Full_BIT, 2636e3bac3eSIvan Bornyakov priv->supported)); 2646e3bac3eSIvan Bornyakov } 2656e3bac3eSIvan Bornyakov 2666e3bac3eSIvan Bornyakov static bool mv2222_is_1gbx_capable(struct phy_device *phydev) 2676e3bac3eSIvan Bornyakov { 2686e3bac3eSIvan Bornyakov struct mv2222_data *priv = phydev->priv; 2696e3bac3eSIvan Bornyakov 2706e3bac3eSIvan Bornyakov return linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, 2716e3bac3eSIvan Bornyakov priv->supported); 2726e3bac3eSIvan Bornyakov } 2736e3bac3eSIvan Bornyakov 2746e3bac3eSIvan Bornyakov static int mv2222_config_line(struct phy_device *phydev) 2756e3bac3eSIvan Bornyakov { 2766e3bac3eSIvan Bornyakov struct mv2222_data *priv = phydev->priv; 2776e3bac3eSIvan Bornyakov 2786e3bac3eSIvan Bornyakov switch (priv->line_interface) { 2796e3bac3eSIvan Bornyakov case PHY_INTERFACE_MODE_10GBASER: 2806e3bac3eSIvan Bornyakov return phy_write_mmd(phydev, MDIO_MMD_VEND2, MV_PCS_CONFIG, 2816e3bac3eSIvan Bornyakov MV_PCS_HOST_XAUI | MV_PCS_LINE_10GBR); 2826e3bac3eSIvan Bornyakov case PHY_INTERFACE_MODE_1000BASEX: 2836e3bac3eSIvan Bornyakov return phy_write_mmd(phydev, MDIO_MMD_VEND2, MV_PCS_CONFIG, 2846e3bac3eSIvan Bornyakov MV_PCS_HOST_XAUI | MV_PCS_LINE_1GBX_AN); 2856e3bac3eSIvan Bornyakov case PHY_INTERFACE_MODE_SGMII: 2866e3bac3eSIvan Bornyakov return phy_write_mmd(phydev, MDIO_MMD_VEND2, MV_PCS_CONFIG, 2876e3bac3eSIvan Bornyakov MV_PCS_HOST_XAUI | MV_PCS_LINE_SGMII_AN); 2886e3bac3eSIvan Bornyakov default: 2896e3bac3eSIvan Bornyakov return -EINVAL; 2906e3bac3eSIvan Bornyakov } 2916e3bac3eSIvan Bornyakov } 2926e3bac3eSIvan Bornyakov 2936e3bac3eSIvan Bornyakov static int mv2222_setup_forced(struct phy_device *phydev) 2946e3bac3eSIvan Bornyakov { 2956e3bac3eSIvan Bornyakov struct mv2222_data *priv = phydev->priv; 2966e3bac3eSIvan Bornyakov bool changed = false; 2976e3bac3eSIvan Bornyakov int ret; 2986e3bac3eSIvan Bornyakov 2996e3bac3eSIvan Bornyakov switch (priv->line_interface) { 3006e3bac3eSIvan Bornyakov case PHY_INTERFACE_MODE_10GBASER: 3016e3bac3eSIvan Bornyakov if (phydev->speed == SPEED_1000 && 3026e3bac3eSIvan Bornyakov mv2222_is_1gbx_capable(phydev)) { 3036e3bac3eSIvan Bornyakov priv->line_interface = PHY_INTERFACE_MODE_1000BASEX; 3046e3bac3eSIvan Bornyakov changed = true; 3056e3bac3eSIvan Bornyakov } 3066e3bac3eSIvan Bornyakov 3076e3bac3eSIvan Bornyakov break; 3086e3bac3eSIvan Bornyakov case PHY_INTERFACE_MODE_1000BASEX: 3096e3bac3eSIvan Bornyakov if (phydev->speed == SPEED_10000 && 3106e3bac3eSIvan Bornyakov mv2222_is_10g_capable(phydev)) { 3116e3bac3eSIvan Bornyakov priv->line_interface = PHY_INTERFACE_MODE_10GBASER; 3126e3bac3eSIvan Bornyakov changed = true; 3136e3bac3eSIvan Bornyakov } 3146e3bac3eSIvan Bornyakov 3156e3bac3eSIvan Bornyakov break; 3166e3bac3eSIvan Bornyakov case PHY_INTERFACE_MODE_SGMII: 3176e3bac3eSIvan Bornyakov ret = mv2222_set_sgmii_speed(phydev); 3186e3bac3eSIvan Bornyakov if (ret < 0) 3196e3bac3eSIvan Bornyakov return ret; 3206e3bac3eSIvan Bornyakov 3216e3bac3eSIvan Bornyakov break; 3226e3bac3eSIvan Bornyakov default: 3236e3bac3eSIvan Bornyakov return -EINVAL; 3246e3bac3eSIvan Bornyakov } 3256e3bac3eSIvan Bornyakov 3266e3bac3eSIvan Bornyakov if (changed) { 3276e3bac3eSIvan Bornyakov ret = mv2222_config_line(phydev); 3286e3bac3eSIvan Bornyakov if (ret < 0) 3296e3bac3eSIvan Bornyakov return ret; 3306e3bac3eSIvan Bornyakov } 3316e3bac3eSIvan Bornyakov 3326e3bac3eSIvan Bornyakov return mv2222_disable_aneg(phydev); 3336e3bac3eSIvan Bornyakov } 3346e3bac3eSIvan Bornyakov 3356e3bac3eSIvan Bornyakov static int mv2222_config_aneg(struct phy_device *phydev) 3366e3bac3eSIvan Bornyakov { 3376e3bac3eSIvan Bornyakov struct mv2222_data *priv = phydev->priv; 3386e3bac3eSIvan Bornyakov int ret, adv; 3396e3bac3eSIvan Bornyakov 3406e3bac3eSIvan Bornyakov /* SFP is not present, do nothing */ 3416e3bac3eSIvan Bornyakov if (priv->line_interface == PHY_INTERFACE_MODE_NA) 3426e3bac3eSIvan Bornyakov return 0; 3436e3bac3eSIvan Bornyakov 3446e3bac3eSIvan Bornyakov if (phydev->autoneg == AUTONEG_DISABLE || 3456e3bac3eSIvan Bornyakov phydev->speed == SPEED_10000) 3466e3bac3eSIvan Bornyakov return mv2222_setup_forced(phydev); 3476e3bac3eSIvan Bornyakov 3486e3bac3eSIvan Bornyakov if (priv->line_interface == PHY_INTERFACE_MODE_10GBASER && 3496e3bac3eSIvan Bornyakov mv2222_is_1gbx_capable(phydev)) { 3506e3bac3eSIvan Bornyakov priv->line_interface = PHY_INTERFACE_MODE_1000BASEX; 3516e3bac3eSIvan Bornyakov ret = mv2222_config_line(phydev); 3526e3bac3eSIvan Bornyakov if (ret < 0) 3536e3bac3eSIvan Bornyakov return ret; 3546e3bac3eSIvan Bornyakov } 3556e3bac3eSIvan Bornyakov 3566e3bac3eSIvan Bornyakov adv = linkmode_adv_to_mii_adv_x(priv->supported, 3576e3bac3eSIvan Bornyakov ETHTOOL_LINK_MODE_1000baseX_Full_BIT); 3586e3bac3eSIvan Bornyakov 3596e3bac3eSIvan Bornyakov ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, MV_1GBX_ADVERTISE, 3606e3bac3eSIvan Bornyakov ADVERTISE_1000XFULL | 3616e3bac3eSIvan Bornyakov ADVERTISE_1000XPAUSE | ADVERTISE_1000XPSE_ASYM, 3626e3bac3eSIvan Bornyakov adv); 3636e3bac3eSIvan Bornyakov if (ret < 0) 3646e3bac3eSIvan Bornyakov return ret; 3656e3bac3eSIvan Bornyakov 3666e3bac3eSIvan Bornyakov return mv2222_enable_aneg(phydev); 3676e3bac3eSIvan Bornyakov } 3686e3bac3eSIvan Bornyakov 3696e3bac3eSIvan Bornyakov static int mv2222_aneg_done(struct phy_device *phydev) 3706e3bac3eSIvan Bornyakov { 3716e3bac3eSIvan Bornyakov int ret; 3726e3bac3eSIvan Bornyakov 3736e3bac3eSIvan Bornyakov if (mv2222_is_10g_capable(phydev)) { 3746e3bac3eSIvan Bornyakov ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_STAT1); 3756e3bac3eSIvan Bornyakov if (ret < 0) 3766e3bac3eSIvan Bornyakov return ret; 3776e3bac3eSIvan Bornyakov 3786e3bac3eSIvan Bornyakov if (ret & MDIO_STAT1_LSTATUS) 3796e3bac3eSIvan Bornyakov return 1; 3806e3bac3eSIvan Bornyakov } 3816e3bac3eSIvan Bornyakov 3826e3bac3eSIvan Bornyakov ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_1GBX_STAT); 3836e3bac3eSIvan Bornyakov if (ret < 0) 3846e3bac3eSIvan Bornyakov return ret; 3856e3bac3eSIvan Bornyakov 3866e3bac3eSIvan Bornyakov return (ret & BMSR_ANEGCOMPLETE); 3876e3bac3eSIvan Bornyakov } 3886e3bac3eSIvan Bornyakov 3896e3bac3eSIvan Bornyakov static int mv2222_resume(struct phy_device *phydev) 3906e3bac3eSIvan Bornyakov { 3916e3bac3eSIvan Bornyakov return mv2222_tx_enable(phydev); 3926e3bac3eSIvan Bornyakov } 3936e3bac3eSIvan Bornyakov 3946e3bac3eSIvan Bornyakov static int mv2222_suspend(struct phy_device *phydev) 3956e3bac3eSIvan Bornyakov { 3966e3bac3eSIvan Bornyakov return mv2222_tx_disable(phydev); 3976e3bac3eSIvan Bornyakov } 3986e3bac3eSIvan Bornyakov 3996e3bac3eSIvan Bornyakov static int mv2222_get_features(struct phy_device *phydev) 4006e3bac3eSIvan Bornyakov { 4016e3bac3eSIvan Bornyakov /* All supported linkmodes are set at probe */ 4026e3bac3eSIvan Bornyakov 4036e3bac3eSIvan Bornyakov return 0; 4046e3bac3eSIvan Bornyakov } 4056e3bac3eSIvan Bornyakov 4066e3bac3eSIvan Bornyakov static int mv2222_config_init(struct phy_device *phydev) 4076e3bac3eSIvan Bornyakov { 4086e3bac3eSIvan Bornyakov if (phydev->interface != PHY_INTERFACE_MODE_XAUI) 4096e3bac3eSIvan Bornyakov return -EINVAL; 4106e3bac3eSIvan Bornyakov 4116e3bac3eSIvan Bornyakov phydev->autoneg = AUTONEG_DISABLE; 4126e3bac3eSIvan Bornyakov 4136e3bac3eSIvan Bornyakov return 0; 4146e3bac3eSIvan Bornyakov } 4156e3bac3eSIvan Bornyakov 4166e3bac3eSIvan Bornyakov static int mv2222_sfp_insert(void *upstream, const struct sfp_eeprom_id *id) 4176e3bac3eSIvan Bornyakov { 4186e3bac3eSIvan Bornyakov struct phy_device *phydev = upstream; 4196e3bac3eSIvan Bornyakov phy_interface_t sfp_interface; 4206e3bac3eSIvan Bornyakov struct mv2222_data *priv; 4216e3bac3eSIvan Bornyakov struct device *dev; 4226e3bac3eSIvan Bornyakov int ret; 4236e3bac3eSIvan Bornyakov 4246e3bac3eSIvan Bornyakov __ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_supported) = { 0, }; 4256e3bac3eSIvan Bornyakov 4266e3bac3eSIvan Bornyakov priv = (struct mv2222_data *)phydev->priv; 4276e3bac3eSIvan Bornyakov dev = &phydev->mdio.dev; 4286e3bac3eSIvan Bornyakov 4296e3bac3eSIvan Bornyakov sfp_parse_support(phydev->sfp_bus, id, sfp_supported); 4306e3bac3eSIvan Bornyakov sfp_interface = sfp_select_interface(phydev->sfp_bus, sfp_supported); 4316e3bac3eSIvan Bornyakov 4326e3bac3eSIvan Bornyakov dev_info(dev, "%s SFP module inserted\n", phy_modes(sfp_interface)); 4336e3bac3eSIvan Bornyakov 4346e3bac3eSIvan Bornyakov if (sfp_interface != PHY_INTERFACE_MODE_10GBASER && 4356e3bac3eSIvan Bornyakov sfp_interface != PHY_INTERFACE_MODE_1000BASEX && 4366e3bac3eSIvan Bornyakov sfp_interface != PHY_INTERFACE_MODE_SGMII) { 4376e3bac3eSIvan Bornyakov dev_err(dev, "Incompatible SFP module inserted\n"); 4386e3bac3eSIvan Bornyakov 4396e3bac3eSIvan Bornyakov return -EINVAL; 4406e3bac3eSIvan Bornyakov } 4416e3bac3eSIvan Bornyakov 4426e3bac3eSIvan Bornyakov priv->line_interface = sfp_interface; 4436e3bac3eSIvan Bornyakov linkmode_and(priv->supported, phydev->supported, sfp_supported); 4446e3bac3eSIvan Bornyakov 4456e3bac3eSIvan Bornyakov ret = mv2222_config_line(phydev); 4466e3bac3eSIvan Bornyakov if (ret < 0) 4476e3bac3eSIvan Bornyakov return ret; 4486e3bac3eSIvan Bornyakov 4496e3bac3eSIvan Bornyakov if (mutex_trylock(&phydev->lock)) { 4506e3bac3eSIvan Bornyakov if (priv->line_interface == PHY_INTERFACE_MODE_10GBASER) 4516e3bac3eSIvan Bornyakov ret = mv2222_setup_forced(phydev); 4526e3bac3eSIvan Bornyakov else 4536e3bac3eSIvan Bornyakov ret = mv2222_config_aneg(phydev); 4546e3bac3eSIvan Bornyakov 4556e3bac3eSIvan Bornyakov mutex_unlock(&phydev->lock); 4566e3bac3eSIvan Bornyakov } 4576e3bac3eSIvan Bornyakov 4586e3bac3eSIvan Bornyakov return ret; 4596e3bac3eSIvan Bornyakov } 4606e3bac3eSIvan Bornyakov 4616e3bac3eSIvan Bornyakov static void mv2222_sfp_remove(void *upstream) 4626e3bac3eSIvan Bornyakov { 4636e3bac3eSIvan Bornyakov struct phy_device *phydev = upstream; 4646e3bac3eSIvan Bornyakov struct mv2222_data *priv; 4656e3bac3eSIvan Bornyakov 4666e3bac3eSIvan Bornyakov priv = (struct mv2222_data *)phydev->priv; 4676e3bac3eSIvan Bornyakov 4686e3bac3eSIvan Bornyakov priv->line_interface = PHY_INTERFACE_MODE_NA; 4696e3bac3eSIvan Bornyakov linkmode_zero(priv->supported); 4706e3bac3eSIvan Bornyakov } 4716e3bac3eSIvan Bornyakov 472*58581478SIvan Bornyakov static void mv2222_sfp_link_up(void *upstream) 473*58581478SIvan Bornyakov { 474*58581478SIvan Bornyakov struct phy_device *phydev = upstream; 475*58581478SIvan Bornyakov struct mv2222_data *priv; 476*58581478SIvan Bornyakov 477*58581478SIvan Bornyakov priv = phydev->priv; 478*58581478SIvan Bornyakov priv->sfp_link = true; 479*58581478SIvan Bornyakov } 480*58581478SIvan Bornyakov 481*58581478SIvan Bornyakov static void mv2222_sfp_link_down(void *upstream) 482*58581478SIvan Bornyakov { 483*58581478SIvan Bornyakov struct phy_device *phydev = upstream; 484*58581478SIvan Bornyakov struct mv2222_data *priv; 485*58581478SIvan Bornyakov 486*58581478SIvan Bornyakov priv = phydev->priv; 487*58581478SIvan Bornyakov priv->sfp_link = false; 488*58581478SIvan Bornyakov } 489*58581478SIvan Bornyakov 4906e3bac3eSIvan Bornyakov static const struct sfp_upstream_ops sfp_phy_ops = { 4916e3bac3eSIvan Bornyakov .module_insert = mv2222_sfp_insert, 4926e3bac3eSIvan Bornyakov .module_remove = mv2222_sfp_remove, 493*58581478SIvan Bornyakov .link_up = mv2222_sfp_link_up, 494*58581478SIvan Bornyakov .link_down = mv2222_sfp_link_down, 4956e3bac3eSIvan Bornyakov .attach = phy_sfp_attach, 4966e3bac3eSIvan Bornyakov .detach = phy_sfp_detach, 4976e3bac3eSIvan Bornyakov }; 4986e3bac3eSIvan Bornyakov 4996e3bac3eSIvan Bornyakov static int mv2222_probe(struct phy_device *phydev) 5006e3bac3eSIvan Bornyakov { 5016e3bac3eSIvan Bornyakov struct device *dev = &phydev->mdio.dev; 5026e3bac3eSIvan Bornyakov struct mv2222_data *priv = NULL; 5036e3bac3eSIvan Bornyakov 5046e3bac3eSIvan Bornyakov __ETHTOOL_DECLARE_LINK_MODE_MASK(supported) = { 0, }; 5056e3bac3eSIvan Bornyakov 5066e3bac3eSIvan Bornyakov linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, supported); 5076e3bac3eSIvan Bornyakov linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT, supported); 5086e3bac3eSIvan Bornyakov linkmode_set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, supported); 5096e3bac3eSIvan Bornyakov linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, supported); 5106e3bac3eSIvan Bornyakov linkmode_set_bit(ETHTOOL_LINK_MODE_TP_BIT, supported); 5116e3bac3eSIvan Bornyakov linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, supported); 5126e3bac3eSIvan Bornyakov linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, supported); 5136e3bac3eSIvan Bornyakov linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, supported); 5146e3bac3eSIvan Bornyakov linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, supported); 5156e3bac3eSIvan Bornyakov linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, supported); 5166e3bac3eSIvan Bornyakov linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, supported); 5176e3bac3eSIvan Bornyakov linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, supported); 5186e3bac3eSIvan Bornyakov linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, supported); 5196e3bac3eSIvan Bornyakov linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseCR_Full_BIT, supported); 5206e3bac3eSIvan Bornyakov linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseSR_Full_BIT, supported); 5216e3bac3eSIvan Bornyakov linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseLR_Full_BIT, supported); 5226e3bac3eSIvan Bornyakov linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT, supported); 5236e3bac3eSIvan Bornyakov linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseER_Full_BIT, supported); 5246e3bac3eSIvan Bornyakov 5256e3bac3eSIvan Bornyakov linkmode_copy(phydev->supported, supported); 5266e3bac3eSIvan Bornyakov 5276e3bac3eSIvan Bornyakov priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 5286e3bac3eSIvan Bornyakov if (!priv) 5296e3bac3eSIvan Bornyakov return -ENOMEM; 5306e3bac3eSIvan Bornyakov 5316e3bac3eSIvan Bornyakov priv->line_interface = PHY_INTERFACE_MODE_NA; 5326e3bac3eSIvan Bornyakov phydev->priv = priv; 5336e3bac3eSIvan Bornyakov 5346e3bac3eSIvan Bornyakov return phy_sfp_probe(phydev, &sfp_phy_ops); 5356e3bac3eSIvan Bornyakov } 5366e3bac3eSIvan Bornyakov 5376e3bac3eSIvan Bornyakov static struct phy_driver mv2222_drivers[] = { 5386e3bac3eSIvan Bornyakov { 5396e3bac3eSIvan Bornyakov .phy_id = MARVELL_PHY_ID_88X2222, 5406e3bac3eSIvan Bornyakov .phy_id_mask = MARVELL_PHY_ID_MASK, 5416e3bac3eSIvan Bornyakov .name = "Marvell 88X2222", 5426e3bac3eSIvan Bornyakov .get_features = mv2222_get_features, 5436e3bac3eSIvan Bornyakov .soft_reset = mv2222_soft_reset, 5446e3bac3eSIvan Bornyakov .config_init = mv2222_config_init, 5456e3bac3eSIvan Bornyakov .config_aneg = mv2222_config_aneg, 5466e3bac3eSIvan Bornyakov .aneg_done = mv2222_aneg_done, 5476e3bac3eSIvan Bornyakov .probe = mv2222_probe, 5486e3bac3eSIvan Bornyakov .suspend = mv2222_suspend, 5496e3bac3eSIvan Bornyakov .resume = mv2222_resume, 5506e3bac3eSIvan Bornyakov .read_status = mv2222_read_status, 5516e3bac3eSIvan Bornyakov }, 5526e3bac3eSIvan Bornyakov }; 5536e3bac3eSIvan Bornyakov module_phy_driver(mv2222_drivers); 5546e3bac3eSIvan Bornyakov 5556e3bac3eSIvan Bornyakov static struct mdio_device_id __maybe_unused mv2222_tbl[] = { 5566e3bac3eSIvan Bornyakov { MARVELL_PHY_ID_88X2222, MARVELL_PHY_ID_MASK }, 5576e3bac3eSIvan Bornyakov { } 5586e3bac3eSIvan Bornyakov }; 5596e3bac3eSIvan Bornyakov MODULE_DEVICE_TABLE(mdio, mv2222_tbl); 5606e3bac3eSIvan Bornyakov 5616e3bac3eSIvan Bornyakov MODULE_DESCRIPTION("Marvell 88x2222 ethernet transceiver driver"); 5626e3bac3eSIvan Bornyakov MODULE_LICENSE("GPL"); 563