xref: /openbmc/linux/drivers/net/mdio.c (revision 8be98d2f2a0a262f8bf8a0bc1fdf522b3c7aab17)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21b1c2e95SBen Hutchings /*
31b1c2e95SBen Hutchings  * mdio.c: Generic support for MDIO-compatible transceivers
41b1c2e95SBen Hutchings  * Copyright 2006-2009 Solarflare Communications Inc.
51b1c2e95SBen Hutchings  */
61b1c2e95SBen Hutchings 
71b1c2e95SBen Hutchings #include <linux/kernel.h>
81b1c2e95SBen Hutchings #include <linux/capability.h>
91b1c2e95SBen Hutchings #include <linux/errno.h>
101b1c2e95SBen Hutchings #include <linux/ethtool.h>
111b1c2e95SBen Hutchings #include <linux/mdio.h>
121b1c2e95SBen Hutchings #include <linux/module.h>
131b1c2e95SBen Hutchings 
1430767636SNicolas Reinecke MODULE_DESCRIPTION("Generic support for MDIO-compatible transceivers");
1530767636SNicolas Reinecke MODULE_AUTHOR("Copyright 2006-2009 Solarflare Communications Inc.");
1630767636SNicolas Reinecke MODULE_LICENSE("GPL");
1730767636SNicolas Reinecke 
181b1c2e95SBen Hutchings /**
191b1c2e95SBen Hutchings  * mdio45_probe - probe for an MDIO (clause 45) device
201b1c2e95SBen Hutchings  * @mdio: MDIO interface
211b1c2e95SBen Hutchings  * @prtad: Expected PHY address
221b1c2e95SBen Hutchings  *
231b1c2e95SBen Hutchings  * This sets @prtad and @mmds in the MDIO interface if successful.
241b1c2e95SBen Hutchings  * Returns 0 on success, negative on error.
251b1c2e95SBen Hutchings  */
mdio45_probe(struct mdio_if_info * mdio,int prtad)261b1c2e95SBen Hutchings int mdio45_probe(struct mdio_if_info *mdio, int prtad)
271b1c2e95SBen Hutchings {
281b1c2e95SBen Hutchings 	int mmd, stat2, devs1, devs2;
291b1c2e95SBen Hutchings 
301b1c2e95SBen Hutchings 	/* Assume PHY must have at least one of PMA/PMD, WIS, PCS, PHY
311b1c2e95SBen Hutchings 	 * XS or DTE XS; give up if none is present. */
321b1c2e95SBen Hutchings 	for (mmd = 1; mmd <= 5; mmd++) {
331b1c2e95SBen Hutchings 		/* Is this MMD present? */
341b1c2e95SBen Hutchings 		stat2 = mdio->mdio_read(mdio->dev, prtad, mmd, MDIO_STAT2);
351b1c2e95SBen Hutchings 		if (stat2 < 0 ||
361b1c2e95SBen Hutchings 		    (stat2 & MDIO_STAT2_DEVPRST) != MDIO_STAT2_DEVPRST_VAL)
371b1c2e95SBen Hutchings 			continue;
381b1c2e95SBen Hutchings 
391b1c2e95SBen Hutchings 		/* It should tell us about all the other MMDs */
401b1c2e95SBen Hutchings 		devs1 = mdio->mdio_read(mdio->dev, prtad, mmd, MDIO_DEVS1);
411b1c2e95SBen Hutchings 		devs2 = mdio->mdio_read(mdio->dev, prtad, mmd, MDIO_DEVS2);
421b1c2e95SBen Hutchings 		if (devs1 < 0 || devs2 < 0)
431b1c2e95SBen Hutchings 			continue;
441b1c2e95SBen Hutchings 
451b1c2e95SBen Hutchings 		mdio->prtad = prtad;
461b1c2e95SBen Hutchings 		mdio->mmds = devs1 | (devs2 << 16);
471b1c2e95SBen Hutchings 		return 0;
481b1c2e95SBen Hutchings 	}
491b1c2e95SBen Hutchings 
501b1c2e95SBen Hutchings 	return -ENODEV;
511b1c2e95SBen Hutchings }
521b1c2e95SBen Hutchings EXPORT_SYMBOL(mdio45_probe);
531b1c2e95SBen Hutchings 
541b1c2e95SBen Hutchings /**
551b1c2e95SBen Hutchings  * mdio_set_flag - set or clear flag in an MDIO register
561b1c2e95SBen Hutchings  * @mdio: MDIO interface
571b1c2e95SBen Hutchings  * @prtad: PHY address
581b1c2e95SBen Hutchings  * @devad: MMD address
591b1c2e95SBen Hutchings  * @addr: Register address
601b1c2e95SBen Hutchings  * @mask: Mask for flag (single bit set)
611b1c2e95SBen Hutchings  * @sense: New value of flag
621b1c2e95SBen Hutchings  *
631b1c2e95SBen Hutchings  * This debounces changes: it does not write the register if the flag
641b1c2e95SBen Hutchings  * already has the proper value.  Returns 0 on success, negative on error.
651b1c2e95SBen Hutchings  */
mdio_set_flag(const struct mdio_if_info * mdio,int prtad,int devad,u16 addr,int mask,bool sense)661b1c2e95SBen Hutchings int mdio_set_flag(const struct mdio_if_info *mdio,
671b1c2e95SBen Hutchings 		  int prtad, int devad, u16 addr, int mask,
681b1c2e95SBen Hutchings 		  bool sense)
691b1c2e95SBen Hutchings {
701b1c2e95SBen Hutchings 	int old_val = mdio->mdio_read(mdio->dev, prtad, devad, addr);
711b1c2e95SBen Hutchings 	int new_val;
721b1c2e95SBen Hutchings 
731b1c2e95SBen Hutchings 	if (old_val < 0)
741b1c2e95SBen Hutchings 		return old_val;
751b1c2e95SBen Hutchings 	if (sense)
761b1c2e95SBen Hutchings 		new_val = old_val | mask;
771b1c2e95SBen Hutchings 	else
781b1c2e95SBen Hutchings 		new_val = old_val & ~mask;
791b1c2e95SBen Hutchings 	if (old_val == new_val)
801b1c2e95SBen Hutchings 		return 0;
811b1c2e95SBen Hutchings 	return mdio->mdio_write(mdio->dev, prtad, devad, addr, new_val);
821b1c2e95SBen Hutchings }
831b1c2e95SBen Hutchings EXPORT_SYMBOL(mdio_set_flag);
841b1c2e95SBen Hutchings 
851b1c2e95SBen Hutchings /**
86*177cb787SYang Yingliang  * mdio45_links_ok - is link status up/OK
871b1c2e95SBen Hutchings  * @mdio: MDIO interface
881b1c2e95SBen Hutchings  * @mmd_mask: Mask for MMDs to check
891b1c2e95SBen Hutchings  *
901b1c2e95SBen Hutchings  * Returns 1 if the PHY reports link status up/OK, 0 otherwise.
911b1c2e95SBen Hutchings  * @mmd_mask is normally @mdio->mmds, but if loopback is enabled
921b1c2e95SBen Hutchings  * the MMDs being bypassed should be excluded from the mask.
931b1c2e95SBen Hutchings  */
mdio45_links_ok(const struct mdio_if_info * mdio,u32 mmd_mask)941b1c2e95SBen Hutchings int mdio45_links_ok(const struct mdio_if_info *mdio, u32 mmd_mask)
951b1c2e95SBen Hutchings {
961b1c2e95SBen Hutchings 	int devad, reg;
971b1c2e95SBen Hutchings 
981b1c2e95SBen Hutchings 	if (!mmd_mask) {
991b1c2e95SBen Hutchings 		/* Use absence of XGMII faults in lieu of link state */
1001b1c2e95SBen Hutchings 		reg = mdio->mdio_read(mdio->dev, mdio->prtad,
1011b1c2e95SBen Hutchings 				      MDIO_MMD_PHYXS, MDIO_STAT2);
1021b1c2e95SBen Hutchings 		return reg >= 0 && !(reg & MDIO_STAT2_RXFAULT);
1031b1c2e95SBen Hutchings 	}
1041b1c2e95SBen Hutchings 
1051b1c2e95SBen Hutchings 	for (devad = 0; mmd_mask; devad++) {
1061b1c2e95SBen Hutchings 		if (mmd_mask & (1 << devad)) {
1071b1c2e95SBen Hutchings 			mmd_mask &= ~(1 << devad);
1081b1c2e95SBen Hutchings 
109771046d7SBen Hutchings 			/* Reset the latched status and fault flags */
1101b1c2e95SBen Hutchings 			mdio->mdio_read(mdio->dev, mdio->prtad,
1111b1c2e95SBen Hutchings 					devad, MDIO_STAT1);
112771046d7SBen Hutchings 			if (devad == MDIO_MMD_PMAPMD || devad == MDIO_MMD_PCS ||
113771046d7SBen Hutchings 			    devad == MDIO_MMD_PHYXS || devad == MDIO_MMD_DTEXS)
114771046d7SBen Hutchings 				mdio->mdio_read(mdio->dev, mdio->prtad,
115771046d7SBen Hutchings 						devad, MDIO_STAT2);
116771046d7SBen Hutchings 
117771046d7SBen Hutchings 			/* Check the current status and fault flags */
1181b1c2e95SBen Hutchings 			reg = mdio->mdio_read(mdio->dev, mdio->prtad,
1191b1c2e95SBen Hutchings 					      devad, MDIO_STAT1);
120771046d7SBen Hutchings 			if (reg < 0 ||
121771046d7SBen Hutchings 			    (reg & (MDIO_STAT1_FAULT | MDIO_STAT1_LSTATUS)) !=
122771046d7SBen Hutchings 			    MDIO_STAT1_LSTATUS)
1231b1c2e95SBen Hutchings 				return false;
1241b1c2e95SBen Hutchings 		}
1251b1c2e95SBen Hutchings 	}
1261b1c2e95SBen Hutchings 
1271b1c2e95SBen Hutchings 	return true;
1281b1c2e95SBen Hutchings }
1291b1c2e95SBen Hutchings EXPORT_SYMBOL(mdio45_links_ok);
1301b1c2e95SBen Hutchings 
1311b1c2e95SBen Hutchings /**
1321b1c2e95SBen Hutchings  * mdio45_nway_restart - restart auto-negotiation for this interface
1331b1c2e95SBen Hutchings  * @mdio: MDIO interface
1341b1c2e95SBen Hutchings  *
1351b1c2e95SBen Hutchings  * Returns 0 on success, negative on error.
1361b1c2e95SBen Hutchings  */
mdio45_nway_restart(const struct mdio_if_info * mdio)1371b1c2e95SBen Hutchings int mdio45_nway_restart(const struct mdio_if_info *mdio)
1381b1c2e95SBen Hutchings {
1391b1c2e95SBen Hutchings 	if (!(mdio->mmds & MDIO_DEVS_AN))
1401b1c2e95SBen Hutchings 		return -EOPNOTSUPP;
1411b1c2e95SBen Hutchings 
1421b1c2e95SBen Hutchings 	mdio_set_flag(mdio, mdio->prtad, MDIO_MMD_AN, MDIO_CTRL1,
1431b1c2e95SBen Hutchings 		      MDIO_AN_CTRL1_RESTART, true);
1441b1c2e95SBen Hutchings 	return 0;
1451b1c2e95SBen Hutchings }
1461b1c2e95SBen Hutchings EXPORT_SYMBOL(mdio45_nway_restart);
1471b1c2e95SBen Hutchings 
mdio45_get_an(const struct mdio_if_info * mdio,u16 addr)1481b1c2e95SBen Hutchings static u32 mdio45_get_an(const struct mdio_if_info *mdio, u16 addr)
1491b1c2e95SBen Hutchings {
1501b1c2e95SBen Hutchings 	u32 result = 0;
1511b1c2e95SBen Hutchings 	int reg;
1521b1c2e95SBen Hutchings 
1531b1c2e95SBen Hutchings 	reg = mdio->mdio_read(mdio->dev, mdio->prtad, MDIO_MMD_AN, addr);
1541b1c2e95SBen Hutchings 	if (reg & ADVERTISE_10HALF)
1551b1c2e95SBen Hutchings 		result |= ADVERTISED_10baseT_Half;
1561b1c2e95SBen Hutchings 	if (reg & ADVERTISE_10FULL)
1571b1c2e95SBen Hutchings 		result |= ADVERTISED_10baseT_Full;
1581b1c2e95SBen Hutchings 	if (reg & ADVERTISE_100HALF)
1591b1c2e95SBen Hutchings 		result |= ADVERTISED_100baseT_Half;
1601b1c2e95SBen Hutchings 	if (reg & ADVERTISE_100FULL)
1611b1c2e95SBen Hutchings 		result |= ADVERTISED_100baseT_Full;
16227fbc7dbSBen Hutchings 	if (reg & ADVERTISE_PAUSE_CAP)
16327fbc7dbSBen Hutchings 		result |= ADVERTISED_Pause;
16427fbc7dbSBen Hutchings 	if (reg & ADVERTISE_PAUSE_ASYM)
16527fbc7dbSBen Hutchings 		result |= ADVERTISED_Asym_Pause;
1661b1c2e95SBen Hutchings 	return result;
1671b1c2e95SBen Hutchings }
1681b1c2e95SBen Hutchings 
1691b1c2e95SBen Hutchings /**
1701b1c2e95SBen Hutchings  * mdio45_ethtool_gset_npage - get settings for ETHTOOL_GSET
1711b1c2e95SBen Hutchings  * @mdio: MDIO interface
1721b1c2e95SBen Hutchings  * @ecmd: Ethtool request structure
1731b1c2e95SBen Hutchings  * @npage_adv: Modes currently advertised on next pages
1741b1c2e95SBen Hutchings  * @npage_lpa: Modes advertised by link partner on next pages
1751b1c2e95SBen Hutchings  *
1768ae6dacaSDavid Decotigny  * The @ecmd parameter is expected to have been cleared before calling
1778ae6dacaSDavid Decotigny  * mdio45_ethtool_gset_npage().
1788ae6dacaSDavid Decotigny  *
1791b1c2e95SBen Hutchings  * Since the CSRs for auto-negotiation using next pages are not fully
1801b1c2e95SBen Hutchings  * standardised, this function does not attempt to decode them.  The
1811b1c2e95SBen Hutchings  * caller must pass them in.
1821b1c2e95SBen Hutchings  */
mdio45_ethtool_gset_npage(const struct mdio_if_info * mdio,struct ethtool_cmd * ecmd,u32 npage_adv,u32 npage_lpa)1831b1c2e95SBen Hutchings void mdio45_ethtool_gset_npage(const struct mdio_if_info *mdio,
1841b1c2e95SBen Hutchings 			       struct ethtool_cmd *ecmd,
1851b1c2e95SBen Hutchings 			       u32 npage_adv, u32 npage_lpa)
1861b1c2e95SBen Hutchings {
1871b1c2e95SBen Hutchings 	int reg;
18870739497SDavid Decotigny 	u32 speed;
1891b1c2e95SBen Hutchings 
1909c4df53bSBen Hutchings 	BUILD_BUG_ON(MDIO_SUPPORTS_C22 != ETH_MDIO_SUPPORTS_C22);
1919c4df53bSBen Hutchings 	BUILD_BUG_ON(MDIO_SUPPORTS_C45 != ETH_MDIO_SUPPORTS_C45);
1929c4df53bSBen Hutchings 
1931b1c2e95SBen Hutchings 	ecmd->transceiver = XCVR_INTERNAL;
1941b1c2e95SBen Hutchings 	ecmd->phy_address = mdio->prtad;
1950c09c1a4SBen Hutchings 	ecmd->mdio_support =
1960c09c1a4SBen Hutchings 		mdio->mode_support & (MDIO_SUPPORTS_C45 | MDIO_SUPPORTS_C22);
1971b1c2e95SBen Hutchings 
1981b1c2e95SBen Hutchings 	reg = mdio->mdio_read(mdio->dev, mdio->prtad, MDIO_MMD_PMAPMD,
1991b1c2e95SBen Hutchings 			      MDIO_CTRL2);
2001b1c2e95SBen Hutchings 	switch (reg & MDIO_PMA_CTRL2_TYPE) {
2011b1c2e95SBen Hutchings 	case MDIO_PMA_CTRL2_10GBT:
2021b1c2e95SBen Hutchings 	case MDIO_PMA_CTRL2_1000BT:
2031b1c2e95SBen Hutchings 	case MDIO_PMA_CTRL2_100BTX:
2041b1c2e95SBen Hutchings 	case MDIO_PMA_CTRL2_10BT:
2051b1c2e95SBen Hutchings 		ecmd->port = PORT_TP;
2061b1c2e95SBen Hutchings 		ecmd->supported = SUPPORTED_TP;
2071b1c2e95SBen Hutchings 		reg = mdio->mdio_read(mdio->dev, mdio->prtad, MDIO_MMD_PMAPMD,
2081b1c2e95SBen Hutchings 				      MDIO_SPEED);
2091b1c2e95SBen Hutchings 		if (reg & MDIO_SPEED_10G)
2101b1c2e95SBen Hutchings 			ecmd->supported |= SUPPORTED_10000baseT_Full;
2111b1c2e95SBen Hutchings 		if (reg & MDIO_PMA_SPEED_1000)
2121b1c2e95SBen Hutchings 			ecmd->supported |= (SUPPORTED_1000baseT_Full |
2131b1c2e95SBen Hutchings 					    SUPPORTED_1000baseT_Half);
2141b1c2e95SBen Hutchings 		if (reg & MDIO_PMA_SPEED_100)
2151b1c2e95SBen Hutchings 			ecmd->supported |= (SUPPORTED_100baseT_Full |
2161b1c2e95SBen Hutchings 					    SUPPORTED_100baseT_Half);
2171b1c2e95SBen Hutchings 		if (reg & MDIO_PMA_SPEED_10)
2181b1c2e95SBen Hutchings 			ecmd->supported |= (SUPPORTED_10baseT_Full |
2191b1c2e95SBen Hutchings 					    SUPPORTED_10baseT_Half);
2201b1c2e95SBen Hutchings 		ecmd->advertising = ADVERTISED_TP;
2211b1c2e95SBen Hutchings 		break;
2221b1c2e95SBen Hutchings 
2231b1c2e95SBen Hutchings 	case MDIO_PMA_CTRL2_10GBCX4:
224894b19a6SBen Hutchings 		ecmd->port = PORT_OTHER;
225894b19a6SBen Hutchings 		ecmd->supported = 0;
226894b19a6SBen Hutchings 		ecmd->advertising = 0;
227894b19a6SBen Hutchings 		break;
228894b19a6SBen Hutchings 
2291b1c2e95SBen Hutchings 	case MDIO_PMA_CTRL2_10GBKX4:
2301b1c2e95SBen Hutchings 	case MDIO_PMA_CTRL2_10GBKR:
2311b1c2e95SBen Hutchings 	case MDIO_PMA_CTRL2_1000BKX:
2321b1c2e95SBen Hutchings 		ecmd->port = PORT_OTHER;
233894b19a6SBen Hutchings 		ecmd->supported = SUPPORTED_Backplane;
234894b19a6SBen Hutchings 		reg = mdio->mdio_read(mdio->dev, mdio->prtad, MDIO_MMD_PMAPMD,
235894b19a6SBen Hutchings 				      MDIO_PMA_EXTABLE);
236894b19a6SBen Hutchings 		if (reg & MDIO_PMA_EXTABLE_10GBKX4)
237894b19a6SBen Hutchings 			ecmd->supported |= SUPPORTED_10000baseKX4_Full;
238894b19a6SBen Hutchings 		if (reg & MDIO_PMA_EXTABLE_10GBKR)
239894b19a6SBen Hutchings 			ecmd->supported |= SUPPORTED_10000baseKR_Full;
240894b19a6SBen Hutchings 		if (reg & MDIO_PMA_EXTABLE_1000BKX)
241894b19a6SBen Hutchings 			ecmd->supported |= SUPPORTED_1000baseKX_Full;
242894b19a6SBen Hutchings 		reg = mdio->mdio_read(mdio->dev, mdio->prtad, MDIO_MMD_PMAPMD,
243894b19a6SBen Hutchings 				      MDIO_PMA_10GBR_FECABLE);
244894b19a6SBen Hutchings 		if (reg & MDIO_PMA_10GBR_FECABLE_ABLE)
245894b19a6SBen Hutchings 			ecmd->supported |= SUPPORTED_10000baseR_FEC;
246894b19a6SBen Hutchings 		ecmd->advertising = ADVERTISED_Backplane;
2471b1c2e95SBen Hutchings 		break;
2481b1c2e95SBen Hutchings 
2491b1c2e95SBen Hutchings 	/* All the other defined modes are flavours of optical */
2501b1c2e95SBen Hutchings 	default:
2511b1c2e95SBen Hutchings 		ecmd->port = PORT_FIBRE;
2521b1c2e95SBen Hutchings 		ecmd->supported = SUPPORTED_FIBRE;
2531b1c2e95SBen Hutchings 		ecmd->advertising = ADVERTISED_FIBRE;
2541b1c2e95SBen Hutchings 		break;
2551b1c2e95SBen Hutchings 	}
2561b1c2e95SBen Hutchings 
2571b1c2e95SBen Hutchings 	if (mdio->mmds & MDIO_DEVS_AN) {
2581b1c2e95SBen Hutchings 		ecmd->supported |= SUPPORTED_Autoneg;
2591b1c2e95SBen Hutchings 		reg = mdio->mdio_read(mdio->dev, mdio->prtad, MDIO_MMD_AN,
2601b1c2e95SBen Hutchings 				      MDIO_CTRL1);
2611b1c2e95SBen Hutchings 		if (reg & MDIO_AN_CTRL1_ENABLE) {
2621b1c2e95SBen Hutchings 			ecmd->autoneg = AUTONEG_ENABLE;
2631b1c2e95SBen Hutchings 			ecmd->advertising |=
2641b1c2e95SBen Hutchings 				ADVERTISED_Autoneg |
2651b1c2e95SBen Hutchings 				mdio45_get_an(mdio, MDIO_AN_ADVERTISE) |
2661b1c2e95SBen Hutchings 				npage_adv;
2671b1c2e95SBen Hutchings 		} else {
2681b1c2e95SBen Hutchings 			ecmd->autoneg = AUTONEG_DISABLE;
2691b1c2e95SBen Hutchings 		}
2701b1c2e95SBen Hutchings 	} else {
2711b1c2e95SBen Hutchings 		ecmd->autoneg = AUTONEG_DISABLE;
2721b1c2e95SBen Hutchings 	}
2731b1c2e95SBen Hutchings 
2741b1c2e95SBen Hutchings 	if (ecmd->autoneg) {
2751b1c2e95SBen Hutchings 		u32 modes = 0;
2760c09c1a4SBen Hutchings 		int an_stat = mdio->mdio_read(mdio->dev, mdio->prtad,
2770c09c1a4SBen Hutchings 					      MDIO_MMD_AN, MDIO_STAT1);
2781b1c2e95SBen Hutchings 
2791b1c2e95SBen Hutchings 		/* If AN is complete and successful, report best common
2801b1c2e95SBen Hutchings 		 * mode, otherwise report best advertised mode. */
2810c09c1a4SBen Hutchings 		if (an_stat & MDIO_AN_STAT1_COMPLETE) {
2820c09c1a4SBen Hutchings 			ecmd->lp_advertising =
2830c09c1a4SBen Hutchings 				mdio45_get_an(mdio, MDIO_AN_LPA) | npage_lpa;
2840c09c1a4SBen Hutchings 			if (an_stat & MDIO_AN_STAT1_LPABLE)
2850c09c1a4SBen Hutchings 				ecmd->lp_advertising |= ADVERTISED_Autoneg;
2860c09c1a4SBen Hutchings 			modes = ecmd->advertising & ecmd->lp_advertising;
2870c09c1a4SBen Hutchings 		}
2880c09c1a4SBen Hutchings 		if ((modes & ~ADVERTISED_Autoneg) == 0)
2891b1c2e95SBen Hutchings 			modes = ecmd->advertising;
2901b1c2e95SBen Hutchings 
291894b19a6SBen Hutchings 		if (modes & (ADVERTISED_10000baseT_Full |
292894b19a6SBen Hutchings 			     ADVERTISED_10000baseKX4_Full |
293894b19a6SBen Hutchings 			     ADVERTISED_10000baseKR_Full)) {
29470739497SDavid Decotigny 			speed = SPEED_10000;
2951b1c2e95SBen Hutchings 			ecmd->duplex = DUPLEX_FULL;
2961b1c2e95SBen Hutchings 		} else if (modes & (ADVERTISED_1000baseT_Full |
297894b19a6SBen Hutchings 				    ADVERTISED_1000baseT_Half |
298894b19a6SBen Hutchings 				    ADVERTISED_1000baseKX_Full)) {
29970739497SDavid Decotigny 			speed = SPEED_1000;
300894b19a6SBen Hutchings 			ecmd->duplex = !(modes & ADVERTISED_1000baseT_Half);
3011b1c2e95SBen Hutchings 		} else if (modes & (ADVERTISED_100baseT_Full |
3021b1c2e95SBen Hutchings 				    ADVERTISED_100baseT_Half)) {
30370739497SDavid Decotigny 			speed = SPEED_100;
3041b1c2e95SBen Hutchings 			ecmd->duplex = !!(modes & ADVERTISED_100baseT_Full);
3051b1c2e95SBen Hutchings 		} else {
30670739497SDavid Decotigny 			speed = SPEED_10;
3071b1c2e95SBen Hutchings 			ecmd->duplex = !!(modes & ADVERTISED_10baseT_Full);
3081b1c2e95SBen Hutchings 		}
3091b1c2e95SBen Hutchings 	} else {
3101b1c2e95SBen Hutchings 		/* Report forced settings */
3111b1c2e95SBen Hutchings 		reg = mdio->mdio_read(mdio->dev, mdio->prtad, MDIO_MMD_PMAPMD,
3121b1c2e95SBen Hutchings 				      MDIO_CTRL1);
31370739497SDavid Decotigny 		speed = (((reg & MDIO_PMA_CTRL1_SPEED1000) ? 100 : 1)
31470739497SDavid Decotigny 			 * ((reg & MDIO_PMA_CTRL1_SPEED100) ? 100 : 10));
3151b1c2e95SBen Hutchings 		ecmd->duplex = (reg & MDIO_CTRL1_FULLDPLX ||
31670739497SDavid Decotigny 				speed == SPEED_10000);
3171b1c2e95SBen Hutchings 	}
318d005ba6cSBen Hutchings 
31970739497SDavid Decotigny 	ethtool_cmd_speed_set(ecmd, speed);
32070739497SDavid Decotigny 
321d005ba6cSBen Hutchings 	/* 10GBASE-T MDI/MDI-X */
32270739497SDavid Decotigny 	if (ecmd->port == PORT_TP
32370739497SDavid Decotigny 	    && (ethtool_cmd_speed(ecmd) == SPEED_10000)) {
324d005ba6cSBen Hutchings 		switch (mdio->mdio_read(mdio->dev, mdio->prtad, MDIO_MMD_PMAPMD,
325d005ba6cSBen Hutchings 					MDIO_PMA_10GBT_SWAPPOL)) {
326d005ba6cSBen Hutchings 		case MDIO_PMA_10GBT_SWAPPOL_ABNX | MDIO_PMA_10GBT_SWAPPOL_CDNX:
327d005ba6cSBen Hutchings 			ecmd->eth_tp_mdix = ETH_TP_MDI;
328d005ba6cSBen Hutchings 			break;
329d005ba6cSBen Hutchings 		case 0:
330d005ba6cSBen Hutchings 			ecmd->eth_tp_mdix = ETH_TP_MDI_X;
331d005ba6cSBen Hutchings 			break;
332d005ba6cSBen Hutchings 		default:
333d005ba6cSBen Hutchings 			/* It's complicated... */
334d005ba6cSBen Hutchings 			ecmd->eth_tp_mdix = ETH_TP_MDI_INVALID;
335d005ba6cSBen Hutchings 			break;
336d005ba6cSBen Hutchings 		}
337d005ba6cSBen Hutchings 	}
3381b1c2e95SBen Hutchings }
3391b1c2e95SBen Hutchings EXPORT_SYMBOL(mdio45_ethtool_gset_npage);
3401b1c2e95SBen Hutchings 
3411b1c2e95SBen Hutchings /**
3428e4881aaSPhilippe Reynes  * mdio45_ethtool_ksettings_get_npage - get settings for ETHTOOL_GLINKSETTINGS
3438e4881aaSPhilippe Reynes  * @mdio: MDIO interface
3448e4881aaSPhilippe Reynes  * @cmd: Ethtool request structure
3458e4881aaSPhilippe Reynes  * @npage_adv: Modes currently advertised on next pages
3468e4881aaSPhilippe Reynes  * @npage_lpa: Modes advertised by link partner on next pages
3478e4881aaSPhilippe Reynes  *
3488e4881aaSPhilippe Reynes  * The @cmd parameter is expected to have been cleared before calling
3498e4881aaSPhilippe Reynes  * mdio45_ethtool_ksettings_get_npage().
3508e4881aaSPhilippe Reynes  *
3518e4881aaSPhilippe Reynes  * Since the CSRs for auto-negotiation using next pages are not fully
3528e4881aaSPhilippe Reynes  * standardised, this function does not attempt to decode them.  The
3538e4881aaSPhilippe Reynes  * caller must pass them in.
3548e4881aaSPhilippe Reynes  */
mdio45_ethtool_ksettings_get_npage(const struct mdio_if_info * mdio,struct ethtool_link_ksettings * cmd,u32 npage_adv,u32 npage_lpa)3558e4881aaSPhilippe Reynes void mdio45_ethtool_ksettings_get_npage(const struct mdio_if_info *mdio,
3568e4881aaSPhilippe Reynes 					struct ethtool_link_ksettings *cmd,
3578e4881aaSPhilippe Reynes 					u32 npage_adv, u32 npage_lpa)
3588e4881aaSPhilippe Reynes {
3598e4881aaSPhilippe Reynes 	int reg;
3608e4881aaSPhilippe Reynes 	u32 speed, supported = 0, advertising = 0, lp_advertising = 0;
3618e4881aaSPhilippe Reynes 
3628e4881aaSPhilippe Reynes 	BUILD_BUG_ON(MDIO_SUPPORTS_C22 != ETH_MDIO_SUPPORTS_C22);
3638e4881aaSPhilippe Reynes 	BUILD_BUG_ON(MDIO_SUPPORTS_C45 != ETH_MDIO_SUPPORTS_C45);
3648e4881aaSPhilippe Reynes 
3658e4881aaSPhilippe Reynes 	cmd->base.phy_address = mdio->prtad;
3668e4881aaSPhilippe Reynes 	cmd->base.mdio_support =
3678e4881aaSPhilippe Reynes 		mdio->mode_support & (MDIO_SUPPORTS_C45 | MDIO_SUPPORTS_C22);
3688e4881aaSPhilippe Reynes 
3698e4881aaSPhilippe Reynes 	reg = mdio->mdio_read(mdio->dev, mdio->prtad, MDIO_MMD_PMAPMD,
3708e4881aaSPhilippe Reynes 			      MDIO_CTRL2);
3718e4881aaSPhilippe Reynes 	switch (reg & MDIO_PMA_CTRL2_TYPE) {
3728e4881aaSPhilippe Reynes 	case MDIO_PMA_CTRL2_10GBT:
3738e4881aaSPhilippe Reynes 	case MDIO_PMA_CTRL2_1000BT:
3748e4881aaSPhilippe Reynes 	case MDIO_PMA_CTRL2_100BTX:
3758e4881aaSPhilippe Reynes 	case MDIO_PMA_CTRL2_10BT:
3768e4881aaSPhilippe Reynes 		cmd->base.port = PORT_TP;
3778e4881aaSPhilippe Reynes 		supported = SUPPORTED_TP;
3788e4881aaSPhilippe Reynes 		reg = mdio->mdio_read(mdio->dev, mdio->prtad, MDIO_MMD_PMAPMD,
3798e4881aaSPhilippe Reynes 				      MDIO_SPEED);
3808e4881aaSPhilippe Reynes 		if (reg & MDIO_SPEED_10G)
3818e4881aaSPhilippe Reynes 			supported |= SUPPORTED_10000baseT_Full;
3828e4881aaSPhilippe Reynes 		if (reg & MDIO_PMA_SPEED_1000)
3838e4881aaSPhilippe Reynes 			supported |= (SUPPORTED_1000baseT_Full |
3848e4881aaSPhilippe Reynes 					    SUPPORTED_1000baseT_Half);
3858e4881aaSPhilippe Reynes 		if (reg & MDIO_PMA_SPEED_100)
3868e4881aaSPhilippe Reynes 			supported |= (SUPPORTED_100baseT_Full |
3878e4881aaSPhilippe Reynes 					    SUPPORTED_100baseT_Half);
3888e4881aaSPhilippe Reynes 		if (reg & MDIO_PMA_SPEED_10)
3898e4881aaSPhilippe Reynes 			supported |= (SUPPORTED_10baseT_Full |
3908e4881aaSPhilippe Reynes 					    SUPPORTED_10baseT_Half);
3918e4881aaSPhilippe Reynes 		advertising = ADVERTISED_TP;
3928e4881aaSPhilippe Reynes 		break;
3938e4881aaSPhilippe Reynes 
3948e4881aaSPhilippe Reynes 	case MDIO_PMA_CTRL2_10GBCX4:
3958e4881aaSPhilippe Reynes 		cmd->base.port = PORT_OTHER;
3968e4881aaSPhilippe Reynes 		supported = 0;
3978e4881aaSPhilippe Reynes 		advertising = 0;
3988e4881aaSPhilippe Reynes 		break;
3998e4881aaSPhilippe Reynes 
4008e4881aaSPhilippe Reynes 	case MDIO_PMA_CTRL2_10GBKX4:
4018e4881aaSPhilippe Reynes 	case MDIO_PMA_CTRL2_10GBKR:
4028e4881aaSPhilippe Reynes 	case MDIO_PMA_CTRL2_1000BKX:
4038e4881aaSPhilippe Reynes 		cmd->base.port = PORT_OTHER;
4048e4881aaSPhilippe Reynes 		supported = SUPPORTED_Backplane;
4058e4881aaSPhilippe Reynes 		reg = mdio->mdio_read(mdio->dev, mdio->prtad, MDIO_MMD_PMAPMD,
4068e4881aaSPhilippe Reynes 				      MDIO_PMA_EXTABLE);
4078e4881aaSPhilippe Reynes 		if (reg & MDIO_PMA_EXTABLE_10GBKX4)
4088e4881aaSPhilippe Reynes 			supported |= SUPPORTED_10000baseKX4_Full;
4098e4881aaSPhilippe Reynes 		if (reg & MDIO_PMA_EXTABLE_10GBKR)
4108e4881aaSPhilippe Reynes 			supported |= SUPPORTED_10000baseKR_Full;
4118e4881aaSPhilippe Reynes 		if (reg & MDIO_PMA_EXTABLE_1000BKX)
4128e4881aaSPhilippe Reynes 			supported |= SUPPORTED_1000baseKX_Full;
4138e4881aaSPhilippe Reynes 		reg = mdio->mdio_read(mdio->dev, mdio->prtad, MDIO_MMD_PMAPMD,
4148e4881aaSPhilippe Reynes 				      MDIO_PMA_10GBR_FECABLE);
4158e4881aaSPhilippe Reynes 		if (reg & MDIO_PMA_10GBR_FECABLE_ABLE)
4168e4881aaSPhilippe Reynes 			supported |= SUPPORTED_10000baseR_FEC;
4178e4881aaSPhilippe Reynes 		advertising = ADVERTISED_Backplane;
4188e4881aaSPhilippe Reynes 		break;
4198e4881aaSPhilippe Reynes 
4208e4881aaSPhilippe Reynes 	/* All the other defined modes are flavours of optical */
4218e4881aaSPhilippe Reynes 	default:
4228e4881aaSPhilippe Reynes 		cmd->base.port = PORT_FIBRE;
4238e4881aaSPhilippe Reynes 		supported = SUPPORTED_FIBRE;
4248e4881aaSPhilippe Reynes 		advertising = ADVERTISED_FIBRE;
4258e4881aaSPhilippe Reynes 		break;
4268e4881aaSPhilippe Reynes 	}
4278e4881aaSPhilippe Reynes 
4288e4881aaSPhilippe Reynes 	if (mdio->mmds & MDIO_DEVS_AN) {
4298e4881aaSPhilippe Reynes 		supported |= SUPPORTED_Autoneg;
4308e4881aaSPhilippe Reynes 		reg = mdio->mdio_read(mdio->dev, mdio->prtad, MDIO_MMD_AN,
4318e4881aaSPhilippe Reynes 				      MDIO_CTRL1);
4328e4881aaSPhilippe Reynes 		if (reg & MDIO_AN_CTRL1_ENABLE) {
4338e4881aaSPhilippe Reynes 			cmd->base.autoneg = AUTONEG_ENABLE;
4348e4881aaSPhilippe Reynes 			advertising |=
4358e4881aaSPhilippe Reynes 				ADVERTISED_Autoneg |
4368e4881aaSPhilippe Reynes 				mdio45_get_an(mdio, MDIO_AN_ADVERTISE) |
4378e4881aaSPhilippe Reynes 				npage_adv;
4388e4881aaSPhilippe Reynes 		} else {
4398e4881aaSPhilippe Reynes 			cmd->base.autoneg = AUTONEG_DISABLE;
4408e4881aaSPhilippe Reynes 		}
4418e4881aaSPhilippe Reynes 	} else {
4428e4881aaSPhilippe Reynes 		cmd->base.autoneg = AUTONEG_DISABLE;
4438e4881aaSPhilippe Reynes 	}
4448e4881aaSPhilippe Reynes 
4458e4881aaSPhilippe Reynes 	if (cmd->base.autoneg) {
4468e4881aaSPhilippe Reynes 		u32 modes = 0;
4478e4881aaSPhilippe Reynes 		int an_stat = mdio->mdio_read(mdio->dev, mdio->prtad,
4488e4881aaSPhilippe Reynes 					      MDIO_MMD_AN, MDIO_STAT1);
4498e4881aaSPhilippe Reynes 
4508e4881aaSPhilippe Reynes 		/* If AN is complete and successful, report best common
4518e4881aaSPhilippe Reynes 		 * mode, otherwise report best advertised mode.
4528e4881aaSPhilippe Reynes 		 */
4538e4881aaSPhilippe Reynes 		if (an_stat & MDIO_AN_STAT1_COMPLETE) {
4548e4881aaSPhilippe Reynes 			lp_advertising =
4558e4881aaSPhilippe Reynes 				mdio45_get_an(mdio, MDIO_AN_LPA) | npage_lpa;
4568e4881aaSPhilippe Reynes 			if (an_stat & MDIO_AN_STAT1_LPABLE)
4578e4881aaSPhilippe Reynes 				lp_advertising |= ADVERTISED_Autoneg;
4588e4881aaSPhilippe Reynes 			modes = advertising & lp_advertising;
4598e4881aaSPhilippe Reynes 		}
4608e4881aaSPhilippe Reynes 		if ((modes & ~ADVERTISED_Autoneg) == 0)
4618e4881aaSPhilippe Reynes 			modes = advertising;
4628e4881aaSPhilippe Reynes 
4638e4881aaSPhilippe Reynes 		if (modes & (ADVERTISED_10000baseT_Full |
4648e4881aaSPhilippe Reynes 			     ADVERTISED_10000baseKX4_Full |
4658e4881aaSPhilippe Reynes 			     ADVERTISED_10000baseKR_Full)) {
4668e4881aaSPhilippe Reynes 			speed = SPEED_10000;
4678e4881aaSPhilippe Reynes 			cmd->base.duplex = DUPLEX_FULL;
4688e4881aaSPhilippe Reynes 		} else if (modes & (ADVERTISED_1000baseT_Full |
4698e4881aaSPhilippe Reynes 				    ADVERTISED_1000baseT_Half |
4708e4881aaSPhilippe Reynes 				    ADVERTISED_1000baseKX_Full)) {
4718e4881aaSPhilippe Reynes 			speed = SPEED_1000;
4728e4881aaSPhilippe Reynes 			cmd->base.duplex = !(modes & ADVERTISED_1000baseT_Half);
4738e4881aaSPhilippe Reynes 		} else if (modes & (ADVERTISED_100baseT_Full |
4748e4881aaSPhilippe Reynes 				    ADVERTISED_100baseT_Half)) {
4758e4881aaSPhilippe Reynes 			speed = SPEED_100;
4768e4881aaSPhilippe Reynes 			cmd->base.duplex = !!(modes & ADVERTISED_100baseT_Full);
4778e4881aaSPhilippe Reynes 		} else {
4788e4881aaSPhilippe Reynes 			speed = SPEED_10;
4798e4881aaSPhilippe Reynes 			cmd->base.duplex = !!(modes & ADVERTISED_10baseT_Full);
4808e4881aaSPhilippe Reynes 		}
4818e4881aaSPhilippe Reynes 	} else {
4828e4881aaSPhilippe Reynes 		/* Report forced settings */
4838e4881aaSPhilippe Reynes 		reg = mdio->mdio_read(mdio->dev, mdio->prtad, MDIO_MMD_PMAPMD,
4848e4881aaSPhilippe Reynes 				      MDIO_CTRL1);
4858e4881aaSPhilippe Reynes 		speed = (((reg & MDIO_PMA_CTRL1_SPEED1000) ? 100 : 1)
4868e4881aaSPhilippe Reynes 			 * ((reg & MDIO_PMA_CTRL1_SPEED100) ? 100 : 10));
4878e4881aaSPhilippe Reynes 		cmd->base.duplex = (reg & MDIO_CTRL1_FULLDPLX ||
4888e4881aaSPhilippe Reynes 				    speed == SPEED_10000);
4898e4881aaSPhilippe Reynes 	}
4908e4881aaSPhilippe Reynes 
4918e4881aaSPhilippe Reynes 	cmd->base.speed = speed;
4928e4881aaSPhilippe Reynes 
4938e4881aaSPhilippe Reynes 	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported,
4948e4881aaSPhilippe Reynes 						supported);
4958e4881aaSPhilippe Reynes 	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising,
4968e4881aaSPhilippe Reynes 						advertising);
4978e4881aaSPhilippe Reynes 	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.lp_advertising,
4988e4881aaSPhilippe Reynes 						lp_advertising);
4998e4881aaSPhilippe Reynes 
5008e4881aaSPhilippe Reynes 	/* 10GBASE-T MDI/MDI-X */
5018e4881aaSPhilippe Reynes 	if (cmd->base.port == PORT_TP && (cmd->base.speed == SPEED_10000)) {
5028e4881aaSPhilippe Reynes 		switch (mdio->mdio_read(mdio->dev, mdio->prtad, MDIO_MMD_PMAPMD,
5038e4881aaSPhilippe Reynes 					MDIO_PMA_10GBT_SWAPPOL)) {
5048e4881aaSPhilippe Reynes 		case MDIO_PMA_10GBT_SWAPPOL_ABNX | MDIO_PMA_10GBT_SWAPPOL_CDNX:
5058e4881aaSPhilippe Reynes 			cmd->base.eth_tp_mdix = ETH_TP_MDI;
5068e4881aaSPhilippe Reynes 			break;
5078e4881aaSPhilippe Reynes 		case 0:
5088e4881aaSPhilippe Reynes 			cmd->base.eth_tp_mdix = ETH_TP_MDI_X;
5098e4881aaSPhilippe Reynes 			break;
5108e4881aaSPhilippe Reynes 		default:
5118e4881aaSPhilippe Reynes 			/* It's complicated... */
5128e4881aaSPhilippe Reynes 			cmd->base.eth_tp_mdix = ETH_TP_MDI_INVALID;
5138e4881aaSPhilippe Reynes 			break;
5148e4881aaSPhilippe Reynes 		}
5158e4881aaSPhilippe Reynes 	}
5168e4881aaSPhilippe Reynes }
5178e4881aaSPhilippe Reynes EXPORT_SYMBOL(mdio45_ethtool_ksettings_get_npage);
5188e4881aaSPhilippe Reynes 
5198e4881aaSPhilippe Reynes /**
5201b1c2e95SBen Hutchings  * mdio_mii_ioctl - MII ioctl interface for MDIO (clause 22 or 45) PHYs
5211b1c2e95SBen Hutchings  * @mdio: MDIO interface
5221b1c2e95SBen Hutchings  * @mii_data: MII ioctl data structure
5231b1c2e95SBen Hutchings  * @cmd: MII ioctl command
5241b1c2e95SBen Hutchings  *
5251b1c2e95SBen Hutchings  * Returns 0 on success, negative on error.
5261b1c2e95SBen Hutchings  */
mdio_mii_ioctl(const struct mdio_if_info * mdio,struct mii_ioctl_data * mii_data,int cmd)5271b1c2e95SBen Hutchings int mdio_mii_ioctl(const struct mdio_if_info *mdio,
5281b1c2e95SBen Hutchings 		   struct mii_ioctl_data *mii_data, int cmd)
5291b1c2e95SBen Hutchings {
5301b1c2e95SBen Hutchings 	int prtad, devad;
5311b1c2e95SBen Hutchings 	u16 addr = mii_data->reg_num;
5321b1c2e95SBen Hutchings 
5331b1c2e95SBen Hutchings 	/* Validate/convert cmd to one of SIOC{G,S}MIIREG */
5341b1c2e95SBen Hutchings 	switch (cmd) {
5351b1c2e95SBen Hutchings 	case SIOCGMIIPHY:
5361b1c2e95SBen Hutchings 		if (mdio->prtad == MDIO_PRTAD_NONE)
5371b1c2e95SBen Hutchings 			return -EOPNOTSUPP;
5381b1c2e95SBen Hutchings 		mii_data->phy_id = mdio->prtad;
5391b1c2e95SBen Hutchings 		cmd = SIOCGMIIREG;
5401b1c2e95SBen Hutchings 		break;
5411b1c2e95SBen Hutchings 	case SIOCGMIIREG:
5421b1c2e95SBen Hutchings 	case SIOCSMIIREG:
5431b1c2e95SBen Hutchings 		break;
5441b1c2e95SBen Hutchings 	default:
5451b1c2e95SBen Hutchings 		return -EOPNOTSUPP;
5461b1c2e95SBen Hutchings 	}
5471b1c2e95SBen Hutchings 
5481b1c2e95SBen Hutchings 	/* Validate/convert phy_id */
5491b1c2e95SBen Hutchings 	if ((mdio->mode_support & MDIO_SUPPORTS_C45) &&
5501b1c2e95SBen Hutchings 	    mdio_phy_id_is_c45(mii_data->phy_id)) {
5511b1c2e95SBen Hutchings 		prtad = mdio_phy_id_prtad(mii_data->phy_id);
5521b1c2e95SBen Hutchings 		devad = mdio_phy_id_devad(mii_data->phy_id);
5531b1c2e95SBen Hutchings 	} else if ((mdio->mode_support & MDIO_SUPPORTS_C22) &&
5541b1c2e95SBen Hutchings 		   mii_data->phy_id < 0x20) {
5551b1c2e95SBen Hutchings 		prtad = mii_data->phy_id;
5561b1c2e95SBen Hutchings 		devad = MDIO_DEVAD_NONE;
5571b1c2e95SBen Hutchings 		addr &= 0x1f;
5581b1c2e95SBen Hutchings 	} else if ((mdio->mode_support & MDIO_EMULATE_C22) &&
5591b1c2e95SBen Hutchings 		   mdio->prtad != MDIO_PRTAD_NONE &&
5601b1c2e95SBen Hutchings 		   mii_data->phy_id == mdio->prtad) {
5611b1c2e95SBen Hutchings 		/* Remap commonly-used MII registers. */
5621b1c2e95SBen Hutchings 		prtad = mdio->prtad;
5631b1c2e95SBen Hutchings 		switch (addr) {
5641b1c2e95SBen Hutchings 		case MII_BMCR:
5651b1c2e95SBen Hutchings 		case MII_BMSR:
5661b1c2e95SBen Hutchings 		case MII_PHYSID1:
5671b1c2e95SBen Hutchings 		case MII_PHYSID2:
5681b1c2e95SBen Hutchings 			devad = __ffs(mdio->mmds);
5691b1c2e95SBen Hutchings 			break;
5701b1c2e95SBen Hutchings 		case MII_ADVERTISE:
5711b1c2e95SBen Hutchings 		case MII_LPA:
5721b1c2e95SBen Hutchings 			if (!(mdio->mmds & MDIO_DEVS_AN))
5731b1c2e95SBen Hutchings 				return -EINVAL;
5741b1c2e95SBen Hutchings 			devad = MDIO_MMD_AN;
5751b1c2e95SBen Hutchings 			if (addr == MII_ADVERTISE)
5761b1c2e95SBen Hutchings 				addr = MDIO_AN_ADVERTISE;
5771b1c2e95SBen Hutchings 			else
5781b1c2e95SBen Hutchings 				addr = MDIO_AN_LPA;
5791b1c2e95SBen Hutchings 			break;
5801b1c2e95SBen Hutchings 		default:
5811b1c2e95SBen Hutchings 			return -EINVAL;
5821b1c2e95SBen Hutchings 		}
5831b1c2e95SBen Hutchings 	} else {
5841b1c2e95SBen Hutchings 		return -EINVAL;
5851b1c2e95SBen Hutchings 	}
5861b1c2e95SBen Hutchings 
5871b1c2e95SBen Hutchings 	if (cmd == SIOCGMIIREG) {
5881b1c2e95SBen Hutchings 		int rc = mdio->mdio_read(mdio->dev, prtad, devad, addr);
5891b1c2e95SBen Hutchings 		if (rc < 0)
5901b1c2e95SBen Hutchings 			return rc;
5911b1c2e95SBen Hutchings 		mii_data->val_out = rc;
5921b1c2e95SBen Hutchings 		return 0;
5931b1c2e95SBen Hutchings 	} else {
5941b1c2e95SBen Hutchings 		return mdio->mdio_write(mdio->dev, prtad, devad, addr,
5951b1c2e95SBen Hutchings 					mii_data->val_in);
5961b1c2e95SBen Hutchings 	}
5971b1c2e95SBen Hutchings }
5981b1c2e95SBen Hutchings EXPORT_SYMBOL(mdio_mii_ioctl);
599