diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index 14c7d73790b45627ec12a4b24bb024adf5527278..6da245dacca49e5a3d2cd828a8196857afe3c957 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -166,9 +166,45 @@ static const char *phylink_an_mode_str(unsigned int mode) return mode < ARRAY_SIZE(modestr) ? modestr[mode] : "unknown"; } +static int phylink_validate_any(struct phylink *pl, unsigned long *supported, + struct phylink_link_state *state) +{ + __ETHTOOL_DECLARE_LINK_MODE_MASK(all_adv) = { 0, }; + __ETHTOOL_DECLARE_LINK_MODE_MASK(all_s) = { 0, }; + __ETHTOOL_DECLARE_LINK_MODE_MASK(s); + struct phylink_link_state t; + int intf; + + for (intf = 0; intf < PHY_INTERFACE_MODE_MAX; intf++) { + if (test_bit(intf, pl->config->supported_interfaces)) { + linkmode_copy(s, supported); + + t = *state; + t.interface = intf; + pl->mac_ops->validate(pl->config, s, &t); + linkmode_or(all_s, all_s, s); + linkmode_or(all_adv, all_adv, t.advertising); + } + } + + linkmode_copy(supported, all_s); + linkmode_copy(state->advertising, all_adv); + + return phylink_is_empty_linkmode(supported) ? -EINVAL : 0; +} + static int phylink_validate(struct phylink *pl, unsigned long *supported, struct phylink_link_state *state) { + if (!phy_interface_empty(pl->config->supported_interfaces)) { + if (state->interface == PHY_INTERFACE_MODE_NA) + return phylink_validate_any(pl, supported, state); + + if (!test_bit(state->interface, + pl->config->supported_interfaces)) + return -EINVAL; + } + pl->mac_ops->validate(pl->config, supported, state); return phylink_is_empty_linkmode(supported) ? -EINVAL : 0; diff --git a/include/linux/phy.h b/include/linux/phy.h index 04e90423fa88b439c0b82a3ecf32a5dd5d7def05..96e43fbb2dd89f151dcc68eed2fe88a7966d0d0c 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -155,6 +155,40 @@ typedef enum { PHY_INTERFACE_MODE_MAX, } phy_interface_t; +/* PHY interface mode bitmap handling */ +#define DECLARE_PHY_INTERFACE_MASK(name) \ + DECLARE_BITMAP(name, PHY_INTERFACE_MODE_MAX) + +static inline void phy_interface_zero(unsigned long *intf) +{ + bitmap_zero(intf, PHY_INTERFACE_MODE_MAX); +} + +static inline bool phy_interface_empty(const unsigned long *intf) +{ + return bitmap_empty(intf, PHY_INTERFACE_MODE_MAX); +} + +static inline void phy_interface_and(unsigned long *dst, const unsigned long *a, + const unsigned long *b) +{ + bitmap_and(dst, a, b, PHY_INTERFACE_MODE_MAX); +} + +static inline void phy_interface_or(unsigned long *dst, const unsigned long *a, + const unsigned long *b) +{ + bitmap_or(dst, a, b, PHY_INTERFACE_MODE_MAX); +} + +static inline void phy_interface_set_rgmii(unsigned long *intf) +{ + __set_bit(PHY_INTERFACE_MODE_RGMII, intf); + __set_bit(PHY_INTERFACE_MODE_RGMII_ID, intf); + __set_bit(PHY_INTERFACE_MODE_RGMII_RXID, intf); + __set_bit(PHY_INTERFACE_MODE_RGMII_TXID, intf); +} + /* * phy_supported_speeds - return all speeds currently supported by a PHY device */ diff --git a/include/linux/phylink.h b/include/linux/phylink.h index f7b5ed06a815b37bc3d3fbd3fcbddb48e9ff45ed..f037470b6fb33b65a01a9c22720c0a3c8990c0e2 100644 --- a/include/linux/phylink.h +++ b/include/linux/phylink.h @@ -67,6 +67,8 @@ enum phylink_op_type { * @ovr_an_inband: if true, override PCS to MLO_AN_INBAND * @get_fixed_state: callback to execute to determine the fixed link state, * if MAC link is at %MLO_AN_FIXED mode. + * @supported_interfaces: bitmap describing which PHY_INTERFACE_MODE_xxx + * are supported by the MAC/PCS. */ struct phylink_config { struct device *dev; @@ -76,6 +78,7 @@ struct phylink_config { bool ovr_an_inband; void (*get_fixed_state)(struct phylink_config *config, struct phylink_link_state *state); + DECLARE_PHY_INTERFACE_MASK(supported_interfaces); }; /** @@ -133,8 +136,14 @@ struct phylink_mac_ops { * based on @state->advertising and/or @state->speed and update * @state->interface accordingly. See phylink_helper_basex_speed(). * - * When @state->interface is %PHY_INTERFACE_MODE_NA, phylink expects the - * MAC driver to return all supported link modes. + * When @config->supported_interfaces has been set, phylink will iterate + * over the supported interfaces to determine the full capability of the + * MAC. The validation function must not print errors if @state->interface + * is set to an unexpected value. + * + * When @config->supported_interfaces is empty, phylink will call this + * function with @state->interface set to %PHY_INTERFACE_MODE_NA, and + * expects the MAC driver to return all supported link modes. * * If the @state->interface mode is not supported, then the @supported * mask must be cleared.