igphy.c revision 1.19 1 /* $NetBSD: igphy.c,v 1.19 2009/12/16 04:50:35 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.19 2009/12/16 04:50:35 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 };
98
99 static void igphy_reset(struct mii_softc *);
100 static void igphy_load_dspcode(struct mii_softc *);
101 static void igphy_smartspeed_workaround(struct mii_softc *sc);
102
103 static int igphymatch(device_t, cfdata_t, void *);
104 static void igphyattach(device_t, device_t, void *);
105
106 CFATTACH_DECL_NEW(igphy, sizeof(struct igphy_softc),
107 igphymatch, igphyattach, mii_phy_detach, mii_phy_activate);
108
109 static int igphy_service(struct mii_softc *, struct mii_data *, int);
110 static void igphy_status(struct mii_softc *);
111
112 static const struct mii_phy_funcs igphy_funcs = {
113 igphy_service, igphy_status, igphy_reset,
114 };
115
116 static const struct mii_phydesc igphys[] = {
117 { MII_OUI_yyINTEL, MII_MODEL_yyINTEL_IGP01E1000,
118 MII_STR_yyINTEL_IGP01E1000 },
119
120 { MII_OUI_yyINTEL, MII_MODEL_yyINTEL_I82566,
121 MII_STR_yyINTEL_I82566 },
122
123 {0, 0,
124 NULL },
125 };
126
127 static int
128 igphymatch(device_t parent, cfdata_t match, void *aux)
129 {
130 struct mii_attach_args *ma = aux;
131
132 if (mii_phy_match(ma, igphys) != NULL)
133 return 10;
134
135 return 0;
136 }
137
138 static void
139 igphyattach(device_t parent, device_t self, void *aux)
140 {
141 struct mii_softc *sc = device_private(self);
142 struct mii_attach_args *ma = aux;
143 struct mii_data *mii = ma->mii_data;
144 const struct mii_phydesc *mpd;
145 struct igphy_softc *igsc = (struct igphy_softc *) sc;
146 prop_dictionary_t dict;
147
148 mpd = mii_phy_match(ma, igphys);
149 aprint_naive(": Media interface\n");
150 aprint_normal(": %s, rev. %d\n", mpd->mpd_name, MII_REV(ma->mii_id2));
151
152 dict = device_properties(parent);
153 if (!prop_dictionary_get_uint32(dict, "mactype", &igsc->sc_mactype))
154 aprint_error("WARNING! Failed to get mactype\n");
155
156 sc->mii_dev = self;
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_dev(self, "");
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 struct igphy_softc *igsc = (struct igphy_softc *) sc;
183 static const struct {
184 int reg;
185 uint16_t val;
186 } dspcode[] = {
187 { 0x1f95, 0x0001 },
188 { 0x1f71, 0xbd21 },
189 { 0x1f79, 0x0018 },
190 { 0x1f30, 0x1600 },
191 { 0x1f31, 0x0014 },
192 { 0x1f32, 0x161c },
193 { 0x1f94, 0x0003 },
194 { 0x1f96, 0x003f },
195 { 0x2010, 0x0008 },
196 { 0, 0 },
197 };
198 int i;
199
200 /* This workaround is only for 82541 and 82547 */
201 switch (igsc->sc_mactype) {
202 case WM_T_82541:
203 case WM_T_82547:
204 case WM_T_82541_2:
205 case WM_T_82547_2:
206 break;
207 default:
208 /* byebye */
209 return;
210 }
211
212 delay(10);
213
214 PHY_WRITE(sc, MII_IGPHY_PAGE_SELECT, 0x0000);
215 PHY_WRITE(sc, 0x0000, 0x0140);
216
217 delay(5);
218
219 switch (igsc->sc_mactype) {
220 case WM_T_82541:
221 case WM_T_82547:
222 for (i = 0; dspcode[i].reg != 0; i++)
223 IGPHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
224 break;
225 case WM_T_82541_2:
226 case WM_T_82547_2:
227 IGPHY_WRITE(sc, 0x1f73, 0x0099);
228 break;
229 default:
230 break;
231 }
232
233 PHY_WRITE(sc, MII_IGPHY_PAGE_SELECT,0x0000);
234 PHY_WRITE(sc, 0x0000, 0x3300);
235 }
236
237 static void
238 igphy_reset(struct mii_softc *sc)
239 {
240 struct igphy_softc *igsc = (struct igphy_softc *) sc;
241 uint16_t fused, fine, coarse;
242
243 mii_phy_reset(sc);
244 igphy_load_dspcode(sc);
245
246 if (igsc->sc_mactype == WM_T_82547) {
247 fused = IGPHY_READ(sc, MII_IGPHY_ANALOG_SPARE_FUSE_STATUS);
248 if ((fused & ANALOG_SPARE_FUSE_ENABLED) == 0) {
249 fused = IGPHY_READ(sc, MII_IGPHY_ANALOG_FUSE_STATUS);
250
251 fine = fused & ANALOG_FUSE_FINE_MASK;
252 coarse = fused & ANALOG_FUSE_COARSE_MASK;
253
254 if (coarse > ANALOG_FUSE_COARSE_THRESH) {
255 coarse -= ANALOG_FUSE_COARSE_10;
256 fine -= ANALOG_FUSE_FINE_1;
257 } else if (coarse == ANALOG_FUSE_COARSE_THRESH)
258 fine -= ANALOG_FUSE_FINE_10;
259
260 fused = (fused & ANALOG_FUSE_POLY_MASK) |
261 (fine & ANALOG_FUSE_FINE_MASK) |
262 (coarse & ANALOG_FUSE_COARSE_MASK);
263
264 IGPHY_WRITE(sc, MII_IGPHY_ANALOG_FUSE_CONTROL, fused);
265 IGPHY_WRITE(sc, MII_IGPHY_ANALOG_FUSE_BYPASS,
266 ANALOG_FUSE_ENABLE_SW_CONTROL);
267 }
268 }
269 PHY_WRITE(sc, MII_IGPHY_PAGE_SELECT,0x0000);
270 }
271
272
273 static int
274 igphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
275 {
276 struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
277 uint16_t reg;
278
279 switch (cmd) {
280 case MII_POLLSTAT:
281 /*
282 * If we're not polling our PHY instance, just return.
283 */
284 if (IFM_INST(ife->ifm_media) != sc->mii_inst)
285 return (0);
286 break;
287
288 case MII_MEDIACHG:
289 /*
290 * If the media indicates a different PHY instance,
291 * isolate ourselves.
292 */
293 if (IFM_INST(ife->ifm_media) != sc->mii_inst) {
294 reg = PHY_READ(sc, MII_BMCR);
295 PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO);
296 return (0);
297 }
298
299 /*
300 * If the interface is not up, don't do anything.
301 */
302 if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
303 break;
304
305 reg = PHY_READ(sc, MII_IGPHY_PORT_CTRL);
306 if (IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO) {
307 reg |= PSCR_AUTO_MDIX;
308 reg &= ~PSCR_FORCE_MDI_MDIX;
309 PHY_WRITE(sc, MII_IGPHY_PORT_CTRL, reg);
310 } else {
311 reg &= ~(PSCR_AUTO_MDIX | PSCR_FORCE_MDI_MDIX);
312 PHY_WRITE(sc, MII_IGPHY_PORT_CTRL, reg);
313 }
314
315 mii_phy_setmedia(sc);
316 break;
317
318 case MII_TICK:
319 /*
320 * If we're not currently selected, just return.
321 */
322 if (IFM_INST(ife->ifm_media) != sc->mii_inst)
323 return (0);
324
325 igphy_smartspeed_workaround(sc);
326
327 if (mii_phy_tick(sc) == EJUSTRETURN)
328 return (0);
329 break;
330
331 case MII_DOWN:
332 mii_phy_down(sc);
333 return (0);
334 }
335
336 /* Update the media status. */
337 mii_phy_status(sc);
338
339 /* Callback if something changed. */
340 mii_phy_update(sc, cmd);
341 return (0);
342 }
343
344
345 static void
346 igphy_status(struct mii_softc *sc)
347 {
348 struct mii_data *mii = sc->mii_pdata;
349 struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
350 uint16_t bmcr, pssr, gtsr, bmsr;
351
352 mii->mii_media_status = IFM_AVALID;
353 mii->mii_media_active = IFM_ETHER;
354
355 pssr = PHY_READ(sc, MII_IGPHY_PORT_STATUS);
356
357 if (pssr & PSSR_LINK_UP)
358 mii->mii_media_status |= IFM_ACTIVE;
359
360 bmcr = PHY_READ(sc, MII_BMCR);
361 if (bmcr & BMCR_ISO) {
362 mii->mii_media_active |= IFM_NONE;
363 mii->mii_media_status = 0;
364 return;
365 }
366
367 if (bmcr & BMCR_LOOP)
368 mii->mii_media_active |= IFM_LOOP;
369
370 bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
371
372 /*
373 * XXX can't check if the info is valid, no
374 * 'negotiation done' bit?
375 */
376 if (bmcr & BMCR_AUTOEN) {
377 if ((bmsr & BMSR_ACOMP) == 0) {
378 mii->mii_media_active |= IFM_NONE;
379 return;
380 }
381 switch (pssr & PSSR_SPEED_MASK) {
382 case PSSR_SPEED_1000MBPS:
383 mii->mii_media_active |= IFM_1000_T;
384 gtsr = PHY_READ(sc, MII_100T2SR);
385 if (gtsr & GTSR_MS_RES)
386 mii->mii_media_active |= IFM_ETH_MASTER;
387 break;
388
389 case PSSR_SPEED_100MBPS:
390 mii->mii_media_active |= IFM_100_TX;
391 break;
392
393 case PSSR_SPEED_10MBPS:
394 mii->mii_media_active |= IFM_10_T;
395 break;
396
397 default:
398 mii->mii_media_active |= IFM_NONE;
399 mii->mii_media_status = 0;
400 return;
401 }
402
403 if (pssr & PSSR_FULL_DUPLEX)
404 mii->mii_media_active |=
405 IFM_FDX | mii_phy_flowstatus(sc);
406 } else
407 mii->mii_media_active = ife->ifm_media;
408 }
409
410 static void
411 igphy_smartspeed_workaround(struct mii_softc *sc)
412 {
413 struct igphy_softc *igsc = (struct igphy_softc *) sc;
414 uint16_t reg, gtsr, gtcr;
415
416
417 /* This workaround is only for 82541 and 82547 */
418 switch (igsc->sc_mactype) {
419 case WM_T_82541:
420 case WM_T_82541_2:
421 case WM_T_82547:
422 case WM_T_82547_2:
423 break;
424 default:
425 /* byebye */
426 return;
427 }
428
429 if ((PHY_READ(sc, MII_BMCR) & BMCR_AUTOEN) == 0)
430 return;
431
432 /* XXX Assume 1000TX-FDX is advertized if doing autonegotiation. */
433
434 reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
435 if ((reg & BMSR_LINK) == 0) {
436 switch (igsc->sc_smartspeed) {
437 case 0:
438 gtsr = PHY_READ(sc, MII_100T2SR);
439 if (!(gtsr & GTSR_MAN_MS_FLT))
440 break;
441 gtsr = PHY_READ(sc, MII_100T2SR);
442 if (gtsr & GTSR_MAN_MS_FLT) {
443 gtcr = PHY_READ(sc, MII_100T2CR);
444 if (gtcr & GTCR_MAN_MS) {
445 gtcr &= ~GTCR_MAN_MS;
446 PHY_WRITE(sc, MII_100T2CR,
447 gtcr);
448 }
449 mii_phy_auto(sc, 0);
450 }
451 break;
452 case IGPHY_TICK_DOWNSHIFT:
453 gtcr = PHY_READ(sc, MII_100T2CR);
454 gtcr |= GTCR_MAN_MS;
455 PHY_WRITE(sc, MII_100T2CR, gtcr);
456 mii_phy_auto(sc, 0);
457 break;
458 default:
459 break;
460 }
461 if (igsc->sc_smartspeed++ == IGPHY_TICK_MAX)
462 igsc->sc_smartspeed = 0;
463 } else
464 igsc->sc_smartspeed = 0;
465 }
466