]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/mii/tlphy.c
MFV r354582: file 5.37.
[FreeBSD/FreeBSD.git] / sys / dev / mii / tlphy.c
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
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
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
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
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
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
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 }