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 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        /* if forced media, go no further */
 322        if (mii->force_media)
 323                return 0; /* duplex did not change */
 324
 325        /* check current and old link status */
 326        old_carrier = netif_carrier_ok(mii->dev) ? 1 : 0;
 327        new_carrier = (unsigned int) mii_link_ok(mii);
 328
 329        /* if carrier state did not change, this is a "bounce",
 330         * just exit as everything is already set correctly
 331         */
 332        if ((!init_media) && (old_carrier == new_carrier))
 333                return 0; /* duplex did not change */
 334
 335        /* no carrier, nothing much to do */
 336        if (!new_carrier) {
 337                netif_carrier_off(mii->dev);
 338                if (ok_to_print)
 339                        netdev_info(mii->dev, "link down\n");
 340                return 0; /* duplex did not change */
 341        }
 342
 343        /*
 344         * we have carrier, see who's on the other end
 345         */
 346        netif_carrier_on(mii->dev);
 347
 348        /* get MII advertise and LPA values */
 349        if ((!init_media) && (mii->advertising))
 350                advertise = mii->advertising;
 351        else {
 352                advertise = mii->mdio_read(mii->dev, mii->phy_id, MII_ADVERTISE);
 353                mii->advertising = advertise;
 354        }
 355        lpa = mii->mdio_read(mii->dev, mii->phy_id, MII_LPA);
 356        if (mii->supports_gmii)
 357                lpa2 = mii->mdio_read(mii->dev, mii->phy_id, MII_STAT1000);
 358
 359        /* figure out media and duplex from advertise and LPA values */
 360        media = mii_nway_result(lpa & advertise);
 361        duplex = (media & ADVERTISE_FULL) ? 1 : 0;
 362        if (lpa2 & LPA_1000FULL)
 363                duplex = 1;
 364
 365        if (ok_to_print)
 366                netdev_info(mii->dev, "link up, %uMbps, %s-duplex, lpa 0x%04X\n",
 367                            lpa2 & (LPA_1000FULL | LPA_1000HALF) ? 1000 :
 368                            media & (ADVERTISE_100FULL | ADVERTISE_100HALF) ?
 369                            100 : 10,
 370                            duplex ? "full" : "half",
 371                            lpa);
 372
 373        if ((init_media) || (mii->full_duplex != duplex)) {
 374                mii->full_duplex = duplex;
 375                return 1; /* duplex changed */
 376        }
 377
 378        return 0; /* duplex did not change */
 379}
 380
 381/**
 382 * generic_mii_ioctl - main MII ioctl interface
 383 * @mii_if: the MII interface
 384 * @mii_data: MII ioctl data structure
 385 * @cmd: MII ioctl command
 386 * @duplex_chg_out: pointer to @duplex_changed status if there was no
 387 *      ioctl error
 388 *
 389 * Returns 0 on success, negative on error.
 390 */
 391int generic_mii_ioctl(struct mii_if_info *mii_if,
 392                      struct mii_ioctl_data *mii_data, int cmd,
 393                      unsigned int *duplex_chg_out)
 394{
 395        int rc = 0;
 396        unsigned int duplex_changed = 0;
 397
 398        if (duplex_chg_out)
 399                *duplex_chg_out = 0;
 400
 401        mii_data->phy_id &= mii_if->phy_id_mask;
 402        mii_data->reg_num &= mii_if->reg_num_mask;
 403
 404        switch(cmd) {
 405        case SIOCGMIIPHY:
 406                mii_data->phy_id = mii_if->phy_id;
 407                /* fall through */
 408
 409        case SIOCGMIIREG:
 410                mii_data->val_out =
 411                        mii_if->mdio_read(mii_if->dev, mii_data->phy_id,
 412                                          mii_data->reg_num);
 413                break;
 414
 415        case SIOCSMIIREG: {
 416                u16 val = mii_data->val_in;
 417
 418                if (mii_data->phy_id == mii_if->phy_id) {
 419                        switch(mii_data->reg_num) {
 420                        case MII_BMCR: {
 421                                unsigned int new_duplex = 0;
 422                                if (val & (BMCR_RESET|BMCR_ANENABLE))
 423                                        mii_if->force_media = 0;
 424                                else
 425                                        mii_if->force_media = 1;
 426                                if (mii_if->force_media &&
 427                                    (val & BMCR_FULLDPLX))
 428                                        new_duplex = 1;
 429                                if (mii_if->full_duplex != new_duplex) {
 430                                        duplex_changed = 1;
 431                                        mii_if->full_duplex = new_duplex;
 432                                }
 433                                break;
 434                        }
 435                        case MII_ADVERTISE:
 436                                mii_if->advertising = val;
 437                                break;
 438                        default:
 439                                /* do nothing */
 440                                break;
 441                        }
 442                }
 443
 444                mii_if->mdio_write(mii_if->dev, mii_data->phy_id,
 445                                   mii_data->reg_num, val);
 446                break;
 447        }
 448
 449        default:
 450                rc = -EOPNOTSUPP;
 451                break;
 452        }
 453
 454        if ((rc == 0) && (duplex_chg_out) && (duplex_changed))
 455                *duplex_chg_out = 1;
 456
 457        return rc;
 458}
 459
 460MODULE_AUTHOR ("Jeff Garzik <jgarzik@pobox.com>");
 461MODULE_DESCRIPTION ("MII hardware support library");
 462MODULE_LICENSE("GPL");
 463
 464EXPORT_SYMBOL(mii_link_ok);
 465EXPORT_SYMBOL(mii_nway_restart);
 466EXPORT_SYMBOL(mii_ethtool_gset);
 467EXPORT_SYMBOL(mii_ethtool_sset);
 468EXPORT_SYMBOL(mii_check_link);
 469EXPORT_SYMBOL(mii_check_media);
 470EXPORT_SYMBOL(mii_check_gmii_support);
 471EXPORT_SYMBOL(generic_mii_ioctl);
 472
 473
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.