# Base git commit: 98f7e32f20d2
# (Linux 6.11)
# 
# Author:    Russell King (Fri 14 Feb 13:32:36 GMT 2020)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:41:35 BST 2024)
# 
#   net: dsa: debugging prints
# 
#   Signed-off-by: Russell King
# 
#   20908720b29757796501935e7088fe34ee24015b
#    drivers/net/dsa/mv88e6xxx/port.c | 12 ++++++++++++
#    1 file changed, 12 insertions(+)
# 
# Author:    Russell King (Thu  6 Feb 18:11:03 GMT 2020)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:41:34 BST 2024)
# 
#   ARM: dts: vf610-zii-dev-rev-c: fix 88x3310 reset
# 
#   Hogged GPIOs no longer work, switch to using a bus-level reset
#   instead.
# 
#   Signed-off-by: Russell King
# 
#   536efe7f60fc01e34563a112ebc17dc3bee4b3c0
#    arch/arm/boot/dts/nxp/vf/vf610-zii-dev-rev-c.dts | 10 +++-------
#    1 file changed, 3 insertions(+), 7 deletions(-)
# 
# Author:    Russell King (Fri  8 Dec 10:08:41 GMT 2017)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:41:34 BST 2024)
# 
#   ARM: dts: vf610-zii-dev-rev-c: add support for SFF modules
# 
#   Signed-off-by: Russell King
# 
#   1e956cd03d5221196191f3d4654b2534bee2c757
#    arch/arm/boot/dts/nxp/vf/vf610-zii-dev-rev-c.dts | 2 +-
#    drivers/net/phy/Kconfig                          | 2 +-
#    2 files changed, 2 insertions(+), 2 deletions(-)
# 
# Author:    Russell King (Fri  8 Dec 10:08:41 GMT 2017)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:41:33 BST 2024)
# 
#   ARM: dts: vf610-zii-dev-rev-c: add support for 88x3310 10Gbit PHY
# 
#   Add support for the 88x3310 10Gbit PHY on the ZII rev C board.
# 
#   Signed-off-by: Russell King
# 
#   78c183d0cee53076ddcbe614fad8fb1bdff1bcfd
#    arch/arm/boot/dts/nxp/vf/vf610-zii-dev-rev-c.dts | 23 +++++++++++++++++++++++
#    1 file changed, 23 insertions(+)
# 
# Author:    Russell King (Fri 21 Feb 12:46:58 GMT 2020)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:41:33 BST 2024)
# 
#   ARM: dts: vf610-zii-dev-rev-b: remove lan8
# 
#   lan8 is mis-wired, so it can only transmit packets but not receive.
#   Remove the non-functional lan8 from the dts file.
# 
#   Signed-off-by: Russell King
# 
#   6e0101a5d1702d93b2333f11b94355dd36763fc6
#    arch/arm/boot/dts/nxp/vf/vf610-zii-dev-rev-b.dts | 6 +-----
#    1 file changed, 1 insertion(+), 5 deletions(-)
# 
# Author:    Russell King (Wed 20 Dec 22:26:48 GMT 2017)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:41:32 BST 2024)
# 
#   ARM: dts: vf610-zii-dev-rev-b: add optical p2 SFF description
# 
#   Add a description for the optical p2 port on the 88e6352.
# 
#   Note: to use this, R412 must not be fitted.
# 
#   Signed-off-by: Russell King
# 
#   a4a7221aebc2bc89506c2a364bee0752398c8bbb
#    arch/arm/boot/dts/nxp/vf/vf610-zii-dev-rev-b.dts | 6 ++++++
#    1 file changed, 6 insertions(+)
# 
# Author:    Russell King (Tue 19 Dec 11:12:15 GMT 2017)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:41:32 BST 2024)
# 
#   ARM: dts: vf610-zii-dev-rev-b: add support for SFF modules
# 
#   Signed-off-by: Russell King
# 
#   38bc8d8f43a8942a250050726bcf0cda2722474b
#    arch/arm/boot/dts/nxp/vf/vf610-zii-dev-rev-b.dts | 69 +++++++++++-------------
#    1 file changed, 31 insertions(+), 38 deletions(-)
# 
# Author:    Russell King (Tue 19 Dec 11:27:31 GMT 2017)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:41:31 BST 2024)
# 
#   ARM: dts: vf610-zii-dev-rev-b: add interrupts for 88e1545 PHY
# 
#   The 88e1545 PHY has its interrupts wired to the VF610, so we might as
#   well use them.
# 
#   Signed-off-by: Russell King
#   ---
#   This is certainly not correct, as all PHYs on this device share the
#   same interrupt line, but we can't specify the pinmux settings
#   individually on each PHY.  How should this be handled?
# 
#   1e8c29c764d66dfb0fa48a62a6558991e5b24531
#    arch/arm/boot/dts/nxp/vf/vf610-zii-dev-rev-b.dts | 14 ++++++++++++++
#    1 file changed, 14 insertions(+)
# 
# Author:    Russell King (Fri 15 Dec 16:13:06 GMT 2017)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:41:31 BST 2024)
# 
#   net: dsa: allow init to continue when no phy
# 
#   Signed-off-by: Russell King
# 
#   993625d7fb18a426f2c64b9824eeaede24c0e3f5
#    net/dsa/user.c | 3 ++-
#    1 file changed, 2 insertions(+), 1 deletion(-)
# 
# Author:    Russell King (Oracle) (Thu 17 Aug 21:56:27 BST 2023)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:40:37 BST 2024)
# 
#   net: dsa: mv88e6xxx: add 6352 family EEE support
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   95df51f9752bdca961c1354ad25034f6b4f8b31e
#    drivers/net/dsa/mv88e6xxx/chip.c | 8 ++++++++
#    1 file changed, 8 insertions(+)
# 
# Author:    Russell King (Oracle) (Sat  4 Dec 20:13:11 GMT 2021)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:40:37 BST 2024)
# 
#   net: phy: generate PHY mdio modalias
# 
#   The modalias string provided in the uevent sysfs file does not conform
#   to the format used in PHY driver modules. One of the reasons is that
#   udev loading of PHY driver modules has not been an expected use case.
# 
#   This patch changes the MODALIAS entry for only PHY devices from:
#           MODALIAS=of:Nethernet-phyT(null)
#   to:
#           MODALIAS=mdio:00000000001000100001010100010011
# 
#   Other MDIO devices (such as DSA) remain as before.
# 
#   However, having udev automatically load the module has the advantage
#   of making use of existing functionality to have the module loaded
#   before the device is bound to the driver, thus taking advantage of
#   multithreaded boot systems, potentially decreasing the boot time.
# 
#   However, this patch will not solve any issues with the driver module
#   not being loaded prior to the network device needing to use the PHY.
#   This is something that is completely out of control of any patch to
#   change the uevent mechanism.
# 
#   Reported-by: Yinbo Zhu
#   Signed-off-by: Russell King (Oracle)
# 
#   f5d49a8d7c9aa62d6232a479541b21bbe96a72f8
#    drivers/net/phy/mdio_bus.c   |  8 ++++++++
#    drivers/net/phy/phy_device.c | 14 ++++++++++++++
#    include/linux/mdio.h         |  2 ++
#    3 files changed, 24 insertions(+)
# 
# Author:    Russell King (Oracle) (Sat 27 Nov 16:05:36 GMT 2021)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:40:37 BST 2024)
# 
#   net: use phylink_mode_*() helpers
# 
#   Use the phylink_mode_*() helpers in all drivers so we can change the
#   definition of the "mode" argument.
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   64ef1e85aa74ac3024a5083411aa9e41968de1aa
#    drivers/net/dsa/b53/b53_common.c         |  7 ++++---
#    drivers/net/dsa/bcm_sf2.c                |  2 +-
#    drivers/net/dsa/mv88e6xxx/chip.c         | 13 +++++++------
#    drivers/net/dsa/mv88e6xxx/port.c         |  2 +-
#    drivers/net/ethernet/cadence/macb_main.c |  2 +-
#    drivers/net/phy/phylink.c                | 29 +++++++++++++++--------------
#    6 files changed, 29 insertions(+), 26 deletions(-)
# 
# Author:    Russell King (Oracle) (Sat 27 Nov 14:15:19 GMT 2021)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:40:36 BST 2024)
# 
#   net: phylink: add helpers for decoding mode
# 
#   Add helpers to decode the mode argument passed to the various MAC and
#   PCS functions.
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   70c380b6b58d51d17ab3342e809329a69a37ac24
#    include/linux/phylink.h | 17 ++++++++++++++++-
#    1 file changed, 16 insertions(+), 1 deletion(-)
# 
# Author:    Russell King (Tue  3 Mar 12:12:43 GMT 2020)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:40:36 BST 2024)
# 
#   net: phylink: use phy interface mode bitmaps for SFP PHYs
# 
#   Select the initial SFP PHY interface mode from the PHY supported
#   interface bitmaps.
# 
#   Signed-off-by: Russell King
# 
#   cdf0803cee5ef0ae771dfc9195e0dcc5d2da5126
#    drivers/net/phy/phylink.c | 53 +++++++++++++++++++++++++++++++++++++++++++++--
#    1 file changed, 51 insertions(+), 2 deletions(-)
# 
# Author:    Russell King (Oracle) (Sat  5 Aug 14:30:51 BST 2023)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:40:36 BST 2024)
# 
#   net: phy: add supported_interfaces to Aquantia AQR113C
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   2ca0ca028cfdff5a8c2cf9b14088c1188c625f77
#    drivers/net/phy/aquantia/aquantia_main.c | 15 ++++++++++++++-
#    1 file changed, 14 insertions(+), 1 deletion(-)
# 
# Author:    Russell King (Mon 23 Dec 23:26:04 GMT 2019)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:40:35 BST 2024)
# 
#   net: phy: add supported_interfaces to marvell10g PHYs
# 
#   Signed-off-by: Russell King
# 
#   e19052609e276b2a5ea735eb101afeeacdc00037
#    drivers/net/phy/marvell10g.c | 5 ++---
#    1 file changed, 2 insertions(+), 3 deletions(-)
# 
# Author:    Russell King (Mon 23 Dec 23:25:49 GMT 2019)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:40:35 BST 2024)
# 
#   net: phy: add supported_interfaces to marvell PHYs
# 
#   Signed-off-by: Russell King
# 
#   2218cd1d988c9013b1ce90d0e09a5ae3550e1859
#    drivers/net/phy/marvell.c | 9 +++++++++
#    1 file changed, 9 insertions(+)
# 
# Author:    Russell King (Mon 23 Dec 23:25:35 GMT 2019)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:40:35 BST 2024)
# 
#   net: phy: add supported_interfaces to bcm84881
# 
#   Signed-off-by: Russell King
# 
#   30e14176927f3b84107439974d0d8edf642d8d93
#    drivers/net/phy/bcm84881.c | 4 ++++
#    1 file changed, 4 insertions(+)
# 
# Author:    Russell King (Mon 23 Dec 23:24:17 GMT 2019)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:40:34 BST 2024)
# 
#   net: phy: add supported_interfaces to phylib
# 
#   Add a supported_interfaces member to phylib so we know which
#   interfaces a PHY supports. Currently, set any unconverted driver
#   to indicate all interfaces are supported.
# 
#   Signed-off-by: Russell King
# 
#   f5af60810618158035eea7cfc31ca0a12fb058ac
#    include/linux/phy.h | 3 +++
#    1 file changed, 3 insertions(+)
# 
# Author:    Russell King (Sun 13 Sep 01:06:31 BST 2015)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:40:34 BST 2024)
# 
#   net: sfp: display SFP module information       [*not for mainline*]
# 
#   Display SFP module information verbosely, splitting the generic parts
#   into a separate file.
# 
#   Signed-off-by: Russell King
# 
#   4ac1a09c062a4d845794977fee8f60603293eb50
#    drivers/net/phy/Makefile |   2 +-
#    drivers/net/phy/sff.c    | 114 +++++++++++++++++++++++
#    drivers/net/phy/sff.h    |  16 ++++
#    drivers/net/phy/sfp.c    | 229 ++++++++++++++++++++++++++++++++++++++++++++---
#    4 files changed, 350 insertions(+), 11 deletions(-)
#    create mode 100644 drivers/net/phy/sff.c
#    create mode 100644 drivers/net/phy/sff.h
# 
# Author:    Russell King (Fri 14 Apr 20:17:13 BST 2017)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:40:34 BST 2024)
# 
#   net: sfp: add sfp+ compatible                  [*not for mainline*]
# 
#   Add a compatible for SFP+ cages.  SFP+ cages are backwards compatible,
#   but the ethernet device behind them may not support the slower speeds
#   of SFP modules.
# 
#   Signed-off-by: Russell King
# 
#   05296f411e6b7afbb64e436b7f19e4143451851a
#    drivers/net/phy/sfp.c | 1 +
#    1 file changed, 1 insertion(+)
# 
# Author:    Russell King (Fri 18 Oct 10:37:44 BST 2019)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:40:34 BST 2024)
# 
#   net: sfp: add support for cooled SFP+ transceivers
# 
#   Cooled SFP+ transceivers need a longer initialisation (startup) time.
#   Select the initialisation time depending on the cooled option bit.
# 
#   Signed-off-by: Russell King
# 
#   6be06ab228ad4e9f8cc24ad548829b70c4ce8f02
#    drivers/net/phy/sfp.c | 18 +++++++++++-------
#    1 file changed, 11 insertions(+), 7 deletions(-)
# 
# Author:    Russell King (Tue  5 Nov 10:34:39 GMT 2019)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:40:33 BST 2024)
# 
#   net: phy: make phy_error() report which PHY has failed
# 
#   phy_error() is called from phy_interrupt() or phy_state_machine(), and
#   uses WARN_ON() to print a backtrace. The backtrace is not useful when
#   reporting a PHY error.
# 
#   However, a system may contain multiple ethernet PHYs, and phy_error()
#   gives no clue which one caused the problem.
# 
#   Replace WARN_ON() with a call to phydev_err() so that we can see which
#   PHY had an error, and also inform the user that we are halting the PHY.
# 
#   Signed-off-by: Russell King
# 
#   7433873f8e0b7d72bacd8c1d40694d511bb6b482
#    drivers/net/phy/phy.c | 3 ++-
#    1 file changed, 2 insertions(+), 1 deletion(-)
# 
# Author:    Russell King (Wed  5 Jun 11:44:55 BST 2019)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:40:33 BST 2024)
# 
#   net: phy: marvell10g: allow PHY to probe without firmware
# 
#   Allow the PHY to probe when there is no firmware, but do not allow the
#   link to come up by forcing the PHY state to PHY_HALTED in a similar way
#   to phy_error().
# 
#   Signed-off-by: Russell King
# 
#   2513f83a736f2ca5416fb6d30aaa8d8cc8028f95
#    drivers/net/phy/marvell10g.c | 18 +++++++++++++++++-
#    1 file changed, 17 insertions(+), 1 deletion(-)
# 
# Author:    Russell King (Mon 26 Aug 12:19:35 BST 2019)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:40:33 BST 2024)
# 
#   net: phy: provide phy driver start/stop hooks
# 
#   Provide phy driver start/stop hooks so that the PHY driver knows when
#   the network driver is starting or stopping.  This will be used for the
#   Marvell 10G driver so that we can sanely refuse to start if the PHYs
#   firmware is not present, and also so that we can sanely support SFPs
#   behind the PHY.
# 
#   Signed-off-by: Russell King
# 
#   a7631777c26fe1ca15ab784ffb5687699733701c
#    drivers/net/phy/phy.c | 7 +++++++
#    include/linux/phy.h   | 3 +++
#    2 files changed, 10 insertions(+)
# 
# Author:    Russell King (Sat  4 Jan 00:41:35 GMT 2020)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:40:32 BST 2024)
# 
#   net: phy: marvell*: add support for hw resolved pause modes
# 
#   Support reporting the hardware resolved pause enablement states via
#   phylib, overriding our software implementation.
# 
#   Signed-off-by: Russell King
# 
#   f80ae90d00c1ad10e0388955e69303e0b0ecfb45
#    drivers/net/phy/marvell.c    | 40 ++++++++++++++++++++++++++++++++++------
#    drivers/net/phy/marvell10g.c |  6 ++++++
#    2 files changed, 40 insertions(+), 6 deletions(-)
# 
# Author:    Russell King (Fri  3 Jan 23:13:28 GMT 2020)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:40:32 BST 2024)
# 
#   net: phy: add resolved pause support           [*not for mainline*]
# 
#   Allow phylib drivers to pass the hardware-resolved pause state to MAC
#   drivers, rather than using the software-based pause resolution code.
# 
#   Signed-off-by: Russell King
# 
#   b7ca68de8215ba264e06160779cd7b1ff4909f6a
#    drivers/net/phy/phy_device.c | 6 ++++++
#    include/linux/phy.h          | 9 +++++++++
#    2 files changed, 15 insertions(+)
# 
# Author:    Russell King (Oracle) (Fri  2 Jun 11:51:53 BST 2023)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:40:32 BST 2024)
# 
#   net: phylink: handle MDIO_USXGMII_LINK when decoding USXGMII
# 
#   If MDIO_USXGMII_LINK is not set, it means that the PHYs media side
#   link is down. Indicate back to phylink that the link as a whole is
#   down.
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   e4196183ef64aaf82265877e7b38211b706cf334
#    drivers/net/phy/phylink.c | 10 ++++++++--
#    1 file changed, 8 insertions(+), 2 deletions(-)
# 
# Author:    Russell King (Sun  1 Dec 15:35:08 GMT 2019)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:40:31 BST 2024)
# 
#   arm64: dts: configure Macchiatobin 10G PHY LED modes
# 
#   Configure the Macchiatobin 10G PHY LED modes to correct their polarity.
#   We keep the existing LED behaviours, but switch their polarity to
#   reflect how they are connected. Tweak the LED modes as well to be:
# 
#   left:  off          = no link
#          solid green  = RJ45 link up (not SFP+ cage)
#          flash green  = traffic
#   right: off          = no link
#          solid green  = 10G
#          solid yellow = 1G
#          flash green  = 100M
#          flash yellow = 10M
# 
#   Signed-off-by: Russell King
# 
#   4136835f24eb63294f17ac287f8049d36df990b3
#    arch/arm64/boot/dts/marvell/armada-8040-mcbin.dts | 2 ++
#    1 file changed, 2 insertions(+)
# 
# Author:    Russell King (Sun  1 Dec 15:33:39 GMT 2019)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:40:30 BST 2024)
# 
#   net: phy: marvell10g: add support for configuring LEDs
# 
#   Add support for configuring the LEDs. Macchiatobin has an oddity in
#   that the left LED goes out when the cable is connected, and flashes
#   when there's link activity. This is because the reset default for
#   the LED outputs assume that the LED is connected to supply, not to
#   ground. Add support for configuring the LED modes and polarities.
# 
#   Signed-off-by: Russell King
# 
#   79ca61a62c2f28a263c609c912c83c93b5ccc847
#    drivers/net/phy/marvell10g.c | 62 +++++++++++++++++++++++++++++++++++++++-----
#    1 file changed, 55 insertions(+), 7 deletions(-)
# 
# Author:    Russell King (Tue 17 Dec 15:21:50 GMT 2019)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:40:30 BST 2024)
# 
#   dt-bindings: net: add dt bindings for marvell10g driver
# 
#   Add a DT bindings document for the Marvell 10G driver, which will
#   augment the generic ethernet PHY binding by having LED mode
#   configuration.
# 
#   Signed-off-by: Russell King
# 
#   89990b631dcec62ebfac63805bc3832b63e85c97
#    .../devicetree/bindings/net/marvell,10g.yaml       | 36 ++++++++++++++++++++++
#    1 file changed, 36 insertions(+)
#    create mode 100644 Documentation/devicetree/bindings/net/marvell,10g.yaml
# 
# Author:    Russell King (Oracle) (Fri 26 Apr 10:59:38 BST 2024)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:40:30 BST 2024)
# 
#   net: phylink: clean up phylink_resolve()
# 
#   The switch() statement doesn't sit very well with the preceeding if()
#   statements, so let's just convert everything to if()s. This allows
#   better formatting of the code.
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   b165410302e7664d43b65fd89cf67beb49e3b84c
#    drivers/net/phy/phylink.c | 108 ++++++++++++++++++++++------------------------
#    1 file changed, 52 insertions(+), 56 deletions(-)
# 
# Author:    Russell King (Oracle) (Mon 22 Apr 17:24:46 BST 2024)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:40:29 BST 2024)
# 
#   net: phylink: move manual flow control setting
# 
#   Move the handling of manual flow control configuration to a common
#   location during resolve. We currently evaluate this for all but
#   fixed links.
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   50179d18e1f486ba75e6beddb004a410884c5ce4
#    drivers/net/phy/phylink.c | 5 +++--
#    1 file changed, 3 insertions(+), 2 deletions(-)
# 
# Author:    Russell King (Oracle) (Fri 26 Apr 15:58:29 BST 2024)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:40:29 BST 2024)
# 
#   net: phylink: remove phylink_phy_no_inband()
# 
#   Remove phylink_phy_no_inband() now that we are handling the lack of
#   inband negotiation by querying the capabilities of the PHY and PCS,
#   and the BCM84881 PHY driver provides us the information necessary to
#   make the decision.
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   a087002b1be26502d099c8edf2d2271108584268
#    drivers/net/phy/phylink.c | 27 ++++++---------------------
#    1 file changed, 6 insertions(+), 21 deletions(-)
# 
# Author:    Russell King (Oracle) (Thu  8 Feb 16:12:43 GMT 2024)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:40:29 BST 2024)
# 
#   net: phylink: add pcs_query_inband() *WIP*
# 
#   Add a pcs_query_inband() interface which reflects phy_query_inband()
#   for PHYs. This can be used to determine for the specified interface
#   mode whether in-band signalling is supported by the PCS, and whether
#   the PCS requires in-band signalling.
# 
#   This is used to determine whether we should use inband autonegotiation
#   in inband mode, which may be required or may be unsupported in various
#   interface modes.
# 
#   This revised version uses enable/disable/bypass flags to indicate the
#   capabilities.
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   72309af067dfbd4c0cbc1a6af94aadc1ceeb1e8c
#    drivers/net/phy/phylink.c | 141 ++++++++++++++++++++++++++++++++++++++++------
#    1 file changed, 123 insertions(+), 18 deletions(-)
# 
# Author:    Russell King (Oracle) (Thu  8 Feb 19:39:42 GMT 2024)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:40:28 BST 2024)
# 
#   net: mvpp2: add support for querying PCS inband properties
# 
#   Report the PCS inband properties to phylink for Marvell PP2 interfaces.
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   1f038bdcdc0248773258414a6a774b4175a3eff4
#    drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c | 25 ++++++++++++++++---------
#    1 file changed, 16 insertions(+), 9 deletions(-)
# 
# Author:    Russell King (Oracle) (Thu  8 Feb 16:41:31 GMT 2024)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:40:28 BST 2024)
# 
#   net: mvneta: add support for querying PCS inband properties
# 
#   Report the PCS inband properties to phylink for Marvell NETA
#   interfaces.
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   c7015cea5aa49154f4a675b4165d0847c4057be3
#    drivers/net/ethernet/marvell/mvneta.c | 27 +++++++++++++++++----------
#    1 file changed, 17 insertions(+), 10 deletions(-)
# 
# Author:    Russell King (Oracle) (Sat 27 Apr 12:03:27 BST 2024)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:40:28 BST 2024)
# 
#   net: phylink: add pcs_query_inband() method
# 
#   Add a pcs_query_inband() method to query the PCS for its inband link
#   capabilities, and use this to determine whether link modes used with
#   optical SFPs can be supported.
# 
#   When a PCS does not provide a method, we allow inband negotiation to
#   be either on or off, making this a no-op until the pcs_query_inband()
#   method is implemented by a PCS driver.
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   99852d52fe57297773c1143552dee332cdf5b341
#    drivers/net/phy/phylink.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++
#    include/linux/phylink.h   | 17 ++++++++++++++
#    2 files changed, 77 insertions(+)
# 
# Author:    Russell King (Oracle) (Thu  8 Feb 15:54:46 GMT 2024)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:40:28 BST 2024)
# 
#   net: phy: marvell: provide phy_query_inband() implementation
# 
#   Provide an implementation for phy_query_inband() for Marvell PHYs used
#   on SFP modules.
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   427eabc80b67a10778b322b43b3b6c4d8fc02526
#    drivers/net/phy/marvell.c | 16 ++++++++++++++++
#    1 file changed, 16 insertions(+)
# 
# Author:    Russell King (Oracle) (Tue 30 Jan 14:38:27 GMT 2024)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:40:27 BST 2024)
# 
#   net: phy: bcm84881: provide phy_query_inband() implementation
# 
#   BCM84881 has no support for inband signalling, so this is a trivial
#   implementation that returns no support for inband.
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   6ea582b10cc3e5c373530ced629c248edef5bae7
#    drivers/net/phy/bcm84881.c | 10 ++++++++++
#    1 file changed, 10 insertions(+)
# 
# Author:    Russell King (Oracle) (Mon 21 Aug 13:04:32 BST 2023)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:40:27 BST 2024)
# 
#   net: phy: add phy_query_inband()
# 
#   Add a method to query the PHY's in-band capabilities for a PHY
#   interface mode. This can be used to determine for the specified
#   interface mode whether in-band signalling is supported, and whether
#   the PHY requires in-band signalling.
# 
#   When not implemented, or the PHY driver doesn't report any modes
#   for the interface, LINK_INBAND_VALID will not be set. When set, the
#   remainder of the flags can be interpreted.
# 
#   LINK_INBAND_POSSIBLE means that the device can be configured to use
#   or not use in-band signalling. Later patches may add support to
#   configure this at the PHY.
# 
#   LINK_INBAND_REQUIRED means that the device uses in-band signalling
#   which can not be disabled.
# 
#   When only LINK_INBAND_VALID has been set, this means that the device
#   does not support any in-band signalling, and can't be configured to
#   do so.
# 
#   "Bypass" mode (where the device may be configured for in-band, but
#   may still bring the link up if there is no in-band received from the
#   link partner) is not considered in this patch.
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   2cf464c25db5f495a3ff0d0fecd2ebe12c6f102d
#    drivers/net/phy/phy.c | 21 +++++++++++++++++++++
#    include/linux/phy.h   | 28 ++++++++++++++++++++++++++++
#    2 files changed, 49 insertions(+)
# 
# Author:    Russell King (Oracle) (Fri 26 Apr 09:58:56 BST 2024)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:40:27 BST 2024)
# 
#   net: phylink: move phylink_pcs_neg_mode() in phylink_major_config()
# 
#   Move the call to phylink_pcs_neg_mode() in phylink_major_config() after
#   we have selected the appropriate PCS to later allow the capabilities of
#   the PCS to be used when deciding which pcs_neg_mode should be used.
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   193e76843e0600322bfadf3811ba276ac39b095c
#    drivers/net/phy/phylink.c | 8 ++++----
#    1 file changed, 4 insertions(+), 4 deletions(-)
# 
# Author:    Russell King (Oracle) (Sat 27 Apr 14:09:10 BST 2024)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:40:26 BST 2024)
# 
#   net: phylink: add debug for phylink_major_config()
# 
#   Now that we have a more complexity in phylink_major_config(), augment
#   the debugging so we can see what's going on there.
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   a9bb5d5403e0ac3c6471e88f9fab329deacf9422
#    drivers/net/phy/phylink.c | 27 ++++++++++++++++++++++++++-
#    1 file changed, 26 insertions(+), 1 deletion(-)
# 
# Author:    Russell King (Oracle) (Mon 22 Apr 16:44:44 BST 2024)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:40:26 BST 2024)
# 
#   net: phylink: switch to MLO_AN_PHY when PCS uses outband
# 
#   phylink_pcs_neg_mode() currently only outband mode when we are using
#   MLO_AN_PHY or MLO_AN_FIXED. However, we want to extend the use of this
#   to MLO_AN_INBAND when the PCS and/or PHY are unable to use inband mode.
# 
#   For example, when a PHY is not capable of supporting inband mode, we
#   need to operate in outband mode. However, using outband mode for the
#   PCS while using MLO_AN_INBAND causes drivers to misbehave as it is not
#   a situation they are expecting. Therefore, we need to switch to
#   MLO_AN_PHY when using outband mode.
# 
#   Add the logic to select MLO_AN_PHY mode when phylink_pcs_neg_mode()
#   indicates that it will be using outband mode with a PHY and the
#   requested mode is MLO_AN_INBAND.
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   33eadbebbb0746c4d4f1223f867621b1818a91e7
#    drivers/net/phy/phylink.c | 19 +++++++++++++++++--
#    1 file changed, 17 insertions(+), 2 deletions(-)
# 
# Author:    Russell King (Oracle) (Fri 26 Apr 10:30:39 BST 2024)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:40:26 BST 2024)
# 
#   net: phylink: split cur_link_an_mode into requested and active
# 
#   There is an interdependence between the link_an_mode and pcs_neg_mode
#   that at least drivers rely upon. In order to support changing the MAC
#   link_an_mode when we switch interfaces, we need to keep track of its
#   original setting so that we can appropriately re-calculate pcs_neg_mode
#   from the same starting point.
# 
#   Split the current link_an_mode into a requested link_an_mode and an
#   active link_an_mode. This will allow us to keep the MAC's link_an_mode
#   synchronised with the pcs_neg_mode should we decide to switch to
#   outband with a PHY.
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   aca110fd5a9be9aaf2f8fb9c30a7a9526f764863
#    drivers/net/phy/phylink.c | 60 ++++++++++++++++++++++++++---------------------
#    1 file changed, 33 insertions(+), 27 deletions(-)
# 
# Author:    Russell King (Oracle) (Fri 26 Apr 13:04:32 BST 2024)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:40:26 BST 2024)
# 
#   net: phylink: simplify how SFP PHYs are attached
# 
#   There are a few issues with how SFP PHYs are attached:
# 
#   a) The phylink_sfp_connect_phy() and phylink_sfp_config_phy() code
#      validates the configuration three times:
# 
#   1. To discover the support/advertising masks that the PHY/PCS/MAC
#      can support in order to select an interface.
#   2. To validate the selected interface.
#   3. When the PHY is brought up after being attached, another validation
#      is done.
# 
#      This is needlessly complex.
# 
#   b) The configuration is set prior to the PHY being attached, which
#      means we don't have the PHY available in phylink_major_config()
#      for phylink_pcs_neg_mode() to make decisions upon.
# 
#   We have already added an extra step to validate the selected interface,
#   so we can now move the attachment and bringup of the PHY earlier,
#   inside phylink_sfp_config_phy(). This results in the validation at
#   step 2 above becoming entirely unnecessary, so remove that too.
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   f889c15c30e388b199cfdc224dbf9410977827dd
#    drivers/net/phy/phylink.c | 43 ++++++++++++++-----------------------------
#    1 file changed, 14 insertions(+), 29 deletions(-)
# 
# Author:    Russell King (Oracle) (Fri 26 Apr 12:58:22 BST 2024)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:40:25 BST 2024)
# 
#   net: phylink: validate sfp_select_interface() returned interface
# 
#   When configuring for a SFP PHY, validate that the MAC supports the
#   interface returned from sfp_select_interface(). This is a preparatory
#   step to reorganising how a PHY on a SFP module is handled.
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   3236566589b28eaa636890d9c5fcbb8b003f4d9d
#    drivers/net/phy/phylink.c | 43 ++++++++++++++++++++++++++++++-------------
#    1 file changed, 30 insertions(+), 13 deletions(-)
# 
# Author:    Russell King (Oracle) (Thu  1 Jun 09:48:20 BST 2023)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:40:25 BST 2024)
# 
#   net: phylink: allow EEE management for SFPs without PHYs
# 
#   Allow EEE management for SFPs without accessible PHYs.
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   a790044d9ea65c36028d483cbd358c8980d94bd6
#    drivers/net/phy/phylink.c | 11 ++++++++++-
#    1 file changed, 10 insertions(+), 1 deletion(-)
# 
# Author:    Russell King (Oracle) (Wed 16 Aug 20:48:48 BST 2023)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:40:25 BST 2024)
# 
#   net: dsa: mt7530: convert to phylink managed EEE
# 
#   Fixme: doesn't bit 25 and 26 also need to be set in the PMCR for
#   PMCR_FORCE_EEE100 and PMCR_FORCE_EEE1G to take effect?
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   9e983f5441ce9f3f2fd1b2c28b0434f28aa0ab1a
#    drivers/net/dsa/mt7530.c | 98 ++++++++++++++++++++++++++----------------------
#    drivers/net/dsa/mt7530.h |  6 +--
#    2 files changed, 54 insertions(+), 50 deletions(-)
# 
# Author:    Russell King (Oracle) (Wed 16 Aug 14:02:11 BST 2023)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:40:24 BST 2024)
# 
#   net: dsa: add support for phylink managed EEE
# 
#   Add support to allow DSA drivers to use phylink managed EEE, with only
#   needing support for controlling the LPI state.
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   459eee339d6795e6d71a83100712fa8fdd1a6ee0
#    net/dsa/user.c | 42 ++++++++++++++++++++++++++----------------
#    1 file changed, 26 insertions(+), 16 deletions(-)
# 
# Author:    Russell King (Oracle) (Thu 17 Aug 16:32:32 BST 2023)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:40:24 BST 2024)
# 
#   net: dsa: mv88e6xxx: add EEE controls
# 
#   Add phylink EEE control methods to allow EEE to be configured. When
#   LPI is to be disabled, we force the port to have EEE disabled, but
#   when enabling EEE, if the port is under the control of the PPU, we
#   stop forcing it, otherwise we force-enable EEE.
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   12943f7447dfdd36c7b118b4970a733419be219a
#    drivers/net/dsa/mv88e6xxx/chip.c | 45 ++++++++++++++++++++++++++++++++++++++++
#    1 file changed, 45 insertions(+)
# 
# Author:    Russell King (Oracle) (Thu 17 Aug 15:36:22 BST 2023)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:40:24 BST 2024)
# 
#   net: dsa: mv88e6xxx: add support for EEE forcing
# 
#   Add support for EEE forcing using the MAC control register. Replace
#   the 88e6393x errata 4.5 EEE disable code with a call to the new EEE
#   forcing code.
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   a37fa764f67d7b33cfa9a22d8ce3e1bf00b8d284
#    drivers/net/dsa/mv88e6xxx/port.c | 39 +++++++++++++++++++++++++++++++--------
#    drivers/net/dsa/mv88e6xxx/port.h |  1 +
#    2 files changed, 32 insertions(+), 8 deletions(-)
# 
# Author:    Russell King (Oracle) (Thu 17 Aug 16:32:32 BST 2023)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:40:23 BST 2024)
# 
#   net: dsa: mv88e6xxx: add port_set_eee() method
# 
#   Add a port_set_eee() method to allow the EEE settings for a port to be
#   configured.
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   9169749e782b58bfd580146469157142a3225a83
#    drivers/net/dsa/mv88e6xxx/chip.h | 10 ++++++++++
#    1 file changed, 10 insertions(+)
# 
# Author:    Russell King (Oracle) (Wed 31 May 17:17:11 BST 2023)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:40:23 BST 2024)
# 
#   net: mvpp2: add EEE implementation
# 
#   Add EEE support for mvpp2, using phylink's EEE implementation, which
#   means we just need to implement the two methods for LPI control, and
#   with the initial configuration. Only the GMAC is supported, so only
#   100M, 1G and 2.5G speeds.
# 
#   Disabling LPI requires clearing a single bit. Enabling LPI needs a full
#   configuration of several values, as the timer values are dependent on
#   the MAC operating speed.
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   ce02531f81a7bcc00ed074c82744e0baec2d2de0
#    drivers/net/ethernet/marvell/mvpp2/mvpp2.h      |  5 ++
#    drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c | 85 +++++++++++++++++++++++++
#    2 files changed, 90 insertions(+)
# 
# Author:    Russell King (Oracle) (Wed 31 May 15:17:24 BST 2023)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:40:23 BST 2024)
# 
#   net: mvneta: convert to phylink EEE implementation
# 
#   Convert mvneta to use phylink's EEE implementation, which means we just
#   need to implement the two methods for LPI control, and adding the
#   initial configuration.
# 
#   Disabling LPI requires clearing a single bit. Enabling LPI needs a full
#   configuration of several values, as the timer values are dependent on
#   the MAC operating speed.
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   08de07efdb45669a70dffb74b28722cc0d941bf0
#    drivers/net/ethernet/marvell/mvneta.c | 107 +++++++++++++++++++---------------
#    1 file changed, 60 insertions(+), 47 deletions(-)
# 
# Author:    Russell King (Oracle) (Wed 31 May 10:02:35 BST 2023)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:40:22 BST 2024)
# 
#   net: phylink: add EEE management
# 
#   Add EEE management to phylink.
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   7dffe465e60c13801a6d51edfcd70f0b306b7bdd
#    drivers/net/phy/phylink.c | 135 +++++++++++++++++++++++++++++++++++++++++++++-
#    include/linux/phylink.h   |  36 +++++++++++++
#    2 files changed, 170 insertions(+), 1 deletion(-)
# 
# Author:    Russell King (Oracle) (Wed 31 May 10:02:35 BST 2023)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:40:22 BST 2024)
# 
#   net: phylink: add phylink_link_is_up() helper
# 
#   Add a helper to determine whether the link is up or down. Currently
#   this is only used in one location, but becomes necessary to test
#   when reconfiguring EEE.
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   5e19b9b1a2094cb380519997c447fb63018bee27
#    drivers/net/phy/phylink.c | 10 ++++++----
#    1 file changed, 6 insertions(+), 4 deletions(-)
# 
# Author:    Russell King (Oracle) (Fri 24 May 22:04:13 BST 2024)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:37:33 BST 2024)
# 
#   net: dsa: remove obsolete phylink dsa_switch operations
# 
#   No driver now uses the DSA switch phylink members, so we can now remove
#   the shim functions and method pointers. Arrange to print an error
#   message and fail registration if a DSA driver does not provide the
#   phylink MAC operations structure.
# 
#   Signed-off-by: Russell King (oracle)
# 
#   a83ceb824a2f91d76e95639c9f850b78d62e4ab7
#    net/dsa/dsa.c  |  5 +++++
#    net/dsa/port.c | 34 +---------------------------------
#    2 files changed, 6 insertions(+), 33 deletions(-)
# 
# Author:    Russell King (Oracle) (Fri 18 Oct 10:35:33 BST 2024)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:36:03 BST 2024)
# 
#   net: phylink: simplify phylink_parse_fixedlink()
# 
#   phylink_parse_fixedlink() wants to preserve the pause, asym_pause and
#   autoneg bits in pl->supported. Rather than reading the bits into
#   separate bools, zeroing pl->supported, and then setting them if they
#   were previously set, use a mask and linkmode_and() to achieve the same
#   result.
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   7a2612b21b8ee64badb20ca193d1eeb728d1dfc3
#    drivers/net/phy/phylink.c | 19 ++++++-------------
#    1 file changed, 6 insertions(+), 13 deletions(-)
# 
# Author:    Russell King (Oracle) (Fri 18 Oct 10:35:13 BST 2024)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:36:03 BST 2024)
# 
#   net: phylink: remove "using_mac_select_pcs"
# 
#   With DSA's implementation of the mac_select_pcs() method removed, we
#   can now remove the detection of mac_select_pcs() implementation.
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   fb2dbb27e96002032defb0d2fd6a7dae0574a210
#    drivers/net/phy/phylink.c | 12 ++----------
#    1 file changed, 2 insertions(+), 10 deletions(-)
# 
# Author:    Russell King (Oracle) (Fri 18 Oct 10:35:13 BST 2024)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:36:02 BST 2024)
# 
#   net: phylink: remove use of pl->pcs in phylink_validate_mac_and_pcs()
# 
#   When the mac_select_pcs() method is not implemented, there is no way
#   for pl->pcs to be set to a non-NULL value. This was here to support
#   the old phylink_set_pcs() method which has been removed a few years
#   ago. Simplify the code in phylink_validate_mac_and_pcs().
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   d4713f12599e6ff49738ab9485e26fe296547985
#    drivers/net/phy/phylink.c | 4 +---
#    1 file changed, 1 insertion(+), 3 deletions(-)
# 
# Author:    Russell King (Oracle) (Fri 18 Oct 10:35:13 BST 2024)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:36:02 BST 2024)
# 
#   net: phylink: allow mac_select_pcs() to remove a PCS
# 
#   phylink has historically not permitted a PCS to be removed. An attempt
#   to permit this with phylink_set_pcs() resulted in comments indicating
#   that there was no need for this. This behaviour has been propagated
#   forward to the mac_select_pcs() approach as it was believed from these
#   comments that changing this would be NAK'd.
# 
#   However, with mac_select_pcs(), it takes more code and thus complexity
#   to maintain this behaviour, which can - and in this case has - resulted
#   in a bug. If mac_select_pcs() returns NULL for a particular interface
#   type, but there is already a PCS in-use, then we skip the pcs_validate()
#   method, but continue using the old PCS. Also, it wouldn't be expected
#   behaviour by implementers of mac_select_pcs().
# 
#   Allow this by removing this old unnecessary restriction.
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   b4858d43a97e6c213f9b647d07d7923a9f4eba21
#    drivers/net/phy/phylink.c | 2 +-
#    1 file changed, 1 insertion(+), 1 deletion(-)
# 
# Author:    Russell King (Oracle) (Fri 18 Oct 10:35:12 BST 2024)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:36:02 BST 2024)
# 
#   net: dsa: mv88e6xxx: return NULL when no PCS is present
# 
#   Rather than returning an EOPNOTSUPP error pointer when the switch
#   has no support for PCS, return NULL to indicate that no PCS is
#   required.
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   9e14ae9465303c8959dd8bf37878dc66a85ac43d
#    drivers/net/dsa/mv88e6xxx/chip.c | 2 +-
#    1 file changed, 1 insertion(+), 1 deletion(-)
# 
# Author:    Russell King (Oracle) (Fri 18 Oct 10:35:12 BST 2024)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:36:02 BST 2024)
# 
#   net: dsa: remove dsa_port_phylink_mac_select_pcs()
# 
#   There is no longer any reason to implement the mac_select_pcs()
#   callback in DSA. Returning ERR_PTR(-EOPNOTSUPP) is functionally
#   equivalent to not providing the function.
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   483a52c200b85fbcf812be55f6b7fcd996671666
#    net/dsa/port.c | 8 --------
#    1 file changed, 8 deletions(-)
# 
# Author:    Russell King (Oracle) (Fri 18 Oct 10:35:05 BST 2024)
# Committer: Russell King (Oracle) (Fri 18 Oct 10:36:01 BST 2024)
# 
#   net: dsa: remove obsolete phylink dsa_switch operations
# 
#   No driver now uses the DSA switch phylink members, so we can now remove
#   the method pointers, but we need to leave empty shim functions to allow
#   those drivers that do not provide phylink MAC operations structure to
#   continue functioning.
# 
#   Signed-off-by: Russell King (oracle)
#   Reviewed-by: Vladimir Oltean
#   Tested-by: Vladimir Oltean # sja1105, felix, dsa_loop
#   Link: https://patch.msgid.link/E1swKNV-0060oN-1b@rmk-PC.armlinux.org.uk
#   Signed-off-by: Jakub Kicinski
# 
#   25b6615e4c78f8d207fc9f6c88b6424628182da3
#    include/net/dsa.h | 15 ---------------
#    net/dsa/dsa.c     |  8 --------
#    net/dsa/port.c    | 34 +---------------------------------
#    3 files changed, 1 insertion(+), 56 deletions(-)
# 
# Author:    Russell King (Oracle) (Thu 10 Oct 18:14:37 BST 2024)
# Committer: Russell King (Oracle) (Thu 10 Oct 18:14:37 BST 2024)
# 
#   net: pcs: xpcs: move Wangxun VR_XS_PCS_DIG_CTRL1 configuration
# 
#   According to commits 2a22b7ae2fa3 ("net: pcs: xpcs: adapt Wangxun NICs
#   for SGMII mode") and 2deea43f386d ("net: pcs: xpcs: add 1000BASE-X AN
#   interrupt support"), Wangxun devices need special VR_XS_PCS_DIG_CTRL1
#   settings for SGMII and 1000BASE-X. Both SGMII and 1000BASE-X use the
#   same settings.
# 
#   Rather than placing these in the individual xpcs_config_*() functions,
#   move it to where we already test for the Wangxun devices in
#   xpcs_do_config().
# 
#   Signed-off-by: Russell King (Oracle)
#   Signed-off-by: David S. Miller
# 
#   dc770f983ce9175aeb5959930e13bee4178e4961
#    drivers/net/pcs/pcs-xpcs.c | 14 ++++++++------
#    1 file changed, 8 insertions(+), 6 deletions(-)
# 
# Author:    Russell King (Oracle) (Thu 10 Oct 18:14:37 BST 2024)
# Committer: Russell King (Oracle) (Thu 10 Oct 18:14:37 BST 2024)
# 
#   net: pcs: xpcs: correctly place DW_VR_MII_DIG_CTRL1_2G5_EN
# 
#   Place DW_VR_MII_DIG_CTRL1_2G5_EN with the other DW_VR_MII_DIG_CTRL1
#   definitions rather than in the middle of a register list.
# 
#   Signed-off-by: Russell King (Oracle)
#   Signed-off-by: David S. Miller
# 
#   a21044ac9b90c712c72239c49ba4202709707f4f
#    drivers/net/pcs/pcs-xpcs.h | 3 +--
#    1 file changed, 1 insertion(+), 2 deletions(-)
# 
# Author:    Russell King (Oracle) (Thu 10 Oct 18:14:36 BST 2024)
# Committer: Russell King (Oracle) (Thu 10 Oct 18:14:36 BST 2024)
# 
#   net: pcs: xpcs: use dev_*() to print messages
# 
#   Use the dev_*() family of functions to print all messages from the XPCS
#   driver so we know which instance issues the messages.
# 
#   Signed-off-by: Russell King (Oracle)
#   Signed-off-by: David S. Miller
# 
#   0ac9f2cb9e7cf916a9e6955e483329912a399b71
#    drivers/net/pcs/pcs-xpcs.c | 44 ++++++++++++++++++++++----------------------
#    1 file changed, 22 insertions(+), 22 deletions(-)
# 
# Author:    Russell King (Oracle) (Thu 10 Oct 18:14:36 BST 2024)
# Committer: Russell King (Oracle) (Thu 10 Oct 18:14:36 BST 2024)
# 
#   net: pcs: xpcs: convert to use read_poll_timeout()
# 
#   Convert the xpcs driver to use read_poll_timeout() when waiting for
#   reset to complete, rather than open-coding this.
# 
#   Signed-off-by: Russell King (Oracle)
#   Signed-off-by: David S. Miller
# 
#   32fa243acd5a36131604ad4985e2030dd34f5256
#    drivers/net/pcs/pcs-xpcs.c | 17 +++++++----------
#    1 file changed, 7 insertions(+), 10 deletions(-)
# 
# Author:    Russell King (Oracle) (Thu 10 Oct 18:14:36 BST 2024)
# Committer: Russell King (Oracle) (Thu 10 Oct 18:14:36 BST 2024)
# 
#   net: pcs: xpcs: add _modify() accessors
# 
#   The xpcs driver does a lot of read-modify-write operations on
#   registers, which leads to long-winded code to read the register, check
#   whether the read was successful, modify the value in some way, and then
#   write it back.
# 
#   We have a mdiodev _modify() accessor that encapsulates this, and does
#   the register modification under the MDIO bus lock ensuring that the
#   modification is atomic with respect to other bus operations. Convert
#   the xpcs driver to use this accessor.
# 
#   Signed-off-by: Russell King (Oracle)
#   Signed-off-by: David S. Miller
# 
#   eca8cfb56c333e5daff4e2742c3e2d37f514bd8c
#    drivers/net/pcs/pcs-xpcs-nxp.c |  24 ++----
#    drivers/net/pcs/pcs-xpcs-wx.c  |  56 ++++++--------
#    drivers/net/pcs/pcs-xpcs.c     | 165 ++++++++++++++++++-----------------------
#    drivers/net/pcs/pcs-xpcs.h     |   1 +
#    4 files changed, 104 insertions(+), 142 deletions(-)
# 
# Author:    Russell King (Oracle) (Thu 10 Oct 18:14:35 BST 2024)
# Committer: Russell King (Oracle) (Thu 10 Oct 18:14:35 BST 2024)
# 
#   net: pcs: xpcs: use FIELD_PREP() and FIELD_GET()
# 
#   Convert xpcs to use the bitfield macros rather than definining the
#   bitfield shifts and open-coding the insertion and extraction of these
#   bitfields.
# 
#   Signed-off-by: Russell King (Oracle)
#   Signed-off-by: David S. Miller
# 
#   eb5245c70009276c52a9ec9000f1cd235ce1c31e
#    drivers/net/pcs/pcs-xpcs.c | 14 ++++++--------
#    drivers/net/pcs/pcs-xpcs.h |  4 ----
#    2 files changed, 6 insertions(+), 12 deletions(-)
# 
# Author:    Russell King (Oracle) (Thu 10 Oct 18:14:35 BST 2024)
# Committer: Russell King (Oracle) (Thu 10 Oct 18:14:35 BST 2024)
# 
#   net: pcs: xpcs: move searching ID list out of line
# 
#   Move the searching of the physical ID out of xpcs_create() and into
#   its own xpcs_identify() function, which makes it self contained.
#   This reduces the complexity in xpcs_craete(), making it easier to
#   follow, rather than having a lot of once-run code in the big for()
#   loop.
# 
#   Signed-off-by: Russell King (Oracle)
#   Signed-off-by: David S. Miller
# 
#   dffe211a790ffd43f59f4cd97ce3de4ea57f8af8
#    drivers/net/pcs/pcs-xpcs.c | 41 +++++++++++++++++++++--------------------
#    1 file changed, 21 insertions(+), 20 deletions(-)
# 
# Author:    Russell King (Oracle) (Thu 10 Oct 18:14:35 BST 2024)
# Committer: Russell King (Oracle) (Thu 10 Oct 18:14:35 BST 2024)
# 
#   net: pcs: xpcs: rename xpcs_get_id()
# 
#   Rename xpcs_get_id() to xpcs_read_id() which more closely reflects
#   the purpose of this function.
# 
#   Signed-off-by: Russell King (Oracle)
#   Signed-off-by: David S. Miller
# 
#   cd681b84d96d92da95ff217ddfa3a3babf827366
#    drivers/net/pcs/pcs-xpcs.c | 4 ++--
#    1 file changed, 2 insertions(+), 2 deletions(-)
# 
# Author:    Russell King (Oracle) (Thu 10 Oct 18:14:34 BST 2024)
# Committer: Russell King (Oracle) (Thu 10 Oct 18:14:34 BST 2024)
# 
#   net: pcs: xpcs: move definition of struct dw_xpcs to private header
# 
#   There should be no reason for anything outside the XPCS code to know
#   the contents of struct dw_xpcs - this is a private structure to XPCS.
#   Move the definition to the private pcs-xpcs.h header, leaving a
#   declaration in the global pcs/pcs-xpcs.h
# 
#   Signed-off-by: Russell King (Oracle)
#   Signed-off-by: David S. Miller
# 
#   80e5323ca90d6e12eddd8f81bd25fe6361ea4fc3
#    drivers/net/pcs/pcs-xpcs.h   | 18 ++++++++++++++++++
#    include/linux/pcs/pcs-xpcs.h | 18 +-----------------
#    2 files changed, 19 insertions(+), 17 deletions(-)
# 
# Author:    Russell King (Oracle) (Thu 10 Oct 18:14:34 BST 2024)
# Committer: Russell King (Oracle) (Thu 10 Oct 18:14:34 BST 2024)
# 
#   net: pcs: xpcs: provide a helper to get the phylink pcs given xpcs
# 
#   Provide a helper to provide the pointer to the phylink_pcs struct
#   given a valid xpcs pointer. This will be necessary when we make
#   struct dw_xpcs private to pcs-xpcs.c
# 
#   Signed-off-by: Russell King (Oracle)
#   Signed-off-by: David S. Miller
# 
#   d24a4b2c633a6eef2b53bdecb532f86cd416f404
#    drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c | 2 +-
#    drivers/net/pcs/pcs-xpcs.c                        | 6 ++++++
#    include/linux/pcs/pcs-xpcs.h                      | 1 +
#    3 files changed, 8 insertions(+), 1 deletion(-)
# 
# Author:    Russell King (Oracle) (Thu 10 Oct 18:14:34 BST 2024)
# Committer: Russell King (Oracle) (Thu 10 Oct 18:14:34 BST 2024)
# 
#   net: pcs: xpcs: pass xpcs instead of xpcs->id to xpcs_find_compat()
# 
#   xpcs_find_compat() is now always passed xpcs->id. Rather than always
#   dereferencing this in the caller, move it into xpcs_find_compat(),
#   thus making this function consistent with most of the other xpcs
#   functions in taking an xpcs pointer.
# 
#   Signed-off-by: Russell King (Oracle)
#   Signed-off-by: David S. Miller
# 
#   c3c3b82c7599e5e24c65504b5ba853da9df4c54d
#    drivers/net/pcs/pcs-xpcs.c | 14 +++++++-------
#    1 file changed, 7 insertions(+), 7 deletions(-)
# 
# Author:    Russell King (Oracle) (Thu 10 Oct 18:14:34 BST 2024)
# Committer: Russell King (Oracle) (Thu 10 Oct 18:14:34 BST 2024)
# 
#   net: pcs: xpcs: don't use array for interface
# 
#   Currently, xpcs uses an array of interfaces that each "compat" entry
#   supports. When looking up the compat entry for an interface, we
#   iterate over the compat entries and then over each interface.
# 
#   Since each compat entry only has a single interface in its interfaces
#   array, replace the array with a single member in the compat structure.
# 
#   Signed-off-by: Russell King (Oracle)
#   Signed-off-by: David S. Miller
# 
#   2d9ff35bb97ea222fa98f5be73fdb434a259bdd9
#    drivers/net/pcs/pcs-xpcs.c | 71 +++++++++-------------------------------------
#    1 file changed, 14 insertions(+), 57 deletions(-)
# 
# Author:    Russell King (Oracle) (Thu 10 Oct 18:14:33 BST 2024)
# Committer: Russell King (Oracle) (Thu 10 Oct 18:14:33 BST 2024)
# 
#   net: pcs: xpcs: remove dw_xpcs_compat enum
# 
#   There is no reason for the struct dw_xpcs_compat arrays to be a fixed
#   size other than the way we iterate over them. The index into the array
#   isn't used for anything, and having them fixed size needlessly wastes
#   space.
# 
#   Remove the enum that defines their size, and instead use an empty
#   array entry (with NULL ->supported) to mark the end of the array.
# 
#   Signed-off-by: Russell King (Oracle)
#   Signed-off-by: David S. Miller
# 
#   523bd58de2d373f0f229a40540a7cad560de4c14
#    drivers/net/pcs/pcs-xpcs.c | 69 +++++++++++++++++-----------------------------
#    1 file changed, 25 insertions(+), 44 deletions(-)
# 
# Author:    Russell King (Oracle) (Thu 10 Oct 18:10:59 BST 2024)
# Committer: Russell King (Oracle) (Thu 10 Oct 18:14:18 BST 2024)
# 
#   net: pcs: xpcs: make xpcs_do_config() and xpcs_link_up() internal
# 
#   As nothing outside pcs-xpcs.c calls neither xpcs_do_config() nor
#   xpcs_link_up(), remove their exports and prototypes.
# 
#   Reviewed-by: Vladimir Oltean
#   Signed-off-by: Russell King (Oracle)
#   Link: https://patch.msgid.link/E1svfMv-005ZIv-2M@rmk-PC.armlinux.org.uk
#   Signed-off-by: Jakub Kicinski
# 
#   325088cc386197039507d6acb9474ca680608bb4
#    drivers/net/pcs/pcs-xpcs.c   | 11 +++++------
#    include/linux/pcs/pcs-xpcs.h |  4 ----
#    2 files changed, 5 insertions(+), 10 deletions(-)
# 
# Author:    Russell King (Oracle) (Thu 10 Oct 18:10:58 BST 2024)
# Committer: Russell King (Oracle) (Thu 10 Oct 18:14:18 BST 2024)
# 
#   net: pcs: xpcs: drop interface argument from xpcs_create*()
# 
#   The XPCS sub-driver no longer uses the "interface" argument to the
#   xpcs_create_mdiodev() and xpcs_create_fwnode() functions. Remove
#   this now unnecessary argument, updating the stmmac driver
#   appropriately.
# 
#   Reviewed-by: Vladimir Oltean
#   Signed-off-by: Russell King (Oracle)
#   Link: https://patch.msgid.link/E1svfMp-005ZIp-UX@rmk-PC.armlinux.org.uk
#   Signed-off-by: Jakub Kicinski
# 
#   07e688f02f8d1f18e6a2dccf1a8187248abec307
#    drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c |  7 +++----
#    drivers/net/pcs/pcs-xpcs.c                        | 10 +++-------
#    include/linux/pcs/pcs-xpcs.h                      |  6 ++----
#    3 files changed, 8 insertions(+), 15 deletions(-)
# 
# Author:    Russell King (Oracle) (Thu 10 Oct 18:10:58 BST 2024)
# Committer: Russell King (Oracle) (Thu 10 Oct 18:14:17 BST 2024)
# 
#   net: dsa: sja1105: use phylink_pcs internally
# 
#   Use xpcs_create_pcs_mdiodev() to create the XPCS instance, storing
#   and using the phylink_pcs pointer internally, rather than dw_xpcs.
#   Use xpcs_destroy_pcs() to destroy the XPCS instance when we've
#   finished with it.
# 
#   Signed-off-by: Russell King (Oracle)
#   Link: https://patch.msgid.link/E1svfMk-005ZIj-R3@rmk-PC.armlinux.org.uk
#   Signed-off-by: Jakub Kicinski
# 
#   2f7779d321d93cd9de17d02a64baeabc7c4ca58c
#    drivers/net/dsa/sja1105/sja1105.h      |  2 +-
#    drivers/net/dsa/sja1105/sja1105_main.c | 16 ++++------------
#    drivers/net/dsa/sja1105/sja1105_mdio.c | 28 +++++++++++++---------------
#    3 files changed, 18 insertions(+), 28 deletions(-)
# 
# Author:    Russell King (Oracle) (Thu 10 Oct 18:10:58 BST 2024)
# Committer: Russell King (Oracle) (Thu 10 Oct 18:14:17 BST 2024)
# 
#   net: dsa: sja1105: call PCS config/link_up via pcs_ops structure
# 
#   Call the PCS operations through the ops structure, which avoids needing
#   to export xpcs internal functions.
# 
#   Signed-off-by: Russell King (Oracle)
#   Link: https://patch.msgid.link/E1svfMf-005ZId-Mx@rmk-PC.armlinux.org.uk
#   Signed-off-by: Jakub Kicinski
# 
#   d7c6202f6ca1e981c2e94d0601aff8333dfb6ac4
#    drivers/net/dsa/sja1105/sja1105_main.c | 10 +++++++---
#    1 file changed, 7 insertions(+), 3 deletions(-)
# 
# Author:    Russell King (Oracle) (Thu 10 Oct 18:10:57 BST 2024)
# Committer: Russell King (Oracle) (Thu 10 Oct 18:14:17 BST 2024)
# 
#   net: dsa: sja1105: simplify static configuration reload
# 
#   The static configuration reload saves the port speed in the static
#   configuration tables by first converting it from the internal
#   respresentation to the SPEED_xxx ethtool representation, and then
#   converts it back to restore the setting. This is because
#   sja1105_adjust_port_config() takes the speed as SPEED_xxx.
# 
#   However, this is unnecessarily complex. If we split
#   sja1105_adjust_port_config() up, we can simply save and restore the
#   mac[port].speed member in the static configuration tables.
# 
#   Signed-off-by: Russell King (Oracle)
#   Link: https://patch.msgid.link/E1svfMa-005ZIX-If@rmk-PC.armlinux.org.uk
#   Signed-off-by: Jakub Kicinski
# 
#   72295265c6cb86ce3dce5e33ab385cbee5bcec51
#    drivers/net/dsa/sja1105/sja1105_main.c | 65 ++++++++++++++++++----------------
#    1 file changed, 34 insertions(+), 31 deletions(-)
# 
# Author:    Russell King (Oracle) (Thu 10 Oct 18:10:57 BST 2024)
# Committer: Russell King (Oracle) (Thu 10 Oct 18:14:16 BST 2024)
# 
#   net: wangxun: txgbe: use phylink_pcs internally
# 
#   Use xpcs_create_pcs_mdiodev() to create the XPCS instance, storing
#   and using the phylink_pcs pointer internally, rather than dw_xpcs.
#   Use xpcs_destroy_pcs() to destroy the XPCS instance when we've
#   finished with it.
# 
#   Signed-off-by: Russell King (Oracle)
#   Reviewed-by: Andrew Lunn
#   Link: https://patch.msgid.link/E1svfMV-005ZIR-FE@rmk-PC.armlinux.org.uk
#   Signed-off-by: Jakub Kicinski
# 
#   af8353842089477c1bc9014b9c9dcc24793b839b
#    drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c  | 18 +++++++++---------
#    drivers/net/ethernet/wangxun/txgbe/txgbe_type.h |  2 +-
#    2 files changed, 10 insertions(+), 10 deletions(-)
# 
# Author:    Russell King (Oracle) (Thu 10 Oct 18:10:57 BST 2024)
# Committer: Russell King (Oracle) (Thu 10 Oct 18:14:16 BST 2024)
# 
#   net: pcs: xpcs: add xpcs_destroy_pcs() and xpcs_create_pcs_mdiodev()
# 
#   Provide xpcs create/destroy functions that return and take a phylink_pcs
#   pointer instead of an xpcs pointer. This will be used by drivers that
#   have been converted to use phylink_pcs pointers internally, rather than
#   dw_xpcs pointers.
# 
#   As xpcs_create_mdiodev() no longer makes use of its interface argument,
#   pass PHY_INTERFACE_MODE_NA into xpcs_create_mdiodev() until it is
#   removed later in the series.
# 
#   Reviewed-by: Vladimir Oltean
#   Signed-off-by: Russell King (Oracle)
#   Link: https://patch.msgid.link/E1svfMQ-005ZIL-Bi@rmk-PC.armlinux.org.uk
#   Signed-off-by: Jakub Kicinski
# 
#   b63243460de5a1a63309b5cf8d9f14db102cf65d
#    drivers/net/pcs/pcs-xpcs.c   | 18 ++++++++++++++++++
#    include/linux/pcs/pcs-xpcs.h |  3 +++
#    2 files changed, 21 insertions(+)
# 
# Author:    Russell King (Oracle) (Thu 10 Oct 18:10:56 BST 2024)
# Committer: Russell King (Oracle) (Thu 10 Oct 18:14:16 BST 2024)
# 
#   net: pcs: xpcs: get rid of xpcs_init_iface()
# 
#   xpcs_init_iface() no longer does anything with the interface mode, and
#   now merely does configuration related to the PMA ID. Move this back
#   into xpcs_create() as it doesn't warrant being a separate function
#   anymore.
# 
#   Reviewed-by: Vladimir Oltean
#   Signed-off-by: Russell King (Oracle)
#   Link: https://patch.msgid.link/E1svfML-005ZIF-84@rmk-PC.armlinux.org.uk
#   Signed-off-by: Jakub Kicinski
# 
#   3370bc0ff82859ccead5037e7cc9fefe5ad50809
#    drivers/net/pcs/pcs-xpcs.c | 17 ++++-------------
#    1 file changed, 4 insertions(+), 13 deletions(-)
# 
# Author:    Russell King (Oracle) (Thu 10 Oct 18:10:56 BST 2024)
# Committer: Russell King (Oracle) (Thu 10 Oct 18:14:16 BST 2024)
# 
#   net: pcs: xpcs: drop interface argument from internal functions
# 
#   Now that we no longer use the "interface" argument when creating the
#   XPCS sub-driver, remove it from xpcs_create() and xpcs_init_iface().
# 
#   Reviewed-by: Vladimir Oltean
#   Signed-off-by: Russell King (Oracle)
#   Link: https://patch.msgid.link/E1svfMG-005ZI9-3k@rmk-PC.armlinux.org.uk
#   Signed-off-by: Jakub Kicinski
# 
#   f72fd50ed5cfdcc017f3ccb534543a122f0ec507
#    drivers/net/pcs/pcs-xpcs.c | 11 +++++------
#    1 file changed, 5 insertions(+), 6 deletions(-)
# 
# Author:    Russell King (Oracle) (Thu 10 Oct 18:10:56 BST 2024)
# Committer: Russell King (Oracle) (Thu 10 Oct 18:14:15 BST 2024)
# 
#   net: pcs: xpcs: move PCS reset to .pcs_pre_config()
# 
#   Move the PCS reset to .pcs_pre_config() rather than at creation time,
#   which means we call the reset function with the interface that we're
#   actually going to be using to talk to the downstream device.
# 
#   Reviewed-by: Vladimir Oltean
#   Tested-by: Vladimir Oltean # sja1105
#   Signed-off-by: Russell King (Oracle)
#   Tested-by: for them?
#   Link: https://patch.msgid.link/E1svfMA-005ZI3-Va@rmk-PC.armlinux.org.uk
#   Signed-off-by: Jakub Kicinski
# 
#   8984bddae8e8e6768b10bcec7268a293f21812ca
#    drivers/net/pcs/pcs-xpcs.c   | 39 +++++++++++++++++++++++++++++----------
#    include/linux/pcs/pcs-xpcs.h |  1 +
#    2 files changed, 30 insertions(+), 10 deletions(-)
# 
# Author:    Jiawen Wu (Thu 10 Oct 18:13:43 BST 2024)
# Committer: Russell King (Oracle) (Thu 10 Oct 18:13:43 BST 2024)
# 
#   net: pcs: xpcs: fix the wrong register that was written back
# 
#   The value is read from the register TXGBE_RX_GEN_CTL3, and it should be
#   written back to TXGBE_RX_GEN_CTL3 when it changes some fields.
# 
#   Cc: stable@vger.kernel.org
#   Fixes: f629acc6f210 ("net: pcs: xpcs: support to switch mode for Wangxun NICs")
#   Signed-off-by: Jiawen Wu
#   Reported-by: Russell King (Oracle)
#   Reviewed-by: Russell King (Oracle)
#   Link: https://patch.msgid.link/20240924022857.865422-1-jiawenwu@trustnetic.com
#   Signed-off-by: Paolo Abeni
# 
#   9a24481f885088efba8d59dfe3e016d6bf9dc18b
#    drivers/net/pcs/pcs-xpcs-wx.c | 2 +-
#    1 file changed, 1 insertion(+), 1 deletion(-)
# 
diff --git a/Documentation/devicetree/bindings/net/marvell,10g.yaml b/Documentation/devicetree/bindings/net/marvell,10g.yaml
new file mode 100644
index 000000000000..9ec85333f4a4
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/marvell,10g.yaml
@@ -0,0 +1,36 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/marvell,10g.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Marvell Alaska X family Ethernet PHYs
+
+maintainers:
+  - Russell King <rmk+kernel@armlinux.org.uk>
+
+allOf:
+  - $ref: ethernet-phy.yaml#
+
+properties:
+  marvell,led-mode:
+    description:
+      An array of one to four 16-bit integers to write to the PHY LED
+      configuration registers.
+    $ref: /schemas/types.yaml#/definitions/uint16-array
+    minItems: 1
+    maxItems: 4
+
+additionalProperties: false
+
+examples:
+  - |
+    mdio {
+        #address-cells = <1>;
+        #size-cells = <0>;
+        ethernet-phy@0 {
+            reg = <0>;
+            compatible = "ethernet-phy-ieee802.3-c45";
+            marvell,led-mode = /bits/ 16 <0x0129 0x095d 0x0855>;
+        };
+    };
diff --git a/arch/arm/boot/dts/nxp/vf/vf610-zii-dev-rev-b.dts b/arch/arm/boot/dts/nxp/vf/vf610-zii-dev-rev-b.dts
index 029f49be40e3..3df582758e45 100644
--- a/arch/arm/boot/dts/nxp/vf/vf610-zii-dev-rev-b.dts
+++ b/arch/arm/boot/dts/nxp/vf/vf610-zii-dev-rev-b.dts
@@ -61,6 +61,12 @@ port@2 {
 						phy-handle = <&switch0phy2>;
 					};
 
