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_sset - set settings that are specified in @ecmd
 139 * @mii: MII interface
 140 * @ecmd: requested ethtool_cmd
 141 *
 142 * Returns 0 for success, negative on error.
 143 */
 144int mii_ethtool_sset(struct mii_if_info *mii, struct ethtool_cmd *ecmd)
 145{
 146        struct net_device *dev = mii->dev;
 147        u32 speed = ethtool_cmd_speed(ecmd);
 148
 149        if (speed != SPEED_10 &&
 150            speed != SPEED_100 &&
 151            speed != SPEED_1000)
 152                return -EINVAL;
 153        if (ecmd->duplex != DUPLEX_HALF && ecmd->duplex != DUPLEX_FULL)
 154                return -EINVAL;
 155        if (ecmd->port != PORT_MII)
 156                return -EINVAL;
 157        if (ecmd->transceiver != XCVR_INTERNAL)
 158                return -EINVAL;
 159        if (ecmd->phy_address != mii->phy_id)
 160                return -EINVAL;
 161        if (ecmd->autoneg != AUTONEG_DISABLE && ecmd->autoneg != AUTONEG_ENABLE)
 162                return -EINVAL;
 163        if ((speed == SPEED_1000) && (!mii->supports_gmii))
 164                return -EINVAL;
 165
 166        /* ignore supported, maxtxpkt, maxrxpkt */
 167
 168        if (ecmd->autoneg == AUTONEG_ENABLE) {
 169                u32 bmcr, advert, tmp;
 170                u32 advert2 = 0, tmp2 = 0;
 171
 172                if ((ecmd->advertising & (ADVERTISED_10baseT_Half |
 173                                          ADVERTISED_10baseT_Full |
 174                                          ADVERTISED_100baseT_Half |
 175                                          ADVERTISED_100baseT_Full |
 176                                          ADVERTISED_1000baseT_Half |
 177                                          ADVERTISED_1000baseT_Full)) == 0)
 178                        return -EINVAL;
 179
 180                /* advertise only what has been requested */
 181                advert = mii->mdio_read(dev, mii->phy_id, MII_ADVERTISE);
 182                tmp = advert & ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
 183                if (mii->supports_gmii) {
 184                        advert2 = mii->mdio_read(dev, mii->phy_id, MII_CTRL1000);
 185                        tmp2 = advert2 & ~(ADVERTISE_1000HALF | ADVERTISE_1000FULL);
 186                }
 187                tmp |= ethtool_adv_to_mii_adv_t(ecmd->advertising);
 188
 189                if (mii->supports_gmii)
 190                        tmp2 |=
 191                              ethtool_adv_to_mii_ctrl1000_t(ecmd->advertising);
 192                if (advert != tmp) {
 193                        mii->mdio_write(dev, mii->phy_id, MII_ADVERTISE, tmp);
 194                        mii->advertising = tmp;
 195                }
 196                if ((mii->supports_gmii) && (advert2 != tmp2))
 197                        mii->mdio_write(dev, mii->phy_id, MII_CTRL1000, tmp2);
 198
 199                /* turn on autonegotiation, and force a renegotiate */
 200                bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
 201                bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART);
 202                mii->mdio_write(dev, mii->phy_id, MII_BMCR, bmcr);
 203
 204                mii->force_media = 0;
 205        } else {
 206                u32 bmcr, tmp;
 207
 208                /* turn off auto negotiation, set speed and duplexity */
 209                bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
 210                tmp = bmcr & ~(BMCR_ANENABLE | BMCR_SPEED100 |
 211                               BMCR_SPEED1000 | BMCR_FULLDPLX);
 212                if (speed == SPEED_1000)
 213                        tmp |= BMCR_SPEED1000;
 214                else if (speed == SPEED_100)
 215                        tmp |= BMCR_SPEED100;
 216                if (ecmd->duplex == DUPLEX_FULL) {
 217                        tmp |= BMCR_FULLDPLX;
 218                        mii->full_duplex = 1;
 219                } else
 220                        mii->full_duplex = 0;
 221                if (bmcr != tmp)
 222                        mii->mdio_write(dev, mii->phy_id, MII_BMCR, tmp);
 223
 224                mii->force_media = 1;
 225        }
 226        return 0;
 227}
 228
 229/**
 230 * mii_check_gmii_support - check if the MII supports Gb interfaces
 231 * @mii: the MII interface
 232 */
 233int mii_check_gmii_support(struct mii_if_info *mii)
 234{
 235        int reg;
 236
 237        reg = mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR);
 238        if (reg & BMSR_ESTATEN) {
 239                reg = mii->mdio_read(mii->dev, mii->phy_id, MII_ESTATUS);
 240                if (reg & (ESTATUS_1000_TFULL | ESTATUS_1000_THALF))
 241                        return 1;
 242        }
 243
 244        return 0;
 245}
 246
 247/**
 248 * mii_link_ok - is link status up/ok
 249 * @mii: the MII interface
 250 *
 251 * Returns 1 if the MII reports link status up/ok, 0 otherwise.
 252 */
 253int mii_link_ok (struct mii_if_info *mii)
 254{
 255        /* first, a dummy read, needed to latch some MII phys */
 256        mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR);
 257        if (mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR) & BMSR_LSTATUS)
 258                return 1;
 259        return 0;
 260}
 261
 262/**
 263 * mii_nway_restart - restart NWay (autonegotiation) for this interface
 264 * @mii: the MII interface
 265 *
 266 * Returns 0 on success, negative on error.
 267 */
 268int mii_nway_restart (struct mii_if_info *mii)
 269{
 270        int bmcr;
 271        int r = -EINVAL;
 272
 273        /* if autoneg is off, it's an error */
 274        bmcr = mii->mdio_read(mii->dev, mii->phy_id, MII_BMCR);
 275
 276        if (bmcr & BMCR_ANENABLE) {
 277                bmcr |= BMCR_ANRESTART;
 278                mii->mdio_write(mii->dev, mii->phy_id, MII_BMCR, bmcr);
 279                r = 0;
 280        }
 281
 282        return r;
 283}
 284
 285/**
 286 * mii_check_link - check MII link status
 287 * @mii: MII interface
 288 *
 289 * If the link status changed (previous != current), call
 290 * netif_carrier_on() if current link status is Up or call
 291 * netif_carrier_off() if current link status is Down.
 292 */
 293void mii_check_link (struct mii_if_info *mii)
 294{
 295        int cur_link = mii_link_ok(mii);
 296        int prev_link = netif_carrier_ok(mii->dev);
 297
 298        if (cur_link && !prev_link)
 299                netif_carrier_on(mii->dev);
 300        else if (prev_link && !cur_link)
 301                netif_carrier_off(mii->dev);
 302}
 303
 304/**
 305 * mii_check_media - check the MII interface for a carrier/speed/duplex change
 306 * @mii: the MII interface
 307 * @ok_to_print: OK to print link up/down messages
 308 * @init_media: OK to save duplex mode in @mii
 309 *
 310 * Returns 1 if the duplex mode changed, 0 if not.
 311 * If the media type is forced, always returns 0.
 312 */
 313unsigned int mii_check_media (struct mii_if_info *mii,
 314                              unsigned int ok_to_print,
 315                              unsigned int init_media)
 316{
 317        unsigned int old_carrier, new_carrier;
 318        int advertise, lpa, media, duplex;
 319        int lpa2 = 0;
 320
 321        /* check current and old link status */
 322        old_carrier = netif_carrier_ok(mii->dev) ? 1 : 0;
 323        new_carrier = (unsigned int) mii_link_ok(mii);
 324
 325        /* if carrier state did not change, this is a "bounce",
 326         * just exit as everything is already set correctly
 327         */
 328        if ((!init_media) && (old_carrier == new_carrier))
 329                return 0; /* duplex did not change */
 330
 331        /* no carrier, nothing much to do */
 332        if (!new_carrier) {
 333                netif_carrier_off(mii->dev);
 334                if (ok_to_print)
 335                        netdev_info(mii->dev, "link down\n");
 336                return 0; /* duplex did not change */
 337        }
 338
 339        /*
 340         * we have carrier, see who's on the other end
 341         */
 342        netif_carrier_on(mii->dev);
 343
 344        if (mii->force_media) {
 345                if (ok_to_print)
 346                        netdev_info(mii->dev, "link up\n");
 347                return 0; /* duplex did not change */
 348        }
 349
 350        /* get MII advertise and LPA values */
 351        if ((!init_media) && (mii->advertising))
 352                advertise = mii->advertising;
 353        else {
 354                advertise = mii->mdio_read(mii->dev, mii->phy_id, MII_ADVERTISE);
 355                mii->advertising = advertise;
 356        }
 357        lpa = mii->mdio_read(mii->dev, mii->phy_id, MII_LPA);
 358        if (mii->supports_gmii)
 359                lpa2 = mii->mdio_read(mii->dev, mii->phy_id, MII_STAT1000);
 360
 361        /* figure out media and duplex from advertise and LPA values */
 362        media = mii_nway_result(lpa & advertise);
 363        duplex = (media & ADVERTISE_FULL) ? 1 : 0;
 364        if (lpa2 & LPA_1000FULL)
 365                duplex = 1;
 366
 367        if (ok_to_print)
 368                netdev_info(mii->dev, "link up, %uMbps, %s-duplex, lpa 0x%04X\n",
 369                            lpa2 & (LPA_1000FULL | LPA_1000HALF) ? 1000 :
 370                            media & (ADVERTISE_100FULL | ADVERTISE_100HALF) ?
 371                            100 : 10,
 372                            duplex ? "full" : "half",
 373                            lpa);
 374
 375        if ((init_media) || (mii->full_duplex != duplex)) {
 376                mii->full_duplex = duplex;
 377                return 1; /* duplex changed */
 378        }
 379
 380        return 0; /* duplex did not change */
 381}
 382
 383/**
 384 * generic_mii_ioctl - main MII ioctl interface
 385 * @mii_if: the MII interface
 386 * @mii_data: MII ioctl data structure
 387 * @cmd: MII ioctl command
 388 * @duplex_chg_out: pointer to @duplex_changed status if there was no
 389 *      ioctl error
 390 *
 391 * Returns 0 on success, negative on error.
 392 */
 393int generic_mii_ioctl(struct mii_if_info *mii_if,
 394                      struct mii_ioctl_data *mii_data, int cmd,
 395                      unsigned int *duplex_chg_out)
 396{
 397        int rc = 0;
 398        unsigned int duplex_changed = 0;
 399
 400        if (duplex_chg_out)
 401                *duplex_chg_out = 0;
 402
 403        mii_data->phy_id &= mii_if->phy_id_mask;
 404        mii_data->reg_num &= mii_if->reg_num_mask;
 405
 406        switch(cmd) {
 407        case SIOCGMIIPHY:
 408                mii_data->phy_id = mii_if->phy_id;
 409                /* fall through */
 410
 411        case SIOCGMIIREG:
 412                mii_data->val_out =
 413                        mii_if->mdio_read(mii_if->dev, mii_data->phy_id,
 414                                          mii_data->reg_num);
 415                break;
 416
 417        case SIOCSMIIREG: {
 418                u16 val = mii_data->val_in;
 419
 420                if (mii_data->phy_id == mii_if->phy_id) {
 421                        switch(mii_data->reg_num) {
 422                        case MII_BMCR: {
 423                                unsigned int new_duplex = 0;
 424                                if (val & (BMCR_RESET|BMCR_ANENABLE))
 425                                        mii_if->force_media = 0;
 426                                else
 427                                        mii_if->force_media = 1;
 428                                if (mii_if->force_media &&
 429                                    (val & BMCR_FULLDPLX))
 430                                        new_duplex = 1;
 431                                if (mii_if->full_duplex != new_duplex) {
 432                                        duplex_changed = 1;
 433                                        mii_if->full_duplex = new_duplex;
 434                                }
 435                                break;
 436                        }
 437                        case MII_ADVERTISE:
 438                                mii_if->advertising = val;
 439                                break;
 440                        default:
 441                                /* do nothing */
 442                                break;
 443                        }
 444                }
 445
 446                mii_if->mdio_write(mii_if->dev, mii_data->phy_id,
 447                                   mii_data->reg_num, val);
 448                break;
 449        }
 450
 451        default:
 452                rc = -EOPNOTSUPP;
 453                break;
 454        }
 455
 456        if ((rc == 0) && (duplex_chg_out) && (duplex_changed))
 457                *duplex_chg_out = 1;
 458
 459        return rc;
 460}
 461
 462MODULE_AUTHOR ("Jeff Garzik <jgarzik@pobox.com>");
 463MODULE_DESCRIPTION ("MII hardware support library");
 464MODULE_LICENSE("GPL");
 465
 466EXPORT_SYMBOL(mii_link_ok);
 467EXPORT_SYMBOL(mii_nway_restart);
 468EXPORT_SYMBOL(mii_ethtool_gset);
 469EXPORT_SYMBOL(mii_ethtool_sset);
 470EXPORT_SYMBOL(mii_check_link);
 471EXPORT_SYMBOL(mii_check_media);
 472EXPORT_SYMBOL(mii_check_gmii_support);
 473EXPORT_SYMBOL(generic_mii_ioctl);
 474
 475