source: rtems-libbsd/freebsd/sys/dev/mii/rgephy.c @ 4b127e7

55-freebsd-126-freebsd-12freebsd-9.3
Last change on this file since 4b127e7 was 4b127e7, checked in by Chris Johns <chrisj@…>, on 05/04/16 at 06:01:08

Add RealTek? Gig PHY.

  • Property mode set to 100644
File size: 14.3 KB
Line 
1#include <machine/rtems-bsd-kernel-space.h>
2
3/*-
4 * Copyright (c) 2003
5 *      Bill Paul <wpaul@windriver.com>.  All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 *    must display the following acknowledgement:
17 *      This product includes software developed by Bill Paul.
18 * 4. Neither the name of the author nor the names of any co-contributors
19 *    may be used to endorse or promote products derived from this software
20 *    without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
26 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
32 * THE POSSIBILITY OF SUCH DAMAGE.
33 */
34
35#include <sys/cdefs.h>
36__FBSDID("$FreeBSD$");
37
38/*
39 * Driver for the RealTek 8169S/8110S/8211B/8211C internal 10/100/1000 PHY.
40 */
41
42#include <rtems/bsd/sys/param.h>
43#include <sys/systm.h>
44#include <sys/kernel.h>
45#include <sys/module.h>
46#include <sys/socket.h>
47#include <sys/bus.h>
48
49#include <net/if.h>
50#include <net/if_arp.h>
51#include <net/if_media.h>
52
53#include <dev/mii/mii.h>
54#include <dev/mii/miivar.h>
55#include <rtems/bsd/local/miidevs.h>
56
57#include <dev/mii/rgephyreg.h>
58
59#include <rtems/bsd/local/miibus_if.h>
60
61#include <machine/bus.h>
62#include <pci/if_rlreg.h>
63
64static int rgephy_probe(device_t);
65static int rgephy_attach(device_t);
66
67static device_method_t rgephy_methods[] = {
68        /* device interface */
69        DEVMETHOD(device_probe,         rgephy_probe),
70        DEVMETHOD(device_attach,        rgephy_attach),
71        DEVMETHOD(device_detach,        mii_phy_detach),
72        DEVMETHOD(device_shutdown,      bus_generic_shutdown),
73        DEVMETHOD_END
74};
75
76static devclass_t rgephy_devclass;
77
78static driver_t rgephy_driver = {
79        "rgephy",
80        rgephy_methods,
81        sizeof(struct mii_softc)
82};
83
84DRIVER_MODULE(rgephy, miibus, rgephy_driver, rgephy_devclass, 0, 0);
85
86static int      rgephy_service(struct mii_softc *, struct mii_data *, int);
87static void     rgephy_status(struct mii_softc *);
88static int      rgephy_mii_phy_auto(struct mii_softc *, int);
89static void     rgephy_reset(struct mii_softc *);
90static int      rgephy_linkup(struct mii_softc *);
91static void     rgephy_loop(struct mii_softc *);
92static void     rgephy_load_dspcode(struct mii_softc *);
93
94static const struct mii_phydesc rgephys[] = {
95        MII_PHY_DESC(REALTEK, RTL8169S),
96        MII_PHY_DESC(REALTEK, RTL8251),
97        MII_PHY_END
98};
99
100static const struct mii_phy_funcs rgephy_funcs = {
101        rgephy_service,
102        rgephy_status,
103        rgephy_reset
104};
105
106static int
107rgephy_probe(device_t dev)
108{
109
110        return (mii_phy_dev_probe(dev, rgephys, BUS_PROBE_DEFAULT));
111}
112
113static int
114rgephy_attach(device_t dev)
115{
116        struct mii_softc *sc;
117        struct mii_attach_args *ma;
118        u_int flags;
119
120        sc = device_get_softc(dev);
121        ma = device_get_ivars(dev);
122        flags = 0;
123        if (strcmp(ma->mii_data->mii_ifp->if_dname, "re") == 0)
124                flags |= MIIF_PHYPRIV0;
125        mii_phy_dev_attach(dev, flags, &rgephy_funcs, 0);
126
127        /* RTL8169S do not report auto-sense; add manually. */
128        sc->mii_capabilities = (PHY_READ(sc, MII_BMSR) | BMSR_ANEG) &
129            sc->mii_capmask;
130        if (sc->mii_capabilities & BMSR_EXTSTAT)
131                sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR);
132        device_printf(dev, " ");
133        mii_phy_add_media(sc);
134        printf("\n");
135        /*
136         * Allow IFM_FLAG0 to be set indicating that auto-negotiation with
137         * manual configuration, which is used to work around issues with
138         * certain setups by default, should not be triggered as it may in
139         * turn cause harm in some edge cases.
140         */
141        sc->mii_pdata->mii_media.ifm_mask |= IFM_FLAG0;
142
143        PHY_RESET(sc);
144
145        MIIBUS_MEDIAINIT(sc->mii_dev);
146        return (0);
147}
148
149static int
150rgephy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
151{
152        struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
153        int speed, gig, anar;
154
155        switch (cmd) {
156        case MII_POLLSTAT:
157                break;
158
159        case MII_MEDIACHG:
160                /*
161                 * If the interface is not up, don't do anything.
162                 */
163                if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
164                        break;
165
166                PHY_RESET(sc);  /* XXX hardware bug work-around */
167
168                anar = PHY_READ(sc, RGEPHY_MII_ANAR);
169                anar &= ~(RGEPHY_ANAR_PC | RGEPHY_ANAR_ASP |
170                    RGEPHY_ANAR_TX_FD | RGEPHY_ANAR_TX |
171                    RGEPHY_ANAR_10_FD | RGEPHY_ANAR_10);
172
173                switch (IFM_SUBTYPE(ife->ifm_media)) {
174                case IFM_AUTO:
175#ifdef foo
176                        /*
177                         * If we're already in auto mode, just return.
178                         */
179                        if (PHY_READ(sc, RGEPHY_MII_BMCR) & RGEPHY_BMCR_AUTOEN)
180                                return (0);
181#endif
182                        (void)rgephy_mii_phy_auto(sc, ife->ifm_media);
183                        break;
184                case IFM_1000_T:
185                        speed = RGEPHY_S1000;
186                        goto setit;
187                case IFM_100_TX:
188                        speed = RGEPHY_S100;
189                        anar |= RGEPHY_ANAR_TX_FD | RGEPHY_ANAR_TX;
190                        goto setit;
191                case IFM_10_T:
192                        speed = RGEPHY_S10;
193                        anar |= RGEPHY_ANAR_10_FD | RGEPHY_ANAR_10;
194setit:
195                        if ((ife->ifm_media & IFM_FLOW) != 0 &&
196                            (mii->mii_media.ifm_media & IFM_FLAG0) != 0)
197                                return (EINVAL);
198
199                        if ((ife->ifm_media & IFM_FDX) != 0) {
200                                speed |= RGEPHY_BMCR_FDX;
201                                gig = RGEPHY_1000CTL_AFD;
202                                anar &= ~(RGEPHY_ANAR_TX | RGEPHY_ANAR_10);
203                                if ((ife->ifm_media & IFM_FLOW) != 0 ||
204                                    (sc->mii_flags & MIIF_FORCEPAUSE) != 0)
205                                        anar |=
206                                            RGEPHY_ANAR_PC | RGEPHY_ANAR_ASP;
207                        } else {
208                                gig = RGEPHY_1000CTL_AHD;
209                                anar &=
210                                    ~(RGEPHY_ANAR_TX_FD | RGEPHY_ANAR_10_FD);
211                        }
212                        if (IFM_SUBTYPE(ife->ifm_media) == IFM_1000_T) {
213                                gig |= RGEPHY_1000CTL_MSE;
214                                if ((ife->ifm_media & IFM_ETH_MASTER) != 0)
215                                    gig |= RGEPHY_1000CTL_MSC;
216                        } else {
217                                gig = 0;
218                                anar &= ~RGEPHY_ANAR_ASP;
219                        }
220                        if ((mii->mii_media.ifm_media & IFM_FLAG0) == 0)
221                                speed |=
222                                    RGEPHY_BMCR_AUTOEN | RGEPHY_BMCR_STARTNEG;
223                        rgephy_loop(sc);
224                        PHY_WRITE(sc, RGEPHY_MII_1000CTL, gig);
225                        PHY_WRITE(sc, RGEPHY_MII_ANAR, anar);
226                        PHY_WRITE(sc, RGEPHY_MII_BMCR, speed);
227                        break;
228                case IFM_NONE:
229                        PHY_WRITE(sc, MII_BMCR, BMCR_ISO | BMCR_PDOWN);
230                        break;
231                default:
232                        return (EINVAL);
233                }
234                break;
235
236        case MII_TICK:
237                /*
238                 * Is the interface even up?
239                 */
240                if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
241                        return (0);
242
243                /*
244                 * Only used for autonegotiation.
245                 */
246                if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) {
247                        sc->mii_ticks = 0;
248                        break;
249                }
250
251                /*
252                 * Check to see if we have link.  If we do, we don't
253                 * need to restart the autonegotiation process.
254                 */
255                if (rgephy_linkup(sc) != 0) {
256                        sc->mii_ticks = 0;
257                        break;
258                }
259
260                /* Announce link loss right after it happens. */
261                if (sc->mii_ticks++ == 0)
262                        break;
263
264                /* Only retry autonegotiation every mii_anegticks seconds. */
265                if (sc->mii_ticks <= sc->mii_anegticks)
266                        return (0);
267
268                sc->mii_ticks = 0;
269                rgephy_mii_phy_auto(sc, ife->ifm_media);
270                break;
271        }
272
273        /* Update the media status. */
274        PHY_STATUS(sc);
275
276        /*
277         * Callback if something changed. Note that we need to poke
278         * the DSP on the RealTek PHYs if the media changes.
279         *
280         */
281        if (sc->mii_media_active != mii->mii_media_active ||
282            sc->mii_media_status != mii->mii_media_status ||
283            cmd == MII_MEDIACHG) {
284                rgephy_load_dspcode(sc);
285        }
286        mii_phy_update(sc, cmd);
287        return (0);
288}
289
290static int
291rgephy_linkup(struct mii_softc *sc)
292{
293        int linkup;
294        uint16_t reg;
295
296        linkup = 0;
297        if ((sc->mii_flags & MIIF_PHYPRIV0) == 0 &&
298            sc->mii_mpd_rev >= RGEPHY_8211B) {
299                if (sc->mii_mpd_rev == RGEPHY_8211F) {
300                        reg = PHY_READ(sc, RGEPHY_F_MII_SSR);
301                        if (reg & RGEPHY_F_SSR_LINK)
302                                linkup++;
303                } else {
304                        reg = PHY_READ(sc, RGEPHY_MII_SSR);
305                        if (reg & RGEPHY_SSR_LINK)
306                                linkup++;
307                }
308        } else {
309                reg = PHY_READ(sc, RL_GMEDIASTAT);
310                if (reg & RL_GMEDIASTAT_LINK)
311                        linkup++;
312        }
313
314        return (linkup);
315}
316
317static void
318rgephy_status(struct mii_softc *sc)
319{
320        struct mii_data *mii = sc->mii_pdata;
321        int bmsr, bmcr;
322        uint16_t ssr;
323
324        mii->mii_media_status = IFM_AVALID;
325        mii->mii_media_active = IFM_ETHER;
326
327        if (rgephy_linkup(sc) != 0)
328                mii->mii_media_status |= IFM_ACTIVE;
329
330        bmsr = PHY_READ(sc, RGEPHY_MII_BMSR);
331        bmcr = PHY_READ(sc, RGEPHY_MII_BMCR);
332        if (bmcr & RGEPHY_BMCR_ISO) {
333                mii->mii_media_active |= IFM_NONE;
334                mii->mii_media_status = 0;
335                return;
336        }
337
338        if (bmcr & RGEPHY_BMCR_LOOP)
339                mii->mii_media_active |= IFM_LOOP;
340
341        if (bmcr & RGEPHY_BMCR_AUTOEN) {
342                if ((bmsr & RGEPHY_BMSR_ACOMP) == 0) {
343                        /* Erg, still trying, I guess... */
344                        mii->mii_media_active |= IFM_NONE;
345                        return;
346                }
347        }
348
349        if ((sc->mii_flags & MIIF_PHYPRIV0) == 0 &&
350            sc->mii_mpd_rev >= RGEPHY_8211B) {
351                if (sc->mii_mpd_rev == RGEPHY_8211F) {
352                        ssr = PHY_READ(sc, RGEPHY_F_MII_SSR);
353                        switch (ssr & RGEPHY_F_SSR_SPD_MASK) {
354                        case RGEPHY_F_SSR_S1000:
355                                mii->mii_media_active |= IFM_1000_T;
356                                break;
357                        case RGEPHY_F_SSR_S100:
358                                mii->mii_media_active |= IFM_100_TX;
359                                break;
360                        case RGEPHY_F_SSR_S10:
361                                mii->mii_media_active |= IFM_10_T;
362                                break;
363                        default:
364                                mii->mii_media_active |= IFM_NONE;
365                                break;
366                        }
367                        if (ssr & RGEPHY_F_SSR_FDX)
368                                mii->mii_media_active |= IFM_FDX;
369                        else
370                                mii->mii_media_active |= IFM_HDX;
371
372                } else {
373                        ssr = PHY_READ(sc, RGEPHY_MII_SSR);
374                        switch (ssr & RGEPHY_SSR_SPD_MASK) {
375                        case RGEPHY_SSR_S1000:
376                                mii->mii_media_active |= IFM_1000_T;
377                                break;
378                        case RGEPHY_SSR_S100:
379                                mii->mii_media_active |= IFM_100_TX;
380                                break;
381                        case RGEPHY_SSR_S10:
382                                mii->mii_media_active |= IFM_10_T;
383                                break;
384                        default:
385                                mii->mii_media_active |= IFM_NONE;
386                                break;
387                        }
388                        if (ssr & RGEPHY_SSR_FDX)
389                                mii->mii_media_active |= IFM_FDX;
390                        else
391                                mii->mii_media_active |= IFM_HDX;
392                }
393        } else {
394                bmsr = PHY_READ(sc, RL_GMEDIASTAT);
395                if (bmsr & RL_GMEDIASTAT_1000MBPS)
396                        mii->mii_media_active |= IFM_1000_T;
397                else if (bmsr & RL_GMEDIASTAT_100MBPS)
398                        mii->mii_media_active |= IFM_100_TX;
399                else if (bmsr & RL_GMEDIASTAT_10MBPS)
400                        mii->mii_media_active |= IFM_10_T;
401                else
402                        mii->mii_media_active |= IFM_NONE;
403                if (bmsr & RL_GMEDIASTAT_FDX)
404                        mii->mii_media_active |= IFM_FDX;
405                else
406                        mii->mii_media_active |= IFM_HDX;
407        }
408
409        if ((mii->mii_media_active & IFM_FDX) != 0)
410                mii->mii_media_active |= mii_phy_flowstatus(sc);
411
412        if ((IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T) &&
413            (PHY_READ(sc, RGEPHY_MII_1000STS) & RGEPHY_1000STS_MSR) != 0)
414                mii->mii_media_active |= IFM_ETH_MASTER;
415}
416
417static int
418rgephy_mii_phy_auto(struct mii_softc *sc, int media)
419{
420        int anar;
421
422        rgephy_loop(sc);
423        PHY_RESET(sc);
424
425        anar = BMSR_MEDIA_TO_ANAR(sc->mii_capabilities) | ANAR_CSMA;
426        if ((media & IFM_FLOW) != 0 || (sc->mii_flags & MIIF_FORCEPAUSE) != 0)
427                anar |= RGEPHY_ANAR_PC | RGEPHY_ANAR_ASP;
428        PHY_WRITE(sc, RGEPHY_MII_ANAR, anar);
429        DELAY(1000);
430        PHY_WRITE(sc, RGEPHY_MII_1000CTL,
431            RGEPHY_1000CTL_AHD | RGEPHY_1000CTL_AFD);
432        DELAY(1000);
433        PHY_WRITE(sc, RGEPHY_MII_BMCR,
434            RGEPHY_BMCR_AUTOEN | RGEPHY_BMCR_STARTNEG);
435        DELAY(100);
436
437        return (EJUSTRETURN);
438}
439
440static void
441rgephy_loop(struct mii_softc *sc)
442{
443        int i;
444
445        if (sc->mii_mpd_model != MII_MODEL_REALTEK_RTL8251 &&
446            sc->mii_mpd_rev < RGEPHY_8211B) {
447                PHY_WRITE(sc, RGEPHY_MII_BMCR, RGEPHY_BMCR_PDOWN);
448                DELAY(1000);
449        }
450
451        for (i = 0; i < 15000; i++) {
452                if (!(PHY_READ(sc, RGEPHY_MII_BMSR) & RGEPHY_BMSR_LINK)) {
453#if 0
454                        device_printf(sc->mii_dev, "looped %d\n", i);
455#endif
456                        break;
457                }
458                DELAY(10);
459        }
460}
461
462#define PHY_SETBIT(x, y, z) \
463        PHY_WRITE(x, y, (PHY_READ(x, y) | (z)))
464#define PHY_CLRBIT(x, y, z) \
465        PHY_WRITE(x, y, (PHY_READ(x, y) & ~(z)))
466
467/*
468 * Initialize RealTek PHY per the datasheet. The DSP in the PHYs of
469 * existing revisions of the 8169S/8110S chips need to be tuned in
470 * order to reliably negotiate a 1000Mbps link. This is only needed
471 * for rev 0 and rev 1 of the PHY. Later versions work without
472 * any fixups.
473 */
474static void
475rgephy_load_dspcode(struct mii_softc *sc)
476{
477        int val;
478
479        if (sc->mii_mpd_model == MII_MODEL_REALTEK_RTL8251 ||
480            sc->mii_mpd_rev >= RGEPHY_8211B)
481                return;
482
483        PHY_WRITE(sc, 31, 0x0001);
484        PHY_WRITE(sc, 21, 0x1000);
485        PHY_WRITE(sc, 24, 0x65C7);
486        PHY_CLRBIT(sc, 4, 0x0800);
487        val = PHY_READ(sc, 4) & 0xFFF;
488        PHY_WRITE(sc, 4, val);
489        PHY_WRITE(sc, 3, 0x00A1);
490        PHY_WRITE(sc, 2, 0x0008);
491        PHY_WRITE(sc, 1, 0x1020);
492        PHY_WRITE(sc, 0, 0x1000);
493        PHY_SETBIT(sc, 4, 0x0800);
494        PHY_CLRBIT(sc, 4, 0x0800);
495        val = (PHY_READ(sc, 4) & 0xFFF) | 0x7000;
496        PHY_WRITE(sc, 4, val);
497        PHY_WRITE(sc, 3, 0xFF41);
498        PHY_WRITE(sc, 2, 0xDE60);
499        PHY_WRITE(sc, 1, 0x0140);
500        PHY_WRITE(sc, 0, 0x0077);
501        val = (PHY_READ(sc, 4) & 0xFFF) | 0xA000;
502        PHY_WRITE(sc, 4, val);
503        PHY_WRITE(sc, 3, 0xDF01);
504        PHY_WRITE(sc, 2, 0xDF20);
505        PHY_WRITE(sc, 1, 0xFF95);
506        PHY_WRITE(sc, 0, 0xFA00);
507        val = (PHY_READ(sc, 4) & 0xFFF) | 0xB000;
508        PHY_WRITE(sc, 4, val);
509        PHY_WRITE(sc, 3, 0xFF41);
510        PHY_WRITE(sc, 2, 0xDE20);
511        PHY_WRITE(sc, 1, 0x0140);
512        PHY_WRITE(sc, 0, 0x00BB);
513        val = (PHY_READ(sc, 4) & 0xFFF) | 0xF000;
514        PHY_WRITE(sc, 4, val);
515        PHY_WRITE(sc, 3, 0xDF01);
516        PHY_WRITE(sc, 2, 0xDF20);
517        PHY_WRITE(sc, 1, 0xFF95);
518        PHY_WRITE(sc, 0, 0xBF00);
519        PHY_SETBIT(sc, 4, 0x0800);
520        PHY_CLRBIT(sc, 4, 0x0800);
521        PHY_WRITE(sc, 31, 0x0000);
522
523        DELAY(40);
524}
525
526static void
527rgephy_reset(struct mii_softc *sc)
528{
529        uint16_t pcr, ssr;
530
531        switch (sc->mii_mpd_rev) {
532        case RGEPHY_8211F:
533                pcr = PHY_READ(sc, RGEPHY_F_MII_PCR1);
534                if ((pcr & RGEPHY_F_PCR1_MDI_MM) != 0) {
535                        pcr &= ~RGEPHY_F_PCR1_MDI_MM;
536                        PHY_WRITE(sc, RGEPHY_F_MII_PCR1, pcr);
537                }
538                break;
539        case RGEPHY_8211C:
540                if ((sc->mii_flags & MIIF_PHYPRIV0) == 0) {
541                        /* RTL8211C(L) */
542                        ssr = PHY_READ(sc, RGEPHY_MII_SSR);
543                        if ((ssr & RGEPHY_SSR_ALDPS) != 0) {
544                                ssr &= ~RGEPHY_SSR_ALDPS;
545                                PHY_WRITE(sc, RGEPHY_MII_SSR, ssr);
546                        }
547                }
548                /* FALLTHROUGH */
549        default:
550                if (sc->mii_mpd_rev >= RGEPHY_8211B) {
551                        pcr = PHY_READ(sc, RGEPHY_MII_PCR);
552                        if ((pcr & RGEPHY_PCR_MDIX_AUTO) == 0) {
553                                pcr &= ~RGEPHY_PCR_MDI_MASK;
554                                pcr |= RGEPHY_PCR_MDIX_AUTO;
555                                PHY_WRITE(sc, RGEPHY_MII_PCR, pcr);
556                        }
557                }
558                break;
559        }
560
561        mii_phy_reset(sc);
562        DELAY(1000);
563        rgephy_load_dspcode(sc);
564}
Note: See TracBrowser for help on using the repository browser.