# Base git commit: 7111951b8d49 # (Linux 5.6) # # Author: Russell King (Fri 24 Jan 11:03:16 GMT 2020) # Committer: Russell King (Mon 30 Mar 21:38:46 BST 2020) # # net: phylink: add separate pcs operations structure # # Add a separate set of PCS operations, which MAC drivers can use to # couple phylink with their associated MAC PCS layer. The PCS # operations include: # # - pcs_get_state() - reads the link up/down, resolved speed, duplex # and pause from the PCS. # - pcs_config() - configures the PCS for the specified mode, PHY # interface type, and setting the advertisement. # - pcs_an_restart() - restarts 802.3 in-band negotiation with the # link partner # - pcs_link_up() - informs the PCS that link has come up, and the # parameters of the link. Link parameters are used to program the # PCS for fixed speed and non-inband modes. # # Reviewed-by: Andrew Lunn # Reviewed-by: Florian Fainelli # Signed-off-by: Russell King # # d87628ccc12938efc2e6c7702fa82fcfec540268 # drivers/net/phy/phylink.c | 77 +++++++++++++++++++++++++++++------------ # include/linux/phylink.h | 88 +++++++++++++++++++++++++++++++++++++++++++++++ # 2 files changed, 143 insertions(+), 22 deletions(-) # # Author: Russell King (Fri 24 Jan 10:46:03 GMT 2020) # Committer: Russell King (Mon 30 Mar 21:38:44 BST 2020) # # net: phylink: rename 'ops' to 'mac_ops' # # Rename the bland 'ops' member of struct phylink to be a more # descriptive 'mac_ops' - this is necessary as we're about to introduce # another set of operations. # # Reviewed-by: Andrew Lunn # Reviewed-by: Florian Fainelli # Signed-off-by: Russell King # # 3191180969bf40551728b95fa62769baadc94128 # drivers/net/phy/phylink.c | 30 +++++++++++++++--------------- # include/linux/phylink.h | 2 +- # 2 files changed, 16 insertions(+), 16 deletions(-) # # Author: Russell King (Sun 29 Mar 15:58:07 BST 2020) # Committer: Russell King (Mon 30 Mar 21:38:43 BST 2020) # # net: phylink: change phylink_mii_c22_pcs_set_advertisement() prototype # # Change phylink_mii_c22_pcs_set_advertisement() to take only the PHY # interface and advertisement mask, rather than the full phylink state. # # Reviewed-by: Andrew Lunn # Reviewed-by: Florian Fainelli # Signed-off-by: Russell King # # b345990f77d6b9f7b5cb4e55448fa1feca95717c # drivers/net/phy/phylink.c | 12 +++++++----- # include/linux/phylink.h | 3 ++- # 2 files changed, 9 insertions(+), 6 deletions(-) # # Author: Russell King (Fri 31 Jan 18:17:58 GMT 2020) # Committer: Russell King (Mon 30 Mar 21:38:41 BST 2020) # # net: phylink: pcs: add 802.3 clause 45 helpers # # Implement helpers for PCS accessed via the MII bus using 802.3 clause # 45 cycles for 10GBASE-R. Only link up/down is supported, 10G full # duplex is assumed. # # Signed-off-by: Russell King # # 5a4e341931aa140bc558293f49b2d1e235698419 # drivers/net/phy/phylink.c | 30 ++++++++++++++++++++++++++++++ # include/linux/phylink.h | 2 ++ # 2 files changed, 32 insertions(+) # # Author: Russell King (Fri 17 Jan 11:10:02 GMT 2020) # Committer: Russell King (Mon 30 Mar 21:38:40 BST 2020) # # net: phylink: pcs: add 802.3 clause 22 helpers # # Implement helpers for PCS accessed via the MII bus using 802.3 clause # 22 cycles, conforming to 802.3 clause 37 and Cisco SGMII specifications # for the advertisement word. # # Signed-off-by: Russell King # # b85eff0fc835200b1aaaa96d04bf5012c6133026 # drivers/net/phy/phylink.c | 205 ++++++++++++++++++++++++++++++++++++++++++++++ # include/linux/phylink.h | 6 ++ # 2 files changed, 211 insertions(+) # # Author: Russell King (Tue 4 Feb 15:47:27 GMT 2020) # Committer: Russell King (Mon 30 Mar 21:38:38 BST 2020) # # net: mdiobus: add APIs for modifying a MDIO device register # # Add APIs for modifying a MDIO device register, similar to the existing # phy_modify() group of functions, but at mdiobus level instead. Adapt # __phy_modify_changed() to use the new mdiobus level helper. # # Signed-off-by: Russell King # # d83767dc02e232995e54ac6cb23a68a9d657e9e3 # drivers/net/phy/mdio_bus.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++ # drivers/net/phy/phy-core.c | 31 -------------------------- # include/linux/mdio.h | 4 ++++ # include/linux/phy.h | 19 ++++++++++++++++ # 4 files changed, 78 insertions(+), 31 deletions(-) # # Author: Russell King (Fri 14 Feb 11:53:06 GMT 2020) # Committer: Russell King (Mon 30 Mar 21:38:36 BST 2020) # # net: dsa: mv88e6xxx: use PHY_DETECT in mac_link_up/mac_link_down # # Use the status of the PHY_DETECT bit to determine whether we need to # force the MAC settings in mac_link_up() and mac_link_down(). # # Signed-off-by: Russell King # # e6340abb7516cecbe5b2314170f3eacc42c521bc # drivers/net/dsa/mv88e6xxx/chip.c | 53 ++++++++++++++++++++++------------------ # 1 file changed, 29 insertions(+), 24 deletions(-) # # Author: Russell King (Tue 11 Feb 11:35:30 GMT 2020) # Committer: Russell King (Mon 30 Mar 21:38:35 BST 2020) # # net: dsa: mv88e6xxx: remove port_link_state functions # # The port_link_state method is only used by mv88e6xxx_port_setup_mac(), # which is now only called during port setup, rather than also being # called via phylink's mac_config method. # # Remove this now unnecessary optimisation, which allows us to remove the # port_link_state methods as well. # # Signed-off-by: Russell King # # fde5bfef94bf6f6ee38257169d252c049b9216d8 # drivers/net/dsa/mv88e6xxx/chip.c | 46 ---------- # drivers/net/dsa/mv88e6xxx/chip.h | 3 - # drivers/net/dsa/mv88e6xxx/port.c | 177 --------------------------------------- # drivers/net/dsa/mv88e6xxx/port.h | 6 -- # 4 files changed, 232 deletions(-) # # Author: Russell King (Tue 11 Feb 12:27:54 GMT 2020) # Committer: Russell King (Mon 30 Mar 21:38:34 BST 2020) # # net: dsa: mv88e6xxx: combine port_set_speed and port_set_duplex # # Setting the speed independently of duplex makes little sense; the two # parameters result from negotiation or fixed setup, and may have inter- # dependencies. Moreover, they are always controlled via the same # register - having them split means we have to read-modify-write this # register twice. # # Combine the two operations into a single port_set_speed_duplex() # operation. Not only is this more efficient, it reduces the size of the # code as well. # # Signed-off-by: Russell King # # 0d2904090e2b4200c919e675b718e2450552bbec # drivers/net/dsa/mv88e6xxx/chip.c | 103 ++++++++++++------------------------- # drivers/net/dsa/mv88e6xxx/chip.h | 18 +++---- # drivers/net/dsa/mv88e6xxx/port.c | 108 +++++++++++++++++++-------------------- # drivers/net/dsa/mv88e6xxx/port.h | 23 +++++---- # 4 files changed, 107 insertions(+), 145 deletions(-) # # Author: Russell King (Sun 9 Feb 14:12:05 GMT 2020) # Committer: Russell King (Mon 30 Mar 21:38:32 BST 2020) # # net: dsa: mv88e6xxx: fix Serdes link changes # # phylink_mac_change() is supposed to be called with a 'false' argument # if the link has gone down since it was last reported up; this is to # ensure that link events along with renegotiation events are always # correctly reported to userspace. # # Read the BMSR once when we have an interrupt, and report the link # latched status to phylink via phylink_mac_change(). phylink will deal # automatically with re-reading the link state once it has processed the # link-down event. # # Signed-off-by: Russell King # # dcc98ce7365cadcf14bbe8b4fa0c2540fdaa8d1e # drivers/net/dsa/mv88e6xxx/serdes.c | 33 ++++++++++++--------------------- # drivers/net/dsa/mv88e6xxx/serdes.h | 1 + # 2 files changed, 13 insertions(+), 21 deletions(-) # # Author: Russell King (Fri 7 Feb 10:54:56 GMT 2020) # Committer: Russell King (Mon 30 Mar 21:38:31 BST 2020) # # net: dsa: mv88e6xxx: extend phylink to Serdes PHYs # # Extend the mv88e6xxx phylink implementation down to Serdes PHYs, which # handle the PCS layer of such links. # # - Implement phylink PCS link state reading, so that we can provide # ethtool with the linkmodes and link speed in the expected manner. # Note: this will only be called for in-band negotiation, which is # only supported by the serdes interfaces. # - Implement phylink PCS configuration, so that the in-band AN and # advertisement can be configured. # - Implement phylink PCS negotiation restart, so that the in-band AN # can be restarted. # - Implement phylink PCS link up, so that when operating out-of-band, # the Serdes can be configured for the appropriate fixed speed mode. # # Signed-off-by: Russell King # # 246a39877ab1d866d45e9adb86e05e13d211a8fb # drivers/net/dsa/mv88e6xxx/chip.c | 174 ++++++++++++++++--- # drivers/net/dsa/mv88e6xxx/chip.h | 14 +- # drivers/net/dsa/mv88e6xxx/serdes.c | 342 +++++++++++++++++++++++++++++++------ # drivers/net/dsa/mv88e6xxx/serdes.h | 24 +++ # 4 files changed, 480 insertions(+), 74 deletions(-) # # Author: Russell King (Fri 7 Feb 10:54:20 GMT 2020) # Committer: Russell King (Mon 30 Mar 21:38:29 BST 2020) # # net: dsa: mv88e6xxx: configure interface settings in mac_config # # Only configure the interface settings in mac_config(), leaving the # speed and duplex settings to mac_link_up to deal with. # # Signed-off-by: Russell King # # 2291e342db6c770a747161a917ce1684031e2b01 # drivers/net/dsa/mv88e6xxx/chip.c | 67 +++++++++++++++++++++------------------- # 1 file changed, 35 insertions(+), 32 deletions(-) # # Author: Russell King (Sat 8 Feb 15:45:38 GMT 2020) # Committer: Russell King (Mon 30 Mar 21:38:28 BST 2020) # # net: dsa: mv88e6xxx: use BMCR definitions for serdes control register # # The SGMII/1000base-X serdes register set is a clause 22 register set # offset at 0x2000 in the PHYXS device. Rather than inventing our own # defintions, use those that already exist, and name the register # MV88E6390_SGMII_BMCR. Also remove the unused MV88E6390_SGMII_STATUS # definitions. # # Signed-off-by: Russell King # # 4adebdcefef98dfbd579fc0cd2afb4e6e1a5f65a # drivers/net/dsa/mv88e6xxx/serdes.c | 10 ++++------ # drivers/net/dsa/mv88e6xxx/serdes.h | 9 +-------- # 2 files changed, 5 insertions(+), 14 deletions(-) # # Author: Russell King (Mon 10 Feb 14:54:10 GMT 2020) # Committer: Russell King (Mon 30 Mar 21:38:26 BST 2020) # # net: dsa: warn if phylink_mac_link_state returns error # # Issue a warning to the kernel log if phylink_mac_link_state() returns # an error. This should not occur, but let's make it visible. # # Signed-off-by: Russell King # # eb5ed6ddeeaac338f0faabac04260009d0d80864 # net/dsa/port.c | 7 ++++++- # 1 file changed, 6 insertions(+), 1 deletion(-) # # Author: Russell King (Sat 8 Feb 22:07:53 GMT 2020) # Committer: Russell King (Mon 30 Mar 21:38:25 BST 2020) # # net: mii: add linkmode_adv_to_mii_adv_x() # # Add a helper to convert a linkmode advertisement to a clause 37 # advertisement value for 1000base-x and 2500base-x. # # Signed-off-by: Russell King # # 12946cf608c567b3f405e1e790da1963d0d4ee67 # include/linux/mii.h | 20 ++++++++++++++++++++ # 1 file changed, 20 insertions(+) # # Author: Russell King (Thu 6 Feb 23:12:47 GMT 2020) # Committer: Russell King (Mon 30 Mar 21:38:23 BST 2020) # # net: mii: convert mii_lpa_to_ethtool_lpa_x() to linkmode variant # # Add a LPA to linkmode decoder for 1000BASE-X protocols; this decoder # only provides the modify semantics similar to other such decoders. # This replaces the unused mii_lpa_to_ethtool_lpa_x() helper. # # Signed-off-by: Russell King # # 56ace7bc15b6327f599b17873bddb8ff2138baa9 # include/linux/mii.h | 37 +++++++++++++++++++------------------ # 1 file changed, 19 insertions(+), 18 deletions(-) # # Author: Jose Abreu (Sun 15 Mar 16:04:33 GMT 2020) # Committer: Russell King (Mon 30 Mar 21:38:22 BST 2020) # # net: phylink: Add XLGMII support # # Add XLGMII interface and the list of XLGMII speeds to PHYLINK. # # Signed-off-by: Jose Abreu # Signed-off-by: David S. Miller # # b71d751e6bad559fcf14ec3ebc96df2ab63e8fb8 # drivers/net/phy/phylink.c | 27 +++++++++++++++++++++++++++ # 1 file changed, 27 insertions(+) # # Author: Jose Abreu (Sun 15 Mar 16:04:32 GMT 2020) # Committer: Russell King (Mon 30 Mar 21:38:20 BST 2020) # # net: phy: Add XLGMII interface define # # Add a define for XLGMII interface. # # Signed-off-by: Jose Abreu # Signed-off-by: David S. Miller # # a93d074a4c1daa238e119be27202ea5552df40c5 # include/linux/phy.h | 3 +++ # 1 file changed, 3 insertions(+) # # Author: Vladimir Oltean (Sat 29 Feb 21:06:55 GMT 2020) # Committer: Russell King (Mon 30 Mar 21:38:18 BST 2020) # # net: dsa: sja1105: move MAC configuration to .phylink_mac_link_up # # The switches supported so far by the driver only have non-SerDes ports, # so they should be configured in the PHYLINK callback that provides the # resolved PHY link parameters. # # Signed-off-by: Vladimir Oltean # Signed-off-by: Russell King # # f8116160a1575f78908d629124425a564caa1115 # drivers/net/dsa/sja1105/sja1105_main.c | 13 +++++++++---- # 1 file changed, 9 insertions(+), 4 deletions(-) # # Author: Russell King (Sat 30 Nov 14:52:08 GMT 2019) # Committer: Russell King (Mon 30 Mar 21:38:17 BST 2020) # # net: phy: marvell10g: place in powersave mode at probe # # Place the 88x3310 into powersaving mode when probing, which saves 600mW # per PHY. For both PHYs on the Macchiatobin double-shot, this saves # about 10% of the board idle power. # # Reviewed-by: Andrew Lunn # Signed-off-by: Russell King # # 49e22bcc0a93a0569d9b7012b2fc41945034fcf0 # drivers/net/phy/marvell10g.c | 30 ++++++++++++++++++++++++++---- # 1 file changed, 26 insertions(+), 4 deletions(-) # # Author: Russell King (Fri 29 Nov 22:45:50 GMT 2019) # Committer: Russell King (Mon 30 Mar 21:38:16 BST 2020) # # net: phy: marvell10g: add energy detect power down tunable # # Add support for the energy detect power down tunable, which saves # around 600mW when the link is down. The 88x3310 supports off, rx-only # and NLP every second. Enable EDPD by default for 88x3310. # # Signed-off-by: Russell King # # 49272a6f30415f0f961b74fb675344f71c60d500 # drivers/net/phy/marvell10g.c | 86 +++++++++++++++++++++++++++++++++++++++++++- # 1 file changed, 85 insertions(+), 1 deletion(-) # # Author: Russell King (Sat 30 Nov 01:13:51 GMT 2019) # Committer: Russell King (Mon 30 Mar 21:38:14 BST 2020) # # net: phy: marvell10g: add mdix control # # Add support for controlling the MDI-X state of the PHY. # # Signed-off-by: Russell King # # 50317256d6d260d6933cea9627f9a61d581512a8 # drivers/net/phy/marvell10g.c | 61 ++++++++++++++++++++++++++++++++++++++++++-- # 1 file changed, 59 insertions(+), 2 deletions(-) # # Author: Russell King (Mon 16 Dec 16:19:19 GMT 2019) # Committer: Russell King (Mon 30 Mar 21:38:13 BST 2020) # # doc: sfp-phylink: correct code indentation # # Using vim to edit the phylink documentation reveals some mistakes due # to the "invisible" pythonesque white space indentation that can't be # seen with other editors. Fix it. # # Signed-off-by: Russell King # # 5175815ede3f41a91eaee3cf06b3c59935c1a9f3 # Documentation/networking/sfp-phylink.rst | 32 ++++++++++++++++---------------- # 1 file changed, 16 insertions(+), 16 deletions(-) # # Author: Russell King (Wed 12 Feb 13:46:37 GMT 2020) # Committer: Russell King (Mon 30 Mar 21:38:11 BST 2020) # # net: mvpp2: use resolved link config in mac_link_up() # # Convert the Marvell mvpp2 ethernet driver to use the finalised link # parameters in mac_link_up() rather than the parameters in mac_config(). # # Signed-off-by: Russell King # # a3acb1c297f55f469fbe0d2d8843d491a459d97b # drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c | 83 ++++++++++++++----------- # 1 file changed, 47 insertions(+), 36 deletions(-) # # Author: Russell King (Wed 12 Feb 12:28:01 GMT 2020) # Committer: Russell King (Mon 30 Mar 21:38:09 BST 2020) # # net: mvneta: use resolved link config in mac_link_up() # # Convert the Marvell mvneta ethernet driver to use the finalised link # parameters in mac_link_up() rather than the parameters in mac_config(). # # Signed-off-by: Russell King # # dc598a69311866489f750384dae9d1d84f3cf39d # drivers/net/ethernet/marvell/mvneta.c | 55 ++++++++++++++++++++++++----------- # 1 file changed, 38 insertions(+), 17 deletions(-) # # Author: Russell King (Wed 12 Feb 18:08:29 GMT 2020) # Committer: Russell King (Mon 30 Mar 21:38:08 BST 2020) # # net: macb: use resolved link config in mac_link_up() # # Convert the macb ethernet driver to use the finalised link # parameters in mac_link_up() rather than the parameters in mac_config(). # # Tested-by: Alexandre Belloni # Signed-off-by: Russell King # --- # Updated patch from Alexandre Belloni # that depends on ac2fcfa9fd26 ("net: macb: Properly handle phylink on # at91rm9200"). # # 5cbe84c69fab8add73cdf14a72e6dbd7a3b9228f # drivers/net/ethernet/cadence/macb.h | 1 - # drivers/net/ethernet/cadence/macb_main.c | 50 ++++++++++++++++++-------------- # 2 files changed, 29 insertions(+), 22 deletions(-) # # Author: Russell King (Thu 30 Jan 11:50:32 GMT 2020) # Committer: Russell King (Mon 30 Mar 21:38:06 BST 2020) # # net: dpaa2-mac: use resolved link config in mac_link_up() # # Convert the DPAA2 ethernet driver to use the finalised link parameters # in mac_link_up() rather than the parameters in mac_config(), which are # more suited to the needs of the DPAA2 MC firmware than those available # via mac_config(). # # Signed-off-by: Russell King # # a6b542d812eb92d459fae2f5f40536e804262283 # drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c | 54 ++++++++++++++---------- # drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h | 1 + # 2 files changed, 33 insertions(+), 22 deletions(-) # # Author: Russell King (Wed 12 Feb 12:28:01 GMT 2020) # Committer: Russell King (Mon 30 Mar 21:38:05 BST 2020) # # net: axienet: use resolved link config in mac_link_up() # # Convert the Xilinx AXI ethernet driver to use the finalised link # parameters in mac_link_up() rather than the parameters in mac_config(). # # Tested-by: Andre Przywara # Signed-off-by: Russell King # # 5746b687ec389cb28f35c3ca8426c0485e1c237e # drivers/net/ethernet/xilinx/xilinx_axienet_main.c | 38 +++++++++++------------ # 1 file changed, 19 insertions(+), 19 deletions(-) # # Author: Russell King (Wed 29 Jan 00:26:29 GMT 2020) # Committer: Russell King (Mon 30 Mar 21:38:03 BST 2020) # # net: mv88e6xxx: use resolved link config in mac_link_up() # # Use the resolved link configuration to set the MAC configuration when # mac_link_up() for non-internal-PHY ports. # # Signed-off-by: Russell King # # 7f73e70bab675b988d8bd43f6ea3a40e290aa808 # drivers/net/dsa/mv88e6xxx/chip.c | 75 +++++++++++++++++++++++++++++++--------- # 1 file changed, 59 insertions(+), 16 deletions(-) # # Author: Russell King (Tue 28 Jan 18:53:13 GMT 2020) # Committer: Russell King (Mon 30 Mar 21:38:02 BST 2020) # # net: dsa: propagate resolved link config via mac_link_up() # # Propagate the resolved link configuration down via DSA's # phylink_mac_link_up() operation to allow split PCS/MAC to work. # # Tested-by: Vladimir Oltean # Signed-off-by: Russell King # # 6f9538c663d4d54ef73d8c6b78f24e3f4eb144ac # drivers/net/dsa/b53/b53_common.c | 4 +++- # drivers/net/dsa/b53/b53_priv.h | 4 +++- # drivers/net/dsa/bcm_sf2.c | 4 +++- # drivers/net/dsa/lantiq_gswip.c | 4 +++- # drivers/net/dsa/mt7530.c | 4 +++- # drivers/net/dsa/mv88e6xxx/chip.c | 4 +++- # drivers/net/dsa/sja1105/sja1105_main.c | 4 +++- # include/net/dsa.h | 4 +++- # net/dsa/port.c | 3 ++- # 9 files changed, 26 insertions(+), 9 deletions(-) # # Author: Russell King (Tue 28 Jan 16:11:27 GMT 2020) # Committer: Russell King (Mon 30 Mar 21:37:58 BST 2020) # # net: phylink: propagate resolved link config via mac_link_up() # # Propagate the resolved link parameters via the mac_link_up() call for # MACs that do not automatically track their PCS state. We propagate the # link parameters via function arguments so that inappropriate members # of struct phylink_link_state can't be accessed, and creating a new # structure just for this adds needless complexity to the API. # # Tested-by: Andre Przywara # Tested-by: Alexandre Belloni # Tested-by: Vladimir Oltean # Signed-off-by: Russell King # # 0df5a546d811b7c2dcd6be76655b4658f2b776f6 # Documentation/networking/sfp-phylink.rst | 17 +++++-- # drivers/net/ethernet/cadence/macb_main.c | 7 ++- # drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c | 7 ++- # drivers/net/ethernet/marvell/mvneta.c | 8 ++-- # drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c | 19 +++++--- # drivers/net/ethernet/mediatek/mtk_eth_soc.c | 7 +-- # drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 4 +- # drivers/net/ethernet/xilinx/xilinx_axienet_main.c | 7 +-- # drivers/net/phy/phylink.c | 9 +++- # include/linux/phylink.h | 57 +++++++++++++++++------ # net/dsa/port.c | 4 +- # 11 files changed, 105 insertions(+), 41 deletions(-) # # Author: Russell King (Sun 17 Nov 15:25:42 GMT 2019) # Committer: Russell King (Mon 30 Mar 12:40:47 BST 2020) # # dpaa2-eth: add support for nway reset # # Add support for ethtool -r so that PHY negotiation can be restarted. # # Signed-off-by: Russell King # # fca5a7db693c72590fe44e2dcd40f13b52cfa975 # drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c | 11 +++++++++++ # 1 file changed, 11 insertions(+) # # Author: Russell King (Sun 17 Nov 15:23:14 GMT 2019) # Committer: Russell King (Mon 30 Mar 12:40:46 BST 2020) # # dpaa2-eth: add support for mii ioctls # # Add support for the MII ioctl API. # # Signed-off-by: Russell King # # e3b7de540a2e6a078f6cfcb0137e120b2875a191 # drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c | 7 ++++++- # 1 file changed, 6 insertions(+), 1 deletion(-) # # Author: Russell King (Fri 29 Nov 21:31:45 GMT 2019) # Committer: Russell King (Mon 30 Mar 12:40:44 BST 2020) # # net: phy: marvell10g: read copper results from CSSR1 # # Read the copper autonegotiation results from the copper specific # status register, rather than decoding the advertisements. Reading # what the link is actually doing will allow us to support downshift # modes. # # Reviewed-by: Andrew Lunn # Signed-off-by: Russell King # # 72ba55915db0799ffe145d6f74438c1bf580b368 # drivers/net/phy/marvell10g.c | 141 +++++++++++++++++++++++++++---------------- # 1 file changed, 89 insertions(+), 52 deletions(-) # # Author: Russell King (Sun 22 Dec 19:13:08 GMT 2019) # Committer: Russell King (Mon 30 Mar 12:39:48 BST 2020) # # net: dsa: mv88e6xxx: fix duplicate vlan warning # # When setting VLANs on DSA switches, the VLAN is added to both the port # concerned as well as the CPU port by dsa_slave_vlan_add(), as well as # any DSA ports. If multiple ports are configured with the same VLAN ID, # this triggers a warning on the CPU and DSA ports. # # Avoid this warning for CPU and DSA ports. # # Signed-off-by: Russell King # # 74aa2bc73a270da24dbe2910fbd91cfb05958246 # drivers/net/dsa/mv88e6xxx/chip.c | 12 +++++++++--- # 1 file changed, 9 insertions(+), 3 deletions(-) # # Author: Russell King (Sun 22 Dec 16:36:35 GMT 2019) # Committer: Russell King (Mon 30 Mar 12:39:47 BST 2020) # # net: switchdev: do not propagate bridge updates across bridges # # When configuring a tree of independent bridges, propagating changes # from the upper bridge across a bridge master to the lower bridge # ports brings surprises. # # For example, a lower bridge may have vlan filtering enabled. It # may have a vlan interface attached to the bridge master, which may # then be incorporated into another bridge. As soon as the lower # bridge vlan interface is attached to the upper bridge, the lower # bridge has vlan filtering disabled. # # This occurs because switchdev recursively applies its changes to # all lower devices no matter what. # # Reviewed-by: Ido Schimmel # Tested-by: Ido Schimmel # Signed-off-by: Russell King # # bada37db36c8c24d2c459b1ec6ea18f8c8c8f03c # net/switchdev/switchdev.c | 9 +++++++++ # 1 file changed, 9 insertions(+) # # Author: Russell King (Thu 19 Dec 17:18:25 GMT 2019) # Committer: Russell King (Mon 30 Mar 12:39:44 BST 2020) # # net: phy: allow bcm84881 to be a module # # Now that the phylib module loading issue has been resolved, we can # allow this PHY driver to be built as a module. # # Signed-off-by: Russell King # # a7e408cc7c7b47bda32c52e272e6d4751d7a4f3a # drivers/net/phy/Kconfig | 4 ++-- # 1 file changed, 2 insertions(+), 2 deletions(-) # # Author: Russell King (Tue 10 Dec 14:52:08 GMT 2019) # Committer: Russell King (Mon 30 Mar 12:39:42 BST 2020) # # net: phylink: clarify flow control settings in documentation # # Clarify the expected flow control settings operation in the phylink # documentation for each negotiation mode. # # Signed-off-by: Russell King # # 145754a7943ebe65ec71202a6ff23137605468ae # include/linux/phylink.h | 26 ++++++++++++++++++-------- # 1 file changed, 18 insertions(+), 8 deletions(-) # # Author: Russell King (Wed 22 Jan 10:18:02 GMT 2020) # Committer: Russell King (Mon 30 Mar 12:39:41 BST 2020) # # net: phylink: improve initial mac configuration # # Improve the initial MAC configuration so we get a configuration which # more represents the final operating mode, in particular with respect # to the flow control settings. # # We do this by: # 1) more fully initialising our phy state, so we can use this as the # initial state for PHY based connections. # 2) reading the fixed link state. # 3) ensuring that in-band mode has sane pause settings for SGMII vs # 802.3z negotiation modes. # # In all three cases, we ensure that state->link is false, just in case # any MAC drivers have other ideas by mis-using this member, and we also # take account of manual pause mode configuration at this point. # # This avoids MLO_PAUSE_AN being seen in mac_config() when operating in # PHY, fixed mode or inband SGMII mode, thereby giving cleaner semantics # to the pause flags. As a result of this, the pause flags now indicate # in a mode-independent way what is required from a mac_config() # implementation. # # Signed-off-by: Russell King # # 7d1891db3be8f25ebd50082e36ff7d9a46965e61 # drivers/net/phy/phylink.c | 36 ++++++++++++++++++++++++++++++++++-- # 1 file changed, 34 insertions(+), 2 deletions(-) # # Author: Russell King (Tue 19 Nov 15:53:31 GMT 2019) # Committer: Russell King (Mon 30 Mar 12:39:39 BST 2020) # # net: phylink: allow ethtool -A to change flow control advertisement # # When ethtool -A is used to change the pause modes, the pause # advertisement is not being changed, but the documentation in # uapi/linux/ethtool.h says we should be. Add that capability to # phylink. # # Signed-off-by: Russell King # # e7a5639f35055da272e21b4bed9492a2ed7270db # drivers/net/phy/phylink.c | 18 ++++++++++++++++++ # 1 file changed, 18 insertions(+) # # Author: Russell King (Fri 13 Dec 16:06:45 GMT 2019) # Committer: Russell King (Mon 30 Mar 12:39:37 BST 2020) # # net: phylink: resolve fixed link flow control # # Resolve the fixed link flow control using the recently introduced # linkmode_resolve_pause() helper, which we use in # phylink_get_fixed_state() only when operating in full duplex mode. # # Signed-off-by: Russell King # # 0f9ebe22f427809bda92d98dff6ff780289934fe # drivers/net/phy/phylink.c | 70 +++++++++++++++++++++-------------------------- # include/linux/phylink.h | 8 ++---- # 2 files changed, 34 insertions(+), 44 deletions(-) # # Author: Russell King (Fri 13 Dec 14:19:13 GMT 2019) # Committer: Russell King (Mon 30 Mar 12:39:35 BST 2020) # # net: phylink: use phylib resolved flow control modes # # Use the new phy_get_pause() helper to get the resolved pause modes for # a PHY rather than resolving the pause modes ourselves. We temporarily # retain our pause mode resolution for causes where there is no PHY # attached, e.g. for fixed-link modes. # # Signed-off-by: Russell King # # 8bbcd44c572339135465a498ea3c1566278e0dcf # drivers/net/phy/phylink.c | 17 +++++++++-------- # 1 file changed, 9 insertions(+), 8 deletions(-) # # Author: Russell King (Mon 20 Jan 17:52:04 GMT 2020) # Committer: Russell King (Mon 30 Mar 12:39:34 BST 2020) # # net: phylink: ensure manual flow control is selected appropriately # # Split the application of manually controlled flow control modes from # phylink_resolve_flow(), so that we can use alternative providers of # flow control resolution. # # We also want to clear the MLO_PAUSE_AN flag when autoneg is disabled, # since flow control can't be negotiated in this circumstance. # # Signed-off-by: Russell King # # cfbcf9211536b03e275e94151a7c36b4f772e853 # drivers/net/phy/phylink.c | 42 +++++++++++++++++++++++++----------------- # 1 file changed, 25 insertions(+), 17 deletions(-) # # Author: Russell King (Mon 20 Jan 16:49:25 GMT 2020) # Committer: Russell King (Mon 30 Mar 12:39:32 BST 2020) # # net: phylink: remove pause mode ethtool setting for fixed links # # Remove the ability for ethtool -A to change the pause settings for # fixed links; if this is really required, we can reinstate it later. # # Andrew Lunn agrees: "So I think it is safe to not implement ethtool # -A, at least until somebody has a real use case for it." # # Lets avoid making things too complex for use cases that aren't being # used. # # Signed-off-by: Russell King # # 7ed4e12d466c937ac22c8a71e1d27bd0e50f25b9 # drivers/net/phy/phylink.c | 17 +++++------------ # 1 file changed, 5 insertions(+), 12 deletions(-) # # Author: Russell King (Fri 3 Jan 17:50:27 GMT 2020) # Committer: Russell King (Mon 30 Mar 12:39:00 BST 2020) # # net: add linkmode helper for setting flow control advertisement # # Add a linkmode helper to set the flow control advertisement in an # ethtool linkmode mask according to the tx/rx capabilities. This # implementation is moved from phylib, and documented with an # analysis of its shortcomings. # # Signed-off-by: Russell King # # 7ab23c72777424fffd839aa4bc90adf5c84e7aff # drivers/net/phy/linkmode.c | 51 ++++++++++++++++++++++++++++++++++++++++++++ # drivers/net/phy/phy_device.c | 17 +-------------- # include/linux/linkmode.h | 2 ++ # 3 files changed, 54 insertions(+), 16 deletions(-) # # Author: Russell King (Mon 9 Dec 23:13:51 GMT 2019) # Committer: Russell King (Mon 30 Mar 12:38:58 BST 2020) # # net: add helpers to resolve negotiated flow control # # Add a couple of helpers to resolve negotiated flow control. Two helpers # are provided: # # - linkmode_resolve_pause() which takes the link partner and local # advertisements, and decodes whether we should enable TX or RX pause # at the MAC. This is useful outside of phylib, e.g. in phylink. # - phy_get_pause(), which returns the TX/RX enablement status for the # current negotiation results of the PHY. # # This allows us to centralise the flow control resolution, rather than # spreading it around. # # Signed-off-by: Russell King # # 5510b32e3bc38585cbdeb73a1b49f9337ec5f772 # drivers/net/phy/Makefile | 3 ++- # drivers/net/phy/linkmode.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ # drivers/net/phy/phy_device.c | 26 ++++++++++++++++++++++++++ # include/linux/linkmode.h | 4 ++++ # include/linux/phy.h | 3 +++ # 5 files changed, 79 insertions(+), 1 deletion(-) # create mode 100644 drivers/net/phy/linkmode.c # # Author: Russell King (Fri 3 Jan 19:41:39 GMT 2020) # Committer: Russell King (Mon 30 Mar 12:38:57 BST 2020) # # net: linkmode: make linkmode_test_bit() take const pointer # # linkmode_test_bit() does not modify the address; test_bit() is also # declared const volatile for the same reason. There's no need for # linkmode_test_bit() to be any different, and allows implementation of # helpers that take a const linkmode pointer. # # Signed-off-by: Russell King # # 406e21b10db69e05bd909727fd8b6f4f4866e810 # include/linux/linkmode.h | 2 +- # 1 file changed, 1 insertion(+), 1 deletion(-) # # Author: Russell King (Thu 21 Nov 11:55:06 GMT 2019) # Committer: Russell King (Mon 30 Mar 12:38:07 BST 2020) # # net: phy: marvell: initialise link partner state earlier # # Move the initialisation of the link partner state earlier, inside # marvell_read_status_page(), so we don't have the same initialisation # scattered amongst the other files. This is in a similar place to # the genphy implementation, so would result in the same behaviour if # a PHY read error occurs. # # This allows us to get rid of marvell_read_status_page_fixed(), which # became a pointless wrapper around genphy_read_status_fixed(). # # Signed-off-by: Russell King # # 5ecdbb6b1f782f468214ef5bdb89d6ebe373ea44 # drivers/net/phy/marvell.c | 4 ++++ # 1 file changed, 4 insertions(+) # diff --git a/Documentation/networking/sfp-phylink.rst b/Documentation/networking/sfp-phylink.rst index d753a309f9d1..5aec7c8857d0 100644 --- a/Documentation/networking/sfp-phylink.rst +++ b/Documentation/networking/sfp-phylink.rst @@ -74,10 +74,13 @@ phylib to the sfp/phylink support. Please send patches to improve this documentation. 1. Optionally split the network driver's phylib update function into - three parts dealing with link-down, link-up and reconfiguring the - MAC settings. This can be done as a separate preparation commit. + two parts dealing with link-down and link-up. This can be done as + a separate preparation commit. - An example of this preparation can be found in git commit fc548b991fb0. + An older example of this preparation can be found in git commit + fc548b991fb0, although this was splitting into three parts; the + link-up part now includes configuring the MAC for the link settings. + Please see :c:func:`mac_link_up` for more information on this. 2. Replace:: @@ -135,27 +138,27 @@ this documentation. .. code-block:: c - static int foo_ethtool_set_link_ksettings(struct net_device *dev, - const struct ethtool_link_ksettings *cmd) - { - struct foo_priv *priv = netdev_priv(dev); - - return phylink_ethtool_ksettings_set(priv->phylink, cmd); - } - - static int foo_ethtool_get_link_ksettings(struct net_device *dev, - struct ethtool_link_ksettings *cmd) - { - struct foo_priv *priv = netdev_priv(dev); + static int foo_ethtool_set_link_ksettings(struct net_device *dev, + const struct ethtool_link_ksettings *cmd) + { + struct foo_priv *priv = netdev_priv(dev); + + return phylink_ethtool_ksettings_set(priv->phylink, cmd); + } - return phylink_ethtool_ksettings_get(priv->phylink, cmd); - } + static int foo_ethtool_get_link_ksettings(struct net_device *dev, + struct ethtool_link_ksettings *cmd) + { + struct foo_priv *priv = netdev_priv(dev); + + return phylink_ethtool_ksettings_get(priv->phylink, cmd); + } -7. Replace the call to: +7. Replace the call to:: phy_dev = of_phy_connect(dev, node, link_func, flags, phy_interface); - and associated code with a call to: + and associated code with a call to:: err = phylink_of_phy_connect(priv->phylink, node, flags); @@ -207,6 +210,14 @@ this documentation. using. This is particularly important for in-band negotiation methods such as 1000base-X and SGMII. + The :c:func:`mac_link_up` method is used to inform the MAC that the + link has come up. The call includes the negotiation mode and interface + for reference only. The finalised link parameters are also supplied + (speed, duplex and flow control/pause enablement settings) which + should be used to configure the MAC when the MAC and PCS are not + tightly integrated, or when the settings are not coming from in-band + negotiation. + The :c:func:`mac_config` method is used to update the MAC with the requested state, and must avoid unnecessarily taking the link down when making changes to the MAC configuration. This means the diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c index 1a69286daa8d..ceafce446317 100644 --- a/drivers/net/dsa/b53/b53_common.c +++ b/drivers/net/dsa/b53/b53_common.c @@ -1289,7 +1289,9 @@ EXPORT_SYMBOL(b53_phylink_mac_link_down); void b53_phylink_mac_link_up(struct dsa_switch *ds, int port, unsigned int mode, phy_interface_t interface, - struct phy_device *phydev) + struct phy_device *phydev, + int speed, int duplex, + bool tx_pause, bool rx_pause) { struct b53_device *dev = ds->priv; diff --git a/drivers/net/dsa/b53/b53_priv.h b/drivers/net/dsa/b53/b53_priv.h index 3c30f3a7eb29..3d42318bc3f1 100644 --- a/drivers/net/dsa/b53/b53_priv.h +++ b/drivers/net/dsa/b53/b53_priv.h @@ -338,7 +338,9 @@ void b53_phylink_mac_link_down(struct dsa_switch *ds, int port, void b53_phylink_mac_link_up(struct dsa_switch *ds, int port, unsigned int mode, phy_interface_t interface, - struct phy_device *phydev); + struct phy_device *phydev, + int speed, int duplex, + bool tx_pause, bool rx_pause); int b53_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering); int b53_vlan_prepare(struct dsa_switch *ds, int port, const struct switchdev_obj_port_vlan *vlan); diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index b0f5280a83cb..368ead87e07a 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c @@ -648,7 +648,9 @@ static void bcm_sf2_sw_mac_link_down(struct dsa_switch *ds, int port, static void bcm_sf2_sw_mac_link_up(struct dsa_switch *ds, int port, unsigned int mode, phy_interface_t interface, - struct phy_device *phydev) + struct phy_device *phydev, + int speed, int duplex, + bool tx_pause, bool rx_pause) { struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); struct ethtool_eee *p = &priv->dev->ports[port].eee; diff --git a/drivers/net/dsa/lantiq_gswip.c b/drivers/net/dsa/lantiq_gswip.c index 0369c22fe3e1..cf6fa8fede33 100644 --- a/drivers/net/dsa/lantiq_gswip.c +++ b/drivers/net/dsa/lantiq_gswip.c @@ -1517,7 +1517,9 @@ static void gswip_phylink_mac_link_down(struct dsa_switch *ds, int port, static void gswip_phylink_mac_link_up(struct dsa_switch *ds, int port, unsigned int mode, phy_interface_t interface, - struct phy_device *phydev) + struct phy_device *phydev, + int speed, int duplex, + bool tx_pause, bool rx_pause) { struct gswip_priv *priv = ds->priv; diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index 7cbd1bd4c5a6..056686a8fdb6 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -1482,7 +1482,9 @@ static void mt7530_phylink_mac_link_down(struct dsa_switch *ds, int port, static void mt7530_phylink_mac_link_up(struct dsa_switch *ds, int port, unsigned int mode, phy_interface_t interface, - struct phy_device *phydev) + struct phy_device *phydev, + int speed, int duplex, + bool tx_pause, bool rx_pause) { struct mt7530_priv *priv = ds->priv; diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 2f993e673ec7..6c9c89818082 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -397,32 +397,35 @@ static void mv88e6xxx_irq_poll_free(struct mv88e6xxx_chip *chip) mv88e6xxx_reg_unlock(chip); } -int mv88e6xxx_port_setup_mac(struct mv88e6xxx_chip *chip, int port, int link, - int speed, int duplex, int pause, - phy_interface_t mode) +static int mv88e6xxx_port_config_interface(struct mv88e6xxx_chip *chip, + int port, phy_interface_t interface) { - struct phylink_link_state state; int err; - if (!chip->info->ops->port_set_link) - return 0; + if (chip->info->ops->port_set_rgmii_delay) { + err = chip->info->ops->port_set_rgmii_delay(chip, port, + interface); + if (err && err != -EOPNOTSUPP) + return err; + } - if (!chip->info->ops->port_link_state) - return 0; + if (chip->info->ops->port_set_cmode) { + err = chip->info->ops->port_set_cmode(chip, port, + interface); + if (err && err != -EOPNOTSUPP) + return err; + } - err = chip->info->ops->port_link_state(chip, port, &state); - if (err) - return err; + return 0; +} - /* Has anything actually changed? We don't expect the - * interface mode to change without one of the other - * parameters also changing - */ - if (state.link == link && - state.speed == speed && - state.duplex == duplex && - (state.interface == mode || - state.interface == PHY_INTERFACE_MODE_NA)) +static int mv88e6xxx_port_setup_mac(struct mv88e6xxx_chip *chip, int port, + int link, int speed, int duplex, int pause, + phy_interface_t mode) +{ + int err; + + if (!chip->info->ops->port_set_link) return 0; /* Port's MAC control must not be changed unless the link is down */ @@ -430,8 +433,9 @@ int mv88e6xxx_port_setup_mac(struct mv88e6xxx_chip *chip, int port, int link, if (err) return err; - if (chip->info->ops->port_set_speed) { - err = chip->info->ops->port_set_speed(chip, port, speed); + if (chip->info->ops->port_set_speed_duplex) { + err = chip->info->ops->port_set_speed_duplex(chip, port, + speed, duplex); if (err && err != -EOPNOTSUPP) goto restore_link; } @@ -445,25 +449,7 @@ int mv88e6xxx_port_setup_mac(struct mv88e6xxx_chip *chip, int port, int link, goto restore_link; } - if (chip->info->ops->port_set_duplex) { - err = chip->info->ops->port_set_duplex(chip, port, duplex); - if (err && err != -EOPNOTSUPP) - goto restore_link; - } - - if (chip->info->ops->port_set_rgmii_delay) { - err = chip->info->ops->port_set_rgmii_delay(chip, port, mode); - if (err && err != -EOPNOTSUPP) - goto restore_link; - } - - if (chip->info->ops->port_set_cmode) { - err = chip->info->ops->port_set_cmode(chip, port, mode); - if (err && err != -EOPNOTSUPP) - goto restore_link; - } - - err = 0; + err = mv88e6xxx_port_config_interface(chip, port, mode); restore_link: if (chip->info->ops->port_set_link(chip, port, link)) dev_err(chip->dev, "p%d: failed to restore MAC's link\n", port); @@ -478,6 +464,97 @@ static int mv88e6xxx_phy_is_internal(struct dsa_switch *ds, int port) return port < chip->info->num_internal_phys; } +static int mv88e6xxx_port_ppu_updates(struct mv88e6xxx_chip *chip, int port) +{ + u16 reg; + int err; + + err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, ®); + if (err) { + dev_err(chip->dev, + "p%d: %s: failed to read port status\n", + port, __func__); + return err; + } + + return !!(reg & MV88E6XXX_PORT_STS_PHY_DETECT); +} + +static int mv88e6xxx_serdes_pcs_get_state(struct dsa_switch *ds, int port, + struct phylink_link_state *state) +{ + struct mv88e6xxx_chip *chip = ds->priv; + u8 lane; + int err; + + mv88e6xxx_reg_lock(chip); + lane = mv88e6xxx_serdes_get_lane(chip, port); + if (lane && chip->info->ops->serdes_pcs_get_state) + err = chip->info->ops->serdes_pcs_get_state(chip, port, lane, + state); + else + err = -EOPNOTSUPP; + mv88e6xxx_reg_unlock(chip); + + return err; +} + +static int mv88e6xxx_serdes_pcs_config(struct mv88e6xxx_chip *chip, int port, + unsigned int mode, + phy_interface_t interface, + const unsigned long *advertise) +{ + const struct mv88e6xxx_ops *ops = chip->info->ops; + u8 lane; + + if (ops->serdes_pcs_config) { + lane = mv88e6xxx_serdes_get_lane(chip, port); + if (lane) + return ops->serdes_pcs_config(chip, port, lane, mode, + interface, advertise); + } + + return 0; +} + +static void mv88e6xxx_serdes_pcs_an_restart(struct dsa_switch *ds, int port) +{ + struct mv88e6xxx_chip *chip = ds->priv; + const struct mv88e6xxx_ops *ops; + int err = 0; + u8 lane; + + ops = chip->info->ops; + + if (ops->serdes_pcs_an_restart) { + mv88e6xxx_reg_lock(chip); + lane = mv88e6xxx_serdes_get_lane(chip, port); + if (lane) + err = ops->serdes_pcs_an_restart(chip, port, lane); + mv88e6xxx_reg_unlock(chip); + + if (err) + dev_err(ds->dev, "p%d: failed to restart AN\n", port); + } +} + +static int mv88e6xxx_serdes_pcs_link_up(struct mv88e6xxx_chip *chip, int port, + unsigned int mode, + int speed, int duplex) +{ + const struct mv88e6xxx_ops *ops = chip->info->ops; + u8 lane; + + if (!phylink_autoneg_inband(mode) && ops->serdes_pcs_link_up) { + lane = mv88e6xxx_serdes_get_lane(chip, port); + if (lane) + return ops->serdes_pcs_link_up(chip, port, lane, + speed, duplex); + } + + return 0; +} + static void mv88e6065_phylink_validate(struct mv88e6xxx_chip *chip, int port, unsigned long *mask, struct phylink_link_state *state) @@ -582,83 +659,106 @@ static void mv88e6xxx_validate(struct dsa_switch *ds, int port, phylink_helper_basex_speed(state); } -static int mv88e6xxx_link_state(struct dsa_switch *ds, int port, - struct phylink_link_state *state) -{ - struct mv88e6xxx_chip *chip = ds->priv; - int err; - - mv88e6xxx_reg_lock(chip); - if (chip->info->ops->port_link_state) - err = chip->info->ops->port_link_state(chip, port, state); - else - err = -EOPNOTSUPP; - mv88e6xxx_reg_unlock(chip); - - return err; -} - static void mv88e6xxx_mac_config(struct dsa_switch *ds, int port, unsigned int mode, const struct phylink_link_state *state) { struct mv88e6xxx_chip *chip = ds->priv; - int speed, duplex, link, pause, err; + int err; + /* FIXME: is this the correct test? If we're in fixed mode on an + * internal port, why should we process this any different from + * PHY mode? On the other hand, the port may be automedia between + * an internal PHY and the serdes... + */ if ((mode == MLO_AN_PHY) && mv88e6xxx_phy_is_internal(ds, port)) return; - if (mode == MLO_AN_FIXED) { - link = LINK_FORCED_UP; - speed = state->speed; - duplex = state->duplex; - } else if (!mv88e6xxx_phy_is_internal(ds, port)) { - link = state->link; - speed = state->speed; - duplex = state->duplex; - } else { - speed = SPEED_UNFORCED; - duplex = DUPLEX_UNFORCED; - link = LINK_UNFORCED; - } - pause = !!phylink_test(state->advertising, Pause); - mv88e6xxx_reg_lock(chip); - err = mv88e6xxx_port_setup_mac(chip, port, link, speed, duplex, pause, - state->interface); + /* FIXME: should we force the link down here - but if we do, how + * do we restore the link force/unforce state? The driver layering + * gets in the way. + */ + err = mv88e6xxx_port_config_interface(chip, port, state->interface); + if (err && err != -EOPNOTSUPP) + goto err_unlock; + + err = mv88e6xxx_serdes_pcs_config(chip, port, mode, state->interface, + state->advertising); + /* FIXME: we should restart negotiation if something changed - which + * is something we get if we convert to using phylinks PCS operations. + */ + if (err > 0) + err = 0; + +err_unlock: mv88e6xxx_reg_unlock(chip); if (err && err != -EOPNOTSUPP) - dev_err(ds->dev, "p%d: failed to configure MAC\n", port); + dev_err(ds->dev, "p%d: failed to configure MAC/PCS\n", port); } -static void mv88e6xxx_mac_link_force(struct dsa_switch *ds, int port, int link) +static void mv88e6xxx_mac_link_down(struct dsa_switch *ds, int port, + unsigned int mode, + phy_interface_t interface) { struct mv88e6xxx_chip *chip = ds->priv; - int err; + const struct mv88e6xxx_ops *ops; + int err = 0; + + ops = chip->info->ops; mv88e6xxx_reg_lock(chip); - err = chip->info->ops->port_set_link(chip, port, link); + if (!mv88e6xxx_port_ppu_updates(chip, port) && ops->port_set_link) + err = ops->port_set_link(chip, port, LINK_FORCED_DOWN); mv88e6xxx_reg_unlock(chip); if (err) - dev_err(chip->dev, "p%d: failed to force MAC link\n", port); -} - -static void mv88e6xxx_mac_link_down(struct dsa_switch *ds, int port, - unsigned int mode, - phy_interface_t interface) -{ - if (mode == MLO_AN_FIXED) - mv88e6xxx_mac_link_force(ds, port, LINK_FORCED_DOWN); + dev_err(chip->dev, + "p%d: failed to force MAC link down\n", port); } static void mv88e6xxx_mac_link_up(struct dsa_switch *ds, int port, unsigned int mode, phy_interface_t interface, - struct phy_device *phydev) + struct phy_device *phydev, + int speed, int duplex, + bool tx_pause, bool rx_pause) { - if (mode == MLO_AN_FIXED) - mv88e6xxx_mac_link_force(ds, port, LINK_FORCED_UP); + struct mv88e6xxx_chip *chip = ds->priv; + const struct mv88e6xxx_ops *ops; + int err = 0; + + ops = chip->info->ops; + + mv88e6xxx_reg_lock(chip); + if (!mv88e6xxx_port_ppu_updates(chip, port)) { + /* FIXME: for an automedia port, should we force the link + * down here - what if the link comes up due to "other" media + * while we're bringing the port up, how is the exclusivity + * handled in the Marvell hardware? E.g. port 2 on 88E6390 + * shared between internal PHY and Serdes. + */ + err = mv88e6xxx_serdes_pcs_link_up(chip, port, mode, speed, + duplex); + if (err) + goto error; + + if (ops->port_set_speed_duplex) { + err = ops->port_set_speed_duplex(chip, port, + speed, duplex); + if (err && err != -EOPNOTSUPP) + goto error; + } + + if (ops->port_set_link) + err = ops->port_set_link(chip, port, LINK_FORCED_UP); + } +error: + mv88e6xxx_reg_unlock(chip); + + if (err && err != -EOPNOTSUPP) + dev_err(ds->dev, + "p%d: failed to configure MAC link up\n", port); } static int mv88e6xxx_stats_snapshot(struct mv88e6xxx_chip *chip, int port) @@ -1785,7 +1885,7 @@ static int mv88e6xxx_broadcast_setup(struct mv88e6xxx_chip *chip, u16 vid) } static int mv88e6xxx_port_vlan_join(struct mv88e6xxx_chip *chip, int port, - u16 vid, u8 member) + u16 vid, u8 member, bool warn) { const u8 non_member = MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_NON_MEMBER; struct mv88e6xxx_vtu_entry vlan; @@ -1830,7 +1930,7 @@ static int mv88e6xxx_port_vlan_join(struct mv88e6xxx_chip *chip, int port, err = mv88e6xxx_vtu_loadpurge(chip, &vlan); if (err) return err; - } else { + } else if (warn) { dev_info(chip->dev, "p%d: already a member of VLAN %d\n", port, vid); } @@ -1844,6 +1944,7 @@ static void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port, struct mv88e6xxx_chip *chip = ds->priv; bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; + bool warn; u8 member; u16 vid; @@ -1857,10 +1958,15 @@ static void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port, else member = MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_TAGGED; + /* net/dsa/slave.c will call dsa_port_vlan_add() for the affected port + * and then the CPU port. Do not warn for duplicates for the CPU port. + */ + warn = !dsa_is_cpu_port(ds, port) && !dsa_is_dsa_port(ds, port); + mv88e6xxx_reg_lock(chip); for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) - if (mv88e6xxx_port_vlan_join(chip, port, vid, member)) + if (mv88e6xxx_port_vlan_join(chip, port, vid, member, warn)) dev_err(ds->dev, "p%d: failed to add VLAN %d%c\n", port, vid, untagged ? 'u' : 't'); @@ -3272,8 +3378,7 @@ static const struct mv88e6xxx_ops mv88e6085_ops = { .phy_read = mv88e6185_phy_ppu_read, .phy_write = mv88e6185_phy_ppu_write, .port_set_link = mv88e6xxx_port_set_link, - .port_set_duplex = mv88e6xxx_port_set_duplex, - .port_set_speed = mv88e6185_port_set_speed, + .port_set_speed_duplex = mv88e6185_port_set_speed_duplex, .port_tag_remap = mv88e6095_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_floods = mv88e6352_port_set_egress_floods, @@ -3282,7 +3387,6 @@ static const struct mv88e6xxx_ops mv88e6085_ops = { .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, - .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6185_port_get_cmode, .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, @@ -3312,12 +3416,10 @@ static const struct mv88e6xxx_ops mv88e6095_ops = { .phy_read = mv88e6185_phy_ppu_read, .phy_write = mv88e6185_phy_ppu_write, .port_set_link = mv88e6xxx_port_set_link, - .port_set_duplex = mv88e6xxx_port_set_duplex, - .port_set_speed = mv88e6185_port_set_speed, + .port_set_speed_duplex = mv88e6185_port_set_speed_duplex, .port_set_frame_mode = mv88e6085_port_set_frame_mode, .port_set_egress_floods = mv88e6185_port_set_egress_floods, .port_set_upstream_port = mv88e6095_port_set_upstream_port, - .port_link_state = mv88e6185_port_link_state, .port_get_cmode = mv88e6185_port_get_cmode, .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, @@ -3343,8 +3445,7 @@ static const struct mv88e6xxx_ops mv88e6097_ops = { .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, - .port_set_duplex = mv88e6xxx_port_set_duplex, - .port_set_speed = mv88e6185_port_set_speed, + .port_set_speed_duplex = mv88e6185_port_set_speed_duplex, .port_tag_remap = mv88e6095_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_floods = mv88e6352_port_set_egress_floods, @@ -3354,7 +3455,6 @@ static const struct mv88e6xxx_ops mv88e6097_ops = { .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, - .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6185_port_get_cmode, .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, @@ -3383,13 +3483,11 @@ static const struct mv88e6xxx_ops mv88e6123_ops = { .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, - .port_set_duplex = mv88e6xxx_port_set_duplex, - .port_set_speed = mv88e6185_port_set_speed, + .port_set_speed_duplex = mv88e6185_port_set_speed_duplex, .port_set_frame_mode = mv88e6085_port_set_frame_mode, .port_set_egress_floods = mv88e6352_port_set_egress_floods, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, - .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6185_port_get_cmode, .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6320_g1_stats_snapshot, @@ -3418,8 +3516,7 @@ static const struct mv88e6xxx_ops mv88e6131_ops = { .phy_read = mv88e6185_phy_ppu_read, .phy_write = mv88e6185_phy_ppu_write, .port_set_link = mv88e6xxx_port_set_link, - .port_set_duplex = mv88e6xxx_port_set_duplex, - .port_set_speed = mv88e6185_port_set_speed, + .port_set_speed_duplex = mv88e6185_port_set_speed_duplex, .port_tag_remap = mv88e6095_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_floods = mv88e6185_port_set_egress_floods, @@ -3429,7 +3526,6 @@ static const struct mv88e6xxx_ops mv88e6131_ops = { .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, .port_pause_limit = mv88e6097_port_pause_limit, .port_set_pause = mv88e6185_port_set_pause, - .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6185_port_get_cmode, .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, @@ -3461,9 +3557,8 @@ static const struct mv88e6xxx_ops mv88e6141_ops = { .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, - .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, - .port_set_speed = mv88e6341_port_set_speed, + .port_set_speed_duplex = mv88e6341_port_set_speed_duplex, .port_max_speed_mode = mv88e6341_port_max_speed_mode, .port_tag_remap = mv88e6095_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, @@ -3474,7 +3569,6 @@ static const struct mv88e6xxx_ops mv88e6141_ops = { .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, - .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6352_port_get_cmode, .port_set_cmode = mv88e6341_port_set_cmode, .port_setup_message_port = mv88e6xxx_setup_message_port, @@ -3493,6 +3587,11 @@ static const struct mv88e6xxx_ops mv88e6141_ops = { .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, .serdes_power = mv88e6390_serdes_power, .serdes_get_lane = mv88e6341_serdes_get_lane, + /* Check status register pause & lpa register */ + .serdes_pcs_get_state = mv88e6390_serdes_pcs_get_state, + .serdes_pcs_config = mv88e6390_serdes_pcs_config, + .serdes_pcs_an_restart = mv88e6390_serdes_pcs_an_restart, + .serdes_pcs_link_up = mv88e6390_serdes_pcs_link_up, .serdes_irq_mapping = mv88e6390_serdes_irq_mapping, .serdes_irq_enable = mv88e6390_serdes_irq_enable, .serdes_irq_status = mv88e6390_serdes_irq_status, @@ -3509,8 +3608,7 @@ static const struct mv88e6xxx_ops mv88e6161_ops = { .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, - .port_set_duplex = mv88e6xxx_port_set_duplex, - .port_set_speed = mv88e6185_port_set_speed, + .port_set_speed_duplex = mv88e6185_port_set_speed_duplex, .port_tag_remap = mv88e6095_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_floods = mv88e6352_port_set_egress_floods, @@ -3520,7 +3618,6 @@ static const struct mv88e6xxx_ops mv88e6161_ops = { .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, - .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6185_port_get_cmode, .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, @@ -3552,11 +3649,9 @@ static const struct mv88e6xxx_ops mv88e6165_ops = { .phy_read = mv88e6165_phy_read, .phy_write = mv88e6165_phy_write, .port_set_link = mv88e6xxx_port_set_link, - .port_set_duplex = mv88e6xxx_port_set_duplex, - .port_set_speed = mv88e6185_port_set_speed, + .port_set_speed_duplex = mv88e6185_port_set_speed_duplex, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, - .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6185_port_get_cmode, .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, @@ -3588,9 +3683,8 @@ static const struct mv88e6xxx_ops mv88e6171_ops = { .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, - .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, - .port_set_speed = mv88e6185_port_set_speed, + .port_set_speed_duplex = mv88e6185_port_set_speed_duplex, .port_tag_remap = mv88e6095_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_floods = mv88e6352_port_set_egress_floods, @@ -3600,7 +3694,6 @@ static const struct mv88e6xxx_ops mv88e6171_ops = { .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, - .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6352_port_get_cmode, .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6320_g1_stats_snapshot, @@ -3632,9 +3725,8 @@ static const struct mv88e6xxx_ops mv88e6172_ops = { .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, - .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, - .port_set_speed = mv88e6352_port_set_speed, + .port_set_speed_duplex = mv88e6352_port_set_speed_duplex, .port_tag_remap = mv88e6095_port_tag_remap, .port_set_policy = mv88e6352_port_set_policy, .port_set_frame_mode = mv88e6351_port_set_frame_mode, @@ -3645,7 +3737,6 @@ static const struct mv88e6xxx_ops mv88e6172_ops = { .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, - .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6352_port_get_cmode, .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6320_g1_stats_snapshot, @@ -3665,6 +3756,10 @@ static const struct mv88e6xxx_ops mv88e6172_ops = { .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, .serdes_get_lane = mv88e6352_serdes_get_lane, + .serdes_pcs_get_state = mv88e6352_serdes_pcs_get_state, + .serdes_pcs_config = mv88e6352_serdes_pcs_config, + .serdes_pcs_an_restart = mv88e6352_serdes_pcs_an_restart, + .serdes_pcs_link_up = mv88e6352_serdes_pcs_link_up, .serdes_power = mv88e6352_serdes_power, .gpio_ops = &mv88e6352_gpio_ops, .phylink_validate = mv88e6352_phylink_validate, @@ -3679,9 +3774,8 @@ static const struct mv88e6xxx_ops mv88e6175_ops = { .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, - .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, - .port_set_speed = mv88e6185_port_set_speed, + .port_set_speed_duplex = mv88e6185_port_set_speed_duplex, .port_tag_remap = mv88e6095_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_floods = mv88e6352_port_set_egress_floods, @@ -3691,7 +3785,6 @@ static const struct mv88e6xxx_ops mv88e6175_ops = { .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, - .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6352_port_get_cmode, .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6320_g1_stats_snapshot, @@ -3723,9 +3816,8 @@ static const struct mv88e6xxx_ops mv88e6176_ops = { .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, - .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, - .port_set_speed = mv88e6352_port_set_speed, + .port_set_speed_duplex = mv88e6352_port_set_speed_duplex, .port_tag_remap = mv88e6095_port_tag_remap, .port_set_policy = mv88e6352_port_set_policy, .port_set_frame_mode = mv88e6351_port_set_frame_mode, @@ -3736,7 +3828,6 @@ static const struct mv88e6xxx_ops mv88e6176_ops = { .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, - .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6352_port_get_cmode, .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6320_g1_stats_snapshot, @@ -3756,6 +3847,10 @@ static const struct mv88e6xxx_ops mv88e6176_ops = { .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, .serdes_get_lane = mv88e6352_serdes_get_lane, + .serdes_pcs_get_state = mv88e6352_serdes_pcs_get_state, + .serdes_pcs_config = mv88e6352_serdes_pcs_config, + .serdes_pcs_an_restart = mv88e6352_serdes_pcs_an_restart, + .serdes_pcs_link_up = mv88e6352_serdes_pcs_link_up, .serdes_power = mv88e6352_serdes_power, .serdes_irq_mapping = mv88e6352_serdes_irq_mapping, .serdes_irq_enable = mv88e6352_serdes_irq_enable, @@ -3772,14 +3867,12 @@ static const struct mv88e6xxx_ops mv88e6185_ops = { .phy_read = mv88e6185_phy_ppu_read, .phy_write = mv88e6185_phy_ppu_write, .port_set_link = mv88e6xxx_port_set_link, - .port_set_duplex = mv88e6xxx_port_set_duplex, - .port_set_speed = mv88e6185_port_set_speed, + .port_set_speed_duplex = mv88e6185_port_set_speed_duplex, .port_set_frame_mode = mv88e6085_port_set_frame_mode, .port_set_egress_floods = mv88e6185_port_set_egress_floods, .port_egress_rate_limiting = mv88e6095_port_egress_rate_limiting, .port_set_upstream_port = mv88e6095_port_set_upstream_port, .port_set_pause = mv88e6185_port_set_pause, - .port_link_state = mv88e6185_port_link_state, .port_get_cmode = mv88e6185_port_get_cmode, .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, @@ -3810,9 +3903,8 @@ static const struct mv88e6xxx_ops mv88e6190_ops = { .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, - .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, - .port_set_speed = mv88e6390_port_set_speed, + .port_set_speed_duplex = mv88e6390_port_set_speed_duplex, .port_max_speed_mode = mv88e6390_port_max_speed_mode, .port_tag_remap = mv88e6390_port_tag_remap, .port_set_policy = mv88e6352_port_set_policy, @@ -3822,7 +3914,6 @@ static const struct mv88e6xxx_ops mv88e6190_ops = { .port_pause_limit = mv88e6390_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, - .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6352_port_get_cmode, .port_set_cmode = mv88e6390_port_set_cmode, .port_setup_message_port = mv88e6xxx_setup_message_port, @@ -3844,6 +3935,11 @@ static const struct mv88e6xxx_ops mv88e6190_ops = { .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, .serdes_power = mv88e6390_serdes_power, .serdes_get_lane = mv88e6390_serdes_get_lane, + /* Check status register pause & lpa register */ + .serdes_pcs_get_state = mv88e6390_serdes_pcs_get_state, + .serdes_pcs_config = mv88e6390_serdes_pcs_config, + .serdes_pcs_an_restart = mv88e6390_serdes_pcs_an_restart, + .serdes_pcs_link_up = mv88e6390_serdes_pcs_link_up, .serdes_irq_mapping = mv88e6390_serdes_irq_mapping, .serdes_irq_enable = mv88e6390_serdes_irq_enable, .serdes_irq_status = mv88e6390_serdes_irq_status, @@ -3864,9 +3960,8 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = { .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, - .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, - .port_set_speed = mv88e6390x_port_set_speed, + .port_set_speed_duplex = mv88e6390x_port_set_speed_duplex, .port_max_speed_mode = mv88e6390x_port_max_speed_mode, .port_tag_remap = mv88e6390_port_tag_remap, .port_set_policy = mv88e6352_port_set_policy, @@ -3876,7 +3971,6 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = { .port_pause_limit = mv88e6390_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, - .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6352_port_get_cmode, .port_set_cmode = mv88e6390x_port_set_cmode, .port_setup_message_port = mv88e6xxx_setup_message_port, @@ -3898,6 +3992,11 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = { .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, .serdes_power = mv88e6390_serdes_power, .serdes_get_lane = mv88e6390x_serdes_get_lane, + /* Check status register pause & lpa register */ + .serdes_pcs_get_state = mv88e6390_serdes_pcs_get_state, + .serdes_pcs_config = mv88e6390_serdes_pcs_config, + .serdes_pcs_an_restart = mv88e6390_serdes_pcs_an_restart, + .serdes_pcs_link_up = mv88e6390_serdes_pcs_link_up, .serdes_irq_mapping = mv88e6390_serdes_irq_mapping, .serdes_irq_enable = mv88e6390_serdes_irq_enable, .serdes_irq_status = mv88e6390_serdes_irq_status, @@ -3918,9 +4017,8 @@ static const struct mv88e6xxx_ops mv88e6191_ops = { .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, - .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, - .port_set_speed = mv88e6390_port_set_speed, + .port_set_speed_duplex = mv88e6390_port_set_speed_duplex, .port_max_speed_mode = mv88e6390_port_max_speed_mode, .port_tag_remap = mv88e6390_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, @@ -3929,7 +4027,6 @@ static const struct mv88e6xxx_ops mv88e6191_ops = { .port_pause_limit = mv88e6390_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, - .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6352_port_get_cmode, .port_set_cmode = mv88e6390_port_set_cmode, .port_setup_message_port = mv88e6xxx_setup_message_port, @@ -3951,6 +4048,11 @@ static const struct mv88e6xxx_ops mv88e6191_ops = { .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, .serdes_power = mv88e6390_serdes_power, .serdes_get_lane = mv88e6390_serdes_get_lane, + /* Check status register pause & lpa register */ + .serdes_pcs_get_state = mv88e6390_serdes_pcs_get_state, + .serdes_pcs_config = mv88e6390_serdes_pcs_config, + .serdes_pcs_an_restart = mv88e6390_serdes_pcs_an_restart, + .serdes_pcs_link_up = mv88e6390_serdes_pcs_link_up, .serdes_irq_mapping = mv88e6390_serdes_irq_mapping, .serdes_irq_enable = mv88e6390_serdes_irq_enable, .serdes_irq_status = mv88e6390_serdes_irq_status, @@ -3973,9 +4075,8 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, - .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, - .port_set_speed = mv88e6352_port_set_speed, + .port_set_speed_duplex = mv88e6352_port_set_speed_duplex, .port_tag_remap = mv88e6095_port_tag_remap, .port_set_policy = mv88e6352_port_set_policy, .port_set_frame_mode = mv88e6351_port_set_frame_mode, @@ -3986,7 +4087,6 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, - .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6352_port_get_cmode, .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6320_g1_stats_snapshot, @@ -4006,6 +4106,10 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, .serdes_get_lane = mv88e6352_serdes_get_lane, + .serdes_pcs_get_state = mv88e6352_serdes_pcs_get_state, + .serdes_pcs_config = mv88e6352_serdes_pcs_config, + .serdes_pcs_an_restart = mv88e6352_serdes_pcs_an_restart, + .serdes_pcs_link_up = mv88e6352_serdes_pcs_link_up, .serdes_power = mv88e6352_serdes_power, .serdes_irq_mapping = mv88e6352_serdes_irq_mapping, .serdes_irq_enable = mv88e6352_serdes_irq_enable, @@ -4027,9 +4131,8 @@ static const struct mv88e6xxx_ops mv88e6250_ops = { .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, - .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, - .port_set_speed = mv88e6250_port_set_speed, + .port_set_speed_duplex = mv88e6250_port_set_speed_duplex, .port_tag_remap = mv88e6095_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_floods = mv88e6352_port_set_egress_floods, @@ -4037,7 +4140,6 @@ static const struct mv88e6xxx_ops mv88e6250_ops = { .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, - .port_link_state = mv88e6250_port_link_state, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6250_stats_get_sset_count, @@ -4066,9 +4168,8 @@ static const struct mv88e6xxx_ops mv88e6290_ops = { .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, - .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, - .port_set_speed = mv88e6390_port_set_speed, + .port_set_speed_duplex = mv88e6390_port_set_speed_duplex, .port_max_speed_mode = mv88e6390_port_max_speed_mode, .port_tag_remap = mv88e6390_port_tag_remap, .port_set_policy = mv88e6352_port_set_policy, @@ -4078,7 +4179,6 @@ static const struct mv88e6xxx_ops mv88e6290_ops = { .port_pause_limit = mv88e6390_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, - .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6352_port_get_cmode, .port_set_cmode = mv88e6390_port_set_cmode, .port_setup_message_port = mv88e6xxx_setup_message_port, @@ -4100,6 +4200,11 @@ static const struct mv88e6xxx_ops mv88e6290_ops = { .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, .serdes_power = mv88e6390_serdes_power, .serdes_get_lane = mv88e6390_serdes_get_lane, + /* Check status register pause & lpa register */ + .serdes_pcs_get_state = mv88e6390_serdes_pcs_get_state, + .serdes_pcs_config = mv88e6390_serdes_pcs_config, + .serdes_pcs_an_restart = mv88e6390_serdes_pcs_an_restart, + .serdes_pcs_link_up = mv88e6390_serdes_pcs_link_up, .serdes_irq_mapping = mv88e6390_serdes_irq_mapping, .serdes_irq_enable = mv88e6390_serdes_irq_enable, .serdes_irq_status = mv88e6390_serdes_irq_status, @@ -4123,8 +4228,7 @@ static const struct mv88e6xxx_ops mv88e6320_ops = { .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, - .port_set_duplex = mv88e6xxx_port_set_duplex, - .port_set_speed = mv88e6185_port_set_speed, + .port_set_speed_duplex = mv88e6185_port_set_speed_duplex, .port_tag_remap = mv88e6095_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_floods = mv88e6352_port_set_egress_floods, @@ -4134,7 +4238,6 @@ static const struct mv88e6xxx_ops mv88e6320_ops = { .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, - .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6352_port_get_cmode, .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6320_g1_stats_snapshot, @@ -4167,8 +4270,7 @@ static const struct mv88e6xxx_ops mv88e6321_ops = { .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, - .port_set_duplex = mv88e6xxx_port_set_duplex, - .port_set_speed = mv88e6185_port_set_speed, + .port_set_speed_duplex = mv88e6185_port_set_speed_duplex, .port_tag_remap = mv88e6095_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_floods = mv88e6352_port_set_egress_floods, @@ -4178,7 +4280,6 @@ static const struct mv88e6xxx_ops mv88e6321_ops = { .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, - .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6352_port_get_cmode, .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6320_g1_stats_snapshot, @@ -4209,9 +4310,8 @@ static const struct mv88e6xxx_ops mv88e6341_ops = { .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, - .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, - .port_set_speed = mv88e6341_port_set_speed, + .port_set_speed_duplex = mv88e6341_port_set_speed_duplex, .port_max_speed_mode = mv88e6341_port_max_speed_mode, .port_tag_remap = mv88e6095_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, @@ -4222,7 +4322,6 @@ static const struct mv88e6xxx_ops mv88e6341_ops = { .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, - .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6352_port_get_cmode, .port_set_cmode = mv88e6341_port_set_cmode, .port_setup_message_port = mv88e6xxx_setup_message_port, @@ -4241,6 +4340,11 @@ static const struct mv88e6xxx_ops mv88e6341_ops = { .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, .serdes_power = mv88e6390_serdes_power, .serdes_get_lane = mv88e6341_serdes_get_lane, + /* Check status register pause & lpa register */ + .serdes_pcs_get_state = mv88e6390_serdes_pcs_get_state, + .serdes_pcs_config = mv88e6390_serdes_pcs_config, + .serdes_pcs_an_restart = mv88e6390_serdes_pcs_an_restart, + .serdes_pcs_link_up = mv88e6390_serdes_pcs_link_up, .serdes_irq_mapping = mv88e6390_serdes_irq_mapping, .serdes_irq_enable = mv88e6390_serdes_irq_enable, .serdes_irq_status = mv88e6390_serdes_irq_status, @@ -4259,9 +4363,8 @@ static const struct mv88e6xxx_ops mv88e6350_ops = { .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, - .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, - .port_set_speed = mv88e6185_port_set_speed, + .port_set_speed_duplex = mv88e6185_port_set_speed_duplex, .port_tag_remap = mv88e6095_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_floods = mv88e6352_port_set_egress_floods, @@ -4271,7 +4374,6 @@ static const struct mv88e6xxx_ops mv88e6350_ops = { .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, - .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6352_port_get_cmode, .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6320_g1_stats_snapshot, @@ -4301,9 +4403,8 @@ static const struct mv88e6xxx_ops mv88e6351_ops = { .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, - .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, - .port_set_speed = mv88e6185_port_set_speed, + .port_set_speed_duplex = mv88e6185_port_set_speed_duplex, .port_tag_remap = mv88e6095_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_floods = mv88e6352_port_set_egress_floods, @@ -4313,7 +4414,6 @@ static const struct mv88e6xxx_ops mv88e6351_ops = { .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, - .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6352_port_get_cmode, .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6320_g1_stats_snapshot, @@ -4347,9 +4447,8 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, - .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, - .port_set_speed = mv88e6352_port_set_speed, + .port_set_speed_duplex = mv88e6352_port_set_speed_duplex, .port_tag_remap = mv88e6095_port_tag_remap, .port_set_policy = mv88e6352_port_set_policy, .port_set_frame_mode = mv88e6351_port_set_frame_mode, @@ -4360,7 +4459,6 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, - .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6352_port_get_cmode, .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6320_g1_stats_snapshot, @@ -4380,6 +4478,10 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, .serdes_get_lane = mv88e6352_serdes_get_lane, + .serdes_pcs_get_state = mv88e6352_serdes_pcs_get_state, + .serdes_pcs_config = mv88e6352_serdes_pcs_config, + .serdes_pcs_an_restart = mv88e6352_serdes_pcs_an_restart, + .serdes_pcs_link_up = mv88e6352_serdes_pcs_link_up, .serdes_power = mv88e6352_serdes_power, .serdes_irq_mapping = mv88e6352_serdes_irq_mapping, .serdes_irq_enable = mv88e6352_serdes_irq_enable, @@ -4403,9 +4505,8 @@ static const struct mv88e6xxx_ops mv88e6390_ops = { .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, - .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, - .port_set_speed = mv88e6390_port_set_speed, + .port_set_speed_duplex = mv88e6390_port_set_speed_duplex, .port_max_speed_mode = mv88e6390_port_max_speed_mode, .port_tag_remap = mv88e6390_port_tag_remap, .port_set_policy = mv88e6352_port_set_policy, @@ -4417,7 +4518,6 @@ static const struct mv88e6xxx_ops mv88e6390_ops = { .port_pause_limit = mv88e6390_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, - .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6352_port_get_cmode, .port_set_cmode = mv88e6390_port_set_cmode, .port_setup_message_port = mv88e6xxx_setup_message_port, @@ -4439,6 +4539,11 @@ static const struct mv88e6xxx_ops mv88e6390_ops = { .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, .serdes_power = mv88e6390_serdes_power, .serdes_get_lane = mv88e6390_serdes_get_lane, + /* Check status register pause & lpa register */ + .serdes_pcs_get_state = mv88e6390_serdes_pcs_get_state, + .serdes_pcs_config = mv88e6390_serdes_pcs_config, + .serdes_pcs_an_restart = mv88e6390_serdes_pcs_an_restart, + .serdes_pcs_link_up = mv88e6390_serdes_pcs_link_up, .serdes_irq_mapping = mv88e6390_serdes_irq_mapping, .serdes_irq_enable = mv88e6390_serdes_irq_enable, .serdes_irq_status = mv88e6390_serdes_irq_status, @@ -4461,9 +4566,8 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = { .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, - .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, - .port_set_speed = mv88e6390x_port_set_speed, + .port_set_speed_duplex = mv88e6390x_port_set_speed_duplex, .port_max_speed_mode = mv88e6390x_port_max_speed_mode, .port_tag_remap = mv88e6390_port_tag_remap, .port_set_policy = mv88e6352_port_set_policy, @@ -4475,7 +4579,6 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = { .port_pause_limit = mv88e6390_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, - .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6352_port_get_cmode, .port_set_cmode = mv88e6390x_port_set_cmode, .port_setup_message_port = mv88e6xxx_setup_message_port, @@ -4497,6 +4600,10 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = { .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, .serdes_power = mv88e6390_serdes_power, .serdes_get_lane = mv88e6390x_serdes_get_lane, + .serdes_pcs_get_state = mv88e6390_serdes_pcs_get_state, + .serdes_pcs_config = mv88e6390_serdes_pcs_config, + .serdes_pcs_an_restart = mv88e6390_serdes_pcs_an_restart, + .serdes_pcs_link_up = mv88e6390_serdes_pcs_link_up, .serdes_irq_mapping = mv88e6390_serdes_irq_mapping, .serdes_irq_enable = mv88e6390_serdes_irq_enable, .serdes_irq_status = mv88e6390_serdes_irq_status, @@ -5373,8 +5480,9 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = { .setup = mv88e6xxx_setup, .teardown = mv88e6xxx_teardown, .phylink_validate = mv88e6xxx_validate, - .phylink_mac_link_state = mv88e6xxx_link_state, + .phylink_mac_link_state = mv88e6xxx_serdes_pcs_get_state, .phylink_mac_config = mv88e6xxx_mac_config, + .phylink_mac_an_restart = mv88e6xxx_serdes_pcs_an_restart, .phylink_mac_link_down = mv88e6xxx_mac_link_down, .phylink_mac_link_up = mv88e6xxx_mac_link_up, .get_strings = mv88e6xxx_get_strings, diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h index 79cad5e751c6..50c4d1343b0f 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.h +++ b/drivers/net/dsa/mv88e6xxx/chip.h @@ -399,15 +399,6 @@ struct mv88e6xxx_ops { */ int (*port_set_link)(struct mv88e6xxx_chip *chip, int port, int link); -#define DUPLEX_UNFORCED -2 - - /* Port's MAC duplex mode - * - * Use DUPLEX_HALF or DUPLEX_FULL to force half or full duplex, - * or DUPLEX_UNFORCED for normal duplex detection. - */ - int (*port_set_duplex)(struct mv88e6xxx_chip *chip, int port, int dup); - #define PAUSE_ON 1 #define PAUSE_OFF 0 @@ -417,13 +408,18 @@ struct mv88e6xxx_ops { #define SPEED_MAX INT_MAX #define SPEED_UNFORCED -2 +#define DUPLEX_UNFORCED -2 - /* Port's MAC speed (in Mbps) + /* Port's MAC speed (in Mbps) and MAC duplex mode * * Depending on the chip, 10, 100, 200, 1000, 2500, 10000 are valid. * Use SPEED_UNFORCED for normal detection, SPEED_MAX for max value. + * + * Use DUPLEX_HALF or DUPLEX_FULL to force half or full duplex, + * or DUPLEX_UNFORCED for normal duplex detection. */ - int (*port_set_speed)(struct mv88e6xxx_chip *chip, int port, int speed); + int (*port_set_speed_duplex)(struct mv88e6xxx_chip *chip, int port, + int speed, int duplex); /* What interface mode should be used for maximum speed? */ phy_interface_t (*port_max_speed_mode)(int port); @@ -462,9 +458,6 @@ struct mv88e6xxx_ops { */ int (*port_set_upstream_port)(struct mv88e6xxx_chip *chip, int port, int upstream_port); - /* Return the port link state, as required by phylink */ - int (*port_link_state)(struct mv88e6xxx_chip *chip, int port, - struct phylink_link_state *state); /* Snapshot the statistics for a port. The statistics can then * be read back a leisure but still with a consistent view. @@ -502,6 +495,17 @@ struct mv88e6xxx_ops { /* SERDES lane mapping */ u8 (*serdes_get_lane)(struct mv88e6xxx_chip *chip, int port); + int (*serdes_pcs_get_state)(struct mv88e6xxx_chip *chip, int port, + u8 lane, struct phylink_link_state *state); + int (*serdes_pcs_config)(struct mv88e6xxx_chip *chip, int port, + u8 lane, unsigned int mode, + phy_interface_t interface, + const unsigned long *advertise); + int (*serdes_pcs_an_restart)(struct mv88e6xxx_chip *chip, int port, + u8 lane); + int (*serdes_pcs_link_up)(struct mv88e6xxx_chip *chip, int port, + u8 lane, int speed, int duplex); + /* SERDES interrupt handling */ unsigned int (*serdes_irq_mapping)(struct mv88e6xxx_chip *chip, int port); @@ -664,9 +668,6 @@ int mv88e6xxx_wait_mask(struct mv88e6xxx_chip *chip, int addr, int reg, u16 mask, u16 val); int mv88e6xxx_wait_bit(struct mv88e6xxx_chip *chip, int addr, int reg, int bit, int val); -int mv88e6xxx_port_setup_mac(struct mv88e6xxx_chip *chip, int port, int link, - int speed, int duplex, int pause, - phy_interface_t mode); struct mii_bus *mv88e6xxx_default_mdio_bus(struct mv88e6xxx_chip *chip); static inline void mv88e6xxx_reg_lock(struct mv88e6xxx_chip *chip) diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c index 0b43c650e100..8128dc607cf4 100644 --- a/drivers/net/dsa/mv88e6xxx/port.c +++ b/drivers/net/dsa/mv88e6xxx/port.c @@ -162,46 +162,9 @@ int mv88e6xxx_port_set_link(struct mv88e6xxx_chip *chip, int port, int link) return 0; } -int mv88e6xxx_port_set_duplex(struct mv88e6xxx_chip *chip, int port, int dup) -{ - u16 reg; - int err; - - err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_MAC_CTL, ®); - if (err) - return err; - - reg &= ~(MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX | - MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL); - - switch (dup) { - case DUPLEX_HALF: - reg |= MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX; - break; - case DUPLEX_FULL: - reg |= MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX | - MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL; - break; - case DUPLEX_UNFORCED: - /* normal duplex detection */ - break; - default: - return -EOPNOTSUPP; - } - - err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_MAC_CTL, reg); - if (err) - return err; - - dev_dbg(chip->dev, "p%d: %s %s duplex\n", port, - reg & MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX ? "Force" : "Unforce", - reg & MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL ? "full" : "half"); - - return 0; -} - -static int mv88e6xxx_port_set_speed(struct mv88e6xxx_chip *chip, int port, - int speed, bool alt_bit, bool force_bit) +static int mv88e6xxx_port_set_speed_duplex(struct mv88e6xxx_chip *chip, + int port, int speed, bool alt_bit, + bool force_bit, int duplex) { u16 reg, ctrl; int err; @@ -239,11 +202,29 @@ static int mv88e6xxx_port_set_speed(struct mv88e6xxx_chip *chip, int port, return -EOPNOTSUPP; } + switch (duplex) { + case DUPLEX_HALF: + ctrl |= MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX; + break; + case DUPLEX_FULL: + ctrl |= MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX | + MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL; + break; + case DUPLEX_UNFORCED: + /* normal duplex detection */ + break; + default: + return -EOPNOTSUPP; + } + err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_MAC_CTL, ®); if (err) return err; - reg &= ~MV88E6XXX_PORT_MAC_CTL_SPEED_MASK; + reg &= ~(MV88E6XXX_PORT_MAC_CTL_SPEED_MASK | + MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX | + MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL); + if (alt_bit) reg &= ~MV88E6390_PORT_MAC_CTL_ALTSPEED; if (force_bit) { @@ -261,12 +242,16 @@ static int mv88e6xxx_port_set_speed(struct mv88e6xxx_chip *chip, int port, dev_dbg(chip->dev, "p%d: Speed set to %d Mbps\n", port, speed); else dev_dbg(chip->dev, "p%d: Speed unforced\n", port); + dev_dbg(chip->dev, "p%d: %s %s duplex\n", port, + reg & MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX ? "Force" : "Unforce", + reg & MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL ? "full" : "half"); return 0; } /* Support 10, 100, 200 Mbps (e.g. 88E6065 family) */ -int mv88e6065_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) +int mv88e6065_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, + int speed, int duplex) { if (speed == SPEED_MAX) speed = 200; @@ -275,11 +260,13 @@ int mv88e6065_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) return -EOPNOTSUPP; /* Setting 200 Mbps on port 0 to 3 selects 100 Mbps */ - return mv88e6xxx_port_set_speed(chip, port, speed, false, false); + return mv88e6xxx_port_set_speed_duplex(chip, port, speed, false, false, + duplex); } /* Support 10, 100, 1000 Mbps (e.g. 88E6185 family) */ -int mv88e6185_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) +int mv88e6185_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, + int speed, int duplex) { if (speed == SPEED_MAX) speed = 1000; @@ -287,11 +274,13 @@ int mv88e6185_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) if (speed == 200 || speed > 1000) return -EOPNOTSUPP; - return mv88e6xxx_port_set_speed(chip, port, speed, false, false); + return mv88e6xxx_port_set_speed_duplex(chip, port, speed, false, false, + duplex); } /* Support 10, 100 Mbps (e.g. 88E6250 family) */ -int mv88e6250_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) +int mv88e6250_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, + int speed, int duplex) { if (speed == SPEED_MAX) speed = 100; @@ -299,11 +288,13 @@ int mv88e6250_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) if (speed > 100) return -EOPNOTSUPP; - return mv88e6xxx_port_set_speed(chip, port, speed, false, false); + return mv88e6xxx_port_set_speed_duplex(chip, port, speed, false, false, + duplex); } /* Support 10, 100, 200, 1000, 2500 Mbps (e.g. 88E6341) */ -int mv88e6341_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) +int mv88e6341_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, + int speed, int duplex) { if (speed == SPEED_MAX) speed = port < 5 ? 1000 : 2500; @@ -317,7 +308,8 @@ int mv88e6341_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) if (speed == 2500 && port < 5) return -EOPNOTSUPP; - return mv88e6xxx_port_set_speed(chip, port, speed, !port, true); + return mv88e6xxx_port_set_speed_duplex(chip, port, speed, !port, true, + duplex); } phy_interface_t mv88e6341_port_max_speed_mode(int port) @@ -329,7 +321,8 @@ phy_interface_t mv88e6341_port_max_speed_mode(int port) } /* Support 10, 100, 200, 1000 Mbps (e.g. 88E6352 family) */ -int mv88e6352_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) +int mv88e6352_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, + int speed, int duplex) { if (speed == SPEED_MAX) speed = 1000; @@ -340,11 +333,13 @@ int mv88e6352_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) if (speed == 200 && port < 5) return -EOPNOTSUPP; - return mv88e6xxx_port_set_speed(chip, port, speed, true, false); + return mv88e6xxx_port_set_speed_duplex(chip, port, speed, true, false, + duplex); } /* Support 10, 100, 200, 1000, 2500 Mbps (e.g. 88E6390) */ -int mv88e6390_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) +int mv88e6390_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, + int speed, int duplex) { if (speed == SPEED_MAX) speed = port < 9 ? 1000 : 2500; @@ -358,7 +353,8 @@ int mv88e6390_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) if (speed == 2500 && port < 9) return -EOPNOTSUPP; - return mv88e6xxx_port_set_speed(chip, port, speed, true, true); + return mv88e6xxx_port_set_speed_duplex(chip, port, speed, true, true, + duplex); } phy_interface_t mv88e6390_port_max_speed_mode(int port) @@ -370,7 +366,8 @@ phy_interface_t mv88e6390_port_max_speed_mode(int port) } /* Support 10, 100, 200, 1000, 2500, 10000 Mbps (e.g. 88E6190X) */ -int mv88e6390x_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) +int mv88e6390x_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, + int speed, int duplex) { if (speed == SPEED_MAX) speed = port < 9 ? 1000 : 10000; @@ -381,7 +378,8 @@ int mv88e6390x_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) if (speed >= 2500 && port < 9) return -EOPNOTSUPP; - return mv88e6xxx_port_set_speed(chip, port, speed, true, true); + return mv88e6xxx_port_set_speed_duplex(chip, port, speed, true, true, + duplex); } phy_interface_t mv88e6390x_port_max_speed_mode(int port) @@ -586,183 +584,6 @@ int mv88e6352_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode) return 0; } -int mv88e6250_port_link_state(struct mv88e6xxx_chip *chip, int port, - struct phylink_link_state *state) -{ - int err; - u16 reg; - - err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, ®); - if (err) - return err; - - if (port < 5) { - switch (reg & MV88E6250_PORT_STS_PORTMODE_MASK) { - case MV88E6250_PORT_STS_PORTMODE_PHY_10_HALF: - state->speed = SPEED_10; - state->duplex = DUPLEX_HALF; - break; - case MV88E6250_PORT_STS_PORTMODE_PHY_100_HALF: - state->speed = SPEED_100; - state->duplex = DUPLEX_HALF; - break; - case MV88E6250_PORT_STS_PORTMODE_PHY_10_FULL: - state->speed = SPEED_10; - state->duplex = DUPLEX_FULL; - break; - case MV88E6250_PORT_STS_PORTMODE_PHY_100_FULL: - state->speed = SPEED_100; - state->duplex = DUPLEX_FULL; - break; - default: - state->speed = SPEED_UNKNOWN; - state->duplex = DUPLEX_UNKNOWN; - break; - } - } else { - switch (reg & MV88E6250_PORT_STS_PORTMODE_MASK) { - case MV88E6250_PORT_STS_PORTMODE_MII_10_HALF: - state->speed = SPEED_10; - state->duplex = DUPLEX_HALF; - break; - case MV88E6250_PORT_STS_PORTMODE_MII_100_HALF: - state->speed = SPEED_100; - state->duplex = DUPLEX_HALF; - break; - case MV88E6250_PORT_STS_PORTMODE_MII_10_FULL: - state->speed = SPEED_10; - state->duplex = DUPLEX_FULL; - break; - case MV88E6250_PORT_STS_PORTMODE_MII_100_FULL: - state->speed = SPEED_100; - state->duplex = DUPLEX_FULL; - break; - default: - state->speed = SPEED_UNKNOWN; - state->duplex = DUPLEX_UNKNOWN; - break; - } - } - - state->link = !!(reg & MV88E6250_PORT_STS_LINK); - state->an_enabled = 1; - state->an_complete = state->link; - state->interface = PHY_INTERFACE_MODE_NA; - - return 0; -} - -int mv88e6352_port_link_state(struct mv88e6xxx_chip *chip, int port, - struct phylink_link_state *state) -{ - int err; - u16 reg; - - switch (chip->ports[port].cmode) { - case MV88E6XXX_PORT_STS_CMODE_RGMII: - err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_MAC_CTL, - ®); - if (err) - return err; - - if ((reg & MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_RXCLK) && - (reg & MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_TXCLK)) - state->interface = PHY_INTERFACE_MODE_RGMII_ID; - else if (reg & MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_RXCLK) - state->interface = PHY_INTERFACE_MODE_RGMII_RXID; - else if (reg & MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_TXCLK) - state->interface = PHY_INTERFACE_MODE_RGMII_TXID; - else - state->interface = PHY_INTERFACE_MODE_RGMII; - break; - case MV88E6XXX_PORT_STS_CMODE_1000BASEX: - state->interface = PHY_INTERFACE_MODE_1000BASEX; - break; - case MV88E6XXX_PORT_STS_CMODE_SGMII: - state->interface = PHY_INTERFACE_MODE_SGMII; - break; - case MV88E6XXX_PORT_STS_CMODE_2500BASEX: - state->interface = PHY_INTERFACE_MODE_2500BASEX; - break; - case MV88E6XXX_PORT_STS_CMODE_XAUI: - state->interface = PHY_INTERFACE_MODE_XAUI; - break; - case MV88E6XXX_PORT_STS_CMODE_RXAUI: - state->interface = PHY_INTERFACE_MODE_RXAUI; - break; - default: - /* we do not support other cmode values here */ - state->interface = PHY_INTERFACE_MODE_NA; - } - - err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, ®); - if (err) - return err; - - switch (reg & MV88E6XXX_PORT_STS_SPEED_MASK) { - case MV88E6XXX_PORT_STS_SPEED_10: - state->speed = SPEED_10; - break; - case MV88E6XXX_PORT_STS_SPEED_100: - state->speed = SPEED_100; - break; - case MV88E6XXX_PORT_STS_SPEED_1000: - state->speed = SPEED_1000; - break; - case MV88E6XXX_PORT_STS_SPEED_10000: - if ((reg & MV88E6XXX_PORT_STS_CMODE_MASK) == - MV88E6XXX_PORT_STS_CMODE_2500BASEX) - state->speed = SPEED_2500; - else - state->speed = SPEED_10000; - break; - } - - state->duplex = reg & MV88E6XXX_PORT_STS_DUPLEX ? - DUPLEX_FULL : DUPLEX_HALF; - state->link = !!(reg & MV88E6XXX_PORT_STS_LINK); - state->an_enabled = 1; - state->an_complete = state->link; - - return 0; -} - -int mv88e6185_port_link_state(struct mv88e6xxx_chip *chip, int port, - struct phylink_link_state *state) -{ - if (state->interface == PHY_INTERFACE_MODE_1000BASEX) { - u8 cmode = chip->ports[port].cmode; - - /* When a port is in "Cross-chip serdes" mode, it uses - * 1000Base-X full duplex mode, but there is no automatic - * link detection. Use the sync OK status for link (as it - * would do for 1000Base-X mode.) - */ - if (cmode == MV88E6185_PORT_STS_CMODE_SERDES) { - u16 mac; - int err; - - err = mv88e6xxx_port_read(chip, port, - MV88E6XXX_PORT_MAC_CTL, &mac); - if (err) - return err; - - state->link = !!(mac & MV88E6185_PORT_MAC_CTL_SYNC_OK); - state->an_enabled = 1; - state->an_complete = - !!(mac & MV88E6185_PORT_MAC_CTL_AN_DONE); - state->duplex = - state->link ? DUPLEX_FULL : DUPLEX_UNKNOWN; - state->speed = - state->link ? SPEED_1000 : SPEED_UNKNOWN; - - return 0; - } - } - - return mv88e6352_port_link_state(chip, port, state); -} - /* Offset 0x02: Jamming Control * * Do not limit the period of time that this port can be paused for by diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h index 0ec4327c2b42..44d76ac973f6 100644 --- a/drivers/net/dsa/mv88e6xxx/port.h +++ b/drivers/net/dsa/mv88e6xxx/port.h @@ -298,15 +298,20 @@ int mv88e6390_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port, int mv88e6xxx_port_set_link(struct mv88e6xxx_chip *chip, int port, int link); -int mv88e6xxx_port_set_duplex(struct mv88e6xxx_chip *chip, int port, int dup); - -int mv88e6065_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); -int mv88e6185_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); -int mv88e6250_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); -int mv88e6341_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); -int mv88e6352_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); -int mv88e6390_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); -int mv88e6390x_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); +int mv88e6065_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, + int speed, int duplex); +int mv88e6185_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, + int speed, int duplex); +int mv88e6250_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, + int speed, int duplex); +int mv88e6341_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, + int speed, int duplex); +int mv88e6352_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, + int speed, int duplex); +int mv88e6390_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, + int speed, int duplex); +int mv88e6390x_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, + int speed, int duplex); phy_interface_t mv88e6341_port_max_speed_mode(int port); phy_interface_t mv88e6390_port_max_speed_mode(int port); @@ -359,12 +364,6 @@ int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port, phy_interface_t mode); int mv88e6185_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode); int mv88e6352_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode); -int mv88e6185_port_link_state(struct mv88e6xxx_chip *chip, int port, - struct phylink_link_state *state); -int mv88e6250_port_link_state(struct mv88e6xxx_chip *chip, int port, - struct phylink_link_state *state); -int mv88e6352_port_link_state(struct mv88e6xxx_chip *chip, int port, - struct phylink_link_state *state); int mv88e6xxx_port_set_map_da(struct mv88e6xxx_chip *chip, int port); int mv88e6095_port_set_upstream_port(struct mv88e6xxx_chip *chip, int port, int upstream_port); diff --git a/drivers/net/dsa/mv88e6xxx/serdes.c b/drivers/net/dsa/mv88e6xxx/serdes.c index 8d8b3b74aee1..ad9629945f91 100644 --- a/drivers/net/dsa/mv88e6xxx/serdes.c +++ b/drivers/net/dsa/mv88e6xxx/serdes.c @@ -49,6 +49,52 @@ static int mv88e6390_serdes_write(struct mv88e6xxx_chip *chip, return mv88e6xxx_phy_write(chip, lane, reg_c45, val); } +static int mv88e6xxx_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, + u16 status, u16 lpa, + struct phylink_link_state *state) +{ + if (status & MV88E6390_SGMII_PHY_STATUS_SPD_DPL_VALID) { + state->link = !!(status & MV88E6390_SGMII_PHY_STATUS_LINK); + state->duplex = status & + MV88E6390_SGMII_PHY_STATUS_DUPLEX_FULL ? + DUPLEX_FULL : DUPLEX_HALF; + + if (status & MV88E6390_SGMII_PHY_STATUS_TX_PAUSE) + state->pause |= MLO_PAUSE_TX; + if (status & MV88E6390_SGMII_PHY_STATUS_RX_PAUSE) + state->pause |= MLO_PAUSE_RX; + + switch (status & MV88E6390_SGMII_PHY_STATUS_SPEED_MASK) { + case MV88E6390_SGMII_PHY_STATUS_SPEED_1000: + if (state->interface == PHY_INTERFACE_MODE_2500BASEX) + state->speed = SPEED_2500; + else + state->speed = SPEED_1000; + break; + case MV88E6390_SGMII_PHY_STATUS_SPEED_100: + state->speed = SPEED_100; + break; + case MV88E6390_SGMII_PHY_STATUS_SPEED_10: + state->speed = SPEED_10; + break; + default: + dev_err(chip->dev, "invalid PHY speed\n"); + return -EINVAL; + } + } else { + state->link = false; + } + + if (state->interface == PHY_INTERFACE_MODE_2500BASEX) + mii_lpa_mod_linkmode_x(state->lp_advertising, lpa, + ETHTOOL_LINK_MODE_2500baseX_Full_BIT); + else if (state->interface == PHY_INTERFACE_MODE_1000BASEX) + mii_lpa_mod_linkmode_x(state->lp_advertising, lpa, + ETHTOOL_LINK_MODE_1000baseX_Full_BIT); + + return 0; +} + int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, u8 lane, bool up) { @@ -70,6 +116,120 @@ int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, u8 lane, return err; } +int mv88e6352_serdes_pcs_config(struct mv88e6xxx_chip *chip, int port, + u8 lane, unsigned int mode, + phy_interface_t interface, + const unsigned long *advertise) +{ + u16 adv, bmcr, val; + bool changed; + int err; + + switch (interface) { + case PHY_INTERFACE_MODE_SGMII: + adv = 0x0001; + break; + + case PHY_INTERFACE_MODE_1000BASEX: + adv = linkmode_adv_to_mii_adv_x(advertise, + ETHTOOL_LINK_MODE_1000baseX_Full_BIT); + break; + + default: + return 0; + } + + err = mv88e6352_serdes_read(chip, MII_ADVERTISE, &val); + if (err) + return err; + + changed = val != adv; + if (changed) { + err = mv88e6352_serdes_write(chip, MII_ADVERTISE, adv); + if (err) + return err; + } + + err = mv88e6352_serdes_read(chip, MII_BMCR, &val); + if (err) + return err; + + if (phylink_autoneg_inband(mode)) + bmcr = val | BMCR_ANENABLE; + else + bmcr = val & ~BMCR_ANENABLE; + + if (bmcr == val) + return changed; + + return mv88e6352_serdes_write(chip, MII_BMCR, bmcr); +} + +int mv88e6352_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port, + u8 lane, struct phylink_link_state *state) +{ + u16 lpa, status; + int err; + + err = mv88e6352_serdes_read(chip, 0x11, &status); + if (err) { + dev_err(chip->dev, "can't read Serdes PHY status: %d\n", err); + return err; + } + + err = mv88e6352_serdes_read(chip, MII_LPA, &lpa); + if (err) { + dev_err(chip->dev, "can't read Serdes PHY LPA: %d\n", err); + return err; + } + + return mv88e6xxx_serdes_pcs_get_state(chip, status, lpa, state); +} + +int mv88e6352_serdes_pcs_an_restart(struct mv88e6xxx_chip *chip, int port, + u8 lane) +{ + u16 bmcr; + int err; + + err = mv88e6352_serdes_read(chip, MII_BMCR, &bmcr); + if (err) + return err; + + return mv88e6352_serdes_write(chip, MII_BMCR, bmcr | BMCR_ANRESTART); +} + +int mv88e6352_serdes_pcs_link_up(struct mv88e6xxx_chip *chip, int port, + u8 lane, int speed, int duplex) +{ + u16 val, bmcr; + int err; + + err = mv88e6352_serdes_read(chip, MII_BMCR, &val); + if (err) + return err; + + bmcr = val & ~(BMCR_SPEED100 | BMCR_FULLDPLX | BMCR_SPEED1000); + switch (speed) { + case SPEED_1000: + bmcr |= BMCR_SPEED1000; + break; + case SPEED_100: + bmcr |= BMCR_SPEED100; + break; + case SPEED_10: + break; + } + + if (duplex == DUPLEX_FULL) + bmcr |= BMCR_FULLDPLX; + + if (bmcr == val) + return 0; + + return mv88e6352_serdes_write(chip, MII_BMCR, bmcr); +} + u8 mv88e6352_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) { u8 cmode = chip->ports[port].cmode; @@ -180,26 +340,17 @@ int mv88e6352_serdes_get_stats(struct mv88e6xxx_chip *chip, int port, static void mv88e6352_serdes_irq_link(struct mv88e6xxx_chip *chip, int port) { - struct dsa_switch *ds = chip->ds; - u16 status; - bool up; + u16 bmsr; int err; - err = mv88e6352_serdes_read(chip, MII_BMSR, &status); - if (err) - return; - - /* Status must be read twice in order to give the current link - * status. Otherwise the change in link status since the last - * read of the register is returned. - */ - err = mv88e6352_serdes_read(chip, MII_BMSR, &status); - if (err) + /* If the link has dropped, we want to know about it. */ + err = mv88e6352_serdes_read(chip, MII_BMSR, &bmsr); + if (err) { + dev_err(chip->dev, "can't read Serdes BMSR: %d\n", err); return; + } - up = status & BMSR_LSTATUS; - - dsa_port_phylink_mac_change(ds, port, up); + dsa_port_phylink_mac_change(chip->ds, port, !!(bmsr & BMSR_LSTATUS)); } irqreturn_t mv88e6352_serdes_irq_status(struct mv88e6xxx_chip *chip, int port, @@ -387,20 +538,18 @@ static int mv88e6390_serdes_power_sgmii(struct mv88e6xxx_chip *chip, u8 lane, int err; err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, - MV88E6390_SGMII_CONTROL, &val); + MV88E6390_SGMII_BMCR, &val); if (err) return err; if (up) - new_val = val & ~(MV88E6390_SGMII_CONTROL_RESET | - MV88E6390_SGMII_CONTROL_LOOPBACK | - MV88E6390_SGMII_CONTROL_PDOWN); + new_val = val & ~(BMCR_RESET | BMCR_LOOPBACK | BMCR_PDOWN); else - new_val = val | MV88E6390_SGMII_CONTROL_PDOWN; + new_val = val | BMCR_PDOWN; if (val != new_val) err = mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, - MV88E6390_SGMII_CONTROL, new_val); + MV88E6390_SGMII_BMCR, new_val); return err; } @@ -517,71 +666,153 @@ int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, u8 lane, return err; } -static void mv88e6390_serdes_irq_link_sgmii(struct mv88e6xxx_chip *chip, - int port, u8 lane) +int mv88e6390_serdes_pcs_config(struct mv88e6xxx_chip *chip, int port, + u8 lane, unsigned int mode, + phy_interface_t interface, + const unsigned long *advertise) { - u8 cmode = chip->ports[port].cmode; - struct dsa_switch *ds = chip->ds; - int duplex = DUPLEX_UNKNOWN; - int speed = SPEED_UNKNOWN; - phy_interface_t mode; - int link, err; - u16 status; + u16 val, bmcr, adv; + bool changed; + int err; + + switch (interface) { + case PHY_INTERFACE_MODE_SGMII: + adv = 0x0001; + break; + + case PHY_INTERFACE_MODE_1000BASEX: + adv = linkmode_adv_to_mii_adv_x(advertise, + ETHTOOL_LINK_MODE_1000baseX_Full_BIT); + break; + + case PHY_INTERFACE_MODE_2500BASEX: + adv = linkmode_adv_to_mii_adv_x(advertise, + ETHTOOL_LINK_MODE_2500baseX_Full_BIT); + break; + + default: + return 0; + } + + err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, + MV88E6390_SGMII_ADVERTISE, &val); + if (err) + return err; + + changed = val != adv; + if (changed) { + err = mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, + MV88E6390_SGMII_ADVERTISE, adv); + if (err) + return err; + } + + err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, + MV88E6390_SGMII_BMCR, &val); + if (err) + return err; + + if (phylink_autoneg_inband(mode)) + bmcr = val | BMCR_ANENABLE; + else + bmcr = val & ~BMCR_ANENABLE; + + /* setting ANENABLE triggers a restart of negotiation */ + if (bmcr == val) + return changed; + + return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, + MV88E6390_SGMII_BMCR, bmcr); +} + +int mv88e6390_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port, + u8 lane, struct phylink_link_state *state) +{ + u16 lpa, status; + int err; err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, MV88E6390_SGMII_PHY_STATUS, &status); if (err) { - dev_err(chip->dev, "can't read SGMII PHY status: %d\n", err); - return; + dev_err(chip->dev, "can't read Serdes PHY status: %d\n", err); + return err; } - link = status & MV88E6390_SGMII_PHY_STATUS_LINK ? - LINK_FORCED_UP : LINK_FORCED_DOWN; + err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, + MV88E6390_SGMII_LPA, &lpa); + if (err) { + dev_err(chip->dev, "can't read Serdes PHY LPA: %d\n", err); + return err; + } - if (status & MV88E6390_SGMII_PHY_STATUS_SPD_DPL_VALID) { - duplex = status & MV88E6390_SGMII_PHY_STATUS_DUPLEX_FULL ? - DUPLEX_FULL : DUPLEX_HALF; + return mv88e6xxx_serdes_pcs_get_state(chip, status, lpa, state); +} - switch (status & MV88E6390_SGMII_PHY_STATUS_SPEED_MASK) { - case MV88E6390_SGMII_PHY_STATUS_SPEED_1000: - if (cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX) - speed = SPEED_2500; - else - speed = SPEED_1000; - break; - case MV88E6390_SGMII_PHY_STATUS_SPEED_100: - speed = SPEED_100; - break; - case MV88E6390_SGMII_PHY_STATUS_SPEED_10: - speed = SPEED_10; - break; - default: - dev_err(chip->dev, "invalid PHY speed\n"); - return; - } - } +int mv88e6390_serdes_pcs_an_restart(struct mv88e6xxx_chip *chip, int port, + u8 lane) +{ + u16 bmcr; + int err; - switch (cmode) { - case MV88E6XXX_PORT_STS_CMODE_SGMII: - mode = PHY_INTERFACE_MODE_SGMII; + err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, + MV88E6390_SGMII_BMCR, &bmcr); + if (err) + return err; + + return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, + MV88E6390_SGMII_BMCR, + bmcr | BMCR_ANRESTART); +} + +int mv88e6390_serdes_pcs_link_up(struct mv88e6xxx_chip *chip, int port, + u8 lane, int speed, int duplex) +{ + u16 val, bmcr; + int err; + + err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, + MV88E6390_SGMII_BMCR, &val); + if (err) + return err; + + bmcr = val & ~(BMCR_SPEED100 | BMCR_FULLDPLX | BMCR_SPEED1000); + switch (speed) { + case SPEED_2500: + case SPEED_1000: + bmcr |= BMCR_SPEED1000; break; - case MV88E6XXX_PORT_STS_CMODE_1000BASEX: - mode = PHY_INTERFACE_MODE_1000BASEX; + case SPEED_100: + bmcr |= BMCR_SPEED100; break; - case MV88E6XXX_PORT_STS_CMODE_2500BASEX: - mode = PHY_INTERFACE_MODE_2500BASEX; + case SPEED_10: break; - default: - mode = PHY_INTERFACE_MODE_NA; } - err = mv88e6xxx_port_setup_mac(chip, port, link, speed, duplex, - PAUSE_OFF, mode); - if (err) - dev_err(chip->dev, "can't propagate PHY settings to MAC: %d\n", - err); - else - dsa_port_phylink_mac_change(ds, port, link == LINK_FORCED_UP); + if (duplex == DUPLEX_FULL) + bmcr |= BMCR_FULLDPLX; + + if (bmcr == val) + return 0; + + return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, + MV88E6390_SGMII_BMCR, bmcr); +} + +static void mv88e6390_serdes_irq_link_sgmii(struct mv88e6xxx_chip *chip, + int port, u8 lane) +{ + u16 bmsr; + int err; + + /* If the link has dropped, we want to know about it. */ + err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, + MV88E6390_SGMII_BMSR, &bmsr); + if (err) { + dev_err(chip->dev, "can't read Serdes BMSR: %d\n", err); + return; + } + + dsa_port_phylink_mac_change(chip->ds, port, !!(bmsr & BMSR_LSTATUS)); } static int mv88e6390_serdes_irq_enable_sgmii(struct mv88e6xxx_chip *chip, diff --git a/drivers/net/dsa/mv88e6xxx/serdes.h b/drivers/net/dsa/mv88e6xxx/serdes.h index d16ef4da20b0..3365864818cf 100644 --- a/drivers/net/dsa/mv88e6xxx/serdes.h +++ b/drivers/net/dsa/mv88e6xxx/serdes.h @@ -47,14 +47,10 @@ #define MV88E6390_PCS_CONTROL_1_PDOWN BIT(11) /* 1000BASE-X and SGMII */ -#define MV88E6390_SGMII_CONTROL 0x2000 -#define MV88E6390_SGMII_CONTROL_RESET BIT(15) -#define MV88E6390_SGMII_CONTROL_LOOPBACK BIT(14) -#define MV88E6390_SGMII_CONTROL_PDOWN BIT(11) -#define MV88E6390_SGMII_STATUS 0x2001 -#define MV88E6390_SGMII_STATUS_AN_DONE BIT(5) -#define MV88E6390_SGMII_STATUS_REMOTE_FAULT BIT(4) -#define MV88E6390_SGMII_STATUS_LINK BIT(2) +#define MV88E6390_SGMII_BMCR (0x2000 + MII_BMCR) +#define MV88E6390_SGMII_BMSR (0x2000 + MII_BMSR) +#define MV88E6390_SGMII_ADVERTISE (0x2000 + MII_ADVERTISE) +#define MV88E6390_SGMII_LPA (0x2000 + MII_LPA) #define MV88E6390_SGMII_INT_ENABLE 0xa001 #define MV88E6390_SGMII_INT_SPEED_CHANGE BIT(14) #define MV88E6390_SGMII_INT_DUPLEX_CHANGE BIT(13) @@ -73,6 +69,8 @@ #define MV88E6390_SGMII_PHY_STATUS_DUPLEX_FULL BIT(13) #define MV88E6390_SGMII_PHY_STATUS_SPD_DPL_VALID BIT(11) #define MV88E6390_SGMII_PHY_STATUS_LINK BIT(10) +#define MV88E6390_SGMII_PHY_STATUS_TX_PAUSE BIT(3) +#define MV88E6390_SGMII_PHY_STATUS_RX_PAUSE BIT(2) /* Packet generator pad packet checker */ #define MV88E6390_PG_CONTROL 0xf010 @@ -82,6 +80,26 @@ u8 mv88e6341_serdes_get_lane(struct mv88e6xxx_chip *chip, int port); u8 mv88e6352_serdes_get_lane(struct mv88e6xxx_chip *chip, int port); u8 mv88e6390_serdes_get_lane(struct mv88e6xxx_chip *chip, int port); u8 mv88e6390x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port); +int mv88e6352_serdes_pcs_config(struct mv88e6xxx_chip *chip, int port, + u8 lane, unsigned int mode, + phy_interface_t interface, + const unsigned long *advertise); +int mv88e6390_serdes_pcs_config(struct mv88e6xxx_chip *chip, int port, + u8 lane, unsigned int mode, + phy_interface_t interface, + const unsigned long *advertise); +int mv88e6352_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port, + u8 lane, struct phylink_link_state *state); +int mv88e6390_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port, + u8 lane, struct phylink_link_state *state); +int mv88e6352_serdes_pcs_an_restart(struct mv88e6xxx_chip *chip, int port, + u8 lane); +int mv88e6390_serdes_pcs_an_restart(struct mv88e6xxx_chip *chip, int port, + u8 lane); +int mv88e6352_serdes_pcs_link_up(struct mv88e6xxx_chip *chip, int port, + u8 lane, int speed, int duplex); +int mv88e6390_serdes_pcs_link_up(struct mv88e6xxx_chip *chip, int port, + u8 lane, int speed, int duplex); unsigned int mv88e6352_serdes_irq_mapping(struct mv88e6xxx_chip *chip, int port); unsigned int mv88e6390_serdes_irq_mapping(struct mv88e6xxx_chip *chip, diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c index 7edea5741a5f..65925f5f6b84 100644 --- a/drivers/net/dsa/sja1105/sja1105_main.c +++ b/drivers/net/dsa/sja1105/sja1105_main.c @@ -765,15 +765,16 @@ static void sja1105_mac_config(struct dsa_switch *ds, int port, { struct sja1105_private *priv = ds->priv; - if (sja1105_phy_mode_mismatch(priv, port, state->interface)) + if (sja1105_phy_mode_mismatch(priv, port, state->interface)) { + dev_err(ds->dev, "Changing PHY mode to %s not supported!\n", + phy_modes(state->interface)); return; + } if (link_an_mode == MLO_AN_INBAND) { dev_err(ds->dev, "In-band AN not supported!\n"); return; } - - sja1105_adjust_port_config(priv, port, state->speed); } static void sja1105_mac_link_down(struct dsa_switch *ds, int port, @@ -786,9 +787,15 @@ static void sja1105_mac_link_down(struct dsa_switch *ds, int port, static void sja1105_mac_link_up(struct dsa_switch *ds, int port, unsigned int mode, phy_interface_t interface, - struct phy_device *phydev) + struct phy_device *phydev, + int speed, int duplex, + bool tx_pause, bool rx_pause) { - sja1105_inhibit_tx(ds->priv, BIT(port), false); + struct sja1105_private *priv = ds->priv; + + sja1105_adjust_port_config(priv, port, speed); + + sja1105_inhibit_tx(priv, BIT(port), false); } static void sja1105_phylink_validate(struct dsa_switch *ds, int port, diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h index a3f0f27fc79a..ab827fb4b6b9 100644 --- a/drivers/net/ethernet/cadence/macb.h +++ b/drivers/net/ethernet/cadence/macb.h @@ -1200,7 +1200,6 @@ struct macb { unsigned int dma_burst_length; phy_interface_t phy_interface; - int speed; /* AT91RM9200 transmit */ struct sk_buff *skb; /* holds skb until xmit interrupt completes */ diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index 2c28da1737fe..3a7c26b08607 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -571,37 +571,20 @@ static void macb_mac_config(struct phylink_config *config, unsigned int mode, old_ctrl = ctrl = macb_or_gem_readl(bp, NCFGR); - /* Clear all the bits we might set later */ - ctrl &= ~(MACB_BIT(SPD) | MACB_BIT(FD) | MACB_BIT(PAE)); - if (bp->caps & MACB_CAPS_MACB_IS_EMAC) { if (state->interface == PHY_INTERFACE_MODE_RMII) ctrl |= MACB_BIT(RM9200_RMII); } else { - ctrl &= ~(GEM_BIT(GBE) | GEM_BIT(SGMIIEN) | GEM_BIT(PCSSEL)); - - /* We do not support MLO_PAUSE_RX yet */ - if (state->pause & MLO_PAUSE_TX) - ctrl |= MACB_BIT(PAE); + ctrl &= ~(GEM_BIT(SGMIIEN) | GEM_BIT(PCSSEL)); if (state->interface == PHY_INTERFACE_MODE_SGMII) ctrl |= GEM_BIT(SGMIIEN) | GEM_BIT(PCSSEL); } - if (state->speed == SPEED_1000) - ctrl |= GEM_BIT(GBE); - else if (state->speed == SPEED_100) - ctrl |= MACB_BIT(SPD); - - if (state->duplex) - ctrl |= MACB_BIT(FD); - /* Apply the new configuration, if any */ if (old_ctrl ^ ctrl) macb_or_gem_writel(bp, NCFGR, ctrl); - bp->speed = state->speed; - spin_unlock_irqrestore(&bp->lock, flags); } @@ -626,16 +609,42 @@ static void macb_mac_link_down(struct phylink_config *config, unsigned int mode, netif_tx_stop_all_queues(ndev); } -static void macb_mac_link_up(struct phylink_config *config, unsigned int mode, - phy_interface_t interface, struct phy_device *phy) +static void macb_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) { struct net_device *ndev = to_net_dev(config->dev); struct macb *bp = netdev_priv(ndev); struct macb_queue *queue; + unsigned long flags; unsigned int q; + u32 ctrl; + + spin_lock_irqsave(&bp->lock, flags); + + ctrl = macb_or_gem_readl(bp, NCFGR); + + ctrl &= ~(MACB_BIT(SPD) | MACB_BIT(FD)); + + if (speed == SPEED_100) + ctrl |= MACB_BIT(SPD); + + if (duplex) + ctrl |= MACB_BIT(FD); if (!(bp->caps & MACB_CAPS_MACB_IS_EMAC)) { - macb_set_tx_clk(bp->tx_clk, bp->speed, ndev); + ctrl &= ~(GEM_BIT(GBE) | MACB_BIT(PAE)); + + if (speed == SPEED_1000) + ctrl |= GEM_BIT(GBE); + + /* We do not support MLO_PAUSE_RX yet */ + if (tx_pause) + ctrl |= MACB_BIT(PAE); + + macb_set_tx_clk(bp->tx_clk, speed, ndev); /* Initialize rings & buffers as clearing MACB_BIT(TE) in link down * cleared the pipeline and control registers. @@ -648,6 +657,10 @@ static void macb_mac_link_up(struct phylink_config *config, unsigned int mode, bp->rx_intr_mask | MACB_TX_INT_FLAGS | MACB_BIT(HRESP)); } + macb_or_gem_writel(bp, NCFGR, ctrl); + + spin_unlock_irqrestore(&bp->lock, flags); + /* Enable Rx and Tx */ macb_writel(bp, NCR, macb_readl(bp, NCR) | MACB_BIT(RE) | MACB_BIT(TE)); @@ -4429,8 +4442,6 @@ static int macb_probe(struct platform_device *pdev) else bp->phy_interface = interface; - bp->speed = SPEED_UNKNOWN; - /* IP specific init */ err = init(pdev); if (err) diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c index 7ff147e89426..b6c46639aa4c 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c @@ -1704,10 +1704,15 @@ static int dpaa2_eth_ts_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) static int dpaa2_eth_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) { + struct dpaa2_eth_priv *priv = netdev_priv(dev); + if (cmd == SIOCSHWTSTAMP) return dpaa2_eth_ts_ioctl(dev, rq, cmd); - return -EINVAL; + if (priv->mac) + return phylink_mii_ioctl(priv->mac->phylink, rq, cmd); + + return -EOPNOTSUPP; } static bool xdp_mtu_valid(struct dpaa2_eth_priv *priv, int mtu) diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c index 96676abcebd5..94347c695233 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c @@ -79,6 +79,16 @@ static void dpaa2_eth_get_drvinfo(struct net_device *net_dev, sizeof(drvinfo->bus_info)); } +static int dpaa2_eth_nway_reset(struct net_device *net_dev) +{ + struct dpaa2_eth_priv *priv = netdev_priv(net_dev); + + if (priv->mac) + return phylink_ethtool_nway_reset(priv->mac->phylink); + + return -EOPNOTSUPP; +} + static int dpaa2_eth_get_link_ksettings(struct net_device *net_dev, struct ethtool_link_ksettings *link_settings) @@ -761,6 +771,7 @@ static int dpaa2_eth_get_ts_info(struct net_device *dev, const struct ethtool_ops dpaa2_ethtool_ops = { .get_drvinfo = dpaa2_eth_get_drvinfo, + .nway_reset = dpaa2_eth_nway_reset, .get_link = ethtool_op_get_link, .get_link_ksettings = dpaa2_eth_get_link_ksettings, .set_link_ksettings = dpaa2_eth_set_link_ksettings, diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c index 84233e467ed1..3ee236c5fc37 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c @@ -123,49 +123,60 @@ static void dpaa2_mac_config(struct phylink_config *config, unsigned int mode, struct dpmac_link_state *dpmac_state = &mac->state; int err; - if (state->speed != SPEED_UNKNOWN) - dpmac_state->rate = state->speed; - - if (state->duplex != DUPLEX_UNKNOWN) { - if (!state->duplex) - dpmac_state->options |= DPMAC_LINK_OPT_HALF_DUPLEX; - else - dpmac_state->options &= ~DPMAC_LINK_OPT_HALF_DUPLEX; - } - if (state->an_enabled) dpmac_state->options |= DPMAC_LINK_OPT_AUTONEG; else dpmac_state->options &= ~DPMAC_LINK_OPT_AUTONEG; - if (state->pause & MLO_PAUSE_RX) - dpmac_state->options |= DPMAC_LINK_OPT_PAUSE; - else - dpmac_state->options &= ~DPMAC_LINK_OPT_PAUSE; - - if (!!(state->pause & MLO_PAUSE_RX) ^ !!(state->pause & MLO_PAUSE_TX)) - dpmac_state->options |= DPMAC_LINK_OPT_ASYM_PAUSE; - else - dpmac_state->options &= ~DPMAC_LINK_OPT_ASYM_PAUSE; - err = dpmac_set_link_state(mac->mc_io, 0, mac->mc_dev->mc_handle, dpmac_state); if (err) - netdev_err(mac->net_dev, "dpmac_set_link_state() = %d\n", err); + netdev_err(mac->net_dev, "%s: dpmac_set_link_state() = %d\n", + __func__, err); } -static void dpaa2_mac_link_up(struct phylink_config *config, unsigned int mode, - phy_interface_t interface, struct phy_device *phy) +static void dpaa2_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) { struct dpaa2_mac *mac = phylink_to_dpaa2_mac(config); struct dpmac_link_state *dpmac_state = &mac->state; int err; dpmac_state->up = 1; + + if (mac->if_link_type == DPMAC_LINK_TYPE_PHY) { + /* If the DPMAC is configured for PHY mode, we need + * to pass the link parameters to the MC firmware. + */ + dpmac_state->rate = speed; + + if (duplex == DUPLEX_HALF) + dpmac_state->options |= DPMAC_LINK_OPT_HALF_DUPLEX; + else if (duplex == DUPLEX_FULL) + dpmac_state->options &= ~DPMAC_LINK_OPT_HALF_DUPLEX; + + /* This is lossy; the firmware really should take the pause + * enablement status rather than pause/asym pause status. + */ + if (rx_pause) + dpmac_state->options |= DPMAC_LINK_OPT_PAUSE; + else + dpmac_state->options &= ~DPMAC_LINK_OPT_PAUSE; + + if (rx_pause ^ tx_pause) + dpmac_state->options |= DPMAC_LINK_OPT_ASYM_PAUSE; + else + dpmac_state->options &= ~DPMAC_LINK_OPT_ASYM_PAUSE; + } + err = dpmac_set_link_state(mac->mc_io, 0, mac->mc_dev->mc_handle, dpmac_state); if (err) - netdev_err(mac->net_dev, "dpmac_set_link_state() = %d\n", err); + netdev_err(mac->net_dev, "%s: dpmac_set_link_state() = %d\n", + __func__, err); } static void dpaa2_mac_link_down(struct phylink_config *config, @@ -238,6 +249,8 @@ int dpaa2_mac_connect(struct dpaa2_mac *mac) goto err_close_dpmac; } + mac->if_link_type = attr.link_type; + dpmac_node = dpaa2_mac_get_node(attr.id); if (!dpmac_node) { netdev_err(net_dev, "No dpmac@%d node found.\n", attr.id); diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h index 4da8079b9155..2130d9c7d40e 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h @@ -20,6 +20,7 @@ struct dpaa2_mac { struct phylink_config phylink_config; struct phylink *phylink; phy_interface_t if_mode; + enum dpmac_link_type if_link_type; }; bool dpaa2_mac_is_type_fixed(struct fsl_mc_device *dpmac_dev, diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 11babc79dc6c..ae0ee12e724e 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -3766,13 +3766,9 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, new_clk = gmac_clk & ~MVNETA_GMAC_1MS_CLOCK_ENABLE; new_an = gmac_an & ~(MVNETA_GMAC_INBAND_AN_ENABLE | MVNETA_GMAC_INBAND_RESTART_AN | - MVNETA_GMAC_CONFIG_MII_SPEED | - MVNETA_GMAC_CONFIG_GMII_SPEED | MVNETA_GMAC_AN_SPEED_EN | MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL | - MVNETA_GMAC_CONFIG_FLOW_CTRL | MVNETA_GMAC_AN_FLOW_CTRL_EN | - MVNETA_GMAC_CONFIG_FULL_DUPLEX | MVNETA_GMAC_AN_DUPLEX_EN); /* Even though it might look weird, when we're configured in @@ -3787,24 +3783,20 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, if (phylink_test(state->advertising, Pause)) new_an |= MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL; - if (state->pause & MLO_PAUSE_TXRX_MASK) - new_an |= MVNETA_GMAC_CONFIG_FLOW_CTRL; if (!phylink_autoneg_inband(mode)) { - /* Phy or fixed speed */ - if (state->duplex) - new_an |= MVNETA_GMAC_CONFIG_FULL_DUPLEX; - - if (state->speed == SPEED_1000 || state->speed == SPEED_2500) - new_an |= MVNETA_GMAC_CONFIG_GMII_SPEED; - else if (state->speed == SPEED_100) - new_an |= MVNETA_GMAC_CONFIG_MII_SPEED; + /* 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; new_clk |= MVNETA_GMAC_1MS_CLOCK_ENABLE; new_an = (new_an & ~(MVNETA_GMAC_FORCE_LINK_DOWN | - MVNETA_GMAC_FORCE_LINK_PASS)) | + MVNETA_GMAC_FORCE_LINK_PASS | + MVNETA_GMAC_CONFIG_MII_SPEED | + MVNETA_GMAC_CONFIG_GMII_SPEED | + MVNETA_GMAC_CONFIG_FULL_DUPLEX)) | MVNETA_GMAC_INBAND_AN_ENABLE | MVNETA_GMAC_AN_SPEED_EN | MVNETA_GMAC_AN_DUPLEX_EN; @@ -3813,7 +3805,8 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, new_ctrl0 |= MVNETA_GMAC0_PORT_1000BASE_X; new_clk |= MVNETA_GMAC_1MS_CLOCK_ENABLE; new_an = (new_an & ~(MVNETA_GMAC_FORCE_LINK_DOWN | - MVNETA_GMAC_FORCE_LINK_PASS)) | + MVNETA_GMAC_FORCE_LINK_PASS | + MVNETA_GMAC_CONFIG_MII_SPEED)) | MVNETA_GMAC_INBAND_AN_ENABLE | MVNETA_GMAC_CONFIG_GMII_SPEED | /* The MAC only supports FD mode */ @@ -3901,9 +3894,11 @@ static void mvneta_mac_link_down(struct phylink_config *config, mvneta_set_eee(pp, false); } -static void mvneta_mac_link_up(struct phylink_config *config, unsigned int mode, - phy_interface_t interface, - struct phy_device *phy) +static void mvneta_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) { struct net_device *ndev = to_net_dev(config->dev); struct mvneta_port *pp = netdev_priv(ndev); @@ -3911,8 +3906,36 @@ static void mvneta_mac_link_up(struct phylink_config *config, unsigned int mode, if (!phylink_autoneg_inband(mode)) { val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); - val &= ~MVNETA_GMAC_FORCE_LINK_DOWN; + 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; + + if (tx_pause || rx_pause) + val |= MVNETA_GMAC_CONFIG_FLOW_CTRL; + mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); } diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c index 72133cbe55d4..6b9c7ed2547e 100644 --- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c @@ -58,8 +58,11 @@ static struct { */ static void mvpp2_mac_config(struct phylink_config *config, unsigned int mode, const struct phylink_link_state *state); -static void mvpp2_mac_link_up(struct phylink_config *config, unsigned int mode, - phy_interface_t interface, struct phy_device *phy); +static void mvpp2_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); /* Queue modes */ #define MVPP2_QDIST_SINGLE_MODE 0 @@ -3473,8 +3476,9 @@ static void mvpp2_start_dev(struct mvpp2_port *port) .interface = port->phy_interface, }; mvpp2_mac_config(&port->phylink_config, MLO_AN_INBAND, &state); - mvpp2_mac_link_up(&port->phylink_config, MLO_AN_INBAND, - port->phy_interface, NULL); + mvpp2_mac_link_up(&port->phylink_config, NULL, + MLO_AN_INBAND, port->phy_interface, + SPEED_UNKNOWN, DUPLEX_UNKNOWN, false, false); } netif_tx_start_all_queues(port->dev); @@ -4972,15 +4976,13 @@ static void mvpp2_gmac_config(struct mvpp2_port *port, unsigned int mode, old_ctrl2 = ctrl2 = readl(port->base + MVPP2_GMAC_CTRL_2_REG); old_ctrl4 = ctrl4 = readl(port->base + MVPP22_GMAC_CTRL_4_REG); - an &= ~(MVPP2_GMAC_CONFIG_MII_SPEED | MVPP2_GMAC_CONFIG_GMII_SPEED | - MVPP2_GMAC_AN_SPEED_EN | MVPP2_GMAC_FC_ADV_EN | + an &= ~(MVPP2_GMAC_AN_SPEED_EN | MVPP2_GMAC_FC_ADV_EN | MVPP2_GMAC_FC_ADV_ASM_EN | MVPP2_GMAC_FLOW_CTRL_AUTONEG | - MVPP2_GMAC_CONFIG_FULL_DUPLEX | MVPP2_GMAC_AN_DUPLEX_EN | - MVPP2_GMAC_IN_BAND_AUTONEG | MVPP2_GMAC_IN_BAND_AUTONEG_BYPASS); + MVPP2_GMAC_AN_DUPLEX_EN | MVPP2_GMAC_IN_BAND_AUTONEG | + MVPP2_GMAC_IN_BAND_AUTONEG_BYPASS); ctrl0 &= ~MVPP2_GMAC_PORT_TYPE_MASK; ctrl2 &= ~(MVPP2_GMAC_INBAND_AN_MASK | MVPP2_GMAC_PORT_RESET_MASK | MVPP2_GMAC_PCS_ENABLE_MASK); - ctrl4 &= ~(MVPP22_CTRL4_RX_FC_EN | MVPP22_CTRL4_TX_FC_EN); /* Configure port type */ if (phy_interface_mode_is_8023z(state->interface)) { @@ -5010,31 +5012,20 @@ static void mvpp2_gmac_config(struct mvpp2_port *port, unsigned int mode, /* Configure negotiation style */ if (!phylink_autoneg_inband(mode)) { - /* Phy or fixed speed - no in-band AN */ - if (state->duplex) - an |= MVPP2_GMAC_CONFIG_FULL_DUPLEX; - - if (state->speed == SPEED_1000 || state->speed == SPEED_2500) - an |= MVPP2_GMAC_CONFIG_GMII_SPEED; - else if (state->speed == SPEED_100) - an |= MVPP2_GMAC_CONFIG_MII_SPEED; - - if (state->pause & MLO_PAUSE_TX) - ctrl4 |= MVPP22_CTRL4_TX_FC_EN; - if (state->pause & MLO_PAUSE_RX) - ctrl4 |= MVPP22_CTRL4_RX_FC_EN; + /* Phy or fixed speed - no in-band AN, nothing to do, leave the + * configured speed, duplex and flow control as-is. + */ } else if (state->interface == PHY_INTERFACE_MODE_SGMII) { /* SGMII in-band mode receives the speed and duplex from * the PHY. Flow control information is not received. */ - an &= ~(MVPP2_GMAC_FORCE_LINK_DOWN | MVPP2_GMAC_FORCE_LINK_PASS); + an &= ~(MVPP2_GMAC_FORCE_LINK_DOWN | + MVPP2_GMAC_FORCE_LINK_PASS | + MVPP2_GMAC_CONFIG_MII_SPEED | + MVPP2_GMAC_CONFIG_GMII_SPEED | + MVPP2_GMAC_CONFIG_FULL_DUPLEX); an |= MVPP2_GMAC_IN_BAND_AUTONEG | MVPP2_GMAC_AN_SPEED_EN | MVPP2_GMAC_AN_DUPLEX_EN; - - if (state->pause & MLO_PAUSE_TX) - ctrl4 |= MVPP22_CTRL4_TX_FC_EN; - if (state->pause & MLO_PAUSE_RX) - ctrl4 |= MVPP22_CTRL4_RX_FC_EN; } else if (phy_interface_mode_is_8023z(state->interface)) { /* 1000BaseX and 2500BaseX ports cannot negotiate speed nor can * they negotiate duplex: they are always operating with a fixed @@ -5042,19 +5033,17 @@ static void mvpp2_gmac_config(struct mvpp2_port *port, unsigned int mode, * speed and full duplex here. */ ctrl0 |= MVPP2_GMAC_PORT_TYPE_MASK; - an &= ~(MVPP2_GMAC_FORCE_LINK_DOWN | MVPP2_GMAC_FORCE_LINK_PASS); + an &= ~(MVPP2_GMAC_FORCE_LINK_DOWN | + MVPP2_GMAC_FORCE_LINK_PASS | + MVPP2_GMAC_CONFIG_MII_SPEED | + MVPP2_GMAC_CONFIG_GMII_SPEED | + MVPP2_GMAC_CONFIG_FULL_DUPLEX); an |= MVPP2_GMAC_IN_BAND_AUTONEG | MVPP2_GMAC_CONFIG_GMII_SPEED | MVPP2_GMAC_CONFIG_FULL_DUPLEX; - if (state->pause & MLO_PAUSE_AN && state->an_enabled) { + if (state->pause & MLO_PAUSE_AN && state->an_enabled) an |= MVPP2_GMAC_FLOW_CTRL_AUTONEG; - } else { - if (state->pause & MLO_PAUSE_TX) - ctrl4 |= MVPP22_CTRL4_TX_FC_EN; - if (state->pause & MLO_PAUSE_RX) - ctrl4 |= MVPP22_CTRL4_RX_FC_EN; - } } /* Some fields of the auto-negotiation register require the port to be down when @@ -5141,25 +5130,54 @@ static void mvpp2_mac_config(struct phylink_config *config, unsigned int mode, mvpp2_port_enable(port); } -static void mvpp2_mac_link_up(struct phylink_config *config, unsigned int mode, - phy_interface_t interface, struct phy_device *phy) +static void mvpp2_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) { struct net_device *dev = to_net_dev(config->dev); struct mvpp2_port *port = netdev_priv(dev); u32 val; - if (!phylink_autoneg_inband(mode)) { - if (mvpp2_is_xlg(interface)) { + if (mvpp2_is_xlg(interface)) { + if (!phylink_autoneg_inband(mode)) { val = readl(port->base + MVPP22_XLG_CTRL0_REG); val &= ~MVPP22_XLG_CTRL0_FORCE_LINK_DOWN; val |= MVPP22_XLG_CTRL0_FORCE_LINK_PASS; writel(val, port->base + MVPP22_XLG_CTRL0_REG); - } else { + } + } else { + if (!phylink_autoneg_inband(mode)) { val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG); - val &= ~MVPP2_GMAC_FORCE_LINK_DOWN; + val &= ~(MVPP2_GMAC_FORCE_LINK_DOWN | + MVPP2_GMAC_CONFIG_MII_SPEED | + MVPP2_GMAC_CONFIG_GMII_SPEED | + MVPP2_GMAC_CONFIG_FULL_DUPLEX); val |= MVPP2_GMAC_FORCE_LINK_PASS; + + if (speed == SPEED_1000 || speed == SPEED_2500) + val |= MVPP2_GMAC_CONFIG_GMII_SPEED; + else if (speed == SPEED_100) + val |= MVPP2_GMAC_CONFIG_MII_SPEED; + + if (duplex == DUPLEX_FULL) + val |= MVPP2_GMAC_CONFIG_FULL_DUPLEX; + writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG); } + + /* We can always update the flow control enable bits; + * these will only be effective if flow control AN + * (MVPP2_GMAC_FLOW_CTRL_AUTONEG) is disabled. + */ + val = readl(port->base + MVPP22_GMAC_CTRL_4_REG); + val &= ~(MVPP22_CTRL4_RX_FC_EN | MVPP22_CTRL4_TX_FC_EN); + if (tx_pause) + val |= MVPP22_CTRL4_TX_FC_EN; + if (rx_pause) + val |= MVPP22_CTRL4_RX_FC_EN; + writel(val, port->base + MVPP22_GMAC_CTRL_4_REG); } mvpp2_port_enable(port); diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c index 8c6cfd15481c..8d28f90acfe7 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -412,9 +412,10 @@ static void mtk_mac_link_down(struct phylink_config *config, unsigned int mode, mtk_w32(mac->hw, mcr, MTK_MAC_MCR(mac->id)); } -static void mtk_mac_link_up(struct phylink_config *config, unsigned int mode, - phy_interface_t interface, - struct phy_device *phy) +static void mtk_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) { struct mtk_mac *mac = container_of(config, struct mtk_mac, phylink_config); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 7da18c9afa01..d5abdc616b4d 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -950,8 +950,10 @@ static void stmmac_mac_link_down(struct phylink_config *config, } static void stmmac_mac_link_up(struct phylink_config *config, + struct phy_device *phy, unsigned int mode, phy_interface_t interface, - struct phy_device *phy) + int speed, int duplex, + bool tx_pause, bool rx_pause) { struct stmmac_priv *priv = netdev_priv(to_net_dev(config->dev)); diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c index 20746b801959..c2f4c5ca2e80 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c +++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c @@ -1440,6 +1440,22 @@ static void axienet_mac_an_restart(struct phylink_config *config) static void axienet_mac_config(struct phylink_config *config, unsigned int mode, const struct phylink_link_state *state) +{ + /* nothing meaningful to do */ +} + +static void axienet_mac_link_down(struct phylink_config *config, + unsigned int mode, + phy_interface_t interface) +{ + /* nothing meaningful to do */ +} + +static void axienet_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) { struct net_device *ndev = to_net_dev(config->dev); struct axienet_local *lp = netdev_priv(ndev); @@ -1448,7 +1464,7 @@ static void axienet_mac_config(struct phylink_config *config, unsigned int mode, emmc_reg = axienet_ior(lp, XAE_EMMC_OFFSET); emmc_reg &= ~XAE_EMMC_LINKSPEED_MASK; - switch (state->speed) { + switch (speed) { case SPEED_1000: emmc_reg |= XAE_EMMC_LINKSPD_1000; break; @@ -1467,32 +1483,17 @@ static void axienet_mac_config(struct phylink_config *config, unsigned int mode, axienet_iow(lp, XAE_EMMC_OFFSET, emmc_reg); fcc_reg = axienet_ior(lp, XAE_FCC_OFFSET); - if (state->pause & MLO_PAUSE_TX) + if (tx_pause) fcc_reg |= XAE_FCC_FCTX_MASK; else fcc_reg &= ~XAE_FCC_FCTX_MASK; - if (state->pause & MLO_PAUSE_RX) + if (rx_pause) fcc_reg |= XAE_FCC_FCRX_MASK; else fcc_reg &= ~XAE_FCC_FCRX_MASK; axienet_iow(lp, XAE_FCC_OFFSET, fcc_reg); } -static void axienet_mac_link_down(struct phylink_config *config, - unsigned int mode, - phy_interface_t interface) -{ - /* nothing meaningful to do */ -} - -static void axienet_mac_link_up(struct phylink_config *config, - unsigned int mode, - phy_interface_t interface, - struct phy_device *phy) -{ - /* nothing meaningful to do */ -} - static const struct phylink_mac_ops axienet_phylink_ops = { .validate = axienet_validate, .mac_pcs_get_state = axienet_mac_pcs_get_state, diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 9dabe03a668c..edb1cb8a228e 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -326,8 +326,8 @@ config BROADCOM_PHY BCM5481, BCM54810 and BCM5482 PHYs. config BCM84881_PHY - bool "Broadcom BCM84881 PHY" - depends on PHYLIB=y + tristate "Broadcom BCM84881 PHY" + depends on PHYLIB ---help--- Support the Broadcom BCM84881 PHY. diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index fe5badf13b65..d523fd5670e4 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -1,7 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 # Makefile for Linux PHY drivers and MDIO bus drivers -libphy-y := phy.o phy-c45.o phy-core.o phy_device.o +libphy-y := phy.o phy-c45.o phy-core.o phy_device.o \ + linkmode.o mdio-bus-y += mdio_bus.o mdio_device.o ifdef CONFIG_MDIO_DEVICE diff --git a/drivers/net/phy/linkmode.c b/drivers/net/phy/linkmode.c new file mode 100644 index 000000000000..f60560fe3499 --- /dev/null +++ b/drivers/net/phy/linkmode.c @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: GPL-2.0+ +#include + +/** + * linkmode_resolve_pause - resolve the allowable pause modes + * @local_adv: local advertisement in ethtool format + * @partner_adv: partner advertisement in ethtool format + * @tx_pause: pointer to bool to indicate whether transmit pause should be + * enabled. + * @rx_pause: pointer to bool to indicate whether receive pause should be + * enabled. + * + * Flow control is resolved according to our and the link partners + * advertisements using the following drawn from the 802.3 specs: + * Local device Link partner + * Pause AsymDir Pause AsymDir Result + * 0 X 0 X Disabled + * 0 1 1 0 Disabled + * 0 1 1 1 TX + * 1 0 0 X Disabled + * 1 X 1 X TX+RX + * 1 1 0 1 RX + */ +void linkmode_resolve_pause(const unsigned long *local_adv, + const unsigned long *partner_adv, + bool *tx_pause, bool *rx_pause) +{ + __ETHTOOL_DECLARE_LINK_MODE_MASK(m); + + linkmode_and(m, local_adv, partner_adv); + if (linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, m)) { + *tx_pause = true; + *rx_pause = true; + } else if (linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, m)) { + *tx_pause = linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, + partner_adv); + *rx_pause = linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, + local_adv); + } else { + *tx_pause = false; + *rx_pause = false; + } +} +EXPORT_SYMBOL_GPL(linkmode_resolve_pause); + +/** + * linkmode_set_pause - set the pause mode advertisement + * @advertisement: advertisement in ethtool format + * @tx: boolean from ethtool struct ethtool_pauseparam tx_pause member + * @rx: boolean from ethtool struct ethtool_pauseparam rx_pause member + * + * Configure the advertised Pause and Asym_Pause bits according to the + * capabilities of provided in @tx and @rx. + * + * We convert as follows: + * tx rx Pause AsymDir + * 0 0 0 0 + * 0 1 1 1 + * 1 0 0 1 + * 1 1 1 0 + * + * Note: this translation from ethtool tx/rx notation to the advertisement + * is actually very problematical. Here are some examples: + * + * For tx=0 rx=1, meaning transmit is unsupported, receive is supported: + * + * Local device Link partner + * Pause AsymDir Pause AsymDir Result + * 1 1 1 0 TX + RX - but we have no TX support. + * 1 1 0 1 Only this gives RX only + * + * For tx=1 rx=1, meaning we have the capability to transmit and receive + * pause frames: + * + * Local device Link partner + * Pause AsymDir Pause AsymDir Result + * 1 0 0 1 Disabled - but since we do support tx and rx, + * this should resolve to RX only. + * + * Hence, asking for: + * rx=1 tx=0 gives Pause+AsymDir advertisement, but we may end up + * resolving to tx+rx pause or only rx pause depending on + * the partners advertisement. + * rx=0 tx=1 gives AsymDir only, which will only give tx pause if + * the partners advertisement allows it. + * rx=1 tx=1 gives Pause only, which will only allow tx+rx pause + * if the other end also advertises Pause. + */ +void linkmode_set_pause(unsigned long *advertisement, bool tx, bool rx) +{ + linkmode_mod_bit(ETHTOOL_LINK_MODE_Pause_BIT, advertisement, rx); + linkmode_mod_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, advertisement, + rx ^ tx); +} +EXPORT_SYMBOL_GPL(linkmode_set_pause); diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index 9a8badafea8a..81f2ceef70f7 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -1371,6 +1371,10 @@ static int marvell_read_status_page(struct phy_device *phydev, int page) phydev->speed = SPEED_UNKNOWN; phydev->duplex = DUPLEX_UNKNOWN; + linkmode_zero(phydev->lp_advertising); + phydev->pause = 0; + phydev->asym_pause = 0; + if (phydev->autoneg == AUTONEG_ENABLE) err = marvell_read_status_page_an(phydev, fiber, status); else diff --git a/drivers/net/phy/marvell10g.c b/drivers/net/phy/marvell10g.c index 64c9f3bba2cd..7e05b92504f0 100644 --- a/drivers/net/phy/marvell10g.c +++ b/drivers/net/phy/marvell10g.c @@ -23,6 +23,7 @@ * link takes priority and the other port is completely locked out. */ #include +#include #include #include #include @@ -39,10 +40,29 @@ enum { MV_PCS_BASE_R = 0x1000, MV_PCS_1000BASEX = 0x2000, - MV_PCS_PAIRSWAP = 0x8182, - MV_PCS_PAIRSWAP_MASK = 0x0003, - MV_PCS_PAIRSWAP_AB = 0x0002, - MV_PCS_PAIRSWAP_NONE = 0x0003, + MV_PCS_CSCR1 = 0x8000, + MV_PCS_CSCR1_ED_MASK = 0x0300, + MV_PCS_CSCR1_ED_OFF = 0x0000, + MV_PCS_CSCR1_ED_RX = 0x0200, + MV_PCS_CSCR1_ED_NLP = 0x0300, + MV_PCS_CSCR1_MDIX_MASK = 0x0060, + MV_PCS_CSCR1_MDIX_MDI = 0x0000, + MV_PCS_CSCR1_MDIX_MDIX = 0x0020, + MV_PCS_CSCR1_MDIX_AUTO = 0x0060, + + MV_PCS_CSSR1 = 0x8008, + MV_PCS_CSSR1_SPD1_MASK = 0xc000, + MV_PCS_CSSR1_SPD1_SPD2 = 0xc000, + MV_PCS_CSSR1_SPD1_1000 = 0x8000, + MV_PCS_CSSR1_SPD1_100 = 0x4000, + MV_PCS_CSSR1_SPD1_10 = 0x0000, + MV_PCS_CSSR1_DUPLEX_FULL= BIT(13), + MV_PCS_CSSR1_RESOLVED = BIT(11), + MV_PCS_CSSR1_MDIX = BIT(6), + MV_PCS_CSSR1_SPD2_MASK = 0x000c, + MV_PCS_CSSR1_SPD2_5000 = 0x0008, + MV_PCS_CSSR1_SPD2_2500 = 0x0004, + MV_PCS_CSSR1_SPD2_10000 = 0x0000, /* These registers appear at 0x800X and 0xa00X - the 0xa00X control * registers appear to set themselves to the 0x800X when AN is @@ -207,6 +227,91 @@ static int mv3310_hwmon_probe(struct phy_device *phydev) } #endif +static int mv3310_power_down(struct phy_device *phydev) +{ + return phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, MV_V2_PORT_CTRL, + MV_V2_PORT_CTRL_PWRDOWN); +} + +static int mv3310_power_up(struct phy_device *phydev) +{ + return phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2, MV_V2_PORT_CTRL, + MV_V2_PORT_CTRL_PWRDOWN); +} + +static int mv3310_reset(struct phy_device *phydev, u32 unit) +{ + int retries, val, err; + + err = phy_modify_mmd(phydev, MDIO_MMD_PCS, unit + MDIO_CTRL1, + MDIO_CTRL1_RESET, MDIO_CTRL1_RESET); + if (err < 0) + return err; + + retries = 20; + do { + msleep(5); + val = phy_read_mmd(phydev, MDIO_MMD_PCS, unit + MDIO_CTRL1); + if (val < 0) + return val; + } while (val & MDIO_CTRL1_RESET && --retries); + + return val & MDIO_CTRL1_RESET ? -ETIMEDOUT : 0; +} + +static int mv3310_get_edpd(struct phy_device *phydev, u16 *edpd) +{ + int val; + + val = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_PCS_CSCR1); + if (val < 0) + return val; + + switch (val & MV_PCS_CSCR1_ED_MASK) { + case MV_PCS_CSCR1_ED_NLP: + *edpd = 1000; + break; + case MV_PCS_CSCR1_ED_RX: + *edpd = ETHTOOL_PHY_EDPD_NO_TX; + break; + default: + *edpd = ETHTOOL_PHY_EDPD_DISABLE; + break; + } + return 0; +} + +static int mv3310_set_edpd(struct phy_device *phydev, u16 edpd) +{ + u16 val; + int err; + + switch (edpd) { + case 1000: + case ETHTOOL_PHY_EDPD_DFLT_TX_MSECS: + val = MV_PCS_CSCR1_ED_NLP; + break; + + case ETHTOOL_PHY_EDPD_NO_TX: + val = MV_PCS_CSCR1_ED_RX; + break; + + case ETHTOOL_PHY_EDPD_DISABLE: + val = MV_PCS_CSCR1_ED_OFF; + break; + + default: + return -EINVAL; + } + + err = phy_modify_mmd_changed(phydev, MDIO_MMD_PCS, MV_PCS_CSCR1, + MV_PCS_CSCR1_ED_MASK, val); + if (err > 0) + err = mv3310_reset(phydev, MV_PCS_BASE_T); + + return err; +} + static int mv3310_sfp_insert(void *upstream, const struct sfp_eeprom_id *id) { struct phy_device *phydev = upstream; @@ -255,6 +360,11 @@ static int mv3310_probe(struct phy_device *phydev) dev_set_drvdata(&phydev->mdio.dev, priv); + /* Powering down the port when not in use saves about 600mW */ + ret = mv3310_power_down(phydev); + if (ret) + return ret; + ret = mv3310_hwmon_probe(phydev); if (ret) return ret; @@ -264,16 +374,14 @@ static int mv3310_probe(struct phy_device *phydev) static int mv3310_suspend(struct phy_device *phydev) { - return phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, MV_V2_PORT_CTRL, - MV_V2_PORT_CTRL_PWRDOWN); + return mv3310_power_down(phydev); } static int mv3310_resume(struct phy_device *phydev) { int ret; - ret = phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2, MV_V2_PORT_CTRL, - MV_V2_PORT_CTRL_PWRDOWN); + ret = mv3310_power_up(phydev); if (ret) return ret; @@ -299,6 +407,8 @@ static bool mv3310_has_pma_ngbaset_quirk(struct phy_device *phydev) static int mv3310_config_init(struct phy_device *phydev) { + int err; + /* Check that the PHY interface type is compatible */ if (phydev->interface != PHY_INTERFACE_MODE_SGMII && phydev->interface != PHY_INTERFACE_MODE_2500BASEX && @@ -307,7 +417,15 @@ static int mv3310_config_init(struct phy_device *phydev) phydev->interface != PHY_INTERFACE_MODE_10GBASER) return -ENODEV; - return 0; + phydev->mdix_ctrl = ETH_TP_MDI_AUTO; + + /* Power up so reset works */ + err = mv3310_power_up(phydev); + if (err) + return err; + + /* Enable EDPD mode - saving 600mW */ + return mv3310_set_edpd(phydev, ETHTOOL_PHY_EDPD_DFLT_TX_MSECS); } static int mv3310_get_features(struct phy_device *phydev) @@ -336,14 +454,42 @@ static int mv3310_get_features(struct phy_device *phydev) return 0; } +static int mv3310_config_mdix(struct phy_device *phydev) +{ + u16 val; + int err; + + switch (phydev->mdix_ctrl) { + case ETH_TP_MDI_AUTO: + val = MV_PCS_CSCR1_MDIX_AUTO; + break; + case ETH_TP_MDI_X: + val = MV_PCS_CSCR1_MDIX_MDIX; + break; + case ETH_TP_MDI: + val = MV_PCS_CSCR1_MDIX_MDI; + break; + default: + return -EINVAL; + } + + err = phy_modify_mmd_changed(phydev, MDIO_MMD_PCS, MV_PCS_CSCR1, + MV_PCS_CSCR1_MDIX_MASK, val); + if (err > 0) + err = mv3310_reset(phydev, MV_PCS_BASE_T); + + return err; +} + static int mv3310_config_aneg(struct phy_device *phydev) { bool changed = false; u16 reg; int ret; - /* We don't support manual MDI control */ - phydev->mdix_ctrl = ETH_TP_MDI_AUTO; + ret = mv3310_config_mdix(phydev); + if (ret < 0) + return ret; if (phydev->autoneg == AUTONEG_DISABLE) return genphy_c45_pma_setup_forced(phydev); @@ -413,35 +559,18 @@ static void mv3310_update_interface(struct phy_device *phydev) } /* 10GBASE-ER,LR,LRM,SR do not support autonegotiation. */ -static int mv3310_read_10gbr_status(struct phy_device *phydev) +static int mv3310_read_status_10gbaser(struct phy_device *phydev) { phydev->link = 1; phydev->speed = SPEED_10000; phydev->duplex = DUPLEX_FULL; - mv3310_update_interface(phydev); - return 0; } -static int mv3310_read_status(struct phy_device *phydev) +static int mv3310_read_status_copper(struct phy_device *phydev) { - int val; - - phydev->speed = SPEED_UNKNOWN; - phydev->duplex = DUPLEX_UNKNOWN; - linkmode_zero(phydev->lp_advertising); - phydev->link = 0; - phydev->pause = 0; - phydev->asym_pause = 0; - phydev->mdix = 0; - - val = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_PCS_BASE_R + MDIO_STAT1); - if (val < 0) - return val; - - if (val & MDIO_STAT1_LSTATUS) - return mv3310_read_10gbr_status(phydev); + int cssr1, speed, val; val = genphy_c45_read_link(phydev); if (val < 0) @@ -451,6 +580,52 @@ static int mv3310_read_status(struct phy_device *phydev) if (val < 0) return val; + cssr1 = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_PCS_CSSR1); + if (cssr1 < 0) + return val; + + /* If the link settings are not resolved, mark the link down */ + if (!(cssr1 & MV_PCS_CSSR1_RESOLVED)) { + phydev->link = 0; + return 0; + } + + /* Read the copper link settings */ + speed = cssr1 & MV_PCS_CSSR1_SPD1_MASK; + if (speed == MV_PCS_CSSR1_SPD1_SPD2) + speed |= cssr1 & MV_PCS_CSSR1_SPD2_MASK; + + switch (speed) { + case MV_PCS_CSSR1_SPD1_SPD2 | MV_PCS_CSSR1_SPD2_10000: + phydev->speed = SPEED_10000; + break; + + case MV_PCS_CSSR1_SPD1_SPD2 | MV_PCS_CSSR1_SPD2_5000: + phydev->speed = SPEED_5000; + break; + + case MV_PCS_CSSR1_SPD1_SPD2 | MV_PCS_CSSR1_SPD2_2500: + phydev->speed = SPEED_2500; + break; + + case MV_PCS_CSSR1_SPD1_1000: + phydev->speed = SPEED_1000; + break; + + case MV_PCS_CSSR1_SPD1_100: + phydev->speed = SPEED_100; + break; + + case MV_PCS_CSSR1_SPD1_10: + phydev->speed = SPEED_10; + break; + } + + phydev->duplex = cssr1 & MV_PCS_CSSR1_DUPLEX_FULL ? + DUPLEX_FULL : DUPLEX_HALF; + phydev->mdix = cssr1 & MV_PCS_CSSR1_MDIX ? + ETH_TP_MDI_X : ETH_TP_MDI; + if (val & MDIO_AN_STAT1_COMPLETE) { val = genphy_c45_read_lpa(phydev); if (val < 0) @@ -463,43 +638,64 @@ static int mv3310_read_status(struct phy_device *phydev) mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, val); - if (phydev->autoneg == AUTONEG_ENABLE) - phy_resolve_aneg_linkmode(phydev); + /* Update the pause status */ + phy_resolve_aneg_pause(phydev); } - if (phydev->autoneg != AUTONEG_ENABLE) { - val = genphy_c45_read_pma(phydev); - if (val < 0) - return val; - } + return 0; +} - if (phydev->speed == SPEED_10000) { - val = genphy_c45_read_mdix(phydev); - if (val < 0) - return val; - } else { - val = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_PCS_PAIRSWAP); - if (val < 0) - return val; +static int mv3310_read_status(struct phy_device *phydev) +{ + int err, val; - switch (val & MV_PCS_PAIRSWAP_MASK) { - case MV_PCS_PAIRSWAP_AB: - phydev->mdix = ETH_TP_MDI_X; - break; - case MV_PCS_PAIRSWAP_NONE: - phydev->mdix = ETH_TP_MDI; - break; - default: - phydev->mdix = ETH_TP_MDI_INVALID; - break; - } - } + phydev->speed = SPEED_UNKNOWN; + phydev->duplex = DUPLEX_UNKNOWN; + linkmode_zero(phydev->lp_advertising); + phydev->link = 0; + phydev->pause = 0; + phydev->asym_pause = 0; + phydev->mdix = ETH_TP_MDI_INVALID; + + val = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_PCS_BASE_R + MDIO_STAT1); + if (val < 0) + return val; + + if (val & MDIO_STAT1_LSTATUS) + err = mv3310_read_status_10gbaser(phydev); + else + err = mv3310_read_status_copper(phydev); + if (err < 0) + return err; - mv3310_update_interface(phydev); + if (phydev->link) + mv3310_update_interface(phydev); return 0; } +static int mv3310_get_tunable(struct phy_device *phydev, + struct ethtool_tunable *tuna, void *data) +{ + switch (tuna->id) { + case ETHTOOL_PHY_EDPD: + return mv3310_get_edpd(phydev, data); + default: + return -EOPNOTSUPP; + } +} + +static int mv3310_set_tunable(struct phy_device *phydev, + struct ethtool_tunable *tuna, const void *data) +{ + switch (tuna->id) { + case ETHTOOL_PHY_EDPD: + return mv3310_set_edpd(phydev, *(u16 *)data); + default: + return -EOPNOTSUPP; + } +} + static struct phy_driver mv3310_drivers[] = { { .phy_id = MARVELL_PHY_ID_88X3310, @@ -514,6 +710,8 @@ static struct phy_driver mv3310_drivers[] = { .config_aneg = mv3310_config_aneg, .aneg_done = mv3310_aneg_done, .read_status = mv3310_read_status, + .get_tunable = mv3310_get_tunable, + .set_tunable = mv3310_set_tunable, }, { .phy_id = MARVELL_PHY_ID_88E2110, @@ -527,6 +725,8 @@ static struct phy_driver mv3310_drivers[] = { .config_aneg = mv3310_config_aneg, .aneg_done = mv3310_aneg_done, .read_status = mv3310_read_status, + .get_tunable = mv3310_get_tunable, + .set_tunable = mv3310_set_tunable, }, }; diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 9bb9f37f21dc..587eb5663d34 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -807,6 +807,38 @@ int __mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val) } EXPORT_SYMBOL(__mdiobus_write); +/** + * __mdiobus_modify_changed - Unlocked version of the mdiobus_modify function + * @bus: the mii_bus struct + * @addr: the phy address + * @regnum: register number to modify + * @mask: bit mask of bits to clear + * @set: bit mask of bits to set + * + * Read, modify, and if any change, write the register value back to the + * device. Any error returns a negative number. + * + * NOTE: MUST NOT be called from interrupt context. + */ +int __mdiobus_modify_changed(struct mii_bus *bus, int addr, u32 regnum, + u16 mask, u16 set) +{ + int new, ret; + + ret = __mdiobus_read(bus, addr, regnum); + if (ret < 0) + return ret; + + new = (ret & ~mask) | set; + if (new == ret) + return 0; + + ret = __mdiobus_write(bus, addr, regnum, new); + + return ret < 0 ? ret : 1; +} +EXPORT_SYMBOL_GPL(__mdiobus_modify_changed); + /** * mdiobus_read_nested - Nested version of the mdiobus_read function * @bus: the mii_bus struct @@ -911,6 +943,29 @@ int mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val) } EXPORT_SYMBOL(mdiobus_write); +/** + * mdiobus_modify - Convenience function for modifying a given mdio device + * register + * @bus: the mii_bus struct + * @addr: the phy address + * @regnum: register number to write + * @mask: bit mask of bits to clear + * @set: bit mask of bits to set + */ +int mdiobus_modify(struct mii_bus *bus, int addr, u32 regnum, u16 mask, u16 set) +{ + int err; + + BUG_ON(in_interrupt()); + + mutex_lock(&bus->mdio_lock); + err = __mdiobus_modify_changed(bus, addr, regnum, mask, set); + mutex_unlock(&bus->mdio_lock); + + return err < 0 ? err : 0; +} +EXPORT_SYMBOL_GPL(mdiobus_modify); + /** * mdio_bus_match - determine if given MDIO driver supports the given * MDIO device diff --git a/drivers/net/phy/phy-core.c b/drivers/net/phy/phy-core.c index a4d2d59fceca..a2d5220ed6e4 100644 --- a/drivers/net/phy/phy-core.c +++ b/drivers/net/phy/phy-core.c @@ -488,37 +488,6 @@ int phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val) } EXPORT_SYMBOL(phy_write_mmd); -/** - * __phy_modify_changed() - Convenience function for modifying a PHY register - * @phydev: a pointer to a &struct phy_device - * @regnum: register number - * @mask: bit mask of bits to clear - * @set: bit mask of bits to set - * - * Unlocked helper function which allows a PHY register to be modified as - * new register value = (old register value & ~mask) | set - * - * Returns negative errno, 0 if there was no change, and 1 in case of change - */ -int __phy_modify_changed(struct phy_device *phydev, u32 regnum, u16 mask, - u16 set) -{ - int new, ret; - - ret = __phy_read(phydev, regnum); - if (ret < 0) - return ret; - - new = (ret & ~mask) | set; - if (new == ret) - return 0; - - ret = __phy_write(phydev, regnum, new); - - return ret < 0 ? ret : 1; -} -EXPORT_SYMBOL_GPL(__phy_modify_changed); - /** * phy_modify_changed - Function for modifying a PHY register * @phydev: the phy_device struct diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 28e3c5c0e3c3..022fcc8e7b6f 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -2366,22 +2366,7 @@ void phy_set_asym_pause(struct phy_device *phydev, bool rx, bool tx) __ETHTOOL_DECLARE_LINK_MODE_MASK(oldadv); linkmode_copy(oldadv, phydev->advertising); - - linkmode_clear_bit(ETHTOOL_LINK_MODE_Pause_BIT, - phydev->advertising); - linkmode_clear_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, - phydev->advertising); - - if (rx) { - linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT, - phydev->advertising); - linkmode_set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, - phydev->advertising); - } - - if (tx) - linkmode_change_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, - phydev->advertising); + linkmode_set_pause(phydev->advertising, tx, rx); if (!linkmode_equal(oldadv, phydev->advertising) && phydev->autoneg) @@ -2414,6 +2399,32 @@ bool phy_validate_pause(struct phy_device *phydev, } EXPORT_SYMBOL(phy_validate_pause); +/** + * phy_get_pause - resolve negotiated pause modes + * @phydev: phy_device struct + * @tx_pause: pointer to bool to indicate whether transmit pause should be + * enabled. + * @rx_pause: pointer to bool to indicate whether receive pause should be + * enabled. + * + * Resolve and return the flow control modes according to the negotiation + * result. This includes checking that we are operating in full duplex mode. + * See linkmode_resolve_pause() for further details. + */ +void phy_get_pause(struct phy_device *phydev, bool *tx_pause, bool *rx_pause) +{ + if (phydev->duplex != DUPLEX_FULL) { + *tx_pause = false; + *rx_pause = false; + return; + } + + return linkmode_resolve_pause(phydev->advertising, + phydev->lp_advertising, + tx_pause, rx_pause); +} +EXPORT_SYMBOL(phy_get_pause); + static bool phy_drv_supports_irq(struct phy_driver *phydrv) { return phydrv->config_intr && phydrv->ack_interrupt; diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index 6e66b8e77ec7..d830108f7a8e 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -40,7 +40,8 @@ enum { struct phylink { /* private: */ struct net_device *netdev; - const struct phylink_mac_ops *ops; + const struct phylink_mac_ops *mac_ops; + const struct phylink_pcs_ops *pcs_ops; struct phylink_config *config; struct device *dev; unsigned int old_link_state:1; @@ -154,7 +155,7 @@ static const char *phylink_an_mode_str(unsigned int mode) static int phylink_validate(struct phylink *pl, unsigned long *supported, struct phylink_link_state *state) { - pl->ops->validate(pl->config, supported, state); + pl->mac_ops->validate(pl->config, supported, state); return phylink_is_empty_linkmode(supported) ? -EINVAL : 0; } @@ -181,9 +182,11 @@ static int phylink_parse_fixedlink(struct phylink *pl, /* We treat the "pause" and "asym-pause" terminology as * defining the link partner's ability. */ if (fwnode_property_read_bool(fixed_node, "pause")) - pl->link_config.pause |= MLO_PAUSE_SYM; + __set_bit(ETHTOOL_LINK_MODE_Pause_BIT, + pl->link_config.lp_advertising); if (fwnode_property_read_bool(fixed_node, "asym-pause")) - pl->link_config.pause |= MLO_PAUSE_ASYM; + __set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, + pl->link_config.lp_advertising); if (ret == 0) { desc = fwnode_gpiod_get_index(fixed_node, "link", 0, @@ -215,9 +218,11 @@ static int phylink_parse_fixedlink(struct phylink *pl, DUPLEX_FULL : DUPLEX_HALF; pl->link_config.speed = prop[2]; if (prop[3]) - pl->link_config.pause |= MLO_PAUSE_SYM; + __set_bit(ETHTOOL_LINK_MODE_Pause_BIT, + pl->link_config.lp_advertising); if (prop[4]) - pl->link_config.pause |= MLO_PAUSE_ASYM; + __set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, + pl->link_config.lp_advertising); } } @@ -320,6 +325,33 @@ static int phylink_parse_mode(struct phylink *pl, struct fwnode_handle *fwnode) phylink_set(pl->supported, 10000baseER_Full); break; + case PHY_INTERFACE_MODE_XLGMII: + phylink_set(pl->supported, 25000baseCR_Full); + phylink_set(pl->supported, 25000baseKR_Full); + phylink_set(pl->supported, 25000baseSR_Full); + phylink_set(pl->supported, 40000baseKR4_Full); + phylink_set(pl->supported, 40000baseCR4_Full); + phylink_set(pl->supported, 40000baseSR4_Full); + phylink_set(pl->supported, 40000baseLR4_Full); + phylink_set(pl->supported, 50000baseCR2_Full); + phylink_set(pl->supported, 50000baseKR2_Full); + phylink_set(pl->supported, 50000baseSR2_Full); + phylink_set(pl->supported, 50000baseKR_Full); + phylink_set(pl->supported, 50000baseSR_Full); + phylink_set(pl->supported, 50000baseCR_Full); + phylink_set(pl->supported, 50000baseLR_ER_FR_Full); + phylink_set(pl->supported, 50000baseDR_Full); + phylink_set(pl->supported, 100000baseKR4_Full); + phylink_set(pl->supported, 100000baseSR4_Full); + phylink_set(pl->supported, 100000baseCR4_Full); + phylink_set(pl->supported, 100000baseLR4_ER4_Full); + phylink_set(pl->supported, 100000baseKR2_Full); + phylink_set(pl->supported, 100000baseSR2_Full); + phylink_set(pl->supported, 100000baseCR2_Full); + phylink_set(pl->supported, 100000baseLR2_ER2_FR2_Full); + phylink_set(pl->supported, 100000baseDR2_Full); + break; + default: phylink_err(pl, "incorrect link mode %s for in-band status\n", @@ -339,6 +371,34 @@ static int phylink_parse_mode(struct phylink *pl, struct fwnode_handle *fwnode) return 0; } +static void phylink_apply_manual_flow(struct phylink *pl, + struct phylink_link_state *state) +{ + /* If autoneg is disabled, pause AN is also disabled */ + if (!state->an_enabled) + state->pause &= ~MLO_PAUSE_AN; + + /* Manual configuration of pause modes */ + if (!(pl->link_config.pause & MLO_PAUSE_AN)) + state->pause = pl->link_config.pause; +} + +static void phylink_resolve_flow(struct phylink_link_state *state) +{ + bool tx_pause, rx_pause; + + state->pause = MLO_PAUSE_NONE; + if (state->duplex == DUPLEX_FULL) { + linkmode_resolve_pause(state->advertising, + state->lp_advertising, + &tx_pause, &rx_pause); + if (tx_pause) + state->pause |= MLO_PAUSE_TX; + if (rx_pause) + state->pause |= MLO_PAUSE_RX; + } +} + static void phylink_mac_config(struct phylink *pl, const struct phylink_link_state *state) { @@ -351,7 +411,7 @@ static void phylink_mac_config(struct phylink *pl, __ETHTOOL_LINK_MODE_MASK_NBITS, state->advertising, state->pause, state->link, state->an_enabled); - pl->ops->mac_config(pl->config, pl->cur_link_an_mode, state); + pl->mac_ops->mac_config(pl->config, pl->cur_link_an_mode, state); } static void phylink_mac_config_up(struct phylink *pl, @@ -361,11 +421,32 @@ static void phylink_mac_config_up(struct phylink *pl, phylink_mac_config(pl, state); } -static void phylink_mac_an_restart(struct phylink *pl) +static void phylink_mac_pcs_an_restart(struct phylink *pl) { if (pl->link_config.an_enabled && - phy_interface_mode_is_8023z(pl->link_config.interface)) - pl->ops->mac_an_restart(pl->config); + phy_interface_mode_is_8023z(pl->link_config.interface)) { + if (pl->pcs_ops) + pl->pcs_ops->pcs_an_restart(pl->config); + else + pl->mac_ops->mac_an_restart(pl->config); + } +} + +static void phylink_pcs_config(struct phylink *pl, bool force_restart, + const struct phylink_link_state *state) +{ + bool restart = force_restart; + + if (pl->pcs_ops && pl->pcs_ops->pcs_config(pl->config, + pl->cur_link_an_mode, + state->interface, + state->advertising)) + restart = true; + + phylink_mac_config(pl, state); + + if (restart) + phylink_mac_pcs_an_restart(pl); } static void phylink_mac_pcs_get_state(struct phylink *pl, @@ -381,55 +462,54 @@ static void phylink_mac_pcs_get_state(struct phylink *pl, state->an_complete = 0; state->link = 1; - pl->ops->mac_pcs_get_state(pl->config, state); + if (pl->pcs_ops) + pl->pcs_ops->pcs_get_state(pl->config, state); + else + pl->mac_ops->mac_pcs_get_state(pl->config, state); } /* The fixed state is... fixed except for the link state, * which may be determined by a GPIO or a callback. */ -static void phylink_get_fixed_state(struct phylink *pl, struct phylink_link_state *state) +static void phylink_get_fixed_state(struct phylink *pl, + struct phylink_link_state *state) { *state = pl->link_config; if (pl->get_fixed_state) pl->get_fixed_state(pl->netdev, state); else if (pl->link_gpio) state->link = !!gpiod_get_value_cansleep(pl->link_gpio); + + phylink_resolve_flow(state); } -/* Flow control is resolved according to our and the link partners - * advertisements using the following drawn from the 802.3 specs: - * Local device Link partner - * Pause AsymDir Pause AsymDir Result - * 1 X 1 X TX+RX - * 0 1 1 1 TX - * 1 1 0 1 RX - */ -static void phylink_resolve_flow(struct phylink *pl, - struct phylink_link_state *state) +static void phylink_mac_initial_config(struct phylink *pl, bool force_restart) { - int new_pause = 0; + struct phylink_link_state link_state; - if (pl->link_config.pause & MLO_PAUSE_AN) { - int pause = 0; + switch (pl->cur_link_an_mode) { + case MLO_AN_PHY: + link_state = pl->phy_state; + break; - if (phylink_test(pl->link_config.advertising, Pause)) - pause |= MLO_PAUSE_SYM; - if (phylink_test(pl->link_config.advertising, Asym_Pause)) - pause |= MLO_PAUSE_ASYM; + case MLO_AN_FIXED: + phylink_get_fixed_state(pl, &link_state); + break; - pause &= state->pause; + case MLO_AN_INBAND: + link_state = pl->link_config; + if (link_state.interface == PHY_INTERFACE_MODE_SGMII) + link_state.pause = MLO_PAUSE_NONE; + break; - if (pause & MLO_PAUSE_SYM) - new_pause = MLO_PAUSE_TX | MLO_PAUSE_RX; - else if (pause & MLO_PAUSE_ASYM) - new_pause = state->pause & MLO_PAUSE_SYM ? - MLO_PAUSE_TX : MLO_PAUSE_RX; - } else { - new_pause = pl->link_config.pause & MLO_PAUSE_TXRX_MASK; + default: /* can't happen */ + return; } - state->pause &= ~MLO_PAUSE_TXRX_MASK; - state->pause |= new_pause; + link_state.link = false; + + phylink_apply_manual_flow(pl, &link_state); + phylink_pcs_config(pl, force_restart, &link_state); } static const char *phylink_pause_to_str(int pause) @@ -446,14 +526,23 @@ static const char *phylink_pause_to_str(int pause) } } -static void phylink_mac_link_up(struct phylink *pl, - struct phylink_link_state link_state) +static void phylink_link_up(struct phylink *pl, + struct phylink_link_state link_state) { struct net_device *ndev = pl->netdev; pl->cur_interface = link_state.interface; - pl->ops->mac_link_up(pl->config, pl->cur_link_an_mode, - pl->cur_interface, pl->phydev); + + if (pl->pcs_ops && pl->pcs_ops->pcs_link_up) + pl->pcs_ops->pcs_link_up(pl->config, pl->cur_link_an_mode, + pl->cur_interface, + link_state.speed, link_state.duplex); + + pl->mac_ops->mac_link_up(pl->config, pl->phydev, + pl->cur_link_an_mode, pl->cur_interface, + link_state.speed, link_state.duplex, + !!(link_state.pause & MLO_PAUSE_TX), + !!(link_state.pause & MLO_PAUSE_RX)); if (ndev) netif_carrier_on(ndev); @@ -465,14 +554,14 @@ static void phylink_mac_link_up(struct phylink *pl, phylink_pause_to_str(link_state.pause)); } -static void phylink_mac_link_down(struct phylink *pl) +static void phylink_link_down(struct phylink *pl) { struct net_device *ndev = pl->netdev; if (ndev) netif_carrier_off(ndev); - pl->ops->mac_link_down(pl->config, pl->cur_link_an_mode, - pl->cur_interface); + pl->mac_ops->mac_link_down(pl->config, pl->cur_link_an_mode, + pl->cur_interface); phylink_info(pl, "Link is Down\n"); } @@ -493,7 +582,7 @@ static void phylink_resolve(struct work_struct *w) switch (pl->cur_link_an_mode) { case MLO_AN_PHY: link_state = pl->phy_state; - phylink_resolve_flow(pl, &link_state); + phylink_apply_manual_flow(pl, &link_state); phylink_mac_config_up(pl, &link_state); break; @@ -515,10 +604,12 @@ static void phylink_resolve(struct work_struct *w) link_state.interface = pl->phy_state.interface; /* If we have a PHY, we need to update with - * the pause mode bits. */ - link_state.pause |= pl->phy_state.pause; - phylink_resolve_flow(pl, &link_state); + * the PHY flow control bits. */ + link_state.pause = pl->phy_state.pause; + phylink_apply_manual_flow(pl, &link_state); phylink_mac_config(pl, &link_state); + } else { + phylink_apply_manual_flow(pl, &link_state); } break; } @@ -532,9 +623,9 @@ static void phylink_resolve(struct work_struct *w) if (link_changed) { pl->old_link_state = link_state.link; if (!link_state.link) - phylink_mac_link_down(pl); + phylink_link_down(pl); else - phylink_mac_link_up(pl, link_state); + phylink_link_up(pl, link_state); } if (!link_state.link && pl->mac_link_dropped) { pl->mac_link_dropped = false; @@ -601,7 +692,7 @@ static int phylink_register_sfp(struct phylink *pl, * @fwnode: a pointer to a &struct fwnode_handle describing the network * interface * @iface: the desired link mode defined by &typedef phy_interface_t - * @ops: a pointer to a &struct phylink_mac_ops for the MAC. + * @mac_ops: a pointer to a &struct phylink_mac_ops for the MAC. * * Create a new phylink instance, and parse the link parameters found in @np. * This will parse in-band modes, fixed-link or SFP configuration. @@ -614,7 +705,7 @@ static int phylink_register_sfp(struct phylink *pl, struct phylink *phylink_create(struct phylink_config *config, struct fwnode_handle *fwnode, phy_interface_t iface, - const struct phylink_mac_ops *ops) + const struct phylink_mac_ops *mac_ops) { struct phylink *pl; int ret; @@ -647,7 +738,7 @@ struct phylink *phylink_create(struct phylink_config *config, pl->link_config.speed = SPEED_UNKNOWN; pl->link_config.duplex = DUPLEX_UNKNOWN; pl->link_config.an_enabled = true; - pl->ops = ops; + pl->mac_ops = mac_ops; __set_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state); timer_setup(&pl->link_poll, phylink_fixed_poll, 0); @@ -681,6 +772,12 @@ struct phylink *phylink_create(struct phylink_config *config, } EXPORT_SYMBOL_GPL(phylink_create); +void phylink_add_pcs(struct phylink *pl, const struct phylink_pcs_ops *ops) +{ + pl->pcs_ops = ops; +} +EXPORT_SYMBOL_GPL(phylink_add_pcs); + /** * phylink_destroy() - cleanup and destroy the phylink instance * @pl: a pointer to a &struct phylink returned from phylink_create() @@ -705,15 +802,18 @@ static void phylink_phy_change(struct phy_device *phydev, bool up, bool do_carrier) { struct phylink *pl = phydev->phylink; + bool tx_pause, rx_pause; + + phy_get_pause(phydev, &tx_pause, &rx_pause); mutex_lock(&pl->state_mutex); pl->phy_state.speed = phydev->speed; pl->phy_state.duplex = phydev->duplex; pl->phy_state.pause = MLO_PAUSE_NONE; - if (phydev->pause) - pl->phy_state.pause |= MLO_PAUSE_SYM; - if (phydev->asym_pause) - pl->phy_state.pause |= MLO_PAUSE_ASYM; + if (tx_pause) + pl->phy_state.pause |= MLO_PAUSE_TX; + if (rx_pause) + pl->phy_state.pause |= MLO_PAUSE_RX; pl->phy_state.interface = phydev->interface; pl->phy_state.link = up; mutex_unlock(&pl->state_mutex); @@ -783,6 +883,9 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy, mutex_lock(&pl->state_mutex); pl->phydev = phy; pl->phy_state.interface = interface; + pl->phy_state.pause = MLO_PAUSE_NONE; + pl->phy_state.speed = SPEED_UNKNOWN; + pl->phy_state.duplex = DUPLEX_UNKNOWN; linkmode_copy(pl->supported, supported); linkmode_copy(pl->link_config.advertising, config.advertising); @@ -1011,15 +1114,12 @@ void phylink_start(struct phylink *pl) /* Apply the link configuration to the MAC when starting. This allows * a fixed-link to start with the correct parameters, and also * ensures that we set the appropriate advertisement for Serdes links. - */ - phylink_resolve_flow(pl, &pl->link_config); - phylink_mac_config(pl, &pl->link_config); - - /* Restart autonegotiation if using 802.3z to ensure that the link + * + * Restart autonegotiation if using 802.3z to ensure that the link * parameters are properly negotiated. This is necessary for DSA * switches using 802.3z negotiation to ensure they see our modes. */ - phylink_mac_an_restart(pl); + phylink_mac_initial_config(pl, true); clear_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state); phylink_run_resolve(pl); @@ -1316,8 +1416,7 @@ int phylink_ethtool_ksettings_set(struct phylink *pl, * advertisement; the only thing we have is the pause * modes which can only come from a PHY. */ - phylink_mac_config(pl, &pl->link_config); - phylink_mac_an_restart(pl); + phylink_pcs_config(pl, true, &pl->link_config); } mutex_unlock(&pl->state_mutex); } @@ -1345,7 +1444,7 @@ int phylink_ethtool_nway_reset(struct phylink *pl) if (pl->phydev) ret = phy_restart_aneg(pl->phydev); - phylink_mac_an_restart(pl); + phylink_mac_pcs_an_restart(pl); return ret; } @@ -1379,6 +1478,9 @@ int phylink_ethtool_set_pauseparam(struct phylink *pl, ASSERT_RTNL(); + if (pl->cur_link_an_mode == MLO_AN_FIXED) + return -EOPNOTSUPP; + if (!phylink_test(pl->supported, Pause) && !phylink_test(pl->supported, Asym_Pause)) return -EOPNOTSUPP; @@ -1387,8 +1489,8 @@ int phylink_ethtool_set_pauseparam(struct phylink *pl, !pause->autoneg && pause->rx_pause != pause->tx_pause) return -EINVAL; - config->pause &= ~(MLO_PAUSE_AN | MLO_PAUSE_TXRX_MASK); - + mutex_lock(&pl->state_mutex); + config->pause = 0; if (pause->autoneg) config->pause |= MLO_PAUSE_AN; if (pause->rx_pause) @@ -1396,6 +1498,22 @@ int phylink_ethtool_set_pauseparam(struct phylink *pl, if (pause->tx_pause) config->pause |= MLO_PAUSE_TX; + /* + * See the comments for linkmode_set_pause(), wrt the deficiencies + * with the current implementation. A solution to this issue would + * be: + * ethtool Local device + * rx tx Pause AsymDir + * 0 0 0 0 + * 1 0 1 1 + * 0 1 0 1 + * 1 1 1 1 + * and then use the ethtool rx/tx enablement status to mask the + * rx/tx pause resolution. + */ + linkmode_set_pause(config->advertising, pause->tx_pause, + pause->rx_pause); + /* If we have a PHY, phylib will call our link state function if the * mode has changed, which will trigger a resolve and update the MAC * configuration. @@ -1405,19 +1523,9 @@ int phylink_ethtool_set_pauseparam(struct phylink *pl, pause->tx_pause); } else if (!test_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state)) { - switch (pl->cur_link_an_mode) { - case MLO_AN_FIXED: - /* Should we allow fixed links to change against the config? */ - phylink_resolve_flow(pl, config); - phylink_mac_config(pl, config); - break; - - case MLO_AN_INBAND: - phylink_mac_config(pl, config); - phylink_mac_an_restart(pl); - break; - } + phylink_pcs_config(pl, true, &pl->link_config); } + mutex_unlock(&pl->state_mutex); return 0; } @@ -1509,13 +1617,14 @@ static int phylink_mii_emul_read(unsigned int reg, struct phylink_link_state *state) { struct fixed_phy_status fs; + unsigned long *lpa = state->lp_advertising; int val; fs.link = state->link; fs.speed = state->speed; fs.duplex = state->duplex; - fs.pause = state->pause & MLO_PAUSE_SYM; - fs.asym_pause = state->pause & MLO_PAUSE_ASYM; + fs.pause = test_bit(ETHTOOL_LINK_MODE_Pause_BIT, lpa); + fs.asym_pause = test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, lpa); val = swphy_read_reg(reg, &fs); if (reg == MII_BMSR) { @@ -1820,7 +1929,7 @@ static int phylink_sfp_config(struct phylink *pl, u8 mode, if (changed && !test_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state)) - phylink_mac_config(pl, &pl->link_config); + phylink_mac_initial_config(pl, false); return ret; } @@ -1987,4 +2096,241 @@ void phylink_helper_basex_speed(struct phylink_link_state *state) } EXPORT_SYMBOL_GPL(phylink_helper_basex_speed); +static void phylink_decode_c37_word(struct phylink_link_state *state, + uint16_t config_reg, int speed) +{ + bool tx_pause, rx_pause; + int fd_bit; + + if (speed == SPEED_2500) + fd_bit = ETHTOOL_LINK_MODE_2500baseX_Full_BIT; + else + fd_bit = ETHTOOL_LINK_MODE_1000baseX_Full_BIT; + + mii_lpa_mod_linkmode_x(state->lp_advertising, config_reg, fd_bit); + + if (linkmode_test_bit(fd_bit, state->advertising) && + linkmode_test_bit(fd_bit, state->lp_advertising)) { + state->speed = speed; + state->duplex = DUPLEX_FULL; + } else { + /* negotiation failure */ + state->link = false; + } + + linkmode_resolve_pause(state->advertising, state->lp_advertising, + &tx_pause, &rx_pause); + + if (tx_pause) + state->pause |= MLO_PAUSE_TX; + if (rx_pause) + state->pause |= MLO_PAUSE_RX; +} + +static void phylink_decode_sgmii_word(struct phylink_link_state *state, + uint16_t config_reg) +{ + if (!(config_reg & BIT(15))) { + state->link = false; + return; + } + + switch (config_reg & 0x0c00) { + case 0x0000: + state->speed = SPEED_10; + state->duplex = config_reg & 0x1000 ? DUPLEX_FULL : DUPLEX_HALF; + break; + case 0x0400: + state->speed = SPEED_100; + state->duplex = config_reg & 0x1000 ? DUPLEX_FULL : DUPLEX_HALF; + break; + case 0x0800: + state->speed = SPEED_1000; + state->duplex = config_reg & 0x1000 ? DUPLEX_FULL : DUPLEX_HALF; + break; + default: + state->link = false; + break; + } +} + +/** + * phylink_mii_c22_pcs_get_state() - read the MAC PCS state + * @pcs: a pointer to a &struct mdio_device. + * @state: a pointer to a &struct phylink_link_state. + * + * Helper for MAC PCS supporting the 802.3 clause 22 register set for + * clause 37 negotiation and/or SGMII control. + * + * Read the MAC PCS state from the MII device configured in @config and + * parse the Clause 37 or Cisco SGMII link partner negotiation word into + * the phylink @state structure. This is suitable to be directly plugged + * into the mac_pcs_get_state() member of the struct phylink_mac_ops + * structure. + */ +void phylink_mii_c22_pcs_get_state(struct mdio_device *pcs, + struct phylink_link_state *state) +{ + struct mii_bus *bus = pcs->bus; + int addr = pcs->addr; + int bmsr, lpa; + + bmsr = mdiobus_read(bus, addr, MII_BMSR); + lpa = mdiobus_read(bus, addr, MII_LPA); + if (bmsr < 0 || lpa < 0) { + state->link = false; + return; + } + + state->link = !!(bmsr & BMSR_LSTATUS); + state->an_complete = !!(bmsr & BMSR_ANEGCOMPLETE); + if (!state->link) + return; + + switch (state->interface) { + case PHY_INTERFACE_MODE_1000BASEX: + phylink_decode_c37_word(state, lpa, SPEED_1000); + break; + + case PHY_INTERFACE_MODE_2500BASEX: + phylink_decode_c37_word(state, lpa, SPEED_2500); + break; + + case PHY_INTERFACE_MODE_SGMII: + phylink_decode_sgmii_word(state, lpa); + break; + + default: + state->link = false; + break; + } +} +EXPORT_SYMBOL_GPL(phylink_mii_c22_pcs_get_state); + +/** + * phylink_mii_c22_pcs_set_advertisement() - configure the clause 37 PCS + * advertisement + * @pcs: a pointer to a &struct mdio_device. + * @interface: the PHY interface mode being configured + * @advertising: the ethtool advertisement mask + * + * Helper for MAC PCS supporting the 802.3 clause 22 register set for + * clause 37 negotiation and/or SGMII control. + * + * Configure the clause 37 PCS advertisement as specified by @state. This + * does not trigger a renegotiation; phylink will do that via the + * mac_an_restart() method of the struct phylink_mac_ops structure. + * + * Returns negative error code on failure to configure the advertisement, + * zero if no change has been made, or one if the advertisement has changed. + */ +int phylink_mii_c22_pcs_set_advertisement(struct mdio_device *pcs, + phy_interface_t interface, + const unsigned long *advertising) +{ + struct mii_bus *bus = pcs->bus; + int addr = pcs->addr; + int val, ret; + u16 adv; + + switch (interface) { + case PHY_INTERFACE_MODE_1000BASEX: + case PHY_INTERFACE_MODE_2500BASEX: + adv = ADVERTISE_1000XFULL; + if (linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, + advertising)) + adv |= ADVERTISE_1000XPAUSE; + if (linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, + advertising)) + adv |= ADVERTISE_1000XPSE_ASYM; + + val = mdiobus_read(bus, addr, MII_ADVERTISE); + if (val < 0) + return val; + + if (val == adv) + return 0; + + ret = mdiobus_write(bus, addr, MII_ADVERTISE, adv); + if (ret < 0) + return ret; + + return 1; + + case PHY_INTERFACE_MODE_SGMII: + val = mdiobus_read(bus, addr, MII_ADVERTISE); + if (val < 0) + return val; + + if (val == 0x0001) + return 0; + + ret = mdiobus_write(bus, addr, MII_ADVERTISE, 0x0001); + if (ret < 0) + return ret; + + return 1; + + default: + /* Nothing to do for other modes */ + return 0; + } +} +EXPORT_SYMBOL_GPL(phylink_mii_c22_pcs_set_advertisement); + +/** + * phylink_mii_c22_pcs_an_restart() - restart 802.3z autonegotiation + * @pcs: a pointer to a &struct mdio_device. + * + * Helper for MAC PCS supporting the 802.3 clause 22 register set for + * clause 37 negotiation. + * + * Restart the clause 37 negotiation with the link partner. This is + * suitable to be directly plugged into the mac_pcs_get_state() member + * of the struct phylink_mac_ops structure. + */ +void phylink_mii_c22_pcs_an_restart(struct mdio_device *pcs) +{ + struct mii_bus *bus = pcs->bus; + int val, addr = pcs->addr; + + val = mdiobus_read(bus, addr, MII_BMCR); + if (val >= 0) { + val |= BMCR_ANRESTART; + + mdiobus_write(bus, addr, MII_BMCR, val); + } +} +EXPORT_SYMBOL_GPL(phylink_mii_c22_pcs_an_restart); + +#define C45_ADDR(d,a) (MII_ADDR_C45 | (d) << 16 | (a)) +void phylink_mii_c45_pcs_get_state(struct mdio_device *pcs, + struct phylink_link_state *state) +{ + struct mii_bus *bus = pcs->bus; + int addr = pcs->addr; + int stat; + + stat = mdiobus_read(bus, addr, C45_ADDR(MDIO_MMD_PCS, MDIO_STAT1)); + if (stat < 0) { + state->link = false; + return; + } + + state->link = !!(stat & MDIO_STAT1_LSTATUS); + if (!state->link) + return; + + switch (state->interface) { + case PHY_INTERFACE_MODE_10GBASER: + state->speed = SPEED_10000; + state->duplex = DUPLEX_FULL; + break; + + default: + break; + } +} +EXPORT_SYMBOL_GPL(phylink_mii_c45_pcs_get_state); + MODULE_LICENSE("GPL v2"); diff --git a/include/linux/linkmode.h b/include/linux/linkmode.h index fe740031339d..c664c27a29a0 100644 --- a/include/linux/linkmode.h +++ b/include/linux/linkmode.h @@ -71,7 +71,7 @@ static inline void linkmode_change_bit(int nr, volatile unsigned long *addr) __change_bit(nr, addr); } -static inline int linkmode_test_bit(int nr, volatile unsigned long *addr) +static inline int linkmode_test_bit(int nr, const volatile unsigned long *addr) { return test_bit(nr, addr); } @@ -88,4 +88,10 @@ static inline int linkmode_subset(const unsigned long *src1, return bitmap_subset(src1, src2, __ETHTOOL_LINK_MODE_MASK_NBITS); } +void linkmode_resolve_pause(const unsigned long *local_adv, + const unsigned long *partner_adv, + bool *tx_pause, bool *rx_pause); + +void linkmode_set_pause(unsigned long *advertisement, bool tx, bool rx); + #endif /* __LINKMODE_H */ diff --git a/include/linux/mdio.h b/include/linux/mdio.h index a7604248777b..917e4bb2ed71 100644 --- a/include/linux/mdio.h +++ b/include/linux/mdio.h @@ -316,11 +316,15 @@ static inline void mii_10gbt_stat_mod_linkmode_lpa_t(unsigned long *advertising, int __mdiobus_read(struct mii_bus *bus, int addr, u32 regnum); int __mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val); +int __mdiobus_modify_changed(struct mii_bus *bus, int addr, u32 regnum, + u16 mask, u16 set); int mdiobus_read(struct mii_bus *bus, int addr, u32 regnum); int mdiobus_read_nested(struct mii_bus *bus, int addr, u32 regnum); int mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val); int mdiobus_write_nested(struct mii_bus *bus, int addr, u32 regnum, u16 val); +int mdiobus_modify(struct mii_bus *bus, int addr, u32 regnum, u16 mask, + u16 set); int mdiobus_register_device(struct mdio_device *mdiodev); int mdiobus_unregister_device(struct mdio_device *mdiodev); diff --git a/include/linux/mii.h b/include/linux/mii.h index 18c6208f56fc..219b93cad1dd 100644 --- a/include/linux/mii.h +++ b/include/linux/mii.h @@ -354,24 +354,6 @@ static inline u32 mii_adv_to_ethtool_adv_x(u32 adv) return result; } -/** - * mii_lpa_to_ethtool_lpa_x - * @adv: value of the MII_LPA register - * - * A small helper function that translates MII_LPA - * bits, when in 1000Base-X mode, to ethtool - * LP advertisement settings. - */ -static inline u32 mii_lpa_to_ethtool_lpa_x(u32 lpa) -{ - u32 result = 0; - - if (lpa & LPA_LPACK) - result |= ADVERTISED_Autoneg; - - return result | mii_adv_to_ethtool_adv_x(lpa); -} - /** * mii_lpa_mod_linkmode_adv_sgmii * @lp_advertising: pointer to destination link mode. @@ -535,6 +517,45 @@ static inline u32 linkmode_adv_to_lcl_adv_t(unsigned long *advertising) return lcl_adv; } +/** + * mii_lpa_mod_linkmode_x - decode the link partner's config_reg to linkmodes + * @linkmodes: link modes array + * @lpa: config_reg word from link partner + * @fd_bit: link mode for 1000XFULL bit + */ +static inline void mii_lpa_mod_linkmode_x(unsigned long *linkmodes, u16 lpa, + int fd_bit) +{ + linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, linkmodes, + lpa & LPA_LPACK); + linkmode_mod_bit(ETHTOOL_LINK_MODE_Pause_BIT, linkmodes, + lpa & LPA_1000XPAUSE); + linkmode_mod_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, linkmodes, + lpa & LPA_1000XPAUSE_ASYM); + linkmode_mod_bit(fd_bit, linkmodes, + lpa & LPA_1000XFULL); +} + +/** + * linkmode_adv_to_mii_adv_x - encode a linkmode to config_reg + * @linkmodes: linkmodes + * @fd_bit: full duplex bit + */ +static inline u16 linkmode_adv_to_mii_adv_x(const unsigned long *linkmodes, + int fd_bit) +{ + u16 adv = 0; + + if (linkmode_test_bit(fd_bit, linkmodes)) + adv |= ADVERTISE_1000XFULL; + if (linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, linkmodes)) + adv |= ADVERTISE_1000XPAUSE; + if (linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, linkmodes)) + adv |= ADVERTISE_1000XPSE_ASYM; + + return adv; +} + /** * mii_advertise_flowctrl - get flow control advertisement flags * @cap: Flow control capabilities (FLOW_CTRL_RX, FLOW_CTRL_TX or both) diff --git a/include/linux/phy.h b/include/linux/phy.h index 452e8ba8665f..8f73e4ea288c 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -94,6 +94,7 @@ typedef enum { PHY_INTERFACE_MODE_RTBI, PHY_INTERFACE_MODE_SMII, PHY_INTERFACE_MODE_XGMII, + PHY_INTERFACE_MODE_XLGMII, PHY_INTERFACE_MODE_MOCA, PHY_INTERFACE_MODE_QSGMII, PHY_INTERFACE_MODE_TRGMII, @@ -165,6 +166,8 @@ static inline const char *phy_modes(phy_interface_t interface) return "smii"; case PHY_INTERFACE_MODE_XGMII: return "xgmii"; + case PHY_INTERFACE_MODE_XLGMII: + return "xlgmii"; case PHY_INTERFACE_MODE_MOCA: return "moca"; case PHY_INTERFACE_MODE_QSGMII: @@ -749,6 +752,25 @@ static inline int __phy_write(struct phy_device *phydev, u32 regnum, u16 val) val); } +/** + * __phy_modify_changed() - Convenience function for modifying a PHY register + * @phydev: a pointer to a &struct phy_device + * @regnum: register number + * @mask: bit mask of bits to clear + * @set: bit mask of bits to set + * + * Unlocked helper function which allows a PHY register to be modified as + * new register value = (old register value & ~mask) | set + * + * Returns negative errno, 0 if there was no change, and 1 in case of change + */ +static inline int __phy_modify_changed(struct phy_device *phydev, u32 regnum, + u16 mask, u16 set) +{ + return __mdiobus_modify_changed(phydev->mdio.bus, phydev->mdio.addr, + regnum, mask, set); +} + /** * phy_read_mmd - Convenience function for reading a register * from an MMD on a given PHY. @@ -1260,6 +1282,9 @@ void phy_set_sym_pause(struct phy_device *phydev, bool rx, bool tx, void phy_set_asym_pause(struct phy_device *phydev, bool rx, bool tx); bool phy_validate_pause(struct phy_device *phydev, struct ethtool_pauseparam *pp); +void phy_get_pause(struct phy_device *phydev, bool *tx_pause, bool *rx_pause); +void phy_resolve_pause(unsigned long *local_adv, unsigned long *partner_adv, + bool *tx_pause, bool *rx_pause); int phy_register_fixup(const char *bus_id, u32 phy_uid, u32 phy_uid_mask, int (*run)(struct phy_device *)); diff --git a/include/linux/phylink.h b/include/linux/phylink.h index 523209e70947..3f8d37ec5503 100644 --- a/include/linux/phylink.h +++ b/include/linux/phylink.h @@ -12,12 +12,10 @@ struct net_device; enum { MLO_PAUSE_NONE, - MLO_PAUSE_ASYM = BIT(0), - MLO_PAUSE_SYM = BIT(1), - MLO_PAUSE_RX = BIT(2), - MLO_PAUSE_TX = BIT(3), + MLO_PAUSE_RX = BIT(0), + MLO_PAUSE_TX = BIT(1), MLO_PAUSE_TXRX_MASK = MLO_PAUSE_TX | MLO_PAUSE_RX, - MLO_PAUSE_AN = BIT(4), + MLO_PAUSE_AN = BIT(2), MLO_AN_PHY = 0, /* Conventional PHY */ MLO_AN_FIXED, /* Fixed-link mode */ @@ -93,9 +91,10 @@ struct phylink_mac_ops { void (*mac_an_restart)(struct phylink_config *config); void (*mac_link_down)(struct phylink_config *config, unsigned int mode, phy_interface_t interface); - void (*mac_link_up)(struct phylink_config *config, unsigned int mode, - phy_interface_t interface, - struct phy_device *phy); + 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); }; #if 0 /* For kernel-doc purposes only. */ @@ -154,15 +153,33 @@ void mac_pcs_get_state(struct phylink_config *config, * guaranteed to be correct, and so any mac_config() implementation must * never reference these fields. * + * (this requires a rewrite - please refer to mac_link_up() for situations + * where the PCS and MAC are not tightly integrated.) + * + * In all negotiation modes, as defined by @mode, @state->pause indicates the + * pause settings which should be applied as follows. If %MLO_PAUSE_AN is not + * set, %MLO_PAUSE_TX and %MLO_PAUSE_RX indicate whether the MAC should send + * pause frames and/or act on received pause frames respectively. Otherwise, + * the results of in-band negotiation/status from the MAC PCS should be used + * to control the MAC pause mode settings. + * * The action performed depends on the currently selected mode: * * %MLO_AN_FIXED, %MLO_AN_PHY: - * Configure the specified @state->speed, @state->duplex and - * @state->pause (%MLO_PAUSE_TX / %MLO_PAUSE_RX) modes over a link - * specified by @state->interface. @state->advertising may be used, - * but is not required. Other members of @state must be ignored. + * Configure for non-inband negotiation mode, where the link settings + * are completely communicated via mac_link_up(). The physical link + * protocol from the MAC is specified by @state->interface. + * + * @state->advertising may be used, but is not required. + * + * Older drivers (prior to the mac_link_up() change) may use @state->speed, + * @state->duplex and @state->pause to configure the MAC, but this is + * deprecated; such drivers should be converted to use mac_link_up(). * - * Valid state members: interface, speed, duplex, pause, advertising. + * Other members of @state must be ignored. + * + * Valid state members: interface, advertising. + * Deprecated state members: speed, duplex, pause. * * %MLO_AN_INBAND: * place the link in an inband negotiation mode (such as 802.3z @@ -172,11 +189,14 @@ void mac_pcs_get_state(struct phylink_config *config, * mac_pcs_get_state() callback. Changes in link state must be made * by calling phylink_mac_change(). * + * Interface mode specific details are mentioned below. + * * If in 802.3z mode, the link speed is fixed, dependent on the - * @state->interface. Duplex is negotiated, and pause is advertised - * according to @state->an_enabled, @state->pause and - * @state->advertising flags. Beware of MACs which only support full - * duplex at gigabit and higher speeds. + * @state->interface. Duplex and pause modes are negotiated via + * the in-band configuration word. Advertised pause modes are set + * according to the @state->an_enabled and @state->advertising + * flags. Beware of MACs which only support full duplex at gigabit + * and higher speeds. * * If in Cisco SGMII mode, the link speed and duplex mode are passed * in the serial bitstream 16-bit configuration word, and the MAC @@ -220,24 +240,127 @@ void mac_link_down(struct phylink_config *config, unsigned int mode, /** * mac_link_up() - allow the link to come up * @config: a pointer to a &struct phylink_config. + * @phy: any attached phy * @mode: link autonegotiation mode * @interface: link &typedef phy_interface_t mode - * @phy: any attached phy + * @speed: link speed + * @duplex: link duplex + * @tx_pause: link transmit pause enablement status + * @rx_pause: link receive pause enablement status * - * If @mode is not an in-band negotiation mode (as defined by - * phylink_autoneg_inband()), allow the link to come up. If @phy - * is non-%NULL, configure Energy Efficient Ethernet by calling + * Configure the MAC for an established link. + * + * @speed, @duplex, @tx_pause and @rx_pause indicate the finalised link + * settings, and should be used to configure the MAC block appropriately + * where these settings are not automatically conveyed from the PCS block, + * or if in-band negotiation (as defined by phylink_autoneg_inband(@mode)) + * is disabled. + * + * Note that when 802.3z in-band negotiation is in use, it is possible + * that the user wishes to override the pause settings, and this should + * be allowed when considering the implementation of this method. + * + * If in-band negotiation mode is disabled, allow the link to come up. If + * @phy is non-%NULL, configure Energy Efficient Ethernet by calling * phy_init_eee() and perform appropriate MAC configuration for EEE. * Interface type selection must be done in mac_config(). */ -void mac_link_up(struct phylink_config *config, unsigned int mode, - phy_interface_t interface, - struct phy_device *phy); +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); +#endif + +/** + * struct phylink_pcs_ops - MAC PCS operations structure. + * @pcs_get_state: read the current MAC PCS link state from the hardware. + * @pcs_config: configure the MAC PCS for the selected mode and state. + * @pcs_an_restart: restart 802.3z BaseX autonegotiation. + * @pcs_link_up: program the PCS for the resolved link configuration + * (where necessary). + */ +struct phylink_pcs_ops { + void (*pcs_get_state)(struct phylink_config *config, + struct phylink_link_state *state); + int (*pcs_config)(struct phylink_config *config, unsigned int mode, + phy_interface_t interface, + const unsigned long *advertising); + void (*pcs_an_restart)(struct phylink_config *config); + void (*pcs_link_up)(struct phylink_config *config, unsigned int mode, + phy_interface_t interface, int speed, int duplex); +}; + +#if 0 /* For kernel-doc purposes only. */ +/** + * pcs_get_state() - Read the current inband link state from the hardware + * @config: a pointer to a &struct phylink_config. + * @state: a pointer to a &struct phylink_link_state. + * + * Read the current inband link state from the MAC PCS, reporting the + * current speed in @state->speed, duplex mode in @state->duplex, pause + * mode in @state->pause using the %MLO_PAUSE_RX and %MLO_PAUSE_TX bits, + * negotiation completion state in @state->an_complete, and link up state + * in @state->link. If possible, @state->lp_advertising should also be + * populated. + * + * When present, this overrides mac_pcs_get_state() in &struct + * phylink_mac_ops. + */ +void pcs_get_state(struct phylink_config *config, + struct phylink_link_state *state); + +/** + * pcs_config() - Configure the PCS mode and advertisement + * @config: a pointer to a &struct phylink_config. + * @mode: one of %MLO_AN_FIXED, %MLO_AN_PHY, %MLO_AN_INBAND. + * @interface: interface mode to be used + * @advertising: adertisement ethtool link mode mask + * + * Configure the PCS for the operating mode, the interface mode, and set + * the advertisement mask. + * + * When operating in %MLO_AN_INBAND, inband should always be enabled, + * otherwise inband should be disabled. + * + * For SGMII, there is no advertisement from the MAC side, the PCS should + * be programmed to acknowledge the inband word from the PHY. + * + * For 1000BASE-X, the advertisement should be programmed into the PCS. + * + * For most 10GBASE-R, there is no advertisement. + */ +int (*pcs_config)(struct phylink_config *config, unsigned int mode, + phy_interface_t interface, const unsigned long *advertising); + +/** + * pcs_an_restart() - restart 802.3z BaseX autonegotiation + * @config: a pointer to a &struct phylink_config. + * + * When PCS ops are present, this overrides mac_an_restart() in &struct + * phylink_mac_ops. + */ +void (*pcs_an_restart)(struct phylink_config *config); + +/** + * pcs_link_up() - program the PCS for the resolved link configuration + * @config: a pointer to a &struct phylink_config. + * @mode: link autonegotiation mode + * @interface: link &typedef phy_interface_t mode + * @speed: link speed + * @duplex: link duplex + * + * This call will be made just before mac_link_up() to inform the PCS of + * the resolved link parameters. For example, a PCS operating in SGMII + * mode without in-band AN needs to be manually configured for the link + * and duplex setting. Otherwise, this should be a no-op. + */ +void (*pcs_link_up)(struct phylink_config *config, unsigned int mode, + phy_interface_t interface, int speed, int duplex); #endif struct phylink *phylink_create(struct phylink_config *, struct fwnode_handle *, phy_interface_t iface, - const struct phylink_mac_ops *ops); + const struct phylink_mac_ops *mac_ops); +void phylink_add_pcs(struct phylink *, const struct phylink_pcs_ops *ops); void phylink_destroy(struct phylink *); int phylink_connect_phy(struct phylink *, struct phy_device *); @@ -282,4 +405,13 @@ int phylink_mii_ioctl(struct phylink *, struct ifreq *, int); void phylink_set_port_modes(unsigned long *bits); void phylink_helper_basex_speed(struct phylink_link_state *state); +void phylink_mii_c22_pcs_get_state(struct mdio_device *pcs, + struct phylink_link_state *state); +int phylink_mii_c22_pcs_set_advertisement(struct mdio_device *pcs, + phy_interface_t interface, + const unsigned long *advertising); +void phylink_mii_c22_pcs_an_restart(struct mdio_device *pcs); + +void phylink_mii_c45_pcs_get_state(struct mdio_device *pcs, + struct phylink_link_state *state); #endif diff --git a/include/net/dsa.h b/include/net/dsa.h index 63495e3443ac..7d3d84f0ef42 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -420,7 +420,9 @@ struct dsa_switch_ops { void (*phylink_mac_link_up)(struct dsa_switch *ds, int port, unsigned int mode, phy_interface_t interface, - struct phy_device *phydev); + 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/port.c b/net/dsa/port.c index ec13dc666788..a18e65a474a5 100644 --- a/net/dsa/port.c +++ b/net/dsa/port.c @@ -457,6 +457,7 @@ static void dsa_port_phylink_mac_pcs_get_state(struct phylink_config *config, { struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); struct dsa_switch *ds = dp->ds; + int err; /* Only called for inband modes */ if (!ds->ops->phylink_mac_link_state) { @@ -464,8 +465,12 @@ static void dsa_port_phylink_mac_pcs_get_state(struct phylink_config *config, return; } - if (ds->ops->phylink_mac_link_state(ds, dp->index, state) < 0) + err = ds->ops->phylink_mac_link_state(ds, dp->index, state); + if (err < 0) { + dev_err(ds->dev, "p%d: phylink_mac_link_state() failed: %d\n", + dp->index, err); state->link = 0; + } } static void dsa_port_phylink_mac_config(struct phylink_config *config, @@ -513,9 +518,11 @@ static void dsa_port_phylink_mac_link_down(struct phylink_config *config, } static void dsa_port_phylink_mac_link_up(struct phylink_config *config, + struct phy_device *phydev, unsigned int mode, phy_interface_t interface, - struct phy_device *phydev) + int speed, int duplex, + bool tx_pause, bool rx_pause) { struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); struct dsa_switch *ds = dp->ds; @@ -526,7 +533,8 @@ static void dsa_port_phylink_mac_link_up(struct phylink_config *config, return; } - ds->ops->phylink_mac_link_up(ds, dp->index, mode, interface, phydev); + ds->ops->phylink_mac_link_up(ds, dp->index, mode, interface, phydev, + speed, duplex, tx_pause, rx_pause); } const struct phylink_mac_ops dsa_port_phylink_mac_ops = { diff --git a/net/switchdev/switchdev.c b/net/switchdev/switchdev.c index 3a1d428c1336..d881e5e4a889 100644 --- a/net/switchdev/switchdev.c +++ b/net/switchdev/switchdev.c @@ -475,6 +475,9 @@ static int __switchdev_handle_port_obj_add(struct net_device *dev, * necessary to go through this helper. */ netdev_for_each_lower_dev(dev, lower_dev, iter) { + if (netif_is_bridge_master(lower_dev)) + continue; + err = __switchdev_handle_port_obj_add(lower_dev, port_obj_info, check_cb, add_cb); if (err && err != -EOPNOTSUPP) @@ -526,6 +529,9 @@ static int __switchdev_handle_port_obj_del(struct net_device *dev, * necessary to go through this helper. */ netdev_for_each_lower_dev(dev, lower_dev, iter) { + if (netif_is_bridge_master(lower_dev)) + continue; + err = __switchdev_handle_port_obj_del(lower_dev, port_obj_info, check_cb, del_cb); if (err && err != -EOPNOTSUPP) @@ -576,6 +582,9 @@ static int __switchdev_handle_port_attr_set(struct net_device *dev, * necessary to go through this helper. */ netdev_for_each_lower_dev(dev, lower_dev, iter) { + if (netif_is_bridge_master(lower_dev)) + continue; + err = __switchdev_handle_port_attr_set(lower_dev, port_attr_info, check_cb, set_cb); if (err && err != -EOPNOTSUPP)