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