1 /* $NetBSD: mii.c,v 1.49 2009/11/12 19:38:35 dyoung Exp $ */
4 * Copyright (c) 1998, 2000 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.
34 * MII bus layer, glues MII-capable network interface drivers to sharable
38 #include <sys/cdefs.h>
39 __KERNEL_RCSID(0, "$NetBSD: mii.c,v 1.49 2009/11/12 19:38:35 dyoung Exp $");
41 #include <sys/param.h>
42 #include <sys/device.h>
43 #include <sys/systm.h>
44 #include <sys/socket.h>
47 #include <net/if_media.h>
49 #include <dev/mii/mii.h>
50 #include <dev/mii/miivar.h>
54 static int mii_print(void *, const char *);
57 * Helper function used by network interface drivers, attaches PHYs
58 * to the network interface driver parent.
61 mii_attach(device_t parent
, struct mii_data
*mii
, int capmask
,
62 int phyloc
, int offloc
, int flags
)
64 struct mii_attach_args ma
;
65 struct mii_softc
*child
;
68 int locs
[MIICF_NLOCS
];
70 if (phyloc
!= MII_PHY_ANY
&& offloc
!= MII_OFFSET_ANY
)
71 panic("mii_attach: phyloc and offloc specified");
73 if (phyloc
== MII_PHY_ANY
) {
75 phymax
= MII_NPHY
- 1;
77 phymin
= phymax
= phyloc
;
79 if ((mii
->mii_flags
& MIIF_INITDONE
) == 0) {
80 LIST_INIT(&mii
->mii_phys
);
81 mii
->mii_flags
|= MIIF_INITDONE
;
84 for (ma
.mii_phyno
= phymin
; ma
.mii_phyno
<= phymax
; ma
.mii_phyno
++) {
86 * Make sure we haven't already configured a PHY at this
87 * address. This allows mii_attach() to be called
90 LIST_FOREACH(child
, &mii
->mii_phys
, mii_list
) {
91 if (child
->mii_phy
== ma
.mii_phyno
) {
93 * Yes, there is already something
94 * configured at this address.
102 * Check to see if there is a PHY at this address. Note,
103 * many braindead PHYs report 0/0 in their ID registers,
104 * so we test for media in the BMSR.
106 bmsr
= (*mii
->mii_readreg
)(parent
, ma
.mii_phyno
, MII_BMSR
);
107 if (bmsr
== 0 || bmsr
== 0xffff ||
108 (bmsr
& (BMSR_EXTSTAT
|BMSR_MEDIAMASK
)) == 0) {
109 /* Assume no PHY at this address. */
114 * There is a PHY at this address. If we were given an
115 * `offset' locator, skip this PHY if it doesn't match.
117 if (offloc
!= MII_OFFSET_ANY
&& offloc
!= offset
) {
123 * Extract the IDs. Braindead PHYs will be handled by
124 * the `ukphy' driver, as we have no ID information to
127 ma
.mii_id1
= (*mii
->mii_readreg
)(parent
, ma
.mii_phyno
,
129 ma
.mii_id2
= (*mii
->mii_readreg
)(parent
, ma
.mii_phyno
,
133 ma
.mii_capmask
= capmask
;
134 ma
.mii_flags
= flags
| (mii
->mii_flags
& MIIF_INHERIT_MASK
);
136 locs
[MIICF_PHY
] = ma
.mii_phyno
;
138 child
= device_private(config_found_sm_loc(parent
, "mii",
139 locs
, &ma
, mii_print
, config_stdsubmatch
));
142 * Link it up in the parent's MII data.
144 callout_init(&child
->mii_nway_ch
, 0);
145 LIST_INSERT_HEAD(&mii
->mii_phys
, child
, mii_list
);
146 child
->mii_offset
= offset
;
154 mii_detach(struct mii_data
*mii
, int phyloc
, int offloc
)
156 struct mii_softc
*child
, *nchild
;
158 if (phyloc
!= MII_PHY_ANY
&& offloc
!= MII_PHY_ANY
)
159 panic("mii_detach: phyloc and offloc specified");
161 if ((mii
->mii_flags
& MIIF_INITDONE
) == 0)
164 for (child
= LIST_FIRST(&mii
->mii_phys
);
165 child
!= NULL
; child
= nchild
) {
166 nchild
= LIST_NEXT(child
, mii_list
);
167 if (phyloc
!= MII_PHY_ANY
|| offloc
!= MII_OFFSET_ANY
) {
168 if (phyloc
!= MII_PHY_ANY
&&
169 phyloc
!= child
->mii_phy
)
171 if (offloc
!= MII_OFFSET_ANY
&&
172 offloc
!= child
->mii_offset
)
175 (void)config_detach(child
->mii_dev
, DETACH_FORCE
);
180 mii_print(void *aux
, const char *pnp
)
182 struct mii_attach_args
*ma
= aux
;
185 aprint_normal("OUI 0x%06x model 0x%04x rev %d at %s",
186 MII_OUI(ma
->mii_id1
, ma
->mii_id2
), MII_MODEL(ma
->mii_id2
),
187 MII_REV(ma
->mii_id2
), pnp
);
189 aprint_normal(" phy %d", ma
->mii_phyno
);
194 phy_service(struct mii_softc
*sc
, struct mii_data
*mii
, int cmd
)
196 if (!device_is_active(sc
->mii_dev
))
198 return PHY_SERVICE(sc
, mii
, cmd
);
202 mii_ifmedia_change(struct mii_data
*mii
)
204 return ifmedia_change(&mii
->mii_media
, mii
->mii_ifp
);
208 * Media changed; notify all PHYs.
211 mii_mediachg(struct mii_data
*mii
)
213 struct mii_softc
*child
;
216 mii
->mii_media_status
= 0;
217 mii
->mii_media_active
= IFM_NONE
;
219 LIST_FOREACH(child
, &mii
->mii_phys
, mii_list
) {
220 rv
= phy_service(child
, mii
, MII_MEDIACHG
);
228 * Call the PHY tick routines, used during autonegotiation.
231 mii_tick(struct mii_data
*mii
)
233 struct mii_softc
*child
;
235 LIST_FOREACH(child
, &mii
->mii_phys
, mii_list
)
236 (void)phy_service(child
, mii
, MII_TICK
);
240 * Get media status from PHYs.
243 mii_pollstat(struct mii_data
*mii
)
245 struct mii_softc
*child
;
247 mii
->mii_media_status
= 0;
248 mii
->mii_media_active
= IFM_NONE
;
250 LIST_FOREACH(child
, &mii
->mii_phys
, mii_list
)
251 (void)phy_service(child
, mii
, MII_POLLSTAT
);
255 * Inform the PHYs that the interface is down.
258 mii_down(struct mii_data
*mii
)
260 struct mii_softc
*child
;
262 LIST_FOREACH(child
, &mii
->mii_phys
, mii_list
)
263 (void)phy_service(child
, mii
, MII_DOWN
);
267 bitreverse(unsigned char x
)
269 static unsigned char nibbletab
[16] = {
270 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15
273 return ((nibbletab
[x
& 15] << 4) | nibbletab
[x
>> 4]);
277 mii_oui(u_int id1
, u_int id2
)
281 h
= (id1
<< 6) | (id2
>> 10);
283 return ((bitreverse(h
>> 16) << 16) |
284 (bitreverse((h
>> 8) & 255) << 8) |
285 bitreverse(h
& 255));