xref: /openbsd-src/sys/dev/mii/xmphy.c (revision b2ea75c1b17e1a9a339660e7ed45cd24946b230e)
1 /*	$OpenBSD: xmphy.c,v 1.2 2000/08/29 19:00:36 jason Exp $	*/
2 
3 /*
4  * Copyright (c) 2000
5  *	Bill Paul <wpaul@ee.columbia.edu>.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *	This product includes software developed by Bill Paul.
18  * 4. Neither the name of the author nor the names of any co-contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
26  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
32  * THE POSSIBILITY OF SUCH DAMAGE.
33  *
34  * $FreeBSD: src/sys/dev/mii/xmphy.c,v 1.1 2000/04/22 01:58:18 wpaul Exp $
35  */
36 
37 /*
38  * driver for the XaQti XMAC II's internal PHY. This is sort of
39  * like a 10/100 PHY, except the only thing we're really autoselecting
40  * here is full/half duplex. Speed is always 1000mbps.
41  */
42 
43 #include <sys/param.h>
44 #include <sys/systm.h>
45 #include <sys/kernel.h>
46 #include <sys/device.h>
47 #include <sys/malloc.h>
48 #include <sys/socket.h>
49 #include <sys/errno.h>
50 
51 #include <net/if.h>
52 #include <net/if_media.h>
53 
54 #include <dev/mii/mii.h>
55 #include <dev/mii/miivar.h>
56 #include <dev/mii/miidevs.h>
57 
58 #include <dev/mii/xmphyreg.h>
59 
60 int xmphy_probe __P((struct device *, void *, void *));
61 void xmphy_attach __P((struct device *, struct device *, void *));
62 
63 struct cfattach xmphy_ca = {
64 	sizeof(struct mii_softc), xmphy_probe, xmphy_attach, mii_phy_detach,
65 	    mii_phy_activate
66 };
67 
68 struct cfdriver xmphy_cd = {
69 	NULL, "xmphy", DV_DULL
70 };
71 
72 int	xmphy_service __P((struct mii_softc *, struct mii_data *, int));
73 void	xmphy_status __P((struct mii_softc *));
74 
75 int	xmphy_mii_phy_auto __P((struct mii_softc *, int));
76 extern void	mii_phy_auto_timeout __P((void *));
77 
78 int xmphy_probe(parent, match, aux)
79 	struct device *parent;
80 	void *match, *aux;
81 {
82 	struct mii_attach_args *ma = aux;
83 
84 	if (MII_OUI(ma->mii_id1, ma->mii_id2) == MII_OUI_xxXAQTI &&
85 	    MII_MODEL(ma->mii_id2) == MII_MODEL_XAQTI_XMACII)
86 		return(10);
87 
88 	return(0);
89 }
90 
91 void
92 xmphy_attach(parent, self, aux)
93 	struct device *parent, *self;
94 	void *aux;
95 {
96 	struct mii_softc *sc = (struct mii_softc *)self;
97 	struct mii_attach_args *ma = aux;
98 	struct mii_data *mii = ma->mii_data;
99 
100 	printf(": %s, rev. %d\n", MII_STR_XAQTI_XMACII, MII_REV(ma->mii_id2));
101 
102 	sc->mii_inst = mii->mii_instance;
103 	sc->mii_phy = ma->mii_phyno;
104 	sc->mii_service = xmphy_service;
105 	sc->mii_status = xmphy_status;
106 	sc->mii_pdata = mii;
107 	sc->mii_flags |= MIIF_NOISOLATE | mii->mii_flags;
108 
109 #define	ADD(m, c)	ifmedia_add(&mii->mii_media, (m), (c), NULL)
110 
111 	ADD(IFM_MAKEWORD(IFM_ETHER, IFM_NONE, 0, sc->mii_inst),
112 	    BMCR_ISO);
113 
114 	mii_phy_reset(sc);
115 
116 	ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_SX, 0, sc->mii_inst),
117 	    XMPHY_BMCR_FDX);
118 	ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_SX, IFM_FDX, sc->mii_inst), 0);
119 	ADD(IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, sc->mii_inst), 0);
120 
121 #undef ADD
122 }
123 
124 int
125 xmphy_service(sc, mii, cmd)
126 	struct mii_softc *sc;
127 	struct mii_data *mii;
128 	int cmd;
129 {
130 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
131 	int reg;
132 
133 	if ((sc->mii_dev.dv_flags & DVF_ACTIVE) == 0)
134 		return (ENXIO);
135 
136 	switch (cmd) {
137 	case MII_POLLSTAT:
138 		/*
139 		 * If we're not polling our PHY instance, just return.
140 		 */
141 		if (IFM_INST(ife->ifm_media) != sc->mii_inst)
142 			return (0);
143 		break;
144 
145 	case MII_MEDIACHG:
146 		/*
147 		 * If the media indicates a different PHY instance,
148 		 * isolate ourselves.
149 		 */
150 		if (IFM_INST(ife->ifm_media) != sc->mii_inst) {
151 			reg = PHY_READ(sc, MII_BMCR);
152 			PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO);
153 			return (0);
154 		}
155 
156 		/*
157 		 * If the interface is not up, don't do anything.
158 		 */
159 		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
160 			break;
161 
162 		switch (IFM_SUBTYPE(ife->ifm_media)) {
163 		case IFM_AUTO:
164 			/*
165 			 * If we're already in auto mode, just return.
166 			 */
167 			if (PHY_READ(sc, XMPHY_MII_BMCR) & XMPHY_BMCR_AUTOEN)
168 				return (0);
169 			(void) xmphy_mii_phy_auto(sc, 1);
170 			break;
171 		case IFM_1000_SX:
172 			mii_phy_reset(sc);
173 			if ((ife->ifm_media & IFM_GMASK) == IFM_FDX) {
174 				PHY_WRITE(sc, XMPHY_MII_ANAR, XMPHY_ANAR_FDX);
175 				PHY_WRITE(sc, XMPHY_MII_BMCR, XMPHY_BMCR_FDX);
176 			} else {
177 				PHY_WRITE(sc, XMPHY_MII_ANAR, XMPHY_ANAR_HDX);
178 				PHY_WRITE(sc, XMPHY_MII_BMCR, 0);
179 			}
180 			break;
181 		case IFM_100_T4:
182 		case IFM_100_TX:
183 		case IFM_10_T:
184 		default:
185 			return (EINVAL);
186 		}
187 		break;
188 
189 	case MII_TICK:
190 		/*
191 		 * If we're not currently selected, just return.
192 		 */
193 		if (IFM_INST(ife->ifm_media) != sc->mii_inst)
194 			return (0);
195 
196 		/*
197 		 * Only used for autonegotiation.
198 		 */
199 		if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO)
200 			return (0);
201 
202 		/*
203 		 * Is the interface even up?
204 		 */
205 		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
206 			return (0);
207 
208 		/*
209 		 * Only retry autonegotiation every 5 seconds.
210 		 */
211 		if (++sc->mii_ticks != 5)
212 			return (0);
213 
214 		sc->mii_ticks = 0;
215 
216 		/*
217 		 * Check to see if we have link.  If we do, we don't
218 		 * need to restart the autonegotiation process.  Read
219 		 * the BMSR twice in case it's latched.
220 		 */
221 		reg = PHY_READ(sc, XMPHY_MII_BMSR) |
222 		    PHY_READ(sc, XMPHY_MII_BMSR);
223 		if (reg & XMPHY_BMSR_LINK)
224 			break;
225 
226 		mii_phy_reset(sc);
227 		if (xmphy_mii_phy_auto(sc, 0) == EJUSTRETURN)
228 			return(0);
229 		break;
230 	}
231 
232 	/* Update the media status. */
233 	xmphy_status(sc);
234 
235 	/* Callback if something changed. */
236 	mii_phy_update(sc, cmd);
237 	return (0);
238 }
239 
240 void
241 xmphy_status(sc)
242 	struct mii_softc *sc;
243 {
244 	struct mii_data *mii = sc->mii_pdata;
245 	int bmsr, bmcr, anlpar;
246 
247 	mii->mii_media_status = IFM_AVALID;
248 	mii->mii_media_active = IFM_ETHER;
249 
250 	bmsr = PHY_READ(sc, XMPHY_MII_BMSR) |
251 	    PHY_READ(sc, XMPHY_MII_BMSR);
252 	if (bmsr & XMPHY_BMSR_LINK)
253 		mii->mii_media_status |= IFM_ACTIVE;
254 
255 	/* Do dummy read of extended status register. */
256 	bmcr = PHY_READ(sc, XMPHY_MII_EXTSTS);
257 
258 	bmcr = PHY_READ(sc, XMPHY_MII_BMCR);
259 
260 	if (bmcr & XMPHY_BMCR_LOOP)
261 		mii->mii_media_active |= IFM_LOOP;
262 
263 
264 	if (bmcr & XMPHY_BMCR_AUTOEN) {
265 		if ((bmsr & XMPHY_BMSR_ACOMP) == 0) {
266 			if (bmsr & XMPHY_BMSR_LINK) {
267 				mii->mii_media_active |= IFM_1000_SX|IFM_HDX;
268 				return;
269 			}
270 			/* Erg, still trying, I guess... */
271 			mii->mii_media_active |= IFM_NONE;
272 			return;
273 		}
274 
275 		mii->mii_media_active |= IFM_1000_SX;
276 		anlpar = PHY_READ(sc, XMPHY_MII_ANAR) &
277 		    PHY_READ(sc, XMPHY_MII_ANLPAR);
278 		if (anlpar & XMPHY_ANLPAR_FDX)
279 			mii->mii_media_active |= IFM_FDX;
280 		else
281 			mii->mii_media_active |= IFM_HDX;
282 		return;
283 	}
284 
285 	mii->mii_media_active |= IFM_1000_SX;
286 	if (bmcr & XMPHY_BMCR_FDX)
287 		mii->mii_media_active |= IFM_FDX;
288 	else
289 		mii->mii_media_active |= IFM_HDX;
290 
291 	return;
292 }
293 
294 
295 int
296 xmphy_mii_phy_auto(mii, waitfor)
297 	struct mii_softc *mii;
298 	int waitfor;
299 {
300 	int bmsr, i;
301 
302 	if ((mii->mii_flags & MIIF_DOINGAUTO) == 0) {
303 		PHY_WRITE(mii, XMPHY_MII_ANAR,
304 		    XMPHY_ANAR_FDX|XMPHY_ANAR_HDX);
305 		PHY_WRITE(mii, XMPHY_MII_BMCR,
306 		    XMPHY_BMCR_AUTOEN | XMPHY_BMCR_STARTNEG);
307 	}
308 
309 	if (waitfor) {
310 		/* Wait 500ms for it to complete. */
311 		for (i = 0; i < 500; i++) {
312 			if ((bmsr = PHY_READ(mii, XMPHY_MII_BMSR)) &
313 			    XMPHY_BMSR_ACOMP)
314 				return (0);
315 			DELAY(1000);
316 #if 0
317 		if ((bmsr & BMSR_ACOMP) == 0)
318 			printf("%s: autonegotiation failed to complete\n",
319 			    mii->mii_dev.dv_xname);
320 #endif
321 		}
322 
323 		/*
324 		 * Don't need to worry about clearing MIIF_DOINGAUTO.
325 		 * If that's set, a timeout is pending, and it will
326 		 * clear the flag.
327 		 */
328 		return (EIO);
329 	}
330 
331 	/*
332 	 * Just let it finish asynchronously.  This is for the benefit of
333 	 * the tick handler driving autonegotiation.  Don't want 500ms
334 	 * delays all the time while the system is running!
335 	 */
336 	if ((mii->mii_flags & MIIF_DOINGAUTO) == 0) {
337 		mii->mii_flags |= MIIF_DOINGAUTO;
338 		timeout(mii_phy_auto_timeout, mii, hz >> 1);
339 	}
340 	return (EJUSTRETURN);
341 }
342