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