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