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/mips/cavium/octe/mv88e61xxphy.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) 2010 Juli Mallett <jmallett@FreeBSD.org>
    3  * 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  *
   14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   24  * SUCH DAMAGE.
   25  *
   26  * $FreeBSD$
   27  */
   28 
   29 #include <sys/cdefs.h>
   30 __FBSDID("$FreeBSD$");
   31 
   32 /*
   33  * Driver for the Marvell 88E61xx family of switch PHYs
   34  */
   35 
   36 #include <sys/param.h>
   37 #include <sys/systm.h>
   38 #include <sys/kernel.h>
   39 #include <sys/socket.h>
   40 #include <sys/errno.h>
   41 #include <sys/module.h>
   42 #include <sys/bus.h>
   43 #include <sys/sysctl.h>
   44 
   45 #include <net/ethernet.h>
   46 #include <net/if.h>
   47 #include <net/if_media.h>
   48 
   49 #include "miibus_if.h"
   50 
   51 #include "mv88e61xxphyreg.h"
   52 
   53 struct mv88e61xxphy_softc;
   54 
   55 struct mv88e61xxphy_port_softc {
   56         struct mv88e61xxphy_softc *sc_switch;
   57         unsigned sc_port;
   58         unsigned sc_domain;
   59         unsigned sc_vlan;
   60         unsigned sc_priority;
   61         unsigned sc_flags;
   62 };
   63 
   64 #define MV88E61XXPHY_PORT_FLAG_VTU_UPDATE       (0x0001)
   65 
   66 struct mv88e61xxphy_softc {
   67         device_t sc_dev;
   68         struct mv88e61xxphy_port_softc sc_ports[MV88E61XX_PORTS];
   69 };
   70 
   71 enum mv88e61xxphy_vtu_membership_type {
   72         MV88E61XXPHY_VTU_UNMODIFIED,
   73         MV88E61XXPHY_VTU_UNTAGGED,
   74         MV88E61XXPHY_VTU_TAGGED,
   75         MV88E61XXPHY_VTU_DISCARDED,
   76 };
   77 
   78 enum mv88e61xxphy_sysctl_link_type {
   79         MV88E61XXPHY_LINK_SYSCTL_DUPLEX,
   80         MV88E61XXPHY_LINK_SYSCTL_LINK,
   81         MV88E61XXPHY_LINK_SYSCTL_MEDIA,
   82 };
   83 
   84 enum mv88e61xxphy_sysctl_port_type {
   85         MV88E61XXPHY_PORT_SYSCTL_DOMAIN,
   86         MV88E61XXPHY_PORT_SYSCTL_VLAN,
   87         MV88E61XXPHY_PORT_SYSCTL_PRIORITY,
   88 };
   89 
   90 /*
   91  * Register access macros.
   92  */
   93 #define MV88E61XX_READ(sc, phy, reg)                                    \
   94         MIIBUS_READREG(device_get_parent((sc)->sc_dev), (phy), (reg))
   95 
   96 #define MV88E61XX_WRITE(sc, phy, reg, val)                              \
   97         MIIBUS_WRITEREG(device_get_parent((sc)->sc_dev), (phy), (reg), (val))
   98 
   99 #define MV88E61XX_READ_PORT(psc, reg)                                   \
  100         MV88E61XX_READ((psc)->sc_switch, MV88E61XX_PORT((psc)->sc_port), (reg))
  101 
  102 #define MV88E61XX_WRITE_PORT(psc, reg, val)                             \
  103         MV88E61XX_WRITE((psc)->sc_switch, MV88E61XX_PORT((psc)->sc_port), (reg), (val))
  104 
  105 static int mv88e61xxphy_probe(device_t);
  106 static int mv88e61xxphy_attach(device_t);
  107 
  108 static void mv88e61xxphy_init(struct mv88e61xxphy_softc *);
  109 static void mv88e61xxphy_init_port(struct mv88e61xxphy_port_softc *);
  110 static void mv88e61xxphy_init_vtu(struct mv88e61xxphy_softc *);
  111 static int mv88e61xxphy_sysctl_link_proc(SYSCTL_HANDLER_ARGS);
  112 static int mv88e61xxphy_sysctl_port_proc(SYSCTL_HANDLER_ARGS);
  113 static void mv88e61xxphy_vtu_load(struct mv88e61xxphy_softc *, uint16_t);
  114 static void mv88e61xxphy_vtu_set_membership(struct mv88e61xxphy_softc *, unsigned, enum mv88e61xxphy_vtu_membership_type);
  115 static void mv88e61xxphy_vtu_wait(struct mv88e61xxphy_softc *);
  116 
  117 static int
  118 mv88e61xxphy_probe(device_t dev)
  119 {
  120         uint16_t val;
  121 
  122         val = MIIBUS_READREG(device_get_parent(dev), MV88E61XX_PORT(0),
  123             MV88E61XX_PORT_REVISION);
  124         switch (val >> 4) {
  125         case 0x121:
  126                 device_set_desc(dev, "Marvell Link Street 88E6123 3-Port Gigabit Switch");
  127                 return (0);
  128         case 0x161:
  129                 device_set_desc(dev, "Marvell Link Street 88E6161 6-Port Gigabit Switch");
  130                 return (0);
  131         case 0x165:
  132                 device_set_desc(dev, "Marvell Link Street 88E6161 6-Port Advanced Gigabit Switch");
  133                 return (0);
  134         default:
  135                 return (ENXIO);
  136         }
  137 }
  138 
  139 static int
  140 mv88e61xxphy_attach(device_t dev)
  141 {
  142         char portbuf[] = "N";
  143         struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(dev);
  144         struct sysctl_oid *tree = device_get_sysctl_tree(dev);
  145         struct sysctl_oid_list *child = SYSCTL_CHILDREN(tree);
  146         struct sysctl_oid *port_node, *portN_node;
  147         struct sysctl_oid_list *port_tree, *portN_tree;
  148         struct mv88e61xxphy_softc *sc;
  149         unsigned port;
  150 
  151         sc = device_get_softc(dev);
  152         sc->sc_dev = dev;
  153 
  154         /*
  155          * Initialize port softcs.
  156          */
  157         for (port = 0; port < MV88E61XX_PORTS; port++) {
  158                 struct mv88e61xxphy_port_softc *psc;
  159 
  160                 psc = &sc->sc_ports[port];
  161                 psc->sc_switch = sc;
  162                 psc->sc_port = port;
  163                 psc->sc_domain = 0; /* One broadcast domain by default.  */
  164                 psc->sc_vlan = port + 1; /* Tag VLANs by default.  */
  165                 psc->sc_priority = 0; /* No default special priority.  */
  166                 psc->sc_flags = 0;
  167         }
  168 
  169         /*
  170          * Add per-port sysctl tree/handlers.
  171          */
  172         port_node = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "port",
  173             CTLFLAG_RD, NULL, "Switch Ports");
  174         port_tree = SYSCTL_CHILDREN(port_node);
  175         for (port = 0; port < MV88E61XX_PORTS; port++) {
  176                 struct mv88e61xxphy_port_softc *psc;
  177 
  178                 psc = &sc->sc_ports[port];
  179 
  180                 portbuf[0] = '' + port;
  181                 portN_node = SYSCTL_ADD_NODE(ctx, port_tree, OID_AUTO, portbuf,
  182                     CTLFLAG_RD, NULL, "Switch Port");
  183                 portN_tree = SYSCTL_CHILDREN(portN_node);
  184 
  185                 SYSCTL_ADD_PROC(ctx, portN_tree, OID_AUTO, "duplex",
  186                     CTLFLAG_RD | CTLTYPE_INT, psc,
  187                     MV88E61XXPHY_LINK_SYSCTL_DUPLEX,
  188                     mv88e61xxphy_sysctl_link_proc, "IU",
  189                     "Media duplex status (0 = half duplex; 1 = full duplex)");
  190 
  191                 SYSCTL_ADD_PROC(ctx, portN_tree, OID_AUTO, "link",
  192                     CTLFLAG_RD | CTLTYPE_INT, psc,
  193                     MV88E61XXPHY_LINK_SYSCTL_LINK,
  194                     mv88e61xxphy_sysctl_link_proc, "IU",
  195                     "Link status (0 = down; 1 = up)");
  196 
  197                 SYSCTL_ADD_PROC(ctx, portN_tree, OID_AUTO, "media",
  198                     CTLFLAG_RD | CTLTYPE_INT, psc,
  199                     MV88E61XXPHY_LINK_SYSCTL_MEDIA,
  200                     mv88e61xxphy_sysctl_link_proc, "IU",
  201                     "Media speed (0 = unknown; 10 = 10Mbps; 100 = 100Mbps; 1000 = 1Gbps)");
  202 
  203                 SYSCTL_ADD_PROC(ctx, portN_tree, OID_AUTO, "domain",
  204                     CTLFLAG_RW | CTLTYPE_INT, psc,
  205                     MV88E61XXPHY_PORT_SYSCTL_DOMAIN,
  206                     mv88e61xxphy_sysctl_port_proc, "IU",
  207                     "Broadcast domain (ports can only talk to other ports in the same domain)");
  208 
  209                 SYSCTL_ADD_PROC(ctx, portN_tree, OID_AUTO, "vlan",
  210                     CTLFLAG_RW | CTLTYPE_INT, psc,
  211                     MV88E61XXPHY_PORT_SYSCTL_VLAN,
  212                     mv88e61xxphy_sysctl_port_proc, "IU",
  213                     "Tag packets from/for this port with a given VLAN.");
  214 
  215                 SYSCTL_ADD_PROC(ctx, portN_tree, OID_AUTO, "priority",
  216                     CTLFLAG_RW | CTLTYPE_INT, psc,
  217                     MV88E61XXPHY_PORT_SYSCTL_PRIORITY,
  218                     mv88e61xxphy_sysctl_port_proc, "IU",
  219                     "Default packet priority for this port.");
  220         }
  221 
  222         mv88e61xxphy_init(sc);
  223 
  224         return (0);
  225 }
  226 
  227 static void
  228 mv88e61xxphy_init(struct mv88e61xxphy_softc *sc)
  229 {
  230         unsigned port;
  231         uint16_t val;
  232         unsigned i;
  233 
  234         /* Disable all ports.  */
  235         for (port = 0; port < MV88E61XX_PORTS; port++) {
  236                 struct mv88e61xxphy_port_softc *psc;
  237 
  238                 psc = &sc->sc_ports[port];
  239 
  240                 val = MV88E61XX_READ_PORT(psc, MV88E61XX_PORT_CONTROL);
  241                 val &= ~0x3;
  242                 MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_CONTROL, val);
  243         }
  244 
  245         DELAY(2000);
  246 
  247         /* Reset the switch.  */
  248         MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_CONTROL, 0xc400);
  249         for (i = 0; i < 100; i++) {
  250                 val = MV88E61XX_READ(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_STATUS);
  251                 if ((val & 0xc800) == 0xc800)
  252                         break;
  253                 DELAY(10);
  254         }
  255         if (i == 100) {
  256                 device_printf(sc->sc_dev, "%s: switch reset timed out.\n", __func__);
  257                 return;
  258         }
  259 
  260         /* Disable PPU.  */
  261         MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_CONTROL, 0x0000);
  262 
  263         /* Configure host port and send monitor frames to it.  */
  264         MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_MONITOR,
  265             (MV88E61XX_HOST_PORT << 12) | (MV88E61XX_HOST_PORT << 8) |
  266             (MV88E61XX_HOST_PORT << 4));
  267 
  268         /* Disable remote management.  */
  269         MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_REMOTE_MGMT, 0x0000);
  270 
  271         /* Send all specifically-addressed frames to the host port.  */
  272         MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL2, MV88E61XX_GLOBAL2_MANAGE_2X, 0xffff);
  273         MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL2, MV88E61XX_GLOBAL2_MANAGE_0X, 0xffff);
  274 
  275         /* Remove provider-supplied tag and use it for switching.  */
  276         MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL2, MV88E61XX_GLOBAL2_CONTROL2,
  277             MV88E61XX_GLOBAL2_CONTROL2_REMOVE_PTAG);
  278 
  279         /* Configure all ports.  */
  280         for (port = 0; port < MV88E61XX_PORTS; port++) {
  281                 struct mv88e61xxphy_port_softc *psc;
  282 
  283                 psc = &sc->sc_ports[port];
  284                 mv88e61xxphy_init_port(psc);
  285         }
  286 
  287         /* Reprogram VLAN table (VTU.)  */
  288         mv88e61xxphy_init_vtu(sc);
  289 
  290         /* Enable all ports.  */
  291         for (port = 0; port < MV88E61XX_PORTS; port++) {
  292                 struct mv88e61xxphy_port_softc *psc;
  293 
  294                 psc = &sc->sc_ports[port];
  295 
  296                 val = MV88E61XX_READ_PORT(psc, MV88E61XX_PORT_CONTROL);
  297                 val |= 0x3;
  298                 MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_CONTROL, val);
  299         }
  300 }
  301 
  302 static void
  303 mv88e61xxphy_init_port(struct mv88e61xxphy_port_softc *psc)
  304 {
  305         struct mv88e61xxphy_softc *sc;
  306         unsigned allow_mask;
  307 
  308         sc = psc->sc_switch;
  309 
  310         /* Set media type and flow control.  */
  311         if (psc->sc_port != MV88E61XX_HOST_PORT) {
  312                 /* Don't force any media type or flow control.  */
  313                 MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_FORCE_MAC, 0x0003);
  314         } else {
  315                 /* Make CPU port 1G FDX.  */
  316                 MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_FORCE_MAC, 0x003e);
  317         }
  318 
  319         /* Don't limit flow control pauses.  */
  320         MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_PAUSE_CONTROL, 0x0000);
  321 
  322         /* Set various port functions per Linux.  */
  323         if (psc->sc_port != MV88E61XX_HOST_PORT) {
  324                 MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_CONTROL, 0x04bc);
  325         } else {
  326                 /*
  327                  * Send frames for unknown unicast and multicast groups to
  328                  * host, too.
  329                  */
  330                 MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_CONTROL, 0x063f);
  331         }
  332 
  333         if (psc->sc_port != MV88E61XX_HOST_PORT) {
  334                 /* Disable trunking.  */
  335                 MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_CONTROL2, 0x0000);
  336         } else {
  337                 /* Disable trunking and send learn messages to host.  */
  338                 MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_CONTROL2, 0x8000);
  339         }
  340 
  341         /*
  342          * Port-based VLAN map; isolates MAC tables and forces ports to talk
  343          * only to the host.
  344          *
  345          * Always allow the host to send to all ports and allow all ports to
  346          * send to the host.
  347          */
  348         if (psc->sc_port != MV88E61XX_HOST_PORT) {
  349                 allow_mask = 1 << MV88E61XX_HOST_PORT;
  350         } else {
  351                 allow_mask = (1 << MV88E61XX_PORTS) - 1;
  352                 allow_mask &= ~(1 << MV88E61XX_HOST_PORT);
  353         }
  354         MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_VLAN_MAP,
  355             (psc->sc_domain << 12) | allow_mask);
  356 
  357         /* VLAN tagging.  Set default priority and VLAN tag (or none.)  */
  358         MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_VLAN,
  359             (psc->sc_priority << 14) | psc->sc_vlan);
  360 
  361         if (psc->sc_port == MV88E61XX_HOST_PORT) {
  362                 /* Set provider ingress tag.  */
  363                 MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_PROVIDER_PROTO,
  364                     ETHERTYPE_VLAN);
  365 
  366                 /* Set provider egress tag.  */
  367                 MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_ETHER_PROTO,
  368                     ETHERTYPE_VLAN);
  369 
  370                 /* Use secure 802.1q mode and accept only tagged frames.  */
  371                 MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_FILTER,
  372                     MV88E61XX_PORT_FILTER_MAP_DEST |
  373                     MV88E61XX_PORT_FILTER_8021Q_SECURE |
  374                     MV88E61XX_PORT_FILTER_DISCARD_UNTAGGED);
  375         } else {
  376                 /* Don't allow tagged frames.  */
  377                 MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_FILTER,
  378                     MV88E61XX_PORT_FILTER_MAP_DEST |
  379                     MV88E61XX_PORT_FILTER_DISCARD_TAGGED);
  380         }
  381 }
  382 
  383 static void
  384 mv88e61xxphy_init_vtu(struct mv88e61xxphy_softc *sc)
  385 {
  386         unsigned port;
  387 
  388         /*
  389          * Start flush of the VTU.
  390          */
  391         mv88e61xxphy_vtu_wait(sc);
  392         MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_VTU_OP,
  393             MV88E61XX_GLOBAL_VTU_OP_BUSY | MV88E61XX_GLOBAL_VTU_OP_OP_FLUSH);
  394 
  395         /*
  396          * Queue each port's VLAN to be programmed.
  397          */
  398         for (port = 0; port < MV88E61XX_PORTS; port++) {
  399                 struct mv88e61xxphy_port_softc *psc;
  400 
  401                 psc = &sc->sc_ports[port];
  402                 psc->sc_flags &= ~MV88E61XXPHY_PORT_FLAG_VTU_UPDATE;
  403                 if (psc->sc_vlan == 0)
  404                         continue;
  405                 psc->sc_flags |= MV88E61XXPHY_PORT_FLAG_VTU_UPDATE;
  406         }
  407 
  408         /*
  409          * Program each VLAN that is in use.
  410          */
  411         for (port = 0; port < MV88E61XX_PORTS; port++) {
  412                 struct mv88e61xxphy_port_softc *psc;
  413 
  414                 psc = &sc->sc_ports[port];
  415                 if ((psc->sc_flags & MV88E61XXPHY_PORT_FLAG_VTU_UPDATE) == 0)
  416                         continue;
  417                 mv88e61xxphy_vtu_load(sc, psc->sc_vlan);
  418         }
  419 
  420         /*
  421          * Wait for last pending VTU operation to complete.
  422          */
  423         mv88e61xxphy_vtu_wait(sc);
  424 }
  425 
  426 static int
  427 mv88e61xxphy_sysctl_link_proc(SYSCTL_HANDLER_ARGS)
  428 {
  429         struct mv88e61xxphy_port_softc *psc = arg1;
  430         enum mv88e61xxphy_sysctl_link_type type = arg2;
  431         uint16_t val;
  432         unsigned out;
  433 
  434         val = MV88E61XX_READ_PORT(psc, MV88E61XX_PORT_STATUS);
  435         switch (type) {
  436         case MV88E61XXPHY_LINK_SYSCTL_DUPLEX:
  437                 if ((val & MV88E61XX_PORT_STATUS_DUPLEX) != 0)
  438                         out = 1;
  439                 else
  440                         out = 0;
  441                 break;
  442         case MV88E61XXPHY_LINK_SYSCTL_LINK:
  443                 if ((val & MV88E61XX_PORT_STATUS_LINK) != 0)
  444                         out = 1;
  445                 else
  446                         out = 0;
  447                 break;
  448         case MV88E61XXPHY_LINK_SYSCTL_MEDIA:
  449                 switch (val & MV88E61XX_PORT_STATUS_MEDIA) {
  450                 case MV88E61XX_PORT_STATUS_MEDIA_10M:
  451                         out = 10;
  452                         break;
  453                 case MV88E61XX_PORT_STATUS_MEDIA_100M:
  454                         out = 100;
  455                         break;
  456                 case MV88E61XX_PORT_STATUS_MEDIA_1G:
  457                         out = 1000;
  458                         break;
  459                 default:
  460                         out = 0;
  461                         break;
  462                 }
  463                 break;
  464         default:
  465                 return (EINVAL);
  466         }
  467         return (sysctl_handle_int(oidp, NULL, out, req));
  468 }
  469 
  470 static int
  471 mv88e61xxphy_sysctl_port_proc(SYSCTL_HANDLER_ARGS)
  472 {
  473         struct mv88e61xxphy_port_softc *psc = arg1;
  474         enum mv88e61xxphy_sysctl_port_type type = arg2;
  475         struct mv88e61xxphy_softc *sc = psc->sc_switch;
  476         unsigned max, val, *valp;
  477         int error;
  478 
  479         switch (type) {
  480         case MV88E61XXPHY_PORT_SYSCTL_DOMAIN:
  481                 valp = &psc->sc_domain;
  482                 max = 0xf;
  483                 break;
  484         case MV88E61XXPHY_PORT_SYSCTL_VLAN:
  485                 valp = &psc->sc_vlan;
  486                 max = 0x1000;
  487                 break;
  488         case MV88E61XXPHY_PORT_SYSCTL_PRIORITY:
  489                 valp = &psc->sc_priority;
  490                 max = 3;
  491                 break;
  492         default:
  493                 return (EINVAL);
  494         }
  495 
  496         val = *valp;
  497         error = sysctl_handle_int(oidp, &val, 0, req);
  498         if (error != 0 || req->newptr == NULL)
  499                 return (error);
  500 
  501         /* Bounds check value.  */
  502         if (val >= max)
  503                 return (EINVAL);
  504 
  505         /* Reinitialize switch with new value.  */
  506         *valp = val;
  507         mv88e61xxphy_init(sc);
  508 
  509         return (0);
  510 }
  511 
  512 static void
  513 mv88e61xxphy_vtu_load(struct mv88e61xxphy_softc *sc, uint16_t vid)
  514 {
  515         unsigned port;
  516 
  517         /*
  518          * Wait for previous operation to complete.
  519          */
  520         mv88e61xxphy_vtu_wait(sc);
  521 
  522         /*
  523          * Set VID.
  524          */
  525         MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_VTU_VID,
  526             MV88E61XX_GLOBAL_VTU_VID_VALID | vid);
  527 
  528         /*
  529          * Add ports to this VTU.
  530          */
  531         for (port = 0; port < MV88E61XX_PORTS; port++) {
  532                 struct mv88e61xxphy_port_softc *psc;
  533 
  534                 psc = &sc->sc_ports[port];
  535                 if (psc->sc_vlan == vid) {
  536                         /*
  537                          * Send this port its VLAN traffic untagged.
  538                          */
  539                         psc->sc_flags &= ~MV88E61XXPHY_PORT_FLAG_VTU_UPDATE;
  540                         mv88e61xxphy_vtu_set_membership(sc, port, MV88E61XXPHY_VTU_UNTAGGED);
  541                 } else if (psc->sc_port == MV88E61XX_HOST_PORT) {
  542                         /*
  543                          * The host sees all VLANs tagged.
  544                          */
  545                         mv88e61xxphy_vtu_set_membership(sc, port, MV88E61XXPHY_VTU_TAGGED);
  546                 } else {
  547                         /*
  548                          * This port isn't on this VLAN.
  549                          */
  550                         mv88e61xxphy_vtu_set_membership(sc, port, MV88E61XXPHY_VTU_DISCARDED);
  551                 }
  552         }
  553 
  554         /*
  555          * Start adding this entry.
  556          */
  557         MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_VTU_OP,
  558             MV88E61XX_GLOBAL_VTU_OP_BUSY |
  559             MV88E61XX_GLOBAL_VTU_OP_OP_VTU_LOAD);
  560 }
  561 
  562 static void
  563 mv88e61xxphy_vtu_set_membership(struct mv88e61xxphy_softc *sc, unsigned port,
  564     enum mv88e61xxphy_vtu_membership_type type)
  565 {
  566         unsigned shift, reg;
  567         uint16_t bits;
  568         uint16_t val;
  569 
  570         switch (type) {
  571         case MV88E61XXPHY_VTU_UNMODIFIED:
  572                 bits = 0;
  573                 break;
  574         case MV88E61XXPHY_VTU_UNTAGGED:
  575                 bits = 1;
  576                 break;
  577         case MV88E61XXPHY_VTU_TAGGED:
  578                 bits = 2;
  579                 break;
  580         case MV88E61XXPHY_VTU_DISCARDED:
  581                 bits = 3;
  582                 break;
  583         default:
  584                 return;
  585         }
  586 
  587         if (port < 4) {
  588                 reg = MV88E61XX_GLOBAL_VTU_DATA_P0P3;
  589                 shift = port * 4;
  590         } else {
  591                 reg = MV88E61XX_GLOBAL_VTU_DATA_P4P5;
  592                 shift = (port - 4) * 4;
  593         }
  594 
  595         val = MV88E61XX_READ(sc, MV88E61XX_GLOBAL, reg);
  596         val |= bits << shift;
  597         MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, reg, val);
  598 }
  599 
  600 static void
  601 mv88e61xxphy_vtu_wait(struct mv88e61xxphy_softc *sc)
  602 {
  603         uint16_t val;
  604 
  605         for (;;) {
  606                 val = MV88E61XX_READ(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_VTU_OP);
  607                 if ((val & MV88E61XX_GLOBAL_VTU_OP_BUSY) == 0)
  608                         return;
  609         }
  610 }
  611 
  612 static device_method_t mv88e61xxphy_methods[] = {
  613         /* device interface */
  614         DEVMETHOD(device_probe,         mv88e61xxphy_probe),
  615         DEVMETHOD(device_attach,        mv88e61xxphy_attach),
  616         DEVMETHOD(device_detach,        bus_generic_detach),
  617         DEVMETHOD(device_shutdown,      bus_generic_shutdown),
  618 
  619         { 0, 0 }
  620 };
  621 
  622 static devclass_t mv88e61xxphy_devclass;
  623 
  624 static driver_t mv88e61xxphy_driver = {
  625         "mv88e61xxphy",
  626         mv88e61xxphy_methods,
  627         sizeof(struct mv88e61xxphy_softc)
  628 };
  629 
  630 DRIVER_MODULE(mv88e61xxphy, octe, mv88e61xxphy_driver, mv88e61xxphy_devclass, 0, 0);

Cache object: d330c958d5b860a79c25a0470da288db


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