1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 /*
  27  * mii - MII/PHY support for MAC drivers
  28  *
  29  * Utility module to provide a consistent interface to a MAC driver accross
  30  * different implementations of PHY devices
  31  */
  32 
  33 #include <sys/types.h>
  34 #include <sys/debug.h>
  35 #include <sys/errno.h>
  36 #include <sys/param.h>
  37 #include <sys/sysmacros.h>
  38 #include <sys/stropts.h>
  39 #include <sys/stream.h>
  40 #include <sys/kmem.h>
  41 #include <sys/conf.h>
  42 #include <sys/ddi.h>
  43 #include <sys/sunddi.h>
  44 #include <sys/devops.h>
  45 #include <sys/modctl.h>
  46 #include <sys/cmn_err.h>
  47 #include <sys/miiregs.h>
  48 #include "dnet_mii.h"
  49 
  50 
  51 #ifdef DEBUG
  52 #define MIIDEBUG
  53 int miidebug = 0;
  54 #define MIITRACE 1
  55 #define MIIDUMP 2
  56 #define MIIPROBE 4
  57 #define MIICOMPAT 8
  58 #endif
  59 
  60 /* Local functions */
  61 static struct phydata *mii_get_valid_phydata(mii_handle_t mac, int phy);
  62 static void mii_portmon(mii_handle_t mac);
  63 
  64 /* Vendor specific callback function prototypes */
  65 static void dump_NS83840(mii_handle_t, int);
  66 static void dump_ICS1890(struct mii_info *, int);
  67 static int getspeed_NS83840(mii_handle_t, int, int *, int *);
  68 static int getspeed_82553(mii_handle_t, int, int *, int *);
  69 static int getspeed_ICS1890(mii_handle_t, int, int *, int *);
  70 static int getspeed_generic(mii_handle_t, int, int *, int *);
  71 static void postreset_ICS1890(mii_handle_t mac, int phy);
  72 static void postreset_NS83840(mii_handle_t mac, int phy);
  73 
  74 /*
  75  * MII Interface functions
  76  */
  77 
  78 /*
  79  * Register an instance of an MII interface user
  80  */
  81 
  82 int
  83 mii_create(dev_info_t *dip,             /* Passed to read/write functions */
  84             mii_writefunc_t writefunc,  /* How to write to a MII register */
  85             mii_readfunc_t readfunc,    /* How to read from a MII regster */
  86             mii_handle_t *macp)
  87 {
  88         mii_handle_t mac;
  89 
  90         /*  Allocate space for the mii structure */
  91         if ((mac = (mii_handle_t)
  92             kmem_zalloc(sizeof (struct mii_info), KM_NOSLEEP)) == NULL)
  93                 return (MII_NOMEM);
  94 
  95         mac->mii_write = writefunc;
  96         mac->mii_read = readfunc;
  97         mac->mii_dip = dip;
  98         *macp = mac;
  99         return (MII_SUCCESS);
 100 }
 101 
 102 /*
 103  * Returns true if PHY at address phy is accessible. This should be
 104  * considered the only function that takes a PHY address that can be called
 105  * before mii_init_phy. There should be at least one bit set in the status
 106  * register, and at least one clear
 107  */
 108 int
 109 mii_probe_phy(mii_handle_t mac, int phy)
 110 {
 111         ushort_t status;
 112         dev_info_t *dip;
 113 
 114         if (!mac || phy < 0 || phy > 31)
 115                 return (MII_PARAM);
 116 
 117         dip = mac->mii_dip;
 118 
 119         /* Clear any latched bits by reading twice */
 120         mac->mii_read(dip, phy, MII_STATUS);
 121         status = mac->mii_read(dip, phy, MII_STATUS);
 122 
 123 #ifdef MIIDEBUG
 124         mac->mii_read(dip, phy, MII_CONTROL);
 125         if (miidebug & MIIPROBE)
 126                 cmn_err(CE_NOTE, "PHY Probe: Control=%x, Status=%x",
 127                     mac->mii_read(dip, phy, MII_CONTROL), status);
 128 #endif
 129         /*
 130          * At least one bit in status should be clear (one of the error
 131          * bits), and there must be at least one bit set for the device
 132          * capabilities. Unconnected devices tend to show 0xffff, but 0x0000
 133          * has been seen.
 134          */
 135 
 136         if (status == 0xffff || status == 0x0000)
 137                 return (MII_PHYNOTPRESENT);
 138         return (MII_SUCCESS);
 139 }
 140 
 141 /*
 142  * Initialise PHY, and store info about it in the handle for future
 143  * reference when the MAC calls us. PHY Vendor-specific code here isolates
 144  * the LAN driver from worrying about different PHY implementations
 145  */
 146 
 147 int
 148 mii_init_phy(mii_handle_t mac, int phy)
 149 {
 150         ushort_t status;
 151         void *dip;
 152         struct phydata *phydata;
 153 
 154         if ((mac == (mii_handle_t)NULL) || phy < 0 || phy > 31)
 155                 return (MII_PARAM);
 156 
 157         dip = mac->mii_dip;
 158 
 159         /* Create a phydata structure for this new phy */
 160         if (mac->phys[phy])
 161                 return (MII_PHYPRESENT);
 162 
 163         mac->phys[phy] = phydata = (struct phydata *)
 164             kmem_zalloc(sizeof (struct phydata), KM_NOSLEEP);
 165 
 166         if (!phydata)
 167                 return (MII_NOMEM);
 168 
 169         phydata->id = (ulong_t)mac->mii_read(dip, phy, MII_PHYIDH) << 16;
 170         phydata->id |= (ulong_t)mac->mii_read(dip, phy, MII_PHYIDL);
 171         phydata->state = phy_state_unknown;
 172 
 173         /* Override speed and duplex mode from conf-file if present */
 174         phydata->fix_duplex =
 175             ddi_getprop(DDI_DEV_T_NONE,
 176             mac->mii_dip, DDI_PROP_DONTPASS, "full-duplex", 0);
 177 
 178         phydata->fix_speed =
 179             ddi_getprop(DDI_DEV_T_NONE,
 180             mac->mii_dip, DDI_PROP_DONTPASS, "speed", 0);
 181 
 182         status = mac->mii_read(dip, phy, MII_STATUS);
 183 
 184         /*
 185          * when explicitly setting speed or duplex, we must
 186          * disable autonegotiation
 187          */
 188         if (!(status & MII_STATUS_CANAUTONEG) ||
 189             phydata->fix_speed || phydata->fix_duplex) {
 190                 /*
 191                  * If local side cannot autonegotiate, we can't try to enable
 192                  * full duplex without the user's consent, because we cannot
 193                  * tell without AN if the partner can support it
 194                  */
 195                 if ((status & (MII_STATUS_100_BASEX | MII_STATUS_100_BASEX_FD |
 196                     MII_STATUS_100_BASE_T4)) && phydata->fix_speed == 0) {
 197                         phydata->fix_speed = 100;
 198                 } else if ((status & (MII_STATUS_10 | MII_STATUS_10_FD)) &&
 199                     phydata->fix_speed == 0) {
 200                         phydata->fix_speed = 10;
 201                 } else if (phydata->fix_speed == 0) {
 202                         /* A very stupid PHY would not be supported */
 203                         kmem_free(mac->phys[phy], sizeof (struct phydata));
 204                         mac->phys[phy] = NULL;
 205                         return (MII_NOTSUPPORTED);
 206                 }
 207                 /* mii_sync will sort out the speed selection on the PHY */
 208         } else
 209                 phydata->control = MII_CONTROL_ANE;
 210 
 211         switch (MII_PHY_MFG(phydata->id)) {
 212         case OUI_NATIONAL_SEMICONDUCTOR:
 213                 switch (MII_PHY_MODEL(phydata->id)) {
 214                 case NS_DP83840:
 215                         phydata->phy_postreset = postreset_NS83840;
 216                         phydata->phy_dump = dump_NS83840;
 217                         phydata->description =
 218                             "National Semiconductor DP-83840";
 219                         phydata->phy_getspeed = getspeed_NS83840;
 220                         break;
 221                 default:
 222                         phydata->description = "Unknown NS";
 223                         break;
 224                 }
 225                 break;
 226 
 227         case OUI_INTEL:
 228                 switch (MII_PHY_MODEL(phydata->id)) {
 229                 case INTEL_82553_CSTEP:
 230                         phydata->description = "Intel 82553 C-step";
 231                         phydata->phy_getspeed = getspeed_82553;
 232                         break;
 233                 case INTEL_82555:
 234                         phydata->description = "Intel 82555";
 235                         phydata->phy_getspeed = getspeed_82553;
 236                         break;
 237                 case INTEL_82562_EH:
 238                         phydata->description = "Intel 82562 EH";
 239                         phydata->phy_getspeed = getspeed_82553;
 240                         break;
 241                 case INTEL_82562_ET:
 242                         phydata->description = "Intel 82562 ET";
 243                         phydata->phy_getspeed = getspeed_82553;
 244                         break;
 245                 case INTEL_82562_EM:
 246                         phydata->description = "Intel 82562 EM";
 247                         phydata->phy_getspeed = getspeed_82553;
 248                         break;
 249                 default:
 250                         phydata->description = "Unknown INTEL";
 251                         break;
 252                 }
 253                 break;
 254 
 255         case OUI_ICS:
 256                 switch (MII_PHY_MODEL(phydata->id)) {
 257                 case ICS_1890:
 258                 case ICS_1889:
 259                         phydata->phy_postreset = postreset_ICS1890;
 260                         phydata->description = "ICS 1890/1889 PHY";
 261                         phydata->phy_getspeed = getspeed_ICS1890;
 262                         phydata->phy_dump = dump_ICS1890;
 263                         break;
 264                 default:
 265                         phydata->description = "ICS Unknown PHY";
 266                         break;
 267                 }
 268                 break;
 269 
 270         default: /* Non-standard PHYs, that encode weird IDs */
 271                 phydata->description = "Unknown PHY";
 272                 phydata->phy_dump = NULL;
 273                 phydata->phy_getspeed = getspeed_generic;
 274                 break;
 275         }
 276 
 277         /* Do all post-reset hacks and user settings */
 278         (void) mii_sync(mac, phy);
 279 
 280         if (ddi_getprop(DDI_DEV_T_NONE, mac->mii_dip, DDI_PROP_DONTPASS,
 281             "dump-phy", 0))
 282                 (void) mii_dump_phy(mac, phy);
 283 
 284         return (MII_SUCCESS);
 285 }
 286 
 287 /*
 288  * Cause a reset on a PHY
 289  */
 290 
 291 int
 292 mii_reset_phy(mii_handle_t mac, int phy, enum mii_wait_type wait)
 293 {
 294         int i;
 295         struct phydata *phyd;
 296         ushort_t control;
 297         if (!(phyd = mii_get_valid_phydata(mac, phy)))
 298                 return (MII_PARAM);
 299 
 300         /* Strobe the reset bit in the control register */
 301         mac->mii_write(mac->mii_dip, phy, MII_CONTROL,
 302             phyd->control | MII_CONTROL_RESET);
 303 
 304         phyd->state = phy_state_unknown;
 305 
 306         /*
 307          * This is likely to be very fast (ie, by the time we read the
 308          * control register once, the devices we have seen can have already
 309          * reset), but according to 802.3u 22.2.4.1.1, it could be up to .5 sec.
 310          */
 311         if (wait == mii_wait_interrupt || wait == mii_wait_user) {
 312                 for (i = 100; i--; ) {
 313                         control = mac->mii_read(mac->mii_dip, phy, MII_CONTROL);
 314                         if (!(control & MII_CONTROL_RESET))
 315                                 break;
 316                         drv_usecwait(10);
 317                 }
 318                 if (i)
 319                         goto reset_completed;
 320         }
 321 
 322         if (wait == mii_wait_user) {
 323                 for (i = 50; i--; ) {
 324                         control = mac->mii_read(mac->mii_dip, phy, MII_CONTROL);
 325                         if (!(control & MII_CONTROL_RESET))
 326                                 break;
 327                         delay(drv_usectohz(10000));
 328                 }
 329                 if (i)
 330                         goto reset_completed;
 331                 return (MII_HARDFAIL);  /* It MUST reset within this time */
 332 
 333         }
 334         return (MII_TIMEOUT);
 335 
 336 reset_completed:
 337         (void) mii_sync(mac, phy);
 338         return (MII_SUCCESS);
 339 }
 340 
 341 /*
 342  * This routine is called to synchronise the software and the PHY. It should
 343  * be called after the PHY is reset, and after initialising the PHY. This
 344  * routine is external because devices (DNET) can reset the PHY in ways beyond
 345  * the control of the mii interface. Should this happen, the driver is
 346  * required to call mii_sync().
 347  * If the PHY is resetting still when this is called, it will do nothing,
 348  * but, it will be retriggered when the portmon timer expires.
 349  */
 350 
 351 int
 352 mii_sync(mii_handle_t mac, int phy)
 353 {
 354         struct phydata *phyd = mac->phys[phy];
 355         int len, i, numprop;
 356         struct regprop {
 357                 int reg;
 358                 int value;
 359         } *regprop;
 360 
 361 #ifdef MIIDEBUG
 362         if (miidebug & MIITRACE)
 363                 cmn_err(CE_NOTE, "mii_sync (phy addr %d)", phy);
 364 #endif
 365 
 366         len = 0;
 367         /*
 368          * Conf file can specify a sequence of values to write to
 369          * the PHY registers if required
 370          */
 371         if (ddi_getlongprop(DDI_DEV_T_ANY, mac->mii_dip,
 372             DDI_PROP_DONTPASS, "phy-registers", (caddr_t)&regprop,
 373             &len) == DDI_PROP_SUCCESS) {
 374                 numprop = len / sizeof (struct regprop);
 375                 for (i = 0; i < numprop; i++) {
 376                         mac->mii_write(mac->mii_dip, phy,
 377                             regprop[i].reg, regprop[i].value);
 378 #ifdef MIIDEBUG
 379                         if (miidebug & MIITRACE)
 380                                 cmn_err(CE_NOTE, "PHY Write reg %d=%x",
 381                                     regprop[i].reg, regprop[i].value);
 382 #endif
 383                 }
 384                 kmem_free(regprop, len);
 385         } else {
 386                 mac->mii_write(mac->mii_dip, phy, MII_CONTROL, phyd->control);
 387                 if (phyd->phy_postreset)
 388                         phyd->phy_postreset(mac, phy);
 389                 if (phyd->fix_speed || phyd->fix_duplex) {
 390                         /* XXX function return value ignored */
 391                         (void) mii_fixspeed(mac, phy, phyd->fix_speed,
 392                             phyd->fix_duplex);
 393                 }
 394         }
 395         return (MII_SUCCESS);
 396 }
 397 
 398 /*
 399  * Disable full-duplex negotiation on the PHY. This is useful if the
 400  * driver or link-partner is advertising full duplex, but does not support
 401  * it properly (as some previous solaris drivers didn't)
 402  */
 403 
 404 int
 405 mii_disable_fullduplex(mii_handle_t mac, int phy)
 406 {
 407         void *dip = mac->mii_dip;
 408         ushort_t expansion,  miiadvert;
 409         /* dont advertise full duplex capabilites */
 410         const int fullduplex = MII_ABILITY_10BASE_T_FD
 411             | MII_ABILITY_100BASE_TX_FD;
 412 
 413         if (!(mac->mii_read(dip, phy, MII_STATUS) & MII_STATUS_CANAUTONEG)) {
 414                 /*
 415                  * Local side cannot autonegotiate, so full duplex should
 416                  * never be negotiated. Consider it as a success
 417                  */
 418                 return (MII_SUCCESS);
 419         }
 420 
 421         /* Change what we advertise if it includes full duplex */
 422 
 423         miiadvert = mac->mii_read(dip, phy, MII_AN_ADVERT);
 424         if (miiadvert & fullduplex)
 425                 mac->mii_write(dip, phy, MII_AN_ADVERT,
 426                     miiadvert & ~fullduplex);
 427 
 428         /* See what other end is able to do.  */
 429 
 430         expansion = mac->mii_read(dip, phy, MII_AN_EXPANSION);
 431 
 432         /*
 433          * Renegotiate if the link partner supports autonegotiation
 434          * If it doesn't, we will never have auto-negotiated full duplex
 435          * anyway
 436          */
 437 
 438         if (expansion & MII_AN_EXP_LPCANAN)
 439                 return (mii_rsan(mac, phy, mii_wait_none));
 440         else
 441                 return (MII_SUCCESS);
 442 }
 443 
 444 /*
 445  * (re)enable autonegotiation on a PHY.
 446  */
 447 
 448 int
 449 mii_autoneg_enab(mii_handle_t mac, int phy)
 450 {
 451         struct phydata *phyd;
 452         if (!(phyd = mii_get_valid_phydata(mac, phy)))
 453                 return (MII_PARAM);
 454         phyd->control |= MII_CONTROL_ANE;
 455         mac->mii_write(mac->mii_dip, phy, MII_CONTROL, phyd->control);
 456         return (MII_SUCCESS);
 457 }
 458 
 459 /*
 460  * Check the link status of a PHY connection
 461  */
 462 int
 463 mii_linkup(mii_handle_t mac, int phy)
 464 {
 465         ushort_t status;
 466 
 467         /*
 468          * Link status latches, so we need to read it twice, to make sure we
 469          * get its current status
 470          */
 471         mac->mii_read(mac->mii_dip, phy, MII_STATUS);
 472         status = mac->mii_read(mac->mii_dip, phy, MII_STATUS);
 473 
 474         if (status != 0xffff && (status & MII_STATUS_LINKUP))
 475                 return (1);
 476         else
 477                 return (0);
 478 }
 479 
 480 /*
 481  * Discover what speed the PHY is running at, irrespective of wheather it
 482  * autonegotiated this, or was fixed at that rate.
 483  */
 484 
 485 int
 486 mii_getspeed(mii_handle_t mac, int phy, int *speed, int *fulld)
 487 {
 488         struct phydata *phyd;
 489 
 490         if (!(phyd = mii_get_valid_phydata(mac, phy)))
 491                 return (MII_PARAM);
 492         if (!(phyd->control & MII_CONTROL_ANE)) {
 493                 /*
 494                  * user has requested fixed speed operation, return what we
 495                  * wrote to the control registerfrom control register
 496                  */
 497 
 498                 *speed = phyd->control & MII_CONTROL_100MB ? 100:10;
 499                 *fulld = phyd->control & MII_CONTROL_FDUPLEX ? 1:0;
 500                 return (MII_SUCCESS);
 501         }
 502 
 503         if (!phyd->phy_getspeed) /* No standard way to do this(!) */
 504                 return (MII_NOTSUPPORTED);
 505 
 506         return (phyd->phy_getspeed(mac, phy, speed, fulld));
 507 }
 508 
 509 /*
 510  * Fix the speed and duplex mode of a PHY
 511  */
 512 
 513 int
 514 mii_fixspeed(mii_handle_t mac, int phy, int speed, int fullduplex)
 515 {
 516         struct phydata *phyd;
 517 
 518 #ifdef MIIDEBUG
 519         cmn_err(CE_CONT, "!%s: setting speed to %d, %s duplex",
 520             ddi_get_name(mac->mii_dip), speed,
 521             fullduplex ? "full" : "half");
 522 #endif
 523 
 524         if (!(phyd = mii_get_valid_phydata(mac, phy)))
 525                 return (MII_PARAM);
 526         phyd->control &= ~MII_CONTROL_ANE;
 527 
 528         if (speed == 100)
 529                 phyd->control |= MII_CONTROL_100MB;
 530         else if (speed == 10)
 531                 phyd->control &= ~MII_CONTROL_100MB;
 532         else
 533                 cmn_err(CE_NOTE, "%s: mii does not support %d Mb/s speed",
 534                     ddi_get_name(mac->mii_dip), speed);
 535 
 536         if (fullduplex)
 537                 phyd->control |= MII_CONTROL_FDUPLEX;
 538         else
 539                 phyd->control &= ~MII_CONTROL_FDUPLEX;
 540 
 541         mac->mii_write(mac->mii_dip, phy, MII_CONTROL, phyd->control);
 542         phyd->fix_speed = speed;
 543         phyd->fix_duplex = fullduplex;
 544         return (MII_SUCCESS);
 545 }
 546 /*
 547  * Electrically isolate/unisolate the PHY
 548  */
 549 
 550 int
 551 mii_isolate(mii_handle_t mac, int phy)
 552 {
 553         struct phydata *phyd;
 554 
 555         if (!(phyd = mii_get_valid_phydata(mac, phy)))
 556                 return (MII_PARAM);
 557 
 558         phyd->control |= MII_CONTROL_ISOLATE;
 559         mac->mii_write(mac->mii_dip, phy, MII_CONTROL, phyd->control);
 560 
 561         /* Wait for device to settle */
 562         drv_usecwait(50);
 563         return (MII_SUCCESS);
 564 }
 565 
 566 int
 567 mii_unisolate(mii_handle_t mac, int phy)
 568 {
 569         struct phydata *phyd;
 570 
 571         if (!(phyd = mii_get_valid_phydata(mac, phy)))
 572                 return (MII_PARAM);
 573 
 574         phyd->control &= ~MII_CONTROL_ISOLATE;
 575         mac->mii_write(mac->mii_dip, phy, MII_CONTROL, phyd->control);
 576         return (MII_SUCCESS);
 577 }
 578 
 579 /*
 580  * Restart autonegotiation on a PHY
 581  */
 582 
 583 int
 584 mii_rsan(mii_handle_t mac, int phy, enum mii_wait_type wait)
 585 {
 586         int i;
 587         void *dip;
 588         struct phydata *phyd;
 589 
 590         if (wait == mii_wait_interrupt ||
 591             !(phyd = mii_get_valid_phydata(mac, phy)))
 592                 return (MII_PARAM);
 593 
 594         if (phyd->fix_speed)
 595                 return (MII_STATE);
 596 
 597         dip = mac->mii_dip;
 598 
 599         phyd->control |= MII_CONTROL_ANE;
 600         mac->mii_write(dip, phy, MII_CONTROL, phyd->control|MII_CONTROL_RSAN);
 601 
 602         /*
 603          * This can take ages (a second or so). It makes more sense to use
 604          * the port monitor rather than waiting for completion of this on the
 605          * PHY. It is pointless doing a busy wait here
 606          */
 607 
 608         if (wait == mii_wait_user) {
 609                 for (i = 200; i--; ) {
 610                         delay(drv_usectohz(10000));
 611                         if (mac->mii_read(dip, phy, MII_STATUS) &
 612                             MII_STATUS_ANDONE)
 613                                 return (MII_SUCCESS);
 614                 }
 615                 cmn_err(CE_NOTE,
 616                     "!%s:Timed out waiting for autonegotiation",
 617                     ddi_get_name(mac->mii_dip));
 618                 return (MII_TIMEOUT);
 619         }
 620         return (MII_TIMEOUT);
 621 }
 622 
 623 /*
 624  * Debuging function to dump contents of PHY registers
 625  */
 626 int
 627 mii_dump_phy(mii_handle_t mac, int phy)
 628 {
 629         struct phydata *phydat;
 630 
 631         char *miiregs[] = {
 632                 "Control             ",
 633                 "Status              ",
 634                 "PHY Id(H)           ",
 635                 "PHY Id(L)           ",
 636                 "Advertisement       ",
 637                 "Link Partner Ability",
 638                 "Expansion           ",
 639                 "Next Page Transmit  ",
 640                 0
 641         };
 642         int i;
 643 
 644         if (!(phydat = mii_get_valid_phydata(mac, phy)))
 645                 return (MII_PARAM);
 646 
 647         cmn_err(CE_NOTE, "%s: PHY %d, type %s", ddi_get_name(mac->mii_dip), phy,
 648             phydat->description ? phydat->description: "Unknown");
 649 
 650         for (i = 0; miiregs[i]; i++)
 651                 cmn_err(CE_NOTE, "%s:\t%x",
 652                     miiregs[i], mac->mii_read(mac->mii_dip, phy, i));
 653 
 654         if (phydat->phy_dump)
 655                 phydat->phy_dump((struct mii_info *)mac, phy);
 656 
 657         return (MII_SUCCESS);
 658 }
 659 
 660 /*
 661  * Start a periodic check to monitor the MII devices attached, and callback
 662  * to the MAC driver when the state on a device changes
 663  */
 664 
 665 int
 666 mii_start_portmon(mii_handle_t mac, mii_linkfunc_t notify, kmutex_t *lock)
 667 {
 668         if (mac->mii_linknotify || mac->portmon_timer)
 669                 return (MII_STATE);
 670         mac->mii_linknotify = notify;
 671         /*
 672          * NOTE: Portmon is normally called through a timeout. In the case
 673          * of starting off, we assume that the lock is already held
 674          */
 675         mac->lock = NULL; /* portmon wont try to aquire any lock this time */
 676         mii_portmon(mac);
 677         mac->lock = lock;
 678         return (MII_SUCCESS);
 679 }
 680 
 681 int
 682 mii_stop_portmon(mii_handle_t mac)
 683 {
 684         if (!mac->mii_linknotify || !mac->portmon_timer)
 685                 return (MII_STATE);
 686 
 687         mac->mii_linknotify = NULL;
 688         mac->lock = NULL;
 689         (void) untimeout(mac->portmon_timer);
 690         mac->portmon_timer = 0;
 691         return (MII_SUCCESS);
 692 }
 693 
 694 static void
 695 mii_portmon(mii_handle_t mac)
 696 {
 697         int i;
 698         enum mii_phy_state state;
 699         struct phydata *phydata;
 700 
 701         /*
 702          * There is a potential deadlock between this test and the
 703          * mutex_enter
 704          */
 705         if (!mac->mii_linknotify) /* Exiting */
 706                 return;
 707 
 708         if (mac->lock)
 709                 mutex_enter(mac->lock);
 710 
 711         /*
 712          * For each initialised phy, see if the link state has changed, and
 713          * callback to the mac driver if it has
 714          */
 715         for (i = 0; i < 32; i++) {
 716                 if ((phydata = mac->phys[i]) != 0) {
 717                         state = mii_linkup(mac, i) ?
 718                             phy_state_linkup : phy_state_linkdown;
 719                         if (state != phydata->state) {
 720 #ifdef MIIDEBUG
 721                                 if (miidebug)
 722                                         cmn_err(CE_NOTE, "%s: PHY %d link %s",
 723                                             ddi_get_name(mac->mii_dip), i,
 724                                             state == phy_state_linkup ?
 725                                             "up" : "down");
 726 #endif
 727                                 phydata->state = state;
 728                                 mac->mii_linknotify(mac->mii_dip, i, state);
 729                         }
 730                 }
 731         }
 732         /* Check the ports every 5 seconds */
 733         mac->portmon_timer = timeout((void (*)(void*))mii_portmon, (void *)mac,
 734             (clock_t)(5 * drv_usectohz(1000000)));
 735         if (mac->lock)
 736                 mutex_exit(mac->lock);
 737 }
 738 
 739 /*
 740  * Close a handle to the MII interface from a registered user
 741  */
 742 
 743 void
 744 mii_destroy(mii_handle_t mac)
 745 {
 746         /* Free per-PHY information */
 747         int i;
 748 
 749         (void) mii_stop_portmon(mac);
 750 
 751         for (i = 0; i < 32; i++)
 752                 if (mac->phys[i])
 753                         kmem_free(mac->phys[i], sizeof (struct phydata));
 754 
 755         kmem_free(mac, sizeof (*mac));
 756 }
 757 
 758 /*
 759  * Get a PHY data structure from an MII handle, and validate the common
 760  * parameters to the MII functions. Used to verify parameters in most MII
 761  * functions
 762  */
 763 static struct phydata *
 764 mii_get_valid_phydata(mii_handle_t mac, int phy)
 765 {
 766         if (!mac || phy > 31 || phy < 0 || !mac->phys[phy]) {
 767                 ASSERT(!"MII: Bad invocation");
 768                 return (NULL);
 769         }
 770         return (mac->phys[phy]);
 771 }
 772 /*
 773  * Device-specific routines - National Semiconductor
 774  */
 775 
 776 #define BIT(bit, value) ((value) & (1<<(bit)))
 777 static void
 778 dump_NS83840(mii_handle_t mac, int phy)
 779 {
 780         ushort_t reg;
 781         void *dip;
 782 
 783         dip = mac->mii_dip;
 784         cmn_err(CE_NOTE, "Disconnect count: %x",
 785             mac->mii_read(dip, phy, 0x12));
 786         cmn_err(CE_NOTE, "False Carrier detect count: %x",
 787             mac->mii_read(dip, phy, 0x13));
 788         cmn_err(CE_NOTE, "Receive error count: %x",
 789             mac->mii_read(dip, phy, 0x15));
 790         cmn_err(CE_NOTE, "Silicon revision: %x",
 791             mac->mii_read(dip, phy, 0x16));
 792         cmn_err(CE_NOTE, "PCS Configuration : %x",
 793             mac->mii_read(dip, phy, 0x17));
 794 
 795         cmn_err(CE_NOTE, "Loopback, Bypass and Receiver error mask: %x",
 796             mac->mii_read(dip, phy, 0x18));
 797         cmn_err(CE_NOTE, "Wired phy address: %x",
 798             mac->mii_read(dip, phy, 0x19)&0xf);
 799 
 800         reg = mac->mii_read(dip, phy, 0x1b);
 801         cmn_err(CE_NOTE, "10 Base T in %s mode",
 802             BIT(9, reg) ? "serial":"nibble");
 803 
 804         cmn_err(CE_NOTE, "%slink pulses, %sheartbeat, %s,%s squelch,jabber %s",
 805             BIT(reg, 5) ? "" : "no ",
 806             BIT(reg, 4) ? "" : "no ",
 807             BIT(reg, 3) ? "UTP" : "STP",
 808             BIT(reg, 2) ? "low" : "normal",
 809             BIT(reg, 0) ? "enabled" : "disabled");
 810 }
 811 
 812 static int
 813 getspeed_NS83840(mii_handle_t mac, int phy, int *speed, int *fulld)
 814 {
 815         int exten =  mac->mii_read(mac->mii_dip, phy, MII_AN_EXPANSION);
 816         if (exten & MII_AN_EXP_LPCANAN) {
 817                 /*
 818                  * Link partner can auto-neg, take speed from LP Ability
 819                  * register
 820                  */
 821                 int lpable, anadv, mask;
 822 
 823                 lpable = mac->mii_read(mac->mii_dip, phy, MII_AN_LPABLE);
 824                 anadv = mac->mii_read(mac->mii_dip, phy, MII_AN_ADVERT);
 825                 mask = anadv & lpable;
 826 
 827                 if (mask & MII_ABILITY_100BASE_TX_FD) {
 828                         *speed = 100;
 829                         *fulld = 1;
 830                 } else if (mask & MII_ABILITY_100BASE_T4) {
 831                         *speed = 100;
 832                         *fulld = 0;
 833                 } else if (mask & MII_ABILITY_100BASE_TX) {
 834                         *speed = 100;
 835                         *fulld = 0;
 836                 } else if (mask & MII_ABILITY_10BASE_T_FD) {
 837                         *speed = 10;
 838                         *fulld = 1;
 839                 } else if (mask & MII_ABILITY_10BASE_T) {
 840                         *speed = 10;
 841                         *fulld = 0;
 842                 }
 843         } else {
 844                 int addr = mac->mii_read(mac->mii_dip, phy, MII_83840_ADDR);
 845                 *speed = (addr & NS83840_ADDR_SPEED10) ? 10:100;
 846                 /* No fullduplex without autonegotiation on link partner */
 847                 *fulld = 0;
 848         }
 849         return (0);
 850 }
 851 
 852 /*
 853  * Device-specific routines - INTEL
 854  */
 855 
 856 static int
 857 getspeed_82553(mii_handle_t mac, int phy, int *speed, int *fulld)
 858 {
 859         int ex0 = mac->mii_read(mac->mii_dip, phy, MII_82553_EX0);
 860         *fulld = (ex0 & I82553_EX0_FDUPLEX) ? 1:0;
 861         *speed = (ex0 & I82553_EX0_100MB) ? 100:10;
 862         return (0);
 863 }
 864 
 865 /*
 866  * Device-specific routines - ICS
 867  */
 868 
 869 static int
 870 getspeed_ICS1890(mii_handle_t mac, int phy, int *speed, int *fulld)
 871 {
 872         ushort_t quickpoll = mac->mii_read(mac->mii_dip, phy, ICS_QUICKPOLL);
 873         *speed = (quickpoll & ICS_QUICKPOLL_100MB) ? 100 : 10;
 874         *fulld = (quickpoll & ICS_QUICKPOLL_FDUPLEX) ? 1 : 0;
 875         return (0);
 876 }
 877 
 878 static void
 879 dump_ICS1890(mii_handle_t mac, int phy)
 880 {
 881         ushort_t quickpoll = mac->mii_read(mac->mii_dip, phy, ICS_QUICKPOLL);
 882         cmn_err(CE_NOTE, "QuickPoll:%x (Speed:%d FullDuplex:%c) ",
 883             quickpoll,
 884             quickpoll & ICS_QUICKPOLL_100MB ? 100:10,
 885             quickpoll & ICS_QUICKPOLL_FDUPLEX ? 'Y' : 'N');
 886 }
 887 
 888 static void
 889 postreset_NS83840(mii_handle_t mac, int phy)
 890 {
 891         ushort_t reg;
 892         struct phydata *phyd = mac->phys[phy];
 893         /*
 894          * As per INTEL "PRO/100B Adapter Software Technical
 895          * Reference Manual", set bit 10 of MII register 23.
 896          * National Semiconductor documentation shows this as
 897          * "reserved, write to as zero". We also set the
 898          * "f_connect" bit, also as requested by the PRO/100B
 899          * doc
 900          */
 901 
 902         reg = mac->mii_read(mac->mii_dip, phy, 23) | (1<<10) | (1<<5);
 903         mac->mii_write(mac->mii_dip, phy, 23, reg);
 904 
 905         /*
 906          * Some of thses PHYs seem to reset with the wrong value in the
 907          * AN advertisment register. It should containt 1e1, indicating that
 908          * the device can do 802.3 10BASE-T, 10BASE-T Full duplex, 100BASE-TX,
 909          * and 100 BASE-TX full duplex. Instead it seems to advertise only
 910          * 100BASE-TX Full duplex. The result of this is that the device will
 911          * NOT autonegotiate at all against a 10MB only or 100MB/Half duplex
 912          * autonegotiating hub
 913          * NEEDSWORK:
 914          * There is possibly a time-dependancy here.
 915          * If the autonegotiation has completed BEFORE we get to here
 916          * (after the reset) then this could possibly have not effect
 917          */
 918         if (!phyd->fix_speed) {
 919 #ifdef MIIDEBUG
 920                 if (miidebug & MIICOMPAT)
 921                         cmn_err(CE_NOTE, "Reset value of AN_ADV reg:%x",
 922                             mac->mii_read(mac->mii_dip, phy, MII_AN_ADVERT));
 923 #endif
 924                 mac->mii_write(mac->mii_dip, phy, MII_AN_ADVERT, 0x1e1);
 925         }
 926 }
 927 
 928 void
 929 postreset_ICS1890(mii_handle_t mac, int phy)
 930 {
 931         /* This device comes up isolated if no link is found */
 932         (void) mii_unisolate(mac, phy);
 933 }
 934 
 935 /*
 936  * generic getspeed routine
 937  */
 938 static int
 939 getspeed_generic(mii_handle_t mac, int phy, int *speed, int *fulld)
 940 {
 941         int exten =  mac->mii_read(mac->mii_dip, phy, MII_AN_EXPANSION);
 942         if (exten & MII_AN_EXP_LPCANAN) {
 943                 /*
 944                  * Link partner can auto-neg, take speed from LP Ability
 945                  * register
 946                  */
 947                 int lpable, anadv, mask;
 948 
 949                 lpable = mac->mii_read(mac->mii_dip, phy, MII_AN_LPABLE);
 950                 anadv = mac->mii_read(mac->mii_dip, phy, MII_AN_ADVERT);
 951                 mask = anadv & lpable;
 952 
 953                 if (mask & MII_ABILITY_100BASE_TX_FD) {
 954                         *speed = 100;
 955                         *fulld = 1;
 956                 } else if (mask & MII_ABILITY_100BASE_T4) {
 957                         *speed = 100;
 958                         *fulld = 0;
 959                 } else if (mask & MII_ABILITY_100BASE_TX) {
 960                         *speed = 100;
 961                         *fulld = 0;
 962                 } else if (mask & MII_ABILITY_10BASE_T_FD) {
 963                         *speed = 10;
 964                         *fulld = 1;
 965                 } else if (mask & MII_ABILITY_10BASE_T) {
 966                         *speed = 10;
 967                         *fulld = 0;
 968                 }
 969         } else {
 970                 /*
 971                  * Link partner cannot auto-neg, it would be nice if we
 972                  * could figure out what the device selected.  (NWay?)
 973                  */
 974                 *speed = 0;
 975                 *fulld = 0;
 976         }
 977         return (MII_SUCCESS);
 978 }