xref: /openbsd-src/sys/dev/mii/brgphy.c (revision 2b0358df1d88d06ef4139321dd05bd5e05d91eaf)
1 /*	$OpenBSD: brgphy.c,v 1.84 2008/11/08 03:03:50 brad Exp $	*/
2 
3 /*
4  * Copyright (c) 2000
5  *	Bill Paul <wpaul@ee.columbia.edu>.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *	This product includes software developed by Bill Paul.
18  * 4. Neither the name of the author nor the names of any co-contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
26  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
32  * THE POSSIBILITY OF SUCH DAMAGE.
33  *
34  * $FreeBSD: brgphy.c,v 1.8 2002/03/22 06:38:52 wpaul Exp $
35  */
36 
37 /*
38  * Driver for the Broadcom BCR5400 1000baseTX PHY. Speed is always
39  * 1000mbps; all we need to negotiate here is full or half duplex.
40  */
41 
42 #include <sys/param.h>
43 #include <sys/systm.h>
44 #include <sys/kernel.h>
45 #include <sys/device.h>
46 #include <sys/socket.h>
47 #include <sys/errno.h>
48 
49 #include <machine/bus.h>
50 
51 #include <net/if.h>
52 #include <net/if_media.h>
53 
54 #include <netinet/in.h>
55 #include <netinet/if_ether.h>
56 
57 #include <dev/pci/pcivar.h>
58 
59 #include <dev/mii/mii.h>
60 #include <dev/mii/miivar.h>
61 #include <dev/mii/miidevs.h>
62 
63 #include <dev/mii/brgphyreg.h>
64 
65 #include <dev/pci/if_bgereg.h>
66 #include <dev/pci/if_bnxreg.h>
67 
68 int brgphy_probe(struct device *, void *, void *);
69 void brgphy_attach(struct device *, struct device *, void *);
70 
71 struct cfattach brgphy_ca = {
72 	sizeof(struct mii_softc), brgphy_probe, brgphy_attach, mii_phy_detach,
73 	    mii_phy_activate
74 };
75 
76 struct cfdriver brgphy_cd = {
77 	NULL, "brgphy", DV_DULL
78 };
79 
80 int	brgphy_service(struct mii_softc *, struct mii_data *, int);
81 void	brgphy_copper_status(struct mii_softc *);
82 void	brgphy_fiber_status(struct mii_softc *);
83 void	brgphy_5708s_status(struct mii_softc *);
84 int	brgphy_mii_phy_auto(struct mii_softc *);
85 void	brgphy_loop(struct mii_softc *);
86 void	brgphy_reset(struct mii_softc *);
87 void	brgphy_bcm5401_dspcode(struct mii_softc *);
88 void	brgphy_bcm5411_dspcode(struct mii_softc *);
89 void	brgphy_bcm5421_dspcode(struct mii_softc *);
90 void	brgphy_bcm54k2_dspcode(struct mii_softc *);
91 void	brgphy_adc_bug(struct mii_softc *);
92 void	brgphy_5704_a0_bug(struct mii_softc *);
93 void	brgphy_ber_bug(struct mii_softc *);
94 void	brgphy_crc_bug(struct mii_softc *);
95 void	brgphy_jumbo_settings(struct mii_softc *);
96 void	brgphy_eth_wirespeed(struct mii_softc *);
97 
98 const struct mii_phy_funcs brgphy_copper_funcs = {
99 	brgphy_service, brgphy_copper_status, brgphy_reset,
100 };
101 
102 const struct mii_phy_funcs brgphy_fiber_funcs = {
103 	brgphy_service, brgphy_fiber_status, brgphy_reset,
104 };
105 
106 const struct mii_phy_funcs brgphy_5708s_funcs = {
107 	brgphy_service, brgphy_5708s_status, brgphy_reset,
108 };
109 
110 static const struct mii_phydesc brgphys[] = {
111 	{ MII_OUI_xxBROADCOM,		MII_MODEL_xxBROADCOM_BCM5400,
112 	  MII_STR_xxBROADCOM_BCM5400 },
113 	{ MII_OUI_xxBROADCOM,		MII_MODEL_xxBROADCOM_BCM5401,
114 	  MII_STR_xxBROADCOM_BCM5401 },
115 	{ MII_OUI_xxBROADCOM,		MII_MODEL_xxBROADCOM_BCM5411,
116 	  MII_STR_xxBROADCOM_BCM5411 },
117 	{ MII_OUI_xxBROADCOM,		MII_MODEL_xxBROADCOM_BCM5421,
118 	  MII_STR_xxBROADCOM_BCM5421 },
119 	{ MII_OUI_xxBROADCOM,		MII_MODEL_xxBROADCOM_BCM54K2,
120 	  MII_STR_xxBROADCOM_BCM54K2 },
121 	{ MII_OUI_xxBROADCOM,		MII_MODEL_xxBROADCOM_BCM5462,
122 	  MII_STR_xxBROADCOM_BCM5462 },
123 	{ MII_OUI_xxBROADCOM,		MII_MODEL_xxBROADCOM_BCM5701,
124 	  MII_STR_xxBROADCOM_BCM5701 },
125 	{ MII_OUI_xxBROADCOM,		MII_MODEL_xxBROADCOM_BCM5703,
126 	  MII_STR_xxBROADCOM_BCM5703 },
127 	{ MII_OUI_xxBROADCOM,		MII_MODEL_xxBROADCOM_BCM5704,
128 	  MII_STR_xxBROADCOM_BCM5704 },
129 	{ MII_OUI_xxBROADCOM,		MII_MODEL_xxBROADCOM_BCM5705,
130 	  MII_STR_xxBROADCOM_BCM5705 },
131 	{ MII_OUI_xxBROADCOM,		MII_MODEL_xxBROADCOM_BCM5714,
132 	  MII_STR_xxBROADCOM_BCM5714 },
133 	{ MII_OUI_xxBROADCOM,		MII_MODEL_xxBROADCOM_BCM5750,
134 	  MII_STR_xxBROADCOM_BCM5750 },
135 	{ MII_OUI_xxBROADCOM,		MII_MODEL_xxBROADCOM_BCM5752,
136 	  MII_STR_xxBROADCOM_BCM5752 },
137 	{ MII_OUI_xxBROADCOM,		MII_MODEL_xxBROADCOM_BCM5780,
138 	  MII_STR_xxBROADCOM_BCM5780 },
139 	{ MII_OUI_xxBROADCOM2,		MII_MODEL_xxBROADCOM2_BCM5722,
140 	  MII_STR_xxBROADCOM2_BCM5722 },
141 	{ MII_OUI_xxBROADCOM2,		MII_MODEL_xxBROADCOM2_BCM5755,
142 	  MII_STR_xxBROADCOM2_BCM5755 },
143 	{ MII_OUI_xxBROADCOM2,		MII_MODEL_xxBROADCOM2_BCM5787,
144 	  MII_STR_xxBROADCOM2_BCM5787 },
145 	{ MII_OUI_xxBROADCOM,		MII_MODEL_xxBROADCOM_BCM5706,
146 	  MII_STR_xxBROADCOM_BCM5706 },
147 	{ MII_OUI_xxBROADCOM,		MII_MODEL_xxBROADCOM_BCM5708C,
148 	  MII_STR_xxBROADCOM_BCM5708C },
149 	{ MII_OUI_xxBROADCOM2,		MII_MODEL_xxBROADCOM2_BCM5708S,
150 	  MII_STR_xxBROADCOM2_BCM5708S },
151 	{ MII_OUI_BROADCOM2,		MII_MODEL_BROADCOM2_BCM5906,
152 	  MII_STR_BROADCOM2_BCM5906 },
153 
154 	{ 0,				0,
155 	  NULL },
156 };
157 
158 int
159 brgphy_probe(struct device *parent, void *match, void *aux)
160 {
161 	struct mii_attach_args *ma = aux;
162 
163 	if (mii_phy_match(ma, brgphys) != NULL)
164 		return (10);
165 
166 	return (0);
167 }
168 
169 void
170 brgphy_attach(struct device *parent, struct device *self, void *aux)
171 {
172 	struct mii_softc *sc = (struct mii_softc *)self;
173 	struct bnx_softc *bnx_sc = NULL;
174 	struct mii_attach_args *ma = aux;
175 	struct mii_data *mii = ma->mii_data;
176 	const struct mii_phydesc *mpd;
177 	char *devname;
178 
179 	devname = sc->mii_dev.dv_parent->dv_cfdata->cf_driver->cd_name;
180 
181 	mpd = mii_phy_match(ma, brgphys);
182 	printf(": %s, rev. %d\n", mpd->mpd_name, MII_REV(ma->mii_id2));
183 
184 	sc->mii_inst = mii->mii_instance;
185 	sc->mii_phy = ma->mii_phyno;
186 	sc->mii_model = MII_MODEL(ma->mii_id2);
187 	sc->mii_rev = MII_REV(ma->mii_id2);
188 	sc->mii_pdata = mii;
189 	sc->mii_flags = ma->mii_flags;
190 	if (sc->mii_flags & MIIF_HAVEFIBER) {
191 		if (MII_OUI(ma->mii_id1, ma->mii_id2) ==
192 		    MII_OUI_xxBROADCOM &&
193 		    (sc->mii_model == MII_MODEL_xxBROADCOM_BCM5706 ||
194 		     sc->mii_model == MII_MODEL_xxBROADCOM_BCM5714 ||
195 		     sc->mii_model == MII_MODEL_xxBROADCOM_BCM5780))
196 			sc->mii_funcs = &brgphy_fiber_funcs;
197 		else if (MII_OUI(ma->mii_id1, ma->mii_id2) ==
198 		    MII_OUI_xxBROADCOM2 && sc->mii_model ==
199 		    MII_MODEL_xxBROADCOM2_BCM5708S)
200 			sc->mii_funcs = &brgphy_5708s_funcs;
201 	} else
202 		sc->mii_funcs = &brgphy_copper_funcs;
203 	sc->mii_anegticks = MII_ANEGTICKS;
204 
205 	sc->mii_flags |= MIIF_NOISOLATE | MIIF_NOLOOP;
206 
207 	if (strcmp(devname, "bnx") == 0)
208 		bnx_sc = sc->mii_pdata->mii_ifp->if_softc;
209 
210 	PHY_RESET(sc);
211 
212 	sc->mii_capabilities = PHY_READ(sc, MII_BMSR) & ma->mii_capmask;
213 	if (sc->mii_capabilities & BMSR_EXTSTAT)
214 		sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR);
215 
216 	mii_phy_add_media(sc);
217 
218 	if (sc->mii_flags & MIIF_HAVEFIBER && bnx_sc &&
219 	   (bnx_sc->bnx_phy_flags & BNX_PHY_2_5G_CAPABLE_FLAG)) {
220 		sc->mii_anegticks = MII_ANEGTICKS_GIGE;
221 		ifmedia_add(&mii->mii_media,
222 		    IFM_MAKEWORD(IFM_ETHER, IFM_2500_SX, IFM_FDX,
223 		    sc->mii_inst), 0, NULL);
224 	}
225 }
226 
227 int
228 brgphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
229 {
230 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
231 	int reg, speed = 0, gig;
232 
233 	if ((sc->mii_dev.dv_flags & DVF_ACTIVE) == 0)
234 		return (ENXIO);
235 
236 	switch (cmd) {
237 	case MII_POLLSTAT:
238 		/*
239 		 * If we're not polling our PHY instance, just return.
240 		 */
241 		if (IFM_INST(ife->ifm_media) != sc->mii_inst)
242 			return (0);
243 		break;
244 
245 	case MII_MEDIACHG:
246 		/*
247 		 * If the media indicates a different PHY instance,
248 		 * isolate ourselves.
249 		 */
250 		if (IFM_INST(ife->ifm_media) != sc->mii_inst) {
251 			reg = PHY_READ(sc, MII_BMCR);
252 			PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO);
253 			return (0);
254 		}
255 
256 		/*
257 		 * If the interface is not up, don't do anything.
258 		 */
259 		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
260 			break;
261 
262 		PHY_RESET(sc); /* XXX hardware bug work-around */
263 
264 		switch (IFM_SUBTYPE(ife->ifm_media)) {
265 		case IFM_AUTO:
266 			(void) brgphy_mii_phy_auto(sc);
267 			break;
268 		case IFM_2500_SX:
269 			speed = BRGPHY_5708S_BMCR_2500;
270 			goto setit;
271 		case IFM_1000_T:
272 			speed = BRGPHY_S1000;
273 			goto setit;
274 		case IFM_100_TX:
275 			speed = BRGPHY_S100;
276 			goto setit;
277 		case IFM_10_T:
278 			speed = BRGPHY_S10;
279 setit:
280 			brgphy_loop(sc);
281 			if ((ife->ifm_media & IFM_GMASK) == IFM_FDX) {
282 				speed |= BRGPHY_BMCR_FDX;
283 				gig = BRGPHY_1000CTL_AFD;
284 			} else {
285 				gig = BRGPHY_1000CTL_AHD;
286 			}
287 
288 			PHY_WRITE(sc, BRGPHY_MII_1000CTL, 0);
289 			PHY_WRITE(sc, BRGPHY_MII_BMCR, speed);
290 			PHY_WRITE(sc, BRGPHY_MII_ANAR, BRGPHY_SEL_TYPE);
291 
292 			if ((IFM_SUBTYPE(ife->ifm_media) != IFM_1000_T) &&
293 			    (IFM_SUBTYPE(ife->ifm_media) != IFM_1000_SX) &&
294 			    (IFM_SUBTYPE(ife->ifm_media) != IFM_2500_SX))
295 				break;
296 
297 			PHY_WRITE(sc, BRGPHY_MII_1000CTL, gig);
298 			PHY_WRITE(sc, BRGPHY_MII_BMCR,
299 			    speed|BRGPHY_BMCR_AUTOEN|BRGPHY_BMCR_STARTNEG);
300 
301 			if (sc->mii_model != MII_MODEL_xxBROADCOM_BCM5701)
302  				break;
303 
304 			if (mii->mii_media.ifm_media & IFM_ETH_MASTER)
305 				gig |= BRGPHY_1000CTL_MSE|BRGPHY_1000CTL_MSC;
306 			PHY_WRITE(sc, BRGPHY_MII_1000CTL, gig);
307 			break;
308 		default:
309 			return (EINVAL);
310 		}
311 		break;
312 
313 	case MII_TICK:
314 		/*
315 		 * If we're not currently selected, just return.
316 		 */
317 		if (IFM_INST(ife->ifm_media) != sc->mii_inst)
318 			return (0);
319 
320 		/*
321 		 * Is the interface even up?
322 		 */
323 		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
324 			return (0);
325 
326 		/*
327 		 * Only used for autonegotiation.
328 		 */
329 		if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO)
330 			break;
331 
332 		/*
333 		 * Check to see if we have link.  If we do, we don't
334 		 * need to restart the autonegotiation process.  Read
335 		 * the BMSR twice in case it's latched.
336 		 */
337 		reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
338 		if (reg & BMSR_LINK)
339 			break;
340 
341 		/*
342 		 * Only retry autonegotiation every mii_anegticks seconds.
343 		 */
344 		if (++sc->mii_ticks <= sc->mii_anegticks)
345 			break;
346 
347 		sc->mii_ticks = 0;
348 		brgphy_mii_phy_auto(sc);
349 		break;
350 	}
351 
352 	/* Update the media status. */
353 	mii_phy_status(sc);
354 
355 	/*
356 	 * Callback if something changed. Note that we need to poke the DSP on
357 	 * the Broadcom PHYs if the media changes.
358 	 */
359 	if (sc->mii_media_active != mii->mii_media_active ||
360 	    sc->mii_media_status != mii->mii_media_status ||
361 	    cmd == MII_MEDIACHG) {
362 		switch (sc->mii_model) {
363 		case MII_MODEL_BROADCOM_BCM5400:
364 			brgphy_bcm5401_dspcode(sc);
365 			break;
366 		case MII_MODEL_xxBROADCOM_BCM5401:
367 			if (sc->mii_rev == 1 || sc->mii_rev == 3)
368 				brgphy_bcm5401_dspcode(sc);
369 			break;
370 		case MII_MODEL_xxBROADCOM_BCM5411:
371 			brgphy_bcm5411_dspcode(sc);
372 			break;
373 		}
374 	}
375 
376 	/* Callback if something changed. */
377 	mii_phy_update(sc, cmd);
378 
379 	return (0);
380 }
381 
382 void
383 brgphy_copper_status(struct mii_softc *sc)
384 {
385 	struct mii_data *mii = sc->mii_pdata;
386 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
387 	int bmcr, bmsr;
388 
389 	mii->mii_media_status = IFM_AVALID;
390 	mii->mii_media_active = IFM_ETHER;
391 
392 	bmsr = PHY_READ(sc, BRGPHY_MII_BMSR) | PHY_READ(sc, BRGPHY_MII_BMSR);
393 	if (bmsr & BRGPHY_BMSR_LINK)
394 		mii->mii_media_status |= IFM_ACTIVE;
395 
396 	bmcr = PHY_READ(sc, BRGPHY_MII_BMCR);
397 	if (bmcr & BRGPHY_BMCR_LOOP)
398 		mii->mii_media_active |= IFM_LOOP;
399 
400 	if (bmcr & BRGPHY_BMCR_AUTOEN) {
401 		int auxsts;
402 
403 		if ((bmsr & BRGPHY_BMSR_ACOMP) == 0) {
404 			/* Erg, still trying, I guess... */
405 			mii->mii_media_active |= IFM_NONE;
406 			return;
407 		}
408 
409 		auxsts = PHY_READ(sc, BRGPHY_MII_AUXSTS);
410 
411 		switch (auxsts & BRGPHY_AUXSTS_AN_RES) {
412 		case BRGPHY_RES_1000FD:
413 			mii->mii_media_active |= IFM_1000_T | IFM_FDX;
414 			break;
415 		case BRGPHY_RES_1000HD:
416 			mii->mii_media_active |= IFM_1000_T | IFM_HDX;
417 			break;
418 		case BRGPHY_RES_100FD:
419 			mii->mii_media_active |= IFM_100_TX | IFM_FDX;
420 			break;
421 		case BRGPHY_RES_100T4:
422 			mii->mii_media_active |= IFM_100_T4 | IFM_HDX;
423 			break;
424 		case BRGPHY_RES_100HD:
425 			mii->mii_media_active |= IFM_100_TX | IFM_HDX;
426 			break;
427 		case BRGPHY_RES_10FD:
428 			mii->mii_media_active |= IFM_10_T | IFM_FDX;
429 			break;
430 		case BRGPHY_RES_10HD:
431 			mii->mii_media_active |= IFM_10_T | IFM_HDX;
432 			break;
433 		default:
434 			if (sc->mii_model == MII_MODEL_BROADCOM2_BCM5906) {
435 				mii->mii_media_active |= (auxsts &
436 				    BRGPHY_RES_100) ? IFM_100_TX : IFM_10_T;
437 				mii->mii_media_active |= (auxsts &
438 				    BRGPHY_RES_FULL) ? IFM_FDX : IFM_HDX;
439 				break;
440 			}
441 			mii->mii_media_active |= IFM_NONE;
442 			return;
443 		}
444 
445 		if (mii->mii_media_active & IFM_FDX)
446 			mii->mii_media_active |= mii_phy_flowstatus(sc);
447 
448 		if (IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T) {
449 			if (PHY_READ(sc, BRGPHY_MII_1000STS) &
450 			    BRGPHY_1000STS_MSR)
451 				mii->mii_media_active |= IFM_ETH_MASTER;
452 		}
453 	} else
454 		mii->mii_media_active = ife->ifm_media;
455 }
456 
457 void
458 brgphy_fiber_status(struct mii_softc *sc)
459 {
460 	struct mii_data *mii = sc->mii_pdata;
461 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
462 	int bmcr, bmsr;
463 
464 	mii->mii_media_status = IFM_AVALID;
465 	mii->mii_media_active = IFM_ETHER;
466 
467 	bmsr = PHY_READ(sc, BRGPHY_MII_BMSR) | PHY_READ(sc, BRGPHY_MII_BMSR);
468 	if (bmsr & BRGPHY_BMSR_LINK)
469 		mii->mii_media_status |= IFM_ACTIVE;
470 
471 	bmcr = PHY_READ(sc, BRGPHY_MII_BMCR);
472 	if (bmcr & BRGPHY_BMCR_LOOP)
473 		mii->mii_media_active |= IFM_LOOP;
474 
475 	if (bmcr & BRGPHY_BMCR_AUTOEN) {
476 		int val;
477 
478 		if ((bmsr & BRGPHY_BMSR_ACOMP) == 0) {
479 			/* Erg, still trying, I guess... */
480 			mii->mii_media_active |= IFM_NONE;
481 			return;
482 		}
483 
484 		mii->mii_media_active |= IFM_1000_SX;
485 
486 		val = PHY_READ(sc, BRGPHY_SERDES_ANAR) &
487 		      PHY_READ(sc, BRGPHY_SERDES_ANLPAR);
488 
489 		if (val & BRGPHY_SERDES_ANAR_FDX)
490 			mii->mii_media_active |= IFM_FDX;
491 		else
492 			mii->mii_media_active |= IFM_HDX;
493 
494 		if (mii->mii_media_active & IFM_FDX)
495 			mii->mii_media_active |= mii_phy_flowstatus(sc);
496 	} else
497 		mii->mii_media_active = ife->ifm_media;
498 }
499 
500 void
501 brgphy_5708s_status(struct mii_softc *sc)
502 {
503 	struct mii_data *mii = sc->mii_pdata;
504 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
505 	int bmcr, bmsr;
506 
507 	mii->mii_media_status = IFM_AVALID;
508 	mii->mii_media_active = IFM_ETHER;
509 
510 	bmsr = PHY_READ(sc, BRGPHY_MII_BMSR) | PHY_READ(sc, BRGPHY_MII_BMSR);
511 	if (bmsr & BRGPHY_BMSR_LINK)
512 		mii->mii_media_status |= IFM_ACTIVE;
513 
514 	bmcr = PHY_READ(sc, BRGPHY_MII_BMCR);
515 	if (bmcr & BRGPHY_BMCR_LOOP)
516 		mii->mii_media_active |= IFM_LOOP;
517 
518 	if (bmcr & BRGPHY_BMCR_AUTOEN) {
519 		int xstat;
520 
521 		if ((bmsr & BRGPHY_BMSR_ACOMP) == 0) {
522 			/* Erg, still trying, I guess... */
523 			mii->mii_media_active |= IFM_NONE;
524 			return;
525 		}
526 
527 		PHY_WRITE(sc, BRGPHY_5708S_BLOCK_ADDR,
528 		    BRGPHY_5708S_DIG_PG0);
529 
530 		xstat = PHY_READ(sc, BRGPHY_5708S_PG0_1000X_STAT1);
531 
532 		switch (xstat & BRGPHY_5708S_PG0_1000X_STAT1_SPEED_MASK) {
533 		case BRGPHY_5708S_PG0_1000X_STAT1_SPEED_10:
534 			mii->mii_media_active |= IFM_10_FL;
535 			break;
536 		case BRGPHY_5708S_PG0_1000X_STAT1_SPEED_100:
537 			mii->mii_media_active |= IFM_100_FX;
538 			break;
539 		case BRGPHY_5708S_PG0_1000X_STAT1_SPEED_1G:
540 			mii->mii_media_active |= IFM_1000_SX;
541 			break;
542 		case BRGPHY_5708S_PG0_1000X_STAT1_SPEED_25G:
543 			mii->mii_media_active |= IFM_2500_SX;
544 			break;
545 		}
546 
547 		if (xstat & BRGPHY_5708S_PG0_1000X_STAT1_FDX)
548 			mii->mii_media_active |= IFM_FDX;
549 		else
550 			mii->mii_media_active |= IFM_HDX;
551 	} else
552 		mii->mii_media_active = ife->ifm_media;
553 }
554 
555 int
556 brgphy_mii_phy_auto(struct mii_softc *sc)
557 {
558 	int anar, ktcr = 0;
559 
560 	PHY_RESET(sc);
561 
562 	if (sc->mii_flags & MIIF_HAVEFIBER) {
563 		anar = BRGPHY_SERDES_ANAR_FDX | BRGPHY_SERDES_ANAR_HDX;
564 		if (sc->mii_flags & MIIF_DOPAUSE)
565 			anar |= BRGPHY_SERDES_ANAR_BOTH_PAUSE;
566 		PHY_WRITE(sc, BRGPHY_SERDES_ANAR, anar);
567 	} else {
568 		anar = BMSR_MEDIA_TO_ANAR(sc->mii_capabilities) | ANAR_CSMA;
569 		if (sc->mii_flags & MIIF_DOPAUSE)
570 			anar |= BRGPHY_ANAR_ASP | BRGPHY_ANAR_PC;
571 		PHY_WRITE(sc, BRGPHY_MII_ANAR, anar);
572 	}
573 
574 	/* Enable speed in the 1000baseT control register */
575 	ktcr = BRGPHY_1000CTL_AFD | BRGPHY_1000CTL_AHD;
576 	if (sc->mii_model == MII_MODEL_xxBROADCOM_BCM5701)
577 		ktcr |= BRGPHY_1000CTL_MSE | BRGPHY_1000CTL_MSC;
578 	PHY_WRITE(sc, BRGPHY_MII_1000CTL, ktcr);
579 	ktcr = PHY_READ(sc, BRGPHY_MII_1000CTL);
580 
581 	/* Start autonegotiation */
582 	PHY_WRITE(sc, BRGPHY_MII_BMCR,
583 	    BRGPHY_BMCR_AUTOEN | BRGPHY_BMCR_STARTNEG);
584 	PHY_WRITE(sc, BRGPHY_MII_IMR, 0xFF00);
585 
586 	return (EJUSTRETURN);
587 }
588 
589 /* Enable loopback to force the link down. */
590 void
591 brgphy_loop(struct mii_softc *sc)
592 {
593 	u_int32_t bmsr;
594 	int i;
595 
596 	PHY_WRITE(sc, BRGPHY_MII_BMCR, BRGPHY_BMCR_LOOP);
597 	for (i = 0; i < 15000; i++) {
598 		bmsr = PHY_READ(sc, BRGPHY_MII_BMSR);
599 		if (!(bmsr & BRGPHY_BMSR_LINK))
600 			break;
601 		DELAY(10);
602 	}
603 }
604 
605 void
606 brgphy_reset(struct mii_softc *sc)
607 {
608 	struct bge_softc *bge_sc = NULL;
609 	struct bnx_softc *bnx_sc = NULL;
610 	char *devname;
611 
612 	devname = sc->mii_dev.dv_parent->dv_cfdata->cf_driver->cd_name;
613 
614 	mii_phy_reset(sc);
615 
616 	switch (sc->mii_model) {
617 	case MII_MODEL_BROADCOM_BCM5400:
618 		brgphy_bcm5401_dspcode(sc);
619 		break;
620 	case MII_MODEL_BROADCOM_BCM5401:
621 		if (sc->mii_rev == 1 || sc->mii_rev == 3)
622 			brgphy_bcm5401_dspcode(sc);
623 		break;
624 	case MII_MODEL_BROADCOM_BCM5411:
625 		brgphy_bcm5411_dspcode(sc);
626 		break;
627 	case MII_MODEL_xxBROADCOM_BCM5421:
628 		brgphy_bcm5421_dspcode(sc);
629 		break;
630 	case MII_MODEL_xxBROADCOM_BCM54K2:
631 		brgphy_bcm54k2_dspcode(sc);
632 		break;
633 	}
634 
635 	/* Handle any bge (NetXtreme/NetLink) workarounds. */
636 	if (strcmp(devname, "bge") == 0) {
637 		if (!(sc->mii_flags & MIIF_HAVEFIBER)) {
638 			bge_sc = sc->mii_pdata->mii_ifp->if_softc;
639 
640 			if (bge_sc->bge_flags & BGE_PHY_ADC_BUG)
641 				brgphy_adc_bug(sc);
642 			if (bge_sc->bge_flags & BGE_PHY_5704_A0_BUG)
643 				brgphy_5704_a0_bug(sc);
644 			if (bge_sc->bge_flags & BGE_PHY_BER_BUG)
645 				brgphy_ber_bug(sc);
646 			else if (bge_sc->bge_flags & BGE_PHY_JITTER_BUG) {
647 				PHY_WRITE(sc, BRGPHY_MII_AUXCTL, 0x0c00);
648 				PHY_WRITE(sc, BRGPHY_MII_DSP_ADDR_REG,
649 				    0x000a);
650 
651 				if (bge_sc->bge_flags & BGE_PHY_ADJUST_TRIM) {
652 					PHY_WRITE(sc, BRGPHY_MII_DSP_RW_PORT,
653 					    0x110b);
654 					PHY_WRITE(sc, BRGPHY_TEST1,
655 					    BRGPHY_TEST1_TRIM_EN | 0x4);
656 				} else {
657 					PHY_WRITE(sc, BRGPHY_MII_DSP_RW_PORT,
658 					    0x010b);
659 				}
660 
661 				PHY_WRITE(sc, BRGPHY_MII_AUXCTL, 0x0400);
662 			}
663 			if (bge_sc->bge_flags & BGE_PHY_CRC_BUG)
664 				brgphy_crc_bug(sc);
665 
666 			/* Set Jumbo frame settings in the PHY. */
667 			if (bge_sc->bge_flags & BGE_JUMBO_CAP)
668 				brgphy_jumbo_settings(sc);
669 
670 			/* Adjust output voltage */
671 			if (sc->mii_model == MII_MODEL_BROADCOM2_BCM5906)
672 				PHY_WRITE(sc, BRGPHY_MII_EPHY_PTEST, 0x12);
673 
674 			/* Enable Ethernet@Wirespeed */
675 			if (!(bge_sc->bge_flags & BGE_NO_ETH_WIRE_SPEED))
676 				brgphy_eth_wirespeed(sc);
677 
678 			/* Enable Link LED on Dell boxes */
679 			if (bge_sc->bge_flags & BGE_NO_3LED) {
680 				PHY_WRITE(sc, BRGPHY_MII_PHY_EXTCTL,
681 				PHY_READ(sc, BRGPHY_MII_PHY_EXTCTL)
682 					& ~BRGPHY_PHY_EXTCTL_3_LED);
683 			}
684 		}
685 	/* Handle any bnx (NetXtreme II) workarounds. */
686 	} else if (strcmp(devname, "bnx") == 0) {
687 		bnx_sc = sc->mii_pdata->mii_ifp->if_softc;
688 
689 		if (sc->mii_model == MII_MODEL_xxBROADCOM2_BCM5708S) {
690 			/* Store autoneg capabilities/results in digital block (Page 0) */
691 			PHY_WRITE(sc, BRGPHY_5708S_BLOCK_ADDR, BRGPHY_5708S_DIG3_PG2);
692 			PHY_WRITE(sc, BRGPHY_5708S_PG2_DIGCTL_3_0,
693 				BRGPHY_5708S_PG2_DIGCTL_3_0_USE_IEEE);
694 			PHY_WRITE(sc, BRGPHY_5708S_BLOCK_ADDR, BRGPHY_5708S_DIG_PG0);
695 
696 			/* Enable fiber mode and autodetection */
697 			PHY_WRITE(sc, BRGPHY_5708S_PG0_1000X_CTL1,
698 				PHY_READ(sc, BRGPHY_5708S_PG0_1000X_CTL1) |
699 				BRGPHY_5708S_PG0_1000X_CTL1_AUTODET_EN |
700 				BRGPHY_5708S_PG0_1000X_CTL1_FIBER_MODE);
701 
702 			/* Enable parallel detection */
703 			PHY_WRITE(sc, BRGPHY_5708S_PG0_1000X_CTL2,
704 				PHY_READ(sc, BRGPHY_5708S_PG0_1000X_CTL2) |
705 				BRGPHY_5708S_PG0_1000X_CTL2_PAR_DET_EN);
706 
707 			/* Advertise 2.5G support through next page during autoneg */
708 			if (bnx_sc->bnx_phy_flags & BNX_PHY_2_5G_CAPABLE_FLAG)
709 				PHY_WRITE(sc, BRGPHY_5708S_ANEG_NXT_PG_XMIT1,
710 					PHY_READ(sc, BRGPHY_5708S_ANEG_NXT_PG_XMIT1) |
711 					BRGPHY_5708S_ANEG_NXT_PG_XMIT1_25G);
712 
713 			/* Increase TX signal amplitude */
714 			if ((BNX_CHIP_ID(bnx_sc) == BNX_CHIP_ID_5708_A0) ||
715 			    (BNX_CHIP_ID(bnx_sc) == BNX_CHIP_ID_5708_B0) ||
716 			    (BNX_CHIP_ID(bnx_sc) == BNX_CHIP_ID_5708_B1)) {
717 				PHY_WRITE(sc, BRGPHY_5708S_BLOCK_ADDR,
718 					BRGPHY_5708S_TX_MISC_PG5);
719 				PHY_WRITE(sc, BRGPHY_5708S_PG5_TXACTL1,
720 					PHY_READ(sc, BRGPHY_5708S_PG5_TXACTL1) &
721 					~BRGPHY_5708S_PG5_TXACTL1_VCM);
722 				PHY_WRITE(sc, BRGPHY_5708S_BLOCK_ADDR,
723 					BRGPHY_5708S_DIG_PG0);
724 			}
725 
726 			/* Backplanes use special driver/pre-driver/pre-emphasis values. */
727 			if ((bnx_sc->bnx_shared_hw_cfg & BNX_SHARED_HW_CFG_PHY_BACKPLANE) &&
728 			    (bnx_sc->bnx_port_hw_cfg & BNX_PORT_HW_CFG_CFG_TXCTL3_MASK)) {
729 					PHY_WRITE(sc, BRGPHY_5708S_BLOCK_ADDR,
730 						BRGPHY_5708S_TX_MISC_PG5);
731 					PHY_WRITE(sc, BRGPHY_5708S_PG5_TXACTL3,
732 						bnx_sc->bnx_port_hw_cfg &
733 						BNX_PORT_HW_CFG_CFG_TXCTL3_MASK);
734 					PHY_WRITE(sc, BRGPHY_5708S_BLOCK_ADDR,
735 						BRGPHY_5708S_DIG_PG0);
736 			}
737 		} else {
738 			if (!(sc->mii_flags & MIIF_HAVEFIBER)) {
739 				brgphy_ber_bug(sc);
740 
741 				/* Set Jumbo frame settings in the PHY. */
742 				brgphy_jumbo_settings(sc);
743 
744 				/* Enable Ethernet@Wirespeed */
745 				brgphy_eth_wirespeed(sc);
746 			}
747 		}
748 	}
749 }
750 
751 /* Disable tap power management */
752 void
753 brgphy_bcm5401_dspcode(struct mii_softc *sc)
754 {
755 	static const struct {
756 		int		reg;
757 		uint16_t	val;
758 	} dspcode[] = {
759 		{ BRGPHY_MII_AUXCTL,		0x0c20 },
760 		{ BRGPHY_MII_DSP_ADDR_REG,	0x0012 },
761 		{ BRGPHY_MII_DSP_RW_PORT,	0x1804 },
762 		{ BRGPHY_MII_DSP_ADDR_REG,	0x0013 },
763 		{ BRGPHY_MII_DSP_RW_PORT,	0x1204 },
764 		{ BRGPHY_MII_DSP_ADDR_REG,	0x8006 },
765 		{ BRGPHY_MII_DSP_RW_PORT,	0x0132 },
766 		{ BRGPHY_MII_DSP_ADDR_REG,	0x8006 },
767 		{ BRGPHY_MII_DSP_RW_PORT,	0x0232 },
768 		{ BRGPHY_MII_DSP_ADDR_REG,	0x201f },
769 		{ BRGPHY_MII_DSP_RW_PORT,	0x0a20 },
770 		{ 0,				0 },
771 	};
772 	int i;
773 
774 	for (i = 0; dspcode[i].reg != 0; i++)
775 		PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
776 	DELAY(40);
777 }
778 
779 /* Setting some undocumented voltage */
780 void
781 brgphy_bcm5411_dspcode(struct mii_softc *sc)
782 {
783 	static const struct {
784 		int		reg;
785 		uint16_t	val;
786 	} dspcode[] = {
787 		{ 0x1c,				0x8c23 },
788 		{ 0x1c,				0x8ca3 },
789 		{ 0x1c,				0x8c23 },
790 		{ 0,				0 },
791 	};
792 	int i;
793 
794 	for (i = 0; dspcode[i].reg != 0; i++)
795 		PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
796 }
797 
798 void
799 brgphy_bcm5421_dspcode(struct mii_softc *sc)
800 {
801 	uint16_t data;
802 
803 	/* Set Class A mode */
804 	PHY_WRITE(sc, BRGPHY_MII_AUXCTL, 0x1007);
805 	data = PHY_READ(sc, BRGPHY_MII_AUXCTL);
806 	PHY_WRITE(sc, BRGPHY_MII_AUXCTL, data | 0x0400);
807 
808 	/* Set FFE gamma override to -0.125 */
809 	PHY_WRITE(sc, BRGPHY_MII_AUXCTL, 0x0007);
810 	data = PHY_READ(sc, BRGPHY_MII_AUXCTL);
811 	PHY_WRITE(sc, BRGPHY_MII_AUXCTL, data | 0x0800);
812 	PHY_WRITE(sc, BRGPHY_MII_DSP_ADDR_REG, 0x000a);
813 	data = PHY_READ(sc, BRGPHY_MII_DSP_RW_PORT);
814 	PHY_WRITE(sc, BRGPHY_MII_DSP_RW_PORT, data | 0x0200);
815 }
816 
817 void
818 brgphy_bcm54k2_dspcode(struct mii_softc *sc)
819 {
820 	static const struct {
821 		int		reg;
822 		uint16_t	val;
823 	} dspcode[] = {
824 		{ 4,				0x01e1 },
825 		{ 9,				0x0300 },
826 		{ 0,				0 },
827 	};
828 	int i;
829 
830 	for (i = 0; dspcode[i].reg != 0; i++)
831 		PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
832 }
833 
834 void
835 brgphy_adc_bug(struct mii_softc *sc)
836 {
837 	static const struct {
838 		int		reg;
839 		uint16_t	val;
840 	} dspcode[] = {
841 		{ BRGPHY_MII_AUXCTL,		0x0c00 },
842 		{ BRGPHY_MII_DSP_ADDR_REG,	0x201f },
843 		{ BRGPHY_MII_DSP_RW_PORT,	0x2aaa },
844 		{ BRGPHY_MII_DSP_ADDR_REG,	0x000a },
845 		{ BRGPHY_MII_DSP_RW_PORT,	0x0323 },
846 		{ BRGPHY_MII_AUXCTL,		0x0400 },
847 		{ 0,				0 },
848 	};
849 	int i;
850 
851 	for (i = 0; dspcode[i].reg != 0; i++)
852 		PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
853 }
854 
855 void
856 brgphy_5704_a0_bug(struct mii_softc *sc)
857 {
858 	static const struct {
859 		int		reg;
860 		uint16_t	val;
861 	} dspcode[] = {
862 		{ 0x1c,				0x8d68 },
863 		{ 0x1c,				0x8d68 },
864 		{ 0,				0 },
865 	};
866 	int i;
867 
868 	for (i = 0; dspcode[i].reg != 0; i++)
869 		PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
870 }
871 
872 void
873 brgphy_ber_bug(struct mii_softc *sc)
874 {
875 	static const struct {
876 		int		reg;
877 		uint16_t	val;
878 	} dspcode[] = {
879 		{ BRGPHY_MII_AUXCTL,		0x0c00 },
880 		{ BRGPHY_MII_DSP_ADDR_REG,	0x000a },
881 		{ BRGPHY_MII_DSP_RW_PORT,	0x310b },
882 		{ BRGPHY_MII_DSP_ADDR_REG,	0x201f },
883 		{ BRGPHY_MII_DSP_RW_PORT,	0x9506 },
884 		{ BRGPHY_MII_DSP_ADDR_REG,	0x401f },
885 		{ BRGPHY_MII_DSP_RW_PORT,	0x14e2 },
886 		{ BRGPHY_MII_AUXCTL,		0x0400 },
887 		{ 0,				0 },
888 	};
889 	int i;
890 
891 	for (i = 0; dspcode[i].reg != 0; i++)
892 		PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
893 }
894 
895 /* BCM5701 A0/B0 CRC bug workaround */
896 void
897 brgphy_crc_bug(struct mii_softc *sc)
898 {
899 	static const struct {
900 		int		reg;
901 		uint16_t	val;
902 	} dspcode[] = {
903 		{ BRGPHY_MII_DSP_ADDR_REG,	0x0a75 },
904 		{ 0x1c,				0x8c68 },
905 		{ 0x1c,				0x8d68 },
906 		{ 0x1c,				0x8c68 },
907 		{ 0,				0 },
908 	};
909 	int i;
910 
911 	for (i = 0; dspcode[i].reg != 0; i++)
912 		PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
913 }
914 
915 void
916 brgphy_jumbo_settings(struct mii_softc *sc)
917 {
918 	u_int32_t val;
919 
920 	/* Set Jumbo frame settings in the PHY. */
921 	if (sc->mii_model == MII_MODEL_BROADCOM_BCM5401) {
922 		/* Cannot do read-modify-write on the BCM5401 */
923 		PHY_WRITE(sc, BRGPHY_MII_AUXCTL, 0x4c20);
924 	} else {
925 		PHY_WRITE(sc, BRGPHY_MII_AUXCTL, 0x7);
926 		val = PHY_READ(sc, BRGPHY_MII_AUXCTL);
927 		PHY_WRITE(sc, BRGPHY_MII_AUXCTL,
928 			val & ~(BRGPHY_AUXCTL_LONG_PKT | 0x7));
929 	}
930 
931 	val = PHY_READ(sc, BRGPHY_MII_PHY_EXTCTL);
932 	PHY_WRITE(sc, BRGPHY_MII_PHY_EXTCTL,
933 		val & ~BRGPHY_PHY_EXTCTL_HIGH_LA);
934 }
935 
936 void
937 brgphy_eth_wirespeed(struct mii_softc *sc)
938 {
939 	u_int32_t val;
940 
941 	/* Enable Ethernet@Wirespeed */
942 	PHY_WRITE(sc, BRGPHY_MII_AUXCTL, 0x7007);
943 	val = PHY_READ(sc, BRGPHY_MII_AUXCTL);
944 	PHY_WRITE(sc, BRGPHY_MII_AUXCTL,
945 		(val | (1 << 15) | (1 << 4)));
946 }
947