19860Sgdamore@opensolaris.org /* 29860Sgdamore@opensolaris.org * CDDL HEADER START 39860Sgdamore@opensolaris.org * 49860Sgdamore@opensolaris.org * The contents of this file are subject to the terms of the 59860Sgdamore@opensolaris.org * Common Development and Distribution License (the "License"). 69860Sgdamore@opensolaris.org * You may not use this file except in compliance with the License. 79860Sgdamore@opensolaris.org * 89860Sgdamore@opensolaris.org * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 99860Sgdamore@opensolaris.org * or http://www.opensolaris.org/os/licensing. 109860Sgdamore@opensolaris.org * See the License for the specific language governing permissions 119860Sgdamore@opensolaris.org * and limitations under the License. 129860Sgdamore@opensolaris.org * 139860Sgdamore@opensolaris.org * When distributing Covered Code, include this CDDL HEADER in each 149860Sgdamore@opensolaris.org * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 159860Sgdamore@opensolaris.org * If applicable, add the following below this CDDL HEADER, with the 169860Sgdamore@opensolaris.org * fields enclosed by brackets "[]" replaced with your own identifying 179860Sgdamore@opensolaris.org * information: Portions Copyright [yyyy] [name of copyright owner] 189860Sgdamore@opensolaris.org * 199860Sgdamore@opensolaris.org * CDDL HEADER END 209860Sgdamore@opensolaris.org */ 219860Sgdamore@opensolaris.org /* 2211414Sgdamore@opensolaris.org * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 239860Sgdamore@opensolaris.org * Use is subject to license terms. 249860Sgdamore@opensolaris.org */ 259860Sgdamore@opensolaris.org 269860Sgdamore@opensolaris.org /* 279860Sgdamore@opensolaris.org * mii - MII/PHY support for MAC drivers 289860Sgdamore@opensolaris.org * 299860Sgdamore@opensolaris.org * Utility module to provide a consistent interface to a MAC driver accross 309860Sgdamore@opensolaris.org * different implementations of PHY devices 319860Sgdamore@opensolaris.org */ 329860Sgdamore@opensolaris.org 339860Sgdamore@opensolaris.org #include <sys/types.h> 349860Sgdamore@opensolaris.org #include <sys/debug.h> 359860Sgdamore@opensolaris.org #include <sys/errno.h> 369860Sgdamore@opensolaris.org #include <sys/param.h> 379860Sgdamore@opensolaris.org #include <sys/kmem.h> 389860Sgdamore@opensolaris.org #include <sys/conf.h> 399860Sgdamore@opensolaris.org #include <sys/ddi.h> 409860Sgdamore@opensolaris.org #include <sys/sunddi.h> 419860Sgdamore@opensolaris.org #include <sys/modctl.h> 429860Sgdamore@opensolaris.org #include <sys/cmn_err.h> 439860Sgdamore@opensolaris.org #include <sys/policy.h> 449860Sgdamore@opensolaris.org #include <sys/note.h> 459860Sgdamore@opensolaris.org #include <sys/strsun.h> 469860Sgdamore@opensolaris.org #include <sys/miiregs.h> 479860Sgdamore@opensolaris.org #include <sys/mac_provider.h> 489860Sgdamore@opensolaris.org #include <sys/mac_ether.h> 499860Sgdamore@opensolaris.org #include <sys/mii.h> 509860Sgdamore@opensolaris.org #include "miipriv.h" 519860Sgdamore@opensolaris.org 529860Sgdamore@opensolaris.org #define MII_SECOND 1000000 539860Sgdamore@opensolaris.org 549860Sgdamore@opensolaris.org /* indices into error array */ 559860Sgdamore@opensolaris.org enum { 569860Sgdamore@opensolaris.org MII_EOK = 0, 579860Sgdamore@opensolaris.org MII_ERESET, 589860Sgdamore@opensolaris.org MII_ESTART, 599860Sgdamore@opensolaris.org MII_ENOPHY, 609860Sgdamore@opensolaris.org MII_ECHECK, 6110359SGarrett.Damore@Sun.COM MII_ELOOP, 629860Sgdamore@opensolaris.org }; 639860Sgdamore@opensolaris.org 649860Sgdamore@opensolaris.org static const char *mii_errors[] = { 659860Sgdamore@opensolaris.org "", 669860Sgdamore@opensolaris.org "Failure resetting PHY.", 679860Sgdamore@opensolaris.org "Failure starting PHY.", 689860Sgdamore@opensolaris.org "No Ethernet PHY found.", 6910359SGarrett.Damore@Sun.COM "Failure reading PHY (removed?)", 7010359SGarrett.Damore@Sun.COM "Failure setting loopback." 719860Sgdamore@opensolaris.org }; 729860Sgdamore@opensolaris.org 739860Sgdamore@opensolaris.org /* Indexed by XCVR_ type */ 749860Sgdamore@opensolaris.org static const const char *mii_xcvr_types[] = { 759860Sgdamore@opensolaris.org "Undefined", 769860Sgdamore@opensolaris.org "Unknown", 779860Sgdamore@opensolaris.org "10 Mbps", 789860Sgdamore@opensolaris.org "100BASE-T4", 799860Sgdamore@opensolaris.org "100BASE-X", 809860Sgdamore@opensolaris.org "100BASE-T2", 819860Sgdamore@opensolaris.org "1000BASE-X", 829860Sgdamore@opensolaris.org "1000BASE-T" 839860Sgdamore@opensolaris.org }; 849860Sgdamore@opensolaris.org 859860Sgdamore@opensolaris.org /* state machine */ 869860Sgdamore@opensolaris.org typedef enum { 879860Sgdamore@opensolaris.org MII_STATE_PROBE = 0, 889860Sgdamore@opensolaris.org MII_STATE_RESET, 899860Sgdamore@opensolaris.org MII_STATE_START, 909860Sgdamore@opensolaris.org MII_STATE_RUN, 9110359SGarrett.Damore@Sun.COM MII_STATE_LOOPBACK, 929860Sgdamore@opensolaris.org } mii_tstate_t; 939860Sgdamore@opensolaris.org 949860Sgdamore@opensolaris.org struct mii_handle { 959860Sgdamore@opensolaris.org dev_info_t *m_dip; 969860Sgdamore@opensolaris.org void *m_private; 979860Sgdamore@opensolaris.org mii_ops_t m_ops; 989860Sgdamore@opensolaris.org 999860Sgdamore@opensolaris.org kt_did_t m_tq_id; 1009860Sgdamore@opensolaris.org kmutex_t m_lock; 1019860Sgdamore@opensolaris.org kcondvar_t m_cv; 1029860Sgdamore@opensolaris.org ddi_taskq_t *m_tq; 1039860Sgdamore@opensolaris.org int m_flags; 1049860Sgdamore@opensolaris.org 1059860Sgdamore@opensolaris.org boolean_t m_started; 1069860Sgdamore@opensolaris.org boolean_t m_suspending; 1079860Sgdamore@opensolaris.org boolean_t m_suspended; 1089860Sgdamore@opensolaris.org int m_error; 1099860Sgdamore@opensolaris.org mii_tstate_t m_tstate; 1109860Sgdamore@opensolaris.org 1119860Sgdamore@opensolaris.org #define MII_FLAG_EXIT 0x1 /* exit the thread */ 1129860Sgdamore@opensolaris.org #define MII_FLAG_STOP 0x2 /* shutdown MII monitoring */ 1139860Sgdamore@opensolaris.org #define MII_FLAG_RESET 0x4 /* reset the MII */ 1149860Sgdamore@opensolaris.org #define MII_FLAG_PROBE 0x8 /* probe for PHYs */ 1159860Sgdamore@opensolaris.org #define MII_FLAG_NOTIFY 0x10 /* notify about a change */ 1169860Sgdamore@opensolaris.org #define MII_FLAG_SUSPEND 0x20 /* monitoring suspended */ 1179860Sgdamore@opensolaris.org #define MII_FLAG_MACRESET 0x40 /* send reset to MAC */ 1189860Sgdamore@opensolaris.org #define MII_FLAG_PHYSTART 0x80 /* start up the PHY */ 1199860Sgdamore@opensolaris.org 1209860Sgdamore@opensolaris.org /* device name for printing, e.g. "hme0" */ 1219860Sgdamore@opensolaris.org char m_name[MODMAXNAMELEN + 16]; 1229860Sgdamore@opensolaris.org 1239860Sgdamore@opensolaris.org int m_addr; 1249860Sgdamore@opensolaris.org phy_handle_t m_phys[32]; 1259860Sgdamore@opensolaris.org phy_handle_t m_bogus_phy; 1269860Sgdamore@opensolaris.org phy_handle_t *m_phy; 1279860Sgdamore@opensolaris.org 1289860Sgdamore@opensolaris.org link_state_t m_link; 1299860Sgdamore@opensolaris.org 1309860Sgdamore@opensolaris.org /* these start out undefined, but get values due to mac_prop_set */ 1319860Sgdamore@opensolaris.org int m_en_aneg; 1329860Sgdamore@opensolaris.org int m_en_10_hdx; 1339860Sgdamore@opensolaris.org int m_en_10_fdx; 1349860Sgdamore@opensolaris.org int m_en_100_t4; 1359860Sgdamore@opensolaris.org int m_en_100_hdx; 1369860Sgdamore@opensolaris.org int m_en_100_fdx; 1379860Sgdamore@opensolaris.org int m_en_1000_hdx; 1389860Sgdamore@opensolaris.org int m_en_1000_fdx; 1399860Sgdamore@opensolaris.org int m_en_flowctrl; 1409860Sgdamore@opensolaris.org 1419860Sgdamore@opensolaris.org boolean_t m_cap_pause; 1429860Sgdamore@opensolaris.org boolean_t m_cap_asmpause; 1439860Sgdamore@opensolaris.org }; 1449860Sgdamore@opensolaris.org 1459860Sgdamore@opensolaris.org 1469860Sgdamore@opensolaris.org static void _mii_task(void *); 14710359SGarrett.Damore@Sun.COM static void _mii_probe_phy(phy_handle_t *); 14810359SGarrett.Damore@Sun.COM static void _mii_probe(mii_handle_t); 14910359SGarrett.Damore@Sun.COM static int _mii_reset(mii_handle_t); 15010359SGarrett.Damore@Sun.COM static int _mii_loopback(mii_handle_t); 15110359SGarrett.Damore@Sun.COM static void _mii_notify(mii_handle_t); 15210359SGarrett.Damore@Sun.COM static int _mii_check(mii_handle_t); 15310359SGarrett.Damore@Sun.COM static int _mii_start(mii_handle_t); 1549860Sgdamore@opensolaris.org 1559860Sgdamore@opensolaris.org /* 1569860Sgdamore@opensolaris.org * Loadable module structures/entrypoints 1579860Sgdamore@opensolaris.org */ 1589860Sgdamore@opensolaris.org 1599860Sgdamore@opensolaris.org extern struct mod_ops mod_misc_ops; 1609860Sgdamore@opensolaris.org 1619860Sgdamore@opensolaris.org static struct modlmisc modlmisc = { 1629860Sgdamore@opensolaris.org &mod_miscops, 1639860Sgdamore@opensolaris.org "802.3 MII support", 1649860Sgdamore@opensolaris.org }; 1659860Sgdamore@opensolaris.org 1669860Sgdamore@opensolaris.org static struct modlinkage modlinkage = { 1679860Sgdamore@opensolaris.org MODREV_1, &modlmisc, NULL 1689860Sgdamore@opensolaris.org }; 1699860Sgdamore@opensolaris.org 1709860Sgdamore@opensolaris.org int 1719860Sgdamore@opensolaris.org _init(void) 1729860Sgdamore@opensolaris.org { 1739860Sgdamore@opensolaris.org return (mod_install(&modlinkage)); 1749860Sgdamore@opensolaris.org } 1759860Sgdamore@opensolaris.org 1769860Sgdamore@opensolaris.org int 1779860Sgdamore@opensolaris.org _fini(void) 1789860Sgdamore@opensolaris.org { 1799860Sgdamore@opensolaris.org return (mod_remove(&modlinkage)); 1809860Sgdamore@opensolaris.org } 1819860Sgdamore@opensolaris.org 1829860Sgdamore@opensolaris.org int 1839860Sgdamore@opensolaris.org _info(struct modinfo *modinfop) 1849860Sgdamore@opensolaris.org { 1859860Sgdamore@opensolaris.org return (mod_info(&modlinkage, modinfop)); 1869860Sgdamore@opensolaris.org } 1879860Sgdamore@opensolaris.org 1889860Sgdamore@opensolaris.org void 1899860Sgdamore@opensolaris.org _mii_error(mii_handle_t mh, int errno) 1909860Sgdamore@opensolaris.org { 1919860Sgdamore@opensolaris.org /* 1929860Sgdamore@opensolaris.org * This dumps an error message, but it avoids filling the log with 1939860Sgdamore@opensolaris.org * repeated error messages. 1949860Sgdamore@opensolaris.org */ 1959860Sgdamore@opensolaris.org if (mh->m_error != errno) { 1969860Sgdamore@opensolaris.org cmn_err(CE_WARN, "%s: %s", mh->m_name, mii_errors[errno]); 1979860Sgdamore@opensolaris.org mh->m_error = errno; 1989860Sgdamore@opensolaris.org } 1999860Sgdamore@opensolaris.org } 2009860Sgdamore@opensolaris.org 2019860Sgdamore@opensolaris.org /* 2029860Sgdamore@opensolaris.org * Known list of specific PHY probes. 2039860Sgdamore@opensolaris.org */ 2049860Sgdamore@opensolaris.org typedef boolean_t (*phy_probe_t)(phy_handle_t *); 2059860Sgdamore@opensolaris.org phy_probe_t _phy_probes[] = { 2069860Sgdamore@opensolaris.org phy_natsemi_probe, 2079860Sgdamore@opensolaris.org phy_intel_probe, 2089860Sgdamore@opensolaris.org phy_qualsemi_probe, 2099860Sgdamore@opensolaris.org phy_cicada_probe, 21010359SGarrett.Damore@Sun.COM phy_marvell_probe, 2119860Sgdamore@opensolaris.org phy_other_probe, 2129860Sgdamore@opensolaris.org NULL 2139860Sgdamore@opensolaris.org }; 2149860Sgdamore@opensolaris.org 2159860Sgdamore@opensolaris.org /* 2169860Sgdamore@opensolaris.org * MII Interface functions 2179860Sgdamore@opensolaris.org */ 2189860Sgdamore@opensolaris.org 2199860Sgdamore@opensolaris.org mii_handle_t 2209860Sgdamore@opensolaris.org mii_alloc_instance(void *private, dev_info_t *dip, int inst, mii_ops_t *ops) 2219860Sgdamore@opensolaris.org { 2229860Sgdamore@opensolaris.org mii_handle_t mh; 2239860Sgdamore@opensolaris.org char tqname[16]; 2249860Sgdamore@opensolaris.org 2259860Sgdamore@opensolaris.org if (ops->mii_version != MII_OPS_VERSION) { 2269860Sgdamore@opensolaris.org cmn_err(CE_WARN, "%s: incompatible MII version (%d)", 2279860Sgdamore@opensolaris.org ddi_driver_name(dip), ops->mii_version); 2289860Sgdamore@opensolaris.org return (NULL); 2299860Sgdamore@opensolaris.org } 2309860Sgdamore@opensolaris.org mh = kmem_zalloc(sizeof (*mh), KM_SLEEP); 2319860Sgdamore@opensolaris.org 2329860Sgdamore@opensolaris.org (void) snprintf(mh->m_name, sizeof (mh->m_name), "%s%d", 2339860Sgdamore@opensolaris.org ddi_driver_name(dip), inst); 2349860Sgdamore@opensolaris.org 2359860Sgdamore@opensolaris.org /* DDI will prepend the driver name */ 2369860Sgdamore@opensolaris.org (void) snprintf(tqname, sizeof (tqname), "mii%d", inst); 2379860Sgdamore@opensolaris.org 2389860Sgdamore@opensolaris.org mh->m_dip = dip; 2399860Sgdamore@opensolaris.org mh->m_ops = *ops; 2409860Sgdamore@opensolaris.org mh->m_private = private; 2419860Sgdamore@opensolaris.org mh->m_suspended = B_FALSE; 2429860Sgdamore@opensolaris.org mh->m_started = B_FALSE; 2439860Sgdamore@opensolaris.org mh->m_tstate = MII_STATE_PROBE; 24410479SGarrett.Damore@Sun.COM mh->m_link = LINK_STATE_UNKNOWN; 2459860Sgdamore@opensolaris.org mh->m_error = MII_EOK; 24610359SGarrett.Damore@Sun.COM mh->m_addr = -1; 2479860Sgdamore@opensolaris.org mutex_init(&mh->m_lock, NULL, MUTEX_DRIVER, NULL); 2489860Sgdamore@opensolaris.org cv_init(&mh->m_cv, NULL, CV_DRIVER, NULL); 2499860Sgdamore@opensolaris.org 2509860Sgdamore@opensolaris.org mh->m_tq = ddi_taskq_create(dip, tqname, 1, TASKQ_DEFAULTPRI, 0); 2519860Sgdamore@opensolaris.org if (mh->m_tq == NULL) { 2529860Sgdamore@opensolaris.org cmn_err(CE_WARN, "%s: unable to create MII monitoring task", 2539860Sgdamore@opensolaris.org ddi_driver_name(dip)); 2549860Sgdamore@opensolaris.org cv_destroy(&mh->m_cv); 2559860Sgdamore@opensolaris.org mutex_destroy(&mh->m_lock); 2569860Sgdamore@opensolaris.org kmem_free(mh, sizeof (*mh)); 2579860Sgdamore@opensolaris.org return (NULL); 2589860Sgdamore@opensolaris.org } 2599860Sgdamore@opensolaris.org 2609860Sgdamore@opensolaris.org /* 2619860Sgdamore@opensolaris.org * Initialize user prefs by loading properties. Ultimately, 2629860Sgdamore@opensolaris.org * Brussels interfaces would be superior here. 2639860Sgdamore@opensolaris.org */ 2649860Sgdamore@opensolaris.org #define GETPROP(name) ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0, name, -1) 2659860Sgdamore@opensolaris.org mh->m_en_aneg = GETPROP("adv_autoneg_cap"); 2669860Sgdamore@opensolaris.org mh->m_en_10_hdx = GETPROP("adv_10hdx_cap"); 2679860Sgdamore@opensolaris.org mh->m_en_10_fdx = GETPROP("adv_10fdx_cap"); 2689860Sgdamore@opensolaris.org mh->m_en_100_hdx = GETPROP("adv_100hdx_cap"); 2699860Sgdamore@opensolaris.org mh->m_en_100_fdx = GETPROP("adv_100fdx_cap"); 2709860Sgdamore@opensolaris.org mh->m_en_100_t4 = GETPROP("adv_100T4_cap"); 2719860Sgdamore@opensolaris.org mh->m_en_1000_hdx = GETPROP("adv_1000hdx_cap"); 2729860Sgdamore@opensolaris.org mh->m_en_1000_fdx = GETPROP("adv_1000fdx_cap"); 2739860Sgdamore@opensolaris.org 2749860Sgdamore@opensolaris.org mh->m_cap_pause = B_FALSE; 2759860Sgdamore@opensolaris.org mh->m_cap_asmpause = B_FALSE; 2769860Sgdamore@opensolaris.org 2779860Sgdamore@opensolaris.org bzero(&mh->m_bogus_phy, sizeof (mh->m_bogus_phy)); 2789860Sgdamore@opensolaris.org mh->m_bogus_phy.phy_link = LINK_STATE_UNKNOWN; 2799860Sgdamore@opensolaris.org mh->m_bogus_phy.phy_duplex = LINK_DUPLEX_UNKNOWN; 2809860Sgdamore@opensolaris.org mh->m_bogus_phy.phy_addr = 0xff; 2819860Sgdamore@opensolaris.org mh->m_bogus_phy.phy_type = XCVR_NONE; 2829860Sgdamore@opensolaris.org mh->m_bogus_phy.phy_id = (uint32_t)-1; 2839860Sgdamore@opensolaris.org mh->m_bogus_phy.phy_loopback = PHY_LB_NONE; 2849860Sgdamore@opensolaris.org mh->m_bogus_phy.phy_flowctrl = LINK_FLOWCTRL_NONE; 2859860Sgdamore@opensolaris.org mh->m_phy = &mh->m_bogus_phy; 2869860Sgdamore@opensolaris.org 2879860Sgdamore@opensolaris.org for (int i = 0; i < 32; i++) { 2889860Sgdamore@opensolaris.org mh->m_phys[i].phy_mii = mh; 2899860Sgdamore@opensolaris.org } 2909860Sgdamore@opensolaris.org mh->m_bogus_phy.phy_mii = mh; 2919860Sgdamore@opensolaris.org 2929860Sgdamore@opensolaris.org return (mh); 2939860Sgdamore@opensolaris.org } 2949860Sgdamore@opensolaris.org 2959860Sgdamore@opensolaris.org mii_handle_t 2969860Sgdamore@opensolaris.org mii_alloc(void *private, dev_info_t *dip, mii_ops_t *ops) 2979860Sgdamore@opensolaris.org { 2989860Sgdamore@opensolaris.org return (mii_alloc_instance(private, dip, ddi_get_instance(dip), ops)); 2999860Sgdamore@opensolaris.org } 3009860Sgdamore@opensolaris.org 3019860Sgdamore@opensolaris.org void 3029860Sgdamore@opensolaris.org mii_set_pauseable(mii_handle_t mh, boolean_t pauseable, boolean_t asymetric) 3039860Sgdamore@opensolaris.org { 3049860Sgdamore@opensolaris.org phy_handle_t *ph; 3059860Sgdamore@opensolaris.org 3069860Sgdamore@opensolaris.org mutex_enter(&mh->m_lock); 3079860Sgdamore@opensolaris.org ph = mh->m_phy; 3089860Sgdamore@opensolaris.org ph->phy_cap_pause = mh->m_cap_pause = pauseable; 3099860Sgdamore@opensolaris.org ph->phy_cap_asmpause = mh->m_cap_asmpause = asymetric; 3109860Sgdamore@opensolaris.org if (pauseable) { 3119860Sgdamore@opensolaris.org mh->m_en_flowctrl = LINK_FLOWCTRL_BI; 3129860Sgdamore@opensolaris.org } else { 3139860Sgdamore@opensolaris.org mh->m_en_flowctrl = LINK_FLOWCTRL_NONE; 3149860Sgdamore@opensolaris.org } 3159860Sgdamore@opensolaris.org mutex_exit(&mh->m_lock); 3169860Sgdamore@opensolaris.org } 3179860Sgdamore@opensolaris.org 3189860Sgdamore@opensolaris.org void 3199860Sgdamore@opensolaris.org mii_free(mii_handle_t mh) 3209860Sgdamore@opensolaris.org { 3219860Sgdamore@opensolaris.org mutex_enter(&mh->m_lock); 3229860Sgdamore@opensolaris.org mh->m_started = B_FALSE; 3239860Sgdamore@opensolaris.org cv_broadcast(&mh->m_cv); 3249860Sgdamore@opensolaris.org mutex_exit(&mh->m_lock); 3259860Sgdamore@opensolaris.org 3269860Sgdamore@opensolaris.org ddi_taskq_destroy(mh->m_tq); 3279860Sgdamore@opensolaris.org mutex_destroy(&mh->m_lock); 3289860Sgdamore@opensolaris.org cv_destroy(&mh->m_cv); 3299860Sgdamore@opensolaris.org kmem_free(mh, sizeof (*mh)); 3309860Sgdamore@opensolaris.org } 3319860Sgdamore@opensolaris.org 3329860Sgdamore@opensolaris.org void 3339860Sgdamore@opensolaris.org mii_reset(mii_handle_t mh) 3349860Sgdamore@opensolaris.org { 3359860Sgdamore@opensolaris.org mutex_enter(&mh->m_lock); 3369860Sgdamore@opensolaris.org if (mh->m_tstate > MII_STATE_RESET) 3379860Sgdamore@opensolaris.org mh->m_tstate = MII_STATE_RESET; 3389860Sgdamore@opensolaris.org cv_broadcast(&mh->m_cv); 3399860Sgdamore@opensolaris.org mutex_exit(&mh->m_lock); 3409860Sgdamore@opensolaris.org } 3419860Sgdamore@opensolaris.org 3429860Sgdamore@opensolaris.org void 3439860Sgdamore@opensolaris.org mii_suspend(mii_handle_t mh) 3449860Sgdamore@opensolaris.org { 3459860Sgdamore@opensolaris.org mutex_enter(&mh->m_lock); 3469860Sgdamore@opensolaris.org while ((!mh->m_suspended) && (mh->m_started)) { 3479860Sgdamore@opensolaris.org mh->m_suspending = B_TRUE; 3489860Sgdamore@opensolaris.org cv_broadcast(&mh->m_cv); 3499860Sgdamore@opensolaris.org cv_wait(&mh->m_cv, &mh->m_lock); 3509860Sgdamore@opensolaris.org } 3519860Sgdamore@opensolaris.org mutex_exit(&mh->m_lock); 3529860Sgdamore@opensolaris.org } 3539860Sgdamore@opensolaris.org 3549860Sgdamore@opensolaris.org void 3559860Sgdamore@opensolaris.org mii_resume(mii_handle_t mh) 3569860Sgdamore@opensolaris.org { 3579860Sgdamore@opensolaris.org mutex_enter(&mh->m_lock); 35810359SGarrett.Damore@Sun.COM 35910359SGarrett.Damore@Sun.COM switch (mh->m_tstate) { 36010359SGarrett.Damore@Sun.COM case MII_STATE_PROBE: 36110359SGarrett.Damore@Sun.COM break; 36210359SGarrett.Damore@Sun.COM case MII_STATE_RESET: 36310359SGarrett.Damore@Sun.COM case MII_STATE_START: 36410359SGarrett.Damore@Sun.COM case MII_STATE_RUN: 36510359SGarrett.Damore@Sun.COM /* let monitor thread deal with this */ 3669860Sgdamore@opensolaris.org mh->m_tstate = MII_STATE_RESET; 36710359SGarrett.Damore@Sun.COM break; 36810359SGarrett.Damore@Sun.COM 36910359SGarrett.Damore@Sun.COM case MII_STATE_LOOPBACK: 37010359SGarrett.Damore@Sun.COM /* loopback is handled synchronously */ 37110359SGarrett.Damore@Sun.COM (void) _mii_loopback(mh); 37210359SGarrett.Damore@Sun.COM break; 3739860Sgdamore@opensolaris.org } 37410359SGarrett.Damore@Sun.COM 3759860Sgdamore@opensolaris.org mh->m_suspended = B_FALSE; 3769860Sgdamore@opensolaris.org cv_broadcast(&mh->m_cv); 3779860Sgdamore@opensolaris.org mutex_exit(&mh->m_lock); 3789860Sgdamore@opensolaris.org } 3799860Sgdamore@opensolaris.org 3809860Sgdamore@opensolaris.org void 3819860Sgdamore@opensolaris.org mii_start(mii_handle_t mh) 3829860Sgdamore@opensolaris.org { 3839860Sgdamore@opensolaris.org mutex_enter(&mh->m_lock); 3849860Sgdamore@opensolaris.org if (!mh->m_started) { 38510359SGarrett.Damore@Sun.COM mh->m_tstate = MII_STATE_PROBE; 3869860Sgdamore@opensolaris.org mh->m_started = B_TRUE; 3879860Sgdamore@opensolaris.org if (ddi_taskq_dispatch(mh->m_tq, _mii_task, mh, DDI_NOSLEEP) != 3889860Sgdamore@opensolaris.org DDI_SUCCESS) { 3899860Sgdamore@opensolaris.org cmn_err(CE_WARN, 3909860Sgdamore@opensolaris.org "%s: unable to start MII monitoring task", 3919860Sgdamore@opensolaris.org mh->m_name); 3929860Sgdamore@opensolaris.org mh->m_started = B_FALSE; 3939860Sgdamore@opensolaris.org } 3949860Sgdamore@opensolaris.org } 3959860Sgdamore@opensolaris.org cv_broadcast(&mh->m_cv); 3969860Sgdamore@opensolaris.org mutex_exit(&mh->m_lock); 3979860Sgdamore@opensolaris.org } 3989860Sgdamore@opensolaris.org 3999860Sgdamore@opensolaris.org void 4009860Sgdamore@opensolaris.org mii_stop(mii_handle_t mh) 4019860Sgdamore@opensolaris.org { 4029860Sgdamore@opensolaris.org mutex_enter(&mh->m_lock); 4039860Sgdamore@opensolaris.org mh->m_started = B_FALSE; 404*11428Sgdamore@opensolaris.org /* 405*11428Sgdamore@opensolaris.org * Reset link state to unknown defaults, since we're not 406*11428Sgdamore@opensolaris.org * monitoring it anymore. We'll reprobe all link state later. 407*11428Sgdamore@opensolaris.org */ 408*11428Sgdamore@opensolaris.org mh->m_link = LINK_STATE_UNKNOWN; 409*11428Sgdamore@opensolaris.org mh->m_phy = &mh->m_bogus_phy; 4109860Sgdamore@opensolaris.org cv_broadcast(&mh->m_cv); 4119860Sgdamore@opensolaris.org mutex_exit(&mh->m_lock); 412*11428Sgdamore@opensolaris.org /* 413*11428Sgdamore@opensolaris.org * Notify the MAC driver. This will allow it to call back 414*11428Sgdamore@opensolaris.org * into the MAC framework to clear any previous link state. 415*11428Sgdamore@opensolaris.org */ 416*11428Sgdamore@opensolaris.org _mii_notify(mh); 4179860Sgdamore@opensolaris.org } 4189860Sgdamore@opensolaris.org 4199860Sgdamore@opensolaris.org void 4209860Sgdamore@opensolaris.org mii_probe(mii_handle_t mh) 4219860Sgdamore@opensolaris.org { 4229860Sgdamore@opensolaris.org mutex_enter(&mh->m_lock); 42310359SGarrett.Damore@Sun.COM _mii_probe(mh); 4249860Sgdamore@opensolaris.org mutex_exit(&mh->m_lock); 4259860Sgdamore@opensolaris.org } 4269860Sgdamore@opensolaris.org 4279860Sgdamore@opensolaris.org void 4289860Sgdamore@opensolaris.org mii_check(mii_handle_t mh) 4299860Sgdamore@opensolaris.org { 4309860Sgdamore@opensolaris.org mutex_enter(&mh->m_lock); 4319860Sgdamore@opensolaris.org cv_broadcast(&mh->m_cv); 4329860Sgdamore@opensolaris.org mutex_exit(&mh->m_lock); 4339860Sgdamore@opensolaris.org } 4349860Sgdamore@opensolaris.org 4359860Sgdamore@opensolaris.org int 4369860Sgdamore@opensolaris.org mii_get_speed(mii_handle_t mh) 4379860Sgdamore@opensolaris.org { 4389860Sgdamore@opensolaris.org phy_handle_t *ph = mh->m_phy; 4399860Sgdamore@opensolaris.org 4409860Sgdamore@opensolaris.org return (ph->phy_speed); 4419860Sgdamore@opensolaris.org } 4429860Sgdamore@opensolaris.org 4439860Sgdamore@opensolaris.org link_duplex_t 4449860Sgdamore@opensolaris.org mii_get_duplex(mii_handle_t mh) 4459860Sgdamore@opensolaris.org { 4469860Sgdamore@opensolaris.org phy_handle_t *ph = mh->m_phy; 4479860Sgdamore@opensolaris.org 4489860Sgdamore@opensolaris.org return (ph->phy_duplex); 4499860Sgdamore@opensolaris.org } 4509860Sgdamore@opensolaris.org 4519860Sgdamore@opensolaris.org link_state_t 4529860Sgdamore@opensolaris.org mii_get_state(mii_handle_t mh) 4539860Sgdamore@opensolaris.org { 4549860Sgdamore@opensolaris.org phy_handle_t *ph = mh->m_phy; 4559860Sgdamore@opensolaris.org 4569860Sgdamore@opensolaris.org return (ph->phy_link); 4579860Sgdamore@opensolaris.org } 4589860Sgdamore@opensolaris.org 4599860Sgdamore@opensolaris.org link_flowctrl_t 4609860Sgdamore@opensolaris.org mii_get_flowctrl(mii_handle_t mh) 4619860Sgdamore@opensolaris.org { 4629860Sgdamore@opensolaris.org phy_handle_t *ph = mh->m_phy; 4639860Sgdamore@opensolaris.org 4649860Sgdamore@opensolaris.org return (ph->phy_flowctrl); 4659860Sgdamore@opensolaris.org } 4669860Sgdamore@opensolaris.org 4679860Sgdamore@opensolaris.org int 4689860Sgdamore@opensolaris.org mii_get_loopmodes(mii_handle_t mh, lb_property_t *modes) 4699860Sgdamore@opensolaris.org { 4709860Sgdamore@opensolaris.org phy_handle_t *ph = mh->m_phy; 47110359SGarrett.Damore@Sun.COM int cnt = 0; 47210359SGarrett.Damore@Sun.COM lb_property_t lmodes[MII_LOOPBACK_MAX]; 47310359SGarrett.Damore@Sun.COM 47410359SGarrett.Damore@Sun.COM lmodes[cnt].lb_type = normal; 47510363Sgdamore@opensolaris.org (void) strlcpy(lmodes[cnt].key, "normal", sizeof (lmodes[cnt].key)); 47610359SGarrett.Damore@Sun.COM lmodes[cnt].value = PHY_LB_NONE; 47710359SGarrett.Damore@Sun.COM cnt++; 47810359SGarrett.Damore@Sun.COM 47910359SGarrett.Damore@Sun.COM if (ph->phy_cap_1000_fdx || 48010359SGarrett.Damore@Sun.COM ph->phy_cap_100_fdx || 48110359SGarrett.Damore@Sun.COM ph->phy_cap_10_fdx) { 48210359SGarrett.Damore@Sun.COM /* we only support full duplex internal phy testing */ 48310359SGarrett.Damore@Sun.COM lmodes[cnt].lb_type = internal; 48410363Sgdamore@opensolaris.org (void) strlcpy(lmodes[cnt].key, "PHY", 48510363Sgdamore@opensolaris.org sizeof (lmodes[cnt].key)); 48610359SGarrett.Damore@Sun.COM lmodes[cnt].value = PHY_LB_INT_PHY; 48710359SGarrett.Damore@Sun.COM cnt++; 48810359SGarrett.Damore@Sun.COM } 4899860Sgdamore@opensolaris.org 49010359SGarrett.Damore@Sun.COM if (ph->phy_cap_1000_fdx) { 49110359SGarrett.Damore@Sun.COM lmodes[cnt].lb_type = external; 49210363Sgdamore@opensolaris.org (void) strlcpy(lmodes[cnt].key, "1000Mbps", 49310363Sgdamore@opensolaris.org sizeof (lmodes[cnt].key)); 49410359SGarrett.Damore@Sun.COM lmodes[cnt].value = PHY_LB_EXT_1000; 49510359SGarrett.Damore@Sun.COM cnt++; 49610359SGarrett.Damore@Sun.COM } 49710359SGarrett.Damore@Sun.COM 49810359SGarrett.Damore@Sun.COM if (ph->phy_cap_100_fdx) { 49910359SGarrett.Damore@Sun.COM lmodes[cnt].lb_type = external; 50010363Sgdamore@opensolaris.org (void) strlcpy(lmodes[cnt].key, "100Mbps", 50110363Sgdamore@opensolaris.org sizeof (lmodes[cnt].key)); 50210359SGarrett.Damore@Sun.COM lmodes[cnt].value = PHY_LB_EXT_100; 50310359SGarrett.Damore@Sun.COM cnt++; 50410359SGarrett.Damore@Sun.COM } 50510359SGarrett.Damore@Sun.COM 50610359SGarrett.Damore@Sun.COM if (ph->phy_cap_10_fdx) { 50710359SGarrett.Damore@Sun.COM lmodes[cnt].lb_type = external; 50810363Sgdamore@opensolaris.org (void) strlcpy(lmodes[cnt].key, "10Mbps", 50910363Sgdamore@opensolaris.org sizeof (lmodes[cnt].key)); 51010359SGarrett.Damore@Sun.COM lmodes[cnt].value = PHY_LB_EXT_10; 51110359SGarrett.Damore@Sun.COM cnt++; 5129860Sgdamore@opensolaris.org } 5139860Sgdamore@opensolaris.org 5149860Sgdamore@opensolaris.org if (modes) { 51510359SGarrett.Damore@Sun.COM bcopy(lmodes, modes, sizeof (lb_property_t) * cnt); 5169860Sgdamore@opensolaris.org } 51710359SGarrett.Damore@Sun.COM 5189860Sgdamore@opensolaris.org return (cnt); 5199860Sgdamore@opensolaris.org } 5209860Sgdamore@opensolaris.org 5219860Sgdamore@opensolaris.org uint32_t 5229860Sgdamore@opensolaris.org mii_get_loopback(mii_handle_t mh) 5239860Sgdamore@opensolaris.org { 5249860Sgdamore@opensolaris.org phy_handle_t *ph = mh->m_phy; 5259860Sgdamore@opensolaris.org 5269860Sgdamore@opensolaris.org return (ph->phy_loopback); 5279860Sgdamore@opensolaris.org } 5289860Sgdamore@opensolaris.org 5299860Sgdamore@opensolaris.org int 5309860Sgdamore@opensolaris.org mii_set_loopback(mii_handle_t mh, uint32_t loop) 5319860Sgdamore@opensolaris.org { 5329860Sgdamore@opensolaris.org phy_handle_t *ph; 53310359SGarrett.Damore@Sun.COM int rv; 5349860Sgdamore@opensolaris.org 5359860Sgdamore@opensolaris.org mutex_enter(&mh->m_lock); 5369860Sgdamore@opensolaris.org ph = mh->m_phy; 5379860Sgdamore@opensolaris.org 5389860Sgdamore@opensolaris.org if ((!mh->m_started) || (!ph->phy_present) || 5399860Sgdamore@opensolaris.org (loop >= mii_get_loopmodes(mh, NULL))) { 5409860Sgdamore@opensolaris.org return (EINVAL); 5419860Sgdamore@opensolaris.org } 5429860Sgdamore@opensolaris.org 5439860Sgdamore@opensolaris.org ph->phy_loopback = loop; 54410359SGarrett.Damore@Sun.COM rv = _mii_loopback(mh); 54510359SGarrett.Damore@Sun.COM if (rv == DDI_SUCCESS) { 54610359SGarrett.Damore@Sun.COM mh->m_tstate = MII_STATE_LOOPBACK; 54710359SGarrett.Damore@Sun.COM } 5489860Sgdamore@opensolaris.org cv_broadcast(&mh->m_cv); 5499860Sgdamore@opensolaris.org mutex_exit(&mh->m_lock); 5509860Sgdamore@opensolaris.org 55110359SGarrett.Damore@Sun.COM return (rv == DDI_SUCCESS ? 0 : EIO); 5529860Sgdamore@opensolaris.org } 5539860Sgdamore@opensolaris.org 5549860Sgdamore@opensolaris.org uint32_t 5559860Sgdamore@opensolaris.org mii_get_id(mii_handle_t mh) 5569860Sgdamore@opensolaris.org { 5579860Sgdamore@opensolaris.org phy_handle_t *ph = mh->m_phy; 5589860Sgdamore@opensolaris.org 5599860Sgdamore@opensolaris.org return (ph->phy_id); 5609860Sgdamore@opensolaris.org } 5619860Sgdamore@opensolaris.org 5629860Sgdamore@opensolaris.org int 5639860Sgdamore@opensolaris.org mii_get_addr(mii_handle_t mh) 5649860Sgdamore@opensolaris.org { 5659860Sgdamore@opensolaris.org return (mh->m_addr); 5669860Sgdamore@opensolaris.org } 5679860Sgdamore@opensolaris.org 5689860Sgdamore@opensolaris.org /* GLDv3 helpers */ 5699860Sgdamore@opensolaris.org 5709860Sgdamore@opensolaris.org boolean_t 5719860Sgdamore@opensolaris.org mii_m_loop_ioctl(mii_handle_t mh, queue_t *wq, mblk_t *mp) 5729860Sgdamore@opensolaris.org { 5739860Sgdamore@opensolaris.org struct iocblk *iocp; 5749860Sgdamore@opensolaris.org int rv = 0; 5759860Sgdamore@opensolaris.org int cnt; 5769860Sgdamore@opensolaris.org lb_property_t modes[MII_LOOPBACK_MAX]; 5779860Sgdamore@opensolaris.org lb_info_sz_t sz; 5789860Sgdamore@opensolaris.org int cmd; 5799860Sgdamore@opensolaris.org uint32_t mode; 5809860Sgdamore@opensolaris.org 5819860Sgdamore@opensolaris.org iocp = (void *)mp->b_rptr; 5829860Sgdamore@opensolaris.org cmd = iocp->ioc_cmd; 5839860Sgdamore@opensolaris.org 5849860Sgdamore@opensolaris.org switch (cmd) { 5859860Sgdamore@opensolaris.org case LB_SET_MODE: 5869860Sgdamore@opensolaris.org case LB_GET_INFO_SIZE: 5879860Sgdamore@opensolaris.org case LB_GET_INFO: 5889860Sgdamore@opensolaris.org case LB_GET_MODE: 5899860Sgdamore@opensolaris.org break; 5909860Sgdamore@opensolaris.org 5919860Sgdamore@opensolaris.org default: 5929860Sgdamore@opensolaris.org return (B_FALSE); 5939860Sgdamore@opensolaris.org } 5949860Sgdamore@opensolaris.org 5959860Sgdamore@opensolaris.org if (mp->b_cont == NULL) { 5969860Sgdamore@opensolaris.org miocnak(wq, mp, 0, EINVAL); 5979860Sgdamore@opensolaris.org return (B_TRUE); 5989860Sgdamore@opensolaris.org } 5999860Sgdamore@opensolaris.org 6009860Sgdamore@opensolaris.org switch (cmd) { 6019860Sgdamore@opensolaris.org case LB_GET_INFO_SIZE: 6029860Sgdamore@opensolaris.org cnt = mii_get_loopmodes(mh, modes); 6039860Sgdamore@opensolaris.org if (iocp->ioc_count != sizeof (sz)) { 6049860Sgdamore@opensolaris.org rv = EINVAL; 6059860Sgdamore@opensolaris.org } else { 6069860Sgdamore@opensolaris.org sz = cnt * sizeof (lb_property_t); 6079860Sgdamore@opensolaris.org bcopy(&sz, mp->b_cont->b_rptr, sizeof (sz)); 6089860Sgdamore@opensolaris.org } 6099860Sgdamore@opensolaris.org break; 6109860Sgdamore@opensolaris.org 6119860Sgdamore@opensolaris.org case LB_GET_INFO: 6129860Sgdamore@opensolaris.org cnt = mii_get_loopmodes(mh, modes); 6139860Sgdamore@opensolaris.org if (iocp->ioc_count != (cnt * sizeof (lb_property_t))) { 6149860Sgdamore@opensolaris.org rv = EINVAL; 6159860Sgdamore@opensolaris.org } else { 6169860Sgdamore@opensolaris.org bcopy(modes, mp->b_cont->b_rptr, iocp->ioc_count); 6179860Sgdamore@opensolaris.org } 6189860Sgdamore@opensolaris.org break; 6199860Sgdamore@opensolaris.org 6209860Sgdamore@opensolaris.org case LB_GET_MODE: 6219860Sgdamore@opensolaris.org if (iocp->ioc_count != sizeof (mode)) { 6229860Sgdamore@opensolaris.org rv = EINVAL; 6239860Sgdamore@opensolaris.org } else { 6249860Sgdamore@opensolaris.org mode = mii_get_loopback(mh); 6259860Sgdamore@opensolaris.org bcopy(&mode, mp->b_cont->b_rptr, sizeof (mode)); 6269860Sgdamore@opensolaris.org } 6279860Sgdamore@opensolaris.org break; 6289860Sgdamore@opensolaris.org 6299860Sgdamore@opensolaris.org case LB_SET_MODE: 6309860Sgdamore@opensolaris.org rv = secpolicy_net_config(iocp->ioc_cr, B_FALSE); 6319860Sgdamore@opensolaris.org if (rv != 0) 6329860Sgdamore@opensolaris.org break; 6339860Sgdamore@opensolaris.org if (iocp->ioc_count != sizeof (mode)) { 6349860Sgdamore@opensolaris.org rv = EINVAL; 6359860Sgdamore@opensolaris.org break; 6369860Sgdamore@opensolaris.org } 6379860Sgdamore@opensolaris.org bcopy(mp->b_cont->b_rptr, &mode, sizeof (mode)); 6389860Sgdamore@opensolaris.org rv = mii_set_loopback(mh, mode); 6399860Sgdamore@opensolaris.org break; 6409860Sgdamore@opensolaris.org } 6419860Sgdamore@opensolaris.org 6429860Sgdamore@opensolaris.org if (rv == 0) { 6439860Sgdamore@opensolaris.org miocack(wq, mp, iocp->ioc_count, 0); 6449860Sgdamore@opensolaris.org } else { 6459860Sgdamore@opensolaris.org miocnak(wq, mp, 0, rv); 6469860Sgdamore@opensolaris.org } 6479860Sgdamore@opensolaris.org return (B_TRUE); 6489860Sgdamore@opensolaris.org } 6499860Sgdamore@opensolaris.org 6509860Sgdamore@opensolaris.org int 6519860Sgdamore@opensolaris.org mii_m_getprop(mii_handle_t mh, const char *name, mac_prop_id_t num, 6529860Sgdamore@opensolaris.org uint_t flags, uint_t sz, void *val, uint_t *permp) 6539860Sgdamore@opensolaris.org { 6549860Sgdamore@opensolaris.org phy_handle_t *ph; 6559860Sgdamore@opensolaris.org int err = 0; 6569860Sgdamore@opensolaris.org uint_t perm; 6579860Sgdamore@opensolaris.org boolean_t dfl = flags & MAC_PROP_DEFAULT; 6589860Sgdamore@opensolaris.org 6599860Sgdamore@opensolaris.org _NOTE(ARGUNUSED(name)); 6609860Sgdamore@opensolaris.org 6619860Sgdamore@opensolaris.org if (sz < 1) 6629860Sgdamore@opensolaris.org return (EINVAL); 6639860Sgdamore@opensolaris.org 6649860Sgdamore@opensolaris.org mutex_enter(&mh->m_lock); 6659860Sgdamore@opensolaris.org 6669860Sgdamore@opensolaris.org ph = mh->m_phy; 6679860Sgdamore@opensolaris.org perm = MAC_PROP_PERM_RW; 6689860Sgdamore@opensolaris.org 6699860Sgdamore@opensolaris.org #define CASE_PROP_ABILITY(PROP, VAR) \ 6709860Sgdamore@opensolaris.org case MAC_PROP_ADV_##PROP: \ 6719860Sgdamore@opensolaris.org perm = MAC_PROP_PERM_READ; \ 6729860Sgdamore@opensolaris.org *(uint8_t *)val = \ 6739860Sgdamore@opensolaris.org dfl ? ph->phy_cap_##VAR : ph->phy_adv_##VAR; \ 6749860Sgdamore@opensolaris.org break; \ 6759860Sgdamore@opensolaris.org \ 6769860Sgdamore@opensolaris.org case MAC_PROP_EN_##PROP: \ 6779860Sgdamore@opensolaris.org if (!ph->phy_cap_##VAR) \ 6789860Sgdamore@opensolaris.org perm = MAC_PROP_PERM_READ; \ 6799860Sgdamore@opensolaris.org *(uint8_t *)val = \ 6809860Sgdamore@opensolaris.org dfl ? ph->phy_cap_##VAR : ph->phy_en_##VAR; \ 6819860Sgdamore@opensolaris.org break; 6829860Sgdamore@opensolaris.org 6839860Sgdamore@opensolaris.org switch (num) { 6849860Sgdamore@opensolaris.org case MAC_PROP_DUPLEX: 6859860Sgdamore@opensolaris.org perm = MAC_PROP_PERM_READ; 6869860Sgdamore@opensolaris.org if (sz >= sizeof (link_duplex_t)) { 6879860Sgdamore@opensolaris.org bcopy(&ph->phy_duplex, val, sizeof (link_duplex_t)); 6889860Sgdamore@opensolaris.org } else { 6899860Sgdamore@opensolaris.org err = EINVAL; 6909860Sgdamore@opensolaris.org } 6919860Sgdamore@opensolaris.org break; 6929860Sgdamore@opensolaris.org 6939860Sgdamore@opensolaris.org case MAC_PROP_SPEED: 6949860Sgdamore@opensolaris.org perm = MAC_PROP_PERM_READ; 6959860Sgdamore@opensolaris.org if (sz >= sizeof (uint64_t)) { 6969860Sgdamore@opensolaris.org uint64_t speed = ph->phy_speed * 1000000ull; 6979860Sgdamore@opensolaris.org bcopy(&speed, val, sizeof (speed)); 6989860Sgdamore@opensolaris.org } else { 6999860Sgdamore@opensolaris.org err = EINVAL; 7009860Sgdamore@opensolaris.org } 7019860Sgdamore@opensolaris.org break; 7029860Sgdamore@opensolaris.org 7039860Sgdamore@opensolaris.org case MAC_PROP_AUTONEG: 7049860Sgdamore@opensolaris.org *(uint8_t *)val = 7059860Sgdamore@opensolaris.org dfl ? ph->phy_cap_aneg : ph->phy_adv_aneg; 7069860Sgdamore@opensolaris.org break; 7079860Sgdamore@opensolaris.org 7089860Sgdamore@opensolaris.org case MAC_PROP_FLOWCTRL: 7099860Sgdamore@opensolaris.org if (sz >= sizeof (link_flowctrl_t)) { 7109860Sgdamore@opensolaris.org bcopy(&ph->phy_flowctrl, val, 7119860Sgdamore@opensolaris.org sizeof (link_flowctrl_t)); 7129860Sgdamore@opensolaris.org } else { 7139860Sgdamore@opensolaris.org err = EINVAL; 7149860Sgdamore@opensolaris.org } 7159860Sgdamore@opensolaris.org break; 7169860Sgdamore@opensolaris.org 7179860Sgdamore@opensolaris.org CASE_PROP_ABILITY(1000FDX_CAP, 1000_fdx) 7189860Sgdamore@opensolaris.org CASE_PROP_ABILITY(1000HDX_CAP, 1000_hdx) 7199860Sgdamore@opensolaris.org CASE_PROP_ABILITY(100T4_CAP, 100_t4) 7209860Sgdamore@opensolaris.org CASE_PROP_ABILITY(100FDX_CAP, 100_fdx) 7219860Sgdamore@opensolaris.org CASE_PROP_ABILITY(100HDX_CAP, 100_hdx) 7229860Sgdamore@opensolaris.org CASE_PROP_ABILITY(10FDX_CAP, 10_fdx) 7239860Sgdamore@opensolaris.org CASE_PROP_ABILITY(10HDX_CAP, 10_hdx) 7249860Sgdamore@opensolaris.org 7259860Sgdamore@opensolaris.org default: 7269860Sgdamore@opensolaris.org err = ENOTSUP; 7279860Sgdamore@opensolaris.org break; 7289860Sgdamore@opensolaris.org } 7299860Sgdamore@opensolaris.org 7309860Sgdamore@opensolaris.org if (err == 0) { 7319860Sgdamore@opensolaris.org *permp = perm; 7329860Sgdamore@opensolaris.org } 7339860Sgdamore@opensolaris.org 7349860Sgdamore@opensolaris.org mutex_exit(&mh->m_lock); 7359860Sgdamore@opensolaris.org 7369860Sgdamore@opensolaris.org return (err); 7379860Sgdamore@opensolaris.org } 7389860Sgdamore@opensolaris.org 7399860Sgdamore@opensolaris.org int 7409860Sgdamore@opensolaris.org mii_m_setprop(mii_handle_t mh, const char *name, mac_prop_id_t num, 7419860Sgdamore@opensolaris.org uint_t sz, const void *valp) 7429860Sgdamore@opensolaris.org { 7439860Sgdamore@opensolaris.org phy_handle_t *ph; 7449860Sgdamore@opensolaris.org boolean_t *advp = NULL; 7459860Sgdamore@opensolaris.org boolean_t *capp = NULL; 7469860Sgdamore@opensolaris.org int *macpp = NULL; 7479860Sgdamore@opensolaris.org int rv = ENOTSUP; 7489860Sgdamore@opensolaris.org 7499860Sgdamore@opensolaris.org _NOTE(ARGUNUSED(name)); 7509860Sgdamore@opensolaris.org 7519860Sgdamore@opensolaris.org if (sz < 1) 7529860Sgdamore@opensolaris.org return (EINVAL); 7539860Sgdamore@opensolaris.org 7549860Sgdamore@opensolaris.org mutex_enter(&mh->m_lock); 7559860Sgdamore@opensolaris.org 7569860Sgdamore@opensolaris.org ph = mh->m_phy; 7579860Sgdamore@opensolaris.org 7589860Sgdamore@opensolaris.org /* we don't support changing parameters while in loopback mode */ 7599860Sgdamore@opensolaris.org if (ph->phy_loopback != PHY_LB_NONE) { 7609860Sgdamore@opensolaris.org switch (num) { 7619860Sgdamore@opensolaris.org case MAC_PROP_EN_1000FDX_CAP: 7629860Sgdamore@opensolaris.org case MAC_PROP_EN_1000HDX_CAP: 7639860Sgdamore@opensolaris.org case MAC_PROP_EN_100FDX_CAP: 7649860Sgdamore@opensolaris.org case MAC_PROP_EN_100HDX_CAP: 7659860Sgdamore@opensolaris.org case MAC_PROP_EN_100T4_CAP: 7669860Sgdamore@opensolaris.org case MAC_PROP_EN_10FDX_CAP: 7679860Sgdamore@opensolaris.org case MAC_PROP_EN_10HDX_CAP: 7689860Sgdamore@opensolaris.org case MAC_PROP_AUTONEG: 7699860Sgdamore@opensolaris.org case MAC_PROP_FLOWCTRL: 7709860Sgdamore@opensolaris.org return (EBUSY); 7719860Sgdamore@opensolaris.org } 7729860Sgdamore@opensolaris.org } 7739860Sgdamore@opensolaris.org 7749860Sgdamore@opensolaris.org switch (num) { 7759860Sgdamore@opensolaris.org case MAC_PROP_EN_1000FDX_CAP: 7769860Sgdamore@opensolaris.org capp = &ph->phy_cap_1000_fdx; 7779860Sgdamore@opensolaris.org advp = &ph->phy_en_1000_fdx; 7789860Sgdamore@opensolaris.org macpp = &mh->m_en_1000_fdx; 7799860Sgdamore@opensolaris.org break; 7809860Sgdamore@opensolaris.org case MAC_PROP_EN_1000HDX_CAP: 7819860Sgdamore@opensolaris.org capp = &ph->phy_cap_1000_hdx; 7829860Sgdamore@opensolaris.org advp = &ph->phy_en_1000_hdx; 7839860Sgdamore@opensolaris.org macpp = &mh->m_en_1000_hdx; 7849860Sgdamore@opensolaris.org break; 7859860Sgdamore@opensolaris.org case MAC_PROP_EN_100FDX_CAP: 7869860Sgdamore@opensolaris.org capp = &ph->phy_cap_100_fdx; 7879860Sgdamore@opensolaris.org advp = &ph->phy_en_100_fdx; 7889860Sgdamore@opensolaris.org macpp = &mh->m_en_100_fdx; 7899860Sgdamore@opensolaris.org break; 7909860Sgdamore@opensolaris.org case MAC_PROP_EN_100HDX_CAP: 7919860Sgdamore@opensolaris.org capp = &ph->phy_cap_100_hdx; 7929860Sgdamore@opensolaris.org advp = &ph->phy_en_100_hdx; 7939860Sgdamore@opensolaris.org macpp = &mh->m_en_100_hdx; 7949860Sgdamore@opensolaris.org break; 7959860Sgdamore@opensolaris.org case MAC_PROP_EN_100T4_CAP: 7969860Sgdamore@opensolaris.org capp = &ph->phy_cap_100_t4; 7979860Sgdamore@opensolaris.org advp = &ph->phy_en_100_t4; 7989860Sgdamore@opensolaris.org macpp = &mh->m_en_100_t4; 7999860Sgdamore@opensolaris.org break; 8009860Sgdamore@opensolaris.org case MAC_PROP_EN_10FDX_CAP: 8019860Sgdamore@opensolaris.org capp = &ph->phy_cap_10_fdx; 8029860Sgdamore@opensolaris.org advp = &ph->phy_en_10_fdx; 8039860Sgdamore@opensolaris.org macpp = &mh->m_en_10_fdx; 8049860Sgdamore@opensolaris.org break; 8059860Sgdamore@opensolaris.org case MAC_PROP_EN_10HDX_CAP: 8069860Sgdamore@opensolaris.org capp = &ph->phy_cap_10_hdx; 8079860Sgdamore@opensolaris.org advp = &ph->phy_en_10_hdx; 8089860Sgdamore@opensolaris.org macpp = &mh->m_en_10_hdx; 8099860Sgdamore@opensolaris.org break; 8109860Sgdamore@opensolaris.org case MAC_PROP_AUTONEG: 8119860Sgdamore@opensolaris.org capp = &ph->phy_cap_aneg; 8129860Sgdamore@opensolaris.org advp = &ph->phy_en_aneg; 8139860Sgdamore@opensolaris.org macpp = &mh->m_en_aneg; 8149860Sgdamore@opensolaris.org break; 8159860Sgdamore@opensolaris.org case MAC_PROP_FLOWCTRL: 8169860Sgdamore@opensolaris.org if (sz < sizeof (link_flowctrl_t)) { 8179860Sgdamore@opensolaris.org rv = EINVAL; 8189860Sgdamore@opensolaris.org } else { 8199860Sgdamore@opensolaris.org link_flowctrl_t fc; 8209860Sgdamore@opensolaris.org boolean_t chg; 8219860Sgdamore@opensolaris.org 8229860Sgdamore@opensolaris.org bcopy(valp, &fc, sizeof (fc)); 8239860Sgdamore@opensolaris.org 8249860Sgdamore@opensolaris.org chg = fc == ph->phy_en_flowctrl ? B_FALSE : B_TRUE; 8259860Sgdamore@opensolaris.org switch (fc) { 8269860Sgdamore@opensolaris.org case LINK_FLOWCTRL_NONE: 8279860Sgdamore@opensolaris.org ph->phy_en_pause = B_FALSE; 8289860Sgdamore@opensolaris.org ph->phy_en_asmpause = B_FALSE; 8299860Sgdamore@opensolaris.org ph->phy_en_flowctrl = fc; 8309860Sgdamore@opensolaris.org break; 8319860Sgdamore@opensolaris.org /* 8329860Sgdamore@opensolaris.org * Note that while we don't have a way to 8339860Sgdamore@opensolaris.org * advertise that we can RX pause (we just 8349860Sgdamore@opensolaris.org * won't send pause frames), we advertise full 8359860Sgdamore@opensolaris.org * support. The MAC driver will learn of the 8369860Sgdamore@opensolaris.org * configuration via the saved value of the 8379860Sgdamore@opensolaris.org * tunable. 8389860Sgdamore@opensolaris.org */ 8399860Sgdamore@opensolaris.org case LINK_FLOWCTRL_BI: 8409860Sgdamore@opensolaris.org case LINK_FLOWCTRL_RX: 8419860Sgdamore@opensolaris.org if (ph->phy_cap_pause) { 8429860Sgdamore@opensolaris.org ph->phy_en_pause = B_TRUE; 8439860Sgdamore@opensolaris.org ph->phy_en_asmpause = B_TRUE; 8449860Sgdamore@opensolaris.org ph->phy_en_flowctrl = fc; 8459860Sgdamore@opensolaris.org } else { 8469860Sgdamore@opensolaris.org rv = EINVAL; 8479860Sgdamore@opensolaris.org } 8489860Sgdamore@opensolaris.org break; 8499860Sgdamore@opensolaris.org 8509860Sgdamore@opensolaris.org /* 8519860Sgdamore@opensolaris.org * Tell the other side that we can assert 8529860Sgdamore@opensolaris.org * pause, but we cannot resend. 8539860Sgdamore@opensolaris.org */ 8549860Sgdamore@opensolaris.org case LINK_FLOWCTRL_TX: 8559860Sgdamore@opensolaris.org if (ph->phy_cap_asmpause) { 8569860Sgdamore@opensolaris.org ph->phy_en_pause = B_FALSE; 8579860Sgdamore@opensolaris.org ph->phy_en_flowctrl = fc; 8589860Sgdamore@opensolaris.org ph->phy_en_asmpause = B_TRUE; 8599860Sgdamore@opensolaris.org } else { 8609860Sgdamore@opensolaris.org rv = EINVAL; 8619860Sgdamore@opensolaris.org } 8629860Sgdamore@opensolaris.org break; 8639860Sgdamore@opensolaris.org default: 8649860Sgdamore@opensolaris.org rv = EINVAL; 8659860Sgdamore@opensolaris.org break; 8669860Sgdamore@opensolaris.org } 8679860Sgdamore@opensolaris.org if ((rv == 0) && chg) { 8689860Sgdamore@opensolaris.org mh->m_en_flowctrl = fc; 8699860Sgdamore@opensolaris.org mh->m_tstate = MII_STATE_RESET; 8709860Sgdamore@opensolaris.org cv_broadcast(&mh->m_cv); 8719860Sgdamore@opensolaris.org } 8729860Sgdamore@opensolaris.org } 8739860Sgdamore@opensolaris.org break; 8749860Sgdamore@opensolaris.org 8759860Sgdamore@opensolaris.org default: 8769860Sgdamore@opensolaris.org rv = ENOTSUP; 8779860Sgdamore@opensolaris.org break; 8789860Sgdamore@opensolaris.org } 8799860Sgdamore@opensolaris.org 8809860Sgdamore@opensolaris.org if (capp && advp && macpp) { 8819860Sgdamore@opensolaris.org if (sz < sizeof (uint8_t)) { 8829860Sgdamore@opensolaris.org rv = EINVAL; 8839860Sgdamore@opensolaris.org 8849860Sgdamore@opensolaris.org } else if (*capp) { 8859860Sgdamore@opensolaris.org if (*advp != *(uint8_t *)valp) { 8869860Sgdamore@opensolaris.org *advp = *(uint8_t *)valp; 8879860Sgdamore@opensolaris.org *macpp = *(uint8_t *)valp; 8889860Sgdamore@opensolaris.org mh->m_tstate = MII_STATE_RESET; 8899860Sgdamore@opensolaris.org cv_broadcast(&mh->m_cv); 8909860Sgdamore@opensolaris.org } 8919860Sgdamore@opensolaris.org rv = 0; 8929860Sgdamore@opensolaris.org } 8939860Sgdamore@opensolaris.org } 8949860Sgdamore@opensolaris.org 8959860Sgdamore@opensolaris.org mutex_exit(&mh->m_lock); 8969860Sgdamore@opensolaris.org return (rv); 8979860Sgdamore@opensolaris.org } 8989860Sgdamore@opensolaris.org 8999860Sgdamore@opensolaris.org int 9009860Sgdamore@opensolaris.org mii_m_getstat(mii_handle_t mh, uint_t stat, uint64_t *val) 9019860Sgdamore@opensolaris.org { 9029860Sgdamore@opensolaris.org phy_handle_t *ph; 9039860Sgdamore@opensolaris.org int rv = 0; 9049860Sgdamore@opensolaris.org 9059860Sgdamore@opensolaris.org mutex_enter(&mh->m_lock); 9069860Sgdamore@opensolaris.org 9079860Sgdamore@opensolaris.org ph = mh->m_phy; 9089860Sgdamore@opensolaris.org 9099860Sgdamore@opensolaris.org switch (stat) { 9109860Sgdamore@opensolaris.org case MAC_STAT_IFSPEED: 9119860Sgdamore@opensolaris.org *val = ph->phy_speed * 1000000ull; 9129860Sgdamore@opensolaris.org break; 9139860Sgdamore@opensolaris.org case ETHER_STAT_LINK_DUPLEX: 9149860Sgdamore@opensolaris.org *val = ph->phy_duplex; 9159860Sgdamore@opensolaris.org break; 9169860Sgdamore@opensolaris.org case ETHER_STAT_LINK_AUTONEG: 9179860Sgdamore@opensolaris.org *val = !!(ph->phy_adv_aneg && ph->phy_lp_aneg); 9189860Sgdamore@opensolaris.org break; 9199860Sgdamore@opensolaris.org case ETHER_STAT_XCVR_ID: 9209860Sgdamore@opensolaris.org *val = ph->phy_id; 9219860Sgdamore@opensolaris.org break; 9229860Sgdamore@opensolaris.org case ETHER_STAT_XCVR_INUSE: 9239860Sgdamore@opensolaris.org *val = ph->phy_type; 9249860Sgdamore@opensolaris.org break; 9259860Sgdamore@opensolaris.org case ETHER_STAT_XCVR_ADDR: 9269860Sgdamore@opensolaris.org *val = ph->phy_addr; 9279860Sgdamore@opensolaris.org break; 9289860Sgdamore@opensolaris.org case ETHER_STAT_LINK_ASMPAUSE: 9299860Sgdamore@opensolaris.org *val = ph->phy_adv_asmpause && ph->phy_lp_asmpause && 9309860Sgdamore@opensolaris.org ph->phy_adv_pause != ph->phy_lp_pause; 9319860Sgdamore@opensolaris.org break; 9329860Sgdamore@opensolaris.org case ETHER_STAT_LINK_PAUSE: 9339860Sgdamore@opensolaris.org *val = (ph->phy_flowctrl == LINK_FLOWCTRL_BI) || 9349860Sgdamore@opensolaris.org (ph->phy_flowctrl == LINK_FLOWCTRL_RX); 9359860Sgdamore@opensolaris.org break; 9369860Sgdamore@opensolaris.org case ETHER_STAT_CAP_1000FDX: 9379860Sgdamore@opensolaris.org *val = ph->phy_cap_1000_fdx; 9389860Sgdamore@opensolaris.org break; 9399860Sgdamore@opensolaris.org case ETHER_STAT_CAP_1000HDX: 9409860Sgdamore@opensolaris.org *val = ph->phy_cap_1000_hdx; 9419860Sgdamore@opensolaris.org break; 9429860Sgdamore@opensolaris.org case ETHER_STAT_CAP_100FDX: 9439860Sgdamore@opensolaris.org *val = ph->phy_cap_100_fdx; 9449860Sgdamore@opensolaris.org break; 9459860Sgdamore@opensolaris.org case ETHER_STAT_CAP_100HDX: 9469860Sgdamore@opensolaris.org *val = ph->phy_cap_100_hdx; 9479860Sgdamore@opensolaris.org break; 9489860Sgdamore@opensolaris.org case ETHER_STAT_CAP_10FDX: 9499860Sgdamore@opensolaris.org *val = ph->phy_cap_10_fdx; 9509860Sgdamore@opensolaris.org break; 9519860Sgdamore@opensolaris.org case ETHER_STAT_CAP_10HDX: 9529860Sgdamore@opensolaris.org *val = ph->phy_cap_10_hdx; 9539860Sgdamore@opensolaris.org break; 9549860Sgdamore@opensolaris.org case ETHER_STAT_CAP_100T4: 9559860Sgdamore@opensolaris.org *val = ph->phy_cap_100_t4; 9569860Sgdamore@opensolaris.org break; 9579860Sgdamore@opensolaris.org case ETHER_STAT_CAP_AUTONEG: 9589860Sgdamore@opensolaris.org *val = ph->phy_cap_aneg; 9599860Sgdamore@opensolaris.org break; 9609860Sgdamore@opensolaris.org case ETHER_STAT_CAP_PAUSE: 9619860Sgdamore@opensolaris.org *val = ph->phy_cap_pause; 9629860Sgdamore@opensolaris.org break; 9639860Sgdamore@opensolaris.org case ETHER_STAT_CAP_ASMPAUSE: 9649860Sgdamore@opensolaris.org *val = ph->phy_cap_asmpause; 9659860Sgdamore@opensolaris.org break; 9669860Sgdamore@opensolaris.org 9679860Sgdamore@opensolaris.org case ETHER_STAT_LP_CAP_1000FDX: 9689860Sgdamore@opensolaris.org *val = ph->phy_lp_1000_fdx; 9699860Sgdamore@opensolaris.org break; 9709860Sgdamore@opensolaris.org case ETHER_STAT_LP_CAP_1000HDX: 9719860Sgdamore@opensolaris.org *val = ph->phy_lp_1000_hdx; 9729860Sgdamore@opensolaris.org break; 9739860Sgdamore@opensolaris.org case ETHER_STAT_LP_CAP_100FDX: 9749860Sgdamore@opensolaris.org *val = ph->phy_lp_100_fdx; 9759860Sgdamore@opensolaris.org break; 9769860Sgdamore@opensolaris.org case ETHER_STAT_LP_CAP_100HDX: 9779860Sgdamore@opensolaris.org *val = ph->phy_lp_100_hdx; 9789860Sgdamore@opensolaris.org break; 9799860Sgdamore@opensolaris.org case ETHER_STAT_LP_CAP_10FDX: 9809860Sgdamore@opensolaris.org *val = ph->phy_lp_10_fdx; 9819860Sgdamore@opensolaris.org break; 9829860Sgdamore@opensolaris.org case ETHER_STAT_LP_CAP_10HDX: 9839860Sgdamore@opensolaris.org *val = ph->phy_lp_10_hdx; 9849860Sgdamore@opensolaris.org break; 9859860Sgdamore@opensolaris.org case ETHER_STAT_LP_CAP_100T4: 9869860Sgdamore@opensolaris.org *val = ph->phy_lp_100_t4; 9879860Sgdamore@opensolaris.org break; 9889860Sgdamore@opensolaris.org case ETHER_STAT_LP_CAP_AUTONEG: 9899860Sgdamore@opensolaris.org *val = ph->phy_lp_aneg; 9909860Sgdamore@opensolaris.org break; 9919860Sgdamore@opensolaris.org case ETHER_STAT_LP_CAP_PAUSE: 9929860Sgdamore@opensolaris.org *val = ph->phy_lp_pause; 9939860Sgdamore@opensolaris.org break; 9949860Sgdamore@opensolaris.org case ETHER_STAT_LP_CAP_ASMPAUSE: 9959860Sgdamore@opensolaris.org *val = ph->phy_lp_asmpause; 9969860Sgdamore@opensolaris.org break; 9979860Sgdamore@opensolaris.org 9989860Sgdamore@opensolaris.org case ETHER_STAT_ADV_CAP_1000FDX: 9999860Sgdamore@opensolaris.org *val = ph->phy_adv_1000_fdx; 10009860Sgdamore@opensolaris.org break; 10019860Sgdamore@opensolaris.org case ETHER_STAT_ADV_CAP_1000HDX: 10029860Sgdamore@opensolaris.org *val = ph->phy_adv_1000_hdx; 10039860Sgdamore@opensolaris.org break; 10049860Sgdamore@opensolaris.org case ETHER_STAT_ADV_CAP_100FDX: 10059860Sgdamore@opensolaris.org *val = ph->phy_adv_100_fdx; 10069860Sgdamore@opensolaris.org break; 10079860Sgdamore@opensolaris.org case ETHER_STAT_ADV_CAP_100HDX: 10089860Sgdamore@opensolaris.org *val = ph->phy_adv_100_hdx; 10099860Sgdamore@opensolaris.org break; 10109860Sgdamore@opensolaris.org case ETHER_STAT_ADV_CAP_10FDX: 10119860Sgdamore@opensolaris.org *val = ph->phy_adv_10_fdx; 10129860Sgdamore@opensolaris.org break; 10139860Sgdamore@opensolaris.org case ETHER_STAT_ADV_CAP_10HDX: 10149860Sgdamore@opensolaris.org *val = ph->phy_adv_10_hdx; 10159860Sgdamore@opensolaris.org break; 10169860Sgdamore@opensolaris.org case ETHER_STAT_ADV_CAP_100T4: 10179860Sgdamore@opensolaris.org *val = ph->phy_adv_100_t4; 10189860Sgdamore@opensolaris.org break; 10199860Sgdamore@opensolaris.org case ETHER_STAT_ADV_CAP_AUTONEG: 10209860Sgdamore@opensolaris.org *val = ph->phy_adv_aneg; 10219860Sgdamore@opensolaris.org break; 10229860Sgdamore@opensolaris.org case ETHER_STAT_ADV_CAP_PAUSE: 10239860Sgdamore@opensolaris.org *val = ph->phy_adv_pause; 10249860Sgdamore@opensolaris.org break; 10259860Sgdamore@opensolaris.org case ETHER_STAT_ADV_CAP_ASMPAUSE: 10269860Sgdamore@opensolaris.org *val = ph->phy_adv_asmpause; 10279860Sgdamore@opensolaris.org break; 10289860Sgdamore@opensolaris.org 10299860Sgdamore@opensolaris.org default: 10309860Sgdamore@opensolaris.org rv = ENOTSUP; 10319860Sgdamore@opensolaris.org break; 10329860Sgdamore@opensolaris.org } 10339860Sgdamore@opensolaris.org mutex_exit(&mh->m_lock); 10349860Sgdamore@opensolaris.org 10359860Sgdamore@opensolaris.org return (rv); 10369860Sgdamore@opensolaris.org } 10379860Sgdamore@opensolaris.org 10389860Sgdamore@opensolaris.org /* 10399860Sgdamore@opensolaris.org * PHY support routines. Private to the MII module and the vendor 10409860Sgdamore@opensolaris.org * specific PHY implementation code. 10419860Sgdamore@opensolaris.org */ 10429860Sgdamore@opensolaris.org uint16_t 10439860Sgdamore@opensolaris.org phy_read(phy_handle_t *ph, uint8_t reg) 10449860Sgdamore@opensolaris.org { 10459860Sgdamore@opensolaris.org mii_handle_t mh = ph->phy_mii; 10469860Sgdamore@opensolaris.org 10479860Sgdamore@opensolaris.org return ((*mh->m_ops.mii_read)(mh->m_private, ph->phy_addr, reg)); 10489860Sgdamore@opensolaris.org } 10499860Sgdamore@opensolaris.org 10509860Sgdamore@opensolaris.org void 10519860Sgdamore@opensolaris.org phy_write(phy_handle_t *ph, uint8_t reg, uint16_t val) 10529860Sgdamore@opensolaris.org { 10539860Sgdamore@opensolaris.org mii_handle_t mh = ph->phy_mii; 10549860Sgdamore@opensolaris.org 10559860Sgdamore@opensolaris.org (*mh->m_ops.mii_write)(mh->m_private, ph->phy_addr, reg, val); 10569860Sgdamore@opensolaris.org } 10579860Sgdamore@opensolaris.org 10589860Sgdamore@opensolaris.org int 10599860Sgdamore@opensolaris.org phy_reset(phy_handle_t *ph) 10609860Sgdamore@opensolaris.org { 10619860Sgdamore@opensolaris.org ASSERT(mutex_owned(&ph->phy_mii->m_lock)); 10629860Sgdamore@opensolaris.org 10639860Sgdamore@opensolaris.org /* 106410359SGarrett.Damore@Sun.COM * For our device, make sure its powered up and unisolated. 10659860Sgdamore@opensolaris.org */ 10669860Sgdamore@opensolaris.org PHY_CLR(ph, MII_CONTROL, 106710359SGarrett.Damore@Sun.COM MII_CONTROL_PWRDN | MII_CONTROL_ISOLATE); 10689860Sgdamore@opensolaris.org 10699860Sgdamore@opensolaris.org /* 10709860Sgdamore@opensolaris.org * Finally reset it. 10719860Sgdamore@opensolaris.org */ 10729860Sgdamore@opensolaris.org PHY_SET(ph, MII_CONTROL, MII_CONTROL_RESET); 10739860Sgdamore@opensolaris.org 10749860Sgdamore@opensolaris.org /* 10759860Sgdamore@opensolaris.org * Apparently some devices (DP83840A) like to have a little 10769860Sgdamore@opensolaris.org * bit of a wait before we start accessing anything else on 10779860Sgdamore@opensolaris.org * the PHY. 10789860Sgdamore@opensolaris.org */ 10799860Sgdamore@opensolaris.org drv_usecwait(500); 10809860Sgdamore@opensolaris.org 10819860Sgdamore@opensolaris.org /* 10829860Sgdamore@opensolaris.org * Wait for reset to complete - probably very fast, but no 10839860Sgdamore@opensolaris.org * more than 0.5 sec according to spec. It would be nice if 10849860Sgdamore@opensolaris.org * we could use delay() here, but MAC drivers may call 10859860Sgdamore@opensolaris.org * functions which hold this lock in interrupt context, so 10869860Sgdamore@opensolaris.org * sleeping would be a definite no-no. The good news here is 10879860Sgdamore@opensolaris.org * that it seems to be the case that most devices come back 10889860Sgdamore@opensolaris.org * within only a few hundred usec. 10899860Sgdamore@opensolaris.org */ 10909860Sgdamore@opensolaris.org for (int i = 500000; i; i -= 100) { 10919860Sgdamore@opensolaris.org if ((phy_read(ph, MII_CONTROL) & MII_CONTROL_RESET) == 0) { 10929860Sgdamore@opensolaris.org /* reset completed */ 10939860Sgdamore@opensolaris.org return (DDI_SUCCESS); 10949860Sgdamore@opensolaris.org } 10959860Sgdamore@opensolaris.org drv_usecwait(100); 10969860Sgdamore@opensolaris.org } 10979860Sgdamore@opensolaris.org 10989860Sgdamore@opensolaris.org return (DDI_FAILURE); 10999860Sgdamore@opensolaris.org } 11009860Sgdamore@opensolaris.org 11019860Sgdamore@opensolaris.org int 11029860Sgdamore@opensolaris.org phy_stop(phy_handle_t *ph) 11039860Sgdamore@opensolaris.org { 110410066Sgdamore@opensolaris.org phy_write(ph, MII_CONTROL, MII_CONTROL_ISOLATE); 110510066Sgdamore@opensolaris.org 11069860Sgdamore@opensolaris.org return (DDI_SUCCESS); 11079860Sgdamore@opensolaris.org } 11089860Sgdamore@opensolaris.org 11099860Sgdamore@opensolaris.org int 111010359SGarrett.Damore@Sun.COM phy_loop(phy_handle_t *ph) 11119860Sgdamore@opensolaris.org { 111210359SGarrett.Damore@Sun.COM uint16_t bmcr, gtcr; 111310359SGarrett.Damore@Sun.COM 11149860Sgdamore@opensolaris.org ASSERT(mutex_owned(&ph->phy_mii->m_lock)); 11159860Sgdamore@opensolaris.org 11169860Sgdamore@opensolaris.org /* 11179860Sgdamore@opensolaris.org * Disable everything to start... we'll add in modes as we go. 11189860Sgdamore@opensolaris.org */ 11199860Sgdamore@opensolaris.org ph->phy_adv_aneg = B_FALSE; 11209860Sgdamore@opensolaris.org ph->phy_adv_1000_fdx = B_FALSE; 11219860Sgdamore@opensolaris.org ph->phy_adv_1000_hdx = B_FALSE; 11229860Sgdamore@opensolaris.org ph->phy_adv_100_fdx = B_FALSE; 11239860Sgdamore@opensolaris.org ph->phy_adv_100_t4 = B_FALSE; 11249860Sgdamore@opensolaris.org ph->phy_adv_100_hdx = B_FALSE; 11259860Sgdamore@opensolaris.org ph->phy_adv_10_fdx = B_FALSE; 11269860Sgdamore@opensolaris.org ph->phy_adv_10_hdx = B_FALSE; 11279860Sgdamore@opensolaris.org ph->phy_adv_pause = B_FALSE; 11289860Sgdamore@opensolaris.org ph->phy_adv_asmpause = B_FALSE; 11299860Sgdamore@opensolaris.org 113010359SGarrett.Damore@Sun.COM bmcr = 0; 113110359SGarrett.Damore@Sun.COM gtcr = MII_MSCONTROL_MANUAL | MII_MSCONTROL_MASTER; 113210359SGarrett.Damore@Sun.COM 11339860Sgdamore@opensolaris.org switch (ph->phy_loopback) { 11349860Sgdamore@opensolaris.org case PHY_LB_NONE: 113510359SGarrett.Damore@Sun.COM /* We shouldn't be here */ 113610359SGarrett.Damore@Sun.COM ASSERT(0); 11379860Sgdamore@opensolaris.org break; 11389860Sgdamore@opensolaris.org 11399860Sgdamore@opensolaris.org case PHY_LB_INT_PHY: 114010359SGarrett.Damore@Sun.COM bmcr |= MII_CONTROL_LOOPBACK; 114110359SGarrett.Damore@Sun.COM ph->phy_duplex = LINK_DUPLEX_FULL; 11429860Sgdamore@opensolaris.org if (ph->phy_cap_1000_fdx) { 114310359SGarrett.Damore@Sun.COM bmcr |= MII_CONTROL_1GB | MII_CONTROL_FDUPLEX; 114410359SGarrett.Damore@Sun.COM ph->phy_speed = 1000; 11459860Sgdamore@opensolaris.org } else if (ph->phy_cap_100_fdx) { 114610359SGarrett.Damore@Sun.COM bmcr |= MII_CONTROL_100MB | MII_CONTROL_FDUPLEX; 114710359SGarrett.Damore@Sun.COM ph->phy_speed = 100; 11489860Sgdamore@opensolaris.org } else if (ph->phy_cap_10_fdx) { 114910359SGarrett.Damore@Sun.COM bmcr |= MII_CONTROL_FDUPLEX; 115010359SGarrett.Damore@Sun.COM ph->phy_speed = 10; 11519860Sgdamore@opensolaris.org } 11529860Sgdamore@opensolaris.org break; 11539860Sgdamore@opensolaris.org 11549860Sgdamore@opensolaris.org case PHY_LB_EXT_10: 115510359SGarrett.Damore@Sun.COM bmcr = MII_CONTROL_FDUPLEX; 115610359SGarrett.Damore@Sun.COM ph->phy_speed = 10; 115710359SGarrett.Damore@Sun.COM ph->phy_duplex = LINK_DUPLEX_FULL; 11589860Sgdamore@opensolaris.org break; 11599860Sgdamore@opensolaris.org 11609860Sgdamore@opensolaris.org case PHY_LB_EXT_100: 116110359SGarrett.Damore@Sun.COM bmcr = MII_CONTROL_100MB | MII_CONTROL_FDUPLEX; 116210359SGarrett.Damore@Sun.COM ph->phy_speed = 100; 116310359SGarrett.Damore@Sun.COM ph->phy_duplex = LINK_DUPLEX_FULL; 11649860Sgdamore@opensolaris.org break; 11659860Sgdamore@opensolaris.org 11669860Sgdamore@opensolaris.org case PHY_LB_EXT_1000: 116710359SGarrett.Damore@Sun.COM bmcr = MII_CONTROL_1GB | MII_CONTROL_FDUPLEX; 116810359SGarrett.Damore@Sun.COM ph->phy_speed = 1000; 116910359SGarrett.Damore@Sun.COM ph->phy_duplex = LINK_DUPLEX_FULL; 117010359SGarrett.Damore@Sun.COM break; 117110359SGarrett.Damore@Sun.COM } 117210359SGarrett.Damore@Sun.COM 117310359SGarrett.Damore@Sun.COM ph->phy_link = LINK_STATE_UP; /* force up for loopback */ 117410359SGarrett.Damore@Sun.COM ph->phy_flowctrl = LINK_FLOWCTRL_NONE; 117510359SGarrett.Damore@Sun.COM 117610359SGarrett.Damore@Sun.COM switch (ph->phy_type) { 117710359SGarrett.Damore@Sun.COM case XCVR_1000T: 117810359SGarrett.Damore@Sun.COM case XCVR_1000X: 117910359SGarrett.Damore@Sun.COM case XCVR_100T2: 118010359SGarrett.Damore@Sun.COM phy_write(ph, MII_MSCONTROL, gtcr); 11819860Sgdamore@opensolaris.org break; 11829860Sgdamore@opensolaris.org } 11839860Sgdamore@opensolaris.org 118410359SGarrett.Damore@Sun.COM phy_write(ph, MII_CONTROL, bmcr); 118510359SGarrett.Damore@Sun.COM 118610359SGarrett.Damore@Sun.COM return (DDI_SUCCESS); 118710359SGarrett.Damore@Sun.COM } 118810359SGarrett.Damore@Sun.COM 118910359SGarrett.Damore@Sun.COM int 119010359SGarrett.Damore@Sun.COM phy_start(phy_handle_t *ph) 119110359SGarrett.Damore@Sun.COM { 119210359SGarrett.Damore@Sun.COM uint16_t bmcr, anar, gtcr; 119310359SGarrett.Damore@Sun.COM ASSERT(mutex_owned(&ph->phy_mii->m_lock)); 119410359SGarrett.Damore@Sun.COM 119510359SGarrett.Damore@Sun.COM ASSERT(ph->phy_loopback == PHY_LB_NONE); 119610359SGarrett.Damore@Sun.COM 119710359SGarrett.Damore@Sun.COM /* 119810359SGarrett.Damore@Sun.COM * No loopback overrides, so try to advertise everything 119910359SGarrett.Damore@Sun.COM * that is administratively enabled. 120010359SGarrett.Damore@Sun.COM */ 120110359SGarrett.Damore@Sun.COM ph->phy_adv_aneg = ph->phy_en_aneg; 120210359SGarrett.Damore@Sun.COM ph->phy_adv_1000_fdx = ph->phy_en_1000_fdx; 120310359SGarrett.Damore@Sun.COM ph->phy_adv_1000_hdx = ph->phy_en_1000_hdx; 120410359SGarrett.Damore@Sun.COM ph->phy_adv_100_fdx = ph->phy_en_100_fdx; 120510359SGarrett.Damore@Sun.COM ph->phy_adv_100_t4 = ph->phy_en_100_t4; 120610359SGarrett.Damore@Sun.COM ph->phy_adv_100_hdx = ph->phy_en_100_hdx; 120710359SGarrett.Damore@Sun.COM ph->phy_adv_10_fdx = ph->phy_en_10_fdx; 120810359SGarrett.Damore@Sun.COM ph->phy_adv_10_hdx = ph->phy_en_10_hdx; 120910359SGarrett.Damore@Sun.COM ph->phy_adv_pause = ph->phy_en_pause; 121010359SGarrett.Damore@Sun.COM ph->phy_adv_asmpause = ph->phy_en_asmpause; 121110359SGarrett.Damore@Sun.COM 12129860Sgdamore@opensolaris.org /* 12139860Sgdamore@opensolaris.org * Limit properties to what the hardware can actually support. 12149860Sgdamore@opensolaris.org */ 12159860Sgdamore@opensolaris.org #define FILTER_ADV(CAP) \ 12169860Sgdamore@opensolaris.org if (!ph->phy_cap_##CAP) \ 12179860Sgdamore@opensolaris.org ph->phy_adv_##CAP = 0 12189860Sgdamore@opensolaris.org 12199860Sgdamore@opensolaris.org FILTER_ADV(aneg); 12209860Sgdamore@opensolaris.org FILTER_ADV(1000_fdx); 12219860Sgdamore@opensolaris.org FILTER_ADV(1000_hdx); 12229860Sgdamore@opensolaris.org FILTER_ADV(100_fdx); 12239860Sgdamore@opensolaris.org FILTER_ADV(100_t4); 12249860Sgdamore@opensolaris.org FILTER_ADV(100_hdx); 12259860Sgdamore@opensolaris.org FILTER_ADV(10_fdx); 12269860Sgdamore@opensolaris.org FILTER_ADV(10_hdx); 12279860Sgdamore@opensolaris.org FILTER_ADV(pause); 12289860Sgdamore@opensolaris.org FILTER_ADV(asmpause); 12299860Sgdamore@opensolaris.org 12309860Sgdamore@opensolaris.org #undef FILTER_ADV 12319860Sgdamore@opensolaris.org 12329860Sgdamore@opensolaris.org /* 12339860Sgdamore@opensolaris.org * We need at least one valid mode. 12349860Sgdamore@opensolaris.org */ 12359860Sgdamore@opensolaris.org if ((!ph->phy_adv_1000_fdx) && 12369860Sgdamore@opensolaris.org (!ph->phy_adv_1000_hdx) && 12379860Sgdamore@opensolaris.org (!ph->phy_adv_100_t4) && 12389860Sgdamore@opensolaris.org (!ph->phy_adv_100_fdx) && 12399860Sgdamore@opensolaris.org (!ph->phy_adv_100_hdx) && 12409860Sgdamore@opensolaris.org (!ph->phy_adv_10_fdx) && 12419860Sgdamore@opensolaris.org (!ph->phy_adv_10_hdx)) { 12429860Sgdamore@opensolaris.org 12439860Sgdamore@opensolaris.org phy_warn(ph, 12449860Sgdamore@opensolaris.org "No valid link mode selected. Powering down PHY."); 12459860Sgdamore@opensolaris.org 12469860Sgdamore@opensolaris.org PHY_SET(ph, MII_CONTROL, MII_CONTROL_PWRDN); 12479860Sgdamore@opensolaris.org 12489860Sgdamore@opensolaris.org ph->phy_link = LINK_STATE_DOWN; 12499860Sgdamore@opensolaris.org return (DDI_SUCCESS); 12509860Sgdamore@opensolaris.org } 12519860Sgdamore@opensolaris.org 125210359SGarrett.Damore@Sun.COM bmcr = 0; 125310359SGarrett.Damore@Sun.COM gtcr = 0; 12549860Sgdamore@opensolaris.org 12559860Sgdamore@opensolaris.org if (ph->phy_adv_aneg) { 12569860Sgdamore@opensolaris.org bmcr |= MII_CONTROL_ANE | MII_CONTROL_RSAN; 12579860Sgdamore@opensolaris.org } 12589860Sgdamore@opensolaris.org 12599860Sgdamore@opensolaris.org if ((ph->phy_adv_1000_fdx) || (ph->phy_adv_1000_hdx)) { 12609860Sgdamore@opensolaris.org bmcr |= MII_CONTROL_1GB; 12619860Sgdamore@opensolaris.org 12629860Sgdamore@opensolaris.org } else if (ph->phy_adv_100_fdx || ph->phy_adv_100_hdx || 12639860Sgdamore@opensolaris.org ph->phy_adv_100_t4) { 12649860Sgdamore@opensolaris.org bmcr |= MII_CONTROL_100MB; 12659860Sgdamore@opensolaris.org } 12669860Sgdamore@opensolaris.org 12679860Sgdamore@opensolaris.org if (ph->phy_adv_1000_fdx || ph->phy_adv_100_fdx || ph->phy_adv_10_fdx) { 12689860Sgdamore@opensolaris.org bmcr |= MII_CONTROL_FDUPLEX; 12699860Sgdamore@opensolaris.org } 12709860Sgdamore@opensolaris.org 12719860Sgdamore@opensolaris.org if (ph->phy_type == XCVR_1000X) { 12729860Sgdamore@opensolaris.org /* 1000BASE-X (usually fiber) */ 12739860Sgdamore@opensolaris.org anar = 0; 12749860Sgdamore@opensolaris.org if (ph->phy_adv_1000_fdx) { 12759860Sgdamore@opensolaris.org anar |= MII_ABILITY_X_FD; 12769860Sgdamore@opensolaris.org } 12779860Sgdamore@opensolaris.org if (ph->phy_adv_1000_hdx) { 12789860Sgdamore@opensolaris.org anar |= MII_ABILITY_X_HD; 12799860Sgdamore@opensolaris.org } 12809860Sgdamore@opensolaris.org if (ph->phy_adv_pause) { 12819860Sgdamore@opensolaris.org anar |= MII_ABILITY_X_PAUSE; 12829860Sgdamore@opensolaris.org } 12839860Sgdamore@opensolaris.org if (ph->phy_adv_asmpause) { 12849860Sgdamore@opensolaris.org anar |= MII_ABILITY_X_ASMPAUSE; 12859860Sgdamore@opensolaris.org } 12869860Sgdamore@opensolaris.org 12879860Sgdamore@opensolaris.org } else if (ph->phy_type == XCVR_100T2) { 12889860Sgdamore@opensolaris.org /* 100BASE-T2 */ 12899860Sgdamore@opensolaris.org anar = 0; 12909860Sgdamore@opensolaris.org if (ph->phy_adv_100_fdx) { 12919860Sgdamore@opensolaris.org anar |= MII_ABILITY_T2_FD; 12929860Sgdamore@opensolaris.org } 12939860Sgdamore@opensolaris.org if (ph->phy_adv_100_hdx) { 12949860Sgdamore@opensolaris.org anar |= MII_ABILITY_T2_HD; 12959860Sgdamore@opensolaris.org } 12969860Sgdamore@opensolaris.org 12979860Sgdamore@opensolaris.org } else { 12989860Sgdamore@opensolaris.org anar = MII_AN_SELECTOR_8023; 12999860Sgdamore@opensolaris.org 13009860Sgdamore@opensolaris.org /* 1000BASE-T or 100BASE-X probably */ 13019860Sgdamore@opensolaris.org if (ph->phy_adv_1000_fdx) { 13029860Sgdamore@opensolaris.org gtcr |= MII_MSCONTROL_1000T_FD; 13039860Sgdamore@opensolaris.org } 13049860Sgdamore@opensolaris.org if (ph->phy_adv_1000_hdx) { 13059860Sgdamore@opensolaris.org gtcr |= MII_MSCONTROL_1000T; 13069860Sgdamore@opensolaris.org } 13079860Sgdamore@opensolaris.org if (ph->phy_adv_100_fdx) { 13089860Sgdamore@opensolaris.org anar |= MII_ABILITY_100BASE_TX_FD; 13099860Sgdamore@opensolaris.org } 13109860Sgdamore@opensolaris.org if (ph->phy_adv_100_hdx) { 13119860Sgdamore@opensolaris.org anar |= MII_ABILITY_100BASE_TX; 13129860Sgdamore@opensolaris.org } 13139860Sgdamore@opensolaris.org if (ph->phy_adv_100_t4) { 13149860Sgdamore@opensolaris.org anar |= MII_ABILITY_100BASE_T4; 13159860Sgdamore@opensolaris.org } 13169860Sgdamore@opensolaris.org if (ph->phy_adv_10_fdx) { 13179860Sgdamore@opensolaris.org anar |= MII_ABILITY_10BASE_T_FD; 13189860Sgdamore@opensolaris.org } 131911414Sgdamore@opensolaris.org if (ph->phy_adv_10_hdx) { 13209860Sgdamore@opensolaris.org anar |= MII_ABILITY_10BASE_T; 13219860Sgdamore@opensolaris.org } 13229860Sgdamore@opensolaris.org if (ph->phy_adv_pause) { 13239860Sgdamore@opensolaris.org anar |= MII_ABILITY_PAUSE; 13249860Sgdamore@opensolaris.org } 13259860Sgdamore@opensolaris.org if (ph->phy_adv_asmpause) { 13269860Sgdamore@opensolaris.org anar |= MII_ABILITY_ASMPAUSE; 13279860Sgdamore@opensolaris.org } 13289860Sgdamore@opensolaris.org } 13299860Sgdamore@opensolaris.org 13309860Sgdamore@opensolaris.org ph->phy_link = LINK_STATE_DOWN; 13319860Sgdamore@opensolaris.org ph->phy_duplex = LINK_DUPLEX_UNKNOWN; 13329860Sgdamore@opensolaris.org ph->phy_speed = 0; 13339860Sgdamore@opensolaris.org 13349860Sgdamore@opensolaris.org phy_write(ph, MII_AN_ADVERT, anar); 133510291Sgdamore@opensolaris.org phy_write(ph, MII_CONTROL, bmcr & ~(MII_CONTROL_RSAN)); 133610291Sgdamore@opensolaris.org 13379860Sgdamore@opensolaris.org switch (ph->phy_type) { 13389860Sgdamore@opensolaris.org case XCVR_1000T: 13399860Sgdamore@opensolaris.org case XCVR_1000X: 13409860Sgdamore@opensolaris.org case XCVR_100T2: 13419860Sgdamore@opensolaris.org phy_write(ph, MII_MSCONTROL, gtcr); 13429860Sgdamore@opensolaris.org } 13439860Sgdamore@opensolaris.org 13449860Sgdamore@opensolaris.org /* 13459860Sgdamore@opensolaris.org * Finally, this will start up autoneg if it is enabled, or 13469860Sgdamore@opensolaris.org * force link settings otherwise. 13479860Sgdamore@opensolaris.org */ 13489860Sgdamore@opensolaris.org phy_write(ph, MII_CONTROL, bmcr); 13499860Sgdamore@opensolaris.org 13509860Sgdamore@opensolaris.org return (DDI_SUCCESS); 13519860Sgdamore@opensolaris.org } 13529860Sgdamore@opensolaris.org 13539860Sgdamore@opensolaris.org 13549860Sgdamore@opensolaris.org int 13559860Sgdamore@opensolaris.org phy_check(phy_handle_t *ph) 13569860Sgdamore@opensolaris.org { 13579860Sgdamore@opensolaris.org uint16_t control, status, lpar, msstat, anexp; 13589860Sgdamore@opensolaris.org int debounces = 100; 13599860Sgdamore@opensolaris.org 13609860Sgdamore@opensolaris.org ASSERT(mutex_owned(&ph->phy_mii->m_lock)); 13619860Sgdamore@opensolaris.org 13629860Sgdamore@opensolaris.org debounce: 13639860Sgdamore@opensolaris.org status = phy_read(ph, MII_STATUS); 13649860Sgdamore@opensolaris.org control = phy_read(ph, MII_CONTROL); 13659860Sgdamore@opensolaris.org 13669860Sgdamore@opensolaris.org if (status & MII_STATUS_EXTENDED) { 13679860Sgdamore@opensolaris.org lpar = phy_read(ph, MII_AN_LPABLE); 13689860Sgdamore@opensolaris.org anexp = phy_read(ph, MII_AN_EXPANSION); 13699860Sgdamore@opensolaris.org } else { 13709860Sgdamore@opensolaris.org lpar = 0; 13719860Sgdamore@opensolaris.org anexp = 0; 13729860Sgdamore@opensolaris.org } 13739860Sgdamore@opensolaris.org 13749860Sgdamore@opensolaris.org /* 13759860Sgdamore@opensolaris.org * We reread to clear any latched bits. This also debounces 13769860Sgdamore@opensolaris.org * any state that might be in transition. 13779860Sgdamore@opensolaris.org */ 13789860Sgdamore@opensolaris.org drv_usecwait(10); 13799860Sgdamore@opensolaris.org if ((status != phy_read(ph, MII_STATUS)) && debounces) { 13809860Sgdamore@opensolaris.org debounces--; 13819860Sgdamore@opensolaris.org goto debounce; 13829860Sgdamore@opensolaris.org } 13839860Sgdamore@opensolaris.org 13849860Sgdamore@opensolaris.org /* 13859860Sgdamore@opensolaris.org * Detect the situation where the PHY is removed or has died. 13869860Sgdamore@opensolaris.org * According to spec, at least one bit of status must be set, 13879860Sgdamore@opensolaris.org * and at least one bit must be clear. 13889860Sgdamore@opensolaris.org */ 13899860Sgdamore@opensolaris.org if ((status == 0xffff) || (status == 0)) { 13909860Sgdamore@opensolaris.org ph->phy_speed = 0; 13919860Sgdamore@opensolaris.org ph->phy_duplex = LINK_DUPLEX_UNKNOWN; 13929860Sgdamore@opensolaris.org ph->phy_link = LINK_STATE_UNKNOWN; 13939860Sgdamore@opensolaris.org ph->phy_present = B_FALSE; 13949860Sgdamore@opensolaris.org return (DDI_FAILURE); 13959860Sgdamore@opensolaris.org } 13969860Sgdamore@opensolaris.org 13979860Sgdamore@opensolaris.org /* We only respect the link flag if we are not in loopback. */ 13989860Sgdamore@opensolaris.org if ((ph->phy_loopback != PHY_LB_INT_PHY) && 13999860Sgdamore@opensolaris.org ((status & MII_STATUS_LINKUP) == 0)) { 14009860Sgdamore@opensolaris.org ph->phy_speed = 0; 14019860Sgdamore@opensolaris.org ph->phy_duplex = LINK_DUPLEX_UNKNOWN; 14029860Sgdamore@opensolaris.org ph->phy_link = LINK_STATE_DOWN; 14039860Sgdamore@opensolaris.org return (DDI_SUCCESS); 14049860Sgdamore@opensolaris.org } 14059860Sgdamore@opensolaris.org 14069860Sgdamore@opensolaris.org ph->phy_link = LINK_STATE_UP; 14079860Sgdamore@opensolaris.org 14089860Sgdamore@opensolaris.org if ((control & MII_CONTROL_ANE) == 0) { 14099860Sgdamore@opensolaris.org 14109860Sgdamore@opensolaris.org ph->phy_lp_aneg = B_FALSE; 14119860Sgdamore@opensolaris.org ph->phy_lp_10_hdx = B_FALSE; 14129860Sgdamore@opensolaris.org ph->phy_lp_10_fdx = B_FALSE; 14139860Sgdamore@opensolaris.org ph->phy_lp_100_t4 = B_FALSE; 14149860Sgdamore@opensolaris.org ph->phy_lp_100_hdx = B_FALSE; 14159860Sgdamore@opensolaris.org ph->phy_lp_100_fdx = B_FALSE; 14169860Sgdamore@opensolaris.org ph->phy_lp_1000_hdx = B_FALSE; 14179860Sgdamore@opensolaris.org ph->phy_lp_1000_fdx = B_FALSE; 14189860Sgdamore@opensolaris.org 14199860Sgdamore@opensolaris.org /* 14209860Sgdamore@opensolaris.org * We have no idea what our link partner might or might 14219860Sgdamore@opensolaris.org * not be able to support, except that it appears to 14229860Sgdamore@opensolaris.org * support the same mode that we have forced. 14239860Sgdamore@opensolaris.org */ 14249860Sgdamore@opensolaris.org if (control & MII_CONTROL_1GB) { 14259860Sgdamore@opensolaris.org ph->phy_speed = 1000; 14269860Sgdamore@opensolaris.org } else if (control & MII_CONTROL_100MB) { 14279860Sgdamore@opensolaris.org ph->phy_speed = 100; 14289860Sgdamore@opensolaris.org } else { 14299860Sgdamore@opensolaris.org ph->phy_speed = 10; 14309860Sgdamore@opensolaris.org } 14319860Sgdamore@opensolaris.org ph->phy_duplex = control & MII_CONTROL_FDUPLEX ? 14329860Sgdamore@opensolaris.org LINK_DUPLEX_FULL : LINK_DUPLEX_HALF; 14339860Sgdamore@opensolaris.org 14349860Sgdamore@opensolaris.org return (DDI_SUCCESS); 14359860Sgdamore@opensolaris.org } 14369860Sgdamore@opensolaris.org 14379860Sgdamore@opensolaris.org if (ph->phy_type == XCVR_1000X) { 14389860Sgdamore@opensolaris.org 14399860Sgdamore@opensolaris.org ph->phy_lp_10_hdx = B_FALSE; 14409860Sgdamore@opensolaris.org ph->phy_lp_10_fdx = B_FALSE; 14419860Sgdamore@opensolaris.org ph->phy_lp_100_t4 = B_FALSE; 14429860Sgdamore@opensolaris.org ph->phy_lp_100_hdx = B_FALSE; 14439860Sgdamore@opensolaris.org ph->phy_lp_100_fdx = B_FALSE; 14449860Sgdamore@opensolaris.org 14459860Sgdamore@opensolaris.org /* 1000BASE-X requires autonegotiation */ 14469860Sgdamore@opensolaris.org ph->phy_lp_aneg = B_TRUE; 14479860Sgdamore@opensolaris.org ph->phy_lp_1000_fdx = !!(lpar & MII_ABILITY_X_FD); 14489860Sgdamore@opensolaris.org ph->phy_lp_1000_hdx = !!(lpar & MII_ABILITY_X_HD); 14499860Sgdamore@opensolaris.org ph->phy_lp_pause = !!(lpar & MII_ABILITY_X_PAUSE); 14509860Sgdamore@opensolaris.org ph->phy_lp_asmpause = !!(lpar & MII_ABILITY_X_ASMPAUSE); 14519860Sgdamore@opensolaris.org 14529860Sgdamore@opensolaris.org } else if (ph->phy_type == XCVR_100T2) { 14539860Sgdamore@opensolaris.org ph->phy_lp_10_hdx = B_FALSE; 14549860Sgdamore@opensolaris.org ph->phy_lp_10_fdx = B_FALSE; 14559860Sgdamore@opensolaris.org ph->phy_lp_100_t4 = B_FALSE; 14569860Sgdamore@opensolaris.org ph->phy_lp_1000_hdx = B_FALSE; 14579860Sgdamore@opensolaris.org ph->phy_lp_1000_fdx = B_FALSE; 14589860Sgdamore@opensolaris.org ph->phy_lp_pause = B_FALSE; 14599860Sgdamore@opensolaris.org ph->phy_lp_asmpause = B_FALSE; 14609860Sgdamore@opensolaris.org 14619860Sgdamore@opensolaris.org /* 100BASE-T2 requires autonegotiation */ 14629860Sgdamore@opensolaris.org ph->phy_lp_aneg = B_TRUE; 14639860Sgdamore@opensolaris.org ph->phy_lp_100_fdx = !!(lpar & MII_ABILITY_T2_FD); 14649860Sgdamore@opensolaris.org ph->phy_lp_100_hdx = !!(lpar & MII_ABILITY_T2_HD); 14659860Sgdamore@opensolaris.org 14669860Sgdamore@opensolaris.org } else if (anexp & MII_AN_EXP_PARFAULT) { 14679860Sgdamore@opensolaris.org /* 14689860Sgdamore@opensolaris.org * Parallel detection fault! This happens when the 14699860Sgdamore@opensolaris.org * peer does not use autonegotiation, and the 14709860Sgdamore@opensolaris.org * detection logic reports more than one type of legal 14719860Sgdamore@opensolaris.org * link is available. Note that parallel detection 14729860Sgdamore@opensolaris.org * can only happen with half duplex 10, 100, and 14739860Sgdamore@opensolaris.org * 100TX4. We also should not have got here, because 14749860Sgdamore@opensolaris.org * the link state bit should have failed. 14759860Sgdamore@opensolaris.org */ 14769860Sgdamore@opensolaris.org #ifdef DEBUG 14779860Sgdamore@opensolaris.org phy_warn(ph, "Parallel detection fault!"); 14789860Sgdamore@opensolaris.org #endif 14799860Sgdamore@opensolaris.org ph->phy_lp_10_hdx = B_FALSE; 14809860Sgdamore@opensolaris.org ph->phy_lp_10_fdx = B_FALSE; 14819860Sgdamore@opensolaris.org ph->phy_lp_100_t4 = B_FALSE; 14829860Sgdamore@opensolaris.org ph->phy_lp_100_hdx = B_FALSE; 14839860Sgdamore@opensolaris.org ph->phy_lp_100_fdx = B_FALSE; 14849860Sgdamore@opensolaris.org ph->phy_lp_1000_hdx = B_FALSE; 14859860Sgdamore@opensolaris.org ph->phy_lp_1000_fdx = B_FALSE; 14869860Sgdamore@opensolaris.org ph->phy_lp_pause = B_FALSE; 14879860Sgdamore@opensolaris.org ph->phy_lp_asmpause = B_FALSE; 14889860Sgdamore@opensolaris.org ph->phy_speed = 0; 14899860Sgdamore@opensolaris.org ph->phy_duplex = LINK_DUPLEX_UNKNOWN; 14909860Sgdamore@opensolaris.org return (DDI_SUCCESS); 14919860Sgdamore@opensolaris.org 14929860Sgdamore@opensolaris.org } else { 14939860Sgdamore@opensolaris.org ph->phy_lp_aneg = !!(anexp & MII_AN_EXP_LPCANAN); 14949860Sgdamore@opensolaris.org 14959860Sgdamore@opensolaris.org /* 14969860Sgdamore@opensolaris.org * Note: If the peer doesn't support autonegotiation, then 14979860Sgdamore@opensolaris.org * according to clause 28.5.4.5, the link partner ability 14989860Sgdamore@opensolaris.org * register will still have the right bits set. However, 14999860Sgdamore@opensolaris.org * gigabit modes cannot use legacy parallel detection. 15009860Sgdamore@opensolaris.org */ 15019860Sgdamore@opensolaris.org 15029860Sgdamore@opensolaris.org if ((ph->phy_type == XCVR_1000T) & 15039860Sgdamore@opensolaris.org (anexp & MII_AN_EXP_LPCANAN)) { 15049860Sgdamore@opensolaris.org 15059860Sgdamore@opensolaris.org /* check for gige */ 15069860Sgdamore@opensolaris.org msstat = phy_read(ph, MII_MSSTATUS); 15079860Sgdamore@opensolaris.org 15089860Sgdamore@opensolaris.org ph->phy_lp_1000_hdx = 15099860Sgdamore@opensolaris.org !!(msstat & MII_MSSTATUS_LP1000T); 15109860Sgdamore@opensolaris.org 15119860Sgdamore@opensolaris.org ph->phy_lp_1000_fdx = 15129860Sgdamore@opensolaris.org !!(msstat & MII_MSSTATUS_LP1000T_FD); 15139860Sgdamore@opensolaris.org } 15149860Sgdamore@opensolaris.org 15159860Sgdamore@opensolaris.org ph->phy_lp_100_fdx = !!(lpar & MII_ABILITY_100BASE_TX_FD); 15169860Sgdamore@opensolaris.org ph->phy_lp_100_hdx = !!(lpar & MII_ABILITY_100BASE_TX); 15179860Sgdamore@opensolaris.org ph->phy_lp_100_t4 = !!(lpar & MII_ABILITY_100BASE_T4); 15189860Sgdamore@opensolaris.org ph->phy_lp_10_fdx = !!(lpar & MII_ABILITY_10BASE_T_FD); 15199860Sgdamore@opensolaris.org ph->phy_lp_10_hdx = !!(lpar & MII_ABILITY_10BASE_T); 15209860Sgdamore@opensolaris.org ph->phy_lp_pause = !!(lpar & MII_ABILITY_PAUSE); 15219860Sgdamore@opensolaris.org ph->phy_lp_asmpause = !!(lpar & MII_ABILITY_ASMPAUSE); 15229860Sgdamore@opensolaris.org } 15239860Sgdamore@opensolaris.org 15249860Sgdamore@opensolaris.org /* resolve link pause */ 15259860Sgdamore@opensolaris.org if ((ph->phy_en_flowctrl == LINK_FLOWCTRL_BI) && 15269860Sgdamore@opensolaris.org (ph->phy_lp_pause)) { 15279860Sgdamore@opensolaris.org ph->phy_flowctrl = LINK_FLOWCTRL_BI; 15289860Sgdamore@opensolaris.org } else if ((ph->phy_en_flowctrl == LINK_FLOWCTRL_RX) && 15299860Sgdamore@opensolaris.org (ph->phy_lp_pause || ph->phy_lp_asmpause)) { 15309860Sgdamore@opensolaris.org ph->phy_flowctrl = LINK_FLOWCTRL_RX; 15319860Sgdamore@opensolaris.org } else if ((ph->phy_en_flowctrl == LINK_FLOWCTRL_TX) && 15329860Sgdamore@opensolaris.org (ph->phy_lp_pause)) { 15339860Sgdamore@opensolaris.org ph->phy_flowctrl = LINK_FLOWCTRL_TX; 15349860Sgdamore@opensolaris.org } else { 15359860Sgdamore@opensolaris.org ph->phy_flowctrl = LINK_FLOWCTRL_NONE; 15369860Sgdamore@opensolaris.org } 15379860Sgdamore@opensolaris.org 15389860Sgdamore@opensolaris.org if (ph->phy_adv_1000_fdx && ph->phy_lp_1000_fdx) { 15399860Sgdamore@opensolaris.org ph->phy_speed = 1000; 15409860Sgdamore@opensolaris.org ph->phy_duplex = LINK_DUPLEX_FULL; 15419860Sgdamore@opensolaris.org 15429860Sgdamore@opensolaris.org } else if (ph->phy_adv_1000_hdx && ph->phy_lp_1000_hdx) { 15439860Sgdamore@opensolaris.org ph->phy_speed = 1000; 15449860Sgdamore@opensolaris.org ph->phy_duplex = LINK_DUPLEX_HALF; 15459860Sgdamore@opensolaris.org 15469860Sgdamore@opensolaris.org } else if (ph->phy_adv_100_fdx && ph->phy_lp_100_fdx) { 15479860Sgdamore@opensolaris.org ph->phy_speed = 100; 15489860Sgdamore@opensolaris.org ph->phy_duplex = LINK_DUPLEX_FULL; 15499860Sgdamore@opensolaris.org 15509860Sgdamore@opensolaris.org } else if (ph->phy_adv_100_t4 && ph->phy_lp_100_t4) { 15519860Sgdamore@opensolaris.org ph->phy_speed = 100; 15529860Sgdamore@opensolaris.org ph->phy_duplex = LINK_DUPLEX_HALF; 15539860Sgdamore@opensolaris.org 15549860Sgdamore@opensolaris.org } else if (ph->phy_adv_100_hdx && ph->phy_lp_100_hdx) { 15559860Sgdamore@opensolaris.org ph->phy_speed = 100; 15569860Sgdamore@opensolaris.org ph->phy_duplex = LINK_DUPLEX_HALF; 15579860Sgdamore@opensolaris.org 15589860Sgdamore@opensolaris.org } else if (ph->phy_adv_10_fdx && ph->phy_lp_10_fdx) { 15599860Sgdamore@opensolaris.org ph->phy_speed = 10; 15609860Sgdamore@opensolaris.org ph->phy_duplex = LINK_DUPLEX_FULL; 15619860Sgdamore@opensolaris.org 15629860Sgdamore@opensolaris.org } else if (ph->phy_adv_10_hdx && ph->phy_lp_10_hdx) { 15639860Sgdamore@opensolaris.org ph->phy_speed = 10; 15649860Sgdamore@opensolaris.org ph->phy_duplex = LINK_DUPLEX_HALF; 15659860Sgdamore@opensolaris.org 15669860Sgdamore@opensolaris.org } else { 15679860Sgdamore@opensolaris.org #ifdef DEBUG 15689860Sgdamore@opensolaris.org phy_warn(ph, "No common abilities."); 15699860Sgdamore@opensolaris.org #endif 15709860Sgdamore@opensolaris.org ph->phy_speed = 0; 15719860Sgdamore@opensolaris.org ph->phy_duplex = LINK_DUPLEX_UNKNOWN; 15729860Sgdamore@opensolaris.org } 15739860Sgdamore@opensolaris.org 15749860Sgdamore@opensolaris.org return (DDI_SUCCESS); 15759860Sgdamore@opensolaris.org } 15769860Sgdamore@opensolaris.org 15779860Sgdamore@opensolaris.org int 15789860Sgdamore@opensolaris.org phy_get_prop(phy_handle_t *ph, char *prop, int dflt) 15799860Sgdamore@opensolaris.org { 15809860Sgdamore@opensolaris.org mii_handle_t mh = ph->phy_mii; 15819860Sgdamore@opensolaris.org 15829860Sgdamore@opensolaris.org return (ddi_prop_get_int(DDI_DEV_T_ANY, mh->m_dip, 0, prop, dflt)); 15839860Sgdamore@opensolaris.org } 15849860Sgdamore@opensolaris.org 15859860Sgdamore@opensolaris.org const char * 15869860Sgdamore@opensolaris.org phy_get_name(phy_handle_t *ph) 15879860Sgdamore@opensolaris.org { 15889860Sgdamore@opensolaris.org mii_handle_t mh = ph->phy_mii; 15899860Sgdamore@opensolaris.org 15909860Sgdamore@opensolaris.org return (mh->m_name); 15919860Sgdamore@opensolaris.org } 15929860Sgdamore@opensolaris.org 15939860Sgdamore@opensolaris.org const char * 15949860Sgdamore@opensolaris.org phy_get_driver(phy_handle_t *ph) 15959860Sgdamore@opensolaris.org { 15969860Sgdamore@opensolaris.org mii_handle_t mh = ph->phy_mii; 15979860Sgdamore@opensolaris.org 15989860Sgdamore@opensolaris.org return (ddi_driver_name(mh->m_dip)); 15999860Sgdamore@opensolaris.org } 16009860Sgdamore@opensolaris.org 16019860Sgdamore@opensolaris.org void 16029860Sgdamore@opensolaris.org phy_warn(phy_handle_t *ph, const char *fmt, ...) 16039860Sgdamore@opensolaris.org { 16049860Sgdamore@opensolaris.org va_list va; 16059860Sgdamore@opensolaris.org char buf[256]; 16069860Sgdamore@opensolaris.org 16079860Sgdamore@opensolaris.org (void) snprintf(buf, sizeof (buf), "%s: %s", phy_get_name(ph), fmt); 16089860Sgdamore@opensolaris.org 16099860Sgdamore@opensolaris.org va_start(va, fmt); 16109860Sgdamore@opensolaris.org vcmn_err(CE_WARN, buf, va); 16119860Sgdamore@opensolaris.org va_end(va); 16129860Sgdamore@opensolaris.org } 16139860Sgdamore@opensolaris.org 16149860Sgdamore@opensolaris.org /* 16159860Sgdamore@opensolaris.org * Internal support routines. 16169860Sgdamore@opensolaris.org */ 16179860Sgdamore@opensolaris.org 16189860Sgdamore@opensolaris.org void 161910359SGarrett.Damore@Sun.COM _mii_notify(mii_handle_t mh) 162010359SGarrett.Damore@Sun.COM { 162110359SGarrett.Damore@Sun.COM if (mh->m_ops.mii_notify != NULL) { 162210359SGarrett.Damore@Sun.COM mh->m_ops.mii_notify(mh->m_private, mh->m_link); 162310359SGarrett.Damore@Sun.COM } 162410359SGarrett.Damore@Sun.COM } 162510359SGarrett.Damore@Sun.COM 162610359SGarrett.Damore@Sun.COM void 16279860Sgdamore@opensolaris.org _mii_probe_phy(phy_handle_t *ph) 16289860Sgdamore@opensolaris.org { 16299860Sgdamore@opensolaris.org uint16_t bmsr; 16309860Sgdamore@opensolaris.org uint16_t extsr; 16319860Sgdamore@opensolaris.org mii_handle_t mh = ph->phy_mii; 16329860Sgdamore@opensolaris.org 16339860Sgdamore@opensolaris.org 16349860Sgdamore@opensolaris.org /* 16359860Sgdamore@opensolaris.org * Apparently, PHY 0 is less likely to be physically 16369860Sgdamore@opensolaris.org * connected, and should always be the last one tried. Most 16379860Sgdamore@opensolaris.org * single solution NICs use PHY1 for their built-in 16389860Sgdamore@opensolaris.org * transceiver. NICs with an external MII will often place 16399860Sgdamore@opensolaris.org * the external PHY at address 1, and use address 0 for the 16409860Sgdamore@opensolaris.org * internal PHY. 16419860Sgdamore@opensolaris.org */ 16429860Sgdamore@opensolaris.org 16439860Sgdamore@opensolaris.org ph->phy_id = 0; 16449860Sgdamore@opensolaris.org ph->phy_model = "PHY"; 16459860Sgdamore@opensolaris.org ph->phy_vendor = "Unknown Vendor"; 16469860Sgdamore@opensolaris.org 16479860Sgdamore@opensolaris.org /* done twice to clear any latched bits */ 16489860Sgdamore@opensolaris.org bmsr = phy_read(ph, MII_STATUS); 16499860Sgdamore@opensolaris.org bmsr = phy_read(ph, MII_STATUS); 16509860Sgdamore@opensolaris.org if ((bmsr == 0) || (bmsr == 0xffff)) { 16519860Sgdamore@opensolaris.org ph->phy_present = B_FALSE; 16529860Sgdamore@opensolaris.org return; 16539860Sgdamore@opensolaris.org } 16549860Sgdamore@opensolaris.org 16559860Sgdamore@opensolaris.org if (bmsr & MII_STATUS_EXTSTAT) { 16569860Sgdamore@opensolaris.org extsr = phy_read(ph, MII_EXTSTATUS); 16579860Sgdamore@opensolaris.org } else { 16589860Sgdamore@opensolaris.org extsr = 0; 16599860Sgdamore@opensolaris.org } 16609860Sgdamore@opensolaris.org 16619860Sgdamore@opensolaris.org ph->phy_present = B_TRUE; 16629860Sgdamore@opensolaris.org ph->phy_id = ((uint32_t)phy_read(ph, MII_PHYIDH) << 16) | 16639860Sgdamore@opensolaris.org phy_read(ph, MII_PHYIDL); 16649860Sgdamore@opensolaris.org 16659860Sgdamore@opensolaris.org /* setup default handlers */ 16669860Sgdamore@opensolaris.org ph->phy_reset = phy_reset; 16679860Sgdamore@opensolaris.org ph->phy_start = phy_start; 16689860Sgdamore@opensolaris.org ph->phy_stop = phy_stop; 16699860Sgdamore@opensolaris.org ph->phy_check = phy_check; 167010359SGarrett.Damore@Sun.COM ph->phy_loop = phy_loop; 16719860Sgdamore@opensolaris.org 16729860Sgdamore@opensolaris.org /* 16739860Sgdamore@opensolaris.org * We ignore the non-existent 100baseT2 stuff -- no 16749860Sgdamore@opensolaris.org * known products for it exist. 16759860Sgdamore@opensolaris.org */ 16769860Sgdamore@opensolaris.org ph->phy_cap_aneg = !!(bmsr & MII_STATUS_CANAUTONEG); 16779860Sgdamore@opensolaris.org ph->phy_cap_100_t4 = !!(bmsr & MII_STATUS_100_BASE_T4); 16789860Sgdamore@opensolaris.org ph->phy_cap_100_fdx = !!(bmsr & MII_STATUS_100_BASEX_FD); 16799860Sgdamore@opensolaris.org ph->phy_cap_100_hdx = !!(bmsr & MII_STATUS_100_BASEX); 16809860Sgdamore@opensolaris.org ph->phy_cap_10_fdx = !!(bmsr & MII_STATUS_10_FD); 16819860Sgdamore@opensolaris.org ph->phy_cap_10_hdx = !!(bmsr & MII_STATUS_10); 16829860Sgdamore@opensolaris.org ph->phy_cap_1000_fdx = 16839860Sgdamore@opensolaris.org !!(extsr & (MII_EXTSTATUS_1000X_FD|MII_EXTSTATUS_1000T_FD)); 16849860Sgdamore@opensolaris.org ph->phy_cap_1000_hdx = 16859860Sgdamore@opensolaris.org !!(extsr & (MII_EXTSTATUS_1000X | MII_EXTSTATUS_1000T)); 16869860Sgdamore@opensolaris.org ph->phy_cap_pause = mh->m_cap_pause; 16879860Sgdamore@opensolaris.org ph->phy_cap_asmpause = mh->m_cap_asmpause; 16889860Sgdamore@opensolaris.org 16899860Sgdamore@opensolaris.org if (bmsr & MII_STATUS_10) { 16909860Sgdamore@opensolaris.org ph->phy_cap_10_hdx = B_TRUE; 16919860Sgdamore@opensolaris.org ph->phy_type = XCVR_10; 16929860Sgdamore@opensolaris.org } 16939860Sgdamore@opensolaris.org if (bmsr & MII_STATUS_10_FD) { 16949860Sgdamore@opensolaris.org ph->phy_cap_10_fdx = B_TRUE; 16959860Sgdamore@opensolaris.org ph->phy_type = XCVR_10; 16969860Sgdamore@opensolaris.org } 16979860Sgdamore@opensolaris.org if (bmsr & MII_STATUS_100T2) { 16989860Sgdamore@opensolaris.org ph->phy_cap_100_hdx = B_TRUE; 16999860Sgdamore@opensolaris.org ph->phy_type = XCVR_100T2; 17009860Sgdamore@opensolaris.org } 17019860Sgdamore@opensolaris.org if (bmsr & MII_STATUS_100T2_FD) { 17029860Sgdamore@opensolaris.org ph->phy_cap_100_fdx = B_TRUE; 17039860Sgdamore@opensolaris.org ph->phy_type = XCVR_100T2; 17049860Sgdamore@opensolaris.org } 17059860Sgdamore@opensolaris.org if (bmsr & MII_STATUS_100_BASE_T4) { 17069860Sgdamore@opensolaris.org ph->phy_cap_100_hdx = B_TRUE; 17079860Sgdamore@opensolaris.org ph->phy_type = XCVR_100T4; 17089860Sgdamore@opensolaris.org } 17099860Sgdamore@opensolaris.org if (bmsr & MII_STATUS_100_BASEX) { 17109860Sgdamore@opensolaris.org ph->phy_cap_100_hdx = B_TRUE; 17119860Sgdamore@opensolaris.org ph->phy_type = XCVR_100X; 17129860Sgdamore@opensolaris.org } 17139860Sgdamore@opensolaris.org if (bmsr & MII_STATUS_100_BASEX_FD) { 17149860Sgdamore@opensolaris.org ph->phy_cap_100_fdx = B_TRUE; 17159860Sgdamore@opensolaris.org ph->phy_type = XCVR_100X; 17169860Sgdamore@opensolaris.org } 17179860Sgdamore@opensolaris.org if (extsr & MII_EXTSTATUS_1000X) { 17189860Sgdamore@opensolaris.org ph->phy_cap_1000_hdx = B_TRUE; 17199860Sgdamore@opensolaris.org ph->phy_type = XCVR_1000X; 17209860Sgdamore@opensolaris.org } 17219860Sgdamore@opensolaris.org if (extsr & MII_EXTSTATUS_1000X_FD) { 17229860Sgdamore@opensolaris.org ph->phy_cap_1000_fdx = B_TRUE; 17239860Sgdamore@opensolaris.org ph->phy_type = XCVR_1000X; 17249860Sgdamore@opensolaris.org } 17259860Sgdamore@opensolaris.org if (extsr & MII_EXTSTATUS_1000T) { 17269860Sgdamore@opensolaris.org ph->phy_cap_1000_hdx = B_TRUE; 17279860Sgdamore@opensolaris.org ph->phy_type = XCVR_1000T; 17289860Sgdamore@opensolaris.org } 17299860Sgdamore@opensolaris.org if (extsr & MII_EXTSTATUS_1000T_FD) { 17309860Sgdamore@opensolaris.org ph->phy_cap_1000_fdx = B_TRUE; 17319860Sgdamore@opensolaris.org ph->phy_type = XCVR_1000T; 17329860Sgdamore@opensolaris.org } 17339860Sgdamore@opensolaris.org 17349860Sgdamore@opensolaris.org for (int j = 0; _phy_probes[j] != NULL; j++) { 17359860Sgdamore@opensolaris.org if ((*_phy_probes[j])(ph)) { 17369860Sgdamore@opensolaris.org break; 17379860Sgdamore@opensolaris.org } 17389860Sgdamore@opensolaris.org } 17399860Sgdamore@opensolaris.org 17409860Sgdamore@opensolaris.org #define INIT_ENABLE(CAP) \ 17419860Sgdamore@opensolaris.org ph->phy_en_##CAP = (mh->m_en_##CAP > 0) ? \ 17429860Sgdamore@opensolaris.org mh->m_en_##CAP : ph->phy_cap_##CAP 17439860Sgdamore@opensolaris.org 17449860Sgdamore@opensolaris.org INIT_ENABLE(aneg); 17459860Sgdamore@opensolaris.org INIT_ENABLE(1000_fdx); 17469860Sgdamore@opensolaris.org INIT_ENABLE(1000_hdx); 17479860Sgdamore@opensolaris.org INIT_ENABLE(100_fdx); 17489860Sgdamore@opensolaris.org INIT_ENABLE(100_t4); 17499860Sgdamore@opensolaris.org INIT_ENABLE(100_hdx); 17509860Sgdamore@opensolaris.org INIT_ENABLE(10_fdx); 17519860Sgdamore@opensolaris.org INIT_ENABLE(10_hdx); 17529860Sgdamore@opensolaris.org 17539860Sgdamore@opensolaris.org #undef INIT_ENABLE 17549860Sgdamore@opensolaris.org ph->phy_en_flowctrl = mh->m_en_flowctrl; 17559860Sgdamore@opensolaris.org switch (ph->phy_en_flowctrl) { 17569860Sgdamore@opensolaris.org case LINK_FLOWCTRL_BI: 17579860Sgdamore@opensolaris.org case LINK_FLOWCTRL_RX: 17589860Sgdamore@opensolaris.org ph->phy_en_pause = B_TRUE; 17599860Sgdamore@opensolaris.org ph->phy_en_asmpause = B_TRUE; 17609860Sgdamore@opensolaris.org break; 17619860Sgdamore@opensolaris.org case LINK_FLOWCTRL_TX: 17629860Sgdamore@opensolaris.org ph->phy_en_pause = B_FALSE; 17639860Sgdamore@opensolaris.org ph->phy_en_asmpause = B_TRUE; 17649860Sgdamore@opensolaris.org break; 17659860Sgdamore@opensolaris.org default: 17669860Sgdamore@opensolaris.org ph->phy_en_pause = B_FALSE; 17679860Sgdamore@opensolaris.org ph->phy_en_asmpause = B_FALSE; 17689860Sgdamore@opensolaris.org break; 17699860Sgdamore@opensolaris.org } 17709860Sgdamore@opensolaris.org } 17719860Sgdamore@opensolaris.org 17729860Sgdamore@opensolaris.org void 177310359SGarrett.Damore@Sun.COM _mii_probe(mii_handle_t mh) 17749860Sgdamore@opensolaris.org { 17759860Sgdamore@opensolaris.org uint8_t new_addr; 17769860Sgdamore@opensolaris.org uint8_t old_addr; 17779860Sgdamore@opensolaris.org uint8_t user_addr; 17789860Sgdamore@opensolaris.org uint8_t curr_addr; 17799860Sgdamore@opensolaris.org phy_handle_t *ph; 17809860Sgdamore@opensolaris.org int pri = 0; 178111419Sgdamore@opensolaris.org int first; 17829860Sgdamore@opensolaris.org 17839860Sgdamore@opensolaris.org user_addr = ddi_prop_get_int(DDI_DEV_T_ANY, mh->m_dip, 0, 17849860Sgdamore@opensolaris.org "phy-addr", -1); 17859860Sgdamore@opensolaris.org old_addr = mh->m_addr; 17869860Sgdamore@opensolaris.org new_addr = 0xff; 17879860Sgdamore@opensolaris.org 17889860Sgdamore@opensolaris.org /* 17899860Sgdamore@opensolaris.org * Apparently, PHY 0 is less likely to be physically 17909860Sgdamore@opensolaris.org * connected, and should always be the last one tried. Most 17919860Sgdamore@opensolaris.org * single solution NICs use PHY1 for their built-in 17929860Sgdamore@opensolaris.org * transceiver. NICs with an external MII will often place 17939860Sgdamore@opensolaris.org * the external PHY at address 1, and use address 0 for the 17949860Sgdamore@opensolaris.org * internal PHY. 179511419Sgdamore@opensolaris.org * 179611419Sgdamore@opensolaris.org * Some devices have a different preference however. They can 179711419Sgdamore@opensolaris.org * override the default starting point of the search by 179811419Sgdamore@opensolaris.org * exporting a "first-phy" property. 17999860Sgdamore@opensolaris.org */ 18009860Sgdamore@opensolaris.org 180111419Sgdamore@opensolaris.org first = ddi_prop_get_int(DDI_DEV_T_ANY, mh->m_dip, 0, "first-phy", 1); 180211419Sgdamore@opensolaris.org if ((first < 0) || (first > 31)) { 180311419Sgdamore@opensolaris.org first = 1; 180411419Sgdamore@opensolaris.org } 180511419Sgdamore@opensolaris.org for (int i = first; i < (first + 32); i++) { 18069860Sgdamore@opensolaris.org 18079860Sgdamore@opensolaris.org /* 180811419Sgdamore@opensolaris.org * This is tricky: it lets us start searching at an 180911419Sgdamore@opensolaris.org * arbitrary address instead of 0, dealing with the 181011419Sgdamore@opensolaris.org * wrap-around at address 31 properly. 18119860Sgdamore@opensolaris.org */ 18129860Sgdamore@opensolaris.org curr_addr = i % 32; 18139860Sgdamore@opensolaris.org 18149860Sgdamore@opensolaris.org ph = &mh->m_phys[curr_addr]; 18159860Sgdamore@opensolaris.org 18169860Sgdamore@opensolaris.org bzero(ph, sizeof (*ph)); 18179860Sgdamore@opensolaris.org ph->phy_addr = curr_addr; 18189860Sgdamore@opensolaris.org ph->phy_mii = mh; 18199860Sgdamore@opensolaris.org 18209860Sgdamore@opensolaris.org _mii_probe_phy(ph); 18219860Sgdamore@opensolaris.org 18229860Sgdamore@opensolaris.org if (!ph->phy_present) 18239860Sgdamore@opensolaris.org continue; 18249860Sgdamore@opensolaris.org 18259860Sgdamore@opensolaris.org if (curr_addr == user_addr) { 18269860Sgdamore@opensolaris.org /* 18279860Sgdamore@opensolaris.org * We always try to honor the user configured phy. 18289860Sgdamore@opensolaris.org */ 18299860Sgdamore@opensolaris.org new_addr = curr_addr; 18309860Sgdamore@opensolaris.org pri = 4; 18319860Sgdamore@opensolaris.org 18329860Sgdamore@opensolaris.org } 18339860Sgdamore@opensolaris.org 18349860Sgdamore@opensolaris.org /* two reads to clear latched bits */ 18359860Sgdamore@opensolaris.org if ((phy_read(ph, MII_STATUS) & MII_STATUS_LINKUP) && 18369860Sgdamore@opensolaris.org (phy_read(ph, MII_STATUS) & MII_STATUS_LINKUP) && 18379860Sgdamore@opensolaris.org (pri < 3)) { 18389860Sgdamore@opensolaris.org /* 18399860Sgdamore@opensolaris.org * Link present is good. We prefer this over 18409860Sgdamore@opensolaris.org * a possibly disconnected link. 18419860Sgdamore@opensolaris.org */ 18429860Sgdamore@opensolaris.org new_addr = curr_addr; 18439860Sgdamore@opensolaris.org pri = 3; 18449860Sgdamore@opensolaris.org } 18459860Sgdamore@opensolaris.org if ((curr_addr == old_addr) && (pri < 2)) { 18469860Sgdamore@opensolaris.org /* 18479860Sgdamore@opensolaris.org * All else being equal, minimize change. 18489860Sgdamore@opensolaris.org */ 18499860Sgdamore@opensolaris.org new_addr = curr_addr; 18509860Sgdamore@opensolaris.org pri = 2; 18519860Sgdamore@opensolaris.org 18529860Sgdamore@opensolaris.org } 18539860Sgdamore@opensolaris.org if (pri < 1) { 18549860Sgdamore@opensolaris.org /* 18559860Sgdamore@opensolaris.org * But make sure we at least select a present PHY. 18569860Sgdamore@opensolaris.org */ 18579860Sgdamore@opensolaris.org new_addr = curr_addr; 18589860Sgdamore@opensolaris.org pri = 1; 18599860Sgdamore@opensolaris.org } 18609860Sgdamore@opensolaris.org } 18619860Sgdamore@opensolaris.org 18629860Sgdamore@opensolaris.org if (new_addr == 0xff) { 18639860Sgdamore@opensolaris.org mh->m_addr = -1; 18649860Sgdamore@opensolaris.org mh->m_phy = &mh->m_bogus_phy; 18659860Sgdamore@opensolaris.org _mii_error(mh, MII_ENOPHY); 18669860Sgdamore@opensolaris.org } else { 18679860Sgdamore@opensolaris.org mh->m_addr = new_addr; 18689860Sgdamore@opensolaris.org mh->m_phy = &mh->m_phys[new_addr]; 18699860Sgdamore@opensolaris.org mh->m_tstate = MII_STATE_RESET; 187010359SGarrett.Damore@Sun.COM if (new_addr != old_addr) { 187110359SGarrett.Damore@Sun.COM cmn_err(CE_CONT, 187210359SGarrett.Damore@Sun.COM "?%s: Using %s Ethernet PHY at %d: %s %s\n", 187310359SGarrett.Damore@Sun.COM mh->m_name, mii_xcvr_types[mh->m_phy->phy_type], 187410359SGarrett.Damore@Sun.COM mh->m_addr, mh->m_phy->phy_vendor, 187510359SGarrett.Damore@Sun.COM mh->m_phy->phy_model); 187610479SGarrett.Damore@Sun.COM mh->m_link = LINK_STATE_UNKNOWN; 187710359SGarrett.Damore@Sun.COM } 18789860Sgdamore@opensolaris.org } 18799860Sgdamore@opensolaris.org } 18809860Sgdamore@opensolaris.org 188110359SGarrett.Damore@Sun.COM int 188210359SGarrett.Damore@Sun.COM _mii_reset(mii_handle_t mh) 18839860Sgdamore@opensolaris.org { 18849860Sgdamore@opensolaris.org phy_handle_t *ph; 188510359SGarrett.Damore@Sun.COM boolean_t notify; 18869860Sgdamore@opensolaris.org 18879860Sgdamore@opensolaris.org ASSERT(mutex_owned(&mh->m_lock)); 18889860Sgdamore@opensolaris.org 18899860Sgdamore@opensolaris.org /* 18909860Sgdamore@opensolaris.org * Reset logic. We want to isolate all the other 18919860Sgdamore@opensolaris.org * phys that are not in use. 18929860Sgdamore@opensolaris.org */ 18939860Sgdamore@opensolaris.org for (int i = 0; i < 32; i++) { 18949860Sgdamore@opensolaris.org ph = &mh->m_phys[i]; 18959860Sgdamore@opensolaris.org 18969860Sgdamore@opensolaris.org if (!ph->phy_present) 18979860Sgdamore@opensolaris.org continue; 18989860Sgdamore@opensolaris.org 189910359SGarrett.Damore@Sun.COM /* Don't touch our own phy, yet. */ 19009860Sgdamore@opensolaris.org if (ph == mh->m_phy) 19019860Sgdamore@opensolaris.org continue; 19029860Sgdamore@opensolaris.org 19039860Sgdamore@opensolaris.org ph->phy_stop(ph); 19049860Sgdamore@opensolaris.org } 19059860Sgdamore@opensolaris.org 19069860Sgdamore@opensolaris.org ph = mh->m_phy; 19079860Sgdamore@opensolaris.org 19089860Sgdamore@opensolaris.org ASSERT(ph->phy_present); 19099860Sgdamore@opensolaris.org 191010359SGarrett.Damore@Sun.COM /* If we're resetting the PHY, then we want to notify loss of link */ 191110479SGarrett.Damore@Sun.COM notify = (mh->m_link != LINK_STATE_DOWN); 191210359SGarrett.Damore@Sun.COM mh->m_link = LINK_STATE_DOWN; 191310359SGarrett.Damore@Sun.COM ph->phy_link = LINK_STATE_DOWN; 191410359SGarrett.Damore@Sun.COM ph->phy_speed = 0; 191510359SGarrett.Damore@Sun.COM ph->phy_duplex = LINK_DUPLEX_UNKNOWN; 19169860Sgdamore@opensolaris.org 19179860Sgdamore@opensolaris.org if (ph->phy_reset(ph) != DDI_SUCCESS) { 19189860Sgdamore@opensolaris.org _mii_error(mh, MII_ERESET); 19199860Sgdamore@opensolaris.org return (DDI_FAILURE); 19209860Sgdamore@opensolaris.org } 19219860Sgdamore@opensolaris.org 192210359SGarrett.Damore@Sun.COM /* Perform optional mac layer reset. */ 192310359SGarrett.Damore@Sun.COM if (mh->m_ops.mii_reset != NULL) { 192410359SGarrett.Damore@Sun.COM mh->m_ops.mii_reset(mh->m_private); 192510359SGarrett.Damore@Sun.COM } 192610359SGarrett.Damore@Sun.COM 192710359SGarrett.Damore@Sun.COM /* Perform optional mac layer notification. */ 192810359SGarrett.Damore@Sun.COM if (notify) { 192910359SGarrett.Damore@Sun.COM _mii_notify(mh); 193010359SGarrett.Damore@Sun.COM } 19319860Sgdamore@opensolaris.org return (DDI_SUCCESS); 19329860Sgdamore@opensolaris.org } 19339860Sgdamore@opensolaris.org 193410359SGarrett.Damore@Sun.COM int 193510359SGarrett.Damore@Sun.COM _mii_loopback(mii_handle_t mh) 193610359SGarrett.Damore@Sun.COM { 193710359SGarrett.Damore@Sun.COM phy_handle_t *ph; 193810359SGarrett.Damore@Sun.COM 193910359SGarrett.Damore@Sun.COM ASSERT(mutex_owned(&mh->m_lock)); 194010359SGarrett.Damore@Sun.COM 194110359SGarrett.Damore@Sun.COM ph = mh->m_phy; 194210359SGarrett.Damore@Sun.COM 194310359SGarrett.Damore@Sun.COM if (_mii_reset(mh) != DDI_SUCCESS) { 194410359SGarrett.Damore@Sun.COM return (DDI_FAILURE); 194510359SGarrett.Damore@Sun.COM } 194610359SGarrett.Damore@Sun.COM if (ph->phy_loopback == PHY_LB_NONE) { 194710359SGarrett.Damore@Sun.COM mh->m_tstate = MII_STATE_START; 194810359SGarrett.Damore@Sun.COM return (DDI_SUCCESS); 194910359SGarrett.Damore@Sun.COM } 195010359SGarrett.Damore@Sun.COM if (ph->phy_loop(ph) != DDI_SUCCESS) { 195110359SGarrett.Damore@Sun.COM _mii_error(mh, MII_ELOOP); 195210359SGarrett.Damore@Sun.COM return (DDI_FAILURE); 195310359SGarrett.Damore@Sun.COM } 195410359SGarrett.Damore@Sun.COM 195510359SGarrett.Damore@Sun.COM /* Just force loopback to link up. */ 195610359SGarrett.Damore@Sun.COM mh->m_link = ph->phy_link = LINK_STATE_UP; 195710359SGarrett.Damore@Sun.COM _mii_notify(mh); 195810359SGarrett.Damore@Sun.COM 195910359SGarrett.Damore@Sun.COM return (DDI_SUCCESS); 196010359SGarrett.Damore@Sun.COM } 196110359SGarrett.Damore@Sun.COM 196210359SGarrett.Damore@Sun.COM int 196310359SGarrett.Damore@Sun.COM _mii_start(mii_handle_t mh) 19649860Sgdamore@opensolaris.org { 19659860Sgdamore@opensolaris.org phy_handle_t *ph; 19669860Sgdamore@opensolaris.org 19679860Sgdamore@opensolaris.org ph = mh->m_phy; 19689860Sgdamore@opensolaris.org 19699860Sgdamore@opensolaris.org ASSERT(mutex_owned(&mh->m_lock)); 19709860Sgdamore@opensolaris.org ASSERT(ph->phy_present); 197110359SGarrett.Damore@Sun.COM ASSERT(ph->phy_loopback == PHY_LB_NONE); 19729860Sgdamore@opensolaris.org 197310359SGarrett.Damore@Sun.COM if (ph->phy_start(ph) != DDI_SUCCESS) { 19749860Sgdamore@opensolaris.org _mii_error(mh, MII_ESTART); 197510359SGarrett.Damore@Sun.COM return (DDI_FAILURE); 19769860Sgdamore@opensolaris.org } 19779860Sgdamore@opensolaris.org /* clear the error state since we got a good startup! */ 19789860Sgdamore@opensolaris.org mh->m_error = MII_EOK; 197910359SGarrett.Damore@Sun.COM return (DDI_SUCCESS); 19809860Sgdamore@opensolaris.org } 19819860Sgdamore@opensolaris.org 198210359SGarrett.Damore@Sun.COM int 198310359SGarrett.Damore@Sun.COM _mii_check(mii_handle_t mh) 19849860Sgdamore@opensolaris.org { 19859860Sgdamore@opensolaris.org link_state_t olink; 19869860Sgdamore@opensolaris.org int ospeed; 19879860Sgdamore@opensolaris.org link_duplex_t oduplex; 19889860Sgdamore@opensolaris.org link_flowctrl_t ofctrl; 19899860Sgdamore@opensolaris.org phy_handle_t *ph; 19909860Sgdamore@opensolaris.org 19919860Sgdamore@opensolaris.org ph = mh->m_phy; 19929860Sgdamore@opensolaris.org 19939860Sgdamore@opensolaris.org olink = mh->m_link; 19949860Sgdamore@opensolaris.org ospeed = ph->phy_speed; 19959860Sgdamore@opensolaris.org oduplex = ph->phy_duplex; 19969860Sgdamore@opensolaris.org ofctrl = ph->phy_flowctrl; 19979860Sgdamore@opensolaris.org 19989860Sgdamore@opensolaris.org ASSERT(ph->phy_present); 19999860Sgdamore@opensolaris.org 20009860Sgdamore@opensolaris.org if (ph->phy_check(ph) == DDI_FAILURE) { 20019860Sgdamore@opensolaris.org _mii_error(mh, MII_ECHECK); 20029860Sgdamore@opensolaris.org mh->m_link = LINK_STATE_UNKNOWN; 200310359SGarrett.Damore@Sun.COM _mii_notify(mh); 20049860Sgdamore@opensolaris.org return (DDI_FAILURE); 20059860Sgdamore@opensolaris.org } 20069860Sgdamore@opensolaris.org 20079860Sgdamore@opensolaris.org mh->m_link = ph->phy_link; 20089860Sgdamore@opensolaris.org 20099860Sgdamore@opensolaris.org /* if anything changed, notify! */ 20109860Sgdamore@opensolaris.org if ((mh->m_link != olink) || 20119860Sgdamore@opensolaris.org (ph->phy_speed != ospeed) || 20129860Sgdamore@opensolaris.org (ph->phy_duplex != oduplex) || 20139860Sgdamore@opensolaris.org (ph->phy_flowctrl != ofctrl)) { 201410359SGarrett.Damore@Sun.COM _mii_notify(mh); 20159860Sgdamore@opensolaris.org } 20169860Sgdamore@opensolaris.org 20179860Sgdamore@opensolaris.org return (DDI_SUCCESS); 20189860Sgdamore@opensolaris.org } 20199860Sgdamore@opensolaris.org 202010359SGarrett.Damore@Sun.COM void 20219860Sgdamore@opensolaris.org _mii_task(void *_mh) 20229860Sgdamore@opensolaris.org { 20239860Sgdamore@opensolaris.org mii_handle_t mh = _mh; 20249860Sgdamore@opensolaris.org phy_handle_t *ph; 20259860Sgdamore@opensolaris.org clock_t wait; 20269860Sgdamore@opensolaris.org clock_t downtime; 20279860Sgdamore@opensolaris.org 20289860Sgdamore@opensolaris.org mutex_enter(&mh->m_lock); 20299860Sgdamore@opensolaris.org 20309860Sgdamore@opensolaris.org for (;;) { 20319860Sgdamore@opensolaris.org 20329860Sgdamore@opensolaris.org /* If detaching, exit the thread. */ 20339860Sgdamore@opensolaris.org if (!mh->m_started) { 20349860Sgdamore@opensolaris.org break; 20359860Sgdamore@opensolaris.org } 20369860Sgdamore@opensolaris.org 20379860Sgdamore@opensolaris.org ph = mh->m_phy; 20389860Sgdamore@opensolaris.org 20399860Sgdamore@opensolaris.org /* 20409860Sgdamore@opensolaris.org * If we're suspended or otherwise not supposed to be 20419860Sgdamore@opensolaris.org * monitoring the link, just go back to sleep. 20429860Sgdamore@opensolaris.org * 20439860Sgdamore@opensolaris.org * Theoretically we could power down the PHY, but we 20449860Sgdamore@opensolaris.org * don't bother. (The link might be used for 20459860Sgdamore@opensolaris.org * wake-on-lan!) Another option would be to reduce 20469860Sgdamore@opensolaris.org * power on the PHY if both it and the link partner 20479860Sgdamore@opensolaris.org * support 10 Mbps mode. 20489860Sgdamore@opensolaris.org */ 20499860Sgdamore@opensolaris.org if (mh->m_suspending) { 20509860Sgdamore@opensolaris.org mh->m_suspended = B_TRUE; 20519860Sgdamore@opensolaris.org cv_broadcast(&mh->m_cv); 20529860Sgdamore@opensolaris.org } 20539860Sgdamore@opensolaris.org if (mh->m_suspended) { 20549860Sgdamore@opensolaris.org mh->m_suspending = B_FALSE; 20559860Sgdamore@opensolaris.org cv_wait(&mh->m_cv, &mh->m_lock); 20569860Sgdamore@opensolaris.org continue; 20579860Sgdamore@opensolaris.org } 20589860Sgdamore@opensolaris.org 20599860Sgdamore@opensolaris.org switch (mh->m_tstate) { 20609860Sgdamore@opensolaris.org case MII_STATE_PROBE: 206110359SGarrett.Damore@Sun.COM _mii_probe(mh); 20629860Sgdamore@opensolaris.org ph = mh->m_phy; 20639860Sgdamore@opensolaris.org if (!ph->phy_present) { 20649860Sgdamore@opensolaris.org /* 20659860Sgdamore@opensolaris.org * If no PHY is found, wait a bit before 20669860Sgdamore@opensolaris.org * trying the probe again. 10 seconds ought 20679860Sgdamore@opensolaris.org * to be enough. 20689860Sgdamore@opensolaris.org */ 20699860Sgdamore@opensolaris.org wait = 10 * MII_SECOND; 20709860Sgdamore@opensolaris.org } else { 20719860Sgdamore@opensolaris.org wait = 0; 20729860Sgdamore@opensolaris.org } 20739860Sgdamore@opensolaris.org break; 20749860Sgdamore@opensolaris.org 20759860Sgdamore@opensolaris.org case MII_STATE_RESET: 207610359SGarrett.Damore@Sun.COM if (_mii_reset(mh) == DDI_SUCCESS) { 207710359SGarrett.Damore@Sun.COM mh->m_tstate = MII_STATE_START; 20789860Sgdamore@opensolaris.org wait = 0; 20799860Sgdamore@opensolaris.org } else { 20809860Sgdamore@opensolaris.org /* 20819860Sgdamore@opensolaris.org * If an error occurred, wait a bit and 20829860Sgdamore@opensolaris.org * try again later. 20839860Sgdamore@opensolaris.org */ 20849860Sgdamore@opensolaris.org wait = 10 * MII_SECOND; 20859860Sgdamore@opensolaris.org } 20869860Sgdamore@opensolaris.org break; 20879860Sgdamore@opensolaris.org 20889860Sgdamore@opensolaris.org case MII_STATE_START: 20899860Sgdamore@opensolaris.org /* 20909860Sgdamore@opensolaris.org * If an error occurs, we're going to go back to 20919860Sgdamore@opensolaris.org * probe or reset state. Otherwise we go to run 20929860Sgdamore@opensolaris.org * state. In all cases we want to wait 1 second 20939860Sgdamore@opensolaris.org * before doing anything else - either for link to 20949860Sgdamore@opensolaris.org * settle, or to give other code a chance to run 20959860Sgdamore@opensolaris.org * while we reset. 20969860Sgdamore@opensolaris.org */ 209710359SGarrett.Damore@Sun.COM if (_mii_start(mh) == DDI_SUCCESS) { 209810359SGarrett.Damore@Sun.COM /* reset watchdog to latest */ 209910359SGarrett.Damore@Sun.COM downtime = ddi_get_lbolt(); 210010359SGarrett.Damore@Sun.COM mh->m_tstate = MII_STATE_RUN; 210110359SGarrett.Damore@Sun.COM } else { 210210359SGarrett.Damore@Sun.COM mh->m_tstate = MII_STATE_PROBE; 210310359SGarrett.Damore@Sun.COM } 210410359SGarrett.Damore@Sun.COM wait = 0; 210510359SGarrett.Damore@Sun.COM break; 210610359SGarrett.Damore@Sun.COM 210710359SGarrett.Damore@Sun.COM case MII_STATE_LOOPBACK: 210810359SGarrett.Damore@Sun.COM /* 210910359SGarrett.Damore@Sun.COM * In loopback mode we don't check anything, 211010359SGarrett.Damore@Sun.COM * and just wait for some condition to change. 211110359SGarrett.Damore@Sun.COM */ 211210359SGarrett.Damore@Sun.COM wait = (clock_t)-1; 21139860Sgdamore@opensolaris.org break; 21149860Sgdamore@opensolaris.org 21159860Sgdamore@opensolaris.org case MII_STATE_RUN: 21169860Sgdamore@opensolaris.org default: 211710359SGarrett.Damore@Sun.COM if (_mii_check(mh) == DDI_FAILURE) { 21189860Sgdamore@opensolaris.org /* 21199860Sgdamore@opensolaris.org * On error (PHY removed?), wait a 21209860Sgdamore@opensolaris.org * short bit before reprobing or 21219860Sgdamore@opensolaris.org * resetting. 21229860Sgdamore@opensolaris.org */ 21239860Sgdamore@opensolaris.org wait = MII_SECOND; 212410359SGarrett.Damore@Sun.COM mh->m_tstate = MII_STATE_PROBE; 21259860Sgdamore@opensolaris.org 21269860Sgdamore@opensolaris.org } else if (mh->m_link == LINK_STATE_UP) { 21279860Sgdamore@opensolaris.org /* got goood link, so reset the watchdog */ 21289860Sgdamore@opensolaris.org downtime = ddi_get_lbolt(); 21299860Sgdamore@opensolaris.org /* rescan again in a second */ 21309860Sgdamore@opensolaris.org wait = MII_SECOND; 21319860Sgdamore@opensolaris.org 21329860Sgdamore@opensolaris.org } else if ((ddi_get_lbolt() - downtime) > 21339860Sgdamore@opensolaris.org (drv_usectohz(MII_SECOND * 10))) { 21349860Sgdamore@opensolaris.org 21359860Sgdamore@opensolaris.org /* 21369860Sgdamore@opensolaris.org * If we were down for 10 seconds, 21379860Sgdamore@opensolaris.org * hard reset the PHY. 21389860Sgdamore@opensolaris.org */ 21399860Sgdamore@opensolaris.org mh->m_tstate = MII_STATE_RESET; 21409860Sgdamore@opensolaris.org wait = 0; 21419860Sgdamore@opensolaris.org 21429860Sgdamore@opensolaris.org } else { 21439860Sgdamore@opensolaris.org /* 21449860Sgdamore@opensolaris.org * Otherwise, if we are still down, 21459860Sgdamore@opensolaris.org * rescan the link much more 21469860Sgdamore@opensolaris.org * frequently. We might be trying to 21479860Sgdamore@opensolaris.org * autonegotiate. 21489860Sgdamore@opensolaris.org */ 21499860Sgdamore@opensolaris.org wait = MII_SECOND / 4; 21509860Sgdamore@opensolaris.org } 21519860Sgdamore@opensolaris.org break; 21529860Sgdamore@opensolaris.org } 21539860Sgdamore@opensolaris.org 215410359SGarrett.Damore@Sun.COM switch (wait) { 215510359SGarrett.Damore@Sun.COM case 0: 215610359SGarrett.Damore@Sun.COM break; 21579860Sgdamore@opensolaris.org 215810359SGarrett.Damore@Sun.COM case (clock_t)-1: 215910359SGarrett.Damore@Sun.COM cv_wait(&mh->m_cv, &mh->m_lock); 216010359SGarrett.Damore@Sun.COM break; 21619860Sgdamore@opensolaris.org 216210359SGarrett.Damore@Sun.COM default: 216311066Srafael.vanoni@sun.com (void) cv_reltimedwait(&mh->m_cv, &mh->m_lock, 216411066Srafael.vanoni@sun.com drv_usectohz(wait), TR_CLOCK_TICK); 216510359SGarrett.Damore@Sun.COM } 21669860Sgdamore@opensolaris.org } 21679860Sgdamore@opensolaris.org 21689860Sgdamore@opensolaris.org mutex_exit(&mh->m_lock); 21699860Sgdamore@opensolaris.org } 2170