# Base git commit: 98f7e32f20d2 # (Linux 6.11) # # Author: Russell King (Oracle) (Sat 27 May 21:09:07 BST 2023) # Committer: Russell King (Oracle) (Fri 18 Oct 10:41:57 BST 2024) # # arm64: dts: add SolidRun CN9130 Clearfog Base dts # # Add support for the CN9130 SOM attached to a Clearfog Base platform. # This is similar to the Armada 388 based Clearfog Base platform. # # Signed-off-by: Russell King (Oracle) # # f4fc4816ff2bb5fd50639ea658d193a9921c8a7d # arch/arm64/boot/dts/marvell/Makefile | 1 + # arch/arm64/boot/dts/marvell/cn9130-cf-base.dts | 14 ++++- # arch/arm64/boot/dts/marvell/cn9130-cf.dtsi | 77 +++++++++++++++++++++++--- # 3 files changed, 80 insertions(+), 12 deletions(-) # # Author: Russell King (Oracle) (Sat 27 May 21:09:07 BST 2023) # Committer: Russell King (Oracle) (Fri 18 Oct 10:41:56 BST 2024) # # arm64: dts: add SolidRun CN9130 SOM dtsi # # Add the dts include file for the SolidRun CN9130 SOM (which is designed # to be attached to another board to make a functional system.) This SOM # is designed to be inter-changeable with the Armada 388 SOM used on # SolidRun's Clearfog Base and Pro platforms. # # Signed-off-by: Russell King (Oracle) # # 18a664c60d3aaa2ba8ada20246f4087ae0272640 # arch/arm64/boot/dts/marvell/cn9130-sr-som.dtsi | 10 +++++++--- # 1 file changed, 7 insertions(+), 3 deletions(-) # # Author: Russell King (Oracle) (Thu 8 Jun 12:41:29 BST 2023) # Committer: Russell King (Oracle) (Fri 18 Oct 10:41:56 BST 2024) # # ARM: dts: Clearfog: use USB3 and SATA labels to reference nodes # # Use the labels created by commit f3d1f7597ec1 ("ARM: dts: armada-38x: # label USB and SATA nodes") # # Signed-off-by: Russell King (Oracle) # # 65cdb0a32bd56e485ae8953eb91779e65b3feffe # arch/arm/boot/dts/marvell/armada-388-clearfog.dts | 14 ++++----- # arch/arm/boot/dts/marvell/armada-388-clearfog.dtsi | 34 ++++++++++------------ # 2 files changed, 20 insertions(+), 28 deletions(-) # # Author: Russell King (Oracle) (Thu 8 Jun 12:48:00 BST 2023) # Committer: Russell King (Oracle) (Fri 18 Oct 10:41:55 BST 2024) # # ARM: dts: Clearfog: use PCIe, SDHCI and USB labels to reference nodes # # Use the labels created by commit a126de75c1b8 ("ARM: dts: armada-38x # add node labels") # # Signed-off-by: Russell King (Oracle) # # 8b9a113a1f92cb44055530557a9b487629b94915 # arch/arm/boot/dts/marvell/armada-388-clearfog.dts | 14 +++-- # arch/arm/boot/dts/marvell/armada-388-clearfog.dtsi | 61 +++++++++++----------- # 2 files changed, 37 insertions(+), 38 deletions(-) # # Author: Russell King (Tue 29 Nov 10:13:48 GMT 2016) # Committer: Russell King (Oracle) (Fri 18 Oct 10:41:54 BST 2024) # # implement slot capabilities (SSPL) # # 7195ff867d0dba065156c8faa8e1e3c1e2e9ede7 # drivers/pci/controller/pci-mvebu.c | 20 ++++++++++++++++++++ # 1 file changed, 20 insertions(+) # # Author: Russell King (Tue 29 Nov 10:13:46 GMT 2016) # Committer: Russell King (Oracle) (Fri 18 Oct 10:41:54 BST 2024) # # mvebu/clearfog pcie updates # # Signed-off-by: Russell King # # 89e837305b38c98c8408a200500ccd690fb6416a # drivers/pci/controller/pci-mvebu.c | 76 ++++++++++++++++++++++++++++++++++++++ # drivers/pci/pci-bridge-emul.c | 2 + # drivers/pci/pcie/aspm.c | 6 +++ # drivers/pci/pcie/portdrv.c | 2 + # 4 files changed, 86 insertions(+) # # Author: Russell King (Tue 2 Feb 13:45:28 GMT 2021) # Committer: Russell King (Oracle) (Fri 18 Oct 10:41:53 BST 2024) # # PCI: pci-bridge-emul: re-arrange register tests # # Re-arrange the tests for which sets of registers are being accessed # so that it is easier to add further regions later. No functional # change. # # Signed-off-by: Russell King # # 9d1dcbc3bb653c218d118cbd55878b62fa9813e6 # drivers/pci/pci-bridge-emul.c | 15 ++++++++++----- # 1 file changed, 10 insertions(+), 5 deletions(-) # # Author: Russell King (Thu 14 Jul 15:31:42 BST 2016) # Committer: Russell King (Oracle) (Fri 18 Oct 10:41:53 BST 2024) # # ARM: dts: armada388-clearfog: use 1000BaseX mode for 88e6176 switch # # Use 1000BaseX mode for the 88e6176 switch, which allows mvneta to # negotiate correctly without needing to be forced. # # Signed-off-by: Russell King # # c5669c45c1658b656dab74903097455fcda855d4 # arch/arm/boot/dts/marvell/armada-388-clearfog.dts | 12 ++---------- # 1 file changed, 2 insertions(+), 10 deletions(-) # # Author: Russell King (Tue 29 Nov 10:15:43 GMT 2016) # Committer: Russell King (Oracle) (Fri 18 Oct 10:41:52 BST 2024) # # ARM: dts: armada388-clearfog: document MPP usage # # Signed-off-by: Russell King # # 959ca92699166fe07596fa2474f7ce52aa68c6e6 # .../boot/dts/marvell/armada-388-clearfog-base.dts | 51 ++++++++++++++++++++++ # arch/arm/boot/dts/marvell/armada-388-clearfog.dts | 50 +++++++++++++++++++++ # 2 files changed, 101 insertions(+) # # Author: Russell King (Tue 29 Nov 10:15:45 GMT 2016) # Committer: Russell King (Oracle) (Fri 18 Oct 10:41:52 BST 2024) # # ARM: dts: armada388-clearfog: emmc on clearfog base # # Signed-off-by: Russell King # # f247588c2085c6830be3ee234ca9d702515a6106 # .../boot/dts/marvell/armada-388-clearfog-base.dts | 1 + # .../marvell/armada-38x-solidrun-microsom-emmc.dtsi | 62 ++++++++++++++++++++++ # 2 files changed, 63 insertions(+) # create mode 100644 arch/arm/boot/dts/marvell/armada-38x-solidrun-microsom-emmc.dtsi # # Author: Russell King (Oracle) (Fri 18 Oct 10:41:50 BST 2024) # Committer: Russell King (Oracle) (Fri 18 Oct 10:41:50 BST 2024) # # Merge branch 'dsa-mv88e6xxx' into clearfog # # Author: Russell King (Oracle) (Fri 18 Oct 10:41:47 BST 2024) # Committer: Russell King (Oracle) (Fri 18 Oct 10:41:47 BST 2024) # # Merge branches 'mvebu-cpuidle' and 'mvneta' into clearfog # # Author: Russell King (Fri 23 Dec 01:09:44 GMT 2016) # Committer: Russell King (Oracle) (Fri 18 Oct 10:41:38 BST 2024) # # net: mvgmac: support different hw versions # # Signed-off-by: Russell King # # 262de2394a9d1f267ff398f45bddc95df7c30419 # drivers/net/ethernet/marvell/mvgmac.c | 118 ++++++++++++++++++++++++++++++---- # drivers/net/ethernet/marvell/mvgmac.h | 9 +++ # drivers/net/ethernet/marvell/mvneta.c | 1 + # 3 files changed, 115 insertions(+), 13 deletions(-) # # Author: Russell King (Thu 22 Dec 16:03:15 GMT 2016) # Committer: Russell King (Oracle) (Fri 18 Oct 10:41:38 BST 2024) # # net: mvneta: split out GMAC # # Split out the code handling the GMAC from the rest of the driver. This # block appears to be shared amongst several revisions of the IP. # # Signed-off-by: Russell King # # 162f11b2b579c21111e6ac970c60300b7b987bf3 # drivers/net/ethernet/marvell/Kconfig | 4 + # drivers/net/ethernet/marvell/Makefile | 1 + # drivers/net/ethernet/marvell/mvgmac.c | 369 ++++++++++++++++++++++++++++++++++ # drivers/net/ethernet/marvell/mvgmac.h | 47 +++++ # drivers/net/ethernet/marvell/mvneta.c | 355 +++----------------------------- # 5 files changed, 450 insertions(+), 326 deletions(-) # create mode 100644 drivers/net/ethernet/marvell/mvgmac.c # create mode 100644 drivers/net/ethernet/marvell/mvgmac.h # # Author: Russell King (Mon 29 Jun 23:16:21 BST 2020) # Committer: Russell King (Oracle) (Fri 18 Oct 10:41:37 BST 2024) # # phy: armada-38x: further augmentation of setup # # Further augmentation of the comphy setup. # # Signed-off-by: Russell King # # 8d59efd053aa1a348172f2c88aa8a3aa71299ff6 # arch/arm/boot/dts/marvell/armada-38x.dtsi | 5 +- # drivers/phy/marvell/phy-armada38x-comphy.c | 147 +++++++++++++++++++++++++---- # 2 files changed, 132 insertions(+), 20 deletions(-) # # Author: Russell King (Oracle) (Thu 17 Aug 21:56:27 BST 2023) # Committer: Russell King (Oracle) (Fri 18 Oct 10:40:37 BST 2024) # # net: dsa: mv88e6xxx: add 6352 family EEE support # # Signed-off-by: Russell King (Oracle) # # 95df51f9752bdca961c1354ad25034f6b4f8b31e # drivers/net/dsa/mv88e6xxx/chip.c | 8 ++++++++ # 1 file changed, 8 insertions(+) # # Author: Russell King (Oracle) (Sat 4 Dec 20:13:11 GMT 2021) # Committer: Russell King (Oracle) (Fri 18 Oct 10:40:37 BST 2024) # # net: phy: generate PHY mdio modalias # # The modalias string provided in the uevent sysfs file does not conform # to the format used in PHY driver modules. One of the reasons is that # udev loading of PHY driver modules has not been an expected use case. # # This patch changes the MODALIAS entry for only PHY devices from: # MODALIAS=of:Nethernet-phyT(null) # to: # MODALIAS=mdio:00000000001000100001010100010011 # # Other MDIO devices (such as DSA) remain as before. # # However, having udev automatically load the module has the advantage # of making use of existing functionality to have the module loaded # before the device is bound to the driver, thus taking advantage of # multithreaded boot systems, potentially decreasing the boot time. # # However, this patch will not solve any issues with the driver module # not being loaded prior to the network device needing to use the PHY. # This is something that is completely out of control of any patch to # change the uevent mechanism. # # Reported-by: Yinbo Zhu # Signed-off-by: Russell King (Oracle) # # f5d49a8d7c9aa62d6232a479541b21bbe96a72f8 # drivers/net/phy/mdio_bus.c | 8 ++++++++ # drivers/net/phy/phy_device.c | 14 ++++++++++++++ # include/linux/mdio.h | 2 ++ # 3 files changed, 24 insertions(+) # # Author: Russell King (Oracle) (Sat 27 Nov 16:05:36 GMT 2021) # Committer: Russell King (Oracle) (Fri 18 Oct 10:40:37 BST 2024) # # net: use phylink_mode_*() helpers # # Use the phylink_mode_*() helpers in all drivers so we can change the # definition of the "mode" argument. # # Signed-off-by: Russell King (Oracle) # # 64ef1e85aa74ac3024a5083411aa9e41968de1aa # drivers/net/dsa/b53/b53_common.c | 7 ++++--- # drivers/net/dsa/bcm_sf2.c | 2 +- # drivers/net/dsa/mv88e6xxx/chip.c | 13 +++++++------ # drivers/net/dsa/mv88e6xxx/port.c | 2 +- # drivers/net/ethernet/cadence/macb_main.c | 2 +- # drivers/net/phy/phylink.c | 29 +++++++++++++++-------------- # 6 files changed, 29 insertions(+), 26 deletions(-) # # Author: Russell King (Oracle) (Sat 27 Nov 14:15:19 GMT 2021) # Committer: Russell King (Oracle) (Fri 18 Oct 10:40:36 BST 2024) # # net: phylink: add helpers for decoding mode # # Add helpers to decode the mode argument passed to the various MAC and # PCS functions. # # Signed-off-by: Russell King (Oracle) # # 70c380b6b58d51d17ab3342e809329a69a37ac24 # include/linux/phylink.h | 17 ++++++++++++++++- # 1 file changed, 16 insertions(+), 1 deletion(-) # # Author: Russell King (Tue 3 Mar 12:12:43 GMT 2020) # Committer: Russell King (Oracle) (Fri 18 Oct 10:40:36 BST 2024) # # net: phylink: use phy interface mode bitmaps for SFP PHYs # # Select the initial SFP PHY interface mode from the PHY supported # interface bitmaps. # # Signed-off-by: Russell King # # cdf0803cee5ef0ae771dfc9195e0dcc5d2da5126 # drivers/net/phy/phylink.c | 53 +++++++++++++++++++++++++++++++++++++++++++++-- # 1 file changed, 51 insertions(+), 2 deletions(-) # # Author: Russell King (Oracle) (Sat 5 Aug 14:30:51 BST 2023) # Committer: Russell King (Oracle) (Fri 18 Oct 10:40:36 BST 2024) # # net: phy: add supported_interfaces to Aquantia AQR113C # # Signed-off-by: Russell King (Oracle) # # 2ca0ca028cfdff5a8c2cf9b14088c1188c625f77 # drivers/net/phy/aquantia/aquantia_main.c | 15 ++++++++++++++- # 1 file changed, 14 insertions(+), 1 deletion(-) # # Author: Russell King (Mon 23 Dec 23:26:04 GMT 2019) # Committer: Russell King (Oracle) (Fri 18 Oct 10:40:35 BST 2024) # # net: phy: add supported_interfaces to marvell10g PHYs # # Signed-off-by: Russell King # # e19052609e276b2a5ea735eb101afeeacdc00037 # drivers/net/phy/marvell10g.c | 5 ++--- # 1 file changed, 2 insertions(+), 3 deletions(-) # # Author: Russell King (Mon 23 Dec 23:25:49 GMT 2019) # Committer: Russell King (Oracle) (Fri 18 Oct 10:40:35 BST 2024) # # net: phy: add supported_interfaces to marvell PHYs # # Signed-off-by: Russell King # # 2218cd1d988c9013b1ce90d0e09a5ae3550e1859 # drivers/net/phy/marvell.c | 9 +++++++++ # 1 file changed, 9 insertions(+) # # Author: Russell King (Mon 23 Dec 23:25:35 GMT 2019) # Committer: Russell King (Oracle) (Fri 18 Oct 10:40:35 BST 2024) # # net: phy: add supported_interfaces to bcm84881 # # Signed-off-by: Russell King # # 30e14176927f3b84107439974d0d8edf642d8d93 # drivers/net/phy/bcm84881.c | 4 ++++ # 1 file changed, 4 insertions(+) # # Author: Russell King (Mon 23 Dec 23:24:17 GMT 2019) # Committer: Russell King (Oracle) (Fri 18 Oct 10:40:34 BST 2024) # # net: phy: add supported_interfaces to phylib # # Add a supported_interfaces member to phylib so we know which # interfaces a PHY supports. Currently, set any unconverted driver # to indicate all interfaces are supported. # # Signed-off-by: Russell King # # f5af60810618158035eea7cfc31ca0a12fb058ac # include/linux/phy.h | 3 +++ # 1 file changed, 3 insertions(+) # # Author: Russell King (Sun 13 Sep 01:06:31 BST 2015) # Committer: Russell King (Oracle) (Fri 18 Oct 10:40:34 BST 2024) # # net: sfp: display SFP module information [*not for mainline*] # # Display SFP module information verbosely, splitting the generic parts # into a separate file. # # Signed-off-by: Russell King # # 4ac1a09c062a4d845794977fee8f60603293eb50 # drivers/net/phy/Makefile | 2 +- # drivers/net/phy/sff.c | 114 +++++++++++++++++++++++ # drivers/net/phy/sff.h | 16 ++++ # drivers/net/phy/sfp.c | 229 ++++++++++++++++++++++++++++++++++++++++++++--- # 4 files changed, 350 insertions(+), 11 deletions(-) # create mode 100644 drivers/net/phy/sff.c # create mode 100644 drivers/net/phy/sff.h # # Author: Russell King (Fri 14 Apr 20:17:13 BST 2017) # Committer: Russell King (Oracle) (Fri 18 Oct 10:40:34 BST 2024) # # net: sfp: add sfp+ compatible [*not for mainline*] # # Add a compatible for SFP+ cages. SFP+ cages are backwards compatible, # but the ethernet device behind them may not support the slower speeds # of SFP modules. # # Signed-off-by: Russell King # # 05296f411e6b7afbb64e436b7f19e4143451851a # drivers/net/phy/sfp.c | 1 + # 1 file changed, 1 insertion(+) # # Author: Russell King (Fri 18 Oct 10:37:44 BST 2019) # Committer: Russell King (Oracle) (Fri 18 Oct 10:40:34 BST 2024) # # net: sfp: add support for cooled SFP+ transceivers # # Cooled SFP+ transceivers need a longer initialisation (startup) time. # Select the initialisation time depending on the cooled option bit. # # Signed-off-by: Russell King # # 6be06ab228ad4e9f8cc24ad548829b70c4ce8f02 # drivers/net/phy/sfp.c | 18 +++++++++++------- # 1 file changed, 11 insertions(+), 7 deletions(-) # # Author: Russell King (Tue 5 Nov 10:34:39 GMT 2019) # Committer: Russell King (Oracle) (Fri 18 Oct 10:40:33 BST 2024) # # net: phy: make phy_error() report which PHY has failed # # phy_error() is called from phy_interrupt() or phy_state_machine(), and # uses WARN_ON() to print a backtrace. The backtrace is not useful when # reporting a PHY error. # # However, a system may contain multiple ethernet PHYs, and phy_error() # gives no clue which one caused the problem. # # Replace WARN_ON() with a call to phydev_err() so that we can see which # PHY had an error, and also inform the user that we are halting the PHY. # # Signed-off-by: Russell King # # 7433873f8e0b7d72bacd8c1d40694d511bb6b482 # drivers/net/phy/phy.c | 3 ++- # 1 file changed, 2 insertions(+), 1 deletion(-) # # Author: Russell King (Wed 5 Jun 11:44:55 BST 2019) # Committer: Russell King (Oracle) (Fri 18 Oct 10:40:33 BST 2024) # # net: phy: marvell10g: allow PHY to probe without firmware # # Allow the PHY to probe when there is no firmware, but do not allow the # link to come up by forcing the PHY state to PHY_HALTED in a similar way # to phy_error(). # # Signed-off-by: Russell King # # 2513f83a736f2ca5416fb6d30aaa8d8cc8028f95 # drivers/net/phy/marvell10g.c | 18 +++++++++++++++++- # 1 file changed, 17 insertions(+), 1 deletion(-) # # Author: Russell King (Mon 26 Aug 12:19:35 BST 2019) # Committer: Russell King (Oracle) (Fri 18 Oct 10:40:33 BST 2024) # # net: phy: provide phy driver start/stop hooks # # Provide phy driver start/stop hooks so that the PHY driver knows when # the network driver is starting or stopping. This will be used for the # Marvell 10G driver so that we can sanely refuse to start if the PHYs # firmware is not present, and also so that we can sanely support SFPs # behind the PHY. # # Signed-off-by: Russell King # # a7631777c26fe1ca15ab784ffb5687699733701c # drivers/net/phy/phy.c | 7 +++++++ # include/linux/phy.h | 3 +++ # 2 files changed, 10 insertions(+) # # Author: Russell King (Sat 4 Jan 00:41:35 GMT 2020) # Committer: Russell King (Oracle) (Fri 18 Oct 10:40:32 BST 2024) # # net: phy: marvell*: add support for hw resolved pause modes # # Support reporting the hardware resolved pause enablement states via # phylib, overriding our software implementation. # # Signed-off-by: Russell King # # f80ae90d00c1ad10e0388955e69303e0b0ecfb45 # drivers/net/phy/marvell.c | 40 ++++++++++++++++++++++++++++++++++------ # drivers/net/phy/marvell10g.c | 6 ++++++ # 2 files changed, 40 insertions(+), 6 deletions(-) # # Author: Russell King (Fri 3 Jan 23:13:28 GMT 2020) # Committer: Russell King (Oracle) (Fri 18 Oct 10:40:32 BST 2024) # # net: phy: add resolved pause support [*not for mainline*] # # Allow phylib drivers to pass the hardware-resolved pause state to MAC # drivers, rather than using the software-based pause resolution code. # # Signed-off-by: Russell King # # b7ca68de8215ba264e06160779cd7b1ff4909f6a # drivers/net/phy/phy_device.c | 6 ++++++ # include/linux/phy.h | 9 +++++++++ # 2 files changed, 15 insertions(+) # # Author: Russell King (Oracle) (Fri 2 Jun 11:51:53 BST 2023) # Committer: Russell King (Oracle) (Fri 18 Oct 10:40:32 BST 2024) # # net: phylink: handle MDIO_USXGMII_LINK when decoding USXGMII # # If MDIO_USXGMII_LINK is not set, it means that the PHYs media side # link is down. Indicate back to phylink that the link as a whole is # down. # # Signed-off-by: Russell King (Oracle) # # e4196183ef64aaf82265877e7b38211b706cf334 # drivers/net/phy/phylink.c | 10 ++++++++-- # 1 file changed, 8 insertions(+), 2 deletions(-) # # Author: Russell King (Sun 1 Dec 15:35:08 GMT 2019) # Committer: Russell King (Oracle) (Fri 18 Oct 10:40:31 BST 2024) # # arm64: dts: configure Macchiatobin 10G PHY LED modes # # Configure the Macchiatobin 10G PHY LED modes to correct their polarity. # We keep the existing LED behaviours, but switch their polarity to # reflect how they are connected. Tweak the LED modes as well to be: # # left: off = no link # solid green = RJ45 link up (not SFP+ cage) # flash green = traffic # right: off = no link # solid green = 10G # solid yellow = 1G # flash green = 100M # flash yellow = 10M # # Signed-off-by: Russell King # # 4136835f24eb63294f17ac287f8049d36df990b3 # arch/arm64/boot/dts/marvell/armada-8040-mcbin.dts | 2 ++ # 1 file changed, 2 insertions(+) # # Author: Russell King (Sun 1 Dec 15:33:39 GMT 2019) # Committer: Russell King (Oracle) (Fri 18 Oct 10:40:30 BST 2024) # # net: phy: marvell10g: add support for configuring LEDs # # Add support for configuring the LEDs. Macchiatobin has an oddity in # that the left LED goes out when the cable is connected, and flashes # when there's link activity. This is because the reset default for # the LED outputs assume that the LED is connected to supply, not to # ground. Add support for configuring the LED modes and polarities. # # Signed-off-by: Russell King # # 79ca61a62c2f28a263c609c912c83c93b5ccc847 # drivers/net/phy/marvell10g.c | 62 +++++++++++++++++++++++++++++++++++++++----- # 1 file changed, 55 insertions(+), 7 deletions(-) # # Author: Russell King (Tue 17 Dec 15:21:50 GMT 2019) # Committer: Russell King (Oracle) (Fri 18 Oct 10:40:30 BST 2024) # # dt-bindings: net: add dt bindings for marvell10g driver # # Add a DT bindings document for the Marvell 10G driver, which will # augment the generic ethernet PHY binding by having LED mode # configuration. # # Signed-off-by: Russell King # # 89990b631dcec62ebfac63805bc3832b63e85c97 # .../devicetree/bindings/net/marvell,10g.yaml | 36 ++++++++++++++++++++++ # 1 file changed, 36 insertions(+) # create mode 100644 Documentation/devicetree/bindings/net/marvell,10g.yaml # # Author: Russell King (Oracle) (Fri 26 Apr 10:59:38 BST 2024) # Committer: Russell King (Oracle) (Fri 18 Oct 10:40:30 BST 2024) # # net: phylink: clean up phylink_resolve() # # The switch() statement doesn't sit very well with the preceeding if() # statements, so let's just convert everything to if()s. This allows # better formatting of the code. # # Signed-off-by: Russell King (Oracle) # # b165410302e7664d43b65fd89cf67beb49e3b84c # drivers/net/phy/phylink.c | 108 ++++++++++++++++++++++------------------------ # 1 file changed, 52 insertions(+), 56 deletions(-) # # Author: Russell King (Oracle) (Mon 22 Apr 17:24:46 BST 2024) # Committer: Russell King (Oracle) (Fri 18 Oct 10:40:29 BST 2024) # # net: phylink: move manual flow control setting # # Move the handling of manual flow control configuration to a common # location during resolve. We currently evaluate this for all but # fixed links. # # Signed-off-by: Russell King (Oracle) # # 50179d18e1f486ba75e6beddb004a410884c5ce4 # drivers/net/phy/phylink.c | 5 +++-- # 1 file changed, 3 insertions(+), 2 deletions(-) # # Author: Russell King (Oracle) (Fri 26 Apr 15:58:29 BST 2024) # Committer: Russell King (Oracle) (Fri 18 Oct 10:40:29 BST 2024) # # net: phylink: remove phylink_phy_no_inband() # # Remove phylink_phy_no_inband() now that we are handling the lack of # inband negotiation by querying the capabilities of the PHY and PCS, # and the BCM84881 PHY driver provides us the information necessary to # make the decision. # # Signed-off-by: Russell King (Oracle) # # a087002b1be26502d099c8edf2d2271108584268 # drivers/net/phy/phylink.c | 27 ++++++--------------------- # 1 file changed, 6 insertions(+), 21 deletions(-) # # Author: Russell King (Oracle) (Thu 8 Feb 16:12:43 GMT 2024) # Committer: Russell King (Oracle) (Fri 18 Oct 10:40:29 BST 2024) # # net: phylink: add pcs_query_inband() *WIP* # # Add a pcs_query_inband() interface which reflects phy_query_inband() # for PHYs. This can be used to determine for the specified interface # mode whether in-band signalling is supported by the PCS, and whether # the PCS requires in-band signalling. # # This is used to determine whether we should use inband autonegotiation # in inband mode, which may be required or may be unsupported in various # interface modes. # # This revised version uses enable/disable/bypass flags to indicate the # capabilities. # # Signed-off-by: Russell King (Oracle) # # 72309af067dfbd4c0cbc1a6af94aadc1ceeb1e8c # drivers/net/phy/phylink.c | 141 ++++++++++++++++++++++++++++++++++++++++------ # 1 file changed, 123 insertions(+), 18 deletions(-) # # Author: Russell King (Oracle) (Thu 8 Feb 19:39:42 GMT 2024) # Committer: Russell King (Oracle) (Fri 18 Oct 10:40:28 BST 2024) # # net: mvpp2: add support for querying PCS inband properties # # Report the PCS inband properties to phylink for Marvell PP2 interfaces. # # Signed-off-by: Russell King (Oracle) # # 1f038bdcdc0248773258414a6a774b4175a3eff4 # drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c | 25 ++++++++++++++++--------- # 1 file changed, 16 insertions(+), 9 deletions(-) # # Author: Russell King (Oracle) (Thu 8 Feb 16:41:31 GMT 2024) # Committer: Russell King (Oracle) (Fri 18 Oct 10:40:28 BST 2024) # # net: mvneta: add support for querying PCS inband properties # # Report the PCS inband properties to phylink for Marvell NETA # interfaces. # # Signed-off-by: Russell King (Oracle) # # c7015cea5aa49154f4a675b4165d0847c4057be3 # drivers/net/ethernet/marvell/mvneta.c | 27 +++++++++++++++++---------- # 1 file changed, 17 insertions(+), 10 deletions(-) # # Author: Russell King (Oracle) (Sat 27 Apr 12:03:27 BST 2024) # Committer: Russell King (Oracle) (Fri 18 Oct 10:40:28 BST 2024) # # net: phylink: add pcs_query_inband() method # # Add a pcs_query_inband() method to query the PCS for its inband link # capabilities, and use this to determine whether link modes used with # optical SFPs can be supported. # # When a PCS does not provide a method, we allow inband negotiation to # be either on or off, making this a no-op until the pcs_query_inband() # method is implemented by a PCS driver. # # Signed-off-by: Russell King (Oracle) # # 99852d52fe57297773c1143552dee332cdf5b341 # drivers/net/phy/phylink.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++ # include/linux/phylink.h | 17 ++++++++++++++ # 2 files changed, 77 insertions(+) # # Author: Russell King (Oracle) (Thu 8 Feb 15:54:46 GMT 2024) # Committer: Russell King (Oracle) (Fri 18 Oct 10:40:28 BST 2024) # # net: phy: marvell: provide phy_query_inband() implementation # # Provide an implementation for phy_query_inband() for Marvell PHYs used # on SFP modules. # # Signed-off-by: Russell King (Oracle) # # 427eabc80b67a10778b322b43b3b6c4d8fc02526 # drivers/net/phy/marvell.c | 16 ++++++++++++++++ # 1 file changed, 16 insertions(+) # # Author: Russell King (Oracle) (Tue 30 Jan 14:38:27 GMT 2024) # Committer: Russell King (Oracle) (Fri 18 Oct 10:40:27 BST 2024) # # net: phy: bcm84881: provide phy_query_inband() implementation # # BCM84881 has no support for inband signalling, so this is a trivial # implementation that returns no support for inband. # # Signed-off-by: Russell King (Oracle) # # 6ea582b10cc3e5c373530ced629c248edef5bae7 # drivers/net/phy/bcm84881.c | 10 ++++++++++ # 1 file changed, 10 insertions(+) # # Author: Russell King (Oracle) (Mon 21 Aug 13:04:32 BST 2023) # Committer: Russell King (Oracle) (Fri 18 Oct 10:40:27 BST 2024) # # net: phy: add phy_query_inband() # # Add a method to query the PHY's in-band capabilities for a PHY # interface mode. This can be used to determine for the specified # interface mode whether in-band signalling is supported, and whether # the PHY requires in-band signalling. # # When not implemented, or the PHY driver doesn't report any modes # for the interface, LINK_INBAND_VALID will not be set. When set, the # remainder of the flags can be interpreted. # # LINK_INBAND_POSSIBLE means that the device can be configured to use # or not use in-band signalling. Later patches may add support to # configure this at the PHY. # # LINK_INBAND_REQUIRED means that the device uses in-band signalling # which can not be disabled. # # When only LINK_INBAND_VALID has been set, this means that the device # does not support any in-band signalling, and can't be configured to # do so. # # "Bypass" mode (where the device may be configured for in-band, but # may still bring the link up if there is no in-band received from the # link partner) is not considered in this patch. # # Signed-off-by: Russell King (Oracle) # # 2cf464c25db5f495a3ff0d0fecd2ebe12c6f102d # drivers/net/phy/phy.c | 21 +++++++++++++++++++++ # include/linux/phy.h | 28 ++++++++++++++++++++++++++++ # 2 files changed, 49 insertions(+) # # Author: Russell King (Oracle) (Fri 26 Apr 09:58:56 BST 2024) # Committer: Russell King (Oracle) (Fri 18 Oct 10:40:27 BST 2024) # # net: phylink: move phylink_pcs_neg_mode() in phylink_major_config() # # Move the call to phylink_pcs_neg_mode() in phylink_major_config() after # we have selected the appropriate PCS to later allow the capabilities of # the PCS to be used when deciding which pcs_neg_mode should be used. # # Signed-off-by: Russell King (Oracle) # # 193e76843e0600322bfadf3811ba276ac39b095c # drivers/net/phy/phylink.c | 8 ++++---- # 1 file changed, 4 insertions(+), 4 deletions(-) # # Author: Russell King (Oracle) (Sat 27 Apr 14:09:10 BST 2024) # Committer: Russell King (Oracle) (Fri 18 Oct 10:40:26 BST 2024) # # net: phylink: add debug for phylink_major_config() # # Now that we have a more complexity in phylink_major_config(), augment # the debugging so we can see what's going on there. # # Signed-off-by: Russell King (Oracle) # # a9bb5d5403e0ac3c6471e88f9fab329deacf9422 # drivers/net/phy/phylink.c | 27 ++++++++++++++++++++++++++- # 1 file changed, 26 insertions(+), 1 deletion(-) # # Author: Russell King (Oracle) (Mon 22 Apr 16:44:44 BST 2024) # Committer: Russell King (Oracle) (Fri 18 Oct 10:40:26 BST 2024) # # net: phylink: switch to MLO_AN_PHY when PCS uses outband # # phylink_pcs_neg_mode() currently only outband mode when we are using # MLO_AN_PHY or MLO_AN_FIXED. However, we want to extend the use of this # to MLO_AN_INBAND when the PCS and/or PHY are unable to use inband mode. # # For example, when a PHY is not capable of supporting inband mode, we # need to operate in outband mode. However, using outband mode for the # PCS while using MLO_AN_INBAND causes drivers to misbehave as it is not # a situation they are expecting. Therefore, we need to switch to # MLO_AN_PHY when using outband mode. # # Add the logic to select MLO_AN_PHY mode when phylink_pcs_neg_mode() # indicates that it will be using outband mode with a PHY and the # requested mode is MLO_AN_INBAND. # # Signed-off-by: Russell King (Oracle) # # 33eadbebbb0746c4d4f1223f867621b1818a91e7 # drivers/net/phy/phylink.c | 19 +++++++++++++++++-- # 1 file changed, 17 insertions(+), 2 deletions(-) # # Author: Russell King (Oracle) (Fri 26 Apr 10:30:39 BST 2024) # Committer: Russell King (Oracle) (Fri 18 Oct 10:40:26 BST 2024) # # net: phylink: split cur_link_an_mode into requested and active # # There is an interdependence between the link_an_mode and pcs_neg_mode # that at least drivers rely upon. In order to support changing the MAC # link_an_mode when we switch interfaces, we need to keep track of its # original setting so that we can appropriately re-calculate pcs_neg_mode # from the same starting point. # # Split the current link_an_mode into a requested link_an_mode and an # active link_an_mode. This will allow us to keep the MAC's link_an_mode # synchronised with the pcs_neg_mode should we decide to switch to # outband with a PHY. # # Signed-off-by: Russell King (Oracle) # # aca110fd5a9be9aaf2f8fb9c30a7a9526f764863 # drivers/net/phy/phylink.c | 60 ++++++++++++++++++++++++++--------------------- # 1 file changed, 33 insertions(+), 27 deletions(-) # # Author: Russell King (Oracle) (Fri 26 Apr 13:04:32 BST 2024) # Committer: Russell King (Oracle) (Fri 18 Oct 10:40:26 BST 2024) # # net: phylink: simplify how SFP PHYs are attached # # There are a few issues with how SFP PHYs are attached: # # a) The phylink_sfp_connect_phy() and phylink_sfp_config_phy() code # validates the configuration three times: # # 1. To discover the support/advertising masks that the PHY/PCS/MAC # can support in order to select an interface. # 2. To validate the selected interface. # 3. When the PHY is brought up after being attached, another validation # is done. # # This is needlessly complex. # # b) The configuration is set prior to the PHY being attached, which # means we don't have the PHY available in phylink_major_config() # for phylink_pcs_neg_mode() to make decisions upon. # # We have already added an extra step to validate the selected interface, # so we can now move the attachment and bringup of the PHY earlier, # inside phylink_sfp_config_phy(). This results in the validation at # step 2 above becoming entirely unnecessary, so remove that too. # # Signed-off-by: Russell King (Oracle) # # f889c15c30e388b199cfdc224dbf9410977827dd # drivers/net/phy/phylink.c | 43 ++++++++++++++----------------------------- # 1 file changed, 14 insertions(+), 29 deletions(-) # # Author: Russell King (Oracle) (Fri 26 Apr 12:58:22 BST 2024) # Committer: Russell King (Oracle) (Fri 18 Oct 10:40:25 BST 2024) # # net: phylink: validate sfp_select_interface() returned interface # # When configuring for a SFP PHY, validate that the MAC supports the # interface returned from sfp_select_interface(). This is a preparatory # step to reorganising how a PHY on a SFP module is handled. # # Signed-off-by: Russell King (Oracle) # # 3236566589b28eaa636890d9c5fcbb8b003f4d9d # drivers/net/phy/phylink.c | 43 ++++++++++++++++++++++++++++++------------- # 1 file changed, 30 insertions(+), 13 deletions(-) # # Author: Russell King (Oracle) (Thu 1 Jun 09:48:20 BST 2023) # Committer: Russell King (Oracle) (Fri 18 Oct 10:40:25 BST 2024) # # net: phylink: allow EEE management for SFPs without PHYs # # Allow EEE management for SFPs without accessible PHYs. # # Signed-off-by: Russell King (Oracle) # # a790044d9ea65c36028d483cbd358c8980d94bd6 # drivers/net/phy/phylink.c | 11 ++++++++++- # 1 file changed, 10 insertions(+), 1 deletion(-) # # Author: Russell King (Oracle) (Wed 16 Aug 20:48:48 BST 2023) # Committer: Russell King (Oracle) (Fri 18 Oct 10:40:25 BST 2024) # # net: dsa: mt7530: convert to phylink managed EEE # # Fixme: doesn't bit 25 and 26 also need to be set in the PMCR for # PMCR_FORCE_EEE100 and PMCR_FORCE_EEE1G to take effect? # # Signed-off-by: Russell King (Oracle) # # 9e983f5441ce9f3f2fd1b2c28b0434f28aa0ab1a # drivers/net/dsa/mt7530.c | 98 ++++++++++++++++++++++++++---------------------- # drivers/net/dsa/mt7530.h | 6 +-- # 2 files changed, 54 insertions(+), 50 deletions(-) # # Author: Russell King (Oracle) (Wed 16 Aug 14:02:11 BST 2023) # Committer: Russell King (Oracle) (Fri 18 Oct 10:40:24 BST 2024) # # net: dsa: add support for phylink managed EEE # # Add support to allow DSA drivers to use phylink managed EEE, with only # needing support for controlling the LPI state. # # Signed-off-by: Russell King (Oracle) # # 459eee339d6795e6d71a83100712fa8fdd1a6ee0 # net/dsa/user.c | 42 ++++++++++++++++++++++++++---------------- # 1 file changed, 26 insertions(+), 16 deletions(-) # # Author: Russell King (Oracle) (Thu 17 Aug 16:32:32 BST 2023) # Committer: Russell King (Oracle) (Fri 18 Oct 10:40:24 BST 2024) # # net: dsa: mv88e6xxx: add EEE controls # # Add phylink EEE control methods to allow EEE to be configured. When # LPI is to be disabled, we force the port to have EEE disabled, but # when enabling EEE, if the port is under the control of the PPU, we # stop forcing it, otherwise we force-enable EEE. # # Signed-off-by: Russell King (Oracle) # # 12943f7447dfdd36c7b118b4970a733419be219a # drivers/net/dsa/mv88e6xxx/chip.c | 45 ++++++++++++++++++++++++++++++++++++++++ # 1 file changed, 45 insertions(+) # # Author: Russell King (Oracle) (Thu 17 Aug 15:36:22 BST 2023) # Committer: Russell King (Oracle) (Fri 18 Oct 10:40:24 BST 2024) # # net: dsa: mv88e6xxx: add support for EEE forcing # # Add support for EEE forcing using the MAC control register. Replace # the 88e6393x errata 4.5 EEE disable code with a call to the new EEE # forcing code. # # Signed-off-by: Russell King (Oracle) # # a37fa764f67d7b33cfa9a22d8ce3e1bf00b8d284 # drivers/net/dsa/mv88e6xxx/port.c | 39 +++++++++++++++++++++++++++++++-------- # drivers/net/dsa/mv88e6xxx/port.h | 1 + # 2 files changed, 32 insertions(+), 8 deletions(-) # # Author: Russell King (Oracle) (Thu 17 Aug 16:32:32 BST 2023) # Committer: Russell King (Oracle) (Fri 18 Oct 10:40:23 BST 2024) # # net: dsa: mv88e6xxx: add port_set_eee() method # # Add a port_set_eee() method to allow the EEE settings for a port to be # configured. # # Signed-off-by: Russell King (Oracle) # # 9169749e782b58bfd580146469157142a3225a83 # drivers/net/dsa/mv88e6xxx/chip.h | 10 ++++++++++ # 1 file changed, 10 insertions(+) # # Author: Russell King (Oracle) (Wed 31 May 17:17:11 BST 2023) # Committer: Russell King (Oracle) (Fri 18 Oct 10:40:23 BST 2024) # # net: mvpp2: add EEE implementation # # Add EEE support for mvpp2, using phylink's EEE implementation, which # means we just need to implement the two methods for LPI control, and # with the initial configuration. Only the GMAC is supported, so only # 100M, 1G and 2.5G speeds. # # Disabling LPI requires clearing a single bit. Enabling LPI needs a full # configuration of several values, as the timer values are dependent on # the MAC operating speed. # # Signed-off-by: Russell King (Oracle) # # ce02531f81a7bcc00ed074c82744e0baec2d2de0 # drivers/net/ethernet/marvell/mvpp2/mvpp2.h | 5 ++ # drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c | 85 +++++++++++++++++++++++++ # 2 files changed, 90 insertions(+) # # Author: Russell King (Oracle) (Wed 31 May 15:17:24 BST 2023) # Committer: Russell King (Oracle) (Fri 18 Oct 10:40:23 BST 2024) # # net: mvneta: convert to phylink EEE implementation # # Convert mvneta to use phylink's EEE implementation, which means we just # need to implement the two methods for LPI control, and adding the # initial configuration. # # Disabling LPI requires clearing a single bit. Enabling LPI needs a full # configuration of several values, as the timer values are dependent on # the MAC operating speed. # # Signed-off-by: Russell King (Oracle) # # 08de07efdb45669a70dffb74b28722cc0d941bf0 # drivers/net/ethernet/marvell/mvneta.c | 107 +++++++++++++++++++--------------- # 1 file changed, 60 insertions(+), 47 deletions(-) # # Author: Russell King (Oracle) (Wed 31 May 10:02:35 BST 2023) # Committer: Russell King (Oracle) (Fri 18 Oct 10:40:22 BST 2024) # # net: phylink: add EEE management # # Add EEE management to phylink. # # Signed-off-by: Russell King (Oracle) # # 7dffe465e60c13801a6d51edfcd70f0b306b7bdd # drivers/net/phy/phylink.c | 135 +++++++++++++++++++++++++++++++++++++++++++++- # include/linux/phylink.h | 36 +++++++++++++ # 2 files changed, 170 insertions(+), 1 deletion(-) # # Author: Russell King (Oracle) (Wed 31 May 10:02:35 BST 2023) # Committer: Russell King (Oracle) (Fri 18 Oct 10:40:22 BST 2024) # # net: phylink: add phylink_link_is_up() helper # # Add a helper to determine whether the link is up or down. Currently # this is only used in one location, but becomes necessary to test # when reconfiguring EEE. # # Signed-off-by: Russell King (Oracle) # # 5e19b9b1a2094cb380519997c447fb63018bee27 # drivers/net/phy/phylink.c | 10 ++++++---- # 1 file changed, 6 insertions(+), 4 deletions(-) # # Author: Russell King (Oracle) (Fri 24 May 22:04:13 BST 2024) # Committer: Russell King (Oracle) (Fri 18 Oct 10:37:33 BST 2024) # # net: dsa: remove obsolete phylink dsa_switch operations # # No driver now uses the DSA switch phylink members, so we can now remove # the shim functions and method pointers. Arrange to print an error # message and fail registration if a DSA driver does not provide the # phylink MAC operations structure. # # Signed-off-by: Russell King (oracle) # # a83ceb824a2f91d76e95639c9f850b78d62e4ab7 # net/dsa/dsa.c | 5 +++++ # net/dsa/port.c | 34 +--------------------------------- # 2 files changed, 6 insertions(+), 33 deletions(-) # # Author: Russell King (Oracle) (Fri 18 Oct 10:35:33 BST 2024) # Committer: Russell King (Oracle) (Fri 18 Oct 10:36:03 BST 2024) # # net: phylink: simplify phylink_parse_fixedlink() # # phylink_parse_fixedlink() wants to preserve the pause, asym_pause and # autoneg bits in pl->supported. Rather than reading the bits into # separate bools, zeroing pl->supported, and then setting them if they # were previously set, use a mask and linkmode_and() to achieve the same # result. # # Signed-off-by: Russell King (Oracle) # # 7a2612b21b8ee64badb20ca193d1eeb728d1dfc3 # drivers/net/phy/phylink.c | 19 ++++++------------- # 1 file changed, 6 insertions(+), 13 deletions(-) # # Author: Russell King (Oracle) (Fri 18 Oct 10:35:13 BST 2024) # Committer: Russell King (Oracle) (Fri 18 Oct 10:36:03 BST 2024) # # net: phylink: remove "using_mac_select_pcs" # # With DSA's implementation of the mac_select_pcs() method removed, we # can now remove the detection of mac_select_pcs() implementation. # # Signed-off-by: Russell King (Oracle) # # fb2dbb27e96002032defb0d2fd6a7dae0574a210 # drivers/net/phy/phylink.c | 12 ++---------- # 1 file changed, 2 insertions(+), 10 deletions(-) # # Author: Russell King (Oracle) (Fri 18 Oct 10:35:13 BST 2024) # Committer: Russell King (Oracle) (Fri 18 Oct 10:36:02 BST 2024) # # net: phylink: remove use of pl->pcs in phylink_validate_mac_and_pcs() # # When the mac_select_pcs() method is not implemented, there is no way # for pl->pcs to be set to a non-NULL value. This was here to support # the old phylink_set_pcs() method which has been removed a few years # ago. Simplify the code in phylink_validate_mac_and_pcs(). # # Signed-off-by: Russell King (Oracle) # # d4713f12599e6ff49738ab9485e26fe296547985 # drivers/net/phy/phylink.c | 4 +--- # 1 file changed, 1 insertion(+), 3 deletions(-) # # Author: Russell King (Oracle) (Fri 18 Oct 10:35:13 BST 2024) # Committer: Russell King (Oracle) (Fri 18 Oct 10:36:02 BST 2024) # # net: phylink: allow mac_select_pcs() to remove a PCS # # phylink has historically not permitted a PCS to be removed. An attempt # to permit this with phylink_set_pcs() resulted in comments indicating # that there was no need for this. This behaviour has been propagated # forward to the mac_select_pcs() approach as it was believed from these # comments that changing this would be NAK'd. # # However, with mac_select_pcs(), it takes more code and thus complexity # to maintain this behaviour, which can - and in this case has - resulted # in a bug. If mac_select_pcs() returns NULL for a particular interface # type, but there is already a PCS in-use, then we skip the pcs_validate() # method, but continue using the old PCS. Also, it wouldn't be expected # behaviour by implementers of mac_select_pcs(). # # Allow this by removing this old unnecessary restriction. # # Signed-off-by: Russell King (Oracle) # # b4858d43a97e6c213f9b647d07d7923a9f4eba21 # drivers/net/phy/phylink.c | 2 +- # 1 file changed, 1 insertion(+), 1 deletion(-) # # Author: Russell King (Oracle) (Fri 18 Oct 10:35:12 BST 2024) # Committer: Russell King (Oracle) (Fri 18 Oct 10:36:02 BST 2024) # # net: dsa: mv88e6xxx: return NULL when no PCS is present # # Rather than returning an EOPNOTSUPP error pointer when the switch # has no support for PCS, return NULL to indicate that no PCS is # required. # # Signed-off-by: Russell King (Oracle) # # 9e14ae9465303c8959dd8bf37878dc66a85ac43d # drivers/net/dsa/mv88e6xxx/chip.c | 2 +- # 1 file changed, 1 insertion(+), 1 deletion(-) # # Author: Russell King (Oracle) (Fri 18 Oct 10:35:12 BST 2024) # Committer: Russell King (Oracle) (Fri 18 Oct 10:36:02 BST 2024) # # net: dsa: remove dsa_port_phylink_mac_select_pcs() # # There is no longer any reason to implement the mac_select_pcs() # callback in DSA. Returning ERR_PTR(-EOPNOTSUPP) is functionally # equivalent to not providing the function. # # Signed-off-by: Russell King (Oracle) # # 483a52c200b85fbcf812be55f6b7fcd996671666 # net/dsa/port.c | 8 -------- # 1 file changed, 8 deletions(-) # # Author: Russell King (Oracle) (Fri 18 Oct 10:35:05 BST 2024) # Committer: Russell King (Oracle) (Fri 18 Oct 10:36:01 BST 2024) # # net: dsa: remove obsolete phylink dsa_switch operations # # No driver now uses the DSA switch phylink members, so we can now remove # the method pointers, but we need to leave empty shim functions to allow # those drivers that do not provide phylink MAC operations structure to # continue functioning. # # Signed-off-by: Russell King (oracle) # Reviewed-by: Vladimir Oltean # Tested-by: Vladimir Oltean # sja1105, felix, dsa_loop # Link: https://patch.msgid.link/E1swKNV-0060oN-1b@rmk-PC.armlinux.org.uk # Signed-off-by: Jakub Kicinski # # 25b6615e4c78f8d207fc9f6c88b6424628182da3 # include/net/dsa.h | 15 --------------- # net/dsa/dsa.c | 8 -------- # net/dsa/port.c | 34 +--------------------------------- # 3 files changed, 1 insertion(+), 56 deletions(-) # # Author: Russell King (Sat 3 Oct 09:13:05 BST 2015) # Committer: Russell King (Oracle) (Thu 10 Oct 18:28:41 BST 2024) # # cpuidle: mvebu: indicate failure to enter deeper sleep states # # The cpuidle ->enter method expects the return value to be the sleep # state we entered. Returning negative numbers or other codes is not # permissible since coupled CPU idle was merged. # # At least some of the mvebu_v7_cpu_suspend() implementations return the # value from cpu_suspend(), which returns zero if the CPU vectors back # into the kernel via cpu_resume() (the success case), or the non-zero # return value of the suspend actor, or one (failure cases). # # We do not want to be returning the failure case value back to CPU idle # as that indicates that we successfully entered one of the deeper idle # states. Always return zero instead, indicating that we slept for the # shortest amount of time. # # Signed-off-by: Russell King # # c5ff572b6f05ea7a14395f95f4790de718ad6882 # drivers/cpuidle/cpuidle-mvebu-v7.c | 6 +++++- # 1 file changed, 5 insertions(+), 1 deletion(-) # # Author: Russell King (Sat 7 Jan 20:47:36 GMT 2017) # Committer: Russell King (Oracle) (Thu 10 Oct 18:28:35 BST 2024) # # net: dsa: program 6176 LED registers # # Signed-off-by: Russell King # # 103f93e6789e55f644c37279b98963d71acfaf46 # drivers/net/dsa/mv88e6xxx/chip.c | 20 ++++++++++++++++++++ # 1 file changed, 20 insertions(+) # # Author: Russell King (Thu 28 Sep 12:09:56 BST 2017) # Committer: Russell King (Oracle) (Thu 10 Oct 18:28:32 BST 2024) # # Revert "net: dsa: mv88e6xxx: remove LED control register" # # This reverts commit c56a71a92114e3198e249593841cb744abaadcb7. # # 35f846ff3389293254353add7c92dcf13dcab57d # drivers/net/dsa/mv88e6xxx/port.h | 3 +++ # 1 file changed, 3 insertions(+) # # Author: Russell King (Mon 27 Jan 14:00:12 GMT 2020) # Committer: Russell King (Oracle) (Thu 10 Oct 18:28:30 BST 2024) # # net: dsa: mv88e6xxx: debugfs hacks to fix the compile # # This is the problem with out-of-tree maintained patches; they break, # sometimes requiring substantial rework. It's all very well promising # to publish new versions as that happens, but it causes pain when they # aren't published in a timely manner. Hence this hack. # # Signed-off-by: Russell King # # f3ba54558134769feefaa21b83b3048e365f9451 # drivers/net/dsa/mv88e6xxx/mv88e6xxx_debugfs.c | 5 ++++- # 1 file changed, 4 insertions(+), 1 deletion(-) # # Author: Vivien Didelot (Thu 22 Oct 19:31:23 BST 2015) # Committer: Russell King (Oracle) (Thu 10 Oct 18:28:29 BST 2024) # # net: dsa: mv88e6xxx: add debugfs interface # # Add a debugfs directory named mv88e6xxx.X where X is the DSA switch # index. Mount the debugfs file system with: # # # mount -t debugfs none /sys/kernel/debug # # Signed-off-by: Vivien Didelot # [Modified by rmk for current kernels.] # Signed-off-by: Russell King # # 49bad4b416310d5947ad69fd9374708da04d42ab # drivers/net/dsa/mv88e6xxx/chip.c | 5 + # drivers/net/dsa/mv88e6xxx/chip.h | 2 + # drivers/net/dsa/mv88e6xxx/mv88e6xxx_debugfs.c | 1099 +++++++++++++++++++++++++ # 3 files changed, 1106 insertions(+) # create mode 100644 drivers/net/dsa/mv88e6xxx/mv88e6xxx_debugfs.c # # Author: Russell King (Oracle) (Thu 10 Oct 18:14:37 BST 2024) # Committer: Russell King (Oracle) (Thu 10 Oct 18:14:37 BST 2024) # # net: pcs: xpcs: move Wangxun VR_XS_PCS_DIG_CTRL1 configuration # # According to commits 2a22b7ae2fa3 ("net: pcs: xpcs: adapt Wangxun NICs # for SGMII mode") and 2deea43f386d ("net: pcs: xpcs: add 1000BASE-X AN # interrupt support"), Wangxun devices need special VR_XS_PCS_DIG_CTRL1 # settings for SGMII and 1000BASE-X. Both SGMII and 1000BASE-X use the # same settings. # # Rather than placing these in the individual xpcs_config_*() functions, # move it to where we already test for the Wangxun devices in # xpcs_do_config(). # # Signed-off-by: Russell King (Oracle) # Signed-off-by: David S. Miller # # dc770f983ce9175aeb5959930e13bee4178e4961 # drivers/net/pcs/pcs-xpcs.c | 14 ++++++++------ # 1 file changed, 8 insertions(+), 6 deletions(-) # # Author: Russell King (Oracle) (Thu 10 Oct 18:14:37 BST 2024) # Committer: Russell King (Oracle) (Thu 10 Oct 18:14:37 BST 2024) # # net: pcs: xpcs: correctly place DW_VR_MII_DIG_CTRL1_2G5_EN # # Place DW_VR_MII_DIG_CTRL1_2G5_EN with the other DW_VR_MII_DIG_CTRL1 # definitions rather than in the middle of a register list. # # Signed-off-by: Russell King (Oracle) # Signed-off-by: David S. Miller # # a21044ac9b90c712c72239c49ba4202709707f4f # drivers/net/pcs/pcs-xpcs.h | 3 +-- # 1 file changed, 1 insertion(+), 2 deletions(-) # # Author: Russell King (Oracle) (Thu 10 Oct 18:14:36 BST 2024) # Committer: Russell King (Oracle) (Thu 10 Oct 18:14:36 BST 2024) # # net: pcs: xpcs: use dev_*() to print messages # # Use the dev_*() family of functions to print all messages from the XPCS # driver so we know which instance issues the messages. # # Signed-off-by: Russell King (Oracle) # Signed-off-by: David S. Miller # # 0ac9f2cb9e7cf916a9e6955e483329912a399b71 # drivers/net/pcs/pcs-xpcs.c | 44 ++++++++++++++++++++++---------------------- # 1 file changed, 22 insertions(+), 22 deletions(-) # # Author: Russell King (Oracle) (Thu 10 Oct 18:14:36 BST 2024) # Committer: Russell King (Oracle) (Thu 10 Oct 18:14:36 BST 2024) # # net: pcs: xpcs: convert to use read_poll_timeout() # # Convert the xpcs driver to use read_poll_timeout() when waiting for # reset to complete, rather than open-coding this. # # Signed-off-by: Russell King (Oracle) # Signed-off-by: David S. Miller # # 32fa243acd5a36131604ad4985e2030dd34f5256 # drivers/net/pcs/pcs-xpcs.c | 17 +++++++---------- # 1 file changed, 7 insertions(+), 10 deletions(-) # # Author: Russell King (Oracle) (Thu 10 Oct 18:14:36 BST 2024) # Committer: Russell King (Oracle) (Thu 10 Oct 18:14:36 BST 2024) # # net: pcs: xpcs: add _modify() accessors # # The xpcs driver does a lot of read-modify-write operations on # registers, which leads to long-winded code to read the register, check # whether the read was successful, modify the value in some way, and then # write it back. # # We have a mdiodev _modify() accessor that encapsulates this, and does # the register modification under the MDIO bus lock ensuring that the # modification is atomic with respect to other bus operations. Convert # the xpcs driver to use this accessor. # # Signed-off-by: Russell King (Oracle) # Signed-off-by: David S. Miller # # eca8cfb56c333e5daff4e2742c3e2d37f514bd8c # drivers/net/pcs/pcs-xpcs-nxp.c | 24 ++---- # drivers/net/pcs/pcs-xpcs-wx.c | 56 ++++++-------- # drivers/net/pcs/pcs-xpcs.c | 165 ++++++++++++++++++----------------------- # drivers/net/pcs/pcs-xpcs.h | 1 + # 4 files changed, 104 insertions(+), 142 deletions(-) # # Author: Russell King (Oracle) (Thu 10 Oct 18:14:35 BST 2024) # Committer: Russell King (Oracle) (Thu 10 Oct 18:14:35 BST 2024) # # net: pcs: xpcs: use FIELD_PREP() and FIELD_GET() # # Convert xpcs to use the bitfield macros rather than definining the # bitfield shifts and open-coding the insertion and extraction of these # bitfields. # # Signed-off-by: Russell King (Oracle) # Signed-off-by: David S. Miller # # eb5245c70009276c52a9ec9000f1cd235ce1c31e # drivers/net/pcs/pcs-xpcs.c | 14 ++++++-------- # drivers/net/pcs/pcs-xpcs.h | 4 ---- # 2 files changed, 6 insertions(+), 12 deletions(-) # # Author: Russell King (Oracle) (Thu 10 Oct 18:14:35 BST 2024) # Committer: Russell King (Oracle) (Thu 10 Oct 18:14:35 BST 2024) # # net: pcs: xpcs: move searching ID list out of line # # Move the searching of the physical ID out of xpcs_create() and into # its own xpcs_identify() function, which makes it self contained. # This reduces the complexity in xpcs_craete(), making it easier to # follow, rather than having a lot of once-run code in the big for() # loop. # # Signed-off-by: Russell King (Oracle) # Signed-off-by: David S. Miller # # dffe211a790ffd43f59f4cd97ce3de4ea57f8af8 # drivers/net/pcs/pcs-xpcs.c | 41 +++++++++++++++++++++-------------------- # 1 file changed, 21 insertions(+), 20 deletions(-) # # Author: Russell King (Oracle) (Thu 10 Oct 18:14:35 BST 2024) # Committer: Russell King (Oracle) (Thu 10 Oct 18:14:35 BST 2024) # # net: pcs: xpcs: rename xpcs_get_id() # # Rename xpcs_get_id() to xpcs_read_id() which more closely reflects # the purpose of this function. # # Signed-off-by: Russell King (Oracle) # Signed-off-by: David S. Miller # # cd681b84d96d92da95ff217ddfa3a3babf827366 # drivers/net/pcs/pcs-xpcs.c | 4 ++-- # 1 file changed, 2 insertions(+), 2 deletions(-) # # Author: Russell King (Oracle) (Thu 10 Oct 18:14:34 BST 2024) # Committer: Russell King (Oracle) (Thu 10 Oct 18:14:34 BST 2024) # # net: pcs: xpcs: move definition of struct dw_xpcs to private header # # There should be no reason for anything outside the XPCS code to know # the contents of struct dw_xpcs - this is a private structure to XPCS. # Move the definition to the private pcs-xpcs.h header, leaving a # declaration in the global pcs/pcs-xpcs.h # # Signed-off-by: Russell King (Oracle) # Signed-off-by: David S. Miller # # 80e5323ca90d6e12eddd8f81bd25fe6361ea4fc3 # drivers/net/pcs/pcs-xpcs.h | 18 ++++++++++++++++++ # include/linux/pcs/pcs-xpcs.h | 18 +----------------- # 2 files changed, 19 insertions(+), 17 deletions(-) # # Author: Russell King (Oracle) (Thu 10 Oct 18:14:34 BST 2024) # Committer: Russell King (Oracle) (Thu 10 Oct 18:14:34 BST 2024) # # net: pcs: xpcs: provide a helper to get the phylink pcs given xpcs # # Provide a helper to provide the pointer to the phylink_pcs struct # given a valid xpcs pointer. This will be necessary when we make # struct dw_xpcs private to pcs-xpcs.c # # Signed-off-by: Russell King (Oracle) # Signed-off-by: David S. Miller # # d24a4b2c633a6eef2b53bdecb532f86cd416f404 # drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c | 2 +- # drivers/net/pcs/pcs-xpcs.c | 6 ++++++ # include/linux/pcs/pcs-xpcs.h | 1 + # 3 files changed, 8 insertions(+), 1 deletion(-) # # Author: Russell King (Oracle) (Thu 10 Oct 18:14:34 BST 2024) # Committer: Russell King (Oracle) (Thu 10 Oct 18:14:34 BST 2024) # # net: pcs: xpcs: pass xpcs instead of xpcs->id to xpcs_find_compat() # # xpcs_find_compat() is now always passed xpcs->id. Rather than always # dereferencing this in the caller, move it into xpcs_find_compat(), # thus making this function consistent with most of the other xpcs # functions in taking an xpcs pointer. # # Signed-off-by: Russell King (Oracle) # Signed-off-by: David S. Miller # # c3c3b82c7599e5e24c65504b5ba853da9df4c54d # drivers/net/pcs/pcs-xpcs.c | 14 +++++++------- # 1 file changed, 7 insertions(+), 7 deletions(-) # # Author: Russell King (Oracle) (Thu 10 Oct 18:14:34 BST 2024) # Committer: Russell King (Oracle) (Thu 10 Oct 18:14:34 BST 2024) # # net: pcs: xpcs: don't use array for interface # # Currently, xpcs uses an array of interfaces that each "compat" entry # supports. When looking up the compat entry for an interface, we # iterate over the compat entries and then over each interface. # # Since each compat entry only has a single interface in its interfaces # array, replace the array with a single member in the compat structure. # # Signed-off-by: Russell King (Oracle) # Signed-off-by: David S. Miller # # 2d9ff35bb97ea222fa98f5be73fdb434a259bdd9 # drivers/net/pcs/pcs-xpcs.c | 71 +++++++++------------------------------------- # 1 file changed, 14 insertions(+), 57 deletions(-) # # Author: Russell King (Oracle) (Thu 10 Oct 18:14:33 BST 2024) # Committer: Russell King (Oracle) (Thu 10 Oct 18:14:33 BST 2024) # # net: pcs: xpcs: remove dw_xpcs_compat enum # # There is no reason for the struct dw_xpcs_compat arrays to be a fixed # size other than the way we iterate over them. The index into the array # isn't used for anything, and having them fixed size needlessly wastes # space. # # Remove the enum that defines their size, and instead use an empty # array entry (with NULL ->supported) to mark the end of the array. # # Signed-off-by: Russell King (Oracle) # Signed-off-by: David S. Miller # # 523bd58de2d373f0f229a40540a7cad560de4c14 # drivers/net/pcs/pcs-xpcs.c | 69 +++++++++++++++++----------------------------- # 1 file changed, 25 insertions(+), 44 deletions(-) # # Author: Russell King (Oracle) (Thu 10 Oct 18:10:59 BST 2024) # Committer: Russell King (Oracle) (Thu 10 Oct 18:14:18 BST 2024) # # net: pcs: xpcs: make xpcs_do_config() and xpcs_link_up() internal # # As nothing outside pcs-xpcs.c calls neither xpcs_do_config() nor # xpcs_link_up(), remove their exports and prototypes. # # Reviewed-by: Vladimir Oltean # Signed-off-by: Russell King (Oracle) # Link: https://patch.msgid.link/E1svfMv-005ZIv-2M@rmk-PC.armlinux.org.uk # Signed-off-by: Jakub Kicinski # # 325088cc386197039507d6acb9474ca680608bb4 # drivers/net/pcs/pcs-xpcs.c | 11 +++++------ # include/linux/pcs/pcs-xpcs.h | 4 ---- # 2 files changed, 5 insertions(+), 10 deletions(-) # # Author: Russell King (Oracle) (Thu 10 Oct 18:10:58 BST 2024) # Committer: Russell King (Oracle) (Thu 10 Oct 18:14:18 BST 2024) # # net: pcs: xpcs: drop interface argument from xpcs_create*() # # The XPCS sub-driver no longer uses the "interface" argument to the # xpcs_create_mdiodev() and xpcs_create_fwnode() functions. Remove # this now unnecessary argument, updating the stmmac driver # appropriately. # # Reviewed-by: Vladimir Oltean # Signed-off-by: Russell King (Oracle) # Link: https://patch.msgid.link/E1svfMp-005ZIp-UX@rmk-PC.armlinux.org.uk # Signed-off-by: Jakub Kicinski # # 07e688f02f8d1f18e6a2dccf1a8187248abec307 # drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c | 7 +++---- # drivers/net/pcs/pcs-xpcs.c | 10 +++------- # include/linux/pcs/pcs-xpcs.h | 6 ++---- # 3 files changed, 8 insertions(+), 15 deletions(-) # # Author: Russell King (Oracle) (Thu 10 Oct 18:10:58 BST 2024) # Committer: Russell King (Oracle) (Thu 10 Oct 18:14:17 BST 2024) # # net: dsa: sja1105: use phylink_pcs internally # # Use xpcs_create_pcs_mdiodev() to create the XPCS instance, storing # and using the phylink_pcs pointer internally, rather than dw_xpcs. # Use xpcs_destroy_pcs() to destroy the XPCS instance when we've # finished with it. # # Signed-off-by: Russell King (Oracle) # Link: https://patch.msgid.link/E1svfMk-005ZIj-R3@rmk-PC.armlinux.org.uk # Signed-off-by: Jakub Kicinski # # 2f7779d321d93cd9de17d02a64baeabc7c4ca58c # drivers/net/dsa/sja1105/sja1105.h | 2 +- # drivers/net/dsa/sja1105/sja1105_main.c | 16 ++++------------ # drivers/net/dsa/sja1105/sja1105_mdio.c | 28 +++++++++++++--------------- # 3 files changed, 18 insertions(+), 28 deletions(-) # # Author: Russell King (Oracle) (Thu 10 Oct 18:10:58 BST 2024) # Committer: Russell King (Oracle) (Thu 10 Oct 18:14:17 BST 2024) # # net: dsa: sja1105: call PCS config/link_up via pcs_ops structure # # Call the PCS operations through the ops structure, which avoids needing # to export xpcs internal functions. # # Signed-off-by: Russell King (Oracle) # Link: https://patch.msgid.link/E1svfMf-005ZId-Mx@rmk-PC.armlinux.org.uk # Signed-off-by: Jakub Kicinski # # d7c6202f6ca1e981c2e94d0601aff8333dfb6ac4 # drivers/net/dsa/sja1105/sja1105_main.c | 10 +++++++--- # 1 file changed, 7 insertions(+), 3 deletions(-) # # Author: Russell King (Oracle) (Thu 10 Oct 18:10:57 BST 2024) # Committer: Russell King (Oracle) (Thu 10 Oct 18:14:17 BST 2024) # # net: dsa: sja1105: simplify static configuration reload # # The static configuration reload saves the port speed in the static # configuration tables by first converting it from the internal # respresentation to the SPEED_xxx ethtool representation, and then # converts it back to restore the setting. This is because # sja1105_adjust_port_config() takes the speed as SPEED_xxx. # # However, this is unnecessarily complex. If we split # sja1105_adjust_port_config() up, we can simply save and restore the # mac[port].speed member in the static configuration tables. # # Signed-off-by: Russell King (Oracle) # Link: https://patch.msgid.link/E1svfMa-005ZIX-If@rmk-PC.armlinux.org.uk # Signed-off-by: Jakub Kicinski # # 72295265c6cb86ce3dce5e33ab385cbee5bcec51 # drivers/net/dsa/sja1105/sja1105_main.c | 65 ++++++++++++++++++---------------- # 1 file changed, 34 insertions(+), 31 deletions(-) # # Author: Russell King (Oracle) (Thu 10 Oct 18:10:57 BST 2024) # Committer: Russell King (Oracle) (Thu 10 Oct 18:14:16 BST 2024) # # net: wangxun: txgbe: use phylink_pcs internally # # Use xpcs_create_pcs_mdiodev() to create the XPCS instance, storing # and using the phylink_pcs pointer internally, rather than dw_xpcs. # Use xpcs_destroy_pcs() to destroy the XPCS instance when we've # finished with it. # # Signed-off-by: Russell King (Oracle) # Reviewed-by: Andrew Lunn # Link: https://patch.msgid.link/E1svfMV-005ZIR-FE@rmk-PC.armlinux.org.uk # Signed-off-by: Jakub Kicinski # # af8353842089477c1bc9014b9c9dcc24793b839b # drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c | 18 +++++++++--------- # drivers/net/ethernet/wangxun/txgbe/txgbe_type.h | 2 +- # 2 files changed, 10 insertions(+), 10 deletions(-) # # Author: Russell King (Oracle) (Thu 10 Oct 18:10:57 BST 2024) # Committer: Russell King (Oracle) (Thu 10 Oct 18:14:16 BST 2024) # # net: pcs: xpcs: add xpcs_destroy_pcs() and xpcs_create_pcs_mdiodev() # # Provide xpcs create/destroy functions that return and take a phylink_pcs # pointer instead of an xpcs pointer. This will be used by drivers that # have been converted to use phylink_pcs pointers internally, rather than # dw_xpcs pointers. # # As xpcs_create_mdiodev() no longer makes use of its interface argument, # pass PHY_INTERFACE_MODE_NA into xpcs_create_mdiodev() until it is # removed later in the series. # # Reviewed-by: Vladimir Oltean # Signed-off-by: Russell King (Oracle) # Link: https://patch.msgid.link/E1svfMQ-005ZIL-Bi@rmk-PC.armlinux.org.uk # Signed-off-by: Jakub Kicinski # # b63243460de5a1a63309b5cf8d9f14db102cf65d # drivers/net/pcs/pcs-xpcs.c | 18 ++++++++++++++++++ # include/linux/pcs/pcs-xpcs.h | 3 +++ # 2 files changed, 21 insertions(+) # # Author: Russell King (Oracle) (Thu 10 Oct 18:10:56 BST 2024) # Committer: Russell King (Oracle) (Thu 10 Oct 18:14:16 BST 2024) # # net: pcs: xpcs: get rid of xpcs_init_iface() # # xpcs_init_iface() no longer does anything with the interface mode, and # now merely does configuration related to the PMA ID. Move this back # into xpcs_create() as it doesn't warrant being a separate function # anymore. # # Reviewed-by: Vladimir Oltean # Signed-off-by: Russell King (Oracle) # Link: https://patch.msgid.link/E1svfML-005ZIF-84@rmk-PC.armlinux.org.uk # Signed-off-by: Jakub Kicinski # # 3370bc0ff82859ccead5037e7cc9fefe5ad50809 # drivers/net/pcs/pcs-xpcs.c | 17 ++++------------- # 1 file changed, 4 insertions(+), 13 deletions(-) # # Author: Russell King (Oracle) (Thu 10 Oct 18:10:56 BST 2024) # Committer: Russell King (Oracle) (Thu 10 Oct 18:14:16 BST 2024) # # net: pcs: xpcs: drop interface argument from internal functions # # Now that we no longer use the "interface" argument when creating the # XPCS sub-driver, remove it from xpcs_create() and xpcs_init_iface(). # # Reviewed-by: Vladimir Oltean # Signed-off-by: Russell King (Oracle) # Link: https://patch.msgid.link/E1svfMG-005ZI9-3k@rmk-PC.armlinux.org.uk # Signed-off-by: Jakub Kicinski # # f72fd50ed5cfdcc017f3ccb534543a122f0ec507 # drivers/net/pcs/pcs-xpcs.c | 11 +++++------ # 1 file changed, 5 insertions(+), 6 deletions(-) # # Author: Russell King (Oracle) (Thu 10 Oct 18:10:56 BST 2024) # Committer: Russell King (Oracle) (Thu 10 Oct 18:14:15 BST 2024) # # net: pcs: xpcs: move PCS reset to .pcs_pre_config() # # Move the PCS reset to .pcs_pre_config() rather than at creation time, # which means we call the reset function with the interface that we're # actually going to be using to talk to the downstream device. # # Reviewed-by: Vladimir Oltean # Tested-by: Vladimir Oltean # sja1105 # Signed-off-by: Russell King (Oracle) # Tested-by: for them? # Link: https://patch.msgid.link/E1svfMA-005ZI3-Va@rmk-PC.armlinux.org.uk # Signed-off-by: Jakub Kicinski # # 8984bddae8e8e6768b10bcec7268a293f21812ca # drivers/net/pcs/pcs-xpcs.c | 39 +++++++++++++++++++++++++++++---------- # include/linux/pcs/pcs-xpcs.h | 1 + # 2 files changed, 30 insertions(+), 10 deletions(-) # # Author: Jiawen Wu (Thu 10 Oct 18:13:43 BST 2024) # Committer: Russell King (Oracle) (Thu 10 Oct 18:13:43 BST 2024) # # net: pcs: xpcs: fix the wrong register that was written back # # The value is read from the register TXGBE_RX_GEN_CTL3, and it should be # written back to TXGBE_RX_GEN_CTL3 when it changes some fields. # # Cc: stable@vger.kernel.org # Fixes: f629acc6f210 ("net: pcs: xpcs: support to switch mode for Wangxun NICs") # Signed-off-by: Jiawen Wu # Reported-by: Russell King (Oracle) # Reviewed-by: Russell King (Oracle) # Link: https://patch.msgid.link/20240924022857.865422-1-jiawenwu@trustnetic.com # Signed-off-by: Paolo Abeni # # 9a24481f885088efba8d59dfe3e016d6bf9dc18b # drivers/net/pcs/pcs-xpcs-wx.c | 2 +- # 1 file changed, 1 insertion(+), 1 deletion(-) # diff --git a/Documentation/devicetree/bindings/net/marvell,10g.yaml b/Documentation/devicetree/bindings/net/marvell,10g.yaml new file mode 100644 index 000000000000..9ec85333f4a4 --- /dev/null +++ b/Documentation/devicetree/bindings/net/marvell,10g.yaml @@ -0,0 +1,36 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/marvell,10g.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Marvell Alaska X family Ethernet PHYs + +maintainers: + - Russell King + +allOf: + - $ref: ethernet-phy.yaml# + +properties: + marvell,led-mode: + description: + An array of one to four 16-bit integers to write to the PHY LED + configuration registers. + $ref: /schemas/types.yaml#/definitions/uint16-array + minItems: 1 + maxItems: 4 + +additionalProperties: false + +examples: + - | + mdio { + #address-cells = <1>; + #size-cells = <0>; + ethernet-phy@0 { + reg = <0>; + compatible = "ethernet-phy-ieee802.3-c45"; + marvell,led-mode = /bits/ 16 <0x0129 0x095d 0x0855>; + }; + }; diff --git a/arch/arm/boot/dts/marvell/armada-388-clearfog-base.dts b/arch/arm/boot/dts/marvell/armada-388-clearfog-base.dts index f7daa3bc707e..0daa19415879 100644 --- a/arch/arm/boot/dts/marvell/armada-388-clearfog-base.dts +++ b/arch/arm/boot/dts/marvell/armada-388-clearfog-base.dts @@ -7,6 +7,7 @@ /dts-v1/; #include "armada-388-clearfog.dtsi" +#include "armada-38x-solidrun-microsom-emmc.dtsi" / { model = "SolidRun Clearfog Base A1"; @@ -66,3 +67,54 @@ rear_button_pins: rear-button-pins { marvell,function = "gpio"; }; }; + +/* +MPP +18: pu gpio pca9655 int +19: gpio phy reset +20: pu gpio sd0 detect +21: sd0:cmd +22: pd gpio mikro int +23: + +24: ua1:rxd mikro rx +25: ua1:txd mikro tx +26: pu i2c1:sck +27: pu i2c1:sda +28: sd0:clk +29: pd gpio mikro rst +30: +31: + +32: +33: +34: +35: +36: +37: sd0:d3 +38: sd0:d0 +39: sd0:d1 + +40: sd0:d2 +41: +42: +43: spi1:cs2 mikro cs +44: gpio rear button sw3 +45: ref:clk_out0 phy#0 clock +46: ref:clk_out1 phy#1 clock +47: + +48: gpio J18 spare gpio +49: gpio U10 I2C_IRQ(GNSS) +50: gpio board id? +51: +52: +53: +54: gpio mikro pwm +55: + +56: pu spi1:mosi mikro mosi +57: pd spi1:sck mikro sck +58: spi1:miso mikro miso +59: +*/ diff --git a/arch/arm/boot/dts/marvell/armada-388-clearfog.dts b/arch/arm/boot/dts/marvell/armada-388-clearfog.dts index 09bf2e6d4ed0..285d887d8b3f 100644 --- a/arch/arm/boot/dts/marvell/armada-388-clearfog.dts +++ b/arch/arm/boot/dts/marvell/armada-388-clearfog.dts @@ -14,23 +14,6 @@ / { "marvell,armada388", "marvell,armada385", "marvell,armada380"; - soc { - internal-regs { - usb3@f0000 { - /* CON2, nearest CPU, USB2 only. */ - status = "okay"; - }; - }; - - pcie { - pcie@3,0 { - /* Port 2, Lane 0. CON2, nearest CPU. */ - reset-gpios = <&expander0 2 GPIO_ACTIVE_LOW>; - status = "okay"; - }; - }; - }; - gpio-keys { compatible = "gpio-keys"; pinctrl-0 = <&rear_button_pins>; @@ -48,12 +31,8 @@ button-0 { ð1 { /* ethernet@30000 */ + managed = "in-band-status"; phy-mode = "1000base-x"; - - fixed-link { - speed = <1000>; - full-duplex; - }; }; &expander0 { @@ -131,12 +110,8 @@ ethernet-port@4 { ethernet-port@5 { reg = <5>; ethernet = <ð1>; + managed = "in-band-status"; phy-mode = "1000base-x"; - - fixed-link { - speed = <1000>; - full-duplex; - }; }; ethernet-port@6 { @@ -154,6 +129,12 @@ fixed-link { }; }; +&pcie3 { + /* Port 2, Lane 0. CON2, nearest CPU. */ + reset-gpios = <&expander0 2 GPIO_ACTIVE_LOW>; + status = "okay"; +}; + &pinctrl { clearfog_dsa0_clk_pins: clearfog-dsa0-clk-pins { marvell,pins = "mpp46"; @@ -182,3 +163,58 @@ &spi1 { */ pinctrl-0 = <&spi1_pins &clearfog_spi1_cs_pins &mikro_spi_pins>; }; + +&usb3_0 { + /* CON2, nearest CPU, USB2 only. */ + status = "okay"; +}; +/* ++#define A38x_CUSTOMER_BOARD_1_MPP16_23 0x00400011 +MPP18: gpio ? (pca9655 int?) +MPP19: gpio ? (clkreq?) +MPP20: gpio ? (sd0 detect) +MPP21: sd0:cmd x sd0 +MPP22: gpio x mikro int +MPP23: gpio x switch irq ++#define A38x_CUSTOMER_BOARD_1_MPP24_31 0x22043333 +MPP24: ua1:rxd x mikro rx +MPP25: ua1:txd x mikro tx +MPP26: i2c1:sck x mikro sck +MPP27: i2c1:sda x mikro sda +MPP28: sd0:clk x sd0 +MPP29: gpio x mikro rst +MPP30: ge1:txd2 ? (config) +MPP31: ge1:txd3 ? (config) ++#define A38x_CUSTOMER_BOARD_1_MPP32_39 0x44400002 +MPP32: ge1:txctl ? (unused) +MPP33: gpio ? (pic_com0) +MPP34: gpio x rear button (pic_com1) +MPP35: gpio ? (pic_com2) +MPP36: gpio ? (unused) +MPP37: sd0:d3 x sd0 +MPP38: sd0:d0 x sd0 +MPP39: sd0:d1 x sd0 ++#define A38x_CUSTOMER_BOARD_1_MPP40_47 0x41144004 +MPP40: sd0:d2 x sd0 +MPP41: gpio x switch reset +MPP42: gpio ? sw1-1 +MPP43: spi1:cs2 x mikro cs +MPP44: sata3:prsnt ? (unused) +MPP45: ref:clk_out0 ? +MPP46: ref:clk_out1 x switch clk +MPP47: 4 ? (unused) ++#define A38x_CUSTOMER_BOARD_1_MPP48_55 0x40333333 +MPP48: tdm:pclk +MPP49: tdm:fsync +MPP50: tdm:drx +MPP51: tdm:dtx +MPP52: tdm:int +MPP53: tdm:rst +MPP54: gpio ? (pwm) +MPP55: spi1:cs1 x slic ++#define A38x_CUSTOMER_BOARD_1_MPP56_63 0x00004444 +MPP56: spi1:mosi x mikro mosi +MPP57: spi1:sck x mikro sck +MPP58: spi1:miso x mikro miso +MPP59: spi1:cs0 x w25q32 +*/ diff --git a/arch/arm/boot/dts/marvell/armada-388-clearfog.dtsi b/arch/arm/boot/dts/marvell/armada-388-clearfog.dtsi index f8a06ae4a3c9..606f9b4b6888 100644 --- a/arch/arm/boot/dts/marvell/armada-388-clearfog.dtsi +++ b/arch/arm/boot/dts/marvell/armada-388-clearfog.dtsi @@ -28,55 +28,6 @@ reg_3p3v: regulator-3p3v { regulator-always-on; }; - soc { - internal-regs { - sata@a8000 { - /* pinctrl? */ - status = "okay"; - }; - - sata@e0000 { - /* pinctrl? */ - status = "okay"; - }; - - sdhci@d8000 { - bus-width = <4>; - cd-gpios = <&gpio0 20 GPIO_ACTIVE_LOW>; - no-1-8-v; - pinctrl-0 = <µsom_sdhci_pins - &clearfog_sdhci_cd_pins>; - pinctrl-names = "default"; - status = "okay"; - vmmc-supply = <®_3p3v>; - wp-inverted; - }; - - usb@58000 { - /* CON3, nearest power. */ - status = "okay"; - }; - - usb3@f8000 { - /* CON7 */ - status = "okay"; - }; - }; - - pcie { - status = "okay"; - /* - * The two PCIe units are accessible through - * the mini-PCIe connectors on the board. - */ - pcie@2,0 { - /* Port 1, Lane 0. CON3, nearest power. */ - reset-gpios = <&expander0 1 GPIO_ACTIVE_LOW>; - status = "okay"; - }; - }; - }; - sfp: sfp { compatible = "sff,sfp"; i2c-bus = <&i2c1>; @@ -88,6 +39,16 @@ sfp: sfp { }; }; +&ahci0 { + /* pinctrl? */ + status = "okay"; +}; + +&ahci1 { + /* pinctrl? */ + status = "okay"; +}; + ð1 { /* ethernet@30000 */ bm,pool-long = <2>; @@ -200,6 +161,20 @@ &i2c1 { status = "okay"; }; +&pciec { + status = "okay"; +}; + +/* + * The two PCIe units are accessible through + * the mini-PCIe connectors on the board. + */ +&pcie2 { + /* Port 1, Lane 0. CON3, nearest power. */ + reset-gpios = <&expander0 1 GPIO_ACTIVE_LOW>; + status = "okay"; +}; + &pinctrl { clearfog_i2c1_pins: i2c1-pins { /* SFP, PCIe, mSATA, mikrobus */ @@ -225,6 +200,18 @@ mikro_uart_pins: mikro-uart-pins { }; }; +&sdhci { + bus-width = <4>; + cd-gpios = <&gpio0 20 GPIO_ACTIVE_LOW>; + no-1-8-v; + pinctrl-0 = <µsom_sdhci_pins + &clearfog_sdhci_cd_pins>; + pinctrl-names = "default"; + status = "okay"; + vmmc-supply = <®_3p3v>; + wp-inverted; +}; + &spi1 { /* * Add SPI CS pins for clearfog: @@ -243,3 +230,13 @@ &uart1 { pinctrl-names = "default"; status = "okay"; }; + +&usb0 { + /* CON3, nearest power. */ + status = "okay"; +}; + +&usb3_1 { + /* CON7 */ + status = "okay"; +}; diff --git a/arch/arm/boot/dts/marvell/armada-38x-solidrun-microsom-emmc.dtsi b/arch/arm/boot/dts/marvell/armada-38x-solidrun-microsom-emmc.dtsi new file mode 100644 index 000000000000..6b623b6aa602 --- /dev/null +++ b/arch/arm/boot/dts/marvell/armada-38x-solidrun-microsom-emmc.dtsi @@ -0,0 +1,62 @@ +/* + * Device Tree file for SolidRun Armada 38x Microsom add-on for eMMC + * + * Copyright (C) 2015 Russell King + * + * This board is in development; the contents of this file work with + * the A1 rev 2.0 of the board, which does not represent final + * production board. Things will change, don't expect this file to + * remain compatible info the future. + * + * This file is dual-licensed: you can use it either under the terms + * of the GPL or the X11 license, at your option. Note that this dual + * licensing only applies to this file, and not this project as a + * whole. + * + * a) This file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This file is distributed in the hope that it will be useful + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Or, alternatively + * + * b) Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED , WITHOUT WARRANTY OF ANY KIND + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +/ { + soc { + internal-regs { + sdhci@d8000 { + bus-width = <4>; + no-1-8-v; + non-removable; + pinctrl-0 = <µsom_sdhci_pins>; + pinctrl-names = "default"; + status = "okay"; + wp-inverted; + }; + }; + }; +}; diff --git a/arch/arm/boot/dts/marvell/armada-38x.dtsi b/arch/arm/boot/dts/marvell/armada-38x.dtsi index 1181b13deabc..76d7d91c7264 100644 --- a/arch/arm/boot/dts/marvell/armada-38x.dtsi +++ b/arch/arm/boot/dts/marvell/armada-38x.dtsi @@ -356,8 +356,9 @@ gateclk: clock-gating-control@18220 { comphy: phy@18300 { compatible = "marvell,armada-380-comphy"; - reg-names = "comphy", "conf"; - reg = <0x18300 0x100>, <0x18460 4>; + reg-names = "comphy", "pipe", "conf"; + reg = <0x18300 0x100>, <0xa0000 0x3000>, + <0x18460 4>; #address-cells = <1>; #size-cells = <0>; diff --git a/arch/arm64/boot/dts/marvell/Makefile b/arch/arm64/boot/dts/marvell/Makefile index ce751b5028e2..700e6752c5fb 100644 --- a/arch/arm64/boot/dts/marvell/Makefile +++ b/arch/arm64/boot/dts/marvell/Makefile @@ -26,6 +26,7 @@ dtb-$(CONFIG_ARCH_MVEBU) += cn9132-db.dtb dtb-$(CONFIG_ARCH_MVEBU) += cn9132-db-B.dtb dtb-$(CONFIG_ARCH_MVEBU) += cn9130-crb-A.dtb dtb-$(CONFIG_ARCH_MVEBU) += cn9130-crb-B.dtb +dtb-$(CONFIG_ARCH_MVEBU) += cn9130-cf-base.dtb dtb-$(CONFIG_ARCH_MVEBU) += ac5x-rd-carrier-cn9131.dtb dtb-$(CONFIG_ARCH_MVEBU) += ac5-98dx35xx-rd.dtb dtb-$(CONFIG_ARCH_MVEBU) += cn9130-cf-base.dtb 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/arch/arm64/boot/dts/marvell/cn9130-cf-base.dts b/arch/arm64/boot/dts/marvell/cn9130-cf-base.dts index 788a5c302b17..ebc45b3d2a48 100644 --- a/arch/arm64/boot/dts/marvell/cn9130-cf-base.dts +++ b/arch/arm64/boot/dts/marvell/cn9130-cf-base.dts @@ -52,7 +52,7 @@ rfkill-m2-wwan { }; }; -/* SRDS #3 - SGMII 1GE */ +/* SRDS #3 - SGMII 1GE on carrier board */ &cp0_eth1 { phy = <&phy1>; phys = <&cp0_comphy3 1>; @@ -111,6 +111,7 @@ phy1: ethernet-phy@1 { * - on-state: low * - off-state: high (not hi-z, to avoid residual glow) */ + /* marvell,reg-init = <3 16 0 0x0064>; */ marvell,reg-init = <3 16 0xf000 0x0a61>, <3 17 0x003f 0x000a>; @@ -136,8 +137,10 @@ led@1 { }; &cp0_pinctrl { - pinctrl-0 = <&sim_select_pins>; - pintrl-names = "default"; + expander0_pins: cp0-expander0-pins { + marvell,pins = "mpp4"; + marvell,function = "gpio"; + }; rear_button_pins: cp0-rear-button-pins { marvell,pins = "mpp31"; @@ -162,6 +165,11 @@ &cp0_usb3_1 { }; &expander0 { + pinctrl-0 = <&expander0_pins>; + pinctrl-names = "default"; + interrupt-parent = <&cp0_gpio1>; + interrupts = <4 IRQ_TYPE_LEVEL_LOW>; + m2-full-card-power-off-hog { gpio-hog; gpios = <2 GPIO_ACTIVE_LOW>; diff --git a/arch/arm64/boot/dts/marvell/cn9130-cf.dtsi b/arch/arm64/boot/dts/marvell/cn9130-cf.dtsi index ad0ab34b6602..49d5d9111454 100644 --- a/arch/arm64/boot/dts/marvell/cn9130-cf.dtsi +++ b/arch/arm64/boot/dts/marvell/cn9130-cf.dtsi @@ -6,6 +6,8 @@ * */ +#include + / { aliases { /* label nics same order as armada 388 clearfog */ @@ -16,6 +18,14 @@ aliases { mmc1 = &cp0_sdhci0; }; + reg_3p3v: regulator-3p3v { + compatible = "regulator-fixed"; + regulator-name = "3P3V"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + reg_usb3_vbus0: regulator-usb3-vbus0 { compatible = "regulator-fixed"; regulator-name = "vbus0"; @@ -33,6 +43,23 @@ sfp: sfp { tx-fault-gpios = <&expander0 13 GPIO_ACTIVE_HIGH>; maximum-power-milliwatt = <2000>; }; + + rfkill-gnss { + compatible = "rfkill-gpio"; + label = "m.2 GNSS"; + radio-type = "gps"; + /* rfkill-gpio inverts internally */ + shutdown-gpios = <&expander0 9 GPIO_ACTIVE_HIGH>; + }; + + /* M.2 is B-keyed, so w-disable is for WWAN */ + rfkill-wwan { + compatible = "rfkill-gpio"; + label = "m.2 WWAN"; + radio-type = "wwan"; + /* rfkill-gpio inverts internally */ + shutdown-gpios = <&expander0 8 GPIO_ACTIVE_HIGH>; + }; }; /* SRDS #2 - SFP+ 10GE */ @@ -60,15 +87,23 @@ pcie2-0-clkreq-hog { gpio-hog; gpios = <0 GPIO_ACTIVE_LOW>; input; - line-name = "pcie2.0-clkreq"; + line-name = "pcie1.0-clkreq"; + }; + + m2-ful-card-power-off { + gpio-hog; + gpios = <2 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "m2-ful-card-power-off"; }; /* CON3 */ + /* mini-PCIe can be either WWAN or WLAN */ pcie2-0-w-disable-hog { gpio-hog; gpios = <3 GPIO_ACTIVE_LOW>; output-low; - line-name = "pcie2.0-w-disable"; + line-name = "pcie1.0-w-disable"; }; usb3-ilimit-hog { @@ -78,6 +113,20 @@ usb3-ilimit-hog { line-name = "usb3-current-limit"; }; + usb3-power-hog { + gpio-hog; + gpios = <6 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "usb3-power"; + }; + + m2-reset-hog { + gpio-hog; + gpios = <10 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "m.2 reset"; + }; + m2-devslp-hog { gpio-hog; gpios = <11 GPIO_ACTIVE_HIGH>; @@ -108,7 +157,7 @@ &cp0_i2c1 { * PCIe uses ARP to assign addresses, or 0x63-0x64. */ clock-frequency = <100000>; - pinctrl-0 = <&cp0_i2c1_pins>; + pinctrl-0 = <&clearfog_i2c1_pins>; pinctrl-names = "default"; status = "okay"; }; @@ -123,7 +172,7 @@ &cp0_pcie2 { }; &cp0_pinctrl { - cp0_i2c1_pins: cp0-i2c1-pins { + clearfog_i2c1_pins: cp0-i2c1-pins { marvell,pins = "mpp35", "mpp36"; marvell,function = "i2c1"; }; @@ -161,10 +210,13 @@ &cp0_sata0 { /* microSD */ &cp0_sdhci0 { + cd-gpios = <&cp0_gpio2 11 GPIO_ACTIVE_LOW>; pinctrl-0 = <&cp0_mmc0_pins>; pinctrl-names = "default"; bus-width = <4>; no-1-8-v; + vmmc-supply = <®_3p3v>; + vqmmc-supply = <®_3p3v>; status = "okay"; }; @@ -173,6 +225,13 @@ &cp0_spi1 { pinctrl-0 = <&cp0_spi1_pins &mikro_spi_pins>; }; +/* mikrobus uart */ +&cp0_uart0 { + pinctrl-0 = <&mikro_uart_pins>; + pinctrl-names = "default"; + status = "okay"; +}; + /* * SRDS #1 - USB-3.0 Host on Type-A connector * USB-2.0 Host on mPCI-e connector (CON3) @@ -185,13 +244,13 @@ &cp0_usb3_0 { status = "okay"; }; -&cp0_utmi { +/* SRDS #4 - M.2 USB 3.0 */ +&cp0_usb3_1 { + phy-names = "usb"; + phys = <&cp0_comphy4 1>; status = "okay"; }; -/* mikrobus uart */ -&cp0_uart0 { - pinctrl-0 = <&mikro_uart_pins>; - pinctrl-names = "default"; +&cp0_utmi { status = "okay"; }; diff --git a/arch/arm64/boot/dts/marvell/cn9130-sr-som.dtsi b/arch/arm64/boot/dts/marvell/cn9130-sr-som.dtsi index 4676e3488f54..de1bc2997d52 100644 --- a/arch/arm64/boot/dts/marvell/cn9130-sr-som.dtsi +++ b/arch/arm64/boot/dts/marvell/cn9130-sr-som.dtsi @@ -59,6 +59,7 @@ ap_mmc0_pins: ap-mmc0-pins { }; }; +/* EMMC */ &ap_sdhci0 { bus-width = <8>; pinctrl-0 = <&ap_mmc0_pins>; @@ -80,12 +81,14 @@ &cp0_eth2 { status = "okay"; }; +/* For Assy=PHY_ETH */ &cp0_i2c0 { pinctrl-names = "default"; pinctrl-0 = <&cp0_i2c0_pins>; clock-frequency = <100000>; status = "okay"; + /* M24C02-WMN6TP / ST24C02WP */ som_eeprom: eeprom@53 { compatible = "atmel,24c02"; reg = <0x53>; @@ -97,8 +100,9 @@ &cp0_mdio { pinctrl-0 = <&cp0_mdio_pins>; status = "okay"; - /* assembly option */ + /* For Assy=PHY_ETH */ cp0_eth2_phy: ethernet-phy@0 { + marvell,reg-init = <3 16 0 0x0064>; reg = <0>; }; }; @@ -130,7 +134,7 @@ cp0_eth2_pins: cp0-ge2-rgmii-pins { marvell,function = "ge1"; }; - cp0_i2c0_pins: cp0-i2c0-pins { + cp0_i2c0_pins: cp0-i2c-pins-0 { marvell,pins = "mpp37", "mpp38"; marvell,function = "i2c0"; }; @@ -140,7 +144,7 @@ cp0_mdio_pins: cp0-mdio-pins { marvell,function = "ge"; }; - cp0_spi1_pins: cp0-spi1-pins { + cp0_spi1_pins: cp0-spi-pins-1 { marvell,pins = "mpp13", "mpp14", "mpp15", "mpp16"; marvell,function = "spi1"; }; diff --git a/drivers/cpuidle/cpuidle-mvebu-v7.c b/drivers/cpuidle/cpuidle-mvebu-v7.c index 563dba609b98..47873145421e 100644 --- a/drivers/cpuidle/cpuidle-mvebu-v7.c +++ b/drivers/cpuidle/cpuidle-mvebu-v7.c @@ -42,8 +42,12 @@ static __cpuidle int mvebu_v7_enter_idle(struct cpuidle_device *dev, cpu_pm_exit(); + /* + * If we failed to enter the desired state, indicate that we + * slept lightly. + */ if (ret) - return ret; + return 0; return index; } diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c index 0783fc121bbb..be2e5ee6bc5f 100644 --- a/drivers/net/dsa/b53/b53_common.c +++ b/drivers/net/dsa/b53/b53_common.c @@ -1426,10 +1426,10 @@ static void b53_phylink_mac_link_down(struct phylink_config *config, struct b53_device *dev = dp->ds->priv; int port = dp->index; - if (mode == MLO_AN_PHY) + if (phylink_mode_phy(mode)) return; - if (mode == MLO_AN_FIXED) { + if (phylink_mode_fixed(mode)) { b53_force_link(dev, port, false); return; } @@ -1452,12 +1452,13 @@ static void b53_phylink_mac_link_up(struct phylink_config *config, struct ethtool_keee *p = &dev->ports[dp->index].eee; int port = dp->index; - if (mode == MLO_AN_PHY) { + if (phylink_mode_phy(mode)) { /* Re-negotiate EEE if it was enabled already */ p->eee_enabled = b53_eee_init(ds, port, phydev); return; } + if (phylink_mode_fixed(mode)) { if (mode == MLO_AN_FIXED) { /* Force flow control on BCM5301x's CPU port */ if (is5301x(dev) && dsa_is_cpu_port(ds, port)) diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index 0e663ec0c12a..c3ba405b3c1a 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c @@ -897,7 +897,7 @@ static void bcm_sf2_sw_mac_link_up(struct phylink_config *config, core_writel(priv, reg, offset); - if (mode == MLO_AN_PHY && phydev) { + if (phylink_mode_phy(mode) && phydev) { p = &priv->dev->ports[port].eee; p->eee_enabled = b53_eee_init(dp->ds, port, phydev); } diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index ec18e68bf3a8..75a5ff988f0e 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -2439,7 +2439,8 @@ mt7530_setup(struct dsa_switch *ds) * on all ports until they're enabled later. */ mt7530_rmw(priv, MT753X_PMCR_P(i), PMCR_LINK_SETTINGS_MASK | - MT7530_FORCE_MODE, MT7530_FORCE_MODE); + MT7530_FORCE_MODE | PMCR_FORCE_EEE1G | + PMCR_FORCE_EEE100, MT7530_FORCE_MODE); /* Disable forwarding by default on all ports */ mt7530_rmw(priv, MT7530_PCR_P(i), PCR_MATRIX_MASK, @@ -2929,28 +2930,65 @@ static void mt753x_phylink_mac_link_up(struct phylink_config *config, mcr |= PMCR_FORCE_RX_FC_EN; } - if (mode == MLO_AN_PHY && phydev && phy_init_eee(phydev, false) >= 0) { - switch (speed) { - case SPEED_1000: - case SPEED_2500: - mcr |= PMCR_FORCE_EEE1G; - break; - case SPEED_100: - mcr |= PMCR_FORCE_EEE100; - break; - } - } - mt7530_set(priv, MT753X_PMCR_P(dp->index), mcr); } +static void mt753x_phylink_mac_disable_tx_lpi(struct phylink_config *config) +{ + struct dsa_port *dp = dsa_phylink_to_port(config); + struct mt7530_priv *priv = dp->ds->priv; + + mt7530_clear(priv, MT753X_PMCR_P(dp->index), + PMCR_FORCE_EEE1G | PMCR_FORCE_EEE100); +} + +static void mt753x_phylink_mac_enable_tx_lpi(struct phylink_config *config, + u32 timer) +{ + struct dsa_port *dp = dsa_phylink_to_port(config); + struct mt7530_priv *priv = dp->ds->priv; + u32 val; + + /* If the timer is zero, then set LPI_MODE_EN, which allows the + * system to enter LPI mode immediately rather than waiting for + * the LPI threshold. + */ + if (!timer) + val = LPI_MODE_EN; + else if (FIELD_FIT(LPI_THRESH_MASK, timer)) + val = FIELD_PREP(LPI_THRESH_MASK, timer); + else + val = LPI_THRESH_MASK; + + mt7530_rmw(priv, MT753X_PMEEECR_P(port), + LPI_THRESH_MASK | LPI_MODE_EN, val); + + /* FIXME: mt7531 docs say that bits 26 and 25 need to be set to + * enable EEE forcing. The PMCR_FORCE_EEE* bits just determine + * whether we force-enable or force-disable these modes. + */ + mt7530_set(priv, MT753X_PMCR_P(port), + PMCR_FORCE_EEE1G | PMCR_FORCE_EEE100); +} + static void mt753x_phylink_get_caps(struct dsa_switch *ds, int port, struct phylink_config *config) { struct mt7530_priv *priv = ds->priv; + u32 eeecr; config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE; + config->lpi_capabilities = MAC_100FD | MAC_1000FD MAC_2500FD; + config->lpi_timer_limit_us = FIELD_GET(LPI_THRESH_MASK, + LPI_THRESH_MASK); + + eeecr = mt7530_read(priv, MT753X_PMEEECR_P(port)); + /* tx_lpi_timer should be in microseconds. The time units for + * LPI threshold are unspecified. + */ + config->eee.tx_lpi_timer = FIELD_GET(LPI_THRESH_MASK, eeecr); + priv->info->mac_port_get_caps(ds, port, config); } @@ -3057,36 +3095,6 @@ mt753x_setup(struct dsa_switch *ds) return ret; } -static int mt753x_get_mac_eee(struct dsa_switch *ds, int port, - struct ethtool_keee *e) -{ - struct mt7530_priv *priv = ds->priv; - u32 eeecr = mt7530_read(priv, MT753X_PMEEECR_P(port)); - - e->tx_lpi_enabled = !(eeecr & LPI_MODE_EN); - e->tx_lpi_timer = LPI_THRESH_GET(eeecr); - - return 0; -} - -static int mt753x_set_mac_eee(struct dsa_switch *ds, int port, - struct ethtool_keee *e) -{ - struct mt7530_priv *priv = ds->priv; - u32 set, mask = LPI_THRESH_MASK | LPI_MODE_EN; - - if (e->tx_lpi_timer > 0xFFF) - return -EINVAL; - - set = LPI_THRESH_SET(e->tx_lpi_timer); - if (!e->tx_lpi_enabled) - /* Force LPI Mode without a delay */ - set |= LPI_MODE_EN; - mt7530_rmw(priv, MT753X_PMEEECR_P(port), mask, set); - - return 0; -} - static void mt753x_conduit_state_change(struct dsa_switch *ds, const struct net_device *conduit, @@ -3163,8 +3171,6 @@ const struct dsa_switch_ops mt7530_switch_ops = { .port_mirror_add = mt753x_port_mirror_add, .port_mirror_del = mt753x_port_mirror_del, .phylink_get_caps = mt753x_phylink_get_caps, - .get_mac_eee = mt753x_get_mac_eee, - .set_mac_eee = mt753x_set_mac_eee, .conduit_state_change = mt753x_conduit_state_change, }; EXPORT_SYMBOL_GPL(mt7530_switch_ops); @@ -3174,6 +3180,8 @@ static const struct phylink_mac_ops mt753x_phylink_mac_ops = { .mac_config = mt753x_phylink_mac_config, .mac_link_down = mt753x_phylink_mac_link_down, .mac_link_up = mt753x_phylink_mac_link_up, + .mac_disable_tx_lpi = mt753x_phylink_mac_disable_tx_lpi, + .mac_enable_tx_lpi = mt753x_phylink_mac_enable_tx_lpi, }; const struct mt753x_info mt753x_table[] = { diff --git a/drivers/net/dsa/mt7530.h b/drivers/net/dsa/mt7530.h index 28592123070b..9adb1a7a1fa0 100644 --- a/drivers/net/dsa/mt7530.h +++ b/drivers/net/dsa/mt7530.h @@ -352,12 +352,8 @@ enum mt7530_vlan_port_acc_frm { MT7531_FORCE_MODE_SPD | \ MT7531_FORCE_MODE_DPX | \ MT7531_FORCE_MODE_RX_FC | \ - MT7531_FORCE_MODE_TX_FC | \ - MT7531_FORCE_MODE_EEE100 | \ - MT7531_FORCE_MODE_EEE1G) + MT7531_FORCE_MODE_TX_FC) #define PMCR_LINK_SETTINGS_MASK (PMCR_MAC_TX_EN | PMCR_MAC_RX_EN | \ - PMCR_FORCE_EEE1G | \ - PMCR_FORCE_EEE100 | \ PMCR_FORCE_RX_FC_EN | \ PMCR_FORCE_TX_FC_EN | \ PMCR_FORCE_SPEED_1000 | \ diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 5b4e2ce5470d..1164e8b14097 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -678,6 +678,9 @@ static void mv88e6352_phylink_get_caps(struct mv88e6xxx_chip *chip, int port, config->mac_capabilities = MAC_SYM_PAUSE | MAC_10 | MAC_100 | MAC_1000FD; + config->lpi_capabilities = MAC_1000FD | MAC_100FD; + config->eee.eee_enabled = true; + config->eee.tx_lpi_enabled = true; /* Port 4 supports automedia if the serdes is associated with it. */ if (port == 4) { @@ -867,7 +870,7 @@ mv88e6xxx_mac_select_pcs(struct phylink_config *config, { struct dsa_port *dp = dsa_phylink_to_port(config); struct mv88e6xxx_chip *chip = dp->ds->priv; - struct phylink_pcs *pcs = ERR_PTR(-EOPNOTSUPP); + struct phylink_pcs *pcs = NULL; if (chip->info->ops->pcs_ops) pcs = chip->info->ops->pcs_ops->pcs_select(chip, dp->index, @@ -888,7 +891,7 @@ static int mv88e6xxx_mac_prepare(struct phylink_config *config, * is not forced down. Force the link down while we reconfigure the * interface mode. */ - if (mode == MLO_AN_INBAND && + if (phylink_mode_inband(mode) && chip->ports[port].interface != interface && chip->info->ops->port_set_link) { mv88e6xxx_reg_lock(chip); @@ -911,7 +914,7 @@ static void mv88e6xxx_mac_config(struct phylink_config *config, mv88e6xxx_reg_lock(chip); - if (mode != MLO_AN_PHY || !mv88e6xxx_phy_is_internal(chip, port)) { + if (!phylink_mode_phy(mode) || !mv88e6xxx_phy_is_internal(chip, port)) { err = mv88e6xxx_port_config_interface(chip, port, state->interface); if (err && err != -EOPNOTSUPP) @@ -942,9 +945,10 @@ static int mv88e6xxx_mac_finish(struct phylink_config *config, mv88e6xxx_reg_lock(chip); if (chip->info->ops->port_set_link && - ((mode == MLO_AN_INBAND && + ((phylink_mode_inband(mode) && chip->ports[port].interface != interface) || - (mode == MLO_AN_PHY && mv88e6xxx_port_ppu_updates(chip, port)))) + (phylink_mode_phy(mode) && + mv88e6xxx_port_ppu_updates(chip, port)))) err = chip->info->ops->port_set_link(chip, port, LINK_UNFORCED); mv88e6xxx_reg_unlock(chip); @@ -971,7 +975,7 @@ static void mv88e6xxx_mac_link_down(struct phylink_config *config, * updated by the switch or if we are using fixed-link mode. */ if ((!mv88e6xxx_port_ppu_updates(chip, port) || - mode == MLO_AN_FIXED) && ops->port_sync_link) + phylink_mode_fixed(mode)) && ops->port_sync_link) err = ops->port_sync_link(chip, port, mode, false); if (!err && ops->port_set_speed_duplex) @@ -1004,7 +1008,7 @@ static void mv88e6xxx_mac_link_up(struct phylink_config *config, * mode. */ if (!mv88e6xxx_port_ppu_updates(chip, port) || - mode == MLO_AN_FIXED) { + phylink_mode_fixed(mode)) { if (ops->port_set_speed_duplex) { err = ops->port_set_speed_duplex(chip, port, speed, duplex); @@ -1023,6 +1027,49 @@ static void mv88e6xxx_mac_link_up(struct phylink_config *config, "p%d: failed to configure MAC link up\n", port); } +static void mv88e6xxx_mac_disable_tx_lpi(struct phylink_config *config) +{ + struct dsa_port *dp = dsa_phylink_to_port(config); + struct mv88e6xxx_chip *chip = dp->ds->priv; + int port = dp->index; + int err; + + if (!chip->info->ops->port_set_eee) + return; + + mv88e6xxx_reg_lock(chip); + err = chip->info->ops->port_set_eee(chip, port, EEE_FORCE_DISABLE); + mv88e6xxx_reg_unlock(chip); + + if (err) + dev_err(chip->dev, "p%d: failed to set EEE mode: %pe\n", port, + ERR_PTR(err)); +} + +static void mv88e6xxx_mac_enable_tx_lpi(struct phylink_config *config, + u32 timer) +{ + struct dsa_port *dp = dsa_phylink_to_port(config); + struct mv88e6xxx_chip *chip = dp->ds->priv; + int port = dp->index; + int eee, err; + + if (!chip->info->ops->port_set_eee) + return; + + mv88e6xxx_reg_lock(chip); + if (mv88e6xxx_port_ppu_updates(chip, port)) + eee = EEE_UNFORCED; + else + eee = EEE_FORCE_DISABLE; + err = chip->info->ops->port_set_eee(chip, port, eee); + mv88e6xxx_reg_unlock(chip); + + if (err) + dev_err(chip->dev, "p%d: failed to set EEE mode: %pe\n", port, + ERR_PTR(err)); +} + static int mv88e6xxx_stats_snapshot(struct mv88e6xxx_chip *chip, int port) { int err; @@ -3368,6 +3415,20 @@ static int mv88e6xxx_setup_upstream_port(struct mv88e6xxx_chip *chip, int port) return 0; } +static int mv88e6xxx_setup_led(struct mv88e6xxx_chip *chip, int port) +{ + int err; + + /* LED0 = link/activity, LED1 = 10/100 */ + err = mv88e6xxx_wait_bit(chip, chip->info->port_base_addr + port, + MV88E6XXX_PORT_LED_CONTROL, 15, 0); + if (err) + return err; + + return mv88e6xxx_write(chip, chip->info->port_base_addr + port, + MV88E6XXX_PORT_LED_CONTROL, 0x80b3); +} + static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) { struct device_node *phy_handle = NULL; @@ -3420,6 +3481,12 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) if (err) return err; + if (chip->info->num_gpio) { + err = mv88e6xxx_setup_led(chip, port); + if (err) + return err; + } + /* Port Control 2: don't force a good FCS, set the MTU size to * 10222 bytes, disable 802.1q tags checking, don't discard * tagged or untagged frames on this port, skip destination @@ -3919,10 +3986,13 @@ static int mv88e6xxx_mdios_register(struct mv88e6xxx_chip *chip) return 0; } +#include "mv88e6xxx_debugfs.c" + static void mv88e6xxx_teardown(struct dsa_switch *ds) { struct mv88e6xxx_chip *chip = ds->priv; + mv88e6xxx_remove_debugfs(chip); mv88e6xxx_teardown_devlink_params(ds); dsa_devlink_resources_unregister(ds); mv88e6xxx_teardown_devlink_regions_global(ds); @@ -4062,6 +4132,8 @@ static int mv88e6xxx_setup(struct dsa_switch *ds) if (err) goto unlock; + mv88e6xxx_init_debugfs(chip); + unlock: mv88e6xxx_reg_unlock(chip); @@ -4582,6 +4654,7 @@ static const struct mv88e6xxx_ops mv88e6172_ops = { .phy_read_c45 = mv88e6xxx_g2_smi_phy_read_c45, .phy_write_c45 = mv88e6xxx_g2_smi_phy_write_c45, .port_set_link = mv88e6xxx_port_set_link, + .port_set_eee = mv88e6xxx_port_set_eee, .port_sync_link = mv88e6xxx_port_sync_link, .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, .port_set_speed_duplex = mv88e6352_port_set_speed_duplex, @@ -4684,6 +4757,7 @@ static const struct mv88e6xxx_ops mv88e6176_ops = { .phy_read_c45 = mv88e6xxx_g2_smi_phy_read_c45, .phy_write_c45 = mv88e6xxx_g2_smi_phy_write_c45, .port_set_link = mv88e6xxx_port_set_link, + .port_set_eee = mv88e6xxx_port_set_eee, .port_sync_link = mv88e6xxx_port_sync_link, .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, .port_set_speed_duplex = mv88e6352_port_set_speed_duplex, @@ -4959,6 +5033,7 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { .phy_read_c45 = mv88e6xxx_g2_smi_phy_read_c45, .phy_write_c45 = mv88e6xxx_g2_smi_phy_write_c45, .port_set_link = mv88e6xxx_port_set_link, + .port_set_eee = mv88e6xxx_port_set_eee, .port_sync_link = mv88e6xxx_port_sync_link, .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, .port_set_speed_duplex = mv88e6352_port_set_speed_duplex, @@ -5381,6 +5456,7 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { .phy_read_c45 = mv88e6xxx_g2_smi_phy_read_c45, .phy_write_c45 = mv88e6xxx_g2_smi_phy_write_c45, .port_set_link = mv88e6xxx_port_set_link, + .port_set_eee = mv88e6xxx_port_set_eee, .port_sync_link = mv88e6xxx_port_sync_link, .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, .port_set_speed_duplex = mv88e6352_port_set_speed_duplex, @@ -5570,6 +5646,7 @@ static const struct mv88e6xxx_ops mv88e6393x_ops = { .phy_read_c45 = mv88e6xxx_g2_smi_phy_read_c45, .phy_write_c45 = mv88e6xxx_g2_smi_phy_write_c45, .port_set_link = mv88e6xxx_port_set_link, + /* no port_set_eee due to mv88e6393x errata 4.5 */ .port_sync_link = mv88e6xxx_port_sync_link, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, .port_set_speed_duplex = mv88e6393x_port_set_speed_duplex, @@ -7054,6 +7131,8 @@ static const struct phylink_mac_ops mv88e6xxx_phylink_mac_ops = { .mac_finish = mv88e6xxx_mac_finish, .mac_link_down = mv88e6xxx_mac_link_down, .mac_link_up = mv88e6xxx_mac_link_up, + .mac_disable_tx_lpi = mv88e6xxx_mac_disable_tx_lpi, + .mac_enable_tx_lpi = mv88e6xxx_mac_enable_tx_lpi, }; static const struct dsa_switch_ops mv88e6xxx_switch_ops = { diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h index c34caf9815c5..203ab6ececb0 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.h +++ b/drivers/net/dsa/mv88e6xxx/chip.h @@ -432,6 +432,8 @@ struct mv88e6xxx_chip { /* Bridge MST to SID mappings */ struct list_head msts; + + struct dentry *dbgfs; }; struct mv88e6xxx_bus_ops { @@ -512,6 +514,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/mv88e6xxx_debugfs.c b/drivers/net/dsa/mv88e6xxx/mv88e6xxx_debugfs.c new file mode 100644 index 000000000000..219a2c015a84 --- /dev/null +++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx_debugfs.c @@ -0,0 +1,1102 @@ +#include +#include + +#define GLOBAL2_PVT_ADDR 0x0b +#define GLOBAL2_PVT_ADDR_BUSY BIT(15) +#define GLOBAL2_PVT_ADDR_OP_INIT_ONES ((0x01 << 12) | GLOBAL2_PVT_ADDR_BUSY) +#define GLOBAL2_PVT_ADDR_OP_WRITE_PVLAN ((0x03 << 12) | GLOBAL2_PVT_ADDR_BUSY) +#define GLOBAL2_PVT_ADDR_OP_READ ((0x04 << 12) | GLOBAL2_PVT_ADDR_BUSY) +#define GLOBAL2_PVT_DATA 0x0c + +#define ADDR_GLOBAL2 0x1c + +static int mv88e6xxx_serdes_read(struct mv88e6xxx_chip *chip, int reg, u16 *val) +{ + return mv88e6xxx_phy_page_read(chip, MV88E6352_ADDR_SERDES, + MV88E6352_SERDES_PAGE_FIBER, + reg, val); +} + +static int mv88e6xxx_serdes_write(struct mv88e6xxx_chip *chip, int reg, u16 val) +{ + return mv88e6xxx_phy_page_write(chip, MV88E6352_ADDR_SERDES, + MV88E6352_SERDES_PAGE_FIBER, + reg, val); +} + +static int _mv88e6xxx_pvt_wait(struct mv88e6xxx_chip *chip) +{ + return mv88e6xxx_wait_mask(chip, ADDR_GLOBAL2, GLOBAL2_PVT_ADDR, + GLOBAL2_PVT_ADDR_BUSY, 0); +} + +static int _mv88e6xxx_pvt_cmd(struct mv88e6xxx_chip *chip, int src_dev, + int src_port, u16 op) +{ + u16 reg = op; + int err; + + /* 9-bit Cross-chip PVT pointer: with GLOBAL2_MISC_5_BIT_PORT cleared, + * source device is 5-bit, source port is 4-bit. + */ + reg |= (src_dev & 0x1f) << 4; + reg |= (src_port & 0xf); + + err = mv88e6xxx_g2_write(chip, GLOBAL2_PVT_ADDR, reg); + if (err) + return err; + + return _mv88e6xxx_pvt_wait(chip); +} + +static int _mv88e6xxx_pvt_read(struct mv88e6xxx_chip *chip, int src_dev, + int src_port, u16 *data) +{ + int ret; + + ret = _mv88e6xxx_pvt_wait(chip); + if (ret < 0) + return ret; + + ret = _mv88e6xxx_pvt_cmd(chip, src_dev, src_port, + GLOBAL2_PVT_ADDR_OP_READ); + if (ret < 0) + return ret; + + return mv88e6xxx_g2_read(chip, GLOBAL2_PVT_DATA, data); +} + +static int _mv88e6xxx_pvt_write(struct mv88e6xxx_chip *chip, int src_dev, + int src_port, u16 data) +{ + int err; + + err = _mv88e6xxx_pvt_wait(chip); + if (err) + return err; + + err = mv88e6xxx_g2_write(chip, GLOBAL2_PVT_DATA, data); + if (err) + return err; + + return _mv88e6xxx_pvt_cmd(chip, src_dev, src_port, + GLOBAL2_PVT_ADDR_OP_WRITE_PVLAN); +} + +static int mv88e6xxx_regs_show(struct seq_file *s, void *p) +{ + struct mv88e6xxx_chip *chip = s->private; + int port, reg, ret; + u16 data; + + seq_puts(s, " GLOBAL GLOBAL2 SERDES "); + for (port = 0; port < mv88e6xxx_num_ports(chip); port++) + seq_printf(s, " %2d ", port); + seq_puts(s, "\n"); + + mutex_lock(&chip->reg_lock); + + for (reg = 0; reg < 32; reg++) { + seq_printf(s, "%2x:", reg); + + ret = mv88e6xxx_g1_read(chip, reg, &data); + if (ret < 0) + goto unlock; + seq_printf(s, " %4x ", data); + + ret = mv88e6xxx_g2_read(chip, reg, &data); + if (ret < 0) + goto unlock; + seq_printf(s, " %4x ", data); + + if (reg != MV88E6XXX_PHY_PAGE) { + ret = mv88e6xxx_serdes_read(chip, reg, &data); + if (ret < 0) + goto unlock; + } else { + data = 0; + } + seq_printf(s, " %4x ", data); + + /* Port regs 0x1a-0x1f are reserved in 6185 family */ + if (chip->info->family == MV88E6XXX_FAMILY_6185 && reg > 25) { + for (port = 0; port < mv88e6xxx_num_ports(chip); ++port) + seq_printf(s, "%4c ", '-'); + seq_puts(s, "\n"); + continue; + } + + for (port = 0; port < mv88e6xxx_num_ports(chip); ++port) { + ret = mv88e6xxx_port_read(chip, port, reg, &data); + if (ret < 0) + goto unlock; + + seq_printf(s, "%4x ", data); + } + + seq_puts(s, "\n"); + } + + ret = 0; +unlock: + mutex_unlock(&chip->reg_lock); + + return ret; +} + +static ssize_t mv88e6xxx_regs_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct seq_file *s = file->private_data; + struct mv88e6xxx_chip *chip = s->private; + char cmd[32], name[32] = { 0 }; + unsigned int port, reg, val; + int ret; + + if (count > sizeof(name) - 1) + return -EINVAL; + + if (copy_from_user(cmd, buf, sizeof(cmd))) + return -EFAULT; + + ret = sscanf(cmd, "%s %x %x", name, ®, &val); + if (ret != 3) + return -EINVAL; + + if (reg > 0x1f || val > 0xffff) + return -ERANGE; + + mutex_lock(&chip->reg_lock); + + if (strcasecmp(name, "GLOBAL") == 0) + ret = mv88e6xxx_g1_write(chip, reg, val); + else if (strcasecmp(name, "GLOBAL2") == 0) + ret = mv88e6xxx_g2_write(chip, reg, val); + else if (strcasecmp(name, "SERDES") == 0) + ret = mv88e6xxx_serdes_write(chip, reg, val); + else if (kstrtouint(name, 10, &port) == 0 && port < mv88e6xxx_num_ports(chip)) + ret = mv88e6xxx_port_write(chip, port, reg, val); + else + ret = -EINVAL; + + mutex_unlock(&chip->reg_lock); + + return ret < 0 ? ret : count; +} + +static int mv88e6xxx_regs_open(struct inode *inode, struct file *file) +{ + return single_open(file, mv88e6xxx_regs_show, inode->i_private); +} + +static const struct file_operations mv88e6xxx_regs_fops = { + .open = mv88e6xxx_regs_open, + .read = seq_read, + .write = mv88e6xxx_regs_write, + .llseek = no_llseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static int mv88e6xxx_name_show(struct seq_file *s, void *p) +{ + struct mv88e6xxx_chip *chip = s->private; + struct dsa_switch *ds = chip->ds; + struct dsa_switch_tree *dst = ds->dst; + struct dsa_port *dp; + int i; + + if (!ds->cd) + return 0; + + seq_puts(s, " Port Name\n"); + + list_for_each_entry(dp, &dst->ports, list) { + if (dp->ds != ds) + continue; + + i = dp->index; + if (!ds->cd->port_names[i]) + continue; + + seq_printf(s, "%4d %s", i, ds->cd->port_names[i]); + + if (dp->user) + seq_printf(s, " (%s)", netdev_name(dp->user)); + + seq_puts(s, "\n"); + } + + return 0; +} + +static int mv88e6xxx_name_open(struct inode *inode, struct file *file) +{ + return single_open(file, mv88e6xxx_name_show, inode->i_private); +} + +static const struct file_operations mv88e6xxx_name_fops = { + .open = mv88e6xxx_name_open, + .read = seq_read, + .llseek = no_llseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static int mv88e6xxx_atu_show(struct seq_file *s, void *p) +{ + struct mv88e6xxx_chip *chip = s->private; + struct mv88e6xxx_atu_entry addr; + const char *state; + int fid, i, err; + + seq_puts(s, " FID MAC Addr State Trunk? DPV/Trunk ID\n"); + + for (fid = 0; fid < mv88e6xxx_num_databases(chip); ++fid) { + addr.state = 0; + eth_broadcast_addr(addr.mac); + + do { + mutex_lock(&chip->reg_lock); + err = mv88e6xxx_g1_atu_getnext(chip, fid, &addr); + mutex_unlock(&chip->reg_lock); + if (err) + return err; + + if (addr.state == 0) + break; + + /* print ATU entry */ + seq_printf(s, "%4d %pM", fid, addr.mac); + + if (is_multicast_ether_addr(addr.mac)) { + switch (addr.state) { + case MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC_PO: + state = "MC_STATIC_PO"; + break; + case MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC_DA_MGMT_PO: + state = "MC_STATIC_MGMT_PO"; + break; + case MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC_AVB_NRL_PO: + state = "MC_STATIC_NRL_PO"; + break; + case MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC_POLICY_PO: + state = "MC_STATIC_POLICY_PO"; + break; + case MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC: + state = "MC_STATIC"; + break; + case MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC_DA_MGMT: + state = "MC_STATIC_MGMT"; + break; + case MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC_AVB_NRL: + state = "MC_STATIC_NRL"; + break; + case MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC_POLICY: + state = "MC_STATIC_POLICY"; + break; + case 0xb: case 0xa: case 0x9: case 0x8: + /* Reserved for future use */ + case 0x3: case 0x2: case 0x1: + /* Reserved for future use */ + case MV88E6XXX_G1_ATU_DATA_STATE_MC_UNUSED: + default: + state = "???"; + break; + } + } else { + switch (addr.state) { + case MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC_PO: + state = "UC_STATIC_PO"; + break; + case MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC: + state = "UC_STATIC"; + break; + case MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC_DA_MGMT_PO: + state = "UC_STATIC_MGMT_PO"; + break; + case MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC_DA_MGMT: + state = "UC_STATIC_MGMT"; + break; + case MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC_AVB_NRL_PO: + state = "UC_STATIC_NRL_PO"; + break; + case MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC_AVB_NRL: + state = "UC_STATIC_NRL"; + break; + case MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC_POLICY_PO: + state = "UC_STATIC_POLICY_PO"; + break; + case MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC_POLICY: + state = "UC_STATIC_POLICY"; + break; + case MV88E6XXX_G1_ATU_DATA_STATE_UC_AGE_7_NEWEST: + state = "Age 7 (newest)"; + break; + case MV88E6XXX_G1_ATU_DATA_STATE_UC_AGE_6: + state = "Age 6"; + break; + case MV88E6XXX_G1_ATU_DATA_STATE_UC_AGE_5: + state = "Age 5"; + break; + case MV88E6XXX_G1_ATU_DATA_STATE_UC_AGE_4: + state = "Age 4"; + break; + case MV88E6XXX_G1_ATU_DATA_STATE_UC_AGE_3: + state = "Age 3"; + break; + case MV88E6XXX_G1_ATU_DATA_STATE_UC_AGE_2: + state = "Age 2"; + break; + case MV88E6XXX_G1_ATU_DATA_STATE_UC_AGE_1_OLDEST: + state = "Age 1 (oldest)"; + break; + case MV88E6XXX_G1_ATU_DATA_STATE_UC_UNUSED: + default: + state = "???"; + break; + } + } + + seq_printf(s, " %19s", state); + + if (addr.trunk) { + seq_printf(s, " y %d", + addr.portvec); + } else { + seq_puts(s, " n "); + for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) + seq_printf(s, " %c", + addr.portvec & BIT(i) ? + 48 + i : '-'); + } + + seq_puts(s, "\n"); + } while (!is_broadcast_ether_addr(addr.mac)); + } + + return 0; +} + +static ssize_t mv88e6xxx_atu_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct seq_file *s = file->private_data; + struct mv88e6xxx_chip *chip = s->private; + char cmd[64]; + unsigned int fid; + int ret; + + if (copy_from_user(cmd, buf, sizeof(cmd))) + return -EFAULT; + + ret = sscanf(cmd, "%u", &fid); + if (ret != 1) + return -EINVAL; + + if (fid >= mv88e6xxx_num_databases(chip)) + return -ERANGE; + + mutex_lock(&chip->reg_lock); + ret = mv88e6xxx_g1_atu_flush(chip, fid, true); + mutex_unlock(&chip->reg_lock); + + return ret < 0 ? ret : count; +} + +static int mv88e6xxx_atu_open(struct inode *inode, struct file *file) +{ + return single_open(file, mv88e6xxx_atu_show, inode->i_private); +} + +static const struct file_operations mv88e6xxx_atu_fops = { + .open = mv88e6xxx_atu_open, + .read = seq_read, + .write = mv88e6xxx_atu_write, + .llseek = no_llseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static int mv88e6xxx_default_vid_show(struct seq_file *s, void *p) +{ + struct mv88e6xxx_chip *chip = s->private; + u16 pvid; + int i, err; + + seq_puts(s, " Port DefaultVID\n"); + + mutex_lock(&chip->reg_lock); + + for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { + err = mv88e6xxx_port_get_pvid(chip, i, &pvid); + if (err) + break; + + seq_printf(s, "%4d %d\n", i, pvid); + } + + mutex_unlock(&chip->reg_lock); + + return err; +} + +static ssize_t mv88e6xxx_default_vid_write(struct file *file, + const char __user *buf, size_t count, + loff_t *ppos) +{ + struct seq_file *s = file->private_data; + struct mv88e6xxx_chip *chip = s->private; + char cmd[32]; + unsigned int port, pvid; + int ret; + + if (copy_from_user(cmd, buf, sizeof(cmd))) + return -EFAULT; + + ret = sscanf(cmd, "%u %u", &port, &pvid); + if (ret != 2) + return -EINVAL; + + if (port >= mv88e6xxx_num_ports(chip) || pvid > 0xfff) + return -ERANGE; + + mutex_lock(&chip->reg_lock); + ret = mv88e6xxx_port_set_pvid(chip, port, pvid); + mutex_unlock(&chip->reg_lock); + + return ret < 0 ? ret : count; +} + +static int mv88e6xxx_default_vid_open(struct inode *inode, struct file *file) +{ + return single_open(file, mv88e6xxx_default_vid_show, inode->i_private); +} + +static const struct file_operations mv88e6xxx_default_vid_fops = { + .open = mv88e6xxx_default_vid_open, + .read = seq_read, + .write = mv88e6xxx_default_vid_write, + .llseek = no_llseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static int mv88e6xxx_fid_show(struct seq_file *s, void *p) +{ + struct mv88e6xxx_chip *chip = s->private; + u16 fid; + int i, err; + + seq_puts(s, " Port FID\n"); + + mutex_lock(&chip->reg_lock); + + for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { + err = mv88e6xxx_port_get_fid(chip, i, &fid); + if (err) + break; + + seq_printf(s, "%4d %d\n", i, fid); + } + + mutex_unlock(&chip->reg_lock); + + return err; +} + +static int mv88e6xxx_fid_open(struct inode *inode, struct file *file) +{ + return single_open(file, mv88e6xxx_fid_show, inode->i_private); +} + +static const struct file_operations mv88e6xxx_fid_fops = { + .open = mv88e6xxx_fid_open, + .read = seq_read, + .llseek = no_llseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static const char * const mv88e6xxx_port_state_names[] = { + [MV88E6XXX_PORT_CTL0_STATE_DISABLED] = "Disabled", + [MV88E6XXX_PORT_CTL0_STATE_BLOCKING] = "Blocking/Listening", + [MV88E6XXX_PORT_CTL0_STATE_LEARNING] = "Learning", + [MV88E6XXX_PORT_CTL0_STATE_FORWARDING] = "Forwarding", +}; + +static int mv88e6xxx_state_show(struct seq_file *s, void *p) +{ + struct mv88e6xxx_chip *chip = s->private; + int i, ret; + u16 data; + + /* header */ + seq_puts(s, " Port Mode\n"); + + mutex_lock(&chip->reg_lock); + + /* One line per input port */ + for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { + seq_printf(s, "%4d ", i); + + ret = mv88e6xxx_port_read(chip, i, MV88E6XXX_PORT_CTL0, &data); + if (ret < 0) + goto unlock; + + data &= MV88E6XXX_PORT_CTL0_STATE_MASK; + seq_printf(s, " %s\n", mv88e6xxx_port_state_names[data]); + ret = 0; + } + +unlock: + mutex_unlock(&chip->reg_lock); + + return ret; +} + +static int mv88e6xxx_state_open(struct inode *inode, struct file *file) +{ + return single_open(file, mv88e6xxx_state_show, inode->i_private); +} + +static const struct file_operations mv88e6xxx_state_fops = { + .open = mv88e6xxx_state_open, + .read = seq_read, + .llseek = no_llseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static const char * const mv88e6xxx_port_8021q_mode_names[] = { + [MV88E6XXX_PORT_CTL2_8021Q_MODE_DISABLED] = "Disabled", + [MV88E6XXX_PORT_CTL2_8021Q_MODE_FALLBACK] = "Fallback", + [MV88E6XXX_PORT_CTL2_8021Q_MODE_CHECK] = "Check", + [MV88E6XXX_PORT_CTL2_8021Q_MODE_SECURE] = "Secure", +}; + +static int mv88e6xxx_8021q_mode_show(struct seq_file *s, void *p) +{ + struct mv88e6xxx_chip *chip = s->private; + int i, ret; + u16 data; + + /* header */ + seq_puts(s, " Port Mode\n"); + + mutex_lock(&chip->reg_lock); + + /* One line per input port */ + for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { + seq_printf(s, "%4d ", i); + + ret = mv88e6xxx_port_read(chip, i, MV88E6XXX_PORT_CTL2, &data); + if (ret < 0) + goto unlock; + + data &= MV88E6XXX_PORT_CTL2_8021Q_MODE_MASK; + seq_printf(s, " %s\n", mv88e6xxx_port_8021q_mode_names[data]); + ret = 0; + } + +unlock: + mutex_unlock(&chip->reg_lock); + + return ret; +} + +static int mv88e6xxx_8021q_mode_open(struct inode *inode, struct file *file) +{ + return single_open(file, mv88e6xxx_8021q_mode_show, inode->i_private); +} + +static const struct file_operations mv88e6xxx_8021q_mode_fops = { + .open = mv88e6xxx_8021q_mode_open, + .read = seq_read, + .llseek = no_llseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static int mv88e6xxx_vlan_table_show(struct seq_file *s, void *p) +{ + struct mv88e6xxx_chip *chip = s->private; + int i, j, ret; + u16 data; + + /* header */ + seq_puts(s, " Port"); + for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) + seq_printf(s, " %2d", i); + seq_puts(s, "\n"); + + mutex_lock(&chip->reg_lock); + + /* One line per input port */ + for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { + seq_printf(s, "%4d ", i); + + ret = mv88e6xxx_port_read(chip, i, MV88E6XXX_PORT_BASE_VLAN, &data); + if (ret < 0) + goto unlock; + + /* One column per output port */ + for (j = 0; j < mv88e6xxx_num_ports(chip); ++j) + seq_printf(s, " %c", data & BIT(j) ? '*' : '-'); + seq_puts(s, "\n"); + } + + ret = 0; +unlock: + mutex_unlock(&chip->reg_lock); + + return ret; +} + +static int mv88e6xxx_vlan_table_open(struct inode *inode, struct file *file) +{ + return single_open(file, mv88e6xxx_vlan_table_show, inode->i_private); +} + +static const struct file_operations mv88e6xxx_vlan_table_fops = { + .open = mv88e6xxx_vlan_table_open, + .read = seq_read, + .llseek = no_llseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static int mv88e6xxx_pvt_show(struct seq_file *s, void *p) +{ +#if 0 + struct mv88e6xxx_chip *chip = s->private; + struct dsa_switch_tree *dst = chip->ds->dst; + int port, src_dev, src_port; + u16 pvlan; + int err = 0; + + if (chip->info->family == MV88E6XXX_FAMILY_6185) + return -ENODEV; + + /* header */ + seq_puts(s, " Dev Port PVLAN"); + for (port = 0; port < mv88e6xxx_num_ports(chip); ++port) + seq_printf(s, " %2d", port); + seq_puts(s, "\n"); + + mutex_lock(&chip->reg_lock); + + /* One line per external port */ + for (src_dev = 0; src_dev < DSA_MAX_SWITCHES; ++src_dev) { + if (!dst->ds[src_dev]) + break; + + if (src_dev == chip->ds->index) + continue; + + seq_puts(s, "\n"); + for (src_port = 0; src_port < 16; ++src_port) { + if (src_port >= DSA_MAX_PORTS) + break; + + err = _mv88e6xxx_pvt_read(chip, src_dev, src_port, + &pvlan); + if (err) + goto unlock; + + seq_printf(s, " %d %2d %03hhx ", src_dev, src_port, + pvlan); + + /* One column per internal output port */ + for (port = 0; port < mv88e6xxx_num_ports(chip); ++port) + seq_printf(s, " %c", + pvlan & BIT(port) ? '*' : '-'); + seq_puts(s, "\n"); + } + } + +unlock: + mutex_unlock(&chip->reg_lock); + return err; +#else + return 0; +#endif +} + +static ssize_t mv88e6xxx_pvt_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct seq_file *s = file->private_data; + struct mv88e6xxx_chip *chip = s->private; + const u16 mask = (1 << mv88e6xxx_num_ports(chip)) - 1; + char cmd[32]; + unsigned int src_dev, src_port, pvlan; + int ret; + + if (copy_from_user(cmd, buf, sizeof(cmd))) + return -EFAULT; + + if (sscanf(cmd, "%d %d %x", &src_dev, &src_port, &pvlan) != 3) + return -EINVAL; + + if (src_dev >= 32 || src_port >= 16 || pvlan & ~mask) + return -ERANGE; + + mutex_lock(&chip->reg_lock); + ret = _mv88e6xxx_pvt_write(chip, src_dev, src_port, pvlan); + mutex_unlock(&chip->reg_lock); + + return ret < 0 ? ret : count; +} + +static int mv88e6xxx_pvt_open(struct inode *inode, struct file *file) +{ + return single_open(file, mv88e6xxx_pvt_show, inode->i_private); +} + +static const struct file_operations mv88e6xxx_pvt_fops = { + .open = mv88e6xxx_pvt_open, + .read = seq_read, + .write = mv88e6xxx_pvt_write, + .llseek = no_llseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static int mv88e6xxx_vtu_show(struct seq_file *s, void *p) +{ + struct mv88e6xxx_chip *chip = s->private; + struct mv88e6xxx_vtu_entry next = { 0 }; + int port, ret = 0; + + seq_puts(s, " VID FID SID"); + for (port = 0; port < mv88e6xxx_num_ports(chip); ++port) + seq_printf(s, " %2d", port); + seq_puts(s, "\n"); + + if (!chip->info->ops->vtu_getnext) + return 0; + + next.vid = chip->info->max_vid; /* first or lowest VID */ + + do { + mutex_lock(&chip->reg_lock); + ret = chip->info->ops->vtu_getnext(chip, &next); + mutex_unlock(&chip->reg_lock); + if (ret < 0) + break; + + if (!next.valid) + break; + + seq_printf(s, "%4d %4d %2d", next.vid, next.fid, next.sid); + for (port = 0; port < mv88e6xxx_num_ports(chip); ++port) { + switch (next.member[port]) { + case MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_UNMODIFIED: + seq_puts(s, " ="); + break; + case MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_UNTAGGED: + seq_puts(s, " u"); + break; + case MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_TAGGED: + seq_puts(s, " t"); + break; + case MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_NON_MEMBER: + seq_puts(s, " x"); + break; + default: + seq_puts(s, " ??"); + break; + } + } + seq_puts(s, "\n"); + } while (next.vid < chip->info->max_vid); + + return ret; +} + +static ssize_t mv88e6xxx_vtu_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct seq_file *s = file->private_data; + struct mv88e6xxx_chip *chip = s->private; + struct mv88e6xxx_vtu_entry entry = { 0 }; + bool valid = true; + char cmd[64], tags[12]; /* DSA_MAX_PORTS */ + int vid, fid, sid, port, ret; + + if (!chip->info->ops->vtu_loadpurge) + return -EOPNOTSUPP; + + if (copy_from_user(cmd, buf, sizeof(cmd))) + return -EFAULT; + + /* scan 12 chars instead of num_ports to avoid dynamic scanning... */ + ret = sscanf(cmd, "%d %d %d %c %c %c %c %c %c %c %c %c %c %c %c", &vid, + &fid, &sid, &tags[0], &tags[1], &tags[2], &tags[3], + &tags[4], &tags[5], &tags[6], &tags[7], &tags[8], &tags[9], + &tags[10], &tags[11]); + if (ret == 1) + valid = false; + else if (ret != 3 + mv88e6xxx_num_ports(chip)) + return -EINVAL; + + entry.vid = vid; + entry.valid = valid; + + if (valid) { + entry.fid = fid; + entry.sid = sid; + /* Note: The VTU entry pointed by VID will be loaded but not + * considered valid until the STU entry pointed by SID is valid. + */ + + for (port = 0; port < mv88e6xxx_num_ports(chip); ++port) { + u8 tag; + + switch (tags[port]) { + case 'u': + tag = MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_UNTAGGED; + break; + case 't': + tag = MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_TAGGED; + break; + case 'x': + tag = MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_NON_MEMBER; + break; + case '=': + tag = MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_UNMODIFIED; + break; + default: + return -EINVAL; + } + + entry.member[port] = tag; + } + } + + mutex_lock(&chip->reg_lock); + ret = chip->info->ops->vtu_loadpurge(chip, &entry); + mutex_unlock(&chip->reg_lock); + + return ret < 0 ? ret : count; +} + +static int mv88e6xxx_vtu_open(struct inode *inode, struct file *file) +{ + return single_open(file, mv88e6xxx_vtu_show, inode->i_private); +} + +static const struct file_operations mv88e6xxx_vtu_fops = { + .open = mv88e6xxx_vtu_open, + .read = seq_read, + .write = mv88e6xxx_vtu_write, + .llseek = no_llseek, + .release = single_release, + .owner = THIS_MODULE, +}; +#if 0 +static int mv88e6xxx_stats_show(struct seq_file *s, void *p) +{ + struct mv88e6xxx_chip *chip = s->private; + char *strs; + u64 *stats; + int stat, port, num_stats, num_ports; + int err = 0; + + num_stats = mv88e6xxx_get_sset_count(chip->ds); + if (num_stats == 0) + return 0; + + num_ports = mv88e6xxx_num_ports(chip); + + strs = kcalloc(num_stats, ETH_GSTRING_LEN, GFP_KERNEL); + stats = kcalloc(num_stats, num_ports * sizeof(*stats), GFP_KERNEL); + if (!strs || !strs) { + kfree(strs); + kfree(stats); + return -ENOMEM; + } + + mv88e6xxx_get_strings(chip->ds, 0, strs); + + for (port = 0; port < num_ports; port++) + mv88e6xxx_get_ethtool_stats(chip->ds, port, stats + (port * num_stats)); + + seq_puts(s, " Statistic "); + for (port = 0; port < mv88e6xxx_num_ports(chip); port++) + seq_printf(s, " Port %2d ", port); + seq_puts(s, "\n"); + + for (stat = 0; stat < num_stats; stat++) { + seq_printf(s, "%19s: ", strs + stat * ETH_GSTRING_LEN); + for (port = 0 ; port < num_ports; port++) { + u64 value = stats[stat + port * num_stats]; + + seq_printf(s, "%8llu ", value); + } + seq_puts(s, "\n"); + } + + kfree(stats); + kfree(strs); + + return err; +} + +static int mv88e6xxx_stats_open(struct inode *inode, struct file *file) +{ + return single_open(file, mv88e6xxx_stats_show, inode->i_private); +} + +static const struct file_operations mv88e6xxx_stats_fops = { + .open = mv88e6xxx_stats_open, + .read = seq_read, + .llseek = no_llseek, + .release = single_release, + .owner = THIS_MODULE, +}; +#endif +static int mv88e6xxx_device_map_show(struct seq_file *s, void *p) +{ + struct mv88e6xxx_chip *chip = s->private; + int target, ret; + u16 data, port_mask; + + seq_puts(s, "Target Port\n"); + + /* FIXME */ + port_mask = MV88E6390_G2_DEVICE_MAPPING_PORT_MASK; + + mutex_lock(&chip->reg_lock); + for (target = 0; target < 32; target++) { + ret = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_DEVICE_MAPPING, + target << 8 /* MV88E6XXX_G2_DEVICE_MAPPING_DEV_MASK */); + if (ret < 0) + goto out; + ret = mv88e6xxx_g2_read(chip, MV88E6XXX_G2_DEVICE_MAPPING, &data); + if (ret < 0) + goto out; + seq_printf(s, " %2d %2d\n", target, data & port_mask); + } +out: + mutex_unlock(&chip->reg_lock); + + return 0; +} + +static int mv88e6xxx_device_map_open(struct inode *inode, struct file *file) +{ + return single_open(file, mv88e6xxx_device_map_show, inode->i_private); +} + +static const struct file_operations mv88e6xxx_device_map_fops = { + .open = mv88e6xxx_device_map_open, + .read = seq_read, + .llseek = no_llseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +/* Must be called with SMI lock held */ +static int _mv88e6xxx_scratch_wait(struct mv88e6xxx_chip *chip) +{ + return mv88e6xxx_wait_mask(chip, ADDR_GLOBAL2, + MV88E6XXX_G2_SCRATCH_MISC_MISC, + MV88E6XXX_G2_SCRATCH_MISC_UPDATE, 0); +} + +static int mv88e6xxx_scratch_show(struct seq_file *s, void *p) +{ + struct mv88e6xxx_chip *chip = s->private; + int reg, ret; + u16 data; + + seq_puts(s, "Register Value\n"); + + mutex_lock(&chip->reg_lock); + for (reg = 0; reg < 0x80; reg++) { + ret = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_SCRATCH_MISC_MISC, + reg << 8 /* MV88E6XXX_G2_SCRATCH_MISC_PTR_MASK */); + if (ret < 0) + goto out; + + ret = _mv88e6xxx_scratch_wait(chip); + if (ret < 0) + goto out; + + ret = mv88e6xxx_g2_read(chip, MV88E6XXX_G2_SCRATCH_MISC_MISC, &data); + seq_printf(s, " %2x %2x\n", reg, + data & MV88E6XXX_G2_SCRATCH_MISC_DATA_MASK); + } +out: + mutex_unlock(&chip->reg_lock); + + return 0; +} + +static int mv88e6xxx_scratch_open(struct inode *inode, struct file *file) +{ + return single_open(file, mv88e6xxx_scratch_show, inode->i_private); +} + +static const struct file_operations mv88e6xxx_scratch_fops = { + .open = mv88e6xxx_scratch_open, + .read = seq_read, + .llseek = no_llseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static void mv88e6xxx_init_debugfs(struct mv88e6xxx_chip *chip) +{ + char *name; + + name = kasprintf(GFP_KERNEL, "mv88e6xxx.%d", chip->ds->index); + chip->dbgfs = debugfs_create_dir(name, NULL); + + kfree(name); + + debugfs_create_file("regs", S_IRUGO | S_IWUSR, chip->dbgfs, chip, + &mv88e6xxx_regs_fops); + + debugfs_create_file("name", S_IRUGO, chip->dbgfs, chip, + &mv88e6xxx_name_fops); + + debugfs_create_file("atu", S_IRUGO | S_IWUSR, chip->dbgfs, chip, + &mv88e6xxx_atu_fops); + + debugfs_create_file("default_vid", S_IRUGO | S_IWUSR, chip->dbgfs, chip, + &mv88e6xxx_default_vid_fops); + + debugfs_create_file("fid", S_IRUGO, chip->dbgfs, chip, &mv88e6xxx_fid_fops); + + debugfs_create_file("state", S_IRUGO, chip->dbgfs, chip, + &mv88e6xxx_state_fops); + + debugfs_create_file("8021q_mode", S_IRUGO, chip->dbgfs, chip, + &mv88e6xxx_8021q_mode_fops); + + debugfs_create_file("vlan_table", S_IRUGO, chip->dbgfs, chip, + &mv88e6xxx_vlan_table_fops); + + debugfs_create_file("pvt", S_IRUGO | S_IWUSR, chip->dbgfs, chip, + &mv88e6xxx_pvt_fops); + + debugfs_create_file("vtu", S_IRUGO | S_IWUSR, chip->dbgfs, chip, + &mv88e6xxx_vtu_fops); +#if 0 + debugfs_create_file("stats", S_IRUGO, chip->dbgfs, chip, + &mv88e6xxx_stats_fops); +#endif + debugfs_create_file("device_map", S_IRUGO, chip->dbgfs, chip, + &mv88e6xxx_device_map_fops); + + debugfs_create_file("scratch", S_IRUGO, chip->dbgfs, chip, + &mv88e6xxx_scratch_fops); +} + +static void mv88e6xxx_remove_debugfs(struct mv88e6xxx_chip *chip) +{ + debugfs_remove_recursive(chip->dbgfs); +} 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, ®); + 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, ®); - 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..f952fb1a0de1 100644 --- a/drivers/net/dsa/mv88e6xxx/port.h +++ b/drivers/net/dsa/mv88e6xxx/port.h @@ -309,6 +309,9 @@ /* Offset 0x13: OutFiltered Counter */ #define MV88E6XXX_PORT_OUT_FILTERED 0x13 +/* Offset 0x16: LED Control */ +#define MV88E6XXX_PORT_LED_CONTROL 0x16 + /* Offset 0x18: IEEE Priority Mapping Table */ #define MV88E6390_PORT_IEEE_PRIO_MAP_TABLE 0x18 #define MV88E6390_PORT_IEEE_PRIO_MAP_TABLE_UPDATE 0x8000 @@ -447,6 +450,7 @@ int mv88e6097_port_pause_limit(struct mv88e6xxx_chip *chip, int port, u8 in, u8 out); int mv88e6390_port_pause_limit(struct mv88e6xxx_chip *chip, int port, u8 in, u8 out); +int mv88e6xxx_port_set_eee(struct mv88e6xxx_chip *chip, int port, int eee); int mv88e6341_port_set_cmode(struct mv88e6xxx_chip *chip, int port, phy_interface_t mode); int mv88e6390_port_set_cmode(struct mv88e6xxx_chip *chip, int port, diff --git a/drivers/net/dsa/sja1105/sja1105.h b/drivers/net/dsa/sja1105/sja1105.h index 8c66d3bf61f0..dceb96ae9c83 100644 --- a/drivers/net/dsa/sja1105/sja1105.h +++ b/drivers/net/dsa/sja1105/sja1105.h @@ -278,7 +278,7 @@ struct sja1105_private { struct mii_bus *mdio_base_t1; struct mii_bus *mdio_base_tx; struct mii_bus *mdio_pcs; - struct dw_xpcs *xpcs[SJA1105_MAX_NUM_PORTS]; + struct phylink_pcs *pcs[SJA1105_MAX_NUM_PORTS]; struct sja1105_ptp_data ptp_data; struct sja1105_tas_data tas_data; }; diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c index c7282ce3d11c..4d7284b38c52 100644 --- a/drivers/net/dsa/sja1105/sja1105_main.c +++ b/drivers/net/dsa/sja1105/sja1105_main.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include @@ -1263,29 +1262,11 @@ static int sja1105_parse_dt(struct sja1105_private *priv) return rc; } -/* Convert link speed from SJA1105 to ethtool encoding */ -static int sja1105_port_speed_to_ethtool(struct sja1105_private *priv, - u64 speed) -{ - if (speed == priv->info->port_speed[SJA1105_SPEED_10MBPS]) - return SPEED_10; - if (speed == priv->info->port_speed[SJA1105_SPEED_100MBPS]) - return SPEED_100; - if (speed == priv->info->port_speed[SJA1105_SPEED_1000MBPS]) - return SPEED_1000; - if (speed == priv->info->port_speed[SJA1105_SPEED_2500MBPS]) - return SPEED_2500; - return SPEED_UNKNOWN; -} - -/* Set link speed in the MAC configuration for a specific port. */ -static int sja1105_adjust_port_config(struct sja1105_private *priv, int port, - int speed_mbps) +static int sja1105_set_port_speed(struct sja1105_private *priv, int port, + int speed_mbps) { struct sja1105_mac_config_entry *mac; - struct device *dev = priv->ds->dev; u64 speed; - int rc; /* On P/Q/R/S, one can read from the device via the MAC reconfiguration * tables. On E/T, MAC reconfig tables are not readable, only writable. @@ -1319,7 +1300,7 @@ static int sja1105_adjust_port_config(struct sja1105_private *priv, int port, speed = priv->info->port_speed[SJA1105_SPEED_2500MBPS]; break; default: - dev_err(dev, "Invalid speed %iMbps\n", speed_mbps); + dev_err(priv->ds->dev, "Invalid speed %iMbps\n", speed_mbps); return -EINVAL; } @@ -1331,11 +1312,31 @@ static int sja1105_adjust_port_config(struct sja1105_private *priv, int port, * we need to configure the PCS only (if even that). */ if (priv->phy_mode[port] == PHY_INTERFACE_MODE_SGMII) - mac[port].speed = priv->info->port_speed[SJA1105_SPEED_1000MBPS]; + speed = priv->info->port_speed[SJA1105_SPEED_1000MBPS]; else if (priv->phy_mode[port] == PHY_INTERFACE_MODE_2500BASEX) - mac[port].speed = priv->info->port_speed[SJA1105_SPEED_2500MBPS]; - else - mac[port].speed = speed; + speed = priv->info->port_speed[SJA1105_SPEED_2500MBPS]; + + mac[port].speed = speed; + + return 0; +} + +/* Write the MAC Configuration Table entry and, if necessary, the CGU settings, + * after a link speedchange for this port. + */ +static int sja1105_set_port_config(struct sja1105_private *priv, int port) +{ + struct sja1105_mac_config_entry *mac; + struct device *dev = priv->ds->dev; + int rc; + + /* On P/Q/R/S, one can read from the device via the MAC reconfiguration + * tables. On E/T, MAC reconfig tables are not readable, only writable. + * We have to *know* what the MAC looks like. For the sake of keeping + * the code common, we'll use the static configuration tables as a + * reasonable approximation for both E/T and P/Q/R/S. + */ + mac = priv->static_config.tables[BLK_IDX_MAC_CONFIG].entries; /* Write to the dynamic reconfiguration tables */ rc = sja1105_dynamic_config_write(priv, BLK_IDX_MAC_CONFIG, port, @@ -1362,12 +1363,8 @@ sja1105_mac_select_pcs(struct phylink_config *config, phy_interface_t iface) { struct dsa_port *dp = dsa_phylink_to_port(config); struct sja1105_private *priv = dp->ds->priv; - struct dw_xpcs *xpcs = priv->xpcs[dp->index]; - - if (xpcs) - return &xpcs->pcs; - return NULL; + return priv->pcs[dp->index]; } static void sja1105_mac_config(struct phylink_config *config, @@ -1396,7 +1393,8 @@ static void sja1105_mac_link_up(struct phylink_config *config, struct sja1105_private *priv = dp->ds->priv; int port = dp->index; - sja1105_adjust_port_config(priv, port, speed); + if (!sja1105_set_port_speed(priv, port, speed)) + sja1105_set_port_config(priv, port); sja1105_inhibit_tx(priv, BIT(port), false); } @@ -2299,8 +2297,8 @@ int sja1105_static_config_reload(struct sja1105_private *priv, { struct ptp_system_timestamp ptp_sts_before; struct ptp_system_timestamp ptp_sts_after; - int speed_mbps[SJA1105_MAX_NUM_PORTS]; u16 bmcr[SJA1105_MAX_NUM_PORTS] = {0}; + u64 mac_speed[SJA1105_MAX_NUM_PORTS]; struct sja1105_mac_config_entry *mac; struct dsa_switch *ds = priv->ds; s64 t1, t2, t3, t4; @@ -2313,17 +2311,16 @@ int sja1105_static_config_reload(struct sja1105_private *priv, mac = priv->static_config.tables[BLK_IDX_MAC_CONFIG].entries; - /* Back up the dynamic link speed changed by sja1105_adjust_port_config + /* Back up the dynamic link speed changed by sja1105_set_port_speed() * in order to temporarily restore it to SJA1105_SPEED_AUTO - which the * switch wants to see in the static config in order to allow us to * change it through the dynamic interface later. */ for (i = 0; i < ds->num_ports; i++) { - speed_mbps[i] = sja1105_port_speed_to_ethtool(priv, - mac[i].speed); + mac_speed[i] = mac[i].speed; mac[i].speed = priv->info->port_speed[SJA1105_SPEED_AUTO]; - if (priv->xpcs[i]) + if (priv->pcs[i]) bmcr[i] = mdiobus_c45_read(priv->mdio_pcs, i, MDIO_MMD_VEND2, MDIO_CTRL1); } @@ -2380,14 +2377,15 @@ int sja1105_static_config_reload(struct sja1105_private *priv, } for (i = 0; i < ds->num_ports; i++) { - struct dw_xpcs *xpcs = priv->xpcs[i]; + struct phylink_pcs *pcs = priv->pcs[i]; unsigned int neg_mode; - rc = sja1105_adjust_port_config(priv, i, speed_mbps[i]); + mac[i].speed = mac_speed[i]; + rc = sja1105_set_port_config(priv, i); if (rc < 0) goto out; - if (!xpcs) + if (!pcs) continue; if (bmcr[i] & BMCR_ANENABLE) @@ -2395,7 +2393,8 @@ int sja1105_static_config_reload(struct sja1105_private *priv, else neg_mode = PHYLINK_PCS_NEG_OUTBAND; - rc = xpcs_do_config(xpcs, priv->phy_mode[i], NULL, neg_mode); + rc = pcs->ops->pcs_config(pcs, neg_mode, priv->phy_mode[i], + NULL, true); if (rc < 0) goto out; @@ -2411,8 +2410,8 @@ int sja1105_static_config_reload(struct sja1105_private *priv, else speed = SPEED_10; - xpcs_link_up(&xpcs->pcs, neg_mode, priv->phy_mode[i], - speed, DUPLEX_FULL); + pcs->ops->pcs_link_up(pcs, neg_mode, priv->phy_mode[i], + speed, DUPLEX_FULL); } } diff --git a/drivers/net/dsa/sja1105/sja1105_mdio.c b/drivers/net/dsa/sja1105/sja1105_mdio.c index 52ddb4ef259e..84b7169f2974 100644 --- a/drivers/net/dsa/sja1105/sja1105_mdio.c +++ b/drivers/net/dsa/sja1105/sja1105_mdio.c @@ -400,7 +400,7 @@ static int sja1105_mdiobus_pcs_register(struct sja1105_private *priv) } for (port = 0; port < ds->num_ports; port++) { - struct dw_xpcs *xpcs; + struct phylink_pcs *pcs; if (dsa_is_unused_port(ds, port)) continue; @@ -409,13 +409,13 @@ static int sja1105_mdiobus_pcs_register(struct sja1105_private *priv) priv->phy_mode[port] != PHY_INTERFACE_MODE_2500BASEX) continue; - xpcs = xpcs_create_mdiodev(bus, port, priv->phy_mode[port]); - if (IS_ERR(xpcs)) { - rc = PTR_ERR(xpcs); + pcs = xpcs_create_pcs_mdiodev(bus, port); + if (IS_ERR(pcs)) { + rc = PTR_ERR(pcs); goto out_pcs_free; } - priv->xpcs[port] = xpcs; + priv->pcs[port] = pcs; } priv->mdio_pcs = bus; @@ -424,11 +424,10 @@ static int sja1105_mdiobus_pcs_register(struct sja1105_private *priv) out_pcs_free: for (port = 0; port < ds->num_ports; port++) { - if (!priv->xpcs[port]) - continue; - - xpcs_destroy(priv->xpcs[port]); - priv->xpcs[port] = NULL; + if (priv->pcs[port]) { + xpcs_destroy_pcs(priv->pcs[port]); + priv->pcs[port] = NULL; + } } mdiobus_unregister(bus); @@ -446,11 +445,10 @@ static void sja1105_mdiobus_pcs_unregister(struct sja1105_private *priv) return; for (port = 0; port < ds->num_ports; port++) { - if (!priv->xpcs[port]) - continue; - - xpcs_destroy(priv->xpcs[port]); - priv->xpcs[port] = NULL; + if (priv->pcs[port]) { + xpcs_destroy_pcs(priv->pcs[port]); + priv->pcs[port] = NULL; + } } mdiobus_unregister(priv->mdio_pcs); diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index dcd3f54ed0cf..05ec8da4a69d 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -687,7 +687,7 @@ static void macb_mac_config(struct phylink_config *config, unsigned int mode, u32 pcsctrl, old_pcsctrl; old_pcsctrl = gem_readl(bp, PCSCNTRL); - if (mode == MLO_AN_FIXED) + if (phylink_mode_fixed(mode)) pcsctrl = old_pcsctrl & ~GEM_BIT(PCSAUTONEG); else pcsctrl = old_pcsctrl | GEM_BIT(PCSAUTONEG); diff --git a/drivers/net/ethernet/marvell/Kconfig b/drivers/net/ethernet/marvell/Kconfig index 837295fecd17..c9c9b99e3e06 100644 --- a/drivers/net/ethernet/marvell/Kconfig +++ b/drivers/net/ethernet/marvell/Kconfig @@ -60,6 +60,7 @@ config MVNETA_BM_ENABLE config MVNETA tristate "Marvell Armada 370/38x/XP/37xx network interface support" depends on ARCH_MVEBU || COMPILE_TEST + select MVGMAC select MVMDIO select PHYLINK select PAGE_POOL @@ -73,6 +74,9 @@ config MVNETA driver, which should be used for the older Marvell SoCs (Dove, Orion, Discovery, Kirkwood). +config MVGMAC + tristate + config MVNETA_BM tristate depends on !64BIT diff --git a/drivers/net/ethernet/marvell/Makefile b/drivers/net/ethernet/marvell/Makefile index a399defe25fd..994996990dfb 100644 --- a/drivers/net/ethernet/marvell/Makefile +++ b/drivers/net/ethernet/marvell/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_MVMDIO) += mvmdio.o obj-$(CONFIG_MV643XX_ETH) += mv643xx_eth.o obj-$(CONFIG_MVNETA_BM) += mvneta_bm.o obj-$(CONFIG_MVNETA) += mvneta.o +obj-$(CONFIG_MVGMAC) += mvgmac.o obj-$(CONFIG_MVPP2) += mvpp2/ obj-$(CONFIG_PXA168_ETH) += pxa168_eth.o obj-$(CONFIG_SKGE) += skge.o diff --git a/drivers/net/ethernet/marvell/mvgmac.c b/drivers/net/ethernet/marvell/mvgmac.c new file mode 100644 index 000000000000..b5bf4f783396 --- /dev/null +++ b/drivers/net/ethernet/marvell/mvgmac.c @@ -0,0 +1,461 @@ +/* + * GMAC driver for Marvell network interfaces on Armada SoCs. + * + * Copyright (C) 2012 Marvell + * + * Rami Rosen + * Thomas Petazzoni + * + * Split from mvneta and mvpp2 by Russell King. + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ +#include +#include +#include +#include + +#include "mvgmac.h" + +enum { + /* N = Neta, 21 = PPV2.1, 22 = PPV2.2 */ + /* N: 0-14 21: 0,2-15 22: 0-14 */ + GMAC_CTRL0_REG = 0x00, + GMAC_CTRL0_PORT_ENABLE = BIT(0), + GMAC_CTRL0_PORT_1000BASE_X = BIT(1), + GMAC_CTRL0_MAX_RX_SIZE_SHIFT = 2, + GMAC_CTRL0_MAX_RX_SIZE_MASK = 0x1fff << GMAC_CTRL0_MAX_RX_SIZE_SHIFT, + GMAC_CTRL0_MIB_CNTR_ENABLE = BIT(15), + + /* N: 21: 1,5,6 22: */ + GMAC_CTRL1_REG = 0x04, + GMAC_CTRL1_PERIODIC_XON_ENABLE = BIT(1), + GMAC_CTRL1_GMII_LB_ENABLE = BIT(5), + GMAC_CTRL1_PCS_LB_ENABLE = BIT(6), + + /* ALL: 0,3,4,6 */ + GMAC_CTRL2_REG = 0x08, + GMAC_CTRL2_INBAND_AN_SGMII = BIT(0), + GMAC_CTRL2_PCS_ENABLE = BIT(3), + GMAC_CTRL2_PORT_RGMII = BIT(4), + GMAC_CTRL2_PORT_RESET = BIT(6), + + /* N:0-9,11-13 21:0,1,5-7,9,12,13 22:0-7,9-15 */ + /* 22 bit 2 - EN_PCS_AN */ + GMAC_ANEG_REG = 0x0c, + GMAC_ANEG_FORCE_LINK_DOWN = BIT(0), + GMAC_ANEG_FORCE_LINK_PASS = BIT(1), + GMAC_ANEG_INBAND_AN_ENABLE = BIT(2), + GMAC_ANEG_AN_BYPASS_ENABLE = BIT(3), + GMAC_ANEG_INBAND_RESTART_AN = BIT(4), + GMAC_ANEG_MII_SPEED = BIT(5), + GMAC_ANEG_GMII_SPEED = BIT(6), + GMAC_ANEG_AN_SPEED_ENABLE = BIT(7), + GMAC_ANEG_CONFIG_FLOW_CTRL = BIT(8), + GMAC_ANEG_ADVERT_SYM_FLOW_CTRL = BIT(9), + GMAC_ANEG_ADVERT_ASYM_FLOW_CTRL = BIT(10), + GMAC_ANEG_AN_FLOW_CTRL_ENABLE = BIT(11), + GMAC_ANEG_FULL_DUPLEX = BIT(12), + GMAC_ANEG_AN_DUPLEX_ENABLE = BIT(13), + /* pp22: bit 14 - phy mode */ + /* pp22: bit 15 - choose sample tx config */ + + GMAC_STATUS_REG = 0x10, + MVGMAC_LINK_UP = BIT(0), + MVGMAC_SPEED_1000 = BIT(1), + MVGMAC_SPEED_100 = BIT(2), + MVGMAC_FULL_DUPLEX = BIT(3), + MVGMAC_RX_FLOW_CTRL_ENABLE = BIT(4), + MVGMAC_TX_FLOW_CTRL_ENABLE = BIT(5), + MVGMAC_RX_FLOW_CTRL_ACTIVE = BIT(6), + MVGMAC_TX_FLOW_CTRL_ACTIVE = BIT(7), + MVGMAC_AN_COMPLETE = BIT(11), + MVGMAC_SYNC_OK = BIT(14), + + /* N: 21:6-13 22: */ + GMAC_FIFO_CFG1_REG = 0x1c, + GMAC_FIFO_CFG1_TX_MIN_TH_SHIFT = 6, + GMAC_FIFO_CFG1_TX_MIN_TH_MASK = 0x7f << + GMAC_FIFO_CFG1_TX_MIN_TH_SHIFT, + + /* N:1 21: 22:0,3-7 */ + GMAC_CTRL4_REG = 0x90, + GMAC_CTRL4_EXT_PIN_GMII_SEL = BIT(0), + GMAC_CTRL4_SHORT_PREAMBLE_ENABLE = BIT(1), + GMAC_CTRL4_FC_RX_ENABLE = BIT(3), + GMAC_CTRL4_FC_TX_ENABLE = BIT(4), + GMAC_CTRL4_DP_CLK_SEL = BIT(5), + GMAC_CTRL4_SYNC_BYPASS = BIT(6), + GMAC_CTRL4_QSGMII_BYPASS = BIT(7), + + GMAC_LPI_CTRL0_REG = 0xc0, + GMAC_LPI_CTRL0_TS = 0xff << 8, + GMAC_LPI_CTRL1_REG = 0xc4, + GMAC_LPI_CTRL1_REQ_EN = BIT(0), + GMAC_LPI_CTRL1_TW = 0xfff << 4, + GMAC_LPI_CTRL2_REG = 0xc8, + GMAC_LPI_STATUS_REG = 0xcc, + GMAC_LPI_CNTR_REG = 0xd0, +}; + +#define insert(var, mask, val) ({ \ + u32 __mask = mask; \ + ((var) & ~(__mask)) | (((val) << __ffs(__mask)) & (__mask)); \ +}) + +static void mvgmac_modify(void __iomem *reg, u32 mask, u32 val) +{ + u32 v; + + val &= mask; + v = readl_relaxed(reg) & ~mask; + writel_relaxed(v | val, reg); +} + +#define mvgmac_set(reg, val) mvgmac_modify(reg, val, val) +#define mvgmac_clear(reg, val) mvgmac_modify(reg, val, 0) + +/* Change maximum receive size of the port. */ +void mvgmac_set_max_rx_size(struct mvgmac *gmac, size_t max_rx_size) +{ + int size = (max_rx_size - MARVELL_HEADER_SIZE) / 2; + + mvgmac_modify(gmac->base + GMAC_CTRL0_REG, + GMAC_CTRL0_MAX_RX_SIZE_MASK, + FIELD_PREP(GMAC_CTRL0_MAX_RX_SIZE_MASK, size)); +} +EXPORT_SYMBOL_GPL(mvgmac_set_max_rx_size); + +/* Enable the port by setting the port enable bit of the MAC control register */ +void mvgmac_port_enable(struct mvgmac *gmac) +{ + mvgmac_set(gmac->base + GMAC_CTRL0_REG, + GMAC_CTRL0_PORT_ENABLE | GMAC_CTRL0_MIB_CNTR_ENABLE); +} +EXPORT_SYMBOL_GPL(mvgmac_port_enable); + +/* Disable the port */ +void mvgmac_port_disable(struct mvgmac *gmac) +{ + mvgmac_clear(gmac->base + GMAC_CTRL0_REG, GMAC_CTRL0_PORT_ENABLE); +} +EXPORT_SYMBOL_GPL(mvgmac_port_disable); + +int mvgmac_configure(struct mvgmac *gmac, phy_interface_t phy_mode) +{ + u32 ctrl4; + + switch (phy_mode) { + case PHY_INTERFACE_MODE_QSGMII: + ctrl4 = 0; + break; + + case PHY_INTERFACE_MODE_SGMII: + ctrl4 = GMAC_CTRL4_QSGMII_BYPASS; + break; + + case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII_RXID: + case PHY_INTERFACE_MODE_RGMII_TXID: + ctrl4 = GMAC_CTRL4_QSGMII_BYPASS | + GMAC_CTRL4_EXT_PIN_GMII_SEL; + break; + + default: + return -EINVAL; + } + + if (gmac->version == MVGMAC_PP21) { + /* Min. TX threshold must be less than minimum packet length */ + mvgmac_modify(gmac->base + GMAC_FIFO_CFG1_REG, + GMAC_FIFO_CFG1_TX_MIN_TH_MASK, + FIELD_PREP(GMAC_FIFO_CFG1_TX_MIN_TH_MASK, + 64 - 4 - 2)); + } else if (gmac->version == MVGMAC_PP22) { + mvgmac_modify(gmac->base + GMAC_CTRL4_REG, + GMAC_CTRL4_DP_CLK_SEL | GMAC_CTRL4_SYNC_BYPASS | + GMAC_CTRL4_QSGMII_BYPASS | + GMAC_CTRL4_EXT_PIN_GMII_SEL, + GMAC_CTRL4_SYNC_BYPASS | ctrl4); + } + + return 0; +} +EXPORT_SYMBOL_GPL(mvgmac_configure); + +void mvgmac_link_unforce(struct mvgmac *gmac) +{ + mvgmac_clear(gmac->base + GMAC_ANEG_REG, + GMAC_ANEG_FORCE_LINK_PASS | GMAC_ANEG_FORCE_LINK_DOWN); +} +EXPORT_SYMBOL_GPL(mvgmac_link_unforce); + +void mvgmac_link_force_down(struct mvgmac *gmac) +{ + mvgmac_modify(gmac->base + GMAC_ANEG_REG, + GMAC_ANEG_FORCE_LINK_PASS | GMAC_ANEG_FORCE_LINK_DOWN, + GMAC_ANEG_FORCE_LINK_DOWN); +} +EXPORT_SYMBOL_GPL(mvgmac_link_force_down); + +void mvgmac_link_down(struct mvgmac *gmac, int mode) +{ + if (!phylink_autoneg_inband(mode)) + mvgmac_link_force_down(gmac); +} +EXPORT_SYMBOL_GPL(mvgmac_link_down); + +void mvgmac_link_up(struct mvgmac *gmac, int mode, int speed, int duplex, + bool tx_pause, bool rx_pause) +{ + u32 an_mask, an_val, ctrl4; + + if (gmac->version == MVGMAC_NETA) { + an_mask = GMAC_ANEG_CONFIG_FLOW_CTRL; + an_val = FIELD_PREP(GMAC_ANEG_CONFIG_FLOW_CTRL, + tx_pause || rx_pause); + } else { + an_mask = 0; + an_val = 0; + } + + if (!phylink_autoneg_inband(mode)) { + an_mask |= GMAC_ANEG_FORCE_LINK_DOWN | + GMAC_ANEG_FORCE_LINK_PASS | + GMAC_ANEG_MII_SPEED | GMAC_ANEG_GMII_SPEED | + GMAC_ANEG_FULL_DUPLEX; + an_val |= GMAC_ANEG_FORCE_LINK_PASS; + + if (speed == SPEED_1000 || speed == SPEED_2500) + an_val |= GMAC_ANEG_GMII_SPEED; + else if (speed == SPEED_100) + an_val |= GMAC_ANEG_MII_SPEED; + + if (duplex == DUPLEX_FULL) + an_val |= GMAC_ANEG_FULL_DUPLEX; + } + + if (an_mask) + mvgmac_modify(gmac->base + GMAC_ANEG_REG, an_mask, an_val); + + if (gmac->version == MVGMAC_PP22) { + ctrl4 = readl_relaxed(gmac->base + GMAC_CTRL4_REG); + ctrl4 = insert(ctrl4, GMAC_CTRL4_FC_TX_ENABLE, tx_pause); + ctrl4 = insert(ctrl4, GMAC_CTRL4_FC_RX_ENABLE, rx_pause); + writel_relaxed(ctrl4, gmac->base + GMAC_CTRL4_REG); + } +} +EXPORT_SYMBOL_GPL(mvgmac_link_up); + +void mvgmac_link_change(struct mvgmac *gmac) +{ + u32 gmac_stat = readl_relaxed(gmac->base + GMAC_STATUS_REG); + + phylink_pcs_change(&gmac->pcs, !!(gmac_stat & MVGMAC_LINK_UP)); +} +EXPORT_SYMBOL_GPL(mvgmac_link_change); + +void mvgmac_pcs_get_state(struct phylink_pcs *pcs, + struct phylink_link_state *state) +{ + struct mvgmac *gmac = pcs_to_mvgmac(pcs); + u32 gmac_stat = readl_relaxed(gmac->base + GMAC_STATUS_REG); + + if (gmac_stat & MVGMAC_SPEED_1000) + state->speed = + state->interface == PHY_INTERFACE_MODE_2500BASEX ? + SPEED_2500 : SPEED_1000; + else if (gmac_stat & MVGMAC_SPEED_100) + state->speed = SPEED_100; + else + state->speed = SPEED_10; + + state->an_complete = !!(gmac_stat & MVGMAC_AN_COMPLETE); + state->link = !!(gmac_stat & MVGMAC_LINK_UP); + state->duplex = !!(gmac_stat & MVGMAC_FULL_DUPLEX); + + if (gmac_stat & MVGMAC_RX_FLOW_CTRL_ENABLE) + state->pause |= MLO_PAUSE_RX; + if (gmac_stat & MVGMAC_TX_FLOW_CTRL_ENABLE) + state->pause |= MLO_PAUSE_TX; +} +EXPORT_SYMBOL_GPL(mvgmac_pcs_get_state); + +void mvgmac_pcs_an_restart(struct phylink_pcs *pcs) +{ + struct mvgmac *gmac = pcs_to_mvgmac(pcs); + u32 gmac_an = readl_relaxed(gmac->base + GMAC_ANEG_REG); + + writel_relaxed(gmac_an | GMAC_ANEG_INBAND_RESTART_AN, + gmac->base + GMAC_ANEG_REG); + writel_relaxed(gmac_an & ~GMAC_ANEG_INBAND_RESTART_AN, + gmac->base + GMAC_ANEG_REG); +} +EXPORT_SYMBOL_GPL(mvgmac_pcs_an_restart); + +int mvgmac_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode, + phy_interface_t interface, + const unsigned long *advertising, + bool permit_pause_to_mac) +{ + struct mvgmac *gmac = pcs_to_mvgmac(pcs); + u32 mask, val, an, old_an, changed; + + mask = GMAC_ANEG_INBAND_AN_ENABLE | + GMAC_ANEG_INBAND_RESTART_AN | + GMAC_ANEG_AN_SPEED_ENABLE | + GMAC_ANEG_AN_FLOW_CTRL_ENABLE | + GMAC_ANEG_AN_DUPLEX_ENABLE; + + if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) { + mask |= GMAC_ANEG_MII_SPEED | + GMAC_ANEG_GMII_SPEED | + GMAC_ANEG_FULL_DUPLEX; + val = GMAC_ANEG_INBAND_AN_ENABLE; + + if (interface == PHY_INTERFACE_MODE_SGMII) { + /* SGMII mode receives the speed and duplex from PHY */ + val |= GMAC_ANEG_AN_SPEED_ENABLE | + GMAC_ANEG_AN_DUPLEX_ENABLE; + } else { + /* 802.3z mode has fixed speed and duplex */ + val |= GMAC_ANEG_GMII_SPEED | + GMAC_ANEG_FULL_DUPLEX; + + /* The FLOW_CTRL_ENABLE bit selects either the hardware + * automatically or the GMAC_ANEG_FLOW_CTRL manually + * controls the GMAC pause mode. + */ + if (permit_pause_to_mac) + val |= GMAC_ANEG_AN_FLOW_CTRL_ENABLE; + + /* Update the advertisement bits */ + mask |= GMAC_ANEG_ADVERT_SYM_FLOW_CTRL; + if (phylink_test(advertising, Pause)) + val |= GMAC_ANEG_ADVERT_SYM_FLOW_CTRL; + if (gmac->version == MVGMAC_PP22) { + mask |= GMAC_ANEG_ADVERT_ASYM_FLOW_CTRL; + if (phylink_test(advertising, Asym_Pause)) + val |= GMAC_ANEG_ADVERT_ASYM_FLOW_CTRL; + } + } + } else { + /* Phy or fixed speed - disable in-band AN modes */ + val = 0; + } + + old_an = an = readl_relaxed(gmac->base + GMAC_ANEG_REG); + an = (an & ~mask) | val; + changed = old_an ^ an; + if (changed) + writel_relaxed(an, gmac->base + GMAC_ANEG_REG); + + /* We are only interested in the advertisement bits changing */ + return !!(changed & (GMAC_ANEG_ADVERT_SYM_FLOW_CTRL | + GMAC_ANEG_ADVERT_ASYM_FLOW_CTRL)); +} +EXPORT_SYMBOL_GPL(mvgmac_pcs_config); + +void mvgmac_config_mac(struct mvgmac *gmac, unsigned int mode, + const struct phylink_link_state *state) +{ + u32 new_ctrl0, gmac_ctrl0 = readl_relaxed(gmac->base + GMAC_CTRL0_REG); + u32 new_ctrl2, gmac_ctrl2 = readl_relaxed(gmac->base + GMAC_CTRL2_REG); + u32 new_ctrl4, gmac_ctrl4 = readl_relaxed(gmac->base + GMAC_CTRL4_REG); + + new_ctrl0 = gmac_ctrl0 & ~GMAC_CTRL0_PORT_1000BASE_X; + new_ctrl2 = gmac_ctrl2 & ~(GMAC_CTRL2_INBAND_AN_SGMII | + GMAC_CTRL2_PORT_RESET); + new_ctrl4 = gmac_ctrl4; + + /* Even though it might look weird, when we're configured in + * SGMII or QSGMII mode, the RGMII bit needs to be set. + */ + new_ctrl2 |= GMAC_CTRL2_PORT_RGMII; + + if (state->interface == PHY_INTERFACE_MODE_QSGMII || + state->interface == PHY_INTERFACE_MODE_SGMII || + phy_interface_mode_is_8023z(state->interface)) + new_ctrl2 |= GMAC_CTRL2_PCS_ENABLE; + + if (!phylink_autoneg_inband(mode)) { + /* Phy or fixed speed - nothing to do, leave the + * configured speed, duplex and flow control as-is. + */ + } else if (state->interface == PHY_INTERFACE_MODE_SGMII) { + /* SGMII mode receives the state from the PHY */ + new_ctrl2 |= GMAC_CTRL2_INBAND_AN_SGMII; + } else { + /* 802.3z negotiation - 1000BaseX */ + new_ctrl0 |= GMAC_CTRL0_PORT_1000BASE_X; + } + + if (gmac->version == MVGMAC_NETA) { + /* When at 2.5G, the link partner can send frames with + * shortened preambles. + */ + new_ctrl4 &= ~GMAC_CTRL4_SHORT_PREAMBLE_ENABLE; + if (state->interface == PHY_INTERFACE_MODE_2500BASEX) + new_ctrl4 |= GMAC_CTRL4_SHORT_PREAMBLE_ENABLE; + } + + if (new_ctrl0 != gmac_ctrl0) + writel_relaxed(new_ctrl0, gmac->base + GMAC_CTRL0_REG); + if (new_ctrl2 != gmac_ctrl2) + writel_relaxed(new_ctrl2, gmac->base + GMAC_CTRL2_REG); + if (new_ctrl4 != gmac_ctrl4) + writel_relaxed(new_ctrl4, gmac->base + GMAC_CTRL4_REG); + + if (gmac_ctrl2 & GMAC_CTRL2_PORT_RESET) { + while ((readl_relaxed(gmac->base + GMAC_CTRL2_REG) & + GMAC_CTRL2_PORT_RESET) != 0) + continue; + } +} +EXPORT_SYMBOL_GPL(mvgmac_config_mac); + +void mvgmac_set_lpi_timers(struct mvgmac *gmac, unsigned int timer) +{ + u32 ts, tw, status; + + status = readl_relaxed(gmac->base + GMAC_STATUS_REG); + if (status & MVGMAC_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; + + mvgmac_modify(gmac->base + GMAC_LPI_CTRL0_REG, + GMAC_LPI_CTRL0_TS, + FIELD_PREP(GMAC_LPI_CTRL0_TS, ts)); + + mvgmac_modify(gmac->base + GMAC_LPI_CTRL1_REG, + GMAC_LPI_CTRL1_TW, + FIELD_PREP(GMAC_LPI_CTRL1_TW, tw)); +} +EXPORT_SYMBOL_GPL(mvgmac_set_lpi_timers); + +void mvgmac_set_eee(struct mvgmac *gmac, bool enable) +{ + mvgmac_modify(gmac->base + GMAC_LPI_CTRL1_REG, + GMAC_LPI_CTRL1_REQ_EN, + FIELD_PREP(GMAC_LPI_CTRL1_REQ_EN, enable)); +} +EXPORT_SYMBOL_GPL(mvgmac_set_eee); + +MODULE_DESCRIPTION("Marvell GMAC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/marvell/mvgmac.h b/drivers/net/ethernet/marvell/mvgmac.h new file mode 100644 index 000000000000..e423b484f369 --- /dev/null +++ b/drivers/net/ethernet/marvell/mvgmac.h @@ -0,0 +1,56 @@ +#ifndef MVGMAC_H +#define MVGMAC_H + +#include + +/* The two bytes Marvell header. Either contains a special value used by + * Marvell switches when a specific hardware mode is enabled (not supported + * by this driver) or is filled automatically by zeroes on the RX side. + * Those two bytes being at the front of the Ethernet header, they allow + * to have the IP header aligned on a 4 bytes boundary automatically: the + * hardware skips those two bytes on its own. + */ +#define MARVELL_HEADER_SIZE 2 + +enum { + /* GMAC version */ + MVGMAC_NETA, + MVGMAC_PP21, + MVGMAC_PP22, +}; + +struct mvgmac { + void __iomem *base; + unsigned int version; + struct phylink_pcs pcs; +}; + +static inline struct mvgmac *pcs_to_mvgmac(struct phylink_pcs *pcs) +{ + return container_of(pcs, struct mvgmac, pcs); +} + +void mvgmac_set_max_rx_size(struct mvgmac *gmac, size_t max_rx_size); +void mvgmac_port_enable(struct mvgmac *gmac); +void mvgmac_port_disable(struct mvgmac *gmac); +int mvgmac_configure(struct mvgmac *gmac, phy_interface_t phy_mode); +void mvgmac_link_unforce(struct mvgmac *gmac); +void mvgmac_link_force_down(struct mvgmac *gmac); +void mvgmac_link_down(struct mvgmac *gmac, int mode); +void mvgmac_link_up(struct mvgmac *gmac, int mode, int speed, int duplex, + bool tx_pause, bool rx_pause); +void mvgmac_link_change(struct mvgmac *gmac); +void mvgmac_pcs_get_state(struct phylink_pcs *pcs, + struct phylink_link_state *state); +void mvgmac_pcs_an_restart(struct phylink_pcs *pcs); +int mvgmac_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode, + phy_interface_t interface, + const unsigned long *advertising, + bool permit_pause_to_mac); +void mvgmac_config_mac(struct mvgmac *gmac, unsigned int mode, + const struct phylink_link_state *state); + +void mvgmac_set_lpi_timers(struct mvgmac *gmac, unsigned int timer); +void mvgmac_set_eee(struct mvgmac *gmac, bool enable); + +#endif diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 41894834fb53..3dc65d2bfe11 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -34,6 +34,7 @@ #include #include #include "mvneta_bm.h" +#include "mvgmac.h" #include #include #include @@ -196,43 +197,7 @@ #define MVNETA_RXQ_ENABLE_MASK 0x000000ff #define MVETH_TXQ_TOKEN_COUNT_REG(q) (0x2700 + ((q) << 4)) #define MVETH_TXQ_TOKEN_CFG_REG(q) (0x2704 + ((q) << 4)) -#define MVNETA_GMAC_CTRL_0 0x2c00 -#define MVNETA_GMAC_MAX_RX_SIZE_SHIFT 2 -#define MVNETA_GMAC_MAX_RX_SIZE_MASK 0x7ffc -#define MVNETA_GMAC0_PORT_1000BASE_X BIT(1) -#define MVNETA_GMAC0_PORT_ENABLE BIT(0) -#define MVNETA_GMAC_CTRL_2 0x2c08 -#define MVNETA_GMAC2_INBAND_AN_ENABLE BIT(0) -#define MVNETA_GMAC2_PCS_ENABLE BIT(3) -#define MVNETA_GMAC2_PORT_RGMII BIT(4) -#define MVNETA_GMAC2_PORT_RESET BIT(6) -#define MVNETA_GMAC_STATUS 0x2c10 -#define MVNETA_GMAC_LINK_UP BIT(0) -#define MVNETA_GMAC_SPEED_1000 BIT(1) -#define MVNETA_GMAC_SPEED_100 BIT(2) -#define MVNETA_GMAC_FULL_DUPLEX BIT(3) -#define MVNETA_GMAC_RX_FLOW_CTRL_ENABLE BIT(4) -#define MVNETA_GMAC_TX_FLOW_CTRL_ENABLE BIT(5) -#define MVNETA_GMAC_RX_FLOW_CTRL_ACTIVE BIT(6) -#define MVNETA_GMAC_TX_FLOW_CTRL_ACTIVE BIT(7) -#define MVNETA_GMAC_AN_COMPLETE BIT(11) -#define MVNETA_GMAC_SYNC_OK BIT(14) -#define MVNETA_GMAC_AUTONEG_CONFIG 0x2c0c -#define MVNETA_GMAC_FORCE_LINK_DOWN BIT(0) -#define MVNETA_GMAC_FORCE_LINK_PASS BIT(1) -#define MVNETA_GMAC_INBAND_AN_ENABLE BIT(2) -#define MVNETA_GMAC_AN_BYPASS_ENABLE BIT(3) -#define MVNETA_GMAC_INBAND_RESTART_AN BIT(4) -#define MVNETA_GMAC_CONFIG_MII_SPEED BIT(5) -#define MVNETA_GMAC_CONFIG_GMII_SPEED BIT(6) -#define MVNETA_GMAC_AN_SPEED_EN BIT(7) -#define MVNETA_GMAC_CONFIG_FLOW_CTRL BIT(8) -#define MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL BIT(9) -#define MVNETA_GMAC_AN_FLOW_CTRL_EN BIT(11) -#define MVNETA_GMAC_CONFIG_FULL_DUPLEX BIT(12) -#define MVNETA_GMAC_AN_DUPLEX_EN BIT(13) -#define MVNETA_GMAC_CTRL_4 0x2c90 -#define MVNETA_GMAC4_SHORT_PREAMBLE_ENABLE BIT(1) +#define MVNETA_GMAC_BASE 0x2c00 #define MVNETA_MIB_COUNTERS_BASE 0x3000 #define MVNETA_MIB_LATE_COLLISION 0x7c #define MVNETA_DA_FILT_SPEC_MCAST 0x3400 @@ -283,12 +248,6 @@ (MVNETA_TXQ_BUCKET_REFILL_BASE_PERIOD_NS * \ MVNETA_TXQ_BUCKET_REFILL_PERIOD)) -#define MVNETA_LPI_CTRL_0 0x2cc0 -#define MVNETA_LPI_CTRL_1 0x2cc4 -#define MVNETA_LPI_REQUEST_ENABLE BIT(0) -#define MVNETA_LPI_CTRL_2 0x2cc8 -#define MVNETA_LPI_STATUS 0x2ccc - #define MVNETA_CAUSE_TXQ_SENT_DESC_ALL_MASK 0xff /* Descriptor ring Macros */ @@ -310,7 +269,7 @@ * boundary automatically: the hardware skips those two bytes on its * own. */ -#define MVNETA_MH_SIZE 2 +#define MVNETA_MH_SIZE MARVELL_HEADER_SIZE #define MVNETA_VLAN_TAG_LEN 4 @@ -533,18 +492,14 @@ struct mvneta_port { unsigned int tx_csum_limit; struct phylink *phylink; struct phylink_config phylink_config; - struct phylink_pcs phylink_pcs; struct phy *comphy; + struct mvgmac gmac; struct mvneta_bm *bm_priv; struct mvneta_bm_pool *pool_long; 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]; @@ -928,19 +883,6 @@ mvneta_rxq_next_desc_get(struct mvneta_rx_queue *rxq) return rxq->descs + rx_desc; } -/* Change maximum receive size of the port. */ -static void mvneta_max_rx_size_set(struct mvneta_port *pp, int max_rx_size) -{ - u32 val; - - val = mvreg_read(pp, MVNETA_GMAC_CTRL_0); - val &= ~MVNETA_GMAC_MAX_RX_SIZE_MASK; - val |= ((max_rx_size - MVNETA_MH_SIZE) / 2) << - MVNETA_GMAC_MAX_RX_SIZE_SHIFT; - mvreg_write(pp, MVNETA_GMAC_CTRL_0, val); -} - - /* Set rx queue offset */ static void mvneta_rxq_offset_set(struct mvneta_port *pp, struct mvneta_rx_queue *rxq, @@ -1345,26 +1287,10 @@ static void mvneta_port_down(struct mvneta_port *pp) udelay(200); } -/* Enable the port by setting the port enable bit of the MAC control register */ -static void mvneta_port_enable(struct mvneta_port *pp) -{ - u32 val; - - /* Enable port */ - val = mvreg_read(pp, MVNETA_GMAC_CTRL_0); - val |= MVNETA_GMAC0_PORT_ENABLE; - mvreg_write(pp, MVNETA_GMAC_CTRL_0, val); -} - /* Disable the port and wait for about 200 usec before retuning */ static void mvneta_port_disable(struct mvneta_port *pp) { - u32 val; - - /* Reset the Enable bit in the Serial Control Register */ - val = mvreg_read(pp, MVNETA_GMAC_CTRL_0); - val &= ~MVNETA_GMAC0_PORT_ENABLE; - mvreg_write(pp, MVNETA_GMAC_CTRL_0, val); + mvgmac_port_disable(&pp->gmac); udelay(200); } @@ -3255,14 +3181,6 @@ static irqreturn_t mvneta_percpu_isr(int irq, void *dev_id) return IRQ_HANDLED; } -static void mvneta_link_change(struct mvneta_port *pp) -{ - u32 gmac_stat = mvreg_read(pp, MVNETA_GMAC_STATUS); - - phylink_pcs_change(&pp->phylink_pcs, - !!(gmac_stat & MVNETA_GMAC_LINK_UP)); -} - /* NAPI handler * Bits 0 - 7 of the causeRxTx register indicate that are transmitted * packets on the corresponding TXQ (Bit 0 is for TX queue 1). @@ -3292,7 +3210,7 @@ static int mvneta_poll(struct napi_struct *napi, int budget) if (cause_misc & (MVNETA_CAUSE_PHY_STATUS_CHANGE | MVNETA_CAUSE_LINK_CHANGE)) - mvneta_link_change(pp); + mvgmac_link_change(&pp->gmac); } /* Release Tx descriptors */ @@ -3749,11 +3667,11 @@ static void mvneta_start_dev(struct mvneta_port *pp) WARN_ON(mvneta_config_interface(pp, pp->phy_interface)); - mvneta_max_rx_size_set(pp, pp->pkt_size); + mvgmac_set_max_rx_size(&pp->gmac, pp->pkt_size); mvneta_txq_max_tx_size_set(pp, pp->pkt_size); /* start the Rx/Tx activity */ - mvneta_port_enable(pp); + mvgmac_port_enable(&pp->gmac); if (!pp->neta_armada3700) { /* Enable polling on the port */ @@ -3955,52 +3873,27 @@ static int mvneta_set_mac_addr(struct net_device *dev, void *addr) return 0; } -static struct mvneta_port *mvneta_pcs_to_port(struct phylink_pcs *pcs) +static unsigned int mvneta_pcs_query_inband(struct phylink_pcs *pcs, + phy_interface_t interface) { - 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) -{ - /* 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 = 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; -} - -static void mvneta_pcs_get_state(struct phylink_pcs *pcs, - struct phylink_link_state *state) -{ - struct mvneta_port *pp = mvneta_pcs_to_port(pcs); - u32 gmac_stat; - - gmac_stat = mvreg_read(pp, MVNETA_GMAC_STATUS); - - if (gmac_stat & MVNETA_GMAC_SPEED_1000) - state->speed = - state->interface == PHY_INTERFACE_MODE_2500BASEX ? - SPEED_2500 : SPEED_1000; - else if (gmac_stat & MVNETA_GMAC_SPEED_100) - state->speed = SPEED_100; - else - state->speed = SPEED_10; - - state->an_complete = !!(gmac_stat & MVNETA_GMAC_AN_COMPLETE); - state->link = !!(gmac_stat & MVNETA_GMAC_LINK_UP); - state->duplex = !!(gmac_stat & MVNETA_GMAC_FULL_DUPLEX); + /* 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; - if (gmac_stat & MVNETA_GMAC_RX_FLOW_CTRL_ENABLE) - state->pause |= MLO_PAUSE_RX; - if (gmac_stat & MVNETA_GMAC_TX_FLOW_CTRL_ENABLE) - state->pause |= MLO_PAUSE_TX; + /* For any other modes, indicate that inband is not supported. */ + return LINK_INBAND_DISABLE; } static int mvneta_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode, @@ -4008,73 +3901,18 @@ static int mvneta_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode, const unsigned long *advertising, bool permit_pause_to_mac) { - struct mvneta_port *pp = mvneta_pcs_to_port(pcs); - u32 mask, val, an, old_an, changed; - - mask = MVNETA_GMAC_INBAND_AN_ENABLE | - MVNETA_GMAC_INBAND_RESTART_AN | - MVNETA_GMAC_AN_SPEED_EN | - MVNETA_GMAC_AN_FLOW_CTRL_EN | - MVNETA_GMAC_AN_DUPLEX_EN; - - if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) { - mask |= MVNETA_GMAC_CONFIG_MII_SPEED | - MVNETA_GMAC_CONFIG_GMII_SPEED | - MVNETA_GMAC_CONFIG_FULL_DUPLEX; - val = MVNETA_GMAC_INBAND_AN_ENABLE; - - if (interface == PHY_INTERFACE_MODE_SGMII) { - /* SGMII mode receives the speed and duplex from PHY */ - val |= MVNETA_GMAC_AN_SPEED_EN | - MVNETA_GMAC_AN_DUPLEX_EN; - } else { - /* 802.3z mode has fixed speed and duplex */ - val |= MVNETA_GMAC_CONFIG_GMII_SPEED | - MVNETA_GMAC_CONFIG_FULL_DUPLEX; - - /* The FLOW_CTRL_EN bit selects either the hardware - * automatically or the CONFIG_FLOW_CTRL manually - * controls the GMAC pause mode. - */ - if (permit_pause_to_mac) - val |= MVNETA_GMAC_AN_FLOW_CTRL_EN; + /* We should never see Asym_Pause set */ + WARN_ON(phylink_test(advertising, Asym_Pause)); - /* Update the advertisement bits */ - mask |= MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL; - if (phylink_test(advertising, Pause)) - val |= MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL; - } - } else { - /* Phy or fixed speed - disable in-band AN modes */ - val = 0; - } - - old_an = an = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); - an = (an & ~mask) | val; - changed = old_an ^ an; - if (changed) - mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, an); - - /* We are only interested in the advertisement bits changing */ - return !!(changed & MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL); -} - -static void mvneta_pcs_an_restart(struct phylink_pcs *pcs) -{ - struct mvneta_port *pp = mvneta_pcs_to_port(pcs); - u32 gmac_an = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); - - mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, - gmac_an | MVNETA_GMAC_INBAND_RESTART_AN); - mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, - gmac_an & ~MVNETA_GMAC_INBAND_RESTART_AN); + return mvgmac_pcs_config(pcs, neg_mode, interface, advertising, + permit_pause_to_mac); } static const struct phylink_pcs_ops mvneta_phylink_pcs_ops = { - .pcs_validate = mvneta_pcs_validate, - .pcs_get_state = mvneta_pcs_get_state, + .pcs_query_inband = mvneta_pcs_query_inband, + .pcs_get_state = mvgmac_pcs_get_state, .pcs_config = mvneta_pcs_config, - .pcs_an_restart = mvneta_pcs_an_restart, + .pcs_an_restart = mvgmac_pcs_an_restart, }; static struct phylink_pcs *mvneta_mac_select_pcs(struct phylink_config *config, @@ -4083,7 +3921,7 @@ static struct phylink_pcs *mvneta_mac_select_pcs(struct phylink_config *config, struct net_device *ndev = to_net_dev(config->dev); struct mvneta_port *pp = netdev_priv(ndev); - return &pp->phylink_pcs; + return &pp->gmac.pcs; } static int mvneta_mac_prepare(struct phylink_config *config, unsigned int mode, @@ -4091,7 +3929,6 @@ static int mvneta_mac_prepare(struct phylink_config *config, unsigned int mode, { struct net_device *ndev = to_net_dev(config->dev); struct mvneta_port *pp = netdev_priv(ndev); - u32 val; if (pp->phy_interface != interface || phylink_autoneg_inband(mode)) { @@ -4100,10 +3937,7 @@ static int mvneta_mac_prepare(struct phylink_config *config, unsigned int mode, * can only change the port mode and in-band enable when the * link is down. */ - val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); - val &= ~MVNETA_GMAC_FORCE_LINK_PASS; - val |= MVNETA_GMAC_FORCE_LINK_DOWN; - mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); + mvgmac_link_force_down(&pp->gmac); } if (pp->phy_interface != interface) @@ -4125,55 +3959,8 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, { struct net_device *ndev = to_net_dev(config->dev); struct mvneta_port *pp = netdev_priv(ndev); - u32 new_ctrl0, gmac_ctrl0 = mvreg_read(pp, MVNETA_GMAC_CTRL_0); - u32 new_ctrl2, gmac_ctrl2 = mvreg_read(pp, MVNETA_GMAC_CTRL_2); - u32 new_ctrl4, gmac_ctrl4 = mvreg_read(pp, MVNETA_GMAC_CTRL_4); - new_ctrl0 = gmac_ctrl0 & ~MVNETA_GMAC0_PORT_1000BASE_X; - new_ctrl2 = gmac_ctrl2 & ~(MVNETA_GMAC2_INBAND_AN_ENABLE | - MVNETA_GMAC2_PORT_RESET); - new_ctrl4 = gmac_ctrl4 & ~(MVNETA_GMAC4_SHORT_PREAMBLE_ENABLE); - - /* Even though it might look weird, when we're configured in - * SGMII or QSGMII mode, the RGMII bit needs to be set. - */ - new_ctrl2 |= MVNETA_GMAC2_PORT_RGMII; - - if (state->interface == PHY_INTERFACE_MODE_QSGMII || - state->interface == PHY_INTERFACE_MODE_SGMII || - phy_interface_mode_is_8023z(state->interface)) - new_ctrl2 |= MVNETA_GMAC2_PCS_ENABLE; - - if (!phylink_autoneg_inband(mode)) { - /* Phy or fixed speed - nothing to do, leave the - * configured speed, duplex and flow control as-is. - */ - } else if (state->interface == PHY_INTERFACE_MODE_SGMII) { - /* SGMII mode receives the state from the PHY */ - new_ctrl2 |= MVNETA_GMAC2_INBAND_AN_ENABLE; - } else { - /* 802.3z negotiation - only 1000base-X */ - new_ctrl0 |= MVNETA_GMAC0_PORT_1000BASE_X; - } - - /* When at 2.5G, the link partner can send frames with shortened - * preambles. - */ - if (state->interface == PHY_INTERFACE_MODE_2500BASEX) - new_ctrl4 |= MVNETA_GMAC4_SHORT_PREAMBLE_ENABLE; - - if (new_ctrl0 != gmac_ctrl0) - mvreg_write(pp, MVNETA_GMAC_CTRL_0, new_ctrl0); - if (new_ctrl2 != gmac_ctrl2) - mvreg_write(pp, MVNETA_GMAC_CTRL_2, new_ctrl2); - if (new_ctrl4 != gmac_ctrl4) - mvreg_write(pp, MVNETA_GMAC_CTRL_4, new_ctrl4); - - if (gmac_ctrl2 & MVNETA_GMAC2_PORT_RESET) { - while ((mvreg_read(pp, MVNETA_GMAC_CTRL_2) & - MVNETA_GMAC2_PORT_RESET) != 0) - continue; - } + mvgmac_config_mac(&pp->gmac, mode, state); } static int mvneta_mac_finish(struct phylink_config *config, unsigned int mode, @@ -4181,7 +3968,7 @@ static int mvneta_mac_finish(struct phylink_config *config, unsigned int mode, { struct net_device *ndev = to_net_dev(config->dev); struct mvneta_port *pp = netdev_priv(ndev); - u32 val, clk; + u32 clk; /* Disable 1ms clock if not in in-band mode */ if (!phylink_autoneg_inband(mode)) { @@ -4197,45 +3984,20 @@ static int mvneta_mac_finish(struct phylink_config *config, unsigned int mode, /* Allow the link to come up if in in-band mode, otherwise the * link is forced via mac_link_down()/mac_link_up() */ - if (phylink_autoneg_inband(mode)) { - val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); - val &= ~MVNETA_GMAC_FORCE_LINK_DOWN; - mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); - } + if (phylink_autoneg_inband(mode)) + mvgmac_link_unforce(&pp->gmac); 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) { struct net_device *ndev = to_net_dev(config->dev); struct mvneta_port *pp = netdev_priv(ndev); - u32 val; mvneta_port_down(pp); - - if (!phylink_autoneg_inband(mode)) { - val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); - val &= ~MVNETA_GMAC_FORCE_LINK_PASS; - val |= MVNETA_GMAC_FORCE_LINK_DOWN; - mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); - } - - pp->eee_active = false; - mvneta_set_eee(pp, false); + mvgmac_link_down(&pp->gmac, mode); } static void mvneta_mac_link_up(struct phylink_config *config, @@ -4246,49 +4008,25 @@ static void mvneta_mac_link_up(struct phylink_config *config, { struct net_device *ndev = to_net_dev(config->dev); struct mvneta_port *pp = netdev_priv(ndev); - u32 val; - if (!phylink_autoneg_inband(mode)) { - val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); - val &= ~(MVNETA_GMAC_FORCE_LINK_DOWN | - MVNETA_GMAC_CONFIG_MII_SPEED | - MVNETA_GMAC_CONFIG_GMII_SPEED | - MVNETA_GMAC_CONFIG_FLOW_CTRL | - MVNETA_GMAC_CONFIG_FULL_DUPLEX); - val |= MVNETA_GMAC_FORCE_LINK_PASS; - - if (speed == SPEED_1000 || speed == SPEED_2500) - val |= MVNETA_GMAC_CONFIG_GMII_SPEED; - else if (speed == SPEED_100) - val |= MVNETA_GMAC_CONFIG_MII_SPEED; - - if (duplex == DUPLEX_FULL) - val |= MVNETA_GMAC_CONFIG_FULL_DUPLEX; - - if (tx_pause || rx_pause) - val |= MVNETA_GMAC_CONFIG_FLOW_CTRL; - - mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); - } else { - /* When inband doesn't cover flow control or flow control is - * disabled, we need to manually configure it. This bit will - * only have effect if MVNETA_GMAC_AN_FLOW_CTRL_EN is unset. - */ - val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); - val &= ~MVNETA_GMAC_CONFIG_FLOW_CTRL; + mvgmac_link_up(&pp->gmac, mode, speed, duplex, tx_pause, rx_pause); + mvneta_port_up(pp); +} - if (tx_pause || rx_pause) - val |= MVNETA_GMAC_CONFIG_FLOW_CTRL; +static void mvneta_mac_disable_tx_lpi(struct phylink_config *config) +{ + struct mvneta_port *pp = netdev_priv(to_net_dev(config->dev)); - mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); - } + mvgmac_set_eee(&pp->gmac, false); +} - mvneta_port_up(pp); +static void mvneta_mac_enable_tx_lpi(struct phylink_config *config, u32 timer) +{ + struct mvneta_port *pp = netdev_priv(to_net_dev(config->dev)); - 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); - } + mvgmac_set_eee(&pp->gmac, false); + mvgmac_set_lpi_timers(&pp->gmac, timer); + mvgmac_set_eee(&pp->gmac, true); } static const struct phylink_mac_ops mvneta_phylink_ops = { @@ -4298,6 +4036,8 @@ static const struct phylink_mac_ops mvneta_phylink_ops = { .mac_finish = mvneta_mac_finish, .mac_link_down = mvneta_mac_link_down, .mac_link_up = mvneta_mac_link_up, + .mac_disable_tx_lpi = mvneta_mac_disable_tx_lpi, + .mac_enable_tx_lpi = mvneta_mac_enable_tx_lpi, }; static int mvneta_mdio_probe(struct mvneta_port *pp) @@ -5101,14 +4841,6 @@ static int mvneta_ethtool_get_eee(struct net_device *dev, struct ethtool_keee *eee) { struct mvneta_port *pp = netdev_priv(dev); - u32 lpi_ctl0; - - lpi_ctl0 = mvreg_read(pp, MVNETA_LPI_CTRL_0); - - eee->eee_enabled = pp->eee_enabled; - eee->eee_active = pp->eee_active; - eee->tx_lpi_enabled = pp->tx_lpi_enabled; - eee->tx_lpi_timer = (lpi_ctl0) >> 8; // * scale; return phylink_ethtool_get_eee(pp->phylink, eee); } @@ -5117,23 +4849,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); } @@ -5531,13 +5246,17 @@ static int mvneta_probe(struct platform_device *pdev) if (!IS_ERR(pp->clk_bus)) clk_prepare_enable(pp->clk_bus); - pp->phylink_pcs.ops = &mvneta_phylink_pcs_ops; - pp->phylink_pcs.neg_mode = true; + pp->gmac.base = pp->base + MVNETA_GMAC_BASE; + pp->gmac.version = MVGMAC_NETA; + pp->gmac.pcs.ops = &mvneta_phylink_pcs_ops; + pp->gmac.pcs.neg_mode = true; pp->phylink_config.dev = &dev->dev; pp->phylink_config.type = PHYLINK_NETDEV; pp->phylink_config.mac_capabilities = MAC_SYM_PAUSE | MAC_10 | MAC_100 | MAC_1000FD | MAC_2500FD; + pp->phylink_config.lpi_capabilities = MAC_100FD | MAC_1000FD; + pp->phylink_config.lpi_timer_limit_us = 255; phy_interface_set_rgmii(pp->phylink_config.supported_interfaces); __set_bit(PHY_INTERFACE_MODE_QSGMII, @@ -5565,6 +5284,11 @@ static int mvneta_probe(struct platform_device *pdev) pp->phylink_config.supported_interfaces); } + /* Setup EEE. Choose 250us idle. */ + pp->phylink_config.eee.eee_enabled = true; + pp->phylink_config.eee.tx_lpi_enabled = true; + pp->phylink_config.eee.tx_lpi_timer = 250; + phylink = phylink_create(&pp->phylink_config, pdev->dev.fwnode, phy_mode, &mvneta_phylink_ops); if (IS_ERR(phylink)) { diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2.h b/drivers/net/ethernet/marvell/mvpp2/mvpp2.h index e809f91c08fb..299b996ac5df 100644 --- a/drivers/net/ethernet/marvell/mvpp2/mvpp2.h +++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2.h @@ -481,6 +481,11 @@ #define MVPP22_GMAC_INT_SUM_MASK 0xa4 #define MVPP22_GMAC_INT_SUM_MASK_LINK_STAT BIT(1) #define MVPP22_GMAC_INT_SUM_MASK_PTP BIT(2) +#define MVPP2_GMAC_LPI_CTRL0 0xc0 +#define MVPP2_GMAC_LPI_CTRL0_TS_MASK GENMASK(8, 8) +#define MVPP2_GMAC_LPI_CTRL1 0xc4 +#define MVPP2_GMAC_LPI_CTRL1_REQ_EN BIT(0) +#define MVPP2_GMAC_LPI_CTRL1_TW_MASK GENMASK(15, 4) /* Per-port XGMAC registers. PPv2.2 and PPv2.3, only for GOP port 0, * relative to port->base. diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c index 0d62a33afa80..cdd900fe6fd2 100644 --- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c @@ -5730,6 +5730,28 @@ static int mvpp2_ethtool_set_rxfh(struct net_device *dev, return ret; } +static int mvpp2_ethtool_get_eee(struct net_device *dev, + struct ethtool_keee *eee) +{ + struct mvpp2_port *port = netdev_priv(dev); + + if (!port->phylink) + return -EOPNOTSUPP; + + return phylink_ethtool_get_eee(port->phylink, eee); +} + +static int mvpp2_ethtool_set_eee(struct net_device *dev, + struct ethtool_keee *eee) +{ + struct mvpp2_port *port = netdev_priv(dev); + + if (!port->phylink) + return -EOPNOTSUPP; + + return phylink_ethtool_set_eee(port->phylink, eee); +} + /* Device ops */ static const struct net_device_ops mvpp2_netdev_ops = { @@ -5772,6 +5794,8 @@ static const struct ethtool_ops mvpp2_eth_tool_ops = { .get_rxfh_indir_size = mvpp2_ethtool_get_rxfh_indir_size, .get_rxfh = mvpp2_ethtool_get_rxfh, .set_rxfh = mvpp2_ethtool_set_rxfh, + .get_eee = mvpp2_ethtool_get_eee, + .set_eee = mvpp2_ethtool_set_eee, }; /* Used for PPv2.1, or PPv2.2 with the old Device Tree binding that @@ -6194,19 +6218,26 @@ static const struct phylink_pcs_ops mvpp2_phylink_xlg_pcs_ops = { .pcs_config = mvpp2_xlg_pcs_config, }; -static int mvpp2_gmac_pcs_validate(struct phylink_pcs *pcs, - unsigned long *supported, - const struct phylink_link_state *state) +static unsigned int mvpp2_gmac_pcs_query_inband(struct phylink_pcs *pcs, + phy_interface_t interface) { - /* When in 802.3z mode, we must have AN enabled: + /* When operating in an 802.3z mode, we must have AN enabled: * Bit 2 Field InBandAnEn In-band Auto-Negotiation enable. ... * When = 1 (1000BASE-X) this field must be set to 1. + * Therefore, inband is "required". */ - if (phy_interface_mode_is_8023z(state->interface) && - !phylink_test(state->advertising, Autoneg)) - return -EINVAL; + if (phy_interface_mode_is_8023z(interface)) + return LINK_INBAND_ENABLE; - return 0; + /* SGMII and RGMII can be configured to use inband signalling of the + * AN result. Indicate these as "possible". + */ + if (interface == PHY_INTERFACE_MODE_SGMII || + phy_interface_mode_is_rgmii(interface)) + return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE; + + /* For any other modes, indicate that inband is not supported. */ + return LINK_INBAND_DISABLE; } static void mvpp2_gmac_pcs_get_state(struct phylink_pcs *pcs, @@ -6313,7 +6344,7 @@ static void mvpp2_gmac_pcs_an_restart(struct phylink_pcs *pcs) } static const struct phylink_pcs_ops mvpp2_phylink_gmac_pcs_ops = { - .pcs_validate = mvpp2_gmac_pcs_validate, + .pcs_query_inband = mvpp2_gmac_pcs_query_inband, .pcs_get_state = mvpp2_gmac_pcs_get_state, .pcs_config = mvpp2_gmac_pcs_config, .pcs_an_restart = mvpp2_gmac_pcs_an_restart, @@ -6635,6 +6666,57 @@ static void mvpp2_mac_link_down(struct phylink_config *config, mvpp2_port_disable(port); } +static void mvpp2_mac_disable_tx_lpi(struct phylink_config *config) +{ + struct mvpp2_port *port = mvpp2_phylink_to_port(config); + + mvpp2_modify(port->base + MVPP2_GMAC_LPI_CTRL1, + MVPP2_GMAC_LPI_CTRL1_REQ_EN, 0); +} + +static void mvpp2_mac_enable_tx_lpi(struct phylink_config *config, u32 timer) +{ + struct mvpp2_port *port = mvpp2_phylink_to_port(config); + u32 ts, tw, lpi0, lpi1, status; + + status = readl(port->base + MVPP2_GMAC_STATUS0); + if (status & MVPP2_GMAC_STATUS0_GMII_SPEED) { + /* At 1G speeds, the timer resolution are 1us, and + * 802.3 says tw is 16.5us. Round up to 17us. + */ + tw = 17; + ts = timer; + } else { + /* At 100M speeds, the timer resolutions are 10us, and + * 802.3 says tw is 30us. + */ + tw = 3; + ts = DIV_ROUND_UP(timer, 10); + } + + if (ts > 255) + ts = 255; + + /* Ensure LPI generation is disabled */ + lpi1 = readl(port->base + MVPP2_GMAC_LPI_CTRL1); + writel(lpi1 & ~MVPP2_GMAC_LPI_CTRL1_REQ_EN, + port->base + MVPP2_GMAC_LPI_CTRL1); + + /* Configure ts */ + lpi0 = readl(port->base + MVPP2_GMAC_LPI_CTRL0); + lpi0 &= ~MVPP2_GMAC_LPI_CTRL0_TS_MASK; + lpi0 |= FIELD_PREP(MVPP2_GMAC_LPI_CTRL0_TS_MASK, ts); + writel(lpi0, port->base + MVPP2_GMAC_LPI_CTRL0); + + /* Configure tw */ + lpi1 &= ~MVPP2_GMAC_LPI_CTRL1_TW_MASK; + lpi1 |= FIELD_PREP(MVPP2_GMAC_LPI_CTRL1_TW_MASK, tw); + + /* Enable LPI generation */ + writel(lpi1 | MVPP2_GMAC_LPI_CTRL1_REQ_EN, + port->base + MVPP2_GMAC_LPI_CTRL1); +} + static const struct phylink_mac_ops mvpp2_phylink_ops = { .mac_select_pcs = mvpp2_select_pcs, .mac_prepare = mvpp2_mac_prepare, @@ -6642,6 +6724,8 @@ static const struct phylink_mac_ops mvpp2_phylink_ops = { .mac_finish = mvpp2_mac_finish, .mac_link_up = mvpp2_mac_link_up, .mac_link_down = mvpp2_mac_link_down, + .mac_enable_tx_lpi = mvpp2_mac_enable_tx_lpi, + .mac_disable_tx_lpi = mvpp2_mac_disable_tx_lpi, }; /* Work-around for ACPI */ @@ -6919,6 +7003,9 @@ static int mvpp2_port_probe(struct platform_device *pdev, port->phylink_config.type = PHYLINK_NETDEV; port->phylink_config.mac_capabilities = MAC_2500FD | MAC_1000FD | MAC_100 | MAC_10; + port->phylink_config.lpi_capabilities = + MAC_2500FD | MAC_1000FD | MAC_100FD; + port->phylink_config.lpi_timer_limit_us = 255; if (port->priv->global_tx_fc) port->phylink_config.mac_capabilities |= @@ -6987,6 +7074,11 @@ static int mvpp2_port_probe(struct platform_device *pdev, port->phylink_config.supported_interfaces); } + /* Setup EEE. Choose 250us idle. */ + port->phylink_config.eee.eee_enabled = true; + port->phylink_config.eee.tx_lpi_enabled = true; + port->phylink_config.eee.tx_lpi_timer = 250; + phylink = phylink_create(&port->phylink_config, port_fwnode, phy_mode, &mvpp2_phylink_ops); if (IS_ERR(phylink)) { diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c index 83ad7c7935e3..48acba5eb178 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c @@ -451,7 +451,7 @@ static struct phylink_pcs *intel_mgbe_select_pcs(struct stmmac_priv *priv, * should always be an XPCS. The original code would always * return this if present. */ - return &priv->hw->xpcs->pcs; + return xpcs_to_phylink_pcs(priv->hw->xpcs); } static int intel_mgbe_common_data(struct pci_dev *pdev, diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c index 03f90676b3ad..0c7d81ddd440 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c @@ -500,23 +500,22 @@ int stmmac_pcs_setup(struct net_device *ndev) struct fwnode_handle *devnode, *pcsnode; struct dw_xpcs *xpcs = NULL; struct stmmac_priv *priv; - int addr, mode, ret; + int addr, ret; priv = netdev_priv(ndev); - mode = priv->plat->phy_interface; devnode = priv->plat->port_node; if (priv->plat->pcs_init) { ret = priv->plat->pcs_init(priv); } else if (fwnode_property_present(devnode, "pcs-handle")) { pcsnode = fwnode_find_reference(devnode, "pcs-handle", 0); - xpcs = xpcs_create_fwnode(pcsnode, mode); + xpcs = xpcs_create_fwnode(pcsnode); fwnode_handle_put(pcsnode); ret = PTR_ERR_OR_ZERO(xpcs); } else if (priv->plat->mdio_bus_data && priv->plat->mdio_bus_data->pcs_mask) { addr = ffs(priv->plat->mdio_bus_data->pcs_mask) - 1; - xpcs = xpcs_create_mdiodev(priv->mii, addr, mode); + xpcs = xpcs_create_mdiodev(priv->mii, addr); ret = PTR_ERR_OR_ZERO(xpcs); } else { return 0; diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c index 5f502265f0a6..515e917333a6 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c @@ -122,7 +122,7 @@ static int txgbe_pcs_write(struct mii_bus *bus, int addr, int devnum, int regnum static int txgbe_mdio_pcs_init(struct txgbe *txgbe) { struct mii_bus *mii_bus; - struct dw_xpcs *xpcs; + struct phylink_pcs *pcs; struct pci_dev *pdev; struct wx *wx; int ret = 0; @@ -147,11 +147,11 @@ static int txgbe_mdio_pcs_init(struct txgbe *txgbe) if (ret) return ret; - xpcs = xpcs_create_mdiodev(mii_bus, 0, PHY_INTERFACE_MODE_10GBASER); - if (IS_ERR(xpcs)) - return PTR_ERR(xpcs); + pcs = xpcs_create_pcs_mdiodev(mii_bus, 0); + if (IS_ERR(pcs)) + return PTR_ERR(pcs); - txgbe->xpcs = xpcs; + txgbe->pcs = pcs; return 0; } @@ -163,7 +163,7 @@ static struct phylink_pcs *txgbe_phylink_mac_select(struct phylink_config *confi struct txgbe *txgbe = wx->priv; if (interface == PHY_INTERFACE_MODE_10GBASER) - return &txgbe->xpcs->pcs; + return txgbe->pcs; return NULL; } @@ -302,7 +302,7 @@ irqreturn_t txgbe_link_irq_handler(int irq, void *data) status = rd32(wx, TXGBE_CFG_PORT_ST); up = !!(status & TXGBE_CFG_PORT_ST_LINK_UP); - phylink_pcs_change(&txgbe->xpcs->pcs, up); + phylink_pcs_change(txgbe->pcs, up); return IRQ_HANDLED; } @@ -779,7 +779,7 @@ int txgbe_init_phy(struct txgbe *txgbe) err_destroy_phylink: phylink_destroy(wx->phylink); err_destroy_xpcs: - xpcs_destroy(txgbe->xpcs); + xpcs_destroy_pcs(txgbe->pcs); err_unregister_swnode: software_node_unregister_node_group(txgbe->nodes.group); @@ -799,6 +799,6 @@ void txgbe_remove_phy(struct txgbe *txgbe) clkdev_drop(txgbe->clock); clk_unregister(txgbe->clk); phylink_destroy(txgbe->wx->phylink); - xpcs_destroy(txgbe->xpcs); + xpcs_destroy_pcs(txgbe->pcs); software_node_unregister_node_group(txgbe->nodes.group); } diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h index 959102c4c379..cc3a7b62fe9e 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h @@ -329,7 +329,7 @@ struct txgbe { struct wx *wx; struct txgbe_nodes nodes; struct txgbe_irq misc; - struct dw_xpcs *xpcs; + struct phylink_pcs *pcs; struct platform_device *sfp_dev; struct platform_device *i2c_dev; struct clk_lookup *clock; diff --git a/drivers/net/pcs/pcs-xpcs-nxp.c b/drivers/net/pcs/pcs-xpcs-nxp.c index d16fc58cd48d..e8efe94cf4ec 100644 --- a/drivers/net/pcs/pcs-xpcs-nxp.c +++ b/drivers/net/pcs/pcs-xpcs-nxp.c @@ -152,26 +152,18 @@ static int nxp_sja1110_pma_config(struct dw_xpcs *xpcs, /* Enable TX and RX PLLs and circuits. * Release reset of PMA to enable data flow to/from PCS. */ - ret = xpcs_read(xpcs, MDIO_MMD_VEND2, SJA1110_POWERDOWN_ENABLE); - if (ret < 0) - return ret; - - val = ret & ~(SJA1110_TXPLL_PD | SJA1110_TXPD | SJA1110_RXCH_PD | - SJA1110_RXBIAS_PD | SJA1110_RESET_SER_EN | - SJA1110_RESET_SER | SJA1110_RESET_DES); - val |= SJA1110_RXPKDETEN | SJA1110_RCVEN; - - ret = xpcs_write(xpcs, MDIO_MMD_VEND2, SJA1110_POWERDOWN_ENABLE, val); + ret = xpcs_modify(xpcs, MDIO_MMD_VEND2, SJA1110_POWERDOWN_ENABLE, + SJA1110_TXPLL_PD | SJA1110_TXPD | SJA1110_RXCH_PD | + SJA1110_RXBIAS_PD | SJA1110_RESET_SER_EN | + SJA1110_RESET_SER | SJA1110_RESET_DES | + SJA1110_RXPKDETEN | SJA1110_RCVEN, + SJA1110_RXPKDETEN | SJA1110_RCVEN); if (ret < 0) return ret; /* Program continuous-time linear equalizer (CTLE) settings. */ - ret = xpcs_write(xpcs, MDIO_MMD_VEND2, SJA1110_RX_CDR_CTLE, - rx_cdr_ctle); - if (ret < 0) - return ret; - - return 0; + return xpcs_write(xpcs, MDIO_MMD_VEND2, SJA1110_RX_CDR_CTLE, + rx_cdr_ctle); } int nxp_sja1110_sgmii_pma_config(struct dw_xpcs *xpcs) diff --git a/drivers/net/pcs/pcs-xpcs-wx.c b/drivers/net/pcs/pcs-xpcs-wx.c index 19c75886f070..fc52f7aa5f59 100644 --- a/drivers/net/pcs/pcs-xpcs-wx.c +++ b/drivers/net/pcs/pcs-xpcs-wx.c @@ -46,25 +46,23 @@ #define TXGBE_VCO_CAL_LD0 0x72 #define TXGBE_VCO_CAL_REF0 0x76 -static int txgbe_read_pma(struct dw_xpcs *xpcs, int reg) +static int txgbe_write_pma(struct dw_xpcs *xpcs, int reg, u16 val) { - return xpcs_read(xpcs, MDIO_MMD_PMAPMD, TXGBE_PMA_MMD + reg); + return xpcs_write(xpcs, MDIO_MMD_PMAPMD, TXGBE_PMA_MMD + reg, val); } -static int txgbe_write_pma(struct dw_xpcs *xpcs, int reg, u16 val) +static int txgbe_modify_pma(struct dw_xpcs *xpcs, int reg, u16 mask, u16 set) { - return xpcs_write(xpcs, MDIO_MMD_PMAPMD, TXGBE_PMA_MMD + reg, val); + return xpcs_modify(xpcs, MDIO_MMD_PMAPMD, TXGBE_PMA_MMD + reg, mask, + set); } static void txgbe_pma_config_10gbaser(struct dw_xpcs *xpcs) { - int val; - txgbe_write_pma(xpcs, TXGBE_MPLLA_CTL0, 0x21); txgbe_write_pma(xpcs, TXGBE_MPLLA_CTL3, 0); - val = txgbe_read_pma(xpcs, TXGBE_TX_GENCTL1); - val = u16_replace_bits(val, 0x5, TXGBE_TX_GENCTL1_VBOOST_LVL); - txgbe_write_pma(xpcs, TXGBE_TX_GENCTL1, val); + txgbe_modify_pma(xpcs, TXGBE_TX_GENCTL1, TXGBE_TX_GENCTL1_VBOOST_LVL, + FIELD_PREP(TXGBE_TX_GENCTL1_VBOOST_LVL, 0x5)); txgbe_write_pma(xpcs, TXGBE_MISC_CTL0, TXGBE_MISC_CTL0_PLL | TXGBE_MISC_CTL0_CR_PARA_SEL | TXGBE_MISC_CTL0_RX_VREF(0xF)); txgbe_write_pma(xpcs, TXGBE_VCO_CAL_LD0, 0x549); @@ -78,38 +76,29 @@ static void txgbe_pma_config_10gbaser(struct dw_xpcs *xpcs) txgbe_write_pma(xpcs, TXGBE_RX_EQ_CTL0, TXGBE_RX_EQ_CTL0_CTLE_POLE(2) | TXGBE_RX_EQ_CTL0_CTLE_BOOST(5)); - val = txgbe_read_pma(xpcs, TXGBE_RX_EQ_ATTN_CTL); - val &= ~TXGBE_RX_EQ_ATTN_LVL0; - txgbe_write_pma(xpcs, TXGBE_RX_EQ_ATTN_CTL, val); + txgbe_modify_pma(xpcs, TXGBE_RX_EQ_ATTN_CTL, TXGBE_RX_EQ_ATTN_LVL0, 0); txgbe_write_pma(xpcs, TXGBE_DFE_TAP_CTL0, 0xBE); - val = txgbe_read_pma(xpcs, TXGBE_AFE_DFE_ENABLE); - val &= ~(TXGBE_DFE_EN_0 | TXGBE_AFE_EN_0); - txgbe_write_pma(xpcs, TXGBE_AFE_DFE_ENABLE, val); - val = txgbe_read_pma(xpcs, TXGBE_RX_EQ_CTL4); - val &= ~TXGBE_RX_EQ_CTL4_CONT_ADAPT0; - txgbe_write_pma(xpcs, TXGBE_RX_EQ_CTL4, val); + txgbe_modify_pma(xpcs, TXGBE_AFE_DFE_ENABLE, + TXGBE_DFE_EN_0 | TXGBE_AFE_EN_0, 0); + txgbe_modify_pma(xpcs, TXGBE_RX_EQ_CTL4, TXGBE_RX_EQ_CTL4_CONT_ADAPT0, + 0); } static void txgbe_pma_config_1g(struct dw_xpcs *xpcs) { - int val; - - val = txgbe_read_pma(xpcs, TXGBE_TX_GENCTL1); - val = u16_replace_bits(val, 0x5, TXGBE_TX_GENCTL1_VBOOST_LVL); - val &= ~TXGBE_TX_GENCTL1_VBOOST_EN0; - txgbe_write_pma(xpcs, TXGBE_TX_GENCTL1, val); + txgbe_modify_pma(xpcs, TXGBE_TX_GENCTL1, + TXGBE_TX_GENCTL1_VBOOST_LVL | + TXGBE_TX_GENCTL1_VBOOST_EN0, + FIELD_PREP(TXGBE_TX_GENCTL1_VBOOST_LVL, 0x5)); txgbe_write_pma(xpcs, TXGBE_MISC_CTL0, TXGBE_MISC_CTL0_PLL | TXGBE_MISC_CTL0_CR_PARA_SEL | TXGBE_MISC_CTL0_RX_VREF(0xF)); txgbe_write_pma(xpcs, TXGBE_RX_EQ_CTL0, TXGBE_RX_EQ_CTL0_VGA1_GAIN(7) | TXGBE_RX_EQ_CTL0_VGA2_GAIN(7) | TXGBE_RX_EQ_CTL0_CTLE_BOOST(6)); - val = txgbe_read_pma(xpcs, TXGBE_RX_EQ_ATTN_CTL); - val &= ~TXGBE_RX_EQ_ATTN_LVL0; - txgbe_write_pma(xpcs, TXGBE_RX_EQ_ATTN_CTL, val); + txgbe_modify_pma(xpcs, TXGBE_RX_EQ_ATTN_CTL, TXGBE_RX_EQ_ATTN_LVL0, 0); txgbe_write_pma(xpcs, TXGBE_DFE_TAP_CTL0, 0); - val = txgbe_read_pma(xpcs, TXGBE_RX_GEN_CTL3); - val = u16_replace_bits(val, 0x4, TXGBE_RX_GEN_CTL3_LOS_TRSHLD0); - txgbe_write_pma(xpcs, TXGBE_RX_EQ_ATTN_CTL, val); + txgbe_modify_pma(xpcs, TXGBE_RX_GEN_CTL3, TXGBE_RX_GEN_CTL3_LOS_TRSHLD0, + FIELD_PREP(TXGBE_RX_GEN_CTL3_LOS_TRSHLD0, 0x4)); txgbe_write_pma(xpcs, TXGBE_MPLLA_CTL0, 0x20); txgbe_write_pma(xpcs, TXGBE_MPLLA_CTL3, 0x46); @@ -172,7 +161,7 @@ static bool txgbe_xpcs_mode_quirk(struct dw_xpcs *xpcs) int txgbe_xpcs_switch_mode(struct dw_xpcs *xpcs, phy_interface_t interface) { - int val, ret; + int ret; switch (interface) { case PHY_INTERFACE_MODE_10GBASER: @@ -194,9 +183,8 @@ int txgbe_xpcs_switch_mode(struct dw_xpcs *xpcs, phy_interface_t interface) if (interface == PHY_INTERFACE_MODE_10GBASER) { xpcs_write(xpcs, MDIO_MMD_PCS, MDIO_CTRL2, MDIO_PCS_CTRL2_10GBR); - val = xpcs_read(xpcs, MDIO_MMD_PMAPMD, MDIO_CTRL1); - val |= MDIO_CTRL1_SPEED10G; - xpcs_write(xpcs, MDIO_MMD_PMAPMD, MDIO_CTRL1, val); + xpcs_modify(xpcs, MDIO_MMD_PMAPMD, MDIO_CTRL1, + MDIO_CTRL1_SPEED10G, MDIO_CTRL1_SPEED10G); txgbe_pma_config_10gbaser(xpcs); } else { xpcs_write(xpcs, MDIO_MMD_PCS, MDIO_CTRL2, MDIO_PCS_CTRL2_10GBX); diff --git a/drivers/net/pcs/pcs-xpcs.c b/drivers/net/pcs/pcs-xpcs.c index 82463f9d50c8..c69421e80d19 100644 --- a/drivers/net/pcs/pcs-xpcs.c +++ b/drivers/net/pcs/pcs-xpcs.c @@ -107,49 +107,9 @@ static const int xpcs_2500basex_features[] = { __ETHTOOL_LINK_MODE_MASK_NBITS, }; -static const phy_interface_t xpcs_usxgmii_interfaces[] = { - PHY_INTERFACE_MODE_USXGMII, -}; - -static const phy_interface_t xpcs_10gkr_interfaces[] = { - PHY_INTERFACE_MODE_10GKR, -}; - -static const phy_interface_t xpcs_xlgmii_interfaces[] = { - PHY_INTERFACE_MODE_XLGMII, -}; - -static const phy_interface_t xpcs_10gbaser_interfaces[] = { - PHY_INTERFACE_MODE_10GBASER, -}; - -static const phy_interface_t xpcs_sgmii_interfaces[] = { - PHY_INTERFACE_MODE_SGMII, -}; - -static const phy_interface_t xpcs_1000basex_interfaces[] = { - PHY_INTERFACE_MODE_1000BASEX, -}; - -static const phy_interface_t xpcs_2500basex_interfaces[] = { - PHY_INTERFACE_MODE_2500BASEX, -}; - -enum { - DW_XPCS_USXGMII, - DW_XPCS_10GKR, - DW_XPCS_XLGMII, - DW_XPCS_10GBASER, - DW_XPCS_SGMII, - DW_XPCS_1000BASEX, - DW_XPCS_2500BASEX, - DW_XPCS_INTERFACE_MAX, -}; - struct dw_xpcs_compat { + phy_interface_t interface; const int *supported; - const phy_interface_t *interface; - int num_interfaces; int an_mode; int (*pma_config)(struct dw_xpcs *xpcs); }; @@ -161,26 +121,28 @@ struct dw_xpcs_desc { }; static const struct dw_xpcs_compat * -xpcs_find_compat(const struct dw_xpcs_desc *desc, phy_interface_t interface) +xpcs_find_compat(struct dw_xpcs *xpcs, phy_interface_t interface) { - int i, j; - - for (i = 0; i < DW_XPCS_INTERFACE_MAX; i++) { - const struct dw_xpcs_compat *compat = &desc->compat[i]; + const struct dw_xpcs_compat *compat; - for (j = 0; j < compat->num_interfaces; j++) - if (compat->interface[j] == interface) - return compat; - } + for (compat = xpcs->desc->compat; compat->supported; compat++) + if (compat->interface == interface) + return compat; return NULL; } +struct phylink_pcs *xpcs_to_phylink_pcs(struct dw_xpcs *xpcs) +{ + return &xpcs->pcs; +} +EXPORT_SYMBOL_GPL(xpcs_to_phylink_pcs); + int xpcs_get_an_mode(struct dw_xpcs *xpcs, phy_interface_t interface) { const struct dw_xpcs_compat *compat; - compat = xpcs_find_compat(xpcs->desc, interface); + compat = xpcs_find_compat(xpcs, interface); if (!compat) return -ENODEV; @@ -213,6 +175,11 @@ int xpcs_write(struct dw_xpcs *xpcs, int dev, u32 reg, u16 val) return mdiodev_c45_write(xpcs->mdiodev, dev, reg, val); } +int xpcs_modify(struct dw_xpcs *xpcs, int dev, u32 reg, u16 mask, u16 set) +{ + return mdiodev_c45_modify(xpcs->mdiodev, dev, reg, mask, set); +} + static int xpcs_modify_changed(struct dw_xpcs *xpcs, int dev, u32 reg, u16 mask, u16 set) { @@ -230,6 +197,12 @@ static int xpcs_write_vendor(struct dw_xpcs *xpcs, int dev, int reg, return xpcs_write(xpcs, dev, DW_VENDOR | reg, val); } +static int xpcs_modify_vendor(struct dw_xpcs *xpcs, int dev, int reg, u16 mask, + u16 set) +{ + return xpcs_modify(xpcs, dev, DW_VENDOR | reg, mask, set); +} + int xpcs_read_vpcs(struct dw_xpcs *xpcs, int reg) { return xpcs_read_vendor(xpcs, MDIO_MMD_PCS, reg); @@ -240,20 +213,22 @@ int xpcs_write_vpcs(struct dw_xpcs *xpcs, int reg, u16 val) return xpcs_write_vendor(xpcs, MDIO_MMD_PCS, reg, val); } +static int xpcs_modify_vpcs(struct dw_xpcs *xpcs, int reg, u16 mask, u16 val) +{ + return xpcs_modify_vendor(xpcs, MDIO_MMD_PCS, reg, mask, val); +} + static int xpcs_poll_reset(struct dw_xpcs *xpcs, int dev) { - /* Poll until the reset bit clears (50ms per retry == 0.6 sec) */ - unsigned int retries = 12; - int ret; + int ret, val; - do { - msleep(50); - ret = xpcs_read(xpcs, dev, MDIO_CTRL1); - if (ret < 0) - return ret; - } while (ret & MDIO_CTRL1_RESET && --retries); + ret = read_poll_timeout(xpcs_read, val, + val < 0 || !(val & MDIO_CTRL1_RESET), + 50000, 600000, true, xpcs, dev, MDIO_CTRL1); + if (val < 0) + ret = val; - return (ret & MDIO_CTRL1_RESET) ? -ETIMEDOUT : 0; + return ret; } static int xpcs_soft_reset(struct dw_xpcs *xpcs, @@ -364,37 +339,25 @@ static void xpcs_config_usxgmii(struct dw_xpcs *xpcs, int speed) return; } - ret = xpcs_read_vpcs(xpcs, MDIO_CTRL1); - if (ret < 0) - goto out; - - ret = xpcs_write_vpcs(xpcs, MDIO_CTRL1, ret | DW_USXGMII_EN); - if (ret < 0) - goto out; - - ret = xpcs_read(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1); - if (ret < 0) - goto out; - - ret &= ~DW_USXGMII_SS_MASK; - ret |= speed_sel | DW_USXGMII_FULL; - - ret = xpcs_write(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1, ret); + ret = xpcs_modify_vpcs(xpcs, MDIO_CTRL1, DW_USXGMII_EN, DW_USXGMII_EN); if (ret < 0) goto out; - ret = xpcs_read_vpcs(xpcs, MDIO_CTRL1); + ret = xpcs_modify(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1, DW_USXGMII_SS_MASK, + speed_sel | DW_USXGMII_FULL); if (ret < 0) goto out; - ret = xpcs_write_vpcs(xpcs, MDIO_CTRL1, ret | DW_USXGMII_RST); + ret = xpcs_modify_vpcs(xpcs, MDIO_CTRL1, DW_USXGMII_RST, + DW_USXGMII_RST); if (ret < 0) goto out; return; out: - pr_err("%s: XPCS access returned %pe\n", __func__, ERR_PTR(ret)); + dev_err(&xpcs->mdiodev->dev, "%s: XPCS access returned %pe\n", + __func__, ERR_PTR(ret)); } static int _xpcs_config_aneg_c73(struct dw_xpcs *xpcs, @@ -451,13 +414,9 @@ static int xpcs_config_aneg_c73(struct dw_xpcs *xpcs, if (ret < 0) return ret; - ret = xpcs_read(xpcs, MDIO_MMD_AN, MDIO_CTRL1); - if (ret < 0) - return ret; - - ret |= MDIO_AN_CTRL1_ENABLE | MDIO_AN_CTRL1_RESTART; - - return xpcs_write(xpcs, MDIO_MMD_AN, MDIO_CTRL1, ret); + return xpcs_modify(xpcs, MDIO_MMD_AN, MDIO_CTRL1, + MDIO_AN_CTRL1_ENABLE | MDIO_AN_CTRL1_RESTART, + MDIO_AN_CTRL1_ENABLE | MDIO_AN_CTRL1_RESTART); } static int xpcs_aneg_done_c73(struct dw_xpcs *xpcs, @@ -592,7 +551,7 @@ static int xpcs_validate(struct phylink_pcs *pcs, unsigned long *supported, int i; xpcs = phylink_pcs_to_xpcs(pcs); - compat = xpcs_find_compat(xpcs->desc, state->interface); + compat = xpcs_find_compat(xpcs, state->interface); if (!compat) return -EINVAL; @@ -610,62 +569,72 @@ static int xpcs_validate(struct phylink_pcs *pcs, unsigned long *supported, void xpcs_get_interfaces(struct dw_xpcs *xpcs, unsigned long *interfaces) { - int i, j; - - for (i = 0; i < DW_XPCS_INTERFACE_MAX; i++) { - const struct dw_xpcs_compat *compat = &xpcs->desc->compat[i]; + const struct dw_xpcs_compat *compat; - for (j = 0; j < compat->num_interfaces; j++) - __set_bit(compat->interface[j], interfaces); - } + for (compat = xpcs->desc->compat; compat->supported; compat++) + __set_bit(compat->interface, interfaces); } EXPORT_SYMBOL_GPL(xpcs_get_interfaces); int xpcs_config_eee(struct dw_xpcs *xpcs, int mult_fact_100ns, int enable) { + u16 mask, val; int ret; - ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_EEE_MCTRL0); - if (ret < 0) - return ret; + mask = DW_VR_MII_EEE_LTX_EN | DW_VR_MII_EEE_LRX_EN | + DW_VR_MII_EEE_TX_QUIET_EN | DW_VR_MII_EEE_RX_QUIET_EN | + DW_VR_MII_EEE_TX_EN_CTRL | DW_VR_MII_EEE_RX_EN_CTRL | + DW_VR_MII_EEE_MULT_FACT_100NS; - if (enable) { - /* Enable EEE */ - ret = DW_VR_MII_EEE_LTX_EN | DW_VR_MII_EEE_LRX_EN | + if (enable) + val = DW_VR_MII_EEE_LTX_EN | DW_VR_MII_EEE_LRX_EN | DW_VR_MII_EEE_TX_QUIET_EN | DW_VR_MII_EEE_RX_QUIET_EN | DW_VR_MII_EEE_TX_EN_CTRL | DW_VR_MII_EEE_RX_EN_CTRL | - mult_fact_100ns << DW_VR_MII_EEE_MULT_FACT_100NS_SHIFT; - } else { - ret &= ~(DW_VR_MII_EEE_LTX_EN | DW_VR_MII_EEE_LRX_EN | - DW_VR_MII_EEE_TX_QUIET_EN | DW_VR_MII_EEE_RX_QUIET_EN | - DW_VR_MII_EEE_TX_EN_CTRL | DW_VR_MII_EEE_RX_EN_CTRL | - DW_VR_MII_EEE_MULT_FACT_100NS); - } + FIELD_PREP(DW_VR_MII_EEE_MULT_FACT_100NS, + mult_fact_100ns); + else + val = 0; - ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_EEE_MCTRL0, ret); + ret = xpcs_modify(xpcs, MDIO_MMD_VEND2, DW_VR_MII_EEE_MCTRL0, mask, + val); if (ret < 0) return ret; - ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_EEE_MCTRL1); - if (ret < 0) - return ret; + return xpcs_modify(xpcs, MDIO_MMD_VEND2, DW_VR_MII_EEE_MCTRL1, + DW_VR_MII_EEE_TRN_LPI, + enable ? DW_VR_MII_EEE_TRN_LPI : 0); +} +EXPORT_SYMBOL_GPL(xpcs_config_eee); - if (enable) - ret |= DW_VR_MII_EEE_TRN_LPI; - else - ret &= ~DW_VR_MII_EEE_TRN_LPI; +static void xpcs_pre_config(struct phylink_pcs *pcs, phy_interface_t interface) +{ + struct dw_xpcs *xpcs = phylink_pcs_to_xpcs(pcs); + const struct dw_xpcs_compat *compat; + int ret; + + if (!xpcs->need_reset) + return; + + compat = xpcs_find_compat(xpcs, interface); + if (!compat) { + dev_err(&xpcs->mdiodev->dev, "unsupported interface %s\n", + phy_modes(interface)); + return; + } + + ret = xpcs_soft_reset(xpcs, compat); + if (ret) + dev_err(&xpcs->mdiodev->dev, "soft reset failed: %pe\n", + ERR_PTR(ret)); - return xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_EEE_MCTRL1, ret); + xpcs->need_reset = false; } -EXPORT_SYMBOL_GPL(xpcs_config_eee); static int xpcs_config_aneg_c37_sgmii(struct dw_xpcs *xpcs, unsigned int neg_mode) { int ret, mdio_ctrl, tx_conf; - - if (xpcs->info.pma == WX_TXGBE_XPCS_PMA_10G_ID) - xpcs_write_vpcs(xpcs, DW_VR_XS_PCS_DIG_CTRL1, DW_CL37_BP | DW_EN_VSMMD1); + u16 mask, val; /* For AN for C37 SGMII mode, the settings are :- * 1) VR_MII_MMD_CTRL Bit(12) [AN_ENABLE] = 0b (Disable SGMII AN in case @@ -694,40 +663,35 @@ static int xpcs_config_aneg_c37_sgmii(struct dw_xpcs *xpcs, return ret; } - ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_CTRL); - if (ret < 0) - return ret; + mask = DW_VR_MII_PCS_MODE_MASK | DW_VR_MII_TX_CONFIG_MASK; + val = FIELD_PREP(DW_VR_MII_PCS_MODE_MASK, + DW_VR_MII_PCS_MODE_C37_SGMII); - ret &= ~(DW_VR_MII_PCS_MODE_MASK | DW_VR_MII_TX_CONFIG_MASK); - ret |= (DW_VR_MII_PCS_MODE_C37_SGMII << - DW_VR_MII_AN_CTRL_PCS_MODE_SHIFT & - DW_VR_MII_PCS_MODE_MASK); if (xpcs->info.pma == WX_TXGBE_XPCS_PMA_10G_ID) { - ret |= DW_VR_MII_AN_CTRL_8BIT; + mask |= DW_VR_MII_AN_CTRL_8BIT; + val |= DW_VR_MII_AN_CTRL_8BIT; /* Hardware requires it to be PHY side SGMII */ tx_conf = DW_VR_MII_TX_CONFIG_PHY_SIDE_SGMII; } else { tx_conf = DW_VR_MII_TX_CONFIG_MAC_SIDE_SGMII; } - ret |= tx_conf << DW_VR_MII_AN_CTRL_TX_CONFIG_SHIFT & - DW_VR_MII_TX_CONFIG_MASK; - ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_CTRL, ret); - if (ret < 0) - return ret; - ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL1); + val |= FIELD_PREP(DW_VR_MII_TX_CONFIG_MASK, tx_conf); + + ret = xpcs_modify(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_CTRL, mask, val); if (ret < 0) return ret; + mask = DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW; if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) - ret |= DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW; - else - ret &= ~DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW; + val = DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW; - if (xpcs->info.pma == WX_TXGBE_XPCS_PMA_10G_ID) - ret |= DW_VR_MII_DIG_CTRL1_PHY_MODE_CTRL; + if (xpcs->info.pma == WX_TXGBE_XPCS_PMA_10G_ID) { + mask |= DW_VR_MII_DIG_CTRL1_PHY_MODE_CTRL; + val |= DW_VR_MII_DIG_CTRL1_PHY_MODE_CTRL; + } - ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL1, ret); + ret = xpcs_modify(xpcs, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL1, mask, val); if (ret < 0) return ret; @@ -745,9 +709,7 @@ static int xpcs_config_aneg_c37_1000basex(struct dw_xpcs *xpcs, phy_interface_t interface = PHY_INTERFACE_MODE_1000BASEX; int ret, mdio_ctrl, adv; bool changed = 0; - - if (xpcs->info.pma == WX_TXGBE_XPCS_PMA_10G_ID) - xpcs_write_vpcs(xpcs, DW_VR_XS_PCS_DIG_CTRL1, DW_CL37_BP | DW_EN_VSMMD1); + u16 mask, val; /* According to Chap 7.12, to set 1000BASE-X C37 AN, AN must * be disabled first:- @@ -765,14 +727,16 @@ static int xpcs_config_aneg_c37_1000basex(struct dw_xpcs *xpcs, return ret; } - ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_CTRL); - if (ret < 0) - return ret; + mask = DW_VR_MII_PCS_MODE_MASK; + val = FIELD_PREP(DW_VR_MII_PCS_MODE_MASK, + DW_VR_MII_PCS_MODE_C37_1000BASEX); + + if (!xpcs->pcs.poll) { + mask |= DW_VR_MII_AN_INTR_EN; + val |= DW_VR_MII_AN_INTR_EN; + } - ret &= ~DW_VR_MII_PCS_MODE_MASK; - if (!xpcs->pcs.poll) - ret |= DW_VR_MII_AN_INTR_EN; - ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_CTRL, ret); + ret = xpcs_modify(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_CTRL, mask, val); if (ret < 0) return ret; @@ -809,31 +773,26 @@ static int xpcs_config_2500basex(struct dw_xpcs *xpcs) { int ret; - ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL1); - if (ret < 0) - return ret; - ret |= DW_VR_MII_DIG_CTRL1_2G5_EN; - ret &= ~DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW; - ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL1, ret); + ret = xpcs_modify(xpcs, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL1, + DW_VR_MII_DIG_CTRL1_2G5_EN | + DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW, + DW_VR_MII_DIG_CTRL1_2G5_EN); if (ret < 0) return ret; - ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_MMD_CTRL); - if (ret < 0) - return ret; - ret &= ~AN_CL37_EN; - ret |= SGMII_SPEED_SS6; - ret &= ~SGMII_SPEED_SS13; - return xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_MMD_CTRL, ret); + return xpcs_modify(xpcs, MDIO_MMD_VEND2, DW_VR_MII_MMD_CTRL, + AN_CL37_EN | SGMII_SPEED_SS6 | SGMII_SPEED_SS13, + SGMII_SPEED_SS6); } -int xpcs_do_config(struct dw_xpcs *xpcs, phy_interface_t interface, - const unsigned long *advertising, unsigned int neg_mode) +static int xpcs_do_config(struct dw_xpcs *xpcs, phy_interface_t interface, + const unsigned long *advertising, + unsigned int neg_mode) { const struct dw_xpcs_compat *compat; int ret; - compat = xpcs_find_compat(xpcs->desc, interface); + compat = xpcs_find_compat(xpcs, interface); if (!compat) return -ENODEV; @@ -841,6 +800,14 @@ int xpcs_do_config(struct dw_xpcs *xpcs, phy_interface_t interface, ret = txgbe_xpcs_switch_mode(xpcs, interface); if (ret) return ret; + + /* Wangxun devices need backplane CL37 AN enabled for + * SGMII and 1000base-X + */ + if (interface == PHY_INTERFACE_MODE_SGMII || + interface == PHY_INTERFACE_MODE_1000BASEX) + xpcs_write_vpcs(xpcs, DW_VR_XS_PCS_DIG_CTRL1, + DW_CL37_BP | DW_EN_VSMMD1); } switch (compat->an_mode) { @@ -881,7 +848,6 @@ int xpcs_do_config(struct dw_xpcs *xpcs, phy_interface_t interface, return 0; } -EXPORT_SYMBOL_GPL(xpcs_do_config); static int xpcs_config(struct phylink_pcs *pcs, unsigned int neg_mode, phy_interface_t interface, @@ -989,8 +955,7 @@ static int xpcs_get_state_c37_sgmii(struct dw_xpcs *xpcs, state->link = true; - speed_value = (ret & DW_VR_MII_AN_STS_C37_ANSGM_SP) >> - DW_VR_MII_AN_STS_C37_ANSGM_SP_SHIFT; + speed_value = FIELD_GET(DW_VR_MII_AN_STS_C37_ANSGM_SP, ret); if (speed_value == DW_VR_MII_C37_ANSGM_SP_1000) state->speed = SPEED_1000; else if (speed_value == DW_VR_MII_C37_ANSGM_SP_100) @@ -1098,7 +1063,7 @@ static void xpcs_get_state(struct phylink_pcs *pcs, const struct dw_xpcs_compat *compat; int ret; - compat = xpcs_find_compat(xpcs->desc, state->interface); + compat = xpcs_find_compat(xpcs, state->interface); if (!compat) return; @@ -1108,32 +1073,27 @@ static void xpcs_get_state(struct phylink_pcs *pcs, break; case DW_AN_C73: ret = xpcs_get_state_c73(xpcs, state, compat); - if (ret) { - pr_err("xpcs_get_state_c73 returned %pe\n", - ERR_PTR(ret)); - return; - } + if (ret) + dev_err(&xpcs->mdiodev->dev, "%s returned %pe\n", + "xpcs_get_state_c73", ERR_PTR(ret)); break; case DW_AN_C37_SGMII: ret = xpcs_get_state_c37_sgmii(xpcs, state); - if (ret) { - pr_err("xpcs_get_state_c37_sgmii returned %pe\n", - ERR_PTR(ret)); - } + if (ret) + dev_err(&xpcs->mdiodev->dev, "%s returned %pe\n", + "xpcs_get_state_c37_sgmii", ERR_PTR(ret)); break; case DW_AN_C37_1000BASEX: ret = xpcs_get_state_c37_1000basex(xpcs, state); - if (ret) { - pr_err("xpcs_get_state_c37_1000basex returned %pe\n", - ERR_PTR(ret)); - } + if (ret) + dev_err(&xpcs->mdiodev->dev, "%s returned %pe\n", + "xpcs_get_state_c37_1000basex", ERR_PTR(ret)); break; case DW_2500BASEX: ret = xpcs_get_state_2500basex(xpcs, state); - if (ret) { - pr_err("xpcs_get_state_2500basex returned %pe\n", - ERR_PTR(ret)); - } + if (ret) + dev_err(&xpcs->mdiodev->dev, "%s returned %pe\n", + "xpcs_get_state_2500basex", ERR_PTR(ret)); break; default: return; @@ -1151,7 +1111,8 @@ static void xpcs_link_up_sgmii(struct dw_xpcs *xpcs, unsigned int neg_mode, val = mii_bmcr_encode_fixed(speed, duplex); ret = xpcs_write(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1, val); if (ret) - pr_err("%s: xpcs_write returned %pe\n", __func__, ERR_PTR(ret)); + dev_err(&xpcs->mdiodev->dev, "%s: xpcs_write returned %pe\n", + __func__, ERR_PTR(ret)); } static void xpcs_link_up_1000basex(struct dw_xpcs *xpcs, unsigned int neg_mode, @@ -1169,22 +1130,25 @@ static void xpcs_link_up_1000basex(struct dw_xpcs *xpcs, unsigned int neg_mode, case SPEED_100: case SPEED_10: default: - pr_err("%s: speed = %d\n", __func__, speed); + dev_err(&xpcs->mdiodev->dev, "%s: speed = %d\n", + __func__, speed); return; } if (duplex == DUPLEX_FULL) val |= BMCR_FULLDPLX; else - pr_err("%s: half duplex not supported\n", __func__); + dev_err(&xpcs->mdiodev->dev, "%s: half duplex not supported\n", + __func__); ret = xpcs_write(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1, val); if (ret) - pr_err("%s: xpcs_write returned %pe\n", __func__, ERR_PTR(ret)); + dev_err(&xpcs->mdiodev->dev, "%s: xpcs_write returned %pe\n", + __func__, ERR_PTR(ret)); } -void xpcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode, - phy_interface_t interface, int speed, int duplex) +static void xpcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode, + phy_interface_t interface, int speed, int duplex) { struct dw_xpcs *xpcs = phylink_pcs_to_xpcs(pcs); @@ -1195,21 +1159,16 @@ void xpcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode, if (interface == PHY_INTERFACE_MODE_1000BASEX) return xpcs_link_up_1000basex(xpcs, neg_mode, speed, duplex); } -EXPORT_SYMBOL_GPL(xpcs_link_up); static void xpcs_an_restart(struct phylink_pcs *pcs) { struct dw_xpcs *xpcs = phylink_pcs_to_xpcs(pcs); - int ret; - ret = xpcs_read(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1); - if (ret >= 0) { - ret |= BMCR_ANRESTART; - xpcs_write(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1, ret); - } + xpcs_modify(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1, BMCR_ANRESTART, + BMCR_ANRESTART); } -static int xpcs_get_id(struct dw_xpcs *xpcs) +static int xpcs_read_ids(struct dw_xpcs *xpcs) { int ret; u32 id; @@ -1275,76 +1234,62 @@ static int xpcs_get_id(struct dw_xpcs *xpcs) return 0; } -static const struct dw_xpcs_compat synopsys_xpcs_compat[DW_XPCS_INTERFACE_MAX] = { - [DW_XPCS_USXGMII] = { +static const struct dw_xpcs_compat synopsys_xpcs_compat[] = { + { + .interface = PHY_INTERFACE_MODE_USXGMII, .supported = xpcs_usxgmii_features, - .interface = xpcs_usxgmii_interfaces, - .num_interfaces = ARRAY_SIZE(xpcs_usxgmii_interfaces), .an_mode = DW_AN_C73, - }, - [DW_XPCS_10GKR] = { + }, { + .interface = PHY_INTERFACE_MODE_10GKR, .supported = xpcs_10gkr_features, - .interface = xpcs_10gkr_interfaces, - .num_interfaces = ARRAY_SIZE(xpcs_10gkr_interfaces), .an_mode = DW_AN_C73, - }, - [DW_XPCS_XLGMII] = { + }, { + .interface = PHY_INTERFACE_MODE_XLGMII, .supported = xpcs_xlgmii_features, - .interface = xpcs_xlgmii_interfaces, - .num_interfaces = ARRAY_SIZE(xpcs_xlgmii_interfaces), .an_mode = DW_AN_C73, - }, - [DW_XPCS_10GBASER] = { + }, { + .interface = PHY_INTERFACE_MODE_10GBASER, .supported = xpcs_10gbaser_features, - .interface = xpcs_10gbaser_interfaces, - .num_interfaces = ARRAY_SIZE(xpcs_10gbaser_interfaces), .an_mode = DW_10GBASER, - }, - [DW_XPCS_SGMII] = { + }, { + .interface = PHY_INTERFACE_MODE_SGMII, .supported = xpcs_sgmii_features, - .interface = xpcs_sgmii_interfaces, - .num_interfaces = ARRAY_SIZE(xpcs_sgmii_interfaces), .an_mode = DW_AN_C37_SGMII, - }, - [DW_XPCS_1000BASEX] = { + }, { + .interface = PHY_INTERFACE_MODE_1000BASEX, .supported = xpcs_1000basex_features, - .interface = xpcs_1000basex_interfaces, - .num_interfaces = ARRAY_SIZE(xpcs_1000basex_interfaces), .an_mode = DW_AN_C37_1000BASEX, - }, - [DW_XPCS_2500BASEX] = { + }, { + .interface = PHY_INTERFACE_MODE_2500BASEX, .supported = xpcs_2500basex_features, - .interface = xpcs_2500basex_interfaces, - .num_interfaces = ARRAY_SIZE(xpcs_2500basex_interfaces), .an_mode = DW_2500BASEX, - }, + }, { + } }; -static const struct dw_xpcs_compat nxp_sja1105_xpcs_compat[DW_XPCS_INTERFACE_MAX] = { - [DW_XPCS_SGMII] = { +static const struct dw_xpcs_compat nxp_sja1105_xpcs_compat[] = { + { + .interface = PHY_INTERFACE_MODE_SGMII, .supported = xpcs_sgmii_features, - .interface = xpcs_sgmii_interfaces, - .num_interfaces = ARRAY_SIZE(xpcs_sgmii_interfaces), .an_mode = DW_AN_C37_SGMII, .pma_config = nxp_sja1105_sgmii_pma_config, - }, + }, { + } }; -static const struct dw_xpcs_compat nxp_sja1110_xpcs_compat[DW_XPCS_INTERFACE_MAX] = { - [DW_XPCS_SGMII] = { +static const struct dw_xpcs_compat nxp_sja1110_xpcs_compat[] = { + { + .interface = PHY_INTERFACE_MODE_SGMII, .supported = xpcs_sgmii_features, - .interface = xpcs_sgmii_interfaces, - .num_interfaces = ARRAY_SIZE(xpcs_sgmii_interfaces), .an_mode = DW_AN_C37_SGMII, .pma_config = nxp_sja1110_sgmii_pma_config, - }, - [DW_XPCS_2500BASEX] = { + }, { + .interface = PHY_INTERFACE_MODE_2500BASEX, .supported = xpcs_2500basex_features, - .interface = xpcs_2500basex_interfaces, - .num_interfaces = ARRAY_SIZE(xpcs_2500basex_interfaces), .an_mode = DW_2500BASEX, .pma_config = nxp_sja1110_2500basex_pma_config, - }, + }, { + } }; static const struct dw_xpcs_desc xpcs_desc_list[] = { @@ -1365,12 +1310,33 @@ static const struct dw_xpcs_desc xpcs_desc_list[] = { static const struct phylink_pcs_ops xpcs_phylink_ops = { .pcs_validate = xpcs_validate, + .pcs_pre_config = xpcs_pre_config, .pcs_config = xpcs_config, .pcs_get_state = xpcs_get_state, .pcs_an_restart = xpcs_an_restart, .pcs_link_up = xpcs_link_up, }; +static int xpcs_identify(struct dw_xpcs *xpcs) +{ + int i, ret; + + ret = xpcs_read_ids(xpcs); + if (ret < 0) + return ret; + + for (i = 0; i < ARRAY_SIZE(xpcs_desc_list); i++) { + const struct dw_xpcs_desc *entry = &xpcs_desc_list[i]; + + if ((xpcs->info.pcs & entry->mask) == entry->id) { + xpcs->desc = entry; + return 0; + } + } + + return -ENODEV; +} + static struct dw_xpcs *xpcs_create_data(struct mdio_device *mdiodev) { struct dw_xpcs *xpcs; @@ -1427,7 +1393,6 @@ static void xpcs_clear_clks(struct dw_xpcs *xpcs) static int xpcs_init_id(struct dw_xpcs *xpcs) { const struct dw_xpcs_info *info; - int i, ret; info = dev_get_platdata(&xpcs->mdiodev->dev); if (!info) { @@ -1437,45 +1402,10 @@ static int xpcs_init_id(struct dw_xpcs *xpcs) xpcs->info = *info; } - ret = xpcs_get_id(xpcs); - if (ret < 0) - return ret; - - for (i = 0; i < ARRAY_SIZE(xpcs_desc_list); i++) { - const struct dw_xpcs_desc *desc = &xpcs_desc_list[i]; - - if ((xpcs->info.pcs & desc->mask) != desc->id) - continue; - - xpcs->desc = desc; - - break; - } - - if (!xpcs->desc) - return -ENODEV; - - return 0; -} - -static int xpcs_init_iface(struct dw_xpcs *xpcs, phy_interface_t interface) -{ - const struct dw_xpcs_compat *compat; - - compat = xpcs_find_compat(xpcs->desc, interface); - if (!compat) - return -EINVAL; - - if (xpcs->info.pma == WX_TXGBE_XPCS_PMA_10G_ID) { - xpcs->pcs.poll = false; - return 0; - } - - return xpcs_soft_reset(xpcs, compat); + return xpcs_identify(xpcs); } -static struct dw_xpcs *xpcs_create(struct mdio_device *mdiodev, - phy_interface_t interface) +static struct dw_xpcs *xpcs_create(struct mdio_device *mdiodev) { struct dw_xpcs *xpcs; int ret; @@ -1492,9 +1422,10 @@ static struct dw_xpcs *xpcs_create(struct mdio_device *mdiodev, if (ret) goto out_clear_clks; - ret = xpcs_init_iface(xpcs, interface); - if (ret) - goto out_clear_clks; + if (xpcs->info.pma == WX_TXGBE_XPCS_PMA_10G_ID) + xpcs->pcs.poll = false; + else + xpcs->need_reset = true; return xpcs; @@ -1511,14 +1442,12 @@ static struct dw_xpcs *xpcs_create(struct mdio_device *mdiodev, * xpcs_create_mdiodev() - create a DW xPCS instance with the MDIO @addr * @bus: pointer to the MDIO-bus descriptor for the device to be looked at * @addr: device MDIO-bus ID - * @interface: requested PHY interface * * Return: a pointer to the DW XPCS handle if successful, otherwise -ENODEV if * the PCS device couldn't be found on the bus and other negative errno related * to the data allocation and MDIO-bus communications. */ -struct dw_xpcs *xpcs_create_mdiodev(struct mii_bus *bus, int addr, - phy_interface_t interface) +struct dw_xpcs *xpcs_create_mdiodev(struct mii_bus *bus, int addr) { struct mdio_device *mdiodev; struct dw_xpcs *xpcs; @@ -1527,7 +1456,7 @@ struct dw_xpcs *xpcs_create_mdiodev(struct mii_bus *bus, int addr, if (IS_ERR(mdiodev)) return ERR_CAST(mdiodev); - xpcs = xpcs_create(mdiodev, interface); + xpcs = xpcs_create(mdiodev); /* xpcs_create() has taken a refcount on the mdiodev if it was * successful. If xpcs_create() fails, this will free the mdio @@ -1541,10 +1470,21 @@ struct dw_xpcs *xpcs_create_mdiodev(struct mii_bus *bus, int addr, } EXPORT_SYMBOL_GPL(xpcs_create_mdiodev); +struct phylink_pcs *xpcs_create_pcs_mdiodev(struct mii_bus *bus, int addr) +{ + struct dw_xpcs *xpcs; + + xpcs = xpcs_create_mdiodev(bus, addr); + if (IS_ERR(xpcs)) + return ERR_CAST(xpcs); + + return &xpcs->pcs; +} +EXPORT_SYMBOL_GPL(xpcs_create_pcs_mdiodev); + /** * xpcs_create_fwnode() - Create a DW xPCS instance from @fwnode * @fwnode: fwnode handle poining to the DW XPCS device - * @interface: requested PHY interface * * Return: a pointer to the DW XPCS handle if successful, otherwise -ENODEV if * the fwnode device is unavailable or the PCS device couldn't be found on the @@ -1552,8 +1492,7 @@ EXPORT_SYMBOL_GPL(xpcs_create_mdiodev); * other negative errno related to the data allocations and MDIO-bus * communications. */ -struct dw_xpcs *xpcs_create_fwnode(struct fwnode_handle *fwnode, - phy_interface_t interface) +struct dw_xpcs *xpcs_create_fwnode(struct fwnode_handle *fwnode) { struct mdio_device *mdiodev; struct dw_xpcs *xpcs; @@ -1565,7 +1504,7 @@ struct dw_xpcs *xpcs_create_fwnode(struct fwnode_handle *fwnode, if (!mdiodev) return ERR_PTR(-EPROBE_DEFER); - xpcs = xpcs_create(mdiodev, interface); + xpcs = xpcs_create(mdiodev); /* xpcs_create() has taken a refcount on the mdiodev if it was * successful. If xpcs_create() fails, this will free the mdio @@ -1590,5 +1529,11 @@ void xpcs_destroy(struct dw_xpcs *xpcs) } EXPORT_SYMBOL_GPL(xpcs_destroy); +void xpcs_destroy_pcs(struct phylink_pcs *pcs) +{ + xpcs_destroy(phylink_pcs_to_xpcs(pcs)); +} +EXPORT_SYMBOL_GPL(xpcs_destroy_pcs); + MODULE_DESCRIPTION("Synopsys DesignWare XPCS library"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/pcs/pcs-xpcs.h b/drivers/net/pcs/pcs-xpcs.h index fa05adfae220..9a22eed4404d 100644 --- a/drivers/net/pcs/pcs-xpcs.h +++ b/drivers/net/pcs/pcs-xpcs.h @@ -60,8 +60,6 @@ #define DW_VR_MII_DIG_CTRL1 0x8000 #define DW_VR_MII_AN_CTRL 0x8001 #define DW_VR_MII_AN_INTR_STS 0x8002 -/* Enable 2.5G Mode */ -#define DW_VR_MII_DIG_CTRL1_2G5_EN BIT(2) /* EEE Mode Control Register */ #define DW_VR_MII_EEE_MCTRL0 0x8006 #define DW_VR_MII_EEE_MCTRL1 0x800b @@ -69,6 +67,7 @@ /* VR_MII_DIG_CTRL1 */ #define DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW BIT(9) +#define DW_VR_MII_DIG_CTRL1_2G5_EN BIT(2) #define DW_VR_MII_DIG_CTRL1_PHY_MODE_CTRL BIT(0) /* VR_MII_DIG_CTRL2 */ @@ -77,11 +76,9 @@ /* VR_MII_AN_CTRL */ #define DW_VR_MII_AN_CTRL_8BIT BIT(8) -#define DW_VR_MII_AN_CTRL_TX_CONFIG_SHIFT 3 #define DW_VR_MII_TX_CONFIG_MASK BIT(3) #define DW_VR_MII_TX_CONFIG_PHY_SIDE_SGMII 0x1 #define DW_VR_MII_TX_CONFIG_MAC_SIDE_SGMII 0x0 -#define DW_VR_MII_AN_CTRL_PCS_MODE_SHIFT 1 #define DW_VR_MII_PCS_MODE_MASK GENMASK(2, 1) #define DW_VR_MII_PCS_MODE_C37_1000BASEX 0x0 #define DW_VR_MII_PCS_MODE_C37_SGMII 0x2 @@ -90,7 +87,6 @@ /* VR_MII_AN_INTR_STS */ #define DW_VR_MII_AN_STS_C37_ANCMPLT_INTR BIT(0) #define DW_VR_MII_AN_STS_C37_ANSGM_FD BIT(1) -#define DW_VR_MII_AN_STS_C37_ANSGM_SP_SHIFT 2 #define DW_VR_MII_AN_STS_C37_ANSGM_SP GENMASK(3, 2) #define DW_VR_MII_C37_ANSGM_SP_10 0x0 #define DW_VR_MII_C37_ANSGM_SP_100 0x1 @@ -114,7 +110,6 @@ #define DW_VR_MII_EEE_TX_EN_CTRL BIT(4) /* Tx Control Enable */ #define DW_VR_MII_EEE_RX_EN_CTRL BIT(7) /* Rx Control Enable */ -#define DW_VR_MII_EEE_MULT_FACT_100NS_SHIFT 8 #define DW_VR_MII_EEE_MULT_FACT_100NS GENMASK(11, 8) /* VR MII EEE Control 1 defines */ @@ -123,8 +118,27 @@ #define DW_XPCS_INFO_DECLARE(_name, _pcs, _pma) \ static const struct dw_xpcs_info _name = { .pcs = _pcs, .pma = _pma } +struct dw_xpcs_desc; + +enum dw_xpcs_clock { + DW_XPCS_CORE_CLK, + DW_XPCS_PAD_CLK, + DW_XPCS_NUM_CLKS, +}; + +struct dw_xpcs { + struct dw_xpcs_info info; + const struct dw_xpcs_desc *desc; + struct mdio_device *mdiodev; + struct clk_bulk_data clks[DW_XPCS_NUM_CLKS]; + struct phylink_pcs pcs; + phy_interface_t interface; + bool need_reset; +}; + int xpcs_read(struct dw_xpcs *xpcs, int dev, u32 reg); int xpcs_write(struct dw_xpcs *xpcs, int dev, u32 reg, u16 val); +int xpcs_modify(struct dw_xpcs *xpcs, int dev, u32 reg, u16 mask, u16 set); int xpcs_read_vpcs(struct dw_xpcs *xpcs, int reg); int xpcs_write_vpcs(struct dw_xpcs *xpcs, int reg, u16 val); int nxp_sja1105_sgmii_pma_config(struct dw_xpcs *xpcs); diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index 202ed7f450da..217e205d56f4 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -28,7 +28,7 @@ obj-$(CONFIG_PHYLIB) += libphy.o obj-$(CONFIG_NETWORK_PHY_TIMESTAMPING) += mii_timestamper.o -obj-$(CONFIG_SFP) += sfp.o +obj-$(CONFIG_SFP) += sff.o sfp.o sfp-obj-$(CONFIG_SFP) += sfp-bus.o obj-y += $(sfp-obj-y) $(sfp-obj-m) diff --git a/drivers/net/phy/aquantia/aquantia_main.c b/drivers/net/phy/aquantia/aquantia_main.c index e982e9ce44a5..15021e36e468 100644 --- a/drivers/net/phy/aquantia/aquantia_main.c +++ b/drivers/net/phy/aquantia/aquantia_main.c @@ -767,6 +767,19 @@ static int aqr111_config_init(struct phy_device *phydev) return aqr107_config_init(phydev); } +static int aqr113c_probe(struct phy_device *phydev) +{ + unsigned long *supported = phydev->supported_interfaces; + + __set_bit(PHY_INTERFACE_MODE_USXGMII, supported); + __set_bit(PHY_INTERFACE_MODE_10GBASER, supported); + __set_bit(PHY_INTERFACE_MODE_5GBASER, supported); + __set_bit(PHY_INTERFACE_MODE_2500BASEX, supported); + __set_bit(PHY_INTERFACE_MODE_SGMII, supported); + + return aqr107_probe(phydev); +} + static struct phy_driver aqr_driver[] = { { PHY_ID_MATCH_MODEL(PHY_ID_AQ1202), @@ -974,7 +987,7 @@ static struct phy_driver aqr_driver[] = { { PHY_ID_MATCH_MODEL(PHY_ID_AQR113C), .name = "Aquantia AQR113C", - .probe = aqr107_probe, + .probe = aqr113c_probe, .get_rate_matching = aqr107_get_rate_matching, .config_init = aqr113c_config_init, .config_aneg = aqr_config_aneg, diff --git a/drivers/net/phy/bcm84881.c b/drivers/net/phy/bcm84881.c index f1d47c264058..95c239474b43 100644 --- a/drivers/net/phy/bcm84881.c +++ b/drivers/net/phy/bcm84881.c @@ -63,6 +63,10 @@ static int bcm84881_probe(struct phy_device *phydev) (phydev->c45_ids.devices_in_package & mmd_mask) != mmd_mask) return -ENODEV; + __set_bit(PHY_INTERFACE_MODE_SGMII, phydev->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_2500BASEX, phydev->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_10GBASER, phydev->supported_interfaces); + return 0; } @@ -235,11 +239,21 @@ static int bcm84881_read_status(struct phy_device *phydev) return genphy_c45_read_mdix(phydev); } +/* The Broadcom BCM84881 in the Methode DM7052 is unable to provide a SGMII + * or 802.3z control word, so inband will not work. + */ +static unsigned int bcm84881_query_inband(struct phy_device *phydev, + phy_interface_t interface) +{ + return LINK_INBAND_DISABLE; +} + static struct phy_driver bcm84881_drivers[] = { { .phy_id = 0xae025150, .phy_id_mask = 0xfffffff0, .name = "Broadcom BCM84881", + .query_inband = bcm84881_query_inband, .config_init = bcm84881_config_init, .probe = bcm84881_probe, .get_features = bcm84881_get_features, diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index b89fbffa6a93..9a24752382d5 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -176,6 +176,10 @@ #define MII_M1011_PHY_STATUS_FULLDUPLEX 0x2000 #define MII_M1011_PHY_STATUS_RESOLVED 0x0800 #define MII_M1011_PHY_STATUS_LINK 0x0400 +#define MII_M1111_PHY_STATUS_TX_PAUSE 0x0008 +#define MII_M1111_PHY_STATUS_RX_PAUSE 0x0004 +#define MII_88E151X_PHY_STATUS_TX_PAUSE 0x0200 +#define MII_88E151X_PHY_STATUS_RX_PAUSE 0x0100 #define MII_88E3016_PHY_SPEC_CTRL 0x10 #define MII_88E3016_DISABLE_SCRAMBLER 0x0200 @@ -353,6 +357,8 @@ struct marvell_priv { u32 step; s8 pair; u8 vct_phase; + u16 tx_pause_mask; + u16 rx_pause_mask; }; static int marvell_read_page(struct phy_device *phydev) @@ -716,6 +722,19 @@ static int marvell_config_aneg_fiber(struct phy_device *phydev) return genphy_check_and_restart_aneg(phydev, changed); } +static unsigned int m88e1111_query_inband(struct phy_device *phydev, + phy_interface_t interface) +{ + /* In 1000base-X and SGMII modes, the inband mode can be changed + * through the Fibre page BMCR ANENABLE bit. + */ + if (interface == PHY_INTERFACE_MODE_1000BASEX || + interface == PHY_INTERFACE_MODE_SGMII) + return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE; + + return 0; +} + static int m88e1111_config_aneg(struct phy_device *phydev) { int extsr = phy_read(phydev, MII_M1111_PHY_EXT_SR); @@ -1623,6 +1642,7 @@ static void fiber_lpa_mod_linkmode_lpa_t(unsigned long *advertising, u32 lpa) static int marvell_read_status_page_an(struct phy_device *phydev, int fiber, int status) { + struct marvell_priv *priv = phydev->priv; int lpa; int err; @@ -1678,6 +1698,11 @@ static int marvell_read_status_page_an(struct phy_device *phydev, } } + phydev->resolved_tx_pause = !!(status & priv->tx_pause_mask); + phydev->resolved_rx_pause = !!(status & priv->rx_pause_mask); + phydev->resolved_pause_valid = !fiber && priv->tx_pause_mask && + priv->rx_pause_mask; + return 0; } @@ -1721,6 +1746,7 @@ static int marvell_read_status_page(struct phy_device *phydev, int page) phydev->speed = SPEED_UNKNOWN; phydev->duplex = DUPLEX_UNKNOWN; phydev->port = fiber ? PORT_FIBRE : PORT_TP; + phydev->resolved_pause_valid = false; if (phydev->autoneg == AUTONEG_ENABLE) err = marvell_read_status_page_an(phydev, fiber, status); @@ -3518,14 +3544,26 @@ static int m88e1318_led_hw_control_get(struct phy_device *phydev, u8 index, return marvell_get_led_rules(index, rules, mode); } -static int marvell_probe(struct phy_device *phydev) +static int marvell_probe_pause(struct phy_device *phydev, u16 tx_pause_mask, + u16 rx_pause_mask) { struct marvell_priv *priv; + __set_bit(PHY_INTERFACE_MODE_GMII, phydev->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_SGMII, phydev->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_TBI, phydev->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_RGMII, phydev->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_RGMII_ID, phydev->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_RGMII_RXID, phydev->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_RGMII_TXID, phydev->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_RTBI, phydev->supported_interfaces); + priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; + priv->tx_pause_mask = tx_pause_mask; + priv->rx_pause_mask = rx_pause_mask; phydev->priv = priv; return marvell_hwmon_probe(phydev); @@ -3615,11 +3653,23 @@ static const struct sfp_upstream_ops m88e1510_sfp_ops = { .detach = phy_sfp_detach, }; +static int marvell_probe(struct phy_device *phydev) +{ + return marvell_probe_pause(phydev, 0, 0); +} + +static int m88e1111_probe(struct phy_device *phydev) +{ + return marvell_probe_pause(phydev, MII_M1111_PHY_STATUS_TX_PAUSE, + MII_M1111_PHY_STATUS_RX_PAUSE); +} + static int m88e1510_probe(struct phy_device *phydev) { int err; - err = marvell_probe(phydev); + err = marvell_probe_pause(phydev, MII_88E151X_PHY_STATUS_TX_PAUSE, + MII_88E151X_PHY_STATUS_RX_PAUSE); if (err) return err; @@ -3665,6 +3715,7 @@ static struct phy_driver marvell_drivers[] = { .name = "Marvell 88E1112", /* PHY_GBIT_FEATURES */ .probe = marvell_probe, + .query_inband = m88e1111_query_inband, .config_init = m88e1112_config_init, .config_aneg = marvell_config_aneg, .config_intr = marvell_config_intr, @@ -3685,7 +3736,8 @@ static struct phy_driver marvell_drivers[] = { .name = "Marvell 88E1111", /* PHY_GBIT_FEATURES */ .flags = PHY_POLL_CABLE_TEST, - .probe = marvell_probe, + .probe = m88e1111_probe, + .query_inband = m88e1111_query_inband, .config_init = m88e1111gbe_config_init, .config_aneg = m88e1111_config_aneg, .read_status = marvell_read_status, @@ -3709,6 +3761,7 @@ static struct phy_driver marvell_drivers[] = { .name = "Marvell 88E1111 (Finisar)", /* PHY_GBIT_FEATURES */ .probe = marvell_probe, + .query_inband = m88e1111_query_inband, .config_init = m88e1111gbe_config_init, .config_aneg = m88e1111_config_aneg, .read_status = marvell_read_status, @@ -3911,7 +3964,7 @@ static struct phy_driver marvell_drivers[] = { .driver_data = DEF_MARVELL_HWMON_OPS(m88e1510_hwmon_ops), /* PHY_GBIT_FEATURES */ .flags = PHY_POLL_CABLE_TEST, - .probe = marvell_probe, + .probe = m88e1510_probe, .config_init = marvell_1011gbe_config_init, .config_aneg = m88e1510_config_aneg, .read_status = marvell_read_status, @@ -3940,7 +3993,7 @@ static struct phy_driver marvell_drivers[] = { .phy_id_mask = MARVELL_PHY_ID_MASK, .name = "Marvell 88E1545", .driver_data = DEF_MARVELL_HWMON_OPS(m88e1510_hwmon_ops), - .probe = marvell_probe, + .probe = m88e1510_probe, /* PHY_GBIT_FEATURES */ .flags = PHY_POLL_CABLE_TEST, .config_init = marvell_1011gbe_config_init, @@ -4007,7 +4060,7 @@ static struct phy_driver marvell_drivers[] = { .driver_data = DEF_MARVELL_HWMON_OPS(m88e1510_hwmon_ops), /* PHY_GBIT_FEATURES */ .flags = PHY_POLL_CABLE_TEST, - .probe = marvell_probe, + .probe = m88e1510_probe, .config_init = marvell_1011gbe_config_init, .config_aneg = m88e6390_config_aneg, .read_status = marvell_read_status, diff --git a/drivers/net/phy/marvell10g.c b/drivers/net/phy/marvell10g.c index ad43e280930c..eb05a578dbd7 100644 --- a/drivers/net/phy/marvell10g.c +++ b/drivers/net/phy/marvell10g.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -82,6 +83,8 @@ enum { MV_PCS_CSSR1_SPD1_10 = 0x0000, MV_PCS_CSSR1_DUPLEX_FULL= BIT(13), MV_PCS_CSSR1_RESOLVED = BIT(11), + MV_PCS_CSSR1_TX_PAUSE = BIT(9), + MV_PCS_CSSR1_RX_PAUSE = BIT(8), MV_PCS_CSSR1_MDIX = BIT(6), MV_PCS_CSSR1_SPD2_MASK = 0x000c, MV_PCS_CSSR1_SPD2_5000 = 0x0008, @@ -163,14 +166,16 @@ struct mv3310_chip { }; struct mv3310_priv { - DECLARE_BITMAP(supported_interfaces, PHY_INTERFACE_MODE_MAX); const struct mv3310_mactype *mactype; u32 firmware_ver; bool has_downshift; + bool firmware_failed; struct device *hwmon_dev; char *hwmon_name; + u8 num_leds; + u16 led_mode[4]; }; static const struct mv3310_chip *to_mv3310_chip(struct phy_device *phydev) @@ -506,6 +511,43 @@ static const struct sfp_upstream_ops mv3310_sfp_ops = { .module_insert = mv3310_sfp_insert, }; +static int mv3310_leds_write(struct phy_device *phydev) +{ + struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev); + int i, ret; + + for (i = 0; i < priv->num_leds; i++) { + ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, 0xf020 + i, + priv->led_mode[i]); + if (ret < 0) + return ret; + } + + return 0; +} + +static int mv3310_fw_config(struct phy_device *phydev) +{ + struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev); + struct device_node *node; + int ret; + + node = phydev->mdio.dev.of_node; + if (!node) + return 0; + + ret = of_property_read_variable_u16_array(node, "marvell,led-mode", + priv->led_mode, 1, ARRAY_SIZE(priv->led_mode)); + if (ret == -EINVAL) + ret = 0; + if (ret < 0) + return ret; + + priv->num_leds = ret; + + return 0; +} + static int mv3310_probe(struct phy_device *phydev) { const struct mv3310_chip *chip = to_mv3310_chip(phydev); @@ -517,6 +559,20 @@ static int mv3310_probe(struct phy_device *phydev) (phydev->c45_ids.devices_in_package & mmd_mask) != mmd_mask) return -ENODEV; + priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + dev_set_drvdata(&phydev->mdio.dev, priv); + + ret = mv3310_fw_config(phydev); + if (ret < 0) + return ret; + + ret = mv3310_leds_write(phydev); + if (ret < 0) + return ret; + ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MV_PMA_BOOT); if (ret < 0) return ret; @@ -524,15 +580,9 @@ static int mv3310_probe(struct phy_device *phydev) if (ret & MV_PMA_BOOT_FATAL) { dev_warn(&phydev->mdio.dev, "PHY failed to boot firmware, status=%04x\n", ret); - return -ENODEV; + priv->firmware_failed = true; } - priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - dev_set_drvdata(&phydev->mdio.dev, priv); - ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MV_PMA_FW_VER0); if (ret < 0) return ret; @@ -561,7 +611,7 @@ static int mv3310_probe(struct phy_device *phydev) if (ret) return ret; - chip->init_supported_interfaces(priv->supported_interfaces); + chip->init_supported_interfaces(phydev->supported_interfaces); return phy_sfp_probe(phydev, &mv3310_sfp_ops); } @@ -587,6 +637,19 @@ static int mv3310_resume(struct phy_device *phydev) return mv3310_hwmon_config(phydev, true); } +static int mv3310_start(struct phy_device *phydev) +{ + struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev); + + if (priv->firmware_failed) { + dev_warn(&phydev->mdio.dev, + "PHY firmware failure: PHY not starting"); + return -EINVAL; + } + + return 0; +} + /* Some PHYs in the Alaska family such as the 88X3310 and the 88E2010 * don't set bit 14 in PMA Extended Abilities (1.11), although they do * support 2.5GBASET and 5GBASET. For these models, we can still read their @@ -826,7 +889,7 @@ static int mv3310_config_init(struct phy_device *phydev) int err, mactype; /* Check that the PHY interface type is compatible */ - if (!test_bit(phydev->interface, priv->supported_interfaces)) + if (!test_bit(phydev->interface, phydev->supported_interfaces)) return -ENODEV; phydev->mdix_ctrl = ETH_TP_MDI_AUTO; @@ -873,7 +936,7 @@ static int mv3310_config_init(struct phy_device *phydev) if (err && err != -EOPNOTSUPP) return err; - return 0; + return mv3310_leds_write(phydev); } static int mv3310_get_features(struct phy_device *phydev) @@ -1090,6 +1153,10 @@ static int mv3310_read_status_copper(struct phy_device *phydev) phydev->mdix = cssr1 & MV_PCS_CSSR1_MDIX ? ETH_TP_MDI_X : ETH_TP_MDI; + phydev->resolved_tx_pause = !!(cssr1 & MV_PCS_CSSR1_TX_PAUSE); + phydev->resolved_rx_pause = !!(cssr1 & MV_PCS_CSSR1_RX_PAUSE); + phydev->resolved_pause_valid = true; + if (val & MDIO_AN_STAT1_COMPLETE) { val = genphy_c45_read_lpa(phydev); if (val < 0) @@ -1432,6 +1499,7 @@ static struct phy_driver mv3310_drivers[] = { .probe = mv3310_probe, .suspend = mv3310_suspend, .resume = mv3310_resume, + .start = mv3310_start, .config_aneg = mv3310_config_aneg, .aneg_done = mv3310_aneg_done, .read_status = mv3310_read_status, @@ -1469,6 +1537,7 @@ static struct phy_driver mv3310_drivers[] = { .probe = mv3310_probe, .suspend = mv3310_suspend, .resume = mv3310_resume, + .start = mv3310_start, .config_init = mv3310_config_init, .config_aneg = mv3310_config_aneg, .aneg_done = mv3310_aneg_done, diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 7e2f10182c0c..fada095a778e 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -1396,8 +1396,16 @@ static int mdio_bus_match(struct device *dev, const struct device_driver *drv) static int mdio_uevent(const struct device *dev, struct kobj_uevent_env *env) { + struct mdio_device *mdio = to_mdio_device(dev); int rc; + /* Use the device-specific uevent if specified */ + if (mdio->bus_uevent) { + rc = mdio->bus_uevent(mdio, env); + if (rc != -ENODEV) + return rc; + } + /* Some devices have extra OF data and an OF-style MODALIAS */ rc = of_device_uevent_modalias(dev, env); if (rc != -ENODEV) diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 785182fa5fe0..0ae2b0dbf94f 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -1000,6 +1000,27 @@ static int phy_check_link_status(struct phy_device *phydev) return 0; } +/** + * phy_query_inband - query which in-band signalling modes are supported + * @phydev: a pointer to a &struct phy_device + * @interface: the interface mode for the PHY + * + * Returns zero if it is unknown what in-band signalling is supported by the + * PHY (e.g. because the PHY driver doesn't implement the method.) Otherwise, + * returns a bit mask of the LINK_INBAND_* values from + * &enum link_inband_signalling to describe which inband modes are supported + * for this interface mode. + */ +unsigned int phy_query_inband(struct phy_device *phydev, + phy_interface_t interface) +{ + if (phydev->drv && phydev->drv->query_inband) + return phydev->drv->query_inband(phydev, interface); + + return 0; +} +EXPORT_SYMBOL_GPL(phy_query_inband); + /** * _phy_start_aneg - start auto-negotiation for this PHY device * @phydev: the phy_device struct @@ -1247,6 +1268,8 @@ void phy_stop_machine(struct phy_device *phydev) static void phy_process_error(struct phy_device *phydev) { + phydev_err(phydev, "Error detected, halting PHY\n"); + /* phydev->lock must be held for the state change to be safe */ if (!mutex_is_locked(&phydev->lock)) phydev_err(phydev, "PHY-device data unsafe context\n"); @@ -1273,7 +1296,6 @@ static void phy_error_precise(struct phy_device *phydev, */ void phy_error(struct phy_device *phydev) { - WARN_ON(1); phy_process_error(phydev); } EXPORT_SYMBOL(phy_error); @@ -1520,6 +1542,10 @@ void phy_stop(struct phy_device *phydev) phy_process_state_change(phydev, old_state); state_work = _phy_state_machine(phydev); + + if (phydev->drv->stop) + phydev->drv->stop(phydev); + mutex_unlock(&phydev->lock); _phy_state_machine_post_work(phydev, state_work); @@ -1552,6 +1578,9 @@ void phy_start(struct phy_device *phydev) goto out; } + if (phydev->drv->start && phydev->drv->start(phydev)) + goto out; + if (phydev->sfp_bus) sfp_upstream_start(phydev->sfp_bus); diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 6bb2793de0a9..629aca88bbd2 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -642,6 +642,19 @@ static int phy_request_driver_module(struct phy_device *dev, u32 phy_id) return 0; } +static int phy_bus_uevent(struct mdio_device *mdiodev, + struct kobj_uevent_env *env) +{ + struct phy_device *phydev; + + phydev = container_of(mdiodev, struct phy_device, mdio); + + add_uevent_var(env, "MODALIAS=" MDIO_MODULE_PREFIX MDIO_ID_FMT, + MDIO_ID_ARGS(phydev->phy_id)); + + return 0; +} + struct phy_device *phy_device_create(struct mii_bus *bus, int addr, u32 phy_id, bool is_c45, struct phy_c45_device_ids *c45_ids) @@ -661,6 +674,7 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, u32 phy_id, mdiodev->dev.type = &mdio_bus_phy_type; mdiodev->bus = bus; mdiodev->bus_match = phy_bus_match; + mdiodev->bus_uevent = phy_bus_uevent; mdiodev->addr = addr; mdiodev->flags = MDIO_DEVICE_FLAG_PHY; mdiodev->device_free = phy_mdio_device_free; @@ -3058,6 +3072,12 @@ void phy_get_pause(struct phy_device *phydev, bool *tx_pause, bool *rx_pause) return; } + if (phydev->resolved_pause_valid) { + *tx_pause = phydev->resolved_tx_pause; + *rx_pause = phydev->resolved_rx_pause; + return; + } + return linkmode_resolve_pause(phydev->advertising, phydev->lp_advertising, tx_pause, rx_pause); diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index 51c526d227fa..aa54eac2b9a0 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -56,7 +56,8 @@ struct phylink { struct phy_device *phydev; phy_interface_t link_interface; /* PHY_INTERFACE_xxx */ u8 cfg_link_an_mode; /* MLO_AN_xxx */ - u8 cur_link_an_mode; + u8 req_link_an_mode; /* Requested MLO_AN_xxx mode */ + u8 act_link_an_mode; /* Active MLO_AN_xxx mode */ u8 link_port; /* The current non-phy ethtool port */ __ETHTOOL_DECLARE_LINK_MODE_MASK(supported); @@ -79,13 +80,15 @@ struct phylink { unsigned int pcs_state; bool mac_link_dropped; - bool using_mac_select_pcs; struct sfp_bus *sfp_bus; bool sfp_may_have_phy; DECLARE_PHY_INTERFACE_MASK(sfp_interfaces); __ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support); u8 sfp_port; + + struct eee_config eee_cfg; + bool eee_active; }; #define phylink_printk(level, pl, fmt, ...) \ @@ -175,6 +178,24 @@ static const char *phylink_an_mode_str(unsigned int mode) return mode < ARRAY_SIZE(modestr) ? modestr[mode] : "unknown"; } +static const char *phylink_pcs_mode_str(unsigned int mode) +{ + if (!mode) + return "none"; + + if (mode & PHYLINK_PCS_NEG_OUTBAND) + return "outband"; + + if (mode & PHYLINK_PCS_NEG_INBAND) { + if (mode & PHYLINK_PCS_NEG_ENABLED) + return "inband/an-enabled"; + else + return "inband/an-disabled"; + } + + return "unknown"; +} + static unsigned int phylink_interface_signal_rate(phy_interface_t interface) { switch (interface) { @@ -656,17 +677,15 @@ static int phylink_validate_mac_and_pcs(struct phylink *pl, unsigned long *supported, struct phylink_link_state *state) { + struct phylink_pcs *pcs = NULL; unsigned long capabilities; - struct phylink_pcs *pcs; int ret; /* Get the PCS for this interface mode */ - if (pl->using_mac_select_pcs) { + if (pl->mac_ops->mac_select_pcs) { pcs = pl->mac_ops->mac_select_pcs(pl->config, state->interface); if (IS_ERR(pcs)) return PTR_ERR(pcs); - } else { - pcs = pl->pcs; } if (pcs) { @@ -774,8 +793,8 @@ static int phylink_validate(struct phylink *pl, unsigned long *supported, static int phylink_parse_fixedlink(struct phylink *pl, const struct fwnode_handle *fwnode) { + __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; struct fwnode_handle *fixed_node; - bool pause, asym_pause, autoneg; const struct phy_setting *s; struct gpio_desc *desc; u32 speed; @@ -848,22 +867,15 @@ static int phylink_parse_fixedlink(struct phylink *pl, linkmode_copy(pl->link_config.advertising, pl->supported); phylink_validate(pl, pl->supported, &pl->link_config); - pause = phylink_test(pl->supported, Pause); - asym_pause = phylink_test(pl->supported, Asym_Pause); - autoneg = phylink_test(pl->supported, Autoneg); s = phy_lookup_setting(pl->link_config.speed, pl->link_config.duplex, pl->supported, true); - linkmode_zero(pl->supported); - phylink_set(pl->supported, MII); - if (pause) - phylink_set(pl->supported, Pause); + linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT, mask); + linkmode_set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, mask); + linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, mask); + linkmode_and(pl->supported, pl->supported, mask); - if (asym_pause) - phylink_set(pl->supported, Asym_Pause); - - if (autoneg) - phylink_set(pl->supported, Autoneg); + phylink_set(pl->supported, MII); if (s) { __set_bit(s->bit, pl->supported); @@ -900,7 +912,7 @@ static int phylink_parse_mode(struct phylink *pl, if ((fwnode_property_read_string(fwnode, "managed", &managed) == 0 && strcmp(managed, "in-band-status") == 0)) { - if (pl->cfg_link_an_mode == MLO_AN_FIXED) { + if (phylink_mode_fixed(pl->cfg_link_an_mode)) { phylink_err(pl, "can't use both fixed-link and in-band-status\n"); return -EINVAL; @@ -909,7 +921,7 @@ static int phylink_parse_mode(struct phylink *pl, pl->cfg_link_an_mode = MLO_AN_INBAND; } - if (pl->cfg_link_an_mode == MLO_AN_INBAND) { + if (phylink_mode_inband(pl->cfg_link_an_mode)) { linkmode_zero(pl->supported); phylink_set(pl->supported, MII); phylink_set(pl->supported, Autoneg); @@ -988,6 +1000,15 @@ static void phylink_resolve_an_pause(struct phylink_link_state *state) } } +static unsigned int phylink_pcs_query_inband(struct phylink_pcs *pcs, + phy_interface_t interface) +{ + if (pcs && pcs->ops->pcs_query_inband) + return pcs->ops->pcs_query_inband(pcs, interface); + + return 0; +} + static void phylink_pcs_pre_config(struct phylink_pcs *pcs, phy_interface_t interface) { @@ -1041,15 +1062,34 @@ static void phylink_pcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode, pcs->ops->pcs_link_up(pcs, neg_mode, interface, speed, duplex); } +/* Query inband for a specific interface mode, asking the MAC for the + * PCS which will be used to handle the interface mode. + */ +static unsigned int phylink_query_inband(struct phylink *pl, + phy_interface_t interface) +{ + struct phylink_pcs *pcs; + + if (!pl->using_mac_select_pcs) + return 0; + + pcs = pl->mac_ops->mac_select_pcs(pl->config, interface); + if (!pcs) + return 0; + + return phylink_pcs_query_inband(pcs, interface); +} + static void phylink_pcs_poll_stop(struct phylink *pl) { - if (pl->cfg_link_an_mode == MLO_AN_INBAND) + if (phylink_mode_inband(pl->cfg_link_an_mode)) del_timer(&pl->link_poll); } static void phylink_pcs_poll_start(struct phylink *pl) { - if (pl->pcs && pl->pcs->poll && pl->cfg_link_an_mode == MLO_AN_INBAND) + if (pl->pcs && pl->pcs->poll && + phylink_mode_inband(pl->cfg_link_an_mode)) mod_timer(&pl->link_poll, jiffies + HZ); } @@ -1082,13 +1122,13 @@ static void phylink_mac_config(struct phylink *pl, phylink_dbg(pl, "%s: mode=%s/%s/%s adv=%*pb pause=%02x\n", - __func__, phylink_an_mode_str(pl->cur_link_an_mode), + __func__, phylink_an_mode_str(pl->act_link_an_mode), phy_modes(st.interface), phy_rate_matching_to_str(st.rate_matching), __ETHTOOL_LINK_MODE_MASK_NBITS, st.advertising, st.pause); - pl->mac_ops->mac_config(pl->config, pl->cur_link_an_mode, &st); + pl->mac_ops->mac_config(pl->config, pl->act_link_an_mode, &st); } static void phylink_pcs_an_restart(struct phylink *pl) @@ -1096,12 +1136,14 @@ static void phylink_pcs_an_restart(struct phylink *pl) if (pl->pcs && linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, pl->link_config.advertising) && phy_interface_mode_is_8023z(pl->link_config.interface) && - phylink_autoneg_inband(pl->cur_link_an_mode)) + phylink_autoneg_inband(pl->act_link_an_mode)) pl->pcs->ops->pcs_an_restart(pl->pcs); } /** * phylink_pcs_neg_mode() - helper to determine PCS inband mode + * @pl: a pointer to a &struct phylink returned from phylink_create() + * @pcs: a pointer to &struct phylink_pcs * @mode: one of %MLO_AN_FIXED, %MLO_AN_PHY, %MLO_AN_INBAND. * @interface: interface mode to be used * @advertising: adertisement ethtool link mode mask @@ -1119,11 +1161,19 @@ static void phylink_pcs_an_restart(struct phylink *pl) * Note: this is for cases where the PCS itself is involved in negotiation * (e.g. Clause 37, SGMII and similar) not Clause 73. */ -static unsigned int phylink_pcs_neg_mode(unsigned int mode, +static unsigned int phylink_pcs_neg_mode(struct phylink *pl, + struct phylink_pcs *pcs, + unsigned int mode, phy_interface_t interface, const unsigned long *advertising) { + unsigned int phy_link_mode = 0; + unsigned int pcs_link_mode; unsigned int neg_mode; + enum { + INBAND_CISCO_SGMII, + INBAND_8023Z, + } type; switch (interface) { case PHY_INTERFACE_MODE_SGMII: @@ -1136,10 +1186,7 @@ static unsigned int phylink_pcs_neg_mode(unsigned int mode, * inband communication. Note: there exist PHYs that run * with SGMII but do not send the inband data. */ - if (!phylink_autoneg_inband(mode)) - neg_mode = PHYLINK_PCS_NEG_OUTBAND; - else - neg_mode = PHYLINK_PCS_NEG_INBAND_ENABLED; + type = INBAND_CISCO_SGMII; break; case PHY_INTERFACE_MODE_1000BASEX: @@ -1150,23 +1197,128 @@ static unsigned int phylink_pcs_neg_mode(unsigned int mode, * as well, but drivers may not support this, so may * need to override this. */ - if (!phylink_autoneg_inband(mode)) - neg_mode = PHYLINK_PCS_NEG_OUTBAND; - else if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, - advertising)) - neg_mode = PHYLINK_PCS_NEG_INBAND_ENABLED; - else - neg_mode = PHYLINK_PCS_NEG_INBAND_DISABLED; + type = INBAND_8023Z; break; default: - neg_mode = PHYLINK_PCS_NEG_NONE; - break; + return PHYLINK_PCS_NEG_NONE; } + pcs_link_mode = phylink_pcs_query_inband(pcs, interface); + pcs_link_mode &= LINK_INBAND_DISABLE | LINK_INBAND_ENABLE; + if (pl->phydev) { + phy_link_mode = phy_query_inband(pl->phydev, interface); + phy_link_mode &= LINK_INBAND_DISABLE | LINK_INBAND_ENABLE; + } + + if (!phylink_autoneg_inband(mode)) { + const char *s1, *s2, *s3, *empty = ""; + + s1 = s2 = s3 = empty; + + if (pcs_link_mode == LINK_INBAND_ENABLE) + s1 = "PCS"; + + if (phy_link_mode == LINK_INBAND_ENABLE) + s3 = "PHY"; + + /* If either the PCS or PHY requires inband to be enabled, + * this is an invalid configuration. Provide a diagnostic + * message for this case, but don't try to force the issue. + */ + if (s1 != empty || s3 != empty) { + if (s1 != empty && s3 != empty) + s2 = " and "; + + phylink_warn(pl, + "firmware wants %s mode, but %s%s%s requires inband\n", + phylink_an_mode_str(mode), + s1, s2, s3); + } + return PHYLINK_PCS_NEG_OUTBAND; + } + + /* For SGMII modes, which are designed to be used with PHYs, we + * try to use inband mode where-ever possible. However, there are + * some PHYs e.g. BCM84881 which do not support in-band. + */ + if (type == INBAND_CISCO_SGMII) { + /* PCS PHY + * D E D E + * 0 0 0 0 no information inband enabled + * 1 0 0 0 pcs doesn't support outband + * 0 1 0 0 pcs required inband enabled + * 1 1 0 0 pcs optional inband enabled + * 0 0 1 0 phy doesn't support outband + * 1 0 1 0 pcs+phy doesn't support outband + * 0 1 1 0 pcs required, phy doesn't support, invalid + * 1 1 1 0 pcs optional, phy doesn't support, outband + * 0 0 0 1 phy required inband enabled + * 1 0 0 1 pcs doesn't support, phy required, invalid + * 0 1 0 1 pcs+phy required inband enabled + * 1 1 0 1 pcs optional, phy required inband enabled + * 0 0 1 1 phy optional inband enabled + * 1 0 1 1 pcs doesn't support, phy optional, outband + * 0 1 1 1 pcs required, phy optional inband enabled + * 1 1 1 1 pcs+phy optional inband enabled + */ + if (!pcs_link_mode) + pcs_link_mode = LINK_INBAND_DISABLE | LINK_INBAND_ENABLE; + + if (!phy_link_mode) + phy_link_mode = LINK_INBAND_DISABLE | LINK_INBAND_ENABLE; + + if (pcs_link_mode & phy_link_mode & LINK_INBAND_ENABLE) { + /* inband supported at both ends */ + return PHYLINK_PCS_NEG_INBAND_ENABLED; + } else if (pcs_link_mode & phy_link_mode & LINK_INBAND_DISABLE) { + /* inband not supported at both ends */ + return PHYLINK_PCS_NEG_OUTBAND; + } else { + /* invalid */ + } + } + + /* For 802.3z, handle the PHY present vs absent cases separately */ + if (!pl->phydev) { + /* PHY absent: always use inband but inband may be disabled */ + /* If the PCS doesn't support inband, then inband must be + * disabled. + */ + if (pcs_link_mode == LINK_INBAND_DISABLE) + return PHYLINK_PCS_NEG_INBAND_DISABLED; + + /* If the PCS requires inband, then inband must always be + * enabled. + */ + if (pcs_link_mode == LINK_INBAND_ENABLE) + return PHYLINK_PCS_NEG_INBAND_ENABLED; + + /* For the possible case, fall through to the "legacy" code. */ + } else { + /* PHY present, inband mode depends on the capabilities + * of both. + */ + } + + /* Legacy, so determine inband depending on the advertising bit */ + if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, advertising)) + neg_mode = PHYLINK_PCS_NEG_INBAND_ENABLED; + else + neg_mode = PHYLINK_PCS_NEG_INBAND_DISABLED; + return neg_mode; } +static u8 phylink_choose_act_link_an_mode(struct phylink *pl) +{ + if (pl->req_link_an_mode == MLO_AN_INBAND && pl->phydev && + pl->pcs_neg_mode == PHYLINK_PCS_NEG_OUTBAND) + return MLO_AN_PHY; + + return pl->req_link_an_mode; +} + static void phylink_major_config(struct phylink *pl, bool restart, const struct phylink_link_state *state) { @@ -1176,13 +1328,11 @@ static void phylink_major_config(struct phylink *pl, bool restart, unsigned int neg_mode; int err; - phylink_dbg(pl, "major config %s\n", phy_modes(state->interface)); - - pl->pcs_neg_mode = phylink_pcs_neg_mode(pl->cur_link_an_mode, - state->interface, - state->advertising); + phylink_dbg(pl, "major config, requested %s/%s\n", + phylink_an_mode_str(pl->req_link_an_mode), + phy_modes(state->interface)); - if (pl->using_mac_select_pcs) { + if (pl->mac_ops->mac_select_pcs) { pcs = pl->mac_ops->mac_select_pcs(pl->config, state->interface); if (IS_ERR(pcs)) { phylink_err(pl, @@ -1191,13 +1341,28 @@ static void phylink_major_config(struct phylink *pl, bool restart, return; } - pcs_changed = pcs && pl->pcs != pcs; + pcs_changed = pl->pcs != pcs; } + pl->pcs_neg_mode = phylink_pcs_neg_mode(pl, pcs, + pl->req_link_an_mode, + state->interface, + state->advertising); + + /* set the active link AN mode, which may end up different from the + * current link AN mode depending on the PCS and PHY capabilities. + */ + pl->act_link_an_mode = phylink_choose_act_link_an_mode(pl); + + phylink_dbg(pl, "major config, active %s/%s/%s\n", + phylink_an_mode_str(pl->act_link_an_mode), + phylink_pcs_mode_str(pl->pcs_neg_mode), + phy_modes(state->interface)); + phylink_pcs_poll_stop(pl); if (pl->mac_ops->mac_prepare) { - err = pl->mac_ops->mac_prepare(pl->config, pl->cur_link_an_mode, + err = pl->mac_ops->mac_prepare(pl->config, pl->act_link_an_mode, state->interface); if (err < 0) { phylink_err(pl, "mac_prepare failed: %pe\n", @@ -1231,7 +1396,7 @@ static void phylink_major_config(struct phylink *pl, bool restart, if (pl->pcs_state == PCS_STATE_STARTING || pcs_changed) phylink_pcs_enable(pl->pcs); - neg_mode = pl->cur_link_an_mode; + neg_mode = pl->act_link_an_mode; if (pl->pcs && pl->pcs->neg_mode) neg_mode = pl->pcs_neg_mode; @@ -1247,7 +1412,7 @@ static void phylink_major_config(struct phylink *pl, bool restart, phylink_pcs_an_restart(pl); if (pl->mac_ops->mac_finish) { - err = pl->mac_ops->mac_finish(pl->config, pl->cur_link_an_mode, + err = pl->mac_ops->mac_finish(pl->config, pl->act_link_an_mode, state->interface); if (err < 0) phylink_err(pl, "mac_finish failed: %pe\n", @@ -1278,17 +1443,23 @@ static int phylink_change_inband_advert(struct phylink *pl) return 0; phylink_dbg(pl, "%s: mode=%s/%s adv=%*pb pause=%02x\n", __func__, - phylink_an_mode_str(pl->cur_link_an_mode), + phylink_an_mode_str(pl->req_link_an_mode), phy_modes(pl->link_config.interface), __ETHTOOL_LINK_MODE_MASK_NBITS, pl->link_config.advertising, pl->link_config.pause); /* Recompute the PCS neg mode */ - pl->pcs_neg_mode = phylink_pcs_neg_mode(pl->cur_link_an_mode, - pl->link_config.interface, - pl->link_config.advertising); + pl->pcs_neg_mode = phylink_pcs_neg_mode(pl, pl->pcs, + pl->req_link_an_mode, + pl->link_config.interface, + pl->link_config.advertising); - neg_mode = pl->cur_link_an_mode; + /* set the active link AN mode, which may end up different from the + * current link AN mode depending on the PCS and PHY capabilities. + */ + pl->act_link_an_mode = phylink_choose_act_link_an_mode(pl); + + neg_mode = pl->act_link_an_mode; if (pl->pcs->neg_mode) neg_mode = pl->pcs_neg_mode; @@ -1353,7 +1524,7 @@ static void phylink_mac_initial_config(struct phylink *pl, bool force_restart) { struct phylink_link_state link_state; - switch (pl->cur_link_an_mode) { + switch (pl->req_link_an_mode) { case MLO_AN_PHY: link_state = pl->phy_state; break; @@ -1392,6 +1563,75 @@ static const char *phylink_pause_to_str(int pause) } } +static void phylink_disable_tx_lpi(struct phylink *pl) +{ + phylink_dbg(pl, "disabling tx_lpi\n"); + + if (pl->mac_ops->mac_disable_tx_lpi) + pl->mac_ops->mac_disable_tx_lpi(pl->config); +} + +static void phylink_enable_tx_lpi(struct phylink *pl) +{ + phylink_dbg(pl, "enabling tx_lpi, timer %uus\n", + pl->eee_cfg.tx_lpi_timer); + + if (pl->mac_ops->mac_enable_tx_lpi) + pl->mac_ops->mac_enable_tx_lpi(pl->config, + pl->eee_cfg.tx_lpi_timer); +} + +static bool phylink_eee_is_active(struct phylink *pl) +{ + return phylink_init_eee(pl, pl->config->eee_clk_stop_enable) >= 0; +} + +static void phylink_deactivate_eee(struct phylink *pl) +{ + phylink_dbg(pl, "deactivating EEE, was %sactive\n", + pl->eee_active ? "" : "in"); + + if (pl->eee_active) { + pl->eee_active = false; + phylink_disable_tx_lpi(pl); + } +} + +static void phylink_activate_eee(struct phylink *pl) +{ + pl->eee_active = phylink_eee_is_active(pl); + + phylink_dbg(pl, "can LPI, EEE enabled, %sactive\n", + pl->eee_active ? "" : "in"); + + if (pl->eee_active) + phylink_enable_tx_lpi(pl); +} + +/* Determine whether the MAC has new EEE support. We detect this by checking + * for the two new methods being present, but for DSA it will populate these + * anyway, so also check that lpi_capabilities is non-zero. + */ +static bool phylink_mac_supports_eee(struct phylink *pl) +{ + return pl->mac_ops->mac_disable_tx_lpi && + pl->mac_ops->mac_enable_tx_lpi && + pl->config->lpi_capabilities; +} + +static void phylink_phy_restrict_eee(struct phylink *pl, struct phy_device *phy) +{ + __ETHTOOL_DECLARE_LINK_MODE_MASK(eee_supported); + + /* Convert the MAC's LPI capabilities to linkmodes */ + linkmode_zero(eee_supported); + phylink_caps_to_linkmodes(eee_supported, pl->config->lpi_capabilities); + + /* Mask out EEE modes that are not supported */ + linkmode_and(phy->supported_eee, phy->supported_eee, eee_supported); + linkmode_and(phy->advertising_eee, phy->advertising_eee, eee_supported); +} + static void phylink_link_up(struct phylink *pl, struct phylink_link_state link_state) { @@ -1427,17 +1667,20 @@ static void phylink_link_up(struct phylink *pl, pl->cur_interface = link_state.interface; - neg_mode = pl->cur_link_an_mode; + neg_mode = pl->act_link_an_mode; if (pl->pcs && pl->pcs->neg_mode) neg_mode = pl->pcs_neg_mode; phylink_pcs_link_up(pl->pcs, neg_mode, pl->cur_interface, speed, duplex); - pl->mac_ops->mac_link_up(pl->config, pl->phydev, pl->cur_link_an_mode, + pl->mac_ops->mac_link_up(pl->config, pl->phydev, pl->act_link_an_mode, pl->cur_interface, speed, duplex, !!(link_state.pause & MLO_PAUSE_TX), rx_pause); + if (eeecfg_mac_can_tx_lpi(&pl->eee_cfg)) + phylink_activate_eee(pl); + if (ndev) netif_carrier_on(ndev); @@ -1454,25 +1697,29 @@ static void phylink_link_down(struct phylink *pl) if (ndev) netif_carrier_off(ndev); - pl->mac_ops->mac_link_down(pl->config, pl->cur_link_an_mode, + + phylink_deactivate_eee(pl); + + pl->mac_ops->mac_link_down(pl->config, pl->act_link_an_mode, pl->cur_interface); phylink_info(pl, "Link is Down\n"); } +static bool phylink_link_is_up(struct phylink *pl) +{ + return pl->netdev ? netif_carrier_ok(pl->netdev) : pl->old_link_state; +} + static void phylink_resolve(struct work_struct *w) { struct phylink *pl = container_of(w, struct phylink, resolve); struct phylink_link_state link_state; - struct net_device *ndev = pl->netdev; bool mac_config = false; bool retrigger = false; bool cur_link_state; mutex_lock(&pl->state_mutex); - if (pl->netdev) - cur_link_state = netif_carrier_ok(ndev); - else - cur_link_state = pl->old_link_state; + cur_link_state = phylink_link_is_up(pl); if (pl->phylink_disable_state) { pl->mac_link_dropped = false; @@ -1480,76 +1727,73 @@ static void phylink_resolve(struct work_struct *w) } else if (pl->mac_link_dropped) { link_state.link = false; retrigger = true; - } else { - switch (pl->cur_link_an_mode) { - case MLO_AN_PHY: + } else if (pl->act_link_an_mode == MLO_AN_FIXED) { + phylink_get_fixed_state(pl, &link_state); + mac_config = link_state.link; + } else if (pl->act_link_an_mode == MLO_AN_PHY) { + /* PHY mode, or PCS configured for outband mode */ + if (pl->phydev) { link_state = pl->phy_state; - phylink_apply_manual_flow(pl, &link_state); - mac_config = link_state.link; - break; + } else { + /* No PHY - assume link is down */ + link_state.link = false; + } + mac_config = link_state.link; + } else { + /* Inband mode */ + phylink_mac_pcs_get_state(pl, &link_state); - case MLO_AN_FIXED: - phylink_get_fixed_state(pl, &link_state); - mac_config = link_state.link; - break; + /* The PCS may have a latching link-fail indicator. + * If the link was up, bring the link down and + * re-trigger the resolve. Otherwise, re-read the + * PCS state to get the current status of the link. + */ + if (!link_state.link) { + if (cur_link_state) + retrigger = true; + else + phylink_mac_pcs_get_state(pl, &link_state); + } - case MLO_AN_INBAND: - phylink_mac_pcs_get_state(pl, &link_state); + /* If we have a phy, the "up" state is the union of + * both the PHY and the MAC + */ + if (pl->phydev) + link_state.link &= pl->phy_state.link; - /* The PCS may have a latching link-fail indicator. - * If the link was up, bring the link down and - * re-trigger the resolve. Otherwise, re-read the - * PCS state to get the current status of the link. + /* Only update if the PHY link is up */ + if (pl->phydev && pl->phy_state.link) { + /* If the interface has changed, force a + * link down event if the link isn't already + * down, and re-resolve. */ - if (!link_state.link) { - if (cur_link_state) - retrigger = true; - else - phylink_mac_pcs_get_state(pl, - &link_state); + if (link_state.interface != pl->phy_state.interface) { + retrigger = true; + link_state.link = false; } + link_state.interface = pl->phy_state.interface; - /* If we have a phy, the "up" state is the union of - * both the PHY and the MAC + /* If we are doing rate matching, then the + * link speed/duplex comes from the PHY */ - if (pl->phydev) - link_state.link &= pl->phy_state.link; - - /* Only update if the PHY link is up */ - if (pl->phydev && pl->phy_state.link) { - /* If the interface has changed, force a - * link down event if the link isn't already - * down, and re-resolve. - */ - if (link_state.interface != - pl->phy_state.interface) { - retrigger = true; - link_state.link = false; - } - link_state.interface = pl->phy_state.interface; - - /* If we are doing rate matching, then the - * link speed/duplex comes from the PHY - */ - if (pl->phy_state.rate_matching) { - link_state.rate_matching = - pl->phy_state.rate_matching; - link_state.speed = pl->phy_state.speed; - link_state.duplex = - pl->phy_state.duplex; - } - - /* If we have a PHY, we need to update with - * the PHY flow control bits. - */ - link_state.pause = pl->phy_state.pause; - mac_config = true; + if (pl->phy_state.rate_matching) { + link_state.rate_matching = + pl->phy_state.rate_matching; + link_state.speed = pl->phy_state.speed; + link_state.duplex = pl->phy_state.duplex; } - phylink_apply_manual_flow(pl, &link_state); - break; + + /* If we have a PHY, we need to update with + * the PHY flow control bits. + */ + link_state.pause = pl->phy_state.pause; + mac_config = true; } } + if (pl->act_link_an_mode != MLO_AN_FIXED) + phylink_apply_manual_flow(pl, &link_state); + if (mac_config) { if (link_state.interface != pl->link_config.interface) { /* The interface has changed, force the link down and @@ -1656,7 +1900,6 @@ struct phylink *phylink_create(struct phylink_config *config, phy_interface_t iface, const struct phylink_mac_ops *mac_ops) { - bool using_mac_select_pcs = false; struct phylink *pl; int ret; @@ -1667,11 +1910,6 @@ struct phylink *phylink_create(struct phylink_config *config, return ERR_PTR(-EINVAL); } - if (mac_ops->mac_select_pcs && - mac_ops->mac_select_pcs(config, PHY_INTERFACE_MODE_NA) != - ERR_PTR(-EOPNOTSUPP)) - using_mac_select_pcs = true; - pl = kzalloc(sizeof(*pl), GFP_KERNEL); if (!pl) return ERR_PTR(-ENOMEM); @@ -1690,7 +1928,6 @@ struct phylink *phylink_create(struct phylink_config *config, return ERR_PTR(-EINVAL); } - pl->using_mac_select_pcs = using_mac_select_pcs; pl->phy_state.interface = iface; pl->link_interface = iface; if (iface == PHY_INTERFACE_MODE_MOCA) @@ -1710,13 +1947,16 @@ struct phylink *phylink_create(struct phylink_config *config, linkmode_copy(pl->link_config.advertising, pl->supported); phylink_validate(pl, pl->supported, &pl->link_config); + /* Set the default EEE configuration */ + pl->eee_cfg = pl->config->eee; + ret = phylink_parse_mode(pl, fwnode); if (ret < 0) { kfree(pl); return ERR_PTR(ret); } - if (pl->cfg_link_an_mode == MLO_AN_FIXED) { + if (phylink_mode_fixed(pl->cfg_link_an_mode)) { ret = phylink_parse_fixedlink(pl, fwnode); if (ret < 0) { kfree(pl); @@ -1724,7 +1964,8 @@ struct phylink *phylink_create(struct phylink_config *config, } } - pl->cur_link_an_mode = pl->cfg_link_an_mode; + pl->req_link_an_mode = pl->cfg_link_an_mode; + pl->act_link_an_mode = pl->req_link_an_mode; ret = phylink_register_sfp(pl, fwnode); if (ret < 0) { @@ -1932,6 +2173,13 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy, /* Restrict the phy advertisement according to the MAC support. */ linkmode_copy(phy->advertising, config.advertising); + + /* If the MAC supports phylink managed EEE, restrict the EEE + * advertisement according to the MAC's LPI capabilities. + */ + if (phylink_mac_supports_eee(pl)) + phylink_phy_restrict_eee(pl, phy); + mutex_unlock(&pl->state_mutex); mutex_unlock(&phy->lock); @@ -1955,8 +2203,8 @@ static int phylink_attach_phy(struct phylink *pl, struct phy_device *phy, { u32 flags = 0; - if (WARN_ON(pl->cfg_link_an_mode == MLO_AN_FIXED || - (pl->cfg_link_an_mode == MLO_AN_INBAND && + if (WARN_ON(phylink_mode_fixed(pl->cfg_link_an_mode) || + (phylink_mode_inband(pl->cfg_link_an_mode) && phy_interface_mode_is_8023z(interface) && !pl->sfp_bus))) return -EINVAL; @@ -2045,14 +2293,14 @@ int phylink_fwnode_phy_connect(struct phylink *pl, int ret; /* Fixed links and 802.3z are handled without needing a PHY */ - if (pl->cfg_link_an_mode == MLO_AN_FIXED || - (pl->cfg_link_an_mode == MLO_AN_INBAND && + if (phylink_mode_fixed(pl->cfg_link_an_mode) || + (phylink_mode_inband(pl->cfg_link_an_mode) && phy_interface_mode_is_8023z(pl->link_interface))) return 0; phy_fwnode = fwnode_get_phy_node(fwnode); if (IS_ERR(phy_fwnode)) { - if (pl->cfg_link_an_mode == MLO_AN_PHY) + if (phylink_mode_phy(pl->cfg_link_an_mode)) return -ENODEV; return 0; } @@ -2179,7 +2427,7 @@ void phylink_start(struct phylink *pl) ASSERT_RTNL(); phylink_info(pl, "configuring for %s/%s link mode\n", - phylink_an_mode_str(pl->cur_link_an_mode), + phylink_an_mode_str(pl->req_link_an_mode), phy_modes(pl->link_config.interface)); /* Always set the carrier off */ @@ -2202,7 +2450,7 @@ void phylink_start(struct phylink *pl) phylink_enable_and_run_resolve(pl, PHYLINK_DISABLE_STOPPED); - if (pl->cfg_link_an_mode == MLO_AN_FIXED && pl->link_gpio) { + if (phylink_mode_fixed(pl->cfg_link_an_mode) && pl->link_gpio) { int irq = gpiod_to_irq(pl->link_gpio); if (irq > 0) { @@ -2390,6 +2638,32 @@ int phylink_ethtool_set_wol(struct phylink *pl, struct ethtool_wolinfo *wol) } EXPORT_SYMBOL_GPL(phylink_ethtool_set_wol); +static phy_interface_t phylink_sfp_select_interface(struct phylink *pl, + const unsigned long *link_modes) +{ + phy_interface_t interface; + + interface = sfp_select_interface(pl->sfp_bus, link_modes); + if (interface == PHY_INTERFACE_MODE_NA) { + phylink_err(pl, + "selection of interface failed, advertisement %*pb\n", + __ETHTOOL_LINK_MODE_MASK_NBITS, + link_modes); + return interface; + } + + if (!test_bit(interface, pl->config->supported_interfaces)) { + phylink_err(pl, + "selection of interface failed - SFP selected %s (%u) but MAC supports %*pbl\n", + phy_modes(interface), interface, + (int)PHY_INTERFACE_MODE_MAX, + pl->config->supported_interfaces); + return PHY_INTERFACE_MODE_NA; + } + + return interface; +} + static void phylink_merge_link_mode(unsigned long *dst, const unsigned long *b) { __ETHTOOL_DECLARE_LINK_MODE_MASK(mask); @@ -2438,7 +2712,7 @@ int phylink_ethtool_ksettings_get(struct phylink *pl, linkmode_copy(kset->link_modes.supported, pl->supported); - switch (pl->cur_link_an_mode) { + switch (pl->act_link_an_mode) { case MLO_AN_FIXED: /* We are using fixed settings. Report these as the * current link settings - and note that these also @@ -2469,6 +2743,26 @@ int phylink_ethtool_ksettings_get(struct phylink *pl, } EXPORT_SYMBOL_GPL(phylink_ethtool_ksettings_get); +static bool phylink_validate_pcs_inband_autoneg(struct phylink *pl, + phy_interface_t interface, + unsigned long *adv) +{ + unsigned int inband = phylink_query_inband(pl, interface); + unsigned int mask; + + /* If the PCS doesn't implement inband support, be permissive. */ + if (!inband) + return true; + + if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, adv)) + mask = LINK_INBAND_ENABLE; + else + mask = LINK_INBAND_DISABLE; + + /* Check whether the PCS implements the required mode */ + return !!(inband & mask); +} + /** * phylink_ethtool_ksettings_set() - set the link settings * @pl: a pointer to a &struct phylink returned from phylink_create() @@ -2530,7 +2824,7 @@ int phylink_ethtool_ksettings_set(struct phylink *pl, /* If we have a fixed link, refuse to change link parameters. * If the link parameters match, accept them but do nothing. */ - if (pl->cur_link_an_mode == MLO_AN_FIXED) { + if (phylink_mode_fixed(pl->req_link_an_mode)) { if (s->speed != pl->link_config.speed || s->duplex != pl->link_config.duplex) return -EINVAL; @@ -2546,7 +2840,7 @@ int phylink_ethtool_ksettings_set(struct phylink *pl, * is our default case) but do not allow the advertisement to * be changed. If the advertisement matches, simply return. */ - if (pl->cur_link_an_mode == MLO_AN_FIXED) { + if (phylink_mode_fixed(pl->req_link_an_mode)) { if (!linkmode_equal(config.advertising, pl->link_config.advertising)) return -EINVAL; @@ -2572,21 +2866,16 @@ int phylink_ethtool_ksettings_set(struct phylink *pl, * link can be configured correctly. */ if (pl->sfp_bus) { - config.interface = sfp_select_interface(pl->sfp_bus, + config.interface = phylink_sfp_select_interface(pl, config.advertising); - if (config.interface == PHY_INTERFACE_MODE_NA) { - phylink_err(pl, - "selection of interface failed, advertisement %*pb\n", - __ETHTOOL_LINK_MODE_MASK_NBITS, - config.advertising); + if (config.interface == PHY_INTERFACE_MODE_NA) return -EINVAL; - } /* Revalidate with the selected interface */ linkmode_copy(support, pl->supported); if (phylink_validate(pl, support, &config)) { phylink_err(pl, "validation of %s/%s with support %*pb failed\n", - phylink_an_mode_str(pl->cur_link_an_mode), + phylink_an_mode_str(pl->req_link_an_mode), phy_modes(config.interface), __ETHTOOL_LINK_MODE_MASK_NBITS, support); return -EINVAL; @@ -2604,6 +2893,13 @@ int phylink_ethtool_ksettings_set(struct phylink *pl, phylink_is_empty_linkmode(config.advertising)) return -EINVAL; + /* Validate the autonegotiation state. We don't have a PHY in this + * situation, so the PCS is the media-facing entity. + */ + if (!phylink_validate_pcs_inband_autoneg(pl, config.interface, + config.advertising)) + return -EINVAL; + mutex_lock(&pl->state_mutex); pl->link_config.speed = config.speed; pl->link_config.duplex = config.duplex; @@ -2686,7 +2982,7 @@ int phylink_ethtool_set_pauseparam(struct phylink *pl, ASSERT_RTNL(); - if (pl->cur_link_an_mode == MLO_AN_FIXED) + if (phylink_mode_fixed(pl->req_link_an_mode)) return -EOPNOTSUPP; if (!phylink_test(pl->supported, Pause) && @@ -2794,6 +3090,8 @@ int phylink_init_eee(struct phylink *pl, bool clk_stop_enable) if (pl->phydev) ret = phy_init_eee(pl->phydev, clk_stop_enable); + else if (pl->sfp_bus && phylink_mac_supports_eee(pl)) + ret = 0; return ret; } @@ -2810,8 +3108,19 @@ int phylink_ethtool_get_eee(struct phylink *pl, struct ethtool_keee *eee) ASSERT_RTNL(); - if (pl->phydev) + if (pl->phydev) { ret = phy_ethtool_get_eee(pl->phydev, eee); + } else if (pl->sfp_bus && phylink_mac_supports_eee(pl)) { + /* For optical SFPs, ensure that eee->supported is nonzero. */ + __set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, eee->supported); + ret = 0; + } + + if (!ret && phylink_mac_supports_eee(pl)) { + /* Overwrite phylib's interpretation of configuration */ + eeecfg_to_eee(eee, &pl->eee_cfg); + eee->eee_active = pl->eee_active; + } return ret; } @@ -2825,11 +3134,53 @@ EXPORT_SYMBOL_GPL(phylink_ethtool_get_eee); int phylink_ethtool_set_eee(struct phylink *pl, struct ethtool_keee *eee) { int ret = -EOPNOTSUPP; + bool mac_eee; ASSERT_RTNL(); + mac_eee = phylink_mac_supports_eee(pl); + + phylink_dbg(pl, "mac %s phylink EEE%s, adv 0x%*pbl, LPI%s timer %uus\n", + mac_eee ? "supports" : "does not support", + eee->eee_enabled ? ", enabled" : "", + __ETHTOOL_LINK_MODE_MASK_NBITS, eee->advertised, + eee->tx_lpi_enabled ? " enabled" : "", eee->tx_lpi_timer); + + /* Clamp the LPI timer maximum value */ + if (mac_eee && eee->tx_lpi_timer > pl->config->lpi_timer_limit_us) { + eee->tx_lpi_timer = pl->config->lpi_timer_limit_us; + phylink_dbg(pl, "LPI timer limited to %uus\n", + eee->tx_lpi_timer); + } + if (pl->phydev) ret = phy_ethtool_set_eee(pl->phydev, eee); + else if (pl->sfp_bus && phylink_mac_supports_eee(pl)) + ret = 0; + + if (!ret && mac_eee) { + bool can_lpi, old_can_lpi; + + mutex_lock(&pl->state_mutex); + old_can_lpi = eeecfg_mac_can_tx_lpi(&pl->eee_cfg); + eee_to_eeecfg(&pl->eee_cfg, eee); + can_lpi = eeecfg_mac_can_tx_lpi(&pl->eee_cfg); + + phylink_dbg(pl, "can_lpi %u -> %u\n", old_can_lpi, can_lpi); + + /* If the link is up, and the configuration changes the + * LPI permissive state, deal with the change at the MAC. + */ + if (phylink_link_is_up(pl) && old_can_lpi != can_lpi) { + phylink_dbg(pl, "link is up, lpi changed\n"); + if (can_lpi) + phylink_activate_eee(pl); + else + phylink_deactivate_eee(pl); + } + + mutex_unlock(&pl->state_mutex); + } return ret; } @@ -2950,7 +3301,7 @@ static int phylink_mii_read(struct phylink *pl, unsigned int phy_id, struct phylink_link_state state; int val = 0xffff; - switch (pl->cur_link_an_mode) { + switch (pl->act_link_an_mode) { case MLO_AN_FIXED: if (phy_id == 0) { phylink_get_fixed_state(pl, &state); @@ -2975,7 +3326,7 @@ static int phylink_mii_read(struct phylink *pl, unsigned int phy_id, static int phylink_mii_write(struct phylink *pl, unsigned int phy_id, unsigned int reg, unsigned int val) { - switch (pl->cur_link_an_mode) { + switch (pl->act_link_an_mode) { case MLO_AN_FIXED: break; @@ -3145,10 +3496,11 @@ static phy_interface_t phylink_choose_sfp_interface(struct phylink *pl, return interface; } -static void phylink_sfp_set_config(struct phylink *pl, u8 mode, +static void phylink_sfp_set_config(struct phylink *pl, unsigned long *supported, struct phylink_link_state *state) { + u8 mode = MLO_AN_INBAND; bool changed = false; phylink_dbg(pl, "requesting link mode %s/%s with support %*pb\n", @@ -3165,9 +3517,9 @@ static void phylink_sfp_set_config(struct phylink *pl, u8 mode, changed = true; } - if (pl->cur_link_an_mode != mode || + if (pl->req_link_an_mode != mode || pl->link_config.interface != state->interface) { - pl->cur_link_an_mode = mode; + pl->req_link_an_mode = mode; pl->link_config.interface = state->interface; changed = true; @@ -3182,13 +3534,10 @@ static void phylink_sfp_set_config(struct phylink *pl, u8 mode, phylink_mac_initial_config(pl, false); } -static int phylink_sfp_config_phy(struct phylink *pl, u8 mode, - struct phy_device *phy) +static int phylink_sfp_config_phy(struct phylink *pl, struct phy_device *phy) { - __ETHTOOL_DECLARE_LINK_MODE_MASK(support1); __ETHTOOL_DECLARE_LINK_MODE_MASK(support); struct phylink_link_state config; - phy_interface_t iface; int ret; linkmode_copy(support, phy->supported); @@ -3209,30 +3558,27 @@ static int phylink_sfp_config_phy(struct phylink *pl, u8 mode, return ret; } - iface = sfp_select_interface(pl->sfp_bus, config.advertising); - if (iface == PHY_INTERFACE_MODE_NA) { - phylink_err(pl, - "selection of interface failed, advertisement %*pb\n", - __ETHTOOL_LINK_MODE_MASK_NBITS, config.advertising); + config.interface = phylink_sfp_select_interface(pl, config.advertising); + if (config.interface == PHY_INTERFACE_MODE_NA) return -EINVAL; - } - config.interface = iface; - linkmode_copy(support1, support); - ret = phylink_validate(pl, support1, &config); - if (ret) { - phylink_err(pl, - "validation of %s/%s with support %*pb failed: %pe\n", - phylink_an_mode_str(mode), - phy_modes(config.interface), - __ETHTOOL_LINK_MODE_MASK_NBITS, support, - ERR_PTR(ret)); + /* Attach the PHY so that the PHY is present when we do the major + * configuration step. + */ + ret = phylink_attach_phy(pl, phy, config.interface); + if (ret < 0) + return ret; + + /* This will validate the configuration for us. */ + ret = phylink_bringup_phy(pl, phy, config.interface); + if (ret < 0) { + phy_detach(phy); return ret; } pl->link_port = pl->sfp_port; - phylink_sfp_set_config(pl, mode, support, &config); + phylink_sfp_set_config(pl, support, &config); return 0; } @@ -3288,6 +3634,12 @@ static int phylink_sfp_config_optical(struct phylink *pl) phylink_dbg(pl, "optical SFP: chosen %s interface\n", phy_modes(interface)); + if (!phylink_validate_pcs_inband_autoneg(pl, interface, + config.advertising)) { + phylink_err(pl, "autoneg setting not compatible with PCS"); + return -EINVAL; + } + config.interface = interface; /* Ignore errors if we're expecting a PHY to attach later */ @@ -3301,7 +3653,7 @@ static int phylink_sfp_config_optical(struct phylink *pl) pl->link_port = pl->sfp_port; - phylink_sfp_set_config(pl, MLO_AN_INBAND, pl->sfp_support, &config); + phylink_sfp_set_config(pl, pl->sfp_support, &config); return 0; } @@ -3372,20 +3724,11 @@ static void phylink_sfp_link_up(void *upstream) phylink_enable_and_run_resolve(pl, PHYLINK_DISABLE_LINK); } -/* The Broadcom BCM84881 in the Methode DM7052 is unable to provide a SGMII - * or 802.3z control word, so inband will not work. - */ -static bool phylink_phy_no_inband(struct phy_device *phy) -{ - return phy->is_c45 && phy_id_compare(phy->c45_ids.device_ids[1], - 0xae025150, 0xfffffff0); -} - static int phylink_sfp_connect_phy(void *upstream, struct phy_device *phy) { + DECLARE_PHY_INTERFACE_MASK(interfaces); struct phylink *pl = upstream; phy_interface_t interface; - u8 mode; int ret; /* @@ -3397,30 +3740,59 @@ static int phylink_sfp_connect_phy(void *upstream, struct phy_device *phy) */ phy_support_asym_pause(phy); - if (phylink_phy_no_inband(phy)) - mode = MLO_AN_PHY; - else - mode = MLO_AN_INBAND; - /* Set the PHY's host supported interfaces */ phy_interface_and(phy->host_interfaces, phylink_sfp_interfaces, pl->config->supported_interfaces); - /* Do the initial configuration */ - ret = phylink_sfp_config_phy(pl, mode, phy); - if (ret < 0) - return ret; + if (phy_interface_empty(phy->supported_interfaces)) { + phylink_dbg(pl, "copper SFP: PHY provides empty supported_interfaces\n"); + + /* Do the initial configuration */ + return phylink_sfp_config_phy(pl, phy); + } + + phylink_dbg(pl, "copper SFP: interfaces=[mac=%*pbl, sfp=%*pbl]\n", + (int)PHY_INTERFACE_MODE_MAX, + pl->config->supported_interfaces, + (int)PHY_INTERFACE_MODE_MAX, + phy->supported_interfaces); + + phy_interface_and(interfaces, phy->supported_interfaces, + pl->config->supported_interfaces); + interface = phylink_choose_sfp_interface(pl, interfaces); + if (interface == PHY_INTERFACE_MODE_NA) { + phylink_err(pl, + "selection of interface for PHY failed\n"); + return -EINVAL; + } + + phylink_dbg(pl, "copper SFP: chosen %s interface\n", + phy_modes(interface)); - interface = pl->link_config.interface; ret = phylink_attach_phy(pl, phy, interface); if (ret < 0) return ret; ret = phylink_bringup_phy(pl, phy, interface); - if (ret) + if (ret) { phy_detach(phy); + return ret; + } - return ret; + if (pl->req_link_an_mode != MLO_AN_INBAND || + pl->link_config.interface != interface) { + pl->link_config.interface = interface; + pl->req_link_an_mode = MLO_AN_INBAND; + + phylink_info(pl, "switched to %s/%s link mode\n", + phylink_an_mode_str(pl->req_link_an_mode), + phy_modes(pl->link_config.interface)); + } + + if (!test_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state)) + phylink_mac_initial_config(pl, false); + + return 0; } static void phylink_sfp_disconnect_phy(void *upstream) @@ -3539,12 +3911,18 @@ static void phylink_decode_sgmii_word(struct phylink_link_state *state, * @lpa: a 16 bit value which stores the USXGMII auto-negotiation word * * Helper for MAC PCS supporting the USXGMII protocol and the auto-negotiation - * code word. Decode the USXGMII code word and populate the corresponding fields - * (speed, duplex) into the phylink_link_state structure. + * code word. Decode the USXGMII code word and populate the corresponding fields + * (speed, duplex) into the phylink_link_state structure. If the code word + * indicates link is down, or we are unable to decode it, set the link down. */ void phylink_decode_usxgmii_word(struct phylink_link_state *state, uint16_t lpa) { + if (!(lpa & MDIO_USXGMII_LINK)) { + state->link = false; + return; + } + switch (lpa & MDIO_USXGMII_SPD_MASK) { case MDIO_USXGMII_10: state->speed = SPEED_10; diff --git a/drivers/net/phy/sff.c b/drivers/net/phy/sff.c new file mode 100644 index 000000000000..a2eb56118dd4 --- /dev/null +++ b/drivers/net/phy/sff.c @@ -0,0 +1,114 @@ +#include +#include +#include "sff.h" + +const char *sff_link_len(char *buf, size_t size, unsigned int length, + unsigned int multiplier) +{ + if (length == 0) + return "unsupported/unspecified"; + + if (length == 255) { + *buf++ = '>'; + size -= 1; + length -= 1; + } + + length *= multiplier; + + if (length >= 1000) + snprintf(buf, size, "%u.%0*ukm", + length / 1000, + multiplier > 100 ? 1 : + multiplier > 10 ? 2 : 3, + length % 1000); + else + snprintf(buf, size, "%um", length); + + return buf; +} +EXPORT_SYMBOL_GPL(sff_link_len); + +const char *sff_bitfield(char *buf, size_t size, + const struct sff_bitfield *bits, unsigned int val) +{ + char *p = buf; + int n; + + *p = '\0'; + while (bits->mask) { + if ((val & bits->mask) == bits->val) { + n = snprintf(p, size, "%s%s", + buf != p ? ", " : "", + bits->str); + if (n == size) + break; + p += n; + size -= n; + } + bits++; + } + + return buf; +} +EXPORT_SYMBOL_GPL(sff_bitfield); + +const char *sff_connector(unsigned int connector) +{ + switch (connector) { + case SFF8024_CONNECTOR_UNSPEC: + return "unknown/unspecified"; + case SFF8024_CONNECTOR_SC: + return "SC"; + case SFF8024_CONNECTOR_FIBERJACK: + return "Fiberjack"; + case SFF8024_CONNECTOR_LC: + return "LC"; + case SFF8024_CONNECTOR_MT_RJ: + return "MT-RJ"; + case SFF8024_CONNECTOR_MU: + return "MU"; + case SFF8024_CONNECTOR_SG: + return "SG"; + case SFF8024_CONNECTOR_OPTICAL_PIGTAIL: + return "Optical pigtail"; + case SFF8024_CONNECTOR_MPO_1X12: + return "MPO 1X12"; + case SFF8024_CONNECTOR_MPO_2X16: + return "MPO 2X16"; + case SFF8024_CONNECTOR_HSSDC_II: + return "HSSDC II"; + case SFF8024_CONNECTOR_COPPER_PIGTAIL: + return "Copper pigtail"; + case SFF8024_CONNECTOR_RJ45: + return "RJ45"; + case SFF8024_CONNECTOR_MXC_2X16: + return "MXC 2X16"; + default: + return "unknown"; + } +} +EXPORT_SYMBOL_GPL(sff_connector); + +const char *sff_encoding(unsigned int encoding) +{ + switch (encoding) { + case SFF8024_ENCODING_UNSPEC: + return "unspecified"; + case SFF8024_ENCODING_8472_64B66B: + return "64b66b"; + case SFF8024_ENCODING_8B10B: + return "8b10b"; + case SFF8024_ENCODING_4B5B: + return "4b5b"; + case SFF8024_ENCODING_NRZ: + return "NRZ"; + case SFF8024_ENCODING_8472_MANCHESTER: + return "MANCHESTER"; + default: + return "unknown"; + } +} +EXPORT_SYMBOL_GPL(sff_encoding); + +MODULE_LICENSE("GPL"); diff --git a/drivers/net/phy/sff.h b/drivers/net/phy/sff.h new file mode 100644 index 000000000000..cd7bb7c7ae4a --- /dev/null +++ b/drivers/net/phy/sff.h @@ -0,0 +1,16 @@ +#ifndef SFF_H +#define SFF_H + +struct sff_bitfield { + unsigned int mask; + unsigned int val; + const char *str; +}; + +const char *sff_link_len(char *buf, size_t size, unsigned int length, + unsigned int multiplier); +const char *sff_bitfield(char *buf, size_t size, + const struct sff_bitfield *bits, unsigned int val); +const char *sff_connector(unsigned int connector); +const char *sff_encoding(unsigned int encoding); +#endif diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index a5684ef5884b..8cc2672d0d77 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -16,6 +16,7 @@ #include #include +#include "sff.h" #include "sfp.h" #include "swphy.h" @@ -172,6 +173,7 @@ static const enum gpiod_flags gpio_flags[] = { #define T_WAIT msecs_to_jiffies(50) #define T_START_UP msecs_to_jiffies(300) #define T_START_UP_BAD_GPON msecs_to_jiffies(60000) +#define T_START_UP_COOLED msecs_to_jiffies(90000) /* t_reset is the time required to assert the TX_DISABLE signal to reset * an indicated TX_FAULT. @@ -337,6 +339,7 @@ static const struct sff_data sfp_data = { static const struct of_device_id sfp_of_match[] = { { .compatible = "sff,sff", .data = &sff_data, }, { .compatible = "sff,sfp", .data = &sfp_data, }, + { .compatible = "sff,sfp+", .data = &sfp_data, }, { }, }; MODULE_DEVICE_TABLE(of, sfp_of_match); @@ -1674,6 +1677,114 @@ static void sfp_hwmon_exit(struct sfp *sfp) } #endif +static const struct sff_bitfield sfp_options[] = { + { + .mask = SFP_OPTIONS_HIGH_POWER_LEVEL, + .val = SFP_OPTIONS_HIGH_POWER_LEVEL, + .str = "hpl", + }, { + .mask = SFP_OPTIONS_PAGING_A2, + .val = SFP_OPTIONS_PAGING_A2, + .str = "paginga2", + }, { + .mask = SFP_OPTIONS_RETIMER, + .val = SFP_OPTIONS_RETIMER, + .str = "retimer", + }, { + .mask = SFP_OPTIONS_COOLED_XCVR, + .val = SFP_OPTIONS_COOLED_XCVR, + .str = "cooled", + }, { + .mask = SFP_OPTIONS_POWER_DECL, + .val = SFP_OPTIONS_POWER_DECL, + .str = "powerdecl", + }, { + .mask = SFP_OPTIONS_RX_LINEAR_OUT, + .val = SFP_OPTIONS_RX_LINEAR_OUT, + .str = "rxlinear", + }, { + .mask = SFP_OPTIONS_RX_DECISION_THRESH, + .val = SFP_OPTIONS_RX_DECISION_THRESH, + .str = "rxthresh", + }, { + .mask = SFP_OPTIONS_TUNABLE_TX, + .val = SFP_OPTIONS_TUNABLE_TX, + .str = "tunabletx", + }, { + .mask = SFP_OPTIONS_RATE_SELECT, + .val = SFP_OPTIONS_RATE_SELECT, + .str = "ratesel", + }, { + .mask = SFP_OPTIONS_TX_DISABLE, + .val = SFP_OPTIONS_TX_DISABLE, + .str = "txdisable", + }, { + .mask = SFP_OPTIONS_TX_FAULT, + .val = SFP_OPTIONS_TX_FAULT, + .str = "txfault", + }, { + .mask = SFP_OPTIONS_LOS_INVERTED, + .val = SFP_OPTIONS_LOS_INVERTED, + .str = "los-", + }, { + .mask = SFP_OPTIONS_LOS_NORMAL, + .val = SFP_OPTIONS_LOS_NORMAL, + .str = "los+", + }, { } +}; + +static const struct sff_bitfield diagmon[] = { + { + .mask = SFP_DIAGMON_DDM, + .val = SFP_DIAGMON_DDM, + .str = "ddm", + }, { + .mask = SFP_DIAGMON_INT_CAL, + .val = SFP_DIAGMON_INT_CAL, + .str = "intcal", + }, { + .mask = SFP_DIAGMON_EXT_CAL, + .val = SFP_DIAGMON_EXT_CAL, + .str = "extcal", + }, { + .mask = SFP_DIAGMON_RXPWR_AVG, + .val = SFP_DIAGMON_RXPWR_AVG, + .str = "rxpwravg", + }, { } +}; + +static const struct sff_bitfield sfp_enhopts[] = { + { + .mask = SFP_ENHOPTS_ALARMWARN, + .val = SFP_ENHOPTS_ALARMWARN, + .str = "alarmwarn", + }, { + .mask = SFP_ENHOPTS_SOFT_TX_DISABLE, + .val = SFP_ENHOPTS_SOFT_TX_DISABLE, + .str = "soft_tx_dis", + }, { + .mask = SFP_ENHOPTS_SOFT_TX_FAULT, + .val = SFP_ENHOPTS_SOFT_TX_FAULT, + .str = "soft_tx_fault", + }, { + .mask = SFP_ENHOPTS_SOFT_RX_LOS, + .val = SFP_ENHOPTS_SOFT_RX_LOS, + .str = "soft_rx_los", + }, { + .mask = SFP_ENHOPTS_SOFT_RATE_SELECT, + .val = SFP_ENHOPTS_SOFT_RATE_SELECT, + .str = "soft_rs", + }, { + .mask = SFP_ENHOPTS_APP_SELECT_SFF8079, + .val = SFP_ENHOPTS_APP_SELECT_SFF8079, + .str = "app_sel", + }, { + .mask = SFP_ENHOPTS_SOFT_RATE_SFF8431, + .val = SFP_ENHOPTS_SOFT_RATE_SFF8431, + .str = "soft_r8431", + }, { } +}; + /* Helpers */ static void sfp_module_tx_disable(struct sfp *sfp) { @@ -2175,6 +2286,111 @@ static int sfp_cotsworks_fixup_check(struct sfp *sfp, struct sfp_eeprom_id *id) return 0; } +static void sfp_print_module_info(struct sfp *sfp, + const struct sfp_eeprom_id *id, bool cotsworks) +{ + unsigned int br_nom, br_min, br_max; + char date[9]; + char options[80]; + + /* Cotsworks also gets the date code wrong. */ + date[0] = id->ext.datecode[4 - 2 * cotsworks]; + date[1] = id->ext.datecode[5 - 2 * cotsworks]; + date[2] = '-'; + date[3] = id->ext.datecode[2 + 2 * cotsworks]; + date[4] = id->ext.datecode[3 + 2 * cotsworks]; + date[5] = '-'; + date[6] = id->ext.datecode[0]; + date[7] = id->ext.datecode[1]; + date[8] = '\0'; + + if (id->base.br_nominal == 0) { + br_min = br_nom = br_max = 0; + } else if (id->base.br_nominal == 255) { + br_nom = 250 * id->ext.br_max; + br_max = br_nom + br_nom * id->ext.br_min / 100; + br_min = br_nom - br_nom * id->ext.br_min / 100; + } else { + br_nom = id->base.br_nominal * 100; + br_min = br_nom - id->base.br_nominal * id->ext.br_min; + br_max = br_nom + id->base.br_nominal * id->ext.br_max; + } + + dev_info(sfp->dev, "module %.*s %.*s rev %.*s sn %.*s dc %s\n", + (int)sizeof(id->base.vendor_name), id->base.vendor_name, + (int)sizeof(id->base.vendor_pn), id->base.vendor_pn, + (int)sizeof(id->base.vendor_rev), id->base.vendor_rev, + (int)sizeof(id->ext.vendor_sn), id->ext.vendor_sn, date); + dev_info(sfp->dev, " %s connector, encoding %s, bitrate %u.%03u (%u.%03u-%u.%03u) Gbps\n", + sff_connector(id->base.connector), + sff_encoding(id->base.encoding), + br_nom / 1000, br_nom % 1000, + br_min / 1000, br_min % 1000, br_max / 1000, br_max % 1000); + dev_info(sfp->dev, " 1000BaseSX%c 1000BaseLX%c 1000BaseCX%c 1000BaseT%c 100BaseLX%c 100BaseFX%c BaseBX10%c BasePX%c\n", + id->base.e1000_base_sx ? '+' : '-', + id->base.e1000_base_lx ? '+' : '-', + id->base.e1000_base_cx ? '+' : '-', + id->base.e1000_base_t ? '+' : '-', + id->base.e100_base_lx ? '+' : '-', + id->base.e100_base_fx ? '+' : '-', + id->base.e_base_bx10 ? '+' : '-', + id->base.e_base_px ? '+' : '-'); + dev_info(sfp->dev, " 10GBaseSR%c 10GBaseLR%c 10GBaseLRM%c 10GBaseER%c\n", + id->base.e10g_base_sr ? '+' : '-', + id->base.e10g_base_lr ? '+' : '-', + id->base.e10g_base_lrm ? '+' : '-', + id->base.e10g_base_er ? '+' : '-'); + + if (!id->base.sfp_ct_passive && !id->base.sfp_ct_active && + !id->base.e1000_base_t) { + char len_9um[16], len_om[16]; + + dev_info(sfp->dev, " Wavelength %unm, fiber lengths:\n", + be16_to_cpup(&id->base.optical_wavelength)); + + if (id->base.link_len[0] == 255) + strcpy(len_9um, ">254km"); + else if (id->base.link_len[1] && id->base.link_len[1] != 255) + sprintf(len_9um, "%um", + id->base.link_len[1] * 100); + else if (id->base.link_len[0]) + sprintf(len_9um, "%ukm", id->base.link_len[0]); + else if (id->base.link_len[1] == 255) + strcpy(len_9um, ">25.4km"); + else + strcpy(len_9um, "unsupported"); + + dev_info(sfp->dev, " 9µm SM : %s\n", len_9um); + dev_info(sfp->dev, " 62.5µm MM OM1: %s\n", + sff_link_len(len_om, sizeof(len_om), + id->base.link_len[3], 10)); + dev_info(sfp->dev, " 50µm MM OM2: %s\n", + sff_link_len(len_om, sizeof(len_om), + id->base.link_len[2], 10)); + dev_info(sfp->dev, " 50µm MM OM3: %s\n", + sff_link_len(len_om, sizeof(len_om), + id->base.link_len[5], 10)); + dev_info(sfp->dev, " 50µm MM OM4: %s\n", + sff_link_len(len_om, sizeof(len_om), + id->base.link_len[4], 10)); + } else { + char len[16]; + dev_info(sfp->dev, " Copper length: %s\n", + sff_link_len(len, sizeof(len), + id->base.link_len[4], 1)); + } + + dev_info(sfp->dev, " Options: %s\n", + sff_bitfield(options, sizeof(options), sfp_options, + be16_to_cpu(id->ext.options))); + dev_info(sfp->dev, " Diagnostics: %s\n", + sff_bitfield(options, sizeof(options), diagmon, + id->ext.diagmon)); + dev_info(sfp->dev, " EnhOpts: %s\n", + sff_bitfield(options, sizeof(options), sfp_enhopts, + id->ext.enhopts)); +} + static int sfp_module_parse_sff8472(struct sfp *sfp) { /* If the module requires address swap mode, warn about it */ @@ -2241,9 +2457,9 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report) } } - /* Cotsworks do not seem to update the checksums when they - * do the final programming with the final module part number, - * serial number and date code. + /* Cotsworks do not seem to update the checksums when they update the + * module part number, serial number and date code. They also format + * the date code incorrectly. */ cotsworks = !memcmp(id.base.vendor_name, "COTSWORKS ", 16); cotsworks_sfbg = !memcmp(id.base.vendor_pn, "SFBG", 4); @@ -2304,14 +2520,9 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report) } } - sfp->id = id; + sfp_print_module_info(sfp, &id, cotsworks); - dev_info(sfp->dev, "module %.*s %.*s rev %.*s sn %.*s dc %.*s\n", - (int)sizeof(id.base.vendor_name), id.base.vendor_name, - (int)sizeof(id.base.vendor_pn), id.base.vendor_pn, - (int)sizeof(id.base.vendor_rev), id.base.vendor_rev, - (int)sizeof(id.ext.vendor_sn), id.ext.vendor_sn, - (int)sizeof(id.ext.datecode), id.ext.datecode); + sfp->id = id; /* Check whether we support this module */ if (!sfp->type->module_supported(&id)) { @@ -2345,8 +2556,11 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report) mask |= SFP_F_RS0; if (sfp->gpio[GPIO_RS1]) mask |= SFP_F_RS1; + if (id.ext.options & cpu_to_be16(SFP_OPTIONS_COOLED_XCVR)) + sfp->module_t_start_up = T_START_UP_COOLED; + else + sfp->module_t_start_up = T_START_UP; - sfp->module_t_start_up = T_START_UP; sfp->module_t_wait = T_WAIT; sfp->phy_t_retry = T_PHY_RETRY; @@ -2595,10 +2809,10 @@ static void sfp_sm_main(struct sfp *sfp, unsigned int event) break; if (sfp->state & SFP_F_TX_FAULT) { - /* Wait up to t_init (SFF-8472) or t_start_up (SFF-8431) - * from the TX_DISABLE deassertion for the module to - * initialise, which is indicated by TX_FAULT - * deasserting. + /* Wait up to t_init (SFF-8472), t_start_up (SFF-8431), + * or t_start_up_cooled (SFF-8431) from the TX_DISABLE + * deassertion for the module to initialise, which is + * indicated by TX_FAULT deasserting. */ timeout = sfp->module_t_start_up; if (timeout > sfp->module_t_wait) @@ -2617,8 +2831,8 @@ static void sfp_sm_main(struct sfp *sfp, unsigned int event) case SFP_S_INIT: if (event == SFP_E_TIMEOUT && sfp->state & SFP_F_TX_FAULT) { - /* TX_FAULT is still asserted after t_init - * or t_start_up, so assume there is a fault. + /* TX_FAULT is still asserted after t_init, t_start_up + * or t_start_up_cooled, so assume there is a fault. */ sfp_sm_fault(sfp, SFP_S_INIT_TX_FAULT, sfp->sm_fault_retries == N_FAULT_INIT); diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c index 29fe09c99e7d..309956dff9a6 100644 --- a/drivers/pci/controller/pci-mvebu.c +++ b/drivers/pci/controller/pci-mvebu.c @@ -60,6 +60,12 @@ #define PCIE_INT_INTX(i) BIT(24+i) #define PCIE_INT_PM_PME BIT(28) #define PCIE_INT_ALL_MASK GENMASK(31, 0) +#define PCIE_MASK_ERR_COR BIT(18) +#define PCIE_MASK_ERR_NONFATAL BIT(17) +#define PCIE_MASK_ERR_FATAL BIT(16) +#define PCIE_MASK_FERR_DET BIT(10) +#define PCIE_MASK_NFERR_DET BIT(9) +#define PCIE_MASK_CORERR_DET BIT(8) #define PCIE_CTRL_OFF 0x1a00 #define PCIE_CTRL_X1_MODE 0x0001 #define PCIE_CTRL_RC_MODE BIT(1) @@ -73,6 +79,7 @@ #define PCIE_SSPL_VALUE_MASK GENMASK(7, 0) #define PCIE_SSPL_SCALE_SHIFT 8 #define PCIE_SSPL_SCALE_MASK GENMASK(9, 8) +#define PCIE_SSPL_MSGEN BIT(14) #define PCIE_SSPL_ENABLE BIT(16) #define PCIE_RC_RTSTA 0x1a14 #define PCIE_DEBUG_CTRL 0x1a60 @@ -618,6 +625,54 @@ mvebu_pci_bridge_emul_base_conf_read(struct pci_bridge_emul *bridge, return PCI_BRIDGE_EMUL_HANDLED; } +static void mvebu_pcie_handle_irq_change(struct mvebu_pcie_port *port) +{ + u32 reg, old; + u16 devctl, rtctl; + + /* + * Errors from downstream devices: + * bridge control register SERR: enables reception of errors + * Errors from this device, or received errors: + * command SERR: enables ERR_NONFATAL and ERR_FATAL messages + * => when enabled, these conditions also flag SERR in status register + * devctl CERE: enables ERR_CORR messages + * devctl NFERE: enables ERR_NONFATAL messages + * devctl FERE: enables ERR_FATAL messages + * Enabled messages then have three paths: + * 1. rtctl: enables system error indication + * 2. root error status register updated + * 3. root error command register: forwarding via MSI + */ + old = mvebu_readl(port, PCIE_INT_UNMASK_OFF); + reg = old & ~(PCIE_INT_PM_PME | PCIE_MASK_FERR_DET | + PCIE_MASK_NFERR_DET | PCIE_MASK_CORERR_DET | + PCIE_MASK_ERR_COR | PCIE_MASK_ERR_NONFATAL | + PCIE_MASK_ERR_FATAL); + + devctl = port->bridge.pcie_conf.devctl; + if (devctl & PCI_EXP_DEVCTL_FERE) + reg |= PCIE_MASK_FERR_DET | PCIE_MASK_ERR_FATAL; + if (devctl & PCI_EXP_DEVCTL_NFERE) + reg |= PCIE_MASK_NFERR_DET | PCIE_MASK_ERR_NONFATAL; + if (devctl & PCI_EXP_DEVCTL_CERE) + reg |= PCIE_MASK_CORERR_DET | PCIE_MASK_ERR_COR; + if (port->bridge.conf.command & PCI_COMMAND_SERR) + reg |= PCIE_MASK_FERR_DET | PCIE_MASK_NFERR_DET | + PCIE_MASK_ERR_FATAL | PCIE_MASK_ERR_NONFATAL; + + if (!(port->bridge.conf.bridgectrl & PCI_BRIDGE_CTL_SERR)) + reg &= ~(PCIE_MASK_ERR_COR | PCIE_MASK_ERR_NONFATAL | + PCIE_MASK_ERR_FATAL); + + rtctl = port->bridge.pcie_conf.rootctl; + if (rtctl & PCI_EXP_RTCTL_PMEIE) + reg |= PCIE_INT_PM_PME; + + if (old != reg) + mvebu_writel(port, reg, PCIE_INT_UNMASK_OFF); +} + static pci_bridge_emul_read_status_t mvebu_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge, int reg, u32 *value) @@ -651,6 +706,14 @@ mvebu_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge, (PCI_EXP_LNKSTA_DLLLA << 16) : 0); break; + case PCI_EXP_SLTCAP: + { + u32 tmp = mvebu_readl(port, PCIE_SSPL_OFF); + *value = FIELD_GET(PCIE_SSPL_SCALE_MASK, tmp) << 15 | + FIELD_GET(PCIE_SSPL_VALUE_MASK, tmp) << 7; + break; + } + case PCI_EXP_SLTCTL: { u16 slotctl = le16_to_cpu(bridge->pcie_conf.slotctl); u16 slotsta = le16_to_cpu(bridge->pcie_conf.slotsta); @@ -734,6 +797,9 @@ mvebu_pci_bridge_emul_base_conf_write(struct pci_bridge_emul *bridge, switch (reg) { case PCI_COMMAND: mvebu_writel(port, new, PCIE_CMD_OFF); + + if ((old ^ new) & PCI_COMMAND_SERR) + mvebu_pcie_handle_irq_change(port); break; case PCI_IO_BASE: @@ -775,6 +841,8 @@ mvebu_pci_bridge_emul_base_conf_write(struct pci_bridge_emul *bridge, break; case PCI_INTERRUPT_LINE: + if (((old ^ new) >> 16) & PCI_BRIDGE_CTL_SERR) + mvebu_pcie_handle_irq_change(port); if (mask & (PCI_BRIDGE_CTL_BUS_RESET << 16)) { u32 ctrl = mvebu_readl(port, PCIE_CTRL_OFF); if (new & (PCI_BRIDGE_CTL_BUS_RESET << 16)) @@ -798,7 +866,18 @@ mvebu_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge, switch (reg) { case PCI_EXP_DEVCTL: + /* + * Armada370 data says these bits must always + * be zero when in root complex mode. + */ + new &= ~(PCI_EXP_DEVCTL_URRE | PCI_EXP_DEVCTL_FERE | + PCI_EXP_DEVCTL_NFERE | PCI_EXP_DEVCTL_CERE); + mvebu_writel(port, new, PCIE_CAP_PCIEXP + PCI_EXP_DEVCTL); + + if ((new ^ old) & (PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_NFERE | + PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_URRE)) + mvebu_pcie_handle_irq_change(port); break; case PCI_EXP_LNKCTL: @@ -812,6 +891,17 @@ mvebu_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge, mvebu_writel(port, new, PCIE_CAP_PCIEXP + PCI_EXP_LNKCTL); break; + case PCI_EXP_SLTCAP: + { + u32 sspl = FIELD_PREP(PCIE_SSPL_VALUE_MASK, + FIELD_GET(PCI_EXP_SLTCAP_SPLV, new)) | + FIELD_PREP(PCIE_SSPL_SCALE_MASK, + FIELD_GET(PCI_EXP_SLTCAP_SPLS, new)) | + PCIE_SSPL_MSGEN; + mvebu_writel(port, sspl, PCIE_SSPL_OFF); + break; + } + case PCI_EXP_SLTCTL: /* * Allow to change PCIE_SSPL_ENABLE bit only when slot power @@ -849,6 +939,12 @@ mvebu_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge, default: break; + + case PCI_EXP_RTCTL: + if ((new ^ old) & (PCI_EXP_RTCTL_SECEE | PCI_EXP_RTCTL_SENFEE | + PCI_EXP_RTCTL_SEFEE | PCI_EXP_RTCTL_PMEIE)) + mvebu_pcie_handle_irq_change(port); + break; } } diff --git a/drivers/pci/pci-bridge-emul.c b/drivers/pci/pci-bridge-emul.c index 9334b2dd4764..003511971108 100644 --- a/drivers/pci/pci-bridge-emul.c +++ b/drivers/pci/pci-bridge-emul.c @@ -157,6 +157,7 @@ struct pci_bridge_reg_behavior pci_regs_behavior[PCI_STD_HEADER_SIZEOF / 4] = { .rw = (GENMASK(7, 0) | ((PCI_BRIDGE_CTL_PARITY | PCI_BRIDGE_CTL_SERR | + /* NOTE: PCIe does not allow ISA, VGA, MASTER_ABORT */ PCI_BRIDGE_CTL_ISA | PCI_BRIDGE_CTL_VGA | PCI_BRIDGE_CTL_MASTER_ABORT | @@ -355,6 +356,7 @@ int pci_bridge_emul_init(struct pci_bridge_emul *bridge, bridge->conf.header_type = PCI_HEADER_TYPE_BRIDGE; bridge->conf.cache_line_size = 0x10; bridge->conf.status = cpu_to_le16(PCI_STATUS_CAP_LIST); + bridge->conf.bridgectrl = cpu_to_le16(PCI_BRIDGE_CTL_SERR); bridge->pci_regs_behavior = kmemdup(pci_regs_behavior, sizeof(pci_regs_behavior), GFP_KERNEL); @@ -477,8 +479,11 @@ int pci_bridge_emul_conf_read(struct pci_bridge_emul *bridge, int where, read_op = pci_bridge_emul_read_ssid; cfgspace = NULL; behavior = NULL; - } else if (reg >= bridge->pcie_start && reg < bridge->pcie_start + PCI_CAP_PCIE_SIZEOF && - bridge->has_pcie) { + } else if (!bridge->has_pcie) { + /* PCIe space is not implemented, and no PCI capabilities */ + *value = 0; + return PCIBIOS_SUCCESSFUL; + } else if (reg >= bridge->pcie_start && reg < bridge->pcie_start + PCI_CAP_PCIE_SIZEOF) { /* Our emulated PCIe capability */ reg -= bridge->pcie_start; read_op = bridge->ops->read_pcie; @@ -551,14 +556,16 @@ int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where, write_op = bridge->ops->write_base; cfgspace = (__le32 *) &bridge->conf; behavior = bridge->pci_regs_behavior; - } else if (reg >= bridge->pcie_start && reg < bridge->pcie_start + PCI_CAP_PCIE_SIZEOF && - bridge->has_pcie) { + } else if (!bridge->has_pcie) { + /* PCIe space is not implemented, and no PCI capabilities */ + return PCIBIOS_SUCCESSFUL; + } else if (reg >= bridge->pcie_start && reg < bridge->pcie_start + PCI_CAP_PCIE_SIZEOF) { /* Our emulated PCIe capability */ reg -= bridge->pcie_start; write_op = bridge->ops->write_pcie; cfgspace = (__le32 *) &bridge->pcie_conf; behavior = bridge->pcie_cap_regs_behavior; - } else if (reg >= PCI_CFG_SPACE_SIZE && bridge->has_pcie) { + } else if (reg >= PCI_CFG_SPACE_SIZE) { /* PCIe extended capability space */ reg -= PCI_CFG_SPACE_SIZE; write_op = bridge->ops->write_ext; diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index cee2365e54b8..f80a23afa482 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -804,6 +804,12 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist) pcie_capability_read_dword(child, PCI_EXP_LNKCAP, &child_lnkcap); pcie_capability_read_word(parent, PCI_EXP_LNKCTL, &parent_lnkctl); pcie_capability_read_word(child, PCI_EXP_LNKCTL, &child_lnkctl); +dev_info(&parent->dev, "up support %x enabled %x\n", + (parent_lnkcap & PCI_EXP_LNKCAP_ASPMS) >> 10, + !!(parent_lnkctl & PCI_EXP_LNKCTL_ASPMC)); +dev_info(&parent->dev, "dn support %x enabled %x\n", + (child_lnkcap & PCI_EXP_LNKCAP_ASPMS) >> 10, + !!(child_lnkctl & PCI_EXP_LNKCTL_ASPMC)); /* * Setup L0s state diff --git a/drivers/pci/pcie/portdrv.c b/drivers/pci/pcie/portdrv.c index 6af5e0425872..4e5b2c415ea7 100644 --- a/drivers/pci/pcie/portdrv.c +++ b/drivers/pci/pcie/portdrv.c @@ -336,6 +336,7 @@ static int pcie_port_device_register(struct pci_dev *dev) /* Get and check PCI Express port services */ capabilities = get_port_device_capability(dev); +dev_info(&dev->dev, "PCIe capabilities: 0x%x\n", capabilities); if (!capabilities) return 0; @@ -348,6 +349,7 @@ static int pcie_port_device_register(struct pci_dev *dev) * if that is to be used. */ status = pcie_init_service_irqs(dev, irqs, capabilities); +dev_info(&dev->dev, "init_service_irqs: %d\n", status); if (status) { capabilities &= PCIE_PORT_SERVICE_HP; if (!capabilities) diff --git a/drivers/phy/marvell/phy-armada38x-comphy.c b/drivers/phy/marvell/phy-armada38x-comphy.c index 5063361b0120..a4533c924edd 100644 --- a/drivers/phy/marvell/phy-armada38x-comphy.c +++ b/drivers/phy/marvell/phy-armada38x-comphy.c @@ -16,32 +16,49 @@ #define MAX_A38X_COMPHY 6 #define MAX_A38X_PORTS 3 +/* Common PHY registers */ +#define COMPHY_REG_SIZE 0x28 #define COMPHY_CFG1 0x00 +#define COMPHY_CFG1_RX_INIT BIT(30) #define COMPHY_CFG1_GEN_TX(x) ((x) << 26) #define COMPHY_CFG1_GEN_TX_MSK COMPHY_CFG1_GEN_TX(15) #define COMPHY_CFG1_GEN_RX(x) ((x) << 22) #define COMPHY_CFG1_GEN_RX_MSK COMPHY_CFG1_GEN_RX(15) +#define COMPHY_CFG1_POWER_UP_TX BIT(18) +#define COMPHY_CFG1_POWER_UP_RX BIT(17) +#define COMPHY_CFG1_POWER_UP_PLL BIT(16) #define GEN_SGMII_1_25GBPS 6 #define GEN_SGMII_3_125GBPS 8 #define COMPHY_STAT1 0x18 #define COMPHY_STAT1_PLL_RDY_TX BIT(3) #define COMPHY_STAT1_PLL_RDY_RX BIT(2) +#define COMPHY_STAT1_RX_INIT_DONE BIT(0) #define COMPHY_SELECTOR 0xfc +/* Common PHY and Pipe Registers */ +#define PIPE_REG_SIZE 0x800 +#define PIPE_POWER_CTRL 0x148 +#define PIPE_POWER_CTRL_SFT_RST_NO_REG BIT(10) +/* u-boot sets bit 0 during comphy initialisation, but it is undocumented */ +#define PIPE_POWER_CTRL_BIT0 BIT(0) + struct a38x_comphy; struct a38x_comphy_lane { void __iomem *base; + void __iomem *pipe; struct a38x_comphy *priv; unsigned int n; int port; + u8 gen; }; struct a38x_comphy { void __iomem *base; + void __iomem *pipe; void __iomem *conf; struct device *dev; struct a38x_comphy_lane lane[MAX_A38X_COMPHY]; @@ -94,6 +111,7 @@ static void a38x_comphy_set_speed(struct a38x_comphy_lane *lane, COMPHY_CFG1_GEN_RX(gen_rx)); } +/* Poll every 1ms for 150ms for a status */ static int a38x_comphy_poll(struct a38x_comphy_lane *lane, unsigned int offset, u32 mask, u32 value) { @@ -111,6 +129,97 @@ static int a38x_comphy_poll(struct a38x_comphy_lane *lane, return ret; } +static int a38x_comphy_power_up(struct a38x_comphy_lane *lane) +{ + /* Power up TX, RX and PLL */ + a38x_comphy_set_reg(lane, COMPHY_CFG1, 0, + COMPHY_CFG1_POWER_UP_TX | COMPHY_CFG1_POWER_UP_RX | + COMPHY_CFG1_POWER_UP_PLL); + + /* Wait for power up */ + return a38x_comphy_poll(lane, COMPHY_STAT1, + COMPHY_STAT1_PLL_RDY_TX | + COMPHY_STAT1_PLL_RDY_RX, + COMPHY_STAT1_PLL_RDY_TX | + COMPHY_STAT1_PLL_RDY_RX); +} + +static int a38x_comphy_rx_init(struct a38x_comphy_lane *lane) +{ + int ret; + + /* Perform RX init */ + a38x_comphy_set_reg(lane, COMPHY_CFG1, 0, COMPHY_CFG1_RX_INIT); + + /* Wait for RX init done */ + ret = a38x_comphy_poll(lane, COMPHY_STAT1, COMPHY_STAT1_RX_INIT_DONE, + COMPHY_STAT1_RX_INIT_DONE); + + /* Clear RX init */ + a38x_comphy_set_reg(lane, COMPHY_CFG1, COMPHY_CFG1_RX_INIT, 0); + + return ret; +} + +static int a38x_comphy_power_off(struct phy *phy) +{ + struct a38x_comphy_lane *lane = phy_get_drvdata(phy); + + a38x_set_conf(lane, false); + + /* Soft reset */ + if (lane->pipe) { + u32 rst = PIPE_POWER_CTRL_SFT_RST_NO_REG | PIPE_POWER_CTRL_BIT0; + u32 val; + + val = readl_relaxed(lane->pipe + PIPE_POWER_CTRL); + writel(val | rst, lane->pipe + PIPE_POWER_CTRL); + } + + a38x_comphy_set_reg(lane, COMPHY_CFG1, + COMPHY_CFG1_POWER_UP_TX | COMPHY_CFG1_POWER_UP_RX | + COMPHY_CFG1_POWER_UP_PLL, 0); + + return 0; +} + +static int a38x_comphy_power_on(struct phy *phy) +{ + struct a38x_comphy_lane *lane = phy_get_drvdata(phy); + int ret; + + /* Program the GEN settings */ + a38x_comphy_set_speed(lane, lane->gen, lane->gen); + + /* Soft reset */ + if (lane->pipe) { + u32 rst = PIPE_POWER_CTRL_SFT_RST_NO_REG | PIPE_POWER_CTRL_BIT0; + u32 val; + + val = readl_relaxed(lane->pipe + PIPE_POWER_CTRL); + writel(val | rst, lane->pipe + PIPE_POWER_CTRL); + writel(val & ~rst, lane->pipe + PIPE_POWER_CTRL); + } + + /* Power up TX, RX and PLL and wait */ + ret = a38x_comphy_power_up(lane); + if (ret) + goto fail; + + /* Perform RX init */ + ret = a38x_comphy_rx_init(lane); + if (ret) + goto fail; + + a38x_set_conf(lane, true); + + return 0; + +fail: + a38x_comphy_power_off(phy); + return ret; +} + /* * We only support changing the speed for comphys configured for GBE. * Since that is all we do, we only poll for PLL ready status. @@ -119,7 +228,6 @@ static int a38x_comphy_set_mode(struct phy *phy, enum phy_mode mode, int sub) { struct a38x_comphy_lane *lane = phy_get_drvdata(phy); unsigned int gen; - int ret; if (mode != PHY_MODE_ETHERNET) return -EINVAL; @@ -138,23 +246,14 @@ static int a38x_comphy_set_mode(struct phy *phy, enum phy_mode mode, int sub) return -EINVAL; } - a38x_set_conf(lane, false); - - a38x_comphy_set_speed(lane, gen, gen); - - ret = a38x_comphy_poll(lane, COMPHY_STAT1, - COMPHY_STAT1_PLL_RDY_TX | - COMPHY_STAT1_PLL_RDY_RX, - COMPHY_STAT1_PLL_RDY_TX | - COMPHY_STAT1_PLL_RDY_RX); - - if (ret == 0) - a38x_set_conf(lane, true); + lane->gen = gen; - return ret; + return 0; } static const struct phy_ops a38x_comphy_ops = { + .power_on = a38x_comphy_power_on, + .power_off = a38x_comphy_power_off, .set_mode = a38x_comphy_set_mode, .owner = THIS_MODULE, }; @@ -199,6 +298,7 @@ static int a38x_comphy_probe(struct platform_device *pdev) struct a38x_comphy *priv; struct resource *res; void __iomem *base; + void __iomem *pipe = NULL; priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -208,8 +308,16 @@ static int a38x_comphy_probe(struct platform_device *pdev) if (IS_ERR(base)) return PTR_ERR(base); + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pipe"); + if (res) { + pipe = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + } + priv->dev = &pdev->dev; priv->base = base; + priv->pipe = pipe; /* Optional */ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "conf"); @@ -236,16 +344,19 @@ static int a38x_comphy_probe(struct platform_device *pdev) continue; } + priv->lane[val].base = base + COMPHY_REG_SIZE * val; + if (pipe) + priv->lane[val].pipe = pipe + PIPE_REG_SIZE * val; + priv->lane[val].priv = priv; + priv->lane[val].n = val; + priv->lane[val].port = -1; + phy = devm_phy_create(&pdev->dev, child, &a38x_comphy_ops); if (IS_ERR(phy)) { of_node_put(child); return PTR_ERR(phy); } - priv->lane[val].base = base + 0x28 * val; - priv->lane[val].priv = priv; - priv->lane[val].n = val; - priv->lane[val].port = -1; phy_set_drvdata(phy, &priv->lane[val]); } diff --git a/include/linux/mdio.h b/include/linux/mdio.h index efeca5bd7600..e298cc4c8804 100644 --- a/include/linux/mdio.h +++ b/include/linux/mdio.h @@ -32,6 +32,8 @@ struct mdio_device { char modalias[MDIO_NAME_SIZE]; int (*bus_match)(struct device *dev, const struct device_driver *drv); + int (*bus_uevent)(struct mdio_device *mdiodev, + struct kobj_uevent_env *env); void (*device_free)(struct mdio_device *mdiodev); void (*device_remove)(struct mdio_device *mdiodev); diff --git a/include/linux/pcs/pcs-xpcs.h b/include/linux/pcs/pcs-xpcs.h index b4a4eb6c8866..b5b5d17998b8 100644 --- a/include/linux/pcs/pcs-xpcs.h +++ b/include/linux/pcs/pcs-xpcs.h @@ -21,8 +21,6 @@ #define DW_AN_C37_1000BASEX 4 #define DW_10GBASER 5 -struct dw_xpcs_desc; - enum dw_xpcs_pcs_id { DW_XPCS_ID_NATIVE = 0, NXP_SJA1105_XPCS_ID = 0x00000010, @@ -48,33 +46,18 @@ struct dw_xpcs_info { u32 pma; }; -enum dw_xpcs_clock { - DW_XPCS_CORE_CLK, - DW_XPCS_PAD_CLK, - DW_XPCS_NUM_CLKS, -}; - -struct dw_xpcs { - struct dw_xpcs_info info; - const struct dw_xpcs_desc *desc; - struct mdio_device *mdiodev; - struct clk_bulk_data clks[DW_XPCS_NUM_CLKS]; - struct phylink_pcs pcs; - phy_interface_t interface; -}; +struct dw_xpcs; +struct phylink_pcs *xpcs_to_phylink_pcs(struct dw_xpcs *xpcs); int xpcs_get_an_mode(struct dw_xpcs *xpcs, phy_interface_t interface); -void xpcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode, - phy_interface_t interface, int speed, int duplex); -int xpcs_do_config(struct dw_xpcs *xpcs, phy_interface_t interface, - const unsigned long *advertising, unsigned int neg_mode); void xpcs_get_interfaces(struct dw_xpcs *xpcs, unsigned long *interfaces); int xpcs_config_eee(struct dw_xpcs *xpcs, int mult_fact_100ns, int enable); -struct dw_xpcs *xpcs_create_mdiodev(struct mii_bus *bus, int addr, - phy_interface_t interface); -struct dw_xpcs *xpcs_create_fwnode(struct fwnode_handle *fwnode, - phy_interface_t interface); +struct dw_xpcs *xpcs_create_mdiodev(struct mii_bus *bus, int addr); +struct dw_xpcs *xpcs_create_fwnode(struct fwnode_handle *fwnode); void xpcs_destroy(struct dw_xpcs *xpcs); +struct phylink_pcs *xpcs_create_pcs_mdiodev(struct mii_bus *bus, int addr); +void xpcs_destroy_pcs(struct phylink_pcs *pcs); + #endif /* __LINUX_PCS_XPCS_H */ diff --git a/include/linux/phy.h b/include/linux/phy.h index 6b7d40d49129..e07deab4d3ad 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -707,6 +707,15 @@ struct phy_device { u8 master_slave_set; u8 master_slave_state; + /* + * private to phylib: the resolved pause state - only valid if + * resolved_pause_valid is true. only phy drivers and phylib + * should touch this. + */ + bool resolved_pause_valid; + bool resolved_tx_pause; + bool resolved_rx_pause; + /* Union of PHY and Attached devices' supported link modes */ /* See ethtool.h for more info */ __ETHTOOL_DECLARE_LINK_MODE_MASK(supported); @@ -722,6 +731,9 @@ struct phy_device { /* Host supported PHY interface types. Should be ignored if empty. */ DECLARE_PHY_INTERFACE_MASK(host_interfaces); + /* supported PHY interface types */ + DECLARE_PHY_INTERFACE_MASK(supported_interfaces); + /* Energy efficient ethernet modes which should be prohibited */ u32 eee_broken_modes; bool enable_tx_lpi; @@ -814,6 +826,24 @@ struct phy_tdr_config { }; #define PHY_PAIR_ALL -1 +/** + * enum link_inband_signalling - inband signalling modes that are supported + * + * @LINK_INBAND_DISABLE: inband signalling can be disabled + * @LINK_INBAND_ENABLE: inband signalling can be enabled + * @LINK_INBAND_BYPASS: only valid with LINK_INBAND_ENABLE and supports bypass + * + * The possible and required bits can only be used if the valid bit is set. + * If possible is clear, that means inband signalling can not be used. + * Required is only valid when possible is set, and means that inband + * signalling must be used. + */ +enum link_inband_signalling { + LINK_INBAND_DISABLE = BIT(0), + LINK_INBAND_ENABLE = BIT(1), + LINK_INBAND_BYPASS = BIT(2), +}; + /** * struct phy_plca_cfg - Configuration of the PLCA (Physical Layer Collision * Avoidance) Reconciliation Sublayer. @@ -952,6 +982,14 @@ struct phy_driver { */ int (*get_features)(struct phy_device *phydev); + /** + * @query_inband: query whether inband is supported for the given PHY + * interface mode. Returns a bitmask of bits defined by enum + * link_inband_signalling. + */ + unsigned int (*query_inband)(struct phy_device *phydev, + phy_interface_t interface); + /** * @get_rate_matching: Get the supported type of rate matching for a * particular phy interface. This is used by phy consumers to determine @@ -970,6 +1008,9 @@ struct phy_driver { /** @resume: Resume the hardware, restoring state if needed */ int (*resume)(struct phy_device *phydev); + int (*start)(struct phy_device *phydev); + void (*stop)(struct phy_device *phydev); + /** * @config_aneg: Configures the advertisement and resets * autonegotiation if phydev->autoneg is on, @@ -1800,6 +1841,8 @@ int phy_config_aneg(struct phy_device *phydev); int _phy_start_aneg(struct phy_device *phydev); int phy_start_aneg(struct phy_device *phydev); int phy_aneg_done(struct phy_device *phydev); +unsigned int phy_query_inband(struct phy_device *phydev, + phy_interface_t interface); int phy_speed_down(struct phy_device *phydev, bool sync); int phy_speed_up(struct phy_device *phydev); bool phy_check_valid(int speed, int duplex, unsigned long *features); diff --git a/include/linux/phylink.h b/include/linux/phylink.h index 2381e07429a2..a2d10259a134 100644 --- a/include/linux/phylink.h +++ b/include/linux/phylink.h @@ -5,6 +5,8 @@ #include #include +#include + struct device_node; struct ethtool_cmd; struct fwnode_handle; @@ -93,11 +95,26 @@ enum { MAC_400000FD = BIT(18), }; -static inline bool phylink_autoneg_inband(unsigned int mode) +static inline bool phylink_mode_inband(unsigned int mode) { return mode == MLO_AN_INBAND; } +static inline bool phylink_mode_fixed(unsigned int mode) +{ + return mode == MLO_AN_FIXED; +} + +static inline bool phylink_mode_phy(unsigned int mode) +{ + return mode == MLO_AN_PHY; +} + +static inline bool phylink_autoneg_inband(unsigned int mode) +{ + return phylink_mode_inband(mode); +} + /** * struct phylink_link_state - link state structure * @advertising: ethtool bitmask containing advertised link modes @@ -143,11 +160,15 @@ enum phylink_op_type { * possible and avoid stopping it during suspend events. * @default_an_inband: if true, defaults to MLO_AN_INBAND rather than * MLO_AN_PHY. A fixed-link specification will override. + * @eee_clk_stop_enable: if true, PHY can stop the receive clock during LPI * @get_fixed_state: callback to execute to determine the fixed link state, * if MAC link is at %MLO_AN_FIXED mode. * @supported_interfaces: bitmap describing which PHY_INTERFACE_MODE_xxx * are supported by the MAC/PCS. * @mac_capabilities: MAC pause/speed/duplex capabilities. + * @lpi_capabilities: MAC speeds which can support LPI signalling + * @eee: default EEE configuration. + * @lpi_timer_limit_us: Maximum (inclusive) value of the EEE LPI timer. */ struct phylink_config { struct device *dev; @@ -156,10 +177,14 @@ struct phylink_config { bool mac_managed_pm; bool mac_requires_rxc; bool default_an_inband; + bool eee_clk_stop_enable; void (*get_fixed_state)(struct phylink_config *config, struct phylink_link_state *state); DECLARE_PHY_INTERFACE_MASK(supported_interfaces); unsigned long mac_capabilities; + unsigned long lpi_capabilities; + struct eee_config eee; + u32 lpi_timer_limit_us; }; void phylink_limit_mac_speed(struct phylink_config *config, u32 max_speed); @@ -173,6 +198,8 @@ void phylink_limit_mac_speed(struct phylink_config *config, u32 max_speed); * @mac_finish: finish a major reconfiguration of the interface. * @mac_link_down: take the link down. * @mac_link_up: allow the link to come up. + * @mac_disable_tx_lpi: disable LPI. + * @mac_enable_tx_lpi: enable and configure LPI. * * The individual methods are described more fully below. */ @@ -193,6 +220,8 @@ struct phylink_mac_ops { struct phy_device *phy, unsigned int mode, phy_interface_t interface, int speed, int duplex, bool tx_pause, bool rx_pause); + void (*mac_disable_tx_lpi)(struct phylink_config *config); + void (*mac_enable_tx_lpi)(struct phylink_config *config, u32 timer); }; #if 0 /* For kernel-doc purposes only. */ @@ -387,6 +416,28 @@ void mac_link_down(struct phylink_config *config, unsigned int mode, void mac_link_up(struct phylink_config *config, struct phy_device *phy, unsigned int mode, phy_interface_t interface, int speed, int duplex, bool tx_pause, bool rx_pause); + +/** + * mac_disable_tx_lpi() - disable LPI generation at the MAC + * @config: a pointer to a &struct phylink_config. + * + * Disable generation of LPI at the MAC, effectively preventing the MAC + * from indicating that it is idle. + */ +void mac_disable_tx_lpi(struct phylink_config *config); + +/** + * mac_enable_tx_lpi() - configure and enable LPI generation at the MAC + * @config: a pointer to a &struct phylink_config. + * @timer: LPI timeout in microseconds. + * + * Configure the LPI timeout accordingly. This will only be called when + * the link is already up, to cater for situations where the hardware + * needs to be programmed according to the link speed. + * + * Enable LPI generation at the MAC. + */ +void mac_enable_tx_lpi(struct phylink_config *config, u32 timer); #endif struct phylink_pcs_ops; @@ -419,6 +470,7 @@ struct phylink_pcs { /** * struct phylink_pcs_ops - MAC PCS operations structure. * @pcs_validate: validate the link configuration. + * @pcs_query_inband: query inband support for interface mode. * @pcs_enable: enable the PCS. * @pcs_disable: disable the PCS. * @pcs_pre_config: pre-mac_config method (for errata) @@ -434,6 +486,8 @@ struct phylink_pcs { struct phylink_pcs_ops { int (*pcs_validate)(struct phylink_pcs *pcs, unsigned long *supported, const struct phylink_link_state *state); + unsigned int (*pcs_query_inband)(struct phylink_pcs *pcs, + phy_interface_t interface); int (*pcs_enable)(struct phylink_pcs *pcs); void (*pcs_disable)(struct phylink_pcs *pcs); void (*pcs_pre_config)(struct phylink_pcs *pcs, @@ -470,6 +524,20 @@ struct phylink_pcs_ops { int pcs_validate(struct phylink_pcs *pcs, unsigned long *supported, const struct phylink_link_state *state); +/** + * pcs_query_inband - query inband support for interface mode. + * @pcs: a pointer to a &struct phylink_pcs. + * @interface: interface mode to be queried + * + * Returns zero if it is unknown what in-band signalling is supported by the + * PHY (e.g. because the PHY driver doesn't implement the method.) Otherwise, + * returns a bit mask of the LINK_INBAND_* values from + * &enum link_inband_signalling to describe which inband modes are supported + * for this interface mode. + */ +unsigned int pcs_query_inband(struct phylink_pcs *pcs, + phy_interface_t interface); + /** * pcs_enable() - enable the PCS. * @pcs: a pointer to a &struct phylink_pcs. diff --git a/include/net/dsa.h b/include/net/dsa.h index d7a6c2930277..72ae65e7246a 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -885,21 +885,6 @@ struct dsa_switch_ops { */ void (*phylink_get_caps)(struct dsa_switch *ds, int port, struct phylink_config *config); - struct phylink_pcs *(*phylink_mac_select_pcs)(struct dsa_switch *ds, - int port, - phy_interface_t iface); - void (*phylink_mac_config)(struct dsa_switch *ds, int port, - unsigned int mode, - const struct phylink_link_state *state); - void (*phylink_mac_link_down)(struct dsa_switch *ds, int port, - unsigned int mode, - phy_interface_t interface); - void (*phylink_mac_link_up)(struct dsa_switch *ds, int port, - unsigned int mode, - phy_interface_t interface, - struct phy_device *phydev, - int speed, int duplex, - bool tx_pause, bool rx_pause); void (*phylink_fixed_state)(struct dsa_switch *ds, int port, struct phylink_link_state *state); /* diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index 668c729946ea..ceeadb52d1cc 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -1505,12 +1505,9 @@ static int dsa_switch_probe(struct dsa_switch *ds) if (!ds->num_ports) return -EINVAL; - if (ds->phylink_mac_ops) { - if (ds->ops->phylink_mac_select_pcs || - ds->ops->phylink_mac_config || - ds->ops->phylink_mac_link_down || - ds->ops->phylink_mac_link_up) - return -EINVAL; + if (!ds->phylink_mac_ops) { + dev_err(ds->dev, "DSA switch driver does not provide phylink MAC operations"); + return -EINVAL; } if (np) { diff --git a/net/dsa/port.c b/net/dsa/port.c index 25258b33e59e..2fc432797fbd 100644 --- a/net/dsa/port.c +++ b/net/dsa/port.c @@ -1575,73 +1575,8 @@ void dsa_port_set_tag_protocol(struct dsa_port *cpu_dp, cpu_dp->tag_ops = tag_ops; } -static struct phylink_pcs * -dsa_port_phylink_mac_select_pcs(struct phylink_config *config, - phy_interface_t interface) -{ - struct dsa_port *dp = dsa_phylink_to_port(config); - struct phylink_pcs *pcs = ERR_PTR(-EOPNOTSUPP); - struct dsa_switch *ds = dp->ds; - - if (ds->ops->phylink_mac_select_pcs) - pcs = ds->ops->phylink_mac_select_pcs(ds, dp->index, interface); - - return pcs; -} - -static void dsa_port_phylink_mac_config(struct phylink_config *config, - unsigned int mode, - const struct phylink_link_state *state) -{ - struct dsa_port *dp = dsa_phylink_to_port(config); - struct dsa_switch *ds = dp->ds; - - if (!ds->ops->phylink_mac_config) - return; - - ds->ops->phylink_mac_config(ds, dp->index, mode, state); -} - -static void dsa_port_phylink_mac_link_down(struct phylink_config *config, - unsigned int mode, - phy_interface_t interface) -{ - struct dsa_port *dp = dsa_phylink_to_port(config); - struct dsa_switch *ds = dp->ds; - - if (!ds->ops->phylink_mac_link_down) - return; - - ds->ops->phylink_mac_link_down(ds, dp->index, mode, interface); -} - -static void dsa_port_phylink_mac_link_up(struct phylink_config *config, - struct phy_device *phydev, - unsigned int mode, - phy_interface_t interface, - int speed, int duplex, - bool tx_pause, bool rx_pause) -{ - struct dsa_port *dp = dsa_phylink_to_port(config); - struct dsa_switch *ds = dp->ds; - - if (!ds->ops->phylink_mac_link_up) - return; - - ds->ops->phylink_mac_link_up(ds, dp->index, mode, interface, phydev, - speed, duplex, tx_pause, rx_pause); -} - -static const struct phylink_mac_ops dsa_port_phylink_mac_ops = { - .mac_select_pcs = dsa_port_phylink_mac_select_pcs, - .mac_config = dsa_port_phylink_mac_config, - .mac_link_down = dsa_port_phylink_mac_link_down, - .mac_link_up = dsa_port_phylink_mac_link_up, -}; - int dsa_port_phylink_create(struct dsa_port *dp) { - const struct phylink_mac_ops *mac_ops; struct dsa_switch *ds = dp->ds; phy_interface_t mode; struct phylink *pl; @@ -1665,12 +1600,8 @@ int dsa_port_phylink_create(struct dsa_port *dp) } } - mac_ops = &dsa_port_phylink_mac_ops; - if (ds->phylink_mac_ops) - mac_ops = ds->phylink_mac_ops; - pl = phylink_create(&dp->pl_config, of_fwnode_handle(dp->dn), mode, - mac_ops); + ds->phylink_mac_ops); if (IS_ERR(pl)) { pr_err("error creating PHYLINK: %ld\n", PTR_ERR(pl)); return PTR_ERR(pl); @@ -1871,9 +1802,6 @@ static void dsa_shared_port_link_down(struct dsa_port *dp) if (ds->phylink_mac_ops && ds->phylink_mac_ops->mac_link_down) ds->phylink_mac_ops->mac_link_down(&dp->pl_config, MLO_AN_FIXED, PHY_INTERFACE_MODE_NA); - else if (ds->ops->phylink_mac_link_down) - ds->ops->phylink_mac_link_down(ds, dp->index, MLO_AN_FIXED, - PHY_INTERFACE_MODE_NA); } int dsa_shared_port_link_register_of(struct dsa_port *dp) diff --git a/net/dsa/user.c b/net/dsa/user.c index f5adfa1d978a..f0e54631f6ab 100644 --- a/net/dsa/user.c +++ b/net/dsa/user.c @@ -1231,16 +1231,21 @@ static int dsa_user_set_eee(struct net_device *dev, struct ethtool_keee *e) struct dsa_switch *ds = dp->ds; int ret; - /* Port's PHY and MAC both need to be EEE capable */ - if (!dev->phydev || !dp->pl) - return -ENODEV; + /* If the port is using phylink managed EEE, then get_mac_eee is + * unnecessary. + */ + if (!dp->pl_config.lpi_capabilities) { + /* Port's PHY and MAC both need to be EEE capable */ + if (!dev->phydev || !dp->pl) + return -ENODEV; - if (!ds->ops->set_mac_eee) - return -EOPNOTSUPP; + if (!ds->ops->set_mac_eee) + return -EOPNOTSUPP; - ret = ds->ops->set_mac_eee(ds, dp->index, e); - if (ret) - return ret; + ret = ds->ops->set_mac_eee(ds, dp->index, e); + if (ret) + return ret; + } return phylink_ethtool_set_eee(dp->pl, e); } @@ -1251,16 +1256,21 @@ static int dsa_user_get_eee(struct net_device *dev, struct ethtool_keee *e) struct dsa_switch *ds = dp->ds; int ret; - /* Port's PHY and MAC both need to be EEE capable */ - if (!dev->phydev || !dp->pl) - return -ENODEV; + /* If the port is using phylink managed EEE, then get_mac_eee is + * unnecessary. + */ + if (!dp->pl_config.lpi_capabilities) { + /* Port's PHY and MAC both need to be EEE capable */ + if (!dev->phydev || !dp->pl) + return -ENODEV; - if (!ds->ops->get_mac_eee) - return -EOPNOTSUPP; + if (!ds->ops->get_mac_eee) + return -EOPNOTSUPP; - ret = ds->ops->get_mac_eee(ds, dp->index, e); - if (ret) - return ret; + ret = ds->ops->get_mac_eee(ds, dp->index, e); + if (ret) + return ret; + } return phylink_ethtool_get_eee(dp->pl, e); }