+					port@4 {
+						reg = <4>;
+						label = "optical2";
+						sfp = <&opt_p2>;
+					};
+
 					switch0port5: port@5 {
 						reg = <5>;
 						label = "dsa";
@@ -221,34 +227,18 @@ port@1 {
 						phy-mode = "sgmii";
 					};
 
-					port@2 {
-						reg = <2>;
-						label = "lan8";
-						phy-handle = <&switch2phy2>;
-					};
+					/* port 2 is mis-wired */
 
 					port@3 {
 						reg = <3>;
 						label = "optical3";
-
-						fixed-link {
-							speed = <1000>;
-							full-duplex;
-							link-gpios = <&gpio6 2
-							      GPIO_ACTIVE_HIGH>;
-						};
+						sfp = <&opt_p3>;
 					};
 
 					port@4 {
 						reg = <4>;
 						label = "optical4";
-
-						fixed-link {
-							speed = <1000>;
-							full-duplex;
-							link-gpios = <&gpio6 3
-							      GPIO_ACTIVE_HIGH>;
-						};
+						sfp = <&opt_p4>;
 					};
 
 					switch2port9: port@9 {
@@ -269,12 +259,20 @@ mdio {
 					#size-cells = <0>;
 
 					switch2phy0: phy@0 {
+						interrupt-parent = <&gpio0>;
+						interrupts = <22 IRQ_TYPE_LEVEL_LOW>;
 						reg = <0>;
+						pinctrl-names = "default";
+						pinctrl-0 = <&pinctrl_mv88e1545>;
 					};
 					switch2phy1: phy@1 {
+						interrupt-parent = <&gpio0>;
+						interrupts = <22 IRQ_TYPE_LEVEL_LOW>;
 						reg = <1>;
 					};
 					switch2phy2: phy@2 {
+						interrupt-parent = <&gpio0>;
+						interrupts = <22 IRQ_TYPE_LEVEL_LOW>;
 						reg = <2>;
 					};
 				};
@@ -320,6 +318,31 @@ at93c46d@1 {
 			select-gpios = <&gpio4 4 GPIO_ACTIVE_HIGH>;
 		};
 	};
+
+	opt_p2: sff0 {
+		compatible = "sff,sff";
+		i2c-bus = <&opt_p2_i2c>;
+		los-gpios = <&gpio6 1 GPIO_ACTIVE_HIGH>;
+	};
+
+	opt_p1: sff1 {
+		compatible = "sff,sff";
+		i2c-bus = <&opt_p1_i2c>;
+		los-gpios = <&gpio6 0 GPIO_ACTIVE_HIGH>;
+		status = "disabled";
+	};
+
+	opt_p4: sff2 {
+		compatible = "sff,sff";
+		i2c-bus = <&opt_p4_i2c>;
+		los-gpios = <&gpio6 3 GPIO_ACTIVE_HIGH>;
+	};
+
+	opt_p3: sff3 {
+		compatible = "sff,sff";
+		i2c-bus = <&opt_p3_i2c>;
+		los-gpios = <&gpio6 2 GPIO_ACTIVE_HIGH>;
+	};
 };
 
 &i2c0 {
@@ -355,48 +378,28 @@ i2c-mux@70 {
 		reg = <0x70>;
 		reset-gpios = <&gpio3 23 GPIO_ACTIVE_LOW>;
 
-		i2c@0 {
+		opt_p2_i2c: i2c@0 {
 			#address-cells = <1>;
 			#size-cells = <0>;
 			reg = <0>;
-
-			sfp1: eeprom@50 {
-				compatible = "atmel,24c02";
-				reg = <0x50>;
-			};
 		};
 
-		i2c@1 {
+		opt_p1_i2c: i2c@1 {
 			#address-cells = <1>;
 			#size-cells = <0>;
 			reg = <1>;
-
-			sfp2: eeprom@50 {
-				compatible = "atmel,24c02";
-				reg = <0x50>;
-			};
 		};
 
-		i2c@2 {
+		opt_p4_i2c: i2c@2 {
 			#address-cells = <1>;
 			#size-cells = <0>;
 			reg = <2>;
-
-			sfp3: eeprom@50 {
-				compatible = "atmel,24c02";
-				reg = <0x50>;
-			};
 		};
 
-		i2c@3 {
+		opt_p3_i2c: i2c@3 {
 			#address-cells = <1>;
 			#size-cells = <0>;
 			reg = <3>;
-
-			sfp4: eeprom@50 {
-				compatible = "atmel,24c02";
-				reg = <0x50>;
-			};
 		};
 
 		i2c@4 {
@@ -437,6 +440,12 @@ VF610_PAD_PTB3__GPIO_25		0x31c2
 		>;
 	};
 
+	pinctrl_mv88e1545: pinctrl-mv88e1545 {
+		fsl,pins = <
+			VF610_PAD_PTB0__GPIO_22		0x219d
+		>;
+	};
+
 	pinctrl_pca9554_22: pinctrl-pca95540-22 {
 		fsl,pins = <
 			VF610_PAD_PTB28__GPIO_98	0x219d
diff --git a/arch/arm/boot/dts/nxp/vf/vf610-zii-dev-rev-c.dts b/arch/arm/boot/dts/nxp/vf/vf610-zii-dev-rev-c.dts
index 6f9878f124c4..e5510cb6319c 100644
--- a/arch/arm/boot/dts/nxp/vf/vf610-zii-dev-rev-c.dts
+++ b/arch/arm/boot/dts/nxp/vf/vf610-zii-dev-rev-c.dts
@@ -77,6 +77,13 @@ port@4 {
 						phy-handle = <&switch0phy4>;
 					};
 
+					port@9 {
+						reg = <9>;
+						label = "r9";
+						phy-mode = "xaui";
+						phy-handle = <&switch0phy9>;
+					};
+
 					switch0port10: port@10 {
 						reg = <10>;
 						label = "dsa";
@@ -118,6 +125,18 @@ switch0phy4: switch0phy@4 {
 						interrupts = <4 IRQ_TYPE_LEVEL_HIGH>;
 					};
 				};
+				mdio1 {
+					compatible = "marvell,mv88e6xxx-mdio-external";
+					#address-cells = <1>;
+					#size-cells = <0>;
+					reset-gpios = <&gpio6 9 GPIO_ACTIVE_HIGH>;
+					reset-delay-us = <2>;
+
+					switch0phy9: switch0phy@9 {
+						compatible = "ethernet-phy-ieee802.3-c45";
+						reg = <9>;
+					};
+				};
 			};
 		};
 
diff --git a/arch/arm64/boot/dts/marvell/armada-8040-mcbin.dts b/arch/arm64/boot/dts/marvell/armada-8040-mcbin.dts
index 1766cf58101b..68c27f22ff57 100644
--- a/arch/arm64/boot/dts/marvell/armada-8040-mcbin.dts
+++ b/arch/arm64/boot/dts/marvell/armada-8040-mcbin.dts
@@ -21,12 +21,14 @@ phy0: ethernet-phy@0 {
 		compatible = "ethernet-phy-ieee802.3-c45";
 		reg = <0>;
 		sfp = <&sfp_eth0>;
+		marvell,led-mode = /bits/ 16 <0x0129 0x095d 0x0855>;
 	};
 
 	phy8: ethernet-phy@8 {
 		compatible = "ethernet-phy-ieee802.3-c45";
 		reg = <8>;
 		sfp = <&sfp_eth1>;
+		marvell,led-mode = /bits/ 16 <0x0129 0x095d 0x0855>;
 	};
 };
 
diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c
index 0783fc121bbb..be2e5ee6bc5f 100644
--- a/drivers/net/dsa/b53/b53_common.c
+++ b/drivers/net/dsa/b53/b53_common.c
@@ -1426,10 +1426,10 @@ static void b53_phylink_mac_link_down(struct phylink_config *config,
 	struct b53_device *dev = dp->ds->priv;
 	int port = dp->index;
 
-	if (mode == MLO_AN_PHY)
+	if (phylink_mode_phy(mode))
 		return;
 
-	if (mode == MLO_AN_FIXED) {
+	if (phylink_mode_fixed(mode)) {
 		b53_force_link(dev, port, false);
 		return;
 	}
@@ -1452,12 +1452,13 @@ static void b53_phylink_mac_link_up(struct phylink_config *config,
 	struct ethtool_keee *p = &dev->ports[dp->index].eee;
 	int port = dp->index;
 
-	if (mode == MLO_AN_PHY) {
+	if (phylink_mode_phy(mode)) {
 		/* Re-negotiate EEE if it was enabled already */
 		p->eee_enabled = b53_eee_init(ds, port, phydev);
 		return;
 	}
 
+	if (phylink_mode_fixed(mode)) {
 	if (mode == MLO_AN_FIXED) {
 		/* Force flow control on BCM5301x's CPU port */
 		if (is5301x(dev) && dsa_is_cpu_port(ds, port))
diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c
index 0e663ec0c12a..c3ba405b3c1a 100644
--- a/drivers/net/dsa/bcm_sf2.c
+++ b/drivers/net/dsa/bcm_sf2.c
@@ -897,7 +897,7 @@ static void bcm_sf2_sw_mac_link_up(struct phylink_config *config,
 
 	core_writel(priv, reg, offset);
 
-	if (mode == MLO_AN_PHY && phydev) {
+	if (phylink_mode_phy(mode) && phydev) {
 		p = &priv->dev->ports[port].eee;
 		p->eee_enabled = b53_eee_init(dp->ds, port, phydev);
 	}
diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c
index ec18e68bf3a8..75a5ff988f0e 100644
--- a/drivers/net/dsa/mt7530.c
+++ b/drivers/net/dsa/mt7530.c
@@ -2439,7 +2439,8 @@ mt7530_setup(struct dsa_switch *ds)
 		 * on all ports until they're enabled later.
 		 */
 		mt7530_rmw(priv, MT753X_PMCR_P(i), PMCR_LINK_SETTINGS_MASK |
-			   MT7530_FORCE_MODE, MT7530_FORCE_MODE);
+			   MT7530_FORCE_MODE | PMCR_FORCE_EEE1G |
+			   PMCR_FORCE_EEE100, MT7530_FORCE_MODE);
 
 		/* Disable forwarding by default on all ports */
 		mt7530_rmw(priv, MT7530_PCR_P(i), PCR_MATRIX_MASK,
@@ -2929,28 +2930,65 @@ static void mt753x_phylink_mac_link_up(struct phylink_config *config,
 			mcr |= PMCR_FORCE_RX_FC_EN;
 	}
 
-	if (mode == MLO_AN_PHY && phydev && phy_init_eee(phydev, false) >= 0) {
-		switch (speed) {
-		case SPEED_1000:
-		case SPEED_2500:
-			mcr |= PMCR_FORCE_EEE1G;
-			break;
-		case SPEED_100:
-			mcr |= PMCR_FORCE_EEE100;
-			break;
-		}
-	}
-
 	mt7530_set(priv, MT753X_PMCR_P(dp->index), mcr);
 }
 
+static void mt753x_phylink_mac_disable_tx_lpi(struct phylink_config *config)
+{
+	struct dsa_port *dp = dsa_phylink_to_port(config);
+	struct mt7530_priv *priv = dp->ds->priv;
+
+	mt7530_clear(priv, MT753X_PMCR_P(dp->index),
+		     PMCR_FORCE_EEE1G | PMCR_FORCE_EEE100);
+}
+
+static void mt753x_phylink_mac_enable_tx_lpi(struct phylink_config *config,
+					     u32 timer)
+{
+	struct dsa_port *dp = dsa_phylink_to_port(config);
+	struct mt7530_priv *priv = dp->ds->priv;
+	u32 val;
+
+	/* If the timer is zero, then set LPI_MODE_EN, which allows the
+	 * system to enter LPI mode immediately rather than waiting for
+	 * the LPI threshold.
+	 */
+	if (!timer)
+		val = LPI_MODE_EN;
+	else if (FIELD_FIT(LPI_THRESH_MASK, timer))
+		val = FIELD_PREP(LPI_THRESH_MASK, timer);
+	else
+		val = LPI_THRESH_MASK;
+
+	mt7530_rmw(priv, MT753X_PMEEECR_P(port),
+		   LPI_THRESH_MASK | LPI_MODE_EN, val);
+
+	/* FIXME: mt7531 docs say that bits 26 and 25 need to be set to
+	 * enable EEE forcing. The PMCR_FORCE_EEE* bits just determine
+	 * whether we force-enable or force-disable these modes.
+	 */
+	mt7530_set(priv, MT753X_PMCR_P(port),
+		   PMCR_FORCE_EEE1G | PMCR_FORCE_EEE100);
+}
+
 static void mt753x_phylink_get_caps(struct dsa_switch *ds, int port,
 				    struct phylink_config *config)
 {
 	struct mt7530_priv *priv = ds->priv;
+	u32 eeecr;
 
 	config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE;
 
+	config->lpi_capabilities = MAC_100FD | MAC_1000FD  MAC_2500FD;
+	config->lpi_timer_limit_us = FIELD_GET(LPI_THRESH_MASK,
+					       LPI_THRESH_MASK);
+
+	eeecr = mt7530_read(priv, MT753X_PMEEECR_P(port));
+	/* tx_lpi_timer should be in microseconds. The time units for
+	 * LPI threshold are unspecified.
+	 */
+	config->eee.tx_lpi_timer = FIELD_GET(LPI_THRESH_MASK, eeecr);
+
 	priv->info->mac_port_get_caps(ds, port, config);
 }
 
@@ -3057,36 +3095,6 @@ mt753x_setup(struct dsa_switch *ds)
 	return ret;
 }
 
-static int mt753x_get_mac_eee(struct dsa_switch *ds, int port,
-			      struct ethtool_keee *e)
-{
-	struct mt7530_priv *priv = ds->priv;
-	u32 eeecr = mt7530_read(priv, MT753X_PMEEECR_P(port));
-
-	e->tx_lpi_enabled = !(eeecr & LPI_MODE_EN);
-	e->tx_lpi_timer = LPI_THRESH_GET(eeecr);
-
-	return 0;
-}
-
-static int mt753x_set_mac_eee(struct dsa_switch *ds, int port,
-			      struct ethtool_keee *e)
-{
-	struct mt7530_priv *priv = ds->priv;
-	u32 set, mask = LPI_THRESH_MASK | LPI_MODE_EN;
-
-	if (e->tx_lpi_timer > 0xFFF)
-		return -EINVAL;
-
-	set = LPI_THRESH_SET(e->tx_lpi_timer);
-	if (!e->tx_lpi_enabled)
-		/* Force LPI Mode without a delay */
-		set |= LPI_MODE_EN;
-	mt7530_rmw(priv, MT753X_PMEEECR_P(port), mask, set);
-
-	return 0;
-}
-
 static void
 mt753x_conduit_state_change(struct dsa_switch *ds,
 			    const struct net_device *conduit,
@@ -3163,8 +3171,6 @@ const struct dsa_switch_ops mt7530_switch_ops = {
 	.port_mirror_add	= mt753x_port_mirror_add,
 	.port_mirror_del	= mt753x_port_mirror_del,
 	.phylink_get_caps	= mt753x_phylink_get_caps,
-	.get_mac_eee		= mt753x_get_mac_eee,
-	.set_mac_eee		= mt753x_set_mac_eee,
 	.conduit_state_change	= mt753x_conduit_state_change,
 };
 EXPORT_SYMBOL_GPL(mt7530_switch_ops);
@@ -3174,6 +3180,8 @@ static const struct phylink_mac_ops mt753x_phylink_mac_ops = {
 	.mac_config	= mt753x_phylink_mac_config,
 	.mac_link_down	= mt753x_phylink_mac_link_down,
 	.mac_link_up	= mt753x_phylink_mac_link_up,
+	.mac_disable_tx_lpi = mt753x_phylink_mac_disable_tx_lpi,
+	.mac_enable_tx_lpi = mt753x_phylink_mac_enable_tx_lpi,
 };
 
 const struct mt753x_info mt753x_table[] = {
diff --git a/drivers/net/dsa/mt7530.h b/drivers/net/dsa/mt7530.h
index 28592123070b..9adb1a7a1fa0 100644
--- a/drivers/net/dsa/mt7530.h
+++ b/drivers/net/dsa/mt7530.h
@@ -352,12 +352,8 @@ enum mt7530_vlan_port_acc_frm {
 					 MT7531_FORCE_MODE_SPD | \
 					 MT7531_FORCE_MODE_DPX | \
 					 MT7531_FORCE_MODE_RX_FC | \
-					 MT7531_FORCE_MODE_TX_FC | \
-					 MT7531_FORCE_MODE_EEE100 | \
-					 MT7531_FORCE_MODE_EEE1G)
+					 MT7531_FORCE_MODE_TX_FC)
 #define  PMCR_LINK_SETTINGS_MASK	(PMCR_MAC_TX_EN | PMCR_MAC_RX_EN | \
-					 PMCR_FORCE_EEE1G | \
-					 PMCR_FORCE_EEE100 | \
 					 PMCR_FORCE_RX_FC_EN | \
 					 PMCR_FORCE_TX_FC_EN | \
 					 PMCR_FORCE_SPEED_1000 | \
diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
index 5b4e2ce5470d..8b1f4057fbe9 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.c
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
@@ -678,6 +678,9 @@ static void mv88e6352_phylink_get_caps(struct mv88e6xxx_chip *chip, int port,
 
 	config->mac_capabilities = MAC_SYM_PAUSE | MAC_10 | MAC_100 |
 				   MAC_1000FD;
+	config->lpi_capabilities = MAC_1000FD | MAC_100FD;
+	config->eee.eee_enabled = true;
+	config->eee.tx_lpi_enabled = true;
 
 	/* Port 4 supports automedia if the serdes is associated with it. */
 	if (port == 4) {
@@ -867,7 +870,7 @@ mv88e6xxx_mac_select_pcs(struct phylink_config *config,
 {
 	struct dsa_port *dp = dsa_phylink_to_port(config);
 	struct mv88e6xxx_chip *chip = dp->ds->priv;
-	struct phylink_pcs *pcs = ERR_PTR(-EOPNOTSUPP);
+	struct phylink_pcs *pcs = NULL;
 
 	if (chip->info->ops->pcs_ops)
 		pcs = chip->info->ops->pcs_ops->pcs_select(chip, dp->index,
@@ -888,7 +891,7 @@ static int mv88e6xxx_mac_prepare(struct phylink_config *config,
 	 * is not forced down. Force the link down while we reconfigure the
 	 * interface mode.
 	 */
-	if (mode == MLO_AN_INBAND &&
+	if (phylink_mode_inband(mode) &&
 	    chip->ports[port].interface != interface &&
 	    chip->info->ops->port_set_link) {
 		mv88e6xxx_reg_lock(chip);
@@ -911,7 +914,7 @@ static void mv88e6xxx_mac_config(struct phylink_config *config,
 
 	mv88e6xxx_reg_lock(chip);
 
-	if (mode != MLO_AN_PHY || !mv88e6xxx_phy_is_internal(chip, port)) {
+	if (!phylink_mode_phy(mode) || !mv88e6xxx_phy_is_internal(chip, port)) {
 		err = mv88e6xxx_port_config_interface(chip, port,
 						      state->interface);
 		if (err && err != -EOPNOTSUPP)
@@ -942,9 +945,10 @@ static int mv88e6xxx_mac_finish(struct phylink_config *config,
 	mv88e6xxx_reg_lock(chip);
 
 	if (chip->info->ops->port_set_link &&
-	    ((mode == MLO_AN_INBAND &&
+	    ((phylink_mode_inband(mode) &&
 	      chip->ports[port].interface != interface) ||
-	     (mode == MLO_AN_PHY && mv88e6xxx_port_ppu_updates(chip, port))))
+	     (phylink_mode_phy(mode) &&
+	      mv88e6xxx_port_ppu_updates(chip, port))))
 		err = chip->info->ops->port_set_link(chip, port, LINK_UNFORCED);
 
 	mv88e6xxx_reg_unlock(chip);
@@ -971,7 +975,7 @@ static void mv88e6xxx_mac_link_down(struct phylink_config *config,
 	 * updated by the switch or if we are using fixed-link mode.
 	 */
 	if ((!mv88e6xxx_port_ppu_updates(chip, port) ||
-	     mode == MLO_AN_FIXED) && ops->port_sync_link)
+	     phylink_mode_fixed(mode)) && ops->port_sync_link)
 		err = ops->port_sync_link(chip, port, mode, false);
 
 	if (!err && ops->port_set_speed_duplex)
@@ -1004,7 +1008,7 @@ static void mv88e6xxx_mac_link_up(struct phylink_config *config,
 	 * mode.
 	 */
 	if (!mv88e6xxx_port_ppu_updates(chip, port) ||
-	    mode == MLO_AN_FIXED) {
+	    phylink_mode_fixed(mode)) {
 		if (ops->port_set_speed_duplex) {
 			err = ops->port_set_speed_duplex(chip, port,
 							 speed, duplex);
@@ -1023,6 +1027,49 @@ static void mv88e6xxx_mac_link_up(struct phylink_config *config,
 			"p%d: failed to configure MAC link up\n", port);
 }
 
+static void mv88e6xxx_mac_disable_tx_lpi(struct phylink_config *config)
+{
+	struct dsa_port *dp = dsa_phylink_to_port(config);
+	struct mv88e6xxx_chip *chip = dp->ds->priv;
+	int port = dp->index;
+	int err;
+
+	if (!chip->info->ops->port_set_eee)
+		return;
+
+	mv88e6xxx_reg_lock(chip);
+	err = chip->info->ops->port_set_eee(chip, port, EEE_FORCE_DISABLE);
+	mv88e6xxx_reg_unlock(chip);
+
+	if (err)
+		dev_err(chip->dev, "p%d: failed to set EEE mode: %pe\n", port,
+			ERR_PTR(err));
+}
+
+static void mv88e6xxx_mac_enable_tx_lpi(struct phylink_config *config,
+					u32 timer)
+{
+	struct dsa_port *dp = dsa_phylink_to_port(config);
+	struct mv88e6xxx_chip *chip = dp->ds->priv;
+	int port = dp->index;
+	int eee, err;
+
+	if (!chip->info->ops->port_set_eee)
+		return;
+
+	mv88e6xxx_reg_lock(chip);
+	if (mv88e6xxx_port_ppu_updates(chip, port))
+		eee = EEE_UNFORCED;
+	else
+		eee = EEE_FORCE_DISABLE;
+	err = chip->info->ops->port_set_eee(chip, port, eee);
+	mv88e6xxx_reg_unlock(chip);
+
+	if (err)
+		dev_err(chip->dev, "p%d: failed to set EEE mode: %pe\n", port,
+			ERR_PTR(err));
+}
+
 static int mv88e6xxx_stats_snapshot(struct mv88e6xxx_chip *chip, int port)
 {
 	int err;
@@ -4582,6 +4629,7 @@ static const struct mv88e6xxx_ops mv88e6172_ops = {
 	.phy_read_c45 = mv88e6xxx_g2_smi_phy_read_c45,
 	.phy_write_c45 = mv88e6xxx_g2_smi_phy_write_c45,
 	.port_set_link = mv88e6xxx_port_set_link,
+	.port_set_eee = mv88e6xxx_port_set_eee,
 	.port_sync_link = mv88e6xxx_port_sync_link,
 	.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay,
 	.port_set_speed_duplex = mv88e6352_port_set_speed_duplex,
@@ -4684,6 +4732,7 @@ static const struct mv88e6xxx_ops mv88e6176_ops = {
 	.phy_read_c45 = mv88e6xxx_g2_smi_phy_read_c45,
 	.phy_write_c45 = mv88e6xxx_g2_smi_phy_write_c45,
 	.port_set_link = mv88e6xxx_port_set_link,
+	.port_set_eee = mv88e6xxx_port_set_eee,
 	.port_sync_link = mv88e6xxx_port_sync_link,
 	.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay,
 	.port_set_speed_duplex = mv88e6352_port_set_speed_duplex,
@@ -4959,6 +5008,7 @@ static const struct mv88e6xxx_ops mv88e6240_ops = {
 	.phy_read_c45 = mv88e6xxx_g2_smi_phy_read_c45,
 	.phy_write_c45 = mv88e6xxx_g2_smi_phy_write_c45,
 	.port_set_link = mv88e6xxx_port_set_link,
+	.port_set_eee = mv88e6xxx_port_set_eee,
 	.port_sync_link = mv88e6xxx_port_sync_link,
 	.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay,
 	.port_set_speed_duplex = mv88e6352_port_set_speed_duplex,
@@ -5381,6 +5431,7 @@ static const struct mv88e6xxx_ops mv88e6352_ops = {
 	.phy_read_c45 = mv88e6xxx_g2_smi_phy_read_c45,
 	.phy_write_c45 = mv88e6xxx_g2_smi_phy_write_c45,
 	.port_set_link = mv88e6xxx_port_set_link,
+	.port_set_eee = mv88e6xxx_port_set_eee,
 	.port_sync_link = mv88e6xxx_port_sync_link,
 	.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay,
 	.port_set_speed_duplex = mv88e6352_port_set_speed_duplex,
@@ -5570,6 +5621,7 @@ static const struct mv88e6xxx_ops mv88e6393x_ops = {
 	.phy_read_c45 = mv88e6xxx_g2_smi_phy_read_c45,
 	.phy_write_c45 = mv88e6xxx_g2_smi_phy_write_c45,
 	.port_set_link = mv88e6xxx_port_set_link,
+	/* no port_set_eee due to mv88e6393x errata 4.5 */
 	.port_sync_link = mv88e6xxx_port_sync_link,
 	.port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay,
 	.port_set_speed_duplex = mv88e6393x_port_set_speed_duplex,
@@ -7054,6 +7106,8 @@ static const struct phylink_mac_ops mv88e6xxx_phylink_mac_ops = {
 	.mac_finish		= mv88e6xxx_mac_finish,
 	.mac_link_down		= mv88e6xxx_mac_link_down,
 	.mac_link_up		= mv88e6xxx_mac_link_up,
+	.mac_disable_tx_lpi	= mv88e6xxx_mac_disable_tx_lpi,
+	.mac_enable_tx_lpi	= mv88e6xxx_mac_enable_tx_lpi,
 };
 
 static const struct dsa_switch_ops mv88e6xxx_switch_ops = {
diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h
index c34caf9815c5..c737bbc7d304 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.h
+++ b/drivers/net/dsa/mv88e6xxx/chip.h
@@ -512,6 +512,16 @@ struct mv88e6xxx_ops {
 	 */
 	int (*port_set_link)(struct mv88e6xxx_chip *chip, int port, int link);
 
+#define EEE_FORCE_DISABLE	0
+#define EEE_FORCE_ENABLE	1
+#define EEE_UNFORCED		-1
+
+	/* Port's EEE state
+	 * Use EEE_FORCE_ENABLE or EEE_FORCE_DISABLE to force EEE to be enabled
+	 * or disabled, or EEE_UNFORCED for normal EEE.
+	 */
+	int (*port_set_eee)(struct mv88e6xxx_chip *chip, int port, int eee);
+
 	/* Synchronise the port link state with that of the SERDES
 	 */
 	int (*port_sync_link)(struct mv88e6xxx_chip *chip, int port, unsigned int mode, bool isup);
diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c
index 5394a8cf7bf1..e56d24d5cdde 100644
--- a/drivers/net/dsa/mv88e6xxx/port.c
+++ b/drivers/net/dsa/mv88e6xxx/port.c
@@ -62,6 +62,10 @@ int mv88e6185_port_set_pause(struct mv88e6xxx_chip *chip, int port,
 	else
 		reg &= ~MV88E6XXX_PORT_STS_MY_PAUSE;
 
+	if (reg & MV88E6XXX_PORT_MAC_CTL_FORCE_LINK &&
+	    reg & MV88E6XXX_PORT_MAC_CTL_LINK_UP)
+		dev_err(chip->dev, "p%d: %s with port up\n", port, __func__);
+
 	return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_STS, reg);
 }
 
@@ -104,6 +108,10 @@ static int mv88e6xxx_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port,
 		return 0;
 	}
 
+	if (reg & MV88E6XXX_PORT_MAC_CTL_FORCE_LINK &&
+	    reg & MV88E6XXX_PORT_MAC_CTL_LINK_UP)
+		dev_err(chip->dev, "p%d: %s with port up\n", port, __func__);
+
 	err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_MAC_CTL, reg);
 	if (err)
 		return err;
@@ -203,7 +211,7 @@ int mv88e6185_port_sync_link(struct mv88e6xxx_chip *chip, int port, unsigned int
 	int err = 0;
 	int link;
 
-	if (mode == MLO_AN_INBAND)
+	if (phylink_mode_inband(mode))
 		link = LINK_UNFORCED;
 	else if (isup)
 		link = LINK_FORCED_UP;
@@ -288,6 +296,10 @@ static int mv88e6xxx_port_set_speed_duplex(struct mv88e6xxx_chip *chip,
 	}
 	reg |= ctrl;
 
+	if (reg & MV88E6XXX_PORT_MAC_CTL_FORCE_LINK &&
+	    reg & MV88E6XXX_PORT_MAC_CTL_LINK_UP)
+		dev_err(chip->dev, "p%d: %s with port up\n", port, __func__);
+
 	err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_MAC_CTL, reg);
 	if (err)
 		return err;
@@ -520,6 +532,36 @@ phy_interface_t mv88e6393x_port_max_speed_mode(struct mv88e6xxx_chip *chip,
 	return PHY_INTERFACE_MODE_10GBASER;
 }
 
+int mv88e6xxx_port_set_eee(struct mv88e6xxx_chip *chip, int port, int eee)
+{
+	u16 reg, val = 0;
+	int err;
+
+	switch (eee) {
+	case EEE_FORCE_ENABLE:
+		val = MV88E6XXX_PORT_MAC_CTL_EEE;
+		fallthrough;
+	case EEE_FORCE_DISABLE:
+		val |= MV88E6XXX_PORT_MAC_CTL_FORCE_EEE;
+		break;
+	default:
+		break;
+	}
+
+	dev_dbg(chip->dev, "p%d: %s eee %sable\n", port,
+		val & MV88E6XXX_PORT_MAC_CTL_FORCE_EEE ? "Force" : "Unforce",
+		val & MV88E6XXX_PORT_MAC_CTL_EEE ? "en" : "dis");
+
+	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_MAC_CTL, &reg);
+	if (err < 0)
+		return err;
+
+	reg &= ~(MV88E6XXX_PORT_MAC_CTL_EEE | MV88E6XXX_PORT_MAC_CTL_FORCE_EEE);
+	reg |= val;
+
+	return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_MAC_CTL, reg);
+}
+
 static int mv88e6xxx_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
 				    phy_interface_t mode, bool force)
 {
@@ -629,7 +671,6 @@ int mv88e6393x_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
 			      phy_interface_t mode)
 {
 	int err;
-	u16 reg;
 
 	if (port != 0 && port != 9 && port != 10)
 		return -EOPNOTSUPP;
@@ -648,13 +689,7 @@ int mv88e6393x_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
 	}
 
 	/* mv88e6393x errata 4.5: EEE should be disabled on SERDES ports */
-	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_MAC_CTL, &reg);
-	if (err)
-		return err;
-
-	reg &= ~MV88E6XXX_PORT_MAC_CTL_EEE;
-	reg |= MV88E6XXX_PORT_MAC_CTL_FORCE_EEE;
-	err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_MAC_CTL, reg);
+	err = mv88e6xxx_port_set_eee(chip, port, EEE_FORCE_DISABLE);
 	if (err)
 		return err;
 
diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h
index ddadeb9bfdae..78ef7e2744a1 100644
--- a/drivers/net/dsa/mv88e6xxx/port.h
+++ b/drivers/net/dsa/mv88e6xxx/port.h
@@ -447,6 +447,7 @@ int mv88e6097_port_pause_limit(struct mv88e6xxx_chip *chip, int port, u8 in,
 			       u8 out);
 int mv88e6390_port_pause_limit(struct mv88e6xxx_chip *chip, int port, u8 in,
 			       u8 out);
+int mv88e6xxx_port_set_eee(struct mv88e6xxx_chip *chip, int port, int eee);
 int mv88e6341_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
 			     phy_interface_t mode);
 int mv88e6390_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
diff --git a/drivers/net/dsa/sja1105/sja1105.h b/drivers/net/dsa/sja1105/sja1105.h
index 8c66d3bf61f0..dceb96ae9c83 100644
--- a/drivers/net/dsa/sja1105/sja1105.h
+++ b/drivers/net/dsa/sja1105/sja1105.h
@@ -278,7 +278,7 @@ struct sja1105_private {
 	struct mii_bus *mdio_base_t1;
 	struct mii_bus *mdio_base_tx;
 	struct mii_bus *mdio_pcs;
-	struct dw_xpcs *xpcs[SJA1105_MAX_NUM_PORTS];
+	struct phylink_pcs *pcs[SJA1105_MAX_NUM_PORTS];
 	struct sja1105_ptp_data ptp_data;
 	struct sja1105_tas_data tas_data;
 };
diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c
index c7282ce3d11c..4d7284b38c52 100644
--- a/drivers/net/dsa/sja1105/sja1105_main.c
+++ b/drivers/net/dsa/sja1105/sja1105_main.c
@@ -15,7 +15,6 @@
 #include <linux/of.h>
 #include <linux/of_net.h>
 #include <linux/of_mdio.h>
-#include <linux/pcs/pcs-xpcs.h>
 #include <linux/netdev_features.h>
 #include <linux/netdevice.h>
 #include <linux/if_bridge.h>
@@ -1263,29 +1262,11 @@ static int sja1105_parse_dt(struct sja1105_private *priv)
 	return rc;
 }
 
-/* Convert link speed from SJA1105 to ethtool encoding */
-static int sja1105_port_speed_to_ethtool(struct sja1105_private *priv,
-					 u64 speed)
-{
-	if (speed == priv->info->port_speed[SJA1105_SPEED_10MBPS])
-		return SPEED_10;
-	if (speed == priv->info->port_speed[SJA1105_SPEED_100MBPS])
-		return SPEED_100;
-	if (speed == priv->info->port_speed[SJA1105_SPEED_1000MBPS])
-		return SPEED_1000;
-	if (speed == priv->info->port_speed[SJA1105_SPEED_2500MBPS])
-		return SPEED_2500;
-	return SPEED_UNKNOWN;
-}
-
-/* Set link speed in the MAC configuration for a specific port. */
-static int sja1105_adjust_port_config(struct sja1105_private *priv, int port,
-				      int speed_mbps)
+static int sja1105_set_port_speed(struct sja1105_private *priv, int port,
+				  int speed_mbps)
 {
 	struct sja1105_mac_config_entry *mac;
-	struct device *dev = priv->ds->dev;
 	u64 speed;
-	int rc;
 
 	/* On P/Q/R/S, one can read from the device via the MAC reconfiguration
 	 * tables. On E/T, MAC reconfig tables are not readable, only writable.
@@ -1319,7 +1300,7 @@ static int sja1105_adjust_port_config(struct sja1105_private *priv, int port,
 		speed = priv->info->port_speed[SJA1105_SPEED_2500MBPS];
 		break;
 	default:
-		dev_err(dev, "Invalid speed %iMbps\n", speed_mbps);
+		dev_err(priv->ds->dev, "Invalid speed %iMbps\n", speed_mbps);
 		return -EINVAL;
 	}
 
@@ -1331,11 +1312,31 @@ static int sja1105_adjust_port_config(struct sja1105_private *priv, int port,
 	 * we need to configure the PCS only (if even that).
 	 */
 	if (priv->phy_mode[port] == PHY_INTERFACE_MODE_SGMII)
-		mac[port].speed = priv->info->port_speed[SJA1105_SPEED_1000MBPS];
+		speed = priv->info->port_speed[SJA1105_SPEED_1000MBPS];
 	else if (priv->phy_mode[port] == PHY_INTERFACE_MODE_2500BASEX)
-		mac[port].speed = priv->info->port_speed[SJA1105_SPEED_2500MBPS];
-	else
-		mac[port].speed = speed;
+		speed = priv->info->port_speed[SJA1105_SPEED_2500MBPS];
+
+	mac[port].speed = speed;
+
+	return 0;
+}
+
+/* Write the MAC Configuration Table entry and, if necessary, the CGU settings,
+ * after a link speedchange for this port.
+ */
+static int sja1105_set_port_config(struct sja1105_private *priv, int port)
+{
+	struct sja1105_mac_config_entry *mac;
+	struct device *dev = priv->ds->dev;
+	int rc;
+
+	/* On P/Q/R/S, one can read from the device via the MAC reconfiguration
+	 * tables. On E/T, MAC reconfig tables are not readable, only writable.
+	 * We have to *know* what the MAC looks like.  For the sake of keeping
+	 * the code common, we'll use the static configuration tables as a
+	 * reasonable approximation for both E/T and P/Q/R/S.
+	 */
+	mac = priv->static_config.tables[BLK_IDX_MAC_CONFIG].entries;
 
 	/* Write to the dynamic reconfiguration tables */
 	rc = sja1105_dynamic_config_write(priv, BLK_IDX_MAC_CONFIG, port,
@@ -1362,12 +1363,8 @@ sja1105_mac_select_pcs(struct phylink_config *config, phy_interface_t iface)
 {
 	struct dsa_port *dp = dsa_phylink_to_port(config);
 	struct sja1105_private *priv = dp->ds->priv;
-	struct dw_xpcs *xpcs = priv->xpcs[dp->index];
-
-	if (xpcs)
-		return &xpcs->pcs;
 
-	return NULL;
+	return priv->pcs[dp->index];
 }
 
 static void sja1105_mac_config(struct phylink_config *config,
@@ -1396,7 +1393,8 @@ static void sja1105_mac_link_up(struct phylink_config *config,
 	struct sja1105_private *priv = dp->ds->priv;
 	int port = dp->index;
 
-	sja1105_adjust_port_config(priv, port, speed);
+	if (!sja1105_set_port_speed(priv, port, speed))
+		sja1105_set_port_config(priv, port);
 
 	sja1105_inhibit_tx(priv, BIT(port), false);
 }
@@ -2299,8 +2297,8 @@ int sja1105_static_config_reload(struct sja1105_private *priv,
 {
 	struct ptp_system_timestamp ptp_sts_before;
 	struct ptp_system_timestamp ptp_sts_after;
-	int speed_mbps[SJA1105_MAX_NUM_PORTS];
 	u16 bmcr[SJA1105_MAX_NUM_PORTS] = {0};
+	u64 mac_speed[SJA1105_MAX_NUM_PORTS];
 	struct sja1105_mac_config_entry *mac;
 	struct dsa_switch *ds = priv->ds;
 	s64 t1, t2, t3, t4;
@@ -2313,17 +2311,16 @@ int sja1105_static_config_reload(struct sja1105_private *priv,
 
 	mac = priv->static_config.tables[BLK_IDX_MAC_CONFIG].entries;
 
-	/* Back up the dynamic link speed changed by sja1105_adjust_port_config
+	/* Back up the dynamic link speed changed by sja1105_set_port_speed()
 	 * in order to temporarily restore it to SJA1105_SPEED_AUTO - which the
 	 * switch wants to see in the static config in order to allow us to
 	 * change it through the dynamic interface later.
 	 */
 	for (i = 0; i < ds->num_ports; i++) {
-		speed_mbps[i] = sja1105_port_speed_to_ethtool(priv,
-							      mac[i].speed);
+		mac_speed[i] = mac[i].speed;
 		mac[i].speed = priv->info->port_speed[SJA1105_SPEED_AUTO];
 
-		if (priv->xpcs[i])
+		if (priv->pcs[i])
 			bmcr[i] = mdiobus_c45_read(priv->mdio_pcs, i,
 						   MDIO_MMD_VEND2, MDIO_CTRL1);
 	}
@@ -2380,14 +2377,15 @@ int sja1105_static_config_reload(struct sja1105_private *priv,
 	}
 
 	for (i = 0; i < ds->num_ports; i++) {
-		struct dw_xpcs *xpcs = priv->xpcs[i];
+		struct phylink_pcs *pcs = priv->pcs[i];
 		unsigned int neg_mode;
 
-		rc = sja1105_adjust_port_config(priv, i, speed_mbps[i]);
+		mac[i].speed = mac_speed[i];
+		rc = sja1105_set_port_config(priv, i);
 		if (rc < 0)
 			goto out;
 
-		if (!xpcs)
+		if (!pcs)
 			continue;
 
 		if (bmcr[i] & BMCR_ANENABLE)
@@ -2395,7 +2393,8 @@ int sja1105_static_config_reload(struct sja1105_private *priv,
 		else
 			neg_mode = PHYLINK_PCS_NEG_OUTBAND;
 
-		rc = xpcs_do_config(xpcs, priv->phy_mode[i], NULL, neg_mode);
+		rc = pcs->ops->pcs_config(pcs, neg_mode, priv->phy_mode[i],
+					  NULL, true);
 		if (rc < 0)
 			goto out;
 
@@ -2411,8 +2410,8 @@ int sja1105_static_config_reload(struct sja1105_private *priv,
 			else
 				speed = SPEED_10;
 
-			xpcs_link_up(&xpcs->pcs, neg_mode, priv->phy_mode[i],
-				     speed, DUPLEX_FULL);
+			pcs->ops->pcs_link_up(pcs, neg_mode, priv->phy_mode[i],
+					      speed, DUPLEX_FULL);
 		}
 	}
 
diff --git a/drivers/net/dsa/sja1105/sja1105_mdio.c b/drivers/net/dsa/sja1105/sja1105_mdio.c
index 52ddb4ef259e..84b7169f2974 100644
--- a/drivers/net/dsa/sja1105/sja1105_mdio.c
+++ b/drivers/net/dsa/sja1105/sja1105_mdio.c
@@ -400,7 +400,7 @@ static int sja1105_mdiobus_pcs_register(struct sja1105_private *priv)
 	}
 
 	for (port = 0; port < ds->num_ports; port++) {
-		struct dw_xpcs *xpcs;
+		struct phylink_pcs *pcs;
 
 		if (dsa_is_unused_port(ds, port))
 			continue;
@@ -409,13 +409,13 @@ static int sja1105_mdiobus_pcs_register(struct sja1105_private *priv)
 		    priv->phy_mode[port] != PHY_INTERFACE_MODE_2500BASEX)
 			continue;
 
-		xpcs = xpcs_create_mdiodev(bus, port, priv->phy_mode[port]);
-		if (IS_ERR(xpcs)) {
-			rc = PTR_ERR(xpcs);
+		pcs = xpcs_create_pcs_mdiodev(bus, port);
+		if (IS_ERR(pcs)) {
+			rc = PTR_ERR(pcs);
 			goto out_pcs_free;
 		}
 
-		priv->xpcs[port] = xpcs;
+		priv->pcs[port] = pcs;
 	}
 
 	priv->mdio_pcs = bus;
@@ -424,11 +424,10 @@ static int sja1105_mdiobus_pcs_register(struct sja1105_private *priv)
 
 out_pcs_free:
 	for (port = 0; port < ds->num_ports; port++) {
-		if (!priv->xpcs[port])
-			continue;
-
-		xpcs_destroy(priv->xpcs[port]);
-		priv->xpcs[port] = NULL;
+		if (priv->pcs[port]) {
+			xpcs_destroy_pcs(priv->pcs[port]);
+			priv->pcs[port] = NULL;
+		}
 	}
 
 	mdiobus_unregister(bus);
@@ -446,11 +445,10 @@ static void sja1105_mdiobus_pcs_unregister(struct sja1105_private *priv)
 		return;
 
 	for (port = 0; port < ds->num_ports; port++) {
-		if (!priv->xpcs[port])
-			continue;
-
-		xpcs_destroy(priv->xpcs[port]);
-		priv->xpcs[port] = NULL;
+		if (priv->pcs[port]) {
+			xpcs_destroy_pcs(priv->pcs[port]);
+			priv->pcs[port] = NULL;
+		}
 	}
 
 	mdiobus_unregister(priv->mdio_pcs);
diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c
index dcd3f54ed0cf..05ec8da4a69d 100644
--- a/drivers/net/ethernet/cadence/macb_main.c
+++ b/drivers/net/ethernet/cadence/macb_main.c
@@ -687,7 +687,7 @@ static void macb_mac_config(struct phylink_config *config, unsigned int mode,
 		u32 pcsctrl, old_pcsctrl;
 
 		old_pcsctrl = gem_readl(bp, PCSCNTRL);
-		if (mode == MLO_AN_FIXED)
+		if (phylink_mode_fixed(mode))
 			pcsctrl = old_pcsctrl & ~GEM_BIT(PCSAUTONEG);
 		else
 			pcsctrl = old_pcsctrl | GEM_BIT(PCSAUTONEG);
diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c
index 41894834fb53..d421257c8e6e 100644
--- a/drivers/net/ethernet/marvell/mvneta.c
+++ b/drivers/net/ethernet/marvell/mvneta.c
@@ -284,8 +284,10 @@
 					  MVNETA_TXQ_BUCKET_REFILL_PERIOD))
 
 #define MVNETA_LPI_CTRL_0                        0x2cc0
+#define      MVNETA_LPI_CTRL_0_TS                (0xff << 8)
 #define MVNETA_LPI_CTRL_1                        0x2cc4
 #define      MVNETA_LPI_REQUEST_ENABLE           BIT(0)
+#define      MVNETA_LPI_CTRL_1_TW                (0xfff << 4)
 #define MVNETA_LPI_CTRL_2                        0x2cc8
 #define MVNETA_LPI_STATUS                        0x2ccc
 
@@ -541,10 +543,6 @@ struct mvneta_port {
 	struct mvneta_bm_pool *pool_short;
 	int bm_win_id;
 
-	bool eee_enabled;
-	bool eee_active;
-	bool tx_lpi_enabled;
-
 	u64 ethtool_stats[ARRAY_SIZE(mvneta_statistics)];
 
 	u32 indir[MVNETA_RSS_LU_TABLE_SIZE];
@@ -3960,20 +3958,27 @@ static struct mvneta_port *mvneta_pcs_to_port(struct phylink_pcs *pcs)
 	return container_of(pcs, struct mvneta_port, phylink_pcs);
 }
 
-static int mvneta_pcs_validate(struct phylink_pcs *pcs,
-			       unsigned long *supported,
-			       const struct phylink_link_state *state)
+static unsigned int mvneta_pcs_query_inband(struct phylink_pcs *pcs,
+					    phy_interface_t interface)
 {
-	/* We only support QSGMII, SGMII, 802.3z and RGMII modes.
-	 * When in 802.3z mode, we must have AN enabled:
+	/* When operating in an 802.3z mode, we must have AN enabled:
 	 * "Bit 2 Field InBandAnEn In-band Auto-Negotiation enable. ...
 	 * When <PortType> = 1 (1000BASE-X) this field must be set to 1."
+	 * Therefore, inband is "required".
 	 */
-	if (phy_interface_mode_is_8023z(state->interface) &&
-	    !phylink_test(state->advertising, Autoneg))
-		return -EINVAL;
+	if (phy_interface_mode_is_8023z(interface))
+		return LINK_INBAND_ENABLE;
 
-	return 0;
+	/* QSGMII, SGMII and RGMII can be configured to use inband
+	 * signalling of the AN result. Indicate these as "possible".
+	 */
+	if (interface == PHY_INTERFACE_MODE_SGMII ||
+	    interface == PHY_INTERFACE_MODE_QSGMII ||
+	    phy_interface_mode_is_rgmii(interface))
+		return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE;
+
+	/* For any other modes, indicate that inband is not supported. */
+	return LINK_INBAND_DISABLE;
 }
 
 static void mvneta_pcs_get_state(struct phylink_pcs *pcs,
@@ -4071,7 +4076,7 @@ static void mvneta_pcs_an_restart(struct phylink_pcs *pcs)
 }
 
 static const struct phylink_pcs_ops mvneta_phylink_pcs_ops = {
-	.pcs_validate = mvneta_pcs_validate,
+	.pcs_query_inband = mvneta_pcs_query_inband,
 	.pcs_get_state = mvneta_pcs_get_state,
 	.pcs_config = mvneta_pcs_config,
 	.pcs_an_restart = mvneta_pcs_an_restart,
@@ -4206,18 +4211,6 @@ static int mvneta_mac_finish(struct phylink_config *config, unsigned int mode,
 	return 0;
 }
 
-static void mvneta_set_eee(struct mvneta_port *pp, bool enable)
-{
-	u32 lpi_ctl1;
-
-	lpi_ctl1 = mvreg_read(pp, MVNETA_LPI_CTRL_1);
-	if (enable)
-		lpi_ctl1 |= MVNETA_LPI_REQUEST_ENABLE;
-	else
-		lpi_ctl1 &= ~MVNETA_LPI_REQUEST_ENABLE;
-	mvreg_write(pp, MVNETA_LPI_CTRL_1, lpi_ctl1);
-}
-
 static void mvneta_mac_link_down(struct phylink_config *config,
 				 unsigned int mode, phy_interface_t interface)
 {
@@ -4233,9 +4226,6 @@ static void mvneta_mac_link_down(struct phylink_config *config,
 		val |= MVNETA_GMAC_FORCE_LINK_DOWN;
 		mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
 	}
-
-	pp->eee_active = false;
-	mvneta_set_eee(pp, false);
 }
 
 static void mvneta_mac_link_up(struct phylink_config *config,
@@ -4284,11 +4274,57 @@ static void mvneta_mac_link_up(struct phylink_config *config,
 	}
 
 	mvneta_port_up(pp);
+}
+
+static void mvneta_mac_disable_tx_lpi(struct phylink_config *config)
+{
+	struct mvneta_port *pp = netdev_priv(to_net_dev(config->dev));
+	u32 lpi1;
+
+	lpi1 = mvreg_read(pp, MVNETA_LPI_CTRL_1);
+	lpi1 &= ~MVNETA_LPI_REQUEST_ENABLE;
+	mvreg_write(pp, MVNETA_LPI_CTRL_1, lpi1);
+}
+
+static void mvneta_mac_enable_tx_lpi(struct phylink_config *config, u32 timer)
+{
+	struct mvneta_port *pp = netdev_priv(to_net_dev(config->dev));
+	u32 ts, tw, lpi0, lpi1, status;
 
-	if (phy && pp->eee_enabled) {
-		pp->eee_active = phy_init_eee(phy, false) >= 0;
-		mvneta_set_eee(pp, pp->eee_active && pp->tx_lpi_enabled);
+	status = mvreg_read(pp, MVNETA_GMAC_STATUS);
+
+	if (status & MVNETA_GMAC_SPEED_1000) {
+		/* At 1G speeds, the timer resolution are 1us, and
+		 * 802.3 says tw is 16.5us. Round up to 17us.
+		 */
+		tw = 17;
+		ts = timer;
+	} else {
+		/* At 100M speeds, the timer resolutions are 10us, and
+		 * 802.3 says tw is 30us.
+		 */
+		tw = 3;
+		ts = DIV_ROUND_UP(timer, 10);
 	}
+
+	if (ts > 255)
+		ts = 255;
+
+	/* Ensure LPI generation is disabled */
+	lpi1 = mvreg_read(pp, MVNETA_LPI_CTRL_1);
+	mvreg_write(pp, MVNETA_LPI_CTRL_1, lpi1 & ~MVNETA_LPI_REQUEST_ENABLE);
+
+	/* Configure ts */
+	lpi0 = mvreg_read(pp, MVNETA_LPI_CTRL_0) & ~MVNETA_LPI_CTRL_0_TS;
+	lpi0 |= FIELD_PREP(MVNETA_LPI_CTRL_0_TS, ts);
+	mvreg_write(pp, MVNETA_LPI_CTRL_0, lpi0);
+
+	/* Configure tw */
+	lpi1 &= ~MVNETA_LPI_CTRL_1_TW;
+	lpi1 |= FIELD_PREP(MVNETA_LPI_CTRL_1_TW, tw);
+
+	/* Enable LPI generation */
+	mvreg_write(pp, MVNETA_LPI_CTRL_1, lpi1 | MVNETA_LPI_REQUEST_ENABLE);
 }
 
 static const struct phylink_mac_ops mvneta_phylink_ops = {
@@ -4298,6 +4334,8 @@ static const struct phylink_mac_ops mvneta_phylink_ops = {
 	.mac_finish = mvneta_mac_finish,
 	.mac_link_down = mvneta_mac_link_down,
 	.mac_link_up = mvneta_mac_link_up,
+	.mac_disable_tx_lpi = mvneta_mac_disable_tx_lpi,
+	.mac_enable_tx_lpi = mvneta_mac_enable_tx_lpi,
 };
 
 static int mvneta_mdio_probe(struct mvneta_port *pp)
@@ -5101,14 +5139,6 @@ static int mvneta_ethtool_get_eee(struct net_device *dev,
 				  struct ethtool_keee *eee)
 {
 	struct mvneta_port *pp = netdev_priv(dev);
-	u32 lpi_ctl0;
-
-	lpi_ctl0 = mvreg_read(pp, MVNETA_LPI_CTRL_0);
-
-	eee->eee_enabled = pp->eee_enabled;
-	eee->eee_active = pp->eee_active;
-	eee->tx_lpi_enabled = pp->tx_lpi_enabled;
-	eee->tx_lpi_timer = (lpi_ctl0) >> 8; // * scale;
 
 	return phylink_ethtool_get_eee(pp->phylink, eee);
 }
@@ -5117,23 +5147,6 @@ static int mvneta_ethtool_set_eee(struct net_device *dev,
 				  struct ethtool_keee *eee)
 {
 	struct mvneta_port *pp = netdev_priv(dev);
-	u32 lpi_ctl0;
-
-	/* The Armada 37x documents do not give limits for this other than
-	 * it being an 8-bit register.
-	 */
-	if (eee->tx_lpi_enabled && eee->tx_lpi_timer > 255)
-		return -EINVAL;
-
-	lpi_ctl0 = mvreg_read(pp, MVNETA_LPI_CTRL_0);
-	lpi_ctl0 &= ~(0xff << 8);
-	lpi_ctl0 |= eee->tx_lpi_timer << 8;
-	mvreg_write(pp, MVNETA_LPI_CTRL_0, lpi_ctl0);
-
-	pp->eee_enabled = eee->eee_enabled;
-	pp->tx_lpi_enabled = eee->tx_lpi_enabled;
-
-	mvneta_set_eee(pp, eee->tx_lpi_enabled && eee->eee_enabled);
 
 	return phylink_ethtool_set_eee(pp->phylink, eee);
 }
@@ -5538,6 +5551,8 @@ static int mvneta_probe(struct platform_device *pdev)
 	pp->phylink_config.type = PHYLINK_NETDEV;
 	pp->phylink_config.mac_capabilities = MAC_SYM_PAUSE | MAC_10 |
 		MAC_100 | MAC_1000FD | MAC_2500FD;
+	pp->phylink_config.lpi_capabilities = MAC_100FD | MAC_1000FD;
+	pp->phylink_config.lpi_timer_limit_us = 255;
 
 	phy_interface_set_rgmii(pp->phylink_config.supported_interfaces);
 	__set_bit(PHY_INTERFACE_MODE_QSGMII,
@@ -5565,6 +5580,11 @@ static int mvneta_probe(struct platform_device *pdev)
 			  pp->phylink_config.supported_interfaces);
 	}
 
+	/* Setup EEE.  Choose 250us idle. */
+	pp->phylink_config.eee.eee_enabled = true;
+	pp->phylink_config.eee.tx_lpi_enabled = true;
+	pp->phylink_config.eee.tx_lpi_timer = 250;
+
 	phylink = phylink_create(&pp->phylink_config, pdev->dev.fwnode,
 				 phy_mode, &mvneta_phylink_ops);
 	if (IS_ERR(phylink)) {
diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2.h b/drivers/net/ethernet/marvell/mvpp2/mvpp2.h
index e809f91c08fb..299b996ac5df 100644
--- a/drivers/net/ethernet/marvell/mvpp2/mvpp2.h
+++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2.h
@@ -481,6 +481,11 @@
 #define MVPP22_GMAC_INT_SUM_MASK		0xa4
 #define     MVPP22_GMAC_INT_SUM_MASK_LINK_STAT	BIT(1)
 #define	    MVPP22_GMAC_INT_SUM_MASK_PTP	BIT(2)
+#define MVPP2_GMAC_LPI_CTRL0			0xc0
+#define     MVPP2_GMAC_LPI_CTRL0_TS_MASK	GENMASK(8, 8)
+#define MVPP2_GMAC_LPI_CTRL1			0xc4
+#define     MVPP2_GMAC_LPI_CTRL1_REQ_EN		BIT(0)
+#define     MVPP2_GMAC_LPI_CTRL1_TW_MASK	GENMASK(15, 4)
 
 /* Per-port XGMAC registers. PPv2.2 and PPv2.3, only for GOP port 0,
  * relative to port->base.
diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
index 0d62a33afa80..cdd900fe6fd2 100644
--- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
+++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
@@ -5730,6 +5730,28 @@ static int mvpp2_ethtool_set_rxfh(struct net_device *dev,
 	return ret;
 }
 
+static int mvpp2_ethtool_get_eee(struct net_device *dev,
+				 struct ethtool_keee *eee)
+{
+	struct mvpp2_port *port = netdev_priv(dev);
+
+	if (!port->phylink)
+		return -EOPNOTSUPP;
+
+	return phylink_ethtool_get_eee(port->phylink, eee);
+}
+
+static int mvpp2_ethtool_set_eee(struct net_device *dev,
+				 struct ethtool_keee *eee)
+{
+	struct mvpp2_port *port = netdev_priv(dev);
+
+	if (!port->phylink)
+		return -EOPNOTSUPP;
+
+	return phylink_ethtool_set_eee(port->phylink, eee);
+}
+
 /* Device ops */
 
 static const struct net_device_ops mvpp2_netdev_ops = {
@@ -5772,6 +5794,8 @@ static const struct ethtool_ops mvpp2_eth_tool_ops = {
 	.get_rxfh_indir_size	= mvpp2_ethtool_get_rxfh_indir_size,
 	.get_rxfh		= mvpp2_ethtool_get_rxfh,
 	.set_rxfh		= mvpp2_ethtool_set_rxfh,
+	.get_eee		= mvpp2_ethtool_get_eee,
+	.set_eee		= mvpp2_ethtool_set_eee,
 };
 
 /* Used for PPv2.1, or PPv2.2 with the old Device Tree binding that
@@ -6194,19 +6218,26 @@ static const struct phylink_pcs_ops mvpp2_phylink_xlg_pcs_ops = {
 	.pcs_config = mvpp2_xlg_pcs_config,
 };
 
-static int mvpp2_gmac_pcs_validate(struct phylink_pcs *pcs,
-				   unsigned long *supported,
-				   const struct phylink_link_state *state)
+static unsigned int mvpp2_gmac_pcs_query_inband(struct phylink_pcs *pcs,
+						phy_interface_t interface)
 {
-	/* When in 802.3z mode, we must have AN enabled:
+	/* When operating in an 802.3z mode, we must have AN enabled:
 	 * Bit 2 Field InBandAnEn In-band Auto-Negotiation enable. ...
 	 * When <PortType> = 1 (1000BASE-X) this field must be set to 1.
+	 * Therefore, inband is "required".
 	 */
-	if (phy_interface_mode_is_8023z(state->interface) &&
-	    !phylink_test(state->advertising, Autoneg))
-		return -EINVAL;
+	if (phy_interface_mode_is_8023z(interface))
+		return LINK_INBAND_ENABLE;
 
-	return 0;
+	/* SGMII and RGMII can be configured to use inband signalling of the
+	 * AN result. Indicate these as "possible".
+	 */
+	if (interface == PHY_INTERFACE_MODE_SGMII ||
+	    phy_interface_mode_is_rgmii(interface))
+		return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE;
+
+	/* For any other modes, indicate that inband is not supported. */
+	return LINK_INBAND_DISABLE;
 }
 
 static void mvpp2_gmac_pcs_get_state(struct phylink_pcs *pcs,
@@ -6313,7 +6344,7 @@ static void mvpp2_gmac_pcs_an_restart(struct phylink_pcs *pcs)
 }
 
 static const struct phylink_pcs_ops mvpp2_phylink_gmac_pcs_ops = {
-	.pcs_validate = mvpp2_gmac_pcs_validate,
+	.pcs_query_inband = mvpp2_gmac_pcs_query_inband,
 	.pcs_get_state = mvpp2_gmac_pcs_get_state,
 	.pcs_config = mvpp2_gmac_pcs_config,
 	.pcs_an_restart = mvpp2_gmac_pcs_an_restart,
@@ -6635,6 +6666,57 @@ static void mvpp2_mac_link_down(struct phylink_config *config,
 	mvpp2_port_disable(port);
 }
 
+static void mvpp2_mac_disable_tx_lpi(struct phylink_config *config)
+{
+	struct mvpp2_port *port = mvpp2_phylink_to_port(config);
+
+	mvpp2_modify(port->base + MVPP2_GMAC_LPI_CTRL1,
+		     MVPP2_GMAC_LPI_CTRL1_REQ_EN, 0);
+}
+
+static void mvpp2_mac_enable_tx_lpi(struct phylink_config *config, u32 timer)
+{
+	struct mvpp2_port *port = mvpp2_phylink_to_port(config);
+	u32 ts, tw, lpi0, lpi1, status;
+
+	status = readl(port->base + MVPP2_GMAC_STATUS0);
+	if (status & MVPP2_GMAC_STATUS0_GMII_SPEED) {
+		/* At 1G speeds, the timer resolution are 1us, and
+		 * 802.3 says tw is 16.5us. Round up to 17us.
+		 */
+		tw = 17;
+		ts = timer;
+	} else {
+		/* At 100M speeds, the timer resolutions are 10us, and
+		 * 802.3 says tw is 30us.
+		 */
+		tw = 3;
+		ts = DIV_ROUND_UP(timer, 10);
+	}
+
+	if (ts > 255)
+		ts = 255;
+
+	/* Ensure LPI generation is disabled */
+	lpi1 = readl(port->base + MVPP2_GMAC_LPI_CTRL1);
+	writel(lpi1 & ~MVPP2_GMAC_LPI_CTRL1_REQ_EN,
+	       port->base + MVPP2_GMAC_LPI_CTRL1);
+
+	/* Configure ts */
+	lpi0 = readl(port->base + MVPP2_GMAC_LPI_CTRL0);
+	lpi0 &= ~MVPP2_GMAC_LPI_CTRL0_TS_MASK;
+	lpi0 |= FIELD_PREP(MVPP2_GMAC_LPI_CTRL0_TS_MASK, ts);
+	writel(lpi0, port->base + MVPP2_GMAC_LPI_CTRL0);
+
+	/* Configure tw */
+	lpi1 &= ~MVPP2_GMAC_LPI_CTRL1_TW_MASK;
+	lpi1 |= FIELD_PREP(MVPP2_GMAC_LPI_CTRL1_TW_MASK, tw);
+
+	/* Enable LPI generation */
+	writel(lpi1 | MVPP2_GMAC_LPI_CTRL1_REQ_EN,
+	       port->base + MVPP2_GMAC_LPI_CTRL1);
+}
+
 static const struct phylink_mac_ops mvpp2_phylink_ops = {
 	.mac_select_pcs = mvpp2_select_pcs,
 	.mac_prepare = mvpp2_mac_prepare,
@@ -6642,6 +6724,8 @@ static const struct phylink_mac_ops mvpp2_phylink_ops = {
 	.mac_finish = mvpp2_mac_finish,
 	.mac_link_up = mvpp2_mac_link_up,
 	.mac_link_down = mvpp2_mac_link_down,
+	.mac_enable_tx_lpi = mvpp2_mac_enable_tx_lpi,
+	.mac_disable_tx_lpi = mvpp2_mac_disable_tx_lpi,
 };
 
 /* Work-around for ACPI */
@@ -6919,6 +7003,9 @@ static int mvpp2_port_probe(struct platform_device *pdev,
 		port->phylink_config.type = PHYLINK_NETDEV;
 		port->phylink_config.mac_capabilities =
 			MAC_2500FD | MAC_1000FD | MAC_100 | MAC_10;
+		port->phylink_config.lpi_capabilities =
+			MAC_2500FD | MAC_1000FD | MAC_100FD;
+		port->phylink_config.lpi_timer_limit_us = 255;
 
 		if (port->priv->global_tx_fc)
 			port->phylink_config.mac_capabilities |=
@@ -6987,6 +7074,11 @@ static int mvpp2_port_probe(struct platform_device *pdev,
 				  port->phylink_config.supported_interfaces);
 		}
 
+		/* Setup EEE.  Choose 250us idle. */
+		port->phylink_config.eee.eee_enabled = true;
+		port->phylink_config.eee.tx_lpi_enabled = true;
+		port->phylink_config.eee.tx_lpi_timer = 250;
+
 		phylink = phylink_create(&port->phylink_config, port_fwnode,
 					 phy_mode, &mvpp2_phylink_ops);
 		if (IS_ERR(phylink)) {
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c
index 83ad7c7935e3..48acba5eb178 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c
@@ -451,7 +451,7 @@ static struct phylink_pcs *intel_mgbe_select_pcs(struct stmmac_priv *priv,
 	 * should always be an XPCS. The original code would always
 	 * return this if present.
 	 */
-	return &priv->hw->xpcs->pcs;
+	return xpcs_to_phylink_pcs(priv->hw->xpcs);
 }
 
 static int intel_mgbe_common_data(struct pci_dev *pdev,
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c
index 03f90676b3ad..0c7d81ddd440 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c
@@ -500,23 +500,22 @@ int stmmac_pcs_setup(struct net_device *ndev)
 	struct fwnode_handle *devnode, *pcsnode;
 	struct dw_xpcs *xpcs = NULL;
 	struct stmmac_priv *priv;
-	int addr, mode, ret;
+	int addr, ret;
 
 	priv = netdev_priv(ndev);
-	mode = priv->plat->phy_interface;
 	devnode = priv->plat->port_node;
 
 	if (priv->plat->pcs_init) {
 		ret = priv->plat->pcs_init(priv);
 	} else if (fwnode_property_present(devnode, "pcs-handle")) {
 		pcsnode = fwnode_find_reference(devnode, "pcs-handle", 0);
-		xpcs = xpcs_create_fwnode(pcsnode, mode);
+		xpcs = xpcs_create_fwnode(pcsnode);
 		fwnode_handle_put(pcsnode);
 		ret = PTR_ERR_OR_ZERO(xpcs);
 	} else if (priv->plat->mdio_bus_data &&
 		   priv->plat->mdio_bus_data->pcs_mask) {
 		addr = ffs(priv->plat->mdio_bus_data->pcs_mask) - 1;
-		xpcs = xpcs_create_mdiodev(priv->mii, addr, mode);
+		xpcs = xpcs_create_mdiodev(priv->mii, addr);
 		ret = PTR_ERR_OR_ZERO(xpcs);
 	} else {
 		return 0;
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c
index 5f502265f0a6..515e917333a6 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c
@@ -122,7 +122,7 @@ static int txgbe_pcs_write(struct mii_bus *bus, int addr, int devnum, int regnum
 static int txgbe_mdio_pcs_init(struct txgbe *txgbe)
 {
 	struct mii_bus *mii_bus;
-	struct dw_xpcs *xpcs;
+	struct phylink_pcs *pcs;
 	struct pci_dev *pdev;
 	struct wx *wx;
 	int ret = 0;
@@ -147,11 +147,11 @@ static int txgbe_mdio_pcs_init(struct txgbe *txgbe)
 	if (ret)
 		return ret;
 
-	xpcs = xpcs_create_mdiodev(mii_bus, 0, PHY_INTERFACE_MODE_10GBASER);
-	if (IS_ERR(xpcs))
-		return PTR_ERR(xpcs);
+	pcs = xpcs_create_pcs_mdiodev(mii_bus, 0);
+	if (IS_ERR(pcs))
+		return PTR_ERR(pcs);
 
-	txgbe->xpcs = xpcs;
+	txgbe->pcs = pcs;
 
 	return 0;
 }
@@ -163,7 +163,7 @@ static struct phylink_pcs *txgbe_phylink_mac_select(struct phylink_config *confi
 	struct txgbe *txgbe = wx->priv;
 
 	if (interface == PHY_INTERFACE_MODE_10GBASER)
-		return &txgbe->xpcs->pcs;
+		return txgbe->pcs;
 
 	return NULL;
 }
@@ -302,7 +302,7 @@ irqreturn_t txgbe_link_irq_handler(int irq, void *data)
 	status = rd32(wx, TXGBE_CFG_PORT_ST);
 	up = !!(status & TXGBE_CFG_PORT_ST_LINK_UP);
 
-	phylink_pcs_change(&txgbe->xpcs->pcs, up);
+	phylink_pcs_change(txgbe->pcs, up);
 
 	return IRQ_HANDLED;
 }
@@ -779,7 +779,7 @@ int txgbe_init_phy(struct txgbe *txgbe)
 err_destroy_phylink:
 	phylink_destroy(wx->phylink);
 err_destroy_xpcs:
-	xpcs_destroy(txgbe->xpcs);
+	xpcs_destroy_pcs(txgbe->pcs);
 err_unregister_swnode:
 	software_node_unregister_node_group(txgbe->nodes.group);
 
@@ -799,6 +799,6 @@ void txgbe_remove_phy(struct txgbe *txgbe)
 	clkdev_drop(txgbe->clock);
 	clk_unregister(txgbe->clk);
 	phylink_destroy(txgbe->wx->phylink);
-	xpcs_destroy(txgbe->xpcs);
+	xpcs_destroy_pcs(txgbe->pcs);
 	software_node_unregister_node_group(txgbe->nodes.group);
 }
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h
index 959102c4c379..cc3a7b62fe9e 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h
@@ -329,7 +329,7 @@ struct txgbe {
 	struct wx *wx;
 	struct txgbe_nodes nodes;
 	struct txgbe_irq misc;
-	struct dw_xpcs *xpcs;
+	struct phylink_pcs *pcs;
 	struct platform_device *sfp_dev;
 	struct platform_device *i2c_dev;
 	struct clk_lookup *clock;
diff --git a/drivers/net/pcs/pcs-xpcs-nxp.c b/drivers/net/pcs/pcs-xpcs-nxp.c
index d16fc58cd48d..e8efe94cf4ec 100644
--- a/drivers/net/pcs/pcs-xpcs-nxp.c
+++ b/drivers/net/pcs/pcs-xpcs-nxp.c
@@ -152,26 +152,18 @@ static int nxp_sja1110_pma_config(struct dw_xpcs *xpcs,
 	/* Enable TX and RX PLLs and circuits.
 	 * Release reset of PMA to enable data flow to/from PCS.
 	 */
-	ret = xpcs_read(xpcs, MDIO_MMD_VEND2, SJA1110_POWERDOWN_ENABLE);
-	if (ret < 0)
-		return ret;
-
-	val = ret & ~(SJA1110_TXPLL_PD | SJA1110_TXPD | SJA1110_RXCH_PD |
-		      SJA1110_RXBIAS_PD | SJA1110_RESET_SER_EN |
-		      SJA1110_RESET_SER | SJA1110_RESET_DES);
-	val |= SJA1110_RXPKDETEN | SJA1110_RCVEN;
-
-	ret = xpcs_write(xpcs, MDIO_MMD_VEND2, SJA1110_POWERDOWN_ENABLE, val);
+	ret = xpcs_modify(xpcs, MDIO_MMD_VEND2, SJA1110_POWERDOWN_ENABLE,
+			  SJA1110_TXPLL_PD | SJA1110_TXPD | SJA1110_RXCH_PD |
+			  SJA1110_RXBIAS_PD | SJA1110_RESET_SER_EN |
+			  SJA1110_RESET_SER | SJA1110_RESET_DES |
+			  SJA1110_RXPKDETEN | SJA1110_RCVEN,
+			  SJA1110_RXPKDETEN | SJA1110_RCVEN);
 	if (ret < 0)
 		return ret;
 
 	/* Program continuous-time linear equalizer (CTLE) settings. */
-	ret = xpcs_write(xpcs, MDIO_MMD_VEND2, SJA1110_RX_CDR_CTLE,
-			 rx_cdr_ctle);
-	if (ret < 0)
-		return ret;
-
-	return 0;
+	return xpcs_write(xpcs, MDIO_MMD_VEND2, SJA1110_RX_CDR_CTLE,
+			  rx_cdr_ctle);
 }
 
 int nxp_sja1110_sgmii_pma_config(struct dw_xpcs *xpcs)
diff --git a/drivers/net/pcs/pcs-xpcs-wx.c b/drivers/net/pcs/pcs-xpcs-wx.c
index 19c75886f070..fc52f7aa5f59 100644
--- a/drivers/net/pcs/pcs-xpcs-wx.c
+++ b/drivers/net/pcs/pcs-xpcs-wx.c
@@ -46,25 +46,23 @@
 #define TXGBE_VCO_CAL_LD0		0x72
 #define TXGBE_VCO_CAL_REF0		0x76
 
-static int txgbe_read_pma(struct dw_xpcs *xpcs, int reg)
+static int txgbe_write_pma(struct dw_xpcs *xpcs, int reg, u16 val)
 {
-	return xpcs_read(xpcs, MDIO_MMD_PMAPMD, TXGBE_PMA_MMD + reg);
+	return xpcs_write(xpcs, MDIO_MMD_PMAPMD, TXGBE_PMA_MMD + reg, val);
 }
 
-static int txgbe_write_pma(struct dw_xpcs *xpcs, int reg, u16 val)
+static int txgbe_modify_pma(struct dw_xpcs *xpcs, int reg, u16 mask, u16 set)
 {
-	return xpcs_write(xpcs, MDIO_MMD_PMAPMD, TXGBE_PMA_MMD + reg, val);
+	return xpcs_modify(xpcs, MDIO_MMD_PMAPMD, TXGBE_PMA_MMD + reg, mask,
+			   set);
 }
 
 static void txgbe_pma_config_10gbaser(struct dw_xpcs *xpcs)
 {
-	int val;
-
 	txgbe_write_pma(xpcs, TXGBE_MPLLA_CTL0, 0x21);
 	txgbe_write_pma(xpcs, TXGBE_MPLLA_CTL3, 0);
-	val = txgbe_read_pma(xpcs, TXGBE_TX_GENCTL1);
-	val = u16_replace_bits(val, 0x5, TXGBE_TX_GENCTL1_VBOOST_LVL);
-	txgbe_write_pma(xpcs, TXGBE_TX_GENCTL1, val);
+	txgbe_modify_pma(xpcs, TXGBE_TX_GENCTL1, TXGBE_TX_GENCTL1_VBOOST_LVL,
+			 FIELD_PREP(TXGBE_TX_GENCTL1_VBOOST_LVL, 0x5));
 	txgbe_write_pma(xpcs, TXGBE_MISC_CTL0, TXGBE_MISC_CTL0_PLL |
 			TXGBE_MISC_CTL0_CR_PARA_SEL | TXGBE_MISC_CTL0_RX_VREF(0xF));
 	txgbe_write_pma(xpcs, TXGBE_VCO_CAL_LD0, 0x549);
@@ -78,38 +76,29 @@ static void txgbe_pma_config_10gbaser(struct dw_xpcs *xpcs)
 
 	txgbe_write_pma(xpcs, TXGBE_RX_EQ_CTL0, TXGBE_RX_EQ_CTL0_CTLE_POLE(2) |
 			TXGBE_RX_EQ_CTL0_CTLE_BOOST(5));
-	val = txgbe_read_pma(xpcs, TXGBE_RX_EQ_ATTN_CTL);
-	val &= ~TXGBE_RX_EQ_ATTN_LVL0;
-	txgbe_write_pma(xpcs, TXGBE_RX_EQ_ATTN_CTL, val);
+	txgbe_modify_pma(xpcs, TXGBE_RX_EQ_ATTN_CTL, TXGBE_RX_EQ_ATTN_LVL0, 0);
 	txgbe_write_pma(xpcs, TXGBE_DFE_TAP_CTL0, 0xBE);
-	val = txgbe_read_pma(xpcs, TXGBE_AFE_DFE_ENABLE);
-	val &= ~(TXGBE_DFE_EN_0 | TXGBE_AFE_EN_0);
-	txgbe_write_pma(xpcs, TXGBE_AFE_DFE_ENABLE, val);
-	val = txgbe_read_pma(xpcs, TXGBE_RX_EQ_CTL4);
-	val &= ~TXGBE_RX_EQ_CTL4_CONT_ADAPT0;
-	txgbe_write_pma(xpcs, TXGBE_RX_EQ_CTL4, val);
+	txgbe_modify_pma(xpcs, TXGBE_AFE_DFE_ENABLE,
+			 TXGBE_DFE_EN_0 | TXGBE_AFE_EN_0, 0);
+	txgbe_modify_pma(xpcs, TXGBE_RX_EQ_CTL4, TXGBE_RX_EQ_CTL4_CONT_ADAPT0,
+			 0);
 }
 
 static void txgbe_pma_config_1g(struct dw_xpcs *xpcs)
 {
-	int val;
-
-	val = txgbe_read_pma(xpcs, TXGBE_TX_GENCTL1);
-	val = u16_replace_bits(val, 0x5, TXGBE_TX_GENCTL1_VBOOST_LVL);
-	val &= ~TXGBE_TX_GENCTL1_VBOOST_EN0;
-	txgbe_write_pma(xpcs, TXGBE_TX_GENCTL1, val);
+	txgbe_modify_pma(xpcs, TXGBE_TX_GENCTL1,
+			 TXGBE_TX_GENCTL1_VBOOST_LVL |
+			 TXGBE_TX_GENCTL1_VBOOST_EN0,
+			 FIELD_PREP(TXGBE_TX_GENCTL1_VBOOST_LVL, 0x5));
 	txgbe_write_pma(xpcs, TXGBE_MISC_CTL0, TXGBE_MISC_CTL0_PLL |
 			TXGBE_MISC_CTL0_CR_PARA_SEL | TXGBE_MISC_CTL0_RX_VREF(0xF));
 
 	txgbe_write_pma(xpcs, TXGBE_RX_EQ_CTL0, TXGBE_RX_EQ_CTL0_VGA1_GAIN(7) |
 			TXGBE_RX_EQ_CTL0_VGA2_GAIN(7) | TXGBE_RX_EQ_CTL0_CTLE_BOOST(6));
-	val = txgbe_read_pma(xpcs, TXGBE_RX_EQ_ATTN_CTL);
-	val &= ~TXGBE_RX_EQ_ATTN_LVL0;
-	txgbe_write_pma(xpcs, TXGBE_RX_EQ_ATTN_CTL, val);
+	txgbe_modify_pma(xpcs, TXGBE_RX_EQ_ATTN_CTL, TXGBE_RX_EQ_ATTN_LVL0, 0);
 	txgbe_write_pma(xpcs, TXGBE_DFE_TAP_CTL0, 0);
-	val = txgbe_read_pma(xpcs, TXGBE_RX_GEN_CTL3);
-	val = u16_replace_bits(val, 0x4, TXGBE_RX_GEN_CTL3_LOS_TRSHLD0);
-	txgbe_write_pma(xpcs, TXGBE_RX_EQ_ATTN_CTL, val);
+	txgbe_modify_pma(xpcs, TXGBE_RX_GEN_CTL3, TXGBE_RX_GEN_CTL3_LOS_TRSHLD0,
+			 FIELD_PREP(TXGBE_RX_GEN_CTL3_LOS_TRSHLD0, 0x4));
 
 	txgbe_write_pma(xpcs, TXGBE_MPLLA_CTL0, 0x20);
 	txgbe_write_pma(xpcs, TXGBE_MPLLA_CTL3, 0x46);
@@ -172,7 +161,7 @@ static bool txgbe_xpcs_mode_quirk(struct dw_xpcs *xpcs)
 
 int txgbe_xpcs_switch_mode(struct dw_xpcs *xpcs, phy_interface_t interface)
 {
-	int val, ret;
+	int ret;
 
 	switch (interface) {
 	case PHY_INTERFACE_MODE_10GBASER:
@@ -194,9 +183,8 @@ int txgbe_xpcs_switch_mode(struct dw_xpcs *xpcs, phy_interface_t interface)
 
 	if (interface == PHY_INTERFACE_MODE_10GBASER) {
 		xpcs_write(xpcs, MDIO_MMD_PCS, MDIO_CTRL2, MDIO_PCS_CTRL2_10GBR);
-		val = xpcs_read(xpcs, MDIO_MMD_PMAPMD, MDIO_CTRL1);
-		val |= MDIO_CTRL1_SPEED10G;
-		xpcs_write(xpcs, MDIO_MMD_PMAPMD, MDIO_CTRL1, val);
+		xpcs_modify(xpcs, MDIO_MMD_PMAPMD, MDIO_CTRL1,
+			    MDIO_CTRL1_SPEED10G, MDIO_CTRL1_SPEED10G);
 		txgbe_pma_config_10gbaser(xpcs);
 	} else {
 		xpcs_write(xpcs, MDIO_MMD_PCS, MDIO_CTRL2, MDIO_PCS_CTRL2_10GBX);
diff --git a/drivers/net/pcs/pcs-xpcs.c b/drivers/net/pcs/pcs-xpcs.c
index 82463f9d50c8..c69421e80d19 100644
--- a/drivers/net/pcs/pcs-xpcs.c
+++ b/drivers/net/pcs/pcs-xpcs.c
@@ -107,49 +107,9 @@ static const int xpcs_2500basex_features[] = {
 	__ETHTOOL_LINK_MODE_MASK_NBITS,
 };
 
-static const phy_interface_t xpcs_usxgmii_interfaces[] = {
-	PHY_INTERFACE_MODE_USXGMII,
-};
-
-static const phy_interface_t xpcs_10gkr_interfaces[] = {
-	PHY_INTERFACE_MODE_10GKR,
-};
-
-static const phy_interface_t xpcs_xlgmii_interfaces[] = {
-	PHY_INTERFACE_MODE_XLGMII,
-};
-
-static const phy_interface_t xpcs_10gbaser_interfaces[] = {
-	PHY_INTERFACE_MODE_10GBASER,
-};
-
-static const phy_interface_t xpcs_sgmii_interfaces[] = {
-	PHY_INTERFACE_MODE_SGMII,
-};
-
-static const phy_interface_t xpcs_1000basex_interfaces[] = {
-	PHY_INTERFACE_MODE_1000BASEX,
-};
-
-static const phy_interface_t xpcs_2500basex_interfaces[] = {
-	PHY_INTERFACE_MODE_2500BASEX,
-};
-
-enum {
-	DW_XPCS_USXGMII,
-	DW_XPCS_10GKR,
-	DW_XPCS_XLGMII,
-	DW_XPCS_10GBASER,
-	DW_XPCS_SGMII,
-	DW_XPCS_1000BASEX,
-	DW_XPCS_2500BASEX,
-	DW_XPCS_INTERFACE_MAX,
-};
-
 struct dw_xpcs_compat {
+	phy_interface_t interface;
 	const int *supported;
-	const phy_interface_t *interface;
-	int num_interfaces;
 	int an_mode;
 	int (*pma_config)(struct dw_xpcs *xpcs);
 };
@@ -161,26 +121,28 @@ struct dw_xpcs_desc {
 };
 
 static const struct dw_xpcs_compat *
-xpcs_find_compat(const struct dw_xpcs_desc *desc, phy_interface_t interface)
+xpcs_find_compat(struct dw_xpcs *xpcs, phy_interface_t interface)
 {
-	int i, j;
-
-	for (i = 0; i < DW_XPCS_INTERFACE_MAX; i++) {
-		const struct dw_xpcs_compat *compat = &desc->compat[i];
+	const struct dw_xpcs_compat *compat;
 
-		for (j = 0; j < compat->num_interfaces; j++)
-			if (compat->interface[j] == interface)
-				return compat;
-	}
+	for (compat = xpcs->desc->compat; compat->supported; compat++)
+		if (compat->interface == interface)
+			return compat;
 
 	return NULL;
 }
 
+struct phylink_pcs *xpcs_to_phylink_pcs(struct dw_xpcs *xpcs)
+{
+	return &xpcs->pcs;
+}
+EXPORT_SYMBOL_GPL(xpcs_to_phylink_pcs);
+
 int xpcs_get_an_mode(struct dw_xpcs *xpcs, phy_interface_t interface)
 {
 	const struct dw_xpcs_compat *compat;
 
-	compat = xpcs_find_compat(xpcs->desc, interface);
+	compat = xpcs_find_compat(xpcs, interface);
 	if (!compat)
 		return -ENODEV;
 
@@ -213,6 +175,11 @@ int xpcs_write(struct dw_xpcs *xpcs, int dev, u32 reg, u16 val)
 	return mdiodev_c45_write(xpcs->mdiodev, dev, reg, val);
 }
 
+int xpcs_modify(struct dw_xpcs *xpcs, int dev, u32 reg, u16 mask, u16 set)
+{
+	return mdiodev_c45_modify(xpcs->mdiodev, dev, reg, mask, set);
+}
+
 static int xpcs_modify_changed(struct dw_xpcs *xpcs, int dev, u32 reg,
 			       u16 mask, u16 set)
 {
@@ -230,6 +197,12 @@ static int xpcs_write_vendor(struct dw_xpcs *xpcs, int dev, int reg,
 	return xpcs_write(xpcs, dev, DW_VENDOR | reg, val);
 }
 
+static int xpcs_modify_vendor(struct dw_xpcs *xpcs, int dev, int reg, u16 mask,
+			      u16 set)
+{
+	return xpcs_modify(xpcs, dev, DW_VENDOR | reg, mask, set);
+}
+
 int xpcs_read_vpcs(struct dw_xpcs *xpcs, int reg)
 {
 	return xpcs_read_vendor(xpcs, MDIO_MMD_PCS, reg);
@@ -240,20 +213,22 @@ int xpcs_write_vpcs(struct dw_xpcs *xpcs, int reg, u16 val)
 	return xpcs_write_vendor(xpcs, MDIO_MMD_PCS, reg, val);
 }
 
+static int xpcs_modify_vpcs(struct dw_xpcs *xpcs, int reg, u16 mask, u16 val)
+{
+	return xpcs_modify_vendor(xpcs, MDIO_MMD_PCS, reg, mask, val);
+}
+
 static int xpcs_poll_reset(struct dw_xpcs *xpcs, int dev)
 {
-	/* Poll until the reset bit clears (50ms per retry == 0.6 sec) */
-	unsigned int retries = 12;
-	int ret;
+	int ret, val;
 
-	do {
-		msleep(50);
-		ret = xpcs_read(xpcs, dev, MDIO_CTRL1);
-		if (ret < 0)
-			return ret;
-	} while (ret & MDIO_CTRL1_RESET && --retries);
+	ret = read_poll_timeout(xpcs_read, val,
+				val < 0 || !(val & MDIO_CTRL1_RESET),
+				50000, 600000, true, xpcs, dev, MDIO_CTRL1);
+	if (val < 0)
+		ret = val;
 
-	return (ret & MDIO_CTRL1_RESET) ? -ETIMEDOUT : 0;
+	return ret;
 }
 
 static int xpcs_soft_reset(struct dw_xpcs *xpcs,
@@ -364,37 +339,25 @@ static void xpcs_config_usxgmii(struct dw_xpcs *xpcs, int speed)
 		return;
 	}
 
-	ret = xpcs_read_vpcs(xpcs, MDIO_CTRL1);
-	if (ret < 0)
-		goto out;
-
-	ret = xpcs_write_vpcs(xpcs, MDIO_CTRL1, ret | DW_USXGMII_EN);
-	if (ret < 0)
-		goto out;
-
-	ret = xpcs_read(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1);
-	if (ret < 0)
-		goto out;
-
-	ret &= ~DW_USXGMII_SS_MASK;
-	ret |= speed_sel | DW_USXGMII_FULL;
-
-	ret = xpcs_write(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1, ret);
+	ret = xpcs_modify_vpcs(xpcs, MDIO_CTRL1, DW_USXGMII_EN, DW_USXGMII_EN);
 	if (ret < 0)
 		goto out;
 
-	ret = xpcs_read_vpcs(xpcs, MDIO_CTRL1);
+	ret = xpcs_modify(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1, DW_USXGMII_SS_MASK,
+			  speed_sel | DW_USXGMII_FULL);
 	if (ret < 0)
 		goto out;
 
-	ret = xpcs_write_vpcs(xpcs, MDIO_CTRL1, ret | DW_USXGMII_RST);
+	ret = xpcs_modify_vpcs(xpcs, MDIO_CTRL1, DW_USXGMII_RST,
+			       DW_USXGMII_RST);
 	if (ret < 0)
 		goto out;
 
 	return;
 
 out:
-	pr_err("%s: XPCS access returned %pe\n", __func__, ERR_PTR(ret));
+	dev_err(&xpcs->mdiodev->dev, "%s: XPCS access returned %pe\n",
+		__func__, ERR_PTR(ret));
 }
 
 static int _xpcs_config_aneg_c73(struct dw_xpcs *xpcs,
@@ -451,13 +414,9 @@ static int xpcs_config_aneg_c73(struct dw_xpcs *xpcs,
 	if (ret < 0)
 		return ret;
 
-	ret = xpcs_read(xpcs, MDIO_MMD_AN, MDIO_CTRL1);
-	if (ret < 0)
-		return ret;
-
-	ret |= MDIO_AN_CTRL1_ENABLE | MDIO_AN_CTRL1_RESTART;
-
-	return xpcs_write(xpcs, MDIO_MMD_AN, MDIO_CTRL1, ret);
+	return xpcs_modify(xpcs, MDIO_MMD_AN, MDIO_CTRL1,
+			   MDIO_AN_CTRL1_ENABLE | MDIO_AN_CTRL1_RESTART,
+			   MDIO_AN_CTRL1_ENABLE | MDIO_AN_CTRL1_RESTART);
 }
 
 static int xpcs_aneg_done_c73(struct dw_xpcs *xpcs,
@@ -592,7 +551,7 @@ static int xpcs_validate(struct phylink_pcs *pcs, unsigned long *supported,
 	int i;
 
 	xpcs = phylink_pcs_to_xpcs(pcs);
-	compat = xpcs_find_compat(xpcs->desc, state->interface);
+	compat = xpcs_find_compat(xpcs, state->interface);
 	if (!compat)
 		return -EINVAL;
 
@@ -610,62 +569,72 @@ static int xpcs_validate(struct phylink_pcs *pcs, unsigned long *supported,
 
 void xpcs_get_interfaces(struct dw_xpcs *xpcs, unsigned long *interfaces)
 {
-	int i, j;
-
-	for (i = 0; i < DW_XPCS_INTERFACE_MAX; i++) {
-		const struct dw_xpcs_compat *compat = &xpcs->desc->compat[i];
+	const struct dw_xpcs_compat *compat;
 
-		for (j = 0; j < compat->num_interfaces; j++)
-			__set_bit(compat->interface[j], interfaces);
-	}
+	for (compat = xpcs->desc->compat; compat->supported; compat++)
+		__set_bit(compat->interface, interfaces);
 }
 EXPORT_SYMBOL_GPL(xpcs_get_interfaces);
 
 int xpcs_config_eee(struct dw_xpcs *xpcs, int mult_fact_100ns, int enable)
 {
+	u16 mask, val;
 	int ret;
 
-	ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_EEE_MCTRL0);
-	if (ret < 0)
-		return ret;
+	mask = DW_VR_MII_EEE_LTX_EN | DW_VR_MII_EEE_LRX_EN |
+	       DW_VR_MII_EEE_TX_QUIET_EN | DW_VR_MII_EEE_RX_QUIET_EN |
+	       DW_VR_MII_EEE_TX_EN_CTRL | DW_VR_MII_EEE_RX_EN_CTRL |
+	       DW_VR_MII_EEE_MULT_FACT_100NS;
 
-	if (enable) {
-	/* Enable EEE */
-		ret = DW_VR_MII_EEE_LTX_EN | DW_VR_MII_EEE_LRX_EN |
+	if (enable)
+		val = DW_VR_MII_EEE_LTX_EN | DW_VR_MII_EEE_LRX_EN |
 		      DW_VR_MII_EEE_TX_QUIET_EN | DW_VR_MII_EEE_RX_QUIET_EN |
 		      DW_VR_MII_EEE_TX_EN_CTRL | DW_VR_MII_EEE_RX_EN_CTRL |
-		      mult_fact_100ns << DW_VR_MII_EEE_MULT_FACT_100NS_SHIFT;
-	} else {
-		ret &= ~(DW_VR_MII_EEE_LTX_EN | DW_VR_MII_EEE_LRX_EN |
-		       DW_VR_MII_EEE_TX_QUIET_EN | DW_VR_MII_EEE_RX_QUIET_EN |
-		       DW_VR_MII_EEE_TX_EN_CTRL | DW_VR_MII_EEE_RX_EN_CTRL |
-		       DW_VR_MII_EEE_MULT_FACT_100NS);
-	}
+		      FIELD_PREP(DW_VR_MII_EEE_MULT_FACT_100NS,
+				 mult_fact_100ns);
+	else
+		val = 0;
 
-	ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_EEE_MCTRL0, ret);
+	ret = xpcs_modify(xpcs, MDIO_MMD_VEND2, DW_VR_MII_EEE_MCTRL0, mask,
+			  val);
 	if (ret < 0)
 		return ret;
 
-	ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_EEE_MCTRL1);
-	if (ret < 0)
-		return ret;
+	return xpcs_modify(xpcs, MDIO_MMD_VEND2, DW_VR_MII_EEE_MCTRL1,
+			   DW_VR_MII_EEE_TRN_LPI,
+			   enable ? DW_VR_MII_EEE_TRN_LPI : 0);
+}
+EXPORT_SYMBOL_GPL(xpcs_config_eee);
 
-	if (enable)
-		ret |= DW_VR_MII_EEE_TRN_LPI;
-	else
-		ret &= ~DW_VR_MII_EEE_TRN_LPI;
+static void xpcs_pre_config(struct phylink_pcs *pcs, phy_interface_t interface)
+{
+	struct dw_xpcs *xpcs = phylink_pcs_to_xpcs(pcs);
+	const struct dw_xpcs_compat *compat;
+	int ret;
+
+	if (!xpcs->need_reset)
+		return;
+
+	compat = xpcs_find_compat(xpcs, interface);
+	if (!compat) {
+		dev_err(&xpcs->mdiodev->dev, "unsupported interface %s\n",
+			phy_modes(interface));
+		return;
+	}
+
+	ret = xpcs_soft_reset(xpcs, compat);
+	if (ret)
+		dev_err(&xpcs->mdiodev->dev, "soft reset failed: %pe\n",
+			ERR_PTR(ret));
 
-	return xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_EEE_MCTRL1, ret);
+	xpcs->need_reset = false;
 }
-EXPORT_SYMBOL_GPL(xpcs_config_eee);
 
 static int xpcs_config_aneg_c37_sgmii(struct dw_xpcs *xpcs,
 				      unsigned int neg_mode)
 {
 	int ret, mdio_ctrl, tx_conf;
-
-	if (xpcs->info.pma == WX_TXGBE_XPCS_PMA_10G_ID)
-		xpcs_write_vpcs(xpcs, DW_VR_XS_PCS_DIG_CTRL1, DW_CL37_BP | DW_EN_VSMMD1);
+	u16 mask, val;
 
 	/* For AN for C37 SGMII mode, the settings are :-
 	 * 1) VR_MII_MMD_CTRL Bit(12) [AN_ENABLE] = 0b (Disable SGMII AN in case
@@ -694,40 +663,35 @@ static int xpcs_config_aneg_c37_sgmii(struct dw_xpcs *xpcs,
 			return ret;
 	}
 
-	ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_CTRL);
-	if (ret < 0)
-		return ret;
+	mask = DW_VR_MII_PCS_MODE_MASK | DW_VR_MII_TX_CONFIG_MASK;
+	val = FIELD_PREP(DW_VR_MII_PCS_MODE_MASK,
+			 DW_VR_MII_PCS_MODE_C37_SGMII);
 
-	ret &= ~(DW_VR_MII_PCS_MODE_MASK | DW_VR_MII_TX_CONFIG_MASK);
-	ret |= (DW_VR_MII_PCS_MODE_C37_SGMII <<
-		DW_VR_MII_AN_CTRL_PCS_MODE_SHIFT &
-		DW_VR_MII_PCS_MODE_MASK);
 	if (xpcs->info.pma == WX_TXGBE_XPCS_PMA_10G_ID) {
-		ret |= DW_VR_MII_AN_CTRL_8BIT;
+		mask |= DW_VR_MII_AN_CTRL_8BIT;
+		val |= DW_VR_MII_AN_CTRL_8BIT;
 		/* Hardware requires it to be PHY side SGMII */
 		tx_conf = DW_VR_MII_TX_CONFIG_PHY_SIDE_SGMII;
 	} else {
 		tx_conf = DW_VR_MII_TX_CONFIG_MAC_SIDE_SGMII;
 	}
-	ret |= tx_conf << DW_VR_MII_AN_CTRL_TX_CONFIG_SHIFT &
-		DW_VR_MII_TX_CONFIG_MASK;
-	ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_CTRL, ret);
-	if (ret < 0)
-		return ret;
 
-	ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL1);
+	val |= FIELD_PREP(DW_VR_MII_TX_CONFIG_MASK, tx_conf);
+
+	ret = xpcs_modify(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_CTRL, mask, val);
 	if (ret < 0)
 		return ret;
 
+	mask = DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW;
 	if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED)
-		ret |= DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW;
-	else
-		ret &= ~DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW;
+		val = DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW;
 
-	if (xpcs->info.pma == WX_TXGBE_XPCS_PMA_10G_ID)
-		ret |= DW_VR_MII_DIG_CTRL1_PHY_MODE_CTRL;
+	if (xpcs->info.pma == WX_TXGBE_XPCS_PMA_10G_ID) {
+		mask |= DW_VR_MII_DIG_CTRL1_PHY_MODE_CTRL;
+		val |= DW_VR_MII_DIG_CTRL1_PHY_MODE_CTRL;
+	}
 
-	ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL1, ret);
+	ret = xpcs_modify(xpcs, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL1, mask, val);
 	if (ret < 0)
 		return ret;
 
@@ -745,9 +709,7 @@ static int xpcs_config_aneg_c37_1000basex(struct dw_xpcs *xpcs,
 	phy_interface_t interface = PHY_INTERFACE_MODE_1000BASEX;
 	int ret, mdio_ctrl, adv;
 	bool changed = 0;
-
-	if (xpcs->info.pma == WX_TXGBE_XPCS_PMA_10G_ID)
-		xpcs_write_vpcs(xpcs, DW_VR_XS_PCS_DIG_CTRL1, DW_CL37_BP | DW_EN_VSMMD1);
+	u16 mask, val;
 
 	/* According to Chap 7.12, to set 1000BASE-X C37 AN, AN must
 	 * be disabled first:-
@@ -765,14 +727,16 @@ static int xpcs_config_aneg_c37_1000basex(struct dw_xpcs *xpcs,
 			return ret;
 	}
 
-	ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_CTRL);
-	if (ret < 0)
-		return ret;
+	mask = DW_VR_MII_PCS_MODE_MASK;
+	val = FIELD_PREP(DW_VR_MII_PCS_MODE_MASK,
+			 DW_VR_MII_PCS_MODE_C37_1000BASEX);
+
+	if (!xpcs->pcs.poll) {
+		mask |= DW_VR_MII_AN_INTR_EN;
+		val |= DW_VR_MII_AN_INTR_EN;
+	}
 
-	ret &= ~DW_VR_MII_PCS_MODE_MASK;
-	if (!xpcs->pcs.poll)
-		ret |= DW_VR_MII_AN_INTR_EN;
-	ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_CTRL, ret);
+	ret = xpcs_modify(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_CTRL, mask, val);
 	if (ret < 0)
 		return ret;
 
@@ -809,31 +773,26 @@ static int xpcs_config_2500basex(struct dw_xpcs *xpcs)
 {
 	int ret;
 
-	ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL1);
-	if (ret < 0)
-		return ret;
-	ret |= DW_VR_MII_DIG_CTRL1_2G5_EN;
-	ret &= ~DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW;
-	ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL1, ret);
+	ret = xpcs_modify(xpcs, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL1,
+			  DW_VR_MII_DIG_CTRL1_2G5_EN |
+			  DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW,
+			  DW_VR_MII_DIG_CTRL1_2G5_EN);
 	if (ret < 0)
 		return ret;
 
-	ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_MMD_CTRL);
-	if (ret < 0)
-		return ret;
-	ret &= ~AN_CL37_EN;
-	ret |= SGMII_SPEED_SS6;
-	ret &= ~SGMII_SPEED_SS13;
-	return xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_MMD_CTRL, ret);
+	return xpcs_modify(xpcs, MDIO_MMD_VEND2, DW_VR_MII_MMD_CTRL,
+			   AN_CL37_EN | SGMII_SPEED_SS6 | SGMII_SPEED_SS13,
+			   SGMII_SPEED_SS6);
 }
 
-int xpcs_do_config(struct dw_xpcs *xpcs, phy_interface_t interface,
-		   const unsigned long *advertising, unsigned int neg_mode)
+static int xpcs_do_config(struct dw_xpcs *xpcs, phy_interface_t interface,
+			  const unsigned long *advertising,
+			  unsigned int neg_mode)
 {
 	const struct dw_xpcs_compat *compat;
 	int ret;
 
-	compat = xpcs_find_compat(xpcs->desc, interface);
+	compat = xpcs_find_compat(xpcs, interface);
 	if (!compat)
 		return -ENODEV;
 
@@ -841,6 +800,14 @@ int xpcs_do_config(struct dw_xpcs *xpcs, phy_interface_t interface,
 		ret = txgbe_xpcs_switch_mode(xpcs, interface);
 		if (ret)
 			return ret;
+
+		/* Wangxun devices need backplane CL37 AN enabled for
+		 * SGMII and 1000base-X
+		 */
+		if (interface == PHY_INTERFACE_MODE_SGMII ||
+		    interface == PHY_INTERFACE_MODE_1000BASEX)
+			xpcs_write_vpcs(xpcs, DW_VR_XS_PCS_DIG_CTRL1,
+					DW_CL37_BP | DW_EN_VSMMD1);
 	}
 
 	switch (compat->an_mode) {
@@ -881,7 +848,6 @@ int xpcs_do_config(struct dw_xpcs *xpcs, phy_interface_t interface,
 
 	return 0;
 }
-EXPORT_SYMBOL_GPL(xpcs_do_config);
 
 static int xpcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
 		       phy_interface_t interface,
@@ -989,8 +955,7 @@ static int xpcs_get_state_c37_sgmii(struct dw_xpcs *xpcs,
 
 		state->link = true;
 
-		speed_value = (ret & DW_VR_MII_AN_STS_C37_ANSGM_SP) >>
-			      DW_VR_MII_AN_STS_C37_ANSGM_SP_SHIFT;
+		speed_value = FIELD_GET(DW_VR_MII_AN_STS_C37_ANSGM_SP, ret);
 		if (speed_value == DW_VR_MII_C37_ANSGM_SP_1000)
 			state->speed = SPEED_1000;
 		else if (speed_value == DW_VR_MII_C37_ANSGM_SP_100)
@@ -1098,7 +1063,7 @@ static void xpcs_get_state(struct phylink_pcs *pcs,
 	const struct dw_xpcs_compat *compat;
 	int ret;
 
-	compat = xpcs_find_compat(xpcs->desc, state->interface);
+	compat = xpcs_find_compat(xpcs, state->interface);
 	if (!compat)
 		return;
 
@@ -1108,32 +1073,27 @@ static void xpcs_get_state(struct phylink_pcs *pcs,
 		break;
 	case DW_AN_C73:
 		ret = xpcs_get_state_c73(xpcs, state, compat);
-		if (ret) {
-			pr_err("xpcs_get_state_c73 returned %pe\n",
-			       ERR_PTR(ret));
-			return;
-		}
+		if (ret)
+			dev_err(&xpcs->mdiodev->dev, "%s returned %pe\n",
+				"xpcs_get_state_c73", ERR_PTR(ret));
 		break;
 	case DW_AN_C37_SGMII:
 		ret = xpcs_get_state_c37_sgmii(xpcs, state);
-		if (ret) {
-			pr_err("xpcs_get_state_c37_sgmii returned %pe\n",
-			       ERR_PTR(ret));
-		}
+		if (ret)
+			dev_err(&xpcs->mdiodev->dev, "%s returned %pe\n",
+				"xpcs_get_state_c37_sgmii", ERR_PTR(ret));
 		break;
 	case DW_AN_C37_1000BASEX:
 		ret = xpcs_get_state_c37_1000basex(xpcs, state);
-		if (ret) {
-			pr_err("xpcs_get_state_c37_1000basex returned %pe\n",
-			       ERR_PTR(ret));
-		}
+		if (ret)
+			dev_err(&xpcs->mdiodev->dev, "%s returned %pe\n",
+				"xpcs_get_state_c37_1000basex", ERR_PTR(ret));
 		break;
 	case DW_2500BASEX:
 		ret = xpcs_get_state_2500basex(xpcs, state);
-		if (ret) {
-			pr_err("xpcs_get_state_2500basex returned %pe\n",
-			       ERR_PTR(ret));
-		}
+		if (ret)
+			dev_err(&xpcs->mdiodev->dev, "%s returned %pe\n",
+				"xpcs_get_state_2500basex", ERR_PTR(ret));
 		break;
 	default:
 		return;
@@ -1151,7 +1111,8 @@ static void xpcs_link_up_sgmii(struct dw_xpcs *xpcs, unsigned int neg_mode,
 	val = mii_bmcr_encode_fixed(speed, duplex);
 	ret = xpcs_write(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1, val);
 	if (ret)
-		pr_err("%s: xpcs_write returned %pe\n", __func__, ERR_PTR(ret));
+		dev_err(&xpcs->mdiodev->dev, "%s: xpcs_write returned %pe\n",
+			__func__, ERR_PTR(ret));
 }
 
 static void xpcs_link_up_1000basex(struct dw_xpcs *xpcs, unsigned int neg_mode,
@@ -1169,22 +1130,25 @@ static void xpcs_link_up_1000basex(struct dw_xpcs *xpcs, unsigned int neg_mode,
 	case SPEED_100:
 	case SPEED_10:
 	default:
-		pr_err("%s: speed = %d\n", __func__, speed);
+		dev_err(&xpcs->mdiodev->dev, "%s: speed = %d\n",
+			__func__, speed);
 		return;
 	}
 
 	if (duplex == DUPLEX_FULL)
 		val |= BMCR_FULLDPLX;
 	else
-		pr_err("%s: half duplex not supported\n", __func__);
+		dev_err(&xpcs->mdiodev->dev, "%s: half duplex not supported\n",
+			__func__);
 
 	ret = xpcs_write(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1, val);
 	if (ret)
-		pr_err("%s: xpcs_write returned %pe\n", __func__, ERR_PTR(ret));
+		dev_err(&xpcs->mdiodev->dev, "%s: xpcs_write returned %pe\n",
+			__func__, ERR_PTR(ret));
 }
 
-void xpcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode,
-		  phy_interface_t interface, int speed, int duplex)
+static void xpcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode,
+			 phy_interface_t interface, int speed, int duplex)
 {
 	struct dw_xpcs *xpcs = phylink_pcs_to_xpcs(pcs);
 
@@ -1195,21 +1159,16 @@ void xpcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode,
 	if (interface == PHY_INTERFACE_MODE_1000BASEX)
 		return xpcs_link_up_1000basex(xpcs, neg_mode, speed, duplex);
 }
-EXPORT_SYMBOL_GPL(xpcs_link_up);
 
 static void xpcs_an_restart(struct phylink_pcs *pcs)
 {
 	struct dw_xpcs *xpcs = phylink_pcs_to_xpcs(pcs);
-	int ret;
 
-	ret = xpcs_read(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1);
-	if (ret >= 0) {
-		ret |= BMCR_ANRESTART;
-		xpcs_write(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1, ret);
-	}
+	xpcs_modify(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1, BMCR_ANRESTART,
+		    BMCR_ANRESTART);
 }
 
-static int xpcs_get_id(struct dw_xpcs *xpcs)
+static int xpcs_read_ids(struct dw_xpcs *xpcs)
 {
 	int ret;
 	u32 id;
@@ -1275,76 +1234,62 @@ static int xpcs_get_id(struct dw_xpcs *xpcs)
 	return 0;
 }
 
-static const struct dw_xpcs_compat synopsys_xpcs_compat[DW_XPCS_INTERFACE_MAX] = {
-	[DW_XPCS_USXGMII] = {
+static const struct dw_xpcs_compat synopsys_xpcs_compat[] = {
+	{
+		.interface = PHY_INTERFACE_MODE_USXGMII,
 		.supported = xpcs_usxgmii_features,
-		.interface = xpcs_usxgmii_interfaces,
-		.num_interfaces = ARRAY_SIZE(xpcs_usxgmii_interfaces),
 		.an_mode = DW_AN_C73,
-	},
-	[DW_XPCS_10GKR] = {
+	}, {
+		.interface = PHY_INTERFACE_MODE_10GKR,
 		.supported = xpcs_10gkr_features,
-		.interface = xpcs_10gkr_interfaces,
-		.num_interfaces = ARRAY_SIZE(xpcs_10gkr_interfaces),
 		.an_mode = DW_AN_C73,
-	},
-	[DW_XPCS_XLGMII] = {
+	}, {
+		.interface = PHY_INTERFACE_MODE_XLGMII,
 		.supported = xpcs_xlgmii_features,
-		.interface = xpcs_xlgmii_interfaces,
-		.num_interfaces = ARRAY_SIZE(xpcs_xlgmii_interfaces),
 		.an_mode = DW_AN_C73,
-	},
-	[DW_XPCS_10GBASER] = {
+	}, {
+		.interface = PHY_INTERFACE_MODE_10GBASER,
 		.supported = xpcs_10gbaser_features,
-		.interface = xpcs_10gbaser_interfaces,
-		.num_interfaces = ARRAY_SIZE(xpcs_10gbaser_interfaces),
 		.an_mode = DW_10GBASER,
-	},
-	[DW_XPCS_SGMII] = {
+	}, {
+		.interface = PHY_INTERFACE_MODE_SGMII,
 		.supported = xpcs_sgmii_features,
-		.interface = xpcs_sgmii_interfaces,
-		.num_interfaces = ARRAY_SIZE(xpcs_sgmii_interfaces),
 		.an_mode = DW_AN_C37_SGMII,
-	},
-	[DW_XPCS_1000BASEX] = {
+	}, {
+		.interface = PHY_INTERFACE_MODE_1000BASEX,
 		.supported = xpcs_1000basex_features,
-		.interface = xpcs_1000basex_interfaces,
-		.num_interfaces = ARRAY_SIZE(xpcs_1000basex_interfaces),
 		.an_mode = DW_AN_C37_1000BASEX,
-	},
-	[DW_XPCS_2500BASEX] = {
+	}, {
+		.interface = PHY_INTERFACE_MODE_2500BASEX,
 		.supported = xpcs_2500basex_features,
-		.interface = xpcs_2500basex_interfaces,
-		.num_interfaces = ARRAY_SIZE(xpcs_2500basex_interfaces),
 		.an_mode = DW_2500BASEX,
-	},
+	}, {
+	}
 };
 
-static const struct dw_xpcs_compat nxp_sja1105_xpcs_compat[DW_XPCS_INTERFACE_MAX] = {
-	[DW_XPCS_SGMII] = {
+static const struct dw_xpcs_compat nxp_sja1105_xpcs_compat[] = {
+	{
+		.interface = PHY_INTERFACE_MODE_SGMII,
 		.supported = xpcs_sgmii_features,
-		.interface = xpcs_sgmii_interfaces,
-		.num_interfaces = ARRAY_SIZE(xpcs_sgmii_interfaces),
 		.an_mode = DW_AN_C37_SGMII,
 		.pma_config = nxp_sja1105_sgmii_pma_config,
-	},
+	}, {
+	}
 };
 
-static const struct dw_xpcs_compat nxp_sja1110_xpcs_compat[DW_XPCS_INTERFACE_MAX] = {
-	[DW_XPCS_SGMII] = {
+static const struct dw_xpcs_compat nxp_sja1110_xpcs_compat[] = {
+	{
+		.interface = PHY_INTERFACE_MODE_SGMII,
 		.supported = xpcs_sgmii_features,
-		.interface = xpcs_sgmii_interfaces,
-		.num_interfaces = ARRAY_SIZE(xpcs_sgmii_interfaces),
 		.an_mode = DW_AN_C37_SGMII,
 		.pma_config = nxp_sja1110_sgmii_pma_config,
-	},
-	[DW_XPCS_2500BASEX] = {
+	}, {
+		.interface = PHY_INTERFACE_MODE_2500BASEX,
 		.supported = xpcs_2500basex_features,
-		.interface = xpcs_2500basex_interfaces,
-		.num_interfaces = ARRAY_SIZE(xpcs_2500basex_interfaces),
 		.an_mode = DW_2500BASEX,
 		.pma_config = nxp_sja1110_2500basex_pma_config,
-	},
+	}, {
+	}
 };
 
 static const struct dw_xpcs_desc xpcs_desc_list[] = {
@@ -1365,12 +1310,33 @@ static const struct dw_xpcs_desc xpcs_desc_list[] = {
 
 static const struct phylink_pcs_ops xpcs_phylink_ops = {
 	.pcs_validate = xpcs_validate,
+	.pcs_pre_config = xpcs_pre_config,
 	.pcs_config = xpcs_config,
 	.pcs_get_state = xpcs_get_state,
 	.pcs_an_restart = xpcs_an_restart,
 	.pcs_link_up = xpcs_link_up,
 };
 
+static int xpcs_identify(struct dw_xpcs *xpcs)
+{
+	int i, ret;
+
+	ret = xpcs_read_ids(xpcs);
+	if (ret < 0)
+		return ret;
+
+	for (i = 0; i < ARRAY_SIZE(xpcs_desc_list); i++) {
+		const struct dw_xpcs_desc *entry = &xpcs_desc_list[i];
+
+		if ((xpcs->info.pcs & entry->mask) == entry->id) {
+			xpcs->desc = entry;
+			return 0;
+		}
+	}
+
+	return -ENODEV;
+}
+
 static struct dw_xpcs *xpcs_create_data(struct mdio_device *mdiodev)
 {
 	struct dw_xpcs *xpcs;
@@ -1427,7 +1393,6 @@ static void xpcs_clear_clks(struct dw_xpcs *xpcs)
 static int xpcs_init_id(struct dw_xpcs *xpcs)
 {
 	const struct dw_xpcs_info *info;
-	int i, ret;
 
 	info = dev_get_platdata(&xpcs->mdiodev->dev);
 	if (!info) {
@@ -1437,45 +1402,10 @@ static int xpcs_init_id(struct dw_xpcs *xpcs)
 		xpcs->info = *info;
 	}
 
-	ret = xpcs_get_id(xpcs);
-	if (ret < 0)
-		return ret;
-
-	for (i = 0; i < ARRAY_SIZE(xpcs_desc_list); i++) {
-		const struct dw_xpcs_desc *desc = &xpcs_desc_list[i];
-
-		if ((xpcs->info.pcs & desc->mask) != desc->id)
-			continue;
-
-		xpcs->desc = desc;
-
-		break;
-	}
-
-	if (!xpcs->desc)
-		return -ENODEV;
-
-	return 0;
-}
-
-static int xpcs_init_iface(struct dw_xpcs *xpcs, phy_interface_t interface)
-{
-	const struct dw_xpcs_compat *compat;
-
-	compat = xpcs_find_compat(xpcs->desc, interface);
-	if (!compat)
-		return -EINVAL;
-
-	if (xpcs->info.pma == WX_TXGBE_XPCS_PMA_10G_ID) {
-		xpcs->pcs.poll = false;
-		return 0;
-	}
-
-	return xpcs_soft_reset(xpcs, compat);
+	return xpcs_identify(xpcs);
 }
 
-static struct dw_xpcs *xpcs_create(struct mdio_device *mdiodev,
-				   phy_interface_t interface)
+static struct dw_xpcs *xpcs_create(struct mdio_device *mdiodev)
 {
 	struct dw_xpcs *xpcs;
 	int ret;
@@ -1492,9 +1422,10 @@ static struct dw_xpcs *xpcs_create(struct mdio_device *mdiodev,
 	if (ret)
 		goto out_clear_clks;
 
-	ret = xpcs_init_iface(xpcs, interface);
-	if (ret)
-		goto out_clear_clks;
+	if (xpcs->info.pma == WX_TXGBE_XPCS_PMA_10G_ID)
+		xpcs->pcs.poll = false;
+	else
+		xpcs->need_reset = true;
 
 	return xpcs;
 
@@ -1511,14 +1442,12 @@ static struct dw_xpcs *xpcs_create(struct mdio_device *mdiodev,
  * xpcs_create_mdiodev() - create a DW xPCS instance with the MDIO @addr
  * @bus: pointer to the MDIO-bus descriptor for the device to be looked at
  * @addr: device MDIO-bus ID
- * @interface: requested PHY interface
  *
  * Return: a pointer to the DW XPCS handle if successful, otherwise -ENODEV if
  * the PCS device couldn't be found on the bus and other negative errno related
  * to the data allocation and MDIO-bus communications.
  */
-struct dw_xpcs *xpcs_create_mdiodev(struct mii_bus *bus, int addr,
-				    phy_interface_t interface)
+struct dw_xpcs *xpcs_create_mdiodev(struct mii_bus *bus, int addr)
 {
 	struct mdio_device *mdiodev;
 	struct dw_xpcs *xpcs;
@@ -1527,7 +1456,7 @@ struct dw_xpcs *xpcs_create_mdiodev(struct mii_bus *bus, int addr,
 	if (IS_ERR(mdiodev))
 		return ERR_CAST(mdiodev);
 
-	xpcs = xpcs_create(mdiodev, interface);
+	xpcs = xpcs_create(mdiodev);
 
 	/* xpcs_create() has taken a refcount on the mdiodev if it was
 	 * successful. If xpcs_create() fails, this will free the mdio
@@ -1541,10 +1470,21 @@ struct dw_xpcs *xpcs_create_mdiodev(struct mii_bus *bus, int addr,
 }
 EXPORT_SYMBOL_GPL(xpcs_create_mdiodev);
 
+struct phylink_pcs *xpcs_create_pcs_mdiodev(struct mii_bus *bus, int addr)
+{
+	struct dw_xpcs *xpcs;
+
+	xpcs = xpcs_create_mdiodev(bus, addr);
+	if (IS_ERR(xpcs))
+		return ERR_CAST(xpcs);
+
+	return &xpcs->pcs;
+}
+EXPORT_SYMBOL_GPL(xpcs_create_pcs_mdiodev);
+
 /**
  * xpcs_create_fwnode() - Create a DW xPCS instance from @fwnode
  * @fwnode: fwnode handle poining to the DW XPCS device
- * @interface: requested PHY interface
  *
  * Return: a pointer to the DW XPCS handle if successful, otherwise -ENODEV if
  * the fwnode device is unavailable or the PCS device couldn't be found on the
@@ -1552,8 +1492,7 @@ EXPORT_SYMBOL_GPL(xpcs_create_mdiodev);
  * other negative errno related to the data allocations and MDIO-bus
  * communications.
  */
-struct dw_xpcs *xpcs_create_fwnode(struct fwnode_handle *fwnode,
-				   phy_interface_t interface)
+struct dw_xpcs *xpcs_create_fwnode(struct fwnode_handle *fwnode)
 {
 	struct mdio_device *mdiodev;
 	struct dw_xpcs *xpcs;
@@ -1565,7 +1504,7 @@ struct dw_xpcs *xpcs_create_fwnode(struct fwnode_handle *fwnode,
 	if (!mdiodev)
 		return ERR_PTR(-EPROBE_DEFER);
 
-	xpcs = xpcs_create(mdiodev, interface);
+	xpcs = xpcs_create(mdiodev);
 
 	/* xpcs_create() has taken a refcount on the mdiodev if it was
 	 * successful. If xpcs_create() fails, this will free the mdio
@@ -1590,5 +1529,11 @@ void xpcs_destroy(struct dw_xpcs *xpcs)
 }
 EXPORT_SYMBOL_GPL(xpcs_destroy);
 
+void xpcs_destroy_pcs(struct phylink_pcs *pcs)
+{
+	xpcs_destroy(phylink_pcs_to_xpcs(pcs));
+}
+EXPORT_SYMBOL_GPL(xpcs_destroy_pcs);
+
 MODULE_DESCRIPTION("Synopsys DesignWare XPCS library");
 MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/pcs/pcs-xpcs.h b/drivers/net/pcs/pcs-xpcs.h
index fa05adfae220..9a22eed4404d 100644
--- a/drivers/net/pcs/pcs-xpcs.h
+++ b/drivers/net/pcs/pcs-xpcs.h
@@ -60,8 +60,6 @@
 #define DW_VR_MII_DIG_CTRL1		0x8000
 #define DW_VR_MII_AN_CTRL		0x8001
 #define DW_VR_MII_AN_INTR_STS		0x8002
-/* Enable 2.5G Mode */
-#define DW_VR_MII_DIG_CTRL1_2G5_EN	BIT(2)
 /* EEE Mode Control Register */
 #define DW_VR_MII_EEE_MCTRL0		0x8006
 #define DW_VR_MII_EEE_MCTRL1		0x800b
@@ -69,6 +67,7 @@
 
 /* VR_MII_DIG_CTRL1 */
 #define DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW		BIT(9)
+#define DW_VR_MII_DIG_CTRL1_2G5_EN		BIT(2)
 #define DW_VR_MII_DIG_CTRL1_PHY_MODE_CTRL	BIT(0)
 
 /* VR_MII_DIG_CTRL2 */
@@ -77,11 +76,9 @@
 
 /* VR_MII_AN_CTRL */
 #define DW_VR_MII_AN_CTRL_8BIT			BIT(8)
-#define DW_VR_MII_AN_CTRL_TX_CONFIG_SHIFT	3
 #define DW_VR_MII_TX_CONFIG_MASK		BIT(3)
 #define DW_VR_MII_TX_CONFIG_PHY_SIDE_SGMII	0x1
 #define DW_VR_MII_TX_CONFIG_MAC_SIDE_SGMII	0x0
-#define DW_VR_MII_AN_CTRL_PCS_MODE_SHIFT	1
 #define DW_VR_MII_PCS_MODE_MASK			GENMASK(2, 1)
 #define DW_VR_MII_PCS_MODE_C37_1000BASEX	0x0
 #define DW_VR_MII_PCS_MODE_C37_SGMII		0x2
@@ -90,7 +87,6 @@
 /* VR_MII_AN_INTR_STS */
 #define DW_VR_MII_AN_STS_C37_ANCMPLT_INTR	BIT(0)
 #define DW_VR_MII_AN_STS_C37_ANSGM_FD		BIT(1)
-#define DW_VR_MII_AN_STS_C37_ANSGM_SP_SHIFT	2
 #define DW_VR_MII_AN_STS_C37_ANSGM_SP		GENMASK(3, 2)
 #define DW_VR_MII_C37_ANSGM_SP_10		0x0
 #define DW_VR_MII_C37_ANSGM_SP_100		0x1
@@ -114,7 +110,6 @@
 #define DW_VR_MII_EEE_TX_EN_CTRL		BIT(4)  /* Tx Control Enable */
 #define DW_VR_MII_EEE_RX_EN_CTRL		BIT(7)  /* Rx Control Enable */
 
-#define DW_VR_MII_EEE_MULT_FACT_100NS_SHIFT	8
 #define DW_VR_MII_EEE_MULT_FACT_100NS		GENMASK(11, 8)
 
 /* VR MII EEE Control 1 defines */
@@ -123,8 +118,27 @@
 #define DW_XPCS_INFO_DECLARE(_name, _pcs, _pma)				\
 	static const struct dw_xpcs_info _name = { .pcs = _pcs, .pma = _pma }
 
+struct dw_xpcs_desc;
+
+enum dw_xpcs_clock {
+	DW_XPCS_CORE_CLK,
+	DW_XPCS_PAD_CLK,
+	DW_XPCS_NUM_CLKS,
+};
+
+struct dw_xpcs {
+	struct dw_xpcs_info info;
+	const struct dw_xpcs_desc *desc;
+	struct mdio_device *mdiodev;
+	struct clk_bulk_data clks[DW_XPCS_NUM_CLKS];
+	struct phylink_pcs pcs;
+	phy_interface_t interface;
+	bool need_reset;
+};
+
 int xpcs_read(struct dw_xpcs *xpcs, int dev, u32 reg);
 int xpcs_write(struct dw_xpcs *xpcs, int dev, u32 reg, u16 val);
+int xpcs_modify(struct dw_xpcs *xpcs, int dev, u32 reg, u16 mask, u16 set);
 int xpcs_read_vpcs(struct dw_xpcs *xpcs, int reg);
 int xpcs_write_vpcs(struct dw_xpcs *xpcs, int reg, u16 val);
 int nxp_sja1105_sgmii_pma_config(struct dw_xpcs *xpcs);
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 7fddc8306d82..784eef64849e 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -70,7 +70,7 @@ config RUST_PHYLIB_ABSTRACTIONS
 
 config SFP
 	tristate "SFP cage support"
-	depends on I2C && PHYLINK
+	depends on I2C
 	depends on HWMON || HWMON=n
 	select MDIO_I2C
 
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index 202ed7f450da..217e205d56f4 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -28,7 +28,7 @@ obj-$(CONFIG_PHYLIB)		+= libphy.o
 
 obj-$(CONFIG_NETWORK_PHY_TIMESTAMPING) += mii_timestamper.o
 
-obj-$(CONFIG_SFP)		+= sfp.o
+obj-$(CONFIG_SFP)		+= sff.o sfp.o
 sfp-obj-$(CONFIG_SFP)		+= sfp-bus.o
 obj-y				+= $(sfp-obj-y) $(sfp-obj-m)
 
diff --git a/drivers/net/phy/aquantia/aquantia_main.c b/drivers/net/phy/aquantia/aquantia_main.c
index e982e9ce44a5..15021e36e468 100644
--- a/drivers/net/phy/aquantia/aquantia_main.c
+++ b/drivers/net/phy/aquantia/aquantia_main.c
@@ -767,6 +767,19 @@ static int aqr111_config_init(struct phy_device *phydev)
 	return aqr107_config_init(phydev);
 }
 
+static int aqr113c_probe(struct phy_device *phydev)
+{
+	unsigned long *supported = phydev->supported_interfaces;
+
+	__set_bit(PHY_INTERFACE_MODE_USXGMII, supported);
+	__set_bit(PHY_INTERFACE_MODE_10GBASER, supported);
+	__set_bit(PHY_INTERFACE_MODE_5GBASER, supported);
+	__set_bit(PHY_INTERFACE_MODE_2500BASEX, supported);
+	__set_bit(PHY_INTERFACE_MODE_SGMII, supported);
+
+	return aqr107_probe(phydev);
+}
+
 static struct phy_driver aqr_driver[] = {
 {
 	PHY_ID_MATCH_MODEL(PHY_ID_AQ1202),
@@ -974,7 +987,7 @@ static struct phy_driver aqr_driver[] = {
 {
 	PHY_ID_MATCH_MODEL(PHY_ID_AQR113C),
 	.name           = "Aquantia AQR113C",
-	.probe          = aqr107_probe,
+	.probe          = aqr113c_probe,
 	.get_rate_matching = aqr107_get_rate_matching,
 	.config_init    = aqr113c_config_init,
 	.config_aneg    = aqr_config_aneg,
diff --git a/drivers/net/phy/bcm84881.c b/drivers/net/phy/bcm84881.c
index f1d47c264058..95c239474b43 100644
--- a/drivers/net/phy/bcm84881.c
+++ b/drivers/net/phy/bcm84881.c
@@ -63,6 +63,10 @@ static int bcm84881_probe(struct phy_device *phydev)
 	    (phydev->c45_ids.devices_in_package & mmd_mask) != mmd_mask)
 		return -ENODEV;
 
+	__set_bit(PHY_INTERFACE_MODE_SGMII, phydev->supported_interfaces);
+	__set_bit(PHY_INTERFACE_MODE_2500BASEX, phydev->supported_interfaces);
+	__set_bit(PHY_INTERFACE_MODE_10GBASER, phydev->supported_interfaces);
+
 	return 0;
 }
 
@@ -235,11 +239,21 @@ static int bcm84881_read_status(struct phy_device *phydev)
 	return genphy_c45_read_mdix(phydev);
 }
 
+/* The Broadcom BCM84881 in the Methode DM7052 is unable to provide a SGMII
+ * or 802.3z control word, so inband will not work.
+ */
+static unsigned int bcm84881_query_inband(struct phy_device *phydev,
+					  phy_interface_t interface)
+{
+	return LINK_INBAND_DISABLE;
+}
+
 static struct phy_driver bcm84881_drivers[] = {
 	{
 		.phy_id		= 0xae025150,
 		.phy_id_mask	= 0xfffffff0,
 		.name		= "Broadcom BCM84881",
+		.query_inband	= bcm84881_query_inband,
 		.config_init	= bcm84881_config_init,
 		.probe		= bcm84881_probe,
 		.get_features	= bcm84881_get_features,
diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c
index b89fbffa6a93..9a24752382d5 100644
--- a/drivers/net/phy/marvell.c
+++ b/drivers/net/phy/marvell.c
@@ -176,6 +176,10 @@
 #define MII_M1011_PHY_STATUS_FULLDUPLEX	0x2000
 #define MII_M1011_PHY_STATUS_RESOLVED	0x0800
 #define MII_M1011_PHY_STATUS_LINK	0x0400
+#define MII_M1111_PHY_STATUS_TX_PAUSE	0x0008
+#define MII_M1111_PHY_STATUS_RX_PAUSE	0x0004
+#define MII_88E151X_PHY_STATUS_TX_PAUSE	0x0200
+#define MII_88E151X_PHY_STATUS_RX_PAUSE	0x0100
 
 #define MII_88E3016_PHY_SPEC_CTRL	0x10
 #define MII_88E3016_DISABLE_SCRAMBLER	0x0200
@@ -353,6 +357,8 @@ struct marvell_priv {
 	u32 step;
 	s8 pair;
 	u8 vct_phase;
+	u16 tx_pause_mask;
+	u16 rx_pause_mask;
 };
 
 static int marvell_read_page(struct phy_device *phydev)
@@ -716,6 +722,19 @@ static int marvell_config_aneg_fiber(struct phy_device *phydev)
 	return genphy_check_and_restart_aneg(phydev, changed);
 }
 
+static unsigned int m88e1111_query_inband(struct phy_device *phydev,
+					  phy_interface_t interface)
+{
+	/* In 1000base-X and SGMII modes, the inband mode can be changed
+	 * through the Fibre page BMCR ANENABLE bit.
+	 */
+	if (interface == PHY_INTERFACE_MODE_1000BASEX ||
+	    interface == PHY_INTERFACE_MODE_SGMII)
+		return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE;
+
+	return 0;
+}
+
 static int m88e1111_config_aneg(struct phy_device *phydev)
 {
 	int extsr = phy_read(phydev, MII_M1111_PHY_EXT_SR);
@@ -1623,6 +1642,7 @@ static void fiber_lpa_mod_linkmode_lpa_t(unsigned long *advertising, u32 lpa)
 static int marvell_read_status_page_an(struct phy_device *phydev,
 				       int fiber, int status)
 {
+	struct marvell_priv *priv = phydev->priv;
 	int lpa;
 	int err;
 
@@ -1678,6 +1698,11 @@ static int marvell_read_status_page_an(struct phy_device *phydev,
 		}
 	}
 
+	phydev->resolved_tx_pause = !!(status & priv->tx_pause_mask);
+	phydev->resolved_rx_pause = !!(status & priv->rx_pause_mask);
+	phydev->resolved_pause_valid = !fiber && priv->tx_pause_mask &&
+				       priv->rx_pause_mask;
+
 	return 0;
 }
 
@@ -1721,6 +1746,7 @@ static int marvell_read_status_page(struct phy_device *phydev, int page)
 	phydev->speed = SPEED_UNKNOWN;
 	phydev->duplex = DUPLEX_UNKNOWN;
 	phydev->port = fiber ? PORT_FIBRE : PORT_TP;
+	phydev->resolved_pause_valid = false;
 
 	if (phydev->autoneg == AUTONEG_ENABLE)
 		err = marvell_read_status_page_an(phydev, fiber, status);
@@ -3518,14 +3544,26 @@ static int m88e1318_led_hw_control_get(struct phy_device *phydev, u8 index,
 	return marvell_get_led_rules(index, rules, mode);
 }
 
-static int marvell_probe(struct phy_device *phydev)
+static int marvell_probe_pause(struct phy_device *phydev, u16 tx_pause_mask,
+			       u16 rx_pause_mask)
 {
 	struct marvell_priv *priv;
 
+	__set_bit(PHY_INTERFACE_MODE_GMII, phydev->supported_interfaces);
+	__set_bit(PHY_INTERFACE_MODE_SGMII, phydev->supported_interfaces);
+	__set_bit(PHY_INTERFACE_MODE_TBI, phydev->supported_interfaces);
+	__set_bit(PHY_INTERFACE_MODE_RGMII, phydev->supported_interfaces);
+	__set_bit(PHY_INTERFACE_MODE_RGMII_ID, phydev->supported_interfaces);
+	__set_bit(PHY_INTERFACE_MODE_RGMII_RXID, phydev->supported_interfaces);
+	__set_bit(PHY_INTERFACE_MODE_RGMII_TXID, phydev->supported_interfaces);
+	__set_bit(PHY_INTERFACE_MODE_RTBI, phydev->supported_interfaces);
+
 	priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
 	if (!priv)
 		return -ENOMEM;
 
+	priv->tx_pause_mask = tx_pause_mask;
+	priv->rx_pause_mask = rx_pause_mask;
 	phydev->priv = priv;
 
 	return marvell_hwmon_probe(phydev);
@@ -3615,11 +3653,23 @@ static const struct sfp_upstream_ops m88e1510_sfp_ops = {
 	.detach = phy_sfp_detach,
 };
 
+static int marvell_probe(struct phy_device *phydev)
+{
+	return marvell_probe_pause(phydev, 0, 0);
+}
+
+static int m88e1111_probe(struct phy_device *phydev)
+{
+	return marvell_probe_pause(phydev, MII_M1111_PHY_STATUS_TX_PAUSE,
+				   MII_M1111_PHY_STATUS_RX_PAUSE);
+}
+
 static int m88e1510_probe(struct phy_device *phydev)
 {
 	int err;
 
-	err = marvell_probe(phydev);
+	err = marvell_probe_pause(phydev, MII_88E151X_PHY_STATUS_TX_PAUSE,
+				  MII_88E151X_PHY_STATUS_RX_PAUSE);
 	if (err)
 		return err;
 
@@ -3665,6 +3715,7 @@ static struct phy_driver marvell_drivers[] = {
 		.name = "Marvell 88E1112",
 		/* PHY_GBIT_FEATURES */
 		.probe = marvell_probe,
+		.query_inband = m88e1111_query_inband,
 		.config_init = m88e1112_config_init,
 		.config_aneg = marvell_config_aneg,
 		.config_intr = marvell_config_intr,
@@ -3685,7 +3736,8 @@ static struct phy_driver marvell_drivers[] = {
 		.name = "Marvell 88E1111",
 		/* PHY_GBIT_FEATURES */
 		.flags = PHY_POLL_CABLE_TEST,
-		.probe = marvell_probe,
+		.probe = m88e1111_probe,
+		.query_inband = m88e1111_query_inband,
 		.config_init = m88e1111gbe_config_init,
 		.config_aneg = m88e1111_config_aneg,
 		.read_status = marvell_read_status,
@@ -3709,6 +3761,7 @@ static struct phy_driver marvell_drivers[] = {
 		.name = "Marvell 88E1111 (Finisar)",
 		/* PHY_GBIT_FEATURES */
 		.probe = marvell_probe,
+		.query_inband = m88e1111_query_inband,
 		.config_init = m88e1111gbe_config_init,
 		.config_aneg = m88e1111_config_aneg,
 		.read_status = marvell_read_status,
@@ -3911,7 +3964,7 @@ static struct phy_driver marvell_drivers[] = {
 		.driver_data = DEF_MARVELL_HWMON_OPS(m88e1510_hwmon_ops),
 		/* PHY_GBIT_FEATURES */
 		.flags = PHY_POLL_CABLE_TEST,
-		.probe = marvell_probe,
+		.probe = m88e1510_probe,
 		.config_init = marvell_1011gbe_config_init,
 		.config_aneg = m88e1510_config_aneg,
 		.read_status = marvell_read_status,
@@ -3940,7 +3993,7 @@ static struct phy_driver marvell_drivers[] = {
 		.phy_id_mask = MARVELL_PHY_ID_MASK,
 		.name = "Marvell 88E1545",
 		.driver_data = DEF_MARVELL_HWMON_OPS(m88e1510_hwmon_ops),
-		.probe = marvell_probe,
+		.probe = m88e1510_probe,
 		/* PHY_GBIT_FEATURES */
 		.flags = PHY_POLL_CABLE_TEST,
 		.config_init = marvell_1011gbe_config_init,
@@ -4007,7 +4060,7 @@ static struct phy_driver marvell_drivers[] = {
 		.driver_data = DEF_MARVELL_HWMON_OPS(m88e1510_hwmon_ops),
 		/* PHY_GBIT_FEATURES */
 		.flags = PHY_POLL_CABLE_TEST,
-		.probe = marvell_probe,
+		.probe = m88e1510_probe,
 		.config_init = marvell_1011gbe_config_init,
 		.config_aneg = m88e6390_config_aneg,
 		.read_status = marvell_read_status,
diff --git a/drivers/net/phy/marvell10g.c b/drivers/net/phy/marvell10g.c
index ad43e280930c..eb05a578dbd7 100644
--- a/drivers/net/phy/marvell10g.c
+++ b/drivers/net/phy/marvell10g.c
@@ -27,6 +27,7 @@
 #include <linux/delay.h>
 #include <linux/hwmon.h>
 #include <linux/marvell_phy.h>
+#include <linux/of.h>
 #include <linux/phy.h>
 #include <linux/sfp.h>
 #include <linux/netdevice.h>
@@ -82,6 +83,8 @@ enum {
 	MV_PCS_CSSR1_SPD1_10	= 0x0000,
 	MV_PCS_CSSR1_DUPLEX_FULL= BIT(13),
 	MV_PCS_CSSR1_RESOLVED	= BIT(11),
+	MV_PCS_CSSR1_TX_PAUSE	= BIT(9),
+	MV_PCS_CSSR1_RX_PAUSE	= BIT(8),
 	MV_PCS_CSSR1_MDIX	= BIT(6),
 	MV_PCS_CSSR1_SPD2_MASK	= 0x000c,
 	MV_PCS_CSSR1_SPD2_5000	= 0x0008,
@@ -163,14 +166,16 @@ struct mv3310_chip {
 };
 
 struct mv3310_priv {
-	DECLARE_BITMAP(supported_interfaces, PHY_INTERFACE_MODE_MAX);
 	const struct mv3310_mactype *mactype;
 
 	u32 firmware_ver;
 	bool has_downshift;
+	bool firmware_failed;
 
 	struct device *hwmon_dev;
 	char *hwmon_name;
+	u8 num_leds;
+	u16 led_mode[4];
 };
 
 static const struct mv3310_chip *to_mv3310_chip(struct phy_device *phydev)
@@ -506,6 +511,43 @@ static const struct sfp_upstream_ops mv3310_sfp_ops = {
 	.module_insert = mv3310_sfp_insert,
 };
 
+static int mv3310_leds_write(struct phy_device *phydev)
+{
+	struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev);
+	int i, ret;
+
+	for (i = 0; i < priv->num_leds; i++) {
+		ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, 0xf020 + i,
+				    priv->led_mode[i]);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int mv3310_fw_config(struct phy_device *phydev)
+{
+	struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev);
+	struct device_node *node;
+	int ret;
+
+	node = phydev->mdio.dev.of_node;
+	if (!node)
+		return 0;
+
+	ret = of_property_read_variable_u16_array(node, "marvell,led-mode",
+				priv->led_mode, 1, ARRAY_SIZE(priv->led_mode));
+	if (ret == -EINVAL)
+		ret = 0;
+	if (ret < 0)
+		return ret;
+
+	priv->num_leds = ret;
+
+	return 0;
+}
+
 static int mv3310_probe(struct phy_device *phydev)
 {
 	const struct mv3310_chip *chip = to_mv3310_chip(phydev);
@@ -517,6 +559,20 @@ static int mv3310_probe(struct phy_device *phydev)
 	    (phydev->c45_ids.devices_in_package & mmd_mask) != mmd_mask)
 		return -ENODEV;
 
+	priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	dev_set_drvdata(&phydev->mdio.dev, priv);
+
+	ret = mv3310_fw_config(phydev);
+	if (ret < 0)
+		return ret;
+
+	ret = mv3310_leds_write(phydev);
+	if (ret < 0)
+		return ret;
+
 	ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MV_PMA_BOOT);
 	if (ret < 0)
 		return ret;
@@ -524,15 +580,9 @@ static int mv3310_probe(struct phy_device *phydev)
 	if (ret & MV_PMA_BOOT_FATAL) {
 		dev_warn(&phydev->mdio.dev,
 			 "PHY failed to boot firmware, status=%04x\n", ret);
-		return -ENODEV;
+		priv->firmware_failed = true;
 	}
 
-	priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
-	if (!priv)
-		return -ENOMEM;
-
-	dev_set_drvdata(&phydev->mdio.dev, priv);
-
 	ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MV_PMA_FW_VER0);
 	if (ret < 0)
 		return ret;
@@ -561,7 +611,7 @@ static int mv3310_probe(struct phy_device *phydev)
 	if (ret)
 		return ret;
 
-	chip->init_supported_interfaces(priv->supported_interfaces);
+	chip->init_supported_interfaces(phydev->supported_interfaces);
 
 	return phy_sfp_probe(phydev, &mv3310_sfp_ops);
 }
@@ -587,6 +637,19 @@ static int mv3310_resume(struct phy_device *phydev)
 	return mv3310_hwmon_config(phydev, true);
 }
 
+static int mv3310_start(struct phy_device *phydev)
+{
+	struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev);
+
+	if (priv->firmware_failed) {
+		dev_warn(&phydev->mdio.dev,
+			 "PHY firmware failure: PHY not starting");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
 /* Some PHYs in the Alaska family such as the 88X3310 and the 88E2010
  * don't set bit 14 in PMA Extended Abilities (1.11), although they do
  * support 2.5GBASET and 5GBASET. For these models, we can still read their
@@ -826,7 +889,7 @@ static int mv3310_config_init(struct phy_device *phydev)
 	int err, mactype;
 
 	/* Check that the PHY interface type is compatible */
-	if (!test_bit(phydev->interface, priv->supported_interfaces))
+	if (!test_bit(phydev->interface, phydev->supported_interfaces))
 		return -ENODEV;
 
 	phydev->mdix_ctrl = ETH_TP_MDI_AUTO;
@@ -873,7 +936,7 @@ static int mv3310_config_init(struct phy_device *phydev)
 	if (err && err != -EOPNOTSUPP)
 		return err;
 
-	return 0;
+	return mv3310_leds_write(phydev);
 }
 
 static int mv3310_get_features(struct phy_device *phydev)
@@ -1090,6 +1153,10 @@ static int mv3310_read_status_copper(struct phy_device *phydev)
 	phydev->mdix = cssr1 & MV_PCS_CSSR1_MDIX ?
 		       ETH_TP_MDI_X : ETH_TP_MDI;
 
+	phydev->resolved_tx_pause = !!(cssr1 & MV_PCS_CSSR1_TX_PAUSE);
+	phydev->resolved_rx_pause = !!(cssr1 & MV_PCS_CSSR1_RX_PAUSE);
+	phydev->resolved_pause_valid = true;
+
 	if (val & MDIO_AN_STAT1_COMPLETE) {
 		val = genphy_c45_read_lpa(phydev);
 		if (val < 0)
@@ -1432,6 +1499,7 @@ static struct phy_driver mv3310_drivers[] = {
 		.probe		= mv3310_probe,
 		.suspend	= mv3310_suspend,
 		.resume		= mv3310_resume,
+		.start		= mv3310_start,
 		.config_aneg	= mv3310_config_aneg,
 		.aneg_done	= mv3310_aneg_done,
 		.read_status	= mv3310_read_status,
@@ -1469,6 +1537,7 @@ static struct phy_driver mv3310_drivers[] = {
 		.probe		= mv3310_probe,
 		.suspend	= mv3310_suspend,
 		.resume		= mv3310_resume,
+		.start		= mv3310_start,
 		.config_init	= mv3310_config_init,
 		.config_aneg	= mv3310_config_aneg,
 		.aneg_done	= mv3310_aneg_done,
diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c
index 7e2f10182c0c..fada095a778e 100644
--- a/drivers/net/phy/mdio_bus.c
+++ b/drivers/net/phy/mdio_bus.c
@@ -1396,8 +1396,16 @@ static int mdio_bus_match(struct device *dev, const struct device_driver *drv)
 
 static int mdio_uevent(const struct device *dev, struct kobj_uevent_env *env)
 {
+	struct mdio_device *mdio = to_mdio_device(dev);
 	int rc;
 
+	/* Use the device-specific uevent if specified */
+	if (mdio->bus_uevent) {
+		rc = mdio->bus_uevent(mdio, env);
+		if (rc != -ENODEV)
+			return rc;
+	}
+
 	/* Some devices have extra OF data and an OF-style MODALIAS */
 	rc = of_device_uevent_modalias(dev, env);
 	if (rc != -ENODEV)
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index 785182fa5fe0..0ae2b0dbf94f 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -1000,6 +1000,27 @@ static int phy_check_link_status(struct phy_device *phydev)
 	return 0;
 }
 
+/**
+ * phy_query_inband - query which in-band signalling modes are supported
+ * @phydev: a pointer to a &struct phy_device
+ * @interface: the interface mode for the PHY
+ *
+ * Returns zero if it is unknown what in-band signalling is supported by the
+ * PHY (e.g. because the PHY driver doesn't implement the method.) Otherwise,
+ * returns a bit mask of the LINK_INBAND_* values from
+ * &enum link_inband_signalling to describe which inband modes are supported
+ * for this interface mode.
+ */
+unsigned int phy_query_inband(struct phy_device *phydev,
+			      phy_interface_t interface)
+{
+	if (phydev->drv && phydev->drv->query_inband)
+		return phydev->drv->query_inband(phydev, interface);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(phy_query_inband);
+
 /**
  * _phy_start_aneg - start auto-negotiation for this PHY device
  * @phydev: the phy_device struct
@@ -1247,6 +1268,8 @@ void phy_stop_machine(struct phy_device *phydev)
 
 static void phy_process_error(struct phy_device *phydev)
 {
+	phydev_err(phydev, "Error detected, halting PHY\n");
+
 	/* phydev->lock must be held for the state change to be safe */
 	if (!mutex_is_locked(&phydev->lock))
 		phydev_err(phydev, "PHY-device data unsafe context\n");
@@ -1273,7 +1296,6 @@ static void phy_error_precise(struct phy_device *phydev,
  */
 void phy_error(struct phy_device *phydev)
 {
-	WARN_ON(1);
 	phy_process_error(phydev);
 }
 EXPORT_SYMBOL(phy_error);
@@ -1520,6 +1542,10 @@ void phy_stop(struct phy_device *phydev)
 	phy_process_state_change(phydev, old_state);
 
 	state_work = _phy_state_machine(phydev);
+
+	if (phydev->drv->stop)
+		phydev->drv->stop(phydev);
+
 	mutex_unlock(&phydev->lock);
 
 	_phy_state_machine_post_work(phydev, state_work);
@@ -1552,6 +1578,9 @@ void phy_start(struct phy_device *phydev)
 		goto out;
 	}
 
+	if (phydev->drv->start && phydev->drv->start(phydev))
+		goto out;
+
 	if (phydev->sfp_bus)
 		sfp_upstream_start(phydev->sfp_bus);
 
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 6bb2793de0a9..629aca88bbd2 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -642,6 +642,19 @@ static int phy_request_driver_module(struct phy_device *dev, u32 phy_id)
 	return 0;
 }
 
+static int phy_bus_uevent(struct mdio_device *mdiodev,
+			  struct kobj_uevent_env *env)
+{
+	struct phy_device *phydev;
+
+	phydev = container_of(mdiodev, struct phy_device, mdio);
+
+	add_uevent_var(env, "MODALIAS=" MDIO_MODULE_PREFIX MDIO_ID_FMT,
+		       MDIO_ID_ARGS(phydev->phy_id));
+
+	return 0;
+}
+
 struct phy_device *phy_device_create(struct mii_bus *bus, int addr, u32 phy_id,
 				     bool is_c45,
 				     struct phy_c45_device_ids *c45_ids)
@@ -661,6 +674,7 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, u32 phy_id,
 	mdiodev->dev.type = &mdio_bus_phy_type;
 	mdiodev->bus = bus;
 	mdiodev->bus_match = phy_bus_match;
+	mdiodev->bus_uevent = phy_bus_uevent;
 	mdiodev->addr = addr;
 	mdiodev->flags = MDIO_DEVICE_FLAG_PHY;
 	mdiodev->device_free = phy_mdio_device_free;
@@ -3058,6 +3072,12 @@ void phy_get_pause(struct phy_device *phydev, bool *tx_pause, bool *rx_pause)
 		return;
 	}
 
+	if (phydev->resolved_pause_valid) {
+		*tx_pause = phydev->resolved_tx_pause;
+		*rx_pause = phydev->resolved_rx_pause;
+		return;
+	}
+
 	return linkmode_resolve_pause(phydev->advertising,
 				      phydev->lp_advertising,
 				      tx_pause, rx_pause);
diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
index 51c526d227fa..aa54eac2b9a0 100644
--- a/drivers/net/phy/phylink.c
+++ b/drivers/net/phy/phylink.c
@@ -56,7 +56,8 @@ struct phylink {
 	struct phy_device *phydev;
 	phy_interface_t link_interface;	/* PHY_INTERFACE_xxx */
 	u8 cfg_link_an_mode;		/* MLO_AN_xxx */
-	u8 cur_link_an_mode;
+	u8 req_link_an_mode;		/* Requested MLO_AN_xxx mode */
+	u8 act_link_an_mode;		/* Active MLO_AN_xxx mode */
 	u8 link_port;			/* The current non-phy ethtool port */
 	__ETHTOOL_DECLARE_LINK_MODE_MASK(supported);
 
@@ -79,13 +80,15 @@ struct phylink {
 	unsigned int pcs_state;
 
 	bool mac_link_dropped;
-	bool using_mac_select_pcs;
 
 	struct sfp_bus *sfp_bus;
 	bool sfp_may_have_phy;
 	DECLARE_PHY_INTERFACE_MASK(sfp_interfaces);
 	__ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support);
 	u8 sfp_port;
+
+	struct eee_config eee_cfg;
+	bool eee_active;
 };
 
 #define phylink_printk(level, pl, fmt, ...) \
@@ -175,6 +178,24 @@ static const char *phylink_an_mode_str(unsigned int mode)
 	return mode < ARRAY_SIZE(modestr) ? modestr[mode] : "unknown";
 }
 
+static const char *phylink_pcs_mode_str(unsigned int mode)
+{
+	if (!mode)
+		return "none";
+
+	if (mode & PHYLINK_PCS_NEG_OUTBAND)
+		return "outband";
+
+	if (mode & PHYLINK_PCS_NEG_INBAND) {
+		if (mode & PHYLINK_PCS_NEG_ENABLED)
+			return "inband/an-enabled";
+		else
+			return "inband/an-disabled";
+	}
+
+	return "unknown";
+}
+
 static unsigned int phylink_interface_signal_rate(phy_interface_t interface)
 {
 	switch (interface) {
@@ -656,17 +677,15 @@ static int phylink_validate_mac_and_pcs(struct phylink *pl,
 					unsigned long *supported,
 					struct phylink_link_state *state)
 {
+	struct phylink_pcs *pcs = NULL;
 	unsigned long capabilities;
-	struct phylink_pcs *pcs;
 	int ret;
 
 	/* Get the PCS for this interface mode */
-	if (pl->using_mac_select_pcs) {
+	if (pl->mac_ops->mac_select_pcs) {
 		pcs = pl->mac_ops->mac_select_pcs(pl->config, state->interface);
 		if (IS_ERR(pcs))
 			return PTR_ERR(pcs);
-	} else {
-		pcs = pl->pcs;
 	}
 
 	if (pcs) {
@@ -774,8 +793,8 @@ static int phylink_validate(struct phylink *pl, unsigned long *supported,
 static int phylink_parse_fixedlink(struct phylink *pl,
 				   const struct fwnode_handle *fwnode)
 {
+	__ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
 	struct fwnode_handle *fixed_node;
-	bool pause, asym_pause, autoneg;
 	const struct phy_setting *s;
 	struct gpio_desc *desc;
 	u32 speed;
@@ -848,22 +867,15 @@ static int phylink_parse_fixedlink(struct phylink *pl,
 	linkmode_copy(pl->link_config.advertising, pl->supported);
 	phylink_validate(pl, pl->supported, &pl->link_config);
 
-	pause = phylink_test(pl->supported, Pause);
-	asym_pause = phylink_test(pl->supported, Asym_Pause);
-	autoneg = phylink_test(pl->supported, Autoneg);
 	s = phy_lookup_setting(pl->link_config.speed, pl->link_config.duplex,
 			       pl->supported, true);
-	linkmode_zero(pl->supported);
-	phylink_set(pl->supported, MII);
 
-	if (pause)
-		phylink_set(pl->supported, Pause);
+	linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT, mask);
+	linkmode_set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, mask);
+	linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, mask);
+	linkmode_and(pl->supported, pl->supported, mask);
 
-	if (asym_pause)
-		phylink_set(pl->supported, Asym_Pause);
-
-	if (autoneg)
-		phylink_set(pl->supported, Autoneg);
+	phylink_set(pl->supported, MII);
 
 	if (s) {
 		__set_bit(s->bit, pl->supported);
@@ -900,7 +912,7 @@ static int phylink_parse_mode(struct phylink *pl,
 
 	if ((fwnode_property_read_string(fwnode, "managed", &managed) == 0 &&
 	     strcmp(managed, "in-band-status") == 0)) {
-		if (pl->cfg_link_an_mode == MLO_AN_FIXED) {
+		if (phylink_mode_fixed(pl->cfg_link_an_mode)) {
 			phylink_err(pl,
 				    "can't use both fixed-link and in-band-status\n");
 			return -EINVAL;
@@ -909,7 +921,7 @@ static int phylink_parse_mode(struct phylink *pl,
 		pl->cfg_link_an_mode = MLO_AN_INBAND;
 	}
 
-	if (pl->cfg_link_an_mode == MLO_AN_INBAND) {
+	if (phylink_mode_inband(pl->cfg_link_an_mode)) {
 		linkmode_zero(pl->supported);
 		phylink_set(pl->supported, MII);
 		phylink_set(pl->supported, Autoneg);
@@ -988,6 +1000,15 @@ static void phylink_resolve_an_pause(struct phylink_link_state *state)
 	}
 }
 
+static unsigned int phylink_pcs_query_inband(struct phylink_pcs *pcs,
+				    phy_interface_t interface)
+{
+	if (pcs && pcs->ops->pcs_query_inband)
+		return pcs->ops->pcs_query_inband(pcs, interface);
+
+	return 0;
+}
+
 static void phylink_pcs_pre_config(struct phylink_pcs *pcs,
 				   phy_interface_t interface)
 {
@@ -1041,15 +1062,34 @@ static void phylink_pcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode,
 		pcs->ops->pcs_link_up(pcs, neg_mode, interface, speed, duplex);
 }
 
+/* Query inband for a specific interface mode, asking the MAC for the
+ * PCS which will be used to handle the interface mode.
+ */
+static unsigned int phylink_query_inband(struct phylink *pl,
+					 phy_interface_t interface)
+{
+	struct phylink_pcs *pcs;
+
+	if (!pl->using_mac_select_pcs)
+		return 0;
+
+	pcs = pl->mac_ops->mac_select_pcs(pl->config, interface);
+	if (!pcs)
+		return 0;
+
+	return phylink_pcs_query_inband(pcs, interface);
+}
+
 static void phylink_pcs_poll_stop(struct phylink *pl)
 {
-	if (pl->cfg_link_an_mode == MLO_AN_INBAND)
+	if (phylink_mode_inband(pl->cfg_link_an_mode))
 		del_timer(&pl->link_poll);
 }
 
 static void phylink_pcs_poll_start(struct phylink *pl)
 {
-	if (pl->pcs && pl->pcs->poll && pl->cfg_link_an_mode == MLO_AN_INBAND)
+	if (pl->pcs && pl->pcs->poll &&
+	    phylink_mode_inband(pl->cfg_link_an_mode))
 		mod_timer(&pl->link_poll, jiffies + HZ);
 }
 
@@ -1082,13 +1122,13 @@ static void phylink_mac_config(struct phylink *pl,
 
 	phylink_dbg(pl,
 		    "%s: mode=%s/%s/%s adv=%*pb pause=%02x\n",
-		    __func__, phylink_an_mode_str(pl->cur_link_an_mode),
+		    __func__, phylink_an_mode_str(pl->act_link_an_mode),
 		    phy_modes(st.interface),
 		    phy_rate_matching_to_str(st.rate_matching),
 		    __ETHTOOL_LINK_MODE_MASK_NBITS, st.advertising,
 		    st.pause);
 
-	pl->mac_ops->mac_config(pl->config, pl->cur_link_an_mode, &st);
+	pl->mac_ops->mac_config(pl->config, pl->act_link_an_mode, &st);
 }
 
 static void phylink_pcs_an_restart(struct phylink *pl)
@@ -1096,12 +1136,14 @@ static void phylink_pcs_an_restart(struct phylink *pl)
 	if (pl->pcs && linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
 					 pl->link_config.advertising) &&
 	    phy_interface_mode_is_8023z(pl->link_config.interface) &&
-	    phylink_autoneg_inband(pl->cur_link_an_mode))
+	    phylink_autoneg_inband(pl->act_link_an_mode))
 		pl->pcs->ops->pcs_an_restart(pl->pcs);
 }
 
 /**
  * phylink_pcs_neg_mode() - helper to determine PCS inband mode
+ * @pl: a pointer to a &struct phylink returned from phylink_create()
+ * @pcs: a pointer to &struct phylink_pcs
  * @mode: one of %MLO_AN_FIXED, %MLO_AN_PHY, %MLO_AN_INBAND.
  * @interface: interface mode to be used
  * @advertising: adertisement ethtool link mode mask
@@ -1119,11 +1161,19 @@ static void phylink_pcs_an_restart(struct phylink *pl)
  * Note: this is for cases where the PCS itself is involved in negotiation
  * (e.g. Clause 37, SGMII and similar) not Clause 73.
  */
-static unsigned int phylink_pcs_neg_mode(unsigned int mode,
+static unsigned int phylink_pcs_neg_mode(struct phylink *pl,
+					 struct phylink_pcs *pcs,
+					 unsigned int mode,
 					 phy_interface_t interface,
 					 const unsigned long *advertising)
 {
+	unsigned int phy_link_mode = 0;
+	unsigned int pcs_link_mode;
 	unsigned int neg_mode;
+	enum {
+		INBAND_CISCO_SGMII,
+		INBAND_8023Z,
+	} type;
 
 	switch (interface) {
 	case PHY_INTERFACE_MODE_SGMII:
@@ -1136,10 +1186,7 @@ static unsigned int phylink_pcs_neg_mode(unsigned int mode,
 		 * inband communication. Note: there exist PHYs that run
 		 * with SGMII but do not send the inband data.
 		 */
-		if (!phylink_autoneg_inband(mode))
-			neg_mode = PHYLINK_PCS_NEG_OUTBAND;
-		else
-			neg_mode = PHYLINK_PCS_NEG_INBAND_ENABLED;
+		type = INBAND_CISCO_SGMII;
 		break;
 
 	case PHY_INTERFACE_MODE_1000BASEX:
@@ -1150,23 +1197,128 @@ static unsigned int phylink_pcs_neg_mode(unsigned int mode,
 		 * as well, but drivers may not support this, so may
 		 * need to override this.
 		 */
-		if (!phylink_autoneg_inband(mode))
-			neg_mode = PHYLINK_PCS_NEG_OUTBAND;
-		else if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
-					   advertising))
-			neg_mode = PHYLINK_PCS_NEG_INBAND_ENABLED;
-		else
-			neg_mode = PHYLINK_PCS_NEG_INBAND_DISABLED;
+		type = INBAND_8023Z;
 		break;
 
 	default:
-		neg_mode = PHYLINK_PCS_NEG_NONE;
-		break;
+		return PHYLINK_PCS_NEG_NONE;
 	}
 
+	pcs_link_mode = phylink_pcs_query_inband(pcs, interface);
+	pcs_link_mode &= LINK_INBAND_DISABLE | LINK_INBAND_ENABLE;
+	if (pl->phydev) {
+		phy_link_mode = phy_query_inband(pl->phydev, interface);
+		phy_link_mode &= LINK_INBAND_DISABLE | LINK_INBAND_ENABLE;
+	}
+
+	if (!phylink_autoneg_inband(mode)) {
+		const char *s1, *s2, *s3, *empty = "";
+
+		s1 = s2 = s3 = empty;
+
+		if (pcs_link_mode == LINK_INBAND_ENABLE)
+			s1 = "PCS";
+
+		if (phy_link_mode == LINK_INBAND_ENABLE)
+			s3 = "PHY";
+
+		/* If either the PCS or PHY requires inband to be enabled,
+		 * this is an invalid configuration. Provide a diagnostic
+		 * message for this case, but don't try to force the issue.
+		 */
+		if (s1 != empty || s3 != empty) {
+			if (s1 != empty && s3 != empty)
+				s2 = " and ";
+
+			phylink_warn(pl,
+				     "firmware wants %s mode, but %s%s%s requires inband\n",
+				     phylink_an_mode_str(mode),
+				     s1, s2, s3);
+		}
+		return PHYLINK_PCS_NEG_OUTBAND;
+	}
+
+	/* For SGMII modes, which are designed to be used with PHYs, we
+	 * try to use inband mode where-ever possible. However, there are
+	 * some PHYs e.g. BCM84881 which do not support in-band.
+	 */
+	if (type == INBAND_CISCO_SGMII) {
+		/* PCS	PHY
+		 * D E	D E
+		 * 0 0  0 0	no information			inband enabled
+		 * 1 0  0 0	pcs doesn't support		outband
+		 * 0 1  0 0	pcs required			inband enabled
+		 * 1 1  0 0	pcs optional			inband enabled
+		 * 0 0  1 0	phy doesn't support		outband
+		 * 1 0  1 0	pcs+phy doesn't support		outband
+		 * 0 1  1 0	pcs required, phy doesn't support, invalid
+		 * 1 1  1 0	pcs optional, phy doesn't support, outband
+		 * 0 0  0 1	phy required			inband enabled
+		 * 1 0  0 1	pcs doesn't support, phy required, invalid
+		 * 0 1  0 1	pcs+phy required		inband enabled
+		 * 1 1  0 1	pcs optional, phy required	inband enabled
+		 * 0 0  1 1	phy optional			inband enabled
+		 * 1 0  1 1	pcs doesn't support, phy optional, outband
+		 * 0 1  1 1	pcs required, phy optional	inband enabled
+		 * 1 1  1 1	pcs+phy optional		inband enabled
+		 */
+		if (!pcs_link_mode)
+			pcs_link_mode = LINK_INBAND_DISABLE | LINK_INBAND_ENABLE;
+
+		if (!phy_link_mode)
+			phy_link_mode = LINK_INBAND_DISABLE | LINK_INBAND_ENABLE;
+
+		if (pcs_link_mode & phy_link_mode & LINK_INBAND_ENABLE) {
+			/* inband supported at both ends */
+			return PHYLINK_PCS_NEG_INBAND_ENABLED;
+		} else if (pcs_link_mode & phy_link_mode & LINK_INBAND_DISABLE) {
+			/* inband not supported at both ends */
+			return PHYLINK_PCS_NEG_OUTBAND;
+		} else {
+			/* invalid */
+		}
+	}
+
+	/* For 802.3z, handle the PHY present vs absent cases separately */
+	if (!pl->phydev) {
+		/* PHY absent: always use inband but inband may be disabled */
+		/* If the PCS doesn't support inband, then inband must be
+		 * disabled.
+		 */
+		if (pcs_link_mode == LINK_INBAND_DISABLE)
+			return PHYLINK_PCS_NEG_INBAND_DISABLED;
+
+		/* If the PCS requires inband, then inband must always be
+		 * enabled.
+		 */
+		if (pcs_link_mode == LINK_INBAND_ENABLE)
+			return PHYLINK_PCS_NEG_INBAND_ENABLED;
+
+		/* For the possible case, fall through to the "legacy" code. */
+	} else {
+		/* PHY present, inband mode depends on the capabilities
+		 * of both.
+		 */
+	}
+
+	/* Legacy, so determine inband depending on the advertising bit */
+	if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, advertising))
+		neg_mode = PHYLINK_PCS_NEG_INBAND_ENABLED;
+	else
+		neg_mode = PHYLINK_PCS_NEG_INBAND_DISABLED;
+
 	return neg_mode;
 }
 
+static u8 phylink_choose_act_link_an_mode(struct phylink *pl)
+{
+	if (pl->req_link_an_mode == MLO_AN_INBAND && pl->phydev &&
+	    pl->pcs_neg_mode == PHYLINK_PCS_NEG_OUTBAND)
+		return MLO_AN_PHY;
+
+	return pl->req_link_an_mode;
+}
+
 static void phylink_major_config(struct phylink *pl, bool restart,
 				  const struct phylink_link_state *state)
 {
@@ -1176,13 +1328,11 @@ static void phylink_major_config(struct phylink *pl, bool restart,
 	unsigned int neg_mode;
 	int err;
 
-	phylink_dbg(pl, "major config %s\n", phy_modes(state->interface));
-
-	pl->pcs_neg_mode = phylink_pcs_neg_mode(pl->cur_link_an_mode,
-						state->interface,
-						state->advertising);
+	phylink_dbg(pl, "major config, requested %s/%s\n",
+		    phylink_an_mode_str(pl->req_link_an_mode),
+		    phy_modes(state->interface));
 
-	if (pl->using_mac_select_pcs) {
+	if (pl->mac_ops->mac_select_pcs) {
 		pcs = pl->mac_ops->mac_select_pcs(pl->config, state->interface);
 		if (IS_ERR(pcs)) {
 			phylink_err(pl,
@@ -1191,13 +1341,28 @@ static void phylink_major_config(struct phylink *pl, bool restart,
 			return;
 		}
 
-		pcs_changed = pcs && pl->pcs != pcs;
+		pcs_changed = pl->pcs != pcs;
 	}
 
+	pl->pcs_neg_mode = phylink_pcs_neg_mode(pl, pcs,
+						pl->req_link_an_mode,
+						state->interface,
+						state->advertising);
+
+	/* set the active link AN mode, which may end up different from the
+	 * current link AN mode depending on the PCS and PHY capabilities.
+	 */
+	pl->act_link_an_mode = phylink_choose_act_link_an_mode(pl);
+
+	phylink_dbg(pl, "major config, active %s/%s/%s\n",
+		    phylink_an_mode_str(pl->act_link_an_mode),
+		    phylink_pcs_mode_str(pl->pcs_neg_mode),
+		    phy_modes(state->interface));
+
 	phylink_pcs_poll_stop(pl);
 
 	if (pl->mac_ops->mac_prepare) {
-		err = pl->mac_ops->mac_prepare(pl->config, pl->cur_link_an_mode,
+		err = pl->mac_ops->mac_prepare(pl->config, pl->act_link_an_mode,
 					       state->interface);
 		if (err < 0) {
 			phylink_err(pl, "mac_prepare failed: %pe\n",
@@ -1231,7 +1396,7 @@ static void phylink_major_config(struct phylink *pl, bool restart,
 	if (pl->pcs_state == PCS_STATE_STARTING || pcs_changed)
 		phylink_pcs_enable(pl->pcs);
 
-	neg_mode = pl->cur_link_an_mode;
+	neg_mode = pl->act_link_an_mode;
 	if (pl->pcs && pl->pcs->neg_mode)
 		neg_mode = pl->pcs_neg_mode;
 
@@ -1247,7 +1412,7 @@ static void phylink_major_config(struct phylink *pl, bool restart,
 		phylink_pcs_an_restart(pl);
 
 	if (pl->mac_ops->mac_finish) {
-		err = pl->mac_ops->mac_finish(pl->config, pl->cur_link_an_mode,
+		err = pl->mac_ops->mac_finish(pl->config, pl->act_link_an_mode,
 					      state->interface);
 		if (err < 0)
 			phylink_err(pl, "mac_finish failed: %pe\n",
@@ -1278,17 +1443,23 @@ static int phylink_change_inband_advert(struct phylink *pl)
 		return 0;
 
 	phylink_dbg(pl, "%s: mode=%s/%s adv=%*pb pause=%02x\n", __func__,
-		    phylink_an_mode_str(pl->cur_link_an_mode),
+		    phylink_an_mode_str(pl->req_link_an_mode),
 		    phy_modes(pl->link_config.interface),
 		    __ETHTOOL_LINK_MODE_MASK_NBITS, pl->link_config.advertising,
 		    pl->link_config.pause);
 
 	/* Recompute the PCS neg mode */
-	pl->pcs_neg_mode = phylink_pcs_neg_mode(pl->cur_link_an_mode,
-					pl->link_config.interface,
-					pl->link_config.advertising);
+	pl->pcs_neg_mode = phylink_pcs_neg_mode(pl, pl->pcs,
+						pl->req_link_an_mode,
+						pl->link_config.interface,
+						pl->link_config.advertising);
 
-	neg_mode = pl->cur_link_an_mode;
+	/* set the active link AN mode, which may end up different from the
+	 * current link AN mode depending on the PCS and PHY capabilities.
+	 */
+	pl->act_link_an_mode = phylink_choose_act_link_an_mode(pl);
+
+	neg_mode = pl->act_link_an_mode;
 	if (pl->pcs->neg_mode)
 		neg_mode = pl->pcs_neg_mode;
 
@@ -1353,7 +1524,7 @@ static void phylink_mac_initial_config(struct phylink *pl, bool force_restart)
 {
 	struct phylink_link_state link_state;
 
-	switch (pl->cur_link_an_mode) {
+	switch (pl->req_link_an_mode) {
 	case MLO_AN_PHY:
 		link_state = pl->phy_state;
 		break;
@@ -1392,6 +1563,75 @@ static const char *phylink_pause_to_str(int pause)
 	}
 }
 
+static void phylink_disable_tx_lpi(struct phylink *pl)
+{
+	phylink_dbg(pl, "disabling tx_lpi\n");
+
+	if (pl->mac_ops->mac_disable_tx_lpi)
+		pl->mac_ops->mac_disable_tx_lpi(pl->config);
+}
+
+static void phylink_enable_tx_lpi(struct phylink *pl)
+{
+	phylink_dbg(pl, "enabling tx_lpi, timer %uus\n",
+		    pl->eee_cfg.tx_lpi_timer);
+
+	if (pl->mac_ops->mac_enable_tx_lpi)
+		pl->mac_ops->mac_enable_tx_lpi(pl->config,
+					       pl->eee_cfg.tx_lpi_timer);
+}
+
+static bool phylink_eee_is_active(struct phylink *pl)
+{
+	return phylink_init_eee(pl, pl->config->eee_clk_stop_enable) >= 0;
+}
+
+static void phylink_deactivate_eee(struct phylink *pl)
+{
+	phylink_dbg(pl, "deactivating EEE, was %sactive\n",
+		    pl->eee_active ? "" : "in");
+
+	if (pl->eee_active) {
+		pl->eee_active = false;
+		phylink_disable_tx_lpi(pl);
+	}
+}
+
+static void phylink_activate_eee(struct phylink *pl)
+{
+	pl->eee_active = phylink_eee_is_active(pl);
+
+	phylink_dbg(pl, "can LPI, EEE enabled, %sactive\n",
+		    pl->eee_active ? "" : "in");
+
+	if (pl->eee_active)
+		phylink_enable_tx_lpi(pl);
+}
+
+/* Determine whether the MAC has new EEE support. We detect this by checking
+ * for the two new methods being present, but for DSA it will populate these
+ * anyway, so also check that lpi_capabilities is non-zero.
+ */
+static bool phylink_mac_supports_eee(struct phylink *pl)
+{
+	return pl->mac_ops->mac_disable_tx_lpi &&
+	       pl->mac_ops->mac_enable_tx_lpi &&
+	       pl->config->lpi_capabilities;
+}
+
+static void phylink_phy_restrict_eee(struct phylink *pl, struct phy_device *phy)
+{
+	__ETHTOOL_DECLARE_LINK_MODE_MASK(eee_supported);
+
+	/* Convert the MAC's LPI capabilities to linkmodes */
+	linkmode_zero(eee_supported);
+	phylink_caps_to_linkmodes(eee_supported, pl->config->lpi_capabilities);
+
+	/* Mask out EEE modes that are not supported */
+	linkmode_and(phy->supported_eee, phy->supported_eee, eee_supported);
+	linkmode_and(phy->advertising_eee, phy->advertising_eee, eee_supported);
+}
+
 static void phylink_link_up(struct phylink *pl,
 			    struct phylink_link_state link_state)
 {
@@ -1427,17 +1667,20 @@ static void phylink_link_up(struct phylink *pl,
 
 	pl->cur_interface = link_state.interface;
 
-	neg_mode = pl->cur_link_an_mode;
+	neg_mode = pl->act_link_an_mode;
 	if (pl->pcs && pl->pcs->neg_mode)
 		neg_mode = pl->pcs_neg_mode;
 
 	phylink_pcs_link_up(pl->pcs, neg_mode, pl->cur_interface, speed,
 			    duplex);
 
-	pl->mac_ops->mac_link_up(pl->config, pl->phydev, pl->cur_link_an_mode,
+	pl->mac_ops->mac_link_up(pl->config, pl->phydev, pl->act_link_an_mode,
 				 pl->cur_interface, speed, duplex,
 				 !!(link_state.pause & MLO_PAUSE_TX), rx_pause);
 
+	if (eeecfg_mac_can_tx_lpi(&pl->eee_cfg))
+		phylink_activate_eee(pl);
+
 	if (ndev)
 		netif_carrier_on(ndev);
 
@@ -1454,25 +1697,29 @@ static void phylink_link_down(struct phylink *pl)
 
 	if (ndev)
 		netif_carrier_off(ndev);
-	pl->mac_ops->mac_link_down(pl->config, pl->cur_link_an_mode,
+
+	phylink_deactivate_eee(pl);
+
+	pl->mac_ops->mac_link_down(pl->config, pl->act_link_an_mode,
 				   pl->cur_interface);
 	phylink_info(pl, "Link is Down\n");
 }
 
+static bool phylink_link_is_up(struct phylink *pl)
+{
+	return pl->netdev ? netif_carrier_ok(pl->netdev) : pl->old_link_state;
+}
+
 static void phylink_resolve(struct work_struct *w)
 {
 	struct phylink *pl = container_of(w, struct phylink, resolve);
 	struct phylink_link_state link_state;
-	struct net_device *ndev = pl->netdev;
 	bool mac_config = false;
 	bool retrigger = false;
 	bool cur_link_state;
 
 	mutex_lock(&pl->state_mutex);
-	if (pl->netdev)
-		cur_link_state = netif_carrier_ok(ndev);
-	else
-		cur_link_state = pl->old_link_state;
+	cur_link_state = phylink_link_is_up(pl);
 
 	if (pl->phylink_disable_state) {
 		pl->mac_link_dropped = false;
@@ -1480,76 +1727,73 @@ static void phylink_resolve(struct work_struct *w)
 	} else if (pl->mac_link_dropped) {
 		link_state.link = false;
 		retrigger = true;
-	} else {
-		switch (pl->cur_link_an_mode) {
-		case MLO_AN_PHY:
+	} else if (pl->act_link_an_mode == MLO_AN_FIXED) {
+		phylink_get_fixed_state(pl, &link_state);
+		mac_config = link_state.link;
+	} else if (pl->act_link_an_mode == MLO_AN_PHY) {
+		/* PHY mode, or PCS configured for outband mode */
+		if (pl->phydev) {
 			link_state = pl->phy_state;
-			phylink_apply_manual_flow(pl, &link_state);
-			mac_config = link_state.link;
-			break;
+		} else {
+			/* No PHY - assume link is down */
+			link_state.link = false;
+		}
+		mac_config = link_state.link;
+	} else {
+		/* Inband mode */
+		phylink_mac_pcs_get_state(pl, &link_state);
 
-		case MLO_AN_FIXED:
-			phylink_get_fixed_state(pl, &link_state);
-			mac_config = link_state.link;
-			break;
+		/* The PCS may have a latching link-fail indicator.
+		 * If the link was up, bring the link down and
+		 * re-trigger the resolve. Otherwise, re-read the
+		 * PCS state to get the current status of the link.
+		 */
+		if (!link_state.link) {
+			if (cur_link_state)
+				retrigger = true;
+			else
+				phylink_mac_pcs_get_state(pl, &link_state);
+		}
 
-		case MLO_AN_INBAND:
-			phylink_mac_pcs_get_state(pl, &link_state);
+		/* If we have a phy, the "up" state is the union of
+		 * both the PHY and the MAC
+		 */
+		if (pl->phydev)
+			link_state.link &= pl->phy_state.link;
 
-			/* The PCS may have a latching link-fail indicator.
-			 * If the link was up, bring the link down and
-			 * re-trigger the resolve. Otherwise, re-read the
-			 * PCS state to get the current status of the link.
+		/* Only update if the PHY link is up */
+		if (pl->phydev && pl->phy_state.link) {
+			/* If the interface has changed, force a
+			 * link down event if the link isn't already
+			 * down, and re-resolve.
 			 */
-			if (!link_state.link) {
-				if (cur_link_state)
-					retrigger = true;
-				else
-					phylink_mac_pcs_get_state(pl,
-								  &link_state);
+			if (link_state.interface != pl->phy_state.interface) {
+				retrigger = true;
+				link_state.link = false;
 			}
+			link_state.interface = pl->phy_state.interface;
 
-			/* If we have a phy, the "up" state is the union of
-			 * both the PHY and the MAC
+			/* If we are doing rate matching, then the
+			 * link speed/duplex comes from the PHY
 			 */
-			if (pl->phydev)
-				link_state.link &= pl->phy_state.link;
-
-			/* Only update if the PHY link is up */
-			if (pl->phydev && pl->phy_state.link) {
-				/* If the interface has changed, force a
-				 * link down event if the link isn't already
-				 * down, and re-resolve.
-				 */
-				if (link_state.interface !=
-				    pl->phy_state.interface) {
-					retrigger = true;
-					link_state.link = false;
-				}
-				link_state.interface = pl->phy_state.interface;
-
-				/* If we are doing rate matching, then the
-				 * link speed/duplex comes from the PHY
-				 */
-				if (pl->phy_state.rate_matching) {
-					link_state.rate_matching =
-						pl->phy_state.rate_matching;
-					link_state.speed = pl->phy_state.speed;
-					link_state.duplex =
-						pl->phy_state.duplex;
-				}
-
-				/* If we have a PHY, we need to update with
-				 * the PHY flow control bits.
-				 */
-				link_state.pause = pl->phy_state.pause;
-				mac_config = true;
+			if (pl->phy_state.rate_matching) {
+				link_state.rate_matching =
+					pl->phy_state.rate_matching;
+				link_state.speed = pl->phy_state.speed;
+				link_state.duplex = pl->phy_state.duplex;
 			}
-			phylink_apply_manual_flow(pl, &link_state);
-			break;
+
+			/* If we have a PHY, we need to update with
+			 * the PHY flow control bits.
+			 */
+			link_state.pause = pl->phy_state.pause;
+			mac_config = true;
 		}
 	}
 
+	if (pl->act_link_an_mode != MLO_AN_FIXED)
+		phylink_apply_manual_flow(pl, &link_state);
+
 	if (mac_config) {
 		if (link_state.interface != pl->link_config.interface) {
 			/* The interface has changed, force the link down and
@@ -1656,7 +1900,6 @@ struct phylink *phylink_create(struct phylink_config *config,
 			       phy_interface_t iface,
 			       const struct phylink_mac_ops *mac_ops)
 {
-	bool using_mac_select_pcs = false;
 	struct phylink *pl;
 	int ret;
 
@@ -1667,11 +1910,6 @@ struct phylink *phylink_create(struct phylink_config *config,
 		return ERR_PTR(-EINVAL);
 	}
 
-	if (mac_ops->mac_select_pcs &&
-	    mac_ops->mac_select_pcs(config, PHY_INTERFACE_MODE_NA) !=
-	      ERR_PTR(-EOPNOTSUPP))
-		using_mac_select_pcs = true;
-
 	pl = kzalloc(sizeof(*pl), GFP_KERNEL);
 	if (!pl)
 		return ERR_PTR(-ENOMEM);
@@ -1690,7 +1928,6 @@ struct phylink *phylink_create(struct phylink_config *config,
 		return ERR_PTR(-EINVAL);
 	}
 
-	pl->using_mac_select_pcs = using_mac_select_pcs;
 	pl->phy_state.interface = iface;
 	pl->link_interface = iface;
 	if (iface == PHY_INTERFACE_MODE_MOCA)
@@ -1710,13 +1947,16 @@ struct phylink *phylink_create(struct phylink_config *config,
 	linkmode_copy(pl->link_config.advertising, pl->supported);
 	phylink_validate(pl, pl->supported, &pl->link_config);
 
+	/* Set the default EEE configuration */
+	pl->eee_cfg = pl->config->eee;
+
 	ret = phylink_parse_mode(pl, fwnode);
 	if (ret < 0) {
 		kfree(pl);
 		return ERR_PTR(ret);
 	}
 
-	if (pl->cfg_link_an_mode == MLO_AN_FIXED) {
+	if (phylink_mode_fixed(pl->cfg_link_an_mode)) {
 		ret = phylink_parse_fixedlink(pl, fwnode);
 		if (ret < 0) {
 			kfree(pl);
@@ -1724,7 +1964,8 @@ struct phylink *phylink_create(struct phylink_config *config,
 		}
 	}
 
-	pl->cur_link_an_mode = pl->cfg_link_an_mode;
+	pl->req_link_an_mode = pl->cfg_link_an_mode;
+	pl->act_link_an_mode = pl->req_link_an_mode;
 
 	ret = phylink_register_sfp(pl, fwnode);
 	if (ret < 0) {
@@ -1932,6 +2173,13 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy,
 
 	/* Restrict the phy advertisement according to the MAC support. */
 	linkmode_copy(phy->advertising, config.advertising);
+
+	/* If the MAC supports phylink managed EEE, restrict the EEE
+	 * advertisement according to the MAC's LPI capabilities.
+	 */
+	if (phylink_mac_supports_eee(pl))
+		phylink_phy_restrict_eee(pl, phy);
+
 	mutex_unlock(&pl->state_mutex);
 	mutex_unlock(&phy->lock);
 
@@ -1955,8 +2203,8 @@ static int phylink_attach_phy(struct phylink *pl, struct phy_device *phy,
 {
 	u32 flags = 0;
 
-	if (WARN_ON(pl->cfg_link_an_mode == MLO_AN_FIXED ||
-		    (pl->cfg_link_an_mode == MLO_AN_INBAND &&
+	if (WARN_ON(phylink_mode_fixed(pl->cfg_link_an_mode) ||
+		    (phylink_mode_inband(pl->cfg_link_an_mode) &&
 		     phy_interface_mode_is_8023z(interface) && !pl->sfp_bus)))
 		return -EINVAL;
 
@@ -2045,14 +2293,14 @@ int phylink_fwnode_phy_connect(struct phylink *pl,
 	int ret;
 
 	/* Fixed links and 802.3z are handled without needing a PHY */
-	if (pl->cfg_link_an_mode == MLO_AN_FIXED ||
-	    (pl->cfg_link_an_mode == MLO_AN_INBAND &&
+	if (phylink_mode_fixed(pl->cfg_link_an_mode) ||
+	    (phylink_mode_inband(pl->cfg_link_an_mode) &&
 	     phy_interface_mode_is_8023z(pl->link_interface)))
 		return 0;
 
 	phy_fwnode = fwnode_get_phy_node(fwnode);
 	if (IS_ERR(phy_fwnode)) {
-		if (pl->cfg_link_an_mode == MLO_AN_PHY)
+		if (phylink_mode_phy(pl->cfg_link_an_mode))
 			return -ENODEV;
 		return 0;
 	}
@@ -2179,7 +2427,7 @@ void phylink_start(struct phylink *pl)
 	ASSERT_RTNL();
 
 	phylink_info(pl, "configuring for %s/%s link mode\n",
-		     phylink_an_mode_str(pl->cur_link_an_mode),
+		     phylink_an_mode_str(pl->req_link_an_mode),
 		     phy_modes(pl->link_config.interface));
 
 	/* Always set the carrier off */
@@ -2202,7 +2450,7 @@ void phylink_start(struct phylink *pl)
 
 	phylink_enable_and_run_resolve(pl, PHYLINK_DISABLE_STOPPED);
 
-	if (pl->cfg_link_an_mode == MLO_AN_FIXED && pl->link_gpio) {
+	if (phylink_mode_fixed(pl->cfg_link_an_mode) && pl->link_gpio) {
 		int irq = gpiod_to_irq(pl->link_gpio);
 
 		if (irq > 0) {
@@ -2390,6 +2638,32 @@ int phylink_ethtool_set_wol(struct phylink *pl, struct ethtool_wolinfo *wol)
 }
 EXPORT_SYMBOL_GPL(phylink_ethtool_set_wol);
 
+static phy_interface_t phylink_sfp_select_interface(struct phylink *pl,
+						const unsigned long *link_modes)
+{
+	phy_interface_t interface;
+
+	interface = sfp_select_interface(pl->sfp_bus, link_modes);
+	if (interface == PHY_INTERFACE_MODE_NA) {
+		phylink_err(pl,
+			    "selection of interface failed, advertisement %*pb\n",
+			    __ETHTOOL_LINK_MODE_MASK_NBITS,
+			    link_modes);
+		return interface;
+	}
+
+	if (!test_bit(interface, pl->config->supported_interfaces)) {
+		phylink_err(pl,
+			    "selection of interface failed - SFP selected %s (%u) but MAC supports %*pbl\n",
+			    phy_modes(interface), interface,
+			    (int)PHY_INTERFACE_MODE_MAX,
+			    pl->config->supported_interfaces);
+		return PHY_INTERFACE_MODE_NA;
+	}
+
+	return interface;
+}
+
 static void phylink_merge_link_mode(unsigned long *dst, const unsigned long *b)
 {
 	__ETHTOOL_DECLARE_LINK_MODE_MASK(mask);
@@ -2438,7 +2712,7 @@ int phylink_ethtool_ksettings_get(struct phylink *pl,
 
 	linkmode_copy(kset->link_modes.supported, pl->supported);
 
-	switch (pl->cur_link_an_mode) {
+	switch (pl->act_link_an_mode) {
 	case MLO_AN_FIXED:
 		/* We are using fixed settings. Report these as the
 		 * current link settings - and note that these also
@@ -2469,6 +2743,26 @@ int phylink_ethtool_ksettings_get(struct phylink *pl,
 }
 EXPORT_SYMBOL_GPL(phylink_ethtool_ksettings_get);
 
+static bool phylink_validate_pcs_inband_autoneg(struct phylink *pl,
+					        phy_interface_t interface,
+						unsigned long *adv)
+{
+	unsigned int inband = phylink_query_inband(pl, interface);
+	unsigned int mask;
+
+	/* If the PCS doesn't implement inband support, be permissive. */
+	if (!inband)
+		return true;
+
+	if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, adv))
+		mask = LINK_INBAND_ENABLE;
+	else
+		mask = LINK_INBAND_DISABLE;
+
+	/* Check whether the PCS implements the required mode */
+	return !!(inband & mask);
+}
+
 /**
  * phylink_ethtool_ksettings_set() - set the link settings
  * @pl: a pointer to a &struct phylink returned from phylink_create()
@@ -2530,7 +2824,7 @@ int phylink_ethtool_ksettings_set(struct phylink *pl,
 		/* If we have a fixed link, refuse to change link parameters.
 		 * If the link parameters match, accept them but do nothing.
 		 */
-		if (pl->cur_link_an_mode == MLO_AN_FIXED) {
+		if (phylink_mode_fixed(pl->req_link_an_mode)) {
 			if (s->speed != pl->link_config.speed ||
 			    s->duplex != pl->link_config.duplex)
 				return -EINVAL;
@@ -2546,7 +2840,7 @@ int phylink_ethtool_ksettings_set(struct phylink *pl,
 		 * is our default case) but do not allow the advertisement to
 		 * be changed. If the advertisement matches, simply return.
 		 */
-		if (pl->cur_link_an_mode == MLO_AN_FIXED) {
+		if (phylink_mode_fixed(pl->req_link_an_mode)) {
 			if (!linkmode_equal(config.advertising,
 					    pl->link_config.advertising))
 				return -EINVAL;
@@ -2572,21 +2866,16 @@ int phylink_ethtool_ksettings_set(struct phylink *pl,
 	 * link can be configured correctly.
 	 */
 	if (pl->sfp_bus) {
-		config.interface = sfp_select_interface(pl->sfp_bus,
+		config.interface = phylink_sfp_select_interface(pl,
 							config.advertising);
-		if (config.interface == PHY_INTERFACE_MODE_NA) {
-			phylink_err(pl,
-				    "selection of interface failed, advertisement %*pb\n",
-				    __ETHTOOL_LINK_MODE_MASK_NBITS,
-				    config.advertising);
+		if (config.interface == PHY_INTERFACE_MODE_NA)
 			return -EINVAL;
-		}
 
 		/* Revalidate with the selected interface */
 		linkmode_copy(support, pl->supported);
 		if (phylink_validate(pl, support, &config)) {
 			phylink_err(pl, "validation of %s/%s with support %*pb failed\n",
-				    phylink_an_mode_str(pl->cur_link_an_mode),
+				    phylink_an_mode_str(pl->req_link_an_mode),
 				    phy_modes(config.interface),
 				    __ETHTOOL_LINK_MODE_MASK_NBITS, support);
 			return -EINVAL;
@@ -2604,6 +2893,13 @@ int phylink_ethtool_ksettings_set(struct phylink *pl,
 	    phylink_is_empty_linkmode(config.advertising))
 		return -EINVAL;
 
+	/* Validate the autonegotiation state. We don't have a PHY in this
+	 * situation, so the PCS is the media-facing entity.
+	 */
+	if (!phylink_validate_pcs_inband_autoneg(pl, config.interface,
+						 config.advertising))
+		return -EINVAL;
+
 	mutex_lock(&pl->state_mutex);
 	pl->link_config.speed = config.speed;
 	pl->link_config.duplex = config.duplex;
@@ -2686,7 +2982,7 @@ int phylink_ethtool_set_pauseparam(struct phylink *pl,
 
 	ASSERT_RTNL();
 
-	if (pl->cur_link_an_mode == MLO_AN_FIXED)
+	if (phylink_mode_fixed(pl->req_link_an_mode))
 		return -EOPNOTSUPP;
 
 	if (!phylink_test(pl->supported, Pause) &&
@@ -2794,6 +3090,8 @@ int phylink_init_eee(struct phylink *pl, bool clk_stop_enable)
 
 	if (pl->phydev)
 		ret = phy_init_eee(pl->phydev, clk_stop_enable);
+	else if (pl->sfp_bus && phylink_mac_supports_eee(pl))
+		ret = 0;
 
 	return ret;
 }
@@ -2810,8 +3108,19 @@ int phylink_ethtool_get_eee(struct phylink *pl, struct ethtool_keee *eee)
 
 	ASSERT_RTNL();
 
-	if (pl->phydev)
+	if (pl->phydev) {
 		ret = phy_ethtool_get_eee(pl->phydev, eee);
+	} else if (pl->sfp_bus && phylink_mac_supports_eee(pl)) {
+		/* For optical SFPs, ensure that eee->supported is nonzero. */
+		__set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, eee->supported);
+		ret = 0;
+	}
+
+	if (!ret && phylink_mac_supports_eee(pl)) {
+		/* Overwrite phylib's interpretation of configuration */
+		eeecfg_to_eee(eee, &pl->eee_cfg);
+		eee->eee_active = pl->eee_active;
+	}
 
 	return ret;
 }
@@ -2825,11 +3134,53 @@ EXPORT_SYMBOL_GPL(phylink_ethtool_get_eee);
 int phylink_ethtool_set_eee(struct phylink *pl, struct ethtool_keee *eee)
 {
 	int ret = -EOPNOTSUPP;
+	bool mac_eee;
 
 	ASSERT_RTNL();
 
+	mac_eee = phylink_mac_supports_eee(pl);
+
+	phylink_dbg(pl, "mac %s phylink EEE%s, adv 0x%*pbl, LPI%s timer %uus\n",
+		    mac_eee ? "supports" : "does not support",
+		    eee->eee_enabled ? ", enabled" : "",
+		    __ETHTOOL_LINK_MODE_MASK_NBITS, eee->advertised,
+		    eee->tx_lpi_enabled ? " enabled" : "", eee->tx_lpi_timer);
+
+	/* Clamp the LPI timer maximum value */
+	if (mac_eee && eee->tx_lpi_timer > pl->config->lpi_timer_limit_us) {
+		eee->tx_lpi_timer = pl->config->lpi_timer_limit_us;
+		phylink_dbg(pl, "LPI timer limited to %uus\n",
+			    eee->tx_lpi_timer);
+	}
+
 	if (pl->phydev)
 		ret = phy_ethtool_set_eee(pl->phydev, eee);
+	else if (pl->sfp_bus && phylink_mac_supports_eee(pl))
+		ret = 0;
+
+	if (!ret && mac_eee) {
+		bool can_lpi, old_can_lpi;
+
+		mutex_lock(&pl->state_mutex);
+		old_can_lpi = eeecfg_mac_can_tx_lpi(&pl->eee_cfg);
+		eee_to_eeecfg(&pl->eee_cfg, eee);
+		can_lpi = eeecfg_mac_can_tx_lpi(&pl->eee_cfg);
+
+		phylink_dbg(pl, "can_lpi %u -> %u\n", old_can_lpi, can_lpi);
+
+		/* If the link is up, and the configuration changes the
+		 * LPI permissive state, deal with the change at the MAC.
+		 */
+		if (phylink_link_is_up(pl) && old_can_lpi != can_lpi) {
+			phylink_dbg(pl, "link is up, lpi changed\n");
+			if (can_lpi)
+				phylink_activate_eee(pl);
+			else
+				phylink_deactivate_eee(pl);
+		}
+
+		mutex_unlock(&pl->state_mutex);
+	}
 
 	return ret;
 }
@@ -2950,7 +3301,7 @@ static int phylink_mii_read(struct phylink *pl, unsigned int phy_id,
 	struct phylink_link_state state;
 	int val = 0xffff;
 
-	switch (pl->cur_link_an_mode) {
+	switch (pl->act_link_an_mode) {
 	case MLO_AN_FIXED:
 		if (phy_id == 0) {
 			phylink_get_fixed_state(pl, &state);
@@ -2975,7 +3326,7 @@ static int phylink_mii_read(struct phylink *pl, unsigned int phy_id,
 static int phylink_mii_write(struct phylink *pl, unsigned int phy_id,
 			     unsigned int reg, unsigned int val)
 {
-	switch (pl->cur_link_an_mode) {
+	switch (pl->act_link_an_mode) {
 	case MLO_AN_FIXED:
 		break;
 
@@ -3145,10 +3496,11 @@ static phy_interface_t phylink_choose_sfp_interface(struct phylink *pl,
 	return interface;
 }
 
-static void phylink_sfp_set_config(struct phylink *pl, u8 mode,
+static void phylink_sfp_set_config(struct phylink *pl,
 				   unsigned long *supported,
 				   struct phylink_link_state *state)
 {
+	u8 mode = MLO_AN_INBAND;
 	bool changed = false;
 
 	phylink_dbg(pl, "requesting link mode %s/%s with support %*pb\n",
@@ -3165,9 +3517,9 @@ static void phylink_sfp_set_config(struct phylink *pl, u8 mode,
 		changed = true;
 	}
 
-	if (pl->cur_link_an_mode != mode ||
+	if (pl->req_link_an_mode != mode ||
 	    pl->link_config.interface != state->interface) {
-		pl->cur_link_an_mode = mode;
+		pl->req_link_an_mode = mode;
 		pl->link_config.interface = state->interface;
 
 		changed = true;
@@ -3182,13 +3534,10 @@ static void phylink_sfp_set_config(struct phylink *pl, u8 mode,
 		phylink_mac_initial_config(pl, false);
 }
 
-static int phylink_sfp_config_phy(struct phylink *pl, u8 mode,
-				  struct phy_device *phy)
+static int phylink_sfp_config_phy(struct phylink *pl, struct phy_device *phy)
 {
-	__ETHTOOL_DECLARE_LINK_MODE_MASK(support1);
 	__ETHTOOL_DECLARE_LINK_MODE_MASK(support);
 	struct phylink_link_state config;
-	phy_interface_t iface;
 	int ret;
 
 	linkmode_copy(support, phy->supported);
@@ -3209,30 +3558,27 @@ static int phylink_sfp_config_phy(struct phylink *pl, u8 mode,
 		return ret;
 	}
 
-	iface = sfp_select_interface(pl->sfp_bus, config.advertising);
-	if (iface == PHY_INTERFACE_MODE_NA) {
-		phylink_err(pl,
-			    "selection of interface failed, advertisement %*pb\n",
-			    __ETHTOOL_LINK_MODE_MASK_NBITS, config.advertising);
+	config.interface = phylink_sfp_select_interface(pl, config.advertising);
+	if (config.interface == PHY_INTERFACE_MODE_NA)
 		return -EINVAL;
-	}
 
-	config.interface = iface;
-	linkmode_copy(support1, support);
-	ret = phylink_validate(pl, support1, &config);
-	if (ret) {
-		phylink_err(pl,
-			    "validation of %s/%s with support %*pb failed: %pe\n",
-			    phylink_an_mode_str(mode),
-			    phy_modes(config.interface),
-			    __ETHTOOL_LINK_MODE_MASK_NBITS, support,
-			    ERR_PTR(ret));
+	/* Attach the PHY so that the PHY is present when we do the major
+	 * configuration step.
+	 */
+	ret = phylink_attach_phy(pl, phy, config.interface);
+	if (ret < 0)
+		return ret;
+
+	/* This will validate the configuration for us. */
+	ret = phylink_bringup_phy(pl, phy, config.interface);
+	if (ret < 0) {
+		phy_detach(phy);
 		return ret;
 	}
 
 	pl->link_port = pl->sfp_port;
 
-	phylink_sfp_set_config(pl, mode, support, &config);
+	phylink_sfp_set_config(pl, support, &config);
 
 	return 0;
 }
@@ -3288,6 +3634,12 @@ static int phylink_sfp_config_optical(struct phylink *pl)
 	phylink_dbg(pl, "optical SFP: chosen %s interface\n",
 		    phy_modes(interface));
 
+	if (!phylink_validate_pcs_inband_autoneg(pl, interface,
+						 config.advertising)) {
+		phylink_err(pl, "autoneg setting not compatible with PCS");
+		return -EINVAL;
+	}
+
 	config.interface = interface;
 
 	/* Ignore errors if we're expecting a PHY to attach later */
@@ -3301,7 +3653,7 @@ static int phylink_sfp_config_optical(struct phylink *pl)
 
 	pl->link_port = pl->sfp_port;
 
-	phylink_sfp_set_config(pl, MLO_AN_INBAND, pl->sfp_support, &config);
+	phylink_sfp_set_config(pl, pl->sfp_support, &config);
 
 	return 0;
 }
@@ -3372,20 +3724,11 @@ static void phylink_sfp_link_up(void *upstream)
 	phylink_enable_and_run_resolve(pl, PHYLINK_DISABLE_LINK);
 }
 
-/* The Broadcom BCM84881 in the Methode DM7052 is unable to provide a SGMII
- * or 802.3z control word, so inband will not work.
- */
-static bool phylink_phy_no_inband(struct phy_device *phy)
-{
-	return phy->is_c45 && phy_id_compare(phy->c45_ids.device_ids[1],
-					     0xae025150, 0xfffffff0);
-}
-
 static int phylink_sfp_connect_phy(void *upstream, struct phy_device *phy)
 {
+	DECLARE_PHY_INTERFACE_MASK(interfaces);
 	struct phylink *pl = upstream;
 	phy_interface_t interface;
-	u8 mode;
 	int ret;
 
 	/*
@@ -3397,30 +3740,59 @@ static int phylink_sfp_connect_phy(void *upstream, struct phy_device *phy)
 	 */
 	phy_support_asym_pause(phy);
 
-	if (phylink_phy_no_inband(phy))
-		mode = MLO_AN_PHY;
-	else
-		mode = MLO_AN_INBAND;
-
 	/* Set the PHY's host supported interfaces */
 	phy_interface_and(phy->host_interfaces, phylink_sfp_interfaces,
 			  pl->config->supported_interfaces);
 
-	/* Do the initial configuration */
-	ret = phylink_sfp_config_phy(pl, mode, phy);
-	if (ret < 0)
-		return ret;
+	if (phy_interface_empty(phy->supported_interfaces)) {
+		phylink_dbg(pl, "copper SFP: PHY provides empty supported_interfaces\n");
+
+		/* Do the initial configuration */
+		return phylink_sfp_config_phy(pl, phy);
+	}
+
+	phylink_dbg(pl, "copper SFP: interfaces=[mac=%*pbl, sfp=%*pbl]\n",
+		    (int)PHY_INTERFACE_MODE_MAX,
+		    pl->config->supported_interfaces,
+		    (int)PHY_INTERFACE_MODE_MAX,
+		    phy->supported_interfaces);
+
+	phy_interface_and(interfaces, phy->supported_interfaces,
+			  pl->config->supported_interfaces);
+	interface = phylink_choose_sfp_interface(pl, interfaces);
+	if (interface == PHY_INTERFACE_MODE_NA) {
+		phylink_err(pl,
+			    "selection of interface for PHY failed\n");
+		return -EINVAL;
+	}
+
+	phylink_dbg(pl, "copper SFP: chosen %s interface\n",
+		    phy_modes(interface));
 
-	interface = pl->link_config.interface;
 	ret = phylink_attach_phy(pl, phy, interface);
 	if (ret < 0)
 		return ret;
 
 	ret = phylink_bringup_phy(pl, phy, interface);
-	if (ret)
+	if (ret) {
 		phy_detach(phy);
+		return ret;
+	}
 
-	return ret;
+	if (pl->req_link_an_mode != MLO_AN_INBAND ||
+	    pl->link_config.interface != interface) {
+		pl->link_config.interface = interface;
+		pl->req_link_an_mode = MLO_AN_INBAND;
+
+		phylink_info(pl, "switched to %s/%s link mode\n",
+			     phylink_an_mode_str(pl->req_link_an_mode),
+			     phy_modes(pl->link_config.interface));
+	}
+
+	if (!test_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state))
+		phylink_mac_initial_config(pl, false);
+
+	return 0;
 }
 
 static void phylink_sfp_disconnect_phy(void *upstream)
@@ -3539,12 +3911,18 @@ static void phylink_decode_sgmii_word(struct phylink_link_state *state,
  * @lpa: a 16 bit value which stores the USXGMII auto-negotiation word
  *
  * Helper for MAC PCS supporting the USXGMII protocol and the auto-negotiation
- * code word.  Decode the USXGMII code word and populate the corresponding fields
- * (speed, duplex) into the phylink_link_state structure.
+ * code word. Decode the USXGMII code word and populate the corresponding fields
+ * (speed, duplex) into the phylink_link_state structure. If the code word
+ * indicates link is down, or we are unable to decode it, set the link down.
  */
 void phylink_decode_usxgmii_word(struct phylink_link_state *state,
 				 uint16_t lpa)
 {
+	if (!(lpa & MDIO_USXGMII_LINK)) {
+		state->link = false;
+		return;
+	}
+
 	switch (lpa & MDIO_USXGMII_SPD_MASK) {
 	case MDIO_USXGMII_10:
 		state->speed = SPEED_10;
diff --git a/drivers/net/phy/sff.c b/drivers/net/phy/sff.c
new file mode 100644
index 000000000000..a2eb56118dd4
--- /dev/null
+++ b/drivers/net/phy/sff.c
@@ -0,0 +1,114 @@
+#include <linux/kernel.h>
+#include <linux/sfp.h>
+#include "sff.h"
+
+const char *sff_link_len(char *buf, size_t size, unsigned int length,
+			 unsigned int multiplier)
+{
+	if (length == 0)
+		return "unsupported/unspecified";
+
+	if (length == 255) {
+		*buf++ = '>';
+		size -= 1;
+		length -= 1;
+	}
+
+	length *= multiplier;
+
+	if (length >= 1000)
+		snprintf(buf, size, "%u.%0*ukm",
+			length / 1000,
+			multiplier > 100 ? 1 :
+			multiplier > 10 ? 2 : 3,
+			length % 1000);
+	else
+		snprintf(buf, size, "%um", length);
+
+	return buf;
+}
+EXPORT_SYMBOL_GPL(sff_link_len);
+
+const char *sff_bitfield(char *buf, size_t size,
+			 const struct sff_bitfield *bits, unsigned int val)
+{
+	char *p = buf;
+	int n;
+
+	*p = '\0';
+	while (bits->mask) {
+		if ((val & bits->mask) == bits->val) {
+			n = snprintf(p, size, "%s%s",
+				     buf != p ? ", " : "",
+				     bits->str);
+			if (n == size)
+				break;
+			p += n;
+			size -= n;
+		}
+		bits++;
+	}
+
+	return buf;
+}
+EXPORT_SYMBOL_GPL(sff_bitfield);
+
+const char *sff_connector(unsigned int connector)
+{
+	switch (connector) {
+	case SFF8024_CONNECTOR_UNSPEC:
+		return "unknown/unspecified";
+	case SFF8024_CONNECTOR_SC:
+		return "SC";
+	case SFF8024_CONNECTOR_FIBERJACK:
+		return "Fiberjack";
+	case SFF8024_CONNECTOR_LC:
+		return "LC";
+	case SFF8024_CONNECTOR_MT_RJ:
+		return "MT-RJ";
+	case SFF8024_CONNECTOR_MU:
+		return "MU";
+	case SFF8024_CONNECTOR_SG:
+		return "SG";
+	case SFF8024_CONNECTOR_OPTICAL_PIGTAIL:
+		return "Optical pigtail";
+	case SFF8024_CONNECTOR_MPO_1X12:
+		return "MPO 1X12";
+	case SFF8024_CONNECTOR_MPO_2X16:
+		return "MPO 2X16";
+	case SFF8024_CONNECTOR_HSSDC_II:
+		return "HSSDC II";
+	case SFF8024_CONNECTOR_COPPER_PIGTAIL:
+		return "Copper pigtail";
+	case SFF8024_CONNECTOR_RJ45:
+		return "RJ45";
+	case SFF8024_CONNECTOR_MXC_2X16:
+		return "MXC 2X16";
+	default:
+		return "unknown";
+	}
+}
+EXPORT_SYMBOL_GPL(sff_connector);
+
+const char *sff_encoding(unsigned int encoding)
+{
+	switch (encoding) {
+	case SFF8024_ENCODING_UNSPEC:
+		return "unspecified";
+	case SFF8024_ENCODING_8472_64B66B:
+		return "64b66b";
+	case SFF8024_ENCODING_8B10B:
+		return "8b10b";
+	case SFF8024_ENCODING_4B5B:
+		return "4b5b";
+	case SFF8024_ENCODING_NRZ:
+		return "NRZ";
+	case SFF8024_ENCODING_8472_MANCHESTER:
+		return "MANCHESTER";
+	default:
+		return "unknown";
+	}
+}
+EXPORT_SYMBOL_GPL(sff_encoding);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/phy/sff.h b/drivers/net/phy/sff.h
new file mode 100644
index 000000000000..cd7bb7c7ae4a
--- /dev/null
+++ b/drivers/net/phy/sff.h
@@ -0,0 +1,16 @@
+#ifndef SFF_H
+#define SFF_H
+
+struct sff_bitfield {
+	unsigned int mask;
+	unsigned int val;
+	const char *str;
+};
+
+const char *sff_link_len(char *buf, size_t size, unsigned int length,
+			 unsigned int multiplier);
+const char *sff_bitfield(char *buf, size_t size,
+			 const struct sff_bitfield *bits, unsigned int val);
+const char *sff_connector(unsigned int connector);
+const char *sff_encoding(unsigned int encoding);
+#endif
diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c
index a5684ef5884b..8cc2672d0d77 100644
--- a/drivers/net/phy/sfp.c
+++ b/drivers/net/phy/sfp.c
@@ -16,6 +16,7 @@
 #include <linux/slab.h>
 #include <linux/workqueue.h>
 
+#include "sff.h"
 #include "sfp.h"
 #include "swphy.h"
 
@@ -172,6 +173,7 @@ static const enum gpiod_flags gpio_flags[] = {
 #define T_WAIT			msecs_to_jiffies(50)
 #define T_START_UP		msecs_to_jiffies(300)
 #define T_START_UP_BAD_GPON	msecs_to_jiffies(60000)
+#define T_START_UP_COOLED	msecs_to_jiffies(90000)
 
 /* t_reset is the time required to assert the TX_DISABLE signal to reset
  * an indicated TX_FAULT.
@@ -337,6 +339,7 @@ static const struct sff_data sfp_data = {
 static const struct of_device_id sfp_of_match[] = {
 	{ .compatible = "sff,sff", .data = &sff_data, },
 	{ .compatible = "sff,sfp", .data = &sfp_data, },
+	{ .compatible = "sff,sfp+", .data = &sfp_data, },
 	{ },
 };
 MODULE_DEVICE_TABLE(of, sfp_of_match);
@@ -1674,6 +1677,114 @@ static void sfp_hwmon_exit(struct sfp *sfp)
 }
 #endif
 
+static const struct sff_bitfield sfp_options[] = {
+	{
+		.mask = SFP_OPTIONS_HIGH_POWER_LEVEL,
+		.val = SFP_OPTIONS_HIGH_POWER_LEVEL,
+		.str = "hpl",
+	}, {
+		.mask = SFP_OPTIONS_PAGING_A2,
+		.val = SFP_OPTIONS_PAGING_A2,
+		.str = "paginga2",
+	}, {
+		.mask = SFP_OPTIONS_RETIMER,
+		.val = SFP_OPTIONS_RETIMER,
+		.str = "retimer",
+	}, {
+		.mask = SFP_OPTIONS_COOLED_XCVR,
+		.val = SFP_OPTIONS_COOLED_XCVR,
+		.str = "cooled",
+	}, {
+		.mask = SFP_OPTIONS_POWER_DECL,
+		.val = SFP_OPTIONS_POWER_DECL,
+		.str = "powerdecl",
+	}, {
+		.mask = SFP_OPTIONS_RX_LINEAR_OUT,
+		.val = SFP_OPTIONS_RX_LINEAR_OUT,
+		.str = "rxlinear",
+	}, {
+		.mask = SFP_OPTIONS_RX_DECISION_THRESH,
+		.val = SFP_OPTIONS_RX_DECISION_THRESH,
+		.str = "rxthresh",
+	}, {
+		.mask = SFP_OPTIONS_TUNABLE_TX,
+		.val = SFP_OPTIONS_TUNABLE_TX,
+		.str = "tunabletx",
+	}, {
+		.mask = SFP_OPTIONS_RATE_SELECT,
+		.val = SFP_OPTIONS_RATE_SELECT,
+		.str = "ratesel",
+	}, {
+		.mask = SFP_OPTIONS_TX_DISABLE,
+		.val = SFP_OPTIONS_TX_DISABLE,
+		.str = "txdisable",
+	}, {
+		.mask = SFP_OPTIONS_TX_FAULT,
+		.val = SFP_OPTIONS_TX_FAULT,
+		.str = "txfault",
+	}, {
+		.mask = SFP_OPTIONS_LOS_INVERTED,
+		.val = SFP_OPTIONS_LOS_INVERTED,
+		.str = "los-",
+	}, {
+		.mask = SFP_OPTIONS_LOS_NORMAL,
+		.val = SFP_OPTIONS_LOS_NORMAL,
+		.str = "los+",
+	}, { }
+};
+
+static const struct sff_bitfield diagmon[] = {
+	{
+		.mask = SFP_DIAGMON_DDM,
+		.val = SFP_DIAGMON_DDM,
+		.str = "ddm",
+	}, {
+		.mask = SFP_DIAGMON_INT_CAL,
+		.val = SFP_DIAGMON_INT_CAL,
+		.str = "intcal",
+	}, {
+		.mask = SFP_DIAGMON_EXT_CAL,
+		.val = SFP_DIAGMON_EXT_CAL,
+		.str = "extcal",
+	}, {
+		.mask = SFP_DIAGMON_RXPWR_AVG,
+		.val = SFP_DIAGMON_RXPWR_AVG,
+		.str = "rxpwravg",
+	}, { }
+};
+
+static const struct sff_bitfield sfp_enhopts[] = {
+	{
+		.mask = SFP_ENHOPTS_ALARMWARN,
+		.val = SFP_ENHOPTS_ALARMWARN,
+		.str = "alarmwarn",
+	}, {
+		.mask = SFP_ENHOPTS_SOFT_TX_DISABLE,
+		.val = SFP_ENHOPTS_SOFT_TX_DISABLE,
+		.str = "soft_tx_dis",
+	}, {
+		.mask = SFP_ENHOPTS_SOFT_TX_FAULT,
+		.val = SFP_ENHOPTS_SOFT_TX_FAULT,
+		.str = "soft_tx_fault",
+	}, {
+		.mask = SFP_ENHOPTS_SOFT_RX_LOS,
+		.val = SFP_ENHOPTS_SOFT_RX_LOS,
+		.str = "soft_rx_los",
+	}, {
+		.mask = SFP_ENHOPTS_SOFT_RATE_SELECT,
+		.val = SFP_ENHOPTS_SOFT_RATE_SELECT,
+		.str = "soft_rs",
+	}, {
+		.mask = SFP_ENHOPTS_APP_SELECT_SFF8079,
+		.val = SFP_ENHOPTS_APP_SELECT_SFF8079,
+		.str = "app_sel",
+	}, {
+		.mask = SFP_ENHOPTS_SOFT_RATE_SFF8431,
+		.val = SFP_ENHOPTS_SOFT_RATE_SFF8431,
+		.str = "soft_r8431",
+	}, { }
+};
+
 /* Helpers */
 static void sfp_module_tx_disable(struct sfp *sfp)
 {
@@ -2175,6 +2286,111 @@ static int sfp_cotsworks_fixup_check(struct sfp *sfp, struct sfp_eeprom_id *id)
 	return 0;
 }
 
+static void sfp_print_module_info(struct sfp *sfp,
+				  const struct sfp_eeprom_id *id, bool cotsworks)
+{
+	unsigned int br_nom, br_min, br_max;
+	char date[9];
+	char options[80];
+
+	/* Cotsworks also gets the date code wrong. */
+	date[0] = id->ext.datecode[4 - 2 * cotsworks];
+	date[1] = id->ext.datecode[5 - 2 * cotsworks];
+	date[2] = '-';
+	date[3] = id->ext.datecode[2 + 2 * cotsworks];
+	date[4] = id->ext.datecode[3 + 2 * cotsworks];
+	date[5] = '-';
+	date[6] = id->ext.datecode[0];
+	date[7] = id->ext.datecode[1];
+	date[8] = '\0';
+
+	if (id->base.br_nominal == 0) {
+		br_min = br_nom = br_max = 0;
+	} else if (id->base.br_nominal == 255) {
+		br_nom = 250 * id->ext.br_max;
+		br_max = br_nom + br_nom * id->ext.br_min / 100;
+		br_min = br_nom - br_nom * id->ext.br_min / 100;
+	} else {
+		br_nom = id->base.br_nominal * 100;
+		br_min = br_nom - id->base.br_nominal * id->ext.br_min;
+		br_max = br_nom + id->base.br_nominal * id->ext.br_max;
+	}
+
+	dev_info(sfp->dev, "module %.*s %.*s rev %.*s sn %.*s dc %s\n",
+		 (int)sizeof(id->base.vendor_name), id->base.vendor_name,
+		 (int)sizeof(id->base.vendor_pn), id->base.vendor_pn,
+		 (int)sizeof(id->base.vendor_rev), id->base.vendor_rev,
+		 (int)sizeof(id->ext.vendor_sn), id->ext.vendor_sn, date);
+	dev_info(sfp->dev, "  %s connector, encoding %s, bitrate %u.%03u (%u.%03u-%u.%03u) Gbps\n",
+		 sff_connector(id->base.connector),
+		 sff_encoding(id->base.encoding),
+		 br_nom / 1000, br_nom % 1000,
+		 br_min / 1000, br_min % 1000, br_max / 1000, br_max % 1000);
+	dev_info(sfp->dev, "  1000BaseSX%c 1000BaseLX%c 1000BaseCX%c 1000BaseT%c 100BaseLX%c 100BaseFX%c BaseBX10%c BasePX%c\n",
+		 id->base.e1000_base_sx ? '+' : '-',
+		 id->base.e1000_base_lx ? '+' : '-',
+		 id->base.e1000_base_cx ? '+' : '-',
+		 id->base.e1000_base_t ? '+' : '-',
+		 id->base.e100_base_lx ? '+' : '-',
+		 id->base.e100_base_fx ? '+' : '-',
+		 id->base.e_base_bx10 ? '+' : '-',
+		 id->base.e_base_px ? '+' : '-');
+	dev_info(sfp->dev, "  10GBaseSR%c 10GBaseLR%c 10GBaseLRM%c 10GBaseER%c\n",
+		 id->base.e10g_base_sr ? '+' : '-',
+		 id->base.e10g_base_lr ? '+' : '-',
+		 id->base.e10g_base_lrm ? '+' : '-',
+		 id->base.e10g_base_er ? '+' : '-');
+
+	if (!id->base.sfp_ct_passive && !id->base.sfp_ct_active &&
+	    !id->base.e1000_base_t) {
+		char len_9um[16], len_om[16];
+
+		dev_info(sfp->dev, "  Wavelength %unm, fiber lengths:\n",
+			 be16_to_cpup(&id->base.optical_wavelength));
+
+		if (id->base.link_len[0] == 255)
+			strcpy(len_9um, ">254km");
+		else if (id->base.link_len[1] && id->base.link_len[1] != 255)
+			sprintf(len_9um, "%um",
+				id->base.link_len[1] * 100);
+		else if (id->base.link_len[0])
+			sprintf(len_9um, "%ukm", id->base.link_len[0]);
+		else if (id->base.link_len[1] == 255)
+			strcpy(len_9um, ">25.4km");
+		else
+			strcpy(len_9um, "unsupported");
+
+		dev_info(sfp->dev, "    9µm SM    : %s\n", len_9um);
+		dev_info(sfp->dev, " 62.5µm MM OM1: %s\n",
+			 sff_link_len(len_om, sizeof(len_om),
+				      id->base.link_len[3], 10));
+		dev_info(sfp->dev, "   50µm MM OM2: %s\n",
+			 sff_link_len(len_om, sizeof(len_om),
+				      id->base.link_len[2], 10));
+		dev_info(sfp->dev, "   50µm MM OM3: %s\n",
+			 sff_link_len(len_om, sizeof(len_om),
+				      id->base.link_len[5], 10));
+		dev_info(sfp->dev, "   50µm MM OM4: %s\n",
+			 sff_link_len(len_om, sizeof(len_om),
+				      id->base.link_len[4], 10));
+	} else {
+		char len[16];
+		dev_info(sfp->dev, "  Copper length: %s\n",
+			 sff_link_len(len, sizeof(len),
+				      id->base.link_len[4], 1));
+	}
+
+	dev_info(sfp->dev, "  Options: %s\n",
+		 sff_bitfield(options, sizeof(options), sfp_options,
+			      be16_to_cpu(id->ext.options)));
+	dev_info(sfp->dev, "  Diagnostics: %s\n",
+		 sff_bitfield(options, sizeof(options), diagmon,
+			      id->ext.diagmon));
+	dev_info(sfp->dev, "  EnhOpts: %s\n",
+		 sff_bitfield(options, sizeof(options), sfp_enhopts,
+			      id->ext.enhopts));
+}
+
 static int sfp_module_parse_sff8472(struct sfp *sfp)
 {
 	/* If the module requires address swap mode, warn about it */
@@ -2241,9 +2457,9 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report)
 		}
 	}
 
-	/* Cotsworks do not seem to update the checksums when they
-	 * do the final programming with the final module part number,
-	 * serial number and date code.
+	/* Cotsworks do not seem to update the checksums when they update the
+	 * module part number, serial number and date code. They also format
+	 * the date code incorrectly.
 	 */
 	cotsworks = !memcmp(id.base.vendor_name, "COTSWORKS       ", 16);
 	cotsworks_sfbg = !memcmp(id.base.vendor_pn, "SFBG", 4);
@@ -2304,14 +2520,9 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report)
 		}
 	}
 
-	sfp->id = id;
+	sfp_print_module_info(sfp, &id, cotsworks);
 
-	dev_info(sfp->dev, "module %.*s %.*s rev %.*s sn %.*s dc %.*s\n",
-		 (int)sizeof(id.base.vendor_name), id.base.vendor_name,
-		 (int)sizeof(id.base.vendor_pn), id.base.vendor_pn,
-		 (int)sizeof(id.base.vendor_rev), id.base.vendor_rev,
-		 (int)sizeof(id.ext.vendor_sn), id.ext.vendor_sn,
-		 (int)sizeof(id.ext.datecode), id.ext.datecode);
+	sfp->id = id;
 
 	/* Check whether we support this module */
 	if (!sfp->type->module_supported(&id)) {
@@ -2345,8 +2556,11 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report)
 		mask |= SFP_F_RS0;
 	if (sfp->gpio[GPIO_RS1])
 		mask |= SFP_F_RS1;
+	if (id.ext.options & cpu_to_be16(SFP_OPTIONS_COOLED_XCVR))
+		sfp->module_t_start_up = T_START_UP_COOLED;
+	else
+		sfp->module_t_start_up = T_START_UP;
 
-	sfp->module_t_start_up = T_START_UP;
 	sfp->module_t_wait = T_WAIT;
 	sfp->phy_t_retry = T_PHY_RETRY;
 
@@ -2595,10 +2809,10 @@ static void sfp_sm_main(struct sfp *sfp, unsigned int event)
 			break;
 
 		if (sfp->state & SFP_F_TX_FAULT) {
-			/* Wait up to t_init (SFF-8472) or t_start_up (SFF-8431)
-			 * from the TX_DISABLE deassertion for the module to
-			 * initialise, which is indicated by TX_FAULT
-			 * deasserting.
+			/* Wait up to t_init (SFF-8472), t_start_up (SFF-8431),
+			 * or t_start_up_cooled (SFF-8431) from the TX_DISABLE
+			 * deassertion for the module to initialise, which is
+			 * indicated by TX_FAULT deasserting.
 			 */
 			timeout = sfp->module_t_start_up;
 			if (timeout > sfp->module_t_wait)
@@ -2617,8 +2831,8 @@ static void sfp_sm_main(struct sfp *sfp, unsigned int event)
 
 	case SFP_S_INIT:
 		if (event == SFP_E_TIMEOUT && sfp->state & SFP_F_TX_FAULT) {
-			/* TX_FAULT is still asserted after t_init
-			 * or t_start_up, so assume there is a fault.
+			/* TX_FAULT is still asserted after t_init, t_start_up
+			 * or t_start_up_cooled, so assume there is a fault.
 			 */
 			sfp_sm_fault(sfp, SFP_S_INIT_TX_FAULT,
 				     sfp->sm_fault_retries == N_FAULT_INIT);
diff --git a/include/linux/mdio.h b/include/linux/mdio.h
index efeca5bd7600..e298cc4c8804 100644
--- a/include/linux/mdio.h
+++ b/include/linux/mdio.h
@@ -32,6 +32,8 @@ struct mdio_device {
 	char modalias[MDIO_NAME_SIZE];
 
 	int (*bus_match)(struct device *dev, const struct device_driver *drv);
+	int (*bus_uevent)(struct mdio_device *mdiodev,
+			  struct kobj_uevent_env *env);
 	void (*device_free)(struct mdio_device *mdiodev);
 	void (*device_remove)(struct mdio_device *mdiodev);
 
diff --git a/include/linux/pcs/pcs-xpcs.h b/include/linux/pcs/pcs-xpcs.h
index b4a4eb6c8866..b5b5d17998b8 100644
--- a/include/linux/pcs/pcs-xpcs.h
+++ b/include/linux/pcs/pcs-xpcs.h
@@ -21,8 +21,6 @@
 #define DW_AN_C37_1000BASEX		4
 #define DW_10GBASER			5
 
-struct dw_xpcs_desc;
-
 enum dw_xpcs_pcs_id {
 	DW_XPCS_ID_NATIVE = 0,
 	NXP_SJA1105_XPCS_ID = 0x00000010,
@@ -48,33 +46,18 @@ struct dw_xpcs_info {
 	u32 pma;
 };
 
-enum dw_xpcs_clock {
-	DW_XPCS_CORE_CLK,
-	DW_XPCS_PAD_CLK,
-	DW_XPCS_NUM_CLKS,
-};
-
-struct dw_xpcs {
-	struct dw_xpcs_info info;
-	const struct dw_xpcs_desc *desc;
-	struct mdio_device *mdiodev;
-	struct clk_bulk_data clks[DW_XPCS_NUM_CLKS];
-	struct phylink_pcs pcs;
-	phy_interface_t interface;
-};
+struct dw_xpcs;
 
+struct phylink_pcs *xpcs_to_phylink_pcs(struct dw_xpcs *xpcs);
 int xpcs_get_an_mode(struct dw_xpcs *xpcs, phy_interface_t interface);
-void xpcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode,
-		  phy_interface_t interface, int speed, int duplex);
-int xpcs_do_config(struct dw_xpcs *xpcs, phy_interface_t interface,
-		   const unsigned long *advertising, unsigned int neg_mode);
 void xpcs_get_interfaces(struct dw_xpcs *xpcs, unsigned long *interfaces);
 int xpcs_config_eee(struct dw_xpcs *xpcs, int mult_fact_100ns,
 		    int enable);
-struct dw_xpcs *xpcs_create_mdiodev(struct mii_bus *bus, int addr,
-				    phy_interface_t interface);
-struct dw_xpcs *xpcs_create_fwnode(struct fwnode_handle *fwnode,
-				   phy_interface_t interface);
+struct dw_xpcs *xpcs_create_mdiodev(struct mii_bus *bus, int addr);
+struct dw_xpcs *xpcs_create_fwnode(struct fwnode_handle *fwnode);
 void xpcs_destroy(struct dw_xpcs *xpcs);
 
+struct phylink_pcs *xpcs_create_pcs_mdiodev(struct mii_bus *bus, int addr);
+void xpcs_destroy_pcs(struct phylink_pcs *pcs);
+
 #endif /* __LINUX_PCS_XPCS_H */
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 6b7d40d49129..e07deab4d3ad 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -707,6 +707,15 @@ struct phy_device {
 	u8 master_slave_set;
 	u8 master_slave_state;
 
+	/*
+	 * private to phylib: the resolved pause state - only valid if
+	 * resolved_pause_valid is true. only phy drivers and phylib
+	 * should touch this.
+	 */
+	bool resolved_pause_valid;
+	bool resolved_tx_pause;
+	bool resolved_rx_pause;
+
 	/* Union of PHY and Attached devices' supported link modes */
 	/* See ethtool.h for more info */
 	__ETHTOOL_DECLARE_LINK_MODE_MASK(supported);
@@ -722,6 +731,9 @@ struct phy_device {
 	/* Host supported PHY interface types. Should be ignored if empty. */
 	DECLARE_PHY_INTERFACE_MASK(host_interfaces);
 
+	/* supported PHY interface types */
+	DECLARE_PHY_INTERFACE_MASK(supported_interfaces);
+
 	/* Energy efficient ethernet modes which should be prohibited */
 	u32 eee_broken_modes;
 	bool enable_tx_lpi;
@@ -814,6 +826,24 @@ struct phy_tdr_config {
 };
 #define PHY_PAIR_ALL -1
 
+/**
+ * enum link_inband_signalling - inband signalling modes that are supported
+ *
+ * @LINK_INBAND_DISABLE: inband signalling can be disabled
+ * @LINK_INBAND_ENABLE: inband signalling can be enabled
+ * @LINK_INBAND_BYPASS: only valid with LINK_INBAND_ENABLE and supports bypass
+ *
+ * The possible and required bits can only be used if the valid bit is set.
+ * If possible is clear, that means inband signalling can not be used.
+ * Required is only valid when possible is set, and means that inband
+ * signalling must be used.
+ */
+enum link_inband_signalling {
+	LINK_INBAND_DISABLE		= BIT(0),
+	LINK_INBAND_ENABLE		= BIT(1),
+	LINK_INBAND_BYPASS		= BIT(2),
+};
+
 /**
  * struct phy_plca_cfg - Configuration of the PLCA (Physical Layer Collision
  * Avoidance) Reconciliation Sublayer.
@@ -952,6 +982,14 @@ struct phy_driver {
 	 */
 	int (*get_features)(struct phy_device *phydev);
 
+	/**
+	 * @query_inband: query whether inband is supported for the given PHY
+	 * interface mode. Returns a bitmask of bits defined by enum
+	 * link_inband_signalling.
+	 */
+	unsigned int (*query_inband)(struct phy_device *phydev,
+				     phy_interface_t interface);
+
 	/**
 	 * @get_rate_matching: Get the supported type of rate matching for a
 	 * particular phy interface. This is used by phy consumers to determine
@@ -970,6 +1008,9 @@ struct phy_driver {
 	/** @resume: Resume the hardware, restoring state if needed */
 	int (*resume)(struct phy_device *phydev);
 
+	int (*start)(struct phy_device *phydev);
+	void (*stop)(struct phy_device *phydev);
+
 	/**
 	 * @config_aneg: Configures the advertisement and resets
 	 * autonegotiation if phydev->autoneg is on,
@@ -1800,6 +1841,8 @@ int phy_config_aneg(struct phy_device *phydev);
 int _phy_start_aneg(struct phy_device *phydev);
 int phy_start_aneg(struct phy_device *phydev);
 int phy_aneg_done(struct phy_device *phydev);
+unsigned int phy_query_inband(struct phy_device *phydev,
+			      phy_interface_t interface);
 int phy_speed_down(struct phy_device *phydev, bool sync);
 int phy_speed_up(struct phy_device *phydev);
 bool phy_check_valid(int speed, int duplex, unsigned long *features);
diff --git a/include/linux/phylink.h b/include/linux/phylink.h
index 2381e07429a2..a2d10259a134 100644
--- a/include/linux/phylink.h
+++ b/include/linux/phylink.h
@@ -5,6 +5,8 @@
 #include <linux/spinlock.h>
 #include <linux/workqueue.h>
 
+#include <net/eee.h>
+
 struct device_node;
 struct ethtool_cmd;
 struct fwnode_handle;
@@ -93,11 +95,26 @@ enum {
 	MAC_400000FD	= BIT(18),
 };
 
-static inline bool phylink_autoneg_inband(unsigned int mode)
+static inline bool phylink_mode_inband(unsigned int mode)
 {
 	return mode == MLO_AN_INBAND;
 }
 
+static inline bool phylink_mode_fixed(unsigned int mode)
+{
+	return mode == MLO_AN_FIXED;
+}
+
+static inline bool phylink_mode_phy(unsigned int mode)
+{
+	return mode == MLO_AN_PHY;
+}
+
+static inline bool phylink_autoneg_inband(unsigned int mode)
+{
+	return phylink_mode_inband(mode);
+}
+
 /**
  * struct phylink_link_state - link state structure
  * @advertising: ethtool bitmask containing advertised link modes
@@ -143,11 +160,15 @@ enum phylink_op_type {
  *                    possible and avoid stopping it during suspend events.
  * @default_an_inband: if true, defaults to MLO_AN_INBAND rather than
  *		       MLO_AN_PHY. A fixed-link specification will override.
+ * @eee_clk_stop_enable: if true, PHY can stop the receive clock during LPI
  * @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.
  * @mac_capabilities: MAC pause/speed/duplex capabilities.
+ * @lpi_capabilities: MAC speeds which can support LPI signalling
+ * @eee: default EEE configuration.
+ * @lpi_timer_limit_us: Maximum (inclusive) value of the EEE LPI timer.
  */
 struct phylink_config {
 	struct device *dev;
@@ -156,10 +177,14 @@ struct phylink_config {
 	bool mac_managed_pm;
 	bool mac_requires_rxc;
 	bool default_an_inband;
+	bool eee_clk_stop_enable;
 	void (*get_fixed_state)(struct phylink_config *config,
 				struct phylink_link_state *state);
 	DECLARE_PHY_INTERFACE_MASK(supported_interfaces);
 	unsigned long mac_capabilities;
+	unsigned long lpi_capabilities;
+	struct eee_config eee;
+	u32 lpi_timer_limit_us;
 };
 
 void phylink_limit_mac_speed(struct phylink_config *config, u32 max_speed);
@@ -173,6 +198,8 @@ void phylink_limit_mac_speed(struct phylink_config *config, u32 max_speed);
  * @mac_finish: finish a major reconfiguration of the interface.
  * @mac_link_down: take the link down.
  * @mac_link_up: allow the link to come up.
+ * @mac_disable_tx_lpi: disable LPI.
+ * @mac_enable_tx_lpi: enable and configure LPI.
  *
  * The individual methods are described more fully below.
  */
@@ -193,6 +220,8 @@ struct phylink_mac_ops {
 			    struct phy_device *phy, unsigned int mode,
 			    phy_interface_t interface, int speed, int duplex,
 			    bool tx_pause, bool rx_pause);
+	void (*mac_disable_tx_lpi)(struct phylink_config *config);
+	void (*mac_enable_tx_lpi)(struct phylink_config *config, u32 timer);
 };
 
 #if 0 /* For kernel-doc purposes only. */
@@ -387,6 +416,28 @@ void mac_link_down(struct phylink_config *config, unsigned int mode,
 void mac_link_up(struct phylink_config *config, struct phy_device *phy,
 		 unsigned int mode, phy_interface_t interface,
 		 int speed, int duplex, bool tx_pause, bool rx_pause);
+
+/**
+ * mac_disable_tx_lpi() - disable LPI generation at the MAC
+ * @config: a pointer to a &struct phylink_config.
+ *
+ * Disable generation of LPI at the MAC, effectively preventing the MAC
+ * from indicating that it is idle.
+ */
+void mac_disable_tx_lpi(struct phylink_config *config);
+
+/**
+ * mac_enable_tx_lpi() - configure and enable LPI generation at the MAC
+ * @config: a pointer to a &struct phylink_config.
+ * @timer: LPI timeout in microseconds.
+ *
+ * Configure the LPI timeout accordingly. This will only be called when
+ * the link is already up, to cater for situations where the hardware
+ * needs to be programmed according to the link speed.
+ *
+ * Enable LPI generation at the MAC.
+ */
+void mac_enable_tx_lpi(struct phylink_config *config, u32 timer);
 #endif
 
 struct phylink_pcs_ops;
@@ -419,6 +470,7 @@ struct phylink_pcs {
 /**
  * struct phylink_pcs_ops - MAC PCS operations structure.
  * @pcs_validate: validate the link configuration.
+ * @pcs_query_inband: query inband support for interface mode.
  * @pcs_enable: enable the PCS.
  * @pcs_disable: disable the PCS.
  * @pcs_pre_config: pre-mac_config method (for errata)
@@ -434,6 +486,8 @@ struct phylink_pcs {
 struct phylink_pcs_ops {
 	int (*pcs_validate)(struct phylink_pcs *pcs, unsigned long *supported,
 			    const struct phylink_link_state *state);
+	unsigned int (*pcs_query_inband)(struct phylink_pcs *pcs,
+					 phy_interface_t interface);
 	int (*pcs_enable)(struct phylink_pcs *pcs);
 	void (*pcs_disable)(struct phylink_pcs *pcs);
 	void (*pcs_pre_config)(struct phylink_pcs *pcs,
@@ -470,6 +524,20 @@ struct phylink_pcs_ops {
 int pcs_validate(struct phylink_pcs *pcs, unsigned long *supported,
 		 const struct phylink_link_state *state);
 
+/**
+ * pcs_query_inband - query inband support for interface mode.
+ * @pcs: a pointer to a &struct phylink_pcs.
+ * @interface: interface mode to be queried
+ *
+ * Returns zero if it is unknown what in-band signalling is supported by the
+ * PHY (e.g. because the PHY driver doesn't implement the method.) Otherwise,
+ * returns a bit mask of the LINK_INBAND_* values from
+ * &enum link_inband_signalling to describe which inband modes are supported
+ * for this interface mode.
+ */
+unsigned int pcs_query_inband(struct phylink_pcs *pcs,
+			      phy_interface_t interface);
+
 /**
  * pcs_enable() - enable the PCS.
  * @pcs: a pointer to a &struct phylink_pcs.
diff --git a/include/net/dsa.h b/include/net/dsa.h
index d7a6c2930277..72ae65e7246a 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -885,21 +885,6 @@ struct dsa_switch_ops {
 	 */
 	void	(*phylink_get_caps)(struct dsa_switch *ds, int port,
 				    struct phylink_config *config);
-	struct phylink_pcs *(*phylink_mac_select_pcs)(struct dsa_switch *ds,
-						      int port,
-						      phy_interface_t iface);
-	void	(*phylink_mac_config)(struct dsa_switch *ds, int port,
-				      unsigned int mode,
-				      const struct phylink_link_state *state);
-	void	(*phylink_mac_link_down)(struct dsa_switch *ds, int port,
-					 unsigned int mode,
-					 phy_interface_t interface);
-	void	(*phylink_mac_link_up)(struct dsa_switch *ds, int port,
-				       unsigned int mode,
-				       phy_interface_t interface,
-				       struct phy_device *phydev,
-				       int speed, int duplex,
-				       bool tx_pause, bool rx_pause);
 	void	(*phylink_fixed_state)(struct dsa_switch *ds, int port,
 				       struct phylink_link_state *state);
 	/*
diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c
index 668c729946ea..ceeadb52d1cc 100644
--- a/net/dsa/dsa.c
+++ b/net/dsa/dsa.c
@@ -1505,12 +1505,9 @@ static int dsa_switch_probe(struct dsa_switch *ds)
 	if (!ds->num_ports)
 		return -EINVAL;
 
-	if (ds->phylink_mac_ops) {
-		if (ds->ops->phylink_mac_select_pcs ||
-		    ds->ops->phylink_mac_config ||
-		    ds->ops->phylink_mac_link_down ||
-		    ds->ops->phylink_mac_link_up)
-			return -EINVAL;
+	if (!ds->phylink_mac_ops) {
+		dev_err(ds->dev, "DSA switch driver does not provide phylink MAC operations");
+		return -EINVAL;
 	}
 
 	if (np) {
diff --git a/net/dsa/port.c b/net/dsa/port.c
index 25258b33e59e..2fc432797fbd 100644
--- a/net/dsa/port.c
+++ b/net/dsa/port.c
@@ -1575,73 +1575,8 @@ void dsa_port_set_tag_protocol(struct dsa_port *cpu_dp,
 	cpu_dp->tag_ops = tag_ops;
 }
 
-static struct phylink_pcs *
-dsa_port_phylink_mac_select_pcs(struct phylink_config *config,
-				phy_interface_t interface)
-{
-	struct dsa_port *dp = dsa_phylink_to_port(config);
-	struct phylink_pcs *pcs = ERR_PTR(-EOPNOTSUPP);
-	struct dsa_switch *ds = dp->ds;
-
-	if (ds->ops->phylink_mac_select_pcs)
-		pcs = ds->ops->phylink_mac_select_pcs(ds, dp->index, interface);
-
-	return pcs;
-}
-
-static void dsa_port_phylink_mac_config(struct phylink_config *config,
-					unsigned int mode,
-					const struct phylink_link_state *state)
-{
-	struct dsa_port *dp = dsa_phylink_to_port(config);
-	struct dsa_switch *ds = dp->ds;
-
-	if (!ds->ops->phylink_mac_config)
-		return;
-
-	ds->ops->phylink_mac_config(ds, dp->index, mode, state);
-}
-
-static void dsa_port_phylink_mac_link_down(struct phylink_config *config,
-					   unsigned int mode,
-					   phy_interface_t interface)
-{
-	struct dsa_port *dp = dsa_phylink_to_port(config);
-	struct dsa_switch *ds = dp->ds;
-
-	if (!ds->ops->phylink_mac_link_down)
-		return;
-
-	ds->ops->phylink_mac_link_down(ds, dp->index, mode, interface);
-}
-
-static void dsa_port_phylink_mac_link_up(struct phylink_config *config,
-					 struct phy_device *phydev,
-					 unsigned int mode,
-					 phy_interface_t interface,
-					 int speed, int duplex,
-					 bool tx_pause, bool rx_pause)
-{
-	struct dsa_port *dp = dsa_phylink_to_port(config);
-	struct dsa_switch *ds = dp->ds;
-
-	if (!ds->ops->phylink_mac_link_up)
-		return;
-
-	ds->ops->phylink_mac_link_up(ds, dp->index, mode, interface, phydev,
-				     speed, duplex, tx_pause, rx_pause);
-}
-
-static const struct phylink_mac_ops dsa_port_phylink_mac_ops = {
-	.mac_select_pcs = dsa_port_phylink_mac_select_pcs,
-	.mac_config = dsa_port_phylink_mac_config,
-	.mac_link_down = dsa_port_phylink_mac_link_down,
-	.mac_link_up = dsa_port_phylink_mac_link_up,
-};
-
 int dsa_port_phylink_create(struct dsa_port *dp)
 {
-	const struct phylink_mac_ops *mac_ops;
 	struct dsa_switch *ds = dp->ds;
 	phy_interface_t mode;
 	struct phylink *pl;
@@ -1665,12 +1600,8 @@ int dsa_port_phylink_create(struct dsa_port *dp)
 		}
 	}
 
-	mac_ops = &dsa_port_phylink_mac_ops;
-	if (ds->phylink_mac_ops)
-		mac_ops = ds->phylink_mac_ops;
-
 	pl = phylink_create(&dp->pl_config, of_fwnode_handle(dp->dn), mode,
-			    mac_ops);
+			    ds->phylink_mac_ops);
 	if (IS_ERR(pl)) {
 		pr_err("error creating PHYLINK: %ld\n", PTR_ERR(pl));
 		return PTR_ERR(pl);
@@ -1871,9 +1802,6 @@ static void dsa_shared_port_link_down(struct dsa_port *dp)
 	if (ds->phylink_mac_ops && ds->phylink_mac_ops->mac_link_down)
 		ds->phylink_mac_ops->mac_link_down(&dp->pl_config, MLO_AN_FIXED,
 						   PHY_INTERFACE_MODE_NA);
-	else if (ds->ops->phylink_mac_link_down)
-		ds->ops->phylink_mac_link_down(ds, dp->index, MLO_AN_FIXED,
-					       PHY_INTERFACE_MODE_NA);
 }
 
 int dsa_shared_port_link_register_of(struct dsa_port *dp)
diff --git a/net/dsa/user.c b/net/dsa/user.c
index f5adfa1d978a..04d3606dffb9 100644
--- a/net/dsa/user.c
+++ b/net/dsa/user.c
@@ -1231,16 +1231,21 @@ static int dsa_user_set_eee(struct net_device *dev, struct ethtool_keee *e)
 	struct dsa_switch *ds = dp->ds;
 	int ret;
 
-	/* Port's PHY and MAC both need to be EEE capable */
-	if (!dev->phydev || !dp->pl)
-		return -ENODEV;
+	/* If the port is using phylink managed EEE, then get_mac_eee is
+	 * unnecessary.
+	 */
+	if (!dp->pl_config.lpi_capabilities) {
+		/* Port's PHY and MAC both need to be EEE capable */
+		if (!dev->phydev || !dp->pl)
+			return -ENODEV;
 
-	if (!ds->ops->set_mac_eee)
-		return -EOPNOTSUPP;
+		if (!ds->ops->set_mac_eee)
+			return -EOPNOTSUPP;
 
-	ret = ds->ops->set_mac_eee(ds, dp->index, e);
-	if (ret)
-		return ret;
+		ret = ds->ops->set_mac_eee(ds, dp->index, e);
+		if (ret)
+			return ret;
+	}
 
 	return phylink_ethtool_set_eee(dp->pl, e);
 }
@@ -1251,16 +1256,21 @@ static int dsa_user_get_eee(struct net_device *dev, struct ethtool_keee *e)
 	struct dsa_switch *ds = dp->ds;
 	int ret;
 
-	/* Port's PHY and MAC both need to be EEE capable */
-	if (!dev->phydev || !dp->pl)
-		return -ENODEV;
+	/* If the port is using phylink managed EEE, then get_mac_eee is
+	 * unnecessary.
+	 */
+	if (!dp->pl_config.lpi_capabilities) {
+		/* Port's PHY and MAC both need to be EEE capable */
+		if (!dev->phydev || !dp->pl)
+			return -ENODEV;
 
-	if (!ds->ops->get_mac_eee)
-		return -EOPNOTSUPP;
+		if (!ds->ops->get_mac_eee)
+			return -EOPNOTSUPP;
 
-	ret = ds->ops->get_mac_eee(ds, dp->index, e);
-	if (ret)
-		return ret;
+		ret = ds->ops->get_mac_eee(ds, dp->index, e);
+		if (ret)
+			return ret;
+	}
 
 	return phylink_ethtool_get_eee(dp->pl, e);
 }
@@ -2615,7 +2625,8 @@ static int dsa_user_phy_setup(struct net_device *user_dev)
 	if (ret) {
 		netdev_err(user_dev, "failed to connect to PHY: %pe\n",
 			   ERR_PTR(ret));
-		dsa_port_phylink_destroy(dp);
+//		dsa_port_phylink_destroy(dp);
+		ret = 0;
 	}
 
 	return ret;
