162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Core PHY library, taken from phy.c
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci#include <linux/export.h>
662306a36Sopenharmony_ci#include <linux/phy.h>
762306a36Sopenharmony_ci#include <linux/of.h>
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci/**
1062306a36Sopenharmony_ci * phy_speed_to_str - Return a string representing the PHY link speed
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci * @speed: Speed of the link
1362306a36Sopenharmony_ci */
1462306a36Sopenharmony_ciconst char *phy_speed_to_str(int speed)
1562306a36Sopenharmony_ci{
1662306a36Sopenharmony_ci	BUILD_BUG_ON_MSG(__ETHTOOL_LINK_MODE_MASK_NBITS != 102,
1762306a36Sopenharmony_ci		"Enum ethtool_link_mode_bit_indices and phylib are out of sync. "
1862306a36Sopenharmony_ci		"If a speed or mode has been added please update phy_speed_to_str "
1962306a36Sopenharmony_ci		"and the PHY settings array.\n");
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci	switch (speed) {
2262306a36Sopenharmony_ci	case SPEED_10:
2362306a36Sopenharmony_ci		return "10Mbps";
2462306a36Sopenharmony_ci	case SPEED_100:
2562306a36Sopenharmony_ci		return "100Mbps";
2662306a36Sopenharmony_ci	case SPEED_1000:
2762306a36Sopenharmony_ci		return "1Gbps";
2862306a36Sopenharmony_ci	case SPEED_2500:
2962306a36Sopenharmony_ci		return "2.5Gbps";
3062306a36Sopenharmony_ci	case SPEED_5000:
3162306a36Sopenharmony_ci		return "5Gbps";
3262306a36Sopenharmony_ci	case SPEED_10000:
3362306a36Sopenharmony_ci		return "10Gbps";
3462306a36Sopenharmony_ci	case SPEED_14000:
3562306a36Sopenharmony_ci		return "14Gbps";
3662306a36Sopenharmony_ci	case SPEED_20000:
3762306a36Sopenharmony_ci		return "20Gbps";
3862306a36Sopenharmony_ci	case SPEED_25000:
3962306a36Sopenharmony_ci		return "25Gbps";
4062306a36Sopenharmony_ci	case SPEED_40000:
4162306a36Sopenharmony_ci		return "40Gbps";
4262306a36Sopenharmony_ci	case SPEED_50000:
4362306a36Sopenharmony_ci		return "50Gbps";
4462306a36Sopenharmony_ci	case SPEED_56000:
4562306a36Sopenharmony_ci		return "56Gbps";
4662306a36Sopenharmony_ci	case SPEED_100000:
4762306a36Sopenharmony_ci		return "100Gbps";
4862306a36Sopenharmony_ci	case SPEED_200000:
4962306a36Sopenharmony_ci		return "200Gbps";
5062306a36Sopenharmony_ci	case SPEED_400000:
5162306a36Sopenharmony_ci		return "400Gbps";
5262306a36Sopenharmony_ci	case SPEED_800000:
5362306a36Sopenharmony_ci		return "800Gbps";
5462306a36Sopenharmony_ci	case SPEED_UNKNOWN:
5562306a36Sopenharmony_ci		return "Unknown";
5662306a36Sopenharmony_ci	default:
5762306a36Sopenharmony_ci		return "Unsupported (update phy-core.c)";
5862306a36Sopenharmony_ci	}
5962306a36Sopenharmony_ci}
6062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(phy_speed_to_str);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci/**
6362306a36Sopenharmony_ci * phy_duplex_to_str - Return string describing the duplex
6462306a36Sopenharmony_ci *
6562306a36Sopenharmony_ci * @duplex: Duplex setting to describe
6662306a36Sopenharmony_ci */
6762306a36Sopenharmony_ciconst char *phy_duplex_to_str(unsigned int duplex)
6862306a36Sopenharmony_ci{
6962306a36Sopenharmony_ci	if (duplex == DUPLEX_HALF)
7062306a36Sopenharmony_ci		return "Half";
7162306a36Sopenharmony_ci	if (duplex == DUPLEX_FULL)
7262306a36Sopenharmony_ci		return "Full";
7362306a36Sopenharmony_ci	if (duplex == DUPLEX_UNKNOWN)
7462306a36Sopenharmony_ci		return "Unknown";
7562306a36Sopenharmony_ci	return "Unsupported (update phy-core.c)";
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(phy_duplex_to_str);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci/**
8062306a36Sopenharmony_ci * phy_rate_matching_to_str - Return a string describing the rate matching
8162306a36Sopenharmony_ci *
8262306a36Sopenharmony_ci * @rate_matching: Type of rate matching to describe
8362306a36Sopenharmony_ci */
8462306a36Sopenharmony_ciconst char *phy_rate_matching_to_str(int rate_matching)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	switch (rate_matching) {
8762306a36Sopenharmony_ci	case RATE_MATCH_NONE:
8862306a36Sopenharmony_ci		return "none";
8962306a36Sopenharmony_ci	case RATE_MATCH_PAUSE:
9062306a36Sopenharmony_ci		return "pause";
9162306a36Sopenharmony_ci	case RATE_MATCH_CRS:
9262306a36Sopenharmony_ci		return "crs";
9362306a36Sopenharmony_ci	case RATE_MATCH_OPEN_LOOP:
9462306a36Sopenharmony_ci		return "open-loop";
9562306a36Sopenharmony_ci	}
9662306a36Sopenharmony_ci	return "Unsupported (update phy-core.c)";
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(phy_rate_matching_to_str);
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci/**
10162306a36Sopenharmony_ci * phy_interface_num_ports - Return the number of links that can be carried by
10262306a36Sopenharmony_ci *			     a given MAC-PHY physical link. Returns 0 if this is
10362306a36Sopenharmony_ci *			     unknown, the number of links else.
10462306a36Sopenharmony_ci *
10562306a36Sopenharmony_ci * @interface: The interface mode we want to get the number of ports
10662306a36Sopenharmony_ci */
10762306a36Sopenharmony_ciint phy_interface_num_ports(phy_interface_t interface)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	switch (interface) {
11062306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_NA:
11162306a36Sopenharmony_ci		return 0;
11262306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_INTERNAL:
11362306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_MII:
11462306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_GMII:
11562306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_TBI:
11662306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_REVMII:
11762306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_RMII:
11862306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_REVRMII:
11962306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_RGMII:
12062306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_RGMII_ID:
12162306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_RGMII_RXID:
12262306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_RGMII_TXID:
12362306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_RTBI:
12462306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_XGMII:
12562306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_XLGMII:
12662306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_MOCA:
12762306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_TRGMII:
12862306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_USXGMII:
12962306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_SGMII:
13062306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_SMII:
13162306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_1000BASEX:
13262306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_2500BASEX:
13362306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_5GBASER:
13462306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_10GBASER:
13562306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_25GBASER:
13662306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_10GKR:
13762306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_100BASEX:
13862306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_RXAUI:
13962306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_XAUI:
14062306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_1000BASEKX:
14162306a36Sopenharmony_ci		return 1;
14262306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_QSGMII:
14362306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_QUSGMII:
14462306a36Sopenharmony_ci		return 4;
14562306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_PSGMII:
14662306a36Sopenharmony_ci		return 5;
14762306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_MAX:
14862306a36Sopenharmony_ci		WARN_ONCE(1, "PHY_INTERFACE_MODE_MAX isn't a valid interface mode");
14962306a36Sopenharmony_ci		return 0;
15062306a36Sopenharmony_ci	}
15162306a36Sopenharmony_ci	return 0;
15262306a36Sopenharmony_ci}
15362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(phy_interface_num_ports);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci/* A mapping of all SUPPORTED settings to speed/duplex.  This table
15662306a36Sopenharmony_ci * must be grouped by speed and sorted in descending match priority
15762306a36Sopenharmony_ci * - iow, descending speed.
15862306a36Sopenharmony_ci */
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci#define PHY_SETTING(s, d, b) { .speed = SPEED_ ## s, .duplex = DUPLEX_ ## d, \
16162306a36Sopenharmony_ci			       .bit = ETHTOOL_LINK_MODE_ ## b ## _BIT}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_cistatic const struct phy_setting settings[] = {
16462306a36Sopenharmony_ci	/* 800G */
16562306a36Sopenharmony_ci	PHY_SETTING( 800000, FULL, 800000baseCR8_Full		),
16662306a36Sopenharmony_ci	PHY_SETTING( 800000, FULL, 800000baseKR8_Full		),
16762306a36Sopenharmony_ci	PHY_SETTING( 800000, FULL, 800000baseDR8_Full		),
16862306a36Sopenharmony_ci	PHY_SETTING( 800000, FULL, 800000baseDR8_2_Full		),
16962306a36Sopenharmony_ci	PHY_SETTING( 800000, FULL, 800000baseSR8_Full		),
17062306a36Sopenharmony_ci	PHY_SETTING( 800000, FULL, 800000baseVR8_Full		),
17162306a36Sopenharmony_ci	/* 400G */
17262306a36Sopenharmony_ci	PHY_SETTING( 400000, FULL, 400000baseCR8_Full		),
17362306a36Sopenharmony_ci	PHY_SETTING( 400000, FULL, 400000baseKR8_Full		),
17462306a36Sopenharmony_ci	PHY_SETTING( 400000, FULL, 400000baseLR8_ER8_FR8_Full	),
17562306a36Sopenharmony_ci	PHY_SETTING( 400000, FULL, 400000baseDR8_Full		),
17662306a36Sopenharmony_ci	PHY_SETTING( 400000, FULL, 400000baseSR8_Full		),
17762306a36Sopenharmony_ci	PHY_SETTING( 400000, FULL, 400000baseCR4_Full		),
17862306a36Sopenharmony_ci	PHY_SETTING( 400000, FULL, 400000baseKR4_Full		),
17962306a36Sopenharmony_ci	PHY_SETTING( 400000, FULL, 400000baseLR4_ER4_FR4_Full	),
18062306a36Sopenharmony_ci	PHY_SETTING( 400000, FULL, 400000baseDR4_Full		),
18162306a36Sopenharmony_ci	PHY_SETTING( 400000, FULL, 400000baseSR4_Full		),
18262306a36Sopenharmony_ci	/* 200G */
18362306a36Sopenharmony_ci	PHY_SETTING( 200000, FULL, 200000baseCR4_Full		),
18462306a36Sopenharmony_ci	PHY_SETTING( 200000, FULL, 200000baseKR4_Full		),
18562306a36Sopenharmony_ci	PHY_SETTING( 200000, FULL, 200000baseLR4_ER4_FR4_Full	),
18662306a36Sopenharmony_ci	PHY_SETTING( 200000, FULL, 200000baseDR4_Full		),
18762306a36Sopenharmony_ci	PHY_SETTING( 200000, FULL, 200000baseSR4_Full		),
18862306a36Sopenharmony_ci	PHY_SETTING( 200000, FULL, 200000baseCR2_Full		),
18962306a36Sopenharmony_ci	PHY_SETTING( 200000, FULL, 200000baseKR2_Full		),
19062306a36Sopenharmony_ci	PHY_SETTING( 200000, FULL, 200000baseLR2_ER2_FR2_Full	),
19162306a36Sopenharmony_ci	PHY_SETTING( 200000, FULL, 200000baseDR2_Full		),
19262306a36Sopenharmony_ci	PHY_SETTING( 200000, FULL, 200000baseSR2_Full		),
19362306a36Sopenharmony_ci	/* 100G */
19462306a36Sopenharmony_ci	PHY_SETTING( 100000, FULL, 100000baseCR4_Full		),
19562306a36Sopenharmony_ci	PHY_SETTING( 100000, FULL, 100000baseKR4_Full		),
19662306a36Sopenharmony_ci	PHY_SETTING( 100000, FULL, 100000baseLR4_ER4_Full	),
19762306a36Sopenharmony_ci	PHY_SETTING( 100000, FULL, 100000baseSR4_Full		),
19862306a36Sopenharmony_ci	PHY_SETTING( 100000, FULL, 100000baseCR2_Full		),
19962306a36Sopenharmony_ci	PHY_SETTING( 100000, FULL, 100000baseKR2_Full		),
20062306a36Sopenharmony_ci	PHY_SETTING( 100000, FULL, 100000baseLR2_ER2_FR2_Full	),
20162306a36Sopenharmony_ci	PHY_SETTING( 100000, FULL, 100000baseDR2_Full		),
20262306a36Sopenharmony_ci	PHY_SETTING( 100000, FULL, 100000baseSR2_Full		),
20362306a36Sopenharmony_ci	PHY_SETTING( 100000, FULL, 100000baseCR_Full		),
20462306a36Sopenharmony_ci	PHY_SETTING( 100000, FULL, 100000baseKR_Full		),
20562306a36Sopenharmony_ci	PHY_SETTING( 100000, FULL, 100000baseLR_ER_FR_Full	),
20662306a36Sopenharmony_ci	PHY_SETTING( 100000, FULL, 100000baseDR_Full		),
20762306a36Sopenharmony_ci	PHY_SETTING( 100000, FULL, 100000baseSR_Full		),
20862306a36Sopenharmony_ci	/* 56G */
20962306a36Sopenharmony_ci	PHY_SETTING(  56000, FULL,  56000baseCR4_Full	  	),
21062306a36Sopenharmony_ci	PHY_SETTING(  56000, FULL,  56000baseKR4_Full	  	),
21162306a36Sopenharmony_ci	PHY_SETTING(  56000, FULL,  56000baseLR4_Full	  	),
21262306a36Sopenharmony_ci	PHY_SETTING(  56000, FULL,  56000baseSR4_Full	  	),
21362306a36Sopenharmony_ci	/* 50G */
21462306a36Sopenharmony_ci	PHY_SETTING(  50000, FULL,  50000baseCR2_Full		),
21562306a36Sopenharmony_ci	PHY_SETTING(  50000, FULL,  50000baseKR2_Full		),
21662306a36Sopenharmony_ci	PHY_SETTING(  50000, FULL,  50000baseSR2_Full		),
21762306a36Sopenharmony_ci	PHY_SETTING(  50000, FULL,  50000baseCR_Full		),
21862306a36Sopenharmony_ci	PHY_SETTING(  50000, FULL,  50000baseKR_Full		),
21962306a36Sopenharmony_ci	PHY_SETTING(  50000, FULL,  50000baseLR_ER_FR_Full	),
22062306a36Sopenharmony_ci	PHY_SETTING(  50000, FULL,  50000baseDR_Full		),
22162306a36Sopenharmony_ci	PHY_SETTING(  50000, FULL,  50000baseSR_Full		),
22262306a36Sopenharmony_ci	/* 40G */
22362306a36Sopenharmony_ci	PHY_SETTING(  40000, FULL,  40000baseCR4_Full		),
22462306a36Sopenharmony_ci	PHY_SETTING(  40000, FULL,  40000baseKR4_Full		),
22562306a36Sopenharmony_ci	PHY_SETTING(  40000, FULL,  40000baseLR4_Full		),
22662306a36Sopenharmony_ci	PHY_SETTING(  40000, FULL,  40000baseSR4_Full		),
22762306a36Sopenharmony_ci	/* 25G */
22862306a36Sopenharmony_ci	PHY_SETTING(  25000, FULL,  25000baseCR_Full		),
22962306a36Sopenharmony_ci	PHY_SETTING(  25000, FULL,  25000baseKR_Full		),
23062306a36Sopenharmony_ci	PHY_SETTING(  25000, FULL,  25000baseSR_Full		),
23162306a36Sopenharmony_ci	/* 20G */
23262306a36Sopenharmony_ci	PHY_SETTING(  20000, FULL,  20000baseKR2_Full		),
23362306a36Sopenharmony_ci	PHY_SETTING(  20000, FULL,  20000baseMLD2_Full		),
23462306a36Sopenharmony_ci	/* 10G */
23562306a36Sopenharmony_ci	PHY_SETTING(  10000, FULL,  10000baseCR_Full		),
23662306a36Sopenharmony_ci	PHY_SETTING(  10000, FULL,  10000baseER_Full		),
23762306a36Sopenharmony_ci	PHY_SETTING(  10000, FULL,  10000baseKR_Full		),
23862306a36Sopenharmony_ci	PHY_SETTING(  10000, FULL,  10000baseKX4_Full		),
23962306a36Sopenharmony_ci	PHY_SETTING(  10000, FULL,  10000baseLR_Full		),
24062306a36Sopenharmony_ci	PHY_SETTING(  10000, FULL,  10000baseLRM_Full		),
24162306a36Sopenharmony_ci	PHY_SETTING(  10000, FULL,  10000baseR_FEC		),
24262306a36Sopenharmony_ci	PHY_SETTING(  10000, FULL,  10000baseSR_Full		),
24362306a36Sopenharmony_ci	PHY_SETTING(  10000, FULL,  10000baseT_Full		),
24462306a36Sopenharmony_ci	/* 5G */
24562306a36Sopenharmony_ci	PHY_SETTING(   5000, FULL,   5000baseT_Full		),
24662306a36Sopenharmony_ci	/* 2.5G */
24762306a36Sopenharmony_ci	PHY_SETTING(   2500, FULL,   2500baseT_Full		),
24862306a36Sopenharmony_ci	PHY_SETTING(   2500, FULL,   2500baseX_Full		),
24962306a36Sopenharmony_ci	/* 1G */
25062306a36Sopenharmony_ci	PHY_SETTING(   1000, FULL,   1000baseT_Full		),
25162306a36Sopenharmony_ci	PHY_SETTING(   1000, HALF,   1000baseT_Half		),
25262306a36Sopenharmony_ci	PHY_SETTING(   1000, FULL,   1000baseT1_Full		),
25362306a36Sopenharmony_ci	PHY_SETTING(   1000, FULL,   1000baseX_Full		),
25462306a36Sopenharmony_ci	PHY_SETTING(   1000, FULL,   1000baseKX_Full		),
25562306a36Sopenharmony_ci	/* 100M */
25662306a36Sopenharmony_ci	PHY_SETTING(    100, FULL,    100baseT_Full		),
25762306a36Sopenharmony_ci	PHY_SETTING(    100, FULL,    100baseT1_Full		),
25862306a36Sopenharmony_ci	PHY_SETTING(    100, HALF,    100baseT_Half		),
25962306a36Sopenharmony_ci	PHY_SETTING(    100, HALF,    100baseFX_Half		),
26062306a36Sopenharmony_ci	PHY_SETTING(    100, FULL,    100baseFX_Full		),
26162306a36Sopenharmony_ci	/* 10M */
26262306a36Sopenharmony_ci	PHY_SETTING(     10, FULL,     10baseT_Full		),
26362306a36Sopenharmony_ci	PHY_SETTING(     10, HALF,     10baseT_Half		),
26462306a36Sopenharmony_ci	PHY_SETTING(     10, FULL,     10baseT1L_Full		),
26562306a36Sopenharmony_ci	PHY_SETTING(     10, FULL,     10baseT1S_Full		),
26662306a36Sopenharmony_ci	PHY_SETTING(     10, HALF,     10baseT1S_Half		),
26762306a36Sopenharmony_ci	PHY_SETTING(     10, HALF,     10baseT1S_P2MP_Half	),
26862306a36Sopenharmony_ci};
26962306a36Sopenharmony_ci#undef PHY_SETTING
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci/**
27262306a36Sopenharmony_ci * phy_lookup_setting - lookup a PHY setting
27362306a36Sopenharmony_ci * @speed: speed to match
27462306a36Sopenharmony_ci * @duplex: duplex to match
27562306a36Sopenharmony_ci * @mask: allowed link modes
27662306a36Sopenharmony_ci * @exact: an exact match is required
27762306a36Sopenharmony_ci *
27862306a36Sopenharmony_ci * Search the settings array for a setting that matches the speed and
27962306a36Sopenharmony_ci * duplex, and which is supported.
28062306a36Sopenharmony_ci *
28162306a36Sopenharmony_ci * If @exact is unset, either an exact match or %NULL for no match will
28262306a36Sopenharmony_ci * be returned.
28362306a36Sopenharmony_ci *
28462306a36Sopenharmony_ci * If @exact is set, an exact match, the fastest supported setting at
28562306a36Sopenharmony_ci * or below the specified speed, the slowest supported setting, or if
28662306a36Sopenharmony_ci * they all fail, %NULL will be returned.
28762306a36Sopenharmony_ci */
28862306a36Sopenharmony_ciconst struct phy_setting *
28962306a36Sopenharmony_ciphy_lookup_setting(int speed, int duplex, const unsigned long *mask, bool exact)
29062306a36Sopenharmony_ci{
29162306a36Sopenharmony_ci	const struct phy_setting *p, *match = NULL, *last = NULL;
29262306a36Sopenharmony_ci	int i;
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	for (i = 0, p = settings; i < ARRAY_SIZE(settings); i++, p++) {
29562306a36Sopenharmony_ci		if (p->bit < __ETHTOOL_LINK_MODE_MASK_NBITS &&
29662306a36Sopenharmony_ci		    test_bit(p->bit, mask)) {
29762306a36Sopenharmony_ci			last = p;
29862306a36Sopenharmony_ci			if (p->speed == speed && p->duplex == duplex) {
29962306a36Sopenharmony_ci				/* Exact match for speed and duplex */
30062306a36Sopenharmony_ci				match = p;
30162306a36Sopenharmony_ci				break;
30262306a36Sopenharmony_ci			} else if (!exact) {
30362306a36Sopenharmony_ci				if (!match && p->speed <= speed)
30462306a36Sopenharmony_ci					/* Candidate */
30562306a36Sopenharmony_ci					match = p;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci				if (p->speed < speed)
30862306a36Sopenharmony_ci					break;
30962306a36Sopenharmony_ci			}
31062306a36Sopenharmony_ci		}
31162306a36Sopenharmony_ci	}
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	if (!match && !exact)
31462306a36Sopenharmony_ci		match = last;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	return match;
31762306a36Sopenharmony_ci}
31862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(phy_lookup_setting);
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_cisize_t phy_speeds(unsigned int *speeds, size_t size,
32162306a36Sopenharmony_ci		  unsigned long *mask)
32262306a36Sopenharmony_ci{
32362306a36Sopenharmony_ci	size_t count;
32462306a36Sopenharmony_ci	int i;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	for (i = 0, count = 0; i < ARRAY_SIZE(settings) && count < size; i++)
32762306a36Sopenharmony_ci		if (settings[i].bit < __ETHTOOL_LINK_MODE_MASK_NBITS &&
32862306a36Sopenharmony_ci		    test_bit(settings[i].bit, mask) &&
32962306a36Sopenharmony_ci		    (count == 0 || speeds[count - 1] != settings[i].speed))
33062306a36Sopenharmony_ci			speeds[count++] = settings[i].speed;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	return count;
33362306a36Sopenharmony_ci}
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_cistatic void __set_linkmode_max_speed(u32 max_speed, unsigned long *addr)
33662306a36Sopenharmony_ci{
33762306a36Sopenharmony_ci	const struct phy_setting *p;
33862306a36Sopenharmony_ci	int i;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	for (i = 0, p = settings; i < ARRAY_SIZE(settings); i++, p++) {
34162306a36Sopenharmony_ci		if (p->speed > max_speed)
34262306a36Sopenharmony_ci			linkmode_clear_bit(p->bit, addr);
34362306a36Sopenharmony_ci		else
34462306a36Sopenharmony_ci			break;
34562306a36Sopenharmony_ci	}
34662306a36Sopenharmony_ci}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_cistatic void __set_phy_supported(struct phy_device *phydev, u32 max_speed)
34962306a36Sopenharmony_ci{
35062306a36Sopenharmony_ci	__set_linkmode_max_speed(max_speed, phydev->supported);
35162306a36Sopenharmony_ci}
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci/**
35462306a36Sopenharmony_ci * phy_set_max_speed - Set the maximum speed the PHY should support
35562306a36Sopenharmony_ci *
35662306a36Sopenharmony_ci * @phydev: The phy_device struct
35762306a36Sopenharmony_ci * @max_speed: Maximum speed
35862306a36Sopenharmony_ci *
35962306a36Sopenharmony_ci * The PHY might be more capable than the MAC. For example a Fast Ethernet
36062306a36Sopenharmony_ci * is connected to a 1G PHY. This function allows the MAC to indicate its
36162306a36Sopenharmony_ci * maximum speed, and so limit what the PHY will advertise.
36262306a36Sopenharmony_ci */
36362306a36Sopenharmony_civoid phy_set_max_speed(struct phy_device *phydev, u32 max_speed)
36462306a36Sopenharmony_ci{
36562306a36Sopenharmony_ci	__set_phy_supported(phydev, max_speed);
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	phy_advertise_supported(phydev);
36862306a36Sopenharmony_ci}
36962306a36Sopenharmony_ciEXPORT_SYMBOL(phy_set_max_speed);
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_civoid of_set_phy_supported(struct phy_device *phydev)
37262306a36Sopenharmony_ci{
37362306a36Sopenharmony_ci	struct device_node *node = phydev->mdio.dev.of_node;
37462306a36Sopenharmony_ci	u32 max_speed;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	if (!IS_ENABLED(CONFIG_OF_MDIO))
37762306a36Sopenharmony_ci		return;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	if (!node)
38062306a36Sopenharmony_ci		return;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	if (!of_property_read_u32(node, "max-speed", &max_speed))
38362306a36Sopenharmony_ci		__set_phy_supported(phydev, max_speed);
38462306a36Sopenharmony_ci}
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_civoid of_set_phy_eee_broken(struct phy_device *phydev)
38762306a36Sopenharmony_ci{
38862306a36Sopenharmony_ci	struct device_node *node = phydev->mdio.dev.of_node;
38962306a36Sopenharmony_ci	u32 broken = 0;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	if (!IS_ENABLED(CONFIG_OF_MDIO))
39262306a36Sopenharmony_ci		return;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	if (!node)
39562306a36Sopenharmony_ci		return;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	if (of_property_read_bool(node, "eee-broken-100tx"))
39862306a36Sopenharmony_ci		broken |= MDIO_EEE_100TX;
39962306a36Sopenharmony_ci	if (of_property_read_bool(node, "eee-broken-1000t"))
40062306a36Sopenharmony_ci		broken |= MDIO_EEE_1000T;
40162306a36Sopenharmony_ci	if (of_property_read_bool(node, "eee-broken-10gt"))
40262306a36Sopenharmony_ci		broken |= MDIO_EEE_10GT;
40362306a36Sopenharmony_ci	if (of_property_read_bool(node, "eee-broken-1000kx"))
40462306a36Sopenharmony_ci		broken |= MDIO_EEE_1000KX;
40562306a36Sopenharmony_ci	if (of_property_read_bool(node, "eee-broken-10gkx4"))
40662306a36Sopenharmony_ci		broken |= MDIO_EEE_10GKX4;
40762306a36Sopenharmony_ci	if (of_property_read_bool(node, "eee-broken-10gkr"))
40862306a36Sopenharmony_ci		broken |= MDIO_EEE_10GKR;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	phydev->eee_broken_modes = broken;
41162306a36Sopenharmony_ci}
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci/**
41462306a36Sopenharmony_ci * phy_resolve_aneg_pause - Determine pause autoneg results
41562306a36Sopenharmony_ci *
41662306a36Sopenharmony_ci * @phydev: The phy_device struct
41762306a36Sopenharmony_ci *
41862306a36Sopenharmony_ci * Once autoneg has completed the local pause settings can be
41962306a36Sopenharmony_ci * resolved.  Determine if pause and asymmetric pause should be used
42062306a36Sopenharmony_ci * by the MAC.
42162306a36Sopenharmony_ci */
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_civoid phy_resolve_aneg_pause(struct phy_device *phydev)
42462306a36Sopenharmony_ci{
42562306a36Sopenharmony_ci	if (phydev->duplex == DUPLEX_FULL) {
42662306a36Sopenharmony_ci		phydev->pause = linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT,
42762306a36Sopenharmony_ci						  phydev->lp_advertising);
42862306a36Sopenharmony_ci		phydev->asym_pause = linkmode_test_bit(
42962306a36Sopenharmony_ci			ETHTOOL_LINK_MODE_Asym_Pause_BIT,
43062306a36Sopenharmony_ci			phydev->lp_advertising);
43162306a36Sopenharmony_ci	}
43262306a36Sopenharmony_ci}
43362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(phy_resolve_aneg_pause);
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci/**
43662306a36Sopenharmony_ci * phy_resolve_aneg_linkmode - resolve the advertisements into PHY settings
43762306a36Sopenharmony_ci * @phydev: The phy_device struct
43862306a36Sopenharmony_ci *
43962306a36Sopenharmony_ci * Resolve our and the link partner advertisements into their corresponding
44062306a36Sopenharmony_ci * speed and duplex. If full duplex was negotiated, extract the pause mode
44162306a36Sopenharmony_ci * from the link partner mask.
44262306a36Sopenharmony_ci */
44362306a36Sopenharmony_civoid phy_resolve_aneg_linkmode(struct phy_device *phydev)
44462306a36Sopenharmony_ci{
44562306a36Sopenharmony_ci	__ETHTOOL_DECLARE_LINK_MODE_MASK(common);
44662306a36Sopenharmony_ci	int i;
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	linkmode_and(common, phydev->lp_advertising, phydev->advertising);
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(settings); i++)
45162306a36Sopenharmony_ci		if (test_bit(settings[i].bit, common)) {
45262306a36Sopenharmony_ci			phydev->speed = settings[i].speed;
45362306a36Sopenharmony_ci			phydev->duplex = settings[i].duplex;
45462306a36Sopenharmony_ci			break;
45562306a36Sopenharmony_ci		}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	phy_resolve_aneg_pause(phydev);
45862306a36Sopenharmony_ci}
45962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(phy_resolve_aneg_linkmode);
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci/**
46262306a36Sopenharmony_ci * phy_check_downshift - check whether downshift occurred
46362306a36Sopenharmony_ci * @phydev: The phy_device struct
46462306a36Sopenharmony_ci *
46562306a36Sopenharmony_ci * Check whether a downshift to a lower speed occurred. If this should be the
46662306a36Sopenharmony_ci * case warn the user.
46762306a36Sopenharmony_ci * Prerequisite for detecting downshift is that PHY driver implements the
46862306a36Sopenharmony_ci * read_status callback and sets phydev->speed to the actual link speed.
46962306a36Sopenharmony_ci */
47062306a36Sopenharmony_civoid phy_check_downshift(struct phy_device *phydev)
47162306a36Sopenharmony_ci{
47262306a36Sopenharmony_ci	__ETHTOOL_DECLARE_LINK_MODE_MASK(common);
47362306a36Sopenharmony_ci	int i, speed = SPEED_UNKNOWN;
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	phydev->downshifted_rate = 0;
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	if (phydev->autoneg == AUTONEG_DISABLE ||
47862306a36Sopenharmony_ci	    phydev->speed == SPEED_UNKNOWN)
47962306a36Sopenharmony_ci		return;
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	linkmode_and(common, phydev->lp_advertising, phydev->advertising);
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(settings); i++)
48462306a36Sopenharmony_ci		if (test_bit(settings[i].bit, common)) {
48562306a36Sopenharmony_ci			speed = settings[i].speed;
48662306a36Sopenharmony_ci			break;
48762306a36Sopenharmony_ci		}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	if (speed == SPEED_UNKNOWN || phydev->speed >= speed)
49062306a36Sopenharmony_ci		return;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	phydev_warn(phydev, "Downshift occurred from negotiated speed %s to actual speed %s, check cabling!\n",
49362306a36Sopenharmony_ci		    phy_speed_to_str(speed), phy_speed_to_str(phydev->speed));
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	phydev->downshifted_rate = 1;
49662306a36Sopenharmony_ci}
49762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(phy_check_downshift);
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_cistatic int phy_resolve_min_speed(struct phy_device *phydev, bool fdx_only)
50062306a36Sopenharmony_ci{
50162306a36Sopenharmony_ci	__ETHTOOL_DECLARE_LINK_MODE_MASK(common);
50262306a36Sopenharmony_ci	int i = ARRAY_SIZE(settings);
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	linkmode_and(common, phydev->lp_advertising, phydev->advertising);
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	while (--i >= 0) {
50762306a36Sopenharmony_ci		if (test_bit(settings[i].bit, common)) {
50862306a36Sopenharmony_ci			if (fdx_only && settings[i].duplex != DUPLEX_FULL)
50962306a36Sopenharmony_ci				continue;
51062306a36Sopenharmony_ci			return settings[i].speed;
51162306a36Sopenharmony_ci		}
51262306a36Sopenharmony_ci	}
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	return SPEED_UNKNOWN;
51562306a36Sopenharmony_ci}
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ciint phy_speed_down_core(struct phy_device *phydev)
51862306a36Sopenharmony_ci{
51962306a36Sopenharmony_ci	int min_common_speed = phy_resolve_min_speed(phydev, true);
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	if (min_common_speed == SPEED_UNKNOWN)
52262306a36Sopenharmony_ci		return -EINVAL;
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	__set_linkmode_max_speed(min_common_speed, phydev->advertising);
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	return 0;
52762306a36Sopenharmony_ci}
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_cistatic void mmd_phy_indirect(struct mii_bus *bus, int phy_addr, int devad,
53062306a36Sopenharmony_ci			     u16 regnum)
53162306a36Sopenharmony_ci{
53262306a36Sopenharmony_ci	/* Write the desired MMD Devad */
53362306a36Sopenharmony_ci	__mdiobus_write(bus, phy_addr, MII_MMD_CTRL, devad);
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	/* Write the desired MMD register address */
53662306a36Sopenharmony_ci	__mdiobus_write(bus, phy_addr, MII_MMD_DATA, regnum);
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	/* Select the Function : DATA with no post increment */
53962306a36Sopenharmony_ci	__mdiobus_write(bus, phy_addr, MII_MMD_CTRL,
54062306a36Sopenharmony_ci			devad | MII_MMD_CTRL_NOINCR);
54162306a36Sopenharmony_ci}
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci/**
54462306a36Sopenharmony_ci * __phy_read_mmd - Convenience function for reading a register
54562306a36Sopenharmony_ci * from an MMD on a given PHY.
54662306a36Sopenharmony_ci * @phydev: The phy_device struct
54762306a36Sopenharmony_ci * @devad: The MMD to read from (0..31)
54862306a36Sopenharmony_ci * @regnum: The register on the MMD to read (0..65535)
54962306a36Sopenharmony_ci *
55062306a36Sopenharmony_ci * Same rules as for __phy_read();
55162306a36Sopenharmony_ci */
55262306a36Sopenharmony_ciint __phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum)
55362306a36Sopenharmony_ci{
55462306a36Sopenharmony_ci	int val;
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	if (regnum > (u16)~0 || devad > 32)
55762306a36Sopenharmony_ci		return -EINVAL;
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	if (phydev->drv && phydev->drv->read_mmd) {
56062306a36Sopenharmony_ci		val = phydev->drv->read_mmd(phydev, devad, regnum);
56162306a36Sopenharmony_ci	} else if (phydev->is_c45) {
56262306a36Sopenharmony_ci		val = __mdiobus_c45_read(phydev->mdio.bus, phydev->mdio.addr,
56362306a36Sopenharmony_ci					 devad, regnum);
56462306a36Sopenharmony_ci	} else {
56562306a36Sopenharmony_ci		struct mii_bus *bus = phydev->mdio.bus;
56662306a36Sopenharmony_ci		int phy_addr = phydev->mdio.addr;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci		mmd_phy_indirect(bus, phy_addr, devad, regnum);
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci		/* Read the content of the MMD's selected register */
57162306a36Sopenharmony_ci		val = __mdiobus_read(bus, phy_addr, MII_MMD_DATA);
57262306a36Sopenharmony_ci	}
57362306a36Sopenharmony_ci	return val;
57462306a36Sopenharmony_ci}
57562306a36Sopenharmony_ciEXPORT_SYMBOL(__phy_read_mmd);
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci/**
57862306a36Sopenharmony_ci * phy_read_mmd - Convenience function for reading a register
57962306a36Sopenharmony_ci * from an MMD on a given PHY.
58062306a36Sopenharmony_ci * @phydev: The phy_device struct
58162306a36Sopenharmony_ci * @devad: The MMD to read from
58262306a36Sopenharmony_ci * @regnum: The register on the MMD to read
58362306a36Sopenharmony_ci *
58462306a36Sopenharmony_ci * Same rules as for phy_read();
58562306a36Sopenharmony_ci */
58662306a36Sopenharmony_ciint phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum)
58762306a36Sopenharmony_ci{
58862306a36Sopenharmony_ci	int ret;
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	phy_lock_mdio_bus(phydev);
59162306a36Sopenharmony_ci	ret = __phy_read_mmd(phydev, devad, regnum);
59262306a36Sopenharmony_ci	phy_unlock_mdio_bus(phydev);
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	return ret;
59562306a36Sopenharmony_ci}
59662306a36Sopenharmony_ciEXPORT_SYMBOL(phy_read_mmd);
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci/**
59962306a36Sopenharmony_ci * __phy_write_mmd - Convenience function for writing a register
60062306a36Sopenharmony_ci * on an MMD on a given PHY.
60162306a36Sopenharmony_ci * @phydev: The phy_device struct
60262306a36Sopenharmony_ci * @devad: The MMD to read from
60362306a36Sopenharmony_ci * @regnum: The register on the MMD to read
60462306a36Sopenharmony_ci * @val: value to write to @regnum
60562306a36Sopenharmony_ci *
60662306a36Sopenharmony_ci * Same rules as for __phy_write();
60762306a36Sopenharmony_ci */
60862306a36Sopenharmony_ciint __phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val)
60962306a36Sopenharmony_ci{
61062306a36Sopenharmony_ci	int ret;
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	if (regnum > (u16)~0 || devad > 32)
61362306a36Sopenharmony_ci		return -EINVAL;
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	if (phydev->drv && phydev->drv->write_mmd) {
61662306a36Sopenharmony_ci		ret = phydev->drv->write_mmd(phydev, devad, regnum, val);
61762306a36Sopenharmony_ci	} else if (phydev->is_c45) {
61862306a36Sopenharmony_ci		ret = __mdiobus_c45_write(phydev->mdio.bus, phydev->mdio.addr,
61962306a36Sopenharmony_ci					  devad, regnum, val);
62062306a36Sopenharmony_ci	} else {
62162306a36Sopenharmony_ci		struct mii_bus *bus = phydev->mdio.bus;
62262306a36Sopenharmony_ci		int phy_addr = phydev->mdio.addr;
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci		mmd_phy_indirect(bus, phy_addr, devad, regnum);
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci		/* Write the data into MMD's selected register */
62762306a36Sopenharmony_ci		__mdiobus_write(bus, phy_addr, MII_MMD_DATA, val);
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci		ret = 0;
63062306a36Sopenharmony_ci	}
63162306a36Sopenharmony_ci	return ret;
63262306a36Sopenharmony_ci}
63362306a36Sopenharmony_ciEXPORT_SYMBOL(__phy_write_mmd);
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci/**
63662306a36Sopenharmony_ci * phy_write_mmd - Convenience function for writing a register
63762306a36Sopenharmony_ci * on an MMD on a given PHY.
63862306a36Sopenharmony_ci * @phydev: The phy_device struct
63962306a36Sopenharmony_ci * @devad: The MMD to read from
64062306a36Sopenharmony_ci * @regnum: The register on the MMD to read
64162306a36Sopenharmony_ci * @val: value to write to @regnum
64262306a36Sopenharmony_ci *
64362306a36Sopenharmony_ci * Same rules as for phy_write();
64462306a36Sopenharmony_ci */
64562306a36Sopenharmony_ciint phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val)
64662306a36Sopenharmony_ci{
64762306a36Sopenharmony_ci	int ret;
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	phy_lock_mdio_bus(phydev);
65062306a36Sopenharmony_ci	ret = __phy_write_mmd(phydev, devad, regnum, val);
65162306a36Sopenharmony_ci	phy_unlock_mdio_bus(phydev);
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	return ret;
65462306a36Sopenharmony_ci}
65562306a36Sopenharmony_ciEXPORT_SYMBOL(phy_write_mmd);
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci/**
65862306a36Sopenharmony_ci * phy_modify_changed - Function for modifying a PHY register
65962306a36Sopenharmony_ci * @phydev: the phy_device struct
66062306a36Sopenharmony_ci * @regnum: register number to modify
66162306a36Sopenharmony_ci * @mask: bit mask of bits to clear
66262306a36Sopenharmony_ci * @set: new value of bits set in mask to write to @regnum
66362306a36Sopenharmony_ci *
66462306a36Sopenharmony_ci * NOTE: MUST NOT be called from interrupt context,
66562306a36Sopenharmony_ci * because the bus read/write functions may wait for an interrupt
66662306a36Sopenharmony_ci * to conclude the operation.
66762306a36Sopenharmony_ci *
66862306a36Sopenharmony_ci * Returns negative errno, 0 if there was no change, and 1 in case of change
66962306a36Sopenharmony_ci */
67062306a36Sopenharmony_ciint phy_modify_changed(struct phy_device *phydev, u32 regnum, u16 mask, u16 set)
67162306a36Sopenharmony_ci{
67262306a36Sopenharmony_ci	int ret;
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	phy_lock_mdio_bus(phydev);
67562306a36Sopenharmony_ci	ret = __phy_modify_changed(phydev, regnum, mask, set);
67662306a36Sopenharmony_ci	phy_unlock_mdio_bus(phydev);
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	return ret;
67962306a36Sopenharmony_ci}
68062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(phy_modify_changed);
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci/**
68362306a36Sopenharmony_ci * __phy_modify - Convenience function for modifying a PHY register
68462306a36Sopenharmony_ci * @phydev: the phy_device struct
68562306a36Sopenharmony_ci * @regnum: register number to modify
68662306a36Sopenharmony_ci * @mask: bit mask of bits to clear
68762306a36Sopenharmony_ci * @set: new value of bits set in mask to write to @regnum
68862306a36Sopenharmony_ci *
68962306a36Sopenharmony_ci * NOTE: MUST NOT be called from interrupt context,
69062306a36Sopenharmony_ci * because the bus read/write functions may wait for an interrupt
69162306a36Sopenharmony_ci * to conclude the operation.
69262306a36Sopenharmony_ci */
69362306a36Sopenharmony_ciint __phy_modify(struct phy_device *phydev, u32 regnum, u16 mask, u16 set)
69462306a36Sopenharmony_ci{
69562306a36Sopenharmony_ci	int ret;
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	ret = __phy_modify_changed(phydev, regnum, mask, set);
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	return ret < 0 ? ret : 0;
70062306a36Sopenharmony_ci}
70162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__phy_modify);
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci/**
70462306a36Sopenharmony_ci * phy_modify - Convenience function for modifying a given PHY register
70562306a36Sopenharmony_ci * @phydev: the phy_device struct
70662306a36Sopenharmony_ci * @regnum: register number to write
70762306a36Sopenharmony_ci * @mask: bit mask of bits to clear
70862306a36Sopenharmony_ci * @set: new value of bits set in mask to write to @regnum
70962306a36Sopenharmony_ci *
71062306a36Sopenharmony_ci * NOTE: MUST NOT be called from interrupt context,
71162306a36Sopenharmony_ci * because the bus read/write functions may wait for an interrupt
71262306a36Sopenharmony_ci * to conclude the operation.
71362306a36Sopenharmony_ci */
71462306a36Sopenharmony_ciint phy_modify(struct phy_device *phydev, u32 regnum, u16 mask, u16 set)
71562306a36Sopenharmony_ci{
71662306a36Sopenharmony_ci	int ret;
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	phy_lock_mdio_bus(phydev);
71962306a36Sopenharmony_ci	ret = __phy_modify(phydev, regnum, mask, set);
72062306a36Sopenharmony_ci	phy_unlock_mdio_bus(phydev);
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	return ret;
72362306a36Sopenharmony_ci}
72462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(phy_modify);
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci/**
72762306a36Sopenharmony_ci * __phy_modify_mmd_changed - Function for modifying a register on MMD
72862306a36Sopenharmony_ci * @phydev: the phy_device struct
72962306a36Sopenharmony_ci * @devad: the MMD containing register to modify
73062306a36Sopenharmony_ci * @regnum: register number to modify
73162306a36Sopenharmony_ci * @mask: bit mask of bits to clear
73262306a36Sopenharmony_ci * @set: new value of bits set in mask to write to @regnum
73362306a36Sopenharmony_ci *
73462306a36Sopenharmony_ci * Unlocked helper function which allows a MMD register to be modified as
73562306a36Sopenharmony_ci * new register value = (old register value & ~mask) | set
73662306a36Sopenharmony_ci *
73762306a36Sopenharmony_ci * Returns negative errno, 0 if there was no change, and 1 in case of change
73862306a36Sopenharmony_ci */
73962306a36Sopenharmony_ciint __phy_modify_mmd_changed(struct phy_device *phydev, int devad, u32 regnum,
74062306a36Sopenharmony_ci			     u16 mask, u16 set)
74162306a36Sopenharmony_ci{
74262306a36Sopenharmony_ci	int new, ret;
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	ret = __phy_read_mmd(phydev, devad, regnum);
74562306a36Sopenharmony_ci	if (ret < 0)
74662306a36Sopenharmony_ci		return ret;
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	new = (ret & ~mask) | set;
74962306a36Sopenharmony_ci	if (new == ret)
75062306a36Sopenharmony_ci		return 0;
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci	ret = __phy_write_mmd(phydev, devad, regnum, new);
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	return ret < 0 ? ret : 1;
75562306a36Sopenharmony_ci}
75662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__phy_modify_mmd_changed);
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci/**
75962306a36Sopenharmony_ci * phy_modify_mmd_changed - Function for modifying a register on MMD
76062306a36Sopenharmony_ci * @phydev: the phy_device struct
76162306a36Sopenharmony_ci * @devad: the MMD containing register to modify
76262306a36Sopenharmony_ci * @regnum: register number to modify
76362306a36Sopenharmony_ci * @mask: bit mask of bits to clear
76462306a36Sopenharmony_ci * @set: new value of bits set in mask to write to @regnum
76562306a36Sopenharmony_ci *
76662306a36Sopenharmony_ci * NOTE: MUST NOT be called from interrupt context,
76762306a36Sopenharmony_ci * because the bus read/write functions may wait for an interrupt
76862306a36Sopenharmony_ci * to conclude the operation.
76962306a36Sopenharmony_ci *
77062306a36Sopenharmony_ci * Returns negative errno, 0 if there was no change, and 1 in case of change
77162306a36Sopenharmony_ci */
77262306a36Sopenharmony_ciint phy_modify_mmd_changed(struct phy_device *phydev, int devad, u32 regnum,
77362306a36Sopenharmony_ci			   u16 mask, u16 set)
77462306a36Sopenharmony_ci{
77562306a36Sopenharmony_ci	int ret;
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	phy_lock_mdio_bus(phydev);
77862306a36Sopenharmony_ci	ret = __phy_modify_mmd_changed(phydev, devad, regnum, mask, set);
77962306a36Sopenharmony_ci	phy_unlock_mdio_bus(phydev);
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	return ret;
78262306a36Sopenharmony_ci}
78362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(phy_modify_mmd_changed);
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci/**
78662306a36Sopenharmony_ci * __phy_modify_mmd - Convenience function for modifying a register on MMD
78762306a36Sopenharmony_ci * @phydev: the phy_device struct
78862306a36Sopenharmony_ci * @devad: the MMD containing register to modify
78962306a36Sopenharmony_ci * @regnum: register number to modify
79062306a36Sopenharmony_ci * @mask: bit mask of bits to clear
79162306a36Sopenharmony_ci * @set: new value of bits set in mask to write to @regnum
79262306a36Sopenharmony_ci *
79362306a36Sopenharmony_ci * NOTE: MUST NOT be called from interrupt context,
79462306a36Sopenharmony_ci * because the bus read/write functions may wait for an interrupt
79562306a36Sopenharmony_ci * to conclude the operation.
79662306a36Sopenharmony_ci */
79762306a36Sopenharmony_ciint __phy_modify_mmd(struct phy_device *phydev, int devad, u32 regnum,
79862306a36Sopenharmony_ci		     u16 mask, u16 set)
79962306a36Sopenharmony_ci{
80062306a36Sopenharmony_ci	int ret;
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	ret = __phy_modify_mmd_changed(phydev, devad, regnum, mask, set);
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	return ret < 0 ? ret : 0;
80562306a36Sopenharmony_ci}
80662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__phy_modify_mmd);
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci/**
80962306a36Sopenharmony_ci * phy_modify_mmd - Convenience function for modifying a register on MMD
81062306a36Sopenharmony_ci * @phydev: the phy_device struct
81162306a36Sopenharmony_ci * @devad: the MMD containing register to modify
81262306a36Sopenharmony_ci * @regnum: register number to modify
81362306a36Sopenharmony_ci * @mask: bit mask of bits to clear
81462306a36Sopenharmony_ci * @set: new value of bits set in mask to write to @regnum
81562306a36Sopenharmony_ci *
81662306a36Sopenharmony_ci * NOTE: MUST NOT be called from interrupt context,
81762306a36Sopenharmony_ci * because the bus read/write functions may wait for an interrupt
81862306a36Sopenharmony_ci * to conclude the operation.
81962306a36Sopenharmony_ci */
82062306a36Sopenharmony_ciint phy_modify_mmd(struct phy_device *phydev, int devad, u32 regnum,
82162306a36Sopenharmony_ci		   u16 mask, u16 set)
82262306a36Sopenharmony_ci{
82362306a36Sopenharmony_ci	int ret;
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	phy_lock_mdio_bus(phydev);
82662306a36Sopenharmony_ci	ret = __phy_modify_mmd(phydev, devad, regnum, mask, set);
82762306a36Sopenharmony_ci	phy_unlock_mdio_bus(phydev);
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	return ret;
83062306a36Sopenharmony_ci}
83162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(phy_modify_mmd);
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_cistatic int __phy_read_page(struct phy_device *phydev)
83462306a36Sopenharmony_ci{
83562306a36Sopenharmony_ci	if (WARN_ONCE(!phydev->drv->read_page, "read_page callback not available, PHY driver not loaded?\n"))
83662306a36Sopenharmony_ci		return -EOPNOTSUPP;
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	return phydev->drv->read_page(phydev);
83962306a36Sopenharmony_ci}
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_cistatic int __phy_write_page(struct phy_device *phydev, int page)
84262306a36Sopenharmony_ci{
84362306a36Sopenharmony_ci	if (WARN_ONCE(!phydev->drv->write_page, "write_page callback not available, PHY driver not loaded?\n"))
84462306a36Sopenharmony_ci		return -EOPNOTSUPP;
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	return phydev->drv->write_page(phydev, page);
84762306a36Sopenharmony_ci}
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ci/**
85062306a36Sopenharmony_ci * phy_save_page() - take the bus lock and save the current page
85162306a36Sopenharmony_ci * @phydev: a pointer to a &struct phy_device
85262306a36Sopenharmony_ci *
85362306a36Sopenharmony_ci * Take the MDIO bus lock, and return the current page number. On error,
85462306a36Sopenharmony_ci * returns a negative errno. phy_restore_page() must always be called
85562306a36Sopenharmony_ci * after this, irrespective of success or failure of this call.
85662306a36Sopenharmony_ci */
85762306a36Sopenharmony_ciint phy_save_page(struct phy_device *phydev)
85862306a36Sopenharmony_ci{
85962306a36Sopenharmony_ci	phy_lock_mdio_bus(phydev);
86062306a36Sopenharmony_ci	return __phy_read_page(phydev);
86162306a36Sopenharmony_ci}
86262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(phy_save_page);
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci/**
86562306a36Sopenharmony_ci * phy_select_page() - take the bus lock, save the current page, and set a page
86662306a36Sopenharmony_ci * @phydev: a pointer to a &struct phy_device
86762306a36Sopenharmony_ci * @page: desired page
86862306a36Sopenharmony_ci *
86962306a36Sopenharmony_ci * Take the MDIO bus lock to protect against concurrent access, save the
87062306a36Sopenharmony_ci * current PHY page, and set the current page.  On error, returns a
87162306a36Sopenharmony_ci * negative errno, otherwise returns the previous page number.
87262306a36Sopenharmony_ci * phy_restore_page() must always be called after this, irrespective
87362306a36Sopenharmony_ci * of success or failure of this call.
87462306a36Sopenharmony_ci */
87562306a36Sopenharmony_ciint phy_select_page(struct phy_device *phydev, int page)
87662306a36Sopenharmony_ci{
87762306a36Sopenharmony_ci	int ret, oldpage;
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci	oldpage = ret = phy_save_page(phydev);
88062306a36Sopenharmony_ci	if (ret < 0)
88162306a36Sopenharmony_ci		return ret;
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci	if (oldpage != page) {
88462306a36Sopenharmony_ci		ret = __phy_write_page(phydev, page);
88562306a36Sopenharmony_ci		if (ret < 0)
88662306a36Sopenharmony_ci			return ret;
88762306a36Sopenharmony_ci	}
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci	return oldpage;
89062306a36Sopenharmony_ci}
89162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(phy_select_page);
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci/**
89462306a36Sopenharmony_ci * phy_restore_page() - restore the page register and release the bus lock
89562306a36Sopenharmony_ci * @phydev: a pointer to a &struct phy_device
89662306a36Sopenharmony_ci * @oldpage: the old page, return value from phy_save_page() or phy_select_page()
89762306a36Sopenharmony_ci * @ret: operation's return code
89862306a36Sopenharmony_ci *
89962306a36Sopenharmony_ci * Release the MDIO bus lock, restoring @oldpage if it is a valid page.
90062306a36Sopenharmony_ci * This function propagates the earliest error code from the group of
90162306a36Sopenharmony_ci * operations.
90262306a36Sopenharmony_ci *
90362306a36Sopenharmony_ci * Returns:
90462306a36Sopenharmony_ci *   @oldpage if it was a negative value, otherwise
90562306a36Sopenharmony_ci *   @ret if it was a negative errno value, otherwise
90662306a36Sopenharmony_ci *   phy_write_page()'s negative value if it were in error, otherwise
90762306a36Sopenharmony_ci *   @ret.
90862306a36Sopenharmony_ci */
90962306a36Sopenharmony_ciint phy_restore_page(struct phy_device *phydev, int oldpage, int ret)
91062306a36Sopenharmony_ci{
91162306a36Sopenharmony_ci	int r;
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci	if (oldpage >= 0) {
91462306a36Sopenharmony_ci		r = __phy_write_page(phydev, oldpage);
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci		/* Propagate the operation return code if the page write
91762306a36Sopenharmony_ci		 * was successful.
91862306a36Sopenharmony_ci		 */
91962306a36Sopenharmony_ci		if (ret >= 0 && r < 0)
92062306a36Sopenharmony_ci			ret = r;
92162306a36Sopenharmony_ci	} else {
92262306a36Sopenharmony_ci		/* Propagate the phy page selection error code */
92362306a36Sopenharmony_ci		ret = oldpage;
92462306a36Sopenharmony_ci	}
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci	phy_unlock_mdio_bus(phydev);
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci	return ret;
92962306a36Sopenharmony_ci}
93062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(phy_restore_page);
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci/**
93362306a36Sopenharmony_ci * phy_read_paged() - Convenience function for reading a paged register
93462306a36Sopenharmony_ci * @phydev: a pointer to a &struct phy_device
93562306a36Sopenharmony_ci * @page: the page for the phy
93662306a36Sopenharmony_ci * @regnum: register number
93762306a36Sopenharmony_ci *
93862306a36Sopenharmony_ci * Same rules as for phy_read().
93962306a36Sopenharmony_ci */
94062306a36Sopenharmony_ciint phy_read_paged(struct phy_device *phydev, int page, u32 regnum)
94162306a36Sopenharmony_ci{
94262306a36Sopenharmony_ci	int ret = 0, oldpage;
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ci	oldpage = phy_select_page(phydev, page);
94562306a36Sopenharmony_ci	if (oldpage >= 0)
94662306a36Sopenharmony_ci		ret = __phy_read(phydev, regnum);
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci	return phy_restore_page(phydev, oldpage, ret);
94962306a36Sopenharmony_ci}
95062306a36Sopenharmony_ciEXPORT_SYMBOL(phy_read_paged);
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_ci/**
95362306a36Sopenharmony_ci * phy_write_paged() - Convenience function for writing a paged register
95462306a36Sopenharmony_ci * @phydev: a pointer to a &struct phy_device
95562306a36Sopenharmony_ci * @page: the page for the phy
95662306a36Sopenharmony_ci * @regnum: register number
95762306a36Sopenharmony_ci * @val: value to write
95862306a36Sopenharmony_ci *
95962306a36Sopenharmony_ci * Same rules as for phy_write().
96062306a36Sopenharmony_ci */
96162306a36Sopenharmony_ciint phy_write_paged(struct phy_device *phydev, int page, u32 regnum, u16 val)
96262306a36Sopenharmony_ci{
96362306a36Sopenharmony_ci	int ret = 0, oldpage;
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci	oldpage = phy_select_page(phydev, page);
96662306a36Sopenharmony_ci	if (oldpage >= 0)
96762306a36Sopenharmony_ci		ret = __phy_write(phydev, regnum, val);
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci	return phy_restore_page(phydev, oldpage, ret);
97062306a36Sopenharmony_ci}
97162306a36Sopenharmony_ciEXPORT_SYMBOL(phy_write_paged);
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci/**
97462306a36Sopenharmony_ci * phy_modify_paged_changed() - Function for modifying a paged register
97562306a36Sopenharmony_ci * @phydev: a pointer to a &struct phy_device
97662306a36Sopenharmony_ci * @page: the page for the phy
97762306a36Sopenharmony_ci * @regnum: register number
97862306a36Sopenharmony_ci * @mask: bit mask of bits to clear
97962306a36Sopenharmony_ci * @set: bit mask of bits to set
98062306a36Sopenharmony_ci *
98162306a36Sopenharmony_ci * Returns negative errno, 0 if there was no change, and 1 in case of change
98262306a36Sopenharmony_ci */
98362306a36Sopenharmony_ciint phy_modify_paged_changed(struct phy_device *phydev, int page, u32 regnum,
98462306a36Sopenharmony_ci			     u16 mask, u16 set)
98562306a36Sopenharmony_ci{
98662306a36Sopenharmony_ci	int ret = 0, oldpage;
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci	oldpage = phy_select_page(phydev, page);
98962306a36Sopenharmony_ci	if (oldpage >= 0)
99062306a36Sopenharmony_ci		ret = __phy_modify_changed(phydev, regnum, mask, set);
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci	return phy_restore_page(phydev, oldpage, ret);
99362306a36Sopenharmony_ci}
99462306a36Sopenharmony_ciEXPORT_SYMBOL(phy_modify_paged_changed);
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci/**
99762306a36Sopenharmony_ci * phy_modify_paged() - Convenience function for modifying a paged register
99862306a36Sopenharmony_ci * @phydev: a pointer to a &struct phy_device
99962306a36Sopenharmony_ci * @page: the page for the phy
100062306a36Sopenharmony_ci * @regnum: register number
100162306a36Sopenharmony_ci * @mask: bit mask of bits to clear
100262306a36Sopenharmony_ci * @set: bit mask of bits to set
100362306a36Sopenharmony_ci *
100462306a36Sopenharmony_ci * Same rules as for phy_read() and phy_write().
100562306a36Sopenharmony_ci */
100662306a36Sopenharmony_ciint phy_modify_paged(struct phy_device *phydev, int page, u32 regnum,
100762306a36Sopenharmony_ci		     u16 mask, u16 set)
100862306a36Sopenharmony_ci{
100962306a36Sopenharmony_ci	int ret = phy_modify_paged_changed(phydev, page, regnum, mask, set);
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci	return ret < 0 ? ret : 0;
101262306a36Sopenharmony_ci}
101362306a36Sopenharmony_ciEXPORT_SYMBOL(phy_modify_paged);
1014