<net/if.h>: Do not include <net/if_var.h> for _KERNEL
[dragonfly.git] / sys / dev / netif / mii_layer / jmphy.c
1 /*-
2  * Copyright (c) 2008, Pyun YongHyeon <yongari@FreeBSD.org>
3  * 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 unmodified, this list of conditions, and the following
10  *    disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  * $FreeBSD: src/sys/dev/mii/jmphy.c,v 1.1 2008/05/27 01:16:40 yongari Exp $
28  */
29
30 /*
31  * Driver for the JMicron JMP211 10/100/1000, JMP202 10/100 PHY.
32  */
33
34 #include <sys/param.h>
35 #include <sys/bus.h>
36 #include <sys/kernel.h>
37
38 #include <net/if.h>
39 #include <net/if_var.h>
40 #include <net/if_media.h>
41
42 #include <dev/netif/mii_layer/mii.h>
43 #include <dev/netif/mii_layer/miivar.h>
44 #include <dev/netif/mii_layer/jmphyreg.h>
45
46 #include "miibus_if.h"
47 #include "miidevs.h"
48
49 static int      jmphy_service(struct mii_softc *, struct mii_data *, int);
50 static void     jmphy_status(struct mii_softc *);
51 static int      jmphy_probe(device_t);
52 static int      jmphy_attach(device_t);
53 static void     jmphy_reset(struct mii_softc *);
54 static uint16_t jmphy_anar(struct ifmedia_entry *);
55 static int      jmphy_auto(struct mii_softc *, struct ifmedia_entry *);
56
57 static device_method_t jmphy_methods[] = {
58         /* Device interface. */
59         DEVMETHOD(device_probe,         jmphy_probe),
60         DEVMETHOD(device_attach,        jmphy_attach),
61         DEVMETHOD(device_detach,        ukphy_detach),
62         DEVMETHOD(device_shutdown,      bus_generic_shutdown),
63         { NULL, NULL }
64 };
65
66 static devclass_t jmphy_devclass;
67 static driver_t jmphy_driver = {
68         "jmphy",
69         jmphy_methods,
70         sizeof(struct mii_softc)
71 };
72
73 DRIVER_MODULE(jmphy, miibus, jmphy_driver, jmphy_devclass, NULL, NULL);
74
75 static const struct mii_phydesc jmphys[] = {
76         MII_PHYDESC(JMICRON, JMP202),
77         MII_PHYDESC(JMICRON, JMP211),
78         MII_PHYDESC_NULL
79 };
80
81 static int
82 jmphy_probe(device_t dev)
83 {
84         struct mii_attach_args *ma = device_get_ivars(dev);
85         const struct mii_phydesc *mpd;
86
87         mpd = mii_phy_match(ma, jmphys);
88         if (mpd != NULL) {
89                 device_set_desc(dev, mpd->mpd_name);
90                 return 0;
91         }
92         return ENXIO;
93 }
94
95 static int
96 jmphy_attach(device_t dev)
97 {
98         struct mii_softc *sc;
99         struct mii_attach_args *ma;
100         struct mii_data *mii;
101
102         sc = device_get_softc(dev);
103         ma = device_get_ivars(dev);
104
105         mii_softc_init(sc, ma);
106         sc->mii_dev = device_get_parent(dev);
107         mii = device_get_softc(sc->mii_dev);
108         LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list);
109
110         sc->mii_inst = mii->mii_instance;
111         sc->mii_service = jmphy_service;
112         sc->mii_reset = jmphy_reset;
113         sc->mii_pdata = mii;
114
115         mii->mii_instance++;
116
117         jmphy_reset(sc);
118
119         sc->mii_capabilities = PHY_READ(sc, MII_BMSR) & ma->mii_capmask;
120         if (sc->mii_capabilities & BMSR_EXTSTAT)
121                 sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR);
122
123         device_printf(dev, " ");
124         if ((sc->mii_capabilities & BMSR_MEDIAMASK) == 0 &&
125             (sc->mii_extcapabilities & EXTSR_MEDIAMASK) == 0)
126                 kprintf("no media present");
127         else
128                 mii_phy_add_media(sc);
129         kprintf("\n");
130
131         MIIBUS_MEDIAINIT(sc->mii_dev);
132         return(0);
133 }
134
135 static int
136 jmphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
137 {
138         struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
139         uint16_t bmcr;
140
141         switch (cmd) {
142         case MII_POLLSTAT:
143                 /*
144                  * If we're not polling our PHY instance, just return.
145                  */
146                 if (IFM_INST(ife->ifm_media) != sc->mii_inst)
147                         return (0);
148                 break;
149
150         case MII_MEDIACHG:
151                 /*
152                  * If the media indicates a different PHY instance,
153                  * isolate ourselves.
154                  */
155                 if (IFM_INST(ife->ifm_media) != sc->mii_inst) {
156                         bmcr = PHY_READ(sc, MII_BMCR);
157                         PHY_WRITE(sc, MII_BMCR, bmcr | BMCR_ISO);
158                         return (0);
159                 }
160
161                 /*
162                  * If the interface is not up, don't do anything.
163                  */
164                 if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
165                         break;
166
167                 if (jmphy_auto(sc, ife) != EJUSTRETURN)
168                         return (EINVAL);
169                 break;
170
171         case MII_TICK:
172                 /*
173                  * If we're not currently selected, just return.
174                  */
175                 if (IFM_INST(ife->ifm_media) != sc->mii_inst)
176                         return (0);
177
178                 /*
179                  * Is the interface even up?
180                  */
181                 if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
182                         return (0);
183
184                 /*
185                  * Only used for autonegotiation.
186                  */
187                 if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO)
188                         break;
189
190                 /* Check for link. */
191                 if (PHY_READ(sc, JMPHY_SSR) & JMPHY_SSR_LINK_UP) {
192                         sc->mii_ticks = 0;
193                         break;
194                 }
195
196                 /* Announce link loss right after it happens. */
197                 if (sc->mii_ticks++ == 0)
198                         break;
199                 if (sc->mii_ticks <= sc->mii_anegticks)
200                         return (0);
201
202                 sc->mii_ticks = 0;
203                 jmphy_auto(sc, ife);
204                 break;
205         }
206
207         /* Update the media status. */
208         jmphy_status(sc);
209
210         /* Callback if something changed. */
211         mii_phy_update(sc, cmd);
212         return (0);
213 }
214
215 static void
216 jmphy_status(struct mii_softc *sc)
217 {
218         struct mii_data *mii = sc->mii_pdata;
219         int bmcr, ssr;
220
221         mii->mii_media_status = IFM_AVALID;
222         mii->mii_media_active = IFM_ETHER;
223
224         ssr = PHY_READ(sc, JMPHY_SSR);
225         if ((ssr & JMPHY_SSR_LINK_UP) != 0)
226                 mii->mii_media_status |= IFM_ACTIVE;
227
228         bmcr = PHY_READ(sc, MII_BMCR);
229         if ((bmcr & BMCR_ISO) != 0) {
230                 mii->mii_media_active |= IFM_NONE;
231                 mii->mii_media_status = 0;
232                 return;
233         }
234
235         if ((bmcr & BMCR_LOOP) != 0)
236                 mii->mii_media_active |= IFM_LOOP;
237
238         if ((ssr & JMPHY_SSR_SPD_DPLX_RESOLVED) == 0) {
239                 /* Erg, still trying, I guess... */
240                 mii->mii_media_active |= IFM_NONE;
241                 return;
242         }
243
244         switch ((ssr & JMPHY_SSR_SPEED_MASK)) {
245         case JMPHY_SSR_SPEED_1000:
246                 mii->mii_media_active |= IFM_1000_T;
247                 /*
248                  * jmphy(4) got a valid link so reset mii_ticks.
249                  * Resetting mii_ticks is needed in order to
250                  * detect link loss after auto-negotiation.
251                  */
252                 sc->mii_ticks = 0;
253                 break;
254         case JMPHY_SSR_SPEED_100:
255                 mii->mii_media_active |= IFM_100_TX;
256                 sc->mii_ticks = 0;
257                 break;
258         case JMPHY_SSR_SPEED_10:
259                 mii->mii_media_active |= IFM_10_T;
260                 sc->mii_ticks = 0;
261                 break;
262         default:
263                 mii->mii_media_active |= IFM_NONE;
264                 return;
265         }
266
267         if ((ssr & JMPHY_SSR_DUPLEX) != 0)
268                 mii->mii_media_active |= IFM_FDX;
269         else
270                 mii->mii_media_active |= IFM_HDX;
271         /* XXX Flow-control. */
272 #ifdef notyet
273         if (IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T) {
274                 if ((PHY_READ(sc, MII_100T2SR) & GTSR_MS_RES) != 0)
275                         mii->mii_media_active |= IFM_ETH_MASTER;
276         }
277 #endif
278 }
279
280 static void
281 jmphy_reset(struct mii_softc *sc)
282 {
283         int i;
284
285         /* Disable sleep mode. */
286         PHY_WRITE(sc, JMPHY_TMCTL,
287             PHY_READ(sc, JMPHY_TMCTL) & ~JMPHY_TMCTL_SLEEP_ENB);
288         PHY_WRITE(sc, MII_BMCR, BMCR_RESET | BMCR_AUTOEN);
289
290         for (i = 0; i < 1000; i++) {
291                 DELAY(1);
292                 if ((PHY_READ(sc, MII_BMCR) & BMCR_RESET) == 0)
293                         break;
294         }
295 }
296
297 static uint16_t
298 jmphy_anar(struct ifmedia_entry *ife)
299 {
300         uint16_t anar;
301
302         anar = 0;
303         switch (IFM_SUBTYPE(ife->ifm_media)) {
304         case IFM_AUTO:
305                 anar |= ANAR_TX_FD | ANAR_TX | ANAR_10_FD | ANAR_10;
306                 break;
307         case IFM_1000_T:
308                 break;
309         case IFM_100_TX:
310                 anar |= ANAR_TX | ANAR_TX_FD;
311                 break;
312         case IFM_10_T:
313                 anar |= ANAR_10 | ANAR_10_FD;
314                 break;
315         default:
316                 break;
317         }
318
319         return (anar);
320 }
321
322 static int
323 jmphy_auto(struct mii_softc *sc, struct ifmedia_entry *ife)
324 {
325         uint16_t anar, bmcr, gig;
326
327         gig = 0;
328         bmcr = PHY_READ(sc, MII_BMCR);
329         switch (IFM_SUBTYPE(ife->ifm_media)) {
330         case IFM_AUTO:
331                 gig |= GTCR_ADV_1000TFDX | GTCR_ADV_1000THDX;
332                 break;
333         case IFM_1000_T:
334                 gig |= GTCR_ADV_1000TFDX | GTCR_ADV_1000THDX;
335                 break;
336         case IFM_100_TX:
337         case IFM_10_T:
338                 break;
339         case IFM_NONE:
340                 PHY_WRITE(sc, MII_BMCR, bmcr | BMCR_ISO | BMCR_PDOWN);
341                 return (EJUSTRETURN);
342         default:
343                 return (EINVAL);
344         }
345
346         if ((ife->ifm_media & IFM_LOOP) != 0)
347                 bmcr |= BMCR_LOOP;
348
349         anar = jmphy_anar(ife);
350         /* XXX Always advertise pause capability. */
351         anar |= (3 << 10);
352
353         if ((sc->mii_flags & MIIF_HAVE_GTCR) != 0) {
354 #ifdef notyet
355                 struct mii_data *mii;
356
357                 mii = sc->mii_pdata;
358                 if ((mii->mii_media.ifm_media & IFM_ETH_MASTER) != 0)
359                         gig |= GTCR_MAN_MS | GTCR_MAN_ADV;
360 #endif
361                 PHY_WRITE(sc, MII_100T2CR, gig);
362         }
363         PHY_WRITE(sc, MII_ANAR, anar | ANAR_CSMA);
364         PHY_WRITE(sc, MII_BMCR, bmcr | BMCR_AUTOEN | BMCR_STARTNEG);
365
366         return (EJUSTRETURN);
367 }