xref: /onnv-gate/usr/src/uts/common/io/mii/mii.c (revision 11878:ac93462db6d7)
19860Sgdamore@opensolaris.org /*
29860Sgdamore@opensolaris.org  * CDDL HEADER START
39860Sgdamore@opensolaris.org  *
49860Sgdamore@opensolaris.org  * The contents of this file are subject to the terms of the
59860Sgdamore@opensolaris.org  * Common Development and Distribution License (the "License").
69860Sgdamore@opensolaris.org  * You may not use this file except in compliance with the License.
79860Sgdamore@opensolaris.org  *
89860Sgdamore@opensolaris.org  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
99860Sgdamore@opensolaris.org  * or http://www.opensolaris.org/os/licensing.
109860Sgdamore@opensolaris.org  * See the License for the specific language governing permissions
119860Sgdamore@opensolaris.org  * and limitations under the License.
129860Sgdamore@opensolaris.org  *
139860Sgdamore@opensolaris.org  * When distributing Covered Code, include this CDDL HEADER in each
149860Sgdamore@opensolaris.org  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
159860Sgdamore@opensolaris.org  * If applicable, add the following below this CDDL HEADER, with the
169860Sgdamore@opensolaris.org  * fields enclosed by brackets "[]" replaced with your own identifying
179860Sgdamore@opensolaris.org  * information: Portions Copyright [yyyy] [name of copyright owner]
189860Sgdamore@opensolaris.org  *
199860Sgdamore@opensolaris.org  * CDDL HEADER END
209860Sgdamore@opensolaris.org  */
219860Sgdamore@opensolaris.org /*
2211414Sgdamore@opensolaris.org  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
239860Sgdamore@opensolaris.org  * Use is subject to license terms.
249860Sgdamore@opensolaris.org  */
259860Sgdamore@opensolaris.org 
269860Sgdamore@opensolaris.org /*
279860Sgdamore@opensolaris.org  * mii - MII/PHY support for MAC drivers
289860Sgdamore@opensolaris.org  *
299860Sgdamore@opensolaris.org  * Utility module to provide a consistent interface to a MAC driver accross
309860Sgdamore@opensolaris.org  * different implementations of PHY devices
319860Sgdamore@opensolaris.org  */
329860Sgdamore@opensolaris.org 
339860Sgdamore@opensolaris.org #include <sys/types.h>
349860Sgdamore@opensolaris.org #include <sys/debug.h>
359860Sgdamore@opensolaris.org #include <sys/errno.h>
369860Sgdamore@opensolaris.org #include <sys/param.h>
379860Sgdamore@opensolaris.org #include <sys/kmem.h>
389860Sgdamore@opensolaris.org #include <sys/conf.h>
399860Sgdamore@opensolaris.org #include <sys/ddi.h>
409860Sgdamore@opensolaris.org #include <sys/sunddi.h>
419860Sgdamore@opensolaris.org #include <sys/modctl.h>
429860Sgdamore@opensolaris.org #include <sys/cmn_err.h>
439860Sgdamore@opensolaris.org #include <sys/policy.h>
449860Sgdamore@opensolaris.org #include <sys/note.h>
459860Sgdamore@opensolaris.org #include <sys/strsun.h>
469860Sgdamore@opensolaris.org #include <sys/miiregs.h>
479860Sgdamore@opensolaris.org #include <sys/mac_provider.h>
489860Sgdamore@opensolaris.org #include <sys/mac_ether.h>
499860Sgdamore@opensolaris.org #include <sys/mii.h>
509860Sgdamore@opensolaris.org #include "miipriv.h"
519860Sgdamore@opensolaris.org 
529860Sgdamore@opensolaris.org #define	MII_SECOND	1000000
539860Sgdamore@opensolaris.org 
549860Sgdamore@opensolaris.org /* indices into error array */
559860Sgdamore@opensolaris.org enum {
569860Sgdamore@opensolaris.org 	MII_EOK = 0,
579860Sgdamore@opensolaris.org 	MII_ERESET,
589860Sgdamore@opensolaris.org 	MII_ESTART,
599860Sgdamore@opensolaris.org 	MII_ENOPHY,
609860Sgdamore@opensolaris.org 	MII_ECHECK,
6110359SGarrett.Damore@Sun.COM 	MII_ELOOP,
629860Sgdamore@opensolaris.org };
639860Sgdamore@opensolaris.org 
649860Sgdamore@opensolaris.org static const char *mii_errors[] = {
659860Sgdamore@opensolaris.org 	"",
669860Sgdamore@opensolaris.org 	"Failure resetting PHY.",
679860Sgdamore@opensolaris.org 	"Failure starting PHY.",
689860Sgdamore@opensolaris.org 	"No Ethernet PHY found.",
6910359SGarrett.Damore@Sun.COM 	"Failure reading PHY (removed?)",
7010359SGarrett.Damore@Sun.COM 	"Failure setting loopback."
719860Sgdamore@opensolaris.org };
729860Sgdamore@opensolaris.org 
739860Sgdamore@opensolaris.org /* Indexed by XCVR_ type */
749860Sgdamore@opensolaris.org static const const char *mii_xcvr_types[] = {
759860Sgdamore@opensolaris.org 	"Undefined",
769860Sgdamore@opensolaris.org 	"Unknown",
779860Sgdamore@opensolaris.org 	"10 Mbps",
789860Sgdamore@opensolaris.org 	"100BASE-T4",
799860Sgdamore@opensolaris.org 	"100BASE-X",
809860Sgdamore@opensolaris.org 	"100BASE-T2",
819860Sgdamore@opensolaris.org 	"1000BASE-X",
829860Sgdamore@opensolaris.org 	"1000BASE-T"
839860Sgdamore@opensolaris.org };
849860Sgdamore@opensolaris.org 
859860Sgdamore@opensolaris.org /* state machine */
869860Sgdamore@opensolaris.org typedef enum {
879860Sgdamore@opensolaris.org 	MII_STATE_PROBE = 0,
889860Sgdamore@opensolaris.org 	MII_STATE_RESET,
899860Sgdamore@opensolaris.org 	MII_STATE_START,
909860Sgdamore@opensolaris.org 	MII_STATE_RUN,
9110359SGarrett.Damore@Sun.COM 	MII_STATE_LOOPBACK,
929860Sgdamore@opensolaris.org } mii_tstate_t;
939860Sgdamore@opensolaris.org 
949860Sgdamore@opensolaris.org struct mii_handle {
959860Sgdamore@opensolaris.org 	dev_info_t	*m_dip;
969860Sgdamore@opensolaris.org 	void		*m_private;
979860Sgdamore@opensolaris.org 	mii_ops_t	m_ops;
989860Sgdamore@opensolaris.org 
999860Sgdamore@opensolaris.org 	kt_did_t	m_tq_id;
1009860Sgdamore@opensolaris.org 	kmutex_t	m_lock;
1019860Sgdamore@opensolaris.org 	kcondvar_t	m_cv;
1029860Sgdamore@opensolaris.org 	ddi_taskq_t	*m_tq;
1039860Sgdamore@opensolaris.org 	int		m_flags;
1049860Sgdamore@opensolaris.org 
1059860Sgdamore@opensolaris.org 	boolean_t	m_started;
1069860Sgdamore@opensolaris.org 	boolean_t	m_suspending;
1079860Sgdamore@opensolaris.org 	boolean_t	m_suspended;
1089860Sgdamore@opensolaris.org 	int		m_error;
1099860Sgdamore@opensolaris.org 	mii_tstate_t	m_tstate;
1109860Sgdamore@opensolaris.org 
1119860Sgdamore@opensolaris.org #define	MII_FLAG_EXIT		0x1	/* exit the thread */
1129860Sgdamore@opensolaris.org #define	MII_FLAG_STOP		0x2	/* shutdown MII monitoring */
1139860Sgdamore@opensolaris.org #define	MII_FLAG_RESET		0x4	/* reset the MII */
1149860Sgdamore@opensolaris.org #define	MII_FLAG_PROBE		0x8	/* probe for PHYs */
1159860Sgdamore@opensolaris.org #define	MII_FLAG_NOTIFY		0x10	/* notify about a change */
1169860Sgdamore@opensolaris.org #define	MII_FLAG_SUSPEND	0x20	/* monitoring suspended */
1179860Sgdamore@opensolaris.org #define	MII_FLAG_MACRESET	0x40	/* send reset to MAC */
1189860Sgdamore@opensolaris.org #define	MII_FLAG_PHYSTART	0x80	/* start up the PHY */
1199860Sgdamore@opensolaris.org 
1209860Sgdamore@opensolaris.org 	/* device name for printing, e.g. "hme0" */
1219860Sgdamore@opensolaris.org 	char		m_name[MODMAXNAMELEN + 16];
1229860Sgdamore@opensolaris.org 
1239860Sgdamore@opensolaris.org 	int		m_addr;
1249860Sgdamore@opensolaris.org 	phy_handle_t	m_phys[32];
1259860Sgdamore@opensolaris.org 	phy_handle_t	m_bogus_phy;
1269860Sgdamore@opensolaris.org 	phy_handle_t	*m_phy;
1279860Sgdamore@opensolaris.org 
1289860Sgdamore@opensolaris.org 	link_state_t	m_link;
1299860Sgdamore@opensolaris.org 
1309860Sgdamore@opensolaris.org 	/* these start out undefined, but get values due to mac_prop_set */
1319860Sgdamore@opensolaris.org 	int		m_en_aneg;
1329860Sgdamore@opensolaris.org 	int		m_en_10_hdx;
1339860Sgdamore@opensolaris.org 	int		m_en_10_fdx;
1349860Sgdamore@opensolaris.org 	int		m_en_100_t4;
1359860Sgdamore@opensolaris.org 	int		m_en_100_hdx;
1369860Sgdamore@opensolaris.org 	int		m_en_100_fdx;
1379860Sgdamore@opensolaris.org 	int		m_en_1000_hdx;
1389860Sgdamore@opensolaris.org 	int		m_en_1000_fdx;
1399860Sgdamore@opensolaris.org 	int		m_en_flowctrl;
1409860Sgdamore@opensolaris.org 
1419860Sgdamore@opensolaris.org 	boolean_t	m_cap_pause;
1429860Sgdamore@opensolaris.org 	boolean_t	m_cap_asmpause;
1439860Sgdamore@opensolaris.org };
1449860Sgdamore@opensolaris.org 
1459860Sgdamore@opensolaris.org 
1469860Sgdamore@opensolaris.org static void _mii_task(void *);
14710359SGarrett.Damore@Sun.COM static void _mii_probe_phy(phy_handle_t *);
14810359SGarrett.Damore@Sun.COM static void _mii_probe(mii_handle_t);
14910359SGarrett.Damore@Sun.COM static int _mii_reset(mii_handle_t);
15010359SGarrett.Damore@Sun.COM static int _mii_loopback(mii_handle_t);
15110359SGarrett.Damore@Sun.COM static void _mii_notify(mii_handle_t);
15210359SGarrett.Damore@Sun.COM static int _mii_check(mii_handle_t);
15310359SGarrett.Damore@Sun.COM static int _mii_start(mii_handle_t);
1549860Sgdamore@opensolaris.org 
1559860Sgdamore@opensolaris.org /*
1569860Sgdamore@opensolaris.org  * Loadable module structures/entrypoints
1579860Sgdamore@opensolaris.org  */
1589860Sgdamore@opensolaris.org 
1599860Sgdamore@opensolaris.org extern struct mod_ops mod_misc_ops;
1609860Sgdamore@opensolaris.org 
1619860Sgdamore@opensolaris.org static struct modlmisc modlmisc = {
1629860Sgdamore@opensolaris.org 	&mod_miscops,
1639860Sgdamore@opensolaris.org 	"802.3 MII support",
1649860Sgdamore@opensolaris.org };
1659860Sgdamore@opensolaris.org 
1669860Sgdamore@opensolaris.org static struct modlinkage modlinkage = {
1679860Sgdamore@opensolaris.org 	MODREV_1, &modlmisc, NULL
1689860Sgdamore@opensolaris.org };
1699860Sgdamore@opensolaris.org 
1709860Sgdamore@opensolaris.org int
_init(void)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
_fini(void)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
_info(struct modinfo * modinfop)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
_mii_error(mii_handle_t mh,int errno)1899860Sgdamore@opensolaris.org _mii_error(mii_handle_t mh, int errno)
1909860Sgdamore@opensolaris.org {
1919860Sgdamore@opensolaris.org 	/*
1929860Sgdamore@opensolaris.org 	 * This dumps an error message, but it avoids filling the log with
1939860Sgdamore@opensolaris.org 	 * repeated error messages.
1949860Sgdamore@opensolaris.org 	 */
1959860Sgdamore@opensolaris.org 	if (mh->m_error != errno) {
1969860Sgdamore@opensolaris.org 		cmn_err(CE_WARN, "%s: %s", mh->m_name, mii_errors[errno]);
1979860Sgdamore@opensolaris.org 		mh->m_error = errno;
1989860Sgdamore@opensolaris.org 	}
1999860Sgdamore@opensolaris.org }
2009860Sgdamore@opensolaris.org 
2019860Sgdamore@opensolaris.org /*
2029860Sgdamore@opensolaris.org  * Known list of specific PHY probes.
2039860Sgdamore@opensolaris.org  */
2049860Sgdamore@opensolaris.org typedef boolean_t (*phy_probe_t)(phy_handle_t *);
2059860Sgdamore@opensolaris.org phy_probe_t _phy_probes[] = {
2069860Sgdamore@opensolaris.org 	phy_natsemi_probe,
2079860Sgdamore@opensolaris.org 	phy_intel_probe,
2089860Sgdamore@opensolaris.org 	phy_qualsemi_probe,
2099860Sgdamore@opensolaris.org 	phy_cicada_probe,
21010359SGarrett.Damore@Sun.COM 	phy_marvell_probe,
21111453Sgdamore@opensolaris.org 	phy_realtek_probe,
2129860Sgdamore@opensolaris.org 	phy_other_probe,
2139860Sgdamore@opensolaris.org 	NULL
2149860Sgdamore@opensolaris.org };
2159860Sgdamore@opensolaris.org 
2169860Sgdamore@opensolaris.org /*
2179860Sgdamore@opensolaris.org  * MII Interface functions
2189860Sgdamore@opensolaris.org  */
2199860Sgdamore@opensolaris.org 
2209860Sgdamore@opensolaris.org mii_handle_t
mii_alloc_instance(void * private,dev_info_t * dip,int inst,mii_ops_t * ops)2219860Sgdamore@opensolaris.org mii_alloc_instance(void *private, dev_info_t *dip, int inst, mii_ops_t *ops)
2229860Sgdamore@opensolaris.org {
2239860Sgdamore@opensolaris.org 	mii_handle_t	mh;
2249860Sgdamore@opensolaris.org 	char		tqname[16];
2259860Sgdamore@opensolaris.org 
2269860Sgdamore@opensolaris.org 	if (ops->mii_version != MII_OPS_VERSION) {
2279860Sgdamore@opensolaris.org 		cmn_err(CE_WARN, "%s: incompatible MII version (%d)",
2289860Sgdamore@opensolaris.org 		    ddi_driver_name(dip), ops->mii_version);
2299860Sgdamore@opensolaris.org 		return (NULL);
2309860Sgdamore@opensolaris.org 	}
2319860Sgdamore@opensolaris.org 	mh = kmem_zalloc(sizeof (*mh), KM_SLEEP);
2329860Sgdamore@opensolaris.org 
2339860Sgdamore@opensolaris.org 	(void) snprintf(mh->m_name, sizeof (mh->m_name), "%s%d",
2349860Sgdamore@opensolaris.org 	    ddi_driver_name(dip), inst);
2359860Sgdamore@opensolaris.org 
2369860Sgdamore@opensolaris.org 	/* DDI will prepend the driver name */
2379860Sgdamore@opensolaris.org 	(void) snprintf(tqname, sizeof (tqname), "mii%d", inst);
2389860Sgdamore@opensolaris.org 
2399860Sgdamore@opensolaris.org 	mh->m_dip = dip;
2409860Sgdamore@opensolaris.org 	mh->m_ops = *ops;
2419860Sgdamore@opensolaris.org 	mh->m_private = private;
2429860Sgdamore@opensolaris.org 	mh->m_suspended = B_FALSE;
2439860Sgdamore@opensolaris.org 	mh->m_started = B_FALSE;
2449860Sgdamore@opensolaris.org 	mh->m_tstate = MII_STATE_PROBE;
24510479SGarrett.Damore@Sun.COM 	mh->m_link = LINK_STATE_UNKNOWN;
2469860Sgdamore@opensolaris.org 	mh->m_error = MII_EOK;
24710359SGarrett.Damore@Sun.COM 	mh->m_addr = -1;
2489860Sgdamore@opensolaris.org 	mutex_init(&mh->m_lock, NULL, MUTEX_DRIVER, NULL);
2499860Sgdamore@opensolaris.org 	cv_init(&mh->m_cv, NULL, CV_DRIVER, NULL);
2509860Sgdamore@opensolaris.org 
2519860Sgdamore@opensolaris.org 	mh->m_tq = ddi_taskq_create(dip, tqname, 1, TASKQ_DEFAULTPRI, 0);
2529860Sgdamore@opensolaris.org 	if (mh->m_tq == NULL) {
2539860Sgdamore@opensolaris.org 		cmn_err(CE_WARN, "%s: unable to create MII monitoring task",
2549860Sgdamore@opensolaris.org 		    ddi_driver_name(dip));
2559860Sgdamore@opensolaris.org 		cv_destroy(&mh->m_cv);
2569860Sgdamore@opensolaris.org 		mutex_destroy(&mh->m_lock);
2579860Sgdamore@opensolaris.org 		kmem_free(mh, sizeof (*mh));
2589860Sgdamore@opensolaris.org 		return (NULL);
2599860Sgdamore@opensolaris.org 	}
2609860Sgdamore@opensolaris.org 
2619860Sgdamore@opensolaris.org 	/*
2629860Sgdamore@opensolaris.org 	 * Initialize user prefs by loading properties.  Ultimately,
2639860Sgdamore@opensolaris.org 	 * Brussels interfaces would be superior here.
2649860Sgdamore@opensolaris.org 	 */
2659860Sgdamore@opensolaris.org #define	GETPROP(name)	ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0, name, -1)
2669860Sgdamore@opensolaris.org 	mh->m_en_aneg = GETPROP("adv_autoneg_cap");
2679860Sgdamore@opensolaris.org 	mh->m_en_10_hdx = GETPROP("adv_10hdx_cap");
2689860Sgdamore@opensolaris.org 	mh->m_en_10_fdx = GETPROP("adv_10fdx_cap");
2699860Sgdamore@opensolaris.org 	mh->m_en_100_hdx = GETPROP("adv_100hdx_cap");
2709860Sgdamore@opensolaris.org 	mh->m_en_100_fdx = GETPROP("adv_100fdx_cap");
2719860Sgdamore@opensolaris.org 	mh->m_en_100_t4 = GETPROP("adv_100T4_cap");
2729860Sgdamore@opensolaris.org 	mh->m_en_1000_hdx = GETPROP("adv_1000hdx_cap");
2739860Sgdamore@opensolaris.org 	mh->m_en_1000_fdx = GETPROP("adv_1000fdx_cap");
2749860Sgdamore@opensolaris.org 
2759860Sgdamore@opensolaris.org 	mh->m_cap_pause = B_FALSE;
2769860Sgdamore@opensolaris.org 	mh->m_cap_asmpause = B_FALSE;
2779860Sgdamore@opensolaris.org 
2789860Sgdamore@opensolaris.org 	bzero(&mh->m_bogus_phy, sizeof (mh->m_bogus_phy));
2799860Sgdamore@opensolaris.org 	mh->m_bogus_phy.phy_link = LINK_STATE_UNKNOWN;
2809860Sgdamore@opensolaris.org 	mh->m_bogus_phy.phy_duplex = LINK_DUPLEX_UNKNOWN;
2819860Sgdamore@opensolaris.org 	mh->m_bogus_phy.phy_addr = 0xff;
2829860Sgdamore@opensolaris.org 	mh->m_bogus_phy.phy_type = XCVR_NONE;
2839860Sgdamore@opensolaris.org 	mh->m_bogus_phy.phy_id = (uint32_t)-1;
2849860Sgdamore@opensolaris.org 	mh->m_bogus_phy.phy_loopback = PHY_LB_NONE;
2859860Sgdamore@opensolaris.org 	mh->m_bogus_phy.phy_flowctrl = LINK_FLOWCTRL_NONE;
2869860Sgdamore@opensolaris.org 	mh->m_phy = &mh->m_bogus_phy;
2879860Sgdamore@opensolaris.org 
2889860Sgdamore@opensolaris.org 	for (int i = 0; i < 32; i++) {
2899860Sgdamore@opensolaris.org 		mh->m_phys[i].phy_mii = mh;
2909860Sgdamore@opensolaris.org 	}
2919860Sgdamore@opensolaris.org 	mh->m_bogus_phy.phy_mii = mh;
2929860Sgdamore@opensolaris.org 
2939860Sgdamore@opensolaris.org 	return (mh);
2949860Sgdamore@opensolaris.org }
2959860Sgdamore@opensolaris.org 
2969860Sgdamore@opensolaris.org mii_handle_t
mii_alloc(void * private,dev_info_t * dip,mii_ops_t * ops)2979860Sgdamore@opensolaris.org mii_alloc(void *private, dev_info_t *dip, mii_ops_t *ops)
2989860Sgdamore@opensolaris.org {
2999860Sgdamore@opensolaris.org 	return (mii_alloc_instance(private, dip, ddi_get_instance(dip), ops));
3009860Sgdamore@opensolaris.org }
3019860Sgdamore@opensolaris.org 
3029860Sgdamore@opensolaris.org void
mii_set_pauseable(mii_handle_t mh,boolean_t pauseable,boolean_t asymetric)3039860Sgdamore@opensolaris.org mii_set_pauseable(mii_handle_t mh, boolean_t pauseable, boolean_t asymetric)
3049860Sgdamore@opensolaris.org {
3059860Sgdamore@opensolaris.org 	phy_handle_t	*ph;
3069860Sgdamore@opensolaris.org 
3079860Sgdamore@opensolaris.org 	mutex_enter(&mh->m_lock);
3089860Sgdamore@opensolaris.org 	ph = mh->m_phy;
3099860Sgdamore@opensolaris.org 	ph->phy_cap_pause = mh->m_cap_pause = pauseable;
3109860Sgdamore@opensolaris.org 	ph->phy_cap_asmpause = mh->m_cap_asmpause = asymetric;
3119860Sgdamore@opensolaris.org 	if (pauseable) {
3129860Sgdamore@opensolaris.org 		mh->m_en_flowctrl = LINK_FLOWCTRL_BI;
3139860Sgdamore@opensolaris.org 	} else {
3149860Sgdamore@opensolaris.org 		mh->m_en_flowctrl = LINK_FLOWCTRL_NONE;
3159860Sgdamore@opensolaris.org 	}
3169860Sgdamore@opensolaris.org 	mutex_exit(&mh->m_lock);
3179860Sgdamore@opensolaris.org }
3189860Sgdamore@opensolaris.org 
3199860Sgdamore@opensolaris.org void
mii_free(mii_handle_t mh)3209860Sgdamore@opensolaris.org mii_free(mii_handle_t mh)
3219860Sgdamore@opensolaris.org {
3229860Sgdamore@opensolaris.org 	mutex_enter(&mh->m_lock);
3239860Sgdamore@opensolaris.org 	mh->m_started = B_FALSE;
3249860Sgdamore@opensolaris.org 	cv_broadcast(&mh->m_cv);
3259860Sgdamore@opensolaris.org 	mutex_exit(&mh->m_lock);
3269860Sgdamore@opensolaris.org 
3279860Sgdamore@opensolaris.org 	ddi_taskq_destroy(mh->m_tq);
3289860Sgdamore@opensolaris.org 	mutex_destroy(&mh->m_lock);
3299860Sgdamore@opensolaris.org 	cv_destroy(&mh->m_cv);
3309860Sgdamore@opensolaris.org 	kmem_free(mh, sizeof (*mh));
3319860Sgdamore@opensolaris.org }
3329860Sgdamore@opensolaris.org 
3339860Sgdamore@opensolaris.org void
mii_reset(mii_handle_t mh)3349860Sgdamore@opensolaris.org mii_reset(mii_handle_t mh)
3359860Sgdamore@opensolaris.org {
3369860Sgdamore@opensolaris.org 	mutex_enter(&mh->m_lock);
3379860Sgdamore@opensolaris.org 	if (mh->m_tstate > MII_STATE_RESET)
3389860Sgdamore@opensolaris.org 		mh->m_tstate = MII_STATE_RESET;
3399860Sgdamore@opensolaris.org 	cv_broadcast(&mh->m_cv);
3409860Sgdamore@opensolaris.org 	mutex_exit(&mh->m_lock);
3419860Sgdamore@opensolaris.org }
3429860Sgdamore@opensolaris.org 
3439860Sgdamore@opensolaris.org void
mii_suspend(mii_handle_t mh)3449860Sgdamore@opensolaris.org mii_suspend(mii_handle_t mh)
3459860Sgdamore@opensolaris.org {
3469860Sgdamore@opensolaris.org 	mutex_enter(&mh->m_lock);
3479860Sgdamore@opensolaris.org 	while ((!mh->m_suspended) && (mh->m_started)) {
3489860Sgdamore@opensolaris.org 		mh->m_suspending = B_TRUE;
3499860Sgdamore@opensolaris.org 		cv_broadcast(&mh->m_cv);
3509860Sgdamore@opensolaris.org 		cv_wait(&mh->m_cv, &mh->m_lock);
3519860Sgdamore@opensolaris.org 	}
3529860Sgdamore@opensolaris.org 	mutex_exit(&mh->m_lock);
3539860Sgdamore@opensolaris.org }
3549860Sgdamore@opensolaris.org 
3559860Sgdamore@opensolaris.org void
mii_resume(mii_handle_t mh)3569860Sgdamore@opensolaris.org mii_resume(mii_handle_t mh)
3579860Sgdamore@opensolaris.org {
3589860Sgdamore@opensolaris.org 	mutex_enter(&mh->m_lock);
35910359SGarrett.Damore@Sun.COM 
36010359SGarrett.Damore@Sun.COM 	switch (mh->m_tstate) {
36110359SGarrett.Damore@Sun.COM 	case MII_STATE_PROBE:
36210359SGarrett.Damore@Sun.COM 		break;
36310359SGarrett.Damore@Sun.COM 	case MII_STATE_RESET:
36410359SGarrett.Damore@Sun.COM 	case MII_STATE_START:
36510359SGarrett.Damore@Sun.COM 	case MII_STATE_RUN:
36610359SGarrett.Damore@Sun.COM 		/* let monitor thread deal with this */
3679860Sgdamore@opensolaris.org 		mh->m_tstate = MII_STATE_RESET;
36810359SGarrett.Damore@Sun.COM 		break;
36910359SGarrett.Damore@Sun.COM 
37010359SGarrett.Damore@Sun.COM 	case MII_STATE_LOOPBACK:
37110359SGarrett.Damore@Sun.COM 		/* loopback is handled synchronously */
37210359SGarrett.Damore@Sun.COM 		(void) _mii_loopback(mh);
37310359SGarrett.Damore@Sun.COM 		break;
3749860Sgdamore@opensolaris.org 	}
37510359SGarrett.Damore@Sun.COM 
3769860Sgdamore@opensolaris.org 	mh->m_suspended = B_FALSE;
3779860Sgdamore@opensolaris.org 	cv_broadcast(&mh->m_cv);
3789860Sgdamore@opensolaris.org 	mutex_exit(&mh->m_lock);
3799860Sgdamore@opensolaris.org }
3809860Sgdamore@opensolaris.org 
3819860Sgdamore@opensolaris.org void
mii_start(mii_handle_t mh)3829860Sgdamore@opensolaris.org mii_start(mii_handle_t mh)
3839860Sgdamore@opensolaris.org {
3849860Sgdamore@opensolaris.org 	mutex_enter(&mh->m_lock);
3859860Sgdamore@opensolaris.org 	if (!mh->m_started) {
38610359SGarrett.Damore@Sun.COM 		mh->m_tstate = MII_STATE_PROBE;
3879860Sgdamore@opensolaris.org 		mh->m_started = B_TRUE;
3889860Sgdamore@opensolaris.org 		if (ddi_taskq_dispatch(mh->m_tq, _mii_task, mh, DDI_NOSLEEP) !=
3899860Sgdamore@opensolaris.org 		    DDI_SUCCESS) {
3909860Sgdamore@opensolaris.org 			cmn_err(CE_WARN,
3919860Sgdamore@opensolaris.org 			    "%s: unable to start MII monitoring task",
3929860Sgdamore@opensolaris.org 			    mh->m_name);
3939860Sgdamore@opensolaris.org 			mh->m_started = B_FALSE;
3949860Sgdamore@opensolaris.org 		}
3959860Sgdamore@opensolaris.org 	}
3969860Sgdamore@opensolaris.org 	cv_broadcast(&mh->m_cv);
3979860Sgdamore@opensolaris.org 	mutex_exit(&mh->m_lock);
3989860Sgdamore@opensolaris.org }
3999860Sgdamore@opensolaris.org 
4009860Sgdamore@opensolaris.org void
mii_stop(mii_handle_t mh)4019860Sgdamore@opensolaris.org mii_stop(mii_handle_t mh)
4029860Sgdamore@opensolaris.org {
4039860Sgdamore@opensolaris.org 	mutex_enter(&mh->m_lock);
4049860Sgdamore@opensolaris.org 	mh->m_started = B_FALSE;
40511428Sgdamore@opensolaris.org 	/*
40611428Sgdamore@opensolaris.org 	 * Reset link state to unknown defaults, since we're not
40711428Sgdamore@opensolaris.org 	 * monitoring it anymore.  We'll reprobe all link state later.
40811428Sgdamore@opensolaris.org 	 */
40911428Sgdamore@opensolaris.org 	mh->m_link = LINK_STATE_UNKNOWN;
41011428Sgdamore@opensolaris.org 	mh->m_phy = &mh->m_bogus_phy;
4119860Sgdamore@opensolaris.org 	cv_broadcast(&mh->m_cv);
4129860Sgdamore@opensolaris.org 	mutex_exit(&mh->m_lock);
41311428Sgdamore@opensolaris.org 	/*
41411428Sgdamore@opensolaris.org 	 * Notify the MAC driver.  This will allow it to call back
41511428Sgdamore@opensolaris.org 	 * into the MAC framework to clear any previous link state.
41611428Sgdamore@opensolaris.org 	 */
41711428Sgdamore@opensolaris.org 	_mii_notify(mh);
4189860Sgdamore@opensolaris.org }
4199860Sgdamore@opensolaris.org 
4209860Sgdamore@opensolaris.org void
mii_probe(mii_handle_t mh)4219860Sgdamore@opensolaris.org mii_probe(mii_handle_t mh)
4229860Sgdamore@opensolaris.org {
4239860Sgdamore@opensolaris.org 	mutex_enter(&mh->m_lock);
42410359SGarrett.Damore@Sun.COM 	_mii_probe(mh);
4259860Sgdamore@opensolaris.org 	mutex_exit(&mh->m_lock);
4269860Sgdamore@opensolaris.org }
4279860Sgdamore@opensolaris.org 
4289860Sgdamore@opensolaris.org void
mii_check(mii_handle_t mh)4299860Sgdamore@opensolaris.org mii_check(mii_handle_t mh)
4309860Sgdamore@opensolaris.org {
4319860Sgdamore@opensolaris.org 	mutex_enter(&mh->m_lock);
4329860Sgdamore@opensolaris.org 	cv_broadcast(&mh->m_cv);
4339860Sgdamore@opensolaris.org 	mutex_exit(&mh->m_lock);
4349860Sgdamore@opensolaris.org }
4359860Sgdamore@opensolaris.org 
4369860Sgdamore@opensolaris.org int
mii_get_speed(mii_handle_t mh)4379860Sgdamore@opensolaris.org mii_get_speed(mii_handle_t mh)
4389860Sgdamore@opensolaris.org {
4399860Sgdamore@opensolaris.org 	phy_handle_t	*ph = mh->m_phy;
4409860Sgdamore@opensolaris.org 
4419860Sgdamore@opensolaris.org 	return (ph->phy_speed);
4429860Sgdamore@opensolaris.org }
4439860Sgdamore@opensolaris.org 
4449860Sgdamore@opensolaris.org link_duplex_t
mii_get_duplex(mii_handle_t mh)4459860Sgdamore@opensolaris.org mii_get_duplex(mii_handle_t mh)
4469860Sgdamore@opensolaris.org {
4479860Sgdamore@opensolaris.org 	phy_handle_t	*ph = mh->m_phy;
4489860Sgdamore@opensolaris.org 
4499860Sgdamore@opensolaris.org 	return (ph->phy_duplex);
4509860Sgdamore@opensolaris.org }
4519860Sgdamore@opensolaris.org 
4529860Sgdamore@opensolaris.org link_state_t
mii_get_state(mii_handle_t mh)4539860Sgdamore@opensolaris.org mii_get_state(mii_handle_t mh)
4549860Sgdamore@opensolaris.org {
4559860Sgdamore@opensolaris.org 	phy_handle_t	*ph = mh->m_phy;
4569860Sgdamore@opensolaris.org 
4579860Sgdamore@opensolaris.org 	return (ph->phy_link);
4589860Sgdamore@opensolaris.org }
4599860Sgdamore@opensolaris.org 
4609860Sgdamore@opensolaris.org link_flowctrl_t
mii_get_flowctrl(mii_handle_t mh)4619860Sgdamore@opensolaris.org mii_get_flowctrl(mii_handle_t mh)
4629860Sgdamore@opensolaris.org {
4639860Sgdamore@opensolaris.org 	phy_handle_t	*ph = mh->m_phy;
4649860Sgdamore@opensolaris.org 
4659860Sgdamore@opensolaris.org 	return (ph->phy_flowctrl);
4669860Sgdamore@opensolaris.org }
4679860Sgdamore@opensolaris.org 
4689860Sgdamore@opensolaris.org int
mii_get_loopmodes(mii_handle_t mh,lb_property_t * modes)4699860Sgdamore@opensolaris.org mii_get_loopmodes(mii_handle_t mh, lb_property_t *modes)
4709860Sgdamore@opensolaris.org {
4719860Sgdamore@opensolaris.org 	phy_handle_t	*ph = mh->m_phy;
47210359SGarrett.Damore@Sun.COM 	int		cnt = 0;
47310359SGarrett.Damore@Sun.COM 	lb_property_t	lmodes[MII_LOOPBACK_MAX];
47410359SGarrett.Damore@Sun.COM 
47510359SGarrett.Damore@Sun.COM 	lmodes[cnt].lb_type = normal;
47610363Sgdamore@opensolaris.org 	(void) strlcpy(lmodes[cnt].key, "normal", sizeof (lmodes[cnt].key));
47710359SGarrett.Damore@Sun.COM 	lmodes[cnt].value = PHY_LB_NONE;
47810359SGarrett.Damore@Sun.COM 	cnt++;
47910359SGarrett.Damore@Sun.COM 
48010359SGarrett.Damore@Sun.COM 	if (ph->phy_cap_1000_fdx ||
48110359SGarrett.Damore@Sun.COM 	    ph->phy_cap_100_fdx ||
48210359SGarrett.Damore@Sun.COM 	    ph->phy_cap_10_fdx) {
48310359SGarrett.Damore@Sun.COM 		/* we only support full duplex internal phy testing */
48410359SGarrett.Damore@Sun.COM 		lmodes[cnt].lb_type = internal;
48510363Sgdamore@opensolaris.org 		(void) strlcpy(lmodes[cnt].key, "PHY",
48610363Sgdamore@opensolaris.org 		    sizeof (lmodes[cnt].key));
48710359SGarrett.Damore@Sun.COM 		lmodes[cnt].value = PHY_LB_INT_PHY;
48810359SGarrett.Damore@Sun.COM 		cnt++;
48910359SGarrett.Damore@Sun.COM 	}
4909860Sgdamore@opensolaris.org 
49110359SGarrett.Damore@Sun.COM 	if (ph->phy_cap_1000_fdx) {
49210359SGarrett.Damore@Sun.COM 		lmodes[cnt].lb_type = external;
49310363Sgdamore@opensolaris.org 		(void) strlcpy(lmodes[cnt].key, "1000Mbps",
49410363Sgdamore@opensolaris.org 		    sizeof (lmodes[cnt].key));
49510359SGarrett.Damore@Sun.COM 		lmodes[cnt].value = PHY_LB_EXT_1000;
49610359SGarrett.Damore@Sun.COM 		cnt++;
49710359SGarrett.Damore@Sun.COM 	}
49810359SGarrett.Damore@Sun.COM 
49910359SGarrett.Damore@Sun.COM 	if (ph->phy_cap_100_fdx) {
50010359SGarrett.Damore@Sun.COM 		lmodes[cnt].lb_type = external;
50110363Sgdamore@opensolaris.org 		(void) strlcpy(lmodes[cnt].key, "100Mbps",
50210363Sgdamore@opensolaris.org 		    sizeof (lmodes[cnt].key));
50310359SGarrett.Damore@Sun.COM 		lmodes[cnt].value = PHY_LB_EXT_100;
50410359SGarrett.Damore@Sun.COM 		cnt++;
50510359SGarrett.Damore@Sun.COM 	}
50610359SGarrett.Damore@Sun.COM 
50710359SGarrett.Damore@Sun.COM 	if (ph->phy_cap_10_fdx) {
50810359SGarrett.Damore@Sun.COM 		lmodes[cnt].lb_type = external;
50910363Sgdamore@opensolaris.org 		(void) strlcpy(lmodes[cnt].key, "10Mbps",
51010363Sgdamore@opensolaris.org 		    sizeof (lmodes[cnt].key));
51110359SGarrett.Damore@Sun.COM 		lmodes[cnt].value = PHY_LB_EXT_10;
51210359SGarrett.Damore@Sun.COM 		cnt++;
5139860Sgdamore@opensolaris.org 	}
5149860Sgdamore@opensolaris.org 
5159860Sgdamore@opensolaris.org 	if (modes) {
51610359SGarrett.Damore@Sun.COM 		bcopy(lmodes, modes, sizeof (lb_property_t) * cnt);
5179860Sgdamore@opensolaris.org 	}
51810359SGarrett.Damore@Sun.COM 
5199860Sgdamore@opensolaris.org 	return (cnt);
5209860Sgdamore@opensolaris.org }
5219860Sgdamore@opensolaris.org 
5229860Sgdamore@opensolaris.org uint32_t
mii_get_loopback(mii_handle_t mh)5239860Sgdamore@opensolaris.org mii_get_loopback(mii_handle_t mh)
5249860Sgdamore@opensolaris.org {
5259860Sgdamore@opensolaris.org 	phy_handle_t	*ph = mh->m_phy;
5269860Sgdamore@opensolaris.org 
5279860Sgdamore@opensolaris.org 	return (ph->phy_loopback);
5289860Sgdamore@opensolaris.org }
5299860Sgdamore@opensolaris.org 
5309860Sgdamore@opensolaris.org int
mii_set_loopback(mii_handle_t mh,uint32_t loop)5319860Sgdamore@opensolaris.org mii_set_loopback(mii_handle_t mh, uint32_t loop)
5329860Sgdamore@opensolaris.org {
5339860Sgdamore@opensolaris.org 	phy_handle_t	*ph;
53410359SGarrett.Damore@Sun.COM 	int		rv;
5359860Sgdamore@opensolaris.org 
5369860Sgdamore@opensolaris.org 	mutex_enter(&mh->m_lock);
5379860Sgdamore@opensolaris.org 	ph = mh->m_phy;
5389860Sgdamore@opensolaris.org 
5399860Sgdamore@opensolaris.org 	if ((!mh->m_started) || (!ph->phy_present) ||
5409860Sgdamore@opensolaris.org 	    (loop >= mii_get_loopmodes(mh, NULL))) {
5419860Sgdamore@opensolaris.org 		return (EINVAL);
5429860Sgdamore@opensolaris.org 	}
5439860Sgdamore@opensolaris.org 
5449860Sgdamore@opensolaris.org 	ph->phy_loopback = loop;
54510359SGarrett.Damore@Sun.COM 	rv = _mii_loopback(mh);
54610359SGarrett.Damore@Sun.COM 	if (rv == DDI_SUCCESS) {
54710359SGarrett.Damore@Sun.COM 		mh->m_tstate = MII_STATE_LOOPBACK;
54810359SGarrett.Damore@Sun.COM 	}
5499860Sgdamore@opensolaris.org 	cv_broadcast(&mh->m_cv);
5509860Sgdamore@opensolaris.org 	mutex_exit(&mh->m_lock);
5519860Sgdamore@opensolaris.org 
55210359SGarrett.Damore@Sun.COM 	return (rv == DDI_SUCCESS ? 0 : EIO);
5539860Sgdamore@opensolaris.org }
5549860Sgdamore@opensolaris.org 
5559860Sgdamore@opensolaris.org uint32_t
mii_get_id(mii_handle_t mh)5569860Sgdamore@opensolaris.org mii_get_id(mii_handle_t mh)
5579860Sgdamore@opensolaris.org {
5589860Sgdamore@opensolaris.org 	phy_handle_t	*ph = mh->m_phy;
5599860Sgdamore@opensolaris.org 
5609860Sgdamore@opensolaris.org 	return (ph->phy_id);
5619860Sgdamore@opensolaris.org }
5629860Sgdamore@opensolaris.org 
5639860Sgdamore@opensolaris.org int
mii_get_addr(mii_handle_t mh)5649860Sgdamore@opensolaris.org mii_get_addr(mii_handle_t mh)
5659860Sgdamore@opensolaris.org {
5669860Sgdamore@opensolaris.org 	return (mh->m_addr);
5679860Sgdamore@opensolaris.org }
5689860Sgdamore@opensolaris.org 
5699860Sgdamore@opensolaris.org /* GLDv3 helpers */
5709860Sgdamore@opensolaris.org 
5719860Sgdamore@opensolaris.org boolean_t
mii_m_loop_ioctl(mii_handle_t mh,queue_t * wq,mblk_t * mp)5729860Sgdamore@opensolaris.org mii_m_loop_ioctl(mii_handle_t mh, queue_t *wq, mblk_t *mp)
5739860Sgdamore@opensolaris.org {
5749860Sgdamore@opensolaris.org 	struct iocblk	*iocp;
5759860Sgdamore@opensolaris.org 	int		rv = 0;
5769860Sgdamore@opensolaris.org 	int		cnt;
5779860Sgdamore@opensolaris.org 	lb_property_t	modes[MII_LOOPBACK_MAX];
5789860Sgdamore@opensolaris.org 	lb_info_sz_t	sz;
5799860Sgdamore@opensolaris.org 	int		cmd;
5809860Sgdamore@opensolaris.org 	uint32_t	mode;
5819860Sgdamore@opensolaris.org 
5829860Sgdamore@opensolaris.org 	iocp = (void *)mp->b_rptr;
5839860Sgdamore@opensolaris.org 	cmd = iocp->ioc_cmd;
5849860Sgdamore@opensolaris.org 
5859860Sgdamore@opensolaris.org 	switch (cmd) {
5869860Sgdamore@opensolaris.org 	case LB_SET_MODE:
5879860Sgdamore@opensolaris.org 	case LB_GET_INFO_SIZE:
5889860Sgdamore@opensolaris.org 	case LB_GET_INFO:
5899860Sgdamore@opensolaris.org 	case LB_GET_MODE:
5909860Sgdamore@opensolaris.org 		break;
5919860Sgdamore@opensolaris.org 
5929860Sgdamore@opensolaris.org 	default:
5939860Sgdamore@opensolaris.org 		return (B_FALSE);
5949860Sgdamore@opensolaris.org 	}
5959860Sgdamore@opensolaris.org 
5969860Sgdamore@opensolaris.org 	if (mp->b_cont == NULL) {
5979860Sgdamore@opensolaris.org 		miocnak(wq, mp, 0, EINVAL);
5989860Sgdamore@opensolaris.org 		return (B_TRUE);
5999860Sgdamore@opensolaris.org 	}
6009860Sgdamore@opensolaris.org 
6019860Sgdamore@opensolaris.org 	switch (cmd) {
6029860Sgdamore@opensolaris.org 	case LB_GET_INFO_SIZE:
6039860Sgdamore@opensolaris.org 		cnt = mii_get_loopmodes(mh, modes);
6049860Sgdamore@opensolaris.org 		if (iocp->ioc_count != sizeof (sz)) {
6059860Sgdamore@opensolaris.org 			rv = EINVAL;
6069860Sgdamore@opensolaris.org 		} else {
6079860Sgdamore@opensolaris.org 			sz = cnt * sizeof (lb_property_t);
6089860Sgdamore@opensolaris.org 			bcopy(&sz, mp->b_cont->b_rptr, sizeof (sz));
6099860Sgdamore@opensolaris.org 		}
6109860Sgdamore@opensolaris.org 		break;
6119860Sgdamore@opensolaris.org 
6129860Sgdamore@opensolaris.org 	case LB_GET_INFO:
6139860Sgdamore@opensolaris.org 		cnt = mii_get_loopmodes(mh, modes);
6149860Sgdamore@opensolaris.org 		if (iocp->ioc_count != (cnt * sizeof (lb_property_t))) {
6159860Sgdamore@opensolaris.org 			rv = EINVAL;
6169860Sgdamore@opensolaris.org 		} else {
6179860Sgdamore@opensolaris.org 			bcopy(modes, mp->b_cont->b_rptr, iocp->ioc_count);
6189860Sgdamore@opensolaris.org 		}
6199860Sgdamore@opensolaris.org 		break;
6209860Sgdamore@opensolaris.org 
6219860Sgdamore@opensolaris.org 	case LB_GET_MODE:
6229860Sgdamore@opensolaris.org 		if (iocp->ioc_count != sizeof (mode)) {
6239860Sgdamore@opensolaris.org 			rv = EINVAL;
6249860Sgdamore@opensolaris.org 		} else {
6259860Sgdamore@opensolaris.org 			mode = mii_get_loopback(mh);
6269860Sgdamore@opensolaris.org 			bcopy(&mode, mp->b_cont->b_rptr, sizeof (mode));
6279860Sgdamore@opensolaris.org 		}
6289860Sgdamore@opensolaris.org 		break;
6299860Sgdamore@opensolaris.org 
6309860Sgdamore@opensolaris.org 	case LB_SET_MODE:
6319860Sgdamore@opensolaris.org 		rv = secpolicy_net_config(iocp->ioc_cr, B_FALSE);
6329860Sgdamore@opensolaris.org 		if (rv != 0)
6339860Sgdamore@opensolaris.org 			break;
6349860Sgdamore@opensolaris.org 		if (iocp->ioc_count != sizeof (mode)) {
6359860Sgdamore@opensolaris.org 			rv = EINVAL;
6369860Sgdamore@opensolaris.org 			break;
6379860Sgdamore@opensolaris.org 		}
6389860Sgdamore@opensolaris.org 		bcopy(mp->b_cont->b_rptr, &mode, sizeof (mode));
6399860Sgdamore@opensolaris.org 		rv = mii_set_loopback(mh, mode);
6409860Sgdamore@opensolaris.org 		break;
6419860Sgdamore@opensolaris.org 	}
6429860Sgdamore@opensolaris.org 
6439860Sgdamore@opensolaris.org 	if (rv == 0) {
6449860Sgdamore@opensolaris.org 		miocack(wq, mp, iocp->ioc_count, 0);
6459860Sgdamore@opensolaris.org 	} else {
6469860Sgdamore@opensolaris.org 		miocnak(wq, mp, 0, rv);
6479860Sgdamore@opensolaris.org 	}
6489860Sgdamore@opensolaris.org 	return (B_TRUE);
6499860Sgdamore@opensolaris.org }
6509860Sgdamore@opensolaris.org 
6519860Sgdamore@opensolaris.org int
mii_m_getprop(mii_handle_t mh,const char * name,mac_prop_id_t num,uint_t sz,void * val)6529860Sgdamore@opensolaris.org mii_m_getprop(mii_handle_t mh, const char *name, mac_prop_id_t num,
653*11878SVenu.Iyer@Sun.COM     uint_t sz, void *val)
6549860Sgdamore@opensolaris.org {
6559860Sgdamore@opensolaris.org 	phy_handle_t	*ph;
6569860Sgdamore@opensolaris.org 	int		err = 0;
6579860Sgdamore@opensolaris.org 
6589860Sgdamore@opensolaris.org 	_NOTE(ARGUNUSED(name));
6599860Sgdamore@opensolaris.org 
6609860Sgdamore@opensolaris.org 	if (sz < 1)
6619860Sgdamore@opensolaris.org 		return (EINVAL);
6629860Sgdamore@opensolaris.org 
6639860Sgdamore@opensolaris.org 	mutex_enter(&mh->m_lock);
6649860Sgdamore@opensolaris.org 
6659860Sgdamore@opensolaris.org 	ph = mh->m_phy;
6669860Sgdamore@opensolaris.org 
6679860Sgdamore@opensolaris.org #define	CASE_PROP_ABILITY(PROP, VAR)					\
6689860Sgdamore@opensolaris.org 	case MAC_PROP_ADV_##PROP:					\
669*11878SVenu.Iyer@Sun.COM 		*(uint8_t *)val = ph->phy_adv_##VAR;			\
6709860Sgdamore@opensolaris.org 		break;							\
6719860Sgdamore@opensolaris.org 									\
6729860Sgdamore@opensolaris.org 	case MAC_PROP_EN_##PROP:					\
673*11878SVenu.Iyer@Sun.COM 		*(uint8_t *)val = ph->phy_en_##VAR;			\
6749860Sgdamore@opensolaris.org 		break;
6759860Sgdamore@opensolaris.org 
6769860Sgdamore@opensolaris.org 	switch (num) {
6779860Sgdamore@opensolaris.org 	case MAC_PROP_DUPLEX:
678*11878SVenu.Iyer@Sun.COM 		ASSERT(sz >= sizeof (link_duplex_t));
679*11878SVenu.Iyer@Sun.COM 		bcopy(&ph->phy_duplex, val, sizeof (link_duplex_t));
6809860Sgdamore@opensolaris.org 		break;
6819860Sgdamore@opensolaris.org 
682*11878SVenu.Iyer@Sun.COM 	case MAC_PROP_SPEED: {
683*11878SVenu.Iyer@Sun.COM 		uint64_t speed = ph->phy_speed * 1000000ull;
684*11878SVenu.Iyer@Sun.COM 		ASSERT(sz >= sizeof (uint64_t));
685*11878SVenu.Iyer@Sun.COM 		bcopy(&speed, val, sizeof (speed));
6869860Sgdamore@opensolaris.org 		break;
687*11878SVenu.Iyer@Sun.COM 	}
6889860Sgdamore@opensolaris.org 
6899860Sgdamore@opensolaris.org 	case MAC_PROP_AUTONEG:
690*11878SVenu.Iyer@Sun.COM 		*(uint8_t *)val = ph->phy_adv_aneg;
6919860Sgdamore@opensolaris.org 		break;
6929860Sgdamore@opensolaris.org 
6939860Sgdamore@opensolaris.org 	case MAC_PROP_FLOWCTRL:
694*11878SVenu.Iyer@Sun.COM 		ASSERT(sz >= sizeof (link_flowctrl_t));
695*11878SVenu.Iyer@Sun.COM 		bcopy(&ph->phy_flowctrl, val, sizeof (link_flowctrl_t));
6969860Sgdamore@opensolaris.org 		break;
6979860Sgdamore@opensolaris.org 
6989860Sgdamore@opensolaris.org 	CASE_PROP_ABILITY(1000FDX_CAP, 1000_fdx)
6999860Sgdamore@opensolaris.org 	CASE_PROP_ABILITY(1000HDX_CAP, 1000_hdx)
7009860Sgdamore@opensolaris.org 	CASE_PROP_ABILITY(100T4_CAP, 100_t4)
7019860Sgdamore@opensolaris.org 	CASE_PROP_ABILITY(100FDX_CAP, 100_fdx)
7029860Sgdamore@opensolaris.org 	CASE_PROP_ABILITY(100HDX_CAP, 100_hdx)
7039860Sgdamore@opensolaris.org 	CASE_PROP_ABILITY(10FDX_CAP, 10_fdx)
7049860Sgdamore@opensolaris.org 	CASE_PROP_ABILITY(10HDX_CAP, 10_hdx)
7059860Sgdamore@opensolaris.org 
7069860Sgdamore@opensolaris.org 	default:
7079860Sgdamore@opensolaris.org 		err = ENOTSUP;
7089860Sgdamore@opensolaris.org 		break;
7099860Sgdamore@opensolaris.org 	}
7109860Sgdamore@opensolaris.org 
711*11878SVenu.Iyer@Sun.COM 	mutex_exit(&mh->m_lock);
712*11878SVenu.Iyer@Sun.COM 
713*11878SVenu.Iyer@Sun.COM 	return (err);
714*11878SVenu.Iyer@Sun.COM }
715*11878SVenu.Iyer@Sun.COM 
716*11878SVenu.Iyer@Sun.COM void
mii_m_propinfo(mii_handle_t mh,const char * name,mac_prop_id_t num,mac_prop_info_handle_t prh)717*11878SVenu.Iyer@Sun.COM mii_m_propinfo(mii_handle_t mh, const char *name, mac_prop_id_t num,
718*11878SVenu.Iyer@Sun.COM     mac_prop_info_handle_t prh)
719*11878SVenu.Iyer@Sun.COM {
720*11878SVenu.Iyer@Sun.COM 	phy_handle_t	*ph;
721*11878SVenu.Iyer@Sun.COM 
722*11878SVenu.Iyer@Sun.COM 	_NOTE(ARGUNUSED(name));
723*11878SVenu.Iyer@Sun.COM 
724*11878SVenu.Iyer@Sun.COM 	mutex_enter(&mh->m_lock);
725*11878SVenu.Iyer@Sun.COM 
726*11878SVenu.Iyer@Sun.COM 	ph = mh->m_phy;
727*11878SVenu.Iyer@Sun.COM 
728*11878SVenu.Iyer@Sun.COM 	switch (num) {
729*11878SVenu.Iyer@Sun.COM 	case MAC_PROP_DUPLEX:
730*11878SVenu.Iyer@Sun.COM 	case MAC_PROP_SPEED:
731*11878SVenu.Iyer@Sun.COM 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
732*11878SVenu.Iyer@Sun.COM 		break;
733*11878SVenu.Iyer@Sun.COM 
734*11878SVenu.Iyer@Sun.COM 	case MAC_PROP_AUTONEG:
735*11878SVenu.Iyer@Sun.COM 		mac_prop_info_set_default_uint8(prh, ph->phy_cap_aneg);
736*11878SVenu.Iyer@Sun.COM 		break;
737*11878SVenu.Iyer@Sun.COM 
738*11878SVenu.Iyer@Sun.COM #define	CASE_PROP_PERM(PROP, VAR)					\
739*11878SVenu.Iyer@Sun.COM 	case MAC_PROP_ADV_##PROP:					\
740*11878SVenu.Iyer@Sun.COM 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);	\
741*11878SVenu.Iyer@Sun.COM 		mac_prop_info_set_default_uint8(prh, ph->phy_cap_##VAR); \
742*11878SVenu.Iyer@Sun.COM 		break;							\
743*11878SVenu.Iyer@Sun.COM 									\
744*11878SVenu.Iyer@Sun.COM 	case MAC_PROP_EN_##PROP:					\
745*11878SVenu.Iyer@Sun.COM 		if (!ph->phy_cap_##VAR)					\
746*11878SVenu.Iyer@Sun.COM 			mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ); \
747*11878SVenu.Iyer@Sun.COM 		mac_prop_info_set_default_uint8(prh, ph->phy_cap_##VAR); \
748*11878SVenu.Iyer@Sun.COM 		break;
749*11878SVenu.Iyer@Sun.COM 
750*11878SVenu.Iyer@Sun.COM 	CASE_PROP_PERM(1000FDX_CAP, 1000_fdx)
751*11878SVenu.Iyer@Sun.COM 	CASE_PROP_PERM(1000HDX_CAP, 1000_hdx)
752*11878SVenu.Iyer@Sun.COM 	CASE_PROP_PERM(100T4_CAP, 100_t4)
753*11878SVenu.Iyer@Sun.COM 	CASE_PROP_PERM(100FDX_CAP, 100_fdx)
754*11878SVenu.Iyer@Sun.COM 	CASE_PROP_PERM(100HDX_CAP, 100_hdx)
755*11878SVenu.Iyer@Sun.COM 	CASE_PROP_PERM(10FDX_CAP, 10_fdx)
756*11878SVenu.Iyer@Sun.COM 	CASE_PROP_PERM(10HDX_CAP, 10_hdx)
7579860Sgdamore@opensolaris.org 	}
7589860Sgdamore@opensolaris.org 
7599860Sgdamore@opensolaris.org 	mutex_exit(&mh->m_lock);
7609860Sgdamore@opensolaris.org }
7619860Sgdamore@opensolaris.org 
7629860Sgdamore@opensolaris.org int
mii_m_setprop(mii_handle_t mh,const char * name,mac_prop_id_t num,uint_t sz,const void * valp)7639860Sgdamore@opensolaris.org mii_m_setprop(mii_handle_t mh, const char *name, mac_prop_id_t num,
7649860Sgdamore@opensolaris.org     uint_t sz, const void *valp)
7659860Sgdamore@opensolaris.org {
7669860Sgdamore@opensolaris.org 	phy_handle_t	*ph;
7679860Sgdamore@opensolaris.org 	boolean_t	*advp = NULL;
7689860Sgdamore@opensolaris.org 	boolean_t	*capp = NULL;
7699860Sgdamore@opensolaris.org 	int		*macpp = NULL;
7709860Sgdamore@opensolaris.org 	int		rv = ENOTSUP;
7719860Sgdamore@opensolaris.org 
7729860Sgdamore@opensolaris.org 	_NOTE(ARGUNUSED(name));
7739860Sgdamore@opensolaris.org 
7749860Sgdamore@opensolaris.org 	if (sz < 1)
7759860Sgdamore@opensolaris.org 		return (EINVAL);
7769860Sgdamore@opensolaris.org 
7779860Sgdamore@opensolaris.org 	mutex_enter(&mh->m_lock);
7789860Sgdamore@opensolaris.org 
7799860Sgdamore@opensolaris.org 	ph = mh->m_phy;
7809860Sgdamore@opensolaris.org 
7819860Sgdamore@opensolaris.org 	/* we don't support changing parameters while in loopback mode */
7829860Sgdamore@opensolaris.org 	if (ph->phy_loopback != PHY_LB_NONE) {
7839860Sgdamore@opensolaris.org 		switch (num) {
7849860Sgdamore@opensolaris.org 		case MAC_PROP_EN_1000FDX_CAP:
7859860Sgdamore@opensolaris.org 		case MAC_PROP_EN_1000HDX_CAP:
7869860Sgdamore@opensolaris.org 		case MAC_PROP_EN_100FDX_CAP:
7879860Sgdamore@opensolaris.org 		case MAC_PROP_EN_100HDX_CAP:
7889860Sgdamore@opensolaris.org 		case MAC_PROP_EN_100T4_CAP:
7899860Sgdamore@opensolaris.org 		case MAC_PROP_EN_10FDX_CAP:
7909860Sgdamore@opensolaris.org 		case MAC_PROP_EN_10HDX_CAP:
7919860Sgdamore@opensolaris.org 		case MAC_PROP_AUTONEG:
7929860Sgdamore@opensolaris.org 		case MAC_PROP_FLOWCTRL:
7939860Sgdamore@opensolaris.org 			return (EBUSY);
7949860Sgdamore@opensolaris.org 		}
7959860Sgdamore@opensolaris.org 	}
7969860Sgdamore@opensolaris.org 
7979860Sgdamore@opensolaris.org 	switch (num) {
7989860Sgdamore@opensolaris.org 	case MAC_PROP_EN_1000FDX_CAP:
7999860Sgdamore@opensolaris.org 		capp = &ph->phy_cap_1000_fdx;
8009860Sgdamore@opensolaris.org 		advp = &ph->phy_en_1000_fdx;
8019860Sgdamore@opensolaris.org 		macpp = &mh->m_en_1000_fdx;
8029860Sgdamore@opensolaris.org 		break;
8039860Sgdamore@opensolaris.org 	case MAC_PROP_EN_1000HDX_CAP:
8049860Sgdamore@opensolaris.org 		capp = &ph->phy_cap_1000_hdx;
8059860Sgdamore@opensolaris.org 		advp = &ph->phy_en_1000_hdx;
8069860Sgdamore@opensolaris.org 		macpp = &mh->m_en_1000_hdx;
8079860Sgdamore@opensolaris.org 		break;
8089860Sgdamore@opensolaris.org 	case MAC_PROP_EN_100FDX_CAP:
8099860Sgdamore@opensolaris.org 		capp = &ph->phy_cap_100_fdx;
8109860Sgdamore@opensolaris.org 		advp = &ph->phy_en_100_fdx;
8119860Sgdamore@opensolaris.org 		macpp = &mh->m_en_100_fdx;
8129860Sgdamore@opensolaris.org 		break;
8139860Sgdamore@opensolaris.org 	case MAC_PROP_EN_100HDX_CAP:
8149860Sgdamore@opensolaris.org 		capp = &ph->phy_cap_100_hdx;
8159860Sgdamore@opensolaris.org 		advp = &ph->phy_en_100_hdx;
8169860Sgdamore@opensolaris.org 		macpp = &mh->m_en_100_hdx;
8179860Sgdamore@opensolaris.org 		break;
8189860Sgdamore@opensolaris.org 	case MAC_PROP_EN_100T4_CAP:
8199860Sgdamore@opensolaris.org 		capp = &ph->phy_cap_100_t4;
8209860Sgdamore@opensolaris.org 		advp = &ph->phy_en_100_t4;
8219860Sgdamore@opensolaris.org 		macpp = &mh->m_en_100_t4;
8229860Sgdamore@opensolaris.org 		break;
8239860Sgdamore@opensolaris.org 	case MAC_PROP_EN_10FDX_CAP:
8249860Sgdamore@opensolaris.org 		capp = &ph->phy_cap_10_fdx;
8259860Sgdamore@opensolaris.org 		advp = &ph->phy_en_10_fdx;
8269860Sgdamore@opensolaris.org 		macpp = &mh->m_en_10_fdx;
8279860Sgdamore@opensolaris.org 		break;
8289860Sgdamore@opensolaris.org 	case MAC_PROP_EN_10HDX_CAP:
8299860Sgdamore@opensolaris.org 		capp = &ph->phy_cap_10_hdx;
8309860Sgdamore@opensolaris.org 		advp = &ph->phy_en_10_hdx;
8319860Sgdamore@opensolaris.org 		macpp = &mh->m_en_10_hdx;
8329860Sgdamore@opensolaris.org 		break;
8339860Sgdamore@opensolaris.org 	case MAC_PROP_AUTONEG:
8349860Sgdamore@opensolaris.org 		capp = &ph->phy_cap_aneg;
8359860Sgdamore@opensolaris.org 		advp = &ph->phy_en_aneg;
8369860Sgdamore@opensolaris.org 		macpp = &mh->m_en_aneg;
8379860Sgdamore@opensolaris.org 		break;
838*11878SVenu.Iyer@Sun.COM 	case MAC_PROP_FLOWCTRL: {
839*11878SVenu.Iyer@Sun.COM 		link_flowctrl_t	fc;
840*11878SVenu.Iyer@Sun.COM 		boolean_t chg;
841*11878SVenu.Iyer@Sun.COM 
842*11878SVenu.Iyer@Sun.COM 		ASSERT(sz >= sizeof (link_flowctrl_t));
843*11878SVenu.Iyer@Sun.COM 		bcopy(valp, &fc, sizeof (fc));
8449860Sgdamore@opensolaris.org 
845*11878SVenu.Iyer@Sun.COM 		chg = fc == ph->phy_en_flowctrl ? B_FALSE : B_TRUE;
846*11878SVenu.Iyer@Sun.COM 		switch (fc) {
847*11878SVenu.Iyer@Sun.COM 		case LINK_FLOWCTRL_NONE:
848*11878SVenu.Iyer@Sun.COM 			ph->phy_en_pause = B_FALSE;
849*11878SVenu.Iyer@Sun.COM 			ph->phy_en_asmpause = B_FALSE;
850*11878SVenu.Iyer@Sun.COM 			ph->phy_en_flowctrl = fc;
851*11878SVenu.Iyer@Sun.COM 			break;
852*11878SVenu.Iyer@Sun.COM 		/*
853*11878SVenu.Iyer@Sun.COM 		 * Note that while we don't have a way to advertise
854*11878SVenu.Iyer@Sun.COM 		 * that we can RX pause (we just won't send pause
855*11878SVenu.Iyer@Sun.COM 		 * frames), we advertise full support.  The MAC driver
856*11878SVenu.Iyer@Sun.COM 		 * will learn of the configuration via the saved value
857*11878SVenu.Iyer@Sun.COM 		 * of the tunable.
858*11878SVenu.Iyer@Sun.COM 		 */
859*11878SVenu.Iyer@Sun.COM 		case LINK_FLOWCTRL_BI:
860*11878SVenu.Iyer@Sun.COM 		case LINK_FLOWCTRL_RX:
861*11878SVenu.Iyer@Sun.COM 			if (ph->phy_cap_pause) {
862*11878SVenu.Iyer@Sun.COM 				ph->phy_en_pause = B_TRUE;
863*11878SVenu.Iyer@Sun.COM 				ph->phy_en_asmpause = B_TRUE;
8649860Sgdamore@opensolaris.org 				ph->phy_en_flowctrl = fc;
865*11878SVenu.Iyer@Sun.COM 			} else {
866*11878SVenu.Iyer@Sun.COM 				rv = EINVAL;
867*11878SVenu.Iyer@Sun.COM 			}
868*11878SVenu.Iyer@Sun.COM 			break;
8699860Sgdamore@opensolaris.org 
870*11878SVenu.Iyer@Sun.COM 		/*
871*11878SVenu.Iyer@Sun.COM 		 * Tell the other side that we can assert pause, but
872*11878SVenu.Iyer@Sun.COM 		 * we cannot resend.
873*11878SVenu.Iyer@Sun.COM 		 */
874*11878SVenu.Iyer@Sun.COM 		case LINK_FLOWCTRL_TX:
875*11878SVenu.Iyer@Sun.COM 			if (ph->phy_cap_asmpause) {
876*11878SVenu.Iyer@Sun.COM 				ph->phy_en_pause = B_FALSE;
877*11878SVenu.Iyer@Sun.COM 				ph->phy_en_flowctrl = fc;
878*11878SVenu.Iyer@Sun.COM 				ph->phy_en_asmpause = B_TRUE;
879*11878SVenu.Iyer@Sun.COM 			} else {
8809860Sgdamore@opensolaris.org 				rv = EINVAL;
8819860Sgdamore@opensolaris.org 			}
882*11878SVenu.Iyer@Sun.COM 			break;
883*11878SVenu.Iyer@Sun.COM 		default:
884*11878SVenu.Iyer@Sun.COM 			rv = EINVAL;
885*11878SVenu.Iyer@Sun.COM 			break;
886*11878SVenu.Iyer@Sun.COM 		}
887*11878SVenu.Iyer@Sun.COM 		if ((rv == 0) && chg) {
888*11878SVenu.Iyer@Sun.COM 			mh->m_en_flowctrl = fc;
889*11878SVenu.Iyer@Sun.COM 			mh->m_tstate = MII_STATE_RESET;
890*11878SVenu.Iyer@Sun.COM 			cv_broadcast(&mh->m_cv);
8919860Sgdamore@opensolaris.org 		}
8929860Sgdamore@opensolaris.org 		break;
893*11878SVenu.Iyer@Sun.COM 	}
8949860Sgdamore@opensolaris.org 
8959860Sgdamore@opensolaris.org 	default:
8969860Sgdamore@opensolaris.org 		rv = ENOTSUP;
8979860Sgdamore@opensolaris.org 		break;
8989860Sgdamore@opensolaris.org 	}
8999860Sgdamore@opensolaris.org 
9009860Sgdamore@opensolaris.org 	if (capp && advp && macpp) {
9019860Sgdamore@opensolaris.org 		if (sz < sizeof (uint8_t)) {
9029860Sgdamore@opensolaris.org 			rv = EINVAL;
9039860Sgdamore@opensolaris.org 
9049860Sgdamore@opensolaris.org 		} else if (*capp) {
9059860Sgdamore@opensolaris.org 			if (*advp != *(uint8_t *)valp) {
9069860Sgdamore@opensolaris.org 				*advp = *(uint8_t *)valp;
9079860Sgdamore@opensolaris.org 				*macpp = *(uint8_t *)valp;
9089860Sgdamore@opensolaris.org 				mh->m_tstate = MII_STATE_RESET;
9099860Sgdamore@opensolaris.org 				cv_broadcast(&mh->m_cv);
9109860Sgdamore@opensolaris.org 			}
9119860Sgdamore@opensolaris.org 			rv = 0;
9129860Sgdamore@opensolaris.org 		}
9139860Sgdamore@opensolaris.org 	}
9149860Sgdamore@opensolaris.org 
9159860Sgdamore@opensolaris.org 	mutex_exit(&mh->m_lock);
9169860Sgdamore@opensolaris.org 	return (rv);
9179860Sgdamore@opensolaris.org }
9189860Sgdamore@opensolaris.org 
9199860Sgdamore@opensolaris.org int
mii_m_getstat(mii_handle_t mh,uint_t stat,uint64_t * val)9209860Sgdamore@opensolaris.org mii_m_getstat(mii_handle_t mh, uint_t stat, uint64_t *val)
9219860Sgdamore@opensolaris.org {
9229860Sgdamore@opensolaris.org 	phy_handle_t	*ph;
9239860Sgdamore@opensolaris.org 	int		rv = 0;
9249860Sgdamore@opensolaris.org 
9259860Sgdamore@opensolaris.org 	mutex_enter(&mh->m_lock);
9269860Sgdamore@opensolaris.org 
9279860Sgdamore@opensolaris.org 	ph = mh->m_phy;
9289860Sgdamore@opensolaris.org 
9299860Sgdamore@opensolaris.org 	switch (stat) {
9309860Sgdamore@opensolaris.org 	case MAC_STAT_IFSPEED:
9319860Sgdamore@opensolaris.org 		*val = ph->phy_speed * 1000000ull;
9329860Sgdamore@opensolaris.org 		break;
9339860Sgdamore@opensolaris.org 	case ETHER_STAT_LINK_DUPLEX:
9349860Sgdamore@opensolaris.org 		*val = ph->phy_duplex;
9359860Sgdamore@opensolaris.org 		break;
9369860Sgdamore@opensolaris.org 	case ETHER_STAT_LINK_AUTONEG:
9379860Sgdamore@opensolaris.org 		*val = !!(ph->phy_adv_aneg && ph->phy_lp_aneg);
9389860Sgdamore@opensolaris.org 		break;
9399860Sgdamore@opensolaris.org 	case ETHER_STAT_XCVR_ID:
9409860Sgdamore@opensolaris.org 		*val = ph->phy_id;
9419860Sgdamore@opensolaris.org 		break;
9429860Sgdamore@opensolaris.org 	case ETHER_STAT_XCVR_INUSE:
9439860Sgdamore@opensolaris.org 		*val = ph->phy_type;
9449860Sgdamore@opensolaris.org 		break;
9459860Sgdamore@opensolaris.org 	case ETHER_STAT_XCVR_ADDR:
9469860Sgdamore@opensolaris.org 		*val = ph->phy_addr;
9479860Sgdamore@opensolaris.org 		break;
9489860Sgdamore@opensolaris.org 	case ETHER_STAT_LINK_ASMPAUSE:
9499860Sgdamore@opensolaris.org 		*val = ph->phy_adv_asmpause && ph->phy_lp_asmpause &&
9509860Sgdamore@opensolaris.org 		    ph->phy_adv_pause != ph->phy_lp_pause;
9519860Sgdamore@opensolaris.org 		break;
9529860Sgdamore@opensolaris.org 	case ETHER_STAT_LINK_PAUSE:
9539860Sgdamore@opensolaris.org 		*val = (ph->phy_flowctrl == LINK_FLOWCTRL_BI) ||
9549860Sgdamore@opensolaris.org 		    (ph->phy_flowctrl == LINK_FLOWCTRL_RX);
9559860Sgdamore@opensolaris.org 		break;
9569860Sgdamore@opensolaris.org 	case ETHER_STAT_CAP_1000FDX:
9579860Sgdamore@opensolaris.org 		*val = ph->phy_cap_1000_fdx;
9589860Sgdamore@opensolaris.org 		break;
9599860Sgdamore@opensolaris.org 	case ETHER_STAT_CAP_1000HDX:
9609860Sgdamore@opensolaris.org 		*val = ph->phy_cap_1000_hdx;
9619860Sgdamore@opensolaris.org 		break;
9629860Sgdamore@opensolaris.org 	case ETHER_STAT_CAP_100FDX:
9639860Sgdamore@opensolaris.org 		*val = ph->phy_cap_100_fdx;
9649860Sgdamore@opensolaris.org 		break;
9659860Sgdamore@opensolaris.org 	case ETHER_STAT_CAP_100HDX:
9669860Sgdamore@opensolaris.org 		*val = ph->phy_cap_100_hdx;
9679860Sgdamore@opensolaris.org 		break;
9689860Sgdamore@opensolaris.org 	case ETHER_STAT_CAP_10FDX:
9699860Sgdamore@opensolaris.org 		*val = ph->phy_cap_10_fdx;
9709860Sgdamore@opensolaris.org 		break;
9719860Sgdamore@opensolaris.org 	case ETHER_STAT_CAP_10HDX:
9729860Sgdamore@opensolaris.org 		*val = ph->phy_cap_10_hdx;
9739860Sgdamore@opensolaris.org 		break;
9749860Sgdamore@opensolaris.org 	case ETHER_STAT_CAP_100T4:
9759860Sgdamore@opensolaris.org 		*val = ph->phy_cap_100_t4;
9769860Sgdamore@opensolaris.org 		break;
9779860Sgdamore@opensolaris.org 	case ETHER_STAT_CAP_AUTONEG:
9789860Sgdamore@opensolaris.org 		*val = ph->phy_cap_aneg;
9799860Sgdamore@opensolaris.org 		break;
9809860Sgdamore@opensolaris.org 	case ETHER_STAT_CAP_PAUSE:
9819860Sgdamore@opensolaris.org 		*val = ph->phy_cap_pause;
9829860Sgdamore@opensolaris.org 		break;
9839860Sgdamore@opensolaris.org 	case ETHER_STAT_CAP_ASMPAUSE:
9849860Sgdamore@opensolaris.org 		*val = ph->phy_cap_asmpause;
9859860Sgdamore@opensolaris.org 		break;
9869860Sgdamore@opensolaris.org 
9879860Sgdamore@opensolaris.org 	case ETHER_STAT_LP_CAP_1000FDX:
9889860Sgdamore@opensolaris.org 		*val = ph->phy_lp_1000_fdx;
9899860Sgdamore@opensolaris.org 		break;
9909860Sgdamore@opensolaris.org 	case ETHER_STAT_LP_CAP_1000HDX:
9919860Sgdamore@opensolaris.org 		*val = ph->phy_lp_1000_hdx;
9929860Sgdamore@opensolaris.org 		break;
9939860Sgdamore@opensolaris.org 	case ETHER_STAT_LP_CAP_100FDX:
9949860Sgdamore@opensolaris.org 		*val = ph->phy_lp_100_fdx;
9959860Sgdamore@opensolaris.org 		break;
9969860Sgdamore@opensolaris.org 	case ETHER_STAT_LP_CAP_100HDX:
9979860Sgdamore@opensolaris.org 		*val = ph->phy_lp_100_hdx;
9989860Sgdamore@opensolaris.org 		break;
9999860Sgdamore@opensolaris.org 	case ETHER_STAT_LP_CAP_10FDX:
10009860Sgdamore@opensolaris.org 		*val = ph->phy_lp_10_fdx;
10019860Sgdamore@opensolaris.org 		break;
10029860Sgdamore@opensolaris.org 	case ETHER_STAT_LP_CAP_10HDX:
10039860Sgdamore@opensolaris.org 		*val = ph->phy_lp_10_hdx;
10049860Sgdamore@opensolaris.org 		break;
10059860Sgdamore@opensolaris.org 	case ETHER_STAT_LP_CAP_100T4:
10069860Sgdamore@opensolaris.org 		*val = ph->phy_lp_100_t4;
10079860Sgdamore@opensolaris.org 		break;
10089860Sgdamore@opensolaris.org 	case ETHER_STAT_LP_CAP_AUTONEG:
10099860Sgdamore@opensolaris.org 		*val = ph->phy_lp_aneg;
10109860Sgdamore@opensolaris.org 		break;
10119860Sgdamore@opensolaris.org 	case ETHER_STAT_LP_CAP_PAUSE:
10129860Sgdamore@opensolaris.org 		*val = ph->phy_lp_pause;
10139860Sgdamore@opensolaris.org 		break;
10149860Sgdamore@opensolaris.org 	case ETHER_STAT_LP_CAP_ASMPAUSE:
10159860Sgdamore@opensolaris.org 		*val = ph->phy_lp_asmpause;
10169860Sgdamore@opensolaris.org 		break;
10179860Sgdamore@opensolaris.org 
10189860Sgdamore@opensolaris.org 	case ETHER_STAT_ADV_CAP_1000FDX:
10199860Sgdamore@opensolaris.org 		*val = ph->phy_adv_1000_fdx;
10209860Sgdamore@opensolaris.org 		break;
10219860Sgdamore@opensolaris.org 	case ETHER_STAT_ADV_CAP_1000HDX:
10229860Sgdamore@opensolaris.org 		*val = ph->phy_adv_1000_hdx;
10239860Sgdamore@opensolaris.org 		break;
10249860Sgdamore@opensolaris.org 	case ETHER_STAT_ADV_CAP_100FDX:
10259860Sgdamore@opensolaris.org 		*val = ph->phy_adv_100_fdx;
10269860Sgdamore@opensolaris.org 		break;
10279860Sgdamore@opensolaris.org 	case ETHER_STAT_ADV_CAP_100HDX:
10289860Sgdamore@opensolaris.org 		*val = ph->phy_adv_100_hdx;
10299860Sgdamore@opensolaris.org 		break;
10309860Sgdamore@opensolaris.org 	case ETHER_STAT_ADV_CAP_10FDX:
10319860Sgdamore@opensolaris.org 		*val = ph->phy_adv_10_fdx;
10329860Sgdamore@opensolaris.org 		break;
10339860Sgdamore@opensolaris.org 	case ETHER_STAT_ADV_CAP_10HDX:
10349860Sgdamore@opensolaris.org 		*val = ph->phy_adv_10_hdx;
10359860Sgdamore@opensolaris.org 		break;
10369860Sgdamore@opensolaris.org 	case ETHER_STAT_ADV_CAP_100T4:
10379860Sgdamore@opensolaris.org 		*val = ph->phy_adv_100_t4;
10389860Sgdamore@opensolaris.org 		break;
10399860Sgdamore@opensolaris.org 	case ETHER_STAT_ADV_CAP_AUTONEG:
10409860Sgdamore@opensolaris.org 		*val = ph->phy_adv_aneg;
10419860Sgdamore@opensolaris.org 		break;
10429860Sgdamore@opensolaris.org 	case ETHER_STAT_ADV_CAP_PAUSE:
10439860Sgdamore@opensolaris.org 		*val = ph->phy_adv_pause;
10449860Sgdamore@opensolaris.org 		break;
10459860Sgdamore@opensolaris.org 	case ETHER_STAT_ADV_CAP_ASMPAUSE:
10469860Sgdamore@opensolaris.org 		*val = ph->phy_adv_asmpause;
10479860Sgdamore@opensolaris.org 		break;
10489860Sgdamore@opensolaris.org 
10499860Sgdamore@opensolaris.org 	default:
10509860Sgdamore@opensolaris.org 		rv = ENOTSUP;
10519860Sgdamore@opensolaris.org 		break;
10529860Sgdamore@opensolaris.org 	}
10539860Sgdamore@opensolaris.org 	mutex_exit(&mh->m_lock);
10549860Sgdamore@opensolaris.org 
10559860Sgdamore@opensolaris.org 	return (rv);
10569860Sgdamore@opensolaris.org }
10579860Sgdamore@opensolaris.org 
10589860Sgdamore@opensolaris.org /*
10599860Sgdamore@opensolaris.org  * PHY support routines.  Private to the MII module and the vendor
10609860Sgdamore@opensolaris.org  * specific PHY implementation code.
10619860Sgdamore@opensolaris.org  */
10629860Sgdamore@opensolaris.org uint16_t
phy_read(phy_handle_t * ph,uint8_t reg)10639860Sgdamore@opensolaris.org phy_read(phy_handle_t *ph, uint8_t reg)
10649860Sgdamore@opensolaris.org {
10659860Sgdamore@opensolaris.org 	mii_handle_t	mh = ph->phy_mii;
10669860Sgdamore@opensolaris.org 
10679860Sgdamore@opensolaris.org 	return ((*mh->m_ops.mii_read)(mh->m_private, ph->phy_addr, reg));
10689860Sgdamore@opensolaris.org }
10699860Sgdamore@opensolaris.org 
10709860Sgdamore@opensolaris.org void
phy_write(phy_handle_t * ph,uint8_t reg,uint16_t val)10719860Sgdamore@opensolaris.org phy_write(phy_handle_t *ph, uint8_t reg, uint16_t val)
10729860Sgdamore@opensolaris.org {
10739860Sgdamore@opensolaris.org 	mii_handle_t	mh = ph->phy_mii;
10749860Sgdamore@opensolaris.org 
10759860Sgdamore@opensolaris.org 	(*mh->m_ops.mii_write)(mh->m_private, ph->phy_addr, reg, val);
10769860Sgdamore@opensolaris.org }
10779860Sgdamore@opensolaris.org 
10789860Sgdamore@opensolaris.org int
phy_reset(phy_handle_t * ph)10799860Sgdamore@opensolaris.org phy_reset(phy_handle_t *ph)
10809860Sgdamore@opensolaris.org {
10819860Sgdamore@opensolaris.org 	ASSERT(mutex_owned(&ph->phy_mii->m_lock));
10829860Sgdamore@opensolaris.org 
10839860Sgdamore@opensolaris.org 	/*
108410359SGarrett.Damore@Sun.COM 	 * For our device, make sure its powered up and unisolated.
10859860Sgdamore@opensolaris.org 	 */
10869860Sgdamore@opensolaris.org 	PHY_CLR(ph, MII_CONTROL,
108710359SGarrett.Damore@Sun.COM 	    MII_CONTROL_PWRDN | MII_CONTROL_ISOLATE);
10889860Sgdamore@opensolaris.org 
10899860Sgdamore@opensolaris.org 	/*
10909860Sgdamore@opensolaris.org 	 * Finally reset it.
10919860Sgdamore@opensolaris.org 	 */
10929860Sgdamore@opensolaris.org 	PHY_SET(ph, MII_CONTROL, MII_CONTROL_RESET);
10939860Sgdamore@opensolaris.org 
10949860Sgdamore@opensolaris.org 	/*
10959860Sgdamore@opensolaris.org 	 * Apparently some devices (DP83840A) like to have a little
10969860Sgdamore@opensolaris.org 	 * bit of a wait before we start accessing anything else on
10979860Sgdamore@opensolaris.org 	 * the PHY.
10989860Sgdamore@opensolaris.org 	 */
10999860Sgdamore@opensolaris.org 	drv_usecwait(500);
11009860Sgdamore@opensolaris.org 
11019860Sgdamore@opensolaris.org 	/*
11029860Sgdamore@opensolaris.org 	 * Wait for reset to complete - probably very fast, but no
11039860Sgdamore@opensolaris.org 	 * more than 0.5 sec according to spec.  It would be nice if
11049860Sgdamore@opensolaris.org 	 * we could use delay() here, but MAC drivers may call
11059860Sgdamore@opensolaris.org 	 * functions which hold this lock in interrupt context, so
11069860Sgdamore@opensolaris.org 	 * sleeping would be a definite no-no.  The good news here is
11079860Sgdamore@opensolaris.org 	 * that it seems to be the case that most devices come back
11089860Sgdamore@opensolaris.org 	 * within only a few hundred usec.
11099860Sgdamore@opensolaris.org 	 */
11109860Sgdamore@opensolaris.org 	for (int i = 500000; i; i -= 100) {
11119860Sgdamore@opensolaris.org 		if ((phy_read(ph, MII_CONTROL) & MII_CONTROL_RESET) == 0) {
11129860Sgdamore@opensolaris.org 			/* reset completed */
11139860Sgdamore@opensolaris.org 			return (DDI_SUCCESS);
11149860Sgdamore@opensolaris.org 		}
11159860Sgdamore@opensolaris.org 		drv_usecwait(100);
11169860Sgdamore@opensolaris.org 	}
11179860Sgdamore@opensolaris.org 
11189860Sgdamore@opensolaris.org 	return (DDI_FAILURE);
11199860Sgdamore@opensolaris.org }
11209860Sgdamore@opensolaris.org 
11219860Sgdamore@opensolaris.org int
phy_stop(phy_handle_t * ph)11229860Sgdamore@opensolaris.org phy_stop(phy_handle_t *ph)
11239860Sgdamore@opensolaris.org {
112410066Sgdamore@opensolaris.org 	phy_write(ph, MII_CONTROL, MII_CONTROL_ISOLATE);
112510066Sgdamore@opensolaris.org 
11269860Sgdamore@opensolaris.org 	return (DDI_SUCCESS);
11279860Sgdamore@opensolaris.org }
11289860Sgdamore@opensolaris.org 
11299860Sgdamore@opensolaris.org int
phy_loop(phy_handle_t * ph)113010359SGarrett.Damore@Sun.COM phy_loop(phy_handle_t *ph)
11319860Sgdamore@opensolaris.org {
113210359SGarrett.Damore@Sun.COM 	uint16_t	bmcr, gtcr;
113310359SGarrett.Damore@Sun.COM 
11349860Sgdamore@opensolaris.org 	ASSERT(mutex_owned(&ph->phy_mii->m_lock));
11359860Sgdamore@opensolaris.org 
11369860Sgdamore@opensolaris.org 	/*
11379860Sgdamore@opensolaris.org 	 * Disable everything to start... we'll add in modes as we go.
11389860Sgdamore@opensolaris.org 	 */
11399860Sgdamore@opensolaris.org 	ph->phy_adv_aneg = B_FALSE;
11409860Sgdamore@opensolaris.org 	ph->phy_adv_1000_fdx = B_FALSE;
11419860Sgdamore@opensolaris.org 	ph->phy_adv_1000_hdx = B_FALSE;
11429860Sgdamore@opensolaris.org 	ph->phy_adv_100_fdx = B_FALSE;
11439860Sgdamore@opensolaris.org 	ph->phy_adv_100_t4 = B_FALSE;
11449860Sgdamore@opensolaris.org 	ph->phy_adv_100_hdx = B_FALSE;
11459860Sgdamore@opensolaris.org 	ph->phy_adv_10_fdx = B_FALSE;
11469860Sgdamore@opensolaris.org 	ph->phy_adv_10_hdx = B_FALSE;
11479860Sgdamore@opensolaris.org 	ph->phy_adv_pause = B_FALSE;
11489860Sgdamore@opensolaris.org 	ph->phy_adv_asmpause = B_FALSE;
11499860Sgdamore@opensolaris.org 
115010359SGarrett.Damore@Sun.COM 	bmcr = 0;
115110359SGarrett.Damore@Sun.COM 	gtcr = MII_MSCONTROL_MANUAL | MII_MSCONTROL_MASTER;
115210359SGarrett.Damore@Sun.COM 
11539860Sgdamore@opensolaris.org 	switch (ph->phy_loopback) {
11549860Sgdamore@opensolaris.org 	case PHY_LB_NONE:
115510359SGarrett.Damore@Sun.COM 		/* We shouldn't be here */
115610359SGarrett.Damore@Sun.COM 		ASSERT(0);
11579860Sgdamore@opensolaris.org 		break;
11589860Sgdamore@opensolaris.org 
11599860Sgdamore@opensolaris.org 	case PHY_LB_INT_PHY:
116010359SGarrett.Damore@Sun.COM 		bmcr |= MII_CONTROL_LOOPBACK;
116110359SGarrett.Damore@Sun.COM 		ph->phy_duplex = LINK_DUPLEX_FULL;
11629860Sgdamore@opensolaris.org 		if (ph->phy_cap_1000_fdx) {
116310359SGarrett.Damore@Sun.COM 			bmcr |= MII_CONTROL_1GB | MII_CONTROL_FDUPLEX;
116410359SGarrett.Damore@Sun.COM 			ph->phy_speed = 1000;
11659860Sgdamore@opensolaris.org 		} else if (ph->phy_cap_100_fdx) {
116610359SGarrett.Damore@Sun.COM 			bmcr |= MII_CONTROL_100MB | MII_CONTROL_FDUPLEX;
116710359SGarrett.Damore@Sun.COM 			ph->phy_speed = 100;
11689860Sgdamore@opensolaris.org 		} else if (ph->phy_cap_10_fdx) {
116910359SGarrett.Damore@Sun.COM 			bmcr |= MII_CONTROL_FDUPLEX;
117010359SGarrett.Damore@Sun.COM 			ph->phy_speed = 10;
11719860Sgdamore@opensolaris.org 		}
11729860Sgdamore@opensolaris.org 		break;
11739860Sgdamore@opensolaris.org 
11749860Sgdamore@opensolaris.org 	case PHY_LB_EXT_10:
117510359SGarrett.Damore@Sun.COM 		bmcr = MII_CONTROL_FDUPLEX;
117610359SGarrett.Damore@Sun.COM 		ph->phy_speed = 10;
117710359SGarrett.Damore@Sun.COM 		ph->phy_duplex = LINK_DUPLEX_FULL;
11789860Sgdamore@opensolaris.org 		break;
11799860Sgdamore@opensolaris.org 
11809860Sgdamore@opensolaris.org 	case PHY_LB_EXT_100:
118110359SGarrett.Damore@Sun.COM 		bmcr = MII_CONTROL_100MB | MII_CONTROL_FDUPLEX;
118210359SGarrett.Damore@Sun.COM 		ph->phy_speed = 100;
118310359SGarrett.Damore@Sun.COM 		ph->phy_duplex = LINK_DUPLEX_FULL;
11849860Sgdamore@opensolaris.org 		break;
11859860Sgdamore@opensolaris.org 
11869860Sgdamore@opensolaris.org 	case PHY_LB_EXT_1000:
118710359SGarrett.Damore@Sun.COM 		bmcr = MII_CONTROL_1GB | MII_CONTROL_FDUPLEX;
118810359SGarrett.Damore@Sun.COM 		ph->phy_speed = 1000;
118910359SGarrett.Damore@Sun.COM 		ph->phy_duplex = LINK_DUPLEX_FULL;
119010359SGarrett.Damore@Sun.COM 		break;
119110359SGarrett.Damore@Sun.COM 	}
119210359SGarrett.Damore@Sun.COM 
119310359SGarrett.Damore@Sun.COM 	ph->phy_link = LINK_STATE_UP;	/* force up for loopback */
119410359SGarrett.Damore@Sun.COM 	ph->phy_flowctrl = LINK_FLOWCTRL_NONE;
119510359SGarrett.Damore@Sun.COM 
119610359SGarrett.Damore@Sun.COM 	switch (ph->phy_type) {
119710359SGarrett.Damore@Sun.COM 	case XCVR_1000T:
119810359SGarrett.Damore@Sun.COM 	case XCVR_1000X:
119910359SGarrett.Damore@Sun.COM 	case XCVR_100T2:
120010359SGarrett.Damore@Sun.COM 		phy_write(ph, MII_MSCONTROL, gtcr);
12019860Sgdamore@opensolaris.org 		break;
12029860Sgdamore@opensolaris.org 	}
12039860Sgdamore@opensolaris.org 
120410359SGarrett.Damore@Sun.COM 	phy_write(ph, MII_CONTROL, bmcr);
120510359SGarrett.Damore@Sun.COM 
120610359SGarrett.Damore@Sun.COM 	return (DDI_SUCCESS);
120710359SGarrett.Damore@Sun.COM }
120810359SGarrett.Damore@Sun.COM 
120910359SGarrett.Damore@Sun.COM int
phy_start(phy_handle_t * ph)121010359SGarrett.Damore@Sun.COM phy_start(phy_handle_t *ph)
121110359SGarrett.Damore@Sun.COM {
121210359SGarrett.Damore@Sun.COM 	uint16_t	bmcr, anar, gtcr;
121310359SGarrett.Damore@Sun.COM 	ASSERT(mutex_owned(&ph->phy_mii->m_lock));
121410359SGarrett.Damore@Sun.COM 
121510359SGarrett.Damore@Sun.COM 	ASSERT(ph->phy_loopback == PHY_LB_NONE);
121610359SGarrett.Damore@Sun.COM 
121710359SGarrett.Damore@Sun.COM 	/*
121810359SGarrett.Damore@Sun.COM 	 * No loopback overrides, so try to advertise everything
121910359SGarrett.Damore@Sun.COM 	 * that is administratively enabled.
122010359SGarrett.Damore@Sun.COM 	 */
122110359SGarrett.Damore@Sun.COM 	ph->phy_adv_aneg = ph->phy_en_aneg;
122210359SGarrett.Damore@Sun.COM 	ph->phy_adv_1000_fdx = ph->phy_en_1000_fdx;
122310359SGarrett.Damore@Sun.COM 	ph->phy_adv_1000_hdx = ph->phy_en_1000_hdx;
122410359SGarrett.Damore@Sun.COM 	ph->phy_adv_100_fdx = ph->phy_en_100_fdx;
122510359SGarrett.Damore@Sun.COM 	ph->phy_adv_100_t4 = ph->phy_en_100_t4;
122610359SGarrett.Damore@Sun.COM 	ph->phy_adv_100_hdx = ph->phy_en_100_hdx;
122710359SGarrett.Damore@Sun.COM 	ph->phy_adv_10_fdx = ph->phy_en_10_fdx;
122810359SGarrett.Damore@Sun.COM 	ph->phy_adv_10_hdx = ph->phy_en_10_hdx;
122910359SGarrett.Damore@Sun.COM 	ph->phy_adv_pause = ph->phy_en_pause;
123010359SGarrett.Damore@Sun.COM 	ph->phy_adv_asmpause = ph->phy_en_asmpause;
123110359SGarrett.Damore@Sun.COM 
12329860Sgdamore@opensolaris.org 	/*
12339860Sgdamore@opensolaris.org 	 * Limit properties to what the hardware can actually support.
12349860Sgdamore@opensolaris.org 	 */
12359860Sgdamore@opensolaris.org #define	FILTER_ADV(CAP)		\
12369860Sgdamore@opensolaris.org 	if (!ph->phy_cap_##CAP)	\
12379860Sgdamore@opensolaris.org 	    ph->phy_adv_##CAP = 0
12389860Sgdamore@opensolaris.org 
12399860Sgdamore@opensolaris.org 	FILTER_ADV(aneg);
12409860Sgdamore@opensolaris.org 	FILTER_ADV(1000_fdx);
12419860Sgdamore@opensolaris.org 	FILTER_ADV(1000_hdx);
12429860Sgdamore@opensolaris.org 	FILTER_ADV(100_fdx);
12439860Sgdamore@opensolaris.org 	FILTER_ADV(100_t4);
12449860Sgdamore@opensolaris.org 	FILTER_ADV(100_hdx);
12459860Sgdamore@opensolaris.org 	FILTER_ADV(10_fdx);
12469860Sgdamore@opensolaris.org 	FILTER_ADV(10_hdx);
12479860Sgdamore@opensolaris.org 	FILTER_ADV(pause);
12489860Sgdamore@opensolaris.org 	FILTER_ADV(asmpause);
12499860Sgdamore@opensolaris.org 
12509860Sgdamore@opensolaris.org #undef	FILTER_ADV
12519860Sgdamore@opensolaris.org 
12529860Sgdamore@opensolaris.org 	/*
12539860Sgdamore@opensolaris.org 	 * We need at least one valid mode.
12549860Sgdamore@opensolaris.org 	 */
12559860Sgdamore@opensolaris.org 	if ((!ph->phy_adv_1000_fdx) &&
12569860Sgdamore@opensolaris.org 	    (!ph->phy_adv_1000_hdx) &&
12579860Sgdamore@opensolaris.org 	    (!ph->phy_adv_100_t4) &&
12589860Sgdamore@opensolaris.org 	    (!ph->phy_adv_100_fdx) &&
12599860Sgdamore@opensolaris.org 	    (!ph->phy_adv_100_hdx) &&
12609860Sgdamore@opensolaris.org 	    (!ph->phy_adv_10_fdx) &&
12619860Sgdamore@opensolaris.org 	    (!ph->phy_adv_10_hdx)) {
12629860Sgdamore@opensolaris.org 
12639860Sgdamore@opensolaris.org 		phy_warn(ph,
12649860Sgdamore@opensolaris.org 		    "No valid link mode selected.  Powering down PHY.");
12659860Sgdamore@opensolaris.org 
12669860Sgdamore@opensolaris.org 		PHY_SET(ph, MII_CONTROL, MII_CONTROL_PWRDN);
12679860Sgdamore@opensolaris.org 
12689860Sgdamore@opensolaris.org 		ph->phy_link = LINK_STATE_DOWN;
12699860Sgdamore@opensolaris.org 		return (DDI_SUCCESS);
12709860Sgdamore@opensolaris.org 	}
12719860Sgdamore@opensolaris.org 
127210359SGarrett.Damore@Sun.COM 	bmcr = 0;
127310359SGarrett.Damore@Sun.COM 	gtcr = 0;
12749860Sgdamore@opensolaris.org 
12759860Sgdamore@opensolaris.org 	if (ph->phy_adv_aneg) {
12769860Sgdamore@opensolaris.org 		bmcr |= MII_CONTROL_ANE | MII_CONTROL_RSAN;
12779860Sgdamore@opensolaris.org 	}
12789860Sgdamore@opensolaris.org 
12799860Sgdamore@opensolaris.org 	if ((ph->phy_adv_1000_fdx) || (ph->phy_adv_1000_hdx)) {
12809860Sgdamore@opensolaris.org 		bmcr |= MII_CONTROL_1GB;
12819860Sgdamore@opensolaris.org 
12829860Sgdamore@opensolaris.org 	} else if (ph->phy_adv_100_fdx || ph->phy_adv_100_hdx ||
12839860Sgdamore@opensolaris.org 	    ph->phy_adv_100_t4) {
12849860Sgdamore@opensolaris.org 		bmcr |= MII_CONTROL_100MB;
12859860Sgdamore@opensolaris.org 	}
12869860Sgdamore@opensolaris.org 
12879860Sgdamore@opensolaris.org 	if (ph->phy_adv_1000_fdx || ph->phy_adv_100_fdx || ph->phy_adv_10_fdx) {
12889860Sgdamore@opensolaris.org 		bmcr |= MII_CONTROL_FDUPLEX;
12899860Sgdamore@opensolaris.org 	}
12909860Sgdamore@opensolaris.org 
12919860Sgdamore@opensolaris.org 	if (ph->phy_type == XCVR_1000X) {
12929860Sgdamore@opensolaris.org 		/* 1000BASE-X (usually fiber) */
12939860Sgdamore@opensolaris.org 		anar = 0;
12949860Sgdamore@opensolaris.org 		if (ph->phy_adv_1000_fdx) {
12959860Sgdamore@opensolaris.org 			anar |= MII_ABILITY_X_FD;
12969860Sgdamore@opensolaris.org 		}
12979860Sgdamore@opensolaris.org 		if (ph->phy_adv_1000_hdx) {
12989860Sgdamore@opensolaris.org 			anar |= MII_ABILITY_X_HD;
12999860Sgdamore@opensolaris.org 		}
13009860Sgdamore@opensolaris.org 		if (ph->phy_adv_pause) {
13019860Sgdamore@opensolaris.org 			anar |= MII_ABILITY_X_PAUSE;
13029860Sgdamore@opensolaris.org 		}
13039860Sgdamore@opensolaris.org 		if (ph->phy_adv_asmpause) {
13049860Sgdamore@opensolaris.org 			anar |= MII_ABILITY_X_ASMPAUSE;
13059860Sgdamore@opensolaris.org 		}
13069860Sgdamore@opensolaris.org 
13079860Sgdamore@opensolaris.org 	} else if (ph->phy_type == XCVR_100T2) {
13089860Sgdamore@opensolaris.org 		/* 100BASE-T2 */
13099860Sgdamore@opensolaris.org 		anar = 0;
13109860Sgdamore@opensolaris.org 		if (ph->phy_adv_100_fdx) {
13119860Sgdamore@opensolaris.org 			anar |= MII_ABILITY_T2_FD;
13129860Sgdamore@opensolaris.org 		}
13139860Sgdamore@opensolaris.org 		if (ph->phy_adv_100_hdx) {
13149860Sgdamore@opensolaris.org 			anar |= MII_ABILITY_T2_HD;
13159860Sgdamore@opensolaris.org 		}
13169860Sgdamore@opensolaris.org 
13179860Sgdamore@opensolaris.org 	} else {
13189860Sgdamore@opensolaris.org 		anar = MII_AN_SELECTOR_8023;
13199860Sgdamore@opensolaris.org 
13209860Sgdamore@opensolaris.org 		/* 1000BASE-T or 100BASE-X probably  */
13219860Sgdamore@opensolaris.org 		if (ph->phy_adv_1000_fdx) {
13229860Sgdamore@opensolaris.org 			gtcr |= MII_MSCONTROL_1000T_FD;
13239860Sgdamore@opensolaris.org 		}
13249860Sgdamore@opensolaris.org 		if (ph->phy_adv_1000_hdx) {
13259860Sgdamore@opensolaris.org 			gtcr |= MII_MSCONTROL_1000T;
13269860Sgdamore@opensolaris.org 		}
13279860Sgdamore@opensolaris.org 		if (ph->phy_adv_100_fdx) {
13289860Sgdamore@opensolaris.org 			anar |= MII_ABILITY_100BASE_TX_FD;
13299860Sgdamore@opensolaris.org 		}
13309860Sgdamore@opensolaris.org 		if (ph->phy_adv_100_hdx) {
13319860Sgdamore@opensolaris.org 			anar |= MII_ABILITY_100BASE_TX;
13329860Sgdamore@opensolaris.org 		}
13339860Sgdamore@opensolaris.org 		if (ph->phy_adv_100_t4) {
13349860Sgdamore@opensolaris.org 			anar |= MII_ABILITY_100BASE_T4;
13359860Sgdamore@opensolaris.org 		}
13369860Sgdamore@opensolaris.org 		if (ph->phy_adv_10_fdx) {
13379860Sgdamore@opensolaris.org 			anar |= MII_ABILITY_10BASE_T_FD;
13389860Sgdamore@opensolaris.org 		}
133911414Sgdamore@opensolaris.org 		if (ph->phy_adv_10_hdx) {
13409860Sgdamore@opensolaris.org 			anar |= MII_ABILITY_10BASE_T;
13419860Sgdamore@opensolaris.org 		}
13429860Sgdamore@opensolaris.org 		if (ph->phy_adv_pause) {
13439860Sgdamore@opensolaris.org 			anar |= MII_ABILITY_PAUSE;
13449860Sgdamore@opensolaris.org 		}
13459860Sgdamore@opensolaris.org 		if (ph->phy_adv_asmpause) {
13469860Sgdamore@opensolaris.org 			anar |= MII_ABILITY_ASMPAUSE;
13479860Sgdamore@opensolaris.org 		}
13489860Sgdamore@opensolaris.org 	}
13499860Sgdamore@opensolaris.org 
13509860Sgdamore@opensolaris.org 	ph->phy_link = LINK_STATE_DOWN;
13519860Sgdamore@opensolaris.org 	ph->phy_duplex = LINK_DUPLEX_UNKNOWN;
13529860Sgdamore@opensolaris.org 	ph->phy_speed = 0;
13539860Sgdamore@opensolaris.org 
13549860Sgdamore@opensolaris.org 	phy_write(ph, MII_AN_ADVERT, anar);
135510291Sgdamore@opensolaris.org 	phy_write(ph, MII_CONTROL, bmcr & ~(MII_CONTROL_RSAN));
135610291Sgdamore@opensolaris.org 
13579860Sgdamore@opensolaris.org 	switch (ph->phy_type) {
13589860Sgdamore@opensolaris.org 	case XCVR_1000T:
13599860Sgdamore@opensolaris.org 	case XCVR_1000X:
13609860Sgdamore@opensolaris.org 	case XCVR_100T2:
13619860Sgdamore@opensolaris.org 		phy_write(ph, MII_MSCONTROL, gtcr);
13629860Sgdamore@opensolaris.org 	}
13639860Sgdamore@opensolaris.org 
13649860Sgdamore@opensolaris.org 	/*
13659860Sgdamore@opensolaris.org 	 * Finally, this will start up autoneg if it is enabled, or
13669860Sgdamore@opensolaris.org 	 * force link settings otherwise.
13679860Sgdamore@opensolaris.org 	 */
13689860Sgdamore@opensolaris.org 	phy_write(ph, MII_CONTROL, bmcr);
13699860Sgdamore@opensolaris.org 
13709860Sgdamore@opensolaris.org 	return (DDI_SUCCESS);
13719860Sgdamore@opensolaris.org }
13729860Sgdamore@opensolaris.org 
13739860Sgdamore@opensolaris.org 
13749860Sgdamore@opensolaris.org int
phy_check(phy_handle_t * ph)13759860Sgdamore@opensolaris.org phy_check(phy_handle_t *ph)
13769860Sgdamore@opensolaris.org {
13779860Sgdamore@opensolaris.org 	uint16_t control, status, lpar, msstat, anexp;
13789860Sgdamore@opensolaris.org 	int debounces = 100;
13799860Sgdamore@opensolaris.org 
13809860Sgdamore@opensolaris.org 	ASSERT(mutex_owned(&ph->phy_mii->m_lock));
13819860Sgdamore@opensolaris.org 
13829860Sgdamore@opensolaris.org debounce:
13839860Sgdamore@opensolaris.org 	status = phy_read(ph, MII_STATUS);
13849860Sgdamore@opensolaris.org 	control = phy_read(ph, MII_CONTROL);
13859860Sgdamore@opensolaris.org 
13869860Sgdamore@opensolaris.org 	if (status & MII_STATUS_EXTENDED) {
13879860Sgdamore@opensolaris.org 		lpar = phy_read(ph, MII_AN_LPABLE);
13889860Sgdamore@opensolaris.org 		anexp = phy_read(ph, MII_AN_EXPANSION);
13899860Sgdamore@opensolaris.org 	} else {
13909860Sgdamore@opensolaris.org 		lpar = 0;
13919860Sgdamore@opensolaris.org 		anexp = 0;
13929860Sgdamore@opensolaris.org 	}
13939860Sgdamore@opensolaris.org 
13949860Sgdamore@opensolaris.org 	/*
13959860Sgdamore@opensolaris.org 	 * We reread to clear any latched bits.  This also debounces
13969860Sgdamore@opensolaris.org 	 * any state that might be in transition.
13979860Sgdamore@opensolaris.org 	 */
13989860Sgdamore@opensolaris.org 	drv_usecwait(10);
13999860Sgdamore@opensolaris.org 	if ((status != phy_read(ph, MII_STATUS)) && debounces) {
14009860Sgdamore@opensolaris.org 		debounces--;
14019860Sgdamore@opensolaris.org 		goto debounce;
14029860Sgdamore@opensolaris.org 	}
14039860Sgdamore@opensolaris.org 
14049860Sgdamore@opensolaris.org 	/*
14059860Sgdamore@opensolaris.org 	 * Detect the situation where the PHY is removed or has died.
14069860Sgdamore@opensolaris.org 	 * According to spec, at least one bit of status must be set,
14079860Sgdamore@opensolaris.org 	 * and at least one bit must be clear.
14089860Sgdamore@opensolaris.org 	 */
14099860Sgdamore@opensolaris.org 	if ((status == 0xffff) || (status == 0)) {
14109860Sgdamore@opensolaris.org 		ph->phy_speed = 0;
14119860Sgdamore@opensolaris.org 		ph->phy_duplex = LINK_DUPLEX_UNKNOWN;
14129860Sgdamore@opensolaris.org 		ph->phy_link = LINK_STATE_UNKNOWN;
14139860Sgdamore@opensolaris.org 		ph->phy_present = B_FALSE;
14149860Sgdamore@opensolaris.org 		return (DDI_FAILURE);
14159860Sgdamore@opensolaris.org 	}
14169860Sgdamore@opensolaris.org 
14179860Sgdamore@opensolaris.org 	/* We only respect the link flag if we are not in loopback. */
14189860Sgdamore@opensolaris.org 	if ((ph->phy_loopback != PHY_LB_INT_PHY) &&
14199860Sgdamore@opensolaris.org 	    ((status & MII_STATUS_LINKUP) == 0)) {
14209860Sgdamore@opensolaris.org 		ph->phy_speed = 0;
14219860Sgdamore@opensolaris.org 		ph->phy_duplex = LINK_DUPLEX_UNKNOWN;
14229860Sgdamore@opensolaris.org 		ph->phy_link = LINK_STATE_DOWN;
14239860Sgdamore@opensolaris.org 		return (DDI_SUCCESS);
14249860Sgdamore@opensolaris.org 	}
14259860Sgdamore@opensolaris.org 
14269860Sgdamore@opensolaris.org 	ph->phy_link = LINK_STATE_UP;
14279860Sgdamore@opensolaris.org 
14289860Sgdamore@opensolaris.org 	if ((control & MII_CONTROL_ANE) == 0) {
14299860Sgdamore@opensolaris.org 
14309860Sgdamore@opensolaris.org 		ph->phy_lp_aneg = B_FALSE;
14319860Sgdamore@opensolaris.org 		ph->phy_lp_10_hdx = B_FALSE;
14329860Sgdamore@opensolaris.org 		ph->phy_lp_10_fdx = B_FALSE;
14339860Sgdamore@opensolaris.org 		ph->phy_lp_100_t4 = B_FALSE;
14349860Sgdamore@opensolaris.org 		ph->phy_lp_100_hdx = B_FALSE;
14359860Sgdamore@opensolaris.org 		ph->phy_lp_100_fdx = B_FALSE;
14369860Sgdamore@opensolaris.org 		ph->phy_lp_1000_hdx = B_FALSE;
14379860Sgdamore@opensolaris.org 		ph->phy_lp_1000_fdx = B_FALSE;
14389860Sgdamore@opensolaris.org 
14399860Sgdamore@opensolaris.org 		/*
14409860Sgdamore@opensolaris.org 		 * We have no idea what our link partner might or might
14419860Sgdamore@opensolaris.org 		 * not be able to support, except that it appears to
14429860Sgdamore@opensolaris.org 		 * support the same mode that we have forced.
14439860Sgdamore@opensolaris.org 		 */
14449860Sgdamore@opensolaris.org 		if (control & MII_CONTROL_1GB) {
14459860Sgdamore@opensolaris.org 			ph->phy_speed = 1000;
14469860Sgdamore@opensolaris.org 		} else if (control & MII_CONTROL_100MB) {
14479860Sgdamore@opensolaris.org 			ph->phy_speed = 100;
14489860Sgdamore@opensolaris.org 		} else {
14499860Sgdamore@opensolaris.org 			ph->phy_speed = 10;
14509860Sgdamore@opensolaris.org 		}
14519860Sgdamore@opensolaris.org 		ph->phy_duplex = control & MII_CONTROL_FDUPLEX ?
14529860Sgdamore@opensolaris.org 		    LINK_DUPLEX_FULL : LINK_DUPLEX_HALF;
14539860Sgdamore@opensolaris.org 
14549860Sgdamore@opensolaris.org 		return (DDI_SUCCESS);
14559860Sgdamore@opensolaris.org 	}
14569860Sgdamore@opensolaris.org 
14579860Sgdamore@opensolaris.org 	if (ph->phy_type == XCVR_1000X) {
14589860Sgdamore@opensolaris.org 
14599860Sgdamore@opensolaris.org 		ph->phy_lp_10_hdx = B_FALSE;
14609860Sgdamore@opensolaris.org 		ph->phy_lp_10_fdx = B_FALSE;
14619860Sgdamore@opensolaris.org 		ph->phy_lp_100_t4 = B_FALSE;
14629860Sgdamore@opensolaris.org 		ph->phy_lp_100_hdx = B_FALSE;
14639860Sgdamore@opensolaris.org 		ph->phy_lp_100_fdx = B_FALSE;
14649860Sgdamore@opensolaris.org 
14659860Sgdamore@opensolaris.org 		/* 1000BASE-X requires autonegotiation */
14669860Sgdamore@opensolaris.org 		ph->phy_lp_aneg = B_TRUE;
14679860Sgdamore@opensolaris.org 		ph->phy_lp_1000_fdx = !!(lpar & MII_ABILITY_X_FD);
14689860Sgdamore@opensolaris.org 		ph->phy_lp_1000_hdx = !!(lpar & MII_ABILITY_X_HD);
14699860Sgdamore@opensolaris.org 		ph->phy_lp_pause = !!(lpar & MII_ABILITY_X_PAUSE);
14709860Sgdamore@opensolaris.org 		ph->phy_lp_asmpause = !!(lpar & MII_ABILITY_X_ASMPAUSE);
14719860Sgdamore@opensolaris.org 
14729860Sgdamore@opensolaris.org 	} else if (ph->phy_type == XCVR_100T2) {
14739860Sgdamore@opensolaris.org 		ph->phy_lp_10_hdx = B_FALSE;
14749860Sgdamore@opensolaris.org 		ph->phy_lp_10_fdx = B_FALSE;
14759860Sgdamore@opensolaris.org 		ph->phy_lp_100_t4 = B_FALSE;
14769860Sgdamore@opensolaris.org 		ph->phy_lp_1000_hdx = B_FALSE;
14779860Sgdamore@opensolaris.org 		ph->phy_lp_1000_fdx = B_FALSE;
14789860Sgdamore@opensolaris.org 		ph->phy_lp_pause = B_FALSE;
14799860Sgdamore@opensolaris.org 		ph->phy_lp_asmpause = B_FALSE;
14809860Sgdamore@opensolaris.org 
14819860Sgdamore@opensolaris.org 		/* 100BASE-T2 requires autonegotiation */
14829860Sgdamore@opensolaris.org 		ph->phy_lp_aneg = B_TRUE;
14839860Sgdamore@opensolaris.org 		ph->phy_lp_100_fdx = !!(lpar & MII_ABILITY_T2_FD);
14849860Sgdamore@opensolaris.org 		ph->phy_lp_100_hdx = !!(lpar & MII_ABILITY_T2_HD);
14859860Sgdamore@opensolaris.org 
14869860Sgdamore@opensolaris.org 	} else if (anexp & MII_AN_EXP_PARFAULT) {
14879860Sgdamore@opensolaris.org 		/*
14889860Sgdamore@opensolaris.org 		 * Parallel detection fault!  This happens when the
14899860Sgdamore@opensolaris.org 		 * peer does not use autonegotiation, and the
14909860Sgdamore@opensolaris.org 		 * detection logic reports more than one type of legal
14919860Sgdamore@opensolaris.org 		 * link is available.  Note that parallel detection
14929860Sgdamore@opensolaris.org 		 * can only happen with half duplex 10, 100, and
14939860Sgdamore@opensolaris.org 		 * 100TX4.  We also should not have got here, because
14949860Sgdamore@opensolaris.org 		 * the link state bit should have failed.
14959860Sgdamore@opensolaris.org 		 */
14969860Sgdamore@opensolaris.org #ifdef	DEBUG
14979860Sgdamore@opensolaris.org 		phy_warn(ph, "Parallel detection fault!");
14989860Sgdamore@opensolaris.org #endif
14999860Sgdamore@opensolaris.org 		ph->phy_lp_10_hdx = B_FALSE;
15009860Sgdamore@opensolaris.org 		ph->phy_lp_10_fdx = B_FALSE;
15019860Sgdamore@opensolaris.org 		ph->phy_lp_100_t4 = B_FALSE;
15029860Sgdamore@opensolaris.org 		ph->phy_lp_100_hdx = B_FALSE;
15039860Sgdamore@opensolaris.org 		ph->phy_lp_100_fdx = B_FALSE;
15049860Sgdamore@opensolaris.org 		ph->phy_lp_1000_hdx = B_FALSE;
15059860Sgdamore@opensolaris.org 		ph->phy_lp_1000_fdx = B_FALSE;
15069860Sgdamore@opensolaris.org 		ph->phy_lp_pause = B_FALSE;
15079860Sgdamore@opensolaris.org 		ph->phy_lp_asmpause = B_FALSE;
15089860Sgdamore@opensolaris.org 		ph->phy_speed = 0;
15099860Sgdamore@opensolaris.org 		ph->phy_duplex = LINK_DUPLEX_UNKNOWN;
15109860Sgdamore@opensolaris.org 		return (DDI_SUCCESS);
15119860Sgdamore@opensolaris.org 
15129860Sgdamore@opensolaris.org 	} else {
15139860Sgdamore@opensolaris.org 		ph->phy_lp_aneg = !!(anexp & MII_AN_EXP_LPCANAN);
15149860Sgdamore@opensolaris.org 
15159860Sgdamore@opensolaris.org 		/*
15169860Sgdamore@opensolaris.org 		 * Note: If the peer doesn't support autonegotiation, then
15179860Sgdamore@opensolaris.org 		 * according to clause 28.5.4.5, the link partner ability
15189860Sgdamore@opensolaris.org 		 * register will still have the right bits set.  However,
15199860Sgdamore@opensolaris.org 		 * gigabit modes cannot use legacy parallel detection.
15209860Sgdamore@opensolaris.org 		 */
15219860Sgdamore@opensolaris.org 
15229860Sgdamore@opensolaris.org 		if ((ph->phy_type == XCVR_1000T) &
15239860Sgdamore@opensolaris.org 		    (anexp & MII_AN_EXP_LPCANAN)) {
15249860Sgdamore@opensolaris.org 
15259860Sgdamore@opensolaris.org 			/* check for gige */
15269860Sgdamore@opensolaris.org 			msstat = phy_read(ph, MII_MSSTATUS);
15279860Sgdamore@opensolaris.org 
15289860Sgdamore@opensolaris.org 			ph->phy_lp_1000_hdx =
15299860Sgdamore@opensolaris.org 			    !!(msstat & MII_MSSTATUS_LP1000T);
15309860Sgdamore@opensolaris.org 
15319860Sgdamore@opensolaris.org 			ph->phy_lp_1000_fdx =
15329860Sgdamore@opensolaris.org 			    !!(msstat & MII_MSSTATUS_LP1000T_FD);
15339860Sgdamore@opensolaris.org 		}
15349860Sgdamore@opensolaris.org 
15359860Sgdamore@opensolaris.org 		ph->phy_lp_100_fdx = !!(lpar & MII_ABILITY_100BASE_TX_FD);
15369860Sgdamore@opensolaris.org 		ph->phy_lp_100_hdx = !!(lpar & MII_ABILITY_100BASE_TX);
15379860Sgdamore@opensolaris.org 		ph->phy_lp_100_t4 = !!(lpar & MII_ABILITY_100BASE_T4);
15389860Sgdamore@opensolaris.org 		ph->phy_lp_10_fdx = !!(lpar & MII_ABILITY_10BASE_T_FD);
15399860Sgdamore@opensolaris.org 		ph->phy_lp_10_hdx = !!(lpar & MII_ABILITY_10BASE_T);
15409860Sgdamore@opensolaris.org 		ph->phy_lp_pause = !!(lpar & MII_ABILITY_PAUSE);
15419860Sgdamore@opensolaris.org 		ph->phy_lp_asmpause = !!(lpar & MII_ABILITY_ASMPAUSE);
15429860Sgdamore@opensolaris.org 	}
15439860Sgdamore@opensolaris.org 
15449860Sgdamore@opensolaris.org 	/* resolve link pause */
15459860Sgdamore@opensolaris.org 	if ((ph->phy_en_flowctrl == LINK_FLOWCTRL_BI) &&
15469860Sgdamore@opensolaris.org 	    (ph->phy_lp_pause)) {
15479860Sgdamore@opensolaris.org 		ph->phy_flowctrl = LINK_FLOWCTRL_BI;
15489860Sgdamore@opensolaris.org 	} else if ((ph->phy_en_flowctrl == LINK_FLOWCTRL_RX) &&
15499860Sgdamore@opensolaris.org 	    (ph->phy_lp_pause || ph->phy_lp_asmpause)) {
15509860Sgdamore@opensolaris.org 		ph->phy_flowctrl = LINK_FLOWCTRL_RX;
15519860Sgdamore@opensolaris.org 	} else if ((ph->phy_en_flowctrl == LINK_FLOWCTRL_TX) &&
15529860Sgdamore@opensolaris.org 	    (ph->phy_lp_pause)) {
15539860Sgdamore@opensolaris.org 		ph->phy_flowctrl = LINK_FLOWCTRL_TX;
15549860Sgdamore@opensolaris.org 	} else {
15559860Sgdamore@opensolaris.org 		ph->phy_flowctrl = LINK_FLOWCTRL_NONE;
15569860Sgdamore@opensolaris.org 	}
15579860Sgdamore@opensolaris.org 
15589860Sgdamore@opensolaris.org 	if (ph->phy_adv_1000_fdx && ph->phy_lp_1000_fdx) {
15599860Sgdamore@opensolaris.org 		ph->phy_speed = 1000;
15609860Sgdamore@opensolaris.org 		ph->phy_duplex = LINK_DUPLEX_FULL;
15619860Sgdamore@opensolaris.org 
15629860Sgdamore@opensolaris.org 	} else if (ph->phy_adv_1000_hdx && ph->phy_lp_1000_hdx) {
15639860Sgdamore@opensolaris.org 		ph->phy_speed = 1000;
15649860Sgdamore@opensolaris.org 		ph->phy_duplex = LINK_DUPLEX_HALF;
15659860Sgdamore@opensolaris.org 
15669860Sgdamore@opensolaris.org 	} else if (ph->phy_adv_100_fdx && ph->phy_lp_100_fdx) {
15679860Sgdamore@opensolaris.org 		ph->phy_speed = 100;
15689860Sgdamore@opensolaris.org 		ph->phy_duplex = LINK_DUPLEX_FULL;
15699860Sgdamore@opensolaris.org 
15709860Sgdamore@opensolaris.org 	} else if (ph->phy_adv_100_t4 && ph->phy_lp_100_t4) {
15719860Sgdamore@opensolaris.org 		ph->phy_speed = 100;
15729860Sgdamore@opensolaris.org 		ph->phy_duplex = LINK_DUPLEX_HALF;
15739860Sgdamore@opensolaris.org 
15749860Sgdamore@opensolaris.org 	} else if (ph->phy_adv_100_hdx && ph->phy_lp_100_hdx) {
15759860Sgdamore@opensolaris.org 		ph->phy_speed = 100;
15769860Sgdamore@opensolaris.org 		ph->phy_duplex = LINK_DUPLEX_HALF;
15779860Sgdamore@opensolaris.org 
15789860Sgdamore@opensolaris.org 	} else if (ph->phy_adv_10_fdx && ph->phy_lp_10_fdx) {
15799860Sgdamore@opensolaris.org 		ph->phy_speed = 10;
15809860Sgdamore@opensolaris.org 		ph->phy_duplex = LINK_DUPLEX_FULL;
15819860Sgdamore@opensolaris.org 
15829860Sgdamore@opensolaris.org 	} else if (ph->phy_adv_10_hdx && ph->phy_lp_10_hdx) {
15839860Sgdamore@opensolaris.org 		ph->phy_speed = 10;
15849860Sgdamore@opensolaris.org 		ph->phy_duplex = LINK_DUPLEX_HALF;
15859860Sgdamore@opensolaris.org 
15869860Sgdamore@opensolaris.org 	} else {
15879860Sgdamore@opensolaris.org #ifdef	DEBUG
15889860Sgdamore@opensolaris.org 		phy_warn(ph, "No common abilities.");
15899860Sgdamore@opensolaris.org #endif
15909860Sgdamore@opensolaris.org 		ph->phy_speed = 0;
15919860Sgdamore@opensolaris.org 		ph->phy_duplex = LINK_DUPLEX_UNKNOWN;
15929860Sgdamore@opensolaris.org 	}
15939860Sgdamore@opensolaris.org 
15949860Sgdamore@opensolaris.org 	return (DDI_SUCCESS);
15959860Sgdamore@opensolaris.org }
15969860Sgdamore@opensolaris.org 
15979860Sgdamore@opensolaris.org int
phy_get_prop(phy_handle_t * ph,char * prop,int dflt)15989860Sgdamore@opensolaris.org phy_get_prop(phy_handle_t *ph, char *prop, int dflt)
15999860Sgdamore@opensolaris.org {
16009860Sgdamore@opensolaris.org 	mii_handle_t	mh = ph->phy_mii;
16019860Sgdamore@opensolaris.org 
16029860Sgdamore@opensolaris.org 	return (ddi_prop_get_int(DDI_DEV_T_ANY, mh->m_dip, 0, prop, dflt));
16039860Sgdamore@opensolaris.org }
16049860Sgdamore@opensolaris.org 
16059860Sgdamore@opensolaris.org const char *
phy_get_name(phy_handle_t * ph)16069860Sgdamore@opensolaris.org phy_get_name(phy_handle_t *ph)
16079860Sgdamore@opensolaris.org {
16089860Sgdamore@opensolaris.org 	mii_handle_t	mh = ph->phy_mii;
16099860Sgdamore@opensolaris.org 
16109860Sgdamore@opensolaris.org 	return (mh->m_name);
16119860Sgdamore@opensolaris.org }
16129860Sgdamore@opensolaris.org 
16139860Sgdamore@opensolaris.org const char *
phy_get_driver(phy_handle_t * ph)16149860Sgdamore@opensolaris.org phy_get_driver(phy_handle_t *ph)
16159860Sgdamore@opensolaris.org {
16169860Sgdamore@opensolaris.org 	mii_handle_t	mh = ph->phy_mii;
16179860Sgdamore@opensolaris.org 
16189860Sgdamore@opensolaris.org 	return (ddi_driver_name(mh->m_dip));
16199860Sgdamore@opensolaris.org }
16209860Sgdamore@opensolaris.org 
16219860Sgdamore@opensolaris.org void
phy_warn(phy_handle_t * ph,const char * fmt,...)16229860Sgdamore@opensolaris.org phy_warn(phy_handle_t *ph, const char *fmt, ...)
16239860Sgdamore@opensolaris.org {
16249860Sgdamore@opensolaris.org 	va_list	va;
16259860Sgdamore@opensolaris.org 	char buf[256];
16269860Sgdamore@opensolaris.org 
16279860Sgdamore@opensolaris.org 	(void) snprintf(buf, sizeof (buf), "%s: %s", phy_get_name(ph), fmt);
16289860Sgdamore@opensolaris.org 
16299860Sgdamore@opensolaris.org 	va_start(va, fmt);
16309860Sgdamore@opensolaris.org 	vcmn_err(CE_WARN, buf, va);
16319860Sgdamore@opensolaris.org 	va_end(va);
16329860Sgdamore@opensolaris.org }
16339860Sgdamore@opensolaris.org 
16349860Sgdamore@opensolaris.org /*
16359860Sgdamore@opensolaris.org  * Internal support routines.
16369860Sgdamore@opensolaris.org  */
16379860Sgdamore@opensolaris.org 
16389860Sgdamore@opensolaris.org void
_mii_notify(mii_handle_t mh)163910359SGarrett.Damore@Sun.COM _mii_notify(mii_handle_t mh)
164010359SGarrett.Damore@Sun.COM {
164110359SGarrett.Damore@Sun.COM 	if (mh->m_ops.mii_notify != NULL) {
164210359SGarrett.Damore@Sun.COM 		mh->m_ops.mii_notify(mh->m_private, mh->m_link);
164310359SGarrett.Damore@Sun.COM 	}
164410359SGarrett.Damore@Sun.COM }
164510359SGarrett.Damore@Sun.COM 
164610359SGarrett.Damore@Sun.COM void
_mii_probe_phy(phy_handle_t * ph)16479860Sgdamore@opensolaris.org _mii_probe_phy(phy_handle_t *ph)
16489860Sgdamore@opensolaris.org {
16499860Sgdamore@opensolaris.org 	uint16_t	bmsr;
16509860Sgdamore@opensolaris.org 	uint16_t	extsr;
16519860Sgdamore@opensolaris.org 	mii_handle_t	mh = ph->phy_mii;
16529860Sgdamore@opensolaris.org 
16539860Sgdamore@opensolaris.org 
16549860Sgdamore@opensolaris.org 	/*
16559860Sgdamore@opensolaris.org 	 * Apparently, PHY 0 is less likely to be physically
16569860Sgdamore@opensolaris.org 	 * connected, and should always be the last one tried.  Most
16579860Sgdamore@opensolaris.org 	 * single solution NICs use PHY1 for their built-in
16589860Sgdamore@opensolaris.org 	 * transceiver.  NICs with an external MII will often place
16599860Sgdamore@opensolaris.org 	 * the external PHY at address 1, and use address 0 for the
16609860Sgdamore@opensolaris.org 	 * internal PHY.
16619860Sgdamore@opensolaris.org 	 */
16629860Sgdamore@opensolaris.org 
16639860Sgdamore@opensolaris.org 	ph->phy_id = 0;
16649860Sgdamore@opensolaris.org 	ph->phy_model = "PHY";
16659860Sgdamore@opensolaris.org 	ph->phy_vendor = "Unknown Vendor";
16669860Sgdamore@opensolaris.org 
16679860Sgdamore@opensolaris.org 	/* done twice to clear any latched bits */
16689860Sgdamore@opensolaris.org 	bmsr = phy_read(ph, MII_STATUS);
16699860Sgdamore@opensolaris.org 	bmsr = phy_read(ph, MII_STATUS);
16709860Sgdamore@opensolaris.org 	if ((bmsr == 0) || (bmsr == 0xffff)) {
16719860Sgdamore@opensolaris.org 		ph->phy_present = B_FALSE;
16729860Sgdamore@opensolaris.org 		return;
16739860Sgdamore@opensolaris.org 	}
16749860Sgdamore@opensolaris.org 
16759860Sgdamore@opensolaris.org 	if (bmsr & MII_STATUS_EXTSTAT) {
16769860Sgdamore@opensolaris.org 		extsr = phy_read(ph, MII_EXTSTATUS);
16779860Sgdamore@opensolaris.org 	} else {
16789860Sgdamore@opensolaris.org 		extsr = 0;
16799860Sgdamore@opensolaris.org 	}
16809860Sgdamore@opensolaris.org 
16819860Sgdamore@opensolaris.org 	ph->phy_present = B_TRUE;
16829860Sgdamore@opensolaris.org 	ph->phy_id = ((uint32_t)phy_read(ph, MII_PHYIDH) << 16) |
16839860Sgdamore@opensolaris.org 	    phy_read(ph, MII_PHYIDL);
16849860Sgdamore@opensolaris.org 
16859860Sgdamore@opensolaris.org 	/* setup default handlers */
16869860Sgdamore@opensolaris.org 	ph->phy_reset = phy_reset;
16879860Sgdamore@opensolaris.org 	ph->phy_start = phy_start;
16889860Sgdamore@opensolaris.org 	ph->phy_stop = phy_stop;
16899860Sgdamore@opensolaris.org 	ph->phy_check = phy_check;
169010359SGarrett.Damore@Sun.COM 	ph->phy_loop = phy_loop;
16919860Sgdamore@opensolaris.org 
16929860Sgdamore@opensolaris.org 	/*
16939860Sgdamore@opensolaris.org 	 * We ignore the non-existent 100baseT2 stuff -- no
16949860Sgdamore@opensolaris.org 	 * known products for it exist.
16959860Sgdamore@opensolaris.org 	 */
16969860Sgdamore@opensolaris.org 	ph->phy_cap_aneg =	!!(bmsr & MII_STATUS_CANAUTONEG);
16979860Sgdamore@opensolaris.org 	ph->phy_cap_100_t4 =	!!(bmsr & MII_STATUS_100_BASE_T4);
16989860Sgdamore@opensolaris.org 	ph->phy_cap_100_fdx =	!!(bmsr & MII_STATUS_100_BASEX_FD);
16999860Sgdamore@opensolaris.org 	ph->phy_cap_100_hdx =	!!(bmsr & MII_STATUS_100_BASEX);
17009860Sgdamore@opensolaris.org 	ph->phy_cap_10_fdx =	!!(bmsr & MII_STATUS_10_FD);
17019860Sgdamore@opensolaris.org 	ph->phy_cap_10_hdx =	!!(bmsr & MII_STATUS_10);
17029860Sgdamore@opensolaris.org 	ph->phy_cap_1000_fdx =
17039860Sgdamore@opensolaris.org 	    !!(extsr & (MII_EXTSTATUS_1000X_FD|MII_EXTSTATUS_1000T_FD));
17049860Sgdamore@opensolaris.org 	ph->phy_cap_1000_hdx =
17059860Sgdamore@opensolaris.org 	    !!(extsr & (MII_EXTSTATUS_1000X | MII_EXTSTATUS_1000T));
17069860Sgdamore@opensolaris.org 	ph->phy_cap_pause =	mh->m_cap_pause;
17079860Sgdamore@opensolaris.org 	ph->phy_cap_asmpause =	mh->m_cap_asmpause;
17089860Sgdamore@opensolaris.org 
17099860Sgdamore@opensolaris.org 	if (bmsr & MII_STATUS_10) {
17109860Sgdamore@opensolaris.org 		ph->phy_cap_10_hdx = B_TRUE;
17119860Sgdamore@opensolaris.org 		ph->phy_type = XCVR_10;
17129860Sgdamore@opensolaris.org 	}
17139860Sgdamore@opensolaris.org 	if (bmsr & MII_STATUS_10_FD) {
17149860Sgdamore@opensolaris.org 		ph->phy_cap_10_fdx = B_TRUE;
17159860Sgdamore@opensolaris.org 		ph->phy_type = XCVR_10;
17169860Sgdamore@opensolaris.org 	}
17179860Sgdamore@opensolaris.org 	if (bmsr & MII_STATUS_100T2) {
17189860Sgdamore@opensolaris.org 		ph->phy_cap_100_hdx = B_TRUE;
17199860Sgdamore@opensolaris.org 		ph->phy_type = XCVR_100T2;
17209860Sgdamore@opensolaris.org 	}
17219860Sgdamore@opensolaris.org 	if (bmsr & MII_STATUS_100T2_FD) {
17229860Sgdamore@opensolaris.org 		ph->phy_cap_100_fdx = B_TRUE;
17239860Sgdamore@opensolaris.org 		ph->phy_type = XCVR_100T2;
17249860Sgdamore@opensolaris.org 	}
17259860Sgdamore@opensolaris.org 	if (bmsr & MII_STATUS_100_BASE_T4) {
17269860Sgdamore@opensolaris.org 		ph->phy_cap_100_hdx = B_TRUE;
17279860Sgdamore@opensolaris.org 		ph->phy_type = XCVR_100T4;
17289860Sgdamore@opensolaris.org 	}
17299860Sgdamore@opensolaris.org 	if (bmsr & MII_STATUS_100_BASEX) {
17309860Sgdamore@opensolaris.org 		ph->phy_cap_100_hdx = B_TRUE;
17319860Sgdamore@opensolaris.org 		ph->phy_type = XCVR_100X;
17329860Sgdamore@opensolaris.org 	}
17339860Sgdamore@opensolaris.org 	if (bmsr & MII_STATUS_100_BASEX_FD) {
17349860Sgdamore@opensolaris.org 		ph->phy_cap_100_fdx = B_TRUE;
17359860Sgdamore@opensolaris.org 		ph->phy_type = XCVR_100X;
17369860Sgdamore@opensolaris.org 	}
17379860Sgdamore@opensolaris.org 	if (extsr & MII_EXTSTATUS_1000X) {
17389860Sgdamore@opensolaris.org 		ph->phy_cap_1000_hdx = B_TRUE;
17399860Sgdamore@opensolaris.org 		ph->phy_type = XCVR_1000X;
17409860Sgdamore@opensolaris.org 	}
17419860Sgdamore@opensolaris.org 	if (extsr & MII_EXTSTATUS_1000X_FD) {
17429860Sgdamore@opensolaris.org 		ph->phy_cap_1000_fdx = B_TRUE;
17439860Sgdamore@opensolaris.org 		ph->phy_type = XCVR_1000X;
17449860Sgdamore@opensolaris.org 	}
17459860Sgdamore@opensolaris.org 	if (extsr & MII_EXTSTATUS_1000T) {
17469860Sgdamore@opensolaris.org 		ph->phy_cap_1000_hdx = B_TRUE;
17479860Sgdamore@opensolaris.org 		ph->phy_type = XCVR_1000T;
17489860Sgdamore@opensolaris.org 	}
17499860Sgdamore@opensolaris.org 	if (extsr & MII_EXTSTATUS_1000T_FD) {
17509860Sgdamore@opensolaris.org 		ph->phy_cap_1000_fdx = B_TRUE;
17519860Sgdamore@opensolaris.org 		ph->phy_type = XCVR_1000T;
17529860Sgdamore@opensolaris.org 	}
17539860Sgdamore@opensolaris.org 
17549860Sgdamore@opensolaris.org 	for (int j = 0; _phy_probes[j] != NULL; j++) {
17559860Sgdamore@opensolaris.org 		if ((*_phy_probes[j])(ph)) {
17569860Sgdamore@opensolaris.org 			break;
17579860Sgdamore@opensolaris.org 		}
17589860Sgdamore@opensolaris.org 	}
17599860Sgdamore@opensolaris.org 
17609860Sgdamore@opensolaris.org #define	INIT_ENABLE(CAP)	\
17619860Sgdamore@opensolaris.org 	ph->phy_en_##CAP = (mh->m_en_##CAP > 0) ? \
17629860Sgdamore@opensolaris.org 	    mh->m_en_##CAP : ph->phy_cap_##CAP
17639860Sgdamore@opensolaris.org 
17649860Sgdamore@opensolaris.org 	INIT_ENABLE(aneg);
17659860Sgdamore@opensolaris.org 	INIT_ENABLE(1000_fdx);
17669860Sgdamore@opensolaris.org 	INIT_ENABLE(1000_hdx);
17679860Sgdamore@opensolaris.org 	INIT_ENABLE(100_fdx);
17689860Sgdamore@opensolaris.org 	INIT_ENABLE(100_t4);
17699860Sgdamore@opensolaris.org 	INIT_ENABLE(100_hdx);
17709860Sgdamore@opensolaris.org 	INIT_ENABLE(10_fdx);
17719860Sgdamore@opensolaris.org 	INIT_ENABLE(10_hdx);
17729860Sgdamore@opensolaris.org 
17739860Sgdamore@opensolaris.org #undef	INIT_ENABLE
17749860Sgdamore@opensolaris.org 	ph->phy_en_flowctrl = mh->m_en_flowctrl;
17759860Sgdamore@opensolaris.org 	switch (ph->phy_en_flowctrl) {
17769860Sgdamore@opensolaris.org 	case LINK_FLOWCTRL_BI:
17779860Sgdamore@opensolaris.org 	case LINK_FLOWCTRL_RX:
17789860Sgdamore@opensolaris.org 		ph->phy_en_pause = B_TRUE;
17799860Sgdamore@opensolaris.org 		ph->phy_en_asmpause = B_TRUE;
17809860Sgdamore@opensolaris.org 		break;
17819860Sgdamore@opensolaris.org 	case LINK_FLOWCTRL_TX:
17829860Sgdamore@opensolaris.org 		ph->phy_en_pause = B_FALSE;
17839860Sgdamore@opensolaris.org 		ph->phy_en_asmpause = B_TRUE;
17849860Sgdamore@opensolaris.org 		break;
17859860Sgdamore@opensolaris.org 	default:
17869860Sgdamore@opensolaris.org 		ph->phy_en_pause = B_FALSE;
17879860Sgdamore@opensolaris.org 		ph->phy_en_asmpause = B_FALSE;
17889860Sgdamore@opensolaris.org 		break;
17899860Sgdamore@opensolaris.org 	}
17909860Sgdamore@opensolaris.org }
17919860Sgdamore@opensolaris.org 
17929860Sgdamore@opensolaris.org void
_mii_probe(mii_handle_t mh)179310359SGarrett.Damore@Sun.COM _mii_probe(mii_handle_t mh)
17949860Sgdamore@opensolaris.org {
17959860Sgdamore@opensolaris.org 	uint8_t		new_addr;
17969860Sgdamore@opensolaris.org 	uint8_t		old_addr;
17979860Sgdamore@opensolaris.org 	uint8_t		user_addr;
17989860Sgdamore@opensolaris.org 	uint8_t		curr_addr;
17999860Sgdamore@opensolaris.org 	phy_handle_t	*ph;
18009860Sgdamore@opensolaris.org 	int		pri = 0;
180111419Sgdamore@opensolaris.org 	int		first;
18029860Sgdamore@opensolaris.org 
18039860Sgdamore@opensolaris.org 	user_addr = ddi_prop_get_int(DDI_DEV_T_ANY, mh->m_dip, 0,
18049860Sgdamore@opensolaris.org 	    "phy-addr", -1);
18059860Sgdamore@opensolaris.org 	old_addr = mh->m_addr;
18069860Sgdamore@opensolaris.org 	new_addr = 0xff;
18079860Sgdamore@opensolaris.org 
18089860Sgdamore@opensolaris.org 	/*
18099860Sgdamore@opensolaris.org 	 * Apparently, PHY 0 is less likely to be physically
18109860Sgdamore@opensolaris.org 	 * connected, and should always be the last one tried.  Most
18119860Sgdamore@opensolaris.org 	 * single solution NICs use PHY1 for their built-in
18129860Sgdamore@opensolaris.org 	 * transceiver.  NICs with an external MII will often place
18139860Sgdamore@opensolaris.org 	 * the external PHY at address 1, and use address 0 for the
18149860Sgdamore@opensolaris.org 	 * internal PHY.
181511419Sgdamore@opensolaris.org 	 *
181611419Sgdamore@opensolaris.org 	 * Some devices have a different preference however.  They can
181711419Sgdamore@opensolaris.org 	 * override the default starting point of the search by
181811419Sgdamore@opensolaris.org 	 * exporting a "first-phy" property.
18199860Sgdamore@opensolaris.org 	 */
18209860Sgdamore@opensolaris.org 
182111419Sgdamore@opensolaris.org 	first = ddi_prop_get_int(DDI_DEV_T_ANY, mh->m_dip, 0, "first-phy", 1);
182211419Sgdamore@opensolaris.org 	if ((first < 0) || (first > 31)) {
182311419Sgdamore@opensolaris.org 		first = 1;
182411419Sgdamore@opensolaris.org 	}
182511419Sgdamore@opensolaris.org 	for (int i = first; i < (first + 32); i++) {
18269860Sgdamore@opensolaris.org 
18279860Sgdamore@opensolaris.org 		/*
182811419Sgdamore@opensolaris.org 		 * This is tricky: it lets us start searching at an
182911419Sgdamore@opensolaris.org 		 * arbitrary address instead of 0, dealing with the
183011419Sgdamore@opensolaris.org 		 * wrap-around at address 31 properly.
18319860Sgdamore@opensolaris.org 		 */
18329860Sgdamore@opensolaris.org 		curr_addr = i % 32;
18339860Sgdamore@opensolaris.org 
18349860Sgdamore@opensolaris.org 		ph = &mh->m_phys[curr_addr];
18359860Sgdamore@opensolaris.org 
18369860Sgdamore@opensolaris.org 		bzero(ph, sizeof (*ph));
18379860Sgdamore@opensolaris.org 		ph->phy_addr = curr_addr;
18389860Sgdamore@opensolaris.org 		ph->phy_mii = mh;
18399860Sgdamore@opensolaris.org 
18409860Sgdamore@opensolaris.org 		_mii_probe_phy(ph);
18419860Sgdamore@opensolaris.org 
18429860Sgdamore@opensolaris.org 		if (!ph->phy_present)
18439860Sgdamore@opensolaris.org 			continue;
18449860Sgdamore@opensolaris.org 
18459860Sgdamore@opensolaris.org 		if (curr_addr == user_addr) {
18469860Sgdamore@opensolaris.org 			/*
18479860Sgdamore@opensolaris.org 			 * We always try to honor the user configured phy.
18489860Sgdamore@opensolaris.org 			 */
18499860Sgdamore@opensolaris.org 			new_addr = curr_addr;
18509860Sgdamore@opensolaris.org 			pri = 4;
18519860Sgdamore@opensolaris.org 
18529860Sgdamore@opensolaris.org 		}
18539860Sgdamore@opensolaris.org 
18549860Sgdamore@opensolaris.org 		/* two reads to clear latched bits */
18559860Sgdamore@opensolaris.org 		if ((phy_read(ph, MII_STATUS) & MII_STATUS_LINKUP) &&
18569860Sgdamore@opensolaris.org 		    (phy_read(ph, MII_STATUS) & MII_STATUS_LINKUP) &&
18579860Sgdamore@opensolaris.org 		    (pri < 3)) {
18589860Sgdamore@opensolaris.org 			/*
18599860Sgdamore@opensolaris.org 			 * Link present is good.  We prefer this over
18609860Sgdamore@opensolaris.org 			 * a possibly disconnected link.
18619860Sgdamore@opensolaris.org 			 */
18629860Sgdamore@opensolaris.org 			new_addr = curr_addr;
18639860Sgdamore@opensolaris.org 			pri = 3;
18649860Sgdamore@opensolaris.org 		}
18659860Sgdamore@opensolaris.org 		if ((curr_addr == old_addr) && (pri < 2)) {
18669860Sgdamore@opensolaris.org 			/*
18679860Sgdamore@opensolaris.org 			 * All else being equal, minimize change.
18689860Sgdamore@opensolaris.org 			 */
18699860Sgdamore@opensolaris.org 			new_addr = curr_addr;
18709860Sgdamore@opensolaris.org 			pri = 2;
18719860Sgdamore@opensolaris.org 
18729860Sgdamore@opensolaris.org 		}
18739860Sgdamore@opensolaris.org 		if (pri < 1) {
18749860Sgdamore@opensolaris.org 			/*
18759860Sgdamore@opensolaris.org 			 * But make sure we at least select a present PHY.
18769860Sgdamore@opensolaris.org 			 */
18779860Sgdamore@opensolaris.org 			new_addr = curr_addr;
18789860Sgdamore@opensolaris.org 			pri = 1;
18799860Sgdamore@opensolaris.org 		}
18809860Sgdamore@opensolaris.org 	}
18819860Sgdamore@opensolaris.org 
18829860Sgdamore@opensolaris.org 	if (new_addr == 0xff) {
18839860Sgdamore@opensolaris.org 		mh->m_addr = -1;
18849860Sgdamore@opensolaris.org 		mh->m_phy = &mh->m_bogus_phy;
18859860Sgdamore@opensolaris.org 		_mii_error(mh, MII_ENOPHY);
18869860Sgdamore@opensolaris.org 	} else {
18879860Sgdamore@opensolaris.org 		mh->m_addr = new_addr;
18889860Sgdamore@opensolaris.org 		mh->m_phy = &mh->m_phys[new_addr];
18899860Sgdamore@opensolaris.org 		mh->m_tstate = MII_STATE_RESET;
189010359SGarrett.Damore@Sun.COM 		if (new_addr != old_addr) {
189110359SGarrett.Damore@Sun.COM 			cmn_err(CE_CONT,
189210359SGarrett.Damore@Sun.COM 			    "?%s: Using %s Ethernet PHY at %d: %s %s\n",
189310359SGarrett.Damore@Sun.COM 			    mh->m_name, mii_xcvr_types[mh->m_phy->phy_type],
189410359SGarrett.Damore@Sun.COM 			    mh->m_addr, mh->m_phy->phy_vendor,
189510359SGarrett.Damore@Sun.COM 			    mh->m_phy->phy_model);
189610479SGarrett.Damore@Sun.COM 			mh->m_link = LINK_STATE_UNKNOWN;
189710359SGarrett.Damore@Sun.COM 		}
18989860Sgdamore@opensolaris.org 	}
18999860Sgdamore@opensolaris.org }
19009860Sgdamore@opensolaris.org 
190110359SGarrett.Damore@Sun.COM int
_mii_reset(mii_handle_t mh)190210359SGarrett.Damore@Sun.COM _mii_reset(mii_handle_t mh)
19039860Sgdamore@opensolaris.org {
19049860Sgdamore@opensolaris.org 	phy_handle_t	*ph;
190510359SGarrett.Damore@Sun.COM 	boolean_t	notify;
19069860Sgdamore@opensolaris.org 
19079860Sgdamore@opensolaris.org 	ASSERT(mutex_owned(&mh->m_lock));
19089860Sgdamore@opensolaris.org 
19099860Sgdamore@opensolaris.org 	/*
19109860Sgdamore@opensolaris.org 	 * Reset logic.  We want to isolate all the other
19119860Sgdamore@opensolaris.org 	 * phys that are not in use.
19129860Sgdamore@opensolaris.org 	 */
19139860Sgdamore@opensolaris.org 	for (int i = 0; i < 32; i++) {
19149860Sgdamore@opensolaris.org 		ph = &mh->m_phys[i];
19159860Sgdamore@opensolaris.org 
19169860Sgdamore@opensolaris.org 		if (!ph->phy_present)
19179860Sgdamore@opensolaris.org 			continue;
19189860Sgdamore@opensolaris.org 
191910359SGarrett.Damore@Sun.COM 		/* Don't touch our own phy, yet. */
19209860Sgdamore@opensolaris.org 		if (ph == mh->m_phy)
19219860Sgdamore@opensolaris.org 			continue;
19229860Sgdamore@opensolaris.org 
19239860Sgdamore@opensolaris.org 		ph->phy_stop(ph);
19249860Sgdamore@opensolaris.org 	}
19259860Sgdamore@opensolaris.org 
19269860Sgdamore@opensolaris.org 	ph = mh->m_phy;
19279860Sgdamore@opensolaris.org 
19289860Sgdamore@opensolaris.org 	ASSERT(ph->phy_present);
19299860Sgdamore@opensolaris.org 
193010359SGarrett.Damore@Sun.COM 	/* If we're resetting the PHY, then we want to notify loss of link */
193110479SGarrett.Damore@Sun.COM 	notify = (mh->m_link != LINK_STATE_DOWN);
193210359SGarrett.Damore@Sun.COM 	mh->m_link = LINK_STATE_DOWN;
193310359SGarrett.Damore@Sun.COM 	ph->phy_link = LINK_STATE_DOWN;
193410359SGarrett.Damore@Sun.COM 	ph->phy_speed = 0;
193510359SGarrett.Damore@Sun.COM 	ph->phy_duplex = LINK_DUPLEX_UNKNOWN;
19369860Sgdamore@opensolaris.org 
19379860Sgdamore@opensolaris.org 	if (ph->phy_reset(ph) != DDI_SUCCESS) {
19389860Sgdamore@opensolaris.org 		_mii_error(mh, MII_ERESET);
19399860Sgdamore@opensolaris.org 		return (DDI_FAILURE);
19409860Sgdamore@opensolaris.org 	}
19419860Sgdamore@opensolaris.org 
194210359SGarrett.Damore@Sun.COM 	/* Perform optional mac layer reset. */
194310359SGarrett.Damore@Sun.COM 	if (mh->m_ops.mii_reset != NULL) {
194410359SGarrett.Damore@Sun.COM 		mh->m_ops.mii_reset(mh->m_private);
194510359SGarrett.Damore@Sun.COM 	}
194610359SGarrett.Damore@Sun.COM 
194710359SGarrett.Damore@Sun.COM 	/* Perform optional mac layer notification. */
194810359SGarrett.Damore@Sun.COM 	if (notify) {
194910359SGarrett.Damore@Sun.COM 		_mii_notify(mh);
195010359SGarrett.Damore@Sun.COM 	}
19519860Sgdamore@opensolaris.org 	return (DDI_SUCCESS);
19529860Sgdamore@opensolaris.org }
19539860Sgdamore@opensolaris.org 
195410359SGarrett.Damore@Sun.COM int
_mii_loopback(mii_handle_t mh)195510359SGarrett.Damore@Sun.COM _mii_loopback(mii_handle_t mh)
195610359SGarrett.Damore@Sun.COM {
195710359SGarrett.Damore@Sun.COM 	phy_handle_t	*ph;
195810359SGarrett.Damore@Sun.COM 
195910359SGarrett.Damore@Sun.COM 	ASSERT(mutex_owned(&mh->m_lock));
196010359SGarrett.Damore@Sun.COM 
196110359SGarrett.Damore@Sun.COM 	ph = mh->m_phy;
196210359SGarrett.Damore@Sun.COM 
196310359SGarrett.Damore@Sun.COM 	if (_mii_reset(mh) != DDI_SUCCESS) {
196410359SGarrett.Damore@Sun.COM 		return (DDI_FAILURE);
196510359SGarrett.Damore@Sun.COM 	}
196610359SGarrett.Damore@Sun.COM 	if (ph->phy_loopback == PHY_LB_NONE) {
196710359SGarrett.Damore@Sun.COM 		mh->m_tstate = MII_STATE_START;
196810359SGarrett.Damore@Sun.COM 		return (DDI_SUCCESS);
196910359SGarrett.Damore@Sun.COM 	}
197010359SGarrett.Damore@Sun.COM 	if (ph->phy_loop(ph) != DDI_SUCCESS) {
197110359SGarrett.Damore@Sun.COM 		_mii_error(mh, MII_ELOOP);
197210359SGarrett.Damore@Sun.COM 		return (DDI_FAILURE);
197310359SGarrett.Damore@Sun.COM 	}
197410359SGarrett.Damore@Sun.COM 
197510359SGarrett.Damore@Sun.COM 	/* Just force loopback to link up. */
197610359SGarrett.Damore@Sun.COM 	mh->m_link = ph->phy_link = LINK_STATE_UP;
197710359SGarrett.Damore@Sun.COM 	_mii_notify(mh);
197810359SGarrett.Damore@Sun.COM 
197910359SGarrett.Damore@Sun.COM 	return (DDI_SUCCESS);
198010359SGarrett.Damore@Sun.COM }
198110359SGarrett.Damore@Sun.COM 
198210359SGarrett.Damore@Sun.COM int
_mii_start(mii_handle_t mh)198310359SGarrett.Damore@Sun.COM _mii_start(mii_handle_t mh)
19849860Sgdamore@opensolaris.org {
19859860Sgdamore@opensolaris.org 	phy_handle_t		*ph;
19869860Sgdamore@opensolaris.org 
19879860Sgdamore@opensolaris.org 	ph = mh->m_phy;
19889860Sgdamore@opensolaris.org 
19899860Sgdamore@opensolaris.org 	ASSERT(mutex_owned(&mh->m_lock));
19909860Sgdamore@opensolaris.org 	ASSERT(ph->phy_present);
199110359SGarrett.Damore@Sun.COM 	ASSERT(ph->phy_loopback == PHY_LB_NONE);
19929860Sgdamore@opensolaris.org 
199310359SGarrett.Damore@Sun.COM 	if (ph->phy_start(ph) != DDI_SUCCESS) {
19949860Sgdamore@opensolaris.org 		_mii_error(mh, MII_ESTART);
199510359SGarrett.Damore@Sun.COM 		return (DDI_FAILURE);
19969860Sgdamore@opensolaris.org 	}
19979860Sgdamore@opensolaris.org 	/* clear the error state since we got a good startup! */
19989860Sgdamore@opensolaris.org 	mh->m_error = MII_EOK;
199910359SGarrett.Damore@Sun.COM 	return (DDI_SUCCESS);
20009860Sgdamore@opensolaris.org }
20019860Sgdamore@opensolaris.org 
200210359SGarrett.Damore@Sun.COM int
_mii_check(mii_handle_t mh)200310359SGarrett.Damore@Sun.COM _mii_check(mii_handle_t mh)
20049860Sgdamore@opensolaris.org {
20059860Sgdamore@opensolaris.org 	link_state_t	olink;
20069860Sgdamore@opensolaris.org 	int		ospeed;
20079860Sgdamore@opensolaris.org 	link_duplex_t	oduplex;
20089860Sgdamore@opensolaris.org 	link_flowctrl_t	ofctrl;
20099860Sgdamore@opensolaris.org 	phy_handle_t	*ph;
20109860Sgdamore@opensolaris.org 
20119860Sgdamore@opensolaris.org 	ph = mh->m_phy;
20129860Sgdamore@opensolaris.org 
20139860Sgdamore@opensolaris.org 	olink = mh->m_link;
20149860Sgdamore@opensolaris.org 	ospeed = ph->phy_speed;
20159860Sgdamore@opensolaris.org 	oduplex = ph->phy_duplex;
20169860Sgdamore@opensolaris.org 	ofctrl = ph->phy_flowctrl;
20179860Sgdamore@opensolaris.org 
20189860Sgdamore@opensolaris.org 	ASSERT(ph->phy_present);
20199860Sgdamore@opensolaris.org 
20209860Sgdamore@opensolaris.org 	if (ph->phy_check(ph) == DDI_FAILURE) {
20219860Sgdamore@opensolaris.org 		_mii_error(mh, MII_ECHECK);
20229860Sgdamore@opensolaris.org 		mh->m_link = LINK_STATE_UNKNOWN;
202310359SGarrett.Damore@Sun.COM 		_mii_notify(mh);
20249860Sgdamore@opensolaris.org 		return (DDI_FAILURE);
20259860Sgdamore@opensolaris.org 	}
20269860Sgdamore@opensolaris.org 
20279860Sgdamore@opensolaris.org 	mh->m_link = ph->phy_link;
20289860Sgdamore@opensolaris.org 
20299860Sgdamore@opensolaris.org 	/* if anything changed, notify! */
20309860Sgdamore@opensolaris.org 	if ((mh->m_link != olink) ||
20319860Sgdamore@opensolaris.org 	    (ph->phy_speed != ospeed) ||
20329860Sgdamore@opensolaris.org 	    (ph->phy_duplex != oduplex) ||
20339860Sgdamore@opensolaris.org 	    (ph->phy_flowctrl != ofctrl)) {
203410359SGarrett.Damore@Sun.COM 		_mii_notify(mh);
20359860Sgdamore@opensolaris.org 	}
20369860Sgdamore@opensolaris.org 
20379860Sgdamore@opensolaris.org 	return (DDI_SUCCESS);
20389860Sgdamore@opensolaris.org }
20399860Sgdamore@opensolaris.org 
204010359SGarrett.Damore@Sun.COM void
_mii_task(void * _mh)20419860Sgdamore@opensolaris.org _mii_task(void *_mh)
20429860Sgdamore@opensolaris.org {
20439860Sgdamore@opensolaris.org 	mii_handle_t	mh = _mh;
20449860Sgdamore@opensolaris.org 	phy_handle_t	*ph;
20459860Sgdamore@opensolaris.org 	clock_t		wait;
20469860Sgdamore@opensolaris.org 	clock_t		downtime;
20479860Sgdamore@opensolaris.org 
20489860Sgdamore@opensolaris.org 	mutex_enter(&mh->m_lock);
20499860Sgdamore@opensolaris.org 
20509860Sgdamore@opensolaris.org 	for (;;) {
20519860Sgdamore@opensolaris.org 
20529860Sgdamore@opensolaris.org 		/* If detaching, exit the thread. */
20539860Sgdamore@opensolaris.org 		if (!mh->m_started) {
20549860Sgdamore@opensolaris.org 			break;
20559860Sgdamore@opensolaris.org 		}
20569860Sgdamore@opensolaris.org 
20579860Sgdamore@opensolaris.org 		ph = mh->m_phy;
20589860Sgdamore@opensolaris.org 
20599860Sgdamore@opensolaris.org 		/*
20609860Sgdamore@opensolaris.org 		 * If we're suspended or otherwise not supposed to be
20619860Sgdamore@opensolaris.org 		 * monitoring the link, just go back to sleep.
20629860Sgdamore@opensolaris.org 		 *
20639860Sgdamore@opensolaris.org 		 * Theoretically we could power down the PHY, but we
20649860Sgdamore@opensolaris.org 		 * don't bother.  (The link might be used for
20659860Sgdamore@opensolaris.org 		 * wake-on-lan!)  Another option would be to reduce
20669860Sgdamore@opensolaris.org 		 * power on the PHY if both it and the link partner
20679860Sgdamore@opensolaris.org 		 * support 10 Mbps mode.
20689860Sgdamore@opensolaris.org 		 */
20699860Sgdamore@opensolaris.org 		if (mh->m_suspending) {
20709860Sgdamore@opensolaris.org 			mh->m_suspended = B_TRUE;
20719860Sgdamore@opensolaris.org 			cv_broadcast(&mh->m_cv);
20729860Sgdamore@opensolaris.org 		}
20739860Sgdamore@opensolaris.org 		if (mh->m_suspended) {
20749860Sgdamore@opensolaris.org 			mh->m_suspending = B_FALSE;
20759860Sgdamore@opensolaris.org 			cv_wait(&mh->m_cv, &mh->m_lock);
20769860Sgdamore@opensolaris.org 			continue;
20779860Sgdamore@opensolaris.org 		}
20789860Sgdamore@opensolaris.org 
20799860Sgdamore@opensolaris.org 		switch (mh->m_tstate) {
20809860Sgdamore@opensolaris.org 		case MII_STATE_PROBE:
208110359SGarrett.Damore@Sun.COM 			_mii_probe(mh);
20829860Sgdamore@opensolaris.org 			ph = mh->m_phy;
20839860Sgdamore@opensolaris.org 			if (!ph->phy_present) {
20849860Sgdamore@opensolaris.org 				/*
20859860Sgdamore@opensolaris.org 				 * If no PHY is found, wait a bit before
20869860Sgdamore@opensolaris.org 				 * trying the probe again.  10 seconds ought
20879860Sgdamore@opensolaris.org 				 * to be enough.
20889860Sgdamore@opensolaris.org 				 */
20899860Sgdamore@opensolaris.org 				wait = 10 * MII_SECOND;
20909860Sgdamore@opensolaris.org 			} else {
20919860Sgdamore@opensolaris.org 				wait = 0;
20929860Sgdamore@opensolaris.org 			}
20939860Sgdamore@opensolaris.org 			break;
20949860Sgdamore@opensolaris.org 
20959860Sgdamore@opensolaris.org 		case MII_STATE_RESET:
209610359SGarrett.Damore@Sun.COM 			if (_mii_reset(mh) == DDI_SUCCESS) {
209710359SGarrett.Damore@Sun.COM 				mh->m_tstate = MII_STATE_START;
20989860Sgdamore@opensolaris.org 				wait = 0;
20999860Sgdamore@opensolaris.org 			} else {
21009860Sgdamore@opensolaris.org 				/*
21019860Sgdamore@opensolaris.org 				 * If an error occurred, wait a bit and
21029860Sgdamore@opensolaris.org 				 * try again later.
21039860Sgdamore@opensolaris.org 				 */
21049860Sgdamore@opensolaris.org 				wait = 10 * MII_SECOND;
21059860Sgdamore@opensolaris.org 			}
21069860Sgdamore@opensolaris.org 			break;
21079860Sgdamore@opensolaris.org 
21089860Sgdamore@opensolaris.org 		case MII_STATE_START:
21099860Sgdamore@opensolaris.org 			/*
21109860Sgdamore@opensolaris.org 			 * If an error occurs, we're going to go back to
21119860Sgdamore@opensolaris.org 			 * probe or reset state.  Otherwise we go to run
21129860Sgdamore@opensolaris.org 			 * state.  In all cases we want to wait 1 second
21139860Sgdamore@opensolaris.org 			 * before doing anything else - either for link to
21149860Sgdamore@opensolaris.org 			 * settle, or to give other code a chance to run
21159860Sgdamore@opensolaris.org 			 * while we reset.
21169860Sgdamore@opensolaris.org 			 */
211710359SGarrett.Damore@Sun.COM 			if (_mii_start(mh) == DDI_SUCCESS) {
211810359SGarrett.Damore@Sun.COM 				/* reset watchdog to latest */
211910359SGarrett.Damore@Sun.COM 				downtime = ddi_get_lbolt();
212010359SGarrett.Damore@Sun.COM 				mh->m_tstate = MII_STATE_RUN;
212110359SGarrett.Damore@Sun.COM 			} else {
212210359SGarrett.Damore@Sun.COM 				mh->m_tstate = MII_STATE_PROBE;
212310359SGarrett.Damore@Sun.COM 			}
212410359SGarrett.Damore@Sun.COM 			wait = 0;
212510359SGarrett.Damore@Sun.COM 			break;
212610359SGarrett.Damore@Sun.COM 
212710359SGarrett.Damore@Sun.COM 		case MII_STATE_LOOPBACK:
212810359SGarrett.Damore@Sun.COM 			/*
212910359SGarrett.Damore@Sun.COM 			 * In loopback mode we don't check anything,
213010359SGarrett.Damore@Sun.COM 			 * and just wait for some condition to change.
213110359SGarrett.Damore@Sun.COM 			 */
213210359SGarrett.Damore@Sun.COM 			wait = (clock_t)-1;
21339860Sgdamore@opensolaris.org 			break;
21349860Sgdamore@opensolaris.org 
21359860Sgdamore@opensolaris.org 		case MII_STATE_RUN:
21369860Sgdamore@opensolaris.org 		default:
213710359SGarrett.Damore@Sun.COM 			if (_mii_check(mh) == DDI_FAILURE) {
21389860Sgdamore@opensolaris.org 				/*
21399860Sgdamore@opensolaris.org 				 * On error (PHY removed?), wait a
21409860Sgdamore@opensolaris.org 				 * short bit before reprobing or
21419860Sgdamore@opensolaris.org 				 * resetting.
21429860Sgdamore@opensolaris.org 				 */
21439860Sgdamore@opensolaris.org 				wait = MII_SECOND;
214410359SGarrett.Damore@Sun.COM 				mh->m_tstate = MII_STATE_PROBE;
21459860Sgdamore@opensolaris.org 
21469860Sgdamore@opensolaris.org 			} else if (mh->m_link == LINK_STATE_UP) {
21479860Sgdamore@opensolaris.org 				/* got goood link, so reset the watchdog */
21489860Sgdamore@opensolaris.org 				downtime = ddi_get_lbolt();
21499860Sgdamore@opensolaris.org 				/* rescan again in a second */
21509860Sgdamore@opensolaris.org 				wait = MII_SECOND;
21519860Sgdamore@opensolaris.org 
21529860Sgdamore@opensolaris.org 			} else if ((ddi_get_lbolt() - downtime) >
21539860Sgdamore@opensolaris.org 			    (drv_usectohz(MII_SECOND * 10))) {
21549860Sgdamore@opensolaris.org 
21559860Sgdamore@opensolaris.org 				/*
21569860Sgdamore@opensolaris.org 				 * If we were down for 10 seconds,
21579860Sgdamore@opensolaris.org 				 * hard reset the PHY.
21589860Sgdamore@opensolaris.org 				 */
21599860Sgdamore@opensolaris.org 				mh->m_tstate = MII_STATE_RESET;
21609860Sgdamore@opensolaris.org 				wait = 0;
21619860Sgdamore@opensolaris.org 
21629860Sgdamore@opensolaris.org 			} else {
21639860Sgdamore@opensolaris.org 				/*
21649860Sgdamore@opensolaris.org 				 * Otherwise, if we are still down,
21659860Sgdamore@opensolaris.org 				 * rescan the link much more
21669860Sgdamore@opensolaris.org 				 * frequently.  We might be trying to
21679860Sgdamore@opensolaris.org 				 * autonegotiate.
21689860Sgdamore@opensolaris.org 				 */
21699860Sgdamore@opensolaris.org 				wait = MII_SECOND / 4;
21709860Sgdamore@opensolaris.org 			}
21719860Sgdamore@opensolaris.org 			break;
21729860Sgdamore@opensolaris.org 		}
21739860Sgdamore@opensolaris.org 
217410359SGarrett.Damore@Sun.COM 		switch (wait) {
217510359SGarrett.Damore@Sun.COM 		case 0:
217610359SGarrett.Damore@Sun.COM 			break;
21779860Sgdamore@opensolaris.org 
217810359SGarrett.Damore@Sun.COM 		case (clock_t)-1:
217910359SGarrett.Damore@Sun.COM 			cv_wait(&mh->m_cv, &mh->m_lock);
218010359SGarrett.Damore@Sun.COM 			break;
21819860Sgdamore@opensolaris.org 
218210359SGarrett.Damore@Sun.COM 		default:
218311066Srafael.vanoni@sun.com 			(void) cv_reltimedwait(&mh->m_cv, &mh->m_lock,
218411066Srafael.vanoni@sun.com 			    drv_usectohz(wait), TR_CLOCK_TICK);
218510359SGarrett.Damore@Sun.COM 		}
21869860Sgdamore@opensolaris.org 	}
21879860Sgdamore@opensolaris.org 
21889860Sgdamore@opensolaris.org 	mutex_exit(&mh->m_lock);
21899860Sgdamore@opensolaris.org }
2190