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