18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Core PHY library, taken from phy.c
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci#include <linux/export.h>
68c2ecf20Sopenharmony_ci#include <linux/phy.h>
78c2ecf20Sopenharmony_ci#include <linux/of.h>
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci/**
108c2ecf20Sopenharmony_ci * phy_speed_to_str - Return a string representing the PHY link speed
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci * @speed: Speed of the link
138c2ecf20Sopenharmony_ci */
148c2ecf20Sopenharmony_ciconst char *phy_speed_to_str(int speed)
158c2ecf20Sopenharmony_ci{
168c2ecf20Sopenharmony_ci	BUILD_BUG_ON_MSG(__ETHTOOL_LINK_MODE_MASK_NBITS != 92,
178c2ecf20Sopenharmony_ci		"Enum ethtool_link_mode_bit_indices and phylib are out of sync. "
188c2ecf20Sopenharmony_ci		"If a speed or mode has been added please update phy_speed_to_str "
198c2ecf20Sopenharmony_ci		"and the PHY settings array.\n");
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci	switch (speed) {
228c2ecf20Sopenharmony_ci	case SPEED_10:
238c2ecf20Sopenharmony_ci		return "10Mbps";
248c2ecf20Sopenharmony_ci	case SPEED_100:
258c2ecf20Sopenharmony_ci		return "100Mbps";
268c2ecf20Sopenharmony_ci	case SPEED_1000:
278c2ecf20Sopenharmony_ci		return "1Gbps";
288c2ecf20Sopenharmony_ci	case SPEED_2500:
298c2ecf20Sopenharmony_ci		return "2.5Gbps";
308c2ecf20Sopenharmony_ci	case SPEED_5000:
318c2ecf20Sopenharmony_ci		return "5Gbps";
328c2ecf20Sopenharmony_ci	case SPEED_10000:
338c2ecf20Sopenharmony_ci		return "10Gbps";
348c2ecf20Sopenharmony_ci	case SPEED_14000:
358c2ecf20Sopenharmony_ci		return "14Gbps";
368c2ecf20Sopenharmony_ci	case SPEED_20000:
378c2ecf20Sopenharmony_ci		return "20Gbps";
388c2ecf20Sopenharmony_ci	case SPEED_25000:
398c2ecf20Sopenharmony_ci		return "25Gbps";
408c2ecf20Sopenharmony_ci	case SPEED_40000:
418c2ecf20Sopenharmony_ci		return "40Gbps";
428c2ecf20Sopenharmony_ci	case SPEED_50000:
438c2ecf20Sopenharmony_ci		return "50Gbps";
448c2ecf20Sopenharmony_ci	case SPEED_56000:
458c2ecf20Sopenharmony_ci		return "56Gbps";
468c2ecf20Sopenharmony_ci	case SPEED_100000:
478c2ecf20Sopenharmony_ci		return "100Gbps";
488c2ecf20Sopenharmony_ci	case SPEED_200000:
498c2ecf20Sopenharmony_ci		return "200Gbps";
508c2ecf20Sopenharmony_ci	case SPEED_400000:
518c2ecf20Sopenharmony_ci		return "400Gbps";
528c2ecf20Sopenharmony_ci	case SPEED_UNKNOWN:
538c2ecf20Sopenharmony_ci		return "Unknown";
548c2ecf20Sopenharmony_ci	default:
558c2ecf20Sopenharmony_ci		return "Unsupported (update phy-core.c)";
568c2ecf20Sopenharmony_ci	}
578c2ecf20Sopenharmony_ci}
588c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(phy_speed_to_str);
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci/**
618c2ecf20Sopenharmony_ci * phy_duplex_to_str - Return string describing the duplex
628c2ecf20Sopenharmony_ci *
638c2ecf20Sopenharmony_ci * @duplex: Duplex setting to describe
648c2ecf20Sopenharmony_ci */
658c2ecf20Sopenharmony_ciconst char *phy_duplex_to_str(unsigned int duplex)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	if (duplex == DUPLEX_HALF)
688c2ecf20Sopenharmony_ci		return "Half";
698c2ecf20Sopenharmony_ci	if (duplex == DUPLEX_FULL)
708c2ecf20Sopenharmony_ci		return "Full";
718c2ecf20Sopenharmony_ci	if (duplex == DUPLEX_UNKNOWN)
728c2ecf20Sopenharmony_ci		return "Unknown";
738c2ecf20Sopenharmony_ci	return "Unsupported (update phy-core.c)";
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(phy_duplex_to_str);
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci/* A mapping of all SUPPORTED settings to speed/duplex.  This table
788c2ecf20Sopenharmony_ci * must be grouped by speed and sorted in descending match priority
798c2ecf20Sopenharmony_ci * - iow, descending speed. */
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci#define PHY_SETTING(s, d, b) { .speed = SPEED_ ## s, .duplex = DUPLEX_ ## d, \
828c2ecf20Sopenharmony_ci			       .bit = ETHTOOL_LINK_MODE_ ## b ## _BIT}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_cistatic const struct phy_setting settings[] = {
858c2ecf20Sopenharmony_ci	/* 400G */
868c2ecf20Sopenharmony_ci	PHY_SETTING( 400000, FULL, 400000baseCR8_Full		),
878c2ecf20Sopenharmony_ci	PHY_SETTING( 400000, FULL, 400000baseKR8_Full		),
888c2ecf20Sopenharmony_ci	PHY_SETTING( 400000, FULL, 400000baseLR8_ER8_FR8_Full	),
898c2ecf20Sopenharmony_ci	PHY_SETTING( 400000, FULL, 400000baseDR8_Full		),
908c2ecf20Sopenharmony_ci	PHY_SETTING( 400000, FULL, 400000baseSR8_Full		),
918c2ecf20Sopenharmony_ci	PHY_SETTING( 400000, FULL, 400000baseCR4_Full		),
928c2ecf20Sopenharmony_ci	PHY_SETTING( 400000, FULL, 400000baseKR4_Full		),
938c2ecf20Sopenharmony_ci	PHY_SETTING( 400000, FULL, 400000baseLR4_ER4_FR4_Full	),
948c2ecf20Sopenharmony_ci	PHY_SETTING( 400000, FULL, 400000baseDR4_Full		),
958c2ecf20Sopenharmony_ci	PHY_SETTING( 400000, FULL, 400000baseSR4_Full		),
968c2ecf20Sopenharmony_ci	/* 200G */
978c2ecf20Sopenharmony_ci	PHY_SETTING( 200000, FULL, 200000baseCR4_Full		),
988c2ecf20Sopenharmony_ci	PHY_SETTING( 200000, FULL, 200000baseKR4_Full		),
998c2ecf20Sopenharmony_ci	PHY_SETTING( 200000, FULL, 200000baseLR4_ER4_FR4_Full	),
1008c2ecf20Sopenharmony_ci	PHY_SETTING( 200000, FULL, 200000baseDR4_Full		),
1018c2ecf20Sopenharmony_ci	PHY_SETTING( 200000, FULL, 200000baseSR4_Full		),
1028c2ecf20Sopenharmony_ci	PHY_SETTING( 200000, FULL, 200000baseCR2_Full		),
1038c2ecf20Sopenharmony_ci	PHY_SETTING( 200000, FULL, 200000baseKR2_Full		),
1048c2ecf20Sopenharmony_ci	PHY_SETTING( 200000, FULL, 200000baseLR2_ER2_FR2_Full	),
1058c2ecf20Sopenharmony_ci	PHY_SETTING( 200000, FULL, 200000baseDR2_Full		),
1068c2ecf20Sopenharmony_ci	PHY_SETTING( 200000, FULL, 200000baseSR2_Full		),
1078c2ecf20Sopenharmony_ci	/* 100G */
1088c2ecf20Sopenharmony_ci	PHY_SETTING( 100000, FULL, 100000baseCR4_Full		),
1098c2ecf20Sopenharmony_ci	PHY_SETTING( 100000, FULL, 100000baseKR4_Full		),
1108c2ecf20Sopenharmony_ci	PHY_SETTING( 100000, FULL, 100000baseLR4_ER4_Full	),
1118c2ecf20Sopenharmony_ci	PHY_SETTING( 100000, FULL, 100000baseSR4_Full		),
1128c2ecf20Sopenharmony_ci	PHY_SETTING( 100000, FULL, 100000baseCR2_Full		),
1138c2ecf20Sopenharmony_ci	PHY_SETTING( 100000, FULL, 100000baseKR2_Full		),
1148c2ecf20Sopenharmony_ci	PHY_SETTING( 100000, FULL, 100000baseLR2_ER2_FR2_Full	),
1158c2ecf20Sopenharmony_ci	PHY_SETTING( 100000, FULL, 100000baseDR2_Full		),
1168c2ecf20Sopenharmony_ci	PHY_SETTING( 100000, FULL, 100000baseSR2_Full		),
1178c2ecf20Sopenharmony_ci	PHY_SETTING( 100000, FULL, 100000baseCR_Full		),
1188c2ecf20Sopenharmony_ci	PHY_SETTING( 100000, FULL, 100000baseKR_Full		),
1198c2ecf20Sopenharmony_ci	PHY_SETTING( 100000, FULL, 100000baseLR_ER_FR_Full	),
1208c2ecf20Sopenharmony_ci	PHY_SETTING( 100000, FULL, 100000baseDR_Full		),
1218c2ecf20Sopenharmony_ci	PHY_SETTING( 100000, FULL, 100000baseSR_Full		),
1228c2ecf20Sopenharmony_ci	/* 56G */
1238c2ecf20Sopenharmony_ci	PHY_SETTING(  56000, FULL,  56000baseCR4_Full	  	),
1248c2ecf20Sopenharmony_ci	PHY_SETTING(  56000, FULL,  56000baseKR4_Full	  	),
1258c2ecf20Sopenharmony_ci	PHY_SETTING(  56000, FULL,  56000baseLR4_Full	  	),
1268c2ecf20Sopenharmony_ci	PHY_SETTING(  56000, FULL,  56000baseSR4_Full	  	),
1278c2ecf20Sopenharmony_ci	/* 50G */
1288c2ecf20Sopenharmony_ci	PHY_SETTING(  50000, FULL,  50000baseCR2_Full		),
1298c2ecf20Sopenharmony_ci	PHY_SETTING(  50000, FULL,  50000baseKR2_Full		),
1308c2ecf20Sopenharmony_ci	PHY_SETTING(  50000, FULL,  50000baseSR2_Full		),
1318c2ecf20Sopenharmony_ci	PHY_SETTING(  50000, FULL,  50000baseCR_Full		),
1328c2ecf20Sopenharmony_ci	PHY_SETTING(  50000, FULL,  50000baseKR_Full		),
1338c2ecf20Sopenharmony_ci	PHY_SETTING(  50000, FULL,  50000baseLR_ER_FR_Full	),
1348c2ecf20Sopenharmony_ci	PHY_SETTING(  50000, FULL,  50000baseDR_Full		),
1358c2ecf20Sopenharmony_ci	PHY_SETTING(  50000, FULL,  50000baseSR_Full		),
1368c2ecf20Sopenharmony_ci	/* 40G */
1378c2ecf20Sopenharmony_ci	PHY_SETTING(  40000, FULL,  40000baseCR4_Full		),
1388c2ecf20Sopenharmony_ci	PHY_SETTING(  40000, FULL,  40000baseKR4_Full		),
1398c2ecf20Sopenharmony_ci	PHY_SETTING(  40000, FULL,  40000baseLR4_Full		),
1408c2ecf20Sopenharmony_ci	PHY_SETTING(  40000, FULL,  40000baseSR4_Full		),
1418c2ecf20Sopenharmony_ci	/* 25G */
1428c2ecf20Sopenharmony_ci	PHY_SETTING(  25000, FULL,  25000baseCR_Full		),
1438c2ecf20Sopenharmony_ci	PHY_SETTING(  25000, FULL,  25000baseKR_Full		),
1448c2ecf20Sopenharmony_ci	PHY_SETTING(  25000, FULL,  25000baseSR_Full		),
1458c2ecf20Sopenharmony_ci	/* 20G */
1468c2ecf20Sopenharmony_ci	PHY_SETTING(  20000, FULL,  20000baseKR2_Full		),
1478c2ecf20Sopenharmony_ci	PHY_SETTING(  20000, FULL,  20000baseMLD2_Full		),
1488c2ecf20Sopenharmony_ci	/* 10G */
1498c2ecf20Sopenharmony_ci	PHY_SETTING(  10000, FULL,  10000baseCR_Full		),
1508c2ecf20Sopenharmony_ci	PHY_SETTING(  10000, FULL,  10000baseER_Full		),
1518c2ecf20Sopenharmony_ci	PHY_SETTING(  10000, FULL,  10000baseKR_Full		),
1528c2ecf20Sopenharmony_ci	PHY_SETTING(  10000, FULL,  10000baseKX4_Full		),
1538c2ecf20Sopenharmony_ci	PHY_SETTING(  10000, FULL,  10000baseLR_Full		),
1548c2ecf20Sopenharmony_ci	PHY_SETTING(  10000, FULL,  10000baseLRM_Full		),
1558c2ecf20Sopenharmony_ci	PHY_SETTING(  10000, FULL,  10000baseR_FEC		),
1568c2ecf20Sopenharmony_ci	PHY_SETTING(  10000, FULL,  10000baseSR_Full		),
1578c2ecf20Sopenharmony_ci	PHY_SETTING(  10000, FULL,  10000baseT_Full		),
1588c2ecf20Sopenharmony_ci	/* 5G */
1598c2ecf20Sopenharmony_ci	PHY_SETTING(   5000, FULL,   5000baseT_Full		),
1608c2ecf20Sopenharmony_ci	/* 2.5G */
1618c2ecf20Sopenharmony_ci	PHY_SETTING(   2500, FULL,   2500baseT_Full		),
1628c2ecf20Sopenharmony_ci	PHY_SETTING(   2500, FULL,   2500baseX_Full		),
1638c2ecf20Sopenharmony_ci	/* 1G */
1648c2ecf20Sopenharmony_ci	PHY_SETTING(   1000, FULL,   1000baseT_Full		),
1658c2ecf20Sopenharmony_ci	PHY_SETTING(   1000, HALF,   1000baseT_Half		),
1668c2ecf20Sopenharmony_ci	PHY_SETTING(   1000, FULL,   1000baseT1_Full		),
1678c2ecf20Sopenharmony_ci	PHY_SETTING(   1000, FULL,   1000baseX_Full		),
1688c2ecf20Sopenharmony_ci	PHY_SETTING(   1000, FULL,   1000baseKX_Full		),
1698c2ecf20Sopenharmony_ci	/* 100M */
1708c2ecf20Sopenharmony_ci	PHY_SETTING(    100, FULL,    100baseT_Full		),
1718c2ecf20Sopenharmony_ci	PHY_SETTING(    100, FULL,    100baseT1_Full		),
1728c2ecf20Sopenharmony_ci	PHY_SETTING(    100, HALF,    100baseT_Half		),
1738c2ecf20Sopenharmony_ci	PHY_SETTING(    100, HALF,    100baseFX_Half		),
1748c2ecf20Sopenharmony_ci	PHY_SETTING(    100, FULL,    100baseFX_Full		),
1758c2ecf20Sopenharmony_ci	/* 10M */
1768c2ecf20Sopenharmony_ci	PHY_SETTING(     10, FULL,     10baseT_Full		),
1778c2ecf20Sopenharmony_ci	PHY_SETTING(     10, HALF,     10baseT_Half		),
1788c2ecf20Sopenharmony_ci};
1798c2ecf20Sopenharmony_ci#undef PHY_SETTING
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci/**
1828c2ecf20Sopenharmony_ci * phy_lookup_setting - lookup a PHY setting
1838c2ecf20Sopenharmony_ci * @speed: speed to match
1848c2ecf20Sopenharmony_ci * @duplex: duplex to match
1858c2ecf20Sopenharmony_ci * @mask: allowed link modes
1868c2ecf20Sopenharmony_ci * @exact: an exact match is required
1878c2ecf20Sopenharmony_ci *
1888c2ecf20Sopenharmony_ci * Search the settings array for a setting that matches the speed and
1898c2ecf20Sopenharmony_ci * duplex, and which is supported.
1908c2ecf20Sopenharmony_ci *
1918c2ecf20Sopenharmony_ci * If @exact is unset, either an exact match or %NULL for no match will
1928c2ecf20Sopenharmony_ci * be returned.
1938c2ecf20Sopenharmony_ci *
1948c2ecf20Sopenharmony_ci * If @exact is set, an exact match, the fastest supported setting at
1958c2ecf20Sopenharmony_ci * or below the specified speed, the slowest supported setting, or if
1968c2ecf20Sopenharmony_ci * they all fail, %NULL will be returned.
1978c2ecf20Sopenharmony_ci */
1988c2ecf20Sopenharmony_ciconst struct phy_setting *
1998c2ecf20Sopenharmony_ciphy_lookup_setting(int speed, int duplex, const unsigned long *mask, bool exact)
2008c2ecf20Sopenharmony_ci{
2018c2ecf20Sopenharmony_ci	const struct phy_setting *p, *match = NULL, *last = NULL;
2028c2ecf20Sopenharmony_ci	int i;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	for (i = 0, p = settings; i < ARRAY_SIZE(settings); i++, p++) {
2058c2ecf20Sopenharmony_ci		if (p->bit < __ETHTOOL_LINK_MODE_MASK_NBITS &&
2068c2ecf20Sopenharmony_ci		    test_bit(p->bit, mask)) {
2078c2ecf20Sopenharmony_ci			last = p;
2088c2ecf20Sopenharmony_ci			if (p->speed == speed && p->duplex == duplex) {
2098c2ecf20Sopenharmony_ci				/* Exact match for speed and duplex */
2108c2ecf20Sopenharmony_ci				match = p;
2118c2ecf20Sopenharmony_ci				break;
2128c2ecf20Sopenharmony_ci			} else if (!exact) {
2138c2ecf20Sopenharmony_ci				if (!match && p->speed <= speed)
2148c2ecf20Sopenharmony_ci					/* Candidate */
2158c2ecf20Sopenharmony_ci					match = p;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci				if (p->speed < speed)
2188c2ecf20Sopenharmony_ci					break;
2198c2ecf20Sopenharmony_ci			}
2208c2ecf20Sopenharmony_ci		}
2218c2ecf20Sopenharmony_ci	}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	if (!match && !exact)
2248c2ecf20Sopenharmony_ci		match = last;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	return match;
2278c2ecf20Sopenharmony_ci}
2288c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(phy_lookup_setting);
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_cisize_t phy_speeds(unsigned int *speeds, size_t size,
2318c2ecf20Sopenharmony_ci		  unsigned long *mask)
2328c2ecf20Sopenharmony_ci{
2338c2ecf20Sopenharmony_ci	size_t count;
2348c2ecf20Sopenharmony_ci	int i;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	for (i = 0, count = 0; i < ARRAY_SIZE(settings) && count < size; i++)
2378c2ecf20Sopenharmony_ci		if (settings[i].bit < __ETHTOOL_LINK_MODE_MASK_NBITS &&
2388c2ecf20Sopenharmony_ci		    test_bit(settings[i].bit, mask) &&
2398c2ecf20Sopenharmony_ci		    (count == 0 || speeds[count - 1] != settings[i].speed))
2408c2ecf20Sopenharmony_ci			speeds[count++] = settings[i].speed;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	return count;
2438c2ecf20Sopenharmony_ci}
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_cistatic int __set_linkmode_max_speed(u32 max_speed, unsigned long *addr)
2468c2ecf20Sopenharmony_ci{
2478c2ecf20Sopenharmony_ci	const struct phy_setting *p;
2488c2ecf20Sopenharmony_ci	int i;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	for (i = 0, p = settings; i < ARRAY_SIZE(settings); i++, p++) {
2518c2ecf20Sopenharmony_ci		if (p->speed > max_speed)
2528c2ecf20Sopenharmony_ci			linkmode_clear_bit(p->bit, addr);
2538c2ecf20Sopenharmony_ci		else
2548c2ecf20Sopenharmony_ci			break;
2558c2ecf20Sopenharmony_ci	}
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	return 0;
2588c2ecf20Sopenharmony_ci}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_cistatic int __set_phy_supported(struct phy_device *phydev, u32 max_speed)
2618c2ecf20Sopenharmony_ci{
2628c2ecf20Sopenharmony_ci	return __set_linkmode_max_speed(max_speed, phydev->supported);
2638c2ecf20Sopenharmony_ci}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci/**
2668c2ecf20Sopenharmony_ci * phy_set_max_speed - Set the maximum speed the PHY should support
2678c2ecf20Sopenharmony_ci *
2688c2ecf20Sopenharmony_ci * @phydev: The phy_device struct
2698c2ecf20Sopenharmony_ci * @max_speed: Maximum speed
2708c2ecf20Sopenharmony_ci *
2718c2ecf20Sopenharmony_ci * The PHY might be more capable than the MAC. For example a Fast Ethernet
2728c2ecf20Sopenharmony_ci * is connected to a 1G PHY. This function allows the MAC to indicate its
2738c2ecf20Sopenharmony_ci * maximum speed, and so limit what the PHY will advertise.
2748c2ecf20Sopenharmony_ci */
2758c2ecf20Sopenharmony_ciint phy_set_max_speed(struct phy_device *phydev, u32 max_speed)
2768c2ecf20Sopenharmony_ci{
2778c2ecf20Sopenharmony_ci	int err;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	err = __set_phy_supported(phydev, max_speed);
2808c2ecf20Sopenharmony_ci	if (err)
2818c2ecf20Sopenharmony_ci		return err;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	phy_advertise_supported(phydev);
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	return 0;
2868c2ecf20Sopenharmony_ci}
2878c2ecf20Sopenharmony_ciEXPORT_SYMBOL(phy_set_max_speed);
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_civoid of_set_phy_supported(struct phy_device *phydev)
2908c2ecf20Sopenharmony_ci{
2918c2ecf20Sopenharmony_ci	struct device_node *node = phydev->mdio.dev.of_node;
2928c2ecf20Sopenharmony_ci	u32 max_speed;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	if (!IS_ENABLED(CONFIG_OF_MDIO))
2958c2ecf20Sopenharmony_ci		return;
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	if (!node)
2988c2ecf20Sopenharmony_ci		return;
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	if (!of_property_read_u32(node, "max-speed", &max_speed))
3018c2ecf20Sopenharmony_ci		__set_phy_supported(phydev, max_speed);
3028c2ecf20Sopenharmony_ci}
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_civoid of_set_phy_eee_broken(struct phy_device *phydev)
3058c2ecf20Sopenharmony_ci{
3068c2ecf20Sopenharmony_ci	struct device_node *node = phydev->mdio.dev.of_node;
3078c2ecf20Sopenharmony_ci	u32 broken = 0;
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	if (!IS_ENABLED(CONFIG_OF_MDIO))
3108c2ecf20Sopenharmony_ci		return;
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	if (!node)
3138c2ecf20Sopenharmony_ci		return;
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	if (of_property_read_bool(node, "eee-broken-100tx"))
3168c2ecf20Sopenharmony_ci		broken |= MDIO_EEE_100TX;
3178c2ecf20Sopenharmony_ci	if (of_property_read_bool(node, "eee-broken-1000t"))
3188c2ecf20Sopenharmony_ci		broken |= MDIO_EEE_1000T;
3198c2ecf20Sopenharmony_ci	if (of_property_read_bool(node, "eee-broken-10gt"))
3208c2ecf20Sopenharmony_ci		broken |= MDIO_EEE_10GT;
3218c2ecf20Sopenharmony_ci	if (of_property_read_bool(node, "eee-broken-1000kx"))
3228c2ecf20Sopenharmony_ci		broken |= MDIO_EEE_1000KX;
3238c2ecf20Sopenharmony_ci	if (of_property_read_bool(node, "eee-broken-10gkx4"))
3248c2ecf20Sopenharmony_ci		broken |= MDIO_EEE_10GKX4;
3258c2ecf20Sopenharmony_ci	if (of_property_read_bool(node, "eee-broken-10gkr"))
3268c2ecf20Sopenharmony_ci		broken |= MDIO_EEE_10GKR;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	phydev->eee_broken_modes = broken;
3298c2ecf20Sopenharmony_ci}
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci/**
3328c2ecf20Sopenharmony_ci * phy_resolve_aneg_pause - Determine pause autoneg results
3338c2ecf20Sopenharmony_ci *
3348c2ecf20Sopenharmony_ci * @phydev: The phy_device struct
3358c2ecf20Sopenharmony_ci *
3368c2ecf20Sopenharmony_ci * Once autoneg has completed the local pause settings can be
3378c2ecf20Sopenharmony_ci * resolved.  Determine if pause and asymmetric pause should be used
3388c2ecf20Sopenharmony_ci * by the MAC.
3398c2ecf20Sopenharmony_ci */
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_civoid phy_resolve_aneg_pause(struct phy_device *phydev)
3428c2ecf20Sopenharmony_ci{
3438c2ecf20Sopenharmony_ci	if (phydev->duplex == DUPLEX_FULL) {
3448c2ecf20Sopenharmony_ci		phydev->pause = linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT,
3458c2ecf20Sopenharmony_ci						  phydev->lp_advertising);
3468c2ecf20Sopenharmony_ci		phydev->asym_pause = linkmode_test_bit(
3478c2ecf20Sopenharmony_ci			ETHTOOL_LINK_MODE_Asym_Pause_BIT,
3488c2ecf20Sopenharmony_ci			phydev->lp_advertising);
3498c2ecf20Sopenharmony_ci	}
3508c2ecf20Sopenharmony_ci}
3518c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(phy_resolve_aneg_pause);
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci/**
3548c2ecf20Sopenharmony_ci * phy_resolve_aneg_linkmode - resolve the advertisements into PHY settings
3558c2ecf20Sopenharmony_ci * @phydev: The phy_device struct
3568c2ecf20Sopenharmony_ci *
3578c2ecf20Sopenharmony_ci * Resolve our and the link partner advertisements into their corresponding
3588c2ecf20Sopenharmony_ci * speed and duplex. If full duplex was negotiated, extract the pause mode
3598c2ecf20Sopenharmony_ci * from the link partner mask.
3608c2ecf20Sopenharmony_ci */
3618c2ecf20Sopenharmony_civoid phy_resolve_aneg_linkmode(struct phy_device *phydev)
3628c2ecf20Sopenharmony_ci{
3638c2ecf20Sopenharmony_ci	__ETHTOOL_DECLARE_LINK_MODE_MASK(common);
3648c2ecf20Sopenharmony_ci	int i;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	linkmode_and(common, phydev->lp_advertising, phydev->advertising);
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(settings); i++)
3698c2ecf20Sopenharmony_ci		if (test_bit(settings[i].bit, common)) {
3708c2ecf20Sopenharmony_ci			phydev->speed = settings[i].speed;
3718c2ecf20Sopenharmony_ci			phydev->duplex = settings[i].duplex;
3728c2ecf20Sopenharmony_ci			break;
3738c2ecf20Sopenharmony_ci		}
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	phy_resolve_aneg_pause(phydev);
3768c2ecf20Sopenharmony_ci}
3778c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(phy_resolve_aneg_linkmode);
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci/**
3808c2ecf20Sopenharmony_ci * phy_check_downshift - check whether downshift occurred
3818c2ecf20Sopenharmony_ci * @phydev: The phy_device struct
3828c2ecf20Sopenharmony_ci *
3838c2ecf20Sopenharmony_ci * Check whether a downshift to a lower speed occurred. If this should be the
3848c2ecf20Sopenharmony_ci * case warn the user.
3858c2ecf20Sopenharmony_ci * Prerequisite for detecting downshift is that PHY driver implements the
3868c2ecf20Sopenharmony_ci * read_status callback and sets phydev->speed to the actual link speed.
3878c2ecf20Sopenharmony_ci */
3888c2ecf20Sopenharmony_civoid phy_check_downshift(struct phy_device *phydev)
3898c2ecf20Sopenharmony_ci{
3908c2ecf20Sopenharmony_ci	__ETHTOOL_DECLARE_LINK_MODE_MASK(common);
3918c2ecf20Sopenharmony_ci	int i, speed = SPEED_UNKNOWN;
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	phydev->downshifted_rate = 0;
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	if (phydev->autoneg == AUTONEG_DISABLE ||
3968c2ecf20Sopenharmony_ci	    phydev->speed == SPEED_UNKNOWN)
3978c2ecf20Sopenharmony_ci		return;
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	linkmode_and(common, phydev->lp_advertising, phydev->advertising);
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(settings); i++)
4028c2ecf20Sopenharmony_ci		if (test_bit(settings[i].bit, common)) {
4038c2ecf20Sopenharmony_ci			speed = settings[i].speed;
4048c2ecf20Sopenharmony_ci			break;
4058c2ecf20Sopenharmony_ci		}
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	if (speed == SPEED_UNKNOWN || phydev->speed >= speed)
4088c2ecf20Sopenharmony_ci		return;
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	phydev_warn(phydev, "Downshift occurred from negotiated speed %s to actual speed %s, check cabling!\n",
4118c2ecf20Sopenharmony_ci		    phy_speed_to_str(speed), phy_speed_to_str(phydev->speed));
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	phydev->downshifted_rate = 1;
4148c2ecf20Sopenharmony_ci}
4158c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(phy_check_downshift);
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_cistatic int phy_resolve_min_speed(struct phy_device *phydev, bool fdx_only)
4188c2ecf20Sopenharmony_ci{
4198c2ecf20Sopenharmony_ci	__ETHTOOL_DECLARE_LINK_MODE_MASK(common);
4208c2ecf20Sopenharmony_ci	int i = ARRAY_SIZE(settings);
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	linkmode_and(common, phydev->lp_advertising, phydev->advertising);
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	while (--i >= 0) {
4258c2ecf20Sopenharmony_ci		if (test_bit(settings[i].bit, common)) {
4268c2ecf20Sopenharmony_ci			if (fdx_only && settings[i].duplex != DUPLEX_FULL)
4278c2ecf20Sopenharmony_ci				continue;
4288c2ecf20Sopenharmony_ci			return settings[i].speed;
4298c2ecf20Sopenharmony_ci		}
4308c2ecf20Sopenharmony_ci	}
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	return SPEED_UNKNOWN;
4338c2ecf20Sopenharmony_ci}
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ciint phy_speed_down_core(struct phy_device *phydev)
4368c2ecf20Sopenharmony_ci{
4378c2ecf20Sopenharmony_ci	int min_common_speed = phy_resolve_min_speed(phydev, true);
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	if (min_common_speed == SPEED_UNKNOWN)
4408c2ecf20Sopenharmony_ci		return -EINVAL;
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	return __set_linkmode_max_speed(min_common_speed, phydev->advertising);
4438c2ecf20Sopenharmony_ci}
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_cistatic void mmd_phy_indirect(struct mii_bus *bus, int phy_addr, int devad,
4468c2ecf20Sopenharmony_ci			     u16 regnum)
4478c2ecf20Sopenharmony_ci{
4488c2ecf20Sopenharmony_ci	/* Write the desired MMD Devad */
4498c2ecf20Sopenharmony_ci	__mdiobus_write(bus, phy_addr, MII_MMD_CTRL, devad);
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	/* Write the desired MMD register address */
4528c2ecf20Sopenharmony_ci	__mdiobus_write(bus, phy_addr, MII_MMD_DATA, regnum);
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	/* Select the Function : DATA with no post increment */
4558c2ecf20Sopenharmony_ci	__mdiobus_write(bus, phy_addr, MII_MMD_CTRL,
4568c2ecf20Sopenharmony_ci			devad | MII_MMD_CTRL_NOINCR);
4578c2ecf20Sopenharmony_ci}
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci/**
4608c2ecf20Sopenharmony_ci * __phy_read_mmd - Convenience function for reading a register
4618c2ecf20Sopenharmony_ci * from an MMD on a given PHY.
4628c2ecf20Sopenharmony_ci * @phydev: The phy_device struct
4638c2ecf20Sopenharmony_ci * @devad: The MMD to read from (0..31)
4648c2ecf20Sopenharmony_ci * @regnum: The register on the MMD to read (0..65535)
4658c2ecf20Sopenharmony_ci *
4668c2ecf20Sopenharmony_ci * Same rules as for __phy_read();
4678c2ecf20Sopenharmony_ci */
4688c2ecf20Sopenharmony_ciint __phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum)
4698c2ecf20Sopenharmony_ci{
4708c2ecf20Sopenharmony_ci	int val;
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	if (regnum > (u16)~0 || devad > 32)
4738c2ecf20Sopenharmony_ci		return -EINVAL;
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	if (phydev->drv && phydev->drv->read_mmd) {
4768c2ecf20Sopenharmony_ci		val = phydev->drv->read_mmd(phydev, devad, regnum);
4778c2ecf20Sopenharmony_ci	} else if (phydev->is_c45) {
4788c2ecf20Sopenharmony_ci		val = __mdiobus_c45_read(phydev->mdio.bus, phydev->mdio.addr,
4798c2ecf20Sopenharmony_ci					 devad, regnum);
4808c2ecf20Sopenharmony_ci	} else {
4818c2ecf20Sopenharmony_ci		struct mii_bus *bus = phydev->mdio.bus;
4828c2ecf20Sopenharmony_ci		int phy_addr = phydev->mdio.addr;
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci		mmd_phy_indirect(bus, phy_addr, devad, regnum);
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci		/* Read the content of the MMD's selected register */
4878c2ecf20Sopenharmony_ci		val = __mdiobus_read(bus, phy_addr, MII_MMD_DATA);
4888c2ecf20Sopenharmony_ci	}
4898c2ecf20Sopenharmony_ci	return val;
4908c2ecf20Sopenharmony_ci}
4918c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__phy_read_mmd);
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci/**
4948c2ecf20Sopenharmony_ci * phy_read_mmd - Convenience function for reading a register
4958c2ecf20Sopenharmony_ci * from an MMD on a given PHY.
4968c2ecf20Sopenharmony_ci * @phydev: The phy_device struct
4978c2ecf20Sopenharmony_ci * @devad: The MMD to read from
4988c2ecf20Sopenharmony_ci * @regnum: The register on the MMD to read
4998c2ecf20Sopenharmony_ci *
5008c2ecf20Sopenharmony_ci * Same rules as for phy_read();
5018c2ecf20Sopenharmony_ci */
5028c2ecf20Sopenharmony_ciint phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum)
5038c2ecf20Sopenharmony_ci{
5048c2ecf20Sopenharmony_ci	int ret;
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	phy_lock_mdio_bus(phydev);
5078c2ecf20Sopenharmony_ci	ret = __phy_read_mmd(phydev, devad, regnum);
5088c2ecf20Sopenharmony_ci	phy_unlock_mdio_bus(phydev);
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	return ret;
5118c2ecf20Sopenharmony_ci}
5128c2ecf20Sopenharmony_ciEXPORT_SYMBOL(phy_read_mmd);
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci/**
5158c2ecf20Sopenharmony_ci * __phy_write_mmd - Convenience function for writing a register
5168c2ecf20Sopenharmony_ci * on an MMD on a given PHY.
5178c2ecf20Sopenharmony_ci * @phydev: The phy_device struct
5188c2ecf20Sopenharmony_ci * @devad: The MMD to read from
5198c2ecf20Sopenharmony_ci * @regnum: The register on the MMD to read
5208c2ecf20Sopenharmony_ci * @val: value to write to @regnum
5218c2ecf20Sopenharmony_ci *
5228c2ecf20Sopenharmony_ci * Same rules as for __phy_write();
5238c2ecf20Sopenharmony_ci */
5248c2ecf20Sopenharmony_ciint __phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val)
5258c2ecf20Sopenharmony_ci{
5268c2ecf20Sopenharmony_ci	int ret;
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	if (regnum > (u16)~0 || devad > 32)
5298c2ecf20Sopenharmony_ci		return -EINVAL;
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	if (phydev->drv && phydev->drv->write_mmd) {
5328c2ecf20Sopenharmony_ci		ret = phydev->drv->write_mmd(phydev, devad, regnum, val);
5338c2ecf20Sopenharmony_ci	} else if (phydev->is_c45) {
5348c2ecf20Sopenharmony_ci		ret = __mdiobus_c45_write(phydev->mdio.bus, phydev->mdio.addr,
5358c2ecf20Sopenharmony_ci					  devad, regnum, val);
5368c2ecf20Sopenharmony_ci	} else {
5378c2ecf20Sopenharmony_ci		struct mii_bus *bus = phydev->mdio.bus;
5388c2ecf20Sopenharmony_ci		int phy_addr = phydev->mdio.addr;
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci		mmd_phy_indirect(bus, phy_addr, devad, regnum);
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci		/* Write the data into MMD's selected register */
5438c2ecf20Sopenharmony_ci		__mdiobus_write(bus, phy_addr, MII_MMD_DATA, val);
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci		ret = 0;
5468c2ecf20Sopenharmony_ci	}
5478c2ecf20Sopenharmony_ci	return ret;
5488c2ecf20Sopenharmony_ci}
5498c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__phy_write_mmd);
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci/**
5528c2ecf20Sopenharmony_ci * phy_write_mmd - Convenience function for writing a register
5538c2ecf20Sopenharmony_ci * on an MMD on a given PHY.
5548c2ecf20Sopenharmony_ci * @phydev: The phy_device struct
5558c2ecf20Sopenharmony_ci * @devad: The MMD to read from
5568c2ecf20Sopenharmony_ci * @regnum: The register on the MMD to read
5578c2ecf20Sopenharmony_ci * @val: value to write to @regnum
5588c2ecf20Sopenharmony_ci *
5598c2ecf20Sopenharmony_ci * Same rules as for phy_write();
5608c2ecf20Sopenharmony_ci */
5618c2ecf20Sopenharmony_ciint phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val)
5628c2ecf20Sopenharmony_ci{
5638c2ecf20Sopenharmony_ci	int ret;
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	phy_lock_mdio_bus(phydev);
5668c2ecf20Sopenharmony_ci	ret = __phy_write_mmd(phydev, devad, regnum, val);
5678c2ecf20Sopenharmony_ci	phy_unlock_mdio_bus(phydev);
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci	return ret;
5708c2ecf20Sopenharmony_ci}
5718c2ecf20Sopenharmony_ciEXPORT_SYMBOL(phy_write_mmd);
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci/**
5748c2ecf20Sopenharmony_ci * phy_modify_changed - Function for modifying a PHY register
5758c2ecf20Sopenharmony_ci * @phydev: the phy_device struct
5768c2ecf20Sopenharmony_ci * @regnum: register number to modify
5778c2ecf20Sopenharmony_ci * @mask: bit mask of bits to clear
5788c2ecf20Sopenharmony_ci * @set: new value of bits set in mask to write to @regnum
5798c2ecf20Sopenharmony_ci *
5808c2ecf20Sopenharmony_ci * NOTE: MUST NOT be called from interrupt context,
5818c2ecf20Sopenharmony_ci * because the bus read/write functions may wait for an interrupt
5828c2ecf20Sopenharmony_ci * to conclude the operation.
5838c2ecf20Sopenharmony_ci *
5848c2ecf20Sopenharmony_ci * Returns negative errno, 0 if there was no change, and 1 in case of change
5858c2ecf20Sopenharmony_ci */
5868c2ecf20Sopenharmony_ciint phy_modify_changed(struct phy_device *phydev, u32 regnum, u16 mask, u16 set)
5878c2ecf20Sopenharmony_ci{
5888c2ecf20Sopenharmony_ci	int ret;
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci	phy_lock_mdio_bus(phydev);
5918c2ecf20Sopenharmony_ci	ret = __phy_modify_changed(phydev, regnum, mask, set);
5928c2ecf20Sopenharmony_ci	phy_unlock_mdio_bus(phydev);
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci	return ret;
5958c2ecf20Sopenharmony_ci}
5968c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(phy_modify_changed);
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci/**
5998c2ecf20Sopenharmony_ci * __phy_modify - Convenience function for modifying a PHY register
6008c2ecf20Sopenharmony_ci * @phydev: the phy_device struct
6018c2ecf20Sopenharmony_ci * @regnum: register number to modify
6028c2ecf20Sopenharmony_ci * @mask: bit mask of bits to clear
6038c2ecf20Sopenharmony_ci * @set: new value of bits set in mask to write to @regnum
6048c2ecf20Sopenharmony_ci *
6058c2ecf20Sopenharmony_ci * NOTE: MUST NOT be called from interrupt context,
6068c2ecf20Sopenharmony_ci * because the bus read/write functions may wait for an interrupt
6078c2ecf20Sopenharmony_ci * to conclude the operation.
6088c2ecf20Sopenharmony_ci */
6098c2ecf20Sopenharmony_ciint __phy_modify(struct phy_device *phydev, u32 regnum, u16 mask, u16 set)
6108c2ecf20Sopenharmony_ci{
6118c2ecf20Sopenharmony_ci	int ret;
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	ret = __phy_modify_changed(phydev, regnum, mask, set);
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci	return ret < 0 ? ret : 0;
6168c2ecf20Sopenharmony_ci}
6178c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__phy_modify);
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci/**
6208c2ecf20Sopenharmony_ci * phy_modify - Convenience function for modifying a given PHY register
6218c2ecf20Sopenharmony_ci * @phydev: the phy_device struct
6228c2ecf20Sopenharmony_ci * @regnum: register number to write
6238c2ecf20Sopenharmony_ci * @mask: bit mask of bits to clear
6248c2ecf20Sopenharmony_ci * @set: new value of bits set in mask to write to @regnum
6258c2ecf20Sopenharmony_ci *
6268c2ecf20Sopenharmony_ci * NOTE: MUST NOT be called from interrupt context,
6278c2ecf20Sopenharmony_ci * because the bus read/write functions may wait for an interrupt
6288c2ecf20Sopenharmony_ci * to conclude the operation.
6298c2ecf20Sopenharmony_ci */
6308c2ecf20Sopenharmony_ciint phy_modify(struct phy_device *phydev, u32 regnum, u16 mask, u16 set)
6318c2ecf20Sopenharmony_ci{
6328c2ecf20Sopenharmony_ci	int ret;
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ci	phy_lock_mdio_bus(phydev);
6358c2ecf20Sopenharmony_ci	ret = __phy_modify(phydev, regnum, mask, set);
6368c2ecf20Sopenharmony_ci	phy_unlock_mdio_bus(phydev);
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci	return ret;
6398c2ecf20Sopenharmony_ci}
6408c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(phy_modify);
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci/**
6438c2ecf20Sopenharmony_ci * __phy_modify_mmd_changed - Function for modifying a register on MMD
6448c2ecf20Sopenharmony_ci * @phydev: the phy_device struct
6458c2ecf20Sopenharmony_ci * @devad: the MMD containing register to modify
6468c2ecf20Sopenharmony_ci * @regnum: register number to modify
6478c2ecf20Sopenharmony_ci * @mask: bit mask of bits to clear
6488c2ecf20Sopenharmony_ci * @set: new value of bits set in mask to write to @regnum
6498c2ecf20Sopenharmony_ci *
6508c2ecf20Sopenharmony_ci * Unlocked helper function which allows a MMD register to be modified as
6518c2ecf20Sopenharmony_ci * new register value = (old register value & ~mask) | set
6528c2ecf20Sopenharmony_ci *
6538c2ecf20Sopenharmony_ci * Returns negative errno, 0 if there was no change, and 1 in case of change
6548c2ecf20Sopenharmony_ci */
6558c2ecf20Sopenharmony_ciint __phy_modify_mmd_changed(struct phy_device *phydev, int devad, u32 regnum,
6568c2ecf20Sopenharmony_ci			     u16 mask, u16 set)
6578c2ecf20Sopenharmony_ci{
6588c2ecf20Sopenharmony_ci	int new, ret;
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	ret = __phy_read_mmd(phydev, devad, regnum);
6618c2ecf20Sopenharmony_ci	if (ret < 0)
6628c2ecf20Sopenharmony_ci		return ret;
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci	new = (ret & ~mask) | set;
6658c2ecf20Sopenharmony_ci	if (new == ret)
6668c2ecf20Sopenharmony_ci		return 0;
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci	ret = __phy_write_mmd(phydev, devad, regnum, new);
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci	return ret < 0 ? ret : 1;
6718c2ecf20Sopenharmony_ci}
6728c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__phy_modify_mmd_changed);
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci/**
6758c2ecf20Sopenharmony_ci * phy_modify_mmd_changed - Function for modifying a register on MMD
6768c2ecf20Sopenharmony_ci * @phydev: the phy_device struct
6778c2ecf20Sopenharmony_ci * @devad: the MMD containing register to modify
6788c2ecf20Sopenharmony_ci * @regnum: register number to modify
6798c2ecf20Sopenharmony_ci * @mask: bit mask of bits to clear
6808c2ecf20Sopenharmony_ci * @set: new value of bits set in mask to write to @regnum
6818c2ecf20Sopenharmony_ci *
6828c2ecf20Sopenharmony_ci * NOTE: MUST NOT be called from interrupt context,
6838c2ecf20Sopenharmony_ci * because the bus read/write functions may wait for an interrupt
6848c2ecf20Sopenharmony_ci * to conclude the operation.
6858c2ecf20Sopenharmony_ci *
6868c2ecf20Sopenharmony_ci * Returns negative errno, 0 if there was no change, and 1 in case of change
6878c2ecf20Sopenharmony_ci */
6888c2ecf20Sopenharmony_ciint phy_modify_mmd_changed(struct phy_device *phydev, int devad, u32 regnum,
6898c2ecf20Sopenharmony_ci			   u16 mask, u16 set)
6908c2ecf20Sopenharmony_ci{
6918c2ecf20Sopenharmony_ci	int ret;
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci	phy_lock_mdio_bus(phydev);
6948c2ecf20Sopenharmony_ci	ret = __phy_modify_mmd_changed(phydev, devad, regnum, mask, set);
6958c2ecf20Sopenharmony_ci	phy_unlock_mdio_bus(phydev);
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci	return ret;
6988c2ecf20Sopenharmony_ci}
6998c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(phy_modify_mmd_changed);
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci/**
7028c2ecf20Sopenharmony_ci * __phy_modify_mmd - Convenience function for modifying a register on MMD
7038c2ecf20Sopenharmony_ci * @phydev: the phy_device struct
7048c2ecf20Sopenharmony_ci * @devad: the MMD containing register to modify
7058c2ecf20Sopenharmony_ci * @regnum: register number to modify
7068c2ecf20Sopenharmony_ci * @mask: bit mask of bits to clear
7078c2ecf20Sopenharmony_ci * @set: new value of bits set in mask to write to @regnum
7088c2ecf20Sopenharmony_ci *
7098c2ecf20Sopenharmony_ci * NOTE: MUST NOT be called from interrupt context,
7108c2ecf20Sopenharmony_ci * because the bus read/write functions may wait for an interrupt
7118c2ecf20Sopenharmony_ci * to conclude the operation.
7128c2ecf20Sopenharmony_ci */
7138c2ecf20Sopenharmony_ciint __phy_modify_mmd(struct phy_device *phydev, int devad, u32 regnum,
7148c2ecf20Sopenharmony_ci		     u16 mask, u16 set)
7158c2ecf20Sopenharmony_ci{
7168c2ecf20Sopenharmony_ci	int ret;
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci	ret = __phy_modify_mmd_changed(phydev, devad, regnum, mask, set);
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci	return ret < 0 ? ret : 0;
7218c2ecf20Sopenharmony_ci}
7228c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__phy_modify_mmd);
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci/**
7258c2ecf20Sopenharmony_ci * phy_modify_mmd - Convenience function for modifying a register on MMD
7268c2ecf20Sopenharmony_ci * @phydev: the phy_device struct
7278c2ecf20Sopenharmony_ci * @devad: the MMD containing register to modify
7288c2ecf20Sopenharmony_ci * @regnum: register number to modify
7298c2ecf20Sopenharmony_ci * @mask: bit mask of bits to clear
7308c2ecf20Sopenharmony_ci * @set: new value of bits set in mask to write to @regnum
7318c2ecf20Sopenharmony_ci *
7328c2ecf20Sopenharmony_ci * NOTE: MUST NOT be called from interrupt context,
7338c2ecf20Sopenharmony_ci * because the bus read/write functions may wait for an interrupt
7348c2ecf20Sopenharmony_ci * to conclude the operation.
7358c2ecf20Sopenharmony_ci */
7368c2ecf20Sopenharmony_ciint phy_modify_mmd(struct phy_device *phydev, int devad, u32 regnum,
7378c2ecf20Sopenharmony_ci		   u16 mask, u16 set)
7388c2ecf20Sopenharmony_ci{
7398c2ecf20Sopenharmony_ci	int ret;
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci	phy_lock_mdio_bus(phydev);
7428c2ecf20Sopenharmony_ci	ret = __phy_modify_mmd(phydev, devad, regnum, mask, set);
7438c2ecf20Sopenharmony_ci	phy_unlock_mdio_bus(phydev);
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_ci	return ret;
7468c2ecf20Sopenharmony_ci}
7478c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(phy_modify_mmd);
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_cistatic int __phy_read_page(struct phy_device *phydev)
7508c2ecf20Sopenharmony_ci{
7518c2ecf20Sopenharmony_ci	if (WARN_ONCE(!phydev->drv->read_page, "read_page callback not available, PHY driver not loaded?\n"))
7528c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci	return phydev->drv->read_page(phydev);
7558c2ecf20Sopenharmony_ci}
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_cistatic int __phy_write_page(struct phy_device *phydev, int page)
7588c2ecf20Sopenharmony_ci{
7598c2ecf20Sopenharmony_ci	if (WARN_ONCE(!phydev->drv->write_page, "write_page callback not available, PHY driver not loaded?\n"))
7608c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_ci	return phydev->drv->write_page(phydev, page);
7638c2ecf20Sopenharmony_ci}
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ci/**
7668c2ecf20Sopenharmony_ci * phy_save_page() - take the bus lock and save the current page
7678c2ecf20Sopenharmony_ci * @phydev: a pointer to a &struct phy_device
7688c2ecf20Sopenharmony_ci *
7698c2ecf20Sopenharmony_ci * Take the MDIO bus lock, and return the current page number. On error,
7708c2ecf20Sopenharmony_ci * returns a negative errno. phy_restore_page() must always be called
7718c2ecf20Sopenharmony_ci * after this, irrespective of success or failure of this call.
7728c2ecf20Sopenharmony_ci */
7738c2ecf20Sopenharmony_ciint phy_save_page(struct phy_device *phydev)
7748c2ecf20Sopenharmony_ci{
7758c2ecf20Sopenharmony_ci	phy_lock_mdio_bus(phydev);
7768c2ecf20Sopenharmony_ci	return __phy_read_page(phydev);
7778c2ecf20Sopenharmony_ci}
7788c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(phy_save_page);
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ci/**
7818c2ecf20Sopenharmony_ci * phy_select_page() - take the bus lock, save the current page, and set a page
7828c2ecf20Sopenharmony_ci * @phydev: a pointer to a &struct phy_device
7838c2ecf20Sopenharmony_ci * @page: desired page
7848c2ecf20Sopenharmony_ci *
7858c2ecf20Sopenharmony_ci * Take the MDIO bus lock to protect against concurrent access, save the
7868c2ecf20Sopenharmony_ci * current PHY page, and set the current page.  On error, returns a
7878c2ecf20Sopenharmony_ci * negative errno, otherwise returns the previous page number.
7888c2ecf20Sopenharmony_ci * phy_restore_page() must always be called after this, irrespective
7898c2ecf20Sopenharmony_ci * of success or failure of this call.
7908c2ecf20Sopenharmony_ci */
7918c2ecf20Sopenharmony_ciint phy_select_page(struct phy_device *phydev, int page)
7928c2ecf20Sopenharmony_ci{
7938c2ecf20Sopenharmony_ci	int ret, oldpage;
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_ci	oldpage = ret = phy_save_page(phydev);
7968c2ecf20Sopenharmony_ci	if (ret < 0)
7978c2ecf20Sopenharmony_ci		return ret;
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci	if (oldpage != page) {
8008c2ecf20Sopenharmony_ci		ret = __phy_write_page(phydev, page);
8018c2ecf20Sopenharmony_ci		if (ret < 0)
8028c2ecf20Sopenharmony_ci			return ret;
8038c2ecf20Sopenharmony_ci	}
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci	return oldpage;
8068c2ecf20Sopenharmony_ci}
8078c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(phy_select_page);
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci/**
8108c2ecf20Sopenharmony_ci * phy_restore_page() - restore the page register and release the bus lock
8118c2ecf20Sopenharmony_ci * @phydev: a pointer to a &struct phy_device
8128c2ecf20Sopenharmony_ci * @oldpage: the old page, return value from phy_save_page() or phy_select_page()
8138c2ecf20Sopenharmony_ci * @ret: operation's return code
8148c2ecf20Sopenharmony_ci *
8158c2ecf20Sopenharmony_ci * Release the MDIO bus lock, restoring @oldpage if it is a valid page.
8168c2ecf20Sopenharmony_ci * This function propagates the earliest error code from the group of
8178c2ecf20Sopenharmony_ci * operations.
8188c2ecf20Sopenharmony_ci *
8198c2ecf20Sopenharmony_ci * Returns:
8208c2ecf20Sopenharmony_ci *   @oldpage if it was a negative value, otherwise
8218c2ecf20Sopenharmony_ci *   @ret if it was a negative errno value, otherwise
8228c2ecf20Sopenharmony_ci *   phy_write_page()'s negative value if it were in error, otherwise
8238c2ecf20Sopenharmony_ci *   @ret.
8248c2ecf20Sopenharmony_ci */
8258c2ecf20Sopenharmony_ciint phy_restore_page(struct phy_device *phydev, int oldpage, int ret)
8268c2ecf20Sopenharmony_ci{
8278c2ecf20Sopenharmony_ci	int r;
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ci	if (oldpage >= 0) {
8308c2ecf20Sopenharmony_ci		r = __phy_write_page(phydev, oldpage);
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_ci		/* Propagate the operation return code if the page write
8338c2ecf20Sopenharmony_ci		 * was successful.
8348c2ecf20Sopenharmony_ci		 */
8358c2ecf20Sopenharmony_ci		if (ret >= 0 && r < 0)
8368c2ecf20Sopenharmony_ci			ret = r;
8378c2ecf20Sopenharmony_ci	} else {
8388c2ecf20Sopenharmony_ci		/* Propagate the phy page selection error code */
8398c2ecf20Sopenharmony_ci		ret = oldpage;
8408c2ecf20Sopenharmony_ci	}
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_ci	phy_unlock_mdio_bus(phydev);
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci	return ret;
8458c2ecf20Sopenharmony_ci}
8468c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(phy_restore_page);
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ci/**
8498c2ecf20Sopenharmony_ci * phy_read_paged() - Convenience function for reading a paged register
8508c2ecf20Sopenharmony_ci * @phydev: a pointer to a &struct phy_device
8518c2ecf20Sopenharmony_ci * @page: the page for the phy
8528c2ecf20Sopenharmony_ci * @regnum: register number
8538c2ecf20Sopenharmony_ci *
8548c2ecf20Sopenharmony_ci * Same rules as for phy_read().
8558c2ecf20Sopenharmony_ci */
8568c2ecf20Sopenharmony_ciint phy_read_paged(struct phy_device *phydev, int page, u32 regnum)
8578c2ecf20Sopenharmony_ci{
8588c2ecf20Sopenharmony_ci	int ret = 0, oldpage;
8598c2ecf20Sopenharmony_ci
8608c2ecf20Sopenharmony_ci	oldpage = phy_select_page(phydev, page);
8618c2ecf20Sopenharmony_ci	if (oldpage >= 0)
8628c2ecf20Sopenharmony_ci		ret = __phy_read(phydev, regnum);
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_ci	return phy_restore_page(phydev, oldpage, ret);
8658c2ecf20Sopenharmony_ci}
8668c2ecf20Sopenharmony_ciEXPORT_SYMBOL(phy_read_paged);
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_ci/**
8698c2ecf20Sopenharmony_ci * phy_write_paged() - Convenience function for writing a paged register
8708c2ecf20Sopenharmony_ci * @phydev: a pointer to a &struct phy_device
8718c2ecf20Sopenharmony_ci * @page: the page for the phy
8728c2ecf20Sopenharmony_ci * @regnum: register number
8738c2ecf20Sopenharmony_ci * @val: value to write
8748c2ecf20Sopenharmony_ci *
8758c2ecf20Sopenharmony_ci * Same rules as for phy_write().
8768c2ecf20Sopenharmony_ci */
8778c2ecf20Sopenharmony_ciint phy_write_paged(struct phy_device *phydev, int page, u32 regnum, u16 val)
8788c2ecf20Sopenharmony_ci{
8798c2ecf20Sopenharmony_ci	int ret = 0, oldpage;
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_ci	oldpage = phy_select_page(phydev, page);
8828c2ecf20Sopenharmony_ci	if (oldpage >= 0)
8838c2ecf20Sopenharmony_ci		ret = __phy_write(phydev, regnum, val);
8848c2ecf20Sopenharmony_ci
8858c2ecf20Sopenharmony_ci	return phy_restore_page(phydev, oldpage, ret);
8868c2ecf20Sopenharmony_ci}
8878c2ecf20Sopenharmony_ciEXPORT_SYMBOL(phy_write_paged);
8888c2ecf20Sopenharmony_ci
8898c2ecf20Sopenharmony_ci/**
8908c2ecf20Sopenharmony_ci * phy_modify_paged_changed() - Function for modifying a paged register
8918c2ecf20Sopenharmony_ci * @phydev: a pointer to a &struct phy_device
8928c2ecf20Sopenharmony_ci * @page: the page for the phy
8938c2ecf20Sopenharmony_ci * @regnum: register number
8948c2ecf20Sopenharmony_ci * @mask: bit mask of bits to clear
8958c2ecf20Sopenharmony_ci * @set: bit mask of bits to set
8968c2ecf20Sopenharmony_ci *
8978c2ecf20Sopenharmony_ci * Returns negative errno, 0 if there was no change, and 1 in case of change
8988c2ecf20Sopenharmony_ci */
8998c2ecf20Sopenharmony_ciint phy_modify_paged_changed(struct phy_device *phydev, int page, u32 regnum,
9008c2ecf20Sopenharmony_ci			     u16 mask, u16 set)
9018c2ecf20Sopenharmony_ci{
9028c2ecf20Sopenharmony_ci	int ret = 0, oldpage;
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci	oldpage = phy_select_page(phydev, page);
9058c2ecf20Sopenharmony_ci	if (oldpage >= 0)
9068c2ecf20Sopenharmony_ci		ret = __phy_modify_changed(phydev, regnum, mask, set);
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ci	return phy_restore_page(phydev, oldpage, ret);
9098c2ecf20Sopenharmony_ci}
9108c2ecf20Sopenharmony_ciEXPORT_SYMBOL(phy_modify_paged_changed);
9118c2ecf20Sopenharmony_ci
9128c2ecf20Sopenharmony_ci/**
9138c2ecf20Sopenharmony_ci * phy_modify_paged() - Convenience function for modifying a paged register
9148c2ecf20Sopenharmony_ci * @phydev: a pointer to a &struct phy_device
9158c2ecf20Sopenharmony_ci * @page: the page for the phy
9168c2ecf20Sopenharmony_ci * @regnum: register number
9178c2ecf20Sopenharmony_ci * @mask: bit mask of bits to clear
9188c2ecf20Sopenharmony_ci * @set: bit mask of bits to set
9198c2ecf20Sopenharmony_ci *
9208c2ecf20Sopenharmony_ci * Same rules as for phy_read() and phy_write().
9218c2ecf20Sopenharmony_ci */
9228c2ecf20Sopenharmony_ciint phy_modify_paged(struct phy_device *phydev, int page, u32 regnum,
9238c2ecf20Sopenharmony_ci		     u16 mask, u16 set)
9248c2ecf20Sopenharmony_ci{
9258c2ecf20Sopenharmony_ci	int ret = phy_modify_paged_changed(phydev, page, regnum, mask, set);
9268c2ecf20Sopenharmony_ci
9278c2ecf20Sopenharmony_ci	return ret < 0 ? ret : 0;
9288c2ecf20Sopenharmony_ci}
9298c2ecf20Sopenharmony_ciEXPORT_SYMBOL(phy_modify_paged);
930