igphy.c revision 1.14.4.2 1 /* $NetBSD: igphy.c,v 1.14.4.2 2009/05/04 08:12:52 yamt 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.14.4.2 2009/05/04 08:12:52 yamt 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