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