xref: /freebsd-12.1/sys/dev/mii/tlphy.c (revision 718cf2cc)
1 /*	$NetBSD: tlphy.c,v 1.18 1999/05/14 11:40:28 drochner Exp $	*/
2 
3 /*-
4  * SPDX-License-Identifier: BSD-2-Clause-NetBSD AND BSD-2-Clause
5  *
6  * Copyright (c) 1998, 1999 The NetBSD Foundation, Inc.
7  * All rights reserved.
8  *
9  * This code is derived from software contributed to The NetBSD Foundation
10  * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
11  * NASA Ames Research Center.
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above copyright
19  *    notice, this list of conditions and the following disclaimer in the
20  *    documentation and/or other materials provided with the distribution.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
23  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
24  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
25  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
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 THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  */
34 
35 /*-
36  * Copyright (c) 1997 Manuel Bouyer.  All rights reserved.
37  *
38  * Redistribution and use in source and binary forms, with or without
39  * modification, are permitted provided that the following conditions
40  * are met:
41  * 1. Redistributions of source code must retain the above copyright
42  *    notice, this list of conditions and the following disclaimer.
43  * 2. Redistributions in binary form must reproduce the above copyright
44  *    notice, this list of conditions and the following disclaimer in the
45  *    documentation and/or other materials provided with the distribution.
46  *
47  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
48  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
49  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
50  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
51  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
52  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
53  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
54  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
55  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
56  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
57  */
58 
59 #include <sys/cdefs.h>
60 __FBSDID("$FreeBSD$");
61 
62 /*
63  * Driver for Texas Instruments's ThunderLAN PHYs
64  */
65 
66 #include <sys/param.h>
67 #include <sys/systm.h>
68 #include <sys/kernel.h>
69 #include <sys/socket.h>
70 #include <sys/errno.h>
71 #include <sys/module.h>
72 #include <sys/bus.h>
73 #include <sys/malloc.h>
74 
75 #include <machine/bus.h>
76 
77 #include <net/if.h>
78 #include <net/if_media.h>
79 
80 #include <dev/mii/mii.h>
81 #include <dev/mii/miivar.h>
82 #include "miidevs.h"
83 
84 #include <dev/mii/tlphyreg.h>
85 
86 #include "miibus_if.h"
87 
88 struct tlphy_softc {
89 	struct mii_softc sc_mii;		/* generic PHY */
90 	int sc_need_acomp;
91 };
92 
93 static int tlphy_probe(device_t);
94 static int tlphy_attach(device_t);
95 
96 static device_method_t tlphy_methods[] = {
97 	/* device interface */
98 	DEVMETHOD(device_probe,		tlphy_probe),
99 	DEVMETHOD(device_attach,	tlphy_attach),
100 	DEVMETHOD(device_detach,	mii_phy_detach),
101 	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
102 	DEVMETHOD_END
103 };
104 
105 static devclass_t tlphy_devclass;
106 
107 static driver_t tlphy_driver = {
108 	"tlphy",
109 	tlphy_methods,
110 	sizeof(struct tlphy_softc)
111 };
112 
113 DRIVER_MODULE(tlphy, miibus, tlphy_driver, tlphy_devclass, 0, 0);
114 
115 static int	tlphy_service(struct mii_softc *, struct mii_data *, int);
116 static int	tlphy_auto(struct tlphy_softc *);
117 static void	tlphy_acomp(struct tlphy_softc *);
118 static void	tlphy_status(struct mii_softc *);
119 
120 static const struct mii_phydesc tlphys[] = {
121 	MII_PHY_DESC(TI, TLAN10T),
122 	MII_PHY_END
123 };
124 
125 static const struct mii_phy_funcs tlphy_funcs = {
126 	tlphy_service,
127 	tlphy_status,
128 	mii_phy_reset
129 };
130 
131 static int
tlphy_probe(device_t dev)132 tlphy_probe(device_t dev)
133 {
134 
135 	if (!mii_dev_mac_match(dev, "tl"))
136 		return (ENXIO);
137 	return (mii_phy_dev_probe(dev, tlphys, BUS_PROBE_DEFAULT));
138 }
139 
140 static int
tlphy_attach(device_t dev)141 tlphy_attach(device_t dev)
142 {
143 	device_t *devlist;
144 	struct mii_softc *other, *sc_mii;
145 	const char *sep = "";
146 	int capmask, devs, i;
147 
148 	sc_mii = device_get_softc(dev);
149 
150 	mii_phy_dev_attach(dev, MIIF_NOMANPAUSE, &tlphy_funcs, 0);
151 
152 	/*
153 	 * Note that if we're on a device that also supports 100baseTX,
154 	 * we are not going to want to use the built-in 10baseT port,
155 	 * since there will be another PHY on the MII wired up to the
156 	 * UTP connector.
157 	 */
158 	capmask = BMSR_DEFCAPMASK;
159 	if (sc_mii->mii_inst &&
160 	    device_get_children(sc_mii->mii_dev, &devlist, &devs) == 0) {
161 		for (i = 0; i < devs; i++) {
162 			if (devlist[i] != dev) {
163 				other = device_get_softc(devlist[i]);
164 				capmask &= ~other->mii_capabilities;
165 				break;
166 			}
167 		}
168 		free(devlist, M_TEMP);
169 	}
170 
171 	PHY_RESET(sc_mii);
172 
173 	sc_mii->mii_capabilities = PHY_READ(sc_mii, MII_BMSR) & capmask;
174 
175 #define	ADD(m, c)							\
176     ifmedia_add(&sc_mii->mii_pdata->mii_media, (m), (c), NULL)
177 #define	PRINT(s)	printf("%s%s", sep, s); sep = ", "
178 
179 	if ((sc_mii->mii_flags & (MIIF_MACPRIV0 | MIIF_MACPRIV1)) != 0 &&
180 	    (sc_mii->mii_capabilities & BMSR_MEDIAMASK) != 0)
181 		device_printf(dev, " ");
182 	if ((sc_mii->mii_flags & MIIF_MACPRIV0) != 0) {
183 		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_2, 0, sc_mii->mii_inst),
184 		    0);
185 		PRINT("10base2/BNC");
186 	}
187 	if ((sc_mii->mii_flags & MIIF_MACPRIV1) != 0) {
188 		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_5, 0, sc_mii->mii_inst),
189 		    0);
190 		PRINT("10base5/AUI");
191 	}
192 	if ((sc_mii->mii_capabilities & BMSR_MEDIAMASK) != 0) {
193 		printf("%s", sep);
194 		mii_phy_add_media(sc_mii);
195 	}
196 	if ((sc_mii->mii_flags & (MIIF_MACPRIV0 | MIIF_MACPRIV1)) != 0 &&
197 	    (sc_mii->mii_capabilities & BMSR_MEDIAMASK) != 0)
198 		printf("\n");
199 #undef ADD
200 #undef PRINT
201 
202 	MIIBUS_MEDIAINIT(sc_mii->mii_dev);
203 	return (0);
204 }
205 
206 static int
tlphy_service(struct mii_softc * self,struct mii_data * mii,int cmd)207 tlphy_service(struct mii_softc *self, struct mii_data *mii, int cmd)
208 {
209 	struct tlphy_softc *sc = (struct tlphy_softc *)self;
210 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
211 	int reg;
212 
213 	if (sc->sc_need_acomp)
214 		tlphy_acomp(sc);
215 
216 	switch (cmd) {
217 	case MII_POLLSTAT:
218 		break;
219 
220 	case MII_MEDIACHG:
221 		switch (IFM_SUBTYPE(ife->ifm_media)) {
222 		case IFM_AUTO:
223 			/*
224 			 * The ThunderLAN PHY doesn't self-configure after
225 			 * an autonegotiation cycle, so there's no such
226 			 * thing as "already in auto mode".
227 			 */
228 			(void)tlphy_auto(sc);
229 			break;
230 		case IFM_10_2:
231 		case IFM_10_5:
232 			PHY_WRITE(&sc->sc_mii, MII_BMCR, 0);
233 			PHY_WRITE(&sc->sc_mii, MII_TLPHY_CTRL, CTRL_AUISEL);
234 			DELAY(100000);
235 			break;
236 		default:
237 			PHY_WRITE(&sc->sc_mii, MII_TLPHY_CTRL, 0);
238 			DELAY(100000);
239 			mii_phy_setmedia(&sc->sc_mii);
240 		}
241 		break;
242 
243 	case MII_TICK:
244 		/*
245 		 * Only used for autonegotiation.
246 		 */
247 		if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO)
248 			break;
249 
250 		/*
251 		 * Check to see if we have link.  If we do, we don't
252 		 * need to restart the autonegotiation process.  Read
253 		 * the BMSR twice in case it's latched.
254 		 *
255 		 * XXX WHAT ABOUT CHECKING LINK ON THE BNC/AUI?!
256 		 */
257 		reg = PHY_READ(&sc->sc_mii, MII_BMSR) |
258 		    PHY_READ(&sc->sc_mii, MII_BMSR);
259 		if (reg & BMSR_LINK)
260 			break;
261 
262 		/*
263 		 * Only retry autonegotiation every 5 seconds.
264 		 */
265 		if (++sc->sc_mii.mii_ticks <= MII_ANEGTICKS)
266 			break;
267 
268 		sc->sc_mii.mii_ticks = 0;
269 		PHY_RESET(&sc->sc_mii);
270 		(void)tlphy_auto(sc);
271 		return (0);
272 	}
273 
274 	/* Update the media status. */
275 	PHY_STATUS(self);
276 
277 	/* Callback if something changed. */
278 	mii_phy_update(&sc->sc_mii, cmd);
279 	return (0);
280 }
281 
282 static void
tlphy_status(struct mii_softc * self)283 tlphy_status(struct mii_softc *self)
284 {
285 	struct tlphy_softc *sc = (struct tlphy_softc *)self;
286 	struct mii_data *mii = sc->sc_mii.mii_pdata;
287 	int bmsr, bmcr, tlctrl;
288 
289 	mii->mii_media_status = IFM_AVALID;
290 	mii->mii_media_active = IFM_ETHER;
291 
292 	bmcr = PHY_READ(&sc->sc_mii, MII_BMCR);
293 	if (bmcr & BMCR_ISO) {
294 		mii->mii_media_active |= IFM_NONE;
295 		mii->mii_media_status = 0;
296 		return;
297 	}
298 
299 	tlctrl = PHY_READ(&sc->sc_mii, MII_TLPHY_CTRL);
300 	if (tlctrl & CTRL_AUISEL) {
301 		mii->mii_media_status = 0;
302 		mii->mii_media_active = mii->mii_media.ifm_cur->ifm_media;
303 		return;
304 	}
305 
306 	bmsr = PHY_READ(&sc->sc_mii, MII_BMSR) |
307 	    PHY_READ(&sc->sc_mii, MII_BMSR);
308 	if (bmsr & BMSR_LINK)
309 		mii->mii_media_status |= IFM_ACTIVE;
310 
311 	if (bmcr & BMCR_LOOP)
312 		mii->mii_media_active |= IFM_LOOP;
313 
314 	/*
315 	 * Grr, braindead ThunderLAN PHY doesn't have any way to
316 	 * tell which media is actually active.  (Note it also
317 	 * doesn't self-configure after autonegotiation.)  We
318 	 * just have to report what's in the BMCR.
319 	 */
320 	if (bmcr & BMCR_FDX)
321 		mii->mii_media_active |= IFM_FDX | mii_phy_flowstatus(self);
322 	else
323 		mii->mii_media_active |= IFM_HDX;
324 	mii->mii_media_active |= IFM_10_T;
325 }
326 
327 static int
tlphy_auto(struct tlphy_softc * sc)328 tlphy_auto(struct tlphy_softc *sc)
329 {
330 	int error;
331 
332 	switch ((error = mii_phy_auto(&sc->sc_mii))) {
333 	case EIO:
334 		/*
335 		 * Just assume we're not in full-duplex mode.
336 		 * XXX Check link and try AUI/BNC?
337 		 */
338 		PHY_WRITE(&sc->sc_mii, MII_BMCR, 0);
339 		break;
340 
341 	case EJUSTRETURN:
342 		/* Flag that we need to program when it completes. */
343 		sc->sc_need_acomp = 1;
344 		break;
345 
346 	default:
347 		tlphy_acomp(sc);
348 	}
349 
350 	return (error);
351 }
352 
353 static void
tlphy_acomp(struct tlphy_softc * sc)354 tlphy_acomp(struct tlphy_softc *sc)
355 {
356 	int aner, anlpar;
357 
358 	sc->sc_need_acomp = 0;
359 
360 	/*
361 	 * Grr, braindead ThunderLAN PHY doesn't self-configure
362 	 * after autonegotiation.  We have to do it ourselves
363 	 * based on the link partner status.
364 	 */
365 
366 	aner = PHY_READ(&sc->sc_mii, MII_ANER);
367 	if (aner & ANER_LPAN) {
368 		anlpar = PHY_READ(&sc->sc_mii, MII_ANLPAR) &
369 		    PHY_READ(&sc->sc_mii, MII_ANAR);
370 		if (anlpar & ANAR_10_FD) {
371 			PHY_WRITE(&sc->sc_mii, MII_BMCR, BMCR_FDX);
372 			return;
373 		}
374 	}
375 	PHY_WRITE(&sc->sc_mii, MII_BMCR, 0);
376 }
377