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