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