ALTQ support
[dragonfly.git] / sys / dev / netif / mii_layer / rlphy.c
1 /*
2  * Copyright (c) 1997, 1998, 1999
3  *      Bill Paul <wpaul@ctr.columbia.edu>.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by Bill Paul.
16  * 4. Neither the name of the author nor the names of any co-contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
24  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
30  * THE POSSIBILITY OF SUCH DAMAGE.
31  *
32  * $FreeBSD: src/sys/dev/mii/rlphy.c,v 1.2.2.4 2002/11/08 21:53:49 semenu Exp $
33  * $DragonFly: src/sys/dev/netif/mii_layer/rlphy.c,v 1.5 2004/09/18 19:32:59 dillon Exp $
34  *
35  * $FreeBSD: src/sys/dev/mii/rlphy.c,v 1.2.2.4 2002/11/08 21:53:49 semenu Exp $
36  */
37
38 /*
39  * driver for RealTek 8139 internal PHYs
40  */
41
42 #include <sys/param.h>
43 #include <sys/systm.h>
44 #include <sys/kernel.h>
45 #include <sys/socket.h>
46 #include <sys/bus.h>
47
48 #include <net/if.h>
49 #include <net/if_arp.h>
50 #include <net/if_media.h>
51
52 #include "mii.h"
53 #include "miivar.h"
54 #include "miidevs.h"
55
56 #include <machine/bus.h>
57 #include "../rl/if_rlreg.h"
58
59 #include "miibus_if.h"
60
61 static int rlphy_probe          (device_t);
62 static int rlphy_attach         (device_t);
63 static int rlphy_detach         (device_t);
64
65 static device_method_t rlphy_methods[] = {
66         /* device interface */
67         DEVMETHOD(device_probe,         rlphy_probe),
68         DEVMETHOD(device_attach,        rlphy_attach),
69         DEVMETHOD(device_detach,        rlphy_detach),
70         DEVMETHOD(device_shutdown,      bus_generic_shutdown),
71         { 0, 0 }
72 };
73
74 static devclass_t rlphy_devclass;
75
76 static driver_t rlphy_driver = {
77         "rlphy",
78         rlphy_methods,
79         sizeof(struct mii_softc)
80 };
81
82 DRIVER_MODULE(rlphy, miibus, rlphy_driver, rlphy_devclass, 0, 0);
83
84 int     rlphy_service (struct mii_softc *, struct mii_data *, int);
85 void    rlphy_status (struct mii_softc *);
86
87 static int rlphy_probe(dev)
88         device_t                dev;
89 {
90         struct mii_attach_args *ma;
91         device_t                parent;
92
93         ma = device_get_ivars(dev);
94         parent = device_get_parent(device_get_parent(dev));
95
96         /* Test for RealTek 8201L PHY */
97         if (MII_OUI(ma->mii_id1, ma->mii_id2) == MII_OUI_REALTEK &&
98             MII_MODEL(ma->mii_id2) == MII_MODEL_REALTEK_RTL8201L) {
99                 device_set_desc(dev, MII_STR_REALTEK_RTL8201L);
100                 return(0);
101         }
102
103         /*
104          * RealTek PHY doesn't have vendor/device ID registers:
105          * the rl driver fakes up a return value of all zeros.
106          */
107         if (MII_OUI(ma->mii_id1, ma->mii_id2) != 0 ||
108             MII_MODEL(ma->mii_id2) != 0)
109                 return (ENXIO);
110
111         /*
112          * Make sure the parent is an `rl'.
113          */
114         if (strcmp(device_get_name(parent), "rl") != 0)
115                 return (ENXIO);
116
117         device_set_desc(dev, "RealTek internal media interface");
118
119         return (0);
120 }
121
122 static int rlphy_attach(dev)
123         device_t                dev;
124 {
125         struct mii_softc        *sc;
126         struct mii_attach_args  *ma;
127         struct mii_data         *mii;
128
129         sc = device_get_softc(dev);
130         ma = device_get_ivars(dev);
131         mii_softc_init(sc);
132         sc->mii_dev = device_get_parent(dev);
133         mii = device_get_softc(sc->mii_dev);
134
135         /*
136          * The RealTek PHY can never be isolated, so never allow non-zero
137          * instances!
138          */
139         if (mii->mii_instance != 0) {
140                 device_printf(dev, "ignoring this PHY, non-zero instance\n");
141                 return(ENXIO);
142         }
143
144         LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list);
145
146         sc->mii_inst = mii->mii_instance;
147         sc->mii_phy = ma->mii_phyno;
148         sc->mii_service = rlphy_service;
149         sc->mii_pdata = mii;
150         mii->mii_instance++;
151
152         sc->mii_flags |= MIIF_NOISOLATE;
153
154 #define ADD(m, c)       ifmedia_add(&mii->mii_media, (m), (c), NULL)
155
156 #if 0 /* See above. */
157         ADD(IFM_MAKEWORD(IFM_ETHER, IFM_NONE, 0, sc->mii_inst),
158             BMCR_ISO);
159 #endif
160
161         ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_LOOP, sc->mii_inst),
162             BMCR_LOOP|BMCR_S100);
163
164         mii_phy_reset(sc);
165
166         sc->mii_capabilities =
167             PHY_READ(sc, MII_BMSR) & ma->mii_capmask;
168         device_printf(dev, " ");
169         if ((sc->mii_capabilities & BMSR_MEDIAMASK) == 0)
170                 printf("no media present");
171         else
172                 mii_add_media(mii, sc->mii_capabilities,
173                     sc->mii_inst);
174         printf("\n");
175 #undef ADD
176         MIIBUS_MEDIAINIT(sc->mii_dev);
177         return(0);
178 }
179
180 static int rlphy_detach(dev)
181         device_t                dev;
182 {
183         struct mii_softc        *sc;
184         struct mii_data         *mii;
185
186         sc = device_get_softc(dev);
187         mii = device_get_softc(device_get_softc(dev));
188         mii_phy_auto_stop(sc);
189         sc->mii_dev = NULL;
190         LIST_REMOVE(sc, mii_list);
191
192         return(0);
193 }
194
195 int
196 rlphy_service(sc, mii, cmd)
197         struct mii_softc *sc;
198         struct mii_data *mii;
199         int cmd;
200 {
201         struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
202
203         /*
204          * We can't isolate the RealTek PHY, so it has to be the only one!
205          */
206         if (IFM_INST(ife->ifm_media) != sc->mii_inst)
207                 panic("rlphy_service: can't isolate RealTek PHY");
208
209         switch (cmd) {
210         case MII_POLLSTAT:
211                 break;
212
213         case MII_MEDIACHG:
214                 /*
215                  * If the interface is not up, don't do anything.
216                  */
217                 if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
218                         break;
219
220                 switch (IFM_SUBTYPE(ife->ifm_media)) {
221                 case IFM_AUTO:
222                         /*
223                          * If we're already in auto mode, just return.
224                          */
225                         if (PHY_READ(sc, MII_BMCR) & BMCR_AUTOEN)
226                                 return (0);
227                         (void) mii_phy_auto(sc, 0);
228                         break;
229                 case IFM_100_T4:
230                         /*
231                          * XXX Not supported as a manual setting right now.
232                          */
233                         return (EINVAL);
234                 default:
235                         /*
236                          * BMCR data is stored in the ifmedia entry.
237                          */
238                         PHY_WRITE(sc, MII_ANAR,
239                             mii_anar(ife->ifm_media));
240                         PHY_WRITE(sc, MII_BMCR, ife->ifm_data);
241                 }
242                 break;
243
244         case MII_TICK:
245                 /*
246                  * Only used for autonegotiation.
247                  */
248                 if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO)
249                         return (0);
250
251                 /*
252                  * Is the interface even up?
253                  */
254                 if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
255                         return (0);
256
257                 /*
258                  * Only retry autonegotiation every 5 seconds.
259                  */
260                 if (++sc->mii_ticks != 5)
261                         return (0);
262
263                 sc->mii_ticks = 0;
264
265                 /*
266                  * The RealTek PHY's autonegotiation doesn't need to be
267                  * kicked; it continues in the background.
268                  */
269                 break;
270         }
271
272         /* Update the media status. */
273         rlphy_status(sc);
274
275         /* Callback if something changed. */
276         if (sc->mii_active != mii->mii_media_active || cmd == MII_MEDIACHG) {
277                 MIIBUS_STATCHG(sc->mii_dev);
278                 sc->mii_active = mii->mii_media_active;
279         }
280         return (0);
281 }
282
283 void
284 rlphy_status(phy)
285         struct mii_softc *phy;
286 {
287         struct mii_data *mii = phy->mii_pdata;
288         int bmsr, bmcr, anlpar;
289         device_t                parent;
290
291         mii->mii_media_status = IFM_AVALID;
292         mii->mii_media_active = IFM_ETHER;
293
294         bmsr = PHY_READ(phy, MII_BMSR) | PHY_READ(phy, MII_BMSR);
295         if (bmsr & BMSR_LINK)
296                 mii->mii_media_status |= IFM_ACTIVE;
297
298         bmcr = PHY_READ(phy, MII_BMCR);
299         if (bmcr & BMCR_ISO) {
300                 mii->mii_media_active |= IFM_NONE;
301                 mii->mii_media_status = 0;
302                 return;
303         }
304
305         if (bmcr & BMCR_LOOP)
306                 mii->mii_media_active |= IFM_LOOP;
307
308         if (bmcr & BMCR_AUTOEN) {
309                 /*
310                  * NWay autonegotiation takes the highest-order common
311                  * bit of the ANAR and ANLPAR (i.e. best media advertised
312                  * both by us and our link partner).
313                  */
314                 if ((bmsr & BMSR_ACOMP) == 0) {
315                         /* Erg, still trying, I guess... */
316                         mii->mii_media_active |= IFM_NONE;
317                         return;
318                 }
319
320                 if ((anlpar = PHY_READ(phy, MII_ANAR) &
321                     PHY_READ(phy, MII_ANLPAR))) {
322                         if (anlpar & ANLPAR_T4)
323                                 mii->mii_media_active |= IFM_100_T4;
324                         else if (anlpar & ANLPAR_TX_FD)
325                                 mii->mii_media_active |= IFM_100_TX|IFM_FDX;
326                         else if (anlpar & ANLPAR_TX)
327                                 mii->mii_media_active |= IFM_100_TX;
328                         else if (anlpar & ANLPAR_10_FD)
329                                 mii->mii_media_active |= IFM_10_T|IFM_FDX;
330                         else if (anlpar & ANLPAR_10)
331                                 mii->mii_media_active |= IFM_10_T;
332                         else
333                                 mii->mii_media_active |= IFM_NONE; 
334                         return;
335                 }
336                 /*
337                  * If the other side doesn't support NWAY, then the
338                  * best we can do is determine if we have a 10Mbps or
339                  * 100Mbps link. There's no way to know if the link 
340                  * is full or half duplex, so we default to half duplex
341                  * and hope that the user is clever enough to manually
342                  * change the media settings if we're wrong.
343                  */
344
345
346                 /*
347                  * The RealTek PHY supports non-NWAY link speed
348                  * detection, however it does not report the link
349                  * detection results via the ANLPAR or BMSR registers.
350                  * (What? RealTek doesn't do things the way everyone
351                  * else does? I'm just shocked, shocked I tell you.)
352                  * To determine the link speed, we have to do one
353                  * of two things:
354                  *
355                  * - If this is a standalone RealTek RTL8201(L) PHY,
356                  *   we can determine the link speed by testing bit 0
357                  *   in the magic, vendor-specific register at offset
358                  *   0x19.
359                  *
360                  * - If this is a RealTek MAC with integrated PHY, we
361                  *   can test the 'SPEED10' bit of the MAC's media status
362                  *   register.
363                  */
364                 parent = device_get_parent(phy->mii_dev);
365                 if (strcmp(device_get_name(parent), "rl") != 0) {
366                         if (PHY_READ(phy, 0x0019) & 0x01)
367                                 mii->mii_media_active |= IFM_100_TX;
368                         else
369                                 mii->mii_media_active |= IFM_10_T;
370                 } else {
371                         if (PHY_READ(phy, RL_MEDIASTAT) &
372                             RL_MEDIASTAT_SPEED10)
373                                 mii->mii_media_active |= IFM_10_T;
374                         else
375                                 mii->mii_media_active |= IFM_100_TX;
376                 }
377
378         } else
379                 mii->mii_media_active = mii_media_from_bmcr(bmcr);
380 }