e267f4a61ee435a055b57a1fe9c75dde81c40899
[dragonfly.git] / sys / dev / netif / mii_layer / lxtphy.c
1 /*      $OpenBSD: lxtphy.c,v 1.14 2005/02/19 06:00:04 brad Exp $        */
2 /*      NetBSD: lxtphy.c,v 1.19 2000/02/02 23:34:57 thorpej Exp         */
3
4 /*-
5  * Copyright (c) 1998, 1999 The NetBSD Foundation, Inc.
6  * All rights reserved.
7  *
8  * This code is derived from software contributed to The NetBSD Foundation
9  * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
10  * NASA Ames Research Center.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. All advertising materials mentioning features or use of this software
21  *    must display the following acknowledgement:
22  *      This product includes software developed by the NetBSD
23  *      Foundation, Inc. and its contributors.
24  * 4. Neither the name of The NetBSD Foundation nor the names of its
25  *    contributors may be used to endorse or promote products derived
26  *    from this software without specific prior written permission.
27  *
28  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
29  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
30  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
31  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
32  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
33  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
34  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
35  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
36  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
37  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
38  * POSSIBILITY OF SUCH DAMAGE.
39  *
40  * $FreeBSD: src/sys/dev/mii/lxtphy.c,v 1.1.2.1 2001/06/08 19:58:33 semenu Exp $
41  * $DragonFly: src/sys/dev/netif/mii_layer/lxtphy.c,v 1.10 2006/12/22 23:26:20 swildner Exp $
42  */
43  
44 /*
45  * Copyright (c) 1997 Manuel Bouyer.  All rights reserved.
46  *
47  * Redistribution and use in source and binary forms, with or without
48  * modification, are permitted provided that the following conditions
49  * are met:
50  * 1. Redistributions of source code must retain the above copyright
51  *    notice, this list of conditions and the following disclaimer.
52  * 2. Redistributions in binary form must reproduce the above copyright
53  *    notice, this list of conditions and the following disclaimer in the
54  *    documentation and/or other materials provided with the distribution.
55  * 3. All advertising materials mentioning features or use of this software
56  *    must display the following acknowledgement:
57  *      This product includes software developed by Manuel Bouyer.
58  * 4. The name of the author may not be used to endorse or promote products
59  *    derived from this software without specific prior written permission.
60  *
61  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
62  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
63  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
64  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
65  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
66  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
67  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
68  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
69  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
70  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
71  */
72
73 /*
74  * driver for Level One's LXT-970/LXT-971 ethernet 10/100 PHY
75  * datasheet from www.level1.com
76  */
77
78 #include <sys/param.h>
79 #include <sys/systm.h>
80 #include <sys/kernel.h>
81 #include <sys/socket.h>
82 #include <sys/errno.h>
83 #include <sys/module.h>
84 #include <sys/bus.h>
85
86 #include <net/if.h>
87 #include <net/if_media.h>
88
89 #include "mii.h"
90 #include "miivar.h"
91 #include "miidevs.h"
92
93 #include "lxtphyreg.h"
94
95 #include "miibus_if.h"
96
97 static int      lxtphy_probe(device_t);
98 static int      lxtphy_attach(device_t);
99
100 static int      lxtphy_service(struct mii_softc *, struct mii_data *, int);
101 static void     lxtphy_status(struct mii_softc *);
102 static void     lxtphy_reset(struct mii_softc *);
103 static void     lxtphy_set_tp(struct mii_softc *);
104 static void     lxtphy_set_fx(struct mii_softc *);
105
106 static device_method_t lxtphy_methods[] = {
107         /* device interface */
108         DEVMETHOD(device_probe,         lxtphy_probe),
109         DEVMETHOD(device_attach,        lxtphy_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 lxtphys[] = {
116         MII_PHYDESC_ARG(xxLEVEL1,       LXT970, lxtphy_status),
117         MII_PHYDESC_ARG(xxLEVEL1a,      LXT971, ukphy_status),
118         MII_PHYDESC_NULL
119 };
120
121 static devclass_t lxtphy_devclass;
122
123 static driver_t lxtphy_driver = {
124         "lxtphy",
125         lxtphy_methods,
126         sizeof(struct mii_softc)
127 };
128
129 DRIVER_MODULE(lxtphy, miibus, lxtphy_driver, lxtphy_devclass, 0, 0);
130
131 static int
132 lxtphy_probe(device_t dev)
133 {
134         struct mii_attach_args *ma = device_get_ivars(dev);
135         const struct mii_phydesc *mpd;
136
137         mpd = mii_phy_match(ma, lxtphys);
138         if (mpd != NULL) {
139                 struct mii_softc *sc = device_get_softc(dev);
140
141                 sc->mii_status = mpd->mpd_priv;
142                 device_set_desc(dev, mpd->mpd_name);
143                 return (0);
144         }
145         return (ENXIO);
146 }
147
148 static int
149 lxtphy_attach(device_t dev)
150 {
151         struct mii_softc *sc;
152         struct mii_attach_args *ma;
153         struct mii_data *mii;
154
155         sc = device_get_softc(dev);
156         ma = device_get_ivars(dev);
157         mii_softc_init(sc, ma);
158         sc->mii_dev = device_get_parent(dev);
159         mii = device_get_softc(sc->mii_dev);
160         LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list);
161
162         sc->mii_inst = mii->mii_instance;
163         sc->mii_service = lxtphy_service;
164         sc->mii_reset = lxtphy_reset;
165         sc->mii_pdata = mii;
166         sc->mii_flags |= MIIF_NOISOLATE;
167
168         lxtphy_reset(sc);
169
170         mii->mii_instance++;
171
172 #define ADD(m, c)       ifmedia_add(&mii->mii_media, (m), (c), NULL)
173         ADD(IFM_MAKEWORD(IFM_ETHER, IFM_NONE, 0, sc->mii_inst),
174             MII_MEDIA_NONE);
175 #undef ADD
176
177         sc->mii_capabilities = PHY_READ(sc, MII_BMSR) & ma->mii_capmask;
178
179         device_printf(dev, " ");
180         if (sc->mii_capabilities & BMSR_MEDIAMASK)
181                 mii_phy_add_media(sc);
182         else
183                 kprintf("no media present");
184         kprintf("\n");
185
186         MIIBUS_MEDIAINIT(sc->mii_dev);
187         return(0);
188 }
189
190 static int
191 lxtphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
192 {
193         struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
194         int reg;
195
196         switch (cmd) {
197         case MII_POLLSTAT:
198                 /*
199                  * If we're not polling our PHY instance, just return.
200                  */
201                 if (IFM_INST(ife->ifm_media) != sc->mii_inst)
202                         return (0);
203                 break;
204
205         case MII_MEDIACHG:
206                 /*
207                  * If the media indicates a different PHY instance,
208                  * isolate ourselves.
209                  */
210                 if (IFM_INST(ife->ifm_media) != sc->mii_inst) {
211                         reg = PHY_READ(sc, MII_BMCR);
212                         PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO);
213                         return (0);
214                 }
215
216                 /*
217                  * If the interface is not up, don't do anything.
218                  */
219                 if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
220                         break;
221
222                 if (IFM_SUBTYPE(ife->ifm_media) == IFM_100_FX)
223                         lxtphy_set_fx(sc);
224                 else
225                         lxtphy_set_tp(sc);
226
227                 mii_phy_set_media(sc);
228                 break;
229
230         case MII_TICK:
231                 /*
232                  * If we're not currently selected, just return.
233                  */
234                 if (IFM_INST(ife->ifm_media) != sc->mii_inst)
235                         return (0);
236
237                 if (mii_phy_tick(sc) == EJUSTRETURN)
238                         return (0);
239                 break;
240         }
241
242         /* Update the media status. */
243         sc->mii_status(sc);
244
245         /* Callback if something changed. */
246         mii_phy_update(sc, cmd);
247         return (0);
248 }
249
250 static void
251 lxtphy_status(struct mii_softc *sc)
252 {
253         struct mii_data *mii = sc->mii_pdata;
254         struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
255         int bmcr, bmsr, csr;
256
257         mii->mii_media_status = IFM_AVALID;
258         mii->mii_media_active = IFM_ETHER;
259
260         /*
261          * Get link status from the CSR; we need to read the CSR
262          * for media type anyhow, and the link status in the CSR
263          * doens't latch, so fewer register reads are required.
264          */
265         csr = PHY_READ(sc, MII_LXTPHY_CSR);
266         if (csr & CSR_LINK)
267                 mii->mii_media_status |= IFM_ACTIVE;
268
269         bmcr = PHY_READ(sc, MII_BMCR);
270         if (bmcr & BMCR_ISO) {
271                 mii->mii_media_active |= IFM_NONE;
272                 mii->mii_media_status = 0;
273                 return;
274         }
275
276         if (bmcr & BMCR_LOOP)
277                 mii->mii_media_active |= IFM_LOOP;
278
279         if (bmcr & BMCR_AUTOEN) {
280                 bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
281                 if ((bmsr & BMSR_ACOMP) == 0) {
282                         /* Erg, still trying, I guess... */
283                         mii->mii_media_active |= IFM_NONE;
284                         return;
285                 }
286                 if (csr & CSR_SPEED)
287                         mii->mii_media_active |= IFM_100_TX;
288                 else
289                         mii->mii_media_active |= IFM_10_T;
290                 if (csr & CSR_DUPLEX)
291                         mii->mii_media_active |= IFM_FDX;
292         } else
293                 mii->mii_media_active = ife->ifm_media;
294 }
295
296 static void
297 lxtphy_set_tp(struct mii_softc *sc)
298 {
299         int cfg;
300
301         cfg = PHY_READ(sc, MII_LXTPHY_CONFIG);
302         cfg &= ~CONFIG_100BASEFX;
303         PHY_WRITE(sc, MII_LXTPHY_CONFIG, cfg);
304 }
305
306 static void
307 lxtphy_set_fx(struct mii_softc *sc)
308 {
309         int cfg;
310
311         cfg = PHY_READ(sc, MII_LXTPHY_CONFIG);
312         cfg |= CONFIG_100BASEFX;
313         PHY_WRITE(sc, MII_LXTPHY_CONFIG, cfg);
314 }
315
316 static void
317 lxtphy_reset(struct mii_softc *sc)
318 {
319         mii_phy_reset(sc);
320 #if 0
321         PHY_WRITE(sc, MII_LXTPHY_IER,
322                   PHY_READ(sc, MII_LXTPHY_IER) & ~IER_INTEN);
323 #endif
324 }