xref: /netbsd-src/sys/dev/mii/igphy.c (revision 23c8222edbfb0f0932d88a8351d3a0cf817dfb9e)
1 /*	$NetBSD: igphy.c,v 1.5 2004/10/05 20:20:00 thorpej Exp $	*/
2 
3 /*
4  * The Intel copyright applies to the analog register setup, and the
5  * (currently disabled) SmartSpeed workaround code.
6  */
7 
8 /*******************************************************************************
9 
10   Copyright (c) 2001-2003, Intel Corporation
11   All rights reserved.
12 
13   Redistribution and use in source and binary forms, with or without
14   modification, are permitted provided that the following conditions are met:
15 
16    1. Redistributions of source code must retain the above copyright notice,
17       this list of conditions and the following disclaimer.
18 
19    2. Redistributions in binary form must reproduce the above copyright
20       notice, this list of conditions and the following disclaimer in the
21       documentation and/or other materials provided with the distribution.
22 
23    3. Neither the name of the Intel Corporation nor the names of its
24       contributors may be used to endorse or promote products derived from
25       this software without specific prior written permission.
26 
27   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
28   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30   ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
31   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 
42 /*-
43  * Copyright (c) 1998, 1999, 2000, 2003 The NetBSD Foundation, Inc.
44  * All rights reserved.
45  *
46  * This code is derived from software contributed to The NetBSD Foundation
47  * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
48  * NASA Ames Research Center, and by Frank van der Linden.
49  *
50  * Redistribution and use in source and binary forms, with or without
51  * modification, are permitted provided that the following conditions
52  * are met:
53  * 1. Redistributions of source code must retain the above copyright
54  *    notice, this list of conditions and the following disclaimer.
55  * 2. Redistributions in binary form must reproduce the above copyright
56  *    notice, this list of conditions and the following disclaimer in the
57  *    documentation and/or other materials provided with the distribution.
58  * 3. All advertising materials mentioning features or use of this software
59  *    must display the following acknowledgement:
60  *	This product includes software developed by the NetBSD
61  *	Foundation, Inc. and its contributors.
62  * 4. Neither the name of The NetBSD Foundation nor the names of its
63  *    contributors may be used to endorse or promote products derived
64  *    from this software without specific prior written permission.
65  *
66  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
67  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
68  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
69  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
70  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
71  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
72  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
73  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
74  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
75  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
76  * POSSIBILITY OF SUCH DAMAGE.
77  */
78 
79 #include <sys/cdefs.h>
80 __KERNEL_RCSID(0, "$NetBSD: igphy.c,v 1.5 2004/10/05 20:20:00 thorpej Exp $");
81 
82 #include "opt_mii.h"
83 
84 #include <sys/param.h>
85 #include <sys/systm.h>
86 #include <sys/kernel.h>
87 #include <sys/device.h>
88 #include <sys/socket.h>
89 #include <sys/errno.h>
90 
91 #include <net/if.h>
92 #include <net/if_media.h>
93 
94 #include <dev/mii/mii.h>
95 #include <dev/mii/miivar.h>
96 #include <dev/mii/miidevs.h>
97 
98 #include <dev/mii/igphyreg.h>
99 
100 struct igphy_softc {
101 	struct mii_softc sc_mii;
102 	int sc_smartspeed;
103 };
104 
105 static void igphy_reset(struct mii_softc *);
106 static void igphy_load_dspcode(struct mii_softc *);
107 static void igphy_smartspeed_workaround(struct mii_softc *sc);
108 
109 static int	igphymatch(struct device *, struct cfdata *, void *);
110 static void	igphyattach(struct device *, struct device *, void *);
111 
112 CFATTACH_DECL(igphy, sizeof(struct igphy_softc),
113     igphymatch, igphyattach, mii_phy_detach, mii_phy_activate);
114 
115 static int	igphy_service(struct mii_softc *, struct mii_data *, int);
116 static void	igphy_status(struct mii_softc *);
117 
118 static const struct mii_phy_funcs igphy_funcs = {
119 	igphy_service, igphy_status, igphy_reset,
120 };
121 
122 static const struct mii_phydesc igphys[] = {
123 	{ MII_OUI_yyINTEL,		MII_MODEL_yyINTEL_IGP01E1000,
124 	  MII_STR_yyINTEL_IGP01E1000 },
125 
126 	{0,				0,
127 	 NULL },
128 };
129 
130 static int
131 igphymatch(struct device *parent, struct cfdata *match, void *aux)
132 {
133 	struct mii_attach_args *ma = aux;
134 
135 	if (mii_phy_match(ma, igphys) != NULL)
136 		return 10;
137 
138 	return 0;
139 }
140 
141 static void
142 igphyattach(struct device *parent, struct device *self, void *aux)
143 {
144 	struct mii_softc *sc = (struct mii_softc *)self;
145 	struct mii_attach_args *ma = aux;
146 	struct mii_data *mii = ma->mii_data;
147 	const struct mii_phydesc *mpd;
148 
149 	mpd = mii_phy_match(ma, igphys);
150 	aprint_naive(": Media interface\n");
151 	aprint_normal(": %s, rev. %d\n", mpd->mpd_name, MII_REV(ma->mii_id2));
152 
153 	sc->mii_inst = mii->mii_instance;
154 	sc->mii_phy = ma->mii_phyno;
155 	sc->mii_funcs = &igphy_funcs;
156 	sc->mii_pdata = mii;
157 	sc->mii_flags = ma->mii_flags;
158 	sc->mii_anegticks = 10;
159 
160 	PHY_RESET(sc);
161 
162 	sc->mii_capabilities =
163 	    PHY_READ(sc, MII_BMSR) & ma->mii_capmask;
164 	if (sc->mii_capabilities & BMSR_EXTSTAT)
165 		sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR);
166 	aprint_normal("%s: ", sc->mii_dev.dv_xname);
167 	if ((sc->mii_capabilities & BMSR_MEDIAMASK) == 0 &&
168 	    (sc->mii_extcapabilities & EXTSR_MEDIAMASK) == 0)
169 		aprint_error("no media present");
170 	else
171 		mii_phy_add_media(sc);
172 	aprint_normal("\n");
173 }
174 
175 static void
176 igphy_load_dspcode(struct mii_softc *sc)
177 {
178 	static const struct {
179 		int reg;
180 		uint16_t val;
181 	} dspcode[] = {
182 		{ 0x1f95, 0x0001 },
183 		{ 0x1f71, 0xbd21 },
184 		{ 0x1f79, 0x0018 },
185 		{ 0x1f30, 0x1600 },
186 		{ 0x1f31, 0x0014 },
187 		{ 0x1f32, 0x161c },
188 		{ 0x1f94, 0x0003 },
189 		{ 0x1f96, 0x003f },
190 		{ 0x2010, 0x0008 },
191 		{ 0, 0 },
192 	};
193 	int i;
194 
195 	delay(10);
196 
197 	PHY_WRITE(sc, MII_IGPHY_PAGE_SELECT, 0x0000);
198 	PHY_WRITE(sc, 0x0000, 0x0140);
199 
200 	delay(5);
201 
202 	for (i = 0; dspcode[i].reg != 0; i++)
203 		IGPHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
204 
205 	PHY_WRITE(sc, MII_IGPHY_PAGE_SELECT,0x0000);
206 	PHY_WRITE(sc, 0x0000, 0x3300);
207 }
208 
209 static void
210 igphy_reset(struct mii_softc *sc)
211 {
212 	uint16_t fused, fine, coarse;
213 
214 	mii_phy_reset(sc);
215 	igphy_load_dspcode(sc);
216 
217 	fused = IGPHY_READ(sc, MII_IGPHY_ANALOG_SPARE_FUSE_STATUS);
218 	if ((fused & ANALOG_SPARE_FUSE_ENABLED) == 0) {
219 		fused = IGPHY_READ(sc, MII_IGPHY_ANALOG_FUSE_STATUS);
220 
221 		fine = fused & ANALOG_FUSE_FINE_MASK;
222 		coarse = fused & ANALOG_FUSE_COARSE_MASK;
223 
224 		if (coarse > ANALOG_FUSE_COARSE_THRESH) {
225 			coarse -= ANALOG_FUSE_COARSE_10;
226 			fine -= ANALOG_FUSE_FINE_1;
227 		} else if (coarse == ANALOG_FUSE_COARSE_THRESH)
228 			fine -= ANALOG_FUSE_FINE_10;
229 
230 		fused = (fused & ANALOG_FUSE_POLY_MASK) |
231 			(fine & ANALOG_FUSE_FINE_MASK) |
232 			(coarse & ANALOG_FUSE_COARSE_MASK);
233 
234 		IGPHY_WRITE(sc, MII_IGPHY_ANALOG_FUSE_CONTROL, fused);
235 		IGPHY_WRITE(sc, MII_IGPHY_ANALOG_FUSE_BYPASS,
236 		    ANALOG_FUSE_ENABLE_SW_CONTROL);
237 	}
238 	PHY_WRITE(sc, MII_IGPHY_PAGE_SELECT,0x0000);
239 }
240 
241 
242 static int
243 igphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
244 {
245 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
246 	uint16_t reg;
247 
248 	switch (cmd) {
249 	case MII_POLLSTAT:
250 		/*
251 		 * If we're not polling our PHY instance, just return.
252 		 */
253 		if (IFM_INST(ife->ifm_media) != sc->mii_inst)
254 			return (0);
255 		break;
256 
257 	case MII_MEDIACHG:
258 		/*
259 		 * If the media indicates a different PHY instance,
260 		 * isolate ourselves.
261 		 */
262 		if (IFM_INST(ife->ifm_media) != sc->mii_inst) {
263 			reg = PHY_READ(sc, MII_BMCR);
264 			PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO);
265 			return (0);
266 		}
267 
268 		/*
269 		 * If the interface is not up, don't do anything.
270 		 */
271 		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
272 			break;
273 
274 		mii_phy_setmedia(sc);
275 		break;
276 
277 	case MII_TICK:
278 		/*
279 		 * If we're not currently selected, just return.
280 		 */
281 		if (IFM_INST(ife->ifm_media) != sc->mii_inst)
282 			return (0);
283 
284 		igphy_smartspeed_workaround(sc);
285 
286 		if (mii_phy_tick(sc) == EJUSTRETURN)
287 			return (0);
288 		break;
289 
290 	case MII_DOWN:
291 		mii_phy_down(sc);
292 		return (0);
293 	}
294 
295 	/* Update the media status. */
296 	mii_phy_status(sc);
297 
298 	/* Callback if something changed. */
299 	mii_phy_update(sc, cmd);
300 	return (0);
301 }
302 
303 
304 static void
305 igphy_status(struct mii_softc *sc)
306 {
307 	struct mii_data *mii = sc->mii_pdata;
308 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
309 	uint16_t bmcr, pssr, gtsr, bmsr;
310 
311 	mii->mii_media_status = IFM_AVALID;
312 	mii->mii_media_active = IFM_ETHER;
313 
314 	pssr = PHY_READ(sc, MII_IGPHY_PORT_STATUS);
315 
316 	if (pssr & PSSR_LINK_UP)
317 		mii->mii_media_status |= IFM_ACTIVE;
318 
319 	bmcr = PHY_READ(sc, MII_BMCR);
320 	if (bmcr & BMCR_ISO) {
321 		mii->mii_media_active |= IFM_NONE;
322 		mii->mii_media_status = 0;
323 		return;
324 	}
325 
326 	if (bmcr & BMCR_LOOP)
327 		mii->mii_media_active |= IFM_LOOP;
328 
329 	bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
330 
331 	/*
332 	 * XXX can't check if the info is valid, no
333 	 * 'negotiation done' bit?
334 	 */
335 	if (bmcr & BMCR_AUTOEN) {
336 		if ((bmsr & BMSR_ACOMP) == 0) {
337 			mii->mii_media_active |= IFM_NONE;
338 			return;
339 		}
340 		switch (pssr & PSSR_SPEED_MASK) {
341 		case PSSR_SPEED_1000MBPS:
342 			mii->mii_media_active |= IFM_1000_T;
343 			gtsr = PHY_READ(sc, MII_100T2SR);
344 			if (gtsr & GTSR_MS_RES)
345 				mii->mii_media_active |= IFM_ETH_MASTER;
346 			break;
347 
348 		case PSSR_SPEED_100MBPS:
349 			mii->mii_media_active |= IFM_100_TX;
350 			break;
351 
352 		case PSSR_SPEED_10MBPS:
353 			mii->mii_media_active |= IFM_10_T;
354 			break;
355 
356 		default:
357 			mii->mii_media_active |= IFM_NONE;
358 			mii->mii_media_status = 0;
359 			return;
360 		}
361 
362 		if (pssr & PSSR_FULL_DUPLEX)
363 			mii->mii_media_active |=
364 			    IFM_FDX | mii_phy_flowstatus(sc);
365 	} else
366 		mii->mii_media_active = ife->ifm_media;
367 }
368 
369 static void
370 igphy_smartspeed_workaround(struct mii_softc *sc)
371 {
372 	struct igphy_softc *igsc = (struct igphy_softc *) sc;
373 	uint16_t reg, gtsr, gtcr;
374 
375 	if ((PHY_READ(sc, MII_BMCR) & BMCR_AUTOEN) == 0)
376 		return;
377 
378 	/* XXX Assume 1000TX-FDX is advertized if doing autonegotiation. */
379 
380 	reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
381 	if ((reg & BMSR_LINK) == 0) {
382 		switch (igsc->sc_smartspeed) {
383 		case 0:
384 			gtsr = PHY_READ(sc, MII_100T2SR);
385 			if (!(gtsr & GTSR_MAN_MS_FLT))
386 				break;
387 			gtsr = PHY_READ(sc, MII_100T2SR);
388 			if (gtsr & GTSR_MAN_MS_FLT) {
389 				gtcr = PHY_READ(sc, MII_100T2CR);
390 				if (gtcr & GTCR_MAN_MS) {
391 					gtcr &= ~GTCR_MAN_MS;
392 					PHY_WRITE(sc, MII_100T2CR,
393 					    gtcr);
394 				}
395 				mii_phy_auto(sc, 0);
396 			}
397 			break;
398 		case IGPHY_TICK_DOWNSHIFT:
399 			gtcr = PHY_READ(sc, MII_100T2CR);
400 			gtcr |= GTCR_MAN_MS;
401 			PHY_WRITE(sc, MII_100T2CR, gtcr);
402 			mii_phy_auto(sc, 0);
403 			break;
404 		default:
405 			break;
406 		}
407 		if (igsc->sc_smartspeed++ == IGPHY_TICK_MAX)
408 			igsc->sc_smartspeed = 0;
409 	} else
410 		igsc->sc_smartspeed = 0;
411 }
412