| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447 |
- // 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);
|