source: rtems-libbsd/freebsd/sys/dev/mii/e1000phy.c @ 26a8cee

55-freebsd-126-freebsd-12freebsd-9.3
Last change on this file since 26a8cee was 26a8cee, checked in by Chris Johns <chrisj@…>, on 08/11/16 at 07:41:49

e1000phy: Provide local support for Marvell's 88E1512 PHY.

This PHY is close to the standard PHY support so providing enough to
allow the PHY probe to pass lets it work on a MicroZed?.

  • Property mode set to 100644
File size: 14.1 KB
Line 
1#include <machine/rtems-bsd-kernel-space.h>
2
3/*-
4 * Principal Author: Parag Patel
5 * Copyright (c) 2001
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice unmodified, this list of conditions, and the following
13 *    disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 *
30 * Additional Copyright (c) 2001 by Traakan Software under same licence.
31 * Secondary Author: Matthew Jacob
32 */
33
34#include <sys/cdefs.h>
35__FBSDID("$FreeBSD$");
36
37/*
38 * driver for the Marvell 88E1000 series external 1000/100/10-BT PHY.
39 */
40
41/*
42 * Support added for the Marvell 88E1011 (Alaska) 1000/100/10baseTX and
43 * 1000baseSX PHY.
44 * Nathan Binkert <nate@openbsd.org>
45 * Jung-uk Kim <jkim@niksun.com>
46 */
47
48#include <rtems/bsd/sys/param.h>
49#include <sys/systm.h>
50#include <sys/kernel.h>
51#include <sys/module.h>
52#include <sys/socket.h>
53#include <sys/bus.h>
54
55
56#include <net/if.h>
57#include <net/if_media.h>
58
59#include <dev/mii/mii.h>
60#include <dev/mii/miivar.h>
61#include <rtems/bsd/local/miidevs.h>
62
63#ifdef __rtems__
64 /* hacked into here to avoid having to touch the geerated header file. */
65 #define MII_MODEL_xxMARVELL_E1512       0x001d
66 #define MII_STR_xxMARVELL_E1512 "Marvell 88E1512 Gigabit PHY"
67#endif /* __rtems__ */
68
69#include <dev/mii/e1000phyreg.h>
70
71#include <rtems/bsd/local/miibus_if.h>
72
73static int      e1000phy_probe(device_t);
74static int      e1000phy_attach(device_t);
75
76static device_method_t e1000phy_methods[] = {
77        /* device interface */
78        DEVMETHOD(device_probe,         e1000phy_probe),
79        DEVMETHOD(device_attach,        e1000phy_attach),
80        DEVMETHOD(device_detach,        mii_phy_detach),
81        DEVMETHOD(device_shutdown,      bus_generic_shutdown),
82        DEVMETHOD_END
83};
84
85static devclass_t e1000phy_devclass;
86static driver_t e1000phy_driver = {
87        "e1000phy",
88        e1000phy_methods,
89        sizeof(struct mii_softc)
90};
91
92DRIVER_MODULE(e1000phy, miibus, e1000phy_driver, e1000phy_devclass, 0, 0);
93
94static int      e1000phy_service(struct mii_softc *, struct mii_data *, int);
95static void     e1000phy_status(struct mii_softc *);
96static void     e1000phy_reset(struct mii_softc *);
97static int      e1000phy_mii_phy_auto(struct mii_softc *, int);
98
99static const struct mii_phydesc e1000phys[] = {
100        MII_PHY_DESC(MARVELL, E1000),
101        MII_PHY_DESC(MARVELL, E1011),
102        MII_PHY_DESC(MARVELL, E1000_3),
103        MII_PHY_DESC(MARVELL, E1000_5),
104        MII_PHY_DESC(MARVELL, E1111),
105        MII_PHY_DESC(xxMARVELL, E1000),
106        MII_PHY_DESC(xxMARVELL, E1011),
107        MII_PHY_DESC(xxMARVELL, E1000_3),
108        MII_PHY_DESC(xxMARVELL, E1000S),
109        MII_PHY_DESC(xxMARVELL, E1000_5),
110        MII_PHY_DESC(xxMARVELL, E1101),
111        MII_PHY_DESC(xxMARVELL, E3082),
112        MII_PHY_DESC(xxMARVELL, E1112),
113        MII_PHY_DESC(xxMARVELL, E1149),
114        MII_PHY_DESC(xxMARVELL, E1111),
115        MII_PHY_DESC(xxMARVELL, E1116),
116        MII_PHY_DESC(xxMARVELL, E1116R),
117        MII_PHY_DESC(xxMARVELL, E1118),
118        MII_PHY_DESC(xxMARVELL, E1149R),
119        MII_PHY_DESC(xxMARVELL, E3016),
120        MII_PHY_DESC(xxMARVELL, PHYG65G),
121#if __rtems__
122        MII_PHY_DESC(xxMARVELL, E1512),
123#endif /* __rtems__ */
124        MII_PHY_END
125};
126
127static const struct mii_phy_funcs e1000phy_funcs = {
128        e1000phy_service,
129        e1000phy_status,
130        e1000phy_reset
131};
132
133static int
134e1000phy_probe(device_t dev)
135{
136
137        return (mii_phy_dev_probe(dev, e1000phys, BUS_PROBE_DEFAULT));
138}
139
140static int
141e1000phy_attach(device_t dev)
142{
143        struct mii_softc *sc;
144        struct ifnet *ifp;
145
146        sc = device_get_softc(dev);
147
148        mii_phy_dev_attach(dev, MIIF_NOMANPAUSE, &e1000phy_funcs, 0);
149
150        ifp = sc->mii_pdata->mii_ifp;
151        if (strcmp(ifp->if_dname, "msk") == 0 &&
152            (sc->mii_flags & MIIF_MACPRIV0) != 0)
153                sc->mii_flags |= MIIF_PHYPRIV0;
154
155        switch (sc->mii_mpd_model) {
156        case MII_MODEL_xxMARVELL_E1011:
157        case MII_MODEL_xxMARVELL_E1112:
158                if (PHY_READ(sc, E1000_ESSR) & E1000_ESSR_FIBER_LINK)
159                        sc->mii_flags |= MIIF_HAVEFIBER;
160                break;
161        case MII_MODEL_xxMARVELL_E1149:
162        case MII_MODEL_xxMARVELL_E1149R:
163                /*
164                 * Some 88E1149 PHY's page select is initialized to
165                 * point to other bank instead of copper/fiber bank
166                 * which in turn resulted in wrong registers were
167                 * accessed during PHY operation. It is believed that
168                 * page 0 should be used for copper PHY so reinitialize
169                 * E1000_EADR to select default copper PHY. If parent
170                 * device know the type of PHY(either copper or fiber),
171                 * that information should be used to select default
172                 * type of PHY.
173                 */
174                PHY_WRITE(sc, E1000_EADR, 0);
175                break;
176        }
177
178        PHY_RESET(sc);
179
180        sc->mii_capabilities = PHY_READ(sc, MII_BMSR) & sc->mii_capmask;
181        if (sc->mii_capabilities & BMSR_EXTSTAT) {
182                sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR);
183                if ((sc->mii_extcapabilities &
184                    (EXTSR_1000TFDX | EXTSR_1000THDX)) != 0)
185                        sc->mii_flags |= MIIF_HAVE_GTCR;
186        }
187        device_printf(dev, " ");
188        mii_phy_add_media(sc);
189        printf("\n");
190
191        MIIBUS_MEDIAINIT(sc->mii_dev);
192        return (0);
193}
194
195static void
196e1000phy_reset(struct mii_softc *sc)
197{
198        uint16_t reg, page;
199
200        reg = PHY_READ(sc, E1000_SCR);
201        if ((sc->mii_flags & MIIF_HAVEFIBER) != 0) {
202                reg &= ~E1000_SCR_AUTO_X_MODE;
203                PHY_WRITE(sc, E1000_SCR, reg);
204                if (sc->mii_mpd_model == MII_MODEL_xxMARVELL_E1112) {
205                        /* Select 1000BASE-X only mode. */
206                        page = PHY_READ(sc, E1000_EADR);
207                        PHY_WRITE(sc, E1000_EADR, 2);
208                        reg = PHY_READ(sc, E1000_SCR);
209                        reg &= ~E1000_SCR_MODE_MASK;
210                        reg |= E1000_SCR_MODE_1000BX;
211                        PHY_WRITE(sc, E1000_SCR, reg);
212                        if ((sc->mii_flags & MIIF_PHYPRIV0) != 0) {
213                                /* Set SIGDET polarity low for SFP module. */
214                                PHY_WRITE(sc, E1000_EADR, 1);
215                                reg = PHY_READ(sc, E1000_SCR);
216                                reg |= E1000_SCR_FIB_SIGDET_POLARITY;
217                                PHY_WRITE(sc, E1000_SCR, reg);
218                        }
219                        PHY_WRITE(sc, E1000_EADR, page);
220                }
221        } else {
222                switch (sc->mii_mpd_model) {
223                case MII_MODEL_xxMARVELL_E1111:
224                case MII_MODEL_xxMARVELL_E1112:
225                case MII_MODEL_xxMARVELL_E1116:
226                case MII_MODEL_xxMARVELL_E1118:
227                case MII_MODEL_xxMARVELL_E1149:
228                case MII_MODEL_xxMARVELL_E1149R:
229                case MII_MODEL_xxMARVELL_PHYG65G:
230                        /* Disable energy detect mode. */
231                        reg &= ~E1000_SCR_EN_DETECT_MASK;
232                        reg |= E1000_SCR_AUTO_X_MODE;
233                        if (sc->mii_mpd_model == MII_MODEL_xxMARVELL_E1116)
234                                reg &= ~E1000_SCR_POWER_DOWN;
235                        reg |= E1000_SCR_ASSERT_CRS_ON_TX;
236                        break;
237                case MII_MODEL_xxMARVELL_E3082:
238                        reg |= (E1000_SCR_AUTO_X_MODE >> 1);
239                        reg |= E1000_SCR_ASSERT_CRS_ON_TX;
240                        break;
241                case MII_MODEL_xxMARVELL_E3016:
242                        reg |= E1000_SCR_AUTO_MDIX;
243                        reg &= ~(E1000_SCR_EN_DETECT |
244                            E1000_SCR_SCRAMBLER_DISABLE);
245                        reg |= E1000_SCR_LPNP;
246                        /* XXX Enable class A driver for Yukon FE+ A0. */
247                        PHY_WRITE(sc, 0x1C, PHY_READ(sc, 0x1C) | 0x0001);
248                        break;
249                default:
250                        reg &= ~E1000_SCR_AUTO_X_MODE;
251                        reg |= E1000_SCR_ASSERT_CRS_ON_TX;
252                        break;
253                }
254                if (sc->mii_mpd_model != MII_MODEL_xxMARVELL_E3016) {
255                        /* Auto correction for reversed cable polarity. */
256                        reg &= ~E1000_SCR_POLARITY_REVERSAL;
257                }
258                PHY_WRITE(sc, E1000_SCR, reg);
259
260                if (sc->mii_mpd_model == MII_MODEL_xxMARVELL_E1116 ||
261                    sc->mii_mpd_model == MII_MODEL_xxMARVELL_E1149 ||
262                    sc->mii_mpd_model == MII_MODEL_xxMARVELL_E1149R) {
263                        PHY_WRITE(sc, E1000_EADR, 2);
264                        reg = PHY_READ(sc, E1000_SCR);
265                        reg |= E1000_SCR_RGMII_POWER_UP;
266                        PHY_WRITE(sc, E1000_SCR, reg);
267                        PHY_WRITE(sc, E1000_EADR, 0);
268                }
269        }
270
271        switch (sc->mii_mpd_model) {
272        case MII_MODEL_xxMARVELL_E3082:
273        case MII_MODEL_xxMARVELL_E1112:
274        case MII_MODEL_xxMARVELL_E1118:
275                break;
276        case MII_MODEL_xxMARVELL_E1116:
277                page = PHY_READ(sc, E1000_EADR);
278                /* Select page 3, LED control register. */
279                PHY_WRITE(sc, E1000_EADR, 3);
280                PHY_WRITE(sc, E1000_SCR,
281                    E1000_SCR_LED_LOS(1) |      /* Link/Act */
282                    E1000_SCR_LED_INIT(8) |     /* 10Mbps */
283                    E1000_SCR_LED_STAT1(7) |    /* 100Mbps */
284                    E1000_SCR_LED_STAT0(7));    /* 1000Mbps */
285                /* Set blink rate. */
286                PHY_WRITE(sc, E1000_IER, E1000_PULSE_DUR(E1000_PULSE_170MS) |
287                    E1000_BLINK_RATE(E1000_BLINK_84MS));
288                PHY_WRITE(sc, E1000_EADR, page);
289                break;
290        case MII_MODEL_xxMARVELL_E3016:
291                /* LED2 -> ACT, LED1 -> LINK, LED0 -> SPEED. */
292                PHY_WRITE(sc, 0x16, 0x0B << 8 | 0x05 << 4 | 0x04);
293                /* Integrated register calibration workaround. */
294                PHY_WRITE(sc, 0x1D, 17);
295                PHY_WRITE(sc, 0x1E, 0x3F60);
296                break;
297        default:
298                /* Force TX_CLK to 25MHz clock. */
299                reg = PHY_READ(sc, E1000_ESCR);
300                reg |= E1000_ESCR_TX_CLK_25;
301                PHY_WRITE(sc, E1000_ESCR, reg);
302                break;
303        }
304
305        /* Reset the PHY so all changes take effect. */
306        reg = PHY_READ(sc, E1000_CR);
307        reg |= E1000_CR_RESET;
308        PHY_WRITE(sc, E1000_CR, reg);
309}
310
311static int
312e1000phy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
313{
314        struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
315        uint16_t speed, gig;
316        int reg;
317
318        switch (cmd) {
319        case MII_POLLSTAT:
320                break;
321
322        case MII_MEDIACHG:
323                /*
324                 * If the interface is not up, don't do anything.
325                 */
326                if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
327                        break;
328
329                if (IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO) {
330                        e1000phy_mii_phy_auto(sc, ife->ifm_media);
331                        break;
332                }
333
334                speed = 0;
335                switch (IFM_SUBTYPE(ife->ifm_media)) {
336                case IFM_1000_T:
337                        if ((sc->mii_flags & MIIF_HAVE_GTCR) == 0)
338                                return (EINVAL);
339                        speed = E1000_CR_SPEED_1000;
340                        break;
341                case IFM_1000_SX:
342                        if ((sc->mii_extcapabilities &
343                            (EXTSR_1000XFDX | EXTSR_1000XHDX)) == 0)
344                                return (EINVAL);
345                        speed = E1000_CR_SPEED_1000;
346                        break;
347                case IFM_100_TX:
348                        speed = E1000_CR_SPEED_100;
349                        break;
350                case IFM_10_T:
351                        speed = E1000_CR_SPEED_10;
352                        break;
353                case IFM_NONE:
354                        reg = PHY_READ(sc, E1000_CR);
355                        PHY_WRITE(sc, E1000_CR,
356                            reg | E1000_CR_ISOLATE | E1000_CR_POWER_DOWN);
357                        goto done;
358                default:
359                        return (EINVAL);
360                }
361
362                if ((ife->ifm_media & IFM_FDX) != 0) {
363                        speed |= E1000_CR_FULL_DUPLEX;
364                        gig = E1000_1GCR_1000T_FD;
365                } else
366                        gig = E1000_1GCR_1000T;
367
368                reg = PHY_READ(sc, E1000_CR);
369                reg &= ~E1000_CR_AUTO_NEG_ENABLE;
370                PHY_WRITE(sc, E1000_CR, reg | E1000_CR_RESET);
371
372                if (IFM_SUBTYPE(ife->ifm_media) == IFM_1000_T) {
373                        gig |= E1000_1GCR_MS_ENABLE;
374                        if ((ife->ifm_media & IFM_ETH_MASTER) != 0)
375                                gig |= E1000_1GCR_MS_VALUE;
376                } else if ((sc->mii_flags & MIIF_HAVE_GTCR) != 0)
377                        gig = 0;
378                PHY_WRITE(sc, E1000_1GCR, gig);
379                PHY_WRITE(sc, E1000_AR, E1000_AR_SELECTOR_FIELD);
380                PHY_WRITE(sc, E1000_CR, speed | E1000_CR_RESET);
381done:
382                break;
383        case MII_TICK:
384                /*
385                 * Is the interface even up?
386                 */
387                if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
388                        return (0);
389
390                /*
391                 * Only used for autonegotiation.
392                 */
393                if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) {
394                        sc->mii_ticks = 0;
395                        break;
396                }
397
398                /*
399                 * check for link.
400                 * Read the status register twice; BMSR_LINK is latch-low.
401                 */
402                reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
403                if (reg & BMSR_LINK) {
404                        sc->mii_ticks = 0;
405                        break;
406                }
407
408                /* Announce link loss right after it happens. */
409                if (sc->mii_ticks++ == 0)
410                        break;
411                if (sc->mii_ticks <= sc->mii_anegticks)
412                        break;
413
414                sc->mii_ticks = 0;
415                PHY_RESET(sc);
416                e1000phy_mii_phy_auto(sc, ife->ifm_media);
417                break;
418        }
419
420        /* Update the media status. */
421        PHY_STATUS(sc);
422
423        /* Callback if something changed. */
424        mii_phy_update(sc, cmd);
425        return (0);
426}
427
428static void
429e1000phy_status(struct mii_softc *sc)
430{
431        struct mii_data *mii = sc->mii_pdata;
432        int bmcr, bmsr, ssr;
433
434        mii->mii_media_status = IFM_AVALID;
435        mii->mii_media_active = IFM_ETHER;
436
437        bmsr = PHY_READ(sc, E1000_SR) | PHY_READ(sc, E1000_SR);
438        bmcr = PHY_READ(sc, E1000_CR);
439        ssr = PHY_READ(sc, E1000_SSR);
440
441        if (bmsr & E1000_SR_LINK_STATUS)
442                mii->mii_media_status |= IFM_ACTIVE;
443
444        if (bmcr & E1000_CR_LOOPBACK)
445                mii->mii_media_active |= IFM_LOOP;
446
447        if ((bmcr & E1000_CR_AUTO_NEG_ENABLE) != 0 &&
448            (ssr & E1000_SSR_SPD_DPLX_RESOLVED) == 0) {
449                /* Erg, still trying, I guess... */
450                mii->mii_media_active |= IFM_NONE;
451                return;
452        }
453
454        if ((sc->mii_flags & MIIF_HAVEFIBER) == 0) {
455                switch (ssr & E1000_SSR_SPEED) {
456                case E1000_SSR_1000MBS:
457                        mii->mii_media_active |= IFM_1000_T;
458                        break;
459                case E1000_SSR_100MBS:
460                        mii->mii_media_active |= IFM_100_TX;
461                        break;
462                case E1000_SSR_10MBS:
463                        mii->mii_media_active |= IFM_10_T;
464                        break;
465                default:
466                        mii->mii_media_active |= IFM_NONE;
467                        return;
468                }
469        } else {
470                /*
471                 * Some fiber PHY(88E1112) does not seem to set resolved
472                 * speed so always assume we've got IFM_1000_SX.
473                 */
474                mii->mii_media_active |= IFM_1000_SX;
475        }
476
477        if (ssr & E1000_SSR_DUPLEX) {
478                mii->mii_media_active |= IFM_FDX;
479                if ((sc->mii_flags & MIIF_HAVEFIBER) == 0)
480                        mii->mii_media_active |= mii_phy_flowstatus(sc);
481        } else
482                mii->mii_media_active |= IFM_HDX;
483
484        if (IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T) {
485                if (((PHY_READ(sc, E1000_1GSR) | PHY_READ(sc, E1000_1GSR)) &
486                    E1000_1GSR_MS_CONFIG_RES) != 0)
487                        mii->mii_media_active |= IFM_ETH_MASTER;
488        }
489}
490
491static int
492e1000phy_mii_phy_auto(struct mii_softc *sc, int media)
493{
494        uint16_t reg;
495
496        if ((sc->mii_flags & MIIF_HAVEFIBER) == 0) {
497                reg = PHY_READ(sc, E1000_AR);
498                reg &= ~(E1000_AR_PAUSE | E1000_AR_ASM_DIR);
499                reg |= E1000_AR_10T | E1000_AR_10T_FD |
500                    E1000_AR_100TX | E1000_AR_100TX_FD;
501                if ((media & IFM_FLOW) != 0 ||
502                    (sc->mii_flags & MIIF_FORCEPAUSE) != 0)
503                        reg |= E1000_AR_PAUSE | E1000_AR_ASM_DIR;
504                PHY_WRITE(sc, E1000_AR, reg | E1000_AR_SELECTOR_FIELD);
505        } else
506                PHY_WRITE(sc, E1000_AR, E1000_FA_1000X_FD | E1000_FA_1000X);
507        if ((sc->mii_flags & MIIF_HAVE_GTCR) != 0) {
508                reg = 0;
509                if ((sc->mii_extcapabilities & EXTSR_1000TFDX) != 0)
510                        reg |= E1000_1GCR_1000T_FD;
511                if ((sc->mii_extcapabilities & EXTSR_1000THDX) != 0)
512                        reg |= E1000_1GCR_1000T;
513                PHY_WRITE(sc, E1000_1GCR, reg);
514        }
515        PHY_WRITE(sc, E1000_CR,
516            E1000_CR_AUTO_NEG_ENABLE | E1000_CR_RESTART_AUTO_NEG);
517
518        return (EJUSTRETURN);
519}
Note: See TracBrowser for help on using the repository browser.