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