162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci#include <linux/export.h> 362306a36Sopenharmony_ci#include <linux/kref.h> 462306a36Sopenharmony_ci#include <linux/list.h> 562306a36Sopenharmony_ci#include <linux/mutex.h> 662306a36Sopenharmony_ci#include <linux/phylink.h> 762306a36Sopenharmony_ci#include <linux/property.h> 862306a36Sopenharmony_ci#include <linux/rtnetlink.h> 962306a36Sopenharmony_ci#include <linux/slab.h> 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include "sfp.h" 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci/** 1462306a36Sopenharmony_ci * struct sfp_bus - internal representation of a sfp bus 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_cistruct sfp_bus { 1762306a36Sopenharmony_ci /* private: */ 1862306a36Sopenharmony_ci struct kref kref; 1962306a36Sopenharmony_ci struct list_head node; 2062306a36Sopenharmony_ci const struct fwnode_handle *fwnode; 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci const struct sfp_socket_ops *socket_ops; 2362306a36Sopenharmony_ci struct device *sfp_dev; 2462306a36Sopenharmony_ci struct sfp *sfp; 2562306a36Sopenharmony_ci const struct sfp_quirk *sfp_quirk; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci const struct sfp_upstream_ops *upstream_ops; 2862306a36Sopenharmony_ci void *upstream; 2962306a36Sopenharmony_ci struct phy_device *phydev; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci bool registered; 3262306a36Sopenharmony_ci bool started; 3362306a36Sopenharmony_ci}; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/** 3662306a36Sopenharmony_ci * sfp_parse_port() - Parse the EEPROM base ID, setting the port type 3762306a36Sopenharmony_ci * @bus: a pointer to the &struct sfp_bus structure for the sfp module 3862306a36Sopenharmony_ci * @id: a pointer to the module's &struct sfp_eeprom_id 3962306a36Sopenharmony_ci * @support: optional pointer to an array of unsigned long for the 4062306a36Sopenharmony_ci * ethtool support mask 4162306a36Sopenharmony_ci * 4262306a36Sopenharmony_ci * Parse the EEPROM identification given in @id, and return one of 4362306a36Sopenharmony_ci * %PORT_TP, %PORT_FIBRE or %PORT_OTHER. If @support is non-%NULL, 4462306a36Sopenharmony_ci * also set the ethtool %ETHTOOL_LINK_MODE_xxx_BIT corresponding with 4562306a36Sopenharmony_ci * the connector type. 4662306a36Sopenharmony_ci * 4762306a36Sopenharmony_ci * If the port type is not known, returns %PORT_OTHER. 4862306a36Sopenharmony_ci */ 4962306a36Sopenharmony_ciint sfp_parse_port(struct sfp_bus *bus, const struct sfp_eeprom_id *id, 5062306a36Sopenharmony_ci unsigned long *support) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci int port; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci /* port is the physical connector, set this from the connector field. */ 5562306a36Sopenharmony_ci switch (id->base.connector) { 5662306a36Sopenharmony_ci case SFF8024_CONNECTOR_SC: 5762306a36Sopenharmony_ci case SFF8024_CONNECTOR_FIBERJACK: 5862306a36Sopenharmony_ci case SFF8024_CONNECTOR_LC: 5962306a36Sopenharmony_ci case SFF8024_CONNECTOR_MT_RJ: 6062306a36Sopenharmony_ci case SFF8024_CONNECTOR_MU: 6162306a36Sopenharmony_ci case SFF8024_CONNECTOR_OPTICAL_PIGTAIL: 6262306a36Sopenharmony_ci case SFF8024_CONNECTOR_MPO_1X12: 6362306a36Sopenharmony_ci case SFF8024_CONNECTOR_MPO_2X16: 6462306a36Sopenharmony_ci port = PORT_FIBRE; 6562306a36Sopenharmony_ci break; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci case SFF8024_CONNECTOR_RJ45: 6862306a36Sopenharmony_ci port = PORT_TP; 6962306a36Sopenharmony_ci break; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci case SFF8024_CONNECTOR_COPPER_PIGTAIL: 7262306a36Sopenharmony_ci port = PORT_DA; 7362306a36Sopenharmony_ci break; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci case SFF8024_CONNECTOR_UNSPEC: 7662306a36Sopenharmony_ci if (id->base.e1000_base_t) { 7762306a36Sopenharmony_ci port = PORT_TP; 7862306a36Sopenharmony_ci break; 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci fallthrough; 8162306a36Sopenharmony_ci case SFF8024_CONNECTOR_SG: /* guess */ 8262306a36Sopenharmony_ci case SFF8024_CONNECTOR_HSSDC_II: 8362306a36Sopenharmony_ci case SFF8024_CONNECTOR_NOSEPARATE: 8462306a36Sopenharmony_ci case SFF8024_CONNECTOR_MXC_2X16: 8562306a36Sopenharmony_ci port = PORT_OTHER; 8662306a36Sopenharmony_ci break; 8762306a36Sopenharmony_ci default: 8862306a36Sopenharmony_ci dev_warn(bus->sfp_dev, "SFP: unknown connector id 0x%02x\n", 8962306a36Sopenharmony_ci id->base.connector); 9062306a36Sopenharmony_ci port = PORT_OTHER; 9162306a36Sopenharmony_ci break; 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci if (support) { 9562306a36Sopenharmony_ci switch (port) { 9662306a36Sopenharmony_ci case PORT_FIBRE: 9762306a36Sopenharmony_ci phylink_set(support, FIBRE); 9862306a36Sopenharmony_ci break; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci case PORT_TP: 10162306a36Sopenharmony_ci phylink_set(support, TP); 10262306a36Sopenharmony_ci break; 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci return port; 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sfp_parse_port); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci/** 11162306a36Sopenharmony_ci * sfp_may_have_phy() - indicate whether the module may have a PHY 11262306a36Sopenharmony_ci * @bus: a pointer to the &struct sfp_bus structure for the sfp module 11362306a36Sopenharmony_ci * @id: a pointer to the module's &struct sfp_eeprom_id 11462306a36Sopenharmony_ci * 11562306a36Sopenharmony_ci * Parse the EEPROM identification given in @id, and return whether 11662306a36Sopenharmony_ci * this module may have a PHY. 11762306a36Sopenharmony_ci */ 11862306a36Sopenharmony_cibool sfp_may_have_phy(struct sfp_bus *bus, const struct sfp_eeprom_id *id) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci if (id->base.e1000_base_t) 12162306a36Sopenharmony_ci return true; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci if (id->base.phys_id != SFF8024_ID_DWDM_SFP) { 12462306a36Sopenharmony_ci switch (id->base.extended_cc) { 12562306a36Sopenharmony_ci case SFF8024_ECC_10GBASE_T_SFI: 12662306a36Sopenharmony_ci case SFF8024_ECC_10GBASE_T_SR: 12762306a36Sopenharmony_ci case SFF8024_ECC_5GBASE_T: 12862306a36Sopenharmony_ci case SFF8024_ECC_2_5GBASE_T: 12962306a36Sopenharmony_ci return true; 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci return false; 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sfp_may_have_phy); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci/** 13862306a36Sopenharmony_ci * sfp_parse_support() - Parse the eeprom id for supported link modes 13962306a36Sopenharmony_ci * @bus: a pointer to the &struct sfp_bus structure for the sfp module 14062306a36Sopenharmony_ci * @id: a pointer to the module's &struct sfp_eeprom_id 14162306a36Sopenharmony_ci * @support: pointer to an array of unsigned long for the ethtool support mask 14262306a36Sopenharmony_ci * @interfaces: pointer to an array of unsigned long for phy interface modes 14362306a36Sopenharmony_ci * mask 14462306a36Sopenharmony_ci * 14562306a36Sopenharmony_ci * Parse the EEPROM identification information and derive the supported 14662306a36Sopenharmony_ci * ethtool link modes for the module. 14762306a36Sopenharmony_ci */ 14862306a36Sopenharmony_civoid sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id, 14962306a36Sopenharmony_ci unsigned long *support, unsigned long *interfaces) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci unsigned int br_min, br_nom, br_max; 15262306a36Sopenharmony_ci __ETHTOOL_DECLARE_LINK_MODE_MASK(modes) = { 0, }; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci phylink_set(modes, Autoneg); 15562306a36Sopenharmony_ci phylink_set(modes, Pause); 15662306a36Sopenharmony_ci phylink_set(modes, Asym_Pause); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci /* Decode the bitrate information to MBd */ 15962306a36Sopenharmony_ci br_min = br_nom = br_max = 0; 16062306a36Sopenharmony_ci if (id->base.br_nominal) { 16162306a36Sopenharmony_ci if (id->base.br_nominal != 255) { 16262306a36Sopenharmony_ci br_nom = id->base.br_nominal * 100; 16362306a36Sopenharmony_ci br_min = br_nom - id->base.br_nominal * id->ext.br_min; 16462306a36Sopenharmony_ci br_max = br_nom + id->base.br_nominal * id->ext.br_max; 16562306a36Sopenharmony_ci } else if (id->ext.br_max) { 16662306a36Sopenharmony_ci br_nom = 250 * id->ext.br_max; 16762306a36Sopenharmony_ci br_max = br_nom + br_nom * id->ext.br_min / 100; 16862306a36Sopenharmony_ci br_min = br_nom - br_nom * id->ext.br_min / 100; 16962306a36Sopenharmony_ci } 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci /* When using passive cables, in case neither BR,min nor BR,max 17262306a36Sopenharmony_ci * are specified, set br_min to 0 as the nominal value is then 17362306a36Sopenharmony_ci * used as the maximum. 17462306a36Sopenharmony_ci */ 17562306a36Sopenharmony_ci if (br_min == br_max && id->base.sfp_ct_passive) 17662306a36Sopenharmony_ci br_min = 0; 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci /* Set ethtool support from the compliance fields. */ 18062306a36Sopenharmony_ci if (id->base.e10g_base_sr) { 18162306a36Sopenharmony_ci phylink_set(modes, 10000baseSR_Full); 18262306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces); 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci if (id->base.e10g_base_lr) { 18562306a36Sopenharmony_ci phylink_set(modes, 10000baseLR_Full); 18662306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces); 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci if (id->base.e10g_base_lrm) { 18962306a36Sopenharmony_ci phylink_set(modes, 10000baseLRM_Full); 19062306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces); 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci if (id->base.e10g_base_er) { 19362306a36Sopenharmony_ci phylink_set(modes, 10000baseER_Full); 19462306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces); 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci if (id->base.e1000_base_sx || 19762306a36Sopenharmony_ci id->base.e1000_base_lx || 19862306a36Sopenharmony_ci id->base.e1000_base_cx) { 19962306a36Sopenharmony_ci phylink_set(modes, 1000baseX_Full); 20062306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_1000BASEX, interfaces); 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci if (id->base.e1000_base_t) { 20362306a36Sopenharmony_ci phylink_set(modes, 1000baseT_Half); 20462306a36Sopenharmony_ci phylink_set(modes, 1000baseT_Full); 20562306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_1000BASEX, interfaces); 20662306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_SGMII, interfaces); 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci /* 1000Base-PX or 1000Base-BX10 */ 21062306a36Sopenharmony_ci if ((id->base.e_base_px || id->base.e_base_bx10) && 21162306a36Sopenharmony_ci br_min <= 1300 && br_max >= 1200) { 21262306a36Sopenharmony_ci phylink_set(modes, 1000baseX_Full); 21362306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_1000BASEX, interfaces); 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci /* 100Base-FX, 100Base-LX, 100Base-PX, 100Base-BX10 */ 21762306a36Sopenharmony_ci if (id->base.e100_base_fx || id->base.e100_base_lx) { 21862306a36Sopenharmony_ci phylink_set(modes, 100baseFX_Full); 21962306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_100BASEX, interfaces); 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci if ((id->base.e_base_px || id->base.e_base_bx10) && br_nom == 100) { 22262306a36Sopenharmony_ci phylink_set(modes, 100baseFX_Full); 22362306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_100BASEX, interfaces); 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci /* For active or passive cables, select the link modes 22762306a36Sopenharmony_ci * based on the bit rates and the cable compliance bytes. 22862306a36Sopenharmony_ci */ 22962306a36Sopenharmony_ci if ((id->base.sfp_ct_passive || id->base.sfp_ct_active) && br_nom) { 23062306a36Sopenharmony_ci /* This may look odd, but some manufacturers use 12000MBd */ 23162306a36Sopenharmony_ci if (br_min <= 12000 && br_max >= 10300) { 23262306a36Sopenharmony_ci phylink_set(modes, 10000baseCR_Full); 23362306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces); 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci if (br_min <= 3200 && br_max >= 3100) { 23662306a36Sopenharmony_ci phylink_set(modes, 2500baseX_Full); 23762306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_2500BASEX, interfaces); 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci if (br_min <= 1300 && br_max >= 1200) { 24062306a36Sopenharmony_ci phylink_set(modes, 1000baseX_Full); 24162306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_1000BASEX, interfaces); 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci if (id->base.sfp_ct_passive) { 24562306a36Sopenharmony_ci if (id->base.passive.sff8431_app_e) { 24662306a36Sopenharmony_ci phylink_set(modes, 10000baseCR_Full); 24762306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces); 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci if (id->base.sfp_ct_active) { 25162306a36Sopenharmony_ci if (id->base.active.sff8431_app_e || 25262306a36Sopenharmony_ci id->base.active.sff8431_lim) { 25362306a36Sopenharmony_ci phylink_set(modes, 10000baseCR_Full); 25462306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces); 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci switch (id->base.extended_cc) { 25962306a36Sopenharmony_ci case SFF8024_ECC_UNSPEC: 26062306a36Sopenharmony_ci break; 26162306a36Sopenharmony_ci case SFF8024_ECC_100G_25GAUI_C2M_AOC: 26262306a36Sopenharmony_ci if (br_min <= 28000 && br_max >= 25000) { 26362306a36Sopenharmony_ci /* 25GBASE-R, possibly with FEC */ 26462306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_25GBASER, interfaces); 26562306a36Sopenharmony_ci /* There is currently no link mode for 25000base 26662306a36Sopenharmony_ci * with unspecified range, reuse SR. 26762306a36Sopenharmony_ci */ 26862306a36Sopenharmony_ci phylink_set(modes, 25000baseSR_Full); 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci break; 27162306a36Sopenharmony_ci case SFF8024_ECC_100GBASE_SR4_25GBASE_SR: 27262306a36Sopenharmony_ci phylink_set(modes, 100000baseSR4_Full); 27362306a36Sopenharmony_ci phylink_set(modes, 25000baseSR_Full); 27462306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_25GBASER, interfaces); 27562306a36Sopenharmony_ci break; 27662306a36Sopenharmony_ci case SFF8024_ECC_100GBASE_LR4_25GBASE_LR: 27762306a36Sopenharmony_ci case SFF8024_ECC_100GBASE_ER4_25GBASE_ER: 27862306a36Sopenharmony_ci phylink_set(modes, 100000baseLR4_ER4_Full); 27962306a36Sopenharmony_ci break; 28062306a36Sopenharmony_ci case SFF8024_ECC_100GBASE_CR4: 28162306a36Sopenharmony_ci phylink_set(modes, 100000baseCR4_Full); 28262306a36Sopenharmony_ci fallthrough; 28362306a36Sopenharmony_ci case SFF8024_ECC_25GBASE_CR_S: 28462306a36Sopenharmony_ci case SFF8024_ECC_25GBASE_CR_N: 28562306a36Sopenharmony_ci phylink_set(modes, 25000baseCR_Full); 28662306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_25GBASER, interfaces); 28762306a36Sopenharmony_ci break; 28862306a36Sopenharmony_ci case SFF8024_ECC_10GBASE_T_SFI: 28962306a36Sopenharmony_ci case SFF8024_ECC_10GBASE_T_SR: 29062306a36Sopenharmony_ci phylink_set(modes, 10000baseT_Full); 29162306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces); 29262306a36Sopenharmony_ci break; 29362306a36Sopenharmony_ci case SFF8024_ECC_5GBASE_T: 29462306a36Sopenharmony_ci phylink_set(modes, 5000baseT_Full); 29562306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_5GBASER, interfaces); 29662306a36Sopenharmony_ci break; 29762306a36Sopenharmony_ci case SFF8024_ECC_2_5GBASE_T: 29862306a36Sopenharmony_ci phylink_set(modes, 2500baseT_Full); 29962306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_2500BASEX, interfaces); 30062306a36Sopenharmony_ci break; 30162306a36Sopenharmony_ci default: 30262306a36Sopenharmony_ci dev_warn(bus->sfp_dev, 30362306a36Sopenharmony_ci "Unknown/unsupported extended compliance code: 0x%02x\n", 30462306a36Sopenharmony_ci id->base.extended_cc); 30562306a36Sopenharmony_ci break; 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci /* For fibre channel SFP, derive possible BaseX modes */ 30962306a36Sopenharmony_ci if (id->base.fc_speed_100 || 31062306a36Sopenharmony_ci id->base.fc_speed_200 || 31162306a36Sopenharmony_ci id->base.fc_speed_400) { 31262306a36Sopenharmony_ci if (id->base.br_nominal >= 31) { 31362306a36Sopenharmony_ci phylink_set(modes, 2500baseX_Full); 31462306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_2500BASEX, interfaces); 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci if (id->base.br_nominal >= 12) { 31762306a36Sopenharmony_ci phylink_set(modes, 1000baseX_Full); 31862306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_1000BASEX, interfaces); 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci /* If we haven't discovered any modes that this module supports, try 32362306a36Sopenharmony_ci * the bitrate to determine supported modes. Some BiDi modules (eg, 32462306a36Sopenharmony_ci * 1310nm/1550nm) are not 1000BASE-BX compliant due to the differing 32562306a36Sopenharmony_ci * wavelengths, so do not set any transceiver bits. 32662306a36Sopenharmony_ci * 32762306a36Sopenharmony_ci * Do the same for modules supporting 2500BASE-X. Note that some 32862306a36Sopenharmony_ci * modules use 2500Mbaud rather than 3100 or 3200Mbaud for 32962306a36Sopenharmony_ci * 2500BASE-X, so we allow some slack here. 33062306a36Sopenharmony_ci */ 33162306a36Sopenharmony_ci if (bitmap_empty(modes, __ETHTOOL_LINK_MODE_MASK_NBITS) && br_nom) { 33262306a36Sopenharmony_ci if (br_min <= 1300 && br_max >= 1200) { 33362306a36Sopenharmony_ci phylink_set(modes, 1000baseX_Full); 33462306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_1000BASEX, interfaces); 33562306a36Sopenharmony_ci } 33662306a36Sopenharmony_ci if (br_min <= 3200 && br_max >= 2500) { 33762306a36Sopenharmony_ci phylink_set(modes, 2500baseX_Full); 33862306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_2500BASEX, interfaces); 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci if (bus->sfp_quirk && bus->sfp_quirk->modes) 34362306a36Sopenharmony_ci bus->sfp_quirk->modes(id, modes, interfaces); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci linkmode_or(support, support, modes); 34662306a36Sopenharmony_ci} 34762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sfp_parse_support); 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci/** 35062306a36Sopenharmony_ci * sfp_select_interface() - Select appropriate phy_interface_t mode 35162306a36Sopenharmony_ci * @bus: a pointer to the &struct sfp_bus structure for the sfp module 35262306a36Sopenharmony_ci * @link_modes: ethtool link modes mask 35362306a36Sopenharmony_ci * 35462306a36Sopenharmony_ci * Derive the phy_interface_t mode for the SFP module from the link 35562306a36Sopenharmony_ci * modes mask. 35662306a36Sopenharmony_ci */ 35762306a36Sopenharmony_ciphy_interface_t sfp_select_interface(struct sfp_bus *bus, 35862306a36Sopenharmony_ci unsigned long *link_modes) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci if (phylink_test(link_modes, 25000baseCR_Full) || 36162306a36Sopenharmony_ci phylink_test(link_modes, 25000baseKR_Full) || 36262306a36Sopenharmony_ci phylink_test(link_modes, 25000baseSR_Full)) 36362306a36Sopenharmony_ci return PHY_INTERFACE_MODE_25GBASER; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci if (phylink_test(link_modes, 10000baseCR_Full) || 36662306a36Sopenharmony_ci phylink_test(link_modes, 10000baseSR_Full) || 36762306a36Sopenharmony_ci phylink_test(link_modes, 10000baseLR_Full) || 36862306a36Sopenharmony_ci phylink_test(link_modes, 10000baseLRM_Full) || 36962306a36Sopenharmony_ci phylink_test(link_modes, 10000baseER_Full) || 37062306a36Sopenharmony_ci phylink_test(link_modes, 10000baseT_Full)) 37162306a36Sopenharmony_ci return PHY_INTERFACE_MODE_10GBASER; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci if (phylink_test(link_modes, 5000baseT_Full)) 37462306a36Sopenharmony_ci return PHY_INTERFACE_MODE_5GBASER; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci if (phylink_test(link_modes, 2500baseX_Full)) 37762306a36Sopenharmony_ci return PHY_INTERFACE_MODE_2500BASEX; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci if (phylink_test(link_modes, 1000baseT_Half) || 38062306a36Sopenharmony_ci phylink_test(link_modes, 1000baseT_Full)) 38162306a36Sopenharmony_ci return PHY_INTERFACE_MODE_SGMII; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci if (phylink_test(link_modes, 1000baseX_Full)) 38462306a36Sopenharmony_ci return PHY_INTERFACE_MODE_1000BASEX; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci if (phylink_test(link_modes, 100baseFX_Full)) 38762306a36Sopenharmony_ci return PHY_INTERFACE_MODE_100BASEX; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci dev_warn(bus->sfp_dev, "Unable to ascertain link mode\n"); 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci return PHY_INTERFACE_MODE_NA; 39262306a36Sopenharmony_ci} 39362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sfp_select_interface); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_cistatic LIST_HEAD(sfp_buses); 39662306a36Sopenharmony_cistatic DEFINE_MUTEX(sfp_mutex); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_cistatic const struct sfp_upstream_ops *sfp_get_upstream_ops(struct sfp_bus *bus) 39962306a36Sopenharmony_ci{ 40062306a36Sopenharmony_ci return bus->registered ? bus->upstream_ops : NULL; 40162306a36Sopenharmony_ci} 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_cistatic struct sfp_bus *sfp_bus_get(const struct fwnode_handle *fwnode) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci struct sfp_bus *sfp, *new, *found = NULL; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci new = kzalloc(sizeof(*new), GFP_KERNEL); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci mutex_lock(&sfp_mutex); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci list_for_each_entry(sfp, &sfp_buses, node) { 41262306a36Sopenharmony_ci if (sfp->fwnode == fwnode) { 41362306a36Sopenharmony_ci kref_get(&sfp->kref); 41462306a36Sopenharmony_ci found = sfp; 41562306a36Sopenharmony_ci break; 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci if (!found && new) { 42062306a36Sopenharmony_ci kref_init(&new->kref); 42162306a36Sopenharmony_ci new->fwnode = fwnode; 42262306a36Sopenharmony_ci list_add(&new->node, &sfp_buses); 42362306a36Sopenharmony_ci found = new; 42462306a36Sopenharmony_ci new = NULL; 42562306a36Sopenharmony_ci } 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci mutex_unlock(&sfp_mutex); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci kfree(new); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci return found; 43262306a36Sopenharmony_ci} 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_cistatic void sfp_bus_release(struct kref *kref) 43562306a36Sopenharmony_ci{ 43662306a36Sopenharmony_ci struct sfp_bus *bus = container_of(kref, struct sfp_bus, kref); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci list_del(&bus->node); 43962306a36Sopenharmony_ci mutex_unlock(&sfp_mutex); 44062306a36Sopenharmony_ci kfree(bus); 44162306a36Sopenharmony_ci} 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci/** 44462306a36Sopenharmony_ci * sfp_bus_put() - put a reference on the &struct sfp_bus 44562306a36Sopenharmony_ci * @bus: the &struct sfp_bus found via sfp_bus_find_fwnode() 44662306a36Sopenharmony_ci * 44762306a36Sopenharmony_ci * Put a reference on the &struct sfp_bus and free the underlying structure 44862306a36Sopenharmony_ci * if this was the last reference. 44962306a36Sopenharmony_ci */ 45062306a36Sopenharmony_civoid sfp_bus_put(struct sfp_bus *bus) 45162306a36Sopenharmony_ci{ 45262306a36Sopenharmony_ci if (bus) 45362306a36Sopenharmony_ci kref_put_mutex(&bus->kref, sfp_bus_release, &sfp_mutex); 45462306a36Sopenharmony_ci} 45562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sfp_bus_put); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_cistatic int sfp_register_bus(struct sfp_bus *bus) 45862306a36Sopenharmony_ci{ 45962306a36Sopenharmony_ci const struct sfp_upstream_ops *ops = bus->upstream_ops; 46062306a36Sopenharmony_ci int ret; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci if (ops) { 46362306a36Sopenharmony_ci if (ops->link_down) 46462306a36Sopenharmony_ci ops->link_down(bus->upstream); 46562306a36Sopenharmony_ci if (ops->connect_phy && bus->phydev) { 46662306a36Sopenharmony_ci ret = ops->connect_phy(bus->upstream, bus->phydev); 46762306a36Sopenharmony_ci if (ret) 46862306a36Sopenharmony_ci return ret; 46962306a36Sopenharmony_ci } 47062306a36Sopenharmony_ci } 47162306a36Sopenharmony_ci bus->registered = true; 47262306a36Sopenharmony_ci bus->socket_ops->attach(bus->sfp); 47362306a36Sopenharmony_ci if (bus->started) 47462306a36Sopenharmony_ci bus->socket_ops->start(bus->sfp); 47562306a36Sopenharmony_ci bus->upstream_ops->attach(bus->upstream, bus); 47662306a36Sopenharmony_ci return 0; 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_cistatic void sfp_unregister_bus(struct sfp_bus *bus) 48062306a36Sopenharmony_ci{ 48162306a36Sopenharmony_ci const struct sfp_upstream_ops *ops = bus->upstream_ops; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci if (bus->registered) { 48462306a36Sopenharmony_ci bus->upstream_ops->detach(bus->upstream, bus); 48562306a36Sopenharmony_ci if (bus->started) 48662306a36Sopenharmony_ci bus->socket_ops->stop(bus->sfp); 48762306a36Sopenharmony_ci bus->socket_ops->detach(bus->sfp); 48862306a36Sopenharmony_ci if (bus->phydev && ops && ops->disconnect_phy) 48962306a36Sopenharmony_ci ops->disconnect_phy(bus->upstream); 49062306a36Sopenharmony_ci } 49162306a36Sopenharmony_ci bus->registered = false; 49262306a36Sopenharmony_ci} 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci/** 49562306a36Sopenharmony_ci * sfp_get_module_info() - Get the ethtool_modinfo for a SFP module 49662306a36Sopenharmony_ci * @bus: a pointer to the &struct sfp_bus structure for the sfp module 49762306a36Sopenharmony_ci * @modinfo: a &struct ethtool_modinfo 49862306a36Sopenharmony_ci * 49962306a36Sopenharmony_ci * Fill in the type and eeprom_len parameters in @modinfo for a module on 50062306a36Sopenharmony_ci * the sfp bus specified by @bus. 50162306a36Sopenharmony_ci * 50262306a36Sopenharmony_ci * Returns 0 on success or a negative errno number. 50362306a36Sopenharmony_ci */ 50462306a36Sopenharmony_ciint sfp_get_module_info(struct sfp_bus *bus, struct ethtool_modinfo *modinfo) 50562306a36Sopenharmony_ci{ 50662306a36Sopenharmony_ci return bus->socket_ops->module_info(bus->sfp, modinfo); 50762306a36Sopenharmony_ci} 50862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sfp_get_module_info); 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci/** 51162306a36Sopenharmony_ci * sfp_get_module_eeprom() - Read the SFP module EEPROM 51262306a36Sopenharmony_ci * @bus: a pointer to the &struct sfp_bus structure for the sfp module 51362306a36Sopenharmony_ci * @ee: a &struct ethtool_eeprom 51462306a36Sopenharmony_ci * @data: buffer to contain the EEPROM data (must be at least @ee->len bytes) 51562306a36Sopenharmony_ci * 51662306a36Sopenharmony_ci * Read the EEPROM as specified by the supplied @ee. See the documentation 51762306a36Sopenharmony_ci * for &struct ethtool_eeprom for the region to be read. 51862306a36Sopenharmony_ci * 51962306a36Sopenharmony_ci * Returns 0 on success or a negative errno number. 52062306a36Sopenharmony_ci */ 52162306a36Sopenharmony_ciint sfp_get_module_eeprom(struct sfp_bus *bus, struct ethtool_eeprom *ee, 52262306a36Sopenharmony_ci u8 *data) 52362306a36Sopenharmony_ci{ 52462306a36Sopenharmony_ci return bus->socket_ops->module_eeprom(bus->sfp, ee, data); 52562306a36Sopenharmony_ci} 52662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sfp_get_module_eeprom); 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci/** 52962306a36Sopenharmony_ci * sfp_get_module_eeprom_by_page() - Read a page from the SFP module EEPROM 53062306a36Sopenharmony_ci * @bus: a pointer to the &struct sfp_bus structure for the sfp module 53162306a36Sopenharmony_ci * @page: a &struct ethtool_module_eeprom 53262306a36Sopenharmony_ci * @extack: extack for reporting problems 53362306a36Sopenharmony_ci * 53462306a36Sopenharmony_ci * Read an EEPROM page as specified by the supplied @page. See the 53562306a36Sopenharmony_ci * documentation for &struct ethtool_module_eeprom for the page to be read. 53662306a36Sopenharmony_ci * 53762306a36Sopenharmony_ci * Returns 0 on success or a negative errno number. More error 53862306a36Sopenharmony_ci * information might be provided via extack 53962306a36Sopenharmony_ci */ 54062306a36Sopenharmony_ciint sfp_get_module_eeprom_by_page(struct sfp_bus *bus, 54162306a36Sopenharmony_ci const struct ethtool_module_eeprom *page, 54262306a36Sopenharmony_ci struct netlink_ext_ack *extack) 54362306a36Sopenharmony_ci{ 54462306a36Sopenharmony_ci return bus->socket_ops->module_eeprom_by_page(bus->sfp, page, extack); 54562306a36Sopenharmony_ci} 54662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sfp_get_module_eeprom_by_page); 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci/** 54962306a36Sopenharmony_ci * sfp_upstream_start() - Inform the SFP that the network device is up 55062306a36Sopenharmony_ci * @bus: a pointer to the &struct sfp_bus structure for the sfp module 55162306a36Sopenharmony_ci * 55262306a36Sopenharmony_ci * Inform the SFP socket that the network device is now up, so that the 55362306a36Sopenharmony_ci * module can be enabled by allowing TX_DISABLE to be deasserted. This 55462306a36Sopenharmony_ci * should be called from the network device driver's &struct net_device_ops 55562306a36Sopenharmony_ci * ndo_open() method. 55662306a36Sopenharmony_ci */ 55762306a36Sopenharmony_civoid sfp_upstream_start(struct sfp_bus *bus) 55862306a36Sopenharmony_ci{ 55962306a36Sopenharmony_ci if (bus->registered) 56062306a36Sopenharmony_ci bus->socket_ops->start(bus->sfp); 56162306a36Sopenharmony_ci bus->started = true; 56262306a36Sopenharmony_ci} 56362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sfp_upstream_start); 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci/** 56662306a36Sopenharmony_ci * sfp_upstream_stop() - Inform the SFP that the network device is down 56762306a36Sopenharmony_ci * @bus: a pointer to the &struct sfp_bus structure for the sfp module 56862306a36Sopenharmony_ci * 56962306a36Sopenharmony_ci * Inform the SFP socket that the network device is now up, so that the 57062306a36Sopenharmony_ci * module can be disabled by asserting TX_DISABLE, disabling the laser 57162306a36Sopenharmony_ci * in optical modules. This should be called from the network device 57262306a36Sopenharmony_ci * driver's &struct net_device_ops ndo_stop() method. 57362306a36Sopenharmony_ci */ 57462306a36Sopenharmony_civoid sfp_upstream_stop(struct sfp_bus *bus) 57562306a36Sopenharmony_ci{ 57662306a36Sopenharmony_ci if (bus->registered) 57762306a36Sopenharmony_ci bus->socket_ops->stop(bus->sfp); 57862306a36Sopenharmony_ci bus->started = false; 57962306a36Sopenharmony_ci} 58062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sfp_upstream_stop); 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_cistatic void sfp_upstream_clear(struct sfp_bus *bus) 58362306a36Sopenharmony_ci{ 58462306a36Sopenharmony_ci bus->upstream_ops = NULL; 58562306a36Sopenharmony_ci bus->upstream = NULL; 58662306a36Sopenharmony_ci} 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci/** 58962306a36Sopenharmony_ci * sfp_upstream_set_signal_rate() - set data signalling rate 59062306a36Sopenharmony_ci * @bus: a pointer to the &struct sfp_bus structure for the sfp module 59162306a36Sopenharmony_ci * @rate_kbd: signalling rate in units of 1000 baud 59262306a36Sopenharmony_ci * 59362306a36Sopenharmony_ci * Configure the rate select settings on the SFP module for the signalling 59462306a36Sopenharmony_ci * rate (not the same as the data rate). 59562306a36Sopenharmony_ci * 59662306a36Sopenharmony_ci * Locks that may be held: 59762306a36Sopenharmony_ci * Phylink's state_mutex 59862306a36Sopenharmony_ci * rtnl lock 59962306a36Sopenharmony_ci * SFP's sm_mutex 60062306a36Sopenharmony_ci */ 60162306a36Sopenharmony_civoid sfp_upstream_set_signal_rate(struct sfp_bus *bus, unsigned int rate_kbd) 60262306a36Sopenharmony_ci{ 60362306a36Sopenharmony_ci if (bus->registered) 60462306a36Sopenharmony_ci bus->socket_ops->set_signal_rate(bus->sfp, rate_kbd); 60562306a36Sopenharmony_ci} 60662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sfp_upstream_set_signal_rate); 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci/** 60962306a36Sopenharmony_ci * sfp_bus_find_fwnode() - parse and locate the SFP bus from fwnode 61062306a36Sopenharmony_ci * @fwnode: firmware node for the parent device (MAC or PHY) 61162306a36Sopenharmony_ci * 61262306a36Sopenharmony_ci * Parse the parent device's firmware node for a SFP bus, and locate 61362306a36Sopenharmony_ci * the sfp_bus structure, incrementing its reference count. This must 61462306a36Sopenharmony_ci * be put via sfp_bus_put() when done. 61562306a36Sopenharmony_ci * 61662306a36Sopenharmony_ci * Returns: 61762306a36Sopenharmony_ci * - on success, a pointer to the sfp_bus structure, 61862306a36Sopenharmony_ci * - %NULL if no SFP is specified, 61962306a36Sopenharmony_ci * - on failure, an error pointer value: 62062306a36Sopenharmony_ci * 62162306a36Sopenharmony_ci * - corresponding to the errors detailed for 62262306a36Sopenharmony_ci * fwnode_property_get_reference_args(). 62362306a36Sopenharmony_ci * - %-ENOMEM if we failed to allocate the bus. 62462306a36Sopenharmony_ci * - an error from the upstream's connect_phy() method. 62562306a36Sopenharmony_ci */ 62662306a36Sopenharmony_cistruct sfp_bus *sfp_bus_find_fwnode(const struct fwnode_handle *fwnode) 62762306a36Sopenharmony_ci{ 62862306a36Sopenharmony_ci struct fwnode_reference_args ref; 62962306a36Sopenharmony_ci struct sfp_bus *bus; 63062306a36Sopenharmony_ci int ret; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci ret = fwnode_property_get_reference_args(fwnode, "sfp", NULL, 63362306a36Sopenharmony_ci 0, 0, &ref); 63462306a36Sopenharmony_ci if (ret == -ENOENT) 63562306a36Sopenharmony_ci return NULL; 63662306a36Sopenharmony_ci else if (ret < 0) 63762306a36Sopenharmony_ci return ERR_PTR(ret); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci if (!fwnode_device_is_available(ref.fwnode)) { 64062306a36Sopenharmony_ci fwnode_handle_put(ref.fwnode); 64162306a36Sopenharmony_ci return NULL; 64262306a36Sopenharmony_ci } 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci bus = sfp_bus_get(ref.fwnode); 64562306a36Sopenharmony_ci fwnode_handle_put(ref.fwnode); 64662306a36Sopenharmony_ci if (!bus) 64762306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci return bus; 65062306a36Sopenharmony_ci} 65162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sfp_bus_find_fwnode); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci/** 65462306a36Sopenharmony_ci * sfp_bus_add_upstream() - parse and register the neighbouring device 65562306a36Sopenharmony_ci * @bus: the &struct sfp_bus found via sfp_bus_find_fwnode() 65662306a36Sopenharmony_ci * @upstream: the upstream private data 65762306a36Sopenharmony_ci * @ops: the upstream's &struct sfp_upstream_ops 65862306a36Sopenharmony_ci * 65962306a36Sopenharmony_ci * Add upstream driver for the SFP bus, and if the bus is complete, register 66062306a36Sopenharmony_ci * the SFP bus using sfp_register_upstream(). This takes a reference on the 66162306a36Sopenharmony_ci * bus, so it is safe to put the bus after this call. 66262306a36Sopenharmony_ci * 66362306a36Sopenharmony_ci * Returns: 66462306a36Sopenharmony_ci * - on success, a pointer to the sfp_bus structure, 66562306a36Sopenharmony_ci * - %NULL if no SFP is specified, 66662306a36Sopenharmony_ci * - on failure, an error pointer value: 66762306a36Sopenharmony_ci * 66862306a36Sopenharmony_ci * - corresponding to the errors detailed for 66962306a36Sopenharmony_ci * fwnode_property_get_reference_args(). 67062306a36Sopenharmony_ci * - %-ENOMEM if we failed to allocate the bus. 67162306a36Sopenharmony_ci * - an error from the upstream's connect_phy() method. 67262306a36Sopenharmony_ci */ 67362306a36Sopenharmony_ciint sfp_bus_add_upstream(struct sfp_bus *bus, void *upstream, 67462306a36Sopenharmony_ci const struct sfp_upstream_ops *ops) 67562306a36Sopenharmony_ci{ 67662306a36Sopenharmony_ci int ret; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci /* If no bus, return success */ 67962306a36Sopenharmony_ci if (!bus) 68062306a36Sopenharmony_ci return 0; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci rtnl_lock(); 68362306a36Sopenharmony_ci kref_get(&bus->kref); 68462306a36Sopenharmony_ci bus->upstream_ops = ops; 68562306a36Sopenharmony_ci bus->upstream = upstream; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci if (bus->sfp) { 68862306a36Sopenharmony_ci ret = sfp_register_bus(bus); 68962306a36Sopenharmony_ci if (ret) 69062306a36Sopenharmony_ci sfp_upstream_clear(bus); 69162306a36Sopenharmony_ci } else { 69262306a36Sopenharmony_ci ret = 0; 69362306a36Sopenharmony_ci } 69462306a36Sopenharmony_ci rtnl_unlock(); 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci if (ret) 69762306a36Sopenharmony_ci sfp_bus_put(bus); 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci return ret; 70062306a36Sopenharmony_ci} 70162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sfp_bus_add_upstream); 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci/** 70462306a36Sopenharmony_ci * sfp_bus_del_upstream() - Delete a sfp bus 70562306a36Sopenharmony_ci * @bus: a pointer to the &struct sfp_bus structure for the sfp module 70662306a36Sopenharmony_ci * 70762306a36Sopenharmony_ci * Delete a previously registered upstream connection for the SFP 70862306a36Sopenharmony_ci * module. @bus should have been added by sfp_bus_add_upstream(). 70962306a36Sopenharmony_ci */ 71062306a36Sopenharmony_civoid sfp_bus_del_upstream(struct sfp_bus *bus) 71162306a36Sopenharmony_ci{ 71262306a36Sopenharmony_ci if (bus) { 71362306a36Sopenharmony_ci rtnl_lock(); 71462306a36Sopenharmony_ci if (bus->sfp) 71562306a36Sopenharmony_ci sfp_unregister_bus(bus); 71662306a36Sopenharmony_ci sfp_upstream_clear(bus); 71762306a36Sopenharmony_ci rtnl_unlock(); 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci sfp_bus_put(bus); 72062306a36Sopenharmony_ci } 72162306a36Sopenharmony_ci} 72262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sfp_bus_del_upstream); 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci/* Socket driver entry points */ 72562306a36Sopenharmony_ciint sfp_add_phy(struct sfp_bus *bus, struct phy_device *phydev) 72662306a36Sopenharmony_ci{ 72762306a36Sopenharmony_ci const struct sfp_upstream_ops *ops = sfp_get_upstream_ops(bus); 72862306a36Sopenharmony_ci int ret = 0; 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci if (ops && ops->connect_phy) 73162306a36Sopenharmony_ci ret = ops->connect_phy(bus->upstream, phydev); 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci if (ret == 0) 73462306a36Sopenharmony_ci bus->phydev = phydev; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci return ret; 73762306a36Sopenharmony_ci} 73862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sfp_add_phy); 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_civoid sfp_remove_phy(struct sfp_bus *bus) 74162306a36Sopenharmony_ci{ 74262306a36Sopenharmony_ci const struct sfp_upstream_ops *ops = sfp_get_upstream_ops(bus); 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci if (ops && ops->disconnect_phy) 74562306a36Sopenharmony_ci ops->disconnect_phy(bus->upstream); 74662306a36Sopenharmony_ci bus->phydev = NULL; 74762306a36Sopenharmony_ci} 74862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sfp_remove_phy); 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_civoid sfp_link_up(struct sfp_bus *bus) 75162306a36Sopenharmony_ci{ 75262306a36Sopenharmony_ci const struct sfp_upstream_ops *ops = sfp_get_upstream_ops(bus); 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci if (ops && ops->link_up) 75562306a36Sopenharmony_ci ops->link_up(bus->upstream); 75662306a36Sopenharmony_ci} 75762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sfp_link_up); 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_civoid sfp_link_down(struct sfp_bus *bus) 76062306a36Sopenharmony_ci{ 76162306a36Sopenharmony_ci const struct sfp_upstream_ops *ops = sfp_get_upstream_ops(bus); 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci if (ops && ops->link_down) 76462306a36Sopenharmony_ci ops->link_down(bus->upstream); 76562306a36Sopenharmony_ci} 76662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sfp_link_down); 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ciint sfp_module_insert(struct sfp_bus *bus, const struct sfp_eeprom_id *id, 76962306a36Sopenharmony_ci const struct sfp_quirk *quirk) 77062306a36Sopenharmony_ci{ 77162306a36Sopenharmony_ci const struct sfp_upstream_ops *ops = sfp_get_upstream_ops(bus); 77262306a36Sopenharmony_ci int ret = 0; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci bus->sfp_quirk = quirk; 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci if (ops && ops->module_insert) 77762306a36Sopenharmony_ci ret = ops->module_insert(bus->upstream, id); 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci return ret; 78062306a36Sopenharmony_ci} 78162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sfp_module_insert); 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_civoid sfp_module_remove(struct sfp_bus *bus) 78462306a36Sopenharmony_ci{ 78562306a36Sopenharmony_ci const struct sfp_upstream_ops *ops = sfp_get_upstream_ops(bus); 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci if (ops && ops->module_remove) 78862306a36Sopenharmony_ci ops->module_remove(bus->upstream); 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci bus->sfp_quirk = NULL; 79162306a36Sopenharmony_ci} 79262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sfp_module_remove); 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ciint sfp_module_start(struct sfp_bus *bus) 79562306a36Sopenharmony_ci{ 79662306a36Sopenharmony_ci const struct sfp_upstream_ops *ops = sfp_get_upstream_ops(bus); 79762306a36Sopenharmony_ci int ret = 0; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci if (ops && ops->module_start) 80062306a36Sopenharmony_ci ret = ops->module_start(bus->upstream); 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci return ret; 80362306a36Sopenharmony_ci} 80462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sfp_module_start); 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_civoid sfp_module_stop(struct sfp_bus *bus) 80762306a36Sopenharmony_ci{ 80862306a36Sopenharmony_ci const struct sfp_upstream_ops *ops = sfp_get_upstream_ops(bus); 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci if (ops && ops->module_stop) 81162306a36Sopenharmony_ci ops->module_stop(bus->upstream); 81262306a36Sopenharmony_ci} 81362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sfp_module_stop); 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_cistatic void sfp_socket_clear(struct sfp_bus *bus) 81662306a36Sopenharmony_ci{ 81762306a36Sopenharmony_ci bus->sfp_dev = NULL; 81862306a36Sopenharmony_ci bus->sfp = NULL; 81962306a36Sopenharmony_ci bus->socket_ops = NULL; 82062306a36Sopenharmony_ci} 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_cistruct sfp_bus *sfp_register_socket(struct device *dev, struct sfp *sfp, 82362306a36Sopenharmony_ci const struct sfp_socket_ops *ops) 82462306a36Sopenharmony_ci{ 82562306a36Sopenharmony_ci struct sfp_bus *bus = sfp_bus_get(dev->fwnode); 82662306a36Sopenharmony_ci int ret = 0; 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci if (bus) { 82962306a36Sopenharmony_ci rtnl_lock(); 83062306a36Sopenharmony_ci bus->sfp_dev = dev; 83162306a36Sopenharmony_ci bus->sfp = sfp; 83262306a36Sopenharmony_ci bus->socket_ops = ops; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci if (bus->upstream_ops) { 83562306a36Sopenharmony_ci ret = sfp_register_bus(bus); 83662306a36Sopenharmony_ci if (ret) 83762306a36Sopenharmony_ci sfp_socket_clear(bus); 83862306a36Sopenharmony_ci } 83962306a36Sopenharmony_ci rtnl_unlock(); 84062306a36Sopenharmony_ci } 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci if (ret) { 84362306a36Sopenharmony_ci sfp_bus_put(bus); 84462306a36Sopenharmony_ci bus = NULL; 84562306a36Sopenharmony_ci } 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci return bus; 84862306a36Sopenharmony_ci} 84962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sfp_register_socket); 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_civoid sfp_unregister_socket(struct sfp_bus *bus) 85262306a36Sopenharmony_ci{ 85362306a36Sopenharmony_ci rtnl_lock(); 85462306a36Sopenharmony_ci if (bus->upstream_ops) 85562306a36Sopenharmony_ci sfp_unregister_bus(bus); 85662306a36Sopenharmony_ci sfp_socket_clear(bus); 85762306a36Sopenharmony_ci rtnl_unlock(); 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci sfp_bus_put(bus); 86062306a36Sopenharmony_ci} 86162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sfp_unregister_socket); 862