# Base git commit: a38297e3fb01
# (Linux 6.9)
# 
# Author:    Russell King (Tue  3 Mar 21:59:52 GMT 2020)
# Committer: Russell King (Oracle) (Thu 30 May 11:16:47 BST 2024)
# 
#   Revert "iommu: silence iommu group prints"
# 
#   This reverts commit 842002f3ef06e6ca88d90968733878660994b5b8.
# 
#   deff33f26f66b34d269d2f6fc4654e46e9ad6db9
#    drivers/iommu/iommu.c | 4 ++--
#    1 file changed, 2 insertions(+), 2 deletions(-)
# 
# Author:    Russell King (Fri 24 Jan 17:59:49 GMT 2020)
# Committer: Russell King (Oracle) (Thu 30 May 11:16:47 BST 2024)
# 
#   iommu: silence iommu group prints
# 
#   On the LX2160A, there are lots (about 160) of IOMMU messages produced
#   during boot; this is excessive.  Reduce the severity of these messages
#   to debug level.
# 
#   Signed-off-by: Russell King
# 
#   4df2a8edfa00bd864f814fb920b30d637c32b2bd
#    drivers/iommu/iommu.c | 4 ++--
#    1 file changed, 2 insertions(+), 2 deletions(-)
# 
# Author:    Russell King (Thu 12 Sep 13:30:26 BST 2019)
# Committer: Russell King (Oracle) (Thu 30 May 11:16:47 BST 2024)
# 
#   arm64: dts: lx2160a-clearfog-cx: add QSFP support  [*experimental*]
# 
#   Add QSFP cage network interfaces.
# 
#   Signed-off-by: Russell King
# 
#   6e8393697b3e7fc8229d7e32b0964ff4a863f248
#    .../boot/dts/freescale/fsl-lx2160a-clearfog-cx.dts | 51 ++++++++++++++++++++++
#    1 file changed, 51 insertions(+)
# 
# Author:    Russell King (Fri  4 Oct 17:45:58 BST 2019)
# Committer: Russell King (Oracle) (Thu 30 May 11:16:47 BST 2024)
# 
#   net: add qsfp support                              [*experimental*]
# 
#   Add experimental QSFP+ support for the SolidRun Clearfog-CX
#   platform.
# 
#   Signed-off-by: Russell King
# 
#   88061f0e8a4eff5f363afcd685f562dc8f79cb0e
#    drivers/net/phy/Kconfig  |    6 +
#    drivers/net/phy/Makefile |    1 +
#    drivers/net/phy/qsfp.c   | 1835 ++++++++++++++++++++++++++++++++++++++++++++++
#    include/linux/sfp.h      |  111 ++-
#    4 files changed, 1952 insertions(+), 1 deletion(-)
#    create mode 100644 drivers/net/phy/qsfp.c
# 
# Author:    Russell King (Fri 31 Jan 15:45:13 GMT 2020)
# Committer: Russell King (Oracle) (Thu 30 May 11:16:45 BST 2024)
# 
#   net: sfp: clean up sfp-bus building
# 
#   Use a Kconfig symbol to control the build of sfp-bus.c
# 
#   Signed-off-by: Russell King
# 
#   25554bf6800dfa354a9186a77c5d3e4d53f99e50
#    drivers/net/phy/Kconfig  | 4 ++++
#    drivers/net/phy/Makefile | 3 +--
#    include/linux/sfp.h      | 2 +-
#    3 files changed, 6 insertions(+), 3 deletions(-)
# 
# Author:    Peng Ma (Sat 29 Feb 19:14:54 GMT 2020)
# Committer: Russell King (Oracle) (Thu 30 May 11:16:45 BST 2024)
# 
#   ahci: qoriq: workaround for errata A-379364 on lx2160a
# 
#   There is a erratum on lx2160a which is: "SATA link is
#   going down sometime during sata initialization"
#   The workaround for it is to reset the lane. This patch
#   implements this workaround.
#   This erratum only exists on lx2160 Rev1, will be addressed
#   on Rev2 and later.
# 
#   Signed-off-by: Peng Ma
#   Signed-off-by: Russell King
# 
#   9e09bb918778f9fef0ea943d98604733ed01324d
#    drivers/ata/ahci_qoriq.c | 144 +++++++++++++++++++++++++++++++++++++++++++++++
#    1 file changed, 144 insertions(+)
# 
# Author:    Russell King (Tue 24 Dec 14:46:48 GMT 2019)
# Committer: Russell King (Oracle) (Thu 30 May 11:16:45 BST 2024)
# 
#   bus: fsl-mc: fix dprc object reading race
# 
#   When modifying the objects attached to a DPRC, we may end up reading
#   the list of objects from the firmware while another thread is changing
#   changing the list. Since we read the objects via:
# 
#   - Read the number of DPRC objects
#   - Iterate over this number of objects retrieving their details
# 
#   and objects can be added in the middle of the list, this causes the
#   last few objects to unexpectedly disappear. The side effect of this
#   is if network interfaces are added after boot, they come and go. This
#   can result in already configured interfaces unexpectedly disappearing.
# 
#   This has been easy to provoke with the restool interface added, and a
#   script which adds network interfaces one after each other; the kernel
#   rescanning runs asynchronously to restool.
# 
#   NXP's approach to fixing this was to introduce a sysfs "attribute" in
#   their vendor tree, /sys/bus/fsl-mc/rescan, which userspace poked at to
#   request the kernel to rescan the DPRC object tree each time the
#   "restool" command completed (whether or not the tool changed anything.)
#   This has the effect of making the kernel's rescan synchronous with a
#   scripted restool, but still fails if we have multiple restools running
#   concurrently.
# 
#   This patch takes a different approach:
#   - Read the number of DPRC objects
#   - Iterate over this number of objects retrieving their details
#   - Re-read the number of DPRC objects
#   - If the number of DPRC objects has changed while reading, repeat.
# 
#   This solves the issue where network interfaces unexpectedly disappear
#   while adding others via ls-addni, because they've fallen off the end
#   of the object list.
# 
#   This does *not* solve the issue that if an object is deleted while
#   another is added while we are reading the objects - that requires
#   firmware modification, or a more elaborate solution on the Linux side
#   (e.g., CRCing the object details and reading all objects at least
#   twice to check the CRC is stable.)
# 
#   However, without firmware modification, this is probably the best way
#   to ensure that we read all the objects.
# 
#   Signed-off-by: Russell King
# 
#   dfac68813d4be4391e471631fc644e31283c77c1
#    drivers/bus/fsl-mc/dprc-driver.c | 32 +++++++++++++++++++++++++++++---
#    1 file changed, 29 insertions(+), 3 deletions(-)
# 
# Author:    Russell King (Oracle) (Thu 30 Dec 13:49:12 GMT 2021)
# Committer: Russell King (Oracle) (Thu 30 May 11:16:45 BST 2024)
# 
#   bus: fsl-mc: add IOMMU mappings for MC firmware and DCFG
# 
#   The MC firmware (which runs the networking subsystem) is loaded into
#   RAM by U-Boot, and this region is omitted from the memory passed to
#   the kernel via DT. Prior to booting the kernel, the MC processing is
#   halted to allow IOMMU setup.
# 
#   When booting the kernel with IOMMU support enabled and without using
#   both bypass and passthrough mode, the MC firmware crashes as soon as
#   it is released, as the MC is unable to access the RAM that has been
#   assigned to it for both the firmware image and other purposes, and
#   also the DCFG to retrieve the SoC version.
# 
#   In order to avoid this, we need to setup identity mappings in the MC
#   domain. For the MC RAM region, we read the firmware base address
#   registers which tell us where the firmware is located. According to
#   the MC design document, the firmware is loaded within the upper 512M
#   of the MC RAM region, aligned to 512M, and the RAM region is also
#   aligned to 512M. The lower 8 bits of the firmware base address low
#   register tells us how large the RAM region is. Use this to calculate
#   its size and location in order to create an indentity mapping.
# 
#   We also search DT for the DCFG node to retrieve its address, and create
#   a read-only identity mapping to allow the MC firmware to read the SoC
#   version. If we are unable to find the DCFG node, we use a default
#   address for this. [XXX This needs to be improved XXX]
# 
#   This allows "arm-smmu.disable_bypass=1" to be dropped from the kernel
#   command line for LX2160A platforms.
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   df4dabacf2776fc6a9cbac85957fa7ac3279f1fd
#    drivers/bus/fsl-mc/fsl-mc-bus.c | 90 +++++++++++++++++++++++++++++++++++++++++
#    1 file changed, 90 insertions(+)
# 
# Author:    Laurentiu Tudor (Tue 19 Nov 15:01:39 GMT 2019)
# Committer: Russell King (Oracle) (Thu 30 May 11:16:44 BST 2024)
# 
#   arm64: dts: lx2160a: add iommus property for mc node
# 
#   Enable SMMU management for the MC firmware by adding the required
#   iommus property in the device tree node.
# 
#   Signed-off-by: Laurentiu Tudor
# 
#   64eadad4570a9cf5ac126faf6991a63166a4be97
#    arch/arm64/boot/dts/freescale/fsl-lx2160a.dtsi | 1 +
#    1 file changed, 1 insertion(+)
# 
# Author:    Russell King (Oracle) (Thu 30 May 11:16:41 BST 2024)
# Committer: Russell King (Oracle) (Thu 30 May 11:16:41 BST 2024)
# 
#   Merge branches 'net-queue' and 'pci-mobiveil' into cex7
# 
# Author:    Russell King (Oracle) (Thu 17 Aug 21:56:27 BST 2023)
# Committer: Russell King (Oracle) (Thu 30 May 11:16:23 BST 2024)
# 
#   net: dsa: mv88e6xxx: add 6352 family EEE support
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   6b9c5f55b6d54bd549f0ab30ae43e2e8f993cabb
#    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) (Thu 30 May 11:16:23 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)
# 
#   02c2931474b7f20d18672d5eb0c5a6e75bcbd1c5
#    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) (Thu 30 May 11:16:23 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)
# 
#   a9f3196c78a6ce9c91532f56730284860d9f6afa
#    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) (Thu 30 May 11:16:22 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)
# 
#   8312468d4a6a6cd864c055acdcc01b7dea3b4c7d
#    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) (Thu 30 May 11:16:22 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
# 
#   715a6e142694eb402059adf3824cc803a526eca3
#    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) (Thu 30 May 11:16:22 BST 2024)
# 
#   net: phy: add supported_interfaces to Aquantia AQR113C
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   9a749c7296347e63a6628f26d55e7216622b97e9
#    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) (Thu 30 May 11:16:21 BST 2024)
# 
#   net: phy: add supported_interfaces to marvell10g PHYs
# 
#   Signed-off-by: Russell King
# 
#   925baf40a1e1d9cbe41fbe520adbf92efa53e856
#    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) (Thu 30 May 11:16:21 BST 2024)
# 
#   net: phy: add supported_interfaces to marvell PHYs
# 
#   Signed-off-by: Russell King
# 
#   b55b6aa915d72b2d1f86278409a120821f7048e9
#    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) (Thu 30 May 11:16:21 BST 2024)
# 
#   net: phy: add supported_interfaces to bcm84881
# 
#   Signed-off-by: Russell King
# 
#   bb17e6fa6969f24f93c960dcdd4b7cf0e2fb8f60
#    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) (Thu 30 May 11:16:21 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
# 
#   3554e0c605eaf1095833fbddae02493dc595329d
#    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) (Thu 30 May 11:16:20 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
# 
#   b0575b1460d2cbf1f0b916aa45e159a1a58555c4
#    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) (Thu 30 May 11:16:20 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
# 
#   16bf1dd8429447cd1c49baedb893ff8808218c23
#    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) (Thu 30 May 11:16:20 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
# 
#   47abcc45cca3796ed26236b36f2070f202220373
#    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) (Thu 30 May 11:16:20 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
# 
#   d00642476f6b512b1699d0bfb1ed2c436bf989cc
#    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) (Thu 30 May 11:16:19 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
# 
#   a4871e774793ae3225c13f6c7e5dca44edd73585
#    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) (Thu 30 May 11:16:19 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
# 
#   4c3c1940cf36751c4f44cffb12b4cc99a7398ae7
#    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) (Thu 30 May 11:16:19 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
# 
#   b445b02c41fd6360ce734ff1e674c349051602f2
#    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) (Thu 30 May 11:16:18 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
# 
#   dda1d8ef41db1ea9ede70709cd2c1f5b1d9ca1d2
#    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) (Thu 30 May 11:16:18 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)
# 
#   f055e60cfdf4ca30efd538507dca94e76f713bfc
#    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) (Thu 30 May 11:16:18 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
# 
#   1426d1a0f33ca7e2a8e61c8e8f86b55e1aa81aef
#    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) (Thu 30 May 11:16:17 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
# 
#   6b015db1051b7f55ffad05b3809a53f35db5d2bf
#    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) (Thu 30 May 11:16:17 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
# 
#   d8785f125ec0c2c43af3744335ac6ca0766a006e
#    .../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) (Thu 30 May 11:16:17 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)
# 
#   d0d70409fa50ec6c8889a66d3e107210110815ea
#    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) (Thu 30 May 11:16:17 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)
# 
#   617f94c8d224d9a7bf516f4187e87b211ab19133
#    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) (Thu 30 May 11:16:16 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)
# 
#   1a58d07d626a4cff7d861dabcd6798c8208ac574
#    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) (Thu 30 May 11:16:16 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)
# 
#   7a7f8ba5ebc2fc800568928cc7a392e1ebaddaa8
#    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) (Thu 30 May 11:16:16 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)
# 
#   76ef50e1e20250ec60e4b86b11416b24b8292d2d
#    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) (Thu 30 May 11:16:16 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)
# 
#   05a11bbb90d53408c6724c3950c1760c54e3e7ae
#    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) (Thu 30 May 11:16:15 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)
# 
#   9a985e79483e1902d22396f34a48f73239c9bdc5
#    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) (Thu 30 May 11:16:15 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)
# 
#   ad4c6edb6d8dd2fe922ecd143be238fd66d7ab9c
#    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) (Thu 30 May 11:16:15 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)
# 
#   fc4120b05f341dbe74b579f49c9ccfe5faf623b9
#    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) (Thu 30 May 11:16:14 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)
# 
#   d164af1bc5c39862140df9faa9931c97ee24f663
#    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) (Thu 30 May 11:16:14 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)
# 
#   9a0837bc44fc796db1c75b6a9ddc8117f2cafe96
#    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) (Thu 30 May 11:16:14 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)
# 
#   ef82efea41ef7f308e4a842a11a6f127d030cc5d
#    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) (Thu 30 May 11:16:14 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)
# 
#   693976e7b3e9a09315b161ac97ae138e01c83bad
#    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) (Thu 30 May 11:16:13 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)
# 
#   bbfa20c4b8a04460d4d28c117c1b9909433659c6
#    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) (Thu 30 May 11:16:13 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)
# 
#   2cb90a3717f8c08e81784fd5078bb3ae8137e6bd
#    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) (Thu 30 May 11:16:12 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)
# 
#   d956879639698c18c95dc428c19efee3a75e66c1
#    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) (Thu 30 May 11:16:11 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)
# 
#   0b4f35564a8749a990aaca98614ca02e1d6f13a8
#    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) (Thu 30 May 11:16:11 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)
# 
#   75ffbe039932f42acb27f5e9ee08829d551c77a1
#    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) (Thu 30 May 11:16:10 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)
# 
#   59984040378e742548483930824f9fda38d1f927
#    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) (Thu 30 May 11:16:10 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)
# 
#   2c2f32514b72ca5ad5e73b75a153e2c5bab6f46d
#    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) (Thu 30 May 11:16:10 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)
# 
#   99bc6bfa5f05a63492363f4a75e11b59e97934b7
#    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) (Thu 30 May 11:16:10 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)
# 
#   f580924bcdcd60b72b7862776cb57cfe6ca1dfcb
#    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) (Thu 30 May 11:16:09 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)
# 
#   ae8673c0c1281775968f4973d4035a92a594f6fe
#    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) (Thu 30 May 11:16:09 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)
# 
#   2dd9b260c032f6bc9af085817e88fd3c27fc2219
#    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) (Thu 30 May 11:16:09 BST 2024)
# 
#   net: phylink: add EEE management
# 
#   Add EEE management to phylink.
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   c448a167a55ab8280afba4359ba4e89d015ab47f
#    drivers/net/phy/phylink.c | 145 ++++++++++++++++++++++++++++++++++++++++++++--
#    include/linux/phylink.h   |  36 ++++++++++++
#    2 files changed, 176 insertions(+), 5 deletions(-)
# 
# Author:    Russell King (Oracle) (Fri 24 May 22:04:13 BST 2024)
# Committer: Russell King (Oracle) (Thu 30 May 11:16:05 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)
# 
#   827a90f5a50626f93e1da7056c3d6992c10063c0
#    include/net/dsa.h | 15 -----------
#    net/dsa/dsa.c     |  9 +++----
#    net/dsa/port.c    | 74 +------------------------------------------------------
#    3 files changed, 4 insertions(+), 94 deletions(-)
# 
# Author:    Russell King (Oracle) (Wed 29 May 12:26:57 BST 2024)
# Committer: Russell King (Oracle) (Thu 30 May 11:16:04 BST 2024)
# 
#   net: stmmac: dwmac-intel: remove checking for fixed link
# 
#   With the new default_an_inband functionality in phylink, there is no
#   need to check for a fixed link when this flag is set, since a fixed
#   link will now override default_an_inband.
# 
#   Reviewed-by: Andrew Halaney
#   Signed-off-by: Russell King (Oracle)
# 
#   22110d2602fd0dacaa88b51d90da69ffb446fdbf
#    drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c | 11 -----------
#    1 file changed, 11 deletions(-)
# 
# Author:    Russell King (Oracle) (Wed 29 May 12:26:56 BST 2024)
# Committer: Russell King (Oracle) (Thu 30 May 11:16:04 BST 2024)
# 
#   net: stmmac: rename xpcs_an_inband to default_an_inband
# 
#   Rename xpcs_an_inband to default_an_inband to reflect the change in
#   phylink and its changed functionality.
# 
#   Reviewed-by: Andrew Halaney
#   Signed-off-by: Russell King (Oracle)
# 
#   a6a759af3cb6e98a0139d9c2845e74c13cd2f17d
#    drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c | 8 ++++----
#    drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 2 +-
#    include/linux/stmmac.h                            | 2 +-
#    3 files changed, 6 insertions(+), 6 deletions(-)
# 
# Author:    Russell King (Oracle) (Wed 29 May 12:26:56 BST 2024)
# Committer: Russell King (Oracle) (Thu 30 May 11:16:04 BST 2024)
# 
#   net: fman_memac: remove the now unnecessary checking for fixed-link
# 
#   Since default_an_inband can be overriden by a fixed-link specification,
#   there is no need for memac to be checking for this before setting
#   default_an_inband. Remove this code and update the comment.
# 
#   Reviewed-by: Andrew Halaney
#   Signed-off-by: Russell King (Oracle)
# 
#   1b5009138bf4e3a8f64a22ca655cb2b692e9e5de
#    drivers/net/ethernet/freescale/fman/fman_memac.c | 14 +++++---------
#    1 file changed, 5 insertions(+), 9 deletions(-)
# 
# Author:    Russell King (Oracle) (Wed 29 May 12:26:53 BST 2024)
# Committer: Russell King (Oracle) (Thu 30 May 11:16:03 BST 2024)
# 
#   net: phylink: rename ovr_an_inband to default_an_inband
# 
#   Since ovr_an_inband no longer overrides every MLO_AN_xxx mode, rename
#   it to reflect what it now does - it changes the default mode from
#   MLO_AN_PHY to MLO_AN_INBAND. Fix up the two users of this.
# 
#   Reviewed-by: Andrew Halaney
#   Signed-off-by: Russell King (Oracle)
# 
#   072e4036a0d3f78b8865fd77d635e1a2ae889af5
#    drivers/net/ethernet/freescale/fman/fman_memac.c  | 2 +-
#    drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 2 +-
#    drivers/net/phy/phylink.c                         | 2 +-
#    include/linux/phylink.h                           | 5 +++--
#    4 files changed, 6 insertions(+), 5 deletions(-)
# 
# Author:    Russell King (Oracle) (Wed 29 May 12:25:42 BST 2024)
# Committer: Russell King (Oracle) (Thu 30 May 11:16:03 BST 2024)
# 
#   net: phylink: move test for ovr_an_inband
# 
#   Of the two users of phylink_config->ovr_an_inband, both manually check
#   for a fixed link before setting this flag (or clearing it if they find
#   a fixed link.) This is unnecessary complication.
# 
#   Test ovr_an_inband before checking for the fixed-link properties, which
#   will allow ovr_an_inband to be overriden by a fixed link specification.
# 
#   Reviewed-by: Andrew Halaney
#   Signed-off-by: Russell King (Oracle)
# 
#   1580cf455fdf2f30ff051a34cb348ed31bbfb9a4
#    drivers/net/phy/phylink.c | 6 ++++--
#    1 file changed, 4 insertions(+), 2 deletions(-)
# 
# Author:    Russell King (Oracle) (Wed 29 May 12:25:01 BST 2024)
# Committer: Russell King (Oracle) (Thu 30 May 11:16:03 BST 2024)
# 
#   net: phylink: rearrange phylink_parse_mode()
# 
#   Of the two users of phylink_config->ovr_an_inband, both manually check
#   for a fixed link before setting this flag (or clearing it if they find
#   a fixed link.) This is unnecessary complication. This is the first
#   stage of fixing this.
# 
#   Rearrange phylink_parse_mode() a little so we can change how
#   phylink_config->ovr_an_inband works. This will allow the flag to be
#   tested before checking for the fixed link properties in the next patch.
# 
#   Reviewed-by: Andrew Halaney
#   Signed-off-by: Russell King (Oracle)
# 
#   7f9b2e5da5571be0c725f99aef4875e4c827c162
#    drivers/net/phy/phylink.c | 5 ++++-
#    1 file changed, 4 insertions(+), 1 deletion(-)
# 
# Author:    Russell King (Oracle) (Wed 29 May 10:21:27 BST 2024)
# Committer: Russell King (Oracle) (Thu 30 May 11:15:57 BST 2024)
# 
#   net: stmmac: ethqos: clean up setting serdes speed
# 
#   There are four repititions of the same sequence of code, three of which
#   are identical. Pull these out into a separate function to improve
#   readability.
# 
#   Reviewed-by: Serge Semin
#   Reviewed-by: Andrew Halaney
#   Signed-off-by: Russell King (Oracle)
# 
#   5ec73885cafb02ba31391160d880e2b661f9d39c
#    .../ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c    | 24 +++++++++++-----------
#    1 file changed, 12 insertions(+), 12 deletions(-)
# 
# Author:    Russell King (Oracle) (Sun 26 May 13:36:47 BST 2024)
# Committer: Russell King (Oracle) (Thu 30 May 08:55:07 BST 2024)
# 
#   net: stmmac: include linux/io.h rather than asm/io.h
# 
#   Include linux/io.h instead of asm/io.h since linux/ includes are
#   preferred.
# 
#   Reviewed-by: Andrew Halaney
#   Reviewed-by: Serge Semin
#   Signed-off-by: Russell King (Oracle)
# 
#   e58bcccd143bedb99ce6889e7531676c22b93e7c
#    drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c | 2 +-
#    1 file changed, 1 insertion(+), 1 deletion(-)
# 
# Author:    Russell King (Oracle) (Sun 12 May 12:57:13 BST 2024)
# Committer: Russell King (Oracle) (Thu 30 May 08:55:07 BST 2024)
# 
#   net: stmmac: remove unnecessary netif_carrier_off()
# 
#   It is incorrect to call netif_carrier_off(), or in fact any driver
#   teardown, before unregister_netdev() has been called.
# 
#   unregister_netdev() unpublishes the network device from userspace, and
#   takes the interface down if it was up prior to returning. Therefore,
#   once the call has returned, we are guaranteed that .ndo_stop() will
#   have been called for an interface that was up. Phylink will take the
#   carrier down via phylink_stop(), making any manipulation of the carrier
#   in the remove path unnecessary.
# 
#   In the stmmac_release() path, the netif_carrier_off() call follows the
#   call to phylink_stop(), so this call is redundant.
# 
#   Reviewed-by: Andrew Halaney
#   Reviewed-by: Serge Semin
#   Signed-off-by: Russell King (Oracle)
# 
#   eeefc93190d4f2e8e932263ddf4913f25be0e9ec
#    drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 3 ---
#    1 file changed, 3 deletions(-)
# 
# Author:    Russell King (Oracle) (Sat 11 May 22:24:49 BST 2024)
# Committer: Russell King (Oracle) (Thu 30 May 08:55:06 BST 2024)
# 
#   net: stmmac: remove pcs_rane() method
# 
#   The pcs_rane() method is not called, so lets just remove this
#   redundant code.
# 
#   Reviewed-by: Andrew Halaney
#   Reviewed-by: Serge Semin
#   Signed-off-by: Russell King (Oracle)
# 
#   003111330359d156225140df99822e162bcfb787
#    drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c |  6 ------
#    drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c    |  8 --------
#    drivers/net/ethernet/stmicro/stmmac/hwif.h           |  3 ---
#    drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.h     | 17 -----------------
#    4 files changed, 34 deletions(-)
# 
# Author:    Russell King (Oracle) (Sun 26 May 14:19:41 BST 2024)
# Committer: Russell King (Oracle) (Thu 30 May 08:55:06 BST 2024)
# 
#   net: stmmac: dwxgmac2: remove useless NULL pointer initialisations
# 
#   Remove useless NULL pointer initialisations for "PCS" methods from the
#   dwxgmac2 code.
# 
#   Reviewed-by: Andrew Halaney
#   Reviewed-by: Serge Semin
#   Signed-off-by: Russell King (Oracle)
# 
#   e1b741034d701c89dc0e958be3c333549c737597
#    drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c | 6 ------
#    1 file changed, 6 deletions(-)
# 
# Author:    Serge Semin (Fri 24 May 22:02:59 BST 2024)
# Committer: Russell King (Oracle) (Thu 30 May 08:55:06 BST 2024)
# 
#   net: stmmac: Drop TBI/RTBI PCS flags
# 
#   First of all the flags are never set by any of the driver parts. If nobody
#   have them set then the respective statements will always have the same
#   result. Thus the statements can be simplified or even dropped with no risk
#   to break things.
# 
#   Secondly shall any of the TBI or RTBI flag is set the MDIO-bus
#   registration will be bypassed. Why? It really seems weird. It's perfectly
#   fine to have a TBI/RTBI-capable PHY configured over the MDIO bus
#   interface.
# 
#   Based on the notes above the TBI/RTBI PCS flags can be freely dropped thus
#   simplifying the driver code.
# 
#   Reviewed-by: Andrew Halaney
#   Signed-off-by: Serge Semin
#   Signed-off-by: Russell King (Oracle)
# 
#   c975098ec0ba6db7f5113381d83f122eeff1a03b
#    drivers/net/ethernet/stmicro/stmmac/common.h      |  2 --
#    drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 37 +++++++----------------
#    2 files changed, 11 insertions(+), 28 deletions(-)
# 
# Author:    Russell King (Oracle) (Fri  5 Apr 10:59:17 BST 2024)
# Committer: Russell King (Oracle) (Thu 30 May 08:54:42 BST 2024)
# 
#   net: dsa: felix: provide own phylink MAC operations
# 
#   Convert felix to provide its own phylink MAC operations, thus
#   avoiding the shim layer in DSA's port.c.
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   0d233eb1811347a998e49be539b595fb6a3095c1
#    drivers/net/dsa/ocelot/felix.c           | 54 +++++++++++++++++++++-----------
#    drivers/net/dsa/ocelot/felix.h           |  1 +
#    drivers/net/dsa/ocelot/felix_vsc9959.c   |  1 +
#    drivers/net/dsa/ocelot/ocelot_ext.c      |  1 +
#    drivers/net/dsa/ocelot/seville_vsc9953.c |  1 +
#    5 files changed, 40 insertions(+), 18 deletions(-)
# 
# Author:    Russell King (Oracle) (Sat 27 Apr 15:32:20 BST 2024)
# Committer: Russell King (Oracle) (Wed 29 May 10:22:04 BST 2024)
# 
#   net: dsa: remove mac_prepare()/mac_finish() shims
# 
#   No DSA driver makes use of the mac_prepare()/mac_finish() shimmed
#   operations anymore, so we can remove these.
# 
#   Reviewed-by: Florian Fainelli
#   Signed-off-by: Russell King (Oracle)
# 
#   6ffc0a9d78e2a413fbf89422108d87580fb008dc
#    include/net/dsa.h |  6 ------
#    net/dsa/dsa.c     |  2 --
#    net/dsa/port.c    | 32 --------------------------------
#    3 files changed, 40 deletions(-)
# 
# Author:    Serge Semin (Tue 28 May 15:55:13 BST 2024)
# Committer: Russell King (Oracle) (Tue 28 May 15:55:13 BST 2024)
# 
#   net: stmmac: Rename phylink_get_caps() callback to update_caps()
# 
#   Since recent commits the stmmac_ops::phylink_get_caps() callback has no
#   longer been responsible for the phylink MAC capabilities getting, but
#   merely updates the MAC capabilities in the mac_device_info::link::caps
#   field. Rename the callback to comply with the what the method does now.
# 
#   Signed-off-by: Serge Semin
#   Reviewed-by: Romain Gantois
#   Signed-off-by: Paolo Abeni
# 
#   40525a1805ca2964ab778595db61dd9b0e60c0ca
#    drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c | 8 ++++----
#    drivers/net/ethernet/stmicro/stmmac/hwif.h        | 8 ++++----
#    drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 6 +++---
#    3 files changed, 11 insertions(+), 11 deletions(-)
# 
# Author:    Florian Fainelli (Thu  2 May 12:16:22 BST 2024)
# Committer: Russell King (Oracle) (Fri 24 May 21:36:03 BST 2024)
# 
#   net: dsa: Remove adjust_link paths
# 
#   Now that we no longer any drivers using PHYLIB's adjust_link callback,
#   remove all paths that made use of adjust_link as well as the associated
#   functions.
# 
#   Signed-off-by: Florian Fainelli
#   Reviewed-by: Russell King (Oracle)
#   Link: https://lore.kernel.org/r/20240430164816.2400606-3-florian.fainelli@broadcom.com
#   Signed-off-by: Jakub Kicinski
# 
#   37126092f04eddc38255e911cef69c46cc2dfc5c
#    include/net/dsa.h |   7 ---
#    net/dsa/dsa.c     |   3 +-
#    net/dsa/port.c    | 139 +++++-------------------------------------------------
#    3 files changed, 12 insertions(+), 137 deletions(-)
# 
# Author:    Florian Fainelli (Thu  2 May 12:16:22 BST 2024)
# Committer: Russell King (Oracle) (Fri 24 May 21:36:02 BST 2024)
# 
#   net: dsa: Remove fixed_link_update member
# 
#   We have not had a switch driver use a fixed_link_update callback since
#   58d56fcc3964f9be0a9ca42fd126bcd9dc7afc90 ("net: dsa: bcm_sf2: Get rid of
#   PHYLIB functions") remove this callback.
# 
#   Signed-off-by: Florian Fainelli
#   Reviewed-by: Russell King (Oracle)
#   Link: https://lore.kernel.org/r/20240430164816.2400606-2-florian.fainelli@broadcom.com
#   Signed-off-by: Jakub Kicinski
# 
#   7ad2b9b009614ffc6b7be8c89600dfc401ba8ebe
#    include/net/dsa.h | 4 ----
#    1 file changed, 4 deletions(-)
# 
# Author:    Florian Fainelli (Sun  5 May 23:16:04 BST 2024)
# Committer: Russell King (Oracle) (Fri 24 May 21:36:02 BST 2024)
# 
#   net: dsa: b53: provide own phylink MAC operations
# 
#   Convert b53 to provide its own phylink MAC operations, thus avoiding the
#   shim layer in DSA's port.c
# 
#   Signed-off-by: Florian Fainelli
#   Reviewed-by: Russell King (Oracle)
#   Link: https://lore.kernel.org/r/20240423183339.1368511-9-florian.fainelli@broadcom.com
#   Signed-off-by: Jakub Kicinski
# 
#   9c8e7dc2f7ee29bd5801a6242415aa851be29f24
#    drivers/net/dsa/b53/b53_common.c | 40 ++++++++++++++++++++++++++--------------
#    1 file changed, 26 insertions(+), 14 deletions(-)
# 
# Author:    Florian Fainelli (Sun  5 May 23:16:03 BST 2024)
# Committer: Russell King (Oracle) (Fri 24 May 21:36:02 BST 2024)
# 
#   net: dsa: b53: Remove b53_adjust_link()
# 
#   Only use the PHYLINK implementation from there on now that an equivalent
#   configuration is applied to all of the switch ports.
# 
#   Signed-off-by: Florian Fainelli
#   Reviewed-by: Russell King (Oracle)
#   Link: https://lore.kernel.org/r/20240423183339.1368511-8-florian.fainelli@broadcom.com
#   Signed-off-by: Jakub Kicinski
# 
#   2c992553adaa25cdeca004f4e2dc61644579187e
#    drivers/net/dsa/b53/b53_common.c | 40 ----------------------------------------
#    1 file changed, 40 deletions(-)
# 
# Author:    Florian Fainelli (Sun  5 May 23:15:51 BST 2024)
# Committer: Russell King (Oracle) (Fri 24 May 21:36:02 BST 2024)
# 
#   net: dsa: b53: Call b53_eee_init() from b53_mac_link_up()
# 
#   And make sure this is done for the MLO_AN_PHY case, where it actually
#   makes sense, contrary to b53_adjust_link() which only did it for
#   fixed-PHY configurations where it does not make sense.
# 
#   Signed-off-by: Florian Fainelli
#   Reviewed-by: Russell King (Oracle)
#   Link: https://lore.kernel.org/r/20240423183339.1368511-7-florian.fainelli@broadcom.com
#   Signed-off-by: Jakub Kicinski
# 
#   2520b39a9f0830aa9d7e6a48ee88f62cc9b991d5
#    drivers/net/dsa/b53/b53_common.c | 6 +++++-
#    1 file changed, 5 insertions(+), 1 deletion(-)
# 
# Author:    Florian Fainelli (Sun  5 May 23:15:50 BST 2024)
# Committer: Russell King (Oracle) (Fri 24 May 21:36:01 BST 2024)
# 
#   net: dsa: b53: Configure RGMII for 531x5 and MII for 5325
# 
#   Call b53_adjust_531x5_rgmii() and b53_adjust_5325_mii() from
#   b53_phylink_mac_config() when we have a fixed PHY in preparation for removing
#   b53_adjust_link(). Also move b53_adjust_63xx_rgmii() to
#   b53_phylink_mac_config() where it logically belongs.
# 
#   Signed-off-by: Florian Fainelli
#   Reviewed-by: Russell King (Oracle)
#   Link: https://lore.kernel.org/r/20240423183339.1368511-6-florian.fainelli@broadcom.com
#   Signed-off-by: Jakub Kicinski
# 
#   5c8435a6e1a59f91126f1bda9be66a05ce15b970
#    drivers/net/dsa/b53/b53_common.c | 17 ++++++++++++++---
#    1 file changed, 14 insertions(+), 3 deletions(-)
# 
# Author:    Florian Fainelli (Sun  5 May 23:15:50 BST 2024)
# Committer: Russell King (Oracle) (Fri 24 May 21:36:01 BST 2024)
# 
#   net: dsa: b53: Force flow control for BCM5301X CPU port(s)
# 
#   Just like what b53_adjust_link() does, force flow control for the
#   BCM5301X CPU port(s) by forcing rx_pause and tx_pause in
#   b53_phylink_mac_link_up(). Preparatory step for getting rid of
#   b53_adjust_link().
# 
#   Signed-off-by: Florian Fainelli
#   Reviewed-by: Russell King (Oracle)
#   Link: https://lore.kernel.org/r/20240423183339.1368511-5-florian.fainelli@broadcom.com
#   Signed-off-by: Jakub Kicinski
# 
#   0ef4e826fd64141052366b2184c55cff7fc79ced
#    drivers/net/dsa/b53/b53_common.c | 4 ++++
#    1 file changed, 4 insertions(+)
# 
# Author:    Florian Fainelli (Sun  5 May 23:15:49 BST 2024)
# Committer: Russell King (Oracle) (Fri 24 May 21:36:01 BST 2024)
# 
#   net: dsa: b53: Introduce b53_adjust_5325_mii()
# 
#   Takes care of doing the 5325 switch series specific MII programming and
#   is called from b53_adjust_link() to allow the future removal of
#   b53_adjust_link().
# 
#   Signed-off-by: Florian Fainelli
#   Reviewed-by: Russell King (Oracle)
#   Link: https://lore.kernel.org/r/20240423183339.1368511-4-florian.fainelli@broadcom.com
#   Signed-off-by: Jakub Kicinski
# 
#   4c3ecffd23a21ea94156a96fae0dc4d588b250dd
#    drivers/net/dsa/b53/b53_common.c | 44 +++++++++++++++++++++++-----------------
#    1 file changed, 25 insertions(+), 19 deletions(-)
# 
# Author:    Florian Fainelli (Sun  5 May 23:15:35 BST 2024)
# Committer: Russell King (Oracle) (Fri 24 May 21:36:01 BST 2024)
# 
#   net: dsa: b53: Introduce b53_adjust_531x5_rgmii()
# 
#   Takes care of doing the 531x5 switch series specific RGMII programming
#   and is called from b53_adjust_link() to allow the future removal of
#   b53_adjust_link().
# 
#   Signed-off-by: Florian Fainelli
#   Reviewed-by: Russell King (Oracle)
#   Link: https://lore.kernel.org/r/20240423183339.1368511-3-florian.fainelli@broadcom.com
#   Signed-off-by: Jakub Kicinski
# 
#   5e25f042858a48ed50184caae6d93552ba2ee5c4
#    drivers/net/dsa/b53/b53_common.c | 84 ++++++++++++++++++++++------------------
#    1 file changed, 46 insertions(+), 38 deletions(-)
# 
# Author:    Florian Fainelli (Sun  5 May 23:15:05 BST 2024)
# Committer: Russell King (Oracle) (Fri 24 May 21:36:00 BST 2024)
# 
#   net: dsa: b53: Stop exporting b53_phylink_* routines
# 
#   They are not used outside of the b53_common.c file, no need to be
#   exported.
# 
#   Signed-off-by: Florian Fainelli
#   Reviewed-by: Russell King (Oracle)
#   Link: https://lore.kernel.org/r/20240423183339.1368511-2-florian.fainelli@broadcom.com
#   Signed-off-by: Jakub Kicinski
# 
#   b2392c6918c93121f5953fcc633574fe25ce614e
#    drivers/net/dsa/b53/b53_common.c | 27 ++++++++++++---------------
#    drivers/net/dsa/b53/b53_priv.h   | 12 ------------
#    2 files changed, 12 insertions(+), 27 deletions(-)
# 
# Author:    Russell King (Oracle) (Sat 27 Apr 12:28:09 BST 2024)
# Committer: Russell King (Oracle) (Fri 24 May 21:36:00 BST 2024)
# 
#   net: sfp-bus: constify link_modes to sfp_select_interface()
# 
#   sfp_select_interface() does not modify its link_modes argument, so
#   make this a const pointer.
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   43ab90d12a30806e6341254807c000ed2b8f849e
#    drivers/net/phy/sfp-bus.c | 2 +-
#    include/linux/sfp.h       | 4 ++--
#    2 files changed, 3 insertions(+), 3 deletions(-)
# 
# Author:    Russell King (Oracle) (Fri 26 Apr 15:12:40 BST 2024)
# Committer: Russell King (Oracle) (Fri 24 May 21:35:59 BST 2024)
# 
#   net: sfp: allow use 2500base-X for 2500base-T modules
# 
#   Allow use of 2500base-X interface mode for PHY modules that support
#   2500base-T.
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   a4dc104469526a20e7e994153a8c10761745712c
#    drivers/net/phy/sfp-bus.c | 3 ++-
#    1 file changed, 2 insertions(+), 1 deletion(-)
# 
# Author:    Russell King (Oracle) (Sat 27 Apr 16:40:26 BST 2024)
# Committer: Russell King (Oracle) (Fri 24 May 21:35:59 BST 2024)
# 
#   net: phylink: add debug print for empty posssible_interfaces
# 
#   Add a debugging print in phylink_validate_phy() when we detect that the
#   PHY has not supplied a possible_interfaces bitmap.
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   7355f4dfe6a71e9ef54c700dba97701439926962
#    drivers/net/phy/phylink.c | 3 +++
#    1 file changed, 3 insertions(+)
# 
# Author:    Russell King (Oracle) (Wed  3 Apr 20:20:12 BST 2024)
# Committer: Russell King (Oracle) (Fri 24 May 21:35:59 BST 2024)
# 
#   net: txgbe: use phylink_pcs_change() to report PCS link change events
# 
#   Use phylink_pcs_change() when reporting changes in PCS link state to
#   phylink as the interrupts are informing us about changes to the PCS
#   state.
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   1999016dba441057f770a5487c5fcd5a6931ba6c
#    drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c | 2 +-
#    1 file changed, 1 insertion(+), 1 deletion(-)
# 
# Author:    Russell King (Oracle) (Mon 26 Feb 11:29:49 GMT 2024)
# Committer: Russell King (Oracle) (Fri 24 May 21:35:59 BST 2024)
# 
#   net: prestera: use phylink_pcs_change() to report PCS link change events
# 
#   Use phylink_pcs_change() when reporting changes in PCS link state to
#   phylink as the interrupts are informing us about changes to the PCS
#   state.
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   4001d3a69fd9f8cd225ef33aca7b6d92c7885c2e
#    drivers/net/ethernet/marvell/prestera/prestera_main.c | 4 ++--
#    1 file changed, 2 insertions(+), 2 deletions(-)
# 
# Author:    Russell King (Oracle) (Mon 26 Feb 11:27:33 GMT 2024)
# Committer: Russell King (Oracle) (Fri 24 May 21:35:59 BST 2024)
# 
#   net: mvneta: use phylink_pcs_change() to report PCS link change events
# 
#   Use phylink_pcs_change() when reporting changes in PCS link state to
#   phylink as the interrupts are informing us about changes to the PCS
#   state.
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   be976a710cd6c40f1885b424c9985a8e08c0180a
#    drivers/net/ethernet/marvell/mvneta.c | 3 ++-
#    1 file changed, 2 insertions(+), 1 deletion(-)
# 
# Author:    Russell King (Oracle) (Mon 26 Feb 11:25:04 GMT 2024)
# Committer: Russell King (Oracle) (Fri 24 May 21:35:58 BST 2024)
# 
#   net: mvpp2: use phylink_pcs_change() to report PCS link change events
# 
#   Use phylink_pcs_change() when reporting changes in PCS link state to
#   phylink as the interrupts are informing us about changes to the PCS
#   state.
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   04574865bbb35f914189a3cfb73771c9c5f0b114
#    drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c | 9 +++++----
#    1 file changed, 5 insertions(+), 4 deletions(-)
# 
# Author:    Russell King (Oracle) (Fri 24 May 21:35:45 BST 2024)
# Committer: Russell King (Oracle) (Fri 24 May 21:35:45 BST 2024)
# 
#   net: dsa: realtek: provide own phylink MAC operations
# 
#   Convert realtek to provide its own phylink MAC operations, thus
#   avoiding the shim layer in DSA's port.c. We need to provide a stub for
#   the mandatory mac_config() method for rtl8366rb.
# 
#   Signed-off-by: Russell King (Oracle)
#   Reviewed-by: Linus Walleij
#   Link: https://lore.kernel.org/r/E1s11qJ-00AHi0-Kk@rmk-PC.armlinux.org.uk
#   Signed-off-by: Paolo Abeni
# 
#   d2f0fcef4c3b31bc0b719907162509be048c66ab
#    drivers/net/dsa/realtek/realtek.h   |  2 ++
#    drivers/net/dsa/realtek/rtl8365mb.c | 32 +++++++++++++++++++++-----------
#    drivers/net/dsa/realtek/rtl8366rb.c | 29 ++++++++++++++++++++++-------
#    drivers/net/dsa/realtek/rtl83xx.c   |  1 +
#    4 files changed, 46 insertions(+), 18 deletions(-)
# 
# Author:    Russell King (Oracle) (Fri 12 Apr 20:16:26 BST 2024)
# Committer: Russell King (Oracle) (Fri 24 May 21:29:05 BST 2024)
# 
#   net: dsa: ksz_common: use separate phylink_mac_ops for ksz8830
# 
#   Use a separate phylink_mac_ops for the KSZ8830 chip-id.
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   422e4a15542148c25c904b7395726591a9e5d433
#    drivers/net/dsa/microchip/ksz_common.c | 26 ++++++++++++++++++++------
#    1 file changed, 20 insertions(+), 6 deletions(-)
# 
# Author:    Russell King (Oracle) (Fri 12 Apr 18:27:38 BST 2024)
# Committer: Russell King (Oracle) (Fri 24 May 21:29:04 BST 2024)
# 
#   net: dsa: ksz_common: sub-driver phylink ops
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   13719530b2e25c1bb86eaaf3e1ebb4f070140c79
#    drivers/net/dsa/microchip/ksz8.h       |  6 +--
#    drivers/net/dsa/microchip/ksz8795.c    | 10 ++--
#    drivers/net/dsa/microchip/ksz_common.c | 84 +++++++++++++++++++++-------------
#    drivers/net/dsa/microchip/ksz_common.h |  2 +
#    4 files changed, 65 insertions(+), 37 deletions(-)
# 
# Author:    Russell King (Oracle) (Fri  5 Apr 13:00:55 BST 2024)
# Committer: Russell King (Oracle) (Fri 24 May 21:29:04 BST 2024)
# 
#   net: dsa: ksz_common: provide own phylink MAC operations
# 
#   Convert ksz_common to provide its own phylink MAC operations, thus
#   avoiding the shim layer in DSA's port.c
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   1f61a381a66d6650eee5ed6d9e62bbdd900fb13d
#    drivers/net/dsa/microchip/ksz_common.c | 42 +++++++++++++++++++++-------------
#    1 file changed, 26 insertions(+), 16 deletions(-)
# 
# Author:    Russell King (Oracle) (Fri 12 Apr 18:34:15 BST 2024)
# Committer: Russell King (Oracle) (Fri 24 May 21:29:04 BST 2024)
# 
#   net: dsa: ksz_common: remove phylink_mac_config from ksz_dev_ops
# 
#   The phylink_mac_config function pointer member of struct ksz_dev_ops is
#   never initialised, so let's remove it to simplify the code.
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   42fc47a87a0b0146a12abac33ba420328203ac7f
#    drivers/net/dsa/microchip/ksz_common.c | 3 ---
#    drivers/net/dsa/microchip/ksz_common.h | 3 ---
#    2 files changed, 6 deletions(-)
# 
# Author:    Russell King (Oracle) (Fri  5 Apr 10:59:17 BST 2024)
# Committer: Russell King (Oracle) (Fri 24 May 21:29:04 BST 2024)
# 
#   net: dsa: xrs700x: provide own phylink MAC operations
# 
#   Convert xrs700x to provide its own phylink MAC operations, thus
#   avoiding the shim layer in DSA's port.c. We need to provide stubs for
#   the mac_link_down() and mac_config() methods which are mandatory.
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   9a88cce5501a04e442fd0b63906020cebb2ae654
#    drivers/net/dsa/xrs700x/xrs700x.c | 26 ++++++++++++++++++++++----
#    1 file changed, 22 insertions(+), 4 deletions(-)
# 
# Author:    Russell King (Oracle) (Fri  5 Apr 13:00:55 BST 2024)
# Committer: Russell King (Oracle) (Fri 24 May 21:29:03 BST 2024)
# 
#   net: dsa: rzn1_a5psw: provide own phylink MAC operations
# 
#   Convert rzn1_a5psw to provide its own phylink MAC operations, thus
#   avoiding the shim layer in DSA's port.c. We need to provide a stub for
#   the mac_config() method which is mandatory.
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   81b133c51b88225db63ac90eb1edb1ba9c454915
#    drivers/net/dsa/rzn1_a5psw.c | 47 +++++++++++++++++++++++++++++---------------
#    1 file changed, 31 insertions(+), 16 deletions(-)
# 
# Author:    Russell King (Oracle) (Fri  5 Apr 13:00:55 BST 2024)
# Committer: Russell King (Oracle) (Fri 24 May 21:29:03 BST 2024)
# 
#   net: dsa: lan9303: provide own phylink MAC operations
# 
#   Convert lan9303 to provide its own phylink MAC operations, thus
#   avoiding the shim layer in DSA's port.c. We need to provide stubs for
#   the mac_link_down() and mac_config() methods which are mandatory.
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   d7785ee1e1d46dffb805512c79c8f5a1fb5d92ef
#    drivers/net/dsa/lan9303-core.c | 31 ++++++++++++++++++++++++++-----
#    1 file changed, 26 insertions(+), 5 deletions(-)
# 
# Author:    Russell King (Oracle) (Fri 12 Apr 18:00:41 BST 2024)
# Committer: Russell King (Oracle) (Fri 24 May 21:29:03 BST 2024)
# 
#   net: dsa: bcm_sf2: provide own phylink MAC operations
# 
#   Convert bcm_sf2 to provide its own phylink MAC operations, thus
#   avoiding the shim layer in DSA's port.c
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   9468d17635f947e3da314ee3ff5ac2a91149c7a0
#    drivers/net/dsa/bcm_sf2.c | 49 +++++++++++++++++++++++++++++++----------------
#    1 file changed, 32 insertions(+), 17 deletions(-)
# 
# Author:    Russell King (Oracle) (Fri 24 May 21:28:44 BST 2024)
# Committer: Russell King (Oracle) (Fri 24 May 21:28:44 BST 2024)
# 
#   net: dsa: mt7530: provide own phylink MAC operations
# 
#   Convert mt753x to provide its own phylink MAC operations, thus avoiding
#   the shim layer in DSA's port.c
# 
#   Signed-off-by: Russell King (Oracle)
#   Tested-by: Arınç ÜNAL
#   Link: https://lore.kernel.org/r/E1rvIco-006bQu-Fq@rmk-PC.armlinux.org.uk
#   Signed-off-by: Paolo Abeni
# 
#   062cad7c2a02d5585918beea004e98433f7a932f
#    drivers/net/dsa/mt7530.c | 46 +++++++++++++++++++++++++++++-----------------
#    1 file changed, 29 insertions(+), 17 deletions(-)
# 
# Author:    Xiaowei Bao (Wed 25 Sep 22:55:17 BST 2019)
# Committer: Russell King (Oracle) (Fri 24 May 19:49:56 BST 2024)
# 
#   PCI: mobiveil: ls_pcie_g4: fix SError when accessing config space
# 
#   While the Mellanox driver is binding, the following kernel panic
#   occurred:
# 
#   SError Interrupt on CPU1, code 0xbf000002 -- SError
#   CPU: 1 PID: 1 Comm: swapper/0 Not tainted 5.3.0+ #392
#   Hardware name: SolidRun LX2160A COM express type 7 module (DT)
#   pstate: 60400085 (nZCv daIf +PAN -UAO)
#   pc : pci_generic_config_read+0xb0/0xc0
#   lr : pci_generic_config_read+0x1c/0xc0
#   sp : ffffff8010f9baf0
#   x29: ffffff8010f9baf0 x28: ffffff8010d620a0
#   x27: ffffff8010d79000 x26: ffffff8010d62000
#   x25: ffffff8010cb06d4 x24: 0000000000000000
#   x23: ffffff8010e499b8 x22: ffffff8010f9bbaf
#   x21: 0000000000000000 x20: ffffffe2eda11800
#   x19: ffffff8010f62158 x18: ffffff8010bdede0
#   x17: ffffff8010bdede8 x16: ffffff8010b96970
#   x15: ffffffffffffffff x14: ffffffffff000000
#   x13: ffffffffffffffff x12: 0000000000000030
#   x11: 0101010101010101 x10: 7f7f7f7f7f7f7f7f
#   x9 : 2dff716475687163 x8 : ffffffffffffffff
#   x7 : fefefefefefefefe x6 : 0000000000000000
#   x5 : 0000000000000000 x4 : ffffff8010f9bb6c
#   x3 : 0000000000000001 x2 : 0000000000000003
#   x1 : 0000000000000000 x0 : 0000000000000000
#   Kernel panic - not syncing: Asynchronous SError Interrupt
#   CPU: 1 PID: 1 Comm: swapper/0 Not tainted 5.3.0+ #392
#   Hardware name: SolidRun LX2160A COM express type 7 module (DT)
#   Call trace:
#    dump_backtrace+0x0/0x120
#    show_stack+0x14/0x1c
#    dump_stack+0x9c/0xc0
#    panic+0x148/0x34c
#    print_tainted+0x0/0xa8
#    arm64_serror_panic+0x74/0x80
#    do_serror+0x8c/0x13c
#    el1_error+0xbc/0x160
#    pci_generic_config_read+0xb0/0xc0
#    pci_bus_read_config_byte+0x64/0x90
#    pci_read_config_byte+0x40/0x48
#    pci_assign_irq+0x34/0xc8
#    pci_device_probe+0x28/0x148
#    really_probe+0x1c4/0x2d0
#    driver_probe_device+0x58/0xfc
#    device_driver_attach+0x68/0x70
#    __driver_attach+0x94/0xdc
#    bus_for_each_dev+0x50/0xa0
#    driver_attach+0x20/0x28
#    bus_add_driver+0x14c/0x200
#    driver_register+0x6c/0x124
#    __pci_register_driver+0x48/0x50
#    mlx4_init+0x154/0x180
#    do_one_initcall+0x30/0x250
#    kernel_init_freeable+0x23c/0x32c
#    kernel_init+0x10/0xfc
#    ret_from_fork+0x10/0x18
#   SMP: stopping secondary CPUs
#   Kernel Offset: disabled
#   CPU features: 0x0002,21006008
#   Memory Limit: none
# 
#   which appears to be due to:
# 
#           pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
# 
#   in pci_assign_irq().  Avoiding that access just moves the SError later
#   (e.g. while accessing the command register in pci_enable_device()
#   instead.)
# 
#   This patch resolves the SError problem by preventing configuration
#   accesses triggering a SError interrupt.
# 
#   Reported-by: Russell King
#   Tested-by: Russell King
#   Signed-off-by: Xiaowei Bao
#   [description modified -- rmk]
#   Signed-off-by: Russell King
# 
#   9bcc6a55bfc11ea8b8484073799c3bc68b9c3b4b
#    drivers/pci/controller/mobiveil/pcie-layerscape-gen4.c | 4 ++--
#    1 file changed, 2 insertions(+), 2 deletions(-)
# 
# Author:    Hou Zhiqiang (Tue  6 Nov 02:14:57 GMT 2018)
# Committer: Russell King (Oracle) (Fri 24 May 19:49:56 BST 2024)
# 
#   PCI: mobiveil: ls_pcie_g4: add Workaround for A-011451
# 
#   When LX2 PCIe controller is sending multiple split completions and
#   ACK latency expires indicating that ACK should be send at priority.
#   But because of large number of split completions and FC update DLLP,
#   the controller does not give priority to ACK transmission. This
#   results into ACK latency timer timeout error at the link partner and
#   the pending TLPs are replayed by the link partner again.
# 
#   Workaround:
#   1. Reduce the ACK latency timeout value to a very small value.
#   2. Restrict the number of completions from the LX2 PCIe controller
#      to 1, by changing the Max Read Request Size (MRRS) of link partner
#      to the same value as Max Packet size (MPS).
# 
#   This patch implemented part 1, the part 2 can be set by kernel parameter
#   'pci=pcie_bus_perf'
# 
#   This ERRATA is only for LX2160A Rev1.0, and it will be fixed
#   in Rev2.0.
# 
#   Signed-off-by: Hou Zhiqiang
#   [fixed up for mainline -- rmk]
#   Signed-off-by: Russell King
# 
#   15f366d4111787e1c1cb1cd49c47e63477c999ec
#    drivers/pci/controller/mobiveil/pcie-layerscape-gen4.c | 15 +++++++++++++++
#    drivers/pci/controller/mobiveil/pcie-mobiveil.h        |  4 ++++
#    2 files changed, 19 insertions(+)
# 
# Author:    Hou Zhiqiang (Wed 25 Sep 10:21:28 BST 2019)
# Committer: Russell King (Oracle) (Fri 24 May 19:49:55 BST 2024)
# 
#   PCI: mobiveil: ls_pcie_g4: add Workaround for A-011577
# 
#   PCIe configuration access to non-existent function triggered
#   SERROR interrupt exception.
# 
#   Workaround:
#   Disable error reporting on AXI bus during the Vendor ID read
#   transactions in enumeration.
# 
#   This ERRATA is only for LX2160A Rev1.0, and it will be fixed
#   in Rev2.0.
# 
#   Signed-off-by: Hou Zhiqiang
#   Signed-off-by: Russell King
# 
#   85397cd01be0a95333bf16f1509db0cc92ae19f8
#    .../pci/controller/mobiveil/pcie-layerscape-gen4.c | 37 ++++++++++++++++++++++
#    .../pci/controller/mobiveil/pcie-mobiveil-host.c   | 18 ++++++++++-
#    drivers/pci/controller/mobiveil/pcie-mobiveil.h    |  3 ++
#    3 files changed, 57 insertions(+), 1 deletion(-)
# 
# Author:    Russell King (Oracle) (Fri 17 Sep 19:21:18 BST 2021)
# Committer: Russell King (Oracle) (Fri 24 May 19:49:54 BST 2024)
# 
#   Revert "PCI: mobiveil: Remove unused readl and writel functions"
# 
#   This reverts commit 42d7a8dc195f99e2e99d8f38a683e0852a29f6af.
# 
#   9d231ab26285492d172b8795820e84fd6ff7ff2f
#    drivers/pci/controller/mobiveil/pcie-layerscape-gen4.c | 11 +++++++++++
#    1 file changed, 11 insertions(+)
# 
# Author:    Russell King (Wed  3 Jun 15:34:01 BST 2020)
# Committer: Russell King (Oracle) (Fri 24 May 19:49:53 BST 2024)
# 
#   PCI: mobiveil: fix 5.7 merge errors
# 
#   Fix errors in the mobiveil version that was merged in 5.7 kernels:
#   - the type of "root_bus_nr" was changed from "u8" to "char", but
#     it is compared to values that are typed as "unsigned char".
#     Depending whether a platform has "char" as signed or unsigned,
#     this may not do what is intended.
# 
#   - ls_g4_pcie_reinit_hw() now returns a success/failure value, and
#     follows the Linux style of return 0 on success and -ve errno on
#     failure.  However, the testing in ls_pcie_g4_reset() expects 0
#     on failure, so we won't call ls_ig4_pcie_enable_interrupt() except
#     if ls_g4_pcie_reinit_hw() has failed - which is likely not what
#     was intended.
# 
#   Fixes: d29ad70a813b ("PCI: mobiveil: Add PCIe Gen4 RC driver for Layerscape SoCs")
#   Signed-off-by: Russell King
# 
#   54fd569603540e7359e44957cc1216d61e04b89b
#    drivers/pci/controller/mobiveil/pcie-layerscape-gen4.c | 16 +++++++++++++++-
#    1 file changed, 15 insertions(+), 1 deletion(-)
# 
# Author:    Russell King (Oracle) (Fri  5 Apr 10:59:17 BST 2024)
# Committer: Russell King (Oracle) (Fri 24 May 19:16:00 BST 2024)
# 
#   net: dsa: lantiq_gswip: provide own phylink MAC operations
# 
#   Convert lantiq_gswip to provide its own phylink MAC operations, thus
#   avoiding the shim layer in DSA's port.c. For lantiq_gswip, it means
#   we end up with a common instance of phylink MAC operations that are
#   shared between the different variants, rather than having duplicated
#   initialisers in dsa_switch_ops.
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   6b45af596f477baec527bc1160b1ea26459a6ae0
#    drivers/net/dsa/lantiq_gswip.c | 39 +++++++++++++++++++++++----------------
#    1 file changed, 23 insertions(+), 16 deletions(-)
# 
# Author:    Russell King (Oracle) (Fri  5 Apr 10:59:17 BST 2024)
# Committer: Russell King (Oracle) (Fri 24 May 19:15:58 BST 2024)
# 
#   net: dsa: qca8k: provide own phylink MAC operations
# 
#   Convert qca8k to provide its own phylink MAC operations, thus
#   avoiding the shim layer in DSA's port.c.
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   b38540ccc4c227d3db1fbf25fbe870bca773c2fa
#    drivers/net/dsa/qca/qca8k-8xxx.c | 49 ++++++++++++++++++++++++++--------------
#    1 file changed, 32 insertions(+), 17 deletions(-)
# 
# Author:    Russell King (Oracle) (Fri  5 Apr 10:59:17 BST 2024)
# Committer: Russell King (Oracle) (Fri 24 May 19:15:57 BST 2024)
# 
#   net: dsa: ar9331: provide own phylink MAC operations
# 
#   Convert ar9331 to provide its own phylink MAC operations, thus
#   avoiding the shim layer in DSA's port.c.
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   bf188bc2ddd4be8e15d9aa86d16108d872964a22
#    drivers/net/dsa/qca/ar9331.c | 37 ++++++++++++++++++++++---------------
#    1 file changed, 22 insertions(+), 15 deletions(-)
# 
# Author:    Russell King (Oracle) (Fri  5 Apr 13:00:55 BST 2024)
# Committer: Russell King (Oracle) (Fri 24 May 19:15:55 BST 2024)
# 
#   net: dsa: sja1105: provide own phylink MAC operations
# 
#   Convert sja1105 to provide its own phylink MAC operations, thus
#   avoiding the shim layer in DSA's port.c
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   8e3c9b0169b8e6e5d69217b7a6e107dce553f207
#    drivers/net/dsa/sja1105/sja1105_main.c | 38 ++++++++++++++++++++++++----------
#    1 file changed, 27 insertions(+), 11 deletions(-)
# 
# Author:    Russell King (Oracle) (Fri 12 Apr 16:05:05 BST 2024)
# Committer: Russell King (Oracle) (Fri 24 May 19:15:54 BST 2024)
# 
#   net: dsa: convert dsa_user_phylink_fixed_state() to use dsa_phylink_to_port()
# 
#   Convert dsa_user_phylink_fixed_state() to use the newly introduced
#   dsa_phylink_to_port() helper.
# 
#   Suggested-by: Vladimir Oltean
#   Signed-off-by: Russell King (Oracle)
# 
#   dc096af96a5e4915fba9c5fb3963e8ad2d5377c9
#    net/dsa/user.c | 2 +-
#    1 file changed, 1 insertion(+), 1 deletion(-)
# 
# Author:    Russell King (Oracle) (Thu  1 Feb 12:10:57 GMT 2024)
# Committer: Russell King (Oracle) (Fri 24 May 19:15:52 BST 2024)
# 
#   net: dsa: mv88e6xxx: provide own phylink MAC operations
# 
#   Convert mv88e6xxx to provide its own phylink MAC operations, thus
#   avoiding the shim layer in DSA's port.c
# 
#   Reviewed-by: Andrew Lunn
#   Reviewed-by: Florian Fainelli
#   Signed-off-by: Russell King (Oracle)
# 
#   75cf7bb9e67a9ca8e63d470d276d38706e121cf3
#    drivers/net/dsa/mv88e6xxx/chip.c | 63 +++++++++++++++++++++++++---------------
#    1 file changed, 39 insertions(+), 24 deletions(-)
# 
# Author:    Russell King (Oracle) (Thu  1 Feb 11:55:42 GMT 2024)
# Committer: Russell King (Oracle) (Fri 24 May 19:15:51 BST 2024)
# 
#   net: dsa: allow DSA switch drivers to provide their own phylink mac ops
# 
#   Rather than having a shim for each and every phylink MAC operation,
#   allow DSA switch drivers to provide their own ops structure. When a
#   DSA driver provides the phylink MAC operations, the shimmed ops must
#   not be provided, so fail an attempt to register a switch with both
#   the phylink_mac_ops in struct dsa_switch and the phylink_mac_*
#   operations populated in dsa_switch_ops populated.
# 
#   Signed-off-by: Russell King (Oracle)
# 
#   2a472428563caf508e473f9e687e2e2784565973
#    include/net/dsa.h |  5 +++++
#    net/dsa/dsa.c     | 11 +++++++++++
#    net/dsa/port.c    | 26 ++++++++++++++++++++------
#    3 files changed, 36 insertions(+), 6 deletions(-)
# 
# Author:    Russell King (Oracle) (Thu  1 Feb 11:38:04 GMT 2024)
# Committer: Russell King (Oracle) (Fri 24 May 19:15:49 BST 2024)
# 
#   net: dsa: introduce dsa_phylink_to_port()
# 
#   We convert from a phylink_config struct to a dsa_port struct in many
#   places, let's provide a helper for this.
# 
#   Reviewed-by: Andrew Lunn
#   Reviewed-by: Florian Fainelli
#   Signed-off-by: Russell King (Oracle)
# 
#   570774aee67ce8bca645495177fb26f76d26f668
#    include/net/dsa.h |  6 ++++++
#    net/dsa/port.c    | 12 ++++++------
#    2 files changed, 12 insertions(+), 6 deletions(-)
# 
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/arm64/boot/dts/freescale/fsl-lx2160a-clearfog-cx.dts b/arch/arm64/boot/dts/freescale/fsl-lx2160a-clearfog-cx.dts
index 86a9b771428d..c35a29d96baf 100644
--- a/arch/arm64/boot/dts/freescale/fsl-lx2160a-clearfog-cx.dts
+++ b/arch/arm64/boot/dts/freescale/fsl-lx2160a-clearfog-cx.dts
@@ -12,4 +12,55 @@ / {
 	model = "SolidRun LX2160A Clearfog CX";
 	compatible = "solidrun,clearfog-cx",
 		"solidrun,lx2160a-cex7", "fsl,lx2160a";
+
+	qsfp: qsfp-0 {
+		compatible = "sff,qsfp28";
+		i2c-bus = <&i2c2>;
+		mod-prs-gpio = <&gpio2 5 GPIO_ACTIVE_LOW>;
+		maximum-power-milliwatt = <3500>;
+	};
+};
+
+&dpmac3 {
+	fixed-link {
+		speed = <10000>;
+		full-duplex;
+	};
+};
+
+&dpmac4 {
+	fixed-link {
+		speed = <10000>;
+		full-duplex;
+	};
+};
+
+&dpmac5 {
+	fixed-link {
+		speed = <10000>;
+		full-duplex;
+	};
+};
+
+&dpmac6 {
+	fixed-link {
+		speed = <10000>;
+		full-duplex;
+	};
+};
+
+&pcs_mdio3 {
+	status = "okay";
+};
+
+&pcs_mdio4 {
+	status = "okay";
+};
+
+&pcs_mdio5 {
+	status = "okay";
+};
+
+&pcs_mdio6 {
+	status = "okay";
 };
diff --git a/arch/arm64/boot/dts/freescale/fsl-lx2160a.dtsi b/arch/arm64/boot/dts/freescale/fsl-lx2160a.dtsi
index e665c629e1a1..0bf66125805e 100644
--- a/arch/arm64/boot/dts/freescale/fsl-lx2160a.dtsi
+++ b/arch/arm64/boot/dts/freescale/fsl-lx2160a.dtsi
@@ -1676,6 +1676,7 @@ fsl_mc: fsl-mc@80c000000 {
 			msi-parent = <&its>;
 			/* iommu-map property is fixed up by u-boot */
 			iommu-map = <0 &smmu 0 0>;
+			iommus = <&smmu 0x4000>;
 			dma-coherent;
 			#address-cells = <3>;
 			#size-cells = <1>;
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/ata/ahci_qoriq.c b/drivers/ata/ahci_qoriq.c
index b1a4e57578e2..5239703817a9 100644
--- a/drivers/ata/ahci_qoriq.c
+++ b/drivers/ata/ahci_qoriq.c
@@ -46,6 +46,27 @@
 #define ECC_DIS_ARMV8_CH2	0x80000000
 #define ECC_DIS_LS1088A		0x40000000
 
+/* errata for lx2160 */
+#define RCWSR29_BASE			0x1E00170
+#define SERDES2_BASE			0x1EB0000
+#define DEVICE_CONFIG_REG_BASE		0x1E00000
+#define SERDES2_LNAX_RX_CR(x)		(0x840 + (0x100 * (x)))
+#define SERDES2_LNAX_RX_CBR(x)		(0x8C0 + (0x100 * (x)))
+#define SYS_VER_REG			0xA4
+#define LN_RX_RST			0x80000010
+#define LN_RX_RST_DONE			0x3
+#define LN_RX_MASK			0xf
+#define LX2160A_VER1			0x1
+
+#define SERDES2_LNAA 0
+#define SERDES2_LNAB 1
+#define SERDES2_LNAC 2
+#define SERDES2_LNAD 3
+#define SERDES2_LNAE 4
+#define SERDES2_LNAF 5
+#define SERDES2_LNAG 6
+#define SERDES2_LNAH 7
+
 enum ahci_qoriq_type {
 	AHCI_LS1021A,
 	AHCI_LS1028A,
@@ -85,6 +106,126 @@ static const struct acpi_device_id ahci_qoriq_acpi_match[] = {
 };
 MODULE_DEVICE_TABLE(acpi, ahci_qoriq_acpi_match);
 
+static void fsl_sata_errata_379364(bool select)
+{
+	int val = 0;
+	void __iomem *rcw_base = NULL;
+	void __iomem *serdes_base = NULL;
+	void __iomem *dev_con_base = NULL;
+
+	if (select) {
+		dev_con_base = ioremap(DEVICE_CONFIG_REG_BASE, PAGE_SIZE);
+		if (!dev_con_base)
+			return;
+
+		val = (readl(dev_con_base + SYS_VER_REG) & GENMASK(7, 4)) >> 4;
+		if (val != LX2160A_VER1)
+			goto dev_unmap;
+
+		/*
+		 * Add few msec delay.
+		 * Check for corresponding serdes lane RST_DONE .
+		 * apply lane reset.
+		 */
+
+		serdes_base = ioremap(SERDES2_BASE, PAGE_SIZE);
+		if (!serdes_base)
+			goto dev_unmap;
+
+		rcw_base = ioremap(RCWSR29_BASE, PAGE_SIZE);
+		if (!rcw_base)
+			goto serdes_unmap;
+
+		msleep(20);
+
+		val = (readl(rcw_base) & GENMASK(25, 21)) >> 21;
+
+		switch (val) {
+		case 1:
+			if ((readl(serdes_base + SERDES2_LNAX_RX_CBR(SERDES2_LNAC)) &
+				LN_RX_MASK) != LN_RX_RST_DONE)
+				writel(LN_RX_RST, serdes_base +
+					SERDES2_LNAX_RX_CR(SERDES2_LNAC));
+			if ((readl(serdes_base + SERDES2_LNAX_RX_CBR(SERDES2_LNAD)) &
+				LN_RX_MASK) != LN_RX_RST_DONE)
+				writel(LN_RX_RST, serdes_base +
+					SERDES2_LNAX_RX_CR(SERDES2_LNAD));
+			break;
+
+		case 4:
+			if ((readl(serdes_base + SERDES2_LNAX_RX_CBR(SERDES2_LNAG)) &
+				LN_RX_MASK) != LN_RX_RST_DONE)
+				writel(LN_RX_RST, serdes_base +
+					SERDES2_LNAX_RX_CR(SERDES2_LNAG));
+			if ((readl(serdes_base + SERDES2_LNAX_RX_CBR(SERDES2_LNAH)) &
+				LN_RX_MASK) != LN_RX_RST_DONE)
+				writel(LN_RX_RST, serdes_base +
+					SERDES2_LNAX_RX_CR(SERDES2_LNAH));
+			break;
+
+		case 5:
+			if ((readl(serdes_base + SERDES2_LNAX_RX_CBR(SERDES2_LNAE)) &
+				LN_RX_MASK) != LN_RX_RST_DONE)
+				writel(LN_RX_RST, serdes_base +
+					SERDES2_LNAX_RX_CR(SERDES2_LNAE));
+			if ((readl(serdes_base + SERDES2_LNAX_RX_CBR(SERDES2_LNAF)) &
+				LN_RX_MASK) != LN_RX_RST_DONE)
+				writel(LN_RX_RST, serdes_base +
+					SERDES2_LNAX_RX_CR(SERDES2_LNAF));
+			if ((readl(serdes_base + SERDES2_LNAX_RX_CBR(SERDES2_LNAG)) &
+				LN_RX_MASK) != LN_RX_RST_DONE)
+				writel(LN_RX_RST, serdes_base +
+					SERDES2_LNAX_RX_CR(SERDES2_LNAG));
+			if ((readl(serdes_base + SERDES2_LNAX_RX_CBR(SERDES2_LNAH)) &
+				LN_RX_MASK) != LN_RX_RST_DONE)
+				writel(LN_RX_RST, serdes_base +
+					SERDES2_LNAX_RX_CR(SERDES2_LNAH));
+			break;
+
+		case 8:
+			if ((readl(serdes_base + SERDES2_LNAX_RX_CBR(SERDES2_LNAC)) &
+				LN_RX_MASK) != LN_RX_RST_DONE)
+				writel(LN_RX_RST, serdes_base +
+					SERDES2_LNAX_RX_CR(SERDES2_LNAC));
+			if ((readl(serdes_base + SERDES2_LNAX_RX_CBR(SERDES2_LNAD)) &
+				LN_RX_MASK) != LN_RX_RST_DONE)
+				writel(LN_RX_RST, serdes_base +
+					SERDES2_LNAX_RX_CR(SERDES2_LNAD));
+			if ((readl(serdes_base + SERDES2_LNAX_RX_CBR(SERDES2_LNAE)) &
+				LN_RX_MASK) != LN_RX_RST_DONE)
+				writel(LN_RX_RST, serdes_base +
+					SERDES2_LNAX_RX_CR(SERDES2_LNAE));
+			if ((readl(serdes_base + SERDES2_LNAX_RX_CBR(SERDES2_LNAF)) &
+				LN_RX_MASK) != LN_RX_RST_DONE)
+				writel(LN_RX_RST, serdes_base +
+					SERDES2_LNAX_RX_CR(SERDES2_LNAF));
+			break;
+
+		case 12:
+			if ((readl(serdes_base + SERDES2_LNAX_RX_CBR(SERDES2_LNAG)) &
+				LN_RX_MASK) != LN_RX_RST_DONE)
+				writel(LN_RX_RST, serdes_base +
+					SERDES2_LNAX_RX_CR(SERDES2_LNAG));
+			if ((readl(serdes_base + SERDES2_LNAX_RX_CBR(SERDES2_LNAH)) &
+				LN_RX_MASK) != LN_RX_RST_DONE)
+				writel(LN_RX_RST, serdes_base +
+					SERDES2_LNAX_RX_CR(SERDES2_LNAH));
+			break;
+
+		default:
+			break;
+		}
+	} else {
+		return;
+	}
+
+	iounmap(rcw_base);
+serdes_unmap:
+	iounmap(serdes_base);
+dev_unmap:
+	iounmap(dev_con_base);
+}
+
 static int ahci_qoriq_hardreset(struct ata_link *link, unsigned int *class,
 			  unsigned long deadline)
 {
@@ -100,6 +241,7 @@ static int ahci_qoriq_hardreset(struct ata_link *link, unsigned int *class,
 	bool online;
 	int rc;
 	bool ls1021a_workaround = (qoriq_priv->type == AHCI_LS1021A);
+	bool lx2160a_workaround = (qoriq_priv->type == AHCI_LX2160A);
 
 	hpriv->stop_engine(ap);
 
@@ -124,6 +266,8 @@ static int ahci_qoriq_hardreset(struct ata_link *link, unsigned int *class,
 	tf.status = ATA_BUSY;
 	ata_tf_to_fis(&tf, 0, 0, d2h_fis);
 
+	fsl_sata_errata_379364(lx2160a_workaround);
+
 	rc = sata_link_hardreset(link, timing, deadline, &online,
 				 ahci_check_ready);
 
diff --git a/drivers/bus/fsl-mc/dprc-driver.c b/drivers/bus/fsl-mc/dprc-driver.c
index 4b68c84ef485..dc6f937245cf 100644
--- a/drivers/bus/fsl-mc/dprc-driver.c
+++ b/drivers/bus/fsl-mc/dprc-driver.c
@@ -245,11 +245,11 @@ static void dprc_add_new_devices(struct fsl_mc_device *mc_bus_dev,
 int dprc_scan_objects(struct fsl_mc_device *mc_bus_dev,
 		      bool alloc_interrupts)
 {
-	int num_child_objects;
+	int num_child_objects, num_child_objects2;
 	int dprc_get_obj_failures;
 	int error;
-	unsigned int irq_count = mc_bus_dev->obj_desc.irq_count;
-	struct fsl_mc_obj_desc *child_obj_desc_array = NULL;
+	unsigned int irq_count;
+	struct fsl_mc_obj_desc *child_obj_desc_array;
 	struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev);
 
 	error = dprc_get_obj_count(mc_bus_dev->mc_io,
@@ -262,6 +262,9 @@ int dprc_scan_objects(struct fsl_mc_device *mc_bus_dev,
 		return error;
 	}
 
+retry:
+	irq_count = mc_bus_dev->obj_desc.irq_count;
+	child_obj_desc_array = NULL;
 	if (num_child_objects != 0) {
 		int i;
 
@@ -320,6 +323,29 @@ int dprc_scan_objects(struct fsl_mc_device *mc_bus_dev,
 		}
 	}
 
+	error = dprc_get_obj_count(mc_bus_dev->mc_io,
+				   0,
+				   mc_bus_dev->mc_handle,
+				   &num_child_objects2);
+	if (error < 0) {
+		if (child_obj_desc_array)
+			devm_kfree(&mc_bus_dev->dev, child_obj_desc_array);
+		dev_err(&mc_bus_dev->dev, "dprc_get_obj_count() failed: %d\n",
+			error);
+		return error;
+	}
+
+	if (num_child_objects != num_child_objects2) {
+		/*
+		 * Something changed while reading the number of objects.
+		 * Retry reading the child object list.
+		 */
+		if (child_obj_desc_array)
+			devm_kfree(&mc_bus_dev->dev, child_obj_desc_array);
+		num_child_objects = num_child_objects2;
+		goto retry;
+	}
+
 	/*
 	 * Allocate IRQ's before binding the scanned devices with their
 	 * respective drivers.
diff --git a/drivers/bus/fsl-mc/fsl-mc-bus.c b/drivers/bus/fsl-mc/fsl-mc-bus.c
index 78b96cd63de9..f3326f6fc208 100644
--- a/drivers/bus/fsl-mc/fsl-mc-bus.c
+++ b/drivers/bus/fsl-mc/fsl-mc-bus.c
@@ -66,6 +66,12 @@ struct fsl_mc_addr_translation_range {
 #define GCR1_P1_STOP	BIT(31)
 #define GCR1_P2_STOP	BIT(30)
 
+#define FSL_MC_FBALR	0x20
+#define MCFBAR_LOW	0xe0000000
+#define MCFBAR_MEMSZ	0x000000ff
+#define FSL_MC_FBAHR	0x24
+#define MCFBAR_HIGH	0x0000ffff
+
 #define FSL_MC_FAPR	0x28
 #define MC_FAPR_PL	BIT(18)
 #define MC_FAPR_BMT	BIT(17)
@@ -1034,6 +1040,87 @@ static int get_mc_addr_translation_ranges(struct device *dev,
 	return 0;
 }
 
+static void fsl_mc_setup_iommu(struct device *dev, struct fsl_mc *mc)
+{
+	struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
+	struct resource dcfg_res = DEFINE_RES_MEM(0x01e00000, 0x10000);
+	struct device_node *np;
+	u64 firmware_base;
+	u64 mc_ram_base;
+	u32 mc_ram_size;
+	u32 mcfbalr;
+	u32 mcfbahr;
+	int ret;
+
+	if (!domain) {
+		dev_err(dev, "Missing IOMMU domain - MC firmware will crash\n");
+		return;
+	}
+
+	mcfbalr = readl(mc->fsl_mc_regs + FSL_MC_FBALR);
+	mcfbahr = readl(mc->fsl_mc_regs + FSL_MC_FBAHR);
+
+	firmware_base = mcfbalr & MCFBAR_LOW;
+	firmware_base |= (u64)(mcfbahr & MCFBAR_HIGH) << 32;
+
+	/* Calculate the size of MC RAM. */
+	mc_ram_size = mcfbalr & MCFBAR_MEMSZ;
+	if (mc_ram_size != 255)
+		mc_ram_size = (mc_ram_size + 1) * SZ_256M;
+	else
+		mc_ram_size = SZ_128M;
+
+	/*
+	 * Calculate base address of MC RAM. U-Boot says:
+	 * "As per MC design document, MC initial base address should be least
+	 * significant 512MB address of MC private memory, i.e. address should
+	 * point to end address masked with 512MB offset in private DRAM block."
+	 * and uses the following calculation:
+	 *
+	 *    (gd->arch.resv_ram + mc_ram_size - 1) &
+	 *       MC_RAM_BASE_ADDR_ALIGNMENT_MASK
+	 *
+	 * where gd->arch.resv_ram is the start of the MC reserved RAM block,
+	 * and is itself aligned to 512MB.
+	 *
+	 * Hence, if the reserved RAM starts at 0x2780000000 and is 0x70000000
+	 * in size, then the firmware address will be 0x27e0000000. However,
+	 * if it is 512M, then the reserved RAM and the firmware base addresses
+	 * will be identical.
+	 */
+	mc_ram_base = ALIGN(firmware_base - mc_ram_size + 1, SZ_512M);
+
+	/*
+	 * Give MC firmware access to the MC RAM, which includes the MC
+	 * firmware image itself.
+	 */
+	ret = iommu_map(domain, mc_ram_base, mc_ram_base, mc_ram_size,
+			IOMMU_CACHE | IOMMU_WRITE | IOMMU_READ, GFP_KERNEL);
+	if (ret)
+		dev_err(dev, "Failed to setup IOMMU mapping for MC RAM: %pe\n",
+			ERR_PTR(ret));
+
+	/* Give firmware access to the DCFG so it can read the SVR register */
+	np = of_find_compatible_node(NULL, NULL, "fsl,lx2160a-dcfg");
+	if (np) {
+		ret = of_address_to_resource(np, 0, &dcfg_res);
+		if (ret) {
+			dev_err(dev, "Failed to get dcfg resource: %pe\n",
+				ERR_PTR(ret));
+			return;
+		}
+	} else {
+		dev_warn(dev,
+			 "Failed to find dcfg node - using default addresses\n");
+	}
+
+	ret = iommu_map(domain, dcfg_res.start, dcfg_res.start,
+			resource_size(&dcfg_res), IOMMU_READ, GFP_KERNEL);
+	if (ret)
+		dev_err(dev, "Failed to setup IOMMU mapping for DCFG: %pe\n",
+			ERR_PTR(ret));
+}
+
 /*
  * fsl_mc_bus_probe - callback invoked when the root MC bus is being
  * added
@@ -1085,6 +1172,9 @@ static int fsl_mc_bus_probe(struct platform_device *pdev)
 					 error);
 		}
 
+		if (dev_of_node(&pdev->dev))
+			fsl_mc_setup_iommu(&pdev->dev, mc);
+
 		/*
 		 * Some bootloaders pause the MC firmware before booting the
 		 * kernel so that MC will not cause faults as soon as the
diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c
index b2eeff04f4c8..0ebe049b08c7 100644
--- a/drivers/net/dsa/b53/b53_common.c
+++ b/drivers/net/dsa/b53/b53_common.c
@@ -1266,95 +1266,70 @@ static void b53_adjust_63xx_rgmii(struct dsa_switch *ds, int port,
 		phy_modes(interface));
 }
 
-static void b53_adjust_link(struct dsa_switch *ds, int port,
-			    struct phy_device *phydev)
+static void b53_adjust_531x5_rgmii(struct dsa_switch *ds, int port,
+				   phy_interface_t interface)
 {
 	struct b53_device *dev = ds->priv;
-	struct ethtool_keee *p = &dev->ports[port].eee;
-	u8 rgmii_ctrl = 0, reg = 0, off;
-	bool tx_pause = false;
-	bool rx_pause = false;
-
-	if (!phy_is_pseudo_fixed_link(phydev))
-		return;
+	u8 rgmii_ctrl = 0, off;
 
-	/* Enable flow control on BCM5301x's CPU port */
-	if (is5301x(dev) && dsa_is_cpu_port(ds, port))
-		tx_pause = rx_pause = true;
+	if (port == dev->imp_port)
+		off = B53_RGMII_CTRL_IMP;
+	else
+		off = B53_RGMII_CTRL_P(port);
 
-	if (phydev->pause) {
-		if (phydev->asym_pause)
-			tx_pause = true;
-		rx_pause = true;
-	}
+	/* Configure the port RGMII clock delay by DLL disabled and
+	 * tx_clk aligned timing (restoring to reset defaults)
+	 */
+	b53_read8(dev, B53_CTRL_PAGE, off, &rgmii_ctrl);
+	rgmii_ctrl &= ~(RGMII_CTRL_DLL_RXC | RGMII_CTRL_DLL_TXC |
+			RGMII_CTRL_TIMING_SEL);
 
-	b53_force_port_config(dev, port, phydev->speed, phydev->duplex,
-			      tx_pause, rx_pause);
-	b53_force_link(dev, port, phydev->link);
+	/* PHY_INTERFACE_MODE_RGMII_TXID means TX internal delay, make
+	 * sure that we enable the port TX clock internal delay to
+	 * account for this internal delay that is inserted, otherwise
+	 * the switch won't be able to receive correctly.
+	 *
+	 * PHY_INTERFACE_MODE_RGMII means that we are not introducing
+	 * any delay neither on transmission nor reception, so the
+	 * BCM53125 must also be configured accordingly to account for
+	 * the lack of delay and introduce
+	 *
+	 * The BCM53125 switch has its RX clock and TX clock control
+	 * swapped, hence the reason why we modify the TX clock path in
+	 * the "RGMII" case
+	 */
+	if (interface == PHY_INTERFACE_MODE_RGMII_TXID)
+		rgmii_ctrl |= RGMII_CTRL_DLL_TXC;
+	if (interface == PHY_INTERFACE_MODE_RGMII)
+		rgmii_ctrl |= RGMII_CTRL_DLL_TXC | RGMII_CTRL_DLL_RXC;
+	rgmii_ctrl |= RGMII_CTRL_TIMING_SEL;
+	b53_write8(dev, B53_CTRL_PAGE, off, rgmii_ctrl);
 
-	if (is63xx(dev) && port >= B53_63XX_RGMII0)
-		b53_adjust_63xx_rgmii(ds, port, phydev->interface);
+	dev_info(ds->dev, "Configured port %d for %s\n", port,
+		 phy_modes(interface));
+}
 
-	if (is531x5(dev) && phy_interface_is_rgmii(phydev)) {
-		if (port == dev->imp_port)
-			off = B53_RGMII_CTRL_IMP;
-		else
-			off = B53_RGMII_CTRL_P(port);
+static void b53_adjust_5325_mii(struct dsa_switch *ds, int port)
+{
+	struct b53_device *dev = ds->priv;
+	u8 reg = 0;
 
-		/* Configure the port RGMII clock delay by DLL disabled and
-		 * tx_clk aligned timing (restoring to reset defaults)
-		 */
-		b53_read8(dev, B53_CTRL_PAGE, off, &rgmii_ctrl);
-		rgmii_ctrl &= ~(RGMII_CTRL_DLL_RXC | RGMII_CTRL_DLL_TXC |
-				RGMII_CTRL_TIMING_SEL);
-
-		/* PHY_INTERFACE_MODE_RGMII_TXID means TX internal delay, make
-		 * sure that we enable the port TX clock internal delay to
-		 * account for this internal delay that is inserted, otherwise
-		 * the switch won't be able to receive correctly.
-		 *
-		 * PHY_INTERFACE_MODE_RGMII means that we are not introducing
-		 * any delay neither on transmission nor reception, so the
-		 * BCM53125 must also be configured accordingly to account for
-		 * the lack of delay and introduce
-		 *
-		 * The BCM53125 switch has its RX clock and TX clock control
-		 * swapped, hence the reason why we modify the TX clock path in
-		 * the "RGMII" case
-		 */
-		if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
-			rgmii_ctrl |= RGMII_CTRL_DLL_TXC;
-		if (phydev->interface == PHY_INTERFACE_MODE_RGMII)
-			rgmii_ctrl |= RGMII_CTRL_DLL_TXC | RGMII_CTRL_DLL_RXC;
-		rgmii_ctrl |= RGMII_CTRL_TIMING_SEL;
-		b53_write8(dev, B53_CTRL_PAGE, off, rgmii_ctrl);
-
-		dev_info(ds->dev, "Configured port %d for %s\n", port,
-			 phy_modes(phydev->interface));
-	}
+	b53_read8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL,
+		  &reg);
 
-	/* configure MII port if necessary */
-	if (is5325(dev)) {
+	/* reverse mii needs to be enabled */
+	if (!(reg & PORT_OVERRIDE_RV_MII_25)) {
+		b53_write8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL,
+			   reg | PORT_OVERRIDE_RV_MII_25);
 		b53_read8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL,
 			  &reg);
 
-		/* reverse mii needs to be enabled */
 		if (!(reg & PORT_OVERRIDE_RV_MII_25)) {
-			b53_write8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL,
-				   reg | PORT_OVERRIDE_RV_MII_25);
-			b53_read8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL,
-				  &reg);
-
-			if (!(reg & PORT_OVERRIDE_RV_MII_25)) {
-				dev_err(ds->dev,
-					"Failed to enable reverse MII mode\n");
-				return;
-			}
+			dev_err(ds->dev,
+				"Failed to enable reverse MII mode\n");
+			return;
 		}
 	}
-
-	/* Re-negotiate EEE if it was enabled already */
-	p->eee_enabled = b53_eee_init(ds, port, phydev);
 }
 
 void b53_port_event(struct dsa_switch *ds, int port)
@@ -1408,35 +1383,53 @@ static void b53_phylink_get_caps(struct dsa_switch *ds, int port,
 		dev->ops->phylink_get_caps(dev, port, config);
 }
 
-static struct phylink_pcs *b53_phylink_mac_select_pcs(struct dsa_switch *ds,
-						      int port,
+static struct phylink_pcs *b53_phylink_mac_select_pcs(struct phylink_config *config,
 						      phy_interface_t interface)
 {
-	struct b53_device *dev = ds->priv;
+	struct dsa_port *dp = dsa_phylink_to_port(config);
+	struct b53_device *dev = dp->ds->priv;
 
 	if (!dev->ops->phylink_mac_select_pcs)
 		return NULL;
 
-	return dev->ops->phylink_mac_select_pcs(dev, port, interface);
+	return dev->ops->phylink_mac_select_pcs(dev, dp->index, interface);
 }
 
-void b53_phylink_mac_config(struct dsa_switch *ds, int port,
-			    unsigned int mode,
-			    const struct phylink_link_state *state)
+static void b53_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);
+	phy_interface_t interface = state->interface;
+	struct dsa_switch *ds = dp->ds;
+	struct b53_device *dev = ds->priv;
+	int port = dp->index;
+
+	if (is63xx(dev) && port >= B53_63XX_RGMII0)
+		b53_adjust_63xx_rgmii(ds, port, interface);
+
+	if (mode == MLO_AN_FIXED) {
+		if (is531x5(dev) && phy_interface_mode_is_rgmii(interface))
+			b53_adjust_531x5_rgmii(ds, port, interface);
+
+		/* configure MII port if necessary */
+		if (is5325(dev))
+			b53_adjust_5325_mii(ds, port);
+	}
 }
-EXPORT_SYMBOL(b53_phylink_mac_config);
 
-void b53_phylink_mac_link_down(struct dsa_switch *ds, int port,
-			       unsigned int mode,
-			       phy_interface_t interface)
+static void b53_phylink_mac_link_down(struct phylink_config *config,
+				      unsigned int mode,
+				      phy_interface_t interface)
 {
-	struct b53_device *dev = ds->priv;
+	struct dsa_port *dp = dsa_phylink_to_port(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;
 	}
@@ -1445,24 +1438,32 @@ void b53_phylink_mac_link_down(struct dsa_switch *ds, int port,
 	    dev->ops->serdes_link_set)
 		dev->ops->serdes_link_set(dev, port, mode, interface, false);
 }
-EXPORT_SYMBOL(b53_phylink_mac_link_down);
 
-void b53_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)
+static void b53_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;
 	struct b53_device *dev = ds->priv;
+	struct ethtool_eee *p = &dev->ports[dp->index].eee;
+	int port = dp->index;
 
-	if (is63xx(dev) && port >= B53_63XX_RGMII0)
-		b53_adjust_63xx_rgmii(ds, port, interface);
-
-	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))
+			tx_pause = rx_pause = true;
+
 		b53_force_port_config(dev, port, speed, duplex,
 				      tx_pause, rx_pause);
 		b53_force_link(dev, port, true);
@@ -1473,7 +1474,6 @@ void b53_phylink_mac_link_up(struct dsa_switch *ds, int port,
 	    dev->ops->serdes_link_set)
 		dev->ops->serdes_link_set(dev, port, mode, interface, true);
 }
-EXPORT_SYMBOL(b53_phylink_mac_link_up);
 
 int b53_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering,
 		       struct netlink_ext_ack *extack)
@@ -2268,6 +2268,13 @@ static int b53_get_max_mtu(struct dsa_switch *ds, int port)
 	return JMS_MAX_SIZE;
 }
 
+static const struct phylink_mac_ops b53_phylink_mac_ops = {
+	.mac_select_pcs	= b53_phylink_mac_select_pcs,
+	.mac_config	= b53_phylink_mac_config,
+	.mac_link_down	= b53_phylink_mac_link_down,
+	.mac_link_up	= b53_phylink_mac_link_up,
+};
+
 static const struct dsa_switch_ops b53_switch_ops = {
 	.get_tag_protocol	= b53_get_tag_protocol,
 	.setup			= b53_setup,
@@ -2278,12 +2285,7 @@ static const struct dsa_switch_ops b53_switch_ops = {
 	.get_ethtool_phy_stats	= b53_get_ethtool_phy_stats,
 	.phy_read		= b53_phy_read16,
 	.phy_write		= b53_phy_write16,
-	.adjust_link		= b53_adjust_link,
 	.phylink_get_caps	= b53_phylink_get_caps,
-	.phylink_mac_select_pcs	= b53_phylink_mac_select_pcs,
-	.phylink_mac_config	= b53_phylink_mac_config,
-	.phylink_mac_link_down	= b53_phylink_mac_link_down,
-	.phylink_mac_link_up	= b53_phylink_mac_link_up,
 	.port_enable		= b53_enable_port,
 	.port_disable		= b53_disable_port,
 	.get_mac_eee		= b53_get_mac_eee,
@@ -2726,6 +2728,7 @@ struct b53_device *b53_switch_alloc(struct device *base,
 	dev->priv = priv;
 	dev->ops = ops;
 	ds->ops = &b53_switch_ops;
+	ds->phylink_mac_ops = &b53_phylink_mac_ops;
 	dev->vlan_enabled = true;
 	/* Let DSA handle the case were multiple bridges span the same switch
 	 * device and different VLAN awareness settings are requested, which
diff --git a/drivers/net/dsa/b53/b53_priv.h b/drivers/net/dsa/b53/b53_priv.h
index c13a907947f1..05141176daf5 100644
--- a/drivers/net/dsa/b53/b53_priv.h
+++ b/drivers/net/dsa/b53/b53_priv.h
@@ -352,18 +352,6 @@ int b53_br_flags(struct dsa_switch *ds, int port,
 		 struct netlink_ext_ack *extack);
 int b53_setup_devlink_resources(struct dsa_switch *ds);
 void b53_port_event(struct dsa_switch *ds, int port);
-void b53_phylink_mac_config(struct dsa_switch *ds, int port,
-			    unsigned int mode,
-			    const struct phylink_link_state *state);
-void b53_phylink_mac_link_down(struct dsa_switch *ds, int port,
-			       unsigned int mode,
-			       phy_interface_t interface);
-void b53_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);
 int b53_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering,
 		       struct netlink_ext_ack *extack);
 int b53_vlan_add(struct dsa_switch *ds, int port,
diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c
index bc77ee9e6d0a..41045e83a384 100644
--- a/drivers/net/dsa/bcm_sf2.c
+++ b/drivers/net/dsa/bcm_sf2.c
@@ -740,16 +740,19 @@ static void bcm_sf2_sw_get_caps(struct dsa_switch *ds, int port,
 		MAC_10 | MAC_100 | MAC_1000;
 }
 
-static void bcm_sf2_sw_mac_config(struct dsa_switch *ds, int port,
+static void bcm_sf2_sw_mac_config(struct phylink_config *config,
 				  unsigned int mode,
 				  const struct phylink_link_state *state)
 {
-	struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
+	struct dsa_port *dp = dsa_phylink_to_port(config);
 	u32 id_mode_dis = 0, port_mode;
+	struct bcm_sf2_priv *priv;
 	u32 reg_rgmii_ctrl;
 	u32 reg;
 
-	if (port == core_readl(priv, CORE_IMP0_PRT_ID))
+	priv = bcm_sf2_to_priv(dp->ds);
+
+	if (dp->index == core_readl(priv, CORE_IMP0_PRT_ID))
 		return;
 
 	switch (state->interface) {
@@ -770,7 +773,7 @@ static void bcm_sf2_sw_mac_config(struct dsa_switch *ds, int port,
 		return;
 	}
 
-	reg_rgmii_ctrl = bcm_sf2_reg_rgmii_cntrl(priv, port);
+	reg_rgmii_ctrl = bcm_sf2_reg_rgmii_cntrl(priv, dp->index);
 
 	/* Clear id_mode_dis bit, and the existing port mode, let
 	 * RGMII_MODE_EN bet set by mac_link_{up,down}
@@ -809,13 +812,16 @@ static void bcm_sf2_sw_mac_link_set(struct dsa_switch *ds, int port,
 	reg_writel(priv, reg, reg_rgmii_ctrl);
 }
 
-static void bcm_sf2_sw_mac_link_down(struct dsa_switch *ds, int port,
+static void bcm_sf2_sw_mac_link_down(struct phylink_config *config,
 				     unsigned int mode,
 				     phy_interface_t interface)
 {
-	struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
+	struct dsa_port *dp = dsa_phylink_to_port(config);
+	struct bcm_sf2_priv *priv;
+	int port = dp->index;
 	u32 reg, offset;
 
+	priv = bcm_sf2_to_priv(dp->ds);
 	if (priv->wol_ports_mask & BIT(port))
 		return;
 
@@ -824,23 +830,26 @@ static void bcm_sf2_sw_mac_link_down(struct dsa_switch *ds, int port,
 	reg &= ~LINK_STS;
 	core_writel(priv, reg, offset);
 
-	bcm_sf2_sw_mac_link_set(ds, port, interface, false);
+	bcm_sf2_sw_mac_link_set(dp->ds, port, interface, false);
 }
 
-static void bcm_sf2_sw_mac_link_up(struct dsa_switch *ds, int port,
+static void bcm_sf2_sw_mac_link_up(struct phylink_config *config,
+				   struct phy_device *phydev,
 				   unsigned int mode,
 				   phy_interface_t interface,
-				   struct phy_device *phydev,
 				   int speed, int duplex,
 				   bool tx_pause, bool rx_pause)
 {
-	struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
-	struct ethtool_keee *p = &priv->dev->ports[port].eee;
+	struct dsa_port *dp = dsa_phylink_to_port(config);
+	struct bcm_sf2_priv *priv;
 	u32 reg_rgmii_ctrl = 0;
+	struct ethtool_keee *p;
+	int port = dp->index;
 	u32 reg, offset;
 
-	bcm_sf2_sw_mac_link_set(ds, port, interface, true);
+	bcm_sf2_sw_mac_link_set(dp->ds, port, interface, true);
 
+	priv = bcm_sf2_to_priv(dp->ds);
 	offset = bcm_sf2_port_override_offset(priv, port);
 
 	if (phy_interface_mode_is_rgmii(interface) ||
@@ -886,8 +895,10 @@ static void bcm_sf2_sw_mac_link_up(struct dsa_switch *ds, int port,
 
 	core_writel(priv, reg, offset);
 
-	if (mode == MLO_AN_PHY && phydev)
-		p->eee_enabled = b53_eee_init(ds, port, phydev);
+	if (phylink_mode_phy(mode) && phydev) {
+		p = &priv->dev->ports[port].eee;
+		p->eee_enabled = b53_eee_init(dp->ds, port, phydev);
+	}
 }
 
 static void bcm_sf2_sw_fixed_state(struct dsa_switch *ds, int port,
@@ -1196,6 +1207,12 @@ static int bcm_sf2_sw_get_sset_count(struct dsa_switch *ds, int port,
 	return cnt;
 }
 
+static const struct phylink_mac_ops bcm_sf2_phylink_mac_ops = {
+	.mac_config	= bcm_sf2_sw_mac_config,
+	.mac_link_down	= bcm_sf2_sw_mac_link_down,
+	.mac_link_up	= bcm_sf2_sw_mac_link_up,
+};
+
 static const struct dsa_switch_ops bcm_sf2_ops = {
 	.get_tag_protocol	= b53_get_tag_protocol,
 	.setup			= bcm_sf2_sw_setup,
@@ -1206,9 +1223,6 @@ static const struct dsa_switch_ops bcm_sf2_ops = {
 	.get_ethtool_phy_stats	= b53_get_ethtool_phy_stats,
 	.get_phy_flags		= bcm_sf2_sw_get_phy_flags,
 	.phylink_get_caps	= bcm_sf2_sw_get_caps,
-	.phylink_mac_config	= bcm_sf2_sw_mac_config,
-	.phylink_mac_link_down	= bcm_sf2_sw_mac_link_down,
-	.phylink_mac_link_up	= bcm_sf2_sw_mac_link_up,
 	.phylink_fixed_state	= bcm_sf2_sw_fixed_state,
 	.suspend		= bcm_sf2_sw_suspend,
 	.resume			= bcm_sf2_sw_resume,
@@ -1399,6 +1413,7 @@ static int bcm_sf2_sw_probe(struct platform_device *pdev)
 	priv->dev = dev;
 	ds = dev->ds;
 	ds->ops = &bcm_sf2_ops;
+	ds->phylink_mac_ops = &bcm_sf2_phylink_mac_ops;
 
 	/* Advertise the 8 egress queues */
 	ds->num_tx_queues = SF2_NUM_EGRESS_QUEUES;
diff --git a/drivers/net/dsa/lan9303-core.c b/drivers/net/dsa/lan9303-core.c
index fcb20eac332a..666b4d766c00 100644
--- a/drivers/net/dsa/lan9303-core.c
+++ b/drivers/net/dsa/lan9303-core.c
@@ -1293,14 +1293,29 @@ static void lan9303_phylink_get_caps(struct dsa_switch *ds, int port,
 	}
 }
 
-static void lan9303_phylink_mac_link_up(struct dsa_switch *ds, int port,
+static void lan9303_phylink_mac_config(struct phylink_config *config,
+				       unsigned int mode,
+				       const struct phylink_link_state *state)
+{
+}
+
+static void lan9303_phylink_mac_link_down(struct phylink_config *config,
+					  unsigned int mode,
+					  phy_interface_t interface)
+{
+}
+
+static void lan9303_phylink_mac_link_up(struct phylink_config *config,
+					struct phy_device *phydev,
 					unsigned int mode,
 					phy_interface_t interface,
-					struct phy_device *phydev, int speed,
-					int duplex, bool tx_pause,
+					int speed, int duplex, bool tx_pause,
 					bool rx_pause)
 {
-	struct lan9303 *chip = ds->priv;
+	struct dsa_port *dp = dsa_phylink_to_port(config);
+	struct lan9303 *chip = dp->ds->priv;
+	struct dsa_switch *ds = dp->ds;
+	int port = dp->index;
 	u32 ctl;
 	u32 reg;
 
@@ -1330,6 +1345,12 @@ static void lan9303_phylink_mac_link_up(struct dsa_switch *ds, int port,
 	regmap_write(chip->regmap, flow_ctl_reg[port], reg);
 }
 
+static const struct phylink_mac_ops lan9303_phylink_mac_ops = {
+	.mac_config	= lan9303_phylink_mac_config,
+	.mac_link_down	= lan9303_phylink_mac_link_down,
+	.mac_link_up	= lan9303_phylink_mac_link_up,
+};
+
 static const struct dsa_switch_ops lan9303_switch_ops = {
 	.get_tag_protocol	= lan9303_get_tag_protocol,
 	.setup			= lan9303_setup,
@@ -1337,7 +1358,6 @@ static const struct dsa_switch_ops lan9303_switch_ops = {
 	.phy_read		= lan9303_phy_read,
 	.phy_write		= lan9303_phy_write,
 	.phylink_get_caps	= lan9303_phylink_get_caps,
-	.phylink_mac_link_up	= lan9303_phylink_mac_link_up,
 	.get_ethtool_stats	= lan9303_get_ethtool_stats,
 	.get_sset_count		= lan9303_get_sset_count,
 	.port_enable		= lan9303_port_enable,
@@ -1365,6 +1385,7 @@ static int lan9303_register_switch(struct lan9303 *chip)
 	chip->ds->num_ports = LAN9303_NUM_PORTS;
 	chip->ds->priv = chip;
 	chip->ds->ops = &lan9303_switch_ops;
+	chip->ds->phylink_mac_ops = &lan9303_phylink_mac_ops;
 	base = chip->phy_addr_base;
 	chip->ds->phys_mii_mask = GENMASK(LAN9303_NUM_PORTS - 1 + base, base);
 
diff --git a/drivers/net/dsa/lantiq_gswip.c b/drivers/net/dsa/lantiq_gswip.c
index de48b194048f..a557049e34f5 100644
--- a/drivers/net/dsa/lantiq_gswip.c
+++ b/drivers/net/dsa/lantiq_gswip.c
@@ -1670,11 +1670,13 @@ static void gswip_port_set_pause(struct gswip_priv *priv, int port,
 			mdio_phy, GSWIP_MDIO_PHYp(port));
 }
 
-static void gswip_phylink_mac_config(struct dsa_switch *ds, int port,
+static void gswip_phylink_mac_config(struct phylink_config *config,
 				     unsigned int mode,
 				     const struct phylink_link_state *state)
 {
-	struct gswip_priv *priv = ds->priv;
+	struct dsa_port *dp = dsa_phylink_to_port(config);
+	struct gswip_priv *priv = dp->ds->priv;
+	int port = dp->index;
 	u32 miicfg = 0;
 
 	miicfg |= GSWIP_MII_CFG_LDCLKDIS;
@@ -1700,7 +1702,7 @@ static void gswip_phylink_mac_config(struct dsa_switch *ds, int port,
 		miicfg |= GSWIP_MII_CFG_MODE_GMII;
 		break;
 	default:
-		dev_err(ds->dev,
+		dev_err(dp->ds->dev,
 			"Unsupported interface: %d\n", state->interface);
 		return;
 	}
@@ -1726,28 +1728,32 @@ static void gswip_phylink_mac_config(struct dsa_switch *ds, int port,
 	}
 }
 
-static void gswip_phylink_mac_link_down(struct dsa_switch *ds, int port,
+static void gswip_phylink_mac_link_down(struct phylink_config *config,
 					unsigned int mode,
 					phy_interface_t interface)
 {
-	struct gswip_priv *priv = ds->priv;
+	struct dsa_port *dp = dsa_phylink_to_port(config);
+	struct gswip_priv *priv = dp->ds->priv;
+	int port = dp->index;
 
 	gswip_mii_mask_cfg(priv, GSWIP_MII_CFG_EN, 0, port);
 
-	if (!dsa_is_cpu_port(ds, port))
+	if (!dsa_port_is_cpu(dp))
 		gswip_port_set_link(priv, port, false);
 }
 
-static void gswip_phylink_mac_link_up(struct dsa_switch *ds, int port,
+static void gswip_phylink_mac_link_up(struct phylink_config *config,
+				      struct phy_device *phydev,
 				      unsigned int mode,
 				      phy_interface_t interface,
-				      struct phy_device *phydev,
 				      int speed, int duplex,
 				      bool tx_pause, bool rx_pause)
 {
-	struct gswip_priv *priv = ds->priv;
+	struct dsa_port *dp = dsa_phylink_to_port(config);
+	struct gswip_priv *priv = dp->ds->priv;
+	int port = dp->index;
 
-	if (!dsa_is_cpu_port(ds, port)) {
+	if (!dsa_port_is_cpu(dp)) {
 		gswip_port_set_link(priv, port, true);
 		gswip_port_set_speed(priv, port, speed, interface);
 		gswip_port_set_duplex(priv, port, duplex);
@@ -1824,6 +1830,12 @@ static int gswip_get_sset_count(struct dsa_switch *ds, int port, int sset)
 	return ARRAY_SIZE(gswip_rmon_cnt);
 }
 
+static const struct phylink_mac_ops gswip_phylink_mac_ops = {
+	.mac_config	= gswip_phylink_mac_config,
+	.mac_link_down	= gswip_phylink_mac_link_down,
+	.mac_link_up	= gswip_phylink_mac_link_up,
+};
+
 static const struct dsa_switch_ops gswip_xrx200_switch_ops = {
 	.get_tag_protocol	= gswip_get_tag_protocol,
 	.setup			= gswip_setup,
@@ -1842,9 +1854,6 @@ static const struct dsa_switch_ops gswip_xrx200_switch_ops = {
 	.port_change_mtu	= gswip_port_change_mtu,
 	.port_max_mtu		= gswip_port_max_mtu,
 	.phylink_get_caps	= gswip_xrx200_phylink_get_caps,
-	.phylink_mac_config	= gswip_phylink_mac_config,
-	.phylink_mac_link_down	= gswip_phylink_mac_link_down,
-	.phylink_mac_link_up	= gswip_phylink_mac_link_up,
 	.get_strings		= gswip_get_strings,
 	.get_ethtool_stats	= gswip_get_ethtool_stats,
 	.get_sset_count		= gswip_get_sset_count,
@@ -1868,9 +1877,6 @@ static const struct dsa_switch_ops gswip_xrx300_switch_ops = {
 	.port_change_mtu	= gswip_port_change_mtu,
 	.port_max_mtu		= gswip_port_max_mtu,
 	.phylink_get_caps	= gswip_xrx300_phylink_get_caps,
-	.phylink_mac_config	= gswip_phylink_mac_config,
-	.phylink_mac_link_down	= gswip_phylink_mac_link_down,
-	.phylink_mac_link_up	= gswip_phylink_mac_link_up,
 	.get_strings		= gswip_get_strings,
 	.get_ethtool_stats	= gswip_get_ethtool_stats,
 	.get_sset_count		= gswip_get_sset_count,
@@ -2136,6 +2142,7 @@ static int gswip_probe(struct platform_device *pdev)
 	priv->ds->num_ports = priv->hw_info->max_ports;
 	priv->ds->priv = priv;
 	priv->ds->ops = priv->hw_info->ops;
+	priv->ds->phylink_mac_ops = &gswip_phylink_mac_ops;
 	priv->dev = dev;
 	mutex_init(&priv->pce_table_lock);
 	version = gswip_switch_r(priv, GSWIP_VERSION);
diff --git a/drivers/net/dsa/microchip/ksz8.h b/drivers/net/dsa/microchip/ksz8.h
index 1a5225264e6a..719692c8c858 100644
--- a/drivers/net/dsa/microchip/ksz8.h
+++ b/drivers/net/dsa/microchip/ksz8.h
@@ -56,9 +56,9 @@ int ksz8_reset_switch(struct ksz_device *dev);
 int ksz8_switch_init(struct ksz_device *dev);
 void ksz8_switch_exit(struct ksz_device *dev);
 int ksz8_change_mtu(struct ksz_device *dev, int port, int mtu);
-void ksz8_phylink_mac_link_up(struct ksz_device *dev, int port,
-			      unsigned int mode, phy_interface_t interface,
-			      struct phy_device *phydev, int speed, int duplex,
+void ksz8_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);
 
 #endif
diff --git a/drivers/net/dsa/microchip/ksz8795.c b/drivers/net/dsa/microchip/ksz8795.c
index 14923535ca7e..fa0523d4d123 100644
--- a/drivers/net/dsa/microchip/ksz8795.c
+++ b/drivers/net/dsa/microchip/ksz8795.c
@@ -1701,11 +1701,15 @@ static void ksz8_cpu_port_link_up(struct ksz_device *dev, int speed, int duplex,
 		 SW_10_MBIT, ctrl);
 }
 
-void ksz8_phylink_mac_link_up(struct ksz_device *dev, int port,
-			      unsigned int mode, phy_interface_t interface,
-			      struct phy_device *phydev, int speed, int duplex,
+void ksz8_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 ksz_device *dev = dp->ds->priv;
+	int port = dp->index;
+
 	/* If the port is the CPU port, apply special handling. Only the CPU
 	 * port is configured via global registers.
 	 */
diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c
index 2b510f150dd8..b3ddc1604161 100644
--- a/drivers/net/dsa/microchip/ksz_common.c
+++ b/drivers/net/dsa/microchip/ksz_common.c
@@ -253,6 +253,28 @@ static const struct ksz_drive_strength ksz8830_drive_strengths[] = {
 	{ KSZ8873_DRIVE_STRENGTH_16MA, 16000 },
 };
 
+static void ksz8830_phylink_mac_config(struct phylink_config *config,
+				       unsigned int mode,
+				       const struct phylink_link_state *state);
+static void ksz_phylink_mac_config(struct phylink_config *config,
+				   unsigned int mode,
+				   const struct phylink_link_state *state);
+static void ksz_phylink_mac_link_down(struct phylink_config *config,
+				      unsigned int mode,
+				      phy_interface_t interface);
+
+static const struct phylink_mac_ops ksz8830_phylink_mac_ops = {
+	.mac_config	= ksz8830_phylink_mac_config,
+	.mac_link_down	= ksz_phylink_mac_link_down,
+	.mac_link_up	= ksz8_phylink_mac_link_up,
+};
+
+static const struct phylink_mac_ops ksz8_phylink_mac_ops = {
+	.mac_config	= ksz_phylink_mac_config,
+	.mac_link_down	= ksz_phylink_mac_link_down,
+	.mac_link_up	= ksz8_phylink_mac_link_up,
+};
+
 static const struct ksz_dev_ops ksz8_dev_ops = {
 	.setup = ksz8_setup,
 	.get_port_addr = ksz8_get_port_addr,
@@ -277,7 +299,6 @@ static const struct ksz_dev_ops ksz8_dev_ops = {
 	.mirror_add = ksz8_port_mirror_add,
 	.mirror_del = ksz8_port_mirror_del,
 	.get_caps = ksz8_get_caps,
-	.phylink_mac_link_up = ksz8_phylink_mac_link_up,
 	.config_cpu_port = ksz8_config_cpu_port,
 	.enable_stp_addr = ksz8_enable_stp_addr,
 	.reset = ksz8_reset_switch,
@@ -286,13 +307,19 @@ static const struct ksz_dev_ops ksz8_dev_ops = {
 	.change_mtu = ksz8_change_mtu,
 };
 
-static void ksz9477_phylink_mac_link_up(struct ksz_device *dev, int port,
+static void ksz9477_phylink_mac_link_up(struct phylink_config *config,
+					struct phy_device *phydev,
 					unsigned int mode,
 					phy_interface_t interface,
-					struct phy_device *phydev, int speed,
-					int duplex, bool tx_pause,
+					int speed, int duplex, bool tx_pause,
 					bool rx_pause);
 
+static const struct phylink_mac_ops ksz9477_phylink_mac_ops = {
+	.mac_config	= ksz_phylink_mac_config,
+	.mac_link_down	= ksz_phylink_mac_link_down,
+	.mac_link_up	= ksz9477_phylink_mac_link_up,
+};
+
 static const struct ksz_dev_ops ksz9477_dev_ops = {
 	.setup = ksz9477_setup,
 	.get_port_addr = ksz9477_get_port_addr,
@@ -319,7 +346,6 @@ static const struct ksz_dev_ops ksz9477_dev_ops = {
 	.mdb_add = ksz9477_mdb_add,
 	.mdb_del = ksz9477_mdb_del,
 	.change_mtu = ksz9477_change_mtu,
-	.phylink_mac_link_up = ksz9477_phylink_mac_link_up,
 	.get_wol = ksz9477_get_wol,
 	.set_wol = ksz9477_set_wol,
 	.wol_pre_shutdown = ksz9477_wol_pre_shutdown,
@@ -331,6 +357,12 @@ static const struct ksz_dev_ops ksz9477_dev_ops = {
 	.exit = ksz9477_switch_exit,
 };
 
+static const struct phylink_mac_ops lan937x_phylink_mac_ops = {
+	.mac_config	= ksz_phylink_mac_config,
+	.mac_link_down	= ksz_phylink_mac_link_down,
+	.mac_link_up	= ksz9477_phylink_mac_link_up,
+};
+
 static const struct ksz_dev_ops lan937x_dev_ops = {
 	.setup = lan937x_setup,
 	.teardown = lan937x_teardown,
@@ -359,7 +391,6 @@ static const struct ksz_dev_ops lan937x_dev_ops = {
 	.mdb_add = ksz9477_mdb_add,
 	.mdb_del = ksz9477_mdb_del,
 	.change_mtu = lan937x_change_mtu,
-	.phylink_mac_link_up = ksz9477_phylink_mac_link_up,
 	.config_cpu_port = lan937x_config_cpu_port,
 	.tc_cbs_set_cinc = lan937x_tc_cbs_set_cinc,
 	.enable_stp_addr = ksz9477_enable_stp_addr,
@@ -1197,6 +1228,7 @@ const struct ksz_chip_data ksz_switch_chips[] = {
 		.tc_cbs_supported = true,
 		.tc_ets_supported = true,
 		.ops = &ksz9477_dev_ops,
+		.phylink_mac_ops = &ksz9477_phylink_mac_ops,
 		.mib_names = ksz9477_mib_names,
 		.mib_cnt = ARRAY_SIZE(ksz9477_mib_names),
 		.reg_mib_cnt = MIB_COUNTER_NUM,
@@ -1224,6 +1256,7 @@ const struct ksz_chip_data ksz_switch_chips[] = {
 		.port_cnt = 5,		/* total cpu and user ports */
 		.num_tx_queues = 4,
 		.ops = &ksz8_dev_ops,
+		.phylink_mac_ops = &ksz8_phylink_mac_ops,
 		.ksz87xx_eee_link_erratum = true,
 		.mib_names = ksz9477_mib_names,
 		.mib_cnt = ARRAY_SIZE(ksz9477_mib_names),
@@ -1263,6 +1296,7 @@ const struct ksz_chip_data ksz_switch_chips[] = {
 		.port_cnt = 5,		/* total cpu and user ports */
 		.num_tx_queues = 4,
 		.ops = &ksz8_dev_ops,
+		.phylink_mac_ops = &ksz8_phylink_mac_ops,
 		.ksz87xx_eee_link_erratum = true,
 		.mib_names = ksz9477_mib_names,
 		.mib_cnt = ARRAY_SIZE(ksz9477_mib_names),
@@ -1288,6 +1322,7 @@ const struct ksz_chip_data ksz_switch_chips[] = {
 		.port_cnt = 5,		/* total cpu and user ports */
 		.num_tx_queues = 4,
 		.ops = &ksz8_dev_ops,
+		.phylink_mac_ops = &ksz8_phylink_mac_ops,
 		.ksz87xx_eee_link_erratum = true,
 		.mib_names = ksz9477_mib_names,
 		.mib_cnt = ARRAY_SIZE(ksz9477_mib_names),
@@ -1313,6 +1348,7 @@ const struct ksz_chip_data ksz_switch_chips[] = {
 		.port_cnt = 3,
 		.num_tx_queues = 4,
 		.ops = &ksz8_dev_ops,
+		.phylink_mac_ops = &ksz8830_phylink_mac_ops,
 		.mib_names = ksz88xx_mib_names,
 		.mib_cnt = ARRAY_SIZE(ksz88xx_mib_names),
 		.reg_mib_cnt = MIB_COUNTER_NUM,
@@ -1339,6 +1375,7 @@ const struct ksz_chip_data ksz_switch_chips[] = {
 		.tc_cbs_supported = true,
 		.tc_ets_supported = true,
 		.ops = &ksz9477_dev_ops,
+		.phylink_mac_ops = &ksz9477_phylink_mac_ops,
 		.mib_names = ksz9477_mib_names,
 		.mib_cnt = ARRAY_SIZE(ksz9477_mib_names),
 		.reg_mib_cnt = MIB_COUNTER_NUM,
@@ -1371,6 +1408,7 @@ const struct ksz_chip_data ksz_switch_chips[] = {
 		.port_nirqs = 2,
 		.num_tx_queues = 4,
 		.ops = &ksz9477_dev_ops,
+		.phylink_mac_ops = &ksz9477_phylink_mac_ops,
 		.mib_names = ksz9477_mib_names,
 		.mib_cnt = ARRAY_SIZE(ksz9477_mib_names),
 		.reg_mib_cnt = MIB_COUNTER_NUM,
@@ -1403,6 +1441,7 @@ const struct ksz_chip_data ksz_switch_chips[] = {
 		.port_nirqs = 2,
 		.num_tx_queues = 4,
 		.ops = &ksz9477_dev_ops,
+		.phylink_mac_ops = &ksz9477_phylink_mac_ops,
 		.mib_names = ksz9477_mib_names,
 		.mib_cnt = ARRAY_SIZE(ksz9477_mib_names),
 		.reg_mib_cnt = MIB_COUNTER_NUM,
@@ -1433,6 +1472,7 @@ const struct ksz_chip_data ksz_switch_chips[] = {
 		.port_nirqs = 2,
 		.num_tx_queues = 4,
 		.ops = &ksz9477_dev_ops,
+		.phylink_mac_ops = &ksz9477_phylink_mac_ops,
 		.mib_names = ksz9477_mib_names,
 		.mib_cnt = ARRAY_SIZE(ksz9477_mib_names),
 		.reg_mib_cnt = MIB_COUNTER_NUM,
@@ -1461,6 +1501,7 @@ const struct ksz_chip_data ksz_switch_chips[] = {
 		.tc_cbs_supported = true,
 		.tc_ets_supported = true,
 		.ops = &ksz9477_dev_ops,
+		.phylink_mac_ops = &ksz9477_phylink_mac_ops,
 		.mib_names = ksz9477_mib_names,
 		.mib_cnt = ARRAY_SIZE(ksz9477_mib_names),
 		.reg_mib_cnt = MIB_COUNTER_NUM,
@@ -1522,6 +1563,7 @@ const struct ksz_chip_data ksz_switch_chips[] = {
 		.tc_cbs_supported = true,
 		.tc_ets_supported = true,
 		.ops = &ksz9477_dev_ops,
+		.phylink_mac_ops = &ksz9477_phylink_mac_ops,
 		.mib_names = ksz9477_mib_names,
 		.mib_cnt = ARRAY_SIZE(ksz9477_mib_names),
 		.reg_mib_cnt = MIB_COUNTER_NUM,
@@ -1554,6 +1596,7 @@ const struct ksz_chip_data ksz_switch_chips[] = {
 		.tc_cbs_supported = true,
 		.tc_ets_supported = true,
 		.ops = &lan937x_dev_ops,
+		.phylink_mac_ops = &lan937x_phylink_mac_ops,
 		.mib_names = ksz9477_mib_names,
 		.mib_cnt = ARRAY_SIZE(ksz9477_mib_names),
 		.reg_mib_cnt = MIB_COUNTER_NUM,
@@ -1581,6 +1624,7 @@ const struct ksz_chip_data ksz_switch_chips[] = {
 		.tc_cbs_supported = true,
 		.tc_ets_supported = true,
 		.ops = &lan937x_dev_ops,
+		.phylink_mac_ops = &lan937x_phylink_mac_ops,
 		.mib_names = ksz9477_mib_names,
 		.mib_cnt = ARRAY_SIZE(ksz9477_mib_names),
 		.reg_mib_cnt = MIB_COUNTER_NUM,
@@ -1608,6 +1652,7 @@ const struct ksz_chip_data ksz_switch_chips[] = {
 		.tc_cbs_supported = true,
 		.tc_ets_supported = true,
 		.ops = &lan937x_dev_ops,
+		.phylink_mac_ops = &lan937x_phylink_mac_ops,
 		.mib_names = ksz9477_mib_names,
 		.mib_cnt = ARRAY_SIZE(ksz9477_mib_names),
 		.reg_mib_cnt = MIB_COUNTER_NUM,
@@ -1639,6 +1684,7 @@ const struct ksz_chip_data ksz_switch_chips[] = {
 		.tc_cbs_supported = true,
 		.tc_ets_supported = true,
 		.ops = &lan937x_dev_ops,
+		.phylink_mac_ops = &lan937x_phylink_mac_ops,
 		.mib_names = ksz9477_mib_names,
 		.mib_cnt = ARRAY_SIZE(ksz9477_mib_names),
 		.reg_mib_cnt = MIB_COUNTER_NUM,
@@ -1670,6 +1716,7 @@ const struct ksz_chip_data ksz_switch_chips[] = {
 		.tc_cbs_supported = true,
 		.tc_ets_supported = true,
 		.ops = &lan937x_dev_ops,
+		.phylink_mac_ops = &lan937x_phylink_mac_ops,
 		.mib_names = ksz9477_mib_names,
 		.mib_cnt = ARRAY_SIZE(ksz9477_mib_names),
 		.reg_mib_cnt = MIB_COUNTER_NUM,
@@ -2523,14 +2570,15 @@ static u32 ksz_get_phy_flags(struct dsa_switch *ds, int port)
 	return 0;
 }
 
-static void ksz_mac_link_down(struct dsa_switch *ds, int port,
-			      unsigned int mode, phy_interface_t interface)
+static void ksz_phylink_mac_link_down(struct phylink_config *config,
+				      unsigned int mode,
+				      phy_interface_t interface)
 {
-	struct ksz_device *dev = ds->priv;
-	struct ksz_port *p = &dev->ports[port];
+	struct dsa_port *dp = dsa_phylink_to_port(config);
+	struct ksz_device *dev = dp->ds->priv;
 
 	/* Read all MIB counters when the link is going down. */
-	p->read = true;
+	dev->ports[dp->index].read = true;
 	/* timer started */
 	if (dev->mib_read_interval)
 		schedule_delayed_work(&dev->mib_read, 0);
@@ -3065,16 +3113,23 @@ phy_interface_t ksz_get_xmii(struct ksz_device *dev, int port, bool gbit)
 	return interface;
 }
 
-static void ksz_phylink_mac_config(struct dsa_switch *ds, int port,
+static void ksz8830_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 ksz_device *dev = dp->ds->priv;
+
+	dev->ports[dp->index].manual_flow = !(state->pause & MLO_PAUSE_AN);
+}
+
+static void ksz_phylink_mac_config(struct phylink_config *config,
 				   unsigned int mode,
 				   const struct phylink_link_state *state)
 {
-	struct ksz_device *dev = ds->priv;
-
-	if (ksz_is_ksz88x3(dev)) {
-		dev->ports[port].manual_flow = !(state->pause & MLO_PAUSE_AN);
-		return;
-	}
+	struct dsa_port *dp = dsa_phylink_to_port(config);
+	struct ksz_device *dev = dp->ds->priv;
+	int port = dp->index;
 
 	/* Internal PHYs */
 	if (dev->info->internal_phy[port])
@@ -3087,9 +3142,6 @@ static void ksz_phylink_mac_config(struct dsa_switch *ds, int port,
 
 	ksz_set_xmii(dev, port, state->interface);
 
-	if (dev->dev_ops->phylink_mac_config)
-		dev->dev_ops->phylink_mac_config(dev, port, mode, state);
-
 	if (dev->dev_ops->setup_rgmii_delay)
 		dev->dev_ops->setup_rgmii_delay(dev, port);
 }
@@ -3187,13 +3239,16 @@ static void ksz_duplex_flowctrl(struct ksz_device *dev, int port, int duplex,
 	ksz_prmw8(dev, port, regs[P_XMII_CTRL_0], mask, val);
 }
 
-static void ksz9477_phylink_mac_link_up(struct ksz_device *dev, int port,
+static void ksz9477_phylink_mac_link_up(struct phylink_config *config,
+					struct phy_device *phydev,
 					unsigned int mode,
 					phy_interface_t interface,
-					struct phy_device *phydev, int speed,
-					int duplex, bool tx_pause,
+					int speed, int duplex, bool tx_pause,
 					bool rx_pause)
 {
+	struct dsa_port *dp = dsa_phylink_to_port(config);
+	struct ksz_device *dev = dp->ds->priv;
+	int port = dp->index;
 	struct ksz_port *p;
 
 	p = &dev->ports[port];
@@ -3209,18 +3264,6 @@ static void ksz9477_phylink_mac_link_up(struct ksz_device *dev, int port,
 	ksz_duplex_flowctrl(dev, port, duplex, tx_pause, rx_pause);
 }
 
-static void ksz_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)
-{
-	struct ksz_device *dev = ds->priv;
-
-	dev->dev_ops->phylink_mac_link_up(dev, port, mode, interface, phydev,
-					  speed, duplex, tx_pause, rx_pause);
-}
-
 static int ksz_switch_detect(struct ksz_device *dev)
 {
 	u8 id1, id2, id4;
@@ -3881,9 +3924,6 @@ static const struct dsa_switch_ops ksz_switch_ops = {
 	.phy_read		= ksz_phy_read16,
 	.phy_write		= ksz_phy_write16,
 	.phylink_get_caps	= ksz_phylink_get_caps,
-	.phylink_mac_config	= ksz_phylink_mac_config,
-	.phylink_mac_link_up	= ksz_phylink_mac_link_up,
-	.phylink_mac_link_down	= ksz_mac_link_down,
 	.port_setup		= ksz_port_setup,
 	.set_ageing_time	= ksz_set_ageing_time,
 	.get_strings		= ksz_get_strings,
@@ -4328,6 +4368,9 @@ int ksz_switch_register(struct ksz_device *dev)
 	/* set the real number of ports */
 	dev->ds->num_ports = dev->info->port_cnt;
 
+	/* set the phylink ops */
+	dev->ds->phylink_mac_ops = dev->info->phylink_mac_ops;
+
 	/* Host port interface will be self detected, or specifically set in
 	 * device tree.
 	 */
diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h
index 40c11b0d6b62..9409b844af63 100644
--- a/drivers/net/dsa/microchip/ksz_common.h
+++ b/drivers/net/dsa/microchip/ksz_common.h
@@ -22,6 +22,7 @@
 
 struct ksz_device;
 struct ksz_port;
+struct phylink_mac_ops;
 
 enum ksz_regmap_width {
 	KSZ_REGMAP_8,
@@ -61,6 +62,7 @@ struct ksz_chip_data {
 	bool tc_cbs_supported;
 	bool tc_ets_supported;
 	const struct ksz_dev_ops *ops;
+	const struct phylink_mac_ops *phylink_mac_ops;
 	bool ksz87xx_eee_link_erratum;
 	const struct ksz_mib_names *mib_names;
 	int mib_cnt;
@@ -349,9 +351,6 @@ struct ksz_dev_ops {
 	int (*change_mtu)(struct ksz_device *dev, int port, int mtu);
 	void (*freeze_mib)(struct ksz_device *dev, int port, bool freeze);
 	void (*port_init_cnt)(struct ksz_device *dev, int port);
-	void (*phylink_mac_config)(struct ksz_device *dev, int port,
-				   unsigned int mode,
-				   const struct phylink_link_state *state);
 	void (*phylink_mac_link_up)(struct ksz_device *dev, int port,
 				    unsigned int mode,
 				    phy_interface_t interface,
diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c
index 8090390edaf9..05087573ec8b 100644
--- a/drivers/net/dsa/mt7530.c
+++ b/drivers/net/dsa/mt7530.c
@@ -2459,7 +2459,8 @@ mt7530_setup(struct dsa_switch *ds)
 		 * on all ports until they're enabled later.
 		 */
 		mt7530_rmw(priv, MT7530_PMCR_P(i), PMCR_LINK_SETTINGS_MASK |
-			   PMCR_FORCE_MODE, PMCR_FORCE_MODE);
+			   PMCR_FORCE_MODE | PMCR_FORCE_EEE1G |
+			   PMCR_FORCE_EEE100, PMCR_FORCE_MODE);
 
 		/* Disable forwarding by default on all ports */
 		mt7530_rmw(priv, MT7530_PCR_P(i), PCR_MATRIX_MASK,
@@ -2858,28 +2859,34 @@ mt7531_mac_config(struct dsa_switch *ds, int port, unsigned int mode,
 }
 
 static struct phylink_pcs *
-mt753x_phylink_mac_select_pcs(struct dsa_switch *ds, int port,
+mt753x_phylink_mac_select_pcs(struct phylink_config *config,
 			      phy_interface_t interface)
 {
-	struct mt7530_priv *priv = ds->priv;
+	struct dsa_port *dp = dsa_phylink_to_port(config);
+	struct mt7530_priv *priv = dp->ds->priv;
 
 	switch (interface) {
 	case PHY_INTERFACE_MODE_TRGMII:
-		return &priv->pcs[port].pcs;
+		return &priv->pcs[dp->index].pcs;
 	case PHY_INTERFACE_MODE_SGMII:
 	case PHY_INTERFACE_MODE_1000BASEX:
 	case PHY_INTERFACE_MODE_2500BASEX:
-		return priv->ports[port].sgmii_pcs;
+		return priv->ports[dp->index].sgmii_pcs;
 	default:
 		return NULL;
 	}
 }
 
 static void
-mt753x_phylink_mac_config(struct dsa_switch *ds, int port, unsigned int mode,
+mt753x_phylink_mac_config(struct phylink_config *config, unsigned int mode,
 			  const struct phylink_link_state *state)
 {
-	struct mt7530_priv *priv = ds->priv;
+	struct dsa_port *dp = dsa_phylink_to_port(config);
+	struct dsa_switch *ds = dp->ds;
+	struct mt7530_priv *priv;
+	int port = dp->index;
+
+	priv = ds->priv;
 
 	if ((port == 5 || port == 6) && priv->info->mac_port_config)
 		priv->info->mac_port_config(ds, port, mode, state->interface);
@@ -2889,23 +2896,25 @@ mt753x_phylink_mac_config(struct dsa_switch *ds, int port, unsigned int mode,
 		mt7530_set(priv, MT7530_PMCR_P(port), PMCR_EXT_PHY);
 }
 
-static void mt753x_phylink_mac_link_down(struct dsa_switch *ds, int port,
+static void mt753x_phylink_mac_link_down(struct phylink_config *config,
 					 unsigned int mode,
 					 phy_interface_t interface)
 {
-	struct mt7530_priv *priv = ds->priv;
+	struct dsa_port *dp = dsa_phylink_to_port(config);
+	struct mt7530_priv *priv = dp->ds->priv;
 
-	mt7530_clear(priv, MT7530_PMCR_P(port), PMCR_LINK_SETTINGS_MASK);
+	mt7530_clear(priv, MT7530_PMCR_P(dp->index), PMCR_LINK_SETTINGS_MASK);
 }
 
-static void mt753x_phylink_mac_link_up(struct dsa_switch *ds, int port,
+static void mt753x_phylink_mac_link_up(struct phylink_config *config,
+				       struct phy_device *phydev,
 				       unsigned int mode,
 				       phy_interface_t interface,
-				       struct phy_device *phydev,
 				       int speed, int duplex,
 				       bool tx_pause, bool rx_pause)
 {
-	struct mt7530_priv *priv = ds->priv;
+	struct dsa_port *dp = dsa_phylink_to_port(config);
+	struct mt7530_priv *priv = dp->ds->priv;
 	u32 mcr;
 
 	mcr = PMCR_RX_EN | PMCR_TX_EN | PMCR_FORCE_LNK;
@@ -2928,30 +2937,67 @@ static void mt753x_phylink_mac_link_up(struct dsa_switch *ds, int port,
 			mcr |= PMCR_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, MT7530_PMCR_P(dp->index), mcr);
+}
 
-	mt7530_set(priv, MT7530_PMCR_P(port), 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, MT7530_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, MT7530_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, MT7530_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;
 
 	/* This switch only supports full-duplex at 1Gbps */
 	config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE |
 				   MAC_10 | MAC_100 | MAC_1000FD;
 
+	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, MT7530_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);
 }
 
@@ -3056,36 +3102,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, MT7530_PMEEECR_P(port));
-
-	e->tx_lpi_enabled = !(eeecr & LPI_MODE_EN);
-	e->tx_lpi_timer = GET_LPI_THRESH(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 = SET_LPI_THRESH(e->tx_lpi_timer);
-	if (!e->tx_lpi_enabled)
-		/* Force LPI Mode without a delay */
-		set |= LPI_MODE_EN;
-	mt7530_rmw(priv, MT7530_PMEEECR_P(port), mask, set);
-
-	return 0;
-}
-
 static void
 mt753x_conduit_state_change(struct dsa_switch *ds,
 			    const struct net_device *conduit,
@@ -3160,16 +3176,19 @@ 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,
-	.phylink_mac_select_pcs	= mt753x_phylink_mac_select_pcs,
-	.phylink_mac_config	= mt753x_phylink_mac_config,
-	.phylink_mac_link_down	= mt753x_phylink_mac_link_down,
-	.phylink_mac_link_up	= mt753x_phylink_mac_link_up,
-	.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);
 
+static const struct phylink_mac_ops mt753x_phylink_mac_ops = {
+	.mac_select_pcs	= mt753x_phylink_mac_select_pcs,
+	.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[] = {
 	[ID_MT7621] = {
 		.id = ID_MT7621,
@@ -3247,6 +3266,7 @@ mt7530_probe_common(struct mt7530_priv *priv)
 	priv->dev = dev;
 	priv->ds->priv = priv;
 	priv->ds->ops = &mt7530_switch_ops;
+	priv->ds->phylink_mac_ops = &mt753x_phylink_mac_ops;
 	mutex_init(&priv->reg_mutex);
 	dev_set_drvdata(dev, priv);
 
diff --git a/drivers/net/dsa/mt7530.h b/drivers/net/dsa/mt7530.h
index a08053390b28..8ed16a5ec26c 100644
--- a/drivers/net/dsa/mt7530.h
+++ b/drivers/net/dsa/mt7530.h
@@ -336,16 +336,12 @@ enum mt7530_vlan_port_acc_frm {
 #define  PMCR_LINK_SETTINGS_MASK	(PMCR_TX_EN | PMCR_FORCE_SPEED_1000 | \
 					 PMCR_RX_EN | PMCR_FORCE_SPEED_100 | \
 					 PMCR_TX_FC_EN | PMCR_RX_FC_EN | \
-					 PMCR_FORCE_FDX | PMCR_FORCE_LNK | \
-					 PMCR_FORCE_EEE1G | PMCR_FORCE_EEE100)
+					 PMCR_FORCE_FDX | PMCR_FORCE_LNK)
 
 #define MT7530_PMEEECR_P(x)		(0x3004 + (x) * 0x100)
 #define  WAKEUP_TIME_1000(x)		(((x) & 0xFF) << 24)
 #define  WAKEUP_TIME_100(x)		(((x) & 0xFF) << 16)
 #define  LPI_THRESH_MASK		GENMASK(15, 4)
-#define  LPI_THRESH_SHT			4
-#define  SET_LPI_THRESH(x)		(((x) << LPI_THRESH_SHT) & LPI_THRESH_MASK)
-#define  GET_LPI_THRESH(x)		(((x) & LPI_THRESH_MASK) >> LPI_THRESH_SHT)
 #define  LPI_MODE_EN			BIT(0)
 
 #define MT7530_PMSR_P(x)		(0x3008 + (x) * 0x100)
diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
index 0918bd6fa81d..81794875a46c 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) {
@@ -861,31 +864,34 @@ static void mv88e6xxx_get_caps(struct dsa_switch *ds, int port,
 	}
 }
 
-static struct phylink_pcs *mv88e6xxx_mac_select_pcs(struct dsa_switch *ds,
-						    int port,
-						    phy_interface_t interface)
+static struct phylink_pcs *
+mv88e6xxx_mac_select_pcs(struct phylink_config *config,
+			 phy_interface_t interface)
 {
-	struct mv88e6xxx_chip *chip = ds->priv;
+	struct dsa_port *dp = dsa_phylink_to_port(config);
+	struct mv88e6xxx_chip *chip = dp->ds->priv;
 	struct phylink_pcs *pcs = ERR_PTR(-EOPNOTSUPP);
 
 	if (chip->info->ops->pcs_ops)
-		pcs = chip->info->ops->pcs_ops->pcs_select(chip, port,
+		pcs = chip->info->ops->pcs_ops->pcs_select(chip, dp->index,
 							   interface);
 
 	return pcs;
 }
 
-static int mv88e6xxx_mac_prepare(struct dsa_switch *ds, int port,
+static int mv88e6xxx_mac_prepare(struct phylink_config *config,
 				 unsigned int mode, phy_interface_t interface)
 {
-	struct mv88e6xxx_chip *chip = ds->priv;
+	struct dsa_port *dp = dsa_phylink_to_port(config);
+	struct mv88e6xxx_chip *chip = dp->ds->priv;
+	int port = dp->index;
 	int err = 0;
 
 	/* In inband mode, the link may come up at any time while the link
 	 * 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);
@@ -897,16 +903,18 @@ static int mv88e6xxx_mac_prepare(struct dsa_switch *ds, int port,
 	return err;
 }
 
-static void mv88e6xxx_mac_config(struct dsa_switch *ds, int port,
+static void mv88e6xxx_mac_config(struct phylink_config *config,
 				 unsigned int mode,
 				 const struct phylink_link_state *state)
 {
-	struct mv88e6xxx_chip *chip = ds->priv;
+	struct dsa_port *dp = dsa_phylink_to_port(config);
+	struct mv88e6xxx_chip *chip = dp->ds->priv;
+	int port = dp->index;
 	int err = 0;
 
 	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)
@@ -917,13 +925,15 @@ static void mv88e6xxx_mac_config(struct dsa_switch *ds, int port,
 	mv88e6xxx_reg_unlock(chip);
 
 	if (err && err != -EOPNOTSUPP)
-		dev_err(ds->dev, "p%d: failed to configure MAC/PCS\n", port);
+		dev_err(chip->dev, "p%d: failed to configure MAC/PCS\n", port);
 }
 
-static int mv88e6xxx_mac_finish(struct dsa_switch *ds, int port,
+static int mv88e6xxx_mac_finish(struct phylink_config *config,
 				unsigned int mode, phy_interface_t interface)
 {
-	struct mv88e6xxx_chip *chip = ds->priv;
+	struct dsa_port *dp = dsa_phylink_to_port(config);
+	struct mv88e6xxx_chip *chip = dp->ds->priv;
+	int port = dp->index;
 	int err = 0;
 
 	/* Undo the forced down state above after completing configuration
@@ -935,9 +945,10 @@ static int mv88e6xxx_mac_finish(struct dsa_switch *ds, int port,
 	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);
@@ -947,12 +958,14 @@ static int mv88e6xxx_mac_finish(struct dsa_switch *ds, int port,
 	return err;
 }
 
-static void mv88e6xxx_mac_link_down(struct dsa_switch *ds, int port,
+static void mv88e6xxx_mac_link_down(struct phylink_config *config,
 				    unsigned int mode,
 				    phy_interface_t interface)
 {
-	struct mv88e6xxx_chip *chip = ds->priv;
+	struct dsa_port *dp = dsa_phylink_to_port(config);
+	struct mv88e6xxx_chip *chip = dp->ds->priv;
 	const struct mv88e6xxx_ops *ops;
+	int port = dp->index;
 	int err = 0;
 
 	ops = chip->info->ops;
@@ -962,7 +975,7 @@ static void mv88e6xxx_mac_link_down(struct dsa_switch *ds, int port,
 	 * 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)
@@ -975,14 +988,16 @@ static void mv88e6xxx_mac_link_down(struct dsa_switch *ds, int port,
 			"p%d: failed to force MAC link down\n", port);
 }
 
-static void mv88e6xxx_mac_link_up(struct dsa_switch *ds, int port,
-				  unsigned int mode, phy_interface_t interface,
+static void mv88e6xxx_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 mv88e6xxx_chip *chip = ds->priv;
+	struct dsa_port *dp = dsa_phylink_to_port(config);
+	struct mv88e6xxx_chip *chip = dp->ds->priv;
 	const struct mv88e6xxx_ops *ops;
+	int port = dp->index;
 	int err = 0;
 
 	ops = chip->info->ops;
@@ -993,7 +1008,7 @@ static void mv88e6xxx_mac_link_up(struct dsa_switch *ds, int port,
 	 * 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);
@@ -1008,10 +1023,53 @@ static void mv88e6xxx_mac_link_up(struct dsa_switch *ds, int port,
 	mv88e6xxx_reg_unlock(chip);
 
 	if (err && err != -EOPNOTSUPP)
-		dev_err(ds->dev,
+		dev_err(chip->dev,
 			"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;
@@ -4558,6 +4616,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,
@@ -4658,6 +4717,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,
@@ -4925,6 +4985,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,
@@ -5335,6 +5396,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,
@@ -5518,6 +5580,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,
@@ -6993,6 +7056,17 @@ static int mv88e6xxx_crosschip_lag_leave(struct dsa_switch *ds, int sw_index,
 	return err_sync ? : err_pvt;
 }
 
+static const struct phylink_mac_ops mv88e6xxx_phylink_mac_ops = {
+	.mac_select_pcs		= mv88e6xxx_mac_select_pcs,
+	.mac_prepare		= mv88e6xxx_mac_prepare,
+	.mac_config		= mv88e6xxx_mac_config,
+	.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 = {
 	.get_tag_protocol	= mv88e6xxx_get_tag_protocol,
 	.change_tag_protocol	= mv88e6xxx_change_tag_protocol,
@@ -7001,12 +7075,6 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = {
 	.port_setup		= mv88e6xxx_port_setup,
 	.port_teardown		= mv88e6xxx_port_teardown,
 	.phylink_get_caps	= mv88e6xxx_get_caps,
-	.phylink_mac_select_pcs	= mv88e6xxx_mac_select_pcs,
-	.phylink_mac_prepare	= mv88e6xxx_mac_prepare,
-	.phylink_mac_config	= mv88e6xxx_mac_config,
-	.phylink_mac_finish	= mv88e6xxx_mac_finish,
-	.phylink_mac_link_down	= mv88e6xxx_mac_link_down,
-	.phylink_mac_link_up	= mv88e6xxx_mac_link_up,
 	.get_strings		= mv88e6xxx_get_strings,
 	.get_ethtool_stats	= mv88e6xxx_get_ethtool_stats,
 	.get_eth_mac_stats	= mv88e6xxx_get_eth_mac_stats,
@@ -7075,6 +7143,7 @@ static int mv88e6xxx_register_switch(struct mv88e6xxx_chip *chip)
 	ds->priv = chip;
 	ds->dev = dev;
 	ds->ops = &mv88e6xxx_switch_ops;
+	ds->phylink_mac_ops = &mv88e6xxx_phylink_mac_ops;
 	ds->ageing_time_min = chip->info->age_time_coeff;
 	ds->ageing_time_max = chip->info->age_time_coeff * U8_MAX;
 
diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h
index 85eb293381a7..549592a9764d 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.h
+++ b/drivers/net/dsa/mv88e6xxx/chip.h
@@ -506,6 +506,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..ef9bb3974f33 100644
--- a/drivers/net/dsa/mv88e6xxx/port.c
+++ b/drivers/net/dsa/mv88e6xxx/port.c
@@ -203,7 +203,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;
@@ -520,6 +520,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 +659,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 +677,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/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c
index 61e95487732d..3aa66bf9eafc 100644
--- a/drivers/net/dsa/ocelot/felix.c
+++ b/drivers/net/dsa/ocelot/felix.c
@@ -1050,24 +1050,32 @@ static void felix_phylink_get_caps(struct dsa_switch *ds, int port,
 		  config->supported_interfaces);
 }
 
-static void felix_phylink_mac_config(struct dsa_switch *ds, int port,
+static void felix_phylink_mac_config(struct phylink_config *config,
 				     unsigned int mode,
 				     const struct phylink_link_state *state)
 {
-	struct ocelot *ocelot = ds->priv;
-	struct felix *felix = ocelot_to_felix(ocelot);
+	struct dsa_port *dp = dsa_phylink_to_port(config);
+	struct ocelot *ocelot = dp->ds->priv;
+	int port = dp->index;
+	struct felix *felix;
+
+	felix = ocelot_to_felix(ocelot);
 
 	if (felix->info->phylink_mac_config)
 		felix->info->phylink_mac_config(ocelot, port, mode, state);
 }
 
-static struct phylink_pcs *felix_phylink_mac_select_pcs(struct dsa_switch *ds,
-							int port,
-							phy_interface_t iface)
+static struct phylink_pcs *
+felix_phylink_mac_select_pcs(struct phylink_config *config,
+			     phy_interface_t iface)
 {
-	struct ocelot *ocelot = ds->priv;
-	struct felix *felix = ocelot_to_felix(ocelot);
+	struct dsa_port *dp = dsa_phylink_to_port(config);
+	struct ocelot *ocelot = dp->ds->priv;
 	struct phylink_pcs *pcs = NULL;
+	int port = dp->index;
+	struct felix *felix;
+
+	felix = ocelot_to_felix(ocelot);
 
 	if (felix->pcs && felix->pcs[port])
 		pcs = felix->pcs[port];
@@ -1075,11 +1083,13 @@ static struct phylink_pcs *felix_phylink_mac_select_pcs(struct dsa_switch *ds,
 	return pcs;
 }
 
-static void felix_phylink_mac_link_down(struct dsa_switch *ds, int port,
+static void felix_phylink_mac_link_down(struct phylink_config *config,
 					unsigned int link_an_mode,
 					phy_interface_t interface)
 {
-	struct ocelot *ocelot = ds->priv;
+	struct dsa_port *dp = dsa_phylink_to_port(config);
+	struct ocelot *ocelot = dp->ds->priv;
+	int port = dp->index;
 	struct felix *felix;
 
 	felix = ocelot_to_felix(ocelot);
@@ -1088,15 +1098,19 @@ static void felix_phylink_mac_link_down(struct dsa_switch *ds, int port,
 				     felix->info->quirks);
 }
 
-static void felix_phylink_mac_link_up(struct dsa_switch *ds, int port,
+static void felix_phylink_mac_link_up(struct phylink_config *config,
+				      struct phy_device *phydev,
 				      unsigned int link_an_mode,
 				      phy_interface_t interface,
-				      struct phy_device *phydev,
 				      int speed, int duplex,
 				      bool tx_pause, bool rx_pause)
 {
-	struct ocelot *ocelot = ds->priv;
-	struct felix *felix = ocelot_to_felix(ocelot);
+	struct dsa_port *dp = dsa_phylink_to_port(config);
+	struct ocelot *ocelot = dp->ds->priv;
+	int port = dp->index;
+	struct felix *felix;
+
+	felix = ocelot_to_felix(ocelot);
 
 	ocelot_phylink_mac_link_up(ocelot, port, phydev, link_an_mode,
 				   interface, speed, duplex, tx_pause, rx_pause,
@@ -2083,6 +2097,14 @@ static void felix_get_mm_stats(struct dsa_switch *ds, int port,
 	ocelot_port_get_mm_stats(ocelot, port, stats);
 }
 
+const struct phylink_mac_ops felix_phylink_mac_ops = {
+	.mac_select_pcs		= felix_phylink_mac_select_pcs,
+	.mac_config		= felix_phylink_mac_config,
+	.mac_link_down		= felix_phylink_mac_link_down,
+	.mac_link_up		= felix_phylink_mac_link_up,
+};
+EXPORT_SYMBOL_GPL(felix_phylink_mac_ops);
+
 const struct dsa_switch_ops felix_switch_ops = {
 	.get_tag_protocol		= felix_get_tag_protocol,
 	.change_tag_protocol		= felix_change_tag_protocol,
@@ -2104,10 +2126,6 @@ const struct dsa_switch_ops felix_switch_ops = {
 	.get_sset_count			= felix_get_sset_count,
 	.get_ts_info			= felix_get_ts_info,
 	.phylink_get_caps		= felix_phylink_get_caps,
-	.phylink_mac_config		= felix_phylink_mac_config,
-	.phylink_mac_select_pcs		= felix_phylink_mac_select_pcs,
-	.phylink_mac_link_down		= felix_phylink_mac_link_down,
-	.phylink_mac_link_up		= felix_phylink_mac_link_up,
 	.port_enable			= felix_port_enable,
 	.port_fast_age			= felix_port_fast_age,
 	.port_fdb_dump			= felix_fdb_dump,
diff --git a/drivers/net/dsa/ocelot/felix.h b/drivers/net/dsa/ocelot/felix.h
index dbf5872fe367..4d3489aaa659 100644
--- a/drivers/net/dsa/ocelot/felix.h
+++ b/drivers/net/dsa/ocelot/felix.h
@@ -82,6 +82,7 @@ struct felix_tag_proto_ops {
 			      struct netlink_ext_ack *extack);
 };
 
+extern const struct phylink_mac_ops felix_phylink_mac_ops;
 extern const struct dsa_switch_ops felix_switch_ops;
 
 /* DSA glue / front-end for struct ocelot */
diff --git a/drivers/net/dsa/ocelot/felix_vsc9959.c b/drivers/net/dsa/ocelot/felix_vsc9959.c
index 3c5509e75a54..2c639452c585 100644
--- a/drivers/net/dsa/ocelot/felix_vsc9959.c
+++ b/drivers/net/dsa/ocelot/felix_vsc9959.c
@@ -2714,6 +2714,7 @@ static int felix_pci_probe(struct pci_dev *pdev,
 	ds->num_ports = felix->info->num_ports;
 	ds->num_tx_queues = felix->info->num_tx_queues;
 	ds->ops = &felix_switch_ops;
+	ds->phylink_mac_ops = &felix_phylink_mac_ops;
 	ds->priv = ocelot;
 	felix->ds = ds;
 	felix->tag_proto = DSA_TAG_PROTO_OCELOT;
diff --git a/drivers/net/dsa/ocelot/ocelot_ext.c b/drivers/net/dsa/ocelot/ocelot_ext.c
index 22187d831c4b..a8927dc7aca4 100644
--- a/drivers/net/dsa/ocelot/ocelot_ext.c
+++ b/drivers/net/dsa/ocelot/ocelot_ext.c
@@ -96,6 +96,7 @@ static int ocelot_ext_probe(struct platform_device *pdev)
 	ds->num_tx_queues = felix->info->num_tx_queues;
 
 	ds->ops = &felix_switch_ops;
+	ds->phylink_mac_ops = &felix_phylink_mac_ops;
 	ds->priv = ocelot;
 	felix->ds = ds;
 	felix->tag_proto = DSA_TAG_PROTO_OCELOT;
diff --git a/drivers/net/dsa/ocelot/seville_vsc9953.c b/drivers/net/dsa/ocelot/seville_vsc9953.c
index 049930da0521..5ac8897e232b 100644
--- a/drivers/net/dsa/ocelot/seville_vsc9953.c
+++ b/drivers/net/dsa/ocelot/seville_vsc9953.c
@@ -1009,6 +1009,7 @@ static int seville_probe(struct platform_device *pdev)
 	ds->dev = &pdev->dev;
 	ds->num_ports = felix->info->num_ports;
 	ds->ops = &felix_switch_ops;
+	ds->phylink_mac_ops = &felix_phylink_mac_ops;
 	ds->priv = ocelot;
 	felix->ds = ds;
 	felix->tag_proto = DSA_TAG_PROTO_SEVILLE;
diff --git a/drivers/net/dsa/qca/ar9331.c b/drivers/net/dsa/qca/ar9331.c
index 8d9d271ac3af..968cb81088bf 100644
--- a/drivers/net/dsa/qca/ar9331.c
+++ b/drivers/net/dsa/qca/ar9331.c
@@ -523,28 +523,30 @@ static void ar9331_sw_phylink_get_caps(struct dsa_switch *ds, int port,
 	}
 }
 
-static void ar9331_sw_phylink_mac_config(struct dsa_switch *ds, int port,
+static void ar9331_sw_phylink_mac_config(struct phylink_config *config,
 					 unsigned int mode,
 					 const struct phylink_link_state *state)
 {
-	struct ar9331_sw_priv *priv = ds->priv;
+	struct dsa_port *dp = dsa_phylink_to_port(config);
+	struct ar9331_sw_priv *priv = dp->ds->priv;
 	struct regmap *regmap = priv->regmap;
 	int ret;
 
-	ret = regmap_update_bits(regmap, AR9331_SW_REG_PORT_STATUS(port),
+	ret = regmap_update_bits(regmap, AR9331_SW_REG_PORT_STATUS(dp->index),
 				 AR9331_SW_PORT_STATUS_LINK_EN |
 				 AR9331_SW_PORT_STATUS_FLOW_LINK_EN, 0);
 	if (ret)
 		dev_err_ratelimited(priv->dev, "%s: %i\n", __func__, ret);
 }
 
-static void ar9331_sw_phylink_mac_link_down(struct dsa_switch *ds, int port,
+static void ar9331_sw_phylink_mac_link_down(struct phylink_config *config,
 					    unsigned int mode,
 					    phy_interface_t interface)
 {
-	struct ar9331_sw_priv *priv = ds->priv;
-	struct ar9331_sw_port *p = &priv->port[port];
+	struct dsa_port *dp = dsa_phylink_to_port(config);
+	struct ar9331_sw_priv *priv = dp->ds->priv;
 	struct regmap *regmap = priv->regmap;
+	int port = dp->index;
 	int ret;
 
 	ret = regmap_update_bits(regmap, AR9331_SW_REG_PORT_STATUS(port),
@@ -552,23 +554,24 @@ static void ar9331_sw_phylink_mac_link_down(struct dsa_switch *ds, int port,
 	if (ret)
 		dev_err_ratelimited(priv->dev, "%s: %i\n", __func__, ret);
 
-	cancel_delayed_work_sync(&p->mib_read);
+	cancel_delayed_work_sync(&priv->port[port].mib_read);
 }
 
-static void ar9331_sw_phylink_mac_link_up(struct dsa_switch *ds, int port,
+static void ar9331_sw_phylink_mac_link_up(struct phylink_config *config,
+					  struct phy_device *phydev,
 					  unsigned int mode,
 					  phy_interface_t interface,
-					  struct phy_device *phydev,
 					  int speed, int duplex,
 					  bool tx_pause, bool rx_pause)
 {
-	struct ar9331_sw_priv *priv = ds->priv;
-	struct ar9331_sw_port *p = &priv->port[port];
+	struct dsa_port *dp = dsa_phylink_to_port(config);
+	struct ar9331_sw_priv *priv = dp->ds->priv;
 	struct regmap *regmap = priv->regmap;
+	int port = dp->index;
 	u32 val;
 	int ret;
 
-	schedule_delayed_work(&p->mib_read, 0);
+	schedule_delayed_work(&priv->port[port].mib_read, 0);
 
 	val = AR9331_SW_PORT_STATUS_MAC_MASK;
 	switch (speed) {
@@ -684,14 +687,17 @@ static void ar9331_get_pause_stats(struct dsa_switch *ds, int port,
 	spin_unlock(&p->stats_lock);
 }
 
+static const struct phylink_mac_ops ar9331_phylink_mac_ops = {
+	.mac_config	= ar9331_sw_phylink_mac_config,
+	.mac_link_down	= ar9331_sw_phylink_mac_link_down,
+	.mac_link_up	= ar9331_sw_phylink_mac_link_up,
+};
+
 static const struct dsa_switch_ops ar9331_sw_ops = {
 	.get_tag_protocol	= ar9331_sw_get_tag_protocol,
 	.setup			= ar9331_sw_setup,
 	.port_disable		= ar9331_sw_port_disable,
 	.phylink_get_caps	= ar9331_sw_phylink_get_caps,
-	.phylink_mac_config	= ar9331_sw_phylink_mac_config,
-	.phylink_mac_link_down	= ar9331_sw_phylink_mac_link_down,
-	.phylink_mac_link_up	= ar9331_sw_phylink_mac_link_up,
 	.get_stats64		= ar9331_get_stats64,
 	.get_pause_stats	= ar9331_get_pause_stats,
 };
@@ -1059,6 +1065,7 @@ static int ar9331_sw_probe(struct mdio_device *mdiodev)
 	ds->priv = priv;
 	priv->ops = ar9331_sw_ops;
 	ds->ops = &priv->ops;
+	ds->phylink_mac_ops = &ar9331_phylink_mac_ops;
 	dev_set_drvdata(&mdiodev->dev, priv);
 
 	for (i = 0; i < ARRAY_SIZE(priv->port); i++) {
diff --git a/drivers/net/dsa/qca/qca8k-8xxx.c b/drivers/net/dsa/qca/qca8k-8xxx.c
index dab66c0c6f64..b3c27cf538e8 100644
--- a/drivers/net/dsa/qca/qca8k-8xxx.c
+++ b/drivers/net/dsa/qca/qca8k-8xxx.c
@@ -1283,11 +1283,13 @@ qca8k_mac_config_setup_internal_delay(struct qca8k_priv *priv, int cpu_port_inde
 }
 
 static struct phylink_pcs *
-qca8k_phylink_mac_select_pcs(struct dsa_switch *ds, int port,
+qca8k_phylink_mac_select_pcs(struct phylink_config *config,
 			     phy_interface_t interface)
 {
-	struct qca8k_priv *priv = ds->priv;
+	struct dsa_port *dp = dsa_phylink_to_port(config);
+	struct qca8k_priv *priv = dp->ds->priv;
 	struct phylink_pcs *pcs = NULL;
+	int port = dp->index;
 
 	switch (interface) {
 	case PHY_INTERFACE_MODE_SGMII:
@@ -1311,13 +1313,18 @@ qca8k_phylink_mac_select_pcs(struct dsa_switch *ds, int port,
 }
 
 static void
-qca8k_phylink_mac_config(struct dsa_switch *ds, int port, unsigned int mode,
+qca8k_phylink_mac_config(struct phylink_config *config, unsigned int mode,
 			 const struct phylink_link_state *state)
 {
-	struct qca8k_priv *priv = ds->priv;
+	struct dsa_port *dp = dsa_phylink_to_port(config);
+	struct dsa_switch *ds = dp->ds;
+	struct qca8k_priv *priv;
+	int port = dp->index;
 	int cpu_port_index;
 	u32 reg;
 
+	priv = ds->priv;
+
 	switch (port) {
 	case 0: /* 1st CPU port */
 		if (state->interface != PHY_INTERFACE_MODE_RGMII &&
@@ -1426,20 +1433,24 @@ static void qca8k_phylink_get_caps(struct dsa_switch *ds, int port,
 }
 
 static void
-qca8k_phylink_mac_link_down(struct dsa_switch *ds, int port, unsigned int mode,
+qca8k_phylink_mac_link_down(struct phylink_config *config, unsigned int mode,
 			    phy_interface_t interface)
 {
-	struct qca8k_priv *priv = ds->priv;
+	struct dsa_port *dp = dsa_phylink_to_port(config);
+	struct qca8k_priv *priv = dp->ds->priv;
 
-	qca8k_port_set_status(priv, port, 0);
+	qca8k_port_set_status(priv, dp->index, 0);
 }
 
 static void
-qca8k_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)
+qca8k_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 qca8k_priv *priv = ds->priv;
+	struct dsa_port *dp = dsa_phylink_to_port(config);
+	struct qca8k_priv *priv = dp->ds->priv;
+	int port = dp->index;
 	u32 reg;
 
 	if (phylink_autoneg_inband(mode)) {
@@ -1463,10 +1474,10 @@ qca8k_phylink_mac_link_up(struct dsa_switch *ds, int port, unsigned int mode,
 		if (duplex == DUPLEX_FULL)
 			reg |= QCA8K_PORT_STATUS_DUPLEX;
 
-		if (rx_pause || dsa_is_cpu_port(ds, port))
+		if (rx_pause || dsa_port_is_cpu(dp))
 			reg |= QCA8K_PORT_STATUS_RXFLOW;
 
-		if (tx_pause || dsa_is_cpu_port(ds, port))
+		if (tx_pause || dsa_port_is_cpu(dp))
 			reg |= QCA8K_PORT_STATUS_TXFLOW;
 	}
 
@@ -1991,6 +2002,13 @@ qca8k_setup(struct dsa_switch *ds)
 	return 0;
 }
 
+static const struct phylink_mac_ops qca8k_phylink_mac_ops = {
+	.mac_select_pcs	= qca8k_phylink_mac_select_pcs,
+	.mac_config	= qca8k_phylink_mac_config,
+	.mac_link_down	= qca8k_phylink_mac_link_down,
+	.mac_link_up	= qca8k_phylink_mac_link_up,
+};
+
 static const struct dsa_switch_ops qca8k_switch_ops = {
 	.get_tag_protocol	= qca8k_get_tag_protocol,
 	.setup			= qca8k_setup,
@@ -2021,10 +2039,6 @@ static const struct dsa_switch_ops qca8k_switch_ops = {
 	.port_vlan_add		= qca8k_port_vlan_add,
 	.port_vlan_del		= qca8k_port_vlan_del,
 	.phylink_get_caps	= qca8k_phylink_get_caps,
-	.phylink_mac_select_pcs	= qca8k_phylink_mac_select_pcs,
-	.phylink_mac_config	= qca8k_phylink_mac_config,
-	.phylink_mac_link_down	= qca8k_phylink_mac_link_down,
-	.phylink_mac_link_up	= qca8k_phylink_mac_link_up,
 	.get_phy_flags		= qca8k_get_phy_flags,
 	.port_lag_join		= qca8k_port_lag_join,
 	.port_lag_leave		= qca8k_port_lag_leave,
@@ -2091,6 +2105,7 @@ qca8k_sw_probe(struct mdio_device *mdiodev)
 	priv->ds->num_ports = QCA8K_NUM_PORTS;
 	priv->ds->priv = priv;
 	priv->ds->ops = &qca8k_switch_ops;
+	priv->ds->phylink_mac_ops = &qca8k_phylink_mac_ops;
 	mutex_init(&priv->reg_mutex);
 	dev_set_drvdata(&mdiodev->dev, priv);
 
diff --git a/drivers/net/dsa/realtek/realtek.h b/drivers/net/dsa/realtek/realtek.h
index e0b1aa01337b..a1b2e0b529d5 100644
--- a/drivers/net/dsa/realtek/realtek.h
+++ b/drivers/net/dsa/realtek/realtek.h
@@ -17,6 +17,7 @@
 #define REALTEK_HW_STOP_DELAY		25	/* msecs */
 #define REALTEK_HW_START_DELAY		100	/* msecs */
 
+struct phylink_mac_ops;
 struct realtek_ops;
 struct dentry;
 struct inode;
@@ -117,6 +118,7 @@ struct realtek_ops {
 struct realtek_variant {
 	const struct dsa_switch_ops *ds_ops;
 	const struct realtek_ops *ops;
+	const struct phylink_mac_ops *phylink_mac_ops;
 	unsigned int clk_delay;
 	u8 cmd_read;
 	u8 cmd_write;
diff --git a/drivers/net/dsa/realtek/rtl8365mb.c b/drivers/net/dsa/realtek/rtl8365mb.c
index 12665a8a3412..b9674f68b756 100644
--- a/drivers/net/dsa/realtek/rtl8365mb.c
+++ b/drivers/net/dsa/realtek/rtl8365mb.c
@@ -1048,11 +1048,13 @@ static void rtl8365mb_phylink_get_caps(struct dsa_switch *ds, int port,
 		phy_interface_set_rgmii(config->supported_interfaces);
 }
 
-static void rtl8365mb_phylink_mac_config(struct dsa_switch *ds, int port,
+static void rtl8365mb_phylink_mac_config(struct phylink_config *config,
 					 unsigned int mode,
 					 const struct phylink_link_state *state)
 {
-	struct realtek_priv *priv = ds->priv;
+	struct dsa_port *dp = dsa_phylink_to_port(config);
+	struct realtek_priv *priv = dp->ds->priv;
+	u8 port = dp->index;
 	int ret;
 
 	if (mode != MLO_AN_PHY && mode != MLO_AN_FIXED) {
@@ -1076,13 +1078,15 @@ static void rtl8365mb_phylink_mac_config(struct dsa_switch *ds, int port,
 	 */
 }
 
-static void rtl8365mb_phylink_mac_link_down(struct dsa_switch *ds, int port,
+static void rtl8365mb_phylink_mac_link_down(struct phylink_config *config,
 					    unsigned int mode,
 					    phy_interface_t interface)
 {
-	struct realtek_priv *priv = ds->priv;
+	struct dsa_port *dp = dsa_phylink_to_port(config);
+	struct realtek_priv *priv = dp->ds->priv;
 	struct rtl8365mb_port *p;
 	struct rtl8365mb *mb;
+	u8 port = dp->index;
 	int ret;
 
 	mb = priv->chip_data;
@@ -1101,16 +1105,18 @@ static void rtl8365mb_phylink_mac_link_down(struct dsa_switch *ds, int port,
 	}
 }
 
-static void rtl8365mb_phylink_mac_link_up(struct dsa_switch *ds, int port,
+static void rtl8365mb_phylink_mac_link_up(struct phylink_config *config,
+					  struct phy_device *phydev,
 					  unsigned int mode,
 					  phy_interface_t interface,
-					  struct phy_device *phydev, int speed,
-					  int duplex, bool tx_pause,
+					  int speed, int duplex, bool tx_pause,
 					  bool rx_pause)
 {
-	struct realtek_priv *priv = ds->priv;
+	struct dsa_port *dp = dsa_phylink_to_port(config);
+	struct realtek_priv *priv = dp->ds->priv;
 	struct rtl8365mb_port *p;
 	struct rtl8365mb *mb;
+	u8 port = dp->index;
 	int ret;
 
 	mb = priv->chip_data;
@@ -2106,15 +2112,18 @@ static int rtl8365mb_detect(struct realtek_priv *priv)
 	return 0;
 }
 
+static const struct phylink_mac_ops rtl8365mb_phylink_mac_ops = {
+	.mac_config = rtl8365mb_phylink_mac_config,
+	.mac_link_down = rtl8365mb_phylink_mac_link_down,
+	.mac_link_up = rtl8365mb_phylink_mac_link_up,
+};
+
 static const struct dsa_switch_ops rtl8365mb_switch_ops = {
 	.get_tag_protocol = rtl8365mb_get_tag_protocol,
 	.change_tag_protocol = rtl8365mb_change_tag_protocol,
 	.setup = rtl8365mb_setup,
 	.teardown = rtl8365mb_teardown,
 	.phylink_get_caps = rtl8365mb_phylink_get_caps,
-	.phylink_mac_config = rtl8365mb_phylink_mac_config,
-	.phylink_mac_link_down = rtl8365mb_phylink_mac_link_down,
-	.phylink_mac_link_up = rtl8365mb_phylink_mac_link_up,
 	.port_stp_state_set = rtl8365mb_port_stp_state_set,
 	.get_strings = rtl8365mb_get_strings,
 	.get_ethtool_stats = rtl8365mb_get_ethtool_stats,
@@ -2136,6 +2145,7 @@ static const struct realtek_ops rtl8365mb_ops = {
 const struct realtek_variant rtl8365mb_variant = {
 	.ds_ops = &rtl8365mb_switch_ops,
 	.ops = &rtl8365mb_ops,
+	.phylink_mac_ops = &rtl8365mb_phylink_mac_ops,
 	.clk_delay = 10,
 	.cmd_read = 0xb9,
 	.cmd_write = 0xb8,
diff --git a/drivers/net/dsa/realtek/rtl8366rb.c b/drivers/net/dsa/realtek/rtl8366rb.c
index e10ae94cf771..6fb271c2e62d 100644
--- a/drivers/net/dsa/realtek/rtl8366rb.c
+++ b/drivers/net/dsa/realtek/rtl8366rb.c
@@ -1077,11 +1077,19 @@ static void rtl8366rb_phylink_get_caps(struct dsa_switch *ds, int port,
 }
 
 static void
-rtl8366rb_mac_link_up(struct dsa_switch *ds, int port, unsigned int mode,
-		      phy_interface_t interface, struct phy_device *phydev,
+rtl8366rb_mac_config(struct phylink_config *config, unsigned int mode,
+		     const struct phylink_link_state *state)
+{
+}
+
+static void
+rtl8366rb_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 realtek_priv *priv = ds->priv;
+	struct dsa_port *dp = dsa_phylink_to_port(config);
+	struct realtek_priv *priv = dp->ds->priv;
+	int port = dp->index;
 	unsigned int val;
 	int ret;
 
@@ -1147,10 +1155,12 @@ rtl8366rb_mac_link_up(struct dsa_switch *ds, int port, unsigned int mode,
 }
 
 static void
-rtl8366rb_mac_link_down(struct dsa_switch *ds, int port, unsigned int mode,
+rtl8366rb_mac_link_down(struct phylink_config *config, unsigned int mode,
 			phy_interface_t interface)
 {
-	struct realtek_priv *priv = ds->priv;
+	struct dsa_port *dp = dsa_phylink_to_port(config);
+	struct realtek_priv *priv = dp->ds->priv;
+	int port = dp->index;
 	int ret;
 
 	if (port != priv->cpu_port)
@@ -1849,12 +1859,16 @@ static int rtl8366rb_detect(struct realtek_priv *priv)
 	return 0;
 }
 
+static const struct phylink_mac_ops rtl8366rb_phylink_mac_ops = {
+	.mac_config = rtl8366rb_mac_config,
+	.mac_link_down = rtl8366rb_mac_link_down,
+	.mac_link_up = rtl8366rb_mac_link_up,
+};
+
 static const struct dsa_switch_ops rtl8366rb_switch_ops = {
 	.get_tag_protocol = rtl8366_get_tag_protocol,
 	.setup = rtl8366rb_setup,
 	.phylink_get_caps = rtl8366rb_phylink_get_caps,
-	.phylink_mac_link_up = rtl8366rb_mac_link_up,
-	.phylink_mac_link_down = rtl8366rb_mac_link_down,
 	.get_strings = rtl8366_get_strings,
 	.get_ethtool_stats = rtl8366_get_ethtool_stats,
 	.get_sset_count = rtl8366_get_sset_count,
@@ -1892,6 +1906,7 @@ static const struct realtek_ops rtl8366rb_ops = {
 const struct realtek_variant rtl8366rb_variant = {
 	.ds_ops = &rtl8366rb_switch_ops,
 	.ops = &rtl8366rb_ops,
+	.phylink_mac_ops = &rtl8366rb_phylink_mac_ops,
 	.clk_delay = 10,
 	.cmd_read = 0xa9,
 	.cmd_write = 0xa8,
diff --git a/drivers/net/dsa/realtek/rtl83xx.c b/drivers/net/dsa/realtek/rtl83xx.c
index d2e876805393..5f46deb8a21f 100644
--- a/drivers/net/dsa/realtek/rtl83xx.c
+++ b/drivers/net/dsa/realtek/rtl83xx.c
@@ -236,6 +236,7 @@ int rtl83xx_register_switch(struct realtek_priv *priv)
 	ds->priv = priv;
 	ds->dev = priv->dev;
 	ds->ops = priv->variant->ds_ops;
+	ds->phylink_mac_ops = priv->variant->phylink_mac_ops;
 	ds->num_ports = priv->num_ports;
 
 	ret = dsa_register_switch(ds);
diff --git a/drivers/net/dsa/rzn1_a5psw.c b/drivers/net/dsa/rzn1_a5psw.c
index 10092ea85e46..92e032972b34 100644
--- a/drivers/net/dsa/rzn1_a5psw.c
+++ b/drivers/net/dsa/rzn1_a5psw.c
@@ -239,23 +239,31 @@ static void a5psw_phylink_get_caps(struct dsa_switch *ds, int port,
 }
 
 static struct phylink_pcs *
-a5psw_phylink_mac_select_pcs(struct dsa_switch *ds, int port,
+a5psw_phylink_mac_select_pcs(struct phylink_config *config,
 			     phy_interface_t interface)
 {
-	struct dsa_port *dp = dsa_to_port(ds, port);
-	struct a5psw *a5psw = ds->priv;
+	struct dsa_port *dp = dsa_phylink_to_port(config);
+	struct a5psw *a5psw = dp->ds->priv;
 
-	if (!dsa_port_is_cpu(dp) && a5psw->pcs[port])
-		return a5psw->pcs[port];
+	if (dsa_port_is_cpu(dp))
+		return NULL;
 
-	return NULL;
+	return a5psw->pcs[dp->index];
+}
+
+static void a5psw_phylink_mac_config(struct phylink_config *config,
+				     unsigned int mode,
+				     const struct phylink_link_state *state)
+{
 }
 
-static void a5psw_phylink_mac_link_down(struct dsa_switch *ds, int port,
+static void a5psw_phylink_mac_link_down(struct phylink_config *config,
 					unsigned int mode,
 					phy_interface_t interface)
 {
-	struct a5psw *a5psw = ds->priv;
+	struct dsa_port *dp = dsa_phylink_to_port(config);
+	struct a5psw *a5psw = dp->ds->priv;
+	int port = dp->index;
 	u32 cmd_cfg;
 
 	cmd_cfg = a5psw_reg_readl(a5psw, A5PSW_CMD_CFG(port));
@@ -263,15 +271,17 @@ static void a5psw_phylink_mac_link_down(struct dsa_switch *ds, int port,
 	a5psw_reg_writel(a5psw, A5PSW_CMD_CFG(port), cmd_cfg);
 }
 
-static void a5psw_phylink_mac_link_up(struct dsa_switch *ds, int port,
+static void a5psw_phylink_mac_link_up(struct phylink_config *config,
+				      struct phy_device *phydev,
 				      unsigned int mode,
 				      phy_interface_t interface,
-				      struct phy_device *phydev, int speed,
-				      int duplex, bool tx_pause, bool rx_pause)
+				      int speed, int duplex, bool tx_pause,
+				      bool rx_pause)
 {
 	u32 cmd_cfg = A5PSW_CMD_CFG_RX_ENA | A5PSW_CMD_CFG_TX_ENA |
 		      A5PSW_CMD_CFG_TX_CRC_APPEND;
-	struct a5psw *a5psw = ds->priv;
+	struct dsa_port *dp = dsa_phylink_to_port(config);
+	struct a5psw *a5psw = dp->ds->priv;
 
 	if (speed == SPEED_1000)
 		cmd_cfg |= A5PSW_CMD_CFG_ETH_SPEED;
@@ -284,7 +294,7 @@ static void a5psw_phylink_mac_link_up(struct dsa_switch *ds, int port,
 	if (!rx_pause)
 		cmd_cfg &= ~A5PSW_CMD_CFG_PAUSE_IGNORE;
 
-	a5psw_reg_writel(a5psw, A5PSW_CMD_CFG(port), cmd_cfg);
+	a5psw_reg_writel(a5psw, A5PSW_CMD_CFG(dp->index), cmd_cfg);
 }
 
 static int a5psw_set_ageing_time(struct dsa_switch *ds, unsigned int msecs)
@@ -992,15 +1002,19 @@ static int a5psw_setup(struct dsa_switch *ds)
 	return 0;
 }
 
+static const struct phylink_mac_ops a5psw_phylink_mac_ops = {
+	.mac_select_pcs = a5psw_phylink_mac_select_pcs,
+	.mac_config = a5psw_phylink_mac_config,
+	.mac_link_down = a5psw_phylink_mac_link_down,
+	.mac_link_up = a5psw_phylink_mac_link_up,
+};
+
 static const struct dsa_switch_ops a5psw_switch_ops = {
 	.get_tag_protocol = a5psw_get_tag_protocol,
 	.setup = a5psw_setup,
 	.port_disable = a5psw_port_disable,
 	.port_enable = a5psw_port_enable,
 	.phylink_get_caps = a5psw_phylink_get_caps,
-	.phylink_mac_select_pcs = a5psw_phylink_mac_select_pcs,
-	.phylink_mac_link_down = a5psw_phylink_mac_link_down,
-	.phylink_mac_link_up = a5psw_phylink_mac_link_up,
 	.port_change_mtu = a5psw_port_change_mtu,
 	.port_max_mtu = a5psw_port_max_mtu,
 	.get_sset_count = a5psw_get_sset_count,
@@ -1252,6 +1266,7 @@ static int a5psw_probe(struct platform_device *pdev)
 	ds->dev = dev;
 	ds->num_ports = A5PSW_PORTS_NUM;
 	ds->ops = &a5psw_switch_ops;
+	ds->phylink_mac_ops = &a5psw_phylink_mac_ops;
 	ds->priv = a5psw;
 
 	ret = dsa_register_switch(ds);
diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c
index 6646f7fb0f90..9591dab7e991 100644
--- a/drivers/net/dsa/sja1105/sja1105_main.c
+++ b/drivers/net/dsa/sja1105/sja1105_main.c
@@ -1358,10 +1358,11 @@ static int sja1105_adjust_port_config(struct sja1105_private *priv, int port,
 }
 
 static struct phylink_pcs *
-sja1105_mac_select_pcs(struct dsa_switch *ds, int port, phy_interface_t iface)
+sja1105_mac_select_pcs(struct phylink_config *config, phy_interface_t iface)
 {
-	struct sja1105_private *priv = ds->priv;
-	struct dw_xpcs *xpcs = priv->xpcs[port];
+	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;
@@ -1369,21 +1370,31 @@ sja1105_mac_select_pcs(struct dsa_switch *ds, int port, phy_interface_t iface)
 	return NULL;
 }
 
-static void sja1105_mac_link_down(struct dsa_switch *ds, int port,
+static void sja1105_mac_config(struct phylink_config *config,
+			       unsigned int mode,
+			       const struct phylink_link_state *state)
+{
+}
+
+static void sja1105_mac_link_down(struct phylink_config *config,
 				  unsigned int mode,
 				  phy_interface_t interface)
 {
-	sja1105_inhibit_tx(ds->priv, BIT(port), true);
+	struct dsa_port *dp = dsa_phylink_to_port(config);
+
+	sja1105_inhibit_tx(dp->ds->priv, BIT(dp->index), true);
 }
 
-static void sja1105_mac_link_up(struct dsa_switch *ds, int port,
+static void sja1105_mac_link_up(struct phylink_config *config,
+				struct phy_device *phydev,
 				unsigned int mode,
 				phy_interface_t interface,
-				struct phy_device *phydev,
 				int speed, int duplex,
 				bool tx_pause, bool rx_pause)
 {
-	struct sja1105_private *priv = ds->priv;
+	struct dsa_port *dp = dsa_phylink_to_port(config);
+	struct sja1105_private *priv = dp->ds->priv;
+	int port = dp->index;
 
 	sja1105_adjust_port_config(priv, port, speed);
 
@@ -3198,6 +3209,13 @@ static void sja1105_teardown(struct dsa_switch *ds)
 	sja1105_static_config_free(&priv->static_config);
 }
 
+static const struct phylink_mac_ops sja1105_phylink_mac_ops = {
+	.mac_select_pcs	= sja1105_mac_select_pcs,
+	.mac_config	= sja1105_mac_config,
+	.mac_link_up	= sja1105_mac_link_up,
+	.mac_link_down	= sja1105_mac_link_down,
+};
+
 static const struct dsa_switch_ops sja1105_switch_ops = {
 	.get_tag_protocol	= sja1105_get_tag_protocol,
 	.connect_tag_protocol	= sja1105_connect_tag_protocol,
@@ -3207,9 +3225,6 @@ static const struct dsa_switch_ops sja1105_switch_ops = {
 	.port_change_mtu	= sja1105_change_mtu,
 	.port_max_mtu		= sja1105_get_max_mtu,
 	.phylink_get_caps	= sja1105_phylink_get_caps,
-	.phylink_mac_select_pcs	= sja1105_mac_select_pcs,
-	.phylink_mac_link_up	= sja1105_mac_link_up,
-	.phylink_mac_link_down	= sja1105_mac_link_down,
 	.get_strings		= sja1105_get_strings,
 	.get_ethtool_stats	= sja1105_get_ethtool_stats,
 	.get_sset_count		= sja1105_get_sset_count,
@@ -3375,6 +3390,7 @@ static int sja1105_probe(struct spi_device *spi)
 	ds->dev = dev;
 	ds->num_ports = priv->info->num_ports;
 	ds->ops = &sja1105_switch_ops;
+	ds->phylink_mac_ops = &sja1105_phylink_mac_ops;
 	ds->priv = priv;
 	priv->ds = ds;
 
diff --git a/drivers/net/dsa/xrs700x/xrs700x.c b/drivers/net/dsa/xrs700x/xrs700x.c
index 96db032b478f..de3b768f2ff9 100644
--- a/drivers/net/dsa/xrs700x/xrs700x.c
+++ b/drivers/net/dsa/xrs700x/xrs700x.c
@@ -466,13 +466,25 @@ static void xrs700x_phylink_get_caps(struct dsa_switch *ds, int port,
 	}
 }
 
-static void xrs700x_mac_link_up(struct dsa_switch *ds, int port,
-				unsigned int mode, phy_interface_t interface,
+static void xrs700x_mac_config(struct phylink_config *config, unsigned int mode,
+			       const struct phylink_link_state *state)
+{
+}
+
+static void xrs700x_mac_link_down(struct phylink_config *config,
+				  unsigned int mode, phy_interface_t interface)
+{
+}
+
+static void xrs700x_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 xrs700x *priv = ds->priv;
+	struct dsa_port *dp = dsa_phylink_to_port(config);
+	struct xrs700x *priv = dp->ds->priv;
+	int port = dp->index;
 	unsigned int val;
 
 	switch (speed) {
@@ -699,13 +711,18 @@ static int xrs700x_hsr_leave(struct dsa_switch *ds, int port,
 	return 0;
 }
 
+static const struct phylink_mac_ops xrs700x_phylink_mac_ops = {
+	.mac_config		= xrs700x_mac_config,
+	.mac_link_down		= xrs700x_mac_link_down,
+	.mac_link_up		= xrs700x_mac_link_up,
+};
+
 static const struct dsa_switch_ops xrs700x_ops = {
 	.get_tag_protocol	= xrs700x_get_tag_protocol,
 	.setup			= xrs700x_setup,
 	.teardown		= xrs700x_teardown,
 	.port_stp_state_set	= xrs700x_port_stp_state_set,
 	.phylink_get_caps	= xrs700x_phylink_get_caps,
-	.phylink_mac_link_up	= xrs700x_mac_link_up,
 	.get_strings		= xrs700x_get_strings,
 	.get_sset_count		= xrs700x_get_sset_count,
 	.get_ethtool_stats	= xrs700x_get_ethtool_stats,
@@ -763,6 +780,7 @@ struct xrs700x *xrs700x_switch_alloc(struct device *base, void *devpriv)
 	INIT_DELAYED_WORK(&priv->mib_work, xrs700x_mib_work);
 
 	ds->ops = &xrs700x_ops;
+	ds->phylink_mac_ops = &xrs700x_phylink_mac_ops;
 	ds->priv = priv;
 	priv->dev = base;
 
diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c
index 898debfd4db3..decfbf91e83d 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/freescale/fman/fman_memac.c b/drivers/net/ethernet/freescale/fman/fman_memac.c
index 758535adc9ff..7a6ce03d249d 100644
--- a/drivers/net/ethernet/freescale/fman/fman_memac.c
+++ b/drivers/net/ethernet/freescale/fman/fman_memac.c
@@ -1067,7 +1067,6 @@ int memac_initialization(struct mac_device *mac_dev,
 			 struct fman_mac_params *params)
 {
 	int			 err;
-	struct device_node      *fixed;
 	struct phylink_pcs	*pcs;
 	struct fman_mac		*memac;
 	unsigned long		 capabilities;
@@ -1223,18 +1222,15 @@ int memac_initialization(struct mac_device *mac_dev,
 		memac->rgmii_no_half_duplex = true;
 
 	/* Most boards should use MLO_AN_INBAND, but existing boards don't have
-	 * a managed property. Default to MLO_AN_INBAND if nothing else is
-	 * specified. We need to be careful and not enable this if we have a
-	 * fixed link or if we are using MII or RGMII, since those
-	 * configurations modes don't use in-band autonegotiation.
+	 * a managed property. Default to MLO_AN_INBAND rather than MLO_AN_PHY.
+	 * Phylink will allow this to be overriden by a fixed link. We need to
+	 * be careful and not enable this if we are using MII or RGMII, since
+	 * those configurations modes don't use in-band autonegotiation.
 	 */
-	fixed = of_get_child_by_name(mac_node, "fixed-link");
-	if (!fixed && !of_property_read_bool(mac_node, "fixed-link") &&
-	    !of_property_read_bool(mac_node, "managed") &&
+	if (!of_property_read_bool(mac_node, "managed") &&
 	    mac_dev->phy_if != PHY_INTERFACE_MODE_MII &&
 	    !phy_interface_mode_is_rgmii(mac_dev->phy_if))
-		mac_dev->phylink_config.ovr_an_inband = true;
-	of_node_put(fixed);
+		mac_dev->phylink_config.default_an_inband = true;
 
 	err = memac_init(mac_dev->fman_mac);
 	if (err < 0)
diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c
index 40a5f1431e4e..1375b9aeec39 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];
@@ -3259,7 +3257,8 @@ static void mvneta_link_change(struct mvneta_port *pp)
 {
 	u32 gmac_stat = mvreg_read(pp, MVNETA_GMAC_STATUS);
 
-	phylink_mac_change(pp->phylink, !!(gmac_stat & MVNETA_GMAC_LINK_UP));
+	phylink_pcs_change(&pp->phylink_pcs,
+			   !!(gmac_stat & MVNETA_GMAC_LINK_UP));
 }
 
 /* NAPI handler
@@ -3959,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,
@@ -4070,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,
@@ -4205,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)
 {
@@ -4232,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,
@@ -4283,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 = {
@@ -4297,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)
@@ -5100,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);
 }
@@ -5116,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);
 }
@@ -5537,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,
@@ -5564,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 23adf53c2aa1..82c49095c045 100644
--- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
+++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
@@ -3434,12 +3434,13 @@ static void mvpp2_isr_handle_ptp(struct mvpp2_port *port)
 		mvpp2_isr_handle_ptp_queue(port, 1);
 }
 
-static void mvpp2_isr_handle_link(struct mvpp2_port *port, bool link)
+static void mvpp2_isr_handle_link(struct mvpp2_port *port,
+				  struct phylink_pcs *pcs, bool link)
 {
 	struct net_device *dev = port->dev;
 
 	if (port->phylink) {
-		phylink_mac_change(port->phylink, link);
+		phylink_pcs_change(pcs, link);
 		return;
 	}
 
@@ -3472,7 +3473,7 @@ static void mvpp2_isr_handle_xlg(struct mvpp2_port *port)
 	if (val & MVPP22_XLG_INT_STAT_LINK) {
 		val = readl(port->base + MVPP22_XLG_STATUS);
 		link = (val & MVPP22_XLG_STATUS_LINK_UP);
-		mvpp2_isr_handle_link(port, link);
+		mvpp2_isr_handle_link(port, &port->pcs_xlg, link);
 	}
 }
 
@@ -3488,7 +3489,7 @@ static void mvpp2_isr_handle_gmac_internal(struct mvpp2_port *port)
 		if (val & MVPP22_GMAC_INT_STAT_LINK) {
 			val = readl(port->base + MVPP2_GMAC_STATUS0);
 			link = (val & MVPP2_GMAC_STATUS0_LINK_UP);
-			mvpp2_isr_handle_link(port, link);
+			mvpp2_isr_handle_link(port, &port->pcs_gmac, link);
 		}
 	}
 }
@@ -5726,6 +5727,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_eee *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_eee *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 = {
@@ -5768,6 +5791,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
@@ -6190,19 +6215,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,
@@ -6309,7 +6341,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,
@@ -6631,6 +6663,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,
@@ -6638,6 +6721,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 */
@@ -6914,6 +6999,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 |=
@@ -6982,6 +7070,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/marvell/prestera/prestera_main.c b/drivers/net/ethernet/marvell/prestera/prestera_main.c
index 4fb886c57cd7..ba6d53ac7f55 100644
--- a/drivers/net/ethernet/marvell/prestera/prestera_main.c
+++ b/drivers/net/ethernet/marvell/prestera/prestera_main.c
@@ -821,7 +821,7 @@ static void prestera_port_handle_event(struct prestera_switch *sw,
 
 		if (port->state_mac.oper) {
 			if (port->phy_link)
-				phylink_mac_change(port->phy_link, true);
+				phylink_pcs_change(&port->phylink_pcs, true);
 			else
 				netif_carrier_on(port->dev);
 
@@ -829,7 +829,7 @@ static void prestera_port_handle_event(struct prestera_switch *sw,
 				queue_delayed_work(prestera_wq, caching_dw, 0);
 		} else {
 			if (port->phy_link)
-				phylink_mac_change(port->phy_link, false);
+				phylink_pcs_change(&port->phylink_pcs, false);
 			else if (netif_running(port->dev) && netif_carrier_ok(port->dev))
 				netif_carrier_off(port->dev);
 
diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h
index 3b7d4ac1e7be..cb0154a8b039 100644
--- a/drivers/net/ethernet/stmicro/stmmac/common.h
+++ b/drivers/net/ethernet/stmicro/stmmac/common.h
@@ -271,8 +271,6 @@ struct stmmac_safety_stats {
 /* PCS defines */
 #define STMMAC_PCS_RGMII	(1 << 0)
 #define STMMAC_PCS_SGMII	(1 << 1)
-#define STMMAC_PCS_TBI		(1 << 2)
-#define STMMAC_PCS_RTBI		(1 << 3)
 
 #define SF_DMA_MODE 1		/* DMA STORE-AND-FORWARD Operation Mode */
 
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c
index 60283543ffc8..56649edb18cd 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c
@@ -248,7 +248,7 @@ static void intel_speed_mode_2500(struct net_device *ndev, void *intel_data)
 		dev_info(priv->device, "Link Speed Mode: 2.5Gbps\n");
 		priv->plat->max_speed = 2500;
 		priv->plat->phy_interface = PHY_INTERFACE_MODE_2500BASEX;
-		priv->plat->mdio_bus_data->xpcs_an_inband = false;
+		priv->plat->mdio_bus_data->default_an_inband = false;
 	} else {
 		priv->plat->max_speed = 1000;
 	}
@@ -586,18 +586,7 @@ static int intel_mgbe_common_data(struct pci_dev *pdev,
 	if (plat->phy_interface == PHY_INTERFACE_MODE_SGMII ||
 	    plat->phy_interface == PHY_INTERFACE_MODE_1000BASEX) {
 		plat->mdio_bus_data->has_xpcs = true;
-		plat->mdio_bus_data->xpcs_an_inband = true;
-	}
-
-	/* For fixed-link setup, we clear xpcs_an_inband */
-	if (fwnode) {
-		struct fwnode_handle *fixed_node;
-
-		fixed_node = fwnode_get_named_child_node(fwnode, "fixed-link");
-		if (fixed_node)
-			plat->mdio_bus_data->xpcs_an_inband = false;
-
-		fwnode_handle_put(fixed_node);
+		plat->mdio_bus_data->default_an_inband = true;
 	}
 
 	/* Ensure mdio bus scan skips intel serdes and pcs-xpcs */
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c
index e254b21fdb59..d9eed415b0b7 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c
@@ -605,6 +605,14 @@ static int ethqos_configure_rgmii(struct qcom_ethqos *ethqos)
 	return 0;
 }
 
+static void ethqos_set_serdes_speed(struct qcom_ethqos *ethqos, int speed)
+{
+	if (ethqos->serdes_speed != speed) {
+		phy_set_speed(ethqos->serdes_phy, speed);
+		ethqos->serdes_speed = speed;
+	}
+}
+
 /* On interface toggle MAC registers gets reset.
  * Configure MAC block for SGMII on ethernet phy link up
  */
@@ -622,9 +630,7 @@ static int ethqos_configure_sgmii(struct qcom_ethqos *ethqos)
 		rgmii_updatel(ethqos, RGMII_CONFIG2_RGMII_CLK_SEL_CFG,
 			      RGMII_CONFIG2_RGMII_CLK_SEL_CFG,
 			      RGMII_IO_MACRO_CONFIG2);
-		if (ethqos->serdes_speed != SPEED_2500)
-			phy_set_speed(ethqos->serdes_phy, SPEED_2500);
-		ethqos->serdes_speed = SPEED_2500;
+		ethqos_set_serdes_speed(ethqos, SPEED_2500);
 		stmmac_pcs_ctrl_ane(priv, priv->ioaddr, 0, 0, 0);
 		break;
 	case SPEED_1000:
@@ -632,16 +638,12 @@ static int ethqos_configure_sgmii(struct qcom_ethqos *ethqos)
 		rgmii_updatel(ethqos, RGMII_CONFIG2_RGMII_CLK_SEL_CFG,
 			      RGMII_CONFIG2_RGMII_CLK_SEL_CFG,
 			      RGMII_IO_MACRO_CONFIG2);
-		if (ethqos->serdes_speed != SPEED_1000)
-			phy_set_speed(ethqos->serdes_phy, SPEED_1000);
-		ethqos->serdes_speed = SPEED_1000;
+		ethqos_set_serdes_speed(ethqos, SPEED_1000);
 		stmmac_pcs_ctrl_ane(priv, priv->ioaddr, 1, 0, 0);
 		break;
 	case SPEED_100:
 		val |= ETHQOS_MAC_CTRL_PORT_SEL | ETHQOS_MAC_CTRL_SPEED_MODE;
-		if (ethqos->serdes_speed != SPEED_1000)
-			phy_set_speed(ethqos->serdes_phy, SPEED_1000);
-		ethqos->serdes_speed = SPEED_1000;
+		ethqos_set_serdes_speed(ethqos, SPEED_1000);
 		stmmac_pcs_ctrl_ane(priv, priv->ioaddr, 1, 0, 0);
 		break;
 	case SPEED_10:
@@ -651,9 +653,7 @@ static int ethqos_configure_sgmii(struct qcom_ethqos *ethqos)
 			      FIELD_PREP(RGMII_CONFIG_SGMII_CLK_DVDR,
 					 SGMII_10M_RX_CLK_DVDR),
 			      RGMII_IO_MACRO_CONFIG);
-		if (ethqos->serdes_speed != SPEED_1000)
-			phy_set_speed(ethqos->serdes_phy, ethqos->speed);
-		ethqos->serdes_speed = SPEED_1000;
+		ethqos_set_serdes_speed(ethqos, SPEED_1000);
 		stmmac_pcs_ctrl_ane(priv, priv->ioaddr, 1, 0, 0);
 		break;
 	}
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c
index 8555299443f4..d413d76a8936 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c
@@ -15,7 +15,7 @@
 #include <linux/crc32.h>
 #include <linux/slab.h>
 #include <linux/ethtool.h>
-#include <asm/io.h>
+#include <linux/io.h>
 #include "stmmac.h"
 #include "stmmac_pcs.h"
 #include "dwmac1000.h"
@@ -404,11 +404,6 @@ static void dwmac1000_ctrl_ane(void __iomem *ioaddr, bool ane, bool srgmi_ral,
 	dwmac_ctrl_ane(ioaddr, GMAC_PCS_BASE, ane, srgmi_ral, loopback);
 }
 
-static void dwmac1000_rane(void __iomem *ioaddr, bool restart)
-{
-	dwmac_rane(ioaddr, GMAC_PCS_BASE, restart);
-}
-
 static void dwmac1000_get_adv_lp(void __iomem *ioaddr, struct rgmii_adv *adv)
 {
 	dwmac_get_adv_lp(ioaddr, GMAC_PCS_BASE, adv);
@@ -519,7 +514,6 @@ const struct stmmac_ops dwmac1000_ops = {
 	.set_eee_pls = dwmac1000_set_eee_pls,
 	.debug = dwmac1000_debug,
 	.pcs_ctrl_ane = dwmac1000_ctrl_ane,
-	.pcs_rane = dwmac1000_rane,
 	.pcs_get_adv_lp = dwmac1000_get_adv_lp,
 	.set_mac_loopback = dwmac1000_set_mac_loopback,
 };
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c
index a38226d7cc6a..dbd9f93b2460 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c
@@ -68,7 +68,7 @@ static void dwmac4_core_init(struct mac_device_info *hw,
 		init_waitqueue_head(&priv->tstamp_busy_wait);
 }
 
-static void dwmac4_phylink_get_caps(struct stmmac_priv *priv)
+static void dwmac4_update_caps(struct stmmac_priv *priv)
 {
 	if (priv->plat->tx_queues_to_use > 1)
 		priv->hw->link.caps &= ~(MAC_10HD | MAC_100HD | MAC_1000HD);
@@ -758,11 +758,6 @@ static void dwmac4_ctrl_ane(void __iomem *ioaddr, bool ane, bool srgmi_ral,
 	dwmac_ctrl_ane(ioaddr, GMAC_PCS_BASE, ane, srgmi_ral, loopback);
 }
 
-static void dwmac4_rane(void __iomem *ioaddr, bool restart)
-{
-	dwmac_rane(ioaddr, GMAC_PCS_BASE, restart);
-}
-
 static void dwmac4_get_adv_lp(void __iomem *ioaddr, struct rgmii_adv *adv)
 {
 	dwmac_get_adv_lp(ioaddr, GMAC_PCS_BASE, adv);
@@ -1190,7 +1185,7 @@ static void dwmac4_set_hw_vlan_mode(struct mac_device_info *hw)
 
 const struct stmmac_ops dwmac4_ops = {
 	.core_init = dwmac4_core_init,
-	.phylink_get_caps = dwmac4_phylink_get_caps,
+	.update_caps = dwmac4_update_caps,
 	.set_mac = stmmac_set_mac,
 	.rx_ipc = dwmac4_rx_ipc_enable,
 	.rx_queue_enable = dwmac4_rx_queue_enable,
@@ -1215,7 +1210,6 @@ const struct stmmac_ops dwmac4_ops = {
 	.set_eee_timer = dwmac4_set_eee_timer,
 	.set_eee_pls = dwmac4_set_eee_pls,
 	.pcs_ctrl_ane = dwmac4_ctrl_ane,
-	.pcs_rane = dwmac4_rane,
 	.pcs_get_adv_lp = dwmac4_get_adv_lp,
 	.debug = dwmac4_debug,
 	.set_filter = dwmac4_set_filter,
@@ -1235,7 +1229,7 @@ const struct stmmac_ops dwmac4_ops = {
 
 const struct stmmac_ops dwmac410_ops = {
 	.core_init = dwmac4_core_init,
-	.phylink_get_caps = dwmac4_phylink_get_caps,
+	.update_caps = dwmac4_update_caps,
 	.set_mac = stmmac_dwmac4_set_mac,
 	.rx_ipc = dwmac4_rx_ipc_enable,
 	.rx_queue_enable = dwmac4_rx_queue_enable,
@@ -1260,7 +1254,6 @@ const struct stmmac_ops dwmac410_ops = {
 	.set_eee_timer = dwmac4_set_eee_timer,
 	.set_eee_pls = dwmac4_set_eee_pls,
 	.pcs_ctrl_ane = dwmac4_ctrl_ane,
-	.pcs_rane = dwmac4_rane,
 	.pcs_get_adv_lp = dwmac4_get_adv_lp,
 	.debug = dwmac4_debug,
 	.set_filter = dwmac4_set_filter,
@@ -1284,7 +1277,7 @@ const struct stmmac_ops dwmac410_ops = {
 
 const struct stmmac_ops dwmac510_ops = {
 	.core_init = dwmac4_core_init,
-	.phylink_get_caps = dwmac4_phylink_get_caps,
+	.update_caps = dwmac4_update_caps,
 	.set_mac = stmmac_dwmac4_set_mac,
 	.rx_ipc = dwmac4_rx_ipc_enable,
 	.rx_queue_enable = dwmac4_rx_queue_enable,
@@ -1309,7 +1302,6 @@ const struct stmmac_ops dwmac510_ops = {
 	.set_eee_timer = dwmac4_set_eee_timer,
 	.set_eee_pls = dwmac4_set_eee_pls,
 	.pcs_ctrl_ane = dwmac4_ctrl_ane,
-	.pcs_rane = dwmac4_rane,
 	.pcs_get_adv_lp = dwmac4_get_adv_lp,
 	.debug = dwmac4_debug,
 	.set_filter = dwmac4_set_filter,
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c
index f8e7775bb633..6a987cf598e4 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c
@@ -1554,9 +1554,6 @@ const struct stmmac_ops dwxgmac210_ops = {
 	.reset_eee_mode = dwxgmac2_reset_eee_mode,
 	.set_eee_timer = dwxgmac2_set_eee_timer,
 	.set_eee_pls = dwxgmac2_set_eee_pls,
-	.pcs_ctrl_ane = NULL,
-	.pcs_rane = NULL,
-	.pcs_get_adv_lp = NULL,
 	.debug = NULL,
 	.set_filter = dwxgmac2_set_filter,
 	.safety_feat_config = dwxgmac3_safety_feat_config,
@@ -1614,9 +1611,6 @@ const struct stmmac_ops dwxlgmac2_ops = {
 	.reset_eee_mode = dwxgmac2_reset_eee_mode,
 	.set_eee_timer = dwxgmac2_set_eee_timer,
 	.set_eee_pls = dwxgmac2_set_eee_pls,
-	.pcs_ctrl_ane = NULL,
-	.pcs_rane = NULL,
-	.pcs_get_adv_lp = NULL,
 	.debug = NULL,
 	.set_filter = dwxgmac2_set_filter,
 	.safety_feat_config = dwxgmac3_safety_feat_config,
diff --git a/drivers/net/ethernet/stmicro/stmmac/hwif.h b/drivers/net/ethernet/stmicro/stmmac/hwif.h
index 7be04b54738b..97934ccba5b1 100644
--- a/drivers/net/ethernet/stmicro/stmmac/hwif.h
+++ b/drivers/net/ethernet/stmicro/stmmac/hwif.h
@@ -308,8 +308,8 @@ struct stmmac_est;
 struct stmmac_ops {
 	/* MAC core initialization */
 	void (*core_init)(struct mac_device_info *hw, struct net_device *dev);
-	/* Get phylink capabilities */
-	void (*phylink_get_caps)(struct stmmac_priv *priv);
+	/* Update MAC capabilities */
+	void (*update_caps)(struct stmmac_priv *priv);
 	/* Enable the MAC RX/TX */
 	void (*set_mac)(void __iomem *ioaddr, bool enable);
 	/* Enable and verify that the IPC module is supported */
@@ -370,7 +370,6 @@ struct stmmac_ops {
 	/* PCS calls */
 	void (*pcs_ctrl_ane)(void __iomem *ioaddr, bool ane, bool srgmi_ral,
 			     bool loopback);
-	void (*pcs_rane)(void __iomem *ioaddr, bool restart);
 	void (*pcs_get_adv_lp)(void __iomem *ioaddr, struct rgmii_adv *adv);
 	/* Safety Features */
 	int (*safety_feat_config)(void __iomem *ioaddr, unsigned int asp,
@@ -430,8 +429,8 @@ struct stmmac_ops {
 
 #define stmmac_core_init(__priv, __args...) \
 	stmmac_do_void_callback(__priv, mac, core_init, __args)
-#define stmmac_mac_phylink_get_caps(__priv) \
-	stmmac_do_void_callback(__priv, mac, phylink_get_caps, __priv)
+#define stmmac_mac_update_caps(__priv) \
+	stmmac_do_void_callback(__priv, mac, update_caps, __priv)
 #define stmmac_mac_set(__priv, __args...) \
 	stmmac_do_void_callback(__priv, mac, set_mac, __args)
 #define stmmac_rx_ipc(__priv, __args...) \
@@ -484,8 +483,6 @@ struct stmmac_ops {
 	stmmac_do_void_callback(__priv, mac, debug, __priv, __args)
 #define stmmac_pcs_ctrl_ane(__priv, __args...) \
 	stmmac_do_void_callback(__priv, mac, pcs_ctrl_ane, __args)
-#define stmmac_pcs_rane(__priv, __args...) \
-	stmmac_do_void_callback(__priv, mac, pcs_rane, __priv, __args)
 #define stmmac_pcs_get_adv_lp(__priv, __args...) \
 	stmmac_do_void_callback(__priv, mac, pcs_get_adv_lp, __args)
 #define stmmac_safety_feat_config(__priv, __args...) \
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index 7c6fb14b5555..f4566b45dc9f 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -471,13 +471,6 @@ bool stmmac_eee_init(struct stmmac_priv *priv)
 {
 	int eee_tw_timer = priv->eee_tw_timer;
 
-	/* Using PCS we cannot dial with the phy registers at this stage
-	 * so we do not support extra feature like EEE.
-	 */
-	if (priv->hw->pcs == STMMAC_PCS_TBI ||
-	    priv->hw->pcs == STMMAC_PCS_RTBI)
-		return false;
-
 	/* Check if MAC core supports the EEE feature. */
 	if (!priv->dma_cap.eee)
 		return false;
@@ -1212,8 +1205,8 @@ static int stmmac_phy_setup(struct stmmac_priv *priv)
 
 	mdio_bus_data = priv->plat->mdio_bus_data;
 	if (mdio_bus_data)
-		priv->phylink_config.ovr_an_inband =
-			mdio_bus_data->xpcs_an_inband;
+		priv->phylink_config.default_an_inband =
+			mdio_bus_data->default_an_inband;
 
 	/* Set the platform/firmware specified interface mode. Note, phylink
 	 * deals with the PHY interface mode, not the MAC interface mode.
@@ -1225,8 +1218,8 @@ static int stmmac_phy_setup(struct stmmac_priv *priv)
 		xpcs_get_interfaces(priv->hw->xpcs,
 				    priv->phylink_config.supported_interfaces);
 
-	/* Get the MAC specific capabilities */
-	stmmac_mac_phylink_get_caps(priv);
+	/* Refresh the MAC-specific capabilities */
+	stmmac_mac_update_caps(priv);
 
 	priv->phylink_config.mac_capabilities = priv->hw->link.caps;
 
@@ -3942,9 +3935,7 @@ static int __stmmac_open(struct net_device *dev,
 	if (ret < 0)
 		return ret;
 
-	if (priv->hw->pcs != STMMAC_PCS_TBI &&
-	    priv->hw->pcs != STMMAC_PCS_RTBI &&
-	    (!priv->hw->xpcs ||
+	if ((!priv->hw->xpcs ||
 	     xpcs_get_an_mode(priv->hw->xpcs, mode) != DW_AN_C73) &&
 	    !priv->hw->lynx_pcs) {
 		ret = stmmac_init_phy(dev);
@@ -4087,8 +4078,6 @@ static int stmmac_release(struct net_device *dev)
 	if (priv->plat->serdes_powerdown)
 		priv->plat->serdes_powerdown(dev, priv->plat->bsp_priv);
 
-	netif_carrier_off(dev);
-
 	stmmac_release_ptp(priv);
 
 	pm_runtime_put(priv->device);
@@ -7341,7 +7330,7 @@ int stmmac_reinit_queues(struct net_device *dev, u32 rx_cnt, u32 tx_cnt)
 			priv->rss.table[i] = ethtool_rxfh_indir_default(i,
 									rx_cnt);
 
-	stmmac_mac_phylink_get_caps(priv);
+	stmmac_mac_update_caps(priv);
 
 	priv->phylink_config.mac_capabilities = priv->hw->link.caps;
 
@@ -7739,16 +7728,12 @@ int stmmac_dvr_probe(struct device *device,
 	if (!pm_runtime_enabled(device))
 		pm_runtime_enable(device);
 
-	if (priv->hw->pcs != STMMAC_PCS_TBI &&
-	    priv->hw->pcs != STMMAC_PCS_RTBI) {
-		/* MDIO bus Registration */
-		ret = stmmac_mdio_register(ndev);
-		if (ret < 0) {
-			dev_err_probe(priv->device, ret,
-				      "%s: MDIO bus (id: %d) registration failed\n",
-				      __func__, priv->plat->bus_id);
-			goto error_mdio_register;
-		}
+	ret = stmmac_mdio_register(ndev);
+	if (ret < 0) {
+		dev_err_probe(priv->device, ret,
+			      "MDIO bus (id: %d) registration failed\n",
+			      priv->plat->bus_id);
+		goto error_mdio_register;
 	}
 
 	if (priv->plat->speed_mode_2500)
@@ -7791,9 +7776,7 @@ int stmmac_dvr_probe(struct device *device,
 	phylink_destroy(priv->phylink);
 error_xpcs_setup:
 error_phy_setup:
-	if (priv->hw->pcs != STMMAC_PCS_TBI &&
-	    priv->hw->pcs != STMMAC_PCS_RTBI)
-		stmmac_mdio_unregister(ndev);
+	stmmac_mdio_unregister(ndev);
 error_mdio_register:
 	stmmac_napi_del(ndev);
 error_hw_init:
@@ -7822,7 +7805,6 @@ void stmmac_dvr_remove(struct device *dev)
 
 	stmmac_stop_all_dma(priv);
 	stmmac_mac_set(priv, priv->ioaddr, false);
-	netif_carrier_off(ndev);
 	unregister_netdev(ndev);
 
 #ifdef CONFIG_DEBUG_FS
@@ -7832,9 +7814,9 @@ void stmmac_dvr_remove(struct device *dev)
 	if (priv->plat->stmmac_rst)
 		reset_control_assert(priv->plat->stmmac_rst);
 	reset_control_assert(priv->plat->stmmac_ahb_rst);
-	if (priv->hw->pcs != STMMAC_PCS_TBI &&
-	    priv->hw->pcs != STMMAC_PCS_RTBI)
-		stmmac_mdio_unregister(ndev);
+
+	stmmac_mdio_unregister(ndev);
+
 	destroy_workqueue(priv->wq);
 	mutex_destroy(&priv->lock);
 	bitmap_free(priv->af_xdp_zc_qps);
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.h b/drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.h
index 13a30e6df4c1..1bdf87b237c4 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.h
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.h
@@ -74,23 +74,6 @@ static inline void dwmac_pcs_isr(void __iomem *ioaddr, u32 reg,
 	}
 }
 
-/**
- * dwmac_rane - To restart ANE
- * @ioaddr: IO registers pointer
- * @reg: Base address of the AN Control Register.
- * @restart: to restart ANE
- * Description: this is to just restart the Auto-Negotiation.
- */
-static inline void dwmac_rane(void __iomem *ioaddr, u32 reg, bool restart)
-{
-	u32 value = readl(ioaddr + GMAC_AN_CTRL(reg));
-
-	if (restart)
-		value |= GMAC_AN_CTRL_RAN;
-
-	writel(value, ioaddr + GMAC_AN_CTRL(reg));
-}
-
 /**
  * dwmac_ctrl_ane - To program the AN Control Register.
  * @ioaddr: IO registers pointer
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c
index 93295916b1d2..5f502265f0a6 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c
@@ -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_mac_change(wx->phylink, up);
+	phylink_pcs_change(&txgbe->xpcs->pcs, up);
 
 	return IRQ_HANDLED;
 }
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 1df0595c5ba9..6746a18a477d 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -68,11 +68,21 @@ config RUST_PHYLIB_ABSTRACTIONS
           Adds support needed for PHY drivers written in Rust. It provides
           a wrapper around the C phylib core.
 
+config SFP_BUS
+	bool
+
 config SFP
 	tristate "SFP cage support"
 	depends on I2C && PHYLINK
 	depends on HWMON || HWMON=n
 	select MDIO_I2C
+	select SFP_BUS
+
+config QSFP
+	tristate "QSFP cage support"
+	depends on I2C
+	select MDIO_I2C
+	select SFP_BUS
 
 comment "MII PHY device drivers"
 
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index 197acfa0b412..d1061e56ce9f 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -28,9 +28,9 @@ obj-$(CONFIG_PHYLIB)		+= libphy.o
 
 obj-$(CONFIG_NETWORK_PHY_TIMESTAMPING) += mii_timestamper.o
 
-obj-$(CONFIG_SFP)		+= sfp.o
-sfp-obj-$(CONFIG_SFP)		+= sfp-bus.o
-obj-y				+= $(sfp-obj-y) $(sfp-obj-m)
+obj-$(CONFIG_SFP)		+= sff.o sfp.o
+obj-$(CONFIG_QSFP)		+= sff.o qsfp.o
+obj-$(CONFIG_SFP_BUS)		+= sfp-bus.o
 
 obj-$(CONFIG_ADIN_PHY)		+= adin.o
 obj-$(CONFIG_ADIN1100_PHY)	+= adin1100.o
diff --git a/drivers/net/phy/aquantia/aquantia_main.c b/drivers/net/phy/aquantia/aquantia_main.c
index 71bfddb8f453..98a67c93c04a 100644
--- a/drivers/net/phy/aquantia/aquantia_main.c
+++ b/drivers/net/phy/aquantia/aquantia_main.c
@@ -769,6 +769,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),
@@ -946,7 +959,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 42ed013385bf..6f78d6c2bf4c 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
@@ -310,6 +314,8 @@ struct marvell_priv {
 	u32 last;
 	u32 step;
 	s8 pair;
+	u16 tx_pause_mask;
+	u16 rx_pause_mask;
 };
 
 static int marvell_read_page(struct phy_device *phydev)
@@ -673,6 +679,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);
@@ -1580,6 +1599,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;
 
@@ -1635,6 +1655,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;
 }
 
@@ -1678,6 +1703,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);
@@ -3162,14 +3188,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);
@@ -3259,11 +3297,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;
 
@@ -3295,6 +3345,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,
@@ -3314,7 +3365,8 @@ static struct phy_driver marvell_drivers[] = {
 		.phy_id_mask = MARVELL_PHY_ID_MASK,
 		.name = "Marvell 88E1111",
 		/* PHY_GBIT_FEATURES */
-		.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,
@@ -3336,6 +3388,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,
@@ -3535,7 +3588,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,
@@ -3564,7 +3617,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,
@@ -3616,7 +3669,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 8b9ead76e40e..7103c100dd1b 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, 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 c4236564c1cd..5d7a47309d26 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 6c6ec9475709..394fdef02bf3 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;
@@ -3057,6 +3071,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 503fd7c40523..6e237ee74abb 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);
 
@@ -86,6 +87,9 @@ struct phylink {
 	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 +179,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) {
@@ -885,26 +907,31 @@ static int phylink_parse_mode(struct phylink *pl,
 	const char *managed;
 	unsigned long caps;
 
+	if (pl->config->default_an_inband)
+		pl->cfg_link_an_mode = MLO_AN_INBAND;
+
 	dn = fwnode_get_named_child_node(fwnode, "fixed-link");
 	if (dn || fwnode_property_present(fwnode, "fixed-link"))
 		pl->cfg_link_an_mode = MLO_AN_FIXED;
 	fwnode_handle_put(dn);
 
 	if ((fwnode_property_read_string(fwnode, "managed", &managed) == 0 &&
-	     strcmp(managed, "in-band-status") == 0) ||
-	    pl->config->ovr_an_inband) {
-		if (pl->cfg_link_an_mode == MLO_AN_FIXED) {
+	     strcmp(managed, "in-band-status") == 0)) {
+		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;
 		}
 
+		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);
 		phylink_set(pl->supported, Asym_Pause);
 		phylink_set(pl->supported, Pause);
-		pl->cfg_link_an_mode = MLO_AN_INBAND;
 
 		switch (pl->link_config.interface) {
 		case PHY_INTERFACE_MODE_SGMII:
@@ -977,6 +1004,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)
 {
@@ -1030,15 +1066,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);
 }
 
@@ -1056,13 +1111,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)
@@ -1070,12 +1125,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
@@ -1093,11 +1150,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:
@@ -1109,10 +1174,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:
@@ -1123,23 +1185,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)
 {
@@ -1149,11 +1316,9 @@ 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) {
 		pcs = pl->mac_ops->mac_select_pcs(pl->config, state->interface);
@@ -1167,10 +1332,25 @@ static void phylink_major_config(struct phylink *pl, bool restart,
 		pcs_changed = pcs && 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",
@@ -1204,7 +1384,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;
 
@@ -1220,7 +1400,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",
@@ -1251,17 +1431,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);
+
+	/* 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->cur_link_an_mode;
+	neg_mode = pl->act_link_an_mode;
 	if (pl->pcs->neg_mode)
 		neg_mode = pl->pcs_neg_mode;
 
@@ -1326,7 +1512,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;
@@ -1365,6 +1551,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)
 {
@@ -1400,17 +1655,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);
 
@@ -1427,25 +1685,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;
@@ -1453,76 +1715,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
@@ -1683,13 +1942,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);
@@ -1697,7 +1959,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) {
@@ -1823,6 +2086,9 @@ static int phylink_validate_phy(struct phylink *pl, struct phy_device *phy,
 					     interfaces);
 	}
 
+	phylink_dbg(pl, "PHY %s doesn't supply possible interfaces\n",
+		    phydev_name(phy));
+
 	/* Check whether we would use rate matching for the proposed interface
 	 * mode.
 	 */
@@ -1902,6 +2168,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);
 
@@ -1923,8 +2196,8 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy,
 static int phylink_attach_phy(struct phylink *pl, struct phy_device *phy,
 			      phy_interface_t interface)
 {
-	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;
 
@@ -2010,14 +2283,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;
 	}
@@ -2141,7 +2414,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 */
@@ -2164,7 +2437,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) {
@@ -2352,6 +2625,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);
@@ -2400,7 +2699,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
@@ -2431,6 +2730,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()
@@ -2492,7 +2811,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;
@@ -2508,7 +2827,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;
@@ -2534,21 +2853,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;
@@ -2566,6 +2880,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;
@@ -2648,7 +2969,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) &&
@@ -2756,6 +3077,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;
 }
@@ -2772,8 +3095,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;
 }
@@ -2787,11 +3121,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;
 }
@@ -2912,7 +3288,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);
@@ -2937,7 +3313,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;
 
@@ -3107,10 +3483,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",
@@ -3127,9 +3504,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;
@@ -3144,13 +3521,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);
@@ -3171,30 +3545,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;
 }
@@ -3250,6 +3621,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 */
@@ -3263,7 +3640,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;
 }
@@ -3334,20 +3711,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;
 
 	/*
@@ -3359,30 +3727,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)
@@ -3501,12 +3898,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/qsfp.c b/drivers/net/phy/qsfp.c
new file mode 100644
index 000000000000..58139ce976b3
--- /dev/null
+++ b/drivers/net/phy/qsfp.c
@@ -0,0 +1,1835 @@
+// SPDX-License-Identifier: GPL-2.0
+#define DEBUG
+#include <linux/acpi.h>
+#include <linux/ctype.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/hwmon.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/mdio/mdio-i2c.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/phy.h>
+#include <linux/platform_device.h>
+#include <linux/rtnetlink.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+
+#include "sff.h"
+#include "sfp.h"
+#include "swphy.h"
+
+enum {
+	GPIO_MODPRS,
+	GPIO_INTL,
+	GPIO_MAX,
+
+	SFP_F_PRESENT = BIT(GPIO_MODPRS),
+	SFP_F_INTL = BIT(GPIO_INTL),
+	SFP_F_LOS = BIT(GPIO_MAX + 0),
+	SFP_F_TX_FAULT = BIT(GPIO_MAX + 1),
+	SFP_F_TX_DISABLE = BIT(GPIO_MAX + 2),
+
+	SFP_E_ATTACH = 0,
+	SFP_E_DETACH,
+	SFP_E_INSERT,
+	SFP_E_REMOVE,
+	SFP_E_DEV_DOWN,
+	SFP_E_DEV_UP,
+	SFP_E_TX_FAULT,
+	SFP_E_TX_CLEAR,
+	SFP_E_LOS_HIGH,
+	SFP_E_LOS_LOW,
+	SFP_E_TIMEOUT,
+
+	SFP_MOD_EMPTY = 0,
+	SFP_MOD_ERROR,
+	SFP_MOD_PROBE,
+	SFP_MOD_WATTACH,
+	SFP_MOD_PRESENT,
+
+	SFP_DEV_DETACHED = 0,
+	SFP_DEV_DOWN,
+	SFP_DEV_UP,
+
+	SFP_S_DOWN = 0,
+	SFP_S_ERROR,
+	SFP_S_WPOWER,
+	SFP_S_WTXEN,
+	SFP_S_INIT,
+	SFP_S_WAIT_LOS,
+	SFP_S_LINK_UP,
+	SFP_S_TX_FAULT,
+	SFP_S_REINIT,
+	SFP_S_TX_DISABLE,
+};
+
+static const char  * const mod_state_strings[] = {
+	[SFP_MOD_EMPTY] = "empty",
+	[SFP_MOD_ERROR] = "error",
+	[SFP_MOD_PROBE] = "probe",
+	[SFP_MOD_WATTACH] = "wattach",
+	[SFP_MOD_PRESENT] = "present",
+};
+
+static const char *mod_state_to_str(unsigned short mod_state)
+{
+	if (mod_state >= ARRAY_SIZE(mod_state_strings))
+		return "Unknown module state";
+	return mod_state_strings[mod_state];
+}
+
+static const char * const dev_state_strings[] = {
+	[SFP_DEV_DETACHED] = "unattached",
+	[SFP_DEV_DOWN] = "down",
+	[SFP_DEV_UP] = "up",
+};
+
+static const char *dev_state_to_str(unsigned short dev_state)
+{
+	if (dev_state >= ARRAY_SIZE(dev_state_strings))
+		return "Unknown device state";
+	return dev_state_strings[dev_state];
+}
+
+static const char * const event_strings[] = {
+	[SFP_E_ATTACH] = "attach",
+	[SFP_E_DETACH] = "detach",
+	[SFP_E_INSERT] = "insert",
+	[SFP_E_REMOVE] = "remove",
+	[SFP_E_DEV_DOWN] = "dev_down",
+	[SFP_E_DEV_UP] = "dev_up",
+	[SFP_E_TX_FAULT] = "tx_fault",
+	[SFP_E_TX_CLEAR] = "tx_clear",
+	[SFP_E_LOS_HIGH] = "los_high",
+	[SFP_E_LOS_LOW] = "los_low",
+	[SFP_E_TIMEOUT] = "timeout",
+};
+
+static const char *event_to_str(unsigned short event)
+{
+	if (event >= ARRAY_SIZE(event_strings))
+		return "Unknown event";
+	return event_strings[event];
+}
+
+static const char * const sm_state_strings[] = {
+	[SFP_S_DOWN] = "down",
+	[SFP_S_ERROR] = "error",
+	[SFP_S_WPOWER] = "wpower",
+	[SFP_S_WTXEN] = "wtxen",
+	[SFP_S_INIT] = "init",
+	[SFP_S_WAIT_LOS] = "wait_los",
+	[SFP_S_LINK_UP] = "link_up",
+	[SFP_S_TX_FAULT] = "tx_fault",
+	[SFP_S_REINIT] = "reinit",
+	[SFP_S_TX_DISABLE] = "rx_disable",
+};
+
+static const char *sm_state_to_str(unsigned short sm_state)
+{
+	if (sm_state >= ARRAY_SIZE(sm_state_strings))
+		return "Unknown state";
+	return sm_state_strings[sm_state];
+}
+
+static const char *gpio_of_names[] = {
+	"mod-prs",
+	"intl",
+};
+
+static const enum gpiod_flags gpio_flags[] = {
+	GPIOD_IN,
+	GPIOD_IN,
+};
+
+#define T_INIT_JIFFIES	msecs_to_jiffies(300)
+#define T_RESET_US	10
+#define T_FAULT_RECOVER	msecs_to_jiffies(1000)
+
+/*
+ * SFF8436 defines the time from power on until the module responds to data
+ * transmission over the serial bus as t_serial, 2 seconds.
+ */
+#define T_SERIAL	msecs_to_jiffies(2000)
+#define T_OFF_PDOWN	msecs_to_jiffies(300)
+#define T_OFF_TXDIS	msecs_to_jiffies(400)
+#define T_PROBE_RETRY	msecs_to_jiffies(100)
+
+/* SFP modules appear to always have their PHY configured for bus address
+ * 0x56 (which with mdio-i2c, translates to a PHY address of 22).
+ */
+#define SFP_PHY_ADDR	22
+
+struct sff_data {
+	unsigned int gpios;
+	bool (*module_supported)(const struct sfp_eeprom_id *id);
+};
+
+struct qsfp {
+	struct device *dev;
+	struct i2c_adapter *i2c;
+	struct mii_bus *i2c_mii;
+//	struct sfp_bus *sfp_bus;
+	const struct sff_data *type;
+	u32 max_power_mW;
+
+	int (*read)(struct qsfp *, u8, u8, void *, size_t);
+	int (*write)(struct qsfp *, u8, u8, void *, size_t);
+
+	struct gpio_desc *gpio[GPIO_MAX];
+	int gpio_irq[GPIO_MAX];
+
+	bool need_poll;
+
+	struct mutex st_mutex;			/* Protects state */
+	unsigned int state;
+	struct delayed_work poll;
+	struct delayed_work timeout;
+	struct mutex sm_mutex;			/* Protects state machine */
+	unsigned char sm_mod_state;
+	unsigned char sm_dev_state;
+	unsigned short sm_state;
+	unsigned int sm_retries;
+
+	// Module data
+	struct qsfp_sff8x36_id id;
+	unsigned int module_power_mW;
+	bool module_flat_mem;
+	u8 module_power_class;
+	u8 module_revision;
+	u8 module_irq_flags[19];
+	u8 request_tx_disable;
+	u8 current_tx_disable;
+
+#if IS_ENABLED(CONFIG_HWMON)
+	struct sfp_diag diag;
+	struct device *hwmon_dev;
+	char *hwmon_name;
+	__be16 thresholds[48];
+#endif
+
+};
+
+static const struct of_device_id qsfp_of_match[] = {
+	{ .compatible = "sff,qsfp", },
+	{ .compatible = "sff,qsfp+", },
+	{ .compatible = "sff,qsfp28", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, qsfp_of_match);
+
+static unsigned long poll_jiffies;
+
+static int qsfp_i2c_read(struct qsfp *qsfp, u8 page, u8 dev_addr, void *buf,
+			 size_t len)
+{
+	struct i2c_adapter *i2c = qsfp->i2c;
+	struct i2c_msg msgs[2];
+	size_t this_len;
+	u8 page_buf[2];
+	int ret;
+
+	page_buf[0] = 0x7f;
+	page_buf[1] = page;
+
+	msgs[0].addr = 0x50;
+	msgs[0].flags = 0;
+	msgs[0].len = sizeof(page_buf);
+	msgs[0].buf = page_buf;
+	msgs[1].addr = 0x50;
+	msgs[1].flags = I2C_M_RD;
+	msgs[1].len = len;
+	msgs[1].buf = buf;
+
+	i2c_lock_bus(i2c, I2C_LOCK_SEGMENT);
+
+	if (dev_addr >= 128 && !qsfp->module_flat_mem)
+		ret = __i2c_transfer(i2c, msgs, 1);
+	else
+		ret = 1;
+	if (ret == 1) {
+		msgs[0].len = 1;
+		msgs[0].buf = &dev_addr;
+
+		while (len) {
+			this_len = len;
+			if (this_len > 16)
+				this_len = 16;
+
+			msgs[1].len = this_len;
+
+			ret = __i2c_transfer(qsfp->i2c, msgs, ARRAY_SIZE(msgs));
+
+			if (ret != ARRAY_SIZE(msgs))
+				break;
+
+			msgs[1].buf += this_len;
+			dev_addr += this_len;
+			len -= this_len;
+		}
+	}
+	i2c_unlock_bus(i2c, I2C_LOCK_SEGMENT);
+
+	if (ret < 0)
+		return ret;
+
+	return ret == ARRAY_SIZE(msgs) ? 0 : -EIO;
+}
+
+static int qsfp_i2c_write(struct qsfp *qsfp, u8 page, u8 dev_addr, void *buf,
+			  size_t len)
+{
+	struct i2c_adapter *i2c = qsfp->i2c;
+	struct i2c_msg msgs[1];
+	u8 page_buf[2];
+	u8 *p;
+	int ret;
+
+	p = kmalloc(1 + len, GFP_KERNEL);
+	if (!p)
+		return -ENOMEM;
+	p[0] = dev_addr;
+	memcpy(&p[1], buf, len);
+
+	page_buf[0] = 0x7f;
+	page_buf[1] = page;
+
+	msgs[0].addr = 0x50;
+	msgs[0].flags = 0;
+	msgs[0].len = sizeof(page_buf);
+	msgs[0].buf = page_buf;
+
+	i2c_lock_bus(i2c, I2C_LOCK_SEGMENT);
+	if (dev_addr >= 128 && !qsfp->module_flat_mem)
+		ret = __i2c_transfer(i2c, msgs, 1);
+	else
+		ret = 1;
+	if (ret == 1) {
+		msgs[0].addr = 0x50;
+		msgs[0].flags = 0;
+		msgs[0].len = 1 + len;
+		msgs[0].buf = p;
+
+		ret = __i2c_transfer(qsfp->i2c, msgs, ARRAY_SIZE(msgs));
+	}
+	i2c_unlock_bus(i2c, I2C_LOCK_SEGMENT);
+
+	kfree(p);
+
+	if (ret < 0)
+		return ret;
+
+	return ret == ARRAY_SIZE(msgs) ? 0 : -EIO;
+}
+
+static int qsfp_i2c_configure(struct qsfp *qsfp, struct i2c_adapter *i2c)
+{
+	struct mii_bus *i2c_mii;
+	int ret;
+
+	if (!i2c_check_functionality(i2c, I2C_FUNC_I2C))
+		return -EINVAL;
+
+	qsfp->i2c = i2c;
+	qsfp->read = qsfp_i2c_read;
+	qsfp->write = qsfp_i2c_write;
+
+	i2c_mii = mdio_i2c_alloc(qsfp->dev, i2c, MDIO_I2C_MARVELL_C22);
+	if (IS_ERR(i2c_mii))
+		return PTR_ERR(i2c_mii);
+
+	i2c_mii->name = "QSFP I2C Bus";
+	i2c_mii->phy_mask = ~0;
+
+	ret = mdiobus_register(i2c_mii);
+	if (ret < 0) {
+		mdiobus_free(i2c_mii);
+		return ret;
+	}
+
+	qsfp->i2c_mii = i2c_mii;
+
+	return 0;
+}
+
+/* Interface */
+static int qsfp_read(struct qsfp *qsfp, u16 addr, void *buf, size_t len)
+{
+	return qsfp->read(qsfp, addr >> 8, addr, buf, len);
+}
+
+static int qsfp_write(struct qsfp *qsfp, u16 addr, void *buf, size_t len)
+{
+	return qsfp->write(qsfp, addr >> 8, addr, buf, len);
+}
+
+static int qsfp_modb(struct qsfp *qsfp, u16 addr, u8 mask, u8 set)
+{
+	int ret;
+	u8 val;
+
+	ret = qsfp_read(qsfp, addr, &val, sizeof(val));
+	if (ret < 0)
+		return ret;
+
+	val &= ~mask;
+	val |= set & mask;
+
+	return qsfp_write(qsfp, addr, &val, sizeof(val));
+}
+
+static unsigned int sfp_check(void *buf, size_t len)
+{
+	u8 *p, check;
+
+	for (p = buf, check = 0; len; p++, len--)
+		check += *p;
+
+	return check;
+}
+
+#if IS_ENABLED(CONFIG_HWMON)
+static umode_t qsfp_hwmon_is_visible(const void *data,
+				     enum hwmon_sensor_types type,
+				     u32 attr, int channel)
+{
+	const struct qsfp *qsfp = data;
+
+	switch (type) {
+	case hwmon_temp:
+		/* Temperature monitoring is optional, but this bit only
+		 * exists in SFF8636 revision 2.8+ */
+		if (qsfp->module_revision >= SFF8X36_REV_8636_2_8 &&
+		    !(qsfp->id.ext.sff8636.diagmon & SFF8636_DIAGMON_TEMP))
+			break;
+		switch (attr) {
+		case hwmon_temp_input:
+			return 0444;
+		case hwmon_temp_lcrit:
+		case hwmon_temp_min:
+		case hwmon_temp_max:
+		case hwmon_temp_crit:
+			return !qsfp->module_flat_mem ? 0444 : 0;
+		}
+		break;
+	case hwmon_in:
+		/* Supply monitoring is optional, but this bit only
+		 * exists in SFF8636 revision 2.8+ */
+		if (qsfp->module_revision >= SFF8X36_REV_8636_2_8 &&
+		    !(qsfp->id.ext.sff8636.diagmon & SFF8636_DIAGMON_VCC))
+			break;
+		switch (attr) {
+		case hwmon_in_input:
+			return 0444;
+		case hwmon_in_lcrit:
+		case hwmon_in_min:
+		case hwmon_in_max:
+		case hwmon_in_crit:
+			return !qsfp->module_flat_mem ? 0444 : 0;
+		}
+		break;
+	case hwmon_curr:
+		switch (attr) {
+		case hwmon_curr_input:
+		case hwmon_curr_label:
+			return 0444;
+		case hwmon_curr_lcrit:
+		case hwmon_curr_min:
+		case hwmon_curr_max:
+		case hwmon_curr_crit:
+			return !qsfp->module_flat_mem ? 0444 : 0;
+		}
+		break;
+	case hwmon_power:
+		/* TX power monitoring is optional, but this bit only
+		 * exists in SFF8636 revision 1.9+ - we only know 2.0+ */
+		if (channel >= 4 &&
+		    qsfp->module_revision >= SFF8X36_REV_8636_2_0 &&
+		    !(qsfp->id.ext.sff8636.diagmon & SFF8636_DIAGMON_TXPWR))
+			break;
+		switch (attr) {
+		case hwmon_power_input:
+		case hwmon_power_label:
+			return 0444;
+		case hwmon_power_lcrit:
+		case hwmon_power_min:
+		case hwmon_power_max:
+		case hwmon_power_crit:
+			return !qsfp->module_flat_mem ? 0444 : 0;
+		}
+		break;
+	default:
+		break;
+	}
+	return 0;
+}
+
+static int qsfp_hwmon_temp(struct qsfp *qsfp, u32 attr, long *value)
+{
+	__be16 temp;
+	int ret;
+
+	switch (attr) {
+	case hwmon_temp_input:
+		ret = qsfp_read(qsfp, SFF8X36_TEMPERATURE, &temp, sizeof(temp));
+		if (ret < 0)
+			return ret;
+		break;
+	case hwmon_temp_lcrit:
+		temp = qsfp->thresholds[1];
+		break;
+	case hwmon_temp_min:
+		temp = qsfp->thresholds[3];
+		break;
+	case hwmon_temp_max:
+		temp = qsfp->thresholds[2];
+		break;
+	case hwmon_temp_crit:
+		temp = qsfp->thresholds[0];
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	*value = DIV_ROUND_CLOSEST(((s16)be16_to_cpu(temp)) * 1000, 256);
+
+	return 0;
+}
+
+static int qsfp_hwmon_vcc(struct qsfp *qsfp, u32 attr, long *value)
+{
+	__be16 volt;
+	int ret;
+
+	switch (attr) {
+	case hwmon_in_input:
+		ret = qsfp_read(qsfp, SFF8X36_SUPPLY_VOLTAGE,
+				&volt, sizeof(volt));
+		if (ret < 0)
+			return ret < 0;
+		break;
+	case hwmon_in_lcrit:
+		volt = qsfp->thresholds[9];
+		break;
+	case hwmon_in_min:
+		volt = qsfp->thresholds[11];
+		break;
+	case hwmon_in_max:
+		volt = qsfp->thresholds[10];
+		break;
+	case hwmon_in_crit:
+		volt = qsfp->thresholds[8];
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	*value = DIV_ROUND_CLOSEST(be16_to_cpu(volt), 10);
+
+	return 0;
+}
+
+static int qsfp_hwmon_bias(struct qsfp *qsfp, u32 attr, int chan, long *value)
+{
+	__be16 bias;
+	u8 addr;
+	int ret;
+
+	switch (attr) {
+	case hwmon_curr_input:
+		addr = SFF8X36_TX_BIAS + 2 * chan;
+
+		ret = qsfp_read(qsfp, addr, &bias, sizeof(bias));
+		if (ret < 0)
+			return ret < 0;
+		break;
+	case hwmon_curr_lcrit:
+		bias = qsfp->thresholds[29];
+		break;
+	case hwmon_curr_min:
+		bias = qsfp->thresholds[31];
+		break;
+	case hwmon_curr_max:
+		bias = qsfp->thresholds[30];
+		break;
+	case hwmon_curr_crit:
+		bias = qsfp->thresholds[28];
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	*value = DIV_ROUND_CLOSEST(be16_to_cpu(bias), 10);
+
+	return 0;
+}
+
+static int qsfp_hwmon_power(struct qsfp *qsfp, u32 attr, int chan, long *value)
+{
+	__be16 pwr;
+	u8 addr;
+	int ret;
+
+	switch (attr) {
+	case hwmon_power_input:
+		addr = 2 * (chan & 3) +
+		       (chan & 4 ? SFF8X36_TX_POWER : SFF8X36_RX_POWER);
+
+		ret = qsfp_read(qsfp, addr, &pwr, sizeof(pwr));
+		if (ret < 0)
+			return ret;
+		break;
+	case hwmon_power_lcrit:
+		pwr = qsfp->thresholds[chan & 4 ? 33 : 25];
+		break;
+	case hwmon_power_min:
+		pwr = qsfp->thresholds[chan & 4 ? 35 : 27];
+		break;
+	case hwmon_power_max:
+		pwr = qsfp->thresholds[chan & 4 ? 34 : 26];
+		break;
+	case hwmon_power_crit:
+		pwr = qsfp->thresholds[chan & 4 ? 32 : 24];
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	*value = DIV_ROUND_CLOSEST(be16_to_cpu(pwr), 10);
+
+	return 0;
+}
+
+static int qsfp_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
+			   u32 attr, int channel, long *value)
+{
+	struct qsfp *qsfp = dev_get_drvdata(dev);
+
+	switch (type) {
+	case hwmon_temp:
+		return qsfp_hwmon_temp(qsfp, attr, value);
+	case hwmon_in:
+		return qsfp_hwmon_vcc(qsfp, attr, value);
+	case hwmon_curr:
+		return qsfp_hwmon_bias(qsfp, attr, channel, value);
+	case hwmon_power:
+		return qsfp_hwmon_power(qsfp, attr, channel, value);
+	default:
+		break;
+	}
+	return -EOPNOTSUPP;
+}
+
+static const char *qsfp_hwmon_power_labels[] = {
+	"power_rx_1",
+	"power_rx_2",
+	"power_rx_3",
+	"power_rx_4",
+	"power_tx_1",
+	"power_tx_2",
+	"power_tx_3",
+	"power_tx_4",
+};
+
+static const char *qsfp_hwmon_curr_labels[] = {
+	"bias_tx_1",
+	"bias_tx_2",
+	"bias_tx_3",
+	"bias_tx_4",
+};
+
+static int qsfp_hwmon_read_str(struct device *dev, enum hwmon_sensor_types type,
+			       u32 attr, int channel, const char **str)
+{
+	switch (type) {
+	case hwmon_curr:
+		if (attr != hwmon_curr_label || channel < 0 ||
+		    channel > ARRAY_SIZE(qsfp_hwmon_curr_labels))
+			break;
+		*str = qsfp_hwmon_curr_labels[channel];
+		return 0;
+	case hwmon_power:
+		if (attr != hwmon_power_label || channel < 0 ||
+		    channel > ARRAY_SIZE(qsfp_hwmon_power_labels))
+			break;
+		*str = qsfp_hwmon_power_labels[channel];
+		return 0;
+	default:
+		break;
+	}
+	return -EOPNOTSUPP;
+}
+
+static const struct hwmon_ops qsfp_hwmon_ops = {
+	.is_visible = qsfp_hwmon_is_visible,
+	.read = qsfp_hwmon_read,
+	.read_string = qsfp_hwmon_read_str,
+};
+
+static const u32 qsfp_hwmon_chip_config[] = {
+	HWMON_C_REGISTER_TZ,
+	0,
+};
+
+static const struct hwmon_channel_info qsfp_hwmon_chip = {
+	.type = hwmon_chip,
+	.config = qsfp_hwmon_chip_config,
+};
+
+static const u32 qsfp_hwmon_temp_config[] = {
+	HWMON_T_INPUT |
+	HWMON_T_LCRIT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_CRIT,
+	0,
+};
+
+static const struct hwmon_channel_info qsfp_hwmon_temp_channel_info = {
+	.type = hwmon_temp,
+	.config = qsfp_hwmon_temp_config,
+};
+
+static const u32 qsfp_hwmon_vcc_config[] = {
+	HWMON_I_INPUT |
+	HWMON_I_LCRIT | HWMON_I_MIN | HWMON_I_MAX | HWMON_I_CRIT,
+	0,
+};
+
+static const struct hwmon_channel_info qsfp_hwmon_vcc_channel_info = {
+	.type = hwmon_in,
+	.config = qsfp_hwmon_vcc_config,
+};
+
+static u32 qsfp_hwmon_bias_config[] = {
+	HWMON_C_INPUT | HWMON_C_LABEL |
+	HWMON_C_LCRIT | HWMON_C_MIN | HWMON_C_MAX | HWMON_C_CRIT,
+	HWMON_C_INPUT | HWMON_C_LABEL |
+	HWMON_C_LCRIT | HWMON_C_MIN | HWMON_C_MAX | HWMON_C_CRIT,
+	HWMON_C_INPUT | HWMON_C_LABEL |
+	HWMON_C_LCRIT | HWMON_C_MIN | HWMON_C_MAX | HWMON_C_CRIT,
+	HWMON_C_INPUT | HWMON_C_LABEL |
+	HWMON_C_LCRIT | HWMON_C_MIN | HWMON_C_MAX | HWMON_C_CRIT,
+	0,
+};
+
+static const struct hwmon_channel_info qsfp_hwmon_bias_channel_info = {
+	.type = hwmon_curr,
+	.config = qsfp_hwmon_bias_config,
+};
+
+static const u32 qsfp_hwmon_power_config[] = {
+	/* Receive power */
+	HWMON_P_INPUT | HWMON_P_LABEL |
+	HWMON_P_LCRIT | HWMON_P_MIN | HWMON_P_MAX | HWMON_P_CRIT,
+	HWMON_P_INPUT | HWMON_P_LABEL |
+	HWMON_P_LCRIT | HWMON_P_MIN | HWMON_P_MAX | HWMON_P_CRIT,
+	HWMON_P_INPUT | HWMON_P_LABEL |
+	HWMON_P_LCRIT | HWMON_P_MIN | HWMON_P_MAX | HWMON_P_CRIT,
+	HWMON_P_INPUT | HWMON_P_LABEL |
+	HWMON_P_LCRIT | HWMON_P_MIN | HWMON_P_MAX | HWMON_P_CRIT,
+	/* Transmit power */
+	HWMON_P_INPUT | HWMON_P_LABEL |
+	HWMON_P_LCRIT | HWMON_P_MIN | HWMON_P_MAX | HWMON_P_CRIT,
+	HWMON_P_INPUT | HWMON_P_LABEL |
+	HWMON_P_LCRIT | HWMON_P_MIN | HWMON_P_MAX | HWMON_P_CRIT,
+	HWMON_P_INPUT | HWMON_P_LABEL |
+	HWMON_P_LCRIT | HWMON_P_MIN | HWMON_P_MAX | HWMON_P_CRIT,
+	HWMON_P_INPUT | HWMON_P_LABEL |
+	HWMON_P_LCRIT | HWMON_P_MIN | HWMON_P_MAX | HWMON_P_CRIT,
+	0,
+};
+
+static const struct hwmon_channel_info qsfp_hwmon_power_channel_info = {
+	.type = hwmon_power,
+	.config = qsfp_hwmon_power_config,
+};
+
+static const struct hwmon_channel_info *qsfp_hwmon_info[] = {
+	&qsfp_hwmon_chip,
+	&qsfp_hwmon_vcc_channel_info,
+	&qsfp_hwmon_temp_channel_info,
+	&qsfp_hwmon_bias_channel_info,
+	&qsfp_hwmon_power_channel_info,
+	NULL,
+};
+
+static const struct hwmon_chip_info qsfp_hwmon_chip_info = {
+	.ops = &qsfp_hwmon_ops,
+	.info = qsfp_hwmon_info,
+};
+
+static int qsfp_hwmon_insert(struct qsfp *qsfp)
+{
+	int i, ret;
+
+	if (!qsfp->module_flat_mem) {
+		ret = qsfp_read(qsfp, SFF8X36_THRESHOLDS, qsfp->thresholds,
+				sizeof(qsfp->thresholds));
+		if (ret < 0)
+			return ret < 0;
+	}
+
+	qsfp->hwmon_name = kstrdup(dev_name(qsfp->dev), GFP_KERNEL);
+	if (!qsfp->hwmon_name)
+		return -ENOMEM;
+
+	for (i = 0; qsfp->hwmon_name[i]; i++)
+		if (hwmon_is_bad_char(qsfp->hwmon_name[i]))
+			qsfp->hwmon_name[i] = '_';
+
+	qsfp->hwmon_dev = hwmon_device_register_with_info(qsfp->dev,
+							  qsfp->hwmon_name,
+							  qsfp,
+							  &qsfp_hwmon_chip_info,
+							  NULL);
+
+	return PTR_ERR_OR_ZERO(qsfp->hwmon_dev);
+}
+
+static void qsfp_hwmon_remove(struct qsfp *qsfp)
+{
+	if (!IS_ERR_OR_NULL(qsfp->hwmon_dev)) {
+		hwmon_device_unregister(qsfp->hwmon_dev);
+		qsfp->hwmon_dev = NULL;
+		kfree(qsfp->hwmon_name);
+	}
+}
+#else
+static int qsfp_hwmon_insert(struct qsfp *qsfp)
+{
+	return 0;
+}
+
+static void qsfp_hwmon_remove(struct qsfp *qsfp)
+{
+}
+#endif
+
+static const char *sff8436_encoding(unsigned int encoding)
+{
+	switch (encoding) {
+	case SFF8024_ENCODING_UNSPEC:
+		return "unspecified";
+	case SFF8024_ENCODING_8436_64B66B:
+		return "64b66b";
+	case SFF8024_ENCODING_8B10B:
+		return "8b10b";
+	case SFF8024_ENCODING_4B5B:
+		return "4b5b";
+	case SFF8024_ENCODING_NRZ:
+		return "NRZ";
+	case SFF8024_ENCODING_8436_MANCHESTER:
+		return "MANCHESTER";
+	default:
+		return "unknown";
+	}
+}
+
+/* SFP state machine */
+static void qsfp_sm_set_timer(struct qsfp *sfp, unsigned int timeout)
+{
+	if (timeout)
+		mod_delayed_work(system_power_efficient_wq, &sfp->timeout,
+				 timeout);
+	else
+		cancel_delayed_work(&sfp->timeout);
+}
+
+static void qsfp_sm_next(struct qsfp *sfp, unsigned int state,
+			unsigned int timeout)
+{
+	sfp->sm_state = state;
+	qsfp_sm_set_timer(sfp, timeout);
+}
+
+static void qsfp_sm_ins_next(struct qsfp *sfp, unsigned int state,
+			    unsigned int timeout)
+{
+	sfp->sm_mod_state = state;
+	qsfp_sm_set_timer(sfp, timeout);
+}
+
+static void sfp_sm_link_up(struct qsfp *sfp)
+{
+//	sfp_link_up(sfp->sfp_bus);
+	qsfp_sm_next(sfp, SFP_S_LINK_UP, 0);
+}
+
+static void sfp_sm_link_down(struct qsfp *sfp)
+{
+//	sfp_link_down(sfp->sfp_bus);
+}
+
+static void sfp_sm_link_check_los(struct qsfp *sfp)
+{
+	sfp_sm_link_up(sfp);
+}
+
+static bool sfp_los_event_active(struct qsfp *sfp, unsigned int event)
+{
+	return 0;
+}
+
+static bool sfp_los_event_inactive(struct qsfp *sfp, unsigned int event)
+{
+	return 0;
+}
+
+static int qsfp_set_tx_disable(struct qsfp *qsfp, unsigned int val)
+{
+	return qsfp_modb(qsfp, SFF8X36_TX_DISABLE,
+			 SFF8X36_TX_DISABLE_TX4 | SFF8X36_TX_DISABLE_TX3 |
+			 SFF8X36_TX_DISABLE_TX2 | SFF8X36_TX_DISABLE_TX1, val);
+}
+
+static int qsfp_set_power(struct qsfp *qsfp, unsigned int val)
+{
+	return qsfp_modb(qsfp, SFF8X36_CTRL_93,
+			 SFF8X36_CTRL_93_POWER_ORIDE |
+			 SFF8X36_CTRL_93_POWER_SET |
+			 SFF8636_CTRL_93_POWER_CLS5_7 |
+			 SFF8636_CTRL_93_POWER_CLS8, val);
+}
+
+static void sfp_sm_fault(struct qsfp *sfp, bool warn)
+{
+	if (sfp->sm_retries && !--sfp->sm_retries) {
+		dev_err(sfp->dev,
+			"module persistently indicates fault, disabling\n");
+		qsfp_sm_next(sfp, SFP_S_TX_DISABLE, 0);
+	} else {
+		if (warn)
+			dev_err(sfp->dev, "module transmit fault indicated\n");
+
+		qsfp_sm_next(sfp, SFP_S_TX_FAULT, T_FAULT_RECOVER);
+	}
+}
+
+/* Device/upstream state machine - tracks whether we are attached to an
+ * upstream, and whether the upstream is ready to pass data (i.o.w. up).
+ */
+static void qsfp_sm_device(struct qsfp *qsfp, unsigned int event)
+{
+	if (event == SFP_E_DETACH)
+		qsfp->sm_dev_state = SFP_DEV_DETACHED;
+
+	switch (qsfp->sm_dev_state) {
+	default:
+		if (event == SFP_E_ATTACH)
+			qsfp->sm_dev_state = SFP_DEV_DOWN;
+		break;
+
+	case SFP_DEV_DOWN:
+		if (event == SFP_E_DEV_UP)
+			qsfp->sm_dev_state = SFP_DEV_UP;
+		break;
+
+	case SFP_DEV_UP:
+		if (event == SFP_E_DEV_DOWN)
+			qsfp->sm_dev_state = SFP_DEV_DOWN;
+		break;
+	}
+}
+
+static int qsfp_parse_power(struct qsfp *qsfp)
+{
+	unsigned int power_mW, power_class;
+	u8 pwr, mask;
+	int ret;
+
+	if (qsfp->module_revision >= SFF8X36_REV_8636_2_8 &&
+            qsfp->id.base.ext_identifier & BIT(5)) {
+		ret = qsfp_read(qsfp, SFF8X36_CLS8_MAX_POWER, &pwr,
+				sizeof(pwr));
+		if (ret < 0)
+			return ret;
+
+		power_class = 8;
+		power_mW = pwr * 100;
+	} else {
+		if (qsfp->id.base.identifier == SFF8024_ID_QSFP_8438)
+			mask = 0xc0;
+		else
+			mask = 0xc3;
+
+		switch (qsfp->id.base.ext_identifier & mask) {
+		default:
+			power_mW = 1500;
+			power_class = 1;
+			break;
+		case 0x40:
+			power_mW = 2000;
+			power_class = 2;
+			break;
+		case 0x80:
+			power_mW = 2500;
+			power_class = 3;
+			break;
+		case 0xc0:
+			power_mW = 3500;
+			power_class = 4;
+			break;
+		case 0xc1:
+			power_mW = 4000;
+			power_class = 5;
+			break;
+		case 0xc2:
+			power_mW = 4500;
+			power_class = 6;
+			break;
+		case 0xc3:
+			power_mW = 5000;
+			power_class = 7;
+			break;
+		}
+	}
+
+	if (power_mW > qsfp->max_power_mW) {
+		if (power_mW > 3500u) {
+			dev_warn(qsfp->dev,
+				 "Host does not support %u.%uW modules, limited to 3.5W\n",
+				 power_mW / 1000, (power_mW / 100) % 10);
+			return 0;
+		} else {
+			dev_err(qsfp->dev,
+				"Host does not support %u.%uW modules\n",
+				power_mW / 1000, (power_mW / 100) % 10);
+			return -EINVAL;
+		}
+	}
+
+	qsfp->module_power_mW = power_mW;
+	qsfp->module_power_class = power_class;
+
+	dev_info(qsfp->dev, "module power %u.%uW\n",
+		 qsfp->module_power_mW / 1000,
+		 (qsfp->module_power_mW / 100) % 10);
+
+	return 0;
+}
+
+static void qsfp_mod_clear(struct qsfp *qsfp)
+{
+	qsfp->module_power_class = 0;
+	qsfp->module_power_mW = 0;
+	qsfp->module_flat_mem = false;
+	qsfp->module_revision = 0;
+	memset(&qsfp->id, 0, sizeof(qsfp->id));
+	memset(&qsfp->module_irq_flags, 0, sizeof(qsfp->module_irq_flags));
+}
+
+static void qsfp_sm_mod_print(struct qsfp *qsfp)
+{
+	const struct qsfp_sff8x36_id *id = &qsfp->id;
+	unsigned int br_nom, wavelen;
+	bool copper;
+	char length[16];
+	char date[9];
+
+	date[0] = id->ext.sff8436.datecode[4];
+	date[1] = id->ext.sff8436.datecode[5];
+	date[2] = '-';
+	date[3] = id->ext.sff8436.datecode[2];
+	date[4] = id->ext.sff8436.datecode[3];
+	date[5] = '-';
+	date[6] = id->ext.sff8436.datecode[0];
+	date[7] = id->ext.sff8436.datecode[1];
+	date[8] = '\0';
+
+	if (id->base.br_nominal == 0) {
+		br_nom = 0;
+	} else if (id->base.br_nominal == 255 &&
+		   qsfp->module_revision >= SFF8X36_REV_8636_1_3) {
+		br_nom = 250 * id->ext.sff8636.baud_rate_nominal;
+	} else {
+		br_nom = 100 * id->base.br_nominal;
+	}
+
+	dev_info(qsfp->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.sff8436.vendor_sn), id->ext.sff8436.vendor_sn,
+		 date);
+
+	dev_info(qsfp->dev, "  0x%02x %s, 0x%02x %s\n",
+		 id->base.identifier,
+		 id->base.identifier == 0x0c ? "QSFP (INF-8438)" :
+		 id->base.identifier == 0x0d ? "QSFP+ (SFF-8636 or SFF-8436)" :
+		 id->base.identifier == 0x11 ? "QSFP28 (SFF-8636)" : "unknown",
+		 qsfp->module_revision,
+		 qsfp->module_revision == 0x00 ? "unspecified" :
+		 qsfp->module_revision == 0x01 ? "SFF-8436 4.8 or earlier" :
+		 qsfp->module_revision == 0x02 ? "SFF-8436 4.8 or earlier+" :
+		 qsfp->module_revision == 0x03 ? "SFF-8636 1.3 or earlier" :
+		 qsfp->module_revision == 0x04 ? "SFF-8636 1.4" :
+		 qsfp->module_revision == 0x05 ? "SFF-8636 1.5" :
+		 qsfp->module_revision == 0x06 ? "SFF-8636 2.0" :
+		 qsfp->module_revision == 0x07 ? "SFF-8636 2.5..2.7" :
+		 qsfp->module_revision == 0x08 ? "SFF-8636 2.8..2.10" : "unknown");
+
+	dev_info(qsfp->dev, "  %s connector, encoding %s, bitrate %u.%03u GBd\n",
+		 sff_connector(id->base.connector),
+		 sff8436_encoding(id->base.encoding),
+		 br_nom / 1000, br_nom % 1000);
+
+	dev_info(qsfp->dev, "  Compliance bytes: %*ph  %02x\n",
+		 (int)sizeof(id->base.compliance), id->base.compliance,
+		 id->ext.sff8636.link_codes);
+
+	copper = id->base.compliance[0] & BIT(0) ||
+		 id->base.compliance[3] & (BIT(2) | BIT(3));
+
+	if (copper) {
+		dev_info(qsfp->dev, "  Copper Attenuation @ 2.5 GHz: %udB\n",
+			 id->base.copper_atten[0]);
+		dev_info(qsfp->dev, "  Copper Attenuation @ 5 GHz  : %udB\n",
+			 id->base.copper_atten[1]);
+		dev_info(qsfp->dev, "  Copper length               : %s\n",
+			 sff_link_len(length, sizeof(length),
+				      id->base.length[4], 1));
+	} else {
+		wavelen = be16_to_cpup(&id->base.wavelength) * 5;
+		dev_info(qsfp->dev, "  Wavelength %u.%02unm, fiber lengths:\n",
+			 wavelen / 100, wavelen % 100);
+		dev_info(qsfp->dev, "     9µm SM    : %s\n",
+			 sff_link_len(length, sizeof(length),
+				      id->base.length[0], 1));
+		dev_info(qsfp->dev, "  62.5µm MM OM1: %s\n",
+			 sff_link_len(length, sizeof(length),
+				      id->base.length[3], 1));
+		dev_info(qsfp->dev, "    50µm MM OM2: %s\n",
+			 sff_link_len(length, sizeof(length),
+				      id->base.length[2], 1));
+		dev_info(qsfp->dev, "    50µm MM OM3: %s\n",
+			 sff_link_len(length, sizeof(length),
+				      id->base.length[1], 2));
+		dev_info(qsfp->dev, "    50µm MM OM4: %s\n",
+			 sff_link_len(length, sizeof(length),
+				      id->base.length[4], 2));
+	}
+}
+
+static int qsfp_sm_mod_probe(struct qsfp *sfp)
+{
+	struct qsfp_sff8x36_id_stat id_stat;
+	struct qsfp_sff8x36_id id;
+	//char options[80];
+	u8 check;
+	int ret;
+
+	ret = qsfp_read(sfp, 0, &id_stat, sizeof(id_stat));
+	if (ret < 0) {
+		dev_err(sfp->dev, "failed to read EEPROM: %d\n", ret);
+		return -EAGAIN;
+	}
+
+	// FIXME: check id_stat.identifier values
+
+	// Wait for the module to report ready
+	if (id_stat.status & SFF8X36_STAT_DATA_NOT_READY)
+		return -EAGAIN;
+
+	// Early setup - we need to know if this module has a page register
+	sfp->module_flat_mem = id_stat.status & SFF8X36_STAT_FLAT_MEM;
+	sfp->module_revision = id_stat.rev_compliance;
+
+	ret = qsfp_read(sfp, SFF8X36_ID, &id, sizeof(id));
+	if (ret < 0) {
+		qsfp_mod_clear(sfp);
+		dev_err(sfp->dev, "failed to read EEPROM: %d\n", ret);
+		return -EAGAIN;
+	}
+
+	if (id.base.identifier != id_stat.identifier) {
+		qsfp_mod_clear(sfp);
+		dev_err(sfp->dev, "QSFP identifier mismatch: 0x%02x != 0x%02x\n",
+			id_stat.identifier, id.base.identifier);
+		return -EINVAL;
+	}
+
+	// Validate the checksum over the base structure
+	check = sfp_check(&id.base, sizeof(id.base) - 1);
+	if (check != id.base.cc_base) {
+		qsfp_mod_clear(sfp);
+		dev_err(sfp->dev,
+			"EEPROM base structure checksum failure: 0x%02x != 0x%02x\n",
+			check, id.base.cc_base);
+		print_hex_dump(KERN_ERR, "sfp EE: ", DUMP_PREFIX_OFFSET,
+			       16, 1, &id, sizeof(id), true);
+		return -EINVAL;
+	}
+
+	// Validate the checksum over the extended structure
+	check = sfp_check(&id.ext.sff8436, sizeof(id.ext.sff8436) - 1);
+	if (check != id.ext.sff8436.cc_ext) {
+		dev_err(sfp->dev,
+			"EEPROM extended structure checksum failure: 0x%02x != 0x%02x\n",
+			check, id.ext.sff8436.cc_ext);
+		print_hex_dump(KERN_ERR, "sfp EE: ", DUMP_PREFIX_OFFSET,
+			       16, 1, &id, sizeof(id), true);
+		memset(&id.ext, 0, sizeof(id.ext));
+	}
+
+	sfp->id = id;
+
+	if (id.ext.sff8436.options[3] & SFF8X36_OPTIONS195_TX_DISABLE) {
+		// Active tx disable
+		sfp->request_tx_disable = SFF8X36_TX_DISABLE_TX4 |
+					  SFF8X36_TX_DISABLE_TX3 |
+					  SFF8X36_TX_DISABLE_TX2 |
+					  SFF8X36_TX_DISABLE_TX1;
+		sfp->current_tx_disable = sfp->request_tx_disable;
+		ret = qsfp_set_tx_disable(sfp, sfp->current_tx_disable);
+		if (ret < 0) {
+			qsfp_mod_clear(sfp);
+			return ret;
+		}
+	}
+
+	// Parse the module power requirements
+	ret = qsfp_parse_power(sfp);
+	if (ret) {
+		qsfp_mod_clear(sfp);
+		return ret;
+	}
+
+	qsfp_sm_mod_print(sfp);
+
+	/* Check whether we support this module */
+//	if (!sfp->type->module_supported(&id)) {
+//		dev_err(sfp->dev,
+//			"module is not supported - phys id 0x%02x 0x%02x\n",
+//			id.base.phys_id, id.base.phys_ext_id);
+//		return -EINVAL;
+//	}
+
+	return qsfp_hwmon_insert(sfp);
+}
+
+static void qsfp_sm_mod_present(struct qsfp *qsfp)
+{
+//	int ret;
+
+	if (qsfp->sm_dev_state == SFP_DEV_DETACHED) {
+		qsfp_sm_ins_next(qsfp, SFP_MOD_WATTACH, 0);
+		return;
+	}
+
+	// Start the poller if there is no interrupt support if not running
+	if (!qsfp->gpio_irq[GPIO_INTL])
+		queue_delayed_work(system_wq, &qsfp->poll, poll_jiffies);
+
+//	ret = sfp_module_insert(qsfp->sfp_bus, &id);
+//	if (ret < 0)
+//		qsfp_sm_ins_next(qsfp, SFP_MOD_ERROR, 0);
+//	else
+		qsfp_sm_ins_next(qsfp, SFP_MOD_PRESENT, 0);
+}
+
+static void qsfp_sm_mod_remove(struct qsfp *qsfp)
+{
+//	if (qsfp->sm_mod_state >= SFP_MOD_PRESENT)
+//		sfp_module_remove(qsfp->sfp_bus);
+
+	qsfp_hwmon_remove(qsfp);
+	qsfp_mod_clear(qsfp);
+
+	dev_info(qsfp->dev, "module removed\n");
+}
+
+/* Module state machine - this tracks the insertion state of the module,
+ * probes the EEPROM to identify the type of module and the power level.
+ *
+ * We handle remove events outside of the main switch() as what
+ * is required is fairly universal between the various states.
+ */
+static void qsfp_sm_module(struct qsfp *qsfp, unsigned int event)
+{
+	int ret;
+
+	if (event == SFP_E_REMOVE && qsfp->sm_mod_state != SFP_MOD_EMPTY) {
+		if (qsfp->sm_mod_state >= SFP_MOD_WATTACH)
+			qsfp_sm_mod_remove(qsfp);
+		qsfp_sm_ins_next(qsfp, SFP_MOD_EMPTY, 0);
+	}
+
+	switch (qsfp->sm_mod_state) {
+	default:
+		if (event == SFP_E_INSERT) {
+			qsfp_sm_ins_next(qsfp, SFP_MOD_PROBE, T_SERIAL);
+			qsfp->sm_retries = 10;
+		}
+		break;
+
+	case SFP_MOD_PROBE:
+		if (event == SFP_E_TIMEOUT) {
+			ret = qsfp_sm_mod_probe(qsfp);
+			if (ret == 0)
+				qsfp_sm_mod_present(qsfp);
+			else if (ret != -EAGAIN)
+				qsfp_sm_ins_next(qsfp, SFP_MOD_ERROR, 0);
+			else if (--qsfp->sm_retries)
+				qsfp_sm_set_timer(qsfp, T_PROBE_RETRY);
+			else
+				qsfp_sm_mod_present(qsfp);
+		}
+		break;
+
+	case SFP_MOD_WATTACH:
+		if (event == SFP_E_ATTACH)
+			qsfp_sm_mod_present(qsfp);
+		break;
+
+	case SFP_MOD_PRESENT:
+		if (event == SFP_E_DETACH)
+			qsfp_sm_ins_next(qsfp, SFP_MOD_WATTACH, 0);
+		break;
+	}
+}
+
+static int qsfp_sm_power_up(struct qsfp *qsfp)
+{
+	int ret;
+	u8 val;
+
+	val = SFF8X36_CTRL_93_POWER_ORIDE;
+	if (qsfp->module_power_class >= 8)
+		val |= SFF8636_CTRL_93_POWER_CLS8;
+	if (qsfp->module_power_mW >= 1500u)
+		val |= SFF8636_CTRL_93_POWER_CLS5_7;
+
+	ret = qsfp_set_power(qsfp, val);
+	if (ret < 0)
+		qsfp_sm_next(qsfp, SFP_S_ERROR, 0);
+	else
+		qsfp_sm_next(qsfp, SFP_S_WPOWER, T_OFF_PDOWN);
+
+	return 1;
+}
+
+static void qsfp_sm_power_down(struct qsfp *qsfp)
+{
+	qsfp_set_power(qsfp, SFF8X36_CTRL_93_POWER_ORIDE |
+		       SFF8X36_CTRL_93_POWER_SET);
+}
+
+static int sfp_sm_tx_enable(struct qsfp *qsfp)
+{
+	if (qsfp->id.ext.sff8436.options[3] & SFF8X36_OPTIONS195_TX_DISABLE) {
+		if (qsfp_set_tx_disable(qsfp, 0) < 0)
+			qsfp_sm_next(qsfp, SFP_S_ERROR, 0);
+		else
+			qsfp_sm_next(qsfp, SFP_S_WTXEN, T_OFF_TXDIS);
+		return 1;
+	}
+	return 0;
+}
+
+static void sfp_sm_init(struct qsfp *qsfp)
+{
+	qsfp_sm_next(qsfp, SFP_S_INIT, T_OFF_PDOWN);
+}
+
+static void qsfp_sm_main(struct qsfp *qsfp, unsigned int event)
+{
+	if (qsfp->sm_mod_state != SFP_MOD_PRESENT ||
+	    qsfp->sm_dev_state != SFP_DEV_UP) {
+		if (qsfp->sm_state != SFP_S_DOWN) {
+			/* If we reported link up, and the upstream is
+			 * still up, report link down.
+			 */
+			if (qsfp->sm_state == SFP_S_LINK_UP &&
+			    qsfp->sm_dev_state == SFP_DEV_UP)
+				sfp_sm_link_down(qsfp);
+			/* If the module is still present, and we've
+			 * powered it up, power it back down.
+			 */
+			if (qsfp->sm_mod_state == SFP_MOD_PRESENT &&
+			    qsfp->sm_state >= SFP_S_WPOWER)
+				qsfp_sm_power_down(qsfp);
+			qsfp_sm_next(qsfp, SFP_S_DOWN, 0);
+		}
+		return;
+	}
+
+	/* The main state machine - only entered when the upstream is up
+	 * and the module is present.
+	 */
+	switch (qsfp->sm_state) {
+	case SFP_S_ERROR:
+		break;
+
+	case SFP_S_DOWN:
+		if (qsfp_sm_power_up(qsfp))
+			break;
+		fallthrough;
+	case SFP_S_WPOWER:
+		if (qsfp->sm_state != SFP_S_WPOWER || event == SFP_E_TIMEOUT)
+			if (sfp_sm_tx_enable(qsfp))
+				break;
+		fallthrough;
+	case SFP_S_WTXEN:
+		if (qsfp->sm_state != SFP_S_WTXEN || event == SFP_E_TIMEOUT)
+			sfp_sm_init(qsfp);
+		break;
+
+	case SFP_S_INIT:
+		if (event == SFP_E_TIMEOUT && qsfp->state & SFP_F_TX_FAULT)
+			sfp_sm_fault(qsfp, true);
+		else if (event == SFP_E_TIMEOUT || event == SFP_E_TX_CLEAR)
+			sfp_sm_link_check_los(qsfp);
+		break;
+
+	case SFP_S_WAIT_LOS:
+		if (event == SFP_E_TX_FAULT)
+			sfp_sm_fault(qsfp, true);
+		else if (sfp_los_event_inactive(qsfp, event))
+			sfp_sm_link_up(qsfp);
+		break;
+
+	case SFP_S_LINK_UP:
+		if (event == SFP_E_TX_FAULT) {
+			sfp_sm_link_down(qsfp);
+			sfp_sm_fault(qsfp, true);
+		} else if (sfp_los_event_active(qsfp, event)) {
+			sfp_sm_link_down(qsfp);
+			qsfp_sm_next(qsfp, SFP_S_WAIT_LOS, 0);
+		}
+		break;
+
+	case SFP_S_TX_FAULT:
+		if (event == SFP_E_TIMEOUT) {
+			qsfp_sm_next(qsfp, SFP_S_REINIT, T_INIT_JIFFIES);
+		}
+		break;
+
+	case SFP_S_REINIT:
+		if (event == SFP_E_TIMEOUT && qsfp->state & SFP_F_TX_FAULT) {
+			sfp_sm_fault(qsfp, false);
+		} else if (event == SFP_E_TIMEOUT || event == SFP_E_TX_CLEAR) {
+			dev_info(qsfp->dev, "module transmit fault recovered\n");
+			sfp_sm_link_check_los(qsfp);
+		}
+		break;
+
+	case SFP_S_TX_DISABLE:
+		break;
+	}
+
+	if (qsfp->current_tx_disable != qsfp->request_tx_disable &&
+	    qsfp->id.ext.sff8436.options[3] & SFF8X36_OPTIONS195_TX_DISABLE) {
+		qsfp->current_tx_disable = qsfp->request_tx_disable;
+
+		if (qsfp_set_tx_disable(qsfp, qsfp->current_tx_disable) < 0)
+			qsfp_sm_next(qsfp, SFP_S_ERROR, 0);
+	}
+}
+
+static void qsfp_sm_event(struct qsfp *qsfp, unsigned int event)
+{
+	mutex_lock(&qsfp->sm_mutex);
+
+	dev_dbg(qsfp->dev, "SM: enter %s:%s:%s event %s\n",
+		mod_state_to_str(qsfp->sm_mod_state),
+		dev_state_to_str(qsfp->sm_dev_state),
+		sm_state_to_str(qsfp->sm_state),
+		event_to_str(event));
+
+	qsfp_sm_device(qsfp, event);
+	qsfp_sm_module(qsfp, event);
+	qsfp_sm_main(qsfp, event);
+
+	dev_dbg(qsfp->dev, "SM: exit %s:%s:%s\n",
+		mod_state_to_str(qsfp->sm_mod_state),
+		dev_state_to_str(qsfp->sm_dev_state),
+		sm_state_to_str(qsfp->sm_state));
+
+	mutex_unlock(&qsfp->sm_mutex);
+}
+
+static void sfp_attach(struct qsfp *qsfp)
+{
+	qsfp_sm_event(qsfp, SFP_E_ATTACH);
+}
+
+static void sfp_detach(struct qsfp *qsfp)
+{
+	qsfp_sm_event(qsfp, SFP_E_DETACH);
+}
+
+static void sfp_start(struct qsfp *qsfp)
+{
+	qsfp_sm_event(qsfp, SFP_E_DEV_UP);
+}
+
+static void sfp_stop(struct qsfp *qsfp)
+{
+	qsfp_sm_event(qsfp, SFP_E_DEV_DOWN);
+}
+
+#if 0
+static int sfp_module_info(struct qsfp *sfp, struct ethtool_modinfo *modinfo)
+{
+	/* locking... and check module is present */
+
+	if (sfp->id.ext.sff8472_compliance &&
+	    !(sfp->id.ext.diagmon & SFP_DIAGMON_ADDRMODE)) {
+		modinfo->type = ETH_MODULE_SFF_8472;
+		modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN;
+	} else {
+		modinfo->type = ETH_MODULE_SFF_8079;
+		modinfo->eeprom_len = ETH_MODULE_SFF_8079_LEN;
+	}
+	return 0;
+}
+
+static int sfp_module_eeprom(struct qsfp *sfp, struct ethtool_eeprom *ee,
+			     u8 *data)
+{
+	unsigned int first, last, len;
+	int ret;
+
+	if (ee->len == 0)
+		return -EINVAL;
+
+	first = ee->offset;
+	last = ee->offset + ee->len;
+	if (first < ETH_MODULE_SFF_8079_LEN) {
+		len = min_t(unsigned int, last, ETH_MODULE_SFF_8079_LEN);
+		len -= first;
+
+		ret = qsfp_read(sfp, false, first, data, len);
+		if (ret < 0)
+			return ret;
+
+		first += len;
+		data += len;
+	}
+	if (first < ETH_MODULE_SFF_8472_LEN && last > ETH_MODULE_SFF_8079_LEN) {
+		len = min_t(unsigned int, last, ETH_MODULE_SFF_8472_LEN);
+		len -= first;
+		first -= ETH_MODULE_SFF_8079_LEN;
+
+		ret = qsfp_read(sfp, true, first, data, len);
+		if (ret < 0)
+			return ret;
+	}
+	return 0;
+}
+
+static const struct sfp_socket_ops sfp_module_ops = {
+	.attach = sfp_attach,
+	.detach = sfp_detach,
+	.start = sfp_start,
+	.stop = sfp_stop,
+	.module_info = sfp_module_info,
+	.module_eeprom = sfp_module_eeprom,
+};
+#endif
+
+static void sfp_timeout(struct work_struct *work)
+{
+	struct qsfp *sfp = container_of(work, struct qsfp, timeout.work);
+
+	rtnl_lock();
+	qsfp_sm_event(sfp, SFP_E_TIMEOUT);
+	rtnl_unlock();
+}
+
+static void qsfp_check_modprs(struct qsfp *qsfp)
+{
+	unsigned int v, state = 0;
+
+	v = gpiod_get_value_cansleep(qsfp->gpio[GPIO_MODPRS]);
+	if (v)
+		state |= SFP_F_PRESENT;
+
+	if (state != qsfp->state) {
+		rtnl_lock();
+		qsfp_sm_event(qsfp, state & SFP_F_PRESENT ?
+			     SFP_E_INSERT : SFP_E_REMOVE);
+		rtnl_unlock();
+		qsfp->state = state;
+	}
+}
+
+static void qsfp_check_intl(struct qsfp *qsfp, bool poll)
+{
+	u8 irqs[19];
+	int ret;
+
+	// Read interrupt flags
+	ret = qsfp_read(qsfp, SFF8X36_IRQ_FLAGS, irqs, sizeof(irqs));
+	if (ret < 0)
+		return;
+
+	if (!memcmp(qsfp->module_irq_flags, irqs, sizeof(irqs)))
+		return;
+
+	dev_info(qsfp->dev, "irqs: %*ph\n", (int)sizeof(irqs), irqs);
+
+	memcpy(qsfp->module_irq_flags, irqs, sizeof(irqs));
+}
+
+static irqreturn_t qsfp_irq_modprs(int irq, void *data)
+{
+	struct qsfp *qsfp = data;
+
+	mutex_lock(&qsfp->st_mutex);
+	qsfp_check_modprs(qsfp);
+	mutex_unlock(&qsfp->st_mutex);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t qsfp_irq_intl(int irq, void *data)
+{
+	struct qsfp *qsfp = data;
+
+	mutex_lock(&qsfp->st_mutex);
+	qsfp_check_intl(qsfp, false);
+	mutex_unlock(&qsfp->st_mutex);
+
+	return IRQ_HANDLED;
+}
+
+static void qsfp_poll(struct work_struct *work)
+{
+	struct qsfp *qsfp = container_of(work, struct qsfp, poll.work);
+	bool need_poll = false;
+
+	mutex_lock(&qsfp->st_mutex);
+	if (!qsfp->gpio_irq[GPIO_MODPRS]) {
+		qsfp_check_modprs(qsfp);
+		need_poll = true;
+	}
+	if (!qsfp->gpio_irq[GPIO_INTL] && qsfp->sm_mod_state > SFP_MOD_PROBE) {
+		qsfp_check_intl(qsfp, true);
+		need_poll = true;
+	}
+	mutex_unlock(&qsfp->st_mutex);
+
+	if (need_poll)
+		mod_delayed_work(system_wq, &qsfp->poll, poll_jiffies);
+}
+
+static struct qsfp *sfp_alloc(struct device *dev)
+{
+	struct qsfp *qsfp;
+
+	qsfp = kzalloc(sizeof(*qsfp), GFP_KERNEL);
+	if (!qsfp)
+		return ERR_PTR(-ENOMEM);
+
+	qsfp->dev = dev;
+
+	mutex_init(&qsfp->sm_mutex);
+	mutex_init(&qsfp->st_mutex);
+	INIT_DELAYED_WORK(&qsfp->poll, qsfp_poll);
+	INIT_DELAYED_WORK(&qsfp->timeout, sfp_timeout);
+
+	return qsfp;
+}
+
+static void sfp_cleanup(void *data)
+{
+	struct qsfp *qsfp = data;
+
+	cancel_delayed_work_sync(&qsfp->poll);
+	cancel_delayed_work_sync(&qsfp->timeout);
+	if (qsfp->i2c_mii) {
+		mdiobus_unregister(qsfp->i2c_mii);
+		mdiobus_free(qsfp->i2c_mii);
+	}
+	if (qsfp->i2c)
+		i2c_put_adapter(qsfp->i2c);
+	kfree(qsfp);
+}
+
+static int qsfp_probe(struct platform_device *pdev)
+{
+	struct i2c_adapter *i2c;
+	struct qsfp *qsfp;
+	int err, i;
+
+	qsfp = sfp_alloc(&pdev->dev);
+	if (IS_ERR(qsfp))
+		return PTR_ERR(qsfp);
+
+	platform_set_drvdata(pdev, qsfp);
+
+	err = devm_add_action(qsfp->dev, sfp_cleanup, qsfp);
+	if (err < 0)
+		return err;
+
+	if (pdev->dev.of_node) {
+		struct device_node *node = pdev->dev.of_node;
+		const struct of_device_id *id;
+		struct device_node *np;
+
+		id = of_match_node(qsfp_of_match, node);
+		if (WARN_ON(!id))
+			return -EINVAL;
+
+		np = of_parse_phandle(node, "i2c-bus", 0);
+		if (!np) {
+			dev_err(qsfp->dev, "missing 'i2c-bus' property\n");
+			return -ENODEV;
+		}
+
+		i2c = of_find_i2c_adapter_by_node(np);
+		of_node_put(np);
+	} else if (has_acpi_companion(&pdev->dev)) {
+		struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
+		struct fwnode_handle *fw = acpi_fwnode_handle(adev);
+		struct fwnode_reference_args args;
+		struct acpi_handle *acpi_handle;
+		int ret;
+
+		ret = acpi_node_get_property_reference(fw, "i2c-bus", 0, &args);
+		if (ret || !is_acpi_device_node(args.fwnode)) {
+			dev_err(&pdev->dev, "missing 'i2c-bus' property\n");
+			return -ENODEV;
+		}
+
+		acpi_handle = ACPI_HANDLE_FWNODE(args.fwnode);
+		i2c = i2c_acpi_find_adapter_by_handle(acpi_handle);
+	} else {
+		return -EINVAL;
+	}
+
+	if (!i2c)
+		return -EPROBE_DEFER;
+
+	err = qsfp_i2c_configure(qsfp, i2c);
+	if (err < 0) {
+		i2c_put_adapter(i2c);
+		return err;
+	}
+
+	for (i = 0; i < GPIO_MAX; i++) {
+		qsfp->gpio[i] = devm_gpiod_get_optional(qsfp->dev,
+				   gpio_of_names[i], gpio_flags[i]);
+		if (IS_ERR(qsfp->gpio[i]))
+			return PTR_ERR(qsfp->gpio[i]);
+	}
+
+	device_property_read_u32(&pdev->dev, "maximum-power-milliwatt",
+				 &qsfp->max_power_mW);
+	if (!qsfp->max_power_mW)
+		qsfp->max_power_mW = 1500;
+
+	dev_info(qsfp->dev, "Host maximum power %u.%uW\n",
+		 qsfp->max_power_mW / 1000, (qsfp->max_power_mW / 100) % 10);
+
+	for (i = 0; i < GPIO_MAX; i++) {
+		unsigned long irq_flags;
+		irq_handler_t irq_handler;
+
+		if (gpio_flags[i] != GPIOD_IN || !qsfp->gpio[i])
+			continue;
+
+		qsfp->gpio_irq[i] = gpiod_to_irq(qsfp->gpio[i]);
+		if (!qsfp->gpio_irq[i]) {
+			qsfp->need_poll = true;
+			continue;
+		}
+
+		irq_flags = IRQF_ONESHOT;
+		if (i == GPIO_MODPRS) {
+			irq_flags |= IRQF_TRIGGER_RISING |
+				     IRQF_TRIGGER_FALLING;
+			irq_handler = qsfp_irq_modprs;
+		} else {
+			irq_handler = qsfp_irq_intl;
+		}
+
+		err = devm_request_threaded_irq(qsfp->dev, qsfp->gpio_irq[i],
+						NULL, irq_handler, irq_flags,
+						dev_name(qsfp->dev), qsfp);
+		if (err) {
+			qsfp->gpio_irq[i] = 0;
+			qsfp->need_poll = true;
+		}
+	}
+
+	if (qsfp->need_poll)
+		mod_delayed_work(system_wq, &qsfp->poll, poll_jiffies);
+
+	// Get the initial state
+	mutex_lock(&qsfp->st_mutex);
+	qsfp_check_modprs(qsfp);
+	mutex_unlock(&qsfp->st_mutex);
+
+//	qsfp->sfp_bus = sfp_register_socket(qsfp->dev, qsfp, &sfp_module_ops);
+//	if (!qsfp->sfp_bus)
+//		return -ENOMEM;
+
+	rtnl_lock();
+	sfp_attach(qsfp);
+	sfp_start(qsfp);
+	rtnl_unlock();
+
+	return 0;
+}
+
+static int qsfp_remove(struct platform_device *pdev)
+{
+	struct qsfp *qsfp = platform_get_drvdata(pdev);
+
+//	sfp_unregister_socket(sfp->sfp_bus);
+	rtnl_lock();
+	sfp_stop(qsfp);
+	sfp_detach(qsfp);
+	qsfp_sm_event(qsfp, SFP_E_REMOVE);
+	rtnl_unlock();
+
+	return 0;
+}
+
+static void qsfp_shutdown(struct platform_device *pdev)
+{
+	struct qsfp *qsfp = platform_get_drvdata(pdev);
+	int i;
+
+	for (i = 0; i < GPIO_MAX; i++) {
+		if (!qsfp->gpio_irq[i])
+			continue;
+
+		devm_free_irq(qsfp->dev, qsfp->gpio_irq[i], qsfp);
+	}
+
+	cancel_delayed_work_sync(&qsfp->poll);
+	cancel_delayed_work_sync(&qsfp->timeout);
+}
+
+static struct platform_driver qsfp_driver = {
+	.probe = qsfp_probe,
+	.remove = qsfp_remove,
+	.shutdown = qsfp_shutdown,
+	.driver = {
+		.name = "qsfp",
+		.of_match_table = qsfp_of_match,
+	},
+};
+
+static int qsfp_init(void)
+{
+	poll_jiffies = msecs_to_jiffies(100);
+
+	return platform_driver_register(&qsfp_driver);
+}
+module_init(qsfp_init);
+
+static void qsfp_exit(void)
+{
+	platform_driver_unregister(&qsfp_driver);
+}
+module_exit(qsfp_exit);
+
+MODULE_ALIAS("platform:qsfp");
+MODULE_AUTHOR("Russell King");
+MODULE_LICENSE("GPL v2");
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-bus.c b/drivers/net/phy/sfp-bus.c
index db39dec7f247..2f44fc51848f 100644
--- a/drivers/net/phy/sfp-bus.c
+++ b/drivers/net/phy/sfp-bus.c
@@ -355,7 +355,7 @@ EXPORT_SYMBOL_GPL(sfp_parse_support);
  * modes mask.
  */
 phy_interface_t sfp_select_interface(struct sfp_bus *bus,
-				     unsigned long *link_modes)
+				     const unsigned long *link_modes)
 {
 	if (phylink_test(link_modes, 25000baseCR_Full) ||
 	    phylink_test(link_modes, 25000baseKR_Full) ||
@@ -373,7 +373,8 @@ phy_interface_t sfp_select_interface(struct sfp_bus *bus,
 	if (phylink_test(link_modes, 5000baseT_Full))
 		return PHY_INTERFACE_MODE_5GBASER;
 
-	if (phylink_test(link_modes, 2500baseX_Full))
+	if (phylink_test(link_modes, 2500baseX_Full) ||
+	    phylink_test(link_modes, 2500baseT_Full))
 		return PHY_INTERFACE_MODE_2500BASEX;
 
 	if (phylink_test(link_modes, 1000baseT_Half) ||
diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c
index f75c9eb3958e..ddf671621796 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);
@@ -1663,6 +1666,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)
 {
@@ -2164,6 +2275,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 */
@@ -2230,9 +2446,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);
@@ -2293,14 +2509,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)) {
@@ -2334,8 +2545,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;
 
@@ -2585,10 +2799,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)
@@ -2607,8 +2821,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/drivers/pci/controller/mobiveil/pcie-layerscape-gen4.c b/drivers/pci/controller/mobiveil/pcie-layerscape-gen4.c
index d7b7350f02dd..7348a4cd0901 100644
--- a/drivers/pci/controller/mobiveil/pcie-layerscape-gen4.c
+++ b/drivers/pci/controller/mobiveil/pcie-layerscape-gen4.c
@@ -22,8 +22,13 @@
 
 #include "pcie-mobiveil.h"
 
+#define REV_1_0				(0x10)
+
 /* LUT and PF control registers */
 #define PCIE_LUT_OFF			0x80000
+#define PCIE_LUT_GCR			0x28
+#define PCIE_LUT_GCR_RRE		0
+
 #define PCIE_PF_OFF			0xc0000
 #define PCIE_PF_INT_STAT		0x18
 #define PF_INT_STAT_PABRST		BIT(31)
@@ -40,8 +45,20 @@ struct ls_g4_pcie {
 	struct mobiveil_pcie pci;
 	struct delayed_work dwork;
 	int irq;
+	u8 rev;
 };
 
+static inline u32 ls_g4_pcie_lut_readl(struct ls_g4_pcie *pcie, u32 off)
+{
+	return ioread32(pcie->pci.csr_axi_slave_base + PCIE_LUT_OFF + off);
+}
+
+static inline void ls_g4_pcie_lut_writel(struct ls_g4_pcie *pcie,
+					 u32 off, u32 val)
+{
+	iowrite32(val, pcie->pci.csr_axi_slave_base + PCIE_LUT_OFF + off);
+}
+
 static inline u32 ls_g4_pcie_pf_readl(struct ls_g4_pcie *pcie, u32 off)
 {
 	return ioread32(pcie->pci.csr_axi_slave_base + PCIE_PF_OFF + off);
@@ -53,6 +70,41 @@ static inline void ls_g4_pcie_pf_writel(struct ls_g4_pcie *pcie,
 	iowrite32(val, pcie->pci.csr_axi_slave_base + PCIE_PF_OFF + off);
 }
 
+static bool ls_g4_pcie_is_bridge(struct ls_g4_pcie *pcie)
+{
+	struct mobiveil_pcie *mv_pci = &pcie->pci;
+	u32 header_type;
+
+	header_type = mobiveil_csr_readb(mv_pci, PCI_HEADER_TYPE);
+	header_type &= 0x7f;
+
+	return header_type == PCI_HEADER_TYPE_BRIDGE;
+}
+
+static void workaround_A011451(struct ls_g4_pcie *pcie)
+{
+	struct mobiveil_pcie *mv_pci = &pcie->pci;
+	u32 val;
+
+	/* Set ACK latency timeout */
+	val = mobiveil_csr_readl(mv_pci, GPEX_ACK_REPLAY_TO);
+	val &= ~(ACK_LAT_TO_VAL_MASK << ACK_LAT_TO_VAL_SHIFT);
+	val |= (4 << ACK_LAT_TO_VAL_SHIFT);
+	mobiveil_csr_writel(mv_pci, val, GPEX_ACK_REPLAY_TO);
+}
+
+static int ls_g4_pcie_host_init(struct mobiveil_pcie *pci)
+{
+	struct ls_g4_pcie *pcie = to_ls_g4_pcie(pci);
+
+	pcie->rev = mobiveil_csr_readb(pci, PCI_REVISION_ID);
+
+	if (pcie->rev == REV_1_0)
+		workaround_A011451(pcie);
+
+	return 0;
+}
+
 static int ls_g4_pcie_link_up(struct mobiveil_pcie *pci)
 {
 	struct ls_g4_pcie *pcie = to_ls_g4_pcie(pci);
@@ -184,18 +236,40 @@ static void ls_g4_pcie_reset(struct work_struct *work)
 	ctrl &= ~PCI_BRIDGE_CTL_BUS_RESET;
 	mobiveil_csr_writew(mv_pci, ctrl, PCI_BRIDGE_CONTROL);
 
-	if (!ls_g4_pcie_reinit_hw(pcie))
+	if (ls_g4_pcie_reinit_hw(pcie))
 		return;
 
 	ls_g4_pcie_enable_interrupt(pcie);
 }
 
+static int ls_g4_pcie_read_other_conf(struct pci_bus *bus, unsigned int devfn,
+				   int where, int size, u32 *val)
+{
+	struct mobiveil_pcie *pci = bus->sysdata;
+	struct ls_g4_pcie *pcie = to_ls_g4_pcie(pci);
+	int ret;
+
+	if (pcie->rev == REV_1_0)
+		ls_g4_pcie_lut_writel(pcie, PCIE_LUT_GCR,
+				      0 << PCIE_LUT_GCR_RRE);
+
+	ret = pci_generic_config_read(bus, devfn, where, size, val);
+
+	if (pcie->rev == REV_1_0)
+		ls_g4_pcie_lut_writel(pcie, PCIE_LUT_GCR,
+				      1 << PCIE_LUT_GCR_RRE);
+
+	return ret;
+}
+
 static struct mobiveil_rp_ops ls_g4_pcie_rp_ops = {
 	.interrupt_init = ls_g4_pcie_interrupt_init,
+	.read_other_conf = ls_g4_pcie_read_other_conf,
 };
 
 static const struct mobiveil_pab_ops ls_g4_pcie_pab_ops = {
 	.link_up = ls_g4_pcie_link_up,
+	.host_init = ls_g4_pcie_host_init,
 };
 
 static int __init ls_g4_pcie_probe(struct platform_device *pdev)
@@ -234,6 +308,9 @@ static int __init ls_g4_pcie_probe(struct platform_device *pdev)
 		return  ret;
 	}
 
+	if (!ls_g4_pcie_is_bridge(pcie))
+		return -ENODEV;
+
 	ls_g4_pcie_enable_interrupt(pcie);
 
 	return 0;
diff --git a/drivers/pci/controller/mobiveil/pcie-mobiveil-host.c b/drivers/pci/controller/mobiveil/pcie-mobiveil-host.c
index 32951f7d6d6d..97b766fd0638 100644
--- a/drivers/pci/controller/mobiveil/pcie-mobiveil-host.c
+++ b/drivers/pci/controller/mobiveil/pcie-mobiveil-host.c
@@ -73,9 +73,21 @@ static void __iomem *mobiveil_pcie_map_bus(struct pci_bus *bus,
 	return rp->config_axi_slave_base + where;
 }
 
+static int mobiveil_pcie_config_read(struct pci_bus *bus, unsigned int devfn,
+				     int where, int size, u32 *val)
+{
+	struct mobiveil_pcie *pcie = bus->sysdata;
+	struct mobiveil_root_port *rp = &pcie->rp;
+
+	if (bus->number > to_pci_host_bridge(bus->bridge)->busnr &&
+	    rp->ops->read_other_conf)
+		return rp->ops->read_other_conf(bus, devfn, where, size, val);
+
+	return pci_generic_config_read(bus, devfn, where, size, val);
+}
 static struct pci_ops mobiveil_pcie_ops = {
 	.map_bus = mobiveil_pcie_map_bus,
-	.read = pci_generic_config_read,
+	.read = mobiveil_pcie_config_read,
 	.write = pci_generic_config_write,
 };
 
@@ -295,6 +307,10 @@ int mobiveil_host_init(struct mobiveil_pcie *pcie, bool reinit)
 	value |= PCI_CLASS_BRIDGE_PCI_NORMAL << 8;
 	mobiveil_csr_writel(pcie, value, PAB_INTP_AXI_PIO_CLASS);
 
+	/* Platform specific host init */
+	if (pcie->ops->host_init)
+		return pcie->ops->host_init(pcie);
+
 	return 0;
 }
 
diff --git a/drivers/pci/controller/mobiveil/pcie-mobiveil.h b/drivers/pci/controller/mobiveil/pcie-mobiveil.h
index 6082b8afbc31..e03cc8e1399a 100644
--- a/drivers/pci/controller/mobiveil/pcie-mobiveil.h
+++ b/drivers/pci/controller/mobiveil/pcie-mobiveil.h
@@ -86,6 +86,10 @@
 #define PAB_AXI_AMAP_PEX_WIN_H(win)	PAB_REG_ADDR(0x0bac, win)
 #define PAB_INTP_AXI_PIO_CLASS		0x474
 
+#define GPEX_ACK_REPLAY_TO		0x438
+#define  ACK_LAT_TO_VAL_MASK		0x1fff
+#define  ACK_LAT_TO_VAL_SHIFT		0
+
 #define PAB_PEX_AMAP_CTRL(win)		PAB_REG_ADDR(0x4ba0, win)
 #define  AMAP_CTRL_EN_SHIFT		0
 #define  AMAP_CTRL_TYPE_SHIFT		1
@@ -146,6 +150,8 @@ struct mobiveil_pcie;
 
 struct mobiveil_rp_ops {
 	int (*interrupt_init)(struct mobiveil_pcie *pcie);
+	int (*read_other_conf)(struct pci_bus *bus, unsigned int devfn,
+			       int where, int size, u32 *val);
 };
 
 struct mobiveil_root_port {
@@ -161,6 +167,7 @@ struct mobiveil_root_port {
 
 struct mobiveil_pab_ops {
 	int (*link_up)(struct mobiveil_pcie *pcie);
+	int (*host_init)(struct mobiveil_pcie *pcie);
 };
 
 struct mobiveil_pcie {
diff --git a/include/linux/mdio.h b/include/linux/mdio.h
index 68f8d2e970d4..04a136df966d 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, 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/phy.h b/include/linux/phy.h
index 3f68b8239bb1..316f821e2360 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -699,6 +699,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);
@@ -714,6 +723,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;
@@ -805,6 +817,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.
@@ -943,6 +973,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
@@ -961,6 +999,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,
@@ -1776,6 +1817,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 9a57deefcb07..e7ff7d99cb74 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
@@ -138,23 +155,32 @@ enum phylink_op_type {
  * @poll_fixed_state: if true, starts link_poll,
  *		      if MAC link is at %MLO_AN_FIXED mode.
  * @mac_managed_pm: if true, indicate the MAC driver is responsible for PHY PM.
- * @ovr_an_inband: if true, override PCS to MLO_AN_INBAND
+ * @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;
 	enum phylink_op_type type;
 	bool poll_fixed_state;
 	bool mac_managed_pm;
-	bool ovr_an_inband;
+	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);
@@ -168,6 +194,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.
  */
@@ -188,6 +216,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. */
@@ -382,6 +412,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;
@@ -409,6 +461,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)
@@ -422,6 +475,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,
@@ -457,6 +512,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/linux/sfp.h b/include/linux/sfp.h
index 9346cd44814d..2309511c6e64 100644
--- a/include/linux/sfp.h
+++ b/include/linux/sfp.h
@@ -275,6 +275,101 @@ struct sfp_diag {
 	__be16 cal_v_offset;
 } __packed;
 
+struct qsfp_sff8x36_id_stat {
+	u8 identifier;
+	u8 rev_compliance;
+	u8 status;
+} __packed;
+
+struct qsfp_sff8x36_id_base {
+	u8 identifier;
+	u8 ext_identifier;
+	u8 connector;
+	u8 compliance[8];
+	u8 encoding;
+	u8 br_nominal;
+	u8 ext_ratesel_compliance;
+	u8 length[5];
+	u8 device_tech;
+	char vendor_name[16];
+	u8 ext_module;
+	u8 oui[3];
+	char vendor_pn[16];
+	char vendor_rev[2];
+	union {
+		__be16 wavelength;
+		u8 copper_atten[2];
+	} __packed;
+	__be16 wavelength_tolerance;
+	u8 max_case_temp;
+	u8 cc_base;
+} __packed;
+
+struct qsfp_sff8436_id_ext {
+	u8 options[4];
+	char vendor_sn[16];
+	u8 datecode[8];
+	u8 diagmon;
+	u8 enh_options;
+	u8 reserved;
+	u8 cc_ext;
+} __packed;
+
+struct qsfp_sff8636_id_ext {
+	u8 link_codes;
+	u8 options[3];
+	char vendor_sn[16];
+	u8 datecode[8];
+	u8 diagmon;
+	u8 enh_options;
+	u8 baud_rate_nominal;
+	u8 cc_ext;
+} __packed;
+
+struct qsfp_sff8x36_id {
+	struct qsfp_sff8x36_id_base base;
+	union {
+		struct qsfp_sff8436_id_ext sff8436;
+		struct qsfp_sff8636_id_ext sff8636;
+	} __packed ext;
+} __packed;
+
+enum {
+#define SFF8X36_ADDR(page, addr)	((page) << 8 | (addr))
+	SFF8X36_STAT			= SFF8X36_ADDR(0, 2),
+	SFF8X36_STAT_FLAT_MEM		= BIT(2),
+	SFF8X36_STAT_INTL		= BIT(1),
+	SFF8X36_STAT_DATA_NOT_READY	= BIT(0),
+	SFF8X36_IRQ_FLAGS		= SFF8X36_ADDR(0, 3),
+	SFF8X36_TEMPERATURE		= SFF8X36_ADDR(0, 22),
+	SFF8X36_SUPPLY_VOLTAGE		= SFF8X36_ADDR(0, 26),
+	SFF8X36_RX_POWER		= SFF8X36_ADDR(0, 34),
+	SFF8X36_TX_BIAS			= SFF8X36_ADDR(0, 42),
+	SFF8X36_TX_POWER		= SFF8X36_ADDR(0, 50),
+	SFF8X36_TX_DISABLE		= SFF8X36_ADDR(0, 86),
+	SFF8X36_TX_DISABLE_TX4		= BIT(3),
+	SFF8X36_TX_DISABLE_TX3		= BIT(2),
+	SFF8X36_TX_DISABLE_TX2		= BIT(1),
+	SFF8X36_TX_DISABLE_TX1		= BIT(0),
+	SFF8X36_RX_RATE_SELECT		= SFF8X36_ADDR(0, 87),
+	SFF8X36_TX_RATE_SELECT		= SFF8X36_ADDR(0, 88),
+	SFF8X36_CTRL_93			= SFF8X36_ADDR(0, 93),
+	SFF8636_CTRL_93_SW_RESET	= BIT(7),
+	SFF8636_CTRL_93_POWER_CLS8	= BIT(3),
+	SFF8636_CTRL_93_POWER_CLS5_7	= BIT(2),
+	SFF8X36_CTRL_93_POWER_SET	= BIT(1),
+	SFF8X36_CTRL_93_POWER_ORIDE	= BIT(0),
+	SFF8X36_CLS8_MAX_POWER		= SFF8X36_ADDR(0, 107),
+	SFF8X36_ID			= SFF8X36_ADDR(0, 128),
+	SFF8X36_OPTIONS195_TX_DISABLE	= BIT(4),
+	SFF8X36_DIAGMON			= SFF8X36_ADDR(0, 220),
+	SFF8636_DIAGMON_TEMP		= BIT(5),
+	SFF8636_DIAGMON_VCC		= BIT(4),
+	SFF8X36_DIAGMON_RXPWR_AVG	= BIT(3),
+	SFF8636_DIAGMON_TXPWR		= BIT(2),
+	SFF8X36_THRESHOLDS		= SFF8X36_ADDR(3, 128),
+};
+
 /* SFF8024 defined constants */
 enum {
 	SFF8024_ID_UNK			= 0x00,
@@ -299,7 +394,7 @@ enum {
 	SFF8024_ENCODING_PAM4		= 0x08,
 
 	SFF8024_CONNECTOR_UNSPEC	= 0x00,
-	/* codes 01-05 not supportable on SFP, but some modules have single SC */
+	// codes 01-05 not supportable on SFP, but some modules have single SC
 	SFF8024_CONNECTOR_SC		= 0x01,
 	SFF8024_CONNECTOR_FIBERJACK	= 0x06,
 	SFF8024_CONNECTOR_LC		= 0x07,
@@ -330,6 +425,20 @@ enum {
 	SFF8024_ECC_2_5GBASE_T		= 0x1e,
 };
 
+enum {
+	// rev_compliance
+	// SFF8X36_REV_UNSPEC can be used up to SFF-8636 rev 2.5 exclusive.
+	SFF8X36_REV_UNSPEC		= 0,
+	SFF8X36_REV_8436_4_8		= 1,
+	SFF8X36_REV_8436_4_8P		= 2,
+	SFF8X36_REV_8636_1_3		= 3,
+	SFF8X36_REV_8636_1_4		= 4,
+	SFF8X36_REV_8636_1_5		= 5,
+	SFF8X36_REV_8636_2_0		= 6,
+	SFF8X36_REV_8636_2_5		= 7,
+	SFF8X36_REV_8636_2_8		= 8,
+};
+
 /* SFP EEPROM registers */
 enum {
 	SFP_PHYS_ID			= 0,
@@ -547,14 +656,14 @@ struct sfp_upstream_ops {
 	void (*disconnect_phy)(void *priv);
 };
 
-#if IS_ENABLED(CONFIG_SFP)
+#if IS_ENABLED(CONFIG_SFP_BUS)
 int sfp_parse_port(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
 		   unsigned long *support);
 bool sfp_may_have_phy(struct sfp_bus *bus, const struct sfp_eeprom_id *id);
 void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
 		       unsigned long *support, unsigned long *interfaces);
 phy_interface_t sfp_select_interface(struct sfp_bus *bus,
-				     unsigned long *link_modes);
+				     const unsigned long *link_modes);
 
 int sfp_get_module_info(struct sfp_bus *bus, struct ethtool_modinfo *modinfo);
 int sfp_get_module_eeprom(struct sfp_bus *bus, struct ethtool_eeprom *ee,
@@ -592,7 +701,7 @@ static inline void sfp_parse_support(struct sfp_bus *bus,
 }
 
 static inline phy_interface_t sfp_select_interface(struct sfp_bus *bus,
-						   unsigned long *link_modes)
+						const unsigned long *link_modes)
 {
 	return PHY_INTERFACE_MODE_NA;
 }
diff --git a/include/linux/stmmac.h b/include/linux/stmmac.h
index dfa1828cd756..236d7c2ac2cf 100644
--- a/include/linux/stmmac.h
+++ b/include/linux/stmmac.h
@@ -83,7 +83,7 @@ struct stmmac_priv;
 struct stmmac_mdio_bus_data {
 	unsigned int phy_mask;
 	unsigned int has_xpcs;
-	unsigned int xpcs_an_inband;
+	unsigned int default_an_inband;
 	int *irqs;
 	int probed_phy_irq;
 	bool needs_reset;
diff --git a/include/net/dsa.h b/include/net/dsa.h
index 7c0da9effe4e..7c5440e91cc9 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -24,9 +24,6 @@
 
 struct dsa_8021q_context;
 struct tc_action;
-struct phy_device;
-struct fixed_phy_status;
-struct phylink_link_state;
 
 #define DSA_TAG_PROTO_NONE_VALUE		0
 #define DSA_TAG_PROTO_BRCM_VALUE		1
@@ -327,6 +324,12 @@ struct dsa_port {
 	};
 };
 
+static inline struct dsa_port *
+dsa_phylink_to_port(struct phylink_config *config)
+{
+	return container_of(config, struct dsa_port, pl_config);
+}
+
 /* TODO: ideally DSA ports would have a single dp->link_dp member,
  * and no dst->rtable nor this struct dsa_link would be needed,
  * but this would require some more complex tree walking,
@@ -451,6 +454,11 @@ struct dsa_switch {
 	 */
 	const struct dsa_switch_ops	*ops;
 
+	/*
+	 * Allow a DSA switch driver to override the phylink MAC ops
+	 */
+	const struct phylink_mac_ops	*phylink_mac_ops;
+
 	/*
 	 * User mii_bus and devices for the individual ports.
 	 */
@@ -857,40 +865,11 @@ struct dsa_switch_ops {
 	int	(*phy_write)(struct dsa_switch *ds, int port,
 			     int regnum, u16 val);
 
-	/*
-	 * Link state adjustment (called from libphy)
-	 */
-	void	(*adjust_link)(struct dsa_switch *ds, int port,
-				struct phy_device *phydev);
-	void	(*fixed_link_update)(struct dsa_switch *ds, int port,
-				struct fixed_phy_status *st);
-
 	/*
 	 * PHYLINK integration
 	 */
 	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);
-	int	(*phylink_mac_prepare)(struct dsa_switch *ds, int port,
-				       unsigned int mode,
-				       phy_interface_t interface);
-	void	(*phylink_mac_config)(struct dsa_switch *ds, int port,
-				      unsigned int mode,
-				      const struct phylink_link_state *state);
-	int	(*phylink_mac_finish)(struct dsa_switch *ds, int port,
-				      unsigned int mode,
-				      phy_interface_t interface);
-	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 09d2f5d4b3dd..ceeadb52d1cc 100644
--- a/net/dsa/dsa.c
+++ b/net/dsa/dsa.c
@@ -1505,6 +1505,11 @@ static int dsa_switch_probe(struct dsa_switch *ds)
 	if (!ds->num_ports)
 		return -EINVAL;
 
+	if (!ds->phylink_mac_ops) {
+		dev_err(ds->dev, "DSA switch driver does not provide phylink MAC operations");
+		return -EINVAL;
+	}
+
 	if (np) {
 		err = dsa_switch_parse_of(ds, np);
 		if (err)
diff --git a/net/dsa/port.c b/net/dsa/port.c
index c42dac87671b..a31a5517a12f 100644
--- a/net/dsa/port.c
+++ b/net/dsa/port.c
@@ -1535,131 +1535,6 @@ void dsa_port_set_tag_protocol(struct dsa_port *cpu_dp,
 	cpu_dp->tag_ops = tag_ops;
 }
 
-static struct phy_device *dsa_port_get_phy_device(struct dsa_port *dp)
-{
-	struct device_node *phy_dn;
-	struct phy_device *phydev;
-
-	phy_dn = of_parse_phandle(dp->dn, "phy-handle", 0);
-	if (!phy_dn)
-		return NULL;
-
-	phydev = of_phy_find_device(phy_dn);
-	if (!phydev) {
-		of_node_put(phy_dn);
-		return ERR_PTR(-EPROBE_DEFER);
-	}
-
-	of_node_put(phy_dn);
-	return phydev;
-}
-
-static struct phylink_pcs *
-dsa_port_phylink_mac_select_pcs(struct phylink_config *config,
-				phy_interface_t interface)
-{
-	struct dsa_port *dp = container_of(config, struct dsa_port, pl_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 int dsa_port_phylink_mac_prepare(struct phylink_config *config,
-					unsigned int mode,
-					phy_interface_t interface)
-{
-	struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
-	struct dsa_switch *ds = dp->ds;
-	int err = 0;
-
-	if (ds->ops->phylink_mac_prepare)
-		err = ds->ops->phylink_mac_prepare(ds, dp->index, mode,
-						   interface);
-
-	return err;
-}
-
-static void dsa_port_phylink_mac_config(struct phylink_config *config,
-					unsigned int mode,
-					const struct phylink_link_state *state)
-{
-	struct dsa_port *dp = container_of(config, struct dsa_port, pl_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 int dsa_port_phylink_mac_finish(struct phylink_config *config,
-				       unsigned int mode,
-				       phy_interface_t interface)
-{
-	struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
-	struct dsa_switch *ds = dp->ds;
-	int err = 0;
-
-	if (ds->ops->phylink_mac_finish)
-		err = ds->ops->phylink_mac_finish(ds, dp->index, mode,
-						  interface);
-
-	return err;
-}
-
-static void dsa_port_phylink_mac_link_down(struct phylink_config *config,
-					   unsigned int mode,
-					   phy_interface_t interface)
-{
-	struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
-	struct phy_device *phydev = NULL;
-	struct dsa_switch *ds = dp->ds;
-
-	if (dsa_port_is_user(dp))
-		phydev = dp->user->phydev;
-
-	if (!ds->ops->phylink_mac_link_down) {
-		if (ds->ops->adjust_link && phydev)
-			ds->ops->adjust_link(ds, dp->index, phydev);
-		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 = container_of(config, struct dsa_port, pl_config);
-	struct dsa_switch *ds = dp->ds;
-
-	if (!ds->ops->phylink_mac_link_up) {
-		if (ds->ops->adjust_link && phydev)
-			ds->ops->adjust_link(ds, dp->index, phydev);
-		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_prepare = dsa_port_phylink_mac_prepare,
-	.mac_config = dsa_port_phylink_mac_config,
-	.mac_finish = dsa_port_phylink_mac_finish,
-	.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)
 {
 	struct dsa_switch *ds = dp->ds;
@@ -1685,8 +1560,8 @@ int dsa_port_phylink_create(struct dsa_port *dp)
 		}
 	}
 
-	pl = phylink_create(&dp->pl_config, of_fwnode_handle(dp->dn),
-			    mode, &dsa_port_phylink_mac_ops);
+	pl = phylink_create(&dp->pl_config, of_fwnode_handle(dp->dn), mode,
+			    ds->phylink_mac_ops);
 	if (IS_ERR(pl)) {
 		pr_err("error creating PHYLINK: %ld\n", PTR_ERR(pl));
 		return PTR_ERR(pl);
@@ -1703,78 +1578,6 @@ void dsa_port_phylink_destroy(struct dsa_port *dp)
 	dp->pl = NULL;
 }
 
-static int dsa_shared_port_setup_phy_of(struct dsa_port *dp, bool enable)
-{
-	struct dsa_switch *ds = dp->ds;
-	struct phy_device *phydev;
-	int port = dp->index;
-	int err = 0;
-
-	phydev = dsa_port_get_phy_device(dp);
-	if (!phydev)
-		return 0;
-
-	if (IS_ERR(phydev))
-		return PTR_ERR(phydev);
-
-	if (enable) {
-		err = genphy_resume(phydev);
-		if (err < 0)
-			goto err_put_dev;
-
-		err = genphy_read_status(phydev);
-		if (err < 0)
-			goto err_put_dev;
-	} else {
-		err = genphy_suspend(phydev);
-		if (err < 0)
-			goto err_put_dev;
-	}
-
-	if (ds->ops->adjust_link)
-		ds->ops->adjust_link(ds, port, phydev);
-
-	dev_dbg(ds->dev, "enabled port's phy: %s", phydev_name(phydev));
-
-err_put_dev:
-	put_device(&phydev->mdio.dev);
-	return err;
-}
-
-static int dsa_shared_port_fixed_link_register_of(struct dsa_port *dp)
-{
-	struct device_node *dn = dp->dn;
-	struct dsa_switch *ds = dp->ds;
-	struct phy_device *phydev;
-	int port = dp->index;
-	phy_interface_t mode;
-	int err;
-
-	err = of_phy_register_fixed_link(dn);
-	if (err) {
-		dev_err(ds->dev,
-			"failed to register the fixed PHY of port %d\n",
-			port);
-		return err;
-	}
-
-	phydev = of_phy_find_device(dn);
-
-	err = of_get_phy_mode(dn, &mode);
-	if (err)
-		mode = PHY_INTERFACE_MODE_NA;
-	phydev->interface = mode;
-
-	genphy_read_status(phydev);
-
-	if (ds->ops->adjust_link)
-		ds->ops->adjust_link(ds, port, phydev);
-
-	put_device(&phydev->mdio.dev);
-
-	return 0;
-}
-
 static int dsa_shared_port_phylink_register(struct dsa_port *dp)
 {
 	struct dsa_switch *ds = dp->ds;
@@ -1952,12 +1755,20 @@ static void dsa_shared_port_validate_of(struct dsa_port *dp,
 		dn, dsa_port_is_cpu(dp) ? "CPU" : "DSA", dp->index);
 }
 
+static void dsa_shared_port_link_down(struct dsa_port *dp)
+{
+	struct dsa_switch *ds = dp->ds;
+
+	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);
+}
+
 int dsa_shared_port_link_register_of(struct dsa_port *dp)
 {
 	struct dsa_switch *ds = dp->ds;
 	bool missing_link_description;
 	bool missing_phy_mode;
-	int port = dp->index;
 
 	dsa_shared_port_validate_of(dp, &missing_phy_mode,
 				    &missing_link_description);
@@ -1967,46 +1778,28 @@ int dsa_shared_port_link_register_of(struct dsa_port *dp)
 					dsa_switches_apply_workarounds))
 		return -EINVAL;
 
-	if (!ds->ops->adjust_link) {
-		if (missing_link_description) {
-			dev_warn(ds->dev,
-				 "Skipping phylink registration for %s port %d\n",
-				 dsa_port_is_cpu(dp) ? "CPU" : "DSA", dp->index);
-		} else {
-			if (ds->ops->phylink_mac_link_down)
-				ds->ops->phylink_mac_link_down(ds, port,
-					MLO_AN_FIXED, PHY_INTERFACE_MODE_NA);
+	if (missing_link_description) {
+		dev_warn(ds->dev,
+			 "Skipping phylink registration for %s port %d\n",
+			 dsa_port_is_cpu(dp) ? "CPU" : "DSA", dp->index);
+	} else {
+		dsa_shared_port_link_down(dp);
 
-			return dsa_shared_port_phylink_register(dp);
-		}
-		return 0;
+		return dsa_shared_port_phylink_register(dp);
 	}
 
-	dev_warn(ds->dev,
-		 "Using legacy PHYLIB callbacks. Please migrate to PHYLINK!\n");
-
-	if (of_phy_is_fixed_link(dp->dn))
-		return dsa_shared_port_fixed_link_register_of(dp);
-	else
-		return dsa_shared_port_setup_phy_of(dp, true);
+	return 0;
 }
 
 void dsa_shared_port_link_unregister_of(struct dsa_port *dp)
 {
-	struct dsa_switch *ds = dp->ds;
-
-	if (!ds->ops->adjust_link && dp->pl) {
+	if (dp->pl) {
 		rtnl_lock();
 		phylink_disconnect_phy(dp->pl);
 		rtnl_unlock();
 		dsa_port_phylink_destroy(dp);
 		return;
 	}
-
-	if (of_phy_is_fixed_link(dp->dn))
-		of_phy_deregister_fixed_link(dp->dn);
-	else
-		dsa_shared_port_setup_phy_of(dp, false);
 }
 
 int dsa_port_hsr_join(struct dsa_port *dp, struct net_device *hsr,
diff --git a/net/dsa/user.c b/net/dsa/user.c
index 16d395bb1a1f..00d75336990f 100644
--- a/net/dsa/user.c
+++ b/net/dsa/user.c
@@ -1228,16 +1228,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);
 }
@@ -1248,16 +1253,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);
 }
@@ -2445,7 +2455,7 @@ EXPORT_SYMBOL_GPL(dsa_port_phylink_mac_change);
 static void dsa_user_phylink_fixed_state(struct phylink_config *config,
 					 struct phylink_link_state *state)
 {
-	struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
+	struct dsa_port *dp = dsa_phylink_to_port(config);
 	struct dsa_switch *ds = dp->ds;
 
 	/* No need to check that this operation is valid, the callback would
