diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2017-05-02 16:40:27 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-05-02 16:40:27 -0700 |
commit | 8d65b08debc7e62b2c6032d7fe7389d895b92cbc (patch) | |
tree | 0c3141b60c3a03cc32742b5750c5e763b9dae489 /drivers/net/phy/phy.c | |
parent | 5a0387a8a8efb90ae7fea1e2e5c62de3efa74691 (diff) | |
parent | 5d15af6778b8e4ed1fd41b040283af278e7a9a72 (diff) |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
Pull networking updates from David Millar:
"Here are some highlights from the 2065 networking commits that
happened this development cycle:
1) XDP support for IXGBE (John Fastabend) and thunderx (Sunil Kowuri)
2) Add a generic XDP driver, so that anyone can test XDP even if they
lack a networking device whose driver has explicit XDP support
(me).
3) Sparc64 now has an eBPF JIT too (me)
4) Add a BPF program testing framework via BPF_PROG_TEST_RUN (Alexei
Starovoitov)
5) Make netfitler network namespace teardown less expensive (Florian
Westphal)
6) Add symmetric hashing support to nft_hash (Laura Garcia Liebana)
7) Implement NAPI and GRO in netvsc driver (Stephen Hemminger)
8) Support TC flower offload statistics in mlxsw (Arkadi Sharshevsky)
9) Multiqueue support in stmmac driver (Joao Pinto)
10) Remove TCP timewait recycling, it never really could possibly work
well in the real world and timestamp randomization really zaps any
hint of usability this feature had (Soheil Hassas Yeganeh)
11) Support level3 vs level4 ECMP route hashing in ipv4 (Nikolay
Aleksandrov)
12) Add socket busy poll support to epoll (Sridhar Samudrala)
13) Netlink extended ACK support (Johannes Berg, Pablo Neira Ayuso,
and several others)
14) IPSEC hw offload infrastructure (Steffen Klassert)"
* git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next: (2065 commits)
tipc: refactor function tipc_sk_recv_stream()
tipc: refactor function tipc_sk_recvmsg()
net: thunderx: Optimize page recycling for XDP
net: thunderx: Support for XDP header adjustment
net: thunderx: Add support for XDP_TX
net: thunderx: Add support for XDP_DROP
net: thunderx: Add basic XDP support
net: thunderx: Cleanup receive buffer allocation
net: thunderx: Optimize CQE_TX handling
net: thunderx: Optimize RBDR descriptor handling
net: thunderx: Support for page recycling
ipx: call ipxitf_put() in ioctl error path
net: sched: add helpers to handle extended actions
qed*: Fix issues in the ptp filter config implementation.
qede: Fix concurrency issue in PTP Tx path processing.
stmmac: Add support for SIMATIC IOT2000 platform
net: hns: fix ethtool_get_strings overflow in hns driver
tcp: fix wraparound issue in tcp_lp
bpf, arm64: fix jit branch offset related to ldimm64
bpf, arm64: implement jiting of BPF_XADD
...
Diffstat (limited to 'drivers/net/phy/phy.c')
-rw-r--r-- | drivers/net/phy/phy.c | 278 |
1 files changed, 117 insertions, 161 deletions
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 97ff1278167b..82ab8fb82587 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -50,8 +50,22 @@ static const char *phy_speed_to_str(int speed) return "1Gbps"; case SPEED_2500: return "2.5Gbps"; + case SPEED_5000: + return "5Gbps"; case SPEED_10000: return "10Gbps"; + case SPEED_20000: + return "20Gbps"; + case SPEED_25000: + return "25Gbps"; + case SPEED_40000: + return "40Gbps"; + case SPEED_50000: + return "50Gbps"; + case SPEED_56000: + return "56Gbps"; + case SPEED_100000: + return "100Gbps"; case SPEED_UNKNOWN: return "Unknown"; default: @@ -162,7 +176,9 @@ struct phy_setting { u32 setting; }; -/* A mapping of all SUPPORTED settings to speed/duplex */ +/* A mapping of all SUPPORTED settings to speed/duplex. This table + * must be grouped by speed and sorted in descending match priority + * - iow, descending speed. */ static const struct phy_setting settings[] = { { .speed = SPEED_10000, @@ -221,45 +237,70 @@ static const struct phy_setting settings[] = { }, }; -#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 + * @feature: allowed 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, u32 features, 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->setting & features) { + 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; + + if (p->speed < speed) + break; + } + } + } - while (idx < ARRAY_SIZE(settings) && - (settings[idx].speed != speed || settings[idx].duplex != duplex)) - idx++; + 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++; - - return idx < MAX_NUM_SETTINGS ? idx : MAX_NUM_SETTINGS - 1; + return phy_lookup_setting(speed, duplex, supported, false); } /** @@ -279,20 +320,11 @@ unsigned int phy_supported_speeds(struct phy_device *phy, unsigned int count = 0; unsigned int idx = 0; - while (idx < MAX_NUM_SETTINGS && count < size) { - idx = phy_find_valid(idx, phy->supported); - - if (!(settings[idx].setting & phy->supported)) - break; - + for (idx = 0; idx < ARRAY_SIZE(settings) && count < size; idx++) /* Assumes settings are grouped by speed */ - if ((count == 0) || - (speeds[count - 1] != settings[idx].speed)) { - speeds[count] = settings[idx].speed; - count++; - } - idx++; - } + if ((settings[idx].setting & phy->supported) && + (count == 0 || speeds[count - 1] != settings[idx].speed)) + speeds[count++] = settings[idx].speed; return count; } @@ -308,12 +340,7 @@ unsigned int phy_supported_speeds(struct phy_device *phy, */ static inline bool phy_check_valid(int speed, int duplex, u32 features) { - unsigned int idx; - - 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, features, true); } /** @@ -326,18 +353,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; + } } /** @@ -1224,91 +1255,6 @@ void phy_mac_interrupt(struct phy_device *phydev, int new_link) } EXPORT_SYMBOL(phy_mac_interrupt); -static inline void mmd_phy_indirect(struct mii_bus *bus, int prtad, int devad, - int addr) -{ - /* Write the desired MMD Devad */ - bus->write(bus, addr, MII_MMD_CTRL, devad); - - /* Write the desired MMD register address */ - bus->write(bus, addr, MII_MMD_DATA, prtad); - - /* Select the Function : DATA with no post increment */ - bus->write(bus, addr, MII_MMD_CTRL, (devad | MII_MMD_CTRL_NOINCR)); -} - -/** - * phy_read_mmd_indirect - reads data from the MMD registers - * @phydev: The PHY device bus - * @prtad: MMD Address - * @devad: MMD DEVAD - * - * Description: it reads data from the MMD registers (clause 22 to access to - * clause 45) of the specified phy address. - * To read these register we have: - * 1) Write reg 13 // DEVAD - * 2) Write reg 14 // MMD Address - * 3) Write reg 13 // MMD Data Command for MMD DEVAD - * 3) Read reg 14 // Read MMD data - */ -int phy_read_mmd_indirect(struct phy_device *phydev, int prtad, int devad) -{ - struct phy_driver *phydrv = phydev->drv; - int addr = phydev->mdio.addr; - int value = -1; - - if (!phydrv->read_mmd_indirect) { - struct mii_bus *bus = phydev->mdio.bus; - - mutex_lock(&bus->mdio_lock); - mmd_phy_indirect(bus, prtad, devad, addr); - - /* Read the content of the MMD's selected register */ - value = bus->read(bus, addr, MII_MMD_DATA); - mutex_unlock(&bus->mdio_lock); - } else { - value = phydrv->read_mmd_indirect(phydev, prtad, devad, addr); - } - return value; -} -EXPORT_SYMBOL(phy_read_mmd_indirect); - -/** - * phy_write_mmd_indirect - writes data to the MMD registers - * @phydev: The PHY device - * @prtad: MMD Address - * @devad: MMD DEVAD - * @data: data to write in the MMD register - * - * Description: Write data from the MMD registers of the specified - * phy address. - * To write these register we have: - * 1) Write reg 13 // DEVAD - * 2) Write reg 14 // MMD Address - * 3) Write reg 13 // MMD Data Command for MMD DEVAD - * 3) Write reg 14 // Write MMD data - */ -void phy_write_mmd_indirect(struct phy_device *phydev, int prtad, - int devad, u32 data) -{ - struct phy_driver *phydrv = phydev->drv; - int addr = phydev->mdio.addr; - - if (!phydrv->write_mmd_indirect) { - struct mii_bus *bus = phydev->mdio.bus; - - mutex_lock(&bus->mdio_lock); - mmd_phy_indirect(bus, prtad, devad, addr); - - /* Write the data into MMD's selected register */ - bus->write(bus, addr, MII_MMD_DATA, data); - mutex_unlock(&bus->mdio_lock); - } else { - phydrv->write_mmd_indirect(phydev, prtad, devad, addr, data); - } -} -EXPORT_SYMBOL(phy_write_mmd_indirect); - /** * phy_init_eee - init and check the EEE feature * @phydev: target phy_device struct @@ -1325,15 +1271,8 @@ int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable) return -EIO; /* According to 802.3az,the EEE is supported only in full duplex-mode. - * Also EEE feature is active when core is operating with MII, GMII - * or RGMII (all kinds). Internal PHYs are also allowed to proceed and - * should return an error if they do not support EEE. */ - if ((phydev->duplex == DUPLEX_FULL) && - ((phydev->interface == PHY_INTERFACE_MODE_MII) || - (phydev->interface == PHY_INTERFACE_MODE_GMII) || - phy_interface_is_rgmii(phydev) || - phy_is_internal(phydev))) { + if (phydev->duplex == DUPLEX_FULL) { int eee_lp, eee_cap, eee_adv; u32 lp, cap, adv; int status; @@ -1344,8 +1283,7 @@ int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable) return status; /* First check if the EEE ability is supported */ - eee_cap = phy_read_mmd_indirect(phydev, MDIO_PCS_EEE_ABLE, - MDIO_MMD_PCS); + eee_cap = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_EEE_ABLE); if (eee_cap <= 0) goto eee_exit_err; @@ -1356,13 +1294,11 @@ int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable) /* Check which link settings negotiated and verify it in * the EEE advertising registers. */ - eee_lp = phy_read_mmd_indirect(phydev, MDIO_AN_EEE_LPABLE, - MDIO_MMD_AN); + eee_lp = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_LPABLE); if (eee_lp <= 0) goto eee_exit_err; - eee_adv = phy_read_mmd_indirect(phydev, MDIO_AN_EEE_ADV, - MDIO_MMD_AN); + eee_adv = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV); if (eee_adv <= 0) goto eee_exit_err; @@ -1375,14 +1311,12 @@ int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable) /* Configure the PHY to stop receiving xMII * clock while it is signaling LPI. */ - int val = phy_read_mmd_indirect(phydev, MDIO_CTRL1, - MDIO_MMD_PCS); + int val = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1); if (val < 0) return val; val |= MDIO_PCS_CTRL1_CLKSTOP_EN; - phy_write_mmd_indirect(phydev, MDIO_CTRL1, - MDIO_MMD_PCS, val); + phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1, val); } return 0; /* EEE supported */ @@ -1404,7 +1338,7 @@ int phy_get_eee_err(struct phy_device *phydev) if (!phydev->drv) return -EIO; - return phy_read_mmd_indirect(phydev, MDIO_PCS_EEE_WK_ERR, MDIO_MMD_PCS); + return phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_EEE_WK_ERR); } EXPORT_SYMBOL(phy_get_eee_err); @@ -1424,19 +1358,19 @@ int phy_ethtool_get_eee(struct phy_device *phydev, struct ethtool_eee *data) return -EIO; /* Get Supported EEE */ - val = phy_read_mmd_indirect(phydev, MDIO_PCS_EEE_ABLE, MDIO_MMD_PCS); + val = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_EEE_ABLE); if (val < 0) return val; data->supported = mmd_eee_cap_to_ethtool_sup_t(val); /* Get advertisement EEE */ - val = phy_read_mmd_indirect(phydev, MDIO_AN_EEE_ADV, MDIO_MMD_AN); + val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV); if (val < 0) return val; data->advertised = mmd_eee_adv_to_ethtool_adv_t(val); /* Get LP advertisement EEE */ - val = phy_read_mmd_indirect(phydev, MDIO_AN_EEE_LPABLE, MDIO_MMD_AN); + val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_LPABLE); if (val < 0) return val; data->lp_advertised = mmd_eee_adv_to_ethtool_adv_t(val); @@ -1454,15 +1388,37 @@ EXPORT_SYMBOL(phy_ethtool_get_eee); */ int phy_ethtool_set_eee(struct phy_device *phydev, struct ethtool_eee *data) { - int val = ethtool_adv_to_mmd_eee_adv_t(data->advertised); + int cap, old_adv, adv, ret; if (!phydev->drv) return -EIO; + /* Get Supported EEE */ + cap = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_EEE_ABLE); + if (cap < 0) + return cap; + + old_adv = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV); + if (old_adv < 0) + return old_adv; + + adv = ethtool_adv_to_mmd_eee_adv_t(data->advertised) & cap; + /* Mask prohibited EEE modes */ - val &= ~phydev->eee_broken_modes; + adv &= ~phydev->eee_broken_modes; - phy_write_mmd_indirect(phydev, MDIO_AN_EEE_ADV, MDIO_MMD_AN, val); + if (old_adv != adv) { + ret = phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, adv); + if (ret < 0) + return ret; + + /* Restart autonegotiation so the new modes get sent to the + * link partner. + */ + ret = genphy_restart_aneg(phydev); + if (ret < 0) + return ret; + } return 0; } |