diff options
Diffstat (limited to 'drivers/net/phy/phylink.c')
| -rw-r--r-- | drivers/net/phy/phylink.c | 74 | 
1 files changed, 68 insertions, 6 deletions
diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index 0faa3d97e06b..c7f867b361dd 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -127,6 +127,9 @@ do {									\  #endif  static const phy_interface_t phylink_sfp_interface_preference[] = { +	PHY_INTERFACE_MODE_100GBASEP, +	PHY_INTERFACE_MODE_50GBASER, +	PHY_INTERFACE_MODE_LAUI,  	PHY_INTERFACE_MODE_25GBASER,  	PHY_INTERFACE_MODE_USXGMII,  	PHY_INTERFACE_MODE_10GBASER, @@ -234,6 +237,7 @@ static int phylink_interface_max_speed(phy_interface_t interface)  	case PHY_INTERFACE_MODE_SMII:  	case PHY_INTERFACE_MODE_REVMII:  	case PHY_INTERFACE_MODE_MII: +	case PHY_INTERFACE_MODE_MIILITE:  		return SPEED_100;  	case PHY_INTERFACE_MODE_TBI: @@ -274,6 +278,13 @@ static int phylink_interface_max_speed(phy_interface_t interface)  	case PHY_INTERFACE_MODE_XLGMII:  		return SPEED_40000; +	case PHY_INTERFACE_MODE_50GBASER: +	case PHY_INTERFACE_MODE_LAUI: +		return SPEED_50000; + +	case PHY_INTERFACE_MODE_100GBASEP: +		return SPEED_100000; +  	case PHY_INTERFACE_MODE_INTERNAL:  	case PHY_INTERFACE_MODE_NA:  	case PHY_INTERFACE_MODE_MAX: @@ -798,6 +809,9 @@ static int phylink_parse_mode(struct phylink *pl,  		case PHY_INTERFACE_MODE_10GKR:  		case PHY_INTERFACE_MODE_10GBASER:  		case PHY_INTERFACE_MODE_XLGMII: +		case PHY_INTERFACE_MODE_50GBASER: +		case PHY_INTERFACE_MODE_LAUI: +		case PHY_INTERFACE_MODE_100GBASEP:  			caps = ~(MAC_SYM_PAUSE | MAC_ASYM_PAUSE);  			caps = phylink_get_capabilities(pl->link_config.interface, caps,  							RATE_MATCH_NONE); @@ -2696,6 +2710,39 @@ static phy_interface_t phylink_sfp_select_interface(struct phylink *pl,  	return interface;  } +static phy_interface_t phylink_sfp_select_interface_speed(struct phylink *pl, +							  u32 speed) +{ +	phy_interface_t best_interface = PHY_INTERFACE_MODE_NA; +	phy_interface_t interface; +	u32 max_speed; +	int i; + +	for (i = 0; i < ARRAY_SIZE(phylink_sfp_interface_preference); i++) { +		interface = phylink_sfp_interface_preference[i]; +		if (!test_bit(interface, pl->sfp_interfaces)) +			continue; + +		max_speed = phylink_interface_max_speed(interface); + +		/* The logic here is: if speed == max_speed, then we've found +		 * the best interface. Otherwise we find the interface that +		 * can just support the requested speed. +		 */ +		if (max_speed >= speed) +			best_interface = interface; + +		if (max_speed <= speed) +			break; +	} + +	if (best_interface == PHY_INTERFACE_MODE_NA) +		phylink_err(pl, "selection of interface failed, speed %u\n", +			    speed); + +	return best_interface; +} +  static void phylink_merge_link_mode(unsigned long *dst, const unsigned long *b)  {  	__ETHTOOL_DECLARE_LINK_MODE_MASK(mask); @@ -2898,8 +2945,14 @@ int phylink_ethtool_ksettings_set(struct phylink *pl,  	 * link can be configured correctly.  	 */  	if (pl->sfp_bus) { -		config.interface = phylink_sfp_select_interface(pl, +		if (kset->base.autoneg == AUTONEG_ENABLE) +			config.interface = +				phylink_sfp_select_interface(pl,  							config.advertising); +		else +			config.interface = +				phylink_sfp_select_interface_speed(pl, +							config.speed);  		if (config.interface == PHY_INTERFACE_MODE_NA)  			return -EINVAL; @@ -3523,6 +3576,8 @@ static int phylink_sfp_config_phy(struct phylink *pl, struct phy_device *phy)  	struct phylink_link_state config;  	int ret; +	/* We're not using pl->sfp_interfaces, so clear it. */ +	phy_interface_zero(pl->sfp_interfaces);  	linkmode_copy(support, phy->supported);  	memset(&config, 0, sizeof(config)); @@ -3569,7 +3624,6 @@ static int phylink_sfp_config_phy(struct phylink *pl, struct phy_device *phy)  static int phylink_sfp_config_optical(struct phylink *pl)  {  	__ETHTOOL_DECLARE_LINK_MODE_MASK(support); -	DECLARE_PHY_INTERFACE_MASK(interfaces);  	struct phylink_link_state config;  	phy_interface_t interface;  	int ret; @@ -3583,9 +3637,9 @@ static int phylink_sfp_config_optical(struct phylink *pl)  	/* Find the union of the supported interfaces by the PCS/MAC and  	 * the SFP module.  	 */ -	phy_interface_and(interfaces, pl->config->supported_interfaces, +	phy_interface_and(pl->sfp_interfaces, pl->config->supported_interfaces,  			  pl->sfp_interfaces); -	if (phy_interface_empty(interfaces)) { +	if (phy_interface_empty(pl->sfp_interfaces)) {  		phylink_err(pl, "unsupported SFP module: no common interface modes\n");  		return -EINVAL;  	} @@ -3601,14 +3655,14 @@ static int phylink_sfp_config_optical(struct phylink *pl)  	 * mask to only those link modes that can be supported.  	 */  	ret = phylink_validate_mask(pl, NULL, pl->sfp_support, &config, -				    interfaces); +				    pl->sfp_interfaces);  	if (ret) {  		phylink_err(pl, "unsupported SFP module: validation with support %*pb failed\n",  			    __ETHTOOL_LINK_MODE_MASK_NBITS, support);  		return ret;  	} -	interface = phylink_choose_sfp_interface(pl, interfaces); +	interface = phylink_choose_sfp_interface(pl, pl->sfp_interfaces);  	if (interface == PHY_INTERFACE_MODE_NA) {  		phylink_err(pl, "failed to select SFP interface\n");  		return -EINVAL; @@ -3661,6 +3715,13 @@ static int phylink_sfp_module_insert(void *upstream,  	return phylink_sfp_config_optical(pl);  } +static void phylink_sfp_module_remove(void *upstream) +{ +	struct phylink *pl = upstream; + +	phy_interface_zero(pl->sfp_interfaces); +} +  static int phylink_sfp_module_start(void *upstream)  {  	struct phylink *pl = upstream; @@ -3745,6 +3806,7 @@ static const struct sfp_upstream_ops sfp_phylink_ops = {  	.attach = phylink_sfp_attach,  	.detach = phylink_sfp_detach,  	.module_insert = phylink_sfp_module_insert, +	.module_remove = phylink_sfp_module_remove,  	.module_start = phylink_sfp_module_start,  	.module_stop = phylink_sfp_module_stop,  	.link_up = phylink_sfp_link_up,  | 
