xref: /haiku/src/libs/compat/freebsd_network/fbsd_mii.c (revision 8d2bf6953e851d431fc67de1bc970c40afa79e9f)
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  *
20  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  */
32 #ifdef __HAIKU__
33 #include "device.h"
34 
35 #define malloc(size, tag, flags) kernel_malloc(size, tag, flags)
36 #define free(pointer, tag) kernel_free(pointer, tag)
37 #endif
38 
39 #include <sys/cdefs.h>
40 __FBSDID("$FreeBSD$");
41 
42 /*
43  * MII bus layer, glues MII-capable network interface drivers to sharable
44  * PHY drivers.  This exports an interface compatible with BSD/OS 3.0's,
45  * plus some NetBSD extensions.
46  */
47 
48 #include <sys/param.h>
49 #include <sys/systm.h>
50 #include <sys/socket.h>
51 #include <sys/malloc.h>
52 #include <sys/module.h>
53 #include <sys/bus.h>
54 
55 #include <net/if.h>
56 #include <net/if_media.h>
57 #include <net/route.h>
58 
59 #include <dev/mii/mii.h>
60 #include <dev/mii/miivar.h>
61 
62 MODULE_VERSION(miibus, 1);
63 
64 #include "miibus_if.h"
65 
66 static int miibus_print_child(device_t dev, device_t child);
67 static int miibus_read_ivar(device_t dev, device_t child, int which,
68     uintptr_t *result);
69 static int miibus_child_location_str(device_t bus, device_t child, char *buf,
70     size_t buflen);
71 static int miibus_child_pnpinfo_str(device_t bus, device_t child, char *buf,
72     size_t buflen);
73 static int miibus_readreg(device_t, int, int);
74 static int miibus_writereg(device_t, int, int, int);
75 static void miibus_statchg(device_t);
76 static void miibus_linkchg(device_t);
77 static void miibus_mediainit(device_t);
78 static unsigned char mii_bitreverse(unsigned char x);
79 
80 static device_method_t miibus_methods[] = {
81 	/* device interface */
82 	DEVMETHOD(device_probe,		miibus_probe),
83 	DEVMETHOD(device_attach,	miibus_attach),
84 	DEVMETHOD(device_detach,	miibus_detach),
85 	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
86 
87 	/* bus interface */
88 	DEVMETHOD(bus_print_child,	miibus_print_child),
89 	DEVMETHOD(bus_read_ivar,	miibus_read_ivar),
90 	DEVMETHOD(bus_driver_added,	bus_generic_driver_added),
91 	DEVMETHOD(bus_child_pnpinfo_str, miibus_child_pnpinfo_str),
92 	DEVMETHOD(bus_child_location_str, miibus_child_location_str),
93 
94 	/* MII interface */
95 	DEVMETHOD(miibus_readreg,	miibus_readreg),
96 	DEVMETHOD(miibus_writereg,	miibus_writereg),
97 	DEVMETHOD(miibus_statchg,	miibus_statchg),
98 	DEVMETHOD(miibus_linkchg,	miibus_linkchg),
99 	DEVMETHOD(miibus_mediainit,	miibus_mediainit),
100 
101 	{ 0, 0 }
102 };
103 
104 devclass_t miibus_devclass;
105 
106 driver_t miibus_driver = {
107 	"miibus",
108 	miibus_methods,
109 	sizeof(struct mii_data)
110 };
111 
112 struct miibus_ivars {
113 	struct ifnet	*ifp;
114 	ifm_change_cb_t	ifmedia_upd;
115 	ifm_stat_cb_t	ifmedia_sts;
116 	int		mii_flags;
117 };
118 
119 int
120 miibus_probe(device_t dev)
121 {
122 
123 	device_set_desc(dev, "MII bus");
124 
125 	return (BUS_PROBE_SPECIFIC);
126 }
127 
128 int
129 miibus_attach(device_t dev)
130 {
131 	struct miibus_ivars	*ivars;
132 	struct mii_attach_args	*ma;
133 	struct mii_data		*mii;
134 	device_t		*children;
135 	int			i, nchildren;
136 
137 	mii = device_get_softc(dev);
138 	nchildren = 0;
139 	if (device_get_children(dev, &children, &nchildren) == 0) {
140 		for (i = 0; i < nchildren; i++) {
141 			ma = device_get_ivars(children[i]);
142 			ma->mii_data = mii;
143 		}
144 		free(children, M_TEMP);
145 	}
146 	if (nchildren == 0) {
147 		device_printf(dev, "cannot get children\n");
148 		return (ENXIO);
149 	}
150 	ivars = device_get_ivars(dev);
151 	ifmedia_init(&mii->mii_media, IFM_IMASK, ivars->ifmedia_upd,
152 	    ivars->ifmedia_sts);
153 	mii->mii_ifp = ivars->ifp;
154 	mii->mii_ifp->if_capabilities |= IFCAP_LINKSTATE;
155 	mii->mii_ifp->if_capenable |= IFCAP_LINKSTATE;
156 	LIST_INIT(&mii->mii_phys);
157 
158 	return (bus_generic_attach(dev));
159 }
160 
161 int
162 miibus_detach(device_t dev)
163 {
164 	struct mii_data		*mii;
165 
166 	bus_generic_detach(dev);
167 	mii = device_get_softc(dev);
168 	ifmedia_removeall(&mii->mii_media);
169 	mii->mii_ifp = NULL;
170 
171 	return (0);
172 }
173 
174 static int
175 miibus_print_child(device_t dev, device_t child)
176 {
177 	struct mii_attach_args *ma;
178 	int retval;
179 
180 	ma = device_get_ivars(child);
181 	retval = bus_print_child_header(dev, child);
182 	retval += printf(" PHY %d", ma->mii_phyno);
183 	retval += bus_print_child_footer(dev, child);
184 
185 	return (retval);
186 }
187 
188 static int
189 miibus_read_ivar(device_t dev, device_t child __unused, int which,
190     uintptr_t *result)
191 {
192 	struct miibus_ivars *ivars;
193 
194 	/*
195 	 * NB: this uses the instance variables of the miibus rather than
196 	 * its PHY children.
197 	 */
198 	ivars = device_get_ivars(dev);
199 	switch (which) {
200 	case MIIBUS_IVAR_FLAGS:
201 		*result = ivars->mii_flags;
202 		break;
203 	default:
204 		return (ENOENT);
205 	}
206 	return (0);
207 }
208 
209 static int
210 miibus_child_pnpinfo_str(device_t bus __unused, device_t child, char *buf,
211     size_t buflen)
212 {
213 	struct mii_attach_args *ma;
214 
215 	ma = device_get_ivars(child);
216 	snprintf(buf, buflen, "oui=0x%x model=0x%x rev=0x%x",
217 	    MII_OUI(ma->mii_id1, ma->mii_id2),
218 	    MII_MODEL(ma->mii_id2), MII_REV(ma->mii_id2));
219 	return (0);
220 }
221 
222 static int
223 miibus_child_location_str(device_t bus __unused, device_t child, char *buf,
224     size_t buflen)
225 {
226 	struct mii_attach_args *ma;
227 
228 	ma = device_get_ivars(child);
229 	snprintf(buf, buflen, "phyno=%d", ma->mii_phyno);
230 	return (0);
231 }
232 
233 static int
234 miibus_readreg(device_t dev, int phy, int reg)
235 {
236 	device_t		parent;
237 
238 	parent = device_get_parent(dev);
239 	return (MIIBUS_READREG(parent, phy, reg));
240 }
241 
242 static int
243 miibus_writereg(device_t dev, int phy, int reg, int data)
244 {
245 	device_t		parent;
246 
247 	parent = device_get_parent(dev);
248 	return (MIIBUS_WRITEREG(parent, phy, reg, data));
249 }
250 
251 static void
252 miibus_statchg(device_t dev)
253 {
254 	device_t		parent;
255 	struct mii_data		*mii;
256 
257 	parent = device_get_parent(dev);
258 	MIIBUS_STATCHG(parent);
259 
260 	mii = device_get_softc(dev);
261 	mii->mii_ifp->if_baudrate = ifmedia_baudrate(mii->mii_media_active);
262 }
263 
264 static void
265 miibus_linkchg(device_t dev)
266 {
267 	struct mii_data		*mii;
268 	device_t		parent;
269 	int			link_state;
270 
271 	parent = device_get_parent(dev);
272 	MIIBUS_LINKCHG(parent);
273 
274 	mii = device_get_softc(dev);
275 
276 	if (mii->mii_media_status & IFM_AVALID) {
277 		if (mii->mii_media_status & IFM_ACTIVE)
278 			link_state = LINK_STATE_UP;
279 		else
280 			link_state = LINK_STATE_DOWN;
281 	} else
282 		link_state = LINK_STATE_UNKNOWN;
283 	if_link_state_change(mii->mii_ifp, link_state);
284 }
285 
286 static void
287 miibus_mediainit(device_t dev)
288 {
289 	struct mii_data		*mii;
290 	struct ifmedia_entry	*m;
291 	int			media = 0;
292 
293 	/* Poke the parent in case it has any media of its own to add. */
294 	MIIBUS_MEDIAINIT(device_get_parent(dev));
295 
296 	mii = device_get_softc(dev);
297 	LIST_FOREACH(m, &mii->mii_media.ifm_list, ifm_list) {
298 		media = m->ifm_media;
299 		if (media == (IFM_ETHER | IFM_AUTO))
300 			break;
301 	}
302 
303 	ifmedia_set(&mii->mii_media, media);
304 }
305 
306 /*
307  * Helper function used by network interface drivers, attaches the miibus and
308  * the PHYs to the network interface driver parent.
309  */
310 int
311 mii_attach(device_t dev, device_t *miibus, struct ifnet *ifp,
312     ifm_change_cb_t ifmedia_upd, ifm_stat_cb_t ifmedia_sts, int capmask,
313     int phyloc, int offloc, int flags)
314 {
315 	struct miibus_ivars *ivars;
316 	struct mii_attach_args ma, *args;
317 	device_t *children, phy;
318 	int bmsr, first, i, nchildren, offset, phymax, phymin, rv;
319 
320 	if (phyloc != MII_PHY_ANY && offloc != MII_OFFSET_ANY) {
321 		printf("%s: phyloc and offloc specified\n", __func__);
322 		return (EINVAL);
323 	}
324 
325 	if (offloc != MII_OFFSET_ANY && (offloc < 0 || offloc >= MII_NPHY)) {
326 		printf("%s: ivalid offloc %d\n", __func__, offloc);
327 		return (EINVAL);
328 	}
329 
330 	if (phyloc == MII_PHY_ANY) {
331 		phymin = 0;
332 		phymax = MII_NPHY - 1;
333 	} else {
334 		if (phyloc < 0 || phyloc >= MII_NPHY) {
335 			printf("%s: ivalid phyloc %d\n", __func__, phyloc);
336 			return (EINVAL);
337 		}
338 		phymin = phymax = phyloc;
339 	}
340 
341 	first = 0;
342 	if (*miibus == NULL) {
343 		first = 1;
344 		ivars = malloc(sizeof(*ivars), M_DEVBUF, M_NOWAIT);
345 		if (ivars == NULL)
346 			return (ENOMEM);
347 		ivars->ifp = ifp;
348 		ivars->ifmedia_upd = ifmedia_upd;
349 		ivars->ifmedia_sts = ifmedia_sts;
350 		ivars->mii_flags = flags;
351 		*miibus = device_add_child(dev, "miibus", -1);
352 		if (*miibus == NULL) {
353 			rv = ENXIO;
354 			goto fail;
355 		}
356 		device_set_ivars(*miibus, ivars);
357 	} else {
358 		ivars = device_get_ivars(*miibus);
359 		if (ivars->ifp != ifp || ivars->ifmedia_upd != ifmedia_upd ||
360 		    ivars->ifmedia_sts != ifmedia_sts ||
361 		    ivars->mii_flags != flags) {
362 			printf("%s: non-matching invariant\n", __func__);
363 			return (EINVAL);
364 		}
365 		/*
366 		 * Assignment of the attach arguments mii_data for the first
367 		 * pass is done in miibus_attach(), i.e. once the miibus softc
368 		 * has been allocated.
369 		 */
370 		ma.mii_data = device_get_softc(*miibus);
371 	}
372 
373 	ma.mii_capmask = capmask;
374 
375 	phy = NULL;
376 	offset = 0;
377 	for (ma.mii_phyno = phymin; ma.mii_phyno <= phymax; ma.mii_phyno++) {
378 		/*
379 		 * Make sure we haven't already configured a PHY at this
380 		 * address.  This allows mii_attach() to be called
381 		 * multiple times.
382 		 */
383 		if (device_get_children(*miibus, &children, &nchildren) == 0) {
384 			for (i = 0; i < nchildren; i++) {
385 				args = device_get_ivars(children[i]);
386 				if (args->mii_phyno == ma.mii_phyno) {
387 					/*
388 					 * Yes, there is already something
389 					 * configured at this address.
390 					 */
391 					free(children, M_TEMP);
392 					goto skip;
393 				}
394 			}
395 			free(children, M_TEMP);
396 		}
397 
398 		/*
399 		 * Check to see if there is a PHY at this address.  Note,
400 		 * many braindead PHYs report 0/0 in their ID registers,
401 		 * so we test for media in the BMSR.
402 		 */
403 		bmsr = MIIBUS_READREG(dev, ma.mii_phyno, MII_BMSR);
404 		if (bmsr == 0 || bmsr == 0xffff ||
405 		    (bmsr & (BMSR_EXTSTAT | BMSR_MEDIAMASK)) == 0) {
406 			/* Assume no PHY at this address. */
407 			continue;
408 		}
409 
410 		/*
411 		 * There is a PHY at this address.  If we were given an
412 		 * `offset' locator, skip this PHY if it doesn't match.
413 		 */
414 		if (offloc != MII_OFFSET_ANY && offloc != offset)
415 			goto skip;
416 
417 		/*
418 		 * Extract the IDs. Braindead PHYs will be handled by
419 		 * the `ukphy' driver, as we have no ID information to
420 		 * match on.
421 		 */
422 		ma.mii_id1 = MIIBUS_READREG(dev, ma.mii_phyno, MII_PHYIDR1);
423 		ma.mii_id2 = MIIBUS_READREG(dev, ma.mii_phyno, MII_PHYIDR2);
424 
425 		ma.mii_offset = offset;
426 		args = malloc(sizeof(struct mii_attach_args), M_DEVBUF,
427 		    M_NOWAIT);
428 		if (args == NULL)
429 			goto skip;
430 		bcopy((char *)&ma, (char *)args, sizeof(ma));
431 		phy = device_add_child(*miibus, NULL, -1);
432 		if (phy == NULL) {
433 			free(args, M_DEVBUF);
434 			goto skip;
435 		}
436 		device_set_ivars(phy, args);
437  skip:
438 		offset++;
439 	}
440 
441 	if (first != 0) {
442 		if (phy == NULL) {
443 			rv = ENXIO;
444 			goto fail;
445 		}
446 		rv = bus_generic_attach(dev);
447 		if (rv != 0)
448 			goto fail;
449 
450 		/* Attaching of the PHY drivers is done in miibus_attach(). */
451 		return (0);
452 	}
453 	rv = bus_generic_attach(*miibus);
454 	if (rv != 0)
455 		goto fail;
456 
457 	return (0);
458 
459  fail:
460 	if (*miibus != NULL)
461 		device_delete_child(dev, *miibus);
462 	free(ivars, M_DEVBUF);
463 	if (first != 0)
464 		*miibus = NULL;
465 	return (rv);
466 }
467 
468 /*
469  * Media changed; notify all PHYs.
470  */
471 int
472 mii_mediachg(struct mii_data *mii)
473 {
474 	struct mii_softc *child;
475 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
476 	int rv;
477 
478 	mii->mii_media_status = 0;
479 	mii->mii_media_active = IFM_NONE;
480 
481 	LIST_FOREACH(child, &mii->mii_phys, mii_list) {
482 		/*
483 		 * If the media indicates a different PHY instance,
484 		 * isolate this one.
485 		 */
486 		if (IFM_INST(ife->ifm_media) != child->mii_inst) {
487 			if ((child->mii_flags & MIIF_NOISOLATE) != 0) {
488 				device_printf(child->mii_dev, "%s: "
489 				    "can't handle non-zero PHY instance %d\n",
490 				    __func__, child->mii_inst);
491 				continue;
492 			}
493 			PHY_WRITE(child, MII_BMCR, PHY_READ(child, MII_BMCR) |
494 			    BMCR_ISO);
495 			continue;
496 		}
497 		rv = PHY_SERVICE(child, mii, MII_MEDIACHG);
498 		if (rv)
499 			return (rv);
500 	}
501 	return (0);
502 }
503 
504 /*
505  * Call the PHY tick routines, used during autonegotiation.
506  */
507 void
508 mii_tick(struct mii_data *mii)
509 {
510 	struct mii_softc *child;
511 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
512 
513 	LIST_FOREACH(child, &mii->mii_phys, mii_list) {
514 		/*
515 		 * If this PHY instance isn't currently selected, just skip
516 		 * it.
517 		 */
518 		if (IFM_INST(ife->ifm_media) != child->mii_inst)
519 			continue;
520 		(void)PHY_SERVICE(child, mii, MII_TICK);
521 	}
522 }
523 
524 /*
525  * Get media status from PHYs.
526  */
527 void
528 mii_pollstat(struct mii_data *mii)
529 {
530 	struct mii_softc *child;
531 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
532 
533 	mii->mii_media_status = 0;
534 	mii->mii_media_active = IFM_NONE;
535 
536 	LIST_FOREACH(child, &mii->mii_phys, mii_list) {
537 		/*
538 		 * If we're not polling this PHY instance, just skip it.
539 		 */
540 		if (IFM_INST(ife->ifm_media) != child->mii_inst)
541 			continue;
542 		(void)PHY_SERVICE(child, mii, MII_POLLSTAT);
543 	}
544 }
545 
546 /*
547  * Inform the PHYs that the interface is down.
548  */
549 void
550 mii_down(struct mii_data *mii)
551 {
552 	struct mii_softc *child;
553 
554 	LIST_FOREACH(child, &mii->mii_phys, mii_list)
555 		mii_phy_down(child);
556 }
557 
558 static unsigned char
559 mii_bitreverse(unsigned char x)
560 {
561 	unsigned const char const nibbletab[16] = {
562 		0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15
563 	};
564 
565 	return ((nibbletab[x & 15] << 4) | nibbletab[x >> 4]);
566 }
567 
568 u_int
569 mii_oui(u_int id1, u_int id2)
570 {
571 	u_int h;
572 
573 	h = (id1 << 6) | (id2 >> 10);
574 
575 	return ((mii_bitreverse(h >> 16) << 16) |
576 	    (mii_bitreverse((h >> 8) & 0xff) << 8) |
577 	    mii_bitreverse(h & 0xff));
578 }
579