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