mii_physubr.c revision 217414
150120Swpaul/*	$NetBSD: mii_physubr.c,v 1.5 1999/08/03 19:41:49 drochner Exp $	*/
250120Swpaul
350120Swpaul/*-
495718Sphk * Copyright (c) 1998, 1999, 2000, 2001 The NetBSD Foundation, Inc.
550120Swpaul * All rights reserved.
650120Swpaul *
750120Swpaul * This code is derived from software contributed to The NetBSD Foundation
850120Swpaul * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
950120Swpaul * NASA Ames Research Center.
1050120Swpaul *
1150120Swpaul * Redistribution and use in source and binary forms, with or without
1250120Swpaul * modification, are permitted provided that the following conditions
1350120Swpaul * are met:
1450120Swpaul * 1. Redistributions of source code must retain the above copyright
1550120Swpaul *    notice, this list of conditions and the following disclaimer.
1650120Swpaul * 2. Redistributions in binary form must reproduce the above copyright
1750120Swpaul *    notice, this list of conditions and the following disclaimer in the
1850120Swpaul *    documentation and/or other materials provided with the distribution.
1950120Swpaul *
2050120Swpaul * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
2150120Swpaul * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2250120Swpaul * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2350120Swpaul * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2450120Swpaul * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2550120Swpaul * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2650120Swpaul * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2750120Swpaul * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2850120Swpaul * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2950120Swpaul * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3050120Swpaul * POSSIBILITY OF SUCH DAMAGE.
3150120Swpaul */
3250120Swpaul
33119418Sobrien#include <sys/cdefs.h>
34119418Sobrien__FBSDID("$FreeBSD: head/sys/dev/mii/mii_physubr.c 217414 2011-01-14 19:33:58Z marius $");
35119418Sobrien
3650120Swpaul/*
3750120Swpaul * Subroutines common to all PHYs.
3850120Swpaul */
3950120Swpaul
4050120Swpaul#include <sys/param.h>
4150120Swpaul#include <sys/systm.h>
4250120Swpaul#include <sys/kernel.h>
4350120Swpaul#include <sys/socket.h>
4450120Swpaul#include <sys/errno.h>
4550120Swpaul#include <sys/module.h>
4650120Swpaul#include <sys/bus.h>
4750120Swpaul
4850120Swpaul#include <net/if.h>
4950120Swpaul#include <net/if_media.h>
5050120Swpaul
5150120Swpaul#include <dev/mii/mii.h>
5250120Swpaul#include <dev/mii/miivar.h>
5350120Swpaul
5450120Swpaul#include "miibus_if.h"
5550120Swpaul
5695707Sphk/*
5795707Sphk * Media to register setting conversion table.  Order matters.
5895707Sphk */
59214265Smariusstatic const struct mii_media mii_media_table[MII_NMEDIA] = {
6095707Sphk	/* None */
6195707Sphk	{ BMCR_ISO,		ANAR_CSMA,
6295707Sphk	  0, },
6395707Sphk
6495707Sphk	/* 10baseT */
6595707Sphk	{ BMCR_S10,		ANAR_CSMA|ANAR_10,
6695707Sphk	  0, },
6795707Sphk
6895707Sphk	/* 10baseT-FDX */
6995707Sphk	{ BMCR_S10|BMCR_FDX,	ANAR_CSMA|ANAR_10_FD,
7095707Sphk	  0, },
7195707Sphk
7295707Sphk	/* 100baseT4 */
7395707Sphk	{ BMCR_S100,		ANAR_CSMA|ANAR_T4,
7495707Sphk	  0, },
7595707Sphk
7695707Sphk	/* 100baseTX */
7795707Sphk	{ BMCR_S100,		ANAR_CSMA|ANAR_TX,
7895707Sphk	  0, },
7995707Sphk
8095707Sphk	/* 100baseTX-FDX */
8195707Sphk	{ BMCR_S100|BMCR_FDX,	ANAR_CSMA|ANAR_TX_FD,
8295707Sphk	  0, },
8395707Sphk
8495707Sphk	/* 1000baseX */
8595707Sphk	{ BMCR_S1000,		ANAR_CSMA,
8695707Sphk	  0, },
8795707Sphk
8895707Sphk	/* 1000baseX-FDX */
8995707Sphk	{ BMCR_S1000|BMCR_FDX,	ANAR_CSMA,
9095707Sphk	  0, },
9195707Sphk
9295707Sphk	/* 1000baseT */
9395707Sphk	{ BMCR_S1000,		ANAR_CSMA,
9495707Sphk	  GTCR_ADV_1000THDX },
9595707Sphk
9695707Sphk	/* 1000baseT-FDX */
9795707Sphk	{ BMCR_S1000,		ANAR_CSMA,
9895707Sphk	  GTCR_ADV_1000TFDX },
9995707Sphk};
10095707Sphk
10195707Sphkvoid
10295707Sphkmii_phy_setmedia(struct mii_softc *sc)
10395707Sphk{
10495707Sphk	struct mii_data *mii = sc->mii_pdata;
10595707Sphk	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
10695707Sphk	int bmcr, anar, gtcr;
10795707Sphk
10895707Sphk	if (IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO) {
109215297Smarius		/*
110215297Smarius		 * Force renegotiation if MIIF_DOPAUSE or MIIF_FORCEANEG.
111215297Smarius		 * The former is necessary as we might switch from flow-
112215297Smarius		 * control advertisment being off to on or vice versa.
113215297Smarius		 */
114164702Smarius		if ((PHY_READ(sc, MII_BMCR) & BMCR_AUTOEN) == 0 ||
115215297Smarius		    (sc->mii_flags & (MIIF_DOPAUSE | MIIF_FORCEANEG)) != 0)
116214606Smarius			(void)mii_phy_auto(sc);
11795707Sphk		return;
11895707Sphk	}
11995707Sphk
12095707Sphk	/*
12195707Sphk	 * Table index is stored in the media entry.
12295707Sphk	 */
12395707Sphk
12495718Sphk	KASSERT(ife->ifm_data >=0 && ife->ifm_data < MII_NMEDIA,
12595718Sphk	    ("invalid ife->ifm_data (0x%x) in mii_phy_setmedia",
12695718Sphk	    ife->ifm_data));
12795707Sphk
12895707Sphk	anar = mii_media_table[ife->ifm_data].mm_anar;
12995707Sphk	bmcr = mii_media_table[ife->ifm_data].mm_bmcr;
13095707Sphk	gtcr = mii_media_table[ife->ifm_data].mm_gtcr;
13195707Sphk
132215297Smarius	if (IFM_SUBTYPE(ife->ifm_media) == IFM_1000_T) {
133215297Smarius		gtcr |= GTCR_MAN_MS;
134215297Smarius		if ((ife->ifm_media & IFM_ETH_MASTER) != 0)
135215297Smarius			gtcr |= GTCR_ADV_MS;
136215297Smarius	}
13795707Sphk
138217414Smarius	if ((ife->ifm_media & IFM_FDX) != 0 &&
139217414Smarius	    ((ife->ifm_media & IFM_FLOW) != 0 ||
140217414Smarius	    (sc->mii_flags & MIIF_FORCEPAUSE) != 0)) {
141215297Smarius		if ((sc->mii_flags & MIIF_IS_1000X) != 0)
142215297Smarius			anar |= ANAR_X_PAUSE_TOWARDS;
143215297Smarius		else {
144215297Smarius			anar |= ANAR_FC;
145215297Smarius			/* XXX Only 1000BASE-T has PAUSE_ASYM? */
146215297Smarius			if ((sc->mii_flags & MIIF_HAVE_GTCR) != 0 &&
147215297Smarius			    (sc->mii_extcapabilities &
148215297Smarius			    (EXTSR_1000THDX | EXTSR_1000TFDX)) != 0)
149215297Smarius				anar |= ANAR_X_PAUSE_ASYM;
15095707Sphk		}
15195707Sphk	}
15295707Sphk
153214606Smarius	if ((ife->ifm_media & IFM_LOOP) != 0)
15495707Sphk		bmcr |= BMCR_LOOP;
15595707Sphk
15695707Sphk	PHY_WRITE(sc, MII_ANAR, anar);
15795707Sphk	PHY_WRITE(sc, MII_BMCR, bmcr);
158214606Smarius	if ((sc->mii_flags & MIIF_HAVE_GTCR) != 0)
15995707Sphk		PHY_WRITE(sc, MII_100T2CR, gtcr);
16095707Sphk}
16195707Sphk
16250120Swpaulint
16396026Sphkmii_phy_auto(struct mii_softc *sc)
16450120Swpaul{
165215297Smarius	struct ifmedia_entry *ife = sc->mii_pdata->mii_media.ifm_cur;
166214606Smarius	int anar, gtcr;
16750120Swpaul
16896026Sphk	/*
16996026Sphk	 * Check for 1000BASE-X.  Autonegotiation is a bit
17096026Sphk	 * different on such devices.
17196026Sphk	 */
172214606Smarius	if ((sc->mii_flags & MIIF_IS_1000X) != 0) {
173214606Smarius		anar = 0;
174214606Smarius		if ((sc->mii_extcapabilities & EXTSR_1000XFDX) != 0)
17596026Sphk			anar |= ANAR_X_FD;
176214606Smarius		if ((sc->mii_extcapabilities & EXTSR_1000XHDX) != 0)
17796026Sphk			anar |= ANAR_X_HD;
17895718Sphk
179215297Smarius		if ((ife->ifm_media & IFM_FLOW) != 0 ||
180215297Smarius		    (sc->mii_flags & MIIF_FORCEPAUSE) != 0)
181215297Smarius			anar |= ANAR_X_PAUSE_TOWARDS;
18296026Sphk		PHY_WRITE(sc, MII_ANAR, anar);
18396026Sphk	} else {
18496026Sphk		anar = BMSR_MEDIA_TO_ANAR(sc->mii_capabilities) |
18596026Sphk		    ANAR_CSMA;
186215297Smarius		if ((ife->ifm_media & IFM_FLOW) != 0 ||
187215297Smarius		    (sc->mii_flags & MIIF_FORCEPAUSE) != 0) {
188217414Smarius			if ((sc->mii_capabilities &
189217414Smarius			    (BMSR_10TFDX | BMSR_100TXFDX)) != 0)
190215297Smarius				anar |= ANAR_FC;
191215297Smarius			/* XXX Only 1000BASE-T has PAUSE_ASYM? */
192215297Smarius			if (((sc->mii_flags & MIIF_HAVE_GTCR) != 0) &&
193215297Smarius			    (sc->mii_extcapabilities &
194215297Smarius			    (EXTSR_1000THDX | EXTSR_1000TFDX)) != 0)
195215297Smarius				anar |= ANAR_X_PAUSE_ASYM;
196215297Smarius		}
19796026Sphk		PHY_WRITE(sc, MII_ANAR, anar);
198214606Smarius		if ((sc->mii_flags & MIIF_HAVE_GTCR) != 0) {
199214606Smarius			gtcr = 0;
200214606Smarius			if ((sc->mii_extcapabilities & EXTSR_1000TFDX) != 0)
20196026Sphk				gtcr |= GTCR_ADV_1000TFDX;
202214606Smarius			if ((sc->mii_extcapabilities & EXTSR_1000THDX) != 0)
20396026Sphk				gtcr |= GTCR_ADV_1000THDX;
20496026Sphk			PHY_WRITE(sc, MII_100T2CR, gtcr);
20595718Sphk		}
20650120Swpaul	}
20796026Sphk	PHY_WRITE(sc, MII_BMCR, BMCR_AUTOEN | BMCR_STARTNEG);
20850120Swpaul	return (EJUSTRETURN);
20950120Swpaul}
21050120Swpaul
21184140Sjlemonint
21295718Sphkmii_phy_tick(struct mii_softc *sc)
21384140Sjlemon{
21484140Sjlemon	struct ifmedia_entry *ife = sc->mii_pdata->mii_media.ifm_cur;
21584140Sjlemon	struct ifnet *ifp = sc->mii_pdata->mii_ifp;
21684140Sjlemon	int reg;
21784140Sjlemon
21895718Sphk	/* Just bail now if the interface is down. */
21984140Sjlemon	if ((ifp->if_flags & IFF_UP) == 0)
22084140Sjlemon		return (EJUSTRETURN);
22184140Sjlemon
22284140Sjlemon	/*
22384140Sjlemon	 * If we're not doing autonegotiation, we don't need to do
22484140Sjlemon	 * any extra work here.  However, we need to check the link
22584140Sjlemon	 * status so we can generate an announcement if the status
22684140Sjlemon	 * changes.
22784140Sjlemon	 */
228160082Soleg	if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) {
229160082Soleg		sc->mii_ticks = 0;	/* reset autonegotiation timer. */
23084140Sjlemon		return (0);
231160082Soleg	}
23284140Sjlemon
23395718Sphk	/* Read the status register twice; BMSR_LINK is latch-low. */
23484140Sjlemon	reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
235214606Smarius	if ((reg & BMSR_LINK) != 0) {
236158649Soleg		sc->mii_ticks = 0;	/* reset autonegotiation timer. */
237158649Soleg		/* See above. */
23884140Sjlemon		return (0);
23995718Sphk	}
24084140Sjlemon
241158649Soleg	/* Announce link loss right after it happens */
242158649Soleg	if (sc->mii_ticks++ == 0)
243128870Sandre		return (0);
244158649Soleg
245158649Soleg	/* XXX: use default value if phy driver did not set mii_anegticks */
246158649Soleg	if (sc->mii_anegticks == 0)
247158649Soleg		sc->mii_anegticks = MII_ANEGTICKS_GIGE;
248158649Soleg
249158649Soleg	/* Only retry autonegotiation every mii_anegticks ticks. */
250158649Soleg	if (sc->mii_ticks <= sc->mii_anegticks)
25184140Sjlemon		return (EJUSTRETURN);
25284140Sjlemon
25384140Sjlemon	sc->mii_ticks = 0;
25484140Sjlemon	mii_phy_reset(sc);
25596026Sphk	mii_phy_auto(sc);
25684140Sjlemon	return (0);
25784140Sjlemon}
25884140Sjlemon
25950120Swpaulvoid
26095718Sphkmii_phy_reset(struct mii_softc *sc)
26150120Swpaul{
262164702Smarius	struct ifmedia_entry *ife = sc->mii_pdata->mii_media.ifm_cur;
26350120Swpaul	int reg, i;
26450120Swpaul
265214606Smarius	if ((sc->mii_flags & MIIF_NOISOLATE) != 0)
26650120Swpaul		reg = BMCR_RESET;
26750120Swpaul	else
26850120Swpaul		reg = BMCR_RESET | BMCR_ISO;
26995718Sphk	PHY_WRITE(sc, MII_BMCR, reg);
27050120Swpaul
27150120Swpaul	/* Wait 100ms for it to complete. */
27250120Swpaul	for (i = 0; i < 100; i++) {
273164702Smarius		reg = PHY_READ(sc, MII_BMCR);
27450120Swpaul		if ((reg & BMCR_RESET) == 0)
27550120Swpaul			break;
27650120Swpaul		DELAY(1000);
27750120Swpaul	}
27850120Swpaul
279164702Smarius	if ((sc->mii_flags & MIIF_NOISOLATE) == 0) {
280164702Smarius		if ((ife == NULL && sc->mii_inst != 0) ||
281164702Smarius		    (ife != NULL && IFM_INST(ife->ifm_media) != sc->mii_inst))
282164702Smarius			PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO);
283164702Smarius	}
28450120Swpaul}
28550120Swpaul
28684140Sjlemonvoid
28795722Sphkmii_phy_down(struct mii_softc *sc)
28884140Sjlemon{
28995722Sphk
29095722Sphk}
29195722Sphk
29295722Sphkvoid
29395722Sphkmii_phy_update(struct mii_softc *sc, int cmd)
29495722Sphk{
29584140Sjlemon	struct mii_data *mii = sc->mii_pdata;
29684140Sjlemon
29795722Sphk	if (sc->mii_media_active != mii->mii_media_active ||
29895722Sphk	    cmd == MII_MEDIACHG) {
29984140Sjlemon		MIIBUS_STATCHG(sc->mii_dev);
30095705Sphk		sc->mii_media_active = mii->mii_media_active;
30184140Sjlemon	}
30295705Sphk	if (sc->mii_media_status != mii->mii_media_status) {
30384140Sjlemon		MIIBUS_LINKCHG(sc->mii_dev);
30495705Sphk		sc->mii_media_status = mii->mii_media_status;
30584140Sjlemon	}
30684140Sjlemon}
30784140Sjlemon
30850120Swpaul/*
30950120Swpaul * Initialize generic PHY media based on BMSR, called when a PHY is
31050120Swpaul * attached.  We expect to be set up to print a comma-separated list
31150120Swpaul * of media names.  Does not print a newline.
31250120Swpaul */
31350120Swpaulvoid
31495718Sphkmii_phy_add_media(struct mii_softc *sc)
31595718Sphk{
31695718Sphk	struct mii_data *mii = sc->mii_pdata;
31795718Sphk	const char *sep = "";
318215297Smarius	int fdx = 0;
31995718Sphk
320164703Smarius	if ((sc->mii_capabilities & BMSR_MEDIAMASK) == 0 &&
321164703Smarius	    (sc->mii_extcapabilities & EXTSR_MEDIAMASK) == 0) {
322164703Smarius		printf("no media present");
323164703Smarius		return;
324164703Smarius	}
325164703Smarius
326214606Smarius	/*
327214606Smarius	 * Set the autonegotiation timer for 10/100 media.  Gigabit media is
328214606Smarius	 * handled below.
329214606Smarius	 */
330158649Soleg	sc->mii_anegticks = MII_ANEGTICKS;
331158649Soleg
33295718Sphk#define	ADD(m, c)	ifmedia_add(&mii->mii_media, (m), (c), NULL)
33395718Sphk#define	PRINT(s)	printf("%s%s", sep, s); sep = ", "
33495718Sphk
33595718Sphk	if ((sc->mii_flags & MIIF_NOISOLATE) == 0)
33695718Sphk		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_NONE, 0, sc->mii_inst),
33795718Sphk		    MII_MEDIA_NONE);
33895718Sphk
33995718Sphk	/*
34095718Sphk	 * There are different interpretations for the bits in
34195718Sphk	 * HomePNA PHYs.  And there is really only one media type
34295718Sphk	 * that is supported.
34395718Sphk	 */
344214606Smarius	if ((sc->mii_flags & MIIF_IS_HPNA) != 0) {
345214606Smarius		if ((sc->mii_capabilities & BMSR_10THDX) != 0) {
34695718Sphk			ADD(IFM_MAKEWORD(IFM_ETHER, IFM_HPNA_1, 0,
347214606Smarius			    sc->mii_inst), MII_MEDIA_10_T);
34895718Sphk			PRINT("HomePNA1");
34995718Sphk		}
35095718Sphk		return;
35195718Sphk	}
35295718Sphk
353214606Smarius	if ((sc->mii_capabilities & BMSR_10THDX) != 0) {
35495718Sphk		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, 0, sc->mii_inst),
35595718Sphk		    MII_MEDIA_10_T);
35695718Sphk		PRINT("10baseT");
35795718Sphk	}
358214606Smarius	if ((sc->mii_capabilities & BMSR_10TFDX) != 0) {
35995718Sphk		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, IFM_FDX, sc->mii_inst),
36095718Sphk		    MII_MEDIA_10_T_FDX);
36195718Sphk		PRINT("10baseT-FDX");
362215297Smarius		if ((sc->mii_flags & MIIF_DOPAUSE) != 0 &&
363215297Smarius		    (sc->mii_flags & MIIF_NOMANPAUSE) == 0) {
364215297Smarius			ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T,
365215297Smarius			    IFM_FDX | IFM_FLOW, sc->mii_inst),
366215297Smarius			    MII_MEDIA_10_T_FDX);
367215297Smarius			PRINT("10baseT-FDX-flow");
368215297Smarius		}
369215297Smarius		fdx = 1;
37095718Sphk	}
371214606Smarius	if ((sc->mii_capabilities & BMSR_100TXHDX) != 0) {
37295718Sphk		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, 0, sc->mii_inst),
37395718Sphk		    MII_MEDIA_100_TX);
37495718Sphk		PRINT("100baseTX");
37595718Sphk	}
376214606Smarius	if ((sc->mii_capabilities & BMSR_100TXFDX) != 0) {
37795718Sphk		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_FDX, sc->mii_inst),
37895718Sphk		    MII_MEDIA_100_TX_FDX);
37995718Sphk		PRINT("100baseTX-FDX");
380215297Smarius		if ((sc->mii_flags & MIIF_DOPAUSE) != 0 &&
381215297Smarius		    (sc->mii_flags & MIIF_NOMANPAUSE) == 0) {
382215297Smarius			ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX,
383215297Smarius			    IFM_FDX | IFM_FLOW, sc->mii_inst),
384215297Smarius			    MII_MEDIA_100_TX_FDX);
385215297Smarius			PRINT("100baseTX-FDX-flow");
386215297Smarius		}
387215297Smarius		fdx = 1;
38895718Sphk	}
389214606Smarius	if ((sc->mii_capabilities & BMSR_100T4) != 0) {
39095718Sphk		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_T4, 0, sc->mii_inst),
39195718Sphk		    MII_MEDIA_100_T4);
39295718Sphk		PRINT("100baseT4");
39395718Sphk	}
39495718Sphk
395214606Smarius	if ((sc->mii_extcapabilities & EXTSR_MEDIAMASK) != 0) {
39695718Sphk		/*
39795718Sphk		 * XXX Right now only handle 1000SX and 1000TX.  Need
398214606Smarius		 * XXX to handle 1000LX and 1000CX somehow.
39995718Sphk		 */
400214606Smarius		if ((sc->mii_extcapabilities & EXTSR_1000XHDX) != 0) {
401158649Soleg			sc->mii_anegticks = MII_ANEGTICKS_GIGE;
40295718Sphk			sc->mii_flags |= MIIF_IS_1000X;
40395718Sphk			ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_SX, 0,
40495718Sphk			    sc->mii_inst), MII_MEDIA_1000_X);
40595718Sphk			PRINT("1000baseSX");
40695718Sphk		}
407214606Smarius		if ((sc->mii_extcapabilities & EXTSR_1000XFDX) != 0) {
408158649Soleg			sc->mii_anegticks = MII_ANEGTICKS_GIGE;
40995718Sphk			sc->mii_flags |= MIIF_IS_1000X;
41095718Sphk			ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_SX, IFM_FDX,
41195718Sphk			    sc->mii_inst), MII_MEDIA_1000_X_FDX);
41295718Sphk			PRINT("1000baseSX-FDX");
413215297Smarius			if ((sc->mii_flags & MIIF_DOPAUSE) != 0 &&
414215297Smarius			    (sc->mii_flags & MIIF_NOMANPAUSE) == 0) {
415215297Smarius				ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_SX,
416215297Smarius				    IFM_FDX | IFM_FLOW, sc->mii_inst),
417215297Smarius				    MII_MEDIA_1000_X_FDX);
418215297Smarius				PRINT("1000baseSX-FDX-flow");
419215297Smarius			}
420215297Smarius			fdx = 1;
42195718Sphk		}
42295718Sphk
42395718Sphk		/*
42495718Sphk		 * 1000baseT media needs to be able to manipulate
425215297Smarius		 * master/slave mode.
42695718Sphk		 *
42795718Sphk		 * All 1000baseT PHYs have a 1000baseT control register.
42895718Sphk		 */
429214606Smarius		if ((sc->mii_extcapabilities & EXTSR_1000THDX) != 0) {
430158649Soleg			sc->mii_anegticks = MII_ANEGTICKS_GIGE;
43195718Sphk			sc->mii_flags |= MIIF_HAVE_GTCR;
43295718Sphk			ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, 0,
43395718Sphk			    sc->mii_inst), MII_MEDIA_1000_T);
43495718Sphk			PRINT("1000baseT");
435215297Smarius			ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T,
436215297Smarius			    IFM_ETH_MASTER, sc->mii_inst), MII_MEDIA_1000_T);
437215297Smarius			PRINT("1000baseT-master");
43895718Sphk		}
439214606Smarius		if ((sc->mii_extcapabilities & EXTSR_1000TFDX) != 0) {
440158649Soleg			sc->mii_anegticks = MII_ANEGTICKS_GIGE;
44195718Sphk			sc->mii_flags |= MIIF_HAVE_GTCR;
44295718Sphk			ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, IFM_FDX,
44395718Sphk			    sc->mii_inst), MII_MEDIA_1000_T_FDX);
44495718Sphk			PRINT("1000baseT-FDX");
445215297Smarius			ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T,
446215297Smarius			    IFM_FDX | IFM_ETH_MASTER, sc->mii_inst),
447215297Smarius			    MII_MEDIA_1000_T_FDX);
448215297Smarius			PRINT("1000baseT-FDX-master");
449215297Smarius			if ((sc->mii_flags & MIIF_DOPAUSE) != 0 &&
450215297Smarius			    (sc->mii_flags & MIIF_NOMANPAUSE) == 0) {
451215297Smarius				ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T,
452215297Smarius				    IFM_FDX | IFM_FLOW, sc->mii_inst),
453215297Smarius				    MII_MEDIA_1000_T_FDX);
454215297Smarius				PRINT("1000baseT-FDX-flow");
455215297Smarius				ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T,
456215297Smarius				    IFM_FDX | IFM_FLOW | IFM_ETH_MASTER,
457215297Smarius				    sc->mii_inst), MII_MEDIA_1000_T_FDX);
458215297Smarius				PRINT("1000baseT-FDX-flow-master");
459215297Smarius			}
460215297Smarius			fdx = 1;
46195718Sphk		}
46295718Sphk	}
46395718Sphk
464214606Smarius	if ((sc->mii_capabilities & BMSR_ANEG) != 0) {
465215297Smarius		/* intentionally invalid index */
46695718Sphk		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, sc->mii_inst),
467215297Smarius		    MII_NMEDIA);
46895718Sphk		PRINT("auto");
469215297Smarius		if (fdx != 0 && (sc->mii_flags & MIIF_DOPAUSE) != 0) {
470215297Smarius			ADD(IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, IFM_FLOW,
471215297Smarius			    sc->mii_inst), MII_NMEDIA);
472215297Smarius			PRINT("auto-flow");
473215297Smarius		}
47495718Sphk	}
47595718Sphk#undef ADD
47695718Sphk#undef PRINT
47795718Sphk}
47895718Sphk
47995722Sphkint
48095722Sphkmii_phy_detach(device_t dev)
48195722Sphk{
48295722Sphk	struct mii_softc *sc;
48395722Sphk
48495722Sphk	sc = device_get_softc(dev);
48595722Sphk	mii_phy_down(sc);
48695722Sphk	sc->mii_dev = NULL;
48795722Sphk	LIST_REMOVE(sc, mii_list);
488214606Smarius	return (0);
48995722Sphk}
49095724Sphk
49195724Sphkconst struct mii_phydesc *
492150756Simpmii_phy_match_gen(const struct mii_attach_args *ma,
493150756Simp  const struct mii_phydesc *mpd, size_t len)
49495724Sphk{
49595724Sphk
496150756Simp	for (; mpd->mpd_name != NULL;
497215297Smarius	    mpd = (const struct mii_phydesc *)((const char *)mpd + len)) {
49895724Sphk		if (MII_OUI(ma->mii_id1, ma->mii_id2) == mpd->mpd_oui &&
49995724Sphk		    MII_MODEL(ma->mii_id2) == mpd->mpd_model)
50095724Sphk			return (mpd);
50195724Sphk	}
50295724Sphk	return (NULL);
50395724Sphk}
504150756Simp
505150756Simpconst struct mii_phydesc *
506150756Simpmii_phy_match(const struct mii_attach_args *ma, const struct mii_phydesc *mpd)
507150756Simp{
508164702Smarius
509150756Simp	return (mii_phy_match_gen(ma, mpd, sizeof(struct mii_phydesc)));
510150756Simp}
511164827Smarius
512164827Smariusint
513164827Smariusmii_phy_dev_probe(device_t dev, const struct mii_phydesc *mpd, int mrv)
514164827Smarius{
515164827Smarius
516164827Smarius	mpd = mii_phy_match(device_get_ivars(dev), mpd);
517164827Smarius	if (mpd != NULL) {
518164827Smarius		device_set_desc(dev, mpd->mpd_name);
519164827Smarius		return (mrv);
520164827Smarius	}
521164827Smarius	return (ENXIO);
522164827Smarius}
523215297Smarius
524215297Smarius/*
525215297Smarius * Return the flow control status flag from MII_ANAR & MII_ANLPAR.
526215297Smarius */
527215297Smariusu_int
528215297Smariusmii_phy_flowstatus(struct mii_softc *sc)
529215297Smarius{
530215297Smarius	int anar, anlpar;
531215297Smarius
532215297Smarius	if ((sc->mii_flags & MIIF_DOPAUSE) == 0)
533215297Smarius		return (0);
534215297Smarius
535215297Smarius	anar = PHY_READ(sc, MII_ANAR);
536215297Smarius	anlpar = PHY_READ(sc, MII_ANLPAR);
537215297Smarius
538215297Smarius	/*
539215297Smarius	 * Check for 1000BASE-X.  Autonegotiation is a bit
540215297Smarius	 * different on such devices.
541215297Smarius	 */
542215297Smarius	if ((sc->mii_flags & MIIF_IS_1000X) != 0) {
543215297Smarius		anar <<= 3;
544215297Smarius		anlpar <<= 3;
545215297Smarius	}
546215297Smarius
547215297Smarius	if ((anar & ANAR_PAUSE_SYM) != 0 && (anlpar & ANLPAR_PAUSE_SYM) != 0)
548215297Smarius		return (IFM_FLOW | IFM_ETH_TXPAUSE | IFM_ETH_RXPAUSE);
549215297Smarius
550215297Smarius	if ((anar & ANAR_PAUSE_SYM) == 0) {
551215297Smarius		if ((anar & ANAR_PAUSE_ASYM) != 0 &&
552215297Smarius		    (anlpar & ANLPAR_PAUSE_TOWARDS) != 0)
553215297Smarius			return (IFM_FLOW | IFM_ETH_TXPAUSE);
554215297Smarius		else
555215297Smarius			return (0);
556215297Smarius	}
557215297Smarius
558215297Smarius	if ((anar & ANAR_PAUSE_ASYM) == 0) {
559215297Smarius		if ((anlpar & ANLPAR_PAUSE_SYM) != 0)
560215297Smarius			return (IFM_FLOW | IFM_ETH_TXPAUSE | IFM_ETH_RXPAUSE);
561215297Smarius		else
562215297Smarius			return (0);
563215297Smarius	}
564215297Smarius
565215297Smarius	switch ((anlpar & ANLPAR_PAUSE_TOWARDS)) {
566215297Smarius	case ANLPAR_PAUSE_NONE:
567215297Smarius		return (0);
568215297Smarius	case ANLPAR_PAUSE_ASYM:
569215297Smarius		return (IFM_FLOW | IFM_ETH_RXPAUSE);
570215297Smarius	default:
571215297Smarius		return (IFM_FLOW | IFM_ETH_RXPAUSE | IFM_ETH_TXPAUSE);
572215297Smarius	}
573215297Smarius	/* NOTREACHED */
574215297Smarius}
575