mii_physubr.c revision 1.17 1 /* $NetBSD: mii_physubr.c,v 1.17 2000/03/23 07:01:36 thorpej 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
51 #include <net/if.h>
52 #include <net/if_media.h>
53 #include <net/route.h>
54
55 #include <dev/mii/mii.h>
56 #include <dev/mii/miivar.h>
57
58 /*
59 * Media to register setting conversion table. Order matters.
60 */
61 const struct mii_media mii_media_table[] = {
62 { BMCR_ISO, ANAR_CSMA }, /* None */
63 { 0, ANAR_CSMA|ANAR_10 }, /* 10baseT */
64 { BMCR_FDX, ANAR_CSMA|ANAR_10_FD }, /* 10baseT-FDX */
65 { BMCR_S100, ANAR_CSMA|ANAR_T4 }, /* 100baseT4 */
66 { BMCR_S100, ANAR_CSMA|ANAR_TX }, /* 100baseTX */
67 { BMCR_S100|BMCR_FDX, ANAR_CSMA|ANAR_TX_FD }, /* 100baseTX-FDX */
68 };
69
70 void mii_phy_auto_timeout __P((void *));
71
72 void
73 mii_phy_setmedia(sc)
74 struct mii_softc *sc;
75 {
76 struct mii_data *mii = sc->mii_pdata;
77 struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
78 int bmcr, anar;
79
80 if (IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO) {
81 if ((PHY_READ(sc, MII_BMCR) & BMCR_AUTOEN) == 0)
82 (void) mii_phy_auto(sc, 1);
83 return;
84 }
85
86 /*
87 * Table index is stored in the media entry.
88 */
89
90 #ifdef DIAGNOSTIC
91 if (ife->ifm_data < 0 || ife->ifm_data >= MII_NMEDIA)
92 panic("mii_phy_setmedia");
93 #endif
94
95 anar = mii_media_table[ife->ifm_data].mm_anar;
96 bmcr = mii_media_table[ife->ifm_data].mm_bmcr;
97
98 if (ife->ifm_media & IFM_LOOP)
99 bmcr |= BMCR_LOOP;
100
101 PHY_WRITE(sc, MII_ANAR, anar);
102 PHY_WRITE(sc, MII_BMCR, bmcr);
103 }
104
105 int
106 mii_phy_auto(sc, waitfor)
107 struct mii_softc *sc;
108 int waitfor;
109 {
110 int bmsr, i;
111
112 if ((sc->mii_flags & MIIF_DOINGAUTO) == 0) {
113 PHY_WRITE(sc, MII_ANAR,
114 BMSR_MEDIA_TO_ANAR(sc->mii_capabilities) | ANAR_CSMA);
115 PHY_WRITE(sc, MII_BMCR, BMCR_AUTOEN | BMCR_STARTNEG);
116 }
117
118 if (waitfor) {
119 /* Wait 500ms for it to complete. */
120 for (i = 0; i < 500; i++) {
121 if ((bmsr = PHY_READ(sc, MII_BMSR)) & BMSR_ACOMP)
122 return (0);
123 delay(1000);
124 }
125
126 /*
127 * Don't need to worry about clearing MIIF_DOINGAUTO.
128 * If that's set, a timeout is pending, and it will
129 * clear the flag.
130 */
131 return (EIO);
132 }
133
134 /*
135 * Just let it finish asynchronously. This is for the benefit of
136 * the tick handler driving autonegotiation. Don't want 500ms
137 * delays all the time while the system is running!
138 */
139 if ((sc->mii_flags & MIIF_DOINGAUTO) == 0) {
140 sc->mii_flags |= MIIF_DOINGAUTO;
141 callout_reset(&sc->mii_nway_ch, hz >> 1,
142 mii_phy_auto_timeout, sc);
143 }
144 return (EJUSTRETURN);
145 }
146
147 void
148 mii_phy_auto_timeout(arg)
149 void *arg;
150 {
151 struct mii_softc *sc = arg;
152 int s, bmsr;
153
154 if ((sc->mii_dev.dv_flags & DVF_ACTIVE) == 0)
155 return;
156
157 s = splnet();
158 sc->mii_flags &= ~MIIF_DOINGAUTO;
159 bmsr = PHY_READ(sc, MII_BMSR);
160
161 /* Update the media status. */
162 (void) (*sc->mii_service)(sc, sc->mii_pdata, MII_POLLSTAT);
163 splx(s);
164 }
165
166 int
167 mii_phy_tick(sc)
168 struct mii_softc *sc;
169 {
170 struct mii_data *mii = sc->mii_pdata;
171 struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
172 int reg;
173
174 /* Just bail now if the interface is down. */
175 if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
176 return (EJUSTRETURN);
177
178 /*
179 * If we're not doing autonegotiation, we don't need to do
180 * any extra work here. However, we need to check the link
181 * status so we can generate an announcement if the status
182 * changes.
183 */
184 if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO)
185 return (0);
186
187 /* Read the status register twice; BMSR_LINK is latch-low. */
188 reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
189 if (reg & BMSR_LINK) {
190 /*
191 * See above.
192 */
193 return (0);
194 }
195
196 /*
197 * Only retry autonegotiation every 5 seconds.
198 */
199 if (++sc->mii_ticks != 5)
200 return (EJUSTRETURN);
201
202 sc->mii_ticks = 0;
203 mii_phy_reset(sc);
204
205 if (mii_phy_auto(sc, 0) == EJUSTRETURN)
206 return (EJUSTRETURN);
207
208 /*
209 * Might need to generate a status message if autonegotiation
210 * failed.
211 */
212 return (0);
213 }
214
215 void
216 mii_phy_reset(sc)
217 struct mii_softc *sc;
218 {
219 int reg, i;
220
221 if (sc->mii_flags & MIIF_NOISOLATE)
222 reg = BMCR_RESET;
223 else
224 reg = BMCR_RESET | BMCR_ISO;
225 PHY_WRITE(sc, MII_BMCR, reg);
226
227 /* Wait 100ms for it to complete. */
228 for (i = 0; i < 100; i++) {
229 reg = PHY_READ(sc, MII_BMCR);
230 if ((reg & BMCR_RESET) == 0)
231 break;
232 delay(1000);
233 }
234
235 if (sc->mii_inst != 0 && ((sc->mii_flags & MIIF_NOISOLATE) == 0))
236 PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO);
237 }
238
239 void
240 mii_phy_down(sc)
241 struct mii_softc *sc;
242 {
243
244 if (sc->mii_flags & MIIF_DOINGAUTO) {
245 sc->mii_flags &= ~MIIF_DOINGAUTO;
246 callout_stop(&sc->mii_nway_ch);
247 }
248 }
249
250 void
251 mii_phy_status(sc)
252 struct mii_softc *sc;
253 {
254
255 (*sc->mii_status)(sc);
256 }
257
258 void
259 mii_phy_update(sc, cmd)
260 struct mii_softc *sc;
261 int cmd;
262 {
263 struct mii_data *mii = sc->mii_pdata;
264
265 if (sc->mii_media_active != mii->mii_media_active ||
266 sc->mii_media_status != mii->mii_media_status ||
267 cmd == MII_MEDIACHG) {
268 (*mii->mii_statchg)(sc->mii_dev.dv_parent);
269 mii_phy_statusmsg(sc);
270 sc->mii_media_active = mii->mii_media_active;
271 sc->mii_media_status = mii->mii_media_status;
272 }
273 }
274
275 void
276 mii_phy_statusmsg(sc)
277 struct mii_softc *sc;
278 {
279 struct mii_data *mii = sc->mii_pdata;
280 struct ifnet *ifp = mii->mii_ifp;
281 int s, baudrate, link_state, announce = 0;
282
283 if (mii->mii_media_status & IFM_AVALID) {
284 if (mii->mii_media_status & IFM_ACTIVE)
285 link_state = LINK_STATE_UP;
286 else
287 link_state = LINK_STATE_DOWN;
288 } else
289 link_state = LINK_STATE_UNKNOWN;
290
291 baudrate = ifmedia_baudrate(mii->mii_media_active);
292
293 if (link_state != ifp->if_link_state) {
294 ifp->if_link_state = link_state;
295 /*
296 * XXX Right here we'd like to notify protocols
297 * XXX that the link status has changed, so that
298 * XXX e.g. Duplicate Address Detection can restart.
299 */
300 announce = 1;
301 }
302
303 if (baudrate != ifp->if_baudrate) {
304 ifp->if_baudrate = baudrate;
305 announce = 1;
306 }
307
308 if (announce) {
309 s = splimp(); /* XXX Should be splnet() */
310 rt_ifmsg(ifp);
311 splx(s);
312 }
313 }
314
315 /*
316 * Initialize generic PHY media based on BMSR, called when a PHY is
317 * attached. We expect to be set up to print a comma-separated list
318 * of media names. Does not print a newline.
319 */
320 void
321 mii_phy_add_media(sc)
322 struct mii_softc *sc;
323 {
324 struct mii_data *mii = sc->mii_pdata;
325 const char *sep = "";
326
327 #define ADD(m, c) ifmedia_add(&mii->mii_media, (m), (c), NULL)
328 #define PRINT(s) printf("%s%s", sep, s); sep = ", "
329
330 if ((sc->mii_flags & MIIF_NOISOLATE) == 0)
331 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_NONE, 0, sc->mii_inst),
332 MII_MEDIA_NONE);
333
334 if (sc->mii_capabilities & BMSR_10THDX) {
335 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, 0, sc->mii_inst),
336 MII_MEDIA_10_T);
337 #if 0
338 if ((sc->mii_flags & MIIF_NOLOOP) == 0)
339 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, IFM_LOOP,
340 sc->mii_inst), MII_MEDIA_10_T);
341 #endif
342 PRINT("10baseT");
343 }
344 if (sc->mii_capabilities & BMSR_10TFDX) {
345 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, IFM_FDX, sc->mii_inst),
346 MII_MEDIA_10_T_FDX);
347 PRINT("10baseT-FDX");
348 }
349 if (sc->mii_capabilities & BMSR_100TXHDX) {
350 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, 0, sc->mii_inst),
351 MII_MEDIA_100_TX);
352 #if 0
353 if ((sc->mii_flags & MIIF_NOLOOP) == 0)
354 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_LOOP,
355 sc->mii_inst), MII_MEDIA_100_TX);
356 #endif
357 PRINT("100baseTX");
358 }
359 if (sc->mii_capabilities & BMSR_100TXFDX) {
360 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_FDX, sc->mii_inst),
361 MII_MEDIA_100_TX_FDX);
362 PRINT("100baseTX-FDX");
363 }
364 if (sc->mii_capabilities & BMSR_100T4) {
365 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_T4, 0, sc->mii_inst),
366 MII_MEDIA_100_T4);
367 #if 0
368 if ((sc->mii_flags & MIIF_NOLOOP) == 0)
369 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_T4, IFM_LOOP,
370 sc->mii_inst), MII_MEDIA_100_T4);
371 #endif
372 PRINT("100baseT4");
373 }
374 if (sc->mii_capabilities & BMSR_ANEG) {
375 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, sc->mii_inst),
376 MII_NMEDIA); /* intentionally invalid index */
377 PRINT("auto");
378 }
379 #undef ADD
380 #undef PRINT
381 }
382
383 void
384 mii_phy_delete_media(sc)
385 struct mii_softc *sc;
386 {
387 struct mii_data *mii = sc->mii_pdata;
388
389 ifmedia_delete_instance(&mii->mii_media, sc->mii_inst);
390 }
391
392 int
393 mii_phy_activate(self, act)
394 struct device *self;
395 enum devact act;
396 {
397 int rv = 0;
398
399 switch (act) {
400 case DVACT_ACTIVATE:
401 rv = EOPNOTSUPP;
402 break;
403
404 case DVACT_DEACTIVATE:
405 /* Nothing special to do. */
406 break;
407 }
408
409 return (rv);
410 }
411
412 int
413 mii_phy_detach(self, flags)
414 struct device *self;
415 int flags;
416 {
417 struct mii_softc *sc = (void *) self;
418
419 if (sc->mii_flags & MIIF_DOINGAUTO)
420 callout_stop(&sc->mii_nway_ch);
421
422 mii_phy_delete_media(sc);
423
424 return (0);
425 }
426