xref: /freebsd/sys/dev/mii/lxtphy.c (revision c17d43407fe04133a94055b0dbc7ea8965654a9f)
1 /*	OpenBSD: lxtphy.c,v 1.5 2000/08/26 20:04:17 nate Exp 	*/
2 /*	NetBSD: lxtphy.c,v 1.19 2000/02/02 23:34:57 thorpej Exp 	*/
3 
4 /*-
5  * Copyright (c) 1998, 1999 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  * Copyright (c) 1997 Manuel Bouyer.  All rights reserved.
43  *
44  * Redistribution and use in source and binary forms, with or without
45  * modification, are permitted provided that the following conditions
46  * are met:
47  * 1. Redistributions of source code must retain the above copyright
48  *    notice, this list of conditions and the following disclaimer.
49  * 2. Redistributions in binary form must reproduce the above copyright
50  *    notice, this list of conditions and the following disclaimer in the
51  *    documentation and/or other materials provided with the distribution.
52  * 3. All advertising materials mentioning features or use of this software
53  *    must display the following acknowledgement:
54  *	This product includes software developed by Manuel Bouyer.
55  * 4. The name of the author may not be used to endorse or promote products
56  *    derived from this software without specific prior written permission.
57  *
58  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
59  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
60  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
61  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
62  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
63  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
64  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
65  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
66  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
67  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
68  */
69 
70 /*
71  * driver for Level One's LXT-970 ethernet 10/100 PHY
72  * datasheet from www.level1.com
73  */
74 
75 #include <sys/param.h>
76 #include <sys/systm.h>
77 #include <sys/kernel.h>
78 #include <sys/socket.h>
79 #include <sys/errno.h>
80 #include <sys/module.h>
81 #include <sys/bus.h>
82 
83 #include <net/if.h>
84 #include <net/if_media.h>
85 
86 #include <dev/mii/mii.h>
87 #include <dev/mii/miivar.h>
88 #include <dev/mii/miidevs.h>
89 
90 #include <dev/mii/lxtphyreg.h>
91 
92 #include "miibus_if.h"
93 
94 #if !defined(lint)
95 static const char rcsid[] =
96   "$FreeBSD$";
97 #endif
98 
99 static int lxtphy_probe		(device_t);
100 static int lxtphy_attach	(device_t);
101 static int lxtphy_detach	(device_t);
102 
103 static device_method_t lxtphy_methods[] = {
104 	/* device interface */
105 	DEVMETHOD(device_probe,		lxtphy_probe),
106 	DEVMETHOD(device_attach,	lxtphy_attach),
107 	DEVMETHOD(device_detach,	lxtphy_detach),
108 	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
109 	{ 0, 0 }
110 };
111 
112 static devclass_t lxtphy_devclass;
113 
114 static driver_t lxtphy_driver = {
115 	"lxtphy",
116 	lxtphy_methods,
117 	sizeof(struct mii_softc)
118 };
119 
120 DRIVER_MODULE(lxtphy, miibus, lxtphy_driver, lxtphy_devclass, 0, 0);
121 
122 static int	lxtphy_service(struct mii_softc *, struct mii_data *, int);
123 static void	lxtphy_status(struct mii_softc *);
124 static void	lxtphy_set_tp(struct mii_softc *);
125 static void	lxtphy_set_fx(struct mii_softc *);
126 
127 static int lxtphy_probe(dev)
128 	device_t		dev;
129 {
130 	struct mii_attach_args *ma;
131 
132 	ma = device_get_ivars(dev);
133 
134 	if (MII_OUI(ma->mii_id1, ma->mii_id2) == MII_OUI_xxLEVEL1 &&
135 	    MII_MODEL(ma->mii_id2) == MII_MODEL_xxLEVEL1_LXT970) {
136 		device_set_desc(dev, MII_STR_xxLEVEL1_LXT970);
137 	} else
138 		return (ENXIO);
139 
140 	return (0);
141 }
142 
143 static int lxtphy_attach(dev)
144 	device_t		dev;
145 {
146 	struct mii_softc *sc;
147 	struct mii_attach_args *ma;
148 	struct mii_data *mii;
149 
150 	sc = device_get_softc(dev);
151 	ma = device_get_ivars(dev);
152 	sc->mii_dev = device_get_parent(dev);
153 	mii = device_get_softc(sc->mii_dev);
154 	LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list);
155 
156 	sc->mii_inst = mii->mii_instance;
157 	sc->mii_phy = ma->mii_phyno;
158 	sc->mii_service = lxtphy_service;
159 	sc->mii_pdata = mii;
160 	sc->mii_flags |= MIIF_NOISOLATE;
161 
162 	mii_phy_reset(sc);
163 
164 	mii->mii_instance++;
165 
166 	sc->mii_capabilities =
167 	    PHY_READ(sc, MII_BMSR) & ma->mii_capmask;
168 	device_printf(dev, " ");
169 
170 #define	ADD(m, c)	ifmedia_add(&mii->mii_media, (m), (c), NULL)
171 	ADD(IFM_MAKEWORD(IFM_ETHER, IFM_NONE, 0, sc->mii_inst),
172 	    BMCR_ISO);
173 	ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_FX, 0, sc->mii_inst),
174 	    BMCR_S100);
175 	printf("100baseFX, ");
176 	ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_FX, IFM_FDX, sc->mii_inst),
177 	    BMCR_S100|BMCR_FDX);
178 	printf("100baseFX-FDX, ");
179 #undef ADD
180 
181 	if (sc->mii_capabilities & BMSR_MEDIAMASK)
182 		mii_add_media(mii, sc->mii_capabilities,
183 		    sc->mii_inst);
184 	printf("\n");
185 
186 	MIIBUS_MEDIAINIT(sc->mii_dev);
187 	return(0);
188 }
189 
190 
191 static int lxtphy_detach(dev)
192 	device_t		dev;
193 {
194 	struct mii_softc *sc;
195 	struct mii_data *mii;
196 
197 	sc = device_get_softc(dev);
198 	mii = device_get_softc(device_get_parent(dev));
199 	sc->mii_dev = NULL;
200 	LIST_REMOVE(sc, mii_list);
201 
202 	return(0);
203 }
204 
205 static int
206 lxtphy_service(sc, mii, cmd)
207 	struct mii_softc *sc;
208 	struct mii_data *mii;
209 	int cmd;
210 {
211 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
212 	int reg;
213 
214 	switch (cmd) {
215 	case MII_POLLSTAT:
216 		/*
217 		 * If we're not polling our PHY instance, just return.
218 		 */
219 		if (IFM_INST(ife->ifm_media) != sc->mii_inst)
220 			return (0);
221 		break;
222 
223 	case MII_MEDIACHG:
224 		/*
225 		 * If the media indicates a different PHY instance,
226 		 * isolate ourselves.
227 		 */
228 		if (IFM_INST(ife->ifm_media) != sc->mii_inst) {
229 			reg = PHY_READ(sc, MII_BMCR);
230 			PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO);
231 			return (0);
232 		}
233 
234 		/*
235 		 * If the interface is not up, don't do anything.
236 		 */
237 		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
238 			break;
239 
240 		switch (IFM_SUBTYPE(ife->ifm_media)) {
241 		case IFM_AUTO:
242 			/*
243 			 * If we're already in auto mode, just return.
244 			 */
245 			if (PHY_READ(sc, MII_BMCR) & BMCR_AUTOEN)
246 				return (0);
247 
248 			lxtphy_set_tp(sc);
249 
250 			(void) mii_phy_auto(sc, 1);
251 			break;
252 		case IFM_100_T4:
253 			/*
254 			 * XXX Not supported as a manual setting right now.
255 			 */
256 			return (EINVAL);
257 
258 		case IFM_100_FX:
259 			lxtphy_set_fx(sc);
260 
261 		default:
262 			/*
263 			 * BMCR data is stored in the ifmedia entry.
264 			 */
265 			PHY_WRITE(sc, MII_ANAR,
266 			    mii_anar(ife->ifm_media));
267 			PHY_WRITE(sc, MII_BMCR, ife->ifm_data);
268 		}
269 		break;
270 
271 	case MII_TICK:
272 		/*
273 		 * If we're not currently selected, just return.
274 		 */
275 		if (IFM_INST(ife->ifm_media) != sc->mii_inst)
276 			return (0);
277 		if (mii_phy_tick(sc) == EJUSTRETURN)
278 			return (0);
279 		break;
280 	}
281 
282 	/* Update the media status. */
283 	lxtphy_status(sc);
284 
285 	/* Callback if something changed. */
286 	mii_phy_update(sc, cmd);
287 	return (0);
288 }
289 
290 static void
291 lxtphy_status(sc)
292 	struct mii_softc *sc;
293 {
294 	struct mii_data *mii = sc->mii_pdata;
295 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
296 	int bmcr, bmsr, csr;
297 
298 	mii->mii_media_status = IFM_AVALID;
299 	mii->mii_media_active = IFM_ETHER;
300 
301 	/*
302 	 * Get link status from the CSR; we need to read the CSR
303 	 * for media type anyhow, and the link status in the CSR
304 	 * doens't latch, so fewer register reads are required.
305 	 */
306 	csr = PHY_READ(sc, MII_LXTPHY_CSR);
307 	if (csr & CSR_LINK)
308 		mii->mii_media_status |= IFM_ACTIVE;
309 
310 	bmcr = PHY_READ(sc, MII_BMCR);
311 	if (bmcr & BMCR_ISO) {
312 		mii->mii_media_active |= IFM_NONE;
313 		mii->mii_media_status = 0;
314 		return;
315 	}
316 
317 	if (bmcr & BMCR_LOOP)
318 		mii->mii_media_active |= IFM_LOOP;
319 
320 	if (bmcr & BMCR_AUTOEN) {
321 		bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
322 		if ((bmsr & BMSR_ACOMP) == 0) {
323 			/* Erg, still trying, I guess... */
324 			mii->mii_media_active |= IFM_NONE;
325 			return;
326 		}
327 		if (csr & CSR_SPEED)
328 			mii->mii_media_active |= IFM_100_TX;
329 		else
330 			mii->mii_media_active |= IFM_10_T;
331 		if (csr & CSR_DUPLEX)
332 			mii->mii_media_active |= IFM_FDX;
333 	} else
334 		mii->mii_media_active = ife->ifm_media;
335 }
336 
337 static void
338 lxtphy_set_tp(sc)
339 	struct mii_softc *sc;
340 {
341 	int cfg;
342 
343 	cfg = PHY_READ(sc, MII_LXTPHY_CONFIG);
344 	cfg &= ~CONFIG_100BASEFX;
345 	PHY_WRITE(sc, MII_LXTPHY_CONFIG, cfg);
346 }
347 
348 static void
349 lxtphy_set_fx(sc)
350 	struct mii_softc *sc;
351 {
352 	int cfg;
353 
354 	cfg = PHY_READ(sc, MII_LXTPHY_CONFIG);
355 	cfg |= CONFIG_100BASEFX;
356 	PHY_WRITE(sc, MII_LXTPHY_CONFIG, cfg);
357 }
358 
359