xref: /netbsd-src/sys/dev/mii/brgphy.c (revision d710132b4b8ce7f7cccaaf660cb16aa16b4077a0)
1 /*	$NetBSD: brgphy.c,v 1.17 2003/04/29 01:49:33 thorpej Exp $	*/
2 
3 /*-
4  * Copyright (c) 1998, 1999, 2000, 2001 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
9  * NASA Ames Research Center.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. All advertising materials mentioning features or use of this software
20  *    must display the following acknowledgement:
21  *	This product includes software developed by the NetBSD
22  *	Foundation, Inc. and its contributors.
23  * 4. Neither the name of The NetBSD Foundation nor the names of its
24  *    contributors may be used to endorse or promote products derived
25  *    from this software without specific prior written permission.
26  *
27  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
28  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
31  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37  * POSSIBILITY OF SUCH DAMAGE.
38  */
39 
40 /*
41  * Copyright (c) 1997 Manuel Bouyer.  All rights reserved.
42  *
43  * Redistribution and use in source and binary forms, with or without
44  * modification, are permitted provided that the following conditions
45  * are met:
46  * 1. Redistributions of source code must retain the above copyright
47  *    notice, this list of conditions and the following disclaimer.
48  * 2. Redistributions in binary form must reproduce the above copyright
49  *    notice, this list of conditions and the following disclaimer in the
50  *    documentation and/or other materials provided with the distribution.
51  * 3. All advertising materials mentioning features or use of this software
52  *    must display the following acknowledgement:
53  *	This product includes software developed by Manuel Bouyer.
54  * 4. The name of the author may not be used to endorse or promote products
55  *    derived from this software without specific prior written permission.
56  *
57  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
58  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
59  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
60  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
61  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
62  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
63  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
64  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
65  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
66  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
67  */
68 
69 /*
70  * driver for the Broadcom BCM5400 Gig-E PHY.
71  *
72  * Programming information for this PHY was gleaned from FreeBSD
73  * (they were apparently able to get a datasheet from Broadcom).
74  */
75 
76 #include <sys/cdefs.h>
77 __KERNEL_RCSID(0, "$NetBSD: brgphy.c,v 1.17 2003/04/29 01:49:33 thorpej Exp $");
78 
79 #include <sys/param.h>
80 #include <sys/systm.h>
81 #include <sys/kernel.h>
82 #include <sys/device.h>
83 #include <sys/socket.h>
84 #include <sys/errno.h>
85 
86 #include <net/if.h>
87 #include <net/if_media.h>
88 
89 #include <dev/mii/mii.h>
90 #include <dev/mii/miivar.h>
91 #include <dev/mii/miidevs.h>
92 
93 #include <dev/mii/brgphyreg.h>
94 
95 int	brgphymatch(struct device *, struct cfdata *, void *);
96 void	brgphyattach(struct device *, struct device *, void *);
97 
98 CFATTACH_DECL(brgphy, sizeof(struct mii_softc),
99     brgphymatch, brgphyattach, mii_phy_detach, mii_phy_activate);
100 
101 int	brgphy_service(struct mii_softc *, struct mii_data *, int);
102 void	brgphy_status(struct mii_softc *);
103 
104 void	brgphy_5401_reset(struct mii_softc *);
105 void	brgphy_5411_reset(struct mii_softc *);
106 void	brgphy_5703_reset(struct mii_softc *);
107 void	brgphy_5704_reset(struct mii_softc *);
108 
109 const struct mii_phy_funcs brgphy_funcs = {
110 	brgphy_service, brgphy_status, mii_phy_reset,
111 };
112 
113 const struct mii_phy_funcs brgphy_5401_funcs = {
114 	brgphy_service, brgphy_status, brgphy_5401_reset,
115 };
116 
117 const struct mii_phy_funcs brgphy_5411_funcs = {
118 	brgphy_service, brgphy_status, brgphy_5411_reset,
119 };
120 
121 const struct mii_phy_funcs brgphy_5703_funcs = {
122 	brgphy_service, brgphy_status, brgphy_5703_reset,
123 };
124 
125 const struct mii_phy_funcs brgphy_5704_funcs = {
126 	brgphy_service, brgphy_status, brgphy_5704_reset,
127 };
128 
129 
130 const struct mii_phydesc brgphys[] = {
131 	{ MII_OUI_BROADCOM,		MII_MODEL_BROADCOM_BCM5400,
132 	  MII_STR_BROADCOM_BCM5400 },
133 
134 	{ MII_OUI_BROADCOM,		MII_MODEL_BROADCOM_BCM5401,
135 	  MII_STR_BROADCOM_BCM5401 },
136 
137 	{ MII_OUI_BROADCOM,		MII_MODEL_BROADCOM_BCM5411,
138 	  MII_STR_BROADCOM_BCM5411 },
139 
140 	{ MII_OUI_BROADCOM,		MII_MODEL_BROADCOM_BCM5421,
141 	  MII_STR_BROADCOM_BCM5421 },
142 
143 	{ MII_OUI_BROADCOM,		MII_MODEL_BROADCOM_BCM5701,
144 	  MII_STR_BROADCOM_BCM5701 },
145 
146 	{ MII_OUI_BROADCOM,		MII_MODEL_BROADCOM_BCM5703,
147 	  MII_STR_BROADCOM_BCM5703 },
148 
149 	{ MII_OUI_BROADCOM,		MII_MODEL_BROADCOM_BCM5704,
150 	  MII_STR_BROADCOM_BCM5704 },
151 
152 	{ 0,				0,
153 	  NULL },
154 };
155 
156 static void bcm5401_load_dspcode(struct mii_softc *);
157 static void bcm5411_load_dspcode(struct mii_softc *);
158 static void bcm5703_load_dspcode(struct mii_softc *);
159 static void bcm5704_load_dspcode(struct mii_softc *);
160 
161 int
162 brgphymatch(struct device *parent, struct cfdata *match, void *aux)
163 {
164 	struct mii_attach_args *ma = aux;
165 
166 	if (mii_phy_match(ma, brgphys) != NULL)
167 		return (10);
168 
169 	return (0);
170 }
171 
172 void
173 brgphyattach(struct device *parent, struct device *self, void *aux)
174 {
175 	struct mii_softc *sc = (struct mii_softc *)self;
176 	struct mii_attach_args *ma = aux;
177 	struct mii_data *mii = ma->mii_data;
178 	const struct mii_phydesc *mpd;
179 
180 	mpd = mii_phy_match(ma, brgphys);
181 	aprint_naive(": Media interface\n");
182 	aprint_normal(": %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_pdata = mii;
187 	sc->mii_flags = ma->mii_flags;
188 	sc->mii_anegticks = 5;
189 
190 	switch (MII_MODEL(ma->mii_id2)) {
191 	case MII_MODEL_BROADCOM_BCM5400:
192 		sc->mii_funcs = &brgphy_5401_funcs;
193 		aprint_normal("%s: using BCM5401 DSP patch\n",
194 		    sc->mii_dev.dv_xname);
195 		break;
196 
197 	case MII_MODEL_BROADCOM_BCM5401:
198 		if (MII_REV(ma->mii_id2) == 1 || MII_REV(ma->mii_id2) == 3) {
199 			sc->mii_funcs = &brgphy_5401_funcs;
200 			aprint_normal("%s: using BCM5401 DSP patch\n",
201 			    sc->mii_dev.dv_xname);
202 		} else
203 			sc->mii_funcs = &brgphy_funcs;
204 		break;
205 
206 	case MII_MODEL_BROADCOM_BCM5411:
207 		sc->mii_funcs = &brgphy_5411_funcs;
208 		aprint_normal("%s: using BCM5411 DSP patch\n",
209 		    sc->mii_dev.dv_xname);
210 		break;
211 
212 #ifdef notyet /* unverified, untested */
213 	case MII_MODEL_BROADCOM_BCM5703:
214 		sc->mii_funcs = &brgphy_5703_funcs;
215 		aprint_normal("%s: using BCM5703 DSP patch\n",
216 		    sc->mii_dev.dv_xname);
217 		break;
218 #endif
219 
220 	case MII_MODEL_BROADCOM_BCM5704:
221 		sc->mii_funcs = &brgphy_5704_funcs;
222 		aprint_normal("%s: using BCM5704 DSP patch\n",
223 		    sc->mii_dev.dv_xname);
224 		break;
225 
226 	default:
227 		sc->mii_funcs = &brgphy_funcs;
228 		break;
229 	}
230 
231 	PHY_RESET(sc);
232 
233 	sc->mii_capabilities =
234 	    PHY_READ(sc, MII_BMSR) & ma->mii_capmask;
235 	if (sc->mii_capabilities & BMSR_EXTSTAT)
236 		sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR);
237 
238 	aprint_normal("%s: ", sc->mii_dev.dv_xname);
239 	if ((sc->mii_capabilities & BMSR_MEDIAMASK) == 0 &&
240 	    (sc->mii_extcapabilities & EXTSR_MEDIAMASK) == 0)
241 		aprint_error("no media present");
242 	else
243 		mii_phy_add_media(sc);
244 	aprint_normal("\n");
245 }
246 
247 int
248 brgphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
249 {
250 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
251 	int reg;
252 
253 	switch (cmd) {
254 	case MII_POLLSTAT:
255 		/*
256 		 * If we're not polling our PHY instance, just return.
257 		 */
258 		if (IFM_INST(ife->ifm_media) != sc->mii_inst)
259 			return (0);
260 		break;
261 
262 	case MII_MEDIACHG:
263 		/*
264 		 * If the media indicates a different PHY instance,
265 		 * isolate ourselves.
266 		 */
267 		if (IFM_INST(ife->ifm_media) != sc->mii_inst) {
268 			reg = PHY_READ(sc, MII_BMCR);
269 			PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO);
270 			return (0);
271 		}
272 
273 		/*
274 		 * If the interface is not up, don't do anything.
275 		 */
276 		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
277 			break;
278 
279 		mii_phy_reset(sc);	/* XXX hardware bug work-around */
280 		mii_phy_setmedia(sc);
281 		break;
282 
283 	case MII_TICK:
284 		/*
285 		 * If we're not currently selected, just return.
286 		 */
287 		if (IFM_INST(ife->ifm_media) != sc->mii_inst)
288 			return (0);
289 
290 		if (mii_phy_tick(sc) == EJUSTRETURN)
291 			return (0);
292 		break;
293 
294 	case MII_DOWN:
295 		mii_phy_down(sc);
296 		return (0);
297 	}
298 
299 	/* Update the media status. */
300 	mii_phy_status(sc);
301 
302 	/*
303 	 * Callback if something changed.  Note that we need to poke
304 	 * the DSP on the Broadcom PHYs if the media changes.
305 	 */
306 	if (sc->mii_media_active != mii->mii_media_active ||
307 	    sc->mii_media_status != mii->mii_media_status ||
308 	    cmd == MII_MEDIACHG) {
309 		mii_phy_update(sc, cmd);
310 		if (sc->mii_funcs == &brgphy_5401_funcs)
311 			bcm5401_load_dspcode(sc);
312 		else if (sc->mii_funcs == &brgphy_5411_funcs)
313 			bcm5411_load_dspcode(sc);
314 	}
315 	return (0);
316 }
317 
318 void
319 brgphy_status(struct mii_softc *sc)
320 {
321 	struct mii_data *mii = sc->mii_pdata;
322 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
323 	int bmcr, auxsts, gtsr;
324 
325 	mii->mii_media_status = IFM_AVALID;
326 	mii->mii_media_active = IFM_ETHER;
327 
328 	auxsts = PHY_READ(sc, BRGPHY_MII_AUXSTS);
329 
330 	if (auxsts & BRGPHY_AUXSTS_LINK)
331 		mii->mii_media_status |= IFM_ACTIVE;
332 
333 	bmcr = PHY_READ(sc, MII_BMCR);
334 	if (bmcr & BMCR_ISO) {
335 		mii->mii_media_active |= IFM_NONE;
336 		mii->mii_media_status = 0;
337 		return;
338 	}
339 
340 	if (bmcr & BMCR_LOOP)
341 		mii->mii_media_active |= IFM_LOOP;
342 
343 	if (bmcr & BMCR_AUTOEN) {
344 		/*
345 		 * The media status bits are only valid of autonegotiation
346 		 * has completed (or it's disabled).
347 		 */
348 		if ((auxsts & BRGPHY_AUXSTS_ACOMP) == 0) {
349 			/* Erg, still trying, I guess... */
350 			mii->mii_media_active |= IFM_NONE;
351 			return;
352 		}
353 
354 		switch (auxsts & BRGPHY_AUXSTS_AN_RES) {
355 		case BRGPHY_RES_1000FD:
356 			mii->mii_media_active |= IFM_1000_T|IFM_FDX;
357 			gtsr = PHY_READ(sc, MII_100T2SR);
358 			if (gtsr & GTSR_MS_RES)
359 				mii->mii_media_active |= IFM_ETH_MASTER;
360 			break;
361 
362 		case BRGPHY_RES_1000HD:
363 			mii->mii_media_active |= IFM_1000_T;
364 			gtsr = PHY_READ(sc, MII_100T2SR);
365 			if (gtsr & GTSR_MS_RES)
366 				mii->mii_media_active |= IFM_ETH_MASTER;
367 			break;
368 
369 		case BRGPHY_RES_100FD:
370 			mii->mii_media_active |= IFM_100_TX|IFM_FDX;
371 			break;
372 
373 		case BRGPHY_RES_100T4:
374 			mii->mii_media_active |= IFM_100_T4;
375 			break;
376 
377 		case BRGPHY_RES_100HD:
378 			mii->mii_media_active |= IFM_100_TX;
379 			break;
380 
381 		case BRGPHY_RES_10FD:
382 			mii->mii_media_active |= IFM_10_T|IFM_FDX;
383 			break;
384 
385 		case BRGPHY_RES_10HD:
386 			mii->mii_media_active |= IFM_10_T;
387 			break;
388 
389 		default:
390 			mii->mii_media_active |= IFM_NONE;
391 			mii->mii_media_status = 0;
392 		}
393 	} else
394 		mii->mii_media_active = ife->ifm_media;
395 }
396 
397 void
398 brgphy_5401_reset(struct mii_softc *sc)
399 {
400 
401 	mii_phy_reset(sc);
402 	bcm5401_load_dspcode(sc);
403 }
404 
405 void
406 brgphy_5411_reset(struct mii_softc *sc)
407 {
408 
409 	mii_phy_reset(sc);
410 	bcm5411_load_dspcode(sc);
411 }
412 
413 
414 void
415 brgphy_5703_reset(struct mii_softc *sc)
416 {
417 
418 	mii_phy_reset(sc);
419 	bcm5703_load_dspcode(sc);
420 }
421 
422 void
423 brgphy_5704_reset(struct mii_softc *sc)
424 {
425 
426 	mii_phy_reset(sc);
427 	bcm5704_load_dspcode(sc);
428 }
429 
430 /* Turn off tap power management on 5401. */
431 static void
432 bcm5401_load_dspcode(struct mii_softc *sc)
433 {
434 	static const struct {
435 		int		reg;
436 		uint16_t	val;
437 	} dspcode[] = {
438 		{ BRGPHY_MII_AUXCTL,		0x0c20 },
439 		{ BRGPHY_MII_DSP_ADDR_REG,	0x0012 },
440 		{ BRGPHY_MII_DSP_RW_PORT,	0x1804 },
441 		{ BRGPHY_MII_DSP_ADDR_REG,	0x0013 },
442 		{ BRGPHY_MII_DSP_RW_PORT,	0x1204 },
443 		{ BRGPHY_MII_DSP_ADDR_REG,	0x8006 },
444 		{ BRGPHY_MII_DSP_RW_PORT,	0x0132 },
445 		{ BRGPHY_MII_DSP_ADDR_REG,	0x8006 },
446 		{ BRGPHY_MII_DSP_RW_PORT,	0x0232 },
447 		{ BRGPHY_MII_DSP_ADDR_REG,	0x201f },
448 		{ BRGPHY_MII_DSP_RW_PORT,	0x0a20 },
449 		{ 0,				0 },
450 	};
451 	int i;
452 
453 	for (i = 0; dspcode[i].reg != 0; i++)
454 		PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
455     delay(40);
456 }
457 
458 static void
459 bcm5411_load_dspcode(struct mii_softc *sc)
460 {
461 	static const struct {
462 		int		reg;
463 		uint16_t	val;
464 	} dspcode[] = {
465 		{ 0x1c,				0x8c23 },
466 		{ 0x1c,				0x8ca3 },
467 		{ 0x1c,				0x8c23 },
468 		{ 0,				0 },
469 	};
470 	int i;
471 
472 	for (i = 0; dspcode[i].reg != 0; i++)
473 		PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
474 }
475 
476 static void
477 bcm5703_load_dspcode(struct mii_softc *sc)
478 {
479 	static const struct {
480 		int		reg;
481 		uint16_t	val;
482 	} dspcode[] = {
483 		{ BRGPHY_MII_AUXCTL,		0x0c00 },
484 		{ BRGPHY_MII_DSP_ADDR_REG,	0x201f },
485 		{ BRGPHY_MII_DSP_RW_PORT,	0x2aaa },
486 		{ 0,				0 },
487 	};
488 	int i;
489 
490 	for (i = 0; dspcode[i].reg != 0; i++)
491 		PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
492 }
493 
494 static void
495 bcm5704_load_dspcode(struct mii_softc *sc)
496 {
497 	static const struct {
498 		int		reg;
499 		uint16_t	val;
500 	} dspcode[] = {
501 		{ 0x1c,				0x8d68 },
502    		{ 0x1c,				0x8d68 },
503 		{ 0,				0 },
504 	};
505 	int i;
506 
507 	for (i = 0; dspcode[i].reg != 0; i++)
508 		PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
509 }
510