# Base git commit: 219d54332a09 # (Linux 5.4) # # Author: Russell King (Tue 3 Dec 15:22:05 GMT 2019) # Committer: Russell King (Mon 9 Dec 09:06:45 GMT 2019) # # net: sfp: avoid tx-fault with Nokia GPON module # # The Nokia GPON module can hold tx-fault active while it is initialising # which can take up to 60s. Avoid this causing the module to be declared # faulty after the SFP MSA defined non-cooled module timeout. # # Signed-off-by: Russell King # # 2cd7584b84b6e2732e9b87805f38b89b7b633cbf # drivers/net/phy/sfp.c | 42 ++++++++++++++++++++++++++++++------------ # 1 file changed, 30 insertions(+), 12 deletions(-) # # Author: Russell King (Wed 5 Jun 11:44:55 BST 2019) # Committer: Russell King (Mon 9 Dec 09:06:44 GMT 2019) # # net: phy: marvell10g: allow PHY to probe without firmware # # Allow the PHY to probe when there is no firmware, but do not allow the # link to come up by forcing the PHY state to PHY_HALTED in a similar way # to phy_error(). # # Signed-off-by: Russell King # # 4246d7d40f52dd7eda0f946204dbfd6ea79dc5a4 # drivers/net/phy/marvell10g.c | 27 ++++++++++++++++++++++----- # 1 file changed, 22 insertions(+), 5 deletions(-) # # Author: Russell King (Mon 26 Aug 12:19:35 BST 2019) # Committer: Russell King (Mon 9 Dec 09:06:43 GMT 2019) # # net: phy: provide phy driver start/stop hooks # # Provide phy driver start/stop hooks so that the PHY driver knows when # the network driver is starting or stopping. This will be used for the # Marvell 10G driver so that we can sanely refuse to start if the PHYs # firmware is not present, and also so that we can sanely support SFPs # behind the PHY. # # Signed-off-by: Russell King # # 0cadc562fee3eacedf9c3775b03d9aa5e355d3ea # drivers/net/phy/phy.c | 5 +++++ # include/linux/phy.h | 3 +++ # 2 files changed, 8 insertions(+) # # Author: Russell King (Tue 3 Dec 18:46:04 GMT 2019) # Committer: Russell King (Sun 8 Dec 14:41:41 GMT 2019) # # net: sfp: fix hwmon # # The referenced commit below allowed more than one hwmon device to be # created per SFP, which is definitely not what we want. Avoid this by # only creating the hwmon device just as we transition to WAITDEV state. # # Fixes: 139d3a212a1f ("net: sfp: allow modules with slow diagnostics to probe") # Signed-off-by: Russell King # # bef73d1fb67ab4dba8c5a714db578b9082820305 # drivers/net/phy/sfp.c | 13 ++++--------- # 1 file changed, 4 insertions(+), 9 deletions(-) # # Author: Russell King (Tue 3 Dec 17:48:28 GMT 2019) # Committer: Russell King (Sun 8 Dec 14:41:40 GMT 2019) # # net: sfp: fix unbind # # When unbinding, we don't correctly tear down the module state, leaving # (for example) the hwmon registration behind. Ensure everything is # properly removed by sending a remove event at unbind. # # Fixes: 6b0da5c9c1a3 ("net: sfp: track upstream's attachment state in state machine") # Signed-off-by: Russell King # # 2abaedbd25b65f02ef233712baa3fb2bb69fae01 # drivers/net/phy/sfp.c | 4 ++++ # 1 file changed, 4 insertions(+) # # Author: Russell King (Fri 22 Nov 11:49:40 GMT 2019) # Committer: Russell King (Sun 8 Dec 14:41:39 GMT 2019) # # net: phy: initialise phydev speed and duplex sanely # # When a phydev is created, the speed and duplex are set to zero and # -1 respectively, rather than using the predefined SPEED_UNKNOWN and # DUPLEX_UNKNOWN constants. # # There is a window at initialisation time where we may report link # down using the 0/-1 values. Tidy this up and use the predefined # constants, so debug doesn't complain with: # # "Unsupported (update phy-core.c)/Unsupported (update phy-core.c)" # # when the speed and duplex settings are printed. # # Signed-off-by: Russell King # # dd43157174377f277bb56abdd010df1d0507c8f4 # drivers/net/phy/phy_device.c | 4 ++-- # 1 file changed, 2 insertions(+), 2 deletions(-) # # Author: Russell King (Fri 22 Nov 12:11:45 GMT 2019) # Committer: Russell King (Sun 8 Dec 14:41:38 GMT 2019) # # net: phy: remove phy_ethtool_sset() # # There are no users of phy_ethtool_sset() in the kernel anymore, and # as of 3c1bcc8614db ("net: ethernet: Convert phydev advertize and # supported from u32 to link mode"), the implementation is slightly # buggy - it doesn't correctly check the masked advertising mask as it # used to. # # Remove it, and update the phy documentation to refer to its replacement # function. # # Signed-off-by: Russell King # # 8bd4cd88362239081e62f9080429254f2dfaccf7 # Documentation/networking/phy.rst | 3 +- # drivers/net/phy/phy.c | 60 ---------------------------------------- # include/linux/phy.h | 1 - # 3 files changed, 2 insertions(+), 62 deletions(-) # # Author: Russell King (Fri 13 Sep 23:00:35 BST 2019) # Committer: Russell King (Sun 8 Dec 14:41:37 GMT 2019) # # net: sfp: soft status and control support # # Add support for the soft status and control register, which allows # TX_FAULT and RX_LOS to be monitored and TX_DISABLE to be set. We # make use of this when the board does not support GPIOs for these # signals. # # Signed-off-by: Russell King # # ff685fedf94661166ddb0899f925ed283f6c22c5 # drivers/net/phy/sfp.c | 110 +++++++++++++++++++++++++++++++++++++++++--------- # include/linux/sfp.h | 4 ++ # 2 files changed, 94 insertions(+), 20 deletions(-) # # Author: Russell King (Fri 17 May 10:14:45 BST 2019) # Committer: Russell King (Sun 8 Dec 14:41:37 GMT 2019) # # net: sfp: add some quirks for GPON modules # # Marc Micalizzi reports that Huawei MA5671A and Alcatel/Lucent G-010S-P # modules are capable of 2500base-X, but incorrectly report their # capabilities in the EEPROM. It seems rather common that GPON modules # mis-report. # # Let's fix these modules by adding some quirks. # # Signed-off-by: Russell King # # d403d3abc48580561348053d7876becbded3fe2e # drivers/net/phy/sfp-bus.c | 25 +++++++++++++++++++++++++ # 1 file changed, 25 insertions(+) # # Author: Russell King (Fri 8 Mar 14:02:25 GMT 2019) # Committer: Russell King (Sun 8 Dec 14:41:36 GMT 2019) # # net: sfp: add support for module quirks # # Add support for applying module quirks to the list of supported # ethtool link modes. # # Signed-off-by: Russell King # # c310d0eb913d2cb528018a565702729735d0dd8b # drivers/net/phy/sfp-bus.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++ # 1 file changed, 54 insertions(+) # # Author: Russell King (Mon 11 Nov 11:58:09 GMT 2019) # Committer: Russell King (Sun 8 Dec 14:41:35 GMT 2019) # # net: phy: avoid matching all-ones clause 45 PHY IDs # # We currently match clause 45 PHYs using any ID read from a MMD marked # as present in the "Devices in package" registers 5 and 6. However, # this is incorrect. 45.2 says: # # "The definition of the term package is vendor specific and could be # a chip, module, or other similar entity." # # so a package could be more or less than the whole PHY - a PHY could be # made up of several modules instantiated onto a single chip such as the # Marvell 88x3310, or some of the MMDs could be disabled according to # chip configuration, such as the Broadcom 84881. # # In the case of Broadcom 84881, the "Devices in package" registers # contain 0xc000009b, meaning that there is a PHYXS present in the # package, but all registers in MMD 4 return 0xffff. This leads to our # matching code incorrectly binding this PHY to one of our generic PHY # drivers. # # This patch changes the way we determine whether to attempt to match a # MMD identifier, or use it to request a module - if the identifier is # all-ones, then we skip over it. When reading the identifiers, we # initialise phydev->c45_ids.device_ids to all-ones, only reading the # device ID if the "Devices in package" registers indicates we should. # # This avoids the generic drivers incorrectly matching on a PHY ID of # 0xffffffff. # # Signed-off-by: Russell King # # 67cf9e8fe5198e0c84e8bee9535504a2f9f84299 # drivers/net/phy/phy_device.c | 9 ++++++--- # 1 file changed, 6 insertions(+), 3 deletions(-) # # Author: Russell King (Fri 14 Apr 14:21:25 BST 2017) # Committer: Russell King (Sun 8 Dec 14:39:50 GMT 2019) # # net: phy: marvell10g: add SFP+ support # # Add support for SFP+ cages to the Marvell 10G PHY driver. This is # slightly complicated by the way phylib works in that we need to use # a multi-step process to attach the SFP bus, and we also need to track # the phylink state machine to know when the module's transmit disable # signal should change state. # # With appropriate DT changes, this allows the SFP+ canges on the # Macchiatobin platform to be functional. # # Signed-off-by: Russell King # # e4d2d776467e865c96e953bc3c071052d1282dce # drivers/net/phy/marvell10g.c | 25 ++++++++++++++++++++++++- # 1 file changed, 24 insertions(+), 1 deletion(-) # # Author: Russell King (Sun 15 Sep 20:05:34 BST 2019) # Committer: Russell King (Sun 8 Dec 14:39:50 GMT 2019) # # net: phy: add core phylib sfp support # # Add core phylib help for supporting SFP sockets on PHYs. This provides # a mechanism to inform the SFP layer about PHY up/down events, and also # unregister the SFP bus when the PHY is going away. # # Signed-off-by: Russell King # # 45d88d64f8c080e61c099ba9090c0463df2ecf78 # drivers/net/phy/phy.c | 7 +++++ # drivers/net/phy/phy_device.c | 66 ++++++++++++++++++++++++++++++++++++++++++++ # include/linux/phy.h | 11 ++++++++ # 3 files changed, 84 insertions(+) # # Author: Russell King (Mon 11 Nov 16:57:33 GMT 2019) # Committer: Russell King (Sun 8 Dec 14:29:24 GMT 2019) # # dt-bindings: net: add ethernet controller and phy sfp property # # Document the missing sfp property for ethernet controllers (which # has existed for some time) and now more recently for ethernet PHYs. # # Signed-off-by: Russell King # # 9317bb55fca0ced79cae7e5e3af5b59f16c2e202 # Documentation/devicetree/bindings/net/ethernet-controller.yaml | 5 +++++ # Documentation/devicetree/bindings/net/ethernet-phy.yaml | 5 +++++ # 2 files changed, 10 insertions(+) # # Author: Russell King (Fri 8 Nov 15:18:02 GMT 2019) # Committer: Russell King (Sun 8 Dec 14:29:19 GMT 2019) # # net: phylink: update to use phy_support_asym_pause() # # Use phy_support_asym_pause() rather than open-coding it. # # Signed-off-by: Russell King # # ea8a40e91416d00602dd615677da1cf6dbd7b8f6 # drivers/net/phy/phylink.c | 15 ++++++--------- # 1 file changed, 6 insertions(+), 9 deletions(-) # # Author: Colin Ian King (Wed 13 Nov 09:55:48 GMT 2019) # Committer: Russell King (Sun 8 Dec 14:29:02 GMT 2019) # # net: sfp: fix spelling mistake "requies" -> "requires" # # There is a spelling mistake in a dev_warn message. Fix it. # # Signed-off-by: Colin Ian King # Signed-off-by: David S. Miller # # a0d5bf0bc20193349f961756baa98bc18a26f3e5 # drivers/net/phy/sfp.c | 2 +- # 1 file changed, 1 insertion(+), 1 deletion(-) # # Author: Russell King (Thu 7 Nov 18:52:07 GMT 2019) # Committer: Russell King (Mon 25 Nov 12:05:53 GMT 2019) # # net: sfp: allow modules with slow diagnostics to probe # # When a module is inserted, we attempt to read read the ID from address # 0x50. Once we are able to read the ID, we immediately attempt to # initialise the hwmon support by reading from address 0x51. If this # fails, then we fall into error state, and assume that the module is # not usable. # # Modules such as the ALCATELLUCENT 3FE46541AA use a real EEPROM for # I2C address 0x50, which responds immediately. However, address 0x51 # is an emulated, which only becomes available once the on-board firmware # has booted. This prompts us to fall into the error state. # # Since the module may be usable without diagnostics, arrange for the # hwmon probe independent of the rest of the SFP itself, retrying every # 5s for up to about 60s for the monitoring to become available, and # print an error message if it doesn't become available. # # Signed-off-by: Russell King # # 5c0e904e27719c0e5306d2231e75a84458515c86 # drivers/net/phy/sfp.c | 96 +++++++++++++++++++++++++++++++++++++++------------ # 1 file changed, 74 insertions(+), 22 deletions(-) # # Author: Russell King (Tue 5 Nov 13:02:30 GMT 2019) # Committer: Russell King (Mon 25 Nov 12:05:52 GMT 2019) # # net: sfp: allow sfp to probe slow to initialise GPON modules # # Some GPON modules (e.g. Huawei MA5671A) take a significant amount of # time to start responding on the I2C bus, contary to the SFF # specifications. # # Work around this by implementing a two-level timeout strategy, where # we initially quickly retry for the module, and then use a slower retry # after we exceed a maximum number of quick attempts. # # Signed-off-by: Russell King # # 53b4a58370ab937fdddad2603c1d14fc4e0aa2a5 # drivers/net/phy/sfp.c | 38 ++++++++++++++++++++++++++++---------- # 1 file changed, 28 insertions(+), 10 deletions(-) # # Author: Russell King (Tue 5 Nov 13:00:45 GMT 2019) # Committer: Russell King (Mon 25 Nov 12:05:50 GMT 2019) # # net: sfp: move module insert reporting out of probe # # Move the module insertion reporting out of the probe handling, but # after we have detected that the upstream has attached (since that is # whom we are reporting insertion to.) # # Only report module removal if we had previously reported a module # insertion. # # This gives cleaner semantics, and means we can probe the module before # we have an upstream attached. # # Signed-off-by: Russell King # # 18377bd296713ee8bc8d5af22c4134a58527567b # drivers/net/phy/sfp.c | 58 +++++++++++++++++++++++++++++++++++---------------- # 1 file changed, 40 insertions(+), 18 deletions(-) # # Author: Russell King (Tue 5 Nov 12:59:36 GMT 2019) # Committer: Russell King (Mon 25 Nov 12:05:49 GMT 2019) # # net: sfp: split power mode switching from probe # # Switch the power mode switching from the probe, so that we don't # repeatedly re-probe the SFP device if there is a problem accessing # the registers at I2C address 0x51. # # In splitting this out, we can also fix a bug where we leave the module # in high-power mode when the upstream device is detached but the module # is still inserted. # # Signed-off-by: Russell King # # 03cf53a94ff98b954ef7817a1d05e4cae0595f2f # drivers/net/phy/sfp.c | 101 ++++++++++++++++++++++++++++++++------------------ # 1 file changed, 64 insertions(+), 37 deletions(-) # # Author: Russell King (Tue 5 Nov 12:57:40 GMT 2019) # Committer: Russell King (Mon 25 Nov 12:05:48 GMT 2019) # # net: sfp: track upstream's attachment state in state machine # # Track the upstream's attachment state in the state machine rather than # maintaining a boolean, which ensures that we have a strict order of # ATTACH followed by an UP event - we can never believe that a newly # attached upstream will be anything but down. # # Rearrange the order of state machines so we run the module state # machine after the upstream device's state machine, so the module state # machine can check the current state of the device and take action to # e.g. reset back to empty state when the upstream is detached. # # This is to allow the module detection to run independently of the # network device becoming available. # # Signed-off-by: Russell King # # 12eaf7e9f504f1f849368bb07f9e7faeadc87dea # drivers/net/phy/sfp.c | 42 +++++++++++++++++++++++++++++------------- # 1 file changed, 29 insertions(+), 13 deletions(-) # # Author: Russell King (Fri 18 Oct 10:31:07 BST 2019) # Committer: Russell King (Mon 25 Nov 12:05:47 GMT 2019) # # net: sfp: ensure TX_FAULT has deasserted before probing the PHY # # TX_FAULT should be deasserted to indicate that the module has completed # its initialisation. This may include the on-board PHY, so wait until # the module has deasserted TX_FAULT before probing the PHY. # # This means that we need an extra state to handle a TX_FAULT that # remains set for longer than t_init, since using the existing handling # state would bypass the PHY probe. # # Signed-off-by: Russell King # # f44dd47242acae31ddf3499aecde162ff7d8c7c0 # drivers/net/phy/sfp.c | 31 +++++++++++++++++++++++++------ # 1 file changed, 25 insertions(+), 6 deletions(-) # # Author: Russell King (Fri 18 Oct 09:58:33 BST 2019) # Committer: Russell King (Mon 25 Nov 12:05:44 GMT 2019) # # net: sfp: allow fault processing to transition to other states # # Add the next state to sfp_sm_fault() so that it can branch to other # states. This will be necessary to improve the initialisation path. # # Signed-off-by: Russell King # # 09bd742dbed2da21c306275e3516f55bd8cbdce1 # drivers/net/phy/sfp.c | 12 ++++++------ # 1 file changed, 6 insertions(+), 6 deletions(-) # # Author: Russell King (Fri 18 Oct 10:21:46 BST 2019) # Committer: Russell King (Mon 25 Nov 12:05:43 GMT 2019) # # net: sfp: eliminate mdelay() from PHY probe # # Rather than using mdelay() to wait before probing the PHY (which holds # several locks, including the rtnl lock), add an extra wait state to # the state machine to introduce the 50ms delay without holding any # locks. # # Signed-off-by: Russell King # # adf5132fe8d269c568593e0cd0ed7d5d95d2f938 # drivers/net/phy/sfp.c | 52 +++++++++++++++++++++++++++++++++++++++------------ # 1 file changed, 40 insertions(+), 12 deletions(-) # # Author: Russell King (Fri 18 Oct 10:09:02 BST 2019) # Committer: Russell King (Mon 25 Nov 12:05:42 GMT 2019) # # net: sfp: split the PHY probe from sfp_sm_mod_init() # # Move the PHY probe into a separate function, splitting it from # sfp_sm_mod_init(). This will allow us to eliminate the 50ms mdelay() # inside the state machine. # # Signed-off-by: Russell King # # 627f387b78135fbd0b40a6e62e623c9f8db0eeff # drivers/net/phy/sfp.c | 21 +++++++++++++-------- # 1 file changed, 13 insertions(+), 8 deletions(-) # # Author: Russell King (Thu 17 Oct 11:12:42 BST 2019) # Committer: Russell King (Mon 25 Nov 12:05:40 GMT 2019) # # net: sfp: control TX_DISABLE and phy only from main state machine # # We initialise TX_DISABLE when the sfp cage is probed, and then # maintain its state in the main state machine. However, the module # state machine: # - negates it when detecting a newly inserted module when it's already # guaranteed to be negated. # - negates it when the module is removed, but the main state machine # will do this anyway. # # Make TX_DISABLE entirely controlled by the main state machine. # # The main state machine also probes the module for a PHY, and removes # the PHY when the the module is removed. Hence, removing the PHY in # sfp_sm_module_remove() is also redundant, and is a left-over from # when we tried to probe for the PHY from the module state machine. # # Signed-off-by: Russell King # # 585a4996c0b4755c909a87f5ae427360c66fcdce # drivers/net/phy/sfp.c | 9 +-------- # 1 file changed, 1 insertion(+), 8 deletions(-) # # Author: Russell King (Thu 17 Oct 00:24:18 BST 2019) # Committer: Russell King (Mon 25 Nov 12:05:39 GMT 2019) # # net: sfp: avoid power switch on address-change modules # # If the module indicates that it requires an address change sequence to # switch between address 0x50 and 0x51, which we don't support, we can't # write to the register that controls the power mode to switch to high # power mode. Warn the user that the module may not be functional in # this case, and don't try to change the power mode. # # Signed-off-by: Russell King # # 0240772461349a25b8387b1926ca4fbbcfe43f83 # drivers/net/phy/sfp.c | 31 ++++++++++++++++++++----------- # 1 file changed, 20 insertions(+), 11 deletions(-) # # Author: Russell King (Fri 11 Oct 17:24:40 BST 2019) # Committer: Russell King (Mon 25 Nov 12:05:38 GMT 2019) # # net: sfp: parse SFP power requirement earlier # # Parse the SFP power requirement earlier, in preparation for moving the # power level setup code. # # Signed-off-by: Russell King # # cb129dddc4b890a10a63422ef86743020720ad54 # drivers/net/phy/sfp.c | 42 +++++++++++++++++++++++++++++------------- # 1 file changed, 29 insertions(+), 13 deletions(-) # # Author: Russell King (Tue 15 Oct 10:54:15 BST 2019) # Committer: Russell King (Mon 25 Nov 12:05:36 GMT 2019) # # net: sfp: rename T_PROBE_WAIT to T_SERIAL # # SFF-8472 rev 12.2 defines the time for the serial bus to become ready # using t_serial. Use this as our identifier for this timeout to make # it clear what we are referring to. # # Signed-off-by: Russell King # # f2beddb472bd709abe907341152391bcee58c703 # drivers/net/phy/sfp.c | 13 ++++++------- # 1 file changed, 6 insertions(+), 7 deletions(-) # # Author: Russell King (Tue 15 Oct 10:33:13 BST 2019) # Committer: Russell King (Mon 25 Nov 12:05:34 GMT 2019) # # net: sfp: handle module remove outside state machine # # Removing a module resets the module state machine back to its initial # state. Rather than explicitly handling this in every state, handle it # early on outside of the state machine. # # Signed-off-by: Russell King # # f0d34b9b0e620bb427e006ecae8fc6ac8ed3c90a # drivers/net/phy/sfp.c | 16 +++++++++------- # 1 file changed, 9 insertions(+), 7 deletions(-) # # Author: Russell King (Fri 11 Oct 22:27:21 BST 2019) # Committer: Russell King (Mon 25 Nov 12:05:33 GMT 2019) # # net: sfp: rename sfp_sm_ins_next() as sfp_sm_mod_next() # # sfp_sm_ins_next() modifies the module state machine. Change it's name # to reflect this. # # Signed-off-by: Russell King # # 74075d840180e1a3ca4de0240a3fd224611df968 # drivers/net/phy/sfp.c | 16 ++++++++-------- # 1 file changed, 8 insertions(+), 8 deletions(-) # # Author: Russell King (Fri 11 Oct 22:14:47 BST 2019) # Committer: Russell King (Mon 25 Nov 12:05:32 GMT 2019) # # net: sfp: move tx disable on device down to main state machine # # Move the tx disable assertion on device down to the main state # machine. # # Signed-off-by: Russell King # # 77bf332f1c590c9002694fbdb0c6922319ac057c # drivers/net/phy/sfp.c | 10 ++-------- # 1 file changed, 2 insertions(+), 8 deletions(-) # # Author: Russell King (Fri 11 Oct 19:33:58 BST 2019) # Committer: Russell King (Mon 25 Nov 12:05:31 GMT 2019) # # net: sfp: move sfp sub-state machines into separate functions # # Move the SFP sub-state machines out of the main state machine function, # in preparation for it doing a bit more with the device state. By doing # so, we ensure that our debug after the main state machine is always # printed. # # Signed-off-by: Russell King # # 8fc32f551fbb7045f50c8e5d513ec998a60fa19d # drivers/net/phy/sfp.c | 74 ++++++++++++++++++++++++++++++--------------------- # 1 file changed, 43 insertions(+), 31 deletions(-) # # Author: Russell King (Mon 11 Nov 10:23:35 GMT 2019) # Committer: Russell King (Mon 25 Nov 12:05:29 GMT 2019) # # net: sfp: fix sfp_bus_add_upstream() warning # # When building with SFP disabled, the stub for sfp_bus_add_upstream() # missed "inline". Add it. # # Fixes: 727b3668b730 ("net: sfp: rework upstream interface") # Signed-off-by: Russell King # # 0240c336f567cf7610c7fa7f3da0cbf4ce18c8b4 # include/linux/sfp.h | 4 ++-- # 1 file changed, 2 insertions(+), 2 deletions(-) # # Author: Russell King (Sat 9 Nov 08:13:50 GMT 2019) # Committer: Russell King (Mon 25 Nov 12:05:28 GMT 2019) # # net: sfp: fix sfp_bus_put() kernel documentation # # The kbuild test robot found a problem with htmldocs with the recent # change to the SFP interfaces. Fix the kernel documentation for # sfp_bus_put() which was missing an '@' before the argument name # description. # # Fixes: 727b3668b730 ("net: sfp: rework upstream interface") # Signed-off-by: Russell King # # 880170cb9ca2d45817d382ddb3164198b3433578 # drivers/net/phy/sfp-bus.c | 2 +- # 1 file changed, 1 insertion(+), 1 deletion(-) # # Author: Russell King (Thu 7 Nov 17:06:08 GMT 2019) # Committer: Russell King (Mon 25 Nov 12:05:27 GMT 2019) # # net: sfp: rework upstream interface # # The current upstream interface is an all-or-nothing, which is # sub-optimal for future changes, as it doesn't allow the upstream driver # to prepare for the SFP module becoming available, as it is at boot. # # Switch to a find-sfp-bus, add-upstream, del-upstream, put-sfp-bus # interface structure instead, which allows the upstream driver to # prepare for a module being available as soon as add-upstream is called. # # Signed-off-by: Russell King # # 257be598427b33ea7b80cfa9c6de2e1f702e6585 # drivers/net/phy/phylink.c | 10 +++--- # drivers/net/phy/sfp-bus.c | 92 +++++++++++++++++++++++++++++++++-------------- # include/linux/sfp.h | 25 ++++++++----- # 3 files changed, 88 insertions(+), 39 deletions(-) # # Author: Russell King (Sat 14 Sep 14:21:22 BST 2019) # Committer: Russell King (Mon 25 Nov 12:05:25 GMT 2019) # # net: sfp: move fwnode parsing into sfp-bus layer # # Rather than parsing the sfp firmware node in phylink, parse it in the # sfp-bus code, so we can re-use this code for PHYs without having to # duplicate the parsing. # # Signed-off-by: Russell King # # e2176c9dbda8754611a2bea746e2b642025c94d1 # drivers/net/phy/phylink.c | 21 +++++---------- # drivers/net/phy/sfp-bus.c | 65 ++++++++++++++++++++++++++++++----------------- # include/linux/sfp.h | 10 ++++---- # 3 files changed, 53 insertions(+), 43 deletions(-) # # Author: Russell King (Fri 14 Jun 15:26:10 BST 2019) # Committer: Russell King (Mon 25 Nov 12:05:23 GMT 2019) # # net: phylink: use more linkmode_* # # Use more linkmode_* helpers rather than open-coding the bitmap # operations. # # Signed-off-by: Russell King # # 6034505424ef0d844e778fa6dc3a62678b4fcdd4 # drivers/net/phy/phylink.c | 7 ++----- # include/linux/linkmode.h | 6 ++++++ # 2 files changed, 8 insertions(+), 5 deletions(-) # diff --git a/Documentation/devicetree/bindings/net/ethernet-controller.yaml b/Documentation/devicetree/bindings/net/ethernet-controller.yaml index 0e7c31794ae6..ac471b60ed6a 100644 --- a/Documentation/devicetree/bindings/net/ethernet-controller.yaml +++ b/Documentation/devicetree/bindings/net/ethernet-controller.yaml @@ -121,6 +121,11 @@ title: Ethernet Controller Generic Binding and is useful for determining certain configuration settings such as flow control thresholds. + sfp: + $ref: /schemas/types.yaml#definitions/phandle + description: + Specifies a reference to a node representing a SFP cage. + tx-fifo-depth: $ref: /schemas/types.yaml#definitions/uint32 description: diff --git a/Documentation/devicetree/bindings/net/ethernet-phy.yaml b/Documentation/devicetree/bindings/net/ethernet-phy.yaml index f70f18ff821f..8927941c74bb 100644 --- a/Documentation/devicetree/bindings/net/ethernet-phy.yaml +++ b/Documentation/devicetree/bindings/net/ethernet-phy.yaml @@ -153,6 +153,11 @@ title: Ethernet PHY Generic Binding Delay after the reset was deasserted in microseconds. If this property is missing the delay will be skipped. + sfp: + $ref: /schemas/types.yaml#definitions/phandle + description: + Specifies a reference to a node representing a SFP cage. + required: - reg diff --git a/Documentation/networking/phy.rst b/Documentation/networking/phy.rst index a689966bc4be..cda1c0a0492a 100644 --- a/Documentation/networking/phy.rst +++ b/Documentation/networking/phy.rst @@ -352,7 +352,8 @@ Fills the phydev structure with up-to-date information about the current settings in the PHY. :: - int phy_ethtool_sset(struct phy_device *phydev, struct ethtool_cmd *cmd); + int phy_ethtool_ksettings_set(struct phy_device *phydev, + const struct ethtool_link_ksettings *cmd); Ethtool convenience functions. :: diff --git a/drivers/net/phy/marvell10g.c b/drivers/net/phy/marvell10g.c index 3b99882692e3..6397e7c4219f 100644 --- a/drivers/net/phy/marvell10g.c +++ b/drivers/net/phy/marvell10g.c @@ -26,6 +26,7 @@ #include #include #include +#include #define MV_PHY_ALASKA_NBT_QUIRK_MASK 0xfffffffe #define MV_PHY_ALASKA_NBT_QUIRK_REV (MARVELL_PHY_ID_88X3310 | 0xa) @@ -62,6 +63,8 @@ enum { }; struct mv3310_priv { + bool firmware_failed; + struct device *hwmon_dev; char *hwmon_name; }; @@ -206,6 +209,28 @@ static int mv3310_hwmon_probe(struct phy_device *phydev) } #endif +static int mv3310_sfp_insert(void *upstream, const struct sfp_eeprom_id *id) +{ + struct phy_device *phydev = upstream; + __ETHTOOL_DECLARE_LINK_MODE_MASK(support) = { 0, }; + phy_interface_t iface; + + sfp_parse_support(phydev->sfp_bus, id, support); + iface = sfp_select_interface(phydev->sfp_bus, id, support); + + if (iface != PHY_INTERFACE_MODE_10GKR) { + dev_err(&phydev->mdio.dev, "incompatible SFP module inserted\n"); + return -EINVAL; + } + return 0; +} + +static const struct sfp_upstream_ops mv3310_sfp_ops = { + .attach = phy_sfp_attach, + .detach = phy_sfp_detach, + .module_insert = mv3310_sfp_insert, +}; + static int mv3310_probe(struct phy_device *phydev) { struct mv3310_priv *priv; @@ -216,6 +241,10 @@ static int mv3310_probe(struct phy_device *phydev) (phydev->c45_ids.devices_in_package & mmd_mask) != mmd_mask) return -ENODEV; + priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MV_PMA_BOOT); if (ret < 0) return ret; @@ -223,20 +252,16 @@ static int mv3310_probe(struct phy_device *phydev) if (ret & MV_PMA_BOOT_FATAL) { dev_warn(&phydev->mdio.dev, "PHY failed to boot firmware, status=%04x\n", ret); - return -ENODEV; + priv->firmware_failed = true; } - priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - dev_set_drvdata(&phydev->mdio.dev, priv); ret = mv3310_hwmon_probe(phydev); if (ret) return ret; - return 0; + return phy_sfp_probe(phydev, &mv3310_sfp_ops); } static int mv3310_suspend(struct phy_device *phydev) @@ -257,6 +282,19 @@ static int mv3310_resume(struct phy_device *phydev) return mv3310_hwmon_config(phydev, true); } +static int mv3310_start(struct phy_device *phydev) +{ + struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev); + + if (priv->firmware_failed) { + dev_warn(&phydev->mdio.dev, + "PHY firmware failure: PHY not starting"); + return -EINVAL; + } + + return 0; +} + /* Some PHYs in the Alaska family such as the 88X3310 and the 88E2010 * don't set bit 14 in PMA Extended Abilities (1.11), although they do * support 2.5GBASET and 5GBASET. For these models, we can still read their @@ -487,6 +525,7 @@ static struct phy_driver mv3310_drivers[] = { .probe = mv3310_probe, .suspend = mv3310_suspend, .resume = mv3310_resume, + .start = mv3310_start, .config_aneg = mv3310_config_aneg, .aneg_done = mv3310_aneg_done, .read_status = mv3310_read_status, @@ -498,6 +537,7 @@ static struct phy_driver mv3310_drivers[] = { .probe = mv3310_probe, .suspend = mv3310_suspend, .resume = mv3310_resume, + .start = mv3310_start, .soft_reset = genphy_no_soft_reset, .config_init = mv3310_config_init, .config_aneg = mv3310_config_aneg, diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 105d389b58e7..2534b4169613 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -252,66 +253,6 @@ static void phy_sanitize_settings(struct phy_device *phydev) } } -/** - * phy_ethtool_sset - generic ethtool sset function, handles all the details - * @phydev: target phy_device struct - * @cmd: ethtool_cmd - * - * A few notes about parameter checking: - * - * - We don't set port or transceiver, so we don't care what they - * were set to. - * - phy_start_aneg() will make sure forced settings are sane, and - * choose the next best ones from the ones selected, so we don't - * care if ethtool tries to give us bad values. - */ -int phy_ethtool_sset(struct phy_device *phydev, struct ethtool_cmd *cmd) -{ - __ETHTOOL_DECLARE_LINK_MODE_MASK(advertising); - u32 speed = ethtool_cmd_speed(cmd); - - if (cmd->phy_address != phydev->mdio.addr) - return -EINVAL; - - /* We make sure that we don't pass unsupported values in to the PHY */ - ethtool_convert_legacy_u32_to_link_mode(advertising, cmd->advertising); - linkmode_and(advertising, advertising, phydev->supported); - - /* Verify the settings we care about. */ - if (cmd->autoneg != AUTONEG_ENABLE && cmd->autoneg != AUTONEG_DISABLE) - return -EINVAL; - - if (cmd->autoneg == AUTONEG_ENABLE && cmd->advertising == 0) - return -EINVAL; - - if (cmd->autoneg == AUTONEG_DISABLE && - ((speed != SPEED_1000 && - speed != SPEED_100 && - speed != SPEED_10) || - (cmd->duplex != DUPLEX_HALF && - cmd->duplex != DUPLEX_FULL))) - return -EINVAL; - - phydev->autoneg = cmd->autoneg; - - phydev->speed = speed; - - linkmode_copy(phydev->advertising, advertising); - - linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, - phydev->advertising, AUTONEG_ENABLE == cmd->autoneg); - - phydev->duplex = cmd->duplex; - - phydev->mdix_ctrl = cmd->eth_tp_mdix_ctrl; - - /* Restart the PHY */ - phy_start_aneg(phydev); - - return 0; -} -EXPORT_SYMBOL(phy_ethtool_sset); - int phy_ethtool_ksettings_set(struct phy_device *phydev, const struct ethtool_link_ksettings *cmd) { @@ -841,7 +782,12 @@ void phy_stop(struct phy_device *phydev) mutex_lock(&phydev->lock); + if (phydev->sfp_bus) + sfp_upstream_stop(phydev->sfp_bus); + phydev->state = PHY_HALTED; + if (phydev->drv->stop) + phydev->drv->stop(phydev); mutex_unlock(&phydev->lock); @@ -875,6 +821,12 @@ void phy_start(struct phy_device *phydev) goto out; } + if (phydev->drv->start && phydev->drv->start(phydev)) + goto out; + + if (phydev->sfp_bus) + sfp_upstream_start(phydev->sfp_bus); + /* if phy was suspended, bring the physical link up again */ __phy_resume(phydev); diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index adb66a2fae18..ad2e5b233638 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -488,7 +489,7 @@ static int phy_bus_match(struct device *dev, struct device_driver *drv) if (phydev->is_c45) { for (i = 1; i < num_ids; i++) { - if (!(phydev->c45_ids.devices_in_package & (1 << i))) + if (phydev->c45_ids.device_ids[i] == 0xffffffff) continue; if ((phydrv->phy_id & phydrv->phy_id_mask) == @@ -596,8 +597,8 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id, mdiodev->device_free = phy_mdio_device_free; mdiodev->device_remove = phy_mdio_device_remove; - dev->speed = 0; - dev->duplex = -1; + dev->speed = SPEED_UNKNOWN; + dev->duplex = DUPLEX_UNKNOWN; dev->pause = 0; dev->asym_pause = 0; dev->link = 0; @@ -632,7 +633,7 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id, int i; for (i = 1; i < num_ids; i++) { - if (!(c45_ids->devices_in_package & (1 << i))) + if (c45_ids->device_ids[i] == 0xffffffff) continue; ret = phy_request_driver_module(dev, @@ -812,10 +813,13 @@ static int get_phy_id(struct mii_bus *bus, int addr, u32 *phy_id, */ struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45) { - struct phy_c45_device_ids c45_ids = {0}; + struct phy_c45_device_ids c45_ids; u32 phy_id = 0; int r; + c45_ids.devices_in_package = 0; + memset(c45_ids.device_ids, 0xff, sizeof(c45_ids.device_ids)); + r = get_phy_id(bus, addr, &phy_id, is_c45, &c45_ids); if (r) return ERR_PTR(r); @@ -1174,6 +1178,65 @@ phy_standalone_show(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR_RO(phy_standalone); +/** + * phy_sfp_attach - attach the SFP bus to the PHY upstream network device + * @upstream: pointer to the phy device + * @bus: sfp bus representing cage being attached + * + * This is used to fill in the sfp_upstream_ops .attach member. + */ +void phy_sfp_attach(void *upstream, struct sfp_bus *bus) +{ + struct phy_device *phydev = upstream; + + if (phydev->attached_dev) + phydev->attached_dev->sfp_bus = bus; + phydev->sfp_bus_attached = true; +} +EXPORT_SYMBOL(phy_sfp_attach); + +/** + * phy_sfp_detach - detach the SFP bus from the PHY upstream network device + * @upstream: pointer to the phy device + * @bus: sfp bus representing cage being attached + * + * This is used to fill in the sfp_upstream_ops .detach member. + */ +void phy_sfp_detach(void *upstream, struct sfp_bus *bus) +{ + struct phy_device *phydev = upstream; + + if (phydev->attached_dev) + phydev->attached_dev->sfp_bus = NULL; + phydev->sfp_bus_attached = false; +} +EXPORT_SYMBOL(phy_sfp_detach); + +/** + * phy_sfp_probe - probe for a SFP cage attached to this PHY device + * @phydev: Pointer to phy_device + * @ops: SFP's upstream operations + */ +int phy_sfp_probe(struct phy_device *phydev, + const struct sfp_upstream_ops *ops) +{ + struct sfp_bus *bus; + int ret; + + if (phydev->mdio.dev.fwnode) { + bus = sfp_bus_find_fwnode(phydev->mdio.dev.fwnode); + if (IS_ERR(bus)) + return PTR_ERR(bus); + + phydev->sfp_bus = bus; + + ret = sfp_bus_add_upstream(bus, phydev, ops); + sfp_bus_put(bus); + } + return 0; +} +EXPORT_SYMBOL(phy_sfp_probe); + /** * phy_attach_direct - attach a network device to a given PHY device pointer * @dev: network device to attach @@ -1249,6 +1312,9 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev, if (dev) { phydev->attached_dev = dev; dev->phydev = phydev; + + if (phydev->sfp_bus_attached) + dev->sfp_bus = phydev->sfp_bus; } /* Some Ethernet drivers try to connect to a PHY device before @@ -2279,6 +2345,9 @@ static int phy_remove(struct device *dev) phydev->state = PHY_DOWN; mutex_unlock(&phydev->lock); + sfp_bus_del_upstream(phydev->sfp_bus); + phydev->sfp_bus = NULL; + if (phydev->drv && phydev->drv->remove) { phydev->drv->remove(phydev); diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index 536236fdb232..8e2a12885789 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -133,9 +133,7 @@ static int phylink_is_empty_linkmode(const unsigned long *linkmode) phylink_set(tmp, Pause); phylink_set(tmp, Asym_Pause); - bitmap_andnot(tmp, linkmode, tmp, __ETHTOOL_LINK_MODE_MASK_NBITS); - - return linkmode_empty(tmp); + return linkmode_subset(linkmode, tmp); } static const char *phylink_an_mode_str(unsigned int mode) @@ -566,28 +564,22 @@ static const struct sfp_upstream_ops sfp_phylink_ops; static int phylink_register_sfp(struct phylink *pl, struct fwnode_handle *fwnode) { - struct fwnode_reference_args ref; + struct sfp_bus *bus; int ret; - if (!fwnode) - return 0; - - ret = fwnode_property_get_reference_args(fwnode, "sfp", NULL, - 0, 0, &ref); - if (ret < 0) { - if (ret == -ENOENT) - return 0; - - phylink_err(pl, "unable to parse \"sfp\" node: %d\n", - ret); + bus = sfp_bus_find_fwnode(fwnode); + if (IS_ERR(bus)) { + ret = PTR_ERR(bus); + phylink_err(pl, "unable to attach SFP bus: %d\n", ret); return ret; } - pl->sfp_bus = sfp_register_upstream(ref.fwnode, pl, &sfp_phylink_ops); - if (!pl->sfp_bus) - return -ENOMEM; + pl->sfp_bus = bus; - return 0; + ret = sfp_bus_add_upstream(bus, pl, &sfp_phylink_ops); + sfp_bus_put(bus); + + return ret; } /** @@ -685,8 +677,7 @@ EXPORT_SYMBOL_GPL(phylink_create); */ void phylink_destroy(struct phylink *pl) { - if (pl->sfp_bus) - sfp_unregister_upstream(pl->sfp_bus); + sfp_bus_del_upstream(pl->sfp_bus); if (pl->link_gpio) gpiod_put(pl->link_gpio); @@ -726,11 +717,6 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy) __ETHTOOL_DECLARE_LINK_MODE_MASK(supported); int ret; - memset(&config, 0, sizeof(config)); - linkmode_copy(supported, phy->supported); - linkmode_copy(config.advertising, phy->advertising); - config.interface = pl->link_config.interface; - /* * This is the new way of dealing with flow control for PHYs, * as described by Timur Tabi in commit 529ed1275263 ("net: phy: @@ -738,10 +724,12 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy) * using our validate call to the MAC, we rely upon the MAC * clearing the bits from both supported and advertising fields. */ - if (phylink_test(supported, Pause)) - phylink_set(config.advertising, Pause); - if (phylink_test(supported, Asym_Pause)) - phylink_set(config.advertising, Asym_Pause); + phy_support_asym_pause(phy); + + memset(&config, 0, sizeof(config)); + linkmode_copy(supported, phy->supported); + linkmode_copy(config.advertising, phy->advertising); + config.interface = pl->link_config.interface; ret = phylink_validate(pl, supported, &config); if (ret) @@ -1755,8 +1743,7 @@ static int phylink_sfp_module_insert(void *upstream, if (phy_interface_mode_is_8023z(iface) && pl->phydev) return -EINVAL; - changed = !bitmap_equal(pl->supported, support, - __ETHTOOL_LINK_MODE_MASK_NBITS); + changed = !linkmode_equal(pl->supported, support); if (changed) { linkmode_copy(pl->supported, support); linkmode_copy(pl->link_config.advertising, config.advertising); diff --git a/drivers/net/phy/sfp-bus.c b/drivers/net/phy/sfp-bus.c index b23fc41896ef..5a72093ab6e7 100644 --- a/drivers/net/phy/sfp-bus.c +++ b/drivers/net/phy/sfp-bus.c @@ -4,11 +4,18 @@ #include #include #include +#include #include #include #include "sfp.h" +struct sfp_quirk { + const char *vendor; + const char *part; + void (*modes)(const struct sfp_eeprom_id *id, unsigned long *modes); +}; + /** * struct sfp_bus - internal representation of a sfp bus */ @@ -21,6 +28,7 @@ struct sfp_bus { const struct sfp_socket_ops *socket_ops; struct device *sfp_dev; struct sfp *sfp; + const struct sfp_quirk *sfp_quirk; const struct sfp_upstream_ops *upstream_ops; void *upstream; @@ -30,6 +38,71 @@ struct sfp_bus { bool started; }; +static void sfp_quirk_2500basex(const struct sfp_eeprom_id *id, + unsigned long *modes) +{ + phylink_set(modes, 2500baseX_Full); +} + +static const struct sfp_quirk sfp_quirks[] = { + { + // Alcatel Lucent G-010S-P can operate at 2500base-X, but + // incorrectly report 2500MBd NRZ in their EEPROM + .vendor = "ALCATELLUCENT", + .part = "G010SP", + .modes = sfp_quirk_2500basex, + }, { + // Alcatel Lucent G-010S-A can operate at 2500base-X, but + // report 3.2GBd NRZ in their EEPROM + .vendor = "ALCATELLUCENT", + .part = "3FE46541AA", + .modes = sfp_quirk_2500basex, + }, { + // Huawei MA5671A can operate at 2500base-X, but report 1.2GBd + // NRZ in their EEPROM + .vendor = "HUAWEI", + .part = "MA5671A", + .modes = sfp_quirk_2500basex, + }, +}; + +static size_t sfp_strlen(const char *str, size_t maxlen) +{ + size_t size, i; + + /* Trailing characters should be filled with space chars */ + for (i = 0, size = 0; i < maxlen; i++) + if (str[i] != ' ') + size = i + 1; + + return size; +} + +static bool sfp_match(const char *qs, const char *str, size_t len) +{ + if (!qs) + return true; + if (strlen(qs) != len) + return false; + return !strncmp(qs, str, len); +} + +static const struct sfp_quirk *sfp_lookup_quirk(const struct sfp_eeprom_id *id) +{ + const struct sfp_quirk *q; + unsigned int i; + size_t vs, ps; + + vs = sfp_strlen(id->base.vendor_name, ARRAY_SIZE(id->base.vendor_name)); + ps = sfp_strlen(id->base.vendor_pn, ARRAY_SIZE(id->base.vendor_pn)); + + for (i = 0, q = sfp_quirks; i < ARRAY_SIZE(sfp_quirks); i++, q++) + if (sfp_match(q->vendor, id->base.vendor_name, vs) && + sfp_match(q->part, id->base.vendor_pn, ps)) + return q; + + return NULL; +} /** * sfp_parse_port() - Parse the EEPROM base ID, setting the port type * @bus: a pointer to the &struct sfp_bus structure for the sfp module @@ -233,6 +306,9 @@ void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id, phylink_set(modes, 1000baseX_Full); } + if (bus->sfp_quirk) + bus->sfp_quirk->modes(id, modes); + bitmap_or(support, support, modes, __ETHTOOL_LINK_MODE_MASK_NBITS); phylink_set(support, Autoneg); @@ -328,10 +404,19 @@ static void sfp_bus_release(struct kref *kref) kfree(bus); } -static void sfp_bus_put(struct sfp_bus *bus) +/** + * sfp_bus_put() - put a reference on the &struct sfp_bus + * @bus: the &struct sfp_bus found via sfp_bus_find_fwnode() + * + * Put a reference on the &struct sfp_bus and free the underlying structure + * if this was the last reference. + */ +void sfp_bus_put(struct sfp_bus *bus) { - kref_put_mutex(&bus->kref, sfp_bus_release, &sfp_mutex); + if (bus) + kref_put_mutex(&bus->kref, sfp_bus_release, &sfp_mutex); } +EXPORT_SYMBOL_GPL(sfp_bus_put); static int sfp_register_bus(struct sfp_bus *bus) { @@ -347,11 +432,11 @@ static int sfp_register_bus(struct sfp_bus *bus) return ret; } } + bus->registered = true; bus->socket_ops->attach(bus->sfp); if (bus->started) bus->socket_ops->start(bus->sfp); bus->upstream_ops->attach(bus->upstream, bus); - bus->registered = true; return 0; } @@ -445,64 +530,111 @@ static void sfp_upstream_clear(struct sfp_bus *bus) } /** - * sfp_register_upstream() - Register the neighbouring device - * @fwnode: firmware node for the SFP bus + * sfp_bus_find_fwnode() - parse and locate the SFP bus from fwnode + * @fwnode: firmware node for the parent device (MAC or PHY) + * + * Parse the parent device's firmware node for a SFP bus, and locate + * the sfp_bus structure, incrementing its reference count. This must + * be put via sfp_bus_put() when done. + * + * Returns: on success, a pointer to the sfp_bus structure, + * %NULL if no SFP is specified, + * on failure, an error pointer value: + * corresponding to the errors detailed for + * fwnode_property_get_reference_args(). + * %-ENOMEM if we failed to allocate the bus. + * an error from the upstream's connect_phy() method. + */ +struct sfp_bus *sfp_bus_find_fwnode(struct fwnode_handle *fwnode) +{ + struct fwnode_reference_args ref; + struct sfp_bus *bus; + int ret; + + ret = fwnode_property_get_reference_args(fwnode, "sfp", NULL, + 0, 0, &ref); + if (ret == -ENOENT) + return NULL; + else if (ret < 0) + return ERR_PTR(ret); + + bus = sfp_bus_get(ref.fwnode); + fwnode_handle_put(ref.fwnode); + if (!bus) + return ERR_PTR(-ENOMEM); + + return bus; +} +EXPORT_SYMBOL_GPL(sfp_bus_find_fwnode); + +/** + * sfp_bus_add_upstream() - parse and register the neighbouring device + * @bus: the &struct sfp_bus found via sfp_bus_find_fwnode() * @upstream: the upstream private data * @ops: the upstream's &struct sfp_upstream_ops * - * Register the upstream device (eg, PHY) with the SFP bus. MAC drivers - * should use phylink, which will call this function for them. Returns - * a pointer to the allocated &struct sfp_bus. + * Add upstream driver for the SFP bus, and if the bus is complete, register + * the SFP bus using sfp_register_upstream(). This takes a reference on the + * bus, so it is safe to put the bus after this call. * - * On error, returns %NULL. + * Returns: on success, a pointer to the sfp_bus structure, + * %NULL if no SFP is specified, + * on failure, an error pointer value: + * corresponding to the errors detailed for + * fwnode_property_get_reference_args(). + * %-ENOMEM if we failed to allocate the bus. + * an error from the upstream's connect_phy() method. */ -struct sfp_bus *sfp_register_upstream(struct fwnode_handle *fwnode, - void *upstream, - const struct sfp_upstream_ops *ops) +int sfp_bus_add_upstream(struct sfp_bus *bus, void *upstream, + const struct sfp_upstream_ops *ops) { - struct sfp_bus *bus = sfp_bus_get(fwnode); - int ret = 0; + int ret; - if (bus) { - rtnl_lock(); - bus->upstream_ops = ops; - bus->upstream = upstream; + /* If no bus, return success */ + if (!bus) + return 0; - if (bus->sfp) { - ret = sfp_register_bus(bus); - if (ret) - sfp_upstream_clear(bus); - } - rtnl_unlock(); + rtnl_lock(); + kref_get(&bus->kref); + bus->upstream_ops = ops; + bus->upstream = upstream; + + if (bus->sfp) { + ret = sfp_register_bus(bus); + if (ret) + sfp_upstream_clear(bus); + } else { + ret = 0; } + rtnl_unlock(); - if (ret) { + if (ret) sfp_bus_put(bus); - bus = NULL; - } - return bus; + return ret; } -EXPORT_SYMBOL_GPL(sfp_register_upstream); +EXPORT_SYMBOL_GPL(sfp_bus_add_upstream); /** - * sfp_unregister_upstream() - Unregister sfp bus + * sfp_bus_del_upstream() - Delete a sfp bus * @bus: a pointer to the &struct sfp_bus structure for the sfp module * - * Unregister a previously registered upstream connection for the SFP - * module. @bus is returned from sfp_register_upstream(). + * Delete a previously registered upstream connection for the SFP + * module. @bus should have been added by sfp_bus_add_upstream(). */ -void sfp_unregister_upstream(struct sfp_bus *bus) +void sfp_bus_del_upstream(struct sfp_bus *bus) { - rtnl_lock(); - if (bus->sfp) - sfp_unregister_bus(bus); - sfp_upstream_clear(bus); - rtnl_unlock(); + if (bus) { + rtnl_lock(); + if (bus->sfp) + sfp_unregister_bus(bus); + sfp_upstream_clear(bus); + rtnl_unlock(); - sfp_bus_put(bus); + sfp_bus_put(bus); + } } -EXPORT_SYMBOL_GPL(sfp_unregister_upstream); +EXPORT_SYMBOL_GPL(sfp_bus_del_upstream); /* Socket driver entry points */ int sfp_add_phy(struct sfp_bus *bus, struct phy_device *phydev) @@ -553,6 +685,8 @@ int sfp_module_insert(struct sfp_bus *bus, const struct sfp_eeprom_id *id) const struct sfp_upstream_ops *ops = sfp_get_upstream_ops(bus); int ret = 0; + bus->sfp_quirk = sfp_lookup_quirk(id); + if (ops && ops->module_insert) ret = ops->module_insert(bus->upstream, id); @@ -566,6 +700,8 @@ void sfp_module_remove(struct sfp_bus *bus) if (ops && ops->module_remove) ops->module_remove(bus->upstream); + + bus->sfp_quirk = NULL; } EXPORT_SYMBOL_GPL(sfp_module_remove); diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index 272d5773573e..27360d1840b2 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -36,6 +36,8 @@ enum { SFP_E_INSERT = 0, SFP_E_REMOVE, + SFP_E_DEV_ATTACH, + SFP_E_DEV_DETACH, SFP_E_DEV_DOWN, SFP_E_DEV_UP, SFP_E_TX_FAULT, @@ -45,16 +47,21 @@ enum { SFP_E_TIMEOUT, SFP_MOD_EMPTY = 0, + SFP_MOD_ERROR, SFP_MOD_PROBE, + SFP_MOD_WAITDEV, SFP_MOD_HPOWER, + SFP_MOD_WAITPWR, SFP_MOD_PRESENT, - SFP_MOD_ERROR, - SFP_DEV_DOWN = 0, + SFP_DEV_DETACHED = 0, + SFP_DEV_DOWN, SFP_DEV_UP, SFP_S_DOWN = 0, + SFP_S_WAIT, SFP_S_INIT, + SFP_S_INIT_TX_FAULT, SFP_S_WAIT_LOS, SFP_S_LINK_UP, SFP_S_TX_FAULT, @@ -64,10 +71,12 @@ enum { static const char * const mod_state_strings[] = { [SFP_MOD_EMPTY] = "empty", + [SFP_MOD_ERROR] = "error", [SFP_MOD_PROBE] = "probe", + [SFP_MOD_WAITDEV] = "waitdev", [SFP_MOD_HPOWER] = "hpower", + [SFP_MOD_WAITPWR] = "waitpwr", [SFP_MOD_PRESENT] = "present", - [SFP_MOD_ERROR] = "error", }; static const char *mod_state_to_str(unsigned short mod_state) @@ -78,6 +87,7 @@ static const char *mod_state_to_str(unsigned short mod_state) } static const char * const dev_state_strings[] = { + [SFP_DEV_DETACHED] = "detached", [SFP_DEV_DOWN] = "down", [SFP_DEV_UP] = "up", }; @@ -92,6 +102,8 @@ static const char *dev_state_to_str(unsigned short dev_state) static const char * const event_strings[] = { [SFP_E_INSERT] = "insert", [SFP_E_REMOVE] = "remove", + [SFP_E_DEV_ATTACH] = "dev_attach", + [SFP_E_DEV_DETACH] = "dev_detach", [SFP_E_DEV_DOWN] = "dev_down", [SFP_E_DEV_UP] = "dev_up", [SFP_E_TX_FAULT] = "tx_fault", @@ -110,7 +122,9 @@ static const char *event_to_str(unsigned short event) static const char * const sm_state_strings[] = { [SFP_S_DOWN] = "down", + [SFP_S_WAIT] = "wait", [SFP_S_INIT] = "init", + [SFP_S_INIT_TX_FAULT] = "init_tx_fault", [SFP_S_WAIT_LOS] = "wait_los", [SFP_S_LINK_UP] = "link_up", [SFP_S_TX_FAULT] = "tx_fault", @@ -141,30 +155,40 @@ static const enum gpiod_flags gpio_flags[] = { GPIOD_ASIS, }; -#define T_INIT_JIFFIES msecs_to_jiffies(300) -#define T_RESET_US 10 -#define T_FAULT_RECOVER msecs_to_jiffies(1000) +/* t_start_up (SFF-8431) or t_init (SFF-8472) is the time required for a + * non-cooled module to initialise its laser safety circuitry. We wait + * an initial T_WAIT period before we check the tx fault to give any PHY + * on board (for a copper SFP) time to initialise. + */ +#define T_WAIT msecs_to_jiffies(50) +#define T_START_UP msecs_to_jiffies(300) +#define T_START_UP_BAD_GPON msecs_to_jiffies(60000) + +/* t_reset is the time required to assert the TX_DISABLE signal to reset + * an indicated TX_FAULT. + */ +#define T_RESET_US 10 +#define T_FAULT_RECOVER msecs_to_jiffies(1000) /* SFP module presence detection is poor: the three MOD DEF signals are * the same length on the PCB, which means it's possible for MOD DEF 0 to * connect before the I2C bus on MOD DEF 1/2. * - * The SFP MSA specifies 300ms as t_init (the time taken for TX_FAULT to - * be deasserted) but makes no mention of the earliest time before we can - * access the I2C EEPROM. However, Avago modules require 300ms. + * The SFF-8472 specifies t_serial ("Time from power on until module is + * ready for data transmission over the two wire serial bus.") as 300ms. */ -#define T_PROBE_INIT msecs_to_jiffies(300) -#define T_HPOWER_LEVEL msecs_to_jiffies(300) -#define T_PROBE_RETRY msecs_to_jiffies(100) +#define T_SERIAL msecs_to_jiffies(300) +#define T_HPOWER_LEVEL msecs_to_jiffies(300) +#define T_PROBE_RETRY_INIT msecs_to_jiffies(100) +#define R_PROBE_RETRY_INIT 10 +#define T_PROBE_RETRY_SLOW msecs_to_jiffies(5000) +#define R_PROBE_RETRY_SLOW 12 /* SFP modules appear to always have their PHY configured for bus address * 0x56 (which with mdio-i2c, translates to a PHY address of 22). */ #define SFP_PHY_ADDR 22 -/* Give this long for the PHY to reset. */ -#define T_PHY_RESET_MS 50 - struct sff_data { unsigned int gpios; bool (*module_supported)(const struct sfp_eeprom_id *id); @@ -187,20 +211,29 @@ struct sfp { struct gpio_desc *gpio[GPIO_MAX]; int gpio_irq[GPIO_MAX]; - bool attached; + bool need_poll; + struct mutex st_mutex; /* Protects state */ + unsigned int state_soft_mask; unsigned int state; struct delayed_work poll; struct delayed_work timeout; struct mutex sm_mutex; /* Protects state machine */ unsigned char sm_mod_state; + unsigned char sm_mod_tries_init; + unsigned char sm_mod_tries; unsigned char sm_dev_state; unsigned short sm_state; unsigned int sm_retries; struct sfp_eeprom_id id; + unsigned int module_power_mW; + unsigned int module_t_start_up; + #if IS_ENABLED(CONFIG_HWMON) struct sfp_diag diag; + struct delayed_work hwmon_probe; + unsigned int hwmon_tries; struct device *hwmon_dev; char *hwmon_name; #endif @@ -376,24 +409,90 @@ static int sfp_i2c_configure(struct sfp *sfp, struct i2c_adapter *i2c) } /* Interface */ -static unsigned int sfp_get_state(struct sfp *sfp) +static int sfp_read(struct sfp *sfp, bool a2, u8 addr, void *buf, size_t len) { - return sfp->get_state(sfp); + return sfp->read(sfp, a2, addr, buf, len); } -static void sfp_set_state(struct sfp *sfp, unsigned int state) +static int sfp_write(struct sfp *sfp, bool a2, u8 addr, void *buf, size_t len) { - sfp->set_state(sfp, state); + return sfp->write(sfp, a2, addr, buf, len); } -static int sfp_read(struct sfp *sfp, bool a2, u8 addr, void *buf, size_t len) +static unsigned int sfp_soft_get_state(struct sfp *sfp) { - return sfp->read(sfp, a2, addr, buf, len); + unsigned int state = 0; + u8 status; + + if (sfp_read(sfp, true, SFP_STATUS, &status, sizeof(status)) == + sizeof(status)) { + if (status & SFP_STATUS_RX_LOS) + state |= SFP_F_LOS; + if (status & SFP_STATUS_TX_FAULT) + state |= SFP_F_TX_FAULT; + } + + return state & sfp->state_soft_mask; } -static int sfp_write(struct sfp *sfp, bool a2, u8 addr, void *buf, size_t len) +static void sfp_soft_set_state(struct sfp *sfp, unsigned int state) { - return sfp->write(sfp, a2, addr, buf, len); + u8 status; + + if (sfp_read(sfp, true, SFP_STATUS, &status, sizeof(status)) == + sizeof(status)) { + if (state & SFP_F_TX_DISABLE) + status |= SFP_STATUS_TX_DISABLE_FORCE; + else + status &= ~SFP_STATUS_TX_DISABLE_FORCE; + + sfp_write(sfp, true, SFP_STATUS, &status, sizeof(status)); + } +} + +static void sfp_soft_start_poll(struct sfp *sfp) +{ + const struct sfp_eeprom_id *id = &sfp->id; + + sfp->state_soft_mask = 0; + if (id->ext.enhopts & SFP_ENHOPTS_SOFT_TX_DISABLE && + !sfp->gpio[GPIO_TX_DISABLE]) + sfp->state_soft_mask |= SFP_F_TX_DISABLE; + if (id->ext.enhopts & SFP_ENHOPTS_SOFT_TX_FAULT && + !sfp->gpio[GPIO_TX_FAULT]) + sfp->state_soft_mask |= SFP_F_TX_FAULT; + if (id->ext.enhopts & SFP_ENHOPTS_SOFT_RX_LOS && + !sfp->gpio[GPIO_LOS]) + sfp->state_soft_mask |= SFP_F_LOS; + + if (sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT) && + !sfp->need_poll) + mod_delayed_work(system_wq, &sfp->poll, poll_jiffies); +} + +static void sfp_soft_stop_poll(struct sfp *sfp) +{ + sfp->state_soft_mask = 0; +} + +static unsigned int sfp_get_state(struct sfp *sfp) +{ + unsigned int state = sfp->get_state(sfp); + + if (state & SFP_F_PRESENT && + sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT)) + state |= sfp_soft_get_state(sfp); + + return state; +} + +static void sfp_set_state(struct sfp *sfp, unsigned int state) +{ + sfp->set_state(sfp, state); + + if (state & SFP_F_PRESENT && + sfp->state_soft_mask & SFP_F_TX_DISABLE) + sfp_soft_set_state(sfp, state); } static unsigned int sfp_check(void *buf, size_t len) @@ -1142,29 +1241,27 @@ static const struct hwmon_chip_info sfp_hwmon_chip_info = { .info = sfp_hwmon_info, }; -static int sfp_hwmon_insert(struct sfp *sfp) +static void sfp_hwmon_probe(struct work_struct *work) { + struct sfp *sfp = container_of(work, struct sfp, hwmon_probe.work); int err, i; - if (sfp->id.ext.sff8472_compliance == SFP_SFF8472_COMPLIANCE_NONE) - return 0; - - if (!(sfp->id.ext.diagmon & SFP_DIAGMON_DDM)) - return 0; - - if (sfp->id.ext.diagmon & SFP_DIAGMON_ADDRMODE) - /* This driver in general does not support address - * change. - */ - return 0; - err = sfp_read(sfp, true, 0, &sfp->diag, sizeof(sfp->diag)); - if (err < 0) - return err; + if (err < 0) { + if (sfp->hwmon_tries--) { + mod_delayed_work(system_wq, &sfp->hwmon_probe, + T_PROBE_RETRY_SLOW); + } else { + dev_warn(sfp->dev, "hwmon probe failed: %d\n", err); + } + return; + } sfp->hwmon_name = kstrdup(dev_name(sfp->dev), GFP_KERNEL); - if (!sfp->hwmon_name) - return -ENODEV; + if (!sfp->hwmon_name) { + dev_err(sfp->dev, "out of memory for hwmon name\n"); + return; + } for (i = 0; sfp->hwmon_name[i]; i++) if (hwmon_is_bad_char(sfp->hwmon_name[i])) @@ -1174,18 +1271,52 @@ static int sfp_hwmon_insert(struct sfp *sfp) sfp->hwmon_name, sfp, &sfp_hwmon_chip_info, NULL); + if (IS_ERR(sfp->hwmon_dev)) + dev_err(sfp->dev, "failed to register hwmon device: %ld\n", + PTR_ERR(sfp->hwmon_dev)); +} + +static int sfp_hwmon_insert(struct sfp *sfp) +{ + if (sfp->id.ext.sff8472_compliance == SFP_SFF8472_COMPLIANCE_NONE) + return 0; + + if (!(sfp->id.ext.diagmon & SFP_DIAGMON_DDM)) + return 0; + + if (sfp->id.ext.diagmon & SFP_DIAGMON_ADDRMODE) + /* This driver in general does not support address + * change. + */ + return 0; - return PTR_ERR_OR_ZERO(sfp->hwmon_dev); + mod_delayed_work(system_wq, &sfp->hwmon_probe, 1); + sfp->hwmon_tries = R_PROBE_RETRY_SLOW; + + return 0; } static void sfp_hwmon_remove(struct sfp *sfp) { + cancel_delayed_work_sync(&sfp->hwmon_probe); if (!IS_ERR_OR_NULL(sfp->hwmon_dev)) { hwmon_device_unregister(sfp->hwmon_dev); sfp->hwmon_dev = NULL; kfree(sfp->hwmon_name); } } + +static int sfp_hwmon_init(struct sfp *sfp) +{ + INIT_DELAYED_WORK(&sfp->hwmon_probe, sfp_hwmon_probe); + + return 0; +} + +static void sfp_hwmon_exit(struct sfp *sfp) +{ + cancel_delayed_work_sync(&sfp->hwmon_probe); +} #else static int sfp_hwmon_insert(struct sfp *sfp) { @@ -1195,6 +1326,15 @@ static int sfp_hwmon_insert(struct sfp *sfp) static void sfp_hwmon_remove(struct sfp *sfp) { } + +static int sfp_hwmon_init(struct sfp *sfp) +{ + return 0; +} + +static void sfp_hwmon_exit(struct sfp *sfp) +{ +} #endif /* Helpers */ @@ -1245,7 +1385,7 @@ static void sfp_sm_next(struct sfp *sfp, unsigned int state, sfp_sm_set_timer(sfp, timeout); } -static void sfp_sm_ins_next(struct sfp *sfp, unsigned int state, +static void sfp_sm_mod_next(struct sfp *sfp, unsigned int state, unsigned int timeout) { sfp->sm_mod_state = state; @@ -1266,8 +1406,6 @@ static void sfp_sm_probe_phy(struct sfp *sfp) struct phy_device *phy; int err; - msleep(T_PHY_RESET_MS); - phy = mdiobus_scan(sfp->i2c_mii, SFP_PHY_ADDR); if (phy == ERR_PTR(-ENODEV)) { dev_info(sfp->dev, "no PHY detected\n"); @@ -1335,7 +1473,7 @@ static bool sfp_los_event_inactive(struct sfp *sfp, unsigned int event) event == SFP_E_LOS_LOW); } -static void sfp_sm_fault(struct sfp *sfp, bool warn) +static void sfp_sm_fault(struct sfp *sfp, unsigned int next_state, bool warn) { if (sfp->sm_retries && !--sfp->sm_retries) { dev_err(sfp->dev, @@ -1345,21 +1483,12 @@ static void sfp_sm_fault(struct sfp *sfp, bool warn) if (warn) dev_err(sfp->dev, "module transmit fault indicated\n"); - sfp_sm_next(sfp, SFP_S_TX_FAULT, T_FAULT_RECOVER); + sfp_sm_next(sfp, next_state, T_FAULT_RECOVER); } } -static void sfp_sm_mod_init(struct sfp *sfp) +static void sfp_sm_probe_for_phy(struct sfp *sfp) { - sfp_module_tx_enable(sfp); - - /* Wait t_init before indicating that the link is up, provided the - * current state indicates no TX_FAULT. If TX_FAULT clears before - * this time, that's fine too. - */ - sfp_sm_next(sfp, SFP_S_INIT, T_INIT_JIFFIES); - sfp->sm_retries = 5; - /* Setting the serdes link mode is guesswork: there's no * field in the EEPROM which indicates what mode should * be used. @@ -1375,69 +1504,83 @@ static void sfp_sm_mod_init(struct sfp *sfp) sfp_sm_probe_phy(sfp); } -static int sfp_sm_mod_hpower(struct sfp *sfp) +static int sfp_module_parse_power(struct sfp *sfp) { - u32 power; - u8 val; - int err; + u32 power_mW = 1000; - power = 1000; if (sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_POWER_DECL)) - power = 1500; + power_mW = 1500; if (sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_HIGH_POWER_LEVEL)) - power = 2000; - - if (sfp->id.ext.sff8472_compliance == SFP_SFF8472_COMPLIANCE_NONE && - (sfp->id.ext.diagmon & (SFP_DIAGMON_DDM | SFP_DIAGMON_ADDRMODE)) != - SFP_DIAGMON_DDM) { - /* The module appears not to implement bus address 0xa2, - * or requires an address change sequence, so assume that - * the module powers up in the indicated power mode. - */ - if (power > sfp->max_power_mW) { + power_mW = 2000; + + if (power_mW > sfp->max_power_mW) { + /* Module power specification exceeds the allowed maximum. */ + if (sfp->id.ext.sff8472_compliance == + SFP_SFF8472_COMPLIANCE_NONE && + !(sfp->id.ext.diagmon & SFP_DIAGMON_DDM)) { + /* The module appears not to implement bus address + * 0xa2, so assume that the module powers up in the + * indicated mode. + */ dev_err(sfp->dev, "Host does not support %u.%uW modules\n", - power / 1000, (power / 100) % 10); + power_mW / 1000, (power_mW / 100) % 10); return -EINVAL; + } else { + dev_warn(sfp->dev, + "Host does not support %u.%uW modules, module left in power mode 1\n", + power_mW / 1000, (power_mW / 100) % 10); + return 0; } - return 0; } - if (power > sfp->max_power_mW) { + /* If the module requires a higher power mode, but also requires + * an address change sequence, warn the user that the module may + * not be functional. + */ + if (sfp->id.ext.diagmon & SFP_DIAGMON_ADDRMODE && power_mW > 1000) { dev_warn(sfp->dev, - "Host does not support %u.%uW modules, module left in power mode 1\n", - power / 1000, (power / 100) % 10); + "Address Change Sequence not supported but module requires %u.%uW, module may not be functional\n", + power_mW / 1000, (power_mW / 100) % 10); return 0; } - if (power <= 1000) - return 0; + sfp->module_power_mW = power_mW; + + return 0; +} + +static int sfp_sm_mod_hpower(struct sfp *sfp, bool enable) +{ + u8 val; + int err; err = sfp_read(sfp, true, SFP_EXT_STATUS, &val, sizeof(val)); if (err != sizeof(val)) { dev_err(sfp->dev, "Failed to read EEPROM: %d\n", err); - err = -EAGAIN; - goto err; + return -EAGAIN; } - val |= BIT(0); + if (enable) + val |= BIT(0); + else + val &= ~BIT(0); err = sfp_write(sfp, true, SFP_EXT_STATUS, &val, sizeof(val)); if (err != sizeof(val)) { dev_err(sfp->dev, "Failed to write EEPROM: %d\n", err); - err = -EAGAIN; - goto err; + return -EAGAIN; } - dev_info(sfp->dev, "Module switched to %u.%uW power level\n", - power / 1000, (power / 100) % 10); - return T_HPOWER_LEVEL; + if (enable) + dev_info(sfp->dev, "Module switched to %u.%uW power level\n", + sfp->module_power_mW / 1000, + (sfp->module_power_mW / 100) % 10); -err: - return err; + return 0; } -static int sfp_sm_mod_probe(struct sfp *sfp) +static int sfp_sm_mod_probe(struct sfp *sfp, bool report) { /* SFP module inserted - read I2C data */ struct sfp_eeprom_id id; @@ -1447,7 +1590,8 @@ static int sfp_sm_mod_probe(struct sfp *sfp) ret = sfp_read(sfp, false, 0, &id, sizeof(id)); if (ret < 0) { - dev_err(sfp->dev, "failed to read EEPROM: %d\n", ret); + if (report) + dev_err(sfp->dev, "failed to read EEPROM: %d\n", ret); return -EAGAIN; } @@ -1505,7 +1649,7 @@ static int sfp_sm_mod_probe(struct sfp *sfp) (int)sizeof(id.ext.datecode), id.ext.datecode); /* Check whether we support this module */ - if (!sfp->type->module_supported(&sfp->id)) { + if (!sfp->type->module_supported(&id)) { dev_err(sfp->dev, "module is not supported - phys id 0x%02x 0x%02x\n", sfp->id.base.phys_id, sfp->id.base.phys_ext_id); @@ -1517,105 +1661,174 @@ static int sfp_sm_mod_probe(struct sfp *sfp) dev_warn(sfp->dev, "module address swap to access page 0xA2 is not supported.\n"); - ret = sfp_hwmon_insert(sfp); + /* Parse the module power requirement */ + ret = sfp_module_parse_power(sfp); if (ret < 0) return ret; - ret = sfp_module_insert(sfp->sfp_bus, &sfp->id); - if (ret < 0) - return ret; + if (!memcmp(id.base.vendor_name, "ALCATELLUCENT ", 16) && + !memcmp(id.base.vendor_pn, "3FE46541AA ", 16)) + sfp->module_t_start_up = T_START_UP_BAD_GPON; + else + sfp->module_t_start_up = T_START_UP; - return sfp_sm_mod_hpower(sfp); + return 0; } static void sfp_sm_mod_remove(struct sfp *sfp) { - sfp_module_remove(sfp->sfp_bus); + if (sfp->sm_mod_state > SFP_MOD_WAITDEV) + sfp_module_remove(sfp->sfp_bus); sfp_hwmon_remove(sfp); - if (sfp->mod_phy) - sfp_sm_phy_detach(sfp); - - sfp_module_tx_disable(sfp); - memset(&sfp->id, 0, sizeof(sfp->id)); + sfp->module_power_mW = 0; dev_info(sfp->dev, "module removed\n"); } -static void sfp_sm_event(struct sfp *sfp, unsigned int event) +/* This state machine tracks the upstream's state */ +static void sfp_sm_device(struct sfp *sfp, unsigned int event) { - mutex_lock(&sfp->sm_mutex); + switch (sfp->sm_dev_state) { + default: + if (event == SFP_E_DEV_ATTACH) + sfp->sm_dev_state = SFP_DEV_DOWN; + break; - dev_dbg(sfp->dev, "SM: enter %s:%s:%s event %s\n", - mod_state_to_str(sfp->sm_mod_state), - dev_state_to_str(sfp->sm_dev_state), - sm_state_to_str(sfp->sm_state), - event_to_str(event)); + case SFP_DEV_DOWN: + if (event == SFP_E_DEV_DETACH) + sfp->sm_dev_state = SFP_DEV_DETACHED; + else if (event == SFP_E_DEV_UP) + sfp->sm_dev_state = SFP_DEV_UP; + break; + + case SFP_DEV_UP: + if (event == SFP_E_DEV_DETACH) + sfp->sm_dev_state = SFP_DEV_DETACHED; + else if (event == SFP_E_DEV_DOWN) + sfp->sm_dev_state = SFP_DEV_DOWN; + break; + } +} + +/* This state machine tracks the insert/remove state of the module, probes + * the on-board EEPROM, and sets up the power level. + */ +static void sfp_sm_module(struct sfp *sfp, unsigned int event) +{ + int err; + + /* Handle remove event globally, it resets this state machine */ + if (event == SFP_E_REMOVE) { + if (sfp->sm_mod_state > SFP_MOD_PROBE) + sfp_sm_mod_remove(sfp); + sfp_sm_mod_next(sfp, SFP_MOD_EMPTY, 0); + return; + } + + /* Handle device detach globally */ + if (sfp->sm_dev_state < SFP_DEV_DOWN && + sfp->sm_mod_state > SFP_MOD_WAITDEV) { + if (sfp->module_power_mW > 1000 && + sfp->sm_mod_state > SFP_MOD_HPOWER) + sfp_sm_mod_hpower(sfp, false); + sfp_sm_mod_next(sfp, SFP_MOD_WAITDEV, 0); + return; + } - /* This state machine tracks the insert/remove state of - * the module, and handles probing the on-board EEPROM. - */ switch (sfp->sm_mod_state) { default: - if (event == SFP_E_INSERT && sfp->attached) { - sfp_module_tx_disable(sfp); - sfp_sm_ins_next(sfp, SFP_MOD_PROBE, T_PROBE_INIT); + if (event == SFP_E_INSERT) { + sfp_sm_mod_next(sfp, SFP_MOD_PROBE, T_SERIAL); + sfp->sm_mod_tries_init = R_PROBE_RETRY_INIT; + sfp->sm_mod_tries = R_PROBE_RETRY_SLOW; } break; case SFP_MOD_PROBE: - if (event == SFP_E_REMOVE) { - sfp_sm_ins_next(sfp, SFP_MOD_EMPTY, 0); - } else if (event == SFP_E_TIMEOUT) { - int val = sfp_sm_mod_probe(sfp); - - if (val == 0) - sfp_sm_ins_next(sfp, SFP_MOD_PRESENT, 0); - else if (val > 0) - sfp_sm_ins_next(sfp, SFP_MOD_HPOWER, val); - else if (val != -EAGAIN) - sfp_sm_ins_next(sfp, SFP_MOD_ERROR, 0); - else - sfp_sm_set_timer(sfp, T_PROBE_RETRY); + /* Wait for T_PROBE_INIT to time out */ + if (event != SFP_E_TIMEOUT) + break; + + err = sfp_sm_mod_probe(sfp, sfp->sm_mod_tries == 1); + if (err == -EAGAIN) { + if (sfp->sm_mod_tries_init && + --sfp->sm_mod_tries_init) { + sfp_sm_set_timer(sfp, T_PROBE_RETRY_INIT); + break; + } else if (sfp->sm_mod_tries && --sfp->sm_mod_tries) { + if (sfp->sm_mod_tries == R_PROBE_RETRY_SLOW - 1) + dev_warn(sfp->dev, + "please wait, module slow to respond\n"); + sfp_sm_set_timer(sfp, T_PROBE_RETRY_SLOW); + break; + } + } + if (err < 0) { + sfp_sm_mod_next(sfp, SFP_MOD_ERROR, 0); + break; } - break; - case SFP_MOD_HPOWER: - if (event == SFP_E_TIMEOUT) { - sfp_sm_ins_next(sfp, SFP_MOD_PRESENT, 0); + err = sfp_hwmon_insert(sfp); + if (err) + dev_warn(sfp->dev, "hwmon probe failed: %d\n", err); + + sfp_sm_mod_next(sfp, SFP_MOD_WAITDEV, 0); + /* fall through */ + case SFP_MOD_WAITDEV: + /* Ensure that the device is attached before proceeding */ + if (sfp->sm_dev_state < SFP_DEV_DOWN) + break; + + /* Report the module insertion to the upstream device */ + err = sfp_module_insert(sfp->sfp_bus, &sfp->id); + if (err < 0) { + sfp_sm_mod_next(sfp, SFP_MOD_ERROR, 0); break; } - /* fallthrough */ - case SFP_MOD_PRESENT: - case SFP_MOD_ERROR: - if (event == SFP_E_REMOVE) { - sfp_sm_mod_remove(sfp); - sfp_sm_ins_next(sfp, SFP_MOD_EMPTY, 0); + + /* If this is a power level 1 module, we are done */ + if (sfp->module_power_mW <= 1000) + goto insert; + + sfp_sm_mod_next(sfp, SFP_MOD_HPOWER, 0); + /* fall through */ + case SFP_MOD_HPOWER: + /* Enable high power mode */ + err = sfp_sm_mod_hpower(sfp, true); + if (err < 0) { + if (err != -EAGAIN) { + sfp_module_remove(sfp->sfp_bus); + sfp_sm_mod_next(sfp, SFP_MOD_ERROR, 0); + } else { + sfp_sm_set_timer(sfp, T_PROBE_RETRY_INIT); + } + break; } + + sfp_sm_mod_next(sfp, SFP_MOD_WAITPWR, T_HPOWER_LEVEL); break; - } - /* This state machine tracks the netdev up/down state */ - switch (sfp->sm_dev_state) { - default: - if (event == SFP_E_DEV_UP) - sfp->sm_dev_state = SFP_DEV_UP; + case SFP_MOD_WAITPWR: + /* Wait for T_HPOWER_LEVEL to time out */ + if (event != SFP_E_TIMEOUT) + break; + + insert: + sfp_sm_mod_next(sfp, SFP_MOD_PRESENT, 0); break; - case SFP_DEV_UP: - if (event == SFP_E_DEV_DOWN) { - /* If the module has a PHY, avoid raising TX disable - * as this resets the PHY. Otherwise, raise it to - * turn the laser off. - */ - if (!sfp->mod_phy) - sfp_module_tx_disable(sfp); - sfp->sm_dev_state = SFP_DEV_DOWN; - } + case SFP_MOD_PRESENT: + case SFP_MOD_ERROR: break; } +} + +static void sfp_sm_main(struct sfp *sfp, unsigned int event) +{ + unsigned long timeout; /* Some events are global */ if (sfp->sm_state != SFP_S_DOWN && @@ -1626,29 +1839,88 @@ static void sfp_sm_event(struct sfp *sfp, unsigned int event) sfp_sm_link_down(sfp); if (sfp->mod_phy) sfp_sm_phy_detach(sfp); + sfp_module_tx_disable(sfp); + sfp_soft_stop_poll(sfp); sfp_sm_next(sfp, SFP_S_DOWN, 0); - mutex_unlock(&sfp->sm_mutex); return; } /* The main state machine */ switch (sfp->sm_state) { case SFP_S_DOWN: - if (sfp->sm_mod_state == SFP_MOD_PRESENT && - sfp->sm_dev_state == SFP_DEV_UP) - sfp_sm_mod_init(sfp); + if (sfp->sm_mod_state != SFP_MOD_PRESENT || + sfp->sm_dev_state != SFP_DEV_UP) + break; + + if (!(sfp->id.ext.diagmon & SFP_DIAGMON_ADDRMODE)) + sfp_soft_start_poll(sfp); + + sfp_module_tx_enable(sfp); + + /* Initialise the fault clearance retries */ + sfp->sm_retries = 5; + + /* We need to check the TX_FAULT state, which is not defined + * while TX_DISABLE is asserted. The earliest we want to do + * anything (such as probe for a PHY) is 50ms. + */ + sfp_sm_next(sfp, SFP_S_WAIT, T_WAIT); + break; + + case SFP_S_WAIT: + if (event != SFP_E_TIMEOUT) + break; + + if (sfp->state & SFP_F_TX_FAULT) { + /* Wait up to t_init (SFF-8472) or t_start_up (SFF-8431) + * from the TX_DISABLE deassertion for the module to + * initialise, which is indicated by TX_FAULT + * deasserting. + */ + timeout = sfp->module_t_start_up; + if (timeout > T_WAIT) + timeout -= T_WAIT; + else + timeout = 1; + + sfp_sm_next(sfp, SFP_S_INIT, timeout); + } else { + /* TX_FAULT is not asserted, assume the module has + * finished initialising. + */ + goto init_done; + } break; case SFP_S_INIT: - if (event == SFP_E_TIMEOUT && sfp->state & SFP_F_TX_FAULT) - sfp_sm_fault(sfp, true); - else if (event == SFP_E_TIMEOUT || event == SFP_E_TX_CLEAR) + if (event == SFP_E_TIMEOUT && sfp->state & SFP_F_TX_FAULT) { + /* TX_FAULT is still asserted after t_init or + * or t_start_up, so assume there is a fault. + */ + sfp_sm_fault(sfp, SFP_S_INIT_TX_FAULT, + sfp->sm_retries == 5); + } else if (event == SFP_E_TIMEOUT || event == SFP_E_TX_CLEAR) { + init_done: /* TX_FAULT deasserted or we timed out with TX_FAULT + * clear. Probe for the PHY and check the LOS state. + */ + sfp_sm_probe_for_phy(sfp); sfp_sm_link_check_los(sfp); + + /* Reset the fault retry count */ + sfp->sm_retries = 5; + } + break; + + case SFP_S_INIT_TX_FAULT: + if (event == SFP_E_TIMEOUT) { + sfp_module_tx_fault_reset(sfp); + sfp_sm_next(sfp, SFP_S_INIT, sfp->module_t_start_up); + } break; case SFP_S_WAIT_LOS: if (event == SFP_E_TX_FAULT) - sfp_sm_fault(sfp, true); + sfp_sm_fault(sfp, SFP_S_TX_FAULT, true); else if (sfp_los_event_inactive(sfp, event)) sfp_sm_link_up(sfp); break; @@ -1656,7 +1928,7 @@ static void sfp_sm_event(struct sfp *sfp, unsigned int event) case SFP_S_LINK_UP: if (event == SFP_E_TX_FAULT) { sfp_sm_link_down(sfp); - sfp_sm_fault(sfp, true); + sfp_sm_fault(sfp, SFP_S_TX_FAULT, true); } else if (sfp_los_event_active(sfp, event)) { sfp_sm_link_down(sfp); sfp_sm_next(sfp, SFP_S_WAIT_LOS, 0); @@ -1666,13 +1938,13 @@ static void sfp_sm_event(struct sfp *sfp, unsigned int event) case SFP_S_TX_FAULT: if (event == SFP_E_TIMEOUT) { sfp_module_tx_fault_reset(sfp); - sfp_sm_next(sfp, SFP_S_REINIT, T_INIT_JIFFIES); + sfp_sm_next(sfp, SFP_S_REINIT, sfp->module_t_start_up); } break; case SFP_S_REINIT: if (event == SFP_E_TIMEOUT && sfp->state & SFP_F_TX_FAULT) { - sfp_sm_fault(sfp, false); + sfp_sm_fault(sfp, SFP_S_TX_FAULT, false); } else if (event == SFP_E_TIMEOUT || event == SFP_E_TX_CLEAR) { dev_info(sfp->dev, "module transmit fault recovered\n"); sfp_sm_link_check_los(sfp); @@ -1682,6 +1954,21 @@ static void sfp_sm_event(struct sfp *sfp, unsigned int event) case SFP_S_TX_DISABLE: break; } +} + +static void sfp_sm_event(struct sfp *sfp, unsigned int event) +{ + mutex_lock(&sfp->sm_mutex); + + dev_dbg(sfp->dev, "SM: enter %s:%s:%s event %s\n", + mod_state_to_str(sfp->sm_mod_state), + dev_state_to_str(sfp->sm_dev_state), + sm_state_to_str(sfp->sm_state), + event_to_str(event)); + + sfp_sm_device(sfp, event); + sfp_sm_module(sfp, event); + sfp_sm_main(sfp, event); dev_dbg(sfp->dev, "SM: exit %s:%s:%s\n", mod_state_to_str(sfp->sm_mod_state), @@ -1693,15 +1980,12 @@ static void sfp_sm_event(struct sfp *sfp, unsigned int event) static void sfp_attach(struct sfp *sfp) { - sfp->attached = true; - if (sfp->state & SFP_F_PRESENT) - sfp_sm_event(sfp, SFP_E_INSERT); + sfp_sm_event(sfp, SFP_E_DEV_ATTACH); } static void sfp_detach(struct sfp *sfp) { - sfp->attached = false; - sfp_sm_event(sfp, SFP_E_REMOVE); + sfp_sm_event(sfp, SFP_E_DEV_DETACH); } static void sfp_start(struct sfp *sfp) @@ -1828,7 +2112,10 @@ static void sfp_poll(struct work_struct *work) struct sfp *sfp = container_of(work, struct sfp, poll.work); sfp_check_state(sfp); - mod_delayed_work(system_wq, &sfp->poll, poll_jiffies); + + if (sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT) || + sfp->need_poll) + mod_delayed_work(system_wq, &sfp->poll, poll_jiffies); } static struct sfp *sfp_alloc(struct device *dev) @@ -1846,6 +2133,8 @@ static struct sfp *sfp_alloc(struct device *dev) INIT_DELAYED_WORK(&sfp->poll, sfp_poll); INIT_DELAYED_WORK(&sfp->timeout, sfp_timeout); + sfp_hwmon_init(sfp); + return sfp; } @@ -1853,6 +2142,8 @@ static void sfp_cleanup(void *data) { struct sfp *sfp = data; + sfp_hwmon_exit(sfp); + cancel_delayed_work_sync(&sfp->poll); cancel_delayed_work_sync(&sfp->timeout); if (sfp->i2c_mii) { @@ -1869,7 +2160,6 @@ static int sfp_probe(struct platform_device *pdev) const struct sff_data *sff; struct i2c_adapter *i2c; struct sfp *sfp; - bool poll = false; int err, i; sfp = sfp_alloc(&pdev->dev); @@ -1964,6 +2254,11 @@ static int sfp_probe(struct platform_device *pdev) sfp->state |= SFP_F_RATE_SELECT; sfp_set_state(sfp, sfp->state); sfp_module_tx_disable(sfp); + if (sfp->state & SFP_F_PRESENT) { + rtnl_lock(); + sfp_sm_event(sfp, SFP_E_INSERT); + rtnl_unlock(); + } for (i = 0; i < GPIO_MAX; i++) { if (gpio_flags[i] != GPIOD_IN || !sfp->gpio[i]) @@ -1971,7 +2266,7 @@ static int sfp_probe(struct platform_device *pdev) sfp->gpio_irq[i] = gpiod_to_irq(sfp->gpio[i]); if (!sfp->gpio_irq[i]) { - poll = true; + sfp->need_poll = true; continue; } @@ -1983,11 +2278,11 @@ static int sfp_probe(struct platform_device *pdev) dev_name(sfp->dev), sfp); if (err) { sfp->gpio_irq[i] = 0; - poll = true; + sfp->need_poll = true; } } - if (poll) + if (sfp->need_poll) mod_delayed_work(system_wq, &sfp->poll, poll_jiffies); /* We could have an issue in cases no Tx disable pin is available or @@ -2012,6 +2307,10 @@ static int sfp_remove(struct platform_device *pdev) sfp_unregister_socket(sfp->sfp_bus); + rtnl_lock(); + sfp_sm_event(sfp, SFP_E_REMOVE); + rtnl_unlock(); + return 0; } diff --git a/include/linux/linkmode.h b/include/linux/linkmode.h index a99c58866860..fe740031339d 100644 --- a/include/linux/linkmode.h +++ b/include/linux/linkmode.h @@ -82,4 +82,10 @@ static inline int linkmode_equal(const unsigned long *src1, return bitmap_equal(src1, src2, __ETHTOOL_LINK_MODE_MASK_NBITS); } +static inline int linkmode_subset(const unsigned long *src1, + const unsigned long *src2) +{ + return bitmap_subset(src1, src2, __ETHTOOL_LINK_MODE_MASK_NBITS); +} + #endif /* __LINKMODE_H */ diff --git a/include/linux/phy.h b/include/linux/phy.h index 9a0e981df502..439e427334aa 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -203,6 +203,8 @@ static inline const char *phy_modes(phy_interface_t interface) struct device; struct phylink; +struct sfp_bus; +struct sfp_upstream_ops; struct sk_buff; /* @@ -342,6 +344,8 @@ struct phy_c45_device_ids { * dev_flags: Device-specific flags used by the PHY driver. * irq: IRQ number of the PHY's interrupt (-1 if none) * phy_timer: The timer for handling the state machine + * sfp_bus_attached: flag indicating whether the SFP bus has been attached + * sfp_bus: SFP bus attached to this PHY's fiber port * attached_dev: The attached enet driver's device instance ptr * adjust_link: Callback for the enet controller to respond to * changes in the link state. @@ -432,6 +436,9 @@ struct phy_device { struct mutex lock; + /* This may be modified under the rtnl lock */ + bool sfp_bus_attached; + struct sfp_bus *sfp_bus; struct phylink *phylink; struct net_device *attached_dev; @@ -501,6 +508,9 @@ struct phy_driver { int (*suspend)(struct phy_device *phydev); int (*resume)(struct phy_device *phydev); + int (*start)(struct phy_device *phydev); + void (*stop)(struct phy_device *phydev); + /* * Configures the advertisement and resets * autonegotiation if phydev->autoneg is on, @@ -1020,6 +1030,10 @@ int phy_suspend(struct phy_device *phydev); int phy_resume(struct phy_device *phydev); int __phy_resume(struct phy_device *phydev); int phy_loopback(struct phy_device *phydev, bool enable); +void phy_sfp_attach(void *upstream, struct sfp_bus *bus); +void phy_sfp_detach(void *upstream, struct sfp_bus *bus); +int phy_sfp_probe(struct phy_device *phydev, + const struct sfp_upstream_ops *ops); struct phy_device *phy_attach(struct net_device *dev, const char *bus_id, phy_interface_t interface); struct phy_device *phy_find_first(struct mii_bus *bus); @@ -1145,7 +1159,6 @@ void phy_queue_state_machine(struct phy_device *phydev, unsigned long jiffies); void phy_mac_interrupt(struct phy_device *phydev); void phy_start_machine(struct phy_device *phydev); void phy_stop_machine(struct phy_device *phydev); -int phy_ethtool_sset(struct phy_device *phydev, struct ethtool_cmd *cmd); void phy_ethtool_ksettings_get(struct phy_device *phydev, struct ethtool_link_ksettings *cmd); int phy_ethtool_ksettings_set(struct phy_device *phydev, diff --git a/include/linux/sfp.h b/include/linux/sfp.h index 1c35428e98bc..487fd9412d10 100644 --- a/include/linux/sfp.h +++ b/include/linux/sfp.h @@ -428,6 +428,10 @@ enum { SFP_TEC_CUR = 0x6c, SFP_STATUS = 0x6e, + SFP_STATUS_TX_DISABLE = BIT(7), + SFP_STATUS_TX_DISABLE_FORCE = BIT(6), + SFP_STATUS_TX_FAULT = BIT(2), + SFP_STATUS_RX_LOS = BIT(1), SFP_ALARM0 = 0x70, SFP_ALARM0_TEMP_HIGH = BIT(7), SFP_ALARM0_TEMP_LOW = BIT(6), @@ -508,10 +512,11 @@ int sfp_get_module_eeprom(struct sfp_bus *bus, struct ethtool_eeprom *ee, u8 *data); void sfp_upstream_start(struct sfp_bus *bus); void sfp_upstream_stop(struct sfp_bus *bus); -struct sfp_bus *sfp_register_upstream(struct fwnode_handle *fwnode, - void *upstream, - const struct sfp_upstream_ops *ops); -void sfp_unregister_upstream(struct sfp_bus *bus); +void sfp_bus_put(struct sfp_bus *bus); +struct sfp_bus *sfp_bus_find_fwnode(struct fwnode_handle *fwnode); +int sfp_bus_add_upstream(struct sfp_bus *bus, void *upstream, + const struct sfp_upstream_ops *ops); +void sfp_bus_del_upstream(struct sfp_bus *bus); #else static inline int sfp_parse_port(struct sfp_bus *bus, const struct sfp_eeprom_id *id, @@ -553,14 +558,22 @@ static inline void sfp_upstream_stop(struct sfp_bus *bus) { } -static inline struct sfp_bus *sfp_register_upstream( - struct fwnode_handle *fwnode, void *upstream, - const struct sfp_upstream_ops *ops) +static inline void sfp_bus_put(struct sfp_bus *bus) { - return (struct sfp_bus *)-1; } -static inline void sfp_unregister_upstream(struct sfp_bus *bus) +static inline struct sfp_bus *sfp_bus_find_fwnode(struct fwnode_handle *fwnode) +{ + return NULL; +} + +static inline int sfp_bus_add_upstream(struct sfp_bus *bus, void *upstream, + const struct sfp_upstream_ops *ops) +{ + return 0; +} + +static inline void sfp_bus_del_upstream(struct sfp_bus *bus) { } #endif