1a2443fd1SAndrew Lunn // SPDX-License-Identifier: GPL-2.0+
29860118bSRussell King /*
39860118bSRussell King * Core PHY library, taken from phy.c
49860118bSRussell King */
59860118bSRussell King #include <linux/export.h>
69860118bSRussell King #include <linux/phy.h>
7a4eaed9fSMaxime Chevallier #include <linux/of.h>
89860118bSRussell King
94069a572SAndrew Lunn /**
104069a572SAndrew Lunn * phy_speed_to_str - Return a string representing the PHY link speed
114069a572SAndrew Lunn *
124069a572SAndrew Lunn * @speed: Speed of the link
134069a572SAndrew Lunn */
phy_speed_to_str(int speed)14da4625acSRussell King const char *phy_speed_to_str(int speed)
15da4625acSRussell King {
1616178c8eSPiergiorgio Beruto BUILD_BUG_ON_MSG(__ETHTOOL_LINK_MODE_MASK_NBITS != 102,
17c6576bfeSHeiner Kallweit "Enum ethtool_link_mode_bit_indices and phylib are out of sync. "
18c6576bfeSHeiner Kallweit "If a speed or mode has been added please update phy_speed_to_str "
19c6576bfeSHeiner Kallweit "and the PHY settings array.\n");
20c6576bfeSHeiner Kallweit
21da4625acSRussell King switch (speed) {
22da4625acSRussell King case SPEED_10:
23da4625acSRussell King return "10Mbps";
24da4625acSRussell King case SPEED_100:
25da4625acSRussell King return "100Mbps";
26da4625acSRussell King case SPEED_1000:
27da4625acSRussell King return "1Gbps";
28da4625acSRussell King case SPEED_2500:
29da4625acSRussell King return "2.5Gbps";
30da4625acSRussell King case SPEED_5000:
31da4625acSRussell King return "5Gbps";
32da4625acSRussell King case SPEED_10000:
33da4625acSRussell King return "10Gbps";
34da4625acSRussell King case SPEED_14000:
35da4625acSRussell King return "14Gbps";
36da4625acSRussell King case SPEED_20000:
37da4625acSRussell King return "20Gbps";
38da4625acSRussell King case SPEED_25000:
39da4625acSRussell King return "25Gbps";
40da4625acSRussell King case SPEED_40000:
41da4625acSRussell King return "40Gbps";
42da4625acSRussell King case SPEED_50000:
43da4625acSRussell King return "50Gbps";
44da4625acSRussell King case SPEED_56000:
45da4625acSRussell King return "56Gbps";
46da4625acSRussell King case SPEED_100000:
47da4625acSRussell King return "100Gbps";
485a3144e4SHeiner Kallweit case SPEED_200000:
495a3144e4SHeiner Kallweit return "200Gbps";
5014af7fd1SJiri Pirko case SPEED_400000:
5114af7fd1SJiri Pirko return "400Gbps";
52404c7678SAmit Cohen case SPEED_800000:
53404c7678SAmit Cohen return "800Gbps";
54da4625acSRussell King case SPEED_UNKNOWN:
55da4625acSRussell King return "Unknown";
56da4625acSRussell King default:
57da4625acSRussell King return "Unsupported (update phy-core.c)";
58da4625acSRussell King }
59da4625acSRussell King }
60da4625acSRussell King EXPORT_SYMBOL_GPL(phy_speed_to_str);
61da4625acSRussell King
624069a572SAndrew Lunn /**
634069a572SAndrew Lunn * phy_duplex_to_str - Return string describing the duplex
644069a572SAndrew Lunn *
654069a572SAndrew Lunn * @duplex: Duplex setting to describe
664069a572SAndrew Lunn */
phy_duplex_to_str(unsigned int duplex)67da4625acSRussell King const char *phy_duplex_to_str(unsigned int duplex)
68da4625acSRussell King {
69da4625acSRussell King if (duplex == DUPLEX_HALF)
70da4625acSRussell King return "Half";
71da4625acSRussell King if (duplex == DUPLEX_FULL)
72da4625acSRussell King return "Full";
73da4625acSRussell King if (duplex == DUPLEX_UNKNOWN)
74da4625acSRussell King return "Unknown";
75da4625acSRussell King return "Unsupported (update phy-core.c)";
76da4625acSRussell King }
77da4625acSRussell King EXPORT_SYMBOL_GPL(phy_duplex_to_str);
78da4625acSRussell King
79c04ade27SMaxime Chevallier /**
800c3e10cbSSean Anderson * phy_rate_matching_to_str - Return a string describing the rate matching
810c3e10cbSSean Anderson *
820c3e10cbSSean Anderson * @rate_matching: Type of rate matching to describe
830c3e10cbSSean Anderson */
phy_rate_matching_to_str(int rate_matching)840c3e10cbSSean Anderson const char *phy_rate_matching_to_str(int rate_matching)
850c3e10cbSSean Anderson {
860c3e10cbSSean Anderson switch (rate_matching) {
870c3e10cbSSean Anderson case RATE_MATCH_NONE:
880c3e10cbSSean Anderson return "none";
890c3e10cbSSean Anderson case RATE_MATCH_PAUSE:
900c3e10cbSSean Anderson return "pause";
910c3e10cbSSean Anderson case RATE_MATCH_CRS:
920c3e10cbSSean Anderson return "crs";
930c3e10cbSSean Anderson case RATE_MATCH_OPEN_LOOP:
940c3e10cbSSean Anderson return "open-loop";
950c3e10cbSSean Anderson }
960c3e10cbSSean Anderson return "Unsupported (update phy-core.c)";
970c3e10cbSSean Anderson }
980c3e10cbSSean Anderson EXPORT_SYMBOL_GPL(phy_rate_matching_to_str);
990c3e10cbSSean Anderson
1000c3e10cbSSean Anderson /**
101c04ade27SMaxime Chevallier * phy_interface_num_ports - Return the number of links that can be carried by
102c04ade27SMaxime Chevallier * a given MAC-PHY physical link. Returns 0 if this is
103c04ade27SMaxime Chevallier * unknown, the number of links else.
104c04ade27SMaxime Chevallier *
105c04ade27SMaxime Chevallier * @interface: The interface mode we want to get the number of ports
106c04ade27SMaxime Chevallier */
phy_interface_num_ports(phy_interface_t interface)107c04ade27SMaxime Chevallier int phy_interface_num_ports(phy_interface_t interface)
108c04ade27SMaxime Chevallier {
109c04ade27SMaxime Chevallier switch (interface) {
110c04ade27SMaxime Chevallier case PHY_INTERFACE_MODE_NA:
111c04ade27SMaxime Chevallier return 0;
112c04ade27SMaxime Chevallier case PHY_INTERFACE_MODE_INTERNAL:
113c04ade27SMaxime Chevallier case PHY_INTERFACE_MODE_MII:
114c04ade27SMaxime Chevallier case PHY_INTERFACE_MODE_GMII:
115c04ade27SMaxime Chevallier case PHY_INTERFACE_MODE_TBI:
116c04ade27SMaxime Chevallier case PHY_INTERFACE_MODE_REVMII:
117c04ade27SMaxime Chevallier case PHY_INTERFACE_MODE_RMII:
118c04ade27SMaxime Chevallier case PHY_INTERFACE_MODE_REVRMII:
119c04ade27SMaxime Chevallier case PHY_INTERFACE_MODE_RGMII:
120c04ade27SMaxime Chevallier case PHY_INTERFACE_MODE_RGMII_ID:
121c04ade27SMaxime Chevallier case PHY_INTERFACE_MODE_RGMII_RXID:
122c04ade27SMaxime Chevallier case PHY_INTERFACE_MODE_RGMII_TXID:
123c04ade27SMaxime Chevallier case PHY_INTERFACE_MODE_RTBI:
124c04ade27SMaxime Chevallier case PHY_INTERFACE_MODE_XGMII:
125c04ade27SMaxime Chevallier case PHY_INTERFACE_MODE_XLGMII:
126c04ade27SMaxime Chevallier case PHY_INTERFACE_MODE_MOCA:
127c04ade27SMaxime Chevallier case PHY_INTERFACE_MODE_TRGMII:
128c04ade27SMaxime Chevallier case PHY_INTERFACE_MODE_USXGMII:
129c04ade27SMaxime Chevallier case PHY_INTERFACE_MODE_SGMII:
130c04ade27SMaxime Chevallier case PHY_INTERFACE_MODE_SMII:
131c04ade27SMaxime Chevallier case PHY_INTERFACE_MODE_1000BASEX:
132c04ade27SMaxime Chevallier case PHY_INTERFACE_MODE_2500BASEX:
133c04ade27SMaxime Chevallier case PHY_INTERFACE_MODE_5GBASER:
134c04ade27SMaxime Chevallier case PHY_INTERFACE_MODE_10GBASER:
135c04ade27SMaxime Chevallier case PHY_INTERFACE_MODE_25GBASER:
136c04ade27SMaxime Chevallier case PHY_INTERFACE_MODE_10GKR:
137c04ade27SMaxime Chevallier case PHY_INTERFACE_MODE_100BASEX:
138c04ade27SMaxime Chevallier case PHY_INTERFACE_MODE_RXAUI:
139c04ade27SMaxime Chevallier case PHY_INTERFACE_MODE_XAUI:
14005ad5d45SSean Anderson case PHY_INTERFACE_MODE_1000BASEKX:
141c04ade27SMaxime Chevallier return 1;
142c04ade27SMaxime Chevallier case PHY_INTERFACE_MODE_QSGMII:
143c04ade27SMaxime Chevallier case PHY_INTERFACE_MODE_QUSGMII:
144c04ade27SMaxime Chevallier return 4;
145*83b5f025SGabor Juhos case PHY_INTERFACE_MODE_PSGMII:
146*83b5f025SGabor Juhos return 5;
147c04ade27SMaxime Chevallier case PHY_INTERFACE_MODE_MAX:
148c04ade27SMaxime Chevallier WARN_ONCE(1, "PHY_INTERFACE_MODE_MAX isn't a valid interface mode");
149c04ade27SMaxime Chevallier return 0;
150c04ade27SMaxime Chevallier }
151c04ade27SMaxime Chevallier return 0;
152c04ade27SMaxime Chevallier }
153c04ade27SMaxime Chevallier EXPORT_SYMBOL_GPL(phy_interface_num_ports);
154c04ade27SMaxime Chevallier
1550ccb4fc6SRussell King /* A mapping of all SUPPORTED settings to speed/duplex. This table
1560ccb4fc6SRussell King * must be grouped by speed and sorted in descending match priority
1571953feb0SWenpeng Liang * - iow, descending speed.
1581953feb0SWenpeng Liang */
159f1538ecaSHeiner Kallweit
160f1538ecaSHeiner Kallweit #define PHY_SETTING(s, d, b) { .speed = SPEED_ ## s, .duplex = DUPLEX_ ## d, \
161f1538ecaSHeiner Kallweit .bit = ETHTOOL_LINK_MODE_ ## b ## _BIT}
162f1538ecaSHeiner Kallweit
1630ccb4fc6SRussell King static const struct phy_setting settings[] = {
164404c7678SAmit Cohen /* 800G */
165404c7678SAmit Cohen PHY_SETTING( 800000, FULL, 800000baseCR8_Full ),
166404c7678SAmit Cohen PHY_SETTING( 800000, FULL, 800000baseKR8_Full ),
167404c7678SAmit Cohen PHY_SETTING( 800000, FULL, 800000baseDR8_Full ),
168404c7678SAmit Cohen PHY_SETTING( 800000, FULL, 800000baseDR8_2_Full ),
169404c7678SAmit Cohen PHY_SETTING( 800000, FULL, 800000baseSR8_Full ),
170404c7678SAmit Cohen PHY_SETTING( 800000, FULL, 800000baseVR8_Full ),
17114af7fd1SJiri Pirko /* 400G */
17214af7fd1SJiri Pirko PHY_SETTING( 400000, FULL, 400000baseCR8_Full ),
17314af7fd1SJiri Pirko PHY_SETTING( 400000, FULL, 400000baseKR8_Full ),
17414af7fd1SJiri Pirko PHY_SETTING( 400000, FULL, 400000baseLR8_ER8_FR8_Full ),
17514af7fd1SJiri Pirko PHY_SETTING( 400000, FULL, 400000baseDR8_Full ),
17614af7fd1SJiri Pirko PHY_SETTING( 400000, FULL, 400000baseSR8_Full ),
177065e0d42SMeir Lichtinger PHY_SETTING( 400000, FULL, 400000baseCR4_Full ),
178065e0d42SMeir Lichtinger PHY_SETTING( 400000, FULL, 400000baseKR4_Full ),
179065e0d42SMeir Lichtinger PHY_SETTING( 400000, FULL, 400000baseLR4_ER4_FR4_Full ),
180065e0d42SMeir Lichtinger PHY_SETTING( 400000, FULL, 400000baseDR4_Full ),
181065e0d42SMeir Lichtinger PHY_SETTING( 400000, FULL, 400000baseSR4_Full ),
1825a3144e4SHeiner Kallweit /* 200G */
1835a3144e4SHeiner Kallweit PHY_SETTING( 200000, FULL, 200000baseCR4_Full ),
1845a3144e4SHeiner Kallweit PHY_SETTING( 200000, FULL, 200000baseKR4_Full ),
1855a3144e4SHeiner Kallweit PHY_SETTING( 200000, FULL, 200000baseLR4_ER4_FR4_Full ),
1865a3144e4SHeiner Kallweit PHY_SETTING( 200000, FULL, 200000baseDR4_Full ),
1875a3144e4SHeiner Kallweit PHY_SETTING( 200000, FULL, 200000baseSR4_Full ),
188065e0d42SMeir Lichtinger PHY_SETTING( 200000, FULL, 200000baseCR2_Full ),
189065e0d42SMeir Lichtinger PHY_SETTING( 200000, FULL, 200000baseKR2_Full ),
190065e0d42SMeir Lichtinger PHY_SETTING( 200000, FULL, 200000baseLR2_ER2_FR2_Full ),
191065e0d42SMeir Lichtinger PHY_SETTING( 200000, FULL, 200000baseDR2_Full ),
192065e0d42SMeir Lichtinger PHY_SETTING( 200000, FULL, 200000baseSR2_Full ),
1933c6b59d6SAndrew Lunn /* 100G */
194f1538ecaSHeiner Kallweit PHY_SETTING( 100000, FULL, 100000baseCR4_Full ),
195f1538ecaSHeiner Kallweit PHY_SETTING( 100000, FULL, 100000baseKR4_Full ),
196f1538ecaSHeiner Kallweit PHY_SETTING( 100000, FULL, 100000baseLR4_ER4_Full ),
197f1538ecaSHeiner Kallweit PHY_SETTING( 100000, FULL, 100000baseSR4_Full ),
1985a3144e4SHeiner Kallweit PHY_SETTING( 100000, FULL, 100000baseCR2_Full ),
1995a3144e4SHeiner Kallweit PHY_SETTING( 100000, FULL, 100000baseKR2_Full ),
2005a3144e4SHeiner Kallweit PHY_SETTING( 100000, FULL, 100000baseLR2_ER2_FR2_Full ),
2015a3144e4SHeiner Kallweit PHY_SETTING( 100000, FULL, 100000baseDR2_Full ),
2025a3144e4SHeiner Kallweit PHY_SETTING( 100000, FULL, 100000baseSR2_Full ),
203065e0d42SMeir Lichtinger PHY_SETTING( 100000, FULL, 100000baseCR_Full ),
204065e0d42SMeir Lichtinger PHY_SETTING( 100000, FULL, 100000baseKR_Full ),
205065e0d42SMeir Lichtinger PHY_SETTING( 100000, FULL, 100000baseLR_ER_FR_Full ),
206065e0d42SMeir Lichtinger PHY_SETTING( 100000, FULL, 100000baseDR_Full ),
207065e0d42SMeir Lichtinger PHY_SETTING( 100000, FULL, 100000baseSR_Full ),
2083c6b59d6SAndrew Lunn /* 56G */
209f1538ecaSHeiner Kallweit PHY_SETTING( 56000, FULL, 56000baseCR4_Full ),
210f1538ecaSHeiner Kallweit PHY_SETTING( 56000, FULL, 56000baseKR4_Full ),
211f1538ecaSHeiner Kallweit PHY_SETTING( 56000, FULL, 56000baseLR4_Full ),
212f1538ecaSHeiner Kallweit PHY_SETTING( 56000, FULL, 56000baseSR4_Full ),
2133c6b59d6SAndrew Lunn /* 50G */
214f1538ecaSHeiner Kallweit PHY_SETTING( 50000, FULL, 50000baseCR2_Full ),
215f1538ecaSHeiner Kallweit PHY_SETTING( 50000, FULL, 50000baseKR2_Full ),
216f1538ecaSHeiner Kallweit PHY_SETTING( 50000, FULL, 50000baseSR2_Full ),
2175a3144e4SHeiner Kallweit PHY_SETTING( 50000, FULL, 50000baseCR_Full ),
2185a3144e4SHeiner Kallweit PHY_SETTING( 50000, FULL, 50000baseKR_Full ),
2195a3144e4SHeiner Kallweit PHY_SETTING( 50000, FULL, 50000baseLR_ER_FR_Full ),
2205a3144e4SHeiner Kallweit PHY_SETTING( 50000, FULL, 50000baseDR_Full ),
2215a3144e4SHeiner Kallweit PHY_SETTING( 50000, FULL, 50000baseSR_Full ),
2223c6b59d6SAndrew Lunn /* 40G */
223f1538ecaSHeiner Kallweit PHY_SETTING( 40000, FULL, 40000baseCR4_Full ),
224f1538ecaSHeiner Kallweit PHY_SETTING( 40000, FULL, 40000baseKR4_Full ),
225f1538ecaSHeiner Kallweit PHY_SETTING( 40000, FULL, 40000baseLR4_Full ),
226f1538ecaSHeiner Kallweit PHY_SETTING( 40000, FULL, 40000baseSR4_Full ),
2273c6b59d6SAndrew Lunn /* 25G */
228f1538ecaSHeiner Kallweit PHY_SETTING( 25000, FULL, 25000baseCR_Full ),
229f1538ecaSHeiner Kallweit PHY_SETTING( 25000, FULL, 25000baseKR_Full ),
230f1538ecaSHeiner Kallweit PHY_SETTING( 25000, FULL, 25000baseSR_Full ),
2313c6b59d6SAndrew Lunn /* 20G */
232f1538ecaSHeiner Kallweit PHY_SETTING( 20000, FULL, 20000baseKR2_Full ),
233f1538ecaSHeiner Kallweit PHY_SETTING( 20000, FULL, 20000baseMLD2_Full ),
2343c6b59d6SAndrew Lunn /* 10G */
235f1538ecaSHeiner Kallweit PHY_SETTING( 10000, FULL, 10000baseCR_Full ),
236f1538ecaSHeiner Kallweit PHY_SETTING( 10000, FULL, 10000baseER_Full ),
237f1538ecaSHeiner Kallweit PHY_SETTING( 10000, FULL, 10000baseKR_Full ),
238f1538ecaSHeiner Kallweit PHY_SETTING( 10000, FULL, 10000baseKX4_Full ),
239f1538ecaSHeiner Kallweit PHY_SETTING( 10000, FULL, 10000baseLR_Full ),
240f1538ecaSHeiner Kallweit PHY_SETTING( 10000, FULL, 10000baseLRM_Full ),
241f1538ecaSHeiner Kallweit PHY_SETTING( 10000, FULL, 10000baseR_FEC ),
242f1538ecaSHeiner Kallweit PHY_SETTING( 10000, FULL, 10000baseSR_Full ),
243f1538ecaSHeiner Kallweit PHY_SETTING( 10000, FULL, 10000baseT_Full ),
2443c6b59d6SAndrew Lunn /* 5G */
245f1538ecaSHeiner Kallweit PHY_SETTING( 5000, FULL, 5000baseT_Full ),
2463c6b59d6SAndrew Lunn /* 2.5G */
247f1538ecaSHeiner Kallweit PHY_SETTING( 2500, FULL, 2500baseT_Full ),
248f1538ecaSHeiner Kallweit PHY_SETTING( 2500, FULL, 2500baseX_Full ),
2493c6b59d6SAndrew Lunn /* 1G */
250f1538ecaSHeiner Kallweit PHY_SETTING( 1000, FULL, 1000baseT_Full ),
251f1538ecaSHeiner Kallweit PHY_SETTING( 1000, HALF, 1000baseT_Half ),
252b2557764SAndrew Lunn PHY_SETTING( 1000, FULL, 1000baseT1_Full ),
253f1538ecaSHeiner Kallweit PHY_SETTING( 1000, FULL, 1000baseX_Full ),
254f20f94f7SRussell King (Oracle) PHY_SETTING( 1000, FULL, 1000baseKX_Full ),
2553c6b59d6SAndrew Lunn /* 100M */
256f1538ecaSHeiner Kallweit PHY_SETTING( 100, FULL, 100baseT_Full ),
257b2557764SAndrew Lunn PHY_SETTING( 100, FULL, 100baseT1_Full ),
258f1538ecaSHeiner Kallweit PHY_SETTING( 100, HALF, 100baseT_Half ),
25955f13311SDan Murphy PHY_SETTING( 100, HALF, 100baseFX_Half ),
26055f13311SDan Murphy PHY_SETTING( 100, FULL, 100baseFX_Full ),
2613c6b59d6SAndrew Lunn /* 10M */
262f1538ecaSHeiner Kallweit PHY_SETTING( 10, FULL, 10baseT_Full ),
263f1538ecaSHeiner Kallweit PHY_SETTING( 10, HALF, 10baseT_Half ),
2643254e0b9SAlexandru Tachici PHY_SETTING( 10, FULL, 10baseT1L_Full ),
26516178c8eSPiergiorgio Beruto PHY_SETTING( 10, FULL, 10baseT1S_Full ),
26616178c8eSPiergiorgio Beruto PHY_SETTING( 10, HALF, 10baseT1S_Half ),
26716178c8eSPiergiorgio Beruto PHY_SETTING( 10, HALF, 10baseT1S_P2MP_Half ),
2680ccb4fc6SRussell King };
269f1538ecaSHeiner Kallweit #undef PHY_SETTING
2700ccb4fc6SRussell King
2710ccb4fc6SRussell King /**
2720ccb4fc6SRussell King * phy_lookup_setting - lookup a PHY setting
2730ccb4fc6SRussell King * @speed: speed to match
2740ccb4fc6SRussell King * @duplex: duplex to match
2750ccb4fc6SRussell King * @mask: allowed link modes
2760ccb4fc6SRussell King * @exact: an exact match is required
2770ccb4fc6SRussell King *
2780ccb4fc6SRussell King * Search the settings array for a setting that matches the speed and
2790ccb4fc6SRussell King * duplex, and which is supported.
2800ccb4fc6SRussell King *
2810ccb4fc6SRussell King * If @exact is unset, either an exact match or %NULL for no match will
2820ccb4fc6SRussell King * be returned.
2830ccb4fc6SRussell King *
2840ccb4fc6SRussell King * If @exact is set, an exact match, the fastest supported setting at
2850ccb4fc6SRussell King * or below the specified speed, the slowest supported setting, or if
2860ccb4fc6SRussell King * they all fail, %NULL will be returned.
2870ccb4fc6SRussell King */
2880ccb4fc6SRussell King const struct phy_setting *
phy_lookup_setting(int speed,int duplex,const unsigned long * mask,bool exact)2893c1bcc86SAndrew Lunn phy_lookup_setting(int speed, int duplex, const unsigned long *mask, bool exact)
2900ccb4fc6SRussell King {
2910ccb4fc6SRussell King const struct phy_setting *p, *match = NULL, *last = NULL;
2920ccb4fc6SRussell King int i;
2930ccb4fc6SRussell King
2940ccb4fc6SRussell King for (i = 0, p = settings; i < ARRAY_SIZE(settings); i++, p++) {
2953c1bcc86SAndrew Lunn if (p->bit < __ETHTOOL_LINK_MODE_MASK_NBITS &&
2963c1bcc86SAndrew Lunn test_bit(p->bit, mask)) {
2970ccb4fc6SRussell King last = p;
2980ccb4fc6SRussell King if (p->speed == speed && p->duplex == duplex) {
2990ccb4fc6SRussell King /* Exact match for speed and duplex */
3000ccb4fc6SRussell King match = p;
3010ccb4fc6SRussell King break;
3020ccb4fc6SRussell King } else if (!exact) {
3030ccb4fc6SRussell King if (!match && p->speed <= speed)
3040ccb4fc6SRussell King /* Candidate */
3050ccb4fc6SRussell King match = p;
3060ccb4fc6SRussell King
3070ccb4fc6SRussell King if (p->speed < speed)
3080ccb4fc6SRussell King break;
3090ccb4fc6SRussell King }
3100ccb4fc6SRussell King }
3110ccb4fc6SRussell King }
3120ccb4fc6SRussell King
3130ccb4fc6SRussell King if (!match && !exact)
3140ccb4fc6SRussell King match = last;
3150ccb4fc6SRussell King
3160ccb4fc6SRussell King return match;
3170ccb4fc6SRussell King }
3180ccb4fc6SRussell King EXPORT_SYMBOL_GPL(phy_lookup_setting);
3190ccb4fc6SRussell King
phy_speeds(unsigned int * speeds,size_t size,unsigned long * mask)3200ccb4fc6SRussell King size_t phy_speeds(unsigned int *speeds, size_t size,
3213c1bcc86SAndrew Lunn unsigned long *mask)
3220ccb4fc6SRussell King {
3230ccb4fc6SRussell King size_t count;
3240ccb4fc6SRussell King int i;
3250ccb4fc6SRussell King
3260ccb4fc6SRussell King for (i = 0, count = 0; i < ARRAY_SIZE(settings) && count < size; i++)
3273c1bcc86SAndrew Lunn if (settings[i].bit < __ETHTOOL_LINK_MODE_MASK_NBITS &&
3280ccb4fc6SRussell King test_bit(settings[i].bit, mask) &&
3290ccb4fc6SRussell King (count == 0 || speeds[count - 1] != settings[i].speed))
3300ccb4fc6SRussell King speeds[count++] = settings[i].speed;
3310ccb4fc6SRussell King
3320ccb4fc6SRussell King return count;
3330ccb4fc6SRussell King }
3340ccb4fc6SRussell King
__set_linkmode_max_speed(u32 max_speed,unsigned long * addr)33573c105adSSergey Shtylyov static void __set_linkmode_max_speed(u32 max_speed, unsigned long *addr)
336a4eaed9fSMaxime Chevallier {
337a4eaed9fSMaxime Chevallier const struct phy_setting *p;
338a4eaed9fSMaxime Chevallier int i;
339a4eaed9fSMaxime Chevallier
340a4eaed9fSMaxime Chevallier for (i = 0, p = settings; i < ARRAY_SIZE(settings); i++, p++) {
341a4eaed9fSMaxime Chevallier if (p->speed > max_speed)
3427b261e0eSHeiner Kallweit linkmode_clear_bit(p->bit, addr);
343a4eaed9fSMaxime Chevallier else
344a4eaed9fSMaxime Chevallier break;
345a4eaed9fSMaxime Chevallier }
346a4eaed9fSMaxime Chevallier }
347a4eaed9fSMaxime Chevallier
__set_phy_supported(struct phy_device * phydev,u32 max_speed)34873c105adSSergey Shtylyov static void __set_phy_supported(struct phy_device *phydev, u32 max_speed)
3497b261e0eSHeiner Kallweit {
35073c105adSSergey Shtylyov __set_linkmode_max_speed(max_speed, phydev->supported);
3517b261e0eSHeiner Kallweit }
3527b261e0eSHeiner Kallweit
3534069a572SAndrew Lunn /**
3544069a572SAndrew Lunn * phy_set_max_speed - Set the maximum speed the PHY should support
3554069a572SAndrew Lunn *
3564069a572SAndrew Lunn * @phydev: The phy_device struct
3574069a572SAndrew Lunn * @max_speed: Maximum speed
3584069a572SAndrew Lunn *
3594069a572SAndrew Lunn * The PHY might be more capable than the MAC. For example a Fast Ethernet
3604069a572SAndrew Lunn * is connected to a 1G PHY. This function allows the MAC to indicate its
3614069a572SAndrew Lunn * maximum speed, and so limit what the PHY will advertise.
3624069a572SAndrew Lunn */
phy_set_max_speed(struct phy_device * phydev,u32 max_speed)36373c105adSSergey Shtylyov void phy_set_max_speed(struct phy_device *phydev, u32 max_speed)
364a4eaed9fSMaxime Chevallier {
36573c105adSSergey Shtylyov __set_phy_supported(phydev, max_speed);
366a4eaed9fSMaxime Chevallier
36722c0ef6bSHeiner Kallweit phy_advertise_supported(phydev);
368a4eaed9fSMaxime Chevallier }
369a4eaed9fSMaxime Chevallier EXPORT_SYMBOL(phy_set_max_speed);
370a4eaed9fSMaxime Chevallier
of_set_phy_supported(struct phy_device * phydev)371a4eaed9fSMaxime Chevallier void of_set_phy_supported(struct phy_device *phydev)
372a4eaed9fSMaxime Chevallier {
373a4eaed9fSMaxime Chevallier struct device_node *node = phydev->mdio.dev.of_node;
374a4eaed9fSMaxime Chevallier u32 max_speed;
375a4eaed9fSMaxime Chevallier
376a4eaed9fSMaxime Chevallier if (!IS_ENABLED(CONFIG_OF_MDIO))
377a4eaed9fSMaxime Chevallier return;
378a4eaed9fSMaxime Chevallier
379a4eaed9fSMaxime Chevallier if (!node)
380a4eaed9fSMaxime Chevallier return;
381a4eaed9fSMaxime Chevallier
382a4eaed9fSMaxime Chevallier if (!of_property_read_u32(node, "max-speed", &max_speed))
383a4eaed9fSMaxime Chevallier __set_phy_supported(phydev, max_speed);
384a4eaed9fSMaxime Chevallier }
385a4eaed9fSMaxime Chevallier
of_set_phy_eee_broken(struct phy_device * phydev)3863feb9b23SMaxime Chevallier void of_set_phy_eee_broken(struct phy_device *phydev)
3873feb9b23SMaxime Chevallier {
3883feb9b23SMaxime Chevallier struct device_node *node = phydev->mdio.dev.of_node;
3893feb9b23SMaxime Chevallier u32 broken = 0;
3903feb9b23SMaxime Chevallier
3913feb9b23SMaxime Chevallier if (!IS_ENABLED(CONFIG_OF_MDIO))
3923feb9b23SMaxime Chevallier return;
3933feb9b23SMaxime Chevallier
3943feb9b23SMaxime Chevallier if (!node)
3953feb9b23SMaxime Chevallier return;
3963feb9b23SMaxime Chevallier
3973feb9b23SMaxime Chevallier if (of_property_read_bool(node, "eee-broken-100tx"))
3983feb9b23SMaxime Chevallier broken |= MDIO_EEE_100TX;
3993feb9b23SMaxime Chevallier if (of_property_read_bool(node, "eee-broken-1000t"))
4003feb9b23SMaxime Chevallier broken |= MDIO_EEE_1000T;
4013feb9b23SMaxime Chevallier if (of_property_read_bool(node, "eee-broken-10gt"))
4023feb9b23SMaxime Chevallier broken |= MDIO_EEE_10GT;
4033feb9b23SMaxime Chevallier if (of_property_read_bool(node, "eee-broken-1000kx"))
4043feb9b23SMaxime Chevallier broken |= MDIO_EEE_1000KX;
4053feb9b23SMaxime Chevallier if (of_property_read_bool(node, "eee-broken-10gkx4"))
4063feb9b23SMaxime Chevallier broken |= MDIO_EEE_10GKX4;
4073feb9b23SMaxime Chevallier if (of_property_read_bool(node, "eee-broken-10gkr"))
4083feb9b23SMaxime Chevallier broken |= MDIO_EEE_10GKR;
4093feb9b23SMaxime Chevallier
4103feb9b23SMaxime Chevallier phydev->eee_broken_modes = broken;
4113feb9b23SMaxime Chevallier }
4123feb9b23SMaxime Chevallier
4134069a572SAndrew Lunn /**
4144069a572SAndrew Lunn * phy_resolve_aneg_pause - Determine pause autoneg results
4154069a572SAndrew Lunn *
4164069a572SAndrew Lunn * @phydev: The phy_device struct
4174069a572SAndrew Lunn *
4184069a572SAndrew Lunn * Once autoneg has completed the local pause settings can be
4194069a572SAndrew Lunn * resolved. Determine if pause and asymmetric pause should be used
4204069a572SAndrew Lunn * by the MAC.
4214069a572SAndrew Lunn */
4224069a572SAndrew Lunn
phy_resolve_aneg_pause(struct phy_device * phydev)4232d880b87SRussell King void phy_resolve_aneg_pause(struct phy_device *phydev)
4242d880b87SRussell King {
4252d880b87SRussell King if (phydev->duplex == DUPLEX_FULL) {
4262d880b87SRussell King phydev->pause = linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT,
4272d880b87SRussell King phydev->lp_advertising);
4282d880b87SRussell King phydev->asym_pause = linkmode_test_bit(
4292d880b87SRussell King ETHTOOL_LINK_MODE_Asym_Pause_BIT,
4302d880b87SRussell King phydev->lp_advertising);
4312d880b87SRussell King }
4322d880b87SRussell King }
4332d880b87SRussell King EXPORT_SYMBOL_GPL(phy_resolve_aneg_pause);
4342d880b87SRussell King
4358c5e850cSRussell King /**
4364069a572SAndrew Lunn * phy_resolve_aneg_linkmode - resolve the advertisements into PHY settings
4378c5e850cSRussell King * @phydev: The phy_device struct
4388c5e850cSRussell King *
439cc1122b0SColin Ian King * Resolve our and the link partner advertisements into their corresponding
4408c5e850cSRussell King * speed and duplex. If full duplex was negotiated, extract the pause mode
4418c5e850cSRussell King * from the link partner mask.
4428c5e850cSRussell King */
phy_resolve_aneg_linkmode(struct phy_device * phydev)4438c5e850cSRussell King void phy_resolve_aneg_linkmode(struct phy_device *phydev)
4448c5e850cSRussell King {
4453c1bcc86SAndrew Lunn __ETHTOOL_DECLARE_LINK_MODE_MASK(common);
446a2703de7SHeiner Kallweit int i;
4478c5e850cSRussell King
448c0ec3c27SAndrew Lunn linkmode_and(common, phydev->lp_advertising, phydev->advertising);
4493c1bcc86SAndrew Lunn
450a2703de7SHeiner Kallweit for (i = 0; i < ARRAY_SIZE(settings); i++)
451a2703de7SHeiner Kallweit if (test_bit(settings[i].bit, common)) {
452a2703de7SHeiner Kallweit phydev->speed = settings[i].speed;
453a2703de7SHeiner Kallweit phydev->duplex = settings[i].duplex;
454a2703de7SHeiner Kallweit break;
4558c5e850cSRussell King }
4568c5e850cSRussell King
4572d880b87SRussell King phy_resolve_aneg_pause(phydev);
4588c5e850cSRussell King }
4598c5e850cSRussell King EXPORT_SYMBOL_GPL(phy_resolve_aneg_linkmode);
4608c5e850cSRussell King
4615eee3bb7SHeiner Kallweit /**
4625eee3bb7SHeiner Kallweit * phy_check_downshift - check whether downshift occurred
4635eee3bb7SHeiner Kallweit * @phydev: The phy_device struct
4645eee3bb7SHeiner Kallweit *
4655eee3bb7SHeiner Kallweit * Check whether a downshift to a lower speed occurred. If this should be the
4665eee3bb7SHeiner Kallweit * case warn the user.
4675eee3bb7SHeiner Kallweit * Prerequisite for detecting downshift is that PHY driver implements the
4685eee3bb7SHeiner Kallweit * read_status callback and sets phydev->speed to the actual link speed.
4695eee3bb7SHeiner Kallweit */
phy_check_downshift(struct phy_device * phydev)4705eee3bb7SHeiner Kallweit void phy_check_downshift(struct phy_device *phydev)
4715eee3bb7SHeiner Kallweit {
4725eee3bb7SHeiner Kallweit __ETHTOOL_DECLARE_LINK_MODE_MASK(common);
4735eee3bb7SHeiner Kallweit int i, speed = SPEED_UNKNOWN;
4745eee3bb7SHeiner Kallweit
4755eee3bb7SHeiner Kallweit phydev->downshifted_rate = 0;
4765eee3bb7SHeiner Kallweit
4775eee3bb7SHeiner Kallweit if (phydev->autoneg == AUTONEG_DISABLE ||
4785eee3bb7SHeiner Kallweit phydev->speed == SPEED_UNKNOWN)
4795eee3bb7SHeiner Kallweit return;
4805eee3bb7SHeiner Kallweit
4815eee3bb7SHeiner Kallweit linkmode_and(common, phydev->lp_advertising, phydev->advertising);
4825eee3bb7SHeiner Kallweit
4835eee3bb7SHeiner Kallweit for (i = 0; i < ARRAY_SIZE(settings); i++)
4845eee3bb7SHeiner Kallweit if (test_bit(settings[i].bit, common)) {
4855eee3bb7SHeiner Kallweit speed = settings[i].speed;
4865eee3bb7SHeiner Kallweit break;
4875eee3bb7SHeiner Kallweit }
4885eee3bb7SHeiner Kallweit
4895eee3bb7SHeiner Kallweit if (speed == SPEED_UNKNOWN || phydev->speed >= speed)
4905eee3bb7SHeiner Kallweit return;
4915eee3bb7SHeiner Kallweit
4925eee3bb7SHeiner Kallweit phydev_warn(phydev, "Downshift occurred from negotiated speed %s to actual speed %s, check cabling!\n",
4935eee3bb7SHeiner Kallweit phy_speed_to_str(speed), phy_speed_to_str(phydev->speed));
4945eee3bb7SHeiner Kallweit
4955eee3bb7SHeiner Kallweit phydev->downshifted_rate = 1;
4965eee3bb7SHeiner Kallweit }
4975eee3bb7SHeiner Kallweit EXPORT_SYMBOL_GPL(phy_check_downshift);
4985eee3bb7SHeiner Kallweit
phy_resolve_min_speed(struct phy_device * phydev,bool fdx_only)499331c56acSHeiner Kallweit static int phy_resolve_min_speed(struct phy_device *phydev, bool fdx_only)
500331c56acSHeiner Kallweit {
501331c56acSHeiner Kallweit __ETHTOOL_DECLARE_LINK_MODE_MASK(common);
502331c56acSHeiner Kallweit int i = ARRAY_SIZE(settings);
503331c56acSHeiner Kallweit
504331c56acSHeiner Kallweit linkmode_and(common, phydev->lp_advertising, phydev->advertising);
505331c56acSHeiner Kallweit
506331c56acSHeiner Kallweit while (--i >= 0) {
507331c56acSHeiner Kallweit if (test_bit(settings[i].bit, common)) {
508331c56acSHeiner Kallweit if (fdx_only && settings[i].duplex != DUPLEX_FULL)
509331c56acSHeiner Kallweit continue;
510331c56acSHeiner Kallweit return settings[i].speed;
511331c56acSHeiner Kallweit }
512331c56acSHeiner Kallweit }
513331c56acSHeiner Kallweit
514331c56acSHeiner Kallweit return SPEED_UNKNOWN;
515331c56acSHeiner Kallweit }
516331c56acSHeiner Kallweit
phy_speed_down_core(struct phy_device * phydev)517331c56acSHeiner Kallweit int phy_speed_down_core(struct phy_device *phydev)
518331c56acSHeiner Kallweit {
519331c56acSHeiner Kallweit int min_common_speed = phy_resolve_min_speed(phydev, true);
520331c56acSHeiner Kallweit
521331c56acSHeiner Kallweit if (min_common_speed == SPEED_UNKNOWN)
522331c56acSHeiner Kallweit return -EINVAL;
523331c56acSHeiner Kallweit
52473c105adSSergey Shtylyov __set_linkmode_max_speed(min_common_speed, phydev->advertising);
52573c105adSSergey Shtylyov
52673c105adSSergey Shtylyov return 0;
527331c56acSHeiner Kallweit }
528331c56acSHeiner Kallweit
mmd_phy_indirect(struct mii_bus * bus,int phy_addr,int devad,u16 regnum)529060fbc89SRussell King static void mmd_phy_indirect(struct mii_bus *bus, int phy_addr, int devad,
530060fbc89SRussell King u16 regnum)
5319860118bSRussell King {
5329860118bSRussell King /* Write the desired MMD Devad */
5331b2dea2eSRussell King __mdiobus_write(bus, phy_addr, MII_MMD_CTRL, devad);
5349860118bSRussell King
5359860118bSRussell King /* Write the desired MMD register address */
5361b2dea2eSRussell King __mdiobus_write(bus, phy_addr, MII_MMD_DATA, regnum);
5379860118bSRussell King
5389860118bSRussell King /* Select the Function : DATA with no post increment */
5391b2dea2eSRussell King __mdiobus_write(bus, phy_addr, MII_MMD_CTRL,
5401b2dea2eSRussell King devad | MII_MMD_CTRL_NOINCR);
5419860118bSRussell King }
5429860118bSRussell King
5439860118bSRussell King /**
5441878f0dcSNikita Yushchenko * __phy_read_mmd - Convenience function for reading a register
5459860118bSRussell King * from an MMD on a given PHY.
5469860118bSRussell King * @phydev: The phy_device struct
5473b85d8dfSRussell King * @devad: The MMD to read from (0..31)
5483b85d8dfSRussell King * @regnum: The register on the MMD to read (0..65535)
5499860118bSRussell King *
5501878f0dcSNikita Yushchenko * Same rules as for __phy_read();
5519860118bSRussell King */
__phy_read_mmd(struct phy_device * phydev,int devad,u32 regnum)5521878f0dcSNikita Yushchenko int __phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum)
5539860118bSRussell King {
5543b85d8dfSRussell King int val;
5553b85d8dfSRussell King
5561ee6b9bcSRussell King if (regnum > (u16)~0 || devad > 32)
5571ee6b9bcSRussell King return -EINVAL;
5589860118bSRussell King
5593e41d04eSAlex Marginean if (phydev->drv && phydev->drv->read_mmd) {
5603b85d8dfSRussell King val = phydev->drv->read_mmd(phydev, devad, regnum);
5613b85d8dfSRussell King } else if (phydev->is_c45) {
56290ce665cSRussell King val = __mdiobus_c45_read(phydev->mdio.bus, phydev->mdio.addr,
56390ce665cSRussell King devad, regnum);
5643b85d8dfSRussell King } else {
5659860118bSRussell King struct mii_bus *bus = phydev->mdio.bus;
5663b85d8dfSRussell King int phy_addr = phydev->mdio.addr;
5679860118bSRussell King
568060fbc89SRussell King mmd_phy_indirect(bus, phy_addr, devad, regnum);
5699860118bSRussell King
5703b85d8dfSRussell King /* Read the content of the MMD's selected register */
5711b2dea2eSRussell King val = __mdiobus_read(bus, phy_addr, MII_MMD_DATA);
5729860118bSRussell King }
5733b85d8dfSRussell King return val;
5749860118bSRussell King }
5751878f0dcSNikita Yushchenko EXPORT_SYMBOL(__phy_read_mmd);
5761878f0dcSNikita Yushchenko
5771878f0dcSNikita Yushchenko /**
5781878f0dcSNikita Yushchenko * phy_read_mmd - Convenience function for reading a register
5791878f0dcSNikita Yushchenko * from an MMD on a given PHY.
5801878f0dcSNikita Yushchenko * @phydev: The phy_device struct
5811878f0dcSNikita Yushchenko * @devad: The MMD to read from
5821878f0dcSNikita Yushchenko * @regnum: The register on the MMD to read
5831878f0dcSNikita Yushchenko *
5841878f0dcSNikita Yushchenko * Same rules as for phy_read();
5851878f0dcSNikita Yushchenko */
phy_read_mmd(struct phy_device * phydev,int devad,u32 regnum)5861878f0dcSNikita Yushchenko int phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum)
5871878f0dcSNikita Yushchenko {
5881878f0dcSNikita Yushchenko int ret;
5891878f0dcSNikita Yushchenko
590bec170e5SHeiner Kallweit phy_lock_mdio_bus(phydev);
5911878f0dcSNikita Yushchenko ret = __phy_read_mmd(phydev, devad, regnum);
592bec170e5SHeiner Kallweit phy_unlock_mdio_bus(phydev);
5931878f0dcSNikita Yushchenko
5941878f0dcSNikita Yushchenko return ret;
5951878f0dcSNikita Yushchenko }
5963b85d8dfSRussell King EXPORT_SYMBOL(phy_read_mmd);
5979860118bSRussell King
5989860118bSRussell King /**
5991878f0dcSNikita Yushchenko * __phy_write_mmd - Convenience function for writing a register
6001878f0dcSNikita Yushchenko * on an MMD on a given PHY.
6011878f0dcSNikita Yushchenko * @phydev: The phy_device struct
6021878f0dcSNikita Yushchenko * @devad: The MMD to read from
6031878f0dcSNikita Yushchenko * @regnum: The register on the MMD to read
6041878f0dcSNikita Yushchenko * @val: value to write to @regnum
6051878f0dcSNikita Yushchenko *
6061878f0dcSNikita Yushchenko * Same rules as for __phy_write();
6071878f0dcSNikita Yushchenko */
__phy_write_mmd(struct phy_device * phydev,int devad,u32 regnum,u16 val)6081878f0dcSNikita Yushchenko int __phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val)
6091878f0dcSNikita Yushchenko {
6101878f0dcSNikita Yushchenko int ret;
6111878f0dcSNikita Yushchenko
6121878f0dcSNikita Yushchenko if (regnum > (u16)~0 || devad > 32)
6131878f0dcSNikita Yushchenko return -EINVAL;
6141878f0dcSNikita Yushchenko
6153e41d04eSAlex Marginean if (phydev->drv && phydev->drv->write_mmd) {
6161878f0dcSNikita Yushchenko ret = phydev->drv->write_mmd(phydev, devad, regnum, val);
6171878f0dcSNikita Yushchenko } else if (phydev->is_c45) {
61890ce665cSRussell King ret = __mdiobus_c45_write(phydev->mdio.bus, phydev->mdio.addr,
61990ce665cSRussell King devad, regnum, val);
6201878f0dcSNikita Yushchenko } else {
6211878f0dcSNikita Yushchenko struct mii_bus *bus = phydev->mdio.bus;
6221878f0dcSNikita Yushchenko int phy_addr = phydev->mdio.addr;
6231878f0dcSNikita Yushchenko
6241878f0dcSNikita Yushchenko mmd_phy_indirect(bus, phy_addr, devad, regnum);
6251878f0dcSNikita Yushchenko
6261878f0dcSNikita Yushchenko /* Write the data into MMD's selected register */
6271878f0dcSNikita Yushchenko __mdiobus_write(bus, phy_addr, MII_MMD_DATA, val);
6281878f0dcSNikita Yushchenko
6291878f0dcSNikita Yushchenko ret = 0;
6301878f0dcSNikita Yushchenko }
6311878f0dcSNikita Yushchenko return ret;
6321878f0dcSNikita Yushchenko }
6331878f0dcSNikita Yushchenko EXPORT_SYMBOL(__phy_write_mmd);
6341878f0dcSNikita Yushchenko
6351878f0dcSNikita Yushchenko /**
6369860118bSRussell King * phy_write_mmd - Convenience function for writing a register
6379860118bSRussell King * on an MMD on a given PHY.
6389860118bSRussell King * @phydev: The phy_device struct
6399860118bSRussell King * @devad: The MMD to read from
6409860118bSRussell King * @regnum: The register on the MMD to read
6419860118bSRussell King * @val: value to write to @regnum
6429860118bSRussell King *
6439860118bSRussell King * Same rules as for phy_write();
6449860118bSRussell King */
phy_write_mmd(struct phy_device * phydev,int devad,u32 regnum,u16 val)6459860118bSRussell King int phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val)
6469860118bSRussell King {
6473b85d8dfSRussell King int ret;
6483b85d8dfSRussell King
649bec170e5SHeiner Kallweit phy_lock_mdio_bus(phydev);
6501878f0dcSNikita Yushchenko ret = __phy_write_mmd(phydev, devad, regnum, val);
651bec170e5SHeiner Kallweit phy_unlock_mdio_bus(phydev);
6529860118bSRussell King
6533b85d8dfSRussell King return ret;
6549860118bSRussell King }
6559860118bSRussell King EXPORT_SYMBOL(phy_write_mmd);
656788f9933SRussell King
657788f9933SRussell King /**
658b8554d4fSHeiner Kallweit * phy_modify_changed - Function for modifying a PHY register
659b8554d4fSHeiner Kallweit * @phydev: the phy_device struct
660b8554d4fSHeiner Kallweit * @regnum: register number to modify
661b8554d4fSHeiner Kallweit * @mask: bit mask of bits to clear
662b8554d4fSHeiner Kallweit * @set: new value of bits set in mask to write to @regnum
663b8554d4fSHeiner Kallweit *
664b8554d4fSHeiner Kallweit * NOTE: MUST NOT be called from interrupt context,
665b8554d4fSHeiner Kallweit * because the bus read/write functions may wait for an interrupt
666b8554d4fSHeiner Kallweit * to conclude the operation.
667b8554d4fSHeiner Kallweit *
668b8554d4fSHeiner Kallweit * Returns negative errno, 0 if there was no change, and 1 in case of change
669b8554d4fSHeiner Kallweit */
phy_modify_changed(struct phy_device * phydev,u32 regnum,u16 mask,u16 set)670b8554d4fSHeiner Kallweit int phy_modify_changed(struct phy_device *phydev, u32 regnum, u16 mask, u16 set)
671b8554d4fSHeiner Kallweit {
672b8554d4fSHeiner Kallweit int ret;
673b8554d4fSHeiner Kallweit
674bec170e5SHeiner Kallweit phy_lock_mdio_bus(phydev);
675b8554d4fSHeiner Kallweit ret = __phy_modify_changed(phydev, regnum, mask, set);
676bec170e5SHeiner Kallweit phy_unlock_mdio_bus(phydev);
677b8554d4fSHeiner Kallweit
678b8554d4fSHeiner Kallweit return ret;
679b8554d4fSHeiner Kallweit }
680b8554d4fSHeiner Kallweit EXPORT_SYMBOL_GPL(phy_modify_changed);
681b8554d4fSHeiner Kallweit
682b8554d4fSHeiner Kallweit /**
683b8554d4fSHeiner Kallweit * __phy_modify - Convenience function for modifying a PHY register
684b8554d4fSHeiner Kallweit * @phydev: the phy_device struct
685b8554d4fSHeiner Kallweit * @regnum: register number to modify
686b8554d4fSHeiner Kallweit * @mask: bit mask of bits to clear
687b8554d4fSHeiner Kallweit * @set: new value of bits set in mask to write to @regnum
688b8554d4fSHeiner Kallweit *
689b8554d4fSHeiner Kallweit * NOTE: MUST NOT be called from interrupt context,
690b8554d4fSHeiner Kallweit * because the bus read/write functions may wait for an interrupt
691b8554d4fSHeiner Kallweit * to conclude the operation.
692b8554d4fSHeiner Kallweit */
__phy_modify(struct phy_device * phydev,u32 regnum,u16 mask,u16 set)693b8554d4fSHeiner Kallweit int __phy_modify(struct phy_device *phydev, u32 regnum, u16 mask, u16 set)
694b8554d4fSHeiner Kallweit {
695b8554d4fSHeiner Kallweit int ret;
696b8554d4fSHeiner Kallweit
697b8554d4fSHeiner Kallweit ret = __phy_modify_changed(phydev, regnum, mask, set);
6989f239fe6SAndrew Lunn
6999f239fe6SAndrew Lunn return ret < 0 ? ret : 0;
700788f9933SRussell King }
701788f9933SRussell King EXPORT_SYMBOL_GPL(__phy_modify);
70278ffc4acSRussell King
7032b74e5beSRussell King /**
7042b74e5beSRussell King * phy_modify - Convenience function for modifying a given PHY register
7052b74e5beSRussell King * @phydev: the phy_device struct
7062b74e5beSRussell King * @regnum: register number to write
7072b74e5beSRussell King * @mask: bit mask of bits to clear
7082b74e5beSRussell King * @set: new value of bits set in mask to write to @regnum
7092b74e5beSRussell King *
7102b74e5beSRussell King * NOTE: MUST NOT be called from interrupt context,
7112b74e5beSRussell King * because the bus read/write functions may wait for an interrupt
7122b74e5beSRussell King * to conclude the operation.
7132b74e5beSRussell King */
phy_modify(struct phy_device * phydev,u32 regnum,u16 mask,u16 set)7142b74e5beSRussell King int phy_modify(struct phy_device *phydev, u32 regnum, u16 mask, u16 set)
7152b74e5beSRussell King {
7162b74e5beSRussell King int ret;
7172b74e5beSRussell King
718bec170e5SHeiner Kallweit phy_lock_mdio_bus(phydev);
7192b74e5beSRussell King ret = __phy_modify(phydev, regnum, mask, set);
720bec170e5SHeiner Kallweit phy_unlock_mdio_bus(phydev);
7212b74e5beSRussell King
7222b74e5beSRussell King return ret;
7232b74e5beSRussell King }
7242b74e5beSRussell King EXPORT_SYMBOL_GPL(phy_modify);
7252b74e5beSRussell King
7261878f0dcSNikita Yushchenko /**
727b8554d4fSHeiner Kallweit * __phy_modify_mmd_changed - Function for modifying a register on MMD
7281878f0dcSNikita Yushchenko * @phydev: the phy_device struct
7291878f0dcSNikita Yushchenko * @devad: the MMD containing register to modify
7301878f0dcSNikita Yushchenko * @regnum: register number to modify
7311878f0dcSNikita Yushchenko * @mask: bit mask of bits to clear
7321878f0dcSNikita Yushchenko * @set: new value of bits set in mask to write to @regnum
7331878f0dcSNikita Yushchenko *
7341878f0dcSNikita Yushchenko * Unlocked helper function which allows a MMD register to be modified as
7351878f0dcSNikita Yushchenko * new register value = (old register value & ~mask) | set
736b8554d4fSHeiner Kallweit *
737b8554d4fSHeiner Kallweit * Returns negative errno, 0 if there was no change, and 1 in case of change
738b8554d4fSHeiner Kallweit */
__phy_modify_mmd_changed(struct phy_device * phydev,int devad,u32 regnum,u16 mask,u16 set)739b8554d4fSHeiner Kallweit int __phy_modify_mmd_changed(struct phy_device *phydev, int devad, u32 regnum,
740b8554d4fSHeiner Kallweit u16 mask, u16 set)
741b8554d4fSHeiner Kallweit {
742b8554d4fSHeiner Kallweit int new, ret;
743b8554d4fSHeiner Kallweit
744b8554d4fSHeiner Kallweit ret = __phy_read_mmd(phydev, devad, regnum);
745b8554d4fSHeiner Kallweit if (ret < 0)
746b8554d4fSHeiner Kallweit return ret;
747b8554d4fSHeiner Kallweit
748b8554d4fSHeiner Kallweit new = (ret & ~mask) | set;
749b8554d4fSHeiner Kallweit if (new == ret)
750b8554d4fSHeiner Kallweit return 0;
751b8554d4fSHeiner Kallweit
752b8554d4fSHeiner Kallweit ret = __phy_write_mmd(phydev, devad, regnum, new);
753b8554d4fSHeiner Kallweit
754b8554d4fSHeiner Kallweit return ret < 0 ? ret : 1;
755b8554d4fSHeiner Kallweit }
756b8554d4fSHeiner Kallweit EXPORT_SYMBOL_GPL(__phy_modify_mmd_changed);
757b8554d4fSHeiner Kallweit
758b8554d4fSHeiner Kallweit /**
759b8554d4fSHeiner Kallweit * phy_modify_mmd_changed - Function for modifying a register on MMD
760b8554d4fSHeiner Kallweit * @phydev: the phy_device struct
761b8554d4fSHeiner Kallweit * @devad: the MMD containing register to modify
762b8554d4fSHeiner Kallweit * @regnum: register number to modify
763b8554d4fSHeiner Kallweit * @mask: bit mask of bits to clear
764b8554d4fSHeiner Kallweit * @set: new value of bits set in mask to write to @regnum
765b8554d4fSHeiner Kallweit *
766b8554d4fSHeiner Kallweit * NOTE: MUST NOT be called from interrupt context,
767b8554d4fSHeiner Kallweit * because the bus read/write functions may wait for an interrupt
768b8554d4fSHeiner Kallweit * to conclude the operation.
769b8554d4fSHeiner Kallweit *
770b8554d4fSHeiner Kallweit * Returns negative errno, 0 if there was no change, and 1 in case of change
771b8554d4fSHeiner Kallweit */
phy_modify_mmd_changed(struct phy_device * phydev,int devad,u32 regnum,u16 mask,u16 set)772b8554d4fSHeiner Kallweit int phy_modify_mmd_changed(struct phy_device *phydev, int devad, u32 regnum,
773b8554d4fSHeiner Kallweit u16 mask, u16 set)
774b8554d4fSHeiner Kallweit {
775b8554d4fSHeiner Kallweit int ret;
776b8554d4fSHeiner Kallweit
777bec170e5SHeiner Kallweit phy_lock_mdio_bus(phydev);
778b8554d4fSHeiner Kallweit ret = __phy_modify_mmd_changed(phydev, devad, regnum, mask, set);
779bec170e5SHeiner Kallweit phy_unlock_mdio_bus(phydev);
780b8554d4fSHeiner Kallweit
781b8554d4fSHeiner Kallweit return ret;
782b8554d4fSHeiner Kallweit }
783b8554d4fSHeiner Kallweit EXPORT_SYMBOL_GPL(phy_modify_mmd_changed);
784b8554d4fSHeiner Kallweit
785b8554d4fSHeiner Kallweit /**
786b8554d4fSHeiner Kallweit * __phy_modify_mmd - Convenience function for modifying a register on MMD
787b8554d4fSHeiner Kallweit * @phydev: the phy_device struct
788b8554d4fSHeiner Kallweit * @devad: the MMD containing register to modify
789b8554d4fSHeiner Kallweit * @regnum: register number to modify
790b8554d4fSHeiner Kallweit * @mask: bit mask of bits to clear
791b8554d4fSHeiner Kallweit * @set: new value of bits set in mask to write to @regnum
792b8554d4fSHeiner Kallweit *
793b8554d4fSHeiner Kallweit * NOTE: MUST NOT be called from interrupt context,
794b8554d4fSHeiner Kallweit * because the bus read/write functions may wait for an interrupt
795b8554d4fSHeiner Kallweit * to conclude the operation.
7961878f0dcSNikita Yushchenko */
__phy_modify_mmd(struct phy_device * phydev,int devad,u32 regnum,u16 mask,u16 set)7971878f0dcSNikita Yushchenko int __phy_modify_mmd(struct phy_device *phydev, int devad, u32 regnum,
7981878f0dcSNikita Yushchenko u16 mask, u16 set)
7991878f0dcSNikita Yushchenko {
8001878f0dcSNikita Yushchenko int ret;
8011878f0dcSNikita Yushchenko
802b8554d4fSHeiner Kallweit ret = __phy_modify_mmd_changed(phydev, devad, regnum, mask, set);
8031878f0dcSNikita Yushchenko
8041878f0dcSNikita Yushchenko return ret < 0 ? ret : 0;
8051878f0dcSNikita Yushchenko }
8061878f0dcSNikita Yushchenko EXPORT_SYMBOL_GPL(__phy_modify_mmd);
8071878f0dcSNikita Yushchenko
8081878f0dcSNikita Yushchenko /**
8091878f0dcSNikita Yushchenko * phy_modify_mmd - Convenience function for modifying a register on MMD
8101878f0dcSNikita Yushchenko * @phydev: the phy_device struct
8111878f0dcSNikita Yushchenko * @devad: the MMD containing register to modify
8121878f0dcSNikita Yushchenko * @regnum: register number to modify
8131878f0dcSNikita Yushchenko * @mask: bit mask of bits to clear
8141878f0dcSNikita Yushchenko * @set: new value of bits set in mask to write to @regnum
8151878f0dcSNikita Yushchenko *
8161878f0dcSNikita Yushchenko * NOTE: MUST NOT be called from interrupt context,
8171878f0dcSNikita Yushchenko * because the bus read/write functions may wait for an interrupt
8181878f0dcSNikita Yushchenko * to conclude the operation.
8191878f0dcSNikita Yushchenko */
phy_modify_mmd(struct phy_device * phydev,int devad,u32 regnum,u16 mask,u16 set)8201878f0dcSNikita Yushchenko int phy_modify_mmd(struct phy_device *phydev, int devad, u32 regnum,
8211878f0dcSNikita Yushchenko u16 mask, u16 set)
8221878f0dcSNikita Yushchenko {
8231878f0dcSNikita Yushchenko int ret;
8241878f0dcSNikita Yushchenko
825bec170e5SHeiner Kallweit phy_lock_mdio_bus(phydev);
8261878f0dcSNikita Yushchenko ret = __phy_modify_mmd(phydev, devad, regnum, mask, set);
827bec170e5SHeiner Kallweit phy_unlock_mdio_bus(phydev);
8281878f0dcSNikita Yushchenko
8291878f0dcSNikita Yushchenko return ret;
8301878f0dcSNikita Yushchenko }
8311878f0dcSNikita Yushchenko EXPORT_SYMBOL_GPL(phy_modify_mmd);
8321878f0dcSNikita Yushchenko
__phy_read_page(struct phy_device * phydev)83378ffc4acSRussell King static int __phy_read_page(struct phy_device *phydev)
83478ffc4acSRussell King {
835f86854a2SHeiner Kallweit if (WARN_ONCE(!phydev->drv->read_page, "read_page callback not available, PHY driver not loaded?\n"))
836f86854a2SHeiner Kallweit return -EOPNOTSUPP;
837f86854a2SHeiner Kallweit
83878ffc4acSRussell King return phydev->drv->read_page(phydev);
83978ffc4acSRussell King }
84078ffc4acSRussell King
__phy_write_page(struct phy_device * phydev,int page)84178ffc4acSRussell King static int __phy_write_page(struct phy_device *phydev, int page)
84278ffc4acSRussell King {
843f86854a2SHeiner Kallweit if (WARN_ONCE(!phydev->drv->write_page, "write_page callback not available, PHY driver not loaded?\n"))
844f86854a2SHeiner Kallweit return -EOPNOTSUPP;
845f86854a2SHeiner Kallweit
84678ffc4acSRussell King return phydev->drv->write_page(phydev, page);
84778ffc4acSRussell King }
84878ffc4acSRussell King
84978ffc4acSRussell King /**
85078ffc4acSRussell King * phy_save_page() - take the bus lock and save the current page
85178ffc4acSRussell King * @phydev: a pointer to a &struct phy_device
85278ffc4acSRussell King *
85378ffc4acSRussell King * Take the MDIO bus lock, and return the current page number. On error,
85478ffc4acSRussell King * returns a negative errno. phy_restore_page() must always be called
85578ffc4acSRussell King * after this, irrespective of success or failure of this call.
85678ffc4acSRussell King */
phy_save_page(struct phy_device * phydev)85778ffc4acSRussell King int phy_save_page(struct phy_device *phydev)
85878ffc4acSRussell King {
859bec170e5SHeiner Kallweit phy_lock_mdio_bus(phydev);
86078ffc4acSRussell King return __phy_read_page(phydev);
86178ffc4acSRussell King }
86278ffc4acSRussell King EXPORT_SYMBOL_GPL(phy_save_page);
86378ffc4acSRussell King
86478ffc4acSRussell King /**
86578ffc4acSRussell King * phy_select_page() - take the bus lock, save the current page, and set a page
86678ffc4acSRussell King * @phydev: a pointer to a &struct phy_device
86778ffc4acSRussell King * @page: desired page
86878ffc4acSRussell King *
86978ffc4acSRussell King * Take the MDIO bus lock to protect against concurrent access, save the
87078ffc4acSRussell King * current PHY page, and set the current page. On error, returns a
87178ffc4acSRussell King * negative errno, otherwise returns the previous page number.
87278ffc4acSRussell King * phy_restore_page() must always be called after this, irrespective
87378ffc4acSRussell King * of success or failure of this call.
87478ffc4acSRussell King */
phy_select_page(struct phy_device * phydev,int page)87578ffc4acSRussell King int phy_select_page(struct phy_device *phydev, int page)
87678ffc4acSRussell King {
87778ffc4acSRussell King int ret, oldpage;
87878ffc4acSRussell King
87978ffc4acSRussell King oldpage = ret = phy_save_page(phydev);
88078ffc4acSRussell King if (ret < 0)
88178ffc4acSRussell King return ret;
88278ffc4acSRussell King
88378ffc4acSRussell King if (oldpage != page) {
88478ffc4acSRussell King ret = __phy_write_page(phydev, page);
88578ffc4acSRussell King if (ret < 0)
88678ffc4acSRussell King return ret;
88778ffc4acSRussell King }
88878ffc4acSRussell King
88978ffc4acSRussell King return oldpage;
89078ffc4acSRussell King }
89178ffc4acSRussell King EXPORT_SYMBOL_GPL(phy_select_page);
89278ffc4acSRussell King
89378ffc4acSRussell King /**
89478ffc4acSRussell King * phy_restore_page() - restore the page register and release the bus lock
89578ffc4acSRussell King * @phydev: a pointer to a &struct phy_device
89678ffc4acSRussell King * @oldpage: the old page, return value from phy_save_page() or phy_select_page()
89778ffc4acSRussell King * @ret: operation's return code
89878ffc4acSRussell King *
89978ffc4acSRussell King * Release the MDIO bus lock, restoring @oldpage if it is a valid page.
90078ffc4acSRussell King * This function propagates the earliest error code from the group of
90178ffc4acSRussell King * operations.
90278ffc4acSRussell King *
90378ffc4acSRussell King * Returns:
90478ffc4acSRussell King * @oldpage if it was a negative value, otherwise
90578ffc4acSRussell King * @ret if it was a negative errno value, otherwise
90678ffc4acSRussell King * phy_write_page()'s negative value if it were in error, otherwise
90778ffc4acSRussell King * @ret.
90878ffc4acSRussell King */
phy_restore_page(struct phy_device * phydev,int oldpage,int ret)90978ffc4acSRussell King int phy_restore_page(struct phy_device *phydev, int oldpage, int ret)
91078ffc4acSRussell King {
91178ffc4acSRussell King int r;
91278ffc4acSRussell King
91378ffc4acSRussell King if (oldpage >= 0) {
91478ffc4acSRussell King r = __phy_write_page(phydev, oldpage);
91578ffc4acSRussell King
91678ffc4acSRussell King /* Propagate the operation return code if the page write
91778ffc4acSRussell King * was successful.
91878ffc4acSRussell King */
91978ffc4acSRussell King if (ret >= 0 && r < 0)
92078ffc4acSRussell King ret = r;
92178ffc4acSRussell King } else {
92278ffc4acSRussell King /* Propagate the phy page selection error code */
92378ffc4acSRussell King ret = oldpage;
92478ffc4acSRussell King }
92578ffc4acSRussell King
926bec170e5SHeiner Kallweit phy_unlock_mdio_bus(phydev);
92778ffc4acSRussell King
92878ffc4acSRussell King return ret;
92978ffc4acSRussell King }
93078ffc4acSRussell King EXPORT_SYMBOL_GPL(phy_restore_page);
93178ffc4acSRussell King
93278ffc4acSRussell King /**
93378ffc4acSRussell King * phy_read_paged() - Convenience function for reading a paged register
93478ffc4acSRussell King * @phydev: a pointer to a &struct phy_device
93578ffc4acSRussell King * @page: the page for the phy
93678ffc4acSRussell King * @regnum: register number
93778ffc4acSRussell King *
93878ffc4acSRussell King * Same rules as for phy_read().
93978ffc4acSRussell King */
phy_read_paged(struct phy_device * phydev,int page,u32 regnum)94078ffc4acSRussell King int phy_read_paged(struct phy_device *phydev, int page, u32 regnum)
94178ffc4acSRussell King {
94278ffc4acSRussell King int ret = 0, oldpage;
94378ffc4acSRussell King
94478ffc4acSRussell King oldpage = phy_select_page(phydev, page);
94578ffc4acSRussell King if (oldpage >= 0)
94678ffc4acSRussell King ret = __phy_read(phydev, regnum);
94778ffc4acSRussell King
94878ffc4acSRussell King return phy_restore_page(phydev, oldpage, ret);
94978ffc4acSRussell King }
95078ffc4acSRussell King EXPORT_SYMBOL(phy_read_paged);
95178ffc4acSRussell King
95278ffc4acSRussell King /**
95378ffc4acSRussell King * phy_write_paged() - Convenience function for writing a paged register
95478ffc4acSRussell King * @phydev: a pointer to a &struct phy_device
95578ffc4acSRussell King * @page: the page for the phy
95678ffc4acSRussell King * @regnum: register number
95778ffc4acSRussell King * @val: value to write
95878ffc4acSRussell King *
95978ffc4acSRussell King * Same rules as for phy_write().
96078ffc4acSRussell King */
phy_write_paged(struct phy_device * phydev,int page,u32 regnum,u16 val)96178ffc4acSRussell King int phy_write_paged(struct phy_device *phydev, int page, u32 regnum, u16 val)
96278ffc4acSRussell King {
96378ffc4acSRussell King int ret = 0, oldpage;
96478ffc4acSRussell King
96578ffc4acSRussell King oldpage = phy_select_page(phydev, page);
96678ffc4acSRussell King if (oldpage >= 0)
96778ffc4acSRussell King ret = __phy_write(phydev, regnum, val);
96878ffc4acSRussell King
96978ffc4acSRussell King return phy_restore_page(phydev, oldpage, ret);
97078ffc4acSRussell King }
97178ffc4acSRussell King EXPORT_SYMBOL(phy_write_paged);
97278ffc4acSRussell King
97378ffc4acSRussell King /**
974bf22b343SHeiner Kallweit * phy_modify_paged_changed() - Function for modifying a paged register
975bf22b343SHeiner Kallweit * @phydev: a pointer to a &struct phy_device
976bf22b343SHeiner Kallweit * @page: the page for the phy
977bf22b343SHeiner Kallweit * @regnum: register number
978bf22b343SHeiner Kallweit * @mask: bit mask of bits to clear
979bf22b343SHeiner Kallweit * @set: bit mask of bits to set
980bf22b343SHeiner Kallweit *
981bf22b343SHeiner Kallweit * Returns negative errno, 0 if there was no change, and 1 in case of change
982bf22b343SHeiner Kallweit */
phy_modify_paged_changed(struct phy_device * phydev,int page,u32 regnum,u16 mask,u16 set)983bf22b343SHeiner Kallweit int phy_modify_paged_changed(struct phy_device *phydev, int page, u32 regnum,
984bf22b343SHeiner Kallweit u16 mask, u16 set)
985bf22b343SHeiner Kallweit {
986bf22b343SHeiner Kallweit int ret = 0, oldpage;
987bf22b343SHeiner Kallweit
988bf22b343SHeiner Kallweit oldpage = phy_select_page(phydev, page);
989bf22b343SHeiner Kallweit if (oldpage >= 0)
990bf22b343SHeiner Kallweit ret = __phy_modify_changed(phydev, regnum, mask, set);
991bf22b343SHeiner Kallweit
992bf22b343SHeiner Kallweit return phy_restore_page(phydev, oldpage, ret);
993bf22b343SHeiner Kallweit }
994bf22b343SHeiner Kallweit EXPORT_SYMBOL(phy_modify_paged_changed);
995bf22b343SHeiner Kallweit
996bf22b343SHeiner Kallweit /**
99778ffc4acSRussell King * phy_modify_paged() - Convenience function for modifying a paged register
99878ffc4acSRussell King * @phydev: a pointer to a &struct phy_device
99978ffc4acSRussell King * @page: the page for the phy
100078ffc4acSRussell King * @regnum: register number
100178ffc4acSRussell King * @mask: bit mask of bits to clear
100278ffc4acSRussell King * @set: bit mask of bits to set
100378ffc4acSRussell King *
100478ffc4acSRussell King * Same rules as for phy_read() and phy_write().
100578ffc4acSRussell King */
phy_modify_paged(struct phy_device * phydev,int page,u32 regnum,u16 mask,u16 set)100678ffc4acSRussell King int phy_modify_paged(struct phy_device *phydev, int page, u32 regnum,
100778ffc4acSRussell King u16 mask, u16 set)
100878ffc4acSRussell King {
1009bf22b343SHeiner Kallweit int ret = phy_modify_paged_changed(phydev, page, regnum, mask, set);
101078ffc4acSRussell King
1011bf22b343SHeiner Kallweit return ret < 0 ? ret : 0;
101278ffc4acSRussell King }
101378ffc4acSRussell King EXPORT_SYMBOL(phy_modify_paged);
1014