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$");
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-
112220938Smarius		 * control advertisement 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
15395707Sphk	PHY_WRITE(sc, MII_ANAR, anar);
15495707Sphk	PHY_WRITE(sc, MII_BMCR, bmcr);
155214606Smarius	if ((sc->mii_flags & MIIF_HAVE_GTCR) != 0)
15695707Sphk		PHY_WRITE(sc, MII_100T2CR, gtcr);
15795707Sphk}
15895707Sphk
15950120Swpaulint
16096026Sphkmii_phy_auto(struct mii_softc *sc)
16150120Swpaul{
162215297Smarius	struct ifmedia_entry *ife = sc->mii_pdata->mii_media.ifm_cur;
163214606Smarius	int anar, gtcr;
16450120Swpaul
16596026Sphk	/*
16696026Sphk	 * Check for 1000BASE-X.  Autonegotiation is a bit
16796026Sphk	 * different on such devices.
16896026Sphk	 */
169214606Smarius	if ((sc->mii_flags & MIIF_IS_1000X) != 0) {
170214606Smarius		anar = 0;
171214606Smarius		if ((sc->mii_extcapabilities & EXTSR_1000XFDX) != 0)
17296026Sphk			anar |= ANAR_X_FD;
173214606Smarius		if ((sc->mii_extcapabilities & EXTSR_1000XHDX) != 0)
17496026Sphk			anar |= ANAR_X_HD;
17595718Sphk
176215297Smarius		if ((ife->ifm_media & IFM_FLOW) != 0 ||
177215297Smarius		    (sc->mii_flags & MIIF_FORCEPAUSE) != 0)
178215297Smarius			anar |= ANAR_X_PAUSE_TOWARDS;
17996026Sphk		PHY_WRITE(sc, MII_ANAR, anar);
18096026Sphk	} else {
18196026Sphk		anar = BMSR_MEDIA_TO_ANAR(sc->mii_capabilities) |
18296026Sphk		    ANAR_CSMA;
183215297Smarius		if ((ife->ifm_media & IFM_FLOW) != 0 ||
184215297Smarius		    (sc->mii_flags & MIIF_FORCEPAUSE) != 0) {
185217414Smarius			if ((sc->mii_capabilities &
186217414Smarius			    (BMSR_10TFDX | BMSR_100TXFDX)) != 0)
187215297Smarius				anar |= ANAR_FC;
188215297Smarius			/* XXX Only 1000BASE-T has PAUSE_ASYM? */
189215297Smarius			if (((sc->mii_flags & MIIF_HAVE_GTCR) != 0) &&
190215297Smarius			    (sc->mii_extcapabilities &
191215297Smarius			    (EXTSR_1000THDX | EXTSR_1000TFDX)) != 0)
192215297Smarius				anar |= ANAR_X_PAUSE_ASYM;
193215297Smarius		}
19496026Sphk		PHY_WRITE(sc, MII_ANAR, anar);
195214606Smarius		if ((sc->mii_flags & MIIF_HAVE_GTCR) != 0) {
196214606Smarius			gtcr = 0;
197214606Smarius			if ((sc->mii_extcapabilities & EXTSR_1000TFDX) != 0)
19896026Sphk				gtcr |= GTCR_ADV_1000TFDX;
199214606Smarius			if ((sc->mii_extcapabilities & EXTSR_1000THDX) != 0)
20096026Sphk				gtcr |= GTCR_ADV_1000THDX;
20196026Sphk			PHY_WRITE(sc, MII_100T2CR, gtcr);
20295718Sphk		}
20350120Swpaul	}
20496026Sphk	PHY_WRITE(sc, MII_BMCR, BMCR_AUTOEN | BMCR_STARTNEG);
20550120Swpaul	return (EJUSTRETURN);
20650120Swpaul}
20750120Swpaul
20884140Sjlemonint
20995718Sphkmii_phy_tick(struct mii_softc *sc)
21084140Sjlemon{
21184140Sjlemon	struct ifmedia_entry *ife = sc->mii_pdata->mii_media.ifm_cur;
21284140Sjlemon	struct ifnet *ifp = sc->mii_pdata->mii_ifp;
21384140Sjlemon	int reg;
21484140Sjlemon
21595718Sphk	/* Just bail now if the interface is down. */
21684140Sjlemon	if ((ifp->if_flags & IFF_UP) == 0)
21784140Sjlemon		return (EJUSTRETURN);
21884140Sjlemon
21984140Sjlemon	/*
22084140Sjlemon	 * If we're not doing autonegotiation, we don't need to do
22184140Sjlemon	 * any extra work here.  However, we need to check the link
22284140Sjlemon	 * status so we can generate an announcement if the status
22384140Sjlemon	 * changes.
22484140Sjlemon	 */
225160082Soleg	if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) {
226160082Soleg		sc->mii_ticks = 0;	/* reset autonegotiation timer. */
22784140Sjlemon		return (0);
228160082Soleg	}
22984140Sjlemon
23095718Sphk	/* Read the status register twice; BMSR_LINK is latch-low. */
23184140Sjlemon	reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
232214606Smarius	if ((reg & BMSR_LINK) != 0) {
233158649Soleg		sc->mii_ticks = 0;	/* reset autonegotiation timer. */
234158649Soleg		/* See above. */
23584140Sjlemon		return (0);
23695718Sphk	}
23784140Sjlemon
238158649Soleg	/* Announce link loss right after it happens */
239158649Soleg	if (sc->mii_ticks++ == 0)
240128870Sandre		return (0);
241158649Soleg
242158649Soleg	/* XXX: use default value if phy driver did not set mii_anegticks */
243158649Soleg	if (sc->mii_anegticks == 0)
244158649Soleg		sc->mii_anegticks = MII_ANEGTICKS_GIGE;
245158649Soleg
246158649Soleg	/* Only retry autonegotiation every mii_anegticks ticks. */
247158649Soleg	if (sc->mii_ticks <= sc->mii_anegticks)
24884140Sjlemon		return (EJUSTRETURN);
24984140Sjlemon
25084140Sjlemon	sc->mii_ticks = 0;
251221407Smarius	PHY_RESET(sc);
25296026Sphk	mii_phy_auto(sc);
25384140Sjlemon	return (0);
25484140Sjlemon}
25584140Sjlemon
25650120Swpaulvoid
25795718Sphkmii_phy_reset(struct mii_softc *sc)
25850120Swpaul{
259164702Smarius	struct ifmedia_entry *ife = sc->mii_pdata->mii_media.ifm_cur;
260221812Smarius	int i, reg;
26150120Swpaul
262214606Smarius	if ((sc->mii_flags & MIIF_NOISOLATE) != 0)
26350120Swpaul		reg = BMCR_RESET;
26450120Swpaul	else
26550120Swpaul		reg = BMCR_RESET | BMCR_ISO;
26695718Sphk	PHY_WRITE(sc, MII_BMCR, reg);
26750120Swpaul
26850120Swpaul	/* Wait 100ms for it to complete. */
26950120Swpaul	for (i = 0; i < 100; i++) {
270164702Smarius		reg = PHY_READ(sc, MII_BMCR);
27150120Swpaul		if ((reg & BMCR_RESET) == 0)
27250120Swpaul			break;
27350120Swpaul		DELAY(1000);
27450120Swpaul	}
27550120Swpaul
276225014Smarius	/* NB: a PHY may default to being powered down and/or isolated. */
277225014Smarius	reg &= ~(BMCR_PDOWN | BMCR_ISO);
278221812Smarius	if ((sc->mii_flags & MIIF_NOISOLATE) == 0 &&
279221812Smarius	    ((ife == NULL && sc->mii_inst != 0) ||
280221812Smarius	    (ife != NULL && IFM_INST(ife->ifm_media) != sc->mii_inst)))
281221812Smarius		reg |= BMCR_ISO;
282221812Smarius	if (PHY_READ(sc, MII_BMCR) != reg)
283221812Smarius		PHY_WRITE(sc, MII_BMCR, reg);
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
335221407Smarius	if ((sc->mii_flags & MIIF_NOISOLATE) == 0) {
33695718Sphk		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_NONE, 0, sc->mii_inst),
33795718Sphk		    MII_MEDIA_NONE);
338221407Smarius		PRINT("none");
339221407Smarius	}
34095718Sphk
34195718Sphk	/*
34295718Sphk	 * There are different interpretations for the bits in
34395718Sphk	 * HomePNA PHYs.  And there is really only one media type
34495718Sphk	 * that is supported.
34595718Sphk	 */
346214606Smarius	if ((sc->mii_flags & MIIF_IS_HPNA) != 0) {
347214606Smarius		if ((sc->mii_capabilities & BMSR_10THDX) != 0) {
34895718Sphk			ADD(IFM_MAKEWORD(IFM_ETHER, IFM_HPNA_1, 0,
349214606Smarius			    sc->mii_inst), MII_MEDIA_10_T);
35095718Sphk			PRINT("HomePNA1");
35195718Sphk		}
35295718Sphk		return;
35395718Sphk	}
35495718Sphk
355214606Smarius	if ((sc->mii_capabilities & BMSR_10THDX) != 0) {
35695718Sphk		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, 0, sc->mii_inst),
35795718Sphk		    MII_MEDIA_10_T);
35895718Sphk		PRINT("10baseT");
35995718Sphk	}
360214606Smarius	if ((sc->mii_capabilities & BMSR_10TFDX) != 0) {
36195718Sphk		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, IFM_FDX, sc->mii_inst),
36295718Sphk		    MII_MEDIA_10_T_FDX);
36395718Sphk		PRINT("10baseT-FDX");
364215297Smarius		if ((sc->mii_flags & MIIF_DOPAUSE) != 0 &&
365215297Smarius		    (sc->mii_flags & MIIF_NOMANPAUSE) == 0) {
366215297Smarius			ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T,
367215297Smarius			    IFM_FDX | IFM_FLOW, sc->mii_inst),
368215297Smarius			    MII_MEDIA_10_T_FDX);
369215297Smarius			PRINT("10baseT-FDX-flow");
370215297Smarius		}
371215297Smarius		fdx = 1;
37295718Sphk	}
373214606Smarius	if ((sc->mii_capabilities & BMSR_100TXHDX) != 0) {
37495718Sphk		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, 0, sc->mii_inst),
37595718Sphk		    MII_MEDIA_100_TX);
37695718Sphk		PRINT("100baseTX");
37795718Sphk	}
378214606Smarius	if ((sc->mii_capabilities & BMSR_100TXFDX) != 0) {
37995718Sphk		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_FDX, sc->mii_inst),
38095718Sphk		    MII_MEDIA_100_TX_FDX);
38195718Sphk		PRINT("100baseTX-FDX");
382215297Smarius		if ((sc->mii_flags & MIIF_DOPAUSE) != 0 &&
383215297Smarius		    (sc->mii_flags & MIIF_NOMANPAUSE) == 0) {
384215297Smarius			ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX,
385215297Smarius			    IFM_FDX | IFM_FLOW, sc->mii_inst),
386215297Smarius			    MII_MEDIA_100_TX_FDX);
387215297Smarius			PRINT("100baseTX-FDX-flow");
388215297Smarius		}
389215297Smarius		fdx = 1;
39095718Sphk	}
391214606Smarius	if ((sc->mii_capabilities & BMSR_100T4) != 0) {
39295718Sphk		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_T4, 0, sc->mii_inst),
39395718Sphk		    MII_MEDIA_100_T4);
39495718Sphk		PRINT("100baseT4");
39595718Sphk	}
39695718Sphk
397214606Smarius	if ((sc->mii_extcapabilities & EXTSR_MEDIAMASK) != 0) {
39895718Sphk		/*
39995718Sphk		 * XXX Right now only handle 1000SX and 1000TX.  Need
400214606Smarius		 * XXX to handle 1000LX and 1000CX somehow.
40195718Sphk		 */
402214606Smarius		if ((sc->mii_extcapabilities & EXTSR_1000XHDX) != 0) {
403158649Soleg			sc->mii_anegticks = MII_ANEGTICKS_GIGE;
40495718Sphk			sc->mii_flags |= MIIF_IS_1000X;
40595718Sphk			ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_SX, 0,
40695718Sphk			    sc->mii_inst), MII_MEDIA_1000_X);
40795718Sphk			PRINT("1000baseSX");
40895718Sphk		}
409214606Smarius		if ((sc->mii_extcapabilities & EXTSR_1000XFDX) != 0) {
410158649Soleg			sc->mii_anegticks = MII_ANEGTICKS_GIGE;
41195718Sphk			sc->mii_flags |= MIIF_IS_1000X;
41295718Sphk			ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_SX, IFM_FDX,
41395718Sphk			    sc->mii_inst), MII_MEDIA_1000_X_FDX);
41495718Sphk			PRINT("1000baseSX-FDX");
415215297Smarius			if ((sc->mii_flags & MIIF_DOPAUSE) != 0 &&
416215297Smarius			    (sc->mii_flags & MIIF_NOMANPAUSE) == 0) {
417215297Smarius				ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_SX,
418215297Smarius				    IFM_FDX | IFM_FLOW, sc->mii_inst),
419215297Smarius				    MII_MEDIA_1000_X_FDX);
420215297Smarius				PRINT("1000baseSX-FDX-flow");
421215297Smarius			}
422215297Smarius			fdx = 1;
42395718Sphk		}
42495718Sphk
42595718Sphk		/*
42695718Sphk		 * 1000baseT media needs to be able to manipulate
427215297Smarius		 * master/slave mode.
42895718Sphk		 *
42995718Sphk		 * All 1000baseT PHYs have a 1000baseT control register.
43095718Sphk		 */
431214606Smarius		if ((sc->mii_extcapabilities & EXTSR_1000THDX) != 0) {
432158649Soleg			sc->mii_anegticks = MII_ANEGTICKS_GIGE;
43395718Sphk			sc->mii_flags |= MIIF_HAVE_GTCR;
43495718Sphk			ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, 0,
43595718Sphk			    sc->mii_inst), MII_MEDIA_1000_T);
43695718Sphk			PRINT("1000baseT");
437215297Smarius			ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T,
438215297Smarius			    IFM_ETH_MASTER, sc->mii_inst), MII_MEDIA_1000_T);
439215297Smarius			PRINT("1000baseT-master");
44095718Sphk		}
441214606Smarius		if ((sc->mii_extcapabilities & EXTSR_1000TFDX) != 0) {
442158649Soleg			sc->mii_anegticks = MII_ANEGTICKS_GIGE;
44395718Sphk			sc->mii_flags |= MIIF_HAVE_GTCR;
44495718Sphk			ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, IFM_FDX,
44595718Sphk			    sc->mii_inst), MII_MEDIA_1000_T_FDX);
44695718Sphk			PRINT("1000baseT-FDX");
447215297Smarius			ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T,
448215297Smarius			    IFM_FDX | IFM_ETH_MASTER, sc->mii_inst),
449215297Smarius			    MII_MEDIA_1000_T_FDX);
450215297Smarius			PRINT("1000baseT-FDX-master");
451215297Smarius			if ((sc->mii_flags & MIIF_DOPAUSE) != 0 &&
452215297Smarius			    (sc->mii_flags & MIIF_NOMANPAUSE) == 0) {
453215297Smarius				ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T,
454215297Smarius				    IFM_FDX | IFM_FLOW, sc->mii_inst),
455215297Smarius				    MII_MEDIA_1000_T_FDX);
456215297Smarius				PRINT("1000baseT-FDX-flow");
457215297Smarius				ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T,
458215297Smarius				    IFM_FDX | IFM_FLOW | IFM_ETH_MASTER,
459215297Smarius				    sc->mii_inst), MII_MEDIA_1000_T_FDX);
460215297Smarius				PRINT("1000baseT-FDX-flow-master");
461215297Smarius			}
462215297Smarius			fdx = 1;
46395718Sphk		}
46495718Sphk	}
46595718Sphk
466214606Smarius	if ((sc->mii_capabilities & BMSR_ANEG) != 0) {
467215297Smarius		/* intentionally invalid index */
46895718Sphk		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, sc->mii_inst),
469215297Smarius		    MII_NMEDIA);
47095718Sphk		PRINT("auto");
471215297Smarius		if (fdx != 0 && (sc->mii_flags & MIIF_DOPAUSE) != 0) {
472215297Smarius			ADD(IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, IFM_FLOW,
473215297Smarius			    sc->mii_inst), MII_NMEDIA);
474215297Smarius			PRINT("auto-flow");
475215297Smarius		}
47695718Sphk	}
47795718Sphk#undef ADD
47895718Sphk#undef PRINT
47995718Sphk}
48095718Sphk
48195722Sphkint
48295722Sphkmii_phy_detach(device_t dev)
48395722Sphk{
48495722Sphk	struct mii_softc *sc;
48595722Sphk
48695722Sphk	sc = device_get_softc(dev);
48795722Sphk	mii_phy_down(sc);
48895722Sphk	sc->mii_dev = NULL;
48995722Sphk	LIST_REMOVE(sc, mii_list);
490214606Smarius	return (0);
49195722Sphk}
49295724Sphk
49395724Sphkconst struct mii_phydesc *
494150756Simpmii_phy_match_gen(const struct mii_attach_args *ma,
495150756Simp  const struct mii_phydesc *mpd, size_t len)
49695724Sphk{
49795724Sphk
498150756Simp	for (; mpd->mpd_name != NULL;
499215297Smarius	    mpd = (const struct mii_phydesc *)((const char *)mpd + len)) {
50095724Sphk		if (MII_OUI(ma->mii_id1, ma->mii_id2) == mpd->mpd_oui &&
50195724Sphk		    MII_MODEL(ma->mii_id2) == mpd->mpd_model)
50295724Sphk			return (mpd);
50395724Sphk	}
50495724Sphk	return (NULL);
50595724Sphk}
506150756Simp
507150756Simpconst struct mii_phydesc *
508150756Simpmii_phy_match(const struct mii_attach_args *ma, const struct mii_phydesc *mpd)
509150756Simp{
510164702Smarius
511150756Simp	return (mii_phy_match_gen(ma, mpd, sizeof(struct mii_phydesc)));
512150756Simp}
513164827Smarius
514164827Smariusint
515164827Smariusmii_phy_dev_probe(device_t dev, const struct mii_phydesc *mpd, int mrv)
516164827Smarius{
517164827Smarius
518164827Smarius	mpd = mii_phy_match(device_get_ivars(dev), mpd);
519164827Smarius	if (mpd != NULL) {
520164827Smarius		device_set_desc(dev, mpd->mpd_name);
521164827Smarius		return (mrv);
522164827Smarius	}
523164827Smarius	return (ENXIO);
524164827Smarius}
525215297Smarius
526221407Smariusvoid
527221407Smariusmii_phy_dev_attach(device_t dev, u_int flags, const struct mii_phy_funcs *mpf,
528221407Smarius    int add_media)
529221407Smarius{
530221407Smarius	struct mii_softc *sc;
531221407Smarius	struct mii_attach_args *ma;
532221407Smarius	struct mii_data *mii;
533221407Smarius
534221407Smarius	sc = device_get_softc(dev);
535221407Smarius	ma = device_get_ivars(dev);
536221407Smarius	sc->mii_dev = device_get_parent(dev);
537221407Smarius	mii = ma->mii_data;
538221407Smarius	LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list);
539221407Smarius
540221407Smarius	sc->mii_flags = flags | miibus_get_flags(dev);
541221407Smarius	sc->mii_mpd_oui = MII_OUI(ma->mii_id1, ma->mii_id2);
542221407Smarius	sc->mii_mpd_model = MII_MODEL(ma->mii_id2);
543221407Smarius	sc->mii_mpd_rev = MII_REV(ma->mii_id2);
544221407Smarius	sc->mii_capmask = ma->mii_capmask;
545221407Smarius	sc->mii_inst = mii->mii_instance++;
546221407Smarius	sc->mii_phy = ma->mii_phyno;
547221407Smarius	sc->mii_offset = ma->mii_offset;
548221407Smarius	sc->mii_funcs = mpf;
549221407Smarius	sc->mii_pdata = mii;
550221407Smarius
551221407Smarius	if (bootverbose)
552221407Smarius		device_printf(dev, "OUI 0x%06x, model 0x%04x, rev. %d\n",
553221407Smarius		    sc->mii_mpd_oui, sc->mii_mpd_model, sc->mii_mpd_rev);
554221407Smarius
555221407Smarius	if (add_media == 0)
556221407Smarius		return;
557221407Smarius
558221407Smarius	PHY_RESET(sc);
559221407Smarius
560221407Smarius	sc->mii_capabilities = PHY_READ(sc, MII_BMSR) & sc->mii_capmask;
561221407Smarius	if (sc->mii_capabilities & BMSR_EXTSTAT)
562221407Smarius		sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR);
563221407Smarius	device_printf(dev, " ");
564221407Smarius	mii_phy_add_media(sc);
565221407Smarius	printf("\n");
566221407Smarius
567221407Smarius	MIIBUS_MEDIAINIT(sc->mii_dev);
568221407Smarius}
569221407Smarius
570215297Smarius/*
571215297Smarius * Return the flow control status flag from MII_ANAR & MII_ANLPAR.
572215297Smarius */
573215297Smariusu_int
574215297Smariusmii_phy_flowstatus(struct mii_softc *sc)
575215297Smarius{
576215297Smarius	int anar, anlpar;
577215297Smarius
578215297Smarius	if ((sc->mii_flags & MIIF_DOPAUSE) == 0)
579215297Smarius		return (0);
580215297Smarius
581215297Smarius	anar = PHY_READ(sc, MII_ANAR);
582215297Smarius	anlpar = PHY_READ(sc, MII_ANLPAR);
583215297Smarius
584215297Smarius	/*
585215297Smarius	 * Check for 1000BASE-X.  Autonegotiation is a bit
586215297Smarius	 * different on such devices.
587215297Smarius	 */
588215297Smarius	if ((sc->mii_flags & MIIF_IS_1000X) != 0) {
589215297Smarius		anar <<= 3;
590215297Smarius		anlpar <<= 3;
591215297Smarius	}
592215297Smarius
593215297Smarius	if ((anar & ANAR_PAUSE_SYM) != 0 && (anlpar & ANLPAR_PAUSE_SYM) != 0)
594215297Smarius		return (IFM_FLOW | IFM_ETH_TXPAUSE | IFM_ETH_RXPAUSE);
595215297Smarius
596215297Smarius	if ((anar & ANAR_PAUSE_SYM) == 0) {
597215297Smarius		if ((anar & ANAR_PAUSE_ASYM) != 0 &&
598215297Smarius		    (anlpar & ANLPAR_PAUSE_TOWARDS) != 0)
599215297Smarius			return (IFM_FLOW | IFM_ETH_TXPAUSE);
600215297Smarius		else
601215297Smarius			return (0);
602215297Smarius	}
603215297Smarius
604215297Smarius	if ((anar & ANAR_PAUSE_ASYM) == 0) {
605215297Smarius		if ((anlpar & ANLPAR_PAUSE_SYM) != 0)
606215297Smarius			return (IFM_FLOW | IFM_ETH_TXPAUSE | IFM_ETH_RXPAUSE);
607215297Smarius		else
608215297Smarius			return (0);
609215297Smarius	}
610215297Smarius
611215297Smarius	switch ((anlpar & ANLPAR_PAUSE_TOWARDS)) {
612215297Smarius	case ANLPAR_PAUSE_NONE:
613215297Smarius		return (0);
614215297Smarius	case ANLPAR_PAUSE_ASYM:
615215297Smarius		return (IFM_FLOW | IFM_ETH_RXPAUSE);
616215297Smarius	default:
617215297Smarius		return (IFM_FLOW | IFM_ETH_RXPAUSE | IFM_ETH_TXPAUSE);
618215297Smarius	}
619215297Smarius	/* NOTREACHED */
620215297Smarius}
621