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
  36static u32 mii_get_an(struct mii_if_info *mii, u16 addr)
  37{
  38        int advert;
  39
  40        advert = mii->mdio_read(mii->dev, mii->phy_id, addr);
  41
  42        return mii_lpa_to_ethtool_lpa_t(advert);
  43}
  44
  45/**
  46 * mii_ethtool_gset - get settings that are specified in @ecmd
  47 * @mii: MII interface
  48 * @ecmd: requested ethtool_cmd
  49 *
  50 * The @ecmd parameter is expected to have been cleared before calling
  51 * mii_ethtool_gset().
  52 */
  53void mii_ethtool_gset(struct mii_if_info *mii, struct ethtool_cmd *ecmd)
  54{
  55        struct net_device *dev = mii->dev;
  56        u16 bmcr, bmsr, ctrl1000 = 0, stat1000 = 0;
  57        u32 nego;
  58
  59        ecmd->supported =
  60            (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
  61             SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
  62             SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII);
  63        if (mii->supports_gmii)
  64                ecmd->supported |= SUPPORTED_1000baseT_Half |
  65                        SUPPORTED_1000baseT_Full;
  66
  67        /* only supports twisted-pair */
  68        ecmd->port = PORT_MII;
  69
  70        /* only supports internal transceiver */
  71        ecmd->transceiver = XCVR_INTERNAL;
  72
  73        /* this isn't fully supported at higher layers */
  74        ecmd->phy_address = mii->phy_id;
  75        ecmd->mdio_support = ETH_MDIO_SUPPORTS_C22;
  76
  77        ecmd->advertising = ADVERTISED_TP | ADVERTISED_MII;
  78
  79        bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
  80        bmsr = mii->mdio_read(dev, mii->phy_id, MII_BMSR);
  81        if (mii->supports_gmii) {
  82                ctrl1000 = mii->mdio_read(dev, mii->phy_id, MII_CTRL1000);
  83                stat1000 = mii->mdio_read(dev, mii->phy_id, MII_STAT1000);
  84        }
  85
  86        ecmd->advertising |= mii_get_an(mii, MII_ADVERTISE);
  87        if (mii->supports_gmii)
  88                ecmd->advertising |=
  89                        mii_ctrl1000_to_ethtool_adv_t(ctrl1000);
  90
  91        if (bmcr & BMCR_ANENABLE) {
  92                ecmd->advertising |= ADVERTISED_Autoneg;
  93                ecmd->autoneg = AUTONEG_ENABLE;
  94
  95                if (bmsr & BMSR_ANEGCOMPLETE) {
  96                        ecmd->lp_advertising = mii_get_an(mii, MII_LPA);
  97                        ecmd->lp_advertising |=
  98                                        mii_stat1000_to_ethtool_lpa_t(stat1000);
  99                } else {
 100                        ecmd->lp_advertising = 0;
 101                }
 102
 103                nego = ecmd->advertising & ecmd->lp_advertising;
 104
 105                if (nego & (ADVERTISED_1000baseT_Full |
 106                            ADVERTISED_1000baseT_Half)) {
 107                        ethtool_cmd_speed_set(ecmd, SPEED_1000);
 108                        ecmd->duplex = !!(nego & ADVERTISED_1000baseT_Full);
 109                } else if (nego & (ADVERTISED_100baseT_Full |
 110                                   ADVERTISED_100baseT_Half)) {
 111                        ethtool_cmd_speed_set(ecmd, SPEED_100);
 112                        ecmd->duplex = !!(nego & ADVERTISED_100baseT_Full);
 113                } else {
 114                        ethtool_cmd_speed_set(ecmd, SPEED_10);
 115                        ecmd->duplex = !!(nego & ADVERTISED_10baseT_Full);
 116                }
 117        } else {
 118                ecmd->autoneg = AUTONEG_DISABLE;
 119
 120                ethtool_cmd_speed_set(ecmd,
 121                                      ((bmcr & BMCR_SPEED1000 &&
 122                                        (bmcr & BMCR_SPEED100) == 0) ?
 123                                       SPEED_1000 :
 124                                       ((bmcr & BMCR_SPEED100) ?
 125                                        SPEED_100 : SPEED_10)));
 126                ecmd->duplex = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF;
 127        }
 128
 129        mii->full_duplex = ecmd->duplex;
 130
 131        /* ignore maxtxpkt, maxrxpkt for now */
 132}
 133
 134/**
 135 * mii_ethtool_get_link_ksettings - get settings that are specified in @cmd
 136 * @mii: MII interface
 137 * @cmd: requested ethtool_link_ksettings
 138 *
 139 * The @cmd parameter is expected to have been cleared before calling
 140 * mii_ethtool_get_link_ksettings().
 141 */
 142void mii_ethtool_get_link_ksettings(struct mii_if_info *mii,
 143                                    struct ethtool_link_ksettings *cmd)
 144{
 145        struct net_device *dev = mii->dev;
 146        u16 bmcr, bmsr, ctrl1000 = 0, stat1000 = 0;
 147        u32 nego, supported, advertising, lp_advertising;
 148
 149        supported = (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
 150                     SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
 151                     SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII);
 152        if (mii->supports_gmii)
 153                supported |= SUPPORTED_1000baseT_Half |
 154                        SUPPORTED_1000baseT_Full;
 155
 156        /* only supports twisted-pair */
 157        cmd->base.port = PORT_MII;
 158
 159        /* this isn't fully supported at higher layers */
 160        cmd->base.phy_address = mii->phy_id;
 161        cmd->base.mdio_support = ETH_MDIO_SUPPORTS_C22;
 162
 163        advertising = ADVERTISED_TP | ADVERTISED_MII;
 164
 165        bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
 166        bmsr = mii->mdio_read(dev, mii->phy_id, MII_BMSR);
 167        if (mii->supports_gmii) {
 168                ctrl1000 = mii->mdio_read(dev, mii->phy_id, MII_CTRL1000);
 169                stat1000 = mii->mdio_read(dev, mii->phy_id, MII_STAT1000);
 170        }
 171
 172        advertising |= mii_get_an(mii, MII_ADVERTISE);
 173        if (mii->supports_gmii)
 174                advertising |= mii_ctrl1000_to_ethtool_adv_t(ctrl1000);
 175
 176        if (bmcr & BMCR_ANENABLE) {
 177                advertising |= ADVERTISED_Autoneg;
 178                cmd->base.autoneg = AUTONEG_ENABLE;
 179
 180                if (bmsr & BMSR_ANEGCOMPLETE) {
 181                        lp_advertising = mii_get_an(mii, MII_LPA);
 182                        lp_advertising |=
 183                                        mii_stat1000_to_ethtool_lpa_t(stat1000);
 184                } else {
 185                        lp_advertising = 0;
 186                }
 187
 188                nego = advertising & lp_advertising;
 189
 190                if (nego & (ADVERTISED_1000baseT_Full |
 191                            ADVERTISED_1000baseT_Half)) {
 192                        cmd->base.speed = SPEED_1000;
 193                        cmd->base.duplex = !!(nego & ADVERTISED_1000baseT_Full);
 194                } else if (nego & (ADVERTISED_100baseT_Full |
 195                                   ADVERTISED_100baseT_Half)) {
 196                        cmd->base.speed = SPEED_100;
 197                        cmd->base.duplex = !!(nego & ADVERTISED_100baseT_Full);
 198                } else {
 199                        cmd->base.speed = SPEED_10;
 200                        cmd->base.duplex = !!(nego & ADVERTISED_10baseT_Full);
 201                }
 202        } else {
 203                cmd->base.autoneg = AUTONEG_DISABLE;
 204
 205                cmd->base.speed = ((bmcr & BMCR_SPEED1000 &&
 206                                    (bmcr & BMCR_SPEED100) == 0) ?
 207                                   SPEED_1000 :
 208                                   ((bmcr & BMCR_SPEED100) ?
 209                                    SPEED_100 : SPEED_10));
 210                cmd->base.duplex = (bmcr & BMCR_FULLDPLX) ?
 211                        DUPLEX_FULL : DUPLEX_HALF;
 212
 213                lp_advertising = 0;
 214        }
 215
 216        mii->full_duplex = cmd->base.duplex;
 217
 218        ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported,
 219                                                supported);
 220        ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising,
 221                                                advertising);
 222        ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.lp_advertising,
 223                                                lp_advertising);
 224
 225        /* ignore maxtxpkt, maxrxpkt for now */
 226}
 227
 228/**
 229 * mii_ethtool_sset - set settings that are specified in @ecmd
 230 * @mii: MII interface
 231 * @ecmd: requested ethtool_cmd
 232 *
 233 * Returns 0 for success, negative on error.
 234 */
 235int mii_ethtool_sset(struct mii_if_info *mii, struct ethtool_cmd *ecmd)
 236{
 237        struct net_device *dev = mii->dev;
 238        u32 speed = ethtool_cmd_speed(ecmd);
 239
 240        if (speed != SPEED_10 &&
 241            speed != SPEED_100 &&
 242            speed != SPEED_1000)
 243                return -EINVAL;
 244        if (ecmd->duplex != DUPLEX_HALF && ecmd->duplex != DUPLEX_FULL)
 245                return -EINVAL;
 246        if (ecmd->port != PORT_MII)
 247                return -EINVAL;
 248        if (ecmd->transceiver != XCVR_INTERNAL)
 249                return -EINVAL;
 250        if (ecmd->phy_address != mii->phy_id)
 251                return -EINVAL;
 252        if (ecmd->autoneg != AUTONEG_DISABLE && ecmd->autoneg != AUTONEG_ENABLE)
 253                return -EINVAL;
 254        if ((speed == SPEED_1000) && (!mii->supports_gmii))
 255                return -EINVAL;
 256
 257        /* ignore supported, maxtxpkt, maxrxpkt */
 258
 259        if (ecmd->autoneg == AUTONEG_ENABLE) {
 260                u32 bmcr, advert, tmp;
 261                u32 advert2 = 0, tmp2 = 0;
 262
 263                if ((ecmd->advertising & (ADVERTISED_10baseT_Half |
 264                                          ADVERTISED_10baseT_Full |
 265                                          ADVERTISED_100baseT_Half |
 266                                          ADVERTISED_100baseT_Full |
 267                                          ADVERTISED_1000baseT_Half |
 268                                          ADVERTISED_1000baseT_Full)) == 0)
 269                        return -EINVAL;
 270
 271                /* advertise only what has been requested */
 272                advert = mii->mdio_read(dev, mii->phy_id, MII_ADVERTISE);
 273                tmp = advert & ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
 274                if (mii->supports_gmii) {
 275                        advert2 = mii->mdio_read(dev, mii->phy_id, MII_CTRL1000);
 276                        tmp2 = advert2 & ~(ADVERTISE_1000HALF | ADVERTISE_1000FULL);
 277                }
 278                tmp |= ethtool_adv_to_mii_adv_t(ecmd->advertising);
 279
 280                if (mii->supports_gmii)
 281                        tmp2 |=
 282                              ethtool_adv_to_mii_ctrl1000_t(ecmd->advertising);
 283                if (advert != tmp) {
 284                        mii->mdio_write(dev, mii->phy_id, MII_ADVERTISE, tmp);
 285                        mii->advertising = tmp;
 286                }
 287                if ((mii->supports_gmii) && (advert2 != tmp2))
 288                        mii->mdio_write(dev, mii->phy_id, MII_CTRL1000, tmp2);
 289
 290                /* turn on autonegotiation, and force a renegotiate */
 291                bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
 292                bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART);
 293                mii->mdio_write(dev, mii->phy_id, MII_BMCR, bmcr);
 294
 295                mii->force_media = 0;
 296        } else {
 297                u32 bmcr, tmp;
 298
 299                /* turn off auto negotiation, set speed and duplexity */
 300                bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
 301                tmp = bmcr & ~(BMCR_ANENABLE | BMCR_SPEED100 |
 302                               BMCR_SPEED1000 | BMCR_FULLDPLX);
 303                if (speed == SPEED_1000)
 304                        tmp |= BMCR_SPEED1000;
 305                else if (speed == SPEED_100)
 306                        tmp |= BMCR_SPEED100;
 307                if (ecmd->duplex == DUPLEX_FULL) {
 308                        tmp |= BMCR_FULLDPLX;
 309                        mii->full_duplex = 1;
 310                } else
 311                        mii->full_duplex = 0;
 312                if (bmcr != tmp)
 313                        mii->mdio_write(dev, mii->phy_id, MII_BMCR, tmp);
 314
 315                mii->force_media = 1;
 316        }
 317        return 0;
 318}
 319
 320/**
 321 * mii_ethtool_set_link_ksettings - set settings that are specified in @cmd
 322 * @mii: MII interfaces
 323 * @cmd: requested ethtool_link_ksettings
 324 *
 325 * Returns 0 for success, negative on error.
 326 */
 327int mii_ethtool_set_link_ksettings(struct mii_if_info *mii,
 328                                   const struct ethtool_link_ksettings *cmd)
 329{
 330        struct net_device *dev = mii->dev;
 331        u32 speed = cmd->base.speed;
 332
 333        if (speed != SPEED_10 &&
 334            speed != SPEED_100 &&
 335            speed != SPEED_1000)
 336                return -EINVAL;
 337        if (cmd->base.duplex != DUPLEX_HALF && cmd->base.duplex != DUPLEX_FULL)
 338                return -EINVAL;
 339        if (cmd->base.port != PORT_MII)
 340                return -EINVAL;
 341        if (cmd->base.phy_address != mii->phy_id)
 342                return -EINVAL;
 343        if (cmd->base.autoneg != AUTONEG_DISABLE &&
 344            cmd->base.autoneg != AUTONEG_ENABLE)
 345                return -EINVAL;
 346        if ((speed == SPEED_1000) && (!mii->supports_gmii))
 347                return -EINVAL;
 348
 349        /* ignore supported, maxtxpkt, maxrxpkt */
 350
 351        if (cmd->base.autoneg == AUTONEG_ENABLE) {
 352                u32 bmcr, advert, tmp;
 353                u32 advert2 = 0, tmp2 = 0;
 354                u32 advertising;
 355
 356                ethtool_convert_link_mode_to_legacy_u32(
 357                        &advertising, cmd->link_modes.advertising);
 358
 359                if ((advertising & (ADVERTISED_10baseT_Half |
 360                                    ADVERTISED_10baseT_Full |
 361                                    ADVERTISED_100baseT_Half |
 362                                    ADVERTISED_100baseT_Full |
 363                                    ADVERTISED_1000baseT_Half |
 364                                    ADVERTISED_1000baseT_Full)) == 0)
 365                        return -EINVAL;
 366
 367                /* advertise only what has been requested */
 368                advert = mii->mdio_read(dev, mii->phy_id, MII_ADVERTISE);
 369                tmp = advert & ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
 370                if (mii->supports_gmii) {
 371                        advert2 = mii->mdio_read(dev, mii->phy_id,
 372                                                 MII_CTRL1000);
 373                        tmp2 = advert2 &
 374                                ~(ADVERTISE_1000HALF | ADVERTISE_1000FULL);
 375                }
 376                tmp |= ethtool_adv_to_mii_adv_t(advertising);
 377
 378                if (mii->supports_gmii)
 379                        tmp2 |= ethtool_adv_to_mii_ctrl1000_t(advertising);
 380                if (advert != tmp) {
 381                        mii->mdio_write(dev, mii->phy_id, MII_ADVERTISE, tmp);
 382                        mii->advertising = tmp;
 383                }
 384                if ((mii->supports_gmii) && (advert2 != tmp2))
 385                        mii->mdio_write(dev, mii->phy_id, MII_CTRL1000, tmp2);
 386
 387                /* turn on autonegotiation, and force a renegotiate */
 388                bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
 389                bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART);
 390                mii->mdio_write(dev, mii->phy_id, MII_BMCR, bmcr);
 391
 392                mii->force_media = 0;
 393        } else {
 394                u32 bmcr, tmp;
 395
 396                /* turn off auto negotiation, set speed and duplexity */
 397                bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
 398                tmp = bmcr & ~(BMCR_ANENABLE | BMCR_SPEED100 |
 399                               BMCR_SPEED1000 | BMCR_FULLDPLX);
 400                if (speed == SPEED_1000)
 401                        tmp |= BMCR_SPEED1000;
 402                else if (speed == SPEED_100)
 403                        tmp |= BMCR_SPEED100;
 404                if (cmd->base.duplex == DUPLEX_FULL) {
 405                        tmp |= BMCR_FULLDPLX;
 406                        mii->full_duplex = 1;
 407                } else {
 408                        mii->full_duplex = 0;
 409                }
 410                if (bmcr != tmp)
 411                        mii->mdio_write(dev, mii->phy_id, MII_BMCR, tmp);
 412
 413                mii->force_media = 1;
 414        }
 415        return 0;
 416}
 417
 418/**
 419 * mii_check_gmii_support - check if the MII supports Gb interfaces
 420 * @mii: the MII interface
 421 */
 422int mii_check_gmii_support(struct mii_if_info *mii)
 423{
 424        int reg;
 425
 426        reg = mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR);
 427        if (reg & BMSR_ESTATEN) {
 428                reg = mii->mdio_read(mii->dev, mii->phy_id, MII_ESTATUS);
 429                if (reg & (ESTATUS_1000_TFULL | ESTATUS_1000_THALF))
 430                        return 1;
 431        }
 432
 433        return 0;
 434}
 435
 436/**
 437 * mii_link_ok - is link status up/ok
 438 * @mii: the MII interface
 439 *
 440 * Returns 1 if the MII reports link status up/ok, 0 otherwise.
 441 */
 442int mii_link_ok (struct mii_if_info *mii)
 443{
 444        /* first, a dummy read, needed to latch some MII phys */
 445        mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR);
 446        if (mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR) & BMSR_LSTATUS)
 447                return 1;
 448        return 0;
 449}
 450
 451/**
 452 * mii_nway_restart - restart NWay (autonegotiation) for this interface
 453 * @mii: the MII interface
 454 *
 455 * Returns 0 on success, negative on error.
 456 */
 457int mii_nway_restart (struct mii_if_info *mii)
 458{
 459        int bmcr;
 460        int r = -EINVAL;
 461
 462        /* if autoneg is off, it's an error */
 463        bmcr = mii->mdio_read(mii->dev, mii->phy_id, MII_BMCR);
 464
 465        if (bmcr & BMCR_ANENABLE) {
 466                bmcr |= BMCR_ANRESTART;
 467                mii->mdio_write(mii->dev, mii->phy_id, MII_BMCR, bmcr);
 468                r = 0;
 469        }
 470
 471        return r;
 472}
 473
 474/**
 475 * mii_check_link - check MII link status
 476 * @mii: MII interface
 477 *
 478 * If the link status changed (previous != current), call
 479 * netif_carrier_on() if current link status is Up or call
 480 * netif_carrier_off() if current link status is Down.
 481 */
 482void mii_check_link (struct mii_if_info *mii)
 483{
 484        int cur_link = mii_link_ok(mii);
 485        int prev_link = netif_carrier_ok(mii->dev);
 486
 487        if (cur_link && !prev_link)
 488                netif_carrier_on(mii->dev);
 489        else if (prev_link && !cur_link)
 490                netif_carrier_off(mii->dev);
 491}
 492
 493/**
 494 * mii_check_media - check the MII interface for a carrier/speed/duplex change
 495 * @mii: the MII interface
 496 * @ok_to_print: OK to print link up/down messages
 497 * @init_media: OK to save duplex mode in @mii
 498 *
 499 * Returns 1 if the duplex mode changed, 0 if not.
 500 * If the media type is forced, always returns 0.
 501 */
 502unsigned int mii_check_media (struct mii_if_info *mii,
 503                              unsigned int ok_to_print,
 504                              unsigned int init_media)
 505{
 506        unsigned int old_carrier, new_carrier;
 507        int advertise, lpa, media, duplex;
 508        int lpa2 = 0;
 509
 510        /* check current and old link status */
 511        old_carrier = netif_carrier_ok(mii->dev) ? 1 : 0;
 512        new_carrier = (unsigned int) mii_link_ok(mii);
 513
 514        /* if carrier state did not change, this is a "bounce",
 515         * just exit as everything is already set correctly
 516         */
 517        if ((!init_media) && (old_carrier == new_carrier))
 518                return 0; /* duplex did not change */
 519
 520        /* no carrier, nothing much to do */
 521        if (!new_carrier) {
 522                netif_carrier_off(mii->dev);
 523                if (ok_to_print)
 524                        netdev_info(mii->dev, "link down\n");
 525                return 0; /* duplex did not change */
 526        }
 527
 528        /*
 529         * we have carrier, see who's on the other end
 530         */
 531        netif_carrier_on(mii->dev);
 532
 533        if (mii->force_media) {
 534                if (ok_to_print)
 535                        netdev_info(mii->dev, "link up\n");
 536                return 0; /* duplex did not change */
 537        }
 538
 539        /* get MII advertise and LPA values */
 540        if ((!init_media) && (mii->advertising))
 541                advertise = mii->advertising;
 542        else {
 543                advertise = mii->mdio_read(mii->dev, mii->phy_id, MII_ADVERTISE);
 544                mii->advertising = advertise;
 545        }
 546        lpa = mii->mdio_read(mii->dev, mii->phy_id, MII_LPA);
 547        if (mii->supports_gmii)
 548                lpa2 = mii->mdio_read(mii->dev, mii->phy_id, MII_STAT1000);
 549
 550        /* figure out media and duplex from advertise and LPA values */
 551        media = mii_nway_result(lpa & advertise);
 552        duplex = (media & ADVERTISE_FULL) ? 1 : 0;
 553        if (lpa2 & LPA_1000FULL)
 554                duplex = 1;
 555
 556        if (ok_to_print)
 557                netdev_info(mii->dev, "link up, %uMbps, %s-duplex, lpa 0x%04X\n",
 558                            lpa2 & (LPA_1000FULL | LPA_1000HALF) ? 1000 :
 559                            media & (ADVERTISE_100FULL | ADVERTISE_100HALF) ?
 560                            100 : 10,
 561                            duplex ? "full" : "half",
 562                            lpa);
 563
 564        if ((init_media) || (mii->full_duplex != duplex)) {
 565                mii->full_duplex = duplex;
 566                return 1; /* duplex changed */
 567        }
 568
 569        return 0; /* duplex did not change */
 570}
 571
 572/**
 573 * generic_mii_ioctl - main MII ioctl interface
 574 * @mii_if: the MII interface
 575 * @mii_data: MII ioctl data structure
 576 * @cmd: MII ioctl command
 577 * @duplex_chg_out: pointer to @duplex_changed status if there was no
 578 *      ioctl error
 579 *
 580 * Returns 0 on success, negative on error.
 581 */
 582int generic_mii_ioctl(struct mii_if_info *mii_if,
 583                      struct mii_ioctl_data *mii_data, int cmd,
 584                      unsigned int *duplex_chg_out)
 585{
 586        int rc = 0;
 587        unsigned int duplex_changed = 0;
 588
 589        if (duplex_chg_out)
 590                *duplex_chg_out = 0;
 591
 592        mii_data->phy_id &= mii_if->phy_id_mask;
 593        mii_data->reg_num &= mii_if->reg_num_mask;
 594
 595        switch(cmd) {
 596        case SIOCGMIIPHY:
 597                mii_data->phy_id = mii_if->phy_id;
 598                fallthrough;
 599
 600        case SIOCGMIIREG:
 601                mii_data->val_out =
 602                        mii_if->mdio_read(mii_if->dev, mii_data->phy_id,
 603                                          mii_data->reg_num);
 604                break;
 605
 606        case SIOCSMIIREG: {
 607                u16 val = mii_data->val_in;
 608
 609                if (mii_data->phy_id == mii_if->phy_id) {
 610                        switch(mii_data->reg_num) {
 611                        case MII_BMCR: {
 612                                unsigned int new_duplex = 0;
 613                                if (val & (BMCR_RESET|BMCR_ANENABLE))
 614                                        mii_if->force_media = 0;
 615                                else
 616                                        mii_if->force_media = 1;
 617                                if (mii_if->force_media &&
 618                                    (val & BMCR_FULLDPLX))
 619                                        new_duplex = 1;
 620                                if (mii_if->full_duplex != new_duplex) {
 621                                        duplex_changed = 1;
 622                                        mii_if->full_duplex = new_duplex;
 623                                }
 624                                break;
 625                        }
 626                        case MII_ADVERTISE:
 627                                mii_if->advertising = val;
 628                                break;
 629                        default:
 630                                /* do nothing */
 631                                break;
 632                        }
 633                }
 634
 635                mii_if->mdio_write(mii_if->dev, mii_data->phy_id,
 636                                   mii_data->reg_num, val);
 637                break;
 638        }
 639
 640        default:
 641                rc = -EOPNOTSUPP;
 642                break;
 643        }
 644
 645        if ((rc == 0) && (duplex_chg_out) && (duplex_changed))
 646                *duplex_chg_out = 1;
 647
 648        return rc;
 649}
 650
 651MODULE_AUTHOR ("Jeff Garzik <jgarzik@pobox.com>");
 652MODULE_DESCRIPTION ("MII hardware support library");
 653MODULE_LICENSE("GPL");
 654
 655EXPORT_SYMBOL(mii_link_ok);
 656EXPORT_SYMBOL(mii_nway_restart);
 657EXPORT_SYMBOL(mii_ethtool_gset);
 658EXPORT_SYMBOL(mii_ethtool_get_link_ksettings);
 659EXPORT_SYMBOL(mii_ethtool_sset);
 660EXPORT_SYMBOL(mii_ethtool_set_link_ksettings);
 661EXPORT_SYMBOL(mii_check_link);
 662EXPORT_SYMBOL(mii_check_media);
 663EXPORT_SYMBOL(mii_check_gmii_support);
 664EXPORT_SYMBOL(generic_mii_ioctl);
 665
 666