xref: /freebsd/sys/dev/mii/mii_physubr.c (revision 8ef24a0d4b28fe230e20637f56869cc4148cd2ca)
1 /*	$NetBSD: mii_physubr.c,v 1.5 1999/08/03 19:41:49 drochner 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 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
35 
36 /*
37  * Subroutines common to all PHYs.
38  */
39 
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/kernel.h>
43 #include <sys/socket.h>
44 #include <sys/errno.h>
45 #include <sys/module.h>
46 #include <sys/bus.h>
47 
48 #include <net/if.h>
49 #include <net/if_media.h>
50 
51 #include <dev/mii/mii.h>
52 #include <dev/mii/miivar.h>
53 
54 #include "miibus_if.h"
55 
56 /*
57  *
58  * An array of structures to map MII media types to BMCR/ANAR settings.
59  */
60 enum {
61 	MII_MEDIA_NONE = 0,
62 	MII_MEDIA_10_T,
63 	MII_MEDIA_10_T_FDX,
64 	MII_MEDIA_100_T4,
65 	MII_MEDIA_100_TX,
66 	MII_MEDIA_100_TX_FDX,
67 	MII_MEDIA_1000_X,
68 	MII_MEDIA_1000_X_FDX,
69 	MII_MEDIA_1000_T,
70 	MII_MEDIA_1000_T_FDX,
71 	MII_NMEDIA,
72 };
73 
74 static const struct mii_media {
75 	u_int	mm_bmcr;		/* BMCR settings for this media */
76 	u_int	mm_anar;		/* ANAR settings for this media */
77 	u_int	mm_gtcr;		/* 100base-T2 or 1000base-T CR */
78 } mii_media_table[MII_NMEDIA] = {
79 	/* None */
80 	{ BMCR_ISO,		ANAR_CSMA,
81 	  0, },
82 
83 	/* 10baseT */
84 	{ BMCR_S10,		ANAR_CSMA|ANAR_10,
85 	  0, },
86 
87 	/* 10baseT-FDX */
88 	{ BMCR_S10|BMCR_FDX,	ANAR_CSMA|ANAR_10_FD,
89 	  0, },
90 
91 	/* 100baseT4 */
92 	{ BMCR_S100,		ANAR_CSMA|ANAR_T4,
93 	  0, },
94 
95 	/* 100baseTX */
96 	{ BMCR_S100,		ANAR_CSMA|ANAR_TX,
97 	  0, },
98 
99 	/* 100baseTX-FDX */
100 	{ BMCR_S100|BMCR_FDX,	ANAR_CSMA|ANAR_TX_FD,
101 	  0, },
102 
103 	/* 1000baseX */
104 	{ BMCR_S1000,		ANAR_CSMA,
105 	  0, },
106 
107 	/* 1000baseX-FDX */
108 	{ BMCR_S1000|BMCR_FDX,	ANAR_CSMA,
109 	  0, },
110 
111 	/* 1000baseT */
112 	{ BMCR_S1000,		ANAR_CSMA,
113 	  GTCR_ADV_1000THDX },
114 
115 	/* 1000baseT-FDX */
116 	{ BMCR_S1000,		ANAR_CSMA,
117 	  GTCR_ADV_1000TFDX },
118 };
119 
120 void
121 mii_phy_setmedia(struct mii_softc *sc)
122 {
123 	struct mii_data *mii = sc->mii_pdata;
124 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
125 	int bmcr, anar, gtcr;
126 	int index = -1;
127 
128 	switch (IFM_SUBTYPE(ife->ifm_media)) {
129 	case IFM_AUTO:
130 		/*
131 		 * Force renegotiation if MIIF_DOPAUSE or MIIF_FORCEANEG.
132 		 * The former is necessary as we might switch from flow-
133 		 * control advertisement being off to on or vice versa.
134 		 */
135 		if ((PHY_READ(sc, MII_BMCR) & BMCR_AUTOEN) == 0 ||
136 		    (sc->mii_flags & (MIIF_DOPAUSE | MIIF_FORCEANEG)) != 0)
137 			(void)mii_phy_auto(sc);
138 		return;
139 
140 	case IFM_NONE:
141 		index = MII_MEDIA_NONE;
142 		break;
143 
144 	case IFM_HPNA_1:
145 		index = MII_MEDIA_10_T;
146 		break;
147 
148 	case IFM_10_T:
149 		switch (IFM_OPTIONS(ife->ifm_media)) {
150 		case 0:
151 			index = MII_MEDIA_10_T;
152 			break;
153 		case IFM_FDX:
154 		case (IFM_FDX | IFM_FLOW):
155 			index = MII_MEDIA_10_T_FDX;
156 			break;
157 		}
158 		break;
159 
160 	case IFM_100_TX:
161 	case IFM_100_FX:
162 		switch (IFM_OPTIONS(ife->ifm_media)) {
163 		case 0:
164 			index = MII_MEDIA_100_TX;
165 			break;
166 		case IFM_FDX:
167 		case (IFM_FDX | IFM_FLOW):
168 			index = MII_MEDIA_100_TX_FDX;
169 			break;
170 		}
171 		break;
172 
173 	case IFM_100_T4:
174 		index = MII_MEDIA_100_T4;
175 		break;
176 
177 	case IFM_1000_SX:
178 		switch (IFM_OPTIONS(ife->ifm_media)) {
179 		case 0:
180 			index = MII_MEDIA_1000_X;
181 			break;
182 		case IFM_FDX:
183 		case (IFM_FDX | IFM_FLOW):
184 			index = MII_MEDIA_1000_X_FDX;
185 			break;
186 		}
187 		break;
188 
189 	case IFM_1000_T:
190 		switch (IFM_OPTIONS(ife->ifm_media)) {
191 		case 0:
192 		case IFM_ETH_MASTER:
193 			index = MII_MEDIA_1000_T;
194 			break;
195 		case IFM_FDX:
196 		case (IFM_FDX | IFM_ETH_MASTER):
197 		case (IFM_FDX | IFM_FLOW):
198 		case (IFM_FDX | IFM_FLOW | IFM_ETH_MASTER):
199 			index = MII_MEDIA_1000_T_FDX;
200 			break;
201 		}
202 		break;
203 	}
204 
205 	KASSERT(index != -1, ("%s: failed to map media word %d",
206 	    __func__, ife->ifm_media));
207 
208 	anar = mii_media_table[index].mm_anar;
209 	bmcr = mii_media_table[index].mm_bmcr;
210 	gtcr = mii_media_table[index].mm_gtcr;
211 
212 	if (IFM_SUBTYPE(ife->ifm_media) == IFM_1000_T) {
213 		gtcr |= GTCR_MAN_MS;
214 		if ((ife->ifm_media & IFM_ETH_MASTER) != 0)
215 			gtcr |= GTCR_ADV_MS;
216 	}
217 
218 	if ((ife->ifm_media & IFM_FDX) != 0 &&
219 	    ((ife->ifm_media & IFM_FLOW) != 0 ||
220 	    (sc->mii_flags & MIIF_FORCEPAUSE) != 0)) {
221 		if ((sc->mii_flags & MIIF_IS_1000X) != 0)
222 			anar |= ANAR_X_PAUSE_TOWARDS;
223 		else {
224 			anar |= ANAR_FC;
225 			/* XXX Only 1000BASE-T has PAUSE_ASYM? */
226 			if ((sc->mii_flags & MIIF_HAVE_GTCR) != 0 &&
227 			    (sc->mii_extcapabilities &
228 			    (EXTSR_1000THDX | EXTSR_1000TFDX)) != 0)
229 				anar |= ANAR_X_PAUSE_ASYM;
230 		}
231 	}
232 
233 	PHY_WRITE(sc, MII_ANAR, anar);
234 	PHY_WRITE(sc, MII_BMCR, bmcr);
235 	if ((sc->mii_flags & MIIF_HAVE_GTCR) != 0)
236 		PHY_WRITE(sc, MII_100T2CR, gtcr);
237 }
238 
239 int
240 mii_phy_auto(struct mii_softc *sc)
241 {
242 	struct ifmedia_entry *ife = sc->mii_pdata->mii_media.ifm_cur;
243 	int anar, gtcr;
244 
245 	/*
246 	 * Check for 1000BASE-X.  Autonegotiation is a bit
247 	 * different on such devices.
248 	 */
249 	if ((sc->mii_flags & MIIF_IS_1000X) != 0) {
250 		anar = 0;
251 		if ((sc->mii_extcapabilities & EXTSR_1000XFDX) != 0)
252 			anar |= ANAR_X_FD;
253 		if ((sc->mii_extcapabilities & EXTSR_1000XHDX) != 0)
254 			anar |= ANAR_X_HD;
255 
256 		if ((ife->ifm_media & IFM_FLOW) != 0 ||
257 		    (sc->mii_flags & MIIF_FORCEPAUSE) != 0)
258 			anar |= ANAR_X_PAUSE_TOWARDS;
259 		PHY_WRITE(sc, MII_ANAR, anar);
260 	} else {
261 		anar = BMSR_MEDIA_TO_ANAR(sc->mii_capabilities) |
262 		    ANAR_CSMA;
263 		if ((ife->ifm_media & IFM_FLOW) != 0 ||
264 		    (sc->mii_flags & MIIF_FORCEPAUSE) != 0) {
265 			if ((sc->mii_capabilities &
266 			    (BMSR_10TFDX | BMSR_100TXFDX)) != 0)
267 				anar |= ANAR_FC;
268 			/* XXX Only 1000BASE-T has PAUSE_ASYM? */
269 			if (((sc->mii_flags & MIIF_HAVE_GTCR) != 0) &&
270 			    (sc->mii_extcapabilities &
271 			    (EXTSR_1000THDX | EXTSR_1000TFDX)) != 0)
272 				anar |= ANAR_X_PAUSE_ASYM;
273 		}
274 		PHY_WRITE(sc, MII_ANAR, anar);
275 		if ((sc->mii_flags & MIIF_HAVE_GTCR) != 0) {
276 			gtcr = 0;
277 			if ((sc->mii_extcapabilities & EXTSR_1000TFDX) != 0)
278 				gtcr |= GTCR_ADV_1000TFDX;
279 			if ((sc->mii_extcapabilities & EXTSR_1000THDX) != 0)
280 				gtcr |= GTCR_ADV_1000THDX;
281 			PHY_WRITE(sc, MII_100T2CR, gtcr);
282 		}
283 	}
284 	PHY_WRITE(sc, MII_BMCR, BMCR_AUTOEN | BMCR_STARTNEG);
285 	return (EJUSTRETURN);
286 }
287 
288 int
289 mii_phy_tick(struct mii_softc *sc)
290 {
291 	struct ifmedia_entry *ife = sc->mii_pdata->mii_media.ifm_cur;
292 	int reg;
293 
294 	/*
295 	 * If we're not doing autonegotiation, we don't need to do
296 	 * any extra work here.  However, we need to check the link
297 	 * status so we can generate an announcement if the status
298 	 * changes.
299 	 */
300 	if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) {
301 		sc->mii_ticks = 0;	/* reset autonegotiation timer. */
302 		return (0);
303 	}
304 
305 	/* Read the status register twice; BMSR_LINK is latch-low. */
306 	reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
307 	if ((reg & BMSR_LINK) != 0) {
308 		sc->mii_ticks = 0;	/* reset autonegotiation timer. */
309 		/* See above. */
310 		return (0);
311 	}
312 
313 	/* Announce link loss right after it happens */
314 	if (sc->mii_ticks++ == 0)
315 		return (0);
316 
317 	/* XXX: use default value if phy driver did not set mii_anegticks */
318 	if (sc->mii_anegticks == 0)
319 		sc->mii_anegticks = MII_ANEGTICKS_GIGE;
320 
321 	/* Only retry autonegotiation every mii_anegticks ticks. */
322 	if (sc->mii_ticks <= sc->mii_anegticks)
323 		return (EJUSTRETURN);
324 
325 	sc->mii_ticks = 0;
326 	PHY_RESET(sc);
327 	mii_phy_auto(sc);
328 	return (0);
329 }
330 
331 void
332 mii_phy_reset(struct mii_softc *sc)
333 {
334 	struct ifmedia_entry *ife = sc->mii_pdata->mii_media.ifm_cur;
335 	int i, reg;
336 
337 	if ((sc->mii_flags & MIIF_NOISOLATE) != 0)
338 		reg = BMCR_RESET;
339 	else
340 		reg = BMCR_RESET | BMCR_ISO;
341 	PHY_WRITE(sc, MII_BMCR, reg);
342 
343 	/* Wait 100ms for it to complete. */
344 	for (i = 0; i < 100; i++) {
345 		reg = PHY_READ(sc, MII_BMCR);
346 		if ((reg & BMCR_RESET) == 0)
347 			break;
348 		DELAY(1000);
349 	}
350 
351 	/* NB: a PHY may default to being powered down and/or isolated. */
352 	reg &= ~(BMCR_PDOWN | BMCR_ISO);
353 	if ((sc->mii_flags & MIIF_NOISOLATE) == 0 &&
354 	    ((ife == NULL && sc->mii_inst != 0) ||
355 	    (ife != NULL && IFM_INST(ife->ifm_media) != sc->mii_inst)))
356 		reg |= BMCR_ISO;
357 	if (PHY_READ(sc, MII_BMCR) != reg)
358 		PHY_WRITE(sc, MII_BMCR, reg);
359 }
360 
361 void
362 mii_phy_update(struct mii_softc *sc, int cmd)
363 {
364 	struct mii_data *mii = sc->mii_pdata;
365 
366 	if (sc->mii_media_active != mii->mii_media_active ||
367 	    cmd == MII_MEDIACHG) {
368 		MIIBUS_STATCHG(sc->mii_dev);
369 		sc->mii_media_active = mii->mii_media_active;
370 	}
371 	if (sc->mii_media_status != mii->mii_media_status) {
372 		MIIBUS_LINKCHG(sc->mii_dev);
373 		sc->mii_media_status = mii->mii_media_status;
374 	}
375 }
376 
377 /*
378  * Initialize generic PHY media based on BMSR, called when a PHY is
379  * attached.  We expect to be set up to print a comma-separated list
380  * of media names.  Does not print a newline.
381  */
382 void
383 mii_phy_add_media(struct mii_softc *sc)
384 {
385 	struct mii_data *mii = sc->mii_pdata;
386 	const char *sep = "";
387 	int fdx = 0;
388 
389 	if ((sc->mii_capabilities & BMSR_MEDIAMASK) == 0 &&
390 	    (sc->mii_extcapabilities & EXTSR_MEDIAMASK) == 0) {
391 		printf("no media present");
392 		return;
393 	}
394 
395 	/*
396 	 * Set the autonegotiation timer for 10/100 media.  Gigabit media is
397 	 * handled below.
398 	 */
399 	sc->mii_anegticks = MII_ANEGTICKS;
400 
401 #define	ADD(m)		ifmedia_add(&mii->mii_media, (m), 0, NULL)
402 #define	PRINT(s)	printf("%s%s", sep, s); sep = ", "
403 
404 	if ((sc->mii_flags & MIIF_NOISOLATE) == 0) {
405 		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_NONE, 0, sc->mii_inst));
406 		PRINT("none");
407 	}
408 
409 	/*
410 	 * There are different interpretations for the bits in
411 	 * HomePNA PHYs.  And there is really only one media type
412 	 * that is supported.
413 	 */
414 	if ((sc->mii_flags & MIIF_IS_HPNA) != 0) {
415 		if ((sc->mii_capabilities & BMSR_10THDX) != 0) {
416 			ADD(IFM_MAKEWORD(IFM_ETHER, IFM_HPNA_1, 0,
417 			    sc->mii_inst));
418 			PRINT("HomePNA1");
419 		}
420 		return;
421 	}
422 
423 	if ((sc->mii_capabilities & BMSR_10THDX) != 0) {
424 		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, 0, sc->mii_inst));
425 		PRINT("10baseT");
426 	}
427 	if ((sc->mii_capabilities & BMSR_10TFDX) != 0) {
428 		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, IFM_FDX, sc->mii_inst));
429 		PRINT("10baseT-FDX");
430 		if ((sc->mii_flags & MIIF_DOPAUSE) != 0 &&
431 		    (sc->mii_flags & MIIF_NOMANPAUSE) == 0) {
432 			ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T,
433 			    IFM_FDX | IFM_FLOW, sc->mii_inst));
434 			PRINT("10baseT-FDX-flow");
435 		}
436 		fdx = 1;
437 	}
438 	if ((sc->mii_capabilities & BMSR_100TXHDX) != 0) {
439 		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, 0, sc->mii_inst));
440 		PRINT("100baseTX");
441 	}
442 	if ((sc->mii_capabilities & BMSR_100TXFDX) != 0) {
443 		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_FDX, sc->mii_inst));
444 		PRINT("100baseTX-FDX");
445 		if ((sc->mii_flags & MIIF_DOPAUSE) != 0 &&
446 		    (sc->mii_flags & MIIF_NOMANPAUSE) == 0) {
447 			ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX,
448 			    IFM_FDX | IFM_FLOW, sc->mii_inst));
449 			PRINT("100baseTX-FDX-flow");
450 		}
451 		fdx = 1;
452 	}
453 	if ((sc->mii_capabilities & BMSR_100T4) != 0) {
454 		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_T4, 0, sc->mii_inst));
455 		PRINT("100baseT4");
456 	}
457 
458 	if ((sc->mii_extcapabilities & EXTSR_MEDIAMASK) != 0) {
459 		/*
460 		 * XXX Right now only handle 1000SX and 1000TX.  Need
461 		 * XXX to handle 1000LX and 1000CX somehow.
462 		 */
463 		if ((sc->mii_extcapabilities & EXTSR_1000XHDX) != 0) {
464 			sc->mii_anegticks = MII_ANEGTICKS_GIGE;
465 			sc->mii_flags |= MIIF_IS_1000X;
466 			ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_SX, 0,
467 			    sc->mii_inst));
468 			PRINT("1000baseSX");
469 		}
470 		if ((sc->mii_extcapabilities & EXTSR_1000XFDX) != 0) {
471 			sc->mii_anegticks = MII_ANEGTICKS_GIGE;
472 			sc->mii_flags |= MIIF_IS_1000X;
473 			ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_SX, IFM_FDX,
474 			    sc->mii_inst));
475 			PRINT("1000baseSX-FDX");
476 			if ((sc->mii_flags & MIIF_DOPAUSE) != 0 &&
477 			    (sc->mii_flags & MIIF_NOMANPAUSE) == 0) {
478 				ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_SX,
479 				    IFM_FDX | IFM_FLOW, sc->mii_inst));
480 				PRINT("1000baseSX-FDX-flow");
481 			}
482 			fdx = 1;
483 		}
484 
485 		/*
486 		 * 1000baseT media needs to be able to manipulate
487 		 * master/slave mode.
488 		 *
489 		 * All 1000baseT PHYs have a 1000baseT control register.
490 		 */
491 		if ((sc->mii_extcapabilities & EXTSR_1000THDX) != 0) {
492 			sc->mii_anegticks = MII_ANEGTICKS_GIGE;
493 			sc->mii_flags |= MIIF_HAVE_GTCR;
494 			ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, 0,
495 			    sc->mii_inst));
496 			PRINT("1000baseT");
497 			ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T,
498 			    IFM_ETH_MASTER, sc->mii_inst));
499 			PRINT("1000baseT-master");
500 		}
501 		if ((sc->mii_extcapabilities & EXTSR_1000TFDX) != 0) {
502 			sc->mii_anegticks = MII_ANEGTICKS_GIGE;
503 			sc->mii_flags |= MIIF_HAVE_GTCR;
504 			ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, IFM_FDX,
505 			    sc->mii_inst));
506 			PRINT("1000baseT-FDX");
507 			ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T,
508 			    IFM_FDX | IFM_ETH_MASTER, sc->mii_inst));
509 			PRINT("1000baseT-FDX-master");
510 			if ((sc->mii_flags & MIIF_DOPAUSE) != 0 &&
511 			    (sc->mii_flags & MIIF_NOMANPAUSE) == 0) {
512 				ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T,
513 				    IFM_FDX | IFM_FLOW, sc->mii_inst));
514 				PRINT("1000baseT-FDX-flow");
515 				ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T,
516 				    IFM_FDX | IFM_FLOW | IFM_ETH_MASTER,
517 				    sc->mii_inst));
518 				PRINT("1000baseT-FDX-flow-master");
519 			}
520 			fdx = 1;
521 		}
522 	}
523 
524 	if ((sc->mii_capabilities & BMSR_ANEG) != 0) {
525 		/* intentionally invalid index */
526 		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, sc->mii_inst));
527 		PRINT("auto");
528 		if (fdx != 0 && (sc->mii_flags & MIIF_DOPAUSE) != 0) {
529 			ADD(IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, IFM_FLOW,
530 			    sc->mii_inst));
531 			PRINT("auto-flow");
532 		}
533 	}
534 #undef ADD
535 #undef PRINT
536 }
537 
538 int
539 mii_phy_detach(device_t dev)
540 {
541 	struct mii_softc *sc;
542 
543 	sc = device_get_softc(dev);
544 	sc->mii_dev = NULL;
545 	LIST_REMOVE(sc, mii_list);
546 	return (0);
547 }
548 
549 const struct mii_phydesc *
550 mii_phy_match_gen(const struct mii_attach_args *ma,
551   const struct mii_phydesc *mpd, size_t len)
552 {
553 
554 	for (; mpd->mpd_name != NULL;
555 	    mpd = (const struct mii_phydesc *)((const char *)mpd + len)) {
556 		if (MII_OUI(ma->mii_id1, ma->mii_id2) == mpd->mpd_oui &&
557 		    MII_MODEL(ma->mii_id2) == mpd->mpd_model)
558 			return (mpd);
559 	}
560 	return (NULL);
561 }
562 
563 const struct mii_phydesc *
564 mii_phy_match(const struct mii_attach_args *ma, const struct mii_phydesc *mpd)
565 {
566 
567 	return (mii_phy_match_gen(ma, mpd, sizeof(struct mii_phydesc)));
568 }
569 
570 int
571 mii_phy_dev_probe(device_t dev, const struct mii_phydesc *mpd, int mrv)
572 {
573 
574 	mpd = mii_phy_match(device_get_ivars(dev), mpd);
575 	if (mpd != NULL) {
576 		device_set_desc(dev, mpd->mpd_name);
577 		return (mrv);
578 	}
579 	return (ENXIO);
580 }
581 
582 void
583 mii_phy_dev_attach(device_t dev, u_int flags, const struct mii_phy_funcs *mpf,
584     int add_media)
585 {
586 	struct mii_softc *sc;
587 	struct mii_attach_args *ma;
588 	struct mii_data *mii;
589 
590 	sc = device_get_softc(dev);
591 	ma = device_get_ivars(dev);
592 	sc->mii_dev = device_get_parent(dev);
593 	mii = ma->mii_data;
594 	LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list);
595 
596 	sc->mii_flags = flags | miibus_get_flags(dev);
597 	sc->mii_mpd_oui = MII_OUI(ma->mii_id1, ma->mii_id2);
598 	sc->mii_mpd_model = MII_MODEL(ma->mii_id2);
599 	sc->mii_mpd_rev = MII_REV(ma->mii_id2);
600 	sc->mii_capmask = ma->mii_capmask;
601 	sc->mii_inst = mii->mii_instance++;
602 	sc->mii_phy = ma->mii_phyno;
603 	sc->mii_offset = ma->mii_offset;
604 	sc->mii_funcs = mpf;
605 	sc->mii_pdata = mii;
606 
607 	if (bootverbose)
608 		device_printf(dev, "OUI 0x%06x, model 0x%04x, rev. %d\n",
609 		    sc->mii_mpd_oui, sc->mii_mpd_model, sc->mii_mpd_rev);
610 
611 	if (add_media == 0)
612 		return;
613 
614 	PHY_RESET(sc);
615 
616 	sc->mii_capabilities = PHY_READ(sc, MII_BMSR) & sc->mii_capmask;
617 	if (sc->mii_capabilities & BMSR_EXTSTAT)
618 		sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR);
619 	device_printf(dev, " ");
620 	mii_phy_add_media(sc);
621 	printf("\n");
622 
623 	MIIBUS_MEDIAINIT(sc->mii_dev);
624 }
625 
626 /*
627  * Return the flow control status flag from MII_ANAR & MII_ANLPAR.
628  */
629 u_int
630 mii_phy_flowstatus(struct mii_softc *sc)
631 {
632 	int anar, anlpar;
633 
634 	if ((sc->mii_flags & MIIF_DOPAUSE) == 0)
635 		return (0);
636 
637 	anar = PHY_READ(sc, MII_ANAR);
638 	anlpar = PHY_READ(sc, MII_ANLPAR);
639 
640 	/*
641 	 * Check for 1000BASE-X.  Autonegotiation is a bit
642 	 * different on such devices.
643 	 */
644 	if ((sc->mii_flags & MIIF_IS_1000X) != 0) {
645 		anar <<= 3;
646 		anlpar <<= 3;
647 	}
648 
649 	if ((anar & ANAR_PAUSE_SYM) != 0 && (anlpar & ANLPAR_PAUSE_SYM) != 0)
650 		return (IFM_FLOW | IFM_ETH_TXPAUSE | IFM_ETH_RXPAUSE);
651 
652 	if ((anar & ANAR_PAUSE_SYM) == 0) {
653 		if ((anar & ANAR_PAUSE_ASYM) != 0 &&
654 		    (anlpar & ANLPAR_PAUSE_TOWARDS) != 0)
655 			return (IFM_FLOW | IFM_ETH_TXPAUSE);
656 		else
657 			return (0);
658 	}
659 
660 	if ((anar & ANAR_PAUSE_ASYM) == 0) {
661 		if ((anlpar & ANLPAR_PAUSE_SYM) != 0)
662 			return (IFM_FLOW | IFM_ETH_TXPAUSE | IFM_ETH_RXPAUSE);
663 		else
664 			return (0);
665 	}
666 
667 	switch ((anlpar & ANLPAR_PAUSE_TOWARDS)) {
668 	case ANLPAR_PAUSE_NONE:
669 		return (0);
670 	case ANLPAR_PAUSE_ASYM:
671 		return (IFM_FLOW | IFM_ETH_RXPAUSE);
672 	default:
673 		return (IFM_FLOW | IFM_ETH_RXPAUSE | IFM_ETH_TXPAUSE);
674 	}
675 	/* NOTREACHED */
676 }
677