xref: /netbsd-src/sys/dev/mii/igphy.c (revision 3816d47b2c42fcd6e549e3407f842a5b1a1d23ad)
1 /*	$NetBSD: igphy.c,v 1.20 2009/12/16 14:37:26 msaitoh 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  *
59  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
60  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
61  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
62  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
63  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
64  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
65  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
66  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
67  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
68  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
69  * POSSIBILITY OF SUCH DAMAGE.
70  */
71 
72 #include <sys/cdefs.h>
73 __KERNEL_RCSID(0, "$NetBSD: igphy.c,v 1.20 2009/12/16 14:37:26 msaitoh Exp $");
74 
75 #include "opt_mii.h"
76 
77 #include <sys/param.h>
78 #include <sys/systm.h>
79 #include <sys/kernel.h>
80 #include <sys/device.h>
81 #include <sys/socket.h>
82 #include <sys/errno.h>
83 
84 #include <net/if.h>
85 #include <net/if_media.h>
86 
87 #include <dev/mii/mii.h>
88 #include <dev/mii/miivar.h>
89 #include <dev/mii/miidevs.h>
90 #include <dev/mii/igphyreg.h>
91 #include <dev/pci/if_wmvar.h>
92 
93 struct igphy_softc {
94 	struct mii_softc sc_mii;
95 	int sc_smartspeed;
96 	uint32_t sc_mactype;
97 	uint32_t sc_macflags;
98 };
99 
100 static void igphy_reset(struct mii_softc *);
101 static void igphy_load_dspcode(struct mii_softc *);
102 static void igphy_load_dspcode_igp3(struct mii_softc *);
103 static void igphy_smartspeed_workaround(struct mii_softc *sc);
104 
105 static int	igphymatch(device_t, cfdata_t, void *);
106 static void	igphyattach(device_t, device_t, void *);
107 
108 CFATTACH_DECL_NEW(igphy, sizeof(struct igphy_softc),
109     igphymatch, igphyattach, mii_phy_detach, mii_phy_activate);
110 
111 static int	igphy_service(struct mii_softc *, struct mii_data *, int);
112 static void	igphy_status(struct mii_softc *);
113 
114 static const struct mii_phy_funcs igphy_funcs = {
115 	igphy_service, igphy_status, igphy_reset,
116 };
117 
118 static const struct mii_phydesc igphys[] = {
119 	{ MII_OUI_yyINTEL,		MII_MODEL_yyINTEL_IGP01E1000,
120 	  MII_STR_yyINTEL_IGP01E1000 },
121 
122 	{ MII_OUI_yyINTEL,		MII_MODEL_yyINTEL_I82566,
123 	  MII_STR_yyINTEL_I82566 },
124 
125 	{0,				0,
126 	 NULL },
127 };
128 
129 static int
130 igphymatch(device_t parent, cfdata_t match, void *aux)
131 {
132 	struct mii_attach_args *ma = aux;
133 
134 	if (mii_phy_match(ma, igphys) != NULL)
135 		return 10;
136 
137 	return 0;
138 }
139 
140 static void
141 igphyattach(device_t parent, device_t self, void *aux)
142 {
143 	struct mii_softc *sc = device_private(self);
144 	struct mii_attach_args *ma = aux;
145 	struct mii_data *mii = ma->mii_data;
146 	const struct mii_phydesc *mpd;
147 	struct igphy_softc *igsc = (struct igphy_softc *) sc;
148 	prop_dictionary_t dict;
149 
150 	mpd = mii_phy_match(ma, igphys);
151 	aprint_naive(": Media interface\n");
152 	aprint_normal(": %s, rev. %d\n", mpd->mpd_name, MII_REV(ma->mii_id2));
153 
154 	dict = device_properties(parent);
155 	if (!prop_dictionary_get_uint32(dict, "mactype", &igsc->sc_mactype))
156 		aprint_error("WARNING! Failed to get mactype\n");
157 	if (!prop_dictionary_get_uint32(dict, "macflags", &igsc->sc_macflags))
158 		aprint_error("WARNING! Failed to get macflags\n");
159 
160 	sc->mii_dev = self;
161 	sc->mii_inst = mii->mii_instance;
162 	sc->mii_phy = ma->mii_phyno;
163 	sc->mii_funcs = &igphy_funcs;
164 	sc->mii_pdata = mii;
165 	sc->mii_flags = ma->mii_flags;
166 	sc->mii_anegticks = MII_ANEGTICKS_GIGE;
167 
168 	PHY_RESET(sc);
169 
170 	sc->mii_capabilities =
171 	    PHY_READ(sc, MII_BMSR) & ma->mii_capmask;
172 	if (sc->mii_capabilities & BMSR_EXTSTAT)
173 		sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR);
174 	aprint_normal_dev(self, "");
175 	if ((sc->mii_capabilities & BMSR_MEDIAMASK) == 0 &&
176 	    (sc->mii_extcapabilities & EXTSR_MEDIAMASK) == 0)
177 		aprint_error("no media present");
178 	else
179 		mii_phy_add_media(sc);
180 	aprint_normal("\n");
181 }
182 
183 typedef struct {
184 	int reg;
185 	uint16_t val;
186 } dspcode;
187 
188 static const dspcode igp1code[] = {
189 	{ 0x1f95, 0x0001 },
190 	{ 0x1f71, 0xbd21 },
191 	{ 0x1f79, 0x0018 },
192 	{ 0x1f30, 0x1600 },
193 	{ 0x1f31, 0x0014 },
194 	{ 0x1f32, 0x161c },
195 	{ 0x1f94, 0x0003 },
196 	{ 0x1f96, 0x003f },
197 	{ 0x2010, 0x0008 },
198 	{ 0, 0 },
199 };
200 
201 static const dspcode igp1code_r2[] = {
202 	{ 0x1f73, 0x0099 },
203 	{ 0, 0 },
204 };
205 
206 static const dspcode igp3code[] = {
207 	{ 0x2f5b, 0x9018},
208 	{ 0x2f52, 0x0000},
209 	{ 0x2fb1, 0x8b24},
210 	{ 0x2fb2, 0xf8f0},
211 	{ 0x2010, 0x10b0},
212 	{ 0x2011, 0x0000},
213 	{ 0x20dd, 0x249a},
214 	{ 0x20de, 0x00d3},
215 	{ 0x28b4, 0x04ce},
216 	{ 0x2f70, 0x29e4},
217 	{ 0x0000, 0x0140},
218 	{ 0x1f30, 0x1606},
219 	{ 0x1f31, 0xb814},
220 	{ 0x1f35, 0x002a},
221 	{ 0x1f3e, 0x0067},
222 	{ 0x1f54, 0x0065},
223 	{ 0x1f55, 0x002a},
224 	{ 0x1f56, 0x002a},
225 	{ 0x1f72, 0x3fb0},
226 	{ 0x1f76, 0xc0ff},
227 	{ 0x1f77, 0x1dec},
228 	{ 0x1f78, 0xf9ef},
229 	{ 0x1f79, 0x0210},
230 	{ 0x1895, 0x0003},
231 	{ 0x1796, 0x0008},
232 	{ 0x1798, 0xd008},
233 	{ 0x1898, 0xd918},
234 	{ 0x187a, 0x0800},
235 	{ 0x0019, 0x008d},
236 	{ 0x001b, 0x2080},
237 	{ 0x0014, 0x0045},
238 	{ 0x0000, 0x1340},
239 	{ 0, 0 },
240 };
241 
242 /* DSP patch for igp1 and igp2 */
243 static void
244 igphy_load_dspcode(struct mii_softc *sc)
245 {
246 	struct igphy_softc *igsc = (struct igphy_softc *) sc;
247 	const dspcode *code;
248 	uint16_t reg;
249 	int i;
250 
251 	/* This workaround is only for 82541 and 82547 */
252 	switch (igsc->sc_mactype) {
253 	case WM_T_82541:
254 	case WM_T_82547:
255 		code = igp1code;
256 		break;
257 	case WM_T_82541_2:
258 	case WM_T_82547_2:
259 		code = igp1code_r2;
260 		break;
261 	default:
262 		return;	/* byebye */
263 	}
264 
265 	/* Delay after phy reset to enable NVM configuration to load */
266 	delay(20000);
267 
268 	/*
269 	 * Save off the current value of register 0x2F5B to be restored at
270 	 * the end of this routine.
271 	 */
272 	reg = IGPHY_READ(sc, 0x2f5b);
273 
274 	/* Disabled the PHY transmitter */
275 	IGPHY_WRITE(sc, 0x2f5b, 0x0003);
276 
277 	delay(20000);
278 
279 	PHY_WRITE(sc, MII_IGPHY_PAGE_SELECT, 0x0000);
280 	PHY_WRITE(sc, 0x0000, 0x0140);
281 
282 	delay(5000);
283 
284 	for (i = 0; !((code[i].reg == 0) && (code[i].val == 0)); i++)
285 		IGPHY_WRITE(sc, code[i].reg, code[i].val);
286 
287 	PHY_WRITE(sc, MII_IGPHY_PAGE_SELECT,0x0000);
288 	PHY_WRITE(sc, 0x0000, 0x3300);
289 
290 	delay(20000);
291 
292 	/* Now enable the transmitter */
293 	IGPHY_WRITE(sc, 0x2f5b, reg);
294 }
295 
296 static void
297 igphy_load_dspcode_igp3(struct mii_softc *sc)
298 {
299 	const dspcode *code = igp3code;
300 	int i;
301 
302 	for (i = 0; !((code[i].reg == 0) && (code[i].val == 0)); i++)
303 		IGPHY_WRITE(sc, code[i].reg, code[i].val);
304 }
305 
306 static void
307 igphy_reset(struct mii_softc *sc)
308 {
309 	struct igphy_softc *igsc = (struct igphy_softc *) sc;
310 	uint16_t fused, fine, coarse;
311 
312 	mii_phy_reset(sc);
313 	delay(150);
314 
315 	switch (igsc->sc_mactype) {
316 	case WM_T_82541:
317 	case WM_T_82547:
318 	case WM_T_82541_2:
319 	case WM_T_82547_2:
320 		igphy_load_dspcode(sc);
321 		break;
322 	case WM_T_ICH8:
323 	case WM_T_ICH9:
324 		if ((igsc->sc_macflags & WM_F_EEPROM_INVALID) != 0)
325 			igphy_load_dspcode_igp3(sc);
326 		break;
327 	default:	/* Not for ICH10, PCH and 8257[12] */
328 		break;
329 	}
330 
331 	if (igsc->sc_mactype == WM_T_82547) {
332 		fused = IGPHY_READ(sc, MII_IGPHY_ANALOG_SPARE_FUSE_STATUS);
333 		if ((fused & ANALOG_SPARE_FUSE_ENABLED) == 0) {
334 			fused = IGPHY_READ(sc, MII_IGPHY_ANALOG_FUSE_STATUS);
335 
336 			fine = fused & ANALOG_FUSE_FINE_MASK;
337 			coarse = fused & ANALOG_FUSE_COARSE_MASK;
338 
339 			if (coarse > ANALOG_FUSE_COARSE_THRESH) {
340 				coarse -= ANALOG_FUSE_COARSE_10;
341 				fine -= ANALOG_FUSE_FINE_1;
342 			} else if (coarse == ANALOG_FUSE_COARSE_THRESH)
343 				fine -= ANALOG_FUSE_FINE_10;
344 
345 			fused = (fused & ANALOG_FUSE_POLY_MASK) |
346 			    (fine & ANALOG_FUSE_FINE_MASK) |
347 			    (coarse & ANALOG_FUSE_COARSE_MASK);
348 
349 			IGPHY_WRITE(sc, MII_IGPHY_ANALOG_FUSE_CONTROL, fused);
350 			IGPHY_WRITE(sc, MII_IGPHY_ANALOG_FUSE_BYPASS,
351 			    ANALOG_FUSE_ENABLE_SW_CONTROL);
352 		}
353 	}
354 	PHY_WRITE(sc, MII_IGPHY_PAGE_SELECT, 0x0000);
355 }
356 
357 
358 static int
359 igphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
360 {
361 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
362 	uint16_t reg;
363 
364 	switch (cmd) {
365 	case MII_POLLSTAT:
366 		/*
367 		 * If we're not polling our PHY instance, just return.
368 		 */
369 		if (IFM_INST(ife->ifm_media) != sc->mii_inst)
370 			return (0);
371 		break;
372 
373 	case MII_MEDIACHG:
374 		/*
375 		 * If the media indicates a different PHY instance,
376 		 * isolate ourselves.
377 		 */
378 		if (IFM_INST(ife->ifm_media) != sc->mii_inst) {
379 			reg = PHY_READ(sc, MII_BMCR);
380 			PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO);
381 			return (0);
382 		}
383 
384 		/*
385 		 * If the interface is not up, don't do anything.
386 		 */
387 		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
388 			break;
389 
390 		reg = PHY_READ(sc, MII_IGPHY_PORT_CTRL);
391 		if (IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO) {
392 			reg |= PSCR_AUTO_MDIX;
393 			reg &= ~PSCR_FORCE_MDI_MDIX;
394 			PHY_WRITE(sc, MII_IGPHY_PORT_CTRL, reg);
395 		} else {
396 			reg &= ~(PSCR_AUTO_MDIX | PSCR_FORCE_MDI_MDIX);
397 			PHY_WRITE(sc, MII_IGPHY_PORT_CTRL, reg);
398 		}
399 
400 		mii_phy_setmedia(sc);
401 		break;
402 
403 	case MII_TICK:
404 		/*
405 		 * If we're not currently selected, just return.
406 		 */
407 		if (IFM_INST(ife->ifm_media) != sc->mii_inst)
408 			return (0);
409 
410 		igphy_smartspeed_workaround(sc);
411 
412 		if (mii_phy_tick(sc) == EJUSTRETURN)
413 			return (0);
414 		break;
415 
416 	case MII_DOWN:
417 		mii_phy_down(sc);
418 		return (0);
419 	}
420 
421 	/* Update the media status. */
422 	mii_phy_status(sc);
423 
424 	/* Callback if something changed. */
425 	mii_phy_update(sc, cmd);
426 	return (0);
427 }
428 
429 
430 static void
431 igphy_status(struct mii_softc *sc)
432 {
433 	struct mii_data *mii = sc->mii_pdata;
434 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
435 	uint16_t bmcr, pssr, gtsr, bmsr;
436 
437 	mii->mii_media_status = IFM_AVALID;
438 	mii->mii_media_active = IFM_ETHER;
439 
440 	pssr = PHY_READ(sc, MII_IGPHY_PORT_STATUS);
441 
442 	if (pssr & PSSR_LINK_UP)
443 		mii->mii_media_status |= IFM_ACTIVE;
444 
445 	bmcr = PHY_READ(sc, MII_BMCR);
446 	if (bmcr & BMCR_ISO) {
447 		mii->mii_media_active |= IFM_NONE;
448 		mii->mii_media_status = 0;
449 		return;
450 	}
451 
452 	if (bmcr & BMCR_LOOP)
453 		mii->mii_media_active |= IFM_LOOP;
454 
455 	bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
456 
457 	/*
458 	 * XXX can't check if the info is valid, no
459 	 * 'negotiation done' bit?
460 	 */
461 	if (bmcr & BMCR_AUTOEN) {
462 		if ((bmsr & BMSR_ACOMP) == 0) {
463 			mii->mii_media_active |= IFM_NONE;
464 			return;
465 		}
466 		switch (pssr & PSSR_SPEED_MASK) {
467 		case PSSR_SPEED_1000MBPS:
468 			mii->mii_media_active |= IFM_1000_T;
469 			gtsr = PHY_READ(sc, MII_100T2SR);
470 			if (gtsr & GTSR_MS_RES)
471 				mii->mii_media_active |= IFM_ETH_MASTER;
472 			break;
473 
474 		case PSSR_SPEED_100MBPS:
475 			mii->mii_media_active |= IFM_100_TX;
476 			break;
477 
478 		case PSSR_SPEED_10MBPS:
479 			mii->mii_media_active |= IFM_10_T;
480 			break;
481 
482 		default:
483 			mii->mii_media_active |= IFM_NONE;
484 			mii->mii_media_status = 0;
485 			return;
486 		}
487 
488 		if (pssr & PSSR_FULL_DUPLEX)
489 			mii->mii_media_active |=
490 			    IFM_FDX | mii_phy_flowstatus(sc);
491 	} else
492 		mii->mii_media_active = ife->ifm_media;
493 }
494 
495 static void
496 igphy_smartspeed_workaround(struct mii_softc *sc)
497 {
498 	struct igphy_softc *igsc = (struct igphy_softc *) sc;
499 	uint16_t reg, gtsr, gtcr;
500 
501 
502 	/* This workaround is only for 82541 and 82547 */
503 	switch (igsc->sc_mactype) {
504 	case WM_T_82541:
505 	case WM_T_82541_2:
506 	case WM_T_82547:
507 	case WM_T_82547_2:
508 		break;
509 	default:
510 		/* byebye */
511 		return;
512 	}
513 
514 	if ((PHY_READ(sc, MII_BMCR) & BMCR_AUTOEN) == 0)
515 		return;
516 
517 	/* XXX Assume 1000TX-FDX is advertized if doing autonegotiation. */
518 
519 	reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
520 	if ((reg & BMSR_LINK) == 0) {
521 		switch (igsc->sc_smartspeed) {
522 		case 0:
523 			gtsr = PHY_READ(sc, MII_100T2SR);
524 			if (!(gtsr & GTSR_MAN_MS_FLT))
525 				break;
526 			gtsr = PHY_READ(sc, MII_100T2SR);
527 			if (gtsr & GTSR_MAN_MS_FLT) {
528 				gtcr = PHY_READ(sc, MII_100T2CR);
529 				if (gtcr & GTCR_MAN_MS) {
530 					gtcr &= ~GTCR_MAN_MS;
531 					PHY_WRITE(sc, MII_100T2CR,
532 					    gtcr);
533 				}
534 				mii_phy_auto(sc, 0);
535 			}
536 			break;
537 		case IGPHY_TICK_DOWNSHIFT:
538 			gtcr = PHY_READ(sc, MII_100T2CR);
539 			gtcr |= GTCR_MAN_MS;
540 			PHY_WRITE(sc, MII_100T2CR, gtcr);
541 			mii_phy_auto(sc, 0);
542 			break;
543 		default:
544 			break;
545 		}
546 		if (igsc->sc_smartspeed++ == IGPHY_TICK_MAX)
547 			igsc->sc_smartspeed = 0;
548 	} else
549 		igsc->sc_smartspeed = 0;
550 }
551