18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci#include <linux/export.h>
38c2ecf20Sopenharmony_ci#include <linux/kref.h>
48c2ecf20Sopenharmony_ci#include <linux/list.h>
58c2ecf20Sopenharmony_ci#include <linux/mutex.h>
68c2ecf20Sopenharmony_ci#include <linux/phylink.h>
78c2ecf20Sopenharmony_ci#include <linux/property.h>
88c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h>
98c2ecf20Sopenharmony_ci#include <linux/slab.h>
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include "sfp.h"
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_cistruct sfp_quirk {
148c2ecf20Sopenharmony_ci	const char *vendor;
158c2ecf20Sopenharmony_ci	const char *part;
168c2ecf20Sopenharmony_ci	void (*modes)(const struct sfp_eeprom_id *id, unsigned long *modes);
178c2ecf20Sopenharmony_ci};
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci/**
208c2ecf20Sopenharmony_ci * struct sfp_bus - internal representation of a sfp bus
218c2ecf20Sopenharmony_ci */
228c2ecf20Sopenharmony_cistruct sfp_bus {
238c2ecf20Sopenharmony_ci	/* private: */
248c2ecf20Sopenharmony_ci	struct kref kref;
258c2ecf20Sopenharmony_ci	struct list_head node;
268c2ecf20Sopenharmony_ci	struct fwnode_handle *fwnode;
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci	const struct sfp_socket_ops *socket_ops;
298c2ecf20Sopenharmony_ci	struct device *sfp_dev;
308c2ecf20Sopenharmony_ci	struct sfp *sfp;
318c2ecf20Sopenharmony_ci	const struct sfp_quirk *sfp_quirk;
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	const struct sfp_upstream_ops *upstream_ops;
348c2ecf20Sopenharmony_ci	void *upstream;
358c2ecf20Sopenharmony_ci	struct phy_device *phydev;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	bool registered;
388c2ecf20Sopenharmony_ci	bool started;
398c2ecf20Sopenharmony_ci};
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic void sfp_quirk_2500basex(const struct sfp_eeprom_id *id,
428c2ecf20Sopenharmony_ci				unsigned long *modes)
438c2ecf20Sopenharmony_ci{
448c2ecf20Sopenharmony_ci	phylink_set(modes, 2500baseX_Full);
458c2ecf20Sopenharmony_ci}
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistatic void sfp_quirk_ubnt_uf_instant(const struct sfp_eeprom_id *id,
488c2ecf20Sopenharmony_ci				      unsigned long *modes)
498c2ecf20Sopenharmony_ci{
508c2ecf20Sopenharmony_ci	/* Ubiquiti U-Fiber Instant module claims that support all transceiver
518c2ecf20Sopenharmony_ci	 * types including 10G Ethernet which is not truth. So clear all claimed
528c2ecf20Sopenharmony_ci	 * modes and set only one mode which module supports: 1000baseX_Full.
538c2ecf20Sopenharmony_ci	 */
548c2ecf20Sopenharmony_ci	phylink_zero(modes);
558c2ecf20Sopenharmony_ci	phylink_set(modes, 1000baseX_Full);
568c2ecf20Sopenharmony_ci}
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic const struct sfp_quirk sfp_quirks[] = {
598c2ecf20Sopenharmony_ci	{
608c2ecf20Sopenharmony_ci		// Alcatel Lucent G-010S-P can operate at 2500base-X, but
618c2ecf20Sopenharmony_ci		// incorrectly report 2500MBd NRZ in their EEPROM
628c2ecf20Sopenharmony_ci		.vendor = "ALCATELLUCENT",
638c2ecf20Sopenharmony_ci		.part = "G010SP",
648c2ecf20Sopenharmony_ci		.modes = sfp_quirk_2500basex,
658c2ecf20Sopenharmony_ci	}, {
668c2ecf20Sopenharmony_ci		// Alcatel Lucent G-010S-A can operate at 2500base-X, but
678c2ecf20Sopenharmony_ci		// report 3.2GBd NRZ in their EEPROM
688c2ecf20Sopenharmony_ci		.vendor = "ALCATELLUCENT",
698c2ecf20Sopenharmony_ci		.part = "3FE46541AA",
708c2ecf20Sopenharmony_ci		.modes = sfp_quirk_2500basex,
718c2ecf20Sopenharmony_ci	}, {
728c2ecf20Sopenharmony_ci		// Huawei MA5671A can operate at 2500base-X, but report 1.2GBd
738c2ecf20Sopenharmony_ci		// NRZ in their EEPROM
748c2ecf20Sopenharmony_ci		.vendor = "HUAWEI",
758c2ecf20Sopenharmony_ci		.part = "MA5671A",
768c2ecf20Sopenharmony_ci		.modes = sfp_quirk_2500basex,
778c2ecf20Sopenharmony_ci	}, {
788c2ecf20Sopenharmony_ci		// Lantech 8330-262D-E can operate at 2500base-X, but
798c2ecf20Sopenharmony_ci		// incorrectly report 2500MBd NRZ in their EEPROM
808c2ecf20Sopenharmony_ci		.vendor = "Lantech",
818c2ecf20Sopenharmony_ci		.part = "8330-262D-E",
828c2ecf20Sopenharmony_ci		.modes = sfp_quirk_2500basex,
838c2ecf20Sopenharmony_ci	}, {
848c2ecf20Sopenharmony_ci		.vendor = "UBNT",
858c2ecf20Sopenharmony_ci		.part = "UF-INSTANT",
868c2ecf20Sopenharmony_ci		.modes = sfp_quirk_ubnt_uf_instant,
878c2ecf20Sopenharmony_ci	},
888c2ecf20Sopenharmony_ci};
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistatic size_t sfp_strlen(const char *str, size_t maxlen)
918c2ecf20Sopenharmony_ci{
928c2ecf20Sopenharmony_ci	size_t size, i;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	/* Trailing characters should be filled with space chars */
958c2ecf20Sopenharmony_ci	for (i = 0, size = 0; i < maxlen; i++)
968c2ecf20Sopenharmony_ci		if (str[i] != ' ')
978c2ecf20Sopenharmony_ci			size = i + 1;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	return size;
1008c2ecf20Sopenharmony_ci}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_cistatic bool sfp_match(const char *qs, const char *str, size_t len)
1038c2ecf20Sopenharmony_ci{
1048c2ecf20Sopenharmony_ci	if (!qs)
1058c2ecf20Sopenharmony_ci		return true;
1068c2ecf20Sopenharmony_ci	if (strlen(qs) != len)
1078c2ecf20Sopenharmony_ci		return false;
1088c2ecf20Sopenharmony_ci	return !strncmp(qs, str, len);
1098c2ecf20Sopenharmony_ci}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_cistatic const struct sfp_quirk *sfp_lookup_quirk(const struct sfp_eeprom_id *id)
1128c2ecf20Sopenharmony_ci{
1138c2ecf20Sopenharmony_ci	const struct sfp_quirk *q;
1148c2ecf20Sopenharmony_ci	unsigned int i;
1158c2ecf20Sopenharmony_ci	size_t vs, ps;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	vs = sfp_strlen(id->base.vendor_name, ARRAY_SIZE(id->base.vendor_name));
1188c2ecf20Sopenharmony_ci	ps = sfp_strlen(id->base.vendor_pn, ARRAY_SIZE(id->base.vendor_pn));
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	for (i = 0, q = sfp_quirks; i < ARRAY_SIZE(sfp_quirks); i++, q++)
1218c2ecf20Sopenharmony_ci		if (sfp_match(q->vendor, id->base.vendor_name, vs) &&
1228c2ecf20Sopenharmony_ci		    sfp_match(q->part, id->base.vendor_pn, ps))
1238c2ecf20Sopenharmony_ci			return q;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	return NULL;
1268c2ecf20Sopenharmony_ci}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci/**
1298c2ecf20Sopenharmony_ci * sfp_parse_port() - Parse the EEPROM base ID, setting the port type
1308c2ecf20Sopenharmony_ci * @bus: a pointer to the &struct sfp_bus structure for the sfp module
1318c2ecf20Sopenharmony_ci * @id: a pointer to the module's &struct sfp_eeprom_id
1328c2ecf20Sopenharmony_ci * @support: optional pointer to an array of unsigned long for the
1338c2ecf20Sopenharmony_ci *   ethtool support mask
1348c2ecf20Sopenharmony_ci *
1358c2ecf20Sopenharmony_ci * Parse the EEPROM identification given in @id, and return one of
1368c2ecf20Sopenharmony_ci * %PORT_TP, %PORT_FIBRE or %PORT_OTHER. If @support is non-%NULL,
1378c2ecf20Sopenharmony_ci * also set the ethtool %ETHTOOL_LINK_MODE_xxx_BIT corresponding with
1388c2ecf20Sopenharmony_ci * the connector type.
1398c2ecf20Sopenharmony_ci *
1408c2ecf20Sopenharmony_ci * If the port type is not known, returns %PORT_OTHER.
1418c2ecf20Sopenharmony_ci */
1428c2ecf20Sopenharmony_ciint sfp_parse_port(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
1438c2ecf20Sopenharmony_ci		   unsigned long *support)
1448c2ecf20Sopenharmony_ci{
1458c2ecf20Sopenharmony_ci	int port;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	/* port is the physical connector, set this from the connector field. */
1488c2ecf20Sopenharmony_ci	switch (id->base.connector) {
1498c2ecf20Sopenharmony_ci	case SFF8024_CONNECTOR_SC:
1508c2ecf20Sopenharmony_ci	case SFF8024_CONNECTOR_FIBERJACK:
1518c2ecf20Sopenharmony_ci	case SFF8024_CONNECTOR_LC:
1528c2ecf20Sopenharmony_ci	case SFF8024_CONNECTOR_MT_RJ:
1538c2ecf20Sopenharmony_ci	case SFF8024_CONNECTOR_MU:
1548c2ecf20Sopenharmony_ci	case SFF8024_CONNECTOR_OPTICAL_PIGTAIL:
1558c2ecf20Sopenharmony_ci	case SFF8024_CONNECTOR_MPO_1X12:
1568c2ecf20Sopenharmony_ci	case SFF8024_CONNECTOR_MPO_2X16:
1578c2ecf20Sopenharmony_ci		port = PORT_FIBRE;
1588c2ecf20Sopenharmony_ci		break;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	case SFF8024_CONNECTOR_RJ45:
1618c2ecf20Sopenharmony_ci		port = PORT_TP;
1628c2ecf20Sopenharmony_ci		break;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	case SFF8024_CONNECTOR_COPPER_PIGTAIL:
1658c2ecf20Sopenharmony_ci		port = PORT_DA;
1668c2ecf20Sopenharmony_ci		break;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	case SFF8024_CONNECTOR_UNSPEC:
1698c2ecf20Sopenharmony_ci		if (id->base.e1000_base_t) {
1708c2ecf20Sopenharmony_ci			port = PORT_TP;
1718c2ecf20Sopenharmony_ci			break;
1728c2ecf20Sopenharmony_ci		}
1738c2ecf20Sopenharmony_ci		fallthrough;
1748c2ecf20Sopenharmony_ci	case SFF8024_CONNECTOR_SG: /* guess */
1758c2ecf20Sopenharmony_ci	case SFF8024_CONNECTOR_HSSDC_II:
1768c2ecf20Sopenharmony_ci	case SFF8024_CONNECTOR_NOSEPARATE:
1778c2ecf20Sopenharmony_ci	case SFF8024_CONNECTOR_MXC_2X16:
1788c2ecf20Sopenharmony_ci		port = PORT_OTHER;
1798c2ecf20Sopenharmony_ci		break;
1808c2ecf20Sopenharmony_ci	default:
1818c2ecf20Sopenharmony_ci		dev_warn(bus->sfp_dev, "SFP: unknown connector id 0x%02x\n",
1828c2ecf20Sopenharmony_ci			 id->base.connector);
1838c2ecf20Sopenharmony_ci		port = PORT_OTHER;
1848c2ecf20Sopenharmony_ci		break;
1858c2ecf20Sopenharmony_ci	}
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	if (support) {
1888c2ecf20Sopenharmony_ci		switch (port) {
1898c2ecf20Sopenharmony_ci		case PORT_FIBRE:
1908c2ecf20Sopenharmony_ci			phylink_set(support, FIBRE);
1918c2ecf20Sopenharmony_ci			break;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci		case PORT_TP:
1948c2ecf20Sopenharmony_ci			phylink_set(support, TP);
1958c2ecf20Sopenharmony_ci			break;
1968c2ecf20Sopenharmony_ci		}
1978c2ecf20Sopenharmony_ci	}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	return port;
2008c2ecf20Sopenharmony_ci}
2018c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sfp_parse_port);
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci/**
2048c2ecf20Sopenharmony_ci * sfp_may_have_phy() - indicate whether the module may have a PHY
2058c2ecf20Sopenharmony_ci * @bus: a pointer to the &struct sfp_bus structure for the sfp module
2068c2ecf20Sopenharmony_ci * @id: a pointer to the module's &struct sfp_eeprom_id
2078c2ecf20Sopenharmony_ci *
2088c2ecf20Sopenharmony_ci * Parse the EEPROM identification given in @id, and return whether
2098c2ecf20Sopenharmony_ci * this module may have a PHY.
2108c2ecf20Sopenharmony_ci */
2118c2ecf20Sopenharmony_cibool sfp_may_have_phy(struct sfp_bus *bus, const struct sfp_eeprom_id *id)
2128c2ecf20Sopenharmony_ci{
2138c2ecf20Sopenharmony_ci	if (id->base.e1000_base_t)
2148c2ecf20Sopenharmony_ci		return true;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	if (id->base.phys_id != SFF8024_ID_DWDM_SFP) {
2178c2ecf20Sopenharmony_ci		switch (id->base.extended_cc) {
2188c2ecf20Sopenharmony_ci		case SFF8024_ECC_10GBASE_T_SFI:
2198c2ecf20Sopenharmony_ci		case SFF8024_ECC_10GBASE_T_SR:
2208c2ecf20Sopenharmony_ci		case SFF8024_ECC_5GBASE_T:
2218c2ecf20Sopenharmony_ci		case SFF8024_ECC_2_5GBASE_T:
2228c2ecf20Sopenharmony_ci			return true;
2238c2ecf20Sopenharmony_ci		}
2248c2ecf20Sopenharmony_ci	}
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	return false;
2278c2ecf20Sopenharmony_ci}
2288c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sfp_may_have_phy);
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci/**
2318c2ecf20Sopenharmony_ci * sfp_parse_support() - Parse the eeprom id for supported link modes
2328c2ecf20Sopenharmony_ci * @bus: a pointer to the &struct sfp_bus structure for the sfp module
2338c2ecf20Sopenharmony_ci * @id: a pointer to the module's &struct sfp_eeprom_id
2348c2ecf20Sopenharmony_ci * @support: pointer to an array of unsigned long for the ethtool support mask
2358c2ecf20Sopenharmony_ci *
2368c2ecf20Sopenharmony_ci * Parse the EEPROM identification information and derive the supported
2378c2ecf20Sopenharmony_ci * ethtool link modes for the module.
2388c2ecf20Sopenharmony_ci */
2398c2ecf20Sopenharmony_civoid sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
2408c2ecf20Sopenharmony_ci		       unsigned long *support)
2418c2ecf20Sopenharmony_ci{
2428c2ecf20Sopenharmony_ci	unsigned int br_min, br_nom, br_max;
2438c2ecf20Sopenharmony_ci	__ETHTOOL_DECLARE_LINK_MODE_MASK(modes) = { 0, };
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	/* Decode the bitrate information to MBd */
2468c2ecf20Sopenharmony_ci	br_min = br_nom = br_max = 0;
2478c2ecf20Sopenharmony_ci	if (id->base.br_nominal) {
2488c2ecf20Sopenharmony_ci		if (id->base.br_nominal != 255) {
2498c2ecf20Sopenharmony_ci			br_nom = id->base.br_nominal * 100;
2508c2ecf20Sopenharmony_ci			br_min = br_nom - id->base.br_nominal * id->ext.br_min;
2518c2ecf20Sopenharmony_ci			br_max = br_nom + id->base.br_nominal * id->ext.br_max;
2528c2ecf20Sopenharmony_ci		} else if (id->ext.br_max) {
2538c2ecf20Sopenharmony_ci			br_nom = 250 * id->ext.br_max;
2548c2ecf20Sopenharmony_ci			br_max = br_nom + br_nom * id->ext.br_min / 100;
2558c2ecf20Sopenharmony_ci			br_min = br_nom - br_nom * id->ext.br_min / 100;
2568c2ecf20Sopenharmony_ci		}
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci		/* When using passive cables, in case neither BR,min nor BR,max
2598c2ecf20Sopenharmony_ci		 * are specified, set br_min to 0 as the nominal value is then
2608c2ecf20Sopenharmony_ci		 * used as the maximum.
2618c2ecf20Sopenharmony_ci		 */
2628c2ecf20Sopenharmony_ci		if (br_min == br_max && id->base.sfp_ct_passive)
2638c2ecf20Sopenharmony_ci			br_min = 0;
2648c2ecf20Sopenharmony_ci	}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	/* Set ethtool support from the compliance fields. */
2678c2ecf20Sopenharmony_ci	if (id->base.e10g_base_sr)
2688c2ecf20Sopenharmony_ci		phylink_set(modes, 10000baseSR_Full);
2698c2ecf20Sopenharmony_ci	if (id->base.e10g_base_lr)
2708c2ecf20Sopenharmony_ci		phylink_set(modes, 10000baseLR_Full);
2718c2ecf20Sopenharmony_ci	if (id->base.e10g_base_lrm)
2728c2ecf20Sopenharmony_ci		phylink_set(modes, 10000baseLRM_Full);
2738c2ecf20Sopenharmony_ci	if (id->base.e10g_base_er)
2748c2ecf20Sopenharmony_ci		phylink_set(modes, 10000baseER_Full);
2758c2ecf20Sopenharmony_ci	if (id->base.e1000_base_sx ||
2768c2ecf20Sopenharmony_ci	    id->base.e1000_base_lx ||
2778c2ecf20Sopenharmony_ci	    id->base.e1000_base_cx)
2788c2ecf20Sopenharmony_ci		phylink_set(modes, 1000baseX_Full);
2798c2ecf20Sopenharmony_ci	if (id->base.e1000_base_t) {
2808c2ecf20Sopenharmony_ci		phylink_set(modes, 1000baseT_Half);
2818c2ecf20Sopenharmony_ci		phylink_set(modes, 1000baseT_Full);
2828c2ecf20Sopenharmony_ci	}
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	/* 1000Base-PX or 1000Base-BX10 */
2858c2ecf20Sopenharmony_ci	if ((id->base.e_base_px || id->base.e_base_bx10) &&
2868c2ecf20Sopenharmony_ci	    br_min <= 1300 && br_max >= 1200)
2878c2ecf20Sopenharmony_ci		phylink_set(modes, 1000baseX_Full);
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	/* For active or passive cables, select the link modes
2908c2ecf20Sopenharmony_ci	 * based on the bit rates and the cable compliance bytes.
2918c2ecf20Sopenharmony_ci	 */
2928c2ecf20Sopenharmony_ci	if ((id->base.sfp_ct_passive || id->base.sfp_ct_active) && br_nom) {
2938c2ecf20Sopenharmony_ci		/* This may look odd, but some manufacturers use 12000MBd */
2948c2ecf20Sopenharmony_ci		if (br_min <= 12000 && br_max >= 10300)
2958c2ecf20Sopenharmony_ci			phylink_set(modes, 10000baseCR_Full);
2968c2ecf20Sopenharmony_ci		if (br_min <= 3200 && br_max >= 3100)
2978c2ecf20Sopenharmony_ci			phylink_set(modes, 2500baseX_Full);
2988c2ecf20Sopenharmony_ci		if (br_min <= 1300 && br_max >= 1200)
2998c2ecf20Sopenharmony_ci			phylink_set(modes, 1000baseX_Full);
3008c2ecf20Sopenharmony_ci	}
3018c2ecf20Sopenharmony_ci	if (id->base.sfp_ct_passive) {
3028c2ecf20Sopenharmony_ci		if (id->base.passive.sff8431_app_e)
3038c2ecf20Sopenharmony_ci			phylink_set(modes, 10000baseCR_Full);
3048c2ecf20Sopenharmony_ci	}
3058c2ecf20Sopenharmony_ci	if (id->base.sfp_ct_active) {
3068c2ecf20Sopenharmony_ci		if (id->base.active.sff8431_app_e ||
3078c2ecf20Sopenharmony_ci		    id->base.active.sff8431_lim) {
3088c2ecf20Sopenharmony_ci			phylink_set(modes, 10000baseCR_Full);
3098c2ecf20Sopenharmony_ci		}
3108c2ecf20Sopenharmony_ci	}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	switch (id->base.extended_cc) {
3138c2ecf20Sopenharmony_ci	case SFF8024_ECC_UNSPEC:
3148c2ecf20Sopenharmony_ci		break;
3158c2ecf20Sopenharmony_ci	case SFF8024_ECC_100GBASE_SR4_25GBASE_SR:
3168c2ecf20Sopenharmony_ci		phylink_set(modes, 100000baseSR4_Full);
3178c2ecf20Sopenharmony_ci		phylink_set(modes, 25000baseSR_Full);
3188c2ecf20Sopenharmony_ci		break;
3198c2ecf20Sopenharmony_ci	case SFF8024_ECC_100GBASE_LR4_25GBASE_LR:
3208c2ecf20Sopenharmony_ci	case SFF8024_ECC_100GBASE_ER4_25GBASE_ER:
3218c2ecf20Sopenharmony_ci		phylink_set(modes, 100000baseLR4_ER4_Full);
3228c2ecf20Sopenharmony_ci		break;
3238c2ecf20Sopenharmony_ci	case SFF8024_ECC_100GBASE_CR4:
3248c2ecf20Sopenharmony_ci		phylink_set(modes, 100000baseCR4_Full);
3258c2ecf20Sopenharmony_ci		fallthrough;
3268c2ecf20Sopenharmony_ci	case SFF8024_ECC_25GBASE_CR_S:
3278c2ecf20Sopenharmony_ci	case SFF8024_ECC_25GBASE_CR_N:
3288c2ecf20Sopenharmony_ci		phylink_set(modes, 25000baseCR_Full);
3298c2ecf20Sopenharmony_ci		break;
3308c2ecf20Sopenharmony_ci	case SFF8024_ECC_10GBASE_T_SFI:
3318c2ecf20Sopenharmony_ci	case SFF8024_ECC_10GBASE_T_SR:
3328c2ecf20Sopenharmony_ci		phylink_set(modes, 10000baseT_Full);
3338c2ecf20Sopenharmony_ci		break;
3348c2ecf20Sopenharmony_ci	case SFF8024_ECC_5GBASE_T:
3358c2ecf20Sopenharmony_ci		phylink_set(modes, 5000baseT_Full);
3368c2ecf20Sopenharmony_ci		break;
3378c2ecf20Sopenharmony_ci	case SFF8024_ECC_2_5GBASE_T:
3388c2ecf20Sopenharmony_ci		phylink_set(modes, 2500baseT_Full);
3398c2ecf20Sopenharmony_ci		break;
3408c2ecf20Sopenharmony_ci	default:
3418c2ecf20Sopenharmony_ci		dev_warn(bus->sfp_dev,
3428c2ecf20Sopenharmony_ci			 "Unknown/unsupported extended compliance code: 0x%02x\n",
3438c2ecf20Sopenharmony_ci			 id->base.extended_cc);
3448c2ecf20Sopenharmony_ci		break;
3458c2ecf20Sopenharmony_ci	}
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	/* For fibre channel SFP, derive possible BaseX modes */
3488c2ecf20Sopenharmony_ci	if (id->base.fc_speed_100 ||
3498c2ecf20Sopenharmony_ci	    id->base.fc_speed_200 ||
3508c2ecf20Sopenharmony_ci	    id->base.fc_speed_400) {
3518c2ecf20Sopenharmony_ci		if (id->base.br_nominal >= 31)
3528c2ecf20Sopenharmony_ci			phylink_set(modes, 2500baseX_Full);
3538c2ecf20Sopenharmony_ci		if (id->base.br_nominal >= 12)
3548c2ecf20Sopenharmony_ci			phylink_set(modes, 1000baseX_Full);
3558c2ecf20Sopenharmony_ci	}
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	/* If we haven't discovered any modes that this module supports, try
3588c2ecf20Sopenharmony_ci	 * the bitrate to determine supported modes. Some BiDi modules (eg,
3598c2ecf20Sopenharmony_ci	 * 1310nm/1550nm) are not 1000BASE-BX compliant due to the differing
3608c2ecf20Sopenharmony_ci	 * wavelengths, so do not set any transceiver bits.
3618c2ecf20Sopenharmony_ci	 */
3628c2ecf20Sopenharmony_ci	if (bitmap_empty(modes, __ETHTOOL_LINK_MODE_MASK_NBITS)) {
3638c2ecf20Sopenharmony_ci		/* If the bit rate allows 1000baseX */
3648c2ecf20Sopenharmony_ci		if (br_nom && br_min <= 1300 && br_max >= 1200)
3658c2ecf20Sopenharmony_ci			phylink_set(modes, 1000baseX_Full);
3668c2ecf20Sopenharmony_ci	}
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	if (bus->sfp_quirk)
3698c2ecf20Sopenharmony_ci		bus->sfp_quirk->modes(id, modes);
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	bitmap_or(support, support, modes, __ETHTOOL_LINK_MODE_MASK_NBITS);
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	phylink_set(support, Autoneg);
3748c2ecf20Sopenharmony_ci	phylink_set(support, Pause);
3758c2ecf20Sopenharmony_ci	phylink_set(support, Asym_Pause);
3768c2ecf20Sopenharmony_ci}
3778c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sfp_parse_support);
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci/**
3808c2ecf20Sopenharmony_ci * sfp_select_interface() - Select appropriate phy_interface_t mode
3818c2ecf20Sopenharmony_ci * @bus: a pointer to the &struct sfp_bus structure for the sfp module
3828c2ecf20Sopenharmony_ci * @link_modes: ethtool link modes mask
3838c2ecf20Sopenharmony_ci *
3848c2ecf20Sopenharmony_ci * Derive the phy_interface_t mode for the SFP module from the link
3858c2ecf20Sopenharmony_ci * modes mask.
3868c2ecf20Sopenharmony_ci */
3878c2ecf20Sopenharmony_ciphy_interface_t sfp_select_interface(struct sfp_bus *bus,
3888c2ecf20Sopenharmony_ci				     unsigned long *link_modes)
3898c2ecf20Sopenharmony_ci{
3908c2ecf20Sopenharmony_ci	if (phylink_test(link_modes, 10000baseCR_Full) ||
3918c2ecf20Sopenharmony_ci	    phylink_test(link_modes, 10000baseSR_Full) ||
3928c2ecf20Sopenharmony_ci	    phylink_test(link_modes, 10000baseLR_Full) ||
3938c2ecf20Sopenharmony_ci	    phylink_test(link_modes, 10000baseLRM_Full) ||
3948c2ecf20Sopenharmony_ci	    phylink_test(link_modes, 10000baseER_Full) ||
3958c2ecf20Sopenharmony_ci	    phylink_test(link_modes, 10000baseT_Full))
3968c2ecf20Sopenharmony_ci		return PHY_INTERFACE_MODE_10GBASER;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	if (phylink_test(link_modes, 2500baseX_Full))
3998c2ecf20Sopenharmony_ci		return PHY_INTERFACE_MODE_2500BASEX;
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	if (phylink_test(link_modes, 1000baseT_Half) ||
4028c2ecf20Sopenharmony_ci	    phylink_test(link_modes, 1000baseT_Full))
4038c2ecf20Sopenharmony_ci		return PHY_INTERFACE_MODE_SGMII;
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	if (phylink_test(link_modes, 1000baseX_Full))
4068c2ecf20Sopenharmony_ci		return PHY_INTERFACE_MODE_1000BASEX;
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	dev_warn(bus->sfp_dev, "Unable to ascertain link mode\n");
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	return PHY_INTERFACE_MODE_NA;
4118c2ecf20Sopenharmony_ci}
4128c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sfp_select_interface);
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_cistatic LIST_HEAD(sfp_buses);
4158c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(sfp_mutex);
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_cistatic const struct sfp_upstream_ops *sfp_get_upstream_ops(struct sfp_bus *bus)
4188c2ecf20Sopenharmony_ci{
4198c2ecf20Sopenharmony_ci	return bus->registered ? bus->upstream_ops : NULL;
4208c2ecf20Sopenharmony_ci}
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_cistatic struct sfp_bus *sfp_bus_get(struct fwnode_handle *fwnode)
4238c2ecf20Sopenharmony_ci{
4248c2ecf20Sopenharmony_ci	struct sfp_bus *sfp, *new, *found = NULL;
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	new = kzalloc(sizeof(*new), GFP_KERNEL);
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	mutex_lock(&sfp_mutex);
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	list_for_each_entry(sfp, &sfp_buses, node) {
4318c2ecf20Sopenharmony_ci		if (sfp->fwnode == fwnode) {
4328c2ecf20Sopenharmony_ci			kref_get(&sfp->kref);
4338c2ecf20Sopenharmony_ci			found = sfp;
4348c2ecf20Sopenharmony_ci			break;
4358c2ecf20Sopenharmony_ci		}
4368c2ecf20Sopenharmony_ci	}
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	if (!found && new) {
4398c2ecf20Sopenharmony_ci		kref_init(&new->kref);
4408c2ecf20Sopenharmony_ci		new->fwnode = fwnode;
4418c2ecf20Sopenharmony_ci		list_add(&new->node, &sfp_buses);
4428c2ecf20Sopenharmony_ci		found = new;
4438c2ecf20Sopenharmony_ci		new = NULL;
4448c2ecf20Sopenharmony_ci	}
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	mutex_unlock(&sfp_mutex);
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	kfree(new);
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	return found;
4518c2ecf20Sopenharmony_ci}
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_cistatic void sfp_bus_release(struct kref *kref)
4548c2ecf20Sopenharmony_ci{
4558c2ecf20Sopenharmony_ci	struct sfp_bus *bus = container_of(kref, struct sfp_bus, kref);
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	list_del(&bus->node);
4588c2ecf20Sopenharmony_ci	mutex_unlock(&sfp_mutex);
4598c2ecf20Sopenharmony_ci	kfree(bus);
4608c2ecf20Sopenharmony_ci}
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci/**
4638c2ecf20Sopenharmony_ci * sfp_bus_put() - put a reference on the &struct sfp_bus
4648c2ecf20Sopenharmony_ci * @bus: the &struct sfp_bus found via sfp_bus_find_fwnode()
4658c2ecf20Sopenharmony_ci *
4668c2ecf20Sopenharmony_ci * Put a reference on the &struct sfp_bus and free the underlying structure
4678c2ecf20Sopenharmony_ci * if this was the last reference.
4688c2ecf20Sopenharmony_ci */
4698c2ecf20Sopenharmony_civoid sfp_bus_put(struct sfp_bus *bus)
4708c2ecf20Sopenharmony_ci{
4718c2ecf20Sopenharmony_ci	if (bus)
4728c2ecf20Sopenharmony_ci		kref_put_mutex(&bus->kref, sfp_bus_release, &sfp_mutex);
4738c2ecf20Sopenharmony_ci}
4748c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sfp_bus_put);
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_cistatic int sfp_register_bus(struct sfp_bus *bus)
4778c2ecf20Sopenharmony_ci{
4788c2ecf20Sopenharmony_ci	const struct sfp_upstream_ops *ops = bus->upstream_ops;
4798c2ecf20Sopenharmony_ci	int ret;
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	if (ops) {
4828c2ecf20Sopenharmony_ci		if (ops->link_down)
4838c2ecf20Sopenharmony_ci			ops->link_down(bus->upstream);
4848c2ecf20Sopenharmony_ci		if (ops->connect_phy && bus->phydev) {
4858c2ecf20Sopenharmony_ci			ret = ops->connect_phy(bus->upstream, bus->phydev);
4868c2ecf20Sopenharmony_ci			if (ret)
4878c2ecf20Sopenharmony_ci				return ret;
4888c2ecf20Sopenharmony_ci		}
4898c2ecf20Sopenharmony_ci	}
4908c2ecf20Sopenharmony_ci	bus->registered = true;
4918c2ecf20Sopenharmony_ci	bus->socket_ops->attach(bus->sfp);
4928c2ecf20Sopenharmony_ci	if (bus->started)
4938c2ecf20Sopenharmony_ci		bus->socket_ops->start(bus->sfp);
4948c2ecf20Sopenharmony_ci	bus->upstream_ops->attach(bus->upstream, bus);
4958c2ecf20Sopenharmony_ci	return 0;
4968c2ecf20Sopenharmony_ci}
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_cistatic void sfp_unregister_bus(struct sfp_bus *bus)
4998c2ecf20Sopenharmony_ci{
5008c2ecf20Sopenharmony_ci	const struct sfp_upstream_ops *ops = bus->upstream_ops;
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	if (bus->registered) {
5038c2ecf20Sopenharmony_ci		bus->upstream_ops->detach(bus->upstream, bus);
5048c2ecf20Sopenharmony_ci		if (bus->started)
5058c2ecf20Sopenharmony_ci			bus->socket_ops->stop(bus->sfp);
5068c2ecf20Sopenharmony_ci		bus->socket_ops->detach(bus->sfp);
5078c2ecf20Sopenharmony_ci		if (bus->phydev && ops && ops->disconnect_phy)
5088c2ecf20Sopenharmony_ci			ops->disconnect_phy(bus->upstream);
5098c2ecf20Sopenharmony_ci	}
5108c2ecf20Sopenharmony_ci	bus->registered = false;
5118c2ecf20Sopenharmony_ci}
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci/**
5148c2ecf20Sopenharmony_ci * sfp_get_module_info() - Get the ethtool_modinfo for a SFP module
5158c2ecf20Sopenharmony_ci * @bus: a pointer to the &struct sfp_bus structure for the sfp module
5168c2ecf20Sopenharmony_ci * @modinfo: a &struct ethtool_modinfo
5178c2ecf20Sopenharmony_ci *
5188c2ecf20Sopenharmony_ci * Fill in the type and eeprom_len parameters in @modinfo for a module on
5198c2ecf20Sopenharmony_ci * the sfp bus specified by @bus.
5208c2ecf20Sopenharmony_ci *
5218c2ecf20Sopenharmony_ci * Returns 0 on success or a negative errno number.
5228c2ecf20Sopenharmony_ci */
5238c2ecf20Sopenharmony_ciint sfp_get_module_info(struct sfp_bus *bus, struct ethtool_modinfo *modinfo)
5248c2ecf20Sopenharmony_ci{
5258c2ecf20Sopenharmony_ci	return bus->socket_ops->module_info(bus->sfp, modinfo);
5268c2ecf20Sopenharmony_ci}
5278c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sfp_get_module_info);
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci/**
5308c2ecf20Sopenharmony_ci * sfp_get_module_eeprom() - Read the SFP module EEPROM
5318c2ecf20Sopenharmony_ci * @bus: a pointer to the &struct sfp_bus structure for the sfp module
5328c2ecf20Sopenharmony_ci * @ee: a &struct ethtool_eeprom
5338c2ecf20Sopenharmony_ci * @data: buffer to contain the EEPROM data (must be at least @ee->len bytes)
5348c2ecf20Sopenharmony_ci *
5358c2ecf20Sopenharmony_ci * Read the EEPROM as specified by the supplied @ee. See the documentation
5368c2ecf20Sopenharmony_ci * for &struct ethtool_eeprom for the region to be read.
5378c2ecf20Sopenharmony_ci *
5388c2ecf20Sopenharmony_ci * Returns 0 on success or a negative errno number.
5398c2ecf20Sopenharmony_ci */
5408c2ecf20Sopenharmony_ciint sfp_get_module_eeprom(struct sfp_bus *bus, struct ethtool_eeprom *ee,
5418c2ecf20Sopenharmony_ci			  u8 *data)
5428c2ecf20Sopenharmony_ci{
5438c2ecf20Sopenharmony_ci	return bus->socket_ops->module_eeprom(bus->sfp, ee, data);
5448c2ecf20Sopenharmony_ci}
5458c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sfp_get_module_eeprom);
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci/**
5488c2ecf20Sopenharmony_ci * sfp_upstream_start() - Inform the SFP that the network device is up
5498c2ecf20Sopenharmony_ci * @bus: a pointer to the &struct sfp_bus structure for the sfp module
5508c2ecf20Sopenharmony_ci *
5518c2ecf20Sopenharmony_ci * Inform the SFP socket that the network device is now up, so that the
5528c2ecf20Sopenharmony_ci * module can be enabled by allowing TX_DISABLE to be deasserted. This
5538c2ecf20Sopenharmony_ci * should be called from the network device driver's &struct net_device_ops
5548c2ecf20Sopenharmony_ci * ndo_open() method.
5558c2ecf20Sopenharmony_ci */
5568c2ecf20Sopenharmony_civoid sfp_upstream_start(struct sfp_bus *bus)
5578c2ecf20Sopenharmony_ci{
5588c2ecf20Sopenharmony_ci	if (bus->registered)
5598c2ecf20Sopenharmony_ci		bus->socket_ops->start(bus->sfp);
5608c2ecf20Sopenharmony_ci	bus->started = true;
5618c2ecf20Sopenharmony_ci}
5628c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sfp_upstream_start);
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci/**
5658c2ecf20Sopenharmony_ci * sfp_upstream_stop() - Inform the SFP that the network device is down
5668c2ecf20Sopenharmony_ci * @bus: a pointer to the &struct sfp_bus structure for the sfp module
5678c2ecf20Sopenharmony_ci *
5688c2ecf20Sopenharmony_ci * Inform the SFP socket that the network device is now up, so that the
5698c2ecf20Sopenharmony_ci * module can be disabled by asserting TX_DISABLE, disabling the laser
5708c2ecf20Sopenharmony_ci * in optical modules. This should be called from the network device
5718c2ecf20Sopenharmony_ci * driver's &struct net_device_ops ndo_stop() method.
5728c2ecf20Sopenharmony_ci */
5738c2ecf20Sopenharmony_civoid sfp_upstream_stop(struct sfp_bus *bus)
5748c2ecf20Sopenharmony_ci{
5758c2ecf20Sopenharmony_ci	if (bus->registered)
5768c2ecf20Sopenharmony_ci		bus->socket_ops->stop(bus->sfp);
5778c2ecf20Sopenharmony_ci	bus->started = false;
5788c2ecf20Sopenharmony_ci}
5798c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sfp_upstream_stop);
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_cistatic void sfp_upstream_clear(struct sfp_bus *bus)
5828c2ecf20Sopenharmony_ci{
5838c2ecf20Sopenharmony_ci	bus->upstream_ops = NULL;
5848c2ecf20Sopenharmony_ci	bus->upstream = NULL;
5858c2ecf20Sopenharmony_ci}
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci/**
5888c2ecf20Sopenharmony_ci * sfp_bus_find_fwnode() - parse and locate the SFP bus from fwnode
5898c2ecf20Sopenharmony_ci * @fwnode: firmware node for the parent device (MAC or PHY)
5908c2ecf20Sopenharmony_ci *
5918c2ecf20Sopenharmony_ci * Parse the parent device's firmware node for a SFP bus, and locate
5928c2ecf20Sopenharmony_ci * the sfp_bus structure, incrementing its reference count.  This must
5938c2ecf20Sopenharmony_ci * be put via sfp_bus_put() when done.
5948c2ecf20Sopenharmony_ci *
5958c2ecf20Sopenharmony_ci * Returns:
5968c2ecf20Sopenharmony_ci * 	    - on success, a pointer to the sfp_bus structure,
5978c2ecf20Sopenharmony_ci *	    - %NULL if no SFP is specified,
5988c2ecf20Sopenharmony_ci * 	    - on failure, an error pointer value:
5998c2ecf20Sopenharmony_ci *
6008c2ecf20Sopenharmony_ci * 	      - corresponding to the errors detailed for
6018c2ecf20Sopenharmony_ci * 	        fwnode_property_get_reference_args().
6028c2ecf20Sopenharmony_ci * 	      - %-ENOMEM if we failed to allocate the bus.
6038c2ecf20Sopenharmony_ci *	      - an error from the upstream's connect_phy() method.
6048c2ecf20Sopenharmony_ci */
6058c2ecf20Sopenharmony_cistruct sfp_bus *sfp_bus_find_fwnode(struct fwnode_handle *fwnode)
6068c2ecf20Sopenharmony_ci{
6078c2ecf20Sopenharmony_ci	struct fwnode_reference_args ref;
6088c2ecf20Sopenharmony_ci	struct sfp_bus *bus;
6098c2ecf20Sopenharmony_ci	int ret;
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	ret = fwnode_property_get_reference_args(fwnode, "sfp", NULL,
6128c2ecf20Sopenharmony_ci						 0, 0, &ref);
6138c2ecf20Sopenharmony_ci	if (ret == -ENOENT)
6148c2ecf20Sopenharmony_ci		return NULL;
6158c2ecf20Sopenharmony_ci	else if (ret < 0)
6168c2ecf20Sopenharmony_ci		return ERR_PTR(ret);
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci	if (!fwnode_device_is_available(ref.fwnode)) {
6198c2ecf20Sopenharmony_ci		fwnode_handle_put(ref.fwnode);
6208c2ecf20Sopenharmony_ci		return NULL;
6218c2ecf20Sopenharmony_ci	}
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci	bus = sfp_bus_get(ref.fwnode);
6248c2ecf20Sopenharmony_ci	fwnode_handle_put(ref.fwnode);
6258c2ecf20Sopenharmony_ci	if (!bus)
6268c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci	return bus;
6298c2ecf20Sopenharmony_ci}
6308c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sfp_bus_find_fwnode);
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci/**
6338c2ecf20Sopenharmony_ci * sfp_bus_add_upstream() - parse and register the neighbouring device
6348c2ecf20Sopenharmony_ci * @bus: the &struct sfp_bus found via sfp_bus_find_fwnode()
6358c2ecf20Sopenharmony_ci * @upstream: the upstream private data
6368c2ecf20Sopenharmony_ci * @ops: the upstream's &struct sfp_upstream_ops
6378c2ecf20Sopenharmony_ci *
6388c2ecf20Sopenharmony_ci * Add upstream driver for the SFP bus, and if the bus is complete, register
6398c2ecf20Sopenharmony_ci * the SFP bus using sfp_register_upstream().  This takes a reference on the
6408c2ecf20Sopenharmony_ci * bus, so it is safe to put the bus after this call.
6418c2ecf20Sopenharmony_ci *
6428c2ecf20Sopenharmony_ci * Returns:
6438c2ecf20Sopenharmony_ci * 	    - on success, a pointer to the sfp_bus structure,
6448c2ecf20Sopenharmony_ci *	    - %NULL if no SFP is specified,
6458c2ecf20Sopenharmony_ci * 	    - on failure, an error pointer value:
6468c2ecf20Sopenharmony_ci *
6478c2ecf20Sopenharmony_ci * 	      - corresponding to the errors detailed for
6488c2ecf20Sopenharmony_ci * 	        fwnode_property_get_reference_args().
6498c2ecf20Sopenharmony_ci * 	      - %-ENOMEM if we failed to allocate the bus.
6508c2ecf20Sopenharmony_ci *	      - an error from the upstream's connect_phy() method.
6518c2ecf20Sopenharmony_ci */
6528c2ecf20Sopenharmony_ciint sfp_bus_add_upstream(struct sfp_bus *bus, void *upstream,
6538c2ecf20Sopenharmony_ci			 const struct sfp_upstream_ops *ops)
6548c2ecf20Sopenharmony_ci{
6558c2ecf20Sopenharmony_ci	int ret;
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci	/* If no bus, return success */
6588c2ecf20Sopenharmony_ci	if (!bus)
6598c2ecf20Sopenharmony_ci		return 0;
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci	rtnl_lock();
6628c2ecf20Sopenharmony_ci	kref_get(&bus->kref);
6638c2ecf20Sopenharmony_ci	bus->upstream_ops = ops;
6648c2ecf20Sopenharmony_ci	bus->upstream = upstream;
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci	if (bus->sfp) {
6678c2ecf20Sopenharmony_ci		ret = sfp_register_bus(bus);
6688c2ecf20Sopenharmony_ci		if (ret)
6698c2ecf20Sopenharmony_ci			sfp_upstream_clear(bus);
6708c2ecf20Sopenharmony_ci	} else {
6718c2ecf20Sopenharmony_ci		ret = 0;
6728c2ecf20Sopenharmony_ci	}
6738c2ecf20Sopenharmony_ci	rtnl_unlock();
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci	if (ret)
6768c2ecf20Sopenharmony_ci		sfp_bus_put(bus);
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci	return ret;
6798c2ecf20Sopenharmony_ci}
6808c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sfp_bus_add_upstream);
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci/**
6838c2ecf20Sopenharmony_ci * sfp_bus_del_upstream() - Delete a sfp bus
6848c2ecf20Sopenharmony_ci * @bus: a pointer to the &struct sfp_bus structure for the sfp module
6858c2ecf20Sopenharmony_ci *
6868c2ecf20Sopenharmony_ci * Delete a previously registered upstream connection for the SFP
6878c2ecf20Sopenharmony_ci * module. @bus should have been added by sfp_bus_add_upstream().
6888c2ecf20Sopenharmony_ci */
6898c2ecf20Sopenharmony_civoid sfp_bus_del_upstream(struct sfp_bus *bus)
6908c2ecf20Sopenharmony_ci{
6918c2ecf20Sopenharmony_ci	if (bus) {
6928c2ecf20Sopenharmony_ci		rtnl_lock();
6938c2ecf20Sopenharmony_ci		if (bus->sfp)
6948c2ecf20Sopenharmony_ci			sfp_unregister_bus(bus);
6958c2ecf20Sopenharmony_ci		sfp_upstream_clear(bus);
6968c2ecf20Sopenharmony_ci		rtnl_unlock();
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci		sfp_bus_put(bus);
6998c2ecf20Sopenharmony_ci	}
7008c2ecf20Sopenharmony_ci}
7018c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sfp_bus_del_upstream);
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ci/* Socket driver entry points */
7048c2ecf20Sopenharmony_ciint sfp_add_phy(struct sfp_bus *bus, struct phy_device *phydev)
7058c2ecf20Sopenharmony_ci{
7068c2ecf20Sopenharmony_ci	const struct sfp_upstream_ops *ops = sfp_get_upstream_ops(bus);
7078c2ecf20Sopenharmony_ci	int ret = 0;
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ci	if (ops && ops->connect_phy)
7108c2ecf20Sopenharmony_ci		ret = ops->connect_phy(bus->upstream, phydev);
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci	if (ret == 0)
7138c2ecf20Sopenharmony_ci		bus->phydev = phydev;
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	return ret;
7168c2ecf20Sopenharmony_ci}
7178c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sfp_add_phy);
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_civoid sfp_remove_phy(struct sfp_bus *bus)
7208c2ecf20Sopenharmony_ci{
7218c2ecf20Sopenharmony_ci	const struct sfp_upstream_ops *ops = sfp_get_upstream_ops(bus);
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci	if (ops && ops->disconnect_phy)
7248c2ecf20Sopenharmony_ci		ops->disconnect_phy(bus->upstream);
7258c2ecf20Sopenharmony_ci	bus->phydev = NULL;
7268c2ecf20Sopenharmony_ci}
7278c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sfp_remove_phy);
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_civoid sfp_link_up(struct sfp_bus *bus)
7308c2ecf20Sopenharmony_ci{
7318c2ecf20Sopenharmony_ci	const struct sfp_upstream_ops *ops = sfp_get_upstream_ops(bus);
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	if (ops && ops->link_up)
7348c2ecf20Sopenharmony_ci		ops->link_up(bus->upstream);
7358c2ecf20Sopenharmony_ci}
7368c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sfp_link_up);
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_civoid sfp_link_down(struct sfp_bus *bus)
7398c2ecf20Sopenharmony_ci{
7408c2ecf20Sopenharmony_ci	const struct sfp_upstream_ops *ops = sfp_get_upstream_ops(bus);
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_ci	if (ops && ops->link_down)
7438c2ecf20Sopenharmony_ci		ops->link_down(bus->upstream);
7448c2ecf20Sopenharmony_ci}
7458c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sfp_link_down);
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ciint sfp_module_insert(struct sfp_bus *bus, const struct sfp_eeprom_id *id)
7488c2ecf20Sopenharmony_ci{
7498c2ecf20Sopenharmony_ci	const struct sfp_upstream_ops *ops = sfp_get_upstream_ops(bus);
7508c2ecf20Sopenharmony_ci	int ret = 0;
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci	bus->sfp_quirk = sfp_lookup_quirk(id);
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci	if (ops && ops->module_insert)
7558c2ecf20Sopenharmony_ci		ret = ops->module_insert(bus->upstream, id);
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	return ret;
7588c2ecf20Sopenharmony_ci}
7598c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sfp_module_insert);
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_civoid sfp_module_remove(struct sfp_bus *bus)
7628c2ecf20Sopenharmony_ci{
7638c2ecf20Sopenharmony_ci	const struct sfp_upstream_ops *ops = sfp_get_upstream_ops(bus);
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ci	if (ops && ops->module_remove)
7668c2ecf20Sopenharmony_ci		ops->module_remove(bus->upstream);
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ci	bus->sfp_quirk = NULL;
7698c2ecf20Sopenharmony_ci}
7708c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sfp_module_remove);
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ciint sfp_module_start(struct sfp_bus *bus)
7738c2ecf20Sopenharmony_ci{
7748c2ecf20Sopenharmony_ci	const struct sfp_upstream_ops *ops = sfp_get_upstream_ops(bus);
7758c2ecf20Sopenharmony_ci	int ret = 0;
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_ci	if (ops && ops->module_start)
7788c2ecf20Sopenharmony_ci		ret = ops->module_start(bus->upstream);
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ci	return ret;
7818c2ecf20Sopenharmony_ci}
7828c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sfp_module_start);
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_civoid sfp_module_stop(struct sfp_bus *bus)
7858c2ecf20Sopenharmony_ci{
7868c2ecf20Sopenharmony_ci	const struct sfp_upstream_ops *ops = sfp_get_upstream_ops(bus);
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_ci	if (ops && ops->module_stop)
7898c2ecf20Sopenharmony_ci		ops->module_stop(bus->upstream);
7908c2ecf20Sopenharmony_ci}
7918c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sfp_module_stop);
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_cistatic void sfp_socket_clear(struct sfp_bus *bus)
7948c2ecf20Sopenharmony_ci{
7958c2ecf20Sopenharmony_ci	bus->sfp_dev = NULL;
7968c2ecf20Sopenharmony_ci	bus->sfp = NULL;
7978c2ecf20Sopenharmony_ci	bus->socket_ops = NULL;
7988c2ecf20Sopenharmony_ci}
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_cistruct sfp_bus *sfp_register_socket(struct device *dev, struct sfp *sfp,
8018c2ecf20Sopenharmony_ci				    const struct sfp_socket_ops *ops)
8028c2ecf20Sopenharmony_ci{
8038c2ecf20Sopenharmony_ci	struct sfp_bus *bus = sfp_bus_get(dev->fwnode);
8048c2ecf20Sopenharmony_ci	int ret = 0;
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_ci	if (bus) {
8078c2ecf20Sopenharmony_ci		rtnl_lock();
8088c2ecf20Sopenharmony_ci		bus->sfp_dev = dev;
8098c2ecf20Sopenharmony_ci		bus->sfp = sfp;
8108c2ecf20Sopenharmony_ci		bus->socket_ops = ops;
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci		if (bus->upstream_ops) {
8138c2ecf20Sopenharmony_ci			ret = sfp_register_bus(bus);
8148c2ecf20Sopenharmony_ci			if (ret)
8158c2ecf20Sopenharmony_ci				sfp_socket_clear(bus);
8168c2ecf20Sopenharmony_ci		}
8178c2ecf20Sopenharmony_ci		rtnl_unlock();
8188c2ecf20Sopenharmony_ci	}
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_ci	if (ret) {
8218c2ecf20Sopenharmony_ci		sfp_bus_put(bus);
8228c2ecf20Sopenharmony_ci		bus = NULL;
8238c2ecf20Sopenharmony_ci	}
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ci	return bus;
8268c2ecf20Sopenharmony_ci}
8278c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sfp_register_socket);
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_civoid sfp_unregister_socket(struct sfp_bus *bus)
8308c2ecf20Sopenharmony_ci{
8318c2ecf20Sopenharmony_ci	rtnl_lock();
8328c2ecf20Sopenharmony_ci	if (bus->upstream_ops)
8338c2ecf20Sopenharmony_ci		sfp_unregister_bus(bus);
8348c2ecf20Sopenharmony_ci	sfp_socket_clear(bus);
8358c2ecf20Sopenharmony_ci	rtnl_unlock();
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci	sfp_bus_put(bus);
8388c2ecf20Sopenharmony_ci}
8398c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sfp_unregister_socket);
840