xref: /illumos-gate/usr/src/uts/intel/io/dnet/dnet_mii.c (revision 2bbdd445a21f9d61f4a0ca0faf05d5ceb2bd91f3)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * mii - MII/PHY support for MAC drivers
28  *
29  * Utility module to provide a consistent interface to a MAC driver accross
30  * different implementations of PHY devices
31  */
32 
33 #include <sys/types.h>
34 #include <sys/debug.h>
35 #include <sys/errno.h>
36 #include <sys/param.h>
37 #include <sys/sysmacros.h>
38 #include <sys/stropts.h>
39 #include <sys/stream.h>
40 #include <sys/kmem.h>
41 #include <sys/conf.h>
42 #include <sys/ddi.h>
43 #include <sys/sunddi.h>
44 #include <sys/devops.h>
45 #include <sys/modctl.h>
46 #include <sys/cmn_err.h>
47 #include <sys/miiregs.h>
48 #include "dnet_mii.h"
49 
50 
51 #ifdef DEBUG
52 #define	MIIDEBUG
53 int miidebug = 0;
54 #define	MIITRACE 1
55 #define	MIIDUMP 2
56 #define	MIIPROBE 4
57 #define	MIICOMPAT 8
58 #endif
59 
60 /* Local functions */
61 static struct phydata *mii_get_valid_phydata(mii_handle_t mac, int phy);
62 static void mii_portmon(mii_handle_t mac);
63 
64 /* Vendor specific callback function prototypes */
65 static void dump_NS83840(mii_handle_t, int);
66 static void dump_ICS1890(struct mii_info *, int);
67 static int getspeed_NS83840(mii_handle_t, int, int *, int *);
68 static int getspeed_82553(mii_handle_t, int, int *, int *);
69 static int getspeed_ICS1890(mii_handle_t, int, int *, int *);
70 static int getspeed_generic(mii_handle_t, int, int *, int *);
71 static void postreset_ICS1890(mii_handle_t mac, int phy);
72 static void postreset_NS83840(mii_handle_t mac, int phy);
73 
74 /*
75  * MII Interface functions
76  */
77 
78 /*
79  * Register an instance of an MII interface user
80  */
81 
82 int
83 mii_create(dev_info_t *dip,		/* Passed to read/write functions */
84 	    mii_writefunc_t writefunc, 	/* How to write to a MII register */
85 	    mii_readfunc_t readfunc,	/* How to read from a MII regster */
86 	    mii_handle_t *macp)
87 {
88 	mii_handle_t mac;
89 
90 	/*  Allocate space for the mii structure */
91 	if ((mac = (mii_handle_t)
92 	    kmem_zalloc(sizeof (struct mii_info), KM_NOSLEEP)) == NULL)
93 		return (MII_NOMEM);
94 
95 	mac->mii_write = writefunc;
96 	mac->mii_read = readfunc;
97 	mac->mii_dip = dip;
98 	*macp = mac;
99 	return (MII_SUCCESS);
100 }
101 
102 /*
103  * Returns true if PHY at address phy is accessible. This should be
104  * considered the only function that takes a PHY address that can be called
105  * before mii_init_phy. There should be at least one bit set in the status
106  * register, and at least one clear
107  */
108 int
109 mii_probe_phy(mii_handle_t mac, int phy)
110 {
111 	ushort_t status;
112 	dev_info_t *dip;
113 
114 	if (!mac || phy < 0 || phy > 31)
115 		return (MII_PARAM);
116 
117 	dip = mac->mii_dip;
118 
119 	/* Clear any latched bits by reading twice */
120 	mac->mii_read(dip, phy, MII_STATUS);
121 	status = mac->mii_read(dip, phy, MII_STATUS);
122 
123 #ifdef MIIDEBUG
124 	mac->mii_read(dip, phy, MII_CONTROL);
125 	if (miidebug & MIIPROBE)
126 		cmn_err(CE_NOTE, "PHY Probe: Control=%x, Status=%x",
127 		    mac->mii_read(dip, phy, MII_CONTROL), status);
128 #endif
129 	/*
130 	 * At least one bit in status should be clear (one of the error
131 	 * bits), and there must be at least one bit set for the device
132 	 * capabilities. Unconnected devices tend to show 0xffff, but 0x0000
133 	 * has been seen.
134 	 */
135 
136 	if (status == 0xffff || status == 0x0000)
137 		return (MII_PHYNOTPRESENT);
138 	return (MII_SUCCESS);
139 }
140 
141 /*
142  * Initialise PHY, and store info about it in the handle for future
143  * reference when the MAC calls us. PHY Vendor-specific code here isolates
144  * the LAN driver from worrying about different PHY implementations
145  */
146 
147 int
148 mii_init_phy(mii_handle_t mac, int phy)
149 {
150 	ushort_t status;
151 	void *dip;
152 	struct phydata *phydata;
153 
154 	if ((mac == (mii_handle_t)NULL) || phy < 0 || phy > 31)
155 		return (MII_PARAM);
156 
157 	dip = mac->mii_dip;
158 
159 	/* Create a phydata structure for this new phy */
160 	if (mac->phys[phy])
161 		return (MII_PHYPRESENT);
162 
163 	mac->phys[phy] = phydata = (struct phydata *)
164 	    kmem_zalloc(sizeof (struct phydata), KM_NOSLEEP);
165 
166 	if (!phydata)
167 		return (MII_NOMEM);
168 
169 	phydata->id = (ulong_t)mac->mii_read(dip, phy, MII_PHYIDH) << 16;
170 	phydata->id |= (ulong_t)mac->mii_read(dip, phy, MII_PHYIDL);
171 	phydata->state = phy_state_unknown;
172 
173 	/* Override speed and duplex mode from conf-file if present */
174 	phydata->fix_duplex =
175 	    ddi_getprop(DDI_DEV_T_NONE,
176 	    mac->mii_dip, DDI_PROP_DONTPASS, "full-duplex", 0);
177 
178 	phydata->fix_speed =
179 	    ddi_getprop(DDI_DEV_T_NONE,
180 	    mac->mii_dip, DDI_PROP_DONTPASS, "speed", 0);
181 
182 	status = mac->mii_read(dip, phy, MII_STATUS);
183 
184 	/*
185 	 * when explicitly setting speed or duplex, we must
186 	 * disable autonegotiation
187 	 */
188 	if (!(status & MII_STATUS_CANAUTONEG) ||
189 	    phydata->fix_speed || phydata->fix_duplex) {
190 		/*
191 		 * If local side cannot autonegotiate, we can't try to enable
192 		 * full duplex without the user's consent, because we cannot
193 		 * tell without AN if the partner can support it
194 		 */
195 		if ((status & (MII_STATUS_100_BASEX | MII_STATUS_100_BASEX_FD |
196 		    MII_STATUS_100_BASE_T4)) && phydata->fix_speed == 0) {
197 			phydata->fix_speed = 100;
198 		} else if ((status & (MII_STATUS_10 | MII_STATUS_10_FD)) &&
199 		    phydata->fix_speed == 0) {
200 			phydata->fix_speed = 10;
201 		} else if (phydata->fix_speed == 0) {
202 			/* A very stupid PHY would not be supported */
203 			kmem_free(mac->phys[phy], sizeof (struct phydata));
204 			mac->phys[phy] = NULL;
205 			return (MII_NOTSUPPORTED);
206 		}
207 		/* mii_sync will sort out the speed selection on the PHY */
208 	} else
209 		phydata->control = MII_CONTROL_ANE;
210 
211 	switch (MII_PHY_MFG(phydata->id)) {
212 	case OUI_NATIONAL_SEMICONDUCTOR:
213 		switch (MII_PHY_MODEL(phydata->id)) {
214 		case NS_DP83840:
215 			phydata->phy_postreset = postreset_NS83840;
216 			phydata->phy_dump = dump_NS83840;
217 			phydata->description =
218 			    "National Semiconductor DP-83840";
219 			phydata->phy_getspeed = getspeed_NS83840;
220 			break;
221 		default:
222 			phydata->description = "Unknown NS";
223 			break;
224 		}
225 		break;
226 
227 	case OUI_INTEL:
228 		switch (MII_PHY_MODEL(phydata->id)) {
229 		case INTEL_82553_CSTEP:
230 			phydata->description = "Intel 82553 C-step";
231 			phydata->phy_getspeed = getspeed_82553;
232 			break;
233 		case INTEL_82555:
234 			phydata->description = "Intel 82555";
235 			phydata->phy_getspeed = getspeed_82553;
236 			break;
237 		case INTEL_82562_EH:
238 			phydata->description = "Intel 82562 EH";
239 			phydata->phy_getspeed = getspeed_82553;
240 			break;
241 		case INTEL_82562_ET:
242 			phydata->description = "Intel 82562 ET";
243 			phydata->phy_getspeed = getspeed_82553;
244 			break;
245 		case INTEL_82562_EM:
246 			phydata->description = "Intel 82562 EM";
247 			phydata->phy_getspeed = getspeed_82553;
248 			break;
249 		default:
250 			phydata->description = "Unknown INTEL";
251 			break;
252 		}
253 		break;
254 
255 	case OUI_ICS:
256 		switch (MII_PHY_MODEL(phydata->id)) {
257 		case ICS_1890:
258 		case ICS_1889:
259 			phydata->phy_postreset = postreset_ICS1890;
260 			phydata->description = "ICS 1890/1889 PHY";
261 			phydata->phy_getspeed = getspeed_ICS1890;
262 			phydata->phy_dump = dump_ICS1890;
263 			break;
264 		default:
265 			phydata->description = "ICS Unknown PHY";
266 			break;
267 		}
268 		break;
269 
270 	default: /* Non-standard PHYs, that encode weird IDs */
271 		phydata->description = "Unknown PHY";
272 		phydata->phy_dump = NULL;
273 		phydata->phy_getspeed = getspeed_generic;
274 		break;
275 	}
276 
277 	/* Do all post-reset hacks and user settings */
278 	(void) mii_sync(mac, phy);
279 
280 	if (ddi_getprop(DDI_DEV_T_NONE, mac->mii_dip, DDI_PROP_DONTPASS,
281 	    "dump-phy", 0))
282 		(void) mii_dump_phy(mac, phy);
283 
284 	return (MII_SUCCESS);
285 }
286 
287 /*
288  * Cause a reset on a PHY
289  */
290 
291 int
292 mii_reset_phy(mii_handle_t mac, int phy, enum mii_wait_type wait)
293 {
294 	int i;
295 	struct phydata *phyd;
296 	ushort_t control;
297 	if (!(phyd = mii_get_valid_phydata(mac, phy)))
298 		return (MII_PARAM);
299 
300 	/* Strobe the reset bit in the control register */
301 	mac->mii_write(mac->mii_dip, phy, MII_CONTROL,
302 	    phyd->control | MII_CONTROL_RESET);
303 
304 	phyd->state = phy_state_unknown;
305 
306 	/*
307 	 * This is likely to be very fast (ie, by the time we read the
308 	 * control register once, the devices we have seen can have already
309 	 * reset), but according to 802.3u 22.2.4.1.1, it could be up to .5 sec.
310 	 */
311 	if (wait == mii_wait_interrupt || wait == mii_wait_user) {
312 		for (i = 100; i--; ) {
313 			control = mac->mii_read(mac->mii_dip, phy, MII_CONTROL);
314 			if (!(control & MII_CONTROL_RESET))
315 				break;
316 			drv_usecwait(10);
317 		}
318 		if (i)
319 			goto reset_completed;
320 	}
321 
322 	if (wait == mii_wait_user) {
323 		for (i = 50; i--; ) {
324 			control = mac->mii_read(mac->mii_dip, phy, MII_CONTROL);
325 			if (!(control & MII_CONTROL_RESET))
326 				break;
327 			delay(drv_usectohz(10000));
328 		}
329 		if (i)
330 			goto reset_completed;
331 		return (MII_HARDFAIL);	/* It MUST reset within this time */
332 
333 	}
334 	return (MII_TIMEOUT);
335 
336 reset_completed:
337 	(void) mii_sync(mac, phy);
338 	return (MII_SUCCESS);
339 }
340 
341 /*
342  * This routine is called to synchronise the software and the PHY. It should
343  * be called after the PHY is reset, and after initialising the PHY. This
344  * routine is external because devices (DNET) can reset the PHY in ways beyond
345  * the control of the mii interface. Should this happen, the driver is
346  * required to call mii_sync().
347  * If the PHY is resetting still when this is called, it will do nothing,
348  * but, it will be retriggered when the portmon timer expires.
349  */
350 
351 int
352 mii_sync(mii_handle_t mac, int phy)
353 {
354 	struct phydata *phyd = mac->phys[phy];
355 	int len, i, numprop;
356 	struct regprop {
357 		int reg;
358 		int value;
359 	} *regprop;
360 
361 #ifdef MIIDEBUG
362 	if (miidebug & MIITRACE)
363 		cmn_err(CE_NOTE, "mii_sync (phy addr %d)", phy);
364 #endif
365 
366 	len = 0;
367 	/*
368 	 * Conf file can specify a sequence of values to write to
369 	 * the PHY registers if required
370 	 */
371 	if (ddi_getlongprop(DDI_DEV_T_ANY, mac->mii_dip,
372 	    DDI_PROP_DONTPASS, "phy-registers", (caddr_t)&regprop,
373 	    &len) == DDI_PROP_SUCCESS) {
374 		numprop = len / sizeof (struct regprop);
375 		for (i = 0; i < numprop; i++) {
376 			mac->mii_write(mac->mii_dip, phy,
377 			    regprop[i].reg, regprop[i].value);
378 #ifdef MIIDEBUG
379 			if (miidebug & MIITRACE)
380 				cmn_err(CE_NOTE, "PHY Write reg %d=%x",
381 				    regprop[i].reg, regprop[i].value);
382 #endif
383 		}
384 		kmem_free(regprop, len);
385 	} else {
386 		mac->mii_write(mac->mii_dip, phy, MII_CONTROL, phyd->control);
387 		if (phyd->phy_postreset)
388 			phyd->phy_postreset(mac, phy);
389 		if (phyd->fix_speed || phyd->fix_duplex) {
390 			/* XXX function return value ignored */
391 			(void) mii_fixspeed(mac, phy, phyd->fix_speed,
392 			    phyd->fix_duplex);
393 		}
394 	}
395 	return (MII_SUCCESS);
396 }
397 
398 /*
399  * Disable full-duplex negotiation on the PHY. This is useful if the
400  * driver or link-partner is advertising full duplex, but does not support
401  * it properly (as some previous solaris drivers didn't)
402  */
403 
404 int
405 mii_disable_fullduplex(mii_handle_t mac, int phy)
406 {
407 	void *dip = mac->mii_dip;
408 	ushort_t expansion,  miiadvert;
409 	/* dont advertise full duplex capabilites */
410 	const int fullduplex = MII_ABILITY_10BASE_T_FD
411 	    | MII_ABILITY_100BASE_TX_FD;
412 
413 	if (!(mac->mii_read(dip, phy, MII_STATUS) & MII_STATUS_CANAUTONEG)) {
414 		/*
415 		 * Local side cannot autonegotiate, so full duplex should
416 		 * never be negotiated. Consider it as a success
417 		 */
418 		return (MII_SUCCESS);
419 	}
420 
421 	/* Change what we advertise if it includes full duplex */
422 
423 	miiadvert = mac->mii_read(dip, phy, MII_AN_ADVERT);
424 	if (miiadvert & fullduplex)
425 		mac->mii_write(dip, phy, MII_AN_ADVERT,
426 		    miiadvert & ~fullduplex);
427 
428 	/* See what other end is able to do.  */
429 
430 	expansion = mac->mii_read(dip, phy, MII_AN_EXPANSION);
431 
432 	/*
433 	 * Renegotiate if the link partner supports autonegotiation
434 	 * If it doesn't, we will never have auto-negotiated full duplex
435 	 * anyway
436 	 */
437 
438 	if (expansion & MII_AN_EXP_LPCANAN)
439 		return (mii_rsan(mac, phy, mii_wait_none));
440 	else
441 		return (MII_SUCCESS);
442 }
443 
444 /*
445  * (re)enable autonegotiation on a PHY.
446  */
447 
448 int
449 mii_autoneg_enab(mii_handle_t mac, int phy)
450 {
451 	struct phydata *phyd;
452 	if (!(phyd = mii_get_valid_phydata(mac, phy)))
453 		return (MII_PARAM);
454 	phyd->control |= MII_CONTROL_ANE;
455 	mac->mii_write(mac->mii_dip, phy, MII_CONTROL, phyd->control);
456 	return (MII_SUCCESS);
457 }
458 
459 /*
460  * Check the link status of a PHY connection
461  */
462 int
463 mii_linkup(mii_handle_t mac, int phy)
464 {
465 	ushort_t status;
466 
467 	/*
468 	 * Link status latches, so we need to read it twice, to make sure we
469 	 * get its current status
470 	 */
471 	mac->mii_read(mac->mii_dip, phy, MII_STATUS);
472 	status = mac->mii_read(mac->mii_dip, phy, MII_STATUS);
473 
474 	if (status != 0xffff && (status & MII_STATUS_LINKUP))
475 		return (1);
476 	else
477 		return (0);
478 }
479 
480 /*
481  * Discover what speed the PHY is running at, irrespective of wheather it
482  * autonegotiated this, or was fixed at that rate.
483  */
484 
485 int
486 mii_getspeed(mii_handle_t mac, int phy, int *speed, int *fulld)
487 {
488 	struct phydata *phyd;
489 
490 	if (!(phyd = mii_get_valid_phydata(mac, phy)))
491 		return (MII_PARAM);
492 	if (!(phyd->control & MII_CONTROL_ANE)) {
493 		/*
494 		 * user has requested fixed speed operation, return what we
495 		 * wrote to the control registerfrom control register
496 		 */
497 
498 		*speed = phyd->control & MII_CONTROL_100MB ? 100:10;
499 		*fulld = phyd->control & MII_CONTROL_FDUPLEX ? 1:0;
500 		return (MII_SUCCESS);
501 	}
502 
503 	if (!phyd->phy_getspeed) /* No standard way to do this(!) */
504 		return (MII_NOTSUPPORTED);
505 
506 	return (phyd->phy_getspeed(mac, phy, speed, fulld));
507 }
508 
509 /*
510  * Fix the speed and duplex mode of a PHY
511  */
512 
513 int
514 mii_fixspeed(mii_handle_t mac, int phy, int speed, int fullduplex)
515 {
516 	struct phydata *phyd;
517 
518 #ifdef MIIDEBUG
519 	cmn_err(CE_CONT, "!%s: setting speed to %d, %s duplex",
520 	    ddi_get_name(mac->mii_dip), speed,
521 	    fullduplex ? "full" : "half");
522 #endif
523 
524 	if (!(phyd = mii_get_valid_phydata(mac, phy)))
525 		return (MII_PARAM);
526 	phyd->control &= ~MII_CONTROL_ANE;
527 
528 	if (speed == 100)
529 		phyd->control |= MII_CONTROL_100MB;
530 	else if (speed == 10)
531 		phyd->control &= ~MII_CONTROL_100MB;
532 	else
533 		cmn_err(CE_NOTE, "%s: mii does not support %d Mb/s speed",
534 		    ddi_get_name(mac->mii_dip), speed);
535 
536 	if (fullduplex)
537 		phyd->control |= MII_CONTROL_FDUPLEX;
538 	else
539 		phyd->control &= ~MII_CONTROL_FDUPLEX;
540 
541 	mac->mii_write(mac->mii_dip, phy, MII_CONTROL, phyd->control);
542 	phyd->fix_speed = speed;
543 	phyd->fix_duplex = fullduplex;
544 	return (MII_SUCCESS);
545 }
546 /*
547  * Electrically isolate/unisolate the PHY
548  */
549 
550 int
551 mii_isolate(mii_handle_t mac, int phy)
552 {
553 	struct phydata *phyd;
554 
555 	if (!(phyd = mii_get_valid_phydata(mac, phy)))
556 		return (MII_PARAM);
557 
558 	phyd->control |= MII_CONTROL_ISOLATE;
559 	mac->mii_write(mac->mii_dip, phy, MII_CONTROL, phyd->control);
560 
561 	/* Wait for device to settle */
562 	drv_usecwait(50);
563 	return (MII_SUCCESS);
564 }
565 
566 int
567 mii_unisolate(mii_handle_t mac, int phy)
568 {
569 	struct phydata *phyd;
570 
571 	if (!(phyd = mii_get_valid_phydata(mac, phy)))
572 		return (MII_PARAM);
573 
574 	phyd->control &= ~MII_CONTROL_ISOLATE;
575 	mac->mii_write(mac->mii_dip, phy, MII_CONTROL, phyd->control);
576 	return (MII_SUCCESS);
577 }
578 
579 /*
580  * Restart autonegotiation on a PHY
581  */
582 
583 int
584 mii_rsan(mii_handle_t mac, int phy, enum mii_wait_type wait)
585 {
586 	int i;
587 	void *dip;
588 	struct phydata *phyd;
589 
590 	if (wait == mii_wait_interrupt ||
591 	    !(phyd = mii_get_valid_phydata(mac, phy)))
592 		return (MII_PARAM);
593 
594 	if (phyd->fix_speed)
595 		return (MII_STATE);
596 
597 	dip = mac->mii_dip;
598 
599 	phyd->control |= MII_CONTROL_ANE;
600 	mac->mii_write(dip, phy, MII_CONTROL, phyd->control|MII_CONTROL_RSAN);
601 
602 	/*
603 	 * This can take ages (a second or so). It makes more sense to use
604 	 * the port monitor rather than waiting for completion of this on the
605 	 * PHY. It is pointless doing a busy wait here
606 	 */
607 
608 	if (wait == mii_wait_user) {
609 		for (i = 200; i--; ) {
610 			delay(drv_usectohz(10000));
611 			if (mac->mii_read(dip, phy, MII_STATUS) &
612 			    MII_STATUS_ANDONE)
613 				return (MII_SUCCESS);
614 		}
615 		cmn_err(CE_NOTE,
616 		    "!%s:Timed out waiting for autonegotiation",
617 		    ddi_get_name(mac->mii_dip));
618 		return (MII_TIMEOUT);
619 	}
620 	return (MII_TIMEOUT);
621 }
622 
623 /*
624  * Debuging function to dump contents of PHY registers
625  */
626 int
627 mii_dump_phy(mii_handle_t mac, int phy)
628 {
629 	struct phydata *phydat;
630 
631 	char *miiregs[] = {
632 		"Control             ",
633 		"Status              ",
634 		"PHY Id(H)           ",
635 		"PHY Id(L)           ",
636 		"Advertisement       ",
637 		"Link Partner Ability",
638 		"Expansion           ",
639 		"Next Page Transmit  ",
640 		0
641 	};
642 	int i;
643 
644 	if (!(phydat = mii_get_valid_phydata(mac, phy)))
645 		return (MII_PARAM);
646 
647 	cmn_err(CE_NOTE, "%s: PHY %d, type %s", ddi_get_name(mac->mii_dip), phy,
648 	    phydat->description ? phydat->description: "Unknown");
649 
650 	for (i = 0; miiregs[i]; i++)
651 		cmn_err(CE_NOTE, "%s:\t%x",
652 		    miiregs[i], mac->mii_read(mac->mii_dip, phy, i));
653 
654 	if (phydat->phy_dump)
655 		phydat->phy_dump((struct mii_info *)mac, phy);
656 
657 	return (MII_SUCCESS);
658 }
659 
660 /*
661  * Start a periodic check to monitor the MII devices attached, and callback
662  * to the MAC driver when the state on a device changes
663  */
664 
665 int
666 mii_start_portmon(mii_handle_t mac, mii_linkfunc_t notify, kmutex_t *lock)
667 {
668 	if (mac->mii_linknotify || mac->portmon_timer)
669 		return (MII_STATE);
670 	mac->mii_linknotify = notify;
671 	/*
672 	 * NOTE: Portmon is normally called through a timeout. In the case
673 	 * of starting off, we assume that the lock is already held
674 	 */
675 	mac->lock = NULL; /* portmon wont try to aquire any lock this time */
676 	mii_portmon(mac);
677 	mac->lock = lock;
678 	return (MII_SUCCESS);
679 }
680 
681 int
682 mii_stop_portmon(mii_handle_t mac)
683 {
684 	if (!mac->mii_linknotify || !mac->portmon_timer)
685 		return (MII_STATE);
686 
687 	mac->mii_linknotify = NULL;
688 	mac->lock = NULL;
689 	(void) untimeout(mac->portmon_timer);
690 	mac->portmon_timer = 0;
691 	return (MII_SUCCESS);
692 }
693 
694 static void
695 mii_portmon(mii_handle_t mac)
696 {
697 	int i;
698 	enum mii_phy_state state;
699 	struct phydata *phydata;
700 
701 	/*
702 	 * There is a potential deadlock between this test and the
703 	 * mutex_enter
704 	 */
705 	if (!mac->mii_linknotify) /* Exiting */
706 		return;
707 
708 	if (mac->lock)
709 		mutex_enter(mac->lock);
710 
711 	/*
712 	 * For each initialised phy, see if the link state has changed, and
713 	 * callback to the mac driver if it has
714 	 */
715 	for (i = 0; i < 32; i++) {
716 		if ((phydata = mac->phys[i]) != 0) {
717 			state = mii_linkup(mac, i) ?
718 			    phy_state_linkup : phy_state_linkdown;
719 			if (state != phydata->state) {
720 #ifdef MIIDEBUG
721 				if (miidebug)
722 					cmn_err(CE_NOTE, "%s: PHY %d link %s",
723 					    ddi_get_name(mac->mii_dip), i,
724 					    state == phy_state_linkup ?
725 					    "up" : "down");
726 #endif
727 				phydata->state = state;
728 				mac->mii_linknotify(mac->mii_dip, i, state);
729 			}
730 		}
731 	}
732 	/* Check the ports every 5 seconds */
733 	mac->portmon_timer = timeout((void (*)(void*))mii_portmon, (void *)mac,
734 	    (clock_t)(5 * drv_usectohz(1000000)));
735 	if (mac->lock)
736 		mutex_exit(mac->lock);
737 }
738 
739 /*
740  * Close a handle to the MII interface from a registered user
741  */
742 
743 void
744 mii_destroy(mii_handle_t mac)
745 {
746 	/* Free per-PHY information */
747 	int i;
748 
749 	(void) mii_stop_portmon(mac);
750 
751 	for (i = 0; i < 32; i++)
752 		if (mac->phys[i])
753 			kmem_free(mac->phys[i], sizeof (struct phydata));
754 
755 	kmem_free(mac, sizeof (*mac));
756 }
757 
758 /*
759  * Get a PHY data structure from an MII handle, and validate the common
760  * parameters to the MII functions. Used to verify parameters in most MII
761  * functions
762  */
763 static struct phydata *
764 mii_get_valid_phydata(mii_handle_t mac, int phy)
765 {
766 	if (!mac || phy > 31 || phy < 0 || !mac->phys[phy]) {
767 		ASSERT(!"MII: Bad invocation");
768 		return (NULL);
769 	}
770 	return (mac->phys[phy]);
771 }
772 /*
773  * Device-specific routines - National Semiconductor
774  */
775 
776 #define	BIT(bit, value) ((value) & (1<<(bit)))
777 static void
778 dump_NS83840(mii_handle_t mac, int phy)
779 {
780 	ushort_t reg;
781 	void *dip;
782 
783 	dip = mac->mii_dip;
784 	cmn_err(CE_NOTE, "Disconnect count: %x",
785 	    mac->mii_read(dip, phy, 0x12));
786 	cmn_err(CE_NOTE, "False Carrier detect count: %x",
787 	    mac->mii_read(dip, phy, 0x13));
788 	cmn_err(CE_NOTE, "Receive error count: %x",
789 	    mac->mii_read(dip, phy, 0x15));
790 	cmn_err(CE_NOTE, "Silicon revision: %x",
791 	    mac->mii_read(dip, phy, 0x16));
792 	cmn_err(CE_NOTE, "PCS Configuration : %x",
793 	    mac->mii_read(dip, phy, 0x17));
794 
795 	cmn_err(CE_NOTE, "Loopback, Bypass and Receiver error mask: %x",
796 	    mac->mii_read(dip, phy, 0x18));
797 	cmn_err(CE_NOTE, "Wired phy address: %x",
798 	    mac->mii_read(dip, phy, 0x19)&0xf);
799 
800 	reg = mac->mii_read(dip, phy, 0x1b);
801 	cmn_err(CE_NOTE, "10 Base T in %s mode",
802 	    BIT(9, reg) ? "serial":"nibble");
803 
804 	cmn_err(CE_NOTE, "%slink pulses, %sheartbeat, %s,%s squelch,jabber %s",
805 	    BIT(reg, 5) ? "" : "no ",
806 	    BIT(reg, 4) ? "" : "no ",
807 	    BIT(reg, 3) ? "UTP" : "STP",
808 	    BIT(reg, 2) ? "low" : "normal",
809 	    BIT(reg, 0) ? "enabled" : "disabled");
810 }
811 
812 static int
813 getspeed_NS83840(mii_handle_t mac, int phy, int *speed, int *fulld)
814 {
815 	int exten =  mac->mii_read(mac->mii_dip, phy, MII_AN_EXPANSION);
816 	if (exten & MII_AN_EXP_LPCANAN) {
817 		/*
818 		 * Link partner can auto-neg, take speed from LP Ability
819 		 * register
820 		 */
821 		int lpable, anadv, mask;
822 
823 		lpable = mac->mii_read(mac->mii_dip, phy, MII_AN_LPABLE);
824 		anadv = mac->mii_read(mac->mii_dip, phy, MII_AN_ADVERT);
825 		mask = anadv & lpable;
826 
827 		if (mask & MII_ABILITY_100BASE_TX_FD) {
828 			*speed = 100;
829 			*fulld = 1;
830 		} else if (mask & MII_ABILITY_100BASE_T4) {
831 			*speed = 100;
832 			*fulld = 0;
833 		} else if (mask & MII_ABILITY_100BASE_TX) {
834 			*speed = 100;
835 			*fulld = 0;
836 		} else if (mask & MII_ABILITY_10BASE_T_FD) {
837 			*speed = 10;
838 			*fulld = 1;
839 		} else if (mask & MII_ABILITY_10BASE_T) {
840 			*speed = 10;
841 			*fulld = 0;
842 		}
843 	} else {
844 		int addr = mac->mii_read(mac->mii_dip, phy, MII_83840_ADDR);
845 		*speed = (addr & NS83840_ADDR_SPEED10) ? 10:100;
846 		/* No fullduplex without autonegotiation on link partner */
847 		*fulld = 0;
848 	}
849 	return (0);
850 }
851 
852 /*
853  * Device-specific routines - INTEL
854  */
855 
856 static int
857 getspeed_82553(mii_handle_t mac, int phy, int *speed, int *fulld)
858 {
859 	int ex0 = mac->mii_read(mac->mii_dip, phy, MII_82553_EX0);
860 	*fulld = (ex0 & I82553_EX0_FDUPLEX) ? 1:0;
861 	*speed = (ex0 & I82553_EX0_100MB) ? 100:10;
862 	return (0);
863 }
864 
865 /*
866  * Device-specific routines - ICS
867  */
868 
869 static int
870 getspeed_ICS1890(mii_handle_t mac, int phy, int *speed, int *fulld)
871 {
872 	ushort_t quickpoll = mac->mii_read(mac->mii_dip, phy, ICS_QUICKPOLL);
873 	*speed = (quickpoll & ICS_QUICKPOLL_100MB) ? 100 : 10;
874 	*fulld = (quickpoll & ICS_QUICKPOLL_FDUPLEX) ? 1 : 0;
875 	return (0);
876 }
877 
878 static void
879 dump_ICS1890(mii_handle_t mac, int phy)
880 {
881 	ushort_t quickpoll = mac->mii_read(mac->mii_dip, phy, ICS_QUICKPOLL);
882 	cmn_err(CE_NOTE, "QuickPoll:%x (Speed:%d FullDuplex:%c) ",
883 	    quickpoll,
884 	    quickpoll & ICS_QUICKPOLL_100MB ? 100:10,
885 	    quickpoll & ICS_QUICKPOLL_FDUPLEX ? 'Y' : 'N');
886 }
887 
888 static void
889 postreset_NS83840(mii_handle_t mac, int phy)
890 {
891 	ushort_t reg;
892 	struct phydata *phyd = mac->phys[phy];
893 	/*
894 	 * As per INTEL "PRO/100B Adapter Software Technical
895 	 * Reference Manual", set bit 10 of MII register 23.
896 	 * National Semiconductor documentation shows this as
897 	 * "reserved, write to as zero". We also set the
898 	 * "f_connect" bit, also as requested by the PRO/100B
899 	 * doc
900 	 */
901 
902 	reg = mac->mii_read(mac->mii_dip, phy, 23) | (1<<10) | (1<<5);
903 	mac->mii_write(mac->mii_dip, phy, 23, reg);
904 
905 	/*
906 	 * Some of thses PHYs seem to reset with the wrong value in the
907 	 * AN advertisment register. It should containt 1e1, indicating that
908 	 * the device can do 802.3 10BASE-T, 10BASE-T Full duplex, 100BASE-TX,
909 	 * and 100 BASE-TX full duplex. Instead it seems to advertise only
910 	 * 100BASE-TX Full duplex. The result of this is that the device will
911 	 * NOT autonegotiate at all against a 10MB only or 100MB/Half duplex
912 	 * autonegotiating hub
913 	 * NEEDSWORK:
914 	 * There is possibly a time-dependancy here.
915 	 * If the autonegotiation has completed BEFORE we get to here
916 	 * (after the reset) then this could possibly have not effect
917 	 */
918 	if (!phyd->fix_speed) {
919 #ifdef MIIDEBUG
920 		if (miidebug & MIICOMPAT)
921 			cmn_err(CE_NOTE, "Reset value of AN_ADV reg:%x",
922 			    mac->mii_read(mac->mii_dip, phy, MII_AN_ADVERT));
923 #endif
924 		mac->mii_write(mac->mii_dip, phy, MII_AN_ADVERT, 0x1e1);
925 	}
926 }
927 
928 void
929 postreset_ICS1890(mii_handle_t mac, int phy)
930 {
931 	/* This device comes up isolated if no link is found */
932 	(void) mii_unisolate(mac, phy);
933 }
934 
935 /*
936  * generic getspeed routine
937  */
938 static int
939 getspeed_generic(mii_handle_t mac, int phy, int *speed, int *fulld)
940 {
941 	int exten =  mac->mii_read(mac->mii_dip, phy, MII_AN_EXPANSION);
942 	if (exten & MII_AN_EXP_LPCANAN) {
943 		/*
944 		 * Link partner can auto-neg, take speed from LP Ability
945 		 * register
946 		 */
947 		int lpable, anadv, mask;
948 
949 		lpable = mac->mii_read(mac->mii_dip, phy, MII_AN_LPABLE);
950 		anadv = mac->mii_read(mac->mii_dip, phy, MII_AN_ADVERT);
951 		mask = anadv & lpable;
952 
953 		if (mask & MII_ABILITY_100BASE_TX_FD) {
954 			*speed = 100;
955 			*fulld = 1;
956 		} else if (mask & MII_ABILITY_100BASE_T4) {
957 			*speed = 100;
958 			*fulld = 0;
959 		} else if (mask & MII_ABILITY_100BASE_TX) {
960 			*speed = 100;
961 			*fulld = 0;
962 		} else if (mask & MII_ABILITY_10BASE_T_FD) {
963 			*speed = 10;
964 			*fulld = 1;
965 		} else if (mask & MII_ABILITY_10BASE_T) {
966 			*speed = 10;
967 			*fulld = 0;
968 		}
969 	} else {
970 		/*
971 		 * Link partner cannot auto-neg, it would be nice if we
972 		 * could figure out what the device selected.  (NWay?)
973 		 */
974 		*speed = 0;
975 		*fulld = 0;
976 	}
977 	return (MII_SUCCESS);
978 }
979