007f93a9480d9bf28a787d72c0564d628ec7ed72
[dragonfly.git] / sys / dev / netif / mii_layer / qsphy.c
1 /*      $OpenBSD: qsphy.c,v 1.13 2005/02/19 06:00:04 brad Exp $ */
2 /*      NetBSD: qsphy.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/qsphy.c,v 1.1.2.2 2002/10/21 21:21:42 semenu 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 Quality Semiconductor's QS6612 ethernet 10/100 PHY
74  * datasheet from www.qualitysemi.com
75  */
76
77 #include <sys/param.h>
78 #include <sys/systm.h>
79 #include <sys/kernel.h>
80 #include <sys/socket.h>
81 #include <sys/errno.h>
82 #include <sys/module.h>
83 #include <sys/bus.h>
84
85 #include <net/if.h>
86 #include <net/if_media.h>
87
88 #include "mii.h"
89 #include "miivar.h"
90 #include "miidevs.h"
91
92 #include "qsphyreg.h"
93
94 #include "miibus_if.h"
95
96 static int qsphy_probe          (device_t);
97 static int qsphy_attach         (device_t);
98
99 static device_method_t qsphy_methods[] = {
100         /* device interface */
101         DEVMETHOD(device_probe,         qsphy_probe),
102         DEVMETHOD(device_attach,        qsphy_attach),
103         DEVMETHOD(device_detach,        ukphy_detach),
104         DEVMETHOD(device_shutdown,      bus_generic_shutdown),
105         DEVMETHOD_END
106 };
107
108 static const struct mii_phydesc qsphys[] = {
109         MII_PHYDESC(QUALSEMI,   QS6612),
110         MII_PHYDESC_NULL
111 };
112
113 static devclass_t qsphy_devclass;
114
115 static driver_t qsphy_driver = {
116         "qsphy",
117         qsphy_methods,
118         sizeof(struct mii_softc)
119 };
120
121 DRIVER_MODULE(qsphy, miibus, qsphy_driver, qsphy_devclass, NULL, NULL);
122
123 static int      qsphy_service(struct mii_softc *, struct mii_data *, int);
124 static void     qsphy_reset(struct mii_softc *);
125 static void     qsphy_status(struct mii_softc *);
126
127 static int
128 qsphy_probe(device_t dev)
129 {
130         struct mii_attach_args *ma = device_get_ivars(dev);
131         const struct mii_phydesc *mpd;
132
133         mpd = mii_phy_match(ma, qsphys);
134         if (mpd != NULL) {
135                 device_set_desc(dev, mpd->mpd_name);
136                 return (0);
137         }
138         return (ENXIO);
139 }
140
141 static int
142 qsphy_attach(device_t dev)
143 {
144         struct mii_softc *sc;
145         struct mii_attach_args *ma;
146         struct mii_data *mii;
147
148         sc = device_get_softc(dev);
149         ma = device_get_ivars(dev);
150         mii_softc_init(sc, ma);
151         sc->mii_dev = device_get_parent(dev);
152         mii = device_get_softc(sc->mii_dev);
153         LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list);
154
155         sc->mii_inst = mii->mii_instance;
156         sc->mii_service = qsphy_service;
157         sc->mii_reset = qsphy_reset;
158         sc->mii_pdata = mii;
159         sc->mii_flags |= MIIF_NOISOLATE;
160
161         qsphy_reset(sc);
162
163         mii->mii_instance++;
164
165         sc->mii_capabilities = PHY_READ(sc, MII_BMSR) & ma->mii_capmask;
166         device_printf(dev, " ");
167         if (sc->mii_capabilities & BMSR_MEDIAMASK)
168                 mii_phy_add_media(sc);
169         else
170                 kprintf("no media present");
171         kprintf("\n");
172
173         MIIBUS_MEDIAINIT(sc->mii_dev);
174         return (0);
175 }
176
177 static int
178 qsphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
179 {
180         struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
181         int reg;
182
183         /*
184          * If we're not selected, then do nothing, just isolate, if
185          * changing media.
186          */
187         if (IFM_INST(ife->ifm_media) != sc->mii_inst) {
188                 if (cmd == MII_MEDIACHG) {
189                         reg = PHY_READ(sc, MII_BMCR);
190                         PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO);
191                 }
192
193                 return (0);
194         }
195
196         switch (cmd) {
197         case MII_POLLSTAT:
198                 break;
199
200         case MII_MEDIACHG:
201                 /*
202                  * If the interface is not up, don't do anything.
203                  */
204                 if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
205                         break;
206
207                 mii_phy_set_media(sc);
208                 break;
209
210         case MII_TICK:
211                 /*
212                  * Is the interface even up?
213                  */
214                 if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
215                         return (0);
216
217                 /*
218                  * Only used for autonegotiation.
219                  */
220                 if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO)
221                         break;
222
223                 /*
224                  * This PHY's autonegotiation doesn't need to be kicked.
225                  */
226                 break;
227         }
228
229         /* Update the media status. */
230         qsphy_status(sc);
231
232         /* Callback if something changed. */
233         mii_phy_update(sc, cmd);
234         return (0);
235 }
236
237 static void
238 qsphy_status(struct mii_softc *sc)
239 {
240         struct mii_data *mii = sc->mii_pdata;
241         int bmsr, bmcr, pctl;
242
243         mii->mii_media_status = IFM_AVALID;
244         mii->mii_media_active = IFM_ETHER;
245
246         bmsr = PHY_READ(sc, MII_BMSR) |
247             PHY_READ(sc, MII_BMSR);
248         if (bmsr & BMSR_LINK)
249                 mii->mii_media_status |= IFM_ACTIVE;
250
251         bmcr = PHY_READ(sc, MII_BMCR);
252         if (bmcr & BMCR_ISO) {
253                 mii->mii_media_active |= IFM_NONE;
254                 mii->mii_media_status = 0;
255                 return;
256         }
257
258         if (bmcr & BMCR_LOOP)
259                 mii->mii_media_active |= IFM_LOOP;
260
261         if (bmcr & BMCR_AUTOEN) {
262                 if ((bmsr & BMSR_ACOMP) == 0) {
263                         /* Erg, still trying, I guess... */
264                         mii->mii_media_active |= IFM_NONE;
265                         return;
266                 }
267
268                 pctl = PHY_READ(sc, MII_QSPHY_PCTL);
269                 switch (pctl & PCTL_OPMASK) {
270                 case PCTL_10_T:
271                         mii->mii_media_active |= IFM_10_T;
272                         break;
273                 case PCTL_10_T_FDX:
274                         mii->mii_media_active |= IFM_10_T|IFM_FDX;
275                         break;
276                 case PCTL_100_TX:
277                         mii->mii_media_active |= IFM_100_TX;
278                         break;
279                 case PCTL_100_TX_FDX:
280                         mii->mii_media_active |= IFM_100_TX|IFM_FDX;
281                         break;
282                 case PCTL_100_T4:
283                         mii->mii_media_active |= IFM_100_T4;
284                         break;
285                 case PCTL_AN:
286                         mii->mii_media_active |= IFM_NONE;
287                         break;
288                 default:
289                         /* Erg... this shouldn't happen. */
290                         mii->mii_media_active |= IFM_NONE;
291                         break;
292                 }
293         } else {
294                 mii->mii_media_active = mii->mii_media.ifm_cur->ifm_media;
295         }
296 }
297
298 static void
299 qsphy_reset(struct mii_softc *sc)
300 {
301         mii_phy_reset(sc);
302         PHY_WRITE(sc, MII_QSPHY_IMASK, 0);
303 }