mii_physubr.c revision 1.15 1 /* $NetBSD: mii_physubr.c,v 1.15 2000/03/06 20:56:57 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 timeout(mii_phy_auto_timeout, sc, hz >> 1);
142 }
143 return (EJUSTRETURN);
144 }
145
146 void
147 mii_phy_auto_timeout(arg)
148 void *arg;
149 {
150 struct mii_softc *sc = arg;
151 int s, bmsr;
152
153 if ((sc->mii_dev.dv_flags & DVF_ACTIVE) == 0)
154 return;
155
156 s = splnet();
157 sc->mii_flags &= ~MIIF_DOINGAUTO;
158 bmsr = PHY_READ(sc, MII_BMSR);
159
160 /* Update the media status. */
161 (void) (*sc->mii_service)(sc, sc->mii_pdata, MII_POLLSTAT);
162 splx(s);
163 }
164
165 int
166 mii_phy_tick(sc)
167 struct mii_softc *sc;
168 {
169 struct mii_data *mii = sc->mii_pdata;
170 struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
171 int reg;
172
173 /* Just bail now if the interface is down. */
174 if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
175 return (EJUSTRETURN);
176
177 /*
178 * If we're not doing autonegotiation, we don't need to do
179 * any extra work here. However, we need to check the link
180 * status so we can generate an announcement if the status
181 * changes.
182 */
183 if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO)
184 return (0);
185
186 /* Read the status register twice; BMSR_LINK is latch-low. */
187 reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
188 if (reg & BMSR_LINK) {
189 /*
190 * See above.
191 */
192 return (0);
193 }
194
195 /*
196 * Only retry autonegotiation every 5 seconds.
197 */
198 if (++sc->mii_ticks != 5)
199 return (EJUSTRETURN);
200
201 sc->mii_ticks = 0;
202 mii_phy_reset(sc);
203
204 if (mii_phy_auto(sc, 0) == EJUSTRETURN)
205 return (EJUSTRETURN);
206
207 /*
208 * Might need to generate a status message if autonegotiation
209 * failed.
210 */
211 return (0);
212 }
213
214 void
215 mii_phy_reset(sc)
216 struct mii_softc *sc;
217 {
218 int reg, i;
219
220 if (sc->mii_flags & MIIF_NOISOLATE)
221 reg = BMCR_RESET;
222 else
223 reg = BMCR_RESET | BMCR_ISO;
224 PHY_WRITE(sc, MII_BMCR, reg);
225
226 /* Wait 100ms for it to complete. */
227 for (i = 0; i < 100; i++) {
228 reg = PHY_READ(sc, MII_BMCR);
229 if ((reg & BMCR_RESET) == 0)
230 break;
231 delay(1000);
232 }
233
234 if (sc->mii_inst != 0 && ((sc->mii_flags & MIIF_NOISOLATE) == 0))
235 PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO);
236 }
237
238 void
239 mii_phy_down(sc)
240 struct mii_softc *sc;
241 {
242
243 if (sc->mii_flags & MIIF_DOINGAUTO) {
244 sc->mii_flags &= ~MIIF_DOINGAUTO;
245 untimeout(mii_phy_auto_timeout, sc);
246 }
247 }
248
249 void
250 mii_phy_status(sc)
251 struct mii_softc *sc;
252 {
253
254 (*sc->mii_status)(sc);
255 }
256
257 void
258 mii_phy_update(sc, cmd)
259 struct mii_softc *sc;
260 int cmd;
261 {
262 struct mii_data *mii = sc->mii_pdata;
263
264 if (sc->mii_media_active != mii->mii_media_active ||
265 sc->mii_media_status != mii->mii_media_status ||
266 cmd == MII_MEDIACHG) {
267 (*mii->mii_statchg)(sc->mii_dev.dv_parent);
268 mii_phy_statusmsg(sc);
269 sc->mii_media_active = mii->mii_media_active;
270 sc->mii_media_status = mii->mii_media_status;
271 }
272 }
273
274 void
275 mii_phy_statusmsg(sc)
276 struct mii_softc *sc;
277 {
278 struct mii_data *mii = sc->mii_pdata;
279 struct ifnet *ifp = mii->mii_ifp;
280 int baudrate, link_state, announce = 0;
281
282 if (mii->mii_media_status & IFM_AVALID) {
283 if (mii->mii_media_status & IFM_ACTIVE)
284 link_state = LINK_STATE_UP;
285 else
286 link_state = LINK_STATE_DOWN;
287 } else
288 link_state = LINK_STATE_UNKNOWN;
289
290 baudrate = ifmedia_baudrate(mii->mii_media_active);
291
292 if (link_state != ifp->if_link_state) {
293 ifp->if_link_state = link_state;
294 announce = 1;
295 }
296
297 if (baudrate != ifp->if_baudrate) {
298 ifp->if_baudrate = baudrate;
299 announce = 1;
300 }
301
302 if (announce)
303 rt_ifmsg(ifp);
304 }
305
306 /*
307 * Initialize generic PHY media based on BMSR, called when a PHY is
308 * attached. We expect to be set up to print a comma-separated list
309 * of media names. Does not print a newline.
310 */
311 void
312 mii_phy_add_media(sc)
313 struct mii_softc *sc;
314 {
315 struct mii_data *mii = sc->mii_pdata;
316 const char *sep = "";
317
318 #define ADD(m, c) ifmedia_add(&mii->mii_media, (m), (c), NULL)
319 #define PRINT(s) printf("%s%s", sep, s); sep = ", "
320
321 if ((sc->mii_flags & MIIF_NOISOLATE) == 0)
322 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_NONE, 0, sc->mii_inst),
323 MII_MEDIA_NONE);
324
325 if (sc->mii_capabilities & BMSR_10THDX) {
326 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, 0, sc->mii_inst),
327 MII_MEDIA_10_T);
328 #if 0
329 if ((sc->mii_flags & MIIF_NOLOOP) == 0)
330 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, IFM_LOOP,
331 sc->mii_inst), MII_MEDIA_10_T);
332 #endif
333 PRINT("10baseT");
334 }
335 if (sc->mii_capabilities & BMSR_10TFDX) {
336 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, IFM_FDX, sc->mii_inst),
337 MII_MEDIA_10_T_FDX);
338 PRINT("10baseT-FDX");
339 }
340 if (sc->mii_capabilities & BMSR_100TXHDX) {
341 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, 0, sc->mii_inst),
342 MII_MEDIA_100_TX);
343 #if 0
344 if ((sc->mii_flags & MIIF_NOLOOP) == 0)
345 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_LOOP,
346 sc->mii_inst), MII_MEDIA_100_TX);
347 #endif
348 PRINT("100baseTX");
349 }
350 if (sc->mii_capabilities & BMSR_100TXFDX) {
351 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_FDX, sc->mii_inst),
352 MII_MEDIA_100_TX_FDX);
353 PRINT("100baseTX-FDX");
354 }
355 if (sc->mii_capabilities & BMSR_100T4) {
356 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_T4, 0, sc->mii_inst),
357 MII_MEDIA_100_T4);
358 #if 0
359 if ((sc->mii_flags & MIIF_NOLOOP) == 0)
360 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_T4, IFM_LOOP,
361 sc->mii_inst), MII_MEDIA_100_T4);
362 #endif
363 PRINT("100baseT4");
364 }
365 if (sc->mii_capabilities & BMSR_ANEG) {
366 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, sc->mii_inst),
367 MII_NMEDIA); /* intentionally invalid index */
368 PRINT("auto");
369 }
370 #undef ADD
371 #undef PRINT
372 }
373
374 void
375 mii_phy_delete_media(sc)
376 struct mii_softc *sc;
377 {
378 struct mii_data *mii = sc->mii_pdata;
379
380 ifmedia_delete_instance(&mii->mii_media, sc->mii_inst);
381 }
382
383 int
384 mii_phy_activate(self, act)
385 struct device *self;
386 enum devact act;
387 {
388 int rv = 0;
389
390 switch (act) {
391 case DVACT_ACTIVATE:
392 rv = EOPNOTSUPP;
393 break;
394
395 case DVACT_DEACTIVATE:
396 /* Nothing special to do. */
397 break;
398 }
399
400 return (rv);
401 }
402
403 int
404 mii_phy_detach(self, flags)
405 struct device *self;
406 int flags;
407 {
408 struct mii_softc *sc = (void *) self;
409
410 if (sc->mii_flags & MIIF_DOINGAUTO)
411 untimeout(mii_phy_auto_timeout, sc);
412
413 mii_phy_delete_media(sc);
414
415 return (0);
416 }
417