mii_physubr.c revision 1.19 1 /* $NetBSD: mii_physubr.c,v 1.19 2001/04/13 11:12:36 augustss Exp $ */
2
3 /*-
4 * Copyright (c) 1998, 1999, 2000 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
9 * NASA Ames Research Center.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the NetBSD
22 * Foundation, Inc. and its contributors.
23 * 4. Neither the name of The NetBSD Foundation nor the names of its
24 * contributors may be used to endorse or promote products derived
25 * from this software without specific prior written permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
28 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
31 * BE 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 * Subroutines common to all PHYs.
42 */
43
44 #include <sys/param.h>
45 #include <sys/device.h>
46 #include <sys/systm.h>
47 #include <sys/kernel.h>
48 #include <sys/socket.h>
49 #include <sys/errno.h>
50 #include <sys/proc.h>
51
52 #include <net/if.h>
53 #include <net/if_media.h>
54 #include <net/route.h>
55
56 #include <dev/mii/mii.h>
57 #include <dev/mii/miivar.h>
58
59 /*
60 * Media to register setting conversion table. Order matters.
61 */
62 const struct mii_media mii_media_table[] = {
63 { BMCR_ISO, ANAR_CSMA }, /* None */
64 { 0, ANAR_CSMA|ANAR_10 }, /* 10baseT */
65 { BMCR_FDX, ANAR_CSMA|ANAR_10_FD }, /* 10baseT-FDX */
66 { BMCR_S100, ANAR_CSMA|ANAR_T4 }, /* 100baseT4 */
67 { BMCR_S100, ANAR_CSMA|ANAR_TX }, /* 100baseTX */
68 { BMCR_S100|BMCR_FDX, ANAR_CSMA|ANAR_TX_FD }, /* 100baseTX-FDX */
69 };
70
71 void mii_phy_auto_timeout __P((void *));
72
73 void
74 mii_phy_setmedia(sc)
75 struct mii_softc *sc;
76 {
77 struct mii_data *mii = sc->mii_pdata;
78 struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
79 int bmcr, anar;
80
81 if (IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO) {
82 if ((PHY_READ(sc, MII_BMCR) & BMCR_AUTOEN) == 0)
83 (void) mii_phy_auto(sc, 1);
84 return;
85 }
86
87 /*
88 * Table index is stored in the media entry.
89 */
90
91 #ifdef DIAGNOSTIC
92 if (ife->ifm_data < 0 || ife->ifm_data >= MII_NMEDIA)
93 panic("mii_phy_setmedia");
94 #endif
95
96 anar = mii_media_table[ife->ifm_data].mm_anar;
97 bmcr = mii_media_table[ife->ifm_data].mm_bmcr;
98
99 if (ife->ifm_media & IFM_LOOP)
100 bmcr |= BMCR_LOOP;
101
102 PHY_WRITE(sc, MII_ANAR, anar);
103 PHY_WRITE(sc, MII_BMCR, bmcr);
104 }
105
106 int
107 mii_phy_auto(sc, waitfor)
108 struct mii_softc *sc;
109 int waitfor;
110 {
111 int bmsr, i;
112
113 if ((sc->mii_flags & MIIF_DOINGAUTO) == 0) {
114 PHY_WRITE(sc, MII_ANAR,
115 BMSR_MEDIA_TO_ANAR(sc->mii_capabilities) | ANAR_CSMA);
116 PHY_WRITE(sc, MII_BMCR, BMCR_AUTOEN | BMCR_STARTNEG);
117 }
118
119 if (waitfor) {
120 /* Wait 500ms for it to complete. */
121 for (i = 0; i < 500; i++) {
122 if ((bmsr = PHY_READ(sc, MII_BMSR)) & BMSR_ACOMP)
123 return (0);
124 delay(1000);
125 }
126
127 /*
128 * Don't need to worry about clearing MIIF_DOINGAUTO.
129 * If that's set, a timeout is pending, and it will
130 * clear the flag.
131 */
132 return (EIO);
133 }
134
135 /*
136 * Just let it finish asynchronously. This is for the benefit of
137 * the tick handler driving autonegotiation. Don't want 500ms
138 * delays all the time while the system is running!
139 */
140 if (sc->mii_flags & MIIF_AUTOTSLEEP) {
141 sc->mii_flags |= MIIF_DOINGAUTO;
142 tsleep(&sc->mii_flags, PZERO, "miiaut", hz >> 1);
143 mii_phy_auto_timeout(sc);
144 } else if ((sc->mii_flags & MIIF_DOINGAUTO) == 0) {
145 sc->mii_flags |= MIIF_DOINGAUTO;
146 callout_reset(&sc->mii_nway_ch, hz >> 1,
147 mii_phy_auto_timeout, sc);
148 }
149 return (EJUSTRETURN);
150 }
151
152 void
153 mii_phy_auto_timeout(arg)
154 void *arg;
155 {
156 struct mii_softc *sc = arg;
157 int s, bmsr;
158
159 if ((sc->mii_dev.dv_flags & DVF_ACTIVE) == 0)
160 return;
161
162 s = splnet();
163 sc->mii_flags &= ~MIIF_DOINGAUTO;
164 bmsr = PHY_READ(sc, MII_BMSR);
165
166 /* Update the media status. */
167 (void) PHY_SERVICE(sc, sc->mii_pdata, MII_POLLSTAT);
168 splx(s);
169 }
170
171 int
172 mii_phy_tick(sc)
173 struct mii_softc *sc;
174 {
175 struct mii_data *mii = sc->mii_pdata;
176 struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
177 int reg;
178
179 /* Just bail now if the interface is down. */
180 if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
181 return (EJUSTRETURN);
182
183 /*
184 * If we're not doing autonegotiation, we don't need to do
185 * any extra work here. However, we need to check the link
186 * status so we can generate an announcement if the status
187 * changes.
188 */
189 if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO)
190 return (0);
191
192 /* Read the status register twice; BMSR_LINK is latch-low. */
193 reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
194 if (reg & BMSR_LINK) {
195 /*
196 * See above.
197 */
198 return (0);
199 }
200
201 /*
202 * Only retry autonegotiation every 5 seconds.
203 */
204 if (++sc->mii_ticks != 5)
205 return (EJUSTRETURN);
206
207 sc->mii_ticks = 0;
208 PHY_RESET(sc);
209
210 if (mii_phy_auto(sc, 0) == EJUSTRETURN)
211 return (EJUSTRETURN);
212
213 /*
214 * Might need to generate a status message if autonegotiation
215 * failed.
216 */
217 return (0);
218 }
219
220 void
221 mii_phy_reset(sc)
222 struct mii_softc *sc;
223 {
224 int reg, i;
225
226 if (sc->mii_flags & MIIF_NOISOLATE)
227 reg = BMCR_RESET;
228 else
229 reg = BMCR_RESET | BMCR_ISO;
230 PHY_WRITE(sc, MII_BMCR, reg);
231
232 /* Wait 100ms for it to complete. */
233 for (i = 0; i < 100; i++) {
234 reg = PHY_READ(sc, MII_BMCR);
235 if ((reg & BMCR_RESET) == 0)
236 break;
237 delay(1000);
238 }
239
240 if (sc->mii_inst != 0 && ((sc->mii_flags & MIIF_NOISOLATE) == 0))
241 PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO);
242 }
243
244 void
245 mii_phy_down(sc)
246 struct mii_softc *sc;
247 {
248
249 if (sc->mii_flags & MIIF_DOINGAUTO) {
250 sc->mii_flags &= ~MIIF_DOINGAUTO;
251 callout_stop(&sc->mii_nway_ch);
252 }
253 }
254
255 void
256 mii_phy_status(sc)
257 struct mii_softc *sc;
258 {
259
260 PHY_STATUS(sc);
261 }
262
263 void
264 mii_phy_update(sc, cmd)
265 struct mii_softc *sc;
266 int cmd;
267 {
268 struct mii_data *mii = sc->mii_pdata;
269
270 if (sc->mii_media_active != mii->mii_media_active ||
271 sc->mii_media_status != mii->mii_media_status ||
272 cmd == MII_MEDIACHG) {
273 (*mii->mii_statchg)(sc->mii_dev.dv_parent);
274 mii_phy_statusmsg(sc);
275 sc->mii_media_active = mii->mii_media_active;
276 sc->mii_media_status = mii->mii_media_status;
277 }
278 }
279
280 void
281 mii_phy_statusmsg(sc)
282 struct mii_softc *sc;
283 {
284 struct mii_data *mii = sc->mii_pdata;
285 struct ifnet *ifp = mii->mii_ifp;
286 int s, baudrate, link_state, announce = 0;
287
288 if (mii->mii_media_status & IFM_AVALID) {
289 if (mii->mii_media_status & IFM_ACTIVE)
290 link_state = LINK_STATE_UP;
291 else
292 link_state = LINK_STATE_DOWN;
293 } else
294 link_state = LINK_STATE_UNKNOWN;
295
296 baudrate = ifmedia_baudrate(mii->mii_media_active);
297
298 if (link_state != ifp->if_link_state) {
299 ifp->if_link_state = link_state;
300 /*
301 * XXX Right here we'd like to notify protocols
302 * XXX that the link status has changed, so that
303 * XXX e.g. Duplicate Address Detection can restart.
304 */
305 announce = 1;
306 }
307
308 if (baudrate != ifp->if_baudrate) {
309 ifp->if_baudrate = baudrate;
310 announce = 1;
311 }
312
313 if (announce) {
314 s = splimp(); /* XXX Should be splnet() */
315 rt_ifmsg(ifp);
316 splx(s);
317 }
318 }
319
320 /*
321 * Initialize generic PHY media based on BMSR, called when a PHY is
322 * attached. We expect to be set up to print a comma-separated list
323 * of media names. Does not print a newline.
324 */
325 void
326 mii_phy_add_media(sc)
327 struct mii_softc *sc;
328 {
329 struct mii_data *mii = sc->mii_pdata;
330 const char *sep = "";
331
332 #define ADD(m, c) ifmedia_add(&mii->mii_media, (m), (c), NULL)
333 #define PRINT(s) printf("%s%s", sep, s); sep = ", "
334
335 if ((sc->mii_flags & MIIF_NOISOLATE) == 0)
336 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_NONE, 0, sc->mii_inst),
337 MII_MEDIA_NONE);
338
339 if (sc->mii_capabilities & BMSR_10THDX) {
340 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, 0, sc->mii_inst),
341 MII_MEDIA_10_T);
342 #if 0
343 if ((sc->mii_flags & MIIF_NOLOOP) == 0)
344 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, IFM_LOOP,
345 sc->mii_inst), MII_MEDIA_10_T);
346 #endif
347 PRINT("10baseT");
348 }
349 if (sc->mii_capabilities & BMSR_10TFDX) {
350 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, IFM_FDX, sc->mii_inst),
351 MII_MEDIA_10_T_FDX);
352 PRINT("10baseT-FDX");
353 }
354 if (sc->mii_capabilities & BMSR_100TXHDX) {
355 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, 0, sc->mii_inst),
356 MII_MEDIA_100_TX);
357 #if 0
358 if ((sc->mii_flags & MIIF_NOLOOP) == 0)
359 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_LOOP,
360 sc->mii_inst), MII_MEDIA_100_TX);
361 #endif
362 PRINT("100baseTX");
363 }
364 if (sc->mii_capabilities & BMSR_100TXFDX) {
365 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_FDX, sc->mii_inst),
366 MII_MEDIA_100_TX_FDX);
367 PRINT("100baseTX-FDX");
368 }
369 if (sc->mii_capabilities & BMSR_100T4) {
370 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_T4, 0, sc->mii_inst),
371 MII_MEDIA_100_T4);
372 #if 0
373 if ((sc->mii_flags & MIIF_NOLOOP) == 0)
374 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_T4, IFM_LOOP,
375 sc->mii_inst), MII_MEDIA_100_T4);
376 #endif
377 PRINT("100baseT4");
378 }
379 if (sc->mii_capabilities & BMSR_ANEG) {
380 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, sc->mii_inst),
381 MII_NMEDIA); /* intentionally invalid index */
382 PRINT("auto");
383 }
384 #undef ADD
385 #undef PRINT
386 }
387
388 void
389 mii_phy_delete_media(sc)
390 struct mii_softc *sc;
391 {
392 struct mii_data *mii = sc->mii_pdata;
393
394 ifmedia_delete_instance(&mii->mii_media, sc->mii_inst);
395 }
396
397 int
398 mii_phy_activate(self, act)
399 struct device *self;
400 enum devact act;
401 {
402 int rv = 0;
403
404 switch (act) {
405 case DVACT_ACTIVATE:
406 rv = EOPNOTSUPP;
407 break;
408
409 case DVACT_DEACTIVATE:
410 /* Nothing special to do. */
411 break;
412 }
413
414 return (rv);
415 }
416
417 int
418 mii_phy_detach(self, flags)
419 struct device *self;
420 int flags;
421 {
422 struct mii_softc *sc = (void *) self;
423
424 if (sc->mii_flags & MIIF_DOINGAUTO)
425 callout_stop(&sc->mii_nway_ch);
426
427 mii_phy_delete_media(sc);
428
429 return (0);
430 }
431