blob: 3b9d2a671b20efc194708cc1433e0adaaddcd67d [file] [log] [blame]
developerd5969672023-06-07 10:35:00 +08001diff --git a/drivers/net/phy/phy-core.c b/drivers/net/phy/phy-core.c
2index 8406412..96d9e1f 100644
3--- a/drivers/net/phy/phy-core.c
4+++ b/drivers/net/phy/phy-core.c
5@@ -62,6 +62,27 @@ const char *phy_duplex_to_str(unsigned int duplex)
6 }
7 EXPORT_SYMBOL_GPL(phy_duplex_to_str);
8
9+/**
10+ * phy_rate_matching_to_str - Return a string describing the rate matching
11+ *
12+ * @rate_matching: Type of rate matching to describe
13+ */
14+const char *phy_rate_matching_to_str(int rate_matching)
15+{
16+ switch (rate_matching) {
17+ case RATE_MATCH_NONE:
18+ return "none";
19+ case RATE_MATCH_PAUSE:
20+ return "pause";
21+ case RATE_MATCH_CRS:
22+ return "crs";
23+ case RATE_MATCH_OPEN_LOOP:
24+ return "open-loop";
25+ }
26+ return "Unsupported (update phy-core.c)";
27+}
28+EXPORT_SYMBOL_GPL(phy_rate_matching_to_str);
29+
30 /* A mapping of all SUPPORTED settings to speed/duplex. This table
31 * must be grouped by speed and sorted in descending match priority
32 * - iow, descending speed. */
33diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
34index e223f0d..6f2e4c7 100644
35--- a/drivers/net/phy/phy.c
36+++ b/drivers/net/phy/phy.c
37@@ -106,6 +106,33 @@ void phy_print_status(struct phy_device *phydev)
38 }
39 EXPORT_SYMBOL(phy_print_status);
40
41+/**
42+ * phy_get_rate_matching - determine if rate matching is supported
43+ * @phydev: The phy device to return rate matching for
44+ * @iface: The interface mode to use
45+ *
46+ * This determines the type of rate matching (if any) that @phy supports
47+ * using @iface. @iface may be %PHY_INTERFACE_MODE_NA to determine if any
48+ * interface supports rate matching.
49+ *
50+ * Return: The type of rate matching @phy supports for @iface, or
51+ * %RATE_MATCH_NONE.
52+ */
53+int phy_get_rate_matching(struct phy_device *phydev,
54+ phy_interface_t iface)
55+{
56+ int ret = RATE_MATCH_NONE;
57+
58+ if (phydev->drv->get_rate_matching) {
59+ mutex_lock(&phydev->lock);
60+ ret = phydev->drv->get_rate_matching(phydev, iface);
61+ mutex_unlock(&phydev->lock);
62+ }
63+
64+ return ret;
65+}
66+EXPORT_SYMBOL_GPL(phy_get_rate_matching);
67+
68 /**
69 * phy_clear_interrupt - Ack the phy device's interrupt
70 * @phydev: the phy_device struct
71@@ -380,6 +407,7 @@ void phy_ethtool_ksettings_get(struct phy_device *phydev,
72
73 cmd->base.speed = phydev->speed;
74 cmd->base.duplex = phydev->duplex;
75+ cmd->base.rate_matching = phydev->rate_matching;
76 if (phydev->interface == PHY_INTERFACE_MODE_MOCA)
77 cmd->base.port = PORT_BNC;
78 else
79diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
80index 6b63b98..949e3b8 100644
81--- a/drivers/net/phy/phylink.c
82+++ b/drivers/net/phy/phylink.c
83@@ -155,6 +155,64 @@ static const char *phylink_an_mode_str(unsigned int mode)
84 return mode < ARRAY_SIZE(modestr) ? modestr[mode] : "unknown";
85 }
86
87+/**
88+ * phylink_interface_max_speed() - get the maximum speed of a phy interface
89+ * @interface: phy interface mode defined by &typedef phy_interface_t
90+ *
91+ * Determine the maximum speed of a phy interface. This is intended to help
92+ * determine the correct speed to pass to the MAC when the phy is performing
93+ * rate matching.
94+ *
95+ * Return: The maximum speed of @interface
96+ */
97+static int phylink_interface_max_speed(phy_interface_t interface)
98+{
99+ switch (interface) {
100+ case PHY_INTERFACE_MODE_RMII:
101+ case PHY_INTERFACE_MODE_SMII:
102+ case PHY_INTERFACE_MODE_REVMII:
103+ case PHY_INTERFACE_MODE_MII:
104+ return SPEED_100;
105+
106+ case PHY_INTERFACE_MODE_TBI:
107+ case PHY_INTERFACE_MODE_MOCA:
108+ case PHY_INTERFACE_MODE_RTBI:
109+ case PHY_INTERFACE_MODE_1000BASEX:
110+ case PHY_INTERFACE_MODE_TRGMII:
111+ case PHY_INTERFACE_MODE_RGMII_TXID:
112+ case PHY_INTERFACE_MODE_RGMII_RXID:
113+ case PHY_INTERFACE_MODE_RGMII_ID:
114+ case PHY_INTERFACE_MODE_RGMII:
115+ case PHY_INTERFACE_MODE_QSGMII:
116+ case PHY_INTERFACE_MODE_SGMII:
117+ case PHY_INTERFACE_MODE_GMII:
118+ return SPEED_1000;
119+
120+ case PHY_INTERFACE_MODE_2500BASEX:
121+ return SPEED_2500;
122+
123+ case PHY_INTERFACE_MODE_5GBASER:
124+ return SPEED_5000;
125+
126+ case PHY_INTERFACE_MODE_XGMII:
127+ case PHY_INTERFACE_MODE_RXAUI:
128+ case PHY_INTERFACE_MODE_XAUI:
129+ case PHY_INTERFACE_MODE_10GKR:
130+ case PHY_INTERFACE_MODE_USXGMII:
131+ return SPEED_10000;
132+
133+ case PHY_INTERFACE_MODE_INTERNAL:
134+ case PHY_INTERFACE_MODE_NA:
135+ case PHY_INTERFACE_MODE_MAX:
136+ /* No idea! Garbage in, unknown out */
137+ return SPEED_UNKNOWN;
138+ }
139+
140+ /* If we get here, someone forgot to add an interface mode above */
141+ WARN_ON_ONCE(1);
142+ return SPEED_UNKNOWN;
143+}
144+
145 static int phylink_validate_mac_and_pcs(struct phylink *pl,
146 unsigned long *supported,
147 struct phylink_link_state *state)
148@@ -402,11 +460,12 @@ static void phylink_mac_config(struct phylink *pl,
149 const struct phylink_link_state *state)
150 {
151 phylink_dbg(pl,
152- "%s: mode=%s/%s/%s/%s adv=%*pb pause=%02x link=%u an=%u\n",
153+ "%s: mode=%s/%s/%s/%s/%s adv=%*pb pause=%02x link=%u an=%u\n",
154 __func__, phylink_an_mode_str(pl->cur_link_an_mode),
155 phy_modes(state->interface),
156 phy_speed_to_str(state->speed),
157 phy_duplex_to_str(state->duplex),
158+ phy_rate_matching_to_str(state->rate_matching),
159 __ETHTOOL_LINK_MODE_MASK_NBITS, state->advertising,
160 state->pause, state->link, state->an_enabled);
161
162@@ -499,6 +558,7 @@ static void phylink_mac_pcs_get_state(struct phylink *pl,
163 linkmode_zero(state->lp_advertising);
164 state->interface = pl->link_config.interface;
165 state->an_enabled = pl->link_config.an_enabled;
166+ state->rate_matching = pl->link_config.rate_matching;
167 if (state->an_enabled) {
168 state->speed = SPEED_UNKNOWN;
169 state->duplex = DUPLEX_UNKNOWN;
170@@ -614,9 +674,32 @@ static void phylink_link_up(struct phylink *pl,
171 {
172 struct net_device *ndev = pl->netdev;
173 int speed, duplex;
174+ bool rx_pause;
175
176 speed = link_state.speed;
177 duplex = link_state.duplex;
178+ rx_pause = !!(link_state.pause & MLO_PAUSE_RX);
179+
180+ switch (link_state.rate_matching) {
181+ case RATE_MATCH_PAUSE:
182+ /* The PHY is doing rate matchion from the media rate (in
183+ * the link_state) to the interface speed, and will send
184+ * pause frames to the MAC to limit its transmission speed.
185+ */
186+ speed = phylink_interface_max_speed(link_state.interface);
187+ duplex = DUPLEX_FULL;
188+ rx_pause = true;
189+ break;
190+
191+ case RATE_MATCH_CRS:
192+ /* The PHY is doing rate matchion from the media rate (in
193+ * the link_state) to the interface speed, and will cause
194+ * collisions to the MAC to limit its transmission speed.
195+ */
196+ speed = phylink_interface_max_speed(link_state.interface);
197+ duplex = DUPLEX_HALF;
198+ break;
199+ }
200
201 pl->cur_interface = link_state.interface;
202
203@@ -626,8 +709,7 @@ static void phylink_link_up(struct phylink *pl,
204
205 pl->mac_ops->mac_link_up(pl->config, pl->phydev, pl->cur_link_an_mode,
206 pl->cur_interface, speed, duplex,
207- !!(link_state.pause & MLO_PAUSE_TX),
208- !!(link_state.pause & MLO_PAUSE_RX));
209+ !!(link_state.pause & MLO_PAUSE_TX), rx_pause);
210
211 if (ndev)
212 netif_carrier_on(ndev);
213@@ -719,6 +801,17 @@ static void phylink_resolve(struct work_struct *w)
214 }
215 link_state.interface = pl->phy_state.interface;
216
217+ /* If we are doing rate matching, then the
218+ * link speed/duplex comes from the PHY
219+ */
220+ if (pl->phy_state.rate_matching) {
221+ link_state.rate_matching =
222+ pl->phy_state.rate_matching;
223+ link_state.speed = pl->phy_state.speed;
224+ link_state.duplex =
225+ pl->phy_state.duplex;
226+ }
227+
228 /* If we have a PHY, we need to update with
229 * the PHY flow control bits.
230 */
231@@ -935,6 +1028,7 @@ static void phylink_phy_change(struct phy_device *phydev, bool up,
232 mutex_lock(&pl->state_mutex);
233 pl->phy_state.speed = phydev->speed;
234 pl->phy_state.duplex = phydev->duplex;
235+ pl->phy_state.rate_matching = phydev->rate_matching;
236 pl->phy_state.pause = MLO_PAUSE_NONE;
237 if (phydev->pause)
238 pl->phy_state.pause |= MLO_PAUSE_SYM;
239@@ -946,10 +1040,11 @@ static void phylink_phy_change(struct phy_device *phydev, bool up,
240
241 phylink_run_resolve(pl);
242
243- phylink_dbg(pl, "phy link %s %s/%s/%s\n", up ? "up" : "down",
244+ phylink_dbg(pl, "phy link %s %s/%s/%s/%s\n", up ? "up" : "down",
245 phy_modes(phydev->interface),
246 phy_speed_to_str(phydev->speed),
247- phy_duplex_to_str(phydev->duplex));
248+ phy_duplex_to_str(phydev->duplex),
249+ phy_rate_matching_to_str(phydev->rate_matching));
250 }
251
252 static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy,
253@@ -971,6 +1066,12 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy,
254 memset(&config, 0, sizeof(config));
255 linkmode_copy(supported, phy->supported);
256 linkmode_copy(config.advertising, phy->advertising);
257+
258+ /* Check whether we would use rate matching for the proposed interface
259+ * mode.
260+ */
261+ config.rate_matching = phy_get_rate_matching(phy, interface);
262+
263 config.interface = interface;
264
265 ret = phylink_validate(pl, supported, &config);
266@@ -988,6 +1089,7 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy,
267 mutex_lock(&pl->state_mutex);
268 pl->phydev = phy;
269 pl->phy_state.interface = interface;
270+ pl->phy_state.rate_matching = RATE_MATCH_NONE;
271 linkmode_copy(pl->supported, supported);
272 linkmode_copy(pl->link_config.advertising, config.advertising);
273
274@@ -1348,8 +1450,10 @@ static void phylink_get_ksettings(const struct phylink_link_state *state,
275 {
276 phylink_merge_link_mode(kset->link_modes.advertising, state->advertising);
277 linkmode_copy(kset->link_modes.lp_advertising, state->lp_advertising);
278- kset->base.speed = state->speed;
279- kset->base.duplex = state->duplex;
280+ if (kset->base.rate_matching == RATE_MATCH_NONE) {
281+ kset->base.speed = state->speed;
282+ kset->base.duplex = state->duplex;
283+ }
284 kset->base.autoneg = state->an_enabled ? AUTONEG_ENABLE :
285 AUTONEG_DISABLE;
286 }
287diff --git a/include/linux/phy.h b/include/linux/phy.h
288index 4f2c105..df9fb90 100644
289--- a/include/linux/phy.h
290+++ b/include/linux/phy.h
291@@ -423,6 +423,8 @@ struct phy_device {
292 /* Interrupts are enabled */
293 unsigned interrupts:1;
294
295+ int rate_matching;
296+
297 enum phy_state state;
298
299 u32 dev_flags;
300@@ -544,6 +546,18 @@ struct phy_driver {
301 */
302 int (*get_features)(struct phy_device *phydev);
303
304+ /**
305+ * @get_rate_matching: Get the supported type of rate matching for a
306+ * particular phy interface. This is used by phy consumers to determine
307+ * whether to advertise lower-speed modes for that interface. It is
308+ * assumed that if a rate matching mode is supported on an interface,
309+ * then that interface's rate can be adapted to all slower link speeds
310+ * supported by the phy. If the interface is not supported, this should
311+ * return %RATE_MATCH_NONE.
312+ */
313+ int (*get_rate_matching)(struct phy_device *phydev,
314+ phy_interface_t iface);
315+
316 /* PHY Power Management */
317 int (*suspend)(struct phy_device *phydev);
318 int (*resume)(struct phy_device *phydev);
319@@ -704,6 +718,7 @@ struct phy_fixup {
320
321 const char *phy_speed_to_str(int speed);
322 const char *phy_duplex_to_str(unsigned int duplex);
323+const char *phy_rate_matching_to_str(int rate_matching);
324
325 /* A structure for mapping a particular speed and duplex
326 * combination to a particular SUPPORTED and ADVERTISED value
327@@ -1242,6 +1257,8 @@ int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd);
328 void phy_request_interrupt(struct phy_device *phydev);
329 void phy_free_interrupt(struct phy_device *phydev);
330 void phy_print_status(struct phy_device *phydev);
331+int phy_get_rate_matching(struct phy_device *phydev,
332+ phy_interface_t iface);
333 int phy_set_max_speed(struct phy_device *phydev, u32 max_speed);
334 void phy_remove_link_mode(struct phy_device *phydev, u32 link_mode);
335 void phy_advertise_supported(struct phy_device *phydev);
336diff --git a/include/linux/phylink.h b/include/linux/phylink.h
337index 48ff9fe..e1c022f 100644
338--- a/include/linux/phylink.h
339+++ b/include/linux/phylink.h
340@@ -38,6 +38,10 @@ static inline bool phylink_autoneg_inband(unsigned int mode)
341 * @speed: link speed, one of the SPEED_* constants.
342 * @duplex: link duplex mode, one of DUPLEX_* constants.
343 * @pause: link pause state, described by MLO_PAUSE_* constants.
344+ * @rate_matching: rate matching being performed, one of the RATE_MATCH_*
345+ * constants. If rate matching is taking place, then the speed/duplex of
346+ * the medium link mode (@speed and @duplex) and the speed/duplex of the phy
347+ * interface mode (@interface) are different.
348 * @link: true if the link is up.
349 * @an_enabled: true if autonegotiation is enabled/desired.
350 * @an_complete: true if autonegotiation has completed.
351@@ -49,6 +53,7 @@ struct phylink_link_state {
352 int speed;
353 int duplex;
354 int pause;
355+ int rate_matching;
356 unsigned int link:1;
357 unsigned int an_enabled:1;
358 unsigned int an_complete:1;
359diff --git a/include/uapi/linux/ethtool.h b/include/uapi/linux/ethtool.h
360index 8d465e5..ce41d2f 100644
361--- a/include/uapi/linux/ethtool.h
362+++ b/include/uapi/linux/ethtool.h
363@@ -1643,6 +1643,20 @@ static inline int ethtool_validate_duplex(__u8 duplex)
364 return 0;
365 }
366
367+/* These are used to throttle the rate of data on the phy interface when the
368+ * native speed of the interface is higher than the link speed. These should
369+ * not be used for phy interfaces which natively support multiple speeds (e.g.
370+ * MII or SGMII).
371+ */
372+/* No rate matching performed. */
373+#define RATE_MATCH_NONE 0
374+/* The phy sends pause frames to throttle the MAC. */
375+#define RATE_MATCH_PAUSE 1
376+/* The phy asserts CRS to prevent the MAC from transmitting. */
377+#define RATE_MATCH_CRS 2
378+/* The MAC is programmed with a sufficiently-large IPG. */
379+#define RATE_MATCH_OPEN_LOOP 3
380+
381 /* Which connector port. */
382 #define PORT_TP 0x00
383 #define PORT_AUI 0x01
384@@ -1832,6 +1846,7 @@ enum ethtool_reset_flags {
385 * autonegotiation; 0 if unknown or not applicable. Read-only.
386 * @transceiver: Used to distinguish different possible PHY types,
387 * reported consistently by PHYLIB. Read-only.
388+ * @rate_matching: Rate adaptation performed by the PHY
389 *
390 * If autonegotiation is disabled, the speed and @duplex represent the
391 * fixed link mode and are writable if the driver supports multiple
392@@ -1879,7 +1894,8 @@ struct ethtool_link_settings {
393 __u8 eth_tp_mdix_ctrl;
394 __s8 link_mode_masks_nwords;
395 __u8 transceiver;
396- __u8 reserved1[3];
397+ __u8 reserved1[2];
398+ __u8 rate_matching;
399 __u32 reserved[7];
400 __u32 link_mode_masks[0];
401 /* layout of link_mode_masks fields:
402diff --git a/net/core/ethtool.c b/net/core/ethtool.c
403index 9ae38c3..41a8b1d 100644
404--- a/net/core/ethtool.c
405+++ b/net/core/ethtool.c
406@@ -656,6 +656,7 @@ static int ethtool_get_link_ksettings(struct net_device *dev,
407 link_ksettings.base.cmd = ETHTOOL_GLINKSETTINGS;
408 link_ksettings.base.link_mode_masks_nwords
409 = __ETHTOOL_LINK_MODE_MASK_NU32;
410+ link_ksettings.base.rate_matching = RATE_MATCH_NONE;
411
412 return store_link_ksettings_for_user(useraddr, &link_ksettings);
413 }