xref: /openbmc/linux/drivers/net/phy/phylink.c (revision 69280228d2b1cf650b6c9fc43e3158fa911b13d1)
15f857575SAndrew Lunn // SPDX-License-Identifier: GPL-2.0
29525ae83SRussell King /*
39525ae83SRussell King  * phylink models the MAC to optional PHY connection, supporting
49525ae83SRussell King  * technologies such as SFP cages where the PHY is hot-pluggable.
59525ae83SRussell King  *
69525ae83SRussell King  * Copyright (C) 2015 Russell King
79525ae83SRussell King  */
89525ae83SRussell King #include <linux/ethtool.h>
99525ae83SRussell King #include <linux/export.h>
109525ae83SRussell King #include <linux/gpio/consumer.h>
119525ae83SRussell King #include <linux/netdevice.h>
129525ae83SRussell King #include <linux/of.h>
139525ae83SRussell King #include <linux/of_mdio.h>
149525ae83SRussell King #include <linux/phy.h>
159525ae83SRussell King #include <linux/phy_fixed.h>
169525ae83SRussell King #include <linux/phylink.h>
179525ae83SRussell King #include <linux/rtnetlink.h>
189525ae83SRussell King #include <linux/spinlock.h>
199cd00a8aSRussell King #include <linux/timer.h>
209525ae83SRussell King #include <linux/workqueue.h>
219525ae83SRussell King 
22ce0aa27fSRussell King #include "sfp.h"
239525ae83SRussell King #include "swphy.h"
249525ae83SRussell King 
259525ae83SRussell King #define SUPPORTED_INTERFACES \
269525ae83SRussell King 	(SUPPORTED_TP | SUPPORTED_MII | SUPPORTED_FIBRE | \
279525ae83SRussell King 	 SUPPORTED_BNC | SUPPORTED_AUI | SUPPORTED_Backplane)
289525ae83SRussell King #define ADVERTISED_INTERFACES \
299525ae83SRussell King 	(ADVERTISED_TP | ADVERTISED_MII | ADVERTISED_FIBRE | \
309525ae83SRussell King 	 ADVERTISED_BNC | ADVERTISED_AUI | ADVERTISED_Backplane)
319525ae83SRussell King 
329525ae83SRussell King enum {
339525ae83SRussell King 	PHYLINK_DISABLE_STOPPED,
34ce0aa27fSRussell King 	PHYLINK_DISABLE_LINK,
359525ae83SRussell King };
369525ae83SRussell King 
378796c892SRussell King /**
388796c892SRussell King  * struct phylink - internal data type for phylink
398796c892SRussell King  */
409525ae83SRussell King struct phylink {
418796c892SRussell King 	/* private: */
429525ae83SRussell King 	struct net_device *netdev;
43e7765d63SRussell King 	const struct phylink_mac_ops *mac_ops;
444c0d6d3aSRussell King 	const struct phylink_pcs_ops *pcs_ops;
4544cc27e4SIoana Ciornei 	struct phylink_config *config;
467137e18fSRussell King 	struct phylink_pcs *pcs;
4743de6195SIoana Ciornei 	struct device *dev;
4843de6195SIoana Ciornei 	unsigned int old_link_state:1;
499525ae83SRussell King 
509525ae83SRussell King 	unsigned long phylink_disable_state; /* bitmask of disables */
519525ae83SRussell King 	struct phy_device *phydev;
529525ae83SRussell King 	phy_interface_t link_interface;	/* PHY_INTERFACE_xxx */
5324cf0e69SRussell King 	u8 cfg_link_an_mode;		/* MLO_AN_xxx */
5424cf0e69SRussell King 	u8 cur_link_an_mode;
559525ae83SRussell King 	u8 link_port;			/* The current non-phy ethtool port */
569525ae83SRussell King 	__ETHTOOL_DECLARE_LINK_MODE_MASK(supported);
579525ae83SRussell King 
589525ae83SRussell King 	/* The link configuration settings */
599525ae83SRussell King 	struct phylink_link_state link_config;
60c6787263SRussell King 
61c6787263SRussell King 	/* The current settings */
62c6787263SRussell King 	phy_interface_t cur_interface;
63c6787263SRussell King 
649525ae83SRussell King 	struct gpio_desc *link_gpio;
657b3b0e89SRussell King 	unsigned int link_irq;
669cd00a8aSRussell King 	struct timer_list link_poll;
671ac63e39SFlorian Fainelli 	void (*get_fixed_state)(struct net_device *dev,
681ac63e39SFlorian Fainelli 				struct phylink_link_state *s);
699525ae83SRussell King 
709525ae83SRussell King 	struct mutex state_mutex;
719525ae83SRussell King 	struct phylink_link_state phy_state;
729525ae83SRussell King 	struct work_struct resolve;
739525ae83SRussell King 
749525ae83SRussell King 	bool mac_link_dropped;
75ce0aa27fSRussell King 
76ce0aa27fSRussell King 	struct sfp_bus *sfp_bus;
7752c95600SRussell King 	bool sfp_may_have_phy;
7852c95600SRussell King 	__ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support);
7952c95600SRussell King 	u8 sfp_port;
809525ae83SRussell King };
819525ae83SRussell King 
8217091180SIoana Ciornei #define phylink_printk(level, pl, fmt, ...) \
8317091180SIoana Ciornei 	do { \
8417091180SIoana Ciornei 		if ((pl)->config->type == PHYLINK_NETDEV) \
8517091180SIoana Ciornei 			netdev_printk(level, (pl)->netdev, fmt, ##__VA_ARGS__); \
8617091180SIoana Ciornei 		else if ((pl)->config->type == PHYLINK_DEV) \
8717091180SIoana Ciornei 			dev_printk(level, (pl)->dev, fmt, ##__VA_ARGS__); \
8817091180SIoana Ciornei 	} while (0)
8917091180SIoana Ciornei 
9017091180SIoana Ciornei #define phylink_err(pl, fmt, ...) \
9117091180SIoana Ciornei 	phylink_printk(KERN_ERR, pl, fmt, ##__VA_ARGS__)
9217091180SIoana Ciornei #define phylink_warn(pl, fmt, ...) \
9317091180SIoana Ciornei 	phylink_printk(KERN_WARNING, pl, fmt, ##__VA_ARGS__)
9417091180SIoana Ciornei #define phylink_info(pl, fmt, ...) \
9517091180SIoana Ciornei 	phylink_printk(KERN_INFO, pl, fmt, ##__VA_ARGS__)
969d68db50SFlorian Fainelli #if defined(CONFIG_DYNAMIC_DEBUG)
979d68db50SFlorian Fainelli #define phylink_dbg(pl, fmt, ...) \
989d68db50SFlorian Fainelli do {									\
999d68db50SFlorian Fainelli 	if ((pl)->config->type == PHYLINK_NETDEV)			\
1009d68db50SFlorian Fainelli 		netdev_dbg((pl)->netdev, fmt, ##__VA_ARGS__);		\
1019d68db50SFlorian Fainelli 	else if ((pl)->config->type == PHYLINK_DEV)			\
1029d68db50SFlorian Fainelli 		dev_dbg((pl)->dev, fmt, ##__VA_ARGS__);			\
1039d68db50SFlorian Fainelli } while (0)
1049d68db50SFlorian Fainelli #elif defined(DEBUG)
10517091180SIoana Ciornei #define phylink_dbg(pl, fmt, ...)					\
10617091180SIoana Ciornei 	phylink_printk(KERN_DEBUG, pl, fmt, ##__VA_ARGS__)
1079d68db50SFlorian Fainelli #else
1089d68db50SFlorian Fainelli #define phylink_dbg(pl, fmt, ...)					\
1099d68db50SFlorian Fainelli ({									\
1109d68db50SFlorian Fainelli 	if (0)								\
1119d68db50SFlorian Fainelli 		phylink_printk(KERN_DEBUG, pl, fmt, ##__VA_ARGS__);	\
1129d68db50SFlorian Fainelli })
1139d68db50SFlorian Fainelli #endif
11417091180SIoana Ciornei 
1158796c892SRussell King /**
1168796c892SRussell King  * phylink_set_port_modes() - set the port type modes in the ethtool mask
1178796c892SRussell King  * @mask: ethtool link mode mask
1188796c892SRussell King  *
1198796c892SRussell King  * Sets all the port type modes in the ethtool mask.  MAC drivers should
1208796c892SRussell King  * use this in their 'validate' callback.
1218796c892SRussell King  */
1229525ae83SRussell King void phylink_set_port_modes(unsigned long *mask)
1239525ae83SRussell King {
1249525ae83SRussell King 	phylink_set(mask, TP);
1259525ae83SRussell King 	phylink_set(mask, AUI);
1269525ae83SRussell King 	phylink_set(mask, MII);
1279525ae83SRussell King 	phylink_set(mask, FIBRE);
1289525ae83SRussell King 	phylink_set(mask, BNC);
1299525ae83SRussell King 	phylink_set(mask, Backplane);
1309525ae83SRussell King }
1319525ae83SRussell King EXPORT_SYMBOL_GPL(phylink_set_port_modes);
1329525ae83SRussell King 
1339525ae83SRussell King static int phylink_is_empty_linkmode(const unsigned long *linkmode)
1349525ae83SRussell King {
1359525ae83SRussell King 	__ETHTOOL_DECLARE_LINK_MODE_MASK(tmp) = { 0, };
1369525ae83SRussell King 
1379525ae83SRussell King 	phylink_set_port_modes(tmp);
1389525ae83SRussell King 	phylink_set(tmp, Autoneg);
1399525ae83SRussell King 	phylink_set(tmp, Pause);
1409525ae83SRussell King 	phylink_set(tmp, Asym_Pause);
1419525ae83SRussell King 
142554032cdSRussell King 	return linkmode_subset(linkmode, tmp);
1439525ae83SRussell King }
1449525ae83SRussell King 
1459525ae83SRussell King static const char *phylink_an_mode_str(unsigned int mode)
1469525ae83SRussell King {
1479525ae83SRussell King 	static const char *modestr[] = {
1489525ae83SRussell King 		[MLO_AN_PHY] = "phy",
1499525ae83SRussell King 		[MLO_AN_FIXED] = "fixed",
15086a362c4SRussell King 		[MLO_AN_INBAND] = "inband",
1519525ae83SRussell King 	};
1529525ae83SRussell King 
1539525ae83SRussell King 	return mode < ARRAY_SIZE(modestr) ? modestr[mode] : "unknown";
1549525ae83SRussell King }
1559525ae83SRussell King 
1569525ae83SRussell King static int phylink_validate(struct phylink *pl, unsigned long *supported,
1579525ae83SRussell King 			    struct phylink_link_state *state)
1589525ae83SRussell King {
159e7765d63SRussell King 	pl->mac_ops->validate(pl->config, supported, state);
1609525ae83SRussell King 
1619525ae83SRussell King 	return phylink_is_empty_linkmode(supported) ? -EINVAL : 0;
1629525ae83SRussell King }
1639525ae83SRussell King 
1648fa7b9b6SRussell King static int phylink_parse_fixedlink(struct phylink *pl,
1658fa7b9b6SRussell King 				   struct fwnode_handle *fwnode)
1669525ae83SRussell King {
1678fa7b9b6SRussell King 	struct fwnode_handle *fixed_node;
1689525ae83SRussell King 	const struct phy_setting *s;
1699525ae83SRussell King 	struct gpio_desc *desc;
1709525ae83SRussell King 	u32 speed;
1718fa7b9b6SRussell King 	int ret;
1729525ae83SRussell King 
1738fa7b9b6SRussell King 	fixed_node = fwnode_get_named_child_node(fwnode, "fixed-link");
1749525ae83SRussell King 	if (fixed_node) {
1758fa7b9b6SRussell King 		ret = fwnode_property_read_u32(fixed_node, "speed", &speed);
1769525ae83SRussell King 
1779525ae83SRussell King 		pl->link_config.speed = speed;
1789525ae83SRussell King 		pl->link_config.duplex = DUPLEX_HALF;
1799525ae83SRussell King 
1808fa7b9b6SRussell King 		if (fwnode_property_read_bool(fixed_node, "full-duplex"))
1819525ae83SRussell King 			pl->link_config.duplex = DUPLEX_FULL;
1829525ae83SRussell King 
1839525ae83SRussell King 		/* We treat the "pause" and "asym-pause" terminology as
1849525ae83SRussell King 		 * defining the link partner's ability. */
1858fa7b9b6SRussell King 		if (fwnode_property_read_bool(fixed_node, "pause"))
1864e5aeb41SRussell King 			__set_bit(ETHTOOL_LINK_MODE_Pause_BIT,
1874e5aeb41SRussell King 				  pl->link_config.lp_advertising);
1888fa7b9b6SRussell King 		if (fwnode_property_read_bool(fixed_node, "asym-pause"))
1894e5aeb41SRussell King 			__set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
1904e5aeb41SRussell King 				  pl->link_config.lp_advertising);
1919525ae83SRussell King 
1929525ae83SRussell King 		if (ret == 0) {
193b605c9abSDmitry Torokhov 			desc = fwnode_gpiod_get_index(fixed_node, "link", 0,
194b605c9abSDmitry Torokhov 						      GPIOD_IN, "?");
1959525ae83SRussell King 
1969525ae83SRussell King 			if (!IS_ERR(desc))
1979525ae83SRussell King 				pl->link_gpio = desc;
1989525ae83SRussell King 			else if (desc == ERR_PTR(-EPROBE_DEFER))
1999525ae83SRussell King 				ret = -EPROBE_DEFER;
2009525ae83SRussell King 		}
2018fa7b9b6SRussell King 		fwnode_handle_put(fixed_node);
2029525ae83SRussell King 
2039525ae83SRussell King 		if (ret)
2049525ae83SRussell King 			return ret;
2059525ae83SRussell King 	} else {
2068fa7b9b6SRussell King 		u32 prop[5];
2078fa7b9b6SRussell King 
2088fa7b9b6SRussell King 		ret = fwnode_property_read_u32_array(fwnode, "fixed-link",
2098fa7b9b6SRussell King 						     NULL, 0);
2108fa7b9b6SRussell King 		if (ret != ARRAY_SIZE(prop)) {
21117091180SIoana Ciornei 			phylink_err(pl, "broken fixed-link?\n");
2129525ae83SRussell King 			return -EINVAL;
2139525ae83SRussell King 		}
2148fa7b9b6SRussell King 
2158fa7b9b6SRussell King 		ret = fwnode_property_read_u32_array(fwnode, "fixed-link",
2168fa7b9b6SRussell King 						     prop, ARRAY_SIZE(prop));
2178fa7b9b6SRussell King 		if (!ret) {
2188fa7b9b6SRussell King 			pl->link_config.duplex = prop[1] ?
2199525ae83SRussell King 						DUPLEX_FULL : DUPLEX_HALF;
2208fa7b9b6SRussell King 			pl->link_config.speed = prop[2];
2218fa7b9b6SRussell King 			if (prop[3])
2224e5aeb41SRussell King 				__set_bit(ETHTOOL_LINK_MODE_Pause_BIT,
2234e5aeb41SRussell King 					  pl->link_config.lp_advertising);
2248fa7b9b6SRussell King 			if (prop[4])
2254e5aeb41SRussell King 				__set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
2264e5aeb41SRussell King 					  pl->link_config.lp_advertising);
2279525ae83SRussell King 		}
2289525ae83SRussell King 	}
2299525ae83SRussell King 
2309525ae83SRussell King 	if (pl->link_config.speed > SPEED_1000 &&
2319525ae83SRussell King 	    pl->link_config.duplex != DUPLEX_FULL)
23217091180SIoana Ciornei 		phylink_warn(pl, "fixed link specifies half duplex for %dMbps link?\n",
2339525ae83SRussell King 			     pl->link_config.speed);
2349525ae83SRussell King 
2359525ae83SRussell King 	bitmap_fill(pl->supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
2369525ae83SRussell King 	linkmode_copy(pl->link_config.advertising, pl->supported);
2379525ae83SRussell King 	phylink_validate(pl, pl->supported, &pl->link_config);
2389525ae83SRussell King 
2399525ae83SRussell King 	s = phy_lookup_setting(pl->link_config.speed, pl->link_config.duplex,
2403c1bcc86SAndrew Lunn 			       pl->supported, true);
2419525ae83SRussell King 	linkmode_zero(pl->supported);
2429525ae83SRussell King 	phylink_set(pl->supported, MII);
2438aace4f3SRené van Dorst 	phylink_set(pl->supported, Pause);
2448aace4f3SRené van Dorst 	phylink_set(pl->supported, Asym_Pause);
2451ceb7ee7SRussell King 	phylink_set(pl->supported, Autoneg);
2469525ae83SRussell King 	if (s) {
2479525ae83SRussell King 		__set_bit(s->bit, pl->supported);
2481ceb7ee7SRussell King 		__set_bit(s->bit, pl->link_config.lp_advertising);
2499525ae83SRussell King 	} else {
25017091180SIoana Ciornei 		phylink_warn(pl, "fixed link %s duplex %dMbps not recognised\n",
2519525ae83SRussell King 			     pl->link_config.duplex == DUPLEX_FULL ? "full" : "half",
2529525ae83SRussell King 			     pl->link_config.speed);
2539525ae83SRussell King 	}
2549525ae83SRussell King 
2559525ae83SRussell King 	linkmode_and(pl->link_config.advertising, pl->link_config.advertising,
2569525ae83SRussell King 		     pl->supported);
2579525ae83SRussell King 
2589525ae83SRussell King 	pl->link_config.link = 1;
2599525ae83SRussell King 	pl->link_config.an_complete = 1;
2609525ae83SRussell King 
2619525ae83SRussell King 	return 0;
2629525ae83SRussell King }
2639525ae83SRussell King 
2648fa7b9b6SRussell King static int phylink_parse_mode(struct phylink *pl, struct fwnode_handle *fwnode)
2659525ae83SRussell King {
2668fa7b9b6SRussell King 	struct fwnode_handle *dn;
2679525ae83SRussell King 	const char *managed;
2689525ae83SRussell King 
2698fa7b9b6SRussell King 	dn = fwnode_get_named_child_node(fwnode, "fixed-link");
2708fa7b9b6SRussell King 	if (dn || fwnode_property_present(fwnode, "fixed-link"))
27124cf0e69SRussell King 		pl->cfg_link_an_mode = MLO_AN_FIXED;
2728fa7b9b6SRussell King 	fwnode_handle_put(dn);
2739525ae83SRussell King 
2748fa7b9b6SRussell King 	if (fwnode_property_read_string(fwnode, "managed", &managed) == 0 &&
2759525ae83SRussell King 	    strcmp(managed, "in-band-status") == 0) {
27624cf0e69SRussell King 		if (pl->cfg_link_an_mode == MLO_AN_FIXED) {
27717091180SIoana Ciornei 			phylink_err(pl,
2789525ae83SRussell King 				    "can't use both fixed-link and in-band-status\n");
2799525ae83SRussell King 			return -EINVAL;
2809525ae83SRussell King 		}
2819525ae83SRussell King 
2829525ae83SRussell King 		linkmode_zero(pl->supported);
2839525ae83SRussell King 		phylink_set(pl->supported, MII);
2849525ae83SRussell King 		phylink_set(pl->supported, Autoneg);
2859525ae83SRussell King 		phylink_set(pl->supported, Asym_Pause);
2869525ae83SRussell King 		phylink_set(pl->supported, Pause);
2879525ae83SRussell King 		pl->link_config.an_enabled = true;
28824cf0e69SRussell King 		pl->cfg_link_an_mode = MLO_AN_INBAND;
2899525ae83SRussell King 
2909525ae83SRussell King 		switch (pl->link_config.interface) {
2919525ae83SRussell King 		case PHY_INTERFACE_MODE_SGMII:
2923a68ba6fSVladimir Oltean 		case PHY_INTERFACE_MODE_QSGMII:
2939525ae83SRussell King 			phylink_set(pl->supported, 10baseT_Half);
2949525ae83SRussell King 			phylink_set(pl->supported, 10baseT_Full);
2959525ae83SRussell King 			phylink_set(pl->supported, 100baseT_Half);
2969525ae83SRussell King 			phylink_set(pl->supported, 100baseT_Full);
2979525ae83SRussell King 			phylink_set(pl->supported, 1000baseT_Half);
2989525ae83SRussell King 			phylink_set(pl->supported, 1000baseT_Full);
2999525ae83SRussell King 			break;
3009525ae83SRussell King 
3019525ae83SRussell King 		case PHY_INTERFACE_MODE_1000BASEX:
3029525ae83SRussell King 			phylink_set(pl->supported, 1000baseX_Full);
3039525ae83SRussell King 			break;
3049525ae83SRussell King 
3059525ae83SRussell King 		case PHY_INTERFACE_MODE_2500BASEX:
3069525ae83SRussell King 			phylink_set(pl->supported, 2500baseX_Full);
3079525ae83SRussell King 			break;
3089525ae83SRussell King 
30904e22463SAlex Marginean 		case PHY_INTERFACE_MODE_USXGMII:
310da7c1862SRussell King 		case PHY_INTERFACE_MODE_10GKR:
311e0f909bcSRussell King 		case PHY_INTERFACE_MODE_10GBASER:
312da7c1862SRussell King 			phylink_set(pl->supported, 10baseT_Half);
313da7c1862SRussell King 			phylink_set(pl->supported, 10baseT_Full);
314da7c1862SRussell King 			phylink_set(pl->supported, 100baseT_Half);
315da7c1862SRussell King 			phylink_set(pl->supported, 100baseT_Full);
316da7c1862SRussell King 			phylink_set(pl->supported, 1000baseT_Half);
317da7c1862SRussell King 			phylink_set(pl->supported, 1000baseT_Full);
318da7c1862SRussell King 			phylink_set(pl->supported, 1000baseX_Full);
319c580165fSJose Abreu 			phylink_set(pl->supported, 1000baseKX_Full);
3206cbdcf25SVladimir Oltean 			phylink_set(pl->supported, 2500baseT_Full);
3216cbdcf25SVladimir Oltean 			phylink_set(pl->supported, 2500baseX_Full);
3226cbdcf25SVladimir Oltean 			phylink_set(pl->supported, 5000baseT_Full);
3236cbdcf25SVladimir Oltean 			phylink_set(pl->supported, 10000baseT_Full);
324da7c1862SRussell King 			phylink_set(pl->supported, 10000baseKR_Full);
325c580165fSJose Abreu 			phylink_set(pl->supported, 10000baseKX4_Full);
326da7c1862SRussell King 			phylink_set(pl->supported, 10000baseCR_Full);
327da7c1862SRussell King 			phylink_set(pl->supported, 10000baseSR_Full);
328da7c1862SRussell King 			phylink_set(pl->supported, 10000baseLR_Full);
329da7c1862SRussell King 			phylink_set(pl->supported, 10000baseLRM_Full);
330da7c1862SRussell King 			phylink_set(pl->supported, 10000baseER_Full);
331da7c1862SRussell King 			break;
332da7c1862SRussell King 
3331671c42dSJose Abreu 		case PHY_INTERFACE_MODE_XLGMII:
3341671c42dSJose Abreu 			phylink_set(pl->supported, 25000baseCR_Full);
3351671c42dSJose Abreu 			phylink_set(pl->supported, 25000baseKR_Full);
3361671c42dSJose Abreu 			phylink_set(pl->supported, 25000baseSR_Full);
3371671c42dSJose Abreu 			phylink_set(pl->supported, 40000baseKR4_Full);
3381671c42dSJose Abreu 			phylink_set(pl->supported, 40000baseCR4_Full);
3391671c42dSJose Abreu 			phylink_set(pl->supported, 40000baseSR4_Full);
3401671c42dSJose Abreu 			phylink_set(pl->supported, 40000baseLR4_Full);
3411671c42dSJose Abreu 			phylink_set(pl->supported, 50000baseCR2_Full);
3421671c42dSJose Abreu 			phylink_set(pl->supported, 50000baseKR2_Full);
3431671c42dSJose Abreu 			phylink_set(pl->supported, 50000baseSR2_Full);
3441671c42dSJose Abreu 			phylink_set(pl->supported, 50000baseKR_Full);
3451671c42dSJose Abreu 			phylink_set(pl->supported, 50000baseSR_Full);
3461671c42dSJose Abreu 			phylink_set(pl->supported, 50000baseCR_Full);
3471671c42dSJose Abreu 			phylink_set(pl->supported, 50000baseLR_ER_FR_Full);
3481671c42dSJose Abreu 			phylink_set(pl->supported, 50000baseDR_Full);
3491671c42dSJose Abreu 			phylink_set(pl->supported, 100000baseKR4_Full);
3501671c42dSJose Abreu 			phylink_set(pl->supported, 100000baseSR4_Full);
3511671c42dSJose Abreu 			phylink_set(pl->supported, 100000baseCR4_Full);
3521671c42dSJose Abreu 			phylink_set(pl->supported, 100000baseLR4_ER4_Full);
3531671c42dSJose Abreu 			phylink_set(pl->supported, 100000baseKR2_Full);
3541671c42dSJose Abreu 			phylink_set(pl->supported, 100000baseSR2_Full);
3551671c42dSJose Abreu 			phylink_set(pl->supported, 100000baseCR2_Full);
3561671c42dSJose Abreu 			phylink_set(pl->supported, 100000baseLR2_ER2_FR2_Full);
3571671c42dSJose Abreu 			phylink_set(pl->supported, 100000baseDR2_Full);
3581671c42dSJose Abreu 			break;
3591671c42dSJose Abreu 
3609525ae83SRussell King 		default:
36117091180SIoana Ciornei 			phylink_err(pl,
3629525ae83SRussell King 				    "incorrect link mode %s for in-band status\n",
3639525ae83SRussell King 				    phy_modes(pl->link_config.interface));
3649525ae83SRussell King 			return -EINVAL;
3659525ae83SRussell King 		}
3669525ae83SRussell King 
3679525ae83SRussell King 		linkmode_copy(pl->link_config.advertising, pl->supported);
3689525ae83SRussell King 
3699525ae83SRussell King 		if (phylink_validate(pl, pl->supported, &pl->link_config)) {
37017091180SIoana Ciornei 			phylink_err(pl,
3719525ae83SRussell King 				    "failed to validate link configuration for in-band status\n");
3729525ae83SRussell King 			return -EINVAL;
3739525ae83SRussell King 		}
37494148196SJose Abreu 
37594148196SJose Abreu 		/* Check if MAC/PCS also supports Autoneg. */
37694148196SJose Abreu 		pl->link_config.an_enabled = phylink_test(pl->supported, Autoneg);
3779525ae83SRussell King 	}
3789525ae83SRussell King 
3799525ae83SRussell King 	return 0;
3809525ae83SRussell King }
3819525ae83SRussell King 
3822d5fbef0SRussell King static void phylink_apply_manual_flow(struct phylink *pl,
3832d5fbef0SRussell King 				      struct phylink_link_state *state)
3842d5fbef0SRussell King {
3852d5fbef0SRussell King 	/* If autoneg is disabled, pause AN is also disabled */
3862d5fbef0SRussell King 	if (!state->an_enabled)
3872d5fbef0SRussell King 		state->pause &= ~MLO_PAUSE_AN;
3882d5fbef0SRussell King 
3892d5fbef0SRussell King 	/* Manual configuration of pause modes */
3902d5fbef0SRussell King 	if (!(pl->link_config.pause & MLO_PAUSE_AN))
3912d5fbef0SRussell King 		state->pause = pl->link_config.pause;
3922d5fbef0SRussell King }
3932d5fbef0SRussell King 
3944e5aeb41SRussell King static void phylink_resolve_flow(struct phylink_link_state *state)
3954e5aeb41SRussell King {
3964e5aeb41SRussell King 	bool tx_pause, rx_pause;
3974e5aeb41SRussell King 
3984e5aeb41SRussell King 	state->pause = MLO_PAUSE_NONE;
3994e5aeb41SRussell King 	if (state->duplex == DUPLEX_FULL) {
4004e5aeb41SRussell King 		linkmode_resolve_pause(state->advertising,
4014e5aeb41SRussell King 				       state->lp_advertising,
4024e5aeb41SRussell King 				       &tx_pause, &rx_pause);
4034e5aeb41SRussell King 		if (tx_pause)
4044e5aeb41SRussell King 			state->pause |= MLO_PAUSE_TX;
4054e5aeb41SRussell King 		if (rx_pause)
4064e5aeb41SRussell King 			state->pause |= MLO_PAUSE_RX;
4074e5aeb41SRussell King 	}
4084e5aeb41SRussell King }
4094e5aeb41SRussell King 
4109525ae83SRussell King static void phylink_mac_config(struct phylink *pl,
4119525ae83SRussell King 			       const struct phylink_link_state *state)
4129525ae83SRussell King {
41317091180SIoana Ciornei 	phylink_dbg(pl,
4149525ae83SRussell King 		    "%s: mode=%s/%s/%s/%s adv=%*pb pause=%02x link=%u an=%u\n",
41524cf0e69SRussell King 		    __func__, phylink_an_mode_str(pl->cur_link_an_mode),
4169525ae83SRussell King 		    phy_modes(state->interface),
4179525ae83SRussell King 		    phy_speed_to_str(state->speed),
4189525ae83SRussell King 		    phy_duplex_to_str(state->duplex),
4199525ae83SRussell King 		    __ETHTOOL_LINK_MODE_MASK_NBITS, state->advertising,
4209525ae83SRussell King 		    state->pause, state->link, state->an_enabled);
4219525ae83SRussell King 
422e7765d63SRussell King 	pl->mac_ops->mac_config(pl->config, pl->cur_link_an_mode, state);
4239525ae83SRussell King }
4249525ae83SRussell King 
4254c0d6d3aSRussell King static void phylink_mac_pcs_an_restart(struct phylink *pl)
4269525ae83SRussell King {
4279525ae83SRussell King 	if (pl->link_config.an_enabled &&
428575691b3SRussell King 	    phy_interface_mode_is_8023z(pl->link_config.interface) &&
429575691b3SRussell King 	    phylink_autoneg_inband(pl->cur_link_an_mode)) {
4304c0d6d3aSRussell King 		if (pl->pcs_ops)
4317137e18fSRussell King 			pl->pcs_ops->pcs_an_restart(pl->pcs);
4324c0d6d3aSRussell King 		else
433e7765d63SRussell King 			pl->mac_ops->mac_an_restart(pl->config);
4349525ae83SRussell King 	}
4354c0d6d3aSRussell King }
4364c0d6d3aSRussell King 
437b7ad14c2SRussell King static void phylink_major_config(struct phylink *pl, bool restart,
4384c0d6d3aSRussell King 				  const struct phylink_link_state *state)
4394c0d6d3aSRussell King {
440b7ad14c2SRussell King 	int err;
4414c0d6d3aSRussell King 
442b7ad14c2SRussell King 	phylink_dbg(pl, "major config %s\n", phy_modes(state->interface));
443b7ad14c2SRussell King 
444b7ad14c2SRussell King 	if (pl->mac_ops->mac_prepare) {
445b7ad14c2SRussell King 		err = pl->mac_ops->mac_prepare(pl->config, pl->cur_link_an_mode,
446b7ad14c2SRussell King 					       state->interface);
447b7ad14c2SRussell King 		if (err < 0) {
448b7ad14c2SRussell King 			phylink_err(pl, "mac_prepare failed: %pe\n",
449b7ad14c2SRussell King 				    ERR_PTR(err));
450b7ad14c2SRussell King 			return;
451b7ad14c2SRussell King 		}
452b7ad14c2SRussell King 	}
4534c0d6d3aSRussell King 
4544c0d6d3aSRussell King 	phylink_mac_config(pl, state);
4554c0d6d3aSRussell King 
456b7ad14c2SRussell King 	if (pl->pcs_ops) {
4577137e18fSRussell King 		err = pl->pcs_ops->pcs_config(pl->pcs, pl->cur_link_an_mode,
458b7ad14c2SRussell King 					      state->interface,
459b7ad14c2SRussell King 					      state->advertising,
460b7ad14c2SRussell King 					      !!(pl->link_config.pause &
461b7ad14c2SRussell King 						 MLO_PAUSE_AN));
462b7ad14c2SRussell King 		if (err < 0)
463b7ad14c2SRussell King 			phylink_err(pl, "pcs_config failed: %pe\n",
464b7ad14c2SRussell King 				    ERR_PTR(err));
465b7ad14c2SRussell King 		if (err > 0)
466b7ad14c2SRussell King 			restart = true;
467b7ad14c2SRussell King 	}
4684c0d6d3aSRussell King 	if (restart)
4694c0d6d3aSRussell King 		phylink_mac_pcs_an_restart(pl);
470b7ad14c2SRussell King 
471b7ad14c2SRussell King 	if (pl->mac_ops->mac_finish) {
472b7ad14c2SRussell King 		err = pl->mac_ops->mac_finish(pl->config, pl->cur_link_an_mode,
473b7ad14c2SRussell King 					      state->interface);
474b7ad14c2SRussell King 		if (err < 0)
475b7ad14c2SRussell King 			phylink_err(pl, "mac_prepare failed: %pe\n",
476b7ad14c2SRussell King 				    ERR_PTR(err));
477b7ad14c2SRussell King 	}
4784c0d6d3aSRussell King }
4799525ae83SRussell King 
4801571e700SRussell King /*
4811571e700SRussell King  * Reconfigure for a change of inband advertisement.
4821571e700SRussell King  * If we have a separate PCS, we only need to call its pcs_config() method,
4831571e700SRussell King  * and then restart AN if it indicates something changed. Otherwise, we do
4841571e700SRussell King  * the full MAC reconfiguration.
4851571e700SRussell King  */
4861571e700SRussell King static int phylink_change_inband_advert(struct phylink *pl)
4871571e700SRussell King {
4881571e700SRussell King 	int ret;
4891571e700SRussell King 
4901571e700SRussell King 	if (test_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state))
4911571e700SRussell King 		return 0;
4921571e700SRussell King 
4931571e700SRussell King 	if (!pl->pcs_ops) {
4941571e700SRussell King 		/* Legacy method */
4951571e700SRussell King 		phylink_mac_config(pl, &pl->link_config);
4961571e700SRussell King 		phylink_mac_pcs_an_restart(pl);
4971571e700SRussell King 		return 0;
4981571e700SRussell King 	}
4991571e700SRussell King 
5001571e700SRussell King 	phylink_dbg(pl, "%s: mode=%s/%s adv=%*pb pause=%02x\n", __func__,
5011571e700SRussell King 		    phylink_an_mode_str(pl->cur_link_an_mode),
5021571e700SRussell King 		    phy_modes(pl->link_config.interface),
5031571e700SRussell King 		    __ETHTOOL_LINK_MODE_MASK_NBITS, pl->link_config.advertising,
5041571e700SRussell King 		    pl->link_config.pause);
5051571e700SRussell King 
5061571e700SRussell King 	/* Modern PCS-based method; update the advert at the PCS, and
5071571e700SRussell King 	 * restart negotiation if the pcs_config() helper indicates that
5081571e700SRussell King 	 * the programmed advertisement has changed.
5091571e700SRussell King 	 */
5107137e18fSRussell King 	ret = pl->pcs_ops->pcs_config(pl->pcs, pl->cur_link_an_mode,
5111571e700SRussell King 				      pl->link_config.interface,
5121571e700SRussell King 				      pl->link_config.advertising,
5131571e700SRussell King 				      !!(pl->link_config.pause & MLO_PAUSE_AN));
5141571e700SRussell King 	if (ret < 0)
5151571e700SRussell King 		return ret;
5161571e700SRussell King 
5171571e700SRussell King 	if (ret > 0)
5181571e700SRussell King 		phylink_mac_pcs_an_restart(pl);
5191571e700SRussell King 
5201571e700SRussell King 	return 0;
5211571e700SRussell King }
5221571e700SRussell King 
523d46b7e4fSRussell King static void phylink_mac_pcs_get_state(struct phylink *pl,
524d46b7e4fSRussell King 				      struct phylink_link_state *state)
5259525ae83SRussell King {
5269525ae83SRussell King 	linkmode_copy(state->advertising, pl->link_config.advertising);
5279525ae83SRussell King 	linkmode_zero(state->lp_advertising);
5289525ae83SRussell King 	state->interface = pl->link_config.interface;
5299525ae83SRussell King 	state->an_enabled = pl->link_config.an_enabled;
530d25ed413SHeiner Kallweit 	state->speed = SPEED_UNKNOWN;
531d25ed413SHeiner Kallweit 	state->duplex = DUPLEX_UNKNOWN;
532d25ed413SHeiner Kallweit 	state->pause = MLO_PAUSE_NONE;
533d25ed413SHeiner Kallweit 	state->an_complete = 0;
5349525ae83SRussell King 	state->link = 1;
5359525ae83SRussell King 
5364c0d6d3aSRussell King 	if (pl->pcs_ops)
5377137e18fSRussell King 		pl->pcs_ops->pcs_get_state(pl->pcs, state);
538e859a60aSRussell King 	else if (pl->mac_ops->mac_pcs_get_state)
539e7765d63SRussell King 		pl->mac_ops->mac_pcs_get_state(pl->config, state);
540e859a60aSRussell King 	else
541e859a60aSRussell King 		state->link = 0;
5429525ae83SRussell King }
5439525ae83SRussell King 
5449525ae83SRussell King /* The fixed state is... fixed except for the link state,
5451ac63e39SFlorian Fainelli  * which may be determined by a GPIO or a callback.
5469525ae83SRussell King  */
5474e5aeb41SRussell King static void phylink_get_fixed_state(struct phylink *pl,
5484e5aeb41SRussell King 				    struct phylink_link_state *state)
5499525ae83SRussell King {
5509525ae83SRussell King 	*state = pl->link_config;
5515c05c1dbSRussell King 	if (pl->config->get_fixed_state)
5525c05c1dbSRussell King 		pl->config->get_fixed_state(pl->config, state);
5531ac63e39SFlorian Fainelli 	else if (pl->link_gpio)
554bb322a90SFlorian Fainelli 		state->link = !!gpiod_get_value_cansleep(pl->link_gpio);
5559525ae83SRussell King 
5564e5aeb41SRussell King 	phylink_resolve_flow(state);
5579525ae83SRussell King }
5589525ae83SRussell King 
5594c0d6d3aSRussell King static void phylink_mac_initial_config(struct phylink *pl, bool force_restart)
56097fec51fSRussell King {
56197fec51fSRussell King 	struct phylink_link_state link_state;
56297fec51fSRussell King 
56397fec51fSRussell King 	switch (pl->cur_link_an_mode) {
56497fec51fSRussell King 	case MLO_AN_PHY:
56597fec51fSRussell King 		link_state = pl->phy_state;
56697fec51fSRussell King 		break;
56797fec51fSRussell King 
56897fec51fSRussell King 	case MLO_AN_FIXED:
56997fec51fSRussell King 		phylink_get_fixed_state(pl, &link_state);
57097fec51fSRussell King 		break;
57197fec51fSRussell King 
57297fec51fSRussell King 	case MLO_AN_INBAND:
57397fec51fSRussell King 		link_state = pl->link_config;
57497fec51fSRussell King 		if (link_state.interface == PHY_INTERFACE_MODE_SGMII)
57597fec51fSRussell King 			link_state.pause = MLO_PAUSE_NONE;
57697fec51fSRussell King 		break;
57797fec51fSRussell King 
57897fec51fSRussell King 	default: /* can't happen */
57997fec51fSRussell King 		return;
58097fec51fSRussell King 	}
58197fec51fSRussell King 
58297fec51fSRussell King 	link_state.link = false;
58397fec51fSRussell King 
58497fec51fSRussell King 	phylink_apply_manual_flow(pl, &link_state);
585b7ad14c2SRussell King 	phylink_major_config(pl, force_restart, &link_state);
58697fec51fSRussell King }
58797fec51fSRussell King 
5889525ae83SRussell King static const char *phylink_pause_to_str(int pause)
5899525ae83SRussell King {
5909525ae83SRussell King 	switch (pause & MLO_PAUSE_TXRX_MASK) {
5919525ae83SRussell King 	case MLO_PAUSE_TX | MLO_PAUSE_RX:
5929525ae83SRussell King 		return "rx/tx";
5939525ae83SRussell King 	case MLO_PAUSE_TX:
5949525ae83SRussell King 		return "tx";
5959525ae83SRussell King 	case MLO_PAUSE_RX:
5969525ae83SRussell King 		return "rx";
5979525ae83SRussell King 	default:
5989525ae83SRussell King 		return "off";
5999525ae83SRussell King 	}
6009525ae83SRussell King }
6019525ae83SRussell King 
6024c0d6d3aSRussell King static void phylink_link_up(struct phylink *pl,
60327755ff8SIoana Ciornei 			    struct phylink_link_state link_state)
60427755ff8SIoana Ciornei {
60527755ff8SIoana Ciornei 	struct net_device *ndev = pl->netdev;
60627755ff8SIoana Ciornei 
607b4b12b0dSDavid S. Miller 	pl->cur_interface = link_state.interface;
6084c0d6d3aSRussell King 
6094c0d6d3aSRussell King 	if (pl->pcs_ops && pl->pcs_ops->pcs_link_up)
6107137e18fSRussell King 		pl->pcs_ops->pcs_link_up(pl->pcs, pl->cur_link_an_mode,
6114c0d6d3aSRussell King 					 pl->cur_interface,
6124c0d6d3aSRussell King 					 link_state.speed, link_state.duplex);
6134c0d6d3aSRussell King 
614e7765d63SRussell King 	pl->mac_ops->mac_link_up(pl->config, pl->phydev,
61591a208f2SRussell King 				 pl->cur_link_an_mode, pl->cur_interface,
61691a208f2SRussell King 				 link_state.speed, link_state.duplex,
61791a208f2SRussell King 				 !!(link_state.pause & MLO_PAUSE_TX),
61891a208f2SRussell King 				 !!(link_state.pause & MLO_PAUSE_RX));
61927755ff8SIoana Ciornei 
62043de6195SIoana Ciornei 	if (ndev)
62127755ff8SIoana Ciornei 		netif_carrier_on(ndev);
62227755ff8SIoana Ciornei 
62317091180SIoana Ciornei 	phylink_info(pl,
62427755ff8SIoana Ciornei 		     "Link is Up - %s/%s - flow control %s\n",
62527755ff8SIoana Ciornei 		     phy_speed_to_str(link_state.speed),
62627755ff8SIoana Ciornei 		     phy_duplex_to_str(link_state.duplex),
62727755ff8SIoana Ciornei 		     phylink_pause_to_str(link_state.pause));
62827755ff8SIoana Ciornei }
62927755ff8SIoana Ciornei 
6304c0d6d3aSRussell King static void phylink_link_down(struct phylink *pl)
63127755ff8SIoana Ciornei {
63227755ff8SIoana Ciornei 	struct net_device *ndev = pl->netdev;
63327755ff8SIoana Ciornei 
63443de6195SIoana Ciornei 	if (ndev)
63527755ff8SIoana Ciornei 		netif_carrier_off(ndev);
636e7765d63SRussell King 	pl->mac_ops->mac_link_down(pl->config, pl->cur_link_an_mode,
637b4b12b0dSDavid S. Miller 				   pl->cur_interface);
63817091180SIoana Ciornei 	phylink_info(pl, "Link is Down\n");
63927755ff8SIoana Ciornei }
64027755ff8SIoana Ciornei 
6419525ae83SRussell King static void phylink_resolve(struct work_struct *w)
6429525ae83SRussell King {
6439525ae83SRussell King 	struct phylink *pl = container_of(w, struct phylink, resolve);
6449525ae83SRussell King 	struct phylink_link_state link_state;
6459525ae83SRussell King 	struct net_device *ndev = pl->netdev;
646319bfafeSRussell King 	bool mac_config = false;
647b06e5cacSRussell King 	bool cur_link_state;
6489525ae83SRussell King 
6499525ae83SRussell King 	mutex_lock(&pl->state_mutex);
650b06e5cacSRussell King 	if (pl->netdev)
651b06e5cacSRussell King 		cur_link_state = netif_carrier_ok(ndev);
652b06e5cacSRussell King 	else
653b06e5cacSRussell King 		cur_link_state = pl->old_link_state;
654b06e5cacSRussell King 
6559525ae83SRussell King 	if (pl->phylink_disable_state) {
6569525ae83SRussell King 		pl->mac_link_dropped = false;
6579525ae83SRussell King 		link_state.link = false;
6589525ae83SRussell King 	} else if (pl->mac_link_dropped) {
6599525ae83SRussell King 		link_state.link = false;
6609525ae83SRussell King 	} else {
66124cf0e69SRussell King 		switch (pl->cur_link_an_mode) {
6629525ae83SRussell King 		case MLO_AN_PHY:
6639525ae83SRussell King 			link_state = pl->phy_state;
6642d5fbef0SRussell King 			phylink_apply_manual_flow(pl, &link_state);
665319bfafeSRussell King 			mac_config = link_state.link;
6669525ae83SRussell King 			break;
6679525ae83SRussell King 
6689525ae83SRussell King 		case MLO_AN_FIXED:
6699525ae83SRussell King 			phylink_get_fixed_state(pl, &link_state);
670319bfafeSRussell King 			mac_config = link_state.link;
6719525ae83SRussell King 			break;
6729525ae83SRussell King 
67386a362c4SRussell King 		case MLO_AN_INBAND:
674d46b7e4fSRussell King 			phylink_mac_pcs_get_state(pl, &link_state);
6759525ae83SRussell King 
676406cb0c4SRussell King 			/* If we have a phy, the "up" state is the union of
677406cb0c4SRussell King 			 * both the PHY and the MAC */
678406cb0c4SRussell King 			if (pl->phydev)
679406cb0c4SRussell King 				link_state.link &= pl->phy_state.link;
6809525ae83SRussell King 
681406cb0c4SRussell King 			/* Only update if the PHY link is up */
682406cb0c4SRussell King 			if (pl->phydev && pl->phy_state.link) {
6839525ae83SRussell King 				link_state.interface = pl->phy_state.interface;
6849525ae83SRussell King 
685406cb0c4SRussell King 				/* If we have a PHY, we need to update with
68633faac8eSRussell King 				 * the PHY flow control bits. */
68733faac8eSRussell King 				link_state.pause = pl->phy_state.pause;
688319bfafeSRussell King 				mac_config = true;
6899525ae83SRussell King 			}
690319bfafeSRussell King 			phylink_apply_manual_flow(pl, &link_state);
6919525ae83SRussell King 			break;
6929525ae83SRussell King 		}
6939525ae83SRussell King 	}
6949525ae83SRussell King 
69516319a7dSRussell King 	if (mac_config) {
69616319a7dSRussell King 		if (link_state.interface != pl->link_config.interface) {
69716319a7dSRussell King 			/* The interface has changed, force the link down and
69816319a7dSRussell King 			 * then reconfigure.
69916319a7dSRussell King 			 */
70016319a7dSRussell King 			if (cur_link_state) {
70116319a7dSRussell King 				phylink_link_down(pl);
70216319a7dSRussell King 				cur_link_state = false;
70316319a7dSRussell King 			}
704b7ad14c2SRussell King 			phylink_major_config(pl, false, &link_state);
7055005b163SRussell King 			pl->link_config.interface = link_state.interface;
7067cceb599SRussell King 		} else if (!pl->pcs_ops) {
7075005b163SRussell King 			/* The interface remains unchanged, only the speed,
7085005b163SRussell King 			 * duplex or pause settings have changed. Call the
7097cceb599SRussell King 			 * old mac_config() method to configure the MAC/PCS
7107cceb599SRussell King 			 * only if we do not have a PCS installed (an
7117cceb599SRussell King 			 * unconverted user.)
7125005b163SRussell King 			 */
713319bfafeSRussell King 			phylink_mac_config(pl, &link_state);
71416319a7dSRussell King 		}
7155005b163SRussell King 	}
716319bfafeSRussell King 
717b06e5cacSRussell King 	if (link_state.link != cur_link_state) {
71843de6195SIoana Ciornei 		pl->old_link_state = link_state.link;
71927755ff8SIoana Ciornei 		if (!link_state.link)
7204c0d6d3aSRussell King 			phylink_link_down(pl);
72127755ff8SIoana Ciornei 		else
7224c0d6d3aSRussell King 			phylink_link_up(pl, link_state);
7239525ae83SRussell King 	}
7249525ae83SRussell King 	if (!link_state.link && pl->mac_link_dropped) {
7259525ae83SRussell King 		pl->mac_link_dropped = false;
7269525ae83SRussell King 		queue_work(system_power_efficient_wq, &pl->resolve);
7279525ae83SRussell King 	}
7289525ae83SRussell King 	mutex_unlock(&pl->state_mutex);
7299525ae83SRussell King }
7309525ae83SRussell King 
7319525ae83SRussell King static void phylink_run_resolve(struct phylink *pl)
7329525ae83SRussell King {
7339525ae83SRussell King 	if (!pl->phylink_disable_state)
7349525ae83SRussell King 		queue_work(system_power_efficient_wq, &pl->resolve);
7359525ae83SRussell King }
7369525ae83SRussell King 
73787454b6eSRussell King static void phylink_run_resolve_and_disable(struct phylink *pl, int bit)
73887454b6eSRussell King {
73987454b6eSRussell King 	unsigned long state = pl->phylink_disable_state;
74087454b6eSRussell King 
74187454b6eSRussell King 	set_bit(bit, &pl->phylink_disable_state);
74287454b6eSRussell King 	if (state == 0) {
74387454b6eSRussell King 		queue_work(system_power_efficient_wq, &pl->resolve);
74487454b6eSRussell King 		flush_work(&pl->resolve);
74587454b6eSRussell King 	}
74687454b6eSRussell King }
74787454b6eSRussell King 
7489cd00a8aSRussell King static void phylink_fixed_poll(struct timer_list *t)
7499cd00a8aSRussell King {
7509cd00a8aSRussell King 	struct phylink *pl = container_of(t, struct phylink, link_poll);
7519cd00a8aSRussell King 
7529cd00a8aSRussell King 	mod_timer(t, jiffies + HZ);
7539cd00a8aSRussell King 
7549cd00a8aSRussell King 	phylink_run_resolve(pl);
7559cd00a8aSRussell King }
7569cd00a8aSRussell King 
757ce0aa27fSRussell King static const struct sfp_upstream_ops sfp_phylink_ops;
758ce0aa27fSRussell King 
7598fa7b9b6SRussell King static int phylink_register_sfp(struct phylink *pl,
7608fa7b9b6SRussell King 				struct fwnode_handle *fwnode)
761ce0aa27fSRussell King {
7622203cbf2SRussell King 	struct sfp_bus *bus;
7638fa7b9b6SRussell King 	int ret;
764ce0aa27fSRussell King 
765eed70fd9SRussell King 	if (!fwnode)
766eed70fd9SRussell King 		return 0;
767eed70fd9SRussell King 
768727b3668SRussell King 	bus = sfp_bus_find_fwnode(fwnode);
7692203cbf2SRussell King 	if (IS_ERR(bus)) {
7702203cbf2SRussell King 		ret = PTR_ERR(bus);
7712203cbf2SRussell King 		phylink_err(pl, "unable to attach SFP bus: %d\n", ret);
7728fa7b9b6SRussell King 		return ret;
7738fa7b9b6SRussell King 	}
7748fa7b9b6SRussell King 
7752203cbf2SRussell King 	pl->sfp_bus = bus;
776ce0aa27fSRussell King 
777727b3668SRussell King 	ret = sfp_bus_add_upstream(bus, pl, &sfp_phylink_ops);
778727b3668SRussell King 	sfp_bus_put(bus);
779727b3668SRussell King 
780727b3668SRussell King 	return ret;
781ce0aa27fSRussell King }
782ce0aa27fSRussell King 
7838796c892SRussell King /**
7848796c892SRussell King  * phylink_create() - create a phylink instance
7859db74e51SRandy Dunlap  * @config: a pointer to the target &struct phylink_config
7868fa7b9b6SRussell King  * @fwnode: a pointer to a &struct fwnode_handle describing the network
7878fa7b9b6SRussell King  *	interface
7888796c892SRussell King  * @iface: the desired link mode defined by &typedef phy_interface_t
789e7765d63SRussell King  * @mac_ops: a pointer to a &struct phylink_mac_ops for the MAC.
7908796c892SRussell King  *
7918796c892SRussell King  * Create a new phylink instance, and parse the link parameters found in @np.
7928796c892SRussell King  * This will parse in-band modes, fixed-link or SFP configuration.
7938796c892SRussell King  *
794269a6b5fSRussell King  * Note: the rtnl lock must not be held when calling this function.
795269a6b5fSRussell King  *
7968796c892SRussell King  * Returns a pointer to a &struct phylink, or an error-pointer value. Users
7978796c892SRussell King  * must use IS_ERR() to check for errors from this function.
7988796c892SRussell King  */
79944cc27e4SIoana Ciornei struct phylink *phylink_create(struct phylink_config *config,
8008fa7b9b6SRussell King 			       struct fwnode_handle *fwnode,
801516b29edSFlorian Fainelli 			       phy_interface_t iface,
802e7765d63SRussell King 			       const struct phylink_mac_ops *mac_ops)
8039525ae83SRussell King {
8049525ae83SRussell King 	struct phylink *pl;
8059525ae83SRussell King 	int ret;
8069525ae83SRussell King 
8079525ae83SRussell King 	pl = kzalloc(sizeof(*pl), GFP_KERNEL);
8089525ae83SRussell King 	if (!pl)
8099525ae83SRussell King 		return ERR_PTR(-ENOMEM);
8109525ae83SRussell King 
8119525ae83SRussell King 	mutex_init(&pl->state_mutex);
8129525ae83SRussell King 	INIT_WORK(&pl->resolve, phylink_resolve);
81344cc27e4SIoana Ciornei 
81444cc27e4SIoana Ciornei 	pl->config = config;
81544cc27e4SIoana Ciornei 	if (config->type == PHYLINK_NETDEV) {
81644cc27e4SIoana Ciornei 		pl->netdev = to_net_dev(config->dev);
81743de6195SIoana Ciornei 	} else if (config->type == PHYLINK_DEV) {
81843de6195SIoana Ciornei 		pl->dev = config->dev;
81944cc27e4SIoana Ciornei 	} else {
82044cc27e4SIoana Ciornei 		kfree(pl);
82144cc27e4SIoana Ciornei 		return ERR_PTR(-EINVAL);
82244cc27e4SIoana Ciornei 	}
82344cc27e4SIoana Ciornei 
8249525ae83SRussell King 	pl->phy_state.interface = iface;
8259525ae83SRussell King 	pl->link_interface = iface;
8264be11ef0SFlorian Fainelli 	if (iface == PHY_INTERFACE_MODE_MOCA)
8274be11ef0SFlorian Fainelli 		pl->link_port = PORT_BNC;
8284be11ef0SFlorian Fainelli 	else
8299525ae83SRussell King 		pl->link_port = PORT_MII;
8309525ae83SRussell King 	pl->link_config.interface = iface;
8319525ae83SRussell King 	pl->link_config.pause = MLO_PAUSE_AN;
8329525ae83SRussell King 	pl->link_config.speed = SPEED_UNKNOWN;
8339525ae83SRussell King 	pl->link_config.duplex = DUPLEX_UNKNOWN;
83474ee0e8cSRussell King 	pl->link_config.an_enabled = true;
835e7765d63SRussell King 	pl->mac_ops = mac_ops;
8369525ae83SRussell King 	__set_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state);
8379cd00a8aSRussell King 	timer_setup(&pl->link_poll, phylink_fixed_poll, 0);
8389525ae83SRussell King 
8399525ae83SRussell King 	bitmap_fill(pl->supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
8409525ae83SRussell King 	linkmode_copy(pl->link_config.advertising, pl->supported);
8419525ae83SRussell King 	phylink_validate(pl, pl->supported, &pl->link_config);
8429525ae83SRussell King 
8438fa7b9b6SRussell King 	ret = phylink_parse_mode(pl, fwnode);
8449525ae83SRussell King 	if (ret < 0) {
8459525ae83SRussell King 		kfree(pl);
8469525ae83SRussell King 		return ERR_PTR(ret);
8479525ae83SRussell King 	}
8489525ae83SRussell King 
84924cf0e69SRussell King 	if (pl->cfg_link_an_mode == MLO_AN_FIXED) {
8508fa7b9b6SRussell King 		ret = phylink_parse_fixedlink(pl, fwnode);
8519525ae83SRussell King 		if (ret < 0) {
8529525ae83SRussell King 			kfree(pl);
8539525ae83SRussell King 			return ERR_PTR(ret);
8549525ae83SRussell King 		}
8559525ae83SRussell King 	}
8569525ae83SRussell King 
85724cf0e69SRussell King 	pl->cur_link_an_mode = pl->cfg_link_an_mode;
85824cf0e69SRussell King 
8598fa7b9b6SRussell King 	ret = phylink_register_sfp(pl, fwnode);
860ce0aa27fSRussell King 	if (ret < 0) {
861ce0aa27fSRussell King 		kfree(pl);
862ce0aa27fSRussell King 		return ERR_PTR(ret);
863ce0aa27fSRussell King 	}
864ce0aa27fSRussell King 
8659525ae83SRussell King 	return pl;
8669525ae83SRussell King }
8679525ae83SRussell King EXPORT_SYMBOL_GPL(phylink_create);
8689525ae83SRussell King 
8697137e18fSRussell King /**
8707137e18fSRussell King  * phylink_set_pcs() - set the current PCS for phylink to use
8717137e18fSRussell King  * @pl: a pointer to a &struct phylink returned from phylink_create()
8727137e18fSRussell King  * @pcs: a pointer to the &struct phylink_pcs
8737137e18fSRussell King  *
8747137e18fSRussell King  * Bind the MAC PCS to phylink.  This may be called after phylink_create(),
8757137e18fSRussell King  * in mac_prepare() or mac_config() methods if it is desired to dynamically
8767137e18fSRussell King  * change the PCS.
8777137e18fSRussell King  *
8787137e18fSRussell King  * Please note that there are behavioural changes with the mac_config()
8797137e18fSRussell King  * callback if a PCS is present (denoting a newer setup) so removing a PCS
8807137e18fSRussell King  * is not supported, and if a PCS is going to be used, it must be registered
8817137e18fSRussell King  * by calling phylink_set_pcs() at the latest in the first mac_config() call.
8827137e18fSRussell King  */
8837137e18fSRussell King void phylink_set_pcs(struct phylink *pl, struct phylink_pcs *pcs)
8844c0d6d3aSRussell King {
8857137e18fSRussell King 	pl->pcs = pcs;
8867137e18fSRussell King 	pl->pcs_ops = pcs->ops;
8874c0d6d3aSRussell King }
8887137e18fSRussell King EXPORT_SYMBOL_GPL(phylink_set_pcs);
8894c0d6d3aSRussell King 
8908796c892SRussell King /**
8918796c892SRussell King  * phylink_destroy() - cleanup and destroy the phylink instance
8928796c892SRussell King  * @pl: a pointer to a &struct phylink returned from phylink_create()
8938796c892SRussell King  *
8948796c892SRussell King  * Destroy a phylink instance. Any PHY that has been attached must have been
8958796c892SRussell King  * cleaned up via phylink_disconnect_phy() prior to calling this function.
896269a6b5fSRussell King  *
897269a6b5fSRussell King  * Note: the rtnl lock must not be held when calling this function.
8988796c892SRussell King  */
8999525ae83SRussell King void phylink_destroy(struct phylink *pl)
9009525ae83SRussell King {
901727b3668SRussell King 	sfp_bus_del_upstream(pl->sfp_bus);
9027b3b0e89SRussell King 	if (pl->link_gpio)
903daab3349SFlorian Fainelli 		gpiod_put(pl->link_gpio);
904ce0aa27fSRussell King 
9059525ae83SRussell King 	cancel_work_sync(&pl->resolve);
9069525ae83SRussell King 	kfree(pl);
9079525ae83SRussell King }
9089525ae83SRussell King EXPORT_SYMBOL_GPL(phylink_destroy);
9099525ae83SRussell King 
910a307593aSDoug Berger static void phylink_phy_change(struct phy_device *phydev, bool up)
9119525ae83SRussell King {
9129525ae83SRussell King 	struct phylink *pl = phydev->phylink;
91333faac8eSRussell King 	bool tx_pause, rx_pause;
91433faac8eSRussell King 
91533faac8eSRussell King 	phy_get_pause(phydev, &tx_pause, &rx_pause);
9169525ae83SRussell King 
9179525ae83SRussell King 	mutex_lock(&pl->state_mutex);
9189525ae83SRussell King 	pl->phy_state.speed = phydev->speed;
9199525ae83SRussell King 	pl->phy_state.duplex = phydev->duplex;
9209525ae83SRussell King 	pl->phy_state.pause = MLO_PAUSE_NONE;
92133faac8eSRussell King 	if (tx_pause)
92233faac8eSRussell King 		pl->phy_state.pause |= MLO_PAUSE_TX;
92333faac8eSRussell King 	if (rx_pause)
92433faac8eSRussell King 		pl->phy_state.pause |= MLO_PAUSE_RX;
9259525ae83SRussell King 	pl->phy_state.interface = phydev->interface;
9269525ae83SRussell King 	pl->phy_state.link = up;
9279525ae83SRussell King 	mutex_unlock(&pl->state_mutex);
9289525ae83SRussell King 
9299525ae83SRussell King 	phylink_run_resolve(pl);
9309525ae83SRussell King 
93117091180SIoana Ciornei 	phylink_dbg(pl, "phy link %s %s/%s/%s\n", up ? "up" : "down",
9329525ae83SRussell King 		    phy_modes(phydev->interface),
9339525ae83SRussell King 		    phy_speed_to_str(phydev->speed),
9349525ae83SRussell King 		    phy_duplex_to_str(phydev->duplex));
9359525ae83SRussell King }
9369525ae83SRussell King 
937e45d1f52SRussell King static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy,
938e45d1f52SRussell King 			       phy_interface_t interface)
9399525ae83SRussell King {
9409525ae83SRussell King 	struct phylink_link_state config;
9419525ae83SRussell King 	__ETHTOOL_DECLARE_LINK_MODE_MASK(supported);
942e27f1787SFlorian Fainelli 	char *irq_str;
9439525ae83SRussell King 	int ret;
9449525ae83SRussell King 
9459525ae83SRussell King 	/*
9469525ae83SRussell King 	 * This is the new way of dealing with flow control for PHYs,
9479525ae83SRussell King 	 * as described by Timur Tabi in commit 529ed1275263 ("net: phy:
9489525ae83SRussell King 	 * phy drivers should not set SUPPORTED_[Asym_]Pause") except
9499525ae83SRussell King 	 * using our validate call to the MAC, we rely upon the MAC
9509525ae83SRussell King 	 * clearing the bits from both supported and advertising fields.
9519525ae83SRussell King 	 */
952725ea4bfSRussell King 	phy_support_asym_pause(phy);
953725ea4bfSRussell King 
954725ea4bfSRussell King 	memset(&config, 0, sizeof(config));
955725ea4bfSRussell King 	linkmode_copy(supported, phy->supported);
956725ea4bfSRussell King 	linkmode_copy(config.advertising, phy->advertising);
957df3f57acSRussell King 
958df3f57acSRussell King 	/* Clause 45 PHYs switch their Serdes lane between several different
959df3f57acSRussell King 	 * modes, normally 10GBASE-R, SGMII. Some use 2500BASE-X for 2.5G
960df3f57acSRussell King 	 * speeds. We really need to know which interface modes the PHY and
961df3f57acSRussell King 	 * MAC supports to properly work out which linkmodes can be supported.
962df3f57acSRussell King 	 */
963df3f57acSRussell King 	if (phy->is_c45 &&
964df3f57acSRussell King 	    interface != PHY_INTERFACE_MODE_RXAUI &&
965df3f57acSRussell King 	    interface != PHY_INTERFACE_MODE_XAUI &&
966df3f57acSRussell King 	    interface != PHY_INTERFACE_MODE_USXGMII)
967df3f57acSRussell King 		config.interface = PHY_INTERFACE_MODE_NA;
968df3f57acSRussell King 	else
969e45d1f52SRussell King 		config.interface = interface;
9709525ae83SRussell King 
9719525ae83SRussell King 	ret = phylink_validate(pl, supported, &config);
97220d8bb0dSHauke Mehrtens 	if (ret) {
97320d8bb0dSHauke Mehrtens 		phylink_warn(pl, "validation of %s with support %*pb and advertisement %*pb failed: %d\n",
97420d8bb0dSHauke Mehrtens 			     phy_modes(config.interface),
97520d8bb0dSHauke Mehrtens 			     __ETHTOOL_LINK_MODE_MASK_NBITS, phy->supported,
97620d8bb0dSHauke Mehrtens 			     __ETHTOOL_LINK_MODE_MASK_NBITS, config.advertising,
97720d8bb0dSHauke Mehrtens 			     ret);
9789525ae83SRussell King 		return ret;
97920d8bb0dSHauke Mehrtens 	}
9809525ae83SRussell King 
9819525ae83SRussell King 	phy->phylink = pl;
9829525ae83SRussell King 	phy->phy_link_change = phylink_phy_change;
9839525ae83SRussell King 
984e27f1787SFlorian Fainelli 	irq_str = phy_attached_info_irq(phy);
98517091180SIoana Ciornei 	phylink_info(pl,
986e27f1787SFlorian Fainelli 		     "PHY [%s] driver [%s] (irq=%s)\n",
987e27f1787SFlorian Fainelli 		     dev_name(&phy->mdio.dev), phy->drv->name, irq_str);
988e27f1787SFlorian Fainelli 	kfree(irq_str);
9899525ae83SRussell King 
9909525ae83SRussell King 	mutex_lock(&phy->lock);
9919525ae83SRussell King 	mutex_lock(&pl->state_mutex);
9929525ae83SRussell King 	pl->phydev = phy;
993e45d1f52SRussell King 	pl->phy_state.interface = interface;
99497fec51fSRussell King 	pl->phy_state.pause = MLO_PAUSE_NONE;
99597fec51fSRussell King 	pl->phy_state.speed = SPEED_UNKNOWN;
99697fec51fSRussell King 	pl->phy_state.duplex = DUPLEX_UNKNOWN;
9979525ae83SRussell King 	linkmode_copy(pl->supported, supported);
9989525ae83SRussell King 	linkmode_copy(pl->link_config.advertising, config.advertising);
9999525ae83SRussell King 
1000cc1122b0SColin Ian King 	/* Restrict the phy advertisement according to the MAC support. */
10013c1bcc86SAndrew Lunn 	linkmode_copy(phy->advertising, config.advertising);
10029525ae83SRussell King 	mutex_unlock(&pl->state_mutex);
10039525ae83SRussell King 	mutex_unlock(&phy->lock);
10049525ae83SRussell King 
100517091180SIoana Ciornei 	phylink_dbg(pl,
10063c1bcc86SAndrew Lunn 		    "phy: setting supported %*pb advertising %*pb\n",
10079525ae83SRussell King 		    __ETHTOOL_LINK_MODE_MASK_NBITS, pl->supported,
10083c1bcc86SAndrew Lunn 		    __ETHTOOL_LINK_MODE_MASK_NBITS, phy->advertising);
10099525ae83SRussell King 
1010434a4315SHeiner Kallweit 	if (phy_interrupt_is_valid(phy))
1011434a4315SHeiner Kallweit 		phy_request_interrupt(phy);
10129525ae83SRussell King 
10139525ae83SRussell King 	return 0;
10149525ae83SRussell King }
10159525ae83SRussell King 
1016938d44c2SRussell King static int phylink_attach_phy(struct phylink *pl, struct phy_device *phy,
10177e418375SBaruch Siach 			      phy_interface_t interface)
10187e418375SBaruch Siach {
101924cf0e69SRussell King 	if (WARN_ON(pl->cfg_link_an_mode == MLO_AN_FIXED ||
102024cf0e69SRussell King 		    (pl->cfg_link_an_mode == MLO_AN_INBAND &&
10217e418375SBaruch Siach 		     phy_interface_mode_is_8023z(interface))))
10227e418375SBaruch Siach 		return -EINVAL;
10237e418375SBaruch Siach 
10247e418375SBaruch Siach 	if (pl->phydev)
10257e418375SBaruch Siach 		return -EBUSY;
10267e418375SBaruch Siach 
1027938d44c2SRussell King 	return phy_attach_direct(pl->netdev, phy, 0, interface);
10287e418375SBaruch Siach }
10297e418375SBaruch Siach 
10308796c892SRussell King /**
10318796c892SRussell King  * phylink_connect_phy() - connect a PHY to the phylink instance
10328796c892SRussell King  * @pl: a pointer to a &struct phylink returned from phylink_create()
10338796c892SRussell King  * @phy: a pointer to a &struct phy_device.
10348796c892SRussell King  *
10358796c892SRussell King  * Connect @phy to the phylink instance specified by @pl by calling
10368796c892SRussell King  * phy_attach_direct(). Configure the @phy according to the MAC driver's
10378796c892SRussell King  * capabilities, start the PHYLIB state machine and enable any interrupts
10388796c892SRussell King  * that the PHY supports.
10398796c892SRussell King  *
10408796c892SRussell King  * This updates the phylink's ethtool supported and advertising link mode
10418796c892SRussell King  * masks.
10428796c892SRussell King  *
10438796c892SRussell King  * Returns 0 on success or a negative errno.
10448796c892SRussell King  */
10459525ae83SRussell King int phylink_connect_phy(struct phylink *pl, struct phy_device *phy)
10469525ae83SRussell King {
1047938d44c2SRussell King 	int ret;
1048938d44c2SRussell King 
10494904b6eaSFlorian Fainelli 	/* Use PHY device/driver interface */
10504904b6eaSFlorian Fainelli 	if (pl->link_interface == PHY_INTERFACE_MODE_NA) {
10514904b6eaSFlorian Fainelli 		pl->link_interface = phy->interface;
10524904b6eaSFlorian Fainelli 		pl->link_config.interface = pl->link_interface;
10534904b6eaSFlorian Fainelli 	}
10544904b6eaSFlorian Fainelli 
1055938d44c2SRussell King 	ret = phylink_attach_phy(pl, phy, pl->link_interface);
1056938d44c2SRussell King 	if (ret < 0)
1057938d44c2SRussell King 		return ret;
1058938d44c2SRussell King 
1059e45d1f52SRussell King 	ret = phylink_bringup_phy(pl, phy, pl->link_config.interface);
1060938d44c2SRussell King 	if (ret)
1061938d44c2SRussell King 		phy_detach(phy);
1062938d44c2SRussell King 
1063938d44c2SRussell King 	return ret;
10649525ae83SRussell King }
10659525ae83SRussell King EXPORT_SYMBOL_GPL(phylink_connect_phy);
10669525ae83SRussell King 
10678796c892SRussell King /**
10688796c892SRussell King  * phylink_of_phy_connect() - connect the PHY specified in the DT mode.
10698796c892SRussell King  * @pl: a pointer to a &struct phylink returned from phylink_create()
10708796c892SRussell King  * @dn: a pointer to a &struct device_node.
10710a62964cSFlorian Fainelli  * @flags: PHY-specific flags to communicate to the PHY device driver
10728796c892SRussell King  *
10738796c892SRussell King  * Connect the phy specified in the device node @dn to the phylink instance
10748796c892SRussell King  * specified by @pl. Actions specified in phylink_connect_phy() will be
10758796c892SRussell King  * performed.
10768796c892SRussell King  *
10778796c892SRussell King  * Returns 0 on success or a negative errno.
10788796c892SRussell King  */
10790a62964cSFlorian Fainelli int phylink_of_phy_connect(struct phylink *pl, struct device_node *dn,
10800a62964cSFlorian Fainelli 			   u32 flags)
10819525ae83SRussell King {
10829525ae83SRussell King 	struct device_node *phy_node;
10839525ae83SRussell King 	struct phy_device *phy_dev;
10849525ae83SRussell King 	int ret;
10859525ae83SRussell King 
1086cf4f2675SRussell King 	/* Fixed links and 802.3z are handled without needing a PHY */
108724cf0e69SRussell King 	if (pl->cfg_link_an_mode == MLO_AN_FIXED ||
108824cf0e69SRussell King 	    (pl->cfg_link_an_mode == MLO_AN_INBAND &&
108986a362c4SRussell King 	     phy_interface_mode_is_8023z(pl->link_interface)))
10909525ae83SRussell King 		return 0;
10919525ae83SRussell King 
10929525ae83SRussell King 	phy_node = of_parse_phandle(dn, "phy-handle", 0);
10939525ae83SRussell King 	if (!phy_node)
10949525ae83SRussell King 		phy_node = of_parse_phandle(dn, "phy", 0);
10959525ae83SRussell King 	if (!phy_node)
10969525ae83SRussell King 		phy_node = of_parse_phandle(dn, "phy-device", 0);
10979525ae83SRussell King 
10989525ae83SRussell King 	if (!phy_node) {
109924cf0e69SRussell King 		if (pl->cfg_link_an_mode == MLO_AN_PHY)
11009525ae83SRussell King 			return -ENODEV;
11019525ae83SRussell King 		return 0;
11029525ae83SRussell King 	}
11039525ae83SRussell King 
1104f5058a27SRussell King 	phy_dev = of_phy_find_device(phy_node);
11059525ae83SRussell King 	/* We're done with the phy_node handle */
11069525ae83SRussell King 	of_node_put(phy_node);
11079525ae83SRussell King 	if (!phy_dev)
11089525ae83SRussell King 		return -ENODEV;
11099525ae83SRussell King 
1110f5058a27SRussell King 	ret = phy_attach_direct(pl->netdev, phy_dev, flags,
1111f5058a27SRussell King 				pl->link_interface);
1112f5058a27SRussell King 	if (ret)
1113f5058a27SRussell King 		return ret;
1114f5058a27SRussell King 
1115e45d1f52SRussell King 	ret = phylink_bringup_phy(pl, phy_dev, pl->link_config.interface);
11169525ae83SRussell King 	if (ret)
11179525ae83SRussell King 		phy_detach(phy_dev);
11189525ae83SRussell King 
11199525ae83SRussell King 	return ret;
11209525ae83SRussell King }
11219525ae83SRussell King EXPORT_SYMBOL_GPL(phylink_of_phy_connect);
11229525ae83SRussell King 
11238796c892SRussell King /**
11248796c892SRussell King  * phylink_disconnect_phy() - disconnect any PHY attached to the phylink
11258796c892SRussell King  *   instance.
11268796c892SRussell King  * @pl: a pointer to a &struct phylink returned from phylink_create()
11278796c892SRussell King  *
11288796c892SRussell King  * Disconnect any current PHY from the phylink instance described by @pl.
11298796c892SRussell King  */
11309525ae83SRussell King void phylink_disconnect_phy(struct phylink *pl)
11319525ae83SRussell King {
11329525ae83SRussell King 	struct phy_device *phy;
11339525ae83SRussell King 
11348b874514SRussell King 	ASSERT_RTNL();
11359525ae83SRussell King 
11369525ae83SRussell King 	phy = pl->phydev;
11379525ae83SRussell King 	if (phy) {
11389525ae83SRussell King 		mutex_lock(&phy->lock);
11399525ae83SRussell King 		mutex_lock(&pl->state_mutex);
11409525ae83SRussell King 		pl->phydev = NULL;
11419525ae83SRussell King 		mutex_unlock(&pl->state_mutex);
11429525ae83SRussell King 		mutex_unlock(&phy->lock);
11439525ae83SRussell King 		flush_work(&pl->resolve);
11449525ae83SRussell King 
11459525ae83SRussell King 		phy_disconnect(phy);
11469525ae83SRussell King 	}
11479525ae83SRussell King }
11489525ae83SRussell King EXPORT_SYMBOL_GPL(phylink_disconnect_phy);
11499525ae83SRussell King 
11508796c892SRussell King /**
11518796c892SRussell King  * phylink_mac_change() - notify phylink of a change in MAC state
11528796c892SRussell King  * @pl: a pointer to a &struct phylink returned from phylink_create()
11538796c892SRussell King  * @up: indicates whether the link is currently up.
11548796c892SRussell King  *
11558796c892SRussell King  * The MAC driver should call this driver when the state of its link
11568796c892SRussell King  * changes (eg, link failure, new negotiation results, etc.)
11578796c892SRussell King  */
11589525ae83SRussell King void phylink_mac_change(struct phylink *pl, bool up)
11599525ae83SRussell King {
11609525ae83SRussell King 	if (!up)
11619525ae83SRussell King 		pl->mac_link_dropped = true;
11629525ae83SRussell King 	phylink_run_resolve(pl);
116317091180SIoana Ciornei 	phylink_dbg(pl, "mac link %s\n", up ? "up" : "down");
11649525ae83SRussell King }
11659525ae83SRussell King EXPORT_SYMBOL_GPL(phylink_mac_change);
11669525ae83SRussell King 
11677b3b0e89SRussell King static irqreturn_t phylink_link_handler(int irq, void *data)
11687b3b0e89SRussell King {
11697b3b0e89SRussell King 	struct phylink *pl = data;
11707b3b0e89SRussell King 
11717b3b0e89SRussell King 	phylink_run_resolve(pl);
11727b3b0e89SRussell King 
11737b3b0e89SRussell King 	return IRQ_HANDLED;
11747b3b0e89SRussell King }
11757b3b0e89SRussell King 
11768796c892SRussell King /**
11778796c892SRussell King  * phylink_start() - start a phylink instance
11788796c892SRussell King  * @pl: a pointer to a &struct phylink returned from phylink_create()
11798796c892SRussell King  *
11808796c892SRussell King  * Start the phylink instance specified by @pl, configuring the MAC for the
11818796c892SRussell King  * desired link mode(s) and negotiation style. This should be called from the
11828796c892SRussell King  * network device driver's &struct net_device_ops ndo_open() method.
11838796c892SRussell King  */
11849525ae83SRussell King void phylink_start(struct phylink *pl)
11859525ae83SRussell King {
11865c05c1dbSRussell King 	bool poll = false;
11875c05c1dbSRussell King 
11888b874514SRussell King 	ASSERT_RTNL();
11899525ae83SRussell King 
119017091180SIoana Ciornei 	phylink_info(pl, "configuring for %s/%s link mode\n",
119124cf0e69SRussell King 		     phylink_an_mode_str(pl->cur_link_an_mode),
11929525ae83SRussell King 		     phy_modes(pl->link_config.interface));
11939525ae83SRussell King 
1194aeeb2e8fSAntoine Tenart 	/* Always set the carrier off */
119543de6195SIoana Ciornei 	if (pl->netdev)
1196aeeb2e8fSAntoine Tenart 		netif_carrier_off(pl->netdev);
1197aeeb2e8fSAntoine Tenart 
11989525ae83SRussell King 	/* Apply the link configuration to the MAC when starting. This allows
11999525ae83SRussell King 	 * a fixed-link to start with the correct parameters, and also
1200cc1122b0SColin Ian King 	 * ensures that we set the appropriate advertisement for Serdes links.
12014c0d6d3aSRussell King 	 *
12024c0d6d3aSRussell King 	 * Restart autonegotiation if using 802.3z to ensure that the link
120385b43945SRussell King 	 * parameters are properly negotiated.  This is necessary for DSA
120485b43945SRussell King 	 * switches using 802.3z negotiation to ensure they see our modes.
120585b43945SRussell King 	 */
12064c0d6d3aSRussell King 	phylink_mac_initial_config(pl, true);
120785b43945SRussell King 
12089525ae83SRussell King 	clear_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state);
12099525ae83SRussell King 	phylink_run_resolve(pl);
12109525ae83SRussell King 
121124cf0e69SRussell King 	if (pl->cfg_link_an_mode == MLO_AN_FIXED && pl->link_gpio) {
12127b3b0e89SRussell King 		int irq = gpiod_to_irq(pl->link_gpio);
12137b3b0e89SRussell King 
12147b3b0e89SRussell King 		if (irq > 0) {
12157b3b0e89SRussell King 			if (!request_irq(irq, phylink_link_handler,
12167b3b0e89SRussell King 					 IRQF_TRIGGER_RISING |
12177b3b0e89SRussell King 					 IRQF_TRIGGER_FALLING,
12187b3b0e89SRussell King 					 "netdev link", pl))
12197b3b0e89SRussell King 				pl->link_irq = irq;
12207b3b0e89SRussell King 			else
12217b3b0e89SRussell King 				irq = 0;
12227b3b0e89SRussell King 		}
12237b3b0e89SRussell King 		if (irq <= 0)
12245c05c1dbSRussell King 			poll = true;
12257b3b0e89SRussell King 	}
12265c05c1dbSRussell King 
12275c05c1dbSRussell King 	switch (pl->cfg_link_an_mode) {
12285c05c1dbSRussell King 	case MLO_AN_FIXED:
12295c05c1dbSRussell King 		poll |= pl->config->poll_fixed_state;
12305c05c1dbSRussell King 		break;
12315c05c1dbSRussell King 	case MLO_AN_INBAND:
12325c05c1dbSRussell King 		poll |= pl->config->pcs_poll;
12337137e18fSRussell King 		if (pl->pcs)
12347137e18fSRussell King 			poll |= pl->pcs->poll;
12355c05c1dbSRussell King 		break;
12365c05c1dbSRussell King 	}
12375c05c1dbSRussell King 	if (poll)
12389cd00a8aSRussell King 		mod_timer(&pl->link_poll, jiffies + HZ);
12399525ae83SRussell King 	if (pl->phydev)
12409525ae83SRussell King 		phy_start(pl->phydev);
1241c7fa7f56SArseny Solokha 	if (pl->sfp_bus)
1242c7fa7f56SArseny Solokha 		sfp_upstream_start(pl->sfp_bus);
12439525ae83SRussell King }
12449525ae83SRussell King EXPORT_SYMBOL_GPL(phylink_start);
12459525ae83SRussell King 
12468796c892SRussell King /**
12478796c892SRussell King  * phylink_stop() - stop a phylink instance
12488796c892SRussell King  * @pl: a pointer to a &struct phylink returned from phylink_create()
12498796c892SRussell King  *
12508796c892SRussell King  * Stop the phylink instance specified by @pl. This should be called from the
12518796c892SRussell King  * network device driver's &struct net_device_ops ndo_stop() method.  The
12528796c892SRussell King  * network device's carrier state should not be changed prior to calling this
12538796c892SRussell King  * function.
12548796c892SRussell King  */
12559525ae83SRussell King void phylink_stop(struct phylink *pl)
12569525ae83SRussell King {
12578b874514SRussell King 	ASSERT_RTNL();
12589525ae83SRussell King 
1259ce0aa27fSRussell King 	if (pl->sfp_bus)
1260ce0aa27fSRussell King 		sfp_upstream_stop(pl->sfp_bus);
1261c7fa7f56SArseny Solokha 	if (pl->phydev)
1262c7fa7f56SArseny Solokha 		phy_stop(pl->phydev);
12639cd00a8aSRussell King 	del_timer_sync(&pl->link_poll);
12647b3b0e89SRussell King 	if (pl->link_irq) {
12657b3b0e89SRussell King 		free_irq(pl->link_irq, pl);
12667b3b0e89SRussell King 		pl->link_irq = 0;
12677b3b0e89SRussell King 	}
12689525ae83SRussell King 
126987454b6eSRussell King 	phylink_run_resolve_and_disable(pl, PHYLINK_DISABLE_STOPPED);
12709525ae83SRussell King }
12719525ae83SRussell King EXPORT_SYMBOL_GPL(phylink_stop);
12729525ae83SRussell King 
12738796c892SRussell King /**
12748796c892SRussell King  * phylink_ethtool_get_wol() - get the wake on lan parameters for the PHY
12758796c892SRussell King  * @pl: a pointer to a &struct phylink returned from phylink_create()
12768796c892SRussell King  * @wol: a pointer to &struct ethtool_wolinfo to hold the read parameters
12778796c892SRussell King  *
12788796c892SRussell King  * Read the wake on lan parameters from the PHY attached to the phylink
12798796c892SRussell King  * instance specified by @pl. If no PHY is currently attached, report no
12808796c892SRussell King  * support for wake on lan.
12818796c892SRussell King  */
12829525ae83SRussell King void phylink_ethtool_get_wol(struct phylink *pl, struct ethtool_wolinfo *wol)
12839525ae83SRussell King {
12848b874514SRussell King 	ASSERT_RTNL();
12859525ae83SRussell King 
12869525ae83SRussell King 	wol->supported = 0;
12879525ae83SRussell King 	wol->wolopts = 0;
12889525ae83SRussell King 
12899525ae83SRussell King 	if (pl->phydev)
12909525ae83SRussell King 		phy_ethtool_get_wol(pl->phydev, wol);
12919525ae83SRussell King }
12929525ae83SRussell King EXPORT_SYMBOL_GPL(phylink_ethtool_get_wol);
12939525ae83SRussell King 
12948796c892SRussell King /**
12958796c892SRussell King  * phylink_ethtool_set_wol() - set wake on lan parameters
12968796c892SRussell King  * @pl: a pointer to a &struct phylink returned from phylink_create()
12978796c892SRussell King  * @wol: a pointer to &struct ethtool_wolinfo for the desired parameters
12988796c892SRussell King  *
12998796c892SRussell King  * Set the wake on lan parameters for the PHY attached to the phylink
13008796c892SRussell King  * instance specified by @pl. If no PHY is attached, returns %EOPNOTSUPP
13018796c892SRussell King  * error.
13028796c892SRussell King  *
13038796c892SRussell King  * Returns zero on success or negative errno code.
13048796c892SRussell King  */
13059525ae83SRussell King int phylink_ethtool_set_wol(struct phylink *pl, struct ethtool_wolinfo *wol)
13069525ae83SRussell King {
13079525ae83SRussell King 	int ret = -EOPNOTSUPP;
13089525ae83SRussell King 
13098b874514SRussell King 	ASSERT_RTNL();
13109525ae83SRussell King 
13119525ae83SRussell King 	if (pl->phydev)
13129525ae83SRussell King 		ret = phy_ethtool_set_wol(pl->phydev, wol);
13139525ae83SRussell King 
13149525ae83SRussell King 	return ret;
13159525ae83SRussell King }
13169525ae83SRussell King EXPORT_SYMBOL_GPL(phylink_ethtool_set_wol);
13179525ae83SRussell King 
13189525ae83SRussell King static void phylink_merge_link_mode(unsigned long *dst, const unsigned long *b)
13199525ae83SRussell King {
13209525ae83SRussell King 	__ETHTOOL_DECLARE_LINK_MODE_MASK(mask);
13219525ae83SRussell King 
13229525ae83SRussell King 	linkmode_zero(mask);
13239525ae83SRussell King 	phylink_set_port_modes(mask);
13249525ae83SRussell King 
13259525ae83SRussell King 	linkmode_and(dst, dst, mask);
13269525ae83SRussell King 	linkmode_or(dst, dst, b);
13279525ae83SRussell King }
13289525ae83SRussell King 
13299525ae83SRussell King static void phylink_get_ksettings(const struct phylink_link_state *state,
13309525ae83SRussell King 				  struct ethtool_link_ksettings *kset)
13319525ae83SRussell King {
13329525ae83SRussell King 	phylink_merge_link_mode(kset->link_modes.advertising, state->advertising);
13339525ae83SRussell King 	linkmode_copy(kset->link_modes.lp_advertising, state->lp_advertising);
13349525ae83SRussell King 	kset->base.speed = state->speed;
13359525ae83SRussell King 	kset->base.duplex = state->duplex;
13369525ae83SRussell King 	kset->base.autoneg = state->an_enabled ? AUTONEG_ENABLE :
13379525ae83SRussell King 				AUTONEG_DISABLE;
13389525ae83SRussell King }
13399525ae83SRussell King 
13408796c892SRussell King /**
13418796c892SRussell King  * phylink_ethtool_ksettings_get() - get the current link settings
13428796c892SRussell King  * @pl: a pointer to a &struct phylink returned from phylink_create()
13438796c892SRussell King  * @kset: a pointer to a &struct ethtool_link_ksettings to hold link settings
13448796c892SRussell King  *
13458796c892SRussell King  * Read the current link settings for the phylink instance specified by @pl.
13468796c892SRussell King  * This will be the link settings read from the MAC, PHY or fixed link
13478796c892SRussell King  * settings depending on the current negotiation mode.
13488796c892SRussell King  */
13499525ae83SRussell King int phylink_ethtool_ksettings_get(struct phylink *pl,
13509525ae83SRussell King 				  struct ethtool_link_ksettings *kset)
13519525ae83SRussell King {
13529525ae83SRussell King 	struct phylink_link_state link_state;
13539525ae83SRussell King 
13548b874514SRussell King 	ASSERT_RTNL();
13559525ae83SRussell King 
13569525ae83SRussell King 	if (pl->phydev) {
13579525ae83SRussell King 		phy_ethtool_ksettings_get(pl->phydev, kset);
13589525ae83SRussell King 	} else {
13599525ae83SRussell King 		kset->base.port = pl->link_port;
13609525ae83SRussell King 	}
13619525ae83SRussell King 
13629525ae83SRussell King 	linkmode_copy(kset->link_modes.supported, pl->supported);
13639525ae83SRussell King 
136424cf0e69SRussell King 	switch (pl->cur_link_an_mode) {
13659525ae83SRussell King 	case MLO_AN_FIXED:
13669525ae83SRussell King 		/* We are using fixed settings. Report these as the
13679525ae83SRussell King 		 * current link settings - and note that these also
13689525ae83SRussell King 		 * represent the supported speeds/duplex/pause modes.
13699525ae83SRussell King 		 */
13709525ae83SRussell King 		phylink_get_fixed_state(pl, &link_state);
13719525ae83SRussell King 		phylink_get_ksettings(&link_state, kset);
13729525ae83SRussell King 		break;
13739525ae83SRussell King 
137486a362c4SRussell King 	case MLO_AN_INBAND:
13759525ae83SRussell King 		/* If there is a phy attached, then use the reported
13769525ae83SRussell King 		 * settings from the phy with no modification.
13779525ae83SRussell King 		 */
13789525ae83SRussell King 		if (pl->phydev)
13799525ae83SRussell King 			break;
13809525ae83SRussell King 
1381d46b7e4fSRussell King 		phylink_mac_pcs_get_state(pl, &link_state);
13829525ae83SRussell King 
13839525ae83SRussell King 		/* The MAC is reporting the link results from its own PCS
13849525ae83SRussell King 		 * layer via in-band status. Report these as the current
13859525ae83SRussell King 		 * link settings.
13869525ae83SRussell King 		 */
13879525ae83SRussell King 		phylink_get_ksettings(&link_state, kset);
13889525ae83SRussell King 		break;
13899525ae83SRussell King 	}
13909525ae83SRussell King 
13919525ae83SRussell King 	return 0;
13929525ae83SRussell King }
13939525ae83SRussell King EXPORT_SYMBOL_GPL(phylink_ethtool_ksettings_get);
13949525ae83SRussell King 
13958796c892SRussell King /**
13968796c892SRussell King  * phylink_ethtool_ksettings_set() - set the link settings
13978796c892SRussell King  * @pl: a pointer to a &struct phylink returned from phylink_create()
13988796c892SRussell King  * @kset: a pointer to a &struct ethtool_link_ksettings for the desired modes
13998796c892SRussell King  */
14009525ae83SRussell King int phylink_ethtool_ksettings_set(struct phylink *pl,
14019525ae83SRussell King 				  const struct ethtool_link_ksettings *kset)
14029525ae83SRussell King {
140377316763SRussell King 	__ETHTOOL_DECLARE_LINK_MODE_MASK(support);
14049525ae83SRussell King 	struct phylink_link_state config;
1405c8cab719SRussell King 	const struct phy_setting *s;
14069525ae83SRussell King 
14078b874514SRussell King 	ASSERT_RTNL();
14089525ae83SRussell King 
1409cbc1bb1eSRussell King 	if (pl->phydev) {
1410cbc1bb1eSRussell King 		/* We can rely on phylib for this update; we also do not need
1411cbc1bb1eSRussell King 		 * to update the pl->link_config settings:
1412cbc1bb1eSRussell King 		 * - the configuration returned via ksettings_get() will come
1413cbc1bb1eSRussell King 		 *   from phylib whenever a PHY is present.
1414cbc1bb1eSRussell King 		 * - link_config.interface will be updated by the PHY calling
1415cbc1bb1eSRussell King 		 *   back via phylink_phy_change() and a subsequent resolve.
1416cbc1bb1eSRussell King 		 * - initial link configuration for PHY mode comes from the
1417cbc1bb1eSRussell King 		 *   last phy state updated via phylink_phy_change().
1418cbc1bb1eSRussell King 		 * - other configuration changes (e.g. pause modes) are
1419cbc1bb1eSRussell King 		 *   performed directly via phylib.
1420cbc1bb1eSRussell King 		 * - if in in-band mode with a PHY, the link configuration
1421cbc1bb1eSRussell King 		 *   is passed on the link from the PHY, and all of
1422cbc1bb1eSRussell King 		 *   link_config.{speed,duplex,an_enabled,pause} are not used.
1423cbc1bb1eSRussell King 		 * - the only possible use would be link_config.advertising
1424cbc1bb1eSRussell King 		 *   pause modes when in 1000base-X mode with a PHY, but in
1425cbc1bb1eSRussell King 		 *   the presence of a PHY, this should not be changed as that
1426cbc1bb1eSRussell King 		 *   should be determined from the media side advertisement.
1427cbc1bb1eSRussell King 		 */
1428cbc1bb1eSRussell King 		return phy_ethtool_ksettings_set(pl->phydev, kset);
1429cbc1bb1eSRussell King 	}
1430cbc1bb1eSRussell King 
143177316763SRussell King 	linkmode_copy(support, pl->supported);
14329525ae83SRussell King 	config = pl->link_config;
1433c8cab719SRussell King 	config.an_enabled = kset->base.autoneg == AUTONEG_ENABLE;
14349525ae83SRussell King 
1435c8cab719SRussell King 	/* Mask out unsupported advertisements, and force the autoneg bit */
14369525ae83SRussell King 	linkmode_and(config.advertising, kset->link_modes.advertising,
143777316763SRussell King 		     support);
1438c8cab719SRussell King 	linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, config.advertising,
1439c8cab719SRussell King 			 config.an_enabled);
14409525ae83SRussell King 
14419525ae83SRussell King 	/* FIXME: should we reject autoneg if phy/mac does not support it? */
1442c8cab719SRussell King 	switch (kset->base.autoneg) {
1443c8cab719SRussell King 	case AUTONEG_DISABLE:
14449525ae83SRussell King 		/* Autonegotiation disabled, select a suitable speed and
14459525ae83SRussell King 		 * duplex.
14469525ae83SRussell King 		 */
14479525ae83SRussell King 		s = phy_lookup_setting(kset->base.speed, kset->base.duplex,
144877316763SRussell King 				       support, false);
14499525ae83SRussell King 		if (!s)
14509525ae83SRussell King 			return -EINVAL;
14519525ae83SRussell King 
14521e1bf14aSRussell King 		/* If we have a fixed link, refuse to change link parameters.
14531e1bf14aSRussell King 		 * If the link parameters match, accept them but do nothing.
14549525ae83SRussell King 		 */
14551e1bf14aSRussell King 		if (pl->cur_link_an_mode == MLO_AN_FIXED) {
14561e1bf14aSRussell King 			if (s->speed != pl->link_config.speed ||
14571e1bf14aSRussell King 			    s->duplex != pl->link_config.duplex)
14589525ae83SRussell King 				return -EINVAL;
14591e1bf14aSRussell King 			return 0;
14601e1bf14aSRussell King 		}
14619525ae83SRussell King 
14629525ae83SRussell King 		config.speed = s->speed;
14639525ae83SRussell King 		config.duplex = s->duplex;
1464c8cab719SRussell King 		break;
14659525ae83SRussell King 
1466c8cab719SRussell King 	case AUTONEG_ENABLE:
14671e1bf14aSRussell King 		/* If we have a fixed link, allow autonegotiation (since that
14681e1bf14aSRussell King 		 * is our default case) but do not allow the advertisement to
14691e1bf14aSRussell King 		 * be changed. If the advertisement matches, simply return.
14701e1bf14aSRussell King 		 */
14711e1bf14aSRussell King 		if (pl->cur_link_an_mode == MLO_AN_FIXED) {
14721e1bf14aSRussell King 			if (!linkmode_equal(config.advertising,
14731e1bf14aSRussell King 					    pl->link_config.advertising))
14749525ae83SRussell King 				return -EINVAL;
14751e1bf14aSRussell King 			return 0;
14761e1bf14aSRussell King 		}
14779525ae83SRussell King 
14789525ae83SRussell King 		config.speed = SPEED_UNKNOWN;
14799525ae83SRussell King 		config.duplex = DUPLEX_UNKNOWN;
1480c8cab719SRussell King 		break;
14819525ae83SRussell King 
1482c8cab719SRussell King 	default:
1483c8cab719SRussell King 		return -EINVAL;
14849525ae83SRussell King 	}
14859525ae83SRussell King 
14861e1bf14aSRussell King 	/* We have ruled out the case with a PHY attached, and the
14871e1bf14aSRussell King 	 * fixed-link cases.  All that is left are in-band links.
14885d57c327SRussell King 	 */
148977316763SRussell King 	if (phylink_validate(pl, support, &config))
14909525ae83SRussell King 		return -EINVAL;
14919525ae83SRussell King 
1492cc1122b0SColin Ian King 	/* If autonegotiation is enabled, we must have an advertisement */
1493cbc1bb1eSRussell King 	if (config.an_enabled && phylink_is_empty_linkmode(config.advertising))
14949525ae83SRussell King 		return -EINVAL;
14959525ae83SRussell King 
14969525ae83SRussell King 	mutex_lock(&pl->state_mutex);
14975d57c327SRussell King 	pl->link_config.speed = config.speed;
14985d57c327SRussell King 	pl->link_config.duplex = config.duplex;
1499a83c8829SRussell King 	pl->link_config.an_enabled = config.an_enabled;
15009525ae83SRussell King 
1501b7ad14c2SRussell King 	if (pl->link_config.interface != config.interface) {
1502b7ad14c2SRussell King 		/* The interface changed, e.g. 1000base-X <-> 2500base-X */
1503b7ad14c2SRussell King 		/* We need to force the link down, then change the interface */
1504b7ad14c2SRussell King 		if (pl->old_link_state) {
1505b7ad14c2SRussell King 			phylink_link_down(pl);
1506b7ad14c2SRussell King 			pl->old_link_state = false;
1507b7ad14c2SRussell King 		}
1508b7ad14c2SRussell King 		if (!test_bit(PHYLINK_DISABLE_STOPPED,
1509b7ad14c2SRussell King 			      &pl->phylink_disable_state))
1510b7ad14c2SRussell King 			phylink_major_config(pl, false, &config);
1511b7ad14c2SRussell King 		pl->link_config.interface = config.interface;
1512b7ad14c2SRussell King 		linkmode_copy(pl->link_config.advertising, config.advertising);
1513b7ad14c2SRussell King 	} else if (!linkmode_equal(pl->link_config.advertising,
1514b7ad14c2SRussell King 				   config.advertising)) {
1515b7ad14c2SRussell King 		linkmode_copy(pl->link_config.advertising, config.advertising);
1516b7ad14c2SRussell King 		phylink_change_inband_advert(pl);
15179525ae83SRussell King 	}
15189525ae83SRussell King 	mutex_unlock(&pl->state_mutex);
15199525ae83SRussell King 
1520d18c2a1bSDan Carpenter 	return 0;
15219525ae83SRussell King }
15229525ae83SRussell King EXPORT_SYMBOL_GPL(phylink_ethtool_ksettings_set);
15239525ae83SRussell King 
15248796c892SRussell King /**
15258796c892SRussell King  * phylink_ethtool_nway_reset() - restart negotiation
15268796c892SRussell King  * @pl: a pointer to a &struct phylink returned from phylink_create()
15278796c892SRussell King  *
15288796c892SRussell King  * Restart negotiation for the phylink instance specified by @pl. This will
15298796c892SRussell King  * cause any attached phy to restart negotiation with the link partner, and
15308796c892SRussell King  * if the MAC is in a BaseX mode, the MAC will also be requested to restart
15318796c892SRussell King  * negotiation.
15328796c892SRussell King  *
15338796c892SRussell King  * Returns zero on success, or negative error code.
15348796c892SRussell King  */
15359525ae83SRussell King int phylink_ethtool_nway_reset(struct phylink *pl)
15369525ae83SRussell King {
15379525ae83SRussell King 	int ret = 0;
15389525ae83SRussell King 
15398b874514SRussell King 	ASSERT_RTNL();
15409525ae83SRussell King 
15419525ae83SRussell King 	if (pl->phydev)
15429525ae83SRussell King 		ret = phy_restart_aneg(pl->phydev);
15434c0d6d3aSRussell King 	phylink_mac_pcs_an_restart(pl);
15449525ae83SRussell King 
15459525ae83SRussell King 	return ret;
15469525ae83SRussell King }
15479525ae83SRussell King EXPORT_SYMBOL_GPL(phylink_ethtool_nway_reset);
15489525ae83SRussell King 
15498796c892SRussell King /**
15508796c892SRussell King  * phylink_ethtool_get_pauseparam() - get the current pause parameters
15518796c892SRussell King  * @pl: a pointer to a &struct phylink returned from phylink_create()
15528796c892SRussell King  * @pause: a pointer to a &struct ethtool_pauseparam
15538796c892SRussell King  */
15549525ae83SRussell King void phylink_ethtool_get_pauseparam(struct phylink *pl,
15559525ae83SRussell King 				    struct ethtool_pauseparam *pause)
15569525ae83SRussell King {
15578b874514SRussell King 	ASSERT_RTNL();
15589525ae83SRussell King 
15599525ae83SRussell King 	pause->autoneg = !!(pl->link_config.pause & MLO_PAUSE_AN);
15609525ae83SRussell King 	pause->rx_pause = !!(pl->link_config.pause & MLO_PAUSE_RX);
15619525ae83SRussell King 	pause->tx_pause = !!(pl->link_config.pause & MLO_PAUSE_TX);
15629525ae83SRussell King }
15639525ae83SRussell King EXPORT_SYMBOL_GPL(phylink_ethtool_get_pauseparam);
15649525ae83SRussell King 
15658796c892SRussell King /**
15668796c892SRussell King  * phylink_ethtool_set_pauseparam() - set the current pause parameters
15678796c892SRussell King  * @pl: a pointer to a &struct phylink returned from phylink_create()
15688796c892SRussell King  * @pause: a pointer to a &struct ethtool_pauseparam
15698796c892SRussell King  */
15709525ae83SRussell King int phylink_ethtool_set_pauseparam(struct phylink *pl,
15719525ae83SRussell King 				   struct ethtool_pauseparam *pause)
15729525ae83SRussell King {
15739525ae83SRussell King 	struct phylink_link_state *config = &pl->link_config;
15742e919bc4SRussell King 	bool manual_changed;
15752e919bc4SRussell King 	int pause_state;
15769525ae83SRussell King 
15778b874514SRussell King 	ASSERT_RTNL();
15789525ae83SRussell King 
15798cdfa256SRussell King 	if (pl->cur_link_an_mode == MLO_AN_FIXED)
15808cdfa256SRussell King 		return -EOPNOTSUPP;
15818cdfa256SRussell King 
15829525ae83SRussell King 	if (!phylink_test(pl->supported, Pause) &&
15839525ae83SRussell King 	    !phylink_test(pl->supported, Asym_Pause))
15849525ae83SRussell King 		return -EOPNOTSUPP;
15859525ae83SRussell King 
15869525ae83SRussell King 	if (!phylink_test(pl->supported, Asym_Pause) &&
15879525ae83SRussell King 	    !pause->autoneg && pause->rx_pause != pause->tx_pause)
15889525ae83SRussell King 		return -EINVAL;
15899525ae83SRussell King 
15902e919bc4SRussell King 	pause_state = 0;
15919525ae83SRussell King 	if (pause->autoneg)
15922e919bc4SRussell King 		pause_state |= MLO_PAUSE_AN;
15939525ae83SRussell King 	if (pause->rx_pause)
15942e919bc4SRussell King 		pause_state |= MLO_PAUSE_RX;
15959525ae83SRussell King 	if (pause->tx_pause)
15962e919bc4SRussell King 		pause_state |= MLO_PAUSE_TX;
15979525ae83SRussell King 
15982e919bc4SRussell King 	mutex_lock(&pl->state_mutex);
1599f904f15eSRussell King 	/*
1600f904f15eSRussell King 	 * See the comments for linkmode_set_pause(), wrt the deficiencies
1601f904f15eSRussell King 	 * with the current implementation.  A solution to this issue would
1602f904f15eSRussell King 	 * be:
1603f904f15eSRussell King 	 * ethtool  Local device
1604f904f15eSRussell King 	 *  rx  tx  Pause AsymDir
1605f904f15eSRussell King 	 *  0   0   0     0
1606f904f15eSRussell King 	 *  1   0   1     1
1607f904f15eSRussell King 	 *  0   1   0     1
1608f904f15eSRussell King 	 *  1   1   1     1
1609f904f15eSRussell King 	 * and then use the ethtool rx/tx enablement status to mask the
1610f904f15eSRussell King 	 * rx/tx pause resolution.
1611f904f15eSRussell King 	 */
1612f904f15eSRussell King 	linkmode_set_pause(config->advertising, pause->tx_pause,
1613f904f15eSRussell King 			   pause->rx_pause);
1614f904f15eSRussell King 
16152e919bc4SRussell King 	manual_changed = (config->pause ^ pause_state) & MLO_PAUSE_AN ||
16162e919bc4SRussell King 			 (!(pause_state & MLO_PAUSE_AN) &&
16172e919bc4SRussell King 			   (config->pause ^ pause_state) & MLO_PAUSE_TXRX_MASK);
16182e919bc4SRussell King 
16192e919bc4SRussell King 	config->pause = pause_state;
16202e919bc4SRussell King 
16211571e700SRussell King 	/* Update our in-band advertisement, triggering a renegotiation if
16221571e700SRussell King 	 * the advertisement changed.
16231571e700SRussell King 	 */
16241571e700SRussell King 	if (!pl->phydev)
16251571e700SRussell King 		phylink_change_inband_advert(pl);
1626c718af2dSRussell King 
1627c718af2dSRussell King 	mutex_unlock(&pl->state_mutex);
1628c718af2dSRussell King 
1629c718af2dSRussell King 	/* If we have a PHY, a change of the pause frame advertisement will
1630c718af2dSRussell King 	 * cause phylib to renegotiate (if AN is enabled) which will in turn
1631c718af2dSRussell King 	 * call our phylink_phy_change() and trigger a resolve.  Note that
1632c718af2dSRussell King 	 * we can't hold our state mutex while calling phy_set_asym_pause().
1633d9922c0eSRussell King 	 */
1634c718af2dSRussell King 	if (pl->phydev)
1635d9922c0eSRussell King 		phy_set_asym_pause(pl->phydev, pause->rx_pause,
1636d9922c0eSRussell King 				   pause->tx_pause);
16379525ae83SRussell King 
16382e919bc4SRussell King 	/* If the manual pause settings changed, make sure we trigger a
16392e919bc4SRussell King 	 * resolve to update their state; we can not guarantee that the
16402e919bc4SRussell King 	 * link will cycle.
16412e919bc4SRussell King 	 */
16422e919bc4SRussell King 	if (manual_changed) {
16432e919bc4SRussell King 		pl->mac_link_dropped = true;
16442e919bc4SRussell King 		phylink_run_resolve(pl);
16459525ae83SRussell King 	}
16469525ae83SRussell King 
16479525ae83SRussell King 	return 0;
16489525ae83SRussell King }
16499525ae83SRussell King EXPORT_SYMBOL_GPL(phylink_ethtool_set_pauseparam);
16509525ae83SRussell King 
16519525ae83SRussell King /**
1652*69280228SMauro Carvalho Chehab  * phylink_get_eee_err() - read the energy efficient ethernet error
16539525ae83SRussell King  *   counter
16549525ae83SRussell King  * @pl: a pointer to a &struct phylink returned from phylink_create().
16559525ae83SRussell King  *
16569525ae83SRussell King  * Read the Energy Efficient Ethernet error counter from the PHY associated
16579525ae83SRussell King  * with the phylink instance specified by @pl.
16589525ae83SRussell King  *
16599525ae83SRussell King  * Returns positive error counter value, or negative error code.
16609525ae83SRussell King  */
16619525ae83SRussell King int phylink_get_eee_err(struct phylink *pl)
16629525ae83SRussell King {
16639525ae83SRussell King 	int ret = 0;
16649525ae83SRussell King 
16658b874514SRussell King 	ASSERT_RTNL();
16669525ae83SRussell King 
16679525ae83SRussell King 	if (pl->phydev)
16689525ae83SRussell King 		ret = phy_get_eee_err(pl->phydev);
16699525ae83SRussell King 
16709525ae83SRussell King 	return ret;
16719525ae83SRussell King }
16729525ae83SRussell King EXPORT_SYMBOL_GPL(phylink_get_eee_err);
16739525ae83SRussell King 
16748796c892SRussell King /**
167586e58135SRussell King  * phylink_init_eee() - init and check the EEE features
167686e58135SRussell King  * @pl: a pointer to a &struct phylink returned from phylink_create()
167786e58135SRussell King  * @clk_stop_enable: allow PHY to stop receive clock
167886e58135SRussell King  *
167986e58135SRussell King  * Must be called either with RTNL held or within mac_link_up()
168086e58135SRussell King  */
168186e58135SRussell King int phylink_init_eee(struct phylink *pl, bool clk_stop_enable)
168286e58135SRussell King {
168386e58135SRussell King 	int ret = -EOPNOTSUPP;
168486e58135SRussell King 
168586e58135SRussell King 	if (pl->phydev)
168686e58135SRussell King 		ret = phy_init_eee(pl->phydev, clk_stop_enable);
168786e58135SRussell King 
168886e58135SRussell King 	return ret;
168986e58135SRussell King }
169086e58135SRussell King EXPORT_SYMBOL_GPL(phylink_init_eee);
169186e58135SRussell King 
169286e58135SRussell King /**
16938796c892SRussell King  * phylink_ethtool_get_eee() - read the energy efficient ethernet parameters
16948796c892SRussell King  * @pl: a pointer to a &struct phylink returned from phylink_create()
16958796c892SRussell King  * @eee: a pointer to a &struct ethtool_eee for the read parameters
16968796c892SRussell King  */
16979525ae83SRussell King int phylink_ethtool_get_eee(struct phylink *pl, struct ethtool_eee *eee)
16989525ae83SRussell King {
16999525ae83SRussell King 	int ret = -EOPNOTSUPP;
17009525ae83SRussell King 
17018b874514SRussell King 	ASSERT_RTNL();
17029525ae83SRussell King 
17039525ae83SRussell King 	if (pl->phydev)
17049525ae83SRussell King 		ret = phy_ethtool_get_eee(pl->phydev, eee);
17059525ae83SRussell King 
17069525ae83SRussell King 	return ret;
17079525ae83SRussell King }
17089525ae83SRussell King EXPORT_SYMBOL_GPL(phylink_ethtool_get_eee);
17099525ae83SRussell King 
17108796c892SRussell King /**
17118796c892SRussell King  * phylink_ethtool_set_eee() - set the energy efficient ethernet parameters
17128796c892SRussell King  * @pl: a pointer to a &struct phylink returned from phylink_create()
17138796c892SRussell King  * @eee: a pointer to a &struct ethtool_eee for the desired parameters
17148796c892SRussell King  */
17159525ae83SRussell King int phylink_ethtool_set_eee(struct phylink *pl, struct ethtool_eee *eee)
17169525ae83SRussell King {
17179525ae83SRussell King 	int ret = -EOPNOTSUPP;
17189525ae83SRussell King 
17198b874514SRussell King 	ASSERT_RTNL();
17209525ae83SRussell King 
17219525ae83SRussell King 	if (pl->phydev)
17229525ae83SRussell King 		ret = phy_ethtool_set_eee(pl->phydev, eee);
17239525ae83SRussell King 
17249525ae83SRussell King 	return ret;
17259525ae83SRussell King }
17269525ae83SRussell King EXPORT_SYMBOL_GPL(phylink_ethtool_set_eee);
17279525ae83SRussell King 
17289525ae83SRussell King /* This emulates MII registers for a fixed-mode phy operating as per the
17299525ae83SRussell King  * passed in state. "aneg" defines if we report negotiation is possible.
17309525ae83SRussell King  *
17319525ae83SRussell King  * FIXME: should deal with negotiation state too.
17329525ae83SRussell King  */
17337fdc455eSRussell King static int phylink_mii_emul_read(unsigned int reg,
17347fdc455eSRussell King 				 struct phylink_link_state *state)
17359525ae83SRussell King {
17369525ae83SRussell King 	struct fixed_phy_status fs;
17374e5aeb41SRussell King 	unsigned long *lpa = state->lp_advertising;
17389525ae83SRussell King 	int val;
17399525ae83SRussell King 
17409525ae83SRussell King 	fs.link = state->link;
17419525ae83SRussell King 	fs.speed = state->speed;
17429525ae83SRussell King 	fs.duplex = state->duplex;
17434e5aeb41SRussell King 	fs.pause = test_bit(ETHTOOL_LINK_MODE_Pause_BIT, lpa);
17444e5aeb41SRussell King 	fs.asym_pause = test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, lpa);
17459525ae83SRussell King 
17469525ae83SRussell King 	val = swphy_read_reg(reg, &fs);
17479525ae83SRussell King 	if (reg == MII_BMSR) {
17489525ae83SRussell King 		if (!state->an_complete)
17499525ae83SRussell King 			val &= ~BMSR_ANEGCOMPLETE;
17509525ae83SRussell King 	}
17519525ae83SRussell King 	return val;
17529525ae83SRussell King }
17539525ae83SRussell King 
1754ecbd87b8SRussell King static int phylink_phy_read(struct phylink *pl, unsigned int phy_id,
1755ecbd87b8SRussell King 			    unsigned int reg)
1756ecbd87b8SRussell King {
1757ecbd87b8SRussell King 	struct phy_device *phydev = pl->phydev;
1758ecbd87b8SRussell King 	int prtad, devad;
1759ecbd87b8SRussell King 
1760ecbd87b8SRussell King 	if (mdio_phy_id_is_c45(phy_id)) {
1761ecbd87b8SRussell King 		prtad = mdio_phy_id_prtad(phy_id);
1762ecbd87b8SRussell King 		devad = mdio_phy_id_devad(phy_id);
176390ce665cSRussell King 		devad = mdiobus_c45_addr(devad, reg);
1764ecbd87b8SRussell King 	} else if (phydev->is_c45) {
1765ecbd87b8SRussell King 		switch (reg) {
1766ecbd87b8SRussell King 		case MII_BMCR:
1767ecbd87b8SRussell King 		case MII_BMSR:
1768ecbd87b8SRussell King 		case MII_PHYSID1:
1769ecbd87b8SRussell King 		case MII_PHYSID2:
1770320ed3bfSRussell King 			devad = __ffs(phydev->c45_ids.mmds_present);
1771ecbd87b8SRussell King 			break;
1772ecbd87b8SRussell King 		case MII_ADVERTISE:
1773ecbd87b8SRussell King 		case MII_LPA:
1774320ed3bfSRussell King 			if (!(phydev->c45_ids.mmds_present & MDIO_DEVS_AN))
1775ecbd87b8SRussell King 				return -EINVAL;
1776ecbd87b8SRussell King 			devad = MDIO_MMD_AN;
1777ecbd87b8SRussell King 			if (reg == MII_ADVERTISE)
1778ecbd87b8SRussell King 				reg = MDIO_AN_ADVERTISE;
1779ecbd87b8SRussell King 			else
1780ecbd87b8SRussell King 				reg = MDIO_AN_LPA;
1781ecbd87b8SRussell King 			break;
1782ecbd87b8SRussell King 		default:
1783ecbd87b8SRussell King 			return -EINVAL;
1784ecbd87b8SRussell King 		}
1785ecbd87b8SRussell King 		prtad = phy_id;
178690ce665cSRussell King 		devad = mdiobus_c45_addr(devad, reg);
1787ecbd87b8SRussell King 	} else {
1788ecbd87b8SRussell King 		prtad = phy_id;
1789ecbd87b8SRussell King 		devad = reg;
1790ecbd87b8SRussell King 	}
1791ecbd87b8SRussell King 	return mdiobus_read(pl->phydev->mdio.bus, prtad, devad);
1792ecbd87b8SRussell King }
1793ecbd87b8SRussell King 
1794ecbd87b8SRussell King static int phylink_phy_write(struct phylink *pl, unsigned int phy_id,
1795ecbd87b8SRussell King 			     unsigned int reg, unsigned int val)
1796ecbd87b8SRussell King {
1797ecbd87b8SRussell King 	struct phy_device *phydev = pl->phydev;
1798ecbd87b8SRussell King 	int prtad, devad;
1799ecbd87b8SRussell King 
1800ecbd87b8SRussell King 	if (mdio_phy_id_is_c45(phy_id)) {
1801ecbd87b8SRussell King 		prtad = mdio_phy_id_prtad(phy_id);
1802ecbd87b8SRussell King 		devad = mdio_phy_id_devad(phy_id);
180390ce665cSRussell King 		devad = mdiobus_c45_addr(devad, reg);
1804ecbd87b8SRussell King 	} else if (phydev->is_c45) {
1805ecbd87b8SRussell King 		switch (reg) {
1806ecbd87b8SRussell King 		case MII_BMCR:
1807ecbd87b8SRussell King 		case MII_BMSR:
1808ecbd87b8SRussell King 		case MII_PHYSID1:
1809ecbd87b8SRussell King 		case MII_PHYSID2:
1810320ed3bfSRussell King 			devad = __ffs(phydev->c45_ids.mmds_present);
1811ecbd87b8SRussell King 			break;
1812ecbd87b8SRussell King 		case MII_ADVERTISE:
1813ecbd87b8SRussell King 		case MII_LPA:
1814320ed3bfSRussell King 			if (!(phydev->c45_ids.mmds_present & MDIO_DEVS_AN))
1815ecbd87b8SRussell King 				return -EINVAL;
1816ecbd87b8SRussell King 			devad = MDIO_MMD_AN;
1817ecbd87b8SRussell King 			if (reg == MII_ADVERTISE)
1818ecbd87b8SRussell King 				reg = MDIO_AN_ADVERTISE;
1819ecbd87b8SRussell King 			else
1820ecbd87b8SRussell King 				reg = MDIO_AN_LPA;
1821ecbd87b8SRussell King 			break;
1822ecbd87b8SRussell King 		default:
1823ecbd87b8SRussell King 			return -EINVAL;
1824ecbd87b8SRussell King 		}
1825ecbd87b8SRussell King 		prtad = phy_id;
182690ce665cSRussell King 		devad = mdiobus_c45_addr(devad, reg);
1827ecbd87b8SRussell King 	} else {
1828ecbd87b8SRussell King 		prtad = phy_id;
1829ecbd87b8SRussell King 		devad = reg;
1830ecbd87b8SRussell King 	}
1831ecbd87b8SRussell King 
1832ecbd87b8SRussell King 	return mdiobus_write(phydev->mdio.bus, prtad, devad, val);
1833ecbd87b8SRussell King }
1834ecbd87b8SRussell King 
18359525ae83SRussell King static int phylink_mii_read(struct phylink *pl, unsigned int phy_id,
18369525ae83SRussell King 			    unsigned int reg)
18379525ae83SRussell King {
18389525ae83SRussell King 	struct phylink_link_state state;
18399525ae83SRussell King 	int val = 0xffff;
18409525ae83SRussell King 
184124cf0e69SRussell King 	switch (pl->cur_link_an_mode) {
18429525ae83SRussell King 	case MLO_AN_FIXED:
18439525ae83SRussell King 		if (phy_id == 0) {
18449525ae83SRussell King 			phylink_get_fixed_state(pl, &state);
18457fdc455eSRussell King 			val = phylink_mii_emul_read(reg, &state);
18469525ae83SRussell King 		}
18479525ae83SRussell King 		break;
18489525ae83SRussell King 
18499525ae83SRussell King 	case MLO_AN_PHY:
18509525ae83SRussell King 		return -EOPNOTSUPP;
18519525ae83SRussell King 
185286a362c4SRussell King 	case MLO_AN_INBAND:
18539525ae83SRussell King 		if (phy_id == 0) {
1854d46b7e4fSRussell King 			phylink_mac_pcs_get_state(pl, &state);
18557fdc455eSRussell King 			val = phylink_mii_emul_read(reg, &state);
18569525ae83SRussell King 		}
18579525ae83SRussell King 		break;
18589525ae83SRussell King 	}
18599525ae83SRussell King 
18609525ae83SRussell King 	return val & 0xffff;
18619525ae83SRussell King }
18629525ae83SRussell King 
18639525ae83SRussell King static int phylink_mii_write(struct phylink *pl, unsigned int phy_id,
18649525ae83SRussell King 			     unsigned int reg, unsigned int val)
18659525ae83SRussell King {
186624cf0e69SRussell King 	switch (pl->cur_link_an_mode) {
18679525ae83SRussell King 	case MLO_AN_FIXED:
18689525ae83SRussell King 		break;
18699525ae83SRussell King 
18709525ae83SRussell King 	case MLO_AN_PHY:
18719525ae83SRussell King 		return -EOPNOTSUPP;
18729525ae83SRussell King 
187386a362c4SRussell King 	case MLO_AN_INBAND:
18749525ae83SRussell King 		break;
18759525ae83SRussell King 	}
18769525ae83SRussell King 
18779525ae83SRussell King 	return 0;
18789525ae83SRussell King }
18799525ae83SRussell King 
18808796c892SRussell King /**
18818796c892SRussell King  * phylink_mii_ioctl() - generic mii ioctl interface
18828796c892SRussell King  * @pl: a pointer to a &struct phylink returned from phylink_create()
18838796c892SRussell King  * @ifr: a pointer to a &struct ifreq for socket ioctls
18848796c892SRussell King  * @cmd: ioctl cmd to execute
18858796c892SRussell King  *
18868796c892SRussell King  * Perform the specified MII ioctl on the PHY attached to the phylink instance
18878796c892SRussell King  * specified by @pl. If no PHY is attached, emulate the presence of the PHY.
18888796c892SRussell King  *
18898796c892SRussell King  * Returns: zero on success or negative error code.
18908796c892SRussell King  *
18918796c892SRussell King  * %SIOCGMIIPHY:
18928796c892SRussell King  *  read register from the current PHY.
18938796c892SRussell King  * %SIOCGMIIREG:
18948796c892SRussell King  *  read register from the specified PHY.
18958796c892SRussell King  * %SIOCSMIIREG:
18968796c892SRussell King  *  set a register on the specified PHY.
18978796c892SRussell King  */
18989525ae83SRussell King int phylink_mii_ioctl(struct phylink *pl, struct ifreq *ifr, int cmd)
18999525ae83SRussell King {
1900ecbd87b8SRussell King 	struct mii_ioctl_data *mii = if_mii(ifr);
1901ecbd87b8SRussell King 	int  ret;
19029525ae83SRussell King 
19038b874514SRussell King 	ASSERT_RTNL();
19049525ae83SRussell King 
1905ecbd87b8SRussell King 	if (pl->phydev) {
190686a362c4SRussell King 		/* PHYs only exist for MLO_AN_PHY and SGMII */
19079525ae83SRussell King 		switch (cmd) {
19089525ae83SRussell King 		case SIOCGMIIPHY:
1909ecbd87b8SRussell King 			mii->phy_id = pl->phydev->mdio.addr;
1910df561f66SGustavo A. R. Silva 			fallthrough;
19119525ae83SRussell King 
19129525ae83SRussell King 		case SIOCGMIIREG:
1913ecbd87b8SRussell King 			ret = phylink_phy_read(pl, mii->phy_id, mii->reg_num);
1914ecbd87b8SRussell King 			if (ret >= 0) {
1915ecbd87b8SRussell King 				mii->val_out = ret;
19169525ae83SRussell King 				ret = 0;
19179525ae83SRussell King 			}
19189525ae83SRussell King 			break;
19199525ae83SRussell King 
19209525ae83SRussell King 		case SIOCSMIIREG:
1921ecbd87b8SRussell King 			ret = phylink_phy_write(pl, mii->phy_id, mii->reg_num,
1922ecbd87b8SRussell King 						mii->val_in);
1923ecbd87b8SRussell King 			break;
1924ecbd87b8SRussell King 
1925ecbd87b8SRussell King 		default:
1926ecbd87b8SRussell King 			ret = phy_mii_ioctl(pl->phydev, ifr, cmd);
1927ecbd87b8SRussell King 			break;
1928ecbd87b8SRussell King 		}
1929ecbd87b8SRussell King 	} else {
1930ecbd87b8SRussell King 		switch (cmd) {
1931ecbd87b8SRussell King 		case SIOCGMIIPHY:
1932ecbd87b8SRussell King 			mii->phy_id = 0;
1933df561f66SGustavo A. R. Silva 			fallthrough;
1934ecbd87b8SRussell King 
1935ecbd87b8SRussell King 		case SIOCGMIIREG:
1936ecbd87b8SRussell King 			ret = phylink_mii_read(pl, mii->phy_id, mii->reg_num);
1937ecbd87b8SRussell King 			if (ret >= 0) {
1938ecbd87b8SRussell King 				mii->val_out = ret;
1939ecbd87b8SRussell King 				ret = 0;
1940ecbd87b8SRussell King 			}
1941ecbd87b8SRussell King 			break;
1942ecbd87b8SRussell King 
1943ecbd87b8SRussell King 		case SIOCSMIIREG:
1944ecbd87b8SRussell King 			ret = phylink_mii_write(pl, mii->phy_id, mii->reg_num,
1945ecbd87b8SRussell King 						mii->val_in);
19469525ae83SRussell King 			break;
19479525ae83SRussell King 
19489525ae83SRussell King 		default:
19499525ae83SRussell King 			ret = -EOPNOTSUPP;
19509525ae83SRussell King 			break;
19519525ae83SRussell King 		}
1952ecbd87b8SRussell King 	}
19539525ae83SRussell King 
19549525ae83SRussell King 	return ret;
19559525ae83SRussell King }
19569525ae83SRussell King EXPORT_SYMBOL_GPL(phylink_mii_ioctl);
19579525ae83SRussell King 
1958c6d5d843SRussell King /**
1959c6d5d843SRussell King  * phylink_speed_down() - set the non-SFP PHY to lowest speed supported by both
1960c6d5d843SRussell King  *   link partners
1961c6d5d843SRussell King  * @pl: a pointer to a &struct phylink returned from phylink_create()
1962c6d5d843SRussell King  * @sync: perform action synchronously
1963c6d5d843SRussell King  *
1964c6d5d843SRussell King  * If we have a PHY that is not part of a SFP module, then set the speed
1965c6d5d843SRussell King  * as described in the phy_speed_down() function. Please see this function
1966c6d5d843SRussell King  * for a description of the @sync parameter.
1967c6d5d843SRussell King  *
1968c6d5d843SRussell King  * Returns zero if there is no PHY, otherwise as per phy_speed_down().
1969c6d5d843SRussell King  */
1970c6d5d843SRussell King int phylink_speed_down(struct phylink *pl, bool sync)
1971c6d5d843SRussell King {
1972c6d5d843SRussell King 	int ret = 0;
1973c6d5d843SRussell King 
1974c6d5d843SRussell King 	ASSERT_RTNL();
1975c6d5d843SRussell King 
1976c6d5d843SRussell King 	if (!pl->sfp_bus && pl->phydev)
1977c6d5d843SRussell King 		ret = phy_speed_down(pl->phydev, sync);
1978c6d5d843SRussell King 
1979c6d5d843SRussell King 	return ret;
1980c6d5d843SRussell King }
1981c6d5d843SRussell King EXPORT_SYMBOL_GPL(phylink_speed_down);
1982c6d5d843SRussell King 
1983c6d5d843SRussell King /**
1984c6d5d843SRussell King  * phylink_speed_up() - restore the advertised speeds prior to the call to
1985c6d5d843SRussell King  *   phylink_speed_down()
1986c6d5d843SRussell King  * @pl: a pointer to a &struct phylink returned from phylink_create()
1987c6d5d843SRussell King  *
1988c6d5d843SRussell King  * If we have a PHY that is not part of a SFP module, then restore the
1989c6d5d843SRussell King  * PHY speeds as per phy_speed_up().
1990c6d5d843SRussell King  *
1991c6d5d843SRussell King  * Returns zero if there is no PHY, otherwise as per phy_speed_up().
1992c6d5d843SRussell King  */
1993c6d5d843SRussell King int phylink_speed_up(struct phylink *pl)
1994c6d5d843SRussell King {
1995c6d5d843SRussell King 	int ret = 0;
1996c6d5d843SRussell King 
1997c6d5d843SRussell King 	ASSERT_RTNL();
1998c6d5d843SRussell King 
1999c6d5d843SRussell King 	if (!pl->sfp_bus && pl->phydev)
2000c6d5d843SRussell King 		ret = phy_speed_up(pl->phydev);
2001c6d5d843SRussell King 
2002c6d5d843SRussell King 	return ret;
2003c6d5d843SRussell King }
2004c6d5d843SRussell King EXPORT_SYMBOL_GPL(phylink_speed_up);
2005c6d5d843SRussell King 
2006320587e6SRussell King static void phylink_sfp_attach(void *upstream, struct sfp_bus *bus)
2007320587e6SRussell King {
2008320587e6SRussell King 	struct phylink *pl = upstream;
2009320587e6SRussell King 
2010320587e6SRussell King 	pl->netdev->sfp_bus = bus;
2011320587e6SRussell King }
2012320587e6SRussell King 
2013320587e6SRussell King static void phylink_sfp_detach(void *upstream, struct sfp_bus *bus)
2014320587e6SRussell King {
2015320587e6SRussell King 	struct phylink *pl = upstream;
2016320587e6SRussell King 
2017320587e6SRussell King 	pl->netdev->sfp_bus = NULL;
2018320587e6SRussell King }
2019320587e6SRussell King 
202052c95600SRussell King static int phylink_sfp_config(struct phylink *pl, u8 mode,
2021c0de2f47SRussell King 			      const unsigned long *supported,
2022c0de2f47SRussell King 			      const unsigned long *advertising)
2023ce0aa27fSRussell King {
202477316763SRussell King 	__ETHTOOL_DECLARE_LINK_MODE_MASK(support1);
2025c0de2f47SRussell King 	__ETHTOOL_DECLARE_LINK_MODE_MASK(support);
2026ce0aa27fSRussell King 	struct phylink_link_state config;
2027ce0aa27fSRussell King 	phy_interface_t iface;
2028ce0aa27fSRussell King 	bool changed;
2029c0de2f47SRussell King 	int ret;
2030ce0aa27fSRussell King 
2031c0de2f47SRussell King 	linkmode_copy(support, supported);
2032ce0aa27fSRussell King 
2033ce0aa27fSRussell King 	memset(&config, 0, sizeof(config));
2034c0de2f47SRussell King 	linkmode_copy(config.advertising, advertising);
2035a9c79364SRussell King 	config.interface = PHY_INTERFACE_MODE_NA;
2036ce0aa27fSRussell King 	config.speed = SPEED_UNKNOWN;
2037ce0aa27fSRussell King 	config.duplex = DUPLEX_UNKNOWN;
2038ce0aa27fSRussell King 	config.pause = MLO_PAUSE_AN;
2039ce0aa27fSRussell King 	config.an_enabled = pl->link_config.an_enabled;
2040ce0aa27fSRussell King 
2041ce0aa27fSRussell King 	/* Ignore errors if we're expecting a PHY to attach later */
2042ce0aa27fSRussell King 	ret = phylink_validate(pl, support, &config);
2043ce0aa27fSRussell King 	if (ret) {
204417091180SIoana Ciornei 		phylink_err(pl, "validation with support %*pb failed: %d\n",
2045a9c79364SRussell King 			    __ETHTOOL_LINK_MODE_MASK_NBITS, support, ret);
2046a9c79364SRussell King 		return ret;
2047a9c79364SRussell King 	}
2048a9c79364SRussell King 
2049a4516c70SRussell King 	iface = sfp_select_interface(pl->sfp_bus, config.advertising);
2050a9c79364SRussell King 	if (iface == PHY_INTERFACE_MODE_NA) {
205117091180SIoana Ciornei 		phylink_err(pl,
2052cc1122b0SColin Ian King 			    "selection of interface failed, advertisement %*pb\n",
2053a9c79364SRussell King 			    __ETHTOOL_LINK_MODE_MASK_NBITS, config.advertising);
2054a9c79364SRussell King 		return -EINVAL;
2055a9c79364SRussell King 	}
2056a9c79364SRussell King 
2057a9c79364SRussell King 	config.interface = iface;
2058c0de2f47SRussell King 	linkmode_copy(support1, support);
205977316763SRussell King 	ret = phylink_validate(pl, support1, &config);
2060a9c79364SRussell King 	if (ret) {
206117091180SIoana Ciornei 		phylink_err(pl, "validation of %s/%s with support %*pb failed: %d\n",
2062c0de2f47SRussell King 			    phylink_an_mode_str(mode),
2063444d3502SRussell King 			    phy_modes(config.interface),
2064ce0aa27fSRussell King 			    __ETHTOOL_LINK_MODE_MASK_NBITS, support, ret);
2065ce0aa27fSRussell King 		return ret;
2066ce0aa27fSRussell King 	}
2067ce0aa27fSRussell King 
206817091180SIoana Ciornei 	phylink_dbg(pl, "requesting link mode %s/%s with support %*pb\n",
2069c0de2f47SRussell King 		    phylink_an_mode_str(mode), phy_modes(config.interface),
2070ce0aa27fSRussell King 		    __ETHTOOL_LINK_MODE_MASK_NBITS, support);
2071ce0aa27fSRussell King 
207286a362c4SRussell King 	if (phy_interface_mode_is_8023z(iface) && pl->phydev)
2073ce0aa27fSRussell King 		return -EINVAL;
2074ce0aa27fSRussell King 
2075554032cdSRussell King 	changed = !linkmode_equal(pl->supported, support);
2076ce0aa27fSRussell King 	if (changed) {
2077ce0aa27fSRussell King 		linkmode_copy(pl->supported, support);
2078ce0aa27fSRussell King 		linkmode_copy(pl->link_config.advertising, config.advertising);
2079ce0aa27fSRussell King 	}
2080ce0aa27fSRussell King 
2081c0de2f47SRussell King 	if (pl->cur_link_an_mode != mode ||
2082ce0aa27fSRussell King 	    pl->link_config.interface != config.interface) {
2083ce0aa27fSRussell King 		pl->link_config.interface = config.interface;
2084c0de2f47SRussell King 		pl->cur_link_an_mode = mode;
2085ce0aa27fSRussell King 
2086ce0aa27fSRussell King 		changed = true;
2087ce0aa27fSRussell King 
208817091180SIoana Ciornei 		phylink_info(pl, "switched to %s/%s link mode\n",
2089c0de2f47SRussell King 			     phylink_an_mode_str(mode),
2090ce0aa27fSRussell King 			     phy_modes(config.interface));
2091ce0aa27fSRussell King 	}
2092ce0aa27fSRussell King 
209352c95600SRussell King 	pl->link_port = pl->sfp_port;
2094ce0aa27fSRussell King 
2095ce0aa27fSRussell King 	if (changed && !test_bit(PHYLINK_DISABLE_STOPPED,
2096ce0aa27fSRussell King 				 &pl->phylink_disable_state))
20974c0d6d3aSRussell King 		phylink_mac_initial_config(pl, false);
2098ce0aa27fSRussell King 
2099ce0aa27fSRussell King 	return ret;
2100ce0aa27fSRussell King }
2101ce0aa27fSRussell King 
2102c0de2f47SRussell King static int phylink_sfp_module_insert(void *upstream,
2103c0de2f47SRussell King 				     const struct sfp_eeprom_id *id)
2104c0de2f47SRussell King {
2105c0de2f47SRussell King 	struct phylink *pl = upstream;
210652c95600SRussell King 	unsigned long *support = pl->sfp_support;
2107c0de2f47SRussell King 
2108c0de2f47SRussell King 	ASSERT_RTNL();
2109c0de2f47SRussell King 
211052c95600SRussell King 	linkmode_zero(support);
2111c0de2f47SRussell King 	sfp_parse_support(pl->sfp_bus, id, support);
211252c95600SRussell King 	pl->sfp_port = sfp_parse_port(pl->sfp_bus, id, support);
2113c0de2f47SRussell King 
211452c95600SRussell King 	/* If this module may have a PHY connecting later, defer until later */
211552c95600SRussell King 	pl->sfp_may_have_phy = sfp_may_have_phy(pl->sfp_bus, id);
211652c95600SRussell King 	if (pl->sfp_may_have_phy)
211752c95600SRussell King 		return 0;
211852c95600SRussell King 
211952c95600SRussell King 	return phylink_sfp_config(pl, MLO_AN_INBAND, support, support);
2120c0de2f47SRussell King }
2121c0de2f47SRussell King 
21224882057aSRussell King static int phylink_sfp_module_start(void *upstream)
21234882057aSRussell King {
21244882057aSRussell King 	struct phylink *pl = upstream;
21254882057aSRussell King 
21264882057aSRussell King 	/* If this SFP module has a PHY, start the PHY now. */
212752c95600SRussell King 	if (pl->phydev) {
21284882057aSRussell King 		phy_start(pl->phydev);
21294882057aSRussell King 		return 0;
21304882057aSRussell King 	}
21314882057aSRussell King 
213252c95600SRussell King 	/* If the module may have a PHY but we didn't detect one we
213352c95600SRussell King 	 * need to configure the MAC here.
213452c95600SRussell King 	 */
213552c95600SRussell King 	if (!pl->sfp_may_have_phy)
213652c95600SRussell King 		return 0;
213752c95600SRussell King 
213852c95600SRussell King 	return phylink_sfp_config(pl, MLO_AN_INBAND,
213952c95600SRussell King 				  pl->sfp_support, pl->sfp_support);
214052c95600SRussell King }
214152c95600SRussell King 
21424882057aSRussell King static void phylink_sfp_module_stop(void *upstream)
21434882057aSRussell King {
21444882057aSRussell King 	struct phylink *pl = upstream;
21454882057aSRussell King 
21464882057aSRussell King 	/* If this SFP module has a PHY, stop it. */
21474882057aSRussell King 	if (pl->phydev)
21484882057aSRussell King 		phy_stop(pl->phydev);
21494882057aSRussell King }
21504882057aSRussell King 
2151ce0aa27fSRussell King static void phylink_sfp_link_down(void *upstream)
2152ce0aa27fSRussell King {
2153ce0aa27fSRussell King 	struct phylink *pl = upstream;
2154ce0aa27fSRussell King 
21558b874514SRussell King 	ASSERT_RTNL();
2156ce0aa27fSRussell King 
215787454b6eSRussell King 	phylink_run_resolve_and_disable(pl, PHYLINK_DISABLE_LINK);
2158ce0aa27fSRussell King }
2159ce0aa27fSRussell King 
2160ce0aa27fSRussell King static void phylink_sfp_link_up(void *upstream)
2161ce0aa27fSRussell King {
2162ce0aa27fSRussell King 	struct phylink *pl = upstream;
2163ce0aa27fSRussell King 
21648b874514SRussell King 	ASSERT_RTNL();
2165ce0aa27fSRussell King 
2166ce0aa27fSRussell King 	clear_bit(PHYLINK_DISABLE_LINK, &pl->phylink_disable_state);
2167ce0aa27fSRussell King 	phylink_run_resolve(pl);
2168ce0aa27fSRussell King }
2169ce0aa27fSRussell King 
21707adb5b21SRussell King /* The Broadcom BCM84881 in the Methode DM7052 is unable to provide a SGMII
21717adb5b21SRussell King  * or 802.3z control word, so inband will not work.
21727adb5b21SRussell King  */
21737adb5b21SRussell King static bool phylink_phy_no_inband(struct phy_device *phy)
21747adb5b21SRussell King {
21757adb5b21SRussell King 	return phy->is_c45 &&
21767adb5b21SRussell King 		(phy->c45_ids.device_ids[1] & 0xfffffff0) == 0xae025150;
21777adb5b21SRussell King }
21787adb5b21SRussell King 
2179ce0aa27fSRussell King static int phylink_sfp_connect_phy(void *upstream, struct phy_device *phy)
2180ce0aa27fSRussell King {
21817e418375SBaruch Siach 	struct phylink *pl = upstream;
218252c95600SRussell King 	phy_interface_t interface;
21837adb5b21SRussell King 	u8 mode;
2184938d44c2SRussell King 	int ret;
21857e418375SBaruch Siach 
218652c95600SRussell King 	/*
218752c95600SRussell King 	 * This is the new way of dealing with flow control for PHYs,
218852c95600SRussell King 	 * as described by Timur Tabi in commit 529ed1275263 ("net: phy:
218952c95600SRussell King 	 * phy drivers should not set SUPPORTED_[Asym_]Pause") except
219052c95600SRussell King 	 * using our validate call to the MAC, we rely upon the MAC
219152c95600SRussell King 	 * clearing the bits from both supported and advertising fields.
219252c95600SRussell King 	 */
219352c95600SRussell King 	phy_support_asym_pause(phy);
219452c95600SRussell King 
21957adb5b21SRussell King 	if (phylink_phy_no_inband(phy))
21967adb5b21SRussell King 		mode = MLO_AN_PHY;
21977adb5b21SRussell King 	else
21987adb5b21SRussell King 		mode = MLO_AN_INBAND;
21997adb5b21SRussell King 
220052c95600SRussell King 	/* Do the initial configuration */
22017adb5b21SRussell King 	ret = phylink_sfp_config(pl, mode, phy->supported, phy->advertising);
220252c95600SRussell King 	if (ret < 0)
220352c95600SRussell King 		return ret;
220452c95600SRussell King 
220552c95600SRussell King 	interface = pl->link_config.interface;
220652c95600SRussell King 	ret = phylink_attach_phy(pl, phy, interface);
2207938d44c2SRussell King 	if (ret < 0)
2208938d44c2SRussell King 		return ret;
2209938d44c2SRussell King 
2210e45d1f52SRussell King 	ret = phylink_bringup_phy(pl, phy, interface);
2211938d44c2SRussell King 	if (ret)
2212938d44c2SRussell King 		phy_detach(phy);
2213938d44c2SRussell King 
2214938d44c2SRussell King 	return ret;
2215ce0aa27fSRussell King }
2216ce0aa27fSRussell King 
2217ce0aa27fSRussell King static void phylink_sfp_disconnect_phy(void *upstream)
2218ce0aa27fSRussell King {
2219ce0aa27fSRussell King 	phylink_disconnect_phy(upstream);
2220ce0aa27fSRussell King }
2221ce0aa27fSRussell King 
2222ce0aa27fSRussell King static const struct sfp_upstream_ops sfp_phylink_ops = {
2223320587e6SRussell King 	.attach = phylink_sfp_attach,
2224320587e6SRussell King 	.detach = phylink_sfp_detach,
2225ce0aa27fSRussell King 	.module_insert = phylink_sfp_module_insert,
22264882057aSRussell King 	.module_start = phylink_sfp_module_start,
22274882057aSRussell King 	.module_stop = phylink_sfp_module_stop,
2228ce0aa27fSRussell King 	.link_up = phylink_sfp_link_up,
2229ce0aa27fSRussell King 	.link_down = phylink_sfp_link_down,
2230ce0aa27fSRussell King 	.connect_phy = phylink_sfp_connect_phy,
2231ce0aa27fSRussell King 	.disconnect_phy = phylink_sfp_disconnect_phy,
2232ce0aa27fSRussell King };
2233ce0aa27fSRussell King 
2234624c0f02SRussell King /* Helpers for MAC drivers */
2235624c0f02SRussell King 
2236624c0f02SRussell King /**
2237624c0f02SRussell King  * phylink_helper_basex_speed() - 1000BaseX/2500BaseX helper
2238624c0f02SRussell King  * @state: a pointer to a &struct phylink_link_state
2239624c0f02SRussell King  *
2240624c0f02SRussell King  * Inspect the interface mode, advertising mask or forced speed and
2241624c0f02SRussell King  * decide whether to run at 2.5Gbit or 1Gbit appropriately, switching
2242624c0f02SRussell King  * the interface mode to suit.  @state->interface is appropriately
2243624c0f02SRussell King  * updated, and the advertising mask has the "other" baseX_Full flag
2244624c0f02SRussell King  * cleared.
2245624c0f02SRussell King  */
2246624c0f02SRussell King void phylink_helper_basex_speed(struct phylink_link_state *state)
2247624c0f02SRussell King {
2248624c0f02SRussell King 	if (phy_interface_mode_is_8023z(state->interface)) {
2249624c0f02SRussell King 		bool want_2500 = state->an_enabled ?
2250624c0f02SRussell King 			phylink_test(state->advertising, 2500baseX_Full) :
2251624c0f02SRussell King 			state->speed == SPEED_2500;
2252624c0f02SRussell King 
2253624c0f02SRussell King 		if (want_2500) {
2254624c0f02SRussell King 			phylink_clear(state->advertising, 1000baseX_Full);
2255624c0f02SRussell King 			state->interface = PHY_INTERFACE_MODE_2500BASEX;
2256624c0f02SRussell King 		} else {
2257624c0f02SRussell King 			phylink_clear(state->advertising, 2500baseX_Full);
2258624c0f02SRussell King 			state->interface = PHY_INTERFACE_MODE_1000BASEX;
2259624c0f02SRussell King 		}
2260624c0f02SRussell King 	}
2261624c0f02SRussell King }
2262624c0f02SRussell King EXPORT_SYMBOL_GPL(phylink_helper_basex_speed);
2263624c0f02SRussell King 
226474db1c18SRussell King static void phylink_decode_c37_word(struct phylink_link_state *state,
226574db1c18SRussell King 				    uint16_t config_reg, int speed)
226674db1c18SRussell King {
226774db1c18SRussell King 	bool tx_pause, rx_pause;
226874db1c18SRussell King 	int fd_bit;
226974db1c18SRussell King 
227074db1c18SRussell King 	if (speed == SPEED_2500)
227174db1c18SRussell King 		fd_bit = ETHTOOL_LINK_MODE_2500baseX_Full_BIT;
227274db1c18SRussell King 	else
227374db1c18SRussell King 		fd_bit = ETHTOOL_LINK_MODE_1000baseX_Full_BIT;
227474db1c18SRussell King 
227574db1c18SRussell King 	mii_lpa_mod_linkmode_x(state->lp_advertising, config_reg, fd_bit);
227674db1c18SRussell King 
227774db1c18SRussell King 	if (linkmode_test_bit(fd_bit, state->advertising) &&
227874db1c18SRussell King 	    linkmode_test_bit(fd_bit, state->lp_advertising)) {
227974db1c18SRussell King 		state->speed = speed;
228074db1c18SRussell King 		state->duplex = DUPLEX_FULL;
228174db1c18SRussell King 	} else {
228274db1c18SRussell King 		/* negotiation failure */
228374db1c18SRussell King 		state->link = false;
228474db1c18SRussell King 	}
228574db1c18SRussell King 
228674db1c18SRussell King 	linkmode_resolve_pause(state->advertising, state->lp_advertising,
228774db1c18SRussell King 			       &tx_pause, &rx_pause);
228874db1c18SRussell King 
228974db1c18SRussell King 	if (tx_pause)
229074db1c18SRussell King 		state->pause |= MLO_PAUSE_TX;
229174db1c18SRussell King 	if (rx_pause)
229274db1c18SRussell King 		state->pause |= MLO_PAUSE_RX;
229374db1c18SRussell King }
229474db1c18SRussell King 
229574db1c18SRussell King static void phylink_decode_sgmii_word(struct phylink_link_state *state,
229674db1c18SRussell King 				      uint16_t config_reg)
229774db1c18SRussell King {
229874db1c18SRussell King 	if (!(config_reg & LPA_SGMII_LINK)) {
229974db1c18SRussell King 		state->link = false;
230074db1c18SRussell King 		return;
230174db1c18SRussell King 	}
230274db1c18SRussell King 
230374db1c18SRussell King 	switch (config_reg & LPA_SGMII_SPD_MASK) {
230474db1c18SRussell King 	case LPA_SGMII_10:
230574db1c18SRussell King 		state->speed = SPEED_10;
230674db1c18SRussell King 		break;
230774db1c18SRussell King 	case LPA_SGMII_100:
230874db1c18SRussell King 		state->speed = SPEED_100;
230974db1c18SRussell King 		break;
231074db1c18SRussell King 	case LPA_SGMII_1000:
231174db1c18SRussell King 		state->speed = SPEED_1000;
231274db1c18SRussell King 		break;
231374db1c18SRussell King 	default:
231474db1c18SRussell King 		state->link = false;
231574db1c18SRussell King 		return;
231674db1c18SRussell King 	}
231774db1c18SRussell King 	if (config_reg & LPA_SGMII_FULL_DUPLEX)
231874db1c18SRussell King 		state->duplex = DUPLEX_FULL;
231974db1c18SRussell King 	else
232074db1c18SRussell King 		state->duplex = DUPLEX_HALF;
232174db1c18SRussell King }
232274db1c18SRussell King 
232374db1c18SRussell King /**
2324afd62209SIoana Ciornei  * phylink_decode_usxgmii_word() - decode the USXGMII word from a MAC PCS
2325afd62209SIoana Ciornei  * @state: a pointer to a struct phylink_link_state.
2326afd62209SIoana Ciornei  * @lpa: a 16 bit value which stores the USXGMII auto-negotiation word
2327afd62209SIoana Ciornei  *
2328afd62209SIoana Ciornei  * Helper for MAC PCS supporting the USXGMII protocol and the auto-negotiation
2329afd62209SIoana Ciornei  * code word.  Decode the USXGMII code word and populate the corresponding fields
2330afd62209SIoana Ciornei  * (speed, duplex) into the phylink_link_state structure.
2331afd62209SIoana Ciornei  */
2332afd62209SIoana Ciornei void phylink_decode_usxgmii_word(struct phylink_link_state *state,
2333afd62209SIoana Ciornei 				 uint16_t lpa)
2334afd62209SIoana Ciornei {
2335afd62209SIoana Ciornei 	switch (lpa & MDIO_USXGMII_SPD_MASK) {
2336afd62209SIoana Ciornei 	case MDIO_USXGMII_10:
2337afd62209SIoana Ciornei 		state->speed = SPEED_10;
2338afd62209SIoana Ciornei 		break;
2339afd62209SIoana Ciornei 	case MDIO_USXGMII_100:
2340afd62209SIoana Ciornei 		state->speed = SPEED_100;
2341afd62209SIoana Ciornei 		break;
2342afd62209SIoana Ciornei 	case MDIO_USXGMII_1000:
2343afd62209SIoana Ciornei 		state->speed = SPEED_1000;
2344afd62209SIoana Ciornei 		break;
2345afd62209SIoana Ciornei 	case MDIO_USXGMII_2500:
2346afd62209SIoana Ciornei 		state->speed = SPEED_2500;
2347afd62209SIoana Ciornei 		break;
2348afd62209SIoana Ciornei 	case MDIO_USXGMII_5000:
2349afd62209SIoana Ciornei 		state->speed = SPEED_5000;
2350afd62209SIoana Ciornei 		break;
2351afd62209SIoana Ciornei 	case MDIO_USXGMII_10G:
2352afd62209SIoana Ciornei 		state->speed = SPEED_10000;
2353afd62209SIoana Ciornei 		break;
2354afd62209SIoana Ciornei 	default:
2355afd62209SIoana Ciornei 		state->link = false;
2356afd62209SIoana Ciornei 		return;
2357afd62209SIoana Ciornei 	}
2358afd62209SIoana Ciornei 
2359afd62209SIoana Ciornei 	if (lpa & MDIO_USXGMII_FULL_DUPLEX)
2360afd62209SIoana Ciornei 		state->duplex = DUPLEX_FULL;
2361afd62209SIoana Ciornei 	else
2362afd62209SIoana Ciornei 		state->duplex = DUPLEX_HALF;
2363afd62209SIoana Ciornei }
2364afd62209SIoana Ciornei EXPORT_SYMBOL_GPL(phylink_decode_usxgmii_word);
2365afd62209SIoana Ciornei 
2366afd62209SIoana Ciornei /**
236774db1c18SRussell King  * phylink_mii_c22_pcs_get_state() - read the MAC PCS state
236874db1c18SRussell King  * @pcs: a pointer to a &struct mdio_device.
236974db1c18SRussell King  * @state: a pointer to a &struct phylink_link_state.
237074db1c18SRussell King  *
237174db1c18SRussell King  * Helper for MAC PCS supporting the 802.3 clause 22 register set for
237274db1c18SRussell King  * clause 37 negotiation and/or SGMII control.
237374db1c18SRussell King  *
237474db1c18SRussell King  * Read the MAC PCS state from the MII device configured in @config and
237574db1c18SRussell King  * parse the Clause 37 or Cisco SGMII link partner negotiation word into
237674db1c18SRussell King  * the phylink @state structure. This is suitable to be directly plugged
237774db1c18SRussell King  * into the mac_pcs_get_state() member of the struct phylink_mac_ops
237874db1c18SRussell King  * structure.
237974db1c18SRussell King  */
238074db1c18SRussell King void phylink_mii_c22_pcs_get_state(struct mdio_device *pcs,
238174db1c18SRussell King 				   struct phylink_link_state *state)
238274db1c18SRussell King {
238374db1c18SRussell King 	struct mii_bus *bus = pcs->bus;
238474db1c18SRussell King 	int addr = pcs->addr;
238574db1c18SRussell King 	int bmsr, lpa;
238674db1c18SRussell King 
238774db1c18SRussell King 	bmsr = mdiobus_read(bus, addr, MII_BMSR);
238874db1c18SRussell King 	lpa = mdiobus_read(bus, addr, MII_LPA);
238974db1c18SRussell King 	if (bmsr < 0 || lpa < 0) {
239074db1c18SRussell King 		state->link = false;
239174db1c18SRussell King 		return;
239274db1c18SRussell King 	}
239374db1c18SRussell King 
239474db1c18SRussell King 	state->link = !!(bmsr & BMSR_LSTATUS);
239574db1c18SRussell King 	state->an_complete = !!(bmsr & BMSR_ANEGCOMPLETE);
239674db1c18SRussell King 	if (!state->link)
239774db1c18SRussell King 		return;
239874db1c18SRussell King 
239974db1c18SRussell King 	switch (state->interface) {
240074db1c18SRussell King 	case PHY_INTERFACE_MODE_1000BASEX:
240174db1c18SRussell King 		phylink_decode_c37_word(state, lpa, SPEED_1000);
240274db1c18SRussell King 		break;
240374db1c18SRussell King 
240474db1c18SRussell King 	case PHY_INTERFACE_MODE_2500BASEX:
240574db1c18SRussell King 		phylink_decode_c37_word(state, lpa, SPEED_2500);
240674db1c18SRussell King 		break;
240774db1c18SRussell King 
240874db1c18SRussell King 	case PHY_INTERFACE_MODE_SGMII:
240929f02ee4SIoana Ciornei 	case PHY_INTERFACE_MODE_QSGMII:
241074db1c18SRussell King 		phylink_decode_sgmii_word(state, lpa);
241174db1c18SRussell King 		break;
241274db1c18SRussell King 
241374db1c18SRussell King 	default:
241474db1c18SRussell King 		state->link = false;
241574db1c18SRussell King 		break;
241674db1c18SRussell King 	}
241774db1c18SRussell King }
241874db1c18SRussell King EXPORT_SYMBOL_GPL(phylink_mii_c22_pcs_get_state);
241974db1c18SRussell King 
242074db1c18SRussell King /**
242174db1c18SRussell King  * phylink_mii_c22_pcs_set_advertisement() - configure the clause 37 PCS
242274db1c18SRussell King  *	advertisement
242374db1c18SRussell King  * @pcs: a pointer to a &struct mdio_device.
24240bd27406SRussell King  * @interface: the PHY interface mode being configured
24250bd27406SRussell King  * @advertising: the ethtool advertisement mask
242674db1c18SRussell King  *
242774db1c18SRussell King  * Helper for MAC PCS supporting the 802.3 clause 22 register set for
242874db1c18SRussell King  * clause 37 negotiation and/or SGMII control.
242974db1c18SRussell King  *
243074db1c18SRussell King  * Configure the clause 37 PCS advertisement as specified by @state. This
243174db1c18SRussell King  * does not trigger a renegotiation; phylink will do that via the
243274db1c18SRussell King  * mac_an_restart() method of the struct phylink_mac_ops structure.
243374db1c18SRussell King  *
243474db1c18SRussell King  * Returns negative error code on failure to configure the advertisement,
243574db1c18SRussell King  * zero if no change has been made, or one if the advertisement has changed.
243674db1c18SRussell King  */
243774db1c18SRussell King int phylink_mii_c22_pcs_set_advertisement(struct mdio_device *pcs,
24380bd27406SRussell King 					  phy_interface_t interface,
24390bd27406SRussell King 					  const unsigned long *advertising)
244074db1c18SRussell King {
244174db1c18SRussell King 	struct mii_bus *bus = pcs->bus;
244274db1c18SRussell King 	int addr = pcs->addr;
244374db1c18SRussell King 	int val, ret;
244474db1c18SRussell King 	u16 adv;
244574db1c18SRussell King 
24460bd27406SRussell King 	switch (interface) {
244774db1c18SRussell King 	case PHY_INTERFACE_MODE_1000BASEX:
244874db1c18SRussell King 	case PHY_INTERFACE_MODE_2500BASEX:
244974db1c18SRussell King 		adv = ADVERTISE_1000XFULL;
245074db1c18SRussell King 		if (linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT,
24510bd27406SRussell King 				      advertising))
245274db1c18SRussell King 			adv |= ADVERTISE_1000XPAUSE;
245374db1c18SRussell King 		if (linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
24540bd27406SRussell King 				      advertising))
245574db1c18SRussell King 			adv |= ADVERTISE_1000XPSE_ASYM;
245674db1c18SRussell King 
245774db1c18SRussell King 		val = mdiobus_read(bus, addr, MII_ADVERTISE);
245874db1c18SRussell King 		if (val < 0)
245974db1c18SRussell King 			return val;
246074db1c18SRussell King 
246174db1c18SRussell King 		if (val == adv)
246274db1c18SRussell King 			return 0;
246374db1c18SRussell King 
246474db1c18SRussell King 		ret = mdiobus_write(bus, addr, MII_ADVERTISE, adv);
246574db1c18SRussell King 		if (ret < 0)
246674db1c18SRussell King 			return ret;
246774db1c18SRussell King 
246874db1c18SRussell King 		return 1;
246974db1c18SRussell King 
247074db1c18SRussell King 	case PHY_INTERFACE_MODE_SGMII:
247174db1c18SRussell King 		val = mdiobus_read(bus, addr, MII_ADVERTISE);
247274db1c18SRussell King 		if (val < 0)
247374db1c18SRussell King 			return val;
247474db1c18SRussell King 
247574db1c18SRussell King 		if (val == 0x0001)
247674db1c18SRussell King 			return 0;
247774db1c18SRussell King 
247874db1c18SRussell King 		ret = mdiobus_write(bus, addr, MII_ADVERTISE, 0x0001);
247974db1c18SRussell King 		if (ret < 0)
248074db1c18SRussell King 			return ret;
248174db1c18SRussell King 
248274db1c18SRussell King 		return 1;
248374db1c18SRussell King 
248474db1c18SRussell King 	default:
248574db1c18SRussell King 		/* Nothing to do for other modes */
248674db1c18SRussell King 		return 0;
248774db1c18SRussell King 	}
248874db1c18SRussell King }
248974db1c18SRussell King EXPORT_SYMBOL_GPL(phylink_mii_c22_pcs_set_advertisement);
249074db1c18SRussell King 
249174db1c18SRussell King /**
249293eaceb0SRussell King  * phylink_mii_c22_pcs_config() - configure clause 22 PCS
249393eaceb0SRussell King  * @pcs: a pointer to a &struct mdio_device.
249493eaceb0SRussell King  * @mode: link autonegotiation mode
249593eaceb0SRussell King  * @interface: the PHY interface mode being configured
249693eaceb0SRussell King  * @advertising: the ethtool advertisement mask
249793eaceb0SRussell King  *
249893eaceb0SRussell King  * Configure a Clause 22 PCS PHY with the appropriate negotiation
249993eaceb0SRussell King  * parameters for the @mode, @interface and @advertising parameters.
250093eaceb0SRussell King  * Returns negative error number on failure, zero if the advertisement
250193eaceb0SRussell King  * has not changed, or positive if there is a change.
250293eaceb0SRussell King  */
250393eaceb0SRussell King int phylink_mii_c22_pcs_config(struct mdio_device *pcs, unsigned int mode,
250493eaceb0SRussell King 			       phy_interface_t interface,
250593eaceb0SRussell King 			       const unsigned long *advertising)
250693eaceb0SRussell King {
250793eaceb0SRussell King 	bool changed;
250893eaceb0SRussell King 	u16 bmcr;
250993eaceb0SRussell King 	int ret;
251093eaceb0SRussell King 
251193eaceb0SRussell King 	ret = phylink_mii_c22_pcs_set_advertisement(pcs, interface,
251293eaceb0SRussell King 						    advertising);
251393eaceb0SRussell King 	if (ret < 0)
251493eaceb0SRussell King 		return ret;
251593eaceb0SRussell King 
251693eaceb0SRussell King 	changed = ret > 0;
251793eaceb0SRussell King 
2518cd29296fSRobert Hancock 	/* Ensure ISOLATE bit is disabled */
251993eaceb0SRussell King 	bmcr = mode == MLO_AN_INBAND ? BMCR_ANENABLE : 0;
252093eaceb0SRussell King 	ret = mdiobus_modify(pcs->bus, pcs->addr, MII_BMCR,
2521cd29296fSRobert Hancock 			     BMCR_ANENABLE | BMCR_ISOLATE, bmcr);
252293eaceb0SRussell King 	if (ret < 0)
252393eaceb0SRussell King 		return ret;
252493eaceb0SRussell King 
252593eaceb0SRussell King 	return changed ? 1 : 0;
252693eaceb0SRussell King }
252793eaceb0SRussell King EXPORT_SYMBOL_GPL(phylink_mii_c22_pcs_config);
252893eaceb0SRussell King 
252993eaceb0SRussell King /**
253074db1c18SRussell King  * phylink_mii_c22_pcs_an_restart() - restart 802.3z autonegotiation
253174db1c18SRussell King  * @pcs: a pointer to a &struct mdio_device.
253274db1c18SRussell King  *
253374db1c18SRussell King  * Helper for MAC PCS supporting the 802.3 clause 22 register set for
253474db1c18SRussell King  * clause 37 negotiation.
253574db1c18SRussell King  *
253674db1c18SRussell King  * Restart the clause 37 negotiation with the link partner. This is
253774db1c18SRussell King  * suitable to be directly plugged into the mac_pcs_get_state() member
253874db1c18SRussell King  * of the struct phylink_mac_ops structure.
253974db1c18SRussell King  */
254074db1c18SRussell King void phylink_mii_c22_pcs_an_restart(struct mdio_device *pcs)
254174db1c18SRussell King {
254274db1c18SRussell King 	struct mii_bus *bus = pcs->bus;
254374db1c18SRussell King 	int val, addr = pcs->addr;
254474db1c18SRussell King 
254574db1c18SRussell King 	val = mdiobus_read(bus, addr, MII_BMCR);
254674db1c18SRussell King 	if (val >= 0) {
254774db1c18SRussell King 		val |= BMCR_ANRESTART;
254874db1c18SRussell King 
254974db1c18SRussell King 		mdiobus_write(bus, addr, MII_BMCR, val);
255074db1c18SRussell King 	}
255174db1c18SRussell King }
255274db1c18SRussell King EXPORT_SYMBOL_GPL(phylink_mii_c22_pcs_an_restart);
255374db1c18SRussell King 
2554b8679ef8SRussell King void phylink_mii_c45_pcs_get_state(struct mdio_device *pcs,
2555b8679ef8SRussell King 				   struct phylink_link_state *state)
2556b8679ef8SRussell King {
2557b8679ef8SRussell King 	struct mii_bus *bus = pcs->bus;
2558b8679ef8SRussell King 	int addr = pcs->addr;
2559b8679ef8SRussell King 	int stat;
2560b8679ef8SRussell King 
256190ce665cSRussell King 	stat = mdiobus_c45_read(bus, addr, MDIO_MMD_PCS, MDIO_STAT1);
2562b8679ef8SRussell King 	if (stat < 0) {
2563b8679ef8SRussell King 		state->link = false;
2564b8679ef8SRussell King 		return;
2565b8679ef8SRussell King 	}
2566b8679ef8SRussell King 
2567b8679ef8SRussell King 	state->link = !!(stat & MDIO_STAT1_LSTATUS);
2568b8679ef8SRussell King 	if (!state->link)
2569b8679ef8SRussell King 		return;
2570b8679ef8SRussell King 
2571b8679ef8SRussell King 	switch (state->interface) {
2572b8679ef8SRussell King 	case PHY_INTERFACE_MODE_10GBASER:
2573b8679ef8SRussell King 		state->speed = SPEED_10000;
2574b8679ef8SRussell King 		state->duplex = DUPLEX_FULL;
2575b8679ef8SRussell King 		break;
2576b8679ef8SRussell King 
2577b8679ef8SRussell King 	default:
2578b8679ef8SRussell King 		break;
2579b8679ef8SRussell King 	}
2580b8679ef8SRussell King }
2581b8679ef8SRussell King EXPORT_SYMBOL_GPL(phylink_mii_c45_pcs_get_state);
2582b8679ef8SRussell King 
25835f857575SAndrew Lunn MODULE_LICENSE("GPL v2");
2584