1 /*	$OpenBSD: mii.c,v 1.16 2005/07/09 21:32:06 brad 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(void *, const char *);
59 int	mii_submatch(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
mii_attach(struct device * parent,struct mii_data * mii,int capmask,int phyloc,int offloc,int flags)69 mii_attach(struct device *parent, struct mii_data *mii, int capmask,
70     int phyloc, int offloc, int flags)
71 {
72 	struct mii_attach_args ma;
73 	struct mii_softc *child;
74 	int bmsr, offset = 0;
75 	int phymin, phymax;
76 
77 	if (phyloc != MII_PHY_ANY && offloc != MII_OFFSET_ANY)
78 		panic("mii_attach: phyloc and offloc specified");
79 
80 	if (phyloc == MII_PHY_ANY) {
81 		phymin = 0;
82 		phymax = MII_NPHY - 1;
83 	} else
84 		phymin = phymax = phyloc;
85 
86 	if ((mii->mii_flags & MIIF_INITDONE) == 0) {
87 		LIST_INIT(&mii->mii_phys);
88 		mii->mii_flags |= MIIF_INITDONE;
89 	}
90 
91 	for (ma.mii_phyno = phymin; ma.mii_phyno <= phymax; ma.mii_phyno++) {
92 		/*
93 		 * Make sure we haven't already configured a PHY at this
94 		 * address.  This allows mii_attach() to be called
95 		 * multiple times.
96 		 */
97 		for (child = LIST_FIRST(&mii->mii_phys); child != NULL;
98 		     child = LIST_NEXT(child, mii_list)) {
99 			if (child->mii_phy == ma.mii_phyno) {
100 				/*
101 				 * Yes, there is already something
102 				 * configured at this address.
103 				 */
104 				offset++;
105 				continue;
106 			}
107 		}
108 
109 		/*
110 		 * Check to see if there is a PHY at this address.  Note,
111 		 * many braindead PHYs report 0/0 in their ID registers,
112 		 * so we test for media in the BMSR.
113 		 */
114 		bmsr = (*mii->mii_readreg)(parent, ma.mii_phyno, MII_BMSR);
115 		if (bmsr == 0 || bmsr == 0xffff ||
116 		    (bmsr & (BMSR_MEDIAMASK|BMSR_EXTSTAT)) == 0) {
117 			/* Assume no PHY at this address. */
118 			continue;
119 		}
120 
121 		/*
122 		 * There is a PHY at this address.  If we were given an
123 		 * `offset' locator, skip this PHY if it doesn't match.
124 		 */
125 		if (offloc != MII_OFFSET_ANY && offloc != offset) {
126 			offset++;
127 			continue;
128 		}
129 
130 		/*
131 		 * Extract the IDs.  Braindead PHYs will be handled by
132 		 * the `ukphy' driver, as we have no ID information to
133 		 * match on.
134 		 */
135 		ma.mii_id1 = (*mii->mii_readreg)(parent, ma.mii_phyno,
136 		    MII_PHYIDR1);
137 		ma.mii_id2 = (*mii->mii_readreg)(parent, ma.mii_phyno,
138 		    MII_PHYIDR2);
139 
140 		ma.mii_data = mii;
141 		ma.mii_capmask = capmask;
142 		ma.mii_flags = flags | (mii->mii_flags & MIIF_INHERIT_MASK);
143 
144 		if ((child = (struct mii_softc *)config_found_sm(parent, &ma,
145 		    mii_print, mii_submatch)) != NULL) {
146 			/*
147 			 * Link it up in the parent's MII data.
148 			 */
149 			LIST_INSERT_HEAD(&mii->mii_phys, child, mii_list);
150 			child->mii_offset = offset;
151 			mii->mii_instance++;
152 		}
153 		offset++;
154 	}
155 }
156 
157 void
mii_activate(struct mii_data * mii,enum devact act,int phyloc,int offloc)158 mii_activate(struct mii_data *mii, enum devact act, int phyloc, int offloc)
159 {
160 	struct mii_softc *child;
161 
162 	if (phyloc != MII_PHY_ANY && offloc != MII_OFFSET_ANY)
163 		panic("mii_activate: phyloc and offloc specified");
164 
165 	if ((mii->mii_flags & MIIF_INITDONE) == 0)
166 		return;
167 
168 	for (child = LIST_FIRST(&mii->mii_phys);
169 	     child != NULL; child = LIST_NEXT(child, mii_list)) {
170 		if (phyloc != MII_PHY_ANY || offloc != MII_OFFSET_ANY) {
171 			if (phyloc != MII_PHY_ANY &&
172 			    phyloc != child->mii_phy)
173 				continue;
174 			if (offloc != MII_OFFSET_ANY &&
175 			    offloc != child->mii_offset)
176 				continue;
177 		}
178 		switch (act) {
179 		case DVACT_ACTIVATE:
180 			panic("mii_activate: DVACT_ACTIVATE");
181 			break;
182 
183 		case DVACT_DEACTIVATE:
184 			if (config_deactivate(&child->mii_dev) != 0)
185 				panic("%s: config_activate(%d) failed",
186 				    child->mii_dev.dv_xname, act);
187 		}
188 	}
189 }
190 
191 void
mii_detach(struct mii_data * mii,int phyloc,int offloc)192 mii_detach(struct mii_data *mii, int phyloc, int offloc)
193 {
194 	struct mii_softc *child, *nchild;
195 
196 	if (phyloc != MII_PHY_ANY && offloc != MII_PHY_ANY)
197 		panic("mii_detach: phyloc and offloc specified");
198 
199 	if ((mii->mii_flags & MIIF_INITDONE) == 0)
200 		return;
201 
202 	for (child = LIST_FIRST(&mii->mii_phys);
203 	     child != NULL; child = nchild) {
204 		nchild = LIST_NEXT(child, mii_list);
205 		if (phyloc != MII_PHY_ANY || offloc != MII_OFFSET_ANY) {
206 			if (phyloc != MII_PHY_ANY &&
207 			    phyloc != child->mii_phy)
208 				continue;
209 			if (offloc != MII_OFFSET_ANY &&
210 			    offloc != child->mii_offset)
211 				continue;
212 		}
213 		LIST_REMOVE(child, mii_list);
214 		(void) config_detach(&child->mii_dev, DETACH_FORCE);
215 	}
216 }
217 
218 int
mii_print(void * aux,const char * pnp)219 mii_print(void *aux, const char *pnp)
220 {
221 	struct mii_attach_args *ma = aux;
222 
223 	if (pnp != NULL)
224 		printf("OUI 0x%06x model 0x%04x rev %d at %s",
225 		    MII_OUI(ma->mii_id1, ma->mii_id2), MII_MODEL(ma->mii_id2),
226 		    MII_REV(ma->mii_id2), pnp);
227 
228 	printf(" phy %d", ma->mii_phyno);
229 	return (UNCONF);
230 }
231 
232 int
mii_submatch(struct device * parent,void * match,void * aux)233 mii_submatch(struct device *parent, void *match, void *aux)
234 {
235 	struct cfdata *cf = match;
236 	struct mii_attach_args *ma = aux;
237 
238 	if (ma->mii_phyno != cf->cf_loc[MIICF_PHY] &&
239 	    cf->cf_loc[MIICF_PHY] != MIICF_PHY_DEFAULT)
240 		return (0);
241 
242 	return ((*cf->cf_attach->ca_match)(parent, cf, aux));
243 }
244 
245 /*
246  * Media changed; notify all PHYs.
247  */
248 int
mii_mediachg(struct mii_data * mii)249 mii_mediachg(struct mii_data *mii)
250 {
251 	struct mii_softc *child;
252 	int rv;
253 
254 	mii->mii_media_status = 0;
255 	mii->mii_media_active = IFM_NONE;
256 
257 	for (child = LIST_FIRST(&mii->mii_phys); child != NULL;
258 	     child = LIST_NEXT(child, mii_list)) {
259 		rv = PHY_SERVICE(child, mii, MII_MEDIACHG);
260 		if (rv)
261 			return (rv);
262 	}
263 	return (0);
264 }
265 
266 /*
267  * Call the PHY tick routines, used during autonegotiation.
268  */
269 void
mii_tick(struct mii_data * mii)270 mii_tick(struct mii_data *mii)
271 {
272 	struct mii_softc *child;
273 
274 	for (child = LIST_FIRST(&mii->mii_phys); child != NULL;
275 	     child = LIST_NEXT(child, mii_list))
276 		(void) PHY_SERVICE(child, mii, MII_TICK);
277 }
278 
279 /*
280  * Get media status from PHYs.
281  */
282 void
mii_pollstat(struct mii_data * mii)283 mii_pollstat(struct mii_data *mii)
284 {
285 	struct mii_softc *child;
286 
287 	mii->mii_media_status = 0;
288 	mii->mii_media_active = IFM_NONE;
289 
290 	for (child = LIST_FIRST(&mii->mii_phys); child != NULL;
291 	     child = LIST_NEXT(child, mii_list))
292 		(void) PHY_SERVICE(child, mii, MII_POLLSTAT);
293 }
294 
295 /*
296  * Inform the PHYs that the interface is down.
297  */
298 void
mii_down(struct mii_data * mii)299 mii_down(struct mii_data *mii)
300 {
301 	struct mii_softc *child;
302 
303 	for (child = LIST_FIRST(&mii->mii_phys); child != NULL;
304 	     child = LIST_NEXT(child, mii_list))
305 		(void) PHY_SERVICE(child, mii, MII_DOWN);
306 }
307