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