xref: /netbsd/sys/dev/mii/brgphy.c (revision c4a72b64)
1 /*	$NetBSD: brgphy.c,v 1.13 2002/10/02 16:34:15 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.13 2002/10/02 16:34:15 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 
107 const struct mii_phy_funcs brgphy_funcs = {
108 	brgphy_service, brgphy_status, mii_phy_reset,
109 };
110 
111 const struct mii_phy_funcs brgphy_5401_funcs = {
112 	brgphy_service, brgphy_status, brgphy_5401_reset,
113 };
114 
115 const struct mii_phy_funcs brgphy_5411_funcs = {
116 	brgphy_service, brgphy_status, brgphy_5411_reset,
117 };
118 
119 const struct mii_phydesc brgphys[] = {
120 	{ MII_OUI_BROADCOM,		MII_MODEL_BROADCOM_BCM5400,
121 	  MII_STR_BROADCOM_BCM5400 },
122 
123 	{ MII_OUI_BROADCOM,		MII_MODEL_BROADCOM_BCM5401,
124 	  MII_STR_BROADCOM_BCM5401 },
125 
126 	{ MII_OUI_BROADCOM,		MII_MODEL_BROADCOM_BCM5411,
127 	  MII_STR_BROADCOM_BCM5411 },
128 
129 	{ MII_OUI_BROADCOM,		MII_MODEL_BROADCOM_BCM5421,
130 	  MII_STR_BROADCOM_BCM5421 },
131 
132 	{ MII_OUI_BROADCOM,		MII_MODEL_BROADCOM_BCM5701,
133 	  MII_STR_BROADCOM_BCM5701 },
134 
135 	{ 0,				0,
136 	  NULL },
137 };
138 
139 static void bcm5401_load_dspcode(struct mii_softc *);
140 static void bcm5411_load_dspcode(struct mii_softc *);
141 
142 int
143 brgphymatch(struct device *parent, struct cfdata *match, void *aux)
144 {
145 	struct mii_attach_args *ma = aux;
146 
147 	if (mii_phy_match(ma, brgphys) != NULL)
148 		return (10);
149 
150 	return (0);
151 }
152 
153 void
154 brgphyattach(struct device *parent, struct device *self, void *aux)
155 {
156 	struct mii_softc *sc = (struct mii_softc *)self;
157 	struct mii_attach_args *ma = aux;
158 	struct mii_data *mii = ma->mii_data;
159 	const struct mii_phydesc *mpd;
160 
161 	mpd = mii_phy_match(ma, brgphys);
162 	printf(": %s, rev. %d\n", mpd->mpd_name, MII_REV(ma->mii_id2));
163 
164 	sc->mii_inst = mii->mii_instance;
165 	sc->mii_phy = ma->mii_phyno;
166 	sc->mii_pdata = mii;
167 	sc->mii_flags = ma->mii_flags;
168 	sc->mii_anegticks = 5;
169 
170 	switch (MII_MODEL(ma->mii_id2)) {
171 	case MII_MODEL_BROADCOM_BCM5400:
172 		sc->mii_funcs = &brgphy_5401_funcs;
173 		printf("%s: using BCM5401 DSP patch\n", sc->mii_dev.dv_xname);
174 		break;
175 
176 	case MII_MODEL_BROADCOM_BCM5401:
177 		if (MII_REV(ma->mii_id2) == 1 || MII_REV(ma->mii_id2) == 3) {
178 			sc->mii_funcs = &brgphy_5401_funcs;
179 			printf("%s: using BCM5401 DSP patch\n",
180 			    sc->mii_dev.dv_xname);
181 		} else
182 			sc->mii_funcs = &brgphy_funcs;
183 		break;
184 
185 	case MII_MODEL_BROADCOM_BCM5411:
186 		sc->mii_funcs = &brgphy_5411_funcs;
187 		printf("%s: using BCM5411 DSP patch\n", sc->mii_dev.dv_xname);
188 		break;
189 
190 	default:
191 		sc->mii_funcs = &brgphy_funcs;
192 		break;
193 	}
194 
195 	PHY_RESET(sc);
196 
197 	sc->mii_capabilities =
198 	    PHY_READ(sc, MII_BMSR) & ma->mii_capmask;
199 	if (sc->mii_capabilities & BMSR_EXTSTAT)
200 		sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR);
201 
202 	printf("%s: ", sc->mii_dev.dv_xname);
203 	if ((sc->mii_capabilities & BMSR_MEDIAMASK) == 0 &&
204 	    (sc->mii_extcapabilities & EXTSR_MEDIAMASK) == 0)
205 		printf("no media present");
206 	else
207 		mii_phy_add_media(sc);
208 	printf("\n");
209 }
210 
211 int
212 brgphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
213 {
214 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
215 	int reg;
216 
217 	switch (cmd) {
218 	case MII_POLLSTAT:
219 		/*
220 		 * If we're not polling our PHY instance, just return.
221 		 */
222 		if (IFM_INST(ife->ifm_media) != sc->mii_inst)
223 			return (0);
224 		break;
225 
226 	case MII_MEDIACHG:
227 		/*
228 		 * If the media indicates a different PHY instance,
229 		 * isolate ourselves.
230 		 */
231 		if (IFM_INST(ife->ifm_media) != sc->mii_inst) {
232 			reg = PHY_READ(sc, MII_BMCR);
233 			PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO);
234 			return (0);
235 		}
236 
237 		/*
238 		 * If the interface is not up, don't do anything.
239 		 */
240 		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
241 			break;
242 
243 		mii_phy_reset(sc);	/* XXX hardware bug work-around */
244 		mii_phy_setmedia(sc);
245 		break;
246 
247 	case MII_TICK:
248 		/*
249 		 * If we're not currently selected, just return.
250 		 */
251 		if (IFM_INST(ife->ifm_media) != sc->mii_inst)
252 			return (0);
253 
254 		if (mii_phy_tick(sc) == EJUSTRETURN)
255 			return (0);
256 		break;
257 
258 	case MII_DOWN:
259 		mii_phy_down(sc);
260 		return (0);
261 	}
262 
263 	/* Update the media status. */
264 	mii_phy_status(sc);
265 
266 	/*
267 	 * Callback if something changed.  Note that we need to poke
268 	 * the DSP on the Broadcom PHYs if the media changes.
269 	 */
270 	if (sc->mii_media_active != mii->mii_media_active ||
271 	    sc->mii_media_status != mii->mii_media_status ||
272 	    cmd == MII_MEDIACHG) {
273 		mii_phy_update(sc, cmd);
274 		if (sc->mii_funcs == &brgphy_5401_funcs)
275 			bcm5401_load_dspcode(sc);
276 		else if (sc->mii_funcs == &brgphy_5411_funcs)
277 			bcm5411_load_dspcode(sc);
278 	}
279 	return (0);
280 }
281 
282 void
283 brgphy_status(struct mii_softc *sc)
284 {
285 	struct mii_data *mii = sc->mii_pdata;
286 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
287 	int bmcr, auxsts, gtsr;
288 
289 	mii->mii_media_status = IFM_AVALID;
290 	mii->mii_media_active = IFM_ETHER;
291 
292 	auxsts = PHY_READ(sc, BRGPHY_MII_AUXSTS);
293 
294 	if (auxsts & BRGPHY_AUXSTS_LINK)
295 		mii->mii_media_status |= IFM_ACTIVE;
296 
297 	bmcr = PHY_READ(sc, MII_BMCR);
298 	if (bmcr & BMCR_ISO) {
299 		mii->mii_media_active |= IFM_NONE;
300 		mii->mii_media_status = 0;
301 		return;
302 	}
303 
304 	if (bmcr & BMCR_LOOP)
305 		mii->mii_media_active |= IFM_LOOP;
306 
307 	if (bmcr & BMCR_AUTOEN) {
308 		/*
309 		 * The media status bits are only valid of autonegotiation
310 		 * has completed (or it's disabled).
311 		 */
312 		if ((auxsts & BRGPHY_AUXSTS_ACOMP) == 0) {
313 			/* Erg, still trying, I guess... */
314 			mii->mii_media_active |= IFM_NONE;
315 			return;
316 		}
317 
318 		switch (auxsts & BRGPHY_AUXSTS_AN_RES) {
319 		case BRGPHY_RES_1000FD:
320 			mii->mii_media_active |= IFM_1000_T|IFM_FDX;
321 			gtsr = PHY_READ(sc, MII_100T2SR);
322 			if (gtsr & GTSR_MS_RES)
323 				mii->mii_media_active |= IFM_ETH_MASTER;
324 			break;
325 
326 		case BRGPHY_RES_1000HD:
327 			mii->mii_media_active |= IFM_1000_T;
328 			gtsr = PHY_READ(sc, MII_100T2SR);
329 			if (gtsr & GTSR_MS_RES)
330 				mii->mii_media_active |= IFM_ETH_MASTER;
331 			break;
332 
333 		case BRGPHY_RES_100FD:
334 			mii->mii_media_active |= IFM_100_TX|IFM_FDX;
335 			break;
336 
337 		case BRGPHY_RES_100T4:
338 			mii->mii_media_active |= IFM_100_T4;
339 			break;
340 
341 		case BRGPHY_RES_100HD:
342 			mii->mii_media_active |= IFM_100_TX;
343 			break;
344 
345 		case BRGPHY_RES_10FD:
346 			mii->mii_media_active |= IFM_10_T|IFM_FDX;
347 			break;
348 
349 		case BRGPHY_RES_10HD:
350 			mii->mii_media_active |= IFM_10_T;
351 			break;
352 
353 		default:
354 			mii->mii_media_active |= IFM_NONE;
355 			mii->mii_media_status = 0;
356 		}
357 	} else
358 		mii->mii_media_active = ife->ifm_media;
359 }
360 
361 void
362 brgphy_5401_reset(struct mii_softc *sc)
363 {
364 
365 	mii_phy_reset(sc);
366 	bcm5401_load_dspcode(sc);
367 }
368 
369 void
370 brgphy_5411_reset(struct mii_softc *sc)
371 {
372 
373 	mii_phy_reset(sc);
374 	bcm5411_load_dspcode(sc);
375 }
376 
377 static void
378 bcm5401_load_dspcode(struct mii_softc *sc)
379 {
380 	static const struct {
381 		int		reg;
382 		uint16_t	val;
383 	} dspcode[] = {
384 		{ BRGPHY_MII_AUXCTL,		0x4c20 },
385 		{ BRGPHY_MII_DSP_ADDR_REG,	0x0012 },
386 		{ BRGPHY_MII_DSP_RW_PORT,	0x1804 },
387 		{ BRGPHY_MII_DSP_ADDR_REG,	0x0013 },
388 		{ BRGPHY_MII_DSP_RW_PORT,	0x1204 },
389 		{ BRGPHY_MII_DSP_ADDR_REG,	0x8006 },
390 		{ BRGPHY_MII_DSP_RW_PORT,	0x0132 },
391 		{ BRGPHY_MII_DSP_ADDR_REG,	0x8006 },
392 		{ BRGPHY_MII_DSP_RW_PORT,	0x0232 },
393 		{ BRGPHY_MII_DSP_ADDR_REG,	0x201f },
394 		{ BRGPHY_MII_DSP_RW_PORT,	0x0a20 },
395 		{ 0,				0 },
396 	};
397 	int i;
398 
399 	for (i = 0; dspcode[i].reg != 0; i++)
400 		PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
401 }
402 
403 static void
404 bcm5411_load_dspcode(struct mii_softc *sc)
405 {
406 	static const struct {
407 		int		reg;
408 		uint16_t	val;
409 	} dspcode[] = {
410 		{ 0x1c,				0x8c23 },
411 		{ 0x1c,				0x8ca3 },
412 		{ 0x1c,				0x8c23 },
413 		{ 0,				0 },
414 	};
415 	int i;
416 
417 	for (i = 0; dspcode[i].reg != 0; i++)
418 		PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
419 }
420