xref: /netbsd-src/sys/dev/mii/igphy.c (revision 7fa608457b817eca6e0977b37f758ae064f3c99c)
1 /*	$NetBSD: igphy.c,v 1.12 2007/08/28 01:11:50 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  * 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.12 2007/08/28 01:11:50 msaitoh 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 
179 static void
180 igphy_load_dspcode(struct mii_softc *sc)
181 {
182 	static const struct {
183 		int reg;
184 		uint16_t val;
185 	} dspcode[] = {
186 		{ 0x1f95, 0x0001 },
187 		{ 0x1f71, 0xbd21 },
188 		{ 0x1f79, 0x0018 },
189 		{ 0x1f30, 0x1600 },
190 		{ 0x1f31, 0x0014 },
191 		{ 0x1f32, 0x161c },
192 		{ 0x1f94, 0x0003 },
193 		{ 0x1f96, 0x003f },
194 		{ 0x2010, 0x0008 },
195 		{ 0, 0 },
196 	};
197 	int i;
198 
199 	delay(10);
200 
201 	PHY_WRITE(sc, MII_IGPHY_PAGE_SELECT, 0x0000);
202 	PHY_WRITE(sc, 0x0000, 0x0140);
203 
204 	delay(5);
205 
206 	for (i = 0; dspcode[i].reg != 0; i++)
207 		IGPHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
208 
209 	PHY_WRITE(sc, MII_IGPHY_PAGE_SELECT,0x0000);
210 	PHY_WRITE(sc, 0x0000, 0x3300);
211 }
212 
213 static void
214 igphy_reset(struct mii_softc *sc)
215 {
216 	uint16_t fused, fine, coarse;
217 
218 	mii_phy_reset(sc);
219 	igphy_load_dspcode(sc);
220 
221 	fused = IGPHY_READ(sc, MII_IGPHY_ANALOG_SPARE_FUSE_STATUS);
222 	if ((fused & ANALOG_SPARE_FUSE_ENABLED) == 0) {
223 		fused = IGPHY_READ(sc, MII_IGPHY_ANALOG_FUSE_STATUS);
224 
225 		fine = fused & ANALOG_FUSE_FINE_MASK;
226 		coarse = fused & ANALOG_FUSE_COARSE_MASK;
227 
228 		if (coarse > ANALOG_FUSE_COARSE_THRESH) {
229 			coarse -= ANALOG_FUSE_COARSE_10;
230 			fine -= ANALOG_FUSE_FINE_1;
231 		} else if (coarse == ANALOG_FUSE_COARSE_THRESH)
232 			fine -= ANALOG_FUSE_FINE_10;
233 
234 		fused = (fused & ANALOG_FUSE_POLY_MASK) |
235 			(fine & ANALOG_FUSE_FINE_MASK) |
236 			(coarse & ANALOG_FUSE_COARSE_MASK);
237 
238 		IGPHY_WRITE(sc, MII_IGPHY_ANALOG_FUSE_CONTROL, fused);
239 		IGPHY_WRITE(sc, MII_IGPHY_ANALOG_FUSE_BYPASS,
240 		    ANALOG_FUSE_ENABLE_SW_CONTROL);
241 	}
242 	PHY_WRITE(sc, MII_IGPHY_PAGE_SELECT,0x0000);
243 }
244 
245 
246 static int
247 igphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
248 {
249 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
250 	uint16_t reg;
251 
252 	switch (cmd) {
253 	case MII_POLLSTAT:
254 		/*
255 		 * If we're not polling our PHY instance, just return.
256 		 */
257 		if (IFM_INST(ife->ifm_media) != sc->mii_inst)
258 			return (0);
259 		break;
260 
261 	case MII_MEDIACHG:
262 		/*
263 		 * If the media indicates a different PHY instance,
264 		 * isolate ourselves.
265 		 */
266 		if (IFM_INST(ife->ifm_media) != sc->mii_inst) {
267 			reg = PHY_READ(sc, MII_BMCR);
268 			PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO);
269 			return (0);
270 		}
271 
272 		/*
273 		 * If the interface is not up, don't do anything.
274 		 */
275 		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
276 			break;
277 
278 		reg = PHY_READ(sc, MII_IGPHY_PORT_CTRL);
279 		if (IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO) {
280 			reg |= PSCR_AUTO_MDIX;
281 			reg &= ~PSCR_FORCE_MDI_MDIX;
282 			PHY_WRITE(sc, MII_IGPHY_PORT_CTRL, reg);
283 		} else {
284 			reg &= ~(PSCR_AUTO_MDIX | PSCR_FORCE_MDI_MDIX);
285 			PHY_WRITE(sc, MII_IGPHY_PORT_CTRL, reg);
286 		}
287 
288 		mii_phy_setmedia(sc);
289 		break;
290 
291 	case MII_TICK:
292 		/*
293 		 * If we're not currently selected, just return.
294 		 */
295 		if (IFM_INST(ife->ifm_media) != sc->mii_inst)
296 			return (0);
297 
298 		igphy_smartspeed_workaround(sc);
299 
300 		if (mii_phy_tick(sc) == EJUSTRETURN)
301 			return (0);
302 		break;
303 
304 	case MII_DOWN:
305 		mii_phy_down(sc);
306 		return (0);
307 	}
308 
309 	/* Update the media status. */
310 	mii_phy_status(sc);
311 
312 	/* Callback if something changed. */
313 	mii_phy_update(sc, cmd);
314 	return (0);
315 }
316 
317 
318 static void
319 igphy_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 	uint16_t bmcr, pssr, gtsr, bmsr;
324 
325 	mii->mii_media_status = IFM_AVALID;
326 	mii->mii_media_active = IFM_ETHER;
327 
328 	pssr = PHY_READ(sc, MII_IGPHY_PORT_STATUS);
329 
330 	if (pssr & PSSR_LINK_UP)
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 	bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
344 
345 	/*
346 	 * XXX can't check if the info is valid, no
347 	 * 'negotiation done' bit?
348 	 */
349 	if (bmcr & BMCR_AUTOEN) {
350 		if ((bmsr & BMSR_ACOMP) == 0) {
351 			mii->mii_media_active |= IFM_NONE;
352 			return;
353 		}
354 		switch (pssr & PSSR_SPEED_MASK) {
355 		case PSSR_SPEED_1000MBPS:
356 			mii->mii_media_active |= IFM_1000_T;
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 PSSR_SPEED_100MBPS:
363 			mii->mii_media_active |= IFM_100_TX;
364 			break;
365 
366 		case PSSR_SPEED_10MBPS:
367 			mii->mii_media_active |= IFM_10_T;
368 			break;
369 
370 		default:
371 			mii->mii_media_active |= IFM_NONE;
372 			mii->mii_media_status = 0;
373 			return;
374 		}
375 
376 		if (pssr & PSSR_FULL_DUPLEX)
377 			mii->mii_media_active |=
378 			    IFM_FDX | mii_phy_flowstatus(sc);
379 	} else
380 		mii->mii_media_active = ife->ifm_media;
381 }
382 
383 static void
384 igphy_smartspeed_workaround(struct mii_softc *sc)
385 {
386 	struct igphy_softc *igsc = (struct igphy_softc *) sc;
387 	uint16_t reg, gtsr, gtcr;
388 
389 	if ((PHY_READ(sc, MII_BMCR) & BMCR_AUTOEN) == 0)
390 		return;
391 
392 	/* XXX Assume 1000TX-FDX is advertized if doing autonegotiation. */
393 
394 	reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
395 	if ((reg & BMSR_LINK) == 0) {
396 		switch (igsc->sc_smartspeed) {
397 		case 0:
398 			gtsr = PHY_READ(sc, MII_100T2SR);
399 			if (!(gtsr & GTSR_MAN_MS_FLT))
400 				break;
401 			gtsr = PHY_READ(sc, MII_100T2SR);
402 			if (gtsr & GTSR_MAN_MS_FLT) {
403 				gtcr = PHY_READ(sc, MII_100T2CR);
404 				if (gtcr & GTCR_MAN_MS) {
405 					gtcr &= ~GTCR_MAN_MS;
406 					PHY_WRITE(sc, MII_100T2CR,
407 					    gtcr);
408 				}
409 				mii_phy_auto(sc, 0);
410 			}
411 			break;
412 		case IGPHY_TICK_DOWNSHIFT:
413 			gtcr = PHY_READ(sc, MII_100T2CR);
414 			gtcr |= GTCR_MAN_MS;
415 			PHY_WRITE(sc, MII_100T2CR, gtcr);
416 			mii_phy_auto(sc, 0);
417 			break;
418 		default:
419 			break;
420 		}
421 		if (igsc->sc_smartspeed++ == IGPHY_TICK_MAX)
422 			igsc->sc_smartspeed = 0;
423 	} else
424 		igsc->sc_smartspeed = 0;
425 }
426