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$");
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>
4659477Swpaul
4759477Swpaul#include <net/if.h>
48157642Sps#include <net/ethernet.h>
4959477Swpaul#include <net/if_media.h>
5059477Swpaul
5159477Swpaul#include <dev/mii/mii.h>
5259477Swpaul#include <dev/mii/miivar.h>
53109514Sobrien#include "miidevs.h"
5459477Swpaul
5559477Swpaul#include <dev/mii/brgphyreg.h>
56117659Swpaul#include <net/if_arp.h>
57117659Swpaul#include <machine/bus.h>
58117659Swpaul#include <dev/bge/if_bgereg.h>
59157642Sps#include <dev/bce/if_bcereg.h>
6059477Swpaul
61119285Simp#include <dev/pci/pcireg.h>
62119285Simp#include <dev/pci/pcivar.h>
63118814Swpaul
6459477Swpaul#include "miibus_if.h"
6559477Swpaul
66105135Salfredstatic int brgphy_probe(device_t);
67105135Salfredstatic int brgphy_attach(device_t);
6859477Swpaul
69166037Sjkimstruct brgphy_softc {
70166037Sjkim	struct mii_softc mii_sc;
71170391Sdavidch	int serdes_flags;	/* Keeps track of the serdes type used */
72204941Ssobomax#define BRGPHY_5706S		0x0001
73204941Ssobomax#define BRGPHY_5708S		0x0002
74204941Ssobomax#define BRGPHY_NOANWAIT		0x0004
75205299Sdavidch#define BRGPHY_5709S		0x0008
76179772Sdavidch	int bce_phy_flags;	/* PHY flags transferred from the MAC driver */
77166037Sjkim};
78166037Sjkim
7959477Swpaulstatic device_method_t brgphy_methods[] = {
8059477Swpaul	/* device interface */
8159477Swpaul	DEVMETHOD(device_probe,		brgphy_probe),
8259477Swpaul	DEVMETHOD(device_attach,	brgphy_attach),
8395722Sphk	DEVMETHOD(device_detach,	mii_phy_detach),
8459477Swpaul	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
85227908Smarius	DEVMETHOD_END
8659477Swpaul};
8759477Swpaul
8859477Swpaulstatic devclass_t brgphy_devclass;
8959477Swpaul
9059477Swpaulstatic driver_t brgphy_driver = {
9159477Swpaul	"brgphy",
9259477Swpaul	brgphy_methods,
93166049Sjkim	sizeof(struct brgphy_softc)
9459477Swpaul};
9559477Swpaul
9659477SwpaulDRIVER_MODULE(brgphy, miibus, brgphy_driver, brgphy_devclass, 0, 0);
9759477Swpaul
9884145Sjlemonstatic int	brgphy_service(struct mii_softc *, struct mii_data *, int);
99215297Smariusstatic void	brgphy_setmedia(struct mii_softc *, int);
10084145Sjlemonstatic void	brgphy_status(struct mii_softc *);
101215297Smariusstatic void	brgphy_mii_phy_auto(struct mii_softc *, int);
102114590Spsstatic void	brgphy_reset(struct mii_softc *);
103170391Sdavidchstatic void	brgphy_enable_loopback(struct mii_softc *);
104114590Spsstatic void	bcm5401_load_dspcode(struct mii_softc *);
105114590Spsstatic void	bcm5411_load_dspcode(struct mii_softc *);
106204144Smariusstatic void	bcm54k2_load_dspcode(struct mii_softc *);
107166676Sjkimstatic void	brgphy_fixup_5704_a0_bug(struct mii_softc *);
108166031Sjkimstatic void	brgphy_fixup_adc_bug(struct mii_softc *);
109166673Sjkimstatic void	brgphy_fixup_adjust_trim(struct mii_softc *);
110166031Sjkimstatic void	brgphy_fixup_ber_bug(struct mii_softc *);
111166677Sjkimstatic void	brgphy_fixup_crc_bug(struct mii_softc *);
112166031Sjkimstatic void	brgphy_fixup_jitter_bug(struct mii_softc *);
113166032Sjkimstatic void	brgphy_ethernet_wirespeed(struct mii_softc *);
114166032Sjkimstatic void	brgphy_jumbo_settings(struct mii_softc *, u_long);
11559477Swpaul
116160078Syongaristatic const struct mii_phydesc brgphys[] = {
117221407Smarius	MII_PHY_DESC(BROADCOM, BCM5400),
118221407Smarius	MII_PHY_DESC(BROADCOM, BCM5401),
119221407Smarius	MII_PHY_DESC(BROADCOM, BCM5411),
120221407Smarius	MII_PHY_DESC(BROADCOM, BCM54K2),
121221407Smarius	MII_PHY_DESC(BROADCOM, BCM5701),
122221407Smarius	MII_PHY_DESC(BROADCOM, BCM5703),
123221407Smarius	MII_PHY_DESC(BROADCOM, BCM5704),
124221407Smarius	MII_PHY_DESC(BROADCOM, BCM5705),
125221407Smarius	MII_PHY_DESC(BROADCOM, BCM5706),
126221407Smarius	MII_PHY_DESC(BROADCOM, BCM5714),
127221407Smarius	MII_PHY_DESC(BROADCOM, BCM5421),
128221407Smarius	MII_PHY_DESC(BROADCOM, BCM5750),
129221407Smarius	MII_PHY_DESC(BROADCOM, BCM5752),
130221407Smarius	MII_PHY_DESC(BROADCOM, BCM5780),
131221407Smarius	MII_PHY_DESC(BROADCOM, BCM5708C),
132221407Smarius	MII_PHY_DESC(BROADCOM2, BCM5482),
133221407Smarius	MII_PHY_DESC(BROADCOM2, BCM5708S),
134221407Smarius	MII_PHY_DESC(BROADCOM2, BCM5709C),
135221407Smarius	MII_PHY_DESC(BROADCOM2, BCM5709S),
136221407Smarius	MII_PHY_DESC(BROADCOM2, BCM5709CAX),
137221407Smarius	MII_PHY_DESC(BROADCOM2, BCM5722),
138221407Smarius	MII_PHY_DESC(BROADCOM2, BCM5755),
139221407Smarius	MII_PHY_DESC(BROADCOM2, BCM5754),
140221407Smarius	MII_PHY_DESC(BROADCOM2, BCM5761),
141221407Smarius	MII_PHY_DESC(BROADCOM2, BCM5784),
142227913Smarius#ifdef notyet	/* better handled by ukphy(4) until WARs are implemented */
143227907Smarius	MII_PHY_DESC(BROADCOM2, BCM5785),
144227913Smarius#endif
145221407Smarius	MII_PHY_DESC(BROADCOM3, BCM5717C),
146221713Syongari	MII_PHY_DESC(BROADCOM3, BCM5719C),
147226870Syongari	MII_PHY_DESC(BROADCOM3, BCM5720C),
148221407Smarius	MII_PHY_DESC(BROADCOM3, BCM57765),
149231913Smarius	MII_PHY_DESC(BROADCOM3, BCM57780),
150253481Syongari	MII_PHY_DESC(BROADCOM4, BCM5725C),
151221407Smarius	MII_PHY_DESC(xxBROADCOM_ALT1, BCM5906),
152160078Syongari	MII_PHY_END
153160078Syongari};
154160078Syongari
155221407Smariusstatic const struct mii_phy_funcs brgphy_funcs = {
156221407Smarius	brgphy_service,
157221407Smarius	brgphy_status,
158221407Smarius	brgphy_reset
159221407Smarius};
160221407Smarius
161204989Ssobomax#define HS21_PRODUCT_ID	"IBM eServer BladeCenter HS21"
162204989Ssobomax#define HS21_BCM_CHIPID	0x57081021
163170391Sdavidch
164204989Ssobomaxstatic int
165204989Ssobomaxdetect_hs21(struct bce_softc *bce_sc)
166204989Ssobomax{
167204989Ssobomax	char *sysenv;
168215353Sjkim	int found;
169204989Ssobomax
170215353Sjkim	found = 0;
171215353Sjkim	if (bce_sc->bce_chipid == HS21_BCM_CHIPID) {
172215353Sjkim		sysenv = getenv("smbios.system.product");
173215353Sjkim		if (sysenv != NULL) {
174215355Sjkim			if (strncmp(sysenv, HS21_PRODUCT_ID,
175215355Sjkim			    strlen(HS21_PRODUCT_ID)) == 0)
176215353Sjkim				found = 1;
177215353Sjkim			freeenv(sysenv);
178215353Sjkim		}
179215353Sjkim	}
180215353Sjkim	return (found);
181204989Ssobomax}
182204989Ssobomax
183170391Sdavidch/* Search for our PHY in the list of known PHYs */
184105135Salfredstatic int
185150763Simpbrgphy_probe(device_t dev)
18659477Swpaul{
187215297Smarius
188170391Sdavidch	return (mii_phy_dev_probe(dev, brgphys, BUS_PROBE_DEFAULT));
18959477Swpaul}
19059477Swpaul
191170391Sdavidch/* Attach the PHY to the MII bus */
192105135Salfredstatic int
193150763Simpbrgphy_attach(device_t dev)
19459477Swpaul{
195166037Sjkim	struct brgphy_softc *bsc;
196170391Sdavidch	struct bge_softc *bge_sc = NULL;
197170391Sdavidch	struct bce_softc *bce_sc = NULL;
19859477Swpaul	struct mii_softc *sc;
199170391Sdavidch	struct ifnet *ifp;
20059477Swpaul
201166037Sjkim	bsc = device_get_softc(dev);
202166037Sjkim	sc = &bsc->mii_sc;
20359477Swpaul
204221407Smarius	mii_phy_dev_attach(dev, MIIF_NOISOLATE | MIIF_NOMANPAUSE,
205221407Smarius	    &brgphy_funcs, 0);
206213364Smarius
207170391Sdavidch	bsc->serdes_flags = 0;
208244482Syongari	ifp = sc->mii_pdata->mii_ifp;
209170391Sdavidch
210244482Syongari	/* Find the MAC driver associated with this PHY. */
211244482Syongari	if (strcmp(ifp->if_dname, "bge") == 0)
212244482Syongari		bge_sc = ifp->if_softc;
213244482Syongari	else if (strcmp(ifp->if_dname, "bce") == 0)
214244482Syongari		bce_sc = ifp->if_softc;
215244482Syongari
216170391Sdavidch	/* Handle any special cases based on the PHY ID */
217221407Smarius	switch (sc->mii_mpd_oui) {
218181617Syongari	case MII_OUI_BROADCOM:
219221407Smarius		switch (sc->mii_mpd_model) {
220221407Smarius		case MII_MODEL_BROADCOM_BCM5706:
221221407Smarius		case MII_MODEL_BROADCOM_BCM5714:
222205299Sdavidch			/*
223205299Sdavidch			 * The 5464 PHY used in the 5706 supports both copper
224205299Sdavidch			 * and fiber interfaces over GMII.  Need to check the
225205299Sdavidch			 * shadow registers to see which mode is actually
226205299Sdavidch			 * in effect, and therefore whether we have 5706C or
227205299Sdavidch			 * 5706S.
228205299Sdavidch			 */
229205299Sdavidch			PHY_WRITE(sc, BRGPHY_MII_SHADOW_1C,
230205299Sdavidch				BRGPHY_SHADOW_1C_MODE_CTRL);
231205299Sdavidch			if (PHY_READ(sc, BRGPHY_MII_SHADOW_1C) &
232205299Sdavidch				BRGPHY_SHADOW_1C_ENA_1000X) {
233205299Sdavidch				bsc->serdes_flags |= BRGPHY_5706S;
234205299Sdavidch				sc->mii_flags |= MIIF_HAVEFIBER;
235205299Sdavidch			}
236205299Sdavidch			break;
237231913Smarius		}
238231913Smarius		break;
239221407Smarius	case MII_OUI_BROADCOM2:
240221407Smarius		switch (sc->mii_mpd_model) {
241221407Smarius		case MII_MODEL_BROADCOM2_BCM5708S:
242205299Sdavidch			bsc->serdes_flags |= BRGPHY_5708S;
243205299Sdavidch			sc->mii_flags |= MIIF_HAVEFIBER;
244205299Sdavidch			break;
245221407Smarius		case MII_MODEL_BROADCOM2_BCM5709S:
246244482Syongari			/*
247244482Syongari			 * XXX
248244482Syongari			 * 5720S and 5709S shares the same PHY id.
249244482Syongari			 * Assume 5720S PHY if parent device is bge(4).
250244482Syongari			 */
251244482Syongari			if (bge_sc != NULL)
252244482Syongari				bsc->serdes_flags |= BRGPHY_5708S;
253244482Syongari			else
254244482Syongari				bsc->serdes_flags |= BRGPHY_5709S;
255212307Syongari			sc->mii_flags |= MIIF_HAVEFIBER;
256212307Syongari			break;
257212307Syongari		}
258212307Syongari		break;
259170391Sdavidch	}
260170391Sdavidch
261221407Smarius	PHY_RESET(sc);
26259477Swpaul
263214012Smarius	/* Read the PHY's capabilities. */
264221407Smarius	sc->mii_capabilities = PHY_READ(sc, MII_BMSR) & sc->mii_capmask;
265214012Smarius	if (sc->mii_capabilities & BMSR_EXTSTAT)
266214012Smarius		sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR);
26759477Swpaul	device_printf(dev, " ");
26859477Swpaul
269221407Smarius#define	ADD(m, c)	ifmedia_add(&sc->mii_pdata->mii_media, (m), (c), NULL)
270170391Sdavidch
271170391Sdavidch	/* Add the supported media types */
272170391Sdavidch	if ((sc->mii_flags & MIIF_HAVEFIBER) == 0) {
273215297Smarius		mii_phy_add_media(sc);
274215297Smarius		printf("\n");
275170391Sdavidch	} else {
276215297Smarius		sc->mii_anegticks = MII_ANEGTICKS_GIGE;
277170391Sdavidch		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_SX, IFM_FDX, sc->mii_inst),
278170391Sdavidch			BRGPHY_S1000 | BRGPHY_BMCR_FDX);
279170391Sdavidch		printf("1000baseSX-FDX, ");
280179772Sdavidch		/* 2.5G support is a software enabled feature on the 5708S and 5709S. */
281170391Sdavidch		if (bce_sc && (bce_sc->bce_phy_flags & BCE_PHY_2_5G_CAPABLE_FLAG)) {
282170391Sdavidch			ADD(IFM_MAKEWORD(IFM_ETHER, IFM_2500_SX, IFM_FDX, sc->mii_inst), 0);
283170391Sdavidch			printf("2500baseSX-FDX, ");
284204941Ssobomax		} else if ((bsc->serdes_flags & BRGPHY_5708S) && bce_sc &&
285204989Ssobomax		    (detect_hs21(bce_sc) != 0)) {
286204941Ssobomax			/*
287204941Ssobomax			 * There appears to be certain silicon revision
288204989Ssobomax			 * in IBM HS21 blades that is having issues with
289204941Ssobomax			 * this driver wating for the auto-negotiation to
290204941Ssobomax			 * complete. This happens with a specific chip id
291204941Ssobomax			 * only and when the 1000baseSX-FDX is the only
292204941Ssobomax			 * mode. Workaround this issue since it's unlikely
293204941Ssobomax			 * to be ever addressed.
294204941Ssobomax			 */
295204941Ssobomax			printf("auto-neg workaround, ");
296204941Ssobomax			bsc->serdes_flags |= BRGPHY_NOANWAIT;
297170391Sdavidch		}
298215297Smarius		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, sc->mii_inst), 0);
299215297Smarius		printf("auto\n");
300170391Sdavidch	}
301170391Sdavidch
302170391Sdavidch#undef ADD
30359477Swpaul	MIIBUS_MEDIAINIT(sc->mii_dev);
304164830Smarius	return (0);
30559477Swpaul}
30659477Swpaul
30784145Sjlemonstatic int
308150763Simpbrgphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
30959477Swpaul{
31059477Swpaul	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
311170391Sdavidch	int val;
31259477Swpaul
31359477Swpaul	switch (cmd) {
31459477Swpaul	case MII_POLLSTAT:
31559477Swpaul		break;
31659477Swpaul	case MII_MEDIACHG:
317165343Soleg		/* If the interface is not up, don't do anything. */
31859477Swpaul		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
31959477Swpaul			break;
32059477Swpaul
321170391Sdavidch		/* Todo: Why is this here?  Is it really needed? */
322221407Smarius		PHY_RESET(sc);	/* XXX hardware bug work-around */
32359477Swpaul
32459477Swpaul		switch (IFM_SUBTYPE(ife->ifm_media)) {
32559477Swpaul		case IFM_AUTO:
326215297Smarius			brgphy_mii_phy_auto(sc, ife->ifm_media);
32759477Swpaul			break;
328170391Sdavidch		case IFM_2500_SX:
329170391Sdavidch		case IFM_1000_SX:
33095673Sphk		case IFM_1000_T:
33183029Swpaul		case IFM_100_TX:
33283029Swpaul		case IFM_10_T:
333215297Smarius			brgphy_setmedia(sc, ife->ifm_media);
33459477Swpaul			break;
33559477Swpaul		default:
336213364Smarius			return (EINVAL);
33759477Swpaul		}
33859477Swpaul		break;
33959477Swpaul	case MII_TICK:
340170391Sdavidch		/* Bail if the interface isn't up. */
34159477Swpaul		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
342213364Smarius			return (0);
34359477Swpaul
344170391Sdavidch
345170391Sdavidch		/* Bail if autoneg isn't in process. */
346165343Soleg		if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) {
347170391Sdavidch			sc->mii_ticks = 0;
34884145Sjlemon			break;
349165343Soleg		}
35059477Swpaul
35159477Swpaul		/*
35259477Swpaul		 * Check to see if we have link.  If we do, we don't
353165343Soleg		 * need to restart the autonegotiation process.
35459477Swpaul		 */
355215297Smarius		val = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
356170391Sdavidch		if (val & BMSR_LINK) {
357165343Soleg			sc->mii_ticks = 0;	/* Reset autoneg timer. */
35859477Swpaul			break;
359165343Soleg		}
36059477Swpaul
361165343Soleg		/* Announce link loss right after it happens. */
362165343Soleg		if (sc->mii_ticks++ == 0)
363128870Sandre			break;
364164830Smarius
365165343Soleg		/* Only retry autonegotiation every mii_anegticks seconds. */
366165343Soleg		if (sc->mii_ticks <= sc->mii_anegticks)
367181619Syongari			break;
368165343Soleg
369170391Sdavidch
370170391Sdavidch		/* Retry autonegotiation */
37184145Sjlemon		sc->mii_ticks = 0;
372215297Smarius		brgphy_mii_phy_auto(sc, ife->ifm_media);
373153234Soleg		break;
37459477Swpaul	}
37559477Swpaul
37659477Swpaul	/* Update the media status. */
377221407Smarius	PHY_STATUS(sc);
37859477Swpaul
379114628Sps	/*
380114628Sps	 * Callback if something changed. Note that we need to poke
381114628Sps	 * the DSP on the Broadcom PHYs if the media changes.
382114628Sps	 */
383164830Smarius	if (sc->mii_media_active != mii->mii_media_active ||
384114628Sps	    sc->mii_media_status != mii->mii_media_status ||
385114628Sps	    cmd == MII_MEDIACHG) {
386221407Smarius		switch (sc->mii_mpd_oui) {
387170391Sdavidch		case MII_OUI_BROADCOM:
388221407Smarius			switch (sc->mii_mpd_model) {
389221407Smarius			case MII_MODEL_BROADCOM_BCM5400:
390166031Sjkim				bcm5401_load_dspcode(sc);
391170391Sdavidch				break;
392221407Smarius			case MII_MODEL_BROADCOM_BCM5401:
393221407Smarius				if (sc->mii_mpd_rev == 1 || sc->mii_mpd_rev == 3)
394170391Sdavidch					bcm5401_load_dspcode(sc);
395170391Sdavidch				break;
396221407Smarius			case MII_MODEL_BROADCOM_BCM5411:
397170391Sdavidch				bcm5411_load_dspcode(sc);
398170391Sdavidch				break;
399221407Smarius			case MII_MODEL_BROADCOM_BCM54K2:
400204144Smarius				bcm54k2_load_dspcode(sc);
401204144Smarius				break;
402170391Sdavidch			}
403166031Sjkim			break;
404114628Sps		}
405114628Sps	}
406128870Sandre	mii_phy_update(sc, cmd);
407213364Smarius	return (0);
40859477Swpaul}
40959477Swpaul
410179772Sdavidch/****************************************************************************/
411179772Sdavidch/* Sets the PHY link speed.                                                 */
412179772Sdavidch/*                                                                          */
413179772Sdavidch/* Returns:                                                                 */
414179772Sdavidch/*   None                                                                   */
415179772Sdavidch/****************************************************************************/
41684145Sjlemonstatic void
417215297Smariusbrgphy_setmedia(struct mii_softc *sc, int media)
418165360Sjkim{
419170391Sdavidch	int bmcr = 0, gig;
420165360Sjkim
421165360Sjkim	switch (IFM_SUBTYPE(media)) {
422170391Sdavidch	case IFM_2500_SX:
423170391Sdavidch		break;
424170391Sdavidch	case IFM_1000_SX:
425165360Sjkim	case IFM_1000_T:
426165360Sjkim		bmcr = BRGPHY_S1000;
427165360Sjkim		break;
428165360Sjkim	case IFM_100_TX:
429165360Sjkim		bmcr = BRGPHY_S100;
430165360Sjkim		break;
431165360Sjkim	case IFM_10_T:
432165360Sjkim	default:
433165360Sjkim		bmcr = BRGPHY_S10;
434165360Sjkim		break;
435165360Sjkim	}
436179772Sdavidch
437217413Smarius	if ((media & IFM_FDX) != 0) {
438165360Sjkim		bmcr |= BRGPHY_BMCR_FDX;
439165360Sjkim		gig = BRGPHY_1000CTL_AFD;
440165360Sjkim	} else {
441165360Sjkim		gig = BRGPHY_1000CTL_AHD;
442165360Sjkim	}
443165360Sjkim
444215297Smarius	/* Force loopback to disconnect PHY from Ethernet medium. */
445170391Sdavidch	brgphy_enable_loopback(sc);
446179772Sdavidch
447165360Sjkim	PHY_WRITE(sc, BRGPHY_MII_1000CTL, 0);
448179772Sdavidch	PHY_WRITE(sc, BRGPHY_MII_ANAR, BRGPHY_SEL_TYPE);
449165360Sjkim
450215297Smarius	if (IFM_SUBTYPE(media) != IFM_1000_T &&
451215297Smarius	    IFM_SUBTYPE(media) != IFM_1000_SX) {
452215297Smarius		PHY_WRITE(sc, BRGPHY_MII_BMCR, bmcr);
453215297Smarius		return;
454215297Smarius	}
455165360Sjkim
456215297Smarius	if (IFM_SUBTYPE(media) == IFM_1000_T) {
457215297Smarius		gig |= BRGPHY_1000CTL_MSE;
458215297Smarius		if ((media & IFM_ETH_MASTER) != 0)
459215297Smarius			gig |= BRGPHY_1000CTL_MSC;
460215297Smarius	}
461165360Sjkim	PHY_WRITE(sc, BRGPHY_MII_1000CTL, gig);
462165360Sjkim	PHY_WRITE(sc, BRGPHY_MII_BMCR,
463165360Sjkim	    bmcr | BRGPHY_BMCR_AUTOEN | BRGPHY_BMCR_STARTNEG);
464165360Sjkim}
465165360Sjkim
466179772Sdavidch/****************************************************************************/
467179772Sdavidch/* Set the media status based on the PHY settings.                          */
468179772Sdavidch/*                                                                          */
469179772Sdavidch/* Returns:                                                                 */
470179772Sdavidch/*   None                                                                   */
471179772Sdavidch/****************************************************************************/
472165360Sjkimstatic void
473150763Simpbrgphy_status(struct mii_softc *sc)
47459477Swpaul{
475170391Sdavidch	struct brgphy_softc *bsc = (struct brgphy_softc *)sc;
47659477Swpaul	struct mii_data *mii = sc->mii_pdata;
477215297Smarius	int aux, bmcr, bmsr, val, xstat;
478215297Smarius	u_int flowstat;
47959477Swpaul
48059477Swpaul	mii->mii_media_status = IFM_AVALID;
48159477Swpaul	mii->mii_media_active = IFM_ETHER;
48259477Swpaul
483170391Sdavidch	bmsr = PHY_READ(sc, BRGPHY_MII_BMSR) | PHY_READ(sc, BRGPHY_MII_BMSR);
484181618Syongari	bmcr = PHY_READ(sc, BRGPHY_MII_BMCR);
485165360Sjkim
486170391Sdavidch	if (bmcr & BRGPHY_BMCR_LOOP) {
48759477Swpaul		mii->mii_media_active |= IFM_LOOP;
488170391Sdavidch	}
48959477Swpaul
490167728Sjkim	if ((bmcr & BRGPHY_BMCR_AUTOEN) &&
491204941Ssobomax	    (bmsr & BRGPHY_BMSR_ACOMP) == 0 &&
492204941Ssobomax	    (bsc->serdes_flags & BRGPHY_NOANWAIT) == 0) {
493167728Sjkim		/* Erg, still trying, I guess... */
494167728Sjkim		mii->mii_media_active |= IFM_NONE;
495215297Smarius		return;
496165360Sjkim	}
49759477Swpaul
498170391Sdavidch	if ((sc->mii_flags & MIIF_HAVEFIBER) == 0) {
499215297Smarius		/*
500215297Smarius		 * NB: reading the ANAR, ANLPAR or 1000STS after the AUXSTS
501215297Smarius		 * wedges at least the PHY of BCM5704 (but not others).
502215297Smarius		 */
503215297Smarius		flowstat = mii_phy_flowstatus(sc);
504215297Smarius		xstat = PHY_READ(sc, BRGPHY_MII_1000STS);
505170391Sdavidch		aux = PHY_READ(sc, BRGPHY_MII_AUXSTS);
506170391Sdavidch
507170391Sdavidch		/* If copper link is up, get the negotiated speed/duplex. */
508170391Sdavidch		if (aux & BRGPHY_AUXSTS_LINK) {
509170391Sdavidch			mii->mii_media_status |= IFM_ACTIVE;
510170391Sdavidch			switch (aux & BRGPHY_AUXSTS_AN_RES) {
511170391Sdavidch			case BRGPHY_RES_1000FD:
512170391Sdavidch				mii->mii_media_active |= IFM_1000_T | IFM_FDX; 	break;
513170391Sdavidch			case BRGPHY_RES_1000HD:
514170391Sdavidch				mii->mii_media_active |= IFM_1000_T | IFM_HDX; 	break;
515170391Sdavidch			case BRGPHY_RES_100FD:
516170391Sdavidch				mii->mii_media_active |= IFM_100_TX | IFM_FDX; break;
517170391Sdavidch			case BRGPHY_RES_100T4:
518170391Sdavidch				mii->mii_media_active |= IFM_100_T4; break;
519170391Sdavidch			case BRGPHY_RES_100HD:
520170391Sdavidch				mii->mii_media_active |= IFM_100_TX | IFM_HDX; 	break;
521170391Sdavidch			case BRGPHY_RES_10FD:
522170391Sdavidch				mii->mii_media_active |= IFM_10_T | IFM_FDX; break;
523170391Sdavidch			case BRGPHY_RES_10HD:
524170391Sdavidch				mii->mii_media_active |= IFM_10_T | IFM_HDX; break;
525170391Sdavidch			default:
526170391Sdavidch				mii->mii_media_active |= IFM_NONE; break;
527170391Sdavidch			}
528215297Smarius
529215297Smarius			if ((mii->mii_media_active & IFM_FDX) != 0)
530215297Smarius				mii->mii_media_active |= flowstat;
531215297Smarius
532215297Smarius			if (IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T &&
533215297Smarius			    (xstat & BRGPHY_1000STS_MSR) != 0)
534215297Smarius				mii->mii_media_active |= IFM_ETH_MASTER;
53583029Swpaul		}
536170391Sdavidch	} else {
537215297Smarius		/* Todo: Add support for flow control. */
538170391Sdavidch		/* If serdes link is up, get the negotiated speed/duplex. */
539170391Sdavidch		if (bmsr & BRGPHY_BMSR_LINK) {
540170391Sdavidch			mii->mii_media_status |= IFM_ACTIVE;
541170391Sdavidch		}
542170391Sdavidch
543170391Sdavidch		/* Check the link speed/duplex based on the PHY type. */
544170391Sdavidch		if (bsc->serdes_flags & BRGPHY_5706S) {
545170391Sdavidch			mii->mii_media_active |= IFM_1000_SX;
546170391Sdavidch
547170391Sdavidch			/* If autoneg enabled, read negotiated duplex settings */
548170391Sdavidch			if (bmcr & BRGPHY_BMCR_AUTOEN) {
549170391Sdavidch				val = PHY_READ(sc, BRGPHY_SERDES_ANAR) & PHY_READ(sc, BRGPHY_SERDES_ANLPAR);
550170391Sdavidch				if (val & BRGPHY_SERDES_ANAR_FDX)
551170391Sdavidch					mii->mii_media_active |= IFM_FDX;
552170391Sdavidch				else
553170391Sdavidch					mii->mii_media_active |= IFM_HDX;
554170391Sdavidch			}
555170391Sdavidch		} else if (bsc->serdes_flags & BRGPHY_5708S) {
556170391Sdavidch			PHY_WRITE(sc, BRGPHY_5708S_BLOCK_ADDR, BRGPHY_5708S_DIG_PG0);
557170391Sdavidch			xstat = PHY_READ(sc, BRGPHY_5708S_PG0_1000X_STAT1);
558170391Sdavidch
559212307Syongari			/* Check for MRBE auto-negotiated speed results. */
560170391Sdavidch			switch (xstat & BRGPHY_5708S_PG0_1000X_STAT1_SPEED_MASK) {
561181617Syongari			case BRGPHY_5708S_PG0_1000X_STAT1_SPEED_10:
562170391Sdavidch				mii->mii_media_active |= IFM_10_FL; break;
563181617Syongari			case BRGPHY_5708S_PG0_1000X_STAT1_SPEED_100:
564170391Sdavidch				mii->mii_media_active |= IFM_100_FX; break;
565181617Syongari			case BRGPHY_5708S_PG0_1000X_STAT1_SPEED_1G:
566170391Sdavidch				mii->mii_media_active |= IFM_1000_SX; break;
567181617Syongari			case BRGPHY_5708S_PG0_1000X_STAT1_SPEED_25G:
568170391Sdavidch				mii->mii_media_active |= IFM_2500_SX; break;
569170391Sdavidch			}
570170391Sdavidch
571212307Syongari			/* Check for MRBE auto-negotiated duplex results. */
572170391Sdavidch			if (xstat & BRGPHY_5708S_PG0_1000X_STAT1_FDX)
573170391Sdavidch				mii->mii_media_active |= IFM_FDX;
574170391Sdavidch			else
575170391Sdavidch				mii->mii_media_active |= IFM_HDX;
576212307Syongari		} else if (bsc->serdes_flags & BRGPHY_5709S) {
577212307Syongari			/* Select GP Status Block of the AN MMD, get autoneg results. */
578212307Syongari			PHY_WRITE(sc, BRGPHY_BLOCK_ADDR, BRGPHY_BLOCK_ADDR_GP_STATUS);
579205299Sdavidch			xstat = PHY_READ(sc, BRGPHY_GP_STATUS_TOP_ANEG_STATUS);
580205299Sdavidch
581212307Syongari			/* Restore IEEE0 block (assumed in all brgphy(4) code). */
582212307Syongari			PHY_WRITE(sc, BRGPHY_BLOCK_ADDR, BRGPHY_BLOCK_ADDR_COMBO_IEEE0);
583205299Sdavidch
584212307Syongari			/* Check for MRBE auto-negotiated speed results. */
585212307Syongari			switch (xstat & BRGPHY_GP_STATUS_TOP_ANEG_SPEED_MASK) {
586212307Syongari				case BRGPHY_GP_STATUS_TOP_ANEG_SPEED_10:
587212307Syongari					mii->mii_media_active |= IFM_10_FL; break;
588212307Syongari				case BRGPHY_GP_STATUS_TOP_ANEG_SPEED_100:
589212307Syongari					mii->mii_media_active |= IFM_100_FX; break;
590212307Syongari				case BRGPHY_GP_STATUS_TOP_ANEG_SPEED_1G:
591212307Syongari					mii->mii_media_active |= IFM_1000_SX; break;
592212307Syongari				case BRGPHY_GP_STATUS_TOP_ANEG_SPEED_25G:
593212307Syongari					mii->mii_media_active |= IFM_2500_SX; break;
594205299Sdavidch			}
595205299Sdavidch
596212307Syongari			/* Check for MRBE auto-negotiated duplex results. */
597205299Sdavidch			if (xstat & BRGPHY_GP_STATUS_TOP_ANEG_FDX)
598205299Sdavidch				mii->mii_media_active |= IFM_FDX;
599205299Sdavidch			else
600205299Sdavidch				mii->mii_media_active |= IFM_HDX;
601212307Syongari		}
602170391Sdavidch	}
60359477Swpaul}
60459477Swpaul
605170391Sdavidchstatic void
606215297Smariusbrgphy_mii_phy_auto(struct mii_softc *sc, int media)
60759477Swpaul{
608215297Smarius	int anar, ktcr = 0;
60959477Swpaul
610221407Smarius	PHY_RESET(sc);
611170391Sdavidch
612170391Sdavidch	if ((sc->mii_flags & MIIF_HAVEFIBER) == 0) {
613215297Smarius		anar = BMSR_MEDIA_TO_ANAR(sc->mii_capabilities) | ANAR_CSMA;
614215297Smarius		if ((media & IFM_FLOW) != 0 ||
615215297Smarius		    (sc->mii_flags & MIIF_FORCEPAUSE) != 0)
616215297Smarius			anar |= BRGPHY_ANAR_PC | BRGPHY_ANAR_ASP;
617215297Smarius		PHY_WRITE(sc, BRGPHY_MII_ANAR, anar);
618244481Syongari		ktcr = BRGPHY_1000CTL_AFD | BRGPHY_1000CTL_AHD;
619244481Syongari		if (sc->mii_mpd_model == MII_MODEL_BROADCOM_BCM5701)
620244481Syongari			ktcr |= BRGPHY_1000CTL_MSE | BRGPHY_1000CTL_MSC;
621244481Syongari		PHY_WRITE(sc, BRGPHY_MII_1000CTL, ktcr);
622244481Syongari		PHY_READ(sc, BRGPHY_MII_1000CTL);
623170391Sdavidch	} else {
624215297Smarius		anar = BRGPHY_SERDES_ANAR_FDX | BRGPHY_SERDES_ANAR_HDX;
625215297Smarius		if ((media & IFM_FLOW) != 0 ||
626215297Smarius		    (sc->mii_flags & MIIF_FORCEPAUSE) != 0)
627215297Smarius			anar |= BRGPHY_SERDES_ANAR_BOTH_PAUSE;
628215297Smarius		PHY_WRITE(sc, BRGPHY_SERDES_ANAR, anar);
629170391Sdavidch	}
630170391Sdavidch
631215297Smarius	PHY_WRITE(sc, BRGPHY_MII_BMCR, BRGPHY_BMCR_AUTOEN |
632215297Smarius	    BRGPHY_BMCR_STARTNEG);
633165360Sjkim	PHY_WRITE(sc, BRGPHY_MII_IMR, 0xFF00);
63459477Swpaul}
635114590Sps
636170391Sdavidch/* Enable loopback to force the link down. */
637114590Spsstatic void
638170391Sdavidchbrgphy_enable_loopback(struct mii_softc *sc)
639114590Sps{
640114590Sps	int i;
641114590Sps
642114590Sps	PHY_WRITE(sc, BRGPHY_MII_BMCR, BRGPHY_BMCR_LOOP);
643114590Sps	for (i = 0; i < 15000; i++) {
644170391Sdavidch		if (!(PHY_READ(sc, BRGPHY_MII_BMSR) & BRGPHY_BMSR_LINK))
645114590Sps			break;
646114590Sps		DELAY(10);
647114590Sps	}
648114590Sps}
649114590Sps
650114590Sps/* Turn off tap power management on 5401. */
651114590Spsstatic void
652114590Spsbcm5401_load_dspcode(struct mii_softc *sc)
653114590Sps{
654114590Sps	static const struct {
655114590Sps		int		reg;
656114590Sps		uint16_t	val;
657114590Sps	} dspcode[] = {
658114590Sps		{ BRGPHY_MII_AUXCTL,		0x0c20 },
659114590Sps		{ BRGPHY_MII_DSP_ADDR_REG,	0x0012 },
660114590Sps		{ BRGPHY_MII_DSP_RW_PORT,	0x1804 },
661114590Sps		{ BRGPHY_MII_DSP_ADDR_REG,	0x0013 },
662114590Sps		{ BRGPHY_MII_DSP_RW_PORT,	0x1204 },
663114590Sps		{ BRGPHY_MII_DSP_ADDR_REG,	0x8006 },
664114590Sps		{ BRGPHY_MII_DSP_RW_PORT,	0x0132 },
665114590Sps		{ BRGPHY_MII_DSP_ADDR_REG,	0x8006 },
666114590Sps		{ BRGPHY_MII_DSP_RW_PORT,	0x0232 },
667114590Sps		{ BRGPHY_MII_DSP_ADDR_REG,	0x201f },
668114590Sps		{ BRGPHY_MII_DSP_RW_PORT,	0x0a20 },
669114590Sps		{ 0,				0 },
670114590Sps	};
671114590Sps	int i;
672114590Sps
673114590Sps	for (i = 0; dspcode[i].reg != 0; i++)
674114590Sps		PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
675114590Sps	DELAY(40);
676114590Sps}
677114590Sps
678114590Spsstatic void
679114590Spsbcm5411_load_dspcode(struct mii_softc *sc)
680114590Sps{
681114590Sps	static const struct {
682114590Sps		int		reg;
683114590Sps		uint16_t	val;
684114590Sps	} dspcode[] = {
685114590Sps		{ 0x1c,				0x8c23 },
686114590Sps		{ 0x1c,				0x8ca3 },
687114590Sps		{ 0x1c,				0x8c23 },
688114590Sps		{ 0,				0 },
689114590Sps	};
690114590Sps	int i;
691114590Sps
692114590Sps	for (i = 0; dspcode[i].reg != 0; i++)
693114590Sps		PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
694114590Sps}
695114590Sps
696204144Smariusvoid
697204144Smariusbcm54k2_load_dspcode(struct mii_softc *sc)
698204144Smarius{
699204144Smarius	static const struct {
700204144Smarius		int		reg;
701204144Smarius		uint16_t	val;
702204144Smarius	} dspcode[] = {
703204144Smarius		{ 4,				0x01e1 },
704204144Smarius		{ 9,				0x0300 },
705204144Smarius		{ 0,				0 },
706204144Smarius	};
707204144Smarius	int i;
708204144Smarius
709204144Smarius	for (i = 0; dspcode[i].reg != 0; i++)
710204144Smarius		PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
711204144Smarius
712204144Smarius}
713204144Smarius
714114590Spsstatic void
715166676Sjkimbrgphy_fixup_5704_a0_bug(struct mii_softc *sc)
716114590Sps{
717114590Sps	static const struct {
718114590Sps		int		reg;
719114590Sps		uint16_t	val;
720114590Sps	} dspcode[] = {
721166676Sjkim		{ 0x1c,				0x8d68 },
722166676Sjkim		{ 0x1c,				0x8d68 },
723114590Sps		{ 0,				0 },
724114590Sps	};
725114590Sps	int i;
726114590Sps
727114590Sps	for (i = 0; dspcode[i].reg != 0; i++)
728114590Sps		PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
729114590Sps}
730114590Sps
731114590Spsstatic void
732166676Sjkimbrgphy_fixup_adc_bug(struct mii_softc *sc)
733114590Sps{
734114590Sps	static const struct {
735114590Sps		int		reg;
736166676Sjkim		uint16_t	val;
737114590Sps	} dspcode[] = {
738166676Sjkim		{ BRGPHY_MII_AUXCTL,		0x0c00 },
739166676Sjkim		{ BRGPHY_MII_DSP_ADDR_REG,	0x201f },
740166676Sjkim		{ BRGPHY_MII_DSP_RW_PORT,	0x2aaa },
741114590Sps		{ 0,				0 },
742114590Sps	};
743114590Sps	int i;
744114590Sps
745114590Sps	for (i = 0; dspcode[i].reg != 0; i++)
746114590Sps		PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
747114590Sps}
748114590Sps
749114590Spsstatic void
750166673Sjkimbrgphy_fixup_adjust_trim(struct mii_softc *sc)
751166673Sjkim{
752166673Sjkim	static const struct {
753166673Sjkim		int		reg;
754166673Sjkim		uint16_t	val;
755166673Sjkim	} dspcode[] = {
756166673Sjkim		{ BRGPHY_MII_AUXCTL,		0x0c00 },
757166673Sjkim		{ BRGPHY_MII_DSP_ADDR_REG,	0x000a },
758166673Sjkim		{ BRGPHY_MII_DSP_RW_PORT,	0x110b },
759170391Sdavidch		{ BRGPHY_MII_TEST1,			0x0014 },
760166673Sjkim		{ BRGPHY_MII_AUXCTL,		0x0400 },
761166673Sjkim		{ 0,				0 },
762166673Sjkim	};
763166673Sjkim	int i;
764166673Sjkim
765166673Sjkim	for (i = 0; dspcode[i].reg != 0; i++)
766166673Sjkim		PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
767166673Sjkim}
768166673Sjkim
769166673Sjkimstatic void
770166031Sjkimbrgphy_fixup_ber_bug(struct mii_softc *sc)
771135772Sps{
772135772Sps	static const struct {
773135772Sps		int		reg;
774166676Sjkim		uint16_t	val;
775135772Sps	} dspcode[] = {
776166665Sjkim		{ BRGPHY_MII_AUXCTL,		0x0c00 },
777166665Sjkim		{ BRGPHY_MII_DSP_ADDR_REG,	0x000a },
778166665Sjkim		{ BRGPHY_MII_DSP_RW_PORT,	0x310b },
779166665Sjkim		{ BRGPHY_MII_DSP_ADDR_REG,	0x201f },
780166665Sjkim		{ BRGPHY_MII_DSP_RW_PORT,	0x9506 },
781166665Sjkim		{ BRGPHY_MII_DSP_ADDR_REG,	0x401f },
782166665Sjkim		{ BRGPHY_MII_DSP_RW_PORT,	0x14e2 },
783166665Sjkim		{ BRGPHY_MII_AUXCTL,		0x0400 },
784135772Sps		{ 0,				0 },
785135772Sps	};
786135772Sps	int i;
787135772Sps
788135772Sps	for (i = 0; dspcode[i].reg != 0; i++)
789135772Sps		PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
790135772Sps}
791135772Sps
792135772Spsstatic void
793166677Sjkimbrgphy_fixup_crc_bug(struct mii_softc *sc)
794166677Sjkim{
795166677Sjkim	static const struct {
796166677Sjkim		int		reg;
797166677Sjkim		uint16_t	val;
798166677Sjkim	} dspcode[] = {
799166716Sjkim		{ BRGPHY_MII_DSP_RW_PORT,	0x0a75 },
800166677Sjkim		{ 0x1c,				0x8c68 },
801166677Sjkim		{ 0x1c,				0x8d68 },
802166677Sjkim		{ 0x1c,				0x8c68 },
803166677Sjkim		{ 0,				0 },
804166677Sjkim	};
805166677Sjkim	int i;
806166677Sjkim
807166677Sjkim	for (i = 0; dspcode[i].reg != 0; i++)
808166677Sjkim		PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
809166677Sjkim}
810166677Sjkim
811166677Sjkimstatic void
812166031Sjkimbrgphy_fixup_jitter_bug(struct mii_softc *sc)
813166031Sjkim{
814166031Sjkim	static const struct {
815166031Sjkim		int		reg;
816166031Sjkim		uint16_t	val;
817166031Sjkim	} dspcode[] = {
818166031Sjkim		{ BRGPHY_MII_AUXCTL,		0x0c00 },
819166031Sjkim		{ BRGPHY_MII_DSP_ADDR_REG,	0x000a },
820166031Sjkim		{ BRGPHY_MII_DSP_RW_PORT,	0x010b },
821166031Sjkim		{ BRGPHY_MII_AUXCTL,		0x0400 },
822166031Sjkim		{ 0,				0 },
823166031Sjkim	};
824166031Sjkim	int i;
825166031Sjkim
826166031Sjkim	for (i = 0; dspcode[i].reg != 0; i++)
827166031Sjkim		PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
828166031Sjkim}
829166031Sjkim
830166031Sjkimstatic void
831179772Sdavidchbrgphy_fixup_disable_early_dac(struct mii_softc *sc)
832179772Sdavidch{
833179772Sdavidch	uint32_t val;
834179772Sdavidch
835179772Sdavidch	PHY_WRITE(sc, BRGPHY_MII_DSP_ADDR_REG, 0x0f08);
836179772Sdavidch	val = PHY_READ(sc, BRGPHY_MII_DSP_RW_PORT);
837179772Sdavidch	val &= ~(1 << 8);
838179772Sdavidch	PHY_WRITE(sc, BRGPHY_MII_DSP_RW_PORT, val);
839179772Sdavidch
840179772Sdavidch}
841179772Sdavidch
842179772Sdavidchstatic void
843166032Sjkimbrgphy_ethernet_wirespeed(struct mii_softc *sc)
844166032Sjkim{
845166676Sjkim	uint32_t	val;
846166032Sjkim
847166032Sjkim	/* Enable Ethernet@WireSpeed. */
848166032Sjkim	PHY_WRITE(sc, BRGPHY_MII_AUXCTL, 0x7007);
849166032Sjkim	val = PHY_READ(sc, BRGPHY_MII_AUXCTL);
850166032Sjkim	PHY_WRITE(sc, BRGPHY_MII_AUXCTL, val | (1 << 15) | (1 << 4));
851166032Sjkim}
852166032Sjkim
853166032Sjkimstatic void
854166032Sjkimbrgphy_jumbo_settings(struct mii_softc *sc, u_long mtu)
855166032Sjkim{
856166676Sjkim	uint32_t	val;
857166032Sjkim
858166032Sjkim	/* Set or clear jumbo frame settings in the PHY. */
859166032Sjkim	if (mtu > ETHER_MAX_LEN) {
860221407Smarius		if (sc->mii_mpd_model == MII_MODEL_BROADCOM_BCM5401) {
861166680Sjkim			/* BCM5401 PHY cannot read-modify-write. */
862166666Sjkim			PHY_WRITE(sc, BRGPHY_MII_AUXCTL, 0x4c20);
863166680Sjkim		} else {
864166666Sjkim			PHY_WRITE(sc, BRGPHY_MII_AUXCTL, 0x7);
865166666Sjkim			val = PHY_READ(sc, BRGPHY_MII_AUXCTL);
866166666Sjkim			PHY_WRITE(sc, BRGPHY_MII_AUXCTL,
867166666Sjkim			    val | BRGPHY_AUXCTL_LONG_PKT);
868166666Sjkim		}
869166032Sjkim
870166032Sjkim		val = PHY_READ(sc, BRGPHY_MII_PHY_EXTCTL);
871166032Sjkim		PHY_WRITE(sc, BRGPHY_MII_PHY_EXTCTL,
872166032Sjkim		    val | BRGPHY_PHY_EXTCTL_HIGH_LA);
873166032Sjkim	} else {
874166032Sjkim		PHY_WRITE(sc, BRGPHY_MII_AUXCTL, 0x7);
875166032Sjkim		val = PHY_READ(sc, BRGPHY_MII_AUXCTL);
876166032Sjkim		PHY_WRITE(sc, BRGPHY_MII_AUXCTL,
877166032Sjkim		    val & ~(BRGPHY_AUXCTL_LONG_PKT | 0x7));
878166032Sjkim
879166032Sjkim		val = PHY_READ(sc, BRGPHY_MII_PHY_EXTCTL);
880181617Syongari		PHY_WRITE(sc, BRGPHY_MII_PHY_EXTCTL,
881170391Sdavidch			val & ~BRGPHY_PHY_EXTCTL_HIGH_LA);
882166032Sjkim	}
883166032Sjkim}
884166032Sjkim
885166032Sjkimstatic void
886114590Spsbrgphy_reset(struct mii_softc *sc)
887114590Sps{
888166037Sjkim	struct bge_softc *bge_sc = NULL;
889166037Sjkim	struct bce_softc *bce_sc = NULL;
890166037Sjkim	struct ifnet *ifp;
891225014Smarius	int i, val;
892114590Sps
893225014Smarius	/*
894225014Smarius	 * Perform a reset.  Note that at least some Broadcom PHYs default to
895225014Smarius	 * being powered down as well as isolated after a reset but don't work
896225014Smarius	 * if one or both of these bits are cleared.  However, they just work
897225014Smarius	 * fine if both bits remain set, so we don't use mii_phy_reset() here.
898225014Smarius	 */
899225014Smarius	PHY_WRITE(sc, BRGPHY_MII_BMCR, BRGPHY_BMCR_RESET);
900114590Sps
901225014Smarius	/* Wait 100ms for it to complete. */
902225014Smarius	for (i = 0; i < 100; i++) {
903225014Smarius		if ((PHY_READ(sc, BRGPHY_MII_BMCR) & BRGPHY_BMCR_RESET) == 0)
904225014Smarius			break;
905225014Smarius		DELAY(1000);
906225014Smarius	}
907225014Smarius
908179772Sdavidch	/* Handle any PHY specific procedures following the reset. */
909221407Smarius	switch (sc->mii_mpd_oui) {
910170391Sdavidch	case MII_OUI_BROADCOM:
911221407Smarius		switch (sc->mii_mpd_model) {
912221407Smarius		case MII_MODEL_BROADCOM_BCM5400:
913166031Sjkim			bcm5401_load_dspcode(sc);
914170391Sdavidch			break;
915221407Smarius		case MII_MODEL_BROADCOM_BCM5401:
916221407Smarius			if (sc->mii_mpd_rev == 1 || sc->mii_mpd_rev == 3)
917170391Sdavidch				bcm5401_load_dspcode(sc);
918170391Sdavidch			break;
919221407Smarius		case MII_MODEL_BROADCOM_BCM5411:
920170391Sdavidch			bcm5411_load_dspcode(sc);
921170391Sdavidch			break;
922221407Smarius		case MII_MODEL_BROADCOM_BCM54K2:
923204144Smarius			bcm54k2_load_dspcode(sc);
924204144Smarius			break;
925170391Sdavidch		}
926166031Sjkim		break;
927241437Syongari	case MII_OUI_BROADCOM3:
928241437Syongari		switch (sc->mii_mpd_model) {
929241437Syongari		case MII_MODEL_BROADCOM3_BCM5717C:
930241437Syongari		case MII_MODEL_BROADCOM3_BCM5719C:
931241437Syongari		case MII_MODEL_BROADCOM3_BCM5720C:
932241437Syongari		case MII_MODEL_BROADCOM3_BCM57765:
933241437Syongari			return;
934241437Syongari		}
935241437Syongari		break;
936253481Syongari	case MII_OUI_BROADCOM4:
937253481Syongari		return;
938114590Sps	}
939114590Sps
940117659Swpaul	ifp = sc->mii_pdata->mii_ifp;
941117659Swpaul
942157642Sps	/* Find the driver associated with this PHY. */
943157642Sps	if (strcmp(ifp->if_dname, "bge") == 0)	{
944165361Sjkim		bge_sc = ifp->if_softc;
945157642Sps	} else if (strcmp(ifp->if_dname, "bce") == 0) {
946157642Sps		bce_sc = ifp->if_softc;
947157642Sps	}
948117659Swpaul
949157642Sps	if (bge_sc) {
950166031Sjkim		/* Fix up various bugs */
951213464Syongari		if (bge_sc->bge_phy_flags & BGE_PHY_5704_A0_BUG)
952166676Sjkim			brgphy_fixup_5704_a0_bug(sc);
953213464Syongari		if (bge_sc->bge_phy_flags & BGE_PHY_ADC_BUG)
954166031Sjkim			brgphy_fixup_adc_bug(sc);
955213464Syongari		if (bge_sc->bge_phy_flags & BGE_PHY_ADJUST_TRIM)
956166673Sjkim			brgphy_fixup_adjust_trim(sc);
957213464Syongari		if (bge_sc->bge_phy_flags & BGE_PHY_BER_BUG)
958166031Sjkim			brgphy_fixup_ber_bug(sc);
959213464Syongari		if (bge_sc->bge_phy_flags & BGE_PHY_CRC_BUG)
960166677Sjkim			brgphy_fixup_crc_bug(sc);
961213464Syongari		if (bge_sc->bge_phy_flags & BGE_PHY_JITTER_BUG)
962166031Sjkim			brgphy_fixup_jitter_bug(sc);
963157642Sps
964231913Smarius		if (bge_sc->bge_flags & BGE_FLAG_JUMBO)
965231913Smarius			brgphy_jumbo_settings(sc, ifp->if_mtu);
966157642Sps
967221468Syongari		if ((bge_sc->bge_phy_flags & BGE_PHY_NO_WIRESPEED) == 0)
968166032Sjkim			brgphy_ethernet_wirespeed(sc);
969166032Sjkim
970166032Sjkim		/* Enable Link LED on Dell boxes */
971213464Syongari		if (bge_sc->bge_phy_flags & BGE_PHY_NO_3LED) {
972164830Smarius			PHY_WRITE(sc, BRGPHY_MII_PHY_EXTCTL,
973166032Sjkim			    PHY_READ(sc, BRGPHY_MII_PHY_EXTCTL) &
974166032Sjkim			    ~BRGPHY_PHY_EXTCTL_3_LED);
975157642Sps		}
976178667Sjhb
977178667Sjhb		/* Adjust output voltage (From Linux driver) */
978178667Sjhb		if (bge_sc->bge_asicrev == BGE_ASICREV_BCM5906)
979178667Sjhb			PHY_WRITE(sc, BRGPHY_MII_EPHY_PTEST, 0x12);
980166032Sjkim	} else if (bce_sc) {
981170391Sdavidch		if (BCE_CHIP_NUM(bce_sc) == BCE_CHIP_NUM_5708 &&
982176850Sdavidch			(bce_sc->bce_phy_flags & BCE_PHY_SERDES_FLAG)) {
983170391Sdavidch
984170391Sdavidch			/* Store autoneg capabilities/results in digital block (Page 0) */
985170391Sdavidch			PHY_WRITE(sc, BRGPHY_5708S_BLOCK_ADDR, BRGPHY_5708S_DIG3_PG2);
986181618Syongari			PHY_WRITE(sc, BRGPHY_5708S_PG2_DIGCTL_3_0,
987170391Sdavidch				BRGPHY_5708S_PG2_DIGCTL_3_0_USE_IEEE);
988170391Sdavidch			PHY_WRITE(sc, BRGPHY_5708S_BLOCK_ADDR, BRGPHY_5708S_DIG_PG0);
989170391Sdavidch
990170391Sdavidch			/* Enable fiber mode and autodetection */
991181618Syongari			PHY_WRITE(sc, BRGPHY_5708S_PG0_1000X_CTL1,
992181618Syongari				PHY_READ(sc, BRGPHY_5708S_PG0_1000X_CTL1) |
993181618Syongari				BRGPHY_5708S_PG0_1000X_CTL1_AUTODET_EN |
994170391Sdavidch				BRGPHY_5708S_PG0_1000X_CTL1_FIBER_MODE);
995170391Sdavidch
996170391Sdavidch			/* Enable parallel detection */
997181618Syongari			PHY_WRITE(sc, BRGPHY_5708S_PG0_1000X_CTL2,
998181618Syongari				PHY_READ(sc, BRGPHY_5708S_PG0_1000X_CTL2) |
999170391Sdavidch				BRGPHY_5708S_PG0_1000X_CTL2_PAR_DET_EN);
1000170391Sdavidch
1001170391Sdavidch			/* Advertise 2.5G support through next page during autoneg */
1002170391Sdavidch			if (bce_sc->bce_phy_flags & BCE_PHY_2_5G_CAPABLE_FLAG)
1003181618Syongari				PHY_WRITE(sc, BRGPHY_5708S_ANEG_NXT_PG_XMIT1,
1004181618Syongari					PHY_READ(sc, BRGPHY_5708S_ANEG_NXT_PG_XMIT1) |
1005170391Sdavidch					BRGPHY_5708S_ANEG_NXT_PG_XMIT1_25G);
1006170391Sdavidch
1007170391Sdavidch			/* Increase TX signal amplitude */
1008170391Sdavidch			if ((BCE_CHIP_ID(bce_sc) == BCE_CHIP_ID_5708_A0) ||
1009170391Sdavidch			    (BCE_CHIP_ID(bce_sc) == BCE_CHIP_ID_5708_B0) ||
1010170391Sdavidch			    (BCE_CHIP_ID(bce_sc) == BCE_CHIP_ID_5708_B1)) {
1011181618Syongari				PHY_WRITE(sc, BRGPHY_5708S_BLOCK_ADDR,
1012170391Sdavidch					BRGPHY_5708S_TX_MISC_PG5);
1013181618Syongari				PHY_WRITE(sc, BRGPHY_5708S_PG5_TXACTL1,
1014170391Sdavidch					PHY_READ(sc, BRGPHY_5708S_PG5_TXACTL1) & ~0x30);
1015181618Syongari				PHY_WRITE(sc, BRGPHY_5708S_BLOCK_ADDR,
1016170391Sdavidch					BRGPHY_5708S_DIG_PG0);
1017170391Sdavidch			}
1018170391Sdavidch
1019181618Syongari			/* Backplanes use special driver/pre-driver/pre-emphasis values. */
1020170391Sdavidch			if ((bce_sc->bce_shared_hw_cfg & BCE_SHARED_HW_CFG_PHY_BACKPLANE) &&
1021170391Sdavidch				(bce_sc->bce_port_hw_cfg & BCE_PORT_HW_CFG_CFG_TXCTL3_MASK)) {
1022181618Syongari					PHY_WRITE(sc, BRGPHY_5708S_BLOCK_ADDR,
1023170391Sdavidch						BRGPHY_5708S_TX_MISC_PG5);
1024181618Syongari					PHY_WRITE(sc, BRGPHY_5708S_PG5_TXACTL3,
1025181618Syongari						bce_sc->bce_port_hw_cfg &
1026170391Sdavidch						BCE_PORT_HW_CFG_CFG_TXCTL3_MASK);
1027181618Syongari					PHY_WRITE(sc, BRGPHY_5708S_BLOCK_ADDR,
1028170391Sdavidch						BRGPHY_5708S_DIG_PG0);
1029170391Sdavidch			}
1030205299Sdavidch		} else if (BCE_CHIP_NUM(bce_sc) == BCE_CHIP_NUM_5709 &&
1031205299Sdavidch			(bce_sc->bce_phy_flags & BCE_PHY_SERDES_FLAG)) {
1032205299Sdavidch
1033212307Syongari			/* Select the SerDes Digital block of the AN MMD. */
1034212307Syongari			PHY_WRITE(sc, BRGPHY_BLOCK_ADDR, BRGPHY_BLOCK_ADDR_SERDES_DIG);
1035205299Sdavidch			val = PHY_READ(sc, BRGPHY_SERDES_DIG_1000X_CTL1);
1036205299Sdavidch			val &= ~BRGPHY_SD_DIG_1000X_CTL1_AUTODET;
1037205299Sdavidch			val |= BRGPHY_SD_DIG_1000X_CTL1_FIBER;
1038205299Sdavidch			PHY_WRITE(sc, BRGPHY_SERDES_DIG_1000X_CTL1, val);
1039205299Sdavidch
1040212307Syongari			/* Select the Over 1G block of the AN MMD. */
1041205299Sdavidch			PHY_WRITE(sc, BRGPHY_BLOCK_ADDR, BRGPHY_BLOCK_ADDR_OVER_1G);
1042205299Sdavidch
1043212307Syongari			/* Enable autoneg "Next Page" to advertise 2.5G support. */
1044212307Syongari			val = PHY_READ(sc, BRGPHY_OVER_1G_UNFORMAT_PG1);
1045205299Sdavidch			if (bce_sc->bce_phy_flags & BCE_PHY_2_5G_CAPABLE_FLAG)
1046205299Sdavidch				val |= BRGPHY_5708S_ANEG_NXT_PG_XMIT1_25G;
1047205299Sdavidch			else
1048205299Sdavidch				val &= ~BRGPHY_5708S_ANEG_NXT_PG_XMIT1_25G;
1049205299Sdavidch			PHY_WRITE(sc, BRGPHY_OVER_1G_UNFORMAT_PG1, val);
1050205299Sdavidch
1051212307Syongari			/* Select the Multi-Rate Backplane Ethernet block of the AN MMD. */
1052205299Sdavidch			PHY_WRITE(sc, BRGPHY_BLOCK_ADDR, BRGPHY_BLOCK_ADDR_MRBE);
1053205299Sdavidch
1054212307Syongari			/* Enable MRBE speed autoneg. */
1055212307Syongari			val = PHY_READ(sc, BRGPHY_MRBE_MSG_PG5_NP);
1056205299Sdavidch			val |= BRGPHY_MRBE_MSG_PG5_NP_MBRE |
1057205299Sdavidch			    BRGPHY_MRBE_MSG_PG5_NP_T2;
1058205299Sdavidch			PHY_WRITE(sc, BRGPHY_MRBE_MSG_PG5_NP, val);
1059205299Sdavidch
1060212307Syongari			/* Select the Clause 73 User B0 block of the AN MMD. */
1061212307Syongari			PHY_WRITE(sc, BRGPHY_BLOCK_ADDR, BRGPHY_BLOCK_ADDR_CL73_USER_B0);
1062205299Sdavidch
1063212307Syongari			/* Enable MRBE speed autoneg. */
1064205299Sdavidch			PHY_WRITE(sc, BRGPHY_CL73_USER_B0_MBRE_CTL1,
1065205299Sdavidch			    BRGPHY_CL73_USER_B0_MBRE_CTL1_NP_AFT_BP |
1066205299Sdavidch			    BRGPHY_CL73_USER_B0_MBRE_CTL1_STA_MGR |
1067205299Sdavidch			    BRGPHY_CL73_USER_B0_MBRE_CTL1_ANEG);
1068205299Sdavidch
1069212307Syongari			/* Restore IEEE0 block (assumed in all brgphy(4) code). */
1070212307Syongari			PHY_WRITE(sc, BRGPHY_BLOCK_ADDR, BRGPHY_BLOCK_ADDR_COMBO_IEEE0);
1071205299Sdavidch        } else if (BCE_CHIP_NUM(bce_sc) == BCE_CHIP_NUM_5709) {
1072179772Sdavidch			if ((BCE_CHIP_REV(bce_sc) == BCE_CHIP_REV_Ax) ||
1073179772Sdavidch				(BCE_CHIP_REV(bce_sc) == BCE_CHIP_REV_Bx))
1074179772Sdavidch				brgphy_fixup_disable_early_dac(sc);
1075212307Syongari
1076179772Sdavidch			brgphy_jumbo_settings(sc, ifp->if_mtu);
1077179772Sdavidch			brgphy_ethernet_wirespeed(sc);
1078170391Sdavidch		} else {
1079170391Sdavidch			brgphy_fixup_ber_bug(sc);
1080170391Sdavidch			brgphy_jumbo_settings(sc, ifp->if_mtu);
1081170391Sdavidch			brgphy_ethernet_wirespeed(sc);
1082170391Sdavidch		}
1083119157Sambrisko	}
1084114590Sps}
1085