xref: /linux/drivers/net/phy/phy_caps.c (revision dbcd85b05c5ba520b75dbf51d0b48b5a366b6c68)
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 /**
129*dbcd85b0SMaxime Chevallier  * phy_caps_lookup_by_linkmode() - Lookup the fastest matching link_capabilities
130*dbcd85b0SMaxime Chevallier  * @linkmodes: Linkmodes to match against
131*dbcd85b0SMaxime Chevallier  *
132*dbcd85b0SMaxime Chevallier  * Returns: The highest-speed link_capabilities that intersects the given
133*dbcd85b0SMaxime Chevallier  *	    linkmodes. In case several DUPLEX_ options exist at that speed,
134*dbcd85b0SMaxime Chevallier  *	    DUPLEX_FULL is matched first. NULL is returned if no match.
135*dbcd85b0SMaxime Chevallier  */
136*dbcd85b0SMaxime Chevallier const struct link_capabilities *
137*dbcd85b0SMaxime Chevallier phy_caps_lookup_by_linkmode(const unsigned long *linkmodes)
138*dbcd85b0SMaxime Chevallier {
139*dbcd85b0SMaxime Chevallier 	struct link_capabilities *lcap;
140*dbcd85b0SMaxime Chevallier 
141*dbcd85b0SMaxime Chevallier 	for_each_link_caps_desc_speed(lcap)
142*dbcd85b0SMaxime Chevallier 		if (linkmode_intersects(lcap->linkmodes, linkmodes))
143*dbcd85b0SMaxime Chevallier 			return lcap;
144*dbcd85b0SMaxime Chevallier 
145*dbcd85b0SMaxime Chevallier 	return NULL;
146*dbcd85b0SMaxime Chevallier }
147*dbcd85b0SMaxime Chevallier 
148*dbcd85b0SMaxime Chevallier /**
149*dbcd85b0SMaxime Chevallier  * phy_caps_lookup_by_linkmode_rev() - Lookup the slowest matching link_capabilities
150*dbcd85b0SMaxime Chevallier  * @linkmodes: Linkmodes to match against
151*dbcd85b0SMaxime Chevallier  * @fdx_only: Full duplex match only when set
152*dbcd85b0SMaxime Chevallier  *
153*dbcd85b0SMaxime Chevallier  * Returns: The lowest-speed link_capabilities that intersects the given
154*dbcd85b0SMaxime Chevallier  *	    linkmodes. When set, fdx_only will ignore half-duplex matches.
155*dbcd85b0SMaxime Chevallier  *	    NULL is returned if no match.
156*dbcd85b0SMaxime Chevallier  */
157*dbcd85b0SMaxime Chevallier const struct link_capabilities *
158*dbcd85b0SMaxime Chevallier phy_caps_lookup_by_linkmode_rev(const unsigned long *linkmodes, bool fdx_only)
159*dbcd85b0SMaxime Chevallier {
160*dbcd85b0SMaxime Chevallier 	struct link_capabilities *lcap;
161*dbcd85b0SMaxime Chevallier 
162*dbcd85b0SMaxime Chevallier 	for_each_link_caps_asc_speed(lcap) {
163*dbcd85b0SMaxime Chevallier 		if (fdx_only && lcap->duplex != DUPLEX_FULL)
164*dbcd85b0SMaxime Chevallier 			continue;
165*dbcd85b0SMaxime Chevallier 
166*dbcd85b0SMaxime Chevallier 		if (linkmode_intersects(lcap->linkmodes, linkmodes))
167*dbcd85b0SMaxime Chevallier 			return lcap;
168*dbcd85b0SMaxime Chevallier 	}
169*dbcd85b0SMaxime Chevallier 
170*dbcd85b0SMaxime Chevallier 	return NULL;
171*dbcd85b0SMaxime Chevallier }
172*dbcd85b0SMaxime Chevallier 
173*dbcd85b0SMaxime Chevallier /**
1744823ed06SMaxime Chevallier  * phy_caps_linkmode_max_speed() - Clamp a linkmodes set to a max speed
1754823ed06SMaxime Chevallier  * @max_speed: Speed limit for the linkmode set
1764823ed06SMaxime Chevallier  * @linkmodes: Linkmodes to limit
1774823ed06SMaxime Chevallier  */
1784823ed06SMaxime Chevallier void phy_caps_linkmode_max_speed(u32 max_speed, unsigned long *linkmodes)
1794823ed06SMaxime Chevallier {
1804823ed06SMaxime Chevallier 	struct link_capabilities *lcap;
1814823ed06SMaxime Chevallier 
1824823ed06SMaxime Chevallier 	for_each_link_caps_desc_speed(lcap)
1834823ed06SMaxime Chevallier 		if (lcap->speed > max_speed)
1844823ed06SMaxime Chevallier 			linkmode_andnot(linkmodes, linkmodes, lcap->linkmodes);
1854823ed06SMaxime Chevallier 		else
1864823ed06SMaxime Chevallier 			break;
1874823ed06SMaxime Chevallier }
18887b22ce3SMaxime Chevallier 
18987b22ce3SMaxime Chevallier /**
19087b22ce3SMaxime Chevallier  * phy_caps_valid() - Validate a linkmodes set agains given speed and duplex
19187b22ce3SMaxime Chevallier  * @speed: input speed to validate
19287b22ce3SMaxime Chevallier  * @duplex: input duplex to validate. Passing DUPLEX_UNKNOWN is always not valid
19387b22ce3SMaxime Chevallier  * @linkmodes: The linkmodes to validate
19487b22ce3SMaxime Chevallier  *
19587b22ce3SMaxime Chevallier  * Returns: True if at least one of the linkmodes in @linkmodes can function at
19687b22ce3SMaxime Chevallier  *          the given speed and duplex, false otherwise.
19787b22ce3SMaxime Chevallier  */
19887b22ce3SMaxime Chevallier bool phy_caps_valid(int speed, int duplex, const unsigned long *linkmodes)
19987b22ce3SMaxime Chevallier {
20087b22ce3SMaxime Chevallier 	int capa = speed_duplex_to_capa(speed, duplex);
20187b22ce3SMaxime Chevallier 
20287b22ce3SMaxime Chevallier 	if (capa < 0)
20387b22ce3SMaxime Chevallier 		return false;
20487b22ce3SMaxime Chevallier 
20587b22ce3SMaxime Chevallier 	return linkmode_intersects(link_caps[capa].linkmodes, linkmodes);
20687b22ce3SMaxime Chevallier }
207