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