1 /* $NetBSD: mii.c,v 1.12 1999/08/03 19:41:49 drochner Exp $ */
4 * Copyright (c) 1998 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
9 * NASA Ames Research Center.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
35 #define malloc(size, tag, flags) kernel_malloc(size, tag, flags)
36 #define free(pointer, tag) kernel_free(pointer, tag)
39 #include <sys/cdefs.h>
40 __FBSDID("$FreeBSD$");
43 * MII bus layer, glues MII-capable network interface drivers to sharable
44 * PHY drivers. This exports an interface compatible with BSD/OS 3.0's,
45 * plus some NetBSD extensions.
48 #include <sys/param.h>
49 #include <sys/systm.h>
50 #include <sys/socket.h>
51 #include <sys/malloc.h>
52 #include <sys/module.h>
56 #include <net/if_media.h>
57 #include <net/route.h>
59 #include <dev/mii/mii.h>
60 #include <dev/mii/miivar.h>
62 MODULE_VERSION(miibus
, 1);
64 #include "miibus_if.h"
66 static int miibus_print_child(device_t dev
, device_t child
);
67 static int miibus_read_ivar(device_t dev
, device_t child
, int which
,
69 static int miibus_child_location_str(device_t bus
, device_t child
, char *buf
,
71 static int miibus_child_pnpinfo_str(device_t bus
, device_t child
, char *buf
,
73 static int miibus_readreg(device_t
, int, int);
74 static int miibus_writereg(device_t
, int, int, int);
75 static void miibus_statchg(device_t
);
76 static void miibus_linkchg(device_t
);
77 static void miibus_mediainit(device_t
);
78 static unsigned char mii_bitreverse(unsigned char x
);
80 static device_method_t miibus_methods
[] = {
81 /* device interface */
82 DEVMETHOD(device_probe
, miibus_probe
),
83 DEVMETHOD(device_attach
, miibus_attach
),
84 DEVMETHOD(device_detach
, miibus_detach
),
85 DEVMETHOD(device_shutdown
, bus_generic_shutdown
),
88 DEVMETHOD(bus_print_child
, miibus_print_child
),
89 DEVMETHOD(bus_read_ivar
, miibus_read_ivar
),
90 DEVMETHOD(bus_driver_added
, bus_generic_driver_added
),
91 DEVMETHOD(bus_child_pnpinfo_str
, miibus_child_pnpinfo_str
),
92 DEVMETHOD(bus_child_location_str
, miibus_child_location_str
),
95 DEVMETHOD(miibus_readreg
, miibus_readreg
),
96 DEVMETHOD(miibus_writereg
, miibus_writereg
),
97 DEVMETHOD(miibus_statchg
, miibus_statchg
),
98 DEVMETHOD(miibus_linkchg
, miibus_linkchg
),
99 DEVMETHOD(miibus_mediainit
, miibus_mediainit
),
104 devclass_t miibus_devclass
;
106 driver_t miibus_driver
= {
109 sizeof(struct mii_data
)
112 struct miibus_ivars
{
114 ifm_change_cb_t ifmedia_upd
;
115 ifm_stat_cb_t ifmedia_sts
;
120 miibus_probe(device_t dev
)
123 device_set_desc(dev
, "MII bus");
125 return (BUS_PROBE_SPECIFIC
);
129 miibus_attach(device_t dev
)
131 struct miibus_ivars
*ivars
;
132 struct mii_attach_args
*ma
;
133 struct mii_data
*mii
;
137 mii
= device_get_softc(dev
);
139 if (device_get_children(dev
, &children
, &nchildren
) == 0) {
140 for (i
= 0; i
< nchildren
; i
++) {
141 ma
= device_get_ivars(children
[i
]);
144 free(children
, M_TEMP
);
146 if (nchildren
== 0) {
147 device_printf(dev
, "cannot get children\n");
150 ivars
= device_get_ivars(dev
);
151 ifmedia_init(&mii
->mii_media
, IFM_IMASK
, ivars
->ifmedia_upd
,
153 mii
->mii_ifp
= ivars
->ifp
;
154 mii
->mii_ifp
->if_capabilities
|= IFCAP_LINKSTATE
;
155 mii
->mii_ifp
->if_capenable
|= IFCAP_LINKSTATE
;
156 LIST_INIT(&mii
->mii_phys
);
158 return (bus_generic_attach(dev
));
162 miibus_detach(device_t dev
)
164 struct mii_data
*mii
;
166 bus_generic_detach(dev
);
167 mii
= device_get_softc(dev
);
168 ifmedia_removeall(&mii
->mii_media
);
175 miibus_print_child(device_t dev
, device_t child
)
177 struct mii_attach_args
*ma
;
180 ma
= device_get_ivars(child
);
181 retval
= bus_print_child_header(dev
, child
);
182 retval
+= printf(" PHY %d", ma
->mii_phyno
);
183 retval
+= bus_print_child_footer(dev
, child
);
189 miibus_read_ivar(device_t dev
, device_t child __unused
, int which
,
192 struct miibus_ivars
*ivars
;
195 * NB: this uses the instance variables of the miibus rather than
198 ivars
= device_get_ivars(dev
);
200 case MIIBUS_IVAR_FLAGS
:
201 *result
= ivars
->mii_flags
;
210 miibus_child_pnpinfo_str(device_t bus __unused
, device_t child
, char *buf
,
213 struct mii_attach_args
*ma
;
215 ma
= device_get_ivars(child
);
216 snprintf(buf
, buflen
, "oui=0x%x model=0x%x rev=0x%x",
217 MII_OUI(ma
->mii_id1
, ma
->mii_id2
),
218 MII_MODEL(ma
->mii_id2
), MII_REV(ma
->mii_id2
));
223 miibus_child_location_str(device_t bus __unused
, device_t child
, char *buf
,
226 struct mii_attach_args
*ma
;
228 ma
= device_get_ivars(child
);
229 snprintf(buf
, buflen
, "phyno=%d", ma
->mii_phyno
);
234 miibus_readreg(device_t dev
, int phy
, int reg
)
238 parent
= device_get_parent(dev
);
239 return (MIIBUS_READREG(parent
, phy
, reg
));
243 miibus_writereg(device_t dev
, int phy
, int reg
, int data
)
247 parent
= device_get_parent(dev
);
248 return (MIIBUS_WRITEREG(parent
, phy
, reg
, data
));
252 miibus_statchg(device_t dev
)
255 struct mii_data
*mii
;
257 parent
= device_get_parent(dev
);
258 MIIBUS_STATCHG(parent
);
260 mii
= device_get_softc(dev
);
261 mii
->mii_ifp
->if_baudrate
= ifmedia_baudrate(mii
->mii_media_active
);
265 miibus_linkchg(device_t dev
)
267 struct mii_data
*mii
;
271 parent
= device_get_parent(dev
);
272 MIIBUS_LINKCHG(parent
);
274 mii
= device_get_softc(dev
);
276 if (mii
->mii_media_status
& IFM_AVALID
) {
277 if (mii
->mii_media_status
& IFM_ACTIVE
)
278 link_state
= LINK_STATE_UP
;
280 link_state
= LINK_STATE_DOWN
;
282 link_state
= LINK_STATE_UNKNOWN
;
283 if_link_state_change(mii
->mii_ifp
, link_state
);
287 miibus_mediainit(device_t dev
)
289 struct mii_data
*mii
;
290 struct ifmedia_entry
*m
;
293 /* Poke the parent in case it has any media of its own to add. */
294 MIIBUS_MEDIAINIT(device_get_parent(dev
));
296 mii
= device_get_softc(dev
);
297 LIST_FOREACH(m
, &mii
->mii_media
.ifm_list
, ifm_list
) {
298 media
= m
->ifm_media
;
299 if (media
== (IFM_ETHER
| IFM_AUTO
))
303 ifmedia_set(&mii
->mii_media
, media
);
307 * Helper function used by network interface drivers, attaches the miibus and
308 * the PHYs to the network interface driver parent.
311 mii_attach(device_t dev
, device_t
*miibus
, struct ifnet
*ifp
,
312 ifm_change_cb_t ifmedia_upd
, ifm_stat_cb_t ifmedia_sts
, int capmask
,
313 int phyloc
, int offloc
, int flags
)
315 struct miibus_ivars
*ivars
;
316 struct mii_attach_args ma
, *args
;
317 device_t
*children
, phy
;
318 int bmsr
, first
, i
, nchildren
, offset
, phymax
, phymin
, rv
;
320 if (phyloc
!= MII_PHY_ANY
&& offloc
!= MII_OFFSET_ANY
) {
321 printf("%s: phyloc and offloc specified\n", __func__
);
325 if (offloc
!= MII_OFFSET_ANY
&& (offloc
< 0 || offloc
>= MII_NPHY
)) {
326 printf("%s: ivalid offloc %d\n", __func__
, offloc
);
330 if (phyloc
== MII_PHY_ANY
) {
332 phymax
= MII_NPHY
- 1;
334 if (phyloc
< 0 || phyloc
>= MII_NPHY
) {
335 printf("%s: ivalid phyloc %d\n", __func__
, phyloc
);
338 phymin
= phymax
= phyloc
;
342 if (*miibus
== NULL
) {
344 ivars
= malloc(sizeof(*ivars
), M_DEVBUF
, M_NOWAIT
);
348 ivars
->ifmedia_upd
= ifmedia_upd
;
349 ivars
->ifmedia_sts
= ifmedia_sts
;
350 ivars
->mii_flags
= flags
;
351 *miibus
= device_add_child(dev
, "miibus", -1);
352 if (*miibus
== NULL
) {
356 device_set_ivars(*miibus
, ivars
);
358 ivars
= device_get_ivars(*miibus
);
359 if (ivars
->ifp
!= ifp
|| ivars
->ifmedia_upd
!= ifmedia_upd
||
360 ivars
->ifmedia_sts
!= ifmedia_sts
||
361 ivars
->mii_flags
!= flags
) {
362 printf("%s: non-matching invariant\n", __func__
);
366 * Assignment of the attach arguments mii_data for the first
367 * pass is done in miibus_attach(), i.e. once the miibus softc
368 * has been allocated.
370 ma
.mii_data
= device_get_softc(*miibus
);
373 ma
.mii_capmask
= capmask
;
377 for (ma
.mii_phyno
= phymin
; ma
.mii_phyno
<= phymax
; ma
.mii_phyno
++) {
379 * Make sure we haven't already configured a PHY at this
380 * address. This allows mii_attach() to be called
383 if (device_get_children(*miibus
, &children
, &nchildren
) == 0) {
384 for (i
= 0; i
< nchildren
; i
++) {
385 args
= device_get_ivars(children
[i
]);
386 if (args
->mii_phyno
== ma
.mii_phyno
) {
388 * Yes, there is already something
389 * configured at this address.
391 free(children
, M_TEMP
);
395 free(children
, M_TEMP
);
399 * Check to see if there is a PHY at this address. Note,
400 * many braindead PHYs report 0/0 in their ID registers,
401 * so we test for media in the BMSR.
403 bmsr
= MIIBUS_READREG(dev
, ma
.mii_phyno
, MII_BMSR
);
404 if (bmsr
== 0 || bmsr
== 0xffff ||
405 (bmsr
& (BMSR_EXTSTAT
| BMSR_MEDIAMASK
)) == 0) {
406 /* Assume no PHY at this address. */
411 * There is a PHY at this address. If we were given an
412 * `offset' locator, skip this PHY if it doesn't match.
414 if (offloc
!= MII_OFFSET_ANY
&& offloc
!= offset
)
418 * Extract the IDs. Braindead PHYs will be handled by
419 * the `ukphy' driver, as we have no ID information to
422 ma
.mii_id1
= MIIBUS_READREG(dev
, ma
.mii_phyno
, MII_PHYIDR1
);
423 ma
.mii_id2
= MIIBUS_READREG(dev
, ma
.mii_phyno
, MII_PHYIDR2
);
425 ma
.mii_offset
= offset
;
426 args
= malloc(sizeof(struct mii_attach_args
), M_DEVBUF
,
430 bcopy((char *)&ma
, (char *)args
, sizeof(ma
));
431 phy
= device_add_child(*miibus
, NULL
, -1);
433 free(args
, M_DEVBUF
);
436 device_set_ivars(phy
, args
);
446 rv
= bus_generic_attach(dev
);
450 /* Attaching of the PHY drivers is done in miibus_attach(). */
453 rv
= bus_generic_attach(*miibus
);
461 device_delete_child(dev
, *miibus
);
462 free(ivars
, M_DEVBUF
);
469 * Media changed; notify all PHYs.
472 mii_mediachg(struct mii_data
*mii
)
474 struct mii_softc
*child
;
475 struct ifmedia_entry
*ife
= mii
->mii_media
.ifm_cur
;
478 mii
->mii_media_status
= 0;
479 mii
->mii_media_active
= IFM_NONE
;
481 LIST_FOREACH(child
, &mii
->mii_phys
, mii_list
) {
483 * If the media indicates a different PHY instance,
486 if (IFM_INST(ife
->ifm_media
) != child
->mii_inst
) {
487 if ((child
->mii_flags
& MIIF_NOISOLATE
) != 0) {
488 device_printf(child
->mii_dev
, "%s: "
489 "can't handle non-zero PHY instance %d\n",
490 __func__
, child
->mii_inst
);
493 PHY_WRITE(child
, MII_BMCR
, PHY_READ(child
, MII_BMCR
) |
497 rv
= PHY_SERVICE(child
, mii
, MII_MEDIACHG
);
505 * Call the PHY tick routines, used during autonegotiation.
508 mii_tick(struct mii_data
*mii
)
510 struct mii_softc
*child
;
511 struct ifmedia_entry
*ife
= mii
->mii_media
.ifm_cur
;
513 LIST_FOREACH(child
, &mii
->mii_phys
, mii_list
) {
515 * If this PHY instance isn't currently selected, just skip
518 if (IFM_INST(ife
->ifm_media
) != child
->mii_inst
)
520 (void)PHY_SERVICE(child
, mii
, MII_TICK
);
525 * Get media status from PHYs.
528 mii_pollstat(struct mii_data
*mii
)
530 struct mii_softc
*child
;
531 struct ifmedia_entry
*ife
= mii
->mii_media
.ifm_cur
;
533 mii
->mii_media_status
= 0;
534 mii
->mii_media_active
= IFM_NONE
;
536 LIST_FOREACH(child
, &mii
->mii_phys
, mii_list
) {
538 * If we're not polling this PHY instance, just skip it.
540 if (IFM_INST(ife
->ifm_media
) != child
->mii_inst
)
542 (void)PHY_SERVICE(child
, mii
, MII_POLLSTAT
);
547 * Inform the PHYs that the interface is down.
550 mii_down(struct mii_data
*mii
)
552 struct mii_softc
*child
;
554 LIST_FOREACH(child
, &mii
->mii_phys
, mii_list
)
559 mii_bitreverse(unsigned char x
)
561 unsigned const char const nibbletab
[16] = {
562 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15
565 return ((nibbletab
[x
& 15] << 4) | nibbletab
[x
>> 4]);
569 mii_oui(u_int id1
, u_int id2
)
573 h
= (id1
<< 6) | (id2
>> 10);
575 return ((mii_bitreverse(h
>> 16) << 16) |
576 (mii_bitreverse((h
>> 8) & 0xff) << 8) |
577 mii_bitreverse(h
& 0xff));