Line data Source code
1 : /* $OpenBSD: mii.c,v 1.23 2015/12/29 18:35:39 mmcc Exp $ */
2 : /* $NetBSD: mii.c,v 1.19 2000/02/02 17:09:44 thorpej Exp $ */
3 :
4 : /*-
5 : * Copyright (c) 1998, 2000 The NetBSD Foundation, Inc.
6 : * All rights reserved.
7 : *
8 : * This code is derived from software contributed to The NetBSD Foundation
9 : * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
10 : * NASA Ames Research Center.
11 : *
12 : * Redistribution and use in source and binary forms, with or without
13 : * modification, are permitted provided that the following conditions
14 : * are met:
15 : * 1. Redistributions of source code must retain the above copyright
16 : * notice, this list of conditions and the following disclaimer.
17 : * 2. Redistributions in binary form must reproduce the above copyright
18 : * notice, this list of conditions and the following disclaimer in the
19 : * documentation and/or other materials provided with the distribution.
20 : *
21 : * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
22 : * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
23 : * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 : * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
25 : * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 : * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 : * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 : * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 : * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 : * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 : * POSSIBILITY OF SUCH DAMAGE.
32 : */
33 :
34 : /*
35 : * MII bus layer, glues MII-capable network interface drivers to sharable
36 : * PHY drivers. This exports an interface compatible with BSD/OS 3.0's,
37 : * plus some NetBSD extensions.
38 : */
39 :
40 : #include <sys/param.h>
41 : #include <sys/device.h>
42 : #include <sys/systm.h>
43 : #include <sys/socket.h>
44 :
45 : #include <net/if.h>
46 : #include <net/if_media.h>
47 :
48 : #include <dev/mii/mii.h>
49 : #include <dev/mii/miivar.h>
50 :
51 : int mii_print(void *, const char *);
52 : int mii_submatch(struct device *, void *, void *);
53 :
54 : #define MIICF_PHY 0 /* cf_loc index */
55 : #define MIICF_PHY_DEFAULT (-1) /* default phy device */
56 :
57 : /*
58 : * Helper function used by network interface drivers, attaches PHYs
59 : * to the network interface driver parent.
60 : */
61 : void
62 0 : mii_attach(struct device *parent, struct mii_data *mii, int capmask,
63 : int phyloc, int offloc, int flags)
64 : {
65 0 : struct mii_attach_args ma;
66 : struct mii_softc *child;
67 : int bmsr, offset = 0;
68 : int phymin, phymax;
69 :
70 0 : if (phyloc != MII_PHY_ANY && offloc != MII_OFFSET_ANY)
71 0 : panic("mii_attach: phyloc and offloc specified");
72 :
73 0 : if (phyloc == MII_PHY_ANY) {
74 : phymin = 0;
75 : phymax = MII_NPHY - 1;
76 0 : } else
77 : phymin = phymax = phyloc;
78 :
79 0 : if ((mii->mii_flags & MIIF_INITDONE) == 0) {
80 0 : LIST_INIT(&mii->mii_phys);
81 0 : mii->mii_flags |= MIIF_INITDONE;
82 0 : }
83 :
84 0 : for (ma.mii_phyno = phymin; ma.mii_phyno <= phymax; ma.mii_phyno++) {
85 : /*
86 : * Make sure we haven't already configured a PHY at this
87 : * address. This allows mii_attach() to be called
88 : * multiple times.
89 : */
90 0 : for (child = LIST_FIRST(&mii->mii_phys); child != NULL;
91 0 : child = LIST_NEXT(child, mii_list)) {
92 0 : if (child->mii_phy == ma.mii_phyno) {
93 : /*
94 : * Yes, there is already something
95 : * configured at this address.
96 : */
97 0 : offset++;
98 0 : goto loop_end;
99 : }
100 : }
101 :
102 : /*
103 : * Check to see if there is a PHY at this address. Note,
104 : * many braindead PHYs report 0/0 in their ID registers,
105 : * so we test for media in the BMSR.
106 : */
107 0 : bmsr = (*mii->mii_readreg)(parent, ma.mii_phyno, MII_BMSR);
108 0 : if (bmsr == 0 || bmsr == 0xffff ||
109 0 : (bmsr & (BMSR_MEDIAMASK|BMSR_EXTSTAT)) == 0) {
110 : /* Assume no PHY at this address. */
111 : goto loop_end;
112 : }
113 :
114 : /*
115 : * There is a PHY at this address. If we were given an
116 : * `offset' locator, skip this PHY if it doesn't match.
117 : */
118 0 : if (offloc != MII_OFFSET_ANY && offloc != offset) {
119 0 : offset++;
120 0 : goto loop_end;
121 : }
122 :
123 : /*
124 : * Extract the IDs. Braindead PHYs will be handled by
125 : * the `ukphy' driver, as we have no ID information to
126 : * match on.
127 : */
128 0 : ma.mii_id1 = (*mii->mii_readreg)(parent, ma.mii_phyno,
129 : MII_PHYIDR1);
130 0 : ma.mii_id2 = (*mii->mii_readreg)(parent, ma.mii_phyno,
131 : MII_PHYIDR2);
132 :
133 0 : ma.mii_data = mii;
134 0 : ma.mii_capmask = capmask;
135 0 : ma.mii_flags = flags | (mii->mii_flags & MIIF_INHERIT_MASK);
136 :
137 0 : if ((child = (struct mii_softc *)config_found_sm(parent, &ma,
138 0 : mii_print, mii_submatch)) != NULL) {
139 : /*
140 : * Link it up in the parent's MII data.
141 : */
142 0 : LIST_INSERT_HEAD(&mii->mii_phys, child, mii_list);
143 0 : child->mii_offset = offset;
144 0 : mii->mii_instance++;
145 0 : }
146 0 : offset++;
147 :
148 : loop_end: ;
149 : }
150 0 : }
151 :
152 : void
153 0 : mii_detach(struct mii_data *mii, int phyloc, int offloc)
154 : {
155 : struct mii_softc *child, *nchild;
156 :
157 0 : if (phyloc != MII_PHY_ANY && offloc != MII_PHY_ANY)
158 0 : panic("mii_detach: phyloc and offloc specified");
159 :
160 0 : if ((mii->mii_flags & MIIF_INITDONE) == 0)
161 0 : return;
162 :
163 0 : for (child = LIST_FIRST(&mii->mii_phys);
164 0 : child != NULL; child = nchild) {
165 0 : nchild = LIST_NEXT(child, mii_list);
166 0 : if (phyloc != MII_PHY_ANY || offloc != MII_OFFSET_ANY) {
167 0 : if (phyloc != MII_PHY_ANY &&
168 0 : phyloc != child->mii_phy)
169 : continue;
170 0 : if (offloc != MII_OFFSET_ANY &&
171 0 : offloc != child->mii_offset)
172 : continue;
173 : }
174 0 : LIST_REMOVE(child, mii_list);
175 0 : (void) config_detach(&child->mii_dev, DETACH_FORCE);
176 0 : }
177 0 : }
178 :
179 : int
180 0 : mii_print(void *aux, const char *pnp)
181 : {
182 0 : struct mii_attach_args *ma = aux;
183 :
184 0 : if (pnp != NULL)
185 0 : printf("OUI 0x%06x model 0x%04x rev %d at %s",
186 0 : MII_OUI(ma->mii_id1, ma->mii_id2), MII_MODEL(ma->mii_id2),
187 0 : MII_REV(ma->mii_id2), pnp);
188 :
189 0 : printf(" phy %d", ma->mii_phyno);
190 0 : return (UNCONF);
191 : }
192 :
193 : int
194 0 : mii_submatch(struct device *parent, void *match, void *aux)
195 : {
196 0 : struct cfdata *cf = match;
197 0 : struct mii_attach_args *ma = aux;
198 :
199 0 : if (ma->mii_phyno != cf->cf_loc[MIICF_PHY] &&
200 0 : cf->cf_loc[MIICF_PHY] != MIICF_PHY_DEFAULT)
201 0 : return (0);
202 :
203 0 : return ((*cf->cf_attach->ca_match)(parent, cf, aux));
204 0 : }
205 :
206 : /*
207 : * Media changed; notify all PHYs.
208 : */
209 : int
210 0 : mii_mediachg(struct mii_data *mii)
211 : {
212 : struct mii_softc *child;
213 : int rv;
214 :
215 0 : mii->mii_media_status = 0;
216 0 : mii->mii_media_active = IFM_NONE;
217 :
218 0 : for (child = LIST_FIRST(&mii->mii_phys); child != NULL;
219 0 : child = LIST_NEXT(child, mii_list)) {
220 0 : rv = PHY_SERVICE(child, mii, MII_MEDIACHG);
221 0 : if (rv)
222 0 : return (rv);
223 : }
224 0 : return (0);
225 0 : }
226 :
227 : /*
228 : * Call the PHY tick routines, used during autonegotiation.
229 : */
230 : void
231 0 : mii_tick(struct mii_data *mii)
232 : {
233 : struct mii_softc *child;
234 :
235 0 : for (child = LIST_FIRST(&mii->mii_phys); child != NULL;
236 0 : child = LIST_NEXT(child, mii_list))
237 0 : (void) PHY_SERVICE(child, mii, MII_TICK);
238 0 : }
239 :
240 : /*
241 : * Get media status from PHYs.
242 : */
243 : void
244 0 : mii_pollstat(struct mii_data *mii)
245 : {
246 : struct mii_softc *child;
247 :
248 0 : mii->mii_media_status = 0;
249 0 : mii->mii_media_active = IFM_NONE;
250 :
251 0 : for (child = LIST_FIRST(&mii->mii_phys); child != NULL;
252 0 : child = LIST_NEXT(child, mii_list))
253 0 : (void) PHY_SERVICE(child, mii, MII_POLLSTAT);
254 0 : }
255 :
256 : /*
257 : * Inform the PHYs that the interface is down.
258 : */
259 : void
260 0 : mii_down(struct mii_data *mii)
261 : {
262 : struct mii_softc *child;
263 :
264 0 : for (child = LIST_FIRST(&mii->mii_phys); child != NULL;
265 0 : child = LIST_NEXT(child, mii_list))
266 0 : (void) PHY_SERVICE(child, mii, MII_DOWN);
267 0 : }
|