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/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 (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
 478MODULE_AUTHOR ("Jeff Garzik <jgarzik@pobox.com>");
 479MODULE_DESCRIPTION ("MII hardware support library");
 480MODULE_LICENSE("GPL");
 481
 482EXPORT_SYMBOL(mii_link_ok);
 483EXPORT_SYMBOL(mii_nway_restart);
 484EXPORT_SYMBOL(mii_ethtool_gset);
 485EXPORT_SYMBOL(mii_ethtool_sset);
 486EXPORT_SYMBOL(mii_check_link);
 487EXPORT_SYMBOL(mii_check_media);
 488EXPORT_SYMBOL(mii_check_gmii_support);
 489EXPORT_SYMBOL(generic_mii_ioctl);
 490
 491
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.