Merge branch 'net-phy-rework-linkmodes-handling-in-a-dedicated-file'

Maxime Chevallier says:

====================
net: phy: Rework linkmodes handling in a dedicated file

This is V5 of the phy_caps series. In a nutshell, this series reworks the way
we maintain the list of speed/duplex capablities for each linkmode so that we
no longer have multiple definition of these associations.

That will help making sure that when people add new linkmodes in
include/uapi/linux/ethtool.h, they don't have to update phylib and phylink as
well, making the process more straightforward and less error-prone.

It also generalises the phy_caps interface to be able to lookup linkmodes
from phy_interface_t, which is needed for the multi-port work I've been working
on for a while.

This V5 addresse Russell's and Paolo's reviews, namely :

 - Error out when encountering an unknown SPEED_XXX setting

   It prints an error and fails to initialize phylib. I've tested by
   introducing a dummy 1.6T speed, I guess it's only a matter of time
   before that actually happens :)

 - Deal more gracefully with the fixed-link settings, keeping some level of
   compatibility with what we had before by making sure we report a
   single BaseT mode like before.

V1 : https://lore.kernel.org/netdev/20250222142727.894124-1-maxime.chevallier@bootlin.com/
V2 : https://lore.kernel.org/netdev/20250226100929.1646454-1-maxime.chevallier@bootlin.com/
V3 : https://lore.kernel.org/netdev/20250228145540.2209551-1-maxime.chevallier@bootlin.com/
V4 : https://lore.kernel.org/netdev/20250303090321.805785-1-maxime.chevallier@bootlin.com/
====================

Link: https://patch.msgid.link/20250307173611.129125-1-maxime.chevallier@bootlin.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
Paolo Abeni 2025-03-18 09:03:17 +01:00
commit 50698b298b
11 changed files with 571 additions and 545 deletions

View File

@ -3,7 +3,7 @@
libphy-y := phy.o phy-c45.o phy-core.o phy_device.o \
linkmode.o phy_link_topology.o \
phy_package.o
phy_package.o phy_caps.o
mdio-bus-y += mdio_bus.o mdio_device.o
ifdef CONFIG_MDIO_DEVICE

View File

@ -0,0 +1,63 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* link caps internal header, for link modes <-> capabilities <-> interfaces
* conversions.
*/
#ifndef __PHY_CAPS_H
#define __PHY_CAPS_H
#include <linux/ethtool.h>
#include <linux/phy.h>
enum {
LINK_CAPA_10HD = 0,
LINK_CAPA_10FD,
LINK_CAPA_100HD,
LINK_CAPA_100FD,
LINK_CAPA_1000HD,
LINK_CAPA_1000FD,
LINK_CAPA_2500FD,
LINK_CAPA_5000FD,
LINK_CAPA_10000FD,
LINK_CAPA_20000FD,
LINK_CAPA_25000FD,
LINK_CAPA_40000FD,
LINK_CAPA_50000FD,
LINK_CAPA_56000FD,
LINK_CAPA_100000FD,
LINK_CAPA_200000FD,
LINK_CAPA_400000FD,
LINK_CAPA_800000FD,
__LINK_CAPA_MAX,
};
#define LINK_CAPA_ALL GENMASK((__LINK_CAPA_MAX - 1), 0)
struct link_capabilities {
int speed;
unsigned int duplex;
__ETHTOOL_DECLARE_LINK_MODE_MASK(linkmodes);
};
int phy_caps_init(void);
size_t phy_caps_speeds(unsigned int *speeds, size_t size,
unsigned long *linkmodes);
void phy_caps_linkmode_max_speed(u32 max_speed, unsigned long *linkmodes);
bool phy_caps_valid(int speed, int duplex, const unsigned long *linkmodes);
void phy_caps_linkmodes(unsigned long caps, unsigned long *linkmodes);
unsigned long phy_caps_from_interface(phy_interface_t interface);
const struct link_capabilities *
phy_caps_lookup_by_linkmode(const unsigned long *linkmodes);
const struct link_capabilities *
phy_caps_lookup_by_linkmode_rev(const unsigned long *linkmodes, bool fdx_only);
const struct link_capabilities *
phy_caps_lookup(int speed, unsigned int duplex, const unsigned long *supported,
bool exact);
#endif /* __PHY_CAPS_H */

View File

