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