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