| // 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_80000, DUPLEX_FULL, {0} }, /* LINK_CAPA_80000FD */ | |
| { 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 */ | |
| { SPEED_1600000, DUPLEX_FULL, {0} }, /* LINK_CAPA_1600000FD */ | |
| }; | |
| 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_80000: return LINK_CAPA_80000FD; | |
| 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; | |
| case SPEED_1600000: return LINK_CAPA_1600000FD; | |
| } | |
| 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 __init 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]; | |
| /* Sanity check the linkmodes array for number of pairs */ | |
| if (linkmode->pairs < linkmode->min_pairs) { | |
| pr_err("Pairs count must not be under min_pairs for linkmode %d\n", | |
| i); | |
| return -EINVAL; | |
| } | |
| 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. | |
| * | |
| * Non-exact matches will try to return an exact speed and duplex match, but may | |
| * return matching capabilities with same speed but a different duplex. | |
| * | |
| * 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, *match = NULL, *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 (!match && lcap->speed <= speed) | |
| match = lcap; | |
| if (lcap->speed < speed) | |
| break; | |
| } | |
| } | |
| } | |
| if (!match && !exact) | |
| match = last; | |
| return match; | |
| } | |
| 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_MIILITE: | |
| link_caps |= BIT(LINK_CAPA_10FD) | 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_50GBASER: | |
| case PHY_INTERFACE_MODE_LAUI: | |
| link_caps |= BIT(LINK_CAPA_50000FD); | |
| break; | |
| case PHY_INTERFACE_MODE_100GBASEP: | |
| link_caps |= BIT(LINK_CAPA_100000FD); | |
| 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); | |
| /** | |
| * phy_caps_medium_get_supported() - Returns linkmodes supported on a given medium | |
| * @supported: After this call, contains all possible linkmodes on a given medium, | |
| * and with the given number of pairs, or less. | |
| * @medium: The medium to get the support from | |
| * @pairs: The number of pairs used on the given medium. Only relevant for modes | |
| * that support this notion, such as BaseT. Pass 0 if not applicable. | |
| * | |
| * If no match exists, the supported field is left untouched. | |
| */ | |
| void phy_caps_medium_get_supported(unsigned long *supported, | |
| enum ethtool_link_medium medium, | |
| int pairs) | |
| { | |
| int i; | |
| for (i = 0; i < __ETHTOOL_LINK_MODE_MASK_NBITS; i++) { | |
| /* Special bits such as Autoneg, Pause, Asym_pause, etc. are | |
| * set and will be masked away by the port parent. | |
| */ | |
| if (link_mode_params[i].mediums == BIT(ETHTOOL_LINK_MEDIUM_NONE)) { | |
| linkmode_set_bit(i, supported); | |
| continue; | |
| } | |
| /* If this medium matches, and had a non-zero min-pairs */ | |
| if (link_mode_params[i].mediums & BIT(medium) && | |
| (!link_mode_params[i].min_pairs || | |
| (link_mode_params[i].min_pairs <= pairs && | |
| link_mode_params[i].pairs >= pairs))) | |
| linkmode_set_bit(i, supported); | |
| } | |
| } | |
| EXPORT_SYMBOL_GPL(phy_caps_medium_get_supported); | |
| /** | |
| * phy_caps_mediums_from_linkmodes() - Get all mediums from a linkmodes list | |
| * @linkmodes: A bitset of linkmodes to get the mediums from | |
| * | |
| * Returns: A bitset of ETHTOOL_MEDIUM_XXX values corresponding to all medium | |
| * types in the linkmodes list | |
| */ | |
| u32 phy_caps_mediums_from_linkmodes(unsigned long *linkmodes) | |
| { | |
| const struct link_mode_info *linkmode; | |
| u32 mediums = 0; | |
| int i; | |
| for_each_set_bit(i, linkmodes, __ETHTOOL_LINK_MODE_MASK_NBITS) { | |
| linkmode = &link_mode_params[i]; | |
| mediums |= linkmode->mediums; | |
| } | |
| return mediums; | |
| } | |
| EXPORT_SYMBOL_GPL(phy_caps_mediums_from_linkmodes); |