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