@ -8,6 +8,7 @@
#include "phylib.h"
#include "phylib-internal.h"
#include "phy-caps.h"
/**
* phy_speed_to_str - Return a string representing the PHY link speed
@ -156,221 +157,9 @@ int phy_interface_num_ports(phy_interface_t interface)
}
EXPORT_SYMBOL_GPL(phy_interface_num_ports);
/* A mapping of all SUPPORTED settings to speed/duplex. This table
* must be grouped by speed and sorted in descending match priority
* - iow, descending speed.
*/
#define PHY_SETTING(s, d, b) { .speed = SPEED_ ## s, .duplex = DUPLEX_ ## d, \
.bit = ETHTOOL_LINK_MODE_ ## b ## _BIT}
static const struct phy_setting settings[] = {
/* 800G */
PHY_SETTING( 800000, FULL, 800000baseCR8_Full ),
PHY_SETTING( 800000, FULL, 800000baseKR8_Full ),
PHY_SETTING( 800000, FULL, 800000baseDR8_Full ),
PHY_SETTING( 800000, FULL, 800000baseDR8_2_Full ),
PHY_SETTING( 800000, FULL, 800000baseSR8_Full ),
PHY_SETTING( 800000, FULL, 800000baseVR8_Full ),
PHY_SETTING( 800000, FULL, 800000baseCR4_Full ),
PHY_SETTING( 800000, FULL, 800000baseKR4_Full ),
PHY_SETTING( 800000, FULL, 800000baseDR4_Full ),
PHY_SETTING( 800000, FULL, 800000baseDR4_2_Full ),
PHY_SETTING( 800000, FULL, 800000baseSR4_Full ),
PHY_SETTING( 800000, FULL, 800000baseVR4_Full ),
/* 400G */
PHY_SETTING( 400000, FULL, 400000baseCR8_Full ),
PHY_SETTING( 400000, FULL, 400000baseKR8_Full ),
PHY_SETTING( 400000, FULL, 400000baseLR8_ER8_FR8_Full ),
PHY_SETTING( 400000, FULL, 400000baseDR8_Full ),
PHY_SETTING( 400000, FULL, 400000baseSR8_Full ),
PHY_SETTING( 400000, FULL, 400000baseCR4_Full ),
PHY_SETTING( 400000, FULL, 400000baseKR4_Full ),
PHY_SETTING( 400000, FULL, 400000baseLR4_ER4_FR4_Full ),
PHY_SETTING( 400000, FULL, 400000baseDR4_Full ),
PHY_SETTING( 400000, FULL, 400000baseSR4_Full ),
PHY_SETTING( 400000, FULL, 400000baseCR2_Full ),
PHY_SETTING( 400000, FULL, 400000baseKR2_Full ),
PHY_SETTING( 400000, FULL, 400000baseDR2_Full ),
PHY_SETTING( 400000, FULL, 400000baseDR2_2_Full ),
PHY_SETTING( 400000, FULL, 400000baseSR2_Full ),
PHY_SETTING( 400000, FULL, 400000baseVR2_Full ),
/* 200G */
PHY_SETTING( 200000, FULL, 200000baseCR4_Full ),
PHY_SETTING( 200000, FULL, 200000baseKR4_Full ),
PHY_SETTING( 200000, FULL, 200000baseLR4_ER4_FR4_Full ),
PHY_SETTING( 200000, FULL, 200000baseDR4_Full ),
PHY_SETTING( 200000, FULL, 200000baseSR4_Full ),
PHY_SETTING( 200000, FULL, 200000baseCR2_Full ),
PHY_SETTING( 200000, FULL, 200000baseKR2_Full ),
PHY_SETTING( 200000, FULL, 200000baseLR2_ER2_FR2_Full ),
PHY_SETTING( 200000, FULL, 200000baseDR2_Full ),
PHY_SETTING( 200000, FULL, 200000baseSR2_Full ),
PHY_SETTING( 200000, FULL, 200000baseCR_Full ),
PHY_SETTING( 200000, FULL, 200000baseKR_Full ),
PHY_SETTING( 200000, FULL, 200000baseDR_Full ),
PHY_SETTING( 200000, FULL, 200000baseDR_2_Full ),
PHY_SETTING( 200000, FULL, 200000baseSR_Full ),
PHY_SETTING( 200000, FULL, 200000baseVR_Full ),
/* 100G */
PHY_SETTING( 100000, FULL, 100000baseCR4_Full ),
PHY_SETTING( 100000, FULL, 100000baseKR4_Full ),
PHY_SETTING( 100000, FULL, 100000baseLR4_ER4_Full ),
PHY_SETTING( 100000, FULL, 100000baseSR4_Full ),
PHY_SETTING( 100000, FULL, 100000baseCR2_Full ),
PHY_SETTING( 100000, FULL, 100000baseKR2_Full ),
PHY_SETTING( 100000, FULL, 100000baseLR2_ER2_FR2_Full ),
PHY_SETTING( 100000, FULL, 100000baseDR2_Full ),
PHY_SETTING( 100000, FULL, 100000baseSR2_Full ),
PHY_SETTING( 100000, FULL, 100000baseCR_Full ),
PHY_SETTING( 100000, FULL, 100000baseKR_Full ),
PHY_SETTING( 100000, FULL, 100000baseLR_ER_FR_Full ),
PHY_SETTING( 100000, FULL, 100000baseDR_Full ),
PHY_SETTING( 100000, FULL, 100000baseSR_Full ),
/* 56G */
PHY_SETTING( 56000, FULL, 56000baseCR4_Full ),
PHY_SETTING( 56000, FULL, 56000baseKR4_Full ),
PHY_SETTING( 56000, FULL, 56000baseLR4_Full ),
PHY_SETTING( 56000, FULL, 56000baseSR4_Full ),
/* 50G */
PHY_SETTING( 50000, FULL, 50000baseCR2_Full ),
PHY_SETTING( 50000, FULL, 50000baseKR2_Full ),
PHY_SETTING( 50000, FULL, 50000baseSR2_Full ),
PHY_SETTING( 50000, FULL, 50000baseCR_Full ),
PHY_SETTING( 50000, FULL, 50000baseKR_Full ),
PHY_SETTING( 50000, FULL, 50000baseLR_ER_FR_Full ),
PHY_SETTING( 50000, FULL, 50000baseDR_Full ),
PHY_SETTING( 50000, FULL, 50000baseSR_Full ),
/* 40G */
PHY_SETTING( 40000, FULL, 40000baseCR4_Full ),
PHY_SETTING( 40000, FULL, 40000baseKR4_Full ),
PHY_SETTING( 40000, FULL, 40000baseLR4_Full ),
PHY_SETTING( 40000, FULL, 40000baseSR4_Full ),
/* 25G */
PHY_SETTING( 25000, FULL, 25000baseCR_Full ),
PHY_SETTING( 25000, FULL, 25000baseKR_Full ),
PHY_SETTING( 25000, FULL, 25000baseSR_Full ),
/* 20G */
PHY_SETTING( 20000, FULL, 20000baseKR2_Full ),
PHY_SETTING( 20000, FULL, 20000baseMLD2_Full ),
/* 10G */
PHY_SETTING( 10000, FULL, 10000baseCR_Full ),
PHY_SETTING( 10000, FULL, 10000baseER_Full ),
PHY_SETTING( 10000, FULL, 10000baseKR_Full ),
PHY_SETTING( 10000, FULL, 10000baseKX4_Full ),
PHY_SETTING( 10000, FULL, 10000baseLR_Full ),
PHY_SETTING( 10000, FULL, 10000baseLRM_Full ),
PHY_SETTING( 10000, FULL, 10000baseR_FEC ),
PHY_SETTING( 10000, FULL, 10000baseSR_Full ),
PHY_SETTING( 10000, FULL, 10000baseT_Full ),
/* 5G */
PHY_SETTING( 5000, FULL, 5000baseT_Full ),
/* 2.5G */
PHY_SETTING( 2500, FULL, 2500baseT_Full ),
PHY_SETTING( 2500, FULL, 2500baseX_Full ),
/* 1G */
PHY_SETTING( 1000, FULL, 1000baseT_Full ),
PHY_SETTING( 1000, HALF, 1000baseT_Half ),
PHY_SETTING( 1000, FULL, 1000baseT1_Full ),
PHY_SETTING( 1000, FULL, 1000baseX_Full ),
PHY_SETTING( 1000, FULL, 1000baseKX_Full ),
/* 100M */
PHY_SETTING( 100, FULL, 100baseT_Full ),
PHY_SETTING( 100, FULL, 100baseT1_Full ),
PHY_SETTING( 100, HALF, 100baseT_Half ),
PHY_SETTING( 100, HALF, 100baseFX_Half ),
PHY_SETTING( 100, FULL, 100baseFX_Full ),
/* 10M */
PHY_SETTING( 10, FULL, 10baseT_Full ),
PHY_SETTING( 10, HALF, 10baseT_Half ),
PHY_SETTING( 10, FULL, 10baseT1L_Full ),
PHY_SETTING( 10, FULL, 10baseT1S_Full ),
PHY_SETTING( 10, HALF, 10baseT1S_Half ),
PHY_SETTING( 10, HALF, 10baseT1S_P2MP_Half ),
PHY_SETTING( 10, FULL, 10baseT1BRR_Full ),
};
#undef PHY_SETTING
/**
* phy_lookup_setting - lookup a PHY setting
* @speed: speed to match
* @duplex: duplex to match
* @mask: allowed link modes
* @exact: an exact match is required
*
* Search the settings array for a setting that matches the speed and
* duplex, and which is supported.
*
* If @exact is unset, either an exact match or %NULL for no match will
* be returned.
*
* If @exact is set, an exact match, the fastest supported setting at
* or below the specified speed, the slowest supported setting, or if
* they all fail, %NULL will be returned.
*/
const struct phy_setting *
phy_lookup_setting(int speed, int duplex, const unsigned long *mask, bool exact)
{
const struct phy_setting *p, *match = NULL, *last = NULL;
int i;
for (i = 0, p = settings; i < ARRAY_SIZE(settings); i++, p++) {
if (p->bit < __ETHTOOL_LINK_MODE_MASK_NBITS &&
test_bit(p->bit, mask)) {
last = p;
if (p->speed == speed && p->duplex == duplex) {
/* Exact match for speed and duplex */
match = p;
break;
} else if (!exact) {
if (!match && p->speed <= speed)
/* Candidate */
match = p;
if (p->speed < speed)
break;
}
}
}
if (!match && !exact)
match = last;
return match;
}
EXPORT_SYMBOL_GPL(phy_lookup_setting);
size_t phy_speeds(unsigned int *speeds, size_t size,
unsigned long *mask)
{
size_t count;
int i;
for (i = 0, count = 0; i < ARRAY_SIZE(settings) && count < size; i++)
if (settings[i].bit < __ETHTOOL_LINK_MODE_MASK_NBITS &&
test_bit(settings[i].bit, mask) &&
(count == 0 || speeds[count - 1] != settings[i].speed))
speeds[count++] = settings[i].speed;
return count;
}
static void __set_linkmode_max_speed(u32 max_speed, unsigned long *addr)
{
const struct phy_setting *p;
int i;
for (i = 0, p = settings; i < ARRAY_SIZE(settings); i++, p++) {
if (p->speed > max_speed)
linkmode_clear_bit(p->bit, addr);
else
break;
}
}
static void __set_phy_supported(struct phy_device *phydev, u32 max_speed)
{
__set_linkmode_max_speed(max_speed, phydev->supported);
phy_caps_linkmode_max_speed(max_speed, phydev->supported);
}
/**
@ -496,16 +285,15 @@ EXPORT_SYMBOL_GPL(phy_resolve_aneg_pause);
void phy_resolve_aneg_linkmode(struct phy_device *phydev)
{
__ETHTOOL_DECLARE_LINK_MODE_MASK(common);
int i;
const struct link_capabilities *c;
linkmode_and(common, phydev->lp_advertising, phydev->advertising);
for (i = 0; i < ARRAY_SIZE(settings); i++)
if (test_bit(settings[i].bit, common)) {
phydev->speed = settings[i].speed;
phydev->duplex = settings[i].duplex;
break;
}
c = phy_caps_lookup_by_linkmode(common);
if (c) {
phydev->speed = c->speed;
phydev->duplex = c->duplex;
}
phy_resolve_aneg_pause(phydev);
}
@ -523,7 +311,8 @@ EXPORT_SYMBOL_GPL(phy_resolve_aneg_linkmode);
void phy_check_downshift(struct phy_device *phydev)
{
__ETHTOOL_DECLARE_LINK_MODE_MASK(common);
int i, speed = SPEED_UNKNOWN;
const struct link_capabilities *c;
int speed = SPEED_UNKNOWN;
phydev->downshifted_rate = 0;
@ -533,11 +322,9 @@ void phy_check_downshift(struct phy_device *phydev)
linkmode_and(common, phydev->lp_advertising, phydev->advertising);
for (i = 0; i < ARRAY_SIZE(settings); i++)
if (test_bit(settings[i].bit, common)) {
speed = settings[i].speed;
break;
}
c = phy_caps_lookup_by_linkmode(common);
if (c)
speed = c->speed;
if (speed == SPEED_UNKNOWN || phydev->speed >= speed)
return;
@ -551,17 +338,13 @@ void phy_check_downshift(struct phy_device *phydev)
static int phy_resolve_min_speed(struct phy_device *phydev, bool fdx_only)
{
__ETHTOOL_DECLARE_LINK_MODE_MASK(common);
int i = ARRAY_SIZE(settings);
const struct link_capabilities *c;
linkmode_and(common, phydev->lp_advertising, phydev->advertising);
while (--i >= 0) {
if (test_bit(settings[i].bit, common)) {
if (fdx_only && settings[i].duplex != DUPLEX_FULL)
continue;
return settings[i].speed;
}
}
c = phy_caps_lookup_by_linkmode_rev(common, fdx_only);
if (c)
return c->speed;
return SPEED_UNKNOWN;
}
@ -573,7 +356,7 @@ int phy_speed_down_core(struct phy_device *phydev)
if (min_common_speed == SPEED_UNKNOWN)
return -EINVAL;
__set_linkmode_max_speed(min_common_speed, phydev->advertising);
phy_caps_linkmode_max_speed(min_common_speed, phydev->advertising);
return 0;
}

View File

@ -37,6 +37,7 @@
#include <net/sock.h>
#include "phylib-internal.h"
#include "phy-caps.h"
#define PHY_STATE_TIME HZ
@ -212,25 +213,6 @@ int phy_aneg_done(struct phy_device *phydev)
}
EXPORT_SYMBOL(phy_aneg_done);
/**
* phy_find_valid - find a PHY setting that matches the requested parameters
* @speed: desired speed
* @duplex: desired duplex
* @supported: mask of supported link modes
*
* Locate a supported phy setting that is, in priority order:
* - an exact match for the specified speed and duplex mode
* - a match for the specified speed, or slower speed
* - the slowest supported speed
* Returns the matched phy_setting entry, or %NULL if no supported phy
* settings were found.
*/
static const struct phy_setting *
phy_find_valid(int speed, int duplex, unsigned long *supported)
{
return phy_lookup_setting(speed, duplex, supported, false);
}
/**
* phy_supported_speeds - return all speeds currently supported by a phy device
* @phy: The phy device to return supported speeds of.
@ -245,7 +227,7 @@ unsigned int phy_supported_speeds(struct phy_device *phy,
unsigned int *speeds,
unsigned int size)
{
return phy_speeds(speeds, size, phy->supported);
return phy_caps_speeds(speeds, size, phy->supported);
}
/**
@ -259,7 +241,7 @@ unsigned int phy_supported_speeds(struct phy_device *phy,
*/
bool phy_check_valid(int speed, int duplex, unsigned long *features)
{
return !!phy_lookup_setting(speed, duplex, features, true);
return phy_caps_valid(speed, duplex, features);
}
EXPORT_SYMBOL(phy_check_valid);
@ -273,13 +255,14 @@ EXPORT_SYMBOL(phy_check_valid);
*/
static void phy_sanitize_settings(struct phy_device *phydev)
{
const struct phy_setting *setting;
const struct link_capabilities *c;
setting = phy_find_valid(phydev->speed, phydev->duplex,
phydev->supported);
if (setting) {
phydev->speed = setting->speed;
phydev->duplex = setting->duplex;
c = phy_caps_lookup(phydev->speed, phydev->duplex, phydev->supported,
false);
if (c) {
phydev->speed = c->speed;
phydev->duplex = c->duplex;
} else {
/* We failed to find anything (no supported speeds?) */
phydev->speed = SPEED_UNKNOWN;

359
drivers/net/phy/phy_caps.c Normal file
View File

@ -0,0 +1,359 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <linux/ethtool.h>
#include <linux/linkmode.h>
#include <linux/phy.h>
#include "phy-caps.h"
static struct link_capabilities link_caps[__LINK_CAPA_MAX] __ro_after_init = {
{ SPEED_10, DUPLEX_HALF, {0} }, /* LINK_CAPA_10HD */
{ SPEED_10, DUPLEX_FULL, {0} }, /* LINK_CAPA_10FD */
{ SPEED_100, DUPLEX_HALF, {0} }, /* LINK_CAPA_100HD */
{ SPEED_100, DUPLEX_FULL, {0} }, /* LINK_CAPA_100FD */
{ SPEED_1000, DUPLEX_HALF, {0} }, /* LINK_CAPA_1000HD */
{ SPEED_1000, DUPLEX_FULL, {0} }, /* LINK_CAPA_1000FD */
{ SPEED_2500, DUPLEX_FULL, {0} }, /* LINK_CAPA_2500FD */
{ SPEED_5000, DUPLEX_FULL, {0} }, /* LINK_CAPA_5000FD */
{ SPEED_10000, DUPLEX_FULL, {0} }, /* LINK_CAPA_10000FD */
{ SPEED_20000, DUPLEX_FULL, {0} }, /* LINK_CAPA_20000FD */
{ SPEED_25000, DUPLEX_FULL, {0} }, /* LINK_CAPA_25000FD */
{ SPEED_40000, DUPLEX_FULL, {0} }, /* LINK_CAPA_40000FD */
{ SPEED_50000, DUPLEX_FULL, {0} }, /* LINK_CAPA_50000FD */
{ SPEED_56000, DUPLEX_FULL, {0} }, /* LINK_CAPA_56000FD */
{ SPEED_100000, DUPLEX_FULL, {0} }, /* LINK_CAPA_100000FD */
{ SPEED_200000, DUPLEX_FULL, {0} }, /* LINK_CAPA_200000FD */
{ SPEED_400000, DUPLEX_FULL, {0} }, /* LINK_CAPA_400000FD */
{ SPEED_800000, DUPLEX_FULL, {0} }, /* LINK_CAPA_800000FD */
};
static int speed_duplex_to_capa(int speed, unsigned int duplex)
{
if (duplex == DUPLEX_UNKNOWN ||
(speed > SPEED_1000 && duplex != DUPLEX_FULL))
return -EINVAL;
switch (speed) {
case SPEED_10: return duplex == DUPLEX_FULL ?
LINK_CAPA_10FD : LINK_CAPA_10HD;
case SPEED_100: return duplex == DUPLEX_FULL ?
LINK_CAPA_100FD : LINK_CAPA_100HD;
case SPEED_1000: return duplex == DUPLEX_FULL ?
LINK_CAPA_1000FD : LINK_CAPA_1000HD;
case SPEED_2500: return LINK_CAPA_2500FD;
case SPEED_5000: return LINK_CAPA_5000FD;
case SPEED_10000: return LINK_CAPA_10000FD;
case SPEED_20000: return LINK_CAPA_20000FD;
case SPEED_25000: return LINK_CAPA_25000FD;
case SPEED_40000: return LINK_CAPA_40000FD;
case SPEED_50000: return LINK_CAPA_50000FD;
case SPEED_56000: return LINK_CAPA_56000FD;
case SPEED_100000: return LINK_CAPA_100000FD;
case SPEED_200000: return LINK_CAPA_200000FD;
case SPEED_400000: return LINK_CAPA_400000FD;
case SPEED_800000: return LINK_CAPA_800000FD;
}
return -EINVAL;
}
#define for_each_link_caps_asc_speed(cap) \
for (cap = link_caps; cap < &link_caps[__LINK_CAPA_MAX]; cap++)
#define for_each_link_caps_desc_speed(cap) \
for (cap = &link_caps[__LINK_CAPA_MAX - 1]; cap >= link_caps; cap--)
/**
* phy_caps_init() - Initializes the link_caps array from the link_mode_params.
*
* Returns: 0 if phy caps init was successful, -EINVAL if we found an
* unexpected linkmode setting that requires LINK_CAPS update.
*
*/
int phy_caps_init(void)
{
const struct link_mode_info *linkmode;
int i, capa;
/* Fill the caps array from net/ethtool/common.c */
for (i = 0; i < __ETHTOOL_LINK_MODE_MASK_NBITS; i++) {
linkmode = &link_mode_params[i];
capa = speed_duplex_to_capa(linkmode->speed, linkmode->duplex);
if (capa < 0) {
if (linkmode->speed != SPEED_UNKNOWN) {
pr_err("Unknown speed %d, please update LINK_CAPS\n",
linkmode->speed);
return -EINVAL;
}
continue;
}
__set_bit(i, link_caps[capa].linkmodes);
}
return 0;
}
/**
* phy_caps_speeds() - Fill an array of supported SPEED_* values for given modes
* @speeds: Output array to store the speeds list into
* @size: Size of the output array
* @linkmodes: Linkmodes to get the speeds from
*
* Fills the speeds array with all possible speeds that can be achieved with
* the specified linkmodes.
*
* Returns: The number of speeds filled into the array. If the input array isn't
* big enough to store all speeds, fill it as much as possible.
*/
size_t phy_caps_speeds(unsigned int *speeds, size_t size,
unsigned long *linkmodes)
{
struct link_capabilities *lcap;
size_t count = 0;
for_each_link_caps_asc_speed(lcap) {
if (linkmode_intersects(lcap->linkmodes, linkmodes) &&
(count == 0 || speeds[count - 1] != lcap->speed)) {
speeds[count++] = lcap->speed;
if (count >= size)
break;
}
}
return count;
}
/**
* phy_caps_lookup_by_linkmode() - Lookup the fastest matching link_capabilities
* @linkmodes: Linkmodes to match against
*
* Returns: The highest-speed link_capabilities that intersects the given
* linkmodes. In case several DUPLEX_ options exist at that speed,
* DUPLEX_FULL is matched first. NULL is returned if no match.
*/
const struct link_capabilities *
phy_caps_lookup_by_linkmode(const unsigned long *linkmodes)
{
struct link_capabilities *lcap;
for_each_link_caps_desc_speed(lcap)
if (linkmode_intersects(lcap->linkmodes, linkmodes))
return lcap;
return NULL;
}
/**
* phy_caps_lookup_by_linkmode_rev() - Lookup the slowest matching link_capabilities
* @linkmodes: Linkmodes to match against
* @fdx_only: Full duplex match only when set
*
* Returns: The lowest-speed link_capabilities that intersects the given
* linkmodes. When set, fdx_only will ignore half-duplex matches.
* NULL is returned if no match.
*/
const struct link_capabilities *
phy_caps_lookup_by_linkmode_rev(const unsigned long *linkmodes, bool fdx_only)
{
struct link_capabilities *lcap;
for_each_link_caps_asc_speed(lcap) {
if (fdx_only && lcap->duplex != DUPLEX_FULL)
continue;
if (linkmode_intersects(lcap->linkmodes, linkmodes))
return lcap;
}
return NULL;
}
/**
* phy_caps_lookup() - Lookup capabilities by speed/duplex that matches a mask
* @speed: Speed to match
* @duplex: Duplex to match
* @supported: Mask of linkmodes to match
* @exact: Perform an exact match or not.
*
* Lookup a link_capabilities entry that intersect the supported linkmodes mask,
* and that matches the passed speed and duplex.
*
* When @exact is set, an exact match is performed on speed and duplex, meaning
* that if the linkmodes for the given speed and duplex intersect the supported
* mask, this capability is returned, otherwise we don't have a match and return
* NULL.
*
* When @exact is not set, we return either an exact match, or matching capabilities
* at lower speed, or the lowest matching speed, or NULL.
*
* Returns: a matched link_capabilities according to the above process, NULL
* otherwise.
*/
const struct link_capabilities *
phy_caps_lookup(int speed, unsigned int duplex, const unsigned long *supported,
bool exact)
{
const struct link_capabilities *lcap, *last = NULL;
for_each_link_caps_desc_speed(lcap) {
if (linkmode_intersects(lcap->linkmodes, supported)) {
last = lcap;
/* exact match on speed and duplex*/
if (lcap->speed == speed && lcap->duplex == duplex) {
return lcap;
} else if (!exact) {
if (lcap->speed <= speed)
return lcap;
}
}
}
if (!exact)
return last;
return NULL;
}
EXPORT_SYMBOL_GPL(phy_caps_lookup);
/**
* phy_caps_linkmode_max_speed() - Clamp a linkmodes set to a max speed
* @max_speed: Speed limit for the linkmode set
* @linkmodes: Linkmodes to limit
*/
void phy_caps_linkmode_max_speed(u32 max_speed, unsigned long *linkmodes)
{
struct link_capabilities *lcap;
for_each_link_caps_desc_speed(lcap)
if (lcap->speed > max_speed)
linkmode_andnot(linkmodes, linkmodes, lcap->linkmodes);
else
break;
}
/**
* phy_caps_valid() - Validate a linkmodes set agains given speed and duplex
* @speed: input speed to validate
* @duplex: input duplex to validate. Passing DUPLEX_UNKNOWN is always not valid
* @linkmodes: The linkmodes to validate
*
* Returns: True if at least one of the linkmodes in @linkmodes can function at
* the given speed and duplex, false otherwise.
*/
bool phy_caps_valid(int speed, int duplex, const unsigned long *linkmodes)
{
int capa = speed_duplex_to_capa(speed, duplex);
if (capa < 0)
return false;
return linkmode_intersects(link_caps[capa].linkmodes, linkmodes);
}
/**
* phy_caps_linkmodes() - Convert a bitfield of capabilities into linkmodes
* @caps: The list of caps, each bit corresponding to a LINK_CAPA value
* @linkmodes: The set of linkmodes to fill. Must be previously initialized.
*/
void phy_caps_linkmodes(unsigned long caps, unsigned long *linkmodes)
{
unsigned long capa;
for_each_set_bit(capa, &caps, __LINK_CAPA_MAX)
linkmode_or(linkmodes, linkmodes, link_caps[capa].linkmodes);
}
EXPORT_SYMBOL_GPL(phy_caps_linkmodes);
/**
* phy_caps_from_interface() - Get the link capa from a given PHY interface
* @interface: The PHY interface we want to get the possible Speed/Duplex from
*
* Returns: A bitmask of LINK_CAPA_xxx values that can be achieved with the
* provided interface.
*/
unsigned long phy_caps_from_interface(phy_interface_t interface)
{
unsigned long link_caps = 0;
switch (interface) {
case PHY_INTERFACE_MODE_USXGMII:
link_caps |= BIT(LINK_CAPA_10000FD) | BIT(LINK_CAPA_5000FD);
fallthrough;
case PHY_INTERFACE_MODE_10G_QXGMII:
link_caps |= BIT(LINK_CAPA_2500FD);
fallthrough;
case PHY_INTERFACE_MODE_RGMII_TXID:
case PHY_INTERFACE_MODE_RGMII_RXID:
case PHY_INTERFACE_MODE_RGMII_ID:
case PHY_INTERFACE_MODE_RGMII:
case PHY_INTERFACE_MODE_PSGMII:
case PHY_INTERFACE_MODE_QSGMII:
case PHY_INTERFACE_MODE_QUSGMII:
case PHY_INTERFACE_MODE_SGMII:
case PHY_INTERFACE_MODE_GMII:
link_caps |= BIT(LINK_CAPA_1000HD) | BIT(LINK_CAPA_1000FD);
fallthrough;
case PHY_INTERFACE_MODE_REVRMII:
case PHY_INTERFACE_MODE_RMII:
case PHY_INTERFACE_MODE_SMII:
case PHY_INTERFACE_MODE_REVMII:
case PHY_INTERFACE_MODE_MII:
link_caps |= BIT(LINK_CAPA_10HD) | BIT(LINK_CAPA_10FD);
fallthrough;
case PHY_INTERFACE_MODE_100BASEX:
link_caps |= BIT(LINK_CAPA_100HD) | BIT(LINK_CAPA_100FD);
break;
case PHY_INTERFACE_MODE_TBI:
case PHY_INTERFACE_MODE_MOCA:
case PHY_INTERFACE_MODE_RTBI:
case PHY_INTERFACE_MODE_1000BASEX:
link_caps |= BIT(LINK_CAPA_1000HD);
fallthrough;
case PHY_INTERFACE_MODE_1000BASEKX:
case PHY_INTERFACE_MODE_TRGMII:
link_caps |= BIT(LINK_CAPA_1000FD);
break;
case PHY_INTERFACE_MODE_2500BASEX:
link_caps |= BIT(LINK_CAPA_2500FD);
break;
case PHY_INTERFACE_MODE_5GBASER:
link_caps |= BIT(LINK_CAPA_5000FD);
break;
case PHY_INTERFACE_MODE_XGMII:
case PHY_INTERFACE_MODE_RXAUI:
case PHY_INTERFACE_MODE_XAUI:
case PHY_INTERFACE_MODE_10GBASER:
case PHY_INTERFACE_MODE_10GKR:
link_caps |= BIT(LINK_CAPA_10000FD);
break;
case PHY_INTERFACE_MODE_25GBASER:
link_caps |= BIT(LINK_CAPA_25000FD);
break;
case PHY_INTERFACE_MODE_XLGMII:
link_caps |= BIT(LINK_CAPA_40000FD);
break;
case PHY_INTERFACE_MODE_INTERNAL:
link_caps |= LINK_CAPA_ALL;
break;
case PHY_INTERFACE_MODE_NA:
case PHY_INTERFACE_MODE_MAX:
break;
}
return link_caps;
}
EXPORT_SYMBOL_GPL(phy_caps_from_interface);

View File

@ -42,6 +42,7 @@
#include <linux/unistd.h>
#include "phylib-internal.h"
#include "phy-caps.h"
MODULE_DESCRIPTION("PHY library");
MODULE_AUTHOR("Andy Fleming");
@ -2119,7 +2120,7 @@ EXPORT_SYMBOL(genphy_check_and_restart_aneg);
int __genphy_config_aneg(struct phy_device *phydev, bool changed)
{
__ETHTOOL_DECLARE_LINK_MODE_MASK(fixed_advert);
const struct phy_setting *set;
const struct link_capabilities *c;
unsigned long *advert;
int err;
@ -2145,10 +2146,11 @@ int __genphy_config_aneg(struct phy_device *phydev, bool changed)
} else {
linkmode_zero(fixed_advert);
set = phy_lookup_setting(phydev->speed, phydev->duplex,
phydev->supported, true);
if (set)
linkmode_set_bit(set->bit, fixed_advert);
c = phy_caps_lookup(phydev->speed, phydev->duplex,
phydev->supported, true);
if (c)
linkmode_and(fixed_advert, phydev->supported,
c->linkmodes);
advert = fixed_advert;
}
@ -3558,6 +3560,10 @@ static int __init phy_init(void)
if (rc)
goto err_ethtool_phy_ops;
rc = phy_caps_init();
if (rc)
goto err_mdio_bus;
features_init();
rc = phy_driver_register(&genphy_c45_driver, THIS_MODULE);

View File

@ -20,6 +20,7 @@
#include <linux/timer.h>
#include <linux/workqueue.h>
#include "phy-caps.h"
#include "sfp.h"
#include "swphy.h"
@ -291,6 +292,61 @@ static int phylink_interface_max_speed(phy_interface_t interface)
return SPEED_UNKNOWN;
}
static struct {
unsigned long mask;
int speed;
unsigned int duplex;
unsigned int caps_bit;
} phylink_caps_params[] = {
{ MAC_400000FD, SPEED_400000, DUPLEX_FULL, BIT(LINK_CAPA_400000FD) },
{ MAC_200000FD, SPEED_200000, DUPLEX_FULL, BIT(LINK_CAPA_200000FD) },
{ MAC_100000FD, SPEED_100000, DUPLEX_FULL, BIT(LINK_CAPA_100000FD) },
{ MAC_56000FD, SPEED_56000, DUPLEX_FULL, BIT(LINK_CAPA_56000FD) },
{ MAC_50000FD, SPEED_50000, DUPLEX_FULL, BIT(LINK_CAPA_50000FD) },
{ MAC_40000FD, SPEED_40000, DUPLEX_FULL, BIT(LINK_CAPA_40000FD) },
{ MAC_25000FD, SPEED_25000, DUPLEX_FULL, BIT(LINK_CAPA_25000FD) },
{ MAC_20000FD, SPEED_20000, DUPLEX_FULL, BIT(LINK_CAPA_20000FD) },
{ MAC_10000FD, SPEED_10000, DUPLEX_FULL, BIT(LINK_CAPA_10000FD) },
{ MAC_5000FD, SPEED_5000, DUPLEX_FULL, BIT(LINK_CAPA_5000FD) },
{ MAC_2500FD, SPEED_2500, DUPLEX_FULL, BIT(LINK_CAPA_2500FD) },
{ MAC_1000FD, SPEED_1000, DUPLEX_FULL, BIT(LINK_CAPA_1000FD) },
{ MAC_1000HD, SPEED_1000, DUPLEX_HALF, BIT(LINK_CAPA_1000HD) },
{ MAC_100FD, SPEED_100, DUPLEX_FULL, BIT(LINK_CAPA_100FD) },
{ MAC_100HD, SPEED_100, DUPLEX_HALF, BIT(LINK_CAPA_100HD) },
{ MAC_10FD, SPEED_10, DUPLEX_FULL, BIT(LINK_CAPA_10FD) },
{ MAC_10HD, SPEED_10, DUPLEX_HALF, BIT(LINK_CAPA_10HD) },
};
/**
* phylink_caps_to_link_caps() - Convert a set of MAC capabilities LINK caps
* @caps: A set of MAC capabilities
*
* Returns: The corresponding set of LINK_CAPA as defined in phy-caps.h
*/
static unsigned long phylink_caps_to_link_caps(unsigned long caps)
{
unsigned long link_caps = 0;
int i;
for (i = 0; i < ARRAY_SIZE(phylink_caps_params); i++)
if (caps & phylink_caps_params[i].mask)
link_caps |= phylink_caps_params[i].caps_bit;
return link_caps;
}
static unsigned long phylink_link_caps_to_mac_caps(unsigned long link_caps)
{
unsigned long caps = 0;
int i;
for (i = 0; i < ARRAY_SIZE(phylink_caps_params); i++)
if (link_caps & phylink_caps_params[i].caps_bit)
caps |= phylink_caps_params[i].mask;
return caps;
}
/**
* phylink_caps_to_linkmodes() - Convert capabilities to ethtool link modes
* @linkmodes: ethtool linkmode mask (must be already initialised)
@ -302,172 +358,17 @@ static int phylink_interface_max_speed(phy_interface_t interface)
static void phylink_caps_to_linkmodes(unsigned long *linkmodes,
unsigned long caps)
{
unsigned long link_caps = phylink_caps_to_link_caps(caps);
if (caps & MAC_SYM_PAUSE)
__set_bit(ETHTOOL_LINK_MODE_Pause_BIT, linkmodes);
if (caps & MAC_ASYM_PAUSE)
__set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, linkmodes);
if (caps & MAC_10HD) {
__set_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_10baseT1S_Half_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_10baseT1S_P2MP_Half_BIT, linkmodes);
}
if (caps & MAC_10FD) {
__set_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_10baseT1L_Full_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_10baseT1S_Full_BIT, linkmodes);
}
if (caps & MAC_100HD) {
__set_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_100baseFX_Half_BIT, linkmodes);
}
if (caps & MAC_100FD) {
__set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_100baseT1_Full_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_100baseFX_Full_BIT, linkmodes);
}
if (caps & MAC_1000HD)
__set_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, linkmodes);
if (caps & MAC_1000FD) {
__set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_1000baseT1_Full_BIT, linkmodes);
}
if (caps & MAC_2500FD) {
__set_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_2500baseX_Full_BIT, linkmodes);
}
if (caps & MAC_5000FD)
__set_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT, linkmodes);
if (caps & MAC_10000FD) {
__set_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_10000baseR_FEC_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_10000baseCR_Full_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_10000baseSR_Full_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_10000baseLR_Full_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_10000baseER_Full_BIT, linkmodes);
}
if (caps & MAC_25000FD) {
__set_bit(ETHTOOL_LINK_MODE_25000baseCR_Full_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_25000baseKR_Full_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_25000baseSR_Full_BIT, linkmodes);
}
if (caps & MAC_40000FD) {
__set_bit(ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT, linkmodes);
}
if (caps & MAC_50000FD) {
__set_bit(ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_50000baseKR_Full_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_50000baseSR_Full_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_50000baseCR_Full_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT,
linkmodes);
__set_bit(ETHTOOL_LINK_MODE_50000baseDR_Full_BIT, linkmodes);
}
if (caps & MAC_56000FD) {
__set_bit(ETHTOOL_LINK_MODE_56000baseKR4_Full_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_56000baseCR4_Full_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_56000baseSR4_Full_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_56000baseLR4_Full_BIT, linkmodes);
}
if (caps & MAC_100000FD) {
__set_bit(ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT,
linkmodes);
__set_bit(ETHTOOL_LINK_MODE_100000baseKR2_Full_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_100000baseLR2_ER2_FR2_Full_BIT,
linkmodes);
__set_bit(ETHTOOL_LINK_MODE_100000baseDR2_Full_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_100000baseKR_Full_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_100000baseSR_Full_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_100000baseLR_ER_FR_Full_BIT,
linkmodes);
__set_bit(ETHTOOL_LINK_MODE_100000baseCR_Full_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_100000baseDR_Full_BIT, linkmodes);
}
if (caps & MAC_200000FD) {
__set_bit(ETHTOOL_LINK_MODE_200000baseKR4_Full_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_200000baseSR4_Full_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_200000baseLR4_ER4_FR4_Full_BIT,
linkmodes);
__set_bit(ETHTOOL_LINK_MODE_200000baseDR4_Full_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_200000baseKR2_Full_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_200000baseSR2_Full_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_200000baseLR2_ER2_FR2_Full_BIT,
linkmodes);
__set_bit(ETHTOOL_LINK_MODE_200000baseDR2_Full_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_200000baseCR2_Full_BIT, linkmodes);
}
if (caps & MAC_400000FD) {
__set_bit(ETHTOOL_LINK_MODE_400000baseKR8_Full_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_400000baseSR8_Full_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_400000baseLR8_ER8_FR8_Full_BIT,
linkmodes);
__set_bit(ETHTOOL_LINK_MODE_400000baseDR8_Full_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_400000baseCR8_Full_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_400000baseKR4_Full_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_400000baseSR4_Full_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_400000baseLR4_ER4_FR4_Full_BIT,
linkmodes);
__set_bit(ETHTOOL_LINK_MODE_400000baseDR4_Full_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_400000baseCR4_Full_BIT, linkmodes);
}
phy_caps_linkmodes(link_caps, linkmodes);
}
static struct {
unsigned long mask;
int speed;
unsigned int duplex;
} phylink_caps_params[] = {
{ MAC_400000FD, SPEED_400000, DUPLEX_FULL },
{ MAC_200000FD, SPEED_200000, DUPLEX_FULL },
{ MAC_100000FD, SPEED_100000, DUPLEX_FULL },
{ MAC_56000FD, SPEED_56000, DUPLEX_FULL },
{ MAC_50000FD, SPEED_50000, DUPLEX_FULL },
{ MAC_40000FD, SPEED_40000, DUPLEX_FULL },
{ MAC_25000FD, SPEED_25000, DUPLEX_FULL },
{ MAC_20000FD, SPEED_20000, DUPLEX_FULL },
{ MAC_10000FD, SPEED_10000, DUPLEX_FULL },
{ MAC_5000FD, SPEED_5000, DUPLEX_FULL },
{ MAC_2500FD, SPEED_2500, DUPLEX_FULL },
{ MAC_1000FD, SPEED_1000, DUPLEX_FULL },
{ MAC_1000HD, SPEED_1000, DUPLEX_HALF },
{ MAC_100FD, SPEED_100, DUPLEX_FULL },
{ MAC_100HD, SPEED_100, DUPLEX_HALF },
{ MAC_10FD, SPEED_10, DUPLEX_FULL },
{ MAC_10HD, SPEED_10, DUPLEX_HALF },
};
/**
* phylink_limit_mac_speed - limit the phylink_config to a maximum speed
* @config: pointer to a &struct phylink_config
@ -523,86 +424,12 @@ static unsigned long phylink_get_capabilities(phy_interface_t interface,
unsigned long mac_capabilities,
int rate_matching)
{
unsigned long link_caps = phy_caps_from_interface(interface);
int max_speed = phylink_interface_max_speed(interface);
unsigned long caps = MAC_SYM_PAUSE | MAC_ASYM_PAUSE;
unsigned long matched_caps = 0;
switch (interface) {
case PHY_INTERFACE_MODE_USXGMII:
caps |= MAC_10000FD | MAC_5000FD;
fallthrough;
case PHY_INTERFACE_MODE_10G_QXGMII:
caps |= MAC_2500FD;
fallthrough;
case PHY_INTERFACE_MODE_RGMII_TXID:
case PHY_INTERFACE_MODE_RGMII_RXID:
case PHY_INTERFACE_MODE_RGMII_ID:
case PHY_INTERFACE_MODE_RGMII:
case PHY_INTERFACE_MODE_PSGMII:
case PHY_INTERFACE_MODE_QSGMII:
case PHY_INTERFACE_MODE_QUSGMII:
case PHY_INTERFACE_MODE_SGMII:
case PHY_INTERFACE_MODE_GMII:
caps |= MAC_1000HD | MAC_1000FD;
fallthrough;
case PHY_INTERFACE_MODE_REVRMII:
case PHY_INTERFACE_MODE_RMII:
case PHY_INTERFACE_MODE_SMII:
case PHY_INTERFACE_MODE_REVMII:
case PHY_INTERFACE_MODE_MII:
caps |= MAC_10HD | MAC_10FD;
fallthrough;
case PHY_INTERFACE_MODE_100BASEX:
caps |= MAC_100HD | MAC_100FD;
break;
case PHY_INTERFACE_MODE_TBI:
case PHY_INTERFACE_MODE_MOCA:
case PHY_INTERFACE_MODE_RTBI:
case PHY_INTERFACE_MODE_1000BASEX:
caps |= MAC_1000HD;
fallthrough;
case PHY_INTERFACE_MODE_1000BASEKX:
case PHY_INTERFACE_MODE_TRGMII:
caps |= MAC_1000FD;
break;
case PHY_INTERFACE_MODE_2500BASEX:
caps |= MAC_2500FD;
break;
case PHY_INTERFACE_MODE_5GBASER:
caps |= MAC_5000FD;
break;
case PHY_INTERFACE_MODE_XGMII:
case PHY_INTERFACE_MODE_RXAUI:
case PHY_INTERFACE_MODE_XAUI:
case PHY_INTERFACE_MODE_10GBASER:
case PHY_INTERFACE_MODE_10GKR:
caps |= MAC_10000FD;
break;
case PHY_INTERFACE_MODE_25GBASER:
caps |= MAC_25000FD;
break;
case PHY_INTERFACE_MODE_XLGMII:
caps |= MAC_40000FD;
break;
case PHY_INTERFACE_MODE_INTERNAL:
caps |= ~0;
break;
case PHY_INTERFACE_MODE_NA:
case PHY_INTERFACE_MODE_MAX:
break;
}
caps |= phylink_link_caps_to_mac_caps(link_caps);
switch (rate_matching) {
case RATE_MATCH_OPEN_LOOP:
@ -801,12 +628,26 @@ static int phylink_validate(struct phylink *pl, unsigned long *supported,
return phylink_validate_mac_and_pcs(pl, supported, state);
}
static void phylink_fill_fixedlink_supported(unsigned long *supported)
{
linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, supported);
linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, supported);
linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, supported);
linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, supported);
linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, supported);
linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, supported);
linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, supported);
linkmode_set_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT, supported);
linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, supported);
}
static int phylink_parse_fixedlink(struct phylink *pl,
const struct fwnode_handle *fwnode)
{
__ETHTOOL_DECLARE_LINK_MODE_MASK(match) = { 0, };
__ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
const struct link_capabilities *c;
struct fwnode_handle *fixed_node;
const struct phy_setting *s;
struct gpio_desc *desc;
u32 speed;
int ret;
@ -874,12 +715,16 @@ static int phylink_parse_fixedlink(struct phylink *pl,
phylink_warn(pl, "fixed link specifies half duplex for %dMbps link?\n",
pl->link_config.speed);
linkmode_fill(pl->supported);
linkmode_zero(pl->supported);
phylink_fill_fixedlink_supported(pl->supported);
linkmode_copy(pl->link_config.advertising, pl->supported);
phylink_validate(pl, pl->supported, &pl->link_config);
s = phy_lookup_setting(pl->link_config.speed, pl->link_config.duplex,
pl->supported, true);
c = phy_caps_lookup(pl->link_config.speed, pl->link_config.duplex,
pl->supported, true);
if (c)
linkmode_and(match, pl->supported, c->linkmodes);
linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT, mask);
linkmode_set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, mask);
@ -888,9 +733,10 @@ static int phylink_parse_fixedlink(struct phylink *pl,
phylink_set(pl->supported, MII);
if (s) {
__set_bit(s->bit, pl->supported);
__set_bit(s->bit, pl->link_config.lp_advertising);
if (c) {
linkmode_or(pl->supported, pl->supported, match);
linkmode_or(pl->link_config.lp_advertising,
pl->link_config.lp_advertising, match);
} else {
phylink_warn(pl, "fixed link %s duplex %dMbps not recognised\n",
pl->link_config.duplex == DUPLEX_FULL ? "full" : "half",
@ -1878,21 +1724,20 @@ static int phylink_register_sfp(struct phylink *pl,
int phylink_set_fixed_link(struct phylink *pl,
const struct phylink_link_state *state)
{
const struct phy_setting *s;
const struct link_capabilities *c;
unsigned long *adv;
if (pl->cfg_link_an_mode != MLO_AN_PHY || !state ||
!test_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state))
return -EINVAL;
s = phy_lookup_setting(state->speed, state->duplex,
pl->supported, true);
if (!s)
c = phy_caps_lookup(state->speed, state->duplex,
pl->supported, true);
if (!c)
return -EINVAL;
adv = pl->link_config.advertising;
linkmode_zero(adv);
linkmode_set_bit(s->bit, adv);
linkmode_and(adv, pl->supported, c->linkmodes);
linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, adv);
pl->link_config.speed = state->speed;
@ -2852,8 +2697,8 @@ int phylink_ethtool_ksettings_set(struct phylink *pl,
const struct ethtool_link_ksettings *kset)
{
__ETHTOOL_DECLARE_LINK_MODE_MASK(support);
const struct link_capabilities *c;
struct phylink_link_state config;
const struct phy_setting *s;
ASSERT_RTNL();
@ -2896,23 +2741,23 @@ int phylink_ethtool_ksettings_set(struct phylink *pl,
/* Autonegotiation disabled, select a suitable speed and
* duplex.
*/
s = phy_lookup_setting(kset->base.speed, kset->base.duplex,
pl->supported, false);
if (!s)
c = phy_caps_lookup(kset->base.speed, kset->base.duplex,
pl->supported, false);
if (!c)
return -EINVAL;
/* If we have a fixed link, refuse to change link parameters.
* If the link parameters match, accept them but do nothing.
*/
if (pl->req_link_an_mode == MLO_AN_FIXED) {
if (s->speed != pl->link_config.speed ||
s->duplex != pl->link_config.duplex)
if (c->speed != pl->link_config.speed ||
c->duplex != pl->link_config.duplex)
return -EINVAL;
return 0;
}
config.speed = s->speed;
config.duplex = s->duplex;
config.speed = c->speed;
config.duplex = c->duplex;
break;
case AUTONEG_ENABLE:

View File

@ -210,6 +210,14 @@ static inline u8 *ethtool_rxfh_context_key(struct ethtool_rxfh_context *ctx)
void ethtool_rxfh_context_lost(struct net_device *dev, u32 context_id);
struct link_mode_info {
int speed;
u8 lanes;
u8 duplex;
};
extern const struct link_mode_info link_mode_params[];
/* declare a link mode bitmap */
#define __ETHTOOL_DECLARE_LINK_MODE_MASK(name) \
DECLARE_BITMAP(name, __ETHTOOL_LINK_MODE_MASK_NBITS)

View File

@ -1275,21 +1275,6 @@ const char *phy_rate_matching_to_str(int rate_matching);
int phy_interface_num_ports(phy_interface_t interface);
/* A structure for mapping a particular speed and duplex
* combination to a particular SUPPORTED and ADVERTISED value
*/
struct phy_setting {
u32 speed;
u8 duplex;
u8 bit;
};
const struct phy_setting *
phy_lookup_setting(int speed, int duplex, const unsigned long *mask,
bool exact);
size_t phy_speeds(unsigned int *speeds, size_t size,
unsigned long *mask);
/**
* phy_is_started - Convenience function to check whether PHY is started
* @phydev: The phy_device struct

View File

@ -424,6 +424,7 @@ const struct link_mode_info link_mode_params[] = {
__DEFINE_LINK_MODE_PARAMS(800000, VR4, Full),
};
static_assert(ARRAY_SIZE(link_mode_params) == __ETHTOOL_LINK_MODE_MASK_NBITS);
EXPORT_SYMBOL_GPL(link_mode_params);
const char netif_msg_class_names[][ETH_GSTRING_LEN] = {
[NETIF_MSG_DRV_BIT] = "drv",

View File

@ -15,12 +15,6 @@
#define __SOF_TIMESTAMPING_CNT (const_ilog2(SOF_TIMESTAMPING_LAST) + 1)
#define __HWTSTAMP_FLAG_CNT (const_ilog2(HWTSTAMP_FLAG_LAST) + 1)
struct link_mode_info {
int speed;
u8 lanes;
u8 duplex;
};
struct genl_info;
struct hwtstamp_provider_desc;
@ -33,7 +27,6 @@ tunable_strings[__ETHTOOL_TUNABLE_COUNT][ETH_GSTRING_LEN];
extern const char
phy_tunable_strings[__ETHTOOL_PHY_TUNABLE_COUNT][ETH_GSTRING_LEN];
extern const char link_mode_names[][ETH_GSTRING_LEN];
extern const struct link_mode_info link_mode_params[];
extern const char netif_msg_class_names[][ETH_GSTRING_LEN];
extern const char wol_mode_names[][ETH_GSTRING_LEN];
extern const char sof_timestamping_names[][ETH_GSTRING_LEN];