]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - 6/sys/dev/mii/ip1000phy.c
merge fix for boot-time hang on centos' xen
[FreeBSD/FreeBSD.git] / 6 / sys / dev / mii / ip1000phy.c
1 /*-
2  * Copyright (c) 2006, 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
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 /*
33  * Driver for the IC Plus IP1000A/IP1001 10/100/1000 PHY.
34  */
35
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/kernel.h>
39 #include <sys/module.h>
40 #include <sys/socket.h>
41 #include <sys/bus.h>
42
43 #include <net/if.h>
44 #include <net/if_media.h>
45
46 #include <dev/mii/mii.h>
47 #include <dev/mii/miivar.h>
48 #include "miidevs.h"
49
50 #include <dev/mii/ip1000phyreg.h>
51
52 #include "miibus_if.h"
53
54 #include <machine/bus.h>
55 #include <dev/stge/if_stgereg.h>
56
57 static int ip1000phy_probe(device_t);
58 static int ip1000phy_attach(device_t);
59
60 struct ip1000phy_softc {
61         struct mii_softc mii_sc;
62         int model;
63         int revision;
64 };
65
66 static device_method_t ip1000phy_methods[] = {
67         /* device interface */
68         DEVMETHOD(device_probe,         ip1000phy_probe),
69         DEVMETHOD(device_attach,        ip1000phy_attach),
70         DEVMETHOD(device_detach,        mii_phy_detach),
71         DEVMETHOD(device_shutdown,      bus_generic_shutdown),
72         { 0, 0 }
73 };
74
75 static devclass_t ip1000phy_devclass;
76 static driver_t ip1000phy_driver = {
77         "ip1000phy",
78         ip1000phy_methods,
79         sizeof (struct mii_softc)
80 };
81
82 DRIVER_MODULE(ip1000phy, miibus, ip1000phy_driver, ip1000phy_devclass, 0, 0);
83
84 static int      ip1000phy_service(struct mii_softc *, struct mii_data *, int);
85 static void     ip1000phy_status(struct mii_softc *);
86 static void     ip1000phy_reset(struct mii_softc *);
87 static int      ip1000phy_mii_phy_auto(struct mii_softc *);
88
89 static const struct mii_phydesc ip1000phys[] = {
90         MII_PHY_DESC(ICPLUS, IP1000A),
91         MII_PHY_DESC(ICPLUS, IP1001),
92         MII_PHY_END
93 };
94
95 static int
96 ip1000phy_probe(device_t dev)
97 {
98
99         return (mii_phy_dev_probe(dev, ip1000phys, BUS_PROBE_DEFAULT));
100 }
101
102 static int
103 ip1000phy_attach(device_t dev)
104 {
105         struct ip1000phy_softc *isc;
106         struct mii_softc *sc;
107         struct mii_attach_args *ma;
108         struct mii_data *mii;
109
110         isc = device_get_softc(dev);
111         sc = &isc->mii_sc;
112         ma = device_get_ivars(dev);
113         sc->mii_dev = device_get_parent(dev);
114         mii = device_get_softc(sc->mii_dev);
115         LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list);
116
117         sc->mii_inst = mii->mii_instance;
118         sc->mii_phy = ma->mii_phyno;
119         sc->mii_service = ip1000phy_service;
120         sc->mii_pdata = mii;
121         sc->mii_anegticks = MII_ANEGTICKS_GIGE;
122         sc->mii_flags |= MIIF_NOISOLATE;
123
124         mii->mii_instance++;
125
126         isc->model = MII_MODEL(ma->mii_id2);
127         isc->revision = MII_REV(ma->mii_id2);
128
129         device_printf(dev, " ");
130
131 #define ADD(m, c)       ifmedia_add(&mii->mii_media, (m), (c), NULL)
132
133         ADD(IFM_MAKEWORD(IFM_ETHER, IFM_NONE, 0, sc->mii_inst),
134             BMCR_ISO);
135
136         ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, 0, sc->mii_inst),
137             IP1000PHY_BMCR_10);
138         printf("10baseT, ");
139         ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, IFM_FDX, sc->mii_inst),
140             IP1000PHY_BMCR_10 | IP1000PHY_BMCR_FDX);
141         printf("10baseT-FDX, ");
142         ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, 0, sc->mii_inst),
143             IP1000PHY_BMCR_100);
144         printf("100baseTX, ");
145         ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_FDX, sc->mii_inst),
146             IP1000PHY_BMCR_100 | IP1000PHY_BMCR_FDX);
147         printf("100baseTX-FDX, ");
148         /* 1000baseT half-duplex, really supported? */
149         ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, 0, sc->mii_inst),
150             IP1000PHY_BMCR_1000);
151         printf("1000baseTX, ");
152         ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, IFM_FDX, sc->mii_inst),
153             IP1000PHY_BMCR_1000 | IP1000PHY_BMCR_FDX);
154         printf("1000baseTX-FDX, ");
155         ADD(IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, sc->mii_inst), 0);
156         printf("auto\n");
157 #undef ADD
158
159         ip1000phy_reset(sc);
160
161         MIIBUS_MEDIAINIT(sc->mii_dev);
162         return(0);
163 }
164
165 static int
166 ip1000phy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
167 {
168         struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
169         uint32_t gig, reg, speed;
170
171         switch (cmd) {
172         case MII_POLLSTAT:
173                 /*
174                  * If we're not polling our PHY instance, just return.
175                  */
176                 if (IFM_INST(ife->ifm_media) != sc->mii_inst)
177                         return (0);
178                 break;
179
180         case MII_MEDIACHG:
181                 /*
182                  * If the media indicates a different PHY instance,
183                  * isolate ourselves.
184                  */
185                 if (IFM_INST(ife->ifm_media) != sc->mii_inst) {
186                         reg = PHY_READ(sc, IP1000PHY_MII_BMCR);
187                         PHY_WRITE(sc, IP1000PHY_MII_BMCR,
188                             reg | IP1000PHY_BMCR_ISO);
189                         return (0);
190                 }
191
192                 /*
193                  * If the interface is not up, don't do anything.
194                  */
195                 if ((mii->mii_ifp->if_flags & IFF_UP) == 0) {
196                         break;
197                 }
198
199                 ip1000phy_reset(sc);
200                 switch (IFM_SUBTYPE(ife->ifm_media)) {
201                 case IFM_AUTO:
202                         (void)ip1000phy_mii_phy_auto(sc);
203                         goto done;
204                         break;
205
206                 case IFM_1000_T:
207                         /*
208                          * XXX
209                          * Manual 1000baseT setting doesn't seem to work.
210                          */
211                         speed = IP1000PHY_BMCR_1000;
212                         break;
213
214                 case IFM_100_TX:
215                         speed = IP1000PHY_BMCR_100;
216                         break;
217
218                 case IFM_10_T:
219                         speed = IP1000PHY_BMCR_10;
220                         break;
221
222                 default:
223                         return (EINVAL);
224                 }
225
226                 if (((ife->ifm_media & IFM_GMASK) & IFM_FDX) != 0) {
227                         speed |= IP1000PHY_BMCR_FDX;
228                         gig = IP1000PHY_1000CR_1000T_FDX;
229                 } else
230                         gig = IP1000PHY_1000CR_1000T;
231
232                 PHY_WRITE(sc, IP1000PHY_MII_1000CR, 0);
233                 PHY_WRITE(sc, IP1000PHY_MII_BMCR, speed);
234
235                 if (IFM_SUBTYPE(ife->ifm_media) != IFM_1000_T)
236                         break;
237
238                 PHY_WRITE(sc, IP1000PHY_MII_1000CR, gig);
239                 PHY_WRITE(sc, IP1000PHY_MII_BMCR, speed);
240
241                 /*
242                  * When setting the link manually, one side must
243                  * be the master and the other the slave. However
244                  * ifmedia doesn't give us a good way to specify
245                  * this, so we fake it by using one of the LINK
246                  * flags. If LINK0 is set, we program the PHY to
247                  * be a master, otherwise it's a slave.
248                  */
249                 if ((mii->mii_ifp->if_flags & IFF_LINK0))
250                         PHY_WRITE(sc, IP1000PHY_MII_1000CR, gig |
251                             IP1000PHY_1000CR_MASTER |
252                             IP1000PHY_1000CR_MMASTER |
253                             IP1000PHY_1000CR_MANUAL);
254                 else
255                         PHY_WRITE(sc, IP1000PHY_MII_1000CR, gig |
256                             IP1000PHY_1000CR_MASTER |
257                             IP1000PHY_1000CR_MANUAL);
258
259 done:
260                 break;
261
262         case MII_TICK:
263                 /*
264                  * If we're not currently selected, just return.
265                  */
266                 if (IFM_INST(ife->ifm_media) != sc->mii_inst)
267                         return (0);
268                 /*
269                  * Is the interface even up?
270                  */
271                 if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
272                         return (0);
273
274                 /*
275                  * Only used for autonegotiation.
276                  */
277                 if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) {
278                         sc->mii_ticks = 0;
279                         break;
280                 }
281
282                 /*
283                  * check for link.
284                  */
285                 reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
286                 if (reg & BMSR_LINK) {
287                         sc->mii_ticks = 0;
288                         break;
289                 }
290
291                 /* Announce link loss right after it happens */
292                 if (sc->mii_ticks++ == 0)
293                         break;
294
295                 /*
296                  * Only retry autonegotiation every mii_anegticks seconds.
297                  */
298                 if (sc->mii_ticks <= sc->mii_anegticks)
299                         return (0);
300
301                 sc->mii_ticks = 0;
302                 ip1000phy_mii_phy_auto(sc);
303                 break;
304         }
305
306         /* Update the media status. */
307         ip1000phy_status(sc);
308
309         /* Callback if something changed. */
310         mii_phy_update(sc, cmd);
311         return (0);
312 }
313
314 static void
315 ip1000phy_status(struct mii_softc *sc)
316 {
317         struct ip1000phy_softc *isc;
318         struct mii_data *mii = sc->mii_pdata;
319         uint32_t bmsr, bmcr, stat;
320         uint32_t ar, lpar;
321
322         isc = (struct ip1000phy_softc *)sc;
323
324         mii->mii_media_status = IFM_AVALID;
325         mii->mii_media_active = IFM_ETHER;
326
327         bmsr = PHY_READ(sc, IP1000PHY_MII_BMSR) |
328             PHY_READ(sc, IP1000PHY_MII_BMSR);
329         if ((bmsr & IP1000PHY_BMSR_LINK) != 0)
330                 mii->mii_media_status |= IFM_ACTIVE;
331
332         bmcr = PHY_READ(sc, IP1000PHY_MII_BMCR);
333         if ((bmcr & IP1000PHY_BMCR_LOOP) != 0)
334                 mii->mii_media_active |= IFM_LOOP;
335
336         if ((bmcr & IP1000PHY_BMCR_AUTOEN) != 0) {
337                 if ((bmsr & IP1000PHY_BMSR_ANEGCOMP) == 0) {
338                         /* Erg, still trying, I guess... */
339                         mii->mii_media_active |= IFM_NONE;
340                         return;
341                 }
342         }
343
344         if (isc->model == MII_MODEL_ICPLUS_IP1001) {
345                 stat = PHY_READ(sc, IP1000PHY_LSR);
346                 switch (stat & IP1000PHY_LSR_SPEED_MASK) {
347                 case IP1000PHY_LSR_SPEED_10:
348                         mii->mii_media_active |= IFM_10_T;
349                         break;
350                 case IP1000PHY_LSR_SPEED_100:
351                         mii->mii_media_active |= IFM_100_TX;
352                         break;
353                 case IP1000PHY_LSR_SPEED_1000:
354                         mii->mii_media_active |= IFM_1000_T;
355                         break;
356                 }
357                 if ((stat & IP1000PHY_LSR_FULL_DUPLEX) != 0)
358                         mii->mii_media_active |= IFM_FDX;
359                 else
360                         mii->mii_media_active |= IFM_HDX;
361         } else {
362                 stat = PHY_READ(sc, STGE_PhyCtrl);
363                 switch (PC_LinkSpeed(stat)) {
364                 case PC_LinkSpeed_Down:
365                         mii->mii_media_active |= IFM_NONE;
366                         return;
367                 case PC_LinkSpeed_10:
368                         mii->mii_media_active |= IFM_10_T;
369                         break;
370                 case PC_LinkSpeed_100:
371                         mii->mii_media_active |= IFM_100_TX;
372                         break;
373                 case PC_LinkSpeed_1000:
374                         mii->mii_media_active |= IFM_1000_T;
375                         break;
376                 }
377                 if ((stat & PC_PhyDuplexStatus) != 0)
378                         mii->mii_media_active |= IFM_FDX;
379                 else
380                         mii->mii_media_active |= IFM_HDX;
381         }
382
383         ar = PHY_READ(sc, IP1000PHY_MII_ANAR);
384         lpar = PHY_READ(sc, IP1000PHY_MII_ANLPAR);
385
386         /*
387          * FLAG0 : Rx flow-control
388          * FLAG1 : Tx flow-control
389          */
390         if ((ar & IP1000PHY_ANAR_PAUSE) && (lpar & IP1000PHY_ANLPAR_PAUSE))
391                 mii->mii_media_active |= IFM_FLAG0 | IFM_FLAG1;
392         else if (!(ar & IP1000PHY_ANAR_PAUSE) && (ar & IP1000PHY_ANAR_APAUSE) &&
393             (lpar & IP1000PHY_ANLPAR_PAUSE) && (lpar & IP1000PHY_ANLPAR_APAUSE))
394                 mii->mii_media_active |= IFM_FLAG1;
395         else if ((ar & IP1000PHY_ANAR_PAUSE) && (ar & IP1000PHY_ANAR_APAUSE) &&
396             !(lpar & IP1000PHY_ANLPAR_PAUSE) &&
397             (lpar & IP1000PHY_ANLPAR_APAUSE)) {
398                 mii->mii_media_active |= IFM_FLAG0;
399         }
400
401         /*
402          * FLAG2 : local PHY resolved to MASTER
403          */
404         if ((mii->mii_media_active & IFM_1000_T) != 0) {
405                 stat = PHY_READ(sc, IP1000PHY_MII_1000SR);
406                 if ((stat & IP1000PHY_1000SR_MASTER) != 0)
407                         mii->mii_media_active |= IFM_FLAG2;
408         }
409 }
410
411 static int
412 ip1000phy_mii_phy_auto(struct mii_softc *mii)
413 {
414         uint32_t reg;
415
416         PHY_WRITE(mii, IP1000PHY_MII_ANAR,
417             IP1000PHY_ANAR_10T | IP1000PHY_ANAR_10T_FDX |
418             IP1000PHY_ANAR_100TX | IP1000PHY_ANAR_100TX_FDX |
419             IP1000PHY_ANAR_PAUSE | IP1000PHY_ANAR_APAUSE);
420         reg = IP1000PHY_1000CR_1000T | IP1000PHY_1000CR_1000T_FDX;
421         reg |= IP1000PHY_1000CR_MASTER;
422         PHY_WRITE(mii, IP1000PHY_MII_1000CR, reg);
423         PHY_WRITE(mii, IP1000PHY_MII_BMCR, (IP1000PHY_BMCR_FDX |
424             IP1000PHY_BMCR_AUTOEN | IP1000PHY_BMCR_STARTNEG));
425
426         return (EJUSTRETURN);
427 }
428
429 static void
430 ip1000phy_load_dspcode(struct mii_softc *sc)
431 {
432
433         PHY_WRITE(sc, 31, 0x0001);
434         PHY_WRITE(sc, 27, 0x01e0);
435         PHY_WRITE(sc, 31, 0x0002);
436         PHY_WRITE(sc, 27, 0xeb8e);
437         PHY_WRITE(sc, 31, 0x0000);
438         PHY_WRITE(sc, 30, 0x005e);
439         PHY_WRITE(sc, 9, 0x0700);
440
441         DELAY(50);
442 }
443
444 static void
445 ip1000phy_reset(struct mii_softc *sc)
446 {
447         struct ip1000phy_softc *isc;
448         struct stge_softc *stge_sc;
449         struct mii_data *mii;
450         uint32_t reg;
451
452         isc = (struct ip1000phy_softc *)sc;
453         mii_phy_reset(sc);
454
455         /* clear autoneg/full-duplex as we don't want it after reset */
456         reg = PHY_READ(sc, IP1000PHY_MII_BMCR);
457         reg &= ~(IP1000PHY_BMCR_AUTOEN | IP1000PHY_BMCR_FDX);
458         PHY_WRITE(sc, MII_BMCR, reg);
459
460         mii = sc->mii_pdata;
461         /*
462          * XXX There should be more general way to pass PHY specific
463          * data via mii interface.
464          */
465         if (isc->model == MII_MODEL_ICPLUS_IP1000A &&
466              strcmp(mii->mii_ifp->if_dname, "stge") == 0) {
467                 stge_sc = mii->mii_ifp->if_softc;
468                 if (stge_sc->sc_rev >= 0x40 && stge_sc->sc_rev <= 0x4e)
469                         ip1000phy_load_dspcode(sc);
470         }
471 }