Root/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
36static 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 */
63int 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 */
154int 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 */
251int 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 */
271int 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 */
286int 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 */
311void 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 */
331unsigned 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 */
409int 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 (!capable(CAP_NET_ADMIN))
437            return -EPERM;
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
481MODULE_AUTHOR ("Jeff Garzik <jgarzik@pobox.com>");
482MODULE_DESCRIPTION ("MII hardware support library");
483MODULE_LICENSE("GPL");
484
485EXPORT_SYMBOL(mii_link_ok);
486EXPORT_SYMBOL(mii_nway_restart);
487EXPORT_SYMBOL(mii_ethtool_gset);
488EXPORT_SYMBOL(mii_ethtool_sset);
489EXPORT_SYMBOL(mii_check_link);
490EXPORT_SYMBOL(mii_check_media);
491EXPORT_SYMBOL(mii_check_gmii_support);
492EXPORT_SYMBOL(generic_mii_ioctl);
493
494

Archive Download this file



interactive