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