150120Swpaul/*	$NetBSD: mii.c,v 1.12 1999/08/03 19:41:49 drochner Exp $	*/
250120Swpaul
350120Swpaul/*-
450120Swpaul * Copyright (c) 1998 The NetBSD Foundation, Inc.
550120Swpaul * All rights reserved.
650120Swpaul *
750120Swpaul * This code is derived from software contributed to The NetBSD Foundation
850120Swpaul * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
950120Swpaul * NASA Ames Research Center.
1050120Swpaul *
1150120Swpaul * Redistribution and use in source and binary forms, with or without
1250120Swpaul * modification, are permitted provided that the following conditions
1350120Swpaul * are met:
1450120Swpaul * 1. Redistributions of source code must retain the above copyright
1550120Swpaul *    notice, this list of conditions and the following disclaimer.
1650120Swpaul * 2. Redistributions in binary form must reproduce the above copyright
1750120Swpaul *    notice, this list of conditions and the following disclaimer in the
1850120Swpaul *    documentation and/or other materials provided with the distribution.
1950120Swpaul *
2050120Swpaul * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
2150120Swpaul * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2250120Swpaul * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2350120Swpaul * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2450120Swpaul * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2550120Swpaul * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2650120Swpaul * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2750120Swpaul * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2850120Swpaul * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2950120Swpaul * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3050120Swpaul * POSSIBILITY OF SUCH DAMAGE.
3150120Swpaul */
3250120Swpaul
33119418Sobrien#include <sys/cdefs.h>
34119418Sobrien__FBSDID("$FreeBSD$");
35119418Sobrien
3650120Swpaul/*
3750120Swpaul * MII bus layer, glues MII-capable network interface drivers to sharable
3850120Swpaul * PHY drivers.  This exports an interface compatible with BSD/OS 3.0's,
3950120Swpaul * plus some NetBSD extensions.
4050120Swpaul */
4150120Swpaul
4250120Swpaul#include <sys/param.h>
4350120Swpaul#include <sys/systm.h>
4450120Swpaul#include <sys/socket.h>
4550120Swpaul#include <sys/malloc.h>
4650120Swpaul#include <sys/module.h>
47221407Smarius#include <sys/bus.h>
4850120Swpaul
4950120Swpaul#include <net/if.h>
5050120Swpaul#include <net/if_media.h>
5150120Swpaul
5250120Swpaul#include <dev/mii/mii.h>
5350120Swpaul#include <dev/mii/miivar.h>
5450120Swpaul
5559757SpeterMODULE_VERSION(miibus, 1);
5659757Speter
5750120Swpaul#include "miibus_if.h"
5850120Swpaul
59230711Smariusstatic device_attach_t miibus_attach;
60230709Smariusstatic bus_child_location_str_t miibus_child_location_str;
61230709Smariusstatic bus_child_pnpinfo_str_t miibus_child_pnpinfo_str;
62230711Smariusstatic device_detach_t miibus_detach;
63230709Smariusstatic bus_hinted_child_t miibus_hinted_child;
64230709Smariusstatic bus_print_child_t miibus_print_child;
65230711Smariusstatic device_probe_t miibus_probe;
66230709Smariusstatic bus_read_ivar_t miibus_read_ivar;
67230709Smariusstatic miibus_readreg_t miibus_readreg;
68230709Smariusstatic miibus_statchg_t miibus_statchg;
69230709Smariusstatic miibus_writereg_t miibus_writereg;
70230709Smariusstatic miibus_linkchg_t miibus_linkchg;
71230709Smariusstatic miibus_mediainit_t miibus_mediainit;
72230709Smarius
73221407Smariusstatic unsigned char mii_bitreverse(unsigned char x);
7450120Swpaul
7550120Swpaulstatic device_method_t miibus_methods[] = {
7650120Swpaul	/* device interface */
7750120Swpaul	DEVMETHOD(device_probe,		miibus_probe),
7850120Swpaul	DEVMETHOD(device_attach,	miibus_attach),
7950120Swpaul	DEVMETHOD(device_detach,	miibus_detach),
8050120Swpaul	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
8150120Swpaul
8250120Swpaul	/* bus interface */
83166112Smarius	DEVMETHOD(bus_print_child,	miibus_print_child),
84213878Smarius	DEVMETHOD(bus_read_ivar,	miibus_read_ivar),
85141960Simp	DEVMETHOD(bus_child_pnpinfo_str, miibus_child_pnpinfo_str),
86141960Simp	DEVMETHOD(bus_child_location_str, miibus_child_location_str),
87230709Smarius	DEVMETHOD(bus_hinted_child,	miibus_hinted_child),
8850120Swpaul
8950120Swpaul	/* MII interface */
9050120Swpaul	DEVMETHOD(miibus_readreg,	miibus_readreg),
9150120Swpaul	DEVMETHOD(miibus_writereg,	miibus_writereg),
92221407Smarius	DEVMETHOD(miibus_statchg,	miibus_statchg),
93221407Smarius	DEVMETHOD(miibus_linkchg,	miibus_linkchg),
94221407Smarius	DEVMETHOD(miibus_mediainit,	miibus_mediainit),
9550120Swpaul
96229093Shselasky	DEVMETHOD_END
9750120Swpaul};
9850120Swpaul
9950120Swpauldevclass_t miibus_devclass;
10050120Swpaul
10150120Swpauldriver_t miibus_driver = {
10250120Swpaul	"miibus",
10350120Swpaul	miibus_methods,
10450120Swpaul	sizeof(struct mii_data)
10550120Swpaul};
10650120Swpaul
107169184Smarcelstruct miibus_ivars {
108213878Smarius	struct ifnet	*ifp;
109169184Smarcel	ifm_change_cb_t	ifmedia_upd;
110169184Smarcel	ifm_stat_cb_t	ifmedia_sts;
111230709Smarius	u_int		mii_flags;
112230709Smarius	u_int		mii_offset;
113169184Smarcel};
114169184Smarcel
115230711Smariusstatic int
116141937Simpmiibus_probe(device_t dev)
11750120Swpaul{
11850120Swpaul
11950120Swpaul	device_set_desc(dev, "MII bus");
12050120Swpaul
121213878Smarius	return (BUS_PROBE_SPECIFIC);
12250120Swpaul}
12350120Swpaul
124230711Smariusstatic int
125141937Simpmiibus_attach(device_t dev)
12650120Swpaul{
127169184Smarcel	struct miibus_ivars	*ivars;
128213878Smarius	struct mii_attach_args	*ma;
12950120Swpaul	struct mii_data		*mii;
130213878Smarius	device_t		*children;
131213878Smarius	int			i, nchildren;
13250120Swpaul
13350120Swpaul	mii = device_get_softc(dev);
134213878Smarius	if (device_get_children(dev, &children, &nchildren) == 0) {
135213878Smarius		for (i = 0; i < nchildren; i++) {
136213878Smarius			ma = device_get_ivars(children[i]);
137213878Smarius			ma->mii_data = mii;
138213878Smarius		}
139213878Smarius		free(children, M_TEMP);
140213878Smarius	}
141213878Smarius	if (nchildren == 0) {
142215711Smarius		device_printf(dev, "cannot get children\n");
143213878Smarius		return (ENXIO);
144213878Smarius	}
145169184Smarcel	ivars = device_get_ivars(dev);
146169184Smarcel	ifmedia_init(&mii->mii_media, IFM_IMASK, ivars->ifmedia_upd,
147169184Smarcel	    ivars->ifmedia_sts);
148213878Smarius	mii->mii_ifp = ivars->ifp;
149213878Smarius	mii->mii_ifp->if_capabilities |= IFCAP_LINKSTATE;
150213878Smarius	mii->mii_ifp->if_capenable |= IFCAP_LINKSTATE;
151213878Smarius	LIST_INIT(&mii->mii_phys);
15250120Swpaul
153213878Smarius	return (bus_generic_attach(dev));
15450120Swpaul}
15550120Swpaul
156230711Smariusstatic int
157141937Simpmiibus_detach(device_t dev)
15850120Swpaul{
15950120Swpaul	struct mii_data		*mii;
16050120Swpaul
16150120Swpaul	bus_generic_detach(dev);
16250120Swpaul	mii = device_get_softc(dev);
16350120Swpaul	ifmedia_removeall(&mii->mii_media);
16450120Swpaul	mii->mii_ifp = NULL;
16550120Swpaul
166213361Smarius	return (0);
16750120Swpaul}
16850120Swpaul
169105135Salfredstatic int
170166112Smariusmiibus_print_child(device_t dev, device_t child)
171166112Smarius{
172166112Smarius	struct mii_attach_args *ma;
173166112Smarius	int retval;
174166112Smarius
175166112Smarius	ma = device_get_ivars(child);
176166112Smarius	retval = bus_print_child_header(dev, child);
177166112Smarius	retval += printf(" PHY %d", ma->mii_phyno);
178166112Smarius	retval += bus_print_child_footer(dev, child);
179166112Smarius
180166112Smarius	return (retval);
181166112Smarius}
182166112Smarius
183166112Smariusstatic int
184213878Smariusmiibus_read_ivar(device_t dev, device_t child __unused, int which,
185213878Smarius    uintptr_t *result)
186213878Smarius{
187213878Smarius	struct miibus_ivars *ivars;
188213878Smarius
189213878Smarius	/*
190213878Smarius	 * NB: this uses the instance variables of the miibus rather than
191213878Smarius	 * its PHY children.
192213878Smarius	 */
193213878Smarius	ivars = device_get_ivars(dev);
194213878Smarius	switch (which) {
195213878Smarius	case MIIBUS_IVAR_FLAGS:
196213878Smarius		*result = ivars->mii_flags;
197213878Smarius		break;
198213878Smarius	default:
199213878Smarius		return (ENOENT);
200213878Smarius	}
201213878Smarius	return (0);
202213878Smarius}
203213878Smarius
204213878Smariusstatic int
205230709Smariusmiibus_child_pnpinfo_str(device_t dev __unused, device_t child, char *buf,
206141960Simp    size_t buflen)
207141960Simp{
208213361Smarius	struct mii_attach_args *ma;
209213361Smarius
210213361Smarius	ma = device_get_ivars(child);
211141964Simp	snprintf(buf, buflen, "oui=0x%x model=0x%x rev=0x%x",
212213361Smarius	    MII_OUI(ma->mii_id1, ma->mii_id2),
213213361Smarius	    MII_MODEL(ma->mii_id2), MII_REV(ma->mii_id2));
214141960Simp	return (0);
215141960Simp}
216141960Simp
217141960Simpstatic int
218230709Smariusmiibus_child_location_str(device_t dev __unused, device_t child, char *buf,
219141960Simp    size_t buflen)
220141960Simp{
221213361Smarius	struct mii_attach_args *ma;
222213361Smarius
223213361Smarius	ma = device_get_ivars(child);
224213361Smarius	snprintf(buf, buflen, "phyno=%d", ma->mii_phyno);
225141960Simp	return (0);
226141960Simp}
227141960Simp
228230709Smariusstatic void
229230709Smariusmiibus_hinted_child(device_t dev, const char *name, int unit)
230230709Smarius{
231230709Smarius	struct miibus_ivars *ivars;
232230709Smarius	struct mii_attach_args *args, *ma;
233230709Smarius	device_t *children, phy;
234230709Smarius	int i, nchildren;
235230709Smarius	u_int val;
236230709Smarius
237230709Smarius	if (resource_int_value(name, unit, "phyno", &val) != 0)
238230709Smarius		return;
239230709Smarius	if (device_get_children(dev, &children, &nchildren) != 0)
240230709Smarius		return;
241230709Smarius	ma = NULL;
242230709Smarius	for (i = 0; i < nchildren; i++) {
243230709Smarius		args = device_get_ivars(children[i]);
244230709Smarius		if (args->mii_phyno == val) {
245230709Smarius			ma = args;
246230709Smarius			break;
247230709Smarius		}
248230709Smarius	}
249230709Smarius	free(children, M_TEMP);
250230709Smarius
251230709Smarius	/*
252230709Smarius	 * Don't add a PHY that was automatically identified by having media
253230709Smarius	 * in its BMSR twice, only allow to alter its attach arguments.
254230709Smarius	 */
255230709Smarius	if (ma == NULL) {
256230709Smarius		ma = malloc(sizeof(struct mii_attach_args), M_DEVBUF,
257230709Smarius		    M_NOWAIT);
258230709Smarius		if (ma == NULL)
259230709Smarius			return;
260230709Smarius		phy = device_add_child(dev, name, unit);
261230709Smarius		if (phy == NULL) {
262230709Smarius			free(ma, M_DEVBUF);
263230709Smarius			return;
264230709Smarius		}
265230709Smarius		ivars = device_get_ivars(dev);
266230709Smarius		ma->mii_phyno = val;
267230709Smarius		ma->mii_offset = ivars->mii_offset++;
268230709Smarius		ma->mii_id1 = 0;
269230709Smarius		ma->mii_id2 = 0;
270230709Smarius		ma->mii_capmask = BMSR_DEFCAPMASK;
271230709Smarius		device_set_ivars(phy, ma);
272230709Smarius	}
273230709Smarius
274230709Smarius	if (resource_int_value(name, unit, "id1", &val) == 0)
275230709Smarius		ma->mii_id1 = val;
276230709Smarius	if (resource_int_value(name, unit, "id2", &val) == 0)
277230709Smarius		ma->mii_id2 = val;
278230709Smarius	if (resource_int_value(name, unit, "capmask", &val) == 0)
279230709Smarius		ma->mii_capmask = val;
280230709Smarius}
281230709Smarius
282141960Simpstatic int
283141937Simpmiibus_readreg(device_t dev, int phy, int reg)
28450120Swpaul{
28550120Swpaul	device_t		parent;
28650120Swpaul
28750120Swpaul	parent = device_get_parent(dev);
288213361Smarius	return (MIIBUS_READREG(parent, phy, reg));
28950120Swpaul}
29050120Swpaul
291105135Salfredstatic int
292141937Simpmiibus_writereg(device_t dev, int phy, int reg, int data)
29350120Swpaul{
29450120Swpaul	device_t		parent;
29550120Swpaul
29650120Swpaul	parent = device_get_parent(dev);
297213361Smarius	return (MIIBUS_WRITEREG(parent, phy, reg, data));
29850120Swpaul}
29950120Swpaul
300105135Salfredstatic void
301141937Simpmiibus_statchg(device_t dev)
30250120Swpaul{
30350120Swpaul	device_t		parent;
304155669Sglebius	struct mii_data		*mii;
30550120Swpaul
30650120Swpaul	parent = device_get_parent(dev);
30750120Swpaul	MIIBUS_STATCHG(parent);
308155669Sglebius
309155669Sglebius	mii = device_get_softc(dev);
310205270Simp	mii->mii_ifp->if_baudrate = ifmedia_baudrate(mii->mii_media_active);
31150120Swpaul}
31250120Swpaul
31384140Sjlemonstatic void
314141937Simpmiibus_linkchg(device_t dev)
31584140Sjlemon{
316138542Ssam	struct mii_data		*mii;
317138542Ssam	device_t		parent;
318138542Ssam	int			link_state;
31984140Sjlemon
32084140Sjlemon	parent = device_get_parent(dev);
32184140Sjlemon	MIIBUS_LINKCHG(parent);
32284140Sjlemon
32384140Sjlemon	mii = device_get_softc(dev);
324221407Smarius
32584140Sjlemon	if (mii->mii_media_status & IFM_AVALID) {
326138542Ssam		if (mii->mii_media_status & IFM_ACTIVE)
327128871Sandre			link_state = LINK_STATE_UP;
328138542Ssam		else
329128871Sandre			link_state = LINK_STATE_DOWN;
330138542Ssam	} else
331128871Sandre		link_state = LINK_STATE_UNKNOWN;
332205270Simp	if_link_state_change(mii->mii_ifp, link_state);
33384140Sjlemon}
33484140Sjlemon
335105135Salfredstatic void
336141937Simpmiibus_mediainit(device_t dev)
33750120Swpaul{
33850120Swpaul	struct mii_data		*mii;
33950120Swpaul	struct ifmedia_entry	*m;
34050120Swpaul	int			media = 0;
34150120Swpaul
34250577Swpaul	/* Poke the parent in case it has any media of its own to add. */
34350577Swpaul	MIIBUS_MEDIAINIT(device_get_parent(dev));
34450577Swpaul
34550120Swpaul	mii = device_get_softc(dev);
34672012Sphk	LIST_FOREACH(m, &mii->mii_media.ifm_list, ifm_list) {
34750120Swpaul		media = m->ifm_media;
348213361Smarius		if (media == (IFM_ETHER | IFM_AUTO))
34950120Swpaul			break;
35050120Swpaul	}
35150120Swpaul
35250120Swpaul	ifmedia_set(&mii->mii_media, media);
35350120Swpaul}
35450120Swpaul
355213878Smarius/*
356213878Smarius * Helper function used by network interface drivers, attaches the miibus and
357213878Smarius * the PHYs to the network interface driver parent.
358213878Smarius */
359105135Salfredint
360213878Smariusmii_attach(device_t dev, device_t *miibus, struct ifnet *ifp,
361213878Smarius    ifm_change_cb_t ifmedia_upd, ifm_stat_cb_t ifmedia_sts, int capmask,
362213878Smarius    int phyloc, int offloc, int flags)
36350120Swpaul{
364213878Smarius	struct miibus_ivars *ivars;
365230709Smarius	struct mii_attach_args *args, ma;
366213878Smarius	device_t *children, phy;
367230709Smarius	int bmsr, first, i, nchildren, phymax, phymin, rv;
368230709Smarius	uint32_t phymask;
36950120Swpaul
370213878Smarius	if (phyloc != MII_PHY_ANY && offloc != MII_OFFSET_ANY) {
371215711Smarius		printf("%s: phyloc and offloc specified\n", __func__);
372213878Smarius		return (EINVAL);
373213878Smarius	}
37450120Swpaul
375213878Smarius	if (offloc != MII_OFFSET_ANY && (offloc < 0 || offloc >= MII_NPHY)) {
376215711Smarius		printf("%s: ivalid offloc %d\n", __func__, offloc);
377213878Smarius		return (EINVAL);
37850120Swpaul	}
37950120Swpaul
380213878Smarius	if (phyloc == MII_PHY_ANY) {
381213878Smarius		phymin = 0;
382213878Smarius		phymax = MII_NPHY - 1;
383213878Smarius	} else {
384213878Smarius		if (phyloc < 0 || phyloc >= MII_NPHY) {
385215711Smarius			printf("%s: ivalid phyloc %d\n", __func__, phyloc);
386213878Smarius			return (EINVAL);
387213878Smarius		}
388213878Smarius		phymin = phymax = phyloc;
38950120Swpaul	}
39050120Swpaul
391213878Smarius	first = 0;
392213878Smarius	if (*miibus == NULL) {
393213878Smarius		first = 1;
394213878Smarius		ivars = malloc(sizeof(*ivars), M_DEVBUF, M_NOWAIT);
395213878Smarius		if (ivars == NULL)
396213878Smarius			return (ENOMEM);
397213878Smarius		ivars->ifp = ifp;
398213878Smarius		ivars->ifmedia_upd = ifmedia_upd;
399213878Smarius		ivars->ifmedia_sts = ifmedia_sts;
400213878Smarius		ivars->mii_flags = flags;
401213878Smarius		*miibus = device_add_child(dev, "miibus", -1);
402213878Smarius		if (*miibus == NULL) {
403213878Smarius			rv = ENXIO;
404213878Smarius			goto fail;
405213878Smarius		}
406213878Smarius		device_set_ivars(*miibus, ivars);
407213878Smarius	} else {
408213878Smarius		ivars = device_get_ivars(*miibus);
409213878Smarius		if (ivars->ifp != ifp || ivars->ifmedia_upd != ifmedia_upd ||
410213878Smarius		    ivars->ifmedia_sts != ifmedia_sts ||
411213878Smarius		    ivars->mii_flags != flags) {
412215711Smarius			printf("%s: non-matching invariant\n", __func__);
413213878Smarius			return (EINVAL);
414213878Smarius		}
415213878Smarius		/*
416213878Smarius		 * Assignment of the attach arguments mii_data for the first
417213878Smarius		 * pass is done in miibus_attach(), i.e. once the miibus softc
418213878Smarius		 * has been allocated.
419213878Smarius		 */
420213878Smarius		ma.mii_data = device_get_softc(*miibus);
421221407Smarius	}
42250120Swpaul
423213878Smarius	ma.mii_capmask = capmask;
424213878Smarius
425230709Smarius	if (resource_int_value(device_get_name(*miibus),
426230709Smarius	    device_get_unit(*miibus), "phymask", &phymask) != 0)
427230709Smarius		phymask = 0xffffffff;
428230709Smarius
429230709Smarius	if (device_get_children(*miibus, &children, &nchildren) != 0) {
430230709Smarius		children = NULL;
431230709Smarius		nchildren = 0;
432230709Smarius	}
433230709Smarius	ivars->mii_offset = 0;
434213878Smarius	for (ma.mii_phyno = phymin; ma.mii_phyno <= phymax; ma.mii_phyno++) {
435213878Smarius		/*
436213878Smarius		 * Make sure we haven't already configured a PHY at this
437213878Smarius		 * address.  This allows mii_attach() to be called
438213878Smarius		 * multiple times.
439213878Smarius		 */
440230709Smarius		for (i = 0; i < nchildren; i++) {
441230709Smarius			args = device_get_ivars(children[i]);
442230709Smarius			if (args->mii_phyno == ma.mii_phyno) {
443230709Smarius				/*
444230709Smarius				 * Yes, there is already something
445230709Smarius				 * configured at this address.
446230709Smarius				 */
447230709Smarius				goto skip;
448213878Smarius			}
449213878Smarius		}
450213878Smarius
451213878Smarius		/*
452213878Smarius		 * Check to see if there is a PHY at this address.  Note,
453213878Smarius		 * many braindead PHYs report 0/0 in their ID registers,
454213878Smarius		 * so we test for media in the BMSR.
455221407Smarius		 */
456213878Smarius		bmsr = MIIBUS_READREG(dev, ma.mii_phyno, MII_BMSR);
457213878Smarius		if (bmsr == 0 || bmsr == 0xffff ||
458213878Smarius		    (bmsr & (BMSR_EXTSTAT | BMSR_MEDIAMASK)) == 0) {
459213878Smarius			/* Assume no PHY at this address. */
460213878Smarius			continue;
461213878Smarius		}
462213878Smarius
463213878Smarius		/*
464213878Smarius		 * There is a PHY at this address.  If we were given an
465213878Smarius		 * `offset' locator, skip this PHY if it doesn't match.
466213878Smarius		 */
467230709Smarius		if (offloc != MII_OFFSET_ANY && offloc != ivars->mii_offset)
468213878Smarius			goto skip;
469213878Smarius
470213878Smarius		/*
471230709Smarius		 * Skip this PHY if it's not included in the phymask hint.
472230709Smarius		 */
473230709Smarius		if ((phymask & (1 << ma.mii_phyno)) == 0)
474230709Smarius			goto skip;
475230709Smarius
476230709Smarius		/*
477230709Smarius		 * Extract the IDs.  Braindead PHYs will be handled by
478213878Smarius		 * the `ukphy' driver, as we have no ID information to
479213878Smarius		 * match on.
480221407Smarius		 */
481213878Smarius		ma.mii_id1 = MIIBUS_READREG(dev, ma.mii_phyno, MII_PHYIDR1);
482213878Smarius		ma.mii_id2 = MIIBUS_READREG(dev, ma.mii_phyno, MII_PHYIDR2);
483213878Smarius
484230709Smarius		ma.mii_offset = ivars->mii_offset;
485213878Smarius		args = malloc(sizeof(struct mii_attach_args), M_DEVBUF,
486213878Smarius		    M_NOWAIT);
487213878Smarius		if (args == NULL)
488213878Smarius			goto skip;
489213878Smarius		bcopy((char *)&ma, (char *)args, sizeof(ma));
490213878Smarius		phy = device_add_child(*miibus, NULL, -1);
491213878Smarius		if (phy == NULL) {
492213878Smarius			free(args, M_DEVBUF);
493213878Smarius			goto skip;
494213878Smarius		}
495213878Smarius		device_set_ivars(phy, args);
496213878Smarius skip:
497230709Smarius		ivars->mii_offset++;
498213878Smarius	}
499230709Smarius	free(children, M_TEMP);
500213878Smarius
501213878Smarius	if (first != 0) {
502230709Smarius		rv = device_set_driver(*miibus, &miibus_driver);
503230709Smarius		if (rv != 0)
504230709Smarius			goto fail;
505230709Smarius		bus_enumerate_hinted_children(*miibus);
506230709Smarius		rv = device_get_children(*miibus, &children, &nchildren);
507230709Smarius		if (rv != 0)
508230709Smarius			goto fail;
509230709Smarius		free(children, M_TEMP);
510230709Smarius		if (nchildren == 0) {
511213878Smarius			rv = ENXIO;
512213878Smarius			goto fail;
513213878Smarius		}
514213878Smarius		rv = bus_generic_attach(dev);
515213878Smarius		if (rv != 0)
516213878Smarius			goto fail;
517215348Smarius
518215348Smarius		/* Attaching of the PHY drivers is done in miibus_attach(). */
519215348Smarius		return (0);
520213878Smarius	}
521213878Smarius	rv = bus_generic_attach(*miibus);
522213878Smarius	if (rv != 0)
523213878Smarius		goto fail;
524213878Smarius
525213361Smarius	return (0);
526213878Smarius
527213878Smarius fail:
528213878Smarius	if (*miibus != NULL)
529213878Smarius		device_delete_child(dev, *miibus);
530213878Smarius	free(ivars, M_DEVBUF);
531213878Smarius	if (first != 0)
532213878Smarius		*miibus = NULL;
533213878Smarius	return (rv);
53450120Swpaul}
53550120Swpaul
53650120Swpaul/*
53750120Swpaul * Media changed; notify all PHYs.
53850120Swpaul */
53950120Swpaulint
540141937Simpmii_mediachg(struct mii_data *mii)
54150120Swpaul{
54250120Swpaul	struct mii_softc *child;
543213364Smarius	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
54450120Swpaul	int rv;
54550120Swpaul
54650120Swpaul	mii->mii_media_status = 0;
54750120Swpaul	mii->mii_media_active = IFM_NONE;
54850120Swpaul
54972012Sphk	LIST_FOREACH(child, &mii->mii_phys, mii_list) {
550213364Smarius		/*
551213364Smarius		 * If the media indicates a different PHY instance,
552213364Smarius		 * isolate this one.
553213364Smarius		 */
554213364Smarius		if (IFM_INST(ife->ifm_media) != child->mii_inst) {
555213364Smarius			if ((child->mii_flags & MIIF_NOISOLATE) != 0) {
556213364Smarius				device_printf(child->mii_dev, "%s: "
557213364Smarius				    "can't handle non-zero PHY instance %d\n",
558213364Smarius				    __func__, child->mii_inst);
559213364Smarius				continue;
560213364Smarius			}
561213364Smarius			PHY_WRITE(child, MII_BMCR, PHY_READ(child, MII_BMCR) |
562213364Smarius			    BMCR_ISO);
563213364Smarius			continue;
564213364Smarius		}
565221407Smarius		rv = PHY_SERVICE(child, mii, MII_MEDIACHG);
56650120Swpaul		if (rv)
56750120Swpaul			return (rv);
56850120Swpaul	}
56950120Swpaul	return (0);
57050120Swpaul}
57150120Swpaul
57250120Swpaul/*
57350120Swpaul * Call the PHY tick routines, used during autonegotiation.
57450120Swpaul */
57550120Swpaulvoid
576141937Simpmii_tick(struct mii_data *mii)
57750120Swpaul{
57850120Swpaul	struct mii_softc *child;
579213364Smarius	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
58050120Swpaul
581213364Smarius	LIST_FOREACH(child, &mii->mii_phys, mii_list) {
582213364Smarius		/*
583213364Smarius		 * If this PHY instance isn't currently selected, just skip
584213364Smarius		 * it.
585213364Smarius		 */
586213364Smarius		if (IFM_INST(ife->ifm_media) != child->mii_inst)
587213364Smarius			continue;
588221407Smarius		(void)PHY_SERVICE(child, mii, MII_TICK);
589213364Smarius	}
59050120Swpaul}
59150120Swpaul
59250120Swpaul/*
59350120Swpaul * Get media status from PHYs.
59450120Swpaul */
59550120Swpaulvoid
596141937Simpmii_pollstat(struct mii_data *mii)
59750120Swpaul{
59850120Swpaul	struct mii_softc *child;
599213364Smarius	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
60050120Swpaul
60150120Swpaul	mii->mii_media_status = 0;
60250120Swpaul	mii->mii_media_active = IFM_NONE;
60350120Swpaul
604213364Smarius	LIST_FOREACH(child, &mii->mii_phys, mii_list) {
605213364Smarius		/*
606213364Smarius		 * If we're not polling this PHY instance, just skip it.
607213364Smarius		 */
608213364Smarius		if (IFM_INST(ife->ifm_media) != child->mii_inst)
609213364Smarius			continue;
610221407Smarius		(void)PHY_SERVICE(child, mii, MII_POLLSTAT);
611213364Smarius	}
61250120Swpaul}
61395722Sphk
61495722Sphk/*
61595722Sphk * Inform the PHYs that the interface is down.
61695722Sphk */
61795722Sphkvoid
61895722Sphkmii_down(struct mii_data *mii)
61995722Sphk{
62095722Sphk	struct mii_softc *child;
62195722Sphk
62295722Sphk	LIST_FOREACH(child, &mii->mii_phys, mii_list)
62395722Sphk		mii_phy_down(child);
62495722Sphk}
625221407Smarius
626221407Smariusstatic unsigned char
627221407Smariusmii_bitreverse(unsigned char x)
628221407Smarius{
629242908Sdim	static unsigned const char nibbletab[16] = {
630221407Smarius		0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15
631221407Smarius	};
632221407Smarius
633221407Smarius	return ((nibbletab[x & 15] << 4) | nibbletab[x >> 4]);
634221407Smarius}
635221407Smarius
636221407Smariusu_int
637221407Smariusmii_oui(u_int id1, u_int id2)
638221407Smarius{
639221407Smarius	u_int h;
640221407Smarius
641221407Smarius	h = (id1 << 6) | (id2 >> 10);
642221407Smarius
643221407Smarius	return ((mii_bitreverse(h >> 16) << 16) |
644221913Smarius	    (mii_bitreverse((h >> 8) & 0xff) << 8) |
645221913Smarius	    mii_bitreverse(h & 0xff));
646221407Smarius}
647