@@ -370,13 +370,15 @@ static int phy_interface_speed(phy_interface_t interface, int link_speed)
* @linkmodes: ethtool linkmode mask (must be already initialised)
* @interface: phy interface mode defined by &typedef phy_interface_t
* @mac_capabilities: bitmask of MAC capabilities
+ * @rate_adaptation: type of rate adaptation being performed
*
* Set all possible pause, speed and duplex linkmodes in @linkmodes that
* are supported by the @interface mode and @mac_capabilities. @linkmodes
* must have been initialised previously.
*/
void phylink_get_linkmodes(unsigned long *linkmodes, phy_interface_t interface,
- unsigned long mac_capabilities)
+ unsigned long mac_capabilities,
+ enum rate_adaptation rate_adaptation)
{
unsigned long caps = MAC_SYM_PAUSE | MAC_ASYM_PAUSE;
@@ -451,7 +453,38 @@ void phylink_get_linkmodes(unsigned long *linkmodes, phy_interface_t interface,
break;
}
- phylink_caps_to_linkmodes(linkmodes, caps & mac_capabilities);
+ caps = caps & mac_capabilities;
+
+ switch (rate_adaptation) {
+ case RATE_ADAPT_NONE:
+ break;
+ case RATE_ADAPT_PAUSE: {
+ unsigned long adapted_caps;
+
+ /* The mac must support pause for this */
+ if (!(caps & MAC_ASYM_PAUSE))
+ goto open_loop;
+
+ /* Can't adapt if there's nothing slower */
+ if (__fls(caps) <= __fls(MAC_10))
+ break;
+
+ adapted_caps = GENMASK(__fls(caps), __fls(MAC_10HD));
+ /* We can't use pause frames in half-duplex mode */
+ adapted_caps &= ~(MAC_1000HD | MAC_100HD | MAC_10HD);
+ caps |= adapted_caps;
+ break;
+ }
+ case RATE_ADAPT_CRS:
+ /* TODO */
+ break;
+ case RATE_ADAPT_OPEN_LOOP:
+open_loop:
+ /* TODO */
+ break;
+ }
+
+ phylink_caps_to_linkmodes(linkmodes, caps);
}
EXPORT_SYMBOL_GPL(phylink_get_linkmodes);
@@ -473,7 +506,8 @@ void phylink_generic_validate(struct phylink_config *config,
phylink_set_port_modes(mask);
phylink_set(mask, Autoneg);
- phylink_get_linkmodes(mask, state->interface, config->mac_capabilities);
+ phylink_get_linkmodes(mask, state->interface, config->mac_capabilities,
+ state->rate_adaptation);
linkmode_and(supported, supported, mask);
linkmode_and(state->advertising, state->advertising, mask);
@@ -1462,6 +1496,7 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy,
config.interface = PHY_INTERFACE_MODE_NA;
else
config.interface = interface;
+ config.rate_adaptation = phy_get_rate_adaptation(phy, config.interface);
ret = phylink_validate(pl, supported, &config);
if (ret) {
@@ -65,6 +65,8 @@ static inline bool phylink_autoneg_inband(unsigned int mode)
* @link: true if the link is up.
* @an_enabled: true if autonegotiation is enabled/desired.
* @an_complete: true if autonegotiation has completed.
+ * @rate_adaptation: method of throttling @interface_speed to @speed, one of
+ * RATE_ADAPT_* constants.
*/
struct phylink_link_state {
__ETHTOOL_DECLARE_LINK_MODE_MASK(advertising);
@@ -74,6 +76,7 @@ struct phylink_link_state {
int link_speed;
int duplex;
int pause;
+ enum rate_adaptation rate_adaptation;
unsigned int link:1;
unsigned int an_enabled:1;
unsigned int an_complete:1;
@@ -523,7 +526,8 @@ void pcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
#endif
void phylink_get_linkmodes(unsigned long *linkmodes, phy_interface_t interface,
- unsigned long mac_capabilities);
+ unsigned long mac_capabilities,
+ enum rate_adaptation rate_adaptation);
void phylink_generic_validate(struct phylink_config *config,
unsigned long *supported,
struct phylink_link_state *state);
This adds support for adjusting the advertisement for pause-based rate adaptation. This may result in a lossy link, since the final link settings are not adjusted. Asymmetric pause support is necessary. It would be possible for a MAC supporting only symmetric pause to use pause-based rate adaptation, but only if pause reception was enabled as well. Signed-off-by: Sean Anderson <sean.anderson@seco.com> --- Changes in v3: - New drivers/net/phy/phylink.c | 41 ++++++++++++++++++++++++++++++++++++--- include/linux/phylink.h | 6 +++++- 2 files changed, 43 insertions(+), 4 deletions(-)