]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - sys/dev/mii/jmphy.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / sys / dev / mii / 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
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30
31 /*
32  * Driver for the JMicron JMP211 10/100/1000, JMP202 10/100 PHY.
33  */
34
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/kernel.h>
38 #include <sys/module.h>
39 #include <sys/socket.h>
40 #include <sys/bus.h>
41
42 #include <net/if.h>
43 #include <net/if_media.h>
44
45 #include <dev/mii/mii.h>
46 #include <dev/mii/miivar.h>
47 #include "miidevs.h"
48
49 #include <dev/mii/jmphyreg.h>
50
51 #include "miibus_if.h"
52
53 static int      jmphy_probe(device_t);
54 static int      jmphy_attach(device_t);
55 static void     jmphy_reset(struct mii_softc *);
56 static uint16_t jmphy_anar(struct ifmedia_entry *);
57 static int      jmphy_setmedia(struct mii_softc *, struct ifmedia_entry *);
58
59 static device_method_t jmphy_methods[] = {
60         /* Device interface. */
61         DEVMETHOD(device_probe,         jmphy_probe),
62         DEVMETHOD(device_attach,        jmphy_attach),
63         DEVMETHOD(device_detach,        mii_phy_detach),
64         DEVMETHOD(device_shutdown,      bus_generic_shutdown),
65         DEVMETHOD_END
66 };
67
68 static devclass_t jmphy_devclass;
69 static driver_t jmphy_driver = {
70         "jmphy",
71         jmphy_methods,
72         sizeof(struct mii_softc)
73 };
74
75 DRIVER_MODULE(jmphy, miibus, jmphy_driver, jmphy_devclass, 0, 0);
76
77 static int      jmphy_service(struct mii_softc *, struct mii_data *, int);
78 static void     jmphy_status(struct mii_softc *);
79
80 static const struct mii_phydesc jmphys[] = {
81         MII_PHY_DESC(JMICRON, JMP202),
82         MII_PHY_DESC(JMICRON, JMP211),
83         MII_PHY_END
84 };
85
86 static const struct mii_phy_funcs jmphy_funcs = {
87         jmphy_service,
88         jmphy_status,
89         jmphy_reset
90 };
91
92 static int
93 jmphy_probe(device_t dev)
94 {
95
96         return (mii_phy_dev_probe(dev, jmphys, BUS_PROBE_DEFAULT));
97 }
98
99 static int
100 jmphy_attach(device_t dev)
101 {
102         struct mii_attach_args *ma;
103         u_int flags;
104
105         ma = device_get_ivars(dev);
106         flags = 0;
107         if (strcmp(ma->mii_data->mii_ifp->if_dname, "jme") == 0 &&
108             (miibus_get_flags(dev) & MIIF_MACPRIV0) != 0)
109                 flags |= MIIF_PHYPRIV0;
110         mii_phy_dev_attach(dev, flags, &jmphy_funcs, 1);
111         return (0);
112 }
113
114 static int
115 jmphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
116 {
117         struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
118
119         switch (cmd) {
120         case MII_POLLSTAT:
121                 break;
122
123         case MII_MEDIACHG:
124                 /*
125                  * If the interface is not up, don't do anything.
126                  */
127                 if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
128                         break;
129
130                 if (jmphy_setmedia(sc, ife) != EJUSTRETURN)
131                         return (EINVAL);
132                 break;
133
134         case MII_TICK:
135                 /*
136                  * Is the interface even up?
137                  */
138                 if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
139                         return (0);
140
141                 /*
142                  * Only used for autonegotiation.
143                  */
144                 if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) {
145                         sc->mii_ticks = 0;
146                         break;
147                 }
148
149                 /* Check for link. */
150                 if ((PHY_READ(sc, JMPHY_SSR) & JMPHY_SSR_LINK_UP) != 0) {
151                         sc->mii_ticks = 0;
152                         break;
153                 }
154
155                 /* Announce link loss right after it happens. */
156                 if (sc->mii_ticks++ == 0)
157                         break;
158                 if (sc->mii_ticks <= sc->mii_anegticks)
159                         return (0);
160
161                 sc->mii_ticks = 0;
162                 (void)jmphy_setmedia(sc, ife);
163                 break;
164         }
165
166         /* Update the media status. */
167         PHY_STATUS(sc);
168
169         /* Callback if something changed. */
170         mii_phy_update(sc, cmd);
171         return (0);
172 }
173
174 static void
175 jmphy_status(struct mii_softc *sc)
176 {
177         struct mii_data *mii = sc->mii_pdata;
178         int bmcr, ssr;
179
180         mii->mii_media_status = IFM_AVALID;
181         mii->mii_media_active = IFM_ETHER;
182
183         ssr = PHY_READ(sc, JMPHY_SSR);
184         if ((ssr & JMPHY_SSR_LINK_UP) != 0)
185                 mii->mii_media_status |= IFM_ACTIVE;
186
187         bmcr = PHY_READ(sc, MII_BMCR);
188         if ((bmcr & BMCR_ISO) != 0) {
189                 mii->mii_media_active |= IFM_NONE;
190                 mii->mii_media_status = 0;
191                 return;
192         }
193
194         if ((bmcr & BMCR_LOOP) != 0)
195                 mii->mii_media_active |= IFM_LOOP;
196
197         if ((ssr & JMPHY_SSR_SPD_DPLX_RESOLVED) == 0) {
198                 /* Erg, still trying, I guess... */
199                 mii->mii_media_active |= IFM_NONE;
200                 return;
201         }
202
203         switch ((ssr & JMPHY_SSR_SPEED_MASK)) {
204         case JMPHY_SSR_SPEED_1000:
205                 mii->mii_media_active |= IFM_1000_T;
206                 /*
207                  * jmphy(4) got a valid link so reset mii_ticks.
208                  * Resetting mii_ticks is needed in order to
209                  * detect link loss after auto-negotiation.
210                  */
211                 sc->mii_ticks = 0;
212                 break;
213         case JMPHY_SSR_SPEED_100:
214                 mii->mii_media_active |= IFM_100_TX;
215                 sc->mii_ticks = 0;
216                 break;
217         case JMPHY_SSR_SPEED_10:
218                 mii->mii_media_active |= IFM_10_T;
219                 sc->mii_ticks = 0;
220                 break;
221         default:
222                 mii->mii_media_active |= IFM_NONE;
223                 return;
224         }
225
226         if ((ssr & JMPHY_SSR_DUPLEX) != 0)
227                 mii->mii_media_active |= IFM_FDX | mii_phy_flowstatus(sc);
228         else
229                 mii->mii_media_active |= IFM_HDX;
230
231         if (IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T) {
232                 if ((PHY_READ(sc, MII_100T2SR) & GTSR_MS_RES) != 0)
233                         mii->mii_media_active |= IFM_ETH_MASTER;
234         }
235 }
236
237 static void
238 jmphy_reset(struct mii_softc *sc)
239 {
240         uint16_t t2cr, val;
241         int i;
242
243         /* Disable sleep mode. */
244         PHY_WRITE(sc, JMPHY_TMCTL,
245             PHY_READ(sc, JMPHY_TMCTL) & ~JMPHY_TMCTL_SLEEP_ENB);
246         PHY_WRITE(sc, MII_BMCR, BMCR_RESET | BMCR_AUTOEN);
247
248         for (i = 0; i < 1000; i++) {
249                 DELAY(1);
250                 if ((PHY_READ(sc, MII_BMCR) & BMCR_RESET) == 0)
251                         break;
252         }
253         /* Perform vendor recommended PHY calibration. */
254         if ((sc->mii_flags & MIIF_PHYPRIV0) != 0) {
255                 /* Select PHY test mode 1. */
256                 t2cr = PHY_READ(sc, MII_100T2CR);
257                 t2cr &= ~GTCR_TEST_MASK;
258                 t2cr |= 0x2000;
259                 PHY_WRITE(sc, MII_100T2CR, t2cr);
260                 /* Apply calibration patch. */
261                 PHY_WRITE(sc, JMPHY_SPEC_ADDR, JMPHY_SPEC_ADDR_READ |
262                     JMPHY_EXT_COMM_2);
263                 val = PHY_READ(sc, JMPHY_SPEC_DATA);
264                 val &= ~0x0002;
265                 val |= 0x0010 | 0x0001;
266                 PHY_WRITE(sc, JMPHY_SPEC_DATA, val);
267                 PHY_WRITE(sc, JMPHY_SPEC_ADDR, JMPHY_SPEC_ADDR_WRITE |
268                     JMPHY_EXT_COMM_2);
269
270                 /* XXX 20ms to complete recalibration. */
271                 DELAY(20 * 1000);
272
273                 PHY_READ(sc, MII_100T2CR);
274                 PHY_WRITE(sc, JMPHY_SPEC_ADDR, JMPHY_SPEC_ADDR_READ |
275                     JMPHY_EXT_COMM_2);
276                 val = PHY_READ(sc, JMPHY_SPEC_DATA);
277                 val &= ~(0x0001 | 0x0002 | 0x0010);
278                 PHY_WRITE(sc, JMPHY_SPEC_DATA, val);
279                 PHY_WRITE(sc, JMPHY_SPEC_ADDR, JMPHY_SPEC_ADDR_WRITE |
280                     JMPHY_EXT_COMM_2);
281                 /* Disable PHY test mode. */
282                 PHY_READ(sc, MII_100T2CR);
283                 t2cr &= ~GTCR_TEST_MASK;
284                 PHY_WRITE(sc, MII_100T2CR, t2cr);
285         }
286 }
287
288 static uint16_t
289 jmphy_anar(struct ifmedia_entry *ife)
290 {
291         uint16_t anar;
292
293         anar = 0;
294         switch (IFM_SUBTYPE(ife->ifm_media)) {
295         case IFM_AUTO:
296                 anar |= ANAR_TX_FD | ANAR_TX | ANAR_10_FD | ANAR_10;
297                 break;
298         case IFM_1000_T:
299                 break;
300         case IFM_100_TX:
301                 anar |= ANAR_TX | ANAR_TX_FD;
302                 break;
303         case IFM_10_T:
304                 anar |= ANAR_10 | ANAR_10_FD;
305                 break;
306         default:
307                 break;
308         }
309
310         return (anar);
311 }
312
313 static int
314 jmphy_setmedia(struct mii_softc *sc, struct ifmedia_entry *ife)
315 {
316         uint16_t anar, bmcr, gig;
317
318         gig = 0;
319         bmcr = PHY_READ(sc, MII_BMCR);
320         switch (IFM_SUBTYPE(ife->ifm_media)) {
321         case IFM_AUTO:
322                 gig |= GTCR_ADV_1000TFDX | GTCR_ADV_1000THDX;
323                 break;
324         case IFM_1000_T:
325                 gig |= GTCR_ADV_1000TFDX | GTCR_ADV_1000THDX;
326                 break;
327         case IFM_100_TX:
328         case IFM_10_T:
329                 break;
330         case IFM_NONE:
331                 PHY_WRITE(sc, MII_BMCR, bmcr | BMCR_ISO | BMCR_PDOWN);
332                 return (EJUSTRETURN);
333         default:
334                 return (EINVAL);
335         }
336
337         anar = jmphy_anar(ife);
338         if ((IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO ||
339             (ife->ifm_media & IFM_FDX) != 0) &&
340             ((ife->ifm_media & IFM_FLOW) != 0 ||
341             (sc->mii_flags & MIIF_FORCEPAUSE) != 0))
342                 anar |= ANAR_PAUSE_TOWARDS;
343
344         if ((sc->mii_flags & MIIF_HAVE_GTCR) != 0) {
345                 if (IFM_SUBTYPE(ife->ifm_media) == IFM_1000_T) {
346                         gig |= GTCR_MAN_MS;
347                         if ((ife->ifm_media & IFM_ETH_MASTER) != 0)
348                                 gig |= GTCR_ADV_MS;
349                 }
350                 PHY_WRITE(sc, MII_100T2CR, gig);
351         }
352         PHY_WRITE(sc, MII_ANAR, anar | ANAR_CSMA);
353         PHY_WRITE(sc, MII_BMCR, bmcr | BMCR_AUTOEN | BMCR_STARTNEG);
354
355         return (EJUSTRETURN);
356 }