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