xref: /freebsd/sys/dev/mii/brgphy.c (revision 94942af266ac119ede0ca836f9aa5a5ac0582938)
1 /*-
2  * Copyright (c) 2000
3  *	Bill Paul <wpaul@ee.columbia.edu>.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by Bill Paul.
16  * 4. Neither the name of the author nor the names of any co-contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
24  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
30  * THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
35 
36 /*
37  * Driver for the Broadcom BCM54xx/57xx 1000baseTX PHY.
38  */
39 
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/kernel.h>
43 #include <sys/module.h>
44 #include <sys/socket.h>
45 #include <sys/bus.h>
46 
47 #include <net/if.h>
48 #include <net/ethernet.h>
49 #include <net/if_media.h>
50 
51 #include <dev/mii/mii.h>
52 #include <dev/mii/miivar.h>
53 #include "miidevs.h"
54 
55 #include <dev/mii/brgphyreg.h>
56 #include <net/if_arp.h>
57 #include <machine/bus.h>
58 #include <dev/bge/if_bgereg.h>
59 #include <dev/bce/if_bcereg.h>
60 
61 #include <dev/pci/pcireg.h>
62 #include <dev/pci/pcivar.h>
63 
64 #include "miibus_if.h"
65 
66 static int brgphy_probe(device_t);
67 static int brgphy_attach(device_t);
68 
69 struct brgphy_softc {
70 	struct mii_softc mii_sc;
71 	int mii_model;
72 	int mii_rev;
73 };
74 
75 static device_method_t brgphy_methods[] = {
76 	/* device interface */
77 	DEVMETHOD(device_probe,		brgphy_probe),
78 	DEVMETHOD(device_attach,	brgphy_attach),
79 	DEVMETHOD(device_detach,	mii_phy_detach),
80 	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
81 	{ 0, 0 }
82 };
83 
84 static devclass_t brgphy_devclass;
85 
86 static driver_t brgphy_driver = {
87 	"brgphy",
88 	brgphy_methods,
89 	sizeof(struct brgphy_softc)
90 };
91 
92 DRIVER_MODULE(brgphy, miibus, brgphy_driver, brgphy_devclass, 0, 0);
93 
94 static int	brgphy_service(struct mii_softc *, struct mii_data *, int);
95 static void	brgphy_setmedia(struct mii_softc *, int, int);
96 static void	brgphy_status(struct mii_softc *);
97 static int	brgphy_mii_phy_auto(struct mii_softc *);
98 static void	brgphy_reset(struct mii_softc *);
99 static void	brgphy_loop(struct mii_softc *);
100 static int	bcm5706_is_tbi(device_t);
101 static void	bcm5401_load_dspcode(struct mii_softc *);
102 static void	bcm5411_load_dspcode(struct mii_softc *);
103 static void	brgphy_fixup_5704_a0_bug(struct mii_softc *);
104 static void	brgphy_fixup_adc_bug(struct mii_softc *);
105 static void	brgphy_fixup_adjust_trim(struct mii_softc *);
106 static void	brgphy_fixup_ber_bug(struct mii_softc *);
107 static void	brgphy_fixup_crc_bug(struct mii_softc *);
108 static void	brgphy_fixup_jitter_bug(struct mii_softc *);
109 static void	brgphy_ethernet_wirespeed(struct mii_softc *);
110 static void	brgphy_jumbo_settings(struct mii_softc *, u_long);
111 
112 static const struct mii_phydesc brgphys[] = {
113 	MII_PHY_DESC(xxBROADCOM, BCM5400),
114 	MII_PHY_DESC(xxBROADCOM, BCM5401),
115 	MII_PHY_DESC(xxBROADCOM, BCM5411),
116 	MII_PHY_DESC(xxBROADCOM, BCM5701),
117 	MII_PHY_DESC(xxBROADCOM, BCM5703),
118 	MII_PHY_DESC(xxBROADCOM, BCM5704),
119 	MII_PHY_DESC(xxBROADCOM, BCM5705),
120 	MII_PHY_DESC(xxBROADCOM, BCM5706C),
121 	MII_PHY_DESC(xxBROADCOM, BCM5714),
122 	MII_PHY_DESC(xxBROADCOM, BCM5750),
123 	MII_PHY_DESC(xxBROADCOM, BCM5752),
124 	MII_PHY_DESC(xxBROADCOM, BCM5754),
125 	MII_PHY_DESC(xxBROADCOM, BCM5780),
126 	MII_PHY_DESC(xxBROADCOM, BCM5708C),
127 	MII_PHY_DESC(xxBROADCOM_ALT1, BCM5755),
128 	MII_PHY_DESC(xxBROADCOM_ALT1, BCM5787),
129 	MII_PHY_END
130 };
131 
132 static int
133 brgphy_probe(device_t dev)
134 {
135 	struct mii_attach_args *ma;
136 	int error;
137 
138 	error = mii_phy_dev_probe(dev, brgphys, BUS_PROBE_DEFAULT);
139 	if (error != BUS_PROBE_DEFAULT)
140 		return (error);
141 
142 	ma = device_get_ivars(dev);
143 	if (MII_OUI(ma->mii_id1, ma->mii_id2) == MII_OUI_xxBROADCOM &&
144 	    MII_MODEL(ma->mii_id2) == MII_MODEL_xxBROADCOM_BCM5706C) {
145 		/*
146 		 * Broadcom uses the same MII model ID on two
147 		 * different types of phys.  The first is found on the
148 		 * BCM 5706 and is supported by this driver.  The
149 		 * other is found on the BCM 5706S and 5708S and is
150 		 * supported by the gentbi(4) driver, so we check to
151 		 * see if this phy is supported by gentbi(4) and fail
152 		 * the probe if so.
153 		 */
154 		if (bcm5706_is_tbi(dev))
155 			return (ENXIO);
156 	}
157 	return (error);
158 }
159 
160 static int
161 brgphy_attach(device_t dev)
162 {
163 	struct brgphy_softc *bsc;
164 	struct mii_softc *sc;
165 	struct mii_attach_args *ma;
166 	struct mii_data *mii;
167 
168 	bsc = device_get_softc(dev);
169 	sc = &bsc->mii_sc;
170 	ma = device_get_ivars(dev);
171 	sc->mii_dev = device_get_parent(dev);
172 	mii = device_get_softc(sc->mii_dev);
173 
174 	/*
175 	 * At least some variants wedge when isolating, so never allow
176 	 * non-zero instances! At least some also don't support loopback.
177 	 */
178 	if (mii->mii_instance != 0)
179 		panic("%s: ignoring this PHY, non-zero instance", __func__);
180 
181 	LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list);
182 
183 	sc->mii_inst = mii->mii_instance;
184 	sc->mii_phy = ma->mii_phyno;
185 	sc->mii_service = brgphy_service;
186 	sc->mii_pdata = mii;
187 
188 	sc->mii_flags |= MIIF_NOISOLATE | MIIF_NOLOOP;
189 	mii->mii_instance++;
190 
191 	bsc->mii_model = MII_MODEL(ma->mii_id2);
192 	bsc->mii_rev = MII_REV(ma->mii_id2);
193 	brgphy_reset(sc);
194 
195 	sc->mii_capabilities = PHY_READ(sc, MII_BMSR) & ma->mii_capmask;
196 	if (sc->mii_capabilities & BMSR_EXTSTAT)
197 		sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR);
198 	device_printf(dev, " ");
199 	mii_phy_add_media(sc);
200 	printf("\n");
201 
202 	MIIBUS_MEDIAINIT(sc->mii_dev);
203 	return (0);
204 }
205 
206 static int
207 brgphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
208 {
209 	struct brgphy_softc *bsc = (struct brgphy_softc *)sc;
210 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
211 
212 	switch (cmd) {
213 	case MII_POLLSTAT:
214 		/* If we're not polling our PHY instance, just return. */
215 		if (IFM_INST(ife->ifm_media) != sc->mii_inst)
216 			return (0);
217 		break;
218 	case MII_MEDIACHG:
219 		/* If the interface is not up, don't do anything. */
220 		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
221 			break;
222 
223 		brgphy_reset(sc);	/* XXX hardware bug work-around */
224 
225 		switch (IFM_SUBTYPE(ife->ifm_media)) {
226 		case IFM_AUTO:
227 #ifdef foo
228 			/* If we're already in auto mode, just return. */
229 			if (PHY_READ(sc, BRGPHY_MII_BMCR) & BRGPHY_BMCR_AUTOEN)
230 				return (0);
231 #endif
232 			(void)brgphy_mii_phy_auto(sc);
233 			break;
234 		case IFM_1000_T:
235 		case IFM_100_TX:
236 		case IFM_10_T:
237 			brgphy_setmedia(sc, ife->ifm_media,
238 			    mii->mii_ifp->if_flags & IFF_LINK0);
239 			break;
240 		default:
241 			return (EINVAL);
242 		}
243 		break;
244 	case MII_TICK:
245 		/* If we're not currently selected, just return. */
246 		if (IFM_INST(ife->ifm_media) != sc->mii_inst)
247 			return (0);
248 
249 		/* Is the interface even up? */
250 		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
251 			return (0);
252 
253 		/* Only used for autonegotiation. */
254 		if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) {
255 			sc->mii_ticks = 0;	/* Reset autoneg timer. */
256 			break;
257 		}
258 
259 		/*
260 		 * Check to see if we have link.  If we do, we don't
261 		 * need to restart the autonegotiation process.
262 		 */
263 		if (PHY_READ(sc, BRGPHY_MII_AUXSTS) & BRGPHY_AUXSTS_LINK) {
264 			sc->mii_ticks = 0;	/* Reset autoneg timer. */
265 			break;
266 		}
267 
268 		/* Announce link loss right after it happens. */
269 		if (sc->mii_ticks++ == 0)
270 			break;
271 
272 		/* Only retry autonegotiation every mii_anegticks seconds. */
273 		if (sc->mii_ticks <= sc->mii_anegticks)
274 			return (0);
275 
276 		sc->mii_ticks = 0;
277 		(void)brgphy_mii_phy_auto(sc);
278 		break;
279 	}
280 
281 	/* Update the media status. */
282 	brgphy_status(sc);
283 
284 	/*
285 	 * Callback if something changed. Note that we need to poke
286 	 * the DSP on the Broadcom PHYs if the media changes.
287 	 */
288 	if (sc->mii_media_active != mii->mii_media_active ||
289 	    sc->mii_media_status != mii->mii_media_status ||
290 	    cmd == MII_MEDIACHG) {
291 		switch (bsc->mii_model) {
292 		case MII_MODEL_xxBROADCOM_BCM5400:
293 			bcm5401_load_dspcode(sc);
294 			break;
295 		case MII_MODEL_xxBROADCOM_BCM5401:
296 			if (bsc->mii_rev == 1 || bsc->mii_rev == 3)
297 				bcm5401_load_dspcode(sc);
298 			break;
299 		case MII_MODEL_xxBROADCOM_BCM5411:
300 			bcm5411_load_dspcode(sc);
301 			break;
302 		}
303 	}
304 	mii_phy_update(sc, cmd);
305 	return (0);
306 }
307 
308 static void
309 brgphy_setmedia(struct mii_softc *sc, int media, int master)
310 {
311 	struct brgphy_softc *bsc = (struct brgphy_softc *)sc;
312 	int bmcr, gig;
313 
314 	switch (IFM_SUBTYPE(media)) {
315 	case IFM_1000_T:
316 		bmcr = BRGPHY_S1000;
317 		break;
318 	case IFM_100_TX:
319 		bmcr = BRGPHY_S100;
320 		break;
321 	case IFM_10_T:
322 	default:
323 		bmcr = BRGPHY_S10;
324 		break;
325 	}
326 	if ((media & IFM_GMASK) == IFM_FDX) {
327 		bmcr |= BRGPHY_BMCR_FDX;
328 		gig = BRGPHY_1000CTL_AFD;
329 	} else {
330 		gig = BRGPHY_1000CTL_AHD;
331 	}
332 
333 	brgphy_loop(sc);
334 	PHY_WRITE(sc, BRGPHY_MII_1000CTL, 0);
335 	PHY_WRITE(sc, BRGPHY_MII_BMCR, bmcr);
336 	PHY_WRITE(sc, BRGPHY_MII_ANAR, BRGPHY_SEL_TYPE);
337 
338 	if (IFM_SUBTYPE(media) != IFM_1000_T)
339 		return;
340 
341 	PHY_WRITE(sc, BRGPHY_MII_1000CTL, gig);
342 	PHY_WRITE(sc, BRGPHY_MII_BMCR,
343 	    bmcr | BRGPHY_BMCR_AUTOEN | BRGPHY_BMCR_STARTNEG);
344 
345 	if (bsc->mii_model != MII_MODEL_xxBROADCOM_BCM5701)
346 		return;
347 
348 	/*
349 	 * When setting the link manually, one side must be the master and
350 	 * the other the slave. However ifmedia doesn't give us a good way
351 	 * to specify this, so we fake it by using one of the LINK flags.
352 	 * If LINK0 is set, we program the PHY to be a master, otherwise
353 	 * it's a slave.
354 	 */
355 	if (master) {
356 		PHY_WRITE(sc, BRGPHY_MII_1000CTL,
357 		    gig | BRGPHY_1000CTL_MSE | BRGPHY_1000CTL_MSC);
358 	} else {
359 		PHY_WRITE(sc, BRGPHY_MII_1000CTL,
360 		    gig | BRGPHY_1000CTL_MSE);
361 	}
362 }
363 
364 static void
365 brgphy_status(struct mii_softc *sc)
366 {
367 	struct mii_data *mii = sc->mii_pdata;
368 	int aux, bmcr, bmsr;
369 
370 	mii->mii_media_status = IFM_AVALID;
371 	mii->mii_media_active = IFM_ETHER;
372 
373 	aux = PHY_READ(sc, BRGPHY_MII_AUXSTS);
374 	bmcr = PHY_READ(sc, BRGPHY_MII_BMCR);
375 	bmsr = PHY_READ(sc, BRGPHY_MII_BMSR);
376 
377 	if (aux & BRGPHY_AUXSTS_LINK)
378 		mii->mii_media_status |= IFM_ACTIVE;
379 
380 	if (bmcr & BRGPHY_BMCR_LOOP)
381 		mii->mii_media_active |= IFM_LOOP;
382 
383 	if ((bmcr & BRGPHY_BMCR_AUTOEN) &&
384 	    (bmsr & BRGPHY_BMSR_ACOMP) == 0) {
385 		/* Erg, still trying, I guess... */
386 		mii->mii_media_active |= IFM_NONE;
387 		return;
388 	}
389 
390 	if (aux & BRGPHY_AUXSTS_LINK) {
391 		switch (aux & BRGPHY_AUXSTS_AN_RES) {
392 		case BRGPHY_RES_1000FD:
393 			mii->mii_media_active |= IFM_1000_T | IFM_FDX;
394 			break;
395 		case BRGPHY_RES_1000HD:
396 			mii->mii_media_active |= IFM_1000_T | IFM_HDX;
397 			break;
398 		case BRGPHY_RES_100FD:
399 			mii->mii_media_active |= IFM_100_TX | IFM_FDX;
400 			break;
401 		case BRGPHY_RES_100T4:
402 			mii->mii_media_active |= IFM_100_T4;
403 			break;
404 		case BRGPHY_RES_100HD:
405 			mii->mii_media_active |= IFM_100_TX | IFM_HDX;
406 			break;
407 		case BRGPHY_RES_10FD:
408 			mii->mii_media_active |= IFM_10_T | IFM_FDX;
409 			break;
410 		case BRGPHY_RES_10HD:
411 			mii->mii_media_active |= IFM_10_T | IFM_HDX;
412 			break;
413 		default:
414 			mii->mii_media_active |= IFM_NONE;
415 			break;
416 		}
417 	} else
418 		mii->mii_media_active |= IFM_NONE;
419 }
420 
421 static int
422 brgphy_mii_phy_auto(struct mii_softc *sc)
423 {
424 	struct brgphy_softc *bsc = (struct brgphy_softc *)sc;
425 	int ktcr = 0;
426 
427 	brgphy_loop(sc);
428 	brgphy_reset(sc);
429 	ktcr = BRGPHY_1000CTL_AFD | BRGPHY_1000CTL_AHD;
430 	if (bsc->mii_model == MII_MODEL_xxBROADCOM_BCM5701)
431 		ktcr |= BRGPHY_1000CTL_MSE | BRGPHY_1000CTL_MSC;
432 	PHY_WRITE(sc, BRGPHY_MII_1000CTL, ktcr);
433 	ktcr = PHY_READ(sc, BRGPHY_MII_1000CTL);
434 	DELAY(1000);
435 	PHY_WRITE(sc, BRGPHY_MII_ANAR,
436 	    BMSR_MEDIA_TO_ANAR(sc->mii_capabilities) | ANAR_CSMA);
437 	DELAY(1000);
438 	PHY_WRITE(sc, BRGPHY_MII_BMCR,
439 	    BRGPHY_BMCR_AUTOEN | BRGPHY_BMCR_STARTNEG);
440 	PHY_WRITE(sc, BRGPHY_MII_IMR, 0xFF00);
441 	return (EJUSTRETURN);
442 }
443 
444 static void
445 brgphy_loop(struct mii_softc *sc)
446 {
447 	int i;
448 
449 	PHY_WRITE(sc, BRGPHY_MII_BMCR, BRGPHY_BMCR_LOOP);
450 	for (i = 0; i < 15000; i++) {
451 		if (!(PHY_READ(sc, BRGPHY_MII_BMSR) & BRGPHY_BMSR_LINK)) {
452 #if 0
453 			device_printf(sc->mii_dev, "looped %d\n", i);
454 #endif
455 			break;
456 		}
457 		DELAY(10);
458 	}
459 }
460 
461 /*
462  * Check to see if a 5706 phy is really a SerDes phy.  Copied from
463  * gentbi_probe().
464  */
465 static int
466 bcm5706_is_tbi(device_t dev)
467 {
468 	device_t parent;
469 	struct mii_attach_args *ma;
470 	int bmsr, extsr;
471 
472 	parent = device_get_parent(dev);
473 	ma = device_get_ivars(dev);
474 
475 	bmsr = MIIBUS_READREG(parent, ma->mii_phyno, MII_BMSR);
476 	if ((bmsr & BMSR_EXTSTAT) == 0 || (bmsr & BMSR_MEDIAMASK) != 0)
477 		return (0);
478 
479 	extsr = MIIBUS_READREG(parent, ma->mii_phyno, MII_EXTSR);
480 	if (extsr & (EXTSR_1000TFDX|EXTSR_1000THDX))
481 		return (0);
482 
483 	if (extsr & (EXTSR_1000XFDX|EXTSR_1000XHDX))
484 		return (1);
485 
486 	return (0);
487 }
488 
489 /* Turn off tap power management on 5401. */
490 static void
491 bcm5401_load_dspcode(struct mii_softc *sc)
492 {
493 	static const struct {
494 		int		reg;
495 		uint16_t	val;
496 	} dspcode[] = {
497 		{ BRGPHY_MII_AUXCTL,		0x0c20 },
498 		{ BRGPHY_MII_DSP_ADDR_REG,	0x0012 },
499 		{ BRGPHY_MII_DSP_RW_PORT,	0x1804 },
500 		{ BRGPHY_MII_DSP_ADDR_REG,	0x0013 },
501 		{ BRGPHY_MII_DSP_RW_PORT,	0x1204 },
502 		{ BRGPHY_MII_DSP_ADDR_REG,	0x8006 },
503 		{ BRGPHY_MII_DSP_RW_PORT,	0x0132 },
504 		{ BRGPHY_MII_DSP_ADDR_REG,	0x8006 },
505 		{ BRGPHY_MII_DSP_RW_PORT,	0x0232 },
506 		{ BRGPHY_MII_DSP_ADDR_REG,	0x201f },
507 		{ BRGPHY_MII_DSP_RW_PORT,	0x0a20 },
508 		{ 0,				0 },
509 	};
510 	int i;
511 
512 	for (i = 0; dspcode[i].reg != 0; i++)
513 		PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
514 	DELAY(40);
515 }
516 
517 static void
518 bcm5411_load_dspcode(struct mii_softc *sc)
519 {
520 	static const struct {
521 		int		reg;
522 		uint16_t	val;
523 	} dspcode[] = {
524 		{ 0x1c,				0x8c23 },
525 		{ 0x1c,				0x8ca3 },
526 		{ 0x1c,				0x8c23 },
527 		{ 0,				0 },
528 	};
529 	int i;
530 
531 	for (i = 0; dspcode[i].reg != 0; i++)
532 		PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
533 }
534 
535 static void
536 brgphy_fixup_5704_a0_bug(struct mii_softc *sc)
537 {
538 	static const struct {
539 		int		reg;
540 		uint16_t	val;
541 	} dspcode[] = {
542 		{ 0x1c,				0x8d68 },
543 		{ 0x1c,				0x8d68 },
544 		{ 0,				0 },
545 	};
546 	int i;
547 
548 	for (i = 0; dspcode[i].reg != 0; i++)
549 		PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
550 }
551 
552 static void
553 brgphy_fixup_adc_bug(struct mii_softc *sc)
554 {
555 	static const struct {
556 		int		reg;
557 		uint16_t	val;
558 	} dspcode[] = {
559 		{ BRGPHY_MII_AUXCTL,		0x0c00 },
560 		{ BRGPHY_MII_DSP_ADDR_REG,	0x201f },
561 		{ BRGPHY_MII_DSP_RW_PORT,	0x2aaa },
562 		{ 0,				0 },
563 	};
564 	int i;
565 
566 	for (i = 0; dspcode[i].reg != 0; i++)
567 		PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
568 }
569 
570 static void
571 brgphy_fixup_adjust_trim(struct mii_softc *sc)
572 {
573 	static const struct {
574 		int		reg;
575 		uint16_t	val;
576 	} dspcode[] = {
577 		{ BRGPHY_MII_AUXCTL,		0x0c00 },
578 		{ BRGPHY_MII_DSP_ADDR_REG,	0x000a },
579 		{ BRGPHY_MII_DSP_RW_PORT,	0x110b },
580 		{ BRGPHY_MII_TEST1,		0x0014 },
581 		{ BRGPHY_MII_AUXCTL,		0x0400 },
582 		{ 0,				0 },
583 	};
584 	int i;
585 
586 	for (i = 0; dspcode[i].reg != 0; i++)
587 		PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
588 }
589 
590 static void
591 brgphy_fixup_ber_bug(struct mii_softc *sc)
592 {
593 	static const struct {
594 		int		reg;
595 		uint16_t	val;
596 	} dspcode[] = {
597 		{ BRGPHY_MII_AUXCTL,		0x0c00 },
598 		{ BRGPHY_MII_DSP_ADDR_REG,	0x000a },
599 		{ BRGPHY_MII_DSP_RW_PORT,	0x310b },
600 		{ BRGPHY_MII_DSP_ADDR_REG,	0x201f },
601 		{ BRGPHY_MII_DSP_RW_PORT,	0x9506 },
602 		{ BRGPHY_MII_DSP_ADDR_REG,	0x401f },
603 		{ BRGPHY_MII_DSP_RW_PORT,	0x14e2 },
604 		{ BRGPHY_MII_AUXCTL,		0x0400 },
605 		{ 0,				0 },
606 	};
607 	int i;
608 
609 	for (i = 0; dspcode[i].reg != 0; i++)
610 		PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
611 }
612 
613 static void
614 brgphy_fixup_crc_bug(struct mii_softc *sc)
615 {
616 	static const struct {
617 		int		reg;
618 		uint16_t	val;
619 	} dspcode[] = {
620 		{ BRGPHY_MII_DSP_RW_PORT,	0x0a75 },
621 		{ 0x1c,				0x8c68 },
622 		{ 0x1c,				0x8d68 },
623 		{ 0x1c,				0x8c68 },
624 		{ 0,				0 },
625 	};
626 	int i;
627 
628 	for (i = 0; dspcode[i].reg != 0; i++)
629 		PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
630 }
631 
632 static void
633 brgphy_fixup_jitter_bug(struct mii_softc *sc)
634 {
635 	static const struct {
636 		int		reg;
637 		uint16_t	val;
638 	} dspcode[] = {
639 		{ BRGPHY_MII_AUXCTL,		0x0c00 },
640 		{ BRGPHY_MII_DSP_ADDR_REG,	0x000a },
641 		{ BRGPHY_MII_DSP_RW_PORT,	0x010b },
642 		{ BRGPHY_MII_AUXCTL,		0x0400 },
643 		{ 0,				0 },
644 	};
645 	int i;
646 
647 	for (i = 0; dspcode[i].reg != 0; i++)
648 		PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
649 }
650 
651 static void
652 brgphy_ethernet_wirespeed(struct mii_softc *sc)
653 {
654 	uint32_t	val;
655 
656 	/* Enable Ethernet@WireSpeed. */
657 	PHY_WRITE(sc, BRGPHY_MII_AUXCTL, 0x7007);
658 	val = PHY_READ(sc, BRGPHY_MII_AUXCTL);
659 	PHY_WRITE(sc, BRGPHY_MII_AUXCTL, val | (1 << 15) | (1 << 4));
660 }
661 
662 static void
663 brgphy_jumbo_settings(struct mii_softc *sc, u_long mtu)
664 {
665 	struct brgphy_softc *bsc = (struct brgphy_softc *)sc;
666 	uint32_t	val;
667 
668 	/* Set or clear jumbo frame settings in the PHY. */
669 	if (mtu > ETHER_MAX_LEN) {
670 		if (bsc->mii_model == MII_MODEL_xxBROADCOM_BCM5401) {
671 			/* BCM5401 PHY cannot read-modify-write. */
672 			PHY_WRITE(sc, BRGPHY_MII_AUXCTL, 0x4c20);
673 		} else {
674 			PHY_WRITE(sc, BRGPHY_MII_AUXCTL, 0x7);
675 			val = PHY_READ(sc, BRGPHY_MII_AUXCTL);
676 			PHY_WRITE(sc, BRGPHY_MII_AUXCTL,
677 			    val | BRGPHY_AUXCTL_LONG_PKT);
678 		}
679 
680 		val = PHY_READ(sc, BRGPHY_MII_PHY_EXTCTL);
681 		PHY_WRITE(sc, BRGPHY_MII_PHY_EXTCTL,
682 		    val | BRGPHY_PHY_EXTCTL_HIGH_LA);
683 	} else {
684 		PHY_WRITE(sc, BRGPHY_MII_AUXCTL, 0x7);
685 		val = PHY_READ(sc, BRGPHY_MII_AUXCTL);
686 		PHY_WRITE(sc, BRGPHY_MII_AUXCTL,
687 		    val & ~(BRGPHY_AUXCTL_LONG_PKT | 0x7));
688 
689 		val = PHY_READ(sc, BRGPHY_MII_PHY_EXTCTL);
690 		PHY_WRITE(sc, BRGPHY_MII_PHY_EXTCTL,
691 		    val & ~BRGPHY_PHY_EXTCTL_HIGH_LA);
692 	}
693 }
694 
695 static void
696 brgphy_reset(struct mii_softc *sc)
697 {
698 	struct brgphy_softc *bsc = (struct brgphy_softc *)sc;
699 	struct bge_softc *bge_sc = NULL;
700 	struct bce_softc *bce_sc = NULL;
701 	struct ifnet *ifp;
702 
703 	mii_phy_reset(sc);
704 
705 	switch (bsc->mii_model) {
706 	case MII_MODEL_xxBROADCOM_BCM5400:
707 		bcm5401_load_dspcode(sc);
708 		break;
709 	case MII_MODEL_xxBROADCOM_BCM5401:
710 		if (bsc->mii_rev == 1 || bsc->mii_rev == 3)
711 			bcm5401_load_dspcode(sc);
712 		break;
713 	case MII_MODEL_xxBROADCOM_BCM5411:
714 		bcm5411_load_dspcode(sc);
715 		break;
716 	}
717 
718 	ifp = sc->mii_pdata->mii_ifp;
719 
720 	/* Find the driver associated with this PHY. */
721 	if (strcmp(ifp->if_dname, "bge") == 0)	{
722 		bge_sc = ifp->if_softc;
723 	} else if (strcmp(ifp->if_dname, "bce") == 0) {
724 		bce_sc = ifp->if_softc;
725 	}
726 
727 	/* Handle any NetXtreme/bge workarounds. */
728 	if (bge_sc) {
729 		/* Fix up various bugs */
730 		if (bge_sc->bge_flags & BGE_FLAG_5704_A0_BUG)
731 			brgphy_fixup_5704_a0_bug(sc);
732 		if (bge_sc->bge_flags & BGE_FLAG_ADC_BUG)
733 			brgphy_fixup_adc_bug(sc);
734 		if (bge_sc->bge_flags & BGE_FLAG_ADJUST_TRIM)
735 			brgphy_fixup_adjust_trim(sc);
736 		if (bge_sc->bge_flags & BGE_FLAG_BER_BUG)
737 			brgphy_fixup_ber_bug(sc);
738 		if (bge_sc->bge_flags & BGE_FLAG_CRC_BUG)
739 			brgphy_fixup_crc_bug(sc);
740 		if (bge_sc->bge_flags & BGE_FLAG_JITTER_BUG)
741 			brgphy_fixup_jitter_bug(sc);
742 
743 		brgphy_jumbo_settings(sc, ifp->if_mtu);
744 
745 		/*
746 		 * Don't enable Ethernet@WireSpeed for the 5700 or the
747 		 * 5705 A1 and A2 chips.
748 		 */
749 		if (bge_sc->bge_asicrev != BGE_ASICREV_BCM5700 &&
750 		    bge_sc->bge_chipid != BGE_CHIPID_BCM5705_A1 &&
751 		    bge_sc->bge_chipid != BGE_CHIPID_BCM5705_A2)
752 			brgphy_ethernet_wirespeed(sc);
753 
754 		/* Enable Link LED on Dell boxes */
755 		if (bge_sc->bge_flags & BGE_FLAG_NO_3LED) {
756 			PHY_WRITE(sc, BRGPHY_MII_PHY_EXTCTL,
757 			    PHY_READ(sc, BRGPHY_MII_PHY_EXTCTL) &
758 			    ~BRGPHY_PHY_EXTCTL_3_LED);
759 		}
760 	} else if (bce_sc) {
761 		brgphy_fixup_ber_bug(sc);
762 		brgphy_jumbo_settings(sc, ifp->if_mtu);
763 		brgphy_ethernet_wirespeed(sc);
764 	}
765 }
766