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