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