The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/mii/ciphy.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 2004
    3  *      Bill Paul <wpaul@windriver.com>.  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, this list of conditions and the following disclaimer.
   10  * 2. Redistributions in binary form must reproduce the above copyright
   11  *    notice, this list of conditions and the following disclaimer in the
   12  *    documentation and/or other materials provided with the distribution.
   13  * 3. All advertising materials mentioning features or use of this software
   14  *    must display the following acknowledgement:
   15  *      This product includes software developed by Bill Paul.
   16  * 4. Neither the name of the author nor the names of any co-contributors
   17  *    may be used to endorse or promote products derived from this software
   18  *    without specific prior written permission.
   19  *
   20  * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
   21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   23  * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
   24  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
   30  * THE POSSIBILITY OF SUCH DAMAGE.
   31  *
   32  * $FreeBSD$
   33  */
   34 
   35 #include <sys/cdefs.h>
   36 __FBSDID("$FreeBSD$");
   37 
   38 /*
   39  * Driver for the Cicada CS8201 10/100/1000 copper PHY.
   40  */
   41 
   42 #include <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 <machine/clock.h>
   50 
   51 #include <net/if.h>
   52 #include <net/if_arp.h>
   53 #include <net/if_media.h>
   54 
   55 #include <dev/mii/mii.h>
   56 #include <dev/mii/miivar.h>
   57 #include "miidevs.h"
   58 
   59 #include <dev/mii/ciphyreg.h>
   60 
   61 #include "miibus_if.h"
   62 
   63 #include <machine/bus.h>
   64 /*
   65 #include <dev/vge/if_vgereg.h>
   66 */
   67 static int ciphy_probe(device_t);
   68 static int ciphy_attach(device_t);
   69 
   70 static device_method_t ciphy_methods[] = {
   71         /* device interface */
   72         DEVMETHOD(device_probe,         ciphy_probe),
   73         DEVMETHOD(device_attach,        ciphy_attach),
   74         DEVMETHOD(device_detach,        mii_phy_detach),
   75         DEVMETHOD(device_shutdown,      bus_generic_shutdown),
   76         { 0, 0 }
   77 };
   78 
   79 static devclass_t ciphy_devclass;
   80 
   81 static driver_t ciphy_driver = {
   82         "ciphy",
   83         ciphy_methods,
   84         sizeof(struct mii_softc)
   85 };
   86 
   87 DRIVER_MODULE(ciphy, miibus, ciphy_driver, ciphy_devclass, 0, 0);
   88 
   89 static int      ciphy_service(struct mii_softc *, struct mii_data *, int);
   90 static void     ciphy_status(struct mii_softc *);
   91 static void     ciphy_reset(struct mii_softc *);
   92 static void     ciphy_fixup(struct mii_softc *);
   93 
   94 static int
   95 ciphy_probe(dev)
   96         device_t                dev;
   97 {
   98         struct mii_attach_args *ma;
   99 
  100         ma = device_get_ivars(dev);
  101 
  102         if (MII_OUI(ma->mii_id1, ma->mii_id2) == MII_OUI_CICADA &&
  103             MII_MODEL(ma->mii_id2) == MII_MODEL_CICADA_CS8201) {
  104                 device_set_desc(dev, MII_STR_CICADA_CS8201);
  105                 return(0);
  106         }
  107 
  108         if (MII_OUI(ma->mii_id1, ma->mii_id2) == MII_OUI_CICADA &&
  109             MII_MODEL(ma->mii_id2) == MII_MODEL_CICADA_CS8201A) {
  110                 device_set_desc(dev, MII_STR_CICADA_CS8201A);
  111                 return(0);
  112         }
  113 
  114         if (MII_OUI(ma->mii_id1, ma->mii_id2) == MII_OUI_CICADA &&
  115             MII_MODEL(ma->mii_id2) == MII_MODEL_CICADA_CS8201B) {
  116                 device_set_desc(dev, MII_STR_CICADA_CS8201B);
  117                 return(0);
  118         }
  119 
  120         return(ENXIO);
  121 }
  122 
  123 static int
  124 ciphy_attach(dev)
  125         device_t                dev;
  126 {
  127         struct mii_softc *sc;
  128         struct mii_attach_args *ma;
  129         struct mii_data *mii;
  130 
  131         sc = device_get_softc(dev);
  132         ma = device_get_ivars(dev);
  133         sc->mii_dev = device_get_parent(dev);
  134         mii = device_get_softc(sc->mii_dev);
  135         LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list);
  136 
  137         sc->mii_inst = mii->mii_instance;
  138         sc->mii_phy = ma->mii_phyno;
  139         sc->mii_service = ciphy_service;
  140         sc->mii_pdata = mii;
  141 
  142         sc->mii_flags |= MIIF_NOISOLATE;
  143         mii->mii_instance++;
  144 
  145         ciphy_reset(sc);
  146 
  147         sc->mii_capabilities =
  148             PHY_READ(sc, MII_BMSR) & ma->mii_capmask;
  149         if (sc->mii_capabilities & BMSR_EXTSTAT)
  150                 sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR);
  151         device_printf(dev, " ");
  152         mii_phy_add_media(sc);
  153         printf("\n");
  154 
  155         MIIBUS_MEDIAINIT(sc->mii_dev);
  156         return(0);
  157 }
  158 
  159 static int
  160 ciphy_service(sc, mii, cmd)
  161         struct mii_softc *sc;
  162         struct mii_data *mii;
  163         int cmd;
  164 {
  165         struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
  166         int reg, speed, gig;
  167 
  168         switch (cmd) {
  169         case MII_POLLSTAT:
  170                 /*
  171                  * If we're not polling our PHY instance, just return.
  172                  */
  173                 if (IFM_INST(ife->ifm_media) != sc->mii_inst)
  174                         return (0);
  175                 break;
  176 
  177         case MII_MEDIACHG:
  178                 /*
  179                  * If the media indicates a different PHY instance,
  180                  * isolate ourselves.
  181                  */
  182                 if (IFM_INST(ife->ifm_media) != sc->mii_inst) {
  183                         reg = PHY_READ(sc, MII_BMCR);
  184                         PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO);
  185                         return (0);
  186                 }
  187 
  188                 /*
  189                  * If the interface is not up, don't do anything.
  190                  */
  191                 if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
  192                         break;
  193 
  194                 ciphy_fixup(sc);        /* XXX hardware bug work-around */
  195 
  196                 switch (IFM_SUBTYPE(ife->ifm_media)) {
  197                 case IFM_AUTO:
  198 #ifdef foo
  199                         /*
  200                          * If we're already in auto mode, just return.
  201                          */
  202                         if (PHY_READ(sc, CIPHY_MII_BMCR) & CIPHY_BMCR_AUTOEN)
  203                                 return (0);
  204 #endif
  205                         (void) mii_phy_auto(sc);
  206                         break;
  207                 case IFM_1000_T:
  208                         speed = CIPHY_S1000;
  209                         goto setit;
  210                 case IFM_100_TX:
  211                         speed = CIPHY_S100;
  212                         goto setit;
  213                 case IFM_10_T:
  214                         speed = CIPHY_S10;
  215 setit:
  216                         if ((ife->ifm_media & IFM_GMASK) == IFM_FDX) {
  217                                 speed |= CIPHY_BMCR_FDX;
  218                                 gig = CIPHY_1000CTL_AFD;
  219                         } else {
  220                                 gig = CIPHY_1000CTL_AHD;
  221                         }
  222 
  223                         PHY_WRITE(sc, CIPHY_MII_1000CTL, 0);
  224                         PHY_WRITE(sc, CIPHY_MII_BMCR, speed);
  225                         PHY_WRITE(sc, CIPHY_MII_ANAR, CIPHY_SEL_TYPE);
  226 
  227                         if (IFM_SUBTYPE(ife->ifm_media) != IFM_1000_T) 
  228                                 break;
  229 
  230                         PHY_WRITE(sc, CIPHY_MII_1000CTL, gig);
  231                         PHY_WRITE(sc, CIPHY_MII_BMCR,
  232                             speed|CIPHY_BMCR_AUTOEN|CIPHY_BMCR_STARTNEG);
  233 
  234                         /*
  235                          * When setting the link manually, one side must
  236                          * be the master and the other the slave. However
  237                          * ifmedia doesn't give us a good way to specify
  238                          * this, so we fake it by using one of the LINK
  239                          * flags. If LINK0 is set, we program the PHY to
  240                          * be a master, otherwise it's a slave.
  241                          */
  242                         if ((mii->mii_ifp->if_flags & IFF_LINK0)) {
  243                                 PHY_WRITE(sc, CIPHY_MII_1000CTL,
  244                                     gig|CIPHY_1000CTL_MSE|CIPHY_1000CTL_MSC);
  245                         } else {
  246                                 PHY_WRITE(sc, CIPHY_MII_1000CTL,
  247                                     gig|CIPHY_1000CTL_MSE);
  248                         }
  249                         break;
  250                 case IFM_NONE:
  251                         PHY_WRITE(sc, MII_BMCR, BMCR_ISO|BMCR_PDOWN);
  252                         break;
  253                 case IFM_100_T4:
  254                 default:
  255                         return (EINVAL);
  256                 }
  257                 break;
  258 
  259         case MII_TICK:
  260                 /*
  261                  * If we're not currently selected, just return.
  262                  */
  263                 if (IFM_INST(ife->ifm_media) != sc->mii_inst)
  264                         return (0);
  265 
  266                 /*
  267                  * Is the interface even up?
  268                  */
  269                 if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
  270                         return (0);
  271 
  272                 /*
  273                  * Only used for autonegotiation.
  274                  */
  275                 if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO)
  276                         break;
  277 
  278                 /*
  279                  * Check to see if we have link.  If we do, we don't
  280                  * need to restart the autonegotiation process.  Read
  281                  * the BMSR twice in case it's latched.
  282                  */
  283                 reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
  284                 if (reg & BMSR_LINK)
  285                         break;
  286 
  287                 /*
  288                  * Only retry autonegotiation every 5 seconds.
  289                  */
  290                 if (++sc->mii_ticks <= 5/*10*/)
  291                         break;
  292                 
  293                 sc->mii_ticks = 0;
  294                 mii_phy_auto(sc);
  295                 return (0);
  296         }
  297 
  298         /* Update the media status. */
  299         ciphy_status(sc);
  300 
  301         /*
  302          * Callback if something changed. Note that we need to poke
  303          * apply fixups for certain PHY revs.
  304          */
  305         if (sc->mii_media_active != mii->mii_media_active || 
  306             sc->mii_media_status != mii->mii_media_status ||
  307             cmd == MII_MEDIACHG) {
  308                 ciphy_fixup(sc);
  309         }
  310         mii_phy_update(sc, cmd);
  311         return (0);
  312 }
  313 
  314 static void
  315 ciphy_status(sc)
  316         struct mii_softc *sc;
  317 {
  318         struct mii_data *mii = sc->mii_pdata;
  319         int bmsr, bmcr;
  320 
  321         mii->mii_media_status = IFM_AVALID;
  322         mii->mii_media_active = IFM_ETHER;
  323 
  324         bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
  325 
  326         if (bmsr & BMSR_LINK)
  327                 mii->mii_media_status |= IFM_ACTIVE;
  328 
  329         bmcr = PHY_READ(sc, CIPHY_MII_BMCR);
  330 
  331         if (bmcr & CIPHY_BMCR_LOOP)
  332                 mii->mii_media_active |= IFM_LOOP;
  333 
  334         if (bmcr & CIPHY_BMCR_AUTOEN) {
  335                 if ((bmsr & CIPHY_BMSR_ACOMP) == 0) {
  336                         /* Erg, still trying, I guess... */
  337                         mii->mii_media_active |= IFM_NONE;
  338                         return;
  339                 }
  340         }
  341 
  342         bmsr = PHY_READ(sc, CIPHY_MII_AUXCSR);
  343         switch (bmsr & CIPHY_AUXCSR_SPEED) {
  344         case CIPHY_SPEED10:
  345                 mii->mii_media_active |= IFM_10_T;
  346                 break;
  347         case CIPHY_SPEED100:
  348                 mii->mii_media_active |= IFM_100_TX;
  349                 break;
  350         case CIPHY_SPEED1000:
  351                 mii->mii_media_active |= IFM_1000_T;
  352                 break;
  353         default:
  354                 device_printf(sc->mii_dev, "unknown PHY speed %x\n",
  355                     bmsr & CIPHY_AUXCSR_SPEED);
  356                 break;
  357         }
  358 
  359         if (bmsr & CIPHY_AUXCSR_FDX)
  360                 mii->mii_media_active |= IFM_FDX;
  361 
  362         return;
  363 }
  364 
  365 static void
  366 ciphy_reset(struct mii_softc *sc)
  367 {
  368         mii_phy_reset(sc);
  369         DELAY(1000);
  370 
  371         return;
  372 }
  373 
  374 #define PHY_SETBIT(x, y, z) \
  375         PHY_WRITE(x, y, (PHY_READ(x, y) | (z)))
  376 #define PHY_CLRBIT(x, y, z) \
  377         PHY_WRITE(x, y, (PHY_READ(x, y) & ~(z)))
  378 
  379 static void
  380 ciphy_fixup(struct mii_softc *sc)
  381 {
  382         uint16_t                model;
  383         uint16_t                status, speed;
  384 
  385         model = MII_MODEL(PHY_READ(sc, CIPHY_MII_PHYIDR2));
  386         status = PHY_READ(sc, CIPHY_MII_AUXCSR);
  387         speed = status & CIPHY_AUXCSR_SPEED;
  388 
  389         switch (model) {
  390         case MII_MODEL_CICADA_CS8201:
  391 
  392                 /* Turn off "aux mode" (whatever that means) */
  393                 PHY_SETBIT(sc, CIPHY_MII_AUXCSR, CIPHY_AUXCSR_MDPPS);
  394 
  395                 /*
  396                  * Work around speed polling bug in VT3119/VT3216
  397                  * when using MII in full duplex mode.
  398                  */
  399                 if ((speed == CIPHY_SPEED10 || speed == CIPHY_SPEED100) &&
  400                     (status & CIPHY_AUXCSR_FDX)) {
  401                         PHY_SETBIT(sc, CIPHY_MII_10BTCSR, CIPHY_10BTCSR_ECHO);
  402                 } else {
  403                         PHY_CLRBIT(sc, CIPHY_MII_10BTCSR, CIPHY_10BTCSR_ECHO);
  404                 }
  405 
  406                 /* Enable link/activity LED blink. */
  407                 PHY_SETBIT(sc, CIPHY_MII_LED, CIPHY_LED_LINKACTBLINK);
  408 
  409                 break;
  410 
  411         case MII_MODEL_CICADA_CS8201A:
  412         case MII_MODEL_CICADA_CS8201B:
  413 
  414                 /*
  415                  * Work around speed polling bug in VT3119/VT3216
  416                  * when using MII in full duplex mode.
  417                  */
  418                 if ((speed == CIPHY_SPEED10 || speed == CIPHY_SPEED100) &&
  419                     (status & CIPHY_AUXCSR_FDX)) {
  420                         PHY_SETBIT(sc, CIPHY_MII_10BTCSR, CIPHY_10BTCSR_ECHO);
  421                 } else {
  422                         PHY_CLRBIT(sc, CIPHY_MII_10BTCSR, CIPHY_10BTCSR_ECHO);
  423                 }
  424 
  425                 break;
  426         default:
  427                 device_printf(sc->mii_dev, "unknown CICADA PHY model %x\n",
  428                     model);
  429                 break;
  430         }
  431 
  432         return;
  433 }

Cache object: 6c5577f62ae0b118d49b5a37add6fa05


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.