1139749Simp/*-
259477Swpaul * Copyright (c) 2000
359477Swpaul *	Bill Paul <wpaul@ee.columbia.edu>.  All rights reserved.
459477Swpaul *
559477Swpaul * Redistribution and use in source and binary forms, with or without
659477Swpaul * modification, are permitted provided that the following conditions
759477Swpaul * are met:
859477Swpaul * 1. Redistributions of source code must retain the above copyright
959477Swpaul *    notice, this list of conditions and the following disclaimer.
1059477Swpaul * 2. Redistributions in binary form must reproduce the above copyright
1159477Swpaul *    notice, this list of conditions and the following disclaimer in the
1259477Swpaul *    documentation and/or other materials provided with the distribution.
1359477Swpaul * 3. All advertising materials mentioning features or use of this software
1459477Swpaul *    must display the following acknowledgement:
1559477Swpaul *	This product includes software developed by Bill Paul.
1659477Swpaul * 4. Neither the name of the author nor the names of any co-contributors
1759477Swpaul *    may be used to endorse or promote products derived from this software
1859477Swpaul *    without specific prior written permission.
1959477Swpaul *
2059477Swpaul * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
2159477Swpaul * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2259477Swpaul * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2359477Swpaul * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
2459477Swpaul * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2559477Swpaul * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2659477Swpaul * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2759477Swpaul * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2859477Swpaul * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2959477Swpaul * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
3059477Swpaul * THE POSSIBILITY OF SUCH DAMAGE.
3159477Swpaul */
3259477Swpaul
33119418Sobrien#include <sys/cdefs.h>
34119418Sobrien__FBSDID("$FreeBSD: stable/11/sys/dev/mii/brgphy.c 359843 2020-04-13 00:50:35Z jhibbits $");
35119418Sobrien
3659477Swpaul/*
37166680Sjkim * Driver for the Broadcom BCM54xx/57xx 1000baseTX PHY.
3859477Swpaul */
3959477Swpaul
4059477Swpaul#include <sys/param.h>
4159477Swpaul#include <sys/systm.h>
4259477Swpaul#include <sys/kernel.h>
43129876Sphk#include <sys/module.h>
4459477Swpaul#include <sys/socket.h>
4559477Swpaul#include <sys/bus.h>
46266974Smarcel#include <sys/taskqueue.h>
4759477Swpaul
4859477Swpaul#include <net/if.h>
49257184Sglebius#include <net/if_var.h>
50157642Sps#include <net/ethernet.h>
5159477Swpaul#include <net/if_media.h>
5259477Swpaul
5359477Swpaul#include <dev/mii/mii.h>
5459477Swpaul#include <dev/mii/miivar.h>
55109514Sobrien#include "miidevs.h"
5659477Swpaul
5759477Swpaul#include <dev/mii/brgphyreg.h>
58117659Swpaul#include <net/if_arp.h>
59117659Swpaul#include <machine/bus.h>
60117659Swpaul#include <dev/bge/if_bgereg.h>
61157642Sps#include <dev/bce/if_bcereg.h>
6259477Swpaul
63119285Simp#include <dev/pci/pcireg.h>
64119285Simp#include <dev/pci/pcivar.h>
65118814Swpaul
6659477Swpaul#include "miibus_if.h"
6759477Swpaul
68105135Salfredstatic int brgphy_probe(device_t);
69105135Salfredstatic int brgphy_attach(device_t);
7059477Swpaul
71166037Sjkimstruct brgphy_softc {
72166037Sjkim	struct mii_softc mii_sc;
73170391Sdavidch	int serdes_flags;	/* Keeps track of the serdes type used */
74204941Ssobomax#define BRGPHY_5706S		0x0001
75204941Ssobomax#define BRGPHY_5708S		0x0002
76204941Ssobomax#define BRGPHY_NOANWAIT		0x0004
77205299Sdavidch#define BRGPHY_5709S		0x0008
78179772Sdavidch	int bce_phy_flags;	/* PHY flags transferred from the MAC driver */
79166037Sjkim};
80166037Sjkim
8159477Swpaulstatic device_method_t brgphy_methods[] = {
8259477Swpaul	/* device interface */
8359477Swpaul	DEVMETHOD(device_probe,		brgphy_probe),
8459477Swpaul	DEVMETHOD(device_attach,	brgphy_attach),
8595722Sphk	DEVMETHOD(device_detach,	mii_phy_detach),
8659477Swpaul	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
87227908Smarius	DEVMETHOD_END
8859477Swpaul};
8959477Swpaul
9059477Swpaulstatic devclass_t brgphy_devclass;
9159477Swpaul
9259477Swpaulstatic driver_t brgphy_driver = {
9359477Swpaul	"brgphy",
9459477Swpaul	brgphy_methods,
95166049Sjkim	sizeof(struct brgphy_softc)
9659477Swpaul};
9759477Swpaul
9859477SwpaulDRIVER_MODULE(brgphy, miibus, brgphy_driver, brgphy_devclass, 0, 0);
9959477Swpaul
10084145Sjlemonstatic int	brgphy_service(struct mii_softc *, struct mii_data *, int);
101215297Smariusstatic void	brgphy_setmedia(struct mii_softc *, int);
10284145Sjlemonstatic void	brgphy_status(struct mii_softc *);
103215297Smariusstatic void	brgphy_mii_phy_auto(struct mii_softc *, int);
104114590Spsstatic void	brgphy_reset(struct mii_softc *);
105170391Sdavidchstatic void	brgphy_enable_loopback(struct mii_softc *);
106114590Spsstatic void	bcm5401_load_dspcode(struct mii_softc *);
107114590Spsstatic void	bcm5411_load_dspcode(struct mii_softc *);
108204144Smariusstatic void	bcm54k2_load_dspcode(struct mii_softc *);
109166676Sjkimstatic void	brgphy_fixup_5704_a0_bug(struct mii_softc *);
110166031Sjkimstatic void	brgphy_fixup_adc_bug(struct mii_softc *);
111166673Sjkimstatic void	brgphy_fixup_adjust_trim(struct mii_softc *);
112166031Sjkimstatic void	brgphy_fixup_ber_bug(struct mii_softc *);
113166677Sjkimstatic void	brgphy_fixup_crc_bug(struct mii_softc *);
114166031Sjkimstatic void	brgphy_fixup_jitter_bug(struct mii_softc *);
115166032Sjkimstatic void	brgphy_ethernet_wirespeed(struct mii_softc *);
116166032Sjkimstatic void	brgphy_jumbo_settings(struct mii_softc *, u_long);
11759477Swpaul
118160078Syongaristatic const struct mii_phydesc brgphys[] = {
119221407Smarius	MII_PHY_DESC(BROADCOM, BCM5400),
120221407Smarius	MII_PHY_DESC(BROADCOM, BCM5401),
121287470Ssbruno	MII_PHY_DESC(BROADCOM, BCM5402),
122221407Smarius	MII_PHY_DESC(BROADCOM, BCM5411),
123287470Ssbruno	MII_PHY_DESC(BROADCOM, BCM5404),
124287470Ssbruno	MII_PHY_DESC(BROADCOM, BCM5424),
125221407Smarius	MII_PHY_DESC(BROADCOM, BCM54K2),
126221407Smarius	MII_PHY_DESC(BROADCOM, BCM5701),
127221407Smarius	MII_PHY_DESC(BROADCOM, BCM5703),
128221407Smarius	MII_PHY_DESC(BROADCOM, BCM5704),
129221407Smarius	MII_PHY_DESC(BROADCOM, BCM5705),
130221407Smarius	MII_PHY_DESC(BROADCOM, BCM5706),
131221407Smarius	MII_PHY_DESC(BROADCOM, BCM5714),
132221407Smarius	MII_PHY_DESC(BROADCOM, BCM5421),
133221407Smarius	MII_PHY_DESC(BROADCOM, BCM5750),
134221407Smarius	MII_PHY_DESC(BROADCOM, BCM5752),
135221407Smarius	MII_PHY_DESC(BROADCOM, BCM5780),
136221407Smarius	MII_PHY_DESC(BROADCOM, BCM5708C),
137286041Ssbruno	MII_PHY_DESC(BROADCOM, BCM5466),
138287470Ssbruno	MII_PHY_DESC(BROADCOM2, BCM5478),
139287470Ssbruno	MII_PHY_DESC(BROADCOM2, BCM5488),
140221407Smarius	MII_PHY_DESC(BROADCOM2, BCM5482),
141221407Smarius	MII_PHY_DESC(BROADCOM2, BCM5708S),
142221407Smarius	MII_PHY_DESC(BROADCOM2, BCM5709C),
143221407Smarius	MII_PHY_DESC(BROADCOM2, BCM5709S),
144221407Smarius	MII_PHY_DESC(BROADCOM2, BCM5709CAX),
145221407Smarius	MII_PHY_DESC(BROADCOM2, BCM5722),
146221407Smarius	MII_PHY_DESC(BROADCOM2, BCM5755),
147221407Smarius	MII_PHY_DESC(BROADCOM2, BCM5754),
148221407Smarius	MII_PHY_DESC(BROADCOM2, BCM5761),
149221407Smarius	MII_PHY_DESC(BROADCOM2, BCM5784),
150227913Smarius#ifdef notyet	/* better handled by ukphy(4) until WARs are implemented */
151227907Smarius	MII_PHY_DESC(BROADCOM2, BCM5785),
152227913Smarius#endif
153359843Sjhibbits	MII_PHY_DESC(BROADCOM3, BCM54618SE),
154221407Smarius	MII_PHY_DESC(BROADCOM3, BCM5717C),
155221713Syongari	MII_PHY_DESC(BROADCOM3, BCM5719C),
156226870Syongari	MII_PHY_DESC(BROADCOM3, BCM5720C),
157221407Smarius	MII_PHY_DESC(BROADCOM3, BCM57765),
158231913Smarius	MII_PHY_DESC(BROADCOM3, BCM57780),
159253481Syongari	MII_PHY_DESC(BROADCOM4, BCM5725C),
160221407Smarius	MII_PHY_DESC(xxBROADCOM_ALT1, BCM5906),
161160078Syongari	MII_PHY_END
162160078Syongari};
163160078Syongari
164221407Smariusstatic const struct mii_phy_funcs brgphy_funcs = {
165221407Smarius	brgphy_service,
166221407Smarius	brgphy_status,
167221407Smarius	brgphy_reset
168221407Smarius};
169221407Smarius
170281877Syongaristatic const struct hs21_type {
171281877Syongari	const uint32_t id;
172281877Syongari	const char *prod;
173281877Syongari} hs21_type_lists[] = {
174281877Syongari	{ 0x57081021, "IBM eServer BladeCenter HS21" },
175281877Syongari	{ 0x57081011, "IBM eServer BladeCenter HS21 -[8853PAU]-" },
176281877Syongari};
177170391Sdavidch
178204989Ssobomaxstatic int
179204989Ssobomaxdetect_hs21(struct bce_softc *bce_sc)
180204989Ssobomax{
181204989Ssobomax	char *sysenv;
182281877Syongari	int found, i;
183204989Ssobomax
184215353Sjkim	found = 0;
185281877Syongari	sysenv = kern_getenv("smbios.system.product");
186281877Syongari	if (sysenv == NULL)
187281877Syongari		return (found);
188281877Syongari	for (i = 0; i < nitems(hs21_type_lists); i++) {
189281877Syongari		if (bce_sc->bce_chipid == hs21_type_lists[i].id &&
190281877Syongari		    strncmp(sysenv, hs21_type_lists[i].prod,
191281877Syongari		    strlen(hs21_type_lists[i].prod)) == 0) {
192281877Syongari			found++;
193281877Syongari			break;
194215353Sjkim		}
195215353Sjkim	}
196281877Syongari	freeenv(sysenv);
197215353Sjkim	return (found);
198204989Ssobomax}
199204989Ssobomax
200170391Sdavidch/* Search for our PHY in the list of known PHYs */
201105135Salfredstatic int
202150763Simpbrgphy_probe(device_t dev)
20359477Swpaul{
204215297Smarius
205170391Sdavidch	return (mii_phy_dev_probe(dev, brgphys, BUS_PROBE_DEFAULT));
20659477Swpaul}
20759477Swpaul
208170391Sdavidch/* Attach the PHY to the MII bus */
209105135Salfredstatic int
210150763Simpbrgphy_attach(device_t dev)
21159477Swpaul{
212166037Sjkim	struct brgphy_softc *bsc;
213170391Sdavidch	struct bge_softc *bge_sc = NULL;
214170391Sdavidch	struct bce_softc *bce_sc = NULL;
21559477Swpaul	struct mii_softc *sc;
21659477Swpaul
217166037Sjkim	bsc = device_get_softc(dev);
218166037Sjkim	sc = &bsc->mii_sc;
21959477Swpaul
220221407Smarius	mii_phy_dev_attach(dev, MIIF_NOISOLATE | MIIF_NOMANPAUSE,
221221407Smarius	    &brgphy_funcs, 0);
222213364Smarius
223170391Sdavidch	bsc->serdes_flags = 0;
224170391Sdavidch
225244482Syongari	/* Find the MAC driver associated with this PHY. */
226277093Sglebius	if (mii_dev_mac_match(dev, "bge"))
227277093Sglebius		bge_sc = mii_dev_mac_softc(dev);
228277093Sglebius	else if (mii_dev_mac_match(dev, "bce"))
229277093Sglebius		bce_sc = mii_dev_mac_softc(dev);
230244482Syongari
231170391Sdavidch	/* Handle any special cases based on the PHY ID */
232221407Smarius	switch (sc->mii_mpd_oui) {
233181617Syongari	case MII_OUI_BROADCOM:
234221407Smarius		switch (sc->mii_mpd_model) {
235221407Smarius		case MII_MODEL_BROADCOM_BCM5706:
236221407Smarius		case MII_MODEL_BROADCOM_BCM5714:
237205299Sdavidch			/*
238205299Sdavidch			 * The 5464 PHY used in the 5706 supports both copper
239205299Sdavidch			 * and fiber interfaces over GMII.  Need to check the
240205299Sdavidch			 * shadow registers to see which mode is actually
241205299Sdavidch			 * in effect, and therefore whether we have 5706C or
242205299Sdavidch			 * 5706S.
243205299Sdavidch			 */
244205299Sdavidch			PHY_WRITE(sc, BRGPHY_MII_SHADOW_1C,
245205299Sdavidch				BRGPHY_SHADOW_1C_MODE_CTRL);
246205299Sdavidch			if (PHY_READ(sc, BRGPHY_MII_SHADOW_1C) &
247205299Sdavidch				BRGPHY_SHADOW_1C_ENA_1000X) {
248205299Sdavidch				bsc->serdes_flags |= BRGPHY_5706S;
249205299Sdavidch				sc->mii_flags |= MIIF_HAVEFIBER;
250205299Sdavidch			}
251205299Sdavidch			break;
252231913Smarius		}
253231913Smarius		break;
254221407Smarius	case MII_OUI_BROADCOM2:
255221407Smarius		switch (sc->mii_mpd_model) {
256221407Smarius		case MII_MODEL_BROADCOM2_BCM5708S:
257205299Sdavidch			bsc->serdes_flags |= BRGPHY_5708S;
258205299Sdavidch			sc->mii_flags |= MIIF_HAVEFIBER;
259205299Sdavidch			break;
260221407Smarius		case MII_MODEL_BROADCOM2_BCM5709S:
261244482Syongari			/*
262244482Syongari			 * XXX
263244482Syongari			 * 5720S and 5709S shares the same PHY id.
264244482Syongari			 * Assume 5720S PHY if parent device is bge(4).
265244482Syongari			 */
266244482Syongari			if (bge_sc != NULL)
267244482Syongari				bsc->serdes_flags |= BRGPHY_5708S;
268244482Syongari			else
269244482Syongari				bsc->serdes_flags |= BRGPHY_5709S;
270212307Syongari			sc->mii_flags |= MIIF_HAVEFIBER;
271212307Syongari			break;
272212307Syongari		}
273212307Syongari		break;
274170391Sdavidch	}
275170391Sdavidch
276221407Smarius	PHY_RESET(sc);
27759477Swpaul
278214012Smarius	/* Read the PHY's capabilities. */
279221407Smarius	sc->mii_capabilities = PHY_READ(sc, MII_BMSR) & sc->mii_capmask;
280214012Smarius	if (sc->mii_capabilities & BMSR_EXTSTAT)
281214012Smarius		sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR);
28259477Swpaul	device_printf(dev, " ");
28359477Swpaul
284170391Sdavidch	/* Add the supported media types */
285170391Sdavidch	if ((sc->mii_flags & MIIF_HAVEFIBER) == 0) {
286215297Smarius		mii_phy_add_media(sc);
287215297Smarius		printf("\n");
288170391Sdavidch	} else {
289215297Smarius		sc->mii_anegticks = MII_ANEGTICKS_GIGE;
290281816Sglebius		ifmedia_add(&sc->mii_pdata->mii_media,
291281816Sglebius		    IFM_MAKEWORD(IFM_ETHER, IFM_1000_SX, IFM_FDX, sc->mii_inst),
292281816Sglebius		    0, NULL);
293170391Sdavidch		printf("1000baseSX-FDX, ");
294281816Sglebius		/*
295281816Sglebius		 * 2.5G support is a software enabled feature
296281816Sglebius		 * on the 5708S and 5709S.
297281816Sglebius		 */
298281816Sglebius		if (bce_sc && (bce_sc->bce_phy_flags &
299281816Sglebius		    BCE_PHY_2_5G_CAPABLE_FLAG)) {
300281816Sglebius			ifmedia_add(&sc->mii_pdata->mii_media,
301281816Sglebius			    IFM_MAKEWORD(IFM_ETHER, IFM_2500_SX, IFM_FDX,
302281816Sglebius			    sc->mii_inst), 0, NULL);
303170391Sdavidch			printf("2500baseSX-FDX, ");
304204941Ssobomax		} else if ((bsc->serdes_flags & BRGPHY_5708S) && bce_sc &&
305204989Ssobomax		    (detect_hs21(bce_sc) != 0)) {
306204941Ssobomax			/*
307204941Ssobomax			 * There appears to be certain silicon revision
308204989Ssobomax			 * in IBM HS21 blades that is having issues with
309204941Ssobomax			 * this driver wating for the auto-negotiation to
310204941Ssobomax			 * complete. This happens with a specific chip id
311204941Ssobomax			 * only and when the 1000baseSX-FDX is the only
312204941Ssobomax			 * mode. Workaround this issue since it's unlikely
313204941Ssobomax			 * to be ever addressed.
314204941Ssobomax			 */
315204941Ssobomax			printf("auto-neg workaround, ");
316204941Ssobomax			bsc->serdes_flags |= BRGPHY_NOANWAIT;
317170391Sdavidch		}
318281816Sglebius		ifmedia_add(&sc->mii_pdata->mii_media, IFM_MAKEWORD(IFM_ETHER,
319281816Sglebius		    IFM_AUTO, 0, sc->mii_inst), 0, NULL);
320215297Smarius		printf("auto\n");
321170391Sdavidch	}
322170391Sdavidch
32359477Swpaul	MIIBUS_MEDIAINIT(sc->mii_dev);
324164830Smarius	return (0);
32559477Swpaul}
32659477Swpaul
32784145Sjlemonstatic int
328150763Simpbrgphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
32959477Swpaul{
33059477Swpaul	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
331170391Sdavidch	int val;
33259477Swpaul
33359477Swpaul	switch (cmd) {
33459477Swpaul	case MII_POLLSTAT:
33559477Swpaul		break;
33659477Swpaul	case MII_MEDIACHG:
337170391Sdavidch		/* Todo: Why is this here?  Is it really needed? */
338221407Smarius		PHY_RESET(sc);	/* XXX hardware bug work-around */
33959477Swpaul
34059477Swpaul		switch (IFM_SUBTYPE(ife->ifm_media)) {
34159477Swpaul		case IFM_AUTO:
342215297Smarius			brgphy_mii_phy_auto(sc, ife->ifm_media);
34359477Swpaul			break;
344170391Sdavidch		case IFM_2500_SX:
345170391Sdavidch		case IFM_1000_SX:
34695673Sphk		case IFM_1000_T:
34783029Swpaul		case IFM_100_TX:
34883029Swpaul		case IFM_10_T:
349215297Smarius			brgphy_setmedia(sc, ife->ifm_media);
35059477Swpaul			break;
35159477Swpaul		default:
352213364Smarius			return (EINVAL);
35359477Swpaul		}
35459477Swpaul		break;
35559477Swpaul	case MII_TICK:
356170391Sdavidch		/* Bail if autoneg isn't in process. */
357165343Soleg		if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) {
358170391Sdavidch			sc->mii_ticks = 0;
35984145Sjlemon			break;
360165343Soleg		}
36159477Swpaul
36259477Swpaul		/*
36359477Swpaul		 * Check to see if we have link.  If we do, we don't
364165343Soleg		 * need to restart the autonegotiation process.
36559477Swpaul		 */
366215297Smarius		val = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
367170391Sdavidch		if (val & BMSR_LINK) {
368165343Soleg			sc->mii_ticks = 0;	/* Reset autoneg timer. */
36959477Swpaul			break;
370165343Soleg		}
37159477Swpaul
372165343Soleg		/* Announce link loss right after it happens. */
373165343Soleg		if (sc->mii_ticks++ == 0)
374128870Sandre			break;
375164830Smarius
376165343Soleg		/* Only retry autonegotiation every mii_anegticks seconds. */
377165343Soleg		if (sc->mii_ticks <= sc->mii_anegticks)
378181619Syongari			break;
379165343Soleg
380170391Sdavidch
381170391Sdavidch		/* Retry autonegotiation */
38284145Sjlemon		sc->mii_ticks = 0;
383215297Smarius		brgphy_mii_phy_auto(sc, ife->ifm_media);
384153234Soleg		break;
38559477Swpaul	}
38659477Swpaul
38759477Swpaul	/* Update the media status. */
388221407Smarius	PHY_STATUS(sc);
38959477Swpaul
390114628Sps	/*
391114628Sps	 * Callback if something changed. Note that we need to poke
392114628Sps	 * the DSP on the Broadcom PHYs if the media changes.
393114628Sps	 */
394164830Smarius	if (sc->mii_media_active != mii->mii_media_active ||
395114628Sps	    sc->mii_media_status != mii->mii_media_status ||
396114628Sps	    cmd == MII_MEDIACHG) {
397221407Smarius		switch (sc->mii_mpd_oui) {
398170391Sdavidch		case MII_OUI_BROADCOM:
399221407Smarius			switch (sc->mii_mpd_model) {
400221407Smarius			case MII_MODEL_BROADCOM_BCM5400:
401166031Sjkim				bcm5401_load_dspcode(sc);
402170391Sdavidch				break;
403221407Smarius			case MII_MODEL_BROADCOM_BCM5401:
404221407Smarius				if (sc->mii_mpd_rev == 1 || sc->mii_mpd_rev == 3)
405170391Sdavidch					bcm5401_load_dspcode(sc);
406170391Sdavidch				break;
407221407Smarius			case MII_MODEL_BROADCOM_BCM5411:
408170391Sdavidch				bcm5411_load_dspcode(sc);
409170391Sdavidch				break;
410221407Smarius			case MII_MODEL_BROADCOM_BCM54K2:
411204144Smarius				bcm54k2_load_dspcode(sc);
412204144Smarius				break;
413170391Sdavidch			}
414166031Sjkim			break;
415114628Sps		}
416114628Sps	}
417128870Sandre	mii_phy_update(sc, cmd);
418213364Smarius	return (0);
41959477Swpaul}
42059477Swpaul
421179772Sdavidch/****************************************************************************/
422179772Sdavidch/* Sets the PHY link speed.                                                 */
423179772Sdavidch/*                                                                          */
424179772Sdavidch/* Returns:                                                                 */
425179772Sdavidch/*   None                                                                   */
426179772Sdavidch/****************************************************************************/
42784145Sjlemonstatic void
428215297Smariusbrgphy_setmedia(struct mii_softc *sc, int media)
429165360Sjkim{
430170391Sdavidch	int bmcr = 0, gig;
431165360Sjkim
432165360Sjkim	switch (IFM_SUBTYPE(media)) {
433170391Sdavidch	case IFM_2500_SX:
434170391Sdavidch		break;
435170391Sdavidch	case IFM_1000_SX:
436165360Sjkim	case IFM_1000_T:
437165360Sjkim		bmcr = BRGPHY_S1000;
438165360Sjkim		break;
439165360Sjkim	case IFM_100_TX:
440165360Sjkim		bmcr = BRGPHY_S100;
441165360Sjkim		break;
442165360Sjkim	case IFM_10_T:
443165360Sjkim	default:
444165360Sjkim		bmcr = BRGPHY_S10;
445165360Sjkim		break;
446165360Sjkim	}
447179772Sdavidch
448217413Smarius	if ((media & IFM_FDX) != 0) {
449165360Sjkim		bmcr |= BRGPHY_BMCR_FDX;
450165360Sjkim		gig = BRGPHY_1000CTL_AFD;
451165360Sjkim	} else {
452165360Sjkim		gig = BRGPHY_1000CTL_AHD;
453165360Sjkim	}
454165360Sjkim
455215297Smarius	/* Force loopback to disconnect PHY from Ethernet medium. */
456170391Sdavidch	brgphy_enable_loopback(sc);
457179772Sdavidch
458165360Sjkim	PHY_WRITE(sc, BRGPHY_MII_1000CTL, 0);
459179772Sdavidch	PHY_WRITE(sc, BRGPHY_MII_ANAR, BRGPHY_SEL_TYPE);
460165360Sjkim
461215297Smarius	if (IFM_SUBTYPE(media) != IFM_1000_T &&
462215297Smarius	    IFM_SUBTYPE(media) != IFM_1000_SX) {
463215297Smarius		PHY_WRITE(sc, BRGPHY_MII_BMCR, bmcr);
464215297Smarius		return;
465215297Smarius	}
466165360Sjkim
467215297Smarius	if (IFM_SUBTYPE(media) == IFM_1000_T) {
468215297Smarius		gig |= BRGPHY_1000CTL_MSE;
469215297Smarius		if ((media & IFM_ETH_MASTER) != 0)
470215297Smarius			gig |= BRGPHY_1000CTL_MSC;
471215297Smarius	}
472165360Sjkim	PHY_WRITE(sc, BRGPHY_MII_1000CTL, gig);
473165360Sjkim	PHY_WRITE(sc, BRGPHY_MII_BMCR,
474165360Sjkim	    bmcr | BRGPHY_BMCR_AUTOEN | BRGPHY_BMCR_STARTNEG);
475165360Sjkim}
476165360Sjkim
477179772Sdavidch/****************************************************************************/
478179772Sdavidch/* Set the media status based on the PHY settings.                          */
479179772Sdavidch/*                                                                          */
480179772Sdavidch/* Returns:                                                                 */
481179772Sdavidch/*   None                                                                   */
482179772Sdavidch/****************************************************************************/
483165360Sjkimstatic void
484150763Simpbrgphy_status(struct mii_softc *sc)
48559477Swpaul{
486170391Sdavidch	struct brgphy_softc *bsc = (struct brgphy_softc *)sc;
48759477Swpaul	struct mii_data *mii = sc->mii_pdata;
488215297Smarius	int aux, bmcr, bmsr, val, xstat;
489215297Smarius	u_int flowstat;
49059477Swpaul
49159477Swpaul	mii->mii_media_status = IFM_AVALID;
49259477Swpaul	mii->mii_media_active = IFM_ETHER;
49359477Swpaul
494170391Sdavidch	bmsr = PHY_READ(sc, BRGPHY_MII_BMSR) | PHY_READ(sc, BRGPHY_MII_BMSR);
495181618Syongari	bmcr = PHY_READ(sc, BRGPHY_MII_BMCR);
496165360Sjkim
497170391Sdavidch	if (bmcr & BRGPHY_BMCR_LOOP) {
49859477Swpaul		mii->mii_media_active |= IFM_LOOP;
499170391Sdavidch	}
50059477Swpaul
501167728Sjkim	if ((bmcr & BRGPHY_BMCR_AUTOEN) &&
502204941Ssobomax	    (bmsr & BRGPHY_BMSR_ACOMP) == 0 &&
503204941Ssobomax	    (bsc->serdes_flags & BRGPHY_NOANWAIT) == 0) {
504167728Sjkim		/* Erg, still trying, I guess... */
505167728Sjkim		mii->mii_media_active |= IFM_NONE;
506215297Smarius		return;
507165360Sjkim	}
50859477Swpaul
509170391Sdavidch	if ((sc->mii_flags & MIIF_HAVEFIBER) == 0) {
510215297Smarius		/*
511215297Smarius		 * NB: reading the ANAR, ANLPAR or 1000STS after the AUXSTS
512215297Smarius		 * wedges at least the PHY of BCM5704 (but not others).
513215297Smarius		 */
514215297Smarius		flowstat = mii_phy_flowstatus(sc);
515215297Smarius		xstat = PHY_READ(sc, BRGPHY_MII_1000STS);
516170391Sdavidch		aux = PHY_READ(sc, BRGPHY_MII_AUXSTS);
517170391Sdavidch
518170391Sdavidch		/* If copper link is up, get the negotiated speed/duplex. */
519170391Sdavidch		if (aux & BRGPHY_AUXSTS_LINK) {
520170391Sdavidch			mii->mii_media_status |= IFM_ACTIVE;
521170391Sdavidch			switch (aux & BRGPHY_AUXSTS_AN_RES) {
522170391Sdavidch			case BRGPHY_RES_1000FD:
523170391Sdavidch				mii->mii_media_active |= IFM_1000_T | IFM_FDX; 	break;
524170391Sdavidch			case BRGPHY_RES_1000HD:
525170391Sdavidch				mii->mii_media_active |= IFM_1000_T | IFM_HDX; 	break;
526170391Sdavidch			case BRGPHY_RES_100FD:
527170391Sdavidch				mii->mii_media_active |= IFM_100_TX | IFM_FDX; break;
528170391Sdavidch			case BRGPHY_RES_100T4:
529170391Sdavidch				mii->mii_media_active |= IFM_100_T4; break;
530170391Sdavidch			case BRGPHY_RES_100HD:
531170391Sdavidch				mii->mii_media_active |= IFM_100_TX | IFM_HDX; 	break;
532170391Sdavidch			case BRGPHY_RES_10FD:
533170391Sdavidch				mii->mii_media_active |= IFM_10_T | IFM_FDX; break;
534170391Sdavidch			case BRGPHY_RES_10HD:
535170391Sdavidch				mii->mii_media_active |= IFM_10_T | IFM_HDX; break;
536170391Sdavidch			default:
537170391Sdavidch				mii->mii_media_active |= IFM_NONE; break;
538170391Sdavidch			}
539215297Smarius
540215297Smarius			if ((mii->mii_media_active & IFM_FDX) != 0)
541215297Smarius				mii->mii_media_active |= flowstat;
542215297Smarius
543215297Smarius			if (IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T &&
544215297Smarius			    (xstat & BRGPHY_1000STS_MSR) != 0)
545215297Smarius				mii->mii_media_active |= IFM_ETH_MASTER;
54683029Swpaul		}
547170391Sdavidch	} else {
548215297Smarius		/* Todo: Add support for flow control. */
549170391Sdavidch		/* If serdes link is up, get the negotiated speed/duplex. */
550170391Sdavidch		if (bmsr & BRGPHY_BMSR_LINK) {
551170391Sdavidch			mii->mii_media_status |= IFM_ACTIVE;
552170391Sdavidch		}
553170391Sdavidch
554170391Sdavidch		/* Check the link speed/duplex based on the PHY type. */
555170391Sdavidch		if (bsc->serdes_flags & BRGPHY_5706S) {
556170391Sdavidch			mii->mii_media_active |= IFM_1000_SX;
557170391Sdavidch
558170391Sdavidch			/* If autoneg enabled, read negotiated duplex settings */
559170391Sdavidch			if (bmcr & BRGPHY_BMCR_AUTOEN) {
560170391Sdavidch				val = PHY_READ(sc, BRGPHY_SERDES_ANAR) & PHY_READ(sc, BRGPHY_SERDES_ANLPAR);
561170391Sdavidch				if (val & BRGPHY_SERDES_ANAR_FDX)
562170391Sdavidch					mii->mii_media_active |= IFM_FDX;
563170391Sdavidch				else
564170391Sdavidch					mii->mii_media_active |= IFM_HDX;
565170391Sdavidch			}
566170391Sdavidch		} else if (bsc->serdes_flags & BRGPHY_5708S) {
567170391Sdavidch			PHY_WRITE(sc, BRGPHY_5708S_BLOCK_ADDR, BRGPHY_5708S_DIG_PG0);
568170391Sdavidch			xstat = PHY_READ(sc, BRGPHY_5708S_PG0_1000X_STAT1);
569170391Sdavidch
570212307Syongari			/* Check for MRBE auto-negotiated speed results. */
571170391Sdavidch			switch (xstat & BRGPHY_5708S_PG0_1000X_STAT1_SPEED_MASK) {
572181617Syongari			case BRGPHY_5708S_PG0_1000X_STAT1_SPEED_10:
573170391Sdavidch				mii->mii_media_active |= IFM_10_FL; break;
574181617Syongari			case BRGPHY_5708S_PG0_1000X_STAT1_SPEED_100:
575170391Sdavidch				mii->mii_media_active |= IFM_100_FX; break;
576181617Syongari			case BRGPHY_5708S_PG0_1000X_STAT1_SPEED_1G:
577170391Sdavidch				mii->mii_media_active |= IFM_1000_SX; break;
578181617Syongari			case BRGPHY_5708S_PG0_1000X_STAT1_SPEED_25G:
579170391Sdavidch				mii->mii_media_active |= IFM_2500_SX; break;
580170391Sdavidch			}
581170391Sdavidch
582212307Syongari			/* Check for MRBE auto-negotiated duplex results. */
583170391Sdavidch			if (xstat & BRGPHY_5708S_PG0_1000X_STAT1_FDX)
584170391Sdavidch				mii->mii_media_active |= IFM_FDX;
585170391Sdavidch			else
586170391Sdavidch				mii->mii_media_active |= IFM_HDX;
587212307Syongari		} else if (bsc->serdes_flags & BRGPHY_5709S) {
588212307Syongari			/* Select GP Status Block of the AN MMD, get autoneg results. */
589212307Syongari			PHY_WRITE(sc, BRGPHY_BLOCK_ADDR, BRGPHY_BLOCK_ADDR_GP_STATUS);
590205299Sdavidch			xstat = PHY_READ(sc, BRGPHY_GP_STATUS_TOP_ANEG_STATUS);
591205299Sdavidch
592212307Syongari			/* Restore IEEE0 block (assumed in all brgphy(4) code). */
593212307Syongari			PHY_WRITE(sc, BRGPHY_BLOCK_ADDR, BRGPHY_BLOCK_ADDR_COMBO_IEEE0);
594205299Sdavidch
595212307Syongari			/* Check for MRBE auto-negotiated speed results. */
596212307Syongari			switch (xstat & BRGPHY_GP_STATUS_TOP_ANEG_SPEED_MASK) {
597212307Syongari				case BRGPHY_GP_STATUS_TOP_ANEG_SPEED_10:
598212307Syongari					mii->mii_media_active |= IFM_10_FL; break;
599212307Syongari				case BRGPHY_GP_STATUS_TOP_ANEG_SPEED_100:
600212307Syongari					mii->mii_media_active |= IFM_100_FX; break;
601212307Syongari				case BRGPHY_GP_STATUS_TOP_ANEG_SPEED_1G:
602212307Syongari					mii->mii_media_active |= IFM_1000_SX; break;
603212307Syongari				case BRGPHY_GP_STATUS_TOP_ANEG_SPEED_25G:
604212307Syongari					mii->mii_media_active |= IFM_2500_SX; break;
605205299Sdavidch			}
606205299Sdavidch
607212307Syongari			/* Check for MRBE auto-negotiated duplex results. */
608205299Sdavidch			if (xstat & BRGPHY_GP_STATUS_TOP_ANEG_FDX)
609205299Sdavidch				mii->mii_media_active |= IFM_FDX;
610205299Sdavidch			else
611205299Sdavidch				mii->mii_media_active |= IFM_HDX;
612212307Syongari		}
613170391Sdavidch	}
61459477Swpaul}
61559477Swpaul
616170391Sdavidchstatic void
617215297Smariusbrgphy_mii_phy_auto(struct mii_softc *sc, int media)
61859477Swpaul{
619215297Smarius	int anar, ktcr = 0;
62059477Swpaul
621221407Smarius	PHY_RESET(sc);
622170391Sdavidch
623170391Sdavidch	if ((sc->mii_flags & MIIF_HAVEFIBER) == 0) {
624215297Smarius		anar = BMSR_MEDIA_TO_ANAR(sc->mii_capabilities) | ANAR_CSMA;
625215297Smarius		if ((media & IFM_FLOW) != 0 ||
626215297Smarius		    (sc->mii_flags & MIIF_FORCEPAUSE) != 0)
627215297Smarius			anar |= BRGPHY_ANAR_PC | BRGPHY_ANAR_ASP;
628215297Smarius		PHY_WRITE(sc, BRGPHY_MII_ANAR, anar);
629244481Syongari		ktcr = BRGPHY_1000CTL_AFD | BRGPHY_1000CTL_AHD;
630244481Syongari		if (sc->mii_mpd_model == MII_MODEL_BROADCOM_BCM5701)
631244481Syongari			ktcr |= BRGPHY_1000CTL_MSE | BRGPHY_1000CTL_MSC;
632244481Syongari		PHY_WRITE(sc, BRGPHY_MII_1000CTL, ktcr);
633244481Syongari		PHY_READ(sc, BRGPHY_MII_1000CTL);
634170391Sdavidch	} else {
635215297Smarius		anar = BRGPHY_SERDES_ANAR_FDX | BRGPHY_SERDES_ANAR_HDX;
636215297Smarius		if ((media & IFM_FLOW) != 0 ||
637215297Smarius		    (sc->mii_flags & MIIF_FORCEPAUSE) != 0)
638215297Smarius			anar |= BRGPHY_SERDES_ANAR_BOTH_PAUSE;
639215297Smarius		PHY_WRITE(sc, BRGPHY_SERDES_ANAR, anar);
640170391Sdavidch	}
641170391Sdavidch
642215297Smarius	PHY_WRITE(sc, BRGPHY_MII_BMCR, BRGPHY_BMCR_AUTOEN |
643215297Smarius	    BRGPHY_BMCR_STARTNEG);
644165360Sjkim	PHY_WRITE(sc, BRGPHY_MII_IMR, 0xFF00);
64559477Swpaul}
646114590Sps
647170391Sdavidch/* Enable loopback to force the link down. */
648114590Spsstatic void
649170391Sdavidchbrgphy_enable_loopback(struct mii_softc *sc)
650114590Sps{
651114590Sps	int i;
652114590Sps
653114590Sps	PHY_WRITE(sc, BRGPHY_MII_BMCR, BRGPHY_BMCR_LOOP);
654114590Sps	for (i = 0; i < 15000; i++) {
655170391Sdavidch		if (!(PHY_READ(sc, BRGPHY_MII_BMSR) & BRGPHY_BMSR_LINK))
656114590Sps			break;
657114590Sps		DELAY(10);
658114590Sps	}
659114590Sps}
660114590Sps
661114590Sps/* Turn off tap power management on 5401. */
662114590Spsstatic void
663114590Spsbcm5401_load_dspcode(struct mii_softc *sc)
664114590Sps{
665114590Sps	static const struct {
666114590Sps		int		reg;
667114590Sps		uint16_t	val;
668114590Sps	} dspcode[] = {
669114590Sps		{ BRGPHY_MII_AUXCTL,		0x0c20 },
670114590Sps		{ BRGPHY_MII_DSP_ADDR_REG,	0x0012 },
671114590Sps		{ BRGPHY_MII_DSP_RW_PORT,	0x1804 },
672114590Sps		{ BRGPHY_MII_DSP_ADDR_REG,	0x0013 },
673114590Sps		{ BRGPHY_MII_DSP_RW_PORT,	0x1204 },
674114590Sps		{ BRGPHY_MII_DSP_ADDR_REG,	0x8006 },
675114590Sps		{ BRGPHY_MII_DSP_RW_PORT,	0x0132 },
676114590Sps		{ BRGPHY_MII_DSP_ADDR_REG,	0x8006 },
677114590Sps		{ BRGPHY_MII_DSP_RW_PORT,	0x0232 },
678114590Sps		{ BRGPHY_MII_DSP_ADDR_REG,	0x201f },
679114590Sps		{ BRGPHY_MII_DSP_RW_PORT,	0x0a20 },
680114590Sps		{ 0,				0 },
681114590Sps	};
682114590Sps	int i;
683114590Sps
684114590Sps	for (i = 0; dspcode[i].reg != 0; i++)
685114590Sps		PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
686114590Sps	DELAY(40);
687114590Sps}
688114590Sps
689114590Spsstatic void
690114590Spsbcm5411_load_dspcode(struct mii_softc *sc)
691114590Sps{
692114590Sps	static const struct {
693114590Sps		int		reg;
694114590Sps		uint16_t	val;
695114590Sps	} dspcode[] = {
696114590Sps		{ 0x1c,				0x8c23 },
697114590Sps		{ 0x1c,				0x8ca3 },
698114590Sps		{ 0x1c,				0x8c23 },
699114590Sps		{ 0,				0 },
700114590Sps	};
701114590Sps	int i;
702114590Sps
703114590Sps	for (i = 0; dspcode[i].reg != 0; i++)
704114590Sps		PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
705114590Sps}
706114590Sps
707204144Smariusvoid
708204144Smariusbcm54k2_load_dspcode(struct mii_softc *sc)
709204144Smarius{
710204144Smarius	static const struct {
711204144Smarius		int		reg;
712204144Smarius		uint16_t	val;
713204144Smarius	} dspcode[] = {
714204144Smarius		{ 4,				0x01e1 },
715204144Smarius		{ 9,				0x0300 },
716204144Smarius		{ 0,				0 },
717204144Smarius	};
718204144Smarius	int i;
719204144Smarius
720204144Smarius	for (i = 0; dspcode[i].reg != 0; i++)
721204144Smarius		PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
722204144Smarius
723204144Smarius}
724204144Smarius
725114590Spsstatic void
726166676Sjkimbrgphy_fixup_5704_a0_bug(struct mii_softc *sc)
727114590Sps{
728114590Sps	static const struct {
729114590Sps		int		reg;
730114590Sps		uint16_t	val;
731114590Sps	} dspcode[] = {
732166676Sjkim		{ 0x1c,				0x8d68 },
733166676Sjkim		{ 0x1c,				0x8d68 },
734114590Sps		{ 0,				0 },
735114590Sps	};
736114590Sps	int i;
737114590Sps
738114590Sps	for (i = 0; dspcode[i].reg != 0; i++)
739114590Sps		PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
740114590Sps}
741114590Sps
742114590Spsstatic void
743166676Sjkimbrgphy_fixup_adc_bug(struct mii_softc *sc)
744114590Sps{
745114590Sps	static const struct {
746114590Sps		int		reg;
747166676Sjkim		uint16_t	val;
748114590Sps	} dspcode[] = {
749166676Sjkim		{ BRGPHY_MII_AUXCTL,		0x0c00 },
750166676Sjkim		{ BRGPHY_MII_DSP_ADDR_REG,	0x201f },
751166676Sjkim		{ BRGPHY_MII_DSP_RW_PORT,	0x2aaa },
752114590Sps		{ 0,				0 },
753114590Sps	};
754114590Sps	int i;
755114590Sps
756114590Sps	for (i = 0; dspcode[i].reg != 0; i++)
757114590Sps		PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
758114590Sps}
759114590Sps
760114590Spsstatic void
761166673Sjkimbrgphy_fixup_adjust_trim(struct mii_softc *sc)
762166673Sjkim{
763166673Sjkim	static const struct {
764166673Sjkim		int		reg;
765166673Sjkim		uint16_t	val;
766166673Sjkim	} dspcode[] = {
767166673Sjkim		{ BRGPHY_MII_AUXCTL,		0x0c00 },
768166673Sjkim		{ BRGPHY_MII_DSP_ADDR_REG,	0x000a },
769166673Sjkim		{ BRGPHY_MII_DSP_RW_PORT,	0x110b },
770170391Sdavidch		{ BRGPHY_MII_TEST1,			0x0014 },
771166673Sjkim		{ BRGPHY_MII_AUXCTL,		0x0400 },
772166673Sjkim		{ 0,				0 },
773166673Sjkim	};
774166673Sjkim	int i;
775166673Sjkim
776166673Sjkim	for (i = 0; dspcode[i].reg != 0; i++)
777166673Sjkim		PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
778166673Sjkim}
779166673Sjkim
780166673Sjkimstatic void
781166031Sjkimbrgphy_fixup_ber_bug(struct mii_softc *sc)
782135772Sps{
783135772Sps	static const struct {
784135772Sps		int		reg;
785166676Sjkim		uint16_t	val;
786135772Sps	} dspcode[] = {
787166665Sjkim		{ BRGPHY_MII_AUXCTL,		0x0c00 },
788166665Sjkim		{ BRGPHY_MII_DSP_ADDR_REG,	0x000a },
789166665Sjkim		{ BRGPHY_MII_DSP_RW_PORT,	0x310b },
790166665Sjkim		{ BRGPHY_MII_DSP_ADDR_REG,	0x201f },
791166665Sjkim		{ BRGPHY_MII_DSP_RW_PORT,	0x9506 },
792166665Sjkim		{ BRGPHY_MII_DSP_ADDR_REG,	0x401f },
793166665Sjkim		{ BRGPHY_MII_DSP_RW_PORT,	0x14e2 },
794166665Sjkim		{ BRGPHY_MII_AUXCTL,		0x0400 },
795135772Sps		{ 0,				0 },
796135772Sps	};
797135772Sps	int i;
798135772Sps
799135772Sps	for (i = 0; dspcode[i].reg != 0; i++)
800135772Sps		PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
801135772Sps}
802135772Sps
803135772Spsstatic void
804166677Sjkimbrgphy_fixup_crc_bug(struct mii_softc *sc)
805166677Sjkim{
806166677Sjkim	static const struct {
807166677Sjkim		int		reg;
808166677Sjkim		uint16_t	val;
809166677Sjkim	} dspcode[] = {
810166716Sjkim		{ BRGPHY_MII_DSP_RW_PORT,	0x0a75 },
811166677Sjkim		{ 0x1c,				0x8c68 },
812166677Sjkim		{ 0x1c,				0x8d68 },
813166677Sjkim		{ 0x1c,				0x8c68 },
814166677Sjkim		{ 0,				0 },
815166677Sjkim	};
816166677Sjkim	int i;
817166677Sjkim
818166677Sjkim	for (i = 0; dspcode[i].reg != 0; i++)
819166677Sjkim		PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
820166677Sjkim}
821166677Sjkim
822166677Sjkimstatic void
823166031Sjkimbrgphy_fixup_jitter_bug(struct mii_softc *sc)
824166031Sjkim{
825166031Sjkim	static const struct {
826166031Sjkim		int		reg;
827166031Sjkim		uint16_t	val;
828166031Sjkim	} dspcode[] = {
829166031Sjkim		{ BRGPHY_MII_AUXCTL,		0x0c00 },
830166031Sjkim		{ BRGPHY_MII_DSP_ADDR_REG,	0x000a },
831166031Sjkim		{ BRGPHY_MII_DSP_RW_PORT,	0x010b },
832166031Sjkim		{ BRGPHY_MII_AUXCTL,		0x0400 },
833166031Sjkim		{ 0,				0 },
834166031Sjkim	};
835166031Sjkim	int i;
836166031Sjkim
837166031Sjkim	for (i = 0; dspcode[i].reg != 0; i++)
838166031Sjkim		PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
839166031Sjkim}
840166031Sjkim
841166031Sjkimstatic void
842179772Sdavidchbrgphy_fixup_disable_early_dac(struct mii_softc *sc)
843179772Sdavidch{
844179772Sdavidch	uint32_t val;
845179772Sdavidch
846179772Sdavidch	PHY_WRITE(sc, BRGPHY_MII_DSP_ADDR_REG, 0x0f08);
847179772Sdavidch	val = PHY_READ(sc, BRGPHY_MII_DSP_RW_PORT);
848179772Sdavidch	val &= ~(1 << 8);
849179772Sdavidch	PHY_WRITE(sc, BRGPHY_MII_DSP_RW_PORT, val);
850179772Sdavidch
851179772Sdavidch}
852179772Sdavidch
853179772Sdavidchstatic void
854166032Sjkimbrgphy_ethernet_wirespeed(struct mii_softc *sc)
855166032Sjkim{
856166676Sjkim	uint32_t	val;
857166032Sjkim
858166032Sjkim	/* Enable Ethernet@WireSpeed. */
859166032Sjkim	PHY_WRITE(sc, BRGPHY_MII_AUXCTL, 0x7007);
860166032Sjkim	val = PHY_READ(sc, BRGPHY_MII_AUXCTL);
861166032Sjkim	PHY_WRITE(sc, BRGPHY_MII_AUXCTL, val | (1 << 15) | (1 << 4));
862166032Sjkim}
863166032Sjkim
864166032Sjkimstatic void
865166032Sjkimbrgphy_jumbo_settings(struct mii_softc *sc, u_long mtu)
866166032Sjkim{
867166676Sjkim	uint32_t	val;
868166032Sjkim
869166032Sjkim	/* Set or clear jumbo frame settings in the PHY. */
870166032Sjkim	if (mtu > ETHER_MAX_LEN) {
871221407Smarius		if (sc->mii_mpd_model == MII_MODEL_BROADCOM_BCM5401) {
872166680Sjkim			/* BCM5401 PHY cannot read-modify-write. */
873166666Sjkim			PHY_WRITE(sc, BRGPHY_MII_AUXCTL, 0x4c20);
874166680Sjkim		} else {
875166666Sjkim			PHY_WRITE(sc, BRGPHY_MII_AUXCTL, 0x7);
876166666Sjkim			val = PHY_READ(sc, BRGPHY_MII_AUXCTL);
877166666Sjkim			PHY_WRITE(sc, BRGPHY_MII_AUXCTL,
878166666Sjkim			    val | BRGPHY_AUXCTL_LONG_PKT);
879166666Sjkim		}
880166032Sjkim
881166032Sjkim		val = PHY_READ(sc, BRGPHY_MII_PHY_EXTCTL);
882166032Sjkim		PHY_WRITE(sc, BRGPHY_MII_PHY_EXTCTL,
883166032Sjkim		    val | BRGPHY_PHY_EXTCTL_HIGH_LA);
884166032Sjkim	} else {
885166032Sjkim		PHY_WRITE(sc, BRGPHY_MII_AUXCTL, 0x7);
886166032Sjkim		val = PHY_READ(sc, BRGPHY_MII_AUXCTL);
887166032Sjkim		PHY_WRITE(sc, BRGPHY_MII_AUXCTL,
888166032Sjkim		    val & ~(BRGPHY_AUXCTL_LONG_PKT | 0x7));
889166032Sjkim
890166032Sjkim		val = PHY_READ(sc, BRGPHY_MII_PHY_EXTCTL);
891181617Syongari		PHY_WRITE(sc, BRGPHY_MII_PHY_EXTCTL,
892170391Sdavidch			val & ~BRGPHY_PHY_EXTCTL_HIGH_LA);
893166032Sjkim	}
894166032Sjkim}
895166032Sjkim
896166032Sjkimstatic void
897114590Spsbrgphy_reset(struct mii_softc *sc)
898114590Sps{
899166037Sjkim	struct bge_softc *bge_sc = NULL;
900166037Sjkim	struct bce_softc *bce_sc = NULL;
901266974Smarcel	if_t ifp;
902225014Smarius	int i, val;
903114590Sps
904225014Smarius	/*
905225014Smarius	 * Perform a reset.  Note that at least some Broadcom PHYs default to
906225014Smarius	 * being powered down as well as isolated after a reset but don't work
907225014Smarius	 * if one or both of these bits are cleared.  However, they just work
908225014Smarius	 * fine if both bits remain set, so we don't use mii_phy_reset() here.
909225014Smarius	 */
910225014Smarius	PHY_WRITE(sc, BRGPHY_MII_BMCR, BRGPHY_BMCR_RESET);
911114590Sps
912225014Smarius	/* Wait 100ms for it to complete. */
913225014Smarius	for (i = 0; i < 100; i++) {
914225014Smarius		if ((PHY_READ(sc, BRGPHY_MII_BMCR) & BRGPHY_BMCR_RESET) == 0)
915225014Smarius			break;
916225014Smarius		DELAY(1000);
917225014Smarius	}
918225014Smarius
919179772Sdavidch	/* Handle any PHY specific procedures following the reset. */
920221407Smarius	switch (sc->mii_mpd_oui) {
921170391Sdavidch	case MII_OUI_BROADCOM:
922221407Smarius		switch (sc->mii_mpd_model) {
923221407Smarius		case MII_MODEL_BROADCOM_BCM5400:
924166031Sjkim			bcm5401_load_dspcode(sc);
925170391Sdavidch			break;
926221407Smarius		case MII_MODEL_BROADCOM_BCM5401:
927221407Smarius			if (sc->mii_mpd_rev == 1 || sc->mii_mpd_rev == 3)
928170391Sdavidch				bcm5401_load_dspcode(sc);
929170391Sdavidch			break;
930221407Smarius		case MII_MODEL_BROADCOM_BCM5411:
931170391Sdavidch			bcm5411_load_dspcode(sc);
932170391Sdavidch			break;
933221407Smarius		case MII_MODEL_BROADCOM_BCM54K2:
934204144Smarius			bcm54k2_load_dspcode(sc);
935204144Smarius			break;
936170391Sdavidch		}
937166031Sjkim		break;
938241437Syongari	case MII_OUI_BROADCOM3:
939241437Syongari		switch (sc->mii_mpd_model) {
940241437Syongari		case MII_MODEL_BROADCOM3_BCM5717C:
941241437Syongari		case MII_MODEL_BROADCOM3_BCM5719C:
942241437Syongari		case MII_MODEL_BROADCOM3_BCM5720C:
943241437Syongari		case MII_MODEL_BROADCOM3_BCM57765:
944241437Syongari			return;
945241437Syongari		}
946241437Syongari		break;
947253481Syongari	case MII_OUI_BROADCOM4:
948253481Syongari		return;
949114590Sps	}
950114590Sps
951117659Swpaul	ifp = sc->mii_pdata->mii_ifp;
952117659Swpaul
953157642Sps	/* Find the driver associated with this PHY. */
954277093Sglebius	if (mii_phy_mac_match(sc, "bge"))
955277093Sglebius		bge_sc = mii_phy_mac_softc(sc);
956277093Sglebius	else if (mii_phy_mac_match(sc, "bce"))
957277093Sglebius		bce_sc = mii_phy_mac_softc(sc);
958117659Swpaul
959157642Sps	if (bge_sc) {
960166031Sjkim		/* Fix up various bugs */
961213464Syongari		if (bge_sc->bge_phy_flags & BGE_PHY_5704_A0_BUG)
962166676Sjkim			brgphy_fixup_5704_a0_bug(sc);
963213464Syongari		if (bge_sc->bge_phy_flags & BGE_PHY_ADC_BUG)
964166031Sjkim			brgphy_fixup_adc_bug(sc);
965213464Syongari		if (bge_sc->bge_phy_flags & BGE_PHY_ADJUST_TRIM)
966166673Sjkim			brgphy_fixup_adjust_trim(sc);
967213464Syongari		if (bge_sc->bge_phy_flags & BGE_PHY_BER_BUG)
968166031Sjkim			brgphy_fixup_ber_bug(sc);
969213464Syongari		if (bge_sc->bge_phy_flags & BGE_PHY_CRC_BUG)
970166677Sjkim			brgphy_fixup_crc_bug(sc);
971213464Syongari		if (bge_sc->bge_phy_flags & BGE_PHY_JITTER_BUG)
972166031Sjkim			brgphy_fixup_jitter_bug(sc);
973157642Sps
974231913Smarius		if (bge_sc->bge_flags & BGE_FLAG_JUMBO)
975266974Smarcel			brgphy_jumbo_settings(sc, if_getmtu(ifp));
976157642Sps
977221468Syongari		if ((bge_sc->bge_phy_flags & BGE_PHY_NO_WIRESPEED) == 0)
978166032Sjkim			brgphy_ethernet_wirespeed(sc);
979166032Sjkim
980166032Sjkim		/* Enable Link LED on Dell boxes */
981213464Syongari		if (bge_sc->bge_phy_flags & BGE_PHY_NO_3LED) {
982164830Smarius			PHY_WRITE(sc, BRGPHY_MII_PHY_EXTCTL,
983166032Sjkim			    PHY_READ(sc, BRGPHY_MII_PHY_EXTCTL) &
984166032Sjkim			    ~BRGPHY_PHY_EXTCTL_3_LED);
985157642Sps		}
986178667Sjhb
987178667Sjhb		/* Adjust output voltage (From Linux driver) */
988178667Sjhb		if (bge_sc->bge_asicrev == BGE_ASICREV_BCM5906)
989178667Sjhb			PHY_WRITE(sc, BRGPHY_MII_EPHY_PTEST, 0x12);
990166032Sjkim	} else if (bce_sc) {
991170391Sdavidch		if (BCE_CHIP_NUM(bce_sc) == BCE_CHIP_NUM_5708 &&
992176850Sdavidch			(bce_sc->bce_phy_flags & BCE_PHY_SERDES_FLAG)) {
993170391Sdavidch
994170391Sdavidch			/* Store autoneg capabilities/results in digital block (Page 0) */
995170391Sdavidch			PHY_WRITE(sc, BRGPHY_5708S_BLOCK_ADDR, BRGPHY_5708S_DIG3_PG2);
996181618Syongari			PHY_WRITE(sc, BRGPHY_5708S_PG2_DIGCTL_3_0,
997170391Sdavidch				BRGPHY_5708S_PG2_DIGCTL_3_0_USE_IEEE);
998170391Sdavidch			PHY_WRITE(sc, BRGPHY_5708S_BLOCK_ADDR, BRGPHY_5708S_DIG_PG0);
999170391Sdavidch
1000170391Sdavidch			/* Enable fiber mode and autodetection */
1001181618Syongari			PHY_WRITE(sc, BRGPHY_5708S_PG0_1000X_CTL1,
1002181618Syongari				PHY_READ(sc, BRGPHY_5708S_PG0_1000X_CTL1) |
1003181618Syongari				BRGPHY_5708S_PG0_1000X_CTL1_AUTODET_EN |
1004170391Sdavidch				BRGPHY_5708S_PG0_1000X_CTL1_FIBER_MODE);
1005170391Sdavidch
1006170391Sdavidch			/* Enable parallel detection */
1007181618Syongari			PHY_WRITE(sc, BRGPHY_5708S_PG0_1000X_CTL2,
1008181618Syongari				PHY_READ(sc, BRGPHY_5708S_PG0_1000X_CTL2) |
1009170391Sdavidch				BRGPHY_5708S_PG0_1000X_CTL2_PAR_DET_EN);
1010170391Sdavidch
1011170391Sdavidch			/* Advertise 2.5G support through next page during autoneg */
1012170391Sdavidch			if (bce_sc->bce_phy_flags & BCE_PHY_2_5G_CAPABLE_FLAG)
1013181618Syongari				PHY_WRITE(sc, BRGPHY_5708S_ANEG_NXT_PG_XMIT1,
1014181618Syongari					PHY_READ(sc, BRGPHY_5708S_ANEG_NXT_PG_XMIT1) |
1015170391Sdavidch					BRGPHY_5708S_ANEG_NXT_PG_XMIT1_25G);
1016170391Sdavidch
1017170391Sdavidch			/* Increase TX signal amplitude */
1018170391Sdavidch			if ((BCE_CHIP_ID(bce_sc) == BCE_CHIP_ID_5708_A0) ||
1019170391Sdavidch			    (BCE_CHIP_ID(bce_sc) == BCE_CHIP_ID_5708_B0) ||
1020170391Sdavidch			    (BCE_CHIP_ID(bce_sc) == BCE_CHIP_ID_5708_B1)) {
1021181618Syongari				PHY_WRITE(sc, BRGPHY_5708S_BLOCK_ADDR,
1022170391Sdavidch					BRGPHY_5708S_TX_MISC_PG5);
1023181618Syongari				PHY_WRITE(sc, BRGPHY_5708S_PG5_TXACTL1,
1024170391Sdavidch					PHY_READ(sc, BRGPHY_5708S_PG5_TXACTL1) & ~0x30);
1025181618Syongari				PHY_WRITE(sc, BRGPHY_5708S_BLOCK_ADDR,
1026170391Sdavidch					BRGPHY_5708S_DIG_PG0);
1027170391Sdavidch			}
1028170391Sdavidch
1029181618Syongari			/* Backplanes use special driver/pre-driver/pre-emphasis values. */
1030170391Sdavidch			if ((bce_sc->bce_shared_hw_cfg & BCE_SHARED_HW_CFG_PHY_BACKPLANE) &&
1031170391Sdavidch				(bce_sc->bce_port_hw_cfg & BCE_PORT_HW_CFG_CFG_TXCTL3_MASK)) {
1032181618Syongari					PHY_WRITE(sc, BRGPHY_5708S_BLOCK_ADDR,
1033170391Sdavidch						BRGPHY_5708S_TX_MISC_PG5);
1034181618Syongari					PHY_WRITE(sc, BRGPHY_5708S_PG5_TXACTL3,
1035181618Syongari						bce_sc->bce_port_hw_cfg &
1036170391Sdavidch						BCE_PORT_HW_CFG_CFG_TXCTL3_MASK);
1037181618Syongari					PHY_WRITE(sc, BRGPHY_5708S_BLOCK_ADDR,
1038170391Sdavidch						BRGPHY_5708S_DIG_PG0);
1039170391Sdavidch			}
1040205299Sdavidch		} else if (BCE_CHIP_NUM(bce_sc) == BCE_CHIP_NUM_5709 &&
1041205299Sdavidch			(bce_sc->bce_phy_flags & BCE_PHY_SERDES_FLAG)) {
1042205299Sdavidch
1043212307Syongari			/* Select the SerDes Digital block of the AN MMD. */
1044212307Syongari			PHY_WRITE(sc, BRGPHY_BLOCK_ADDR, BRGPHY_BLOCK_ADDR_SERDES_DIG);
1045205299Sdavidch			val = PHY_READ(sc, BRGPHY_SERDES_DIG_1000X_CTL1);
1046205299Sdavidch			val &= ~BRGPHY_SD_DIG_1000X_CTL1_AUTODET;
1047205299Sdavidch			val |= BRGPHY_SD_DIG_1000X_CTL1_FIBER;
1048205299Sdavidch			PHY_WRITE(sc, BRGPHY_SERDES_DIG_1000X_CTL1, val);
1049205299Sdavidch
1050212307Syongari			/* Select the Over 1G block of the AN MMD. */
1051205299Sdavidch			PHY_WRITE(sc, BRGPHY_BLOCK_ADDR, BRGPHY_BLOCK_ADDR_OVER_1G);
1052205299Sdavidch
1053212307Syongari			/* Enable autoneg "Next Page" to advertise 2.5G support. */
1054212307Syongari			val = PHY_READ(sc, BRGPHY_OVER_1G_UNFORMAT_PG1);
1055205299Sdavidch			if (bce_sc->bce_phy_flags & BCE_PHY_2_5G_CAPABLE_FLAG)
1056205299Sdavidch				val |= BRGPHY_5708S_ANEG_NXT_PG_XMIT1_25G;
1057205299Sdavidch			else
1058205299Sdavidch				val &= ~BRGPHY_5708S_ANEG_NXT_PG_XMIT1_25G;
1059205299Sdavidch			PHY_WRITE(sc, BRGPHY_OVER_1G_UNFORMAT_PG1, val);
1060205299Sdavidch
1061212307Syongari			/* Select the Multi-Rate Backplane Ethernet block of the AN MMD. */
1062205299Sdavidch			PHY_WRITE(sc, BRGPHY_BLOCK_ADDR, BRGPHY_BLOCK_ADDR_MRBE);
1063205299Sdavidch
1064212307Syongari			/* Enable MRBE speed autoneg. */
1065212307Syongari			val = PHY_READ(sc, BRGPHY_MRBE_MSG_PG5_NP);
1066205299Sdavidch			val |= BRGPHY_MRBE_MSG_PG5_NP_MBRE |
1067205299Sdavidch			    BRGPHY_MRBE_MSG_PG5_NP_T2;
1068205299Sdavidch			PHY_WRITE(sc, BRGPHY_MRBE_MSG_PG5_NP, val);
1069205299Sdavidch
1070212307Syongari			/* Select the Clause 73 User B0 block of the AN MMD. */
1071212307Syongari			PHY_WRITE(sc, BRGPHY_BLOCK_ADDR, BRGPHY_BLOCK_ADDR_CL73_USER_B0);
1072205299Sdavidch
1073212307Syongari			/* Enable MRBE speed autoneg. */
1074205299Sdavidch			PHY_WRITE(sc, BRGPHY_CL73_USER_B0_MBRE_CTL1,
1075205299Sdavidch			    BRGPHY_CL73_USER_B0_MBRE_CTL1_NP_AFT_BP |
1076205299Sdavidch			    BRGPHY_CL73_USER_B0_MBRE_CTL1_STA_MGR |
1077205299Sdavidch			    BRGPHY_CL73_USER_B0_MBRE_CTL1_ANEG);
1078205299Sdavidch
1079212307Syongari			/* Restore IEEE0 block (assumed in all brgphy(4) code). */
1080212307Syongari			PHY_WRITE(sc, BRGPHY_BLOCK_ADDR, BRGPHY_BLOCK_ADDR_COMBO_IEEE0);
1081205299Sdavidch        } else if (BCE_CHIP_NUM(bce_sc) == BCE_CHIP_NUM_5709) {
1082179772Sdavidch			if ((BCE_CHIP_REV(bce_sc) == BCE_CHIP_REV_Ax) ||
1083179772Sdavidch				(BCE_CHIP_REV(bce_sc) == BCE_CHIP_REV_Bx))
1084179772Sdavidch				brgphy_fixup_disable_early_dac(sc);
1085212307Syongari
1086266974Smarcel			brgphy_jumbo_settings(sc, if_getmtu(ifp));
1087179772Sdavidch			brgphy_ethernet_wirespeed(sc);
1088170391Sdavidch		} else {
1089170391Sdavidch			brgphy_fixup_ber_bug(sc);
1090266974Smarcel			brgphy_jumbo_settings(sc, if_getmtu(ifp));
1091170391Sdavidch			brgphy_ethernet_wirespeed(sc);
1092170391Sdavidch		}
1093119157Sambrisko	}
1094114590Sps}
1095