xref: /netbsd-src/sys/dev/mii/igphy.c (revision 2980e352a13e8f0b545a366830c411e7a542ada8)
1 /*	$NetBSD: igphy.c,v 1.16 2008/05/04 17:06:09 xtraeme 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.16 2008/05/04 17:06:09 xtraeme 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 
91 #include <dev/mii/igphyreg.h>
92 
93 struct igphy_softc {
94 	struct mii_softc sc_mii;
95 	int sc_smartspeed;
96 };
97 
98 static void igphy_reset(struct mii_softc *);
99 static void igphy_load_dspcode(struct mii_softc *);
100 static void igphy_smartspeed_workaround(struct mii_softc *sc);
101 
102 static int	igphymatch(device_t, cfdata_t, void *);
103 static void	igphyattach(device_t, device_t, void *);
104 
105 CFATTACH_DECL_NEW(igphy, sizeof(struct igphy_softc),
106     igphymatch, igphyattach, mii_phy_detach, mii_phy_activate);
107 
108 static int	igphy_service(struct mii_softc *, struct mii_data *, int);
109 static void	igphy_status(struct mii_softc *);
110 
111 static const struct mii_phy_funcs igphy_funcs = {
112 	igphy_service, igphy_status, igphy_reset,
113 };
114 
115 static const struct mii_phydesc igphys[] = {
116 	{ MII_OUI_yyINTEL,		MII_MODEL_yyINTEL_IGP01E1000,
117 	  MII_STR_yyINTEL_IGP01E1000 },
118 
119 	{ MII_OUI_yyINTEL,		MII_MODEL_yyINTEL_I82566,
120 	  MII_STR_yyINTEL_I82566 },
121 
122 	{0,				0,
123 	 NULL },
124 };
125 
126 static int
127 igphymatch(device_t parent, cfdata_t match, void *aux)
128 {
129 	struct mii_attach_args *ma = aux;
130 
131 	if (mii_phy_match(ma, igphys) != NULL)
132 		return 10;
133 
134 	return 0;
135 }
136 
137 static void
138 igphyattach(device_t parent, device_t self, void *aux)
139 {
140 	struct mii_softc *sc = device_private(self);
141 	struct mii_attach_args *ma = aux;
142 	struct mii_data *mii = ma->mii_data;
143 	const struct mii_phydesc *mpd;
144 
145 	mpd = mii_phy_match(ma, igphys);
146 	aprint_naive(": Media interface\n");
147 	aprint_normal(": %s, rev. %d\n", mpd->mpd_name, MII_REV(ma->mii_id2));
148 
149 	sc->mii_dev = self;
150 	sc->mii_inst = mii->mii_instance;
151 	sc->mii_phy = ma->mii_phyno;
152 	sc->mii_funcs = &igphy_funcs;
153 	sc->mii_pdata = mii;
154 	sc->mii_flags = ma->mii_flags;
155 	sc->mii_anegticks = MII_ANEGTICKS_GIGE;
156 
157 	PHY_RESET(sc);
158 
159 	sc->mii_capabilities =
160 	    PHY_READ(sc, MII_BMSR) & ma->mii_capmask;
161 	if (sc->mii_capabilities & BMSR_EXTSTAT)
162 		sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR);
163 	aprint_normal_dev(self, "");
164 	if ((sc->mii_capabilities & BMSR_MEDIAMASK) == 0 &&
165 	    (sc->mii_extcapabilities & EXTSR_MEDIAMASK) == 0)
166 		aprint_error("no media present");
167 	else
168 		mii_phy_add_media(sc);
169 	aprint_normal("\n");
170 
171 	if (!pmf_device_register(self, NULL, mii_phy_resume))
172 		aprint_error_dev(self, "couldn't establish power handler\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 		reg = PHY_READ(sc, MII_IGPHY_PORT_CTRL);
275 		if (IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO) {
276 			reg |= PSCR_AUTO_MDIX;
277 			reg &= ~PSCR_FORCE_MDI_MDIX;
278 			PHY_WRITE(sc, MII_IGPHY_PORT_CTRL, reg);
279 		} else {
280 			reg &= ~(PSCR_AUTO_MDIX | PSCR_FORCE_MDI_MDIX);
281 			PHY_WRITE(sc, MII_IGPHY_PORT_CTRL, reg);
282 		}
283 
284 		mii_phy_setmedia(sc);
285 		break;
286 
287 	case MII_TICK:
288 		/*
289 		 * If we're not currently selected, just return.
290 		 */
291 		if (IFM_INST(ife->ifm_media) != sc->mii_inst)
292 			return (0);
293 
294 		igphy_smartspeed_workaround(sc);
295 
296 		if (mii_phy_tick(sc) == EJUSTRETURN)
297 			return (0);
298 		break;
299 
300 	case MII_DOWN:
301 		mii_phy_down(sc);
302 		return (0);
303 	}
304 
305 	/* Update the media status. */
306 	mii_phy_status(sc);
307 
308 	/* Callback if something changed. */
309 	mii_phy_update(sc, cmd);
310 	return (0);
311 }
312 
313 
314 static void
315 igphy_status(struct mii_softc *sc)
316 {
317 	struct mii_data *mii = sc->mii_pdata;
318 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
319 	uint16_t bmcr, pssr, gtsr, bmsr;
320 
321 	mii->mii_media_status = IFM_AVALID;
322 	mii->mii_media_active = IFM_ETHER;
323 
324 	pssr = PHY_READ(sc, MII_IGPHY_PORT_STATUS);
325 
326 	if (pssr & PSSR_LINK_UP)
327 		mii->mii_media_status |= IFM_ACTIVE;
328 
329 	bmcr = PHY_READ(sc, MII_BMCR);
330 	if (bmcr & BMCR_ISO) {
331 		mii->mii_media_active |= IFM_NONE;
332 		mii->mii_media_status = 0;
333 		return;
334 	}
335 
336 	if (bmcr & BMCR_LOOP)
337 		mii->mii_media_active |= IFM_LOOP;
338 
339 	bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
340 
341 	/*
342 	 * XXX can't check if the info is valid, no
343 	 * 'negotiation done' bit?
344 	 */
345 	if (bmcr & BMCR_AUTOEN) {
346 		if ((bmsr & BMSR_ACOMP) == 0) {
347 			mii->mii_media_active |= IFM_NONE;
348 			return;
349 		}
350 		switch (pssr & PSSR_SPEED_MASK) {
351 		case PSSR_SPEED_1000MBPS:
352 			mii->mii_media_active |= IFM_1000_T;
353 			gtsr = PHY_READ(sc, MII_100T2SR);
354 			if (gtsr & GTSR_MS_RES)
355 				mii->mii_media_active |= IFM_ETH_MASTER;
356 			break;
357 
358 		case PSSR_SPEED_100MBPS:
359 			mii->mii_media_active |= IFM_100_TX;
360 			break;
361 
362 		case PSSR_SPEED_10MBPS:
363 			mii->mii_media_active |= IFM_10_T;
364 			break;
365 
366 		default:
367 			mii->mii_media_active |= IFM_NONE;
368 			mii->mii_media_status = 0;
369 			return;
370 		}
371 
372 		if (pssr & PSSR_FULL_DUPLEX)
373 			mii->mii_media_active |=
374 			    IFM_FDX | mii_phy_flowstatus(sc);
375 	} else
376 		mii->mii_media_active = ife->ifm_media;
377 }
378 
379 static void
380 igphy_smartspeed_workaround(struct mii_softc *sc)
381 {
382 	struct igphy_softc *igsc = (struct igphy_softc *) sc;
383 	uint16_t reg, gtsr, gtcr;
384 
385 	if ((PHY_READ(sc, MII_BMCR) & BMCR_AUTOEN) == 0)
386 		return;
387 
388 	/* XXX Assume 1000TX-FDX is advertized if doing autonegotiation. */
389 
390 	reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
391 	if ((reg & BMSR_LINK) == 0) {
392 		switch (igsc->sc_smartspeed) {
393 		case 0:
394 			gtsr = PHY_READ(sc, MII_100T2SR);
395 			if (!(gtsr & GTSR_MAN_MS_FLT))
396 				break;
397 			gtsr = PHY_READ(sc, MII_100T2SR);
398 			if (gtsr & GTSR_MAN_MS_FLT) {
399 				gtcr = PHY_READ(sc, MII_100T2CR);
400 				if (gtcr & GTCR_MAN_MS) {
401 					gtcr &= ~GTCR_MAN_MS;
402 					PHY_WRITE(sc, MII_100T2CR,
403 					    gtcr);
404 				}
405 				mii_phy_auto(sc, 0);
406 			}
407 			break;
408 		case IGPHY_TICK_DOWNSHIFT:
409 			gtcr = PHY_READ(sc, MII_100T2CR);
410 			gtcr |= GTCR_MAN_MS;
411 			PHY_WRITE(sc, MII_100T2CR, gtcr);
412 			mii_phy_auto(sc, 0);
413 			break;
414 		default:
415 			break;
416 		}
417 		if (igsc->sc_smartspeed++ == IGPHY_TICK_MAX)
418 			igsc->sc_smartspeed = 0;
419 	} else
420 		igsc->sc_smartspeed = 0;
421 }
422