xref: /netbsd-src/sys/dev/mii/mii_physubr.c (revision 54c71dee8ce8ff710b7e2b5a511b77d6cae19a0e)
1 /*	$NetBSD: mii_physubr.c,v 1.70 2010/06/06 18:58:22 pgoyette Exp $	*/
2 
3 /*-
4  * Copyright (c) 1998, 1999, 2000, 2001 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
9  * NASA Ames Research Center.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 /*
34  * Subroutines common to all PHYs.
35  */
36 
37 #include <sys/cdefs.h>
38 __KERNEL_RCSID(0, "$NetBSD: mii_physubr.c,v 1.70 2010/06/06 18:58:22 pgoyette Exp $");
39 
40 #include <sys/param.h>
41 #include <sys/device.h>
42 #include <sys/systm.h>
43 #include <sys/kernel.h>
44 #include <sys/socket.h>
45 #include <sys/errno.h>
46 #include <sys/module.h>
47 #include <sys/proc.h>
48 
49 #include <net/if.h>
50 #include <net/if_media.h>
51 #include <net/route.h>
52 
53 #include <dev/mii/mii.h>
54 #include <dev/mii/miivar.h>
55 
56 const char *(*mii_get_descr)(int, int) = mii_get_descr_stub;
57 
58 int mii_verbose_loaded = 0;
59 
60 const char *mii_get_descr_stub(int oui, int model)
61 {
62 	mii_load_verbose();
63 	if (mii_verbose_loaded)
64 		return mii_get_descr(oui, model);
65 	else
66 		return NULL;
67 }
68 
69 /*
70  * Routine to load the miiverbose kernel module as needed
71  */
72 void mii_load_verbose(void)
73 {
74 	if (mii_verbose_loaded)
75 		return;
76 
77 	mutex_enter(&module_lock);
78 	if (module_autoload("miiverbose", MODULE_CLASS_MISC) ==0)
79 		mii_verbose_loaded++;
80 	mutex_exit(&module_lock);
81 }
82 
83 static void mii_phy_statusmsg(struct mii_softc *);
84 
85 /*
86  * Media to register setting conversion table.  Order matters.
87  */
88 static const struct mii_media mii_media_table[MII_NMEDIA] = {
89 	/* None */
90 	{ BMCR_ISO,		ANAR_CSMA,
91 	  0, },
92 
93 	/* 10baseT */
94 	{ BMCR_S10,		ANAR_CSMA|ANAR_10,
95 	  0, },
96 
97 	/* 10baseT-FDX */
98 	{ BMCR_S10|BMCR_FDX,	ANAR_CSMA|ANAR_10_FD,
99 	  0, },
100 
101 	/* 100baseT4 */
102 	{ BMCR_S100,		ANAR_CSMA|ANAR_T4,
103 	  0, },
104 
105 	/* 100baseTX */
106 	{ BMCR_S100,		ANAR_CSMA|ANAR_TX,
107 	  0, },
108 
109 	/* 100baseTX-FDX */
110 	{ BMCR_S100|BMCR_FDX,	ANAR_CSMA|ANAR_TX_FD,
111 	  0, },
112 
113 	/* 1000baseX */
114 	{ BMCR_S1000,		ANAR_CSMA,
115 	  0, },
116 
117 	/* 1000baseX-FDX */
118 	{ BMCR_S1000|BMCR_FDX,	ANAR_CSMA,
119 	  0, },
120 
121 	/* 1000baseT */
122 	{ BMCR_S1000,		ANAR_CSMA,
123 	  GTCR_ADV_1000THDX },
124 
125 	/* 1000baseT-FDX */
126 	{ BMCR_S1000,		ANAR_CSMA,
127 	  GTCR_ADV_1000TFDX },
128 };
129 
130 static void	mii_phy_auto_timeout(void *);
131 
132 void
133 mii_phy_setmedia(struct mii_softc *sc)
134 {
135 	struct mii_data *mii = sc->mii_pdata;
136 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
137 	int bmcr, anar, gtcr;
138 
139 	if (IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO) {
140 		/*
141 		 * Force renegotiation if MIIF_DOPAUSE.
142 		 *
143 		 * XXX This is only necessary because many NICs don't
144 		 * XXX advertise PAUSE capabilities at boot time.  Maybe
145 		 * XXX we should force this only once?
146 		 */
147 		if ((PHY_READ(sc, MII_BMCR) & BMCR_AUTOEN) == 0 ||
148 		    (sc->mii_flags & (MIIF_FORCEANEG|MIIF_DOPAUSE)))
149 			(void) mii_phy_auto(sc, 1);
150 		return;
151 	}
152 
153 	/*
154 	 * Table index is stored in the media entry.
155 	 */
156 
157 #ifdef DIAGNOSTIC
158 	if (/* ife->ifm_data < 0 || */ ife->ifm_data >= MII_NMEDIA)
159 		panic("mii_phy_setmedia");
160 #endif
161 
162 	anar = mii_media_table[ife->ifm_data].mm_anar;
163 	bmcr = mii_media_table[ife->ifm_data].mm_bmcr;
164 	gtcr = mii_media_table[ife->ifm_data].mm_gtcr;
165 
166 	if (mii->mii_media.ifm_media & IFM_ETH_MASTER) {
167 		switch (IFM_SUBTYPE(ife->ifm_media)) {
168 		case IFM_1000_T:
169 			gtcr |= GTCR_MAN_MS|GTCR_ADV_MS;
170 			break;
171 
172 		default:
173 			panic("mii_phy_setmedia: MASTER on wrong media");
174 		}
175 	}
176 
177 	if (mii->mii_media.ifm_media & IFM_FLOW) {
178 		if (sc->mii_flags & MIIF_IS_1000X)
179 			anar |= ANAR_X_PAUSE_SYM | ANAR_X_PAUSE_ASYM;
180 		else {
181 			anar |= ANAR_FC;
182 			/* XXX Only 1000BASE-T has PAUSE_ASYM? */
183 			if ((sc->mii_flags & MIIF_HAVE_GTCR) &&
184 			    (sc->mii_extcapabilities &
185 			     (EXTSR_1000THDX|EXTSR_1000TFDX)))
186 				anar |= ANAR_X_PAUSE_ASYM;
187 		}
188 	}
189 
190 	if (ife->ifm_media & IFM_LOOP)
191 		bmcr |= BMCR_LOOP;
192 
193 	PHY_WRITE(sc, MII_ANAR, anar);
194 	PHY_WRITE(sc, MII_BMCR, bmcr);
195 	if (sc->mii_flags & MIIF_HAVE_GTCR)
196 		PHY_WRITE(sc, MII_100T2CR, gtcr);
197 }
198 
199 int
200 mii_phy_auto(struct mii_softc *sc, int waitfor)
201 {
202 	int i;
203 
204 	if ((sc->mii_flags & MIIF_DOINGAUTO) == 0) {
205 		/*
206 		 * Check for 1000BASE-X.  Autonegotiation is a bit
207 		 * different on such devices.
208 		 */
209 		if (sc->mii_flags & MIIF_IS_1000X) {
210 			uint16_t anar = 0;
211 
212 			if (sc->mii_extcapabilities & EXTSR_1000XFDX)
213 				anar |= ANAR_X_FD;
214 			if (sc->mii_extcapabilities & EXTSR_1000XHDX)
215 				anar |= ANAR_X_HD;
216 
217 			if (sc->mii_flags & MIIF_DOPAUSE) {
218 				/* XXX Asymmetric vs. symmetric? */
219 				anar |= ANLPAR_X_PAUSE_TOWARDS;
220 			}
221 
222 			PHY_WRITE(sc, MII_ANAR, anar);
223 		} else {
224 			uint16_t anar;
225 
226 			anar = BMSR_MEDIA_TO_ANAR(sc->mii_capabilities) |
227 			    ANAR_CSMA;
228 			if (sc->mii_flags & MIIF_DOPAUSE) {
229 				anar |= ANAR_FC;
230 				/* XXX Only 1000BASE-T has PAUSE_ASYM? */
231 				if ((sc->mii_flags & MIIF_HAVE_GTCR) &&
232 				    (sc->mii_extcapabilities &
233 				     (EXTSR_1000THDX|EXTSR_1000TFDX)))
234 					anar |= ANAR_X_PAUSE_ASYM;
235 			}
236 			PHY_WRITE(sc, MII_ANAR, anar);
237 			if (sc->mii_flags & MIIF_HAVE_GTCR) {
238 				uint16_t gtcr = 0;
239 
240 				if (sc->mii_extcapabilities & EXTSR_1000TFDX)
241 					gtcr |= GTCR_ADV_1000TFDX;
242 				if (sc->mii_extcapabilities & EXTSR_1000THDX)
243 					gtcr |= GTCR_ADV_1000THDX;
244 
245 				PHY_WRITE(sc, MII_100T2CR, gtcr);
246 			}
247 		}
248 		PHY_WRITE(sc, MII_BMCR, BMCR_AUTOEN | BMCR_STARTNEG);
249 	}
250 
251 	if (waitfor) {
252 		/* Wait 500ms for it to complete. */
253 		for (i = 0; i < 500; i++) {
254 			if (PHY_READ(sc, MII_BMSR) & BMSR_ACOMP)
255 				return (0);
256 			delay(1000);
257 		}
258 
259 		/*
260 		 * Don't need to worry about clearing MIIF_DOINGAUTO.
261 		 * If that's set, a timeout is pending, and it will
262 		 * clear the flag.
263 		 */
264 		return (EIO);
265 	}
266 
267 	/*
268 	 * Just let it finish asynchronously.  This is for the benefit of
269 	 * the tick handler driving autonegotiation.  Don't want 500ms
270 	 * delays all the time while the system is running!
271 	 */
272 	if (sc->mii_flags & MIIF_AUTOTSLEEP) {
273 		sc->mii_flags |= MIIF_DOINGAUTO;
274 		tsleep(&sc->mii_flags, PZERO, "miiaut", hz >> 1);
275 		mii_phy_auto_timeout(sc);
276 	} else if ((sc->mii_flags & MIIF_DOINGAUTO) == 0) {
277 		sc->mii_flags |= MIIF_DOINGAUTO;
278 		callout_reset(&sc->mii_nway_ch, hz >> 1,
279 		    mii_phy_auto_timeout, sc);
280 	}
281 	return (EJUSTRETURN);
282 }
283 
284 static void
285 mii_phy_auto_timeout(void *arg)
286 {
287 	struct mii_softc *sc = arg;
288 	int s;
289 
290 	if (!device_is_active(sc->mii_dev))
291 		return;
292 
293 	s = splnet();
294 	sc->mii_flags &= ~MIIF_DOINGAUTO;
295 
296 	/* Update the media status. */
297 	(void) PHY_SERVICE(sc, sc->mii_pdata, MII_POLLSTAT);
298 	splx(s);
299 }
300 
301 int
302 mii_phy_tick(struct mii_softc *sc)
303 {
304 	struct mii_data *mii = sc->mii_pdata;
305 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
306 	int reg;
307 
308 	/* Just bail now if the interface is down. */
309 	if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
310 		return (EJUSTRETURN);
311 
312 	/*
313 	 * If we're not doing autonegotiation, we don't need to do
314 	 * any extra work here.  However, we need to check the link
315 	 * status so we can generate an announcement if the status
316 	 * changes.
317 	 */
318 	if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO)
319 		return (0);
320 
321 	/* Read the status register twice; BMSR_LINK is latch-low. */
322 	reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
323 	if (reg & BMSR_LINK) {
324 		/*
325 		 * See above.
326 		 */
327 		return (0);
328 	}
329 
330 	/*
331 	 * Only retry autonegotiation every N seconds.
332 	 */
333 	KASSERT(sc->mii_anegticks != 0);
334 	if (++sc->mii_ticks <= sc->mii_anegticks)
335 		return (EJUSTRETURN);
336 
337 	sc->mii_ticks = 0;
338 	PHY_RESET(sc);
339 
340 	if (mii_phy_auto(sc, 0) == EJUSTRETURN)
341 		return (EJUSTRETURN);
342 
343 	/*
344 	 * Might need to generate a status message if autonegotiation
345 	 * failed.
346 	 */
347 	return (0);
348 }
349 
350 void
351 mii_phy_reset(struct mii_softc *sc)
352 {
353 	int reg, i;
354 
355 	if (sc->mii_flags & MIIF_NOISOLATE)
356 		reg = BMCR_RESET;
357 	else
358 		reg = BMCR_RESET | BMCR_ISO;
359 	PHY_WRITE(sc, MII_BMCR, reg);
360 
361 	/* Wait another 100ms for it to complete. */
362 	for (i = 0; i < 100; i++) {
363 		reg = PHY_READ(sc, MII_BMCR);
364 		if ((reg & BMCR_RESET) == 0)
365 			break;
366 		delay(1000);
367 	}
368 
369 	if (sc->mii_inst != 0 && ((sc->mii_flags & MIIF_NOISOLATE) == 0))
370 		PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO);
371 }
372 
373 void
374 mii_phy_down(struct mii_softc *sc)
375 {
376 
377 	if (sc->mii_flags & MIIF_DOINGAUTO) {
378 		sc->mii_flags &= ~MIIF_DOINGAUTO;
379 		callout_stop(&sc->mii_nway_ch);
380 	}
381 }
382 
383 void
384 mii_phy_status(struct mii_softc *sc)
385 {
386 
387 	PHY_STATUS(sc);
388 }
389 
390 void
391 mii_phy_update(struct mii_softc *sc, int cmd)
392 {
393 	struct mii_data *mii = sc->mii_pdata;
394 
395 	if (sc->mii_media_active != mii->mii_media_active ||
396 	    sc->mii_media_status != mii->mii_media_status ||
397 	    cmd == MII_MEDIACHG) {
398 		mii_phy_statusmsg(sc);
399 		(*mii->mii_statchg)(device_parent(sc->mii_dev));
400 		sc->mii_media_active = mii->mii_media_active;
401 		sc->mii_media_status = mii->mii_media_status;
402 	}
403 }
404 
405 static void
406 mii_phy_statusmsg(struct mii_softc *sc)
407 {
408 	struct mii_data *mii = sc->mii_pdata;
409 	struct ifnet *ifp = mii->mii_ifp;
410 	int s;
411 
412 	s = splnet();
413 	if (mii->mii_media_status & IFM_AVALID) {
414 		if (mii->mii_media_status & IFM_ACTIVE)
415 			if_link_state_change(ifp, LINK_STATE_UP);
416 		else
417 			if_link_state_change(ifp, LINK_STATE_DOWN);
418 	} else
419 		if_link_state_change(ifp, LINK_STATE_UNKNOWN);
420 	splx(s);
421 
422 	ifp->if_baudrate = ifmedia_baudrate(mii->mii_media_active);
423 }
424 
425 /*
426  * Initialize generic PHY media based on BMSR, called when a PHY is
427  * attached.  We expect to be set up to print a comma-separated list
428  * of media names.  Does not print a newline.
429  */
430 void
431 mii_phy_add_media(struct mii_softc *sc)
432 {
433 	struct mii_data *mii = sc->mii_pdata;
434 	device_t self = sc->mii_dev;
435 	const char *sep = "";
436 	int fdx = 0;
437 
438 #define	ADD(m, c)	ifmedia_add(&mii->mii_media, (m), (c), NULL)
439 #define	PRINT(n)	aprint_normal("%s%s", sep, (n)); sep = ", "
440 
441 	if ((sc->mii_flags & MIIF_NOISOLATE) == 0)
442 		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_NONE, 0, sc->mii_inst),
443 		    MII_MEDIA_NONE);
444 
445 	/*
446 	 * There are different interpretations for the bits in
447 	 * HomePNA PHYs.  And there is really only one media type
448 	 * that is supported.
449 	 */
450 	if (sc->mii_flags & MIIF_IS_HPNA) {
451 		if (sc->mii_capabilities & BMSR_10THDX) {
452 			ADD(IFM_MAKEWORD(IFM_ETHER, IFM_HPNA_1, 0,
453 					 sc->mii_inst),
454 			    MII_MEDIA_10_T);
455 			PRINT("HomePNA1");
456 		}
457 		goto out;
458 	}
459 
460 	if (sc->mii_capabilities & BMSR_10THDX) {
461 		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, 0, sc->mii_inst),
462 		    MII_MEDIA_10_T);
463 		PRINT("10baseT");
464 	}
465 	if (sc->mii_capabilities & BMSR_10TFDX) {
466 		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, IFM_FDX, sc->mii_inst),
467 		    MII_MEDIA_10_T_FDX);
468 		PRINT("10baseT-FDX");
469 		fdx = 1;
470 	}
471 	if (sc->mii_capabilities & BMSR_100TXHDX) {
472 		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, 0, sc->mii_inst),
473 		    MII_MEDIA_100_TX);
474 		PRINT("100baseTX");
475 	}
476 	if (sc->mii_capabilities & BMSR_100TXFDX) {
477 		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_FDX, sc->mii_inst),
478 		    MII_MEDIA_100_TX_FDX);
479 		PRINT("100baseTX-FDX");
480 		fdx = 1;
481 	}
482 	if (sc->mii_capabilities & BMSR_100T4) {
483 		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_T4, 0, sc->mii_inst),
484 		    MII_MEDIA_100_T4);
485 		PRINT("100baseT4");
486 	}
487 
488 	if (sc->mii_extcapabilities & EXTSR_MEDIAMASK) {
489 		/*
490 		 * XXX Right now only handle 1000SX and 1000TX.  Need
491 		 * XXX to handle 1000LX and 1000CX some how.
492 		 *
493 		 * Note since it can take 5 seconds to auto-negotiate
494 		 * a gigabit link, we make anegticks 10 seconds for
495 		 * all the gigabit media types.
496 		 */
497 		if (sc->mii_extcapabilities & EXTSR_1000XHDX) {
498 			sc->mii_anegticks = MII_ANEGTICKS_GIGE;
499 			sc->mii_flags |= MIIF_IS_1000X;
500 			ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_SX, 0,
501 			    sc->mii_inst), MII_MEDIA_1000_X);
502 			PRINT("1000baseSX");
503 		}
504 		if (sc->mii_extcapabilities & EXTSR_1000XFDX) {
505 			sc->mii_anegticks = MII_ANEGTICKS_GIGE;
506 			sc->mii_flags |= MIIF_IS_1000X;
507 			ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_SX, IFM_FDX,
508 			    sc->mii_inst), MII_MEDIA_1000_X_FDX);
509 			PRINT("1000baseSX-FDX");
510 			fdx = 1;
511 		}
512 
513 		/*
514 		 * 1000baseT media needs to be able to manipulate
515 		 * master/slave mode.  We set IFM_ETH_MASTER in
516 		 * the "don't care mask" and filter it out when
517 		 * the media is set.
518 		 *
519 		 * All 1000baseT PHYs have a 1000baseT control register.
520 		 */
521 		if (sc->mii_extcapabilities & EXTSR_1000THDX) {
522 			sc->mii_anegticks = MII_ANEGTICKS_GIGE;
523 			sc->mii_flags |= MIIF_HAVE_GTCR;
524 			mii->mii_media.ifm_mask |= IFM_ETH_MASTER;
525 			ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, 0,
526 			    sc->mii_inst), MII_MEDIA_1000_T);
527 			PRINT("1000baseT");
528 		}
529 		if (sc->mii_extcapabilities & EXTSR_1000TFDX) {
530 			sc->mii_anegticks = MII_ANEGTICKS_GIGE;
531 			sc->mii_flags |= MIIF_HAVE_GTCR;
532 			mii->mii_media.ifm_mask |= IFM_ETH_MASTER;
533 			ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, IFM_FDX,
534 			    sc->mii_inst), MII_MEDIA_1000_T_FDX);
535 			PRINT("1000baseT-FDX");
536 			fdx = 1;
537 		}
538 	}
539 
540 	if (sc->mii_capabilities & BMSR_ANEG) {
541 		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, sc->mii_inst),
542 		    MII_NMEDIA);	/* intentionally invalid index */
543 		PRINT("auto");
544 	}
545 #undef ADD
546 #undef PRINT
547 	if (fdx != 0 && (sc->mii_flags & MIIF_DOPAUSE))
548 		mii->mii_media.ifm_mask |= IFM_ETH_FMASK;
549 out:
550 	if (!pmf_device_register(self, NULL, mii_phy_resume)) {
551 		aprint_normal("\n");
552 		aprint_error_dev(self, "couldn't establish power handler");
553 	}
554 }
555 
556 void
557 mii_phy_delete_media(struct mii_softc *sc)
558 {
559 	struct mii_data *mii = sc->mii_pdata;
560 
561 	ifmedia_delete_instance(&mii->mii_media, sc->mii_inst);
562 }
563 
564 int
565 mii_phy_activate(device_t self, enum devact act)
566 {
567 	switch (act) {
568 	case DVACT_DEACTIVATE:
569 		/* XXX Invalidate parent's media setting? */
570 		return 0;
571 	default:
572 		return EOPNOTSUPP;
573 	}
574 }
575 
576 /* ARGSUSED1 */
577 int
578 mii_phy_detach(device_t self, int flags)
579 {
580 	struct mii_softc *sc = device_private(self);
581 
582 	/* XXX Invalidate parent's media setting? */
583 
584 	if (sc->mii_flags & MIIF_DOINGAUTO)
585 		callout_halt(&sc->mii_nway_ch, NULL);
586 
587 	callout_destroy(&sc->mii_nway_ch);
588 
589 	mii_phy_delete_media(sc);
590 	LIST_REMOVE(sc, mii_list);
591 
592 	return (0);
593 }
594 
595 const struct mii_phydesc *
596 mii_phy_match(const struct mii_attach_args *ma, const struct mii_phydesc *mpd)
597 {
598 
599 	for (; mpd->mpd_name != NULL; mpd++) {
600 		if (MII_OUI(ma->mii_id1, ma->mii_id2) == mpd->mpd_oui &&
601 		    MII_MODEL(ma->mii_id2) == mpd->mpd_model)
602 			return (mpd);
603 	}
604 	return (NULL);
605 }
606 
607 /*
608  * Return the flow control status flag from MII_ANAR & MII_ANLPAR.
609  */
610 u_int
611 mii_phy_flowstatus(struct mii_softc *sc)
612 {
613 	u_int anar, anlpar;
614 
615 	if ((sc->mii_flags & MIIF_DOPAUSE) == 0)
616 		return (0);
617 
618 	anar = PHY_READ(sc, MII_ANAR);
619 	anlpar = PHY_READ(sc, MII_ANLPAR);
620 
621 	if ((anar & ANAR_X_PAUSE_SYM) & (anlpar & ANLPAR_X_PAUSE_SYM))
622 		return (IFM_FLOW|IFM_ETH_TXPAUSE|IFM_ETH_RXPAUSE);
623 
624 	if ((anar & ANAR_X_PAUSE_SYM) == 0) {
625 		if ((anar & ANAR_X_PAUSE_ASYM) &&
626 		    ((anlpar &
627 		      ANLPAR_X_PAUSE_TOWARDS) == ANLPAR_X_PAUSE_TOWARDS))
628 			return (IFM_FLOW|IFM_ETH_TXPAUSE);
629 		else
630 			return (0);
631 	}
632 
633 	if ((anar & ANAR_X_PAUSE_ASYM) == 0) {
634 		if (anlpar & ANLPAR_X_PAUSE_SYM)
635 			return (IFM_FLOW|IFM_ETH_TXPAUSE|IFM_ETH_RXPAUSE);
636 		else
637 			return (0);
638 	}
639 
640 	switch ((anlpar & ANLPAR_X_PAUSE_TOWARDS)) {
641 	case ANLPAR_X_PAUSE_NONE:
642 		return (0);
643 
644 	case ANLPAR_X_PAUSE_ASYM:
645 		return (IFM_FLOW|IFM_ETH_RXPAUSE);
646 
647 	default:
648 		return (IFM_FLOW|IFM_ETH_RXPAUSE|IFM_ETH_TXPAUSE);
649 	}
650 	/* NOTREACHED */
651 }
652 
653 bool
654 mii_phy_resume(device_t dv, const pmf_qual_t *qual)
655 {
656 	struct mii_softc *sc = device_private(dv);
657 
658 	PHY_RESET(sc);
659 	return PHY_SERVICE(sc, sc->mii_pdata, MII_MEDIACHG) == 0;
660 }
661 
662 
663 /*
664  * Given an ifmedia word, return the corresponding ANAR value.
665  */
666 int
667 mii_anar(int media)
668 {
669 	int rv;
670 
671 	switch (media & (IFM_TMASK|IFM_NMASK|IFM_FDX)) {
672 	case IFM_ETHER|IFM_10_T:
673 		rv = ANAR_10|ANAR_CSMA;
674 		break;
675 	case IFM_ETHER|IFM_10_T|IFM_FDX:
676 		rv = ANAR_10_FD|ANAR_CSMA;
677 		break;
678 	case IFM_ETHER|IFM_100_TX:
679 		rv = ANAR_TX|ANAR_CSMA;
680 		break;
681 	case IFM_ETHER|IFM_100_TX|IFM_FDX:
682 		rv = ANAR_TX_FD|ANAR_CSMA;
683 		break;
684 	case IFM_ETHER|IFM_100_T4:
685 		rv = ANAR_T4|ANAR_CSMA;
686 		break;
687 	default:
688 		rv = 0;
689 		break;
690 	}
691 
692 	return rv;
693 }
694