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