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_gpio.h>
186e3bac3eSIvan Bornyakov #include <linux/sfp.h>
196e3bac3eSIvan Bornyakov #include <linux/netdevice.h>
206e3bac3eSIvan Bornyakov
216e3bac3eSIvan Bornyakov /* Port PCS Configuration */
226e3bac3eSIvan Bornyakov #define MV_PCS_CONFIG 0xF002
236e3bac3eSIvan Bornyakov #define MV_PCS_HOST_XAUI 0x73
246e3bac3eSIvan Bornyakov #define MV_PCS_LINE_10GBR (0x71 << 8)
256e3bac3eSIvan Bornyakov #define MV_PCS_LINE_1GBX_AN (0x7B << 8)
266e3bac3eSIvan Bornyakov #define MV_PCS_LINE_SGMII_AN (0x7F << 8)
276e3bac3eSIvan Bornyakov
286e3bac3eSIvan Bornyakov /* Port Reset and Power Down */
296e3bac3eSIvan Bornyakov #define MV_PORT_RST 0xF003
306e3bac3eSIvan Bornyakov #define MV_LINE_RST_SW BIT(15)
316e3bac3eSIvan Bornyakov #define MV_HOST_RST_SW BIT(7)
326e3bac3eSIvan Bornyakov #define MV_PORT_RST_SW (MV_LINE_RST_SW | MV_HOST_RST_SW)
336e3bac3eSIvan Bornyakov
3458581478SIvan Bornyakov /* PMD Receive Signal Detect */
3558581478SIvan Bornyakov #define MV_RX_SIGNAL_DETECT 0x000A
3658581478SIvan Bornyakov #define MV_RX_SIGNAL_DETECT_GLOBAL BIT(0)
3758581478SIvan Bornyakov
386e3bac3eSIvan Bornyakov /* 1000Base-X/SGMII Control Register */
396e3bac3eSIvan Bornyakov #define MV_1GBX_CTRL (0x2000 + MII_BMCR)
406e3bac3eSIvan Bornyakov
416e3bac3eSIvan Bornyakov /* 1000BASE-X/SGMII Status Register */
426e3bac3eSIvan Bornyakov #define MV_1GBX_STAT (0x2000 + MII_BMSR)
436e3bac3eSIvan Bornyakov
446e3bac3eSIvan Bornyakov /* 1000Base-X Auto-Negotiation Advertisement Register */
456e3bac3eSIvan Bornyakov #define MV_1GBX_ADVERTISE (0x2000 + MII_ADVERTISE)
466e3bac3eSIvan Bornyakov
476e3bac3eSIvan Bornyakov /* 1000Base-X PHY Specific Status Register */
486e3bac3eSIvan Bornyakov #define MV_1GBX_PHY_STAT 0xA003
496e3bac3eSIvan Bornyakov #define MV_1GBX_PHY_STAT_AN_RESOLVED BIT(11)
506e3bac3eSIvan Bornyakov #define MV_1GBX_PHY_STAT_DUPLEX BIT(13)
516e3bac3eSIvan Bornyakov #define MV_1GBX_PHY_STAT_SPEED100 BIT(14)
526e3bac3eSIvan Bornyakov #define MV_1GBX_PHY_STAT_SPEED1000 BIT(15)
536e3bac3eSIvan Bornyakov
54d7029f55SIvan Bornyakov #define AUTONEG_TIMEOUT 3
55d7029f55SIvan Bornyakov
566e3bac3eSIvan Bornyakov struct mv2222_data {
576e3bac3eSIvan Bornyakov phy_interface_t line_interface;
586e3bac3eSIvan Bornyakov __ETHTOOL_DECLARE_LINK_MODE_MASK(supported);
5958581478SIvan Bornyakov bool sfp_link;
606e3bac3eSIvan Bornyakov };
616e3bac3eSIvan Bornyakov
626e3bac3eSIvan Bornyakov /* SFI PMA transmit enable */
mv2222_tx_enable(struct phy_device * phydev)636e3bac3eSIvan Bornyakov static int mv2222_tx_enable(struct phy_device *phydev)
646e3bac3eSIvan Bornyakov {
656e3bac3eSIvan Bornyakov return phy_clear_bits_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_TXDIS,
666e3bac3eSIvan Bornyakov MDIO_PMD_TXDIS_GLOBAL);
676e3bac3eSIvan Bornyakov }
686e3bac3eSIvan Bornyakov
696e3bac3eSIvan Bornyakov /* SFI PMA transmit disable */
mv2222_tx_disable(struct phy_device * phydev)706e3bac3eSIvan Bornyakov static int mv2222_tx_disable(struct phy_device *phydev)
716e3bac3eSIvan Bornyakov {
726e3bac3eSIvan Bornyakov return phy_set_bits_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_TXDIS,
736e3bac3eSIvan Bornyakov MDIO_PMD_TXDIS_GLOBAL);
746e3bac3eSIvan Bornyakov }
756e3bac3eSIvan Bornyakov
mv2222_soft_reset(struct phy_device * phydev)766e3bac3eSIvan Bornyakov static int mv2222_soft_reset(struct phy_device *phydev)
776e3bac3eSIvan Bornyakov {
786e3bac3eSIvan Bornyakov int val, ret;
796e3bac3eSIvan Bornyakov
806e3bac3eSIvan Bornyakov ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, MV_PORT_RST,
816e3bac3eSIvan Bornyakov MV_PORT_RST_SW);
826e3bac3eSIvan Bornyakov if (ret < 0)
836e3bac3eSIvan Bornyakov return ret;
846e3bac3eSIvan Bornyakov
856e3bac3eSIvan Bornyakov return phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND2, MV_PORT_RST,
866e3bac3eSIvan Bornyakov val, !(val & MV_PORT_RST_SW),
876e3bac3eSIvan Bornyakov 5000, 1000000, true);
886e3bac3eSIvan Bornyakov }
896e3bac3eSIvan Bornyakov
mv2222_disable_aneg(struct phy_device * phydev)906e3bac3eSIvan Bornyakov static int mv2222_disable_aneg(struct phy_device *phydev)
916e3bac3eSIvan Bornyakov {
926e3bac3eSIvan Bornyakov int ret = phy_clear_bits_mmd(phydev, MDIO_MMD_PCS, MV_1GBX_CTRL,
936e3bac3eSIvan Bornyakov BMCR_ANENABLE | BMCR_ANRESTART);
946e3bac3eSIvan Bornyakov if (ret < 0)
956e3bac3eSIvan Bornyakov return ret;
966e3bac3eSIvan Bornyakov
976e3bac3eSIvan Bornyakov return mv2222_soft_reset(phydev);
986e3bac3eSIvan Bornyakov }
996e3bac3eSIvan Bornyakov
mv2222_enable_aneg(struct phy_device * phydev)1006e3bac3eSIvan Bornyakov static int mv2222_enable_aneg(struct phy_device *phydev)
1016e3bac3eSIvan Bornyakov {
1026e3bac3eSIvan Bornyakov int ret = phy_set_bits_mmd(phydev, MDIO_MMD_PCS, MV_1GBX_CTRL,
1036e3bac3eSIvan Bornyakov BMCR_ANENABLE | BMCR_RESET);
1046e3bac3eSIvan Bornyakov if (ret < 0)
1056e3bac3eSIvan Bornyakov return ret;
1066e3bac3eSIvan Bornyakov
1076e3bac3eSIvan Bornyakov return mv2222_soft_reset(phydev);
1086e3bac3eSIvan Bornyakov }
1096e3bac3eSIvan Bornyakov
mv2222_set_sgmii_speed(struct phy_device * phydev)1106e3bac3eSIvan Bornyakov static int mv2222_set_sgmii_speed(struct phy_device *phydev)
1116e3bac3eSIvan Bornyakov {
1126e3bac3eSIvan Bornyakov struct mv2222_data *priv = phydev->priv;
1136e3bac3eSIvan Bornyakov
1146e3bac3eSIvan Bornyakov switch (phydev->speed) {
1156e3bac3eSIvan Bornyakov default:
1166e3bac3eSIvan Bornyakov case SPEED_1000:
1176e3bac3eSIvan Bornyakov if ((linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
1186e3bac3eSIvan Bornyakov priv->supported) ||
1196e3bac3eSIvan Bornyakov linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
1206e3bac3eSIvan Bornyakov priv->supported)))
1216e3bac3eSIvan Bornyakov return phy_modify_mmd(phydev, MDIO_MMD_PCS,
1226e3bac3eSIvan Bornyakov MV_1GBX_CTRL,
1236e3bac3eSIvan Bornyakov BMCR_SPEED1000 | BMCR_SPEED100,
1246e3bac3eSIvan Bornyakov BMCR_SPEED1000);
1256e3bac3eSIvan Bornyakov
1266e3bac3eSIvan Bornyakov fallthrough;
1276e3bac3eSIvan Bornyakov case SPEED_100:
1286e3bac3eSIvan Bornyakov if ((linkmode_test_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT,
1296e3bac3eSIvan Bornyakov priv->supported) ||
1306e3bac3eSIvan Bornyakov linkmode_test_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT,
1316e3bac3eSIvan Bornyakov priv->supported)))
1326e3bac3eSIvan Bornyakov return phy_modify_mmd(phydev, MDIO_MMD_PCS,
1336e3bac3eSIvan Bornyakov MV_1GBX_CTRL,
1346e3bac3eSIvan Bornyakov BMCR_SPEED1000 | BMCR_SPEED100,
1356e3bac3eSIvan Bornyakov BMCR_SPEED100);
1366e3bac3eSIvan Bornyakov fallthrough;
1376e3bac3eSIvan Bornyakov case SPEED_10:
1386e3bac3eSIvan Bornyakov if ((linkmode_test_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT,
1396e3bac3eSIvan Bornyakov priv->supported) ||
1406e3bac3eSIvan Bornyakov linkmode_test_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT,
1416e3bac3eSIvan Bornyakov priv->supported)))
1426e3bac3eSIvan Bornyakov return phy_modify_mmd(phydev, MDIO_MMD_PCS,
1436e3bac3eSIvan Bornyakov MV_1GBX_CTRL,
1446e3bac3eSIvan Bornyakov BMCR_SPEED1000 | BMCR_SPEED100,
1456e3bac3eSIvan Bornyakov BMCR_SPEED10);
1466e3bac3eSIvan Bornyakov
1476e3bac3eSIvan Bornyakov return -EINVAL;
1486e3bac3eSIvan Bornyakov }
1496e3bac3eSIvan Bornyakov }
1506e3bac3eSIvan Bornyakov
mv2222_is_10g_capable(struct phy_device * phydev)1516e3bac3eSIvan Bornyakov static bool mv2222_is_10g_capable(struct phy_device *phydev)
1526e3bac3eSIvan Bornyakov {
1536e3bac3eSIvan Bornyakov struct mv2222_data *priv = phydev->priv;
1546e3bac3eSIvan Bornyakov
1556e3bac3eSIvan Bornyakov return (linkmode_test_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
1566e3bac3eSIvan Bornyakov priv->supported) ||
1576e3bac3eSIvan Bornyakov linkmode_test_bit(ETHTOOL_LINK_MODE_10000baseCR_Full_BIT,
1586e3bac3eSIvan Bornyakov priv->supported) ||
1596e3bac3eSIvan Bornyakov linkmode_test_bit(ETHTOOL_LINK_MODE_10000baseSR_Full_BIT,
1606e3bac3eSIvan Bornyakov priv->supported) ||
1616e3bac3eSIvan Bornyakov linkmode_test_bit(ETHTOOL_LINK_MODE_10000baseLR_Full_BIT,
1626e3bac3eSIvan Bornyakov priv->supported) ||
1636e3bac3eSIvan Bornyakov linkmode_test_bit(ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT,
1646e3bac3eSIvan Bornyakov priv->supported) ||
1656e3bac3eSIvan Bornyakov linkmode_test_bit(ETHTOOL_LINK_MODE_10000baseER_Full_BIT,
1666e3bac3eSIvan Bornyakov priv->supported));
1676e3bac3eSIvan Bornyakov }
1686e3bac3eSIvan Bornyakov
mv2222_is_1gbx_capable(struct phy_device * phydev)1696e3bac3eSIvan Bornyakov static bool mv2222_is_1gbx_capable(struct phy_device *phydev)
1706e3bac3eSIvan Bornyakov {
1716e3bac3eSIvan Bornyakov struct mv2222_data *priv = phydev->priv;
1726e3bac3eSIvan Bornyakov
1736e3bac3eSIvan Bornyakov return linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
1746e3bac3eSIvan Bornyakov priv->supported);
1756e3bac3eSIvan Bornyakov }
1766e3bac3eSIvan Bornyakov
mv2222_is_sgmii_capable(struct phy_device * phydev)177d7029f55SIvan Bornyakov static bool mv2222_is_sgmii_capable(struct phy_device *phydev)
178d7029f55SIvan Bornyakov {
179d7029f55SIvan Bornyakov struct mv2222_data *priv = phydev->priv;
180d7029f55SIvan Bornyakov
181d7029f55SIvan Bornyakov return (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
182d7029f55SIvan Bornyakov priv->supported) ||
183d7029f55SIvan Bornyakov linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
184d7029f55SIvan Bornyakov priv->supported) ||
185d7029f55SIvan Bornyakov linkmode_test_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT,
186d7029f55SIvan Bornyakov priv->supported) ||
187d7029f55SIvan Bornyakov linkmode_test_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT,
188d7029f55SIvan Bornyakov priv->supported) ||
189d7029f55SIvan Bornyakov linkmode_test_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT,
190d7029f55SIvan Bornyakov priv->supported) ||
191d7029f55SIvan Bornyakov linkmode_test_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT,
192d7029f55SIvan Bornyakov priv->supported));
193d7029f55SIvan Bornyakov }
194d7029f55SIvan Bornyakov
mv2222_config_line(struct phy_device * phydev)1956e3bac3eSIvan Bornyakov static int mv2222_config_line(struct phy_device *phydev)
1966e3bac3eSIvan Bornyakov {
1976e3bac3eSIvan Bornyakov struct mv2222_data *priv = phydev->priv;
1986e3bac3eSIvan Bornyakov
1996e3bac3eSIvan Bornyakov switch (priv->line_interface) {
2006e3bac3eSIvan Bornyakov case PHY_INTERFACE_MODE_10GBASER:
2016e3bac3eSIvan Bornyakov return phy_write_mmd(phydev, MDIO_MMD_VEND2, MV_PCS_CONFIG,
2026e3bac3eSIvan Bornyakov MV_PCS_HOST_XAUI | MV_PCS_LINE_10GBR);
2036e3bac3eSIvan Bornyakov case PHY_INTERFACE_MODE_1000BASEX:
2046e3bac3eSIvan Bornyakov return phy_write_mmd(phydev, MDIO_MMD_VEND2, MV_PCS_CONFIG,
2056e3bac3eSIvan Bornyakov MV_PCS_HOST_XAUI | MV_PCS_LINE_1GBX_AN);
2066e3bac3eSIvan Bornyakov case PHY_INTERFACE_MODE_SGMII:
2076e3bac3eSIvan Bornyakov return phy_write_mmd(phydev, MDIO_MMD_VEND2, MV_PCS_CONFIG,
2086e3bac3eSIvan Bornyakov MV_PCS_HOST_XAUI | MV_PCS_LINE_SGMII_AN);
2096e3bac3eSIvan Bornyakov default:
2106e3bac3eSIvan Bornyakov return -EINVAL;
2116e3bac3eSIvan Bornyakov }
2126e3bac3eSIvan Bornyakov }
2136e3bac3eSIvan Bornyakov
214d7029f55SIvan Bornyakov /* Switch between 1G (1000Base-X/SGMII) and 10G (10GBase-R) modes */
mv2222_swap_line_type(struct phy_device * phydev)215d7029f55SIvan Bornyakov static int mv2222_swap_line_type(struct phy_device *phydev)
2166e3bac3eSIvan Bornyakov {
2176e3bac3eSIvan Bornyakov struct mv2222_data *priv = phydev->priv;
2186e3bac3eSIvan Bornyakov bool changed = false;
2196e3bac3eSIvan Bornyakov int ret;
2206e3bac3eSIvan Bornyakov
2216e3bac3eSIvan Bornyakov switch (priv->line_interface) {
2226e3bac3eSIvan Bornyakov case PHY_INTERFACE_MODE_10GBASER:
223d7029f55SIvan Bornyakov if (mv2222_is_1gbx_capable(phydev)) {
2246e3bac3eSIvan Bornyakov priv->line_interface = PHY_INTERFACE_MODE_1000BASEX;
2256e3bac3eSIvan Bornyakov changed = true;
2266e3bac3eSIvan Bornyakov }
2276e3bac3eSIvan Bornyakov
228d7029f55SIvan Bornyakov if (mv2222_is_sgmii_capable(phydev)) {
229d7029f55SIvan Bornyakov priv->line_interface = PHY_INTERFACE_MODE_SGMII;
230d7029f55SIvan Bornyakov changed = true;
231d7029f55SIvan Bornyakov }
232d7029f55SIvan Bornyakov
2336e3bac3eSIvan Bornyakov break;
2346e3bac3eSIvan Bornyakov case PHY_INTERFACE_MODE_1000BASEX:
235d7029f55SIvan Bornyakov case PHY_INTERFACE_MODE_SGMII:
236d7029f55SIvan Bornyakov if (mv2222_is_10g_capable(phydev)) {
2376e3bac3eSIvan Bornyakov priv->line_interface = PHY_INTERFACE_MODE_10GBASER;
2386e3bac3eSIvan Bornyakov changed = true;
2396e3bac3eSIvan Bornyakov }
2406e3bac3eSIvan Bornyakov
2416e3bac3eSIvan Bornyakov break;
2426e3bac3eSIvan Bornyakov default:
2436e3bac3eSIvan Bornyakov return -EINVAL;
2446e3bac3eSIvan Bornyakov }
2456e3bac3eSIvan Bornyakov
2466e3bac3eSIvan Bornyakov if (changed) {
2476e3bac3eSIvan Bornyakov ret = mv2222_config_line(phydev);
2486e3bac3eSIvan Bornyakov if (ret < 0)
2496e3bac3eSIvan Bornyakov return ret;
2506e3bac3eSIvan Bornyakov }
2516e3bac3eSIvan Bornyakov
252d7029f55SIvan Bornyakov return 0;
253d7029f55SIvan Bornyakov }
254d7029f55SIvan Bornyakov
mv2222_setup_forced(struct phy_device * phydev)255d7029f55SIvan Bornyakov static int mv2222_setup_forced(struct phy_device *phydev)
256d7029f55SIvan Bornyakov {
257d7029f55SIvan Bornyakov struct mv2222_data *priv = phydev->priv;
258d7029f55SIvan Bornyakov int ret;
259d7029f55SIvan Bornyakov
260d7029f55SIvan Bornyakov if (priv->line_interface == PHY_INTERFACE_MODE_10GBASER) {
261d7029f55SIvan Bornyakov if (phydev->speed < SPEED_10000 &&
262d7029f55SIvan Bornyakov phydev->speed != SPEED_UNKNOWN) {
263d7029f55SIvan Bornyakov ret = mv2222_swap_line_type(phydev);
264d7029f55SIvan Bornyakov if (ret < 0)
265d7029f55SIvan Bornyakov return ret;
266d7029f55SIvan Bornyakov }
267d7029f55SIvan Bornyakov }
268d7029f55SIvan Bornyakov
269d7029f55SIvan Bornyakov if (priv->line_interface == PHY_INTERFACE_MODE_SGMII) {
270d7029f55SIvan Bornyakov ret = mv2222_set_sgmii_speed(phydev);
271d7029f55SIvan Bornyakov if (ret < 0)
272d7029f55SIvan Bornyakov return ret;
273d7029f55SIvan Bornyakov }
274d7029f55SIvan Bornyakov
2756e3bac3eSIvan Bornyakov return mv2222_disable_aneg(phydev);
2766e3bac3eSIvan Bornyakov }
2776e3bac3eSIvan Bornyakov
mv2222_config_aneg(struct phy_device * phydev)2786e3bac3eSIvan Bornyakov static int mv2222_config_aneg(struct phy_device *phydev)
2796e3bac3eSIvan Bornyakov {
2806e3bac3eSIvan Bornyakov struct mv2222_data *priv = phydev->priv;
2816e3bac3eSIvan Bornyakov int ret, adv;
2826e3bac3eSIvan Bornyakov
2836e3bac3eSIvan Bornyakov /* SFP is not present, do nothing */
2846e3bac3eSIvan Bornyakov if (priv->line_interface == PHY_INTERFACE_MODE_NA)
2856e3bac3eSIvan Bornyakov return 0;
2866e3bac3eSIvan Bornyakov
2876e3bac3eSIvan Bornyakov if (phydev->autoneg == AUTONEG_DISABLE ||
288d7029f55SIvan Bornyakov priv->line_interface == PHY_INTERFACE_MODE_10GBASER)
2896e3bac3eSIvan Bornyakov return mv2222_setup_forced(phydev);
2906e3bac3eSIvan Bornyakov
2916e3bac3eSIvan Bornyakov adv = linkmode_adv_to_mii_adv_x(priv->supported,
2926e3bac3eSIvan Bornyakov ETHTOOL_LINK_MODE_1000baseX_Full_BIT);
2936e3bac3eSIvan Bornyakov
2946e3bac3eSIvan Bornyakov ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, MV_1GBX_ADVERTISE,
2956e3bac3eSIvan Bornyakov ADVERTISE_1000XFULL |
2966e3bac3eSIvan Bornyakov ADVERTISE_1000XPAUSE | ADVERTISE_1000XPSE_ASYM,
2976e3bac3eSIvan Bornyakov adv);
2986e3bac3eSIvan Bornyakov if (ret < 0)
2996e3bac3eSIvan Bornyakov return ret;
3006e3bac3eSIvan Bornyakov
3016e3bac3eSIvan Bornyakov return mv2222_enable_aneg(phydev);
3026e3bac3eSIvan Bornyakov }
3036e3bac3eSIvan Bornyakov
mv2222_aneg_done(struct phy_device * phydev)3046e3bac3eSIvan Bornyakov static int mv2222_aneg_done(struct phy_device *phydev)
3056e3bac3eSIvan Bornyakov {
3066e3bac3eSIvan Bornyakov int ret;
3076e3bac3eSIvan Bornyakov
3086e3bac3eSIvan Bornyakov if (mv2222_is_10g_capable(phydev)) {
3096e3bac3eSIvan Bornyakov ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_STAT1);
3106e3bac3eSIvan Bornyakov if (ret < 0)
3116e3bac3eSIvan Bornyakov return ret;
3126e3bac3eSIvan Bornyakov
3136e3bac3eSIvan Bornyakov if (ret & MDIO_STAT1_LSTATUS)
3146e3bac3eSIvan Bornyakov return 1;
3156e3bac3eSIvan Bornyakov }
3166e3bac3eSIvan Bornyakov
3176e3bac3eSIvan Bornyakov ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_1GBX_STAT);
3186e3bac3eSIvan Bornyakov if (ret < 0)
3196e3bac3eSIvan Bornyakov return ret;
3206e3bac3eSIvan Bornyakov
3216e3bac3eSIvan Bornyakov return (ret & BMSR_ANEGCOMPLETE);
3226e3bac3eSIvan Bornyakov }
3236e3bac3eSIvan Bornyakov
324473960a7SIvan Bornyakov /* Returns negative on error, 0 if link is down, 1 if link is up */
mv2222_read_status_10g(struct phy_device * phydev)325473960a7SIvan Bornyakov static int mv2222_read_status_10g(struct phy_device *phydev)
326473960a7SIvan Bornyakov {
327d7029f55SIvan Bornyakov static int timeout;
328473960a7SIvan Bornyakov int val, link = 0;
329473960a7SIvan Bornyakov
330473960a7SIvan Bornyakov val = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_STAT1);
331473960a7SIvan Bornyakov if (val < 0)
332473960a7SIvan Bornyakov return val;
333473960a7SIvan Bornyakov
334473960a7SIvan Bornyakov if (val & MDIO_STAT1_LSTATUS) {
335473960a7SIvan Bornyakov link = 1;
336473960a7SIvan Bornyakov
337473960a7SIvan Bornyakov /* 10GBASE-R do not support auto-negotiation */
338473960a7SIvan Bornyakov phydev->autoneg = AUTONEG_DISABLE;
339473960a7SIvan Bornyakov phydev->speed = SPEED_10000;
340473960a7SIvan Bornyakov phydev->duplex = DUPLEX_FULL;
341d7029f55SIvan Bornyakov } else {
342d7029f55SIvan Bornyakov if (phydev->autoneg == AUTONEG_ENABLE) {
343d7029f55SIvan Bornyakov timeout++;
344d7029f55SIvan Bornyakov
345d7029f55SIvan Bornyakov if (timeout > AUTONEG_TIMEOUT) {
346d7029f55SIvan Bornyakov timeout = 0;
347d7029f55SIvan Bornyakov
348d7029f55SIvan Bornyakov val = mv2222_swap_line_type(phydev);
349d7029f55SIvan Bornyakov if (val < 0)
350d7029f55SIvan Bornyakov return val;
351d7029f55SIvan Bornyakov
352d7029f55SIvan Bornyakov return mv2222_config_aneg(phydev);
353d7029f55SIvan Bornyakov }
354d7029f55SIvan Bornyakov }
355473960a7SIvan Bornyakov }
356473960a7SIvan Bornyakov
357473960a7SIvan Bornyakov return link;
358473960a7SIvan Bornyakov }
359473960a7SIvan Bornyakov
360473960a7SIvan Bornyakov /* Returns negative on error, 0 if link is down, 1 if link is up */
mv2222_read_status_1g(struct phy_device * phydev)361473960a7SIvan Bornyakov static int mv2222_read_status_1g(struct phy_device *phydev)
362473960a7SIvan Bornyakov {
363d7029f55SIvan Bornyakov static int timeout;
364473960a7SIvan Bornyakov int val, link = 0;
365473960a7SIvan Bornyakov
366473960a7SIvan Bornyakov val = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_1GBX_STAT);
367473960a7SIvan Bornyakov if (val < 0)
368473960a7SIvan Bornyakov return val;
369473960a7SIvan Bornyakov
370d7029f55SIvan Bornyakov if (phydev->autoneg == AUTONEG_ENABLE &&
371d7029f55SIvan Bornyakov !(val & BMSR_ANEGCOMPLETE)) {
372d7029f55SIvan Bornyakov timeout++;
373d7029f55SIvan Bornyakov
374d7029f55SIvan Bornyakov if (timeout > AUTONEG_TIMEOUT) {
375d7029f55SIvan Bornyakov timeout = 0;
376d7029f55SIvan Bornyakov
377d7029f55SIvan Bornyakov val = mv2222_swap_line_type(phydev);
378d7029f55SIvan Bornyakov if (val < 0)
379d7029f55SIvan Bornyakov return val;
380d7029f55SIvan Bornyakov
381d7029f55SIvan Bornyakov return mv2222_config_aneg(phydev);
382d7029f55SIvan Bornyakov }
383d7029f55SIvan Bornyakov
384d7029f55SIvan Bornyakov return 0;
385d7029f55SIvan Bornyakov }
386d7029f55SIvan Bornyakov
387d7029f55SIvan Bornyakov if (!(val & BMSR_LSTATUS))
388473960a7SIvan Bornyakov return 0;
389473960a7SIvan Bornyakov
390473960a7SIvan Bornyakov link = 1;
391473960a7SIvan Bornyakov
392473960a7SIvan Bornyakov val = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_1GBX_PHY_STAT);
393473960a7SIvan Bornyakov if (val < 0)
394473960a7SIvan Bornyakov return val;
395473960a7SIvan Bornyakov
396473960a7SIvan Bornyakov if (val & MV_1GBX_PHY_STAT_AN_RESOLVED) {
397473960a7SIvan Bornyakov if (val & MV_1GBX_PHY_STAT_DUPLEX)
398473960a7SIvan Bornyakov phydev->duplex = DUPLEX_FULL;
399473960a7SIvan Bornyakov else
400473960a7SIvan Bornyakov phydev->duplex = DUPLEX_HALF;
401473960a7SIvan Bornyakov
402473960a7SIvan Bornyakov if (val & MV_1GBX_PHY_STAT_SPEED1000)
403473960a7SIvan Bornyakov phydev->speed = SPEED_1000;
404473960a7SIvan Bornyakov else if (val & MV_1GBX_PHY_STAT_SPEED100)
405473960a7SIvan Bornyakov phydev->speed = SPEED_100;
406473960a7SIvan Bornyakov else
407473960a7SIvan Bornyakov phydev->speed = SPEED_10;
408473960a7SIvan Bornyakov }
409473960a7SIvan Bornyakov
410473960a7SIvan Bornyakov return link;
411473960a7SIvan Bornyakov }
412473960a7SIvan Bornyakov
mv2222_link_is_operational(struct phy_device * phydev)413473960a7SIvan Bornyakov static bool mv2222_link_is_operational(struct phy_device *phydev)
414473960a7SIvan Bornyakov {
415473960a7SIvan Bornyakov struct mv2222_data *priv = phydev->priv;
416473960a7SIvan Bornyakov int val;
417473960a7SIvan Bornyakov
418473960a7SIvan Bornyakov val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MV_RX_SIGNAL_DETECT);
419473960a7SIvan Bornyakov if (val < 0 || !(val & MV_RX_SIGNAL_DETECT_GLOBAL))
420473960a7SIvan Bornyakov return false;
421473960a7SIvan Bornyakov
422473960a7SIvan Bornyakov if (phydev->sfp_bus && !priv->sfp_link)
423473960a7SIvan Bornyakov return false;
424473960a7SIvan Bornyakov
425473960a7SIvan Bornyakov return true;
426473960a7SIvan Bornyakov }
427473960a7SIvan Bornyakov
mv2222_read_status(struct phy_device * phydev)428473960a7SIvan Bornyakov static int mv2222_read_status(struct phy_device *phydev)
429473960a7SIvan Bornyakov {
430473960a7SIvan Bornyakov struct mv2222_data *priv = phydev->priv;
431473960a7SIvan Bornyakov int link;
432473960a7SIvan Bornyakov
433473960a7SIvan Bornyakov phydev->link = 0;
434473960a7SIvan Bornyakov phydev->speed = SPEED_UNKNOWN;
435473960a7SIvan Bornyakov phydev->duplex = DUPLEX_UNKNOWN;
436473960a7SIvan Bornyakov
437473960a7SIvan Bornyakov if (!mv2222_link_is_operational(phydev))
438473960a7SIvan Bornyakov return 0;
439473960a7SIvan Bornyakov
440473960a7SIvan Bornyakov if (priv->line_interface == PHY_INTERFACE_MODE_10GBASER)
441473960a7SIvan Bornyakov link = mv2222_read_status_10g(phydev);
442473960a7SIvan Bornyakov else
443473960a7SIvan Bornyakov link = mv2222_read_status_1g(phydev);
444473960a7SIvan Bornyakov
445473960a7SIvan Bornyakov if (link < 0)
446473960a7SIvan Bornyakov return link;
447473960a7SIvan Bornyakov
448473960a7SIvan Bornyakov phydev->link = link;
449473960a7SIvan Bornyakov
450473960a7SIvan Bornyakov return 0;
451473960a7SIvan Bornyakov }
452473960a7SIvan Bornyakov
mv2222_resume(struct phy_device * phydev)4536e3bac3eSIvan Bornyakov static int mv2222_resume(struct phy_device *phydev)
4546e3bac3eSIvan Bornyakov {
4556e3bac3eSIvan Bornyakov return mv2222_tx_enable(phydev);
4566e3bac3eSIvan Bornyakov }
4576e3bac3eSIvan Bornyakov
mv2222_suspend(struct phy_device * phydev)4586e3bac3eSIvan Bornyakov static int mv2222_suspend(struct phy_device *phydev)
4596e3bac3eSIvan Bornyakov {
4606e3bac3eSIvan Bornyakov return mv2222_tx_disable(phydev);
4616e3bac3eSIvan Bornyakov }
4626e3bac3eSIvan Bornyakov
mv2222_get_features(struct phy_device * phydev)4636e3bac3eSIvan Bornyakov static int mv2222_get_features(struct phy_device *phydev)
4646e3bac3eSIvan Bornyakov {
4656e3bac3eSIvan Bornyakov /* All supported linkmodes are set at probe */
4666e3bac3eSIvan Bornyakov
4676e3bac3eSIvan Bornyakov return 0;
4686e3bac3eSIvan Bornyakov }
4696e3bac3eSIvan Bornyakov
mv2222_config_init(struct phy_device * phydev)4706e3bac3eSIvan Bornyakov static int mv2222_config_init(struct phy_device *phydev)
4716e3bac3eSIvan Bornyakov {
4726e3bac3eSIvan Bornyakov if (phydev->interface != PHY_INTERFACE_MODE_XAUI)
4736e3bac3eSIvan Bornyakov return -EINVAL;
4746e3bac3eSIvan Bornyakov
4756e3bac3eSIvan Bornyakov return 0;
4766e3bac3eSIvan Bornyakov }
4776e3bac3eSIvan Bornyakov
mv2222_sfp_insert(void * upstream,const struct sfp_eeprom_id * id)4786e3bac3eSIvan Bornyakov static int mv2222_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
4796e3bac3eSIvan Bornyakov {
480fd580c98SRussell King DECLARE_PHY_INTERFACE_MASK(interfaces);
4816e3bac3eSIvan Bornyakov struct phy_device *phydev = upstream;
4826e3bac3eSIvan Bornyakov phy_interface_t sfp_interface;
4836e3bac3eSIvan Bornyakov struct mv2222_data *priv;
4846e3bac3eSIvan Bornyakov struct device *dev;
4856e3bac3eSIvan Bornyakov int ret;
4866e3bac3eSIvan Bornyakov
4876e3bac3eSIvan Bornyakov __ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_supported) = { 0, };
4886e3bac3eSIvan Bornyakov
489*28b17f62Swuych priv = phydev->priv;
4906e3bac3eSIvan Bornyakov dev = &phydev->mdio.dev;
4916e3bac3eSIvan Bornyakov
492fd580c98SRussell King sfp_parse_support(phydev->sfp_bus, id, sfp_supported, interfaces);
4939794ef5aSIvan Bornyakov phydev->port = sfp_parse_port(phydev->sfp_bus, id, sfp_supported);
4946e3bac3eSIvan Bornyakov sfp_interface = sfp_select_interface(phydev->sfp_bus, sfp_supported);
4956e3bac3eSIvan Bornyakov
4966e3bac3eSIvan Bornyakov dev_info(dev, "%s SFP module inserted\n", phy_modes(sfp_interface));
4976e3bac3eSIvan Bornyakov
4986e3bac3eSIvan Bornyakov if (sfp_interface != PHY_INTERFACE_MODE_10GBASER &&
4996e3bac3eSIvan Bornyakov sfp_interface != PHY_INTERFACE_MODE_1000BASEX &&
5006e3bac3eSIvan Bornyakov sfp_interface != PHY_INTERFACE_MODE_SGMII) {
5016e3bac3eSIvan Bornyakov dev_err(dev, "Incompatible SFP module inserted\n");
5026e3bac3eSIvan Bornyakov
5036e3bac3eSIvan Bornyakov return -EINVAL;
5046e3bac3eSIvan Bornyakov }
5056e3bac3eSIvan Bornyakov
5066e3bac3eSIvan Bornyakov priv->line_interface = sfp_interface;
5076e3bac3eSIvan Bornyakov linkmode_and(priv->supported, phydev->supported, sfp_supported);
5086e3bac3eSIvan Bornyakov
5096e3bac3eSIvan Bornyakov ret = mv2222_config_line(phydev);
5106e3bac3eSIvan Bornyakov if (ret < 0)
5116e3bac3eSIvan Bornyakov return ret;
5126e3bac3eSIvan Bornyakov
5136e3bac3eSIvan Bornyakov if (mutex_trylock(&phydev->lock)) {
5146e3bac3eSIvan Bornyakov ret = mv2222_config_aneg(phydev);
5156e3bac3eSIvan Bornyakov mutex_unlock(&phydev->lock);
5166e3bac3eSIvan Bornyakov }
5176e3bac3eSIvan Bornyakov
5186e3bac3eSIvan Bornyakov return ret;
5196e3bac3eSIvan Bornyakov }
5206e3bac3eSIvan Bornyakov
mv2222_sfp_remove(void * upstream)5216e3bac3eSIvan Bornyakov static void mv2222_sfp_remove(void *upstream)
5226e3bac3eSIvan Bornyakov {
5236e3bac3eSIvan Bornyakov struct phy_device *phydev = upstream;
5246e3bac3eSIvan Bornyakov struct mv2222_data *priv;
5256e3bac3eSIvan Bornyakov
526*28b17f62Swuych priv = phydev->priv;
5276e3bac3eSIvan Bornyakov
5286e3bac3eSIvan Bornyakov priv->line_interface = PHY_INTERFACE_MODE_NA;
5296e3bac3eSIvan Bornyakov linkmode_zero(priv->supported);
5309794ef5aSIvan Bornyakov phydev->port = PORT_NONE;
5316e3bac3eSIvan Bornyakov }
5326e3bac3eSIvan Bornyakov
mv2222_sfp_link_up(void * upstream)53358581478SIvan Bornyakov static void mv2222_sfp_link_up(void *upstream)
53458581478SIvan Bornyakov {
53558581478SIvan Bornyakov struct phy_device *phydev = upstream;
53658581478SIvan Bornyakov struct mv2222_data *priv;
53758581478SIvan Bornyakov
53858581478SIvan Bornyakov priv = phydev->priv;
53958581478SIvan Bornyakov priv->sfp_link = true;
54058581478SIvan Bornyakov }
54158581478SIvan Bornyakov
mv2222_sfp_link_down(void * upstream)54258581478SIvan Bornyakov static void mv2222_sfp_link_down(void *upstream)
54358581478SIvan Bornyakov {
54458581478SIvan Bornyakov struct phy_device *phydev = upstream;
54558581478SIvan Bornyakov struct mv2222_data *priv;
54658581478SIvan Bornyakov
54758581478SIvan Bornyakov priv = phydev->priv;
54858581478SIvan Bornyakov priv->sfp_link = false;
54958581478SIvan Bornyakov }
55058581478SIvan Bornyakov
5516e3bac3eSIvan Bornyakov static const struct sfp_upstream_ops sfp_phy_ops = {
5526e3bac3eSIvan Bornyakov .module_insert = mv2222_sfp_insert,
5536e3bac3eSIvan Bornyakov .module_remove = mv2222_sfp_remove,
55458581478SIvan Bornyakov .link_up = mv2222_sfp_link_up,
55558581478SIvan Bornyakov .link_down = mv2222_sfp_link_down,
5566e3bac3eSIvan Bornyakov .attach = phy_sfp_attach,
5576e3bac3eSIvan Bornyakov .detach = phy_sfp_detach,
5586e3bac3eSIvan Bornyakov };
5596e3bac3eSIvan Bornyakov
mv2222_probe(struct phy_device * phydev)5606e3bac3eSIvan Bornyakov static int mv2222_probe(struct phy_device *phydev)
5616e3bac3eSIvan Bornyakov {
5626e3bac3eSIvan Bornyakov struct device *dev = &phydev->mdio.dev;
5636e3bac3eSIvan Bornyakov struct mv2222_data *priv = NULL;
5646e3bac3eSIvan Bornyakov
5656e3bac3eSIvan Bornyakov __ETHTOOL_DECLARE_LINK_MODE_MASK(supported) = { 0, };
5666e3bac3eSIvan Bornyakov
5676e3bac3eSIvan Bornyakov linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, supported);
5686e3bac3eSIvan Bornyakov linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT, supported);
5696e3bac3eSIvan Bornyakov linkmode_set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, supported);
5706e3bac3eSIvan Bornyakov linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, supported);
5716e3bac3eSIvan Bornyakov linkmode_set_bit(ETHTOOL_LINK_MODE_TP_BIT, supported);
5726e3bac3eSIvan Bornyakov linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, supported);
5736e3bac3eSIvan Bornyakov linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, supported);
5746e3bac3eSIvan Bornyakov linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, supported);
5756e3bac3eSIvan Bornyakov linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, supported);
5766e3bac3eSIvan Bornyakov linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, supported);
5776e3bac3eSIvan Bornyakov linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, supported);
5786e3bac3eSIvan Bornyakov linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, supported);
5796e3bac3eSIvan Bornyakov linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, supported);
5806e3bac3eSIvan Bornyakov linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseCR_Full_BIT, supported);
5816e3bac3eSIvan Bornyakov linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseSR_Full_BIT, supported);
5826e3bac3eSIvan Bornyakov linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseLR_Full_BIT, supported);
5836e3bac3eSIvan Bornyakov linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT, supported);
5846e3bac3eSIvan Bornyakov linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseER_Full_BIT, supported);
5856e3bac3eSIvan Bornyakov
5866e3bac3eSIvan Bornyakov linkmode_copy(phydev->supported, supported);
5876e3bac3eSIvan Bornyakov
5886e3bac3eSIvan Bornyakov priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
5896e3bac3eSIvan Bornyakov if (!priv)
5906e3bac3eSIvan Bornyakov return -ENOMEM;
5916e3bac3eSIvan Bornyakov
5926e3bac3eSIvan Bornyakov priv->line_interface = PHY_INTERFACE_MODE_NA;
5936e3bac3eSIvan Bornyakov phydev->priv = priv;
5946e3bac3eSIvan Bornyakov
5956e3bac3eSIvan Bornyakov return phy_sfp_probe(phydev, &sfp_phy_ops);
5966e3bac3eSIvan Bornyakov }
5976e3bac3eSIvan Bornyakov
5986e3bac3eSIvan Bornyakov static struct phy_driver mv2222_drivers[] = {
5996e3bac3eSIvan Bornyakov {
6006e3bac3eSIvan Bornyakov .phy_id = MARVELL_PHY_ID_88X2222,
6016e3bac3eSIvan Bornyakov .phy_id_mask = MARVELL_PHY_ID_MASK,
6026e3bac3eSIvan Bornyakov .name = "Marvell 88X2222",
6036e3bac3eSIvan Bornyakov .get_features = mv2222_get_features,
6046e3bac3eSIvan Bornyakov .soft_reset = mv2222_soft_reset,
6056e3bac3eSIvan Bornyakov .config_init = mv2222_config_init,
6066e3bac3eSIvan Bornyakov .config_aneg = mv2222_config_aneg,
6076e3bac3eSIvan Bornyakov .aneg_done = mv2222_aneg_done,
6086e3bac3eSIvan Bornyakov .probe = mv2222_probe,
6096e3bac3eSIvan Bornyakov .suspend = mv2222_suspend,
6106e3bac3eSIvan Bornyakov .resume = mv2222_resume,
6116e3bac3eSIvan Bornyakov .read_status = mv2222_read_status,
6126e3bac3eSIvan Bornyakov },
6136e3bac3eSIvan Bornyakov };
6146e3bac3eSIvan Bornyakov module_phy_driver(mv2222_drivers);
6156e3bac3eSIvan Bornyakov
6166e3bac3eSIvan Bornyakov static struct mdio_device_id __maybe_unused mv2222_tbl[] = {
6176e3bac3eSIvan Bornyakov { MARVELL_PHY_ID_88X2222, MARVELL_PHY_ID_MASK },
6186e3bac3eSIvan Bornyakov { }
6196e3bac3eSIvan Bornyakov };
6206e3bac3eSIvan Bornyakov MODULE_DEVICE_TABLE(mdio, mv2222_tbl);
6216e3bac3eSIvan Bornyakov
6226e3bac3eSIvan Bornyakov MODULE_DESCRIPTION("Marvell 88x2222 ethernet transceiver driver");
6236e3bac3eSIvan Bornyakov MODULE_LICENSE("GPL");
624