FreeBSD/Linux Kernel Cross Reference
sys/dev/mii/mii.c
1 /* $NetBSD: mii.c,v 1.12 1999/08/03 19:41:49 drochner Exp $ */
2
3 /*-
4 * Copyright (c) 1998 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 * MII bus layer, glues MII-capable network interface drivers to sharable
42 * PHY drivers. This exports an interface compatible with BSD/OS 3.0's,
43 * plus some NetBSD extensions.
44 */
45
46 #include <sys/param.h>
47 #include <sys/systm.h>
48 #include <sys/socket.h>
49 #include <sys/malloc.h>
50 #include <sys/module.h>
51 #include <sys/bus.h>
52
53 #include <net/if.h>
54 #include <net/if_media.h>
55
56 #include <dev/mii/mii.h>
57 #include <dev/mii/miivar.h>
58
59 MODULE_VERSION(miibus, 1);
60
61 #include "miibus_if.h"
62
63 #if !defined(lint)
64 static const char rcsid[] =
65 "$FreeBSD: releng/5.0/sys/dev/mii/mii.c 105135 2002-10-14 22:31:52Z alfred $";
66 #endif
67
68 static int miibus_readreg(device_t, int, int);
69 static int miibus_writereg(device_t, int, int, int);
70 static void miibus_statchg(device_t);
71 static void miibus_linkchg(device_t);
72 static void miibus_mediainit(device_t);
73
74 static device_method_t miibus_methods[] = {
75 /* device interface */
76 DEVMETHOD(device_probe, miibus_probe),
77 DEVMETHOD(device_attach, miibus_attach),
78 DEVMETHOD(device_detach, miibus_detach),
79 DEVMETHOD(device_shutdown, bus_generic_shutdown),
80
81 /* bus interface */
82 DEVMETHOD(bus_print_child, bus_generic_print_child),
83 DEVMETHOD(bus_driver_added, bus_generic_driver_added),
84
85 /* MII interface */
86 DEVMETHOD(miibus_readreg, miibus_readreg),
87 DEVMETHOD(miibus_writereg, miibus_writereg),
88 DEVMETHOD(miibus_statchg, miibus_statchg),
89 DEVMETHOD(miibus_linkchg, miibus_linkchg),
90 DEVMETHOD(miibus_mediainit, miibus_mediainit),
91
92 { 0, 0 }
93 };
94
95 devclass_t miibus_devclass;
96
97 driver_t miibus_driver = {
98 "miibus",
99 miibus_methods,
100 sizeof(struct mii_data)
101 };
102
103 /*
104 * Helper function used by network interface drivers, attaches PHYs
105 * to the network interface driver parent.
106 */
107
108 int
109 miibus_probe(dev)
110 device_t dev;
111 {
112 struct mii_attach_args ma, *args;
113 struct mii_data *mii;
114 device_t child = NULL, parent;
115 int bmsr, capmask = 0xFFFFFFFF;
116
117 mii = device_get_softc(dev);
118 parent = device_get_parent(dev);
119 LIST_INIT(&mii->mii_phys);
120
121 for (ma.mii_phyno = 0; ma.mii_phyno < MII_NPHY; ma.mii_phyno++) {
122 /*
123 * Check to see if there is a PHY at this address. Note,
124 * many braindead PHYs report 0/0 in their ID registers,
125 * so we test for media in the BMSR.
126 */
127 bmsr = MIIBUS_READREG(parent, ma.mii_phyno, MII_BMSR);
128 if (bmsr == 0 || bmsr == 0xffff ||
129 (bmsr & BMSR_MEDIAMASK) == 0) {
130 /* Assume no PHY at this address. */
131 continue;
132 }
133
134 /*
135 * Extract the IDs. Braindead PHYs will be handled by
136 * the `ukphy' driver, as we have no ID information to
137 * match on.
138 */
139 ma.mii_id1 = MIIBUS_READREG(parent, ma.mii_phyno,
140 MII_PHYIDR1);
141 ma.mii_id2 = MIIBUS_READREG(parent, ma.mii_phyno,
142 MII_PHYIDR2);
143
144 ma.mii_data = mii;
145 ma.mii_capmask = capmask;
146
147 args = malloc(sizeof(struct mii_attach_args),
148 M_DEVBUF, M_NOWAIT);
149 bcopy((char *)&ma, (char *)args, sizeof(ma));
150 child = device_add_child(dev, NULL, -1);
151 device_set_ivars(child, args);
152 }
153
154 if (child == NULL)
155 return(ENXIO);
156
157 device_set_desc(dev, "MII bus");
158
159 return(0);
160 }
161
162 int
163 miibus_attach(dev)
164 device_t dev;
165 {
166 void **v;
167 ifm_change_cb_t ifmedia_upd;
168 ifm_stat_cb_t ifmedia_sts;
169 struct mii_data *mii;
170
171 mii = device_get_softc(dev);
172 /*
173 * Note that each NIC's softc must start with an ifnet structure.
174 */
175 mii->mii_ifp = device_get_softc(device_get_parent(dev));
176 v = device_get_ivars(dev);
177 ifmedia_upd = v[0];
178 ifmedia_sts = v[1];
179 ifmedia_init(&mii->mii_media, IFM_IMASK, ifmedia_upd, ifmedia_sts);
180 bus_generic_attach(dev);
181
182 return(0);
183 }
184
185 int
186 miibus_detach(dev)
187 device_t dev;
188 {
189 struct mii_data *mii;
190
191 bus_generic_detach(dev);
192 mii = device_get_softc(dev);
193 ifmedia_removeall(&mii->mii_media);
194 mii->mii_ifp = NULL;
195
196 return(0);
197 }
198
199 static int
200 miibus_readreg(dev, phy, reg)
201 device_t dev;
202 int phy, reg;
203 {
204 device_t parent;
205
206 parent = device_get_parent(dev);
207 return(MIIBUS_READREG(parent, phy, reg));
208 }
209
210 static int
211 miibus_writereg(dev, phy, reg, data)
212 device_t dev;
213 int phy, reg, data;
214 {
215 device_t parent;
216
217 parent = device_get_parent(dev);
218 return(MIIBUS_WRITEREG(parent, phy, reg, data));
219 }
220
221 static void
222 miibus_statchg(dev)
223 device_t dev;
224 {
225 device_t parent;
226
227 parent = device_get_parent(dev);
228 MIIBUS_STATCHG(parent);
229 return;
230 }
231
232 static void
233 miibus_linkchg(dev)
234 device_t dev;
235 {
236 struct mii_data *mii;
237 struct ifnet *ifp;
238 device_t parent;
239 int link;
240
241 parent = device_get_parent(dev);
242 MIIBUS_LINKCHG(parent);
243
244 mii = device_get_softc(dev);
245 /*
246 * Note that each NIC's softc must start with an ifnet structure.
247 */
248 ifp = device_get_softc(parent);
249
250 if (mii->mii_media_status & IFM_AVALID) {
251 if (mii->mii_media_status & IFM_ACTIVE)
252 link = NOTE_LINKUP;
253 else
254 link = NOTE_LINKDOWN;
255 } else {
256 link = NOTE_LINKINV;
257 }
258
259 KNOTE(&ifp->if_klist, link);
260 }
261
262 static void
263 miibus_mediainit(dev)
264 device_t dev;
265 {
266 struct mii_data *mii;
267 struct ifmedia_entry *m;
268 int media = 0;
269
270 /* Poke the parent in case it has any media of its own to add. */
271 MIIBUS_MEDIAINIT(device_get_parent(dev));
272
273 mii = device_get_softc(dev);
274 LIST_FOREACH(m, &mii->mii_media.ifm_list, ifm_list) {
275 media = m->ifm_media;
276 if (media == (IFM_ETHER|IFM_AUTO))
277 break;
278 }
279
280 ifmedia_set(&mii->mii_media, media);
281
282 return;
283 }
284
285 int
286 mii_phy_probe(dev, child, ifmedia_upd, ifmedia_sts)
287 device_t dev;
288 device_t *child;
289 ifm_change_cb_t ifmedia_upd;
290 ifm_stat_cb_t ifmedia_sts;
291 {
292 void **v;
293 int bmsr, i;
294
295 v = malloc(sizeof(vm_offset_t) * 2, M_DEVBUF, M_NOWAIT);
296 if (v == 0) {
297 return (ENOMEM);
298 }
299 v[0] = ifmedia_upd;
300 v[1] = ifmedia_sts;
301 *child = device_add_child(dev, "miibus", -1);
302 device_set_ivars(*child, v);
303
304 for (i = 0; i < MII_NPHY; i++) {
305 bmsr = MIIBUS_READREG(dev, i, MII_BMSR);
306 if (bmsr == 0 || bmsr == 0xffff ||
307 (bmsr & BMSR_MEDIAMASK) == 0) {
308 /* Assume no PHY at this address. */
309 continue;
310 } else
311 break;
312 }
313
314 if (i == MII_NPHY) {
315 device_delete_child(dev, *child);
316 *child = NULL;
317 return(ENXIO);
318 }
319
320 bus_generic_attach(dev);
321
322 return(0);
323 }
324
325 /*
326 * Media changed; notify all PHYs.
327 */
328 int
329 mii_mediachg(mii)
330 struct mii_data *mii;
331 {
332 struct mii_softc *child;
333 int rv;
334
335 mii->mii_media_status = 0;
336 mii->mii_media_active = IFM_NONE;
337
338 LIST_FOREACH(child, &mii->mii_phys, mii_list) {
339 rv = (*child->mii_service)(child, mii, MII_MEDIACHG);
340 if (rv)
341 return (rv);
342 }
343 return (0);
344 }
345
346 /*
347 * Call the PHY tick routines, used during autonegotiation.
348 */
349 void
350 mii_tick(mii)
351 struct mii_data *mii;
352 {
353 struct mii_softc *child;
354
355 LIST_FOREACH(child, &mii->mii_phys, mii_list)
356 (void) (*child->mii_service)(child, mii, MII_TICK);
357 }
358
359 /*
360 * Get media status from PHYs.
361 */
362 void
363 mii_pollstat(mii)
364 struct mii_data *mii;
365 {
366 struct mii_softc *child;
367
368 mii->mii_media_status = 0;
369 mii->mii_media_active = IFM_NONE;
370
371 LIST_FOREACH(child, &mii->mii_phys, mii_list)
372 (void) (*child->mii_service)(child, mii, MII_POLLSTAT);
373 }
374
375 /*
376 * Inform the PHYs that the interface is down.
377 */
378 void
379 mii_down(struct mii_data *mii)
380 {
381 struct mii_softc *child;
382
383 LIST_FOREACH(child, &mii->mii_phys, mii_list)
384 mii_phy_down(child);
385 }
Cache object: f75de12ca606105bbecb211781594aa0
|