ethtool: cosmetic: Use ethtool ethtool_cmd_speed API
[deliverable/linux.git] / drivers / net / mii.c
1 /*
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>
34 #include <linux/mdio.h>
35
36 static u32 mii_get_an(struct mii_if_info *mii, u16 addr)
37 {
38 u32 result = 0;
39 int advert;
40
41 advert = mii->mdio_read(mii->dev, mii->phy_id, addr);
42 if (advert & LPA_LPACK)
43 result |= ADVERTISED_Autoneg;
44 if (advert & ADVERTISE_10HALF)
45 result |= ADVERTISED_10baseT_Half;
46 if (advert & ADVERTISE_10FULL)
47 result |= ADVERTISED_10baseT_Full;
48 if (advert & ADVERTISE_100HALF)
49 result |= ADVERTISED_100baseT_Half;
50 if (advert & ADVERTISE_100FULL)
51 result |= ADVERTISED_100baseT_Full;
52
53 return result;
54 }
55
56 /**
57 * mii_ethtool_gset - get settings that are specified in @ecmd
58 * @mii: MII interface
59 * @ecmd: requested ethtool_cmd
60 *
61 * The @ecmd parameter is expected to have been cleared before calling
62 * mii_ethtool_gset().
63 *
64 * Returns 0 for success, negative on error.
65 */
66 int mii_ethtool_gset(struct mii_if_info *mii, struct ethtool_cmd *ecmd)
67 {
68 struct net_device *dev = mii->dev;
69 u16 bmcr, bmsr, ctrl1000 = 0, stat1000 = 0;
70 u32 nego;
71
72 ecmd->supported =
73 (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
74 SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
75 SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII);
76 if (mii->supports_gmii)
77 ecmd->supported |= SUPPORTED_1000baseT_Half |
78 SUPPORTED_1000baseT_Full;
79
80 /* only supports twisted-pair */
81 ecmd->port = PORT_MII;
82
83 /* only supports internal transceiver */
84 ecmd->transceiver = XCVR_INTERNAL;
85
86 /* this isn't fully supported at higher layers */
87 ecmd->phy_address = mii->phy_id;
88 ecmd->mdio_support = MDIO_SUPPORTS_C22;
89
90 ecmd->advertising = ADVERTISED_TP | ADVERTISED_MII;
91
92 bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
93 bmsr = mii->mdio_read(dev, mii->phy_id, MII_BMSR);
94 if (mii->supports_gmii) {
95 ctrl1000 = mii->mdio_read(dev, mii->phy_id, MII_CTRL1000);
96 stat1000 = mii->mdio_read(dev, mii->phy_id, MII_STAT1000);
97 }
98 if (bmcr & BMCR_ANENABLE) {
99 ecmd->advertising |= ADVERTISED_Autoneg;
100 ecmd->autoneg = AUTONEG_ENABLE;
101
102 ecmd->advertising |= mii_get_an(mii, MII_ADVERTISE);
103 if (ctrl1000 & ADVERTISE_1000HALF)
104 ecmd->advertising |= ADVERTISED_1000baseT_Half;
105 if (ctrl1000 & ADVERTISE_1000FULL)
106 ecmd->advertising |= ADVERTISED_1000baseT_Full;
107
108 if (bmsr & BMSR_ANEGCOMPLETE) {
109 ecmd->lp_advertising = mii_get_an(mii, MII_LPA);
110 if (stat1000 & LPA_1000HALF)
111 ecmd->lp_advertising |=
112 ADVERTISED_1000baseT_Half;
113 if (stat1000 & LPA_1000FULL)
114 ecmd->lp_advertising |=
115 ADVERTISED_1000baseT_Full;
116 } else {
117 ecmd->lp_advertising = 0;
118 }
119
120 nego = ecmd->advertising & ecmd->lp_advertising;
121
122 if (nego & (ADVERTISED_1000baseT_Full |
123 ADVERTISED_1000baseT_Half)) {
124 ethtool_cmd_speed_set(ecmd, SPEED_1000);
125 ecmd->duplex = !!(nego & ADVERTISED_1000baseT_Full);
126 } else if (nego & (ADVERTISED_100baseT_Full |
127 ADVERTISED_100baseT_Half)) {
128 ethtool_cmd_speed_set(ecmd, SPEED_100);
129 ecmd->duplex = !!(nego & ADVERTISED_100baseT_Full);
130 } else {
131 ethtool_cmd_speed_set(ecmd, SPEED_10);
132 ecmd->duplex = !!(nego & ADVERTISED_10baseT_Full);
133 }
134 } else {
135 ecmd->autoneg = AUTONEG_DISABLE;
136
137 ethtool_cmd_speed_set(ecmd,
138 ((bmcr & BMCR_SPEED1000 &&
139 (bmcr & BMCR_SPEED100) == 0) ?
140 SPEED_1000 :
141 ((bmcr & BMCR_SPEED100) ?
142 SPEED_100 : SPEED_10)));
143 ecmd->duplex = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF;
144 }
145
146 mii->full_duplex = ecmd->duplex;
147
148 /* ignore maxtxpkt, maxrxpkt for now */
149
150 return 0;
151 }
152
153 /**
154 * mii_ethtool_sset - set settings that are specified in @ecmd
155 * @mii: MII interface
156 * @ecmd: requested ethtool_cmd
157 *
158 * Returns 0 for success, negative on error.
159 */
160 int mii_ethtool_sset(struct mii_if_info *mii, struct ethtool_cmd *ecmd)
161 {
162 struct net_device *dev = mii->dev;
163 u32 speed = ethtool_cmd_speed(ecmd);
164
165 if (speed != SPEED_10 &&
166 speed != SPEED_100 &&
167 speed != SPEED_1000)
168 return -EINVAL;
169 if (ecmd->duplex != DUPLEX_HALF && ecmd->duplex != DUPLEX_FULL)
170 return -EINVAL;
171 if (ecmd->port != PORT_MII)
172 return -EINVAL;
173 if (ecmd->transceiver != XCVR_INTERNAL)
174 return -EINVAL;
175 if (ecmd->phy_address != mii->phy_id)
176 return -EINVAL;
177 if (ecmd->autoneg != AUTONEG_DISABLE && ecmd->autoneg != AUTONEG_ENABLE)
178 return -EINVAL;
179 if ((speed == SPEED_1000) && (!mii->supports_gmii))
180 return -EINVAL;
181
182 /* ignore supported, maxtxpkt, maxrxpkt */
183
184 if (ecmd->autoneg == AUTONEG_ENABLE) {
185 u32 bmcr, advert, tmp;
186 u32 advert2 = 0, tmp2 = 0;
187
188 if ((ecmd->advertising & (ADVERTISED_10baseT_Half |
189 ADVERTISED_10baseT_Full |
190 ADVERTISED_100baseT_Half |
191 ADVERTISED_100baseT_Full |
192 ADVERTISED_1000baseT_Half |
193 ADVERTISED_1000baseT_Full)) == 0)
194 return -EINVAL;
195
196 /* advertise only what has been requested */
197 advert = mii->mdio_read(dev, mii->phy_id, MII_ADVERTISE);
198 tmp = advert & ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
199 if (mii->supports_gmii) {
200 advert2 = mii->mdio_read(dev, mii->phy_id, MII_CTRL1000);
201 tmp2 = advert2 & ~(ADVERTISE_1000HALF | ADVERTISE_1000FULL);
202 }
203 if (ecmd->advertising & ADVERTISED_10baseT_Half)
204 tmp |= ADVERTISE_10HALF;
205 if (ecmd->advertising & ADVERTISED_10baseT_Full)
206 tmp |= ADVERTISE_10FULL;
207 if (ecmd->advertising & ADVERTISED_100baseT_Half)
208 tmp |= ADVERTISE_100HALF;
209 if (ecmd->advertising & ADVERTISED_100baseT_Full)
210 tmp |= ADVERTISE_100FULL;
211 if (mii->supports_gmii) {
212 if (ecmd->advertising & ADVERTISED_1000baseT_Half)
213 tmp2 |= ADVERTISE_1000HALF;
214 if (ecmd->advertising & ADVERTISED_1000baseT_Full)
215 tmp2 |= ADVERTISE_1000FULL;
216 }
217 if (advert != tmp) {
218 mii->mdio_write(dev, mii->phy_id, MII_ADVERTISE, tmp);
219 mii->advertising = tmp;
220 }
221 if ((mii->supports_gmii) && (advert2 != tmp2))
222 mii->mdio_write(dev, mii->phy_id, MII_CTRL1000, tmp2);
223
224 /* turn on autonegotiation, and force a renegotiate */
225 bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
226 bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART);
227 mii->mdio_write(dev, mii->phy_id, MII_BMCR, bmcr);
228
229 mii->force_media = 0;
230 } else {
231 u32 bmcr, tmp;
232
233 /* turn off auto negotiation, set speed and duplexity */
234 bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
235 tmp = bmcr & ~(BMCR_ANENABLE | BMCR_SPEED100 |
236 BMCR_SPEED1000 | BMCR_FULLDPLX);
237 if (speed == SPEED_1000)
238 tmp |= BMCR_SPEED1000;
239 else if (speed == SPEED_100)
240 tmp |= BMCR_SPEED100;
241 if (ecmd->duplex == DUPLEX_FULL) {
242 tmp |= BMCR_FULLDPLX;
243 mii->full_duplex = 1;
244 } else
245 mii->full_duplex = 0;
246 if (bmcr != tmp)
247 mii->mdio_write(dev, mii->phy_id, MII_BMCR, tmp);
248
249 mii->force_media = 1;
250 }
251 return 0;
252 }
253
254 /**
255 * mii_check_gmii_support - check if the MII supports Gb interfaces
256 * @mii: the MII interface
257 */
258 int mii_check_gmii_support(struct mii_if_info *mii)
259 {
260 int reg;
261
262 reg = mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR);
263 if (reg & BMSR_ESTATEN) {
264 reg = mii->mdio_read(mii->dev, mii->phy_id, MII_ESTATUS);
265 if (reg & (ESTATUS_1000_TFULL | ESTATUS_1000_THALF))
266 return 1;
267 }
268
269 return 0;
270 }
271
272 /**
273 * mii_link_ok - is link status up/ok
274 * @mii: the MII interface
275 *
276 * Returns 1 if the MII reports link status up/ok, 0 otherwise.
277 */
278 int mii_link_ok (struct mii_if_info *mii)
279 {
280 /* first, a dummy read, needed to latch some MII phys */
281 mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR);
282 if (mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR) & BMSR_LSTATUS)
283 return 1;
284 return 0;
285 }
286
287 /**
288 * mii_nway_restart - restart NWay (autonegotiation) for this interface
289 * @mii: the MII interface
290 *
291 * Returns 0 on success, negative on error.
292 */
293 int mii_nway_restart (struct mii_if_info *mii)
294 {
295 int bmcr;
296 int r = -EINVAL;
297
298 /* if autoneg is off, it's an error */
299 bmcr = mii->mdio_read(mii->dev, mii->phy_id, MII_BMCR);
300
301 if (bmcr & BMCR_ANENABLE) {
302 bmcr |= BMCR_ANRESTART;
303 mii->mdio_write(mii->dev, mii->phy_id, MII_BMCR, bmcr);
304 r = 0;
305 }
306
307 return r;
308 }
309
310 /**
311 * mii_check_link - check MII link status
312 * @mii: MII interface
313 *
314 * If the link status changed (previous != current), call
315 * netif_carrier_on() if current link status is Up or call
316 * netif_carrier_off() if current link status is Down.
317 */
318 void mii_check_link (struct mii_if_info *mii)
319 {
320 int cur_link = mii_link_ok(mii);
321 int prev_link = netif_carrier_ok(mii->dev);
322
323 if (cur_link && !prev_link)
324 netif_carrier_on(mii->dev);
325 else if (prev_link && !cur_link)
326 netif_carrier_off(mii->dev);
327 }
328
329 /**
330 * mii_check_media - check the MII interface for a duplex change
331 * @mii: the MII interface
332 * @ok_to_print: OK to print link up/down messages
333 * @init_media: OK to save duplex mode in @mii
334 *
335 * Returns 1 if the duplex mode changed, 0 if not.
336 * If the media type is forced, always returns 0.
337 */
338 unsigned int mii_check_media (struct mii_if_info *mii,
339 unsigned int ok_to_print,
340 unsigned int init_media)
341 {
342 unsigned int old_carrier, new_carrier;
343 int advertise, lpa, media, duplex;
344 int lpa2 = 0;
345
346 /* if forced media, go no further */
347 if (mii->force_media)
348 return 0; /* duplex did not change */
349
350 /* check current and old link status */
351 old_carrier = netif_carrier_ok(mii->dev) ? 1 : 0;
352 new_carrier = (unsigned int) mii_link_ok(mii);
353
354 /* if carrier state did not change, this is a "bounce",
355 * just exit as everything is already set correctly
356 */
357 if ((!init_media) && (old_carrier == new_carrier))
358 return 0; /* duplex did not change */
359
360 /* no carrier, nothing much to do */
361 if (!new_carrier) {
362 netif_carrier_off(mii->dev);
363 if (ok_to_print)
364 netdev_info(mii->dev, "link down\n");
365 return 0; /* duplex did not change */
366 }
367
368 /*
369 * we have carrier, see who's on the other end
370 */
371 netif_carrier_on(mii->dev);
372
373 /* get MII advertise and LPA values */
374 if ((!init_media) && (mii->advertising))
375 advertise = mii->advertising;
376 else {
377 advertise = mii->mdio_read(mii->dev, mii->phy_id, MII_ADVERTISE);
378 mii->advertising = advertise;
379 }
380 lpa = mii->mdio_read(mii->dev, mii->phy_id, MII_LPA);
381 if (mii->supports_gmii)
382 lpa2 = mii->mdio_read(mii->dev, mii->phy_id, MII_STAT1000);
383
384 /* figure out media and duplex from advertise and LPA values */
385 media = mii_nway_result(lpa & advertise);
386 duplex = (media & ADVERTISE_FULL) ? 1 : 0;
387 if (lpa2 & LPA_1000FULL)
388 duplex = 1;
389
390 if (ok_to_print)
391 netdev_info(mii->dev, "link up, %uMbps, %s-duplex, lpa 0x%04X\n",
392 lpa2 & (LPA_1000FULL | LPA_1000HALF) ? 1000 :
393 media & (ADVERTISE_100FULL | ADVERTISE_100HALF) ?
394 100 : 10,
395 duplex ? "full" : "half",
396 lpa);
397
398 if ((init_media) || (mii->full_duplex != duplex)) {
399 mii->full_duplex = duplex;
400 return 1; /* duplex changed */
401 }
402
403 return 0; /* duplex did not change */
404 }
405
406 /**
407 * generic_mii_ioctl - main MII ioctl interface
408 * @mii_if: the MII interface
409 * @mii_data: MII ioctl data structure
410 * @cmd: MII ioctl command
411 * @duplex_chg_out: pointer to @duplex_changed status if there was no
412 * ioctl error
413 *
414 * Returns 0 on success, negative on error.
415 */
416 int generic_mii_ioctl(struct mii_if_info *mii_if,
417 struct mii_ioctl_data *mii_data, int cmd,
418 unsigned int *duplex_chg_out)
419 {
420 int rc = 0;
421 unsigned int duplex_changed = 0;
422
423 if (duplex_chg_out)
424 *duplex_chg_out = 0;
425
426 mii_data->phy_id &= mii_if->phy_id_mask;
427 mii_data->reg_num &= mii_if->reg_num_mask;
428
429 switch(cmd) {
430 case SIOCGMIIPHY:
431 mii_data->phy_id = mii_if->phy_id;
432 /* fall through */
433
434 case SIOCGMIIREG:
435 mii_data->val_out =
436 mii_if->mdio_read(mii_if->dev, mii_data->phy_id,
437 mii_data->reg_num);
438 break;
439
440 case SIOCSMIIREG: {
441 u16 val = mii_data->val_in;
442
443 if (mii_data->phy_id == mii_if->phy_id) {
444 switch(mii_data->reg_num) {
445 case MII_BMCR: {
446 unsigned int new_duplex = 0;
447 if (val & (BMCR_RESET|BMCR_ANENABLE))
448 mii_if->force_media = 0;
449 else
450 mii_if->force_media = 1;
451 if (mii_if->force_media &&
452 (val & BMCR_FULLDPLX))
453 new_duplex = 1;
454 if (mii_if->full_duplex != new_duplex) {
455 duplex_changed = 1;
456 mii_if->full_duplex = new_duplex;
457 }
458 break;
459 }
460 case MII_ADVERTISE:
461 mii_if->advertising = val;
462 break;
463 default:
464 /* do nothing */
465 break;
466 }
467 }
468
469 mii_if->mdio_write(mii_if->dev, mii_data->phy_id,
470 mii_data->reg_num, val);
471 break;
472 }
473
474 default:
475 rc = -EOPNOTSUPP;
476 break;
477 }
478
479 if ((rc == 0) && (duplex_chg_out) && (duplex_changed))
480 *duplex_chg_out = 1;
481
482 return rc;
483 }
484
485 MODULE_AUTHOR ("Jeff Garzik <jgarzik@pobox.com>");
486 MODULE_DESCRIPTION ("MII hardware support library");
487 MODULE_LICENSE("GPL");
488
489 EXPORT_SYMBOL(mii_link_ok);
490 EXPORT_SYMBOL(mii_nway_restart);
491 EXPORT_SYMBOL(mii_ethtool_gset);
492 EXPORT_SYMBOL(mii_ethtool_sset);
493 EXPORT_SYMBOL(mii_check_link);
494 EXPORT_SYMBOL(mii_check_media);
495 EXPORT_SYMBOL(mii_check_gmii_support);
496 EXPORT_SYMBOL(generic_mii_ioctl);
497
This page took 0.040609 seconds and 5 git commands to generate.