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