linux/drivers/net/mii.c
<<
>>
Prefs
   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 */
  43int 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 */
 131int 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 */
 228int 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 */
 248int 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 */
 263int 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 */
 288void 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 */
 308unsigned 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 */
 386int 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
 458MODULE_AUTHOR ("Jeff Garzik <jgarzik@pobox.com>");
 459MODULE_DESCRIPTION ("MII hardware support library");
 460MODULE_LICENSE("GPL");
 461
 462EXPORT_SYMBOL(mii_link_ok);
 463EXPORT_SYMBOL(mii_nway_restart);
 464EXPORT_SYMBOL(mii_ethtool_gset);
 465EXPORT_SYMBOL(mii_ethtool_sset);
 466EXPORT_SYMBOL(mii_check_link);
 467EXPORT_SYMBOL(mii_check_media);
 468EXPORT_SYMBOL(mii_check_gmii_support);
 469EXPORT_SYMBOL(generic_mii_ioctl);
 470
 471
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.