blob: 6d953c53eed6c86d9c6db39704a374ee5fac1bde [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2
3 mii.c: MII interface library
4
5 Maintained by Jeff Garzik <jgarzik@pobox.com>
6 Copyright 2001,2002 Jeff Garzik
7
8 Various code came from myson803.c and other files by
9 Donald Becker. Copyright:
10
11 Written 1998-2002 by Donald Becker.
12
13 This software may be used and distributed according
14 to the terms of the GNU General Public License (GPL),
15 incorporated herein by reference. Drivers based on
16 or derived from this code fall under the GPL and must
17 retain the authorship, copyright and license notice.
18 This file is not a complete program and may only be
19 used when the entire operating system is licensed
20 under the GPL.
21
22 The author may be reached as becker@scyld.com, or C/O
23 Scyld Computing Corporation
24 410 Severn Ave., Suite 210
25 Annapolis MD 21403
26
27
28 */
29
30#include <linux/kernel.h>
31#include <linux/module.h>
32#include <linux/netdevice.h>
33#include <linux/ethtool.h>
Ben Hutchings9c4df532012-02-29 14:26:22 +000034#include <linux/mii.h>
Ben Hutchings59747002009-04-29 08:34:44 +000035
36static u32 mii_get_an(struct mii_if_info *mii, u16 addr)
37{
Ben Hutchings59747002009-04-29 08:34:44 +000038 int advert;
39
40 advert = mii->mdio_read(mii->dev, mii->phy_id, addr);
Ben Hutchings59747002009-04-29 08:34:44 +000041
Matt Carlson37f07022011-11-17 14:30:55 +000042 return mii_lpa_to_ethtool_lpa_t(advert);
Ben Hutchings59747002009-04-29 08:34:44 +000043}
Linus Torvalds1da177e2005-04-16 15:20:36 -070044
Randy Dunlap32684ec2007-04-06 11:08:24 -070045/**
46 * mii_ethtool_gset - get settings that are specified in @ecmd
47 * @mii: MII interface
48 * @ecmd: requested ethtool_cmd
49 *
David Decotigny8ae6daca2011-04-27 18:32:38 +000050 * The @ecmd parameter is expected to have been cleared before calling
51 * mii_ethtool_gset().
52 *
Randy Dunlap32684ec2007-04-06 11:08:24 -070053 * Returns 0 for success, negative on error.
54 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070055int mii_ethtool_gset(struct mii_if_info *mii, struct ethtool_cmd *ecmd)
56{
57 struct net_device *dev = mii->dev;
Ben Hutchings59747002009-04-29 08:34:44 +000058 u16 bmcr, bmsr, ctrl1000 = 0, stat1000 = 0;
59 u32 nego;
Linus Torvalds1da177e2005-04-16 15:20:36 -070060
61 ecmd->supported =
62 (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
63 SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
64 SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII);
65 if (mii->supports_gmii)
66 ecmd->supported |= SUPPORTED_1000baseT_Half |
67 SUPPORTED_1000baseT_Full;
68
69 /* only supports twisted-pair */
70 ecmd->port = PORT_MII;
71
72 /* only supports internal transceiver */
73 ecmd->transceiver = XCVR_INTERNAL;
74
75 /* this isn't fully supported at higher layers */
76 ecmd->phy_address = mii->phy_id;
Ben Hutchings9c4df532012-02-29 14:26:22 +000077 ecmd->mdio_support = ETH_MDIO_SUPPORTS_C22;
Linus Torvalds1da177e2005-04-16 15:20:36 -070078
79 ecmd->advertising = ADVERTISED_TP | ADVERTISED_MII;
Linus Torvalds1da177e2005-04-16 15:20:36 -070080
81 bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
Ben Hutchings59747002009-04-29 08:34:44 +000082 bmsr = mii->mdio_read(dev, mii->phy_id, MII_BMSR);
Linus Torvalds1da177e2005-04-16 15:20:36 -070083 if (mii->supports_gmii) {
Ben Hutchings59747002009-04-29 08:34:44 +000084 ctrl1000 = mii->mdio_read(dev, mii->phy_id, MII_CTRL1000);
85 stat1000 = mii->mdio_read(dev, mii->phy_id, MII_STAT1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -070086 }
87 if (bmcr & BMCR_ANENABLE) {
88 ecmd->advertising |= ADVERTISED_Autoneg;
89 ecmd->autoneg = AUTONEG_ENABLE;
Jeff Garzik6aa20a22006-09-13 13:24:59 -040090
Ben Hutchings59747002009-04-29 08:34:44 +000091 ecmd->advertising |= mii_get_an(mii, MII_ADVERTISE);
Matt Carlson28011cf2011-11-16 18:36:59 -050092 if (mii->supports_gmii)
Matt Carlson37f07022011-11-17 14:30:55 +000093 ecmd->advertising |=
94 mii_ctrl1000_to_ethtool_adv_t(ctrl1000);
Ben Hutchings59747002009-04-29 08:34:44 +000095
96 if (bmsr & BMSR_ANEGCOMPLETE) {
97 ecmd->lp_advertising = mii_get_an(mii, MII_LPA);
Matt Carlson28011cf2011-11-16 18:36:59 -050098 ecmd->lp_advertising |=
Matt Carlson37f07022011-11-17 14:30:55 +000099 mii_stat1000_to_ethtool_lpa_t(stat1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100 } else {
Ben Hutchings59747002009-04-29 08:34:44 +0000101 ecmd->lp_advertising = 0;
102 }
103
104 nego = ecmd->advertising & ecmd->lp_advertising;
105
106 if (nego & (ADVERTISED_1000baseT_Full |
107 ADVERTISED_1000baseT_Half)) {
David Decotigny70739492011-04-27 18:32:40 +0000108 ethtool_cmd_speed_set(ecmd, SPEED_1000);
Ben Hutchings59747002009-04-29 08:34:44 +0000109 ecmd->duplex = !!(nego & ADVERTISED_1000baseT_Full);
110 } else if (nego & (ADVERTISED_100baseT_Full |
111 ADVERTISED_100baseT_Half)) {
David Decotigny70739492011-04-27 18:32:40 +0000112 ethtool_cmd_speed_set(ecmd, SPEED_100);
Ben Hutchings59747002009-04-29 08:34:44 +0000113 ecmd->duplex = !!(nego & ADVERTISED_100baseT_Full);
114 } else {
David Decotigny70739492011-04-27 18:32:40 +0000115 ethtool_cmd_speed_set(ecmd, SPEED_10);
Ben Hutchings59747002009-04-29 08:34:44 +0000116 ecmd->duplex = !!(nego & ADVERTISED_10baseT_Full);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117 }
118 } else {
119 ecmd->autoneg = AUTONEG_DISABLE;
120
David Decotigny70739492011-04-27 18:32:40 +0000121 ethtool_cmd_speed_set(ecmd,
122 ((bmcr & BMCR_SPEED1000 &&
123 (bmcr & BMCR_SPEED100) == 0) ?
124 SPEED_1000 :
125 ((bmcr & BMCR_SPEED100) ?
126 SPEED_100 : SPEED_10)));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127 ecmd->duplex = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF;
128 }
129
Ben Hutchings59747002009-04-29 08:34:44 +0000130 mii->full_duplex = ecmd->duplex;
131
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132 /* ignore maxtxpkt, maxrxpkt for now */
133
134 return 0;
135}
136
Randy Dunlap32684ec2007-04-06 11:08:24 -0700137/**
Philippe Reynesbc8ee592016-11-01 16:32:25 +0100138 * mii_ethtool_get_link_ksettings - get settings that are specified in @cmd
139 * @mii: MII interface
140 * @cmd: requested ethtool_link_ksettings
141 *
142 * The @cmd parameter is expected to have been cleared before calling
143 * mii_ethtool_get_link_ksettings().
144 *
145 * Returns 0 for success, negative on error.
146 */
147int mii_ethtool_get_link_ksettings(struct mii_if_info *mii,
148 struct ethtool_link_ksettings *cmd)
149{
150 struct net_device *dev = mii->dev;
151 u16 bmcr, bmsr, ctrl1000 = 0, stat1000 = 0;
152 u32 nego, supported, advertising, lp_advertising;
153
154 supported = (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
155 SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
156 SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII);
157 if (mii->supports_gmii)
158 supported |= SUPPORTED_1000baseT_Half |
159 SUPPORTED_1000baseT_Full;
160
161 /* only supports twisted-pair */
162 cmd->base.port = PORT_MII;
163
164 /* this isn't fully supported at higher layers */
165 cmd->base.phy_address = mii->phy_id;
166 cmd->base.mdio_support = ETH_MDIO_SUPPORTS_C22;
167
168 advertising = ADVERTISED_TP | ADVERTISED_MII;
169
170 bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
171 bmsr = mii->mdio_read(dev, mii->phy_id, MII_BMSR);
172 if (mii->supports_gmii) {
173 ctrl1000 = mii->mdio_read(dev, mii->phy_id, MII_CTRL1000);
174 stat1000 = mii->mdio_read(dev, mii->phy_id, MII_STAT1000);
175 }
176 if (bmcr & BMCR_ANENABLE) {
177 advertising |= ADVERTISED_Autoneg;
178 cmd->base.autoneg = AUTONEG_ENABLE;
179
180 advertising |= mii_get_an(mii, MII_ADVERTISE);
181 if (mii->supports_gmii)
182 advertising |= mii_ctrl1000_to_ethtool_adv_t(ctrl1000);
183
184 if (bmsr & BMSR_ANEGCOMPLETE) {
185 lp_advertising = mii_get_an(mii, MII_LPA);
186 lp_advertising |=
187 mii_stat1000_to_ethtool_lpa_t(stat1000);
188 } else {
189 lp_advertising = 0;
190 }
191
192 nego = advertising & lp_advertising;
193
194 if (nego & (ADVERTISED_1000baseT_Full |
195 ADVERTISED_1000baseT_Half)) {
196 cmd->base.speed = SPEED_1000;
197 cmd->base.duplex = !!(nego & ADVERTISED_1000baseT_Full);
198 } else if (nego & (ADVERTISED_100baseT_Full |
199 ADVERTISED_100baseT_Half)) {
200 cmd->base.speed = SPEED_100;
201 cmd->base.duplex = !!(nego & ADVERTISED_100baseT_Full);
202 } else {
203 cmd->base.speed = SPEED_10;
204 cmd->base.duplex = !!(nego & ADVERTISED_10baseT_Full);
205 }
206 } else {
207 cmd->base.autoneg = AUTONEG_DISABLE;
208
209 cmd->base.speed = ((bmcr & BMCR_SPEED1000 &&
210 (bmcr & BMCR_SPEED100) == 0) ?
211 SPEED_1000 :
212 ((bmcr & BMCR_SPEED100) ?
213 SPEED_100 : SPEED_10));
214 cmd->base.duplex = (bmcr & BMCR_FULLDPLX) ?
215 DUPLEX_FULL : DUPLEX_HALF;
Arnd Bergmanndc0b2c92016-11-08 14:31:38 +0100216
217 lp_advertising = 0;
Philippe Reynesbc8ee592016-11-01 16:32:25 +0100218 }
219
220 mii->full_duplex = cmd->base.duplex;
221
222 ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported,
223 supported);
224 ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising,
225 advertising);
226 ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.lp_advertising,
227 lp_advertising);
228
229 /* ignore maxtxpkt, maxrxpkt for now */
230
231 return 0;
232}
233
234/**
Randy Dunlap32684ec2007-04-06 11:08:24 -0700235 * mii_ethtool_sset - set settings that are specified in @ecmd
236 * @mii: MII interface
237 * @ecmd: requested ethtool_cmd
238 *
239 * Returns 0 for success, negative on error.
240 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700241int mii_ethtool_sset(struct mii_if_info *mii, struct ethtool_cmd *ecmd)
242{
243 struct net_device *dev = mii->dev;
David Decotigny25db0332011-04-27 18:32:39 +0000244 u32 speed = ethtool_cmd_speed(ecmd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245
David Decotigny25db0332011-04-27 18:32:39 +0000246 if (speed != SPEED_10 &&
247 speed != SPEED_100 &&
248 speed != SPEED_1000)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249 return -EINVAL;
250 if (ecmd->duplex != DUPLEX_HALF && ecmd->duplex != DUPLEX_FULL)
251 return -EINVAL;
252 if (ecmd->port != PORT_MII)
253 return -EINVAL;
254 if (ecmd->transceiver != XCVR_INTERNAL)
255 return -EINVAL;
256 if (ecmd->phy_address != mii->phy_id)
257 return -EINVAL;
258 if (ecmd->autoneg != AUTONEG_DISABLE && ecmd->autoneg != AUTONEG_ENABLE)
259 return -EINVAL;
David Decotigny25db0332011-04-27 18:32:39 +0000260 if ((speed == SPEED_1000) && (!mii->supports_gmii))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261 return -EINVAL;
Jeff Garzik6aa20a22006-09-13 13:24:59 -0400262
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263 /* ignore supported, maxtxpkt, maxrxpkt */
Jeff Garzik6aa20a22006-09-13 13:24:59 -0400264
Linus Torvalds1da177e2005-04-16 15:20:36 -0700265 if (ecmd->autoneg == AUTONEG_ENABLE) {
266 u32 bmcr, advert, tmp;
267 u32 advert2 = 0, tmp2 = 0;
268
269 if ((ecmd->advertising & (ADVERTISED_10baseT_Half |
270 ADVERTISED_10baseT_Full |
271 ADVERTISED_100baseT_Half |
272 ADVERTISED_100baseT_Full |
273 ADVERTISED_1000baseT_Half |
274 ADVERTISED_1000baseT_Full)) == 0)
275 return -EINVAL;
276
277 /* advertise only what has been requested */
278 advert = mii->mdio_read(dev, mii->phy_id, MII_ADVERTISE);
279 tmp = advert & ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
280 if (mii->supports_gmii) {
281 advert2 = mii->mdio_read(dev, mii->phy_id, MII_CTRL1000);
282 tmp2 = advert2 & ~(ADVERTISE_1000HALF | ADVERTISE_1000FULL);
283 }
Matt Carlson37f07022011-11-17 14:30:55 +0000284 tmp |= ethtool_adv_to_mii_adv_t(ecmd->advertising);
Matt Carlson28011cf2011-11-16 18:36:59 -0500285
286 if (mii->supports_gmii)
Matt Carlson37f07022011-11-17 14:30:55 +0000287 tmp2 |=
288 ethtool_adv_to_mii_ctrl1000_t(ecmd->advertising);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289 if (advert != tmp) {
290 mii->mdio_write(dev, mii->phy_id, MII_ADVERTISE, tmp);
291 mii->advertising = tmp;
292 }
293 if ((mii->supports_gmii) && (advert2 != tmp2))
294 mii->mdio_write(dev, mii->phy_id, MII_CTRL1000, tmp2);
Jeff Garzik6aa20a22006-09-13 13:24:59 -0400295
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296 /* turn on autonegotiation, and force a renegotiate */
297 bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
298 bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART);
299 mii->mdio_write(dev, mii->phy_id, MII_BMCR, bmcr);
300
301 mii->force_media = 0;
302 } else {
303 u32 bmcr, tmp;
304
305 /* turn off auto negotiation, set speed and duplexity */
306 bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
Jeff Garzik6aa20a22006-09-13 13:24:59 -0400307 tmp = bmcr & ~(BMCR_ANENABLE | BMCR_SPEED100 |
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308 BMCR_SPEED1000 | BMCR_FULLDPLX);
David Decotigny25db0332011-04-27 18:32:39 +0000309 if (speed == SPEED_1000)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310 tmp |= BMCR_SPEED1000;
David Decotigny25db0332011-04-27 18:32:39 +0000311 else if (speed == SPEED_100)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312 tmp |= BMCR_SPEED100;
313 if (ecmd->duplex == DUPLEX_FULL) {
314 tmp |= BMCR_FULLDPLX;
315 mii->full_duplex = 1;
316 } else
317 mii->full_duplex = 0;
318 if (bmcr != tmp)
319 mii->mdio_write(dev, mii->phy_id, MII_BMCR, tmp);
320
321 mii->force_media = 1;
322 }
323 return 0;
324}
325
Randy Dunlap32684ec2007-04-06 11:08:24 -0700326/**
Philippe Reynesbc8ee592016-11-01 16:32:25 +0100327 * mii_ethtool_set_link_ksettings - set settings that are specified in @cmd
328 * @mii: MII interfaces
329 * @cmd: requested ethtool_link_ksettings
330 *
331 * Returns 0 for success, negative on error.
332 */
333int mii_ethtool_set_link_ksettings(struct mii_if_info *mii,
334 const struct ethtool_link_ksettings *cmd)
335{
336 struct net_device *dev = mii->dev;
337 u32 speed = cmd->base.speed;
338
339 if (speed != SPEED_10 &&
340 speed != SPEED_100 &&
341 speed != SPEED_1000)
342 return -EINVAL;
343 if (cmd->base.duplex != DUPLEX_HALF && cmd->base.duplex != DUPLEX_FULL)
344 return -EINVAL;
345 if (cmd->base.port != PORT_MII)
346 return -EINVAL;
347 if (cmd->base.phy_address != mii->phy_id)
348 return -EINVAL;
349 if (cmd->base.autoneg != AUTONEG_DISABLE &&
350 cmd->base.autoneg != AUTONEG_ENABLE)
351 return -EINVAL;
352 if ((speed == SPEED_1000) && (!mii->supports_gmii))
353 return -EINVAL;
354
355 /* ignore supported, maxtxpkt, maxrxpkt */
356
357 if (cmd->base.autoneg == AUTONEG_ENABLE) {
358 u32 bmcr, advert, tmp;
359 u32 advert2 = 0, tmp2 = 0;
360 u32 advertising;
361
362 ethtool_convert_link_mode_to_legacy_u32(
363 &advertising, cmd->link_modes.advertising);
364
365 if ((advertising & (ADVERTISED_10baseT_Half |
366 ADVERTISED_10baseT_Full |
367 ADVERTISED_100baseT_Half |
368 ADVERTISED_100baseT_Full |
369 ADVERTISED_1000baseT_Half |
370 ADVERTISED_1000baseT_Full)) == 0)
371 return -EINVAL;
372
373 /* advertise only what has been requested */
374 advert = mii->mdio_read(dev, mii->phy_id, MII_ADVERTISE);
375 tmp = advert & ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
376 if (mii->supports_gmii) {
377 advert2 = mii->mdio_read(dev, mii->phy_id,
378 MII_CTRL1000);
379 tmp2 = advert2 &
380 ~(ADVERTISE_1000HALF | ADVERTISE_1000FULL);
381 }
382 tmp |= ethtool_adv_to_mii_adv_t(advertising);
383
384 if (mii->supports_gmii)
385 tmp2 |= ethtool_adv_to_mii_ctrl1000_t(advertising);
386 if (advert != tmp) {
387 mii->mdio_write(dev, mii->phy_id, MII_ADVERTISE, tmp);
388 mii->advertising = tmp;
389 }
390 if ((mii->supports_gmii) && (advert2 != tmp2))
391 mii->mdio_write(dev, mii->phy_id, MII_CTRL1000, tmp2);
392
393 /* turn on autonegotiation, and force a renegotiate */
394 bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
395 bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART);
396 mii->mdio_write(dev, mii->phy_id, MII_BMCR, bmcr);
397
398 mii->force_media = 0;
399 } else {
400 u32 bmcr, tmp;
401
402 /* turn off auto negotiation, set speed and duplexity */
403 bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
404 tmp = bmcr & ~(BMCR_ANENABLE | BMCR_SPEED100 |
405 BMCR_SPEED1000 | BMCR_FULLDPLX);
406 if (speed == SPEED_1000)
407 tmp |= BMCR_SPEED1000;
408 else if (speed == SPEED_100)
409 tmp |= BMCR_SPEED100;
410 if (cmd->base.duplex == DUPLEX_FULL) {
411 tmp |= BMCR_FULLDPLX;
412 mii->full_duplex = 1;
413 } else {
414 mii->full_duplex = 0;
415 }
416 if (bmcr != tmp)
417 mii->mdio_write(dev, mii->phy_id, MII_BMCR, tmp);
418
419 mii->force_media = 1;
420 }
421 return 0;
422}
423
424/**
Randy Dunlap32684ec2007-04-06 11:08:24 -0700425 * mii_check_gmii_support - check if the MII supports Gb interfaces
426 * @mii: the MII interface
427 */
Dale Farnsworth43ec6e92005-08-23 10:30:29 -0700428int mii_check_gmii_support(struct mii_if_info *mii)
429{
430 int reg;
431
432 reg = mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR);
433 if (reg & BMSR_ESTATEN) {
434 reg = mii->mdio_read(mii->dev, mii->phy_id, MII_ESTATUS);
435 if (reg & (ESTATUS_1000_TFULL | ESTATUS_1000_THALF))
436 return 1;
437 }
438
439 return 0;
440}
441
Randy Dunlap32684ec2007-04-06 11:08:24 -0700442/**
443 * mii_link_ok - is link status up/ok
444 * @mii: the MII interface
445 *
446 * Returns 1 if the MII reports link status up/ok, 0 otherwise.
447 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700448int mii_link_ok (struct mii_if_info *mii)
449{
450 /* first, a dummy read, needed to latch some MII phys */
451 mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR);
452 if (mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR) & BMSR_LSTATUS)
453 return 1;
454 return 0;
455}
456
Randy Dunlap32684ec2007-04-06 11:08:24 -0700457/**
458 * mii_nway_restart - restart NWay (autonegotiation) for this interface
459 * @mii: the MII interface
460 *
461 * Returns 0 on success, negative on error.
462 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463int mii_nway_restart (struct mii_if_info *mii)
464{
465 int bmcr;
466 int r = -EINVAL;
467
468 /* if autoneg is off, it's an error */
469 bmcr = mii->mdio_read(mii->dev, mii->phy_id, MII_BMCR);
470
471 if (bmcr & BMCR_ANENABLE) {
472 bmcr |= BMCR_ANRESTART;
473 mii->mdio_write(mii->dev, mii->phy_id, MII_BMCR, bmcr);
474 r = 0;
475 }
476
477 return r;
478}
479
Randy Dunlap32684ec2007-04-06 11:08:24 -0700480/**
481 * mii_check_link - check MII link status
482 * @mii: MII interface
483 *
484 * If the link status changed (previous != current), call
485 * netif_carrier_on() if current link status is Up or call
486 * netif_carrier_off() if current link status is Down.
487 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488void mii_check_link (struct mii_if_info *mii)
489{
490 int cur_link = mii_link_ok(mii);
491 int prev_link = netif_carrier_ok(mii->dev);
492
493 if (cur_link && !prev_link)
494 netif_carrier_on(mii->dev);
495 else if (prev_link && !cur_link)
496 netif_carrier_off(mii->dev);
497}
498
Randy Dunlap32684ec2007-04-06 11:08:24 -0700499/**
Ben Hutchings5bdc7382015-01-16 17:55:35 +0000500 * mii_check_media - check the MII interface for a carrier/speed/duplex change
Randy Dunlap32684ec2007-04-06 11:08:24 -0700501 * @mii: the MII interface
502 * @ok_to_print: OK to print link up/down messages
503 * @init_media: OK to save duplex mode in @mii
504 *
505 * Returns 1 if the duplex mode changed, 0 if not.
506 * If the media type is forced, always returns 0.
507 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508unsigned int mii_check_media (struct mii_if_info *mii,
509 unsigned int ok_to_print,
510 unsigned int init_media)
511{
512 unsigned int old_carrier, new_carrier;
513 int advertise, lpa, media, duplex;
514 int lpa2 = 0;
515
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516 /* check current and old link status */
517 old_carrier = netif_carrier_ok(mii->dev) ? 1 : 0;
518 new_carrier = (unsigned int) mii_link_ok(mii);
519
520 /* if carrier state did not change, this is a "bounce",
521 * just exit as everything is already set correctly
522 */
523 if ((!init_media) && (old_carrier == new_carrier))
524 return 0; /* duplex did not change */
525
526 /* no carrier, nothing much to do */
527 if (!new_carrier) {
528 netif_carrier_off(mii->dev);
529 if (ok_to_print)
Joe Perches967faf32011-03-03 12:55:08 -0800530 netdev_info(mii->dev, "link down\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531 return 0; /* duplex did not change */
532 }
533
534 /*
535 * we have carrier, see who's on the other end
536 */
537 netif_carrier_on(mii->dev);
538
Ben Hutchings5bdc7382015-01-16 17:55:35 +0000539 if (mii->force_media) {
540 if (ok_to_print)
541 netdev_info(mii->dev, "link up\n");
542 return 0; /* duplex did not change */
543 }
544
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545 /* get MII advertise and LPA values */
546 if ((!init_media) && (mii->advertising))
547 advertise = mii->advertising;
548 else {
549 advertise = mii->mdio_read(mii->dev, mii->phy_id, MII_ADVERTISE);
550 mii->advertising = advertise;
551 }
552 lpa = mii->mdio_read(mii->dev, mii->phy_id, MII_LPA);
553 if (mii->supports_gmii)
554 lpa2 = mii->mdio_read(mii->dev, mii->phy_id, MII_STAT1000);
555
556 /* figure out media and duplex from advertise and LPA values */
557 media = mii_nway_result(lpa & advertise);
558 duplex = (media & ADVERTISE_FULL) ? 1 : 0;
559 if (lpa2 & LPA_1000FULL)
560 duplex = 1;
561
562 if (ok_to_print)
Joe Perches967faf32011-03-03 12:55:08 -0800563 netdev_info(mii->dev, "link up, %uMbps, %s-duplex, lpa 0x%04X\n",
564 lpa2 & (LPA_1000FULL | LPA_1000HALF) ? 1000 :
565 media & (ADVERTISE_100FULL | ADVERTISE_100HALF) ?
566 100 : 10,
567 duplex ? "full" : "half",
568 lpa);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569
570 if ((init_media) || (mii->full_duplex != duplex)) {
571 mii->full_duplex = duplex;
572 return 1; /* duplex changed */
573 }
574
575 return 0; /* duplex did not change */
576}
577
Randy Dunlap32684ec2007-04-06 11:08:24 -0700578/**
579 * generic_mii_ioctl - main MII ioctl interface
580 * @mii_if: the MII interface
581 * @mii_data: MII ioctl data structure
582 * @cmd: MII ioctl command
583 * @duplex_chg_out: pointer to @duplex_changed status if there was no
584 * ioctl error
585 *
586 * Returns 0 on success, negative on error.
587 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588int generic_mii_ioctl(struct mii_if_info *mii_if,
589 struct mii_ioctl_data *mii_data, int cmd,
590 unsigned int *duplex_chg_out)
591{
592 int rc = 0;
593 unsigned int duplex_changed = 0;
594
595 if (duplex_chg_out)
596 *duplex_chg_out = 0;
597
598 mii_data->phy_id &= mii_if->phy_id_mask;
599 mii_data->reg_num &= mii_if->reg_num_mask;
600
601 switch(cmd) {
602 case SIOCGMIIPHY:
603 mii_data->phy_id = mii_if->phy_id;
604 /* fall through */
605
606 case SIOCGMIIREG:
607 mii_data->val_out =
608 mii_if->mdio_read(mii_if->dev, mii_data->phy_id,
609 mii_data->reg_num);
610 break;
611
612 case SIOCSMIIREG: {
613 u16 val = mii_data->val_in;
614
Linus Torvalds1da177e2005-04-16 15:20:36 -0700615 if (mii_data->phy_id == mii_if->phy_id) {
616 switch(mii_data->reg_num) {
617 case MII_BMCR: {
618 unsigned int new_duplex = 0;
619 if (val & (BMCR_RESET|BMCR_ANENABLE))
620 mii_if->force_media = 0;
621 else
622 mii_if->force_media = 1;
623 if (mii_if->force_media &&
624 (val & BMCR_FULLDPLX))
625 new_duplex = 1;
626 if (mii_if->full_duplex != new_duplex) {
627 duplex_changed = 1;
628 mii_if->full_duplex = new_duplex;
629 }
630 break;
631 }
632 case MII_ADVERTISE:
633 mii_if->advertising = val;
634 break;
635 default:
636 /* do nothing */
637 break;
638 }
639 }
640
641 mii_if->mdio_write(mii_if->dev, mii_data->phy_id,
642 mii_data->reg_num, val);
643 break;
644 }
645
646 default:
647 rc = -EOPNOTSUPP;
648 break;
649 }
650
651 if ((rc == 0) && (duplex_chg_out) && (duplex_changed))
652 *duplex_chg_out = 1;
653
654 return rc;
655}
656
657MODULE_AUTHOR ("Jeff Garzik <jgarzik@pobox.com>");
658MODULE_DESCRIPTION ("MII hardware support library");
659MODULE_LICENSE("GPL");
660
661EXPORT_SYMBOL(mii_link_ok);
662EXPORT_SYMBOL(mii_nway_restart);
663EXPORT_SYMBOL(mii_ethtool_gset);
Philippe Reynesbc8ee592016-11-01 16:32:25 +0100664EXPORT_SYMBOL(mii_ethtool_get_link_ksettings);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665EXPORT_SYMBOL(mii_ethtool_sset);
Philippe Reynesbc8ee592016-11-01 16:32:25 +0100666EXPORT_SYMBOL(mii_ethtool_set_link_ksettings);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700667EXPORT_SYMBOL(mii_check_link);
668EXPORT_SYMBOL(mii_check_media);
Dale Farnsworth43ec6e92005-08-23 10:30:29 -0700669EXPORT_SYMBOL(mii_check_gmii_support);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670EXPORT_SYMBOL(generic_mii_ioctl);
671