xref: /netbsd-src/sys/dev/mii/igphy.c (revision 8b0f9554ff8762542c4defc4f70e1eb76fb508fa)
1 /*	$NetBSD: igphy.c,v 1.13 2007/12/09 20:28:03 jmcneill 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.13 2007/12/09 20:28:03 jmcneill 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 	{ MII_OUI_yyINTEL,		MII_MODEL_yyINTEL_I82566,
127 	  MII_STR_yyINTEL_I82566 },
128 
129 	{0,				0,
130 	 NULL },
131 };
132 
133 static int
134 igphymatch(struct device *parent, struct cfdata *match,
135     void *aux)
136 {
137 	struct mii_attach_args *ma = aux;
138 
139 	if (mii_phy_match(ma, igphys) != NULL)
140 		return 10;
141 
142 	return 0;
143 }
144 
145 static void
146 igphyattach(struct device *parent, struct device *self, void *aux)
147 {
148 	struct mii_softc *sc = device_private(self);
149 	struct mii_attach_args *ma = aux;
150 	struct mii_data *mii = ma->mii_data;
151 	const struct mii_phydesc *mpd;
152 
153 	mpd = mii_phy_match(ma, igphys);
154 	aprint_naive(": Media interface\n");
155 	aprint_normal(": %s, rev. %d\n", mpd->mpd_name, MII_REV(ma->mii_id2));
156 
157 	sc->mii_inst = mii->mii_instance;
158 	sc->mii_phy = ma->mii_phyno;
159 	sc->mii_funcs = &igphy_funcs;
160 	sc->mii_pdata = mii;
161 	sc->mii_flags = ma->mii_flags;
162 	sc->mii_anegticks = MII_ANEGTICKS_GIGE;
163 
164 	PHY_RESET(sc);
165 
166 	sc->mii_capabilities =
167 	    PHY_READ(sc, MII_BMSR) & ma->mii_capmask;
168 	if (sc->mii_capabilities & BMSR_EXTSTAT)
169 		sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR);
170 	aprint_normal("%s: ", sc->mii_dev.dv_xname);
171 	if ((sc->mii_capabilities & BMSR_MEDIAMASK) == 0 &&
172 	    (sc->mii_extcapabilities & EXTSR_MEDIAMASK) == 0)
173 		aprint_error("no media present");
174 	else
175 		mii_phy_add_media(sc);
176 	aprint_normal("\n");
177 
178 	if (!pmf_device_register(self, NULL, mii_phy_resume))
179 		aprint_error_dev(self, "couldn't establish power handler\n");
180 }
181 
182 static void
183 igphy_load_dspcode(struct mii_softc *sc)
184 {
185 	static const struct {
186 		int reg;
187 		uint16_t val;
188 	} dspcode[] = {
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 	int i;
201 
202 	delay(10);
203 
204 	PHY_WRITE(sc, MII_IGPHY_PAGE_SELECT, 0x0000);
205 	PHY_WRITE(sc, 0x0000, 0x0140);
206 
207 	delay(5);
208 
209 	for (i = 0; dspcode[i].reg != 0; i++)
210 		IGPHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
211 
212 	PHY_WRITE(sc, MII_IGPHY_PAGE_SELECT,0x0000);
213 	PHY_WRITE(sc, 0x0000, 0x3300);
214 }
215 
216 static void
217 igphy_reset(struct mii_softc *sc)
218 {
219 	uint16_t fused, fine, coarse;
220 
221 	mii_phy_reset(sc);
222 	igphy_load_dspcode(sc);
223 
224 	fused = IGPHY_READ(sc, MII_IGPHY_ANALOG_SPARE_FUSE_STATUS);
225 	if ((fused & ANALOG_SPARE_FUSE_ENABLED) == 0) {
226 		fused = IGPHY_READ(sc, MII_IGPHY_ANALOG_FUSE_STATUS);
227 
228 		fine = fused & ANALOG_FUSE_FINE_MASK;
229 		coarse = fused & ANALOG_FUSE_COARSE_MASK;
230 
231 		if (coarse > ANALOG_FUSE_COARSE_THRESH) {
232 			coarse -= ANALOG_FUSE_COARSE_10;
233 			fine -= ANALOG_FUSE_FINE_1;
234 		} else if (coarse == ANALOG_FUSE_COARSE_THRESH)
235 			fine -= ANALOG_FUSE_FINE_10;
236 
237 		fused = (fused & ANALOG_FUSE_POLY_MASK) |
238 			(fine & ANALOG_FUSE_FINE_MASK) |
239 			(coarse & ANALOG_FUSE_COARSE_MASK);
240 
241 		IGPHY_WRITE(sc, MII_IGPHY_ANALOG_FUSE_CONTROL, fused);
242 		IGPHY_WRITE(sc, MII_IGPHY_ANALOG_FUSE_BYPASS,
243 		    ANALOG_FUSE_ENABLE_SW_CONTROL);
244 	}
245 	PHY_WRITE(sc, MII_IGPHY_PAGE_SELECT,0x0000);
246 }
247 
248 
249 static int
250 igphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
251 {
252 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
253 	uint16_t reg;
254 
255 	switch (cmd) {
256 	case MII_POLLSTAT:
257 		/*
258 		 * If we're not polling our PHY instance, just return.
259 		 */
260 		if (IFM_INST(ife->ifm_media) != sc->mii_inst)
261 			return (0);
262 		break;
263 
264 	case MII_MEDIACHG:
265 		/*
266 		 * If the media indicates a different PHY instance,
267 		 * isolate ourselves.
268 		 */
269 		if (IFM_INST(ife->ifm_media) != sc->mii_inst) {
270 			reg = PHY_READ(sc, MII_BMCR);
271 			PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO);
272 			return (0);
273 		}
274 
275 		/*
276 		 * If the interface is not up, don't do anything.
277 		 */
278 		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
279 			break;
280 
281 		reg = PHY_READ(sc, MII_IGPHY_PORT_CTRL);
282 		if (IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO) {
283 			reg |= PSCR_AUTO_MDIX;
284 			reg &= ~PSCR_FORCE_MDI_MDIX;
285 			PHY_WRITE(sc, MII_IGPHY_PORT_CTRL, reg);
286 		} else {
287 			reg &= ~(PSCR_AUTO_MDIX | PSCR_FORCE_MDI_MDIX);
288 			PHY_WRITE(sc, MII_IGPHY_PORT_CTRL, reg);
289 		}
290 
291 		mii_phy_setmedia(sc);
292 		break;
293 
294 	case MII_TICK:
295 		/*
296 		 * If we're not currently selected, just return.
297 		 */
298 		if (IFM_INST(ife->ifm_media) != sc->mii_inst)
299 			return (0);
300 
301 		igphy_smartspeed_workaround(sc);
302 
303 		if (mii_phy_tick(sc) == EJUSTRETURN)
304 			return (0);
305 		break;
306 
307 	case MII_DOWN:
308 		mii_phy_down(sc);
309 		return (0);
310 	}
311 
312 	/* Update the media status. */
313 	mii_phy_status(sc);
314 
315 	/* Callback if something changed. */
316 	mii_phy_update(sc, cmd);
317 	return (0);
318 }
319 
320 
321 static void
322 igphy_status(struct mii_softc *sc)
323 {
324 	struct mii_data *mii = sc->mii_pdata;
325 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
326 	uint16_t bmcr, pssr, gtsr, bmsr;
327 
328 	mii->mii_media_status = IFM_AVALID;
329 	mii->mii_media_active = IFM_ETHER;
330 
331 	pssr = PHY_READ(sc, MII_IGPHY_PORT_STATUS);
332 
333 	if (pssr & PSSR_LINK_UP)
334 		mii->mii_media_status |= IFM_ACTIVE;
335 
336 	bmcr = PHY_READ(sc, MII_BMCR);
337 	if (bmcr & BMCR_ISO) {
338 		mii->mii_media_active |= IFM_NONE;
339 		mii->mii_media_status = 0;
340 		return;
341 	}
342 
343 	if (bmcr & BMCR_LOOP)
344 		mii->mii_media_active |= IFM_LOOP;
345 
346 	bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
347 
348 	/*
349 	 * XXX can't check if the info is valid, no
350 	 * 'negotiation done' bit?
351 	 */
352 	if (bmcr & BMCR_AUTOEN) {
353 		if ((bmsr & BMSR_ACOMP) == 0) {
354 			mii->mii_media_active |= IFM_NONE;
355 			return;
356 		}
357 		switch (pssr & PSSR_SPEED_MASK) {
358 		case PSSR_SPEED_1000MBPS:
359 			mii->mii_media_active |= IFM_1000_T;
360 			gtsr = PHY_READ(sc, MII_100T2SR);
361 			if (gtsr & GTSR_MS_RES)
362 				mii->mii_media_active |= IFM_ETH_MASTER;
363 			break;
364 
365 		case PSSR_SPEED_100MBPS:
366 			mii->mii_media_active |= IFM_100_TX;
367 			break;
368 
369 		case PSSR_SPEED_10MBPS:
370 			mii->mii_media_active |= IFM_10_T;
371 			break;
372 
373 		default:
374 			mii->mii_media_active |= IFM_NONE;
375 			mii->mii_media_status = 0;
376 			return;
377 		}
378 
379 		if (pssr & PSSR_FULL_DUPLEX)
380 			mii->mii_media_active |=
381 			    IFM_FDX | mii_phy_flowstatus(sc);
382 	} else
383 		mii->mii_media_active = ife->ifm_media;
384 }
385 
386 static void
387 igphy_smartspeed_workaround(struct mii_softc *sc)
388 {
389 	struct igphy_softc *igsc = (struct igphy_softc *) sc;
390 	uint16_t reg, gtsr, gtcr;
391 
392 	if ((PHY_READ(sc, MII_BMCR) & BMCR_AUTOEN) == 0)
393 		return;
394 
395 	/* XXX Assume 1000TX-FDX is advertized if doing autonegotiation. */
396 
397 	reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
398 	if ((reg & BMSR_LINK) == 0) {
399 		switch (igsc->sc_smartspeed) {
400 		case 0:
401 			gtsr = PHY_READ(sc, MII_100T2SR);
402 			if (!(gtsr & GTSR_MAN_MS_FLT))
403 				break;
404 			gtsr = PHY_READ(sc, MII_100T2SR);
405 			if (gtsr & GTSR_MAN_MS_FLT) {
406 				gtcr = PHY_READ(sc, MII_100T2CR);
407 				if (gtcr & GTCR_MAN_MS) {
408 					gtcr &= ~GTCR_MAN_MS;
409 					PHY_WRITE(sc, MII_100T2CR,
410 					    gtcr);
411 				}
412 				mii_phy_auto(sc, 0);
413 			}
414 			break;
415 		case IGPHY_TICK_DOWNSHIFT:
416 			gtcr = PHY_READ(sc, MII_100T2CR);
417 			gtcr |= GTCR_MAN_MS;
418 			PHY_WRITE(sc, MII_100T2CR, gtcr);
419 			mii_phy_auto(sc, 0);
420 			break;
421 		default:
422 			break;
423 		}
424 		if (igsc->sc_smartspeed++ == IGPHY_TICK_MAX)
425 			igsc->sc_smartspeed = 0;
426 	} else
427 		igsc->sc_smartspeed = 0;
428 }
429