root/dev/mii/ipgphy.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ipgphy_probe
  2. ipgphy_attach
  3. ipgphy_service
  4. ipgphy_status
  5. ipgphy_mii_phy_auto
  6. ipgphy_load_dspcode
  7. ipgphy_reset

    1 /*      $OpenBSD: ipgphy.c,v 1.7 2006/12/31 15:04:33 krw Exp $  */
    2 
    3 /*-
    4  * Copyright (c) 2006, Pyun YongHyeon <yongari@FreeBSD.org>
    5  * 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 unmodified, this list of conditions, and the following
   12  *    disclaimer.
   13  * 2. Redistributions in binary form must reproduce the above copyright
   14  *    notice, this list of conditions and the following disclaimer in the
   15  *    documentation and/or other materials provided with the distribution.
   16  *
   17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   27  * SUCH DAMAGE.
   28  *
   29  */
   30 
   31 /*
   32  * Driver for the IC Plus IP1000A 10/100/1000 PHY.
   33  */
   34 
   35 #include <sys/param.h>
   36 #include <sys/systm.h>
   37 #include <sys/kernel.h>
   38 #include <sys/device.h>
   39 #include <sys/socket.h>
   40 #include <sys/errno.h>
   41 
   42 #include <machine/bus.h>
   43 
   44 #include <net/if.h>
   45 #include <net/if_media.h>
   46 
   47 #ifdef INET
   48 #include <netinet/in.h>
   49 #include <netinet/if_ether.h>
   50 #endif
   51 
   52 #include <dev/mii/mii.h>
   53 #include <dev/mii/miivar.h>
   54 #include <dev/mii/miidevs.h>
   55 
   56 #include <dev/mii/ipgphyreg.h>
   57 
   58 #include <dev/pci/if_stgereg.h>
   59 
   60 int ipgphy_probe(struct device *, void *, void *);
   61 void ipgphy_attach(struct device *, struct device *, void *);
   62 
   63 struct cfattach ipgphy_ca = {
   64         sizeof(struct mii_softc), ipgphy_probe, ipgphy_attach, mii_phy_detach,
   65             mii_phy_activate
   66 };
   67 
   68 struct cfdriver ipgphy_cd = {
   69         NULL, "ipgphy", DV_DULL
   70 };
   71 
   72 int     ipgphy_service(struct mii_softc *, struct mii_data *, int);
   73 void    ipgphy_status(struct mii_softc *);
   74 int     ipgphy_mii_phy_auto(struct mii_softc *);
   75 void    ipgphy_load_dspcode(struct mii_softc *);
   76 void    ipgphy_reset(struct mii_softc *);
   77 
   78 const struct mii_phy_funcs ipgphy_funcs = {
   79         ipgphy_service, ipgphy_status, ipgphy_reset,
   80 };
   81 
   82 static const struct mii_phydesc ipgphys[] = {
   83         { MII_OUI_ICPLUS,               MII_MODEL_ICPLUS_IP1000A,
   84           MII_STR_ICPLUS_IP1000A },
   85 
   86         { 0,
   87           NULL },
   88 };
   89 
   90 int
   91 ipgphy_probe(struct device *parent, void *match, void *aux)
   92 {
   93         struct mii_attach_args *ma = aux;
   94 
   95         if (mii_phy_match(ma, ipgphys) != NULL)
   96                 return (10);
   97 
   98         return (0);
   99 }
  100 
  101 void
  102 ipgphy_attach(struct device *parent, struct device *self, void *aux)
  103 {
  104         struct mii_softc *sc = (struct mii_softc *)self;
  105         struct mii_attach_args *ma = aux;
  106         struct mii_data *mii = ma->mii_data;
  107         const struct mii_phydesc *mpd;
  108 
  109         mpd = mii_phy_match(ma, ipgphys);
  110         printf(": %s, rev. %d\n", mpd->mpd_name, MII_REV(ma->mii_id2));
  111 
  112         sc->mii_inst = mii->mii_instance;
  113         sc->mii_phy = ma->mii_phyno;
  114         sc->mii_funcs = &ipgphy_funcs;
  115         sc->mii_pdata = mii;
  116         sc->mii_anegticks = MII_ANEGTICKS_GIGE;
  117 
  118         sc->mii_flags |= MIIF_NOISOLATE;
  119 
  120 #define ADD(m, c)       ifmedia_add(&mii->mii_media, (m), (c), NULL)
  121 
  122         ADD(IFM_MAKEWORD(IFM_ETHER, IFM_NONE, 0, sc->mii_inst),
  123             BMCR_ISO);
  124 
  125         ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, 0, sc->mii_inst),
  126             IPGPHY_BMCR_10);
  127         ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, IFM_FDX, sc->mii_inst),
  128             IPGPHY_BMCR_10 | IPGPHY_BMCR_FDX);
  129         ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, 0, sc->mii_inst),
  130             IPGPHY_BMCR_100);
  131         ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_FDX, sc->mii_inst),
  132             IPGPHY_BMCR_100 | IPGPHY_BMCR_FDX);
  133         /* 1000baseT half-duplex, really supported? */
  134         ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, 0, sc->mii_inst),
  135             IPGPHY_BMCR_1000);
  136         ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, IFM_FDX, sc->mii_inst),
  137             IPGPHY_BMCR_1000 | IPGPHY_BMCR_FDX);
  138         ADD(IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, sc->mii_inst), 0);
  139 #undef ADD
  140 
  141         PHY_RESET(sc);
  142 }
  143 
  144 int
  145 ipgphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
  146 {
  147         struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
  148         uint32_t gig, reg, speed;
  149 
  150         switch (cmd) {
  151         case MII_POLLSTAT:
  152                 /*
  153                  * If we're not polling our PHY instance, just return.
  154                  */
  155                 if (IFM_INST(ife->ifm_media) != sc->mii_inst)
  156                         return (0);
  157                 break;
  158 
  159         case MII_MEDIACHG:
  160                 /*
  161                  * If the media indicates a different PHY instance,
  162                  * isolate ourselves.
  163                  */
  164                 if (IFM_INST(ife->ifm_media) != sc->mii_inst) {
  165                         reg = PHY_READ(sc, IPGPHY_MII_BMCR);
  166                         PHY_WRITE(sc, IPGPHY_MII_BMCR,
  167                             reg | IPGPHY_BMCR_ISO);
  168                         return (0);
  169                 }
  170 
  171                 /*
  172                  * If the interface is not up, don't do anything.
  173                  */
  174                 if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
  175                         break;
  176 
  177                 PHY_RESET(sc);
  178 
  179                 switch (IFM_SUBTYPE(ife->ifm_media)) {
  180                 case IFM_AUTO:
  181                         (void)ipgphy_mii_phy_auto(sc);
  182                         goto done;
  183                         break;
  184 
  185                 case IFM_1000_T:
  186                         /*
  187                          * XXX
  188                          * Manual 1000baseT setting doesn't seem to work.
  189                          */
  190                         speed = IPGPHY_BMCR_1000;
  191                         break;
  192 
  193                 case IFM_100_TX:
  194                         speed = IPGPHY_BMCR_100;
  195                         break;
  196 
  197                 case IFM_10_T:
  198                         speed = IPGPHY_BMCR_10;
  199                         break;
  200 
  201                 default:
  202                         return (EINVAL);
  203                 }
  204 
  205                 if (((ife->ifm_media & IFM_GMASK) & IFM_FDX) != 0) {
  206                         speed |= IPGPHY_BMCR_FDX;
  207                         gig = IPGPHY_1000CR_1000T_FDX;
  208                 } else
  209                         gig = IPGPHY_1000CR_1000T;
  210 
  211                 PHY_WRITE(sc, IPGPHY_MII_1000CR, 0);
  212                 PHY_WRITE(sc, IPGPHY_MII_BMCR, speed);
  213 
  214                 if (IFM_SUBTYPE(ife->ifm_media) != IFM_1000_T)
  215                         break;
  216 
  217                 PHY_WRITE(sc, IPGPHY_MII_1000CR, gig);
  218                 PHY_WRITE(sc, IPGPHY_MII_BMCR, speed);
  219 
  220                 if (mii->mii_media.ifm_media & IFM_ETH_MASTER)
  221                         gig |= IPGPHY_1000CR_MASTER | IPGPHY_1000CR_MANUAL;
  222 
  223                 PHY_WRITE(sc, IPGPHY_MII_1000CR, gig);
  224 
  225 done:
  226                 break;
  227 
  228         case MII_TICK:
  229                 /*
  230                  * If we're not currently selected, just return.
  231                  */
  232                 if (IFM_INST(ife->ifm_media) != sc->mii_inst)
  233                         return (0);
  234                 /*
  235                  * Is the interface even up?
  236                  */
  237                 if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
  238                         return (0);
  239 
  240                 /*
  241                  * Only used for autonegotiation.
  242                  */
  243                 if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) {
  244                         sc->mii_ticks = 0;
  245                         break;
  246                 }
  247 
  248                 /*
  249                  * check for link.
  250                  */
  251                 reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
  252                 if (reg & BMSR_LINK) {
  253                         sc->mii_ticks = 0;
  254                         break;
  255                 }
  256 
  257                 /* Announce link loss right after it happens */
  258                 if (sc->mii_ticks++ == 0)
  259                         break;
  260 
  261                 /*
  262                  * Only retry autonegotiation every mii_anegticks seconds.
  263                  */
  264                 if (sc->mii_ticks <= sc->mii_anegticks)
  265                         return (0);
  266 
  267                 sc->mii_ticks = 0;
  268                 ipgphy_mii_phy_auto(sc);
  269                 break;
  270         }
  271 
  272         /* Update the media status. */
  273         mii_phy_status(sc);
  274 
  275         /* Callback if something changed. */
  276         mii_phy_update(sc, cmd);
  277         return (0);
  278 }
  279 
  280 void
  281 ipgphy_status(struct mii_softc *sc)
  282 {
  283         struct mii_data *mii = sc->mii_pdata;
  284         uint32_t bmsr, bmcr, stat;
  285 
  286         mii->mii_media_status = IFM_AVALID;
  287         mii->mii_media_active = IFM_ETHER;
  288 
  289         bmsr = PHY_READ(sc, IPGPHY_MII_BMSR) |
  290             PHY_READ(sc, IPGPHY_MII_BMSR);
  291         if ((bmsr & IPGPHY_BMSR_LINK) != 0)
  292                 mii->mii_media_status |= IFM_ACTIVE;
  293 
  294         bmcr = PHY_READ(sc, IPGPHY_MII_BMCR);
  295         if ((bmcr & IPGPHY_BMCR_LOOP) != 0)
  296                 mii->mii_media_active |= IFM_LOOP;
  297 
  298         if ((bmcr & IPGPHY_BMCR_AUTOEN) != 0) {
  299                 if ((bmsr & IPGPHY_BMSR_ANEGCOMP) == 0) {
  300                         /* Erg, still trying, I guess... */
  301                         mii->mii_media_active |= IFM_NONE;
  302                         return;
  303                 }
  304         }
  305 
  306         stat = PHY_READ(sc, STGE_PhyCtrl);
  307         switch (PC_LinkSpeed(stat)) {
  308         case PC_LinkSpeed_Down:
  309                 mii->mii_media_active |= IFM_NONE;
  310                 return;
  311         case PC_LinkSpeed_10:
  312                 mii->mii_media_active |= IFM_10_T;
  313                 break;
  314         case PC_LinkSpeed_100:
  315                 mii->mii_media_active |= IFM_100_TX;
  316                 break;
  317         case PC_LinkSpeed_1000:
  318                 mii->mii_media_active |= IFM_1000_T;
  319                 break;
  320         }
  321 
  322         if ((stat & PC_PhyDuplexStatus) != 0)
  323                 mii->mii_media_active |= mii_phy_flowstatus(sc) | IFM_FDX;
  324         else
  325                 mii->mii_media_active |= IFM_HDX;
  326 
  327         stat = PHY_READ(sc, IPGPHY_MII_1000SR);
  328         if ((IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T) &&
  329             stat & IPGPHY_1000SR_MASTER)
  330                 mii->mii_media_active |= IFM_ETH_MASTER;
  331 }
  332 
  333 int
  334 ipgphy_mii_phy_auto(struct mii_softc *mii)
  335 {
  336         uint32_t reg;
  337 
  338         reg = IPGPHY_ANAR_10T | IPGPHY_ANAR_10T_FDX |
  339               IPGPHY_ANAR_100TX | IPGPHY_ANAR_100TX_FDX;
  340 
  341         if (sc->mii_flags & MIIF_DOPAUSE)
  342                 reg |= IPGPHY_ANAR_PAUSE | IPGPHY_ANAR_APAUSE;
  343 
  344         PHY_WRITE(mii, IPGPHY_MII_ANAR, reg);
  345         reg = IPGPHY_1000CR_1000T | IPGPHY_1000CR_1000T_FDX;
  346         reg |= IPGPHY_1000CR_MASTER;
  347         PHY_WRITE(mii, IPGPHY_MII_1000CR, reg);
  348         PHY_WRITE(mii, IPGPHY_MII_BMCR, (IPGPHY_BMCR_FDX |
  349             IPGPHY_BMCR_AUTOEN | IPGPHY_BMCR_STARTNEG));
  350 
  351         return (EJUSTRETURN);
  352 }
  353 
  354 void
  355 ipgphy_load_dspcode(struct mii_softc *sc)
  356 {
  357         PHY_WRITE(sc, 31, 0x0001);
  358         PHY_WRITE(sc, 27, 0x01e0);
  359         PHY_WRITE(sc, 31, 0x0002);
  360         PHY_WRITE(sc, 27, 0xeb8e);
  361         PHY_WRITE(sc, 31, 0x0000);
  362         PHY_WRITE(sc, 30, 0x005e);
  363         PHY_WRITE(sc, 9, 0x0700);
  364 
  365         DELAY(50);
  366 }
  367 
  368 void
  369 ipgphy_reset(struct mii_softc *sc)
  370 {
  371         struct stge_softc *stge_sc;
  372         struct ifnet *ifp;
  373         uint32_t reg;
  374 
  375         mii_phy_reset(sc);
  376 
  377         /* clear autoneg/full-duplex as we don't want it after reset */
  378         reg = PHY_READ(sc, IPGPHY_MII_BMCR);
  379         reg &= ~(IPGPHY_BMCR_AUTOEN | IPGPHY_BMCR_FDX);
  380         PHY_WRITE(sc, MII_BMCR, reg);
  381 
  382         ifp = sc->mii_pdata->mii_ifp;
  383 
  384         if (strcmp(ifp->if_xname, "stge") == 0) {
  385                 stge_sc = ifp->if_softc;
  386                 if (stge_sc->sc_rev >= 0x40 && stge_sc->sc_rev <= 0x4e)
  387                         ipgphy_load_dspcode(sc);
  388         }
  389 }

/* [<][>][^][v][top][bottom][index][help] */