Add ifpoll, which support hardware TX/RX queues based polling.
[dragonfly.git] / sys / dev / netif / mii_layer / acphy.c
1 /*      $NetBSD: acphy.c,v 1.16 2006/03/29 07:05:24 thorpej 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/acphy.c,v 1.2.2.2 2002/10/21 21:20:19 semenu Exp $
40  * $DragonFly: src/sys/dev/netif/mii_layer/acphy.c,v 1.10 2006/12/22 23:26:20 swildner 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 Altima AC101 10/100 PHY
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
84 #include <net/if.h>
85 #include <net/if_media.h>
86
87 #include "mii.h"
88 #include "miivar.h"
89 #include "miidevs.h"
90
91 #include "acphyreg.h"
92
93 #include "miibus_if.h"
94
95 static int acphy_probe          (device_t);
96 static int acphy_attach         (device_t);
97
98 static device_method_t acphy_methods[] = {
99         /* device interface */
100         DEVMETHOD(device_probe,         acphy_probe),
101         DEVMETHOD(device_attach,        acphy_attach),
102         DEVMETHOD(device_detach,        ukphy_detach),
103         DEVMETHOD(device_shutdown,      bus_generic_shutdown),
104         { 0, 0 }
105 };
106
107 static const struct mii_phydesc acphys[] = {
108         MII_PHYDESC(xxALTIMA,   AC101),
109         MII_PHYDESC(xxALTIMA,   AC101L),
110         MII_PHYDESC(xxALTIMA,   AC_UNKNOWN),
111         MII_PHYDESC(xxALTIMA,   Am79C875),
112         MII_PHYDESC_NULL
113 };
114
115 static devclass_t acphy_devclass;
116
117 static driver_t acphy_driver = {
118         "acphy",
119         acphy_methods,
120         sizeof(struct mii_softc)
121 };
122
123 DRIVER_MODULE(acphy, miibus, acphy_driver, acphy_devclass, 0, 0);
124
125 static int      acphy_service(struct mii_softc *, struct mii_data *, int);
126 static void     acphy_reset(struct mii_softc *);
127 static void     acphy_status(struct mii_softc *);
128
129 static int
130 acphy_probe(device_t dev)
131 {
132         struct mii_attach_args *ma = device_get_ivars(dev);
133         const struct mii_phydesc *mpd;
134
135         mpd = mii_phy_match(ma, acphys);
136         if (mpd != NULL) {
137                 device_set_desc(dev, mpd->mpd_name);
138                 return (0);
139         }
140         return (ENXIO);
141 }
142
143 static int
144 acphy_attach(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         mii_softc_init(sc, ma);
153         sc->mii_dev = device_get_parent(dev);
154         mii = device_get_softc(sc->mii_dev);
155         LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list);
156
157         sc->mii_inst = mii->mii_instance;
158         sc->mii_service = acphy_service;
159         sc->mii_reset = acphy_reset;
160         sc->mii_pdata = mii;
161         sc->mii_flags |= MIIF_NOISOLATE;
162
163         acphy_reset(sc);
164
165         mii->mii_instance++;
166
167         sc->mii_capabilities = PHY_READ(sc, MII_BMSR) & ma->mii_capmask;
168         device_printf(dev, " ");
169         if (sc->mii_capabilities & BMSR_MEDIAMASK)
170                 mii_phy_add_media(sc);
171         else
172                 kprintf("no media present");
173         kprintf("\n");
174
175         MIIBUS_MEDIAINIT(sc->mii_dev);
176         return (0);
177 }
178
179 static int
180 acphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
181 {
182         struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
183         int reg;
184
185         /*
186          * If we're not selected, then do nothing, just isolate and power
187          * down, if changing media.
188          */
189         if (IFM_INST(ife->ifm_media) != sc->mii_inst) {
190                 if (cmd == MII_MEDIACHG) {
191                         reg = PHY_READ(sc, MII_BMCR);
192                         PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO | BMCR_PDOWN);
193                 }
194
195                 return (0);
196         }
197
198         switch (cmd) {
199         case MII_POLLSTAT:
200                 break;
201
202         case MII_MEDIACHG:
203                 /*
204                  * If the interface is not up, don't do anything.
205                  */
206                 if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
207                         break;
208
209                 mii_phy_set_media(sc);
210                 break;
211
212         case MII_TICK:
213                 /*
214                  * Is the interface even up?
215                  */
216                 if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
217                         return (0);
218
219                 /*
220                  * Only used for autonegotiation.
221                  */
222                 if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO)
223                         break;
224
225                 /*
226                  * This PHY's autonegotiation doesn't need to be kicked.
227                  */
228                 break;
229         }
230
231         /* Update the media status. */
232         acphy_status(sc);
233
234         /* Callback if something changed. */
235         mii_phy_update(sc, cmd);
236         return (0);
237 }
238
239 static void
240 acphy_status(struct mii_softc *sc)
241 {
242         struct mii_data *mii = sc->mii_pdata;
243         struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
244         int bmsr, bmcr;
245
246         mii->mii_media_status = IFM_AVALID;
247         mii->mii_media_active = IFM_ETHER;
248
249         bmsr = PHY_READ(sc, MII_BMSR) |
250             PHY_READ(sc, MII_BMSR);
251         if (bmsr & BMSR_LINK)
252                 mii->mii_media_status |= IFM_ACTIVE;
253
254         bmcr = PHY_READ(sc, MII_BMCR);
255         if (bmcr & BMCR_ISO) {
256                 mii->mii_media_active |= IFM_NONE;
257                 mii->mii_media_status = 0;
258                 return;
259         }
260
261         if (bmcr & BMCR_LOOP)
262                 mii->mii_media_active |= IFM_LOOP;
263
264         if (bmcr & BMCR_AUTOEN) {
265                 int diag;
266
267                 if ((bmsr & BMSR_ACOMP) == 0) {
268                         /* Erg, still trying, I guess... */
269                         mii->mii_media_active |= IFM_NONE;
270                         return;
271                 }
272
273                 diag = PHY_READ(sc, MII_ACPHY_DIAG);
274                 if (diag & AC_DIAG_SPEED)
275                         mii->mii_media_active |= IFM_100_TX;
276                 else
277                         mii->mii_media_active |= IFM_10_T;
278
279                 if (diag & AC_DIAG_DUPLEX)
280                         mii->mii_media_active |= IFM_FDX;
281         } else {
282                 mii->mii_media_active = ife->ifm_media;
283         }
284 }
285
286 static void
287 acphy_reset(struct mii_softc *sc)
288 {
289         mii_phy_reset(sc);
290         PHY_WRITE(sc, MII_ACPHY_INT, 0);
291 }