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

Cache object: d35eaea574846336408cbbeb2b056736


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