xref: /openbsd-src/sys/dev/mii/mii.c (revision b2ea75c1b17e1a9a339660e7ed45cd24946b230e)
1 /*	$OpenBSD: mii.c,v 1.9 2000/08/26 20:04:17 nate 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  * 3. All advertising materials mentioning features or use of this software
21  *    must display the following acknowledgement:
22  *	This product includes software developed by the NetBSD
23  *	Foundation, Inc. and its contributors.
24  * 4. Neither the name of The NetBSD Foundation nor the names of its
25  *    contributors may be used to endorse or promote products derived
26  *    from this software without specific prior written permission.
27  *
28  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
29  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
30  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
31  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
32  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
33  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
34  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
35  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
36  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
37  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
38  * POSSIBILITY OF SUCH DAMAGE.
39  */
40 
41 /*
42  * MII bus layer, glues MII-capable network interface drivers to sharable
43  * PHY drivers.  This exports an interface compatible with BSD/OS 3.0's,
44  * plus some NetBSD extensions.
45  */
46 
47 #include <sys/param.h>
48 #include <sys/device.h>
49 #include <sys/systm.h>
50 #include <sys/socket.h>
51 
52 #include <net/if.h>
53 #include <net/if_media.h>
54 
55 #include <dev/mii/mii.h>
56 #include <dev/mii/miivar.h>
57 
58 int	mii_print __P((void *, const char *));
59 int	mii_submatch __P((struct device *, void *, void *));
60 
61 #define MIICF_PHY		0	/* cf_loc index */
62 #define MIICF_PHY_DEFAULT	(-1)	/* default phy device */
63 
64 /*
65  * Helper function used by network interface drivers, attaches PHYs
66  * to the network interface driver parent.
67  */
68 void
69 mii_attach(parent, mii, capmask, phyloc, offloc, flags)
70 	struct device *parent;
71 	struct mii_data *mii;
72 	int capmask, phyloc, offloc, flags;
73 {
74 	struct mii_attach_args ma;
75 	struct mii_softc *child;
76 	int bmsr, offset = 0;
77 	int phymin, phymax;
78 
79 	if (phyloc != MII_PHY_ANY && offloc != MII_PHY_ANY)
80 		panic("mii_attach: phyloc and offloc specified");
81 
82 	if (phyloc == MII_PHY_ANY) {
83 		phymin = 0;
84 		phymax = MII_NPHY - 1;
85 	} else
86 		phymin = phymax = phyloc;
87 
88 	if ((mii->mii_flags & MIIF_INITDONE) == 0) {
89 		LIST_INIT(&mii->mii_phys);
90 		mii->mii_flags |= MIIF_INITDONE;
91 	}
92 
93 	for (ma.mii_phyno = phymin; ma.mii_phyno <= phymax; ma.mii_phyno++) {
94 		/*
95 		 * Make sure we haven't already configured a PHY at this
96 		 * address.  This allows mii_attach() to be called
97 		 * multiple times.
98 		 */
99 		for (child = LIST_FIRST(&mii->mii_phys); child != NULL;
100 		     child = LIST_NEXT(child, mii_list)) {
101 			if (child->mii_phy == ma.mii_phyno) {
102 				/*
103 				 * Yes, there is already something
104 				 * configured at this address.
105 				 */
106 				offset++;
107 				continue;
108 			}
109 		}
110 
111 		/*
112 		 * Check to see if there is a PHY at this address.  Note,
113 		 * many braindead PHYs report 0/0 in their ID registers,
114 		 * so we test for media in the BMSR.
115 		 */
116 		bmsr = (*mii->mii_readreg)(parent, ma.mii_phyno, MII_BMSR);
117 		if (bmsr == 0 || bmsr == 0xffff ||
118 		    (bmsr & BMSR_MEDIAMASK) == 0) {
119 			/* Assume no PHY at this address. */
120 			continue;
121 		}
122 
123 		/*
124 		 * There is a PHY at this address.  If we were given an
125 		 * `offset' locator, skip this PHY if it doesn't match.
126 		 */
127 		if (offloc != MII_OFFSET_ANY && offloc != offset) {
128 			offset++;
129 			continue;
130 		}
131 
132 		/*
133 		 * Extract the IDs.  Braindead PHYs will be handled by
134 		 * the `ukphy' driver, as we have no ID information to
135 		 * match on.
136 		 */
137 		ma.mii_id1 = (*mii->mii_readreg)(parent, ma.mii_phyno,
138 		    MII_PHYIDR1);
139 		ma.mii_id2 = (*mii->mii_readreg)(parent, ma.mii_phyno,
140 		    MII_PHYIDR2);
141 
142 		ma.mii_data = mii;
143 		ma.mii_capmask = capmask;
144 		ma.mii_flags = flags;
145 
146 		if ((child = (struct mii_softc *)config_found_sm(parent, &ma,
147 		    mii_print, mii_submatch)) != NULL) {
148 			/*
149 			 * Link it up in the parent's MII data.
150 			 */
151 #if defined(__NetBSD__)
152 			callout_init(&child->mii_nway_ch);
153 #endif
154 			LIST_INSERT_HEAD(&mii->mii_phys, child, mii_list);
155 			child->mii_offset = offset;
156 			mii->mii_instance++;
157 		}
158 		offset++;
159 	}
160 }
161 
162 void
163 mii_activate(mii, act, phyloc, offloc)
164 	struct mii_data *mii;
165 	enum devact act;
166 	int phyloc, offloc;
167 {
168 	struct mii_softc *child;
169 
170 	if (phyloc != MII_PHY_ANY && offloc != MII_PHY_ANY)
171 		panic("mii_activate: phyloc and offloc specified");
172 
173 	if ((mii->mii_flags & MIIF_INITDONE) == 0)
174 		return;
175 
176 	for (child = LIST_FIRST(&mii->mii_phys);
177 	     child != NULL; child = LIST_NEXT(child, mii_list)) {
178 		if (phyloc != MII_PHY_ANY || offloc != MII_OFFSET_ANY) {
179 			if (phyloc != MII_PHY_ANY &&
180 			    phyloc != child->mii_phy)
181 				continue;
182 			if (offloc != MII_OFFSET_ANY &&
183 			    offloc != child->mii_offset)
184 				continue;
185 		}
186 		switch (act) {
187 		case DVACT_ACTIVATE:
188 			panic("mii_activate: DVACT_ACTIVATE");
189 			break;
190 
191 		case DVACT_DEACTIVATE:
192 			if (config_deactivate(&child->mii_dev) != 0)
193 				panic("%s: config_activate(%d) failed\n",
194 				    child->mii_dev.dv_xname, act);
195 		}
196 	}
197 }
198 
199 void
200 mii_detach(mii, phyloc, offloc)
201 	struct mii_data *mii;
202 	int phyloc, offloc;
203 {
204 	struct mii_softc *child, *nchild;
205 
206 	if (phyloc != MII_PHY_ANY && offloc != MII_PHY_ANY)
207 		panic("mii_detach: phyloc and offloc specified");
208 
209 	if ((mii->mii_flags & MIIF_INITDONE) == 0)
210 		return;
211 
212 	for (child = LIST_FIRST(&mii->mii_phys);
213 	     child != NULL; child = nchild) {
214 		nchild = LIST_NEXT(child, mii_list);
215 		if (phyloc != MII_PHY_ANY || offloc != MII_OFFSET_ANY) {
216 			if (phyloc != MII_PHY_ANY &&
217 			    phyloc != child->mii_phy)
218 				continue;
219 			if (offloc != MII_OFFSET_ANY &&
220 			    offloc != child->mii_offset)
221 				continue;
222 		}
223 		LIST_REMOVE(child, mii_list);
224 		(void) config_detach(&child->mii_dev, DETACH_FORCE);
225 	}
226 }
227 
228 int
229 mii_print(aux, pnp)
230 	void *aux;
231 	const char *pnp;
232 {
233 	struct mii_attach_args *ma = aux;
234 
235 	if (pnp != NULL)
236 		printf("OUI 0x%06x model 0x%04x rev %d at %s",
237 		    MII_OUI(ma->mii_id1, ma->mii_id2), MII_MODEL(ma->mii_id2),
238 		    MII_REV(ma->mii_id2), pnp);
239 
240 	printf(" phy %d", ma->mii_phyno);
241 	return (UNCONF);
242 }
243 
244 int
245 mii_submatch(parent, match, aux)
246 	struct device *parent;
247 	void *match, *aux;
248 {
249 	struct cfdata *cf = match;
250 	struct mii_attach_args *ma = aux;
251 
252 	if (ma->mii_phyno != cf->cf_loc[MIICF_PHY] &&
253 	    cf->cf_loc[MIICF_PHY] != MIICF_PHY_DEFAULT)
254 		return (0);
255 
256 	return ((*cf->cf_attach->ca_match)(parent, cf, aux));
257 }
258 
259 /*
260  * Media changed; notify all PHYs.
261  */
262 int
263 mii_mediachg(mii)
264 	struct mii_data *mii;
265 {
266 	struct mii_softc *child;
267 	int rv;
268 
269 	mii->mii_media_status = 0;
270 	mii->mii_media_active = IFM_NONE;
271 
272 	for (child = LIST_FIRST(&mii->mii_phys); child != NULL;
273 	     child = LIST_NEXT(child, mii_list)) {
274 		rv = (*child->mii_service)(child, mii, MII_MEDIACHG);
275 		if (rv)
276 			return (rv);
277 	}
278 	return (0);
279 }
280 
281 /*
282  * Call the PHY tick routines, used during autonegotiation.
283  */
284 void
285 mii_tick(mii)
286 	struct mii_data *mii;
287 {
288 	struct mii_softc *child;
289 
290 	for (child = LIST_FIRST(&mii->mii_phys); child != NULL;
291 	     child = LIST_NEXT(child, mii_list))
292 		(void) (*child->mii_service)(child, mii, MII_TICK);
293 }
294 
295 /*
296  * Get media status from PHYs.
297  */
298 void
299 mii_pollstat(mii)
300 	struct mii_data *mii;
301 {
302 	struct mii_softc *child;
303 
304 	mii->mii_media_status = 0;
305 	mii->mii_media_active = IFM_NONE;
306 
307 	for (child = LIST_FIRST(&mii->mii_phys); child != NULL;
308 	     child = LIST_NEXT(child, mii_list))
309 		(void) (*child->mii_service)(child, mii, MII_POLLSTAT);
310 }
311 
312 /*
313  * Inform the PHYs that the interface is down.
314  */
315 void
316 mii_down(mii)
317 	struct mii_data *mii;
318 {
319 	struct mii_softc *child;
320 
321 	for (child = LIST_FIRST(&mii->mii_phys); child != NULL;
322 	     child = LIST_NEXT(child, mii_list))
323 		(void) (*child->mii_service)(child, mii, MII_DOWN);
324 }
325