xref: /freebsd/sys/dev/mii/mii.c (revision 18250ec6c089c0c50cbd9fd87d78e03ff89916df)
1d0027533SBill Paul /*	$NetBSD: mii.c,v 1.12 1999/08/03 19:41:49 drochner Exp $	*/
2d0027533SBill Paul 
3d0027533SBill Paul /*-
4b61a5730SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
5718cf2ccSPedro F. Giffuni  *
6d0027533SBill Paul  * Copyright (c) 1998 The NetBSD Foundation, Inc.
7d0027533SBill Paul  * All rights reserved.
8d0027533SBill Paul  *
9d0027533SBill Paul  * This code is derived from software contributed to The NetBSD Foundation
10d0027533SBill Paul  * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
11d0027533SBill Paul  * NASA Ames Research Center.
12d0027533SBill Paul  *
13d0027533SBill Paul  * Redistribution and use in source and binary forms, with or without
14d0027533SBill Paul  * modification, are permitted provided that the following conditions
15d0027533SBill Paul  * are met:
16d0027533SBill Paul  * 1. Redistributions of source code must retain the above copyright
17d0027533SBill Paul  *    notice, this list of conditions and the following disclaimer.
18d0027533SBill Paul  * 2. Redistributions in binary form must reproduce the above copyright
19d0027533SBill Paul  *    notice, this list of conditions and the following disclaimer in the
20d0027533SBill Paul  *    documentation and/or other materials provided with the distribution.
21d0027533SBill Paul  *
22d0027533SBill Paul  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
23d0027533SBill Paul  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
24d0027533SBill Paul  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
25d0027533SBill Paul  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
26d0027533SBill Paul  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27d0027533SBill Paul  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28d0027533SBill Paul  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29d0027533SBill Paul  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30d0027533SBill Paul  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31d0027533SBill Paul  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32d0027533SBill Paul  * POSSIBILITY OF SUCH DAMAGE.
33d0027533SBill Paul  */
34d0027533SBill Paul 
35aad970f1SDavid E. O'Brien #include <sys/cdefs.h>
36d0027533SBill Paul /*
37d0027533SBill Paul  * MII bus layer, glues MII-capable network interface drivers to sharable
38d0027533SBill Paul  * PHY drivers.  This exports an interface compatible with BSD/OS 3.0's,
39d0027533SBill Paul  * plus some NetBSD extensions.
40d0027533SBill Paul  */
41d0027533SBill Paul 
42d0027533SBill Paul #include <sys/param.h>
43d0027533SBill Paul #include <sys/systm.h>
44d0027533SBill Paul #include <sys/socket.h>
45d0027533SBill Paul #include <sys/malloc.h>
46d0027533SBill Paul #include <sys/module.h>
47d0027533SBill Paul #include <sys/bus.h>
48ddfc9c4cSWarner Losh #include <sys/sbuf.h>
49d0027533SBill Paul 
50d0027533SBill Paul #include <net/if.h>
5176039bc8SGleb Smirnoff #include <net/if_var.h>
52d0027533SBill Paul #include <net/if_media.h>
53d0027533SBill Paul 
54d0027533SBill Paul #include <dev/mii/mii.h>
55d0027533SBill Paul #include <dev/mii/miivar.h>
56d0027533SBill Paul 
579ad6460aSPeter Wemm MODULE_VERSION(miibus, 1);
589ad6460aSPeter Wemm 
59d0027533SBill Paul #include "miibus_if.h"
60d0027533SBill Paul 
61dc569c89SJohn Baldwin static bus_child_deleted_t miibus_child_deleted;
62ddfc9c4cSWarner Losh static bus_child_location_t miibus_child_location;
63ddfc9c4cSWarner Losh static bus_child_pnpinfo_t miibus_child_pnpinfo;
64dcf47c2bSMarius Strobl static device_detach_t miibus_detach;
6599172ab4SMarius Strobl static bus_hinted_child_t miibus_hinted_child;
6699172ab4SMarius Strobl static bus_print_child_t miibus_print_child;
67dcf47c2bSMarius Strobl static device_probe_t miibus_probe;
6899172ab4SMarius Strobl static bus_read_ivar_t miibus_read_ivar;
6999172ab4SMarius Strobl static miibus_readreg_t miibus_readreg;
7099172ab4SMarius Strobl static miibus_statchg_t miibus_statchg;
7199172ab4SMarius Strobl static miibus_writereg_t miibus_writereg;
7299172ab4SMarius Strobl static miibus_linkchg_t miibus_linkchg;
7399172ab4SMarius Strobl static miibus_mediainit_t miibus_mediainit;
7499172ab4SMarius Strobl 
753fcb7a53SMarius Strobl static unsigned char mii_bitreverse(unsigned char x);
76d0027533SBill Paul 
77d0027533SBill Paul static device_method_t miibus_methods[] = {
78d0027533SBill Paul 	/* device interface */
79d0027533SBill Paul 	DEVMETHOD(device_probe,		miibus_probe),
80d0027533SBill Paul 	DEVMETHOD(device_attach,	miibus_attach),
81d0027533SBill Paul 	DEVMETHOD(device_detach,	miibus_detach),
82d0027533SBill Paul 	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
83d0027533SBill Paul 
84d0027533SBill Paul 	/* bus interface */
859bcdfcaeSMarius Strobl 	DEVMETHOD(bus_print_child,	miibus_print_child),
86a55fb8a4SMarius Strobl 	DEVMETHOD(bus_read_ivar,	miibus_read_ivar),
87dc569c89SJohn Baldwin 	DEVMETHOD(bus_child_deleted,	miibus_child_deleted),
88ddfc9c4cSWarner Losh 	DEVMETHOD(bus_child_pnpinfo,	miibus_child_pnpinfo),
89ddfc9c4cSWarner Losh 	DEVMETHOD(bus_child_location,	miibus_child_location),
9099172ab4SMarius Strobl 	DEVMETHOD(bus_hinted_child,	miibus_hinted_child),
91d0027533SBill Paul 
92d0027533SBill Paul 	/* MII interface */
93d0027533SBill Paul 	DEVMETHOD(miibus_readreg,	miibus_readreg),
94d0027533SBill Paul 	DEVMETHOD(miibus_writereg,	miibus_writereg),
95d0027533SBill Paul 	DEVMETHOD(miibus_statchg,	miibus_statchg),
96eb3a7648SJonathan Lemon 	DEVMETHOD(miibus_linkchg,	miibus_linkchg),
97d0027533SBill Paul 	DEVMETHOD(miibus_mediainit,	miibus_mediainit),
98d0027533SBill Paul 
994b7ec270SMarius Strobl 	DEVMETHOD_END
100d0027533SBill Paul };
101d0027533SBill Paul 
1029174eab4SKornel Duleba DEFINE_CLASS_0(miibus, miibus_driver, miibus_methods, sizeof(struct mii_data));
103d0027533SBill Paul 
104bd3032d1SMarcel Moolenaar struct miibus_ivars {
10562d76917SMarcel Moolenaar 	if_t		ifp;
106bd3032d1SMarcel Moolenaar 	ifm_change_cb_t	ifmedia_upd;
107bd3032d1SMarcel Moolenaar 	ifm_stat_cb_t	ifmedia_sts;
10899172ab4SMarius Strobl 	u_int		mii_flags;
10999172ab4SMarius Strobl 	u_int		mii_offset;
110bd3032d1SMarcel Moolenaar };
111bd3032d1SMarcel Moolenaar 
112dcf47c2bSMarius Strobl static int
miibus_probe(device_t dev)11333d2d654SWarner Losh miibus_probe(device_t dev)
114d0027533SBill Paul {
115d0027533SBill Paul 
116d0027533SBill Paul 	device_set_desc(dev, "MII bus");
117d0027533SBill Paul 
118a55fb8a4SMarius Strobl 	return (BUS_PROBE_SPECIFIC);
119d0027533SBill Paul }
120d0027533SBill Paul 
1219174eab4SKornel Duleba int
miibus_attach(device_t dev)12233d2d654SWarner Losh miibus_attach(device_t dev)
123d0027533SBill Paul {
124bd3032d1SMarcel Moolenaar 	struct miibus_ivars	*ivars;
125a55fb8a4SMarius Strobl 	struct mii_attach_args	*ma;
126d0027533SBill Paul 	struct mii_data		*mii;
127a55fb8a4SMarius Strobl 	device_t		*children;
128a55fb8a4SMarius Strobl 	int			i, nchildren;
129d0027533SBill Paul 
130d0027533SBill Paul 	mii = device_get_softc(dev);
131a55fb8a4SMarius Strobl 	if (device_get_children(dev, &children, &nchildren) == 0) {
132a55fb8a4SMarius Strobl 		for (i = 0; i < nchildren; i++) {
133a55fb8a4SMarius Strobl 			ma = device_get_ivars(children[i]);
134a55fb8a4SMarius Strobl 			ma->mii_data = mii;
135a55fb8a4SMarius Strobl 		}
136a55fb8a4SMarius Strobl 		free(children, M_TEMP);
137a55fb8a4SMarius Strobl 	}
138a55fb8a4SMarius Strobl 	if (nchildren == 0) {
1397a05baaeSMarius Strobl 		device_printf(dev, "cannot get children\n");
140a55fb8a4SMarius Strobl 		return (ENXIO);
141a55fb8a4SMarius Strobl 	}
142bd3032d1SMarcel Moolenaar 	ivars = device_get_ivars(dev);
143bd3032d1SMarcel Moolenaar 	ifmedia_init(&mii->mii_media, IFM_IMASK, ivars->ifmedia_upd,
144bd3032d1SMarcel Moolenaar 	    ivars->ifmedia_sts);
145a55fb8a4SMarius Strobl 	mii->mii_ifp = ivars->ifp;
14662d76917SMarcel Moolenaar 	if_setcapabilitiesbit(mii->mii_ifp, IFCAP_LINKSTATE, 0);
14762d76917SMarcel Moolenaar 	if_setcapenablebit(mii->mii_ifp, IFCAP_LINKSTATE, 0);
148a55fb8a4SMarius Strobl 	LIST_INIT(&mii->mii_phys);
149d0027533SBill Paul 
150*18250ec6SJohn Baldwin 	bus_attach_children(dev);
151*18250ec6SJohn Baldwin 	return (0);
152d0027533SBill Paul }
153d0027533SBill Paul 
154dcf47c2bSMarius Strobl static int
miibus_detach(device_t dev)15533d2d654SWarner Losh miibus_detach(device_t dev)
156d0027533SBill Paul {
157d0027533SBill Paul 	struct mii_data		*mii;
158fb3a540bSMark Johnston 	struct miibus_ivars	*ivars;
159d0027533SBill Paul 
160fb3a540bSMark Johnston 	ivars = device_get_ivars(dev);
161d0027533SBill Paul 	bus_generic_detach(dev);
162d0027533SBill Paul 	mii = device_get_softc(dev);
163d0027533SBill Paul 	ifmedia_removeall(&mii->mii_media);
164fb3a540bSMark Johnston 	free(ivars, M_DEVBUF);
165d0027533SBill Paul 	mii->mii_ifp = NULL;
166d0027533SBill Paul 
167d0027533SBill Paul 	return (0);
168d0027533SBill Paul }
169d0027533SBill Paul 
170fb3a540bSMark Johnston static void
miibus_child_deleted(device_t dev,device_t child)171dc569c89SJohn Baldwin miibus_child_deleted(device_t dev, device_t child)
172fb3a540bSMark Johnston {
173fb3a540bSMark Johnston 	struct mii_attach_args *args;
174fb3a540bSMark Johnston 
175fb3a540bSMark Johnston 	args = device_get_ivars(child);
176fb3a540bSMark Johnston 	free(args, M_DEVBUF);
177fb3a540bSMark Johnston }
178fb3a540bSMark Johnston 
1799c1c2e99SAlfred Perlstein static int
miibus_print_child(device_t dev,device_t child)1809bcdfcaeSMarius Strobl miibus_print_child(device_t dev, device_t child)
1819bcdfcaeSMarius Strobl {
1829bcdfcaeSMarius Strobl 	struct mii_attach_args *ma;
1839bcdfcaeSMarius Strobl 	int retval;
1849bcdfcaeSMarius Strobl 
1859bcdfcaeSMarius Strobl 	ma = device_get_ivars(child);
1869bcdfcaeSMarius Strobl 	retval = bus_print_child_header(dev, child);
1879bcdfcaeSMarius Strobl 	retval += printf(" PHY %d", ma->mii_phyno);
1889bcdfcaeSMarius Strobl 	retval += bus_print_child_footer(dev, child);
1899bcdfcaeSMarius Strobl 
1909bcdfcaeSMarius Strobl 	return (retval);
1919bcdfcaeSMarius Strobl }
1929bcdfcaeSMarius Strobl 
1939bcdfcaeSMarius Strobl static int
miibus_read_ivar(device_t dev,device_t child __unused,int which,uintptr_t * result)194a55fb8a4SMarius Strobl miibus_read_ivar(device_t dev, device_t child __unused, int which,
195a55fb8a4SMarius Strobl     uintptr_t *result)
196a55fb8a4SMarius Strobl {
197a55fb8a4SMarius Strobl 	struct miibus_ivars *ivars;
198a55fb8a4SMarius Strobl 
199a55fb8a4SMarius Strobl 	/*
200a55fb8a4SMarius Strobl 	 * NB: this uses the instance variables of the miibus rather than
201a55fb8a4SMarius Strobl 	 * its PHY children.
202a55fb8a4SMarius Strobl 	 */
203a55fb8a4SMarius Strobl 	ivars = device_get_ivars(dev);
204a55fb8a4SMarius Strobl 	switch (which) {
205a55fb8a4SMarius Strobl 	case MIIBUS_IVAR_FLAGS:
206a55fb8a4SMarius Strobl 		*result = ivars->mii_flags;
207a55fb8a4SMarius Strobl 		break;
208a55fb8a4SMarius Strobl 	default:
209a55fb8a4SMarius Strobl 		return (ENOENT);
210a55fb8a4SMarius Strobl 	}
211a55fb8a4SMarius Strobl 	return (0);
212a55fb8a4SMarius Strobl }
213a55fb8a4SMarius Strobl 
214a55fb8a4SMarius Strobl static int
miibus_child_pnpinfo(device_t dev __unused,device_t child,struct sbuf * sb)215ddfc9c4cSWarner Losh miibus_child_pnpinfo(device_t dev __unused, device_t child, struct sbuf *sb)
216aa4c3a8cSWarner Losh {
217915d4d4aSMarius Strobl 	struct mii_attach_args *ma;
218915d4d4aSMarius Strobl 
219915d4d4aSMarius Strobl 	ma = device_get_ivars(child);
220ddfc9c4cSWarner Losh 	sbuf_printf(sb, "oui=0x%x model=0x%x rev=0x%x",
221915d4d4aSMarius Strobl 	    MII_OUI(ma->mii_id1, ma->mii_id2),
222915d4d4aSMarius Strobl 	    MII_MODEL(ma->mii_id2), MII_REV(ma->mii_id2));
223aa4c3a8cSWarner Losh 	return (0);
224aa4c3a8cSWarner Losh }
225aa4c3a8cSWarner Losh 
226aa4c3a8cSWarner Losh static int
miibus_child_location(device_t dev __unused,device_t child,struct sbuf * sb)227ddfc9c4cSWarner Losh miibus_child_location(device_t dev __unused, device_t child, struct sbuf *sb)
228aa4c3a8cSWarner Losh {
229915d4d4aSMarius Strobl 	struct mii_attach_args *ma;
230915d4d4aSMarius Strobl 
231915d4d4aSMarius Strobl 	ma = device_get_ivars(child);
232ddfc9c4cSWarner Losh 	sbuf_printf(sb, "phyno=%d", ma->mii_phyno);
233aa4c3a8cSWarner Losh 	return (0);
234aa4c3a8cSWarner Losh }
235aa4c3a8cSWarner Losh 
23699172ab4SMarius Strobl static void
miibus_hinted_child(device_t dev,const char * name,int unit)23799172ab4SMarius Strobl miibus_hinted_child(device_t dev, const char *name, int unit)
23899172ab4SMarius Strobl {
23999172ab4SMarius Strobl 	struct miibus_ivars *ivars;
24099172ab4SMarius Strobl 	struct mii_attach_args *args, *ma;
24199172ab4SMarius Strobl 	device_t *children, phy;
24299172ab4SMarius Strobl 	int i, nchildren;
24399172ab4SMarius Strobl 	u_int val;
24499172ab4SMarius Strobl 
24599172ab4SMarius Strobl 	if (resource_int_value(name, unit, "phyno", &val) != 0)
24699172ab4SMarius Strobl 		return;
24799172ab4SMarius Strobl 	if (device_get_children(dev, &children, &nchildren) != 0)
24899172ab4SMarius Strobl 		return;
24999172ab4SMarius Strobl 	ma = NULL;
25099172ab4SMarius Strobl 	for (i = 0; i < nchildren; i++) {
25199172ab4SMarius Strobl 		args = device_get_ivars(children[i]);
25299172ab4SMarius Strobl 		if (args->mii_phyno == val) {
25399172ab4SMarius Strobl 			ma = args;
25499172ab4SMarius Strobl 			break;
25599172ab4SMarius Strobl 		}
25699172ab4SMarius Strobl 	}
25799172ab4SMarius Strobl 	free(children, M_TEMP);
25899172ab4SMarius Strobl 
25999172ab4SMarius Strobl 	/*
26099172ab4SMarius Strobl 	 * Don't add a PHY that was automatically identified by having media
26199172ab4SMarius Strobl 	 * in its BMSR twice, only allow to alter its attach arguments.
26299172ab4SMarius Strobl 	 */
26399172ab4SMarius Strobl 	if (ma == NULL) {
26499172ab4SMarius Strobl 		ma = malloc(sizeof(struct mii_attach_args), M_DEVBUF,
26599172ab4SMarius Strobl 		    M_NOWAIT);
26699172ab4SMarius Strobl 		if (ma == NULL)
26799172ab4SMarius Strobl 			return;
26899172ab4SMarius Strobl 		phy = device_add_child(dev, name, unit);
26999172ab4SMarius Strobl 		if (phy == NULL) {
27099172ab4SMarius Strobl 			free(ma, M_DEVBUF);
27199172ab4SMarius Strobl 			return;
27299172ab4SMarius Strobl 		}
27399172ab4SMarius Strobl 		ivars = device_get_ivars(dev);
27499172ab4SMarius Strobl 		ma->mii_phyno = val;
27599172ab4SMarius Strobl 		ma->mii_offset = ivars->mii_offset++;
27699172ab4SMarius Strobl 		ma->mii_id1 = 0;
27799172ab4SMarius Strobl 		ma->mii_id2 = 0;
27899172ab4SMarius Strobl 		ma->mii_capmask = BMSR_DEFCAPMASK;
27999172ab4SMarius Strobl 		device_set_ivars(phy, ma);
28099172ab4SMarius Strobl 	}
28199172ab4SMarius Strobl 
28299172ab4SMarius Strobl 	if (resource_int_value(name, unit, "id1", &val) == 0)
28399172ab4SMarius Strobl 		ma->mii_id1 = val;
28499172ab4SMarius Strobl 	if (resource_int_value(name, unit, "id2", &val) == 0)
28599172ab4SMarius Strobl 		ma->mii_id2 = val;
28699172ab4SMarius Strobl 	if (resource_int_value(name, unit, "capmask", &val) == 0)
28799172ab4SMarius Strobl 		ma->mii_capmask = val;
28899172ab4SMarius Strobl }
28999172ab4SMarius Strobl 
290aa4c3a8cSWarner Losh static int
miibus_readreg(device_t dev,int phy,int reg)29133d2d654SWarner Losh miibus_readreg(device_t dev, int phy, int reg)
292d0027533SBill Paul {
293d0027533SBill Paul 	device_t		parent;
294d0027533SBill Paul 
295d0027533SBill Paul 	parent = device_get_parent(dev);
296d0027533SBill Paul 	return (MIIBUS_READREG(parent, phy, reg));
297d0027533SBill Paul }
298d0027533SBill Paul 
2999c1c2e99SAlfred Perlstein static int
miibus_writereg(device_t dev,int phy,int reg,int data)30033d2d654SWarner Losh miibus_writereg(device_t dev, int phy, int reg, int data)
301d0027533SBill Paul {
302d0027533SBill Paul 	device_t		parent;
303d0027533SBill Paul 
304d0027533SBill Paul 	parent = device_get_parent(dev);
305d0027533SBill Paul 	return (MIIBUS_WRITEREG(parent, phy, reg, data));
306d0027533SBill Paul }
307d0027533SBill Paul 
3089c1c2e99SAlfred Perlstein static void
miibus_statchg(device_t dev)30933d2d654SWarner Losh miibus_statchg(device_t dev)
310d0027533SBill Paul {
311d0027533SBill Paul 	device_t		parent;
312efd19b8fSGleb Smirnoff 	struct mii_data		*mii;
313d0027533SBill Paul 
314d0027533SBill Paul 	parent = device_get_parent(dev);
315d0027533SBill Paul 	MIIBUS_STATCHG(parent);
316efd19b8fSGleb Smirnoff 
317efd19b8fSGleb Smirnoff 	mii = device_get_softc(dev);
31862d76917SMarcel Moolenaar 	if_setbaudrate(mii->mii_ifp, ifmedia_baudrate(mii->mii_media_active));
319d0027533SBill Paul }
320d0027533SBill Paul 
321eb3a7648SJonathan Lemon static void
miibus_linkchg(device_t dev)32233d2d654SWarner Losh miibus_linkchg(device_t dev)
323eb3a7648SJonathan Lemon {
324eb3a7648SJonathan Lemon 	struct mii_data		*mii;
325eb3a7648SJonathan Lemon 	device_t		parent;
32694f5c9cfSSam Leffler 	int			link_state;
327eb3a7648SJonathan Lemon 
328eb3a7648SJonathan Lemon 	parent = device_get_parent(dev);
329eb3a7648SJonathan Lemon 	MIIBUS_LINKCHG(parent);
330eb3a7648SJonathan Lemon 
331eb3a7648SJonathan Lemon 	mii = device_get_softc(dev);
33294f5c9cfSSam Leffler 
33394f5c9cfSSam Leffler 	if (mii->mii_media_status & IFM_AVALID) {
33494f5c9cfSSam Leffler 		if (mii->mii_media_status & IFM_ACTIVE)
33594f5c9cfSSam Leffler 			link_state = LINK_STATE_UP;
33694f5c9cfSSam Leffler 		else
33794f5c9cfSSam Leffler 			link_state = LINK_STATE_DOWN;
33894f5c9cfSSam Leffler 	} else
33994f5c9cfSSam Leffler 		link_state = LINK_STATE_UNKNOWN;
34009a8241fSGleb Smirnoff 	if_link_state_change(mii->mii_ifp, link_state);
341eb3a7648SJonathan Lemon }
342eb3a7648SJonathan Lemon 
3439c1c2e99SAlfred Perlstein static void
miibus_mediainit(device_t dev)34433d2d654SWarner Losh miibus_mediainit(device_t dev)
345d0027533SBill Paul {
346d0027533SBill Paul 	struct mii_data		*mii;
347d0027533SBill Paul 	struct ifmedia_entry	*m;
348d0027533SBill Paul 	int			media = 0;
349d0027533SBill Paul 
35024a7e3d3SBill Paul 	/* Poke the parent in case it has any media of its own to add. */
35124a7e3d3SBill Paul 	MIIBUS_MEDIAINIT(device_get_parent(dev));
35224a7e3d3SBill Paul 
353d0027533SBill Paul 	mii = device_get_softc(dev);
35437d40066SPoul-Henning Kamp 	LIST_FOREACH(m, &mii->mii_media.ifm_list, ifm_list) {
355d0027533SBill Paul 		media = m->ifm_media;
356d0027533SBill Paul 		if (media == (IFM_ETHER | IFM_AUTO))
357d0027533SBill Paul 			break;
358d0027533SBill Paul 	}
359d0027533SBill Paul 
360d0027533SBill Paul 	ifmedia_set(&mii->mii_media, media);
361d0027533SBill Paul }
362d0027533SBill Paul 
363a55fb8a4SMarius Strobl /*
364a55fb8a4SMarius Strobl  * Helper function used by network interface drivers, attaches the miibus and
365a55fb8a4SMarius Strobl  * the PHYs to the network interface driver parent.
366a55fb8a4SMarius Strobl  */
3679c1c2e99SAlfred Perlstein int
mii_attach(device_t dev,device_t * miibus,if_t ifp,ifm_change_cb_t ifmedia_upd,ifm_stat_cb_t ifmedia_sts,int capmask,int phyloc,int offloc,int flags)36809a8241fSGleb Smirnoff mii_attach(device_t dev, device_t *miibus, if_t ifp,
369a55fb8a4SMarius Strobl     ifm_change_cb_t ifmedia_upd, ifm_stat_cb_t ifmedia_sts, int capmask,
370a55fb8a4SMarius Strobl     int phyloc, int offloc, int flags)
371d0027533SBill Paul {
372bd3032d1SMarcel Moolenaar 	struct miibus_ivars *ivars;
37399172ab4SMarius Strobl 	struct mii_attach_args *args, ma;
374a55fb8a4SMarius Strobl 	device_t *children, phy;
37599172ab4SMarius Strobl 	int bmsr, first, i, nchildren, phymax, phymin, rv;
37699172ab4SMarius Strobl 	uint32_t phymask;
377d0027533SBill Paul 
378d14bc723SWarner Losh 	bus_topo_assert();
379d14bc723SWarner Losh 
380a55fb8a4SMarius Strobl 	if (phyloc != MII_PHY_ANY && offloc != MII_OFFSET_ANY) {
3817a05baaeSMarius Strobl 		printf("%s: phyloc and offloc specified\n", __func__);
382a55fb8a4SMarius Strobl 		return (EINVAL);
383a55fb8a4SMarius Strobl 	}
384a55fb8a4SMarius Strobl 
385a55fb8a4SMarius Strobl 	if (offloc != MII_OFFSET_ANY && (offloc < 0 || offloc >= MII_NPHY)) {
386b1d634e6SKevin Lo 		printf("%s: invalid offloc %d\n", __func__, offloc);
387a55fb8a4SMarius Strobl 		return (EINVAL);
388a55fb8a4SMarius Strobl 	}
389a55fb8a4SMarius Strobl 
390a55fb8a4SMarius Strobl 	if (phyloc == MII_PHY_ANY) {
391a55fb8a4SMarius Strobl 		phymin = 0;
392a55fb8a4SMarius Strobl 		phymax = MII_NPHY - 1;
393a55fb8a4SMarius Strobl 	} else {
394a55fb8a4SMarius Strobl 		if (phyloc < 0 || phyloc >= MII_NPHY) {
395b1d634e6SKevin Lo 			printf("%s: invalid phyloc %d\n", __func__, phyloc);
396a55fb8a4SMarius Strobl 			return (EINVAL);
397a55fb8a4SMarius Strobl 		}
398a55fb8a4SMarius Strobl 		phymin = phymax = phyloc;
399a55fb8a4SMarius Strobl 	}
400a55fb8a4SMarius Strobl 
401a55fb8a4SMarius Strobl 	first = 0;
402a55fb8a4SMarius Strobl 	if (*miibus == NULL) {
403a55fb8a4SMarius Strobl 		first = 1;
404bd3032d1SMarcel Moolenaar 		ivars = malloc(sizeof(*ivars), M_DEVBUF, M_NOWAIT);
405bd3032d1SMarcel Moolenaar 		if (ivars == NULL)
40679a79ad9SMatt Jacob 			return (ENOMEM);
407a55fb8a4SMarius Strobl 		ivars->ifp = ifp;
408bd3032d1SMarcel Moolenaar 		ivars->ifmedia_upd = ifmedia_upd;
409bd3032d1SMarcel Moolenaar 		ivars->ifmedia_sts = ifmedia_sts;
410a55fb8a4SMarius Strobl 		ivars->mii_flags = flags;
4115b56413dSWarner Losh 		*miibus = device_add_child(dev, "miibus", DEVICE_UNIT_ANY);
412a55fb8a4SMarius Strobl 		if (*miibus == NULL) {
413a55fb8a4SMarius Strobl 			rv = ENXIO;
414a55fb8a4SMarius Strobl 			goto fail;
415a55fb8a4SMarius Strobl 		}
416a55fb8a4SMarius Strobl 		device_set_ivars(*miibus, ivars);
417a55fb8a4SMarius Strobl 	} else {
418a55fb8a4SMarius Strobl 		ivars = device_get_ivars(*miibus);
419a55fb8a4SMarius Strobl 		if (ivars->ifp != ifp || ivars->ifmedia_upd != ifmedia_upd ||
420a55fb8a4SMarius Strobl 		    ivars->ifmedia_sts != ifmedia_sts ||
421a55fb8a4SMarius Strobl 		    ivars->mii_flags != flags) {
4227a05baaeSMarius Strobl 			printf("%s: non-matching invariant\n", __func__);
423a55fb8a4SMarius Strobl 			return (EINVAL);
424a55fb8a4SMarius Strobl 		}
425a55fb8a4SMarius Strobl 		/*
426a55fb8a4SMarius Strobl 		 * Assignment of the attach arguments mii_data for the first
427a55fb8a4SMarius Strobl 		 * pass is done in miibus_attach(), i.e. once the miibus softc
428a55fb8a4SMarius Strobl 		 * has been allocated.
429a55fb8a4SMarius Strobl 		 */
430a55fb8a4SMarius Strobl 		ma.mii_data = device_get_softc(*miibus);
431a55fb8a4SMarius Strobl 	}
432d0027533SBill Paul 
433a55fb8a4SMarius Strobl 	ma.mii_capmask = capmask;
434a55fb8a4SMarius Strobl 
43599172ab4SMarius Strobl 	if (resource_int_value(device_get_name(*miibus),
43699172ab4SMarius Strobl 	    device_get_unit(*miibus), "phymask", &phymask) != 0)
43799172ab4SMarius Strobl 		phymask = 0xffffffff;
43899172ab4SMarius Strobl 
43999172ab4SMarius Strobl 	if (device_get_children(*miibus, &children, &nchildren) != 0) {
44099172ab4SMarius Strobl 		children = NULL;
44199172ab4SMarius Strobl 		nchildren = 0;
44299172ab4SMarius Strobl 	}
44399172ab4SMarius Strobl 	ivars->mii_offset = 0;
444a55fb8a4SMarius Strobl 	for (ma.mii_phyno = phymin; ma.mii_phyno <= phymax; ma.mii_phyno++) {
445a55fb8a4SMarius Strobl 		/*
446a55fb8a4SMarius Strobl 		 * Make sure we haven't already configured a PHY at this
447a55fb8a4SMarius Strobl 		 * address.  This allows mii_attach() to be called
448a55fb8a4SMarius Strobl 		 * multiple times.
449a55fb8a4SMarius Strobl 		 */
450a55fb8a4SMarius Strobl 		for (i = 0; i < nchildren; i++) {
451a55fb8a4SMarius Strobl 			args = device_get_ivars(children[i]);
452a55fb8a4SMarius Strobl 			if (args->mii_phyno == ma.mii_phyno) {
453a55fb8a4SMarius Strobl 				/*
454a55fb8a4SMarius Strobl 				 * Yes, there is already something
455a55fb8a4SMarius Strobl 				 * configured at this address.
456a55fb8a4SMarius Strobl 				 */
457a55fb8a4SMarius Strobl 				goto skip;
458a55fb8a4SMarius Strobl 			}
459a55fb8a4SMarius Strobl 		}
460a55fb8a4SMarius Strobl 
461a55fb8a4SMarius Strobl 		/*
462a55fb8a4SMarius Strobl 		 * Check to see if there is a PHY at this address.  Note,
463a55fb8a4SMarius Strobl 		 * many braindead PHYs report 0/0 in their ID registers,
464a55fb8a4SMarius Strobl 		 * so we test for media in the BMSR.
465a55fb8a4SMarius Strobl 		 */
466a55fb8a4SMarius Strobl 		bmsr = MIIBUS_READREG(dev, ma.mii_phyno, MII_BMSR);
467d0027533SBill Paul 		if (bmsr == 0 || bmsr == 0xffff ||
468098df780SBjoern A. Zeeb 		    (bmsr & (BMSR_EXTSTAT | BMSR_MEDIAMASK)) == 0) {
469d0027533SBill Paul 			/* Assume no PHY at this address. */
470d0027533SBill Paul 			continue;
471d0027533SBill Paul 		}
472d0027533SBill Paul 
473a55fb8a4SMarius Strobl 		/*
474a55fb8a4SMarius Strobl 		 * There is a PHY at this address.  If we were given an
475a55fb8a4SMarius Strobl 		 * `offset' locator, skip this PHY if it doesn't match.
476a55fb8a4SMarius Strobl 		 */
47799172ab4SMarius Strobl 		if (offloc != MII_OFFSET_ANY && offloc != ivars->mii_offset)
47899172ab4SMarius Strobl 			goto skip;
47999172ab4SMarius Strobl 
48099172ab4SMarius Strobl 		/*
48199172ab4SMarius Strobl 		 * Skip this PHY if it's not included in the phymask hint.
48299172ab4SMarius Strobl 		 */
48399172ab4SMarius Strobl 		if ((phymask & (1 << ma.mii_phyno)) == 0)
484a55fb8a4SMarius Strobl 			goto skip;
485a55fb8a4SMarius Strobl 
486a55fb8a4SMarius Strobl 		/*
487a55fb8a4SMarius Strobl 		 * Extract the IDs.  Braindead PHYs will be handled by
488a55fb8a4SMarius Strobl 		 * the `ukphy' driver, as we have no ID information to
489a55fb8a4SMarius Strobl 		 * match on.
490a55fb8a4SMarius Strobl 		 */
491a55fb8a4SMarius Strobl 		ma.mii_id1 = MIIBUS_READREG(dev, ma.mii_phyno, MII_PHYIDR1);
492a55fb8a4SMarius Strobl 		ma.mii_id2 = MIIBUS_READREG(dev, ma.mii_phyno, MII_PHYIDR2);
493a55fb8a4SMarius Strobl 
49499172ab4SMarius Strobl 		ma.mii_offset = ivars->mii_offset;
495a55fb8a4SMarius Strobl 		args = malloc(sizeof(struct mii_attach_args), M_DEVBUF,
496a55fb8a4SMarius Strobl 		    M_NOWAIT);
497a55fb8a4SMarius Strobl 		if (args == NULL)
498a55fb8a4SMarius Strobl 			goto skip;
499a55fb8a4SMarius Strobl 		bcopy((char *)&ma, (char *)args, sizeof(ma));
5005b56413dSWarner Losh 		phy = device_add_child(*miibus, NULL, DEVICE_UNIT_ANY);
501a55fb8a4SMarius Strobl 		if (phy == NULL) {
502a55fb8a4SMarius Strobl 			free(args, M_DEVBUF);
503a55fb8a4SMarius Strobl 			goto skip;
504a55fb8a4SMarius Strobl 		}
505a55fb8a4SMarius Strobl 		device_set_ivars(phy, args);
506a55fb8a4SMarius Strobl  skip:
50799172ab4SMarius Strobl 		ivars->mii_offset++;
508d0027533SBill Paul 	}
50999172ab4SMarius Strobl 	free(children, M_TEMP);
510d0027533SBill Paul 
511a55fb8a4SMarius Strobl 	if (first != 0) {
512ce0240c2SMarius Strobl 		rv = device_set_driver(*miibus, &miibus_driver);
51399172ab4SMarius Strobl 		if (rv != 0)
51499172ab4SMarius Strobl 			goto fail;
51599172ab4SMarius Strobl 		bus_enumerate_hinted_children(*miibus);
51699172ab4SMarius Strobl 		rv = device_get_children(*miibus, &children, &nchildren);
51799172ab4SMarius Strobl 		if (rv != 0)
51899172ab4SMarius Strobl 			goto fail;
51999172ab4SMarius Strobl 		free(children, M_TEMP);
52099172ab4SMarius Strobl 		if (nchildren == 0) {
521a55fb8a4SMarius Strobl 			rv = ENXIO;
522a55fb8a4SMarius Strobl 			goto fail;
523a55fb8a4SMarius Strobl 		}
524*18250ec6SJohn Baldwin 		bus_attach_children(dev);
525d0fae855SMarius Strobl 
526d0fae855SMarius Strobl 		/* Attaching of the PHY drivers is done in miibus_attach(). */
527d0fae855SMarius Strobl 		return (0);
528a55fb8a4SMarius Strobl 	}
529*18250ec6SJohn Baldwin 	bus_attach_children(*miibus);
530d0027533SBill Paul 
531d0027533SBill Paul 	return (0);
532a55fb8a4SMarius Strobl 
533a55fb8a4SMarius Strobl  fail:
534a55fb8a4SMarius Strobl 	if (*miibus != NULL)
535a55fb8a4SMarius Strobl 		device_delete_child(dev, *miibus);
536a55fb8a4SMarius Strobl 	free(ivars, M_DEVBUF);
537a55fb8a4SMarius Strobl 	if (first != 0)
538a55fb8a4SMarius Strobl 		*miibus = NULL;
539a55fb8a4SMarius Strobl 	return (rv);
540a55fb8a4SMarius Strobl }
541a55fb8a4SMarius Strobl 
542d0027533SBill Paul /*
543d0027533SBill Paul  * Media changed; notify all PHYs.
544d0027533SBill Paul  */
545d0027533SBill Paul int
mii_mediachg(struct mii_data * mii)54633d2d654SWarner Losh mii_mediachg(struct mii_data *mii)
547d0027533SBill Paul {
548d0027533SBill Paul 	struct mii_softc *child;
549de1add1eSMarius Strobl 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
550d0027533SBill Paul 	int rv;
551d0027533SBill Paul 
552d0027533SBill Paul 	mii->mii_media_status = 0;
553d0027533SBill Paul 	mii->mii_media_active = IFM_NONE;
554d0027533SBill Paul 
55537d40066SPoul-Henning Kamp 	LIST_FOREACH(child, &mii->mii_phys, mii_list) {
556de1add1eSMarius Strobl 		/*
557de1add1eSMarius Strobl 		 * If the media indicates a different PHY instance,
558de1add1eSMarius Strobl 		 * isolate this one.
559de1add1eSMarius Strobl 		 */
560de1add1eSMarius Strobl 		if (IFM_INST(ife->ifm_media) != child->mii_inst) {
561de1add1eSMarius Strobl 			if ((child->mii_flags & MIIF_NOISOLATE) != 0) {
562de1add1eSMarius Strobl 				device_printf(child->mii_dev, "%s: "
563de1add1eSMarius Strobl 				    "can't handle non-zero PHY instance %d\n",
564de1add1eSMarius Strobl 				    __func__, child->mii_inst);
565de1add1eSMarius Strobl 				continue;
566de1add1eSMarius Strobl 			}
567de1add1eSMarius Strobl 			PHY_WRITE(child, MII_BMCR, PHY_READ(child, MII_BMCR) |
568de1add1eSMarius Strobl 			    BMCR_ISO);
569de1add1eSMarius Strobl 			continue;
570de1add1eSMarius Strobl 		}
5713fcb7a53SMarius Strobl 		rv = PHY_SERVICE(child, mii, MII_MEDIACHG);
572d0027533SBill Paul 		if (rv)
573d0027533SBill Paul 			return (rv);
574d0027533SBill Paul 	}
575d0027533SBill Paul 	return (0);
576d0027533SBill Paul }
577d0027533SBill Paul 
578d0027533SBill Paul /*
579d0027533SBill Paul  * Call the PHY tick routines, used during autonegotiation.
580d0027533SBill Paul  */
581d0027533SBill Paul void
mii_tick(struct mii_data * mii)58233d2d654SWarner Losh mii_tick(struct mii_data *mii)
583d0027533SBill Paul {
584d0027533SBill Paul 	struct mii_softc *child;
585de1add1eSMarius Strobl 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
586d0027533SBill Paul 
587de1add1eSMarius Strobl 	LIST_FOREACH(child, &mii->mii_phys, mii_list) {
588de1add1eSMarius Strobl 		/*
589de1add1eSMarius Strobl 		 * If this PHY instance isn't currently selected, just skip
590de1add1eSMarius Strobl 		 * it.
591de1add1eSMarius Strobl 		 */
592de1add1eSMarius Strobl 		if (IFM_INST(ife->ifm_media) != child->mii_inst)
593de1add1eSMarius Strobl 			continue;
5943fcb7a53SMarius Strobl 		(void)PHY_SERVICE(child, mii, MII_TICK);
595d0027533SBill Paul 	}
596de1add1eSMarius Strobl }
597d0027533SBill Paul 
598d0027533SBill Paul /*
599d0027533SBill Paul  * Get media status from PHYs.
600d0027533SBill Paul  */
601d0027533SBill Paul void
mii_pollstat(struct mii_data * mii)60233d2d654SWarner Losh mii_pollstat(struct mii_data *mii)
603d0027533SBill Paul {
604d0027533SBill Paul 	struct mii_softc *child;
605de1add1eSMarius Strobl 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
606d0027533SBill Paul 
607d0027533SBill Paul 	mii->mii_media_status = 0;
608d0027533SBill Paul 	mii->mii_media_active = IFM_NONE;
609d0027533SBill Paul 
610de1add1eSMarius Strobl 	LIST_FOREACH(child, &mii->mii_phys, mii_list) {
611de1add1eSMarius Strobl 		/*
612de1add1eSMarius Strobl 		 * If we're not polling this PHY instance, just skip it.
613de1add1eSMarius Strobl 		 */
614de1add1eSMarius Strobl 		if (IFM_INST(ife->ifm_media) != child->mii_inst)
615de1add1eSMarius Strobl 			continue;
6163fcb7a53SMarius Strobl 		(void)PHY_SERVICE(child, mii, MII_POLLSTAT);
617d0027533SBill Paul 	}
618de1add1eSMarius Strobl }
619279fe8d1SPoul-Henning Kamp 
6203fcb7a53SMarius Strobl static unsigned char
mii_bitreverse(unsigned char x)6213fcb7a53SMarius Strobl mii_bitreverse(unsigned char x)
6223fcb7a53SMarius Strobl {
62329658c96SDimitry Andric 	static unsigned const char nibbletab[16] = {
6243fcb7a53SMarius Strobl 		0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15
6253fcb7a53SMarius Strobl 	};
6263fcb7a53SMarius Strobl 
6273fcb7a53SMarius Strobl 	return ((nibbletab[x & 15] << 4) | nibbletab[x >> 4]);
6283fcb7a53SMarius Strobl }
6293fcb7a53SMarius Strobl 
6303fcb7a53SMarius Strobl u_int
mii_oui(u_int id1,u_int id2)6313fcb7a53SMarius Strobl mii_oui(u_int id1, u_int id2)
6323fcb7a53SMarius Strobl {
6333fcb7a53SMarius Strobl 	u_int h;
6343fcb7a53SMarius Strobl 
6353fcb7a53SMarius Strobl 	h = (id1 << 6) | (id2 >> 10);
6363fcb7a53SMarius Strobl 
6373fcb7a53SMarius Strobl 	return ((mii_bitreverse(h >> 16) << 16) |
6383fcb7a53SMarius Strobl 	    (mii_bitreverse((h >> 8) & 0xff) << 8) |
6393fcb7a53SMarius Strobl 	    mii_bitreverse(h & 0xff));
6403fcb7a53SMarius Strobl }
6417e310d2dSGleb Smirnoff 
6427e310d2dSGleb Smirnoff int
mii_phy_mac_match(struct mii_softc * mii,const char * name)6437e310d2dSGleb Smirnoff mii_phy_mac_match(struct mii_softc *mii, const char *name)
6447e310d2dSGleb Smirnoff {
6457e310d2dSGleb Smirnoff 
6467e310d2dSGleb Smirnoff 	return (strcmp(device_get_name(device_get_parent(mii->mii_dev)),
6477e310d2dSGleb Smirnoff 	    name) == 0);
6487e310d2dSGleb Smirnoff }
6497e310d2dSGleb Smirnoff 
6507e310d2dSGleb Smirnoff int
mii_dev_mac_match(device_t parent,const char * name)6517e310d2dSGleb Smirnoff mii_dev_mac_match(device_t parent, const char *name)
6527e310d2dSGleb Smirnoff {
6537e310d2dSGleb Smirnoff 
6547e310d2dSGleb Smirnoff 	return (strcmp(device_get_name(device_get_parent(
6557e310d2dSGleb Smirnoff 	    device_get_parent(parent))), name) == 0);
6567e310d2dSGleb Smirnoff }
6577e310d2dSGleb Smirnoff 
6587e310d2dSGleb Smirnoff void *
mii_phy_mac_softc(struct mii_softc * mii)6597e310d2dSGleb Smirnoff mii_phy_mac_softc(struct mii_softc *mii)
6607e310d2dSGleb Smirnoff {
6617e310d2dSGleb Smirnoff 
6627e310d2dSGleb Smirnoff 	return (device_get_softc(device_get_parent(mii->mii_dev)));
6637e310d2dSGleb Smirnoff }
6647e310d2dSGleb Smirnoff 
6657e310d2dSGleb Smirnoff void *
mii_dev_mac_softc(device_t parent)6667e310d2dSGleb Smirnoff mii_dev_mac_softc(device_t parent)
6677e310d2dSGleb Smirnoff {
6687e310d2dSGleb Smirnoff 
6697e310d2dSGleb Smirnoff 	return (device_get_softc(device_get_parent(device_get_parent(parent))));
6707e310d2dSGleb Smirnoff }
671