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/etherswitch/infineon/adm6996fc.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) 2016 Hiroki Mori
    3  * Copyright (c) 2013 Luiz Otavio O Souza.
    4  * Copyright (c) 2011-2012 Stefan Bethke.
    5  * Copyright (c) 2012 Adrian Chadd.
    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, this list of conditions and the following 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  * $FreeBSD$
   30  */
   31 
   32 /*
   33  * This is Infineon ADM6996FC/M/MX driver code on etherswitch framework.
   34  * Support PORT and DOT1Q VLAN.
   35  * This code suppose ADM6996FC SDC/SDIO connect to SOC network interface
   36  * MDC/MDIO.
   37  * This code development on Netgear WGR614Cv7.
   38  * etherswitchcfg command port option support addtag.
   39  */
   40 
   41 #include <sys/param.h>
   42 #include <sys/bus.h>
   43 #include <sys/errno.h>
   44 #include <sys/kernel.h>
   45 #include <sys/lock.h>
   46 #include <sys/malloc.h>
   47 #include <sys/module.h>
   48 #include <sys/mutex.h>
   49 #include <sys/socket.h>
   50 #include <sys/sockio.h>
   51 #include <sys/sysctl.h>
   52 #include <sys/systm.h>
   53 
   54 #include <net/if.h>
   55 #include <net/if_var.h>
   56 #include <net/ethernet.h>
   57 #include <net/if_media.h>
   58 #include <net/if_types.h>
   59 
   60 #include <machine/bus.h>
   61 #include <dev/mii/mii.h>
   62 #include <dev/mii/miivar.h>
   63 #include <dev/mdio/mdio.h>
   64 
   65 #include <dev/etherswitch/etherswitch.h>
   66 
   67 #include "mdio_if.h"
   68 #include "miibus_if.h"
   69 #include "etherswitch_if.h"
   70 
   71 #define ADM6996FC_PRODUCT_CODE  0x7102
   72 
   73 #define ADM6996FC_SC3           0x11
   74 #define ADM6996FC_VF0L          0x40
   75 #define ADM6996FC_VF0H          0x41
   76 #define ADM6996FC_CI0           0xa0
   77 #define ADM6996FC_CI1           0xa1
   78 #define ADM6996FC_PHY_C0        0x200
   79 
   80 #define ADM6996FC_PC_SHIFT      4
   81 #define ADM6996FC_TBV_SHIFT     5
   82 #define ADM6996FC_PVID_SHIFT    10
   83 #define ADM6996FC_OPTE_SHIFT    4
   84 #define ADM6996FC_VV_SHIFT      15
   85 
   86 #define ADM6996FC_PHY_SIZE      0x20
   87 
   88 MALLOC_DECLARE(M_ADM6996FC);
   89 MALLOC_DEFINE(M_ADM6996FC, "adm6996fc", "adm6996fc data structures");
   90 
   91 struct adm6996fc_softc {
   92         struct mtx      sc_mtx;         /* serialize access to softc */
   93         device_t        sc_dev;
   94         int             vlan_mode;
   95         int             media;          /* cpu port media */
   96         int             cpuport;        /* which PHY is connected to the CPU */
   97         int             phymask;        /* PHYs we manage */
   98         int             numports;       /* number of ports */
   99         int             ifpport[MII_NPHY];
  100         int             *portphy;
  101         char            **ifname;
  102         device_t        **miibus;
  103         if_t *ifp;
  104         struct callout  callout_tick;
  105         etherswitch_info_t      info;
  106 };
  107 
  108 #define ADM6996FC_LOCK(_sc)                     \
  109             mtx_lock(&(_sc)->sc_mtx)
  110 #define ADM6996FC_UNLOCK(_sc)                   \
  111             mtx_unlock(&(_sc)->sc_mtx)
  112 #define ADM6996FC_LOCK_ASSERT(_sc, _what)       \
  113             mtx_assert(&(_sc)->sc_mtx, (_what))
  114 #define ADM6996FC_TRYLOCK(_sc)                  \
  115             mtx_trylock(&(_sc)->sc_mtx)
  116 
  117 #if defined(DEBUG)
  118 #define DPRINTF(dev, args...) device_printf(dev, args)
  119 #else
  120 #define DPRINTF(dev, args...)
  121 #endif
  122 
  123 static inline int adm6996fc_portforphy(struct adm6996fc_softc *, int);
  124 static void adm6996fc_tick(void *);
  125 static int adm6996fc_ifmedia_upd(if_t );
  126 static void adm6996fc_ifmedia_sts(if_t , struct ifmediareq *);
  127 
  128 #define ADM6996FC_READREG(dev, x)                                       \
  129         MDIO_READREG(dev, ((x) >> 5), ((x) & 0x1f));
  130 #define ADM6996FC_WRITEREG(dev, x, v)                                   \
  131         MDIO_WRITEREG(dev, ((x) >> 5), ((x) & 0x1f), v);
  132 
  133 #define ADM6996FC_PVIDBYDATA(data1, data2)                              \
  134         ((((data1) >> ADM6996FC_PVID_SHIFT) & 0x0f) | ((data2) << 4))
  135 
  136 static int
  137 adm6996fc_probe(device_t dev)
  138 {
  139         int data1, data2;
  140         int pc;
  141         struct adm6996fc_softc *sc;
  142 
  143         sc = device_get_softc(dev);
  144         bzero(sc, sizeof(*sc));
  145 
  146         data1 = ADM6996FC_READREG(device_get_parent(dev), ADM6996FC_CI0);
  147         data2 = ADM6996FC_READREG(device_get_parent(dev), ADM6996FC_CI1);
  148         pc = ((data2 << 16) | data1) >> ADM6996FC_PC_SHIFT;
  149         if (bootverbose)
  150                 device_printf(dev,"Chip Identifier Register %x %x\n", data1,
  151                     data2);
  152 
  153         /* check Product Code */
  154         if (pc != ADM6996FC_PRODUCT_CODE) {
  155                 return (ENXIO);
  156         }
  157 
  158         device_set_desc_copy(dev, "Infineon ADM6996FC/M/MX MDIO switch driver");
  159         return (BUS_PROBE_DEFAULT);
  160 }
  161 
  162 static int
  163 adm6996fc_attach_phys(struct adm6996fc_softc *sc)
  164 {
  165         int phy, port, err;
  166         char name[IFNAMSIZ];
  167 
  168         port = 0;
  169         err = 0;
  170         /* PHYs need an interface, so we generate a dummy one */
  171         snprintf(name, IFNAMSIZ, "%sport", device_get_nameunit(sc->sc_dev));
  172         for (phy = 0; phy < sc->numports; phy++) {
  173                 if (((1 << phy) & sc->phymask) == 0)
  174                         continue;
  175                 sc->ifpport[phy] = port;
  176                 sc->portphy[port] = phy;
  177                 sc->ifp[port] = if_alloc(IFT_ETHER);
  178                 if (sc->ifp[port] == NULL) {
  179                         device_printf(sc->sc_dev, "couldn't allocate ifnet structure\n");
  180                         err = ENOMEM;
  181                         break;
  182                 }
  183 
  184                 sc->ifp[port]->if_softc = sc;
  185                 sc->ifp[port]->if_flags |= IFF_UP | IFF_BROADCAST |
  186                     IFF_DRV_RUNNING | IFF_SIMPLEX;
  187                 if_initname(sc->ifp[port], name, port);
  188                 sc->miibus[port] = malloc(sizeof(device_t), M_ADM6996FC,
  189                     M_WAITOK | M_ZERO);
  190                 if (sc->miibus[port] == NULL) {
  191                         err = ENOMEM;
  192                         goto failed;
  193                 }
  194                 err = mii_attach(sc->sc_dev, sc->miibus[port], sc->ifp[port],
  195                     adm6996fc_ifmedia_upd, adm6996fc_ifmedia_sts, \
  196                     BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY, 0);
  197                 DPRINTF(sc->sc_dev, "%s attached to pseudo interface %s\n",
  198                     device_get_nameunit(*sc->miibus[port]),
  199                     sc->ifp[port]->if_xname);
  200                 if (err != 0) {
  201                         device_printf(sc->sc_dev,
  202                             "attaching PHY %d failed\n",
  203                             phy);
  204                         goto failed;
  205                 }
  206                 ++port;
  207         }
  208         sc->info.es_nports = port;
  209         if (sc->cpuport != -1) {
  210                 /* assume cpuport is last one */
  211                 sc->ifpport[sc->cpuport] = port;
  212                 sc->portphy[port] = sc->cpuport;
  213                 ++sc->info.es_nports;
  214         }
  215         return (0);
  216 
  217 failed:
  218         for (phy = 0; phy < sc->numports; phy++) {
  219                 if (((1 << phy) & sc->phymask) == 0)
  220                         continue;
  221                 port = adm6996fc_portforphy(sc, phy);
  222                 if (sc->miibus[port] != NULL)
  223                         device_delete_child(sc->sc_dev, (*sc->miibus[port]));
  224                 if (sc->ifp[port] != NULL)
  225                         if_free(sc->ifp[port]);
  226                 if (sc->ifname[port] != NULL)
  227                         free(sc->ifname[port], M_ADM6996FC);
  228                 if (sc->miibus[port] != NULL)
  229                         free(sc->miibus[port], M_ADM6996FC);
  230         }
  231         return (err);
  232 }
  233 
  234 static int
  235 adm6996fc_attach(device_t dev)
  236 {
  237         struct adm6996fc_softc  *sc;
  238         int                      err;
  239 
  240         err = 0;
  241         sc = device_get_softc(dev);
  242 
  243         sc->sc_dev = dev;
  244         mtx_init(&sc->sc_mtx, "adm6996fc", NULL, MTX_DEF);
  245         strlcpy(sc->info.es_name, device_get_desc(dev),
  246             sizeof(sc->info.es_name));
  247 
  248         /* ADM6996FC Defaults */
  249         sc->numports = 6;
  250         sc->phymask = 0x1f;
  251         sc->cpuport = 5;
  252         sc->media = 100;
  253 
  254         sc->info.es_nvlangroups = 16;
  255         sc->info.es_vlan_caps = ETHERSWITCH_VLAN_PORT | ETHERSWITCH_VLAN_DOT1Q;
  256 
  257         sc->ifp = malloc(sizeof(if_t ) * sc->numports, M_ADM6996FC,
  258             M_WAITOK | M_ZERO);
  259         sc->ifname = malloc(sizeof(char *) * sc->numports, M_ADM6996FC,
  260             M_WAITOK | M_ZERO);
  261         sc->miibus = malloc(sizeof(device_t *) * sc->numports, M_ADM6996FC,
  262             M_WAITOK | M_ZERO);
  263         sc->portphy = malloc(sizeof(int) * sc->numports, M_ADM6996FC,
  264             M_WAITOK | M_ZERO);
  265 
  266         if (sc->ifp == NULL || sc->ifname == NULL || sc->miibus == NULL ||
  267             sc->portphy == NULL) {
  268                 err = ENOMEM;
  269                 goto failed;
  270         }
  271 
  272         /*
  273          * Attach the PHYs and complete the bus enumeration.
  274          */
  275         err = adm6996fc_attach_phys(sc);
  276         if (err != 0)
  277                 goto failed;
  278 
  279         bus_generic_probe(dev);
  280         bus_enumerate_hinted_children(dev);
  281         err = bus_generic_attach(dev);
  282         if (err != 0)
  283                 goto failed;
  284         
  285         callout_init(&sc->callout_tick, 0);
  286 
  287         adm6996fc_tick(sc);
  288         
  289         return (0);
  290 
  291 failed:
  292         if (sc->portphy != NULL)
  293                 free(sc->portphy, M_ADM6996FC);
  294         if (sc->miibus != NULL)
  295                 free(sc->miibus, M_ADM6996FC);
  296         if (sc->ifname != NULL)
  297                 free(sc->ifname, M_ADM6996FC);
  298         if (sc->ifp != NULL)
  299                 free(sc->ifp, M_ADM6996FC);
  300 
  301         return (err);
  302 }
  303 
  304 static int
  305 adm6996fc_detach(device_t dev)
  306 {
  307         struct adm6996fc_softc  *sc;
  308         int                      i, port;
  309 
  310         sc = device_get_softc(dev);
  311 
  312         callout_drain(&sc->callout_tick);
  313 
  314         for (i = 0; i < MII_NPHY; i++) {
  315                 if (((1 << i) & sc->phymask) == 0)
  316                         continue;
  317                 port = adm6996fc_portforphy(sc, i);
  318                 if (sc->miibus[port] != NULL)
  319                         device_delete_child(dev, (*sc->miibus[port]));
  320                 if (sc->ifp[port] != NULL)
  321                         if_free(sc->ifp[port]);
  322                 free(sc->ifname[port], M_ADM6996FC);
  323                 free(sc->miibus[port], M_ADM6996FC);
  324         }
  325 
  326         free(sc->portphy, M_ADM6996FC);
  327         free(sc->miibus, M_ADM6996FC);
  328         free(sc->ifname, M_ADM6996FC);
  329         free(sc->ifp, M_ADM6996FC);
  330 
  331         bus_generic_detach(dev);
  332         mtx_destroy(&sc->sc_mtx);
  333 
  334         return (0);
  335 }
  336 
  337 /*
  338  * Convert PHY number to port number.
  339  */
  340 static inline int
  341 adm6996fc_portforphy(struct adm6996fc_softc *sc, int phy)
  342 {
  343 
  344         return (sc->ifpport[phy]);
  345 }
  346 
  347 static inline struct mii_data *
  348 adm6996fc_miiforport(struct adm6996fc_softc *sc, int port)
  349 {
  350 
  351         if (port < 0 || port > sc->numports)
  352                 return (NULL);
  353         if (port == sc->cpuport)
  354                 return (NULL);
  355         return (device_get_softc(*sc->miibus[port]));
  356 }
  357 
  358 static inline if_t 
  359 adm6996fc_ifpforport(struct adm6996fc_softc *sc, int port)
  360 {
  361 
  362         if (port < 0 || port > sc->numports)
  363                 return (NULL);
  364         return (sc->ifp[port]);
  365 }
  366 
  367 /*
  368  * Poll the status for all PHYs.
  369  */
  370 static void
  371 adm6996fc_miipollstat(struct adm6996fc_softc *sc)
  372 {
  373         int i, port;
  374         struct mii_data *mii;
  375         struct mii_softc *miisc;
  376 
  377         ADM6996FC_LOCK_ASSERT(sc, MA_NOTOWNED);
  378 
  379         for (i = 0; i < MII_NPHY; i++) {
  380                 if (((1 << i) & sc->phymask) == 0)
  381                         continue;
  382                 port = adm6996fc_portforphy(sc, i);
  383                 if ((*sc->miibus[port]) == NULL)
  384                         continue;
  385                 mii = device_get_softc(*sc->miibus[port]);
  386                 LIST_FOREACH(miisc, &mii->mii_phys, mii_list) {
  387                         if (IFM_INST(mii->mii_media.ifm_cur->ifm_media) !=
  388                             miisc->mii_inst)
  389                                 continue;
  390                         ukphy_status(miisc);
  391                         mii_phy_update(miisc, MII_POLLSTAT);
  392                 }
  393         }
  394 }
  395 
  396 static void
  397 adm6996fc_tick(void *arg)
  398 {
  399         struct adm6996fc_softc *sc;
  400 
  401         sc = arg;
  402 
  403         adm6996fc_miipollstat(sc);
  404         callout_reset(&sc->callout_tick, hz, adm6996fc_tick, sc);
  405 }
  406 
  407 static void
  408 adm6996fc_lock(device_t dev)
  409 {
  410         struct adm6996fc_softc *sc;
  411 
  412         sc = device_get_softc(dev);
  413 
  414         ADM6996FC_LOCK_ASSERT(sc, MA_NOTOWNED);
  415         ADM6996FC_LOCK(sc);
  416 }
  417 
  418 static void
  419 adm6996fc_unlock(device_t dev)
  420 {
  421         struct adm6996fc_softc *sc;
  422 
  423         sc = device_get_softc(dev);
  424 
  425         ADM6996FC_LOCK_ASSERT(sc, MA_OWNED);
  426         ADM6996FC_UNLOCK(sc);
  427 }
  428 
  429 static etherswitch_info_t *
  430 adm6996fc_getinfo(device_t dev)
  431 {
  432         struct adm6996fc_softc *sc;
  433 
  434         sc = device_get_softc(dev);
  435         
  436         return (&sc->info);
  437 }
  438 
  439 static int
  440 adm6996fc_getport(device_t dev, etherswitch_port_t *p)
  441 {
  442         struct adm6996fc_softc  *sc;
  443         struct mii_data         *mii;
  444         struct ifmediareq       *ifmr;
  445         device_t                 parent;
  446         int                      err, phy;
  447         int                      data1, data2;
  448 
  449         int     bcaddr[6] = {0x01, 0x03, 0x05, 0x07, 0x08, 0x09};
  450         int     vidaddr[6] = {0x28, 0x29, 0x2a, 0x2b, 0x2b, 0x2c};
  451 
  452         sc = device_get_softc(dev);
  453         ifmr = &p->es_ifmr;
  454 
  455         if (p->es_port < 0 || p->es_port >= sc->numports)
  456                 return (ENXIO);
  457 
  458         parent = device_get_parent(dev);
  459 
  460         if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
  461                 data1 = ADM6996FC_READREG(parent, bcaddr[p->es_port]);
  462                 data2 = ADM6996FC_READREG(parent, vidaddr[p->es_port]);
  463                 /* only port 4 is hi bit */
  464                 if (p->es_port == 4)
  465                         data2 = (data2 >> 8) & 0xff;
  466                 else
  467                         data2 = data2 & 0xff;
  468 
  469                 p->es_pvid = ADM6996FC_PVIDBYDATA(data1, data2);
  470                 if (((data1 >> ADM6996FC_OPTE_SHIFT) & 0x01) == 1)
  471                         p->es_flags |= ETHERSWITCH_PORT_ADDTAG;
  472         } else {
  473                 p->es_pvid = 0;
  474         }
  475 
  476         phy = sc->portphy[p->es_port];
  477         mii = adm6996fc_miiforport(sc, p->es_port);
  478         if (sc->cpuport != -1 && phy == sc->cpuport) {
  479                 /* fill in fixed values for CPU port */
  480                 p->es_flags |= ETHERSWITCH_PORT_CPU;
  481                 ifmr->ifm_count = 0;
  482                 if (sc->media == 100)
  483                         ifmr->ifm_current = ifmr->ifm_active =
  484                             IFM_ETHER | IFM_100_TX | IFM_FDX;
  485                 else
  486                         ifmr->ifm_current = ifmr->ifm_active =
  487                             IFM_ETHER | IFM_1000_T | IFM_FDX;
  488                 ifmr->ifm_mask = 0;
  489                 ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID;
  490         } else if (mii != NULL) {
  491                 err = ifmedia_ioctl(mii->mii_ifp, &p->es_ifr,
  492                     &mii->mii_media, SIOCGIFMEDIA);
  493                 if (err)
  494                         return (err);
  495         } else {
  496                 return (ENXIO);
  497         }
  498         return (0);
  499 }
  500 
  501 static int
  502 adm6996fc_setport(device_t dev, etherswitch_port_t *p)
  503 {
  504         struct adm6996fc_softc  *sc;
  505         struct ifmedia          *ifm;
  506         struct mii_data         *mii;
  507         if_t ifp;
  508         device_t                 parent;
  509         int                      err;
  510         int                      data;
  511 
  512         int     bcaddr[6] = {0x01, 0x03, 0x05, 0x07, 0x08, 0x09};
  513         int     vidaddr[6] = {0x28, 0x29, 0x2a, 0x2b, 0x2b, 0x2c};
  514 
  515         sc = device_get_softc(dev);
  516         parent = device_get_parent(dev);
  517 
  518         if (p->es_port < 0 || p->es_port >= sc->numports)
  519                 return (ENXIO);
  520 
  521         if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
  522                 data = ADM6996FC_READREG(parent, bcaddr[p->es_port]);
  523                 data &= ~(0xf << 10);
  524                 data |= (p->es_pvid & 0xf) << ADM6996FC_PVID_SHIFT;
  525                 if (p->es_flags & ETHERSWITCH_PORT_ADDTAG)
  526                         data |= 1 << ADM6996FC_OPTE_SHIFT;
  527                 else
  528                         data &= ~(1 << ADM6996FC_OPTE_SHIFT);
  529                 ADM6996FC_WRITEREG(parent, bcaddr[p->es_port], data);
  530                 data = ADM6996FC_READREG(parent, vidaddr[p->es_port]);
  531                 /* only port 4 is hi bit */
  532                 if (p->es_port == 4) {
  533                         data &= ~(0xff << 8);
  534                         data = data | (((p->es_pvid >> 4) & 0xff) << 8);
  535                 } else {
  536                         data &= ~0xff;
  537                         data = data | ((p->es_pvid >> 4) & 0xff);
  538                 }
  539                 ADM6996FC_WRITEREG(parent, vidaddr[p->es_port], data);
  540                 err = 0;
  541         } else {
  542                 if (sc->portphy[p->es_port] == sc->cpuport)
  543                         return (ENXIO);
  544         } 
  545 
  546         if (sc->portphy[p->es_port] != sc->cpuport) {
  547                 mii = adm6996fc_miiforport(sc, p->es_port);
  548                 if (mii == NULL)
  549                         return (ENXIO);
  550 
  551                 ifp = adm6996fc_ifpforport(sc, p->es_port);
  552 
  553                 ifm = &mii->mii_media;
  554                 err = ifmedia_ioctl(ifp, &p->es_ifr, ifm, SIOCSIFMEDIA);
  555         }
  556         return (err);
  557 }
  558 
  559 static int
  560 adm6996fc_getvgroup(device_t dev, etherswitch_vlangroup_t *vg)
  561 {
  562         struct adm6996fc_softc  *sc;
  563         device_t                 parent;
  564         int                      datahi, datalo;
  565 
  566         sc = device_get_softc(dev);
  567         parent = device_get_parent(dev);
  568 
  569         if (sc->vlan_mode == ETHERSWITCH_VLAN_PORT) {
  570                 if (vg->es_vlangroup <= 5) {
  571                         vg->es_vid = ETHERSWITCH_VID_VALID;
  572                         vg->es_vid |= vg->es_vlangroup;
  573                         datalo = ADM6996FC_READREG(parent,
  574                             ADM6996FC_VF0L + 2 * vg->es_vlangroup);
  575                         datahi = ADM6996FC_READREG(parent,
  576                             ADM6996FC_VF0H + 2 * vg->es_vlangroup);
  577 
  578                         vg->es_member_ports = datalo & 0x3f;
  579                         vg->es_untagged_ports = vg->es_member_ports;
  580                         vg->es_fid = 0;
  581                 } else {
  582                         vg->es_vid = 0;
  583                 }
  584         } else if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
  585                 datalo = ADM6996FC_READREG(parent,
  586                     ADM6996FC_VF0L + 2 * vg->es_vlangroup);
  587                 datahi = ADM6996FC_READREG(parent,
  588                     ADM6996FC_VF0H + 2 * vg->es_vlangroup);
  589                 
  590                 if (datahi & (1 << ADM6996FC_VV_SHIFT)) {
  591                         vg->es_vid = ETHERSWITCH_VID_VALID;
  592                         vg->es_vid |= datahi & 0xfff;
  593                         vg->es_member_ports = datalo & 0x3f;
  594                         vg->es_untagged_ports = (~datalo >> 6) & 0x3f;
  595                         vg->es_fid = 0;
  596                 } else {
  597                         vg->es_fid = 0;
  598                 }
  599         } else {
  600                 vg->es_fid = 0;
  601         }
  602 
  603         return (0);
  604 }
  605 
  606 static int
  607 adm6996fc_setvgroup(device_t dev, etherswitch_vlangroup_t *vg)
  608 {
  609         struct adm6996fc_softc  *sc;
  610         device_t                 parent;
  611 
  612         sc = device_get_softc(dev);
  613         parent = device_get_parent(dev);
  614 
  615         if (sc->vlan_mode == ETHERSWITCH_VLAN_PORT) {
  616                 ADM6996FC_WRITEREG(parent, ADM6996FC_VF0L + 2 * vg->es_vlangroup,
  617                     vg->es_member_ports);
  618         } else if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
  619                 ADM6996FC_WRITEREG(parent, ADM6996FC_VF0L + 2 * vg->es_vlangroup,
  620                     vg->es_member_ports | ((~vg->es_untagged_ports & 0x3f)<< 6));
  621                 ADM6996FC_WRITEREG(parent, ADM6996FC_VF0H + 2 * vg->es_vlangroup,
  622                     (1 << ADM6996FC_VV_SHIFT) | vg->es_vid);
  623         }
  624 
  625         return (0);
  626 }
  627 
  628 static int
  629 adm6996fc_getconf(device_t dev, etherswitch_conf_t *conf)
  630 {
  631         struct adm6996fc_softc *sc;
  632 
  633         sc = device_get_softc(dev);
  634 
  635         /* Return the VLAN mode. */
  636         conf->cmd = ETHERSWITCH_CONF_VLAN_MODE;
  637         conf->vlan_mode = sc->vlan_mode;
  638 
  639         return (0);
  640 }
  641 
  642 static int
  643 adm6996fc_setconf(device_t dev, etherswitch_conf_t *conf)
  644 {
  645         struct adm6996fc_softc  *sc;
  646         device_t                 parent;
  647         int                      i;
  648         int                      data;
  649         int     bcaddr[6] = {0x01, 0x03, 0x05, 0x07, 0x08, 0x09};
  650 
  651         sc = device_get_softc(dev);
  652         parent = device_get_parent(dev);
  653 
  654         if ((conf->cmd & ETHERSWITCH_CONF_VLAN_MODE) == 0)
  655                 return (0);
  656 
  657         if (conf->vlan_mode == ETHERSWITCH_VLAN_PORT) {
  658                 sc->vlan_mode = ETHERSWITCH_VLAN_PORT;
  659                 data = ADM6996FC_READREG(parent, ADM6996FC_SC3);
  660                 data &= ~(1 << ADM6996FC_TBV_SHIFT);
  661                 ADM6996FC_WRITEREG(parent, ADM6996FC_SC3, data);
  662                 for (i = 0;i <= 5; ++i) {
  663                         data = ADM6996FC_READREG(parent, bcaddr[i]);
  664                         data &= ~(0xf << 10);
  665                         data |= (i << 10);
  666                         ADM6996FC_WRITEREG(parent, bcaddr[i], data);
  667                         ADM6996FC_WRITEREG(parent, ADM6996FC_VF0L + 2 * i,
  668                             0x003f);
  669                         ADM6996FC_WRITEREG(parent, ADM6996FC_VF0H + 2 * i,
  670                             (1 << ADM6996FC_VV_SHIFT) | 1);
  671                 }
  672         } else if (conf->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
  673                 sc->vlan_mode = ETHERSWITCH_VLAN_DOT1Q;
  674                 data = ADM6996FC_READREG(parent, ADM6996FC_SC3);
  675                 data |= (1 << ADM6996FC_TBV_SHIFT);
  676                 ADM6996FC_WRITEREG(parent, ADM6996FC_SC3, data);
  677                 for (i = 0;i <= 5; ++i) {
  678                         data = ADM6996FC_READREG(parent, bcaddr[i]);
  679                         /* Private VID set 1 */
  680                         data &= ~(0xf << 10);
  681                         data |= (1 << 10);
  682                         ADM6996FC_WRITEREG(parent, bcaddr[i], data);
  683                 }
  684                 for (i = 2;i <= 15; ++i) {
  685                         ADM6996FC_WRITEREG(parent, ADM6996FC_VF0H + 2 * i,
  686                             0x0000);
  687                 }
  688         } else {
  689                 /*
  690                  ADM6996FC have no VLAN off. Then set Port base and
  691                  add all port to member. Use VLAN Filter 1 is reset
  692                  default.
  693                  */
  694                 sc->vlan_mode = 0;
  695                 data = ADM6996FC_READREG(parent, ADM6996FC_SC3);
  696                 data &= ~(1 << ADM6996FC_TBV_SHIFT);
  697                 ADM6996FC_WRITEREG(parent, ADM6996FC_SC3, data);
  698                 for (i = 0;i <= 5; ++i) {
  699                         data = ADM6996FC_READREG(parent, bcaddr[i]);
  700                         data &= ~(0xf << 10);
  701                         data |= (1 << 10);
  702                         if (i == 5)
  703                                 data &= ~(1 << 4);
  704                         ADM6996FC_WRITEREG(parent, bcaddr[i], data);
  705                 }
  706                 /* default setting */
  707                 ADM6996FC_WRITEREG(parent, ADM6996FC_VF0L + 2, 0x003f);
  708                 ADM6996FC_WRITEREG(parent, ADM6996FC_VF0H + 2,
  709                     (1 << ADM6996FC_VV_SHIFT) | 1);
  710         }
  711 
  712 
  713         return (0);
  714 }
  715 
  716 static void
  717 adm6996fc_statchg(device_t dev)
  718 {
  719 
  720         DPRINTF(dev, "%s\n", __func__);
  721 }
  722 
  723 static int
  724 adm6996fc_ifmedia_upd(if_t ifp)
  725 {
  726         struct adm6996fc_softc *sc;
  727         struct mii_data *mii;
  728 
  729         sc = if_getsoftc(ifp);
  730         mii = adm6996fc_miiforport(sc, ifp->if_dunit); /* XXX - DRVAPI */
  731 
  732         DPRINTF(sc->sc_dev, "%s\n", __func__);
  733         if (mii == NULL)
  734                 return (ENXIO);
  735         mii_mediachg(mii);
  736         return (0);
  737 }
  738 
  739 static void
  740 adm6996fc_ifmedia_sts(if_t ifp, struct ifmediareq *ifmr)
  741 {
  742         struct adm6996fc_softc *sc;
  743         struct mii_data *mii;
  744 
  745         sc = if_getsoftc(ifp);
  746         mii = adm6996fc_miiforport(sc, ifp->if_dunit); /* XXX - DRVAPI */
  747 
  748         DPRINTF(sc->sc_dev, "%s\n", __func__);
  749 
  750         if (mii == NULL)
  751                 return;
  752         mii_pollstat(mii);
  753         ifmr->ifm_active = mii->mii_media_active;
  754         ifmr->ifm_status = mii->mii_media_status;
  755 }
  756 
  757 static int
  758 adm6996fc_readphy(device_t dev, int phy, int reg)
  759 {
  760         struct adm6996fc_softc  *sc;
  761         int                      data;
  762 
  763         sc = device_get_softc(dev);
  764         ADM6996FC_LOCK_ASSERT(sc, MA_NOTOWNED);
  765 
  766         if (phy < 0 || phy >= 32)
  767                 return (ENXIO);
  768         if (reg < 0 || reg >= 32)
  769                 return (ENXIO);
  770 
  771         ADM6996FC_LOCK(sc);
  772         data = ADM6996FC_READREG(device_get_parent(dev),
  773             (ADM6996FC_PHY_C0 + ADM6996FC_PHY_SIZE * phy) + reg);
  774         ADM6996FC_UNLOCK(sc);
  775 
  776         return (data);
  777 }
  778 
  779 static int
  780 adm6996fc_writephy(device_t dev, int phy, int reg, int data)
  781 {
  782         struct adm6996fc_softc *sc;
  783         int err;
  784 
  785         sc = device_get_softc(dev);
  786         ADM6996FC_LOCK_ASSERT(sc, MA_NOTOWNED);
  787 
  788         if (phy < 0 || phy >= 32)
  789                 return (ENXIO);
  790         if (reg < 0 || reg >= 32)
  791                 return (ENXIO);
  792 
  793         ADM6996FC_LOCK(sc);
  794         err = ADM6996FC_WRITEREG(device_get_parent(dev),
  795             (ADM6996FC_PHY_C0 + ADM6996FC_PHY_SIZE * phy) + reg, data);
  796         ADM6996FC_UNLOCK(sc);
  797 
  798         return (err);
  799 }
  800 
  801 static int
  802 adm6996fc_readreg(device_t dev, int addr)
  803 {
  804 
  805         return ADM6996FC_READREG(device_get_parent(dev),  addr);
  806 }
  807 
  808 static int
  809 adm6996fc_writereg(device_t dev, int addr, int value)
  810 {
  811         int err;
  812 
  813         err = ADM6996FC_WRITEREG(device_get_parent(dev), addr, value);
  814         return (err);
  815 }
  816 
  817 static device_method_t adm6996fc_methods[] = {
  818         /* Device interface */
  819         DEVMETHOD(device_probe,         adm6996fc_probe),
  820         DEVMETHOD(device_attach,        adm6996fc_attach),
  821         DEVMETHOD(device_detach,        adm6996fc_detach),
  822         
  823         /* bus interface */
  824         DEVMETHOD(bus_add_child,        device_add_child_ordered),
  825         
  826         /* MII interface */
  827         DEVMETHOD(miibus_readreg,       adm6996fc_readphy),
  828         DEVMETHOD(miibus_writereg,      adm6996fc_writephy),
  829         DEVMETHOD(miibus_statchg,       adm6996fc_statchg),
  830 
  831         /* MDIO interface */
  832         DEVMETHOD(mdio_readreg,         adm6996fc_readphy),
  833         DEVMETHOD(mdio_writereg,        adm6996fc_writephy),
  834 
  835         /* etherswitch interface */
  836         DEVMETHOD(etherswitch_lock,     adm6996fc_lock),
  837         DEVMETHOD(etherswitch_unlock,   adm6996fc_unlock),
  838         DEVMETHOD(etherswitch_getinfo,  adm6996fc_getinfo),
  839         DEVMETHOD(etherswitch_readreg,  adm6996fc_readreg),
  840         DEVMETHOD(etherswitch_writereg, adm6996fc_writereg),
  841         DEVMETHOD(etherswitch_readphyreg,       adm6996fc_readphy),
  842         DEVMETHOD(etherswitch_writephyreg,      adm6996fc_writephy),
  843         DEVMETHOD(etherswitch_getport,  adm6996fc_getport),
  844         DEVMETHOD(etherswitch_setport,  adm6996fc_setport),
  845         DEVMETHOD(etherswitch_getvgroup,        adm6996fc_getvgroup),
  846         DEVMETHOD(etherswitch_setvgroup,        adm6996fc_setvgroup),
  847         DEVMETHOD(etherswitch_setconf,  adm6996fc_setconf),
  848         DEVMETHOD(etherswitch_getconf,  adm6996fc_getconf),
  849 
  850         DEVMETHOD_END
  851 };
  852 
  853 DEFINE_CLASS_0(adm6996fc, adm6996fc_driver, adm6996fc_methods,
  854     sizeof(struct adm6996fc_softc));
  855 
  856 DRIVER_MODULE(adm6996fc, mdio, adm6996fc_driver, 0, 0);
  857 DRIVER_MODULE(miibus, adm6996fc, miibus_driver, 0, 0);
  858 DRIVER_MODULE(mdio, adm6996fc, mdio_driver, 0, 0);
  859 DRIVER_MODULE(etherswitch, adm6996fc, etherswitch_driver, 0, 0);
  860 MODULE_VERSION(adm6996fc, 1);
  861 MODULE_DEPEND(adm6996fc, miibus, 1, 1, 1); /* XXX which versions? */
  862 MODULE_DEPEND(adm6996fc, etherswitch, 1, 1, 1); /* XXX which versions? */

Cache object: 6852bc62d94a2ba37464b75662a18ead


[ 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.