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