diff options
-rw-r--r-- | drivers/net/phy/phy.c | 128 |
1 files changed, 80 insertions, 48 deletions
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index b6c1bb45b661..1bbf5a168dbc 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -157,107 +157,138 @@ static inline int phy_aneg_done(struct phy_device *phydev) struct phy_setting { int speed; int duplex; - u32 setting; + int bit; }; -/* A mapping of all SUPPORTED settings to speed/duplex */ +/* A mapping of all SUPPORTED settings to speed/duplex. This table + * must be sorted in descending match priority - iow, descending + * speed. */ static const struct phy_setting settings[] = { { .speed = SPEED_10000, .duplex = DUPLEX_FULL, - .setting = SUPPORTED_10000baseKR_Full, + .bit = ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, }, { .speed = SPEED_10000, .duplex = DUPLEX_FULL, - .setting = SUPPORTED_10000baseKX4_Full, + .bit = ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT, }, { .speed = SPEED_10000, .duplex = DUPLEX_FULL, - .setting = SUPPORTED_10000baseT_Full, + .bit = ETHTOOL_LINK_MODE_10000baseT_Full_BIT, }, { .speed = SPEED_2500, .duplex = DUPLEX_FULL, - .setting = SUPPORTED_2500baseX_Full, + .bit = ETHTOOL_LINK_MODE_2500baseX_Full_BIT, }, { .speed = SPEED_1000, .duplex = DUPLEX_FULL, - .setting = SUPPORTED_1000baseKX_Full, + .bit = ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, }, { .speed = SPEED_1000, .duplex = DUPLEX_FULL, - .setting = SUPPORTED_1000baseT_Full, + .bit = ETHTOOL_LINK_MODE_1000baseT_Full_BIT, }, { .speed = SPEED_1000, .duplex = DUPLEX_HALF, - .setting = SUPPORTED_1000baseT_Half, + .bit = ETHTOOL_LINK_MODE_1000baseT_Half_BIT, }, { .speed = SPEED_100, .duplex = DUPLEX_FULL, - .setting = SUPPORTED_100baseT_Full, + .bit = ETHTOOL_LINK_MODE_100baseT_Full_BIT, }, { .speed = SPEED_100, .duplex = DUPLEX_HALF, - .setting = SUPPORTED_100baseT_Half, + .bit = ETHTOOL_LINK_MODE_100baseT_Half_BIT, }, { .speed = SPEED_10, .duplex = DUPLEX_FULL, - .setting = SUPPORTED_10baseT_Full, + .bit = ETHTOOL_LINK_MODE_10baseT_Full_BIT, }, { .speed = SPEED_10, .duplex = DUPLEX_HALF, - .setting = SUPPORTED_10baseT_Half, + .bit = ETHTOOL_LINK_MODE_10baseT_Half_BIT, }, }; -#define MAX_NUM_SETTINGS ARRAY_SIZE(settings) - /** - * phy_find_setting - find a PHY settings array entry that matches speed & duplex + * phy_lookup_setting - lookup a PHY setting * @speed: speed to match * @duplex: duplex to match + * @mask: allowed link modes + * @maxbit: bit size of link modes + * @exact: an exact match is required + * + * Search the settings array for a setting that matches the speed and + * duplex, and which is supported. * - * Description: Searches the settings array for the setting which - * matches the desired speed and duplex, and returns the index - * of that setting. Returns the index of the last setting if - * none of the others match. + * If @exact is unset, either an exact match or %NULL for no match will + * be returned. + * + * If @exact is set, an exact match, the fastest supported setting at + * or below the specified speed, the slowest supported setting, or if + * they all fail, %NULL will be returned. */ -static inline unsigned int phy_find_setting(int speed, int duplex) +static const struct phy_setting * +phy_lookup_setting(int speed, int duplex, const unsigned long *mask, + size_t maxbit, bool exact) { - unsigned int idx = 0; + const struct phy_setting *p, *match = NULL, *last = NULL; + int i; + + for (i = 0, p = settings; i < ARRAY_SIZE(settings); i++, p++) { + if (p->bit < maxbit && test_bit(p->bit, mask)) { + last = p; + if (p->speed == speed && p->duplex == duplex) { + /* Exact match for speed and duplex */ + match = p; + break; + } else if (!exact) { + if (!match && p->speed <= speed) + /* Candidate */ + match = p; - while (idx < ARRAY_SIZE(settings) && - (settings[idx].speed != speed || settings[idx].duplex != duplex)) - idx++; + if (p->speed < speed) + break; + } + } + } + + if (!match && !exact) + match = last; - return idx < MAX_NUM_SETTINGS ? idx : MAX_NUM_SETTINGS - 1; + return match; } /** - * phy_find_valid - find a PHY setting that matches the requested features mask - * @idx: The first index in settings[] to search - * @features: A mask of the valid settings + * phy_find_valid - find a PHY setting that matches the requested parameters + * @speed: desired speed + * @duplex: desired duplex + * @supported: mask of supported link modes * - * Description: Returns the index of the first valid setting less - * than or equal to the one pointed to by idx, as determined by - * the mask in features. Returns the index of the last setting - * if nothing else matches. + * Locate a supported phy setting that is, in priority order: + * - an exact match for the specified speed and duplex mode + * - a match for the specified speed, or slower speed + * - the slowest supported speed + * Returns the matched phy_setting entry, or %NULL if no supported phy + * settings were found. */ -static inline unsigned int phy_find_valid(unsigned int idx, u32 features) +static const struct phy_setting * +phy_find_valid(int speed, int duplex, u32 supported) { - while (idx < MAX_NUM_SETTINGS && !(settings[idx].setting & features)) - idx++; + unsigned long mask = supported; - return idx < MAX_NUM_SETTINGS ? idx : MAX_NUM_SETTINGS - 1; + return phy_lookup_setting(speed, duplex, &mask, BITS_PER_LONG, false); } /** @@ -271,12 +302,9 @@ static inline unsigned int phy_find_valid(unsigned int idx, u32 features) */ static inline bool phy_check_valid(int speed, int duplex, u32 features) { - unsigned int idx; + unsigned long mask = features; - idx = phy_find_valid(phy_find_setting(speed, duplex), features); - - return settings[idx].speed == speed && settings[idx].duplex == duplex && - (settings[idx].setting & features); + return !!phy_lookup_setting(speed, duplex, &mask, BITS_PER_LONG, true); } /** @@ -289,18 +317,22 @@ static inline bool phy_check_valid(int speed, int duplex, u32 features) */ static void phy_sanitize_settings(struct phy_device *phydev) { + const struct phy_setting *setting; u32 features = phydev->supported; - unsigned int idx; /* Sanitize settings based on PHY capabilities */ if ((features & SUPPORTED_Autoneg) == 0) phydev->autoneg = AUTONEG_DISABLE; - idx = phy_find_valid(phy_find_setting(phydev->speed, phydev->duplex), - features); - - phydev->speed = settings[idx].speed; - phydev->duplex = settings[idx].duplex; + setting = phy_find_valid(phydev->speed, phydev->duplex, features); + if (setting) { + phydev->speed = setting->speed; + phydev->duplex = setting->duplex; + } else { + /* We failed to find anything (no supported speeds?) */ + phydev->speed = SPEED_UNKNOWN; + phydev->duplex = DUPLEX_UNKNOWN; + } } /** |