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 /* 229860Sgdamore@opensolaris.org * Copyright 2009 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, 619860Sgdamore@opensolaris.org }; 629860Sgdamore@opensolaris.org 639860Sgdamore@opensolaris.org static const char *mii_errors[] = { 649860Sgdamore@opensolaris.org "", 659860Sgdamore@opensolaris.org "Failure resetting PHY.", 669860Sgdamore@opensolaris.org "Failure starting PHY.", 679860Sgdamore@opensolaris.org "No Ethernet PHY found.", 689860Sgdamore@opensolaris.org "Failure reading PHY (removed?)" 699860Sgdamore@opensolaris.org }; 709860Sgdamore@opensolaris.org 719860Sgdamore@opensolaris.org /* Indexed by XCVR_ type */ 729860Sgdamore@opensolaris.org static const const char *mii_xcvr_types[] = { 739860Sgdamore@opensolaris.org "Undefined", 749860Sgdamore@opensolaris.org "Unknown", 759860Sgdamore@opensolaris.org "10 Mbps", 769860Sgdamore@opensolaris.org "100BASE-T4", 779860Sgdamore@opensolaris.org "100BASE-X", 789860Sgdamore@opensolaris.org "100BASE-T2", 799860Sgdamore@opensolaris.org "1000BASE-X", 809860Sgdamore@opensolaris.org "1000BASE-T" 819860Sgdamore@opensolaris.org }; 829860Sgdamore@opensolaris.org 839860Sgdamore@opensolaris.org static lb_property_t mii_loopmodes[] = { 849860Sgdamore@opensolaris.org { normal, "normal", PHY_LB_NONE }, 859860Sgdamore@opensolaris.org { internal, "PHY", PHY_LB_INT_PHY }, 869860Sgdamore@opensolaris.org { external, "10Mbps", PHY_LB_EXT_10 }, 879860Sgdamore@opensolaris.org { external, "100Mbps", PHY_LB_EXT_100 }, 889860Sgdamore@opensolaris.org { external, "1000Mbps", PHY_LB_EXT_1000 }, 899860Sgdamore@opensolaris.org }; 909860Sgdamore@opensolaris.org 919860Sgdamore@opensolaris.org /* state machine */ 929860Sgdamore@opensolaris.org typedef enum { 939860Sgdamore@opensolaris.org MII_STATE_PROBE = 0, 949860Sgdamore@opensolaris.org MII_STATE_RESET, 959860Sgdamore@opensolaris.org MII_STATE_START, 969860Sgdamore@opensolaris.org MII_STATE_RUN, 979860Sgdamore@opensolaris.org } mii_tstate_t; 989860Sgdamore@opensolaris.org 999860Sgdamore@opensolaris.org struct mii_handle { 1009860Sgdamore@opensolaris.org dev_info_t *m_dip; 1019860Sgdamore@opensolaris.org void *m_private; 1029860Sgdamore@opensolaris.org mii_ops_t m_ops; 1039860Sgdamore@opensolaris.org 1049860Sgdamore@opensolaris.org kt_did_t m_tq_id; 1059860Sgdamore@opensolaris.org kmutex_t m_lock; 1069860Sgdamore@opensolaris.org kcondvar_t m_cv; 1079860Sgdamore@opensolaris.org ddi_taskq_t *m_tq; 1089860Sgdamore@opensolaris.org int m_flags; 1099860Sgdamore@opensolaris.org 1109860Sgdamore@opensolaris.org boolean_t m_started; 1119860Sgdamore@opensolaris.org boolean_t m_suspending; 1129860Sgdamore@opensolaris.org boolean_t m_suspended; 1139860Sgdamore@opensolaris.org boolean_t m_notify; 1149860Sgdamore@opensolaris.org int m_error; 1159860Sgdamore@opensolaris.org mii_tstate_t m_tstate; 1169860Sgdamore@opensolaris.org 1179860Sgdamore@opensolaris.org #define MII_FLAG_EXIT 0x1 /* exit the thread */ 1189860Sgdamore@opensolaris.org #define MII_FLAG_STOP 0x2 /* shutdown MII monitoring */ 1199860Sgdamore@opensolaris.org #define MII_FLAG_RESET 0x4 /* reset the MII */ 1209860Sgdamore@opensolaris.org #define MII_FLAG_PROBE 0x8 /* probe for PHYs */ 1219860Sgdamore@opensolaris.org #define MII_FLAG_NOTIFY 0x10 /* notify about a change */ 1229860Sgdamore@opensolaris.org #define MII_FLAG_SUSPEND 0x20 /* monitoring suspended */ 1239860Sgdamore@opensolaris.org #define MII_FLAG_MACRESET 0x40 /* send reset to MAC */ 1249860Sgdamore@opensolaris.org #define MII_FLAG_PHYSTART 0x80 /* start up the PHY */ 1259860Sgdamore@opensolaris.org 1269860Sgdamore@opensolaris.org /* device name for printing, e.g. "hme0" */ 1279860Sgdamore@opensolaris.org char m_name[MODMAXNAMELEN + 16]; 1289860Sgdamore@opensolaris.org 1299860Sgdamore@opensolaris.org int m_addr; 1309860Sgdamore@opensolaris.org phy_handle_t m_phys[32]; 1319860Sgdamore@opensolaris.org phy_handle_t m_bogus_phy; 1329860Sgdamore@opensolaris.org phy_handle_t *m_phy; 1339860Sgdamore@opensolaris.org 1349860Sgdamore@opensolaris.org link_state_t m_link; 1359860Sgdamore@opensolaris.org 1369860Sgdamore@opensolaris.org /* these start out undefined, but get values due to mac_prop_set */ 1379860Sgdamore@opensolaris.org int m_en_aneg; 1389860Sgdamore@opensolaris.org int m_en_10_hdx; 1399860Sgdamore@opensolaris.org int m_en_10_fdx; 1409860Sgdamore@opensolaris.org int m_en_100_t4; 1419860Sgdamore@opensolaris.org int m_en_100_hdx; 1429860Sgdamore@opensolaris.org int m_en_100_fdx; 1439860Sgdamore@opensolaris.org int m_en_1000_hdx; 1449860Sgdamore@opensolaris.org int m_en_1000_fdx; 1459860Sgdamore@opensolaris.org int m_en_flowctrl; 1469860Sgdamore@opensolaris.org 1479860Sgdamore@opensolaris.org boolean_t m_cap_pause; 1489860Sgdamore@opensolaris.org boolean_t m_cap_asmpause; 1499860Sgdamore@opensolaris.org }; 1509860Sgdamore@opensolaris.org 1519860Sgdamore@opensolaris.org 1529860Sgdamore@opensolaris.org static void _mii_task(void *); 1539860Sgdamore@opensolaris.org static void _mii_probe_task(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, 2109860Sgdamore@opensolaris.org phy_other_probe, 2119860Sgdamore@opensolaris.org NULL 2129860Sgdamore@opensolaris.org }; 2139860Sgdamore@opensolaris.org 2149860Sgdamore@opensolaris.org /* 2159860Sgdamore@opensolaris.org * MII Interface functions 2169860Sgdamore@opensolaris.org */ 2179860Sgdamore@opensolaris.org 2189860Sgdamore@opensolaris.org mii_handle_t 2199860Sgdamore@opensolaris.org mii_alloc_instance(void *private, dev_info_t *dip, int inst, mii_ops_t *ops) 2209860Sgdamore@opensolaris.org { 2219860Sgdamore@opensolaris.org mii_handle_t mh; 2229860Sgdamore@opensolaris.org char tqname[16]; 2239860Sgdamore@opensolaris.org 2249860Sgdamore@opensolaris.org if (ops->mii_version != MII_OPS_VERSION) { 2259860Sgdamore@opensolaris.org cmn_err(CE_WARN, "%s: incompatible MII version (%d)", 2269860Sgdamore@opensolaris.org ddi_driver_name(dip), ops->mii_version); 2279860Sgdamore@opensolaris.org return (NULL); 2289860Sgdamore@opensolaris.org } 2299860Sgdamore@opensolaris.org mh = kmem_zalloc(sizeof (*mh), KM_SLEEP); 2309860Sgdamore@opensolaris.org 2319860Sgdamore@opensolaris.org (void) snprintf(mh->m_name, sizeof (mh->m_name), "%s%d", 2329860Sgdamore@opensolaris.org ddi_driver_name(dip), inst); 2339860Sgdamore@opensolaris.org 2349860Sgdamore@opensolaris.org /* DDI will prepend the driver name */ 2359860Sgdamore@opensolaris.org (void) snprintf(tqname, sizeof (tqname), "mii%d", inst); 2369860Sgdamore@opensolaris.org 2379860Sgdamore@opensolaris.org mh->m_dip = dip; 2389860Sgdamore@opensolaris.org mh->m_ops = *ops; 2399860Sgdamore@opensolaris.org mh->m_private = private; 2409860Sgdamore@opensolaris.org mh->m_suspended = B_FALSE; 2419860Sgdamore@opensolaris.org mh->m_started = B_FALSE; 2429860Sgdamore@opensolaris.org mh->m_notify = B_FALSE; 2439860Sgdamore@opensolaris.org mh->m_tstate = MII_STATE_PROBE; 2449860Sgdamore@opensolaris.org mh->m_error = MII_EOK; 2459860Sgdamore@opensolaris.org mutex_init(&mh->m_lock, NULL, MUTEX_DRIVER, NULL); 2469860Sgdamore@opensolaris.org cv_init(&mh->m_cv, NULL, CV_DRIVER, NULL); 2479860Sgdamore@opensolaris.org 2489860Sgdamore@opensolaris.org mh->m_tq = ddi_taskq_create(dip, tqname, 1, TASKQ_DEFAULTPRI, 0); 2499860Sgdamore@opensolaris.org if (mh->m_tq == NULL) { 2509860Sgdamore@opensolaris.org cmn_err(CE_WARN, "%s: unable to create MII monitoring task", 2519860Sgdamore@opensolaris.org ddi_driver_name(dip)); 2529860Sgdamore@opensolaris.org cv_destroy(&mh->m_cv); 2539860Sgdamore@opensolaris.org mutex_destroy(&mh->m_lock); 2549860Sgdamore@opensolaris.org kmem_free(mh, sizeof (*mh)); 2559860Sgdamore@opensolaris.org return (NULL); 2569860Sgdamore@opensolaris.org } 2579860Sgdamore@opensolaris.org 2589860Sgdamore@opensolaris.org /* 2599860Sgdamore@opensolaris.org * Initialize user prefs by loading properties. Ultimately, 2609860Sgdamore@opensolaris.org * Brussels interfaces would be superior here. 2619860Sgdamore@opensolaris.org */ 2629860Sgdamore@opensolaris.org #define GETPROP(name) ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0, name, -1) 2639860Sgdamore@opensolaris.org mh->m_en_aneg = GETPROP("adv_autoneg_cap"); 2649860Sgdamore@opensolaris.org mh->m_en_10_hdx = GETPROP("adv_10hdx_cap"); 2659860Sgdamore@opensolaris.org mh->m_en_10_fdx = GETPROP("adv_10fdx_cap"); 2669860Sgdamore@opensolaris.org mh->m_en_100_hdx = GETPROP("adv_100hdx_cap"); 2679860Sgdamore@opensolaris.org mh->m_en_100_fdx = GETPROP("adv_100fdx_cap"); 2689860Sgdamore@opensolaris.org mh->m_en_100_t4 = GETPROP("adv_100T4_cap"); 2699860Sgdamore@opensolaris.org mh->m_en_1000_hdx = GETPROP("adv_1000hdx_cap"); 2709860Sgdamore@opensolaris.org mh->m_en_1000_fdx = GETPROP("adv_1000fdx_cap"); 2719860Sgdamore@opensolaris.org 2729860Sgdamore@opensolaris.org mh->m_cap_pause = B_FALSE; 2739860Sgdamore@opensolaris.org mh->m_cap_asmpause = B_FALSE; 2749860Sgdamore@opensolaris.org 2759860Sgdamore@opensolaris.org bzero(&mh->m_bogus_phy, sizeof (mh->m_bogus_phy)); 2769860Sgdamore@opensolaris.org mh->m_bogus_phy.phy_link = LINK_STATE_UNKNOWN; 2779860Sgdamore@opensolaris.org mh->m_bogus_phy.phy_duplex = LINK_DUPLEX_UNKNOWN; 2789860Sgdamore@opensolaris.org mh->m_bogus_phy.phy_addr = 0xff; 2799860Sgdamore@opensolaris.org mh->m_bogus_phy.phy_type = XCVR_NONE; 2809860Sgdamore@opensolaris.org mh->m_bogus_phy.phy_id = (uint32_t)-1; 2819860Sgdamore@opensolaris.org mh->m_bogus_phy.phy_loopback = PHY_LB_NONE; 2829860Sgdamore@opensolaris.org mh->m_bogus_phy.phy_flowctrl = LINK_FLOWCTRL_NONE; 2839860Sgdamore@opensolaris.org mh->m_phy = &mh->m_bogus_phy; 2849860Sgdamore@opensolaris.org 2859860Sgdamore@opensolaris.org for (int i = 0; i < 32; i++) { 2869860Sgdamore@opensolaris.org mh->m_phys[i].phy_mii = mh; 2879860Sgdamore@opensolaris.org } 2889860Sgdamore@opensolaris.org mh->m_bogus_phy.phy_mii = mh; 2899860Sgdamore@opensolaris.org 2909860Sgdamore@opensolaris.org return (mh); 2919860Sgdamore@opensolaris.org } 2929860Sgdamore@opensolaris.org 2939860Sgdamore@opensolaris.org mii_handle_t 2949860Sgdamore@opensolaris.org mii_alloc(void *private, dev_info_t *dip, mii_ops_t *ops) 2959860Sgdamore@opensolaris.org { 2969860Sgdamore@opensolaris.org return (mii_alloc_instance(private, dip, ddi_get_instance(dip), ops)); 2979860Sgdamore@opensolaris.org } 2989860Sgdamore@opensolaris.org 2999860Sgdamore@opensolaris.org void 3009860Sgdamore@opensolaris.org mii_set_pauseable(mii_handle_t mh, boolean_t pauseable, boolean_t asymetric) 3019860Sgdamore@opensolaris.org { 3029860Sgdamore@opensolaris.org phy_handle_t *ph; 3039860Sgdamore@opensolaris.org 3049860Sgdamore@opensolaris.org mutex_enter(&mh->m_lock); 3059860Sgdamore@opensolaris.org ph = mh->m_phy; 3069860Sgdamore@opensolaris.org ph->phy_cap_pause = mh->m_cap_pause = pauseable; 3079860Sgdamore@opensolaris.org ph->phy_cap_asmpause = mh->m_cap_asmpause = asymetric; 3089860Sgdamore@opensolaris.org if (pauseable) { 3099860Sgdamore@opensolaris.org mh->m_en_flowctrl = LINK_FLOWCTRL_BI; 3109860Sgdamore@opensolaris.org } else { 3119860Sgdamore@opensolaris.org mh->m_en_flowctrl = LINK_FLOWCTRL_NONE; 3129860Sgdamore@opensolaris.org } 3139860Sgdamore@opensolaris.org mutex_exit(&mh->m_lock); 3149860Sgdamore@opensolaris.org } 3159860Sgdamore@opensolaris.org 3169860Sgdamore@opensolaris.org void 3179860Sgdamore@opensolaris.org mii_free(mii_handle_t mh) 3189860Sgdamore@opensolaris.org { 3199860Sgdamore@opensolaris.org mutex_enter(&mh->m_lock); 3209860Sgdamore@opensolaris.org mh->m_started = B_FALSE; 3219860Sgdamore@opensolaris.org cv_broadcast(&mh->m_cv); 3229860Sgdamore@opensolaris.org mutex_exit(&mh->m_lock); 3239860Sgdamore@opensolaris.org 3249860Sgdamore@opensolaris.org ddi_taskq_destroy(mh->m_tq); 3259860Sgdamore@opensolaris.org mutex_destroy(&mh->m_lock); 3269860Sgdamore@opensolaris.org cv_destroy(&mh->m_cv); 3279860Sgdamore@opensolaris.org kmem_free(mh, sizeof (*mh)); 3289860Sgdamore@opensolaris.org } 3299860Sgdamore@opensolaris.org 3309860Sgdamore@opensolaris.org void 3319860Sgdamore@opensolaris.org mii_reset(mii_handle_t mh) 3329860Sgdamore@opensolaris.org { 3339860Sgdamore@opensolaris.org mutex_enter(&mh->m_lock); 3349860Sgdamore@opensolaris.org if (mh->m_tstate > MII_STATE_RESET) 3359860Sgdamore@opensolaris.org mh->m_tstate = MII_STATE_RESET; 3369860Sgdamore@opensolaris.org cv_broadcast(&mh->m_cv); 3379860Sgdamore@opensolaris.org mutex_exit(&mh->m_lock); 3389860Sgdamore@opensolaris.org } 3399860Sgdamore@opensolaris.org 3409860Sgdamore@opensolaris.org void 3419860Sgdamore@opensolaris.org mii_suspend(mii_handle_t mh) 3429860Sgdamore@opensolaris.org { 3439860Sgdamore@opensolaris.org mutex_enter(&mh->m_lock); 3449860Sgdamore@opensolaris.org while ((!mh->m_suspended) && (mh->m_started)) { 3459860Sgdamore@opensolaris.org mh->m_suspending = B_TRUE; 3469860Sgdamore@opensolaris.org cv_broadcast(&mh->m_cv); 3479860Sgdamore@opensolaris.org cv_wait(&mh->m_cv, &mh->m_lock); 3489860Sgdamore@opensolaris.org } 3499860Sgdamore@opensolaris.org mutex_exit(&mh->m_lock); 3509860Sgdamore@opensolaris.org } 3519860Sgdamore@opensolaris.org 3529860Sgdamore@opensolaris.org void 3539860Sgdamore@opensolaris.org mii_resume(mii_handle_t mh) 3549860Sgdamore@opensolaris.org { 3559860Sgdamore@opensolaris.org mutex_enter(&mh->m_lock); 3569860Sgdamore@opensolaris.org /* resume implicity causes a PHY reset */ 3579860Sgdamore@opensolaris.org if (mh->m_tstate > MII_STATE_RESET) { 3589860Sgdamore@opensolaris.org mh->m_tstate = MII_STATE_RESET; 3599860Sgdamore@opensolaris.org } 3609860Sgdamore@opensolaris.org mh->m_suspended = B_FALSE; 3619860Sgdamore@opensolaris.org cv_broadcast(&mh->m_cv); 3629860Sgdamore@opensolaris.org mutex_exit(&mh->m_lock); 3639860Sgdamore@opensolaris.org } 3649860Sgdamore@opensolaris.org 3659860Sgdamore@opensolaris.org void 3669860Sgdamore@opensolaris.org mii_start(mii_handle_t mh) 3679860Sgdamore@opensolaris.org { 3689860Sgdamore@opensolaris.org mutex_enter(&mh->m_lock); 3699860Sgdamore@opensolaris.org if (!mh->m_started) { 3709860Sgdamore@opensolaris.org mh->m_started = B_TRUE; 3719860Sgdamore@opensolaris.org if (ddi_taskq_dispatch(mh->m_tq, _mii_task, mh, DDI_NOSLEEP) != 3729860Sgdamore@opensolaris.org DDI_SUCCESS) { 3739860Sgdamore@opensolaris.org cmn_err(CE_WARN, 3749860Sgdamore@opensolaris.org "%s: unable to start MII monitoring task", 3759860Sgdamore@opensolaris.org mh->m_name); 3769860Sgdamore@opensolaris.org mh->m_started = B_FALSE; 3779860Sgdamore@opensolaris.org } 3789860Sgdamore@opensolaris.org } 3799860Sgdamore@opensolaris.org cv_broadcast(&mh->m_cv); 3809860Sgdamore@opensolaris.org mutex_exit(&mh->m_lock); 3819860Sgdamore@opensolaris.org } 3829860Sgdamore@opensolaris.org 3839860Sgdamore@opensolaris.org void 3849860Sgdamore@opensolaris.org mii_stop(mii_handle_t mh) 3859860Sgdamore@opensolaris.org { 3869860Sgdamore@opensolaris.org mutex_enter(&mh->m_lock); 3879860Sgdamore@opensolaris.org mh->m_started = B_FALSE; 3889860Sgdamore@opensolaris.org cv_broadcast(&mh->m_cv); 3899860Sgdamore@opensolaris.org mutex_exit(&mh->m_lock); 3909860Sgdamore@opensolaris.org } 3919860Sgdamore@opensolaris.org 3929860Sgdamore@opensolaris.org void 3939860Sgdamore@opensolaris.org mii_probe(mii_handle_t mh) 3949860Sgdamore@opensolaris.org { 3959860Sgdamore@opensolaris.org mutex_enter(&mh->m_lock); 3969860Sgdamore@opensolaris.org _mii_probe_task(mh); 3979860Sgdamore@opensolaris.org mutex_exit(&mh->m_lock); 3989860Sgdamore@opensolaris.org } 3999860Sgdamore@opensolaris.org 4009860Sgdamore@opensolaris.org void 4019860Sgdamore@opensolaris.org mii_check(mii_handle_t mh) 4029860Sgdamore@opensolaris.org { 4039860Sgdamore@opensolaris.org mutex_enter(&mh->m_lock); 4049860Sgdamore@opensolaris.org cv_broadcast(&mh->m_cv); 4059860Sgdamore@opensolaris.org mutex_exit(&mh->m_lock); 4069860Sgdamore@opensolaris.org } 4079860Sgdamore@opensolaris.org 4089860Sgdamore@opensolaris.org int 4099860Sgdamore@opensolaris.org mii_get_speed(mii_handle_t mh) 4109860Sgdamore@opensolaris.org { 4119860Sgdamore@opensolaris.org phy_handle_t *ph = mh->m_phy; 4129860Sgdamore@opensolaris.org 4139860Sgdamore@opensolaris.org return (ph->phy_speed); 4149860Sgdamore@opensolaris.org } 4159860Sgdamore@opensolaris.org 4169860Sgdamore@opensolaris.org link_duplex_t 4179860Sgdamore@opensolaris.org mii_get_duplex(mii_handle_t mh) 4189860Sgdamore@opensolaris.org { 4199860Sgdamore@opensolaris.org phy_handle_t *ph = mh->m_phy; 4209860Sgdamore@opensolaris.org 4219860Sgdamore@opensolaris.org return (ph->phy_duplex); 4229860Sgdamore@opensolaris.org } 4239860Sgdamore@opensolaris.org 4249860Sgdamore@opensolaris.org link_state_t 4259860Sgdamore@opensolaris.org mii_get_state(mii_handle_t mh) 4269860Sgdamore@opensolaris.org { 4279860Sgdamore@opensolaris.org phy_handle_t *ph = mh->m_phy; 4289860Sgdamore@opensolaris.org 4299860Sgdamore@opensolaris.org return (ph->phy_link); 4309860Sgdamore@opensolaris.org } 4319860Sgdamore@opensolaris.org 4329860Sgdamore@opensolaris.org link_flowctrl_t 4339860Sgdamore@opensolaris.org mii_get_flowctrl(mii_handle_t mh) 4349860Sgdamore@opensolaris.org { 4359860Sgdamore@opensolaris.org phy_handle_t *ph = mh->m_phy; 4369860Sgdamore@opensolaris.org 4379860Sgdamore@opensolaris.org return (ph->phy_flowctrl); 4389860Sgdamore@opensolaris.org } 4399860Sgdamore@opensolaris.org 4409860Sgdamore@opensolaris.org int 4419860Sgdamore@opensolaris.org mii_get_loopmodes(mii_handle_t mh, lb_property_t *modes) 4429860Sgdamore@opensolaris.org { 4439860Sgdamore@opensolaris.org phy_handle_t *ph = mh->m_phy; 4449860Sgdamore@opensolaris.org int cnt; 4459860Sgdamore@opensolaris.org 4469860Sgdamore@opensolaris.org /* 4479860Sgdamore@opensolaris.org * There's a pretty big assumption in here. The assumption is 4489860Sgdamore@opensolaris.org * that for 1000BASE-T and 100BASE-TX modes that we can 4499860Sgdamore@opensolaris.org * support all of the lower speeds and full duplex uniformly. 4509860Sgdamore@opensolaris.org */ 4519860Sgdamore@opensolaris.org switch (ph->phy_type) { 4529860Sgdamore@opensolaris.org case XCVR_1000T: 4539860Sgdamore@opensolaris.org cnt = 5; /* 1000 BASE-T should support all modes */ 4549860Sgdamore@opensolaris.org break; 4559860Sgdamore@opensolaris.org case XCVR_100X: 4569860Sgdamore@opensolaris.org /* 4579860Sgdamore@opensolaris.org * This might be 100BASE-FX. If so, it won't support the 4589860Sgdamore@opensolaris.org * 10Mbps modes. 4599860Sgdamore@opensolaris.org */ 4609860Sgdamore@opensolaris.org if (ph->phy_cap_10_fdx) { 4619860Sgdamore@opensolaris.org cnt = 4; /* 100BASE-TX probably */ 4629860Sgdamore@opensolaris.org } else { 4639860Sgdamore@opensolaris.org cnt = 2; /* 100BASE-FX? */ 4649860Sgdamore@opensolaris.org } 4659860Sgdamore@opensolaris.org break; 4669860Sgdamore@opensolaris.org case XCVR_10: 4679860Sgdamore@opensolaris.org /* Some 10Mbs PHYs don't do full duplex */ 4689860Sgdamore@opensolaris.org if (ph->phy_cap_10_fdx) { 4699860Sgdamore@opensolaris.org cnt = 3; 4709860Sgdamore@opensolaris.org } else { 4719860Sgdamore@opensolaris.org cnt = 2; 4729860Sgdamore@opensolaris.org } 4739860Sgdamore@opensolaris.org break; 4749860Sgdamore@opensolaris.org case XCVR_NONE: 4759860Sgdamore@opensolaris.org cnt = 1; /* No XCVR, all we can do is leave it off */ 4769860Sgdamore@opensolaris.org break; 4779860Sgdamore@opensolaris.org case XCVR_1000X: 4789860Sgdamore@opensolaris.org case XCVR_100T4: 4799860Sgdamore@opensolaris.org case XCVR_100T2: 4809860Sgdamore@opensolaris.org default: 4819860Sgdamore@opensolaris.org /* 4829860Sgdamore@opensolaris.org * Anything else we just allow for internal PHY loopback. 4839860Sgdamore@opensolaris.org * Note that many of these will probably be capable of 4849860Sgdamore@opensolaris.org * supporting additional external loopback modes, but we 4859860Sgdamore@opensolaris.org * can't be sure of it, so we just leave it alone. 4869860Sgdamore@opensolaris.org */ 4879860Sgdamore@opensolaris.org cnt = 2; 4889860Sgdamore@opensolaris.org break; 4899860Sgdamore@opensolaris.org } 4909860Sgdamore@opensolaris.org 4919860Sgdamore@opensolaris.org if (modes) { 4929860Sgdamore@opensolaris.org bcopy(mii_loopmodes, modes, sizeof (lb_property_t) * cnt); 4939860Sgdamore@opensolaris.org } 4949860Sgdamore@opensolaris.org return (cnt); 4959860Sgdamore@opensolaris.org } 4969860Sgdamore@opensolaris.org 4979860Sgdamore@opensolaris.org uint32_t 4989860Sgdamore@opensolaris.org mii_get_loopback(mii_handle_t mh) 4999860Sgdamore@opensolaris.org { 5009860Sgdamore@opensolaris.org phy_handle_t *ph = mh->m_phy; 5019860Sgdamore@opensolaris.org 5029860Sgdamore@opensolaris.org return (ph->phy_loopback); 5039860Sgdamore@opensolaris.org } 5049860Sgdamore@opensolaris.org 5059860Sgdamore@opensolaris.org int 5069860Sgdamore@opensolaris.org mii_set_loopback(mii_handle_t mh, uint32_t loop) 5079860Sgdamore@opensolaris.org { 5089860Sgdamore@opensolaris.org phy_handle_t *ph; 5099860Sgdamore@opensolaris.org 5109860Sgdamore@opensolaris.org mutex_enter(&mh->m_lock); 5119860Sgdamore@opensolaris.org ph = mh->m_phy; 5129860Sgdamore@opensolaris.org 5139860Sgdamore@opensolaris.org if ((!mh->m_started) || (!ph->phy_present) || 5149860Sgdamore@opensolaris.org (loop >= mii_get_loopmodes(mh, NULL))) { 5159860Sgdamore@opensolaris.org return (EINVAL); 5169860Sgdamore@opensolaris.org } 5179860Sgdamore@opensolaris.org 5189860Sgdamore@opensolaris.org ph->phy_loopback = loop; 5199860Sgdamore@opensolaris.org mh->m_tstate = MII_STATE_RESET; 5209860Sgdamore@opensolaris.org cv_broadcast(&mh->m_cv); 5219860Sgdamore@opensolaris.org mutex_exit(&mh->m_lock); 5229860Sgdamore@opensolaris.org 5239860Sgdamore@opensolaris.org return (0); 5249860Sgdamore@opensolaris.org } 5259860Sgdamore@opensolaris.org 5269860Sgdamore@opensolaris.org uint32_t 5279860Sgdamore@opensolaris.org mii_get_id(mii_handle_t mh) 5289860Sgdamore@opensolaris.org { 5299860Sgdamore@opensolaris.org phy_handle_t *ph = mh->m_phy; 5309860Sgdamore@opensolaris.org 5319860Sgdamore@opensolaris.org return (ph->phy_id); 5329860Sgdamore@opensolaris.org } 5339860Sgdamore@opensolaris.org 5349860Sgdamore@opensolaris.org int 5359860Sgdamore@opensolaris.org mii_get_addr(mii_handle_t mh) 5369860Sgdamore@opensolaris.org { 5379860Sgdamore@opensolaris.org return (mh->m_addr); 5389860Sgdamore@opensolaris.org } 5399860Sgdamore@opensolaris.org 5409860Sgdamore@opensolaris.org /* GLDv3 helpers */ 5419860Sgdamore@opensolaris.org 5429860Sgdamore@opensolaris.org boolean_t 5439860Sgdamore@opensolaris.org mii_m_loop_ioctl(mii_handle_t mh, queue_t *wq, mblk_t *mp) 5449860Sgdamore@opensolaris.org { 5459860Sgdamore@opensolaris.org struct iocblk *iocp; 5469860Sgdamore@opensolaris.org int rv = 0; 5479860Sgdamore@opensolaris.org int cnt; 5489860Sgdamore@opensolaris.org lb_property_t modes[MII_LOOPBACK_MAX]; 5499860Sgdamore@opensolaris.org lb_info_sz_t sz; 5509860Sgdamore@opensolaris.org int cmd; 5519860Sgdamore@opensolaris.org uint32_t mode; 5529860Sgdamore@opensolaris.org 5539860Sgdamore@opensolaris.org iocp = (void *)mp->b_rptr; 5549860Sgdamore@opensolaris.org cmd = iocp->ioc_cmd; 5559860Sgdamore@opensolaris.org 5569860Sgdamore@opensolaris.org switch (cmd) { 5579860Sgdamore@opensolaris.org case LB_SET_MODE: 5589860Sgdamore@opensolaris.org case LB_GET_INFO_SIZE: 5599860Sgdamore@opensolaris.org case LB_GET_INFO: 5609860Sgdamore@opensolaris.org case LB_GET_MODE: 5619860Sgdamore@opensolaris.org break; 5629860Sgdamore@opensolaris.org 5639860Sgdamore@opensolaris.org default: 5649860Sgdamore@opensolaris.org return (B_FALSE); 5659860Sgdamore@opensolaris.org } 5669860Sgdamore@opensolaris.org 5679860Sgdamore@opensolaris.org if (mp->b_cont == NULL) { 5689860Sgdamore@opensolaris.org miocnak(wq, mp, 0, EINVAL); 5699860Sgdamore@opensolaris.org return (B_TRUE); 5709860Sgdamore@opensolaris.org } 5719860Sgdamore@opensolaris.org 5729860Sgdamore@opensolaris.org switch (cmd) { 5739860Sgdamore@opensolaris.org case LB_GET_INFO_SIZE: 5749860Sgdamore@opensolaris.org cnt = mii_get_loopmodes(mh, modes); 5759860Sgdamore@opensolaris.org if (iocp->ioc_count != sizeof (sz)) { 5769860Sgdamore@opensolaris.org rv = EINVAL; 5779860Sgdamore@opensolaris.org } else { 5789860Sgdamore@opensolaris.org sz = cnt * sizeof (lb_property_t); 5799860Sgdamore@opensolaris.org bcopy(&sz, mp->b_cont->b_rptr, sizeof (sz)); 5809860Sgdamore@opensolaris.org } 5819860Sgdamore@opensolaris.org break; 5829860Sgdamore@opensolaris.org 5839860Sgdamore@opensolaris.org case LB_GET_INFO: 5849860Sgdamore@opensolaris.org cnt = mii_get_loopmodes(mh, modes); 5859860Sgdamore@opensolaris.org if (iocp->ioc_count != (cnt * sizeof (lb_property_t))) { 5869860Sgdamore@opensolaris.org rv = EINVAL; 5879860Sgdamore@opensolaris.org } else { 5889860Sgdamore@opensolaris.org bcopy(modes, mp->b_cont->b_rptr, iocp->ioc_count); 5899860Sgdamore@opensolaris.org } 5909860Sgdamore@opensolaris.org break; 5919860Sgdamore@opensolaris.org 5929860Sgdamore@opensolaris.org case LB_GET_MODE: 5939860Sgdamore@opensolaris.org if (iocp->ioc_count != sizeof (mode)) { 5949860Sgdamore@opensolaris.org rv = EINVAL; 5959860Sgdamore@opensolaris.org } else { 5969860Sgdamore@opensolaris.org mode = mii_get_loopback(mh); 5979860Sgdamore@opensolaris.org bcopy(&mode, mp->b_cont->b_rptr, sizeof (mode)); 5989860Sgdamore@opensolaris.org } 5999860Sgdamore@opensolaris.org break; 6009860Sgdamore@opensolaris.org 6019860Sgdamore@opensolaris.org case LB_SET_MODE: 6029860Sgdamore@opensolaris.org rv = secpolicy_net_config(iocp->ioc_cr, B_FALSE); 6039860Sgdamore@opensolaris.org if (rv != 0) 6049860Sgdamore@opensolaris.org break; 6059860Sgdamore@opensolaris.org if (iocp->ioc_count != sizeof (mode)) { 6069860Sgdamore@opensolaris.org rv = EINVAL; 6079860Sgdamore@opensolaris.org break; 6089860Sgdamore@opensolaris.org } 6099860Sgdamore@opensolaris.org bcopy(mp->b_cont->b_rptr, &mode, sizeof (mode)); 6109860Sgdamore@opensolaris.org rv = mii_set_loopback(mh, mode); 6119860Sgdamore@opensolaris.org break; 6129860Sgdamore@opensolaris.org } 6139860Sgdamore@opensolaris.org 6149860Sgdamore@opensolaris.org if (rv == 0) { 6159860Sgdamore@opensolaris.org miocack(wq, mp, iocp->ioc_count, 0); 6169860Sgdamore@opensolaris.org } else { 6179860Sgdamore@opensolaris.org miocnak(wq, mp, 0, rv); 6189860Sgdamore@opensolaris.org } 6199860Sgdamore@opensolaris.org return (B_TRUE); 6209860Sgdamore@opensolaris.org } 6219860Sgdamore@opensolaris.org 6229860Sgdamore@opensolaris.org int 6239860Sgdamore@opensolaris.org mii_m_getprop(mii_handle_t mh, const char *name, mac_prop_id_t num, 6249860Sgdamore@opensolaris.org uint_t flags, uint_t sz, void *val, uint_t *permp) 6259860Sgdamore@opensolaris.org { 6269860Sgdamore@opensolaris.org phy_handle_t *ph; 6279860Sgdamore@opensolaris.org int err = 0; 6289860Sgdamore@opensolaris.org uint_t perm; 6299860Sgdamore@opensolaris.org boolean_t dfl = flags & MAC_PROP_DEFAULT; 6309860Sgdamore@opensolaris.org 6319860Sgdamore@opensolaris.org _NOTE(ARGUNUSED(name)); 6329860Sgdamore@opensolaris.org 6339860Sgdamore@opensolaris.org if (sz < 1) 6349860Sgdamore@opensolaris.org return (EINVAL); 6359860Sgdamore@opensolaris.org 6369860Sgdamore@opensolaris.org mutex_enter(&mh->m_lock); 6379860Sgdamore@opensolaris.org 6389860Sgdamore@opensolaris.org ph = mh->m_phy; 6399860Sgdamore@opensolaris.org perm = MAC_PROP_PERM_RW; 6409860Sgdamore@opensolaris.org 6419860Sgdamore@opensolaris.org #define CASE_PROP_ABILITY(PROP, VAR) \ 6429860Sgdamore@opensolaris.org case MAC_PROP_ADV_##PROP: \ 6439860Sgdamore@opensolaris.org perm = MAC_PROP_PERM_READ; \ 6449860Sgdamore@opensolaris.org *(uint8_t *)val = \ 6459860Sgdamore@opensolaris.org dfl ? ph->phy_cap_##VAR : ph->phy_adv_##VAR; \ 6469860Sgdamore@opensolaris.org break; \ 6479860Sgdamore@opensolaris.org \ 6489860Sgdamore@opensolaris.org case MAC_PROP_EN_##PROP: \ 6499860Sgdamore@opensolaris.org if (!ph->phy_cap_##VAR) \ 6509860Sgdamore@opensolaris.org perm = MAC_PROP_PERM_READ; \ 6519860Sgdamore@opensolaris.org *(uint8_t *)val = \ 6529860Sgdamore@opensolaris.org dfl ? ph->phy_cap_##VAR : ph->phy_en_##VAR; \ 6539860Sgdamore@opensolaris.org break; 6549860Sgdamore@opensolaris.org 6559860Sgdamore@opensolaris.org switch (num) { 6569860Sgdamore@opensolaris.org case MAC_PROP_DUPLEX: 6579860Sgdamore@opensolaris.org perm = MAC_PROP_PERM_READ; 6589860Sgdamore@opensolaris.org if (sz >= sizeof (link_duplex_t)) { 6599860Sgdamore@opensolaris.org bcopy(&ph->phy_duplex, val, sizeof (link_duplex_t)); 6609860Sgdamore@opensolaris.org } else { 6619860Sgdamore@opensolaris.org err = EINVAL; 6629860Sgdamore@opensolaris.org } 6639860Sgdamore@opensolaris.org break; 6649860Sgdamore@opensolaris.org 6659860Sgdamore@opensolaris.org case MAC_PROP_SPEED: 6669860Sgdamore@opensolaris.org perm = MAC_PROP_PERM_READ; 6679860Sgdamore@opensolaris.org if (sz >= sizeof (uint64_t)) { 6689860Sgdamore@opensolaris.org uint64_t speed = ph->phy_speed * 1000000ull; 6699860Sgdamore@opensolaris.org bcopy(&speed, val, sizeof (speed)); 6709860Sgdamore@opensolaris.org } else { 6719860Sgdamore@opensolaris.org err = EINVAL; 6729860Sgdamore@opensolaris.org } 6739860Sgdamore@opensolaris.org break; 6749860Sgdamore@opensolaris.org 6759860Sgdamore@opensolaris.org case MAC_PROP_AUTONEG: 6769860Sgdamore@opensolaris.org *(uint8_t *)val = 6779860Sgdamore@opensolaris.org dfl ? ph->phy_cap_aneg : ph->phy_adv_aneg; 6789860Sgdamore@opensolaris.org break; 6799860Sgdamore@opensolaris.org 6809860Sgdamore@opensolaris.org case MAC_PROP_FLOWCTRL: 6819860Sgdamore@opensolaris.org if (sz >= sizeof (link_flowctrl_t)) { 6829860Sgdamore@opensolaris.org bcopy(&ph->phy_flowctrl, val, 6839860Sgdamore@opensolaris.org sizeof (link_flowctrl_t)); 6849860Sgdamore@opensolaris.org } else { 6859860Sgdamore@opensolaris.org err = EINVAL; 6869860Sgdamore@opensolaris.org } 6879860Sgdamore@opensolaris.org break; 6889860Sgdamore@opensolaris.org 6899860Sgdamore@opensolaris.org CASE_PROP_ABILITY(1000FDX_CAP, 1000_fdx) 6909860Sgdamore@opensolaris.org CASE_PROP_ABILITY(1000HDX_CAP, 1000_hdx) 6919860Sgdamore@opensolaris.org CASE_PROP_ABILITY(100T4_CAP, 100_t4) 6929860Sgdamore@opensolaris.org CASE_PROP_ABILITY(100FDX_CAP, 100_fdx) 6939860Sgdamore@opensolaris.org CASE_PROP_ABILITY(100HDX_CAP, 100_hdx) 6949860Sgdamore@opensolaris.org CASE_PROP_ABILITY(10FDX_CAP, 10_fdx) 6959860Sgdamore@opensolaris.org CASE_PROP_ABILITY(10HDX_CAP, 10_hdx) 6969860Sgdamore@opensolaris.org 6979860Sgdamore@opensolaris.org default: 6989860Sgdamore@opensolaris.org err = ENOTSUP; 6999860Sgdamore@opensolaris.org break; 7009860Sgdamore@opensolaris.org } 7019860Sgdamore@opensolaris.org 7029860Sgdamore@opensolaris.org if (err == 0) { 7039860Sgdamore@opensolaris.org *permp = perm; 7049860Sgdamore@opensolaris.org } 7059860Sgdamore@opensolaris.org 7069860Sgdamore@opensolaris.org mutex_exit(&mh->m_lock); 7079860Sgdamore@opensolaris.org 7089860Sgdamore@opensolaris.org return (err); 7099860Sgdamore@opensolaris.org } 7109860Sgdamore@opensolaris.org 7119860Sgdamore@opensolaris.org int 7129860Sgdamore@opensolaris.org mii_m_setprop(mii_handle_t mh, const char *name, mac_prop_id_t num, 7139860Sgdamore@opensolaris.org uint_t sz, const void *valp) 7149860Sgdamore@opensolaris.org { 7159860Sgdamore@opensolaris.org phy_handle_t *ph; 7169860Sgdamore@opensolaris.org boolean_t *advp = NULL; 7179860Sgdamore@opensolaris.org boolean_t *capp = NULL; 7189860Sgdamore@opensolaris.org int *macpp = NULL; 7199860Sgdamore@opensolaris.org int rv = ENOTSUP; 7209860Sgdamore@opensolaris.org 7219860Sgdamore@opensolaris.org _NOTE(ARGUNUSED(name)); 7229860Sgdamore@opensolaris.org 7239860Sgdamore@opensolaris.org if (sz < 1) 7249860Sgdamore@opensolaris.org return (EINVAL); 7259860Sgdamore@opensolaris.org 7269860Sgdamore@opensolaris.org mutex_enter(&mh->m_lock); 7279860Sgdamore@opensolaris.org 7289860Sgdamore@opensolaris.org ph = mh->m_phy; 7299860Sgdamore@opensolaris.org 7309860Sgdamore@opensolaris.org /* we don't support changing parameters while in loopback mode */ 7319860Sgdamore@opensolaris.org if (ph->phy_loopback != PHY_LB_NONE) { 7329860Sgdamore@opensolaris.org switch (num) { 7339860Sgdamore@opensolaris.org case MAC_PROP_EN_1000FDX_CAP: 7349860Sgdamore@opensolaris.org case MAC_PROP_EN_1000HDX_CAP: 7359860Sgdamore@opensolaris.org case MAC_PROP_EN_100FDX_CAP: 7369860Sgdamore@opensolaris.org case MAC_PROP_EN_100HDX_CAP: 7379860Sgdamore@opensolaris.org case MAC_PROP_EN_100T4_CAP: 7389860Sgdamore@opensolaris.org case MAC_PROP_EN_10FDX_CAP: 7399860Sgdamore@opensolaris.org case MAC_PROP_EN_10HDX_CAP: 7409860Sgdamore@opensolaris.org case MAC_PROP_AUTONEG: 7419860Sgdamore@opensolaris.org case MAC_PROP_FLOWCTRL: 7429860Sgdamore@opensolaris.org return (EBUSY); 7439860Sgdamore@opensolaris.org } 7449860Sgdamore@opensolaris.org } 7459860Sgdamore@opensolaris.org 7469860Sgdamore@opensolaris.org switch (num) { 7479860Sgdamore@opensolaris.org case MAC_PROP_EN_1000FDX_CAP: 7489860Sgdamore@opensolaris.org capp = &ph->phy_cap_1000_fdx; 7499860Sgdamore@opensolaris.org advp = &ph->phy_en_1000_fdx; 7509860Sgdamore@opensolaris.org macpp = &mh->m_en_1000_fdx; 7519860Sgdamore@opensolaris.org break; 7529860Sgdamore@opensolaris.org case MAC_PROP_EN_1000HDX_CAP: 7539860Sgdamore@opensolaris.org capp = &ph->phy_cap_1000_hdx; 7549860Sgdamore@opensolaris.org advp = &ph->phy_en_1000_hdx; 7559860Sgdamore@opensolaris.org macpp = &mh->m_en_1000_hdx; 7569860Sgdamore@opensolaris.org break; 7579860Sgdamore@opensolaris.org case MAC_PROP_EN_100FDX_CAP: 7589860Sgdamore@opensolaris.org capp = &ph->phy_cap_100_fdx; 7599860Sgdamore@opensolaris.org advp = &ph->phy_en_100_fdx; 7609860Sgdamore@opensolaris.org macpp = &mh->m_en_100_fdx; 7619860Sgdamore@opensolaris.org break; 7629860Sgdamore@opensolaris.org case MAC_PROP_EN_100HDX_CAP: 7639860Sgdamore@opensolaris.org capp = &ph->phy_cap_100_hdx; 7649860Sgdamore@opensolaris.org advp = &ph->phy_en_100_hdx; 7659860Sgdamore@opensolaris.org macpp = &mh->m_en_100_hdx; 7669860Sgdamore@opensolaris.org break; 7679860Sgdamore@opensolaris.org case MAC_PROP_EN_100T4_CAP: 7689860Sgdamore@opensolaris.org capp = &ph->phy_cap_100_t4; 7699860Sgdamore@opensolaris.org advp = &ph->phy_en_100_t4; 7709860Sgdamore@opensolaris.org macpp = &mh->m_en_100_t4; 7719860Sgdamore@opensolaris.org break; 7729860Sgdamore@opensolaris.org case MAC_PROP_EN_10FDX_CAP: 7739860Sgdamore@opensolaris.org capp = &ph->phy_cap_10_fdx; 7749860Sgdamore@opensolaris.org advp = &ph->phy_en_10_fdx; 7759860Sgdamore@opensolaris.org macpp = &mh->m_en_10_fdx; 7769860Sgdamore@opensolaris.org break; 7779860Sgdamore@opensolaris.org case MAC_PROP_EN_10HDX_CAP: 7789860Sgdamore@opensolaris.org capp = &ph->phy_cap_10_hdx; 7799860Sgdamore@opensolaris.org advp = &ph->phy_en_10_hdx; 7809860Sgdamore@opensolaris.org macpp = &mh->m_en_10_hdx; 7819860Sgdamore@opensolaris.org break; 7829860Sgdamore@opensolaris.org case MAC_PROP_AUTONEG: 7839860Sgdamore@opensolaris.org capp = &ph->phy_cap_aneg; 7849860Sgdamore@opensolaris.org advp = &ph->phy_en_aneg; 7859860Sgdamore@opensolaris.org macpp = &mh->m_en_aneg; 7869860Sgdamore@opensolaris.org break; 7879860Sgdamore@opensolaris.org case MAC_PROP_FLOWCTRL: 7889860Sgdamore@opensolaris.org if (sz < sizeof (link_flowctrl_t)) { 7899860Sgdamore@opensolaris.org rv = EINVAL; 7909860Sgdamore@opensolaris.org } else { 7919860Sgdamore@opensolaris.org link_flowctrl_t fc; 7929860Sgdamore@opensolaris.org boolean_t chg; 7939860Sgdamore@opensolaris.org 7949860Sgdamore@opensolaris.org bcopy(valp, &fc, sizeof (fc)); 7959860Sgdamore@opensolaris.org 7969860Sgdamore@opensolaris.org chg = fc == ph->phy_en_flowctrl ? B_FALSE : B_TRUE; 7979860Sgdamore@opensolaris.org switch (fc) { 7989860Sgdamore@opensolaris.org case LINK_FLOWCTRL_NONE: 7999860Sgdamore@opensolaris.org ph->phy_en_pause = B_FALSE; 8009860Sgdamore@opensolaris.org ph->phy_en_asmpause = B_FALSE; 8019860Sgdamore@opensolaris.org ph->phy_en_flowctrl = fc; 8029860Sgdamore@opensolaris.org break; 8039860Sgdamore@opensolaris.org /* 8049860Sgdamore@opensolaris.org * Note that while we don't have a way to 8059860Sgdamore@opensolaris.org * advertise that we can RX pause (we just 8069860Sgdamore@opensolaris.org * won't send pause frames), we advertise full 8079860Sgdamore@opensolaris.org * support. The MAC driver will learn of the 8089860Sgdamore@opensolaris.org * configuration via the saved value of the 8099860Sgdamore@opensolaris.org * tunable. 8109860Sgdamore@opensolaris.org */ 8119860Sgdamore@opensolaris.org case LINK_FLOWCTRL_BI: 8129860Sgdamore@opensolaris.org case LINK_FLOWCTRL_RX: 8139860Sgdamore@opensolaris.org if (ph->phy_cap_pause) { 8149860Sgdamore@opensolaris.org ph->phy_en_pause = B_TRUE; 8159860Sgdamore@opensolaris.org ph->phy_en_asmpause = B_TRUE; 8169860Sgdamore@opensolaris.org ph->phy_en_flowctrl = fc; 8179860Sgdamore@opensolaris.org } else { 8189860Sgdamore@opensolaris.org rv = EINVAL; 8199860Sgdamore@opensolaris.org } 8209860Sgdamore@opensolaris.org break; 8219860Sgdamore@opensolaris.org 8229860Sgdamore@opensolaris.org /* 8239860Sgdamore@opensolaris.org * Tell the other side that we can assert 8249860Sgdamore@opensolaris.org * pause, but we cannot resend. 8259860Sgdamore@opensolaris.org */ 8269860Sgdamore@opensolaris.org case LINK_FLOWCTRL_TX: 8279860Sgdamore@opensolaris.org if (ph->phy_cap_asmpause) { 8289860Sgdamore@opensolaris.org ph->phy_en_pause = B_FALSE; 8299860Sgdamore@opensolaris.org ph->phy_en_flowctrl = fc; 8309860Sgdamore@opensolaris.org ph->phy_en_asmpause = B_TRUE; 8319860Sgdamore@opensolaris.org } else { 8329860Sgdamore@opensolaris.org rv = EINVAL; 8339860Sgdamore@opensolaris.org } 8349860Sgdamore@opensolaris.org break; 8359860Sgdamore@opensolaris.org default: 8369860Sgdamore@opensolaris.org rv = EINVAL; 8379860Sgdamore@opensolaris.org break; 8389860Sgdamore@opensolaris.org } 8399860Sgdamore@opensolaris.org if ((rv == 0) && chg) { 8409860Sgdamore@opensolaris.org mh->m_en_flowctrl = fc; 8419860Sgdamore@opensolaris.org mh->m_tstate = MII_STATE_RESET; 8429860Sgdamore@opensolaris.org cv_broadcast(&mh->m_cv); 8439860Sgdamore@opensolaris.org } 8449860Sgdamore@opensolaris.org } 8459860Sgdamore@opensolaris.org break; 8469860Sgdamore@opensolaris.org 8479860Sgdamore@opensolaris.org default: 8489860Sgdamore@opensolaris.org rv = ENOTSUP; 8499860Sgdamore@opensolaris.org break; 8509860Sgdamore@opensolaris.org } 8519860Sgdamore@opensolaris.org 8529860Sgdamore@opensolaris.org if (capp && advp && macpp) { 8539860Sgdamore@opensolaris.org if (sz < sizeof (uint8_t)) { 8549860Sgdamore@opensolaris.org rv = EINVAL; 8559860Sgdamore@opensolaris.org 8569860Sgdamore@opensolaris.org } else if (*capp) { 8579860Sgdamore@opensolaris.org if (*advp != *(uint8_t *)valp) { 8589860Sgdamore@opensolaris.org *advp = *(uint8_t *)valp; 8599860Sgdamore@opensolaris.org *macpp = *(uint8_t *)valp; 8609860Sgdamore@opensolaris.org mh->m_tstate = MII_STATE_RESET; 8619860Sgdamore@opensolaris.org cv_broadcast(&mh->m_cv); 8629860Sgdamore@opensolaris.org } 8639860Sgdamore@opensolaris.org rv = 0; 8649860Sgdamore@opensolaris.org } 8659860Sgdamore@opensolaris.org } 8669860Sgdamore@opensolaris.org 8679860Sgdamore@opensolaris.org mutex_exit(&mh->m_lock); 8689860Sgdamore@opensolaris.org return (rv); 8699860Sgdamore@opensolaris.org } 8709860Sgdamore@opensolaris.org 8719860Sgdamore@opensolaris.org int 8729860Sgdamore@opensolaris.org mii_m_getstat(mii_handle_t mh, uint_t stat, uint64_t *val) 8739860Sgdamore@opensolaris.org { 8749860Sgdamore@opensolaris.org phy_handle_t *ph; 8759860Sgdamore@opensolaris.org int rv = 0; 8769860Sgdamore@opensolaris.org 8779860Sgdamore@opensolaris.org mutex_enter(&mh->m_lock); 8789860Sgdamore@opensolaris.org 8799860Sgdamore@opensolaris.org ph = mh->m_phy; 8809860Sgdamore@opensolaris.org 8819860Sgdamore@opensolaris.org switch (stat) { 8829860Sgdamore@opensolaris.org case MAC_STAT_IFSPEED: 8839860Sgdamore@opensolaris.org *val = ph->phy_speed * 1000000ull; 8849860Sgdamore@opensolaris.org break; 8859860Sgdamore@opensolaris.org case ETHER_STAT_LINK_DUPLEX: 8869860Sgdamore@opensolaris.org *val = ph->phy_duplex; 8879860Sgdamore@opensolaris.org break; 8889860Sgdamore@opensolaris.org case ETHER_STAT_LINK_AUTONEG: 8899860Sgdamore@opensolaris.org *val = !!(ph->phy_adv_aneg && ph->phy_lp_aneg); 8909860Sgdamore@opensolaris.org break; 8919860Sgdamore@opensolaris.org case ETHER_STAT_XCVR_ID: 8929860Sgdamore@opensolaris.org *val = ph->phy_id; 8939860Sgdamore@opensolaris.org break; 8949860Sgdamore@opensolaris.org case ETHER_STAT_XCVR_INUSE: 8959860Sgdamore@opensolaris.org *val = ph->phy_type; 8969860Sgdamore@opensolaris.org break; 8979860Sgdamore@opensolaris.org case ETHER_STAT_XCVR_ADDR: 8989860Sgdamore@opensolaris.org *val = ph->phy_addr; 8999860Sgdamore@opensolaris.org break; 9009860Sgdamore@opensolaris.org case ETHER_STAT_LINK_ASMPAUSE: 9019860Sgdamore@opensolaris.org *val = ph->phy_adv_asmpause && ph->phy_lp_asmpause && 9029860Sgdamore@opensolaris.org ph->phy_adv_pause != ph->phy_lp_pause; 9039860Sgdamore@opensolaris.org break; 9049860Sgdamore@opensolaris.org case ETHER_STAT_LINK_PAUSE: 9059860Sgdamore@opensolaris.org *val = (ph->phy_flowctrl == LINK_FLOWCTRL_BI) || 9069860Sgdamore@opensolaris.org (ph->phy_flowctrl == LINK_FLOWCTRL_RX); 9079860Sgdamore@opensolaris.org break; 9089860Sgdamore@opensolaris.org case ETHER_STAT_CAP_1000FDX: 9099860Sgdamore@opensolaris.org *val = ph->phy_cap_1000_fdx; 9109860Sgdamore@opensolaris.org break; 9119860Sgdamore@opensolaris.org case ETHER_STAT_CAP_1000HDX: 9129860Sgdamore@opensolaris.org *val = ph->phy_cap_1000_hdx; 9139860Sgdamore@opensolaris.org break; 9149860Sgdamore@opensolaris.org case ETHER_STAT_CAP_100FDX: 9159860Sgdamore@opensolaris.org *val = ph->phy_cap_100_fdx; 9169860Sgdamore@opensolaris.org break; 9179860Sgdamore@opensolaris.org case ETHER_STAT_CAP_100HDX: 9189860Sgdamore@opensolaris.org *val = ph->phy_cap_100_hdx; 9199860Sgdamore@opensolaris.org break; 9209860Sgdamore@opensolaris.org case ETHER_STAT_CAP_10FDX: 9219860Sgdamore@opensolaris.org *val = ph->phy_cap_10_fdx; 9229860Sgdamore@opensolaris.org break; 9239860Sgdamore@opensolaris.org case ETHER_STAT_CAP_10HDX: 9249860Sgdamore@opensolaris.org *val = ph->phy_cap_10_hdx; 9259860Sgdamore@opensolaris.org break; 9269860Sgdamore@opensolaris.org case ETHER_STAT_CAP_100T4: 9279860Sgdamore@opensolaris.org *val = ph->phy_cap_100_t4; 9289860Sgdamore@opensolaris.org break; 9299860Sgdamore@opensolaris.org case ETHER_STAT_CAP_AUTONEG: 9309860Sgdamore@opensolaris.org *val = ph->phy_cap_aneg; 9319860Sgdamore@opensolaris.org break; 9329860Sgdamore@opensolaris.org case ETHER_STAT_CAP_PAUSE: 9339860Sgdamore@opensolaris.org *val = ph->phy_cap_pause; 9349860Sgdamore@opensolaris.org break; 9359860Sgdamore@opensolaris.org case ETHER_STAT_CAP_ASMPAUSE: 9369860Sgdamore@opensolaris.org *val = ph->phy_cap_asmpause; 9379860Sgdamore@opensolaris.org break; 9389860Sgdamore@opensolaris.org 9399860Sgdamore@opensolaris.org case ETHER_STAT_LP_CAP_1000FDX: 9409860Sgdamore@opensolaris.org *val = ph->phy_lp_1000_fdx; 9419860Sgdamore@opensolaris.org break; 9429860Sgdamore@opensolaris.org case ETHER_STAT_LP_CAP_1000HDX: 9439860Sgdamore@opensolaris.org *val = ph->phy_lp_1000_hdx; 9449860Sgdamore@opensolaris.org break; 9459860Sgdamore@opensolaris.org case ETHER_STAT_LP_CAP_100FDX: 9469860Sgdamore@opensolaris.org *val = ph->phy_lp_100_fdx; 9479860Sgdamore@opensolaris.org break; 9489860Sgdamore@opensolaris.org case ETHER_STAT_LP_CAP_100HDX: 9499860Sgdamore@opensolaris.org *val = ph->phy_lp_100_hdx; 9509860Sgdamore@opensolaris.org break; 9519860Sgdamore@opensolaris.org case ETHER_STAT_LP_CAP_10FDX: 9529860Sgdamore@opensolaris.org *val = ph->phy_lp_10_fdx; 9539860Sgdamore@opensolaris.org break; 9549860Sgdamore@opensolaris.org case ETHER_STAT_LP_CAP_10HDX: 9559860Sgdamore@opensolaris.org *val = ph->phy_lp_10_hdx; 9569860Sgdamore@opensolaris.org break; 9579860Sgdamore@opensolaris.org case ETHER_STAT_LP_CAP_100T4: 9589860Sgdamore@opensolaris.org *val = ph->phy_lp_100_t4; 9599860Sgdamore@opensolaris.org break; 9609860Sgdamore@opensolaris.org case ETHER_STAT_LP_CAP_AUTONEG: 9619860Sgdamore@opensolaris.org *val = ph->phy_lp_aneg; 9629860Sgdamore@opensolaris.org break; 9639860Sgdamore@opensolaris.org case ETHER_STAT_LP_CAP_PAUSE: 9649860Sgdamore@opensolaris.org *val = ph->phy_lp_pause; 9659860Sgdamore@opensolaris.org break; 9669860Sgdamore@opensolaris.org case ETHER_STAT_LP_CAP_ASMPAUSE: 9679860Sgdamore@opensolaris.org *val = ph->phy_lp_asmpause; 9689860Sgdamore@opensolaris.org break; 9699860Sgdamore@opensolaris.org 9709860Sgdamore@opensolaris.org case ETHER_STAT_ADV_CAP_1000FDX: 9719860Sgdamore@opensolaris.org *val = ph->phy_adv_1000_fdx; 9729860Sgdamore@opensolaris.org break; 9739860Sgdamore@opensolaris.org case ETHER_STAT_ADV_CAP_1000HDX: 9749860Sgdamore@opensolaris.org *val = ph->phy_adv_1000_hdx; 9759860Sgdamore@opensolaris.org break; 9769860Sgdamore@opensolaris.org case ETHER_STAT_ADV_CAP_100FDX: 9779860Sgdamore@opensolaris.org *val = ph->phy_adv_100_fdx; 9789860Sgdamore@opensolaris.org break; 9799860Sgdamore@opensolaris.org case ETHER_STAT_ADV_CAP_100HDX: 9809860Sgdamore@opensolaris.org *val = ph->phy_adv_100_hdx; 9819860Sgdamore@opensolaris.org break; 9829860Sgdamore@opensolaris.org case ETHER_STAT_ADV_CAP_10FDX: 9839860Sgdamore@opensolaris.org *val = ph->phy_adv_10_fdx; 9849860Sgdamore@opensolaris.org break; 9859860Sgdamore@opensolaris.org case ETHER_STAT_ADV_CAP_10HDX: 9869860Sgdamore@opensolaris.org *val = ph->phy_adv_10_hdx; 9879860Sgdamore@opensolaris.org break; 9889860Sgdamore@opensolaris.org case ETHER_STAT_ADV_CAP_100T4: 9899860Sgdamore@opensolaris.org *val = ph->phy_adv_100_t4; 9909860Sgdamore@opensolaris.org break; 9919860Sgdamore@opensolaris.org case ETHER_STAT_ADV_CAP_AUTONEG: 9929860Sgdamore@opensolaris.org *val = ph->phy_adv_aneg; 9939860Sgdamore@opensolaris.org break; 9949860Sgdamore@opensolaris.org case ETHER_STAT_ADV_CAP_PAUSE: 9959860Sgdamore@opensolaris.org *val = ph->phy_adv_pause; 9969860Sgdamore@opensolaris.org break; 9979860Sgdamore@opensolaris.org case ETHER_STAT_ADV_CAP_ASMPAUSE: 9989860Sgdamore@opensolaris.org *val = ph->phy_adv_asmpause; 9999860Sgdamore@opensolaris.org break; 10009860Sgdamore@opensolaris.org 10019860Sgdamore@opensolaris.org default: 10029860Sgdamore@opensolaris.org rv = ENOTSUP; 10039860Sgdamore@opensolaris.org break; 10049860Sgdamore@opensolaris.org } 10059860Sgdamore@opensolaris.org mutex_exit(&mh->m_lock); 10069860Sgdamore@opensolaris.org 10079860Sgdamore@opensolaris.org return (rv); 10089860Sgdamore@opensolaris.org } 10099860Sgdamore@opensolaris.org 10109860Sgdamore@opensolaris.org /* 10119860Sgdamore@opensolaris.org * PHY support routines. Private to the MII module and the vendor 10129860Sgdamore@opensolaris.org * specific PHY implementation code. 10139860Sgdamore@opensolaris.org */ 10149860Sgdamore@opensolaris.org uint16_t 10159860Sgdamore@opensolaris.org phy_read(phy_handle_t *ph, uint8_t reg) 10169860Sgdamore@opensolaris.org { 10179860Sgdamore@opensolaris.org mii_handle_t mh = ph->phy_mii; 10189860Sgdamore@opensolaris.org 10199860Sgdamore@opensolaris.org return ((*mh->m_ops.mii_read)(mh->m_private, ph->phy_addr, reg)); 10209860Sgdamore@opensolaris.org } 10219860Sgdamore@opensolaris.org 10229860Sgdamore@opensolaris.org void 10239860Sgdamore@opensolaris.org phy_write(phy_handle_t *ph, uint8_t reg, uint16_t val) 10249860Sgdamore@opensolaris.org { 10259860Sgdamore@opensolaris.org mii_handle_t mh = ph->phy_mii; 10269860Sgdamore@opensolaris.org 10279860Sgdamore@opensolaris.org (*mh->m_ops.mii_write)(mh->m_private, ph->phy_addr, reg, val); 10289860Sgdamore@opensolaris.org } 10299860Sgdamore@opensolaris.org 10309860Sgdamore@opensolaris.org int 10319860Sgdamore@opensolaris.org phy_reset(phy_handle_t *ph) 10329860Sgdamore@opensolaris.org { 10339860Sgdamore@opensolaris.org ASSERT(mutex_owned(&ph->phy_mii->m_lock)); 10349860Sgdamore@opensolaris.org 10359860Sgdamore@opensolaris.org /* 10369860Sgdamore@opensolaris.org * For our device, make sure its powered up, unisolated, and 10379860Sgdamore@opensolaris.org * not in loopback. 10389860Sgdamore@opensolaris.org */ 10399860Sgdamore@opensolaris.org PHY_CLR(ph, MII_CONTROL, 10409860Sgdamore@opensolaris.org MII_CONTROL_PWRDN | MII_CONTROL_LOOPBACK | MII_CONTROL_ISOLATE); 10419860Sgdamore@opensolaris.org 10429860Sgdamore@opensolaris.org /* 10439860Sgdamore@opensolaris.org * Finally reset it. 10449860Sgdamore@opensolaris.org */ 10459860Sgdamore@opensolaris.org PHY_SET(ph, MII_CONTROL, MII_CONTROL_RESET); 10469860Sgdamore@opensolaris.org 10479860Sgdamore@opensolaris.org /* 10489860Sgdamore@opensolaris.org * Apparently some devices (DP83840A) like to have a little 10499860Sgdamore@opensolaris.org * bit of a wait before we start accessing anything else on 10509860Sgdamore@opensolaris.org * the PHY. 10519860Sgdamore@opensolaris.org */ 10529860Sgdamore@opensolaris.org drv_usecwait(500); 10539860Sgdamore@opensolaris.org 10549860Sgdamore@opensolaris.org /* 10559860Sgdamore@opensolaris.org * Wait for reset to complete - probably very fast, but no 10569860Sgdamore@opensolaris.org * more than 0.5 sec according to spec. It would be nice if 10579860Sgdamore@opensolaris.org * we could use delay() here, but MAC drivers may call 10589860Sgdamore@opensolaris.org * functions which hold this lock in interrupt context, so 10599860Sgdamore@opensolaris.org * sleeping would be a definite no-no. The good news here is 10609860Sgdamore@opensolaris.org * that it seems to be the case that most devices come back 10619860Sgdamore@opensolaris.org * within only a few hundred usec. 10629860Sgdamore@opensolaris.org */ 10639860Sgdamore@opensolaris.org for (int i = 500000; i; i -= 100) { 10649860Sgdamore@opensolaris.org if ((phy_read(ph, MII_CONTROL) & MII_CONTROL_RESET) == 0) { 10659860Sgdamore@opensolaris.org /* reset completed */ 10669860Sgdamore@opensolaris.org return (DDI_SUCCESS); 10679860Sgdamore@opensolaris.org } 10689860Sgdamore@opensolaris.org drv_usecwait(100); 10699860Sgdamore@opensolaris.org } 10709860Sgdamore@opensolaris.org 10719860Sgdamore@opensolaris.org return (DDI_FAILURE); 10729860Sgdamore@opensolaris.org } 10739860Sgdamore@opensolaris.org 10749860Sgdamore@opensolaris.org int 10759860Sgdamore@opensolaris.org phy_stop(phy_handle_t *ph) 10769860Sgdamore@opensolaris.org { 107710066Sgdamore@opensolaris.org phy_write(ph, MII_CONTROL, MII_CONTROL_ISOLATE); 107810066Sgdamore@opensolaris.org 10799860Sgdamore@opensolaris.org return (DDI_SUCCESS); 10809860Sgdamore@opensolaris.org } 10819860Sgdamore@opensolaris.org 10829860Sgdamore@opensolaris.org int 10839860Sgdamore@opensolaris.org phy_start(phy_handle_t *ph) 10849860Sgdamore@opensolaris.org { 10859860Sgdamore@opensolaris.org uint16_t bmcr, anar, gtcr; 10869860Sgdamore@opensolaris.org ASSERT(mutex_owned(&ph->phy_mii->m_lock)); 10879860Sgdamore@opensolaris.org 10889860Sgdamore@opensolaris.org /* 10899860Sgdamore@opensolaris.org * Disable everything to start... we'll add in modes as we go. 10909860Sgdamore@opensolaris.org */ 10919860Sgdamore@opensolaris.org ph->phy_adv_aneg = B_FALSE; 10929860Sgdamore@opensolaris.org ph->phy_adv_1000_fdx = B_FALSE; 10939860Sgdamore@opensolaris.org ph->phy_adv_1000_hdx = B_FALSE; 10949860Sgdamore@opensolaris.org ph->phy_adv_100_fdx = B_FALSE; 10959860Sgdamore@opensolaris.org ph->phy_adv_100_t4 = B_FALSE; 10969860Sgdamore@opensolaris.org ph->phy_adv_100_hdx = B_FALSE; 10979860Sgdamore@opensolaris.org ph->phy_adv_10_fdx = B_FALSE; 10989860Sgdamore@opensolaris.org ph->phy_adv_10_hdx = B_FALSE; 10999860Sgdamore@opensolaris.org ph->phy_adv_pause = B_FALSE; 11009860Sgdamore@opensolaris.org ph->phy_adv_asmpause = B_FALSE; 11019860Sgdamore@opensolaris.org 11029860Sgdamore@opensolaris.org switch (ph->phy_loopback) { 11039860Sgdamore@opensolaris.org case PHY_LB_NONE: 11049860Sgdamore@opensolaris.org /* 11059860Sgdamore@opensolaris.org * No loopback overrides, so try to advertise everything 11069860Sgdamore@opensolaris.org * that is administratively enabled. 11079860Sgdamore@opensolaris.org */ 11089860Sgdamore@opensolaris.org ph->phy_adv_aneg = ph->phy_en_aneg; 11099860Sgdamore@opensolaris.org ph->phy_adv_1000_fdx = ph->phy_en_1000_fdx; 11109860Sgdamore@opensolaris.org ph->phy_adv_1000_hdx = ph->phy_en_1000_hdx; 11119860Sgdamore@opensolaris.org ph->phy_adv_100_fdx = ph->phy_en_100_fdx; 11129860Sgdamore@opensolaris.org ph->phy_adv_100_t4 = ph->phy_en_100_t4; 11139860Sgdamore@opensolaris.org ph->phy_adv_100_hdx = ph->phy_en_100_hdx; 11149860Sgdamore@opensolaris.org ph->phy_adv_10_fdx = ph->phy_en_10_fdx; 11159860Sgdamore@opensolaris.org ph->phy_adv_10_hdx = ph->phy_en_10_hdx; 11169860Sgdamore@opensolaris.org ph->phy_adv_pause = ph->phy_en_pause; 11179860Sgdamore@opensolaris.org ph->phy_adv_asmpause = ph->phy_en_asmpause; 11189860Sgdamore@opensolaris.org break; 11199860Sgdamore@opensolaris.org 11209860Sgdamore@opensolaris.org case PHY_LB_INT_PHY: 11219860Sgdamore@opensolaris.org if (ph->phy_cap_1000_fdx) { 11229860Sgdamore@opensolaris.org ph->phy_adv_1000_fdx = B_TRUE; 11239860Sgdamore@opensolaris.org } else if (ph->phy_cap_100_fdx) { 11249860Sgdamore@opensolaris.org ph->phy_adv_100_fdx = B_TRUE; 11259860Sgdamore@opensolaris.org } else if (ph->phy_cap_10_fdx) { 11269860Sgdamore@opensolaris.org ph->phy_adv_10_fdx = B_TRUE; 11279860Sgdamore@opensolaris.org } 11289860Sgdamore@opensolaris.org break; 11299860Sgdamore@opensolaris.org 11309860Sgdamore@opensolaris.org case PHY_LB_EXT_10: 11319860Sgdamore@opensolaris.org ph->phy_adv_10_fdx = B_TRUE; 11329860Sgdamore@opensolaris.org break; 11339860Sgdamore@opensolaris.org 11349860Sgdamore@opensolaris.org case PHY_LB_EXT_100: 11359860Sgdamore@opensolaris.org ph->phy_adv_100_fdx = B_TRUE; 11369860Sgdamore@opensolaris.org break; 11379860Sgdamore@opensolaris.org 11389860Sgdamore@opensolaris.org case PHY_LB_EXT_1000: 11399860Sgdamore@opensolaris.org ph->phy_adv_1000_fdx = B_TRUE; 11409860Sgdamore@opensolaris.org break; 11419860Sgdamore@opensolaris.org } 11429860Sgdamore@opensolaris.org 11439860Sgdamore@opensolaris.org /* 11449860Sgdamore@opensolaris.org * Limit properties to what the hardware can actually support. 11459860Sgdamore@opensolaris.org */ 11469860Sgdamore@opensolaris.org #define FILTER_ADV(CAP) \ 11479860Sgdamore@opensolaris.org if (!ph->phy_cap_##CAP) \ 11489860Sgdamore@opensolaris.org ph->phy_adv_##CAP = 0 11499860Sgdamore@opensolaris.org 11509860Sgdamore@opensolaris.org FILTER_ADV(aneg); 11519860Sgdamore@opensolaris.org FILTER_ADV(1000_fdx); 11529860Sgdamore@opensolaris.org FILTER_ADV(1000_hdx); 11539860Sgdamore@opensolaris.org FILTER_ADV(100_fdx); 11549860Sgdamore@opensolaris.org FILTER_ADV(100_t4); 11559860Sgdamore@opensolaris.org FILTER_ADV(100_hdx); 11569860Sgdamore@opensolaris.org FILTER_ADV(10_fdx); 11579860Sgdamore@opensolaris.org FILTER_ADV(10_hdx); 11589860Sgdamore@opensolaris.org FILTER_ADV(pause); 11599860Sgdamore@opensolaris.org FILTER_ADV(asmpause); 11609860Sgdamore@opensolaris.org 11619860Sgdamore@opensolaris.org #undef FILTER_ADV 11629860Sgdamore@opensolaris.org 11639860Sgdamore@opensolaris.org /* 11649860Sgdamore@opensolaris.org * We need at least one valid mode. 11659860Sgdamore@opensolaris.org */ 11669860Sgdamore@opensolaris.org if ((!ph->phy_adv_1000_fdx) && 11679860Sgdamore@opensolaris.org (!ph->phy_adv_1000_hdx) && 11689860Sgdamore@opensolaris.org (!ph->phy_adv_100_t4) && 11699860Sgdamore@opensolaris.org (!ph->phy_adv_100_fdx) && 11709860Sgdamore@opensolaris.org (!ph->phy_adv_100_hdx) && 11719860Sgdamore@opensolaris.org (!ph->phy_adv_10_fdx) && 11729860Sgdamore@opensolaris.org (!ph->phy_adv_10_hdx)) { 11739860Sgdamore@opensolaris.org 11749860Sgdamore@opensolaris.org phy_warn(ph, 11759860Sgdamore@opensolaris.org "No valid link mode selected. Powering down PHY."); 11769860Sgdamore@opensolaris.org 11779860Sgdamore@opensolaris.org PHY_SET(ph, MII_CONTROL, MII_CONTROL_PWRDN); 11789860Sgdamore@opensolaris.org 11799860Sgdamore@opensolaris.org ph->phy_link = LINK_STATE_DOWN; 11809860Sgdamore@opensolaris.org return (DDI_SUCCESS); 11819860Sgdamore@opensolaris.org } 11829860Sgdamore@opensolaris.org 11839860Sgdamore@opensolaris.org switch (ph->phy_loopback) { 11849860Sgdamore@opensolaris.org case PHY_LB_INT_PHY: 11859860Sgdamore@opensolaris.org bmcr = MII_CONTROL_LOOPBACK; 11869860Sgdamore@opensolaris.org gtcr = 0; 11879860Sgdamore@opensolaris.org break; 11889860Sgdamore@opensolaris.org case PHY_LB_EXT_10: 11899860Sgdamore@opensolaris.org case PHY_LB_EXT_100: 11909860Sgdamore@opensolaris.org case PHY_LB_EXT_1000: 11919860Sgdamore@opensolaris.org bmcr = 0; 11929860Sgdamore@opensolaris.org gtcr = MII_MSCONTROL_MANUAL | MII_MSCONTROL_MASTER; 11939860Sgdamore@opensolaris.org break; 11949860Sgdamore@opensolaris.org case PHY_LB_NONE: 11959860Sgdamore@opensolaris.org default: 11969860Sgdamore@opensolaris.org bmcr = 0; 11979860Sgdamore@opensolaris.org gtcr = 0; 11989860Sgdamore@opensolaris.org break; 11999860Sgdamore@opensolaris.org } 12009860Sgdamore@opensolaris.org 12019860Sgdamore@opensolaris.org if (ph->phy_adv_aneg) { 12029860Sgdamore@opensolaris.org bmcr |= MII_CONTROL_ANE | MII_CONTROL_RSAN; 12039860Sgdamore@opensolaris.org } 12049860Sgdamore@opensolaris.org 12059860Sgdamore@opensolaris.org if ((ph->phy_adv_1000_fdx) || (ph->phy_adv_1000_hdx)) { 12069860Sgdamore@opensolaris.org bmcr |= MII_CONTROL_1GB; 12079860Sgdamore@opensolaris.org 12089860Sgdamore@opensolaris.org } else if (ph->phy_adv_100_fdx || ph->phy_adv_100_hdx || 12099860Sgdamore@opensolaris.org ph->phy_adv_100_t4) { 12109860Sgdamore@opensolaris.org bmcr |= MII_CONTROL_100MB; 12119860Sgdamore@opensolaris.org } 12129860Sgdamore@opensolaris.org 12139860Sgdamore@opensolaris.org if (ph->phy_adv_1000_fdx || ph->phy_adv_100_fdx || ph->phy_adv_10_fdx) { 12149860Sgdamore@opensolaris.org bmcr |= MII_CONTROL_FDUPLEX; 12159860Sgdamore@opensolaris.org } 12169860Sgdamore@opensolaris.org 12179860Sgdamore@opensolaris.org if (ph->phy_type == XCVR_1000X) { 12189860Sgdamore@opensolaris.org /* 1000BASE-X (usually fiber) */ 12199860Sgdamore@opensolaris.org anar = 0; 12209860Sgdamore@opensolaris.org if (ph->phy_adv_1000_fdx) { 12219860Sgdamore@opensolaris.org anar |= MII_ABILITY_X_FD; 12229860Sgdamore@opensolaris.org } 12239860Sgdamore@opensolaris.org if (ph->phy_adv_1000_hdx) { 12249860Sgdamore@opensolaris.org anar |= MII_ABILITY_X_HD; 12259860Sgdamore@opensolaris.org } 12269860Sgdamore@opensolaris.org if (ph->phy_adv_pause) { 12279860Sgdamore@opensolaris.org anar |= MII_ABILITY_X_PAUSE; 12289860Sgdamore@opensolaris.org } 12299860Sgdamore@opensolaris.org if (ph->phy_adv_asmpause) { 12309860Sgdamore@opensolaris.org anar |= MII_ABILITY_X_ASMPAUSE; 12319860Sgdamore@opensolaris.org } 12329860Sgdamore@opensolaris.org 12339860Sgdamore@opensolaris.org } else if (ph->phy_type == XCVR_100T2) { 12349860Sgdamore@opensolaris.org /* 100BASE-T2 */ 12359860Sgdamore@opensolaris.org anar = 0; 12369860Sgdamore@opensolaris.org if (ph->phy_adv_100_fdx) { 12379860Sgdamore@opensolaris.org anar |= MII_ABILITY_T2_FD; 12389860Sgdamore@opensolaris.org } 12399860Sgdamore@opensolaris.org if (ph->phy_adv_100_hdx) { 12409860Sgdamore@opensolaris.org anar |= MII_ABILITY_T2_HD; 12419860Sgdamore@opensolaris.org } 12429860Sgdamore@opensolaris.org 12439860Sgdamore@opensolaris.org } else { 12449860Sgdamore@opensolaris.org anar = MII_AN_SELECTOR_8023; 12459860Sgdamore@opensolaris.org 12469860Sgdamore@opensolaris.org /* 1000BASE-T or 100BASE-X probably */ 12479860Sgdamore@opensolaris.org if (ph->phy_adv_1000_fdx) { 12489860Sgdamore@opensolaris.org gtcr |= MII_MSCONTROL_1000T_FD; 12499860Sgdamore@opensolaris.org } 12509860Sgdamore@opensolaris.org if (ph->phy_adv_1000_hdx) { 12519860Sgdamore@opensolaris.org gtcr |= MII_MSCONTROL_1000T; 12529860Sgdamore@opensolaris.org } 12539860Sgdamore@opensolaris.org if (ph->phy_adv_100_fdx) { 12549860Sgdamore@opensolaris.org anar |= MII_ABILITY_100BASE_TX_FD; 12559860Sgdamore@opensolaris.org } 12569860Sgdamore@opensolaris.org if (ph->phy_adv_100_hdx) { 12579860Sgdamore@opensolaris.org anar |= MII_ABILITY_100BASE_TX; 12589860Sgdamore@opensolaris.org } 12599860Sgdamore@opensolaris.org if (ph->phy_adv_100_t4) { 12609860Sgdamore@opensolaris.org anar |= MII_ABILITY_100BASE_T4; 12619860Sgdamore@opensolaris.org } 12629860Sgdamore@opensolaris.org if (ph->phy_adv_10_fdx) { 12639860Sgdamore@opensolaris.org anar |= MII_ABILITY_10BASE_T_FD; 12649860Sgdamore@opensolaris.org } 12659860Sgdamore@opensolaris.org if (ph->phy_adv_100_hdx) { 12669860Sgdamore@opensolaris.org anar |= MII_ABILITY_10BASE_T; 12679860Sgdamore@opensolaris.org } 12689860Sgdamore@opensolaris.org if (ph->phy_adv_pause) { 12699860Sgdamore@opensolaris.org anar |= MII_ABILITY_PAUSE; 12709860Sgdamore@opensolaris.org } 12719860Sgdamore@opensolaris.org if (ph->phy_adv_asmpause) { 12729860Sgdamore@opensolaris.org anar |= MII_ABILITY_ASMPAUSE; 12739860Sgdamore@opensolaris.org } 12749860Sgdamore@opensolaris.org } 12759860Sgdamore@opensolaris.org 12769860Sgdamore@opensolaris.org ph->phy_link = LINK_STATE_DOWN; 12779860Sgdamore@opensolaris.org ph->phy_duplex = LINK_DUPLEX_UNKNOWN; 12789860Sgdamore@opensolaris.org ph->phy_speed = 0; 12799860Sgdamore@opensolaris.org 12809860Sgdamore@opensolaris.org phy_write(ph, MII_AN_ADVERT, anar); 1281*10291Sgdamore@opensolaris.org phy_write(ph, MII_CONTROL, bmcr & ~(MII_CONTROL_RSAN)); 1282*10291Sgdamore@opensolaris.org 12839860Sgdamore@opensolaris.org switch (ph->phy_type) { 12849860Sgdamore@opensolaris.org case XCVR_1000T: 12859860Sgdamore@opensolaris.org case XCVR_1000X: 12869860Sgdamore@opensolaris.org case XCVR_100T2: 12879860Sgdamore@opensolaris.org phy_write(ph, MII_MSCONTROL, gtcr); 12889860Sgdamore@opensolaris.org } 12899860Sgdamore@opensolaris.org 12909860Sgdamore@opensolaris.org /* 12919860Sgdamore@opensolaris.org * Finally, this will start up autoneg if it is enabled, or 12929860Sgdamore@opensolaris.org * force link settings otherwise. 12939860Sgdamore@opensolaris.org */ 12949860Sgdamore@opensolaris.org phy_write(ph, MII_CONTROL, bmcr); 12959860Sgdamore@opensolaris.org 12969860Sgdamore@opensolaris.org return (DDI_SUCCESS); 12979860Sgdamore@opensolaris.org } 12989860Sgdamore@opensolaris.org 12999860Sgdamore@opensolaris.org 13009860Sgdamore@opensolaris.org int 13019860Sgdamore@opensolaris.org phy_check(phy_handle_t *ph) 13029860Sgdamore@opensolaris.org { 13039860Sgdamore@opensolaris.org uint16_t control, status, lpar, msstat, anexp; 13049860Sgdamore@opensolaris.org int debounces = 100; 13059860Sgdamore@opensolaris.org 13069860Sgdamore@opensolaris.org ASSERT(mutex_owned(&ph->phy_mii->m_lock)); 13079860Sgdamore@opensolaris.org 13089860Sgdamore@opensolaris.org debounce: 13099860Sgdamore@opensolaris.org status = phy_read(ph, MII_STATUS); 13109860Sgdamore@opensolaris.org control = phy_read(ph, MII_CONTROL); 13119860Sgdamore@opensolaris.org 13129860Sgdamore@opensolaris.org if (status & MII_STATUS_EXTENDED) { 13139860Sgdamore@opensolaris.org lpar = phy_read(ph, MII_AN_LPABLE); 13149860Sgdamore@opensolaris.org anexp = phy_read(ph, MII_AN_EXPANSION); 13159860Sgdamore@opensolaris.org } else { 13169860Sgdamore@opensolaris.org lpar = 0; 13179860Sgdamore@opensolaris.org anexp = 0; 13189860Sgdamore@opensolaris.org } 13199860Sgdamore@opensolaris.org 13209860Sgdamore@opensolaris.org /* 13219860Sgdamore@opensolaris.org * We reread to clear any latched bits. This also debounces 13229860Sgdamore@opensolaris.org * any state that might be in transition. 13239860Sgdamore@opensolaris.org */ 13249860Sgdamore@opensolaris.org drv_usecwait(10); 13259860Sgdamore@opensolaris.org if ((status != phy_read(ph, MII_STATUS)) && debounces) { 13269860Sgdamore@opensolaris.org debounces--; 13279860Sgdamore@opensolaris.org goto debounce; 13289860Sgdamore@opensolaris.org } 13299860Sgdamore@opensolaris.org 13309860Sgdamore@opensolaris.org /* 13319860Sgdamore@opensolaris.org * Detect the situation where the PHY is removed or has died. 13329860Sgdamore@opensolaris.org * According to spec, at least one bit of status must be set, 13339860Sgdamore@opensolaris.org * and at least one bit must be clear. 13349860Sgdamore@opensolaris.org */ 13359860Sgdamore@opensolaris.org if ((status == 0xffff) || (status == 0)) { 13369860Sgdamore@opensolaris.org ph->phy_speed = 0; 13379860Sgdamore@opensolaris.org ph->phy_duplex = LINK_DUPLEX_UNKNOWN; 13389860Sgdamore@opensolaris.org ph->phy_link = LINK_STATE_UNKNOWN; 13399860Sgdamore@opensolaris.org ph->phy_present = B_FALSE; 13409860Sgdamore@opensolaris.org return (DDI_FAILURE); 13419860Sgdamore@opensolaris.org } 13429860Sgdamore@opensolaris.org 13439860Sgdamore@opensolaris.org /* We only respect the link flag if we are not in loopback. */ 13449860Sgdamore@opensolaris.org if ((ph->phy_loopback != PHY_LB_INT_PHY) && 13459860Sgdamore@opensolaris.org ((status & MII_STATUS_LINKUP) == 0)) { 13469860Sgdamore@opensolaris.org ph->phy_speed = 0; 13479860Sgdamore@opensolaris.org ph->phy_duplex = LINK_DUPLEX_UNKNOWN; 13489860Sgdamore@opensolaris.org ph->phy_link = LINK_STATE_DOWN; 13499860Sgdamore@opensolaris.org return (DDI_SUCCESS); 13509860Sgdamore@opensolaris.org } 13519860Sgdamore@opensolaris.org 13529860Sgdamore@opensolaris.org ph->phy_link = LINK_STATE_UP; 13539860Sgdamore@opensolaris.org 13549860Sgdamore@opensolaris.org if ((control & MII_CONTROL_ANE) == 0) { 13559860Sgdamore@opensolaris.org 13569860Sgdamore@opensolaris.org ph->phy_lp_aneg = B_FALSE; 13579860Sgdamore@opensolaris.org ph->phy_lp_10_hdx = B_FALSE; 13589860Sgdamore@opensolaris.org ph->phy_lp_10_fdx = B_FALSE; 13599860Sgdamore@opensolaris.org ph->phy_lp_100_t4 = B_FALSE; 13609860Sgdamore@opensolaris.org ph->phy_lp_100_hdx = B_FALSE; 13619860Sgdamore@opensolaris.org ph->phy_lp_100_fdx = B_FALSE; 13629860Sgdamore@opensolaris.org ph->phy_lp_1000_hdx = B_FALSE; 13639860Sgdamore@opensolaris.org ph->phy_lp_1000_fdx = B_FALSE; 13649860Sgdamore@opensolaris.org 13659860Sgdamore@opensolaris.org /* 13669860Sgdamore@opensolaris.org * We have no idea what our link partner might or might 13679860Sgdamore@opensolaris.org * not be able to support, except that it appears to 13689860Sgdamore@opensolaris.org * support the same mode that we have forced. 13699860Sgdamore@opensolaris.org */ 13709860Sgdamore@opensolaris.org if (control & MII_CONTROL_1GB) { 13719860Sgdamore@opensolaris.org ph->phy_speed = 1000; 13729860Sgdamore@opensolaris.org } else if (control & MII_CONTROL_100MB) { 13739860Sgdamore@opensolaris.org ph->phy_speed = 100; 13749860Sgdamore@opensolaris.org } else { 13759860Sgdamore@opensolaris.org ph->phy_speed = 10; 13769860Sgdamore@opensolaris.org } 13779860Sgdamore@opensolaris.org ph->phy_duplex = control & MII_CONTROL_FDUPLEX ? 13789860Sgdamore@opensolaris.org LINK_DUPLEX_FULL : LINK_DUPLEX_HALF; 13799860Sgdamore@opensolaris.org 13809860Sgdamore@opensolaris.org return (DDI_SUCCESS); 13819860Sgdamore@opensolaris.org } 13829860Sgdamore@opensolaris.org 13839860Sgdamore@opensolaris.org if (ph->phy_type == XCVR_1000X) { 13849860Sgdamore@opensolaris.org 13859860Sgdamore@opensolaris.org ph->phy_lp_10_hdx = B_FALSE; 13869860Sgdamore@opensolaris.org ph->phy_lp_10_fdx = B_FALSE; 13879860Sgdamore@opensolaris.org ph->phy_lp_100_t4 = B_FALSE; 13889860Sgdamore@opensolaris.org ph->phy_lp_100_hdx = B_FALSE; 13899860Sgdamore@opensolaris.org ph->phy_lp_100_fdx = B_FALSE; 13909860Sgdamore@opensolaris.org 13919860Sgdamore@opensolaris.org /* 1000BASE-X requires autonegotiation */ 13929860Sgdamore@opensolaris.org ph->phy_lp_aneg = B_TRUE; 13939860Sgdamore@opensolaris.org ph->phy_lp_1000_fdx = !!(lpar & MII_ABILITY_X_FD); 13949860Sgdamore@opensolaris.org ph->phy_lp_1000_hdx = !!(lpar & MII_ABILITY_X_HD); 13959860Sgdamore@opensolaris.org ph->phy_lp_pause = !!(lpar & MII_ABILITY_X_PAUSE); 13969860Sgdamore@opensolaris.org ph->phy_lp_asmpause = !!(lpar & MII_ABILITY_X_ASMPAUSE); 13979860Sgdamore@opensolaris.org 13989860Sgdamore@opensolaris.org } else if (ph->phy_type == XCVR_100T2) { 13999860Sgdamore@opensolaris.org ph->phy_lp_10_hdx = B_FALSE; 14009860Sgdamore@opensolaris.org ph->phy_lp_10_fdx = B_FALSE; 14019860Sgdamore@opensolaris.org ph->phy_lp_100_t4 = B_FALSE; 14029860Sgdamore@opensolaris.org ph->phy_lp_1000_hdx = B_FALSE; 14039860Sgdamore@opensolaris.org ph->phy_lp_1000_fdx = B_FALSE; 14049860Sgdamore@opensolaris.org ph->phy_lp_pause = B_FALSE; 14059860Sgdamore@opensolaris.org ph->phy_lp_asmpause = B_FALSE; 14069860Sgdamore@opensolaris.org 14079860Sgdamore@opensolaris.org /* 100BASE-T2 requires autonegotiation */ 14089860Sgdamore@opensolaris.org ph->phy_lp_aneg = B_TRUE; 14099860Sgdamore@opensolaris.org ph->phy_lp_100_fdx = !!(lpar & MII_ABILITY_T2_FD); 14109860Sgdamore@opensolaris.org ph->phy_lp_100_hdx = !!(lpar & MII_ABILITY_T2_HD); 14119860Sgdamore@opensolaris.org 14129860Sgdamore@opensolaris.org } else if (anexp & MII_AN_EXP_PARFAULT) { 14139860Sgdamore@opensolaris.org /* 14149860Sgdamore@opensolaris.org * Parallel detection fault! This happens when the 14159860Sgdamore@opensolaris.org * peer does not use autonegotiation, and the 14169860Sgdamore@opensolaris.org * detection logic reports more than one type of legal 14179860Sgdamore@opensolaris.org * link is available. Note that parallel detection 14189860Sgdamore@opensolaris.org * can only happen with half duplex 10, 100, and 14199860Sgdamore@opensolaris.org * 100TX4. We also should not have got here, because 14209860Sgdamore@opensolaris.org * the link state bit should have failed. 14219860Sgdamore@opensolaris.org */ 14229860Sgdamore@opensolaris.org #ifdef DEBUG 14239860Sgdamore@opensolaris.org phy_warn(ph, "Parallel detection fault!"); 14249860Sgdamore@opensolaris.org #endif 14259860Sgdamore@opensolaris.org ph->phy_lp_10_hdx = B_FALSE; 14269860Sgdamore@opensolaris.org ph->phy_lp_10_fdx = B_FALSE; 14279860Sgdamore@opensolaris.org ph->phy_lp_100_t4 = B_FALSE; 14289860Sgdamore@opensolaris.org ph->phy_lp_100_hdx = B_FALSE; 14299860Sgdamore@opensolaris.org ph->phy_lp_100_fdx = B_FALSE; 14309860Sgdamore@opensolaris.org ph->phy_lp_1000_hdx = B_FALSE; 14319860Sgdamore@opensolaris.org ph->phy_lp_1000_fdx = B_FALSE; 14329860Sgdamore@opensolaris.org ph->phy_lp_pause = B_FALSE; 14339860Sgdamore@opensolaris.org ph->phy_lp_asmpause = B_FALSE; 14349860Sgdamore@opensolaris.org ph->phy_speed = 0; 14359860Sgdamore@opensolaris.org ph->phy_duplex = LINK_DUPLEX_UNKNOWN; 14369860Sgdamore@opensolaris.org return (DDI_SUCCESS); 14379860Sgdamore@opensolaris.org 14389860Sgdamore@opensolaris.org } else { 14399860Sgdamore@opensolaris.org ph->phy_lp_aneg = !!(anexp & MII_AN_EXP_LPCANAN); 14409860Sgdamore@opensolaris.org 14419860Sgdamore@opensolaris.org /* 14429860Sgdamore@opensolaris.org * Note: If the peer doesn't support autonegotiation, then 14439860Sgdamore@opensolaris.org * according to clause 28.5.4.5, the link partner ability 14449860Sgdamore@opensolaris.org * register will still have the right bits set. However, 14459860Sgdamore@opensolaris.org * gigabit modes cannot use legacy parallel detection. 14469860Sgdamore@opensolaris.org */ 14479860Sgdamore@opensolaris.org 14489860Sgdamore@opensolaris.org if ((ph->phy_type == XCVR_1000T) & 14499860Sgdamore@opensolaris.org (anexp & MII_AN_EXP_LPCANAN)) { 14509860Sgdamore@opensolaris.org 14519860Sgdamore@opensolaris.org /* check for gige */ 14529860Sgdamore@opensolaris.org msstat = phy_read(ph, MII_MSSTATUS); 14539860Sgdamore@opensolaris.org 14549860Sgdamore@opensolaris.org ph->phy_lp_1000_hdx = 14559860Sgdamore@opensolaris.org !!(msstat & MII_MSSTATUS_LP1000T); 14569860Sgdamore@opensolaris.org 14579860Sgdamore@opensolaris.org ph->phy_lp_1000_fdx = 14589860Sgdamore@opensolaris.org !!(msstat & MII_MSSTATUS_LP1000T_FD); 14599860Sgdamore@opensolaris.org } 14609860Sgdamore@opensolaris.org 14619860Sgdamore@opensolaris.org ph->phy_lp_100_fdx = !!(lpar & MII_ABILITY_100BASE_TX_FD); 14629860Sgdamore@opensolaris.org ph->phy_lp_100_hdx = !!(lpar & MII_ABILITY_100BASE_TX); 14639860Sgdamore@opensolaris.org ph->phy_lp_100_t4 = !!(lpar & MII_ABILITY_100BASE_T4); 14649860Sgdamore@opensolaris.org ph->phy_lp_10_fdx = !!(lpar & MII_ABILITY_10BASE_T_FD); 14659860Sgdamore@opensolaris.org ph->phy_lp_10_hdx = !!(lpar & MII_ABILITY_10BASE_T); 14669860Sgdamore@opensolaris.org ph->phy_lp_pause = !!(lpar & MII_ABILITY_PAUSE); 14679860Sgdamore@opensolaris.org ph->phy_lp_asmpause = !!(lpar & MII_ABILITY_ASMPAUSE); 14689860Sgdamore@opensolaris.org } 14699860Sgdamore@opensolaris.org 14709860Sgdamore@opensolaris.org /* resolve link pause */ 14719860Sgdamore@opensolaris.org if ((ph->phy_en_flowctrl == LINK_FLOWCTRL_BI) && 14729860Sgdamore@opensolaris.org (ph->phy_lp_pause)) { 14739860Sgdamore@opensolaris.org ph->phy_flowctrl = LINK_FLOWCTRL_BI; 14749860Sgdamore@opensolaris.org } else if ((ph->phy_en_flowctrl == LINK_FLOWCTRL_RX) && 14759860Sgdamore@opensolaris.org (ph->phy_lp_pause || ph->phy_lp_asmpause)) { 14769860Sgdamore@opensolaris.org ph->phy_flowctrl = LINK_FLOWCTRL_RX; 14779860Sgdamore@opensolaris.org } else if ((ph->phy_en_flowctrl == LINK_FLOWCTRL_TX) && 14789860Sgdamore@opensolaris.org (ph->phy_lp_pause)) { 14799860Sgdamore@opensolaris.org ph->phy_flowctrl = LINK_FLOWCTRL_TX; 14809860Sgdamore@opensolaris.org } else { 14819860Sgdamore@opensolaris.org ph->phy_flowctrl = LINK_FLOWCTRL_NONE; 14829860Sgdamore@opensolaris.org } 14839860Sgdamore@opensolaris.org 14849860Sgdamore@opensolaris.org if (ph->phy_adv_1000_fdx && ph->phy_lp_1000_fdx) { 14859860Sgdamore@opensolaris.org ph->phy_speed = 1000; 14869860Sgdamore@opensolaris.org ph->phy_duplex = LINK_DUPLEX_FULL; 14879860Sgdamore@opensolaris.org 14889860Sgdamore@opensolaris.org } else if (ph->phy_adv_1000_hdx && ph->phy_lp_1000_hdx) { 14899860Sgdamore@opensolaris.org ph->phy_speed = 1000; 14909860Sgdamore@opensolaris.org ph->phy_duplex = LINK_DUPLEX_HALF; 14919860Sgdamore@opensolaris.org 14929860Sgdamore@opensolaris.org } else if (ph->phy_adv_100_fdx && ph->phy_lp_100_fdx) { 14939860Sgdamore@opensolaris.org ph->phy_speed = 100; 14949860Sgdamore@opensolaris.org ph->phy_duplex = LINK_DUPLEX_FULL; 14959860Sgdamore@opensolaris.org 14969860Sgdamore@opensolaris.org } else if (ph->phy_adv_100_t4 && ph->phy_lp_100_t4) { 14979860Sgdamore@opensolaris.org ph->phy_speed = 100; 14989860Sgdamore@opensolaris.org ph->phy_duplex = LINK_DUPLEX_HALF; 14999860Sgdamore@opensolaris.org 15009860Sgdamore@opensolaris.org } else if (ph->phy_adv_100_hdx && ph->phy_lp_100_hdx) { 15019860Sgdamore@opensolaris.org ph->phy_speed = 100; 15029860Sgdamore@opensolaris.org ph->phy_duplex = LINK_DUPLEX_HALF; 15039860Sgdamore@opensolaris.org 15049860Sgdamore@opensolaris.org } else if (ph->phy_adv_10_fdx && ph->phy_lp_10_fdx) { 15059860Sgdamore@opensolaris.org ph->phy_speed = 10; 15069860Sgdamore@opensolaris.org ph->phy_duplex = LINK_DUPLEX_FULL; 15079860Sgdamore@opensolaris.org 15089860Sgdamore@opensolaris.org } else if (ph->phy_adv_10_hdx && ph->phy_lp_10_hdx) { 15099860Sgdamore@opensolaris.org ph->phy_speed = 10; 15109860Sgdamore@opensolaris.org ph->phy_duplex = LINK_DUPLEX_HALF; 15119860Sgdamore@opensolaris.org 15129860Sgdamore@opensolaris.org } else { 15139860Sgdamore@opensolaris.org #ifdef DEBUG 15149860Sgdamore@opensolaris.org phy_warn(ph, "No common abilities."); 15159860Sgdamore@opensolaris.org #endif 15169860Sgdamore@opensolaris.org ph->phy_speed = 0; 15179860Sgdamore@opensolaris.org ph->phy_duplex = LINK_DUPLEX_UNKNOWN; 15189860Sgdamore@opensolaris.org } 15199860Sgdamore@opensolaris.org 15209860Sgdamore@opensolaris.org return (DDI_SUCCESS); 15219860Sgdamore@opensolaris.org } 15229860Sgdamore@opensolaris.org 15239860Sgdamore@opensolaris.org int 15249860Sgdamore@opensolaris.org phy_get_prop(phy_handle_t *ph, char *prop, int dflt) 15259860Sgdamore@opensolaris.org { 15269860Sgdamore@opensolaris.org mii_handle_t mh = ph->phy_mii; 15279860Sgdamore@opensolaris.org 15289860Sgdamore@opensolaris.org return (ddi_prop_get_int(DDI_DEV_T_ANY, mh->m_dip, 0, prop, dflt)); 15299860Sgdamore@opensolaris.org } 15309860Sgdamore@opensolaris.org 15319860Sgdamore@opensolaris.org const char * 15329860Sgdamore@opensolaris.org phy_get_name(phy_handle_t *ph) 15339860Sgdamore@opensolaris.org { 15349860Sgdamore@opensolaris.org mii_handle_t mh = ph->phy_mii; 15359860Sgdamore@opensolaris.org 15369860Sgdamore@opensolaris.org return (mh->m_name); 15379860Sgdamore@opensolaris.org } 15389860Sgdamore@opensolaris.org 15399860Sgdamore@opensolaris.org const char * 15409860Sgdamore@opensolaris.org phy_get_driver(phy_handle_t *ph) 15419860Sgdamore@opensolaris.org { 15429860Sgdamore@opensolaris.org mii_handle_t mh = ph->phy_mii; 15439860Sgdamore@opensolaris.org 15449860Sgdamore@opensolaris.org return (ddi_driver_name(mh->m_dip)); 15459860Sgdamore@opensolaris.org } 15469860Sgdamore@opensolaris.org 15479860Sgdamore@opensolaris.org void 15489860Sgdamore@opensolaris.org phy_warn(phy_handle_t *ph, const char *fmt, ...) 15499860Sgdamore@opensolaris.org { 15509860Sgdamore@opensolaris.org va_list va; 15519860Sgdamore@opensolaris.org char buf[256]; 15529860Sgdamore@opensolaris.org 15539860Sgdamore@opensolaris.org (void) snprintf(buf, sizeof (buf), "%s: %s", phy_get_name(ph), fmt); 15549860Sgdamore@opensolaris.org 15559860Sgdamore@opensolaris.org va_start(va, fmt); 15569860Sgdamore@opensolaris.org vcmn_err(CE_WARN, buf, va); 15579860Sgdamore@opensolaris.org va_end(va); 15589860Sgdamore@opensolaris.org } 15599860Sgdamore@opensolaris.org 15609860Sgdamore@opensolaris.org /* 15619860Sgdamore@opensolaris.org * Internal support routines. 15629860Sgdamore@opensolaris.org */ 15639860Sgdamore@opensolaris.org 15649860Sgdamore@opensolaris.org void 15659860Sgdamore@opensolaris.org _mii_probe_phy(phy_handle_t *ph) 15669860Sgdamore@opensolaris.org { 15679860Sgdamore@opensolaris.org uint16_t bmsr; 15689860Sgdamore@opensolaris.org uint16_t extsr; 15699860Sgdamore@opensolaris.org mii_handle_t mh = ph->phy_mii; 15709860Sgdamore@opensolaris.org 15719860Sgdamore@opensolaris.org 15729860Sgdamore@opensolaris.org /* 15739860Sgdamore@opensolaris.org * Apparently, PHY 0 is less likely to be physically 15749860Sgdamore@opensolaris.org * connected, and should always be the last one tried. Most 15759860Sgdamore@opensolaris.org * single solution NICs use PHY1 for their built-in 15769860Sgdamore@opensolaris.org * transceiver. NICs with an external MII will often place 15779860Sgdamore@opensolaris.org * the external PHY at address 1, and use address 0 for the 15789860Sgdamore@opensolaris.org * internal PHY. 15799860Sgdamore@opensolaris.org */ 15809860Sgdamore@opensolaris.org 15819860Sgdamore@opensolaris.org ph->phy_id = 0; 15829860Sgdamore@opensolaris.org ph->phy_model = "PHY"; 15839860Sgdamore@opensolaris.org ph->phy_vendor = "Unknown Vendor"; 15849860Sgdamore@opensolaris.org 15859860Sgdamore@opensolaris.org /* done twice to clear any latched bits */ 15869860Sgdamore@opensolaris.org bmsr = phy_read(ph, MII_STATUS); 15879860Sgdamore@opensolaris.org bmsr = phy_read(ph, MII_STATUS); 15889860Sgdamore@opensolaris.org if ((bmsr == 0) || (bmsr == 0xffff)) { 15899860Sgdamore@opensolaris.org ph->phy_present = B_FALSE; 15909860Sgdamore@opensolaris.org return; 15919860Sgdamore@opensolaris.org } 15929860Sgdamore@opensolaris.org 15939860Sgdamore@opensolaris.org if (bmsr & MII_STATUS_EXTSTAT) { 15949860Sgdamore@opensolaris.org extsr = phy_read(ph, MII_EXTSTATUS); 15959860Sgdamore@opensolaris.org } else { 15969860Sgdamore@opensolaris.org extsr = 0; 15979860Sgdamore@opensolaris.org } 15989860Sgdamore@opensolaris.org 15999860Sgdamore@opensolaris.org ph->phy_present = B_TRUE; 16009860Sgdamore@opensolaris.org ph->phy_id = ((uint32_t)phy_read(ph, MII_PHYIDH) << 16) | 16019860Sgdamore@opensolaris.org phy_read(ph, MII_PHYIDL); 16029860Sgdamore@opensolaris.org 16039860Sgdamore@opensolaris.org /* setup default handlers */ 16049860Sgdamore@opensolaris.org ph->phy_reset = phy_reset; 16059860Sgdamore@opensolaris.org ph->phy_start = phy_start; 16069860Sgdamore@opensolaris.org ph->phy_stop = phy_stop; 16079860Sgdamore@opensolaris.org ph->phy_check = phy_check; 16089860Sgdamore@opensolaris.org 16099860Sgdamore@opensolaris.org /* 16109860Sgdamore@opensolaris.org * We ignore the non-existent 100baseT2 stuff -- no 16119860Sgdamore@opensolaris.org * known products for it exist. 16129860Sgdamore@opensolaris.org */ 16139860Sgdamore@opensolaris.org ph->phy_cap_aneg = !!(bmsr & MII_STATUS_CANAUTONEG); 16149860Sgdamore@opensolaris.org ph->phy_cap_100_t4 = !!(bmsr & MII_STATUS_100_BASE_T4); 16159860Sgdamore@opensolaris.org ph->phy_cap_100_fdx = !!(bmsr & MII_STATUS_100_BASEX_FD); 16169860Sgdamore@opensolaris.org ph->phy_cap_100_hdx = !!(bmsr & MII_STATUS_100_BASEX); 16179860Sgdamore@opensolaris.org ph->phy_cap_10_fdx = !!(bmsr & MII_STATUS_10_FD); 16189860Sgdamore@opensolaris.org ph->phy_cap_10_hdx = !!(bmsr & MII_STATUS_10); 16199860Sgdamore@opensolaris.org ph->phy_cap_1000_fdx = 16209860Sgdamore@opensolaris.org !!(extsr & (MII_EXTSTATUS_1000X_FD|MII_EXTSTATUS_1000T_FD)); 16219860Sgdamore@opensolaris.org ph->phy_cap_1000_hdx = 16229860Sgdamore@opensolaris.org !!(extsr & (MII_EXTSTATUS_1000X | MII_EXTSTATUS_1000T)); 16239860Sgdamore@opensolaris.org ph->phy_cap_pause = mh->m_cap_pause; 16249860Sgdamore@opensolaris.org ph->phy_cap_asmpause = mh->m_cap_asmpause; 16259860Sgdamore@opensolaris.org 16269860Sgdamore@opensolaris.org if (bmsr & MII_STATUS_10) { 16279860Sgdamore@opensolaris.org ph->phy_cap_10_hdx = B_TRUE; 16289860Sgdamore@opensolaris.org ph->phy_type = XCVR_10; 16299860Sgdamore@opensolaris.org } 16309860Sgdamore@opensolaris.org if (bmsr & MII_STATUS_10_FD) { 16319860Sgdamore@opensolaris.org ph->phy_cap_10_fdx = B_TRUE; 16329860Sgdamore@opensolaris.org ph->phy_type = XCVR_10; 16339860Sgdamore@opensolaris.org } 16349860Sgdamore@opensolaris.org if (bmsr & MII_STATUS_100T2) { 16359860Sgdamore@opensolaris.org ph->phy_cap_100_hdx = B_TRUE; 16369860Sgdamore@opensolaris.org ph->phy_type = XCVR_100T2; 16379860Sgdamore@opensolaris.org } 16389860Sgdamore@opensolaris.org if (bmsr & MII_STATUS_100T2_FD) { 16399860Sgdamore@opensolaris.org ph->phy_cap_100_fdx = B_TRUE; 16409860Sgdamore@opensolaris.org ph->phy_type = XCVR_100T2; 16419860Sgdamore@opensolaris.org } 16429860Sgdamore@opensolaris.org if (bmsr & MII_STATUS_100_BASE_T4) { 16439860Sgdamore@opensolaris.org ph->phy_cap_100_hdx = B_TRUE; 16449860Sgdamore@opensolaris.org ph->phy_type = XCVR_100T4; 16459860Sgdamore@opensolaris.org } 16469860Sgdamore@opensolaris.org if (bmsr & MII_STATUS_100_BASEX) { 16479860Sgdamore@opensolaris.org ph->phy_cap_100_hdx = B_TRUE; 16489860Sgdamore@opensolaris.org ph->phy_type = XCVR_100X; 16499860Sgdamore@opensolaris.org } 16509860Sgdamore@opensolaris.org if (bmsr & MII_STATUS_100_BASEX_FD) { 16519860Sgdamore@opensolaris.org ph->phy_cap_100_fdx = B_TRUE; 16529860Sgdamore@opensolaris.org ph->phy_type = XCVR_100X; 16539860Sgdamore@opensolaris.org } 16549860Sgdamore@opensolaris.org if (extsr & MII_EXTSTATUS_1000X) { 16559860Sgdamore@opensolaris.org ph->phy_cap_1000_hdx = B_TRUE; 16569860Sgdamore@opensolaris.org ph->phy_type = XCVR_1000X; 16579860Sgdamore@opensolaris.org } 16589860Sgdamore@opensolaris.org if (extsr & MII_EXTSTATUS_1000X_FD) { 16599860Sgdamore@opensolaris.org ph->phy_cap_1000_fdx = B_TRUE; 16609860Sgdamore@opensolaris.org ph->phy_type = XCVR_1000X; 16619860Sgdamore@opensolaris.org } 16629860Sgdamore@opensolaris.org if (extsr & MII_EXTSTATUS_1000T) { 16639860Sgdamore@opensolaris.org ph->phy_cap_1000_hdx = B_TRUE; 16649860Sgdamore@opensolaris.org ph->phy_type = XCVR_1000T; 16659860Sgdamore@opensolaris.org } 16669860Sgdamore@opensolaris.org if (extsr & MII_EXTSTATUS_1000T_FD) { 16679860Sgdamore@opensolaris.org ph->phy_cap_1000_fdx = B_TRUE; 16689860Sgdamore@opensolaris.org ph->phy_type = XCVR_1000T; 16699860Sgdamore@opensolaris.org } 16709860Sgdamore@opensolaris.org 16719860Sgdamore@opensolaris.org for (int j = 0; _phy_probes[j] != NULL; j++) { 16729860Sgdamore@opensolaris.org if ((*_phy_probes[j])(ph)) { 16739860Sgdamore@opensolaris.org break; 16749860Sgdamore@opensolaris.org } 16759860Sgdamore@opensolaris.org } 16769860Sgdamore@opensolaris.org 16779860Sgdamore@opensolaris.org #define INIT_ENABLE(CAP) \ 16789860Sgdamore@opensolaris.org ph->phy_en_##CAP = (mh->m_en_##CAP > 0) ? \ 16799860Sgdamore@opensolaris.org mh->m_en_##CAP : ph->phy_cap_##CAP 16809860Sgdamore@opensolaris.org 16819860Sgdamore@opensolaris.org INIT_ENABLE(aneg); 16829860Sgdamore@opensolaris.org INIT_ENABLE(1000_fdx); 16839860Sgdamore@opensolaris.org INIT_ENABLE(1000_hdx); 16849860Sgdamore@opensolaris.org INIT_ENABLE(100_fdx); 16859860Sgdamore@opensolaris.org INIT_ENABLE(100_t4); 16869860Sgdamore@opensolaris.org INIT_ENABLE(100_hdx); 16879860Sgdamore@opensolaris.org INIT_ENABLE(10_fdx); 16889860Sgdamore@opensolaris.org INIT_ENABLE(10_hdx); 16899860Sgdamore@opensolaris.org 16909860Sgdamore@opensolaris.org #undef INIT_ENABLE 16919860Sgdamore@opensolaris.org ph->phy_en_flowctrl = mh->m_en_flowctrl; 16929860Sgdamore@opensolaris.org switch (ph->phy_en_flowctrl) { 16939860Sgdamore@opensolaris.org case LINK_FLOWCTRL_BI: 16949860Sgdamore@opensolaris.org case LINK_FLOWCTRL_RX: 16959860Sgdamore@opensolaris.org ph->phy_en_pause = B_TRUE; 16969860Sgdamore@opensolaris.org ph->phy_en_asmpause = B_TRUE; 16979860Sgdamore@opensolaris.org break; 16989860Sgdamore@opensolaris.org case LINK_FLOWCTRL_TX: 16999860Sgdamore@opensolaris.org ph->phy_en_pause = B_FALSE; 17009860Sgdamore@opensolaris.org ph->phy_en_asmpause = B_TRUE; 17019860Sgdamore@opensolaris.org break; 17029860Sgdamore@opensolaris.org default: 17039860Sgdamore@opensolaris.org ph->phy_en_pause = B_FALSE; 17049860Sgdamore@opensolaris.org ph->phy_en_asmpause = B_FALSE; 17059860Sgdamore@opensolaris.org break; 17069860Sgdamore@opensolaris.org } 17079860Sgdamore@opensolaris.org } 17089860Sgdamore@opensolaris.org 17099860Sgdamore@opensolaris.org void 17109860Sgdamore@opensolaris.org _mii_probe_task(mii_handle_t mh) 17119860Sgdamore@opensolaris.org { 17129860Sgdamore@opensolaris.org uint8_t new_addr; 17139860Sgdamore@opensolaris.org uint8_t old_addr; 17149860Sgdamore@opensolaris.org uint8_t user_addr; 17159860Sgdamore@opensolaris.org uint8_t curr_addr; 17169860Sgdamore@opensolaris.org phy_handle_t *ph; 17179860Sgdamore@opensolaris.org int pri = 0; 17189860Sgdamore@opensolaris.org 17199860Sgdamore@opensolaris.org user_addr = ddi_prop_get_int(DDI_DEV_T_ANY, mh->m_dip, 0, 17209860Sgdamore@opensolaris.org "phy-addr", -1); 17219860Sgdamore@opensolaris.org old_addr = mh->m_addr; 17229860Sgdamore@opensolaris.org new_addr = 0xff; 17239860Sgdamore@opensolaris.org 17249860Sgdamore@opensolaris.org /* 17259860Sgdamore@opensolaris.org * Apparently, PHY 0 is less likely to be physically 17269860Sgdamore@opensolaris.org * connected, and should always be the last one tried. Most 17279860Sgdamore@opensolaris.org * single solution NICs use PHY1 for their built-in 17289860Sgdamore@opensolaris.org * transceiver. NICs with an external MII will often place 17299860Sgdamore@opensolaris.org * the external PHY at address 1, and use address 0 for the 17309860Sgdamore@opensolaris.org * internal PHY. 17319860Sgdamore@opensolaris.org */ 17329860Sgdamore@opensolaris.org 17339860Sgdamore@opensolaris.org for (int i = 1; i < 33; i++) { 17349860Sgdamore@opensolaris.org 17359860Sgdamore@opensolaris.org /* 17369860Sgdamore@opensolaris.org * This is tricky: it lets us try 0 last by starting 17379860Sgdamore@opensolaris.org * loop at 1 instead of 0. 17389860Sgdamore@opensolaris.org */ 17399860Sgdamore@opensolaris.org curr_addr = i % 32; 17409860Sgdamore@opensolaris.org 17419860Sgdamore@opensolaris.org ph = &mh->m_phys[curr_addr]; 17429860Sgdamore@opensolaris.org 17439860Sgdamore@opensolaris.org bzero(ph, sizeof (*ph)); 17449860Sgdamore@opensolaris.org ph->phy_addr = curr_addr; 17459860Sgdamore@opensolaris.org ph->phy_mii = mh; 17469860Sgdamore@opensolaris.org 17479860Sgdamore@opensolaris.org _mii_probe_phy(ph); 17489860Sgdamore@opensolaris.org 17499860Sgdamore@opensolaris.org if (!ph->phy_present) 17509860Sgdamore@opensolaris.org continue; 17519860Sgdamore@opensolaris.org 17529860Sgdamore@opensolaris.org if (curr_addr == user_addr) { 17539860Sgdamore@opensolaris.org /* 17549860Sgdamore@opensolaris.org * We always try to honor the user configured phy. 17559860Sgdamore@opensolaris.org */ 17569860Sgdamore@opensolaris.org new_addr = curr_addr; 17579860Sgdamore@opensolaris.org pri = 4; 17589860Sgdamore@opensolaris.org 17599860Sgdamore@opensolaris.org } 17609860Sgdamore@opensolaris.org 17619860Sgdamore@opensolaris.org /* two reads to clear latched bits */ 17629860Sgdamore@opensolaris.org if ((phy_read(ph, MII_STATUS) & MII_STATUS_LINKUP) && 17639860Sgdamore@opensolaris.org (phy_read(ph, MII_STATUS) & MII_STATUS_LINKUP) && 17649860Sgdamore@opensolaris.org (pri < 3)) { 17659860Sgdamore@opensolaris.org /* 17669860Sgdamore@opensolaris.org * Link present is good. We prefer this over 17679860Sgdamore@opensolaris.org * a possibly disconnected link. 17689860Sgdamore@opensolaris.org */ 17699860Sgdamore@opensolaris.org new_addr = curr_addr; 17709860Sgdamore@opensolaris.org pri = 3; 17719860Sgdamore@opensolaris.org } 17729860Sgdamore@opensolaris.org if ((curr_addr == old_addr) && (pri < 2)) { 17739860Sgdamore@opensolaris.org /* 17749860Sgdamore@opensolaris.org * All else being equal, minimize change. 17759860Sgdamore@opensolaris.org */ 17769860Sgdamore@opensolaris.org new_addr = curr_addr; 17779860Sgdamore@opensolaris.org pri = 2; 17789860Sgdamore@opensolaris.org 17799860Sgdamore@opensolaris.org } 17809860Sgdamore@opensolaris.org if (pri < 1) { 17819860Sgdamore@opensolaris.org /* 17829860Sgdamore@opensolaris.org * But make sure we at least select a present PHY. 17839860Sgdamore@opensolaris.org */ 17849860Sgdamore@opensolaris.org new_addr = curr_addr; 17859860Sgdamore@opensolaris.org pri = 1; 17869860Sgdamore@opensolaris.org } 17879860Sgdamore@opensolaris.org } 17889860Sgdamore@opensolaris.org 17899860Sgdamore@opensolaris.org if (new_addr == 0xff) { 17909860Sgdamore@opensolaris.org mh->m_addr = -1; 17919860Sgdamore@opensolaris.org mh->m_phy = &mh->m_bogus_phy; 17929860Sgdamore@opensolaris.org _mii_error(mh, MII_ENOPHY); 17939860Sgdamore@opensolaris.org } else { 17949860Sgdamore@opensolaris.org mh->m_addr = new_addr; 17959860Sgdamore@opensolaris.org mh->m_phy = &mh->m_phys[new_addr]; 17969860Sgdamore@opensolaris.org mh->m_tstate = MII_STATE_RESET; 17979860Sgdamore@opensolaris.org cmn_err(CE_CONT, "?%s: Using %s Ethernet PHY at %d: %s %s\n", 17989860Sgdamore@opensolaris.org mh->m_name, mii_xcvr_types[mh->m_phy->phy_type], 17999860Sgdamore@opensolaris.org mh->m_addr, mh->m_phy->phy_vendor, mh->m_phy->phy_model); 18009860Sgdamore@opensolaris.org } 18019860Sgdamore@opensolaris.org } 18029860Sgdamore@opensolaris.org 18039860Sgdamore@opensolaris.org static int 18049860Sgdamore@opensolaris.org _mii_reset_task(mii_handle_t mh) 18059860Sgdamore@opensolaris.org { 18069860Sgdamore@opensolaris.org phy_handle_t *ph; 18079860Sgdamore@opensolaris.org 18089860Sgdamore@opensolaris.org ASSERT(mutex_owned(&mh->m_lock)); 18099860Sgdamore@opensolaris.org 18109860Sgdamore@opensolaris.org /* 18119860Sgdamore@opensolaris.org * Reset logic. We want to isolate all the other 18129860Sgdamore@opensolaris.org * phys that are not in use. 18139860Sgdamore@opensolaris.org */ 18149860Sgdamore@opensolaris.org for (int i = 0; i < 32; i++) { 18159860Sgdamore@opensolaris.org ph = &mh->m_phys[i]; 18169860Sgdamore@opensolaris.org 18179860Sgdamore@opensolaris.org if (!ph->phy_present) 18189860Sgdamore@opensolaris.org continue; 18199860Sgdamore@opensolaris.org 18209860Sgdamore@opensolaris.org /* don't touch our own phy, yet */ 18219860Sgdamore@opensolaris.org if (ph == mh->m_phy) 18229860Sgdamore@opensolaris.org continue; 18239860Sgdamore@opensolaris.org 18249860Sgdamore@opensolaris.org ph->phy_stop(ph); 18259860Sgdamore@opensolaris.org } 18269860Sgdamore@opensolaris.org 18279860Sgdamore@opensolaris.org ph = mh->m_phy; 18289860Sgdamore@opensolaris.org 18299860Sgdamore@opensolaris.org ASSERT(ph->phy_present); 18309860Sgdamore@opensolaris.org 18319860Sgdamore@opensolaris.org /* If we're resetting the PHY, then for sure we want to notify */ 18329860Sgdamore@opensolaris.org mh->m_notify = B_TRUE; 18339860Sgdamore@opensolaris.org 18349860Sgdamore@opensolaris.org if (ph->phy_reset(ph) != DDI_SUCCESS) { 18359860Sgdamore@opensolaris.org _mii_error(mh, MII_ERESET); 18369860Sgdamore@opensolaris.org return (DDI_FAILURE); 18379860Sgdamore@opensolaris.org } 18389860Sgdamore@opensolaris.org 18399860Sgdamore@opensolaris.org mh->m_tstate = MII_STATE_START; 18409860Sgdamore@opensolaris.org return (DDI_SUCCESS); 18419860Sgdamore@opensolaris.org } 18429860Sgdamore@opensolaris.org 18439860Sgdamore@opensolaris.org static void 18449860Sgdamore@opensolaris.org _mii_start_task(mii_handle_t mh) 18459860Sgdamore@opensolaris.org { 18469860Sgdamore@opensolaris.org phy_handle_t *ph; 18479860Sgdamore@opensolaris.org 18489860Sgdamore@opensolaris.org ph = mh->m_phy; 18499860Sgdamore@opensolaris.org 18509860Sgdamore@opensolaris.org ASSERT(mutex_owned(&mh->m_lock)); 18519860Sgdamore@opensolaris.org ASSERT(ph->phy_present); 18529860Sgdamore@opensolaris.org 18539860Sgdamore@opensolaris.org if (phy_start(ph) != DDI_SUCCESS) { 18549860Sgdamore@opensolaris.org _mii_error(mh, MII_ESTART); 18559860Sgdamore@opensolaris.org mh->m_tstate = MII_STATE_RESET; 18569860Sgdamore@opensolaris.org return; 18579860Sgdamore@opensolaris.org } 18589860Sgdamore@opensolaris.org /* clear the error state since we got a good startup! */ 18599860Sgdamore@opensolaris.org mh->m_error = MII_EOK; 18609860Sgdamore@opensolaris.org mh->m_tstate = MII_STATE_RUN; 18619860Sgdamore@opensolaris.org } 18629860Sgdamore@opensolaris.org 18639860Sgdamore@opensolaris.org static int 18649860Sgdamore@opensolaris.org _mii_check_task(mii_handle_t mh) 18659860Sgdamore@opensolaris.org { 18669860Sgdamore@opensolaris.org link_state_t olink; 18679860Sgdamore@opensolaris.org int ospeed; 18689860Sgdamore@opensolaris.org link_duplex_t oduplex; 18699860Sgdamore@opensolaris.org link_flowctrl_t ofctrl; 18709860Sgdamore@opensolaris.org phy_handle_t *ph; 18719860Sgdamore@opensolaris.org 18729860Sgdamore@opensolaris.org ph = mh->m_phy; 18739860Sgdamore@opensolaris.org 18749860Sgdamore@opensolaris.org olink = mh->m_link; 18759860Sgdamore@opensolaris.org ospeed = ph->phy_speed; 18769860Sgdamore@opensolaris.org oduplex = ph->phy_duplex; 18779860Sgdamore@opensolaris.org ofctrl = ph->phy_flowctrl; 18789860Sgdamore@opensolaris.org 18799860Sgdamore@opensolaris.org ASSERT(ph->phy_present); 18809860Sgdamore@opensolaris.org 18819860Sgdamore@opensolaris.org if (ph->phy_check(ph) == DDI_FAILURE) { 18829860Sgdamore@opensolaris.org _mii_error(mh, MII_ECHECK); 18839860Sgdamore@opensolaris.org mh->m_link = LINK_STATE_UNKNOWN; 18849860Sgdamore@opensolaris.org mh->m_tstate = MII_STATE_PROBE; 18859860Sgdamore@opensolaris.org mh->m_notify = B_TRUE; 18869860Sgdamore@opensolaris.org return (DDI_FAILURE); 18879860Sgdamore@opensolaris.org } 18889860Sgdamore@opensolaris.org 18899860Sgdamore@opensolaris.org mh->m_link = ph->phy_link; 18909860Sgdamore@opensolaris.org 18919860Sgdamore@opensolaris.org /* if anything changed, notify! */ 18929860Sgdamore@opensolaris.org if ((mh->m_link != olink) || 18939860Sgdamore@opensolaris.org (ph->phy_speed != ospeed) || 18949860Sgdamore@opensolaris.org (ph->phy_duplex != oduplex) || 18959860Sgdamore@opensolaris.org (ph->phy_flowctrl != ofctrl)) { 18969860Sgdamore@opensolaris.org mh->m_notify = B_TRUE; 18979860Sgdamore@opensolaris.org } 18989860Sgdamore@opensolaris.org 18999860Sgdamore@opensolaris.org return (DDI_SUCCESS); 19009860Sgdamore@opensolaris.org } 19019860Sgdamore@opensolaris.org 19029860Sgdamore@opensolaris.org static void 19039860Sgdamore@opensolaris.org _mii_task(void *_mh) 19049860Sgdamore@opensolaris.org { 19059860Sgdamore@opensolaris.org mii_handle_t mh = _mh; 19069860Sgdamore@opensolaris.org phy_handle_t *ph; 19079860Sgdamore@opensolaris.org clock_t wait; 19089860Sgdamore@opensolaris.org clock_t downtime; 19099860Sgdamore@opensolaris.org 19109860Sgdamore@opensolaris.org mutex_enter(&mh->m_lock); 19119860Sgdamore@opensolaris.org 19129860Sgdamore@opensolaris.org for (;;) { 19139860Sgdamore@opensolaris.org 19149860Sgdamore@opensolaris.org /* If detaching, exit the thread. */ 19159860Sgdamore@opensolaris.org if (!mh->m_started) { 19169860Sgdamore@opensolaris.org break; 19179860Sgdamore@opensolaris.org } 19189860Sgdamore@opensolaris.org 19199860Sgdamore@opensolaris.org ph = mh->m_phy; 19209860Sgdamore@opensolaris.org 19219860Sgdamore@opensolaris.org /* 19229860Sgdamore@opensolaris.org * If we're suspended or otherwise not supposed to be 19239860Sgdamore@opensolaris.org * monitoring the link, just go back to sleep. 19249860Sgdamore@opensolaris.org * 19259860Sgdamore@opensolaris.org * Theoretically we could power down the PHY, but we 19269860Sgdamore@opensolaris.org * don't bother. (The link might be used for 19279860Sgdamore@opensolaris.org * wake-on-lan!) Another option would be to reduce 19289860Sgdamore@opensolaris.org * power on the PHY if both it and the link partner 19299860Sgdamore@opensolaris.org * support 10 Mbps mode. 19309860Sgdamore@opensolaris.org */ 19319860Sgdamore@opensolaris.org if (mh->m_suspending) { 19329860Sgdamore@opensolaris.org mh->m_suspended = B_TRUE; 19339860Sgdamore@opensolaris.org cv_broadcast(&mh->m_cv); 19349860Sgdamore@opensolaris.org } 19359860Sgdamore@opensolaris.org if (mh->m_suspended) { 19369860Sgdamore@opensolaris.org mh->m_suspending = B_FALSE; 19379860Sgdamore@opensolaris.org cv_wait(&mh->m_cv, &mh->m_lock); 19389860Sgdamore@opensolaris.org continue; 19399860Sgdamore@opensolaris.org } 19409860Sgdamore@opensolaris.org 19419860Sgdamore@opensolaris.org switch (mh->m_tstate) { 19429860Sgdamore@opensolaris.org case MII_STATE_PROBE: 19439860Sgdamore@opensolaris.org _mii_probe_task(mh); 19449860Sgdamore@opensolaris.org ph = mh->m_phy; 19459860Sgdamore@opensolaris.org if (!ph->phy_present) { 19469860Sgdamore@opensolaris.org /* 19479860Sgdamore@opensolaris.org * If no PHY is found, wait a bit before 19489860Sgdamore@opensolaris.org * trying the probe again. 10 seconds ought 19499860Sgdamore@opensolaris.org * to be enough. 19509860Sgdamore@opensolaris.org */ 19519860Sgdamore@opensolaris.org wait = 10 * MII_SECOND; 19529860Sgdamore@opensolaris.org } else { 19539860Sgdamore@opensolaris.org wait = 0; 19549860Sgdamore@opensolaris.org } 19559860Sgdamore@opensolaris.org break; 19569860Sgdamore@opensolaris.org 19579860Sgdamore@opensolaris.org case MII_STATE_RESET: 19589860Sgdamore@opensolaris.org if (_mii_reset_task(mh) == DDI_SUCCESS) { 19599860Sgdamore@opensolaris.org ASSERT(mh->m_tstate == MII_STATE_START); 19609860Sgdamore@opensolaris.org 19619860Sgdamore@opensolaris.org /* 19629860Sgdamore@opensolaris.org * We have to go back to the top of 19639860Sgdamore@opensolaris.org * the routine to check for changed 19649860Sgdamore@opensolaris.org * conditions while we drop the lock 19659860Sgdamore@opensolaris.org * to call into the mac layer. 19669860Sgdamore@opensolaris.org */ 19679860Sgdamore@opensolaris.org if (mh->m_ops.mii_reset != NULL) { 19689860Sgdamore@opensolaris.org mutex_exit(&mh->m_lock); 19699860Sgdamore@opensolaris.org mh->m_ops.mii_reset(mh->m_private); 19709860Sgdamore@opensolaris.org mutex_enter(&mh->m_lock); 19719860Sgdamore@opensolaris.org continue; 19729860Sgdamore@opensolaris.org } 19739860Sgdamore@opensolaris.org wait = 0; 19749860Sgdamore@opensolaris.org } else { 19759860Sgdamore@opensolaris.org /* 19769860Sgdamore@opensolaris.org * If an error occurred, wait a bit and 19779860Sgdamore@opensolaris.org * try again later. 19789860Sgdamore@opensolaris.org */ 19799860Sgdamore@opensolaris.org wait = 10 * MII_SECOND; 19809860Sgdamore@opensolaris.org } 19819860Sgdamore@opensolaris.org break; 19829860Sgdamore@opensolaris.org 19839860Sgdamore@opensolaris.org case MII_STATE_START: 19849860Sgdamore@opensolaris.org /* 19859860Sgdamore@opensolaris.org * If an error occurs, we're going to go back to 19869860Sgdamore@opensolaris.org * probe or reset state. Otherwise we go to run 19879860Sgdamore@opensolaris.org * state. In all cases we want to wait 1 second 19889860Sgdamore@opensolaris.org * before doing anything else - either for link to 19899860Sgdamore@opensolaris.org * settle, or to give other code a chance to run 19909860Sgdamore@opensolaris.org * while we reset. 19919860Sgdamore@opensolaris.org */ 19929860Sgdamore@opensolaris.org _mii_start_task(mh); 19939860Sgdamore@opensolaris.org /* reset watchdog to latest */ 19949860Sgdamore@opensolaris.org downtime = ddi_get_lbolt(); 19959860Sgdamore@opensolaris.org wait = MII_SECOND; 19969860Sgdamore@opensolaris.org break; 19979860Sgdamore@opensolaris.org 19989860Sgdamore@opensolaris.org case MII_STATE_RUN: 19999860Sgdamore@opensolaris.org default: 20009860Sgdamore@opensolaris.org if (_mii_check_task(mh) == DDI_FAILURE) { 20019860Sgdamore@opensolaris.org /* 20029860Sgdamore@opensolaris.org * On error (PHY removed?), wait a 20039860Sgdamore@opensolaris.org * short bit before reprobing or 20049860Sgdamore@opensolaris.org * resetting. 20059860Sgdamore@opensolaris.org */ 20069860Sgdamore@opensolaris.org wait = MII_SECOND; 20079860Sgdamore@opensolaris.org 20089860Sgdamore@opensolaris.org } else if (mh->m_link == LINK_STATE_UP) { 20099860Sgdamore@opensolaris.org /* got goood link, so reset the watchdog */ 20109860Sgdamore@opensolaris.org downtime = ddi_get_lbolt(); 20119860Sgdamore@opensolaris.org /* rescan again in a second */ 20129860Sgdamore@opensolaris.org wait = MII_SECOND; 20139860Sgdamore@opensolaris.org 20149860Sgdamore@opensolaris.org } else if ((ddi_get_lbolt() - downtime) > 20159860Sgdamore@opensolaris.org (drv_usectohz(MII_SECOND * 10))) { 20169860Sgdamore@opensolaris.org 20179860Sgdamore@opensolaris.org /* 20189860Sgdamore@opensolaris.org * If we were down for 10 seconds, 20199860Sgdamore@opensolaris.org * hard reset the PHY. 20209860Sgdamore@opensolaris.org */ 20219860Sgdamore@opensolaris.org mh->m_tstate = MII_STATE_RESET; 20229860Sgdamore@opensolaris.org wait = 0; 20239860Sgdamore@opensolaris.org 20249860Sgdamore@opensolaris.org } else { 20259860Sgdamore@opensolaris.org /* 20269860Sgdamore@opensolaris.org * Otherwise, if we are still down, 20279860Sgdamore@opensolaris.org * rescan the link much more 20289860Sgdamore@opensolaris.org * frequently. We might be trying to 20299860Sgdamore@opensolaris.org * autonegotiate. 20309860Sgdamore@opensolaris.org */ 20319860Sgdamore@opensolaris.org wait = MII_SECOND / 4; 20329860Sgdamore@opensolaris.org } 20339860Sgdamore@opensolaris.org break; 20349860Sgdamore@opensolaris.org } 20359860Sgdamore@opensolaris.org 20369860Sgdamore@opensolaris.org if (mh->m_notify) { 20379860Sgdamore@opensolaris.org mh->m_notify = B_FALSE; 20389860Sgdamore@opensolaris.org 20399860Sgdamore@opensolaris.org if (mh->m_ops.mii_notify != NULL) { 20409860Sgdamore@opensolaris.org link_state_t state = mh->m_link; 20419860Sgdamore@opensolaris.org mutex_exit(&mh->m_lock); 20429860Sgdamore@opensolaris.org mh->m_ops.mii_notify(mh->m_private, state); 20439860Sgdamore@opensolaris.org mutex_enter(&mh->m_lock); 20449860Sgdamore@opensolaris.org continue; 20459860Sgdamore@opensolaris.org } 20469860Sgdamore@opensolaris.org } 20479860Sgdamore@opensolaris.org 20489860Sgdamore@opensolaris.org if (wait) 20499860Sgdamore@opensolaris.org (void) cv_timedwait(&mh->m_cv, &mh->m_lock, 20509860Sgdamore@opensolaris.org ddi_get_lbolt() + drv_usectohz(wait)); 20519860Sgdamore@opensolaris.org } 20529860Sgdamore@opensolaris.org 20539860Sgdamore@opensolaris.org mutex_exit(&mh->m_lock); 20549860Sgdamore@opensolaris.org } 2055