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