1d8c838a5SMaxime Chevallier // SPDX-License-Identifier: GPL-2.0-or-later 2d8c838a5SMaxime Chevallier 3d8c838a5SMaxime Chevallier #include <linux/ethtool.h> 4d8c838a5SMaxime Chevallier #include <linux/linkmode.h> 5d8c838a5SMaxime Chevallier #include <linux/phy.h> 6d8c838a5SMaxime Chevallier 7d8c838a5SMaxime Chevallier #include "phy-caps.h" 8d8c838a5SMaxime Chevallier 9d8c838a5SMaxime Chevallier static struct link_capabilities link_caps[__LINK_CAPA_MAX] __ro_after_init = { 10d8c838a5SMaxime Chevallier { SPEED_10, DUPLEX_HALF, {0} }, /* LINK_CAPA_10HD */ 11d8c838a5SMaxime Chevallier { SPEED_10, DUPLEX_FULL, {0} }, /* LINK_CAPA_10FD */ 12d8c838a5SMaxime Chevallier { SPEED_100, DUPLEX_HALF, {0} }, /* LINK_CAPA_100HD */ 13d8c838a5SMaxime Chevallier { SPEED_100, DUPLEX_FULL, {0} }, /* LINK_CAPA_100FD */ 14d8c838a5SMaxime Chevallier { SPEED_1000, DUPLEX_HALF, {0} }, /* LINK_CAPA_1000HD */ 15d8c838a5SMaxime Chevallier { SPEED_1000, DUPLEX_FULL, {0} }, /* LINK_CAPA_1000FD */ 16d8c838a5SMaxime Chevallier { SPEED_2500, DUPLEX_FULL, {0} }, /* LINK_CAPA_2500FD */ 17d8c838a5SMaxime Chevallier { SPEED_5000, DUPLEX_FULL, {0} }, /* LINK_CAPA_5000FD */ 18d8c838a5SMaxime Chevallier { SPEED_10000, DUPLEX_FULL, {0} }, /* LINK_CAPA_10000FD */ 19d8c838a5SMaxime Chevallier { SPEED_20000, DUPLEX_FULL, {0} }, /* LINK_CAPA_20000FD */ 20d8c838a5SMaxime Chevallier { SPEED_25000, DUPLEX_FULL, {0} }, /* LINK_CAPA_25000FD */ 21d8c838a5SMaxime Chevallier { SPEED_40000, DUPLEX_FULL, {0} }, /* LINK_CAPA_40000FD */ 22d8c838a5SMaxime Chevallier { SPEED_50000, DUPLEX_FULL, {0} }, /* LINK_CAPA_50000FD */ 23d8c838a5SMaxime Chevallier { SPEED_56000, DUPLEX_FULL, {0} }, /* LINK_CAPA_56000FD */ 24d8c838a5SMaxime Chevallier { SPEED_100000, DUPLEX_FULL, {0} }, /* LINK_CAPA_100000FD */ 25d8c838a5SMaxime Chevallier { SPEED_200000, DUPLEX_FULL, {0} }, /* LINK_CAPA_200000FD */ 26d8c838a5SMaxime Chevallier { SPEED_400000, DUPLEX_FULL, {0} }, /* LINK_CAPA_400000FD */ 27d8c838a5SMaxime Chevallier { SPEED_800000, DUPLEX_FULL, {0} }, /* LINK_CAPA_800000FD */ 28d8c838a5SMaxime Chevallier }; 29d8c838a5SMaxime Chevallier 30d8c838a5SMaxime Chevallier static int speed_duplex_to_capa(int speed, unsigned int duplex) 31d8c838a5SMaxime Chevallier { 32d8c838a5SMaxime Chevallier if (duplex == DUPLEX_UNKNOWN || 33d8c838a5SMaxime Chevallier (speed > SPEED_1000 && duplex != DUPLEX_FULL)) 34d8c838a5SMaxime Chevallier return -EINVAL; 35d8c838a5SMaxime Chevallier 36d8c838a5SMaxime Chevallier switch (speed) { 37d8c838a5SMaxime Chevallier case SPEED_10: return duplex == DUPLEX_FULL ? 38d8c838a5SMaxime Chevallier LINK_CAPA_10FD : LINK_CAPA_10HD; 39d8c838a5SMaxime Chevallier case SPEED_100: return duplex == DUPLEX_FULL ? 40d8c838a5SMaxime Chevallier LINK_CAPA_100FD : LINK_CAPA_100HD; 41d8c838a5SMaxime Chevallier case SPEED_1000: return duplex == DUPLEX_FULL ? 42d8c838a5SMaxime Chevallier LINK_CAPA_1000FD : LINK_CAPA_1000HD; 43d8c838a5SMaxime Chevallier case SPEED_2500: return LINK_CAPA_2500FD; 44d8c838a5SMaxime Chevallier case SPEED_5000: return LINK_CAPA_5000FD; 45d8c838a5SMaxime Chevallier case SPEED_10000: return LINK_CAPA_10000FD; 46d8c838a5SMaxime Chevallier case SPEED_20000: return LINK_CAPA_20000FD; 47d8c838a5SMaxime Chevallier case SPEED_25000: return LINK_CAPA_25000FD; 48d8c838a5SMaxime Chevallier case SPEED_40000: return LINK_CAPA_40000FD; 49d8c838a5SMaxime Chevallier case SPEED_50000: return LINK_CAPA_50000FD; 50d8c838a5SMaxime Chevallier case SPEED_56000: return LINK_CAPA_56000FD; 51d8c838a5SMaxime Chevallier case SPEED_100000: return LINK_CAPA_100000FD; 52d8c838a5SMaxime Chevallier case SPEED_200000: return LINK_CAPA_200000FD; 53d8c838a5SMaxime Chevallier case SPEED_400000: return LINK_CAPA_400000FD; 54d8c838a5SMaxime Chevallier case SPEED_800000: return LINK_CAPA_800000FD; 55d8c838a5SMaxime Chevallier } 56d8c838a5SMaxime Chevallier 57d8c838a5SMaxime Chevallier return -EINVAL; 58d8c838a5SMaxime Chevallier } 59d8c838a5SMaxime Chevallier 608c8c4a87SMaxime Chevallier #define for_each_link_caps_asc_speed(cap) \ 618c8c4a87SMaxime Chevallier for (cap = link_caps; cap < &link_caps[__LINK_CAPA_MAX]; cap++) 628c8c4a87SMaxime Chevallier 634823ed06SMaxime Chevallier #define for_each_link_caps_desc_speed(cap) \ 644823ed06SMaxime Chevallier for (cap = &link_caps[__LINK_CAPA_MAX - 1]; cap >= link_caps; cap--) 654823ed06SMaxime Chevallier 66d8c838a5SMaxime Chevallier /** 67d8c838a5SMaxime Chevallier * phy_caps_init() - Initializes the link_caps array from the link_mode_params. 68d8c838a5SMaxime Chevallier * 69d8c838a5SMaxime Chevallier * Returns: 0 if phy caps init was successful, -EINVAL if we found an 70d8c838a5SMaxime Chevallier * unexpected linkmode setting that requires LINK_CAPS update. 71d8c838a5SMaxime Chevallier * 72d8c838a5SMaxime Chevallier */ 73d8c838a5SMaxime Chevallier int phy_caps_init(void) 74d8c838a5SMaxime Chevallier { 75d8c838a5SMaxime Chevallier const struct link_mode_info *linkmode; 76d8c838a5SMaxime Chevallier int i, capa; 77d8c838a5SMaxime Chevallier 78d8c838a5SMaxime Chevallier /* Fill the caps array from net/ethtool/common.c */ 79d8c838a5SMaxime Chevallier for (i = 0; i < __ETHTOOL_LINK_MODE_MASK_NBITS; i++) { 80d8c838a5SMaxime Chevallier linkmode = &link_mode_params[i]; 81d8c838a5SMaxime Chevallier capa = speed_duplex_to_capa(linkmode->speed, linkmode->duplex); 82d8c838a5SMaxime Chevallier 83d8c838a5SMaxime Chevallier if (capa < 0) { 84d8c838a5SMaxime Chevallier if (linkmode->speed != SPEED_UNKNOWN) { 85d8c838a5SMaxime Chevallier pr_err("Unknown speed %d, please update LINK_CAPS\n", 86d8c838a5SMaxime Chevallier linkmode->speed); 87d8c838a5SMaxime Chevallier return -EINVAL; 88d8c838a5SMaxime Chevallier } 89d8c838a5SMaxime Chevallier continue; 90d8c838a5SMaxime Chevallier } 91d8c838a5SMaxime Chevallier 92d8c838a5SMaxime Chevallier __set_bit(i, link_caps[capa].linkmodes); 93d8c838a5SMaxime Chevallier } 94d8c838a5SMaxime Chevallier 95d8c838a5SMaxime Chevallier return 0; 96d8c838a5SMaxime Chevallier } 978c8c4a87SMaxime Chevallier 988c8c4a87SMaxime Chevallier /** 998c8c4a87SMaxime Chevallier * phy_caps_speeds() - Fill an array of supported SPEED_* values for given modes 1008c8c4a87SMaxime Chevallier * @speeds: Output array to store the speeds list into 1018c8c4a87SMaxime Chevallier * @size: Size of the output array 1028c8c4a87SMaxime Chevallier * @linkmodes: Linkmodes to get the speeds from 1038c8c4a87SMaxime Chevallier * 1048c8c4a87SMaxime Chevallier * Fills the speeds array with all possible speeds that can be achieved with 1058c8c4a87SMaxime Chevallier * the specified linkmodes. 1068c8c4a87SMaxime Chevallier * 1078c8c4a87SMaxime Chevallier * Returns: The number of speeds filled into the array. If the input array isn't 1088c8c4a87SMaxime Chevallier * big enough to store all speeds, fill it as much as possible. 1098c8c4a87SMaxime Chevallier */ 1108c8c4a87SMaxime Chevallier size_t phy_caps_speeds(unsigned int *speeds, size_t size, 1118c8c4a87SMaxime Chevallier unsigned long *linkmodes) 1128c8c4a87SMaxime Chevallier { 1138c8c4a87SMaxime Chevallier struct link_capabilities *lcap; 1148c8c4a87SMaxime Chevallier size_t count = 0; 1158c8c4a87SMaxime Chevallier 1168c8c4a87SMaxime Chevallier for_each_link_caps_asc_speed(lcap) { 1178c8c4a87SMaxime Chevallier if (linkmode_intersects(lcap->linkmodes, linkmodes) && 1188c8c4a87SMaxime Chevallier (count == 0 || speeds[count - 1] != lcap->speed)) { 1198c8c4a87SMaxime Chevallier speeds[count++] = lcap->speed; 1208c8c4a87SMaxime Chevallier if (count >= size) 1218c8c4a87SMaxime Chevallier break; 1228c8c4a87SMaxime Chevallier } 1238c8c4a87SMaxime Chevallier } 1248c8c4a87SMaxime Chevallier 1258c8c4a87SMaxime Chevallier return count; 1268c8c4a87SMaxime Chevallier } 1274823ed06SMaxime Chevallier 1284823ed06SMaxime Chevallier /** 129dbcd85b0SMaxime Chevallier * phy_caps_lookup_by_linkmode() - Lookup the fastest matching link_capabilities 130dbcd85b0SMaxime Chevallier * @linkmodes: Linkmodes to match against 131dbcd85b0SMaxime Chevallier * 132dbcd85b0SMaxime Chevallier * Returns: The highest-speed link_capabilities that intersects the given 133dbcd85b0SMaxime Chevallier * linkmodes. In case several DUPLEX_ options exist at that speed, 134dbcd85b0SMaxime Chevallier * DUPLEX_FULL is matched first. NULL is returned if no match. 135dbcd85b0SMaxime Chevallier */ 136dbcd85b0SMaxime Chevallier const struct link_capabilities * 137dbcd85b0SMaxime Chevallier phy_caps_lookup_by_linkmode(const unsigned long *linkmodes) 138dbcd85b0SMaxime Chevallier { 139dbcd85b0SMaxime Chevallier struct link_capabilities *lcap; 140dbcd85b0SMaxime Chevallier 141dbcd85b0SMaxime Chevallier for_each_link_caps_desc_speed(lcap) 142dbcd85b0SMaxime Chevallier if (linkmode_intersects(lcap->linkmodes, linkmodes)) 143dbcd85b0SMaxime Chevallier return lcap; 144dbcd85b0SMaxime Chevallier 145dbcd85b0SMaxime Chevallier return NULL; 146dbcd85b0SMaxime Chevallier } 147dbcd85b0SMaxime Chevallier 148dbcd85b0SMaxime Chevallier /** 149dbcd85b0SMaxime Chevallier * phy_caps_lookup_by_linkmode_rev() - Lookup the slowest matching link_capabilities 150dbcd85b0SMaxime Chevallier * @linkmodes: Linkmodes to match against 151dbcd85b0SMaxime Chevallier * @fdx_only: Full duplex match only when set 152dbcd85b0SMaxime Chevallier * 153dbcd85b0SMaxime Chevallier * Returns: The lowest-speed link_capabilities that intersects the given 154dbcd85b0SMaxime Chevallier * linkmodes. When set, fdx_only will ignore half-duplex matches. 155dbcd85b0SMaxime Chevallier * NULL is returned if no match. 156dbcd85b0SMaxime Chevallier */ 157dbcd85b0SMaxime Chevallier const struct link_capabilities * 158dbcd85b0SMaxime Chevallier phy_caps_lookup_by_linkmode_rev(const unsigned long *linkmodes, bool fdx_only) 159dbcd85b0SMaxime Chevallier { 160dbcd85b0SMaxime Chevallier struct link_capabilities *lcap; 161dbcd85b0SMaxime Chevallier 162dbcd85b0SMaxime Chevallier for_each_link_caps_asc_speed(lcap) { 163dbcd85b0SMaxime Chevallier if (fdx_only && lcap->duplex != DUPLEX_FULL) 164dbcd85b0SMaxime Chevallier continue; 165dbcd85b0SMaxime Chevallier 166dbcd85b0SMaxime Chevallier if (linkmode_intersects(lcap->linkmodes, linkmodes)) 167dbcd85b0SMaxime Chevallier return lcap; 168dbcd85b0SMaxime Chevallier } 169dbcd85b0SMaxime Chevallier 170dbcd85b0SMaxime Chevallier return NULL; 171dbcd85b0SMaxime Chevallier } 172dbcd85b0SMaxime Chevallier 173dbcd85b0SMaxime Chevallier /** 174fc81e257SMaxime Chevallier * phy_caps_lookup() - Lookup capabilities by speed/duplex that matches a mask 175fc81e257SMaxime Chevallier * @speed: Speed to match 176fc81e257SMaxime Chevallier * @duplex: Duplex to match 177fc81e257SMaxime Chevallier * @supported: Mask of linkmodes to match 178fc81e257SMaxime Chevallier * @exact: Perform an exact match or not. 179fc81e257SMaxime Chevallier * 180fc81e257SMaxime Chevallier * Lookup a link_capabilities entry that intersect the supported linkmodes mask, 181fc81e257SMaxime Chevallier * and that matches the passed speed and duplex. 182fc81e257SMaxime Chevallier * 183fc81e257SMaxime Chevallier * When @exact is set, an exact match is performed on speed and duplex, meaning 184fc81e257SMaxime Chevallier * that if the linkmodes for the given speed and duplex intersect the supported 185fc81e257SMaxime Chevallier * mask, this capability is returned, otherwise we don't have a match and return 186fc81e257SMaxime Chevallier * NULL. 187fc81e257SMaxime Chevallier * 188fc81e257SMaxime Chevallier * When @exact is not set, we return either an exact match, or matching capabilities 189fc81e257SMaxime Chevallier * at lower speed, or the lowest matching speed, or NULL. 190fc81e257SMaxime Chevallier * 191fc81e257SMaxime Chevallier * Returns: a matched link_capabilities according to the above process, NULL 192fc81e257SMaxime Chevallier * otherwise. 193fc81e257SMaxime Chevallier */ 194fc81e257SMaxime Chevallier const struct link_capabilities * 195fc81e257SMaxime Chevallier phy_caps_lookup(int speed, unsigned int duplex, const unsigned long *supported, 196fc81e257SMaxime Chevallier bool exact) 197fc81e257SMaxime Chevallier { 198fc81e257SMaxime Chevallier const struct link_capabilities *lcap, *last = NULL; 199fc81e257SMaxime Chevallier 200fc81e257SMaxime Chevallier for_each_link_caps_desc_speed(lcap) { 201fc81e257SMaxime Chevallier if (linkmode_intersects(lcap->linkmodes, supported)) { 202fc81e257SMaxime Chevallier last = lcap; 203fc81e257SMaxime Chevallier /* exact match on speed and duplex*/ 204fc81e257SMaxime Chevallier if (lcap->speed == speed && lcap->duplex == duplex) { 205fc81e257SMaxime Chevallier return lcap; 206fc81e257SMaxime Chevallier } else if (!exact) { 207fc81e257SMaxime Chevallier if (lcap->speed <= speed) 208fc81e257SMaxime Chevallier return lcap; 209fc81e257SMaxime Chevallier } 210fc81e257SMaxime Chevallier } 211fc81e257SMaxime Chevallier } 212fc81e257SMaxime Chevallier 213fc81e257SMaxime Chevallier if (!exact) 214fc81e257SMaxime Chevallier return last; 215fc81e257SMaxime Chevallier 216fc81e257SMaxime Chevallier return NULL; 217fc81e257SMaxime Chevallier } 218fc81e257SMaxime Chevallier EXPORT_SYMBOL_GPL(phy_caps_lookup); 219fc81e257SMaxime Chevallier 220fc81e257SMaxime Chevallier /** 2214823ed06SMaxime Chevallier * phy_caps_linkmode_max_speed() - Clamp a linkmodes set to a max speed 2224823ed06SMaxime Chevallier * @max_speed: Speed limit for the linkmode set 2234823ed06SMaxime Chevallier * @linkmodes: Linkmodes to limit 2244823ed06SMaxime Chevallier */ 2254823ed06SMaxime Chevallier void phy_caps_linkmode_max_speed(u32 max_speed, unsigned long *linkmodes) 2264823ed06SMaxime Chevallier { 2274823ed06SMaxime Chevallier struct link_capabilities *lcap; 2284823ed06SMaxime Chevallier 2294823ed06SMaxime Chevallier for_each_link_caps_desc_speed(lcap) 2304823ed06SMaxime Chevallier if (lcap->speed > max_speed) 2314823ed06SMaxime Chevallier linkmode_andnot(linkmodes, linkmodes, lcap->linkmodes); 2324823ed06SMaxime Chevallier else 2334823ed06SMaxime Chevallier break; 2344823ed06SMaxime Chevallier } 23587b22ce3SMaxime Chevallier 23687b22ce3SMaxime Chevallier /** 23787b22ce3SMaxime Chevallier * phy_caps_valid() - Validate a linkmodes set agains given speed and duplex 23887b22ce3SMaxime Chevallier * @speed: input speed to validate 23987b22ce3SMaxime Chevallier * @duplex: input duplex to validate. Passing DUPLEX_UNKNOWN is always not valid 24087b22ce3SMaxime Chevallier * @linkmodes: The linkmodes to validate 24187b22ce3SMaxime Chevallier * 24287b22ce3SMaxime Chevallier * Returns: True if at least one of the linkmodes in @linkmodes can function at 24387b22ce3SMaxime Chevallier * the given speed and duplex, false otherwise. 24487b22ce3SMaxime Chevallier */ 24587b22ce3SMaxime Chevallier bool phy_caps_valid(int speed, int duplex, const unsigned long *linkmodes) 24687b22ce3SMaxime Chevallier { 24787b22ce3SMaxime Chevallier int capa = speed_duplex_to_capa(speed, duplex); 24887b22ce3SMaxime Chevallier 24987b22ce3SMaxime Chevallier if (capa < 0) 25087b22ce3SMaxime Chevallier return false; 25187b22ce3SMaxime Chevallier 25287b22ce3SMaxime Chevallier return linkmode_intersects(link_caps[capa].linkmodes, linkmodes); 25387b22ce3SMaxime Chevallier } 254*4ca5b8a2SMaxime Chevallier 255*4ca5b8a2SMaxime Chevallier /** 256*4ca5b8a2SMaxime Chevallier * phy_caps_linkmodes() - Convert a bitfield of capabilities into linkmodes 257*4ca5b8a2SMaxime Chevallier * @caps: The list of caps, each bit corresponding to a LINK_CAPA value 258*4ca5b8a2SMaxime Chevallier * @linkmodes: The set of linkmodes to fill. Must be previously initialized. 259*4ca5b8a2SMaxime Chevallier */ 260*4ca5b8a2SMaxime Chevallier void phy_caps_linkmodes(unsigned long caps, unsigned long *linkmodes) 261*4ca5b8a2SMaxime Chevallier { 262*4ca5b8a2SMaxime Chevallier unsigned long capa; 263*4ca5b8a2SMaxime Chevallier 264*4ca5b8a2SMaxime Chevallier for_each_set_bit(capa, &caps, __LINK_CAPA_MAX) 265*4ca5b8a2SMaxime Chevallier linkmode_or(linkmodes, linkmodes, link_caps[capa].linkmodes); 266*4ca5b8a2SMaxime Chevallier } 267*4ca5b8a2SMaxime Chevallier EXPORT_SYMBOL_GPL(phy_caps_linkmodes); 268