igphy.c revision 1.13 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