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 60*8c8c4a87SMaxime Chevallier #define for_each_link_caps_asc_speed(cap) \ 61*8c8c4a87SMaxime Chevallier for (cap = link_caps; cap < &link_caps[__LINK_CAPA_MAX]; cap++) 62*8c8c4a87SMaxime Chevallier 63d8c838a5SMaxime Chevallier /** 64d8c838a5SMaxime Chevallier * phy_caps_init() - Initializes the link_caps array from the link_mode_params. 65d8c838a5SMaxime Chevallier * 66d8c838a5SMaxime Chevallier * Returns: 0 if phy caps init was successful, -EINVAL if we found an 67d8c838a5SMaxime Chevallier * unexpected linkmode setting that requires LINK_CAPS update. 68d8c838a5SMaxime Chevallier * 69d8c838a5SMaxime Chevallier */ 70d8c838a5SMaxime Chevallier int phy_caps_init(void) 71d8c838a5SMaxime Chevallier { 72d8c838a5SMaxime Chevallier const struct link_mode_info *linkmode; 73d8c838a5SMaxime Chevallier int i, capa; 74d8c838a5SMaxime Chevallier 75d8c838a5SMaxime Chevallier /* Fill the caps array from net/ethtool/common.c */ 76d8c838a5SMaxime Chevallier for (i = 0; i < __ETHTOOL_LINK_MODE_MASK_NBITS; i++) { 77d8c838a5SMaxime Chevallier linkmode = &link_mode_params[i]; 78d8c838a5SMaxime Chevallier capa = speed_duplex_to_capa(linkmode->speed, linkmode->duplex); 79d8c838a5SMaxime Chevallier 80d8c838a5SMaxime Chevallier if (capa < 0) { 81d8c838a5SMaxime Chevallier if (linkmode->speed != SPEED_UNKNOWN) { 82d8c838a5SMaxime Chevallier pr_err("Unknown speed %d, please update LINK_CAPS\n", 83d8c838a5SMaxime Chevallier linkmode->speed); 84d8c838a5SMaxime Chevallier return -EINVAL; 85d8c838a5SMaxime Chevallier } 86d8c838a5SMaxime Chevallier continue; 87d8c838a5SMaxime Chevallier } 88d8c838a5SMaxime Chevallier 89d8c838a5SMaxime Chevallier __set_bit(i, link_caps[capa].linkmodes); 90d8c838a5SMaxime Chevallier } 91d8c838a5SMaxime Chevallier 92d8c838a5SMaxime Chevallier return 0; 93d8c838a5SMaxime Chevallier } 94*8c8c4a87SMaxime Chevallier 95*8c8c4a87SMaxime Chevallier /** 96*8c8c4a87SMaxime Chevallier * phy_caps_speeds() - Fill an array of supported SPEED_* values for given modes 97*8c8c4a87SMaxime Chevallier * @speeds: Output array to store the speeds list into 98*8c8c4a87SMaxime Chevallier * @size: Size of the output array 99*8c8c4a87SMaxime Chevallier * @linkmodes: Linkmodes to get the speeds from 100*8c8c4a87SMaxime Chevallier * 101*8c8c4a87SMaxime Chevallier * Fills the speeds array with all possible speeds that can be achieved with 102*8c8c4a87SMaxime Chevallier * the specified linkmodes. 103*8c8c4a87SMaxime Chevallier * 104*8c8c4a87SMaxime Chevallier * Returns: The number of speeds filled into the array. If the input array isn't 105*8c8c4a87SMaxime Chevallier * big enough to store all speeds, fill it as much as possible. 106*8c8c4a87SMaxime Chevallier */ 107*8c8c4a87SMaxime Chevallier size_t phy_caps_speeds(unsigned int *speeds, size_t size, 108*8c8c4a87SMaxime Chevallier unsigned long *linkmodes) 109*8c8c4a87SMaxime Chevallier { 110*8c8c4a87SMaxime Chevallier struct link_capabilities *lcap; 111*8c8c4a87SMaxime Chevallier size_t count = 0; 112*8c8c4a87SMaxime Chevallier 113*8c8c4a87SMaxime Chevallier for_each_link_caps_asc_speed(lcap) { 114*8c8c4a87SMaxime Chevallier if (linkmode_intersects(lcap->linkmodes, linkmodes) && 115*8c8c4a87SMaxime Chevallier (count == 0 || speeds[count - 1] != lcap->speed)) { 116*8c8c4a87SMaxime Chevallier speeds[count++] = lcap->speed; 117*8c8c4a87SMaxime Chevallier if (count >= size) 118*8c8c4a87SMaxime Chevallier break; 119*8c8c4a87SMaxime Chevallier } 120*8c8c4a87SMaxime Chevallier } 121*8c8c4a87SMaxime Chevallier 122*8c8c4a87SMaxime Chevallier return count; 123*8c8c4a87SMaxime Chevallier } 124