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