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