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/kmem.h> 38*9860Sgdamore@opensolaris.org #include <sys/conf.h> 39*9860Sgdamore@opensolaris.org #include <sys/ddi.h> 40*9860Sgdamore@opensolaris.org #include <sys/sunddi.h> 41*9860Sgdamore@opensolaris.org #include <sys/modctl.h> 42*9860Sgdamore@opensolaris.org #include <sys/cmn_err.h> 43*9860Sgdamore@opensolaris.org #include <sys/policy.h> 44*9860Sgdamore@opensolaris.org #include <sys/note.h> 45*9860Sgdamore@opensolaris.org #include <sys/strsun.h> 46*9860Sgdamore@opensolaris.org #include <sys/miiregs.h> 47*9860Sgdamore@opensolaris.org #include <sys/mac_provider.h> 48*9860Sgdamore@opensolaris.org #include <sys/mac_ether.h> 49*9860Sgdamore@opensolaris.org #include <sys/mii.h> 50*9860Sgdamore@opensolaris.org #include "miipriv.h" 51*9860Sgdamore@opensolaris.org 52*9860Sgdamore@opensolaris.org #define MII_SECOND 1000000 53*9860Sgdamore@opensolaris.org 54*9860Sgdamore@opensolaris.org /* indices into error array */ 55*9860Sgdamore@opensolaris.org enum { 56*9860Sgdamore@opensolaris.org MII_EOK = 0, 57*9860Sgdamore@opensolaris.org MII_ERESET, 58*9860Sgdamore@opensolaris.org MII_ESTART, 59*9860Sgdamore@opensolaris.org MII_ENOPHY, 60*9860Sgdamore@opensolaris.org MII_ECHECK, 61*9860Sgdamore@opensolaris.org }; 62*9860Sgdamore@opensolaris.org 63*9860Sgdamore@opensolaris.org static const char *mii_errors[] = { 64*9860Sgdamore@opensolaris.org "", 65*9860Sgdamore@opensolaris.org "Failure resetting PHY.", 66*9860Sgdamore@opensolaris.org "Failure starting PHY.", 67*9860Sgdamore@opensolaris.org "No Ethernet PHY found.", 68*9860Sgdamore@opensolaris.org "Failure reading PHY (removed?)" 69*9860Sgdamore@opensolaris.org }; 70*9860Sgdamore@opensolaris.org 71*9860Sgdamore@opensolaris.org /* Indexed by XCVR_ type */ 72*9860Sgdamore@opensolaris.org static const const char *mii_xcvr_types[] = { 73*9860Sgdamore@opensolaris.org "Undefined", 74*9860Sgdamore@opensolaris.org "Unknown", 75*9860Sgdamore@opensolaris.org "10 Mbps", 76*9860Sgdamore@opensolaris.org "100BASE-T4", 77*9860Sgdamore@opensolaris.org "100BASE-X", 78*9860Sgdamore@opensolaris.org "100BASE-T2", 79*9860Sgdamore@opensolaris.org "1000BASE-X", 80*9860Sgdamore@opensolaris.org "1000BASE-T" 81*9860Sgdamore@opensolaris.org }; 82*9860Sgdamore@opensolaris.org 83*9860Sgdamore@opensolaris.org static lb_property_t mii_loopmodes[] = { 84*9860Sgdamore@opensolaris.org { normal, "normal", PHY_LB_NONE }, 85*9860Sgdamore@opensolaris.org { internal, "PHY", PHY_LB_INT_PHY }, 86*9860Sgdamore@opensolaris.org { external, "10Mbps", PHY_LB_EXT_10 }, 87*9860Sgdamore@opensolaris.org { external, "100Mbps", PHY_LB_EXT_100 }, 88*9860Sgdamore@opensolaris.org { external, "1000Mbps", PHY_LB_EXT_1000 }, 89*9860Sgdamore@opensolaris.org }; 90*9860Sgdamore@opensolaris.org 91*9860Sgdamore@opensolaris.org /* state machine */ 92*9860Sgdamore@opensolaris.org typedef enum { 93*9860Sgdamore@opensolaris.org MII_STATE_PROBE = 0, 94*9860Sgdamore@opensolaris.org MII_STATE_RESET, 95*9860Sgdamore@opensolaris.org MII_STATE_START, 96*9860Sgdamore@opensolaris.org MII_STATE_RUN, 97*9860Sgdamore@opensolaris.org } mii_tstate_t; 98*9860Sgdamore@opensolaris.org 99*9860Sgdamore@opensolaris.org struct mii_handle { 100*9860Sgdamore@opensolaris.org dev_info_t *m_dip; 101*9860Sgdamore@opensolaris.org void *m_private; 102*9860Sgdamore@opensolaris.org mii_ops_t m_ops; 103*9860Sgdamore@opensolaris.org 104*9860Sgdamore@opensolaris.org kt_did_t m_tq_id; 105*9860Sgdamore@opensolaris.org kmutex_t m_lock; 106*9860Sgdamore@opensolaris.org kcondvar_t m_cv; 107*9860Sgdamore@opensolaris.org ddi_taskq_t *m_tq; 108*9860Sgdamore@opensolaris.org int m_flags; 109*9860Sgdamore@opensolaris.org 110*9860Sgdamore@opensolaris.org boolean_t m_started; 111*9860Sgdamore@opensolaris.org boolean_t m_suspending; 112*9860Sgdamore@opensolaris.org boolean_t m_suspended; 113*9860Sgdamore@opensolaris.org boolean_t m_notify; 114*9860Sgdamore@opensolaris.org int m_error; 115*9860Sgdamore@opensolaris.org mii_tstate_t m_tstate; 116*9860Sgdamore@opensolaris.org 117*9860Sgdamore@opensolaris.org #define MII_FLAG_EXIT 0x1 /* exit the thread */ 118*9860Sgdamore@opensolaris.org #define MII_FLAG_STOP 0x2 /* shutdown MII monitoring */ 119*9860Sgdamore@opensolaris.org #define MII_FLAG_RESET 0x4 /* reset the MII */ 120*9860Sgdamore@opensolaris.org #define MII_FLAG_PROBE 0x8 /* probe for PHYs */ 121*9860Sgdamore@opensolaris.org #define MII_FLAG_NOTIFY 0x10 /* notify about a change */ 122*9860Sgdamore@opensolaris.org #define MII_FLAG_SUSPEND 0x20 /* monitoring suspended */ 123*9860Sgdamore@opensolaris.org #define MII_FLAG_MACRESET 0x40 /* send reset to MAC */ 124*9860Sgdamore@opensolaris.org #define MII_FLAG_PHYSTART 0x80 /* start up the PHY */ 125*9860Sgdamore@opensolaris.org 126*9860Sgdamore@opensolaris.org /* device name for printing, e.g. "hme0" */ 127*9860Sgdamore@opensolaris.org char m_name[MODMAXNAMELEN + 16]; 128*9860Sgdamore@opensolaris.org 129*9860Sgdamore@opensolaris.org int m_addr; 130*9860Sgdamore@opensolaris.org phy_handle_t m_phys[32]; 131*9860Sgdamore@opensolaris.org phy_handle_t m_bogus_phy; 132*9860Sgdamore@opensolaris.org phy_handle_t *m_phy; 133*9860Sgdamore@opensolaris.org 134*9860Sgdamore@opensolaris.org link_state_t m_link; 135*9860Sgdamore@opensolaris.org 136*9860Sgdamore@opensolaris.org /* these start out undefined, but get values due to mac_prop_set */ 137*9860Sgdamore@opensolaris.org int m_en_aneg; 138*9860Sgdamore@opensolaris.org int m_en_10_hdx; 139*9860Sgdamore@opensolaris.org int m_en_10_fdx; 140*9860Sgdamore@opensolaris.org int m_en_100_t4; 141*9860Sgdamore@opensolaris.org int m_en_100_hdx; 142*9860Sgdamore@opensolaris.org int m_en_100_fdx; 143*9860Sgdamore@opensolaris.org int m_en_1000_hdx; 144*9860Sgdamore@opensolaris.org int m_en_1000_fdx; 145*9860Sgdamore@opensolaris.org int m_en_flowctrl; 146*9860Sgdamore@opensolaris.org 147*9860Sgdamore@opensolaris.org boolean_t m_cap_pause; 148*9860Sgdamore@opensolaris.org boolean_t m_cap_asmpause; 149*9860Sgdamore@opensolaris.org }; 150*9860Sgdamore@opensolaris.org 151*9860Sgdamore@opensolaris.org 152*9860Sgdamore@opensolaris.org static void _mii_task(void *); 153*9860Sgdamore@opensolaris.org static void _mii_probe_task(mii_handle_t); 154*9860Sgdamore@opensolaris.org 155*9860Sgdamore@opensolaris.org /* 156*9860Sgdamore@opensolaris.org * Loadable module structures/entrypoints 157*9860Sgdamore@opensolaris.org */ 158*9860Sgdamore@opensolaris.org 159*9860Sgdamore@opensolaris.org extern struct mod_ops mod_misc_ops; 160*9860Sgdamore@opensolaris.org 161*9860Sgdamore@opensolaris.org static struct modlmisc modlmisc = { 162*9860Sgdamore@opensolaris.org &mod_miscops, 163*9860Sgdamore@opensolaris.org "802.3 MII support", 164*9860Sgdamore@opensolaris.org }; 165*9860Sgdamore@opensolaris.org 166*9860Sgdamore@opensolaris.org static struct modlinkage modlinkage = { 167*9860Sgdamore@opensolaris.org MODREV_1, &modlmisc, NULL 168*9860Sgdamore@opensolaris.org }; 169*9860Sgdamore@opensolaris.org 170*9860Sgdamore@opensolaris.org int 171*9860Sgdamore@opensolaris.org _init(void) 172*9860Sgdamore@opensolaris.org { 173*9860Sgdamore@opensolaris.org return (mod_install(&modlinkage)); 174*9860Sgdamore@opensolaris.org } 175*9860Sgdamore@opensolaris.org 176*9860Sgdamore@opensolaris.org int 177*9860Sgdamore@opensolaris.org _fini(void) 178*9860Sgdamore@opensolaris.org { 179*9860Sgdamore@opensolaris.org return (mod_remove(&modlinkage)); 180*9860Sgdamore@opensolaris.org } 181*9860Sgdamore@opensolaris.org 182*9860Sgdamore@opensolaris.org int 183*9860Sgdamore@opensolaris.org _info(struct modinfo *modinfop) 184*9860Sgdamore@opensolaris.org { 185*9860Sgdamore@opensolaris.org return (mod_info(&modlinkage, modinfop)); 186*9860Sgdamore@opensolaris.org } 187*9860Sgdamore@opensolaris.org 188*9860Sgdamore@opensolaris.org void 189*9860Sgdamore@opensolaris.org _mii_error(mii_handle_t mh, int errno) 190*9860Sgdamore@opensolaris.org { 191*9860Sgdamore@opensolaris.org /* 192*9860Sgdamore@opensolaris.org * This dumps an error message, but it avoids filling the log with 193*9860Sgdamore@opensolaris.org * repeated error messages. 194*9860Sgdamore@opensolaris.org */ 195*9860Sgdamore@opensolaris.org if (mh->m_error != errno) { 196*9860Sgdamore@opensolaris.org cmn_err(CE_WARN, "%s: %s", mh->m_name, mii_errors[errno]); 197*9860Sgdamore@opensolaris.org mh->m_error = errno; 198*9860Sgdamore@opensolaris.org } 199*9860Sgdamore@opensolaris.org } 200*9860Sgdamore@opensolaris.org 201*9860Sgdamore@opensolaris.org /* 202*9860Sgdamore@opensolaris.org * Known list of specific PHY probes. 203*9860Sgdamore@opensolaris.org */ 204*9860Sgdamore@opensolaris.org typedef boolean_t (*phy_probe_t)(phy_handle_t *); 205*9860Sgdamore@opensolaris.org phy_probe_t _phy_probes[] = { 206*9860Sgdamore@opensolaris.org phy_natsemi_probe, 207*9860Sgdamore@opensolaris.org phy_intel_probe, 208*9860Sgdamore@opensolaris.org phy_qualsemi_probe, 209*9860Sgdamore@opensolaris.org phy_cicada_probe, 210*9860Sgdamore@opensolaris.org phy_other_probe, 211*9860Sgdamore@opensolaris.org NULL 212*9860Sgdamore@opensolaris.org }; 213*9860Sgdamore@opensolaris.org 214*9860Sgdamore@opensolaris.org /* 215*9860Sgdamore@opensolaris.org * MII Interface functions 216*9860Sgdamore@opensolaris.org */ 217*9860Sgdamore@opensolaris.org 218*9860Sgdamore@opensolaris.org mii_handle_t 219*9860Sgdamore@opensolaris.org mii_alloc_instance(void *private, dev_info_t *dip, int inst, mii_ops_t *ops) 220*9860Sgdamore@opensolaris.org { 221*9860Sgdamore@opensolaris.org mii_handle_t mh; 222*9860Sgdamore@opensolaris.org char tqname[16]; 223*9860Sgdamore@opensolaris.org 224*9860Sgdamore@opensolaris.org if (ops->mii_version != MII_OPS_VERSION) { 225*9860Sgdamore@opensolaris.org cmn_err(CE_WARN, "%s: incompatible MII version (%d)", 226*9860Sgdamore@opensolaris.org ddi_driver_name(dip), ops->mii_version); 227*9860Sgdamore@opensolaris.org return (NULL); 228*9860Sgdamore@opensolaris.org } 229*9860Sgdamore@opensolaris.org mh = kmem_zalloc(sizeof (*mh), KM_SLEEP); 230*9860Sgdamore@opensolaris.org 231*9860Sgdamore@opensolaris.org (void) snprintf(mh->m_name, sizeof (mh->m_name), "%s%d", 232*9860Sgdamore@opensolaris.org ddi_driver_name(dip), inst); 233*9860Sgdamore@opensolaris.org 234*9860Sgdamore@opensolaris.org /* DDI will prepend the driver name */ 235*9860Sgdamore@opensolaris.org (void) snprintf(tqname, sizeof (tqname), "mii%d", inst); 236*9860Sgdamore@opensolaris.org 237*9860Sgdamore@opensolaris.org mh->m_dip = dip; 238*9860Sgdamore@opensolaris.org mh->m_ops = *ops; 239*9860Sgdamore@opensolaris.org mh->m_private = private; 240*9860Sgdamore@opensolaris.org mh->m_suspended = B_FALSE; 241*9860Sgdamore@opensolaris.org mh->m_started = B_FALSE; 242*9860Sgdamore@opensolaris.org mh->m_notify = B_FALSE; 243*9860Sgdamore@opensolaris.org mh->m_tstate = MII_STATE_PROBE; 244*9860Sgdamore@opensolaris.org mh->m_error = MII_EOK; 245*9860Sgdamore@opensolaris.org mutex_init(&mh->m_lock, NULL, MUTEX_DRIVER, NULL); 246*9860Sgdamore@opensolaris.org cv_init(&mh->m_cv, NULL, CV_DRIVER, NULL); 247*9860Sgdamore@opensolaris.org 248*9860Sgdamore@opensolaris.org mh->m_tq = ddi_taskq_create(dip, tqname, 1, TASKQ_DEFAULTPRI, 0); 249*9860Sgdamore@opensolaris.org if (mh->m_tq == NULL) { 250*9860Sgdamore@opensolaris.org cmn_err(CE_WARN, "%s: unable to create MII monitoring task", 251*9860Sgdamore@opensolaris.org ddi_driver_name(dip)); 252*9860Sgdamore@opensolaris.org cv_destroy(&mh->m_cv); 253*9860Sgdamore@opensolaris.org mutex_destroy(&mh->m_lock); 254*9860Sgdamore@opensolaris.org kmem_free(mh, sizeof (*mh)); 255*9860Sgdamore@opensolaris.org return (NULL); 256*9860Sgdamore@opensolaris.org } 257*9860Sgdamore@opensolaris.org 258*9860Sgdamore@opensolaris.org /* 259*9860Sgdamore@opensolaris.org * Initialize user prefs by loading properties. Ultimately, 260*9860Sgdamore@opensolaris.org * Brussels interfaces would be superior here. 261*9860Sgdamore@opensolaris.org */ 262*9860Sgdamore@opensolaris.org #define GETPROP(name) ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0, name, -1) 263*9860Sgdamore@opensolaris.org mh->m_en_aneg = GETPROP("adv_autoneg_cap"); 264*9860Sgdamore@opensolaris.org mh->m_en_10_hdx = GETPROP("adv_10hdx_cap"); 265*9860Sgdamore@opensolaris.org mh->m_en_10_fdx = GETPROP("adv_10fdx_cap"); 266*9860Sgdamore@opensolaris.org mh->m_en_100_hdx = GETPROP("adv_100hdx_cap"); 267*9860Sgdamore@opensolaris.org mh->m_en_100_fdx = GETPROP("adv_100fdx_cap"); 268*9860Sgdamore@opensolaris.org mh->m_en_100_t4 = GETPROP("adv_100T4_cap"); 269*9860Sgdamore@opensolaris.org mh->m_en_1000_hdx = GETPROP("adv_1000hdx_cap"); 270*9860Sgdamore@opensolaris.org mh->m_en_1000_fdx = GETPROP("adv_1000fdx_cap"); 271*9860Sgdamore@opensolaris.org 272*9860Sgdamore@opensolaris.org mh->m_cap_pause = B_FALSE; 273*9860Sgdamore@opensolaris.org mh->m_cap_asmpause = B_FALSE; 274*9860Sgdamore@opensolaris.org 275*9860Sgdamore@opensolaris.org bzero(&mh->m_bogus_phy, sizeof (mh->m_bogus_phy)); 276*9860Sgdamore@opensolaris.org mh->m_bogus_phy.phy_link = LINK_STATE_UNKNOWN; 277*9860Sgdamore@opensolaris.org mh->m_bogus_phy.phy_duplex = LINK_DUPLEX_UNKNOWN; 278*9860Sgdamore@opensolaris.org mh->m_bogus_phy.phy_addr = 0xff; 279*9860Sgdamore@opensolaris.org mh->m_bogus_phy.phy_type = XCVR_NONE; 280*9860Sgdamore@opensolaris.org mh->m_bogus_phy.phy_id = (uint32_t)-1; 281*9860Sgdamore@opensolaris.org mh->m_bogus_phy.phy_loopback = PHY_LB_NONE; 282*9860Sgdamore@opensolaris.org mh->m_bogus_phy.phy_flowctrl = LINK_FLOWCTRL_NONE; 283*9860Sgdamore@opensolaris.org mh->m_phy = &mh->m_bogus_phy; 284*9860Sgdamore@opensolaris.org 285*9860Sgdamore@opensolaris.org for (int i = 0; i < 32; i++) { 286*9860Sgdamore@opensolaris.org mh->m_phys[i].phy_mii = mh; 287*9860Sgdamore@opensolaris.org } 288*9860Sgdamore@opensolaris.org mh->m_bogus_phy.phy_mii = mh; 289*9860Sgdamore@opensolaris.org 290*9860Sgdamore@opensolaris.org return (mh); 291*9860Sgdamore@opensolaris.org } 292*9860Sgdamore@opensolaris.org 293*9860Sgdamore@opensolaris.org mii_handle_t 294*9860Sgdamore@opensolaris.org mii_alloc(void *private, dev_info_t *dip, mii_ops_t *ops) 295*9860Sgdamore@opensolaris.org { 296*9860Sgdamore@opensolaris.org return (mii_alloc_instance(private, dip, ddi_get_instance(dip), ops)); 297*9860Sgdamore@opensolaris.org } 298*9860Sgdamore@opensolaris.org 299*9860Sgdamore@opensolaris.org void 300*9860Sgdamore@opensolaris.org mii_set_pauseable(mii_handle_t mh, boolean_t pauseable, boolean_t asymetric) 301*9860Sgdamore@opensolaris.org { 302*9860Sgdamore@opensolaris.org phy_handle_t *ph; 303*9860Sgdamore@opensolaris.org 304*9860Sgdamore@opensolaris.org mutex_enter(&mh->m_lock); 305*9860Sgdamore@opensolaris.org ph = mh->m_phy; 306*9860Sgdamore@opensolaris.org ph->phy_cap_pause = mh->m_cap_pause = pauseable; 307*9860Sgdamore@opensolaris.org ph->phy_cap_asmpause = mh->m_cap_asmpause = asymetric; 308*9860Sgdamore@opensolaris.org if (pauseable) { 309*9860Sgdamore@opensolaris.org mh->m_en_flowctrl = LINK_FLOWCTRL_BI; 310*9860Sgdamore@opensolaris.org } else { 311*9860Sgdamore@opensolaris.org mh->m_en_flowctrl = LINK_FLOWCTRL_NONE; 312*9860Sgdamore@opensolaris.org } 313*9860Sgdamore@opensolaris.org mutex_exit(&mh->m_lock); 314*9860Sgdamore@opensolaris.org } 315*9860Sgdamore@opensolaris.org 316*9860Sgdamore@opensolaris.org void 317*9860Sgdamore@opensolaris.org mii_free(mii_handle_t mh) 318*9860Sgdamore@opensolaris.org { 319*9860Sgdamore@opensolaris.org mutex_enter(&mh->m_lock); 320*9860Sgdamore@opensolaris.org mh->m_started = B_FALSE; 321*9860Sgdamore@opensolaris.org cv_broadcast(&mh->m_cv); 322*9860Sgdamore@opensolaris.org mutex_exit(&mh->m_lock); 323*9860Sgdamore@opensolaris.org 324*9860Sgdamore@opensolaris.org ddi_taskq_destroy(mh->m_tq); 325*9860Sgdamore@opensolaris.org mutex_destroy(&mh->m_lock); 326*9860Sgdamore@opensolaris.org cv_destroy(&mh->m_cv); 327*9860Sgdamore@opensolaris.org kmem_free(mh, sizeof (*mh)); 328*9860Sgdamore@opensolaris.org } 329*9860Sgdamore@opensolaris.org 330*9860Sgdamore@opensolaris.org void 331*9860Sgdamore@opensolaris.org mii_reset(mii_handle_t mh) 332*9860Sgdamore@opensolaris.org { 333*9860Sgdamore@opensolaris.org mutex_enter(&mh->m_lock); 334*9860Sgdamore@opensolaris.org if (mh->m_tstate > MII_STATE_RESET) 335*9860Sgdamore@opensolaris.org mh->m_tstate = MII_STATE_RESET; 336*9860Sgdamore@opensolaris.org cv_broadcast(&mh->m_cv); 337*9860Sgdamore@opensolaris.org mutex_exit(&mh->m_lock); 338*9860Sgdamore@opensolaris.org } 339*9860Sgdamore@opensolaris.org 340*9860Sgdamore@opensolaris.org void 341*9860Sgdamore@opensolaris.org mii_suspend(mii_handle_t mh) 342*9860Sgdamore@opensolaris.org { 343*9860Sgdamore@opensolaris.org mutex_enter(&mh->m_lock); 344*9860Sgdamore@opensolaris.org while ((!mh->m_suspended) && (mh->m_started)) { 345*9860Sgdamore@opensolaris.org mh->m_suspending = B_TRUE; 346*9860Sgdamore@opensolaris.org cv_broadcast(&mh->m_cv); 347*9860Sgdamore@opensolaris.org cv_wait(&mh->m_cv, &mh->m_lock); 348*9860Sgdamore@opensolaris.org } 349*9860Sgdamore@opensolaris.org mutex_exit(&mh->m_lock); 350*9860Sgdamore@opensolaris.org } 351*9860Sgdamore@opensolaris.org 352*9860Sgdamore@opensolaris.org void 353*9860Sgdamore@opensolaris.org mii_resume(mii_handle_t mh) 354*9860Sgdamore@opensolaris.org { 355*9860Sgdamore@opensolaris.org mutex_enter(&mh->m_lock); 356*9860Sgdamore@opensolaris.org /* resume implicity causes a PHY reset */ 357*9860Sgdamore@opensolaris.org if (mh->m_tstate > MII_STATE_RESET) { 358*9860Sgdamore@opensolaris.org mh->m_tstate = MII_STATE_RESET; 359*9860Sgdamore@opensolaris.org } 360*9860Sgdamore@opensolaris.org mh->m_suspended = B_FALSE; 361*9860Sgdamore@opensolaris.org cv_broadcast(&mh->m_cv); 362*9860Sgdamore@opensolaris.org mutex_exit(&mh->m_lock); 363*9860Sgdamore@opensolaris.org } 364*9860Sgdamore@opensolaris.org 365*9860Sgdamore@opensolaris.org void 366*9860Sgdamore@opensolaris.org mii_start(mii_handle_t mh) 367*9860Sgdamore@opensolaris.org { 368*9860Sgdamore@opensolaris.org mutex_enter(&mh->m_lock); 369*9860Sgdamore@opensolaris.org if (!mh->m_started) { 370*9860Sgdamore@opensolaris.org mh->m_started = B_TRUE; 371*9860Sgdamore@opensolaris.org if (ddi_taskq_dispatch(mh->m_tq, _mii_task, mh, DDI_NOSLEEP) != 372*9860Sgdamore@opensolaris.org DDI_SUCCESS) { 373*9860Sgdamore@opensolaris.org cmn_err(CE_WARN, 374*9860Sgdamore@opensolaris.org "%s: unable to start MII monitoring task", 375*9860Sgdamore@opensolaris.org mh->m_name); 376*9860Sgdamore@opensolaris.org mh->m_started = B_FALSE; 377*9860Sgdamore@opensolaris.org } 378*9860Sgdamore@opensolaris.org } 379*9860Sgdamore@opensolaris.org cv_broadcast(&mh->m_cv); 380*9860Sgdamore@opensolaris.org mutex_exit(&mh->m_lock); 381*9860Sgdamore@opensolaris.org } 382*9860Sgdamore@opensolaris.org 383*9860Sgdamore@opensolaris.org void 384*9860Sgdamore@opensolaris.org mii_stop(mii_handle_t mh) 385*9860Sgdamore@opensolaris.org { 386*9860Sgdamore@opensolaris.org phy_handle_t *ph; 387*9860Sgdamore@opensolaris.org 388*9860Sgdamore@opensolaris.org mutex_enter(&mh->m_lock); 389*9860Sgdamore@opensolaris.org mh->m_started = B_FALSE; 390*9860Sgdamore@opensolaris.org ph = mh->m_phy; 391*9860Sgdamore@opensolaris.org if (ph->phy_present) { 392*9860Sgdamore@opensolaris.org ph->phy_stop(ph); 393*9860Sgdamore@opensolaris.org } 394*9860Sgdamore@opensolaris.org cv_broadcast(&mh->m_cv); 395*9860Sgdamore@opensolaris.org mutex_exit(&mh->m_lock); 396*9860Sgdamore@opensolaris.org } 397*9860Sgdamore@opensolaris.org 398*9860Sgdamore@opensolaris.org void 399*9860Sgdamore@opensolaris.org mii_probe(mii_handle_t mh) 400*9860Sgdamore@opensolaris.org { 401*9860Sgdamore@opensolaris.org mutex_enter(&mh->m_lock); 402*9860Sgdamore@opensolaris.org _mii_probe_task(mh); 403*9860Sgdamore@opensolaris.org mutex_exit(&mh->m_lock); 404*9860Sgdamore@opensolaris.org } 405*9860Sgdamore@opensolaris.org 406*9860Sgdamore@opensolaris.org void 407*9860Sgdamore@opensolaris.org mii_check(mii_handle_t mh) 408*9860Sgdamore@opensolaris.org { 409*9860Sgdamore@opensolaris.org mutex_enter(&mh->m_lock); 410*9860Sgdamore@opensolaris.org cv_broadcast(&mh->m_cv); 411*9860Sgdamore@opensolaris.org mutex_exit(&mh->m_lock); 412*9860Sgdamore@opensolaris.org } 413*9860Sgdamore@opensolaris.org 414*9860Sgdamore@opensolaris.org int 415*9860Sgdamore@opensolaris.org mii_get_speed(mii_handle_t mh) 416*9860Sgdamore@opensolaris.org { 417*9860Sgdamore@opensolaris.org phy_handle_t *ph = mh->m_phy; 418*9860Sgdamore@opensolaris.org 419*9860Sgdamore@opensolaris.org return (ph->phy_speed); 420*9860Sgdamore@opensolaris.org } 421*9860Sgdamore@opensolaris.org 422*9860Sgdamore@opensolaris.org link_duplex_t 423*9860Sgdamore@opensolaris.org mii_get_duplex(mii_handle_t mh) 424*9860Sgdamore@opensolaris.org { 425*9860Sgdamore@opensolaris.org phy_handle_t *ph = mh->m_phy; 426*9860Sgdamore@opensolaris.org 427*9860Sgdamore@opensolaris.org return (ph->phy_duplex); 428*9860Sgdamore@opensolaris.org } 429*9860Sgdamore@opensolaris.org 430*9860Sgdamore@opensolaris.org link_state_t 431*9860Sgdamore@opensolaris.org mii_get_state(mii_handle_t mh) 432*9860Sgdamore@opensolaris.org { 433*9860Sgdamore@opensolaris.org phy_handle_t *ph = mh->m_phy; 434*9860Sgdamore@opensolaris.org 435*9860Sgdamore@opensolaris.org return (ph->phy_link); 436*9860Sgdamore@opensolaris.org } 437*9860Sgdamore@opensolaris.org 438*9860Sgdamore@opensolaris.org link_flowctrl_t 439*9860Sgdamore@opensolaris.org mii_get_flowctrl(mii_handle_t mh) 440*9860Sgdamore@opensolaris.org { 441*9860Sgdamore@opensolaris.org phy_handle_t *ph = mh->m_phy; 442*9860Sgdamore@opensolaris.org 443*9860Sgdamore@opensolaris.org return (ph->phy_flowctrl); 444*9860Sgdamore@opensolaris.org } 445*9860Sgdamore@opensolaris.org 446*9860Sgdamore@opensolaris.org int 447*9860Sgdamore@opensolaris.org mii_get_loopmodes(mii_handle_t mh, lb_property_t *modes) 448*9860Sgdamore@opensolaris.org { 449*9860Sgdamore@opensolaris.org phy_handle_t *ph = mh->m_phy; 450*9860Sgdamore@opensolaris.org int cnt; 451*9860Sgdamore@opensolaris.org 452*9860Sgdamore@opensolaris.org /* 453*9860Sgdamore@opensolaris.org * There's a pretty big assumption in here. The assumption is 454*9860Sgdamore@opensolaris.org * that for 1000BASE-T and 100BASE-TX modes that we can 455*9860Sgdamore@opensolaris.org * support all of the lower speeds and full duplex uniformly. 456*9860Sgdamore@opensolaris.org */ 457*9860Sgdamore@opensolaris.org switch (ph->phy_type) { 458*9860Sgdamore@opensolaris.org case XCVR_1000T: 459*9860Sgdamore@opensolaris.org cnt = 5; /* 1000 BASE-T should support all modes */ 460*9860Sgdamore@opensolaris.org break; 461*9860Sgdamore@opensolaris.org case XCVR_100X: 462*9860Sgdamore@opensolaris.org /* 463*9860Sgdamore@opensolaris.org * This might be 100BASE-FX. If so, it won't support the 464*9860Sgdamore@opensolaris.org * 10Mbps modes. 465*9860Sgdamore@opensolaris.org */ 466*9860Sgdamore@opensolaris.org if (ph->phy_cap_10_fdx) { 467*9860Sgdamore@opensolaris.org cnt = 4; /* 100BASE-TX probably */ 468*9860Sgdamore@opensolaris.org } else { 469*9860Sgdamore@opensolaris.org cnt = 2; /* 100BASE-FX? */ 470*9860Sgdamore@opensolaris.org } 471*9860Sgdamore@opensolaris.org break; 472*9860Sgdamore@opensolaris.org case XCVR_10: 473*9860Sgdamore@opensolaris.org /* Some 10Mbs PHYs don't do full duplex */ 474*9860Sgdamore@opensolaris.org if (ph->phy_cap_10_fdx) { 475*9860Sgdamore@opensolaris.org cnt = 3; 476*9860Sgdamore@opensolaris.org } else { 477*9860Sgdamore@opensolaris.org cnt = 2; 478*9860Sgdamore@opensolaris.org } 479*9860Sgdamore@opensolaris.org break; 480*9860Sgdamore@opensolaris.org case XCVR_NONE: 481*9860Sgdamore@opensolaris.org cnt = 1; /* No XCVR, all we can do is leave it off */ 482*9860Sgdamore@opensolaris.org break; 483*9860Sgdamore@opensolaris.org case XCVR_1000X: 484*9860Sgdamore@opensolaris.org case XCVR_100T4: 485*9860Sgdamore@opensolaris.org case XCVR_100T2: 486*9860Sgdamore@opensolaris.org default: 487*9860Sgdamore@opensolaris.org /* 488*9860Sgdamore@opensolaris.org * Anything else we just allow for internal PHY loopback. 489*9860Sgdamore@opensolaris.org * Note that many of these will probably be capable of 490*9860Sgdamore@opensolaris.org * supporting additional external loopback modes, but we 491*9860Sgdamore@opensolaris.org * can't be sure of it, so we just leave it alone. 492*9860Sgdamore@opensolaris.org */ 493*9860Sgdamore@opensolaris.org cnt = 2; 494*9860Sgdamore@opensolaris.org break; 495*9860Sgdamore@opensolaris.org } 496*9860Sgdamore@opensolaris.org 497*9860Sgdamore@opensolaris.org if (modes) { 498*9860Sgdamore@opensolaris.org bcopy(mii_loopmodes, modes, sizeof (lb_property_t) * cnt); 499*9860Sgdamore@opensolaris.org } 500*9860Sgdamore@opensolaris.org return (cnt); 501*9860Sgdamore@opensolaris.org } 502*9860Sgdamore@opensolaris.org 503*9860Sgdamore@opensolaris.org uint32_t 504*9860Sgdamore@opensolaris.org mii_get_loopback(mii_handle_t mh) 505*9860Sgdamore@opensolaris.org { 506*9860Sgdamore@opensolaris.org phy_handle_t *ph = mh->m_phy; 507*9860Sgdamore@opensolaris.org 508*9860Sgdamore@opensolaris.org return (ph->phy_loopback); 509*9860Sgdamore@opensolaris.org } 510*9860Sgdamore@opensolaris.org 511*9860Sgdamore@opensolaris.org int 512*9860Sgdamore@opensolaris.org mii_set_loopback(mii_handle_t mh, uint32_t loop) 513*9860Sgdamore@opensolaris.org { 514*9860Sgdamore@opensolaris.org phy_handle_t *ph; 515*9860Sgdamore@opensolaris.org 516*9860Sgdamore@opensolaris.org mutex_enter(&mh->m_lock); 517*9860Sgdamore@opensolaris.org ph = mh->m_phy; 518*9860Sgdamore@opensolaris.org 519*9860Sgdamore@opensolaris.org if ((!mh->m_started) || (!ph->phy_present) || 520*9860Sgdamore@opensolaris.org (loop >= mii_get_loopmodes(mh, NULL))) { 521*9860Sgdamore@opensolaris.org return (EINVAL); 522*9860Sgdamore@opensolaris.org } 523*9860Sgdamore@opensolaris.org 524*9860Sgdamore@opensolaris.org ph->phy_loopback = loop; 525*9860Sgdamore@opensolaris.org mh->m_tstate = MII_STATE_RESET; 526*9860Sgdamore@opensolaris.org cv_broadcast(&mh->m_cv); 527*9860Sgdamore@opensolaris.org mutex_exit(&mh->m_lock); 528*9860Sgdamore@opensolaris.org 529*9860Sgdamore@opensolaris.org return (0); 530*9860Sgdamore@opensolaris.org } 531*9860Sgdamore@opensolaris.org 532*9860Sgdamore@opensolaris.org uint32_t 533*9860Sgdamore@opensolaris.org mii_get_id(mii_handle_t mh) 534*9860Sgdamore@opensolaris.org { 535*9860Sgdamore@opensolaris.org phy_handle_t *ph = mh->m_phy; 536*9860Sgdamore@opensolaris.org 537*9860Sgdamore@opensolaris.org return (ph->phy_id); 538*9860Sgdamore@opensolaris.org } 539*9860Sgdamore@opensolaris.org 540*9860Sgdamore@opensolaris.org int 541*9860Sgdamore@opensolaris.org mii_get_addr(mii_handle_t mh) 542*9860Sgdamore@opensolaris.org { 543*9860Sgdamore@opensolaris.org return (mh->m_addr); 544*9860Sgdamore@opensolaris.org } 545*9860Sgdamore@opensolaris.org 546*9860Sgdamore@opensolaris.org /* GLDv3 helpers */ 547*9860Sgdamore@opensolaris.org 548*9860Sgdamore@opensolaris.org boolean_t 549*9860Sgdamore@opensolaris.org mii_m_loop_ioctl(mii_handle_t mh, queue_t *wq, mblk_t *mp) 550*9860Sgdamore@opensolaris.org { 551*9860Sgdamore@opensolaris.org struct iocblk *iocp; 552*9860Sgdamore@opensolaris.org int rv = 0; 553*9860Sgdamore@opensolaris.org int cnt; 554*9860Sgdamore@opensolaris.org lb_property_t modes[MII_LOOPBACK_MAX]; 555*9860Sgdamore@opensolaris.org lb_info_sz_t sz; 556*9860Sgdamore@opensolaris.org int cmd; 557*9860Sgdamore@opensolaris.org uint32_t mode; 558*9860Sgdamore@opensolaris.org 559*9860Sgdamore@opensolaris.org iocp = (void *)mp->b_rptr; 560*9860Sgdamore@opensolaris.org cmd = iocp->ioc_cmd; 561*9860Sgdamore@opensolaris.org 562*9860Sgdamore@opensolaris.org switch (cmd) { 563*9860Sgdamore@opensolaris.org case LB_SET_MODE: 564*9860Sgdamore@opensolaris.org case LB_GET_INFO_SIZE: 565*9860Sgdamore@opensolaris.org case LB_GET_INFO: 566*9860Sgdamore@opensolaris.org case LB_GET_MODE: 567*9860Sgdamore@opensolaris.org break; 568*9860Sgdamore@opensolaris.org 569*9860Sgdamore@opensolaris.org default: 570*9860Sgdamore@opensolaris.org return (B_FALSE); 571*9860Sgdamore@opensolaris.org } 572*9860Sgdamore@opensolaris.org 573*9860Sgdamore@opensolaris.org if (mp->b_cont == NULL) { 574*9860Sgdamore@opensolaris.org miocnak(wq, mp, 0, EINVAL); 575*9860Sgdamore@opensolaris.org return (B_TRUE); 576*9860Sgdamore@opensolaris.org } 577*9860Sgdamore@opensolaris.org 578*9860Sgdamore@opensolaris.org switch (cmd) { 579*9860Sgdamore@opensolaris.org case LB_GET_INFO_SIZE: 580*9860Sgdamore@opensolaris.org cnt = mii_get_loopmodes(mh, modes); 581*9860Sgdamore@opensolaris.org if (iocp->ioc_count != sizeof (sz)) { 582*9860Sgdamore@opensolaris.org rv = EINVAL; 583*9860Sgdamore@opensolaris.org } else { 584*9860Sgdamore@opensolaris.org sz = cnt * sizeof (lb_property_t); 585*9860Sgdamore@opensolaris.org bcopy(&sz, mp->b_cont->b_rptr, sizeof (sz)); 586*9860Sgdamore@opensolaris.org } 587*9860Sgdamore@opensolaris.org break; 588*9860Sgdamore@opensolaris.org 589*9860Sgdamore@opensolaris.org case LB_GET_INFO: 590*9860Sgdamore@opensolaris.org cnt = mii_get_loopmodes(mh, modes); 591*9860Sgdamore@opensolaris.org if (iocp->ioc_count != (cnt * sizeof (lb_property_t))) { 592*9860Sgdamore@opensolaris.org rv = EINVAL; 593*9860Sgdamore@opensolaris.org } else { 594*9860Sgdamore@opensolaris.org bcopy(modes, mp->b_cont->b_rptr, iocp->ioc_count); 595*9860Sgdamore@opensolaris.org } 596*9860Sgdamore@opensolaris.org break; 597*9860Sgdamore@opensolaris.org 598*9860Sgdamore@opensolaris.org case LB_GET_MODE: 599*9860Sgdamore@opensolaris.org if (iocp->ioc_count != sizeof (mode)) { 600*9860Sgdamore@opensolaris.org rv = EINVAL; 601*9860Sgdamore@opensolaris.org } else { 602*9860Sgdamore@opensolaris.org mode = mii_get_loopback(mh); 603*9860Sgdamore@opensolaris.org bcopy(&mode, mp->b_cont->b_rptr, sizeof (mode)); 604*9860Sgdamore@opensolaris.org } 605*9860Sgdamore@opensolaris.org break; 606*9860Sgdamore@opensolaris.org 607*9860Sgdamore@opensolaris.org case LB_SET_MODE: 608*9860Sgdamore@opensolaris.org rv = secpolicy_net_config(iocp->ioc_cr, B_FALSE); 609*9860Sgdamore@opensolaris.org if (rv != 0) 610*9860Sgdamore@opensolaris.org break; 611*9860Sgdamore@opensolaris.org if (iocp->ioc_count != sizeof (mode)) { 612*9860Sgdamore@opensolaris.org rv = EINVAL; 613*9860Sgdamore@opensolaris.org break; 614*9860Sgdamore@opensolaris.org } 615*9860Sgdamore@opensolaris.org bcopy(mp->b_cont->b_rptr, &mode, sizeof (mode)); 616*9860Sgdamore@opensolaris.org rv = mii_set_loopback(mh, mode); 617*9860Sgdamore@opensolaris.org break; 618*9860Sgdamore@opensolaris.org } 619*9860Sgdamore@opensolaris.org 620*9860Sgdamore@opensolaris.org if (rv == 0) { 621*9860Sgdamore@opensolaris.org miocack(wq, mp, iocp->ioc_count, 0); 622*9860Sgdamore@opensolaris.org } else { 623*9860Sgdamore@opensolaris.org miocnak(wq, mp, 0, rv); 624*9860Sgdamore@opensolaris.org } 625*9860Sgdamore@opensolaris.org return (B_TRUE); 626*9860Sgdamore@opensolaris.org } 627*9860Sgdamore@opensolaris.org 628*9860Sgdamore@opensolaris.org int 629*9860Sgdamore@opensolaris.org mii_m_getprop(mii_handle_t mh, const char *name, mac_prop_id_t num, 630*9860Sgdamore@opensolaris.org uint_t flags, uint_t sz, void *val, uint_t *permp) 631*9860Sgdamore@opensolaris.org { 632*9860Sgdamore@opensolaris.org phy_handle_t *ph; 633*9860Sgdamore@opensolaris.org int err = 0; 634*9860Sgdamore@opensolaris.org uint_t perm; 635*9860Sgdamore@opensolaris.org boolean_t dfl = flags & MAC_PROP_DEFAULT; 636*9860Sgdamore@opensolaris.org 637*9860Sgdamore@opensolaris.org _NOTE(ARGUNUSED(name)); 638*9860Sgdamore@opensolaris.org 639*9860Sgdamore@opensolaris.org if (sz < 1) 640*9860Sgdamore@opensolaris.org return (EINVAL); 641*9860Sgdamore@opensolaris.org 642*9860Sgdamore@opensolaris.org mutex_enter(&mh->m_lock); 643*9860Sgdamore@opensolaris.org 644*9860Sgdamore@opensolaris.org ph = mh->m_phy; 645*9860Sgdamore@opensolaris.org perm = MAC_PROP_PERM_RW; 646*9860Sgdamore@opensolaris.org 647*9860Sgdamore@opensolaris.org #define CASE_PROP_ABILITY(PROP, VAR) \ 648*9860Sgdamore@opensolaris.org case MAC_PROP_ADV_##PROP: \ 649*9860Sgdamore@opensolaris.org perm = MAC_PROP_PERM_READ; \ 650*9860Sgdamore@opensolaris.org *(uint8_t *)val = \ 651*9860Sgdamore@opensolaris.org dfl ? ph->phy_cap_##VAR : ph->phy_adv_##VAR; \ 652*9860Sgdamore@opensolaris.org break; \ 653*9860Sgdamore@opensolaris.org \ 654*9860Sgdamore@opensolaris.org case MAC_PROP_EN_##PROP: \ 655*9860Sgdamore@opensolaris.org if (!ph->phy_cap_##VAR) \ 656*9860Sgdamore@opensolaris.org perm = MAC_PROP_PERM_READ; \ 657*9860Sgdamore@opensolaris.org *(uint8_t *)val = \ 658*9860Sgdamore@opensolaris.org dfl ? ph->phy_cap_##VAR : ph->phy_en_##VAR; \ 659*9860Sgdamore@opensolaris.org break; 660*9860Sgdamore@opensolaris.org 661*9860Sgdamore@opensolaris.org switch (num) { 662*9860Sgdamore@opensolaris.org case MAC_PROP_DUPLEX: 663*9860Sgdamore@opensolaris.org perm = MAC_PROP_PERM_READ; 664*9860Sgdamore@opensolaris.org if (sz >= sizeof (link_duplex_t)) { 665*9860Sgdamore@opensolaris.org bcopy(&ph->phy_duplex, val, sizeof (link_duplex_t)); 666*9860Sgdamore@opensolaris.org } else { 667*9860Sgdamore@opensolaris.org err = EINVAL; 668*9860Sgdamore@opensolaris.org } 669*9860Sgdamore@opensolaris.org break; 670*9860Sgdamore@opensolaris.org 671*9860Sgdamore@opensolaris.org case MAC_PROP_SPEED: 672*9860Sgdamore@opensolaris.org perm = MAC_PROP_PERM_READ; 673*9860Sgdamore@opensolaris.org if (sz >= sizeof (uint64_t)) { 674*9860Sgdamore@opensolaris.org uint64_t speed = ph->phy_speed * 1000000ull; 675*9860Sgdamore@opensolaris.org bcopy(&speed, val, sizeof (speed)); 676*9860Sgdamore@opensolaris.org } else { 677*9860Sgdamore@opensolaris.org err = EINVAL; 678*9860Sgdamore@opensolaris.org } 679*9860Sgdamore@opensolaris.org break; 680*9860Sgdamore@opensolaris.org 681*9860Sgdamore@opensolaris.org case MAC_PROP_AUTONEG: 682*9860Sgdamore@opensolaris.org *(uint8_t *)val = 683*9860Sgdamore@opensolaris.org dfl ? ph->phy_cap_aneg : ph->phy_adv_aneg; 684*9860Sgdamore@opensolaris.org break; 685*9860Sgdamore@opensolaris.org 686*9860Sgdamore@opensolaris.org case MAC_PROP_FLOWCTRL: 687*9860Sgdamore@opensolaris.org if (sz >= sizeof (link_flowctrl_t)) { 688*9860Sgdamore@opensolaris.org bcopy(&ph->phy_flowctrl, val, 689*9860Sgdamore@opensolaris.org sizeof (link_flowctrl_t)); 690*9860Sgdamore@opensolaris.org } else { 691*9860Sgdamore@opensolaris.org err = EINVAL; 692*9860Sgdamore@opensolaris.org } 693*9860Sgdamore@opensolaris.org break; 694*9860Sgdamore@opensolaris.org 695*9860Sgdamore@opensolaris.org CASE_PROP_ABILITY(1000FDX_CAP, 1000_fdx) 696*9860Sgdamore@opensolaris.org CASE_PROP_ABILITY(1000HDX_CAP, 1000_hdx) 697*9860Sgdamore@opensolaris.org CASE_PROP_ABILITY(100T4_CAP, 100_t4) 698*9860Sgdamore@opensolaris.org CASE_PROP_ABILITY(100FDX_CAP, 100_fdx) 699*9860Sgdamore@opensolaris.org CASE_PROP_ABILITY(100HDX_CAP, 100_hdx) 700*9860Sgdamore@opensolaris.org CASE_PROP_ABILITY(10FDX_CAP, 10_fdx) 701*9860Sgdamore@opensolaris.org CASE_PROP_ABILITY(10HDX_CAP, 10_hdx) 702*9860Sgdamore@opensolaris.org 703*9860Sgdamore@opensolaris.org default: 704*9860Sgdamore@opensolaris.org err = ENOTSUP; 705*9860Sgdamore@opensolaris.org break; 706*9860Sgdamore@opensolaris.org } 707*9860Sgdamore@opensolaris.org 708*9860Sgdamore@opensolaris.org if (err == 0) { 709*9860Sgdamore@opensolaris.org *permp = perm; 710*9860Sgdamore@opensolaris.org } 711*9860Sgdamore@opensolaris.org 712*9860Sgdamore@opensolaris.org mutex_exit(&mh->m_lock); 713*9860Sgdamore@opensolaris.org 714*9860Sgdamore@opensolaris.org return (err); 715*9860Sgdamore@opensolaris.org } 716*9860Sgdamore@opensolaris.org 717*9860Sgdamore@opensolaris.org int 718*9860Sgdamore@opensolaris.org mii_m_setprop(mii_handle_t mh, const char *name, mac_prop_id_t num, 719*9860Sgdamore@opensolaris.org uint_t sz, const void *valp) 720*9860Sgdamore@opensolaris.org { 721*9860Sgdamore@opensolaris.org phy_handle_t *ph; 722*9860Sgdamore@opensolaris.org boolean_t *advp = NULL; 723*9860Sgdamore@opensolaris.org boolean_t *capp = NULL; 724*9860Sgdamore@opensolaris.org int *macpp = NULL; 725*9860Sgdamore@opensolaris.org int rv = ENOTSUP; 726*9860Sgdamore@opensolaris.org 727*9860Sgdamore@opensolaris.org _NOTE(ARGUNUSED(name)); 728*9860Sgdamore@opensolaris.org 729*9860Sgdamore@opensolaris.org if (sz < 1) 730*9860Sgdamore@opensolaris.org return (EINVAL); 731*9860Sgdamore@opensolaris.org 732*9860Sgdamore@opensolaris.org mutex_enter(&mh->m_lock); 733*9860Sgdamore@opensolaris.org 734*9860Sgdamore@opensolaris.org ph = mh->m_phy; 735*9860Sgdamore@opensolaris.org 736*9860Sgdamore@opensolaris.org /* we don't support changing parameters while in loopback mode */ 737*9860Sgdamore@opensolaris.org if (ph->phy_loopback != PHY_LB_NONE) { 738*9860Sgdamore@opensolaris.org switch (num) { 739*9860Sgdamore@opensolaris.org case MAC_PROP_EN_1000FDX_CAP: 740*9860Sgdamore@opensolaris.org case MAC_PROP_EN_1000HDX_CAP: 741*9860Sgdamore@opensolaris.org case MAC_PROP_EN_100FDX_CAP: 742*9860Sgdamore@opensolaris.org case MAC_PROP_EN_100HDX_CAP: 743*9860Sgdamore@opensolaris.org case MAC_PROP_EN_100T4_CAP: 744*9860Sgdamore@opensolaris.org case MAC_PROP_EN_10FDX_CAP: 745*9860Sgdamore@opensolaris.org case MAC_PROP_EN_10HDX_CAP: 746*9860Sgdamore@opensolaris.org case MAC_PROP_AUTONEG: 747*9860Sgdamore@opensolaris.org case MAC_PROP_FLOWCTRL: 748*9860Sgdamore@opensolaris.org return (EBUSY); 749*9860Sgdamore@opensolaris.org } 750*9860Sgdamore@opensolaris.org } 751*9860Sgdamore@opensolaris.org 752*9860Sgdamore@opensolaris.org switch (num) { 753*9860Sgdamore@opensolaris.org case MAC_PROP_EN_1000FDX_CAP: 754*9860Sgdamore@opensolaris.org capp = &ph->phy_cap_1000_fdx; 755*9860Sgdamore@opensolaris.org advp = &ph->phy_en_1000_fdx; 756*9860Sgdamore@opensolaris.org macpp = &mh->m_en_1000_fdx; 757*9860Sgdamore@opensolaris.org break; 758*9860Sgdamore@opensolaris.org case MAC_PROP_EN_1000HDX_CAP: 759*9860Sgdamore@opensolaris.org capp = &ph->phy_cap_1000_hdx; 760*9860Sgdamore@opensolaris.org advp = &ph->phy_en_1000_hdx; 761*9860Sgdamore@opensolaris.org macpp = &mh->m_en_1000_hdx; 762*9860Sgdamore@opensolaris.org break; 763*9860Sgdamore@opensolaris.org case MAC_PROP_EN_100FDX_CAP: 764*9860Sgdamore@opensolaris.org capp = &ph->phy_cap_100_fdx; 765*9860Sgdamore@opensolaris.org advp = &ph->phy_en_100_fdx; 766*9860Sgdamore@opensolaris.org macpp = &mh->m_en_100_fdx; 767*9860Sgdamore@opensolaris.org break; 768*9860Sgdamore@opensolaris.org case MAC_PROP_EN_100HDX_CAP: 769*9860Sgdamore@opensolaris.org capp = &ph->phy_cap_100_hdx; 770*9860Sgdamore@opensolaris.org advp = &ph->phy_en_100_hdx; 771*9860Sgdamore@opensolaris.org macpp = &mh->m_en_100_hdx; 772*9860Sgdamore@opensolaris.org break; 773*9860Sgdamore@opensolaris.org case MAC_PROP_EN_100T4_CAP: 774*9860Sgdamore@opensolaris.org capp = &ph->phy_cap_100_t4; 775*9860Sgdamore@opensolaris.org advp = &ph->phy_en_100_t4; 776*9860Sgdamore@opensolaris.org macpp = &mh->m_en_100_t4; 777*9860Sgdamore@opensolaris.org break; 778*9860Sgdamore@opensolaris.org case MAC_PROP_EN_10FDX_CAP: 779*9860Sgdamore@opensolaris.org capp = &ph->phy_cap_10_fdx; 780*9860Sgdamore@opensolaris.org advp = &ph->phy_en_10_fdx; 781*9860Sgdamore@opensolaris.org macpp = &mh->m_en_10_fdx; 782*9860Sgdamore@opensolaris.org break; 783*9860Sgdamore@opensolaris.org case MAC_PROP_EN_10HDX_CAP: 784*9860Sgdamore@opensolaris.org capp = &ph->phy_cap_10_hdx; 785*9860Sgdamore@opensolaris.org advp = &ph->phy_en_10_hdx; 786*9860Sgdamore@opensolaris.org macpp = &mh->m_en_10_hdx; 787*9860Sgdamore@opensolaris.org break; 788*9860Sgdamore@opensolaris.org case MAC_PROP_AUTONEG: 789*9860Sgdamore@opensolaris.org capp = &ph->phy_cap_aneg; 790*9860Sgdamore@opensolaris.org advp = &ph->phy_en_aneg; 791*9860Sgdamore@opensolaris.org macpp = &mh->m_en_aneg; 792*9860Sgdamore@opensolaris.org break; 793*9860Sgdamore@opensolaris.org case MAC_PROP_FLOWCTRL: 794*9860Sgdamore@opensolaris.org if (sz < sizeof (link_flowctrl_t)) { 795*9860Sgdamore@opensolaris.org rv = EINVAL; 796*9860Sgdamore@opensolaris.org } else { 797*9860Sgdamore@opensolaris.org link_flowctrl_t fc; 798*9860Sgdamore@opensolaris.org boolean_t chg; 799*9860Sgdamore@opensolaris.org 800*9860Sgdamore@opensolaris.org bcopy(valp, &fc, sizeof (fc)); 801*9860Sgdamore@opensolaris.org 802*9860Sgdamore@opensolaris.org chg = fc == ph->phy_en_flowctrl ? B_FALSE : B_TRUE; 803*9860Sgdamore@opensolaris.org switch (fc) { 804*9860Sgdamore@opensolaris.org case LINK_FLOWCTRL_NONE: 805*9860Sgdamore@opensolaris.org ph->phy_en_pause = B_FALSE; 806*9860Sgdamore@opensolaris.org ph->phy_en_asmpause = B_FALSE; 807*9860Sgdamore@opensolaris.org ph->phy_en_flowctrl = fc; 808*9860Sgdamore@opensolaris.org break; 809*9860Sgdamore@opensolaris.org /* 810*9860Sgdamore@opensolaris.org * Note that while we don't have a way to 811*9860Sgdamore@opensolaris.org * advertise that we can RX pause (we just 812*9860Sgdamore@opensolaris.org * won't send pause frames), we advertise full 813*9860Sgdamore@opensolaris.org * support. The MAC driver will learn of the 814*9860Sgdamore@opensolaris.org * configuration via the saved value of the 815*9860Sgdamore@opensolaris.org * tunable. 816*9860Sgdamore@opensolaris.org */ 817*9860Sgdamore@opensolaris.org case LINK_FLOWCTRL_BI: 818*9860Sgdamore@opensolaris.org case LINK_FLOWCTRL_RX: 819*9860Sgdamore@opensolaris.org if (ph->phy_cap_pause) { 820*9860Sgdamore@opensolaris.org ph->phy_en_pause = B_TRUE; 821*9860Sgdamore@opensolaris.org ph->phy_en_asmpause = B_TRUE; 822*9860Sgdamore@opensolaris.org ph->phy_en_flowctrl = fc; 823*9860Sgdamore@opensolaris.org } else { 824*9860Sgdamore@opensolaris.org rv = EINVAL; 825*9860Sgdamore@opensolaris.org } 826*9860Sgdamore@opensolaris.org break; 827*9860Sgdamore@opensolaris.org 828*9860Sgdamore@opensolaris.org /* 829*9860Sgdamore@opensolaris.org * Tell the other side that we can assert 830*9860Sgdamore@opensolaris.org * pause, but we cannot resend. 831*9860Sgdamore@opensolaris.org */ 832*9860Sgdamore@opensolaris.org case LINK_FLOWCTRL_TX: 833*9860Sgdamore@opensolaris.org if (ph->phy_cap_asmpause) { 834*9860Sgdamore@opensolaris.org ph->phy_en_pause = B_FALSE; 835*9860Sgdamore@opensolaris.org ph->phy_en_flowctrl = fc; 836*9860Sgdamore@opensolaris.org ph->phy_en_asmpause = B_TRUE; 837*9860Sgdamore@opensolaris.org } else { 838*9860Sgdamore@opensolaris.org rv = EINVAL; 839*9860Sgdamore@opensolaris.org } 840*9860Sgdamore@opensolaris.org break; 841*9860Sgdamore@opensolaris.org default: 842*9860Sgdamore@opensolaris.org rv = EINVAL; 843*9860Sgdamore@opensolaris.org break; 844*9860Sgdamore@opensolaris.org } 845*9860Sgdamore@opensolaris.org if ((rv == 0) && chg) { 846*9860Sgdamore@opensolaris.org mh->m_en_flowctrl = fc; 847*9860Sgdamore@opensolaris.org mh->m_tstate = MII_STATE_RESET; 848*9860Sgdamore@opensolaris.org cv_broadcast(&mh->m_cv); 849*9860Sgdamore@opensolaris.org } 850*9860Sgdamore@opensolaris.org } 851*9860Sgdamore@opensolaris.org break; 852*9860Sgdamore@opensolaris.org 853*9860Sgdamore@opensolaris.org default: 854*9860Sgdamore@opensolaris.org rv = ENOTSUP; 855*9860Sgdamore@opensolaris.org break; 856*9860Sgdamore@opensolaris.org } 857*9860Sgdamore@opensolaris.org 858*9860Sgdamore@opensolaris.org if (capp && advp && macpp) { 859*9860Sgdamore@opensolaris.org if (sz < sizeof (uint8_t)) { 860*9860Sgdamore@opensolaris.org rv = EINVAL; 861*9860Sgdamore@opensolaris.org 862*9860Sgdamore@opensolaris.org } else if (*capp) { 863*9860Sgdamore@opensolaris.org if (*advp != *(uint8_t *)valp) { 864*9860Sgdamore@opensolaris.org *advp = *(uint8_t *)valp; 865*9860Sgdamore@opensolaris.org *macpp = *(uint8_t *)valp; 866*9860Sgdamore@opensolaris.org mh->m_tstate = MII_STATE_RESET; 867*9860Sgdamore@opensolaris.org cv_broadcast(&mh->m_cv); 868*9860Sgdamore@opensolaris.org } 869*9860Sgdamore@opensolaris.org rv = 0; 870*9860Sgdamore@opensolaris.org } 871*9860Sgdamore@opensolaris.org } 872*9860Sgdamore@opensolaris.org 873*9860Sgdamore@opensolaris.org mutex_exit(&mh->m_lock); 874*9860Sgdamore@opensolaris.org return (rv); 875*9860Sgdamore@opensolaris.org } 876*9860Sgdamore@opensolaris.org 877*9860Sgdamore@opensolaris.org int 878*9860Sgdamore@opensolaris.org mii_m_getstat(mii_handle_t mh, uint_t stat, uint64_t *val) 879*9860Sgdamore@opensolaris.org { 880*9860Sgdamore@opensolaris.org phy_handle_t *ph; 881*9860Sgdamore@opensolaris.org int rv = 0; 882*9860Sgdamore@opensolaris.org 883*9860Sgdamore@opensolaris.org mutex_enter(&mh->m_lock); 884*9860Sgdamore@opensolaris.org 885*9860Sgdamore@opensolaris.org ph = mh->m_phy; 886*9860Sgdamore@opensolaris.org 887*9860Sgdamore@opensolaris.org switch (stat) { 888*9860Sgdamore@opensolaris.org case MAC_STAT_IFSPEED: 889*9860Sgdamore@opensolaris.org *val = ph->phy_speed * 1000000ull; 890*9860Sgdamore@opensolaris.org break; 891*9860Sgdamore@opensolaris.org case ETHER_STAT_LINK_DUPLEX: 892*9860Sgdamore@opensolaris.org *val = ph->phy_duplex; 893*9860Sgdamore@opensolaris.org break; 894*9860Sgdamore@opensolaris.org case ETHER_STAT_LINK_AUTONEG: 895*9860Sgdamore@opensolaris.org *val = !!(ph->phy_adv_aneg && ph->phy_lp_aneg); 896*9860Sgdamore@opensolaris.org break; 897*9860Sgdamore@opensolaris.org case ETHER_STAT_XCVR_ID: 898*9860Sgdamore@opensolaris.org *val = ph->phy_id; 899*9860Sgdamore@opensolaris.org break; 900*9860Sgdamore@opensolaris.org case ETHER_STAT_XCVR_INUSE: 901*9860Sgdamore@opensolaris.org *val = ph->phy_type; 902*9860Sgdamore@opensolaris.org break; 903*9860Sgdamore@opensolaris.org case ETHER_STAT_XCVR_ADDR: 904*9860Sgdamore@opensolaris.org *val = ph->phy_addr; 905*9860Sgdamore@opensolaris.org break; 906*9860Sgdamore@opensolaris.org case ETHER_STAT_LINK_ASMPAUSE: 907*9860Sgdamore@opensolaris.org *val = ph->phy_adv_asmpause && ph->phy_lp_asmpause && 908*9860Sgdamore@opensolaris.org ph->phy_adv_pause != ph->phy_lp_pause; 909*9860Sgdamore@opensolaris.org break; 910*9860Sgdamore@opensolaris.org case ETHER_STAT_LINK_PAUSE: 911*9860Sgdamore@opensolaris.org *val = (ph->phy_flowctrl == LINK_FLOWCTRL_BI) || 912*9860Sgdamore@opensolaris.org (ph->phy_flowctrl == LINK_FLOWCTRL_RX); 913*9860Sgdamore@opensolaris.org break; 914*9860Sgdamore@opensolaris.org case ETHER_STAT_CAP_1000FDX: 915*9860Sgdamore@opensolaris.org *val = ph->phy_cap_1000_fdx; 916*9860Sgdamore@opensolaris.org break; 917*9860Sgdamore@opensolaris.org case ETHER_STAT_CAP_1000HDX: 918*9860Sgdamore@opensolaris.org *val = ph->phy_cap_1000_hdx; 919*9860Sgdamore@opensolaris.org break; 920*9860Sgdamore@opensolaris.org case ETHER_STAT_CAP_100FDX: 921*9860Sgdamore@opensolaris.org *val = ph->phy_cap_100_fdx; 922*9860Sgdamore@opensolaris.org break; 923*9860Sgdamore@opensolaris.org case ETHER_STAT_CAP_100HDX: 924*9860Sgdamore@opensolaris.org *val = ph->phy_cap_100_hdx; 925*9860Sgdamore@opensolaris.org break; 926*9860Sgdamore@opensolaris.org case ETHER_STAT_CAP_10FDX: 927*9860Sgdamore@opensolaris.org *val = ph->phy_cap_10_fdx; 928*9860Sgdamore@opensolaris.org break; 929*9860Sgdamore@opensolaris.org case ETHER_STAT_CAP_10HDX: 930*9860Sgdamore@opensolaris.org *val = ph->phy_cap_10_hdx; 931*9860Sgdamore@opensolaris.org break; 932*9860Sgdamore@opensolaris.org case ETHER_STAT_CAP_100T4: 933*9860Sgdamore@opensolaris.org *val = ph->phy_cap_100_t4; 934*9860Sgdamore@opensolaris.org break; 935*9860Sgdamore@opensolaris.org case ETHER_STAT_CAP_AUTONEG: 936*9860Sgdamore@opensolaris.org *val = ph->phy_cap_aneg; 937*9860Sgdamore@opensolaris.org break; 938*9860Sgdamore@opensolaris.org case ETHER_STAT_CAP_PAUSE: 939*9860Sgdamore@opensolaris.org *val = ph->phy_cap_pause; 940*9860Sgdamore@opensolaris.org break; 941*9860Sgdamore@opensolaris.org case ETHER_STAT_CAP_ASMPAUSE: 942*9860Sgdamore@opensolaris.org *val = ph->phy_cap_asmpause; 943*9860Sgdamore@opensolaris.org break; 944*9860Sgdamore@opensolaris.org 945*9860Sgdamore@opensolaris.org case ETHER_STAT_LP_CAP_1000FDX: 946*9860Sgdamore@opensolaris.org *val = ph->phy_lp_1000_fdx; 947*9860Sgdamore@opensolaris.org break; 948*9860Sgdamore@opensolaris.org case ETHER_STAT_LP_CAP_1000HDX: 949*9860Sgdamore@opensolaris.org *val = ph->phy_lp_1000_hdx; 950*9860Sgdamore@opensolaris.org break; 951*9860Sgdamore@opensolaris.org case ETHER_STAT_LP_CAP_100FDX: 952*9860Sgdamore@opensolaris.org *val = ph->phy_lp_100_fdx; 953*9860Sgdamore@opensolaris.org break; 954*9860Sgdamore@opensolaris.org case ETHER_STAT_LP_CAP_100HDX: 955*9860Sgdamore@opensolaris.org *val = ph->phy_lp_100_hdx; 956*9860Sgdamore@opensolaris.org break; 957*9860Sgdamore@opensolaris.org case ETHER_STAT_LP_CAP_10FDX: 958*9860Sgdamore@opensolaris.org *val = ph->phy_lp_10_fdx; 959*9860Sgdamore@opensolaris.org break; 960*9860Sgdamore@opensolaris.org case ETHER_STAT_LP_CAP_10HDX: 961*9860Sgdamore@opensolaris.org *val = ph->phy_lp_10_hdx; 962*9860Sgdamore@opensolaris.org break; 963*9860Sgdamore@opensolaris.org case ETHER_STAT_LP_CAP_100T4: 964*9860Sgdamore@opensolaris.org *val = ph->phy_lp_100_t4; 965*9860Sgdamore@opensolaris.org break; 966*9860Sgdamore@opensolaris.org case ETHER_STAT_LP_CAP_AUTONEG: 967*9860Sgdamore@opensolaris.org *val = ph->phy_lp_aneg; 968*9860Sgdamore@opensolaris.org break; 969*9860Sgdamore@opensolaris.org case ETHER_STAT_LP_CAP_PAUSE: 970*9860Sgdamore@opensolaris.org *val = ph->phy_lp_pause; 971*9860Sgdamore@opensolaris.org break; 972*9860Sgdamore@opensolaris.org case ETHER_STAT_LP_CAP_ASMPAUSE: 973*9860Sgdamore@opensolaris.org *val = ph->phy_lp_asmpause; 974*9860Sgdamore@opensolaris.org break; 975*9860Sgdamore@opensolaris.org 976*9860Sgdamore@opensolaris.org case ETHER_STAT_ADV_CAP_1000FDX: 977*9860Sgdamore@opensolaris.org *val = ph->phy_adv_1000_fdx; 978*9860Sgdamore@opensolaris.org break; 979*9860Sgdamore@opensolaris.org case ETHER_STAT_ADV_CAP_1000HDX: 980*9860Sgdamore@opensolaris.org *val = ph->phy_adv_1000_hdx; 981*9860Sgdamore@opensolaris.org break; 982*9860Sgdamore@opensolaris.org case ETHER_STAT_ADV_CAP_100FDX: 983*9860Sgdamore@opensolaris.org *val = ph->phy_adv_100_fdx; 984*9860Sgdamore@opensolaris.org break; 985*9860Sgdamore@opensolaris.org case ETHER_STAT_ADV_CAP_100HDX: 986*9860Sgdamore@opensolaris.org *val = ph->phy_adv_100_hdx; 987*9860Sgdamore@opensolaris.org break; 988*9860Sgdamore@opensolaris.org case ETHER_STAT_ADV_CAP_10FDX: 989*9860Sgdamore@opensolaris.org *val = ph->phy_adv_10_fdx; 990*9860Sgdamore@opensolaris.org break; 991*9860Sgdamore@opensolaris.org case ETHER_STAT_ADV_CAP_10HDX: 992*9860Sgdamore@opensolaris.org *val = ph->phy_adv_10_hdx; 993*9860Sgdamore@opensolaris.org break; 994*9860Sgdamore@opensolaris.org case ETHER_STAT_ADV_CAP_100T4: 995*9860Sgdamore@opensolaris.org *val = ph->phy_adv_100_t4; 996*9860Sgdamore@opensolaris.org break; 997*9860Sgdamore@opensolaris.org case ETHER_STAT_ADV_CAP_AUTONEG: 998*9860Sgdamore@opensolaris.org *val = ph->phy_adv_aneg; 999*9860Sgdamore@opensolaris.org break; 1000*9860Sgdamore@opensolaris.org case ETHER_STAT_ADV_CAP_PAUSE: 1001*9860Sgdamore@opensolaris.org *val = ph->phy_adv_pause; 1002*9860Sgdamore@opensolaris.org break; 1003*9860Sgdamore@opensolaris.org case ETHER_STAT_ADV_CAP_ASMPAUSE: 1004*9860Sgdamore@opensolaris.org *val = ph->phy_adv_asmpause; 1005*9860Sgdamore@opensolaris.org break; 1006*9860Sgdamore@opensolaris.org 1007*9860Sgdamore@opensolaris.org default: 1008*9860Sgdamore@opensolaris.org rv = ENOTSUP; 1009*9860Sgdamore@opensolaris.org break; 1010*9860Sgdamore@opensolaris.org } 1011*9860Sgdamore@opensolaris.org mutex_exit(&mh->m_lock); 1012*9860Sgdamore@opensolaris.org 1013*9860Sgdamore@opensolaris.org return (rv); 1014*9860Sgdamore@opensolaris.org } 1015*9860Sgdamore@opensolaris.org 1016*9860Sgdamore@opensolaris.org /* 1017*9860Sgdamore@opensolaris.org * PHY support routines. Private to the MII module and the vendor 1018*9860Sgdamore@opensolaris.org * specific PHY implementation code. 1019*9860Sgdamore@opensolaris.org */ 1020*9860Sgdamore@opensolaris.org uint16_t 1021*9860Sgdamore@opensolaris.org phy_read(phy_handle_t *ph, uint8_t reg) 1022*9860Sgdamore@opensolaris.org { 1023*9860Sgdamore@opensolaris.org mii_handle_t mh = ph->phy_mii; 1024*9860Sgdamore@opensolaris.org 1025*9860Sgdamore@opensolaris.org return ((*mh->m_ops.mii_read)(mh->m_private, ph->phy_addr, reg)); 1026*9860Sgdamore@opensolaris.org } 1027*9860Sgdamore@opensolaris.org 1028*9860Sgdamore@opensolaris.org void 1029*9860Sgdamore@opensolaris.org phy_write(phy_handle_t *ph, uint8_t reg, uint16_t val) 1030*9860Sgdamore@opensolaris.org { 1031*9860Sgdamore@opensolaris.org mii_handle_t mh = ph->phy_mii; 1032*9860Sgdamore@opensolaris.org 1033*9860Sgdamore@opensolaris.org (*mh->m_ops.mii_write)(mh->m_private, ph->phy_addr, reg, val); 1034*9860Sgdamore@opensolaris.org } 1035*9860Sgdamore@opensolaris.org 1036*9860Sgdamore@opensolaris.org int 1037*9860Sgdamore@opensolaris.org phy_reset(phy_handle_t *ph) 1038*9860Sgdamore@opensolaris.org { 1039*9860Sgdamore@opensolaris.org ASSERT(mutex_owned(&ph->phy_mii->m_lock)); 1040*9860Sgdamore@opensolaris.org 1041*9860Sgdamore@opensolaris.org /* 1042*9860Sgdamore@opensolaris.org * For our device, make sure its powered up, unisolated, and 1043*9860Sgdamore@opensolaris.org * not in loopback. 1044*9860Sgdamore@opensolaris.org */ 1045*9860Sgdamore@opensolaris.org PHY_CLR(ph, MII_CONTROL, 1046*9860Sgdamore@opensolaris.org MII_CONTROL_PWRDN | MII_CONTROL_LOOPBACK | MII_CONTROL_ISOLATE); 1047*9860Sgdamore@opensolaris.org 1048*9860Sgdamore@opensolaris.org /* 1049*9860Sgdamore@opensolaris.org * Finally reset it. 1050*9860Sgdamore@opensolaris.org */ 1051*9860Sgdamore@opensolaris.org PHY_SET(ph, MII_CONTROL, MII_CONTROL_RESET); 1052*9860Sgdamore@opensolaris.org 1053*9860Sgdamore@opensolaris.org /* 1054*9860Sgdamore@opensolaris.org * Apparently some devices (DP83840A) like to have a little 1055*9860Sgdamore@opensolaris.org * bit of a wait before we start accessing anything else on 1056*9860Sgdamore@opensolaris.org * the PHY. 1057*9860Sgdamore@opensolaris.org */ 1058*9860Sgdamore@opensolaris.org drv_usecwait(500); 1059*9860Sgdamore@opensolaris.org 1060*9860Sgdamore@opensolaris.org /* 1061*9860Sgdamore@opensolaris.org * Wait for reset to complete - probably very fast, but no 1062*9860Sgdamore@opensolaris.org * more than 0.5 sec according to spec. It would be nice if 1063*9860Sgdamore@opensolaris.org * we could use delay() here, but MAC drivers may call 1064*9860Sgdamore@opensolaris.org * functions which hold this lock in interrupt context, so 1065*9860Sgdamore@opensolaris.org * sleeping would be a definite no-no. The good news here is 1066*9860Sgdamore@opensolaris.org * that it seems to be the case that most devices come back 1067*9860Sgdamore@opensolaris.org * within only a few hundred usec. 1068*9860Sgdamore@opensolaris.org */ 1069*9860Sgdamore@opensolaris.org for (int i = 500000; i; i -= 100) { 1070*9860Sgdamore@opensolaris.org if ((phy_read(ph, MII_CONTROL) & MII_CONTROL_RESET) == 0) { 1071*9860Sgdamore@opensolaris.org /* reset completed */ 1072*9860Sgdamore@opensolaris.org return (DDI_SUCCESS); 1073*9860Sgdamore@opensolaris.org } 1074*9860Sgdamore@opensolaris.org drv_usecwait(100); 1075*9860Sgdamore@opensolaris.org } 1076*9860Sgdamore@opensolaris.org 1077*9860Sgdamore@opensolaris.org return (DDI_FAILURE); 1078*9860Sgdamore@opensolaris.org } 1079*9860Sgdamore@opensolaris.org 1080*9860Sgdamore@opensolaris.org int 1081*9860Sgdamore@opensolaris.org phy_stop(phy_handle_t *ph) 1082*9860Sgdamore@opensolaris.org { 1083*9860Sgdamore@opensolaris.org phy_write(ph, MII_CONTROL, 1084*9860Sgdamore@opensolaris.org MII_CONTROL_ISOLATE | MII_CONTROL_PWRDN | 1085*9860Sgdamore@opensolaris.org MII_CONTROL_LOOPBACK); 1086*9860Sgdamore@opensolaris.org return (DDI_SUCCESS); 1087*9860Sgdamore@opensolaris.org } 1088*9860Sgdamore@opensolaris.org 1089*9860Sgdamore@opensolaris.org int 1090*9860Sgdamore@opensolaris.org phy_start(phy_handle_t *ph) 1091*9860Sgdamore@opensolaris.org { 1092*9860Sgdamore@opensolaris.org uint16_t bmcr, anar, gtcr; 1093*9860Sgdamore@opensolaris.org ASSERT(mutex_owned(&ph->phy_mii->m_lock)); 1094*9860Sgdamore@opensolaris.org 1095*9860Sgdamore@opensolaris.org /* 1096*9860Sgdamore@opensolaris.org * Disable everything to start... we'll add in modes as we go. 1097*9860Sgdamore@opensolaris.org */ 1098*9860Sgdamore@opensolaris.org ph->phy_adv_aneg = B_FALSE; 1099*9860Sgdamore@opensolaris.org ph->phy_adv_1000_fdx = B_FALSE; 1100*9860Sgdamore@opensolaris.org ph->phy_adv_1000_hdx = B_FALSE; 1101*9860Sgdamore@opensolaris.org ph->phy_adv_100_fdx = B_FALSE; 1102*9860Sgdamore@opensolaris.org ph->phy_adv_100_t4 = B_FALSE; 1103*9860Sgdamore@opensolaris.org ph->phy_adv_100_hdx = B_FALSE; 1104*9860Sgdamore@opensolaris.org ph->phy_adv_10_fdx = B_FALSE; 1105*9860Sgdamore@opensolaris.org ph->phy_adv_10_hdx = B_FALSE; 1106*9860Sgdamore@opensolaris.org ph->phy_adv_pause = B_FALSE; 1107*9860Sgdamore@opensolaris.org ph->phy_adv_asmpause = B_FALSE; 1108*9860Sgdamore@opensolaris.org 1109*9860Sgdamore@opensolaris.org switch (ph->phy_loopback) { 1110*9860Sgdamore@opensolaris.org case PHY_LB_NONE: 1111*9860Sgdamore@opensolaris.org /* 1112*9860Sgdamore@opensolaris.org * No loopback overrides, so try to advertise everything 1113*9860Sgdamore@opensolaris.org * that is administratively enabled. 1114*9860Sgdamore@opensolaris.org */ 1115*9860Sgdamore@opensolaris.org ph->phy_adv_aneg = ph->phy_en_aneg; 1116*9860Sgdamore@opensolaris.org ph->phy_adv_1000_fdx = ph->phy_en_1000_fdx; 1117*9860Sgdamore@opensolaris.org ph->phy_adv_1000_hdx = ph->phy_en_1000_hdx; 1118*9860Sgdamore@opensolaris.org ph->phy_adv_100_fdx = ph->phy_en_100_fdx; 1119*9860Sgdamore@opensolaris.org ph->phy_adv_100_t4 = ph->phy_en_100_t4; 1120*9860Sgdamore@opensolaris.org ph->phy_adv_100_hdx = ph->phy_en_100_hdx; 1121*9860Sgdamore@opensolaris.org ph->phy_adv_10_fdx = ph->phy_en_10_fdx; 1122*9860Sgdamore@opensolaris.org ph->phy_adv_10_hdx = ph->phy_en_10_hdx; 1123*9860Sgdamore@opensolaris.org ph->phy_adv_pause = ph->phy_en_pause; 1124*9860Sgdamore@opensolaris.org ph->phy_adv_asmpause = ph->phy_en_asmpause; 1125*9860Sgdamore@opensolaris.org break; 1126*9860Sgdamore@opensolaris.org 1127*9860Sgdamore@opensolaris.org case PHY_LB_INT_PHY: 1128*9860Sgdamore@opensolaris.org if (ph->phy_cap_1000_fdx) { 1129*9860Sgdamore@opensolaris.org ph->phy_adv_1000_fdx = B_TRUE; 1130*9860Sgdamore@opensolaris.org } else if (ph->phy_cap_100_fdx) { 1131*9860Sgdamore@opensolaris.org ph->phy_adv_100_fdx = B_TRUE; 1132*9860Sgdamore@opensolaris.org } else if (ph->phy_cap_10_fdx) { 1133*9860Sgdamore@opensolaris.org ph->phy_adv_10_fdx = B_TRUE; 1134*9860Sgdamore@opensolaris.org } 1135*9860Sgdamore@opensolaris.org break; 1136*9860Sgdamore@opensolaris.org 1137*9860Sgdamore@opensolaris.org case PHY_LB_EXT_10: 1138*9860Sgdamore@opensolaris.org ph->phy_adv_10_fdx = B_TRUE; 1139*9860Sgdamore@opensolaris.org break; 1140*9860Sgdamore@opensolaris.org 1141*9860Sgdamore@opensolaris.org case PHY_LB_EXT_100: 1142*9860Sgdamore@opensolaris.org ph->phy_adv_100_fdx = B_TRUE; 1143*9860Sgdamore@opensolaris.org break; 1144*9860Sgdamore@opensolaris.org 1145*9860Sgdamore@opensolaris.org case PHY_LB_EXT_1000: 1146*9860Sgdamore@opensolaris.org ph->phy_adv_1000_fdx = B_TRUE; 1147*9860Sgdamore@opensolaris.org break; 1148*9860Sgdamore@opensolaris.org } 1149*9860Sgdamore@opensolaris.org 1150*9860Sgdamore@opensolaris.org /* 1151*9860Sgdamore@opensolaris.org * Limit properties to what the hardware can actually support. 1152*9860Sgdamore@opensolaris.org */ 1153*9860Sgdamore@opensolaris.org #define FILTER_ADV(CAP) \ 1154*9860Sgdamore@opensolaris.org if (!ph->phy_cap_##CAP) \ 1155*9860Sgdamore@opensolaris.org ph->phy_adv_##CAP = 0 1156*9860Sgdamore@opensolaris.org 1157*9860Sgdamore@opensolaris.org FILTER_ADV(aneg); 1158*9860Sgdamore@opensolaris.org FILTER_ADV(1000_fdx); 1159*9860Sgdamore@opensolaris.org FILTER_ADV(1000_hdx); 1160*9860Sgdamore@opensolaris.org FILTER_ADV(100_fdx); 1161*9860Sgdamore@opensolaris.org FILTER_ADV(100_t4); 1162*9860Sgdamore@opensolaris.org FILTER_ADV(100_hdx); 1163*9860Sgdamore@opensolaris.org FILTER_ADV(10_fdx); 1164*9860Sgdamore@opensolaris.org FILTER_ADV(10_hdx); 1165*9860Sgdamore@opensolaris.org FILTER_ADV(pause); 1166*9860Sgdamore@opensolaris.org FILTER_ADV(asmpause); 1167*9860Sgdamore@opensolaris.org 1168*9860Sgdamore@opensolaris.org #undef FILTER_ADV 1169*9860Sgdamore@opensolaris.org 1170*9860Sgdamore@opensolaris.org /* 1171*9860Sgdamore@opensolaris.org * We need at least one valid mode. 1172*9860Sgdamore@opensolaris.org */ 1173*9860Sgdamore@opensolaris.org if ((!ph->phy_adv_1000_fdx) && 1174*9860Sgdamore@opensolaris.org (!ph->phy_adv_1000_hdx) && 1175*9860Sgdamore@opensolaris.org (!ph->phy_adv_100_t4) && 1176*9860Sgdamore@opensolaris.org (!ph->phy_adv_100_fdx) && 1177*9860Sgdamore@opensolaris.org (!ph->phy_adv_100_hdx) && 1178*9860Sgdamore@opensolaris.org (!ph->phy_adv_10_fdx) && 1179*9860Sgdamore@opensolaris.org (!ph->phy_adv_10_hdx)) { 1180*9860Sgdamore@opensolaris.org 1181*9860Sgdamore@opensolaris.org phy_warn(ph, 1182*9860Sgdamore@opensolaris.org "No valid link mode selected. Powering down PHY."); 1183*9860Sgdamore@opensolaris.org 1184*9860Sgdamore@opensolaris.org PHY_SET(ph, MII_CONTROL, MII_CONTROL_PWRDN); 1185*9860Sgdamore@opensolaris.org 1186*9860Sgdamore@opensolaris.org ph->phy_link = LINK_STATE_DOWN; 1187*9860Sgdamore@opensolaris.org return (DDI_SUCCESS); 1188*9860Sgdamore@opensolaris.org } 1189*9860Sgdamore@opensolaris.org 1190*9860Sgdamore@opensolaris.org switch (ph->phy_loopback) { 1191*9860Sgdamore@opensolaris.org case PHY_LB_INT_PHY: 1192*9860Sgdamore@opensolaris.org bmcr = MII_CONTROL_LOOPBACK; 1193*9860Sgdamore@opensolaris.org gtcr = 0; 1194*9860Sgdamore@opensolaris.org break; 1195*9860Sgdamore@opensolaris.org case PHY_LB_EXT_10: 1196*9860Sgdamore@opensolaris.org case PHY_LB_EXT_100: 1197*9860Sgdamore@opensolaris.org case PHY_LB_EXT_1000: 1198*9860Sgdamore@opensolaris.org bmcr = 0; 1199*9860Sgdamore@opensolaris.org gtcr = MII_MSCONTROL_MANUAL | MII_MSCONTROL_MASTER; 1200*9860Sgdamore@opensolaris.org break; 1201*9860Sgdamore@opensolaris.org case PHY_LB_NONE: 1202*9860Sgdamore@opensolaris.org default: 1203*9860Sgdamore@opensolaris.org bmcr = 0; 1204*9860Sgdamore@opensolaris.org gtcr = 0; 1205*9860Sgdamore@opensolaris.org break; 1206*9860Sgdamore@opensolaris.org } 1207*9860Sgdamore@opensolaris.org 1208*9860Sgdamore@opensolaris.org if (ph->phy_adv_aneg) { 1209*9860Sgdamore@opensolaris.org bmcr |= MII_CONTROL_ANE | MII_CONTROL_RSAN; 1210*9860Sgdamore@opensolaris.org } 1211*9860Sgdamore@opensolaris.org 1212*9860Sgdamore@opensolaris.org if ((ph->phy_adv_1000_fdx) || (ph->phy_adv_1000_hdx)) { 1213*9860Sgdamore@opensolaris.org bmcr |= MII_CONTROL_1GB; 1214*9860Sgdamore@opensolaris.org 1215*9860Sgdamore@opensolaris.org } else if (ph->phy_adv_100_fdx || ph->phy_adv_100_hdx || 1216*9860Sgdamore@opensolaris.org ph->phy_adv_100_t4) { 1217*9860Sgdamore@opensolaris.org bmcr |= MII_CONTROL_100MB; 1218*9860Sgdamore@opensolaris.org } 1219*9860Sgdamore@opensolaris.org 1220*9860Sgdamore@opensolaris.org if (ph->phy_adv_1000_fdx || ph->phy_adv_100_fdx || ph->phy_adv_10_fdx) { 1221*9860Sgdamore@opensolaris.org bmcr |= MII_CONTROL_FDUPLEX; 1222*9860Sgdamore@opensolaris.org } 1223*9860Sgdamore@opensolaris.org 1224*9860Sgdamore@opensolaris.org if (ph->phy_type == XCVR_1000X) { 1225*9860Sgdamore@opensolaris.org /* 1000BASE-X (usually fiber) */ 1226*9860Sgdamore@opensolaris.org anar = 0; 1227*9860Sgdamore@opensolaris.org if (ph->phy_adv_1000_fdx) { 1228*9860Sgdamore@opensolaris.org anar |= MII_ABILITY_X_FD; 1229*9860Sgdamore@opensolaris.org } 1230*9860Sgdamore@opensolaris.org if (ph->phy_adv_1000_hdx) { 1231*9860Sgdamore@opensolaris.org anar |= MII_ABILITY_X_HD; 1232*9860Sgdamore@opensolaris.org } 1233*9860Sgdamore@opensolaris.org if (ph->phy_adv_pause) { 1234*9860Sgdamore@opensolaris.org anar |= MII_ABILITY_X_PAUSE; 1235*9860Sgdamore@opensolaris.org } 1236*9860Sgdamore@opensolaris.org if (ph->phy_adv_asmpause) { 1237*9860Sgdamore@opensolaris.org anar |= MII_ABILITY_X_ASMPAUSE; 1238*9860Sgdamore@opensolaris.org } 1239*9860Sgdamore@opensolaris.org 1240*9860Sgdamore@opensolaris.org } else if (ph->phy_type == XCVR_100T2) { 1241*9860Sgdamore@opensolaris.org /* 100BASE-T2 */ 1242*9860Sgdamore@opensolaris.org anar = 0; 1243*9860Sgdamore@opensolaris.org if (ph->phy_adv_100_fdx) { 1244*9860Sgdamore@opensolaris.org anar |= MII_ABILITY_T2_FD; 1245*9860Sgdamore@opensolaris.org } 1246*9860Sgdamore@opensolaris.org if (ph->phy_adv_100_hdx) { 1247*9860Sgdamore@opensolaris.org anar |= MII_ABILITY_T2_HD; 1248*9860Sgdamore@opensolaris.org } 1249*9860Sgdamore@opensolaris.org 1250*9860Sgdamore@opensolaris.org } else { 1251*9860Sgdamore@opensolaris.org anar = MII_AN_SELECTOR_8023; 1252*9860Sgdamore@opensolaris.org 1253*9860Sgdamore@opensolaris.org /* 1000BASE-T or 100BASE-X probably */ 1254*9860Sgdamore@opensolaris.org if (ph->phy_adv_1000_fdx) { 1255*9860Sgdamore@opensolaris.org gtcr |= MII_MSCONTROL_1000T_FD; 1256*9860Sgdamore@opensolaris.org } 1257*9860Sgdamore@opensolaris.org if (ph->phy_adv_1000_hdx) { 1258*9860Sgdamore@opensolaris.org gtcr |= MII_MSCONTROL_1000T; 1259*9860Sgdamore@opensolaris.org } 1260*9860Sgdamore@opensolaris.org if (ph->phy_adv_100_fdx) { 1261*9860Sgdamore@opensolaris.org anar |= MII_ABILITY_100BASE_TX_FD; 1262*9860Sgdamore@opensolaris.org } 1263*9860Sgdamore@opensolaris.org if (ph->phy_adv_100_hdx) { 1264*9860Sgdamore@opensolaris.org anar |= MII_ABILITY_100BASE_TX; 1265*9860Sgdamore@opensolaris.org } 1266*9860Sgdamore@opensolaris.org if (ph->phy_adv_100_t4) { 1267*9860Sgdamore@opensolaris.org anar |= MII_ABILITY_100BASE_T4; 1268*9860Sgdamore@opensolaris.org } 1269*9860Sgdamore@opensolaris.org if (ph->phy_adv_10_fdx) { 1270*9860Sgdamore@opensolaris.org anar |= MII_ABILITY_10BASE_T_FD; 1271*9860Sgdamore@opensolaris.org } 1272*9860Sgdamore@opensolaris.org if (ph->phy_adv_100_hdx) { 1273*9860Sgdamore@opensolaris.org anar |= MII_ABILITY_10BASE_T; 1274*9860Sgdamore@opensolaris.org } 1275*9860Sgdamore@opensolaris.org if (ph->phy_adv_pause) { 1276*9860Sgdamore@opensolaris.org anar |= MII_ABILITY_PAUSE; 1277*9860Sgdamore@opensolaris.org } 1278*9860Sgdamore@opensolaris.org if (ph->phy_adv_asmpause) { 1279*9860Sgdamore@opensolaris.org anar |= MII_ABILITY_ASMPAUSE; 1280*9860Sgdamore@opensolaris.org } 1281*9860Sgdamore@opensolaris.org } 1282*9860Sgdamore@opensolaris.org 1283*9860Sgdamore@opensolaris.org ph->phy_link = LINK_STATE_DOWN; 1284*9860Sgdamore@opensolaris.org ph->phy_duplex = LINK_DUPLEX_UNKNOWN; 1285*9860Sgdamore@opensolaris.org ph->phy_speed = 0; 1286*9860Sgdamore@opensolaris.org 1287*9860Sgdamore@opensolaris.org phy_write(ph, MII_AN_ADVERT, anar); 1288*9860Sgdamore@opensolaris.org phy_write(ph, MII_CONTROL, 1289*9860Sgdamore@opensolaris.org bmcr & ~(MII_CONTROL_ANE|MII_CONTROL_RSAN|MII_CONTROL_LOOPBACK)); 1290*9860Sgdamore@opensolaris.org switch (ph->phy_type) { 1291*9860Sgdamore@opensolaris.org case XCVR_1000T: 1292*9860Sgdamore@opensolaris.org case XCVR_1000X: 1293*9860Sgdamore@opensolaris.org case XCVR_100T2: 1294*9860Sgdamore@opensolaris.org phy_write(ph, MII_MSCONTROL, gtcr); 1295*9860Sgdamore@opensolaris.org } 1296*9860Sgdamore@opensolaris.org 1297*9860Sgdamore@opensolaris.org /* 1298*9860Sgdamore@opensolaris.org * Finally, this will start up autoneg if it is enabled, or 1299*9860Sgdamore@opensolaris.org * force link settings otherwise. 1300*9860Sgdamore@opensolaris.org */ 1301*9860Sgdamore@opensolaris.org phy_write(ph, MII_CONTROL, bmcr); 1302*9860Sgdamore@opensolaris.org 1303*9860Sgdamore@opensolaris.org return (DDI_SUCCESS); 1304*9860Sgdamore@opensolaris.org } 1305*9860Sgdamore@opensolaris.org 1306*9860Sgdamore@opensolaris.org 1307*9860Sgdamore@opensolaris.org int 1308*9860Sgdamore@opensolaris.org phy_check(phy_handle_t *ph) 1309*9860Sgdamore@opensolaris.org { 1310*9860Sgdamore@opensolaris.org uint16_t control, status, lpar, msstat, anexp; 1311*9860Sgdamore@opensolaris.org int debounces = 100; 1312*9860Sgdamore@opensolaris.org 1313*9860Sgdamore@opensolaris.org ASSERT(mutex_owned(&ph->phy_mii->m_lock)); 1314*9860Sgdamore@opensolaris.org 1315*9860Sgdamore@opensolaris.org debounce: 1316*9860Sgdamore@opensolaris.org status = phy_read(ph, MII_STATUS); 1317*9860Sgdamore@opensolaris.org control = phy_read(ph, MII_CONTROL); 1318*9860Sgdamore@opensolaris.org 1319*9860Sgdamore@opensolaris.org if (status & MII_STATUS_EXTENDED) { 1320*9860Sgdamore@opensolaris.org lpar = phy_read(ph, MII_AN_LPABLE); 1321*9860Sgdamore@opensolaris.org anexp = phy_read(ph, MII_AN_EXPANSION); 1322*9860Sgdamore@opensolaris.org } else { 1323*9860Sgdamore@opensolaris.org lpar = 0; 1324*9860Sgdamore@opensolaris.org anexp = 0; 1325*9860Sgdamore@opensolaris.org } 1326*9860Sgdamore@opensolaris.org 1327*9860Sgdamore@opensolaris.org /* 1328*9860Sgdamore@opensolaris.org * We reread to clear any latched bits. This also debounces 1329*9860Sgdamore@opensolaris.org * any state that might be in transition. 1330*9860Sgdamore@opensolaris.org */ 1331*9860Sgdamore@opensolaris.org drv_usecwait(10); 1332*9860Sgdamore@opensolaris.org if ((status != phy_read(ph, MII_STATUS)) && debounces) { 1333*9860Sgdamore@opensolaris.org debounces--; 1334*9860Sgdamore@opensolaris.org goto debounce; 1335*9860Sgdamore@opensolaris.org } 1336*9860Sgdamore@opensolaris.org 1337*9860Sgdamore@opensolaris.org /* 1338*9860Sgdamore@opensolaris.org * Detect the situation where the PHY is removed or has died. 1339*9860Sgdamore@opensolaris.org * According to spec, at least one bit of status must be set, 1340*9860Sgdamore@opensolaris.org * and at least one bit must be clear. 1341*9860Sgdamore@opensolaris.org */ 1342*9860Sgdamore@opensolaris.org if ((status == 0xffff) || (status == 0)) { 1343*9860Sgdamore@opensolaris.org ph->phy_speed = 0; 1344*9860Sgdamore@opensolaris.org ph->phy_duplex = LINK_DUPLEX_UNKNOWN; 1345*9860Sgdamore@opensolaris.org ph->phy_link = LINK_STATE_UNKNOWN; 1346*9860Sgdamore@opensolaris.org ph->phy_present = B_FALSE; 1347*9860Sgdamore@opensolaris.org return (DDI_FAILURE); 1348*9860Sgdamore@opensolaris.org } 1349*9860Sgdamore@opensolaris.org 1350*9860Sgdamore@opensolaris.org /* We only respect the link flag if we are not in loopback. */ 1351*9860Sgdamore@opensolaris.org if ((ph->phy_loopback != PHY_LB_INT_PHY) && 1352*9860Sgdamore@opensolaris.org ((status & MII_STATUS_LINKUP) == 0)) { 1353*9860Sgdamore@opensolaris.org ph->phy_speed = 0; 1354*9860Sgdamore@opensolaris.org ph->phy_duplex = LINK_DUPLEX_UNKNOWN; 1355*9860Sgdamore@opensolaris.org ph->phy_link = LINK_STATE_DOWN; 1356*9860Sgdamore@opensolaris.org return (DDI_SUCCESS); 1357*9860Sgdamore@opensolaris.org } 1358*9860Sgdamore@opensolaris.org 1359*9860Sgdamore@opensolaris.org ph->phy_link = LINK_STATE_UP; 1360*9860Sgdamore@opensolaris.org 1361*9860Sgdamore@opensolaris.org if ((control & MII_CONTROL_ANE) == 0) { 1362*9860Sgdamore@opensolaris.org 1363*9860Sgdamore@opensolaris.org ph->phy_lp_aneg = B_FALSE; 1364*9860Sgdamore@opensolaris.org ph->phy_lp_10_hdx = B_FALSE; 1365*9860Sgdamore@opensolaris.org ph->phy_lp_10_fdx = B_FALSE; 1366*9860Sgdamore@opensolaris.org ph->phy_lp_100_t4 = B_FALSE; 1367*9860Sgdamore@opensolaris.org ph->phy_lp_100_hdx = B_FALSE; 1368*9860Sgdamore@opensolaris.org ph->phy_lp_100_fdx = B_FALSE; 1369*9860Sgdamore@opensolaris.org ph->phy_lp_1000_hdx = B_FALSE; 1370*9860Sgdamore@opensolaris.org ph->phy_lp_1000_fdx = B_FALSE; 1371*9860Sgdamore@opensolaris.org 1372*9860Sgdamore@opensolaris.org /* 1373*9860Sgdamore@opensolaris.org * We have no idea what our link partner might or might 1374*9860Sgdamore@opensolaris.org * not be able to support, except that it appears to 1375*9860Sgdamore@opensolaris.org * support the same mode that we have forced. 1376*9860Sgdamore@opensolaris.org */ 1377*9860Sgdamore@opensolaris.org if (control & MII_CONTROL_1GB) { 1378*9860Sgdamore@opensolaris.org ph->phy_speed = 1000; 1379*9860Sgdamore@opensolaris.org } else if (control & MII_CONTROL_100MB) { 1380*9860Sgdamore@opensolaris.org ph->phy_speed = 100; 1381*9860Sgdamore@opensolaris.org } else { 1382*9860Sgdamore@opensolaris.org ph->phy_speed = 10; 1383*9860Sgdamore@opensolaris.org } 1384*9860Sgdamore@opensolaris.org ph->phy_duplex = control & MII_CONTROL_FDUPLEX ? 1385*9860Sgdamore@opensolaris.org LINK_DUPLEX_FULL : LINK_DUPLEX_HALF; 1386*9860Sgdamore@opensolaris.org 1387*9860Sgdamore@opensolaris.org return (DDI_SUCCESS); 1388*9860Sgdamore@opensolaris.org } 1389*9860Sgdamore@opensolaris.org 1390*9860Sgdamore@opensolaris.org if (ph->phy_type == XCVR_1000X) { 1391*9860Sgdamore@opensolaris.org 1392*9860Sgdamore@opensolaris.org ph->phy_lp_10_hdx = B_FALSE; 1393*9860Sgdamore@opensolaris.org ph->phy_lp_10_fdx = B_FALSE; 1394*9860Sgdamore@opensolaris.org ph->phy_lp_100_t4 = B_FALSE; 1395*9860Sgdamore@opensolaris.org ph->phy_lp_100_hdx = B_FALSE; 1396*9860Sgdamore@opensolaris.org ph->phy_lp_100_fdx = B_FALSE; 1397*9860Sgdamore@opensolaris.org 1398*9860Sgdamore@opensolaris.org /* 1000BASE-X requires autonegotiation */ 1399*9860Sgdamore@opensolaris.org ph->phy_lp_aneg = B_TRUE; 1400*9860Sgdamore@opensolaris.org ph->phy_lp_1000_fdx = !!(lpar & MII_ABILITY_X_FD); 1401*9860Sgdamore@opensolaris.org ph->phy_lp_1000_hdx = !!(lpar & MII_ABILITY_X_HD); 1402*9860Sgdamore@opensolaris.org ph->phy_lp_pause = !!(lpar & MII_ABILITY_X_PAUSE); 1403*9860Sgdamore@opensolaris.org ph->phy_lp_asmpause = !!(lpar & MII_ABILITY_X_ASMPAUSE); 1404*9860Sgdamore@opensolaris.org 1405*9860Sgdamore@opensolaris.org } else if (ph->phy_type == XCVR_100T2) { 1406*9860Sgdamore@opensolaris.org ph->phy_lp_10_hdx = B_FALSE; 1407*9860Sgdamore@opensolaris.org ph->phy_lp_10_fdx = B_FALSE; 1408*9860Sgdamore@opensolaris.org ph->phy_lp_100_t4 = B_FALSE; 1409*9860Sgdamore@opensolaris.org ph->phy_lp_1000_hdx = B_FALSE; 1410*9860Sgdamore@opensolaris.org ph->phy_lp_1000_fdx = B_FALSE; 1411*9860Sgdamore@opensolaris.org ph->phy_lp_pause = B_FALSE; 1412*9860Sgdamore@opensolaris.org ph->phy_lp_asmpause = B_FALSE; 1413*9860Sgdamore@opensolaris.org 1414*9860Sgdamore@opensolaris.org /* 100BASE-T2 requires autonegotiation */ 1415*9860Sgdamore@opensolaris.org ph->phy_lp_aneg = B_TRUE; 1416*9860Sgdamore@opensolaris.org ph->phy_lp_100_fdx = !!(lpar & MII_ABILITY_T2_FD); 1417*9860Sgdamore@opensolaris.org ph->phy_lp_100_hdx = !!(lpar & MII_ABILITY_T2_HD); 1418*9860Sgdamore@opensolaris.org 1419*9860Sgdamore@opensolaris.org } else if (anexp & MII_AN_EXP_PARFAULT) { 1420*9860Sgdamore@opensolaris.org /* 1421*9860Sgdamore@opensolaris.org * Parallel detection fault! This happens when the 1422*9860Sgdamore@opensolaris.org * peer does not use autonegotiation, and the 1423*9860Sgdamore@opensolaris.org * detection logic reports more than one type of legal 1424*9860Sgdamore@opensolaris.org * link is available. Note that parallel detection 1425*9860Sgdamore@opensolaris.org * can only happen with half duplex 10, 100, and 1426*9860Sgdamore@opensolaris.org * 100TX4. We also should not have got here, because 1427*9860Sgdamore@opensolaris.org * the link state bit should have failed. 1428*9860Sgdamore@opensolaris.org */ 1429*9860Sgdamore@opensolaris.org #ifdef DEBUG 1430*9860Sgdamore@opensolaris.org phy_warn(ph, "Parallel detection fault!"); 1431*9860Sgdamore@opensolaris.org #endif 1432*9860Sgdamore@opensolaris.org ph->phy_lp_10_hdx = B_FALSE; 1433*9860Sgdamore@opensolaris.org ph->phy_lp_10_fdx = B_FALSE; 1434*9860Sgdamore@opensolaris.org ph->phy_lp_100_t4 = B_FALSE; 1435*9860Sgdamore@opensolaris.org ph->phy_lp_100_hdx = B_FALSE; 1436*9860Sgdamore@opensolaris.org ph->phy_lp_100_fdx = B_FALSE; 1437*9860Sgdamore@opensolaris.org ph->phy_lp_1000_hdx = B_FALSE; 1438*9860Sgdamore@opensolaris.org ph->phy_lp_1000_fdx = B_FALSE; 1439*9860Sgdamore@opensolaris.org ph->phy_lp_pause = B_FALSE; 1440*9860Sgdamore@opensolaris.org ph->phy_lp_asmpause = B_FALSE; 1441*9860Sgdamore@opensolaris.org ph->phy_speed = 0; 1442*9860Sgdamore@opensolaris.org ph->phy_duplex = LINK_DUPLEX_UNKNOWN; 1443*9860Sgdamore@opensolaris.org return (DDI_SUCCESS); 1444*9860Sgdamore@opensolaris.org 1445*9860Sgdamore@opensolaris.org } else { 1446*9860Sgdamore@opensolaris.org ph->phy_lp_aneg = !!(anexp & MII_AN_EXP_LPCANAN); 1447*9860Sgdamore@opensolaris.org 1448*9860Sgdamore@opensolaris.org /* 1449*9860Sgdamore@opensolaris.org * Note: If the peer doesn't support autonegotiation, then 1450*9860Sgdamore@opensolaris.org * according to clause 28.5.4.5, the link partner ability 1451*9860Sgdamore@opensolaris.org * register will still have the right bits set. However, 1452*9860Sgdamore@opensolaris.org * gigabit modes cannot use legacy parallel detection. 1453*9860Sgdamore@opensolaris.org */ 1454*9860Sgdamore@opensolaris.org 1455*9860Sgdamore@opensolaris.org if ((ph->phy_type == XCVR_1000T) & 1456*9860Sgdamore@opensolaris.org (anexp & MII_AN_EXP_LPCANAN)) { 1457*9860Sgdamore@opensolaris.org 1458*9860Sgdamore@opensolaris.org /* check for gige */ 1459*9860Sgdamore@opensolaris.org msstat = phy_read(ph, MII_MSSTATUS); 1460*9860Sgdamore@opensolaris.org 1461*9860Sgdamore@opensolaris.org ph->phy_lp_1000_hdx = 1462*9860Sgdamore@opensolaris.org !!(msstat & MII_MSSTATUS_LP1000T); 1463*9860Sgdamore@opensolaris.org 1464*9860Sgdamore@opensolaris.org ph->phy_lp_1000_fdx = 1465*9860Sgdamore@opensolaris.org !!(msstat & MII_MSSTATUS_LP1000T_FD); 1466*9860Sgdamore@opensolaris.org } 1467*9860Sgdamore@opensolaris.org 1468*9860Sgdamore@opensolaris.org ph->phy_lp_100_fdx = !!(lpar & MII_ABILITY_100BASE_TX_FD); 1469*9860Sgdamore@opensolaris.org ph->phy_lp_100_hdx = !!(lpar & MII_ABILITY_100BASE_TX); 1470*9860Sgdamore@opensolaris.org ph->phy_lp_100_t4 = !!(lpar & MII_ABILITY_100BASE_T4); 1471*9860Sgdamore@opensolaris.org ph->phy_lp_10_fdx = !!(lpar & MII_ABILITY_10BASE_T_FD); 1472*9860Sgdamore@opensolaris.org ph->phy_lp_10_hdx = !!(lpar & MII_ABILITY_10BASE_T); 1473*9860Sgdamore@opensolaris.org ph->phy_lp_pause = !!(lpar & MII_ABILITY_PAUSE); 1474*9860Sgdamore@opensolaris.org ph->phy_lp_asmpause = !!(lpar & MII_ABILITY_ASMPAUSE); 1475*9860Sgdamore@opensolaris.org } 1476*9860Sgdamore@opensolaris.org 1477*9860Sgdamore@opensolaris.org /* resolve link pause */ 1478*9860Sgdamore@opensolaris.org if ((ph->phy_en_flowctrl == LINK_FLOWCTRL_BI) && 1479*9860Sgdamore@opensolaris.org (ph->phy_lp_pause)) { 1480*9860Sgdamore@opensolaris.org ph->phy_flowctrl = LINK_FLOWCTRL_BI; 1481*9860Sgdamore@opensolaris.org } else if ((ph->phy_en_flowctrl == LINK_FLOWCTRL_RX) && 1482*9860Sgdamore@opensolaris.org (ph->phy_lp_pause || ph->phy_lp_asmpause)) { 1483*9860Sgdamore@opensolaris.org ph->phy_flowctrl = LINK_FLOWCTRL_RX; 1484*9860Sgdamore@opensolaris.org } else if ((ph->phy_en_flowctrl == LINK_FLOWCTRL_TX) && 1485*9860Sgdamore@opensolaris.org (ph->phy_lp_pause)) { 1486*9860Sgdamore@opensolaris.org ph->phy_flowctrl = LINK_FLOWCTRL_TX; 1487*9860Sgdamore@opensolaris.org } else { 1488*9860Sgdamore@opensolaris.org ph->phy_flowctrl = LINK_FLOWCTRL_NONE; 1489*9860Sgdamore@opensolaris.org } 1490*9860Sgdamore@opensolaris.org 1491*9860Sgdamore@opensolaris.org if (ph->phy_adv_1000_fdx && ph->phy_lp_1000_fdx) { 1492*9860Sgdamore@opensolaris.org ph->phy_speed = 1000; 1493*9860Sgdamore@opensolaris.org ph->phy_duplex = LINK_DUPLEX_FULL; 1494*9860Sgdamore@opensolaris.org 1495*9860Sgdamore@opensolaris.org } else if (ph->phy_adv_1000_hdx && ph->phy_lp_1000_hdx) { 1496*9860Sgdamore@opensolaris.org ph->phy_speed = 1000; 1497*9860Sgdamore@opensolaris.org ph->phy_duplex = LINK_DUPLEX_HALF; 1498*9860Sgdamore@opensolaris.org 1499*9860Sgdamore@opensolaris.org } else if (ph->phy_adv_100_fdx && ph->phy_lp_100_fdx) { 1500*9860Sgdamore@opensolaris.org ph->phy_speed = 100; 1501*9860Sgdamore@opensolaris.org ph->phy_duplex = LINK_DUPLEX_FULL; 1502*9860Sgdamore@opensolaris.org 1503*9860Sgdamore@opensolaris.org } else if (ph->phy_adv_100_t4 && ph->phy_lp_100_t4) { 1504*9860Sgdamore@opensolaris.org ph->phy_speed = 100; 1505*9860Sgdamore@opensolaris.org ph->phy_duplex = LINK_DUPLEX_HALF; 1506*9860Sgdamore@opensolaris.org 1507*9860Sgdamore@opensolaris.org } else if (ph->phy_adv_100_hdx && ph->phy_lp_100_hdx) { 1508*9860Sgdamore@opensolaris.org ph->phy_speed = 100; 1509*9860Sgdamore@opensolaris.org ph->phy_duplex = LINK_DUPLEX_HALF; 1510*9860Sgdamore@opensolaris.org 1511*9860Sgdamore@opensolaris.org } else if (ph->phy_adv_10_fdx && ph->phy_lp_10_fdx) { 1512*9860Sgdamore@opensolaris.org ph->phy_speed = 10; 1513*9860Sgdamore@opensolaris.org ph->phy_duplex = LINK_DUPLEX_FULL; 1514*9860Sgdamore@opensolaris.org 1515*9860Sgdamore@opensolaris.org } else if (ph->phy_adv_10_hdx && ph->phy_lp_10_hdx) { 1516*9860Sgdamore@opensolaris.org ph->phy_speed = 10; 1517*9860Sgdamore@opensolaris.org ph->phy_duplex = LINK_DUPLEX_HALF; 1518*9860Sgdamore@opensolaris.org 1519*9860Sgdamore@opensolaris.org } else { 1520*9860Sgdamore@opensolaris.org #ifdef DEBUG 1521*9860Sgdamore@opensolaris.org phy_warn(ph, "No common abilities."); 1522*9860Sgdamore@opensolaris.org #endif 1523*9860Sgdamore@opensolaris.org ph->phy_speed = 0; 1524*9860Sgdamore@opensolaris.org ph->phy_duplex = LINK_DUPLEX_UNKNOWN; 1525*9860Sgdamore@opensolaris.org } 1526*9860Sgdamore@opensolaris.org 1527*9860Sgdamore@opensolaris.org return (DDI_SUCCESS); 1528*9860Sgdamore@opensolaris.org } 1529*9860Sgdamore@opensolaris.org 1530*9860Sgdamore@opensolaris.org int 1531*9860Sgdamore@opensolaris.org phy_get_prop(phy_handle_t *ph, char *prop, int dflt) 1532*9860Sgdamore@opensolaris.org { 1533*9860Sgdamore@opensolaris.org mii_handle_t mh = ph->phy_mii; 1534*9860Sgdamore@opensolaris.org 1535*9860Sgdamore@opensolaris.org return (ddi_prop_get_int(DDI_DEV_T_ANY, mh->m_dip, 0, prop, dflt)); 1536*9860Sgdamore@opensolaris.org } 1537*9860Sgdamore@opensolaris.org 1538*9860Sgdamore@opensolaris.org const char * 1539*9860Sgdamore@opensolaris.org phy_get_name(phy_handle_t *ph) 1540*9860Sgdamore@opensolaris.org { 1541*9860Sgdamore@opensolaris.org mii_handle_t mh = ph->phy_mii; 1542*9860Sgdamore@opensolaris.org 1543*9860Sgdamore@opensolaris.org return (mh->m_name); 1544*9860Sgdamore@opensolaris.org } 1545*9860Sgdamore@opensolaris.org 1546*9860Sgdamore@opensolaris.org const char * 1547*9860Sgdamore@opensolaris.org phy_get_driver(phy_handle_t *ph) 1548*9860Sgdamore@opensolaris.org { 1549*9860Sgdamore@opensolaris.org mii_handle_t mh = ph->phy_mii; 1550*9860Sgdamore@opensolaris.org 1551*9860Sgdamore@opensolaris.org return (ddi_driver_name(mh->m_dip)); 1552*9860Sgdamore@opensolaris.org } 1553*9860Sgdamore@opensolaris.org 1554*9860Sgdamore@opensolaris.org void 1555*9860Sgdamore@opensolaris.org phy_warn(phy_handle_t *ph, const char *fmt, ...) 1556*9860Sgdamore@opensolaris.org { 1557*9860Sgdamore@opensolaris.org va_list va; 1558*9860Sgdamore@opensolaris.org char buf[256]; 1559*9860Sgdamore@opensolaris.org 1560*9860Sgdamore@opensolaris.org (void) snprintf(buf, sizeof (buf), "%s: %s", phy_get_name(ph), fmt); 1561*9860Sgdamore@opensolaris.org 1562*9860Sgdamore@opensolaris.org va_start(va, fmt); 1563*9860Sgdamore@opensolaris.org vcmn_err(CE_WARN, buf, va); 1564*9860Sgdamore@opensolaris.org va_end(va); 1565*9860Sgdamore@opensolaris.org } 1566*9860Sgdamore@opensolaris.org 1567*9860Sgdamore@opensolaris.org /* 1568*9860Sgdamore@opensolaris.org * Internal support routines. 1569*9860Sgdamore@opensolaris.org */ 1570*9860Sgdamore@opensolaris.org 1571*9860Sgdamore@opensolaris.org void 1572*9860Sgdamore@opensolaris.org _mii_probe_phy(phy_handle_t *ph) 1573*9860Sgdamore@opensolaris.org { 1574*9860Sgdamore@opensolaris.org uint16_t bmsr; 1575*9860Sgdamore@opensolaris.org uint16_t extsr; 1576*9860Sgdamore@opensolaris.org mii_handle_t mh = ph->phy_mii; 1577*9860Sgdamore@opensolaris.org 1578*9860Sgdamore@opensolaris.org 1579*9860Sgdamore@opensolaris.org /* 1580*9860Sgdamore@opensolaris.org * Apparently, PHY 0 is less likely to be physically 1581*9860Sgdamore@opensolaris.org * connected, and should always be the last one tried. Most 1582*9860Sgdamore@opensolaris.org * single solution NICs use PHY1 for their built-in 1583*9860Sgdamore@opensolaris.org * transceiver. NICs with an external MII will often place 1584*9860Sgdamore@opensolaris.org * the external PHY at address 1, and use address 0 for the 1585*9860Sgdamore@opensolaris.org * internal PHY. 1586*9860Sgdamore@opensolaris.org */ 1587*9860Sgdamore@opensolaris.org 1588*9860Sgdamore@opensolaris.org ph->phy_id = 0; 1589*9860Sgdamore@opensolaris.org ph->phy_model = "PHY"; 1590*9860Sgdamore@opensolaris.org ph->phy_vendor = "Unknown Vendor"; 1591*9860Sgdamore@opensolaris.org 1592*9860Sgdamore@opensolaris.org /* done twice to clear any latched bits */ 1593*9860Sgdamore@opensolaris.org bmsr = phy_read(ph, MII_STATUS); 1594*9860Sgdamore@opensolaris.org bmsr = phy_read(ph, MII_STATUS); 1595*9860Sgdamore@opensolaris.org if ((bmsr == 0) || (bmsr == 0xffff)) { 1596*9860Sgdamore@opensolaris.org ph->phy_present = B_FALSE; 1597*9860Sgdamore@opensolaris.org return; 1598*9860Sgdamore@opensolaris.org } 1599*9860Sgdamore@opensolaris.org 1600*9860Sgdamore@opensolaris.org if (bmsr & MII_STATUS_EXTSTAT) { 1601*9860Sgdamore@opensolaris.org extsr = phy_read(ph, MII_EXTSTATUS); 1602*9860Sgdamore@opensolaris.org } else { 1603*9860Sgdamore@opensolaris.org extsr = 0; 1604*9860Sgdamore@opensolaris.org } 1605*9860Sgdamore@opensolaris.org 1606*9860Sgdamore@opensolaris.org ph->phy_present = B_TRUE; 1607*9860Sgdamore@opensolaris.org ph->phy_id = ((uint32_t)phy_read(ph, MII_PHYIDH) << 16) | 1608*9860Sgdamore@opensolaris.org phy_read(ph, MII_PHYIDL); 1609*9860Sgdamore@opensolaris.org 1610*9860Sgdamore@opensolaris.org /* setup default handlers */ 1611*9860Sgdamore@opensolaris.org ph->phy_reset = phy_reset; 1612*9860Sgdamore@opensolaris.org ph->phy_start = phy_start; 1613*9860Sgdamore@opensolaris.org ph->phy_stop = phy_stop; 1614*9860Sgdamore@opensolaris.org ph->phy_check = phy_check; 1615*9860Sgdamore@opensolaris.org 1616*9860Sgdamore@opensolaris.org /* 1617*9860Sgdamore@opensolaris.org * We ignore the non-existent 100baseT2 stuff -- no 1618*9860Sgdamore@opensolaris.org * known products for it exist. 1619*9860Sgdamore@opensolaris.org */ 1620*9860Sgdamore@opensolaris.org ph->phy_cap_aneg = !!(bmsr & MII_STATUS_CANAUTONEG); 1621*9860Sgdamore@opensolaris.org ph->phy_cap_100_t4 = !!(bmsr & MII_STATUS_100_BASE_T4); 1622*9860Sgdamore@opensolaris.org ph->phy_cap_100_fdx = !!(bmsr & MII_STATUS_100_BASEX_FD); 1623*9860Sgdamore@opensolaris.org ph->phy_cap_100_hdx = !!(bmsr & MII_STATUS_100_BASEX); 1624*9860Sgdamore@opensolaris.org ph->phy_cap_10_fdx = !!(bmsr & MII_STATUS_10_FD); 1625*9860Sgdamore@opensolaris.org ph->phy_cap_10_hdx = !!(bmsr & MII_STATUS_10); 1626*9860Sgdamore@opensolaris.org ph->phy_cap_1000_fdx = 1627*9860Sgdamore@opensolaris.org !!(extsr & (MII_EXTSTATUS_1000X_FD|MII_EXTSTATUS_1000T_FD)); 1628*9860Sgdamore@opensolaris.org ph->phy_cap_1000_hdx = 1629*9860Sgdamore@opensolaris.org !!(extsr & (MII_EXTSTATUS_1000X | MII_EXTSTATUS_1000T)); 1630*9860Sgdamore@opensolaris.org ph->phy_cap_pause = mh->m_cap_pause; 1631*9860Sgdamore@opensolaris.org ph->phy_cap_asmpause = mh->m_cap_asmpause; 1632*9860Sgdamore@opensolaris.org 1633*9860Sgdamore@opensolaris.org if (bmsr & MII_STATUS_10) { 1634*9860Sgdamore@opensolaris.org ph->phy_cap_10_hdx = B_TRUE; 1635*9860Sgdamore@opensolaris.org ph->phy_type = XCVR_10; 1636*9860Sgdamore@opensolaris.org } 1637*9860Sgdamore@opensolaris.org if (bmsr & MII_STATUS_10_FD) { 1638*9860Sgdamore@opensolaris.org ph->phy_cap_10_fdx = B_TRUE; 1639*9860Sgdamore@opensolaris.org ph->phy_type = XCVR_10; 1640*9860Sgdamore@opensolaris.org } 1641*9860Sgdamore@opensolaris.org if (bmsr & MII_STATUS_100T2) { 1642*9860Sgdamore@opensolaris.org ph->phy_cap_100_hdx = B_TRUE; 1643*9860Sgdamore@opensolaris.org ph->phy_type = XCVR_100T2; 1644*9860Sgdamore@opensolaris.org } 1645*9860Sgdamore@opensolaris.org if (bmsr & MII_STATUS_100T2_FD) { 1646*9860Sgdamore@opensolaris.org ph->phy_cap_100_fdx = B_TRUE; 1647*9860Sgdamore@opensolaris.org ph->phy_type = XCVR_100T2; 1648*9860Sgdamore@opensolaris.org } 1649*9860Sgdamore@opensolaris.org if (bmsr & MII_STATUS_100_BASE_T4) { 1650*9860Sgdamore@opensolaris.org ph->phy_cap_100_hdx = B_TRUE; 1651*9860Sgdamore@opensolaris.org ph->phy_type = XCVR_100T4; 1652*9860Sgdamore@opensolaris.org } 1653*9860Sgdamore@opensolaris.org if (bmsr & MII_STATUS_100_BASEX) { 1654*9860Sgdamore@opensolaris.org ph->phy_cap_100_hdx = B_TRUE; 1655*9860Sgdamore@opensolaris.org ph->phy_type = XCVR_100X; 1656*9860Sgdamore@opensolaris.org } 1657*9860Sgdamore@opensolaris.org if (bmsr & MII_STATUS_100_BASEX_FD) { 1658*9860Sgdamore@opensolaris.org ph->phy_cap_100_fdx = B_TRUE; 1659*9860Sgdamore@opensolaris.org ph->phy_type = XCVR_100X; 1660*9860Sgdamore@opensolaris.org } 1661*9860Sgdamore@opensolaris.org if (extsr & MII_EXTSTATUS_1000X) { 1662*9860Sgdamore@opensolaris.org ph->phy_cap_1000_hdx = B_TRUE; 1663*9860Sgdamore@opensolaris.org ph->phy_type = XCVR_1000X; 1664*9860Sgdamore@opensolaris.org } 1665*9860Sgdamore@opensolaris.org if (extsr & MII_EXTSTATUS_1000X_FD) { 1666*9860Sgdamore@opensolaris.org ph->phy_cap_1000_fdx = B_TRUE; 1667*9860Sgdamore@opensolaris.org ph->phy_type = XCVR_1000X; 1668*9860Sgdamore@opensolaris.org } 1669*9860Sgdamore@opensolaris.org if (extsr & MII_EXTSTATUS_1000T) { 1670*9860Sgdamore@opensolaris.org ph->phy_cap_1000_hdx = B_TRUE; 1671*9860Sgdamore@opensolaris.org ph->phy_type = XCVR_1000T; 1672*9860Sgdamore@opensolaris.org } 1673*9860Sgdamore@opensolaris.org if (extsr & MII_EXTSTATUS_1000T_FD) { 1674*9860Sgdamore@opensolaris.org ph->phy_cap_1000_fdx = B_TRUE; 1675*9860Sgdamore@opensolaris.org ph->phy_type = XCVR_1000T; 1676*9860Sgdamore@opensolaris.org } 1677*9860Sgdamore@opensolaris.org 1678*9860Sgdamore@opensolaris.org for (int j = 0; _phy_probes[j] != NULL; j++) { 1679*9860Sgdamore@opensolaris.org if ((*_phy_probes[j])(ph)) { 1680*9860Sgdamore@opensolaris.org break; 1681*9860Sgdamore@opensolaris.org } 1682*9860Sgdamore@opensolaris.org } 1683*9860Sgdamore@opensolaris.org 1684*9860Sgdamore@opensolaris.org #define INIT_ENABLE(CAP) \ 1685*9860Sgdamore@opensolaris.org ph->phy_en_##CAP = (mh->m_en_##CAP > 0) ? \ 1686*9860Sgdamore@opensolaris.org mh->m_en_##CAP : ph->phy_cap_##CAP 1687*9860Sgdamore@opensolaris.org 1688*9860Sgdamore@opensolaris.org INIT_ENABLE(aneg); 1689*9860Sgdamore@opensolaris.org INIT_ENABLE(1000_fdx); 1690*9860Sgdamore@opensolaris.org INIT_ENABLE(1000_hdx); 1691*9860Sgdamore@opensolaris.org INIT_ENABLE(100_fdx); 1692*9860Sgdamore@opensolaris.org INIT_ENABLE(100_t4); 1693*9860Sgdamore@opensolaris.org INIT_ENABLE(100_hdx); 1694*9860Sgdamore@opensolaris.org INIT_ENABLE(10_fdx); 1695*9860Sgdamore@opensolaris.org INIT_ENABLE(10_hdx); 1696*9860Sgdamore@opensolaris.org 1697*9860Sgdamore@opensolaris.org #undef INIT_ENABLE 1698*9860Sgdamore@opensolaris.org ph->phy_en_flowctrl = mh->m_en_flowctrl; 1699*9860Sgdamore@opensolaris.org switch (ph->phy_en_flowctrl) { 1700*9860Sgdamore@opensolaris.org case LINK_FLOWCTRL_BI: 1701*9860Sgdamore@opensolaris.org case LINK_FLOWCTRL_RX: 1702*9860Sgdamore@opensolaris.org ph->phy_en_pause = B_TRUE; 1703*9860Sgdamore@opensolaris.org ph->phy_en_asmpause = B_TRUE; 1704*9860Sgdamore@opensolaris.org break; 1705*9860Sgdamore@opensolaris.org case LINK_FLOWCTRL_TX: 1706*9860Sgdamore@opensolaris.org ph->phy_en_pause = B_FALSE; 1707*9860Sgdamore@opensolaris.org ph->phy_en_asmpause = B_TRUE; 1708*9860Sgdamore@opensolaris.org break; 1709*9860Sgdamore@opensolaris.org default: 1710*9860Sgdamore@opensolaris.org ph->phy_en_pause = B_FALSE; 1711*9860Sgdamore@opensolaris.org ph->phy_en_asmpause = B_FALSE; 1712*9860Sgdamore@opensolaris.org break; 1713*9860Sgdamore@opensolaris.org } 1714*9860Sgdamore@opensolaris.org } 1715*9860Sgdamore@opensolaris.org 1716*9860Sgdamore@opensolaris.org void 1717*9860Sgdamore@opensolaris.org _mii_probe_task(mii_handle_t mh) 1718*9860Sgdamore@opensolaris.org { 1719*9860Sgdamore@opensolaris.org uint8_t new_addr; 1720*9860Sgdamore@opensolaris.org uint8_t old_addr; 1721*9860Sgdamore@opensolaris.org uint8_t user_addr; 1722*9860Sgdamore@opensolaris.org uint8_t curr_addr; 1723*9860Sgdamore@opensolaris.org phy_handle_t *ph; 1724*9860Sgdamore@opensolaris.org int pri = 0; 1725*9860Sgdamore@opensolaris.org 1726*9860Sgdamore@opensolaris.org user_addr = ddi_prop_get_int(DDI_DEV_T_ANY, mh->m_dip, 0, 1727*9860Sgdamore@opensolaris.org "phy-addr", -1); 1728*9860Sgdamore@opensolaris.org old_addr = mh->m_addr; 1729*9860Sgdamore@opensolaris.org new_addr = 0xff; 1730*9860Sgdamore@opensolaris.org 1731*9860Sgdamore@opensolaris.org /* 1732*9860Sgdamore@opensolaris.org * Apparently, PHY 0 is less likely to be physically 1733*9860Sgdamore@opensolaris.org * connected, and should always be the last one tried. Most 1734*9860Sgdamore@opensolaris.org * single solution NICs use PHY1 for their built-in 1735*9860Sgdamore@opensolaris.org * transceiver. NICs with an external MII will often place 1736*9860Sgdamore@opensolaris.org * the external PHY at address 1, and use address 0 for the 1737*9860Sgdamore@opensolaris.org * internal PHY. 1738*9860Sgdamore@opensolaris.org */ 1739*9860Sgdamore@opensolaris.org 1740*9860Sgdamore@opensolaris.org for (int i = 1; i < 33; i++) { 1741*9860Sgdamore@opensolaris.org 1742*9860Sgdamore@opensolaris.org /* 1743*9860Sgdamore@opensolaris.org * This is tricky: it lets us try 0 last by starting 1744*9860Sgdamore@opensolaris.org * loop at 1 instead of 0. 1745*9860Sgdamore@opensolaris.org */ 1746*9860Sgdamore@opensolaris.org curr_addr = i % 32; 1747*9860Sgdamore@opensolaris.org 1748*9860Sgdamore@opensolaris.org ph = &mh->m_phys[curr_addr]; 1749*9860Sgdamore@opensolaris.org 1750*9860Sgdamore@opensolaris.org bzero(ph, sizeof (*ph)); 1751*9860Sgdamore@opensolaris.org ph->phy_addr = curr_addr; 1752*9860Sgdamore@opensolaris.org ph->phy_mii = mh; 1753*9860Sgdamore@opensolaris.org 1754*9860Sgdamore@opensolaris.org _mii_probe_phy(ph); 1755*9860Sgdamore@opensolaris.org 1756*9860Sgdamore@opensolaris.org if (!ph->phy_present) 1757*9860Sgdamore@opensolaris.org continue; 1758*9860Sgdamore@opensolaris.org 1759*9860Sgdamore@opensolaris.org if (curr_addr == user_addr) { 1760*9860Sgdamore@opensolaris.org /* 1761*9860Sgdamore@opensolaris.org * We always try to honor the user configured phy. 1762*9860Sgdamore@opensolaris.org */ 1763*9860Sgdamore@opensolaris.org new_addr = curr_addr; 1764*9860Sgdamore@opensolaris.org pri = 4; 1765*9860Sgdamore@opensolaris.org 1766*9860Sgdamore@opensolaris.org } 1767*9860Sgdamore@opensolaris.org 1768*9860Sgdamore@opensolaris.org /* two reads to clear latched bits */ 1769*9860Sgdamore@opensolaris.org if ((phy_read(ph, MII_STATUS) & MII_STATUS_LINKUP) && 1770*9860Sgdamore@opensolaris.org (phy_read(ph, MII_STATUS) & MII_STATUS_LINKUP) && 1771*9860Sgdamore@opensolaris.org (pri < 3)) { 1772*9860Sgdamore@opensolaris.org /* 1773*9860Sgdamore@opensolaris.org * Link present is good. We prefer this over 1774*9860Sgdamore@opensolaris.org * a possibly disconnected link. 1775*9860Sgdamore@opensolaris.org */ 1776*9860Sgdamore@opensolaris.org new_addr = curr_addr; 1777*9860Sgdamore@opensolaris.org pri = 3; 1778*9860Sgdamore@opensolaris.org } 1779*9860Sgdamore@opensolaris.org if ((curr_addr == old_addr) && (pri < 2)) { 1780*9860Sgdamore@opensolaris.org /* 1781*9860Sgdamore@opensolaris.org * All else being equal, minimize change. 1782*9860Sgdamore@opensolaris.org */ 1783*9860Sgdamore@opensolaris.org new_addr = curr_addr; 1784*9860Sgdamore@opensolaris.org pri = 2; 1785*9860Sgdamore@opensolaris.org 1786*9860Sgdamore@opensolaris.org } 1787*9860Sgdamore@opensolaris.org if (pri < 1) { 1788*9860Sgdamore@opensolaris.org /* 1789*9860Sgdamore@opensolaris.org * But make sure we at least select a present PHY. 1790*9860Sgdamore@opensolaris.org */ 1791*9860Sgdamore@opensolaris.org new_addr = curr_addr; 1792*9860Sgdamore@opensolaris.org pri = 1; 1793*9860Sgdamore@opensolaris.org } 1794*9860Sgdamore@opensolaris.org } 1795*9860Sgdamore@opensolaris.org 1796*9860Sgdamore@opensolaris.org if (new_addr == 0xff) { 1797*9860Sgdamore@opensolaris.org mh->m_addr = -1; 1798*9860Sgdamore@opensolaris.org mh->m_phy = &mh->m_bogus_phy; 1799*9860Sgdamore@opensolaris.org _mii_error(mh, MII_ENOPHY); 1800*9860Sgdamore@opensolaris.org } else { 1801*9860Sgdamore@opensolaris.org mh->m_addr = new_addr; 1802*9860Sgdamore@opensolaris.org mh->m_phy = &mh->m_phys[new_addr]; 1803*9860Sgdamore@opensolaris.org mh->m_tstate = MII_STATE_RESET; 1804*9860Sgdamore@opensolaris.org cmn_err(CE_CONT, "?%s: Using %s Ethernet PHY at %d: %s %s\n", 1805*9860Sgdamore@opensolaris.org mh->m_name, mii_xcvr_types[mh->m_phy->phy_type], 1806*9860Sgdamore@opensolaris.org mh->m_addr, mh->m_phy->phy_vendor, mh->m_phy->phy_model); 1807*9860Sgdamore@opensolaris.org } 1808*9860Sgdamore@opensolaris.org } 1809*9860Sgdamore@opensolaris.org 1810*9860Sgdamore@opensolaris.org static int 1811*9860Sgdamore@opensolaris.org _mii_reset_task(mii_handle_t mh) 1812*9860Sgdamore@opensolaris.org { 1813*9860Sgdamore@opensolaris.org phy_handle_t *ph; 1814*9860Sgdamore@opensolaris.org 1815*9860Sgdamore@opensolaris.org ASSERT(mutex_owned(&mh->m_lock)); 1816*9860Sgdamore@opensolaris.org 1817*9860Sgdamore@opensolaris.org /* 1818*9860Sgdamore@opensolaris.org * Reset logic. We want to isolate all the other 1819*9860Sgdamore@opensolaris.org * phys that are not in use. 1820*9860Sgdamore@opensolaris.org */ 1821*9860Sgdamore@opensolaris.org for (int i = 0; i < 32; i++) { 1822*9860Sgdamore@opensolaris.org ph = &mh->m_phys[i]; 1823*9860Sgdamore@opensolaris.org 1824*9860Sgdamore@opensolaris.org if (!ph->phy_present) 1825*9860Sgdamore@opensolaris.org continue; 1826*9860Sgdamore@opensolaris.org 1827*9860Sgdamore@opensolaris.org /* don't touch our own phy, yet */ 1828*9860Sgdamore@opensolaris.org if (ph == mh->m_phy) 1829*9860Sgdamore@opensolaris.org continue; 1830*9860Sgdamore@opensolaris.org 1831*9860Sgdamore@opensolaris.org ph->phy_stop(ph); 1832*9860Sgdamore@opensolaris.org } 1833*9860Sgdamore@opensolaris.org 1834*9860Sgdamore@opensolaris.org ph = mh->m_phy; 1835*9860Sgdamore@opensolaris.org 1836*9860Sgdamore@opensolaris.org ASSERT(ph->phy_present); 1837*9860Sgdamore@opensolaris.org 1838*9860Sgdamore@opensolaris.org /* If we're resetting the PHY, then for sure we want to notify */ 1839*9860Sgdamore@opensolaris.org mh->m_notify = B_TRUE; 1840*9860Sgdamore@opensolaris.org 1841*9860Sgdamore@opensolaris.org if (ph->phy_reset(ph) != DDI_SUCCESS) { 1842*9860Sgdamore@opensolaris.org _mii_error(mh, MII_ERESET); 1843*9860Sgdamore@opensolaris.org return (DDI_FAILURE); 1844*9860Sgdamore@opensolaris.org } 1845*9860Sgdamore@opensolaris.org 1846*9860Sgdamore@opensolaris.org mh->m_tstate = MII_STATE_START; 1847*9860Sgdamore@opensolaris.org return (DDI_SUCCESS); 1848*9860Sgdamore@opensolaris.org } 1849*9860Sgdamore@opensolaris.org 1850*9860Sgdamore@opensolaris.org static void 1851*9860Sgdamore@opensolaris.org _mii_start_task(mii_handle_t mh) 1852*9860Sgdamore@opensolaris.org { 1853*9860Sgdamore@opensolaris.org phy_handle_t *ph; 1854*9860Sgdamore@opensolaris.org 1855*9860Sgdamore@opensolaris.org ph = mh->m_phy; 1856*9860Sgdamore@opensolaris.org 1857*9860Sgdamore@opensolaris.org ASSERT(mutex_owned(&mh->m_lock)); 1858*9860Sgdamore@opensolaris.org ASSERT(ph->phy_present); 1859*9860Sgdamore@opensolaris.org 1860*9860Sgdamore@opensolaris.org if (phy_start(ph) != DDI_SUCCESS) { 1861*9860Sgdamore@opensolaris.org _mii_error(mh, MII_ESTART); 1862*9860Sgdamore@opensolaris.org mh->m_tstate = MII_STATE_RESET; 1863*9860Sgdamore@opensolaris.org return; 1864*9860Sgdamore@opensolaris.org } 1865*9860Sgdamore@opensolaris.org /* clear the error state since we got a good startup! */ 1866*9860Sgdamore@opensolaris.org mh->m_error = MII_EOK; 1867*9860Sgdamore@opensolaris.org mh->m_tstate = MII_STATE_RUN; 1868*9860Sgdamore@opensolaris.org } 1869*9860Sgdamore@opensolaris.org 1870*9860Sgdamore@opensolaris.org static int 1871*9860Sgdamore@opensolaris.org _mii_check_task(mii_handle_t mh) 1872*9860Sgdamore@opensolaris.org { 1873*9860Sgdamore@opensolaris.org link_state_t olink; 1874*9860Sgdamore@opensolaris.org int ospeed; 1875*9860Sgdamore@opensolaris.org link_duplex_t oduplex; 1876*9860Sgdamore@opensolaris.org link_flowctrl_t ofctrl; 1877*9860Sgdamore@opensolaris.org phy_handle_t *ph; 1878*9860Sgdamore@opensolaris.org 1879*9860Sgdamore@opensolaris.org ph = mh->m_phy; 1880*9860Sgdamore@opensolaris.org 1881*9860Sgdamore@opensolaris.org olink = mh->m_link; 1882*9860Sgdamore@opensolaris.org ospeed = ph->phy_speed; 1883*9860Sgdamore@opensolaris.org oduplex = ph->phy_duplex; 1884*9860Sgdamore@opensolaris.org ofctrl = ph->phy_flowctrl; 1885*9860Sgdamore@opensolaris.org 1886*9860Sgdamore@opensolaris.org ASSERT(ph->phy_present); 1887*9860Sgdamore@opensolaris.org 1888*9860Sgdamore@opensolaris.org if (ph->phy_check(ph) == DDI_FAILURE) { 1889*9860Sgdamore@opensolaris.org _mii_error(mh, MII_ECHECK); 1890*9860Sgdamore@opensolaris.org mh->m_link = LINK_STATE_UNKNOWN; 1891*9860Sgdamore@opensolaris.org mh->m_tstate = MII_STATE_PROBE; 1892*9860Sgdamore@opensolaris.org mh->m_notify = B_TRUE; 1893*9860Sgdamore@opensolaris.org return (DDI_FAILURE); 1894*9860Sgdamore@opensolaris.org } 1895*9860Sgdamore@opensolaris.org 1896*9860Sgdamore@opensolaris.org mh->m_link = ph->phy_link; 1897*9860Sgdamore@opensolaris.org 1898*9860Sgdamore@opensolaris.org /* if anything changed, notify! */ 1899*9860Sgdamore@opensolaris.org if ((mh->m_link != olink) || 1900*9860Sgdamore@opensolaris.org (ph->phy_speed != ospeed) || 1901*9860Sgdamore@opensolaris.org (ph->phy_duplex != oduplex) || 1902*9860Sgdamore@opensolaris.org (ph->phy_flowctrl != ofctrl)) { 1903*9860Sgdamore@opensolaris.org mh->m_notify = B_TRUE; 1904*9860Sgdamore@opensolaris.org } 1905*9860Sgdamore@opensolaris.org 1906*9860Sgdamore@opensolaris.org return (DDI_SUCCESS); 1907*9860Sgdamore@opensolaris.org } 1908*9860Sgdamore@opensolaris.org 1909*9860Sgdamore@opensolaris.org static void 1910*9860Sgdamore@opensolaris.org _mii_task(void *_mh) 1911*9860Sgdamore@opensolaris.org { 1912*9860Sgdamore@opensolaris.org mii_handle_t mh = _mh; 1913*9860Sgdamore@opensolaris.org phy_handle_t *ph; 1914*9860Sgdamore@opensolaris.org clock_t wait; 1915*9860Sgdamore@opensolaris.org clock_t downtime; 1916*9860Sgdamore@opensolaris.org 1917*9860Sgdamore@opensolaris.org mutex_enter(&mh->m_lock); 1918*9860Sgdamore@opensolaris.org 1919*9860Sgdamore@opensolaris.org for (;;) { 1920*9860Sgdamore@opensolaris.org 1921*9860Sgdamore@opensolaris.org /* If detaching, exit the thread. */ 1922*9860Sgdamore@opensolaris.org if (!mh->m_started) { 1923*9860Sgdamore@opensolaris.org break; 1924*9860Sgdamore@opensolaris.org } 1925*9860Sgdamore@opensolaris.org 1926*9860Sgdamore@opensolaris.org ph = mh->m_phy; 1927*9860Sgdamore@opensolaris.org 1928*9860Sgdamore@opensolaris.org /* 1929*9860Sgdamore@opensolaris.org * If we're suspended or otherwise not supposed to be 1930*9860Sgdamore@opensolaris.org * monitoring the link, just go back to sleep. 1931*9860Sgdamore@opensolaris.org * 1932*9860Sgdamore@opensolaris.org * Theoretically we could power down the PHY, but we 1933*9860Sgdamore@opensolaris.org * don't bother. (The link might be used for 1934*9860Sgdamore@opensolaris.org * wake-on-lan!) Another option would be to reduce 1935*9860Sgdamore@opensolaris.org * power on the PHY if both it and the link partner 1936*9860Sgdamore@opensolaris.org * support 10 Mbps mode. 1937*9860Sgdamore@opensolaris.org */ 1938*9860Sgdamore@opensolaris.org if (mh->m_suspending) { 1939*9860Sgdamore@opensolaris.org mh->m_suspended = B_TRUE; 1940*9860Sgdamore@opensolaris.org cv_broadcast(&mh->m_cv); 1941*9860Sgdamore@opensolaris.org } 1942*9860Sgdamore@opensolaris.org if (mh->m_suspended) { 1943*9860Sgdamore@opensolaris.org mh->m_suspending = B_FALSE; 1944*9860Sgdamore@opensolaris.org cv_wait(&mh->m_cv, &mh->m_lock); 1945*9860Sgdamore@opensolaris.org continue; 1946*9860Sgdamore@opensolaris.org } 1947*9860Sgdamore@opensolaris.org 1948*9860Sgdamore@opensolaris.org switch (mh->m_tstate) { 1949*9860Sgdamore@opensolaris.org case MII_STATE_PROBE: 1950*9860Sgdamore@opensolaris.org _mii_probe_task(mh); 1951*9860Sgdamore@opensolaris.org ph = mh->m_phy; 1952*9860Sgdamore@opensolaris.org if (!ph->phy_present) { 1953*9860Sgdamore@opensolaris.org /* 1954*9860Sgdamore@opensolaris.org * If no PHY is found, wait a bit before 1955*9860Sgdamore@opensolaris.org * trying the probe again. 10 seconds ought 1956*9860Sgdamore@opensolaris.org * to be enough. 1957*9860Sgdamore@opensolaris.org */ 1958*9860Sgdamore@opensolaris.org wait = 10 * MII_SECOND; 1959*9860Sgdamore@opensolaris.org } else { 1960*9860Sgdamore@opensolaris.org wait = 0; 1961*9860Sgdamore@opensolaris.org } 1962*9860Sgdamore@opensolaris.org break; 1963*9860Sgdamore@opensolaris.org 1964*9860Sgdamore@opensolaris.org case MII_STATE_RESET: 1965*9860Sgdamore@opensolaris.org if (_mii_reset_task(mh) == DDI_SUCCESS) { 1966*9860Sgdamore@opensolaris.org ASSERT(mh->m_tstate == MII_STATE_START); 1967*9860Sgdamore@opensolaris.org 1968*9860Sgdamore@opensolaris.org /* 1969*9860Sgdamore@opensolaris.org * We have to go back to the top of 1970*9860Sgdamore@opensolaris.org * the routine to check for changed 1971*9860Sgdamore@opensolaris.org * conditions while we drop the lock 1972*9860Sgdamore@opensolaris.org * to call into the mac layer. 1973*9860Sgdamore@opensolaris.org */ 1974*9860Sgdamore@opensolaris.org if (mh->m_ops.mii_reset != NULL) { 1975*9860Sgdamore@opensolaris.org mutex_exit(&mh->m_lock); 1976*9860Sgdamore@opensolaris.org mh->m_ops.mii_reset(mh->m_private); 1977*9860Sgdamore@opensolaris.org mutex_enter(&mh->m_lock); 1978*9860Sgdamore@opensolaris.org continue; 1979*9860Sgdamore@opensolaris.org } 1980*9860Sgdamore@opensolaris.org wait = 0; 1981*9860Sgdamore@opensolaris.org } else { 1982*9860Sgdamore@opensolaris.org /* 1983*9860Sgdamore@opensolaris.org * If an error occurred, wait a bit and 1984*9860Sgdamore@opensolaris.org * try again later. 1985*9860Sgdamore@opensolaris.org */ 1986*9860Sgdamore@opensolaris.org wait = 10 * MII_SECOND; 1987*9860Sgdamore@opensolaris.org } 1988*9860Sgdamore@opensolaris.org break; 1989*9860Sgdamore@opensolaris.org 1990*9860Sgdamore@opensolaris.org case MII_STATE_START: 1991*9860Sgdamore@opensolaris.org /* 1992*9860Sgdamore@opensolaris.org * If an error occurs, we're going to go back to 1993*9860Sgdamore@opensolaris.org * probe or reset state. Otherwise we go to run 1994*9860Sgdamore@opensolaris.org * state. In all cases we want to wait 1 second 1995*9860Sgdamore@opensolaris.org * before doing anything else - either for link to 1996*9860Sgdamore@opensolaris.org * settle, or to give other code a chance to run 1997*9860Sgdamore@opensolaris.org * while we reset. 1998*9860Sgdamore@opensolaris.org */ 1999*9860Sgdamore@opensolaris.org _mii_start_task(mh); 2000*9860Sgdamore@opensolaris.org /* reset watchdog to latest */ 2001*9860Sgdamore@opensolaris.org downtime = ddi_get_lbolt(); 2002*9860Sgdamore@opensolaris.org wait = MII_SECOND; 2003*9860Sgdamore@opensolaris.org break; 2004*9860Sgdamore@opensolaris.org 2005*9860Sgdamore@opensolaris.org case MII_STATE_RUN: 2006*9860Sgdamore@opensolaris.org default: 2007*9860Sgdamore@opensolaris.org if (_mii_check_task(mh) == DDI_FAILURE) { 2008*9860Sgdamore@opensolaris.org /* 2009*9860Sgdamore@opensolaris.org * On error (PHY removed?), wait a 2010*9860Sgdamore@opensolaris.org * short bit before reprobing or 2011*9860Sgdamore@opensolaris.org * resetting. 2012*9860Sgdamore@opensolaris.org */ 2013*9860Sgdamore@opensolaris.org wait = MII_SECOND; 2014*9860Sgdamore@opensolaris.org 2015*9860Sgdamore@opensolaris.org } else if (mh->m_link == LINK_STATE_UP) { 2016*9860Sgdamore@opensolaris.org /* got goood link, so reset the watchdog */ 2017*9860Sgdamore@opensolaris.org downtime = ddi_get_lbolt(); 2018*9860Sgdamore@opensolaris.org /* rescan again in a second */ 2019*9860Sgdamore@opensolaris.org wait = MII_SECOND; 2020*9860Sgdamore@opensolaris.org 2021*9860Sgdamore@opensolaris.org } else if ((ddi_get_lbolt() - downtime) > 2022*9860Sgdamore@opensolaris.org (drv_usectohz(MII_SECOND * 10))) { 2023*9860Sgdamore@opensolaris.org 2024*9860Sgdamore@opensolaris.org /* 2025*9860Sgdamore@opensolaris.org * If we were down for 10 seconds, 2026*9860Sgdamore@opensolaris.org * hard reset the PHY. 2027*9860Sgdamore@opensolaris.org */ 2028*9860Sgdamore@opensolaris.org mh->m_tstate = MII_STATE_RESET; 2029*9860Sgdamore@opensolaris.org wait = 0; 2030*9860Sgdamore@opensolaris.org 2031*9860Sgdamore@opensolaris.org } else { 2032*9860Sgdamore@opensolaris.org /* 2033*9860Sgdamore@opensolaris.org * Otherwise, if we are still down, 2034*9860Sgdamore@opensolaris.org * rescan the link much more 2035*9860Sgdamore@opensolaris.org * frequently. We might be trying to 2036*9860Sgdamore@opensolaris.org * autonegotiate. 2037*9860Sgdamore@opensolaris.org */ 2038*9860Sgdamore@opensolaris.org wait = MII_SECOND / 4; 2039*9860Sgdamore@opensolaris.org } 2040*9860Sgdamore@opensolaris.org break; 2041*9860Sgdamore@opensolaris.org } 2042*9860Sgdamore@opensolaris.org 2043*9860Sgdamore@opensolaris.org if (mh->m_notify) { 2044*9860Sgdamore@opensolaris.org mh->m_notify = B_FALSE; 2045*9860Sgdamore@opensolaris.org 2046*9860Sgdamore@opensolaris.org if (mh->m_ops.mii_notify != NULL) { 2047*9860Sgdamore@opensolaris.org link_state_t state = mh->m_link; 2048*9860Sgdamore@opensolaris.org mutex_exit(&mh->m_lock); 2049*9860Sgdamore@opensolaris.org mh->m_ops.mii_notify(mh->m_private, state); 2050*9860Sgdamore@opensolaris.org mutex_enter(&mh->m_lock); 2051*9860Sgdamore@opensolaris.org continue; 2052*9860Sgdamore@opensolaris.org } 2053*9860Sgdamore@opensolaris.org } 2054*9860Sgdamore@opensolaris.org 2055*9860Sgdamore@opensolaris.org if (wait) 2056*9860Sgdamore@opensolaris.org (void) cv_timedwait(&mh->m_cv, &mh->m_lock, 2057*9860Sgdamore@opensolaris.org ddi_get_lbolt() + drv_usectohz(wait)); 2058*9860Sgdamore@opensolaris.org } 2059*9860Sgdamore@opensolaris.org 2060*9860Sgdamore@opensolaris.org mutex_exit(&mh->m_lock); 2061*9860Sgdamore@opensolaris.org } 2062